Android: comment faire pivoter une image bitmap sur un point central

84

Je cherchais depuis plus d'un jour une solution à ce problème mais rien n'y fait, même les réponses ici. La documentation n'explique rien non plus.

J'essaie simplement d'obtenir une rotation dans la direction d'un autre objet. Le problème est que le bitmap ne pivote pas autour d'un point fixe, mais plutôt autour des bitmaps (0,0).

Voici le code avec lequel je rencontre des problèmes:

  Matrix mtx = new Matrix();
  mtx.reset();
  mtx.preTranslate(-centerX, -centerY);
  mtx.setRotate((float)direction, -centerX, -centerY);
  mtx.postTranslate(pivotX, pivotY);
  Bitmap rotatedBMP = Bitmap.createBitmap(bitmap, 0, 0, spriteWidth, spriteHeight, mtx, true);
  this.bitmap = rotatedBMP;

La partie étrange est que peu importe comment je change les valeurs dans pre/ postTranslate()et les arguments float dans setRotation(). Quelqu'un peut-il s'il vous plaît m'aider et me pousser dans la bonne direction? :)

Stefan
la source
1
Je suppose que le code ci-dessus est après plusieurs tentatives de variations. Il semble que mtx.setRotate (dir, x, y) devrait faire l'affaire par lui-même, sans tous les éléments pré / post. De plus, vous n'avez pas besoin de réinitialiser une newmatrice fraîchement éditée. C'est déjà l'identité.
Mark Storer

Réponses:

101

J'espère que la séquence de code suivante vous aidera:

Bitmap targetBitmap = Bitmap.createBitmap(targetWidth, targetHeight, config);
Canvas canvas = new Canvas(targetBitmap);
Matrix matrix = new Matrix();
matrix.setRotate(mRotation,source.getWidth()/2,source.getHeight()/2);
canvas.drawBitmap(source, matrix, new Paint());

Si vous cochez la méthode suivante ~frameworks\base\graphics\java\android\graphics\Bitmap.java

public static Bitmap createBitmap(Bitmap source, int x, int y, int width, int height,
        Matrix m, boolean filter)

cela expliquerait ce qu'il fait avec la rotation et la traduction.

