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

Posted

技术标签:

【中文标题】在 Android 中使用 Matrix 缩放和旋转位图【英文标题】:Scale & rotate Bitmap using Matrix in Android 【发布时间】:2012-01-04 05:19:28 【问题描述】:

在创建最终位图之前,我尝试在单个操作中进行缩放和旋转,但 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);

它只应用缩放而不是旋转。

【问题讨论】:

不确定这是否会有所帮助..但是您尝试过 postRotate() 吗?或者,您可以在 setScale() 之前尝试 preRotate() 吗? @geeknizer 我不认为你有这个工作。我想这样做但不创建两个单独的位图,如下所述 【参考方案1】:

答案已经给出,但为了让阅读本文的人更清楚:

1) 如果您希望在位图中执行 ONE 转换,您可以使用 SET(setRotate、setScale 等)。

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

2) 如果您希望执行多个转换,则必须使用“pre”或“post”方法。

例如,postRotate 和 preRotate 有什么区别?好吧,这个矩阵数学的东西不是我的强项,但我知道显卡使用矩阵乘法进行这些转换。它似乎更有效率。据我在学校记得,当矩阵相乘时,顺序很重要。 A X B != B X A。因此,缩放矩阵然后旋转它不同于旋转然后缩放它。

BUUUUT,只要屏幕上的最终结果是一样的,我们高级程序员通常不需要知道这些差异。 GPU可以。

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

【讨论】:

"A X B != B X A. Sp,缩放矩阵然后旋转它与旋转然后缩放它不同。[...] BUUUUT [...] 屏幕上的最终结果是一样的” - 记录一下,关于单点的旋转和缩放可以通勤(AB = BA);当且仅当他们通勤时,他们看起来是一样的。高级程序员确实需要了解这一点。示例:3D 中的旋转不通勤 - 尝试翻转一本书。 你是对的!我在考虑参考点总是相同的。但是如果缩放和旋转的参考点不一样,那当然差别很大!【参考方案2】:

这是代码

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

【讨论】:

这个答案是错误的。创建位图时使用原始宽度和高度。更改位图 resizedBitmap = Bitmap.createBitmap(bitmapOrg, 0, 0, newWidth, newHeight, matrix, true); to Bitmap resizedBitmap = Bitmap.createBitmap(bitmapOrg, 0, 0, width, height, matrix, true);否则它将简单地裁剪部分图像并忽略矩阵 postScale.setScale。 我知道这可能是一篇旧帖子,但我在该链接上收到了病毒警告,因此请注意。 @AndrewIsOffline 感谢您的举报。您说对了它的旧端口,并且该站点似乎不再安全。我刚刚删除了链接。【参考方案3】:

如果您在上述答案中遇到 OutOfMemory 问题,请使用以下答案:

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

【讨论】:

【参考方案4】:

Canvas 包含一个矩阵堆栈,您可以将其与方法一起使用:

Canvas.save()

文档: /** * 保存当前矩阵并剪辑到私有堆栈上。 *

* 后续调用 translate、scale、rotate、skew、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();

【讨论】:

【参考方案5】:

前面的所有答案都假设对位图的这种更改是在视图中进行的。但是,就我而言,我正在将更改保存下来。我想我会为那些在类似船上的人回答这个问题。

有两种方法可以进行翻译。下面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)

【讨论】:

【参考方案6】:
Matrix rotateMatrix = new Matrix();
rotateMatrix.postRotate(rotation);
rotatedBitmap = Bitmap.createBitmap(loadedImage, 0, 0,loadedImage.getWidth(), loadedImage.getHeight(),rotateMatrix, false);

【讨论】:

【参考方案7】:

参考下面的代码,好像可以了。在您的代码中,您将 Matrix 定义为 m 但将其称为 ma​​trix

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


【讨论】:

【参考方案8】:

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

【讨论】:

以上是关于在 Android 中使用 Matrix 缩放和旋转位图的主要内容,如果未能解决你的问题,请参考以下文章

Android图片旋转,缩放,位移,倾斜,对称完整演示样例——imageView.setImageMatrix(matrix)和Matrix

[Android]android.graphics.Camera实现图像的旋转缩放,配合Matrix实现图像的倾斜

Android Matrix手势缩放自定义view 不止于Imageview

在android中使用矩阵缩放和拖动图像

Android实现多点触控,自由缩放图片

Android图片处理一:Matrix与手势