规模&在Android中使用Matrix旋转位图

时间:2012-01-04 05:19:29

标签: android graphics

我正在尝试在创建最终位图之前在单个操作中进行缩放和旋转,但是preRotate,后Concat似乎不起作用。

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);

它只应用比例而不是旋转。

8 个答案:

答案 0 :(得分:38)

给出了答案,但为了让读者更清楚地了解事情:

1)如果你想在你的位图中执行一次转换,你可以使用SET(setRotate,setScale等)。

但请注意,对“set”方法的任何调用都会覆盖其他转换。这就像一个新矩阵。这就是为什么OP的轮换不起作用的原因。这些调用不是逐行执行的。就像它们被安排在GPU运行时在绘制新位图时完成。就像解析你的矩阵一样,GPU旋转它,然后,创建了一个缩放的新矩阵,忽略了之前的矩阵。

2)如果你想进行多次转换,那么你必须使用“pre”或“post”方法。

例如,postRotate和preRotate有什么区别?好吧,这个矩阵数学的东西不是我的力量,但我所知道的是图形卡使用矩阵乘法进行这些变换。它看起来更有效率。而且就我从学校记得的那样,当乘法矩阵时,顺序非常重要。 A X B!= B X A.因此,缩放矩阵然后旋转它与旋转不同然后缩放它。

BUUUUT,就屏幕中的最终结果而言,我们高级程序员通常不需要知道这些差异。 GPU确实如此。

嗯,在极少数情况下,当你执行非常复杂的矩阵操作,结果不是你所期望的或性能很糟糕,你需要深入了解这些方法来修复你的代码,那么,android文档不能无论如何都要有很多帮助。相反,一本好的线性代数书将是你最好的朋友。 ;)

答案 1 :(得分:37)

这是代码

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);
    }
}

答案 2 :(得分:1)

Canvas拥有一个矩阵堆栈,您可以将它与方法一起使用:

Canvas.save()

文档:     / **      *将当前矩阵和剪辑保存到专用堆栈。      *

     *后续调用翻译,缩放,旋转,倾斜,concat或clipRect,      * clipPath将照常运行,但平衡时调用      * restore(),这些调用将被遗忘,以及设置      *在恢复save()之前存在。      *      * @return传递给restoreToCount()以平衡此save()的值 * /

Canvas.restore()

文档: / **      *此调用平衡先前对save()的调用,并用于删除所有      *自上次保存调用以来对矩阵/剪辑状态的修改。它是      *调用restore()的次数比调用save()的次数多。 * /

例如: 自定义视图(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();
}

答案 3 :(得分:1)

Matrix rotateMatrix = new Matrix();
rotateMatrix.postRotate(rotation);
rotatedBitmap = Bitmap.createBitmap(loadedImage, 0, 0,loadedImage.getWidth(), loadedImage.getHeight(),rotateMatrix, false);

答案 4 :(得分:0)

参考以下代码,似乎有效。在您的代码中,您将Matrix定义为m,但将其称为 matrix

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);
        }
    });
}
}

答案 5 :(得分:0)

如果您遇到上述答案的 OutOfMemory 问题,请使用以下内容:

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

答案 6 :(得分:0)

所有先前的答案都假定对位图的此更改是在视图中进行的。但是就我而言,我所做的更改要保存下来。想通了,我会为那些在类似船上的人回答的。

有两种翻译方法。 dx下面是X轴的平移,dy是Y轴的平移。其他变量应该可以自我解释。

1-图像内平移(无旋转)

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

2-复数矩阵

 matrix.postTranslate(dx, dy)

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

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

答案 7 :(得分:0)

使用矩阵将原始位图的面积缩放到50%,并压缩位图,直到其大小小于200k Compress bitmap to a specific byte size in Android