Blame converter/other/pnmtopalm/palmtopnm.c

Packit 78deda
/******************************************************************************
Packit 78deda
                             palmtopnm
Packit 78deda
*******************************************************************************
Packit 78deda
  By Bryan Henderson, San Jose, California, June 2004.
Packit 78deda
Packit 78deda
  Inspired by and using methods from Tbmptopnm by Ian Goldberg
Packit 78deda
  <iang@cs.berkeley.edu>, and Bill Janssen <bill@janssen.org>.
Packit 78deda
Packit 78deda
  Major fixes and new capability added by Paul Bolle <pebolle@tiscali.nl>
Packit 78deda
  in late 2004 / early 2005.
Packit 78deda
Packit 78deda
  Bryan's work is contributed to the public domain by its author.
Packit 78deda
******************************************************************************/
Packit 78deda
Packit 78deda
#include <string.h>
Packit 78deda
#include <assert.h>
Packit 78deda
Packit 78deda
#include "pm_c_util.h"
Packit 78deda
#include "pnm.h"
Packit 78deda
#include "shhopt.h"
Packit 78deda
#include "mallocvar.h"
Packit 78deda
Packit 78deda
#include "palm.h"
Packit 78deda
#include "palmcolormap.h"
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
enum PalmCompressionType {
Packit 78deda
    COMPRESSION_NONE, 
Packit 78deda
    COMPRESSION_RLE, 
Packit 78deda
    COMPRESSION_SCANLINE,
Packit 78deda
    COMPRESSION_PACKBITS
Packit 78deda
};
Packit 78deda
Packit 78deda
struct PalmHeader {
Packit 78deda
    unsigned short cols;
Packit 78deda
    unsigned short rows;
Packit 78deda
    unsigned short bytesPerRow;
Packit 78deda
    unsigned short flags;
Packit 78deda
    bool           directColor;
Packit 78deda
        /* The header indicates a direct color raster, either by flag
Packit 78deda
           (the old way) or by pixel format (the new way)
Packit 78deda
        */
Packit 78deda
    bool           hasColormap;
Packit 78deda
    bool           hasTransparency;
Packit 78deda
    unsigned char  pixelSizeCode;
Packit 78deda
    unsigned int   pixelSize;
Packit 78deda
    unsigned char  version;
Packit 78deda
    unsigned int   transparentIndex;
Packit 78deda
    enum PalmCompressionType compressionType;
Packit 78deda
    /* version 3 encoding specific */
Packit 78deda
    unsigned char  size;
Packit 78deda
    unsigned char  pixelFormat;
Packit 78deda
    unsigned short density;
Packit 78deda
    unsigned long  transparentValue;
Packit 78deda
};
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
struct DirectPixelFormat {
Packit 78deda
    unsigned int redbits;
Packit 78deda
    unsigned int greenbits;
Packit 78deda
    unsigned int bluebits;
Packit 78deda
};
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
struct DirectColorInfo {
Packit 78deda
    struct DirectPixelFormat pixelFormat;
Packit 78deda
    ColormapEntry            transparentColor;
Packit 78deda
};
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
struct CmdlineInfo {
Packit 78deda
    /* All the information the user supplied in the command line,
Packit 78deda
       in a form easy for the program to use.
Packit 78deda
    */
Packit 78deda
    const char * inputFilespec;
Packit 78deda
    unsigned int verbose;
Packit 78deda
    unsigned int rendition;
Packit 78deda
    unsigned int showhist;
Packit 78deda
    unsigned int transparent;
Packit 78deda
};
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
parseCommandLine(int argc, const char ** argv,
Packit 78deda
                 struct CmdlineInfo *cmdlineP) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
   Note that the file spec array we return is stored in the storage that
Packit 78deda
   was passed to us as the argv array.
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    optEntry * option_def;
Packit 78deda
        /* Instructions to pm_optParseOptions3 on how to parse our options.
Packit 78deda
         */
Packit 78deda
    optStruct3 opt;
Packit 78deda
Packit 78deda
    unsigned int renditionSpec;
Packit 78deda
Packit 78deda
    unsigned int option_def_index;
Packit 78deda
Packit 78deda
    MALLOCARRAY_NOFAIL(option_def, 100);
Packit 78deda
    
Packit 78deda
    option_def_index = 0;   /* incremented by OPTENTRY */
Packit 78deda
    OPTENT3(0, "verbose",     OPT_FLAG, NULL,
Packit 78deda
            &cmdlineP->verbose,  0);
Packit 78deda
    OPTENT3(0, "showhist",    OPT_FLAG, NULL,
Packit 78deda
            &cmdlineP->showhist, 0);
Packit 78deda
    OPTENT3(0, "transparent",    OPT_FLAG, NULL,
Packit 78deda
            &cmdlineP->transparent, 0);
Packit 78deda
    OPTENT3(0, "rendition",  OPT_UINT, &cmdlineP->rendition, 
Packit 78deda
            &renditionSpec, 0);
Packit 78deda
Packit 78deda
    opt.opt_table = option_def;
Packit 78deda
    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
Packit 78deda
    opt.allowNegNum = FALSE;  /* We may have parms that are negative numbers */
Packit 78deda
Packit 78deda
    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
Packit 78deda
        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
Packit 78deda
Packit 78deda
Packit 78deda
    if (renditionSpec) {
Packit 78deda
        if (cmdlineP->rendition < 1)
Packit 78deda
            pm_error("The -rendition value must be at least 1");
Packit 78deda
    } else 
Packit 78deda
        cmdlineP->rendition = 1;
Packit 78deda
    
Packit 78deda
    if (cmdlineP->transparent && cmdlineP->showhist)
Packit 78deda
        pm_error("You can't specify -showhist with -transparent");
Packit 78deda
Packit 78deda
    if (argc-1 < 1)
Packit 78deda
        cmdlineP->inputFilespec = "-";
Packit 78deda
    else {
Packit 78deda
        cmdlineP->inputFilespec = argv[1];
Packit 78deda
        if (argc-1 > 1)
Packit 78deda
            pm_error("Too many arguments (%d).  The only non-option "
Packit 78deda
                     "argument is the file name", argc-1);
Packit 78deda
    }
