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