Home | History | Annotate | Download | only in gregbook
      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 Franois 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 
    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 
    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 
    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 
    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 
    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