The PNG Guide is an eBook based on Greg Roelofs' book, originally published by O'Reilly.


In any case, assuming the user did not specify a background color, we call readpng_get_bgcolor() to check the PNG file for one. It takes as arguments pointers to three unsigned character values:

int readpng_get_bgcolor(uch *red, uch *green, uch *blue)

As before, we start with a setjmp() block to handle libpng errors, then check whether the PNG file had a bKGD chunk:

    if (!png_get_valid(png_ptr, info_ptr, PNG_INFO_bKGD))
        return 1;

Assuming the png_get_valid() call returned a nonzero value, we next have libpng give us a pointer to a small struct containing the bKGD color information:

    png_color_16p pBackground;

    png_get_bKGD(png_ptr, info_ptr, &pBackground);

(pBackground was defined at the top of the function.) pBackground now points at a png_color_16 struct, which is defined as follows:

typedef struct png_color_16_struct
    png_byte index;
    png_uint_16 red;
    png_uint_16 green;
    png_uint_16 blue;
    png_uint_16 gray;
} png_color_16;

As suggested by the struct members' names, not all of them are valid with all PNG image types. The first member, index, is only valid with palette-based images, for example, and gray is only valid with grayscale images. But it is one of libpng's handy little features (presently undocumented) that the red, green, and blue struct members are always valid, and those happen to be precisely the values we want.

The other thing to note, however, is that the elements we need are defined as png_uint_16, i.e., as 16-bit (or larger) unsigned integers. That suggests that the color values we get back may depend on the bit depth of the image, which is indeed the case. In fact, this is true regardless of whether the calling program requested libpng to convert 16-bit values or 1-, 2-, and 4-bit values to 8-bit; this is another currently undocumented tidbit. We'll be feeding all of these little gotchas back to the libpng maintainer, however, so one can assume that the documentation will be slightly more complete by the time this book is published.

Since we'll be dealing only with 8-bit samples in this program, and, in particular, since the arguments to readpng_get_bgcolor() are pointers to unsigned (8-bit) characters, we need to shift the high-order bits down in the case of 16-bit data or expand them in the case of low-bit-depth values (only possible with grayscale images). And either way, we need to pass the values back to the main program. Thus:

    if (bit_depth == 16) {
        *red   = pBackground->red   >> 8;
        *green = pBackground->green >> 8;
        *blue  = pBackground->blue  >> 8;
    } else if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) {
        if (bit_depth == 1)
            *red = *green = *blue = pBackground->gray? 255 : 0;
        else if (bit_depth == 2)   /* i.e., max value is 3 */
            *red = *green = *blue = (255/3) * pBackground->gray;
        else /* bit_depth == 4 */  /* i.e., max value is 15 */
            *red = *green = *blue = (255/15) * pBackground->gray;
    } else {
        *red   = pBackground->red;
        *green = pBackground->green;
        *blue  = pBackground->blue;

    return 0;

With that, the main program now has enough information to create an image window of the proper size and fill it with the background color, which it does. The top row of Figure C-5 in the color insert shows the two cases: the middle image is displayed with the background color specified in the PNG file itself, while the image on the right is shown with a user-specified background color.

The main program next calls the heart of the readpng code: readpng_get_image(), which sets the desired libpng transformations, allocates a PNG image buffer, decodes the image, and returns a pointer to the raw data. Before we look at that in detail, we should first discuss some of the design decisions that led to it.

Last Update: 2010-Nov-26