Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/android/217.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

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.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通道_Android_Bitmap_Android Ndk_Alpha - Fatal编程技术网

Android 当背景为黑色时,位图设置像素将丢失alpha通道

Android 当背景为黑色时,位图设置像素将丢失alpha通道,android,bitmap,android-ndk,alpha,Android,Bitmap,Android Ndk,Alpha,我正在尝试用Android绘制一个自定义视图,使用canvas.drawBitmap()方法。但是,我发现如果在本机JNI代码中执行此操作,并且背景为黑色,则alpha通道将丢失。总之,情况如下: 调用javabitmap.setPixels()并在NDK中设置位图像素颜色,当背景为白色时,两个位图都能正确显示 调用javabitmap.setPixels()并在NDK中设置位图像素颜色当背景为黑色时,只有java API绘制的位图显示正确,使用NDK绘制的位图丢失alpha通道 问题是为什么白

我正在尝试用Android绘制一个自定义视图,使用
canvas.drawBitmap()
方法。但是,我发现如果在本机JNI代码中执行此操作,并且背景为黑色,则alpha通道将丢失。总之,情况如下:

  • 调用java
    bitmap.setPixels()
    并在NDK中设置位图像素颜色,当背景为白色时,两个位图都能正确显示
  • 调用java
    bitmap.setPixels()
    并在NDK中设置位图像素颜色当背景为黑色时,只有java API绘制的位图显示正确,使用NDK绘制的位图丢失alpha通道
  • 问题是为什么白色背景上的结果可以,而黑色背景上的结果不可以?我是否遗漏了什么或是做得不对

    布局XML文件:

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:padding="16dp" >
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@android:color/black"
            android:orientation="horizontal"
            android:padding="16dp" >
    
            <com.example.android.TestView
                android:id="@+id/testview1"
                android:layout_width="320px"
                android:layout_height="320px"
                android:layout_margin="16dp" />
    
            <com.example.android.TestView
                android:id="@+id/testview2"
                android:layout_width="320px"
                android:layout_height="320px"
                android:layout_margin="16dp" />
        </LinearLayout>
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@android:color/white"
            android:orientation="horizontal"
            android:padding="16dp" >
    
            <com.example.android.TestView
                android:id="@+id/testview3"
                android:layout_width="320px"
                android:layout_height="320px"
                android:layout_margin="16dp" />
    
            <com.example.android.TestView
                android:id="@+id/testview4"
                android:layout_width="320px"
                android:layout_height="320px"
                android:layout_margin="16dp" />
        </LinearLayout>
    
    </LinearLayout>
    
    package com.example.android;
    import com.example.android.R;
    
    import android.app.Activity;
    import android.os.Bundle;
    
    public class MainActivity extends Activity {
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.main);
            TestView tv2 = (TestView) findViewById(R.id.testview2);
            TestView tv4 = (TestView) findViewById(R.id.testview4);
            tv2.setDrawFromNative();
            tv4.setDrawFromNative();
        }
    }
    
    package com.example.android;
    
    import android.content.Context;
    import android.graphics.Bitmap;
    import android.graphics.Canvas;
    import android.util.AttributeSet;
    import android.view.View;
    
    public class TestView extends View {
        private Bitmap mBitmap;
        private boolean mDrawFromNative;
        private static final int WIDTH = 320;
        private static final int HEIGHT = 320;
    
        static {
            System.loadLibrary("bitmaptest");
        }
        private native void nativeDrawBitmap(Object bitmap);
    
        private static void javaDrawBitmap(Bitmap bitmap) {
            int pixels[] = new int[WIDTH * HEIGHT];
            for (int i = 0; i < pixels.length; i++) {
                pixels[i] = 0x88FF0000;
            }
            bitmap.setPixels(pixels, 0, WIDTH, 0, 0, WIDTH, HEIGHT);
        }
    
        public TestView(Context context, AttributeSet attrs) {
            super(context, attrs);
            mBitmap = Bitmap.createBitmap(WIDTH, HEIGHT, Bitmap.Config.ARGB_8888);
        }
    
        public void setDrawFromNative() {
            mDrawFromNative = true;
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            if(mDrawFromNative) {
                nativeDrawBitmap(mBitmap);
            } else {
                javaDrawBitmap(mBitmap);
            }
            canvas.drawBitmap(mBitmap, 0, 0, null);
        }
    }
    
    #include <jni.h>
    #include <android/bitmap.h>
    #include <android/Log.h>
    
    #define LOGD(...)  __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__)
    #define LOGW(...)  __android_log_print(ANDROID_LOG_WARN,LOG_TAG,__VA_ARGS__)
    #define LOGE(...)  __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
    
    #define LOG_TAG "BMPTEST"
    
    extern "C" {
    void Java_com_example_android_TestView_nativeDrawBitmap(JNIEnv* env, jobject thiz, jobject bitmap) {
    
        AndroidBitmapInfo info;
        void* dst_pixels;
        int   ret;
    
        if((ret = AndroidBitmap_getInfo(env, bitmap, &info)) < 0) {
            LOGE("AndroidBitmap_getInfo() failed ! error=%d", ret);
            return;
        }
        if ((ret = AndroidBitmap_lockPixels(env, bitmap, &dst_pixels)) < 0) {
            LOGE("AndroidBitmap_lockPixels() failed ! error=%d", ret);
            return;
        }
    
        unsigned int *dst = (unsigned int *)dst_pixels;
        for(int i=0; i< info.width * info.height; i++) {
                *(dst+i) = (0x88<<24 | 0xff | 0x00<<8 | 0x00<<16); //(ARGB->ABGR)
        }
        AndroidBitmap_unlockPixels(env, bitmap);
    }
    }
    
    LOCAL_PATH := $(call my-dir)
    include $(CLEAR_VARS)
    LOCAL_MODULE := libbitmaptest
    LOCAL_SRC_FILES := \
        TestNative.cpp
    LOCAL_LDLIBS += -llog -ljnigraphics
    include $(BUILD_SHARED_LIBRARY)
    
    TestView.java:

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:padding="16dp" >
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@android:color/black"
            android:orientation="horizontal"
            android:padding="16dp" >
    
            <com.example.android.TestView
                android:id="@+id/testview1"
                android:layout_width="320px"
                android:layout_height="320px"
                android:layout_margin="16dp" />
    
            <com.example.android.TestView
                android:id="@+id/testview2"
                android:layout_width="320px"
                android:layout_height="320px"
                android:layout_margin="16dp" />
        </LinearLayout>
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@android:color/white"
            android:orientation="horizontal"
            android:padding="16dp" >
    
            <com.example.android.TestView
                android:id="@+id/testview3"
                android:layout_width="320px"
                android:layout_height="320px"
                android:layout_margin="16dp" />
    
            <com.example.android.TestView
                android:id="@+id/testview4"
                android:layout_width="320px"
                android:layout_height="320px"
                android:layout_margin="16dp" />
        </LinearLayout>
    
    </LinearLayout>
    
    package com.example.android;
    import com.example.android.R;
    
    import android.app.Activity;
    import android.os.Bundle;
    
    public class MainActivity extends Activity {
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.main);
            TestView tv2 = (TestView) findViewById(R.id.testview2);
            TestView tv4 = (TestView) findViewById(R.id.testview4);
            tv2.setDrawFromNative();
            tv4.setDrawFromNative();
        }
    }
    
    package com.example.android;
    
    import android.content.Context;
    import android.graphics.Bitmap;
    import android.graphics.Canvas;
    import android.util.AttributeSet;
    import android.view.View;
    
    public class TestView extends View {
        private Bitmap mBitmap;
        private boolean mDrawFromNative;
        private static final int WIDTH = 320;
        private static final int HEIGHT = 320;
    
        static {
            System.loadLibrary("bitmaptest");
        }
        private native void nativeDrawBitmap(Object bitmap);
    
        private static void javaDrawBitmap(Bitmap bitmap) {
            int pixels[] = new int[WIDTH * HEIGHT];
            for (int i = 0; i < pixels.length; i++) {
                pixels[i] = 0x88FF0000;
            }
            bitmap.setPixels(pixels, 0, WIDTH, 0, 0, WIDTH, HEIGHT);
        }
    
        public TestView(Context context, AttributeSet attrs) {
            super(context, attrs);
            mBitmap = Bitmap.createBitmap(WIDTH, HEIGHT, Bitmap.Config.ARGB_8888);
        }
    
        public void setDrawFromNative() {
            mDrawFromNative = true;
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            if(mDrawFromNative) {
                nativeDrawBitmap(mBitmap);
            } else {
                javaDrawBitmap(mBitmap);
            }
            canvas.drawBitmap(mBitmap, 0, 0, null);
        }
    }
    
    #include <jni.h>
    #include <android/bitmap.h>
    #include <android/Log.h>
    
    #define LOGD(...)  __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__)
    #define LOGW(...)  __android_log_print(ANDROID_LOG_WARN,LOG_TAG,__VA_ARGS__)
    #define LOGE(...)  __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
    
    #define LOG_TAG "BMPTEST"
    
    extern "C" {
    void Java_com_example_android_TestView_nativeDrawBitmap(JNIEnv* env, jobject thiz, jobject bitmap) {
    
        AndroidBitmapInfo info;
        void* dst_pixels;
        int   ret;
    
        if((ret = AndroidBitmap_getInfo(env, bitmap, &info)) < 0) {
            LOGE("AndroidBitmap_getInfo() failed ! error=%d", ret);
            return;
        }
        if ((ret = AndroidBitmap_lockPixels(env, bitmap, &dst_pixels)) < 0) {
            LOGE("AndroidBitmap_lockPixels() failed ! error=%d", ret);
            return;
        }
    
        unsigned int *dst = (unsigned int *)dst_pixels;
        for(int i=0; i< info.width * info.height; i++) {
                *(dst+i) = (0x88<<24 | 0xff | 0x00<<8 | 0x00<<16); //(ARGB->ABGR)
        }
        AndroidBitmap_unlockPixels(env, bitmap);
    }
    }
    
    LOCAL_PATH := $(call my-dir)
    include $(CLEAR_VARS)
    LOCAL_MODULE := libbitmaptest
    LOCAL_SRC_FILES := \
        TestNative.cpp
    LOCAL_LDLIBS += -llog -ljnigraphics
    include $(BUILD_SHARED_LIBRARY)
    
    结果屏幕截图:

    Android存储带有预乘alpha的位图。从Java调用
    setPixels()
    时,RGB颜色值将自动乘以alpha值并存储在位图中。但是,当您从本机代码调用
    Android\u lockPixels()
    ,然后直接写入内存时,您需要自己进行预乘法,否则结果会出错。如果您将代码更改为:

     int premultipliedR = (0xff * 0x88) >> 8;
     for(int i=0; i< info.width * info.height; i++) {
            *(dst+i) = (0x88<<24 | premultipliedR | 0x00<<8 | 0x00<<16);
    
    其中
    dest
    是背景像素,source是位图中的像素。预乘以alpha会将此更改为:

     dest.r = ((dest.r * (256 - source.a)) >> 8) + source.premultiplied_r;
     dest.g = ((dest.g * (256 - source.a)) >> 8) + source.premultiplied_g;
     dest.b = ((dest.b * (256 - source.a)) >> 8) + source.premultiplied_b;
    
    这就节省了大量的乘数。结果都被钳制为255。我并不是说这是使用的精确公式,但它与之非常接近

    插入数字,对于Java位图,预乘的r、g、b将为0x87(或0x88,取决于它们如何进行舍入等)、0x00和0x00。对于本机位图,它们将是0xff、0x00和0x00,因为您没有预乘。Alpha将其与黑色背景混合与仅添加零相同,因为
    dest.
    r
    g
    b
    值均为零。所以结果看起来不同

    在白色背景的情况下,
    dest.g
    dest.b
    在这两种情况下都将以相同的结果结束,因为在Java位图和本机位图中预乘的
    g
    b
    值都为零。对于
    dest.r
    ,结果应为255。在本机位图的情况下,由于预乘的
    r
    的值错误,该值溢出,但它被钳制为255,因此结果看起来是一样的


    简言之,预乘的
    r
    值对于本机位图来说太高,因此在结果应小于255的情况下,最终的
    r
    值太高。在结果应该是255的情况下,它是否太高并不重要,因为它会被钳制在255。

    如果在本机代码中切换字节顺序如何?(即,
    0x0000ff11
    )在ARM平台上,我的代码中的字节顺序是正确的(带有字母0x11的红色)。使用0x0000ff11会使位图变成不透明的绿色矩形。我已经用详细的源代码和屏幕截图更新了问题。当你说PremultipledR时,你是指PremultipledAlpha,对吗?我按照你的建议更改了代码,结果是一样的。我仍然在黑色背景上得到一个不透明的红色矩形,与JavaAPI绘制的半透明矩形不同。还是我误解了你的答案,我应该改变*(dst+I)=???如果您可以使用我的代码并对其进行修改以获得正确的结果,那将是非常棒的。关于这个主题,还有一个问题,如果所有的R/G/B都存储为预乘,那么仍然需要将alpha保留在像素中吗?我的意思是,*(dst+I)=(无论什么值)是的,你仍然需要它,因为dest RGB被乘以256减去混合方程中的alpha值。非常感谢你在这个问题上救了我,我被困在这里两天。最后一个问题:有“官方”吗可以告诉框架当前像素缓冲区内容不是预乘的API,因此需要将其乘以,并且框架可能会使用比我的for{}循环更快的硬件加速?我尝试了bitmap.setPremultiplied(false)将导致崩溃…setPremultiplied是我知道的唯一一个。崩溃可能是调用createBitmap引起的运行时异常(请参阅文档:)。也许在调用createBitmap后尝试调用它?除此之外,我不知道。