Part of the Khronos Group
OpenGL.org

The Industry's Foundation for High Performance Graphics

from games to virtual reality, mobile phones to supercomputers

Page 1 of 2 12 LastLast
Results 1 to 10 of 11

Thread: Color-key transparency for a texture image

  1. #1
    Junior Member Newbie
    Join Date
    Jul 2013
    Posts
    5

    Color-key transparency for a texture image

    I'm trying to write a function that color-keys a given texture image. For those of you not familiar with color-keying, you specify a certain color to remove from the image, and all pixels with that color are turned transparent. Right now I have this:

    Code :
    void color_key(int tex, unsigned int key)
    {
    	int w, h, x, y;
     
    	//Retrieve pixel data
    	glBindTexture(GL_TEXTURE_2D, tex);
    	glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &w);
    	glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &h);
    	unsigned int buffer[w][h];
    	glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_INT, buffer);
     
    	//Iterate through pixels -- if a pixel is equal to the key, set its alpha value to 0.
    	for (x = 0; x < w; x++)
    	{
    		for (y = 0; y < h; y++)
    		{
    			if (buffer[x][y] == key) 
    				buffer[x][y] &= 0xFFFFFF00;
    		}
    	}
     
    	//Save the modified texture
    	glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGBA, GL_UNSIGNED_INT, buffer);
    }

    but this causes the program to hang/crash without an error. I also tried using a 1D array to store the image, but it did the same thing.
    Commenting out the glGetTexImage and glTexSubImage2D makes it stop crashing, but of course the function does nothing after that.

    Anyone have an idea what I did wrong?
    Last edited by opusGlass; 07-22-2013 at 03:01 PM. Reason: added details

  2. #2
    Member Regular Contributor
    Join Date
    Aug 2008
    Posts
    445
    OpenGL is trying to write w*h*signof(unsigned int)*4 bytes (width*height*element_size*num_elements) into buffer.
    From the way you are adjusting the pixels, you probably want to use GL_UNSIGNED_BYTE instead of GL_UNSIGNED_INT in your glGetTexImage call (4*1 bytes per pixel instead of 4*4 bytes per pixel).

  3. #3
    Junior Member Newbie
    Join Date
    Jul 2013
    Posts
    5
    Thanks Dan, that fixed the crashing problem. I also added masking to ignore the alpha channel (so any pixel whose RGB values are the same as the key should be removed, regardless of its alpha component). It doesn't seem to be working though -- it doesn't do anything at all. Here's what I have now:

    Code :
    void color_key(int tex, unsigned int key)
    {
    	int w, h, x, y;
    	key &= 0xFFFFFF00;
     
    	//Retrieve pixel data
    	glBindTexture(GL_TEXTURE_2D, tex);
    	glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &w);
    	glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &h);
    	unsigned int buffer[w][h];
    	glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
     
    	//Iterate through pixels -- if a pixel's RGB is equal to that of the key, set its alpha value to 0.
    	for (x = 0; x < w; x++)
    	{
    		for (y = 0; y < h; y++)
    		{
    			if ((buffer[x][y] & 0xFFFFFF00) == key) 
    				buffer[x][y] &= 0xFFFFFF00;
    		}
    	}
     
    	//Save the modified texture
    	glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
    }

    I was under the impression glTexSubImage2D permanently changes the texture in memory. Is this not the case?

    Or could it maybe have something to do with mipmapping? I don't really know much about how that works, I just assumed that changing the full-detail version of the image would change all versions of it.
    Last edited by opusGlass; 07-22-2013 at 06:33 PM.

  4. #4
    Member Regular Contributor
    Join Date
    Aug 2008
    Posts
    445
    The other mipmap levels won't be automatically updated when you update level 0, unless you're using the legacy GL_SGIS_generate_mipmap extension.

    See http://www.g-truc.net/post-0256.html and http://www.opengl.org/wiki/Common_Mi...map_generation for a discussion on the preferred glGenerateMipmap vs the alternative gluBuild2DMipmaps / GL_GENERATE_MIPMAP methods.

    As you suspected, If you're updating level 0, but only higher mipmap levels are being chosen when drawing you won't see the results until the object is closer to you. One way you can help visualise which mipmap levels are visible is to upload a different color to each level, see http://www.arcsynthesis.org/gltut/Te...0Pictures.html for an example of this in action.

  5. #5
    Member Regular Contributor
    Join Date
    Jun 2013
    Posts
    490
    Quote Originally Posted by opusGlass View Post
    Code :
    	unsigned int buffer[w][h];
    Should be:
    Code :
    	unsigned int buffer[h][w];
    Similarly, the accesses should be buffer[y][x] and the loops should have the for-y loop outside the for-x loop.

    It happens to work in this case because using a 1-D array of (w*h) elements would also work.

    Although if the data wasn't RGBA, you would either need to use glPixelStore() to change the pack/unpack alignment (the default is 4-byte alignment), or modify the loop to allow for padding between rows.

  6. #6
    Junior Member Newbie
    Join Date
    Jul 2013
    Posts
    5
    Dan: That is unfortunate...I can't just go through all the mipmap levels, because the target pixels may have been blended with other pixels during the mipmapping. But it should work if I enable GL_GENERATE_MIPMAP, correct? (I'm using a legacy version of OpenGL -- whatever came with my Windows 8.)
    I will give it a try.

    GClements: Really? I'm surprised that OpenGL would use column-major order instead of row-major. Well, I can easily switch that around, but as you said it doesn't actually effect anything since the size of the array doesn't change. It's just a little misleading about where those pixels actually are in the image.

  7. #7
    Member Regular Contributor
    Join Date
    Jun 2013
    Posts
    490
    Quote Originally Posted by opusGlass View Post
    GClements: Really? I'm surprised that OpenGL would use column-major order instead of row-major.
    OpenGL stores textures in row-major order, which is why the existing code is wrong.

    Maybe you're confused about C array syntax? C doesn't have multi-dimensional arrays, but it does have arrays of arrays.

    The declaration
    Code :
    int buffer[h][w];
    declares "buffer" as an array of h elements, each if which is an array of w elements, each of which is an int.

    So for any integer i, buffer[i] is an array of w ints (i.e. a "row").

    For any integers i and j, buffer[i][j] is equivalent to (buffer[i])[j], i.e. element j within buffer[i], i.e. the cell in row i, column j.

  8. #8
    Junior Member Newbie
    Join Date
    Jul 2013
    Posts
    5
    Maybe you're confused about C array syntax? C doesn't have multi-dimensional arrays, but it does have arrays of arrays.
    I know C stores arrays in row-major order, but it considers the first index as the row and the second index as the column.

    An array declared in C as
    int A[2][3] = { {1, 2, 3}, {4, 5, 6} };
    is laid out contiguously in linear memory as:
    1 2 3 4 5 6
    From the wikipedia article, 'Row-Major Order'. So the most efficient way to iterate through a 2D array is:

    Code :
    for (x = 0; x < w; x++)
        for (y = 0; y < h; y++)
            array[x][y] ...

    Because you will access the elements in the order that they are stored in memory.

    I simply use x for the first parameter, making x the row and y the column, because that seems intuitive to me and is the way I was taught.

    Does OpenGL store x as the column and y as the row instead? I can see how this could be problematic in situations where I need to know the actual positions of pixels. In this case it still works either way, since I just need to modify every pixel regardless of where it is.
    Last edited by opusGlass; 07-23-2013 at 10:08 AM.

  9. #9
    Member Regular Contributor
    Join Date
    Jun 2013
    Posts
    490
    Quote Originally Posted by opusGlass View Post
    I simply use x for the first parameter, making x the row and y the column, because that seems intuitive to me and is the way I was taught.
    In which case, you're probably the first person I've ever encountered who uses that, or who would find it intuitive. By itself, I suppose that it's not necessarily wrong, but the universal convention is that X represents horizontal displacement while Y represents vertical displacement. Certainly, all of the OpenGL documentation uses this convention (e.g. glViewport).

    Also, if x is the row and y the column, then the loop tests require that w is the number of rows (height) and h the number of columns (width). But those names are conventionally abbreviations for width and height respectively. And aside from convention, that's how they're being used in the glGetTexLevelParameter() calls in your original code.

    If you swapped w and h in those calls, to:
    Code :
    	glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &h);
    	glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &w);
    then the code would be correct, at least in a formal sense. The variable names would confuse practically every other graphics programmer on the planet, but there wouldn't be anything else wrong with it.

  10. #10
    Intern Contributor
    Join Date
    May 2008
    Location
    USA
    Posts
    99
    Side note - pulling the pixels to the CPU and doing the work then pushing it back is a really poor idea. Use a fragment shader instead.

    Bruce

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •