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