Mercurial > fife-parpg
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 } |