简体   繁体   English

使用Android中的Matrix缩放和旋转位图

[英]Scale & rotate Bitmap using Matrix in Android

I'm trying to scale and rotate in single operation before creting the final bitmap but the preRotate, postConcat doesn't seem to work. 我试图在创建最终位图之前在单个操作中进行缩放和旋转但是preRotate,postConcat似乎不起作用。

Bitmap bmp = ... original image ...

Matrix m = new Matrix()
m.setScale(x, y);
m.preRotate(degrees, (float) width / 2, (float) height / 2);

Bitmap.createBitmap(bmp, 0, 0, bmp.getWidth(), bmp.getHeight(), m, true);

It only applies the scale and not rotation. 它只适用比例而不是旋转。

The answer was given, but to make things more clear to anyone reading this: 给出了答案,但是为了让任何阅读此内容的人更清楚:

1) if you wish to perform ONE transformation in your bitmap, you CAN use SET (setRotate, setScale etc). 1)如果你想在你的位图中执行一次转换,你可以使用SET(setRotate,setScale等)。

But note that any call to a "set" method OVERRIDES other transformations. 但请注意,任何对“set”方法的调用都会覆盖其他转换。 It's like a new matrix. 这就像一个新矩阵。 That's why OP's rotation was not working. 这就是为什么OP的轮换不起作用的原因。 These calls are not performed line by line. 这些调用不是逐行执行的。 It's like they are scheduled to be done at runtime by the GPU when the new bitmap is being drawn. 就像它们被安排在GPU运行时在绘制新位图时完成。 It's like when resolving your matrix, GPU rotated it, but then, created a scaled new one, ignoring previous matrix. 就像解析你的矩阵一样,GPU旋转它,然后,创建了一个缩放的新矩阵,忽略了之前的矩阵。

2) if you wish to perform more then one transformation, then you MUST use "pre" or "post" methods. 2)如果你想进行多次转换,那么你必须使用“pre”或“post”方法。

And what is the difference between a postRotate and a preRotate, for example? 例如,postRotate和preRotate之间有什么区别? Well, this matrix math stuff is not my strength, but what I know is that the graphic cards make these transformations using matrix multiplication. 好吧,这个矩阵数学的东西不是我的力量,但我所知道的是图形卡使用矩阵乘法进行这些变换。 It seems to be way more efficient. 它看起来更有效率。 And as far as I remember from school, when multiplicating matrices the order IS important. 而且就我从学校记得的那样,当乘法矩阵时,顺序非常重要。 AXB != BX A. So, scale a matrix and then rotate it is different from rotate and then scale it. AXB!= BX A.因此,缩放矩阵然后旋转它与旋转不同然后缩放它。

BUUUUT, as far as the final result in the screen is the same, we high level programmers usually do not need to know these differences. BUUUUT,就屏幕的最终结果而言,我们高级程序员通常不需要知道这些差异。 The GPU does. GPU确实如此。

Well, in that rare cases when you are performing really complicated matrix operations, and results are not what you expected or the performance is terrible and you need to deeply understand these methods to fix your code, well, then android documentation can not be of much help anyway. 好吧,在极少数情况下,当你执行非常复杂的矩阵操作,结果不是你所期望的或性能很糟糕,你需要深入了解这些方法来修复你的代码,那么,android文档不能太多无论如何帮助。 Instead, a good Linear Algebra book would be your best friend. 相反,一本好的线性代数书将是你最好的朋友。 ;) ;)

This is the code 这是代码

public class Bitmaptest extends Activity {
    @Override
    public void onCreate(Bundle icicle) {
        super.onCreate(icicle);
        LinearLayout linLayout = new LinearLayout(this);

        // load the origial BitMap (500 x 500 px)
        Bitmap bitmapOrg = BitmapFactory.decodeResource(getResources(),
               R.drawable.android);

        int width = bitmapOrg.getWidth();
        int height = bitmapOrg.getHeight();
        int newWidth = 200;
        int newHeight = 200;

        // calculate the scale - in this case = 0.4f
        float scaleWidth = ((float) newWidth) / width;
        float scaleHeight = ((float) newHeight) / height;

        // createa matrix for the manipulation
        Matrix matrix = new Matrix();
        // resize the bit map
        matrix.postScale(scaleWidth, scaleHeight);
        // rotate the Bitmap
        matrix.postRotate(45);

        // recreate the new Bitmap
        Bitmap resizedBitmap = Bitmap.createBitmap(bitmapOrg, 0, 0,
                          newWidth, newHeight, matrix, true);

        // make a Drawable from Bitmap to allow to set the BitMap
        // to the ImageView, ImageButton or what ever
        BitmapDrawable bmd = new BitmapDrawable(resizedBitmap);

        ImageView imageView = new ImageView(this);

        // set the Drawable on the ImageView
        imageView.setImageDrawable(bmd);

        // center the Image
        imageView.setScaleType(ScaleType.CENTER);

        // add ImageView to the Layout
        linLayout.addView(imageView,
                new LinearLayout.LayoutParams(
                      LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT
                )
        );

        // set LinearLayout as ContentView
        setContentView(linLayout);
    }
}

Canvas holds a matrix stack und you can use it with the methods: Canvas包含矩阵堆栈,您可以将其与方法一起使用:

Canvas.save() Canvas.save()

