简体   繁体   English

Bitmap中的像素值困惑(使用setPixel预先乘以颜色)

[英]Puzzled by pixel value in Bitmap (pre multiplied color using setPixel)

I cannot explain this (screenshot from Eclipse debug): 我无法解释这一点(来自Eclipse调试的截图):

完整截图

代码细节

各种细节

Pixel at (0,0) does not have the values it was set for! (0,0)处的像素没有为其设置的值!
All other pixels are fine, they do have the same value they were assigned to. 所有其他像素都很好,它们具有相同的值。

EDIT I did some more digging, and the code for setPixel is calling a native function: 编辑我做了一些挖掘,setPixel的代码调用本机函数:

1391    public void setPixel(int x, int y, int color) {
1392        checkRecycled("Can't call setPixel() on a recycled bitmap");
1393        if (!isMutable()) {
1394            throw new IllegalStateException();
1395        }
1396        checkPixelAccess(x, y);
1397        nativeSetPixel(mNativeBitmap, x, y, color, mIsPremultiplied);
1398    }

so I run setPixel(0,0) with different values, starting with the one that was OK for setPixel(0,1). 所以我用不同的值运行setPixel(0,0),从setPixel(0,1)的OK值开始。
First I changed only one of the arguments. 首先,我只改变了其中一个论点。 The conclusion is that the alpha value is the culprit, but not sure exactly how. 结论是阿尔法值是罪魁祸首,但不确定如何。 I tried a lot of other alpha values and it seems that around 0xB0 and up the results are coming back OK. 我尝试了很多其他alpha值,似乎在0xB0左右,结果回来了。
I tried the same values on other pixels and the problem is not dependent on the pixel coordinates, it fails as well for others. 我在其他像素上尝试了相同的值,问题不依赖于像素坐标,对其他像素也是如此。 The problem seems to be data dependent. 这个问题似乎与数据有关。

    source.setPixel(0, 0, Color.argb(0x40, 0x1A, 0x11, 0x12));
    int sp00 = source.getPixel(0, 0); // sp00   1075580948 [0x401c1014] BAD

    source.setPixel(0, 0, Color.argb(0xFE, 0xFF, 0xEE, 0xDE));
    sp00 = source.getPixel(0, 0); // sp00   -16781602 [0xfeffeede]  OK!
    source.setPixel(0, 0, Color.argb(0x40, 0xFF, 0xEE, 0xDE));
    sp00 = source.getPixel(0, 0); // sp00   1090514911 [0x40ffefdf] BAD
    source.setPixel(0, 0, Color.argb(0xFE, 0x1A, 0xEE, 0xDE));
    sp00 = source.getPixel(0, 0); // sp00   -31789346 [0xfe1aeede]  OK!
    source.setPixel(0, 0, Color.argb(0xFE, 0xFF, 0x11, 0xDE));
    sp00 = source.getPixel(0, 0); // sp00   -16838178 [0xfeff11de]  OK!
    source.setPixel(0, 0, Color.argb(0xFE, 0xFF, 0xEE, 0x12));
    sp00 = source.getPixel(0, 0); // sp00   -16781806 [0xfeffee12]  OK!

    source.setPixel(0, 0, Color.argb(0x00, 0xFF, 0xEE, 0xDE));
    sp00 = source.getPixel(0, 0); // sp00   0 [0x0] Strange, why Color has to police the alpha value?
    source.setPixel(0, 0, Color.argb(0x10, 0xFF, 0xEE, 0xDE));
    sp00 = source.getPixel(0, 0); // sp00   285208543 [0x10ffefdf]  BAD
    source.setPixel(0, 0, Color.argb(0x20, 0xFF, 0xEE, 0xDE));
    sp00 = source.getPixel(0, 0); // sp00   553643999 [0x20ffefdf]  BAD
    source.setPixel(0, 0, Color.argb(0x30, 0xFF, 0xEE, 0xDE));
    sp00 = source.getPixel(0, 0); // sp00   822079455 [0x30ffefdf]  BAD
    source.setPixel(0, 0, Color.argb(0x50, 0xFF, 0xEE, 0xDE));
    sp00 = source.getPixel(0, 0); // sp00   1358950367 [0x50ffefdf] BAD 
    source.setPixel(0, 0, Color.argb(0x60, 0xFF, 0xEE, 0xDE));
    sp00 = source.getPixel(0, 0); // sp00   1627385823 [0x60ffefdf] BAD
    source.setPixel(0, 0, Color.argb(0x70, 0xFF, 0xEE, 0xDE));
    sp00 = source.getPixel(0, 0); // sp00   1895821279 [0x70ffefdf] BAD
    source.setPixel(0, 0, Color.argb(0x80, 0xFF, 0xEE, 0xDE));
    sp00 = source.getPixel(0, 0); // sp00   -2130711075 [0x80ffeddd] BAD but change of pattern
    source.setPixel(0, 0, Color.argb(0x90, 0xFF, 0xEE, 0xDE));
    sp00 = source.getPixel(0, 0); // sp00   -1862275619 [0x90ffeddd] BAD    
    source.setPixel(0, 0, Color.argb(0xA0, 0xFF, 0xEE, 0xDE));
    sp00 = source.getPixel(0, 0); // sp00   -1593840162 [0xa0ffedde] BAD but change of pattern again
    source.setPixel(0, 0, Color.argb(0xB0, 0xFF, 0xEE, 0xDE));
    sp00 = source.getPixel(0, 0); // sp00   -1325404450 [0xb0ffeede] OK!    
    source.setPixel(0, 0, Color.argb(0xC0, 0xFF, 0xEE, 0xDE));
    sp00 = source.getPixel(0, 0); // sp00   -1056968994 [0xc0ffeede] OK!
    source.setPixel(0, 0, Color.argb(0xD0, 0xFF, 0xEE, 0xDE));
    sp00 = source.getPixel(0, 0); // sp00   -788533538 [0xd0ffeede] OK!
    source.setPixel(0, 0, Color.argb(0xE0, 0xFF, 0xEE, 0xDE));
    sp00 = source.getPixel(0, 0); // sp00   -520098082 [0xe0ffeede] OK!
    source.setPixel(0, 0, Color.argb(0xF0, 0xFF, 0xEE, 0xDE));
    sp00 = source.getPixel(0, 0); // sp00   -251662626 [0xf0ffeede] OK!

    source.setPixel(0, 0, Color.argb(0xA7, 0xFF, 0xEE, 0xDE));
    sp00 = source.getPixel(0, 0); // sp00   -1476399395 [0xa7ffeedd] BAD
    source.setPixel(0, 0, Color.argb(0xA3, 0xFF, 0xEE, 0xDE));
    sp00 = source.getPixel(0, 0); // sp00   -1543508258 [0xa3ffeede] OK!
    source.setPixel(0, 0, Color.argb(0xA1, 0xFF, 0xEE, 0xDE));
    sp00 = source.getPixel(0, 0); // sp00   -1577062690 [0xa1ffeede] OK!
    source.setPixel(0, 0, Color.argb(0xAB, 0xFF, 0xEE, 0xDE));
    sp00 = source.getPixel(0, 0); // sp00   -1409290274 [0xabffefde] BAD
    source.setPixel(0, 0, Color.argb(0xA9, 0xFF, 0xEE, 0xDE));
    sp00 = source.getPixel(0, 0); // sp00   -1442844962 [0xa9ffeede] OK!    

