将 Java 位图打包到 ByteBuffer - 字节顺序与像素格式和字节顺序不匹配 (ARM)

Packing Java bitmap into ByteBuffer - byte order doesn't match pixel format and endianness (ARM)

我对 ByteBuffer 中位图像素的内部表示有点困惑(在 ARM/little 字节序上测试):

1) 在 Java 图层中,我创建了一个 ARGB 位图并用 0xff112233 颜色填充它:

Bitmap sampleBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(sampleBitmap);
Paint paint = new Paint();
paint.setStyle(Paint.Style.FILL);

paint.setColor(Color.rgb(0x11,0x22, 0x33));
canvas.drawRect(0,0, sampleBitmap.getWidth(), sampleBitmap.getHeight(), paint);

测试,sampleBitmap.getPixel(0,0)确实returns0xff112233匹配ARGB像素格式。

2) 位图在传递给native层之前直接打包进ByteBuffer:

 final int byteSize = sampleBitmap.getAllocationByteCount();
 ByteBuffer byteBuffer = ByteBuffer.allocateDirect(byteSize);
 //byteBuffer.order(ByteOrder.LITTLE_ENDIAN);// See below
 sampleBitmap.copyPixelsToBuffer(byteBuffer);

为了测试,无论缓冲区的顺序设置如何,在调试器中我看到字节布局与 ARGB 不太匹配,但更像是大端 RGBA(或小端 ABGR!?)

  byteBuffer.rewind();
  final byte [] out = new byte[4];
  byteBuffer.get(out, 0, out.length);
out = {byte[4]@12852} 
 0 = (0x11)
 1 = (0x22)
 2 = (0x33)
 3 = (0xFF)

现在,我将这个位图传递到我必须提取像素的本机层,我希望 Bitmap.Config.ARGB_8888 被表示,具体取决于缓冲区的字节顺序:

a) byteBuffer.order(ByteOrder.LITTLE_ENDIAN):

out = {byte[4]@12852} 
 0 = (0x33)
 1 = (0x22)
 2 = (0x11)
 3 = (0xFF)

b) byteBuffer.order(ByteOrder.BIG_ENDIAN):

out = {byte[4]@12852} 
 0 = (0xFF)
 1 = (0x11)
 2 = (0x22)
 3 = (0x33)

我可以使提取像素的代码基于以上输出工作,但我不喜欢它,因为我无法解释我希望有人会做的行为:)

谢谢!

让我们来看看实现。 getPixelcopyPixelsToBuffer 都只是调用它们的本地对应项。 Bitmap_getPixels 指定输出格式:

SkImageInfo dstInfo = SkImageInfo::Make(1, 1, kBGRA_8888_SkColorType, kUnpremul_SkAlphaType, sRGB);
bitmap.readPixels(dstInfo, &dst, dstInfo.minRowBytes(), x, y);

它有效地要求位图给出转换为 BGRA_8888 的像素值(由于本机和 java 字节顺序不同,变为 ARGB)。

Bitmap_copyPixelsToBuffer 反过来只是复制原始数据:

memcpy(abp.pointer(), src, bitmap.computeByteSize());

并且没有任何转换。它基本上 returns 数据的格式与它用来存储它的格式相同。让我们看看这个内部格式是什么。

Bitmap_creator 用于创建新的位图,它从调用

传递的配置中获取格式
SkColorType colorType = GraphicsJNI::legacyBitmapConfigToColorType(configHandle);

查看 legacyBitmapConfigToColorType 实现,ARGB_8888(索引为 5)变为 kN32_SkColorType

kN32_SkColorType 来自skia library, so looking at the definitions 找到评论

kN32_SkColorType is an alias for whichever 32bit ARGB format is the 
"native" form for skia's blitters. Use this if you don't have a swizzle 
preference for 32bit pixels.

下面是定义:

#if SK_PMCOLOR_BYTE_ORDER(B,G,R,A)
    kN32_SkColorType = kBGRA_8888_SkColorType,
#elif SK_PMCOLOR_BYTE_ORDER(R,G,B,A)
    kN32_SkColorType = kRGBA_8888_SkColorType,

SK_PMCOLOR_BYTE_ORDER 被定义为 here,它表示 SK_PMCOLOR_BYTE_ORDER(R,G,B,A) will be true on a little endian machine,这就是我们的情况。所以这意味着位图在内部以 kRGBA_8888_SkColorType 格式存储。