Blame contrib/gregbook/rpng-x.c

Packit 0ba690
/*---------------------------------------------------------------------------
Packit 0ba690
Packit 0ba690
   rpng - simple PNG display program                               rpng-x.c
Packit 0ba690
Packit 0ba690
   This program decodes and displays PNG images, with gamma correction and
Packit 0ba690
   optionally with a user-specified background color (in case the image has
Packit 0ba690
   transparency).  It is very nearly the most basic PNG viewer possible.
Packit 0ba690
   This version is for the X Window System (tested by author under Unix and
Packit 0ba690
   by Martin Zinser under OpenVMS; may work under OS/2 with some tweaking).
Packit 0ba690
Packit 0ba690
   to do:
Packit 0ba690
    - 8-bit (colormapped) X support
Packit 0ba690
    - use %.1023s to simplify truncation of title-bar string?
Packit 0ba690
Packit 0ba690
  ---------------------------------------------------------------------------
Packit 0ba690
Packit 0ba690
   Changelog:
Packit 0ba690
    - 1.01:  initial public release
Packit 0ba690
    - 1.02:  modified to allow abbreviated options; fixed long/ulong mis-
Packit 0ba690
              match; switched to png_jmpbuf() macro
Packit 0ba690
    - 1.10:  added support for non-default visuals; fixed X pixel-conversion
Packit 0ba690
    - 1.11:  added extra set of parentheses to png_jmpbuf() macro; fixed
Packit 0ba690
              command-line parsing bug
Packit 0ba690
    - 1.12:  fixed some small X memory leaks (thanks to François Petitjean)
Packit 0ba690
    - 1.13:  fixed XFreeGC() crash bug (thanks to Patrick Welche)
Packit 0ba690
    - 1.14:  added support for X resources (thanks to Gerhard Niklasch)
Packit 0ba690
    - 2.00:  dual-licensed (added GNU GPL)
Packit 0ba690
    - 2.01:  fixed improper display of usage screen on PNG error(s)
Packit 0ba690
Packit 0ba690
  ---------------------------------------------------------------------------
Packit 0ba690
Packit 0ba690
      Copyright (c) 1998-2008 Greg Roelofs.  All rights reserved.
Packit 0ba690
Packit 0ba690
      This software is provided "as is," without warranty of any kind,
Packit 0ba690
      express or implied.  In no event shall the author or contributors
Packit 0ba690
      be held liable for any damages arising in any way from the use of
Packit 0ba690
      this software.
Packit 0ba690
Packit 0ba690
      The contents of this file are DUAL-LICENSED.  You may modify and/or
Packit 0ba690
      redistribute this software according to the terms of one of the
Packit 0ba690
      following two licenses (at your option):
Packit 0ba690
Packit 0ba690
Packit 0ba690
      LICENSE 1 ("BSD-like with advertising clause"):
Packit 0ba690
Packit 0ba690
      Permission is granted to anyone to use this software for any purpose,
Packit 0ba690
      including commercial applications, and to alter it and redistribute
Packit 0ba690
      it freely, subject to the following restrictions:
Packit 0ba690
Packit 0ba690
      1. Redistributions of source code must retain the above copyright
Packit 0ba690
         notice, disclaimer, and this list of conditions.
Packit 0ba690
      2. Redistributions in binary form must reproduce the above copyright
Packit 0ba690
         notice, disclaimer, and this list of conditions in the documenta-
Packit 0ba690
         tion and/or other materials provided with the distribution.
Packit 0ba690
      3. All advertising materials mentioning features or use of this
Packit 0ba690
         software must display the following acknowledgment:
Packit 0ba690
Packit 0ba690
            This product includes software developed by Greg Roelofs
Packit 0ba690
            and contributors for the book, "PNG: The Definitive Guide,"
Packit 0ba690
            published by O'Reilly and Associates.
Packit 0ba690
Packit 0ba690
Packit 0ba690
      LICENSE 2 (GNU GPL v2 or later):
Packit 0ba690
Packit 0ba690
      This program is free software; you can redistribute it and/or modify
Packit 0ba690
      it under the terms of the GNU General Public License as published by
Packit 0ba690
      the Free Software Foundation; either version 2 of the License, or
Packit 0ba690
      (at your option) any later version.
Packit 0ba690
Packit 0ba690
      This program is distributed in the hope that it will be useful,
Packit 0ba690
      but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 0ba690
      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 0ba690
      GNU General Public License for more details.
Packit 0ba690
Packit 0ba690
      You should have received a copy of the GNU General Public License
Packit 0ba690
      along with this program; if not, write to the Free Software Foundation,
Packit 0ba690
      Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
Packit 0ba690
Packit 0ba690
  ---------------------------------------------------------------------------*/
Packit 0ba690
Packit 0ba690
#define PROGNAME  "rpng-x"
Packit 0ba690
#define LONGNAME  "Simple PNG Viewer for X"
Packit 0ba690
#define VERSION   "2.01 of 16 March 2008"
Packit 0ba690
#define RESNAME   "rpng"	/* our X resource application name */
Packit 0ba690
#define RESCLASS  "Rpng"	/* our X resource class name */
Packit 0ba690
Packit 0ba690
#include <stdio.h>
Packit 0ba690
#include <stdlib.h>
Packit 0ba690
#include <string.h>
Packit 0ba690
#include <time.h>
Packit 0ba690
#include <X11/Xlib.h>
Packit 0ba690
#include <X11/Xutil.h>
Packit 0ba690
#include <X11/Xos.h>
Packit 0ba690
#include <X11/keysym.h>
Packit 0ba690
Packit 0ba690
/* #define DEBUG  :  this enables the Trace() macros */
Packit 0ba690
Packit 0ba690
#include "readpng.h"   /* typedefs, common macros, readpng prototypes */
Packit 0ba690
Packit 0ba690
Packit 0ba690
/* could just include png.h, but this macro is the only thing we need
Packit 0ba690
 * (name and typedefs changed to local versions); note that side effects
Packit 0ba690
 * only happen with alpha (which could easily be avoided with
Packit 0ba690
 * "ush acopy = (alpha);") */
Packit 0ba690
Packit 0ba690
#define alpha_composite(composite, fg, alpha, bg) {               \
Packit 0ba690
    ush temp = ((ush)(fg)*(ush)(alpha) +                          \
Packit 0ba690
                (ush)(bg)*(ush)(255 - (ush)(alpha)) + (ush)128);  \
