“然而,我不知道如何确保Bitmap在整个过程中保持为32位。”
我想提供一种替代方案,可以确保ARGB_8888配置不受影响。注意:此代码仅解码位图,需要扩展以便可以存储位图。
我假设您正在编写适用于Android版本低于3.2(API级别<12)的代码,因为自那时起方法的行为已经改变。
BitmapFactory.decodeFile(pathToImage);
BitmapFactory.decodeFile(pathToImage, opt);
bitmapObject.createScaledBitmap(bitmap, desiredWidth, desiredHeight, false );
已更改。
在旧平台上(API级别<12),BitmapFactory.decodeFile(..)方法默认尝试返回RGB_565配置的位图,如果找不到任何alpha,则会降低图像质量。这仍然可以接受,因为您可以使用以下方法强制使用ARGB_8888位图:
options.inPrefferedConfig = Bitmap.Config.ARGB_8888
options.inDither = false
当您的图像每个像素的Alpha值为255(即完全不透明)时,真正的问题就出现了。在这种情况下,位图的标志“hasAlpha”设置为false,即使您的位图具有ARGB_8888配置。如果您的*.png文件至少有一个真正的透明像素,则该标志将被设置为true,您就不必担心任何问题。
因此,当您想要使用缩放的位图创建时
bitmapObject.createScaledBitmap(bitmap, desiredWidth, desiredHeight, false );
该方法检查'hasAlpha'标志是否设置为true或false,在您的情况下,它被设置为false,这将导致获取一个缩放的位图,自动转换为RGB_565格式。
因此,在API级别>= 12上,有一个名为的公共方法
public void setHasAlpha (boolean hasAlpha);
这将解决此问题。到目前为止,这只是问题的说明。
我进行了一些研究,并注意到setHasAlpha方法已经存在很长时间,而且它是公共的,但被隐藏起来了(@hide注释)。以下是Android 2.3中定义的方式:
public void setHasAlpha(boolean hasAlpha) {
nativeSetHasAlpha(mNativeBitmap, hasAlpha);
}
现在,这是我的解决方案提议。它不涉及任何位图数据的复制:
在运行时使用java.lang.Reflect检查当前Bitmap实现是否具有公共的“setHasAlpha”方法。
(根据我的测试,自API级别3以来它完美地工作,我没有测试过更低版本,因为JNI不起作用)。如果制造商已将其明确设置为私有、受保护或删除它,则可能会遇到问题。
使用JNI调用给定位图对象的“setHasAlpha”方法。
即使对于私有方法或字段,这也可以完美地工作。官方声明JNI不检查您是否违反了访问控制规则。
来源:http://java.sun.com/docs/books/jni/html/pitfalls.html(10.9)
这给了我们巨大的力量,应该明智地使用。我不会尝试修改最终字段,即使它能够工作(只是举个例子)。请注意,这只是一个解决方法...
这是我所有必要方法的实现:
JAVA部分:
private static final boolean SETHASALPHA_EXISTS = setHasAlphaExists();
private static boolean setHasAlphaExists() {
java.lang.reflect.Method[] methods = Bitmap.class.getMethods();
for(int i=0; i<methods.length; i++) {
if(methods[i].getName().contains("setHasAlpha")) {
Log.i(TAG, "method setHasAlpha was found");
return true;
}
}
Log.i(TAG, "couldn't find method setHasAlpha");
return false;
}
private static void setHasAlpha(Bitmap bitmap, boolean value) {
if(bitmap.hasAlpha() == value) {
Log.i(TAG, "bitmap.hasAlpha() == value -> do nothing");
return;
}
if(!SETHASALPHA_EXISTS) {
return;
}
if(Integer.valueOf(android.os.Build.VERSION.SDK) <= 11) {
Log.i(TAG, "BEFORE: bitmap.hasAlpha() == " + bitmap.hasAlpha());
Log.i(TAG, "trying to set hasAplha to true");
int result = setHasAlphaNative(bitmap, value);
Log.i(TAG, "AFTER: bitmap.hasAlpha() == " + bitmap.hasAlpha());
if(result == -1) {
Log.e(TAG, "Unable to access bitmap.");
return;
}
} else {
bitmap.setHasAlpha(true);
}
}
public Bitmap decodeBitmapFromFile(String pathToImage, int pixels_limit) {
Bitmap bitmap;
Options opt = new Options();
opt.inDither = false;
opt.inPreferredConfig = Bitmap.Config.ARGB_8888;
bitmap = BitmapFactory.decodeFile(pathToImage, opt);
if(bitmap == null) {
Log.e(TAG, "unable to decode bitmap");
return null;
}
setHasAlpha(bitmap, true);
int numOfPixels = bitmap.getWidth() * bitmap.getHeight();
if(numOfPixels > pixels_limit) {
imageScaleFactor = Math.sqrt((double) pixels_limit / (double) numOfPixels);
Bitmap scaledBitmap = Bitmap.createScaledBitmap(bitmap,
(int) (imageScaleFactor * bitmap.getWidth()), (int) (imageScaleFactor * bitmap.getHeight()), false);
bitmap.recycle();
bitmap = scaledBitmap;
Log.i(TAG, "scaled bitmap config: " + bitmap.getConfig().toString());
Log.i(TAG, "pixels_limit = " + pixels_limit);
Log.i(TAG, "scaled_numOfpixels = " + scaledBitmap.getWidth()*scaledBitmap.getHeight());
setHasAlpha(bitmap, true);
}
return bitmap;
}
加载您的库并声明本地方法:
static {
System.loadLibrary("bitmaputils");
}
private static native int setHasAlphaNative(Bitmap bitmap, boolean value);
本地部分('jni'文件夹)
Android.mk:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := bitmaputils
LOCAL_SRC_FILES := bitmap_utils.c
LOCAL_LDLIBS := -llog -ljnigraphics -lz -ldl -lgcc
include $(BUILD_SHARED_LIBRARY)
bitmapUtils.c:
#include <jni.h>
#include <android/bitmap.h>
#include <android/log.h>
#define LOG_TAG "BitmapTest"
#define Log_i(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
#define Log_e(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
static jclass bitmap_class = 0;
static jmethodID setHasAlphaMethodID = 0;
jint Java_com_example_bitmaptest_MainActivity_setHasAlphaNative(JNIEnv * env, jclass clazz, jobject bitmap, jboolean value) {
AndroidBitmapInfo info;
void* pixels;
if (AndroidBitmap_getInfo(env, bitmap, &info) < 0) {
Log_e("Failed to get Bitmap info");
return -1;
}
if (info.format != ANDROID_BITMAP_FORMAT_RGBA_8888) {
Log_e("Incompatible Bitmap format");
return -1;
}
if (AndroidBitmap_lockPixels(env, bitmap, &pixels) < 0) {
Log_e("Failed to lock the pixels of the Bitmap");
return -1;
}
if(bitmap_class == NULL) {
bitmap_class = (*env)->GetObjectClass(env, bitmap);
if(bitmap_class == NULL) {
Log_e("bitmap_class == NULL");
return -2;
}
}
if(setHasAlphaMethodID == NULL) {
setHasAlphaMethodID = (*env)->GetMethodID(env, bitmap_class, "setHasAlpha", "(Z)V");
if(setHasAlphaMethodID == NULL) {
Log_e("methodID == NULL");
return -2;
}
}
(*env)->CallVoidMethod(env, bitmap, setHasAlphaMethodID, value);
if ((*env)->ExceptionOccurred(env)) {
(*env)->ExceptionDescribe(env);
(*env)->ExceptionClear(env);
Log_e("calling setHasAlpha threw an exception");
return -2;
}
if(AndroidBitmap_unlockPixels(env, bitmap) < 0) {
Log_e("Failed to unlock the pixels of the Bitmap");
return -1;
}
return 0;
}
就是这样,我们完成了。我已经发布了整个代码,以便复制和粘贴。
实际代码并不是很大,但是进行所有这些偏执错误检查使其变得更大。我希望这对任何人都有所帮助。