Как нарисовать растровое изображение для другого, но в заданный четырехугольник (не обязательно прямоугольник)?

Предположим, у меня есть 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,

  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());
    }
}
Другие вопросы по тегам