Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/image/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Android-读取不带alpha的PNG图像并解码为ARGB_8888_Android_Image_Png_Bitmap_Argb - Fatal编程技术网

Android-读取不带alpha的PNG图像并解码为ARGB_8888

Android-读取不带alpha的PNG图像并解码为ARGB_8888,android,image,png,bitmap,argb,Android,Image,Png,Bitmap,Argb,我尝试从SD卡(在emulator中)读取一个图像,然后使用 BitmapFactory.decodeByteArray 方法。我设置选项: options.inpreferedconfig=Bitmap.Config.ARGB_8888 options.inDither=false 然后我将像素提取到ByteBuffer中 ByteBuffer buffer=ByteBuffer.allocateDirect(宽*高*4) bitmap.copyPixelsToBuffer(缓冲区) 我使用这

我尝试从SD卡(在emulator中)读取一个图像,然后使用

BitmapFactory.decodeByteArray

方法。我设置选项:

options.inpreferedconfig=Bitmap.Config.ARGB_8888

options.inDither=false

然后我将像素提取到ByteBuffer中

ByteBuffer buffer=ByteBuffer.allocateDirect(宽*高*4)

bitmap.copyPixelsToBuffer(缓冲区)

我使用这个ByteBuffer,然后在JNI中将其转换为RGB格式,并希望对其进行计算

但我总是得到错误的数据——我在不修改ByteBuffer的情况下进行测试。我所做的唯一一件事就是将它放入JNI的本机方法中。然后将其转换成一个
无符号字符*
,并将其转换回
字节缓冲
,然后返回Java

无符号字符*缓冲=(无符号字符*)(env->GetDirectBufferAddress(byteBuffer))

jobject returnByteBuffer=env->NewDirectByteBuffer(缓冲区,长度)

在显示图像之前,我用

bitmap.copyPixelsFromBuffer(缓冲区)

但是它里面有错误的数据

我的问题是,这是因为图像在内部转换为RGB 565,还是这里出了什么问题

有一个答案:

->>>是的,它在内部转换为RGB565

有人知道如何用argb888像素格式从PNG创建这样的位图图像吗

如果有人有主意,那就太好了

ARGB_8888位图(在蜂窝式之前的版本上)本机以RGBA格式存储。 因此alpha通道在末端移动。当以本机方式访问位图的像素时,应该考虑到这一点

我假设您正在为低于3.2(API级别<12)的Android版本编写代码,因为从那时起,这些方法的行为

BitmapFactory.decodeFile(pathToImage);
BitmapFactory.decodeFile(pathToImage, opt);
bitmapObject.createScaledBitmap(bitmap, desiredWidth, desiredHeight, false /*filter?*/);
已经改变了

在较旧的平台(API级别<12)上,BitmapFactory.decodeFile(..)方法如果找不到任何alpha,则会尝试返回默认为RGB_565配置的位图,这会降低图像质量。这仍然可以,因为您可以使用

options.inPrefferedConfig = Bitmap.Config.ARGB_8888
options.inDither = false 
bitmapObject.createScaledBitmap(bitmap, desiredWidth, desiredHeight, false /*filter?*/);
当图像的每个像素的alpha值为255(即完全不透明)时,真正的问题就会出现。在这种情况下,位图的标志“hasAlpha”设置为false,即使位图具有ARGB_8888配置。如果您的*.png文件至少有一个真正的透明像素,那么该标志将被设置为true,您就不必担心任何事情

因此,当您要使用创建缩放位图时

options.inPrefferedConfig = Bitmap.Config.ARGB_8888
options.inDither = false 
bitmapObject.createScaledBitmap(bitmap, desiredWidth, desiredHeight, false /*filter?*/);
该方法检查“hasAlpha”标志是否设置为true或false,在您的情况下,该标志设置为false,从而获得缩放位图,该位图已自动转换为RGB_565格式

因此,在API级别>=12时,有一个名为

public void setHasAlpha (boolean hasAlpha);
这本可以解决这个问题。到目前为止,这只是对问题的一种解释。 我做了一些研究,发现setHasAlpha方法已经存在很长时间了,它是公开的,但是已经被隐藏了(@hide annotation)。以下是它在Android 2.3上的定义:

/**
 * Tell the bitmap if all of the pixels are known to be opaque (false)
 * or if some of the pixels may contain non-opaque alpha values (true).
 * Note, for some configs (e.g. RGB_565) this call is ignore, since it does
 * not support per-pixel alpha values.
 *
 * This is meant as a drawing hint, as in some cases a bitmap that is known
 * to be opaque can take a faster drawing case than one that may have
 * non-opaque per-pixel alpha values.
 *
 * @hide
 */
