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? :)


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ą.


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

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!


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

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


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

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

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); 
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.