]> git.jsancho.org Git - lugaru.git/blob - Dependencies/libpng/contrib/gregbook/rpng2-win.c
CMake: Purge all the bundled dependencies
[lugaru.git] / Dependencies / libpng / contrib / gregbook / rpng2-win.c
1 /*---------------------------------------------------------------------------
2
3    rpng2 - progressive-model PNG display program                rpng2-win.c
4
5    This program decodes and displays PNG files progressively, as if it were
6    a web browser (though the front end is only set up to read from files).
7    It supports gamma correction, user-specified background colors, and user-
8    specified background patterns (for transparent images).  This version is
9    for 32-bit Windows; it may compile under 16-bit Windows with a little
10    tweaking (or maybe not).  Thanks to Adam Costello and Pieter S. van der
11    Meulen for the "diamond" and "radial waves" patterns, respectively.
12
13    to do (someday, maybe):
14     - handle quoted command-line args (especially filenames with spaces)
15     - finish resizable checkerboard-gradient (sizes 4-128?)
16     - use %.1023s to simplify truncation of title-bar string?
17     - have minimum window width:  oh well
18
19   ---------------------------------------------------------------------------
20
21    Changelog:
22     - 1.01:  initial public release
23     - 1.02:  fixed cut-and-paste error in usage screen (oops...)
24     - 1.03:  modified to allow abbreviated options
25     - 1.04:  removed bogus extra argument from usage fprintf() [Glenn R-P?];
26               fixed command-line parsing bug
27     - 1.10:  enabled "message window"/console (thanks to David Geldreich)
28     - 1.20:  added runtime MMX-enabling/disabling and new -mmx* options
29     - 1.21:  made minor tweak to usage screen to fit within 25-line console
30     - 1.22:  added AMD64/EM64T support (__x86_64__)
31     - 2.00:  dual-licensed (added GNU GPL)
32     - 2.01:  fixed 64-bit typo in readpng2.c
33     - 2.02:  fixed improper display of usage screen on PNG error(s); fixed
34               unexpected-EOF and file-read-error cases
35     - 2.03:  removed runtime MMX-enabling/disabling and obsolete -mmx* options
36
37   ---------------------------------------------------------------------------
38
39       Copyright (c) 1998-2008 Greg Roelofs.  All rights reserved.
40
41       This software is provided "as is," without warranty of any kind,
42       express or implied.  In no event shall the author or contributors
43       be held liable for any damages arising in any way from the use of
44       this software.
45
46       The contents of this file are DUAL-LICENSED.  You may modify and/or
47       redistribute this software according to the terms of one of the
48       following two licenses (at your option):
49
50
51       LICENSE 1 ("BSD-like with advertising clause"):
52
53       Permission is granted to anyone to use this software for any purpose,
54       including commercial applications, and to alter it and redistribute
55       it freely, subject to the following restrictions:
56
57       1. Redistributions of source code must retain the above copyright
58          notice, disclaimer, and this list of conditions.
59       2. Redistributions in binary form must reproduce the above copyright
60          notice, disclaimer, and this list of conditions in the documenta-
61          tion and/or other materials provided with the distribution.
62       3. All advertising materials mentioning features or use of this
63          software must display the following acknowledgment:
64
65             This product includes software developed by Greg Roelofs
66             and contributors for the book, "PNG: The Definitive Guide,"
67             published by O'Reilly and Associates.
68
69
70       LICENSE 2 (GNU GPL v2 or later):
71
72       This program is free software; you can redistribute it and/or modify
73       it under the terms of the GNU General Public License as published by
74       the Free Software Foundation; either version 2 of the License, or
75       (at your option) any later version.
76
77       This program is distributed in the hope that it will be useful,
78       but WITHOUT ANY WARRANTY; without even the implied warranty of
79       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
80       GNU General Public License for more details.
81
82       You should have received a copy of the GNU General Public License
83       along with this program; if not, write to the Free Software Foundation,
84       Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
85
86   ---------------------------------------------------------------------------*/
87
88 #define PROGNAME  "rpng2-win"
89 #define LONGNAME  "Progressive PNG Viewer for Windows"
90 #define VERSION   "2.02 of 16 March 2008"
91
92 #include <stdio.h>
93 #include <stdlib.h>
94 #include <string.h>
95 #include <setjmp.h>    /* for jmpbuf declaration in readpng2.h */
96 #include <time.h>
97 #include <math.h>      /* only for PvdM background code */
98 #include <windows.h>
99 #include <conio.h>     /* only for _getch() */
100
101 /* all for PvdM background code: */
102 #ifndef PI
103 #  define PI             3.141592653589793238
104 #endif
105 #define PI_2             (PI*0.5)
106 #define INV_PI_360       (360.0 / PI)
107 #define MAX(a,b)         (a>b?a:b)
108 #define MIN(a,b)         (a<b?a:b)
109 #define CLIP(a,min,max)  MAX(min,MIN((a),max))
110 #define ABS(a)           ((a)<0?-(a):(a))
111 #define CLIP8P(c)        MAX(0,(MIN((c),255)))   /* 8-bit pos. integer (uch) */
112 #define ROUNDF(f)        ((int)(f + 0.5))
113
114 #define rgb1_max   bg_freq
115 #define rgb1_min   bg_gray
116 #define rgb2_max   bg_bsat
117 #define rgb2_min   bg_brot
118
119 /* #define DEBUG */     /* this enables the Trace() macros */
120
121 #include "readpng2.h"   /* typedefs, common macros, readpng2 prototypes */
122
123
124 /* could just include png.h, but this macro is the only thing we need
125  * (name and typedefs changed to local versions); note that side effects
126  * only happen with alpha (which could easily be avoided with
127  * "ush acopy = (alpha);") */
128
129 #define alpha_composite(composite, fg, alpha, bg) {               \
130     ush temp = ((ush)(fg)*(ush)(alpha) +                          \
131                 (ush)(bg)*(ush)(255 - (ush)(alpha)) + (ush)128);  \
132     (composite) = (uch)((temp + (temp >> 8)) >> 8);               \
133 }
134
135
136 #define INBUFSIZE 4096   /* with pseudo-timing on (1 sec delay/block), this
137                           *  block size corresponds roughly to a download
138                           *  speed 10% faster than theoretical 33.6K maximum
139                           *  (assuming 8 data bits, 1 stop bit and no other
140                           *  overhead) */
141
142 /* local prototypes */
143 static void       rpng2_win_init(void);
144 static int        rpng2_win_create_window(void);
145 static int        rpng2_win_load_bg_image(void);
146 static void       rpng2_win_display_row(ulg row);
147 static void       rpng2_win_finish_display(void);
148 static void       rpng2_win_cleanup(void);
149 LRESULT CALLBACK  rpng2_win_wndproc(HWND, UINT, WPARAM, LPARAM);
150
151
152 static char titlebar[1024];
153 static char *progname = PROGNAME;
154 static char *appname = LONGNAME;
155 static char *filename;
156 static FILE *infile;
157
158 static mainprog_info rpng2_info;
159
160 static uch inbuf[INBUFSIZE];
161 static int incount;
162
163 static int pat = 6;         /* must be less than num_bgpat */
164 static int bg_image = 0;
165 static int bgscale = 16;
166 static ulg bg_rowbytes;
167 static uch *bg_data;
168
169 static struct rgb_color {
170     uch r, g, b;
171 } rgb[] = {
172     {  0,   0,   0},    /*  0:  black */
173     {255, 255, 255},    /*  1:  white */
174     {173, 132,  57},    /*  2:  tan */
175     { 64, 132,   0},    /*  3:  medium green */
176     {189, 117,   1},    /*  4:  gold */
177     {253, 249,   1},    /*  5:  yellow */
178     {  0,   0, 255},    /*  6:  blue */
179     {  0,   0, 120},    /*  7:  medium blue */
180     {255,   0, 255},    /*  8:  magenta */
181     { 64,   0,  64},    /*  9:  dark magenta */
182     {255,   0,   0},    /* 10:  red */
183     { 64,   0,   0},    /* 11:  dark red */
184     {255, 127,   0},    /* 12:  orange */
185     {192,  96,   0},    /* 13:  darker orange */
186     { 24,  60,   0},    /* 14:  dark green-yellow */
187     { 85, 125, 200}     /* 15:  ice blue */
188 };
189 /* not used for now, but should be for error-checking:
190 static int num_rgb = sizeof(rgb) / sizeof(struct rgb_color);
191  */
192
193 /*
194     This whole struct is a fairly cheesy way to keep the number of
195     command-line options to a minimum.  The radial-waves background
196     type is a particularly poor fit to the integer elements of the
197     struct...but a few macros and a little fixed-point math will do
198     wonders for ya.
199
200     type bits:
201        F E D C B A 9 8 7 6 5 4 3 2 1 0
202                              | | | | |
203                              | | +-+-+-- 0 = sharp-edged checkerboard
204                              | |         1 = soft diamonds
205                              | |         2 = radial waves
206                              | |       3-7 = undefined
207                              | +-- gradient #2 inverted?
208                              +-- alternating columns inverted?
209  */
210 static struct background_pattern {
211     ush type;
212     int rgb1_max, rgb1_min;     /* or bg_freq, bg_gray */
213     int rgb2_max, rgb2_min;     /* or bg_bsat, bg_brot (both scaled by 10)*/
214 } bg[] = {
215     {0+8,   2,0,  1,15},        /* checkered:  tan/black vs. white/ice blue */
216     {0+24,  2,0,  1,0},         /* checkered:  tan/black vs. white/black */
217     {0+8,   4,5,  0,2},         /* checkered:  gold/yellow vs. black/tan */
218     {0+8,   4,5,  0,6},         /* checkered:  gold/yellow vs. black/blue */
219     {0,     7,0,  8,9},         /* checkered:  deep blue/black vs. magenta */
220     {0+8,  13,0,  5,14},        /* checkered:  orange/black vs. yellow */
221     {0+8,  12,0, 10,11},        /* checkered:  orange/black vs. red */
222     {1,     7,0,  8,0},         /* diamonds:  deep blue/black vs. magenta */
223     {1,    12,0, 11,0},         /* diamonds:  orange vs. dark red */
224     {1,    10,0,  7,0},         /* diamonds:  red vs. medium blue */
225     {1,     4,0,  5,0},         /* diamonds:  gold vs. yellow */
226     {1,     3,0,  0,0},         /* diamonds:  medium green vs. black */
227     {2,    16, 100,  20,   0},  /* radial:  ~hard radial color-beams */
228     {2,    18, 100,  10,   2},  /* radial:  soft, curved radial color-beams */
229     {2,    16, 256, 100, 250},  /* radial:  very tight spiral */
230     {2, 10000, 256,  11,   0}   /* radial:  dipole-moire' (almost fractal) */
231 };
232 static int num_bgpat = sizeof(bg) / sizeof(struct background_pattern);
233
234
235 /* Windows-specific global variables (could go in struct, but messy...) */
236 static ulg wimage_rowbytes;
237 static uch *dib;
238 static uch *wimage_data;
239 static BITMAPINFOHEADER *bmih;
240
241 static HWND global_hwnd;
242 static HINSTANCE global_hInst;
243 static int global_showmode;
244
245
246
247
248 int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, PSTR cmd, int showmode)
249 {
250     char *args[1024];                 /* arbitrary limit, but should suffice */
251     char **argv = args;
252     char *p, *q, *bgstr = NULL;
253     int argc = 0;
254     int rc, alen, flen;
255     int error = 0;
256     int timing = FALSE;
257     int have_bg = FALSE;
258     double LUT_exponent;              /* just the lookup table */
259     double CRT_exponent = 2.2;        /* just the monitor */
260     double default_display_exponent;  /* whole display system */
261     MSG msg;
262
263
264     /* First initialize a few things, just to be sure--memset takes care of
265      * default background color (black), booleans (FALSE), pointers (NULL),
266      * etc. */
267
268     global_hInst = hInst;
269     global_showmode = showmode;
270     filename = (char *)NULL;
271     memset(&rpng2_info, 0, sizeof(mainprog_info));
272
273
274     /* Next reenable console output, which normally goes to the bit bucket
275      * for windowed apps.  Closing the console window will terminate the
276      * app.  Thanks to David.Geldreich@realviz.com for supplying the magical
277      * incantation. */
278
279     AllocConsole();
280     freopen("CONOUT$", "a", stderr);
281     freopen("CONOUT$", "a", stdout);
282
283
284     /* Set the default value for our display-system exponent, i.e., the
285      * product of the CRT exponent and the exponent corresponding to
286      * the frame-buffer's lookup table (LUT), if any.  This is not an
287      * exhaustive list of LUT values (e.g., OpenStep has a lot of weird
288      * ones), but it should cover 99% of the current possibilities.  And
289      * yes, these ifdefs are completely wasted in a Windows program... */
290
291 #if defined(NeXT)
292     /* third-party utilities can modify the default LUT exponent */
293     LUT_exponent = 1.0 / 2.2;
294     /*
295     if (some_next_function_that_returns_gamma(&next_gamma))
296         LUT_exponent = 1.0 / next_gamma;
297      */
298 #elif defined(sgi)
299     LUT_exponent = 1.0 / 1.7;
300     /* there doesn't seem to be any documented function to
301      * get the "gamma" value, so we do it the hard way */
302     infile = fopen("/etc/config/system.glGammaVal", "r");
303     if (infile) {
304         double sgi_gamma;
305
306         fgets(tmpline, 80, infile);
307         fclose(infile);
308         sgi_gamma = atof(tmpline);
309         if (sgi_gamma > 0.0)
310             LUT_exponent = 1.0 / sgi_gamma;
311     }
312 #elif defined(Macintosh)
313     LUT_exponent = 1.8 / 2.61;
314     /*
315     if (some_mac_function_that_returns_gamma(&mac_gamma))
316         LUT_exponent = mac_gamma / 2.61;
317      */
318 #else
319     LUT_exponent = 1.0;   /* assume no LUT:  most PCs */
320 #endif
321
322     /* the defaults above give 1.0, 1.3, 1.5 and 2.2, respectively: */
323     default_display_exponent = LUT_exponent * CRT_exponent;
324
325
326     /* If the user has set the SCREEN_GAMMA environment variable as suggested
327      * (somewhat imprecisely) in the libpng documentation, use that; otherwise
328      * use the default value we just calculated.  Either way, the user may
329      * override this via a command-line option. */
330
331     if ((p = getenv("SCREEN_GAMMA")) != NULL)
332         rpng2_info.display_exponent = atof(p);
333     else
334         rpng2_info.display_exponent = default_display_exponent;
335
336
337     /* Windows really hates command lines, so we have to set up our own argv.
338      * Note that we do NOT bother with quoted arguments here, so don't use
339      * filenames with spaces in 'em! */
340
341     argv[argc++] = PROGNAME;
342     p = cmd;
343     for (;;) {
344         if (*p == ' ')
345             while (*++p == ' ')
346                 ;
347         /* now p points at the first non-space after some spaces */
348         if (*p == '\0')
349             break;    /* nothing after the spaces:  done */
350         argv[argc++] = q = p;
351         while (*q && *q != ' ')
352             ++q;
353         /* now q points at a space or the end of the string */
354         if (*q == '\0')
355             break;    /* last argv already terminated; quit */
356         *q = '\0';    /* change space to terminator */
357         p = q + 1;
358     }
359     argv[argc] = NULL;   /* terminate the argv array itself */
360
361
362     /* Now parse the command line for options and the PNG filename. */
363
364     while (*++argv && !error) {
365         if (!strncmp(*argv, "-gamma", 2)) {
366             if (!*++argv)
367                 ++error;
368             else {
369                 rpng2_info.display_exponent = atof(*argv);
370                 if (rpng2_info.display_exponent <= 0.0)
371                     ++error;
372             }
373         } else if (!strncmp(*argv, "-bgcolor", 4)) {
374             if (!*++argv)
375                 ++error;
376             else {
377                 bgstr = *argv;
378                 if (strlen(bgstr) != 7 || bgstr[0] != '#')
379                     ++error;
380                 else {
381                     have_bg = TRUE;
382                     bg_image = FALSE;
383                 }
384             }
385         } else if (!strncmp(*argv, "-bgpat", 4)) {
386             if (!*++argv)
387                 ++error;
388             else {
389                 pat = atoi(*argv) - 1;
390                 if (pat < 0 || pat >= num_bgpat)
391                     ++error;
392                 else {
393                     bg_image = TRUE;
394                     have_bg = FALSE;
395                 }
396             }
397         } else if (!strncmp(*argv, "-timing", 2)) {
398             timing = TRUE;
399         } else {
400             if (**argv != '-') {
401                 filename = *argv;
402                 if (argv[1])   /* shouldn't be any more args after filename */
403                     ++error;
404             } else
405                 ++error;   /* not expecting any other options */
406         }
407     }
408
409     if (!filename)
410         ++error;
411
412
413     /* print usage screen if any errors up to this point */
414
415     if (error) {
416         int ch;
417
418         fprintf(stderr, "\n%s %s:  %s\n\n", PROGNAME, VERSION, appname);
419         readpng2_version_info();
420         fprintf(stderr, "\n"
421           "Usage:  %s [-gamma exp] [-bgcolor bg | -bgpat pat] [-timing]\n"
422           "        %*s file.png\n\n"
423           "    exp \ttransfer-function exponent (``gamma'') of the display\n"
424           "\t\t  system in floating-point format (e.g., ``%.1f''); equal\n"
425           "\t\t  to the product of the lookup-table exponent (varies)\n"
426           "\t\t  and the CRT exponent (usually 2.2); must be positive\n"
427           "    bg  \tdesired background color in 7-character hex RGB format\n"
428           "\t\t  (e.g., ``#ff7700'' for orange:  same as HTML colors);\n"
429           "\t\t  used with transparent images; overrides -bgpat option\n"
430           "    pat \tdesired background pattern number (1-%d); used with\n"
431           "\t\t  transparent images; overrides -bgcolor option\n"
432           "    -timing\tenables delay for every block read, to simulate modem\n"
433           "\t\t  download of image (~36 Kbps)\n"
434           "\nPress Q, Esc or mouse button 1 after image is displayed to quit.\n"
435           "Press Q or Esc to quit this usage screen. ",
436           PROGNAME,
437 #if (defined(__i386__) || defined(_M_IX86) || defined(__x86_64__))
438           (int)strlen(PROGNAME), " ",
439 #endif
440           (int)strlen(PROGNAME), " ", default_display_exponent, num_bgpat);
441         fflush(stderr);
442         do
443             ch = _getch();
444         while (ch != 'q' && ch != 'Q' && ch != 0x1B);
445         exit(1);
446     }
447
448
449     if (!(infile = fopen(filename, "rb"))) {
450         fprintf(stderr, PROGNAME ":  can't open PNG file [%s]\n", filename);
451         ++error;
452     } else {
453         incount = fread(inbuf, 1, INBUFSIZE, infile);
454         if (incount < 8 || !readpng2_check_sig(inbuf, 8)) {
455             fprintf(stderr, PROGNAME
456               ":  [%s] is not a PNG file: incorrect signature\n",
457               filename);
458             ++error;
459         } else if ((rc = readpng2_init(&rpng2_info)) != 0) {
460             switch (rc) {
461                 case 2:
462                     fprintf(stderr, PROGNAME
463                       ":  [%s] has bad IHDR (libpng longjmp)\n", filename);
464                     break;
465                 case 4:
466                     fprintf(stderr, PROGNAME ":  insufficient memory\n");
467                     break;
468                 default:
469                     fprintf(stderr, PROGNAME
470                       ":  unknown readpng2_init() error\n");
471                     break;
472             }
473             ++error;
474         }
475         if (error)
476             fclose(infile);
477     }
478
479
480     if (error) {
481         int ch;
482
483         fprintf(stderr, PROGNAME ":  aborting.\n");
484         do
485             ch = _getch();
486         while (ch != 'q' && ch != 'Q' && ch != 0x1B);
487         exit(2);
488     } else {
489         fprintf(stderr, "\n%s %s:  %s\n", PROGNAME, VERSION, appname);
490         fprintf(stderr,
491           "\n   [console window:  closing this window will terminate %s]\n\n",
492           PROGNAME);
493         fflush(stderr);
494     }
495
496
497     /* set the title-bar string, but make sure buffer doesn't overflow */
498
499     alen = strlen(appname);
500     flen = strlen(filename);
501     if (alen + flen + 3 > 1023)
502         sprintf(titlebar, "%s:  ...%s", appname, filename+(alen+flen+6-1023));
503     else
504         sprintf(titlebar, "%s:  %s", appname, filename);
505
506
507     /* set some final rpng2_info variables before entering main data loop */
508
509     if (have_bg) {
510         unsigned r, g, b;   /* this approach quiets compiler warnings */
511
512         sscanf(bgstr+1, "%2x%2x%2x", &r, &g, &b);
513         rpng2_info.bg_red   = (uch)r;
514         rpng2_info.bg_green = (uch)g;
515         rpng2_info.bg_blue  = (uch)b;
516     } else
517         rpng2_info.need_bgcolor = TRUE;
518
519     rpng2_info.state = kPreInit;
520     rpng2_info.mainprog_init = rpng2_win_init;
521     rpng2_info.mainprog_display_row = rpng2_win_display_row;
522     rpng2_info.mainprog_finish_display = rpng2_win_finish_display;
523
524
525     /* OK, this is the fun part:  call readpng2_decode_data() at the start of
526      * the loop to deal with our first buffer of data (read in above to verify
527      * that the file is a PNG image), then loop through the file and continue
528      * calling the same routine to handle each chunk of data.  It in turn
529      * passes the data to libpng, which will invoke one or more of our call-
530      * backs as decoded data become available.  We optionally call Sleep() for
531      * one second per iteration to simulate downloading the image via an analog
532      * modem. */
533
534     for (;;) {
535         Trace((stderr, "about to call readpng2_decode_data()\n"))
536         if (readpng2_decode_data(&rpng2_info, inbuf, incount))
537             ++error;
538         Trace((stderr, "done with readpng2_decode_data()\n"))
539
540         if (error || incount != INBUFSIZE || rpng2_info.state == kDone) {
541             if (rpng2_info.state == kDone) {
542                 Trace((stderr, "done decoding PNG image\n"))
543             } else if (ferror(infile)) {
544                 fprintf(stderr, PROGNAME
545                   ":  error while reading PNG image file\n");
546                 exit(3);
547             } else if (feof(infile)) {
548                 fprintf(stderr, PROGNAME ":  end of file reached "
549                   "(unexpectedly) while reading PNG image file\n");
550                 exit(3);
551             } else /* if (error) */ {
552                 // will print error message below
553             }
554             break;
555         }
556
557         if (timing)
558             Sleep(1000L);
559
560         incount = fread(inbuf, 1, INBUFSIZE, infile);
561     }
562
563
564     /* clean up PNG stuff and report any decoding errors */
565
566     fclose(infile);
567     Trace((stderr, "about to call readpng2_cleanup()\n"))
568     readpng2_cleanup(&rpng2_info);
569
570     if (error) {
571         fprintf(stderr, PROGNAME ":  libpng error while decoding PNG image\n");
572         exit(3);
573     }
574
575
576     /* wait for the user to tell us when to quit */
577
578     while (GetMessage(&msg, NULL, 0, 0)) {
579         TranslateMessage(&msg);
580         DispatchMessage(&msg);
581     }
582
583
584     /* we're done:  clean up all image and Windows resources and go away */
585
586     Trace((stderr, "about to call rpng2_win_cleanup()\n"))
587     rpng2_win_cleanup();
588
589     return msg.wParam;
590 }
591
592
593
594
595
596 /* this function is called by readpng2_info_callback() in readpng2.c, which
597  * in turn is called by libpng after all of the pre-IDAT chunks have been
598  * read and processed--i.e., we now have enough info to finish initializing */
599
600 static void rpng2_win_init()
601 {
602     ulg i;
603     ulg rowbytes = rpng2_info.rowbytes;
604
605     Trace((stderr, "beginning rpng2_win_init()\n"))
606     Trace((stderr, "  rowbytes = %d\n", rpng2_info.rowbytes))
607     Trace((stderr, "  width  = %ld\n", rpng2_info.width))
608     Trace((stderr, "  height = %ld\n", rpng2_info.height))
609
610     rpng2_info.image_data = (uch *)malloc(rowbytes * rpng2_info.height);
611     if (!rpng2_info.image_data) {
612         readpng2_cleanup(&rpng2_info);
613         return;
614     }
615
616     rpng2_info.row_pointers = (uch **)malloc(rpng2_info.height * sizeof(uch *));
617     if (!rpng2_info.row_pointers) {
618         free(rpng2_info.image_data);
619         rpng2_info.image_data = NULL;
620         readpng2_cleanup(&rpng2_info);
621         return;
622     }
623
624     for (i = 0;  i < rpng2_info.height;  ++i)
625         rpng2_info.row_pointers[i] = rpng2_info.image_data + i*rowbytes;
626
627 /*---------------------------------------------------------------------------
628     Do the basic Windows initialization stuff, make the window, and fill it
629     with the user-specified, file-specified or default background color.
630   ---------------------------------------------------------------------------*/
631
632     if (rpng2_win_create_window()) {
633         readpng2_cleanup(&rpng2_info);
634         return;
635     }
636
637     rpng2_info.state = kWindowInit;
638 }
639
640
641
642
643
644 static int rpng2_win_create_window()
645 {
646     uch bg_red   = rpng2_info.bg_red;
647     uch bg_green = rpng2_info.bg_green;
648     uch bg_blue  = rpng2_info.bg_blue;
649     uch *dest;
650     int extra_width, extra_height;
651     ulg i, j;
652     WNDCLASSEX wndclass;
653     RECT rect;
654
655
656 /*---------------------------------------------------------------------------
657     Allocate memory for the display-specific version of the image (round up
658     to multiple of 4 for Windows DIB).
659   ---------------------------------------------------------------------------*/
660
661     wimage_rowbytes = ((3*rpng2_info.width + 3L) >> 2) << 2;
662
663     if (!(dib = (uch *)malloc(sizeof(BITMAPINFOHEADER) +
664                               wimage_rowbytes*rpng2_info.height)))
665     {
666         return 4;   /* fail */
667     }
668
669 /*---------------------------------------------------------------------------
670     Initialize the DIB.  Negative height means to use top-down BMP ordering
671     (must be uncompressed, but that's what we want).  Bit count of 1, 4 or 8
672     implies a colormap of RGBX quads, but 24-bit BMPs just use B,G,R values
673     directly => wimage_data begins immediately after BMP header.
674   ---------------------------------------------------------------------------*/
675
676     memset(dib, 0, sizeof(BITMAPINFOHEADER));
677     bmih = (BITMAPINFOHEADER *)dib;
678     bmih->biSize = sizeof(BITMAPINFOHEADER);
679     bmih->biWidth = rpng2_info.width;
680     bmih->biHeight = -((long)rpng2_info.height);
681     bmih->biPlanes = 1;
682     bmih->biBitCount = 24;
683     bmih->biCompression = 0;
684     wimage_data = dib + sizeof(BITMAPINFOHEADER);
685
686 /*---------------------------------------------------------------------------
687     Fill window with the specified background color (default is black), but
688     defer loading faked "background image" until window is displayed (may be
689     slow to compute).  Data are in BGR order.
690   ---------------------------------------------------------------------------*/
691
692     if (bg_image) {   /* just fill with black for now */
693         memset(wimage_data, 0, wimage_rowbytes*rpng2_info.height);
694     } else {
695         for (j = 0;  j < rpng2_info.height;  ++j) {
696             dest = wimage_data + j*wimage_rowbytes;
697             for (i = rpng2_info.width;  i > 0;  --i) {
698                 *dest++ = bg_blue;
699                 *dest++ = bg_green;
700                 *dest++ = bg_red;
701             }
702         }
703     }
704
705 /*---------------------------------------------------------------------------
706     Set the window parameters.
707   ---------------------------------------------------------------------------*/
708
709     memset(&wndclass, 0, sizeof(wndclass));
710
711     wndclass.cbSize = sizeof(wndclass);
712     wndclass.style = CS_HREDRAW | CS_VREDRAW;
713     wndclass.lpfnWndProc = rpng2_win_wndproc;
714     wndclass.hInstance = global_hInst;
715     wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
716     wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
717     wndclass.hbrBackground = (HBRUSH)GetStockObject(DKGRAY_BRUSH);
718     wndclass.lpszMenuName = NULL;
719     wndclass.lpszClassName = progname;
720     wndclass.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
721
722     RegisterClassEx(&wndclass);
723
724 /*---------------------------------------------------------------------------
725     Finally, create the window.
726   ---------------------------------------------------------------------------*/
727
728     extra_width  = 2*(GetSystemMetrics(SM_CXBORDER) +
729                       GetSystemMetrics(SM_CXDLGFRAME));
730     extra_height = 2*(GetSystemMetrics(SM_CYBORDER) +
731                       GetSystemMetrics(SM_CYDLGFRAME)) +
732                       GetSystemMetrics(SM_CYCAPTION);
733
734     global_hwnd = CreateWindow(progname, titlebar, WS_OVERLAPPEDWINDOW,
735       CW_USEDEFAULT, CW_USEDEFAULT, rpng2_info.width+extra_width,
736       rpng2_info.height+extra_height, NULL, NULL, global_hInst, NULL);
737
738     ShowWindow(global_hwnd, global_showmode);
739     UpdateWindow(global_hwnd);
740
741 /*---------------------------------------------------------------------------
742     Now compute the background image and display it.  If it fails (memory
743     allocation), revert to a plain background color.
744   ---------------------------------------------------------------------------*/
745
746     if (bg_image) {
747         static const char *msg = "Computing background image...";
748         int x, y, len = strlen(msg);
749         HDC hdc = GetDC(global_hwnd);
750         TEXTMETRIC tm;
751
752         GetTextMetrics(hdc, &tm);
753         x = (rpng2_info.width - len*tm.tmAveCharWidth)/2;
754         y = (rpng2_info.height - tm.tmHeight)/2;
755         SetBkMode(hdc, TRANSPARENT);
756         SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
757         /* this can still begin out of bounds even if x is positive (???): */
758         TextOut(hdc, ((x < 0)? 0 : x), ((y < 0)? 0 : y), msg, len);
759         ReleaseDC(global_hwnd, hdc);
760
761         rpng2_win_load_bg_image();   /* resets bg_image if fails */
762     }
763
764     if (!bg_image) {
765         for (j = 0;  j < rpng2_info.height;  ++j) {
766             dest = wimage_data + j*wimage_rowbytes;
767             for (i = rpng2_info.width;  i > 0;  --i) {
768                 *dest++ = bg_blue;
769                 *dest++ = bg_green;
770                 *dest++ = bg_red;
771             }
772         }
773     }
774
775     rect.left = 0L;
776     rect.top = 0L;
777     rect.right = (LONG)rpng2_info.width;       /* possibly off by one? */
778     rect.bottom = (LONG)rpng2_info.height;     /* possibly off by one? */
779     InvalidateRect(global_hwnd, &rect, FALSE);
780     UpdateWindow(global_hwnd);                 /* similar to XFlush() */
781
782     return 0;
783
784 } /* end function rpng2_win_create_window() */
785
786
787
788
789
790 static int rpng2_win_load_bg_image()
791 {
792     uch *src, *dest;
793     uch r1, r2, g1, g2, b1, b2;
794     uch r1_inv, r2_inv, g1_inv, g2_inv, b1_inv, b2_inv;
795     int k, hmax, max;
796     int xidx, yidx, yidx_max = (bgscale-1);
797     int even_odd_vert, even_odd_horiz, even_odd;
798     int invert_gradient2 = (bg[pat].type & 0x08);
799     int invert_column;
800     ulg i, row;
801
802 /*---------------------------------------------------------------------------
803     Allocate buffer for fake background image to be used with transparent
804     images; if this fails, revert to plain background color.
805   ---------------------------------------------------------------------------*/
806
807     bg_rowbytes = 3 * rpng2_info.width;
808     bg_data = (uch *)malloc(bg_rowbytes * rpng2_info.height);
809     if (!bg_data) {
810         fprintf(stderr, PROGNAME
811           ":  unable to allocate memory for background image\n");
812         bg_image = 0;
813         return 1;
814     }
815
816 /*---------------------------------------------------------------------------
817     Vertical gradients (ramps) in NxN squares, alternating direction and
818     colors (N == bgscale).
819   ---------------------------------------------------------------------------*/
820
821     if ((bg[pat].type & 0x07) == 0) {
822         uch r1_min  = rgb[bg[pat].rgb1_min].r;
823         uch g1_min  = rgb[bg[pat].rgb1_min].g;
824         uch b1_min  = rgb[bg[pat].rgb1_min].b;
825         uch r2_min  = rgb[bg[pat].rgb2_min].r;
826         uch g2_min  = rgb[bg[pat].rgb2_min].g;
827         uch b2_min  = rgb[bg[pat].rgb2_min].b;
828         int r1_diff = rgb[bg[pat].rgb1_max].r - r1_min;
829         int g1_diff = rgb[bg[pat].rgb1_max].g - g1_min;
830         int b1_diff = rgb[bg[pat].rgb1_max].b - b1_min;
831         int r2_diff = rgb[bg[pat].rgb2_max].r - r2_min;
832         int g2_diff = rgb[bg[pat].rgb2_max].g - g2_min;
833         int b2_diff = rgb[bg[pat].rgb2_max].b - b2_min;
834
835         for (row = 0;  row < rpng2_info.height;  ++row) {
836             yidx = row % bgscale;
837             even_odd_vert = (row / bgscale) & 1;
838
839             r1 = r1_min + (r1_diff * yidx) / yidx_max;
840             g1 = g1_min + (g1_diff * yidx) / yidx_max;
841             b1 = b1_min + (b1_diff * yidx) / yidx_max;
842             r1_inv = r1_min + (r1_diff * (yidx_max-yidx)) / yidx_max;
843             g1_inv = g1_min + (g1_diff * (yidx_max-yidx)) / yidx_max;
844             b1_inv = b1_min + (b1_diff * (yidx_max-yidx)) / yidx_max;
845
846             r2 = r2_min + (r2_diff * yidx) / yidx_max;
847             g2 = g2_min + (g2_diff * yidx) / yidx_max;
848             b2 = b2_min + (b2_diff * yidx) / yidx_max;
849             r2_inv = r2_min + (r2_diff * (yidx_max-yidx)) / yidx_max;
850             g2_inv = g2_min + (g2_diff * (yidx_max-yidx)) / yidx_max;
851             b2_inv = b2_min + (b2_diff * (yidx_max-yidx)) / yidx_max;
852
853             dest = bg_data + row*bg_rowbytes;
854             for (i = 0;  i < rpng2_info.width;  ++i) {
855                 even_odd_horiz = (i / bgscale) & 1;
856                 even_odd = even_odd_vert ^ even_odd_horiz;
857                 invert_column =
858                   (even_odd_horiz && (bg[pat].type & 0x10));
859                 if (even_odd == 0) {         /* gradient #1 */
860                     if (invert_column) {
861                         *dest++ = r1_inv;
862                         *dest++ = g1_inv;
863                         *dest++ = b1_inv;
864                     } else {
865                         *dest++ = r1;
866                         *dest++ = g1;
867                         *dest++ = b1;
868                     }
869                 } else {                     /* gradient #2 */
870                     if ((invert_column && invert_gradient2) ||
871                         (!invert_column && !invert_gradient2))
872                     {
873                         *dest++ = r2;        /* not inverted or */
874                         *dest++ = g2;        /*  doubly inverted */
875                         *dest++ = b2;
876                     } else {
877                         *dest++ = r2_inv;
878                         *dest++ = g2_inv;    /* singly inverted */
879                         *dest++ = b2_inv;
880                     }
881                 }
882             }
883         }
884
885 /*---------------------------------------------------------------------------
886     Soft gradient-diamonds with scale = bgscale.  Code contributed by Adam
887     M. Costello.
888   ---------------------------------------------------------------------------*/
889
890     } else if ((bg[pat].type & 0x07) == 1) {
891
892         hmax = (bgscale-1)/2;   /* half the max weight of a color */
893         max = 2*hmax;           /* the max weight of a color */
894
895         r1 = rgb[bg[pat].rgb1_max].r;
896         g1 = rgb[bg[pat].rgb1_max].g;
897         b1 = rgb[bg[pat].rgb1_max].b;
898         r2 = rgb[bg[pat].rgb2_max].r;
899         g2 = rgb[bg[pat].rgb2_max].g;
900         b2 = rgb[bg[pat].rgb2_max].b;
901
902         for (row = 0;  row < rpng2_info.height;  ++row) {
903             yidx = row % bgscale;
904             if (yidx > hmax)
905                 yidx = bgscale-1 - yidx;
906             dest = bg_data + row*bg_rowbytes;
907             for (i = 0;  i < rpng2_info.width;  ++i) {
908                 xidx = i % bgscale;
909                 if (xidx > hmax)
910                     xidx = bgscale-1 - xidx;
911                 k = xidx + yidx;
912                 *dest++ = (k*r1 + (max-k)*r2) / max;
913                 *dest++ = (k*g1 + (max-k)*g2) / max;
914                 *dest++ = (k*b1 + (max-k)*b2) / max;
915             }
916         }
917
918 /*---------------------------------------------------------------------------
919     Radial "starburst" with azimuthal sinusoids; [eventually number of sinu-
920     soids will equal bgscale?].  This one is slow but very cool.  Code con-
921     tributed by Pieter S. van der Meulen (originally in Smalltalk).
922   ---------------------------------------------------------------------------*/
923
924     } else if ((bg[pat].type & 0x07) == 2) {
925         uch ch;
926         int ii, x, y, hw, hh, grayspot;
927         double freq, rotate, saturate, gray, intensity;
928         double angle=0.0, aoffset=0.0, maxDist, dist;
929         double red=0.0, green=0.0, blue=0.0, hue, s, v, f, p, q, t;
930
931         fprintf(stderr, "%s:  computing radial background...",
932           PROGNAME);
933         fflush(stderr);
934
935         hh = rpng2_info.height / 2;
936         hw = rpng2_info.width / 2;
937
938         /* variables for radial waves:
939          *   aoffset:  number of degrees to rotate hue [CURRENTLY NOT USED]
940          *   freq:  number of color beams originating from the center
941          *   grayspot:  size of the graying center area (anti-alias)
942          *   rotate:  rotation of the beams as a function of radius
943          *   saturate:  saturation of beams' shape azimuthally
944          */
945         angle = CLIP(angle, 0.0, 360.0);
946         grayspot = CLIP(bg[pat].bg_gray, 1, (hh + hw));
947         freq = MAX((double)bg[pat].bg_freq, 0.0);
948         saturate = (double)bg[pat].bg_bsat * 0.1;
949         rotate = (double)bg[pat].bg_brot * 0.1;
950         gray = 0.0;
951         intensity = 0.0;
952         maxDist = (double)((hw*hw) + (hh*hh));
953
954         for (row = 0;  row < rpng2_info.height;  ++row) {
955             y = row - hh;
956             dest = bg_data + row*bg_rowbytes;
957             for (i = 0;  i < rpng2_info.width;  ++i) {
958                 x = i - hw;
959                 angle = (x == 0)? PI_2 : atan((double)y / (double)x);
960                 gray = (double)MAX(ABS(y), ABS(x)) / grayspot;
961                 gray = MIN(1.0, gray);
962                 dist = (double)((x*x) + (y*y)) / maxDist;
963                 intensity = cos((angle+(rotate*dist*PI)) * freq) *
964                   gray * saturate;
965                 intensity = (MAX(MIN(intensity,1.0),-1.0) + 1.0) * 0.5;
966                 hue = (angle + PI) * INV_PI_360 + aoffset;
967                 s = gray * ((double)(ABS(x)+ABS(y)) / (double)(hw + hh));
968                 s = MIN(MAX(s,0.0), 1.0);
969                 v = MIN(MAX(intensity,0.0), 1.0);
970
971                 if (s == 0.0) {
972                     ch = (uch)(v * 255.0);
973                     *dest++ = ch;
974                     *dest++ = ch;
975                     *dest++ = ch;
976                 } else {
977                     if ((hue < 0.0) || (hue >= 360.0))
978                         hue -= (((int)(hue / 360.0)) * 360.0);
979                     hue /= 60.0;
980                     ii = (int)hue;
981                     f = hue - (double)ii;
982                     p = (1.0 - s) * v;
983                     q = (1.0 - (s * f)) * v;
984                     t = (1.0 - (s * (1.0 - f))) * v;
985                     if      (ii == 0) { red = v; green = t; blue = p; }
986                     else if (ii == 1) { red = q; green = v; blue = p; }
987                     else if (ii == 2) { red = p; green = v; blue = t; }
988                     else if (ii == 3) { red = p; green = q; blue = v; }
989                     else if (ii == 4) { red = t; green = p; blue = v; }
990                     else if (ii == 5) { red = v; green = p; blue = q; }
991                     *dest++ = (uch)(red * 255.0);
992                     *dest++ = (uch)(green * 255.0);
993                     *dest++ = (uch)(blue * 255.0);
994                 }
995             }
996         }
997         fprintf(stderr, "done.\n");
998         fflush(stderr);
999     }
1000
1001 /*---------------------------------------------------------------------------
1002     Blast background image to display buffer before beginning PNG decode;
1003     calling function will handle invalidation and UpdateWindow() call.
1004   ---------------------------------------------------------------------------*/
1005
1006     for (row = 0;  row < rpng2_info.height;  ++row) {
1007         src = bg_data + row*bg_rowbytes;
1008         dest = wimage_data + row*wimage_rowbytes;
1009         for (i = rpng2_info.width;  i > 0;  --i) {
1010             r1 = *src++;
1011             g1 = *src++;
1012             b1 = *src++;
1013             *dest++ = b1;
1014             *dest++ = g1;   /* note reverse order */
1015             *dest++ = r1;
1016         }
1017     }
1018
1019     return 0;
1020
1021 } /* end function rpng2_win_load_bg_image() */
1022
1023
1024
1025
1026
1027 static void rpng2_win_display_row(ulg row)
1028 {
1029     uch bg_red   = rpng2_info.bg_red;
1030     uch bg_green = rpng2_info.bg_green;
1031     uch bg_blue  = rpng2_info.bg_blue;
1032     uch *src, *src2=NULL, *dest;
1033     uch r, g, b, a;
1034     ulg i;
1035     static int rows=0;
1036     static ulg firstrow;
1037
1038 /*---------------------------------------------------------------------------
1039     rows and firstrow simply track how many rows (and which ones) have not
1040     yet been displayed; alternatively, we could call InvalidateRect() for
1041     every row and not bother with the records-keeping.
1042   ---------------------------------------------------------------------------*/
1043
1044     Trace((stderr, "beginning rpng2_win_display_row()\n"))
1045
1046     if (rows == 0)
1047         firstrow = row;   /* first row not yet displayed */
1048
1049     ++rows;   /* count of rows received but not yet displayed */
1050
1051 /*---------------------------------------------------------------------------
1052     Aside from the use of the rpng2_info struct and the lack of an outer
1053     loop (over rows), this routine is identical to rpng_win_display_image()
1054     in the non-progressive version of the program.
1055   ---------------------------------------------------------------------------*/
1056
1057     src = rpng2_info.image_data + row*rpng2_info.rowbytes;
1058     if (bg_image)
1059         src2 = bg_data + row*bg_rowbytes;
1060     dest = wimage_data + row*wimage_rowbytes;
1061
1062     if (rpng2_info.channels == 3) {
1063         for (i = rpng2_info.width;  i > 0;  --i) {
1064             r = *src++;
1065             g = *src++;
1066             b = *src++;
1067             *dest++ = b;
1068             *dest++ = g;   /* note reverse order */
1069             *dest++ = r;
1070         }
1071     } else /* if (rpng2_info.channels == 4) */ {
1072         for (i = rpng2_info.width;  i > 0;  --i) {
1073             r = *src++;
1074             g = *src++;
1075             b = *src++;
1076             a = *src++;
1077             if (bg_image) {
1078                 bg_red   = *src2++;
1079                 bg_green = *src2++;
1080                 bg_blue  = *src2++;
1081             }
1082             if (a == 255) {
1083                 *dest++ = b;
1084                 *dest++ = g;
1085                 *dest++ = r;
1086             } else if (a == 0) {
1087                 *dest++ = bg_blue;
1088                 *dest++ = bg_green;
1089                 *dest++ = bg_red;
1090             } else {
1091                 /* this macro (copied from png.h) composites the
1092                  * foreground and background values and puts the
1093                  * result into the first argument; there are no
1094                  * side effects with the first argument */
1095                 alpha_composite(*dest++, b, a, bg_blue);
1096                 alpha_composite(*dest++, g, a, bg_green);
1097                 alpha_composite(*dest++, r, a, bg_red);
1098             }
1099         }
1100     }
1101
1102 /*---------------------------------------------------------------------------
1103     Display after every 16 rows or when on last row.  (Region may include
1104     previously displayed lines due to interlacing--i.e., not contiguous.)
1105   ---------------------------------------------------------------------------*/
1106
1107     if ((rows & 0xf) == 0 || row == rpng2_info.height-1) {
1108         RECT rect;
1109
1110         rect.left = 0L;
1111         rect.top = (LONG)firstrow;
1112         rect.right = (LONG)rpng2_info.width;       /* possibly off by one? */
1113         rect.bottom = (LONG)row + 1L;              /* possibly off by one? */
1114         InvalidateRect(global_hwnd, &rect, FALSE);
1115         UpdateWindow(global_hwnd);                 /* similar to XFlush() */
1116         rows = 0;
1117     }
1118
1119 } /* end function rpng2_win_display_row() */
1120
1121
1122
1123
1124
1125 static void rpng2_win_finish_display()
1126 {
1127     Trace((stderr, "beginning rpng2_win_finish_display()\n"))
1128
1129     /* last row has already been displayed by rpng2_win_display_row(), so
1130      * we have nothing to do here except set a flag and let the user know
1131      * that the image is done */
1132
1133     rpng2_info.state = kDone;
1134     printf(
1135       "Done.  Press Q, Esc or mouse button 1 (within image window) to quit.\n");
1136     fflush(stdout);
1137 }
1138
1139
1140
1141
1142
1143 static void rpng2_win_cleanup()
1144 {
1145     if (bg_image && bg_data) {
1146         free(bg_data);
1147         bg_data = NULL;
1148     }
1149
1150     if (rpng2_info.image_data) {
1151         free(rpng2_info.image_data);
1152         rpng2_info.image_data = NULL;
1153     }
1154
1155     if (rpng2_info.row_pointers) {
1156         free(rpng2_info.row_pointers);
1157         rpng2_info.row_pointers = NULL;
1158     }
1159
1160     if (dib) {
1161         free(dib);
1162         dib = NULL;
1163     }
1164 }
1165
1166
1167
1168
1169
1170 LRESULT CALLBACK rpng2_win_wndproc(HWND hwnd, UINT iMsg, WPARAM wP, LPARAM lP)
1171 {
1172     HDC         hdc;
1173     PAINTSTRUCT ps;
1174     int rc;
1175
1176     switch (iMsg) {
1177         case WM_CREATE:
1178             /* one-time processing here, if any */
1179             return 0;
1180
1181         case WM_PAINT:
1182             hdc = BeginPaint(hwnd, &ps);
1183             rc = StretchDIBits(hdc, 0, 0, rpng2_info.width, rpng2_info.height,
1184                                     0, 0, rpng2_info.width, rpng2_info.height,
1185                                     wimage_data, (BITMAPINFO *)bmih,
1186                                     0, SRCCOPY);
1187             EndPaint(hwnd, &ps);
1188             return 0;
1189
1190         /* wait for the user to tell us when to quit */
1191         case WM_CHAR:
1192             switch (wP) {       /* only need one, so ignore repeat count */
1193                 case 'q':
1194                 case 'Q':
1195                 case 0x1B:      /* Esc key */
1196                     PostQuitMessage(0);
1197             }
1198             return 0;
1199
1200         case WM_LBUTTONDOWN:    /* another way of quitting */
1201         case WM_DESTROY:
1202             PostQuitMessage(0);
1203             return 0;
1204     }
1205
1206     return DefWindowProc(hwnd, iMsg, wP, lP);
1207 }