【问题标题】:How to draw a bitmap to another, but into a given quadrilateral (not necessary a rectangle)?如何将位图绘制到另一个位置,但绘制到给定的四边形(不一定是矩形)?
【发布时间】:2015-11-17 18:32:11
【问题描述】:

假设我有 2 个位图。一个是smallBitmap,一个是largeBitmap。

我想将整个 smallBitmap 绘制到 largeBitmap 中,但只绘制到 largeBitmap 的一部分,而不是直接矩形,而是改为 quadrilateral

我认为草图最能描述我的意思:

这种情况的一个示例是倾斜的智能手机图像(如thisthis),您需要将屏幕截图放入其屏幕。

输入为:smallBitmap、largeBitmap、largeBitmap 的“四边形”坐标(smallBitmap 的放置位置)。

largeBitmap 的“四边形”只有 4 个坐标,不一定是矩形。例如,它可以是平行四边形或梯形。

我需要将smallBitmap缩放成largeBitmap内的四边形,并且还支持center-crop缩放,这样就不会变形了

我还需要知道如何以同样的方式处理文本,但我想这是相同的解决方案。


这是我尝试过的东西,但它甚至无法扩展:

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

【问题讨论】:

  • 见 Matrix.setPolyToPoly

标签: android bitmap android-canvas tilt


【解决方案1】:

要倾斜位图,矩阵可能会很方便。

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

【讨论】:

  • 我不知道倾斜和旋转值。只有其他位图的矩形的目标坐标。更新了问题以使其更清晰。
  • 您可以将旋转值用作零,将缩放值用作 (1,1),因为您不需要缩放或旋转。尝试 matrix.postSkew (float kx, float ky, float px, float py) 其中 px 和 py 是枢轴坐标。
  • 我不需要创建另一个位图。我写到 largeBitmap。此外,px 和 py 只是 2 个浮点数,而我有 4 个坐标。我该怎么处理它们?
  • 另外,歪斜会造成平行四边形的形状,但我不能让它有梯形的形状。
【解决方案2】:

根据this post找到答案。

似乎不能使用 Matrix,因为它无法创建可能出现在 3d 世界中的梯形形状。

所以建议使用“Camera”类,例如:

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

遗憾的是,如您所见,坐标并未被使用,因此您需要使用数字直到正确为止,或者找到一个公式在坐标和所需值之间进行转换。

我不会将此答案标记为正确答案,因为它不完全符合原始问题的要求(未使用坐标)。

另外,我在使用此解决方案时找不到如何处理文本。

但是,它确实有效,因此可能对其他人有用。


编辑:似乎 setPolyToPoly 根本不缩放图像的原因是第一个输入数组不正确:它被设置为大位图的大小,而不是小位图的大小。

所以,这是正确的代码:

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

但是,对于中心裁剪,它仍然存在这个问题,但是如果您知道矩形倾斜之前的正确大小,您可以在之前进行裁剪,并将其设置为输入。

至于文本,这与往常一样是可能的,因为画布与创建的矩阵保持一致。

【讨论】:

  • "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." 这是因为您使用了错误的 API (Camera),正确的 API 是 Matrix 及其 setPolyToPoly 方法,它使用了两个 exact数组> 坐标:二维空间中的四个 [x, y] 点,如您所见,Camera API 返回 Matrix,与 Matrix 相同,由 setPolyToPoly 方法调用计算(如果它在内部使用了setPolyToPoly 代码)
  • @pskink 如果您知道如何修复代码,请在新答案中编写替代方案。这就是我发现的。
  • @pskink 遗憾的是,这是我已经尝试过的事情之一。你甚至可以在那里看到我的评论。此解决方案不会缩放图像,也不会对其进行中心裁剪。
  • 好吧,所以它似乎必须使用Camera API(恐怕你不会在任何地方找到你确切问题的答案,除非你玩一下它,例如通过调用@987654334 @并查看dst浮点数组的坐标是什么)
  • @pskink 抱歉。英语不是我的主要语言。我不明白你的意思。你认为我仍然可以使用 setPolyToPoly 吗?还是我需要使用 Camera API 是真的? Romain Guy 的意思是它可以满足,但即使这种方法也不正确?
【解决方案3】:

对于我的答案,我将较小的 Bitmap 绘制到较大的 Bitmap 上,然后将其绘制到 SurfaceView 上。

  1. 使用边界四边形创建边界矩形。
  2. 使用边界矩形创建变换Matrix
  3. 使用Matrix.ScaleToFit.CENTER 将边界矩形填充到较小的Bitmap 可能的最大尺寸。

完成这些步骤后,只需将更大的Bitmap 绘制到画布上。边界四边形被绘制为红色,边界矩形被绘制为蓝色,大的Bitmap 被绘制为绿色。将较小的 Bitmap 替换为蓝色的 Bitmap(边界矩形)。

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

【讨论】:

  • 似乎我已经尝试过。使它拉伸并进行中心裁剪拉伸的部分在哪里?
  • 四边形真的只是为了边界吗?那会使小位图的变换完全不同!我会看看那个。
  • 我已经修改了我的答案,现在将四边形用作边界区域来创建边界矩形。
  • 我没有提到在周围放置一个框架。看看我在问题上显示的图像。我希望它能够缩放,具有中心裁剪缩放的能力。我还需要为文本做这件事。例如,看一张智能手机的图片:theinquirer.net/IMG/252/312252/…。现在,从互联网上选择您想要的任何图像,并尝试将其设置为智能手机屏幕的内容。
  • 这是我应该能够绘制的智能手机图像的另一个示例:cdn2.vox-cdn.com/thumbor/mPWCe0qOA1GRjJpFe8RojqCBPp4=/…
猜你喜欢
  • 1970-01-01
  • 2019-01-31
  • 1970-01-01
  • 2013-02-23
  • 1970-01-01
  • 2012-05-15
  • 1970-01-01
  • 2014-06-23
  • 1970-01-01
相关资源
最近更新 更多