1 /*---------------------------------------------------------------------------
2
3 rpng - simple PNG display program rpng-x.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 the X Window System (tested by author under Unix and
9 by Martin Zinser under OpenVMS; may work under OS/2 with some tweaking).
10
11 to do:
12 - 8-bit (colormapped) X support
13 - use %.1023s to simplify truncation of title-bar string?
14
15 ---------------------------------------------------------------------------
16
17 Changelog:
18 - 1.01: initial public release
19 - 1.02: modified to allow abbreviated options; fixed long/ulong mis-
20 match; switched to png_jmpbuf() macro
21 - 1.10: added support for non-default visuals; fixed X pixel-conversion
22 - 1.11: added extra set of parentheses to png_jmpbuf() macro; fixed
23 command-line parsing bug
24 - 1.12: fixed some small X memory leaks (thanks to Fran�ois Petitjean)
25 - 1.13: fixed XFreeGC() crash bug (thanks to Patrick Welche)
26 - 1.14: added support for X resources (thanks to Gerhard Niklasch)
27 - 2.00: dual-licensed (added GNU GPL)
28 - 2.01: fixed improper display of usage screen on PNG error(s)
29
30 ---------------------------------------------------------------------------
31
32 Copyright (c) 1998-2008 Greg Roelofs. All rights reserved.
33
34 This software is provided "as is," without warranty of any kind,
35 express or implied. In no event shall the author or contributors
36 be held liable for any damages arising in any way from the use of
37 this software.
38
39 The contents of this file are DUAL-LICENSED. You may modify and/or
40 redistribute this software according to the terms of one of the
41 following two licenses (at your option):
42
43
44 LICENSE 1 ("BSD-like with advertising clause"):
45
46 Permission is granted to anyone to use this software for any purpose,
47 including commercial applications, and to alter it and redistribute
48 it freely, subject to the following restrictions:
49
50 1. Redistributions of source code must retain the above copyright
51 notice, disclaimer, and this list of conditions.
52 2. Redistributions in binary form must reproduce the above copyright
53 notice, disclaimer, and this list of conditions in the documenta-
54 tion and/or other materials provided with the distribution.
55 3. All advertising materials mentioning features or use of this
56 software must display the following acknowledgment:
57
58 This product includes software developed by Greg Roelofs
59 and contributors for the book, "PNG: The Definitive Guide,"
60 published by O'Reilly and Associates.
61
62
63 LICENSE 2 (GNU GPL v2 or later):
64
65 This program is free software; you can redistribute it and/or modify
66 it under the terms of the GNU General Public License as published by
67 the Free Software Foundation; either version 2 of the License, or
68 (at your option) any later version.
69
70 This program is distributed in the hope that it will be useful,
71 but WITHOUT ANY WARRANTY; without even the implied warranty of
72 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
73 GNU General Public License for more details.
74
75 You should have received a copy of the GNU General Public License
76 along with this program; if not, write to the Free Software Foundation,
77 Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
78
79 ---------------------------------------------------------------------------*/
80
81 #define PROGNAME "rpng-x"
82 #define LONGNAME "Simple PNG Viewer for X"
83 #define VERSION "2.01 of 16 March 2008"
84 #define RESNAME "rpng" /* our X resource application name */
85 #define RESCLASS "Rpng" /* our X resource class name */
86
87 #include <stdio.h>
88 #include <stdlib.h>
89 #include <string.h>
90 #include <time.h>
91 #include <X11/Xlib.h>
92 #include <X11/Xutil.h>
93 #include <X11/Xos.h>
94 #include <X11/keysym.h>
95
96 /* #define DEBUG : this enables the Trace() macros */
97
98 #include "readpng.h" /* typedefs, common macros, readpng prototypes */
99
100
101 /* could just include png.h, but this macro is the only thing we need
102 * (name and typedefs changed to local versions); note that side effects
103 * only happen with alpha (which could easily be avoided with
104 * "ush acopy = (alpha);") */
105
106 #define alpha_composite(composite, fg, alpha, bg) { \
107 ush temp = ((ush)(fg)*(ush)(alpha) + \
108 (ush)(bg)*(ush)(255 - (ush)(alpha)) + (ush)128); \
109 (composite) = (uch)((temp + (temp >> 8)) >> 8); \
110 }
111
112
113 /* local prototypes */
114 static int rpng_x_create_window(void);
115 static int rpng_x_display_image(void);
116 static void rpng_x_cleanup(void);
117 static int rpng_x_msb(ulg u32val);
118
119
120 static char titlebar[1024], *window_name = titlebar;
121 static char *appname = LONGNAME;
122 static char *icon_name = PROGNAME;
123 static char *res_name = RESNAME;
124 static char *res_class = RESCLASS;
125 static char *filename;
126 static FILE *infile;
127
128 static char *bgstr;
129 static uch bg_red=0, bg_green=0, bg_blue=0;
130
131 static double display_exponent;
132
133 static ulg image_width, image_height, image_rowbytes;
134 static int image_channels;
135 static uch *image_data;
136
137 /* X-specific variables */
138 static char *displayname;
139 static XImage *ximage;
140 static Display *display;
141 static int depth;
142 static Visual *visual;
143 static XVisualInfo *visual_list;
144 static int RShift, GShift, BShift;
145 static ulg RMask, GMask, BMask;
146 static Window window;
147 static GC gc;
148 static Colormap colormap;
149
150 static int have_nondefault_visual = FALSE;
151 static int have_colormap = FALSE;
152 static int have_window = FALSE;
153 static int have_gc = FALSE;
154 /*
155 ulg numcolors=0, pixels[256];
156 ush reds[256], greens[256], blues[256];
157 */
158
159
160
161
main(int argc,char ** argv)162 int main(int argc, char **argv)
163 {
164 #ifdef sgi
165 char tmpline[80];
166 #endif
167 char *p;
168 int rc, alen, flen;
169 int error = 0;
170 int have_bg = FALSE;
171 double LUT_exponent; /* just the lookup table */
172 double CRT_exponent = 2.2; /* just the monitor */
173 double default_display_exponent; /* whole display system */
174 XEvent e;
175 KeySym k;
176
177
178 displayname = (char *)NULL;
179 filename = (char *)NULL;
180
181
182 /* First set the default value for our display-system exponent, i.e.,
183 * the product of the CRT exponent and the exponent corresponding to
184 * the frame-buffer's lookup table (LUT), if any. This is not an
185 * exhaustive list of LUT values (e.g., OpenStep has a lot of weird
186 * ones), but it should cover 99% of the current possibilities. */
187
188 #if defined(NeXT)
189 LUT_exponent = 1.0 / 2.2;
190 /*
191 if (some_next_function_that_returns_gamma(&next_gamma))
192 LUT_exponent = 1.0 / next_gamma;
193 */
194 #elif defined(sgi)
195 LUT_exponent = 1.0 / 1.7;
196 /* there doesn't seem to be any documented function to get the
197 * "gamma" value, so we do it the hard way */
198 infile = fopen("/etc/config/system.glGammaVal", "r");
199 if (infile) {
200 double sgi_gamma;
201
202 fgets(tmpline, 80, infile);
203 fclose(infile);
204 sgi_gamma = atof(tmpline);
205 if (sgi_gamma > 0.0)
206 LUT_exponent = 1.0 / sgi_gamma;
207 }
208 #elif defined(Macintosh)
209 LUT_exponent = 1.8 / 2.61;
210 /*
211 if (some_mac_function_that_returns_gamma(&mac_gamma))
212 LUT_exponent = mac_gamma / 2.61;
213 */
214 #else
215 LUT_exponent = 1.0; /* assume no LUT: most PCs */
216 #endif
217
218 /* the defaults above give 1.0, 1.3, 1.5 and 2.2, respectively: */
219 default_display_exponent = LUT_exponent * CRT_exponent;
220
221
222 /* If the user has set the SCREEN_GAMMA environment variable as suggested
223 * (somewhat imprecisely) in the libpng documentation, use that; otherwise
224 * use the default value we just calculated. Either way, the user may
225 * override this via a command-line option. */
226
227 if ((p = getenv("SCREEN_GAMMA")) != NULL)
228 display_exponent = atof(p);
229 else
230 display_exponent = default_display_exponent;
231
232
233 /* Now parse the command line for options and the PNG filename. */
234
235 while (*++argv && !error) {
236 if (!strncmp(*argv, "-display", 2)) {
237 if (!*++argv)
238 ++error;
239 else
240 displayname = *argv;
241 } else if (!strncmp(*argv, "-gamma", 2)) {
242 if (!*++argv)
243 ++error;
244 else {
245 display_exponent = atof(*argv);
246 if (display_exponent <= 0.0)
247 ++error;
248 }
249 } else if (!strncmp(*argv, "-bgcolor", 2)) {
250 if (!*++argv)
251 ++error;
252 else {
253 bgstr = *argv;
254 if (strlen(bgstr) != 7 || bgstr[0] != '#')
255 ++error;
256 else
257 have_bg = TRUE;
258 }
259 } else {
260 if (**argv != '-') {
261 filename = *argv;
262 if (argv[1]) /* shouldn't be any more args after filename */
263 ++error;
264 } else
265 ++error; /* not expecting any other options */
266 }
267 }
268
269 if (!filename)
270 ++error;
271
272
273 /* print usage screen if any errors up to this point */
274
275 if (error) {
276 fprintf(stderr, "\n%s %s: %s\n", PROGNAME, VERSION, appname);
277 readpng_version_info();
278 fprintf(stderr, "\n"
279 "Usage: %s [-display xdpy] [-gamma exp] [-bgcolor bg] file.png\n"
280 " xdpy\tname of the target X display (e.g., ``hostname:0'')\n"
281 " exp \ttransfer-function exponent (``gamma'') of the display\n"
282 "\t\t system in floating-point format (e.g., ``%.1f''); equal\n"
283 "\t\t to the product of the lookup-table exponent (varies)\n"
284 "\t\t and the CRT exponent (usually 2.2); must be positive\n"
285 " bg \tdesired background color in 7-character hex RGB format\n"
286 "\t\t (e.g., ``#ff7700'' for orange: same as HTML colors);\n"
287 "\t\t used with transparent images\n"
288 "\nPress Q, Esc or mouse button 1 (within image window, after image\n"
289 "is displayed) to quit.\n"
290 "\n", PROGNAME, default_display_exponent);
291 exit(1);
292 }
293
294
295 if (!(infile = fopen(filename, "rb"))) {
296 fprintf(stderr, PROGNAME ": can't open PNG file [%s]\n", filename);
297 ++error;
298 } else {
299 if ((rc = readpng_init(infile, &image_width, &image_height)) != 0) {
300 switch (rc) {
301 case 1:
302 fprintf(stderr, PROGNAME
303 ": [%s] is not a PNG file: incorrect signature\n",
304 filename);
305 break;
306 case 2:
307 fprintf(stderr, PROGNAME
308 ": [%s] has bad IHDR (libpng longjmp)\n", filename);
309 break;
310 case 4:
311 fprintf(stderr, PROGNAME ": insufficient memory\n");
312 break;
313 default:
314 fprintf(stderr, PROGNAME
315 ": unknown readpng_init() error\n");
316 break;
317 }
318 ++error;
319 } else {
320 display = XOpenDisplay(displayname);
321 if (!display) {
322 readpng_cleanup(TRUE);
323 fprintf(stderr, PROGNAME ": can't open X display [%s]\n",
324 displayname? displayname : "default");
325 ++error;
326 }
327 }
328 if (error)
329 fclose(infile);
330 }
331
332
333 if (error) {
334 fprintf(stderr, PROGNAME ": aborting.\n");
335 exit(2);
336 }
337
338
339 /* set the title-bar string, but make sure buffer doesn't overflow */
340
341 alen = strlen(appname);
342 flen = strlen(filename);
343 if (alen + flen + 3 > 1023)
344 sprintf(titlebar, "%s: ...%s", appname, filename+(alen+flen+6-1023));
345 else
346 sprintf(titlebar, "%s: %s", appname, filename);
347
348
349 /* if the user didn't specify a background color on the command line,
350 * check for one in the PNG file--if not, the initialized values of 0
351 * (black) will be used */
352
353 if (have_bg) {
354 unsigned r, g, b; /* this approach quiets compiler warnings */
355
356 sscanf(bgstr+1, "%2x%2x%2x", &r, &g, &b);
357 bg_red = (uch)r;
358 bg_green = (uch)g;
359 bg_blue = (uch)b;
360 } else if (readpng_get_bgcolor(&bg_red, &bg_green, &bg_blue) > 1) {
361 readpng_cleanup(TRUE);
362 fprintf(stderr, PROGNAME
363 ": libpng error while checking for background color\n");
364 exit(2);
365 }
366
367
368 /* do the basic X initialization stuff, make the window and fill it
369 * with the background color */
370
371 if (rpng_x_create_window())
372 exit(2);
373
374
375 /* decode the image, all at once */
376
377 Trace((stderr, "calling readpng_get_image()\n"))
378 image_data = readpng_get_image(display_exponent, &image_channels,
379 &image_rowbytes);
380 Trace((stderr, "done with readpng_get_image()\n"))
381
382
383 /* done with PNG file, so clean up to minimize memory usage (but do NOT
384 * nuke image_data!) */
385
386 readpng_cleanup(FALSE);
387 fclose(infile);
388
389 if (!image_data) {
390 fprintf(stderr, PROGNAME ": unable to decode PNG image\n");
391 exit(3);
392 }
393
394
395 /* display image (composite with background if requested) */
396
397 Trace((stderr, "calling rpng_x_display_image()\n"))
398 if (rpng_x_display_image()) {
399 free(image_data);
400 exit(4);
401 }
402 Trace((stderr, "done with rpng_x_display_image()\n"))
403
404
405 /* wait for the user to tell us when to quit */
406
407 printf(
408 "Done. Press Q, Esc or mouse button 1 (within image window) to quit.\n");
409 fflush(stdout);
410
411 do
412 XNextEvent(display, &e);
413 while (!(e.type == ButtonPress && e.xbutton.button == Button1) &&
414 !(e.type == KeyPress && /* v--- or 1 for shifted keys */
415 ((k = XLookupKeysym(&e.xkey, 0)) == XK_q || k == XK_Escape) ));
416
417
418 /* OK, we're done: clean up all image and X resources and go away */
419
420 rpng_x_cleanup();
421
422 return 0;
423 }
424
425
426
427
428
rpng_x_create_window(void)429 static int rpng_x_create_window(void)
430 {
431 uch *xdata;
432 int need_colormap = FALSE;
433 int screen, pad;
434 ulg bg_pixel = 0L;
435 ulg attrmask;
436 Window root;
437 XEvent e;
438 XGCValues gcvalues;
439 XSetWindowAttributes attr;
440 XTextProperty windowName, *pWindowName = &windowName;
441 XTextProperty iconName, *pIconName = &iconName;
442 XVisualInfo visual_info;
443 XSizeHints *size_hints;
444 XWMHints *wm_hints;
445 XClassHint *class_hints;
446
447
448 screen = DefaultScreen(display);
449 depth = DisplayPlanes(display, screen);
450 root = RootWindow(display, screen);
451
452 #ifdef DEBUG
453 XSynchronize(display, True);
454 #endif
455
456 #if 0
457 /* GRR: add 8-bit support */
458 if (/* depth != 8 && */ depth != 16 && depth != 24 && depth != 32) {
459 fprintf(stderr,
460 "screen depth %d not supported (only 16-, 24- or 32-bit TrueColor)\n",
461 depth);
462 return 2;
463 }
464
465 XMatchVisualInfo(display, screen, depth,
466 (depth == 8)? PseudoColor : TrueColor, &visual_info);
467 visual = visual_info.visual;
468 #else
469 if (depth != 16 && depth != 24 && depth != 32) {
470 int visuals_matched = 0;
471
472 Trace((stderr, "default depth is %d: checking other visuals\n",
473 depth))
474
475 /* 24-bit first */
476 visual_info.screen = screen;
477 visual_info.depth = 24;
478 visual_list = XGetVisualInfo(display,
479 VisualScreenMask | VisualDepthMask, &visual_info, &visuals_matched);
480 if (visuals_matched == 0) {
481 /* GRR: add 15-, 16- and 32-bit TrueColor visuals (also DirectColor?) */
482 fprintf(stderr, "default screen depth %d not supported, and no"
483 " 24-bit visuals found\n", depth);
484 return 2;
485 }
486 Trace((stderr, "XGetVisualInfo() returned %d 24-bit visuals\n",
487 visuals_matched))
488 visual = visual_list[0].visual;
489 depth = visual_list[0].depth;
490 /*
491 colormap_size = visual_list[0].colormap_size;
492 visual_class = visual->class;
493 visualID = XVisualIDFromVisual(visual);
494 */
495 have_nondefault_visual = TRUE;
496 need_colormap = TRUE;
497 } else {
498 XMatchVisualInfo(display, screen, depth, TrueColor, &visual_info);
499 visual = visual_info.visual;
500 }
501 #endif
502
503 RMask = visual->red_mask;
504 GMask = visual->green_mask;
505 BMask = visual->blue_mask;
506
507 /* GRR: add/check 8-bit support */
508 if (depth == 8 || need_colormap) {
509 colormap = XCreateColormap(display, root, visual, AllocNone);
510 if (!colormap) {
511 fprintf(stderr, "XCreateColormap() failed\n");
512 return 2;
513 }
514 have_colormap = TRUE;
515 }
516 if (depth == 15 || depth == 16) {
517 RShift = 15 - rpng_x_msb(RMask); /* these are right-shifts */
518 GShift = 15 - rpng_x_msb(GMask);
519 BShift = 15 - rpng_x_msb(BMask);
520 } else if (depth > 16) {
521 #define NO_24BIT_MASKS
522 #ifdef NO_24BIT_MASKS
523 RShift = rpng_x_msb(RMask) - 7; /* these are left-shifts */
524 GShift = rpng_x_msb(GMask) - 7;
525 BShift = rpng_x_msb(BMask) - 7;
526 #else
527 RShift = 7 - rpng_x_msb(RMask); /* these are right-shifts, too */
528 GShift = 7 - rpng_x_msb(GMask);
529 BShift = 7 - rpng_x_msb(BMask);
530 #endif
531 }
532 if (depth >= 15 && (RShift < 0 || GShift < 0 || BShift < 0)) {
533 fprintf(stderr, "rpng internal logic error: negative X shift(s)!\n");
534 return 2;
535 }
536
537 /*---------------------------------------------------------------------------
538 Finally, create the window.
539 ---------------------------------------------------------------------------*/
540
541 attr.backing_store = Always;
542 attr.event_mask = ExposureMask | KeyPressMask | ButtonPressMask;
543 attrmask = CWBackingStore | CWEventMask;
544 if (have_nondefault_visual) {
545 attr.colormap = colormap;
546 attr.background_pixel = 0;
547 attr.border_pixel = 1;
548 attrmask |= CWColormap | CWBackPixel | CWBorderPixel;
549 }
550
551 window = XCreateWindow(display, root, 0, 0, image_width, image_height, 0,
552 depth, InputOutput, visual, attrmask, &attr);
553
554 if (window == None) {
555 fprintf(stderr, "XCreateWindow() failed\n");
556 return 2;
557 } else
558 have_window = TRUE;
559
560 if (depth == 8)
561 XSetWindowColormap(display, window, colormap);
562
563 if (!XStringListToTextProperty(&window_name, 1, pWindowName))
564 pWindowName = NULL;
565 if (!XStringListToTextProperty(&icon_name, 1, pIconName))
566 pIconName = NULL;
567
568 /* OK if any hints allocation fails; XSetWMProperties() allows NULLs */
569
570 if ((size_hints = XAllocSizeHints()) != NULL) {
571 /* window will not be resizable */
572 size_hints->flags = PMinSize | PMaxSize;
573 size_hints->min_width = size_hints->max_width = (int)image_width;
574 size_hints->min_height = size_hints->max_height = (int)image_height;
575 }
576
577 if ((wm_hints = XAllocWMHints()) != NULL) {
578 wm_hints->initial_state = NormalState;
579 wm_hints->input = True;
580 /* wm_hints->icon_pixmap = icon_pixmap; */
581 wm_hints->flags = StateHint | InputHint /* | IconPixmapHint */ ;
582 }
583
584 if ((class_hints = XAllocClassHint()) != NULL) {
585 class_hints->res_name = res_name;
586 class_hints->res_class = res_class;
587 }
588
589 XSetWMProperties(display, window, pWindowName, pIconName, NULL, 0,
590 size_hints, wm_hints, class_hints);
591
592 /* various properties and hints no longer needed; free memory */
593 if (pWindowName)
594 XFree(pWindowName->value);
595 if (pIconName)
596 XFree(pIconName->value);
597 if (size_hints)
598 XFree(size_hints);
599 if (wm_hints)
600 XFree(wm_hints);
601 if (class_hints)
602 XFree(class_hints);
603
604 XMapWindow(display, window);
605
606 gc = XCreateGC(display, window, 0, &gcvalues);
607 have_gc = TRUE;
608
609 /*---------------------------------------------------------------------------
610 Fill window with the specified background color.
611 ---------------------------------------------------------------------------*/
612
613 if (depth == 24 || depth == 32) {
614 bg_pixel = ((ulg)bg_red << RShift) |
615 ((ulg)bg_green << GShift) |
616 ((ulg)bg_blue << BShift);
617 } else if (depth == 16) {
618 bg_pixel = ((((ulg)bg_red << 8) >> RShift) & RMask) |
619 ((((ulg)bg_green << 8) >> GShift) & GMask) |
620 ((((ulg)bg_blue << 8) >> BShift) & BMask);
621 } else /* depth == 8 */ {
622
623 /* GRR: add 8-bit support */
624
625 }
626
627 XSetForeground(display, gc, bg_pixel);
628 XFillRectangle(display, window, gc, 0, 0, image_width, image_height);
629
630 /*---------------------------------------------------------------------------
631 Wait for first Expose event to do any drawing, then flush.
632 ---------------------------------------------------------------------------*/
633
634 do
635 XNextEvent(display, &e);
636 while (e.type != Expose || e.xexpose.count);
637
638 XFlush(display);
639
640 /*---------------------------------------------------------------------------
641 Allocate memory for the X- and display-specific version of the image.
642 ---------------------------------------------------------------------------*/
643
644 if (depth == 24 || depth == 32) {
645 xdata = (uch *)malloc(4*image_width*image_height);
646 pad = 32;
647 } else if (depth == 16) {
648 xdata = (uch *)malloc(2*image_width*image_height);
649 pad = 16;
650 } else /* depth == 8 */ {
651 xdata = (uch *)malloc(image_width*image_height);
652 pad = 8;
653 }
654
655 if (!xdata) {
656 fprintf(stderr, PROGNAME ": unable to allocate image memory\n");
657 return 4;
658 }
659
660 ximage = XCreateImage(display, visual, depth, ZPixmap, 0,
661 (char *)xdata, image_width, image_height, pad, 0);
662
663 if (!ximage) {
664 fprintf(stderr, PROGNAME ": XCreateImage() failed\n");
665 free(xdata);
666 return 3;
667 }
668
669 /* to avoid testing the byte order every pixel (or doubling the size of
670 * the drawing routine with a giant if-test), we arbitrarily set the byte
671 * order to MSBFirst and let Xlib worry about inverting things on little-
672 * endian machines (like Linux/x86, old VAXen, etc.)--this is not the most
673 * efficient approach (the giant if-test would be better), but in the
674 * interest of clarity, we take the easy way out... */
675
676 ximage->byte_order = MSBFirst;
677
678 return 0;
679
680 } /* end function rpng_x_create_window() */
681
682
683
684
685
rpng_x_display_image(void)686 static int rpng_x_display_image(void)
687 {
688 uch *src;
689 char *dest;
690 uch r, g, b, a;
691 ulg i, row, lastrow = 0;
692 ulg pixel;
693 int ximage_rowbytes = ximage->bytes_per_line;
694 /* int bpp = ximage->bits_per_pixel; */
695
696
697 Trace((stderr, "beginning display loop (image_channels == %d)\n",
698 image_channels))
699 Trace((stderr, " (width = %ld, rowbytes = %ld, ximage_rowbytes = %d)\n",
700 image_width, image_rowbytes, ximage_rowbytes))
701 Trace((stderr, " (bpp = %d)\n", ximage->bits_per_pixel))
702 Trace((stderr, " (byte_order = %s)\n", ximage->byte_order == MSBFirst?
703 "MSBFirst" : (ximage->byte_order == LSBFirst? "LSBFirst" : "unknown")))
704
705 if (depth == 24 || depth == 32) {
706 ulg red, green, blue;
707
708 for (lastrow = row = 0; row < image_height; ++row) {
709 src = image_data + row*image_rowbytes;
710 dest = ximage->data + row*ximage_rowbytes;
711 if (image_channels == 3) {
712 for (i = image_width; i > 0; --i) {
713 red = *src++;
714 green = *src++;
715 blue = *src++;
716 #ifdef NO_24BIT_MASKS
717 pixel = (red << RShift) |
718 (green << GShift) |
719 (blue << BShift);
720 /* recall that we set ximage->byte_order = MSBFirst above */
721 /* GRR BUG: this assumes bpp == 32, but may be 24: */
722 *dest++ = (char)((pixel >> 24) & 0xff);
723 *dest++ = (char)((pixel >> 16) & 0xff);
724 *dest++ = (char)((pixel >> 8) & 0xff);
725 *dest++ = (char)( pixel & 0xff);
726 #else
727 red = (RShift < 0)? red << (-RShift) : red >> RShift;
728 green = (GShift < 0)? green << (-GShift) : green >> GShift;
729 blue = (BShift < 0)? blue << (-BShift) : blue >> BShift;
730 pixel = (red & RMask) | (green & GMask) | (blue & BMask);
731 /* recall that we set ximage->byte_order = MSBFirst above */
732 *dest++ = (char)((pixel >> 24) & 0xff);
733 *dest++ = (char)((pixel >> 16) & 0xff);
734 *dest++ = (char)((pixel >> 8) & 0xff);
735 *dest++ = (char)( pixel & 0xff);
736 #endif
737 }
738 } else /* if (image_channels == 4) */ {
739 for (i = image_width; i > 0; --i) {
740 r = *src++;
741 g = *src++;
742 b = *src++;
743 a = *src++;
744 if (a == 255) {
745 red = r;
746 green = g;
747 blue = b;
748 } else if (a == 0) {
749 red = bg_red;
750 green = bg_green;
751 blue = bg_blue;
752 } else {
753 /* this macro (from png.h) composites the foreground
754 * and background values and puts the result into the
755 * first argument */
756 alpha_composite(red, r, a, bg_red);
757 alpha_composite(green, g, a, bg_green);
758 alpha_composite(blue, b, a, bg_blue);
759 }
760 pixel = (red << RShift) |
761 (green << GShift) |
762 (blue << BShift);
763 /* recall that we set ximage->byte_order = MSBFirst above */
764 *dest++ = (char)((pixel >> 24) & 0xff);
765 *dest++ = (char)((pixel >> 16) & 0xff);
766 *dest++ = (char)((pixel >> 8) & 0xff);
767 *dest++ = (char)( pixel & 0xff);
768 }
769 }
770 /* display after every 16 lines */
771 if (((row+1) & 0xf) == 0) {
772 XPutImage(display, window, gc, ximage, 0, (int)lastrow, 0,
773 (int)lastrow, image_width, 16);
774 XFlush(display);
775 lastrow = row + 1;
776 }
777 }
778
779 } else if (depth == 16) {
780 ush red, green, blue;
781
782 for (lastrow = row = 0; row < image_height; ++row) {
783 src = image_data + row*image_rowbytes;
784 dest = ximage->data + row*ximage_rowbytes;
785 if (image_channels == 3) {
786 for (i = image_width; i > 0; --i) {
787 red = ((ush)(*src) << 8);
788 ++src;
789 green = ((ush)(*src) << 8);
790 ++src;
791 blue = ((ush)(*src) << 8);
792 ++src;
793 pixel = ((red >> RShift) & RMask) |
794 ((green >> GShift) & GMask) |
795 ((blue >> BShift) & BMask);
796 /* recall that we set ximage->byte_order = MSBFirst above */
797 *dest++ = (char)((pixel >> 8) & 0xff);
798 *dest++ = (char)( pixel & 0xff);
799 }
800 } else /* if (image_channels == 4) */ {
801 for (i = image_width; i > 0; --i) {
802 r = *src++;
803 g = *src++;
804 b = *src++;
805 a = *src++;
806 if (a == 255) {
807 red = ((ush)r << 8);
808 green = ((ush)g << 8);
809 blue = ((ush)b << 8);
810 } else if (a == 0) {
811 red = ((ush)bg_red << 8);
812 green = ((ush)bg_green << 8);
813 blue = ((ush)bg_blue << 8);
814 } else {
815 /* this macro (from png.h) composites the foreground
816 * and background values and puts the result back into
817 * the first argument (== fg byte here: safe) */
818 alpha_composite(r, r, a, bg_red);
819 alpha_composite(g, g, a, bg_green);
820 alpha_composite(b, b, a, bg_blue);
821 red = ((ush)r << 8);
822 green = ((ush)g << 8);
823 blue = ((ush)b << 8);
824 }
825 pixel = ((red >> RShift) & RMask) |
826 ((green >> GShift) & GMask) |
827 ((blue >> BShift) & BMask);
828 /* recall that we set ximage->byte_order = MSBFirst above */
829 *dest++ = (char)((pixel >> 8) & 0xff);
830 *dest++ = (char)( pixel & 0xff);
831 }
832 }
833 /* display after every 16 lines */
834 if (((row+1) & 0xf) == 0) {
835 XPutImage(display, window, gc, ximage, 0, (int)lastrow, 0,
836 (int)lastrow, image_width, 16);
837 XFlush(display);
838 lastrow = row + 1;
839 }
840 }
841
842 } else /* depth == 8 */ {
843
844 /* GRR: add 8-bit support */
845
846 }
847
848 Trace((stderr, "calling final XPutImage()\n"))
849 if (lastrow < image_height) {
850 XPutImage(display, window, gc, ximage, 0, (int)lastrow, 0,
851 (int)lastrow, image_width, image_height-lastrow);
852 XFlush(display);
853 }
854
855 return 0;
856 }
857
858
859
860
rpng_x_cleanup(void)861 static void rpng_x_cleanup(void)
862 {
863 if (image_data) {
864 free(image_data);
865 image_data = NULL;
866 }
867
868 if (ximage) {
869 if (ximage->data) {
870 free(ximage->data); /* we allocated it, so we free it */
871 ximage->data = (char *)NULL; /* instead of XDestroyImage() */
872 }
873 XDestroyImage(ximage);
874 ximage = NULL;
875 }
876
877 if (have_gc)
878 XFreeGC(display, gc);
879
880 if (have_window)
881 XDestroyWindow(display, window);
882
883 if (have_colormap)
884 XFreeColormap(display, colormap);
885
886 if (have_nondefault_visual)
887 XFree(visual_list);
888 }
889
890
891
892
893
rpng_x_msb(ulg u32val)894 static int rpng_x_msb(ulg u32val)
895 {
896 int i;
897
898 for (i = 31; i >= 0; --i) {
899 if (u32val & 0x80000000L)
900 break;
901 u32val <<= 1;
902 }
903 return i;
904 }
905