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