public void setHasAlpha(boolean hasAlpha) {
    nativeSetHasAlpha(mNativeBitmap, hasAlpha);
}
这是我的解决方案。它不涉及位图数据的任何复制:

  • 在运行时使用java.lang.Reflect检查当前 位图实现有一个公共的“setHasAplha”方法。 (根据我的测试,从API级别3开始,它就可以完美地工作,我还没有测试过较低版本,因为JNI无法工作)。如果制造商明确将其设置为私有、受保护或删除,您可能会遇到问题

  • 使用JNI为给定位图对象调用“setHasAlpha”方法。 即使对于私有方法或字段,这也非常有效。JNI不会检查您是否违反访问控制规则,这是官方规定的。 资料来源:(10.9) 这给了我们巨大的力量,应该明智地加以利用。我不会尝试修改最终字段,即使它可以工作(仅举一个例子)。请注意,这只是一个解决办法

  • 以下是我对所有必要方法的实现:

    JAVA部分:

    // NOTE: this cannot be used in switch statements
        private static final boolean SETHASALPHA_EXISTS = setHasAlphaExists();
    
        private static boolean setHasAlphaExists() {
            // get all puplic Methods of the class Bitmap
            java.lang.reflect.Method[] methods = Bitmap.class.getMethods();
            // search for a method called 'setHasAlpha'
            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) {   // if we can't find it then API level MUST be lower than 12
                // couldn't find the setHasAlpha-method
                // <-- provide alternative here...
                return;
            }
    
            // using android.os.Build.VERSION.SDK to support API level 3 and above
            // use android.os.Build.VERSION.SDK_INT to support API level 4 and above
            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."); // usually due to a bug in the own code
                    return;
                }
            } else {    //API level >= 12
                bitmap.setHasAlpha(true);
            }
        }
    
        /**
         * Decodes a Bitmap from the SD card
         * and scales it if necessary
         */
        public Bitmap decodeBitmapFromFile(String pathToImage, int pixels_limit) {
            Bitmap bitmap;
    
            Options opt = new Options();
            opt.inDither = false;   //important
            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);  // if necessary
    
            int numOfPixels = bitmap.getWidth() * bitmap.getHeight();
    
            if(numOfPixels > pixels_limit) {    //image needs to be scaled down 
                // ensures that the scaled image uses the maximum of the pixel_limit while keeping the original aspect ratio
                // i use: private static final int pixels_limit = 1280*960; //1,3 Megapixel
                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); // if necessary
            }
    
            return bitmap;
        }
    
    本机节('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__)
    
    
    // caching class and method IDs for a faster subsequent access
    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;
        }
    
    
        // get class
        if(bitmap_class == NULL) {  //initializing jclass
            // NOTE: The class Bitmap exists since API level 1, so it just must be found.
            bitmap_class = (*env)->GetObjectClass(env, bitmap);
            if(bitmap_class == NULL) {
                Log_e("bitmap_class == NULL");
                return -2;
            }
        }
    
        // get methodID
        if(setHasAlphaMethodID == NULL) { //initializing jmethodID
            // NOTE: If this fails, because the method could not be found the App will crash.
            // But we only call this part of the code if the method was found using java.lang.Reflect
            setHasAlphaMethodID = (*env)->GetMethodID(env, bitmap_class, "setHasAlpha", "(Z)V");
            if(setHasAlphaMethodID == NULL) {
                Log_e("methodID == NULL");
                return -2;
            }
        }
    
        // call java instance method
        (*env)->CallVoidMethod(env, bitmap, setHasAlphaMethodID, value);
    
        // if an exception was thrown we could handle it here
        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;   // success
    }
    
    #包括
    #包括
    #包括
    #定义日志标签“BitmapTest”
    #定义日志i(…)\uuuuuAndroid\uLog\uPrint(android\uLog\u信息、日志标签、参数)
    #定义日志(…)\uuuuuAndroid\uLog\uPrint(android\uLog\uError,Log\uTag,\uuu VA\uArgs\uuuuuu)
    //缓存类和方法ID以加快后续访问
    静态jclass位图_class=0;
    静态jmethodID setHasAlphaMethodID=0;
    jint Java_com_示例_bitmaptest_MainActivity_setHasAlphaNative(JNIEnv*env、jclass clazz、jobject位图、jboolean值){
    AndroidBitmapInfo信息;
    空*像素;
    if(AndroidBitmap_getInfo(环境、位图和信息)<0){
    日志(无法获取位图信息);
    返回-1;
    }
    如果(info.format!=ANDROID\u位图\u格式\u RGBA\u 8888){
    日志(“不兼容的位图格式”);
    返回-1;
    }
    if(AndroidBitmap_lockPixels(环境、位图和像素)<0){
    Log_e(“未能锁定位图的像素”);
    返回-1;
    }
    //上课
    如果(位图_类==NULL){//初始化jclass
    //注意:类位图从API级别1开始就存在,因此必须找到它。
    位图类=(*env)->GetObjectClass(env,位图);
    if(位图_类==NULL){
    Log_e(“位图_class==NULL”);
    返回-2;
    }
    }
    //获取方法ID
    如果(setHasAlphaMethodID==NULL){//jmethodID
    //注意:如果此操作失败,因为找不到该方法,应用程序将崩溃。
    //但是,只有在使用java.lang.Reflect找到该方法时,我们才调用这部分代码
    setHasAlphaMethodID=(*env)->GetMethodID(env,位图_类,“setHasAlpha”,“Z)V”);
    if(setHasAlphaMethodID==NULL){