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)