Android - 读取不带 alpha 的 PNG 图像并解码为 ARGB_8888
我尝试从 sdcard(在模拟器中)读取图像,然后使用
BitmapFactory.decodeByteArray
方法创建位图图像。我设置了选项:
options.inPreferedConfig = Bitmap.Config.ARGB_8888
options.inDither = false
然后我将像素提取到 ByteBuffer 中。
ByteBuffer buffer = ByteBuffer.allocateDirect(width*height*4)
bitmap.copyPixelsToBuffer(buffer)
我在 JNI 中使用这个 ByteBuffer 将其转换为 RGB 格式并想要对其进行计算。
但我总是得到错误的数据 - 我在不修改 ByteBuffer 的情况下进行测试。我唯一要做的就是将其放入 JNI 的本机方法中。然后将其转换为 unsigned char*
并将其转换回 ByteBuffer
,然后将其返回给 Java。
unsigned char* buffer = (unsinged char*)(env->GetDirectBufferAddress(byteBuffer))
jobject returnByteBuffer = env->NewDirectByteBuffer(buffer, length)
在显示图像之前,我使用
bitmap.copyPixelsFromBuffer( buffer )
返回数据,但是其中包含错误的数据。
我的问题是,这是否是因为图像在内部转换为 RGB 565 还是这里出了什么问题?
.....
有一个答案:
->>>是的,它在内部转换为 RGB565。
有谁知道如何用 ARGB8888 像素格式从 PNG 创建这样的位图图像?
如果有人有想法,那就太好了!
I try to read an image from sdcard (in emulator) and then create a Bitmap image with the
BitmapFactory.decodeByteArray
method. I set the options:
options.inPrefferedConfig = Bitmap.Config.ARGB_8888
options.inDither = false
Then I extract the pixels into a ByteBuffer.
ByteBuffer buffer = ByteBuffer.allocateDirect(width*height*4)
bitmap.copyPixelsToBuffer(buffer)
I use this ByteBuffer then in the JNI to convert it into RGB format and want to calculate on it.
But always I get false data - I test without modifying the ByteBuffer. Only thing I do is to put it into the native method into JNI. Then cast it into a unsigned char*
and convert it back into a ByteBuffer
before returning it back to Java.
unsigned char* buffer = (unsinged char*)(env->GetDirectBufferAddress(byteBuffer))
jobject returnByteBuffer = env->NewDirectByteBuffer(buffer, length)
Before displaying the image I get data back with
bitmap.copyPixelsFromBuffer( buffer )
But then it has wrong data in it.
My Question is if this is because the image is internally converted into RGB 565 or what is wrong here?
.....
Have an answer for it:
->>> yes, it is converted internally to RGB565.
Does anybody know how to create such an bitmap image from PNG with ARGB8888 pixel format?
If anybody has an idea, it would be great!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
ARGB_8888 位图(在 Honeycomb 之前的版本上)本身以 RGBA 格式存储。
所以 Alpha 通道被移动到最后。在本机访问位图的像素时,您应该考虑到这一点。
我假设您正在为低于 3.2 的 Android 版本(API 级别 < 12)编写代码,因为从那时起,方法的行为
已经发生了变化。
在较旧的平台(API 级别 < 12)上,如果找不到任何 alpha,BitmapFactory.decodeFile(..) 方法默认会尝试返回具有 RGB_565 配置的位图,这会降低图像的质量。这仍然没问题,因为您可以使用强制执行 ARGB_8888 位图。
真正的问题是当图像的每个像素的 alpha 值为 255(即完全不透明)时出现。在这种情况下,即使您的位图具有 ARGB_8888 配置,位图的标志“hasAlpha”也会设置为 false。如果您的 *.png 文件至少有一个真正的透明像素,则该标志将被设置为 true,您无需担心任何事情。
因此,当您想要使用
该方法创建缩放位图时,请检查“hasAlpha”标志是否设置为 true 或 false,在您的情况下,将其设置为 false,这会导致获得缩放位图,该位图会自动转换为RGB_565 格式。
因此,在 API 级别 >= 12 上,有一个名为的公共方法
可以解决此问题。到目前为止,这只是对问题的解释。
我做了一些研究,发现 setHasAlpha 方法已经存在很长时间了,并且是公开的,但已被隐藏(@hide 注释)。以下是它在 Android 2.3 上的定义:
现在这是我的解决方案建议。它不涉及任何位图数据的复制:
在运行时使用 java.lang.Reflect 检查当前是否
位图实现有一个公共的“setHasAplha”方法。
(根据我的测试,它从 API 级别 3 开始就完美工作,并且我没有测试较低版本,因为 JNI 不起作用)。如果制造商明确将其设为私有、保护或删除,您可能会遇到问题。
使用 JNI 调用给定 Bitmap 对象的“setHasAlpha”方法。
即使对于私有方法或字段,这也非常有效。据官方说法,JNI 不会检查您是否违反了访问控制规则。
来源:http://java.sun.com/docs/books/jni/html/pitfalls。 html (10.9)
这给了我们巨大的力量,我们应该明智地使用它。我不会尝试修改最终字段,即使它可以工作(仅举一个例子)。请注意,这只是一种解决方法...
这是我对所有必要方法的实现:
JAVA 部分:
加载您的库并声明本机方法:
本机部分('jni'文件夹)
Android.mk:bitmapUtils.c
:
就是这样。我们完成了。我已经发布了整个代码以供复制和粘贴之用。
实际的代码并没有那么大,但是进行所有这些偏执的错误检查使它变得更大。我希望这对任何人都有帮助。
An ARGB_8888 Bitmap (on pre Honeycomb versions) is natively stored in the RGBA format.
So the alpha channel is moved at the end. You should take this into account when accessing a Bitmap's pixels natively.
I assume you are writing code for a version of Android lower than 3.2 (API level < 12), because since then the behavior of the methods
has changed.
On older platforms (API level < 12) the BitmapFactory.decodeFile(..) methods try to return a Bitmap with RGB_565 config by default, if they can't find any alpha, which lowers the quality of an iamge. This is still ok, because you can enforce an ARGB_8888 bitmap using
The real problem comes when each pixel of your image has an alpha value of 255 (i.e. completely opaque). In that case the Bitmap's flag 'hasAlpha' is set to false, even though your Bitmap has ARGB_8888 config. If your *.png-file had at least one real transparent pixel, this flag would have been set to true and you wouldn't have to worry about anything.
So when you want to create a scaled Bitmap using
the method checks whether the 'hasAlpha' flag is set to true or false, and in your case it is set to false, which results in obtaining a scaled Bitmap, which was automatically converted to the RGB_565 format.
Therefore on API level >= 12 there is a public method called
which would have solved this issue. So far this was just an explanation of the problem.
I did some research and noticed that the setHasAlpha method has existed for a long time and it's public, but has been hidden (@hide annotation). Here is how it is defined on Android 2.3:
Now here is my solution proposal. It does not involve any copying of bitmap data:
Checked at runtime using java.lang.Reflect if the current
Bitmap implementation has a public 'setHasAplha' method.
(According to my tests it works perfectly since API level 3, and i haven't tested lower versions, because JNI wouldn't work). You may have problems if a manufacturer has explicitly made it private, protected or deleted it.
Call the 'setHasAlpha' method for a given Bitmap object using JNI.
This works perfectly, even for private methods or fields. It is official that JNI does not check whether you are violating the access control rules or not.
Source: http://java.sun.com/docs/books/jni/html/pitfalls.html (10.9)
This gives us great power, which should be used wisely. I wouldn't try modifying a final field, even if it would work (just to give an example). And please note this is just a workaround...
Here is my implementation of all necessary methods:
JAVA PART:
Load your lib and declare the native method:
Native section ('jni' folder)
Android.mk:
bitmapUtils.c:
That's it. We are done. I've posted the whole code for copy-and-paste purposes.
The actual code isn't that big, but making all these paranoid error checks makes it a lot bigger. I hope this could be helpful to anyone.