访问ARGB_8888 Android Bitmap中的原始数据

发布于 2024-10-17 17:08:42 字数 1847 浏览 9 评论 0原文

我正在尝试使用 copyPixelsToBuffercopyPixelsFromBuffer 方法在 Android 上访问 ARGB_8888 格式的位图的原始数据。然而,这些调用的调用似乎总是将 alpha 通道应用于 rgb 通道。我需要 byte[] 或类似的原始数据(通过 JNI 传递;是的,我知道 Android 2.2 中的 bitmap.h,不能使用它)。

这是一个示例:

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

日志显示

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

我了解 argb 通道的顺序是不同的;没关系。但我不 希望将 Alpha 通道应用于每个副本(这似乎是它正在做的事情)。

这是 copyPixelsToBuffercopyPixelsFromBuffer 应该如何工作的吗?有什么方法可以获取 byte[] 中的原始数据吗?

添加以响应以下答案:

copyPixelsToBuffer 之前放入 buffer.order(ByteOrder.nativeOrder()); 确实会更改结果,但是仍然不是我想要的方式:

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

似乎遇到了本质上相同的问题(alpha 应用于每个copyPixelsFrom/ToBuffer)。

I am trying to access the raw data of a Bitmap in ARGB_8888 format on Android, using the copyPixelsToBuffer and copyPixelsFromBuffer methods. However, invocation of those calls seems to always apply the alpha channel to the rgb channels. I need the raw data in a byte[] or similar (to pass through JNI; yes, I know about bitmap.h in Android 2.2, cannot use that).

Here is a sample:

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

The log then shows

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

I understand that the order of the argb channels is different; that's fine. But I don't
want the alpha channel to be applied upon every copy (which is what it seems to be doing).

Is this how copyPixelsToBuffer and copyPixelsFromBuffer are supposed to work? Is there any way to get the raw data in a byte[]?

Added in response to answer below:

Putting in buffer.order(ByteOrder.nativeOrder()); before the copyPixelsToBuffer does change the result, but still not in the way I want it:

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

Seems to suffer from essentially the same problem (alpha being applied upon each copyPixelsFrom/ToBuffer).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

两个我 2024-10-24 17:08:42

访问位图中数据的一种方法是使用 getPixels() 方法。下面你可以找到一个例子,我用来从 argb 数据获取灰度图像,然后从字节数组返回位图(当然,如果你需要 rgb,你保留 3x 字节并将它们全部保存......):

/*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;
   }

}

One way to access data in Bitmap is to use getPixels() method. Below you can find an example I used to get grayscale image from argb data and then back from byte array to Bitmap (of course if you need rgb you reserve 3x bytes and save them all...):

/*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;
   }

}
能否归途做我良人 2024-10-24 17:08:42

我的猜测是,这可能与您正在使用的 ByteBuffer 的字节顺序有关。 ByteBuffer 默认使用大端字节序。
在缓冲区上设置字节顺序

buffer.order(ByteOrder.nativeOrder());

看看是否有帮助。

此外,copyPixelsFromBuffer/copyPixelsToBuffer 不会以任何方式更改像素数据。它们是原始复制的。

My guess is that this might have to do with the byte order of the ByteBuffer you are using. ByteBuffer uses big endian by default.
Set endianess on the buffer with

buffer.order(ByteOrder.nativeOrder());

See if it helps.

Moreover, copyPixelsFromBuffer/copyPixelsToBuffer does not change the pixel data in any way. They are copied raw.

旧伤还要旧人安 2024-10-24 17:08:42

我意识到这非常陈旧,现在可能对您没有帮助,但我最近在尝试让 copyPixelsFromBuffer 在我的应用程序中工作时遇到了这个问题。 (谢谢你问这个问题,顺便说一句!你在调试中节省了我大量的时间。)我添加这个答案是希望它能帮助像我这样的人继续前进......

虽然我还没有使用它来确保它有效,看起来像这样,从 API 级别 19 开始,我们最终将有一种方法来指定不在 Bitmap 中“应用 alpha”(也称为预乘)。他们添加了 setPremultiplied(boolean)< /code>方法通过允许我们指定 false 来应对类似的情况。

我希望这有帮助!

I realize this is very stale and probably won't help you now, but I came across this recently in trying to get copyPixelsFromBuffer to work in my app. (Thank you for asking this question, btw! You saved me tons of time in debugging.) I'm adding this answer in the hopes it helps others like me going forward...

Although I haven't used this yet to ensure that it works, it looks like that, as of API Level 19, we'll finally have a way to specify not to "apply the alpha" (a.k.a. premultiply) within Bitmap. They're adding a setPremultiplied(boolean) method that should help in situations like this going forward by allowing us to specify false.

I hope this helps!

赠意 2024-10-24 17:08:42

这是一个老问题,但我遇到了同样的问题,只是发现位图字节已预乘,您可以将位图(从 API 19 开始)设置为不预乘缓冲区,但在 API 中他们不做任何保证。

来自文档

public final void setPremultiplied(boolean premultiplied)

设置位图是否应将其数据视为预乘。
出于性能原因,位图始终被视图系统和 Canvas 视为预乘。在位图中存储未预乘的数据(通过 setPixelsetPixelsBitmapFactory.Options.inPremultiplied)可能会导致不正确的混合,如果由框架绘制。

此方法不会影响没有 Alpha 通道的位图的行为,或者如果 hasAlpha() 返回 false。

使用颜色未预乘的源位图调用 createBitmapcreateScaledBitmap 可能会导致 RuntimeException,因为这些函数需要绘图源,不支持未预乘的位图。

This is an old question, but i got to the same issue, and just figured out that the bitmap byte are pre-multiplied, you can set the bitmap (as of API 19) to not pre-multiply the buffer, but in the API they make no guarantee.

From the docs:

public final void setPremultiplied(boolean premultiplied)

Sets whether the bitmap should treat its data as pre-multiplied.
Bitmaps are always treated as pre-multiplied by the view system and Canvas for performance reasons. Storing un-pre-multiplied data in a Bitmap (through setPixel, setPixels, or BitmapFactory.Options.inPremultiplied) can lead to incorrect blending if drawn by the framework.

This method will not affect the behaviour of a bitmap without an alpha channel, or if hasAlpha() returns false.

Calling createBitmap or createScaledBitmap with a source Bitmap whose colors are not pre-multiplied may result in a RuntimeException, since those functions require drawing the source, which is not supported for un-pre-multiplied Bitmaps.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文