Blame converter/ppm/ppmtowinicon.c

Packit 78deda
/* ppmtowinicon.c - read portable pixmap file(s) and write a MS Windows .ico
Packit 78deda
**
Packit 78deda
** Copyright (C) 2000 by Lee Benfield - lee@benf.org
Packit 78deda
**
Packit 78deda
** Permission to use, copy, modify, and distribute this software and its
Packit 78deda
** documentation for any purpose and without fee is hereby granted, provided
Packit 78deda
** that the above copyright notice appear in all copies and that both that
Packit 78deda
** copyright notice and this permission notice appear in supporting
Packit 78deda
** documentation.  This software is provided "as is" without express or
Packit 78deda
** implied warranty.
Packit 78deda
*/
Packit 78deda
Packit 78deda
#include <math.h>
Packit 78deda
#include <string.h>
Packit 38c941
#include <stdlib.h>
Packit 78deda
Packit 78deda
#include "pm_c_util.h"
Packit 78deda
#include "winico.h"
Packit 78deda
#include "ppm.h"
Packit 78deda
#include "mallocvar.h"
Packit 78deda
#include "shhopt.h"
Packit 78deda
#include "nstring.h"
Packit 78deda
Packit 78deda
#define MAJVERSION 0
Packit 78deda
#define MINVERSION 3
Packit 78deda
Packit 78deda
#define MAXCOLORS 256
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
    unsigned int iconCount;
Packit 78deda
    const char **inputFilespec;  /* '-' if stdin; malloc'ed array */
Packit 78deda
    const char **andpgmFilespec;    /* NULL if unspecified; malloc'ed array */
Packit 78deda
    const char *output;     /* '-' if unspecified */
Packit 78deda
    unsigned int truetransparent;
Packit 78deda
    unsigned int verbose;
Packit 78deda
};
Packit 78deda
Packit 78deda
Packit 78deda
static bool verbose;
Packit 78deda
Packit 78deda
static int      file_offset = 0; /* not actually used, but useful for debug. */
Packit 78deda
static FILE *   ofp;
Packit 78deda
Packit 78deda
static void
Packit 78deda
parseCommandLine(int                 argc, 
Packit 78deda
                 char **             argv,
Packit 78deda
                 struct cmdlineInfo *cmdlineP ) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
   Parse program command line described in Unix standard form by argc
Packit 78deda
   and argv.  Return the information in the options as *cmdlineP.  
Packit 78deda
Packit 78deda
   If command line is internally inconsistent (invalid options, etc.),
Packit 78deda
   issue error message to stderr and abort program.
Packit 78deda
Packit 78deda
   Note that the strings we return are stored in the storage that
Packit 78deda
   was passed to us as the argv array.  We also trash *argv.
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 option_def_index;
Packit 78deda
Packit 78deda
    unsigned int outputSpec, andpgms;
Packit 78deda
Packit 78deda
    MALLOCARRAY_NOFAIL(option_def, 100);
Packit 78deda
Packit 78deda
    option_def_index = 0;   /* incremented by OPTENT3 */
Packit 78deda
    OPTENT3(0, "output",     OPT_STRING, &cmdlineP->output,
Packit 78deda
            &outputSpec,                   0);
Packit 78deda
    OPTENT3(0, "andpgms",    OPT_FLAG,   NULL,
Packit 78deda
            &andpgms,                      0);
Packit 78deda
    OPTENT3(0, "truetransparent", OPT_FLAG,   NULL,
Packit 78deda
            &cmdlineP->truetransparent,    0);
