comparison ext/libpng-1.2.29/contrib/gregbook/rpng2-win.c @ 0:4a0efb7baf70

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