readpng_get_bgcolor()
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.
|