Sign in to follow this  
MARS_999

IMG_Load and knowing the RGB order

Recommended Posts

I need to know after I load the image data with IMG_Load() what order the channels are in. I have .tga files which are in BGR I am assuming. PNG, JPG must be in RGB order? Is there anyway to access some variable in the SDL_Surface struct to know this? I looked and don't see any... Thanks

Share this post


Link to post
Share on other sites
SDL_Surface->format which holds

typedef struct{
SDL_Palette *palette;
Uint8 BitsPerPixel;
Uint8 BytesPerPixel;
Uint32 Rmask, Gmask, Bmask, Amask;
Uint8 Rshift, Gshift, Bshift, Ashift;
Uint8 Rloss, Gloss, Bloss, Aloss;
Uint32 colorkey;
Uint8 alpha;
} SDL_PixelFormat;

And IMG_Load returns a SDL_Surface

Share this post


Link to post
Share on other sites
I just manually do it like this: (I'm assuming you are using SDL to load images for use with OpenGL)

Uint32 GL_PIXEL_TYPE = GL_BGR;

//To make sure the color is correct in PNG textures.
if(textureName.find(".png") != std::string::npos)
GL_PIXEL_TYPE = GL_RGB;

...

glTexImage2D(GL_TEXTURE_2D, 0, 3, tempSurface->w, tempSurface->h,
0, GL_PIXEL_TYPE, GL_UNSIGNED_BYTE, tempSurface->pixels);


However, if I recall right, the "Uint32 Rmask, Gmask, Bmask, Amask;" holds data on which bytes is which color channel. (View the bottom example here)

It looks like SDL_ConvertSurface might be a better option, though, allowing you to convert all your loaded images to BGR, or all of them to RGB, but I haven't tried it myself.

Share this post


Link to post
Share on other sites
No, I just searched for the extension myself in the loading function. I also keep the filename+path, to reload the textures whenever the window is resized.

Here's the entire code I used: (This is taken from a project I was working on a few months ago)
ITexture ITextureManager::LoadTexture(std::string textureName)
{
//Search for a matching texture, before deciding to load a new one.
for(int i = 0; i < TextureArray.size(); i++)
{
if(TextureArray[i].filename == textureName)
{
TextureArray[i].references++;
return i;
}
}

Engine->GetLogger()->Log("ITextureManager::LoadTexture(\"%s\")\n", textureName.c_str());

_TEXTURE NewTexture;
NewTexture.filename = textureName;
NewTexture.references = 0;
NewTexture.textureID = 0;

GLuint glTexture = 0;
SDL_Surface* tempSurface = NULL;

//Load the SDL_Surface.
tempSurface = IMG_Load(textureName.c_str());
if(!tempSurface)
{
Engine->GetLogger()->LogError("IMG_Load: %s\n", SDL_GetError());
return 0;
}

//Make sure the texture is a power of two
int newWidth = tempSurface->w;
int newHeight = tempSurface->h;

if(!IsPowerOfTwo(newWidth))
newWidth = NextPowerOfTwo(newWidth);
if(!IsPowerOfTwo(newHeight))
newHeight = NextPowerOfTwo(newHeight);

//If it's not a power of two, make it one. (And send a warning that we had to alter the image)
if(newWidth != tempSurface->w || newHeight != tempSurface->h)
{
Engine->GetLogger()->Log("[!]Texture is not a power of two! Resizing...\n");
SDL_Surface* resizedSurface = NULL;

//Create a new image of a proper power-of-two.
resizedSurface = SDL_CreateRGBSurface(tempSurface->flags, newWidth, newHeight, tempSurface->format->BitsPerPixel,
tempSurface->format->Rmask, tempSurface->format->Gmask, tempSurface->format->Bmask, tempSurface->format->Amask);
if(!resizedSurface)
{
Engine->GetLogger()->LogError("SDL_CreateRGBSurface: %s\n", SDL_GetError());
return 0;
}

//Fill the extra space we created with a bright color so it's visible as a mistake.
SDL_FillRect(resizedSurface, NULL, 0xFF00FF);
//Blit the old image, over the new image.
SDL_BlitSurface(tempSurface, NULL, resizedSurface, NULL);

//Finally, free the old image, and point the old pointer at the new image.
SDL_FreeSurface(tempSurface);
tempSurface = resizedSurface;
}


Uint32 GL_PIXEL_TYPE = GL_BGR;

//To make sure the color is correct in PNG textures.
if(textureName.find(".png") != std::string::npos)
GL_PIXEL_TYPE = GL_RGB;

//Generate and bind a new GL texture ID.
glGenTextures(1, &glTexture);
glBindTexture(GL_TEXTURE_2D, glTexture);

//Setup the parameters.
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

//Create the new OpenGL texture out of the old SDL_Surface image.
glTexImage2D(GL_TEXTURE_2D, 0, 3, tempSurface->w, tempSurface->h,
0, GL_PIXEL_TYPE, GL_UNSIGNED_BYTE, tempSurface->pixels);

//Free the SDL_Surface.
SDL_FreeSurface(tempSurface);

//Push back the new texture, a return the ID.
NewTexture.textureID = glTexture;
TextureArray.push_back(NewTexture);
return TextureArray.size() - 1;
}

Share this post


Link to post
Share on other sites
Thanks for the tip, I had a feeling that most were using the file extension, and I was hoping to avoid having to do that, but I will use that method for now.

BTW is there a site that lists the different texture formats RGBA orders for each format.

e.g.
TGA = BGRA
PNG = RGBA
JPG = RGBA

I need to get the channel orders. I hope there is a matrix that shows them and not a bunch of reading. :)

Share this post


Link to post
Share on other sites
The order of the channels within each pixel's data are specified by the masks/shifts in the format structure. You don't need to know what each file type uses, since you can extract that from the masks and shifts once it's loaded.

Share this post


Link to post
Share on other sites
Thanks Kylotan. I tried this and what I see is values as RGBA, that is fine, but I don't want to know what the color value is for that pixel such as 255,255,255 I want to know if the pixel order in the surface is RGB being red is first, green, then blue or if its BGR. Unless you are saying just flip the data yourself with the values I get from the masks?

Anyway here is the function


void TextureLoader::ExtractColorFrom32BitSDLSurface(SDL_Surface* surface)
{
/* Extracting color components from a 32-bit color value */
SDL_PixelFormat *fmt;
Uint32 temp, pixel;
Uint8 red, green, blue, alpha;

fmt = surface->format;
SDL_LockSurface(surface);
pixel = *((Uint32*)surface->pixels);
SDL_UnlockSurface(surface);

/* Get Red component */
temp = pixel & fmt->Rmask; /* Isolate red component */
temp = temp >> fmt->Rshift; /* Shift it down to 8-bit */
temp = temp << fmt->Rloss; /* Expand to a full 8-bit number */
red = (Uint8)temp;

/* Get Green component */
temp = pixel & fmt->Gmask; /* Isolate green component */
temp = temp >> fmt->Gshift; /* Shift it down to 8-bit */
temp = temp << fmt->Gloss; /* Expand to a full 8-bit number */
green = (Uint8)temp;

/* Get Blue component */
temp = pixel & fmt->Bmask; /* Isolate blue component */
temp = temp >> fmt->Bshift; /* Shift it down to 8-bit */
temp = temp << fmt->Bloss; /* Expand to a full 8-bit number */
blue = (Uint8)temp;

/* Get Alpha component */
temp = pixel & fmt->Amask; /* Isolate alpha component */
temp = temp >> fmt->Ashift; /* Shift it down to 8-bit */
temp = temp << fmt->Aloss; /* Expand to a full 8-bit number */
alpha = (Uint8)temp;

std::cout << "Pixel Color RGB(A) = " << int(red) << "," << int(green) << "," << int(blue) << "," << int(alpha) << std::endl;
}





[Edited by - MARS_999 on May 15, 2008 2:34:23 PM]

Share this post


Link to post
Share on other sites
The masks tell you whereabouts in each pixel the red, the green, the blue, and the alpha components are located. The shift is another way of presenting that information, namely how many bits 'in' to the pixel each component is. That is, by definition, the order of those components. Compare the shift values or the masks and you have your answer. Do you understand how these relate to what you want? (Not that I'm sure why you want it, since it's usually irrelevant at this stage.)

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this