4

I cannot explain this (screenshot from Eclipse debug):

full screenshot

Code detail

Varialbles detail

Pixel at (0,0) does not have the values it was set for!
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:

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).
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.
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!    
ilomambo
  • 8,290
  • 12
  • 57
  • 106
  • strange... is your bitmap mutable? – donfuxx Mar 09 '14 at 14:30
  • @donfuxx According to the doc on `Bitmap.createBitmap`: "Returns a mutable bitmap with the specified width and height." – ilomambo Mar 09 '14 at 14:32
  • true, I have just checked in my project and I used same method to get mutable Bitmap and there it worked fine. The problem must be somewhere else then – donfuxx Mar 09 '14 at 14:36
  • @donfuxx Did you use the same values as my failed pixel? Momentariliy I will post the result of trying other values. – ilomambo Mar 09 '14 at 14:58
  • no I did not do exactly the same, was just looking into some of my code where I was using a mutable Bitmap – donfuxx Mar 09 '14 at 15:01

1 Answers1

3

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.
On api19 there are 2 new methods to Bitmap: isPremultiplied() and setPremultiplied(boolean)
According to the new doc:

When a pixel is pre-multiplied, the RGB components have been multiplied by the alpha component. For instance, if the original color is a 50% translucent red (128, 255, 0, 0), the pre-multiplied form is (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).
Also, for lower values, the value of alpha itself is not retrieved to the original.
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.

Community
  • 1
  • 1
ilomambo
  • 8,290
  • 12
  • 57
  • 106