Pergunta

Estou tentando acessar os dados brutos de um Bitmap no formato ARGB_8888 no Android, usando o copyPixelsToBuffer e copyPixelsFromBuffer métodos.No entanto, a invocação dessas chamadas parece sempre aplicar o canal alfa aos canais RGB.Preciso dos dados brutos em um byte[] ou similar (para passar pelo JNI;sim, eu sei sobre bitmap.h no Android 2.2, não posso usar isso).

Aqui está um exemplo:

    // Create 1x1 Bitmap with alpha channel, 8 bits per channel
    Bitmap one = Bitmap.createBitmap(1,1,Bitmap.Config.ARGB_8888);
    one.setPixel(0,0,0xef234567);
    Log.v("?","hasAlpha() = "+Boolean.toString(one.hasAlpha()));
    Log.v("?","pixel before = "+Integer.toHexString(one.getPixel(0,0)));

    // Copy Bitmap to buffer
    byte[] store = new byte[4];
    ByteBuffer buffer  = ByteBuffer.wrap(store);
    one.copyPixelsToBuffer(buffer);

    // Change value of the pixel
    int value=buffer.getInt(0);
    Log.v("?", "value before = "+Integer.toHexString(value));
    value = (value >> 8) | 0xffffff00;
    buffer.putInt(0, value);
    value=buffer.getInt(0);
    Log.v("?", "value after = "+Integer.toHexString(value));

    // Copy buffer back to Bitmap
    buffer.position(0);
    one.copyPixelsFromBuffer(buffer);
    Log.v("?","pixel after = "+Integer.toHexString(one.getPixel(0,0)));

O log então mostra

hasAlpha() = true
pixel before = ef234567
value before = 214161ef
value after = ffffff61
pixel after = 619e9e9e

Entendo que a ordem dos canais argb é diferente;isso é bom.Mas não quero que o canal alfa seja aplicado em todas as cópias (que é o que parece estar fazendo).

É assim que copyPixelsToBuffer e copyPixelsFromBuffer deveriam funcionar?Existe qualquer maneira de obter os dados brutos em um byte []?

Adicionado em resposta à resposta abaixo:

Colocando buffer.order(ByteOrder.nativeOrder()); antes de o copyPixelsToBuffer muda o resultado, mas ainda não da maneira que desejo:

pixel before = ef234567
value before = ef614121
value after = ffffff41
pixel after = ff41ffff

Parece sofrer essencialmente do mesmo problema (alfa sendo aplicado em cada copyPixelsFrom/ToBuffer).

Foi útil?

Solução

Sei que isso é muito obsoleto e provavelmente não ajudará você agora, mas me deparei com isso recentemente ao tentar obter copyPixelsFromBuffer para trabalhar no meu aplicativo.(Obrigado por fazer esta pergunta, aliás!Você me economizou muito tempo na depuração.) Estou adicionando esta resposta na esperança de que ajude outras pessoas como eu no futuro ...

Embora eu ainda não tenha usado isso para garantir que funcione, parece que, a partir do nível 19 da API, finalmente teremos uma maneira de especificar para não "aplicar o alfa" (também conhecido comopré-multiplicar) dentro Bitmap.Eles estão adicionando um setPremultiplied(boolean) método que deve ajudar em situações como esta daqui para frente, permitindo-nos especificar false.

Eu espero que isso ajude!

Outras dicas

Uma maneira de acessar dados em Bitmap é usar o método getPixels().Abaixo você pode encontrar um exemplo que usei para obter uma imagem em escala de cinza de dados argb e depois voltar da matriz de bytes para o bitmap (é claro, se você precisar de rgb, você reserva 3x bytes e salva todos eles ...):

/*Free to use licence by Sami Varjo (but nice if you retain this line)*/

public final class BitmapConverter {

    private BitmapConverter(){};

   /**
    * Get grayscale data from argb image to byte array
    */
   public static byte[] ARGB2Gray(Bitmap img)
   {

       int width = img.getWidth();
       int height = img.getHeight();

       int[] pixels = new int[height*width];
       byte grayIm[] = new byte[height*width];

       img.getPixels(pixels,0,width,0,0,width,height);

       int pixel=0;
       int count=width*height;

       while(count-->0){
           int inVal = pixels[pixel];

           //Get the pixel channel values from int 
           double r = (double)( (inVal & 0x00ff0000)>>16 );
           double g = (double)( (inVal & 0x0000ff00)>>8  );
           double b = (double)(  inVal & 0x000000ff)      ;

           grayIm[pixel++] = (byte)( 0.2989*r + 0.5870*g + 0.1140*b );
       }

       return grayIm;
   }

   /**
    * Create a gray scale bitmap from byte array
    */
   public static Bitmap gray2ARGB(byte[] data, int width, int height)
   {
       int count = height*width;
       int[] outPix = new int[count];
       int pixel=0;
       while(count-->0){
           int val = data[pixel] & 0xff; //convert byte to unsigned
           outPix[pixel++] = 0xff000000 | val << 16 | val << 8 | val ;
       }

       Bitmap out =  Bitmap.createBitmap(outPix,0,width,width, height, Bitmap.Config.ARGB_8888);
       return out;
   }

}

Meu palpite é que isso pode ter a ver com a ordem dos bytes do ByteBuffer que você está usando.ByteBuffer usa big endian por padrão.Defina endianess no buffer com

buffer.order(ByteOrder.nativeOrder());

Veja se isso ajuda.

Além disso, copyPixelsFromBuffer/copyPixelsToBuffer não altera os dados de pixel de forma alguma.Eles são copiados brutos.

Esta é uma pergunta antiga, mas cheguei ao mesmo problema e descobri que os bytes do bitmap são pré-multiplicados, você pode definir o bitmap (a partir da API 19) para não pré-multiplicar o buffer, mas na API eles não oferecem nenhuma garantia.

De os documentos:

public final void setPremultiplied(boolean premultiplied)

Define se o bitmap deve tratar seus dados como pré-multiplicados.Os bitmaps são sempre tratados como pré-multiplicados pelo sistema de visualização e pelo Canvas por motivos de desempenho.Armazenar dados não pré-multiplicados em um Bitmap (através de setPixel, setPixels, ou BitmapFactory.Options.inPremultiplied) pode levar a uma combinação incorreta se for desenhado pela estrutura.

Este método não afetará o comportamento de um bitmap sem canal alfa, ou se hasAlpha() retorna falso.

Chamando createBitmap ou createScaledBitmap com um Bitmap de origem cujas cores não são pré-multiplicadas pode resultar em RuntimeException, já que essas funções exigem o desenho da fonte, o que não é compatível com Bitmaps não pré-multiplicados.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top