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