问题
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
.
- Use the bounding quadrilateral to create a bounding rectangle.
- Use the bounding rectangle to create a transformation
Matrix
- Use
Matrix.ScaleToFit.CENTER
to fill the bounding rectangle to the maximum size possible for the smallerBitmap
.
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