Как нарисовать растровое изображение для другого, но в заданный четырехугольник (не обязательно прямоугольник)?
Предположим, у меня есть 2 растровых изображения. Один - smallBitmap, а другой - bigBitmap.
Я хочу нарисовать весь smallBitmap в largeBitmap, но только в части largeBitmap, и не в виде прямоугольника, а вместо этого в четырехугольник.
Я думаю, что эскиз лучше всего описывает то, что я имею в виду:
Примером этого сценария является наклонное изображение смартфона (подобное этому или этому), которое необходимо поместить на экран в виде скриншота.
Входные данные: smallBitmap, largeBitmap, "четырехугольные" координаты largeBitmap (куда поместить smallBitmap).
"Четырехсторонний" таблицы LargeBitmap имеет только 4 координаты, и это не обязательно прямоугольник. Это может быть, например, параллелограмм или трапеция.
Мне нужно масштабировать smallBitmap в четырехугольник внутри largeBitmap, а также поддерживать масштабирование по центру, чтобы оно не искажалось
Мне также нужно знать, как обращаться с текстами одинаково, но я думаю, что это примерно то же самое решение.
Вот что я пробовал, но даже не масштабируется:
//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());
3 ответа
Нашел ответ на основании этого поста.
Кажется, что Матрица не может быть использована, поскольку она не может создавать трапециевидные фигуры, которые могут возникнуть в трехмерном мире.
Поэтому предлагается использовать класс " Камера" как таковой:
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);
Однако для обрезки по центру у него все еще есть эта проблема, но если вы знаете правильный размер прямоугольника до того, как он наклонился, вы можете сделать обрезку раньше и установить его в качестве входных данных.
Что касается текста, это возможно как обычно, поскольку холст остается с созданной матрицей.
Чтобы исказить растровое изображение, возможно, 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);
Для моего ответа я рисую меньше Bitmap
на большее Bitmap
затем рисуя это на SurfaceView
,
- Используйте ограничивающий четырехугольник, чтобы создать ограничивающий прямоугольник.
- Используйте ограничивающий прямоугольник, чтобы создать преобразование
Matrix
- использование
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());
}
}