【问题标题】:Why is copyPixelsFromBuffer giving incorrect color? setPixels is correct but slow为什么 copyPixelsFromBuffer 给出不正确的颜色? setPixels 是正确的,但速度很慢
【发布时间】:2018-06-06 19:29:26
【问题描述】:

对于我的 android 应用程序,我从本机代码中获得了 ByteBuffer。它包含用于创建位图的像素颜色值。

原图——

我在位图上使用了copyPixelsFromBuffer,但在显示位图时颜色不正确。

这是这种方法的代码 -

方法 1

ByteBuffer buffer = ...

Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
buffer.rewind();
bitmap.copyPixelsFromBuffer(buffer);

大约。时间 - ~0.4 ms
结果 - 错误颜色 -

方法 2

接下来我尝试了setPixels。它仍然会给出错误的颜色,速度会慢 10 倍以上,并为int[] 使用额外的内存。请注意buffer.hasArray()false,所以我无法从缓冲区中获取数组。

ByteBuffer buffer = ...

Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
buffer.rewind();

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

for (int i = 0; i < width * height; i++) {
    int a = buffer.get();
    int r = buffer.get();
    int g = buffer.get();
    int b = buffer.get();
    pixels[i] = a << 24 | r << 16 | g << 8 | b;
}
bitmap.setPixels(pixels, 0, width, 0, 0, width, height);

大约。时间 - ~4.0 ms
结果 - 错误的颜色 -

方法 3

这次我使用了setPixels,但像素值取自IntBuffer 表示ByteBuffer。颜色是正确的,但时间仍然很长,还有额外的内存分配。

ByteBuffer buffer = ...
IntBuffer intBuffer = buffer.asIntBuffer();

Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
buffer.rewind();

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

for (int i = 0; i < width * height; i++) {
    pixels[i] = intBuffer.get();
}
bitmap.setPixels(pixels, 0, width, 0, 0, width, height);

大约。时间 - ~3.0 ms
结果 - 正确颜色 -

关于我为什么使用copyPixelsFromBuffer 得到错误颜色的任何提示?我想使用它而不是 setPixels,因为它更快并且不需要额外的内存分配。

【问题讨论】:

  • 我怀疑是色彩空间问题,但我不太明白如何才能让您的第三次尝试正常工作。使用bitmap.copyPixelsToBuffer() 根据方法 3 的结果创建一个新缓冲区可能是一个不错的实验,然后检查两个缓冲区之间的差异。这可能有助于诊断问题。
  • 只是想到了别的东西:确保缓冲区的字节顺序设置为ByteOrder.LITTLE_ENDIAN。那里的不匹配可能会导致奇怪的颜色效果。
  • 感谢@TedHopp,关于使用copyPixelsToBuffer() 比较缓冲区的提示非常有帮助。我意识到了这个问题 - 缓冲区是 ARGB,但位图需要 RGBA。
  • 很高兴你知道了!您应该将您的答案标记为解决方案。

标签: android performance colors bitmap bytebuffer


【解决方案1】:

我发现了问题 - 尽管 Bitmap.Config 据说是 ARGB_8888,但实际上是 RGBA。我认为这是 android 开发人员文档和代码中的一个巨大错误。

在这个问题中也注意到了同样的问题 - Is Android's ARGB_8888 Bitmap internal format always RGBA?

ndk 文档正确地指出格式为ANDROID_BITMAP_FORMAT_RGBA_8888

解决方案很简单 - 使用 RGBA 格式创建缓冲区。或者在 java 端切换频道,如下所示 -

for (int i = 0; i < width * height; i++) {
    Byte a = buffer.get();
    Byte r = buffer.get();
    Byte g = buffer.get();
    Byte b = buffer.get();
    bufferCopy.put(r);
    bufferCopy.put(g);
    bufferCopy.put(b);
    bufferCopy.put(a);
}

这不是非常有效的代码,但可以完成工作。

【讨论】:

    【解决方案2】:

    Bitmap.Config.ARGB_8888 文档提到,“使用这个公式打包成 32 位: int 颜色 = (A & 0xff) post 正式称为 ANDROID_BITMAP_FORMAT_RGBA_8888 here (ABGR_8888 向后)。无论如何,这个 Kotlin 函数将转换您的颜色以在 copyPixelsFromBuffer 中使用。

    fun androidBitmapFormatRGBA8888(color: Int): Int {
        val a = (color shr 24) and 255
        val r = (color shr 16) and 255
        val g = (color shr 8) and 255
        val b = color and 255
    
        return (a shl 24) or (b shl 16) or (g shl 8) or r
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-07-30
      • 1970-01-01
      • 2011-02-04
      • 1970-01-01
      相关资源
      最近更新 更多