Packit 78deda
    OPTENT3(0, "verbose",    OPT_STRING, NULL,
Packit 78deda
            &cmdlineP->verbose,            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 have no parms that are negative numbers */
Packit 78deda
Packit 78deda
    pm_optParseOptions3( &argc, argv, opt, sizeof(opt), 0);
Packit 78deda
        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
Packit 78deda
Packit 78deda
    if (!outputSpec)
Packit 78deda
        cmdlineP->output = "-";
Packit 78deda
Packit 78deda
Packit 78deda
    if (!andpgms) {
Packit 78deda
        if (argc-1 == 0) {
Packit 78deda
            cmdlineP->iconCount = 1;
Packit 78deda
            MALLOCARRAY_NOFAIL(cmdlineP->inputFilespec, cmdlineP->iconCount);
Packit 78deda
            cmdlineP->inputFilespec[0] = "-";
Packit 78deda
        } else {
Packit 78deda
            unsigned int iconIndex;
Packit 78deda
Packit 78deda
            cmdlineP->iconCount = argc-1;
Packit 78deda
            MALLOCARRAY_NOFAIL(cmdlineP->inputFilespec, cmdlineP->iconCount);
Packit 78deda
            for (iconIndex = 0; iconIndex < cmdlineP->iconCount; ++iconIndex)
Packit 78deda
                cmdlineP->inputFilespec[iconIndex] = argv[iconIndex+1];
Packit 78deda
        }
Packit 78deda
        {
Packit 78deda
            unsigned int iconIndex;
Packit 78deda
            MALLOCARRAY_NOFAIL(cmdlineP->andpgmFilespec, cmdlineP->iconCount);
Packit 78deda
            for (iconIndex = 0; iconIndex < cmdlineP->iconCount; ++iconIndex)
Packit 78deda
                cmdlineP->andpgmFilespec[iconIndex] = NULL;
Packit 78deda
        }
Packit 78deda
    } else {
Packit 78deda
        if (argc-1 < 2)
Packit 78deda
            pm_error("with -andpgms, you must specify at least two "
Packit 78deda
                     "arguments: image file name and and mask file name.  "
Packit 78deda
                     "You specified %d", argc-1);
Packit 78deda
        else if ((argc-1)/2*2 != (argc-1))
Packit 78deda
            pm_error("with -andpgms, you must specify an even number of "
Packit 78deda
                     "arguments.  You specified %d", argc-1);
Packit 78deda
        else {
Packit 78deda
            unsigned int iconIndex;
Packit 78deda
            cmdlineP->iconCount = (argc-1)/2;
Packit 78deda
            MALLOCARRAY_NOFAIL(cmdlineP->inputFilespec, cmdlineP->iconCount);
Packit 78deda
            MALLOCARRAY_NOFAIL(cmdlineP->andpgmFilespec, cmdlineP->iconCount);
Packit 78deda
            for (iconIndex = 0; iconIndex < cmdlineP->iconCount; ++iconIndex) {
Packit 78deda
                cmdlineP->inputFilespec[iconIndex] = argv[1 + iconIndex*2];
Packit 78deda
                cmdlineP->andpgmFilespec[iconIndex] = argv[2 + iconIndex*2];
Packit 78deda
            }
Packit 78deda
        }
Packit 78deda
    }
Packit 78deda
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void 
Packit 78deda
PutByte(int const v) {
Packit 78deda
   
Packit 78deda
   if (putc(v, ofp) == EOF)
Packit 78deda
       pm_error("Unable to write byte to output file.");
Packit 78deda
}
Packit 78deda
   
Packit 78deda
Packit 78deda
Packit 78deda
static void 
Packit 78deda
PutShort(short const v) {
Packit 78deda
   
Packit 78deda
   if (pm_writelittleshort(ofp, v) == -1)
Packit 78deda
       pm_error("Unable to write short integer to output file");
Packit 78deda
}
Packit 78deda
   
Packit 78deda
Packit 78deda
Packit 78deda
static void 
Packit 78deda
PutLong(long const v) {
Packit 78deda
   
Packit 78deda
   if (pm_writelittlelong(ofp, v) == -1)
Packit 78deda
       pm_error("Unable to write long integer to output file");
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
   
Packit 78deda
/*
Packit 78deda
 * These have no purpose but to wrapper the Byte, Short & Long 
Packit 78deda
 * functions.
Packit 78deda
 */
Packit 78deda
static void 
Packit 78deda
writeU1 (u1 const v) {
Packit 78deda
   file_offset++;
Packit 78deda
   PutByte(v);
Packit 78deda
}
Packit 78deda
Packit 78deda
static  void 
Packit 78deda
writeU2 (u2 const v) {
Packit 78deda
   file_offset +=2;
Packit 78deda
   PutShort(v);
Packit 78deda
}
Packit 78deda
Packit 78deda
static void 
Packit 78deda
writeU4 (u4 const v) {
Packit 78deda
   file_offset += 4;
Packit 78deda
   PutLong(v);
Packit 78deda
}
Packit 78deda
Packit 78deda
static MS_Ico 
Packit 78deda
createIconFile (void) {
Packit 78deda
   MS_Ico MSIconData;
Packit 78deda
   
Packit 78deda
   MALLOCVAR_NOFAIL(MSIconData);
Packit 78deda
Packit 78deda
   MSIconData->reserved     = 0;
Packit 78deda
   MSIconData->type         = 1;
Packit 78deda
   MSIconData->count        = 0;
Packit 78deda
   MSIconData->entries      = NULL;
Packit 78deda
   return MSIconData;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
/* create andBitmap from pgm */
Packit 78deda
Packit 78deda
static ICON_bmp 
Packit 78deda
createAndBitmap (gray ** const ba, int const cols, int const rows,
Packit 78deda
                 gray const maxval) {
Packit 78deda
   /*
Packit 78deda
    * How wide should the u1 string for each row be?
Packit 78deda
    * each byte is 8 pixels, but must be a multiple of 4 bytes.
Packit 78deda
    */
Packit 78deda
   unsigned int const xBytes = ROUNDUP(cols, 32)/8;
Packit 78deda
   ICON_bmp icBitmap;
Packit 78deda
   int y,x;
Packit 78deda
   u1 ** rowData;
Packit 78deda
Packit 78deda
   MALLOCVAR_NOFAIL(icBitmap);
Packit 78deda
Packit 78deda
   MALLOCARRAY_NOFAIL(rowData, rows);
Packit 78deda
   icBitmap->xBytes = xBytes;
Packit 78deda
   icBitmap->data   = rowData;
Packit 38c941
   overflow2(xBytes, rows);
Packit 78deda
   icBitmap->size   = xBytes * rows;
Packit 78deda
   for (y=0;y
Packit 78deda
      u1 * row;
Packit 78deda
      int byteOn = 0;
Packit 78deda
      int bitOn = 128;
Packit 78deda
Packit 78deda
      MALLOCARRAY_NOFAIL(row, xBytes);
Packit 78deda
Packit 78deda
      memset (row, 0, xBytes);
Packit 78deda
      rowData[rows-y-1] = row;
Packit 78deda
      /* 
Packit 78deda
       * Check there's a bit array, otherwise we're just faking this...
Packit 78deda
       */
Packit 78deda
      if (ba) {
Packit 78deda
     for (x=0;x
Packit 78deda
            /* Black (bit clear) is transparent in PGM alpha maps,
Packit 78deda
             * in ICO bit *set* is transparent.
Packit 78deda
             */
Packit 78deda
            if (ba[y][x] <= maxval/2) row[byteOn] |= bitOn;
Packit 78deda
Packit 78deda
        if (bitOn == 1) {
Packit 78deda
           byteOn++;
Packit 78deda
           bitOn = 128;
Packit 78deda
        } else {
Packit 78deda
           bitOn >>= 1;
Packit 78deda
        }
Packit 78deda
     }
Packit 78deda
      }
Packit 78deda
   }
Packit 78deda
   return icBitmap;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
/*
Packit 78deda
 * Depending on if the image is stored as 1bpp, 4bpp or 8bpp, the 
Packit 78deda
 * encoding mechanism is different.
Packit 78deda
 * 
Packit 78deda
 * I didn't re-use the code from ppmtobmp since I need to keep the
Packit 78deda
 * bitmaps in memory till I've loaded all ppms.
Packit 78deda
 * 
Packit 78deda
 * 8bpp => 1 byte/palette index.
Packit 78deda
 * 4bpp => High Nibble, Low Nibble
Packit 78deda
 * 1bpp => 1 palette value per bit, high bit 1st.
Packit 78deda
 */
Packit 78deda
static ICON_bmp 
Packit 78deda
create1Bitmap (pixel ** const pa, int const cols, int const rows, 
Packit 78deda
               colorhash_table const cht) {
Packit 78deda
   /*
Packit 78deda
    * How wide should the u1 string for each row be?
Packit 78deda
    * each byte is 8 pixels, but must be a multiple of 4 bytes.
Packit 78deda
    */
Packit 78deda
   ICON_bmp icBitmap;
Packit 78deda
   int xBytes,y,x;
Packit 78deda
   int wt = cols;
Packit 78deda
   u1 ** rowData;
Packit 78deda
Packit 78deda
   MALLOCVAR_NOFAIL(icBitmap);
Packit 78deda
   
Packit 78deda
   wt >>= 3;
Packit 78deda
   if (wt & 3) {
Packit 78deda
      wt = (wt & ~3) + 4;
Packit 78deda
   }
Packit 78deda
   xBytes = wt;
Packit 78deda
   MALLOCARRAY_NOFAIL(rowData, rows);
Packit 78deda
   icBitmap->xBytes = xBytes;
Packit 78deda
   icBitmap->data   = rowData;
Packit 78deda
   icBitmap->size   = xBytes * rows;
Packit 78deda
   for (y=0;y
Packit 78deda
      u1 * row;
Packit 78deda
      int byteOn = 0;
Packit 78deda
      int bitOn = 128;
Packit 78deda
      int value;
Packit 78deda
      
Packit 78deda
      MALLOCARRAY_NOFAIL(row, xBytes);
Packit 78deda
      memset (row, 0, xBytes);
Packit 78deda
      rowData[rows-y-1] = row;
Packit 78deda
      /* 
Packit 78deda
       * Check there's a pixel array, otherwise we're just faking this...
Packit 78deda
       */
Packit 78deda
      if (pa) {
Packit 78deda
     for (x=0;x
Packit 78deda
        /*
Packit 78deda
         * So we've got a colorhash_table with two colors in it.
Packit 78deda
         * Which is black?!
Packit 78deda
         * 
Packit 78deda
         * Unless the hashing function changes, 0's black.
Packit 78deda
         */
Packit 78deda
        value = ppm_lookupcolor(cht, &pa[y][x]);
Packit 78deda
        if (!value) {
Packit 78deda
           /* leave black. */
Packit 78deda
        } else {
Packit 78deda
           row[byteOn] |= bitOn;
Packit 78deda
        }
Packit 78deda
        if (bitOn == 1) {
Packit 78deda
           byteOn++;
Packit 78deda
           bitOn = 128;
Packit 78deda
        } else {
Packit 78deda
           bitOn >>= 1;
Packit 78deda
        }
Packit 78deda
     }
Packit 78deda
      }
Packit 78deda
   }
Packit 78deda
   return icBitmap;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
static ICON_bmp 
Packit 78deda
create4Bitmap (pixel ** const pa, int const cols, int const rows,
Packit 78deda
               colorhash_table const cht) {
Packit 78deda
   /*
Packit 78deda
    * How wide should the u1 string for each row be?
Packit 78deda
    * each byte is 8 pixels, but must be a multiple of 4 bytes.
Packit 78deda
    */
Packit 78deda
   ICON_bmp icBitmap;
Packit 78deda
   int xBytes,y,x;
Packit 78deda
   int wt = cols;
Packit 78deda
   u1 ** rowData;
Packit 78deda
Packit 78deda
   MALLOCVAR_NOFAIL(icBitmap);
Packit 78deda
Packit 78deda
   wt >>= 1;
Packit 78deda
   if (wt & 3) {
Packit 78deda
      wt = (wt & ~3) + 4;
Packit 78deda
   }
Packit 78deda
   xBytes = wt;
Packit 78deda
   MALLOCARRAY_NOFAIL(rowData, rows);
Packit 78deda
   icBitmap->xBytes = xBytes;
Packit 78deda
   icBitmap->data   = rowData;
Packit 38c941
   overflow2(xBytes, rows);
Packit 78deda
   icBitmap->size   = xBytes * rows;
Packit 78deda
Packit 78deda
   for (y=0;y
Packit 78deda
      u1 * row;
Packit 78deda
      int byteOn = 0;
Packit 78deda
      int nibble = 1;   /* high nibble = 1, low nibble = 0; */
Packit 78deda
      int value;
Packit 78deda
Packit 78deda
      MALLOCARRAY_NOFAIL(row, xBytes);
Packit 78deda
Packit 78deda
      memset (row, 0, xBytes);
Packit 78deda
      rowData[rows-y-1] = row;
Packit 78deda
      /* 
Packit 78deda
       * Check there's a pixel array, otherwise we're just faking this...
Packit 78deda
       */
Packit 78deda
      if (pa) {
Packit 78deda
     for (x=0;x
Packit 78deda
        value = ppm_lookupcolor(cht, &pa[y][x]);
Packit 78deda
        /*
Packit 78deda
         * Shift it, if we're putting it in the high nibble.
Packit 78deda
         */
Packit 78deda
        if (nibble) {
Packit 78deda
           value <<= 4;
Packit 78deda
        }
Packit 78deda
        row[byteOn] |= value;
Packit 78deda
        if (nibble) {
Packit 78deda
           nibble = 0;
Packit 78deda
        } else {
Packit 78deda
           nibble = 1;
Packit 78deda
           byteOn++;
Packit 78deda
        }
Packit 78deda
     }
Packit 78deda
      }
Packit 78deda
   }
Packit 78deda
   return icBitmap;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static ICON_bmp 
Packit 78deda
create8Bitmap (pixel ** const pa, int const cols, int const rows,
Packit 78deda
               colorhash_table const cht) {
Packit 78deda
   /*
Packit 78deda
    * How wide should the u1 string for each row be?
Packit 78deda
    * each byte is 8 pixels, but must be a multiple of 4 bytes.
Packit 78deda
    */
Packit 78deda
   ICON_bmp icBitmap;
Packit 78deda
   int xBytes,y,x;
Packit 78deda
   int wt = cols;
Packit 78deda
   u1 ** rowData;
Packit 78deda
Packit 78deda
   MALLOCVAR_NOFAIL(icBitmap);
Packit 78deda
Packit 78deda
   if (wt & 3) {
Packit 78deda
      wt = (wt & ~3) + 4;
Packit 78deda
   }
Packit 78deda
   xBytes = wt;
Packit 78deda
   MALLOCARRAY_NOFAIL(rowData, rows);
Packit 78deda
   icBitmap->xBytes = xBytes;
Packit 78deda
   icBitmap->data   = rowData;
Packit 38c941
   overflow2(xBytes, rows);
Packit 78deda
   icBitmap->size   = xBytes * rows;
Packit 78deda
Packit 78deda
   for (y=0;y
Packit 78deda
      u1 * row;
Packit 78deda
Packit 78deda
      MALLOCARRAY_NOFAIL(row, xBytes);
Packit 78deda
      memset (row, 0, xBytes);
Packit 78deda
      rowData[rows-y-1] = row;
Packit 78deda
      /* 
Packit 78deda
       * Check there's a pixel array, otherwise we're just faking this...
Packit 78deda
       */
Packit 78deda
      if (pa) {
Packit 78deda
     for (x=0;x
Packit 78deda
        row[x] = ppm_lookupcolor(cht, &pa[y][x]);
Packit 78deda
     }
Packit 78deda
      }
Packit 78deda
   }
Packit 78deda
   return icBitmap;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static IC_InfoHeader 
Packit 78deda
createInfoHeader(IC_Entry const entry, ICON_bmp const xbmp,
Packit 78deda
                 ICON_bmp const abmp) {
Packit 78deda
   IC_InfoHeader ih;
Packit 78deda
   
Packit 78deda
   MALLOCVAR_NOFAIL(ih);
Packit 78deda
Packit 78deda
   ih->size          = 40;
Packit 78deda
   ih->width         = entry->width;
Packit 78deda
   ih->height        = entry->height * 2;  
Packit 78deda
   ih->planes        = 1;  
Packit 78deda
   ih->bitcount      = entry->bitcount;
Packit 78deda
   ih->compression   = 0;
Packit 78deda
   ih->imagesize     = entry->width * entry->height * 8 / entry->bitcount;
Packit 78deda
   ih->x_pixels_per_m= 0;
Packit 78deda
   ih->y_pixels_per_m= 0;
Packit 78deda
   ih->colors_used   = 1 << entry->bitcount;
Packit 78deda
   ih->colors_important = 0;
Packit 78deda
   return ih;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static IC_Palette 
Packit 78deda
createCleanPalette(void) {
Packit 78deda
   IC_Palette palette;
Packit 78deda
   int x;
Packit 78deda
   
Packit 78deda
   MALLOCVAR_NOFAIL(palette);
Packit 78deda
Packit 78deda
   MALLOCARRAY_NOFAIL(palette->colors, MAXCOLORS);
Packit 78deda
   for (x=0;x
Packit 78deda
      palette->colors[x] = NULL;
Packit 78deda
   }
Packit 78deda
   return palette;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void 
Packit 78deda
addColorToPalette(IC_Palette const palette, int const i,
Packit 78deda
                  int const r, int const g, int const b) {
Packit 78deda
Packit 78deda
    MALLOCVAR_NOFAIL(palette->colors[i]);
Packit 78deda
Packit 78deda
    palette->colors[i]->red      = r;
Packit 78deda
    palette->colors[i]->green    = g;
Packit 78deda
    palette->colors[i]->blue     = b;
Packit 78deda
    palette->colors[i]->reserved = 0;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static ICON_bmp 
Packit 78deda
createBitmap (int const bpp, pixel ** const pa, 
Packit 78deda
              int const cols, int const rows, 
Packit 78deda
              colorhash_table const cht) {
Packit 78deda
    
Packit 78deda
    ICON_bmp retval;
Packit 78deda
    const int assumed_bpp = (pa == NULL) ? 1 : bpp;
Packit 78deda
Packit 78deda
    switch (assumed_bpp) {
Packit 78deda
    case 1:
Packit 78deda
        retval = create1Bitmap (pa,cols,rows,cht);
Packit 78deda
        break;
Packit 78deda
    case 4:
Packit 78deda
        retval = create4Bitmap (pa,cols,rows,cht);
Packit 78deda
        break;
Packit 78deda
    case 8:
Packit 78deda
    default:
Packit 78deda
        retval = create8Bitmap (pa,cols,rows,cht);
Packit 78deda
        break;
Packit 78deda
    }
Packit 78deda
    return retval;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
makePalette(pixel **          const xorPPMarray,
Packit 78deda
            int               const xorCols,
Packit 78deda
            int               const xorRows,
Packit 78deda
            pixval            const xorMaxval,
Packit 78deda
            IC_Palette *      const paletteP,
Packit 78deda
            colorhash_table * const xorChtP,
Packit 78deda
            int *             const colorsP,
Packit 78deda
            const char **     const errorP) {
Packit 78deda
   /*
Packit 78deda
    * Figure out the colormap and turn it into the appropriate GIF
Packit 78deda
    * colormap - this code's pretty much straight from ppmtobpm
Packit 78deda
    */
Packit 78deda
    colorhist_vector xorChv;
Packit 78deda
    unsigned int i;
Packit 78deda
    int colors;
Packit 78deda
    IC_Palette palette = createCleanPalette();
Packit 78deda
Packit 78deda
    if (verbose) pm_message("computing colormap...");
Packit 78deda
    xorChv = ppm_computecolorhist(xorPPMarray, xorCols, xorRows, MAXCOLORS, 
Packit 78deda
                                  &colors);
Packit 78deda
    if (xorChv == NULL)
Packit 78deda
        pm_asprintf(errorP,
Packit 78deda
                    "image has too many colors - try doing a 'pnmquant %d'",
Packit 78deda
                    MAXCOLORS);
Packit 78deda
    else {
Packit 78deda
        *errorP = NULL;
Packit 78deda
Packit 78deda
        if (verbose) pm_message("%d colors found", colors);
Packit 78deda
        
Packit 78deda
        if (verbose && (xorMaxval > 255))
Packit 78deda
            pm_message("maxval is not 255 - automatically rescaling colors");
Packit 78deda
        for (i = 0; i < colors; ++i) {
Packit 78deda
            if (xorMaxval == 255) {
Packit 78deda
                addColorToPalette(palette,i,
Packit 78deda
                                  PPM_GETR(xorChv[i].color),
Packit 78deda
                                  PPM_GETG(xorChv[i].color),
Packit 78deda
                                  PPM_GETB(xorChv[i].color));
Packit 78deda
            } else {
Packit 78deda
                addColorToPalette(palette,i,
Packit 78deda
                                  PPM_GETR(xorChv[i].color) * 255 / xorMaxval,
Packit 78deda
                                  PPM_GETG(xorChv[i].color) * 255 / xorMaxval,
Packit 78deda
                                  PPM_GETB(xorChv[i].color) * 255 / xorMaxval);
Packit 78deda
            }
Packit 78deda
        }
Packit 78deda
        
Packit 78deda
        /* And make a hash table for fast lookup. */
Packit 78deda
        *xorChtP = ppm_colorhisttocolorhash(xorChv, colors);
Packit 78deda
        
Packit 78deda
        ppm_freecolorhist(xorChv);
Packit 78deda
        
Packit 78deda
        *paletteP = palette;
Packit 78deda
        *colorsP = colors;
Packit 78deda
    }
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
getOrFakeAndMap(const char *      const andPgmFname,
Packit 78deda
                int               const xorCols,
Packit 78deda
                int               const xorRows,
Packit 78deda
                gray ***          const andPGMarrayP,
Packit 78deda
                pixval *          const andMaxvalP,
Packit 78deda
                colorhash_table * const andChtP,
Packit 78deda
                const char **     const errorP) {
Packit 78deda
Packit 78deda
    int andRows, andCols;
Packit 78deda
    
Packit 78deda
    if (!andPgmFname) {
Packit 78deda
        /* He's not supplying a bitmap for 'and'.  Fake the bitmap. */
Packit 78deda
        *andPGMarrayP = NULL;
Packit 78deda
        *andMaxvalP   = 1;
Packit 78deda
        *andChtP      = NULL;
Packit 78deda
        *errorP       = NULL;
Packit 78deda
    } else {
Packit 78deda
        FILE * andfile;
Packit 78deda
        andfile = pm_openr(andPgmFname);
Packit 78deda
        *andPGMarrayP = pgm_readpgm(andfile, &andCols, &andRows, andMaxvalP);
Packit 78deda
        pm_close(andfile);
Packit 78deda
Packit 78deda
        if ((andCols != xorCols) || (andRows != xorRows)) {
Packit 78deda
            pm_asprintf(errorP,
Packit 78deda
                        "And mask and image have different dimensions "
Packit 78deda
                        "(%d x %d vs %d x %d).  Aborting.",
Packit 78deda
                        andCols, xorCols, andRows, xorRows);
Packit 78deda
        } else
Packit 78deda
            *errorP = NULL;
Packit 78deda
    }
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
blackenTransparentAreas(pixel ** const xorPPMarray,
Packit 78deda
                        int      const cols,
Packit 78deda
                        int      const rows,
Packit 78deda
                        gray **  const andPGMarray,
Packit 78deda
                        pixval   const andMaxval) {
Packit 78deda
Packit 78deda
    unsigned int row;
Packit 78deda
Packit 78deda
    if (verbose) pm_message("Setting transparent pixels to black");
Packit 78deda
Packit 78deda
    for (row = 0; row < rows; ++row) {
Packit 78deda
        unsigned int col;
Packit 78deda
        for (col = 0; col < cols; ++col) {
Packit 78deda
            if (andPGMarray[row][col] < andMaxval)
Packit 78deda
                /* It's not opaque here; make it black */
Packit 78deda
                PPM_ASSIGN(xorPPMarray[row][col], 0, 0, 0);
Packit 78deda
        }
Packit 78deda
    }
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void 
Packit 78deda
addEntryToIcon(MS_Ico       const MSIconData, 
Packit 78deda
               const char * const xorPpmFname,
Packit 78deda
               const char * const andPgmFname,
Packit 78deda
               bool         const trueTransparent) {
Packit 78deda
Packit 78deda
    IC_Entry entry;
Packit 78deda
    FILE * xorfile;
Packit 78deda
    pixel ** xorPPMarray;
Packit 78deda
    gray ** andPGMarray;
Packit 78deda
    ICON_bmp xorBitmap;
Packit 78deda
    ICON_bmp andBitmap;
Packit 78deda
    int rows, cols;
Packit 78deda
    int bpp, colors;
Packit 78deda
    int entry_cols;
Packit 78deda
    IC_Palette palette;
Packit 78deda
    colorhash_table  xorCht;
Packit 78deda
    colorhash_table  andCht; 
Packit 78deda
    const char * error;
Packit 78deda
   
Packit 78deda
    pixval xorMaxval;
Packit 78deda
    gray andMaxval;
Packit 78deda
Packit 78deda
    MALLOCVAR_NOFAIL(entry);
Packit 78deda
Packit 78deda
   /*
Packit 78deda
    * Read the xor PPM.
Packit 78deda
    */
Packit 78deda
    xorfile = pm_openr(xorPpmFname);
Packit 78deda
    xorPPMarray = ppm_readppm(xorfile, &cols, &rows, &xorMaxval);
Packit 78deda
    pm_close(xorfile);
Packit 78deda
    /*
Packit 78deda
    * Since the entry uses 1 byte to hold the width and height of the icon, the
Packit 78deda
    * image can't be more than 256 x 256.
Packit 78deda
    */
Packit 78deda
    if (rows > 255 || cols > 255) {
Packit 78deda
        pm_error("Max size for a icon is 255 x 255 (1 byte fields).  "
Packit 78deda
                 "%s is %d x %d", xorPpmFname, cols, rows);
Packit 78deda
    }
Packit 78deda
   
Packit 78deda
    if (verbose) pm_message("read PPM: %dw x %dh, maxval = %d", 
Packit 78deda
                            cols, rows, xorMaxval);
Packit 78deda
Packit 78deda
    makePalette(xorPPMarray, cols, rows, xorMaxval, 
Packit 78deda
                &palette, &xorCht, &colors, &error);
Packit 78deda
Packit 78deda
    if (error)
Packit 78deda
        pm_error("Unable to make palette for '%s'.  %s", xorPpmFname, error);
Packit 78deda
   /*
Packit 78deda
    * All the icons I found seemed to pad the palette to the max entries
Packit 78deda
    * for that bitdepth.
Packit 78deda
    * 
Packit 78deda
    * The spec indicates this isn't necessary, but I'll follow this behaviour
Packit 78deda
    * just in case.
Packit 78deda
    */
Packit 78deda
    if (colors < 3) {
Packit 78deda
        bpp = 1;
Packit 78deda
        entry_cols = 2;
Packit 78deda
    } else if (colors < 17) {
Packit 78deda
        bpp = 4;
Packit 78deda
        entry_cols = 16;
Packit 78deda
    } else {
Packit 78deda
        bpp = 8;
Packit 78deda
        entry_cols = 256;
Packit 78deda
    }
Packit 78deda
Packit 78deda
    getOrFakeAndMap(andPgmFname, cols, rows,
Packit 78deda
                    &andPGMarray, &andMaxval, &andCht, &error);
Packit 78deda
    if (error)
Packit 78deda
        pm_error("Error in and map for '%s'.  %s", xorPpmFname, error);
Packit 78deda
Packit 78deda
    if (andPGMarray && trueTransparent)
Packit 78deda
        blackenTransparentAreas(xorPPMarray, cols, rows, 
Packit 78deda
                                andPGMarray, andMaxval);
Packit 78deda
Packit 78deda
    xorBitmap = createBitmap(bpp, xorPPMarray, cols, rows, xorCht);
Packit 78deda
    andBitmap = createAndBitmap(andPGMarray, cols, rows, andMaxval);
Packit 78deda
    /*
Packit 78deda
     * Fill in the entry data fields.
Packit 78deda
    */
Packit 78deda
    entry->width         = cols;
Packit 78deda
    entry->height        = rows;
Packit 78deda
    entry->color_count   = entry_cols;
Packit 78deda
    entry->reserved      = 0;
Packit 78deda
    entry->planes        = 1;
Packit 78deda
    /* 
Packit 78deda
    * all the icons I looked at ignored this value...
Packit 78deda
    */
Packit 78deda
    entry->bitcount      = bpp;
Packit 78deda
    entry->ih            = createInfoHeader(entry, xorBitmap, andBitmap);
Packit 78deda
    entry->colors        = palette->colors;
Packit 38c941
    overflow2(4, entry->color_count);
Packit 38c941
    overflow_add(xorBitmap->size, andBitmap->size);
Packit 38c941
    overflow_add(xorBitmap->size + andBitmap->size, 40);
Packit 38c941
    overflow_add(xorBitmap->size + andBitmap->size + 40, 4 * entry->color_count);
Packit 38c941
    entry->size_in_bytes =
Packit 78deda
        xorBitmap->size + andBitmap->size + 40 + (4 * entry->color_count);
Packit 78deda
    if (verbose) 
Packit 78deda
        pm_message("entry->size_in_bytes = %d + %d + %d = %d",
Packit 78deda
                   xorBitmap->size ,andBitmap->size, 
Packit 78deda
                   40, entry->size_in_bytes );
Packit 78deda
    /*
Packit 78deda
    * We don't know the offset ATM, set to 0 for now.
Packit 78deda
    * Have to calculate this at the end.
Packit 78deda
    */
Packit 78deda
    entry->file_offset   = 0;
Packit 78deda
    entry->xorBitmapOut  = xorBitmap->data;
Packit 78deda
    entry->andBitmapOut  = andBitmap->data;
Packit 78deda
    entry->xBytesXor     = xorBitmap->xBytes;
Packit 78deda
    entry->xBytesAnd     = andBitmap->xBytes;  
Packit 78deda
    /*
Packit 78deda
    * Add the entry to the entries array.
Packit 78deda
    */
Packit 78deda
    ++MSIconData->count;
Packit 78deda
    /* Perhaps I should allocate ahead, and take fewer trips to the well. */
Packit 78deda
    REALLOCARRAY(MSIconData->entries, MSIconData->count);
Packit 78deda
    MSIconData->entries[MSIconData->count-1] = entry;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void 
Packit 78deda
writeIC_Entry (IC_Entry const entry) {
Packit 78deda
   writeU1(entry->width);
Packit 78deda
   writeU1(entry->height);
Packit 78deda
   writeU1(entry->color_count); /* chops 256->0 on its own.. */
Packit 78deda
   writeU1(entry->reserved);
Packit 78deda
   writeU2(entry->planes);
Packit 78deda
   writeU2(entry->bitcount);
Packit 78deda
   writeU4(entry->size_in_bytes);
Packit 78deda
   writeU4(entry->file_offset);
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void 
Packit 78deda
writeIC_InfoHeader (IC_InfoHeader const ih) {
Packit 78deda
   writeU4(ih->size);
Packit 78deda
   writeU4(ih->width);
Packit 78deda
   writeU4(ih->height);
Packit 78deda
   writeU2(ih->planes);
Packit 78deda
   writeU2(ih->bitcount);
Packit 78deda
   writeU4(ih->compression);
Packit 78deda
   writeU4(ih->imagesize);
Packit 78deda
   writeU4(ih->x_pixels_per_m);
Packit 78deda
   writeU4(ih->y_pixels_per_m);
Packit 78deda
   writeU4(ih->colors_used);
Packit 78deda
   writeU4(ih->colors_important);
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void 
Packit 78deda
writeIC_Color (IC_Color const col) {
Packit 78deda
   /* Since the ppm might not have as many colors in it as we'd like,
Packit 78deda
    * (2, 16, 256), stick 0 in the gaps.
Packit 78deda
    * 
Packit 78deda
    * This means that we lose palette information, but that can't be
Packit 78deda
    * helped.  
Packit 78deda
    */
Packit 78deda
   if (col == NULL) {
Packit 78deda
      writeU1(0);
Packit 78deda
      writeU1(0);
Packit 78deda
      writeU1(0);
Packit 78deda
      writeU1(0);
Packit 78deda
   } else {
Packit 78deda
      writeU1(col->blue);
Packit 78deda
      writeU1(col->green);
Packit 78deda
      writeU1(col->red);
Packit 78deda
      writeU1(col->reserved);
Packit 78deda
   }
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
writeBitmap(u1 ** const bitmap, int const xBytes, int const height) {
Packit 78deda
   int y;
Packit 78deda
   for (y = 0;y
Packit 78deda
      fwrite (bitmap[y],1,xBytes,ofp);
Packit 78deda
      file_offset += xBytes;
Packit 78deda
   }
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void 
Packit 78deda
writeMS_Ico(MS_Ico       const MSIconData, 
Packit 78deda
            const char * const outFname) {
Packit 78deda
    int x,y;
Packit 78deda
   
Packit 78deda
    ofp = pm_openw(outFname);
Packit 78deda
Packit 78deda
    writeU2(MSIconData->reserved);
Packit 78deda
    writeU2(MSIconData->type);
Packit 78deda
    writeU2(MSIconData->count);
Packit 78deda
    for (x=0;x<MSIconData->count;x++) writeIC_Entry(MSIconData->entries[x]);
Packit 78deda
    for (x=0;x<MSIconData->count;x++) {
Packit 78deda
        writeIC_InfoHeader(MSIconData->entries[x]->ih);
Packit 78deda
        for (y=0;y<(MSIconData->entries[x]->color_count);y++) {
Packit 78deda
            writeIC_Color(MSIconData->entries[x]->colors[y]);
Packit 78deda
        }
Packit 78deda
        if (verbose) pm_message("writing xor bitmap");
Packit 78deda
        writeBitmap(MSIconData->entries[x]->xorBitmapOut,
Packit 78deda
                    MSIconData->entries[x]->xBytesXor,
Packit 78deda
                    MSIconData->entries[x]->height);
Packit 78deda
        if (verbose) pm_message("writing and bitmap");
Packit 78deda
        writeBitmap(MSIconData->entries[x]->andBitmapOut,
Packit 78deda
                    MSIconData->entries[x]->xBytesAnd,
Packit 78deda
                    MSIconData->entries[x]->height);
Packit 78deda
    }
Packit 78deda
    fclose(ofp);
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
int 
Packit 78deda
main(int argc, char ** argv) {
Packit 78deda
Packit 78deda
    struct cmdlineInfo cmdline;
Packit 78deda
Packit 78deda
    MS_Ico const MSIconDataP = createIconFile();
Packit 78deda
    unsigned int iconIndex;
Packit 78deda
    unsigned int offset;
Packit 78deda
   
Packit 78deda
    ppm_init (&argc, argv);
Packit 78deda
Packit 78deda
    parseCommandLine(argc, argv, &cmdline);
Packit 78deda
Packit 78deda
    verbose = cmdline.verbose;
Packit 78deda
Packit 78deda
    for (iconIndex = 0; iconIndex < cmdline.iconCount; ++iconIndex) {
Packit 78deda
        addEntryToIcon(MSIconDataP, cmdline.inputFilespec[iconIndex],
Packit 78deda
                       cmdline.andpgmFilespec[iconIndex], 
Packit 78deda
                       cmdline.truetransparent);
Packit 78deda
    }
Packit 78deda
    /*
Packit 78deda
     * Now we have to go through and calculate the offsets.
Packit 78deda
     * The first infoheader starts at 6 + count*16 bytes.
Packit 78deda
     */
Packit 78deda
    offset = (MSIconDataP->count * 16) + 6;
Packit 78deda
    for (iconIndex = 0; iconIndex < MSIconDataP->count; ++iconIndex) {
Packit 78deda
        IC_Entry entry = MSIconDataP->entries[iconIndex];
Packit 78deda
        entry->file_offset = offset;
Packit 78deda
        /* 
Packit 78deda
         * Increase the offset by the size of this offset & data.
Packit 78deda
         * this includes the size of the color data.
Packit 78deda
         */
Packit 78deda
        offset += entry->size_in_bytes;
Packit 78deda
    }
Packit 78deda
    /*
Packit 78deda
     * And now, we have to actually SAVE the .ico!
Packit 78deda
     */
Packit 78deda
    writeMS_Ico(MSIconDataP, cmdline.output);
Packit 78deda
Packit 78deda
    free(cmdline.inputFilespec);
Packit 78deda
    free(cmdline.andpgmFilespec);
Packit 78deda
Packit 78deda
    return 0;
Packit 78deda
}
Packit 78deda