如何将位图绘制到另一个位图,但绘制到给定的四边形(不一定是矩形)?
How to draw a bitmap to another, but into a given quadrilateral (not necessary a rectangle)?
假设我有 2 个位图。一种是smallBitmap,一种是largeBitmap。
我想将整个 smallBitmap 绘制到 largeBitmap 中,但只绘制到 largeBitmap 的一部分,而不是直接绘制成一个 quadrilateral。
我认为草图最能说明我的意思:
这种情况的一个例子是倾斜的智能手机图像(如 this or this),您需要将屏幕截图放入其屏幕。
输入是:smallBitmap, largeBitmap, "quadrilateral" largeBitmap的坐标(放smallBitmap的地方)。
largeBitmap的"quadrilateral"只有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 可以派上用场。
/*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);
根据 this post 找到答案。
似乎无法使用 Matrix,因为它无法创建可以在 3d 世界中出现的梯形形状。
所以建议使用“Camera”class,例如:
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);
然而,对于中心裁剪,它仍然存在这个问题,但是如果你知道矩形在倾斜之前的正确大小,你可以在之前进行裁剪,并将其设置为输入。
至于文本,这和往常一样是可能的,因为 canvas 保留在创建的矩阵中。
对于我的回答,我将较小的 Bitmap
绘制到较大的 Bitmap
上,然后将其绘制到 SurfaceView
.
- 使用边界四边形创建边界矩形。
- 使用边界矩形创建变换
Matrix
- 使用
Matrix.ScaleToFit.CENTER
将边界矩形填充到较小 Bitmap
可能的最大尺寸。
完成这些步骤后,只需绘制到 canvas 正在使用的更大的 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());
}
}
假设我有 2 个位图。一种是smallBitmap,一种是largeBitmap。
我想将整个 smallBitmap 绘制到 largeBitmap 中,但只绘制到 largeBitmap 的一部分,而不是直接绘制成一个 quadrilateral。
我认为草图最能说明我的意思:
这种情况的一个例子是倾斜的智能手机图像(如 this or this),您需要将屏幕截图放入其屏幕。
输入是:smallBitmap, largeBitmap, "quadrilateral" largeBitmap的坐标(放smallBitmap的地方)。
largeBitmap的"quadrilateral"只有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 可以派上用场。
/*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);
根据 this post 找到答案。
似乎无法使用 Matrix,因为它无法创建可以在 3d 世界中出现的梯形形状。
所以建议使用“Camera”class,例如:
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);
然而,对于中心裁剪,它仍然存在这个问题,但是如果你知道矩形在倾斜之前的正确大小,你可以在之前进行裁剪,并将其设置为输入。
至于文本,这和往常一样是可能的,因为 canvas 保留在创建的矩阵中。
对于我的回答,我将较小的 Bitmap
绘制到较大的 Bitmap
上,然后将其绘制到 SurfaceView
.
- 使用边界四边形创建边界矩形。
- 使用边界矩形创建变换
Matrix
- 使用
Matrix.ScaleToFit.CENTER
将边界矩形填充到较小Bitmap
可能的最大尺寸。
完成这些步骤后,只需绘制到 canvas 正在使用的更大的 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());
}
}