Jason Rogers
Jason Rogers

Reputation: 19344

Issues with GLUtils.texImage2D and Alpha in Textures

I'm successfully generating my textures using GLUtils.texImage2D, but when I use the textures generated I get problems with my alpha: they are darker than wanted.

after having checked several things I finally got the the conclusions that the problem comes from GLUtils.texImage2D(GL10.GL_TEXTURE_2D, level, bmp, 0);

I created a second function that uses gl.glTexImage2D(GL10.GL_TEXTURE_2D, level, GL10.GL_RGBA, width, height, 0, GL10.GL_RGBA, GL10.GL_UNSIGNED_BYTE, pixels2);

but it is costly in processing to create pixels2 which is a bytebuffer in which I have to recopy the bytes while changing the values from the bitmap ARGB to texture RGBA.

Has anybody noticed that ? and if so how did you solve this...

jason


Thank you for your answer, I'm already using

gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);

and I'm getting this problem

my problem is that the alpha generated by GLUtils isn't the one of the texture, its darker.

the difference is like looking at a color in the sun and in the shade (if that makes any sence).

I already tried gl.gltextimage2d but the creating the buffer takes too long, unless there is a tool to convert a bitmap to a byte buffer that I don't know of...

Upvotes: 6

Views: 7159

Answers (6)

wilddev
wilddev

Reputation: 1954

Android's BitmapFactory.decode() premultiplies alpha by default on loading. So if you don't want to load premultiplied bitmaps, use Bitmap.Options with inPremultiplied set to true when loading the bitmap for texture:

//kotlin
val options = BitmapFactory.Options()
options.inPremultiplied = false

val bitmap = BitmapFactory.decodeStream(inputStream, null, options)

Then pass this bitmap to GLUtils.texImage2D


P.S. Nice video for understanding premultiplied alpha:

https://www.youtube.com/watch?v=wVkLeaWQOlQ

Upvotes: 1

Prizoff
Prizoff

Reputation: 4575

Solution is found here. It is related as is stated by others with premultiplied alpha.

In the surfaceView Constructor

    setEGLConfigChooser(8, 8, 8, 8, 0, 0); 
    getHolder().setFormat(PixelFormat.RGBA_8888); 

In the View.Renderer onSurfaceCreated

    gl.glEnable(GL10.GL_BLEND); 
    gl.glBlendFunc(GL10.GL_ONE, GL10.GL_ONE_MINUS_SRC_ALPHA); 

Upvotes: 3

keaukraine
keaukraine

Reputation: 5364

Android Bitmap stores images loaded from PNG with premultiplied colors. GLUtils.texImage2D also uses colors premultiplied by alpha, so you can't get original colours this way.

In order to load PNG images without RGB channels being premultiplied I use 3rd party PNGDecoder and load texture with glTexImage2D. You can get PNGDecoder library to decode PNG from here: http://twl.l33tlabs.org/#downloads

Upvotes: 1

Eike Decker
Eike Decker

Reputation: 102

The alpha channel is the poor mistreated stepchild of a huge number of programmers is all I can say... but the upload works fairly efficient if you do that:

  • Estimate your largest texture (like 1024x1024) and create an int array of that size (1024*1024) that you store in some static variable or somewhere where you can access it so that you don't need to recreate that array (allocation time is precious here)

  • then do this:

    bitmap.getPixels(pixels, 0, width, 0, 0, width, height);
    gl.glTexImage2D(GL10.GL_TEXTURE_2D, 0, GL10.GL_RGBA, width, height, 
         0, GL10.GL_RGBA, GL10.GL_UNSIGNED_BYTE, IntBuffer.wrap(pixels));
    

I am sorry not having found a different solution... the original problem is, that the implementor of the GLUtils.texImage2D function has mistreated the alpha channel somehow resulting in black borders when the image is displayed larger than it is (the bilinear filter calculates the color between four pixels and when the rgb values of the pixels with transparent alphas have been mangled (like set to black), the result is, that there's some kind of a "color bleeding" happening over the transparent border that is forming there. Very ugly. Maybe it was done to improve the compression ratio as the RGB values in alpha transparent areas in PSDs contain a lot of junk that when eliminated yield a lot of room of improvement for compression algorithms)

Edit: Sadly, this approach was only working for grey images correctly as the red and blue channel is swapped when fetching the pixels from the bitmap. At least on MY device. I am not sure how correctly this is for other devices, but in my case, this here did the trick:

for (int i=0;i<pixels.length;i+=1) {
    int argb = pixels[i];
    pixels[i] = argb&0xff00ff00 | ((argb&0xff)<<16) | ((argb>>16)&0xff);
}

Upvotes: 4

ohsawa
ohsawa

Reputation: 51

GLUtils.texImage2D generates a premultiplied-alpha texture. In this generated texture, all pixels are multiplied by alpha value, so you don't need to multiply alpha value once again. Let's try

gl.glBlendFunc(GL10.GL_ONE, GL10.GL_ONE_MINUS_SRC_ALPHA);

Upvotes: 5

cement
cement

Reputation: 2905

There is an issues in GLUtils with premultiplied alpha. The only workaround that I can propose is to use:

gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA)

In case you need other blend functions you will have to use gl.glTexImage2D.

Upvotes: 0

Related Questions