位图中的像素值困扰着我(使用setPixel预乘颜色)

4
我无法解释这个(来自Eclipse调试的截图): full screenshot Code detail Varialbles detail (0,0)处的像素没有设置为预期的值!其他所有像素都没问题,它们确实具有分配的相同值。
编辑 我进行了更深入的挖掘,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    }

因此,我使用不同的数值运行setPixel(0,0),从对setPixel(0,1)正常工作的数值开始。

首先,我仅更改了其中一个参数。结论是alpha值是罪魁祸首,但不确定具体原因。我尝试了许多其他alpha值,似乎在大约0xB0及以上的值时结果会返回OK。

我在其他像素上尝试了相同的值,问题并不取决于像素坐标,对于其他像素也会失败。该问题似乎与数据有关。

    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!    

奇怪...你的位图是否可变? - donfuxx
根据Bitmap.createBitmap的文档: "返回一个指定宽度和高度的可变位图。" @donfuxx - ilomambo
没错,我刚刚检查了我的项目,并使用了相同的方法来获取可变位图,那里它运行良好。问题肯定是出在其他地方。 - donfuxx
@donfuxx 你使用了和我失败的像素相同的值吗?我马上会发布尝试其他值的结果。 - ilomambo
不,我并没有完全这样做,只是在查看我使用可变位图的一些代码。 - donfuxx
1个回答

3

好的,我找到了正在发生的事情。


这被称为“预乘Alpha”,api19之前的位图没有任何方法来控制此功能,它们默认情况下是预乘的,这是无法改变的。


在api19中,Bitmap有两个新方法:isPremultiplied()setPremultiplied(boolean)


根据新文档:

当像素预乘时,RGB分量已经乘以α分量。例如,如果原始颜色是50%半透明红色(128、255、0、0),则预乘形式为(128、128、0、0)。

此外,这篇文章这篇文章提供了更多解释。根据此,一些测试显示:

    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]

对于较低的颜色值,四舍五入会导致信息丢失(参见上面的sp00)。
此外,对于较低的值,alpha本身的值也未恢复到原始值。
除此之外,给定的公式并不能解释我所看到的值。

最后,要使用未修改的像素,我现在使用以下代码设置位图像素:

    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);

为了获取像素,我使用以下代码:

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

使用这些方法不会预乘颜色。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接