Remove .gitignore do nothing is ignored.
[synfig.git] / gtkmm-osx / trunk / libpng-1.2.5 / contrib / gregbook / rpng-win.c
1 /*---------------------------------------------------------------------------
2
3    rpng - simple PNG display program                             rpng-win.c
4
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).
10
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?
15
16   ---------------------------------------------------------------------------
17
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)
25
26   ---------------------------------------------------------------------------
27
28       Copyright (c) 1998-2001 Greg Roelofs.  All rights reserved.
29
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.
34
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:
38
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:
46
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.
50
51   ---------------------------------------------------------------------------*/
52
53 #define PROGNAME  "rpng-win"
54 #define LONGNAME  "Simple PNG Viewer for Windows"
55 #define VERSION   "1.20 of 28 May 2001"
56
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() */
63
64 /* #define DEBUG  :  this enables the Trace() macros */
65
66 #include "readpng.h"    /* typedefs, common macros, readpng prototypes */
67
68
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);") */
73
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);               \
78 }
79
80
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);
86
87
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;
94
95 static char *bgstr;
96 static uch bg_red=0, bg_green=0, bg_blue=0;
97
98 static double display_exponent;
99
100 static ulg image_width, image_height, image_rowbytes;
101 static int image_channels;
102 static uch *image_data;
103
104 /* Windows-specific variables */
105 static ulg wimage_rowbytes;
106 static uch *dib;
107 static uch *wimage_data;
108 static BITMAPINFOHEADER *bmih;
109
110 static HWND global_hwnd;
111
112
113
114
115 int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, PSTR cmd, int showmode)
116 {
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;
127
128
129     filename = (char *)NULL;
130
131
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. */
136
137     AllocConsole();
138     freopen("CONOUT$", "a", stderr);
139     freopen("CONOUT$", "a", stdout);
140
141
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... */
148
149 #if defined(NeXT)
150     LUT_exponent = 1.0 / 2.2;
151     /*
152     if (some_next_function_that_returns_gamma(&next_gamma))
153         LUT_exponent = 1.0 / next_gamma;
154      */
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;
162
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;
168     }
169 #elif defined(Macintosh)
170     LUT_exponent = 1.8 / 2.61;
171     /*
172     if (some_mac_function_that_returns_gamma(&mac_gamma))
173         LUT_exponent = mac_gamma / 2.61;
174      */
175 #else
176     LUT_exponent = 1.0;   /* assume no LUT:  most PCs */
177 #endif
178
179     /* the defaults above give 1.0, 1.3, 1.5 and 2.2, respectively: */
180     default_display_exponent = LUT_exponent * CRT_exponent;
181
182
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. */
187
188     if ((p = getenv("SCREEN_GAMMA")) != NULL)
189         display_exponent = atof(p);
190     else
191         display_exponent = default_display_exponent;
192
193
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! */
197
198     argv[argc++] = PROGNAME;
199     p = cmd;
200     for (;;) {
201         if (*p == ' ')
202             while (*++p == ' ')
203                 ;
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;
215     }
216     argv[argc] = NULL;   /* terminate the argv array itself */
217
218
219     /* Now parse the command line for options and the PNG filename. */
220
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;
229             }
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;
239             }
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 */
247         }
248     }
249
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;
275             }
276             ++error;
277         }
278         if (error)
279             fclose(infile);
280     }
281
282
283     /* usage screen */
284
285     if (error) {
286         int ch;
287
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);
302         do
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);
311     }
312
313
314     /* set the title-bar string, but make sure buffer doesn't overflow */
315
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);
322
323
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 */
327
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);
335     }
336
337
338     /* do the basic Windows initialization stuff, make the window and fill it
339      * with the background color */
340
341     if (rpng_win_create_window(hInst, showmode))
342         exit(2);
343
344
345     /* decode the image, all at once */
346
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"))
351
352
353     /* done with PNG file, so clean up to minimize memory usage (but do NOT
354      * nuke image_data!) */
355
356     readpng_cleanup(FALSE);
357     fclose(infile);
358
359     if (!image_data) {
360         fprintf(stderr, PROGNAME ":  unable to decode PNG image\n");
361         exit(3);
362     }
363
364
365     /* display image (composite with background if requested) */
366
367     Trace((stderr, "calling rpng_win_display_image()\n"))
368     if (rpng_win_display_image()) {
369         free(image_data);
370         exit(4);
371     }
372     Trace((stderr, "done with rpng_win_display_image()\n"))
373
374
375     /* wait for the user to tell us when to quit */
376
377     printf(
378       "Done.  Press Q, Esc or mouse button 1 (within image window) to quit.\n");
379     fflush(stdout);
380
381     while (GetMessage(&msg, NULL, 0, 0)) {
382         TranslateMessage(&msg);
383         DispatchMessage(&msg);
384     }
385
386
387     /* OK, we're done:  clean up all image and Windows resources and go away */
388
389     rpng_win_cleanup();
390
391     return msg.wParam;
392 }
393
394
395
396
397
398 static int rpng_win_create_window(HINSTANCE hInst, int showmode)
399 {
400     uch *dest;
401     int extra_width, extra_height;
402     ulg i, j;
403     WNDCLASSEX wndclass;
404
405
406 /*---------------------------------------------------------------------------
407     Allocate memory for the display-specific version of the image (round up
408     to multiple of 4 for Windows DIB).
409   ---------------------------------------------------------------------------*/
410
411     wimage_rowbytes = ((3*image_width + 3L) >> 2) << 2;
412
413     if (!(dib = (uch *)malloc(sizeof(BITMAPINFOHEADER) +
414                               wimage_rowbytes*image_height)))
415     {
416         return 4;   /* fail */
417     }
418
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   ---------------------------------------------------------------------------*/
425
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);
435
436 /*---------------------------------------------------------------------------
437     Fill in background color (black by default); data are in BGR order.
438   ---------------------------------------------------------------------------*/
439
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;
446         }
447     }
448
449 /*---------------------------------------------------------------------------
450     Set the window parameters.
451   ---------------------------------------------------------------------------*/
452
453     memset(&wndclass, 0, sizeof(wndclass));
454
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);
465
466     RegisterClassEx(&wndclass);
467
468 /*---------------------------------------------------------------------------
469     Finally, create the window.
470   ---------------------------------------------------------------------------*/
471
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);
477
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);
481
482     ShowWindow(global_hwnd, showmode);
483     UpdateWindow(global_hwnd);
484
485     return 0;
486
487 } /* end function rpng_win_create_window() */
488
489
490
491
492
493 static int rpng_win_display_image()
494 {
495     uch *src, *dest;
496     uch r, g, b, a;
497     ulg i, row, lastrow;
498     RECT rect;
499
500
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))
505
506
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   ---------------------------------------------------------------------------*/
512
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;
524             }
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);
547                 }
548             }
549         }
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;
559         }
560     }
561
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() */
570     }
571
572 /*
573     last param determines whether or not background is wiped before paint
574     InvalidateRect(global_hwnd, NULL, TRUE);
575     UpdateWindow(global_hwnd);
576  */
577
578     return 0;
579 }
580
581
582
583
584
585 static void rpng_win_cleanup()
586 {
587     if (image_data) {
588         free(image_data);
589         image_data = NULL;
590     }
591
592     if (dib) {
593         free(dib);
594         dib = NULL;
595     }
596 }
597
598
599
600
601
602 LRESULT CALLBACK rpng_win_wndproc(HWND hwnd, UINT iMsg, WPARAM wP, LPARAM lP)
603 {
604     HDC         hdc;
605     PAINTSTRUCT ps;
606     int rc;
607
608     switch (iMsg) {
609         case WM_CREATE:
610             /* one-time processing here, if any */
611             return 0;
612
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;
624
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);
632             }
633             return 0;
634
635         case WM_LBUTTONDOWN:   /* another way of quitting */
636         case WM_DESTROY:
637             PostQuitMessage(0);
638             return 0;
639     }
640
641     return DefWindowProc(hwnd, iMsg, wP, lP);
642 }