diff options
Diffstat (limited to 'converter/other')
-rw-r--r-- | converter/other/Makefile | 5 | ||||
-rw-r--r-- | converter/other/cameratopam/camera.c | 2 | ||||
-rw-r--r-- | converter/other/cameratopam/cameratopam.c | 2 | ||||
-rw-r--r-- | converter/other/cameratopam/foveon.c | 4 | ||||
-rw-r--r-- | converter/other/cameratopam/foveon.h | 4 | ||||
-rw-r--r-- | converter/other/ipdb.c | 1 | ||||
-rw-r--r-- | converter/other/pamtogif.c | 4 | ||||
-rw-r--r-- | converter/other/pamtooctaveimg.c | 4 | ||||
-rw-r--r-- | converter/other/pamtopam.c | 2 | ||||
-rw-r--r-- | converter/other/pamtopnm.c | 2 | ||||
-rw-r--r-- | converter/other/pamtosrf.c | 4 | ||||
-rw-r--r-- | converter/other/pamtosvg/pamtosvg.c | 2 | ||||
-rw-r--r-- | converter/other/pamtotiff.c | 2 | ||||
-rw-r--r-- | converter/other/pamtowinicon.c | 1177 | ||||
-rw-r--r-- | converter/other/pamtoxvmini.c | 4 | ||||
-rw-r--r-- | converter/other/pnmtopclxl.c | 2 | ||||
-rw-r--r-- | converter/other/pnmtopng.c | 534 | ||||
-rw-r--r-- | converter/other/pnmtops.c | 82 | ||||
-rw-r--r-- | converter/other/pstopnm.c | 378 | ||||
-rw-r--r-- | converter/other/winicon.h | 82 | ||||
-rw-r--r-- | converter/other/winicontopam.c | 1282 |
21 files changed, 3114 insertions, 465 deletions
diff --git a/converter/other/Makefile b/converter/other/Makefile index 746db87c..35f420f2 100644 --- a/converter/other/Makefile +++ b/converter/other/Makefile @@ -118,12 +118,13 @@ PORTBINARIES = avstopam bmptopnm fitstopnm \ pamtoavs pamtodjvurle pamtofits pamtogif \ pamtohdiff pamtohtmltbl pamtompfont pamtooctaveimg \ pamtopam pamtopfm pamtopnm pamtouil \ - pamtoxvmini \ + pamtowinicon pamtoxvmini \ pbmtopgm pfmtopam \ pgmtopbm pgmtoppm ppmtopgm pnmtoddif \ pnmtopclxl \ pnmtosgi pnmtosir pamtotga pnmtoxwd \ - rlatopam sgitopnm sirtopnm sunicontopnm xwdtopnm zeisstopnm + rlatopam sgitopnm sirtopnm sunicontopnm \ + winicontopam xwdtopnm zeisstopnm BINARIES = \ $(PORTBINARIES) \ diff --git a/converter/other/cameratopam/camera.c b/converter/other/cameratopam/camera.c index ea5eec39..254b6710 100644 --- a/converter/other/cameratopam/camera.c +++ b/converter/other/cameratopam/camera.c @@ -761,7 +761,7 @@ kodak_radc_load_raw() void kodak_jpeg_load_raw() {} #else -static boolean +static bool fill_input_buffer (j_decompress_ptr cinfo) { static char jpeg_buffer[4096]; diff --git a/converter/other/cameratopam/cameratopam.c b/converter/other/cameratopam/cameratopam.c index 54b68a23..71c9c7af 100644 --- a/converter/other/cameratopam/cameratopam.c +++ b/converter/other/cameratopam/cameratopam.c @@ -69,7 +69,7 @@ int fuji_secondary; float cam_mul[4], pre_mul[4], coeff[3][4]; int histogram[3][0x2000]; jmp_buf failure; -bool use_secondary; +int use_secondary; bool verbose; #ifdef USE_LCMS diff --git a/converter/other/cameratopam/foveon.c b/converter/other/cameratopam/foveon.c index 0198940c..78e40baf 100644 --- a/converter/other/cameratopam/foveon.c +++ b/converter/other/cameratopam/foveon.c @@ -141,8 +141,8 @@ parse_foveon(FILE * const ifp) { void -foveon_coeff(bool * const useCoeffP, - float coeff[3][4]) { +foveon_coeff(int * const useCoeffP, + float coeff[3][4]) { static const float foveon[3][3] = { { 1.4032, -0.2231, -0.1016 }, diff --git a/converter/other/cameratopam/foveon.h b/converter/other/cameratopam/foveon.h index 57be2244..f3177e50 100644 --- a/converter/other/cameratopam/foveon.h +++ b/converter/other/cameratopam/foveon.h @@ -10,5 +10,5 @@ void foveon_load_raw(void); void -foveon_coeff(bool * const useCoeffP, - float coeff[3][4]); +foveon_coeff(int * const useCoeffP, + float coeff[3][4]); diff --git a/converter/other/ipdb.c b/converter/other/ipdb.c index 5c1fc314..eec4495a 100644 --- a/converter/other/ipdb.c +++ b/converter/other/ipdb.c @@ -19,6 +19,7 @@ * Authors: Eric A. Howe (mu@trends.net) * Bryan Henderson, 2010 */ +#define _BSD_SOURCE /* Ensure strdup() is in <string.h> */ #include <assert.h> #include <time.h> #include <string.h> diff --git a/converter/other/pamtogif.c b/converter/other/pamtogif.c index 4dac8923..5b9c219c 100644 --- a/converter/other/pamtogif.c +++ b/converter/other/pamtogif.c @@ -220,7 +220,7 @@ closestColor(tuple const color, unsigned int i; unsigned int imin, dmin; - bool fits; + int fits; dmin = UINT_MAX; imin = 0; @@ -1544,7 +1544,7 @@ computeTransparent(char const colorarg[], const char * colorspec; bool exact; tuple transcolor; - bool found; + int found; int colorindex; if (colorarg[0] == '=') { diff --git a/converter/other/pamtooctaveimg.c b/converter/other/pamtooctaveimg.c index b090281d..28bc4cd4 100644 --- a/converter/other/pamtooctaveimg.c +++ b/converter/other/pamtooctaveimg.c @@ -75,13 +75,13 @@ findOrAddColor(tuple const color, colormap *cmapP. If the color isn't in the map, give it a new colormap index, put it in the colormap, and return that. -----------------------------------------------------------------------------*/ - bool found; + int found; int colorIndex; pnm_lookuptuple(&cmapP->pam, cmapP->hash, color, &found, &colorIndex); if (!found) { - bool fits; + int fits; unsigned int plane; colorIndex = cmapP->nColors++; diff --git a/converter/other/pamtopam.c b/converter/other/pamtopam.c index cae54060..9cb82f7a 100644 --- a/converter/other/pamtopam.c +++ b/converter/other/pamtopam.c @@ -17,7 +17,7 @@ int main(int argc, const char * argv[]) { - bool eof; /* no more images in input stream */ + int eof; /* no more images in input stream */ struct pam inpam; /* Input PAM image */ struct pam outpam; /* Output PAM image */ diff --git a/converter/other/pamtopnm.c b/converter/other/pamtopnm.c index ba655b1e..9bb662b7 100644 --- a/converter/other/pamtopnm.c +++ b/converter/other/pamtopnm.c @@ -109,7 +109,7 @@ main(int argc, char *argv[]) { struct cmdlineInfo cmdline; FILE* ifP; - bool eof; /* no more images in input stream */ + int eof; /* no more images in input stream */ struct pam inpam; /* Input PAM image */ struct pam outpam; /* Output PNM image */ diff --git a/converter/other/pamtosrf.c b/converter/other/pamtosrf.c index 19328073..3800d77c 100644 --- a/converter/other/pamtosrf.c +++ b/converter/other/pamtosrf.c @@ -112,7 +112,7 @@ srfAlphaFromTuple(tuple const t, const struct pam * const pamP) { uint8_t retval; - bool haveOpacity; + int haveOpacity; unsigned int opacityPlane; pnm_getopacity(pamP, &haveOpacity, &opacityPlane); @@ -181,7 +181,7 @@ main(int argc, const char * argv[]) { struct cmdlineInfo cmdline; FILE * ifP; struct srf srf; - bool eof; /* No more images in input */ + int eof; /* No more images in input */ unsigned int imageSeq; /* Sequence of current image in input file. First = 0 */ diff --git a/converter/other/pamtosvg/pamtosvg.c b/converter/other/pamtosvg/pamtosvg.c index 72aa4151..adf76801 100644 --- a/converter/other/pamtosvg/pamtosvg.c +++ b/converter/other/pamtosvg/pamtosvg.c @@ -134,7 +134,7 @@ parseCommandLine(int argc, Note that the strings we return are stored in the storage that was passed to us as the argv array. We also trash *argv. --------------------------------------------------------------------------*/ - optEntry *option_def; + optEntry * option_def; /* Instructions to pm_optParseOptions3 on how to parse our options. */ optStruct3 opt; diff --git a/converter/other/pamtotiff.c b/converter/other/pamtotiff.c index 551909a0..0206678d 100644 --- a/converter/other/pamtotiff.c +++ b/converter/other/pamtotiff.c @@ -1055,7 +1055,7 @@ main(int argc, char *argv[]) { const char * inputFileDescription; FILE* ifP; TIFF* tifP; - bool eof; + int eof; unsigned int imageSeq; pnm_init(&argc, argv); diff --git a/converter/other/pamtowinicon.c b/converter/other/pamtowinicon.c new file mode 100644 index 00000000..c67267e4 --- /dev/null +++ b/converter/other/pamtowinicon.c @@ -0,0 +1,1177 @@ +#define _POSIX_SOURCE /* Make sure fdopen() is in <stdio.h> */ +#include <assert.h> +#include <string.h> +#include <stdio.h> + +#include "netpbm/pm_config.h" +#include "netpbm/pm_c_util.h" +#include "netpbm/mallocvar.h" +#include "netpbm/nstring.h" +#include "netpbm/shhopt.h" +#include "netpbm/pm_system.h" +#include "netpbm/pam.h" +#include "winicon.h" + + + +struct CmdlineInfo { + const char * inputFileName; + unsigned int verbose; + int pngthreshold; + unsigned int truetransparent; +}; + + + +static void +parseCommandLine(int argc, const char **argv, + struct CmdlineInfo * const cmdlineP) { + + optEntry * option_def; + unsigned int option_def_index; + optStruct3 opt3; + unsigned int pngthresholdSpec; + + MALLOCARRAY_NOFAIL(option_def, 100); + + option_def_index = 0; + + OPTENT3 (0, "verbose", OPT_FLAG, NULL, + &cmdlineP->verbose, 0); + OPTENT3 (0, "pngthreshold", OPT_UINT, &cmdlineP->pngthreshold, + &pngthresholdSpec, 0); + OPTENT3 (0, "truetransparent", OPT_FLAG, NULL, + &cmdlineP->truetransparent, 0); + + opt3.opt_table = option_def; + opt3.short_allowed = false; + opt3.allowNegNum = false; + + pm_optParseOptions3(&argc, (char **)argv, opt3, sizeof(opt3), 0); + + if (pngthresholdSpec) { + if (UINT_MAX / cmdlineP->pngthreshold < cmdlineP->pngthreshold) + pm_error("-pngthreshold is too large: %u", cmdlineP->pngthreshold); + } else + cmdlineP->pngthreshold = 128; + + if (argc-1 > 0) { + cmdlineP->inputFileName = argv[1]; + + if (argc-1 > 1) + pm_error("Too many arguments: %u. The only non-option " + "argument is the optional input file name", argc-1); + } else + cmdlineP->inputFileName = "-"; + + free(option_def); +} + + + +static bool verbose; + +static unsigned char const pngHeader[] = PNG_HEADER; + + + +struct Palette { + sample color[256][3]; + unsigned int colorCt; + /* Number of colors in color[][] */ + bool tooManyColors; + /* There are too many colors for a BMP palette (more than 256); only + the first 256 are in color[][] + */ +}; + + + +static struct IconDir * +newIconDir() { + + struct IconDir * dirP; + + MALLOCVAR_NOFAIL(dirP); + + dirP->zero = 0; + dirP->type = ICONDIR_TYPE_ICO; + dirP->count = 0; + + dirP->entriesAllocCt = 0; + dirP->entries = NULL; + + return dirP; +} + + + +static void +freeIconDir(struct IconDir * const dirP) { + if (dirP->entries) + free(dirP->entries); + + free(dirP); +} + + + +static void +addToDirectory(struct IconDirEntry * const dirEntryP, + struct IconDir * const dirP) { +/*---------------------------------------------------------------------------- + Add an icon to the icon directory. +-----------------------------------------------------------------------------*/ + if (dirP->count + 1 > dirP->entriesAllocCt) { + /* Out of space in dirP->entries[]. Expand. */ + + dirP->entriesAllocCt += 8; + + REALLOCARRAY(dirP->entries, dirP->entriesAllocCt); + + if (!dirP->entries) + pm_error("Unable to get memory for %u entries " + "in the Icon directory.", dirP->entriesAllocCt); + } + + dirP->entries[dirP->count++] = *dirEntryP; +} + + + +typedef void (GetPixelFn) (tuple ** const tuples, + unsigned int const col, + unsigned int const row, + sample * const pixel); + + + +static GetPixelFn get_grayscalePixel; + +static void +get_grayscalePixel(tuple ** const tuples, + unsigned int const col, + unsigned int const row, + sample * const pixel) { +/*---------------------------------------------------------------------------- + Get a pixel from a grayscale PAM +-----------------------------------------------------------------------------*/ + pixel[0] = tuples[row][col][0]; + pixel[1] = tuples[row][col][0]; + pixel[2] = tuples[row][col][0]; +} + + + +static GetPixelFn get_rgbPixel; + +static void +get_rgbPixel(tuple ** const tuples, + unsigned int const col, + unsigned int const row, + sample * const pixel) { +/*---------------------------------------------------------------------------- + Get a pixel from an RGB PAM +-----------------------------------------------------------------------------*/ + pixel [0] = tuples [row][col][0]; + pixel [1] = tuples [row][col][1]; + pixel [2] = tuples [row][col][2]; +} + + +static bool +andMakesOpaque(const struct pam * const pamP, + tuple ** const tuples, + unsigned int const row, + unsigned int const col, + bool const haveAlpha, + unsigned int const alphaPlane, + bool const haveAnd, + unsigned int const andPlane) { +/*---------------------------------------------------------------------------- + The AND mask makes a pixel opaque +-----------------------------------------------------------------------------*/ + if (haveAnd) + return (pamP->maxval <= tuples[row][col][andPlane]); + else if (haveAlpha) + return (pamP->maxval <= tuples[row][col][alphaPlane]); + else + /* neither alpha channel nor AND mask: full opacity */ + return true; +} + + + +static void +getPalette(struct pam * const pamP, + tuple ** const tuples, + GetPixelFn * const getPixel, + struct Palette * const paletteP) { +/*---------------------------------------------------------------------------- + Create the palette for all the colors in 'tuples'. +-----------------------------------------------------------------------------*/ + unsigned int row; + + paletteP->colorCt = 0; /* initial value */ + paletteP->tooManyColors = false; /* initial value */ + + for (row = 0; pamP->height > row && !paletteP->tooManyColors; ++row) { + unsigned int col; + for (col = 0; pamP->width > col && !paletteP->tooManyColors; ++col) { + sample pixel[3]; + unsigned int i; + + getPixel(tuples, col, row, pixel); + + for (i = 0; i < paletteP->colorCt; ++i) { + if ((paletteP->color[i][0] == pixel[0]) + && (paletteP->color[i][1] == pixel[1]) + && (paletteP->color[i][2] == pixel[2])) + break; + } + if (i == paletteP->colorCt) { + /* We didn't find the color. */ + if (paletteP->colorCt >= 256) { + /* Image exceeds the palette capacity */ + paletteP->tooManyColors = true; + } else { + /* Add the color to the palette */ + paletteP->color[paletteP->colorCt][0] = pixel[0]; + paletteP->color[paletteP->colorCt][1] = pixel[1]; + paletteP->color[paletteP->colorCt][2] = pixel[2]; + + ++paletteP->colorCt; + } + } + } + } +} + + + +static bool +realAlphaNeeded(const struct pam * const pamP, + tuple ** const tuples, + unsigned int const alphaPlane) { +/*---------------------------------------------------------------------------- + A real alpha channel (in contrast to an AND mask) is needed to represent the + image in 'tuples', given that 'alphaPlane' is the alpha plane. + + A real alpha channel is needed if any pixel is translucent (neither opaque + nor transparent). +-----------------------------------------------------------------------------*/ + unsigned int row; + + for (row = 0; row < pamP->height; ++row) { + unsigned int col; + for (col = 0; col < pamP->width; ++col) { + sample const opacity = tuples[row][col][alphaPlane]; + + if (opacity != 0 && opacity != pamP->maxval) + return true; + } + } + return false; +} + + + +static void +writeBmpImageHeader(unsigned int const width, + unsigned int const height, + unsigned int const bpp, + unsigned int const rasterSize, + FILE * const ofP) { +/*---------------------------------------------------------------------------- + + Write BMP image header + + Note: bm_height is sum of rows in XOR mask and AND mask, while + image_size is the size of the AND mask only. + + image_size does not include the sizes of the (optional) palette + and the (mandatory) AND mask. +-----------------------------------------------------------------------------*/ + pm_writelittlelongu (ofP, 40); /* header_size */ + pm_writelittlelongu (ofP, width); /* bm_width */ + pm_writelittlelongu (ofP, height *2); /* bm_height */ + pm_writelittleshortu (ofP, 1); /* color_planes */ + pm_writelittleshortu (ofP, bpp); /* bits_per_pixel */ + pm_writelittlelongu (ofP, BI_RGB); /* compression_method */ + pm_writelittlelongu (ofP, rasterSize); /* image_size */ + pm_writelittlelongu (ofP, 0); /* horizontal_resolution*/ + pm_writelittlelongu (ofP, 0); /* vertical_resolution */ + pm_writelittlelongu (ofP, 0); /* colors_in_palette */ + pm_writelittlelongu (ofP, 0); /* important_colors */ +} + + + +static void +write32BitBmp(const struct pam * const pamP, + tuple ** const tuples, + GetPixelFn * const getPixel, + bool const haveAlpha, + unsigned int const alphaPlane, + FILE * const ofP, + uint32_t * const sizeP) { +/*---------------------------------------------------------------------------- + Write a 32-bit BMP encoded image to file *ofP. +-----------------------------------------------------------------------------*/ + int row; + + writeBmpImageHeader(pamP->width, pamP->height, 32, + pamP->width * 4 * pamP->height, ofP); + + /* write "XOR mask" */ + for (row = pamP->height - 1; row >= 0; --row) { + unsigned int col; + for (col = 0; col < pamP->width; ++col) { + sample pixel[3]; + uint32_t val; + + getPixel(tuples, col, row, pixel); + + val = ((uint32_t) pixel[PAM_RED_PLANE] << 16) + + ((uint32_t) pixel[PAM_GRN_PLANE] << 8) + + ((uint32_t) pixel[PAM_BLU_PLANE] << 0) + ; + + if (haveAlpha) + val += (uint32_t) tuples[row][col][alphaPlane] << 24; + + pm_writelittlelongu(ofP, val); + } + } + *sizeP = 40 + pamP->height * pamP->width * 4; +} + + + +static void +writeBmpPalette(const struct Palette * const paletteP, + unsigned int const maxColors, + FILE * const ofP) { +/*---------------------------------------------------------------------------- + Write the palette of a BMP image. +-----------------------------------------------------------------------------*/ + unsigned int i; + + for (i = 0; i < paletteP->colorCt; ++i) + pm_writelittlelongu(ofP, 0 + +(paletteP->color[i][PAM_RED_PLANE] << 16) + +(paletteP->color[i][PAM_GRN_PLANE] << 8) + +(paletteP->color[i][PAM_BLU_PLANE] << 0)); + + for (; i < maxColors; ++i) + pm_writelittlelongu(ofP, 0); +} + + + +static void +writeXorMask(const struct pam * const pamP, + tuple ** const tuples, + GetPixelFn * const getPixel, + const struct Palette * const paletteP, + unsigned int const bpp, + FILE * const ofP) { +/*---------------------------------------------------------------------------- + Write the "XOR mask" part of a BMP image. + + This is what one normally thinks of as the foreground image raster. +-----------------------------------------------------------------------------*/ + unsigned int const maxCol = ((pamP->width * bpp + 31) & ~31) / bpp; + + int row; + + for (row = pamP->height - 1; row >= 0; --row) { + uint8_t val; + uint16_t mask; + unsigned int col; + + mask = 0x1; + val = 0x0; + + for (col = 0; col < pamP->width; ++col) { + sample pixel[3]; + unsigned int i; + + mask <<= bpp; + val <<= bpp; + + getPixel(tuples, col, row, pixel); + + for (i = 0; i < paletteP->colorCt; ++i) + if (true + && (pixel[0] == paletteP->color[i][0]) + && (pixel[1] == paletteP->color[i][1]) + && (pixel[2] == paletteP->color[i][2])) + break; + + assert(i < paletteP->colorCt); + + val |= i; + + if (mask > 0xFF) { + pm_writecharu(ofP, val); + mask = 0x1; + val = 0x0; + } + } + for (; col < maxCol; ++col) { + mask <<= bpp; + val <<= bpp; + + if (mask > 0xFF) { + pm_writecharu(ofP, val); + mask = 0x1; + } + } + } +} + + + +static void +writePaletteBmp(unsigned int const bpp, + const struct pam * const pamP, + tuple ** const tuples, + GetPixelFn * const getPixel, + const struct Palette * const paletteP, + FILE * const ofP, + uint32_t * const sizeP) { +/*---------------------------------------------------------------------------- + Write a `BMP with palette' encoded image to file *ofP. + + Unless it would be smaller as a 32-bit direct image, in which case + write that instead. +-----------------------------------------------------------------------------*/ + unsigned int const maxColors = 1 << bpp; + + unsigned int const rasterSize = + pamP->height *((pamP->width * bpp + 31) & ~31) / 8; + + if (pamP->height * pamP->width * 4 <= maxColors * 4 + rasterSize) + write32BitBmp(pamP, tuples, getPixel, false /*haveAlpha*/, 0, + ofP, sizeP); + else { + unsigned int const headerSize = 40; + unsigned int const paletteSize = maxColors * 4; + + writeBmpImageHeader(pamP->width, pamP->height, bpp, rasterSize, ofP); + + writeBmpPalette(paletteP, maxColors, ofP); + + writeXorMask(pamP, tuples, getPixel, paletteP, bpp, ofP); + + *sizeP = headerSize + paletteSize + rasterSize; + } +} + + + +static void +writeAndMask(const struct pam * const pamP, + tuple ** const tuples, + bool const haveAlpha, + unsigned int const alphaPlane, + bool const haveAnd, + unsigned int const andPlane, + FILE * const ofP, + uint32_t * const sizeP) { +/*---------------------------------------------------------------------------- + Write the AND mask to file *ofP. +-----------------------------------------------------------------------------*/ + unsigned int const maxCol =((pamP->width * 1 + 31) & ~31) / 1; + + int row; + unsigned int sizeSoFar; + + sizeSoFar = 0; + + for (row = pamP->height - 1; row >= 0; --row) { + uint8_t val; + uint16_t mask; + unsigned int col; + + mask = 0x1; + val = 0x0; + + for (col = 0; col < pamP->width; ++col) { + mask <<= 1; + val <<= 1; + + if (!andMakesOpaque(pamP, tuples, row, col, + haveAlpha, alphaPlane, haveAnd, andPlane)) + val |= 0x1; + + if (mask > 0xFF) { + pm_writecharu(ofP, val); + sizeSoFar += 1; + mask = 0x1; + val = 0x0; + } + } + for (; col < maxCol; ++col) { + mask <<= 1; + val <<= 1; + + if (mask > 0xFF){ + pm_writecharu(ofP, val); + sizeSoFar += 1; + mask = 0x1; + } + } + } + *sizeP = sizeSoFar; +} + + + +static void +makeAlphaFile(const struct pam * const imagePamP, + tuple ** const imageTuples, + unsigned int const alphaPlane, + const char ** const alphaFileNameP) { + + FILE * alphaFileP; + struct pam alphaPam; + tuple ** alphaTuples; + unsigned int row; + + pm_make_tmpfile(&alphaFileP, alphaFileNameP); + + alphaPam.size = sizeof(alphaPam); + alphaPam.len = PAM_STRUCT_SIZE(tuple_type); + alphaPam.file = alphaFileP; + alphaPam.format = PAM_FORMAT; + alphaPam.width = imagePamP->width; + alphaPam.height = imagePamP->height; + alphaPam.depth = 1; + alphaPam.maxval = imagePamP->maxval; + strcpy(alphaPam.tuple_type, PAM_PGM_TUPLETYPE); + + alphaTuples = pnm_allocpamarray(&alphaPam); + + assert(alphaPlane < imagePamP->depth); + + for (row = 0; row < alphaPam.height; ++row) { + unsigned int col; + for (col = 0; col < alphaPam.width; ++col) + alphaTuples[row][col][0] = imageTuples[row][col][alphaPlane]; + } + + pnm_writepam(&alphaPam, alphaTuples); + + pnm_freepamarray(alphaTuples, &alphaPam); + + pm_close(alphaFileP); +} + + + +struct AcceptToFileParm { + FILE * ofP; + size_t * writeCtP; +}; + +static void +acceptToFile(int const pipeToSuckFd, + void * const accepterParm) { + + struct AcceptToFileParm * const parmP = accepterParm; + + FILE * const inFileP = fdopen(pipeToSuckFd, "r"); + + bool eof; + size_t copyCt; + + for (eof = false, copyCt = 0; !eof; ) { + size_t readCt; + unsigned char buffer[1024]; + + readCt = fread(buffer, 1, sizeof(buffer), inFileP); + + if (readCt == 0) + eof = true; + else { + size_t writeCt; + + writeCt = fwrite(buffer, 1, readCt, parmP->ofP); + + if (writeCt != readCt) + pm_error("Write to images file failed. errno=%d (%s)", + errno, strerror(errno)); + + copyCt += writeCt; + } + } + *parmP->writeCtP = copyCt; +} + + + +static void +writePng(const struct pam * const pamP, + tuple ** const tuples, + bool const haveAlpha, + unsigned int const alphaPlane, + bool const haveAnd, + unsigned int const andPlane, + uint32_t * const sizeP, + FILE * const ofP) { + + struct pamtuples pamTuples; + size_t pngSize; + struct AcceptToFileParm acceptParm; + struct pam pam; + + pam = *pamP; + pam.depth = pamP->depth - (haveAlpha ? 1 : 0) - (haveAnd ? 1 : 0); + + pamTuples.pamP = &pam; + pamTuples.tuplesP = (tuple ***)&tuples; + + /* We're going to fork a process to add stuff to *ofP, so we flush + out this process' previous writes to that file first: + */ + fflush(ofP); + + acceptParm.ofP = ofP; + acceptParm.writeCtP = &pngSize; + + if (haveAlpha || haveAnd) { + const char * alphaFileName; + const char * command; + + if (haveAlpha) + makeAlphaFile(pamP, tuples, alphaPlane, &alphaFileName); + else + makeAlphaFile(pamP, tuples, andPlane, &alphaFileName); + + strcpy (pam.tuple_type, + pam.depth == 3 ? PAM_PPM_TUPLETYPE: PAM_PGM_TUPLETYPE); + + pm_asprintf(&command, "pnmtopng -alpha=\"%s\"", alphaFileName); + + pm_system(pm_feed_from_pamtuples, &pamTuples, + acceptToFile, &acceptParm, + command); + + pm_strfree(command); + + unlink(alphaFileName); + } else { + pm_system(pm_feed_from_pamtuples, &pamTuples, + acceptToFile, &acceptParm, + "pnmtopng"); + } + + *sizeP = pngSize; +} + + + +static void +blackenXor(const struct pam * const pamP, + tuple ** const tuples, + bool const haveAlpha, + unsigned int const alphaPlane, + bool const haveAnd, + unsigned int const andPlane) { +/*---------------------------------------------------------------------------- + Set all pixels marked as transparent in AND mask to black. +-----------------------------------------------------------------------------*/ + unsigned int row; + + for (row = 0; row < pamP->height; ++row) { + unsigned int col; + for (col = 0; col < pamP->width; ++col) { + if (!andMakesOpaque(pamP, tuples, row, col, + haveAlpha, alphaPlane, haveAnd, andPlane)) { + tuples[row][col][0] = PAM_BLACK; + + if (pamP->depth >= 3) { + tuples[row][col][1] = PAM_BLACK; + tuples[row][col][2] = PAM_BLACK; + } + } + } + } +} + + + + +static void +readAndScalePam(struct pam * const pamP, + bool const doingPng, + tuple ** const tuples) { + + if (doingPng) { + /* Read the image with its native maxval */ + unsigned int row; + for (row = 0; row < pamP->height; ++row) + pnm_readpamrow(pamP, tuples[row]); + } else { + /* Read the image and scale to maxval 255 */ + tuple * tuplerow; + unsigned int row; + tuplerow = pnm_allocpamrow(pamP); + + for (row = 0; row < pamP->height; ++row) { + pnm_readpamrow(pamP, tuplerow); + pnm_scaletuplerow(pamP, tuples[row], tuplerow, 255); + } + pnm_freepamrow(tuplerow); + pamP->maxval = 255; + } +} + + + +static void +determineImageType(const struct pam * const pamP, + tuple ** const tuples, + GetPixelFn ** const getPixelP, + bool * const haveAlphaP, + unsigned int * const alphaPlaneP, + bool * const haveAndP, + unsigned int * const andPlaneP) { + + /* PAM input channels: + * + * 1-channel PAM: Grayscale + * 2-channel PAM: Grayscale +Alpha + * 3-channel PAM: RGB + * 4-channel PAM: RGB +Alpha + * 5-channel PAM: RGB +Alpha +AND mask + */ + switch (pamP->depth) { + case 1: + *getPixelP = get_grayscalePixel; + *haveAlphaP = false; + *haveAndP = false; + break; + + case 2: + *getPixelP = get_grayscalePixel; + if (realAlphaNeeded(pamP, tuples, 1)) { + *haveAlphaP = true; + *alphaPlaneP = 1; + *haveAndP = false; + } else { + *haveAlphaP = false; + *haveAndP = true; + *andPlaneP = 1; + } + break; + + case 3: + *getPixelP = get_rgbPixel; + *haveAlphaP = false; + *haveAndP = false; + break; + + case 4: + *getPixelP = get_rgbPixel; + if (realAlphaNeeded(pamP, tuples, 3)) { + *haveAlphaP = true; + *alphaPlaneP = 3; + *haveAndP = false; + } else { + *haveAlphaP = false; + *haveAndP = true; + *andPlaneP = 3; + } + break; + + case 5: + *getPixelP = get_rgbPixel; + *haveAlphaP = true; + *alphaPlaneP = 3; + *haveAndP = true; + *andPlaneP = 4; + break; + + default: + pm_error("unexpected PAM depth %u. " + "We understand depths 1-5", pamP->depth); + break; + } +} + + + +static void +reportImageInfo(unsigned int const imageNum, + const struct pam * const pamP, + const struct Palette * const paletteP, + bool const haveAlpha, + bool const haveAnd) { + + const char * colorCt; + + if (paletteP->tooManyColors) + pm_asprintf(&colorCt, "> 256"); + else + pm_asprintf(&colorCt, "%u", paletteP->colorCt); + + pm_message("Image %2u:" + " %3u x %3u x %u, %s colors%s%s", + imageNum, + pamP->width, pamP->height, pamP->depth, + colorCt, + haveAlpha ? ", alpha channel": "", + haveAnd ? ", AND mask": ""); + + pm_strfree(colorCt); +} + + + +static void +writeIconAndCreateDirEntry(const struct pam * const pamP, + tuple ** const tuples, + GetPixelFn * const getPixel, + bool const doingPng, + bool const haveAlpha, + unsigned int const alphaPlane, + bool const haveAnd, + unsigned int const andPlane, + bool const mustBlackenXor, + const struct Palette * const paletteP, + FILE * const ofP, + struct IconDirEntry * const dirEntryP) { +/*---------------------------------------------------------------------------- + Write to *ofP the icon image for the image represented by *pamP and + 'tuples'. + + Generate the information for an icon directory entry for this image + and return it as *dirEntryP. ==>BUT: the 'offset' member of this + structure will not be meaningful. <== + + Make a PNG image if 'doingPng' is true; BMP otherwise. + + 'haveAlpha' means that there is an alpha plane in 'tuples' and it is + Plane 'alphaPlane'. + + 'haveAnd' means that there is an AND plane in 'tuples' and it is Plane + 'andPlane'. + + *paletteP is the color palette for the icon; it contains an entry for each + color in 'tuples'. Except: it may simply indicate that there are too many + colors in 'tuples' to have a palette. + + The 'bits_per_pixel' member of the directory entry is supposed to tell the + color resolution of the image so the user can decide which of many versions + of the icon in the file to use. But we just call it 32 bits in every case + except paletted BMP, where it actually relates to how many colors are in + the image. +-----------------------------------------------------------------------------*/ + dirEntryP->width = pamP->width; + dirEntryP->height = pamP->height; + dirEntryP->color_planes = 1; + dirEntryP->zero = 0; + + if (doingPng) { + dirEntryP->color_count = 0; + dirEntryP->bits_per_pixel = 32; + + writePng(pamP, tuples, haveAlpha, alphaPlane, haveAnd, andPlane, + &dirEntryP->size, ofP); + } else { + uint32_t bmpSize; + uint32_t andMaskSize; + + if (mustBlackenXor) + blackenXor(pamP, tuples, + haveAlpha, alphaPlane, haveAnd, andPlane); + + if (haveAlpha) { + dirEntryP->color_count = 0; + dirEntryP->bits_per_pixel = 32; + + write32BitBmp(pamP, tuples, getPixel, haveAlpha, alphaPlane, + ofP, &bmpSize); + } else if (paletteP->tooManyColors) { + /* Do a truecolor image */ + dirEntryP->color_count = 0; + dirEntryP->bits_per_pixel = 32; + + write32BitBmp(pamP, tuples, getPixel, false /*haveAlpha*/, 0, + ofP, &bmpSize); + } else { + /* Do a paletted image */ + + if (paletteP->colorCt <= 2) { + dirEntryP->color_count = paletteP->colorCt; + dirEntryP->bits_per_pixel = 1; + + writePaletteBmp(1, pamP, tuples, getPixel, paletteP, + ofP, &bmpSize); + } else if (paletteP->colorCt <= 16) { + dirEntryP->color_count = paletteP->colorCt; + dirEntryP->bits_per_pixel = 4; + + writePaletteBmp(4, pamP, tuples, getPixel,paletteP, + ofP, &bmpSize); + } else { + dirEntryP->color_count = 0; + dirEntryP->bits_per_pixel = 8; + + writePaletteBmp(8, pamP, tuples, getPixel, paletteP, + ofP, &bmpSize); + } + } + writeAndMask(pamP, tuples, haveAlpha, alphaPlane, haveAnd, andPlane, + ofP, &andMaskSize); + + dirEntryP->size = bmpSize + andMaskSize; + } +} + + + +static void +convertOneImage(unsigned int const imageNum, + FILE * const ifP, + unsigned int const pngThreshold, + bool const mustBlackenXor, + FILE * const ofP, + struct IconDir * const dirP) { + + struct IconDirEntry dirEntry; + struct pam pam; + tuple ** tuples; + bool haveAlpha; + unsigned int alphaPlane; + bool haveAnd; + unsigned int andPlane; + GetPixelFn * getPixel; + struct Palette palette; + bool doingPng; + + /* Output: + * + * threshold^2 pixels or more: + * no alpha channel: PNG (RGB) + * alpha channel: PNG (RGBA) + * alpha channel +AND mask: PNG (RGBA), AND mask dropped + * alpha values other than 0 and maxval: 32bit +alpha BMP + * no more than 2 colors: 1bit or 32bit BMP + * no more than 16 colors: 4bit or 32bit BMP + * no more than 256 colors: 8bit or 32bit BMP + * more than 256 colors: 32bit BMP + */ + pnm_readpaminit(ifP, &pam, PAM_STRUCT_SIZE(tuple_type)); + + if (pam.width > 256 || pam.height > 256) + pm_error("Image %u: too large as a windows icon (%u x %u). " + "Maximum allowed dimension is 256", + imageNum, pam.width, pam.height); + + tuples = pnm_allocpamarray(&pam); + + doingPng = pam.width * pam.height >= pngThreshold; + + readAndScalePam(&pam, doingPng, tuples); + + determineImageType(&pam, tuples, &getPixel, + &haveAlpha, &alphaPlane, &haveAnd, &andPlane); + + getPalette(&pam, tuples, getPixel, &palette); + + if (verbose) + reportImageInfo(imageNum, &pam, &palette, haveAlpha, haveAnd); + + writeIconAndCreateDirEntry(&pam, tuples, getPixel, doingPng, + haveAlpha, alphaPlane, + haveAnd, andPlane, + mustBlackenXor, + &palette, + ofP, &dirEntry); + + if (verbose) + pm_message("Image %2u: %u bytes", imageNum, dirEntry.size); + + pnm_freepamarray(tuples, &pam); + + addToDirectory(&dirEntry, dirP); +} + + + +static void +convert(FILE * const ifP, + unsigned int const pngThreshold, + bool const mustBlackenXor, + struct IconDir * const dirP, + FILE * const ofP) { +/*---------------------------------------------------------------------------- + Read a (multi-image) PAM file from *ifP and convert the individual images + to the proper format for a Windows icon file and write those to *ofP. + + Where the number of pixels in an image is at least 'pngThreshold', use + a PNG image. Otherwise, use a BMP. +-----------------------------------------------------------------------------*/ + unsigned int imageNum; + int eof; + + for (imageNum = 0, eof = false; !eof; ++imageNum) { + convertOneImage(imageNum, ifP, pngThreshold, mustBlackenXor, + ofP, dirP); + + pnm_nextimage(ifP, &eof); + } +} + + + +static void +writeIconDirEntry(const struct IconDirEntry * const dirEntryP, + FILE * const ofP) { + + pm_writecharu (ofP, dirEntryP->width); + pm_writecharu (ofP, dirEntryP->height); + pm_writecharu (ofP, dirEntryP->color_count); + pm_writecharu (ofP, dirEntryP->zero); + pm_writelittleshortu (ofP, dirEntryP->color_planes); + pm_writelittleshortu (ofP, dirEntryP->bits_per_pixel); + pm_writelittlelongu (ofP, dirEntryP->size); + pm_writelittlelongu (ofP, dirEntryP->offset); +} + + + +static void +writeIconDirectory(const struct IconDir * const dirP, + FILE * const ofP) { +/*---------------------------------------------------------------------------- + Write to file *ofP the icon directory described by *dirP. + + *dirP's image offset members are meaningless as input. We fill them in. +-----------------------------------------------------------------------------*/ + uint32_t const hsize = 6 + dirP->count * 16; + + unsigned int imageNum; + unsigned int imageOffset; + + pm_writelittleshortu(ofP, dirP->zero); + pm_writelittleshortu(ofP, dirP->type); + pm_writelittleshortu(ofP, dirP->count); + + for (imageNum = 0, imageOffset = hsize; + imageNum < dirP->count; + ++imageNum) { + + struct IconDirEntry * const dirEntryP = &dirP->entries[imageNum]; + + pm_message("image %2u: %3u x %3u x %2u", + imageNum, + dirEntryP->width, + dirEntryP->height, + dirEntryP->bits_per_pixel); + + dirEntryP->offset = imageOffset; + + writeIconDirEntry(dirEntryP, ofP); + + imageOffset += dirEntryP->size; + } +} + + + +static void +copyFile(FILE * const ifP, + FILE * const ofP) { + + bool eof; + + for (eof = false; !eof; ) { + unsigned char buffer[1024]; + size_t bytesRead; + + bytesRead = fread(buffer, 1, sizeof(buffer), ifP); + + if (bytesRead == 0) + eof = true; + else { + size_t bytesWritten; + + bytesWritten = fwrite(buffer, 1, bytesRead, ofP); + + if (bytesWritten < bytesRead) + pm_error("Error writing to output file."); + } + } +} + + + +static void +writeIconFile(const struct IconDir * const dirP, + FILE * const imagesFileP, + FILE * const ofP) { +/*---------------------------------------------------------------------------- + Write a windows icon file. + + *dirP is the icon directory to put in it. + + *imagesFileP contains all the text of the icon images. The contents of + this file go verbatim into the output. +-----------------------------------------------------------------------------*/ + writeIconDirectory(dirP, ofP); + + copyFile(imagesFileP, ofP); +} + + + +int +main(int argc, const char *argv []) { + + struct CmdlineInfo cmdline; + FILE * ifP; + FILE * imagesFileP; + /* This is the file in which we collect the individual icon + images to be copied later to the output. + */ + struct IconDir * dirP; + + pm_proginit(&argc, argv); + + parseCommandLine(argc, argv, &cmdline); + + verbose = cmdline.verbose; + + /* The output icon file has directory information at the top that we + can't know until we have looked at the input images. So as we pass + through the input, we collect the directory information and generate + the individual icon images and store them in *imageFileP. When we've + been through all of the input, we write out the directory and then + copy the images from *imageFileP to the output. + */ + + dirP = newIconDir(); + + imagesFileP = pm_tmpfile(); + + ifP = pm_openr(cmdline.inputFileName); + + convert(ifP, SQR(cmdline.pngthreshold), cmdline.truetransparent, + dirP, imagesFileP); + + rewind(imagesFileP); + + writeIconFile(dirP, imagesFileP, stdout); + + freeIconDir(dirP); + + return 0; +} + + + diff --git a/converter/other/pamtoxvmini.c b/converter/other/pamtoxvmini.c index e1aa9b52..b57bcc74 100644 --- a/converter/other/pamtoxvmini.c +++ b/converter/other/pamtoxvmini.c @@ -152,14 +152,14 @@ getPaletteIndexThroughCache(struct pam * const pamP, If the tuple-index association is in *paletteIndexP, use it. If not, find it the hard way and add it to *palettedIndexP for the next guy. -----------------------------------------------------------------------------*/ - bool found; + int found; int paletteIndex; pnm_lookuptuple(pamP, paletteHash, tuple, &found, &paletteIndex); if (found) *paletteIndexP = paletteIndex; else { - bool fits; + int fits; findClosestColor(pamP, tuple, xvPaletteP, paletteIndexP); pnm_addtotuplehash(pamP, paletteHash, tuple, *paletteIndexP, &fits); diff --git a/converter/other/pnmtopclxl.c b/converter/other/pnmtopclxl.c index 83fdf7bf..4cd7c4d0 100644 --- a/converter/other/pnmtopclxl.c +++ b/converter/other/pnmtopclxl.c @@ -1177,7 +1177,7 @@ printPages(int const outFd, while (sourceP) { FILE * ifP; struct pam pam; - bool eof; + int eof; unsigned int pageNum; ifP = pm_openr(sourceP->name); diff --git a/converter/other/pnmtopng.c b/converter/other/pnmtopng.c index edbe57f5..bcb94612 100644 --- a/converter/other/pnmtopng.c +++ b/converter/other/pnmtopng.c @@ -128,7 +128,6 @@ struct cmdlineInfo { int filterSet; unsigned int force; unsigned int libversion; - unsigned int compressionSpec; struct zlibCompression zlibCompression; }; @@ -480,6 +479,27 @@ parseCommandLine(int argc, const char ** argv, +static void +reportInputType(int const format, + xelval const maxval) { + + switch (PNM_FORMAT_TYPE(format)) { + case PBM_TYPE: + pm_message ("reading a PBM file"); + break; + case PGM_TYPE: + pm_message ("reading a PGM file (maxval=%d)", maxval); + break; + case PPM_TYPE: + pm_message ("reading a PPM file (maxval=%d)", maxval); + break; + default: + assert(false); + } +} + + + static png_color_16 xelToPngColor_16(xel const input, xelval const maxval, @@ -857,7 +877,7 @@ tryTransparentColor(FILE * const ifp, pixel const transcolor, bool * const singleColorIsTransP) { - int const pnm_type = PNM_FORMAT_TYPE(format); + int const pnmType = PNM_FORMAT_TYPE(format); xel * xelrow; bool singleColorIsTrans; @@ -878,7 +898,7 @@ tryTransparentColor(FILE * const ifp, /* If we have a second transparent color, we're disqualified */ - if (pnm_type == PPM_TYPE) { + if (pnmType == PPM_TYPE) { if (!PPM_EQUAL(xelrow[col], transcolor)) singleColorIsTrans = FALSE; } else { @@ -895,7 +915,7 @@ tryTransparentColor(FILE * const ifp, the same color as our candidate transparent color, that disqualifies us. */ - if (pnm_type == PPM_TYPE) { + if (pnmType == PPM_TYPE) { if (PPM_EQUAL(xelrow[col], transcolor)) singleColorIsTrans = FALSE; } else { @@ -1118,6 +1138,51 @@ determineBackground(struct cmdlineInfo const cmdline, +static bool +hasColor(FILE * const ifP, + unsigned int const cols, + unsigned int const rows, + xelval const maxval, + int const format, + pm_filepos const rasterPos) { +/*---------------------------------------------------------------------------- + The image contains colors other than black, white, and gray. +-----------------------------------------------------------------------------*/ + bool retval; + + if (PNM_FORMAT_TYPE(format) == PPM_TYPE) { + unsigned int row; + xel * xelrow; /* malloc'ed */ + /* The row of the input image currently being analyzed */ + bool isGray; + + xelrow = pnm_allocrow(cols); + + pm_seek2(ifP, &rasterPos, sizeof(rasterPos)); + + for (row = 0, isGray = true; row < rows && isGray; ++row) { + unsigned int col; + + pnm_readpnmrow(ifP, xelrow, cols, maxval, format); + + for (col = 0; col < cols && isGray; ++col) { + xel const p = xelrow[col]; + if (PPM_GETR(p) != PPM_GETG(p) || PPM_GETG(p) != PPM_GETB(p)) + isGray = FALSE; + } + } + + pnm_freerow(xelrow); + + retval = !isGray; + } else + retval = false; + + return retval; +} + + + static void findRedundantBits(FILE * const ifp, int const rasterPos, @@ -1819,19 +1884,19 @@ tryAlphaPalette(FILE * const ifP, static void -computePixelWidth(int const pnm_type, - unsigned int const pnm_meaningful_bits, +computePixelWidth(bool const colorPng, + unsigned int const pnmMeaningfulBitCt, bool const alpha, unsigned int * const bitsPerSampleP, unsigned int * const bitsPerPixelP) { unsigned int bitsPerSample, bitsPerPixel; - if (pnm_type == PPM_TYPE || alpha) { + if (colorPng || alpha) { /* PNG allows only depths of 8 and 16 for a truecolor image and for a grayscale image with an alpha channel. */ - if (pnm_meaningful_bits > 8) + if (pnmMeaningfulBitCt > 8) bitsPerSample = 16; else bitsPerSample = 8; @@ -1839,24 +1904,24 @@ computePixelWidth(int const pnm_type, /* A grayscale, non-colormapped, no-alpha PNG may have any bit depth from 1 to 16 */ - if (pnm_meaningful_bits > 8) + if (pnmMeaningfulBitCt > 8) bitsPerSample = 16; - else if (pnm_meaningful_bits > 4) + else if (pnmMeaningfulBitCt > 4) bitsPerSample = 8; - else if (pnm_meaningful_bits > 2) + else if (pnmMeaningfulBitCt > 2) bitsPerSample = 4; - else if (pnm_meaningful_bits > 1) + else if (pnmMeaningfulBitCt > 1) bitsPerSample = 2; else bitsPerSample = 1; } if (alpha) { - if (pnm_type == PPM_TYPE) + if (colorPng) bitsPerPixel = 4 * bitsPerSample; else bitsPerPixel = 2 * bitsPerSample; } else { - if (pnm_type == PPM_TYPE) + if (colorPng) bitsPerPixel = 3 * bitsPerSample; else bitsPerPixel = bitsPerSample; @@ -1904,7 +1969,7 @@ computeColorMap(FILE * const ifP, int const cols, int const rows, xelval const maxval, - int const pnmType, + bool const colorPng, int const format, bool const force, FILE * const pfP, @@ -1947,6 +2012,8 @@ computeColorMap(FILE * const ifP, If the image is to have a background color, we return the palette index of that color as *backgroundIndexP. + + 'colorPng' means the PNG will be of the RGB variety. -------------------------------------------------------------------------- */ if (force) pm_asprintf(noColormapReasonP, "You requested no color map"); @@ -1956,7 +2023,7 @@ computeColorMap(FILE * const ifP, maxval, PALETTEMAXVAL); else { unsigned int bitsPerPixel; - computePixelWidth(pnmType, pnm_meaningful_bits, alpha, + computePixelWidth(colorPng, pnm_meaningful_bits, alpha, NULL, &bitsPerPixel); if (!pfP && bitsPerPixel == 1) @@ -2069,9 +2136,9 @@ static void computeColorMapLookupTable( static void computeRasterWidth(bool const colorMapped, - unsigned int const palette_size, - int const pnm_type, - unsigned int const pnm_meaningful_bits, + unsigned int const paletteSize, + bool const colorPng, + unsigned int const pnmMeaningfulBitCt, bool const alpha, unsigned int * const bitsPerSampleP, unsigned int * const bitsPerPixelP) { @@ -2082,24 +2149,24 @@ computeRasterWidth(bool const colorMapped, -----------------------------------------------------------------------------*/ if (colorMapped) { /* The raster element is a palette index */ - if (palette_size <= 2) + if (paletteSize <= 2) *bitsPerSampleP = 1; - else if (palette_size <= 4) + else if (paletteSize <= 4) *bitsPerSampleP = 2; - else if (palette_size <= 16) + else if (paletteSize <= 16) *bitsPerSampleP = 4; else *bitsPerSampleP = 8; *bitsPerPixelP = *bitsPerSampleP; if (verbose) - pm_message("Writing %d-bit color indexes", *bitsPerSampleP); + pm_message("Writing %u-bit color indexes", *bitsPerSampleP); } else { /* The raster element is an explicit pixel -- color and transparency */ - computePixelWidth(pnm_type, pnm_meaningful_bits, alpha, + computePixelWidth(colorPng, pnmMeaningfulBitCt, alpha, bitsPerSampleP, bitsPerPixelP); if (verbose) - pm_message("Writing %d bits per component per pixel", + pm_message("Writing %u bits per component per pixel", *bitsPerSampleP); } } @@ -2339,14 +2406,14 @@ doIhdrChunk(struct pngx * const pngxP, unsigned int const height, unsigned int const depth, bool const colorMapped, - int const pnmType, + bool const colorPng, bool const alpha) { int colorType; if (colorMapped) colorType = PNG_COLOR_TYPE_PALETTE; - else if (pnmType == PPM_TYPE) + else if (colorPng) colorType = PNG_COLOR_TYPE_RGB; else colorType = PNG_COLOR_TYPE_GRAY; @@ -2552,252 +2619,235 @@ convertpnm(struct cmdlineInfo const cmdline, lazy -- it takes a great deal of work to carry all that information as separate arguments -- and it's only a very small violation. -----------------------------------------------------------------------------*/ - xel p; - int rows, cols, format; - xelval maxval; - /* The maxval of the input image */ - xelval png_maxval; - /* The maxval of the samples in the PNG output - (must be 1, 3, 7, 15, 255, or 65535) - */ - pixel transcolor; - /* The color that is to be transparent, with maxval equal to that - of the input image. - */ - int transexact; - /* boolean: the user wants only the exact color he specified to be - transparent; not just something close to it. - */ - int transparent; - bool alpha; - /* There will be an alpha mask */ - unsigned int pnm_meaningful_bits; - pixel backcolor; - /* The background color, with maxval equal to that of the input - image. - */ - jmp_buf jmpbuf; - struct pngx * pngxP; - - bool colorMapped; - pixel palettePnm[MAXCOLORS]; - png_color palette[MAXCOLORS]; - /* The color part of the color/alpha palette passed to the PNG - compressor - */ - unsigned int paletteSize; - - gray transPnm[MAXCOLORS]; - png_byte trans[MAXCOLORS]; - /* The alpha part of the color/alpha palette passed to the PNG - compressor - */ - unsigned int transSize; - - colorhash_table cht; - coloralphahash_table caht; - - unsigned int background_index; - /* Index into palette[] of the background color. */ - - gray alphaMaxval; - const char * noColormapReason; - /* The reason that we shouldn't make a colormapped PNG, or NULL if - we should. malloc'ed null-terminated string. - */ - unsigned int depth; - /* The number of bits per sample in the (uncompressed) png - raster -- if the raster contains palette indices, this is the - number of bits in the index. - */ - unsigned int fulldepth; - /* The total number of bits per pixel in the (uncompressed) png - raster, including all channels. - */ - pm_filepos rasterPos; - /* file position in input image file of start of image (i.e. after - the header) - */ - xel *xelrow; /* malloc'ed */ - /* The row of the input image currently being processed */ - - int pnmType; - gray ** alpha_mask; - - /* We initialize these guys to quiet compiler warnings: */ - depth = 0; - - errorlevel = 0; - - if (setjmp(jmpbuf)) - pm_error ("setjmp returns error condition"); - - pngx_create(&pngxP, PNGX_WRITE, &jmpbuf); - - pnm_readpnminit(ifP, &cols, &rows, &maxval, &format); - pm_tell2(ifP, &rasterPos, sizeof(rasterPos)); - pnmType = PNM_FORMAT_TYPE(format); - - xelrow = pnm_allocrow(cols); - - if (verbose) { - if (pnmType == PBM_TYPE) - pm_message ("reading a PBM file"); - else if (pnmType == PGM_TYPE) - pm_message ("reading a PGM file (maxval=%d)", maxval); - else if (pnmType == PPM_TYPE) - pm_message ("reading a PPM file (maxval=%d)", maxval); - } - - determineTransparency(cmdline, ifP, rasterPos, cols, rows, maxval, format, - afP, - &alpha, &transparent, &transcolor, &transexact, - &alpha_mask, &alphaMaxval); - - determineBackground(cmdline, maxval, &backcolor); - - /* first of all, check if we have a grayscale image written as PPM */ - - if (pnmType == PPM_TYPE && !cmdline.force) { - unsigned int row; - bool isgray; - - isgray = TRUE; /* initial assumption */ - pm_seek2(ifP, &rasterPos, sizeof(rasterPos)); - for (row = 0; row < rows && isgray; ++row) { - unsigned int col; - pnm_readpnmrow(ifP, xelrow, cols, maxval, format); - for (col = 0; col < cols && isgray; ++col) { - p = xelrow[col]; - if (PPM_GETR(p) != PPM_GETG(p) || PPM_GETG(p) != PPM_GETB(p)) - isgray = FALSE; - } - } - if (isgray) - pnmType = PGM_TYPE; - } - - /* handle `odd' maxvalues */ - - if (maxval > 65535 && !cmdline.downscale) { - pm_error("can only handle files up to 16-bit " - "(use -downscale to override"); - } - - findRedundantBits(ifP, rasterPos, cols, rows, maxval, format, alpha, - cmdline.force, &pnm_meaningful_bits); + int rows, cols, format; + xelval maxval; + /* The maxval of the input image */ + xelval pngMaxval; + /* The maxval of the samples in the PNG output + (must be 1, 3, 7, 15, 255, or 65535) + */ + pixel transcolor; + /* The color that is to be transparent, with maxval equal to that + of the input image. + */ + bool transExact; + /* boolean: the user wants only the exact color he specified to be + transparent; not just something close to it. + */ + int transparent; + bool alpha; + /* There will be an alpha mask */ + unsigned int pnmMeaningfulBitCt; + pixel backColor; + /* The background color, with maxval equal to that of the input + image. + */ + jmp_buf jmpbuf; + struct pngx * pngxP; + + bool colorMapped; + pixel palettePnm[MAXCOLORS]; + png_color palette[MAXCOLORS]; + /* The color part of the color/alpha palette passed to the PNG + compressor + */ + unsigned int paletteSize; + + gray transPnm[MAXCOLORS]; + png_byte trans[MAXCOLORS]; + /* The alpha part of the color/alpha palette passed to the PNG + compressor + */ + unsigned int transSize; + + colorhash_table cht; + coloralphahash_table caht; + + unsigned int backgroundIndex; + /* Index into palette[] of the background color. */ + + gray alphaMaxval; + const char * noColormapReason; + /* The reason that we shouldn't make a colormapped PNG, or NULL if + we should. malloc'ed null-terminated string. + */ + unsigned int depth; + /* The number of bits per sample in the (uncompressed) png + raster -- if the raster contains palette indices, this is the + number of bits in the index. + */ + unsigned int fulldepth; + /* The total number of bits per pixel in the (uncompressed) png + raster, including all channels. + */ + pm_filepos rasterPos; + /* file position in input image file of start of image (i.e. after + the header) + */ + xel * xelrow; /* malloc'ed */ + /* The row of the input image currently being processed */ + + gray ** alpha_mask; + + bool colorPng; + /* The PNG shall be of the color (RGB) variety */ + + /* We initialize these guys to quiet compiler warnings: */ + depth = 0; + + errorlevel = 0; + + if (setjmp(jmpbuf)) + pm_error ("setjmp returns error condition"); + + pngx_create(&pngxP, PNGX_WRITE, &jmpbuf); + + pnm_readpnminit(ifP, &cols, &rows, &maxval, &format); + pm_tell2(ifP, &rasterPos, sizeof(rasterPos)); + + xelrow = pnm_allocrow(cols); + + if (verbose) + reportInputType(format, maxval); + + determineTransparency(cmdline, ifP, rasterPos, cols, rows, maxval, format, + afP, + &alpha, &transparent, &transcolor, &transExact, + &alpha_mask, &alphaMaxval); + + determineBackground(cmdline, maxval, &backColor); + + if (cmdline.force) + colorPng = (PNM_FORMAT_TYPE(format) == PPM_TYPE); + else { + if (PNM_FORMAT_TYPE(format) == PPM_TYPE) { + colorPng = hasColor(ifP, cols, rows, maxval, format, rasterPos); + } else + colorPng = false; + } + + + /* handle `odd' maxvalues */ + + if (maxval > 65535 && !cmdline.downscale) { + pm_error("can only handle files up to 16-bit " + "(use -downscale to override"); + } + + findRedundantBits(ifP, rasterPos, cols, rows, maxval, format, alpha, + cmdline.force, &pnmMeaningfulBitCt); - computeColorMap(ifP, rasterPos, cols, rows, maxval, pnmType, format, - cmdline.force, pfP, - alpha, transparent >= 0, transcolor, transexact, - !!cmdline.background, backcolor, - alpha_mask, alphaMaxval, pnm_meaningful_bits, - palettePnm, &paletteSize, transPnm, &transSize, - &background_index, &noColormapReason); - - if (noColormapReason) { - if (pfP) - pm_error("You specified a particular palette, but this image " - "cannot be represented by any palette. %s", - noColormapReason); - if (verbose) - pm_message("Not using color map. %s", noColormapReason); - pm_strfree(noColormapReason); - colorMapped = FALSE; - } else - colorMapped = TRUE; + computeColorMap(ifP, rasterPos, cols, rows, maxval, colorPng, format, + cmdline.force, pfP, + alpha, transparent >= 0, transcolor, transExact, + !!cmdline.background, backColor, + alpha_mask, alphaMaxval, pnmMeaningfulBitCt, + palettePnm, &paletteSize, transPnm, &transSize, + &backgroundIndex, &noColormapReason); + + if (noColormapReason) { + if (pfP) + pm_error("You specified a particular palette, but this image " + "cannot be represented by any palette. %s", + noColormapReason); + if (verbose) + pm_message("Not using color map. %s", noColormapReason); + pm_strfree(noColormapReason); + colorMapped = FALSE; + } else + colorMapped = TRUE; - computeColorMapLookupTable(colorMapped, palettePnm, paletteSize, - transPnm, transSize, alpha, alphaMaxval, - &cht, &caht); + computeColorMapLookupTable(colorMapped, palettePnm, paletteSize, + transPnm, transSize, alpha, alphaMaxval, + &cht, &caht); - computeRasterWidth(colorMapped, paletteSize, pnmType, - pnm_meaningful_bits, alpha, - &depth, &fulldepth); - if (verbose) - pm_message ("writing a%s %d-bit %s%s file%s", - fulldepth == 8 ? "n" : "", fulldepth, - colorMapped ? "palette": - (pnmType == PPM_TYPE ? "RGB" : "gray"), - alpha ? (colorMapped ? "+transparency" : "+alpha") : "", - cmdline.interlace ? " (interlaced)" : ""); + computeRasterWidth(colorMapped, paletteSize, colorPng, + pnmMeaningfulBitCt, alpha, + &depth, &fulldepth); + if (verbose) + pm_message ("writing a%s %d-bit %s%s file%s", + fulldepth == 8 ? "n" : "", fulldepth, + colorMapped ? "palette": + colorPng ? "RGB" : "gray", + alpha ? (colorMapped ? "+transparency" : "+alpha") : "", + cmdline.interlace ? " (interlaced)" : ""); - /* now write the file */ + /* now write the file */ - png_maxval = pm_bitstomaxval(depth); + pngMaxval = pm_bitstomaxval(depth); - if (setjmp (pnmtopng_jmpbuf_struct.jmpbuf)) { - pm_error ("setjmp returns error condition (2)"); - } + if (setjmp (pnmtopng_jmpbuf_struct.jmpbuf)) { + pm_error ("setjmp returns error condition (2)"); + } - doIhdrChunk(pngxP, cols, rows, depth, colorMapped, pnmType, alpha); + doIhdrChunk(pngxP, cols, rows, depth, colorMapped, colorPng, alpha); - if (cmdline.interlace) - pngx_setInterlaceHandling(pngxP); + if (cmdline.interlace) + pngx_setInterlaceHandling(pngxP); - doGamaChunk(cmdline, pngxP); + doGamaChunk(cmdline, pngxP); - doChrmChunk(cmdline, pngxP); + doChrmChunk(cmdline, pngxP); - doPhysChunk(cmdline, pngxP); + doPhysChunk(cmdline, pngxP); - if (pngx_colorType(pngxP) == PNG_COLOR_TYPE_PALETTE) { + if (pngx_colorType(pngxP) == PNG_COLOR_TYPE_PALETTE) { - /* creating PNG palette (Not counting the transparency palette) */ + /* creating PNG palette (Not counting the transparency palette) */ - createPngPalette(palettePnm, paletteSize, maxval, - transPnm, transSize, alphaMaxval, - palette, trans); - pngx_setPlte(pngxP, palette, paletteSize); + createPngPalette(palettePnm, paletteSize, maxval, + transPnm, transSize, alphaMaxval, + palette, trans); + pngx_setPlte(pngxP, palette, paletteSize); - doHistChunk(pngxP, cmdline.hist, palettePnm, ifP, rasterPos, - cols, rows, maxval, format, cmdline.verbose); - } + doHistChunk(pngxP, cmdline.hist, palettePnm, ifP, rasterPos, + cols, rows, maxval, format, cmdline.verbose); + } - doTrnsChunk(pngxP, trans, transSize, - transparent, transcolor, maxval, png_maxval); + doTrnsChunk(pngxP, trans, transSize, + transparent, transcolor, maxval, pngMaxval); - doBkgdChunk(pngxP, !!cmdline.background, - background_index, backcolor, - maxval, png_maxval, cmdline.verbose); + doBkgdChunk(pngxP, !!cmdline.background, + backgroundIndex, backColor, + maxval, pngMaxval, cmdline.verbose); - doSbitChunk(pngxP, png_maxval, maxval, alpha, alphaMaxval); + doSbitChunk(pngxP, pngMaxval, maxval, alpha, alphaMaxval); - /* tEXT and zTXT chunks */ - if (cmdline.text || cmdline.ztxt) - pngtxt_read(pngxP, tfP, !!cmdline.ztxt, cmdline.verbose); + /* tEXT and zTXT chunks */ + if (cmdline.text || cmdline.ztxt) + pngtxt_read(pngxP, tfP, !!cmdline.ztxt, cmdline.verbose); - doTimeChunk(cmdline, pngxP); + doTimeChunk(cmdline, pngxP); - if (cmdline.filterSet != 0) - pngx_setFilter(pngxP, cmdline.filterSet); + if (cmdline.filterSet != 0) + pngx_setFilter(pngxP, cmdline.filterSet); - setZlibCompression(pngxP, cmdline.zlibCompression); + setZlibCompression(pngxP, cmdline.zlibCompression); - png_init_io(pngxP->png_ptr, ofP); + png_init_io(pngxP->png_ptr, ofP); - /* write the png-info struct */ - pngx_writeInfo(pngxP); + /* write the png-info struct */ + pngx_writeInfo(pngxP); - /* let libpng take care of, e.g., bit-depth conversions */ - pngx_setPacking(pngxP); + /* let libpng take care of, e.g., bit-depth conversions */ + pngx_setPacking(pngxP); - writeRaster(pngxP, ifP, rasterPos, - cols, rows, maxval, format, - png_maxval, depth, alpha, alpha_mask, cht, caht); + writeRaster(pngxP, ifP, rasterPos, + cols, rows, maxval, format, + pngMaxval, depth, alpha, alpha_mask, cht, caht); - pngx_writeEnd(pngxP); + pngx_writeEnd(pngxP); - pngx_destroy(pngxP); + pngx_destroy(pngxP); - pnm_freerow(xelrow); + pnm_freerow(xelrow); - if (cht) - ppm_freecolorhash(cht); - if (caht) - freecoloralphahash(caht); + if (cht) + ppm_freecolorhash(cht); + if (caht) + freecoloralphahash(caht); - *errorLevelP = errorlevel; + *errorLevelP = errorlevel; } diff --git a/converter/other/pnmtops.c b/converter/other/pnmtops.c index dc55a7e3..1cf23be7 100644 --- a/converter/other/pnmtops.c +++ b/converter/other/pnmtops.c @@ -335,6 +335,22 @@ basebasename(const char * const filespec) { +static void +writeFile(const unsigned char * const buffer, + size_t const writeCt, + const char * const name, + FILE * const ofP) { + + size_t writtenCt; + + writtenCt = fwrite(buffer, 1, writeCt, ofP); + + if (writtenCt != writeCt) + pm_error("Error writing to %s output file", name); +} + + + #define MAX_FILTER_CT 10 /* The maximum number of filters this code is capable of applying */ @@ -494,18 +510,12 @@ flateFilter(FILE * const ifP, */ do { unsigned int have; - size_t bytesWritten; strm.avail_out = chunkSz; strm.next_out = out; deflate(&strm, flush); have = chunkSz - strm.avail_out; - bytesWritten = fwrite(out, 1, have, ofP); - if (ferror(ofP) || bytesWritten != have) { - deflateEnd(&strm); - pm_error("Error writing to internal pipe during " - "flate compression."); - } + writeFile(out, have, "flate filter", ofP); } while (strm.avail_out == 0); assert(strm.avail_in == 0); /* all input is used */ @@ -548,10 +558,8 @@ rlePutBuffer (unsigned int const repeat, if (repeat) { fputc(257 - count, fP); fputc(repeatitem, fP); - } else { - fputc(count - 1, fP); - fwrite(itembuf, 1, count, fP); - } + } else + writeFile(itembuf, count, "rlePutBuffer", fP); } @@ -673,23 +681,24 @@ asciiHexFilter(FILE * const ifP, unsigned char inbuff[40], outbuff[81]; for (eof = false; !eof; ) { - size_t bytesRead; + size_t readCt; - bytesRead = fread(inbuff, 1, 40, ifP); + readCt = fread(inbuff, 1, 40, ifP); - if (bytesRead == 0) + if (readCt == 0) eof = true; else { unsigned int i; - for (i = 0; i < bytesRead; ++i) { + for (i = 0; i < readCt; ++i) { int const item = inbuff[i]; outbuff[i*2] = hexits[item >> 4]; outbuff[i*2+1] = hexits[item & 15]; } } - outbuff[bytesRead * 2] = '\n'; - fwrite(outbuff, 1, bytesRead*2 + 1, ofP); + outbuff[readCt * 2] = '\n'; + + writeFile(outbuff, readCt * 2 + 1, "asciiHex filter", ofP); } fclose(ifP); @@ -737,7 +746,9 @@ ascii85Filter(FILE * const ifP, outbuff[1] = value % 85 + 33; outbuff[0] = value / 85 + 33; - fwrite(outbuff, 1, count + 1, ofP); + writeFile((const unsigned char *)outbuff, count + 1, + "ASCII 85 filter", ofP); + count = value = 0; outcount += 5; } @@ -759,7 +770,8 @@ ascii85Filter(FILE * const ifP, outbuff[0] = value / 85 + 33; outbuff[count + 1] = '\n'; - fwrite(outbuff, 1, count + 2, ofP); + writeFile((const unsigned char *)outbuff, count + 2, + "ASCII 85 filter", ofP); } fclose(ifP); @@ -869,13 +881,22 @@ addFilter(const char * const description, OutputEncoder * const oeP, FILE ** const feedFilePP, pid_t * const pidList) { +/*---------------------------------------------------------------------------- + Add a filter to the front of the chain. - pid_t pid; + Spawn a process to do the filtering, by running function 'filter'. + *feedFilePP is the present head of the chain. We make the new filter + process write its output to that and get its input from a new pipe. + We update *feedFilePP to the sending end of the new pipe. + + Add to the list pidList[] the PID of the process we spawn. +-----------------------------------------------------------------------------*/ FILE * const oldFeedFileP = *feedFilePP; FILE * newFeedFileP; - + pid_t pid; + spawnFilter(oldFeedFileP, filter, oeP, &newFeedFileP, &pid); if (verbose) @@ -883,7 +904,7 @@ addFilter(const char * const description, description, (unsigned)pid); fclose(oldFeedFileP); /* Child keeps this open now */ - + addToPidList(pidList, pid); *feedFilePP = newFeedFileP; @@ -1720,7 +1741,7 @@ convertRowPbm(struct pam * const pamP, bitrow[colChars-1] <<= padRight; /* right edge */ } - fwrite(bitrow, 1, colChars, fP); + writeFile(bitrow, colChars, "PBM reader", fP); } @@ -1863,6 +1884,21 @@ convertRaster(struct pam * const inpamP, +/* FILE MANAGEMENT: File management is pretty hairy here. A filter, which + runs in its own process, needs to be able to cause its output file to + close because it might be an internal pipe and the next stage needs to + know output is done. So the forking process must close its copy of the + file descriptor. BUT: if the output of the filter is not an internal + pipe but this program's output, then we don't want it closed when the + filter terminates because we'll need it to be open for the next image + the program converts (with a whole new chain of filters). + + To prevent the progam output file from getting closed, we pass a + duplicate of it to spawnFilters() and keep the original open. +*/ + + + static void convertPage(FILE * const ifP, int const turnflag, diff --git a/converter/other/pstopnm.c b/converter/other/pstopnm.c index 1dd27140..3704841b 100644 --- a/converter/other/pstopnm.c +++ b/converter/other/pstopnm.c @@ -18,6 +18,7 @@ #define _XOPEN_SOURCE 500 /* Make sure fdopen() is in stdio.h and strdup() is in string.h */ +#include <assert.h> #include <string.h> #include <unistd.h> #include <stdlib.h> @@ -32,8 +33,8 @@ #include "shhopt.h" #include "nstring.h" -enum orientation {PORTRAIT, LANDSCAPE, UNSPECIFIED}; -struct box { +enum Orientation {PORTRAIT, LANDSCAPE, UNSPECIFIED}; +struct Box { /* Description of a rectangle within an image; all coordinates measured in points (1/72") with lower left corner of page being the origin. @@ -45,15 +46,15 @@ struct box { int ury; /* upper right Y coord */ }; -struct cmdlineInfo { +struct CmdlineInfo { /* All the information the user supplied in the command line, in a form easy for the program to use. */ const char * inputFileName; /* Names of input files */ unsigned int forceplain; - struct box extract_box; + struct Box extractBox; unsigned int nocrop; - unsigned int format_type; + unsigned int formatType; unsigned int verbose; float xborder; unsigned int xmax; @@ -62,15 +63,15 @@ struct cmdlineInfo { unsigned int ymax; unsigned int ysize; /* zero means unspecified */ unsigned int dpi; /* zero means unspecified */ - enum orientation orientation; - unsigned int goto_stdout; + enum Orientation orientation; + unsigned int stdout; unsigned int textalphabits; }; static void parseCommandLine(int argc, char ** argv, - struct cmdlineInfo * const cmdlineP) { + struct CmdlineInfo * const cmdlineP) { /*---------------------------------------------------------------------------- Note that the file spec array we return is stored in the storage that was passed to us as the argv array. @@ -82,8 +83,8 @@ parseCommandLine(int argc, char ** argv, unsigned int option_def_index; - unsigned int pbm_opt, pgm_opt, ppm_opt; - unsigned int portrait_opt, landscape_opt; + unsigned int pbmOpt, pgmOpt, ppmOpt; + unsigned int portraitOpt, landscapeOpt; float llx, lly, urx, ury; unsigned int llxSpec, llySpec, urxSpec, urySpec; unsigned int xmaxSpec, ymaxSpec, xsizeSpec, ysizeSpec, dpiSpec; @@ -98,9 +99,9 @@ parseCommandLine(int argc, char ** argv, OPTENT3(0, "urx", OPT_FLOAT, &urx, &urxSpec, 0); OPTENT3(0, "ury", OPT_FLOAT, &ury, &urySpec, 0); OPTENT3(0, "nocrop", OPT_FLAG, NULL, &cmdlineP->nocrop, 0); - OPTENT3(0, "pbm", OPT_FLAG, NULL, &pbm_opt, 0); - OPTENT3(0, "pgm", OPT_FLAG, NULL, &pgm_opt, 0); - OPTENT3(0, "ppm", OPT_FLAG, NULL, &ppm_opt, 0); + OPTENT3(0, "pbm", OPT_FLAG, NULL, &pbmOpt , 0); + OPTENT3(0, "pgm", OPT_FLAG, NULL, &pgmOpt, 0); + OPTENT3(0, "ppm", OPT_FLAG, NULL, &ppmOpt, 0); OPTENT3(0, "verbose", OPT_FLAG, NULL, &cmdlineP->verbose, 0); OPTENT3(0, "xborder", OPT_FLOAT, &cmdlineP->xborder, NULL, 0); OPTENT3(0, "xmax", OPT_UINT, &cmdlineP->xmax, &xmaxSpec, 0); @@ -109,9 +110,9 @@ parseCommandLine(int argc, char ** argv, OPTENT3(0, "ymax", OPT_UINT, &cmdlineP->ymax, &ymaxSpec, 0); OPTENT3(0, "ysize", OPT_UINT, &cmdlineP->ysize, &ysizeSpec, 0); OPTENT3(0, "dpi", OPT_UINT, &cmdlineP->dpi, &dpiSpec, 0); - OPTENT3(0, "portrait", OPT_FLAG, NULL, &portrait_opt, 0); - OPTENT3(0, "landscape", OPT_FLAG, NULL, &landscape_opt, 0); - OPTENT3(0, "stdout", OPT_FLAG, NULL, &cmdlineP->goto_stdout, 0); + OPTENT3(0, "portrait", OPT_FLAG, NULL, &portraitOpt, 0); + OPTENT3(0, "landscape", OPT_FLAG, NULL, &landscapeOpt, 0); + OPTENT3(0, "stdout", OPT_FLAG, NULL, &cmdlineP->stdout, 0); OPTENT3(0, "textalphabits", OPT_UINT, &cmdlineP->textalphabits, &textalphabitsSpec, 0); @@ -149,38 +150,38 @@ parseCommandLine(int argc, char ** argv, } else cmdlineP->ysize = 0; - if (portrait_opt && !landscape_opt) + if (portraitOpt && !landscapeOpt) cmdlineP->orientation = PORTRAIT; - else if (!portrait_opt && landscape_opt) + else if (!portraitOpt && landscapeOpt) cmdlineP->orientation = LANDSCAPE; - else if (!portrait_opt && !landscape_opt) + else if (!portraitOpt && !landscapeOpt) cmdlineP->orientation = UNSPECIFIED; else pm_error("Cannot specify both -portrait and -landscape options"); - if (pbm_opt) - cmdlineP->format_type = PBM_TYPE; - else if (pgm_opt) - cmdlineP->format_type = PGM_TYPE; - else if (ppm_opt) - cmdlineP->format_type = PPM_TYPE; + if (pbmOpt) + cmdlineP->formatType = PBM_TYPE; + else if (pgmOpt) + cmdlineP->formatType = PGM_TYPE; + else if (ppmOpt) + cmdlineP->formatType = PPM_TYPE; else - cmdlineP->format_type = PPM_TYPE; + cmdlineP->formatType = PPM_TYPE; /* If any one of the 4 bounding box coordinates is given on the command line, we default any of the 4 that aren't. */ if (llxSpec || llySpec || urxSpec || urySpec) { - if (!llxSpec) cmdlineP->extract_box.llx = 72; - else cmdlineP->extract_box.llx = llx * 72; - if (!llySpec) cmdlineP->extract_box.lly = 72; - else cmdlineP->extract_box.lly = lly * 72; - if (!urxSpec) cmdlineP->extract_box.urx = 540; - else cmdlineP->extract_box.urx = urx * 72; - if (!urySpec) cmdlineP->extract_box.ury = 720; - else cmdlineP->extract_box.ury = ury * 72; + if (!llxSpec) cmdlineP->extractBox.llx = 72; + else cmdlineP->extractBox.llx = llx * 72; + if (!llySpec) cmdlineP->extractBox.lly = 72; + else cmdlineP->extractBox.lly = lly * 72; + if (!urxSpec) cmdlineP->extractBox.urx = 540; + else cmdlineP->extractBox.urx = urx * 72; + if (!urySpec) cmdlineP->extractBox.ury = 720; + else cmdlineP->extractBox.ury = ury * 72; } else { - cmdlineP->extract_box.llx = -1; + cmdlineP->extractBox.llx = -1; } if (dpiSpec) { @@ -230,19 +231,19 @@ addPsToFileName(char const origFileName[], *newFileNameP. -----------------------------------------------------------------------------*/ struct stat statbuf; - int stat_rc; + int statRc; - stat_rc = lstat(origFileName, &statbuf); + statRc = lstat(origFileName, &statbuf); - if (stat_rc == 0) + if (statRc == 0) *newFileNameP = strdup(origFileName); else { const char * fileNamePlusPs; pm_asprintf(&fileNamePlusPs, "%s.ps", origFileName); - stat_rc = lstat(fileNamePlusPs, &statbuf); - if (stat_rc == 0) + statRc = lstat(fileNamePlusPs, &statbuf); + if (statRc == 0) *newFileNameP = strdup(fileNamePlusPs); else *newFileNameP = strdup(origFileName); @@ -311,9 +312,9 @@ computeSizeResBlind(unsigned int const xmax, static void -computeSizeRes(struct cmdlineInfo const cmdline, - enum orientation const orientation, - struct box const bordered_box, +computeSizeRes(struct CmdlineInfo const cmdline, + enum Orientation const orientation, + struct Box const borderedBox, unsigned int * const xsizeP, unsigned int * const ysizeP, unsigned int * const xresP, @@ -344,11 +345,11 @@ computeSizeRes(struct cmdlineInfo const cmdline, */ if (orientation == LANDSCAPE) { - sx = bordered_box.ury - bordered_box.lly; - sy = bordered_box.urx - bordered_box.llx; + sx = borderedBox.ury - borderedBox.lly; + sy = borderedBox.urx - borderedBox.llx; } else { - sx = bordered_box.urx - bordered_box.llx; - sy = bordered_box.ury - bordered_box.lly; + sx = borderedBox.urx - borderedBox.llx; + sy = borderedBox.ury - borderedBox.lly; } if (cmdline.dpi) { @@ -373,9 +374,9 @@ computeSizeRes(struct cmdlineInfo const cmdline, -enum postscript_language {COMMON_POSTSCRIPT, ENCAPSULATED_POSTSCRIPT}; +enum PostscriptLanguage {COMMON_POSTSCRIPT, ENCAPSULATED_POSTSCRIPT}; -static enum postscript_language +static enum PostscriptLanguage languageDeclaration(char const inputFileName[], bool const verbose) { /*---------------------------------------------------------------------------- @@ -383,7 +384,7 @@ languageDeclaration(char const inputFileName[], (Except that if the file is on Standard Input or doesn't validly declare a languages, just say it is Common Postscript). -----------------------------------------------------------------------------*/ - enum postscript_language language; + enum PostscriptLanguage language; if (streq(inputFileName, "-")) /* Can't read stdin, because we need it to remain positioned for the @@ -399,9 +400,9 @@ languageDeclaration(char const inputFileName[], if (fgets(line, sizeof(line), infile) == NULL) language = COMMON_POSTSCRIPT; else { - const char eps_header[] = " EPSF-"; + const char epsHeader[] = " EPSF-"; - if (strstr(line, eps_header)) + if (strstr(line, epsHeader)) language = ENCAPSULATED_POSTSCRIPT; else language = COMMON_POSTSCRIPT; @@ -418,61 +419,64 @@ languageDeclaration(char const inputFileName[], -static struct box -computeBoxToExtract(struct box const cmdline_extract_box, +static struct Box +computeBoxToExtract(struct Box const cmdlineExtractBox, char const inputFileName[], bool const verbose) { - struct box retval; + struct Box retval; - if (cmdline_extract_box.llx != -1) + if (cmdlineExtractBox.llx != -1) /* User told us what box to extract, so that's what we'll do */ - retval = cmdline_extract_box; + retval = cmdlineExtractBox; else { /* Try to get the bounding box from the DSC %%BoundingBox statement (A Postscript comment) in the input. */ - struct box ps_bb; /* Box described by %%BoundingBox stmt in input */ + struct Box psBb; /* Box described by %%BoundingBox stmt in input */ if (streq(inputFileName, "-")) /* Can't read stdin, because we need it to remain positioned for the Ghostscript interpreter to read it. */ - ps_bb.llx = -1; + psBb.llx = -1; else { - FILE *infile; - int found_BB, eof; /* logical */ - infile = pm_openr(inputFileName); + FILE * ifP; + bool foundBb; + bool eof; + + ifP = pm_openr(inputFileName); - found_BB = FALSE; - eof = FALSE; - while (!eof && !found_BB) { + for (foundBb = FALSE, eof = FALSE; !foundBb && !eof; ) { char line[200]; - - if (fgets(line, sizeof(line), infile) == NULL) + char * fgetsRc; + + fgetsRc = fgets(line, sizeof(line), ifP); + + if (fgetsRc == NULL) eof = TRUE; else { int rc; rc = sscanf(line, "%%%%BoundingBox: %d %d %d %d", - &ps_bb.llx, &ps_bb.lly, - &ps_bb.urx, &ps_bb.ury); + &psBb.llx, &psBb.lly, + &psBb.urx, &psBb.ury); if (rc == 4) - found_BB = TRUE; + foundBb = TRUE; } } - fclose(infile); + fclose(ifP); - if (!found_BB) { - ps_bb.llx = -1; + if (!foundBb) { + psBb.llx = -1; pm_message("Warning: no %%%%BoundingBox statement " - "in the input or command line.\n" + "in the input or command line. " "Will use defaults"); } } - if (ps_bb.llx != -1) { + if (psBb.llx != -1) { if (verbose) pm_message("Using %%%%BoundingBox statement from input."); - retval = ps_bb; + retval = psBb; } else { /* Use the center of an 8.5" x 11" page with 1" border all around*/ retval.llx = 72; @@ -489,13 +493,14 @@ computeBoxToExtract(struct box const cmdline_extract_box, -static enum orientation -computeOrientation(struct cmdlineInfo const cmdline, - struct box const extract_box) { +static enum Orientation +computeOrientation(struct CmdlineInfo const cmdline, + struct Box const extractBox) { - enum orientation retval; - unsigned int const input_width = extract_box.urx - extract_box.llx; - unsigned int const input_height = extract_box.ury - extract_box.lly; + unsigned int const inputWidth = extractBox.urx - extractBox.llx; + unsigned int const inputHeight = extractBox.ury - extractBox.lly; + + enum Orientation retval; if (cmdline.orientation != UNSPECIFIED) retval = cmdline.orientation; @@ -506,24 +511,24 @@ computeOrientation(struct cmdlineInfo const cmdline, so we can't use output dimensions to make the decision. So just use the input dimensions. */ - if (input_height > input_width) retval = PORTRAIT; + if (inputHeight > inputWidth) retval = PORTRAIT; else retval = LANDSCAPE; } else { - int output_width, output_height; + unsigned int outputWidth, outputHeight; if (cmdline.xsize) { /* He gave xsize and ysize, so that's the output size */ - output_width = cmdline.xsize; - output_height = cmdline.ysize; + outputWidth = cmdline.xsize; + outputHeight = cmdline.ysize; } else { /* Well then we'll just use his (or default) xmax, ymax */ - output_width = cmdline.xmax; - output_height = cmdline.ymax; + outputWidth = cmdline.xmax; + outputHeight = cmdline.ymax; } - if (input_height > input_width && output_height > output_width) + if (inputHeight > inputWidth && outputHeight > outputWidth) retval = PORTRAIT; - else if (input_height < input_width && - output_height < output_width) + else if (inputHeight < inputWidth && + outputHeight < outputWidth) retval = PORTRAIT; else retval = LANDSCAPE; @@ -534,32 +539,39 @@ computeOrientation(struct cmdlineInfo const cmdline, -static struct box -addBorders(struct box const input_box, - float const xborder_scale, - float const yborder_scale, +static struct Box +addBorders(struct Box const inputBox, + float const xborderScale, + float const yborderScale, bool const verbose) { /*---------------------------------------------------------------------------- - Return a box which is 'input_box' plus some borders. + Return a box which is 'inputBox' plus some borders. - Add left and right borders that are the fraction 'xborder_scale' of the + Add left and right borders that are the fraction 'xborderScale' of the width of the input box; likewise for top and bottom borders with - 'yborder_scale'. + 'yborderScale'. -----------------------------------------------------------------------------*/ - struct box retval; + unsigned int const leftRightBorderSize = + ROUNDU((inputBox.urx - inputBox.llx) * xborderScale); + unsigned int const topBottomBorderSize = + ROUNDU((inputBox.ury - inputBox.lly) * yborderScale); + + struct Box retval; + - const int left_right_border_size = - (int) ((input_box.urx - input_box.llx) * xborder_scale + 0.5); - const int top_bottom_border_size = - (int) ((input_box.ury - input_box.lly) * yborder_scale + 0.5); + assert(inputBox.urx >= inputBox.llx); + assert(inputBox.ury >= inputBox.lly); - retval.llx = input_box.llx - left_right_border_size; - retval.lly = input_box.lly - top_bottom_border_size; - retval.urx = input_box.urx + left_right_border_size; - retval.ury = input_box.ury + top_bottom_border_size; + assert(inputBox.llx >= leftRightBorderSize); + assert(inputBox.lly >= topBottomBorderSize); + + retval.llx = inputBox.llx - leftRightBorderSize; + retval.lly = inputBox.lly - topBottomBorderSize; + retval.urx = inputBox.urx + leftRightBorderSize; + retval.ury = inputBox.ury + topBottomBorderSize; if (verbose) - pm_message("With borders, extracted box is ((%d,%d),(%d,%d))", + pm_message("With borders, extracted box is ((%u,%u),(%u,%u))", retval.llx, retval.lly, retval.urx, retval.ury); return retval; @@ -568,8 +580,8 @@ addBorders(struct box const input_box, static const char * -computePstrans(struct box const box, - enum orientation const orientation, +computePstrans(struct Box const box, + enum Orientation const orientation, int const xsize, int const ysize, int const xres, @@ -598,11 +610,20 @@ computePstrans(struct box const box, static const char * -computeOutfileArg(struct cmdlineInfo const cmdline) { - +computeOutfileArg(struct CmdlineInfo const cmdline) { +/*---------------------------------------------------------------------------- + Determine the value for the "OutputFile" variable to pass to Ghostscript, + which is what tells Ghostscript where to put its output. This is either + a pattern such as "foo%03d.ppm" or "-" to indicate Standard Output. + + We go with "-" if, according to 'cmdline', the user asked for + Standard Output or is giving his input on Standard Input. Otherwise, + we go with the pattern, based on the name of the input file and output + format type the user requested. +-----------------------------------------------------------------------------*/ const char * retval; /* malloc'ed */ - if (cmdline.goto_stdout) + if (cmdline.stdout) retval = strdup("-"); else if (streq(cmdline.inputFileName, "-")) retval = strdup("-"); @@ -616,12 +637,12 @@ computeOutfileArg(struct cmdlineInfo const cmdline) { /* The input file name ends in ".ps". Chop it off. */ basename[strlen(basename)-3] = '\0'; - switch (cmdline.format_type) { + switch (cmdline.formatType) { case PBM_TYPE: suffix = "pbm"; break; case PGM_TYPE: suffix = "pgm"; break; case PPM_TYPE: suffix = "ppm"; break; - default: pm_error("Internal error: invalid value for format_type: %d", - cmdline.format_type); + default: pm_error("Internal error: invalid value for formatType: %d", + cmdline.formatType); } pm_asprintf(&retval, "%s%%03d.%s", basename, suffix); @@ -633,17 +654,17 @@ computeOutfileArg(struct cmdlineInfo const cmdline) { static const char * -computeGsDevice(int const format_type, +computeGsDevice(int const formatType, bool const forceplain) { const char * basetype; const char * retval; - switch (format_type) { + switch (formatType) { case PBM_TYPE: basetype = "pbm"; break; case PGM_TYPE: basetype = "pgm"; break; case PPM_TYPE: basetype = "ppm"; break; - default: pm_error("Internal error: invalid value format_type"); + default: pm_error("Internal error: invalid value formatType"); } if (forceplain) retval = strdup(basetype); @@ -703,14 +724,13 @@ findGhostscriptProg(const char ** const retvalP) { static void execGhostscript(int const inputPipeFd, - char const ghostscript_device[], - char const outfile_arg[], + char const ghostscriptDevice[], + char const outfileArg[], int const xsize, int const ysize, int const xres, int const yres, unsigned int const textalphabits, - char const inputFileName[], bool const verbose) { const char * arg0; @@ -729,8 +749,8 @@ execGhostscript(int const inputPipeFd, close(inputPipeFd); pm_asprintf(&arg0, "gs"); - pm_asprintf(&deviceopt, "-sDEVICE=%s", ghostscript_device); - pm_asprintf(&outfileopt, "-sOutputFile=%s", outfile_arg); + pm_asprintf(&deviceopt, "-sDEVICE=%s", ghostscriptDevice); + pm_asprintf(&outfileopt, "-sOutputFile=%s", outfileArg); pm_asprintf(&gopt, "-g%dx%d", xsize, ysize); pm_asprintf(&ropt, "-r%dx%d", xres, yres); pm_asprintf(&textalphabitsopt, "-dTextAlphaBits=%u", textalphabits); @@ -760,26 +780,26 @@ execGhostscript(int const inputPipeFd, static void -executeGhostscript(char const pstrans[], - char const ghostscript_device[], - char const outfile_arg[], - int const xsize, - int const ysize, - int const xres, - int const yres, - unsigned int const textalphabits, - char const inputFileName[], - enum postscript_language const language, - bool const verbose) { - - int gs_exit; /* wait4 exit code from Ghostscript */ - FILE *gs; /* Pipe to Ghostscript's standard input */ - FILE *infile; +executeGhostscript(char const pstrans[], + char const ghostscriptDevice[], + char const outfileArg[], + int const xsize, + int const ysize, + int const xres, + int const yres, + unsigned int const textalphabits, + char const inputFileName[], + enum PostscriptLanguage const language, + bool const verbose) { + + int gsTermStatus; /* termination status of Ghostscript process */ + FILE * pipeToGsP; /* Pipe to Ghostscript's standard input */ + FILE * ifP; int rc; int eof; /* End of file on input */ int pipefd[2]; - if (strlen(outfile_arg) > 80) + if (strlen(outfileArg) > 80) pm_error("output file spec too long."); rc = pm_pipe(pipefd); @@ -794,20 +814,20 @@ executeGhostscript(char const pstrans[], else if (rc == 0) { /* Child process */ close(pipefd[1]); - execGhostscript(pipefd[0], ghostscript_device, outfile_arg, + execGhostscript(pipefd[0], ghostscriptDevice, outfileArg, xsize, ysize, xres, yres, textalphabits, - inputFileName, verbose); + verbose); } else { pid_t const ghostscriptPid = rc; int const pipeToGhostscriptFd = pipefd[1]; /* parent process */ close(pipefd[0]); - gs = fdopen(pipeToGhostscriptFd, "w"); - if (gs == NULL) + pipeToGsP = fdopen(pipeToGhostscriptFd, "w"); + if (pipeToGsP == NULL) pm_error("Unable to open stream on pipe to Ghostscript process."); - infile = pm_openr(inputFileName); + ifP = pm_openr(inputFileName); /* In encapsulated Postscript, we the encapsulator are supposed to handle showing the page (which we do by passing a showpage @@ -822,12 +842,12 @@ executeGhostscript(char const pstrans[], here, I think, so I boiled it down a bit. JM */ if (language == ENCAPSULATED_POSTSCRIPT) - fprintf(gs, "\n/b4_Inc_state save def /showpage { } def\n"); + fprintf(pipeToGsP, "\n/b4_Inc_state save def /showpage { } def\n"); if (verbose) pm_message("Postscript prefix command: '%s'", pstrans); - fprintf(gs, "%s\n", pstrans); + fprintf(pipeToGsP, "%s\n", pstrans); /* If our child dies, it closes the pipe and when we next write to it, we get a SIGPIPE. We must survive that signal in order to report @@ -840,34 +860,34 @@ executeGhostscript(char const pstrans[], char buffer[4096]; int bytes_read; - bytes_read = fread(buffer, 1, sizeof(buffer), infile); + bytes_read = fread(buffer, 1, sizeof(buffer), ifP); if (bytes_read == 0) eof = TRUE; else - fwrite(buffer, 1, bytes_read, gs); + fwrite(buffer, 1, bytes_read, pipeToGsP); } - pm_close(infile); + pm_close(ifP); if (language == ENCAPSULATED_POSTSCRIPT) - fprintf(gs, "\nb4_Inc_state restore showpage\n"); + fprintf(pipeToGsP, "\nb4_Inc_state restore showpage\n"); - fclose(gs); + fclose(pipeToGsP); - waitpid(ghostscriptPid, &gs_exit, 0); + waitpid(ghostscriptPid, &gsTermStatus, 0); if (rc < 0) pm_error("Wait for Ghostscript process to terminated failed. " "errno = %d (%s)", errno, strerror(errno)); - if (gs_exit != 0) { - if (WIFEXITED(gs_exit)) + if (gsTermStatus != 0) { + if (WIFEXITED(gsTermStatus)) pm_error("Ghostscript failed. Exit code=%d\n", - WEXITSTATUS(gs_exit)); - else if (WIFSIGNALED(gs_exit)) + WEXITSTATUS(gsTermStatus)); + else if (WIFSIGNALED(gsTermStatus)) pm_error("Ghostscript process died due to a signal %d.", - WTERMSIG(gs_exit)); + WTERMSIG(gsTermStatus)); else pm_error("Ghostscript process died with exit code %d", - gs_exit); + gsTermStatus); } } } @@ -877,22 +897,22 @@ executeGhostscript(char const pstrans[], int main(int argc, char ** argv) { - struct cmdlineInfo cmdline; + struct CmdlineInfo cmdline; const char * inputFileName; /* malloc'ed */ /* The file specification of our Postscript input file */ unsigned int xres, yres; /* Resolution in pixels per inch */ unsigned int xsize, ysize; /* output image size in pixels */ - struct box extract_box; + struct Box extractBox; /* coordinates of the box within the input we are to extract; i.e. that will become the output. */ - struct box bordered_box; + struct Box borderedBox; /* Same as above, but expanded to include borders */ - enum postscript_language language; - enum orientation orientation; - const char * ghostscript_device; - const char * outfile_arg; + enum PostscriptLanguage language; + enum Orientation orientation; + const char * ghostscriptDevice; + const char * outfileArg; const char * pstrans; pnm_init(&argc, argv); @@ -901,36 +921,36 @@ main(int argc, char ** argv) { addPsToFileName(cmdline.inputFileName, &inputFileName, cmdline.verbose); - extract_box = computeBoxToExtract(cmdline.extract_box, inputFileName, + extractBox = computeBoxToExtract(cmdline.extractBox, inputFileName, cmdline.verbose); language = languageDeclaration(inputFileName, cmdline.verbose); - orientation = computeOrientation(cmdline, extract_box); + orientation = computeOrientation(cmdline, extractBox); - bordered_box = addBorders(extract_box, cmdline.xborder, cmdline.yborder, - cmdline.verbose); + borderedBox = addBorders(extractBox, cmdline.xborder, cmdline.yborder, + cmdline.verbose); - computeSizeRes(cmdline, orientation, bordered_box, + computeSizeRes(cmdline, orientation, borderedBox, &xsize, &ysize, &xres, &yres); - pstrans = computePstrans(bordered_box, orientation, + pstrans = computePstrans(borderedBox, orientation, xsize, ysize, xres, yres); - outfile_arg = computeOutfileArg(cmdline); + outfileArg = computeOutfileArg(cmdline); - ghostscript_device = - computeGsDevice(cmdline.format_type, cmdline.forceplain); + ghostscriptDevice = + computeGsDevice(cmdline.formatType, cmdline.forceplain); - pm_message("Writing %s file", ghostscript_device); + pm_message("Writing %s format", ghostscriptDevice); - executeGhostscript(pstrans, ghostscript_device, outfile_arg, + executeGhostscript(pstrans, ghostscriptDevice, outfileArg, xsize, ysize, xres, yres, cmdline.textalphabits, inputFileName, language, cmdline.verbose); - pm_strfree(ghostscript_device); - pm_strfree(outfile_arg); + pm_strfree(ghostscriptDevice); + pm_strfree(outfileArg); pm_strfree(pstrans); return 0; diff --git a/converter/other/winicon.h b/converter/other/winicon.h new file mode 100644 index 00000000..9ede01f5 --- /dev/null +++ b/converter/other/winicon.h @@ -0,0 +1,82 @@ +#include "pm_c_util.h" + +#define ICONDIR_TYPE_ICO (1) + +/* windows icon structures */ +struct IconDirEntry { + uint16_t width; /* image width in pixels 0 == 256 */ + uint16_t height; /* image height in pixels 0 == 256 */ + uint8_t color_count; /* 0 if bits_per_pixel >= 8 */ + uint8_t zero; /* 0 */ + uint16_t color_planes; /* 1 */ + uint16_t bits_per_pixel; /* allowed values: 1, 4, 8, 16 or 32 (1) */ + uint32_t size; /* size of image */ + uint32_t offset; /* file offset of image */ + + uint16_t index; /* extra field (not in file) */ +}; + +/* (1) This is from + * http://blogs.msdn.com/b/oldnewthing/archive/2010/10/19/10077610.aspx. + * + * However, the bpp value in the icon directory is used as a hint for + * image selection only. It seems to be legal to set this value to + * zero, and e.g. in SHELL32.DLL of Win98SE, there are many 8bpp + * images described as 24 bit images in the icon directory. + * + * The bpp value of image 1 in icon 150 in SHELL32.DLL of WinXP is 24 + * (in header and BMP). This may be a bug, as the 32 x 32 x 8 image + * is missing, but it shows the Windows icon rendering engine is able + * to cope with 24 bit images). + * + * 16bpp icons are at least rare in the wild. + */ +struct IconDir { + uint16_t zero; /* 0 */ + uint16_t type; /* 1 */ + uint16_t count; /* number of images in icon */ + + unsigned int entriesAllocCt; /* # of allocated slots in 'entries'*/ + struct IconDirEntry * entries; /* one entry for each image */ +}; + +/* BMP image structures */ + +struct BitmapInfoHeader { + uint32_t header_size; /* >= 40 */ + int32_t bm_width; + int32_t bm_height; + uint16_t color_planes; + uint16_t bits_per_pixel; + uint32_t compression_method; + uint32_t image_size; + int32_t horizontal_resolution; /* pixels per meter (!) */ + int32_t vertical_resolution; /* pixels per meter (!) */ + uint32_t colors_in_palette; + uint32_t important_colors; + + bool top_down; /* extra field (not in file) */ + +}; + +typedef enum { + BI_RGB = 0, + BI_BITFIELDS = 3 + +} BiCompression; + +/* PNG image structures */ +#define PNG_HEADER { 0x89, 'P', 'N', 'G', '\r', '\n', 0x1A /* ^Z */, '\n' } + +struct PngIhdr { + uint32_t length; /* 13 */ + uint32_t signature; /* "IHDR" */ + uint32_t width; /* image width in pixels */ + uint32_t height; /* image height in pixels */ + uint8_t bit_depth; /* depth per channel */ + uint8_t color_type; /* recognized values: 0, 2, 3, 4 and 6 */ + uint8_t compression; + uint8_t filter; + uint8_t interlace; + uint32_t crc; +}; diff --git a/converter/other/winicontopam.c b/converter/other/winicontopam.c new file mode 100644 index 00000000..9bee8b3c --- /dev/null +++ b/converter/other/winicontopam.c @@ -0,0 +1,1282 @@ +#include <assert.h> +#include <stdio.h> +#include <string.h> + +#include "netpbm/pm_config.h" +#include "netpbm/pm_c_util.h" +#include "netpbm/mallocvar.h" +#include "netpbm/nstring.h" +#include "netpbm/shhopt.h" +#include "netpbm/pam.h" +#include "netpbm/pm_system.h" + +#include "winicon.h" + +#define RED 0 +#define GRN 1 +#define BLU 2 +#define ALPHA 3 +#define CHANNEL_CHARS "RGBA" + + + +static bool verbose; + + + +struct CmdlineInfo { + + const char * inputFileName; + unsigned int allimages; + unsigned int imageSpec; + unsigned int image; + unsigned int andmasks; + unsigned int headerdump; + unsigned int verbose; +}; + + + +static void +parseCommandLine(int argc, const char **argv, + struct CmdlineInfo * const cmdlineP) { + + optEntry * option_def; + unsigned int option_def_index; + optStruct3 opt3; + + MALLOCARRAY_NOFAIL(option_def, 100); + + option_def_index = 0; + + OPTENT3 (0, "allimages", OPT_FLAG, NULL, + &cmdlineP->allimages, 0); + OPTENT3 (0, "image", OPT_UINT, &cmdlineP->image, + &cmdlineP->imageSpec, 0); + OPTENT3 (0, "andmasks", OPT_FLAG, NULL, + &cmdlineP->andmasks, 0); + OPTENT3 (0, "headerdump", OPT_FLAG, NULL, + &cmdlineP->headerdump, 0); + OPTENT3 (0, "verbose", OPT_FLAG, NULL, + &cmdlineP->verbose, 0); + + opt3.opt_table = option_def; + opt3.short_allowed = false; + opt3.allowNegNum = false; + + pm_optParseOptions3(&argc, (char **)argv, opt3, sizeof(opt3), 0); + + if (cmdlineP->allimages && cmdlineP->imageSpec) + pm_error("You cannot specify both -allimages and -image"); + + if (argc-1 < 1) + cmdlineP->inputFileName = "-"; + else { + cmdlineP->inputFileName = argv[1]; + + if (argc-1 > 1) + pm_error("Too many arguments. The only possible " + "non-option argument is the input file name"); + } + + free(option_def); +} + + + +static unsigned char const pngHeader[] = PNG_HEADER; + + + +struct File { + + FILE * fileP; + const char * name; + pm_filepos pos; + +}; + + + +static uint32_t +u8_le(const unsigned char * const buf, + size_t const offset) { + + return buf[offset + 0]; +} + + + +static uint32_t +u16_le(const unsigned char * const buf, + size_t const offset) { + + return + ((uint32_t)buf[offset + 0] << 0) + + ((uint32_t)buf[offset + 1] << 8); +} + + + +static uint32_t +u32_le(const unsigned char * const buf, + size_t const offset) { + + return + ((uint32_t)buf[offset + 0] << 0) + + ((uint32_t)buf[offset + 1] << 8) + + ((uint32_t)buf[offset + 2] << 16) + + ((uint32_t)buf[offset + 3] << 24); +} + + + +static uint32_t +s32_le(const unsigned char * const buf, + size_t const offset) { + + return + ((uint32_t)buf[offset + 0] << 0) + + ((uint32_t)buf[offset + 1] << 8) + + ((uint32_t)buf[offset + 2] << 16) + + ((uint32_t)buf[offset + 3] << 24); +} + + + +static uint32_t +u8_be(const unsigned char * const buf, + size_t const offset) { + + return buf[offset + 0]; +} + + + +static uint32_t +u32_be(const unsigned char * const buf, + size_t const offset) { + + return + ((uint32_t)buf[offset + 0] << 24) + + ((uint32_t)buf[offset + 1] << 16) + + ((uint32_t)buf[offset + 2] << 8) + + ((uint32_t)buf[offset + 3] << 0); +} + + + +static uint32_t +u32_xx(const unsigned char * const buf, + size_t const offset) { + + uint32_t u32; + + ((uint8_t*) &u32)[0] = buf[offset + 0]; + ((uint8_t*) &u32)[1] = buf[offset + 1]; + ((uint8_t*) &u32)[2] = buf[offset + 2]; + ((uint8_t*) &u32)[3] = buf[offset + 3]; + + return (u32); +} + + + +static int +cmpfn(const void * const aP, + const void * const bP) { + + const struct IconDirEntry * const dirEntryAP = aP; + const struct IconDirEntry * const dirEntryBP = bP; + + if (dirEntryAP->offset < dirEntryBP->offset) + return -1; + else if (dirEntryAP->offset > dirEntryBP->offset) + return +1; + else + return 0; +} + + + +static void +dumpIconDir(const struct IconDir * const dirP) { + + unsigned int i; + + pm_message("Type: %u", dirP->type); + pm_message("Icon directory has %u images:", dirP->count); + + for (i = 0; i < dirP->count; ++i) { + const struct IconDirEntry * const dirEntryP = &dirP->entries[i]; + + pm_message("width: %u", dirEntryP->width); + pm_message("height: %u", dirEntryP->height); + pm_message("color count: %u", dirEntryP->color_count); + pm_message("# color planes: %u", dirEntryP->color_planes); + pm_message("bits per pixel: %u", dirEntryP->bits_per_pixel); + pm_message("offset in file of image: %u", dirEntryP->offset); + pm_message("size of image: %u", dirEntryP->size); + pm_message("zero field: %u", dirEntryP->zero); + } +} + + + +static struct IconDir * +readIconDir(struct File * const fP, + bool const needHeaderDump) { + + struct IconDir head; + struct IconDir * dirP; + uint32_t imageIndex; /* more bits than dir.count */ + + pm_readlittleshortu(fP->fileP, &head.zero); + pm_readlittleshortu(fP->fileP, &head.type); + pm_readlittleshortu(fP->fileP, &head.count); + fP->pos += 6; + + if (head.zero != 0 || head.type != ICONDIR_TYPE_ICO) + pm_error("Not a valid windows icon file"); + + MALLOCVAR(dirP); + + if (dirP == NULL) + pm_error("Could't allocate memory for Icon directory"); + + MALLOCARRAY(dirP->entries, head.count); + + if (dirP->entries == NULL) + pm_error("Could not allocate memory for %u entries in icon directory", + head.count); + + dirP->zero = head.zero; + dirP->type = head.type; + dirP->count = head.count; + dirP->entriesAllocCt = head.count; + + for (imageIndex = 0; imageIndex < head.count; ++imageIndex) { + struct IconDirEntry * const dirEntryP = &dirP->entries[imageIndex]; + + unsigned char widthField, heightField; + + unsigned long ul; + + pm_readcharu(fP->fileP, &widthField); + dirEntryP->width = (widthField == 0 ? 256 : widthField); + + pm_readcharu(fP->fileP, &heightField); + dirEntryP->height = (heightField == 0 ? 256 : heightField); + + pm_readcharu(fP->fileP, &dirEntryP->color_count); + + pm_readcharu(fP->fileP, &dirEntryP->zero); + + pm_readlittleshortu(fP->fileP, &dirEntryP->color_planes); + + pm_readlittleshortu(fP->fileP, &dirEntryP->bits_per_pixel); + + pm_readlittlelongu(fP->fileP, &ul); dirEntryP->size = ul; + + pm_readlittlelongu(fP->fileP, &ul); dirEntryP->offset = ul; + + fP->pos += 16; + + dirEntryP->index = imageIndex; + } + + /* The following is paranoia code only: + + I've never seen a windows icon file in the wild with having the entries + in the directory stored in a different order than the images + themselves. However, the file format allows for it ... + */ + qsort(dirP->entries, dirP->count, sizeof(struct IconDirEntry), cmpfn); + + if (verbose) { + pm_message("%s icon directory (%u image%s):", + fP->name, + dirP->count, dirP->count == 1 ? "" : "s"); + + for (imageIndex = 0; imageIndex < dirP->count; ++imageIndex) { + const struct IconDirEntry * const dirEntryP = + &dirP->entries[imageIndex]; + + uint32_t colorCt; + + if (dirEntryP->bits_per_pixel == 0) + colorCt = 0; + else if (dirEntryP->bits_per_pixel >= 32) + colorCt = 1u << 24; + else + colorCt = 1u << dirEntryP->bits_per_pixel; + + if (dirEntryP->color_count != 0 && + colorCt > dirEntryP->color_count) { + colorCt = dirEntryP->color_count; + } + pm_message ("%5u: %3u x %3u, %8u colors, %5u bytes", + dirEntryP->index, + dirEntryP->width, + dirEntryP->height, + colorCt, + dirEntryP->size); + } + } + + if (needHeaderDump) + dumpIconDir(dirP); + + return dirP; +} + + + +static void +freeIconDir(struct IconDir * const dirP) { + + free(dirP->entries); + free(dirP); +} + + + +static const unsigned char * +readImage(struct File * const fP, + struct IconDirEntry * const dirEntryP) { + + size_t rc; + unsigned char * image; + uint32_t skippedCt; + + /* Don't try to read an image that is smaller than the + BITMAPINFOHEADER of BMP images (40 bytes). + + PNG compressed images can't be smaller than that either, as the + PNG header plus the mandantory IHDR and IEND chunks already take + 8 + 25 + 12 = 35 bytes, and there is to be a IDAT chunk too. + */ + if (dirEntryP->size < 40) { + pm_error("image %2u: format violation: too small as an image.", + dirEntryP->index); + } + if ((pm_filepos) dirEntryP->offset < fP->pos) + pm_error("image %2u: format violation: invalid offset.", + dirEntryP->index); + + /* The following is paranoia code only: + + I've never seen a windows icon file in the wild with gaps between + the images, but the file format allows for it, and Microsoft + expects the user to fseek() to the start of each image. + */ + skippedCt = 0; + + while ((pm_filepos) dirEntryP->offset > fP->pos) { + if (getc(fP->fileP) == EOF) { + pm_error("seeking to image %u: unexpected EOF", dirEntryP->index); + } + ++fP->pos; + ++skippedCt; + } + + /* The additional four bytes are for purify and friends, as the + routines reading BMP XOR and AND masks might read (but not + evaluate) some bytes beyond the image data. + */ + image = malloc(dirEntryP->size + sizeof(uint32_t)); + if (image == NULL) + pm_error("out of memory."); + + rc = fread (image, 1, dirEntryP->size, fP->fileP); + if (rc != dirEntryP->size) { + pm_error("reading image %2u: unexpected EOF", dirEntryP->index); + } + fP->pos += dirEntryP->size; + + return image; +} + + + +static uint8_t +getIdx1(const unsigned char * const bitmap, + uint32_t const offset, + int16_t const col) { + + return u8_le(bitmap, offset + (col >> 3)) >> (7 - (col & 0x07)) & 0x1; +} + + + +static uint8_t +getIdx4(const unsigned char * const bitmap, + uint32_t const offset, + int16_t const col) { + + if ((col & 1) == 0x0000) + return u8_le(bitmap, offset + (col >> 1)) >> 4 & 0x0F; + else + return u8_le(bitmap, offset + (col >> 1)) >> 0 & 0x0F; +} + + + +static uint8_t +getIdx8(const unsigned char * const bitmap, + uint32_t const offset, + int16_t const col) { + + return u8_le(bitmap, offset + col); +} + + + +typedef unsigned char PaletteEntry[4]; + + + +static void +dumpPalette(const PaletteEntry * const palette, + unsigned int const colorCt) { + + unsigned int i; + + for (i = 0; i < colorCt; ++i) { + pm_message("Color %u: (%u, %u, %u)", + i, palette[i][2], palette[i][1], palette[i][0]); + } +} + + + +static void +readXorPalette(struct BitmapInfoHeader * const hdrP, + const unsigned char * const bitmap, + uint32_t const maxSize, + tuple ** const tuples, + uint16_t const index, + bool const needHeaderDump, + uint32_t * const bytesConsumedP) { + + uint32_t paletteSize; + + int16_t row; + const PaletteEntry * palette; + uint32_t truncatedXorSize; + uint32_t bytesConsumed; + uint32_t bytesPerRow; + const unsigned char * bitmapCursor; + uint32_t sizeRemaining; + + uint8_t (*getIdx) (const unsigned char * bitmap, + uint32_t rowOffset, + int16_t col); + + if (hdrP->compression_method != BI_RGB) + pm_error("image %2u: invalid compression method %u.", + index, hdrP->compression_method); + + assert(hdrP->bits_per_pixel < 16); + + switch (hdrP->bits_per_pixel) { + case 1: + if (hdrP->colors_in_palette == 0) + hdrP->colors_in_palette = 2; + getIdx = getIdx1; + break; + + case 4: + if (hdrP->colors_in_palette == 0) + hdrP->colors_in_palette = 16; + getIdx = getIdx4; + break; + + case 8: + if (hdrP->colors_in_palette == 0) + hdrP->colors_in_palette = 256; + getIdx = getIdx8; + break; + + default: + pm_error("image %2u: " + "bits per pixel is a value we don't understand: %u", + index, hdrP->bits_per_pixel); + getIdx = NULL; + } + + bitmapCursor = &bitmap[0]; /* initial value */ + sizeRemaining = maxSize; /* initial value */ + bytesConsumed = 0; /* initial value */ + + paletteSize = hdrP->colors_in_palette * 4; + + if (sizeRemaining < paletteSize) + pm_error("image %2u: " + "reading palette: image truncated.", index); + + palette = (const PaletteEntry *) bitmapCursor; + + if (needHeaderDump) + dumpPalette(palette, hdrP->colors_in_palette); + + bitmapCursor += paletteSize; + sizeRemaining -= paletteSize; + bytesConsumed += paletteSize; + + { + uint32_t const xorSize = (uint32_t) + (((hdrP->bits_per_pixel * hdrP->bm_width + 31) / 32) + * 4 * hdrP->bm_height / 2); + + if (sizeRemaining < xorSize) { + pm_message("image %2u: " + "reading XOR mask: image truncated.", index); + truncatedXorSize = sizeRemaining; + } else + truncatedXorSize = xorSize; + } + + bytesPerRow = ((hdrP->bits_per_pixel * hdrP->bm_width + 31) / 32) * 4; + + for (row = 0; hdrP->bm_height / 2 > row; ++row) { + uint32_t rowOffset; + + if (hdrP->top_down) + rowOffset = row * bytesPerRow; + else + rowOffset = (hdrP->bm_height / 2 - row - 1) * bytesPerRow; + + if (rowOffset + bytesPerRow <= truncatedXorSize) { + int16_t col; + for (col = 0; hdrP->bm_width > col; ++col) { + uint8_t const idx = getIdx(bitmapCursor, rowOffset, col); + + if (idx > hdrP->colors_in_palette) + pm_error("invalid palette index in row %u, column %u.", + row, col); + + /* The palette is an array of little-endian 32-bit values, + where the RGB value is encoded as follows: + + red: bits 2^16..2^23 + green: bits 2^8 ..2^15 + blue: bits 2^0 ..2^7 + */ + tuples[row][col][PAM_RED_PLANE] = palette[idx][2]; + tuples[row][col][PAM_GRN_PLANE] = palette[idx][1]; + tuples[row][col][PAM_BLU_PLANE] = palette[idx][0]; + } + } + } + + bitmapCursor += truncatedXorSize; + sizeRemaining -= truncatedXorSize; + bytesConsumed += truncatedXorSize; + + *bytesConsumedP = bytesConsumed; +} + + + +static void +readXorBitfields(struct BitmapInfoHeader * const hdrP, + const unsigned char * const bitmap, + uint32_t const maxSize, + tuple ** const tuples, + uint16_t const index, + bool * const haveAlphaP, + uint32_t * const bytesConsumedP) { + + uint32_t bitfields[4]; + uint8_t shift [4]; + sample maxval [4]; + + int16_t row; + uint32_t bytesConsumed; + uint32_t bytesPerSample; + uint32_t bytesPerRow; + const unsigned char * bitmapCursor; + uint32_t sizeRemaining; + uint32_t truncatedXorSize; + + static uint8_t alphas [256]; + bool allOpaque; + bool allTransparent; + + bytesConsumed = 0; + + if (hdrP->compression_method != BI_RGB + && hdrP->compression_method != BI_BITFIELDS) + pm_error("image %2u: invalid compression method %u.", + index, hdrP->compression_method); + + assert(hdrP->bits_per_pixel >= 16); + + switch (hdrP->bits_per_pixel) { + case 16: + bytesPerSample = 2; + bitfields[RED] = 0x7C00; + bitfields[GRN] = 0x03E0; + bitfields[BLU] = 0x001F; + bitfields[ALPHA] = 0x0000; + break; + + case 24: + bytesPerSample = 3; + bitfields[RED] = 0xFF0000; + bitfields[GRN] = 0x00FF00; + bitfields[BLU] = 0x0000FF; + bitfields[ALPHA] = 0x000000; + break; + + case 32: + bytesPerSample = 4; + bitfields[RED] = 0x00FF0000; + bitfields[GRN] = 0x0000FF00; + bitfields[BLU] = 0x000000FF; + bitfields[ALPHA] = 0xFF000000; + break; + + default: + pm_error("image %2u: bits per pixel is one we don't understand: %u.", + index, hdrP->bits_per_pixel); + bytesPerSample = 0; + } + + bitmapCursor = &bitmap[0]; /* initial value */ + sizeRemaining = maxSize; /* initial value */ + + /* read bit fields from image data */ + if (hdrP->compression_method == BI_BITFIELDS) { + if (sizeRemaining < 12) + pm_error("image %2u: " + "reading bit fields: image truncated.", index); + + bitfields[RED] = u32_le(bitmapCursor, 0); + bitfields[GRN] = u32_le(bitmapCursor, 4); + bitfields[BLU] = u32_le(bitmapCursor, 8); + bitfields[ALPHA] = 0; + + bitmapCursor += 12; + sizeRemaining -= 12; + bytesConsumed += 12; + } + + /* determine shift and maxval from bit field for each channel */ + { + unsigned int i; + + for (i = 0; 4 > i; ++i) { + if (bitfields[i] == 0) { + maxval[i] = 1; + shift [i] = 0; + } else { + unsigned int j; + + maxval[i] = bitfields[i]; + + for (j = 0; 32 > j; ++j) { + if ((maxval[i] & 0x1) != 0) + break; + maxval[i] >>= 1; + } + shift[i] = j; + } + + } + } + + /* read the XOR mask */ + { + uint32_t const xorSize = (uint32_t) + (((hdrP->bits_per_pixel * hdrP->bm_width + 31) / 32) + * 4 * hdrP->bm_height / 2); + + if (sizeRemaining < xorSize) { + pm_message("image %2u: " + "reading XOR mask: image truncated.", index); + truncatedXorSize = sizeRemaining; + } else + truncatedXorSize = xorSize; + } + + bytesPerRow = ((hdrP->bits_per_pixel * hdrP->bm_width + 31) / 32) * 4; + MEMSZERO(alphas); + + for (row = 0, allOpaque = true, allTransparent = true; + hdrP->bm_height / 2 > row; + ++row) { + + uint32_t offset; + + if (hdrP->top_down) + offset = row * bytesPerRow; + else + offset = (hdrP->bm_height / 2 - row - 1) * bytesPerRow; + + if (offset + bytesPerRow <= truncatedXorSize) { + unsigned int col; + for (col = 0; col < hdrP->bm_width; ++col) { + uint32_t const pixel = u32_le(bitmapCursor, offset); + offset += bytesPerSample; + + tuples[row][col][PAM_RED_PLANE] = + pnm_scalesample((pixel & bitfields[RED]) >> shift[RED], + maxval[RED], 255); + tuples[row][col][PAM_GRN_PLANE] = + pnm_scalesample((pixel & bitfields[GRN]) >> shift[GRN], + maxval[GRN], 255); + tuples [row][col][PAM_BLU_PLANE] + = pnm_scalesample((pixel & bitfields[BLU]) >> shift[BLU], + maxval[BLU], 255); + + if (bitfields [ALPHA] != 0) { + tuples[row][col][PAM_TRN_PLANE] + = pnm_scalesample( + (pixel & bitfields[ALPHA]) >> shift[ALPHA], + maxval[ALPHA], 255); + + if (tuples[row][col][PAM_TRN_PLANE] != 0) + allTransparent = false; + + if (tuples [row][col][PAM_TRN_PLANE] != 255) + allOpaque = false; + + alphas[tuples[row][col][PAM_TRN_PLANE]] = !0; + } + } + } + } + + bitmapCursor += truncatedXorSize; + sizeRemaining -= truncatedXorSize; + bytesConsumed += truncatedXorSize; + + /* A fully transparent alpha channel (all zero) in XOR mask is + defined to be void by Microsoft, and a fully opaque alpha + channel (all maxval) is trivial and will be dropped. + */ + *haveAlphaP = !allTransparent && !allOpaque; + + if (!allTransparent && verbose) { + unsigned int i; + unsigned int c; + + for (i = 0, c = 0; 256 > i; ++i) { + if (alphas[i] != 0) + ++c; + } + pm_message("image %2u: %u distinct transparency value%s", + index, c, (c == 1) ? "": "s"); + } + *bytesConsumedP = bytesConsumed; +} + + + +static void +readAnd(struct BitmapInfoHeader * const hdrP, + const unsigned char * const bitmap, + uint32_t const maxSize, + tuple ** const tuples, + uint16_t const index, + unsigned int const plane, + sample const maxval) { + + int16_t row; + uint32_t bytesConsumed; + uint32_t bytesPerRow; + uint32_t sizeRemaining; + uint32_t truncatedAndSize; + + sizeRemaining = maxSize; /* initial value */ + bytesConsumed = 0; /* initial value */ + + { + uint32_t const andSize = (uint32_t) + (((1 * hdrP->bm_width + 31) / 32) * 4 * hdrP->bm_height / 2); + + if (sizeRemaining < andSize) { + pm_message ("image %2u: " + "Input image ends %u bytes into the %u-byte " + "AND mask. Implying remainder of mask", + index, sizeRemaining, andSize); + truncatedAndSize = sizeRemaining; + } else + truncatedAndSize = andSize; + } + + bytesPerRow = ((1 * hdrP->bm_width + 31) / 32) * 4; + + for (row = 0; row < hdrP->bm_height / 2; ++row) { + uint32_t offset; + + if (hdrP->top_down) + offset = row * bytesPerRow; + else + offset = (hdrP->bm_height / 2 - row - 1) * bytesPerRow; + + if (offset + bytesPerRow <= sizeRemaining) { + unsigned int col; + + for (col = 0; col < hdrP->bm_width; ++col) { + tuples[row][col][plane] = + ((u8_le(bitmap, offset + col/8) + & (1 << (7 - (col & 0x7)))) == 0x00) ? + maxval : 0 + ; + } + } + } + sizeRemaining -= truncatedAndSize; + bytesConsumed += truncatedAndSize; +} + + + +static void +dumpBmpHeader(struct BitmapInfoHeader const hdr, + unsigned int const imageIndex) { + + pm_message("BMP header for Image %u:", imageIndex); + + pm_message("header size: %u", hdr.header_size); + pm_message("bitmap width: %u", hdr.bm_width); + pm_message("bitmap height * 2: %u", hdr.bm_height); + pm_message("row order: %s", hdr.top_down ? "top down" : "bottom up"); + pm_message("# color planes: %u", hdr.color_planes); + pm_message("bits per pixel: %u", hdr.bits_per_pixel); + pm_message("image size: %u", hdr.image_size); + pm_message("horizontal resolution: %u", hdr.horizontal_resolution); + pm_message("vertical resolution: %u", hdr.vertical_resolution); + pm_message("# colors in palette: %u", hdr.colors_in_palette); + pm_message("# important colors: %u", hdr.important_colors); +} + + + +static void +readBmpHeader(const unsigned char * const image, + uint32_t const size, + unsigned int const imageIndex, + bool const needHeaderDump, + struct BitmapInfoHeader * const hdrP) { + + /* BITMAPINFOHEADER structure */ + + if (size < 40) + pm_error("image %2u: reading BITMAPINFOHEADER: not enough data.", + imageIndex); + + hdrP->header_size = u32_le(image, 0); + hdrP->bm_width = s32_le(image, 4); + hdrP->bm_height = s32_le(image, 8); + hdrP->color_planes = u16_le(image, 12); + hdrP->bits_per_pixel = u16_le(image, 14); + hdrP->compression_method = u32_le(image, 16); + hdrP->image_size = u32_le(image, 20); + hdrP->horizontal_resolution = s32_le(image, 24); + hdrP->vertical_resolution = s32_le(image, 28); + hdrP->colors_in_palette = u32_le(image, 32); + hdrP->important_colors = u32_le(image, 36); + + if (hdrP->bm_height > 0) { + hdrP->top_down = false; + } else { + hdrP->top_down = true; + hdrP->bm_height *= -1; + } + + if (hdrP->header_size < 36 + || hdrP->bm_width == 0 || hdrP->bm_height == 0 + || (hdrP->bm_height & 1) != 0x0000) { + pm_error("image %2u: format violation: invalid BMP header.", + imageIndex); + } + + if (needHeaderDump) + dumpBmpHeader(*hdrP, imageIndex); +} + + + +static void +readXorMask(struct BitmapInfoHeader * const hdrP, + const unsigned char * const imageCursor, + uint32_t const imageSize, + tuple ** const tuples, + uint16_t const index, + bool const needHeaderDump, + bool * const haveAlphaP, + uint32_t * const bytesConsumedP) { +/*---------------------------------------------------------------------------- + Read the so-called XOR mask (for non-monochrome images, this is the + color pixmap) +-----------------------------------------------------------------------------*/ + /* preset the PAM with fully opaque black (just in case the image + is truncated and not all pixels are filled in below). + */ + { + unsigned int row; + + for (row = 0; row < hdrP->bm_height / 2; ++row) { + unsigned int col; + for (col = 0; col < hdrP->bm_width; ++col) { + tuples[row][col][PAM_RED_PLANE] = 0; + tuples[row][col][PAM_GRN_PLANE] = 0; + tuples[row][col][PAM_BLU_PLANE] = 0; + tuples[row][col][PAM_TRN_PLANE] = 255; + } + } + } + + if (hdrP->bits_per_pixel < 16) { + readXorPalette(hdrP, imageCursor, imageSize, tuples, index, + needHeaderDump, + bytesConsumedP); + *haveAlphaP = false; + } else + readXorBitfields(hdrP, imageCursor, imageSize, tuples, index, + haveAlphaP, bytesConsumedP); +} + + + +static void +reportImage(unsigned int const imageIndex, + struct BitmapInfoHeader const hdr, + bool const haveAlpha) { + + const char * const style = + haveAlpha ? "RGB +alpha" : + hdr.bits_per_pixel < 16 ? "RGB/palette" : + "RGB" + ; + + pm_message("image %2u: " + "BMP %3u x %3u x %2u (%s)", + imageIndex, + hdr.bm_width, hdr.bm_height / 2, hdr.bits_per_pixel, + style); +} + + + +static void +convertBmp(const unsigned char * const image, + FILE * const ofP, + struct IconDirEntry * const dirEntryP, + bool const needHeaderDump, + bool const wantAndMaskPlane) { + + struct BitmapInfoHeader hdr; + uint32_t offset; + bool haveAlpha; + uint32_t xorByteCt; + + struct pam outpam; + tuple ** tuples; + + readBmpHeader(image, dirEntryP->size, dirEntryP->index, needHeaderDump, + &hdr); + + offset = hdr.header_size; /* Start after header */ + + if ((dirEntryP->width != hdr.bm_width) + || (dirEntryP->height != hdr.bm_height / 2)) { + pm_message("image %2u: " + "mismatch in header and image dimensions " + "(%u x %u vs. %u x %u)", + dirEntryP->index, + dirEntryP->width, + dirEntryP->height, + hdr.bm_width, + hdr.bm_height / 2); + } + + if ((dirEntryP->bits_per_pixel != 0) + && (dirEntryP->bits_per_pixel != hdr.bits_per_pixel)) { + pm_message("image %2u " + "mismatch in header and image bpp value" + "(%u vs. %u)", + dirEntryP->index, + dirEntryP->bits_per_pixel, + hdr.bits_per_pixel); + } + + outpam.size = sizeof(struct pam); + outpam.len = PAM_STRUCT_SIZE(allocation_depth); + outpam.file = ofP; + outpam.format = PAM_FORMAT; + outpam.width = hdr.bm_width; + outpam.height = hdr.bm_height / 2; + outpam.maxval = 255; + outpam.allocation_depth = 5; + outpam.depth = 0; + /* Just for tuple array allocation; we set the value for the actual + output image below. + */ + + tuples = pnm_allocpamarray(&outpam); + + readXorMask(&hdr, &image[offset], + dirEntryP->size - offset, + tuples, dirEntryP->index, needHeaderDump, + &haveAlpha, &xorByteCt); + + offset += xorByteCt; + + { + /* If there is no alpha channel in XOR mask, store the AND mask to + the transparency plane. Else, here are two transparency + maps. If requested, store the AND mask to a fifth PAM plane + */ + bool haveAnd; + unsigned int andPlane; + + if (!haveAlpha) { + haveAnd = true; + andPlane = PAM_TRN_PLANE; + strcpy (outpam.tuple_type, "RGB_ALPHA"); + outpam.depth = 4; + } else if (wantAndMaskPlane) { + haveAnd = true; + andPlane = PAM_TRN_PLANE + 1; + outpam.depth = 5; + strcpy(outpam.tuple_type, "RGB_ALPHA_ANDMASK"); + } else { + haveAnd = false; + strcpy (outpam.tuple_type, "RGB_ALPHA"); + outpam.depth = 4; + } + if (haveAnd) { + readAnd(&hdr, &image[offset], dirEntryP->size - offset, + tuples, dirEntryP->index, andPlane, outpam.maxval); + } + } + pnm_writepam(&outpam, tuples); + pnm_freepamarray(tuples, &outpam); + + reportImage(dirEntryP->index, hdr, haveAlpha); +} + + + +static void +reportPngInfo(const unsigned char * const image, + struct IconDirEntry * const dirEntryP) { + + struct PngIhdr ihdr; + + ihdr.length = u32_be (image, sizeof(pngHeader) +0); + ihdr.signature = u32_xx (image, sizeof(pngHeader) +4); + ihdr.width = u32_be (image, sizeof(pngHeader) +8); + ihdr.height = u32_be (image, sizeof(pngHeader) +12); + ihdr.bit_depth = u8_be (image, sizeof(pngHeader) +16); + ihdr.color_type = u8_be (image, sizeof(pngHeader) +17); + ihdr.compression = u8_be (image, sizeof(pngHeader) +18); + ihdr.filter = u8_be (image, sizeof(pngHeader) +19); + ihdr.interlace = u8_be (image, sizeof(pngHeader) +20); + + if ((ihdr.length != 13) + || ihdr.signature != *(uint32_t*)"IHDR") { + pm_message("image %2u: PNG (uncommonly formatted)", + dirEntryP->index); + } else { + uint32_t depth; + const char * colorType; + + switch (ihdr.color_type) { + case 0: + colorType = "grayscale"; + depth = ihdr.bit_depth; + break; + + case 2: + colorType = "RGB"; + depth = ihdr.bit_depth * 3; + break; + + case 3: + colorType = "RGB/palette"; + depth = 8; + break; + + case 4: + colorType = "grayscale + alpha"; + depth = ihdr.bit_depth * 2; + break; + + case 6: + colorType = "RGB + alpha"; + depth = ihdr.bit_depth * 4; + break; + + default: + colorType = "unknown color system"; + depth = 0; + break; + } + pm_message("image %2u: PNG %3u x %3u x %2u (%s)", + dirEntryP->index, + ihdr.width, ihdr.height, depth, colorType); + + if ((dirEntryP->width != ihdr.width) + || (dirEntryP->height != ihdr.height)) { + pm_message("image %2u:" + " mismatch in header and image dimensions" + " (%u x %u vs %u x %u)", + dirEntryP->index, dirEntryP->width, dirEntryP->height, + ihdr.width, ihdr.height); + } + /* Mismatch between dirEntryP->bits_per_pixel and 'depth' is + normal, because the creator of the winicon file doesn't necessarily + know the true color resolution. + */ + } +} + + + +static void +convertPng(const unsigned char * const image, + FILE * const ofP, + struct IconDirEntry * const dirEntryP) { + + struct bufferDesc imageBuffer; + + reportPngInfo(image, dirEntryP); + + imageBuffer.size = dirEntryP->size; + imageBuffer.buffer = (unsigned char *)image; + + fflush (stdout); + pm_system(pm_feed_from_memory, &imageBuffer, + NULL /* stdout accepter */, NULL, + "pngtopam -alphapam"); +} + + + +static uint32_t +bestImage(struct IconDir * const dirP) { + + uint32_t imageIndex; + uint32_t bestPixelCt; + uint32_t bestColorCt; + uint16_t best; + + bestPixelCt = 0; /* initial value */ + bestColorCt = 0; /* initial value */ + best = 0; /* initial value */ + + for (imageIndex = 0; dirP->count > imageIndex; ++imageIndex) { + struct IconDirEntry * const dirEntryP = &dirP->entries[imageIndex]; + + uint32_t const pixelCt = dirEntryP->width * dirEntryP->height; + + uint32_t colorCt; + + /* 32-bit icons have 24 bit color information only. + + Since NT 5.1 (aka WinXP), it is allowed to place 8-bit + transparency information in the remaining bits (to check, + you have to read all these bits in the image!), so I prefer + 32-bit images over 24-bit images (which violate the + spec. anyway). + */ + if (dirEntryP->bits_per_pixel > 24) + colorCt = 1u << 25; + else + colorCt = 1u << dirEntryP->bits_per_pixel; + + if (dirEntryP->color_count != 0 && colorCt > dirEntryP->color_count) + colorCt = dirEntryP->color_count; + + if ((pixelCt > bestPixelCt) + || ((pixelCt == bestPixelCt) && (colorCt > bestColorCt))) { + /* This is a new best */ + bestPixelCt = pixelCt; + bestColorCt = colorCt; + best = imageIndex; + } + } + return best; +} + + + +static void +convertImage(struct File * const icoP, + struct IconDirEntry * const dirEntryP, + FILE * const ofP, + bool const needHeaderDump, + bool const wantAndMaskPlane) { + + const unsigned char * image; /* malloced */ + + image = readImage(icoP, dirEntryP); + + if (MEMEQ(image, pngHeader, sizeof (pngHeader))) + convertPng(image, ofP, dirEntryP); + else + convertBmp(image, ofP, dirEntryP, needHeaderDump, wantAndMaskPlane); + + free((void *)image); +} + + + +int +main (int argc, const char *argv []) { + + struct File ico; + struct IconDir * dirP; + struct CmdlineInfo cmdline; + + pm_proginit (&argc, argv); + + parseCommandLine(argc, argv, &cmdline); + + verbose = cmdline.verbose; + + ico.name = + streq(cmdline.inputFileName, "-") ? "<stdin>" : cmdline.inputFileName; + ico.pos = 0; + ico.fileP = pm_openr(cmdline.inputFileName); + + dirP = readIconDir(&ico, cmdline.headerdump); + + if (cmdline.allimages) { + unsigned int i; + for (i = 0; i < dirP->count; ++i) + convertImage(&ico, &dirP->entries[i], stdout, + cmdline.headerdump, cmdline.andmasks); + } else if (cmdline.imageSpec) { + unsigned int i; + bool found; + for (i = 0, found = false; i < dirP->count; ++i) { + if (dirP->entries[i].index == cmdline.image) { + found = true; + convertImage(&ico, &dirP->entries[i], stdout, + cmdline.headerdump, cmdline.andmasks); + } + } + if (!found) + pm_error("no image index %u in.input", cmdline.image); + } else { + convertImage(&ico, &dirP->entries[bestImage(dirP)], stdout, + cmdline.headerdump, cmdline.andmasks); + } + + freeIconDir(dirP); + + if (ico.fileP != stdin) + pm_close(ico.fileP); + + return 0; +} + + + |