Well, more or less I found what is happening. 好吧,或多或少我发现了正在发生的事情。
It is called "pre-multiplied Alpha" and Bitmaps before api19 do not offer any way to control this feature, they are premultiplied by default and this cannot be changed. 在api19没有提供任何控制此功能的方法之前,它被称为“预乘Alpha”和位图,默认情况下它们是预乘的,这是无法更改的。
On api19 there are 2 new methods to Bitmap : isPremultiplied() and setPremultiplied(boolean) 在api19上有两种新的Bitmap方法: isPremultiplied()setPremultiplied(boolean)
According to the new doc: 根据新的文件:

When a pixel is pre-multiplied, the RGB components have been multiplied by the alpha component. 当像素预乘时,RGB分量乘以alpha分量。 For instance, if the original color is a 50% translucent red (128, 255, 0, 0), the pre-multiplied form is (128, 128, 0, 0). 例如,如果原始颜色是50%半透明红色(128,255,0,0),则预乘形式是(128,128,0,0)。

Also this post and this other post give some more explanation. 此帖另一篇文章也给出了更多解释。 According to this, some more tests show: 据此,还有一些测试表明:

    Bitmap source = Bitmap.createBitmap(2, 2, Config.ARGB_8888);
    source.setPixel(0, 0, Color.argb(0x02, 0x10, 0x20, 0x30));
    source.setPixel(0, 1, Color.argb(0x03, 0x10, 0x20, 0x30));
    source.setPixel(1, 0, Color.argb(0x05, 0x78, 0x96, 0x64));
    source.setPixel(1, 1, Color.argb(128, 255, 200, 150));

    int sp00 = source.getPixel(0, 0); // sp00   33554432 [0x2000000]    
    int sp01 = source.getPixel(0, 1); // sp01   50331733 [0x3000055]    
    int sp10 = source.getPixel(1, 0); // sp10   90610022 [0x5669966]    
    int sp11 = source.getPixel(1, 1); // sp11   -2130720875 [0x80ffc795]

For lower color values the rounding causes the info to be lost (see sp00 above). 对于较低的颜色值,舍入会导致信息丢失(请参阅上面的sp00)。
Also, for lower values, the value of alpha itself is not retrieved to the original. 此外,对于较低的值,alpha本身的值不会检索到原始值。
On top of this, the given formulas do not explain the values I see. 除此之外,给定的公式不解释我看到的值。

Finally to use unmodified pixels I now use this code to set the Bitmap pixles: 最后使用未修改的像素我现在使用此代码来设置位图像素:

    Bitmap source = Bitmap.createBitmap(2, 2, Config.ARGB_8888);
    IntBuffer data = IntBuffer.wrap(new int[] {
            Color.argb(0x06, 0xff, 0xa0, 0x8d),
            Color.argb(0x2a, 0xab, 0xce, 0x9f),
            Color.argb(0x8f, 0xfe, 0x05, 0x18),
            Color.argb(0xff, 0xc8, 0xcf, 0xd4)
    });
    source.copyPixelsFromBuffer(data);

and to retrieve the pixels I use: 并检索我使用的像素:

    IntBuffer sourceData = IntBuffer.allocate(4);
    source.copyPixelsToBuffer(sourceData);

Using these methods do not premultiply the color. 使用这些方法不会预先计算颜色。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM