Android繪製點引發的思考

背景

在Canvas上繪製一個點,比如(1.0f,1.0f),原本以爲繪製的範圍應該是[1.0f,1.0f]-[2.0f,2.0f]這麼一個矩形區域,但是實際結果卻是[0.5f,0.5f]-[1.5f,1.5f]這麼一個矩形區域。於是聯想到如果通過繪製點的方式來繪製一張圖片會是什麼效果呢?

繪製一個點

下面代碼是繪製一個點的簡單例子:

/**
 * 繪製一個點
 */

public class PointView extends View {
    //View的寬度
    private int mWidth;
    //View的高度
    private int mHeight;
    //畫線的畫筆
    private Paint mLinePaint = new Paint();
    //畫點的畫筆
    private Paint mPointPaint = new Paint();
    //Canvas縮放的比例
    private float mScale = 100.0f;


    public PointView(Context context) {
        this(context, null);
    }

    public PointView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public PointView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mLinePaint.setColor(Color.BLUE);
        mLinePaint.setStyle(Paint.Style.STROKE);


        mPointPaint.setColor(Color.RED);
        mPointPaint.setStyle(Paint.Style.FILL);
        //設置畫筆大小
        mPointPaint.setStrokeWidth(1.0f);
        mPointPaint.setAntiAlias(true);
        mPointPaint.setDither(true);
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        if (changed) {
            mWidth = right - left;
            mHeight = bottom - top;
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.drawColor(Color.WHITE);
        canvas.save();
        Matrix matrix = new Matrix();
        matrix.setScale(mScale, mScale, 0.0f, 0.0f);
        //讓畫布擴大倍
        canvas.setMatrix(matrix);
        //繪製網格
        for (int i = 0; i <= mWidth; i++) {
            float[] points = new float[]{i, 0, i, mHeight};
            canvas.drawLines(points, mLinePaint);
        }
        for (int i = 0; i <= mHeight; i++) {
            float[] points = new float[]{0, i, mWidth, i};
            canvas.drawLines(points, mLinePaint);
        }
        //繪製點
        canvas.drawPoint(1.0f, 1.0f, mPointPaint);
        canvas.restore();
    }
}

下面圖片中紅色的方塊區域是繪製點(1.0f,1.0f)的結果,把畫布放大了一定倍數,便於觀察繪。

爲什麼繪製的區域是[0.5f,0.5f]-[1.5f,1.5f]這個矩形區域,而不是主觀意識中[1.0f,1.0f]-[2.0f,2.0f]。Android關於繪製點的API其中一個有下面介紹:

    /**
     * Draw a series of points. Each point is centered at the coordinate specified by pts[], and its
     * diameter is specified by the paint's stroke width (as transformed by the canvas' CTM), with
     * special treatment for a stroke width of 0, which always draws exactly 1 pixel (or at most 4
     * if antialiasing is enabled). The shape of the point is controlled by the paint's Cap type.
     * The shape is a square, unless the cap type is Round, in which case the shape is a circle.
     *
     * @param pts Array of points to draw [x0 y0 x1 y1 x2 y2 ...]
     * @param offset Number of values to skip before starting to draw.
     * @param count The number of values to process, after skipping offset of them. Since one point
     *            uses two values, the number of "points" that are drawn is really (count >> 1).
     * @param paint The paint used to draw the points
     */
    public void drawPoints(@Size(multiple = 2) float[] pts, int offset, int count,
            @NonNull Paint paint) {
        super.drawPoints(pts, offset, count, paint);
    }

上面這段註釋說明繪製一個點是以這個點爲中心,畫筆的大小爲直徑繪製的一個方形。所以最後在畫布上顯示的矩形區域就是[0.5f,0.5f]-[1.5f,1.5f]

繪製一張圖片

如果通過很多個點來繪製一張圖片,會是什麼效果呢?具體代碼如下:

/**
 * 利用像素繪製一個點,繪製出整張圖片
 */

public class PixelsView extends View {
    //View的寬度
    private int mWidth;
    //View的高度
    private int mHeight;
    //畫線的畫筆
    private Paint mLinePaint = new Paint();
    //畫點的畫筆
    private Paint mPointPaint = new Paint();
    //存儲圖片的像素
    private int[] mPixels;
    //圖片對象
    private Bitmap mBitmap;
    //圖片寬度
    private int mBmpWidth;
    //圖片高度
    private int mBmpHeight;
    //Canvas縮放的比例
    private float mScale = 15.0f;

    public PixelsView(Context context) {
        this(context, null);
    }

    public PixelsView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public PixelsView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mLinePaint.setColor(Color.BLUE);
        mLinePaint.setStyle(Paint.Style.STROKE);


        mPointPaint.setColor(Color.BLACK);
        mPointPaint.setStyle(Paint.Style.FILL);
        mPointPaint.setStrokeWidth(1.0f);
        mPointPaint.setAntiAlias(true);
        mPointPaint.setDither(true);

        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.test);

        int bmpWidth = mBmpWidth = bitmap.getWidth();
        int bmpHeight = mBmpHeight = bitmap.getHeight();

        int[] pixels = new int[bmpWidth * bmpHeight];
        bitmap.getPixels(pixels, 0, bmpWidth, 0, 0, bmpWidth, bmpHeight);
        mPixels = pixels;
        mBitmap = bitmap;
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        if (changed) {
            mWidth = right - left;
            mHeight = bottom - top;
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.drawColor(Color.WHITE);
        canvas.save();
        Matrix matrix = new Matrix();
        matrix.setScale(mScale, mScale, 0.0f, 0.0f);
        canvas.setMatrix(matrix);
        for (int i = 0; i <= mWidth; i++) {
            float[] points = new float[]{i, 0, i, mHeight};
            canvas.drawLines(points, mLinePaint);
        }
        for (int i = 0; i <= mHeight; i++) {
            float[] points = new float[]{0, i, mWidth, i};
            canvas.drawLines(points, mLinePaint);
        }
        for (int i = 0; i < mBmpHeight; i++) {
            for (int j = 0; j < mBmpWidth; j++) {
                int argbs = mPixels[i * mBmpWidth + j];
                mPointPaint.setColor(argbs);
                canvas.drawPoint(j, i, mPointPaint);
                //點的位置移動0.5單位個大小
                //canvas.drawPoint(((float)j+0.5f),(float)i+0.5f,mPointPaint);
            }
        }
        canvas.drawBitmap(mBitmap, 0f, mBmpHeight + 1, null);
        canvas.restore();
    }
}

這段代碼通過把圖片的像素繪製到對應的點上來繪製圖片對象,同時還繪製了一個Bitmap對象來作爲對比。程序運行結果如下:

(通過點繪製像素形成的圖片由於把畫布放大了就變得模糊了)對比通過點繪製的圖像,以及直接通過Bitmap繪製圖像的方式,發現通過點繪製的圖像在寬與高出都少了半個格子(0.5個單位)。

於是把通過點繪製圖像的方式中所有的點都加上0.5個單位,這樣兩種繪製的方式就是一樣的了。程序運行結果如下:

疑問

直接通過Bitmap對象來繪製一張圖片時,傳入的頂點座標並沒有偏移0.5個單位而繪製出了正確位置的圖像,比如前面程序中的(0,mBmpHeight + 1)。難道是系統內部實現的時候自動調整了0.5個單位嗎?
希望有了解過的人告訴下,或者是自己一開始就理解錯了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章