VirtualBox

source: vbox/trunk/src/libs/libpng-1.2.8/contrib/gregbook/rpng-win.c@ 11115

Last change on this file since 11115 was 6395, checked in by vboxsync, 17 years ago

export libpng and zlib so Windows and OS/2 build cleanly.

  • Property svn:eol-style set to native
File size: 21.0 KB
Line 
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 */
82static int rpng_win_create_window(HINSTANCE hInst, int showmode);
83static int rpng_win_display_image(void);
84static void rpng_win_cleanup(void);
85LRESULT CALLBACK rpng_win_wndproc(HWND, UINT, WPARAM, LPARAM);
86
87
88static char titlebar[1024], *window_name = titlebar;
89static char *progname = PROGNAME;
90static char *appname = LONGNAME;
91static char *icon_name = PROGNAME; /* GRR: not (yet) used */
92static char *filename;
93static FILE *infile;
94
95static char *bgstr;
96static uch bg_red=0, bg_green=0, bg_blue=0;
97
98static double display_exponent;
99
100static ulg image_width, image_height, image_rowbytes;
101static int image_channels;
102static uch *image_data;
103
104/* Windows-specific variables */
105static ulg wimage_rowbytes;
106static uch *dib;
107static uch *wimage_data;
108static BITMAPINFOHEADER *bmih;
109
110static HWND global_hwnd;
111
112
113
114
115int 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
398static 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
493static 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
585static 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
602LRESULT 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}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette