Single-Call OpenGL Texture Loader in DevIL

I’ve been playing around with the DevIL image loading library today, and it has a command to load an image and assign it to an OpenGL texture in one fell swoop like this:

GLuint textureHandle = ilutGLLoadImage("some-picture.jpg");

Only I’m damned if I could get it to work, through I could take the long route and get DevIL to load/bind textures just fine, so I went and wrote my own version of ilutGLLoadImage, which works wonderfully =D

OpenGL DevIL Image Loader

Here’s the code:

  1. // Function load a image, turn it into a texture, and return the texture ID as a GLuint for use
  2. GLuint loadImage(const char* theFileName)
  3. {
  4. 	ILuint imageID;				// Create an image ID as a ULuint
  5.  
  6. 	GLuint textureID;			// Create a texture ID as a GLuint
  7.  
  8. 	ILboolean success;			// Create a flag to keep track of success/failure
  9.  
  10. 	ILenum error;				// Create a flag to keep track of the IL error state
  11.  
  12. 	ilGenImages(1, &imageID); 		// Generate the image ID
  13.  
  14. 	ilBindImage(imageID); 			// Bind the image
  15.  
  16. 	success = ilLoadImage(theFileName); 	// Load the image file
  17.  
  18. 	// If we managed to load the image, then we can start to do things with it...
  19. 	if (success)
  20. 	{
  21. 		// If the image is flipped (i.e. upside-down and mirrored, flip it the right way up!)
  22. 		ILinfo ImageInfo;
  23. 		iluGetImageInfo(&ImageInfo);
  24. 		if (ImageInfo.Origin == IL_ORIGIN_UPPER_LEFT)
  25. 		{
  26. 			iluFlipImage();
  27. 		}
  28.  
  29. 		// Convert the image into a suitable format to work with
  30. 		// NOTE: If your image contains alpha channel you can replace IL_RGB with IL_RGBA
  31. 		success = ilConvertImage(IL_RGB, IL_UNSIGNED_BYTE);
  32.  
  33. 		// Quit out if we failed the conversion
  34. 		if (!success)
  35. 		{
  36. 			error = ilGetError();
  37. 			std::cout << "Image conversion failed - IL reports error: " << error << " - " << iluErrorString(error) << std::endl;
  38. 			exit(-1);
  39. 		}
  40.  
  41. 		// Generate a new texture
  42. 		glGenTextures(1, &textureID);
  43.  
  44. 		// Bind the texture to a name
  45. 		glBindTexture(GL_TEXTURE_2D, textureID);
  46.  
  47. 		// Set texture clamping method
  48. 		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
  49. 		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
  50.  
  51. 		// Set texture interpolation method to use linear interpolation (no MIPMAPS)
  52. 		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  53. 		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  54.  
  55. 		// Specify the texture specification
  56. 		glTexImage2D(GL_TEXTURE_2D, 				// Type of texture
  57. 					 0,				// Pyramid level (for mip-mapping) - 0 is the top level
  58. 					 ilGetInteger(IL_IMAGE_FORMAT),	// Internal pixel format to use. Can be a generic type like GL_RGB or GL_RGBA, or a sized type
  59. 					 ilGetInteger(IL_IMAGE_WIDTH),	// Image width
  60. 					 ilGetInteger(IL_IMAGE_HEIGHT),	// Image height
  61. 					 0,				// Border width in pixels (can either be 1 or 0)
  62. 					 ilGetInteger(IL_IMAGE_FORMAT),	// Format of image pixel data
  63. 					 GL_UNSIGNED_BYTE,		// Image data type
  64. 					 ilGetData());			// The actual image data itself
  65.  	}
  66.   	else // If we failed to open the image file in the first place...
  67.   	{
  68. 		error = ilGetError();
  69. 		std::cout << "Image load failed - IL reports error: " << error << " - " << iluErrorString(error) << std::endl;
  70. 		exit(-1);
  71.   	}
  72.  
  73.  	ilDeleteImages(1, &imageID); // Because we have already copied image data into texture data we can release memory used by image.
  74.  
  75. 	std::cout << "Texture creation successful." << std::endl;
  76.  
  77. 	return textureID; // Return the GLuint to the texture so you can use it!
  78. }

This means that if I had a picture called SpringLandscape.jpg in the same folder as my code, I could bind it to a texture like this:

GLuint textureHandle = loadImage("SpringLandscape.jpg");

Ha! I’m getting the hang of this coding lark… =P

Here’s some complete example code to get you up and running: DevIL-Image-Loader.cpp

Update 11/2010: I had a think about this the next day and decided I wanted the best quality filtering available (which is MipMaps using the GL_LINEAR_MIPMAP_LINEAR function), so to do this you can replace lines 51 to 53 with:

// Set texture interpolation method to the highest visual quality it can be:
// GL_LINEAR_MIPMAP_LINEAR for minification - i.e. trilinear filtering
// GL_LINEAR for magnification (choices are either GL_NEAREST or GL_LINEAR - we do not use any MIPMAP settings for magnification!!
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

And then straight after the glTexImage2D statement, add this line:

glGenerateMipmap(GL_TEXTURE_2D); // Note: This requires OpenGL 3.0 or higher

You’re going to have to abide by that caveat though and use GLEW or something to make the extension available to you, I’d suggest putting something like this into your code immediately after initialising your OpenGL context:

//  ----- Initialise GLEW -----
 
GLenum err = glewInit();
if (GLEW_OK != err)
{
	cout << "GLEW initialisation error: " << glewGetErrorString(err) << endl;
	exit(-1);
}
cout << "GLEW intialised successfully. Using GLEW version: " << glewGetString(GLEW_VERSION) << endl;

Cheers!

Update 09/2011: To use any DevIL functionality in your C++ project, you need to link in libIL, libILU and libILUT (in that order), and then initialise DevIL before you use it with:

	 //  ----- Initialise DevIL -----
	ilutRenderer(ILUT_OPENGL);
	ilInit();
	iluInit();
	ilutInit();
	ilutRenderer(ILUT_OPENGL);

You either have to specify your renderer before or after initialisation, and I don’t know which, so I just call it twice and don’t worry about it. If you know definitively whether it should be called before or after the il/ilu/ilut initialisation calls then share the wealth, bubba ;)

