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