The PNG Guide is an eBook based on Greg Roelofs' book, originally published by O'Reilly. |
![]() |
Home ![]() ![]() |
||
![]() ![]() ![]() ![]() ![]() ![]() |
||
readpng2_init()if (!(infile = fopen(filename, "rb"))) /* report an error and exit */ } else { incount = fread(inbuf, 1, INBUFSIZE, infile); if (incount < 8 || !readpng2_check_sig(inbuf, 8)) { /* report an error and exit */ } else { rc = readpng2_init(&rpng2_info); [etc.] } } Sharp-eyed readers will have noticed that I call readpng2_init() with a different argument than last time: int readpng2_init(mainprog_info *mainprog_ptr) typedef struct _mainprog_info { double display_exponent; ulg width; ulg height; void *png_ptr; void *info_ptr; void (*mainprog_init)(void); void (*mainprog_display_row)(ulg row_num); void (*mainprog_finish_display)(void); uch *image_data; uch **row_pointers; jmp_buf jmpbuf; int passes; int rowbytes; int channels; int need_bgcolor; int done; uch bg_red; uch bg_green; uch bg_blue; } mainprog_info; I'll explain each member as we need it, but it is clear that many of the variables that were formerly global or passed as arguments to functions now reside in this struct. Note that similar variable types have been grouped, with the smallest ones at the end, so that the larger types will be aligned on even memory boundaries by default, minimizing the amount of padding the compiler has to add to the structure. readpng2_init() begins by calling libpng to allocate the two PNG structs: png_structp png_ptr; png_infop info_ptr; png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, mainprog_ptr, readpng2_error_handler, NULL); if (!png_ptr) return 4; /* out of memory */ info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { png_destroy_read_struct(&png_ptr, NULL, NULL); return 4; /* out of memory */ } I have used a pair of local variables here, png_ptr and info_ptr, for convenience. The mainprog_info struct also includes these variables, but because it's used in the main program, which has no knowledge of libpng datatypes, the struct versions of the two variables are simply declared as pointers to void. To use them directly in readpng2_init(), we would need to typecast them repeatedly, which is annoying and makes the program harder to read and somewhat slower. So I spent a few bytes on the temporary (local) variables to make life easier.
The next step is to set up one of those setjmp() calls. This
differs from the previous version only in that now we're using our own
struct's jmpbuf member instead of the one in the main PNG
struct:
if (setjmp(mainprog_ptr->jmpbuf)) { png_destroy_read_struct(&png_ptr, &info_ptr, NULL); return 2; } The second big difference from the basic PNG reader is what comes next:
png_set_progressive_read_fn(png_ptr, mainprog_ptr, readpng2_info_callback, readpng2_row_callback, readpng2_end_callback); Here we get a glimpse of the inversion of the program logic. The original approach was to call libpng and wait for it to return the requested image data, whether header information or actual pixels. That doesn't really work in a progressive program--if you give the library a hunk of data and wait for it to return, you may end up with nothing if the hunk was too small, or you may get the entire image back. More commonly, it is impossible to return a completely sensible result, due to the way compression works. The end of a buffer of compressed data may correspond to the first two bits of the red sample of a single pixel, for example, or it may cut off a piece of a compressed token that is therefore meaningless. Either way, what we really want is a way for the decoding library to provide us with data in a more controlled manner. Callbacks are the answer. The progressive handler in libpng is set up to work with three callback
functions: one to be called when all of the header information has been
read (i.e., everything prior to the first IDAT), one for when each row of
the image is decoded (which includes ``short'' rows if the image is
interlaced), and one for when the complete PNG stream has been read. These
are the last three arguments to png_set_progressive_read_fn(), and
As it turns out, the call to png_set_progressive_read_fn() is essentially the whole point of our readpng2 initialization routine. The only remaining detail is to save the two temporary pointers into the mainprog_info struct before returning to the main program: mainprog_ptr->png_ptr = png_ptr; mainprog_ptr->info_ptr = info_ptr; return 0; These pointers will be used in the readpng2 decoding routine that calls libpng,
which in turn sends the pointers back to the callback functions.
|
||
Home ![]() ![]() |