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 <arb...@gmail.com> 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 <arb...@gmail.com> 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 android-developers@googlegroups.com
To unsubscribe from this group, send email to
android-developers+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/android-developers?hl=en

Reply via email to