Packit 0ba690
    (composite) = (uch)((temp + (temp >> 8)) >> 8);               \
Packit 0ba690
}
Packit 0ba690
Packit 0ba690
Packit 0ba690
/* local prototypes */
Packit 0ba690
static int  rpng_x_create_window(void);
Packit 0ba690
static int  rpng_x_display_image(void);
Packit 0ba690
static void rpng_x_cleanup(void);
Packit 0ba690
static int  rpng_x_msb(ulg u32val);
Packit 0ba690
Packit 0ba690
Packit 0ba690
static char titlebar[1024], *window_name = titlebar;
Packit 0ba690
static char *appname = LONGNAME;
Packit 0ba690
static char *icon_name = PROGNAME;
Packit 0ba690
static char *res_name = RESNAME;
Packit 0ba690
static char *res_class = RESCLASS;
Packit 0ba690
static char *filename;
Packit 0ba690
static FILE *infile;
Packit 0ba690
Packit 0ba690
static char *bgstr;
Packit 0ba690
static uch bg_red=0, bg_green=0, bg_blue=0;
Packit 0ba690
Packit 0ba690
static double display_exponent;
Packit 0ba690
Packit 0ba690
static ulg image_width, image_height, image_rowbytes;
Packit 0ba690
static int image_channels;
Packit 0ba690
static uch *image_data;
Packit 0ba690
Packit 0ba690
/* X-specific variables */
Packit 0ba690
static char *displayname;
Packit 0ba690
static XImage *ximage;
Packit 0ba690
static Display *display;
Packit 0ba690
static int depth;
Packit 0ba690
static Visual *visual;
Packit 0ba690
static XVisualInfo *visual_list;
Packit 0ba690
static int RShift, GShift, BShift;
Packit 0ba690
static ulg RMask, GMask, BMask;
Packit 0ba690
static Window window;
Packit 0ba690
static GC gc;
Packit 0ba690
static Colormap colormap;
Packit 0ba690
Packit 0ba690
static int have_nondefault_visual = FALSE;
Packit 0ba690
static int have_colormap = FALSE;
Packit 0ba690
static int have_window = FALSE;
Packit 0ba690
static int have_gc = FALSE;
Packit 0ba690
/*
Packit 0ba690
ulg numcolors=0, pixels[256];
Packit 0ba690
ush reds[256], greens[256], blues[256];
Packit 0ba690
 */
