web-dev-qa-db-ja.com

Androidのマトリックスを使用してビットマップをスケーリングおよび回転します

最終的なビットマップを作成する前に、単一の操作でスケーリングと回転を試みていますが、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);

スケールのみを適用し、回転は適用しません。

30
Taranfx

これはコードです

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);
    }
}
36
Arslan Anwar

答えは出されましたが、これを読んでいる人に物事をより明確にするために:

1)ビットマップで1つの変換を実行する場合は、SET(setRotate、setScaleなど)を使用できます。

ただし、「set」メソッドを呼び出すと、他の変換が上書きされることに注意してください。それは新しい行列のようなものです。これが、OPの回転が機能しなかった理由です。これらの呼び出しは、行ごとには実行されません。新しいビットマップが描画されるときに、GPUによって実行時に実行されるようにスケジュールされているようです。マトリックスを解決するとき、GPUはそれを回転させたが、その後、以前のマトリックスを無視して、スケーリングされた新しいマトリックスを作成したようなものです。

2)複数の変換を実行する場合は、「pre」または「post」メソッドを使用する必要があります。

また、たとえばpostRotateとpreRotateの違いは何ですか?まあ、この行列演算は私の強さではありませんが、グラフィックカードが行列乗算を使用してこれらの変換を行うことは知っています。ずっと効率的だと思われます。そして、私が学校から覚えている限りでは、行列を乗算するとき、順序IS important。A X B!= B X A.だから、行列をスケーリングしてから回転させるのは、回転してからスケーリングするのとは異なります。

BUUUUT、画面の最終結果が同じである限り、私たちの上級プログラマーは通常これらの違いを知る必要はありません。 GPUが行います。

まあ、本当に複雑な行列演算を実行し、結果が期待したものではないか、パフォーマンスがひどく、コードを修正するためにこれらのメソッドを深く理解する必要があるまれなケースでは、よく、Androidドキュメンテーションはとにかく大した助けにはなりません。代わりに、良い線形代数の本があなたの親友になります;)

39
PFROLIM

Canvasは行列スタックを保持し、メソッドで使用できます:

Canvas.save()

Doc:/ ** *現在のマトリックスとクリップをプライベートスタックに保存します。 *

* translate、scale、rotate、skew、concatまたはclipRectへの後続の呼び出し、* clipPathはすべて通常どおり動作しますが、restore()へのバランス呼び出しが行われると、それらの呼び出しは忘れられ、以前に存在した設定save()が復元されます。 * * @returnこのsave()のバランスを取るためにrestoreToCount()に渡す値* /

Canvas.restore()

Doc:/ ** *この呼び出しは、save()への前の呼び出しとバランスを取り、最後の保存呼び出し以降のマトリックス/クリップ状態へのすべての変更を削除するために使用されます。 * save()が呼び出された回数より多くrestore()を呼び出すとエラーになります。 * /

例:回転ノブのようなカスタムビュー(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();
}
1
RickPat
Matrix rotateMatrix = new Matrix();
rotateMatrix.postRotate(rotation);
rotatedBitmap = Bitmap.createBitmap(loadedImage, 0, 0,loadedImage.getWidth(), loadedImage.getHeight(),rotateMatrix, false);
1
Saurabh Omer

上記の回答でOutOfMemoryの問題に直面した場合は、以下を使用してください:

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

マトリックスを使用して元のビットマップの領域を50%にスケーリングし、サイズが200k未満になるまでビットマップを圧縮します Androidでビットマップを特定のバイトサイズに圧縮

0
Anh Duy

次のコードを参照して、動作しているようです。コードでは、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);
        }
    });
}
}
0
Rajdeep Dua

前の答えはすべて、ビットマップへのこの変更がビューで行われていることを前提としています。ただし、私の場合は、保存するために変更を加えていました。同様の船に乗っている人には答えると思いました。

翻訳を行うには2つの方法があります。以下の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)
0
drspaceboo