25 thoughts on “Single-Call OpenGL Texture Loader in DevIL”

  1. hi, how did you get devil to work? i think i’m having trouble linking the libraries. i’ve got these errors:

    g++ -Wall -o “Basic” “Basic.cpp” -lglut -lGLU -lGL -lIL (in directory: /home/casper/Desktop/UBUNTU/C++ GLUT/SHAPES)
    /tmp/ccYn2MJr.o: In function `loadImage(char const*)’:
    Basic.cpp:(.text+0x49): undefined reference to `iluGetImageInfo’
    Basic.cpp:(.text+0x58): undefined reference to `iluFlipImage’
    Basic.cpp:(.text+0x88): undefined reference to `iluErrorString’
    Basic.cpp:(.text+0x24c): undefined reference to `iluErrorString’
    /tmp/ccYn2MJr.o: In function `main’:
    Basic.cpp:(.text+0x605): undefined reference to `ilutRenderer’
    Basic.cpp:(.text+0x60f): undefined reference to `iluInit’
    Basic.cpp:(.text+0x614): undefined reference to `ilutInit’
    Basic.cpp:(.text+0x620): undefined reference to `ilutRenderer’
    collect2: ld returned 1 exit status
    Compilation failed.

    linking to -lIL caused fixes to some of the previous error, and this time the errors above appears.

    i’m not quite sure what’s troubling the program.

    1. Hi John,

      You need to link in libIL, libILU and libILUT (in that order), and then initialise DevIL before you use it with:

      	 //  ----- Initialise DevIL -----
      	ilutRenderer(ILUT_OPENGL);
      	ilInit();
      	iluInit();
      	ilutInit();
      	ilutRenderer(ILUT_OPENGL);

      I never know if I’m supposed to specify OpenGL as the render before or after initialisation (and the docs don’t make it clear) so I just set the renderer twice – works just fine.

      I should have put this info in the article itself – oops. Will update tfa now ;)

    1. I’m not sure what kind of extension you’re talking about – do you mean file extension, like .jpg, .png etc?

      Also, you haven’t specified which platform you’re on, and if you downloaded a package or built it yourself or what.

      Maybe try loading a couple of different files and filetypes (i.e. convert the image you want to use to a few different formats and try them), or get the DevIL source and build it yourself – it’s the only way to be sure ;)

  2. Extension is tru but its says me its false.I searched and found its a bug.and
    GLuint texture=ilutGLLoadImage(“bla.jpg”); is ti neden glTexImage2D()?

    1. Oh, I get you now – OpenGL extension. Are you using GLEW? I’ve had GLee give incorrect extension availability before, which is why I switched to GLEW.

      To rule out the OpenGL 3.0 thing, you could also try not use mipmaps to just try and get things working by changing the lines:

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

      to read:

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

      and then getting rid of the glGenerateMipmap(GL_TEXTURE_2D) call.

  3. i meant how am i gonna use ilutGLLoadImage();after that is it needed glTexImage2D();if it is how am i get width,height and bit?with ilGetInteger(IL_IMAGE_WIDHT)?

        1. Yes, I did. But I finally solved it. Just to let you know, I incorporated your code into mine. The program is compiling now.I went to Devpak.org and downloaded DevIL. Then I opened my program in Dev-C++ and went to Tool->Package Manager-> and clicked on install. I followed the Wizard directions. It asked me to upload the package I had just downloaded. Once the installation was complete, DevIL was added to the packages in package manager. That’s all I had to do. To verify that all the libraries were on my machine, I went to C:\Dev-cpp\lib

          I saw all the ilu and ilut libraries: the object libraries and those ending in .a. I think the files ending in .a were the ones the compiler was looking for, and not the object library files. Now the program is compiling and all the errors went away.

          1. I have another question. The image I am uploading is not showing. I think the problem is with the “drawScene()” in the main function. I changed mine to “display()” because that was where I have the functions to the textures I drew. I drew a circle, triangle, etc, in different functions and I put all the functions in “display()”. So where you have “drawScene()”, I have “display()” there. But I don’t see the image. Could that be the problem or it might be something else? When I take you code out of my program, I can see the shapes but when I include yours and try to upload it, nothing comes up.

            // Load an image and bind it to a texture
            texture[0] = loadImage(imageName);

            while (running == true)
            {
            // Draw our scene
            drawScene();

            // exit if ESC was pressed or window was closed
            running = !glfwGetKey(GLFW_KEY_ESC) && glfwGetWindowParam( GLFW_OPENED);
            }

            glfwTerminate();

            return 0;

            1. OpenGL black-screen problems are notorious for being a pain to troubleshoot as they could be caused by lots of things. The first things I’d check would be if you’re binding to your texture before doing the drawing, and that your polygon-winding is anti-clockwise (i.e. front-facing by default, although you can change the winding order if desired).

              Oh, and be aware that any colour settings you have specified can affect how the textured polygon is drawn, so you might want to call glcolor4f(1.0f, 1.0f, 1.0f, 1.0f); before drawing anything.

              The cut-down version of my “display()” function has this:

              glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear the screen and depth buffer
               
              // Reset the matrix to identity
              glMatrixMode(GL_MODELVIEW);
              glLoadIdentity();
               
              glTranslatef(0.0f, 0.0f, -40.0f); // Move things back into the screen
               
              glBindTexture(GL_TEXTURE_2D, textureHandle); // Select the texture to use and bind to it
               
              static float hsize = 15.0f; // Vertical size of the quad
              static float vsize = 10.0f; // Vertical size of the quad
               
              glEnable(GL_TEXTURE_2D);
               
              // Draw our textured geometry (just a rectangle in this instance)
              glBegin(GL_QUADS);                         // Anti-clockwise winding = front facing
              	glTexCoord2f(0.0, 0.0);
              	glVertex3f(-hsize, -vsize, 0.0f);  // Top left
              	glTexCoord2f(0.0, 1.0);
              	glVertex3f(-hsize,  vsize, 0.0f);  // Bottom left
              	glTexCoord2f(1.0, 1.0);
              	glVertex3f(hsize,   vsize, 0.0f);  // Bottom right
              	glTexCoord2f(1.0, 0.0);
              	glVertex3f(hsize,  -vsize, 0.0f);  // Top right
              glEnd();
               
              glDisable(GL_TEXTURE_2D);
               
              // ----- Stop Drawing Stuff! ------
               
              glfwSwapBuffers();

              I’ve posted up the full, working version for you here: OpenGLTextureTest.cpp – hope it helps.

              1. The image is still not uploading. This is how my display function looks:

                void display(void)
                {
                 
                    //clear the screen
                   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
                 
                 
                     glMatrixMode(GL_MODELVIEW);
                     glLoadIdentity();
                     glRotatef(Angle, 0.0, 0.0, 0.0);
                 
                    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL); 
                 
                   	// Select the texture to use
                    glBindTexture(GL_TEXTURE_2D, texture[0]);
                 
                 
                	// Draw our texture
                	glEnable(GL_TEXTURE_2D);
                 
                    //Nose
                   drawTriangle();
                 
                   //Face
                   circle (250, 250, 400);
                 
                 
                   //Inner Eyes
                   glLoadIdentity();
                   circle(200, 280, 10);
                   drawCircle1(-30, 280, 10);
                 
                   //Eye lids
                   glLoadIdentity();
                   glScalef(0.6,0.2,0);
                   circle(530, 2400, 60);
                   circle(-150, 2150, 60);
                 
                   //Arc for eyebrows
                   drawArc(500, 500, -70, -140, 100);
                   drawArc(270, 500, -70, -140, 100);
                 
                   //Mouth
                   drawArc(380, 250, 110, -180, 180);
                  // drawArc(380, 100, -100, -180, 180);
                 
                   drawLine();
                 
                   	glDisable(GL_TEXTURE_2D);
                 
                	// ----- Stop Drawing Stuff! ------
                 
                	glfwSwapBuffers(); // Swap the buffers to display the scene (so we don't have to watch it being drawn!)
                 
                }
                 
                An example of the circle function which is called in the display function is:
                 
                void circle(GLint x, GLint y, GLint radius)
                {
                	float angle;
                 
                    glTranslatef(300, 100, 0.0);
                	glTranslatef(0.0,150.0, 1.0);
                 
                	glColor3f(0.0f, 1.0f, 0.0f);
                	glBegin(GL_LINE_LOOP);
                	for (int i = 0; i < 100; i++)
                	{
                		angle = i*2*PI/100;
                		float X = x + (cos(angle) * radius);
                		float Y = y + (sin(angle) * radius);
                		glVertex2f(X, Y);
                		//glVertex2f(x + (cos(angle) * radius), y + (sin(angle) * radius));
                	}
                	glEnd();
                 
                }

                I don’t think it should be a problem calling a function from the display function

                1. Hi Annette,

                  You weren’t specifying texture co-ordinates as well as vertex co-ords, so it’ll work to draw colours, but not textures (where the co-ordinates are normalised to the range 0.0 -> 1.0).

                  I’d recommend getting a copy of the 4th edition of the OpenGL Superbible to help you along with this OpenGL lark (the 5th edition is pure shaders from the word go – so if you’re ready to make that jump by all means snag a copy of the 5th edition and go for it) – it’s a great book and it helped me tons when I was starting off.

                  Anyways, here’s some code which draws textured arcs and circles to get you started:
                  TextureFace.cpp (Windows line endings)
                  5-TextureFace.zip (Code::Blocks project w/ textures)

                  Which when run produces this monstrocity, but you get the idea… ;)

                  OpenGL Moon Face!

                  Regards,
                  r3dux

    1. Hi Florian,

      You can use the code without issue for any purpose – I wrote and posted it in the hope it’d be useful to someone! And as DevIL itself is released under the LGPL, there’s no issue in using it for commercial works.

      You don’t have to give me a mention in the credits, but it would be nice if my code’s helped you at all =D

      Best of luck with your production!

  4. hi,
    Just to let you and others following this wonderful blog post know. The third parameter of glTexImage2D is the internal format which should be ilGetInteger(IL_IMAGE_FORMAT) so the proper glTexImage2D call should be this:

    glTexImage2D(           GL_TEXTURE_2D, // Type of texture
                                        0,
            ilGetInteger(IL_IMAGE_FORMAT), // Image colour depth
             ilGetInteger(IL_IMAGE_WIDTH), // Image width
            ilGetInteger(IL_IMAGE_HEIGHT), // Image height
                                        0, // Border  
            ilGetInteger(IL_IMAGE_FORMAT), // Image format  
                         GL_UNSIGNED_BYTE, // Image data type
    			   ilGetData()
    );

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.