macdevcenter.com
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

A Simple Mac OS X libpng Example with OpenGL
Pages: 1, 2, 3

Reading a PNG File Using libpng

The following code is a no-frills approach to reading a PNG file using the libpng library. I want the code to work cross-platform, so we'll stick with the simple approach of reading the file and verifying the PNG file format signature. If you have ever worked with different graphic formats, then you're aware of embedded byte headers in the files that identify the type of file being read. The PNG file format is no different. Here's how you open and read the PNG file.



/* Open the PNG file. */
infile = fopen(file, "rb");
if (!infile) {
  return 0;
}

/* Check for the 8-byte signature */
fread(sig, 1, 8, infile);
if (!png_check_sig((unsigned char *) sig, 8)) {
  fclose(infile);
  return 0;
}

Once the file is verified to be a PNG file, you'll begin setting up the libpng data structures.

/* 
 * Set up the PNG structs 
 */
png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (!png_ptr) {
  fclose(infile);
  return 4; /* out of memory */
}

info_ptr = png_create_info_struct(png_ptr);
if (!info_ptr) {
  png_destroy_read_struct(&png_ptr, (png_infopp) NULL, (png_infopp) NULL);
  fclose(infile);
  return 4; /* out of memory */
}

end_ptr = png_create_info_struct(png_ptr);
if (!end_ptr) {
  png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) NULL);
  fclose(infile);
  return 4; /* out of memory */
}

We have created three pointers to libpng data structures. These are:

png_structp png_ptr; 
png_infop info_ptr; 
png_infop end_ptr; 

Our png_ptr is used by the libpng library to maintain, basically, state information about the PNG file as it is being read in by the library. It's used for housekeeping by the libpng library. The other two pointers, info_ptr and end_ptr, are used to help us extract data from the PNG file. You'll see png_ptr and info_ptr passed in together as arguments consistently throughout the code once the png_ptr and info_ptr data structures are initialized. The last pointer, end_ptr, is merely a placeholder pointer to data or chunks that may exist after the image data in our file. This is what we have for initializing the pointers and reading the image file data.

png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (!png_ptr) {
  fclose(infile);
  return 4; /* out of memory */
}

info_ptr = png_create_info_struct(png_ptr);
if (!info_ptr) {
  png_destroy_read_struct(&png_ptr, (png_infopp) NULL, (png_infopp) NULL);
  fclose(infile);
  return 4; /* out of memory */
}

end_ptr = png_create_info_struct(png_ptr);
if (!end_ptr) {
  png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) NULL);
  fclose(infile);
  return 4; /* out of memory */
}

/*
 * block to handle libpng errors, 
 * then check whether the PNG file had a bKGD chunk
 */
if (setjmp(png_jmpbuf(png_ptr))) {
  png_destroy_read_struct(&png_ptr, &info_ptr, &end_ptr);
  fclose(infile);
  return 0;
}

/*
 * takes our file stream pointer (infile) and 
 * stores it in the png_ptr struct for later use.
 */
png_ptr->io_ptr = (png_voidp)infile;

/*
 * lets libpng know that we already checked the 8 
 * signature bytes, so it should not expect to find 
 * them at the current file pointer location
 */
png_set_sig_bytes(png_ptr, 8);

That's our code for the data structure initialization. Again, I cloned the lines from Chapter 13.3 of the PNG book, which uses the return 4 /* out of memory */ code.

Let's read in the image info. The code looks like this.

png_read_info(png_ptr, info_ptr);
png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, 
&color_type, NULL, NULL, NULL);
*pwidth = width;
*pheight = height;

/* snipped out the color type code, see source pngLoad.c */
/* Update the png info struct.*/
png_read_update_info(png_ptr, info_ptr);

/* Rowsize in bytes. */
rowbytes = png_get_rowbytes(png_ptr, info_ptr);

/* Allocate the image_data buffer. */
if ((image_data = (unsigned char *) malloc(rowbytes * height))==NULL) {
  png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
  return 4;
}

if ((row_pointers = (png_bytepp)malloc(height*sizeof(png_bytep))) == NULL) {
  png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
  free(image_data);
  image_data = NULL;
   return 4;
}

Now, the most important point of all, this code is to be used for loading PNG files into OpenGL. The code as it's presented will load the PNG file inverted in OpenGL. The OpenGL coordinate system has its origin in the lower-left corner of the texture, where the PNG image has its origin in the upper-left corner. We need to account for this in our code.

/* set the individual row_pointers to point at the correct offsets */
    for (i = 0;  i < height;  ++i)
        row_pointers[height - 1 - i] = image_data + i*rowbytes;
}

That's it for the PNG file loader. Now, let's display this PNG texture using OpenGL.

Pages: 1, 2, 3

Next Pagearrow