Packit 0ba690
Packit 0ba690
Packit 0ba690
Packit 0ba690
Packit 0ba690
int main(int argc, char **argv)
Packit 0ba690
{
Packit 0ba690
#ifdef sgi
Packit 0ba690
    char tmpline[80];
Packit 0ba690
#endif
Packit 0ba690
    char *p;
Packit 0ba690
    int rc, alen, flen;
Packit 0ba690
    int error = 0;
Packit 0ba690
    int have_bg = FALSE;
Packit 0ba690
    double LUT_exponent;               /* just the lookup table */
Packit 0ba690
    double CRT_exponent = 2.2;         /* just the monitor */
Packit 0ba690
    double default_display_exponent;   /* whole display system */
Packit 0ba690
    XEvent e;
Packit 0ba690
    KeySym k;
Packit 0ba690
Packit 0ba690
Packit 0ba690
    displayname = (char *)NULL;
Packit 0ba690
    filename = (char *)NULL;
Packit 0ba690
Packit 0ba690
Packit 0ba690
    /* First set the default value for our display-system exponent, i.e.,
Packit 0ba690
     * the product of the CRT exponent and the exponent corresponding to
Packit 0ba690
     * the frame-buffer's lookup table (LUT), if any.  This is not an
Packit 0ba690
     * exhaustive list of LUT values (e.g., OpenStep has a lot of weird
Packit 0ba690
     * ones), but it should cover 99% of the current possibilities. */
Packit 0ba690
Packit 0ba690
#if defined(NeXT)
Packit 0ba690
    LUT_exponent = 1.0 / 2.2;
Packit 0ba690
    /*
Packit 0ba690
    if (some_next_function_that_returns_gamma(&next_gamma))
Packit 0ba690
        LUT_exponent = 1.0 / next_gamma;
Packit 0ba690
     */
Packit 0ba690
#elif defined(sgi)
Packit 0ba690
    LUT_exponent = 1.0 / 1.7;
Packit 0ba690
    /* there doesn't seem to be any documented function to get the
Packit 0ba690
     * "gamma" value, so we do it the hard way */
Packit 0ba690
    infile = fopen("/etc/config/system.glGammaVal", "r");
Packit 0ba690
    if (infile) {
Packit 0ba690
        double sgi_gamma;
Packit 0ba690
Packit 0ba690
        fgets(tmpline, 80, infile);
Packit 0ba690
        fclose(infile);
Packit 0ba690
        sgi_gamma = atof(tmpline);
Packit 0ba690
        if (sgi_gamma > 0.0)
Packit 0ba690
            LUT_exponent = 1.0 / sgi_gamma;
Packit 0ba690
    }
Packit 0ba690
#elif defined(Macintosh)
Packit 0ba690
    LUT_exponent = 1.8 / 2.61;
Packit 0ba690
    /*
Packit 0ba690
    if (some_mac_function_that_returns_gamma(&mac_gamma))
Packit 0ba690
        LUT_exponent = mac_gamma / 2.61;
Packit 0ba690
     */
Packit 0ba690
#else
Packit 0ba690
    LUT_exponent = 1.0;   /* assume no LUT:  most PCs */
Packit 0ba690
#endif
Packit 0ba690
Packit 0ba690
    /* the defaults above give 1.0, 1.3, 1.5 and 2.2, respectively: */
Packit 0ba690
    default_display_exponent = LUT_exponent * CRT_exponent;
Packit 0ba690
Packit 0ba690
Packit 0ba690
    /* If the user has set the SCREEN_GAMMA environment variable as suggested
Packit 0ba690
     * (somewhat imprecisely) in the libpng documentation, use that; otherwise
Packit 0ba690
     * use the default value we just calculated.  Either way, the user may
Packit 0ba690
     * override this via a command-line option. */
Packit 0ba690
Packit 0ba690
    if ((p = getenv("SCREEN_GAMMA")) != NULL)
Packit 0ba690
        display_exponent = atof(p);
Packit 0ba690
    else
Packit 0ba690
        display_exponent = default_display_exponent;
Packit 0ba690
Packit 0ba690
Packit 0ba690
    /* Now parse the command line for options and the PNG filename. */
Packit 0ba690
Packit 0ba690
    while (*++argv && !error) {
Packit 0ba690
        if (!strncmp(*argv, "-display", 2)) {
Packit 0ba690
            if (!*++argv)
Packit 0ba690
                ++error;
Packit 0ba690
            else
Packit 0ba690
                displayname = *argv;
Packit 0ba690
        } else if (!strncmp(*argv, "-gamma", 2)) {
Packit 0ba690
            if (!*++argv)
Packit 0ba690
                ++error;
Packit 0ba690
            else {
Packit 0ba690
                display_exponent = atof(*argv);
Packit 0ba690
                if (display_exponent <= 0.0)
Packit 0ba690
                    ++error;
Packit 0ba690
            }
Packit 0ba690
        } else if (!strncmp(*argv, "-bgcolor", 2)) {
Packit 0ba690
            if (!*++argv)
Packit 0ba690
                ++error;
Packit 0ba690
            else {
Packit 0ba690
                bgstr = *argv;
Packit 0ba690
                if (strlen(bgstr) != 7 || bgstr[0] != '#')
Packit 0ba690
                    ++error; 
Packit 0ba690
                else 
Packit 0ba690
                    have_bg = TRUE;
Packit 0ba690
            }
Packit 0ba690
        } else {
Packit 0ba690
            if (**argv != '-') {
Packit 0ba690
                filename = *argv;
Packit 0ba690
                if (argv[1])   /* shouldn't be any more args after filename */
Packit 0ba690
                    ++error;
Packit 0ba690
            } else
Packit 0ba690
                ++error;   /* not expecting any other options */
Packit 0ba690
        }
Packit 0ba690
    }
Packit 0ba690
Packit 0ba690
    if (!filename)
Packit 0ba690
        ++error;
Packit 0ba690
Packit 0ba690
Packit 0ba690
    /* print usage screen if any errors up to this point */
Packit 0ba690
Packit 0ba690
    if (error) {
Packit 0ba690
        fprintf(stderr, "\n%s %s:  %s\n", PROGNAME, VERSION, appname);
Packit 0ba690
        readpng_version_info();
Packit 0ba690
        fprintf(stderr, "\n"
Packit 0ba690
          "Usage:  %s [-display xdpy] [-gamma exp] [-bgcolor bg] file.png\n"
Packit 0ba690
          "    xdpy\tname of the target X display (e.g., ``hostname:0'')\n"
Packit 0ba690
          "    exp \ttransfer-function exponent (``gamma'') of the display\n"
Packit 0ba690
          "\t\t  system in floating-point format (e.g., ``%.1f''); equal\n"
Packit 0ba690
          "\t\t  to the product of the lookup-table exponent (varies)\n"
Packit 0ba690
          "\t\t  and the CRT exponent (usually 2.2); must be positive\n"
Packit 0ba690
          "    bg  \tdesired background color in 7-character hex RGB format\n"
Packit 0ba690
          "\t\t  (e.g., ``#ff7700'' for orange:  same as HTML colors);\n"
Packit 0ba690
          "\t\t  used with transparent images\n"
Packit 0ba690
          "\nPress Q, Esc or mouse button 1 (within image window, after image\n"
Packit 0ba690
          "is displayed) to quit.\n"
Packit 0ba690
          "\n", PROGNAME, default_display_exponent);
Packit 0ba690
        exit(1);
Packit 0ba690
    }
Packit 0ba690
Packit 0ba690
Packit 0ba690
    if (!(infile = fopen(filename, "rb"))) {
Packit 0ba690
        fprintf(stderr, PROGNAME ":  can't open PNG file [%s]\n", filename);
Packit 0ba690
        ++error;
Packit 0ba690
    } else {
Packit 0ba690
        if ((rc = readpng_init(infile, &image_width, &image_height)) != 0) {
Packit 0ba690
            switch (rc) {
Packit 0ba690
                case 1:
Packit 0ba690
                    fprintf(stderr, PROGNAME
Packit 0ba690
                      ":  [%s] is not a PNG file: incorrect signature\n",
Packit 0ba690
                      filename);
Packit 0ba690
                    break;
Packit 0ba690
                case 2:
Packit 0ba690
                    fprintf(stderr, PROGNAME
Packit 0ba690
                      ":  [%s] has bad IHDR (libpng longjmp)\n", filename);
Packit 0ba690
                    break;
Packit 0ba690
                case 4:
Packit 0ba690
                    fprintf(stderr, PROGNAME ":  insufficient memory\n");
Packit 0ba690
                    break;
Packit 0ba690
                default:
Packit 0ba690
                    fprintf(stderr, PROGNAME
Packit 0ba690
                      ":  unknown readpng_init() error\n");
Packit 0ba690
                    break;
Packit 0ba690
            }
Packit 0ba690
            ++error;
Packit 0ba690
        } else {
Packit 0ba690
            display = XOpenDisplay(displayname);
Packit 0ba690
            if (!display) {
Packit 0ba690
                readpng_cleanup(TRUE);
Packit 0ba690
                fprintf(stderr, PROGNAME ":  can't open X display [%s]\n",
Packit 0ba690
                  displayname? displayname : "default");
Packit 0ba690
                ++error;
Packit 0ba690
            }
Packit 0ba690
        }
Packit 0ba690
        if (error)
Packit 0ba690
            fclose(infile);
Packit 0ba690
    }
Packit 0ba690
Packit 0ba690
Packit 0ba690
    if (error) {
Packit 0ba690
        fprintf(stderr, PROGNAME ":  aborting.\n");
Packit 0ba690
        exit(2);
Packit 0ba690
    }
Packit 0ba690
Packit 0ba690
Packit 0ba690
    /* set the title-bar string, but make sure buffer doesn't overflow */
Packit 0ba690
Packit 0ba690
    alen = strlen(appname);
Packit 0ba690
    flen = strlen(filename);
Packit 0ba690
    if (alen + flen + 3 > 1023)
Packit 0ba690
        sprintf(titlebar, "%s:  ...%s", appname, filename+(alen+flen+6-1023));
Packit 0ba690
    else
Packit 0ba690
        sprintf(titlebar, "%s:  %s", appname, filename);
Packit 0ba690
Packit 0ba690
Packit 0ba690
    /* if the user didn't specify a background color on the command line,
Packit 0ba690
     * check for one in the PNG file--if not, the initialized values of 0
Packit 0ba690
     * (black) will be used */
Packit 0ba690
Packit 0ba690
    if (have_bg) {
Packit 0ba690
        unsigned r, g, b;   /* this approach quiets compiler warnings */
Packit 0ba690
Packit 0ba690
        sscanf(bgstr+1, "%2x%2x%2x", &r, &g, &b);
Packit 0ba690
        bg_red   = (uch)r;
Packit 0ba690
        bg_green = (uch)g;
Packit 0ba690
        bg_blue  = (uch)b;
Packit 0ba690
    } else if (readpng_get_bgcolor(&bg_red, &bg_green, &bg_blue) > 1) {
Packit 0ba690
        readpng_cleanup(TRUE);
Packit 0ba690
        fprintf(stderr, PROGNAME
Packit 0ba690
          ":  libpng error while checking for background color\n");
Packit 0ba690
        exit(2);
Packit 0ba690
    }
Packit 0ba690
Packit 0ba690
Packit 0ba690
    /* do the basic X initialization stuff, make the window and fill it
Packit 0ba690
     * with the background color */
Packit 0ba690
Packit 0ba690
    if (rpng_x_create_window())
Packit 0ba690
        exit(2);
Packit 0ba690
Packit 0ba690
Packit 0ba690
    /* decode the image, all at once */
Packit 0ba690
Packit 0ba690
    Trace((stderr, "calling readpng_get_image()\n"))
Packit 0ba690
    image_data = readpng_get_image(display_exponent, &image_channels,
Packit 0ba690
      &image_rowbytes);
Packit 0ba690
    Trace((stderr, "done with readpng_get_image()\n"))
Packit 0ba690
Packit 0ba690
Packit 0ba690
    /* done with PNG file, so clean up to minimize memory usage (but do NOT
Packit 0ba690
     * nuke image_data!) */
Packit 0ba690
Packit 0ba690
    readpng_cleanup(FALSE);
Packit 0ba690
    fclose(infile);
Packit 0ba690
Packit 0ba690
    if (!image_data) {
Packit 0ba690
        fprintf(stderr, PROGNAME ":  unable to decode PNG image\n");
Packit 0ba690
        exit(3);
Packit 0ba690
    }
Packit 0ba690
Packit 0ba690
Packit 0ba690
    /* display image (composite with background if requested) */
Packit 0ba690
Packit 0ba690
    Trace((stderr, "calling rpng_x_display_image()\n"))
Packit 0ba690
    if (rpng_x_display_image()) {
Packit 0ba690
        free(image_data);
Packit 0ba690
        exit(4);
Packit 0ba690
    }
Packit 0ba690
    Trace((stderr, "done with rpng_x_display_image()\n"))
Packit 0ba690
Packit 0ba690
Packit 0ba690
    /* wait for the user to tell us when to quit */
Packit 0ba690
Packit 0ba690
    printf(
Packit 0ba690
      "Done.  Press Q, Esc or mouse button 1 (within image window) to quit.\n");
Packit 0ba690
    fflush(stdout);
Packit 0ba690
Packit 0ba690
    do
Packit 0ba690
        XNextEvent(display, &e);
Packit 0ba690
    while (!(e.type == ButtonPress && e.xbutton.button == Button1) &&
Packit 0ba690
           !(e.type == KeyPress &&    /*  v--- or 1 for shifted keys */
Packit 0ba690
             ((k = XLookupKeysym(&e.xkey, 0)) == XK_q || k == XK_Escape) ));
Packit 0ba690
Packit 0ba690
Packit 0ba690
    /* OK, we're done:  clean up all image and X resources and go away */
Packit 0ba690
Packit 0ba690
    rpng_x_cleanup();
Packit 0ba690
Packit 0ba690
    return 0;
Packit 0ba690
}
Packit 0ba690
Packit 0ba690
Packit 0ba690
Packit 0ba690
Packit 0ba690
Packit 0ba690
static int rpng_x_create_window(void)
Packit 0ba690
{
Packit 0ba690
    uch *xdata;
Packit 0ba690
    int need_colormap = FALSE;
Packit 0ba690
    int screen, pad;
Packit 0ba690
    ulg bg_pixel = 0L;
Packit 0ba690
    ulg attrmask;
Packit 0ba690
    Window root;
Packit 0ba690
    XEvent e;
Packit 0ba690
    XGCValues gcvalues;
Packit 0ba690
    XSetWindowAttributes attr;
Packit 0ba690
    XTextProperty windowName, *pWindowName = &windowName;
Packit 0ba690
    XTextProperty iconName, *pIconName = &iconName;
Packit 0ba690
    XVisualInfo visual_info;
Packit 0ba690
    XSizeHints *size_hints;
Packit 0ba690
    XWMHints *wm_hints;
Packit 0ba690
    XClassHint *class_hints;
Packit 0ba690
Packit 0ba690
Packit 0ba690
    screen = DefaultScreen(display);
Packit 0ba690
    depth = DisplayPlanes(display, screen);
Packit 0ba690
    root = RootWindow(display, screen);
Packit 0ba690
Packit 0ba690
#ifdef DEBUG
Packit 0ba690
    XSynchronize(display, True);
Packit 0ba690
#endif
Packit 0ba690
Packit 0ba690
#if 0
Packit 0ba690
/* GRR:  add 8-bit support */
Packit 0ba690
    if (/* depth != 8 && */ depth != 16 && depth != 24 && depth != 32) {
Packit 0ba690
        fprintf(stderr,
Packit 0ba690
          "screen depth %d not supported (only 16-, 24- or 32-bit TrueColor)\n",
Packit 0ba690
          depth);
Packit 0ba690
        return 2;
Packit 0ba690
    }
Packit 0ba690
Packit 0ba690
    XMatchVisualInfo(display, screen, depth,
Packit 0ba690
      (depth == 8)? PseudoColor : TrueColor, &visual_info);
Packit 0ba690
    visual = visual_info.visual;
Packit 0ba690
#else
Packit 0ba690
    if (depth != 16 && depth != 24 && depth != 32) {
Packit 0ba690
        int visuals_matched = 0;
Packit 0ba690
Packit 0ba690
        Trace((stderr, "default depth is %d:  checking other visuals\n",
Packit 0ba690
          depth))
Packit 0ba690
Packit 0ba690
        /* 24-bit first */
Packit 0ba690
        visual_info.screen = screen;
Packit 0ba690
        visual_info.depth = 24;
Packit 0ba690
        visual_list = XGetVisualInfo(display,
Packit 0ba690
          VisualScreenMask | VisualDepthMask, &visual_info, &visuals_matched);
Packit 0ba690
        if (visuals_matched == 0) {
Packit 0ba690
/* GRR:  add 15-, 16- and 32-bit TrueColor visuals (also DirectColor?) */
Packit 0ba690
            fprintf(stderr, "default screen depth %d not supported, and no"
Packit 0ba690
              " 24-bit visuals found\n", depth);
Packit 0ba690
            return 2;
Packit 0ba690
        }
Packit 0ba690
        Trace((stderr, "XGetVisualInfo() returned %d 24-bit visuals\n",
Packit 0ba690
          visuals_matched))
Packit 0ba690
        visual = visual_list[0].visual;
Packit 0ba690
        depth = visual_list[0].depth;
Packit 0ba690
/*
Packit 0ba690
        colormap_size = visual_list[0].colormap_size;
Packit 0ba690
        visual_class = visual->class;
Packit 0ba690
        visualID = XVisualIDFromVisual(visual);
Packit 0ba690
 */
Packit 0ba690
        have_nondefault_visual = TRUE;
Packit 0ba690
        need_colormap = TRUE;
Packit 0ba690
    } else {
Packit 0ba690
        XMatchVisualInfo(display, screen, depth, TrueColor, &visual_info);
Packit 0ba690
        visual = visual_info.visual;
Packit 0ba690
    }
Packit 0ba690
#endif
Packit 0ba690
Packit 0ba690
    RMask = visual->red_mask;
Packit 0ba690
    GMask = visual->green_mask;
Packit 0ba690
    BMask = visual->blue_mask;
Packit 0ba690
Packit 0ba690
/* GRR:  add/check 8-bit support */
Packit 0ba690
    if (depth == 8 || need_colormap) {
Packit 0ba690
        colormap = XCreateColormap(display, root, visual, AllocNone);
Packit 0ba690
        if (!colormap) {
Packit 0ba690
            fprintf(stderr, "XCreateColormap() failed\n");
Packit 0ba690
            return 2;
Packit 0ba690
        }
Packit 0ba690
        have_colormap = TRUE;
Packit 0ba690
    }
Packit 0ba690
    if (depth == 15 || depth == 16) {
Packit 0ba690
        RShift = 15 - rpng_x_msb(RMask);    /* these are right-shifts */
Packit 0ba690
        GShift = 15 - rpng_x_msb(GMask);
Packit 0ba690
        BShift = 15 - rpng_x_msb(BMask);
Packit 0ba690
    } else if (depth > 16) {
Packit 0ba690
#define NO_24BIT_MASKS
Packit 0ba690
#ifdef NO_24BIT_MASKS
Packit 0ba690
        RShift = rpng_x_msb(RMask) - 7;     /* these are left-shifts */
Packit 0ba690
        GShift = rpng_x_msb(GMask) - 7;
Packit 0ba690
        BShift = rpng_x_msb(BMask) - 7;
Packit 0ba690
#else
Packit 0ba690
        RShift = 7 - rpng_x_msb(RMask);     /* these are right-shifts, too */
Packit 0ba690
        GShift = 7 - rpng_x_msb(GMask);
Packit 0ba690
        BShift = 7 - rpng_x_msb(BMask);
Packit 0ba690
#endif
Packit 0ba690
    }
Packit 0ba690
    if (depth >= 15 && (RShift < 0 || GShift < 0 || BShift < 0)) {
Packit 0ba690
        fprintf(stderr, "rpng internal logic error:  negative X shift(s)!\n");
Packit 0ba690
        return 2;
Packit 0ba690
    }
Packit 0ba690
Packit 0ba690
/*---------------------------------------------------------------------------
Packit 0ba690
    Finally, create the window.
Packit 0ba690
  ---------------------------------------------------------------------------*/
Packit 0ba690
Packit 0ba690
    attr.backing_store = Always;
Packit 0ba690
    attr.event_mask = ExposureMask | KeyPressMask | ButtonPressMask;
Packit 0ba690
    attrmask = CWBackingStore | CWEventMask;
Packit 0ba690
    if (have_nondefault_visual) {
Packit 0ba690
        attr.colormap = colormap;
Packit 0ba690
        attr.background_pixel = 0;
Packit 0ba690
        attr.border_pixel = 1;
Packit 0ba690
        attrmask |= CWColormap | CWBackPixel | CWBorderPixel;
Packit 0ba690
    }
Packit 0ba690
Packit 0ba690
    window = XCreateWindow(display, root, 0, 0, image_width, image_height, 0,
Packit 0ba690
      depth, InputOutput, visual, attrmask, &attr);
Packit 0ba690
Packit 0ba690
    if (window == None) {
Packit 0ba690
        fprintf(stderr, "XCreateWindow() failed\n");
Packit 0ba690
        return 2;
Packit 0ba690
    } else
Packit 0ba690
        have_window = TRUE;
Packit 0ba690
Packit 0ba690
    if (depth == 8)
Packit 0ba690
        XSetWindowColormap(display, window, colormap);
Packit 0ba690
Packit 0ba690
    if (!XStringListToTextProperty(&window_name, 1, pWindowName))
Packit 0ba690
        pWindowName = NULL;
Packit 0ba690
    if (!XStringListToTextProperty(&icon_name, 1, pIconName))
Packit 0ba690
        pIconName = NULL;
Packit 0ba690
Packit 0ba690
    /* OK if any hints allocation fails; XSetWMProperties() allows NULLs */
Packit 0ba690
Packit 0ba690
    if ((size_hints = XAllocSizeHints()) != NULL) {
Packit 0ba690
        /* window will not be resizable */
Packit 0ba690
        size_hints->flags = PMinSize | PMaxSize;
Packit 0ba690
        size_hints->min_width = size_hints->max_width = (int)image_width;
Packit 0ba690
        size_hints->min_height = size_hints->max_height = (int)image_height;
Packit 0ba690
    }
Packit 0ba690
Packit 0ba690
    if ((wm_hints = XAllocWMHints()) != NULL) {
Packit 0ba690
        wm_hints->initial_state = NormalState;
Packit 0ba690
        wm_hints->input = True;
Packit 0ba690
     /* wm_hints->icon_pixmap = icon_pixmap; */
Packit 0ba690
        wm_hints->flags = StateHint | InputHint  /* | IconPixmapHint */ ;
Packit 0ba690
    }
Packit 0ba690
Packit 0ba690
    if ((class_hints = XAllocClassHint()) != NULL) {
Packit 0ba690
        class_hints->res_name = res_name;
Packit 0ba690
        class_hints->res_class = res_class;
Packit 0ba690
    }
Packit 0ba690
Packit 0ba690
    XSetWMProperties(display, window, pWindowName, pIconName, NULL, 0,
Packit 0ba690
      size_hints, wm_hints, class_hints);
Packit 0ba690
Packit 0ba690
    /* various properties and hints no longer needed; free memory */
Packit 0ba690
    if (pWindowName)
Packit 0ba690
       XFree(pWindowName->value);
Packit 0ba690
    if (pIconName)
Packit 0ba690
       XFree(pIconName->value);
Packit 0ba690
    if (size_hints)
Packit 0ba690
        XFree(size_hints);
Packit 0ba690
    if (wm_hints)
Packit 0ba690
       XFree(wm_hints);
Packit 0ba690
    if (class_hints)
Packit 0ba690
       XFree(class_hints);
Packit 0ba690
Packit 0ba690
    XMapWindow(display, window);
Packit 0ba690
Packit 0ba690
    gc = XCreateGC(display, window, 0, &gcvalues);
Packit 0ba690
    have_gc = TRUE;
Packit 0ba690
Packit 0ba690
/*---------------------------------------------------------------------------
Packit 0ba690
    Fill window with the specified background color.
Packit 0ba690
  ---------------------------------------------------------------------------*/
Packit 0ba690
Packit 0ba690
    if (depth == 24 || depth == 32) {
Packit 0ba690
        bg_pixel = ((ulg)bg_red   << RShift) |
Packit 0ba690
                   ((ulg)bg_green << GShift) |
Packit 0ba690
                   ((ulg)bg_blue  << BShift);
Packit 0ba690
    } else if (depth == 16) {
Packit 0ba690
        bg_pixel = ((((ulg)bg_red   << 8) >> RShift) & RMask) |
Packit 0ba690
                   ((((ulg)bg_green << 8) >> GShift) & GMask) |
Packit 0ba690
                   ((((ulg)bg_blue  << 8) >> BShift) & BMask);
Packit 0ba690
    } else /* depth == 8 */ {
Packit 0ba690
Packit 0ba690
        /* GRR:  add 8-bit support */
Packit 0ba690
Packit 0ba690
    }
Packit 0ba690
Packit 0ba690
    XSetForeground(display, gc, bg_pixel);
Packit 0ba690
    XFillRectangle(display, window, gc, 0, 0, image_width, image_height);
Packit 0ba690
Packit 0ba690
/*---------------------------------------------------------------------------
Packit 0ba690
    Wait for first Expose event to do any drawing, then flush.
Packit 0ba690
  ---------------------------------------------------------------------------*/
Packit 0ba690
Packit 0ba690
    do
Packit 0ba690
        XNextEvent(display, &e);
Packit 0ba690
    while (e.type != Expose || e.xexpose.count);
Packit 0ba690
Packit 0ba690
    XFlush(display);
Packit 0ba690
Packit 0ba690
/*---------------------------------------------------------------------------
Packit 0ba690
    Allocate memory for the X- and display-specific version of the image.
Packit 0ba690
  ---------------------------------------------------------------------------*/
Packit 0ba690
Packit 0ba690
    if (depth == 24 || depth == 32) {
Packit 0ba690
        xdata = (uch *)malloc(4*image_width*image_height);
Packit 0ba690
        pad = 32;
Packit 0ba690
    } else if (depth == 16) {
Packit 0ba690
        xdata = (uch *)malloc(2*image_width*image_height);
Packit 0ba690
        pad = 16;
Packit 0ba690
    } else /* depth == 8 */ {
Packit 0ba690
        xdata = (uch *)malloc(image_width*image_height);
Packit 0ba690
        pad = 8;
Packit 0ba690
    }
Packit 0ba690
Packit 0ba690
    if (!xdata) {
Packit 0ba690
        fprintf(stderr, PROGNAME ":  unable to allocate image memory\n");
Packit 0ba690
        return 4;
Packit 0ba690
    }
Packit 0ba690
Packit 0ba690
    ximage = XCreateImage(display, visual, depth, ZPixmap, 0,
Packit 0ba690
      (char *)xdata, image_width, image_height, pad, 0);
Packit 0ba690
Packit 0ba690
    if (!ximage) {
Packit 0ba690
        fprintf(stderr, PROGNAME ":  XCreateImage() failed\n");
Packit 0ba690
        free(xdata);
Packit 0ba690
        return 3;
Packit 0ba690
    }
Packit 0ba690
Packit 0ba690
    /* to avoid testing the byte order every pixel (or doubling the size of
Packit 0ba690
     * the drawing routine with a giant if-test), we arbitrarily set the byte
Packit 0ba690
     * order to MSBFirst and let Xlib worry about inverting things on little-
Packit 0ba690
     * endian machines (like Linux/x86, old VAXen, etc.)--this is not the most
Packit 0ba690
     * efficient approach (the giant if-test would be better), but in the
Packit 0ba690
     * interest of clarity, we take the easy way out... */
Packit 0ba690
Packit 0ba690
    ximage->byte_order = MSBFirst;
Packit 0ba690
Packit 0ba690
    return 0;
Packit 0ba690
Packit 0ba690
} /* end function rpng_x_create_window() */
Packit 0ba690
Packit 0ba690
Packit 0ba690
Packit 0ba690
Packit 0ba690
Packit 0ba690
static int rpng_x_display_image(void)
Packit 0ba690
{
Packit 0ba690
    uch *src;
Packit 0ba690
    char *dest;
Packit 0ba690
    uch r, g, b, a;
Packit 0ba690
    ulg i, row, lastrow = 0;
Packit 0ba690
    ulg pixel;
Packit 0ba690
    int ximage_rowbytes = ximage->bytes_per_line;
Packit 0ba690
/*  int bpp = ximage->bits_per_pixel;  */
Packit 0ba690
Packit 0ba690
Packit 0ba690
    Trace((stderr, "beginning display loop (image_channels == %d)\n",
Packit 0ba690
      image_channels))
Packit 0ba690
    Trace((stderr, "   (width = %ld, rowbytes = %ld, ximage_rowbytes = %d)\n",
Packit 0ba690
      image_width, image_rowbytes, ximage_rowbytes))
Packit 0ba690
    Trace((stderr, "   (bpp = %d)\n", ximage->bits_per_pixel))
Packit 0ba690
    Trace((stderr, "   (byte_order = %s)\n", ximage->byte_order == MSBFirst?
Packit 0ba690
      "MSBFirst" : (ximage->byte_order == LSBFirst? "LSBFirst" : "unknown")))
Packit 0ba690
Packit 0ba690
    if (depth == 24 || depth == 32) {
Packit 0ba690
        ulg red, green, blue;
Packit 0ba690
Packit 0ba690
        for (lastrow = row = 0;  row < image_height;  ++row) {
Packit 0ba690
            src = image_data + row*image_rowbytes;
Packit 0ba690
            dest = ximage->data + row*ximage_rowbytes;
Packit 0ba690
            if (image_channels == 3) {
Packit 0ba690
                for (i = image_width;  i > 0;  --i) {
Packit 0ba690
                    red   = *src++;
Packit 0ba690
                    green = *src++;
Packit 0ba690
                    blue  = *src++;
Packit 0ba690
#ifdef NO_24BIT_MASKS
Packit 0ba690
                    pixel = (red   << RShift) |
Packit 0ba690
                            (green << GShift) |
Packit 0ba690
                            (blue  << BShift);
Packit 0ba690
                    /* recall that we set ximage->byte_order = MSBFirst above */
Packit 0ba690
                    /* GRR BUG:  this assumes bpp == 32, but may be 24: */
Packit 0ba690
                    *dest++ = (char)((pixel >> 24) & 0xff);
Packit 0ba690
                    *dest++ = (char)((pixel >> 16) & 0xff);
Packit 0ba690
                    *dest++ = (char)((pixel >>  8) & 0xff);
Packit 0ba690
                    *dest++ = (char)( pixel        & 0xff);
Packit 0ba690
#else
Packit 0ba690
                    red   = (RShift < 0)? red   << (-RShift) : red   >> RShift;
Packit 0ba690
                    green = (GShift < 0)? green << (-GShift) : green >> GShift;
Packit 0ba690
                    blue  = (BShift < 0)? blue  << (-BShift) : blue  >> BShift;
Packit 0ba690
                    pixel = (red & RMask) | (green & GMask) | (blue & BMask);
Packit 0ba690
                    /* recall that we set ximage->byte_order = MSBFirst above */
Packit 0ba690
                    *dest++ = (char)((pixel >> 24) & 0xff);
Packit 0ba690
                    *dest++ = (char)((pixel >> 16) & 0xff);
Packit 0ba690
                    *dest++ = (char)((pixel >>  8) & 0xff);
Packit 0ba690
                    *dest++ = (char)( pixel        & 0xff);
Packit 0ba690
#endif
Packit 0ba690
                }
Packit 0ba690
            } else /* if (image_channels == 4) */ {
Packit 0ba690
                for (i = image_width;  i > 0;  --i) {
Packit 0ba690
                    r = *src++;
Packit 0ba690
                    g = *src++;
Packit 0ba690
                    b = *src++;
Packit 0ba690
                    a = *src++;
Packit 0ba690
                    if (a == 255) {
Packit 0ba690
                        red   = r;
Packit 0ba690
                        green = g;
Packit 0ba690
                        blue  = b;
Packit 0ba690
                    } else if (a == 0) {
Packit 0ba690
                        red   = bg_red;
Packit 0ba690
                        green = bg_green;
Packit 0ba690
                        blue  = bg_blue;
Packit 0ba690
                    } else {
Packit 0ba690
                        /* this macro (from png.h) composites the foreground
Packit 0ba690
                         * and background values and puts the result into the
Packit 0ba690
                         * first argument */
Packit 0ba690
                        alpha_composite(red,   r, a, bg_red);
Packit 0ba690
                        alpha_composite(green, g, a, bg_green);
Packit 0ba690
                        alpha_composite(blue,  b, a, bg_blue);
Packit 0ba690
                    }
Packit 0ba690
                    pixel = (red   << RShift) |
Packit 0ba690
                            (green << GShift) |
Packit 0ba690
                            (blue  << BShift);
Packit 0ba690
                    /* recall that we set ximage->byte_order = MSBFirst above */
Packit 0ba690
                    *dest++ = (char)((pixel >> 24) & 0xff);
Packit 0ba690
                    *dest++ = (char)((pixel >> 16) & 0xff);
Packit 0ba690
                    *dest++ = (char)((pixel >>  8) & 0xff);
Packit 0ba690
                    *dest++ = (char)( pixel        & 0xff);
Packit 0ba690
                }
Packit 0ba690
            }
Packit 0ba690
            /* display after every 16 lines */
Packit 0ba690
            if (((row+1) & 0xf) == 0) {
Packit 0ba690
                XPutImage(display, window, gc, ximage, 0, (int)lastrow, 0,
Packit 0ba690
                  (int)lastrow, image_width, 16);
Packit 0ba690
                XFlush(display);
Packit 0ba690
                lastrow = row + 1;
Packit 0ba690
            }
Packit 0ba690
        }
Packit 0ba690
Packit 0ba690
    } else if (depth == 16) {
Packit 0ba690
        ush red, green, blue;
Packit 0ba690
Packit 0ba690
        for (lastrow = row = 0;  row < image_height;  ++row) {
Packit 0ba690
            src = image_data + row*image_rowbytes;
Packit 0ba690
            dest = ximage->data + row*ximage_rowbytes;
Packit 0ba690
            if (image_channels == 3) {
Packit 0ba690
                for (i = image_width;  i > 0;  --i) {
Packit 0ba690
                    red   = ((ush)(*src) << 8);
Packit 0ba690
                    ++src;
Packit 0ba690
                    green = ((ush)(*src) << 8);
Packit 0ba690
                    ++src;
Packit 0ba690
                    blue  = ((ush)(*src) << 8);
Packit 0ba690
                    ++src;
Packit 0ba690
                    pixel = ((red   >> RShift) & RMask) |
Packit 0ba690
                            ((green >> GShift) & GMask) |
Packit 0ba690
                            ((blue  >> BShift) & BMask);
Packit 0ba690
                    /* recall that we set ximage->byte_order = MSBFirst above */
Packit 0ba690
                    *dest++ = (char)((pixel >>  8) & 0xff);
Packit 0ba690
                    *dest++ = (char)( pixel        & 0xff);
Packit 0ba690
                }
Packit 0ba690
            } else /* if (image_channels == 4) */ {
Packit 0ba690
                for (i = image_width;  i > 0;  --i) {
Packit 0ba690
                    r = *src++;
Packit 0ba690
                    g = *src++;
Packit 0ba690
                    b = *src++;
Packit 0ba690
                    a = *src++;
Packit 0ba690
                    if (a == 255) {
Packit 0ba690
                        red   = ((ush)r << 8);
Packit 0ba690
                        green = ((ush)g << 8);
Packit 0ba690
                        blue  = ((ush)b << 8);
Packit 0ba690
                    } else if (a == 0) {
Packit 0ba690
                        red   = ((ush)bg_red   << 8);
Packit 0ba690
                        green = ((ush)bg_green << 8);
Packit 0ba690
                        blue  = ((ush)bg_blue  << 8);
Packit 0ba690
                    } else {
Packit 0ba690
                        /* this macro (from png.h) composites the foreground
Packit 0ba690
                         * and background values and puts the result back into
Packit 0ba690
                         * the first argument (== fg byte here:  safe) */
Packit 0ba690
                        alpha_composite(r, r, a, bg_red);
Packit 0ba690
                        alpha_composite(g, g, a, bg_green);
Packit 0ba690
                        alpha_composite(b, b, a, bg_blue);
Packit 0ba690
                        red   = ((ush)r << 8);
Packit 0ba690
                        green = ((ush)g << 8);
Packit 0ba690
                        blue  = ((ush)b << 8);
Packit 0ba690
                    }
Packit 0ba690
                    pixel = ((red   >> RShift) & RMask) |
Packit 0ba690
                            ((green >> GShift) & GMask) |
Packit 0ba690
                            ((blue  >> BShift) & BMask);
Packit 0ba690
                    /* recall that we set ximage->byte_order = MSBFirst above */
Packit 0ba690
                    *dest++ = (char)((pixel >>  8) & 0xff);
Packit 0ba690
                    *dest++ = (char)( pixel        & 0xff);
Packit 0ba690
                }
Packit 0ba690
            }
Packit 0ba690
            /* display after every 16 lines */
Packit 0ba690
            if (((row+1) & 0xf) == 0) {
Packit 0ba690
                XPutImage(display, window, gc, ximage, 0, (int)lastrow, 0,
Packit 0ba690
                  (int)lastrow, image_width, 16);
Packit 0ba690
                XFlush(display);
Packit 0ba690
                lastrow = row + 1;
Packit 0ba690
            }
Packit 0ba690
        }
Packit 0ba690
Packit 0ba690
    } else /* depth == 8 */ {
Packit 0ba690
Packit 0ba690
        /* GRR:  add 8-bit support */
Packit 0ba690
Packit 0ba690
    }
Packit 0ba690
Packit 0ba690
    Trace((stderr, "calling final XPutImage()\n"))
Packit 0ba690
    if (lastrow < image_height) {
Packit 0ba690
        XPutImage(display, window, gc, ximage, 0, (int)lastrow, 0,
Packit 0ba690
          (int)lastrow, image_width, image_height-lastrow);
Packit 0ba690
        XFlush(display);
Packit 0ba690
    }
Packit 0ba690
Packit 0ba690
    return 0;
Packit 0ba690
}
Packit 0ba690
Packit 0ba690
Packit 0ba690
Packit 0ba690
Packit 0ba690
static void rpng_x_cleanup(void)
Packit 0ba690
{
Packit 0ba690
    if (image_data) {
Packit 0ba690
        free(image_data);
Packit 0ba690
        image_data = NULL;
Packit 0ba690
    }
Packit 0ba690
Packit 0ba690
    if (ximage) {
Packit 0ba690
        if (ximage->data) {
Packit 0ba690
            free(ximage->data);           /* we allocated it, so we free it */
Packit 0ba690
            ximage->data = (char *)NULL;  /*  instead of XDestroyImage() */
Packit 0ba690
        }
Packit 0ba690
        XDestroyImage(ximage);
Packit 0ba690
        ximage = NULL;
Packit 0ba690
    }
Packit 0ba690
Packit 0ba690
    if (have_gc)
Packit 0ba690
        XFreeGC(display, gc);
Packit 0ba690
Packit 0ba690
    if (have_window)
Packit 0ba690
        XDestroyWindow(display, window);
Packit 0ba690
Packit 0ba690
    if (have_colormap)
Packit 0ba690
        XFreeColormap(display, colormap);
Packit 0ba690
Packit 0ba690
    if (have_nondefault_visual)
Packit 0ba690
        XFree(visual_list);
Packit 0ba690
}
Packit 0ba690
Packit 0ba690
Packit 0ba690
Packit 0ba690
Packit 0ba690
Packit 0ba690
static int rpng_x_msb(ulg u32val)
Packit 0ba690
{
Packit 0ba690
    int i;
Packit 0ba690
Packit 0ba690
    for (i = 31;  i >= 0;  --i) {
Packit 0ba690
        if (u32val & 0x80000000L)
Packit 0ba690
            break;
Packit 0ba690
        u32val <<= 1;
Packit 0ba690
    }
Packit 0ba690
    return i;
Packit 0ba690
}