Android: jak obrócić bitmapę w punkcie środkowym

84

Szukałem rozwiązania tego problemu przez ponad dzień, ale nic nie pomaga, nawet odpowiedzi tutaj. Dokumentacja też niczego nie wyjaśnia.

Po prostu próbuję uzyskać obrót w kierunku innego obiektu. Problem polega na tym, że mapa bitowa nie jest obracana wokół ustalonego punktu, ale raczej wokół map bitowych (0,0).

Oto kod, z którym mam problem:

  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;

Dziwne jest to, że nie ma znaczenia, w jaki sposób zmienię wartości w pre/ postTranslate()i argumenty float w setRotation(). Czy ktoś może mi pomóc i popchnąć mnie we właściwym kierunku? :)

Stefan
źródło
1
Rozumiem, że powyższy kod jest po kilku próbach zmian. Wygląda na to, że mtx.setRotate (dir, x, y) powinien załatwić sprawę samodzielnie, bez wszystkich rzeczy przed / post. Nie musisz też resetować świeżo newwyedytowanej matrycy. To już tożsamość.
Mark Storer

Odpowiedzi:

101

Mam nadzieję, że pomoże ci następująca sekwencja kodu:

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

Jeśli zaznaczysz następującą metodę z ~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)

to wyjaśniałoby, co robi z rotacją i translacją.

Sudar Nimalan
źródło
1
Czy możesz to zrobić za pomocą Bitmap.createBitmap, która pobiera Matrix? Jak obliczasz targetWidth i targetHeight? Zakładam, że prosta trygonometria, ale czy istnieje „łatwiejszy” sposób?
Proszę sprawdzić poniższą odpowiedź, (Przepraszam, nie mogłem dodać kodu w komentarzach)
Sudar Nimalan
jakość się pogarsza podczas stosowania tego. jakieś rozwiązanie?
MSaudi
1
zmień canvas.drawBitmap (źródło, macierz, nowy Paint ()); z canvas.drawBitmap (źródło, macierz, nowy Paint (Paint.ANTI_ALIAS_FLAG));
Sudar Nimalan
3
Tylko komentarz, że ten proces jest bardzo wymagający pamięci i nie powinien być używany w metodach krokowych.
MLProgrammer-CiM
79

Edytowano : zoptymalizowany kod.

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

Aby uzyskać Bitmapę z zasobów:

Bitmap source = BitmapFactory.decodeResource(this.getResources(), R.drawable.your_img);
Arvis
źródło
Spróbuj tego do obracania mapy bitowej z dowolnym w lub h: Matrix matrix = new Matrix (); matrix.postRotate (90); bitmapPicture = Bitmap.createBitmap (bitmapPicture, 0, 0, bitmapPicture.getWidth (), bitmapPicture.getHeight (), matrix, true);
Mark Molina,
U mnie to zadziałało idealnie, ponieważ ustawiam parametry punktów obrazu. Musisz tylko upewnić się, że używasz macierzy podczas rysowania mapy bitowej na płótnie.
a54studio
6
Czy na pewno to jest wydajne? Jeśli RotateBitmap () był w pętli wykonującej N razy, w zależności od rozdzielczości bitmapy, możesz marnować ogromną ilość pamięci, a także wszystkie nowe alokacje obiektów Matrix.
Ryhan
Nie przetestowano, ale dokumentacja mówi: „Jeśli źródłowa mapa bitowa jest niezmienna, a żądany podzbiór jest taki sam, jak sama źródłowa mapa bitowa, wówczas zwracana jest źródłowa mapa bitowa i nie jest tworzona nowa mapa bitowa”. W przypadku obiektu Matrix jest to po prostu przekształcanie klasy współrzędnych. Możesz dowolnie optymalizować kod pod kątem powtórzeń i używać tego samego obiektu Matrix w pętlach.
Arvis
FYI, mapa bitowa musi być
zmienna
25

Wróciłem do tego problemu teraz, kiedy finalizujemy grę i po prostu pomyślałem, że opublikuję, co mi pomogło.

Oto metoda obracania Matrycy:

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

( this.getCenterX()to w zasadzie pozycja X map bitowych + szerokość map bitowych / 2)

I metoda rysowania mapy bitowej (wywoływanej przez RenderManagerklasę):

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

Więc jest to pozornie proste, ale wydaje mi się to trochę dziwne, że nie mogłem go uruchomić, setRotatea następnie postTranslate. Może niektórzy wiedzą, dlaczego to nie działa? Teraz wszystkie mapy bitowe obracają się prawidłowo, ale nie jest to pozbawione niewielkiego spadku jakości mapy bitowej: /

W każdym razie dziękuję za pomoc!

Ste
źródło
Poprzedni kod odpowiedzi również działa. z tym samym problemem jakości.
MSaudi
7

Możesz również obrócić ImageViewza pomocą 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
źródło
4
Odpowiadasz na złe pytanie. Nie chce obracać widoku, tylko jedną bitmapę w nim.
Mark Storer
Jak się przekonałem, metoda setFillAfter (bool) nie działa zgodnie z oczekiwaniami na Android API lvl 11 i niższych. Dlatego programiści nie mogą używać zdarzenia ciągłego obrotu na obiektach widoku, ani też nie uzyskują obróconych wersji tych widoków po obróceniu. Tak więc @Mark Storer, Macarse odpowiedział naprawdę z logicznego punktu widzenia, jeśli ustawisz czas trwania animacji na 0 lub coś bliskiego 0.
Gökhan Barış Aker
5

Możesz użyć czegoś takiego jak:


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
źródło
ten sam problem dla wszystkich rozwiązań: spadek jakości bitmapy
MSaudi
zmień canvas.drawBitmap (źródło, macierz, nowy Paint ()); z canvas.drawBitmap (źródło, macierz, nowy Paint (Paint.ANTI_ALIAS_FLAG));
Sudar Nimalan
jaka jest konfiguracja, której używasz?
Sudar Nimalan
W odpowiedzi zamieściłem poniższy kod. nie mógł tego napisać w komentarzu. Dziękuję bardzo proszę pana :)
MSaudi
1

Spójrz na próbkę z Google o nazwie Lunar Lander, tam obraz statku jest dynamicznie obracany.

Przykładowy kod Lunar Lander

krasnoludek
źródło
1

Użyłem tej konfiguracji i nadal mam problem z pikselizacją:

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
źródło
1
czy możesz spróbować setFilterBitmap, setDither options
Sudar Nimalan
1
Tak, to działało doskonale. Dziękuję bardzo Sudar, naprawdę bardzo mi pomogłeś.
MSaudi
1
Dodawanie linku do własnego follow-up ze zaktualizowanym kodem: 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
źródło