Doc: /** * Saves the current matrix and clip onto a private stack. Doc:/ ** *将当前矩阵和剪辑保存到专用堆栈。 * *

* Subsequent calls to translate,scale,rotate,skew,concat or clipRect, * clipPath will all operate as usual, but when the balancing call to * restore() is made, those calls will be forgotten, and the settings that * existed before the save() will be reinstated. *后续调用translate,scale,rotate,skew,concat或clipRect,* clipPath都将照常运行,但是当对* restore()进行平衡调用时,这些调用将被遗忘,并且*之前存在的设置save()将恢复。 * * @return The value to pass to restoreToCount() to balance this save() */ * * @return传递给restoreToCount()以平衡此save()* /的值

Canvas.restore() Canvas.restore()

Doc: /** * This call balances a previous call to save(), and is used to remove all * modifications to the matrix/clip state since the last save call. Doc:/ ** *此调用平衡先前对save()的调用,并用于删除自上次保存调用以来对矩阵/剪辑状态的所有*修改。 It is * an error to call restore() more times than save() was called. 调用restore()比调用save()更多次是错误的。 */ * /

example: A custom View(Android) which looks like a rotary knob(eg potentiometer) 示例:自定义视图(Android),看起来像旋钮(例如电位计)

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    viewX = getWidth();     //views width
    viewY = getHeight();    //views height
    setMeasuredDimension(widthMeasureSpec, heightMeasureSpec); //a must call for every custom view
}

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    double tempAngel = 3.6 * barValue;
    int deltaX = bitmap.getWidth() / 2;
    int deltaY = bitmap.getHeight() / 2;
    ...
    canvas.save();
    canvas.translate(viewX / 2, viewY / 2);             //translate drawing point to center
    canvas.rotate((float) tempAngel);                   //rotate matrix
    canvas.save();                                      //save matrix. your drawing point is still at (viewX / 2, viewY / 2)
    canvas.translate(deltaX * -1, deltaY * -1);         //translate drawing  point a bit up and left to draw the bitmap in the middle
    canvas.drawBitmap(bitmap, 0,0, bitmapPaint);   // draw bitmap to the tranlated point at 0,0
    canvas.restore();     //must calls...
    canvas.restore();
}
Matrix rotateMatrix = new Matrix();
rotateMatrix.postRotate(rotation);
rotatedBitmap = Bitmap.createBitmap(loadedImage, 0, 0,loadedImage.getWidth(), loadedImage.getHeight(),rotateMatrix, false);

如果您面临OutOfMemory问题以及上述答案,请使用以下内容:

Bitmap MyFinalBitmap = Bitmap.createBitmap(CurrentBitmap, 0, 0,CurrentBitmap.getWidth()/2, CurrentBitmap.getHeight()/2,matrix, true);

All of the previous answer assume that this change to the bitmap is being made in a view. 所有前面的答案都假设在视图中对位图进行了这种更改。 However in my case I was making the change to be saved out. 但是在我的情况下,我正在将更改保存下来。 Figured I would answer it for those in a similar boat. 想想我会回答类似船上的人。

There are two ways to do translation. 翻译有两种方法。 Below dx is the translation in the X axis, and dy is the translation in the Y axis. dx以下是X轴的平移, dy是Y轴的平移。 The other variables should be self explanatory. 其他变量应该是自我解释的。

1 - Translation within the image (without rotation) 1 - 图像内的翻译(不旋转)

val newBitmap = Bitmap.createBitmap(originalBitmap, dx, dy, newWidth, newHeight, matrix, false)

2 - Complex matrix 2 - 复杂矩阵

 matrix.postTranslate(dx, dy)

 val newBitmap = Bitmap.createBitmap(newWidth, newHeight, Bitmap.Config.ARGB_8888)

 val canvas = Canvas(newBitmap)
 canvas.drawBitmap(originalBitmap, matrix, null)

使用矩阵将原始位图的面积缩放到50%并压缩位图直到其大小<200k将位图压缩到Android中的特定字节大小

Refer to the following code, seems to work. 参考下面的代码,似乎工作。 In your code you are defining Matrix as m but referring to it as matrix 在您的代码中,您将Matrix定义为m,但将其称为矩阵

public class FourthActivity extends Activity {
private static final int WIDTH = 50;
private static final int HEIGHT = 50;
private static final int STRIDE = 64;  

private static int[] createColors() {
    int[] colors = new int[STRIDE * HEIGHT];
    for (int y = 0; y < HEIGHT; y++) {
        for (int x = 0; x < WIDTH; x++) {
            int r = x * 255 / (WIDTH - 1);
            int g = y * 255 / (HEIGHT - 1);
            int b = 255 - Math.min(r, g);
            int a = Math.max(r, g);
            colors[y * STRIDE + x] = (a << 24) | (r << 16) | (g << 8) | b;
        }
    }
    return colors;
}

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main2);
    final ImageView view1 = (ImageView) findViewById(R.id.imageView1);

    int[] colors = createColors();
    final Bitmap bmp1 = Bitmap.createBitmap(colors, 0, STRIDE, WIDTH, HEIGHT,
    Bitmap.Config.ARGB_8888);
    view1.setImageBitmap(bmp1);
    Button button = (Button) findViewById(R.id.button1);
    button.setOnClickListener(new OnClickListener(){
        @Override
        public void onClick(View v) {   
            Matrix matrix = new Matrix();
            matrix.setScale(2, 2);
            matrix.preRotate(45, (float) WIDTH / 2, (float) HEIGHT / 2);

            Bitmap bmp2 = Bitmap.createBitmap(bmp1, 0, 0, 
      bmp1.getWidth(), bmp1.getHeight(), matrix, true);
            ImageView view2 = (ImageView) findViewById(R.id.imageView2);
            view2.setImageBitmap(bmp2);
        }
    });
}
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM