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