How to draw a bitmap to another, but into a given quadrilateral (not necessary a rectangle)?

。_饼干妹妹 提交于 2019-11-29 17:21:12
android developer

Found an answer based on this post.

It seems that Matrix cannot be used as it can't create Trapezoid shapes which can occur in a 3d world.

So what is suggested there is to use the "Camera" class, as such:

    Canvas canvas = new Canvas(bigBitmap);
    Matrix matrix = new Matrix();
    Camera camera = new Camera();
    camera.save();
    camera.translate(...,...,0);
    camera.rotateX(...);
    camera.rotateY(...);
    camera.rotateZ(...);
    camera.getMatrix(matrix);
    int centerX = bigBitmap.getWidth() / 2;
    int centerY = bigBitmap.getHeight() / 2;
    matrix.preTranslate(-centerX, -centerY); //This is the key to getting the correct viewing perspective
    matrix.postTranslate(centerX, centerY);
    canvas.concat(matrix);
    camera.restore();
    canvas.drawBitmap(mSmallBitmap, matrix, new Paint());

Sadly, as you see, the coordinates aren't being used, so you need to either play with the numbers till you get it right, or find a formula to convert between the coordinates and the needed values.

I won't mark this answer as the correct one, because it doesn't fully fit the requirements of the original question (no coordinates are being used).

Plus I can't find how to deal with text while using this solution.

However, it does work, so it might be useful for others.


EDIT: It appears that the reason for setPolyToPoly to not scale the image at all, is that the first input array was incorrect: It was set as the size of the large bitmap, instead of the small one.

So, this is the correct code:

mLeftTop = new Point(370, 358);
mRightTop = new Point(650, 384);
mLeftBot = new Point(375, 972);
mRightBot = new Point(660, 942);
Canvas canvas = new Canvas(mBigBitmap);
final Matrix matrix = new Matrix();
matrix.setPolyToPoly(new float[]{0, 0,
                mSmallBitmap.getWidth() - 1, 0,
                0, mSmallBitmap.getHeight() - 1,
                mSmallBitmap.getWidth() - 1, mSmallBitmap.getHeight() - 1},
        0,
        new float[]{mLeftTop.x, mLeftTop.y,
                mRightTop.x, mRightTop.y,
                mLeftBot.x, mLeftBot.y,
                mRightBot.x, mRightBot.y
        }
        , 0, 4);
canvas.concat(matrix);
final Paint paint = new Paint();
paint.setAntiAlias(true);
canvas.drawBitmap(mSmallBitmap, 0, 0, paint);

However, for center-cropping, it still has this issue, but if you know the correct size of the rectangle before it got tilted, you can do the cropping before, and set it as the input.

As for the text, this is possible as usual, as the canvas stays with the matrix that was created.

to skew a bitmap,probably Matrix can be handy.

   /*use values accordingly*/
   Matrix matrix = new Matrix();
   matrix.postScale(curScale, curScale);  
   matrix.postRotate(curRotate);
   matrix.postSkew(curSkewX, curSkewY);

   Bitmap resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0, bmpWidth, bmpHeight, matrix, true);
   myImageView.setImageBitmap(resizedBitmap);

For my answer I am drawing a smaller Bitmap on to a larger Bitmap then drawing that on to a SurfaceView.

  1. Use the bounding quadrilateral to create a bounding rectangle.
  2. Use the bounding rectangle to create a transformation Matrix
  3. Use Matrix.ScaleToFit.CENTER to fill the bounding rectangle to the maximum size possible for the smaller Bitmap.

After these steps are taken just draw to the canvas the bigger Bitmap is using. The bounding quadrilateral is drawn red, the bounding rectangle blue and the big Bitmap is drawn green. Replace your smaller Bitmap with the blue Bitmap (bounding rectangle).

public class MainActivity extends Activity {
final String TAG = this.getClass().getName();

SurfaceView surfaceView;
Bitmap bitmap;
Bitmap bigBitmap;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    surfaceView = (SurfaceView) findViewById(R.id.surfaceView);
    surfaceView.getHolder().addCallback(new SurfaceHolder.Callback2() {
        @Override
        public void surfaceRedrawNeeded(SurfaceHolder holder) {

        }

        @Override
        public void surfaceCreated(SurfaceHolder holder) {
            Canvas surfaceCanvas = holder.lockCanvas();

            surfaceCanvas.drawBitmap(bigBitmap, 0, 0, new Paint());

            holder.unlockCanvasAndPost(surfaceCanvas);
        }

        @Override
        public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

        }

        @Override
        public void surfaceDestroyed(SurfaceHolder holder) {

        }
    });

    bitmap = Bitmap.createBitmap(64, 192, Bitmap.Config.ARGB_8888);
    {
        Canvas canvas = new Canvas(bitmap);
        Paint paint = new Paint();
        paint.setColor(Color.RED);
        canvas.drawRect(0, 0, 64, 192, paint);
    }

    bigBitmap = Bitmap.createBitmap(768,768, Bitmap.Config.ARGB_8888);
    {
        Canvas canvas = new Canvas(bigBitmap);

        // Fill background - For visual reference
        Paint paint = new Paint();
        paint.setColor(Color.GREEN);
        canvas.drawRect(0, 0, bigBitmap.getWidth(), bigBitmap.getHeight(), paint);

        // Setup transformation
        Matrix matrixPoly = new Matrix();
        Log.i(TAG, "matrixPoly: " + matrixPoly);

        // Draw Quadrilateral - For visual reference
        boolean canScale;
        canScale = matrixPoly.setPolyToPoly(new float[]{0,0, 64,0, 0,192, 64,192},
                0,
                new float[]{32,32, 96,16, 16,300, 128,256},
                0,
                4);

        Log.i(TAG, "matrixPoly: " + matrixPoly);
        Log.i(TAG, "matrixPoly canScale: " + canScale);

        canvas.drawBitmap(bitmap, matrixPoly, new Paint());

        // Points of Quadrilateral
        // {32,32, 96,16, 16,300, 128,256}
        float rectInQLeft = Math.max(32, 16);
        float rectInQTop = Math.min(32, 16);
        float rectInQRight = Math.min(96, 128);
        float rectInQBottom = Math.max(300, 256);
        ;
        Matrix matrixRect = new Matrix();
        Log.i(TAG, "matrixRect: " + matrixRect);
        canScale = matrixRect.setRectToRect(new RectF(0, 0, 64, 192),
                new RectF(rectInQLeft, rectInQTop, rectInQRight, rectInQBottom),
                Matrix.ScaleToFit.CENTER);

        Log.i(TAG, "matrixRect: " + matrixRect);
        Log.i(TAG, "matrixRect canScale: " + canScale);

        // Draw scaled bitmap
        Canvas smallBitmapCanvas = new Canvas(bitmap);
        Paint smallBitmapPaint = new Paint();
        smallBitmapPaint.setColor(Color.BLUE);
        smallBitmapCanvas.drawRect(0, 0, 64, 192, smallBitmapPaint);

        canvas.drawBitmap(bitmap, matrixRect, new Paint());
    }
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!