The Lab Book Pages

An online collection of electronics information

http://www.labbookpages.co.uk

Dr. Andrew Greensted
Last modified: 27th June 2013

Hide Menu


Valid XHTML Valid CSS
Valid RSS VIM Powered
RSS Feed Icon

This site uses Google Analytics to track visits. Privacy Statement

Page Icon

Creating PNGs with libPNG

The libPNG library is a C library for creating Portable Network Graphics (PNG) image files. This page provides a simple example of using the libPNG library.


Writing PNG files

An example programme that uses libPNG is linked to below. There are two main stages. The first is generating an image that is stored in memory. In this case a simple fractal is generated. The image is held in a 1D float array of length width x height. The second stage is then to write the image to an actual file.

makePNG.c

> gcc -lm -lpng -o makePNG makePNG.c
> ./makePNG output.png

If you're using Ubuntu, you'll probably need to install libpng-dev. You might also need to try a different compile command:

> sudo apt-get install libpng-dev
> gcc -o makePNG makePNG.c -lm -lpng 
> ./makePNG output.png

The part of the programme that performs the actual writing of the PNG file is described below.

This is just the declaration of a number of variables. Note that the png.h header file defines a set of pointer types; png_ptr, info_ptr, row are actually pointers.

int writeImage(char* filename, int width, int height, float *buffer, char* title)
{
   int code = 0;
   FILE *fp;
   png_structp png_ptr;
   png_infop info_ptr;
   png_bytep row;

Now we open the file that the image will be written to. There's also a check to make sure the file opened was successful.

   // Open file for writing (binary mode)
   fp = fopen(filename, "wb");
   if (fp == NULL) {
      fprintf(stderr, "Could not open file %s for writing\n", filename);
      code = 1;
      goto finalise;
   }

Two libPNG structures are allocated and initialised.

  • The write structure contains information about how the PNG file will be written (or read).
  • The info structure contains information about the PNG image that will be written into the actual file. This allow programmes to find out characteristics of the image.
   // Initialize write structure
   png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
   if (png_ptr == NULL) {
      fprintf(stderr, "Could not allocate write struct\n");
      code = 1;
      goto finalise;
   }

   // Initialize info structure
   info_ptr = png_create_info_struct(png_ptr);
   if (info_ptr == NULL) {
      fprintf(stderr, "Could not allocate info struct\n");
      code = 1;
      goto finalise;
   }

This is a form of exception handling for C. Basically, after this little block of code, if any libPNG function fails, execution will jump back to the setjmp function with a non-zero value. The if statement is then entered.

Within this example code, the jump point is only set once at this point. Therefore, if an 'exception' occurs, it is not possible to determine from which libPNG function it was thrown. However, it is possible to repeat this block before each libPNG function call, defining a new point to jump back to with an appropriate response.

   // Setup Exception handling
   if (setjmp(png_jmpbuf(png_ptr))) {
      fprintf(stderr, "Error during png creation\n");
      code = 1;
      goto finalise;
   }

Various meta data for the image is now set, such as the size and the colour depth per channel.

A further piece of meta information is also set, an image title. There are various other bits of standard text that can be set, such as an author.

   png_init_io(png_ptr, fp);

   // Write header (8 bit colour depth)
   png_set_IHDR(png_ptr, info_ptr, width, height,
         8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE,
         PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);

   // Set title
   if (title != NULL) {
      png_text title_text;
      title_text.compression = PNG_TEXT_COMPRESSION_NONE;
      title_text.key = "Title";
      title_text.text = title;
      png_set_text(png_ptr, info_ptr, &title_text, 1);
   }

   png_write_info(png_ptr, info_ptr);

Now the image data is written one row at a time. A single row buffer is created which is of the correct format. For each row, the floating-point image data is converted and written into the row buffer.

   // Allocate memory for one row (3 bytes per pixel - RGB)
   row = (png_bytep) malloc(3 * width * sizeof(png_byte));

   // Write image data
   int x, y;
   for (y=0 ; y<height ; y++) {
      for (x=0 ; x<width ; x++) {
         setRGB(&(row[x*3]), buffer[y*width + x]);
      }
      png_write_row(png_ptr, row);
   }

   // End write
   png_write_end(png_ptr, NULL);

The last stage is just some cleaning up. This point is jumped to if there has been an error.

   finalise:
   if (fp != NULL) fclose(fp);
   if (info_ptr != NULL) png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1);
   if (png_ptr != NULL) png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
   if (row != NULL) free(row);

   return code;
}

After executing the programme, the result is a PNG file that should look like the one below.

libPNG Output Image

Page Revisions

Rev NumberDateDetails
1.127/06/2013Corrected fractal code in example. Many thanks to Jan-Oliver Frohlich for pointing the mistakes out.
Book Logo