Fix 2502818: Noise when refresh an imported image layer.
[synfig.git] / gtkmm-osx / trunk / libpng-1.2.5 / contrib / gregbook / rpng-win.c
blobb84a7fc2109e24fa1a0930c6f3979054340302e5
1 /*---------------------------------------------------------------------------
3 rpng - simple PNG display program rpng-win.c
5 This program decodes and displays PNG images, with gamma correction and
6 optionally with a user-specified background color (in case the image has
7 transparency). It is very nearly the most basic PNG viewer possible.
8 This version is for 32-bit Windows; it may compile under 16-bit Windows
9 with a little tweaking (or maybe not).
11 to do:
12 - handle quoted command-line args (especially filenames with spaces)
13 - have minimum window width: oh well
14 - use %.1023s to simplify truncation of title-bar string?
16 ---------------------------------------------------------------------------
18 Changelog:
19 - 1.00: initial public release
20 - 1.01: modified to allow abbreviated options; fixed long/ulong mis-
21 match; switched to png_jmpbuf() macro
22 - 1.02: added extra set of parentheses to png_jmpbuf() macro; fixed
23 command-line parsing bug
24 - 1.10: enabled "message window"/console (thanks to David Geldreich)
26 ---------------------------------------------------------------------------
28 Copyright (c) 1998-2001 Greg Roelofs. All rights reserved.
30 This software is provided "as is," without warranty of any kind,
31 express or implied. In no event shall the author or contributors
32 be held liable for any damages arising in any way from the use of
33 this software.
35 Permission is granted to anyone to use this software for any purpose,
36 including commercial applications, and to alter it and redistribute
37 it freely, subject to the following restrictions:
39 1. Redistributions of source code must retain the above copyright
40 notice, disclaimer, and this list of conditions.
41 2. Redistributions in binary form must reproduce the above copyright
42 notice, disclaimer, and this list of conditions in the documenta-
43 tion and/or other materials provided with the distribution.
44 3. All advertising materials mentioning features or use of this
45 software must display the following acknowledgment:
47 This product includes software developed by Greg Roelofs
48 and contributors for the book, "PNG: The Definitive Guide,"
49 published by O'Reilly and Associates.
51 ---------------------------------------------------------------------------*/
53 #define PROGNAME "rpng-win"
54 #define LONGNAME "Simple PNG Viewer for Windows"
55 #define VERSION "1.20 of 28 May 2001"
57 #include <stdio.h>
58 #include <stdlib.h>
59 #include <string.h>
60 #include <time.h>
61 #include <windows.h>
62 #include <conio.h> /* only for _getch() */
64 /* #define DEBUG : this enables the Trace() macros */
66 #include "readpng.h" /* typedefs, common macros, readpng prototypes */
69 /* could just include png.h, but this macro is the only thing we need
70 * (name and typedefs changed to local versions); note that side effects
71 * only happen with alpha (which could easily be avoided with
72 * "ush acopy = (alpha);") */
74 #define alpha_composite(composite, fg, alpha, bg) { \
75 ush temp = ((ush)(fg)*(ush)(alpha) + \
76 (ush)(bg)*(ush)(255 - (ush)(alpha)) + (ush)128); \
77 (composite) = (uch)((temp + (temp >> 8)) >> 8); \
81 /* local prototypes */
82 static int rpng_win_create_window(HINSTANCE hInst, int showmode);
83 static int rpng_win_display_image(void);
84 static void rpng_win_cleanup(void);
85 LRESULT CALLBACK rpng_win_wndproc(HWND, UINT, WPARAM, LPARAM);
88 static char titlebar[1024], *window_name = titlebar;
89 static char *progname = PROGNAME;
90 static char *appname = LONGNAME;
91 static char *icon_name = PROGNAME; /* GRR: not (yet) used */
92 static char *filename;
93 static FILE *infile;
95 static char *bgstr;
96 static uch bg_red=0, bg_green=0, bg_blue=0;
98 static double display_exponent;
100 static ulg image_width, image_height, image_rowbytes;
101 static int image_channels;
102 static uch *image_data;
104 /* Windows-specific variables */
105 static ulg wimage_rowbytes;
106 static uch *dib;
107 static uch *wimage_data;
108 static BITMAPINFOHEADER *bmih;
110 static HWND global_hwnd;
115 int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, PSTR cmd, int showmode)
117 char *args[1024]; /* arbitrary limit, but should suffice */
118 char *p, *q, **argv = args;
119 int argc = 0;
120 int rc, alen, flen;
121 int error = 0;
122 int have_bg = FALSE;
123 double LUT_exponent; /* just the lookup table */
124 double CRT_exponent = 2.2; /* just the monitor */
125 double default_display_exponent; /* whole display system */
126 MSG msg;
129 filename = (char *)NULL;
132 /* First reenable console output, which normally goes to the bit bucket
133 * for windowed apps. Closing the console window will terminate the
134 * app. Thanks to David.Geldreich@realviz.com for supplying the magical
135 * incantation. */
137 AllocConsole();
138 freopen("CONOUT$", "a", stderr);
139 freopen("CONOUT$", "a", stdout);
142 /* Next set the default value for our display-system exponent, i.e.,
143 * the product of the CRT exponent and the exponent corresponding to
144 * the frame-buffer's lookup table (LUT), if any. This is not an
145 * exhaustive list of LUT values (e.g., OpenStep has a lot of weird
146 * ones), but it should cover 99% of the current possibilities. And
147 * yes, these ifdefs are completely wasted in a Windows program... */
149 #if defined(NeXT)
150 LUT_exponent = 1.0 / 2.2;
152 if (some_next_function_that_returns_gamma(&next_gamma))
153 LUT_exponent = 1.0 / next_gamma;
155 #elif defined(sgi)
156 LUT_exponent = 1.0 / 1.7;
157 /* there doesn't seem to be any documented function to get the
158 * "gamma" value, so we do it the hard way */
159 infile = fopen("/etc/config/system.glGammaVal", "r");
160 if (infile) {
161 double sgi_gamma;
163 fgets(tmpline, 80, infile);
164 fclose(infile);
165 sgi_gamma = atof(tmpline);
166 if (sgi_gamma > 0.0)
167 LUT_exponent = 1.0 / sgi_gamma;
169 #elif defined(Macintosh)
170 LUT_exponent = 1.8 / 2.61;
172 if (some_mac_function_that_returns_gamma(&mac_gamma))
173 LUT_exponent = mac_gamma / 2.61;
175 #else
176 LUT_exponent = 1.0; /* assume no LUT: most PCs */
177 #endif
179 /* the defaults above give 1.0, 1.3, 1.5 and 2.2, respectively: */
180 default_display_exponent = LUT_exponent * CRT_exponent;
183 /* If the user has set the SCREEN_GAMMA environment variable as suggested
184 * (somewhat imprecisely) in the libpng documentation, use that; otherwise
185 * use the default value we just calculated. Either way, the user may
186 * override this via a command-line option. */
188 if ((p = getenv("SCREEN_GAMMA")) != NULL)
189 display_exponent = atof(p);
190 else
191 display_exponent = default_display_exponent;
194 /* Windows really hates command lines, so we have to set up our own argv.
195 * Note that we do NOT bother with quoted arguments here, so don't use
196 * filenames with spaces in 'em! */
198 argv[argc++] = PROGNAME;
199 p = cmd;
200 for (;;) {
201 if (*p == ' ')
202 while (*++p == ' ')
204 /* now p points at the first non-space after some spaces */
205 if (*p == '\0')
206 break; /* nothing after the spaces: done */
207 argv[argc++] = q = p;
208 while (*q && *q != ' ')
209 ++q;
210 /* now q points at a space or the end of the string */
211 if (*q == '\0')
212 break; /* last argv already terminated; quit */
213 *q = '\0'; /* change space to terminator */
214 p = q + 1;
216 argv[argc] = NULL; /* terminate the argv array itself */
219 /* Now parse the command line for options and the PNG filename. */
221 while (*++argv && !error) {
222 if (!strncmp(*argv, "-gamma", 2)) {
223 if (!*++argv)
224 ++error;
225 else {
226 display_exponent = atof(*argv);
227 if (display_exponent <= 0.0)
228 ++error;
230 } else if (!strncmp(*argv, "-bgcolor", 2)) {
231 if (!*++argv)
232 ++error;
233 else {
234 bgstr = *argv;
235 if (strlen(bgstr) != 7 || bgstr[0] != '#')
236 ++error;
237 else
238 have_bg = TRUE;
240 } else {
241 if (**argv != '-') {
242 filename = *argv;
243 if (argv[1]) /* shouldn't be any more args after filename */
244 ++error;
245 } else
246 ++error; /* not expecting any other options */
250 if (!filename) {
251 ++error;
252 } else if (!(infile = fopen(filename, "rb"))) {
253 fprintf(stderr, PROGNAME ": can't open PNG file [%s]\n", filename);
254 ++error;
255 } else {
256 if ((rc = readpng_init(infile, &image_width, &image_height)) != 0) {
257 switch (rc) {
258 case 1:
259 fprintf(stderr, PROGNAME
260 ": [%s] is not a PNG file: incorrect signature\n",
261 filename);
262 break;
263 case 2:
264 fprintf(stderr, PROGNAME
265 ": [%s] has bad IHDR (libpng longjmp)\n",
266 filename);
267 break;
268 case 4:
269 fprintf(stderr, PROGNAME ": insufficient memory\n");
270 break;
271 default:
272 fprintf(stderr, PROGNAME
273 ": unknown readpng_init() error\n");
274 break;
276 ++error;
278 if (error)
279 fclose(infile);
283 /* usage screen */
285 if (error) {
286 int ch;
288 fprintf(stderr, "\n%s %s: %s\n\n", PROGNAME, VERSION, appname);
289 readpng_version_info();
290 fprintf(stderr, "\n"
291 "Usage: %s [-gamma exp] [-bgcolor bg] file.png\n"
292 " exp \ttransfer-function exponent (``gamma'') of the display\n"
293 "\t\t system in floating-point format (e.g., ``%.1f''); equal\n"
294 "\t\t to the product of the lookup-table exponent (varies)\n"
295 "\t\t and the CRT exponent (usually 2.2); must be positive\n"
296 " bg \tdesired background color in 7-character hex RGB format\n"
297 "\t\t (e.g., ``#ff7700'' for orange: same as HTML colors);\n"
298 "\t\t used with transparent images\n"
299 "\nPress Q, Esc or mouse button 1 after image is displayed to quit.\n"
300 "Press Q or Esc to quit this usage screen.\n"
301 "\n", PROGNAME, default_display_exponent);
303 ch = _getch();
304 while (ch != 'q' && ch != 'Q' && ch != 0x1B);
305 exit(1);
306 } else {
307 fprintf(stderr, "\n%s %s: %s\n", PROGNAME, VERSION, appname);
308 fprintf(stderr,
309 "\n [console window: closing this window will terminate %s]\n\n",
310 PROGNAME);
314 /* set the title-bar string, but make sure buffer doesn't overflow */
316 alen = strlen(appname);
317 flen = strlen(filename);
318 if (alen + flen + 3 > 1023)
319 sprintf(titlebar, "%s: ...%s", appname, filename+(alen+flen+6-1023));
320 else
321 sprintf(titlebar, "%s: %s", appname, filename);
324 /* if the user didn't specify a background color on the command line,
325 * check for one in the PNG file--if not, the initialized values of 0
326 * (black) will be used */
328 if (have_bg)
329 sscanf(bgstr+1, "%2x%2x%2x", &bg_red, &bg_green, &bg_blue);
330 else if (readpng_get_bgcolor(&bg_red, &bg_green, &bg_blue) > 1) {
331 readpng_cleanup(TRUE);
332 fprintf(stderr, PROGNAME
333 ": libpng error while checking for background color\n");
334 exit(2);
338 /* do the basic Windows initialization stuff, make the window and fill it
339 * with the background color */
341 if (rpng_win_create_window(hInst, showmode))
342 exit(2);
345 /* decode the image, all at once */
347 Trace((stderr, "calling readpng_get_image()\n"))
348 image_data = readpng_get_image(display_exponent, &image_channels,
349 &image_rowbytes);
350 Trace((stderr, "done with readpng_get_image()\n"))
353 /* done with PNG file, so clean up to minimize memory usage (but do NOT
354 * nuke image_data!) */
356 readpng_cleanup(FALSE);
357 fclose(infile);
359 if (!image_data) {
360 fprintf(stderr, PROGNAME ": unable to decode PNG image\n");
361 exit(3);
365 /* display image (composite with background if requested) */
367 Trace((stderr, "calling rpng_win_display_image()\n"))
368 if (rpng_win_display_image()) {
369 free(image_data);
370 exit(4);
372 Trace((stderr, "done with rpng_win_display_image()\n"))
375 /* wait for the user to tell us when to quit */
377 printf(
378 "Done. Press Q, Esc or mouse button 1 (within image window) to quit.\n");
379 fflush(stdout);
381 while (GetMessage(&msg, NULL, 0, 0)) {
382 TranslateMessage(&msg);
383 DispatchMessage(&msg);
387 /* OK, we're done: clean up all image and Windows resources and go away */
389 rpng_win_cleanup();
391 return msg.wParam;
398 static int rpng_win_create_window(HINSTANCE hInst, int showmode)
400 uch *dest;
401 int extra_width, extra_height;
402 ulg i, j;
403 WNDCLASSEX wndclass;
406 /*---------------------------------------------------------------------------
407 Allocate memory for the display-specific version of the image (round up
408 to multiple of 4 for Windows DIB).
409 ---------------------------------------------------------------------------*/
411 wimage_rowbytes = ((3*image_width + 3L) >> 2) << 2;
413 if (!(dib = (uch *)malloc(sizeof(BITMAPINFOHEADER) +
414 wimage_rowbytes*image_height)))
416 return 4; /* fail */
419 /*---------------------------------------------------------------------------
420 Initialize the DIB. Negative height means to use top-down BMP ordering
421 (must be uncompressed, but that's what we want). Bit count of 1, 4 or 8
422 implies a colormap of RGBX quads, but 24-bit BMPs just use B,G,R values
423 directly => wimage_data begins immediately after BMP header.
424 ---------------------------------------------------------------------------*/
426 memset(dib, 0, sizeof(BITMAPINFOHEADER));
427 bmih = (BITMAPINFOHEADER *)dib;
428 bmih->biSize = sizeof(BITMAPINFOHEADER);
429 bmih->biWidth = image_width;
430 bmih->biHeight = -((long)image_height);
431 bmih->biPlanes = 1;
432 bmih->biBitCount = 24;
433 bmih->biCompression = 0;
434 wimage_data = dib + sizeof(BITMAPINFOHEADER);
436 /*---------------------------------------------------------------------------
437 Fill in background color (black by default); data are in BGR order.
438 ---------------------------------------------------------------------------*/
440 for (j = 0; j < image_height; ++j) {
441 dest = wimage_data + j*wimage_rowbytes;
442 for (i = image_width; i > 0; --i) {
443 *dest++ = bg_blue;
444 *dest++ = bg_green;
445 *dest++ = bg_red;
449 /*---------------------------------------------------------------------------
450 Set the window parameters.
451 ---------------------------------------------------------------------------*/
453 memset(&wndclass, 0, sizeof(wndclass));
455 wndclass.cbSize = sizeof(wndclass);
456 wndclass.style = CS_HREDRAW | CS_VREDRAW;
457 wndclass.lpfnWndProc = rpng_win_wndproc;
458 wndclass.hInstance = hInst;
459 wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
460 wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
461 wndclass.hbrBackground = (HBRUSH)GetStockObject(DKGRAY_BRUSH);
462 wndclass.lpszMenuName = NULL;
463 wndclass.lpszClassName = progname;
464 wndclass.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
466 RegisterClassEx(&wndclass);
468 /*---------------------------------------------------------------------------
469 Finally, create the window.
470 ---------------------------------------------------------------------------*/
472 extra_width = 2*(GetSystemMetrics(SM_CXBORDER) +
473 GetSystemMetrics(SM_CXDLGFRAME));
474 extra_height = 2*(GetSystemMetrics(SM_CYBORDER) +
475 GetSystemMetrics(SM_CYDLGFRAME)) +
476 GetSystemMetrics(SM_CYCAPTION);
478 global_hwnd = CreateWindow(progname, titlebar, WS_OVERLAPPEDWINDOW,
479 CW_USEDEFAULT, CW_USEDEFAULT, image_width+extra_width,
480 image_height+extra_height, NULL, NULL, hInst, NULL);
482 ShowWindow(global_hwnd, showmode);
483 UpdateWindow(global_hwnd);
485 return 0;
487 } /* end function rpng_win_create_window() */
493 static int rpng_win_display_image()
495 uch *src, *dest;
496 uch r, g, b, a;
497 ulg i, row, lastrow;
498 RECT rect;
501 Trace((stderr, "beginning display loop (image_channels == %d)\n",
502 image_channels))
503 Trace((stderr, "(width = %ld, rowbytes = %ld, wimage_rowbytes = %d)\n",
504 image_width, image_rowbytes, wimage_rowbytes))
507 /*---------------------------------------------------------------------------
508 Blast image data to buffer. This whole routine takes place before the
509 message loop begins, so there's no real point in any pseudo-progressive
510 display...
511 ---------------------------------------------------------------------------*/
513 for (lastrow = row = 0; row < image_height; ++row) {
514 src = image_data + row*image_rowbytes;
515 dest = wimage_data + row*wimage_rowbytes;
516 if (image_channels == 3) {
517 for (i = image_width; i > 0; --i) {
518 r = *src++;
519 g = *src++;
520 b = *src++;
521 *dest++ = b;
522 *dest++ = g; /* note reverse order */
523 *dest++ = r;
525 } else /* if (image_channels == 4) */ {
526 for (i = image_width; i > 0; --i) {
527 r = *src++;
528 g = *src++;
529 b = *src++;
530 a = *src++;
531 if (a == 255) {
532 *dest++ = b;
533 *dest++ = g;
534 *dest++ = r;
535 } else if (a == 0) {
536 *dest++ = bg_blue;
537 *dest++ = bg_green;
538 *dest++ = bg_red;
539 } else {
540 /* this macro (copied from png.h) composites the
541 * foreground and background values and puts the
542 * result into the first argument; there are no
543 * side effects with the first argument */
544 alpha_composite(*dest++, b, a, bg_blue);
545 alpha_composite(*dest++, g, a, bg_green);
546 alpha_composite(*dest++, r, a, bg_red);
550 /* display after every 16 lines */
551 if (((row+1) & 0xf) == 0) {
552 rect.left = 0L;
553 rect.top = (LONG)lastrow;
554 rect.right = (LONG)image_width; /* possibly off by one? */
555 rect.bottom = (LONG)lastrow + 16L; /* possibly off by one? */
556 InvalidateRect(global_hwnd, &rect, FALSE);
557 UpdateWindow(global_hwnd); /* similar to XFlush() */
558 lastrow = row + 1;
562 Trace((stderr, "calling final image-flush routine\n"))
563 if (lastrow < image_height) {
564 rect.left = 0L;
565 rect.top = (LONG)lastrow;
566 rect.right = (LONG)image_width; /* possibly off by one? */
567 rect.bottom = (LONG)image_height; /* possibly off by one? */
568 InvalidateRect(global_hwnd, &rect, FALSE);
569 UpdateWindow(global_hwnd); /* similar to XFlush() */
573 last param determines whether or not background is wiped before paint
574 InvalidateRect(global_hwnd, NULL, TRUE);
575 UpdateWindow(global_hwnd);
578 return 0;
585 static void rpng_win_cleanup()
587 if (image_data) {
588 free(image_data);
589 image_data = NULL;
592 if (dib) {
593 free(dib);
594 dib = NULL;
602 LRESULT CALLBACK rpng_win_wndproc(HWND hwnd, UINT iMsg, WPARAM wP, LPARAM lP)
604 HDC hdc;
605 PAINTSTRUCT ps;
606 int rc;
608 switch (iMsg) {
609 case WM_CREATE:
610 /* one-time processing here, if any */
611 return 0;
613 case WM_PAINT:
614 hdc = BeginPaint(hwnd, &ps);
615 /* dest */
616 rc = StretchDIBits(hdc, 0, 0, image_width, image_height,
617 /* source */
618 0, 0, image_width, image_height,
619 wimage_data, (BITMAPINFO *)bmih,
620 /* iUsage: no clue */
621 0, SRCCOPY);
622 EndPaint(hwnd, &ps);
623 return 0;
625 /* wait for the user to tell us when to quit */
626 case WM_CHAR:
627 switch (wP) { /* only need one, so ignore repeat count */
628 case 'q':
629 case 'Q':
630 case 0x1B: /* Esc key */
631 PostQuitMessage(0);
633 return 0;
635 case WM_LBUTTONDOWN: /* another way of quitting */
636 case WM_DESTROY:
637 PostQuitMessage(0);
638 return 0;
641 return DefWindowProc(hwnd, iMsg, wP, lP);