将 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)
我可以使提取像素的代码基于以上输出工作,但我不喜欢它,因为我无法解释我希望有人会做的行为:)
谢谢!
让我们来看看实现。 getPixel
和 copyPixelsToBuffer
都只是调用它们的本地对应项。
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
格式存储。
我对 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)
我可以使提取像素的代码基于以上输出工作,但我不喜欢它,因为我无法解释我希望有人会做的行为:)
谢谢!
让我们来看看实现。 getPixel
和 copyPixelsToBuffer
都只是调用它们的本地对应项。
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
格式存储。