Android: como girar um bitmap em um ponto central

84

Há mais de um dia procuro uma solução para este problema, mas nada ajuda, nem mesmo as respostas aqui. A documentação também não explica nada.

Estou simplesmente tentando obter uma rotação na direção de outro objeto. O problema é que o bitmap não é girado em torno de um ponto fixo, mas sim em torno dos bitmaps (0,0).

Aqui está o código com o qual estou tendo problemas:

  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;

A parte estranha é que não importa como eu altero os valores em pre/ postTranslate()e os argumentos flutuantes em setRotation(). Alguém pode me ajudar e me empurrar na direção certa? :)

Stefan
fonte
1
Presumo que o código acima seja após várias tentativas de variações. Parece que mtx.setRotate (dir, x, y) deve fazer o truque por si só, sem todas as coisas pré / pós. Além disso, você não precisa redefinir uma newmatriz recém- editada. Já é a identidade.
Mark Storer

Respostas:

101

Espero que a seguinte sequência de código o ajude:

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

Se você verificar o seguinte método de ~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)

isso explicaria o que ele faz com rotação e translação.

Sudar Nimalan
fonte
1
Você pode fazer isso com o Bitmap.createBitmap que leva uma Matrix? Como você está calculando targetWidth e targetHeight? Trigonometria simples, suponho, mas existe uma maneira "mais fácil"?
Verifique a resposta abaixo, (Desculpe, não consegui adicionar o código nos comentários)
Sudar Nimalan
a qualidade fica ruim ao aplicar isso. qualquer solução ?
MSaudi
1
alterar canvas.drawBitmap (fonte, matriz, novo Paint ()); com canvas.drawBitmap (fonte, matriz, novo Paint (Paint.ANTI_ALIAS_FLAG));
Sudar Nimalan
3
Apenas comentando que esse processo consome muita memória e não deve ser usado em métodos de etapas.
MLProgrammer-CiM
79

Editado : código otimizado.

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

Para obter bitmap de recursos:

Bitmap source = BitmapFactory.decodeResource(this.getResources(), R.drawable.your_img);
Arvis
fonte
Tente fazer isso para girar um bitmap com qualquer w ou h: Matrix matrix = new Matrix (); matrix.postRotate (90); bitmapPicture = Bitmap.createBitmap (bitmapPicture, 0, 0, bitmapPicture.getWidth (), bitmapPicture.getHeight (), matriz, verdadeiro);
Mark Molina
Isso funcionou perfeitamente para mim, pois estou definindo os parâmetros para os pontos da imagem. Você apenas precisa ter certeza de usar a matriz ao desenhar o bitmap na tela.
a54studio de
6
Tem certeza de que isso é eficiente? Se RotateBitmap () estava em um loop executando N vezes, dependendo da resolução do bitmap, você poderia estar desperdiçando uma enorme quantidade de memória, bem como todas as novas alocações de objetos Matrix.
Ryhan
Não testado, mas docs diz: "Se o bitmap de origem for imutável e o subconjunto solicitado for igual ao próprio bitmap de origem, o bitmap de origem será retornado e nenhum novo bitmap será criado." Já o objeto Matrix é apenas transformar a classe de coordenadas. Você pode otimizar livremente o código para repetição e usar o mesmo objeto Matrix em loops.
Arvis
Para sua informação, o bitmap precisa ser mutável
kip2
25

Voltei a esse problema agora que estamos finalizando o jogo e só pensei em postar o que funcionou para mim.

Este é o método para girar a Matriz:

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

( this.getCenterX()é basicamente a posição X dos bitmaps + a largura dos bitmaps / 2)

E o método para desenhar o bitmap (chamado por meio de uma RenderManagerclasse):

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

Portanto, é bastante simples, mas acho um pouco estranho que não consegui fazê-lo funcionar por setRotateseguido de postTranslate. Talvez alguns saibam por que isso não funciona? Agora todos os bitmaps giram adequadamente, mas não sem uma pequena diminuição na qualidade do bitmap: /

De qualquer forma, obrigado pela ajuda!

Ste
fonte
O código de resposta anterior também funciona. com o mesmo problema de qualidade.
MSaudi
7

Você também pode girar o ImageViewusando um 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
fonte
4
Você está respondendo à pergunta errada. Ele não quer girar a visualização, apenas um único bitmap dentro dela.
Mark Storer
Como eu experimentei, o método setFillAfter (bool) não está funcionando como esperado na API Android lvl 11 e abaixo. Portanto, os desenvolvedores não podem usar o evento de rotação contínua em objetos de visualização, nem obter versões giradas dessas visualizações após a rotação. Então @Mark Storer, Macarse respondeu verdadeiramente do ponto de vista lógico se você definir a duração da animação com 0 ou algo próximo a 0.
Gökhan Barış Aker
5

Você pode usar algo como o seguinte:


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
fonte
mesmo problema para todas as soluções: diminuição na qualidade do bitmap
MSaudi
alterar canvas.drawBitmap (fonte, matriz, novo Paint ()); com canvas.drawBitmap (fonte, matriz, novo Paint (Paint.ANTI_ALIAS_FLAG));
Sudar Nimalan
qual é a configuração que você está usando?
Sudar Nimalan,
Postei o código abaixo como resposta. não poderia escrever no comentário. Muito obrigado, senhor :)
MSaudi
1

Usei essas configurações e ainda tenho o problema de pixelização:

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
fonte
1
você poderia tentar setFilterBitmap, opções setDither
Sudar Nimalan
1
Sim, funcionou perfeitamente. Muito obrigado Sudar, você realmente me ajudou muito.
MSaudi
1
Adicionando link ao seu próprio acompanhamento com código atualizado: 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
fonte