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


Back in the main program, after dealing with various windowing-system chores, the code sets a few variables in the mainprog_info struct. The following excerpt is from the X version of the code, but the Windows code is the same, aside from prefixing function names with rpng2_win_ instead of rpng2_x_:

    if (user_did_not_specify_a_background_color_or_pattern)
        rpng2_info.need_bgcolor = TRUE;

    rpng2_info.mainprog_init = rpng2_x_init;
    rpng2_info.mainprog_display_row = rpng2_x_display_row;
    rpng2_info.mainprog_finish_display = rpng2_x_finish_display;

Unlike the basic viewer, where the main program called a special function to check for and retrieve the image's background color, the progressive viewer simply sets the need_bgcolor flag in the struct. It also sets three function pointers corresponding to the three readpng2 callbacks. The reason for this apparent duplication will become clear when we look at the callbacks in detail.

Having prepared everything for decoding, the main program begins the data loop that is at its core, reading file data into a buffer and passing it to the PNG-decoding function:

    for (;;) {
        if (readpng2_decode_data(&rpng2_info, inbuf, incount))
        if (error || feof(infile) || rpng2_info.done)
        if (timing)
        incount = fread(inbuf, 1, INBUFSIZE, infile);

Note the call to readpng2_decode_data() at the beginning of the loop, before fread(); it handles the initial chunk of data we read prior to calling readpng2_init().

The only remarkable feature of the loop itself is the conditional call to the sleep() function. Because this is a demo program, and because it is intended to be a rough simulation of how a web browser functions, I chose to give the user the option of simulating how an image download over a fast modem would appear. The sleep() function is an extremely crude method of doing this--it has only one-second precision, which is too coarse to allow for a smooth simulation--but it is relatively portable and ubiquitous. Less portable but more precise alternatives include usleep() and various Windows API calls. But since no sane programmer would intentionally add a delay like this to the inner loop of a program except for demonstration purposes, I judged that sleep() was good enough for this. The combination of a one-second sleep interval and the default buffer size of 4096 bytes results in an apparent download speed that is 10% to 20% faster than a 33.6K modem can manage. In fact, it's close to the average connection speed of a 56K modem over typical phone lines.

As to readpng2_decode_data() itself, it is little more than a wrapper function for the libpng routine png_process_data(). Its arguments include a pointer to our mainprog_info struct, a pointer to the input buffer, and the number of bytes of input data; the only things it does besides calling libpng are copy the struct pointers and set up the usual error-handling code:

int readpng2_decode_data(mainprog_info *mainprog_ptr, uch *rawbuf,
                        ulg length)
    png_structp png_ptr = (png_structp)mainprog_ptr->png_ptr;
    png_infop info_ptr = (png_infop)mainprog_ptr->info_ptr;

    if (setjmp(mainprog_ptr->jmpbuf)) {
        png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
        mainprog_ptr->png_ptr = NULL;
        mainprog_ptr->info_ptr = NULL;
        return 2;

    png_process_data(png_ptr, info_ptr, rawbuf, length);

    return 0;

The struct pointers are copied merely because the alternative is to typedef them; the latter may be more efficient (though not necessarily, due to the extra level of indirection inherent in the -> operator), but it is also uglier and makes the code somewhat less readable.[101]

[101] Clarity and expediency, that's what we like. Well, we like efficiency, too, but not at the cost of clarity when writing a book on programming PNG.

Last Update: 2010-Nov-26