1 /*---------------------------------------------------------------------------
3 rpng2 - progressive-model PNG display program rpng2-win.c
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.
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
19 ---------------------------------------------------------------------------
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
37 ---------------------------------------------------------------------------
39 Copyright (c) 1998-2008 Greg Roelofs. All rights reserved.
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
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):
51 LICENSE 1 ("BSD-like with advertising clause"):
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:
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:
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.
70 LICENSE 2 (GNU GPL v2 or later):
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.
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.
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
86 ---------------------------------------------------------------------------*/
88 #define PROGNAME "rpng2-win"
89 #define LONGNAME "Progressive PNG Viewer for Windows"
90 #define VERSION "2.02 of 16 March 2008"
95 #include <setjmp.h> /* for jmpbuf declaration in readpng2.h */
97 #include <math.h> /* only for PvdM background code */
99 #include <conio.h> /* only for _getch() */
101 /* all for PvdM background code: */
103 # define PI 3.141592653589793238
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))
114 #define rgb1_max bg_freq
115 #define rgb1_min bg_gray
116 #define rgb2_max bg_bsat
117 #define rgb2_min bg_brot
119 /* #define DEBUG */ /* this enables the Trace() macros */
121 #include "readpng2.h" /* typedefs, common macros, readpng2 prototypes */
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);") */
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); \
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
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);
152 static char titlebar[1024];
153 static char *progname = PROGNAME;
154 static char *appname = LONGNAME;
155 static char *filename;
158 static mainprog_info rpng2_info;
160 static uch inbuf[INBUFSIZE];
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;
169 static struct rgb_color {
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 */
189 /* not used for now, but should be for error-checking:
190 static int num_rgb = sizeof(rgb) / sizeof(struct rgb_color);
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
201 F E D C B A 9 8 7 6 5 4 3 2 1 0
203 | | +-+-+-- 0 = sharp-edged checkerboard
204 | | 1 = soft diamonds
207 | +-- gradient #2 inverted?
208 +-- alternating columns inverted?
210 static struct background_pattern {
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)*/
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) */
232 static int num_bgpat = sizeof(bg) / sizeof(struct background_pattern);
235 /* Windows-specific global variables (could go in struct, but messy...) */
236 static ulg wimage_rowbytes;
238 static uch *wimage_data;
239 static BITMAPINFOHEADER *bmih;
241 static HWND global_hwnd;
242 static HINSTANCE global_hInst;
243 static int global_showmode;
248 int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, PSTR cmd, int showmode)
250 char *args[1024]; /* arbitrary limit, but should suffice */
252 char *p, *q, *bgstr = NULL;
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 */
264 /* First initialize a few things, just to be sure--memset takes care of
265 * default background color (black), booleans (FALSE), pointers (NULL),
268 global_hInst = hInst;
269 global_showmode = showmode;
270 filename = (char *)NULL;
271 memset(&rpng2_info, 0, sizeof(mainprog_info));
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
280 freopen("CONOUT$", "a", stderr);
281 freopen("CONOUT$", "a", stdout);
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... */
292 /* third-party utilities can modify the default LUT exponent */
293 LUT_exponent = 1.0 / 2.2;
295 if (some_next_function_that_returns_gamma(&next_gamma))
296 LUT_exponent = 1.0 / next_gamma;
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");
306 fgets(tmpline, 80, infile);
308 sgi_gamma = atof(tmpline);
310 LUT_exponent = 1.0 / sgi_gamma;
312 #elif defined(Macintosh)
313 LUT_exponent = 1.8 / 2.61;
315 if (some_mac_function_that_returns_gamma(&mac_gamma))
316 LUT_exponent = mac_gamma / 2.61;
319 LUT_exponent = 1.0; /* assume no LUT: most PCs */
322 /* the defaults above give 1.0, 1.3, 1.5 and 2.2, respectively: */
323 default_display_exponent = LUT_exponent * CRT_exponent;
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. */
331 if ((p = getenv("SCREEN_GAMMA")) != NULL)
332 rpng2_info.display_exponent = atof(p);
334 rpng2_info.display_exponent = default_display_exponent;
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! */
341 argv[argc++] = PROGNAME;
347 /* now p points at the first non-space after some spaces */
349 break; /* nothing after the spaces: done */
350 argv[argc++] = q = p;
351 while (*q && *q != ' ')
353 /* now q points at a space or the end of the string */
355 break; /* last argv already terminated; quit */
356 *q = '\0'; /* change space to terminator */
359 argv[argc] = NULL; /* terminate the argv array itself */
362 /* Now parse the command line for options and the PNG filename. */
364 while (*++argv && !error) {
365 if (!strncmp(*argv, "-gamma", 2)) {
369 rpng2_info.display_exponent = atof(*argv);
370 if (rpng2_info.display_exponent <= 0.0)
373 } else if (!strncmp(*argv, "-bgcolor", 4)) {
378 if (strlen(bgstr) != 7 || bgstr[0] != '#')
385 } else if (!strncmp(*argv, "-bgpat", 4)) {
389 pat = atoi(*argv) - 1;
390 if (pat < 0 || pat >= num_bgpat)
397 } else if (!strncmp(*argv, "-timing", 2)) {
402 if (argv[1]) /* shouldn't be any more args after filename */
405 ++error; /* not expecting any other options */
413 /* print usage screen if any errors up to this point */
418 fprintf(stderr, "\n%s %s: %s\n\n", PROGNAME, VERSION, appname);
419 readpng2_version_info();
421 "Usage: %s [-gamma exp] [-bgcolor bg | -bgpat pat] [-timing]\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. ",
437 #if (defined(__i386__) || defined(_M_IX86) || defined(__x86_64__))
438 (int)strlen(PROGNAME), " ",
440 (int)strlen(PROGNAME), " ", default_display_exponent, num_bgpat);
444 while (ch != 'q' && ch != 'Q' && ch != 0x1B);
449 if (!(infile = fopen(filename, "rb"))) {
450 fprintf(stderr, PROGNAME ": can't open PNG file [%s]\n", filename);
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",
459 } else if ((rc = readpng2_init(&rpng2_info)) != 0) {
462 fprintf(stderr, PROGNAME
463 ": [%s] has bad IHDR (libpng longjmp)\n", filename);
466 fprintf(stderr, PROGNAME ": insufficient memory\n");
469 fprintf(stderr, PROGNAME
470 ": unknown readpng2_init() error\n");
483 fprintf(stderr, PROGNAME ": aborting.\n");
486 while (ch != 'q' && ch != 'Q' && ch != 0x1B);
489 fprintf(stderr, "\n%s %s: %s\n", PROGNAME, VERSION, appname);
491 "\n [console window: closing this window will terminate %s]\n\n",
497 /* set the title-bar string, but make sure buffer doesn't overflow */
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));
504 sprintf(titlebar, "%s: %s", appname, filename);
507 /* set some final rpng2_info variables before entering main data loop */
510 unsigned r, g, b; /* this approach quiets compiler warnings */
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;
517 rpng2_info.need_bgcolor = TRUE;
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;
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
535 Trace((stderr, "about to call readpng2_decode_data()\n"))
536 if (readpng2_decode_data(&rpng2_info, inbuf, incount))
538 Trace((stderr, "done with readpng2_decode_data()\n"))
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");
547 } else if (feof(infile)) {
548 fprintf(stderr, PROGNAME ": end of file reached "
549 "(unexpectedly) while reading PNG image file\n");
551 } else /* if (error) */ {
552 // will print error message below
560 incount = fread(inbuf, 1, INBUFSIZE, infile);
564 /* clean up PNG stuff and report any decoding errors */
567 Trace((stderr, "about to call readpng2_cleanup()\n"))
568 readpng2_cleanup(&rpng2_info);
571 fprintf(stderr, PROGNAME ": libpng error while decoding PNG image\n");
576 /* wait for the user to tell us when to quit */
578 while (GetMessage(&msg, NULL, 0, 0)) {
579 TranslateMessage(&msg);
580 DispatchMessage(&msg);
584 /* we're done: clean up all image and Windows resources and go away */
586 Trace((stderr, "about to call rpng2_win_cleanup()\n"))
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 */
600 static void rpng2_win_init()
603 ulg rowbytes = rpng2_info.rowbytes;
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))
610 rpng2_info.image_data = (uch *)malloc(rowbytes * rpng2_info.height);
611 if (!rpng2_info.image_data) {
612 readpng2_cleanup(&rpng2_info);
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);
624 for (i = 0; i < rpng2_info.height; ++i)
625 rpng2_info.row_pointers[i] = rpng2_info.image_data + i*rowbytes;
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 ---------------------------------------------------------------------------*/
632 if (rpng2_win_create_window()) {
633 readpng2_cleanup(&rpng2_info);
637 rpng2_info.state = kWindowInit;
644 static int rpng2_win_create_window()
646 uch bg_red = rpng2_info.bg_red;
647 uch bg_green = rpng2_info.bg_green;
648 uch bg_blue = rpng2_info.bg_blue;
650 int extra_width, extra_height;
656 /*---------------------------------------------------------------------------
657 Allocate memory for the display-specific version of the image (round up
658 to multiple of 4 for Windows DIB).
659 ---------------------------------------------------------------------------*/
661 wimage_rowbytes = ((3*rpng2_info.width + 3L) >> 2) << 2;
663 if (!(dib = (uch *)malloc(sizeof(BITMAPINFOHEADER) +
664 wimage_rowbytes*rpng2_info.height)))
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 ---------------------------------------------------------------------------*/
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);
682 bmih->biBitCount = 24;
683 bmih->biCompression = 0;
684 wimage_data = dib + sizeof(BITMAPINFOHEADER);
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 ---------------------------------------------------------------------------*/
692 if (bg_image) { /* just fill with black for now */
693 memset(wimage_data, 0, wimage_rowbytes*rpng2_info.height);
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) {
705 /*---------------------------------------------------------------------------
706 Set the window parameters.
707 ---------------------------------------------------------------------------*/
709 memset(&wndclass, 0, sizeof(wndclass));
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);
722 RegisterClassEx(&wndclass);
724 /*---------------------------------------------------------------------------
725 Finally, create the window.
726 ---------------------------------------------------------------------------*/
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);
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);
738 ShowWindow(global_hwnd, global_showmode);
739 UpdateWindow(global_hwnd);
741 /*---------------------------------------------------------------------------
742 Now compute the background image and display it. If it fails (memory
743 allocation), revert to a plain background color.
744 ---------------------------------------------------------------------------*/
747 static const char *msg = "Computing background image...";
748 int x, y, len = strlen(msg);
749 HDC hdc = GetDC(global_hwnd);
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);
761 rpng2_win_load_bg_image(); /* resets bg_image if fails */
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) {
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() */
784 } /* end function rpng2_win_create_window() */
790 static int rpng2_win_load_bg_image()
793 uch r1, r2, g1, g2, b1, b2;
794 uch r1_inv, r2_inv, g1_inv, g2_inv, b1_inv, b2_inv;
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);
802 /*---------------------------------------------------------------------------
803 Allocate buffer for fake background image to be used with transparent
804 images; if this fails, revert to plain background color.
805 ---------------------------------------------------------------------------*/
807 bg_rowbytes = 3 * rpng2_info.width;
808 bg_data = (uch *)malloc(bg_rowbytes * rpng2_info.height);
810 fprintf(stderr, PROGNAME
811 ": unable to allocate memory for background image\n");
816 /*---------------------------------------------------------------------------
817 Vertical gradients (ramps) in NxN squares, alternating direction and
818 colors (N == bgscale).
819 ---------------------------------------------------------------------------*/
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;
835 for (row = 0; row < rpng2_info.height; ++row) {
836 yidx = row % bgscale;
837 even_odd_vert = (row / bgscale) & 1;
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;
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;
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;
858 (even_odd_horiz && (bg[pat].type & 0x10));
859 if (even_odd == 0) { /* gradient #1 */
869 } else { /* gradient #2 */
870 if ((invert_column && invert_gradient2) ||
871 (!invert_column && !invert_gradient2))
873 *dest++ = r2; /* not inverted or */
874 *dest++ = g2; /* doubly inverted */
878 *dest++ = g2_inv; /* singly inverted */
885 /*---------------------------------------------------------------------------
886 Soft gradient-diamonds with scale = bgscale. Code contributed by Adam
888 ---------------------------------------------------------------------------*/
890 } else if ((bg[pat].type & 0x07) == 1) {
892 hmax = (bgscale-1)/2; /* half the max weight of a color */
893 max = 2*hmax; /* the max weight of a color */
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;
902 for (row = 0; row < rpng2_info.height; ++row) {
903 yidx = row % bgscale;
905 yidx = bgscale-1 - yidx;
906 dest = bg_data + row*bg_rowbytes;
907 for (i = 0; i < rpng2_info.width; ++i) {
910 xidx = bgscale-1 - xidx;
912 *dest++ = (k*r1 + (max-k)*r2) / max;
913 *dest++ = (k*g1 + (max-k)*g2) / max;
914 *dest++ = (k*b1 + (max-k)*b2) / max;
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 ---------------------------------------------------------------------------*/
924 } else if ((bg[pat].type & 0x07) == 2) {
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;
931 fprintf(stderr, "%s: computing radial background...",
935 hh = rpng2_info.height / 2;
936 hw = rpng2_info.width / 2;
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
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;
952 maxDist = (double)((hw*hw) + (hh*hh));
954 for (row = 0; row < rpng2_info.height; ++row) {
956 dest = bg_data + row*bg_rowbytes;
957 for (i = 0; i < rpng2_info.width; ++i) {
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) *
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);
972 ch = (uch)(v * 255.0);
977 if ((hue < 0.0) || (hue >= 360.0))
978 hue -= (((int)(hue / 360.0)) * 360.0);
981 f = hue - (double)ii;
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);
997 fprintf(stderr, "done.\n");
1001 /*---------------------------------------------------------------------------
1002 Blast background image to display buffer before beginning PNG decode;
1003 calling function will handle invalidation and UpdateWindow() call.
1004 ---------------------------------------------------------------------------*/
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) {
1014 *dest++ = g1; /* note reverse order */
1021 } /* end function rpng2_win_load_bg_image() */
1027 static void rpng2_win_display_row(ulg row)
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;
1036 static ulg firstrow;
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 ---------------------------------------------------------------------------*/
1044 Trace((stderr, "beginning rpng2_win_display_row()\n"))
1047 firstrow = row; /* first row not yet displayed */
1049 ++rows; /* count of rows received but not yet displayed */
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 ---------------------------------------------------------------------------*/
1057 src = rpng2_info.image_data + row*rpng2_info.rowbytes;
1059 src2 = bg_data + row*bg_rowbytes;
1060 dest = wimage_data + row*wimage_rowbytes;
1062 if (rpng2_info.channels == 3) {
1063 for (i = rpng2_info.width; i > 0; --i) {
1068 *dest++ = g; /* note reverse order */
1071 } else /* if (rpng2_info.channels == 4) */ {
1072 for (i = rpng2_info.width; i > 0; --i) {
1086 } else if (a == 0) {
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);
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 ---------------------------------------------------------------------------*/
1107 if ((rows & 0xf) == 0 || row == rpng2_info.height-1) {
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() */
1119 } /* end function rpng2_win_display_row() */
1125 static void rpng2_win_finish_display()
1127 Trace((stderr, "beginning rpng2_win_finish_display()\n"))
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 */
1133 rpng2_info.state = kDone;
1135 "Done. Press Q, Esc or mouse button 1 (within image window) to quit.\n");
1143 static void rpng2_win_cleanup()
1145 if (bg_image && bg_data) {
1150 if (rpng2_info.image_data) {
1151 free(rpng2_info.image_data);
1152 rpng2_info.image_data = NULL;
1155 if (rpng2_info.row_pointers) {
1156 free(rpng2_info.row_pointers);
1157 rpng2_info.row_pointers = NULL;
1170 LRESULT CALLBACK rpng2_win_wndproc(HWND hwnd, UINT iMsg, WPARAM wP, LPARAM lP)
1178 /* one-time processing here, if any */
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,
1187 EndPaint(hwnd, &ps);
1190 /* wait for the user to tell us when to quit */
1192 switch (wP) { /* only need one, so ignore repeat count */
1195 case 0x1B: /* Esc key */
1200 case WM_LBUTTONDOWN: /* another way of quitting */
1206 return DefWindowProc(hwnd, iMsg, wP, lP);