about summary refs log tree commit diff
path: root/converter/other/xwdtopnm.c
diff options
context:
space:
mode:
Diffstat (limited to 'converter/other/xwdtopnm.c')
-rw-r--r--converter/other/xwdtopnm.c1280
1 files changed, 1280 insertions, 0 deletions
diff --git a/converter/other/xwdtopnm.c b/converter/other/xwdtopnm.c
new file mode 100644
index 00000000..28c38cfc
--- /dev/null
+++ b/converter/other/xwdtopnm.c
@@ -0,0 +1,1280 @@
+/* xwdtopnm.c - read an X11 or X10 window dump file and write a PNM image.
+**
+** Copyright (C) 1989, 1991 by Jef Poskanzer.
+**
+** Permission to use, copy, modify, and distribute this software and its
+** documentation for any purpose and without fee is hereby granted, provided
+** that the above copyright notice appear in all copies and that both that
+** copyright notice and this permission notice appear in supporting
+** documentation.  This software is provided "as is" without express or
+** implied warranty.
+*/
+
+/* IMPLEMENTATION NOTES:
+
+   The file X11/XWDFile.h from the X Window System is an authority for the
+   format of an XWD file.  Netpbm uses its own declaration, though.
+*/
+
+
+#define _BSD_SOURCE 1      /* Make sure strdup() is in string.h */
+#define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+#include "pm_c_util.h"
+#include "shhopt.h"
+#include "mallocvar.h"
+#include "nstring.h"
+#include "pnm.h"
+#include "x10wd.h"
+#include "x11wd.h"
+
+struct cmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    char *input_filename;
+    unsigned int verbose;
+    unsigned int debug;
+    unsigned int headerdump;
+};
+
+
+static bool debug;
+static bool verbose;
+
+#ifdef DEBUG_PIXEL
+static unsigned int pixel_count = 0;
+#endif
+
+/* Byte-swapping junk. */
+
+static int
+zero_bits(const unsigned long mask) {
+/*----------------------------------------------------------------------------
+   Return the number of consective zero bits at the least significant end
+   of the binary representation of 'mask'.  E.g. if mask == 0x00fff800,
+   we would return 11.
+-----------------------------------------------------------------------------*/
+    int i;
+    unsigned long shifted_mask;
+
+    for (i=0, shifted_mask = mask; 
+         i < sizeof(mask)*8 && (shifted_mask & 0x00000001) == 0;
+         i++, shifted_mask >>= 1 );
+    return(i);
+}
+
+
+
+static int
+one_bits(const unsigned long input) {
+/*----------------------------------------------------------------------------
+   Return the number of one bits in the binary representation of 'input'.
+-----------------------------------------------------------------------------*/
+    int one_bits;
+    unsigned long mask;
+
+    one_bits = 0;   /* initial value */
+    for (mask = 0x00000001; mask != 0x00000000; mask <<= 1)
+        if (input & mask) one_bits++;
+
+    return(one_bits);
+}
+
+
+
+static void
+parseCommandLine(int argc, char ** argv,
+                 struct cmdlineInfo * const cmdlineP) {
+/*----------------------------------------------------------------------------
+   Note that many of the strings that this function returns in the
+   *cmdline_p structure are actually in the supplied argv array.  And
+   sometimes, one of these strings is actually just a suffix of an entry
+   in argv!
+-----------------------------------------------------------------------------*/
+    optEntry * option_def;
+        /* Instructions to OptParseOptions3 on how to parse our options.
+         */
+    optStruct3 opt;
+
+    unsigned int option_def_index;
+
+    MALLOCARRAY(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENTRY */
+
+    OPTENT3(0,   "verbose",    OPT_FLAG,   NULL, &cmdlineP->verbose,       0);
+    OPTENT3(0,   "debug",      OPT_FLAG,   NULL, &cmdlineP->debug,         0);
+    OPTENT3(0,   "headerdump", OPT_FLAG,   NULL, &cmdlineP->headerdump,    0);
+
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = FALSE;  /* We may have parms that are negative numbers */
+
+    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+
+    if (argc - 1 == 0)
+        cmdlineP->input_filename = NULL;  /* he wants stdin */
+    else if (argc - 1 == 1) {
+        if (STREQ(argv[1], "-"))
+            cmdlineP->input_filename = NULL;  /* he wants stdin */
+        else 
+            cmdlineP->input_filename = strdup(argv[1]);
+    } else 
+        pm_error("Too many arguments.  The only argument accepted\n"
+                 "is the input file specification");
+}
+
+
+
+static void
+processX10Header(X10WDFileHeader *  const h10P, 
+                 FILE *             const file,
+                 int *              const colsP, 
+                 int *              const rowsP, 
+                 int *              const padrightP, 
+                 xelval *           const maxvalP, 
+                 enum visualclass * const visualclassP, 
+                 int *              const formatP, 
+                 xel **             const colorsP, 
+                 int *              const bits_per_pixelP, 
+                 int *              const bits_per_itemP, 
+                 unsigned long *    const red_maskP, 
+                 unsigned long *    const green_maskP, 
+                 unsigned long *    const blue_maskP,
+                 enum byteorder *   const byte_orderP,
+                 enum byteorder *   const bit_orderP) {
+
+    int i;
+    X10Color* x10colors;
+    bool grayscale;
+    bool byte_swap;
+
+    *maxvalP = 65535;   /* Initial assumption */
+
+    if ( h10P->file_version != X10WD_FILE_VERSION ) {
+        byte_swap = TRUE;
+        h10P->header_size     = pm_bs_long(h10P->header_size);
+        h10P->file_version    = pm_bs_long(h10P->file_version);
+        h10P->display_type    = pm_bs_long(h10P->display_type);
+        h10P->display_planes  = pm_bs_long(h10P->display_planes);
+        h10P->pixmap_format   = pm_bs_long(h10P->pixmap_format);
+        h10P->pixmap_width    = pm_bs_long(h10P->pixmap_width);
+        h10P->pixmap_height   = pm_bs_long(h10P->pixmap_height);
+        h10P->window_width    = pm_bs_short(h10P->window_width);
+        h10P->window_height   = pm_bs_short(h10P->window_height);
+        h10P->window_x        = pm_bs_short(h10P->window_x);
+        h10P->window_y        = pm_bs_short(h10P->window_y);
+        h10P->window_bdrwidth = pm_bs_short(h10P->window_bdrwidth);
+        h10P->window_ncolors  = pm_bs_short(h10P->window_ncolors);
+    } else
+        byte_swap = FALSE;
+
+    for ( i = 0; i < h10P->header_size - sizeof(*h10P); ++i )
+        if ( getc( file ) == EOF )
+            pm_error( "couldn't read rest of X10 XWD file header" );
+
+    /* Check whether we can handle this dump. */
+    if ( h10P->window_ncolors > 256 )
+        pm_error( "can't handle X10 window_ncolors > %d", 256 );
+    if ( h10P->pixmap_format != ZFormat && h10P->display_planes != 1 )
+        pm_error(
+            "can't handle X10 pixmap_format %d with planes != 1",
+            h10P->pixmap_format );
+
+    grayscale = TRUE;  /* initial assumption */
+    if ( h10P->window_ncolors != 0 ) {
+        /* Read X10 colormap. */
+        MALLOCARRAY( x10colors, h10P->window_ncolors );
+        if ( x10colors == NULL )
+            pm_error( "out of memory" );
+        for ( i = 0; i < h10P->window_ncolors; ++i ) {
+            if ( fread( &x10colors[i], sizeof(X10Color), 1, file ) != 1 )
+                pm_error( "couldn't read X10 XWD colormap" );
+            if ( byte_swap ) {
+                x10colors[i].red   = pm_bs_short(x10colors[i].red);
+                x10colors[i].green = pm_bs_short(x10colors[i].green);
+                x10colors[i].blue  = pm_bs_short(x10colors[i].blue);
+            }
+            if ( x10colors[i].red != x10colors[i].green ||
+                 x10colors[i].green != x10colors[i].blue )
+                grayscale = FALSE;
+        }
+    }
+
+    if ( h10P->display_planes == 1 ) {
+        *formatP = PBM_TYPE;
+        *visualclassP = StaticGray;
+        *maxvalP = 1;
+        *colorsP = pnm_allocrow( 2 );
+        PNM_ASSIGN1( (*colorsP)[0], 0 );
+        PNM_ASSIGN1( (*colorsP)[1], *maxvalP );
+        *padrightP =
+            ( ( h10P->pixmap_width + 15 ) / 16 ) * 16 - h10P->pixmap_width;
+        *bits_per_itemP = 16;
+        *bits_per_pixelP = 1;
+    } else if ( h10P->window_ncolors == 0 ) { 
+        /* Must be grayscale. */
+        *formatP = PGM_TYPE;
+        *visualclassP = StaticGray;
+        *maxvalP = ( 1 << h10P->display_planes ) - 1;
+        *colorsP = pnm_allocrow( *maxvalP + 1 );
+        for ( i = 0; i <= *maxvalP; ++i )
+            PNM_ASSIGN1( (*colorsP)[i], i );
+        *padrightP =
+            ( ( h10P->pixmap_width + 15 ) / 16 ) * 16 - h10P->pixmap_width;
+        *bits_per_itemP = 16;
+        *bits_per_pixelP = 1;
+    } else {
+        *colorsP = pnm_allocrow( h10P->window_ncolors );
+        *visualclassP = PseudoColor;
+        if ( grayscale ) {
+            *formatP = PGM_TYPE;
+            for ( i = 0; i < h10P->window_ncolors; ++i )
+                PNM_ASSIGN1( (*colorsP)[i], x10colors[i].red );
+        } else {
+            *formatP = PPM_TYPE;
+            for ( i = 0; i < h10P->window_ncolors; ++i )
+                PPM_ASSIGN(
+                    (*colorsP)[i], x10colors[i].red, x10colors[i].green,
+                    x10colors[i].blue);
+        }
+
+        *padrightP = h10P->pixmap_width & 1;
+        *bits_per_itemP = 8;
+        *bits_per_pixelP = 8;
+    }
+    *colsP = h10P->pixmap_width;
+    *rowsP = h10P->pixmap_height;
+    *byte_orderP = MSBFirst;
+    *bit_orderP = LSBFirst;
+}
+
+
+
+static void
+fixH11ByteOrder(X11WDFileHeader *  const h11P,
+                X11WDFileHeader ** const h11FixedPP) {
+
+    X11WDFileHeader * h11FixedP;
+
+    MALLOCVAR_NOFAIL(h11FixedP);
+
+    if (h11P->file_version == X11WD_FILE_VERSION) {
+        memcpy(h11FixedP, h11P, sizeof(*h11FixedP));
+    } else {
+        h11FixedP->header_size      = pm_bs_long(h11P->header_size);
+        h11FixedP->file_version     = pm_bs_long(h11P->file_version);
+        h11FixedP->pixmap_format    = pm_bs_long(h11P->pixmap_format);
+        h11FixedP->pixmap_depth     = pm_bs_long(h11P->pixmap_depth);
+        h11FixedP->pixmap_width     = pm_bs_long(h11P->pixmap_width);
+        h11FixedP->pixmap_height    = pm_bs_long(h11P->pixmap_height);
+        h11FixedP->xoffset          = pm_bs_long(h11P->xoffset);
+        h11FixedP->byte_order       = pm_bs_long(h11P->byte_order);
+        h11FixedP->bitmap_unit      = pm_bs_long(h11P->bitmap_unit);
+        h11FixedP->bitmap_bit_order = pm_bs_long(h11P->bitmap_bit_order);
+        h11FixedP->bitmap_pad       = pm_bs_long(h11P->bitmap_pad);
+        h11FixedP->bits_per_pixel   = pm_bs_long(h11P->bits_per_pixel);
+        h11FixedP->bytes_per_line   = pm_bs_long(h11P->bytes_per_line);
+        h11FixedP->visual_class     = pm_bs_long(h11P->visual_class);
+        h11FixedP->red_mask         = pm_bs_long(h11P->red_mask);
+        h11FixedP->green_mask       = pm_bs_long(h11P->green_mask);
+        h11FixedP->blue_mask        = pm_bs_long(h11P->blue_mask);
+        h11FixedP->bits_per_rgb     = pm_bs_long(h11P->bits_per_rgb);
+        h11FixedP->colormap_entries = pm_bs_long(h11P->colormap_entries);
+        h11FixedP->ncolors          = pm_bs_long(h11P->ncolors);
+        h11FixedP->window_width     = pm_bs_long(h11P->window_width);
+        h11FixedP->window_height    = pm_bs_long(h11P->window_height);
+        h11FixedP->window_x         = pm_bs_long(h11P->window_x);
+        h11FixedP->window_y         = pm_bs_long(h11P->window_y);
+        h11FixedP->window_bdrwidth  = pm_bs_long(h11P->window_bdrwidth);
+    }
+    *h11FixedPP = h11FixedP;
+}
+
+
+
+static void
+readX11Colormap(FILE *      const file,
+                int         const ncolors, 
+                bool        const byteSwap,
+                X11XColor** const x11colorsP) {
+                
+    X11XColor * x11colors;
+    int rc;
+
+    /* Read X11 colormap. */
+    MALLOCARRAY(x11colors, ncolors);
+    if (x11colors == NULL)
+        pm_error("out of memory");
+    rc = fread(x11colors, sizeof(x11colors[0]), ncolors, file);
+    if (rc != ncolors)
+        pm_error("couldn't read X11 XWD colormap");
+    if (byteSwap) {
+        unsigned int i;
+        for (i = 0; i < ncolors; ++i) {
+            x11colors[i].red   = pm_bs_short(x11colors[i].red);
+            x11colors[i].green = pm_bs_short(x11colors[i].green);
+            x11colors[i].blue  = pm_bs_short(x11colors[i].blue);
+        }
+    }
+    if (debug) {
+        unsigned int i;
+        for (i = 0; i < ncolors && i < 8; ++i)
+            pm_message("Color %d r/g/b = %d/%d/%d", i, 
+                       x11colors[i].red, x11colors[i].green, 
+                       x11colors[i].blue);
+    }
+    *x11colorsP = x11colors;
+}
+
+
+
+static bool
+colormapAllGray(const X11XColor* const x11colors,
+                unsigned int     const ncolors) {
+
+    unsigned int i;
+    bool grayscale;
+
+    grayscale = TRUE;  /* initial assumption */
+    for (i = 0; i < ncolors; ++i) 
+        if (x11colors[i].red != x11colors[i].green ||
+            x11colors[i].green != x11colors[i].blue )
+            grayscale = FALSE;
+
+    return grayscale;
+}
+
+
+
+static void
+dumpX11Header(X11WDFileHeader * const h11P) {
+
+    const char * formatDesc;
+    const char * byteOrderDesc;
+    const char * bitOrderDesc;
+    const char * visualClassDesc;
+
+    X11WDFileHeader * h11FixedP;
+
+    fixH11ByteOrder(h11P, &h11FixedP);
+ 
+    switch(h11FixedP->pixmap_format) {
+    case XYBitmap: formatDesc = "XY bit map"; break;
+    case XYPixmap: formatDesc = "XY pix map"; break;
+    case ZPixmap:  formatDesc = "Z pix map";  break;
+    default:       formatDesc = "???";
+    }
+
+    switch(h11FixedP->byte_order) {
+    case LSBFirst: byteOrderDesc = "LSB first"; break;
+    case MSBFirst: byteOrderDesc = "MSB first"; break;
+    default:       byteOrderDesc = "???";
+    }
+
+    switch (h11FixedP->bitmap_bit_order) {
+    case LSBFirst: bitOrderDesc = "LSB first"; break;
+    case MSBFirst: bitOrderDesc = "MSB first"; break;
+    default:       bitOrderDesc = "???";
+    }
+
+    switch (h11FixedP->visual_class) {
+    case StaticGray:  visualClassDesc = "StaticGray";  break;
+    case GrayScale:   visualClassDesc = "GrayScale";   break;
+    case StaticColor: visualClassDesc = "StaticColor"; break;
+    case PseudoColor: visualClassDesc = "PseudoColor"; break;
+    case TrueColor:   visualClassDesc = "TrueColor"; break;
+    case DirectColor: visualClassDesc = "DirectColor"; break;
+    default:          visualClassDesc = "???";
+    }
+
+    pm_message("File version: %u", h11FixedP->file_version);
+    pm_message("Format: %s (%u)", formatDesc, h11FixedP->pixmap_format);
+    pm_message("Width:  %u", h11FixedP->pixmap_width);
+    pm_message("Height: %u", h11FixedP->pixmap_height);
+    pm_message("Depth: %u bits", h11FixedP->pixmap_depth);
+    pm_message("X offset: %u", h11FixedP->xoffset);
+    pm_message("byte order: %s (%u)", byteOrderDesc, h11FixedP->byte_order);
+    pm_message("bitmap unit: %u", h11FixedP->bitmap_unit);
+    pm_message("bit order: %s (%u)", 
+               bitOrderDesc, h11FixedP->bitmap_bit_order);
+    pm_message("bitmap pad: %u", h11FixedP->bitmap_pad);
+    pm_message("bits per pixel: %u", h11FixedP->bits_per_pixel);
+    pm_message("bytes per line: %u", h11FixedP->bytes_per_line);
+    pm_message("visual class: %s (%u)", 
+               visualClassDesc, h11FixedP->visual_class);
+    pm_message("red mask:   %08x", h11FixedP->red_mask);
+    pm_message("green mask: %08x", h11FixedP->green_mask);
+    pm_message("blue mask:  %08x", h11FixedP->blue_mask);
+    pm_message("bits per rgb: %u", h11FixedP->bits_per_rgb);
+    pm_message("number of colormap entries: %u", h11FixedP->colormap_entries);
+    pm_message("number of colors in colormap: %u", h11FixedP->ncolors);
+    pm_message("window width:  %u", h11FixedP->window_width);
+    pm_message("window height: %u", h11FixedP->window_height);
+    pm_message("window upper left X coordinate: %u", h11FixedP->window_x);
+    pm_message("window upper left Y coordinate: %u", h11FixedP->window_y);
+    pm_message("window border width: %u", h11FixedP->window_bdrwidth);
+
+    free(h11FixedP);
+}
+
+
+
+static void
+processX11Header(X11WDFileHeader *  const h11P, 
+                 FILE *             const file,
+                 int *              const colsP, 
+                 int *              const rowsP, 
+                 int *              const padrightP, 
+                 xelval *           const maxvalP, 
+                 enum visualclass * const visualclassP, 
+                 int *              const formatP, 
+                 xel **             const colorsP, 
+                 int *              const bits_per_pixelP, 
+                 int *              const bits_per_itemP, 
+                 unsigned long *    const red_maskP, 
+                 unsigned long *    const green_maskP, 
+                 unsigned long *    const blue_maskP,
+                 enum byteorder *   const byte_orderP,
+                 enum byteorder *   const bit_orderP) {
+
+    int i;
+    X11XColor* x11colors;
+    bool grayscale;
+    bool const byte_swap = (h11P->file_version != X11WD_FILE_VERSION);
+    X11WDFileHeader * h11FixedP;
+ 
+    fixH11ByteOrder(h11P, &h11FixedP);
+
+    if (byte_swap && verbose)
+        pm_message("Header is different endianness from this machine.");
+    
+    for (i = 0; i < h11FixedP->header_size - sizeof(*h11FixedP); ++i)
+        if (getc(file) == EOF)
+            pm_error("couldn't read rest of X11 XWD file header");
+
+    /* Check whether we can handle this dump. */
+    if ( h11FixedP->pixmap_depth > 24 )
+        pm_error( "can't handle X11 pixmap_depth > 24" );
+    if ( h11FixedP->bits_per_rgb > 24 )
+        pm_error( "can't handle X11 bits_per_rgb > 24" );
+    if ( h11FixedP->pixmap_format != ZPixmap && h11FixedP->pixmap_depth != 1 )
+        pm_error(
+            "can't handle X11 pixmap_format %d with depth != 1",
+            h11FixedP->pixmap_format );
+    if ( h11FixedP->bitmap_unit != 8 && h11FixedP->bitmap_unit != 16 &&
+         h11FixedP->bitmap_unit != 32 )
+        pm_error(
+            "X11 bitmap_unit (%d) is non-standard - can't handle",
+            h11FixedP->bitmap_unit );
+    /* The following check was added in 10.19 (November 2003) */
+    if ( h11FixedP->bitmap_pad != 8 && h11FixedP->bitmap_pad != 16 &&
+         h11FixedP->bitmap_pad != 32 )
+        pm_error(
+            "X11 bitmap_pad (%d) is non-standard - can't handle",
+            h11FixedP->bitmap_unit );
+
+    if ( h11FixedP->ncolors > 0 ) {
+        readX11Colormap( file, h11FixedP->ncolors, byte_swap, &x11colors );
+        grayscale = colormapAllGray( x11colors, h11FixedP->ncolors );
+    } else
+        grayscale = TRUE;
+
+    *visualclassP = (enum visualclass) h11FixedP->visual_class;
+    if ( *visualclassP == DirectColor ) {
+        unsigned int i;
+        *formatP = PPM_TYPE;
+        *maxvalP = 65535;
+        /*
+          DirectColor is like PseudoColor except that there are essentially
+          3 colormaps (shade maps) -- one for each color component.  Each pixel
+          is composed of 3 separate indices.
+        */
+
+        *colorsP = pnm_allocrow( h11FixedP->ncolors );
+        for ( i = 0; i < h11FixedP->ncolors; ++i )
+            PPM_ASSIGN(
+                (*colorsP)[i], x11colors[i].red, x11colors[i].green,
+                x11colors[i].blue);
+    } else if ( *visualclassP == TrueColor ) {
+        *formatP = PPM_TYPE;
+
+        *maxvalP = pm_lcm(pm_bitstomaxval(one_bits(h11FixedP->red_mask)),
+                          pm_bitstomaxval(one_bits(h11FixedP->green_mask)),
+                          pm_bitstomaxval(one_bits(h11FixedP->blue_mask)),
+                          PPM_OVERALLMAXVAL
+            );
+    }
+    else if ( *visualclassP == StaticGray && h11FixedP->bits_per_pixel == 1 ) {
+        *formatP = PBM_TYPE;
+        *maxvalP = 1;
+        *colorsP = pnm_allocrow( 2 );
+        PNM_ASSIGN1( (*colorsP)[0], *maxvalP );
+        PNM_ASSIGN1( (*colorsP)[1], 0 );
+    } else if ( *visualclassP == StaticGray ) {
+        unsigned int i;
+        *formatP = PGM_TYPE;
+        *maxvalP = ( 1 << h11FixedP->bits_per_pixel ) - 1;
+        *colorsP = pnm_allocrow( *maxvalP + 1 );
+        for ( i = 0; i <= *maxvalP; ++i )
+            PNM_ASSIGN1( (*colorsP)[i], i );
+    } else {
+        *colorsP = pnm_allocrow( h11FixedP->ncolors );
+        if ( grayscale ) {
+            unsigned int i;
+            *formatP = PGM_TYPE;
+            for ( i = 0; i < h11FixedP->ncolors; ++i )
+                PNM_ASSIGN1( (*colorsP)[i], x11colors[i].red );
+        } else {
+            unsigned int i;
+            *formatP = PPM_TYPE;
+            for ( i = 0; i < h11FixedP->ncolors; ++i )
+                PPM_ASSIGN(
+                    (*colorsP)[i], x11colors[i].red, x11colors[i].green,
+                    x11colors[i].blue);
+        }
+        *maxvalP = 65535;
+    }
+
+    *colsP = h11FixedP->pixmap_width;
+    *rowsP = h11FixedP->pixmap_height;
+    *padrightP =
+        h11FixedP->bytes_per_line * 8 / h11FixedP->bits_per_pixel -
+        h11FixedP->pixmap_width;
+    /* According to X11/XWDFile.h, the item size is 'bitmap_pad' for some
+       images and 'bitmap_unit' for others.  This is strange, so there may
+       be some subtlety of their definitions that we're missing.
+
+       See comments in getpix() about what an item is.
+
+       Ben Kelley in January 2002 had a 32 bits-per-pixel xwd file
+       from a truecolor 32 bit window on a Hummingbird Exceed X server
+       on Win32, and it had bitmap_unit = 8 and bitmap_pad = 32.
+
+       But Björn Eriksson in October 2003 had an xwd file from a 24
+       bit-per-pixel direct color window that had bitmap_unit = 32 and
+       bitmap_pad = 8.  This was made by Xwd in Red Hat Xfree86 4.3.0-2.
+
+       Before Netpbm 9.23 (January 2002), we used bitmap_unit as the
+       item size always.  Then, until 10.19 (November 2003), we used
+       bitmap_pad when pixmap_depth > 1 and pixmap_format == ZPixmap.
+       We still don't see any logic in these fields at all, but we
+       figure whichever one is greater (assuming both are meaningful)
+       has to be the item size.  
+    */
+    *bits_per_itemP = MAX(h11FixedP->bitmap_pad, h11FixedP->bitmap_unit);
+
+    *bits_per_pixelP = h11FixedP->bits_per_pixel;
+
+    *byte_orderP = (enum byteorder) h11FixedP->byte_order;
+    *bit_orderP = (enum byteorder) h11FixedP->bitmap_bit_order;
+    *red_maskP = h11FixedP->red_mask;
+    *green_maskP = h11FixedP->green_mask;
+    *blue_maskP = h11FixedP->blue_mask;
+
+    free(h11FixedP);
+} 
+
+
+
+static void
+getinit(FILE *             const ifP, 
+        int *              const colsP, 
+        int *              const rowsP, 
+        int *              const padrightP, 
+        xelval *           const maxvalP, 
+        enum visualclass * const visualclassP, 
+        int *              const formatP, 
+        xel **             const colorsP,
+        int *              const bits_per_pixelP, 
+        int *              const bits_per_itemP, 
+        unsigned long *    const red_maskP, 
+        unsigned long *    const green_maskP,
+        unsigned long *    const blue_maskP,
+        enum byteorder *   const byte_orderP,
+        enum byteorder *   const bit_orderP,
+        bool               const headerDump) {
+/*----------------------------------------------------------------------------
+   Read the header from the XWD image in input stream 'ifP'.  Leave
+   the stream positioned to the beginning of the raster.
+
+   Return various fields from the header.
+
+   Return as *padrightP the number of additional pixels of padding are
+   at the end of each line of input.  This says the input stream
+   contains *colsP pixels of image data plus *padrightP pixels of
+   padding.
+-----------------------------------------------------------------------------*/
+    /* Assume X11 headers are larger than X10 ones. */
+    unsigned char header[sizeof(X11WDFileHeader)];
+    X10WDFileHeader* h10P;
+    X11WDFileHeader* h11P;
+    int rc;
+
+    h10P = (X10WDFileHeader*) header;
+    h11P = (X11WDFileHeader*) header;
+    if ( sizeof(*h10P) > sizeof(*h11P) )
+        pm_error("ARGH!  On this machine, X10 headers are larger than "
+                 "X11 headers!\n    You will have to re-write xwdtopnm." );
+    
+    /* We read an X10 header's worth of data from the file, then look
+       at it to see if it looks like an X10 header.  If so we process
+       the X10 header.  If not, but it looks like the beginning of an
+       X11 header, we read more bytes so we have an X11 header's worth
+       of data, then process the X11 header.  Otherwise, we raise an
+       error.  
+    */
+
+    rc = fread(&header[0], sizeof(*h10P), 1, ifP);
+    if (rc != 1)
+        pm_error( "couldn't read XWD file header" );
+
+    if (h10P->file_version == X10WD_FILE_VERSION ||
+        pm_bs_long(h10P->file_version) == X10WD_FILE_VERSION) {
+        
+        if (verbose)
+            pm_message("Input is X10");
+        processX10Header(h10P, ifP, colsP, rowsP, padrightP, maxvalP, 
+                         visualclassP, formatP, 
+                         colorsP, bits_per_pixelP, bits_per_itemP, 
+                         red_maskP, green_maskP, blue_maskP, 
+                         byte_orderP, bit_orderP);
+    } else if (h11P->file_version == X11WD_FILE_VERSION ||
+               pm_bs_long(h11P->file_version) == X11WD_FILE_VERSION) {
+        
+        int rc;
+
+        if (verbose)
+            pm_message("Input is X11");
+
+        /* Read the balance of the X11 header */
+        rc = fread(&header[sizeof(*h10P)], 
+                   sizeof(*h11P) - sizeof(*h10P), 1, ifP);
+        if (rc != 1)
+            pm_error("couldn't read end of X11 XWD file header");
+
+        if (headerDump)
+            dumpX11Header(h11P);
+
+        processX11Header(h11P, ifP, colsP, rowsP, padrightP, maxvalP, 
+                         visualclassP, formatP, 
+                         colorsP, bits_per_pixelP, bits_per_itemP, 
+                         red_maskP, green_maskP, blue_maskP, 
+                         byte_orderP, bit_orderP);
+    } else
+        pm_error("unknown XWD file version: %u", h11P->file_version);
+}
+
+
+
+#ifdef DEBUG_PIXEL
+#define DEBUG_PIXEL_1 \
+   if (pixel_count < 4) \
+       pm_message("getting pixel %d", pixel_count);
+
+#define DEBUG_PIXEL_2 \
+    if (pixel_count < 4) \
+        pm_message("item: %.8lx", row_controlP->item.l); 
+
+
+#define DEBUG_PIXEL_3 \
+    if (pixel_count < 4) \
+        pm_message("  bits_taken: %lx(%d), carryover_bits: %lx(%d), " \
+                     "pixel: %lx", \
+                     bits_taken, bits_to_take, row_controlP->carryover_bits, \
+                     row_controlP->bits_carried_over, pixel);
+
+#define DEBUG_PIXEL_4 \
+    if (pixel_count < 4) { \
+        pm_message("  row_control.bits_carried_over = %d" \
+                   "  carryover_bits= %.8lx", \
+                   row_controlP->bits_carried_over, \
+                   row_controlP->carryover_bits); \
+        pm_message("  row_control.bits_used = %d", \
+                   row_controlP->bits_used); \
+        pm_message("  row_control.bits_left = %d", \
+                   row_controlP->bits_left); \
+                   } \
+    \
+    pixel_count++;
+#else
+#define DEBUG_PIXEL_1 do {} while(0)
+#define DEBUG_PIXEL_2 do {} while(0)
+#define DEBUG_PIXEL_3 do {} while(0)
+#define DEBUG_PIXEL_4 do {} while(0)
+#endif
+
+
+
+/*----------------------------------------------------------------------------
+   The pixel reader.
+
+   The pixel reader is an object that reads an XWD raster and gives you
+   one pixel at a time from it.
+
+   It consists of a structure of type 'pixelReader' and the
+   getpix() and pixelReaderInit() subroutines.
+-----------------------------------------------------------------------------*/
+
+typedef struct {
+    /* This structure contains the state of the getpix() reader as it
+       reads across a row in the input image.
+       */
+    FILE * fileP;
+    unsigned long itemBuffer;
+        /* The item buffer.  This contains what's left of the item
+           most recently read from the image file -- an item goes from the
+           XWD raster into here and then bits disappear from it as they
+           become part of pixels returned by the object.
+
+           'nBitsLeft' tells how many bits are in the buffer now.  It's
+           zero when nothing has ever been read from the file.  Only
+           the least signficant 'nBitsLeft' bits are meaningful.
+
+           The numeric value of the member is the number whose pure
+           binary representation is the bit string in the buffer.
+
+           That bit string starts out as the contents of one item of
+           the XWD raster, with the "byte order" value from the XWD
+           header applied.  E.g. Assume bits per item is 16 and the
+           and the "byte order" value is "MSB First".  Assume the
+           raster contains 0x01 at Offset 109 and 0x02 at Offset 110.
+           The value of this member just after that item is read is
+           two hundred fifty-eight.  If we are running on a
+           little-endian machine, it appears in memory as 0x02 at
+           Address A and 0x01 at Address A+1.
+
+           Then we pull bits from either the beginning or end of the
+           buffer according to the "bit order" value from the XWD
+           header.  E.g. in the example above, assume "bit order" is
+           lsb first.  and bits per pixel is 4.  After reading the
+           first pixel (0010b) from the buffer, 'itemBuffer' is
+           sixteen (0x010) and 'nBitsLeft' is 12.
+        */
+    unsigned int nBitsLeft;
+      /* This is the number of bits in the current item that have not yet
+         been returned as part of a pixel.
+      */
+    int bitsPerPixel;
+    int bitsPerItem;
+    enum byteorder byteOrder;
+    enum byteorder bitOrder;
+} pixelReader;
+
+
+
+static void
+pixelReaderInit(pixelReader *  const pixelReaderP,
+                FILE *         const fileP,
+                int            const bitsPerPixel,
+                int            const bitsPerItem, 
+                enum byteorder const byteOrder,
+                enum byteorder const bitOrder) {
+    
+    pixelReaderP->fileP           = fileP;
+    pixelReaderP->bitsPerPixel    = bitsPerPixel;
+    pixelReaderP->bitsPerItem     = bitsPerItem;
+    pixelReaderP->byteOrder       = byteOrder;
+    pixelReaderP->bitOrder        = bitOrder;
+
+    pixelReaderP->nBitsLeft       = 0;
+}    
+
+
+
+static void
+readItem(pixelReader * const rdrP) {
+/*----------------------------------------------------------------------------
+   Read one item from the XWD raster associated with pixel reader *rdrP.
+
+   Put the item into the item buffer of the pixel reader object *rdrP.
+-----------------------------------------------------------------------------*/
+    assert(rdrP->nBitsLeft == 0);
+
+    switch (rdrP->bitsPerItem) {
+    case 8: {
+        unsigned char const item8 = getc(rdrP->fileP);
+        rdrP->itemBuffer = item8;
+        rdrP->nBitsLeft = 8;
+    }
+        break;
+
+    case 16: {
+        short item16;
+
+        switch (rdrP->byteOrder) {
+        case MSBFirst:
+            pm_readbigshort(rdrP->fileP, &item16);
+            break;
+        case LSBFirst:
+            pm_readlittleshort(rdrP->fileP, &item16);
+            break;
+        }
+        rdrP->itemBuffer = (unsigned short)item16;
+        rdrP->nBitsLeft = 16;
+    }
+        break;
+        
+    case 32: {
+        long item32;
+        
+        switch (rdrP->byteOrder) {
+        case MSBFirst:
+            pm_readbiglong(rdrP->fileP, &item32);
+            break;
+        case LSBFirst:
+            pm_readlittlelong(rdrP->fileP, &item32);
+            break;
+        }
+        rdrP->itemBuffer = item32;
+        rdrP->nBitsLeft = 32;
+    }
+        break;
+    default:
+        pm_error("INTERNAL ERROR: impossible bits_per_item");
+    }
+}
+
+
+
+static unsigned long const lsbmask[] = {
+/*----------------------------------------------------------------------------
+   lsbmask[i] is the mask you use to select the i least signficant bits
+   of a bit string.
+-----------------------------------------------------------------------------*/
+    0x00000000,
+    0x00000001, 0x00000003, 0x00000007, 0x0000000f, 
+    0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff,
+    0x000001ff, 0x000003ff, 0x000007ff, 0x00000fff,
+    0x00001fff, 0x00003fff, 0x00007fff, 0x0000ffff,
+    0x0001ffff, 0x0003ffff, 0x0007ffff, 0x000fffff,
+    0x001fffff, 0x003fffff, 0x007fffff, 0x00ffffff,
+    0x01ffffff, 0x03ffffff, 0x07ffffff, 0x0fffffff,
+    0x1fffffff, 0x3fffffff, 0x7fffffff, 0xffffffff
+};
+
+
+static unsigned long
+getpix(pixelReader * const rdrP) {
+/*----------------------------------------------------------------------------
+   Get a pixel from the input image.
+
+   A pixel is a bit string.  It may be either an rgb triplet or an index
+   into the colormap (or even an rgb triplet of indices into the colormaps!).
+   We don't care -- it's just a bit string.
+
+   We return an integer.  It's the integer that the pixel represents as
+   pure binary cipher, with the first bit the most significant bit.
+   
+   The basic unit of storage in the input file is an "item."  An item
+   can be 1, 2, or 4 bytes, and 'bits_per_item' tells us which.  Each
+   item can have its bytes stored in forward or reverse order, and
+   'byte_order' tells us which.
+
+   Each item can contain one or more pixels, and may contain
+   fractional pixels.  'bits_per_pixel' tells us how many bits each
+   pixel has, and 'bits_per_pixel' is always less than or equal to
+   'bits_per_item', but not necessarily a factor of it.  Within an item,
+   after taking care of the endianness of its storage format, the pixels
+   may be arranged from left to right or right to left.  'bit_order' tells
+   us which.
+
+   But it's not that simple.  Sometimes dummy pixels are added to the
+   right edge of the image in order to make an integral number of
+   items in each row of the raster.  getpix() doesn't know anything
+   about that, though -- it gets the dummy pixels the same as any
+   other pixel.  (This program is written as if it is always whole
+   pixels that get added for padding, but I wonder.  When pixels are 3
+   or 4 bytes and items are 4 bytes, sub-pixel padding would make
+   sense.  But then, maybe in formats with 3 or 4 bytes pixels,
+   there's never padding.  That seems to be the case so far...).  The
+   XWD header has a field that tells how many bytes there are per XWD
+   raster line, so that's the final word on how much padding there is.
+   
+   The most difficult part of getting the pixel is the ones that span
+   items.  We detect when an item has part, but not all, of the next
+   pixel (after the one we return) in it and store the fragment as
+   "carryover" bits for use in the next call to this function.
+
+   All this state information (carryover bits, etc.) is kept in 
+   *row_controlP.
+
+-----------------------------------------------------------------------------*/
+    unsigned long pixel;
+        /* Accumulator for the value we ultimately return.  We shift in
+           bits from the right end.  The number of bits presently in the
+           accumulator is rdrP->bitsPerPixel - bitsStillNeeded .
+        */
+    
+    unsigned int nBitsStillNeeded;
+        /* How many bits we still need to add to 'pixel',
+           as we build it up to the
+           full amount we have to return.  The bits are right justified in
+           it -- additional bits will shift in from the right.
+        */
+
+    assert(rdrP->bitsPerPixel <= 32);
+
+    pixel = 0;
+    nBitsStillNeeded = rdrP->bitsPerPixel;
+
+    while (nBitsStillNeeded > 0) {
+        if (rdrP->nBitsLeft == 0)
+            /* Buffer's empty.  Have to go back to the well for
+               rdrP->bitsPerItem more bits.
+            */
+            readItem(rdrP);
+
+        {
+            unsigned int const nBitsToTake =
+                MIN(rdrP->nBitsLeft, nBitsStillNeeded);
+            unsigned long const bitsToTakeMask = lsbmask[nBitsToTake];
+                /* E.g. if nbitsToTake is 4, this is 0x0000000F */
+
+            unsigned long bitsToTake;
+                /* The actual bits we take, in the 'nBitsToTake' low bits */
+
+            assert(nBitsToTake <= 32);
+            
+            if (rdrP->bitOrder == MSBFirst) {
+                unsigned int const nBitsToLeave =
+                    rdrP->nBitsLeft - nBitsToTake;
+                bitsToTake =
+                    (rdrP->itemBuffer >> nBitsToLeave) & bitsToTakeMask;
+            } else {
+                bitsToTake = rdrP->itemBuffer & bitsToTakeMask;
+                rdrP->itemBuffer >>= nBitsToTake;
+            }                
+            /* Shift the bits into the right end of the accumulator */
+            pixel <<= nBitsToTake;
+            pixel |= bitsToTake;
+
+            rdrP->nBitsLeft -= nBitsToTake;
+            nBitsStillNeeded -= nBitsToTake;
+        }
+    }
+    return pixel;
+}
+
+
+
+static void
+reportInfo(int              const cols, 
+           int              const rows, 
+           int              const padright, 
+           xelval           const maxval, 
+           enum visualclass const visualclass,
+           int              const format, 
+           int              const bits_per_pixel,
+           int              const bits_per_item, 
+           int              const red_mask, 
+           int              const green_mask, 
+           int              const blue_mask,
+           enum byteorder   const byte_order, 
+           enum byteorder   const bit_order) {
+    
+    const char *visualclass_name;
+    const char *byte_order_name;
+    const char *bit_order_name;
+    switch (visualclass) {
+    case StaticGray:  visualclass_name="StaticGray";  break;
+    case GrayScale:   visualclass_name="Grayscale";   break;
+    case StaticColor: visualclass_name="StaticColor"; break;
+    case PseudoColor: visualclass_name="PseudoColor"; break;
+    case TrueColor:   visualclass_name="TrueColor";   break;
+    case DirectColor: visualclass_name="DirectColor"; break;
+    default:          visualclass_name="(invalid)";    break;
+    }
+    switch (byte_order) {
+    case MSBFirst: byte_order_name = "MSBFirst";  break;
+    case LSBFirst: byte_order_name = "LSBFirst";  break;
+    default:       byte_order_name = "(invalid)"; break;
+    }
+    switch (bit_order) {
+    case MSBFirst: bit_order_name = "MSBFirst";  break;
+    case LSBFirst: bit_order_name = "LSBFirst";  break;
+    default:       bit_order_name = "(invalid)"; break;
+    }
+    pm_message("%d rows of %d columns with maxval %d",
+               rows, cols, maxval);
+    pm_message("padright=%d.  visualclass = %s.  format=%d (%c%c)",
+               padright, visualclass_name, 
+               format, format/256, format%256);
+    pm_message("bits_per_pixel=%d; bits_per_item=%d",
+               bits_per_pixel, bits_per_item);
+    pm_message("byte_order=%s; bit_order=%s",
+               byte_order_name, bit_order_name);
+    pm_message("red_mask=0x%.8x; green_mask=0x%.8x; blue_mask=0x%.8x",
+               red_mask, green_mask, blue_mask);
+}
+
+
+
+static void 
+convertRowSimpleIndex(pixelReader *  const pixelReaderP,
+                      int            const cols,
+                      const xel *    const colors,
+                      xel *          const xelrow) {
+    
+    unsigned int col;
+    for (col = 0; col < cols; ++col)
+        xelrow[col] = colors[getpix(pixelReaderP)];
+}
+
+
+
+static void
+convertRowDirect(pixelReader *  const pixelReaderP,
+                 int            const cols,
+                 const xel *    const colors,
+                 unsigned long  const red_mask,
+                 unsigned long  const grn_mask,
+                 unsigned long  const blu_mask,
+                 xel *          const xelrow) {
+        
+    unsigned int col;
+
+    for (col = 0; col < cols; ++col) {
+        unsigned long pixel;
+            /* This is a triplet of indices into the color map, packed
+               into this bit string according to red_mask, etc.
+            */
+        unsigned int red_index, grn_index, blu_index;
+            /* These are indices into the color map, unpacked from 'pixel'.
+             */
+            
+        pixel = getpix(pixelReaderP);
+
+        red_index = (pixel & red_mask) >> zero_bits(red_mask);
+        grn_index = (pixel & grn_mask) >> zero_bits(grn_mask); 
+        blu_index = (pixel & blu_mask) >> zero_bits(blu_mask);
+
+        PPM_ASSIGN(xelrow[col],
+                   PPM_GETR(colors[red_index]),
+                   PPM_GETG(colors[grn_index]),
+                   PPM_GETB(colors[blu_index])
+            );
+    }
+}
+
+
+
+static void
+convertRowTrueColor(pixelReader *  const pixelReaderP,
+                    int                  const cols,
+                    pixval               const maxval,
+                    const xel *          const colors,
+                    unsigned long        const red_mask,
+                    unsigned long        const grn_mask,
+                    unsigned long        const blu_mask,
+                    xel *                const xelrow) {
+
+    unsigned int col;
+    unsigned int red_shift, grn_shift, blu_shift;
+    unsigned int red_maxval, grn_maxval, blu_maxval;
+
+    red_shift = zero_bits(red_mask);
+    grn_shift = zero_bits(grn_mask);
+    blu_shift = zero_bits(blu_mask);
+
+    red_maxval = red_mask >> red_shift;
+    grn_maxval = grn_mask >> grn_shift;
+    blu_maxval = blu_mask >> blu_shift;
+
+    for (col = 0; col < cols; ++col) {
+        unsigned long pixel;
+
+        pixel = getpix(pixelReaderP);
+
+        /* The parsing of 'pixel' used to be done with hardcoded layout
+           parameters.  See comments at end of this file.
+        */
+        PPM_ASSIGN(xelrow[col],
+                   ((pixel & red_mask) >> red_shift) * maxval / red_maxval,
+                   ((pixel & grn_mask) >> grn_shift) * maxval / grn_maxval,
+                   ((pixel & blu_mask) >> blu_shift) * maxval / blu_maxval
+            );
+
+    }
+}
+
+
+
+static void
+convertRow(pixelReader *    const pixelReaderP,
+           FILE *           const ofP,
+           int              const padright, 
+           int              const cols, 
+           xelval           const maxval,
+           int              const format, 
+           unsigned long    const red_mask, 
+           unsigned long    const green_mask, 
+           unsigned long    const blue_mask, 
+           const xel*       const colors, 
+           enum visualclass const visualclass) {
+/*----------------------------------------------------------------------------
+   Read a row from the XWD pixel input stream 'pixelReaderP' and write
+   it to the PNM output stream 'ofP'.
+
+   The row is 'cols' pixels.
+
+   After reading the 'cols' pixels, we read and discard an additional
+   'padright' pixels from the input stream, so as to read the entire
+   input line.
+-----------------------------------------------------------------------------*/
+    xel* xelrow;
+    xelrow = pnm_allocrow(cols);
+
+    switch (visualclass) {
+    case StaticGray:
+    case GrayScale:
+    case StaticColor:
+    case PseudoColor:
+        convertRowSimpleIndex(pixelReaderP, cols, colors, xelrow);
+        break;
+    case DirectColor: 
+        convertRowDirect(pixelReaderP, cols, colors,
+                         red_mask, green_mask, blue_mask,
+                         xelrow);
+        
+        break;
+    case TrueColor: 
+        convertRowTrueColor(pixelReaderP, cols, maxval, colors,
+                            red_mask, green_mask, blue_mask,
+                            xelrow);
+        break;
+            
+    default:
+        pm_error("unknown visual class");
+    }
+    {
+        unsigned int col;
+        for (col = 0; col < padright; ++col)
+            getpix(pixelReaderP);
+    }
+    pnm_writepnmrow(ofP, xelrow, cols, maxval, format, 0);
+    pnm_freerow(xelrow);
+}
+
+
+
+static void
+reportOutputType(int const format) {
+
+    switch (PNM_FORMAT_TYPE(format)) {
+    case PBM_TYPE:
+        pm_message("writing PBM file");
+    break;
+
+    case PGM_TYPE:
+        pm_message("writing PGM file");
+    break;
+    
+    case PPM_TYPE:
+        pm_message("writing PPM file");
+    break;
+    
+    default:
+        pm_error("shouldn't happen");
+    }
+}
+
+
+
+int
+main(int argc, char *argv[]) {
+
+    struct cmdlineInfo cmdline;
+    FILE * ifP;
+    int rows, cols, format, padright;
+    unsigned int row;
+    int bits_per_pixel;
+    int bits_per_item;
+    unsigned long red_mask, green_mask, blue_mask;
+    xelval maxval;
+    enum visualclass visualclass;
+    enum byteorder byte_order, bit_order;
+    xel *colors;  /* the color map */
+    pixelReader pixelReader;
+
+    pnm_init(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    debug = cmdline.debug;
+    verbose = cmdline.verbose;
+
+    if (cmdline.input_filename != NULL) 
+        ifP = pm_openr(cmdline.input_filename);
+    else
+        ifP = stdin;
+
+    getinit(ifP, &cols, &rows, &padright, &maxval, &visualclass, &format, 
+            &colors, &bits_per_pixel, &bits_per_item, 
+            &red_mask, &green_mask, &blue_mask, &byte_order, &bit_order,
+            cmdline.headerdump);
+    
+    if (verbose) 
+        reportInfo(cols, rows, padright, maxval, visualclass,
+                   format, bits_per_pixel, bits_per_item,
+                   red_mask, green_mask, blue_mask, 
+                   byte_order, bit_order);
+
+    pixelReaderInit(&pixelReader, ifP, bits_per_pixel, bits_per_item,
+                    byte_order, bit_order);
+
+    pnm_writepnminit(stdout, cols, rows, maxval, format, 0);
+
+    reportOutputType(format);
+
+    for (row = 0; row < rows; ++row) {
+        convertRow(&pixelReader, stdout,
+                   padright, cols, maxval, format,
+                   red_mask, green_mask, blue_mask, colors, visualclass);
+    }
+    
+    pm_close(ifP);
+    pm_close(stdout);
+    
+    return 0;
+}
+
+
+/*
+   This used to be the way we parsed a direct/true color pixel.  I'm 
+   keeping it here in case we find out some application needs it this way.
+
+   There doesn't seem to be any reason to do this hard-coded stuff when
+   the header contains 32 bit masks that tell exactly how to extract the
+   3 colors in all cases.
+
+   We know for a fact that 16 bit TrueColor output from XFree86's xwd
+   doesn't match these hard-coded shift amounts, so we have replaced
+   this whole switch thing.  -Bryan 00.03.01
+
+   switch (bits_per_pixel) {
+                
+   case 16:
+       PPM_ASSIGN( *xP,
+                   ( ( ul & red_mask )   >> 0    ),
+                   ( ( ul & green_mask ) >> 5  ),
+                   ( ( ul & blue_mask )  >> 11) );
+       break;
+
+   case 24:
+   case 32:
+       PPM_ASSIGN( *xP, ( ( ul & 0xff0000 ) >> 16 ),
+                   ( ( ul & 0xff00 ) >> 8 ),
+                   ( ul & 0xff ) );
+       break;
+
+   default:
+       pm_error( "True/Direct is valid only with 16, 24, and 32 bits" );
+   }
+*/