Sudar Nimalan
la source
1
Pouvez-vous le faire avec le Bitmap.createBitmap qui prend une matrice? Comment calculez-vous targetWidth et targetHeight? Simple déclencheur, je suppose, mais y a-t-il un moyen "plus simple"?
Veuillez vérifier la réponse ci-dessous, (Désolé, je n'ai pas pu ajouter de code dans les commentaires)
Sudar Nimalan
la qualité se détériore en appliquant cela. Toute solution ?
MSaudi
1
changer canvas.drawBitmap (source, matrice, nouveau Paint ()); avec canvas.drawBitmap (source, matrice, nouveau Paint (Paint.ANTI_ALIAS_FLAG));
Sudar Nimalan
3
Il suffit de dire que ce processus est très gourmand en mémoire et ne devrait pas être utilisé dans les méthodes par étapes.
MLProgrammer-CiM
79

Edité : code optimisé.

public static Bitmap RotateBitmap(Bitmap source, float angle)
{
      Matrix matrix = new Matrix();
      matrix.postRotate(angle);
      return Bitmap.createBitmap(source, 0, 0, source.getWidth(), source.getHeight(), matrix, true);
}

Pour obtenir Bitmap à partir des ressources:

Bitmap source = BitmapFactory.decodeResource(this.getResources(), R.drawable.your_img);
Arvis
la source
Essayez ceci pour faire pivoter une image bitmap avec n'importe quel w ou h: Matrix matrix = new Matrix (); matrice.postRotate (90); bitmapPicture = Bitmap.createBitmap (bitmapPicture, 0, 0, bitmapPicture.getWidth (), bitmapPicture.getHeight (), matrix, true);
Mark Molina
Cela a parfaitement fonctionné pour moi car je règle les paramètres des points d'image. Vous devez simplement vous assurer d'utiliser la matrice lorsque vous dessinez le bitmap sur le canevas.
a54studio
6
Etes-vous sûr que c'est efficace? Si RotateBitmap () était dans une boucle exécutant N fois, selon la résolution du bitmap, vous pourriez gaspiller une énorme quantité de mémoire ainsi que toutes ces nouvelles allocations d'objets Matrix.
Ryhan
Non testé mais la documentation indique: "Si le bitmap source est immuable et que le sous-ensemble demandé est le même que le bitmap source lui-même, alors le bitmap source est renvoyé et aucun nouveau bitmap n'est créé." Quant à l'objet Matrix, il ne fait que transformer la classe de coordonnées. Vous pouvez optimiser librement le code pour la répétition et utiliser le même objet Matrix dans les boucles.
Arvis
Pour info, le bitmap doit être mutable
kip2
25

Je suis revenu sur ce problème maintenant que nous sommes en train de finaliser le jeu et j'ai juste pensé poster ce qui fonctionnait pour moi.

Voici la méthode de rotation de la matrice:

this.matrix.reset();
this.matrix.setTranslate(this.floatXpos, this.floatYpos);
this.matrix.postRotate((float)this.direction, this.getCenterX(), this.getCenterY()); 

( this.getCenterX()est essentiellement la position X des bitmaps + la largeur des bitmaps / 2)

Et la méthode pour dessiner le bitmap (appelé via une RenderManagerclasse):

canvas.drawBitmap(this.bitmap, this.matrix, null);

C'est donc simple mais je trouve un peu étrange que je ne puisse pas le faire fonctionner, setRotatesuivi de postTranslate. Peut-être que certains savent pourquoi cela ne fonctionne pas? Maintenant, tous les bitmaps tournent correctement mais ce n'est pas sans une légère diminution de la qualité du bitmap: /

Quoi qu'il en soit, merci pour votre aide!

Ste
la source
Le code de réponse précédent fonctionne également. avec le même problème de qualité.
MSaudi
7

Vous pouvez également faire pivoter le en ImageViewutilisant RotateAnimation:

RotateAnimation rotateAnimation = new RotateAnimation(from, to,
Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,
                0.5f);
rotateAnimation.setInterpolator(new LinearInterpolator());
rotateAnimation.setDuration(ANIMATION_DURATION);
rotateAnimation.setFillAfter(true);

imageView.startAnimation(rotateAnimation);
Macarse
la source
4
Vous répondez à la mauvaise question. Il ne veut pas faire pivoter la vue, juste un seul bitmap à l'intérieur.
Mark Storer
Comme je l'ai expérimenté, la méthode setFillAfter (bool) ne fonctionne pas comme prévu sur l'API Android lvl 11 et inférieurs. Par conséquent, les développeurs ne peuvent pas utiliser l'événement de rotation continue sur les objets de vue, ni obtenir des versions pivotées de ces vues après la rotation. Alors @Mark Storer, Macarse a répondu vraiment du point de vue logique si vous définissez la durée de l'animation avec 0 ou quelque chose de proche de 0.
Gökhan Barış Aker
5

Vous pouvez utiliser quelque chose comme suivant:


Matrix matrix = new Matrix();
matrix.setRotate(mRotation,source.getWidth()/2,source.getHeight()/2);
RectF rectF = new RectF(0, 0, source.getWidth(), source.getHeight());
matrix.mapRect(rectF);
Bitmap targetBitmap = Bitmap.createBitmap(rectF.width(), rectF.height(), config);
Canvas canvas = new Canvas(targetBitmap);
canvas.drawBitmap(source, matrix, new Paint());

Sudar Nimalan
la source
même problème pour toutes les solutions: diminution de la qualité du bitmap
MSaudi
changer canvas.drawBitmap (source, matrice, nouveau Paint ()); avec canvas.drawBitmap (source, matrice, nouveau Paint (Paint.ANTI_ALIAS_FLAG));
Sudar Nimalan
quelle est la configuration que vous utilisez?
Sudar Nimalan
J'ai posté le code ci-dessous comme réponse. ne pouvait pas l'écrire dans le commentaire. Merci beaucoup monsieur :)
MSaudi
1

Regardez l'échantillon de Google appelé Lunar Lander, l'image du navire y est tournée de manière dynamique.

Exemple de code Lunar Lander

lutin
la source
1

J'ai utilisé ces configurations et j'ai toujours le problème de la pixellisation:

Bitmap bmpOriginal = BitmapFactory.decodeResource(this.getResources(), R.drawable.map_pin);
        Bitmap targetBitmap = Bitmap.createBitmap((bmpOriginal.getWidth()),
                (bmpOriginal.getHeight()), 
                Bitmap.Config.ARGB_8888);
        Paint p = new Paint();
        p.setAntiAlias(true);

        Matrix matrix = new Matrix();       
        matrix.setRotate((float) lock.getDirection(),(float) (bmpOriginal.getWidth()/2),
                (float)(bmpOriginal.getHeight()/2));

        RectF rectF = new RectF(0, 0, bmpOriginal.getWidth(), bmpOriginal.getHeight());
        matrix.mapRect(rectF);

        targetBitmap = Bitmap.createBitmap((int)rectF.width(), (int)rectF.height(), Bitmap.Config.ARGB_8888);


        Canvas tempCanvas = new Canvas(targetBitmap); 
        tempCanvas.drawBitmap(bmpOriginal, matrix, p);
MSaudi
la source
1
pourriez-vous essayer setFilterBitmap, options
setDither
1
Oui, cela a parfaitement fonctionné. Merci beaucoup Sudar, vous m'avez vraiment beaucoup aidé.
MSaudi
1
Ajout d'un lien vers votre propre suivi avec le code mis à jour: stackoverflow.com/questions/13048953/…
Chris Rae
0
matrix.reset();
matrix.setTranslate( anchor.x, anchor.y );
matrix.postRotate((float) rotation , 0,0);
matrix.postTranslate(positionOfAnchor.x, positionOfAnchor.x);
c.drawBitmap(bitmap, matrix, null); 
Louie Almeda
la source