Packit 78deda
    free(option_def);
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static xelval *
Packit 78deda
createGraymap(unsigned int const ncolors, 
Packit 78deda
              xelval       const maxval) {
Packit 78deda
    int i;
Packit 78deda
    xelval *map;
Packit 78deda
Packit 78deda
    MALLOCARRAY_NOFAIL(map, ncolors);
Packit 78deda
    for (i = 0; i < ncolors; ++i) {
Packit 78deda
        map[i] = maxval - (i * maxval) / (ncolors - 1);
Packit 78deda
    }
Packit 78deda
    return map;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void 
Packit 78deda
skipbytes(FILE *       const ifP, 
Packit 78deda
          unsigned int const nbytes) {
Packit 78deda
Packit 78deda
    unsigned char buf[256];
Packit 78deda
    unsigned int n;
Packit 78deda
    size_t bytesRead;
Packit 78deda
Packit 78deda
    n = nbytes;  /* initial value */
Packit 78deda
Packit 78deda
    while (n > 0) {
Packit 78deda
        if (n > sizeof(buf)) {
Packit 78deda
            bytesRead = fread(buf, sizeof(char), sizeof(buf), ifP);
Packit 78deda
            if (bytesRead != sizeof(buf))
Packit 78deda
               pm_error("Error reading Palm file.  Short read.");
Packit 78deda
            n -= sizeof(buf);
Packit 78deda
        } else {
Packit 78deda
            bytesRead = fread(buf, sizeof(char), n, ifP);
Packit 78deda
            if (bytesRead != n)
Packit 78deda
               pm_error("Error reading Palm file.  Short read.");
Packit 78deda
            n = 0;
Packit 78deda
        }
Packit 78deda
    }    
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
interpretCompression(unsigned char              const compressionValue,
Packit 78deda
                     enum PalmCompressionType * const compressionTypeP) {
Packit 78deda
    
Packit 78deda
    switch (compressionValue) {
Packit 78deda
    case PALM_COMPRESSION_RLE:
Packit 78deda
        *compressionTypeP = COMPRESSION_RLE;
Packit 78deda
        break;
Packit 78deda
    case PALM_COMPRESSION_SCANLINE:
Packit 78deda
        *compressionTypeP = COMPRESSION_SCANLINE;
Packit 78deda
        break;
Packit 78deda
    case PALM_COMPRESSION_PACKBITS:
Packit 78deda
        *compressionTypeP = COMPRESSION_PACKBITS;
Packit 78deda
        break;
Packit 78deda
    case PALM_COMPRESSION_NONE:
Packit 78deda
        /* according to the spec this is not possible */
Packit 78deda
        *compressionTypeP = COMPRESSION_NONE;
Packit 78deda
        break;
Packit 78deda
    default:
Packit 78deda
        pm_error("The Palm image header has an unrecognized value for "
Packit 78deda
                 "compression type: 0x%02x", (unsigned)compressionValue);
Packit 78deda
    }
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
readRestOfHeaderVersion3(FILE *           const ifP,
Packit 78deda
                         unsigned int     const pixelSize,
Packit 78deda
                         unsigned char *  const sizeP,
Packit 78deda
                         unsigned char *  const pixelFormatP,
Packit 78deda
                         unsigned char *  const compressionTypeP,
Packit 78deda
                         short *          const densityP,
Packit 78deda
                         unsigned int *   const transparentIndexP,
Packit 78deda
                         long *           const transparentValueP,
Packit 78deda
                         long *           const nextBitmapOffsetP,
Packit 78deda
                         short *          const nextDepthOffsetP) {
Packit 78deda
Packit 78deda
    unsigned char unused;
Packit 78deda
    
Packit 78deda
    pm_readcharu(ifP, sizeP);
Packit 78deda
    /* should be 0x18, but I can't see why we should really care */
Packit 78deda
    if (*sizeP != 0x18)
Packit 78deda
        pm_message("Strange value for Palm bitmap header size: %hu", *sizeP);
Packit 78deda
Packit 78deda
    pm_readcharu(ifP, pixelFormatP);
Packit 78deda
    if (*pixelFormatP != PALM_FORMAT_INDEXED &&
Packit 78deda
        *pixelFormatP != PALM_FORMAT_565)
Packit 78deda
        pm_error("Unrecognized pixelformat type: %u", *pixelFormatP);
Packit 78deda
    
Packit 78deda
    pm_readcharu(ifP, &unused);
Packit 78deda
Packit 78deda
Packit 78deda
    pm_readcharu(ifP, compressionTypeP);
Packit 78deda
Packit 78deda
    pm_readbigshort(ifP, densityP);
Packit 78deda
    /* the specs imply that 0x00 is not valid */
Packit 78deda
    if (*densityP != PALM_DENSITY_LOW &&
Packit 78deda
        *densityP != PALM_DENSITY_ONEANDAHALF &&
Packit 78deda
        *densityP != PALM_DENSITY_DOUBLE &&
Packit 78deda
        *densityP != PALM_DENSITY_TRIPLE &&
Packit 78deda
        *densityP != PALM_DENSITY_QUADRUPLE)
Packit 78deda
        pm_error("Invalid value for -density: %d.", *densityP);
Packit 78deda
 
Packit 78deda
    pm_readbiglong(ifP, transparentValueP);
Packit 78deda
    if (pixelSize < 16)
Packit 78deda
        *transparentIndexP = *transparentValueP;
Packit 78deda
    else
Packit 78deda
        *transparentIndexP = 0;
Packit 78deda
 
Packit 78deda
    pm_readbiglong(ifP, nextBitmapOffsetP);
Packit 78deda
    
Packit 78deda
    /* version < 3 specific */
Packit 78deda
    *nextDepthOffsetP = 0;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
readRestOfHeaderOld(FILE *           const ifP,
Packit 78deda
                    unsigned char *  const sizeP,
Packit 78deda
                    unsigned char *  const pixelFormatP,
Packit 78deda
                    unsigned char *  const compressionTypeP,
Packit 78deda
                    short *          const densityP,
Packit 78deda
                    unsigned int *   const transparentIndexP,
Packit 78deda
                    long *           const transparentValueP,
Packit 78deda
                    long *           const nextBitmapOffsetP,
Packit 78deda
                    short *          const nextDepthOffsetP) {
Packit 78deda
    
Packit 78deda
    short pad;
Packit 78deda
    unsigned char transparentIndex;
Packit 78deda
    
Packit 78deda
    pm_readbigshort(ifP, nextDepthOffsetP);
Packit 78deda
    pm_readcharu(ifP, &transparentIndex);
Packit 78deda
    *transparentIndexP = transparentIndex;
Packit 78deda
 
Packit 78deda
    pm_readcharu(ifP,compressionTypeP);
Packit 78deda
    
Packit 78deda
    pm_readbigshort(ifP, &pad;; /* reserved by Palm as of 8/9/00 */
Packit 78deda
    
Packit 78deda
    /* version 3 specific */
Packit 78deda
    *sizeP = 0;
Packit 78deda
    *pixelFormatP = 0;
Packit 78deda
    *densityP = 0;
Packit 78deda
    *transparentValueP = 0;
Packit 78deda
    *nextBitmapOffsetP = 0;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
interpretHeader(struct PalmHeader * const palmHeaderP,
Packit 78deda
                short               const cols,
Packit 78deda
                short               const rows,
Packit 78deda
                short               const bytesPerRow,
Packit 78deda
                short               const flags,
Packit 78deda
                unsigned char       const pixelSizeCode,
Packit 78deda
                unsigned int        const pixelSize,
Packit 78deda
                unsigned char       const version,
Packit 78deda
                unsigned char       const size,
Packit 78deda
                unsigned char       const pixelFormat,
Packit 78deda
                short               const density,
Packit 78deda
                long                const transparentValue,
Packit 78deda
                unsigned int        const transparentIndex,
Packit 78deda
                unsigned char       const compressionType) {
Packit 78deda
    
Packit 78deda
    palmHeaderP->cols = cols;
Packit 78deda
    palmHeaderP->rows = rows;
Packit 78deda
    palmHeaderP->bytesPerRow = bytesPerRow;
Packit 78deda
    palmHeaderP->flags = flags;  /* Just for diagnostics */
Packit 78deda
    palmHeaderP->hasColormap = !!(flags & PALM_HAS_COLORMAP_FLAG);
Packit 78deda
    palmHeaderP->hasTransparency = !!(flags & PALM_HAS_TRANSPARENCY_FLAG);
Packit 78deda
    palmHeaderP->pixelSizeCode = pixelSizeCode;
Packit 78deda
    palmHeaderP->pixelSize = pixelSize;
Packit 78deda
    palmHeaderP->version = version;
Packit 78deda
    palmHeaderP->size = size;
Packit 78deda
    palmHeaderP->pixelFormat = pixelFormat;
Packit 78deda
    palmHeaderP->density = density;
Packit 78deda
    palmHeaderP->transparentValue = transparentValue;
Packit 78deda
    palmHeaderP->transparentIndex = transparentIndex;
Packit 78deda
Packit 78deda
    if (palmHeaderP->version == 3 && ((flags & PALM_DIRECT_COLOR_FLAG) &&
Packit 78deda
                                      (pixelFormat != PALM_FORMAT_565)))
Packit 78deda
        /* There's no directColorInfoType section in a version 3 Palm Bitmap
Packit 78deda
           so we also need PALM_FORMAT_565 for this flag to make sense
Packit 78deda
        */
Packit 78deda
        pm_error("PALM_DIRECT_COLOR_FLAG is set but pixelFormat is not"
Packit 78deda
                 "PALM_FORMAT_565.");
Packit 78deda
        
Packit 78deda
    palmHeaderP->directColor = ((flags & PALM_DIRECT_COLOR_FLAG) || 
Packit 78deda
                                palmHeaderP->pixelFormat == PALM_FORMAT_565);
Packit 78deda
    
Packit 78deda
    if (flags & PALM_IS_COMPRESSED_FLAG) 
Packit 78deda
        interpretCompression(compressionType,
Packit 78deda
                             &palmHeaderP->compressionType);
Packit 78deda
    else
Packit 78deda
        palmHeaderP->compressionType = COMPRESSION_NONE;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
readHeader(FILE *              const ifP,
Packit 78deda
           unsigned int        const requestedRendition,
Packit 78deda
           struct PalmHeader * const palmHeaderP) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
   Read the Palm Bitmap header from the file 'ifP'.  Read past all
Packit 78deda
   renditions up to 'requestedRendition' and read the header of that
Packit 78deda
   rendition.  Return the information contained in the header as *palmHeaderP.
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    bool gotHeader;
Packit 78deda
    unsigned int currentRendition;
Packit 78deda
Packit 78deda
    gotHeader = FALSE;
Packit 78deda
    currentRendition = 1;
Packit 78deda
    while (!gotHeader) {
Packit 78deda
        short cols, rows, bytesPerRow, flags, nextDepthOffset, density;
Packit 78deda
        unsigned char pixelSizeCode, version, compressionType, 
Packit 78deda
            size, pixelFormat;
Packit 78deda
        long transparentValue, nextBitmapOffset;
Packit 78deda
        unsigned int pixelSize, transparentIndex;
Packit 78deda
Packit 78deda
        pm_readbigshort(ifP, &cols;;
Packit 78deda
        pm_readbigshort(ifP, &rows);
Packit 78deda
        pm_readbigshort(ifP, &bytesPerRow);
Packit 78deda
        pm_readbigshort(ifP, &flags);
Packit 78deda
 
Packit 78deda
        pm_readcharu(ifP, &pixelSizeCode);
Packit 78deda
        pixelSize = pixelSizeCode == 0 ? 1 : pixelSizeCode;
Packit 78deda
        if (pixelSizeCode != 0x00 &&
Packit 78deda
            pixelSizeCode != 0x01 &&
Packit 78deda
            pixelSizeCode != 0x02 &&
Packit 78deda
            pixelSizeCode != 0x04 &&
Packit 78deda
            pixelSizeCode != 0x08 &&
Packit 78deda
            pixelSizeCode != 0x10 &&
Packit 78deda
            pixelSizeCode != 0xFF)
Packit 78deda
            pm_error("Invalid value for bits per pixel: %u.", pixelSizeCode);
Packit 78deda
Packit 78deda
        if ((bytesPerRow * 8) < (cols * pixelSize))
Packit 78deda
            pm_error("%u bytes per row is not valid with %u columns and %u "
Packit 78deda
                     "bits per pixel.", bytesPerRow, cols, pixelSize);
Packit 78deda
Packit 78deda
        pm_readcharu(ifP, &version);
Packit 78deda
        if (version > 3) 
Packit 78deda
            pm_error("Unknown encoding version type: %d", version);
Packit 78deda
        else if (version == 3)
Packit 78deda
            readRestOfHeaderVersion3(ifP, pixelSize,
Packit 78deda
                                     &size, &pixelFormat, &compressionType,
Packit 78deda
                                     &density, &transparentIndex, 
Packit 78deda
                                     &transparentValue, &nextBitmapOffset,
Packit 78deda
                                     &nextDepthOffset);
Packit 78deda
        else
Packit 78deda
            readRestOfHeaderOld(ifP,
Packit 78deda
                                &size, &pixelFormat, &compressionType,
Packit 78deda
                                &density, &transparentIndex,
Packit 78deda
                                &transparentValue, &nextBitmapOffset,
Packit 78deda
                                &nextDepthOffset);
Packit 78deda
Packit 78deda
        if (currentRendition < requestedRendition) {
Packit 78deda
             if (version < 3 && nextDepthOffset == 0 && pixelSizeCode != 0xFF) 
Packit 78deda
                 pm_error("Not enough renditions in the input Palm Bitmap "
Packit 78deda
                          "to extract the %dth", requestedRendition);
Packit 78deda
             if (version == 3 && nextBitmapOffset == 0) 
Packit 78deda
                 pm_error("Not enough renditions in the input Palm Bitmap "
Packit 78deda
                          "to extract the %dth", requestedRendition);
Packit 78deda
             /* nextDepthOffset is calculated in 4 byte words
Packit 78deda
                from the beginning of this bitmap (so it equals its size) 
Packit 78deda
             */
Packit 78deda
             if (version < 3 && pixelSizeCode != 0xFF )
Packit 78deda
                 skipbytes(ifP, (nextDepthOffset*4)-16);
Packit 78deda
             else if (version == 3)
Packit 78deda
                 /* FIXME rewrite skipbytes to accept longs? */
Packit 78deda
                 skipbytes(ifP, (short) nextBitmapOffset-24); 
Packit 78deda
             if (pixelSizeCode != 0xFF)
Packit 78deda
                 ++currentRendition;
Packit 78deda
        } else if (pixelSizeCode != 0xFF) {
Packit 78deda
            gotHeader = TRUE;
Packit 78deda
            
Packit 78deda
            interpretHeader(palmHeaderP,
Packit 78deda
                            cols, rows, bytesPerRow, flags, pixelSizeCode,
Packit 78deda
                            pixelSize, version, size, pixelFormat, density,
Packit 78deda
                            transparentValue, transparentIndex, 
Packit 78deda
                            compressionType);
Packit 78deda
        }
Packit 78deda
    }
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static const char *
Packit 78deda
yesno(bool const arg) {
Packit 78deda
Packit 78deda
    if (arg)
Packit 78deda
        return "YES";
Packit 78deda
    else
Packit 78deda
        return "NO";
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
reportPalmHeader(struct PalmHeader      const palmHeader,
Packit 78deda
                 struct DirectColorInfo const directColorInfo) {
Packit 78deda
Packit 78deda
    const char *ctype;
Packit 78deda
Packit 78deda
    switch (palmHeader.compressionType) {
Packit 78deda
    case COMPRESSION_RLE:
Packit 78deda
        ctype = "rle (Palm OS 3.5)";
Packit 78deda
        break;
Packit 78deda
    case COMPRESSION_SCANLINE:
Packit 78deda
        ctype = "scanline (Palm OS 2.0)";
Packit 78deda
        break;
Packit 78deda
    case COMPRESSION_PACKBITS:
Packit 78deda
        ctype = "packbits (Palm OS 4.0)";
Packit 78deda
        break;
Packit 78deda
    case COMPRESSION_NONE:
Packit 78deda
        ctype = "none";
Packit 78deda
        break;
Packit 78deda
    }
Packit 78deda
    pm_message("Dimensions: %hu columns x %hu rows",
Packit 78deda
               palmHeader.cols, palmHeader.rows);
Packit 78deda
    pm_message("Row layout: %hu bytes per row, %hu bits per pixel",
Packit 78deda
               palmHeader.bytesPerRow, palmHeader.pixelSize);
Packit 78deda
    pm_message("Pixel Size code: %hu", palmHeader.pixelSizeCode);
Packit 78deda
    pm_message("Flags: 0x%04hx", palmHeader.flags);
Packit 78deda
    pm_message("  Direct Color: %s", yesno(palmHeader.directColor));
Packit 78deda
    pm_message("  Colormap:     %s", yesno(palmHeader.hasColormap));
Packit 78deda
    pm_message("  Transparency: %s", yesno(palmHeader.hasTransparency));
Packit 78deda
    pm_message("Version %d", palmHeader.version);
Packit 78deda
    if (palmHeader.hasTransparency) {
Packit 78deda
        if (palmHeader.directColor) {
Packit 78deda
            /* Copied from doTransparent(...) */
Packit 78deda
            ColormapEntry const color = directColorInfo.transparentColor;
Packit 78deda
            pm_message("Transparent value: #%02x%02x%02x", 
Packit 78deda
                       (unsigned int)((color >> 16) & 0xFF), 
Packit 78deda
                       (unsigned int)((color >>  8) & 0xFF), 
Packit 78deda
                       (unsigned int)((color >>  0) & 0xFF));
Packit 78deda
        } else
Packit 78deda
            pm_message("Transparent index: %u", palmHeader.transparentIndex);
Packit 78deda
    }
Packit 78deda
    pm_message("Compression type: %s", ctype);
Packit 78deda
    if (palmHeader.version == 3)
Packit 78deda
        pm_message("Density: %d", palmHeader.density);
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
determineOutputFormat(struct PalmHeader const palmHeader,
Packit 78deda
                      int *             const formatP,
Packit 78deda
                      xelval *          const maxvalP) {
Packit 78deda
Packit 78deda
    if (palmHeader.directColor) {
Packit 78deda
        *formatP = PPM_TYPE;
Packit 78deda
        *maxvalP = 255;
Packit 78deda
    } else if (palmHeader.hasColormap) {
Packit 78deda
        *formatP = PPM_TYPE;
Packit 78deda
        *maxvalP = 255;
Packit 78deda
    } else if (palmHeader.pixelSize == 1) {
Packit 78deda
        *formatP = PBM_TYPE;
Packit 78deda
        *maxvalP = 1;
Packit 78deda
    } else if (palmHeader.pixelSize >= 8) {
Packit 78deda
        *formatP = PPM_TYPE;
Packit 78deda
        *maxvalP = pm_bitstomaxval(palmHeader.pixelSize);
Packit 78deda
    } else {
Packit 78deda
        *formatP = PGM_TYPE;
Packit 78deda
        *maxvalP = pm_bitstomaxval(palmHeader.pixelSize);
Packit 78deda
    }
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
readRgbFormat(FILE *                     const ifP,
Packit 78deda
              struct DirectPixelFormat * const pixelFormatP) {
Packit 78deda
Packit 78deda
    unsigned char r, g, b;
Packit 78deda
Packit 78deda
    pm_readcharu(ifP, &r);
Packit 78deda
    pm_readcharu(ifP, &g);
Packit 78deda
    pm_readcharu(ifP, &b);
Packit 78deda
    
Packit 78deda
    if (r != 5 || g != 6 || b != 5)
Packit 78deda
        pm_error("This image has a direct color pixel format of "
Packit 78deda
                 "%u red, %u green, %u blue bits.  This program "
Packit 78deda
                 "can handle only 5, 6, 5.", r, g, b);
Packit 78deda
    else {
Packit 78deda
        pixelFormatP->redbits   = r;
Packit 78deda
        pixelFormatP->greenbits = g;
Packit 78deda
        pixelFormatP->bluebits  = b;
Packit 78deda
    }
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
readDirectTransparentColor(FILE *          const ifP,
Packit 78deda
                           ColormapEntry * const colorP) {
Packit 78deda
Packit 78deda
    unsigned char r, g, b;
Packit 78deda
Packit 78deda
    pm_readcharu(ifP, &r);
Packit 78deda
    pm_readcharu(ifP, &g);
Packit 78deda
    pm_readcharu(ifP, &b);
Packit 78deda
Packit 78deda
    *colorP = (r << 16) | (g << 8) | (b << 0);
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
readDirectInfoType(FILE *                   const ifP,
Packit 78deda
                   struct PalmHeader        const palmHeader,
Packit 78deda
                   struct DirectColorInfo * const directInfoTypeP) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
   Read the Palm Bitmap Direct Info Type section, if any.
Packit 78deda
Packit 78deda
   The Direct Info Type section is a section of a pre-Version 3 direct
Packit 78deda
   color Palm Bitmap that tells how to interpret the direct color
Packit 78deda
   raster.
Packit 78deda
Packit 78deda
   Return an undefined value as *directInfoTypeP if there is no such
Packit 78deda
   section in this Palm Bitmap.
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    if ((palmHeader.directColor) && palmHeader.pixelSize != 16)
Packit 78deda
        pm_error("The image is of the direct color type, but has %u "
Packit 78deda
                 "bits per pixel.  The only kind of direct color images "
Packit 78deda
                 "this program understands are 16 bit ones.", 
Packit 78deda
                 palmHeader.pixelSize);
Packit 78deda
Packit 78deda
    if (palmHeader.version == 3) {
Packit 78deda
        /* All direct color info is in the header, because it'sversion
Packit 78deda
           3 encoding.  No Direct Info Type section.  
Packit 78deda
        */
Packit 78deda
    } else {
Packit 78deda
        if (palmHeader.directColor) {
Packit 78deda
            unsigned char padding;
Packit 78deda
Packit 78deda
            readRgbFormat(ifP, &directInfoTypeP->pixelFormat);
Packit 78deda
Packit 78deda
            pm_readcharu(ifP, &padding);
Packit 78deda
            pm_readcharu(ifP, &padding);
Packit 78deda
Packit 78deda
            readDirectTransparentColor(ifP, 
Packit 78deda
                                       &directInfoTypeP->transparentColor);
Packit 78deda
        } else {
Packit 78deda
            /* Not a direct color image; no Direct Info Type section. */
Packit 78deda
        }
Packit 78deda
    }
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
readColormap(FILE *            const ifP,
Packit 78deda
             struct PalmHeader const palmHeader,
Packit 78deda
             Colormap **       const colormapPP) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
   Read the colormap, if any from the Palm Bitmap.
Packit 78deda
Packit 78deda
   If the image described by 'palmHeader' doesn't have a colormap,
Packit 78deda
   return an undefined value as *colormapP.
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    if (palmHeader.hasColormap)
Packit 78deda
        *colormapPP = palmcolor_read_colormap(ifP);
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
getColorInfo(struct PalmHeader        const palmHeader,
Packit 78deda
             struct DirectColorInfo   const directInfoType,
Packit 78deda
             Colormap *               const colormapFromImageP,
Packit 78deda
             Colormap **              const colormapPP,
Packit 78deda
             unsigned int *           const ncolorsP,
Packit 78deda
             struct DirectColorInfo * const directColorInfoP) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
   Gather color encoding information from the various sources.
Packit 78deda
Packit 78deda
   Note that 'directInfoType' and 'colormapFromImage' are meaningful only
Packit 78deda
   with certain values of 'palmHeader'.
Packit 78deda
Packit 78deda
   If it's a version 3 direct color, the pixel format must be "565".
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    if (palmHeader.version == 3 && palmHeader.directColor) {
Packit 78deda
        *colormapPP = NULL;
Packit 78deda
Packit 78deda
        assert(palmHeader.pixelFormat == PALM_FORMAT_565);
Packit 78deda
Packit 78deda
        directColorInfoP->pixelFormat.redbits   = 5;
Packit 78deda
        directColorInfoP->pixelFormat.greenbits = 6;
Packit 78deda
        directColorInfoP->pixelFormat.bluebits  = 5;
Packit 78deda
        directColorInfoP->transparentColor = 
Packit 78deda
            /* See convertRowToPnmDirect for this trick
Packit 78deda
Packit 78deda
               This will break once maxval isn't always set 255 for
Packit 78deda
               directColor
Packit 78deda
            */
Packit 78deda
            ((((palmHeader.transparentValue >> 11) & 0x1F) * 255 / 0x1F)
Packit 78deda
             << 16) |
Packit 78deda
            ((((palmHeader.transparentValue >>  5) & 0x3F) * 255 / 0x3F)
Packit 78deda
             <<  8) |
Packit 78deda
            ((((palmHeader.transparentValue >>  0) & 0x1F) * 255 / 0x1F)
Packit 78deda
             <<  0);
Packit 78deda
    } else if (palmHeader.directColor) {
Packit 78deda
        *colormapPP = NULL;
Packit 78deda
        *directColorInfoP = directInfoType;
Packit 78deda
    } else if (palmHeader.hasColormap)
Packit 78deda
        *colormapPP = colormapFromImageP;
Packit 78deda
    else if (palmHeader.pixelSize >= 8) {
Packit 78deda
        Colormap * const colormapP =
Packit 78deda
            palmcolor_build_default_8bit_colormap();
Packit 78deda
        qsort(colormapP->color_entries, colormapP->ncolors, 
Packit 78deda
              sizeof(ColormapEntry), palmcolor_compare_indices);
Packit 78deda
        *colormapPP = colormapP;
Packit 78deda
    } else
Packit 78deda
        *colormapPP = NULL;
Packit 78deda
Packit 78deda
    *ncolorsP = 1 << palmHeader.pixelSize;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
doTransparent(FILE *                 const ofP,
Packit 78deda
              bool                   const hasTransparency,
Packit 78deda
              bool                   const directColor,
Packit 78deda
              unsigned char          const transparentIndex,
Packit 78deda
              unsigned char          const pixelSize,
Packit 78deda
              Colormap *             const colormapP,
Packit 78deda
              struct DirectColorInfo const directColorInfo) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
   Generate a PNM comment on *ofP telling what color in the raster is
Packit 78deda
   supposed to be transparent.
Packit 78deda
Packit 78deda
   Note that PNM itself doesn't have any way to represent transparency.
Packit 78deda
   (But this program could be converted to a PAM program and use the
Packit 78deda
   RGB_ALPHA and GRAYSCALE_ALPHA tuple types).
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    if (hasTransparency) {
Packit 78deda
        if (colormapP) {
Packit 78deda
            ColormapEntry const searchTarget = transparentIndex << 24;
Packit 78deda
            ColormapEntry * const foundEntryP =
Packit 78deda
                bsearch(&searchTarget,
Packit 78deda
                        colormapP->color_entries, 
Packit 78deda
                        colormapP->ncolors,
Packit 78deda
                        sizeof(searchTarget), 
Packit 78deda
                        palmcolor_compare_indices);
Packit 78deda
            if (!foundEntryP)
Packit 78deda
                pm_error("Invalid input; transparent index %u "
Packit 78deda
                         "is not among the %u colors in the image's colormap",
Packit 78deda
                         transparentIndex, colormapP->ncolors);
Packit 78deda
Packit 78deda
            fprintf(ofP, "#%02x%02x%02x\n", 
Packit 78deda
                   (unsigned int) ((*foundEntryP >> 16) & 0xFF),
Packit 78deda
                   (unsigned int) ((*foundEntryP >>  8) & 0xFF), 
Packit 78deda
                   (unsigned int) ((*foundEntryP >>  0) & 0xFF));
Packit 78deda
        } else if (directColor) {
Packit 78deda
            ColormapEntry const color = directColorInfo.transparentColor;
Packit 78deda
            fprintf(ofP, "#%02x%02x%02x\n", 
Packit 78deda
                   (unsigned int)((color >> 16) & 0xFF), 
Packit 78deda
                   (unsigned int)((color >>  8) & 0xFF), 
Packit 78deda
                   (unsigned int)((color >>  0) & 0xFF));
Packit 78deda
        } else {
Packit 78deda
            unsigned int const maxval = pm_bitstomaxval(pixelSize);
Packit 78deda
            unsigned int const grayval = 
Packit 78deda
                ((maxval - transparentIndex) * 256) / maxval;
Packit 78deda
            fprintf(ofP, "#%02x%02x%02x\n", grayval, grayval, grayval);
Packit 78deda
        }
Packit 78deda
    }
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
createHistogram(unsigned int    const ncolors,
Packit 78deda
                unsigned int ** const seenP) {
Packit 78deda
Packit 78deda
    unsigned int * seen;
Packit 78deda
Packit 78deda
    MALLOCARRAY(seen, ncolors);
Packit 78deda
    if (!seen)
Packit 78deda
        pm_error("Can't allocate array for keeping track of "
Packit 78deda
                 "how many pixels of each of %u colors are in the image.", 
Packit 78deda
                 ncolors);
Packit 78deda
Packit 78deda
    {    
Packit 78deda
        /* Initialize the counter for each color to zero */
Packit 78deda
        unsigned int i;
Packit 78deda
        for (i = 0; i < ncolors; ++i)
Packit 78deda
            seen[i] = 0;
Packit 78deda
    }
Packit 78deda
    *seenP = seen;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
readScanlineRow(FILE *          const ifP,
Packit 78deda
                unsigned char * const palmrow,
Packit 78deda
                unsigned char * const lastrow,
Packit 78deda
                unsigned int    const bytesPerRow,
Packit 78deda
                bool            const firstRow) {
Packit 78deda
Packit 78deda
    unsigned int j;
Packit 78deda
Packit 78deda
    for (j = 0; j < bytesPerRow; j += 8) {
Packit 78deda
        unsigned char diffmask;
Packit 78deda
            /* A mask telling whether each of the 8 raster bytes indexed
Packit 78deda
               j through j+7 is the same as in the previous row ('lastrow')
Packit 78deda
               or is to be read from the file.  Bit 0 of the mask refers
Packit 78deda
               to byte j, Bit 1 to byte j + 1, etc.
Packit 78deda
            */
Packit 78deda
        unsigned int byteCount;
Packit 78deda
            /* How many bytes are covered by 'diffmask'.  Normally 8, but
Packit 78deda
               at the end of the row, could be less.
Packit 78deda
            */
Packit 78deda
        unsigned int k;
Packit 78deda
Packit 78deda
        pm_readcharu(ifP, &diffmask);
Packit 78deda
        byteCount = MIN(bytesPerRow - j, 8);
Packit 78deda
        
Packit 78deda
        for (k = 0; k < byteCount; ++k) {
Packit 78deda
            /* the first row cannot be compressed */
Packit 78deda
            if (firstRow || ((diffmask & (1 << (7 - k))) != 0)) {
Packit 78deda
                unsigned char inval;
Packit 78deda
                pm_readcharu(ifP, &inval);
Packit 78deda
                palmrow[j + k] = inval;
Packit 78deda
            } else
Packit 78deda
                palmrow[j + k] = lastrow[j + k];
Packit 78deda
        }
Packit 78deda
    }
Packit 78deda
    memcpy(lastrow, palmrow, bytesPerRow);
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
readRleRow(FILE *          const ifP,
Packit 78deda
           unsigned char * const palmrow,
Packit 78deda
           unsigned int    const bytesPerRow) {
Packit 78deda
Packit 78deda
    unsigned int j;
Packit 78deda
Packit 78deda
    for (j = 0;  j < bytesPerRow; ) {
Packit 78deda
        unsigned char incount;
Packit 78deda
        unsigned char inval;
Packit 78deda
Packit 78deda
        pm_readcharu(ifP, &incount);
Packit 78deda
        if (incount == 0)
Packit 78deda
            pm_error("Invalid (zero) count in RLE compression.");
Packit 78deda
        if (j + incount > bytesPerRow)
Packit 78deda
            pm_error("Bytes in RLE compressed row exceed bytes per row.  "
Packit 78deda
                     "Bytes per row is %u.  A run length of %u bytes "
Packit 78deda
                     "pushes the bytes in this row up to %u bytes (and then "
Packit 78deda
                     "we gave up).", bytesPerRow, incount, j + incount);
Packit 78deda
        pm_readcharu(ifP, &inval);
Packit 78deda
        memset(palmrow + j, inval, incount);
Packit 78deda
        j += incount;
Packit 78deda
    }
Packit 78deda
} 
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
readPackBitsRow16(FILE *          const ifP,
Packit 78deda
                  unsigned char * const palmrow,
Packit 78deda
                  unsigned int    const bytesPerRow) {
Packit 78deda
Packit 78deda
    /*  From the Palm OS Programmer's API Reference:
Packit 78deda
  
Packit 78deda
        Although the [...] spec is byte-oriented, the 16-bit algorithm is
Packit 78deda
        identical [to the 8-bit algorithm]: just substitute "word" for "byte".
Packit 78deda
    */
Packit 78deda
    unsigned int j;
Packit 78deda
Packit 78deda
    for (j = 0;  j < bytesPerRow; ) {
Packit 78deda
        char incount;
Packit 78deda
        pm_readchar(ifP, &incount);
Packit 78deda
        if (incount < 0) { 
Packit 78deda
            /* How do we handle incount == -128 ? */
Packit 78deda
            unsigned int const runlength = (-incount + 1) * 2;
Packit 78deda
            unsigned int k;
Packit 78deda
            unsigned short inval;
Packit 78deda
            pm_readlittleshortu(ifP, &inval);
Packit 78deda
            if (j + runlength <= bytesPerRow) {
Packit 78deda
                for (k = 0; k < runlength; k += 2)
Packit 78deda
                    memcpy(palmrow + j + k, &inval, 2);
Packit 78deda
            }
Packit 78deda
            j += runlength;
Packit 78deda
        } else {
Packit 78deda
            /* We just read the stream of shorts as a stream of chars */
Packit 78deda
            unsigned int const nonrunlength = (incount + 1) * 2;
Packit 78deda
            unsigned int k;
Packit 78deda
            for (k = 0; (k < nonrunlength) && (j + k <= bytesPerRow); ++k) {
Packit 78deda
                unsigned char inval;
Packit 78deda
                pm_readcharu(ifP, &inval);
Packit 78deda
                palmrow[j + k] = inval;
Packit 78deda
            }
Packit 78deda
            j += nonrunlength;
Packit 78deda
        }
Packit 78deda
        if (j > bytesPerRow) 
Packit 78deda
            pm_error("Bytes in PackBits compressed row exceed bytes per row.  "
Packit 78deda
                     "Bytes per row is %u.  "
Packit 78deda
                     "The bytes in this row were pushed up to %u bytes "
Packit 78deda
                     "(and then we gave up).", bytesPerRow, j);
Packit 78deda
    }
Packit 78deda
} 
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
readPackBitsRow(FILE *          const ifP,
Packit 78deda
                unsigned char * const palmrow,
Packit 78deda
                unsigned int    const bytesPerRow) {
Packit 78deda
Packit 78deda
    unsigned int j;
Packit 78deda
Packit 78deda
    for (j = 0;  j < bytesPerRow; ) {
Packit 78deda
        char incount;
Packit 78deda
        pm_readchar(ifP, &incount);
Packit 78deda
        if (incount < 0) { 
Packit 78deda
            /* How do we handle incount == -128 ? */
Packit 78deda
            unsigned int const runlength = -incount + 1;
Packit 78deda
            unsigned char inval;
Packit 78deda
            pm_readcharu(ifP, &inval);
Packit 78deda
            if (j + runlength <= bytesPerRow)
Packit 78deda
                memset(palmrow + j, inval, runlength);
Packit 78deda
            j += runlength;
Packit 78deda
        } else {
Packit 78deda
            unsigned int const nonrunlength = incount + 1;
Packit 78deda
            unsigned int k;
Packit 78deda
            for (k = 0; k < nonrunlength && j + k <= bytesPerRow; ++k) {
Packit 78deda
                unsigned char inval;
Packit 78deda
                pm_readcharu(ifP, &inval);
Packit 78deda
                palmrow[j + k] = inval;
Packit 78deda
            }
Packit 78deda
            j += nonrunlength;
Packit 78deda
        }
Packit 78deda
        if (j > bytesPerRow) 
Packit 78deda
            pm_error("Bytes in PackBits compressed row exceed bytes per row.  "
Packit 78deda
                     "Bytes per row is %u.  "
Packit 78deda
                     "The bytes in this row were pushed up to %u bytes "
Packit 78deda
                     "(and then we gave up).", bytesPerRow, j);
Packit 78deda
    }
Packit 78deda
} 
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
readUncompressedRow(FILE *          const ifP,
Packit 78deda
                    unsigned char * const palmrow,
Packit 78deda
                    unsigned int    const bytesPerRow) {
Packit 78deda
Packit 78deda
    int bytesRead;
Packit 78deda
    
Packit 78deda
    bytesRead = fread(palmrow, 1, bytesPerRow, ifP);
Packit 78deda
    if (bytesRead != bytesPerRow)
Packit 78deda
        pm_error("Error reading Palm file.  Short read.");
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
readDecompressedRow(FILE *                   const ifP,
Packit 78deda
                    unsigned char *          const palmrow,
Packit 78deda
                    unsigned char *          const lastrow,
Packit 78deda
                    enum PalmCompressionType const compressionType,
Packit 78deda
                    unsigned int             const bytesPerRow,
Packit 78deda
                    unsigned int             const pixelSize,
Packit 78deda
                    bool                     const firstRow) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
   Read a row from Palm file 'ifP', in uncompressed form (i.e. decompress if
Packit 78deda
   necessary).  Assume the row contains 'bytesPerRow' uncompressed bytes,
Packit 78deda
   compressed according to 'compressionType'.  Return the data at 'palmrow'.
Packit 78deda
Packit 78deda
   'firstRow' means decompress it as if it is the first row of the image
Packit 78deda
   (some compression schemes transform the first row differently from the
Packit 78deda
   rest, because each row depends on the row before it).
Packit 78deda
Packit 78deda
   If 'compressionType' is COMPRESSION_SCANLINE, (which means
Packit 78deda
   transformation of a row depends on the contents of the row before
Packit 78deda
   it), then 'lastRow' is as input the uncompressed contents of the
Packit 78deda
   previous row (undefined if 'firstRow' is true).  In that case, we
Packit 78deda
   modify 'lastrow' to contain a copy of 'palmrow' (so Caller can
Packit 78deda
   conveniently use it to read the next row).
Packit 78deda
Packit 78deda
   If 'compressionType' is not COMPRESSION_SCANLINE, 'lastrow' is
Packit 78deda
   undefined both as input and output.
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    switch (compressionType) {
Packit 78deda
    case COMPRESSION_RLE:
Packit 78deda
        readRleRow(ifP, palmrow, bytesPerRow);
Packit 78deda
        break;
Packit 78deda
    case COMPRESSION_SCANLINE:
Packit 78deda
        readScanlineRow(ifP, palmrow, lastrow, bytesPerRow, firstRow);
Packit 78deda
        break;
Packit 78deda
    case COMPRESSION_PACKBITS:
Packit 78deda
        if (pixelSize != 16)
Packit 78deda
            readPackBitsRow(ifP, palmrow, bytesPerRow);
Packit 78deda
        else
Packit 78deda
            readPackBitsRow16(ifP, palmrow, bytesPerRow);
Packit 78deda
        break;
Packit 78deda
    case COMPRESSION_NONE:
Packit 78deda
        readUncompressedRow(ifP, palmrow, bytesPerRow);
Packit 78deda
        break;
Packit 78deda
    }
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
convertRowToPnmDirect(const unsigned char * const palmrow,
Packit 78deda
                      xel *                 const xelrow,
Packit 78deda
                      unsigned int          const cols,
Packit 78deda
                      xelval                const maxval,
Packit 78deda
                      unsigned int *        const seen) {
Packit 78deda
Packit 78deda
    /* There's a problem with this.  Take the Palm 16-bit
Packit 78deda
       direct color.  That's 5 bits for the red, 6 for the
Packit 78deda
       green, and 5 for the blue.  So what should the MAXVAL
Packit 78deda
       be?  I decided to use 255 (8 bits) for everything,
Packit 78deda
       since that's the theoretical max of the number of bits
Packit 78deda
       in any one color, according to Palm.  So the Palm color
Packit 78deda
       0xFFFF (white) would be red=0x1F, green=0x3F, and
Packit 78deda
       blue=0x1F.  How do we promote those colors?  Simple
Packit 78deda
       shift would give us R=248,G=252,B=248; which is
Packit 78deda
       slightly green.  Hardly seems right.
Packit 78deda
       
Packit 78deda
       So I've perverted the math a bit.  Each color value is
Packit 78deda
       multiplied by 255, then divided by either 31 (red or
Packit 78deda
       blue) or 63 (green).  That's the right way to do it
Packit 78deda
       anyway.  
Packit 78deda
    */
Packit 78deda
Packit 78deda
    const unsigned char *inbyte;
Packit 78deda
    unsigned int j;
Packit 78deda
    
Packit 78deda
    for (inbyte = palmrow, j = 0;  j < cols;  ++j) {
Packit 78deda
        unsigned int inval;
Packit 78deda
        inval = *inbyte++ << 8;
Packit 78deda
        inval |= *inbyte++;
Packit 78deda
        
Packit 78deda
        if (seen)
Packit 78deda
            ++seen[inval];
Packit 78deda
        
Packit 78deda
        PPM_ASSIGN(xelrow[j], 
Packit 78deda
                   (((inval >> 11) & 0x1F) * maxval) / 0x1F, 
Packit 78deda
                   (((inval >>  5) & 0x3F) * maxval) / 0x3F, 
Packit 78deda
                   (((inval >>  0) & 0x1F) * maxval) / 0x1F
Packit 78deda
            );
Packit 78deda
    }
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
convertRowToPnmNotDirect(const unsigned char * const palmrow,
Packit 78deda
                         xel *                 const xelrow,
Packit 78deda
                         unsigned int          const cols,
Packit 78deda
                         Colormap *            const colormapP,
Packit 78deda
                         xelval *              const graymap,
Packit 78deda
                         unsigned int *        const seen,
Packit 78deda
                         unsigned int          const pixelSize) {
Packit 78deda
Packit 78deda
    unsigned int const mask = (1 << pixelSize) - 1;
Packit 78deda
Packit 78deda
    const unsigned char * inbyteP;
Packit 78deda
    unsigned int inbit;
Packit 78deda
    unsigned int j;
Packit 78deda
Packit 78deda
    assert(pixelSize <= 8);
Packit 78deda
    
Packit 78deda
    inbit = 8 - pixelSize;
Packit 78deda
    inbyteP = &palmrow[0];
Packit 78deda
    for (j = 0; j < cols; ++j) {
Packit 78deda
        short const color = (*inbyteP & (mask << inbit)) >> inbit;
Packit 78deda
        if (seen)
Packit 78deda
            ++seen[color];
Packit 78deda
        
Packit 78deda
        if (colormapP) {
Packit 78deda
            ColormapEntry const searchTarget = color << 24;
Packit 78deda
            ColormapEntry * const foundEntryP =
Packit 78deda
                bsearch(&searchTarget,
Packit 78deda
                        colormapP->color_entries, 
Packit 78deda
                        colormapP->ncolors,
Packit 78deda
                        sizeof(searchTarget), 
Packit 78deda
                        palmcolor_compare_indices);
Packit 78deda
Packit 78deda
            if (!foundEntryP)
Packit 78deda
                pm_error("Invalid input.  A color index in column %u "
Packit 78deda
                         "is %u, which is not among the %u colors "
Packit 78deda
                         "in the colormap",
Packit 78deda
                         j, color, colormapP->ncolors);
Packit 78deda
Packit 78deda
            PPM_ASSIGN(xelrow[j], 
Packit 78deda
                       (*foundEntryP >> 16) & 0xFF, 
Packit 78deda
                       (*foundEntryP >>  8) & 0xFF, 
Packit 78deda
                       (*foundEntryP >>  0) & 0xFF);
Packit 78deda
        } else
Packit 78deda
            PNM_ASSIGN1(xelrow[j], graymap[color]);
Packit 78deda
        
Packit 78deda
        if (!inbit) {
Packit 78deda
            ++inbyteP;
Packit 78deda
            inbit = 8 - pixelSize;
Packit 78deda
        } else
Packit 78deda
            inbit -= pixelSize;
Packit 78deda
    }
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
writePnm(FILE *            const ofP,
Packit 78deda
         struct PalmHeader const palmHeader,
Packit 78deda
         FILE *            const ifP,
Packit 78deda
         Colormap *        const colormapP,
Packit 78deda
         xelval *          const graymap,
Packit 78deda
         unsigned int      const nColors,
Packit 78deda
         int               const format,
Packit 78deda
         xelval            const maxval,
Packit 78deda
         unsigned int **   const seenP) {
Packit 78deda
Packit 78deda
    int const cols = palmHeader.cols;
Packit 78deda
    int const rows = palmHeader.rows;
Packit 78deda
Packit 78deda
    unsigned char * palmrow;
Packit 78deda
    unsigned char * lastrow;
Packit 78deda
    xel *           xelrow;
Packit 78deda
    unsigned int *  seen;
Packit 78deda
    unsigned int    row;
Packit 78deda
    
Packit 78deda
    pnm_writepnminit(ofP, cols, rows, maxval, format, 0);
Packit 78deda
    xelrow = pnm_allocrow(cols);
Packit 78deda
Packit 78deda
    /* Read the picture data, one row at a time */
Packit 78deda
    MALLOCARRAY_NOFAIL(palmrow, palmHeader.bytesPerRow);
Packit 78deda
    MALLOCARRAY_NOFAIL(lastrow, palmHeader.bytesPerRow); 
Packit 78deda
    
Packit 78deda
    if (seenP) {
Packit 78deda
        createHistogram(nColors, &seen);
Packit 78deda
        *seenP = seen;
Packit 78deda
    } else
Packit 78deda
        seen = NULL;
Packit 78deda
Packit 78deda
    /* We should actually use compressedDataSizeNN for checking the sanity 
Packit 78deda
       of the data we're reading ... 
Packit 78deda
    */
Packit 78deda
    if (palmHeader.compressionType != COMPRESSION_NONE) {
Packit 78deda
        if (palmHeader.version < 3) {
Packit 78deda
            short compressedDataSize16;
Packit 78deda
            pm_readbigshort(ifP, &compressedDataSize16);
Packit 78deda
        } else {
Packit 78deda
            long compressedDataSize32;
Packit 78deda
            pm_readbiglong(ifP, &compressedDataSize32);
Packit 78deda
        }
Packit 78deda
    }
Packit 78deda
Packit 78deda
    for (row = 0; row < rows; ++row) {
Packit 78deda
        readDecompressedRow(ifP, palmrow, lastrow, 
Packit 78deda
                            palmHeader.compressionType, 
Packit 78deda
                            palmHeader.bytesPerRow,
Packit 78deda
                            palmHeader.pixelSize,
Packit 78deda
                            row == 0);
Packit 78deda
Packit 78deda
        if (palmHeader.directColor) {
Packit 78deda
            assert(palmHeader.pixelSize == 16);
Packit 78deda
            convertRowToPnmDirect(palmrow, xelrow, cols, maxval, seen);
Packit 78deda
        } else
Packit 78deda
            convertRowToPnmNotDirect(palmrow, xelrow, cols, colormapP, graymap,
Packit 78deda
                                     seen, palmHeader.pixelSize);
Packit 78deda
Packit 78deda
        pnm_writepnmrow(ofP, xelrow, cols, maxval, format, 0);
Packit 78deda
    }
Packit 78deda
    free(lastrow);
Packit 78deda
    free(palmrow);
Packit 78deda
    pnm_freerow(xelrow);
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
showHistogram(unsigned int * const seen,
Packit 78deda
              Colormap *     const colormapP,
Packit 78deda
              const xelval * const graymap,
Packit 78deda
              unsigned int   const ncolors) {
Packit 78deda
Packit 78deda
    unsigned int colorIndex;
Packit 78deda
    
Packit 78deda
    for (colorIndex = 0;  colorIndex < ncolors; ++colorIndex) {
Packit 78deda
        if (!colormapP)
Packit 78deda
            pm_message("%.3d -> %.3d:  %d", 
Packit 78deda
                       colorIndex, graymap[colorIndex], seen[colorIndex]);
Packit 78deda
        else {
Packit 78deda
            ColormapEntry const searchTarget = colorIndex << 24;
Packit 78deda
            ColormapEntry * const foundEntryP =
Packit 78deda
                bsearch(&searchTarget,
Packit 78deda
                        colormapP->color_entries, 
Packit 78deda
                        colormapP->ncolors,
Packit 78deda
                        sizeof(searchTarget), 
Packit 78deda
                        palmcolor_compare_indices);
Packit 78deda
            if (foundEntryP)
Packit 78deda
                pm_message("%.3d -> %ld,%ld,%ld:  %d", colorIndex,
Packit 78deda
                           (*foundEntryP >> 16) & 0xFF,
Packit 78deda
                           (*foundEntryP >> 8) & 0xFF,
Packit 78deda
                           (*foundEntryP & 0xFF), seen[colorIndex]);
Packit 78deda
        }
Packit 78deda
    }
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
int
Packit 78deda
main(int argc, const char **argv) {
Packit 78deda
Packit 78deda
    struct CmdlineInfo cmdline;
Packit 78deda
Packit 78deda
    FILE * ifP;
Packit 78deda
    struct PalmHeader palmHeader;
Packit 78deda
    struct DirectColorInfo directInfoType;
Packit 78deda
    Colormap * colormapFromImageP;
Packit 78deda
    Colormap * colormapP;
Packit 78deda
    struct DirectColorInfo directColorInfo;
Packit 78deda
    int format;
Packit 78deda
    xelval maxval;
Packit 78deda
    unsigned int nColors;
Packit 78deda
Packit 78deda
    pm_proginit(&argc, argv);
Packit 78deda
Packit 78deda
    parseCommandLine(argc, argv, &cmdline);
Packit 78deda
Packit 78deda
    ifP = pm_openr(cmdline.inputFilespec);
Packit 78deda
Packit 78deda
    readHeader(ifP, cmdline.rendition, &palmHeader);
Packit 78deda
Packit 78deda
    readDirectInfoType(ifP, palmHeader, &directInfoType);
Packit 78deda
   
Packit 78deda
    readColormap(ifP, palmHeader, &colormapFromImageP);
Packit 78deda
    
Packit 78deda
    determineOutputFormat(palmHeader, &format, &maxval);
Packit 78deda
    
Packit 78deda
    getColorInfo(palmHeader, directInfoType, colormapFromImageP,
Packit 78deda
                 &colormapP, &nColors, &directColorInfo);
Packit 78deda
Packit 78deda
    if (cmdline.verbose)
Packit 78deda
        reportPalmHeader(palmHeader, directColorInfo);
Packit 78deda
Packit 78deda
    if (cmdline.transparent)
Packit 78deda
        doTransparent(stdout, 
Packit 78deda
                      palmHeader.hasTransparency, palmHeader.directColor,
Packit 78deda
                      palmHeader.transparentIndex, 
Packit 78deda
                      palmHeader.pixelSize, colormapP, directColorInfo);
Packit 78deda
    else {
Packit 78deda
        unsigned int * seen;
Packit 78deda
        xelval * graymap;
Packit 78deda
Packit 78deda
        graymap = createGraymap(nColors, maxval);
Packit 78deda
Packit 78deda
        writePnm(stdout,
Packit 78deda
                 palmHeader, ifP, colormapP, graymap, nColors, format, maxval, 
Packit 78deda
                 cmdline.showhist ? &seen : NULL);
Packit 78deda
        
Packit 78deda
        if (cmdline.showhist)
Packit 78deda
            showHistogram(seen, colormapP, graymap, nColors);
Packit 78deda
        
Packit 78deda
        free(graymap);
Packit 78deda
    }
Packit 78deda
    pm_close(ifP);
Packit 78deda
Packit 78deda
    return 0;
Packit 78deda
}