web-dev-qa-db-fra.com

Mise à l'échelle et rotation de bitmap à l'aide de Matrix sous Android

J'essaie de redimensionner et de faire pivoter en une seule opération avant de créer le bitmap final, mais le preRotate, post Concat ne semble pas fonctionner.

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

Il applique uniquement l'échelle et non la rotation.

29
Taranfx

S'il vous plaît vérifier ce lien pour plus de détails

http://www.anddev.org/resize_and_rotate_image_-_example-t621.html

C'est le 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);
    }
}
38
Arslan

La réponse a été donnée, mais pour clarifier les choses à quiconque lit ceci: 

1) si vous souhaitez effectuer UNE seule transformation dans votre bitmap, vous POUVEZ utiliser SET (setRotate, setScale, etc.). 

Mais notez que tout appel à une méthode "set" annule les autres transformations. C'est comme une nouvelle matrice. C'est pourquoi la rotation d'OP ne fonctionnait pas. Ces appels ne sont pas effectués ligne par ligne. C'est comme si elles étaient planifiées au moment de l'exécution par le GPU lorsque le nouveau bitmap est dessiné. C'est comme si, lors de la résolution de votre matrice, le GPU l'avait fait pivoter, mais avait ensuite créé une nouvelle échelle, en ignorant la matrice précédente.

2) si vous souhaitez effectuer plus d'une transformation, vous DEVEZ utiliser les méthodes "pre" ou "post". 

Et quelle est la différence entre un postRotate et un préRotate, par exemple? Eh bien, cette matière mathématique matricielle n’est pas ma force, mais ce que je sais, c’est que les cartes graphiques effectuent ces transformations en utilisant la multiplication matricielle. Cela semble être beaucoup plus efficace. Et pour autant que je me souvienne de l'école, lors de la multiplication de matrices, l'ordre IS est important. A X B! = B X A. Ainsi, mettre une matrice à l'échelle et la faire pivoter est différente de la faire pivoter, puis la redimensionner. 

BUUUUT, dans la mesure où le résultat final à l'écran est le même, nous, programmeurs de haut niveau, n'avons généralement pas besoin de connaître ces différences. Le GPU fait. 

Eh bien, dans les rares cas où vous effectuez des opérations matricielles vraiment compliquées, et que les résultats ne sont pas ce que vous attendiez ou que les performances sont terribles et que vous devez comprendre en profondeur ces méthodes pour corriger votre code, la documentation Android ne peut pas être beaucoup aide quand même. Au lieu de cela, un bon livre d'algèbre linéaire serait votre meilleur ami. ;)

38
PFROLIM

Canvas contient une pile de matrice et vous pouvez l'utiliser avec les méthodes suivantes:

Canvas.save ()

Doc: /** * Enregistre la matrice actuelle et clip sur une pile privée . * 

* Appels suivants pour traduire, redimensionner, faire pivoter, incliner, concattre ou clipRect, * clipPath fonctionnera comme d’habitude, mais lors de l’appel d’équilibrage à * restore () est faite, ces appels seront oubliés, et les réglages que * existait avant la sauvegarde () sera rétabli . * * @return La valeur à passer à restoreToCount () pour équilibrer cette sauvegarde () * /

Canvas.restore ()

Doc: /** * Cet appel met en balance un appel précédent à save () et est utilisé pour supprimer tous les * modifications de l'état de la matrice/du clip depuis le dernier appel de sauvegarde. Il est * une erreur pour appeler restore () plus de fois que save () a été appelée . * /

exemple: Une vue personnalisée (Android) qui ressemble à un bouton rotatif (par exemple un potentiomètre)

@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

Si vous faites face au problème de OutOfMemory avec les réponses ci-dessus, utilisez-le ci-dessous:

Bitmap MyFinalBitmap = Bitmap.createBitmap(CurrentBitmap, 0, 0,CurrentBitmap.getWidth()/2, CurrentBitmap.getHeight()/2,matrix, true);
0
Hiren Patel
Matrix rotateMatrix = new Matrix();
rotateMatrix.postRotate(rotation);
rotatedBitmap = Bitmap.createBitmap(loadedImage, 0, 0,loadedImage.getWidth(), loadedImage.getHeight(),rotateMatrix, false);
0
Saurabh Omer

Reportez-vous au code suivant, semble fonctionner. Dans votre code, vous définissez Matrix comme m, mais vous y référez comme 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

Toutes les réponses précédentes supposent que cette modification de l'image bitmap est en cours de modification dans une vue. Cependant, dans mon cas, je faisais le changement pour être sauvé. Je pensais répondre à ceux qui se trouvaient dans un bateau similaire.

Il y a deux façons de traduire. En dessous de dx se trouve la translation selon l'axe des X et dy de la translation dans l'axe des ordonnées. Les autres variables doivent être explicites.

1 - Traduction dans l'image (sans rotation)

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

2 - matrice complexe

 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