Bitmap and BitmapFactory work only with premultiplied alpha values. We
know it is annoying in some cases for GL developers and we've started
thinking of various solutions. I don't think we want to add support
for non-premultiplied bitmaps, which means we would either have a new
method in BitmapFactory that returns images as non-premultiplied byte
arrays or offer new APIs in GLUtils to let you upload resources
directly into non-premultiplied textures.

I'd be happy to hear your suggestions on the topic of course.

On Wed, Jun 20, 2012 at 1:25 PM, arberg <[email protected]> wrote:
> On Behalf of Marcus Mengs (mame8282):
>
> I'm using textures of combined grayscale images, for example a normal
> map
> with normal in RGB-components and height in A-component. So the
> described
> issue is a big problem for me. I've treid several fixes including
> yours or rewriting
> the createScaledBitmap() Method (to prevent premultiplying while
> scaling) etc.
>
> I coulld finally solve the problem. I no more rely on BitmapFactory-
> methods, instead
> I'm decoding the PNG myself. A good example how to do this, is the
> PNGDecoder.java
> from the LWJGL-backend of libgdx. It runs on Android with nearly no
> change needed.
>
> http://code.google.com/p/libgdx/source/browse/trunk/backends/gdx-backend-lwjgl/src/com/badlogic/gdx/backends/lwjgl/PNGDecoder.java?r=845
>
> With BitmapFactory it's nearly impossible to do this, because if you
> follow the source
> of decodeStream(), you'll get to a native method and thus to SKIA,
> which hard-coded
> the alpha-premultiply in the correspondending method.
>
> Marcus
>
> On Thursday, 12 August 2010 14:09:54 UTC+2, arberg wrote:
>>
>> I just realized the Bitmap-class behaviour is screwed. If I use the
>> following method for decoding the bitmap then I get alpha-
>> premultiplied pixels when calling Bitmap.getPixels:
>>
>>   InputStream is =
>> context.getResources().openRawResource(texture.resource);
>>   try {
>>     bitmap = BitmapFactory.decodeStream(is, null, sBitmapOptions);
>>   } finally {
>>     is.close();
>>   }
>>
>> If I use
>>
>>   bitmap = BitmapFactory.decodeResource(context.getResources(),
>> texture.resource, sBitmapOptions);
>>
>> then as mentioned Bitmap.getPixels returns non alpha-premultiplied
>> pixel values. Another difference between these two decode-methods is
>> that the latter (in my experience) throws out-of-memory exceptions
>> more frequently than the former, so either the
>> BitmapFactory.decodeResource-method uses more memory, or it uses more
>> memory when used in conjunction with my manual texImage2D loading
>> algorithm.
>>
>> Of cause nothing is mentioned in the Android javadoc for Bitmap.
>>
>> Alex
>>
>> On Aug 12, 10:50 am, arberg <[email protected]> wrote:
>> > Regarding the big-endian comment in the code, I meant little-endian.
>> > If we use IntBuffer to write ABGR ints to ByteBuffer on a little-
>> > endian-phone we get byte order we RGBA. However the same code running
>> > on a big-endian phone should produce ABGR byte order, which is not
>> > whatopenglexpects. So don't use intbuffer.
>> >
>> > The following should also work, and allocates less memory, but I don't
>> > quite trust it since I don't have a big-endian phone, and since I
>> > cannot test it on a big-endian emulator:
>> >
>> >         private static final boolean IS_LITTLE_ENDIAN =
>> > (ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN);
>> >         private void myTexImage2D(GL10 gl, Bitmap bitmap) {
>> >                 // Don't loading using GLUtils, load using gl-method
>> > directly
>> >                 // GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
>> >                 int[] pixels = extractPixels(bitmap);
>> >                 for (int i = pixels.length - 1; i >= 0; i--) {
>> >                         int p = pixels[i];
>> >                         int r = ((p >> 16) & 0xFF);
>> >                         int g = ((p >> 8) & 0xFF); // green
>> >                         int b = ((p) & 0xFF); // blue
>> >                         int a = (p >> 24); //alpha
>> >                         if (IS_LITTLE_ENDIAN) {
>> >                                 pixels[i] = a << 24 | b << 16 | g << 8 |
>> > r;
>> >                         } else {
>> >                                 pixels[i] = r << 24 | g << 16 | b << 8 |
>> > a;
>> >                         }
>> >                 }
>> >                 IntBuffer pixelBuffer = IntBuffer.wrap(pixels);
>> >
>> >                 gl.glTexImage2D(GL10.GL_TEXTURE_2D, 0, GL10.GL_RGBA,
>> > bitmap.getWidth(), bitmap.getHeight(), 0, GL10.GL_RGBA,
>> > GL10.GL_UNSIGNED_BYTE, pixelBuffer);
>> >         }
>> >
>> > Alex
>> >
>> > On Aug 12, 10:17 am, arberg <[email protected]> wrote:
>> >
>> >
>> >
>> > > Apparently the cause of thepremultipliedalphalies in GLUtils, or
>> > > perhaps the way GLUtils works with the Bitmap class. We can get the
>> > > correct non-premultipliedalphabehaviour by replacing
>> > > GLUtils.texImage2D with theopenglmethod gl.glTexImage2D which takes
>> > > a pixel component array as a parameter. Thus we can avoid
>> > > thepremultipliedalphabehaviour, which means we can use the blend
>> > > function gl.glBlendFunc(GL10.GL_SRC_ALPHA,
>> > > GL10.GL_ONE_MINUS_SRC_ALPHA);
>> >
>> > > Note for uninitiated confusedopenglandroid developers: I posted a
>> > > question onopengl.org discussion forum, where I described the
>> > > symptoms of the
>> > > problem:http://www.opengl.org/discussion_boards/ubbthreads.php?ubb=showflat&N...
>> >
>> > > Here's how I loaded the image in Android:
>> >
>> > >     private int loadTexture(GL10 gl, int resourceId) {
>> > >         int[] textureNameWorkspace = new int[1];
>> > >                 gl.glGenTextures(1, textureNameWorkspace, 0);
>> > >                 int textureName = textureNameWorkspace[0];
>> > >                 gl.glBindTexture(GL10.GL_TEXTURE_2D, textureName);
>> > >                 gl.glTexParameterf(GL10.GL_TEXTURE_2D,
>> > > GL10.GL_TEXTURE_MIN_FILTER,
>> > > GL10.GL_LINEAR);
>> > >                 gl.glTexParameterf(GL10.GL_TEXTURE_2D,
>> > > GL10.GL_TEXTURE_MAG_FILTER,
>> > > GL10.GL_LINEAR);
>> > >                 gl.glTexParameterf(GL10.GL_TEXTURE_2D,
>> > > GL10.GL_TEXTURE_WRAP_S,
>> > > GL10.GL_REPEAT);
>> > >                 gl.glTexParameterf(GL10.GL_TEXTURE_2D,
>> > > GL10.GL_TEXTURE_WRAP_T,
>> > > GL10.GL_REPEAT);
>> >
>> > >                 gl.glTexEnvf(GL10.GL_TEXTURE_ENV,
>> > > GL10.GL_TEXTURE_ENV_MODE,
>> > > GL10.GL_MODULATE);
>> >
>> > >         Bitmap bitmap = BitmapFactory.decodeResource(resources,
>> > > resourceId);
>> > >         myTexImage2D(gl, bitmap);
>> > >         // Set the crop parameter because I use drawtexture extension
>> > >                 ((GL11) gl).glTexParameteriv(GL10.GL_TEXTURE_2D,
>> > > GL11Ext.GL_TEXTURE_CROP_RECT_OES,
>> > >                                 new int[] { 0, bitmap.getHeight(),
>> > > bitmap.getWidth(), -
>> > > bitmap.getHeight() }, 0);
>> > >                 return textureName;
>> > >         }
>> >
>> > >         private void myTexImage2D(GL10 gl, Bitmap bitmap) {
>> > >                 // Don't loading using GLUtils, load using gl-method
>> > > directly
>> > >         // GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
>> > >         int[] pixels = extractPixels(bitmap);
>> > >         byte[] pixelComponents = new byte[pixels.length*4];
>> > >         int byteIndex = 0;
>> > >         for (int i = 0; i < pixels.length; i++) {
>> > >                 int p = pixels[i];
>> > >                         // Convert to byte representation RGBA
>> > > required by gl.glTexImage2D.
>> > >                         // We don't use intbuffer, because then we
>> > >                         // would be relying on the intbuffer wrapping
>> > > to write the ints in
>> > >                         // big-endian format, which means it would
>> > > work for the wrong
>> > >                         // reasons, and it might brake on some
>> > > hardware.
>> > >                 pixelComponents[byteIndex++] = (byte) ((p >> 16) &
>> > > 0xFF); // red
>> > >                 pixelComponents[byteIndex++] = (byte) ((p >> 8) &
>> > > 0xFF); //
>> > > green
>> > >                 pixelComponents[byteIndex++] = (byte) ((p) & 0xFF); //
>> > > blue
>> > >                 pixelComponents[byteIndex++] = (byte) (p >> 24);
>> > >  //alpha
>> > >         }
>> > >         ByteBuffer pixelBuffer = ByteBuffer.wrap(pixelComponents);
>> >
>> > >         gl.glTexImage2D(GL10.GL_TEXTURE_2D, 0, GL10.GL_RGBA,
>> > > bitmap.getWidth(), bitmap.getHeight(), 0, GL10.GL_RGBA,
>> > > GL10.GL_UNSIGNED_BYTE, pixelBuffer);
>> > >         }
>> >
>> > >         public static int[] extractPixels(Bitmap src) {
>> > >                 int x = 0;
>> > >                 int y = 0;
>> > >                 int w = src.getWidth();
>> > >                 int h = src.getHeight();
>> > >                 int[] colors = new int[w * h];
>> > >                 src.getPixels(colors, 0, w, x, y, w, h);
>> > >                 return colors;
>> > >         }
>> >
>> > > Since the above implementation does not use native code to convert the
>> > > bitmap and since it also allocates the bitmap array twice besides
>> > > loading the bitmap (thus taking three times the memory space) its not
>> > > quite optimal. It would be quite reasonable that Android supplied a
>> > > native method for doing this. Certainly the documenation for GLUtils
>> > > should state that it loads apremultipliedalphatexture. Does anyone
>> > > know if there is a bugreport/feature request on this on Android? I
>> > > cant seem to find any mentioning of it in the issue
>> > > list:http://code.google.com/p/android/issues/list
>> >
>> > > Alex.
>
> --
> You received this message because you are subscribed to the Google
> Groups "Android Developers" group.
> To post to this group, send email to [email protected]
> To unsubscribe from this group, send email to
> [email protected]
> For more options, visit this group at
> http://groups.google.com/group/android-developers?hl=en



-- 
Romain Guy
Android framework engineer
[email protected]

-- 
You received this message because you are subscribed to the Google
Groups "Android Developers" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to
[email protected]
For more options, visit this group at
http://groups.google.com/group/android-developers?hl=en

Reply via email to