1 /*---------------------------------------------------------------------------
3 rpng - simple PNG display program rpng-x.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 the X Window System (tested by author under Unix and
9 by Martin Zinser under OpenVMS; may work under OS/2 with some tweaking).
12 - 8-bit (colormapped) X support
13 - use %.1023s to simplify truncation of title-bar string?
15 ---------------------------------------------------------------------------
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)
30 ---------------------------------------------------------------------------
32 Copyright (c) 1998-2008 Greg Roelofs. All rights reserved.
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
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):
44 LICENSE 1 ("BSD-like with advertising clause"):
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:
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:
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.
63 LICENSE 2 (GNU GPL v2 or later):
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.
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.
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
79 ---------------------------------------------------------------------------*/
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 */
92 #include <X11/Xutil.h>
94 #include <X11/keysym.h>
96 /* #define DEBUG : this enables the Trace() macros */
98 #include "readpng.h" /* typedefs, common macros, readpng prototypes */
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);") */
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); \
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
);
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
;
129 static uch bg_red
=0, bg_green
=0, bg_blue
=0;
131 static double display_exponent
;
133 static ulg image_width
, image_height
, image_rowbytes
;
134 static int image_channels
;
135 static uch
*image_data
;
137 /* X-specific variables */
138 static char *displayname
;
139 static XImage
*ximage
;
140 static Display
*display
;
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
;
148 static Colormap colormap
;
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
;
155 ulg numcolors=0, pixels[256];
156 ush reds[256], greens[256], blues[256];
162 int main(int argc
, char **argv
)
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 */
178 displayname
= (char *)NULL
;
179 filename
= (char *)NULL
;
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. */
189 LUT_exponent
= 1.0 / 2.2;
191 if (some_next_function_that_returns_gamma(&next_gamma))
192 LUT_exponent = 1.0 / next_gamma;
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");
202 fgets(tmpline
, 80, infile
);
204 sgi_gamma
= atof(tmpline
);
206 LUT_exponent
= 1.0 / sgi_gamma
;
208 #elif defined(Macintosh)
209 LUT_exponent
= 1.8 / 2.61;
211 if (some_mac_function_that_returns_gamma(&mac_gamma))
212 LUT_exponent = mac_gamma / 2.61;
215 LUT_exponent
= 1.0; /* assume no LUT: most PCs */
218 /* the defaults above give 1.0, 1.3, 1.5 and 2.2, respectively: */
219 default_display_exponent
= LUT_exponent
* CRT_exponent
;
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. */
227 if ((p
= getenv("SCREEN_GAMMA")) != NULL
)
228 display_exponent
= atof(p
);
230 display_exponent
= default_display_exponent
;
233 /* Now parse the command line for options and the PNG filename. */
235 while (*++argv
&& !error
) {
236 if (!strncmp(*argv
, "-display", 2)) {
241 } else if (!strncmp(*argv
, "-gamma", 2)) {
245 display_exponent
= atof(*argv
);
246 if (display_exponent
<= 0.0)
249 } else if (!strncmp(*argv
, "-bgcolor", 2)) {
254 if (strlen(bgstr
) != 7 || bgstr
[0] != '#')
262 if (argv
[1]) /* shouldn't be any more args after filename */
265 ++error
; /* not expecting any other options */
273 /* print usage screen if any errors up to this point */
276 fprintf(stderr
, "\n%s %s: %s\n", PROGNAME
, VERSION
, appname
);
277 readpng_version_info();
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
);
295 if (!(infile
= fopen(filename
, "rb"))) {
296 fprintf(stderr
, PROGNAME
": can't open PNG file [%s]\n", filename
);
299 if ((rc
= readpng_init(infile
, &image_width
, &image_height
)) != 0) {
302 fprintf(stderr
, PROGNAME
303 ": [%s] is not a PNG file: incorrect signature\n",
307 fprintf(stderr
, PROGNAME
308 ": [%s] has bad IHDR (libpng longjmp)\n", filename
);
311 fprintf(stderr
, PROGNAME
": insufficient memory\n");
314 fprintf(stderr
, PROGNAME
315 ": unknown readpng_init() error\n");
320 display
= XOpenDisplay(displayname
);
322 readpng_cleanup(TRUE
);
323 fprintf(stderr
, PROGNAME
": can't open X display [%s]\n",
324 displayname
? displayname
: "default");
334 fprintf(stderr
, PROGNAME
": aborting.\n");
339 /* set the title-bar string, but make sure buffer doesn't overflow */
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));
346 sprintf(titlebar
, "%s: %s", appname
, filename
);
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 */
354 unsigned r
, g
, b
; /* this approach quiets compiler warnings */
356 sscanf(bgstr
+1, "%2x%2x%2x", &r
, &g
, &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");
368 /* do the basic X initialization stuff, make the window and fill it
369 * with the background color */
371 if (rpng_x_create_window())
375 /* decode the image, all at once */
377 Trace((stderr
, "calling readpng_get_image()\n"))
378 image_data
= readpng_get_image(display_exponent
, &image_channels
,
380 Trace((stderr
, "done with readpng_get_image()\n"))
383 /* done with PNG file, so clean up to minimize memory usage (but do NOT
384 * nuke image_data!) */
386 readpng_cleanup(FALSE
);
390 fprintf(stderr
, PROGNAME
": unable to decode PNG image\n");
395 /* display image (composite with background if requested) */
397 Trace((stderr
, "calling rpng_x_display_image()\n"))
398 if (rpng_x_display_image()) {
402 Trace((stderr
, "done with rpng_x_display_image()\n"))
405 /* wait for the user to tell us when to quit */
408 "Done. Press Q, Esc or mouse button 1 (within image window) to quit.\n");
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
) ));
418 /* OK, we're done: clean up all image and X resources and go away */
429 static int rpng_x_create_window(void)
432 int need_colormap
= FALSE
;
439 XSetWindowAttributes attr
;
440 XTextProperty windowName
, *pWindowName
= &windowName
;
441 XTextProperty iconName
, *pIconName
= &iconName
;
442 XVisualInfo visual_info
;
443 XSizeHints
*size_hints
;
445 XClassHint
*class_hints
;
448 screen
= DefaultScreen(display
);
449 depth
= DisplayPlanes(display
, screen
);
450 root
= RootWindow(display
, screen
);
453 XSynchronize(display
, True
);
457 /* GRR: add 8-bit support */
458 if (/* depth != 8 && */ depth
!= 16 && depth
!= 24 && depth
!= 32) {
460 "screen depth %d not supported (only 16-, 24- or 32-bit TrueColor)\n",
465 XMatchVisualInfo(display
, screen
, depth
,
466 (depth
== 8)? PseudoColor
: TrueColor
, &visual_info
);
467 visual
= visual_info
.visual
;
469 if (depth
!= 16 && depth
!= 24 && depth
!= 32) {
470 int visuals_matched
= 0;
472 Trace((stderr
, "default depth is %d: checking other visuals\n",
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
);
486 Trace((stderr
, "XGetVisualInfo() returned %d 24-bit visuals\n",
488 visual
= visual_list
[0].visual
;
489 depth
= visual_list
[0].depth
;
491 colormap_size = visual_list[0].colormap_size;
492 visual_class = visual->class;
493 visualID = XVisualIDFromVisual(visual);
495 have_nondefault_visual
= TRUE
;
496 need_colormap
= TRUE
;
498 XMatchVisualInfo(display
, screen
, depth
, TrueColor
, &visual_info
);
499 visual
= visual_info
.visual
;
503 RMask
= visual
->red_mask
;
504 GMask
= visual
->green_mask
;
505 BMask
= visual
->blue_mask
;
507 /* GRR: add/check 8-bit support */
508 if (depth
== 8 || need_colormap
) {
509 colormap
= XCreateColormap(display
, root
, visual
, AllocNone
);
511 fprintf(stderr
, "XCreateColormap() failed\n");
514 have_colormap
= TRUE
;
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;
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
);
532 if (depth
>= 15 && (RShift
< 0 || GShift
< 0 || BShift
< 0)) {
533 fprintf(stderr
, "rpng internal logic error: negative X shift(s)!\n");
537 /*---------------------------------------------------------------------------
538 Finally, create the window.
539 ---------------------------------------------------------------------------*/
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
;
551 window
= XCreateWindow(display
, root
, 0, 0, image_width
, image_height
, 0,
552 depth
, InputOutput
, visual
, attrmask
, &attr
);
554 if (window
== None
) {
555 fprintf(stderr
, "XCreateWindow() failed\n");
561 XSetWindowColormap(display
, window
, colormap
);
563 if (!XStringListToTextProperty(&window_name
, 1, pWindowName
))
565 if (!XStringListToTextProperty(&icon_name
, 1, pIconName
))
568 /* OK if any hints allocation fails; XSetWMProperties() allows NULLs */
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
;
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 */ ;
584 if ((class_hints
= XAllocClassHint()) != NULL
) {
585 class_hints
->res_name
= res_name
;
586 class_hints
->res_class
= res_class
;
589 XSetWMProperties(display
, window
, pWindowName
, pIconName
, NULL
, 0,
590 size_hints
, wm_hints
, class_hints
);
592 /* various properties and hints no longer needed; free memory */
594 XFree(pWindowName
->value
);
596 XFree(pIconName
->value
);
604 XMapWindow(display
, window
);
606 gc
= XCreateGC(display
, window
, 0, &gcvalues
);
609 /*---------------------------------------------------------------------------
610 Fill window with the specified background color.
611 ---------------------------------------------------------------------------*/
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 */ {
623 /* GRR: add 8-bit support */
627 XSetForeground(display
, gc
, bg_pixel
);
628 XFillRectangle(display
, window
, gc
, 0, 0, image_width
, image_height
);
630 /*---------------------------------------------------------------------------
631 Wait for first Expose event to do any drawing, then flush.
632 ---------------------------------------------------------------------------*/
635 XNextEvent(display
, &e
);
636 while (e
.type
!= Expose
|| e
.xexpose
.count
);
640 /*---------------------------------------------------------------------------
641 Allocate memory for the X- and display-specific version of the image.
642 ---------------------------------------------------------------------------*/
644 if (depth
== 24 || depth
== 32) {
645 xdata
= (uch
*)malloc(4*image_width
*image_height
);
647 } else if (depth
== 16) {
648 xdata
= (uch
*)malloc(2*image_width
*image_height
);
650 } else /* depth == 8 */ {
651 xdata
= (uch
*)malloc(image_width
*image_height
);
656 fprintf(stderr
, PROGNAME
": unable to allocate image memory\n");
660 ximage
= XCreateImage(display
, visual
, depth
, ZPixmap
, 0,
661 (char *)xdata
, image_width
, image_height
, pad
, 0);
664 fprintf(stderr
, PROGNAME
": XCreateImage() failed\n");
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... */
676 ximage
->byte_order
= MSBFirst
;
680 } /* end function rpng_x_create_window() */
686 static int rpng_x_display_image(void)
691 ulg i
, row
, lastrow
= 0;
693 int ximage_rowbytes
= ximage
->bytes_per_line
;
694 /* int bpp = ximage->bits_per_pixel; */
697 Trace((stderr
, "beginning display loop (image_channels == %d)\n",
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")))
705 if (depth
== 24 || depth
== 32) {
706 ulg red
, green
, blue
;
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
) {
716 #ifdef NO_24BIT_MASKS
717 pixel
= (red
<< RShift
) |
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);
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);
738 } else /* if (image_channels == 4) */ {
739 for (i
= image_width
; i
> 0; --i
) {
753 /* this macro (from png.h) composites the foreground
754 * and background values and puts the result into the
756 alpha_composite(red
, r
, a
, bg_red
);
757 alpha_composite(green
, g
, a
, bg_green
);
758 alpha_composite(blue
, b
, a
, bg_blue
);
760 pixel
= (red
<< RShift
) |
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);
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);
779 } else if (depth
== 16) {
780 ush red
, green
, blue
;
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);
789 green
= ((ush
)(*src
) << 8);
791 blue
= ((ush
)(*src
) << 8);
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);
800 } else /* if (image_channels == 4) */ {
801 for (i
= image_width
; i
> 0; --i
) {
808 green
= ((ush
)g
<< 8);
809 blue
= ((ush
)b
<< 8);
811 red
= ((ush
)bg_red
<< 8);
812 green
= ((ush
)bg_green
<< 8);
813 blue
= ((ush
)bg_blue
<< 8);
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
);
822 green
= ((ush
)g
<< 8);
823 blue
= ((ush
)b
<< 8);
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);
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);
842 } else /* depth == 8 */ {
844 /* GRR: add 8-bit support */
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
);
861 static void rpng_x_cleanup(void)
870 free(ximage
->data
); /* we allocated it, so we free it */
871 ximage
->data
= (char *)NULL
; /* instead of XDestroyImage() */
873 XDestroyImage(ximage
);
878 XFreeGC(display
, gc
);
881 XDestroyWindow(display
, window
);
884 XFreeColormap(display
, colormap
);
886 if (have_nondefault_visual
)
894 static int rpng_x_msb(ulg u32val
)
898 for (i
= 31; i
>= 0; --i
) {
899 if (u32val
& 0x80000000L
)