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

本小妞迷上赌 提交于 2019-11-30 09:52:29

问题


Suppose I have 2 bitmaps. One is smallBitmap, and one is largeBitmap.

I want to draw the entire smallBitmap into largeBitmap, but only to a part of largeBitmap, and not in a straight regtangle, but into a quadrilateral instead.

I think a sketch will best describe what I mean:

An example of this scenario is a tilted smartphone image (like this or this), that you need to put a screenshot into its screen.

The input is: smallBitmap, largeBitmap, "quadrilateral" coordinates of the largeBitmap (where to put the smallBitmap).

The "quadrilateral" of the largeBitmap just has 4 coordinates and it's not necessary a rectangle. It could be a parallelogram or trapezoid, for example.

I need to scale the smallBitmap into the quadrilateral within the largeBitmap, and also support center-crop scaling, so that it won't get distorted

I also need to know how to treat texts the same way, but I guess it's about the same solution.


Here's something that I've tried, but it doesn't even scale:

    //mBigBitmap: size is 720x1280
    //mSmallBitmap: size is 720x720
    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,
                    mBigBitmap.getWidth() - 1, 0,
                    0, mBigBitmap.getHeight() - 1,
                    mBigBitmap.getWidth() - 1, mBigBitmap.getHeight() - 1},
            0,
            new float[]{mLeftTop.x, mLeftTop.y,
                    mRightTop.x, mRightTop.y,
                    mLeftBot.x, mLeftBot.y,
                    mRightBot.x, mRightBot.y
            }
            , 0, 4);
    canvas.drawBitmap(mSmallBitmap, matrix, new Paint());

回答1:


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.




回答2:


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);



回答3:


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());
    }
}


来源:https://stackoverflow.com/questions/32180534/how-to-draw-a-bitmap-to-another-but-into-a-given-quadrilateral-not-necessary-a

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