访问ARGB_8888 Android Bitmap中的原始数据
-
14-11-2019 - |
题
我正在尝试使用 Android 上的 ARGB_8888 格式访问位图的原始数据 copyPixelsToBuffer
和 copyPixelsFromBuffer
方法。然而,这些调用的调用似乎总是将 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频道(这似乎是它在做的事情)。
是这样吗 copyPixelsToBuffer
和 copyPixelsFromBuffer
应该可以工作吗?有没有 任何 如何获取 byte[] 中的原始数据?
添加以回应以下答案:
投入 buffer.order(ByteOrder.nativeOrder());
之前 copyPixelsToBuffer
确实改变了结果,但仍然不是我想要的方式:
pixel before = ef234567
value before = ef614121
value after = ffffff41
pixel after = ff41ffff
似乎遇到了本质上相同的问题(alpha 应用于每个 copyPixelsFrom/ToBuffer
).
解决方案
我意识到这很陈旧,现在可能对你没有帮助,但我最近在尝试获取时遇到了这个 copyPixelsFromBuffer
在我的应用程序中工作。(谢谢你问这个问题,顺便说一句!你在调试中节省了我大量的时间。)我添加这个答案是希望它能帮助像我这样的其他人继续前进......
虽然我还没有使用它来确保它的工作原理,但看起来,从 API 级别 19 开始,我们最终将有一种方法来指定不“应用 alpha”(也称为“应用 alpha”)。预乘)内 Bitmap
. 。他们正在添加一个 setPremultiplied(boolean)
通过允许我们指定,应该有助于解决类似的情况 false
.
我希望这有帮助!
其他提示
访问位图中数据的一种方法是使用 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;
}
}
我的猜测是,这可能与您正在使用的 ByteBuffer 的字节顺序有关。ByteBuffer 默认使用大端字节序。设置缓冲区的字节顺序
buffer.order(ByteOrder.nativeOrder());
看看是否有帮助。
此外,copyPixelsFromBuffer/copyPixelsToBuffer 不会以任何方式更改像素数据。它们是原始复制的。
这是一个老问题,但我遇到了同样的问题,只是发现位图字节已预乘,您可以将位图(从 API 19 开始)设置为不预乘缓冲区,但在 API 中他们不做任何保证。
从 文档:
public final void setPremultiplied(boolean premultiplied)
设置位图是否应将其数据视为预乘。出于性能原因,位图始终被视图系统和 Canvas 视为预乘。将未预乘的数据存储在位图中(通过
setPixel
,setPixels
, , 或者BitmapFactory.Options.inPremultiplied
) 如果由框架绘制,可能会导致不正确的混合。此方法不会影响没有 Alpha 通道的位图的行为,或者如果
hasAlpha()
返回假。呼唤
createBitmap
或者createScaledBitmap
使用颜色未预乘的源位图可能会导致RuntimeException
, ,因为这些函数需要绘制源,而未预乘的位图不支持该源。