From 380588e187c12000ac8082cb2a20a905d3c422a5 Mon Sep 17 00:00:00 2001 From: giraffedata Date: Sat, 29 Jun 2013 19:19:47 +0000 Subject: Release 10.63.00 git-svn-id: http://svn.code.sf.net/p/netpbm/code/advanced@1968 9d0c8265-081b-0410-96cb-a4ca84ce46f8 --- GNUmakefile | 10 +- analyzer/pamfile.c | 6 +- analyzer/pamtilt.c | 51 +- converter/other/Makefile | 5 +- converter/other/cameratopam/camera.c | 2 +- converter/other/cameratopam/cameratopam.c | 2 +- converter/other/cameratopam/foveon.c | 4 +- converter/other/cameratopam/foveon.h | 4 +- converter/other/ipdb.c | 1 + converter/other/pamtogif.c | 4 +- converter/other/pamtooctaveimg.c | 4 +- converter/other/pamtopam.c | 2 +- converter/other/pamtopnm.c | 2 +- converter/other/pamtosrf.c | 4 +- converter/other/pamtosvg/pamtosvg.c | 2 +- converter/other/pamtotiff.c | 2 +- converter/other/pamtowinicon.c | 1177 ++++++++++++++++++++++++++ converter/other/pamtoxvmini.c | 4 +- converter/other/pnmtopclxl.c | 2 +- converter/other/pnmtopng.c | 534 ++++++------ converter/other/pnmtops.c | 82 +- converter/other/pstopnm.c | 378 +++++---- converter/other/winicon.h | 82 ++ converter/other/winicontopam.c | 1282 +++++++++++++++++++++++++++++ converter/pbm/pbmtolj.c | 2 +- converter/pbm/pbmtopsg3.c | 2 +- converter/ppm/411toppm.c | 10 +- converter/ppm/ppmtompeg/bframe.c | 2 +- converter/ppm/ppmtompeg/frame.c | 2 +- converter/ppm/ppmtompeg/headers/frames.h | 1 - converter/ppm/ppmtompeg/headers/param.h | 17 +- converter/ppm/ppmtompeg/parallel.c | 2 +- converter/ppm/ppmtompeg/param.c | 65 +- converter/ppm/ppmtompeg/ppmtompeg.c | 5 +- converter/ppm/ppmtompeg/readframe.c | 7 +- converter/ppm/ppmtompeg/specifics.c | 4 +- converter/ppm/ppmtopjxl.c | 229 +++--- converter/ppm/ppmtoyuv.c | 141 ++-- converter/ppm/sldtoppm.c | 137 +-- converter/ppm/winicontoppm.c | 813 +++++++++--------- converter/ppm/yuvtoppm.c | 255 ++++-- doc/HISTORY | 256 +++--- doc/INSTALL | 23 + doc/TESTS | 302 +++++++ doc/patent_summary | 15 + editor/pamcut.c | 2 +- editor/pamscale.c | 4 +- editor/pamthreshold.c | 2 +- editor/pnmcrop.c | 4 +- editor/pnmflip | 25 +- editor/pnmnlfilt.c | 2 +- editor/pnmquant | 25 +- editor/pnmquantall | 24 + editor/pnmremap.c | 4 +- editor/ppmdraw.c | 10 +- editor/ppmfade | 32 +- editor/ppmquant | 26 +- editor/ppmshadow | 35 +- editor/specialty/ppmntsc.c | 311 +++---- generator/pamgauss.c | 2 - generator/pamgradient.c | 8 +- generator/pamseq.c | 2 - generator/pgmnoise.c | 97 ++- generator/ppmcie.c | 114 ++- generator/ppmforge.c | 413 +++++----- generator/ppmrainbow | 26 +- lib/libpam.c | 23 +- lib/libpammap.c | 2 +- lib/libpamn.c | 6 +- lib/libpamwrite.c | 14 +- lib/libpbm1.c | 27 +- lib/libpgm1.c | 20 +- lib/libppm1.c | 22 +- lib/libppmcolor.c | 108 ++- lib/libsystem.c | 16 +- lib/pam.h | 13 +- lib/pmfileio.c | 4 - lib/util/mallocvar.h | 2 +- lib/util/pm_c_util.h | 62 +- other/pamchannel.c | 10 +- other/pamdepth.c | 11 +- other/pamexec.c | 2 +- other/pampick.c | 10 +- other/pamsplit.c | 2 +- other/pamstack.c | 4 +- other/pnmcolormap.c | 4 +- test/Execute-Tests | 39 +- test/Test-Order | 10 + test/all-in-place.ok | 4 + test/all-in-place.test | 426 ++++++++-- test/g3-roundtrip.test | 8 +- test/gem-roundtrip.test | 2 +- test/gif-quant-roundtrip.ok | 1 + test/gif-quant-roundtrip.test | 15 + test/gif-roundtrip.ok | 1 + test/gif-roundtrip.test | 11 +- test/pad-crop-roundtrip.test | 2 +- test/pamchannel.ok | 3 + test/pamchannel.test | 32 + test/pamslice-roundtrip.test | 2 +- test/pbmclean.test | 2 +- test/pbmminkowski.test | 2 +- test/pgmcrater.ok | 1 + test/pgmcrater.test | 29 + test/pgmnoise.ok | 1 + test/pgmnoise.test | 22 + test/pnm-plain-roundtrip.test | 2 +- test/pnmremap1.ok | 2 +- test/pnmtile.test | 3 +- test/pnmtopnm-plain.test | 2 +- test/ppmcie.out-64 | 1 - test/ppmdfont.test | 10 + test/ppmdim.test | 2 +- test/ppmforge.ok | 1 + test/ppmforge.test | 20 + test/ppmhist.test | 2 +- test/ppmmake.test | 2 - test/ppmmix.test | 1 - test/ppmpat.test | 39 +- test/ppmrough.ok | 1 + test/ppmrough.test | 18 + test/ps-roundtrip.ok | 1 + test/ps-roundtrip.test | 33 +- test/rgb3-roundtrip.ok | 1 + test/rgb3-roundtrip.test | 9 +- test/winicon-roundtrip.ok | 2 + test/winicon-roundtrip.test | 13 + test/yuv-roundtrip.test | 3 +- version.mk | 4 +- 129 files changed, 6162 insertions(+), 2089 deletions(-) create mode 100644 converter/other/pamtowinicon.c create mode 100644 converter/other/winicon.h create mode 100644 converter/other/winicontopam.c create mode 100644 doc/TESTS create mode 100644 test/gif-quant-roundtrip.ok create mode 100644 test/gif-quant-roundtrip.test create mode 100644 test/pamchannel.ok create mode 100644 test/pamchannel.test create mode 100644 test/pgmcrater.ok create mode 100644 test/pgmcrater.test create mode 100644 test/pgmnoise.ok create mode 100644 test/pgmnoise.test delete mode 100644 test/ppmcie.out-64 create mode 100644 test/ppmforge.ok create mode 100644 test/ppmforge.test create mode 100644 test/ppmrough.ok create mode 100644 test/ppmrough.test create mode 100644 test/winicon-roundtrip.ok create mode 100644 test/winicon-roundtrip.test diff --git a/GNUmakefile b/GNUmakefile index 489d6888..c6077285 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -72,6 +72,10 @@ NOMERGEBINARIES = netpbm OBJECTS = netpbm.o +PBM_TESTPREFIX ?= $(PKGDIR)/bin +PBM_LIBRARY_PATH ?= $(PKGDIR)/lib +RGBDEF ?= $(SRCDIR)/lib/rgb.txt + default: $(DEFAULT_TARGET) echo "EXISTENCE OF THIS FILE MEANS NETPBM HAS BEEN BUILT." \ >build_complete @@ -412,9 +416,9 @@ check: # This works on typical Linux systems if [ ! -d $(RESULTDIR) ]; then mkdir -pv $(RESULTDIR); fi cd $(RESULTDIR); \ - PBM_TESTPREFIX=$(PKGDIR)/bin \ - LD_LIBRARY_PATH=$(PKGDIR)/lib \ - RGBDEF=$(SRCDIR)/lib/rgb.txt \ + PBM_TESTPREFIX=$(PBM_TESTPREFIX) \ + LD_LIBRARY_PATH=$(PBM_LIBRARY_PATH):${LD_LIBRARY_PATH} \ + RGBDEF=$(RGBDEF) \ $(SRCDIR)/test/Execute-Tests 2>&1 clean: localclean diff --git a/analyzer/pamfile.c b/analyzer/pamfile.c index c2507f66..9c5b2c33 100644 --- a/analyzer/pamfile.c +++ b/analyzer/pamfile.c @@ -171,7 +171,11 @@ doOneImage(const char * const name, pnm_freepamrow(tuplerow); - pnm_nextimage(fileP, eofP); + { + int eof; + pnm_nextimage(fileP, &eof); + *eofP = eof; + } } } diff --git a/analyzer/pamtilt.c b/analyzer/pamtilt.c index 5d1d1986..8fa34791 100644 --- a/analyzer/pamtilt.c +++ b/analyzer/pamtilt.c @@ -47,7 +47,7 @@ abandon(void) { static void -parseCommandLine(int argc, char *argv[], +parseCommandLine(int argc, const char ** const argv, struct cmdlineInfo * const cmdlineP) { static optEntry option_def[50]; @@ -76,7 +76,7 @@ parseCommandLine(int argc, char *argv[], opt.opt_table = option_def; opt.short_allowed = FALSE; /* no short options used */ opt.allowNegNum = FALSE; /* don't allow negative values */ - pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0); + pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0); if (cmdlineP->hstep < 1) pm_error("-hstep must be at least 1 column."); @@ -143,9 +143,6 @@ load(const struct pam * const pamP, tuplerow = pnm_allocpamrow(pamP); MALLOCARRAY(pixels, pamP->height); - if (pixels == NULL) - pm_error("Unable to allocate array of %u pixel rows", - pamP->height); if (pixels == NULL) pm_error("Unable to allocate array of %u rows", pamP->height); @@ -214,9 +211,32 @@ replacePixelValuesWithScaledDiffs( +static unsigned long +totalBrightness(sample ** const pixelWindow, + unsigned int const hsampleCt, + float const dy) { +/*---------------------------------------------------------------------------- + Total brightness of samples in the line that goes from the top left corner + of 'pixelWindow' down to the right at 'dy' rows per column. +-----------------------------------------------------------------------------*/ + unsigned long total; + unsigned int i; + float rowOffset; + + for (i = 0, rowOffset = 0.5, total = 0; + i < hsampleCt; + ++i, rowOffset += dy) { + + total += pixelWindow[(unsigned)rowOffset][i]; + } + return total; +} + + + static void scoreAngleRegion(sample ** const pixels, - unsigned int const hsamples, + unsigned int const hsampleCt, unsigned int const startRow, unsigned int const endRow, unsigned int const vstep, @@ -234,7 +254,7 @@ scoreAngleRegion(sample ** const pixels, Instead of a tilt angle, we have 'dy', the slope (downward) of the lines in our assumed tilt. -----------------------------------------------------------------------------*/ - double const tscale = 1.0 / hsamples; + double const tscale = 1.0 / hsampleCt; unsigned int row; double sum; @@ -245,17 +265,10 @@ scoreAngleRegion(sample ** const pixels, /* Number of lines that went into 'total' */ for (row = startRow, sum = 0.0, n = 0; row < endRow; row += vstep) { - float o; - long t; /* total brightness of the samples in the line */ - double dt; /* mean brightness of the samples in the line */ + double const dt = + tscale * totalBrightness(&pixels[row], hsampleCt, dy); + /* mean brightness of the samples in the line */ - unsigned int i; - - for (i = 0, t = 0, o = 0.5; - i < hsamples; - t += pixels[(int)(row + o)][i], ++i, o += dy) { - } - dt = tscale * t; sum += dt * dt; n += 1; } @@ -478,7 +491,7 @@ getAngle(const struct pam * const pamP, int -main(int argc, char *argv[]) { +main(int argc, const char ** argv) { struct cmdlineInfo cmdline; struct pam pam; @@ -488,7 +501,7 @@ main(int argc, char *argv[]) { unsigned int vstep; /* vertical step size */ float angle; - pgm_init(&argc, argv); /* initialize netpbm system */ + pm_proginit(&argc, argv); parseCommandLine(argc, argv, &cmdline); 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 */ #include #include #include 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 */ +#include +#include +#include + +#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 #include #include #include @@ -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 +#include +#include + +#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, "-") ? "" : 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; +} + + + diff --git a/converter/pbm/pbmtolj.c b/converter/pbm/pbmtolj.c index 89dcb76c..0cceb4fe 100644 --- a/converter/pbm/pbmtolj.c +++ b/converter/pbm/pbmtolj.c @@ -545,7 +545,7 @@ main(int argc, char * argv[]) { struct cmdlineInfo cmdline; FILE * ifP; - bool eof; + int eof; pbm_init(&argc, argv); diff --git a/converter/pbm/pbmtopsg3.c b/converter/pbm/pbmtopsg3.c index 2878686b..8163b70a 100644 --- a/converter/pbm/pbmtopsg3.c +++ b/converter/pbm/pbmtopsg3.c @@ -323,7 +323,7 @@ doPages(FILE * const ifP, unsigned int * const pagesP, double const dpi) { - bool eof; + int eof; unsigned int pagesDone; eof = FALSE; diff --git a/converter/ppm/411toppm.c b/converter/ppm/411toppm.c index b5e3c03b..eb2372a5 100644 --- a/converter/ppm/411toppm.c +++ b/converter/ppm/411toppm.c @@ -192,6 +192,7 @@ YUVtoPPM(FILE * const ifP, int main(int argc, const char **argv) { + pixval const maxval = 255; struct CmdlineInfo cmdline; FILE * ifP; pixel * pixrow; @@ -208,13 +209,16 @@ main(int argc, const char **argv) { ifP = pm_openr(cmdline.inputFileName); - ppm_writeppminit(stdout, cmdline.width, cmdline.height, 255, 0); + ppm_writeppminit(stdout, cmdline.width, cmdline.height, maxval, 0); - for (row = 0; row < cmdline.height; row++) { + for (row = 0; row < cmdline.height; ++row) { YUVtoPPM(ifP, cmdline.width, cmdline.height, pixrow); - ppm_writeppmrow(stdout, pixrow, cmdline.width, 255, 0); + ppm_writeppmrow(stdout, pixrow, cmdline.width, maxval, 0); } + if (fgetc(ifP) != EOF) + pm_message("Extraneous data at end of image."); + pm_close(ifP); ppm_freerow(pixrow); diff --git a/converter/ppm/ppmtompeg/bframe.c b/converter/ppm/ppmtompeg/bframe.c index 1dbc1846..f5009d6c 100644 --- a/converter/ppm/ppmtompeg/bframe.c +++ b/converter/ppm/ppmtompeg/bframe.c @@ -523,7 +523,7 @@ makeNonSkipBlock(int const y, MpegFrame * const curr, MpegFrame * const prev, MpegFrame * const next, - boolean const specificsOn, + bool const specificsOn, int const mbAddress, int const QScale, const LumBlock * const currentBlockP, diff --git a/converter/ppm/ppmtompeg/frame.c b/converter/ppm/ppmtompeg/frame.c index 09f46f66..75b209f8 100644 --- a/converter/ppm/ppmtompeg/frame.c +++ b/converter/ppm/ppmtompeg/frame.c @@ -753,7 +753,7 @@ Frame_AllocHalf(MpegFrame * const frameP) { *===========================================================================*/ void Frame_AllocDecoded(MpegFrame * const frameP, - boolean const makeReference) { + bool const makeReference) { if (frameP->decoded_y != NULL) { /* already allocated */ diff --git a/converter/ppm/ppmtompeg/headers/frames.h b/converter/ppm/ppmtompeg/headers/frames.h index f2bcf6ae..966d9214 100644 --- a/converter/ppm/ppmtompeg/headers/frames.h +++ b/converter/ppm/ppmtompeg/headers/frames.h @@ -255,7 +255,6 @@ extern int gopSize; extern int slicesPerFrame; extern int blocksPerSlice; extern int referenceFrame; -extern boolean specificsOn; extern int quietTime; /* shut up for at least quietTime seconds; * negative means shut up forever */ diff --git a/converter/ppm/ppmtompeg/headers/param.h b/converter/ppm/ppmtompeg/headers/param.h index c7f57b44..0c6d8e5c 100644 --- a/converter/ppm/ppmtompeg/headers/param.h +++ b/converter/ppm/ppmtompeg/headers/param.h @@ -51,7 +51,7 @@ extern char machineName[MAX_MACHINES][256]; extern char userName[MAX_MACHINES][256]; extern char executable[MAX_MACHINES][1024]; extern char remoteParamFile[MAX_MACHINES][1024]; -extern boolean remote[MAX_MACHINES]; +extern bool remote[MAX_MACHINES]; extern char currentPath[MAXPATHLEN]; extern char currentFramePath[MAXPATHLEN]; extern char currentGOPPath[MAXPATHLEN]; @@ -62,12 +62,21 @@ extern int realWidth, realHeight; extern char ioConversion[1024]; extern char slaveConversion[1024]; extern FILE * bitRateFile; -extern boolean showBitRatePerFrame; -extern boolean computeMVHist; extern const double VidRateNum[9]; -extern boolean keepTempFiles; +extern bool keepTempFiles; +extern int outputWidth, outputHeight; +extern bool specificsOn; +extern char specificsFile[256]; +extern char specificsDefines[1024]; +extern bool GammaCorrection; +extern float GammaValue; +extern char userDataFileName[256]; +/* Defined in ppmtompeg.c; computed from command line */ +extern bool showBitRatePerFrame; +extern bool computeMVHist; + /* * Copyright (c) 1995 The Regents of the University of California. * All rights reserved. diff --git a/converter/ppm/ppmtompeg/parallel.c b/converter/ppm/ppmtompeg/parallel.c index 50381271..2835c67c 100644 --- a/converter/ppm/ppmtompeg/parallel.c +++ b/converter/ppm/ppmtompeg/parallel.c @@ -968,7 +968,7 @@ IoServer(struct inputSource * const inputSourceP, -----------------------------------------------------------------------------*/ int ioPortNum; int serverSocket; - boolean done; + bool done; unsigned char *bigBuffer; /* A work buffer that we keep around permanently. We increase its size as needed, but never shrink it. diff --git a/converter/ppm/ppmtompeg/param.c b/converter/ppm/ppmtompeg/param.c index 15f13473..45605981 100644 --- a/converter/ppm/ppmtompeg/param.c +++ b/converter/ppm/ppmtompeg/param.c @@ -73,14 +73,14 @@ #define LAST_OPTION 15 /* put any non-required options after LAST_OPTION */ -#define OPTION_RESIZE 16 +#define OPTION_RESIZE 16 #define OPTION_IO_CONVERT 17 #define OPTION_SLAVE_CONVERT 18 -#define OPTION_IQTABLE 19 -#define OPTION_NIQTABLE 20 +#define OPTION_IQTABLE 19 +#define OPTION_NIQTABLE 20 #define OPTION_FRAME_RATE 21 #define OPTION_ASPECT_RATIO 22 -#define OPTION_YUV_SIZE 23 +#define OPTION_YUV_SIZE 23 #define OPTION_SPECIFICS 24 #define OPTION_DEFS_SPECIFICS 25 #define OPTION_BUFFER_SIZE 26 @@ -98,48 +98,49 @@ * GLOBAL VARIABLES * *==================*/ -extern char currentPath[MAXPATHLEN]; -char outputFileName[256]; -int outputWidth, outputHeight; +char outputFileName[256]; +int outputWidth, outputHeight; char inputConversion[1024]; char ioConversion[1024]; char slaveConversion[1024]; char yuvConversion[256]; char specificsFile[256],specificsDefines[1024]=""; -boolean GammaCorrection=FALSE; -float GammaValue; +bool GammaCorrection=FALSE; +float GammaValue; char userDataFileName[256]={0}; -boolean specificsOn = FALSE; +bool specificsOn = FALSE; char currentGOPPath[MAXPATHLEN]; char currentFramePath[MAXPATHLEN]; -boolean keepTempFiles; +bool keepTempFiles; +int numMachines; +char machineName[MAX_MACHINES][256]; +char userName[MAX_MACHINES][256]; +char executable[MAX_MACHINES][1024]; +char remoteParamFile[MAX_MACHINES][1024]; +bool remote[MAX_MACHINES]; +int mult_seq_headers = 0; /* 0 for none, N for header/N GOPs */ + + +extern char currentPath[MAXPATHLEN]; static const char * const optionText[LAST_OPTION+1] = { "GOP_SIZE", "PATTERN", "PIXEL", "PQSCALE", "OUTPUT", "RANGE", "PSEARCH_ALG", "IQSCALE", "INPUT_DIR", "INPUT_CONVERT", "INPUT", "BQSCALE", "BASE_FILE_FORMAT", "SLICES_PER_FRAME", "BSEARCH_ALG", "REFERENCE_FRAME"}; -static boolean optionSeen[NUM_OPTIONS+1]; +static bool optionSeen[NUM_OPTIONS+1]; /* optionSeen[x] means we have seen option x in the parameter file we've been reading. */ -int numMachines; -char machineName[MAX_MACHINES][256]; -char userName[MAX_MACHINES][256]; -char executable[MAX_MACHINES][1024]; -char remoteParamFile[MAX_MACHINES][1024]; -boolean remote[MAX_MACHINES]; -int mult_seq_headers = 0; /* 0 for none, N for header/N GOPs */ - /*===========================================================================* * * SkipSpacesTabs * - * skip all spaces and tabs + * skip all spaces and tabs * - * RETURNS: point to next character not a space or tab + * RETURNS: point to next character not a space or tab * * SIDE EFFECTS: none * @@ -172,13 +173,13 @@ static int GetAspectRatio(const char * const p) { float ratio; - int ttRatio; + int ttRatio; int retval; sscanf(p, "%f", &ratio); ttRatio = (int)(0.5+ratio*10000.0); - if ( ttRatio == 10000 ) retval = 1; + if ( ttRatio == 10000 ) retval = 1; else if ( ttRatio == 6735 ) retval = 2; else if ( ttRatio == 7031 ) retval = 3; else if ( ttRatio == 7615 ) retval = 4; @@ -205,9 +206,9 @@ GetAspectRatio(const char * const p) * * ReadMachineNames * - * read a list of machine names for parallel execution + * read a list of machine names for parallel execution * - * RETURNS: nothing + * RETURNS: nothing * * SIDE EFFECTS: machine info updated * @@ -219,7 +220,7 @@ ReadMachineNames(FILE * const fpointer) const char *charPtr; while ( (fgets(input, 256, fpointer) != NULL) && - (strncmp(input, "END_PARALLEL", 12) != 0) ) { + (strncmp(input, "END_PARALLEL", 12) != 0) ) { if ( input[0] == '#' || input[0] == '\n') { continue; } @@ -229,13 +230,13 @@ ReadMachineNames(FILE * const fpointer) remote[numMachines] = TRUE; sscanf(charPtr, "%s %s %s %s", machineName[numMachines], - userName[numMachines], executable[numMachines], - remoteParamFile[numMachines]); + userName[numMachines], executable[numMachines], + remoteParamFile[numMachines]); } else { remote[numMachines] = FALSE; sscanf(input, "%s %s %s", machineName[numMachines], - userName[numMachines], executable[numMachines]); + userName[numMachines], executable[numMachines]); } numMachines++; @@ -259,13 +260,13 @@ static int GetFrameRate(const char * const p) { float rate; - int thouRate; + int thouRate; int retval; sscanf(p, "%f", &rate); thouRate = (int)(0.5+1000.0*rate); - if ( thouRate == 23976 ) retval = 1; + if ( thouRate == 23976 ) retval = 1; else if ( thouRate == 24000 ) retval = 2; else if ( thouRate == 25000 ) retval = 3; else if ( thouRate == 29970 ) retval = 4; diff --git a/converter/ppm/ppmtompeg/ppmtompeg.c b/converter/ppm/ppmtompeg/ppmtompeg.c index 6e7e9833..b8ef37f1 100644 --- a/converter/ppm/ppmtompeg/ppmtompeg.c +++ b/converter/ppm/ppmtompeg/ppmtompeg.c @@ -65,7 +65,8 @@ int main _ANSI_ARGS_((int argc, char **argv)); * GLOBAL VARIABLES * *==================*/ -boolean showBitRatePerFrame; +bool showBitRatePerFrame; +bool computeMVHist = FALSE; boolean frameSummary; extern time_t IOtime; @@ -79,9 +80,7 @@ boolean noFrameSummaryOption = FALSE; boolean debugSockets = FALSE; boolean debugMachines = FALSE; boolean bitRateInfoOption = FALSE; -boolean computeMVHist = FALSE; int baseFormat; -extern boolean specificsOn; extern FrameSpecList *fsl; boolean pureDCT=FALSE; char encoder_name[1024]; diff --git a/converter/ppm/ppmtompeg/readframe.c b/converter/ppm/ppmtompeg/readframe.c index 112ebdf0..cac6bdad 100644 --- a/converter/ppm/ppmtompeg/readframe.c +++ b/converter/ppm/ppmtompeg/readframe.c @@ -67,9 +67,6 @@ struct YuvLine { * Global VARIABLES * *==================*/ -extern boolean GammaCorrection; -extern float GammaValue; -extern int outputWidth,outputHeight; boolean resizeFrame; const char *CurrFile; @@ -828,7 +825,7 @@ MpegFrame *mf; int w,h; { static int GammaVal[256]; - static boolean init_done=FALSE; + static bool init_done=FALSE; int i,j; if (!init_done) { @@ -882,7 +879,7 @@ DoKillDim(mf, w, h) MpegFrame *mf; int w,h; { - static boolean init_done=FALSE; + static bool init_done=FALSE; static unsigned char mapper[256]; register int i,j; double slope, intercept; diff --git a/converter/ppm/ppmtompeg/specifics.c b/converter/ppm/ppmtompeg/specifics.c index d2093e7f..ffbce80a 100644 --- a/converter/ppm/ppmtompeg/specifics.c +++ b/converter/ppm/ppmtompeg/specifics.c @@ -46,14 +46,12 @@ #include #include #include "prototypes.h" +#include "param.h" /*==================* * GLOBAL VARIABLES * *==================*/ -extern boolean specificsOn; -extern char specificsFile[]; -extern char specificsDefines[]; FrameSpecList *fsl; /*=====================* diff --git a/converter/ppm/ppmtopjxl.c b/converter/ppm/ppmtopjxl.c index 91cd1a45..ddf49638 100644 --- a/converter/ppm/ppmtopjxl.c +++ b/converter/ppm/ppmtopjxl.c @@ -12,6 +12,7 @@ * */ +#include #include #include #include @@ -33,7 +34,7 @@ const char * const usage="[-nopack] [-gamma ] [-presentation] [-dark]\n\ #define PCL_MAXHEIGHT 32767 #define PCL_MAXVAL 255 -static int nopack = 0; +static bool nopack = false; static int dark = 0; static int diffuse = 0; static int dither = 0; @@ -73,16 +74,28 @@ static const struct options { {"-nopack", BOOL, &nopack }, }; -#define putword(w) (putchar(((w)>>8) & 255), putchar((w) & 255)) -static int -bitsperpixel(int v) { - int bpp = 0; - while (v > 0) { /* calculate # bits for value */ - ++bpp; - v>>=1; - } - return (bpp); + +static void +putword(unsigned short const w) { + putchar((w >> 8) & 0xff); + putchar((w >> 0) & 0xff); +} + + + +static unsigned int +bitsperpixel(unsigned int v) { + + unsigned int bpp; + + /* calculate # bits for value */ + + for (bpp = 0; v > 0; ) { + ++bpp; + v >>= 1; + } + return bpp; } @@ -94,101 +107,117 @@ static char *outrow = NULL; static signed char *runcnt = NULL; static void -putbits(b, n) { - /* put #n bits in b out, packing into bytes; n=0 flushes bits */ - /* n should never be > 8 */ - - static int out = 0; - static int cnt = 0; - static int num = 0; - static int pack = 0; - if (n) { - int xo = 0; - int xc = 0; - if (cnt+n > 8) { /* overflowing current byte? */ - xc = cnt + n - 8; - xo = (b & ~(-1 << xc)) << (8-xc); - n -= xc; - b >>= xc; - } - cnt += n; - out |= (b & ~(-1 << n)) << (8-cnt); - if (cnt >= 8) { - inrow[num++] = out; - out = xo; - cnt = xc; - } - } else { /* flush row */ - int i; - if (cnt) { - inrow[num++] = out; - out = cnt = 0; - } - for (; num > 0 && inrow[num-1] == 0; num--); /* remove trailing zeros */ - printf("\033*b"); - if (num && !nopack) { /* TIFF 4.0 packbits encoding */ - int start = 0; - int next; - runcnt[start] = 0; - for (i = 1; i < num; i++) { - if (inrow[i] == inrow[i-1]) { - if (runcnt[start] <= 0 && runcnt[start] > -127) - runcnt[start]--; - else - runcnt[start = i] = 0; - } else { - if (runcnt[start] >= 0 && runcnt[start] < 127) - runcnt[start]++; - else - runcnt[start = i] = 0; +putbits(int const bArg, + int const nArg) { +/*---------------------------------------------------------------------------- + Put 'n' bits in 'b' out, packing into bytes; n=0 flushes bits. + + n should never be > 8 +-----------------------------------------------------------------------------*/ + static int out = 0; + static int cnt = 0; + static int num = 0; + static bool pack = false; + + int b; + int n; + + b = bArg; + n = nArg; + + if (n) { + int xo = 0; + int xc = 0; + + assert(n <= 8); + + if (cnt + n > 8) { /* overflowing current byte? */ + xc = cnt + n - 8; + xo = (b & ~(-1 << xc)) << (8-xc); + n -= xc; + b >>= xc; } - } - start = 0; - for (i = 0; i < num; i = next) { - int count = runcnt[i]; - int from = i; - if (count >= 0) { /* merge two-byte runs */ - for (;;) { - next = i+1+runcnt[i]; - if(next >= num || runcnt[next] < 0 || - count+runcnt[next]+1 > 127) - break; - count += runcnt[next]+1; - i = next; - } + cnt += n; + out |= (b & ~(-1 << n)) << (8-cnt); + if (cnt >= 8) { + inrow[num++] = out; + out = xo; + cnt = xc; } - next = i + 1 + ((runcnt[i] < 0) ? -runcnt[i] : runcnt[i]); - if (next < num && count > 0 && - runcnt[next] < 0 && runcnt[next] > -127) { - count--; - next--; - runcnt[next] = runcnt[next+1]-1; + } else { /* flush row */ + if (cnt) { + inrow[num++] = out; + out = cnt = 0; } - outrow[start++] = count; - if (count >= 0) { - while (count-- >= 0) - outrow[start++] = inrow[from++]; - } else - outrow[start++] = inrow[from]; - } - if (start < num) { - num = start; - if (!pack) { - printf("2m"); - pack = 1; + for (; num > 0 && inrow[num-1] == 0; --num); + /* remove trailing zeros */ + printf("\033*b"); + if (num && !nopack) { /* TIFF 4.0 packbits encoding */ + unsigned int i; + int start = 0; + int next; + runcnt[start] = 0; + for (i = 1; i < num; ++i) { + if (inrow[i] == inrow[i-1]) { + if (runcnt[start] <= 0 && runcnt[start] > -127) + runcnt[start]--; + else + runcnt[start = i] = 0; + } else { + if (runcnt[start] >= 0 && runcnt[start] < 127) + runcnt[start]++; + else + runcnt[start = i] = 0; + } + } + for (i = 0, start = 0; i < num; i = next) { + int count = runcnt[i]; + int from = i; + if (count >= 0) { /* merge two-byte runs */ + for (;;) { + next = i+1+runcnt[i]; + if(next >= num || runcnt[next] < 0 || + count+runcnt[next]+1 > 127) + break; + count += runcnt[next]+1; + i = next; + } + } + next = i + 1 + ((runcnt[i] < 0) ? -runcnt[i] : runcnt[i]); + if (next < num && count > 0 && + runcnt[next] < 0 && runcnt[next] > -127) { + --count; + --next; + runcnt[next] = runcnt[next+1]-1; + } + outrow[start++] = count; + if (count >= 0) { + while (count-- >= 0) + outrow[start++] = inrow[from++]; + } else + outrow[start++] = inrow[from]; + } + if (start < num) { + num = start; + if (!pack) { + printf("2m"); + pack = true; + } + } else { + if (pack) { + printf("0m"); + pack = false; + } + } } - } else { - if (pack) { - printf("0m"); - pack = 0; + printf("%dW", num); + { + unsigned int i; + for (i = 0; i < num; ++i) + putchar(pack ? outrow[i] : inrow[i]); } - } - } - printf("%dW", num); - for (i = 0; i < num; i++) - putchar(pack ? outrow[i] : inrow[i]); - num = 0; /* new row */ - } + num = 0; /* new row */ + } } diff --git a/converter/ppm/ppmtoyuv.c b/converter/ppm/ppmtoyuv.c index 7d843cc0..75f79c1e 100644 --- a/converter/ppm/ppmtoyuv.c +++ b/converter/ppm/ppmtoyuv.c @@ -19,79 +19,102 @@ #include "ppm.h" -int -main(argc, argv) -char **argv; -{ - FILE *ifp; - pixel *pixelrow; - register pixel *pP; - int rows, cols, format, row; - register int col; - pixval maxval; - unsigned long y1, y2=0, u=0, v=0, u0=0, u1, u2, v0=0, v1, v2; - unsigned char *yuvbuf; - - - ppm_init(&argc, argv); - if (argc > 2) pm_usage("[ppmfile]"); - if (argc == 2) ifp = pm_openr(argv[1]); - else ifp = stdin; - - ppm_readppminit(ifp, &cols, &rows, &maxval, &format); +static void +convertRow(const pixel * const pixelrow, + unsigned int const cols, + unsigned char * const yuvBuf, + unsigned long * const uP, + unsigned long * const vP, + unsigned long * const u0P, + unsigned long * const v0P, + unsigned long * const y2CarryP) { + + unsigned int col; + unsigned char * yuvptr; + + for (col = 0, yuvptr = &yuvBuf[0]; col < cols; col += 2) { + unsigned long y1, y2, u1, u2, v1, v2; + + { + /* first pixel gives Y and 0.5 of chroma */ + pixval const r = PPM_GETR(pixelrow[col]); + pixval const g = PPM_GETG(pixelrow[col]); + pixval const b = PPM_GETB(pixelrow[col]); + + y1 = 16829 * r + 33039 * g + 6416 * b + (*y2CarryP & 0xffff); + u1 = -4853 * r - 9530 * g + 14383 * b; + v1 = 14386 * r - 12046 * g - 2340 * b; + } + { + /* second pixel gives Y and 0.25 of chroma */ + pixval const r = PPM_GETR(pixelrow[col + 1]); + pixval const g = PPM_GETG(pixelrow[col + 1]); + pixval const b = PPM_GETB(pixelrow[col + 1]); + + y2 = 16829 * r + 33039 * g + 6416 * b + (y1 & 0xffff); + u2 = -2426 * r - 4765 * g + 7191 * b; + v2 = 7193 * r - 6023 * g - 1170 * b; + } + /* filter the chroma */ + *uP = *u0P + u1 + u2 + (*uP & 0xffff); + *vP = *v0P + v1 + v2 + (*vP & 0xffff); + + *u0P = u2; + *v0P = v2; + + *yuvptr++ = (*uP >> 16) + 128; + *yuvptr++ = (y1 >> 16) + 16; + *yuvptr++ = (*vP >> 16) + 128; + *yuvptr++ = (y2 >> 16) + 16; + + *y2CarryP = y2; + } +} - if (cols % 2 != 0) - pm_error("Image must have even number of columns.\n" - "This image is %d columns wide. Try Pnmcut.", cols); - pixelrow = ((pixel*) pm_allocrow( cols, sizeof(pixel) )); - yuvbuf = (unsigned char *) pm_allocrow( cols, 2 ); - for (row = 0; row < rows; ++row) { - unsigned char *yuvptr; +int +main(int argc, const char **argv) { - ppm_readppmrow(ifp, pixelrow, cols, maxval, format); + FILE * ifP; + pixel * pixelrow; + int rows, cols, format; + pixval maxval; + unsigned int row; + unsigned char * yuvBuf; + unsigned long u, v, u0, v0, y2Carry; - for (col = 0, pP = pixelrow, yuvptr=yuvbuf; col < cols; col += 2, ++pP) { - pixval r, g, b; + pm_proginit(&argc, argv); - /* first pixel gives Y and 0.5 of chroma */ - r = PPM_GETR(*pP); - g = PPM_GETG(*pP); - b = PPM_GETB(*pP); + if (argc-1 > 1) + pm_error("Too many arguments: %u. The only possible argument " + "is the name of the input file", argc-1); - y1 = 16829 * r + 33039 * g + 6416 * b + (0xffff & y2); - u1 = -4853 * r - 9530 * g + 14383 * b; - v1 = 14386 * r - 12046 * g - 2340 * b; + if (argc-1 == 1) + ifP = pm_openr(argv[1]); + else + ifP = stdin; - pP++; - /* second pixel just yields a Y and 0.25 U, 0.25 V */ - r = PPM_GETR(*pP); - g = PPM_GETG(*pP); - b = PPM_GETB(*pP); + ppm_readppminit(ifP, &cols, &rows, &maxval, &format); - y2 = 16829 * r + 33039 * g + 6416 * b + (0xffff & y1); - u2 = -2426 * r - 4765 * g + 7191 * b; - v2 = 7193 * r - 6023 * g - 1170 * b; + if (cols % 2 != 0) + pm_error("Image must have even number of columns.\n" + "This image is %u columns wide. Try Pamcut.", cols); - /* filter the chroma */ - u = u0 + u1 + u2 + (0xffff & u); - v = v0 + v1 + v2 + (0xffff & v); + pixelrow = ppm_allocrow(cols); + yuvBuf = (unsigned char *) pm_allocrow(cols, 2); - u0 = u2; - v0 = v2; + for (row = 0, u = v = u0 = v0 = y2Carry = 0; row < rows; ++row) { + ppm_readppmrow(ifP, pixelrow, cols, maxval, format); - *yuvptr++ = (u >> 16) + 128; - *yuvptr++ = (y1 >> 16) + 16; - *yuvptr++ = (v >> 16) + 128; - *yuvptr++ = (y2 >> 16) + 16; - } - fwrite(yuvbuf, cols*2, 1, stdout); - } + convertRow(pixelrow, cols, yuvBuf, &u, &v, &u0, &v0, &y2Carry); + + fwrite(yuvBuf, cols*2, 1, stdout); + } - pm_close(ifp); + pm_close(ifP); - exit(0); + return 0; } diff --git a/converter/ppm/sldtoppm.c b/converter/ppm/sldtoppm.c index ad16a649..6ba4cb40 100644 --- a/converter/ppm/sldtoppm.c +++ b/converter/ppm/sldtoppm.c @@ -26,23 +26,20 @@ #include #include +#include "pm_c_util.h" #include "ppm.h" #include "ppmdraw.h" #include "nstring.h" -#ifdef DEBUG #include -#else -#define assert(x) -#endif + +#include "autocad.h" /* AutoCAD standard color assignments */ + /* Define a variable type accepting numbers -127 <= n <= 127. But note that we still expect it to act UNSIGNED. */ #define smallint unsigned char /* Small integers */ -#define TRUE 1 -#define FALSE 0 - #define EOS '\0' /* Screen point */ @@ -70,18 +67,17 @@ typedef void (slvecfn)(struct svector * vec, int color); typedef void (slfloodfn)(struct spolygon * poly, int color); +static unsigned long const pixmaxval = 255; /* Largest pixel value */ + static int ixdots, iydots; /* Screen size in dots */ -static FILE *slfile; /* Slide file descriptor */ -static int blither = FALSE; /* Dump slide file information ? */ -static int info = FALSE; /* Print header information */ +static FILE * slfile; /* Slide file descriptor */ +static bool blither; /* Dump slide file information ? */ +static bool info; /* Print header information */ static pixel **pixels; /* Pixel map */ -static int pixcols, pixrows; /* Pixel map size */ -#define pixmaxval 255 /* Largest pixel value */ +static int pixcols, pixrows; /* Pixel map size */ static double uscale = -1; /* Uniform scale factor */ static int sxsize = -1, sysize = -1; /* Scale to X, Y size ? */ -#include "autocad.h" /* AutoCAD standard color assignments */ - /* Local variables */ struct slhead { @@ -94,11 +90,11 @@ struct slhead { char spad; /* Pad to even byte length */ }; -static int adjust = FALSE; /* Adjust to correct aspect ratio ? */ -static struct slhead slfrof; /* Slide file header */ -static long xfac, yfac; /* Aspect ratio scale factors */ +static bool adjust; /* Adjust to correct aspect ratio ? */ +static struct slhead slfrof; /* Slide file header */ +static long xfac, yfac; /* Aspect ratio scale factors */ -static int sdrawkcab; +static bool sdrawkcab; /* Slide drawing kinematic conversion of ass-backwards data flag */ @@ -108,7 +104,7 @@ static int sdrawkcab; */ static int -extend(smallint ch) { +extend(unsigned char const ch) { return ((int) ((ch & 0x80) ? (ch | ~0xFF) : ch)); } @@ -136,9 +132,9 @@ sli(void) { static int slib(void) { - smallint ch = 0; + unsigned char ch; - if (fread(&ch, sizeof ch, 1, slfile) != 1) { + if (fread(&ch, sizeof(ch), 1, slfile) != 1) { pm_error("error reading slide file"); } return extend(ch); @@ -171,22 +167,28 @@ slidefind(const char * const sname, char uname[32]; unsigned char libent[36]; long pos; + bool found; + bool eof; if (dironly) pm_message("Slides in library:"); else { - int i; + unsigned int i; const char * ip; ip = sname; /* initial value */ - for (i = 0; i < 31; i++) { - char ch = *ip++; + for (i = 0; i < 31; ++i) { + char const ch = *ip++; if (ch == EOS) break; - if (ucasen && ISLOWER(ch)) - ch = TOUPPER(ch); - uname[i] = ch; + + { + char const upperCh = + ucasen && islower(ch) ? toupper(ch) : ch; + + uname[i] = upperCh; + } } uname[i] = EOS; } @@ -199,31 +201,38 @@ slidefind(const char * const sname, } pos = 32; - /* Search for a slide with the requested name. */ + /* Search for a slide with the requested name or list the directory */ - while (TRUE) { - if ((fread(libent, 36, 1, slfile) != 1) || - (strlen((char *)libent) == 0)) { - if (dironly) { - return; - } - pm_error("slide %s not in library.", sname); - } + for (found = false, eof = false; !found && !eof; ) { + size_t readCt; + readCt = fread(libent, 36, 1, slfile); + if (readCt != 1) + eof = true; + else if (strlen((char *)libent) == 0) + eof = true; + } + if (!eof) { pos += 36; if (dironly) { pm_message(" %s", libent); } else if (streq((char *)libent, uname)) { - long dpos = (((((libent[35] << 8) | libent[34]) << 8) | - libent[33]) << 8) | libent[32]; + long dpos; + + dpos = (((((libent[35] << 8) | libent[34]) << 8) | + libent[33]) << 8) | libent[32]; + if ((slfile == stdin) || (fseek(slfile, dpos, 0) == -1)) { dpos -= pos; while (dpos-- > 0) getc(slfile); } - break; + found = true; } } + + if (!found && !dironly) + pm_error("slide '%s' not in library.", sname); } @@ -329,7 +338,7 @@ slider(slvecfn slvec, /* Process the header of the slide file. */ - sdrawkcab = FALSE; /* Initially guess byte order is OK */ + sdrawkcab = false; /* Initially guess byte order is OK */ fread(slfrof.slh, 17, 1, slfile); fread(&slfrof.sntype, sizeof(char), 1, slfile); fread(&slfrof.slevel, sizeof(char), 1, slfile); @@ -364,12 +373,12 @@ slider(slvecfn slvec, */ if (btest != rtest) { - sdrawkcab = TRUE; -#define rshort(x) x = ((x >> 8) & 0xFF) | (x << 8) + sdrawkcab = true; + #define rshort(x) x = ((x >> 8) & 0xFF) | (x << 8) rshort(slfrof.sxdots); rshort(slfrof.sydots); rshort(slfrof.shwfill); -#undef rshort + #undef rshort } /* Dump the header if we're blithering. */ @@ -413,7 +422,7 @@ slider(slvecfn slvec, ixdots = slfrof.sxdots; iydots = slfrof.sydots; dsar = slfrof.sdsar; - adjust = FALSE; /* Mark no adjustment needed */ + adjust = false; /* Mark no adjustment needed */ } /* If there's a uniform scale factor specified, apply it. */ @@ -571,32 +580,42 @@ slider(slvecfn slvec, /* Main program. */ int -main(int argc, - char * argv[]) { +main(int argc, + const char * argv[]) { int argn; const char * const usage = "[-verbose] [-info] [-adjust] [-scale ]\n\ [-dir] [-lib|-Lib ]\n\ [-xsize|-width ] [-ysize|-height ] [sldfile]"; - int scalespec = FALSE, widspec = FALSE, hgtspec = FALSE, dironly = FALSE, - ucasen; + bool dironly; + bool hgtspec; + bool widspec; + bool scalespec; + bool ucasen; const char * slobber; /* Slide library item */ + pm_proginit(&argc, argv); + argn = 1; slobber = NULL; - - ppm_init(&argc, argv); - argn = 1; + dironly = false; + hgtspec = false; + widspec = false; + scalespec = false; + ucasen = false; + blither = false; + info = false; + adjust = false; while (argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0') { if (pm_keymatch(argv[argn], "-verbose", 2)) { - blither = TRUE; + blither = true; } else if (pm_keymatch(argv[argn], "-adjust", 2)) { - adjust = TRUE; + adjust = true; } else if (pm_keymatch(argv[argn], "-dir", 2)) { - dironly = TRUE; + dironly = true; } else if (pm_keymatch(argv[argn], "-info", 2)) { - info = TRUE; + info = true; } else if (pm_keymatch(argv[argn], "-lib", 2)) { if (slobber) pm_error("already specified a library item"); @@ -616,7 +635,7 @@ main(int argc, if (uscale <= 0.0) { pm_error("scale factor must be greater than 0"); } - scalespec = TRUE; + scalespec = true; } else if (pm_keymatch(argv[argn], "-xsize", 2) || pm_keymatch(argv[argn], "-width", 2)) { if (widspec) { @@ -625,7 +644,7 @@ main(int argc, argn++; if ((argn == argc) || (sscanf(argv[argn], "%d", &sxsize) != 1)) pm_usage(usage); - widspec = TRUE; + widspec = true; } else if (pm_keymatch(argv[argn], "-ysize", 2) || pm_keymatch(argv[argn], "-height", 2)) { if (hgtspec) { @@ -634,7 +653,7 @@ main(int argc, argn++; if ((argn == argc) || (sscanf(argv[argn], "%d", &sysize) != 1)) pm_usage(usage); - hgtspec = TRUE; + hgtspec = true; } else { pm_usage(usage); } @@ -665,7 +684,7 @@ main(int argc, if (!dironly) { slider(draw, flood); - ppm_writeppm(stdout, pixels, pixcols, pixrows, pixmaxval, FALSE); + ppm_writeppm(stdout, pixels, pixcols, pixrows, pixmaxval, 0); } pm_close(slfile); pm_close(stdout); diff --git a/converter/ppm/winicontoppm.c b/converter/ppm/winicontoppm.c index 014eab40..6b1376b2 100644 --- a/converter/ppm/winicontoppm.c +++ b/converter/ppm/winicontoppm.c @@ -31,10 +31,9 @@ #define MAJVERSION 0 #define MINVERSION 4 -static int file_offset = 0; /* not actually used, but useful for debug */ +static int fileOffset = 0; /* not actually used, but useful for debug */ static const char er_read[] = "%s: read error"; static const char * infname; -static FILE * ifp; struct cmdlineInfo { /* All the information the user supplied in the command line, @@ -53,8 +52,8 @@ struct cmdlineInfo { static void -parseCommandLine ( int argc, char ** argv, - struct cmdlineInfo *cmdlineP ) { +parseCommandLine (int argc, const char ** argv, + struct cmdlineInfo *cmdlineP ) { /*---------------------------------------------------------------------------- parse program command line described in Unix standard form by argc and argv. Return the information in the options as *cmdlineP. @@ -65,13 +64,15 @@ parseCommandLine ( int argc, char ** argv, 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 = malloc(100*sizeof(optEntry)); + optEntry * option_def; /* Instructions to pm_optParseOptions3 on how to parse our options. */ optStruct3 opt; unsigned int option_def_index; + MALLOCARRAY(option_def, 100); + option_def_index = 0; /* incremented by OPTENT3 */ OPTENT3(0, "allicons", OPT_FLAG, NULL, &cmdlineP->allicons, 0 ); @@ -88,10 +89,9 @@ parseCommandLine ( int argc, char ** argv, opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ opt.allowNegNum = FALSE; /* We have no parms that are negative numbers */ - pm_optParseOptions3( &argc, argv, opt, sizeof(opt), 0); + pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0); /* Uses and sets argc, argv, and some of *cmdlineP and others. */ - if (argc-1 < 1) cmdlineP->inputFilespec = "-"; else @@ -116,37 +116,37 @@ parseCommandLine ( int argc, char ** argv, static int -GetByte(void) { +GetByte(FILE * const ifP) { + int v; - if ((v = getc(ifp)) == EOF) - { + v = getc(ifP); + if (v == EOF) pm_error(er_read, infname); - } return v; } + + static short -GetShort(void) { +GetShort(FILE * const ifP) { + short v; - if (pm_readlittleshort(ifp, &v) == -1) - { - pm_error(er_read, infname); - } - + pm_readlittleshort(ifP, &v); + return v; } + + static long -GetLong(void) { +GetLong(FILE * const ifP) { + long v; - if (pm_readlittlelong(ifp, &v) == -1) - { - pm_error(er_read, infname); - } + pm_readlittlelong(ifP, &v); return v; } @@ -158,14 +158,18 @@ GetLong(void) { * functions. */ static u1 -readU1 (void) { - file_offset++; - return GetByte(); +readU1(FILE * const ifP) { + + ++fileOffset; + + return GetByte(ifP); } + + static u1 * -readU1String (int length) -{ +readU1String (FILE * const ifP, + unsigned int const length) { u1 * string; @@ -173,150 +177,173 @@ readU1String (int length) if (string == NULL) pm_error("out of memory"); - fread(string,sizeof(u1),length,ifp); + fread(string, sizeof(u1), length, ifP); string[length] = 0; - file_offset += length * sizeof(u1); + fileOffset += length * sizeof(u1); + return string; } + + static u2 -readU2 (void) { - file_offset +=2; - return GetShort(); +readU2 (FILE * const ifP) { + + fileOffset +=2; + + return GetShort(ifP); } + + static u4 -readU4 (void) { - file_offset += 4; - return GetLong(); +readU4 (FILE * const ifP) { + + fileOffset += 4; + + return GetLong(ifP); } + + static IC_Entry -readICEntry (void) -{ - IC_Entry entry; +readICEntry(FILE * const ifP) { + + IC_Entry entryP; - MALLOCVAR(entry); + MALLOCVAR(entryP); - if (entry == NULL) + if (entryP == NULL) pm_error("Unable to allcoate memory for IC entry"); - entry->width = readU1(); - entry->height = readU1(); - entry->color_count = readU1(); - entry->reserved = readU1(); - entry->planes = readU2(); - entry->bitcount = readU2(); - entry->size_in_bytes = readU4(); - entry->file_offset = readU4(); - entry->colors = NULL; - entry->ih = NULL; - entry->xorBitmap = NULL; - entry->andBitmap = NULL; + entryP->width = readU1(ifP); + entryP->height = readU1(ifP); + entryP->color_count = readU1(ifP); + entryP->reserved = readU1(ifP); + entryP->planes = readU2(ifP); + entryP->bitcount = readU2(ifP); + entryP->size_in_bytes = readU4(ifP); + entryP->file_offset = readU4(ifP); + entryP->colors = NULL; + entryP->ih = NULL; + entryP->xorBitmap = NULL; + entryP->andBitmap = NULL; - return entry; + return entryP; } static IC_InfoHeader -readInfoHeader (IC_Entry entry) -{ - IC_InfoHeader ih; +readInfoHeader (FILE * const ifP, + IC_Entry const entryP) { + + IC_InfoHeader ihP; - MALLOCVAR(ih); + MALLOCVAR(ihP); - if (ih == NULL) + if (ihP == NULL) pm_error("Unable to allocate memory for info header"); - ih->size = readU4(); - ih->width = readU4(); - ih->height = readU4(); - ih->planes = readU2(); - ih->bitcount = readU2(); - ih->compression = readU4(); - ih->imagesize = readU4(); - ih->x_pixels_per_m = readU4(); - ih->y_pixels_per_m = readU4(); - ih->colors_used = readU4(); - ih->colors_important = readU4(); + ihP->size = readU4(ifP); + ihP->width = readU4(ifP); + ihP->height = readU4(ifP); + ihP->planes = readU2(ifP); + ihP->bitcount = readU2(ifP); + ihP->compression = readU4(ifP); + ihP->imagesize = readU4(ifP); + ihP->x_pixels_per_m = readU4(ifP); + ihP->y_pixels_per_m = readU4(ifP); + ihP->colors_used = readU4(ifP); + ihP->colors_important = readU4(ifP); - if (!entry->bitcount) entry->bitcount = ih->bitcount; - if (entry->color_count == 0 && - entry->bitcount <= 8) entry->color_count = 256; - if (ih->compression) { + if (!entryP->bitcount) + entryP->bitcount = ihP->bitcount; + + if (entryP->color_count == 0 && entryP->bitcount <= 8) + entryP->color_count = 256; + + if (ihP->compression) { pm_error("Can't handle compressed icons"); } - return ih; + return ihP; } -/* - * I don't know why this isn't the same as the spec, it just isn't - * The colors honestly seem to be stored BGR. Bizarre. - * - * I've checked this in the BMP code for bmptoppm and the gimp. Guess the - * spec I have is just plain wrong. - */ + + static IC_Color -readICColor (void) -{ - IC_Color col; +readICColor(FILE * const ifP) { + + IC_Color colorP; - MALLOCVAR(col); + MALLOCVAR(colorP); - if (col == NULL) + if (colorP == NULL) pm_error("Unable to allocate memory for color"); - col->blue = readU1(); - col->green = readU1(); - col->red = readU1(); - col->reserved = readU1(); - return col; + /* I don't know why this isn't the same as the spec, it just isn't. + The colors honestly seem to be stored BGR. Bizarre. + + I've checked this in the BMP code for bmptoppm and the gimp. Guess the + spec I have is just plain wrong. + */ + + colorP->blue = readU1(ifP); + colorP->green = readU1(ifP); + colorP->red = readU1(ifP); + colorP->reserved = readU1(ifP); + + return colorP; } -/* - * Depending on if the image is stored as 1bpp, 4bpp or 8bpp, the - * encoding mechanism is different. - * - * 8bpp => 1 byte/palette index. - * 4bpp => High Nibble, Low Nibble - * 1bpp => 1 palette value per bit, high bit 1st. - */ static u1 * -read1Bitmap (int width, int height) -{ - int tmp; - int xBytes; +read1Bitmap (FILE * const ifP, + unsigned int const width, + unsigned int const height) { + + unsigned int row; + unsigned int xByteCt; u1 * bitmap; - int wt = width; + unsigned int wt; MALLOCARRAY(bitmap, width * height); if (bitmap == NULL) pm_error("out of memory"); - wt >>= 3; - if (wt & 3) { - wt = (wt & ~3) + 4; + /* Depending on if the image is stored as 1bpp, 4bpp or 8bpp, the + encoding mechanism is different. + + 8bpp => 1 byte/palette index. + 4bpp => High Nibble, Low Nibble + 1bpp => 1 palette value per bit, high bit 1st. + */ + + wt = width >> 3; + + if ((wt & 0x3) != 0) { + wt = (wt & ~0x3) + 4; } - xBytes = wt; - for (tmp = 0; tmp>= 1; - } } - free(row); + free(imgRow); } return bitmap; } @@ -324,43 +351,46 @@ read1Bitmap (int width, int height) static u1 * -read4Bitmap (int width, int height) -{ - int tmp; +read4Bitmap (FILE * const ifP, + unsigned int const width, + unsigned int const height) { + + unsigned int row; u1 * bitmap; - int wt = width; - int xBytes; + unsigned int wt; + unsigned int xByteCt; MALLOCARRAY(bitmap, width * height); if (bitmap == NULL) pm_error("out of memory"); + wt = width >> 1; - wt >>= 1; - if (wt & 3) { - wt = (wt & ~3) + 4; + if (wt & 0x3) { + wt = (wt & ~0x3) + 4; } - xBytes = wt; - for (tmp = 0; tmp> 4; + *(bitmap + ((height - row - 1) * width) + col) = + (imgRow[rowByte] & 0xF0) >> 4; } else { - *(bitmap+((height-tmp-1)*width) + (x)) = (row[rowByte] & 0xF); - rowByte++; + *(bitmap + ((height - row -1) * width) + col) = + (imgRow[rowByte] & 0xF); + ++rowByte; } bottom = !bottom; } - free(row); + free(imgRow); } return bitmap; } @@ -368,52 +398,57 @@ read4Bitmap (int width, int height) static u1 * -read8Bitmap (int width, int height) -{ - int tmp; - unsigned int xBytes; - unsigned int wt = width; +read8Bitmap (FILE * const ifP, + unsigned int const width, + unsigned int const height) { + + unsigned int row; + unsigned int xByteCt; + unsigned int wt; u1 * bitmap; MALLOCARRAY(bitmap, width * height); if (bitmap == NULL) pm_error("out of memory"); - if (wt & 3) { - wt = (wt & ~3) + 4; + wt = width; + if (wt & 0x3) { + wt = (wt & ~0x3) + 4; } - xBytes = wt; - for (tmp = 0; tmp> 3; - unsigned int const xBytes = width * bytes; +readXBitmap (FILE * const ifP, + unsigned int const width, + unsigned int const height, + unsigned int const bpp) { +/*---------------------------------------------------------------------------- + Read a true color bitmap. (24/32 bits) + + The output routine deplanarizes it for us, we keep it flat here. +-----------------------------------------------------------------------------*/ + unsigned int const byteCt = bpp >> 3; + unsigned int const xByteCt = width * byteCt; u1 * bitmap; /* remember - bmp (dib) stored upside down, so reverse */ - MALLOCARRAY(bitmap, bytes * width * height); + MALLOCARRAY(bitmap, byteCt * width * height); if (bitmap == NULL) pm_error("out of memory allocating bitmap array"); @@ -421,12 +456,12 @@ readXBitmap (int const width, unsigned int i; u1 * bitcurptr; - for (i = 0, bitcurptr = &bitmap[bytes * width * (height-1)]; + for (i = 0, bitcurptr = &bitmap[byteCt * width * (height-1)]; i < height; - ++i, bitcurptr -= xBytes) { + ++i, bitcurptr -= xByteCt) { - u1 * const row = readU1String(xBytes); - memcpy(bitcurptr, row, xBytes); + u1 * const row = readU1String(ifP, xByteCt); + memcpy(bitcurptr, row, xByteCt); free(row); } } @@ -436,80 +471,70 @@ readXBitmap (int const width, static MS_Ico -readIconFile (bool const verbose) { - int iter,iter2; +readIconFile(FILE * const ifP, + bool const verbose) { + + unsigned int i; MS_Ico MSIconData; MALLOCVAR(MSIconData); - /* - * reserved - should equal 0. - */ - MSIconData->reserved = readU2(); - /* - * Type - should equal 1 - */ - MSIconData->type = readU2(); - /* - * count - no of icons in file.. - */ - MSIconData->count = readU2(); - /* - * Allocate "count" array of entries. - */ + MSIconData->reserved = readU2(ifP); /* should be 0 */ + MSIconData->type = readU2(ifP); /* should be 1 */ + MSIconData->count = readU2(ifP); /* # icons in file */ + if (verbose) pm_message("Icon file contains %d icons.", MSIconData->count); MALLOCARRAY(MSIconData->entries, MSIconData->count); if (MSIconData->entries == NULL) pm_error("out of memory"); - /* - * Read in each of the entries - */ - for (iter = 0;iter < MSIconData->count ; iter++ ) { - MSIconData->entries[iter] = readICEntry(); - } - /* After that, we have to read in the infoheader, color map (if - * any) and the actual bit/pix maps for the icons. - */ + + /* Read in each of the entries */ + for (i = 0; i < MSIconData->count; ++i) + MSIconData->entries[i] = readICEntry(ifP); + + /* Read in the infoheader, color map (if any) and the actual bit/pix maps + for the icons. + */ if (verbose) - fprintf (stderr,"#\tColors\tBPP\tWidth\tHeight\n"); - for (iter = 0;iter < MSIconData->count ; iter++ ) { - int bpp; - MSIconData->entries[iter]->ih = - readInfoHeader (MSIconData->entries[iter]); + pm_message("#\tColors\tBPP\tWidth\tHeight\n"); + + for (i = 0; i < MSIconData->count; ++i) { + unsigned int bpp; /* bits per pixel */ + + MSIconData->entries[i]->ih = + readInfoHeader(ifP, MSIconData->entries[i]); - /* What's the bits per pixel? */ - bpp = MSIconData->entries[iter]->bitcount; + bpp = MSIconData->entries[i]->bitcount; + /* Read the palette, if appropriate */ switch (bpp) { case 24: case 32: /* 24/32 bpp icon has no palette */ break; - default: - MALLOCARRAY(MSIconData->entries[iter]->colors, - MSIconData->entries[iter]->color_count); - if (MSIconData->entries[iter]->colors == NULL) + default: { + unsigned int j; + + MALLOCARRAY(MSIconData->entries[i]->colors, + MSIconData->entries[i]->color_count); + if (MSIconData->entries[i]->colors == NULL) pm_error("out of memory"); - for (iter2 = 0; - iter2 < MSIconData->entries[iter]->color_count ; - iter2++ ) { - MSIconData->entries[iter]->colors[iter2] = readICColor(); - } - break; + for (j = 0; j < MSIconData->entries[i]->color_count; ++j) + MSIconData->entries[i]->colors[j] = readICColor(ifP); + } } if (verbose) { - char cols_text[10]; - sprintf (cols_text, "%d", MSIconData->entries[iter]->color_count); - fprintf (stderr, - "%d\t%s\t%d\t%d\t%d\n", iter, - MSIconData->entries[iter]->color_count ? - cols_text : "TRUE", - bpp, MSIconData->entries[iter]->width, - MSIconData->entries[iter]->height); + char colsText[10]; + sprintf (colsText, "%d", MSIconData->entries[i]->color_count); + pm_message("%d\t%s\t%d\t%d\t%d\n", i, + MSIconData->entries[i]->color_count ? + colsText : "TRUE", + bpp, MSIconData->entries[i]->width, + MSIconData->entries[i]->height); } /* Pixels are stored bottom-up, left-to-right. Pixel lines are * padded with zeros to end on a 32bit (4byte) boundary. Every @@ -528,35 +553,40 @@ readIconFile (bool const verbose) { */ switch (bpp) { case 1: - MSIconData->entries[iter]->xorBitmap = - read1Bitmap(MSIconData->entries[iter]->width, - MSIconData->entries[iter]->height); + MSIconData->entries[i]->xorBitmap = + read1Bitmap(ifP, + MSIconData->entries[i]->width, + MSIconData->entries[i]->height); break; case 4: - MSIconData->entries[iter]->xorBitmap = - read4Bitmap(MSIconData->entries[iter]->width, - MSIconData->entries[iter]->height); + MSIconData->entries[i]->xorBitmap = + read4Bitmap(ifP, + MSIconData->entries[i]->width, + MSIconData->entries[i]->height); break; case 8: - MSIconData->entries[iter]->xorBitmap = - read8Bitmap(MSIconData->entries[iter]->width, - MSIconData->entries[iter]->height); + MSIconData->entries[i]->xorBitmap = + read8Bitmap(ifP, + MSIconData->entries[i]->width, + MSIconData->entries[i]->height); break; case 24: case 32: - MSIconData->entries[iter]->xorBitmap = - readXBitmap(MSIconData->entries[iter]->width, - MSIconData->entries[iter]->height,bpp); + MSIconData->entries[i]->xorBitmap = + readXBitmap(ifP, + MSIconData->entries[i]->width, + MSIconData->entries[i]->height,bpp); break; default: - pm_error("Uncatered bit depth %d",bpp); + pm_error("Uncatered bit depth %u", bpp); } /* * Read AND Bitmap */ - MSIconData->entries[iter]->andBitmap = - read1Bitmap(MSIconData->entries[iter]->width, - MSIconData->entries[iter]->height); + MSIconData->entries[i]->andBitmap = + read1Bitmap(ifP, + MSIconData->entries[i]->width, + MSIconData->entries[i]->height); } } @@ -566,15 +596,14 @@ readIconFile (bool const verbose) { static char * -trimOutputName(const char inputName[]) -{ +trimmedOutputName(const char inputName[]) { /* * Just trim off the final ".ppm", if there is one, else return as is. * oh, for =~ ... :) */ - char * outFile = strdup(inputName); - if (streq(outFile + (strlen (outFile) - 4), ".ppm")) { - *(outFile + (strlen (outFile) - 4)) = 0; + char * const outFile = strdup(inputName); + if (streq(outFile + (strlen(outFile) - 4), ".ppm")) { + *(outFile + (strlen(outFile) - 4)) = 0; } return outFile; @@ -585,29 +614,34 @@ trimOutputName(const char inputName[]) static int getBestQualityIcon(MS_Ico MSIconData) { - int x,best,best_size,best_bpp,bpp,size; - IC_Entry entry; - - best_size = best_bpp = 0; - for (x = 0; x < MSIconData->count; x++) { - entry = MSIconData->entries[x]; - size = entry->width * entry->height; - bpp = entry->bitcount ? entry->bitcount : entry->ih->bitcount; - if (size > best_size) { - best = x; - best_size = size; - } else if (size == best_size && bpp > best_bpp) { - best = x; - best_bpp = bpp; + unsigned int i; + unsigned int best; + unsigned int bestSize; + unsigned int bestBpp; + + for (i = 0, bestSize = 0, bestBpp = 0; i < MSIconData->count; ++i) { + IC_Entry const entryP = MSIconData->entries[i]; + unsigned int const size = entryP->width * entryP->height; + unsigned int const bpp = + entryP->bitcount ? entryP->bitcount : entryP->ih->bitcount; + + if (size > bestSize) { + best = i; + bestSize = size; + } else if (size == bestSize && bpp > bestBpp) { + best = i; + bestBpp = bpp; } } return best; } + + static void writeXors(FILE * const multiOutF, - char outputFileBase[], - IC_Entry const entry, + char * const outputFileBase, + IC_Entry const entryP, int const entryNum, bool const multiple, bool const xor) { @@ -625,85 +659,79 @@ writeXors(FILE * const multiOutF, we are to open a file using outputFileBase[] and 'entryNum' and 'xor' to derive its name, and close it afterward. -----------------------------------------------------------------------------*/ - FILE * outF; - pixel ** ppm_array; - int row; - int pel_size; - const char *outputFile; - int maxval; - int forcetext; + FILE * ofP; + pixel ** pixArray; + unsigned int row; + const char * outputFileName; if (multiOutF) { - outF = multiOutF; - outputFile = strdup(""); + ofP = multiOutF; + outputFileName = strdup(""); } else { if (outputFileBase) { if (multiple) { - pm_asprintf(&outputFile, "%s%s_%d.ppm", + pm_asprintf(&outputFileName, "%s%s_%d.ppm", outputFileBase,(xor ? "_xor" : ""), entryNum); } else { - pm_asprintf(&outputFile, "%s%s.ppm", + pm_asprintf(&outputFileName, "%s%s.ppm", outputFileBase,(xor ? "_xor" : "")); } } else - outputFile = strdup("-"); + outputFileName = strdup("-"); - outF = pm_openw(outputFile); + ofP = pm_openw(outputFileName); } /* - * allocate an array to save the bmp data into. - * note that entry->height will be 1/2 entry->ih->height, - * as the latter adds "and" and "xor" height. - */ - ppm_array = ppm_allocarray(entry->width, entry->height); - for (row=0; row < entry->height; row++) { - u1 * xorRow; - int col; - switch (entry->bitcount) { + Allocate an array to save the bmp data into. + note that entry->height will be 1/2 entry->ih->height, + as the latter adds "and" and "xor" height. + */ + pixArray = ppm_allocarray(entryP->width, entryP->height); + for (row = 0; row < entryP->height; ++row) { + switch (entryP->bitcount) { case 24: - case 32: - pel_size = entry->bitcount >> 3; - xorRow = entry->xorBitmap + row * entry->width * pel_size; - for (col=0; col < entry->width*pel_size;col+=pel_size) { - PPM_ASSIGN(ppm_array[row][col/pel_size], - xorRow[col+2],xorRow[col+1],xorRow[col]); - } - break; - default: - xorRow = entry->xorBitmap + row * entry->width; - for (col=0; col < entry->width; col++) { - int colorIndex; - IC_Color color; - colorIndex = xorRow[col]; - color = entry->colors[colorIndex]; - PPM_ASSIGN(ppm_array[row][col], - color->red,color->green,color->blue); + case 32: { + unsigned int const pixelSize = entryP->bitcount >> 3; + u1 * const xorRow = + entryP->xorBitmap + row * entryP->width * pixelSize; + unsigned int col; + for (col = 0; col < entryP->width * pixelSize; col += pixelSize) + PPM_ASSIGN(pixArray[row][col/pixelSize], + xorRow[col+2], xorRow[col+1], xorRow[col]); + } break; + default: { + u1 * const xorRow = entryP->xorBitmap + row * entryP->width; + unsigned int col; + for (col = 0; col < entryP->width; ++col) { + unsigned int const colorIndex = xorRow[col]; + IC_Color const colorP = entryP->colors[colorIndex]; + PPM_ASSIGN(pixArray[row][col], + colorP->red, colorP->green, colorP->blue); } - break; + } break; } } - maxval = 255; - forcetext = 0; - - ppm_writeppm(outF,ppm_array,entry->width, entry->height, - (pixval) maxval, forcetext); - ppm_freearray(ppm_array,entry->height); + ppm_writeppm(ofP, pixArray, entryP->width, entryP->height, + 255 /* maxval */, false /* text */); + ppm_freearray(pixArray, entryP->height); - pm_strfree(outputFile); + pm_strfree(outputFileName); if (!multiOutF) - pm_close(outF); + pm_close(ofP); } static void -writeAnds(FILE * const multiOutF, - char outputFileBase[], IC_Entry const entry, int const entryNum, - bool multiple) { +writeAnds(FILE * const multiOutF, + char const outputFileBase[], + IC_Entry const entryP, + unsigned int const entryNum, + bool const multiple) { /*---------------------------------------------------------------------------- - Write the "and" image (i.e. the alpha mask) of the image 'IC_Entry' out. + Write the "and" image (i.e. the alpha mask) of the image *entryP out. 'multiple' means this is one of multiple images that are being written. 'entryNum' is the sequence number within the winicon file of the image @@ -714,115 +742,122 @@ writeAnds(FILE * const multiOutF, we are to open a file using outputFileBase[] and 'entryNum' and 'xor' to derive its name, and close it afterward. -----------------------------------------------------------------------------*/ - FILE * outF; - bit ** pbm_array; - u1 * andRow; - int row; + FILE * ofP; + bit ** bitArray; + unsigned int row; if (multiOutF) - outF = multiOutF; + ofP = multiOutF; else { - const char *outputFile; + const char * outputFileName; assert(outputFileBase); if (multiple) - pm_asprintf(&outputFile, "%s_and_%d.pbm", + pm_asprintf(&outputFileName, "%s_and_%u.pbm", outputFileBase, entryNum); else - pm_asprintf(&outputFile, "%s_and.pbm", outputFileBase); - outF = pm_openw(outputFile); - pm_strfree(outputFile); + pm_asprintf(&outputFileName, "%s_and.pbm", outputFileBase); + ofP = pm_openw(outputFileName); + pm_strfree(outputFileName); } - pbm_array = pbm_allocarray(entry->width, entry->height); - for (row=0; row < entry->height; row++) { - int col; - andRow = entry->andBitmap + row * entry->width; - for (col=0; col < entry->width; col++) { + bitArray = pbm_allocarray(entryP->width, entryP->height); + for (row = 0; row < entryP->height; ++row) { + u1 * const andRow = entryP->andBitmap + row * entryP->width; + unsigned int col; + for (col = 0; col < entryP->width; ++col) { /* Note: black is transparent in a Netpbm alpha mask */ - pbm_array[row][col] = andRow[col] ? PBM_BLACK: PBM_WHITE; + bitArray[row][col] = andRow[col] ? PBM_BLACK: PBM_WHITE; } } - pbm_writepbm(outF, pbm_array, entry->width, entry->height, 0); + pbm_writepbm(ofP, bitArray, entryP->width, entryP->height, 0); - pbm_freearray(pbm_array, entry->height); + pbm_freearray(bitArray, entryP->height); if (!multiOutF) - pm_close (outF); + pm_close(ofP); } static void -openMultiXor(char outputFileBase[], +openMultiXor(char const outputFileBase[], bool const writeands, FILE ** const multiOutFP) { - const char *outputFile; + const char * outputFileName; if (outputFileBase) { - pm_asprintf(&outputFile, "%s%s.ppm", + pm_asprintf(&outputFileName, "%s%s.ppm", outputFileBase, (writeands ? "_xor" : "")); } else - outputFile = strdup("-"); + outputFileName = strdup("-"); - /* - * Open the output file now, it'll stay open the whole time. - */ - *multiOutFP = pm_openw(outputFile); + *multiOutFP = pm_openw(outputFileName); - pm_strfree(outputFile); + pm_strfree(outputFileName); } static void -openMultiAnd(char outputFileBase[], FILE ** const multiAndOutFP) { +openMultiAnd(char const outputFileBase[], + FILE ** const multiAndOutFP) { - const char *outputFile; + const char * outputFileName; assert(outputFileBase); - pm_asprintf(&outputFile, "%s_and.pbm", outputFileBase); + pm_asprintf(&outputFileName, "%s_and.pbm", outputFileBase); - *multiAndOutFP = pm_openw(outputFile); + *multiAndOutFP = pm_openw(outputFileName); - pm_strfree(outputFile); + pm_strfree(outputFileName); } -static void free_iconentry(IC_Entry entry) { - int x; - if (entry->colors && entry->color_count) { - for (x=0;xcolor_count;x++) free(entry->colors[x]); - free(entry->colors); + + +static void +freeIconentry(IC_Entry const entryP) { + + if (entryP->colors && entryP->color_count) { + unsigned int i; + for (i = 0; i color_count; ++i) + free(entryP->colors[i]); + free(entryP->colors); } - if (entry->andBitmap) free(entry->andBitmap); - if (entry->xorBitmap) free(entry->xorBitmap); - if (entry->ih) free(entry->ih); - free(entry); + if (entryP->andBitmap) free(entryP->andBitmap); + if (entryP->xorBitmap) free(entryP->xorBitmap); + if (entryP->ih) free(entryP->ih); + free(entryP); } -static void free_icondata(MS_Ico MSIconData) -{ - int x; - for (x=0;xcount;x++) { - free_iconentry(MSIconData->entries[x]); - } - free(MSIconData); + + +static void +freeIcondata(MS_Ico const MSIconDataP) { + + unsigned int i; + for (i = 0; i < MSIconDataP->count; ++i) + freeIconentry(MSIconDataP->entries[i]); + + free(MSIconDataP); } + int -main(int argc, char *argv[]) { +main(int argc, const char *argv[]) { struct cmdlineInfo cmdline; - int startEntry, endEntry; - MS_Ico MSIconData; + FILE * ifP; + unsigned int startEntry, endEntry; + MS_Ico MSIconDataP; char * outputFileBase; FILE * multiOutF; FILE * multiAndOutF; - ppm_init (&argc, argv); + pm_proginit(&argc, argv); parseCommandLine(argc, argv, &cmdline); @@ -833,13 +868,13 @@ main(int argc, char *argv[]) { if (streq(cmdline.outputFilespec, "-")) outputFileBase = NULL; else - outputFileBase = trimOutputName(cmdline.outputFilespec); + outputFileBase = trimmedOutputName(cmdline.outputFilespec); - ifp = pm_openr(cmdline.inputFilespec); + ifP = pm_openr(cmdline.inputFilespec); infname = cmdline.inputFilespec; - MSIconData = readIconFile(cmdline.verbose); + MSIconDataP = readIconFile(ifP, cmdline.verbose); /* * Now we've read the icon file in (Hopefully! :) * Go through each of the entries, and write out files of the @@ -853,18 +888,15 @@ main(int argc, char *argv[]) { /* * If allicons is set, we want everything, if not, just go through once. */ - startEntry = 0; - if (cmdline.allicons) { - endEntry = MSIconData->count; - } else { - endEntry = 1; - } - /* - * If bestqual is set, find the icon with highest size & bpp. - */ if (cmdline.bestqual) { - startEntry = getBestQualityIcon(MSIconData); + startEntry = getBestQualityIcon(MSIconDataP); endEntry = startEntry+1; + } else { + startEntry = 0; + if (cmdline.allicons) + endEntry = MSIconDataP->count; + else + endEntry = 1; } if (cmdline.multippm) @@ -878,24 +910,25 @@ main(int argc, char *argv[]) { multiAndOutF = NULL; { - int entryNum; + unsigned int entryNum; - for (entryNum = startEntry ; entryNum < endEntry ; entryNum++ ) { - IC_Entry const entry = MSIconData->entries[entryNum]; + for (entryNum = startEntry; entryNum < endEntry; ++entryNum) { + IC_Entry const entryP = MSIconDataP->entries[entryNum]; - writeXors(multiOutF, outputFileBase, entry, entryNum, + writeXors(multiOutF, outputFileBase, entryP, entryNum, cmdline.allicons, cmdline.writeands); if (cmdline.writeands) writeAnds(multiAndOutF, outputFileBase, - entry, entryNum, cmdline.allicons); + entryP, entryNum, cmdline.allicons); } } if (multiOutF) - pm_close (multiOutF); + pm_close(multiOutF); if (multiAndOutF) pm_close(multiAndOutF); /* free up the image data here. */ - free_icondata(MSIconData); + freeIcondata(MSIconDataP); + return 0; } diff --git a/converter/ppm/yuvtoppm.c b/converter/ppm/yuvtoppm.c index 2b44c65f..151ff9f9 100644 --- a/converter/ppm/yuvtoppm.c +++ b/converter/ppm/yuvtoppm.c @@ -20,96 +20,201 @@ ** implied warranty. */ -#include "ppm.h" +#include "pm_c_util.h" #include "mallocvar.h" +#include "shhopt.h" +#include "ppm.h" -/* x must be signed for the following to work correctly */ -#define limit(x) ((((x)>0xffffff)?0xff0000:(((x)<=0xffff)?0:(x)&0xff0000))>>16) -int -main(argc, argv) - char **argv; -{ - FILE *ifp; - pixel *pixrow; - int argn, rows, cols, row; - const char * const usage = " [yuvfile]"; - struct yuv { - /* This is an element of a YUV file. It describes two - side-by-side pixels. - */ - unsigned char u; - unsigned char y1; - unsigned char v; - unsigned char y2; - } *yuvbuf; - - ppm_init(&argc, argv); - - argn = 1; - - if (argn + 2 > argc) - pm_usage(usage); - - cols = atoi(argv[argn++]); - rows = atoi(argv[argn++]); - if (cols <= 0 || rows <= 0) - pm_usage(usage); - - if (argn < argc) { - ifp = pm_openr(argv[argn]); - ++argn; - } else - ifp = stdin; - - if (argn != argc) - pm_usage(usage); - - if (cols % 2 != 0) { - pm_error("Number of columns (%d) is odd. A YUV image must have an " - "even number of columns.", cols); + +struct CmdlineInfo { + /* All the information the user supplied in the command line, + in a form easy for the program to use. + */ + unsigned int cols; + unsigned int rows; + const char * inputFileName; /* Name of input file */ +}; + + + +static void +parseCommandLine(int argc, const char ** argv, + struct CmdlineInfo * const cmdlineP) { + + optEntry * option_def; + /* Instructions to OptParseOptions3 on how to parse our options */ + optStruct3 opt; + + MALLOCARRAY_NOFAIL(option_def, 100); + + OPTENTINIT; + + opt.opt_table = option_def; + opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ + opt.allowNegNum = FALSE; /* We have no parms that are negative numbers */ + + pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0); + /* Uses and sets argc, argv, and some of *cmdlineP and others. */ + + if (argc-1 < 2) + pm_error("You need at least two arguments: width and height in " + "pixels. You specified %u", argc-1); + else { + int const widthArg = atoi(argv[1]); + int const heightArg = atoi(argv[2]); + + if (widthArg < 0) + pm_error("Negative number for width: %d", widthArg); + if (heightArg < 0) + pm_error("Negative number for height: %d", heightArg); + + cmdlineP->cols = widthArg; + cmdlineP->rows = heightArg; + + if (cmdlineP->cols % 2 != 0) + pm_error("Number of columns (%u) is odd. " + "A YUV image must have an " + "even number of columns.", cmdlineP->cols); + + if (argc-1 < 3) + cmdlineP->inputFileName = "-"; + else { + cmdlineP->inputFileName = argv[3]; + + if (argc-1 > 3) + pm_error("Too many arguments: %u. " + "The only possible arguments are " + "width, height, and input file name", argc-1); + } } +} - ppm_writeppminit(stdout, cols, rows, (pixval) 255, 0); - pixrow = ppm_allocrow(cols); - MALLOCARRAY(yuvbuf, (cols+1)/2); - if (yuvbuf == NULL) - pm_error("Unable to allocate YUV buffer for %d columns.", cols); - for (row = 0; row < rows; ++row) { - int col; - fread(yuvbuf, cols * 2, 1, ifp); +static int +limit(int const x) { - for (col = 0; col < cols; col += 2) { - /* Produce two pixels in pixrow[] */ - int y1, u, v, y2, r, g, b; + if (x > 0xffffff) + return 0xff; + else if (x <= 0xffff) + return 0; + else + return ((x >> 16) & 0xff); +} + + + +static int +nonneg(int const x) { +/*---------------------------------------------------------------------------- + Raise 'x' to 0 if negative +-----------------------------------------------------------------------------*/ + return x < 0 ? 0 : x; +} + + + +struct Yuv { +/*---------------------------------------------------------------------------- + This is an element of a YUV file. It describes two side-by-side pixels. + + This is the actual layout of the data in the file (4 bytes). +-----------------------------------------------------------------------------*/ + unsigned char u; + unsigned char y1; + unsigned char v; + unsigned char y2; +}; - u = yuvbuf[col/2].u-128; - y1 = yuvbuf[col/2].y1 - 16; - if (y1 < 0) y1 = 0; - v = yuvbuf[col/2].v - 128; +static void +readYuv(FILE * const ifP, + struct Yuv * const yuvP) { - y2 = yuvbuf[col/2].y2 - 16; - if (y2 < 0) y2 = 0; + size_t readCt; - r = 104635 * v; - g = -25690 * u + -53294 * v; - b = 132278 * u; + readCt = fread(yuvP, sizeof(*yuvP), 1, ifP); - y1*=76310; y2*=76310; + if (readCt != 1) { + if (feof(ifP)) + pm_error("Premature end of input."); + else + pm_error("Error reading input."); + } +} + + + +static void +yuvtoppm(FILE * const ifP, + unsigned int const cols, + unsigned int const rows, + FILE * const ofP) { + + pixval const maxval = 255; + + pixel * pixrow; + unsigned int row; + + ppm_writeppminit(ofP, cols, rows, maxval, 0); + + pixrow = ppm_allocrow(cols); + + for (row = 0; row < rows; ++row) { + unsigned int col; + for (col = 0; col < cols; col += 2) { + /* Produce two pixels in pixrow[] */ + struct Yuv yuv; + int y1, u, v, y2, r, g, b; + + readYuv(ifP, &yuv); + + u = yuv.u - 128; + y1 = nonneg (yuv.y1 - 16); + v = yuv.v - 128; + y2 = nonneg (yuv.y2 - 16); + + r = 104635 * v; + g = -25690 * u + -53294 * v; + b = 132278 * u; + + y1 *= 76310; + y2 *= 76310; + + PPM_ASSIGN(pixrow[col], + limit(r + y1), limit(g + y1), limit(b + y1)); + PPM_ASSIGN(pixrow[col + 1], + limit(r + y2), limit(g + y2), limit(b + y2)); + } + ppm_writeppmrow(ofP, pixrow, cols, maxval, 0); + } - PPM_ASSIGN(pixrow[col], limit(r+y1), limit(g+y1), limit(b+y1)); - PPM_ASSIGN(pixrow[col+1], limit(r+y2), limit(g+y2), limit(b+y2)); - } - ppm_writeppmrow(stdout, pixrow, cols, (pixval) 255, 0); - } - free(yuvbuf); ppm_freerow(pixrow); - pm_close(ifp); - pm_close(stdout); - exit(0); + if (fgetc(ifP) != EOF) + pm_message("Extraneous data at end of image."); +} + + + +int +main (int argc, const char ** argv) { + + FILE * ifP; + struct CmdlineInfo cmdline; + + pm_proginit(&argc, argv); + + parseCommandLine(argc, argv, &cmdline); + + ifP = pm_openr(cmdline.inputFileName); + + yuvtoppm(ifP, cmdline.cols, cmdline.rows, stdout); + + pm_close(ifP); + pm_close(stdout); + + return 0; } diff --git a/doc/HISTORY b/doc/HISTORY index 16f04d14..30be7d5c 100644 --- a/doc/HISTORY +++ b/doc/HISTORY @@ -4,51 +4,37 @@ Netpbm. CHANGE HISTORY -------------- -13.06.27 BJH Release 10.62.09 +13.06.29 BJH Release 10.63.00 - libnetpbm: fix bug: pnm_readpaminit and pnm_writepaminit set - 'opacity_plane' member of struct pam incorrectly. No Netpbm - programs are affected. + Add pamtowinicon, winicontopam. Thanks Ludolf Holzheid + (lholzheid@bihl-wiedemann.de). -13.06.14 BJH Release 10.62.08 + pgmnoise: add -maxval, speed up. Thanks Prophet of the Way + . - giftopnm: fix bug: erroneously claims GIF ends prematurely. - Broken in Netpbm 10.38 (March 2007). This affects all GIfs, but - the problem does not manifest when Netpbm was built with Gcc - from 2007 and later. + Perl programs: make them shell programs that reinvoke themselves + as perl so we can get the Perl interpreter from the PATH. - libnetpbm: fix bug: ppm_freecolornames() has wild pointer - dereference when the color name table was generated empty - because the color dictionary file was not openable. ppmtoxpm - suffers from this. Broken in 10.15 (April 2003). + yuvtoppm: check for inconsistencies between specified + width and height and size of input. - ppmtoxpm: fix bug: ignores inability to open the specified color - dictionary file (-rgb) and just doesn't use color names. - Introduced in Netpbm 10.15 (April 2003). + 411toppm: check for inconsistencies between specified + width and height and size of input. - ppmforge. fix crash when -mesh is 1 or less. Always broken - (program was added in Pbmplus (October 1991). Thanks Prophet of - the Way . + Ignore -plain when program generates PAM. Before, programs + failed if the user specified -plain to a program that generates + a PAM image. - ppmforge: fix array bounds violation. Always broken (program - was added in Pbmplus (October 1991). Thanks Prophet of the Way - . - -13.05.27 BJH Release 10.62.07 + giftopnm: fix bug: erroneously claims GIF ends prematurely. + Broken in Netpbm 10.38 (March 2007). This affects all GIFs, but + the problem does not manifest when Netpbm was built with Gcc + from 2007 and later. pnmtops: Fix bug: only first image gets converted. Broken in Netpbm 10.56 (September 2011). -13.05.20 BJH Release 10.62.06 - - pnmremap (ergo pnmquant): fix bug: incorrect output with - -norandom and floyd-steinberg dithering. Always broken. - (-norandom was introduced in Netpbm 10.39 (June 2007)). - -13.05.11 BJH Release 10.62.05 - pnmtopng: fix bug: incorrect output when output should have - an alpha mask. Broken Netpbm in 10.55 (June 2011). Thanks + an alpha mask. Broken in Netpbm 10.55 (June 2011). Thanks Ludolf Holzheid (lholzheid@bihl-wiedemann.de). pnmtopng: fix bug: output bigger than it needs to be when the @@ -57,32 +43,55 @@ CHANGE HISTORY (September 2003). Thanks Ludolf Holzheid (lholzheid@bihl-wiedemann.de). + pnmremap (ergo pnmquant): fix bug: incorrect output with + -norandom and floyd-steinberg dithering. Always broken. + (-norandom was introduced in Netpbm 10.39 (June 2007)). + pamtilt: fix bug: incorrect output or invalid memory access crash. Always broken (program was new in Netpbm 10.30 (October 2005)). -13.05.02 BJH Release 10.62.04 + pnmpsnr: fix bug: says types aren't the same when they are. + Introduced in Netpbm 10.61 (December 2012). + + ppmtowinicon: fix bug: sometimes creates image truncated in the + middle of the AND mask. Always broken (program was new in + Netpbm 9.3 (June 2000)). + + ppmtoxpm: fix bug: ignores inability to open the specified color + dictionary file (-rgb) and just doesn't use color names. + Introduced in Netpbm 10.15 (April 2003). + + libnetpbm: fix bug: ppm_freecolornames() has wild pointer + dereference when the color name table was generated empty + because the color dictionary file was not openable. ppmtoxpm + suffers from this. Broken in 10.15 (April 2003). + + libnetpbm: fix bug: pnm_readpaminit and pnm_writepaminit set + 'opacity_plane' member of struct pam incorrectly. No Netpbm + programs are affected. Always broken (member was added in + Netpbm 10.56 (September 2011)). sparc64 pbmtog3: fix bug that causes crash due to unaligned memory access. -13.04.18 BJH Release 10.62.03 + ppmforge. fix crash when -mesh is 1 or less. Always broken + (program was added in Pbmplus (October 1991). Thanks Prophet of + the Way . - build: fix problem with creating lib/util that already exists. - Broken in 10.62. + ppmforge: fix array bounds violation. Always broken (program + was added in Pbmplus (October 1991). Thanks Prophet of the Way + . -13.04.13 BJH Release 10.62.02 + install: fix Perl warning in installnetpbm. Broken in + Netpbm 10.61. - ppmtowinicon: fix bug: sometimes creates image truncated in the - middle of the AND mask. Always broken (program was new in - Netpbm 9.3 (June 2000)). + build: Use when available. - install: fix Perl warning in installnetpbm. Broken in 10.61. + build: fix problem with creating lib/util that already exists. + Broken in Netpbm 10.62. - pnmpsnr: fix bug: says types aren't the same when they are. - Introduced in 10.61 (December 2012). - -13.03.28 BJH Release 10.62.00 +12.03.28 BJH Release 10.62.00 pnmtorast: set don't care bytes to zero to make output repeatable. @@ -90,16 +99,16 @@ CHANGE HISTORY rasttopnm: add -dumpheader, dumpcolormap. pamstereogram: change -guidesize default from 10 to 20 - (relevant since 10.61). + (relevant since Netpbm 10.61). rasttopnm: fix bug: incorrect output due to used-before-set - variable. Introduced in 10.56 (September 2011). + variable. Introduced in Netpbm 10.56 (September 2011). pamstereogram: fix bug: doesn't reject negative guidesize. - Broken since 10.61. + Broken since Netpbm 10.61. pamstereogram: fix bug: garbage in -verbose listing. Broken - since 10.61 + since Netpbm 10.61 MinGW build: various fixes. @@ -127,7 +136,7 @@ CHANGE HISTORY libpam, pamcomp: fix bug: treats tuple type GRAYSCALE_ALPHA like GRAYSCALE on 32-bit machine. Actually, looks only at first 4 characters (8 characters on machine with 64 bit addressess) of - the tuple type. Broken since 10.56 (September 2011). + the tuple type. Broken since Netpbm 10.56 (September 2011). pngtopam -alphapam with grayscale input: fix bug: generates invalid output: tuple type GRAYSCALE_ALPHA, but depth 1. Depth @@ -135,7 +144,7 @@ CHANGE HISTORY Netpbm 10.44 (September 2008)). pamtotiff: fix bug: XRESOLUTION, YRESOLUTION, and RESOLUTIONUNIT - not allowed in -tags. Broken at least since 10.35. + not allowed in -tags. Broken at least since Netpbm 10.35. pnmpsnr: fix crash when invoked (invalidly) with only one argument. Always broken. @@ -146,29 +155,30 @@ CHANGE HISTORY cmuwmtopbm: fix incorrect determination of whether input is a CMU window manager bitmap on 64 bit systems. Always broken. - pnmmontage: fix totally wrong output. Broken in 10.44 + pnmmontage: fix totally wrong output. Broken in Netpbm 10.44 (August 2008). pnmmontage: fix random stuff placed in unoccupied space in the - output image. Always broken (program was new in 9.10 (January - 2001). + output image. Always broken (program was new in Netpbm 9.10 + (January 2001). - pbmpscale, pgmhist, pampick, pamtompfont: fix wild pointer - in command line parsing. Bug has always been there in - pampick, pamtompfont, since 10.50 (March 2010) in pbmpscale, - since 10.44 (September 2008) in pgmhist. + pbmpscale, pgmhist, pampick, pamtompfont: fix wild pointer in + command line parsing. Bug has always been there in pampick, + pamtompfont, since Netpbm 10.50 (March 2010) in pbmpscale, since + 10.44 (September 2008) in pgmhist. xbmtopbm: fix incorrect output, memory leak. Thanks Prophet of the Way . sunicontopnm: Fix incorrect output for depth 8. Always broken - (depth = 8 capability was added in 10.53 (December 2010). + (depth = 8 capability was added in Netpbm 10.53 (December 2010). pamgauss: Fix bug: erroneously says -maxval is too big on 64 bit - system. Always broken (Pamgauss was added in 10.23 (July 2004). + system. Always broken (Pamgauss was added in Netpbm 10.23 (July + 2004). - ppmpat: Fix bug: wrong output with -poles. Broken in 10.47 - (June 2009). + ppmpat: Fix bug: wrong output with -poles. Broken in Netpbm + 10.47 (June 2009). Add tests. Thanks Prophet of the Way . @@ -179,19 +189,20 @@ CHANGE HISTORY linear search or direct index, go row by row. xpmtoppm: fix bogus "color number too large" failure. Broken - in 10.49 (December 2009). + in Netpbm 10.49 (December 2009). pnm_hashtuple: slight performance improvement from new hash function. Thanks Prophet of the Way . ppmtospu: wild memory accesses. Always broken (program added in - 10.58 (March 2012). + Netpbm 10.58 (March 2012). pamtosrf: fix storage corruption. Always broken (program added - in 10.55 (June 2011). + in Netpbm 10.55 (June 2011). - Build: change _XOPEN_SOURCE 500 to 600 because on a Mac OSX 10.8 - system, this is necessary to get 'strdup' into . + Build: change _XOPEN_SOURCE 500 to 600 because on a Mac OSX + Netpbm 10.8 system, this is necessary to get 'strdup' into + . Build: rename getline() in xpmtoppm.c to avoid collision with some libc. Always broken. @@ -214,20 +225,23 @@ CHANGE HISTORY pamstereogram: improve verbose output. Thanks Scott Pakin (scott@pakin.org). - pamstereogram: fix crash introduced in 10.54 (March 2011). + pamstereogram: fix crash introduced in Netpbm 10.54 (March 2011). - pamstereogram: fix crash introduced in 10.53 (December 2010). + pamstereogram: fix crash introduced in Netpbm 10.53 (December + 2010). - Build: fix bug: declines to build pnmtops. Broken in 10.58. + Build: fix bug: declines to build pnmtops. Broken in Netpbm + 10.58. - Build: move -lm to end of -l's. Broken at least since 10.35. + Build: move -lm to end of -l's. Broken at least since Netpbm + 10.35. 12.03.29 BJH Release 10.58.00 Add ppmtospu, 22 years late. pngtopam: fix incorrect output when PNG has 16 bits per pixel. - Broken in 10.56. + Broken in Netpbm 10.56. pgmtexture: fix integer overflow in difference variance. Always broken. @@ -244,7 +258,7 @@ CHANGE HISTORY to Perl. Windows/Mingw: fix bug: temporary file creation fails - consistently. Broken in 10.34. + consistently. Broken in Netpbm 10.34. Windows/Mingw: set binary mode on files. @@ -272,7 +286,8 @@ CHANGE HISTORY pngtopam: fix crash with invalid tIME chunk. Always broken. - pamarith: fix wrong result with -multiply. Broken in 10.41. + pamarith: fix wrong result with -multiply. Broken in Neptbm + 10.41. pamscale: fix all black output with resampling. Always broken. @@ -306,17 +321,17 @@ CHANGE HISTORY libnetpbm, many programs: fix bug in pm_allocarray(): returns NULL when can't get memory. Calling programs thus crash on - very large images. Introduced in 10.51. + very large images. Introduced in Netpbm 10.51. pnmtopng: fix bug: with -alpha specifying a mask which contains no fully transparent area, output PNG is fully opaque. - Introduced in 10.29. + Introduced in Netpbm 10.29. pnmtops: fix message: says "from top edge" where it means "from bottom edge." ppmcie: fix bug: fails with "X coordinate out of range" error. - Introduced in 10.51. + Introduced in Neptbm 10.51. bmptopnm: Fail properly with BMP that indicates an illegal bits per pixel. @@ -336,18 +351,18 @@ CHANGE HISTORY pgmtexture: fix wrong sum variance result. Wrong since the beginning. Thanks Francois P. S. Luus . - pamrubber: fix crash. Introduced in 10.54. + pamrubber: fix crash. Introduced in Netpbm 10.54. libnetpbm: pm_system(): fix bug - program always takes Standard Input from invoker's Standard Input when you don't - supply a Standard Output accepter. Introduced in 10.40. + supply a Standard Output accepter. Introduced in Netpbm 10.40. ppmtobmp: fix bug: crash ("internal error") or bogus failure or incorrect output on image without palette (e.g. black and - white). Introduced in 10.54. + white). Introduced in Netpbm 10.54. pnmtops: fix floating point exception or bogus width/height - too large error. Introduced in 10.53. + too large error. Introduced in Netpbm 10.53. pnmcat: Fix garbage output when multiple input image are from Standard Input (now it just fails gracefully). @@ -360,12 +375,12 @@ CHANGE HISTORY structure. Build: fix undefined "strsol" and "vsnprintfN" failure on - systems without vasprintf(). Introduced in 10.53. + systems without vasprintf(). Introduced in Netpbm 10.53. Build: fix compiler warning in pbmtocmuwm. configure: fix selection of mingw compiler. Introduced in - 10.48. + Netpbm 10.48. 11.03.30 BJH Release 10.54.00 @@ -386,7 +401,7 @@ CHANGE HISTORY Add libpamd (PAM version of classic libppmd drawing routines). Rename pbmtoicon to pbmtosunicon, to go with change - of icontopbm to sunicontopnm in 10.53. + of icontopbm to sunicontopnm in Netpbm 10.53. g3topbm: correct error message: codes go up to 13 bits. @@ -420,11 +435,11 @@ CHANGE HISTORY the BMP header of a compressed file. Always broken. bmptopnm: don't crash on large invalid value of 'colorsused' in - the BMP header. Introduced in 9.11. + the BMP header. Introduced in Netpbm 9.11. pm_mallocarray2: fix wild pointers when image is too big to fit in a single chunk of malloc memory. Affects many Netpbm - programs. Introduced in 10.51.00. + programs. Introduced in Netpbm 10.51.00. ilbmtoppm: Don't crash on image that has a transparent color index, but no color map. @@ -455,7 +470,7 @@ CHANGE HISTORY fitstopnm: Deal properly with NaN in input image. pm_floatFromBigendFloat, pm_doubleFromBigendFloat, fitstopnm: - fix corrupted output. Broken in 10.46. + fix corrupted output. Broken in Netpbm 10.46. pamtopdbimg: fix corrupted output image. Thanks Scott Pakin. @@ -497,10 +512,10 @@ CHANGE HISTORY bits). pnmconvol: fix reversed sense of -nooffset. Introduced in - 10.49. + Netpbm 10.49. ppmtompeg: fix crash with free of unallocated memory. - Broken after 10.18, not later than 10.26. + Broken after Netpbm 10.18, not later than 10.26. Build: fix parallel make - multiple simultaneous attempts to build lib/util/importinc. @@ -539,19 +554,19 @@ CHANGE HISTORY Prophet of the Way . pbmtext/libpbmfont: Fix wild pointer; probably asymptomatic. - Introduced in 10.39. + Introduced in Netpbm 10.39. pbmtext/libpbmfont: Fix some error messages for bad fonts. pbmtext/libpbmfont: fix crash with a BDF font with negative - left or bottom border. Introduced in 10.39. + left or bottom border. Introduced in Netpbm 10.39. - pamarith: fix memory leak. Introduced in 10.41. + pamarith: fix memory leak. Introduced in Netpbm 10.41. pnm_bytespersample(): fix bogus assertion on 64 bit system. pnmtops: fix bug: 12 bits per sample output when 8 would do. - Introduced in 10.40. + Introduced in Netpbm 10.40. palmtopnm: fix for pixel size 16. Thanks Paul Bolle . @@ -567,17 +582,18 @@ CHANGE HISTORY . pnmsmooth: fix wild pointer: causes wrong arguments to - 'pnmconvol'. Introduced in 10.50. Thanks Prophet of the Way - . + 'pnmconvol'. Introduced in Netpbm 10.50. Thanks Prophet of the + Way . - pamscale: fix -reduce. Introduced in 10.27. + pamscale: fix -reduce. Introduced in Netpbm 10.27. pampaintspill: fix incorrect output. libnetpbm text drawing: fix incorrect output in all cases. - Introduced in 10.47. + Introduced in Netpbm 10.47. - libnetpbm line drawing: fix bogus assertion, introduced in 10.47. + libnetpbm line drawing: fix bogus assertion, introduced in + Netpbm 10.47. build: fix incorrect determination of when vasprintf() exists in libc. @@ -635,14 +651,14 @@ CHANGE HISTORY pnmhistmap: Fix crash with -width. Always broken. libppmd/ppmpat: fix wild pointer in ppmd_fill_drawprocp(); - broken in 10.47. + broken in Netpbm 10.47. palmtopnm: fix incorrect "PALM_DIRECT_COLOR_FLAG is not valid for version 3 encoding type" failure. Thanks Paul Bolle . pamtosvg: fix bug: occasional crash with out of range error. - Introduced in 10.42. + Introduced in Netpbm 10.42. palmtopnm: fix incorrect output with version 3 direct color. Thanks Paul Bolle . @@ -695,7 +711,7 @@ CHANGE HISTORY pnmtofiasco, fiascotopnm: fix bug on bigendian machine. pngtopam: use png_create_read_struct() instead of - png_create_write_struct(). Broken since 10.48. + png_create_write_struct(). Broken since Netpbm 10.48. configure: advise if adding -lz -lm fixes libpng link problem. @@ -786,7 +802,7 @@ CHANGE HISTORY problems. ilbmtoppm: fix some bug in interpreting ILBM input. - (due to wrong pm_uintFromBigend16()). From 10.46. + (due to wrong pm_uintFromBigend16()). From Netpbm 10.46. ximtoppm: fix crash in command line processing. @@ -797,7 +813,7 @@ CHANGE HISTORY ppmquantall: don't use 'set' to set Bourne shell variable. pnmtile: fix reference to arbitrary storage in option - processing. Introduced in 10.42. + processing. Introduced in Netpbm 10.42. pamstereogram: fix tuple type in output file (and crash with -verbose) with -patfile . @@ -837,23 +853,23 @@ CHANGE HISTORY the background color (i.e. the color of added margins). pnmscale: finally make it just an alias of pamscale, which - obsoleted it in 10.20. + obsoleted it in Netpbm 10.20. pnmcut: finally make it just an alias of pamcut, which - obsoleted it in 9.20. + obsoleted it in Netpbm 9.20. tifftopnm: fix orientation problem on big-endian machines. - Introduced in 10.42. + Introduced in Netpbm 10.42. pnmcrop: various fixes. - g3topbm: fix array bound violation. Introduced in 10.32. + g3topbm: fix array bound violation. Introduced in Netpbm 10.32. pnmcat: fix array bound violation with PBM top/bottom - concatenation. Introduced in 10.44. + concatenation. Introduced in Netpbm 10.44. ilbmtoppm: Fix array bound violation with compressed ILBM. - Introduced in 10.18. + Introduced in Netpbm 10.18. fitstopnm: fix garbage output when input is little endian floating point FITS. @@ -864,7 +880,7 @@ CHANGE HISTORY picttoppm: Improve error/informational messages. picttoppm: Don't fail if 'fontdir' file doesn't exist. Bug - from 10.44. + from Netpbm 10.44. ppmtopict: Use two-byte length field when image width > 200 instead of > 250. Former is what Picttoppm has been assuming @@ -873,11 +889,11 @@ CHANGE HISTORY ilbmtoppm: fix bug: appends color map PPM to output if input has color map; fails if input doesn't have color map. Broken - in 10.18. + in Netpbm 10.18. leaftoppm: fix bug: uses red channel as all three channels; - (produces grayscale output). Broken between 10.19 and 10.26, - inclusive. + (produces grayscale output). Broken between Netpbm 10.19 and + 10.26, inclusive. pbmtomrf, mrftopbm: fix crashes, incorrect output in all cases. Broken forever. @@ -918,7 +934,7 @@ CHANGE HISTORY Build: Move LDFLAGS later so that a -L in LDFLAGS doesn't interfere with finding the built libnetpbm. (The common - link rule is already this way in 10.44.00; all the other + link rule is already this way in Netpbm 10.44.00; all the other link rules are now the same). Build: Rename Makefile.common, Makefile.config, to common.mk, @@ -964,13 +980,13 @@ CHANGE HISTORY pamtosvg: remove "needed exchange" debug trace. - Add pbmminkowski (source code has been in package since 10.12 - but not documented or built by default). + Add pbmminkowski (source code has been in package since Netpbm + 10.12 but not documented or built by default). pnmmontage: don't corrupt file names when they contain colons. - pamflip: fix bug: garbage output for PBMs, since 10.42. + pamflip: fix bug: garbage output for PBMs, since Netpbm 10.42. pngtopnm: fix gamma correction. @@ -1013,7 +1029,7 @@ CHANGE HISTORY pamtohtmltbl: fix output error: contains holes due to uninitialized memory. - xvminitoppm: fix. Totally broken since 10.34. + xvminitoppm: fix. Totally broken since Netpbm 10.34. pnmtopclxl: fix crash with Standard Input input. @@ -1029,7 +1045,7 @@ CHANGE HISTORY height, width. Add back ppmd_fill_init() for backward compatibility; - removed in 10.29. + removed in Netpbm 10.29. Build: make it work with Gcc 4.3 and -sse. diff --git a/doc/INSTALL b/doc/INSTALL index 474b61cf..f8397ca2 100644 --- a/doc/INSTALL +++ b/doc/INSTALL @@ -52,6 +52,29 @@ and it will attempt to build whatever it hasn't successfully built yet. +CHECKING THE BUILD +------------------ + +The package comes with a test facility, which you can run after packaging, +against the package you created. The typical sequence is + + make + + make package + + make check + + ./installnetpbm + +To capture all the messages from "make check" do: + + make check > test.log 2>&1 + +The test facility was new in Netpbm 10.61 (December 2012). + +For further information on the tests read the document TESTS. + + AUTOMATING THE BUILD -------------------- diff --git a/doc/TESTS b/doc/TESTS new file mode 100644 index 00000000..3196a8fb --- /dev/null +++ b/doc/TESTS @@ -0,0 +1,302 @@ +Contents +======== + +1. Running the tests + 1.1 Standard test procedure + 1.2 Summary report + 1.3 Prerequisites + 1.4 Repeatability + 1.5 Execution time + 1.6 Testing package in designated directory + 1.7 Post-install check + 1.8 Skipping test items + 1.9 Valgrind + +2. Troubleshooting + 2.1 Missing programs + 2.2 Broken programs + 2.3 Color name file + 2.4 Multiple versions + 2.5 System dependent issues + +3. Reporting test failures + +------------------------------------------------------------------------------ + +1. Running the tests +==================== + +1.1 Standard test procedure +=========================== + +The recommended method of running the tests is after packaging: + + make + make package + make check + +To capture the output do: + + make check 2>&1 | less + +Or: + + make check > check.log 2>&1 + + + +1.2 Summary report +================== + +Like most other test routines, these produce much output. A summary will +appear at the end: + + Test summary: + ================== + SUCCESS 83 + FAILURE 1 + TOTAL SUPPORTED 84 + ================== + All tests done. + Sat, 08 Jun 2013 09:30:33 +0000 + make: *** [check] Error 1 + + + +1.3 Prerequisites +================= + +The tests require the Bash command shell. The script Execute-Tests has some +expressions unique to bash. Quite old versions work, at least back to bash +v. 2.05b. + +The tests also use the following utilities: + + - awk + - cat + - cmp + - egrep + - head + - mktemp + - sed + - seq + - tee + + + +1.4 Repeatability +================= + +The current version of the test package produces identical results if you run +"make check" repeatedly. The tests contain no random elements; some +Netpbm programs use randomness internally, but the tests seed their random +number generators with fixed values so that they too have repeatable results. + + + +1.5 Execution time +================== + +Currently "make check" takes no more time to execute than "make package", +and much less than "make". + + + +1.6 Testing package in designated directory +=========================================== + +If you specify the package directory when you do "make package", +you must do the same with "make check": + + make + make package pkgdir=/tmp/package + make check pkgdir=/tmp/package + + + +1.7 Post-install check +====================== + +You can run the tests after installation as well as from the package +directory. Just set $PBM_TESTPREFIX to the bin directory. For example, if +the Netpbm executables are in /usr/local/bin/ do + + PBM_TESTPREFIX=/usr/local/bin make check + +If you want to test the executables in the default search path do + + PBM_TESTPREFIX="" make check + +If the color dictionary file rgb.txt is in a non-standard location, you must +set RGBDEF. If you don't, the tests will report several false positives. + + PBM_TESTPREFIX=/usr/local/bin RGBDEF=/etc/colors/rgb.txt make check + + + +1.8 Skipping test items +======================= + +The file test/Test-Order is a list of tests which are run. If you want to +skip any test, remove the line or comment it out with a "#". + + + +1.9 Valgrind +============ + +You can run the whole test under Valgrind. This is an advanced feature +intended for programmers who work on Netpbm code. + +To turn on Valgrind, uncomment this line in test/Execute: + +# export PBM_TESTPREFIX="valgrind --log-fd=4 "${PBM_TESTPREFIX} + + make check 4> valgrind.log + +Note that Valgrind output is directed to file descriptor 4. + +Valgrind version 3.5.0 or newer is recommended. Older versions do +not report the command that is being executed. + +You can add any options for Valgrind should by editing the above mentioned +line. To run "valgrind --track-origins=yes", you must make two changes in +config.mk: + + - Add -g to CFLAGS. + - Turn stripping off. + +Valgrind significantly increases execution time. You should consider paring +down the items in Test-Order. You probably don't need to run +"all-in-place.test", which has restrictions in the way errors are reported +when PBM_TESTPREFIX is not a simple directory path. Another test +"pamslice-roundtrip.test" is time-consuming, you may want to skip this one +too. + + + +2. Troubleshooting +================== + +2.1 Missing programs +==================== + +The first test run, "all-in-place.test" detects missing programs. + +If you work around a build glitch with "make --keep-going" you +will get a few errors here. + +A wholesale failure with "all-in-place.test" indicates a systemic +problem, such as a misconfigured dynamic library or a directory-wide +permission issue. This kind of failure is known to happen when N is +set too high with "make -jN" (overambitious parallel make.) + +The current test routines assume a typical build configuration - they are not +aware of the actual configuration you chose. If a choice you make during +configure causes "make" to skip compilation of certain programs, the test +routines won't know and will report failures. + +For details read 'Netpbm Library Prerequisites': +http://netpbm.sourceforge.net/prereq.html . + + + +2.2 Broken programs +=================== + +Broken programs will invariably lead to failures. Certain programs +(for instance, image generators pbmmake pgmmake) are used in numerous +test scripts. Problem in them will lead to multiple failures. + +To aid you in this situation each test routine lists the necessary programs +near the top. + +Each test routine comes in two parts, a ".test" file which is a +shell script and a ".ok" file which denotes its proper output. +When a test fails, a ".out" file will be produced in the +/tmp/netpbm-test/ directory. By comparing ".ok" and ".out" you +can tell exactly what went wrong. Often one does not need to +go this far; the error messages tell enough. + + + +2.3 Color dictionary file +========================= + +If you get the following error message, it indicates a problem with +the color dictionary file rgb.txt. + + ppmmake: can't open color names dictionary file from the path '/usr/share/ + netpbm/rgb.txt:/usr/lib/X11/rgb.txt:/usr/share/X11/rgb.txt:/usr/X11R6/lib/ + X11/rgb.txt' and Environment variable RGBDEF not set. Set RGBDEF to the + pathname of your rgb.txt file or don't use color names. + +This is highly unlikely to occur with "make check" right after packaging, +but may appear after installation. + +To check manually after installation, execute the following and see +whether the proper output or the error message appears: + + ppmmake red 1 1 -plain + +Proper output: + + P3 + 1 1 + 255 + 255 0 0 + +The simple remedy is properly setting the environment value RGBDEF to +the location of rgb.txt. + +If you want to hardcode the path, modify RGB_DB_PATH in pm_config.h +and run "make" again. Note that running "configure" will cause +pm_config.h to be overwritten; changes by editing will be lost. + + + +2.4 Multiple versions +===================== + +If multiple versions of Netpbm executables are installed on your +system, you should do a post-installation check to ensure that +the newly built version is in place and in working condition. + +The test routines can test binaries other than the intended +target, for example pre-compiled binaries distributed in .rpm +or .deb format. If PBM_TESTPREFIX is explicitly set to a directory +that contains programs from such a source, you should expect multiple +failures due to missing programs, missing features, etc. If +PBM_TEXTPREFIX is set to "" (null), the outcome will depend heavily +on what version has precedence in PATH. + +Netpbm distributed with Debian or Ubuntu is called "Netpbm-Free" and +is based on a fork which broke off in 2002. There are many differences. +Many tests will fail. However, the test framework itself is valid for +these distributions. + + + +2.5 System dependent issues +=========================== + +The tests have worked on x86 and x86_64 GNU/Linux systems, with several +versions of GCC and Clang. Reports from users of other systems including Mac +OS, Sun SPARC and BSD and compilers other than GCC are highly welcome. + +Floating point math precision seems to be an issue. Some discrepancies +have been observed between x86 32 bit and 64 bit; the tests are written to +work around them as much as possible. The use of the "--fast-math" +flag by default may also be a factor. + +The current test framework checks whether the random number generator +is the one from glibc and skips certain tests if a different one is +detected. + + +3. Reporting test failures +========================== + +When reporting problems with the tests, please give both +the output of "make check" and the contents of the "netpbm-test" +directory. diff --git a/doc/patent_summary b/doc/patent_summary index 271f227c..324fec0d 100644 --- a/doc/patent_summary +++ b/doc/patent_summary @@ -48,12 +48,27 @@ do their JPEG work via a JPEG library not distributed with Netpbm. Your JPEG-related liability for using Netpbm is limited to your liability for using your JPEG library. +Note that it is possible to use Ppmtompeg without involving JPEG and to +build it without the ability to involve JPEG. + The next best alternative to JPEG is probably PNG and maybe JBIG for bilevel (black and white) images. http://burnalljpegs.org contains information on this issue. +MPEG patents +------------ + +The original University of California distributeion of the Ppmtompeg code +conatins this statement in a README file: + + ... patents are held by several companies on various aspects of the MPEG + video standard. Companies or individuals who want to develop commercial + products that include this code must acquire licenses from these companies. + For information on licensing, see Appendix F in the standard. + + Expired LZW patents ------------------- diff --git a/editor/pamcut.c b/editor/pamcut.c index 068ad377..03573796 100644 --- a/editor/pamcut.c +++ b/editor/pamcut.c @@ -675,7 +675,7 @@ main(int argc, const char *argv[]) { struct cmdlineInfo cmdline; FILE * ifP; - bool eof; + int eof; pm_proginit(&argc, argv); diff --git a/editor/pamscale.c b/editor/pamscale.c index 1b3ab8f0..485ae27f 100644 --- a/editor/pamscale.c +++ b/editor/pamscale.c @@ -1392,7 +1392,7 @@ outputOneResampledRow(const struct pam * const outpamP, -----------------------------------------------------------------------------*/ unsigned int col; - bool haveOpacity; /* There is an opacity plane */ + int haveOpacity; /* There is an opacity plane */ unsigned int opacityPlane; /* Plane number of opacity plane, if any */ pnm_getopacity(outpamP, &haveOpacity, &opacityPlane); @@ -2205,7 +2205,7 @@ main(int argc, const char **argv ) { struct cmdlineInfo cmdline; FILE * ifP; - bool eof; + int eof; pm_proginit(&argc, argv); diff --git a/editor/pamthreshold.c b/editor/pamthreshold.c index 8369602d..8d28bc4a 100644 --- a/editor/pamthreshold.c +++ b/editor/pamthreshold.c @@ -658,7 +658,7 @@ main(int argc, char **argv) { FILE * ifP; struct cmdlineInfo cmdline; struct pam inpam, outpam; - bool eof; /* No more images in input stream */ + int eof; /* No more images in input stream */ pnm_init(&argc, argv); diff --git a/editor/pnmcrop.c b/editor/pnmcrop.c index 042e9e20..c6aabff1 100644 --- a/editor/pnmcrop.c +++ b/editor/pnmcrop.c @@ -870,8 +870,8 @@ main(int argc, const char *argv[]) { */ FILE * bdfP; /* The border file. NULL if none. */ - bool eof; /* no more images in input stream */ - bool beof; /* no more images in borderfile stream */ + int eof; /* no more images in input stream */ + int beof; /* no more images in borderfile stream */ pm_proginit(&argc, argv); diff --git a/editor/pnmflip b/editor/pnmflip index 44d95b45..07d4ddb9 100755 --- a/editor/pnmflip +++ b/editor/pnmflip @@ -1,5 +1,28 @@ -#!/usr/bin/perl -w +#!/bin/sh +############################################################################## +# This is essentially a Perl program. We exec the Perl interpreter specifying +# this same file as the Perl program and use the -x option to cause the Perl +# interpreter to skip down to the Perl code. The reason we do this instead of +# just making /usr/bin/perl the script interpreter (instead of /bin/sh) is +# that the user may have multiple Perl interpreters and the one he wants to +# use is properly located in the PATH. The user's choice of Perl interpreter +# may be crucial, such as when the user also has a PERL5LIB environment +# variable and it selects modules that work with only a certain main +# interpreter program. +# +# An alternative some people use is to have /usr/bin/env as the script +# interpreter. We don't do that because we think the existence and +# compatibility of /bin/sh is more reliable. +# +# Note that we aren't concerned about efficiency because the user who needs +# high efficiency can use directly the programs that this program invokes. +# +############################################################################## + +exec perl -w -x -S -- "$0" "$@" + +#!/usr/bin/perl #============================================================================ # This is a compatibility interface to Pamflip. # diff --git a/editor/pnmnlfilt.c b/editor/pnmnlfilt.c index bde0cd82..f55a67bd 100644 --- a/editor/pnmnlfilt.c +++ b/editor/pnmnlfilt.c @@ -990,7 +990,7 @@ main(int argc, char *argv[]) { FILE * ifP; struct cmdlineInfo cmdline; - bool eof; /* We've hit the end of the input stream */ + int eof; /* We've hit the end of the input stream */ unsigned int imageSeq; /* Sequence number of image, starting from 0 */ pnm_init(&argc, argv); diff --git a/editor/pnmquant b/editor/pnmquant index ae461a3d..93d452cd 100755 --- a/editor/pnmquant +++ b/editor/pnmquant @@ -1,5 +1,28 @@ -#!/usr/bin/perl -w +#!/bin/sh +############################################################################## +# This is essentially a Perl program. We exec the Perl interpreter specifying +# this same file as the Perl program and use the -x option to cause the Perl +# interpreter to skip down to the Perl code. The reason we do this instead of +# just making /usr/bin/perl the script interpreter (instead of /bin/sh) is +# that the user may have multiple Perl interpreters and the one he wants to +# use is properly located in the PATH. The user's choice of Perl interpreter +# may be crucial, such as when the user also has a PERL5LIB environment +# variable and it selects modules that work with only a certain main +# interpreter program. +# +# An alternative some people use is to have /usr/bin/env as the script +# interpreter. We don't do that because we think the existence and +# compatibility of /bin/sh is more reliable. +# +# Note that we aren't concerned about efficiency because the user who needs +# high efficiency can use directly the programs that this program invokes. +# +############################################################################## + +exec perl -w -x -S -- "$0" "$@" + +#!/usr/bin/perl ############################################################################## # pnmquant ############################################################################## diff --git a/editor/pnmquantall b/editor/pnmquantall index b7bb03d7..0890383e 100755 --- a/editor/pnmquantall +++ b/editor/pnmquantall @@ -1,3 +1,27 @@ +#!/bin/sh + +############################################################################## +# This is essentially a Perl program. We exec the Perl interpreter specifying +# this same file as the Perl program and use the -x option to cause the Perl +# interpreter to skip down to the Perl code. The reason we do this instead of +# just making /usr/bin/perl the script interpreter (instead of /bin/sh) is +# that the user may have multiple Perl interpreters and the one he wants to +# use is properly located in the PATH. The user's choice of Perl interpreter +# may be crucial, such as when the user also has a PERL5LIB environment +# variable and it selects modules that work with only a certain main +# interpreter program. +# +# An alternative some people use is to have /usr/bin/env as the script +# interpreter. We don't do that because we think the existence and +# compatibility of /bin/sh is more reliable. +# +# Note that we aren't concerned about efficiency because the user who needs +# high efficiency can use directly the programs that this program invokes. +# +############################################################################## + +exec perl -w -x -S -- "$0" "$@" + #!/usr/bin/perl ############################################################################## # pnmquantall diff --git a/editor/pnmremap.c b/editor/pnmremap.c index ec381f32..b2448cbb 100644 --- a/editor/pnmremap.c +++ b/editor/pnmremap.c @@ -749,7 +749,7 @@ lookupThroughHash(struct pam * const pamP, } else searchColormapClose(pamP, tuple, colorFinderP, colormapIndexP); if (*usehashP) { - bool fits; + int fits; pnm_addtotuplehash(pamP, colorhash, tuple, *colormapIndexP, &fits); if (!fits) { @@ -1063,7 +1063,7 @@ remap(FILE * const ifP, same as that of the input even though the individual pixels have different colors. -----------------------------------------------------------------------------*/ - bool eof; + int eof; eof = FALSE; while (!eof) { struct pam inpam, outpam; diff --git a/editor/ppmdraw.c b/editor/ppmdraw.c index ba513581..c733ffcb 100644 --- a/editor/ppmdraw.c +++ b/editor/ppmdraw.c @@ -44,7 +44,7 @@ struct cmdlineInfo { static void -parseCommandLine (int argc, char ** argv, +parseCommandLine (int argc, const char ** argv, struct cmdlineInfo * const cmdlineP) { /*---------------------------------------------------------------------------- parse program command line described in Unix standard form by argc @@ -80,7 +80,7 @@ parseCommandLine (int argc, char ** argv, opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ opt.allowNegNum = FALSE; /* We have no parms that are negative numbers */ - pm_optParseOptions3( &argc, argv, opt, sizeof(opt), 0); + pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0); /* Uses and sets argc, argv, and some of *cmdlineP and others. */ if (!scriptSpec && !scriptfileSpec) @@ -899,14 +899,14 @@ doOneImage(FILE * const ifP, int -main(int argc, char * argv[]) { +main(int argc, const char * argv[]) { struct cmdlineInfo cmdline; FILE * ifP; struct script * scriptP; - bool eof; + int eof; - ppm_init(&argc, argv); + pm_proginit(&argc, argv); parseCommandLine(argc, argv, &cmdline); diff --git a/editor/ppmfade b/editor/ppmfade index fbc62968..027fc793 100755 --- a/editor/ppmfade +++ b/editor/ppmfade @@ -1,5 +1,31 @@ -#!/usr/bin/perl -w -#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +#!/bin/sh + +############################################################################## +# This is essentially a Perl program. We exec the Perl interpreter specifying +# this same file as the Perl program and use the -x option to cause the Perl +# interpreter to skip down to the Perl code. The reason we do this instead of +# just making /usr/bin/perl the script interpreter (instead of /bin/sh) is +# that the user may have multiple Perl interpreters and the one he wants to +# use is properly located in the PATH. The user's choice of Perl interpreter +# may be crucial, such as when the user also has a PERL5LIB environment +# variable and it selects modules that work with only a certain main +# interpreter program. +# +# An alternative some people use is to have /usr/bin/env as the script +# interpreter. We don't do that because we think the existence and +# compatibility of /bin/sh is more reliable. +# +# Note that we aren't concerned about efficiency because the user who needs +# high efficiency can use directly the programs that this program invokes. +# +############################################################################## + +exec perl -w -x -S -- "$0" "$@" + +#!/usr/bin/perl +############################################################################## +# ppmfade +############################################################################## # # This program creates a fade (a sequence of frames) between two images. # @@ -12,7 +38,7 @@ # much the same thing, but handles non-Netpbm formats too, and is # implemented in a more primitive language. # -#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +############################################################################## use strict; my $SPREAD = 1; diff --git a/editor/ppmquant b/editor/ppmquant index 08f4c187..57963982 100755 --- a/editor/ppmquant +++ b/editor/ppmquant @@ -1,4 +1,28 @@ -#!/usr/bin/perl -w +#!/bin/sh + +############################################################################## +# This is essentially a Perl program. We exec the Perl interpreter specifying +# this same file as the Perl program and use the -x option to cause the Perl +# interpreter to skip down to the Perl code. The reason we do this instead of +# just making /usr/bin/perl the script interpreter (instead of /bin/sh) is +# that the user may have multiple Perl interpreters and the one he wants to +# use is properly located in the PATH. The user's choice of Perl interpreter +# may be crucial, such as when the user also has a PERL5LIB environment +# variable and it selects modules that work with only a certain main +# interpreter program. +# +# An alternative some people use is to have /usr/bin/env as the script +# interpreter. We don't do that because we think the existence and +# compatibility of /bin/sh is more reliable. +# +# Note that we aren't concerned about efficiency because the user who needs +# high efficiency can use directly the programs that this program invokes. +# +############################################################################## + +exec perl -w -x -S -- "$0" "$@" + +#!/usr/bin/perl ############################################################################## # This is nothing but a compatibility interface for Pnmquant. # An old program coded to call Ppmquant will continue working because diff --git a/editor/ppmshadow b/editor/ppmshadow index 2a32fca0..62cdf8b8 100755 --- a/editor/ppmshadow +++ b/editor/ppmshadow @@ -1,15 +1,40 @@ -#!/usr/bin/perl -w +#!/bin/sh -# P P M S H A D O W +############################################################################## +# This is essentially a Perl program. We exec the Perl interpreter specifying +# this same file as the Perl program and use the -x option to cause the Perl +# interpreter to skip down to the Perl code. The reason we do this instead of +# just making /usr/bin/perl the script interpreter (instead of /bin/sh) is +# that the user may have multiple Perl interpreters and the one he wants to +# use is properly located in the PATH. The user's choice of Perl interpreter +# may be crucial, such as when the user also has a PERL5LIB environment +# variable and it selects modules that work with only a certain main +# interpreter program. +# +# An alternative some people use is to have /usr/bin/env as the script +# interpreter. We don't do that because we think the existence and +# compatibility of /bin/sh is more reliable. +# +# Note that we aren't concerned about efficiency because the user who needs +# high efficiency can use directly the programs that this program invokes. +# +############################################################################## + +exec perl -w -x -S -- "$0" "$@" +#!/usr/bin/perl +############################################################################## +# ppmshadow +############################################################################## +# # by John Walker -- http://www.fourmilab.ch/ # version = 1.2; # --> with minor changes by Bryan Henderson to adapt to Netbpm. # See above web site for the real John Walker work, named pnmshadow. - +# # Bryan Henderson later made some major style changes (use strict, etc) and # eliminated most use of shells. See Netbpm HISTORY file. - +# # Pnmshadow is a brutal sledgehammer implemented in Perl which # adds attractive shadows to images, as often seen in titles # of World-Wide Web pages. This program does not actually @@ -20,7 +45,7 @@ # # This program is in the public domain. # -# +############################################################################## use strict; require 5.0; diff --git a/editor/specialty/ppmntsc.c b/editor/specialty/ppmntsc.c index 2585662f..a721b891 100644 --- a/editor/specialty/ppmntsc.c +++ b/editor/specialty/ppmntsc.c @@ -51,9 +51,6 @@ #include "mallocvar.h" #include "shhopt.h" -#define TRUE 1 -#define FALSE 0 - enum legalize {RAISE_SAT, LOWER_SAT, ALREADY_LEGAL}; /* The actions that make a legal pixel */ @@ -70,6 +67,61 @@ struct cmdlineInfo { +static void +parseCommandLine(int argc, const char ** argv, + struct cmdlineInfo * const cmdlineP) { +/*---------------------------------------------------------------------------- + Note that many of the strings that this function returns in the + *cmdlineP structure are actually in the supplied argv array. And + sometimes, one of these strings is actually just a suffix of an entry + in argv! +-----------------------------------------------------------------------------*/ + optStruct3 opt; + optEntry *option_def; + /* Instructions to OptParseOptions on how to parse our options. + */ + unsigned int option_def_index; + unsigned int legalonly, illegalonly, correctedonly; + + MALLOCARRAY(option_def, 100); + + option_def_index = 0; /* incremented by OPTENTRY */ + OPTENT3('v', "verbose", OPT_FLAG, NULL, &cmdlineP->verbose, 0); + OPTENT3('V', "debug", OPT_FLAG, NULL, &cmdlineP->debug, 0); + OPTENT3('p', "pal", OPT_FLAG, NULL, &cmdlineP->pal, 0); + OPTENT3('l', "legalonly", OPT_FLAG, NULL, &legalonly, 0); + OPTENT3('i', "illegalonly", OPT_FLAG, NULL, &illegalonly, 0); + OPTENT3('c', "correctedonly", OPT_FLAG, NULL, &correctedonly, 0); + + opt.opt_table = option_def; + opt.short_allowed = true; + opt.allowNegNum = false; + + pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0); + + if (argc - 1 == 0) + cmdlineP->inputFilename = "-"; /* he wants stdin */ + else if (argc - 1 == 1) + cmdlineP->inputFilename = argv[1]; + else + pm_error("Too many arguments. The only arguments accepted " + "are the mask color and optional input file specification"); + + if (legalonly + illegalonly + correctedonly > 1) + pm_error("--legalonly, --illegalonly, and --correctedonly are " + "conflicting options. Specify at most one of these."); + + if (legalonly) + cmdlineP->output = LEGAL_ONLY; + else if (illegalonly) + cmdlineP->output = ILLEGAL_ONLY; + else if (correctedonly) + cmdlineP->output = CORRECTED_ONLY; + else + cmdlineP->output = ALL; +} + + static void rgbtoyiq(const int r, const int g, const int b, @@ -118,39 +170,39 @@ yuvtorgb(const double y, const double u, const double v, static void -make_legal_yiq(const double y, const double i, const double q, - double * const y_new_p, - double * const i_new_p, - double * const q_new_p, - enum legalize * const action_p - ) { +makeLegalYiq(double const y, + double const i, + double const q, + double * const yNewP, + double * const iNewP, + double * const qNewP, + enum legalize * const actionP) { - double sat_old, sat_new; + double satOld, satNew; /* * I and Q are legs of a right triangle. Saturation is the hypotenuse. */ - sat_old = sqrt(i*i + q*q); - if (y+sat_old > 1.0) { - const double diff = 0.5*((y+sat_old) - 1.0); - *y_new_p = y - diff; - sat_new = 1.0 - *y_new_p; - *i_new_p = i*(sat_new/sat_old); - *q_new_p = q*(sat_new/sat_old); - *action_p = LOWER_SAT; - } else if (y-sat_old <= -0.251) { - const double diff = 0.5*((sat_old-y) - 0.251); - *y_new_p = y + diff; - sat_new = 0.250 + *y_new_p; - *i_new_p = i*(sat_new/sat_old); - *q_new_p = q*(sat_new/sat_old); - *action_p = RAISE_SAT; + satOld = sqrt(SQR(i) + SQR(q)); + if (y+satOld > 1.0) { + const double diff = 0.5*((y + satOld) - 1.0); + *yNewP = y - diff; + satNew = 1.0 - *yNewP; + *iNewP = i * (satNew/satOld); + *qNewP = q * (satNew/satOld); + *actionP = LOWER_SAT; + } else if (y - satOld <= -0.251) { + const double diff = 0.5*((satOld - y) - 0.251); + *yNewP = y + diff; + satNew = 0.250 + *yNewP; + *iNewP = i * (satNew/satOld); + *qNewP = q * (satNew/satOld); + *actionP = RAISE_SAT; } else { - *y_new_p = y; - *i_new_p = i; - *q_new_p = q; - *action_p = ALREADY_LEGAL; + *yNewP = y; + *iNewP = i; + *qNewP = q; + *actionP = ALREADY_LEGAL; } - return; } @@ -206,7 +258,7 @@ make_legal_yiq_i(const int r_in, const int g_in, const int b_in, * Convert to YIQ and compute the new saturation. */ rgbtoyiq(r_in, g_in, b_in, &y, &i, &q); - make_legal_yiq(y, i, q, &y_new, &i_new, &q_new, action_p); + makeLegalYiq(y, i, q, &y_new, &i_new, &q_new, action_p); if (*action_p != ALREADY_LEGAL) /* * Given the new I and Q, compute new RGB values. @@ -295,204 +347,155 @@ make_legal_yuv_b(const pixel input, static void -report_mapping(const pixel old_pixel, const pixel new_pixel) { +reportMapping(pixel const oldPixel, + pixel const newPixel) { /*---------------------------------------------------------------------------- - Assuming old_pixel and new_pixel are input and output pixels, + Assuming oldPixel and newPixel are input and output pixels, tell the user that we changed a pixel to make it legal, if in fact we did and it isn't the same change that we just reported. -----------------------------------------------------------------------------*/ - static pixel last_changed_pixel; - static int first_time = TRUE; - - if (!PPM_EQUAL(old_pixel, new_pixel) && - (first_time || PPM_EQUAL(old_pixel, last_changed_pixel))) { - pm_message("Mapping %d %d %d -> %d %d %d\n", - PPM_GETR(old_pixel), - PPM_GETG(old_pixel), - PPM_GETB(old_pixel), - PPM_GETR(new_pixel), - PPM_GETG(new_pixel), - PPM_GETB(new_pixel) + static pixel lastChangedPixel; + static bool firstTime = true; + + if (!PPM_EQUAL(oldPixel, newPixel) && + (firstTime || PPM_EQUAL(oldPixel, lastChangedPixel))) { + pm_message("Mapping %u %u %u -> %u %u %u\n", + PPM_GETR(oldPixel), + PPM_GETG(oldPixel), + PPM_GETB(oldPixel), + PPM_GETR(newPixel), + PPM_GETG(newPixel), + PPM_GETB(newPixel) ); - last_changed_pixel = old_pixel; - first_time = FALSE; + lastChangedPixel = oldPixel; + firstTime = false; } } static void -convert_one_image(FILE * const ifp, struct cmdlineInfo const cmdline, - bool * const eofP, - int * const hicountP, int * const locountP) { +convertOneImage(FILE * const ifP, + struct cmdlineInfo const cmdline, + unsigned int * const hiCountP, + unsigned int * const loCountP) { /* Parameters of input image: */ int rows, cols; pixval maxval; int format; - ppm_readppminit(ifp, &cols, &rows, &maxval, &format); - ppm_writeppminit(stdout, cols, rows, maxval, FALSE); + ppm_readppminit(ifP, &cols, &rows, &maxval, &format); + ppm_writeppminit(stdout, cols, rows, maxval, 0); { - pixel* const input_row = ppm_allocrow(cols); - pixel* const output_row = ppm_allocrow(cols); - pixel last_illegal_pixel; - /* Value of the illegal pixel we most recently processed */ + pixel * const inputRow = ppm_allocrow(cols); + pixel * const outputRow = ppm_allocrow(cols); + + pixel lastIllegalPixel; + /* Value of the illegal pixel we most recently processed */ pixel black; - /* A constant - black pixel */ + /* A constant - black pixel */ PPM_ASSIGN(black, 0, 0, 0); - PPM_ASSIGN(last_illegal_pixel, 0, 0, 0); /* initial value */ + PPM_ASSIGN(lastIllegalPixel, 0, 0, 0); /* initial value */ { - int row; + unsigned int row; - *hicountP = 0; *locountP = 0; /* initial values */ + *hiCountP = 0; *loCountP = 0; /* initial values */ for (row = 0; row < rows; ++row) { - int col; - ppm_readppmrow(ifp, input_row, cols, maxval, format); + unsigned int col; + ppm_readppmrow(ifP, inputRow, cols, maxval, format); for (col = 0; col < cols; ++col) { pixel corrected; - /* Corrected or would-be corrected value for pixel */ + /* Corrected or would-be corrected value for pixel */ enum legalize action; - /* What action was used to make pixel legal */ + /* What action was used to make pixel legal */ if (cmdline.pal) - make_legal_yuv_b(input_row[col], + make_legal_yuv_b(inputRow[col], &corrected, &action); else - make_legal_yiq_b(input_row[col], + make_legal_yiq_b(inputRow[col], &corrected, &action); if (action == LOWER_SAT) - (*hicountP)++; + ++*hiCountP; if (action == RAISE_SAT) - (*locountP)++; - if (cmdline.debug) report_mapping(input_row[col], - corrected); + ++*loCountP; + if (cmdline.debug) + reportMapping(inputRow[col], corrected); switch (cmdline.output) { case ALL: - output_row[col] = corrected; + outputRow[col] = corrected; break; case LEGAL_ONLY: - output_row[col] = (action == ALREADY_LEGAL) ? - input_row[col] : black; + outputRow[col] = (action == ALREADY_LEGAL) ? + inputRow[col] : black; break; case ILLEGAL_ONLY: - output_row[col] = (action != ALREADY_LEGAL) ? - input_row[col] : black; + outputRow[col] = (action != ALREADY_LEGAL) ? + inputRow[col] : black; break; case CORRECTED_ONLY: - output_row[col] = (action != ALREADY_LEGAL) ? + outputRow[col] = (action != ALREADY_LEGAL) ? corrected : black; break; } } - ppm_writeppmrow(stdout, output_row, cols, maxval, FALSE); + ppm_writeppmrow(stdout, outputRow, cols, maxval, 0); } } - ppm_freerow(output_row); - ppm_freerow(input_row); + ppm_freerow(outputRow); + ppm_freerow(inputRow); } } -static void -parseCommandLine(int argc, char ** argv, - struct cmdlineInfo * const cmdlineP) { -/*---------------------------------------------------------------------------- - Note that many of the strings that this function returns in the - *cmdlineP structure are actually in the supplied argv array. And - sometimes, one of these strings is actually just a suffix of an entry - in argv! ------------------------------------------------------------------------------*/ - optStruct3 opt; - optEntry *option_def; - /* Instructions to OptParseOptions on how to parse our options. - */ - unsigned int option_def_index; - unsigned int legalonly, illegalonly, correctedonly; - - MALLOCARRAY(option_def, 100); - - option_def_index = 0; /* incremented by OPTENTRY */ - OPTENT3('v', "verbose", OPT_FLAG, NULL, &cmdlineP->verbose, 0); - OPTENT3('V', "debug", OPT_FLAG, NULL, &cmdlineP->debug, 0); - OPTENT3('p', "pal", OPT_FLAG, NULL, &cmdlineP->pal, 0); - OPTENT3('l', "legalonly", OPT_FLAG, NULL, &legalonly, 0); - OPTENT3('i', "illegalonly", OPT_FLAG, NULL, &illegalonly, 0); - OPTENT3('c', "correctedonly", OPT_FLAG, NULL, &correctedonly, 0); - - opt.opt_table = option_def; - opt.short_allowed = TRUE; - opt.allowNegNum = FALSE; - - pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0); - - if (argc - 1 == 0) - cmdlineP->inputFilename = "-"; /* he wants stdin */ - else if (argc - 1 == 1) - cmdlineP->inputFilename = argv[1]; - else - pm_error("Too many arguments. The only arguments accepted " - "are the mask color and optional input file specification"); - - if (legalonly + illegalonly + correctedonly > 1) - pm_error("--legalonly, --illegalonly, and --correctedonly are " - "conflicting options. Specify at most one of these."); - - if (legalonly) - cmdlineP->output = LEGAL_ONLY; - else if (illegalonly) - cmdlineP->output = ILLEGAL_ONLY; - else if (correctedonly) - cmdlineP->output = CORRECTED_ONLY; - else - cmdlineP->output = ALL; -} - - int -main(int argc, char **argv) { +main(int argc, const char **argv) { struct cmdlineInfo cmdline; FILE * ifP; - int total_hicount, total_locount; - int image_count; + unsigned int totalHiCount, totalLoCount; + unsigned int imageCount; - bool eof; + int eof; - ppm_init(&argc, argv); + pm_proginit(&argc, argv); parseCommandLine(argc, argv, &cmdline); ifP = pm_openr(cmdline.inputFilename); + + imageCount = 0; /* initial value */ + totalHiCount = 0; /* initial value */ + totalLoCount = 0; /* initial value */ - image_count = 0; /* initial value */ - total_hicount = 0; /* initial value */ - total_locount = 0; /* initial value */ - - eof = FALSE; + eof = false; while (!eof) { - int hicount, locount; - convert_one_image(ifP, cmdline, &eof, &hicount, &locount); - image_count++; - total_hicount += hicount; - total_locount += locount; + unsigned int hiCount, loCount; + + convertOneImage(ifP, cmdline, &hiCount, &loCount); + + ++imageCount; + totalHiCount += hiCount; + totalLoCount += loCount; + ppm_nextimage(ifP, &eof); } if (cmdline.verbose) { - pm_message("%d images processed.", image_count); - pm_message("%d pixels were above the saturation limit.", - total_hicount); - pm_message("%d pixels were below the saturation limit.", - total_locount); + pm_message("%u images processed.", imageCount); + pm_message("%u pixels were above the saturation limit.", + totalHiCount); + pm_message("%u pixels were below the saturation limit.", + totalLoCount); } pm_close(ifP); diff --git a/generator/pamgauss.c b/generator/pamgauss.c index b6afdb7e..0e1661e6 100644 --- a/generator/pamgauss.c +++ b/generator/pamgauss.c @@ -9,8 +9,6 @@ #include "mallocvar.h" #include "pam.h" -#define true (1) -#define false (0) struct cmdlineInfo { diff --git a/generator/pamgradient.c b/generator/pamgradient.c index 6546e334..57e78288 100644 --- a/generator/pamgradient.c +++ b/generator/pamgradient.c @@ -18,7 +18,7 @@ struct cmdlineInfo { }; static void -parseCommandLine(int argc, char **argv, +parseCommandLine(int argc, const char **argv, struct cmdlineInfo * const cmdlineP) { /*---------------------------------------------------------------------------- Convert program invocation arguments (argc,argv) into a format the @@ -45,7 +45,7 @@ parseCommandLine(int argc, char **argv, opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ opt.allowNegNum = FALSE; /* We have no parms that are negative numbers */ - pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0); + pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0); /* Uses and sets argc, argv, and some of *cmdlineP and others. */ if (!maxvalSpec) @@ -153,7 +153,7 @@ createEdge(const struct pam * const pamP, int -main(int argc, char *argv[]) { +main(int argc, const char *argv[]) { struct cmdlineInfo cmdline; struct pam pam; @@ -162,7 +162,7 @@ main(int argc, char *argv[]) { tuple * rightEdge; unsigned int row; - pnm_init(&argc, argv); + pm_proginit(&argc, argv); parseCommandLine(argc, argv, &cmdline); diff --git a/generator/pamseq.c b/generator/pamseq.c index b1ed0c79..1af5252a 100644 --- a/generator/pamseq.c +++ b/generator/pamseq.c @@ -7,8 +7,6 @@ #include "pam.h" #include "shhopt.h" -#define true (1) -#define false (0) struct cmdlineInfo { diff --git a/generator/pgmnoise.c b/generator/pgmnoise.c index bf73ea48..442edc59 100644 --- a/generator/pgmnoise.c +++ b/generator/pgmnoise.c @@ -7,6 +7,8 @@ #include "mallocvar.h" #include "shhopt.h" #include "pgm.h" +#include + struct cmdlineInfo { @@ -15,13 +17,13 @@ struct cmdlineInfo { */ unsigned int width; unsigned int height; + unsigned int maxval; unsigned int randomseed; unsigned int randomseedSpec; }; - static void parseCommandLine(int argc, const char ** const argv, struct cmdlineInfo * const cmdlineP) { @@ -34,12 +36,15 @@ parseCommandLine(int argc, const char ** const argv, */ optStruct3 opt; unsigned int option_def_index; + unsigned int maxvalSpec; MALLOCARRAY_NOFAIL(option_def, 100); option_def_index = 0; /* incremented by OPTENT3 */ OPTENT3(0, "randomseed", OPT_UINT, &cmdlineP->randomseed, &cmdlineP->randomseedSpec, 0); + OPTENT3(0, "maxval", OPT_UINT, &cmdlineP->maxval, + &maxvalSpec, 0); opt.opt_table = option_def; opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ @@ -47,6 +52,16 @@ parseCommandLine(int argc, const char ** const argv, pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0); /* Uses and sets argc, argv, and some of *cmdlineP and others. */ + free(option_def); + + if (maxvalSpec) { + if (cmdlineP->maxval > PGM_OVERALLMAXVAL) + pm_error("Maxval too large: %u. Maximu is %u", + cmdlineP->maxval, PGM_OVERALLMAXVAL); + else if (cmdlineP->maxval == 0) + pm_error("Maxval must not be zero"); + } else + cmdlineP->maxval = PGM_MAXMAXVAL; if (argc-1 != 2) pm_error("Wrong number of arguments: %u. " @@ -66,30 +81,95 @@ parseCommandLine(int argc, const char ** const argv, else cmdlineP->height = height; } - free(option_def); } +static unsigned int +randPool(unsigned int const digits) { +/*---------------------------------------------------------------------------- + Draw 'digits' bits from pool of random bits. If the number of random bits + in pool is insufficient, call rand() and add 31 bits to it. + + 'digits' must be at most 16. + + We assume that each call to rand() generates 31 bits, or RAND_MAX == + 2147483647. + + The underlying logic is flexible and endian-free. The above conditions + can be relaxed. +-----------------------------------------------------------------------------*/ + static unsigned long int hold=0; /* entropy pool */ + static unsigned int len=0; /* number of valid bits in pool */ + + unsigned int const mask = (1 << digits) - 1; + + unsigned int retval; + + assert(RAND_MAX == 2147483647 && digits <= 16); + + retval = hold; /* initial value */ + + if (len > digits) { /* Enough bits in hold to satisfy request */ + hold >>= digits; + len -= digits; + } else { /* Load another 31 bits into hold */ + hold = rand(); + retval |= (hold << len); + hold >>= (digits - len); + len = 31 - digits + len; + } + return (retval & mask); +} + + static void -pgmnoise(FILE * const ofP, +pgmnoise(FILE * const ofP, unsigned int const cols, unsigned int const rows, gray const maxval) { + bool const usingPool = !(RAND_MAX==2147483647 && (maxval & (maxval+1))); + unsigned int const bitLen = pm_maxvaltobits(maxval); + unsigned int row; gray * destrow; + /* If maxval is 2^n-1, we draw exactly n bits from the pool. + Otherwise call rand() and determine gray value by modulo. + + In the latter case, there is a miniscule skew toward 0 (=black) + because smaller numbers are produced more frequently by modulo. + Thus we employ the pool method only when it is certain that no + skew will ensue. + + To illustrate the point, consider converting the outcome of one + roll of a fair, six-sided die to 5 values (0 to 4) by N % 5. The + probability for values 1, 2, 3, 4 are 1/6, but 0 alone is 2/6. + Average is 10/6 or 1.6667, compared to 2.0 from an ideal + generator which produces exactly 5 values. With two dice + average improves to 70/36 or 1.9444. + + The more (distinct) dice we roll, or the more binary digits we + draw, the smaller the skew. + */ + destrow = pgm_allocrow(cols); pgm_writepgminit(ofP, cols, rows, maxval, 0); for (row = 0; row < rows; ++row) { - unsigned int col; - for (col = 0; col < cols; ++col) - destrow[col] = rand() % (maxval + 1); - + if (usingPool) { + unsigned int col; + for (col = 0; col < cols; ++col) + destrow[col] = randPool(bitLen); + } + else { + unsigned int col; + for (col = 0; col < cols; ++col) + destrow[col] = rand() % (maxval + 1); + } pgm_writepgmrow(ofP, destrow, cols, maxval, 0); } @@ -110,8 +190,7 @@ main(int argc, srand(cmdline.randomseedSpec ? cmdline.randomseed : pm_randseed()); - pgmnoise(stdout, cmdline.width, cmdline.height, PGM_MAXMAXVAL); + pgmnoise(stdout, cmdline.width, cmdline.height, cmdline.maxval); return 0; } - diff --git a/generator/ppmcie.c b/generator/ppmcie.c index 69af5732..717ed13b 100644 --- a/generator/ppmcie.c +++ b/generator/ppmcie.c @@ -35,10 +35,8 @@ #include "nstring.h" #define CLAMP(v, l, h) ((v) < (l) ? (l) : (v) > (h) ? (h) : (v)) -#define TRUE 1 -#define FALSE 0 -#define Maxval 255 /* Maxval to use in generated pixmaps */ +pixval const cieMaxval = 255; /* Maxval to use in generated pixmaps */ /* A color system is defined by the CIE x and y coordinates of its three primary illuminants and the x and y coordinates of the white @@ -522,12 +520,12 @@ makeAllBlack(pixel ** const pixels, static void drawTongueOutline(pixel ** const pixels, - int const pixcols, - int const pixrows, - pixval const maxval, - bool const upvp, - int const xBias, - int const yBias) { + int const pixcols, + int const pixrows, + pixval const maxval, + bool const upvp, + int const xBias, + int const yBias) { int const pxcols = pixcols - xBias; int const pxrows = pixrows - yBias; @@ -546,7 +544,7 @@ drawTongueOutline(pixel ** const pixels, &icx, &icy); if (wavelength > 380) - ppmd_line(pixels, pixcols, pixrows, Maxval, + ppmd_line(pixels, pixcols, pixrows, maxval, B(lx, ly), B(icx, icy), PPMD_NULLDRAWPROC, (char *) &rgbcolor); else { @@ -585,12 +583,12 @@ findTongue(pixel ** const pixels, ++i); if (i >= pxcols) - *presentP = FALSE; + *presentP = false; else { int j; int const leftEdge = i; - *presentP = TRUE; + *presentP = true; for (j = pxcols - 1; j >= leftEdge && PPM_GETR(Bixels(row, j)) == 0; @@ -652,16 +650,16 @@ fillInTongue(pixel ** const pixels, xyz_to_rgb(cs, cx, cy, cz, &jr, &jg, &jb); - mx = Maxval; + mx = maxval; /* Check whether the requested color is within the gamut achievable with the given color system. If not, draw it in a reduced intensity, interpolated by desaturation to the closest within-gamut color. */ - if (constrain_rgb(&jr, &jg, &jb)) { - mx = highlightGamut ? Maxval : ((Maxval + 1) * 3) / 4; - } + if (constrain_rgb(&jr, &jg, &jb)) + mx = highlightGamut ? maxval : ((maxval + 1) * 3) / 4; + /* Scale to max(rgb) = 1. */ jmax = MAX(jr, MAX(jg, jb)); if (jmax > 0) { @@ -959,14 +957,14 @@ plotBlackBodyCurve(pixel ** const pixels, } if (t > 1000) { - ppmd_line(pixels, pixcols, pixrows, Maxval, + ppmd_line(pixels, pixcols, pixrows, maxval, B(lx, ly), B(xb, yb), PPMD_NULLDRAWPROC, (char *) &rgbcolor); /* Draw tick mark every 1000 kelvins */ if ((((int) t) % 1000) == 0) { - ppmd_line(pixels, pixcols, pixrows, Maxval, + ppmd_line(pixels, pixcols, pixrows, maxval, B(lx, ly - Sz(2)), B(lx, ly + Sz(2)), PPMD_NULLDRAWPROC, (char *) &rgbcolor); @@ -978,7 +976,7 @@ plotBlackBodyCurve(pixel ** const pixels, char bb[20]; sprintf(bb, "%g", t); - ppmd_text(pixels, pixcols, pixrows, Maxval, + ppmd_text(pixels, pixcols, pixrows, maxval, B(lx - Sz(12), ly - Sz(4)), Sz(6), 0, bb, PPMD_NULLDRAWPROC, (char *) &rgbcolor); } @@ -1057,7 +1055,7 @@ plotMonochromeWavelengths( PPM_ASSIGN(rgbcolor, maxval, maxval, maxval); tx = icx + ((x < 520) ? Sz(-2) : ((x >= 535) ? Sz(2) : 0)); ty = icy + ((x < 520) ? 0 : ((x >= 535) ? Sz(-1) : Sz(-2))); - ppmd_line(pixels, pixcols, pixrows, Maxval, + ppmd_line(pixels, pixcols, pixrows, maxval, B(icx, icy), B(tx, ty), PPMD_NULLDRAWPROC, (char *) &rgbcolor); @@ -1089,13 +1087,13 @@ plotMonochromeWavelengths( } /* gamma correct from linear rgb to nonlinear rgb. */ gamma_correct_rgb(cs, &jr, &jg, &jb); - r = Maxval * jr; - g = Maxval * jg; - b = Maxval * jb; + r = maxval * jr; + g = maxval * jg; + b = maxval * jb; PPM_ASSIGN(rgbcolor, (pixval) r, (pixval) g, (pixval) b); sprintf(wl, "%d", x); - ppmd_text(pixels, pixcols, pixrows, Maxval, + ppmd_text(pixels, pixcols, pixrows, maxval, B(icx + bx, icy + by), Sz(6), 0, wl, PPMD_NULLDRAWPROC, (char *) &rgbcolor); } @@ -1127,7 +1125,7 @@ writeLabel(pixel ** const pixels, cs->xBlue, cs->yBlue, cs->xWhite, cs->yWhite); sysdesc[sizeof(sysdesc)-1] = '\0'; /* for robustness */ - ppmd_text(pixels, pixcols, pixrows, Maxval, + ppmd_text(pixels, pixcols, pixrows, maxval, pixcols / 3, Sz(24), Sz(12), 0, sysdesc, PPMD_NULLDRAWPROC, (char *) &rgbcolor); } @@ -1135,8 +1133,8 @@ writeLabel(pixel ** const pixels, int -main(int argc, - char * argv[]) { +main(int argc, + const char * argv[]) { int argn; const char * const usage = "[-[no]black] [-[no]wpoint] [-[no]label] [-no[axes]] [-full]\n\ @@ -1146,24 +1144,24 @@ main(int argc, [-size ] [-xsize|-width ] [-ysize|-height ]"; const struct colorSystem *cs; - int widspec = FALSE, hgtspec = FALSE; + bool widspec = false, hgtspec = false; unsigned int xBias, yBias; - int upvp = FALSE; /* xy or u'v' color coordinates? */ - int showWhite = TRUE; /* Show white point ? */ - int showBlack = TRUE; /* Show black body curve ? */ - int fullChart = FALSE; /* Fill entire tongue ? */ - int showLabel = TRUE; /* Show labels ? */ - int showAxes = TRUE; /* Plot axes ? */ - - ppm_init(&argc, argv); + bool upvp = false; /* xy or u'v' color coordinates? */ + bool showWhite = true; /* Show white point ? */ + bool showBlack = true; /* Show black body curve ? */ + bool fullChart = false; /* Fill entire tongue ? */ + bool showLabel = true; /* Show labels ? */ + bool showAxes = true; /* Plot axes ? */ + + pm_proginit(&argc, argv); argn = 1; cs = &Rec709system; /* default */ while (argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0') { if (pm_keymatch(argv[argn], "-xy", 2)) { - upvp = FALSE; + upvp = false; } else if (pm_keymatch(argv[argn], "-upvp", 1)) { - upvp = TRUE; + upvp = true; } else if (pm_keymatch(argv[argn], "-xsize", 1) || pm_keymatch(argv[argn], "-width", 2)) { if (widspec) { @@ -1172,7 +1170,7 @@ main(int argc, argn++; if ((argn == argc) || (sscanf(argv[argn], "%d", &sxsize) != 1)) pm_usage(usage); - widspec = TRUE; + widspec = true; } else if (pm_keymatch(argv[argn], "-ysize", 1) || pm_keymatch(argv[argn], "-height", 2)) { if (hgtspec) { @@ -1181,7 +1179,7 @@ main(int argc, argn++; if ((argn == argc) || (sscanf(argv[argn], "%d", &sysize) != 1)) pm_usage(usage); - hgtspec = TRUE; + hgtspec = true; } else if (pm_keymatch(argv[argn], "-size", 2)) { if (hgtspec || widspec) { pm_error("already specified a size/height/ysize"); @@ -1190,7 +1188,7 @@ main(int argc, if ((argn == argc) || (sscanf(argv[argn], "%d", &sysize) != 1)) pm_usage(usage); sxsize = sysize; - hgtspec = widspec = TRUE; + hgtspec = widspec = true; } else if (pm_keymatch(argv[argn], "-rec709", 1)) { cs = &Rec709system; } else if (pm_keymatch(argv[argn], "-ntsc", 1)) { @@ -1204,23 +1202,23 @@ main(int argc, } else if (pm_keymatch(argv[argn], "-cie", 1)) { cs = &CIEsystem; } else if (pm_keymatch(argv[argn], "-black", 3)) { - showBlack = TRUE; /* Show black body curve */ + showBlack = true; /* Show black body curve */ } else if (pm_keymatch(argv[argn], "-wpoint", 2)) { - showWhite = TRUE; /* Show white point of color system */ + showWhite = true; /* Show white point of color system */ } else if (pm_keymatch(argv[argn], "-noblack", 3)) { - showBlack = FALSE; /* Don't show black body curve */ + showBlack = false; /* Don't show black body curve */ } else if (pm_keymatch(argv[argn], "-nowpoint", 3)) { - showWhite = FALSE; /* Don't show white point of system */ + showWhite = false; /* Don't show white point of system */ } else if (pm_keymatch(argv[argn], "-label", 1)) { - showLabel = TRUE; /* Show labels. */ + showLabel = true; /* Show labels. */ } else if (pm_keymatch(argv[argn], "-nolabel", 3)) { - showLabel = FALSE; /* Don't show labels */ + showLabel = false; /* Don't show labels */ } else if (pm_keymatch(argv[argn], "-axes", 1)) { - showAxes = TRUE; /* Show axes. */ + showAxes = true; /* Show axes. */ } else if (pm_keymatch(argv[argn], "-noaxes", 3)) { - showAxes = FALSE; /* Don't show axes */ + showAxes = false; /* Don't show axes */ } else if (pm_keymatch(argv[argn], "-full", 1)) { - fullChart = TRUE; /* Fill whole tongue full-intensity */ + fullChart = true; /* Fill whole tongue full-intensity */ } else if (pm_keymatch(argv[argn], "-gamma", 2)) { cs = &Customsystem; argn++; @@ -1289,32 +1287,32 @@ main(int argc, makeAllBlack(pixels, pixcols, pixrows); - drawTongueOutline(pixels, pixcols, pixrows, Maxval, upvp, xBias, yBias); + drawTongueOutline(pixels, pixcols, pixrows, cieMaxval, upvp, xBias, yBias); - fillInTongue(pixels, pixcols, pixrows, Maxval, cs, upvp, xBias, yBias, + fillInTongue(pixels, pixcols, pixrows, cieMaxval, cs, upvp, xBias, yBias, fullChart); if (showAxes) - drawAxes(pixels, pixcols, pixrows, Maxval, upvp, xBias, yBias); + drawAxes(pixels, pixcols, pixrows, cieMaxval, upvp, xBias, yBias); if (showWhite) - plotWhitePoint(pixels, pixcols, pixrows, Maxval, + plotWhitePoint(pixels, pixcols, pixrows, cieMaxval, cs, upvp, xBias, yBias); if (showBlack) - plotBlackBodyCurve(pixels, pixcols, pixrows, Maxval, + plotBlackBodyCurve(pixels, pixcols, pixrows, cieMaxval, upvp, xBias, yBias); /* Plot wavelengths around periphery of the tongue. */ if (showAxes) - plotMonochromeWavelengths(pixels, pixcols, pixrows, Maxval, + plotMonochromeWavelengths(pixels, pixcols, pixrows, cieMaxval, cs, upvp, xBias, yBias); if (showLabel) - writeLabel(pixels, pixcols, pixrows, Maxval, cs); + writeLabel(pixels, pixcols, pixrows, cieMaxval, cs); - ppm_writeppm(stdout, pixels, pixcols, pixrows, Maxval, FALSE); + ppm_writeppm(stdout, pixels, pixcols, pixrows, cieMaxval, 0); return 0; } diff --git a/generator/ppmforge.c b/generator/ppmforge.c index 189e930c..8ea86429 100644 --- a/generator/ppmforge.c +++ b/generator/ppmforge.c @@ -39,6 +39,7 @@ #include "pm_c_util.h" #include "ppm.h" #include "mallocvar.h" +#include "shhopt.h" static double const hugeVal = 1e50; @@ -74,12 +75,6 @@ static double arand, gaussadd, gaussfac; /* Gaussian random parameters */ static double fracdim; /* Fractal dimension */ static double powscale; /* Power law scaling exponent */ static int meshsize = 256; /* FFT mesh size */ -static unsigned int seedarg; /* Seed specified by user */ -static bool seedspec = FALSE; /* Did the user specify a seed ? */ -static bool clouds = FALSE; /* Just generate clouds */ -static bool stars = FALSE; /* Just generate stars */ -static int screenxsize = 256; /* Screen X size */ -static int screenysize = 256; /* Screen Y size */ static double inclangle, hourangle; /* Star position relative to planet */ static bool inclspec = FALSE; /* No inclination specified yet */ static bool hourspec = FALSE; /* No hour specified yet */ @@ -88,6 +83,166 @@ static double glaciers; /* Glacier level */ static int starfraction; /* Star fraction */ static int starcolor; /* Star color saturation */ + +struct CmdlineInfo { + unsigned int clouds; + unsigned int night; + float dimension; + float hourAngle; + unsigned int hourSpec; + float inclAngle; + unsigned int inclinationSpec; + unsigned int meshSize; + unsigned int meshSpec; + float power; + float glaciers; + float ice; + int saturation; + unsigned int seed; + int stars; + unsigned int starsSpec; + unsigned int width; + unsigned int height; +}; + + + +static void +parseCommandLine(int argc, const char **argv, + struct CmdlineInfo * const cmdlineP) { +/*---------------------------------------------------------------------------- + Convert program invocation arguments (argc,argv) into a format the + program can use easily, struct cmdlineInfo. Validate arguments along + the way and exit program with message if invalid. + + Note that some string information we return as *cmdlineP is in the storage + argv[] points to. +-----------------------------------------------------------------------------*/ + optEntry * option_def; + /* Instructions to OptParseOptions3 on how to parse our options. + */ + optStruct3 opt; + + unsigned int option_def_index; + + unsigned int dimensionSpec, seedSpec, + meshSpec, powerSpec, glaciersSpec, iceSpec, saturationSpec, + starsSpec, widthSpec, heightSpec; + float hour; + float inclination; + unsigned int mesh; + + MALLOCARRAY_NOFAIL(option_def, 100); + + option_def_index = 0; + OPTENT3(0, "clouds", OPT_FLAG, NULL, &cmdlineP->clouds, 0); + OPTENT3(0, "night", OPT_FLAG, NULL, &cmdlineP->night, 0); + OPTENT3(0, "dimension", OPT_FLOAT, &cmdlineP->dimension, + &dimensionSpec, 0); + OPTENT3(0, "hour", OPT_FLOAT, &hour, + &cmdlineP->hourSpec, 0); + OPTENT3(0, "inclination", OPT_FLOAT, &inclination, + &cmdlineP->inclinationSpec, 0); + OPTENT3(0, "tilt", OPT_FLOAT, &inclination, + &cmdlineP->inclinationSpec, 0); + OPTENT3(0, "mesh", OPT_UINT, &mesh, + &meshSpec, 0); + OPTENT3(0, "power", OPT_FLOAT, &cmdlineP->power, + &powerSpec, 0); + OPTENT3(0, "glaciers", OPT_FLOAT, &cmdlineP->glaciers, + &glaciersSpec, 0); + OPTENT3(0, "ice", OPT_FLOAT, &cmdlineP->ice, + &iceSpec, 0); + OPTENT3(0, "saturation", OPT_INT, &cmdlineP->saturation, + &saturationSpec, 0); + OPTENT3(0, "seed", OPT_UINT, &cmdlineP->seed, + &seedSpec, 0); + OPTENT3(0, "stars", OPT_INT, &cmdlineP->stars, + &starsSpec, 0); + OPTENT3(0, "width", OPT_UINT, &cmdlineP->width, + &widthSpec, 0); + OPTENT3(0, "xsize", OPT_UINT, &cmdlineP->width, + &widthSpec, 0); + OPTENT3(0, "height", OPT_UINT, &cmdlineP->height, + &heightSpec, 0); + OPTENT3(0, "ysize", OPT_UINT, &cmdlineP->height, + &heightSpec, 0); + + opt.opt_table = option_def; + opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ + opt.allowNegNum = FALSE; /* We have no parms that are negative numbers */ + + pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0); + /* Uses and sets argc, argv, and some of *cmdlineP and others. */ + + if (dimensionSpec) { + if (cmdlineP->dimension <= 0.0) + pm_error("-dimension must be greater than zero. " + "You specified %f", cmdlineP->dimension); + } else + cmdlineP->dimension = cmdlineP->clouds ? 2.15 : 2.4; + + if (cmdlineP->hourSpec) + cmdlineP->hourAngle = (M_PI / 12.0) * (hour + 12.0); + + if (cmdlineP->inclinationSpec) + cmdlineP->inclAngle = (M_PI / 180.0) * inclination; + + if (meshSpec) { + unsigned int i; + if (mesh < 2) + pm_error("-mesh value must be at least 2. " + "You specified %u", mesh); + /* Force FFT mesh to the next larger power of 2. */ + for (i = 2; i < mesh; i <<= 1); + cmdlineP->meshSize = i; + } else + cmdlineP->meshSize = 256; + + if (powerSpec) { + if (cmdlineP->power <= 0.0) + pm_error("-power must be greater than zero. " + "You specified %f", cmdlineP->power); + } else + cmdlineP->power = cmdlineP->clouds ? 0.75 : 1.2; + + if (iceSpec) { + if (cmdlineP->ice <= 0.0) + pm_error("-ice must be greater than zero. " + "You specified %f", cmdlineP->ice); + } else + cmdlineP->ice = 0.4; + + if (glaciersSpec) { + if (cmdlineP->glaciers <= 0.0) + pm_error("-glaciers must be greater than 0. " + "You specified %f", cmdlineP->glaciers); + } else + cmdlineP->glaciers = 0.75; + + if (!starsSpec) + cmdlineP->stars = 100; + + if (!saturationSpec) + cmdlineP->saturation = 125; + + if (!seedSpec) + cmdlineP->seed = pm_randseed(); + + if (!widthSpec) + cmdlineP->width = 256; + + if (!heightSpec) + cmdlineP->height = 256; + + if (argc-1 > 0) + pm_error("There are no non-option arguments. " + "You specified %u", argc-1); + + free(option_def); +} + + /* FOURN -- Multi-dimensional fast Fourier transform Called with arguments: @@ -274,19 +429,6 @@ static void spectralsynth(x, n, h) } -static unsigned int -initseed(void) { - /* Generate initial random seed. */ - - unsigned int i; - - srand(pm_randseed()); - for (i = 0; i < 7; ++i) - rand(); - return rand(); -} - - /* TEMPRGB -- Calculate the relative R, G, and B components for a black body emitting light at a given temperature. @@ -417,7 +559,8 @@ makeCp(float * const a, static void -createPlanetStuff(float * const a, +createPlanetStuff(bool const clouds, + float * const a, unsigned int const n, double ** const uP, double ** const u1P, @@ -456,7 +599,9 @@ createPlanetStuff(float * const a, " -inclination %.0f -hour %d -ice %.2f -glaciers %.2f", (siang * (180.0 / M_PI)), (int) (((shang * (12.0 / M_PI)) + 12 + - (flipped ? 12 : 0)) + 0.5) % 24, icelevel, glaciers); + (flipped ? 12 : 0)) + 0.5) % 24, + icelevel, + glaciers); pm_message(" -stars %d -saturation %d.", starfraction, starcolor); } @@ -531,9 +676,9 @@ generateCloudRow(pixel * const pixels, /* Render the FFT output as clouds. */ - unsigned int j; + unsigned int col; - for (j = 0; j < cols; j++) { + for (col = 0; col < cols; ++col) { double r; pixval w; @@ -542,15 +687,15 @@ generateCloudRow(pixel * const pixels, referenced below does not exist. */ if (t1 > 0.0) - r += t1 * u1[j] * cp[byf + bxf[j]] + - t1 * u[j] * cp[byf + bxc[j]]; + r += t1 * u1[col] * cp[byf + bxf[col]] + + t1 * u[col] * cp[byf + bxc[col]]; if (t > 0.0) - r += t * u1[j] * cp[byc + bxf[j]] + - t * u[j] * cp[byc + bxc[j]]; + r += t * u1[col] * cp[byc + bxf[col]] + + t * u[col] * cp[byc + bxc[col]]; w = (r > 127.0) ? (maxval * ((r - 127.0) / 128.0)) : 0; - PPM_ASSIGN(*(pixels + j), w, w, maxval); + PPM_ASSIGN(pixels[col], w, w, maxval); } } @@ -819,7 +964,7 @@ genplanet(bool const stars, pm_message("%s: -seed %d -dimension %.2f -power %.2f -mesh %d", clouds ? "clouds" : "planet", rseed, fracdim, powscale, meshsize); - createPlanetStuff(a, n, &u, &u1, &bxf, &bxc, &cp, &sunvec, + createPlanetStuff(clouds, a, n, &u, &u1, &bxf, &bxc, &cp, &sunvec, cols, maxval); } @@ -940,19 +1085,14 @@ static bool planet(unsigned int const cols, unsigned int const rows, bool const stars, - bool const clouds) { + bool const clouds, + unsigned int const rseed) { /*---------------------------------------------------------------------------- Make a planet. -----------------------------------------------------------------------------*/ float * a; bool error; - unsigned int rseed; /* Current random seed */ - - if (seedspec) - rseed = seedarg; - else - rseed = initseed(); - + initgauss(rseed); if (stars) { @@ -981,200 +1121,39 @@ planet(unsigned int const cols, - int -main(int argc, char ** argv) { +main(int argc, const char ** argv) { + struct CmdlineInfo cmdline; bool success; - int i; - const char * const usage = "\n\ -[-width|-xsize ] [-height|-ysize ] [-mesh ]\n\ -[-clouds] [-dimension ] [-power ] [-seed ]\n\ -[-hour ] [-inclination|-tilt ] [-ice ] [-glaciers ]\n\ -[-night] [-stars ] [-saturation ]"; - bool dimspec = FALSE, meshspec = FALSE, powerspec = FALSE, - widspec = FALSE, hgtspec = FALSE, icespec = FALSE, - glacspec = FALSE, starspec = FALSE, starcspec = FALSE; - - int cols, rows; /* Dimensions of our output image */ - - ppm_init(&argc, argv); - i = 1; - - while ((i < argc) && (argv[i][0] == '-') && (argv[i][1] != '\0')) { - - if (pm_keymatch(argv[i], "-clouds", 2)) { - clouds = TRUE; - } else if (pm_keymatch(argv[i], "-night", 2)) { - stars = TRUE; - } else if (pm_keymatch(argv[i], "-dimension", 2)) { - if (dimspec) { - pm_error("already specified a dimension"); - } - i++; - if ((i == argc) || (sscanf(argv[i], "%lf", &fracdim) != 1)) - pm_usage(usage); - if (fracdim <= 0.0) { - pm_error("fractal dimension must be greater than 0"); - } - dimspec = TRUE; - } else if (pm_keymatch(argv[i], "-hour", 3)) { - if (hourspec) { - pm_error("already specified an hour"); - } - i++; - if ((i == argc) || (sscanf(argv[i], "%lf", &hourangle) != 1)) - pm_usage(usage); - hourangle = (M_PI / 12.0) * (hourangle + 12.0); - hourspec = TRUE; - } else if (pm_keymatch(argv[i], "-inclination", 3) || - pm_keymatch(argv[i], "-tilt", 2)) { - if (inclspec) { - pm_error("already specified an inclination/tilt"); - } - i++; - if ((i == argc) || (sscanf(argv[i], "%lf", &inclangle) != 1)) - pm_usage(usage); - inclangle = (M_PI / 180.0) * inclangle; - inclspec = TRUE; - } else if (pm_keymatch(argv[i], "-mesh", 2)) { - unsigned int j; - if (meshspec) { - pm_error("already specified a mesh size"); - } - i++; - if ((i == argc) || (sscanf(argv[i], "%d", &meshsize) != 1)) - pm_usage(usage); + unsigned int cols, rows; /* Dimensions of our output image */ - if (meshsize < 2) - pm_error("mesh must be at least 2"); + pm_proginit(&argc, argv); - /* Force FFT mesh to the next larger power of 2. */ + parseCommandLine(argc, argv, &cmdline); - for (j = meshsize; (j & 1) == 0; j >>= 1) ; - - if (j != 1) { - for (j = 2; j < meshsize; j <<= 1) ; - meshsize = j; - } - meshspec = TRUE; - } else if (pm_keymatch(argv[i], "-power", 2)) { - if (powerspec) { - pm_error("already specified a power factor"); - } - i++; - if ((i == argc) || (sscanf(argv[i], "%lf", &powscale) != 1)) - pm_usage(usage); - if (powscale <= 0.0) { - pm_error("power factor must be greater than 0"); - } - powerspec = TRUE; - } else if (pm_keymatch(argv[i], "-ice", 3)) { - if (icespec) { - pm_error("already specified ice cap level"); - } - i++; - if ((i == argc) || (sscanf(argv[i], "%lf", &icelevel) != 1)) - pm_usage(usage); - if (icelevel <= 0.0) { - pm_error("ice cap level must be greater than 0"); - } - icespec = TRUE; - } else if (pm_keymatch(argv[i], "-glaciers", 2)) { - if (glacspec) { - pm_error("already specified glacier level"); - } - i++; - if ((i == argc) || (sscanf(argv[i], "%lf", &glaciers) != 1)) - pm_usage(usage); - if (glaciers <= 0.0) { - pm_error("glacier level must be greater than 0"); - } - glacspec = TRUE; - } else if (pm_keymatch(argv[i], "-stars", 3)) { - if (starspec) { - pm_error("already specified a star fraction"); - } - i++; - if ((i == argc) || (sscanf(argv[i], "%d", &starfraction) != 1)) - pm_usage(usage); - starspec = TRUE; - } else if (pm_keymatch(argv[i], "-saturation", 3)) { - if (starcspec) { - pm_error("already specified a star color saturation"); - } - i++; - if ((i == argc) || (sscanf(argv[i], "%d", &starcolor) != 1)) - pm_usage(usage); - starcspec = TRUE; - } else if (pm_keymatch(argv[i], "-seed", 3)) { - if (seedspec) { - pm_error("already specified a random seed"); - } - i++; - if ((i == argc) || (sscanf(argv[i], "%u", &seedarg) != 1)) - pm_usage(usage); - seedspec = TRUE; - } else if (pm_keymatch(argv[i], "-xsize", 2) || - pm_keymatch(argv[i], "-width", 2)) { - if (widspec) { - pm_error("already specified a width/xsize"); - } - i++; - if ((i == argc) || (sscanf(argv[i], "%d", &screenxsize) != 1)) - pm_usage(usage); - widspec = TRUE; - } else if (pm_keymatch(argv[i], "-ysize", 2) || - pm_keymatch(argv[i], "-height", 3)) { - if (hgtspec) { - pm_error("already specified a height/ysize"); - } - i++; - if ((i == argc) || (sscanf(argv[i], "%d", &screenysize) != 1)) - pm_usage(usage); - hgtspec = TRUE; - } else { - pm_usage(usage); - } - i++; - } - - - /* Set defaults when explicit specifications were not given. - - The default fractal dimension and power scale depend upon - whether we are generating a planet or clouds. - */ - - if (!dimspec) { - fracdim = clouds ? 2.15 : 2.4; - } - if (!powerspec) { - powscale = clouds ? 0.75 : 1.2; - } - if (!icespec) { - icelevel = 0.4; - } - if (!glacspec) { - glaciers = 0.75; - } - if (!starspec) { - starfraction = 100; - } - if (!starcspec) { - starcolor = 125; - } + fracdim = cmdline.dimension; + hourspec = cmdline.hourSpec; + hourangle = cmdline.hourAngle; + inclspec = cmdline.inclinationSpec; + inclangle = cmdline.inclAngle; + meshsize = cmdline.meshSize; + powscale = cmdline.power; + icelevel = cmdline.ice; + glaciers = cmdline.glaciers; + starfraction = cmdline.stars; + starcolor = cmdline.saturation; /* Force screen to be at least as wide as it is high. Long, skinny screens cause crashes because picture width is calculated based on height. */ - cols = (MAX(screenysize, screenxsize) + 1) & (~1); - rows = screenysize; + cols = (MAX(cmdline.height, cmdline.width) + 1) & (~1); + rows = cmdline.height; - success = planet(cols, rows, stars, clouds); + success = planet(cols, rows, cmdline.night, cmdline.clouds, cmdline.seed); exit(success ? 0 : 1); } diff --git a/generator/ppmrainbow b/generator/ppmrainbow index 96e304ac..c0568d9b 100755 --- a/generator/ppmrainbow +++ b/generator/ppmrainbow @@ -1,4 +1,28 @@ -#!/usr/bin/perl -wl +#!/bin/sh + +############################################################################## +# This is essentially a Perl program. We exec the Perl interpreter specifying +# this same file as the Perl program and use the -x option to cause the Perl +# interpreter to skip down to the Perl code. The reason we do this instead of +# just making /usr/bin/perl the script interpreter (instead of /bin/sh) is +# that the user may have multiple Perl interpreters and the one he wants to +# use is properly located in the PATH. The user's choice of Perl interpreter +# may be crucial, such as when the user also has a PERL5LIB environment +# variable and it selects modules that work with only a certain main +# interpreter program. +# +# An alternative some people use is to have /usr/bin/env as the script +# interpreter. We don't do that because we think the existence and +# compatibility of /bin/sh is more reliable. +# +# Note that we aren't concerned about efficiency because the user who needs +# high efficiency can use directly the programs that this program invokes. +# +############################################################################## + +exec perl -w -x -S -- "$0" "$@" + +#!/usr/bin/perl use strict; use Getopt::Long; diff --git a/lib/libpam.c b/lib/libpam.c index 549f4c55..f188f7d6 100644 --- a/lib/libpam.c +++ b/lib/libpam.c @@ -1046,9 +1046,7 @@ pnm_writepaminit(struct pam * const pamP) { switch (PAM_FORMAT_TYPE(pamP->format)) { case PAM_TYPE: - if (pm_plain_output) - pm_error("There is no plain version of PAM. -plain option " - "is not allowed"); + /* See explanation below of why we ignore 'pm_plain_output' here. */ fprintf(pamP->file, "P7\n"); writeComments(pamP); fprintf(pamP->file, "WIDTH %u\n", (unsigned)pamP->width); @@ -1108,6 +1106,23 @@ pnm_writepaminit(struct pam * const pamP) { +/* EFFECT OF -plain WHEN WRITING PAM FORMAT: + + Before Netpbm 10.63 (June 2013), pnm_writepaminit() did a pm_error() here + if 'pm_plain_output' was set (i.e. the user said -plain). But this isn't + really logical, because -plain is a global option for the program and here + we are just writing one image. As a global option, -plain must be defined + to have effect where it makes sense and have no effect where it doesn't. + Note that a program that generates GIF just ignores -plain. Note also that + a program could conceivably generate both a PPM image and a PAM image. + + Note also how we handle the other a user can request plain format: the + 'plainformat' member of the PAM struct. In the case of PAM, we ignore that + member. +*/ + + + void pnm_checkpam(const struct pam * const pamP, enum pm_check_type const checkType, @@ -1291,7 +1306,7 @@ pnm_addopacityrow(const struct pam * const pamP, void pnm_getopacity(const struct pam * const pamP, - bool * const haveOpacityP, + int * const haveOpacityP, unsigned int * const opacityPlaneP) { /* Usage note: this is obsolete since we added 'have_opacity', etc. diff --git a/lib/libpammap.c b/lib/libpammap.c index 66aa6fc0..ef373e81 100644 --- a/lib/libpammap.c +++ b/lib/libpammap.c @@ -574,7 +574,7 @@ pnm_computetupletablehash(struct pam * const pamP, -----------------------------------------------------------------------------*/ tuplehash tupletablehash; unsigned int i; - bool fits; + int fits; tupletablehash = pnm_createtuplehash(); diff --git a/lib/libpamn.c b/lib/libpamn.c index bd3fab21..b30bde53 100644 --- a/lib/libpamn.c +++ b/lib/libpamn.c @@ -498,7 +498,7 @@ gammaCommon(struct pam * const pamP, unsigned int plane; unsigned int opacityPlane; - bool haveOpacity; + int haveOpacity; pnm_getopacity(pamP, &haveOpacity, &opacityPlane); @@ -548,7 +548,7 @@ applyopacityCommon(enum applyUnapply const applyUnapply, if the foreground values had already been so multiplied. -----------------------------------------------------------------------------*/ unsigned int opacityPlane; - bool haveOpacity; + int haveOpacity; pnm_getopacity(pamP, &haveOpacity, &opacityPlane); @@ -645,7 +645,7 @@ createUngammaMapOffset(const struct pam * const pamP, MALLOCARRAY(ungammaTransformMap, pamP->maxval+1); if (ungammaTransformMap != NULL) { - bool haveOpacity; + int haveOpacity; unsigned int opacityPlane; unsigned int plane; diff --git a/lib/libpamwrite.c b/lib/libpamwrite.c index 73e5e78a..9f86e359 100644 --- a/lib/libpamwrite.c +++ b/lib/libpamwrite.c @@ -22,6 +22,7 @@ #include #include "pm_config.h" +#include "pm_c_util.h" #include "pam.h" @@ -354,7 +355,9 @@ pnm_writepamrow(const struct pam * const pamP, pnm_writepaminit(). */ - if (pm_plain_output || pamP->plainformat) { + if (pamP->format == PAM_FORMAT || !(pm_plain_output || pamP->plainformat)) + writePamRawRow(pamP, tuplerow, 1); + else { switch (PAM_FORMAT_TYPE(pamP->format)) { case PBM_TYPE: writePamPlainPbmRow(pamP, tuplerow); @@ -364,18 +367,13 @@ pnm_writepamrow(const struct pam * const pamP, writePamPlainRow(pamP, tuplerow); break; case PAM_TYPE: - /* pm_plain_output is impossible here due to assumption stated - above about pnm_writepaminit() having checked it. The - pamP->plainformat is meaningless for PAM. - */ - writePamRawRow(pamP, tuplerow, 1); + assert(false); break; default: pm_error("Invalid 'format' value %u in pam structure", pamP->format); } - } else - writePamRawRow(pamP, tuplerow, 1); + } } diff --git a/lib/libpbm1.c b/lib/libpbm1.c index 25498bc7..c96779ed 100644 --- a/lib/libpbm1.c +++ b/lib/libpbm1.c @@ -57,27 +57,28 @@ pbm_nextimage(FILE *file, int * const eofP) { void -pbm_check(FILE * file, const enum pm_check_type check_type, - const int format, const int cols, const int rows, - enum pm_check_code * const retval_p) { +pbm_check(FILE * const fileP, + enum pm_check_type const checkType, + int const format, + int const cols, + int const rows, + enum pm_check_code * const retvalP) { if (rows < 0) pm_error("Invalid number of rows passed to pbm_check(): %d", rows); if (cols < 0) pm_error("Invalid number of columns passed to pbm_check(): %d", cols); - if (check_type != PM_CHECK_BASIC) { - if (retval_p) *retval_p = PM_CHECK_UNKNOWN_TYPE; + if (checkType != PM_CHECK_BASIC) { + if (retvalP) + *retvalP = PM_CHECK_UNKNOWN_TYPE; } else if (format != RPBM_FORMAT) { - if (retval_p) *retval_p = PM_CHECK_UNCHECKABLE; + if (retvalP) + *retvalP = PM_CHECK_UNCHECKABLE; } else { - pm_filepos const bytes_per_row = (cols+7)/8; - pm_filepos const need_raster_size = rows * bytes_per_row; -#ifdef LARGEFILEDEBUG - pm_message("pm_filepos passed to pm_check() is %u bytes", - sizeof(pm_filepos)); -#endif - pm_check(file, check_type, need_raster_size, retval_p); + pm_filepos const bytesPerRow = (cols+7)/8; + pm_filepos const needRasterSize = rows * bytesPerRow; + pm_check(fileP, checkType, needRasterSize, retvalP); } } diff --git a/lib/libpgm1.c b/lib/libpgm1.c index 5a533f49..987ee04e 100644 --- a/lib/libpgm1.c +++ b/lib/libpgm1.c @@ -338,28 +338,30 @@ pgm_readpgm(FILE * const fileP, void pgm_check(FILE * const file, - enum pm_check_type const check_type, + enum pm_check_type const checkType, int const format, int const cols, int const rows, gray const maxval, - enum pm_check_code * const retval_p) { + enum pm_check_code * const retvalP) { if (rows < 0) pm_error("Invalid number of rows passed to pgm_check(): %d", rows); if (cols < 0) pm_error("Invalid number of columns passed to pgm_check(): %d", cols); - if (check_type != PM_CHECK_BASIC) { - if (retval_p) *retval_p = PM_CHECK_UNKNOWN_TYPE; + if (checkType != PM_CHECK_BASIC) { + if (retvalP) + *retvalP = PM_CHECK_UNKNOWN_TYPE; } else if (PGM_FORMAT_TYPE(format) == PBM_TYPE) { - pbm_check(file, check_type, format, cols, rows, retval_p); + pbm_check(file, checkType, format, cols, rows, retvalP); } else if (format != RPGM_FORMAT) { - if (retval_p) *retval_p = PM_CHECK_UNCHECKABLE; + if (retvalP) + *retvalP = PM_CHECK_UNCHECKABLE; } else { - pm_filepos const bytes_per_row = cols * (maxval > 255 ? 2 : 1); - pm_filepos const need_raster_size = rows * bytes_per_row; + pm_filepos const bytesPerRow = cols * (maxval > 255 ? 2 : 1); + pm_filepos const needRasterSize = rows * bytesPerRow; - pm_check(file, check_type, need_raster_size, retval_p); + pm_check(file, checkType, needRasterSize, retvalP); } } diff --git a/lib/libppm1.c b/lib/libppm1.c index 1b417613..195aec70 100644 --- a/lib/libppm1.c +++ b/lib/libppm1.c @@ -422,30 +422,32 @@ ppm_readppm(FILE * const fileP, void ppm_check(FILE * const fileP, - enum pm_check_type const check_type, + enum pm_check_type const checkType, int const format, int const cols, int const rows, pixval const maxval, - enum pm_check_code * const retval_p) { + enum pm_check_code * const retvalP) { if (rows < 0) pm_error("Invalid number of rows passed to ppm_check(): %d", rows); if (cols < 0) pm_error("Invalid number of columns passed to ppm_check(): %d", cols); - if (check_type != PM_CHECK_BASIC) { - if (retval_p) *retval_p = PM_CHECK_UNKNOWN_TYPE; + if (checkType != PM_CHECK_BASIC) { + if (retvalP) + *retvalP = PM_CHECK_UNKNOWN_TYPE; } else if (PPM_FORMAT_TYPE(format) == PBM_TYPE) { - pbm_check(fileP, check_type, format, cols, rows, retval_p); + pbm_check(fileP, checkType, format, cols, rows, retvalP); } else if (PPM_FORMAT_TYPE(format) == PGM_TYPE) { - pgm_check(fileP, check_type, format, cols, rows, maxval, retval_p); + pgm_check(fileP, checkType, format, cols, rows, maxval, retvalP); } else if (format != RPPM_FORMAT) { - if (retval_p) *retval_p = PM_CHECK_UNCHECKABLE; + if (retvalP) + *retvalP = PM_CHECK_UNCHECKABLE; } else { - pm_filepos const bytes_per_row = cols * 3 * (maxval > 255 ? 2 : 1); - pm_filepos const need_raster_size = rows * bytes_per_row; + pm_filepos const bytesPerRow = cols * 3 * (maxval > 255 ? 2 : 1); + pm_filepos const needRasterSize = rows * bytesPerRow; - pm_check(fileP, check_type, need_raster_size, retval_p); + pm_check(fileP, checkType, needRasterSize, retvalP); } } diff --git a/lib/libppmcolor.c b/lib/libppmcolor.c index 70e8499d..347bab29 100644 --- a/lib/libppmcolor.c +++ b/lib/libppmcolor.c @@ -451,6 +451,43 @@ ppm_colorname(const pixel * const colorP, #define MAXCOLORNAMES 1000u +static const char ** +allocColorNames() { + + const char ** colornames; + + MALLOCARRAY(colornames, MAXCOLORNAMES); + + if (colornames) { + unsigned int i; + for (i = 0; i < MAXCOLORNAMES; ++i) + colornames[i] = NULL; + } + return colornames; +} + + + +static colorhash_table +allocColorHash(void) { + + colorhash_table cht; + jmp_buf jmpbuf; + jmp_buf * origJmpbufP; + + if (setjmp(jmpbuf) != 0) + cht = NULL; + else { + pm_setjmpbufsave(&jmpbuf, &origJmpbufP); + cht = ppm_alloccolorhash(); + } + pm_setjmpbuf(origJmpbufP); + + return cht; +} + + + static void processColorfileEntry(struct colorfile_entry const ce, colorhash_table const cht, @@ -525,6 +562,9 @@ readOpenColorFile(FILE * const colorFileP, Read the color dictionary file *colorFileP and add the colors in it to colornames[], colors[], and 'cht'. + colornames[] and colors[] must be allocated with MAXCOLORNAMES entries + at entry. + We may add colors to 'cht' even if we fail. -----------------------------------------------------------------------------*/ unsigned int nColorsDone; @@ -543,12 +583,7 @@ readOpenColorFile(FILE * const colorFileP, processColorfileEntry(ce, cht, colornames, colors, &nColorsDone, errorP); } - if (!*errorP) { - *nColorsP = nColorsDone; - - while (nColorsDone < MAXCOLORNAMES) - colornames[nColorsDone++] = NULL; - } + *nColorsP = nColorsDone; if (*errorP) { unsigned int colorIndex; @@ -560,26 +595,6 @@ readOpenColorFile(FILE * const colorFileP, -static colorhash_table -allocColorHash(void) { - - colorhash_table cht; - jmp_buf jmpbuf; - jmp_buf * origJmpbufP; - - if (setjmp(jmpbuf) != 0) - cht = NULL; - else { - pm_setjmpbufsave(&jmpbuf, &origJmpbufP); - cht = ppm_alloccolorhash(); - } - pm_setjmpbuf(origJmpbufP); - - return cht; -} - - - static void readColorFile(const char * const fileName, bool const mustOpen, @@ -588,7 +603,20 @@ readColorFile(const char * const fileName, pixel * const colors, colorhash_table const cht, const char ** const errorP) { +/*---------------------------------------------------------------------------- + Read the color dictionary file named 'fileName' and add the colors in it + to colornames[], colors[], and 'cht'. Return as *nColorsP the number + of colors in it. + If the file is not openable (e.g. not file by that name exists), abort the + program if 'mustOpen' is true; otherwise, return values indicating a + dictionary with no colors. + + colornames[] and colors[] must be allocated with MAXCOLORNAMES entries + at entry. + + We may add colors to 'cht' even if we fail. +-----------------------------------------------------------------------------*/ FILE * colorFileP; openColornameFile(fileName, mustOpen, &colorFileP, errorP); @@ -598,11 +626,6 @@ readColorFile(const char * const fileName, empty file */ *nColorsP = 0; - { - unsigned int i; - for (i = 0; i < MAXCOLORNAMES; ++i) - colornames[i] = NULL; - } *errorP = NULL; } else { readOpenColorFile(colorFileP, nColorsP, colornames, colors, cht, @@ -626,7 +649,7 @@ readcolordict(const char * const fileName, const char ** colornames; - MALLOCARRAY(colornames, MAXCOLORNAMES); + colornames = allocColorNames(); if (colornames == NULL) pm_asprintf(errorP, "Unable to allocate space for colorname table."); @@ -675,7 +698,28 @@ ppm_readcolordict(const char * const fileName, const char *** const colornamesP, pixel ** const colorsP, colorhash_table * const chtP) { +/*---------------------------------------------------------------------------- + Read the color dictionary from the file named 'fileName'. If we can't open + the file (e.g. because it does not exist), and 'mustOpen' is false, return + an empty dictionary (it contains no colors). But if 'mustOpen' is true, + abort the program instead of returning an empty dictionary. + + Return as *nColorsP the number of colors in the dictionary. + Return as *colornamesP the names of those colors. *colornamesP is a + malloced array that Caller must free with ppm_freecolornames(). + The first *nColorsP entries are valid; *chtP contains indices into this + array. + + Return as *colorsP the colors. *colorsP is a malloced array of size + MAXCOLORS with the first elements filled in and the rest undefined. + + Return as *chtP a color hash table mapping each color in the dictionary + to the index into *colornamesP for the name of the color. + + Each of 'nColorsP, 'colornamesP', and 'colorsP' may be null, in which case + we do not return the corresponding information (or allocate memory for it). +-----------------------------------------------------------------------------*/ colorhash_table cht; const char ** colornames; pixel * colors; diff --git a/lib/libsystem.c b/lib/libsystem.c index 259f18e4..b8c3ea50 100644 --- a/lib/libsystem.c +++ b/lib/libsystem.c @@ -501,20 +501,20 @@ pm_feed_from_memory(int const pipeToFeedFd, struct bufferDesc * const inputBufferP = feederParm; - FILE * const outfile = fdopen(pipeToFeedFd, "w"); + FILE * const outFileP = fdopen(pipeToFeedFd, "w"); - int bytesTransferred; + size_t bytesTransferred; /* The following signals (and normally kills) the process with SIGPIPE if the pipe does not take all 'size' bytes. */ bytesTransferred = - fwrite(inputBufferP->buffer, 1, inputBufferP->size, outfile); + fwrite(inputBufferP->buffer, 1, inputBufferP->size, outFileP); if (inputBufferP->bytesTransferredP) *(inputBufferP->bytesTransferredP) = bytesTransferred; - fclose(outfile); + fclose(outFileP); } @@ -525,14 +525,14 @@ pm_accept_to_memory(int const pipetosuckFd, struct bufferDesc * const outputBufferP = accepterParm; - FILE * const infile = fdopen(pipetosuckFd, "r"); + FILE * const inFileP = fdopen(pipetosuckFd, "r"); - int bytesTransferred; + size_t bytesTransferred; bytesTransferred = - fread(outputBufferP->buffer, 1, outputBufferP->size, infile); + fread(outputBufferP->buffer, 1, outputBufferP->size, inFileP); - fclose(infile); + fclose(inFileP); if (outputBufferP->bytesTransferredP) *(outputBufferP->bytesTransferredP) = bytesTransferred; diff --git a/lib/pam.h b/lib/pam.h index 6213dab3..2726092b 100644 --- a/lib/pam.h +++ b/lib/pam.h @@ -52,9 +52,13 @@ struct pam { */ FILE * file; int format; - /* The format code of the raw image. This is PAM_FORMAT + /* The format code of the image. This is PAM_FORMAT unless the PAM image is really a view of a PBM, PGM, or PPM - image. Then it's PBM_FORMAT, RPBM_FORMAT, etc. + image. Then it's PBM_FORMAT, RPBM_FORMAT, etc. For output, + only the format _type_ is significant, e.g. PBM_FORMAT + and RPBM_FORMAT have identical effect. This is because on + output, 'plainformat' determines whether the output is the + raw or plain format of the type given by 'format'. */ unsigned int plainformat; /* Logical: On output, use plain version of the format type @@ -67,6 +71,9 @@ struct pam { Before Netpbm 10.32, this was rather different. It simply described for convenience the plainness of the format indicated by 'format'. + + This is meaningless when 'format' is PAM_FORMAT, as PAM does not + have plain and raw variations. */ int height; /* Height of image in rows */ int width; @@ -514,6 +521,8 @@ pnm_backgroundtuple(struct pam * const pamP, /*---------------------------------------------------------------------------- These are meant for passing to pm_system() as Standard Input feeder and Standard Output accepter. + + The 'feederParm' or 'accepterParm' is a pointer to a struct pamtuples. -----------------------------------------------------------------------------*/ void diff --git a/lib/pmfileio.c b/lib/pmfileio.c index 82b44cdb..1263261a 100644 --- a/lib/pmfileio.c +++ b/lib/pmfileio.c @@ -919,10 +919,6 @@ pm_check(FILE * const file, pm_filepos curpos; /* Current position of file; -1 if none */ int rc; -#ifdef LARGEFILEDEBUG - pm_message("pm_filepos received by pm_check() is %u bytes.", - sizeof(pm_filepos)); -#endif /* Note: FTELLO() is either ftello() or ftell(), depending on the capabilities of the underlying C library. It is defined in pm_config.h. ftello(), in turn, may be either ftell() or diff --git a/lib/util/mallocvar.h b/lib/util/mallocvar.h index 31ad38bc..e92e3fe4 100644 --- a/lib/util/mallocvar.h +++ b/lib/util/mallocvar.h @@ -87,7 +87,7 @@ reallocProduct(void ** const blockP, void * array; \ array = arrayName; \ reallocProduct(&array, nElements, sizeof(arrayName[0])); \ - if (!array) \ + if (!array && arrayName) \ free(arrayName); \ arrayName = array; \ } while (0) diff --git a/lib/util/pm_c_util.h b/lib/util/pm_c_util.h index f17d1e03..79897cf0 100644 --- a/lib/util/pm_c_util.h +++ b/lib/util/pm_c_util.h @@ -41,35 +41,49 @@ use "int". */ -/* We used to assume that if TRUE was defined, then bool was too. - However, we had a report on 2001.09.21 of a Tru64 system that had - TRUE but not bool and on 2002.03.21 of an AIX 4.3 system that was - likewise. So now we define bool all the time, unless the macro - HAVE_BOOL is defined. If someone is using the Netpbm libraries and - also another library that defines bool, he can either make the - other library define/respect HAVE_BOOL or just define HAVE_BOOL in - the file that includes pm_config.h or with a compiler option. Note - that C++ always has bool. +/* We will probably never again see a system that does not have + , but just in case, we have our own alternative here. - A preferred way of getting booleans is . But it's not - available on all platforms, and it's easy to reproduce what it does - here. + Evidence shows that the compiler actually produces better code with + than with bool simply typedefed to int. */ + +#ifdef __cplusplus + /* C++ has a bool type and false and true constants built in. */ +#else + /* The test for __STDC__ is paranoid. It is there just in case some + nonstandard compiler defines __STDC_VERSION__ in an arbitrary manner. + */ + #if ( defined(__GNUC__) && (__GNUC__ >= 3) ) || \ + ( defined(__STDC__) && (__STDC__ ==1) && \ + defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) ) + #include + #else + /* We used to assume that if TRUE was defined, then bool was too. + However, we had a report on 2001.09.21 of a Tru64 system that had + TRUE but not bool and on 2002.03.21 of an AIX 4.3 system that was + likewise. So now we define bool all the time, unless the macro + HAVE_BOOL is defined. If someone is using the Netpbm libraries and + also another library that defines bool, he can either make the + other library define/respect HAVE_BOOL or just define HAVE_BOOL in + the file that includes pm_config.h or with a compiler option. Note + that C++ always has bool. + */ + #ifndef HAVE_BOOL + #define HAVE_BOOL 1 + typedef int bool; + #endif + #ifndef true + enum boolvalue {false=0, true=1}; + #endif + #endif +#endif + #ifndef TRUE - #define TRUE 1 + #define TRUE true #endif #ifndef FALSE - #define FALSE 0 - #endif -/* C++ has a bool type and false and true constants built in. */ -#ifndef __cplusplus - #ifndef HAVE_BOOL - #define HAVE_BOOL 1 - typedef int bool; - #endif - #ifndef true - enum boolvalue {false=0, true=1}; - #endif +#define FALSE false #endif #define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) diff --git a/other/pamchannel.c b/other/pamchannel.c index 48a2b2d7..3adb0e66 100644 --- a/other/pamchannel.c +++ b/other/pamchannel.c @@ -36,7 +36,7 @@ struct cmdlineInfo { static void -parseCommandLine(int argc, char ** argv, +parseCommandLine(int argc, const char ** argv, struct cmdlineInfo * const cmdlineP) { /*---------------------------------------------------------------------------- Note that the file spec array we return is stored in the storage that @@ -63,7 +63,7 @@ parseCommandLine(int argc, char ** argv, opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ opt.allowNegNum = FALSE; /* We have no parms that are negative numbers */ - pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0); + pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0); /* Uses and sets argc, argv, and some of *cmdlineP and others. */ if (!infileSpec) @@ -174,13 +174,13 @@ doOneImage(FILE * const ifP, int -main(int argc, char *argv[]) { +main(int argc, const char *argv[]) { struct cmdlineInfo cmdline; FILE * ifP; - bool eof; + int eof; - pnm_init(&argc, argv); + pm_proginit(&argc, argv); parseCommandLine(argc, argv, &cmdline); diff --git a/other/pamdepth.c b/other/pamdepth.c index 7b5de8b3..71dae9d8 100644 --- a/other/pamdepth.c +++ b/other/pamdepth.c @@ -28,7 +28,7 @@ struct cmdlineInfo { static void -parseCommandLine(int argc, char ** argv, +parseCommandLine(int argc, const char ** argv, struct cmdlineInfo *cmdlineP) { /*---------------------------------------------------------------------------- Note that the file spec strings we return are stored in the storage that @@ -51,7 +51,7 @@ parseCommandLine(int argc, char ** argv, opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ opt.allowNegNum = FALSE; /* We may have parms that are negative numbers */ - pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0); + pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0); /* Uses and sets argc, argv, and some of *cmdlineP and others. */ if (argc-1 < 1) @@ -133,16 +133,15 @@ transformRaster(struct pam * const inpamP, int -main(int argc, - char * argv[]) { +main(int argc, const char * argv[]) { struct cmdlineInfo cmdline; FILE * ifP; struct pam inpam; struct pam outpam; - bool eof; + int eof; - pnm_init(&argc, argv); + pm_proginit(&argc, argv); parseCommandLine(argc, argv, &cmdline); diff --git a/other/pamexec.c b/other/pamexec.c index 5961319b..d14d8752 100644 --- a/other/pamexec.c +++ b/other/pamexec.c @@ -159,7 +159,7 @@ main(int argc, const char *argv[]) { struct cmdlineInfo cmdline; FILE * ifP; /* Input file pointer */ - bool eof; /* No more images in input */ + int eof; /* No more images in input */ unsigned int imageSeq; /* Sequence number of current image in input file. First = 0. (Useful for tracking down problems). diff --git a/other/pampick.c b/other/pampick.c index 5229502d..61941f06 100644 --- a/other/pampick.c +++ b/other/pampick.c @@ -92,7 +92,7 @@ struct cmdlineInfo { static void -parseCommandLine(int argc, char ** argv, +parseCommandLine(int argc, const char ** argv, struct cmdlineInfo * const cmdlineP) { /*---------------------------------------------------------------------------- Note that the pointers we place into *cmdlineP are sometimes to storage @@ -119,7 +119,7 @@ parseCommandLine(int argc, char ** argv, opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ opt.allowNegNum = FALSE; /* We have no parms that are negative numbers */ - pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0); + pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0); /* Uses and sets argc, argv, and some of *cmdlineP and others. */ initUintSet(&cmdlineP->imageSeqList, argc-1); @@ -217,15 +217,15 @@ failIfUnpickedImages(const struct uintSet * const uintSetP, int -main(int argc, char *argv[]) { +main(int argc, const char *argv[]) { struct cmdlineInfo cmdline; - 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 */ - pnm_init(&argc, argv); + pm_proginit(&argc, argv); parseCommandLine(argc, argv, &cmdline); diff --git a/other/pamsplit.c b/other/pamsplit.c index 2c029d04..26eb0d59 100644 --- a/other/pamsplit.c +++ b/other/pamsplit.c @@ -155,7 +155,7 @@ main(int argc, const char *argv[]) { struct cmdlineInfo cmdline; FILE * ifP; - 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/other/pamstack.c b/other/pamstack.c index e151d99b..308852c8 100644 --- a/other/pamstack.c +++ b/other/pamstack.c @@ -216,10 +216,10 @@ nextImageAllStreams(unsigned int const nInput, unsigned int inputSeq; for (inputSeq = 0; inputSeq < nInput; ++inputSeq) { - bool eof; + int eof; pnm_nextimage(ifP[inputSeq], &eof); if (eof) - *eofP = eof; + *eofP = true; } } diff --git a/other/pnmcolormap.c b/other/pnmcolormap.c index fca4a213..57db4329 100644 --- a/other/pnmcolormap.c +++ b/other/pnmcolormap.c @@ -655,7 +655,7 @@ addImageColorsToHash(struct pam * const pamP, pnm_readpamrow(pamP, tuplerow); for (col = 0; col < pamP->width; ++col) { - bool firstOccurrence; + int firstOccurrence; pnm_addtuplefreqoccurrence(pamP, tuplerow[col], tuplehash, &firstOccurrence); @@ -688,7 +688,7 @@ computeHistogram(FILE * const ifP, struct pam firstPam; tuplehash tuplehash; unsigned int colorCount; - bool eof; + int eof; pm_message("making histogram..."); diff --git a/test/Execute-Tests b/test/Execute-Tests index bec1cd56..39fab58c 100755 --- a/test/Execute-Tests +++ b/test/Execute-Tests @@ -53,6 +53,9 @@ fi srcdir=$(dirname $0) +# Provision to run programs under valgrind. +# export PBM_TESTPREFIX="valgrind --log-fd=4 "${PBM_TESTPREFIX} + # Set tmpdir, which is used in some of the test scripts. By default # this is created by mktemp. The user can override and specify tmpdir, # but in this case it must be an existing directory and must not be @@ -60,7 +63,7 @@ srcdir=$(dirname $0) if [ -z $tmpdir ] then - tmpdir_created=$(mktemp -d TestPBM-XXXXXXX) || exit 1; + tmpdir_created=$(mktemp -d) || exit 1; export tmpdir=${tmpdir_created} else tmpdir_created=""; @@ -90,7 +93,13 @@ declare -a status=("SUCCESS" "FAILURE" "UNEXPECTED SUCCESS" # Copy test files to the current work directory -cp -n -t . ${srcdir}/testgrid.pbm ${srcdir}/testimg.ppm +if [ ! -f ./testgrid.pbm ] + then cp -v ${srcdir}/testgrid.pbm ./testgrid.pbm +fi + +if [ ! -f ./testimg.ppm ] + then cp -v ${srcdir}/testimg.ppm ./testimg.ppm +fi # Execute the tests, as described in the "Test-Order" file. # @@ -103,31 +112,31 @@ cp -n -t . ${srcdir}/testgrid.pbm ${srcdir}/testimg.ppm # # All tests are self-contained. # -# By defeault the tests are executed in the order described in the -# file Test-Order. Normally the Test-Order in the source directory -# will be used, but the user can override this with a file named -# Test-Order placed in the work directory. (This feature comes useful -# when you want to pare down the list.) - -if [ ! -f ./Test-Order ] -then cp ${srcdir}/Test-Order ./Test-Order -fi +# By default the tests are executed in the order described in the +# file Test-Order. Copy this file from the source directory +# to the work directory. +# +# The --no-clobber version comes useful when the user wants a modified +# (pared-down) version of Test-Order. + +cp ${srcdir}/Test-Order ./Test-Order +#cp --no-clobber ${srcdir}/Test-Order ./Test-Order for t in `grep -v "^#" ./Test-Order | fgrep ".test"` do echo == $t == ${srcdir}/$t > ${t%.test}.out ; let result=$? case $result in -0) cmp --quiet ${t%.test}.out ${srcdir}/${t%.test}.ok ; +0) cmp -s ${t%.test}.out ${srcdir}/${t%.test}.ok ; if [ $? -eq 0 ] then let result=0; rm ${t%.test}.out ; else let result=1; fi let supported=1 ;; -*) let result=1 ; let supported=1;; +80) let result=4 ; let supported=0;; +*) let result=1 ; let supported=1;; esac - # Report whether a single test succeeded or failed. # Increment counters. @@ -168,7 +177,7 @@ for s in 0 1 2 3 4 5 echo ================== echo "All tests done." -date --rfc-3339=seconds +date -R -u # Exit with status 0 if all supported tests succeeded, 1 otherwise. diff --git a/test/Test-Order b/test/Test-Order index a23bdd92..698552b9 100644 --- a/test/Test-Order +++ b/test/Test-Order @@ -16,7 +16,14 @@ pgmramp.test ppmgauss.test ppmcie.test ppmwheel.test + +# Generators with random components + +pgmnoise.test ppmpat.test +pgmcrater.test +ppmforge.test +ppmrough.test # Analyzer tests @@ -46,6 +53,7 @@ pnmcat.test pamflip.test pamenlarge.test pnminvert.test +pamchannel.test pbmpscale.test pnmremap1.test @@ -93,6 +101,7 @@ fits-roundtrip.test g3-roundtrip.test gem-roundtrip.test gif-roundtrip.test +gif-quant-roundtrip.test hdiff-roundtrip.test leaf-roundtrip.test mgr-roundtrip.test @@ -105,6 +114,7 @@ targa-roundtrip.test tiff-roundtrip.test utahrle-roundtrip.test wbmp-roundtrip.test +winicon-roundtrip.test xbm-roundtrip.test xpm-roundtrip.test xwd-roundtrip.test diff --git a/test/all-in-place.ok b/test/all-in-place.ok index 75e1e01a..1510f2e7 100644 --- a/test/all-in-place.ok +++ b/test/all-in-place.ok @@ -102,6 +102,7 @@ pamtosvg: ok pamtotga: ok pamtotiff: ok pamtouil: ok +pamtowinicon: ok pamtoxvmini: ok pamundice: ok pamwipeout: ok @@ -329,6 +330,7 @@ ximtoppm: ok xpmtoppm: ok xvminitoppm: ok xwdtopnm: ok +winicontopam: ok ybmtopbm: ok yuvsplittoppm: ok yuvtoppm: ok @@ -348,3 +350,5 @@ ppmquant: ok ppmquantall: ok ppmrainbow: ok ppmfade: ok +pamstretch-gen: ok +pcdovtoppm: ok diff --git a/test/all-in-place.test b/test/all-in-place.test index ae67e809..87ddfad8 100755 --- a/test/all-in-place.test +++ b/test/all-in-place.test @@ -16,11 +16,15 @@ function testExitStatus () { case $3 in $2) echo $1": ok" ;; - 126) if [ ! -z ${PBM_TESTPREFIX} ] && [ ! -x ${PBM_TESTPREFIX}/$1 ] + 126) if [ ! -z "${PBM_TESTPREFIX}" ] && \ + [ -d "${PBM_TESTPREFIX}" ] && \ + [ ! -x "${PBM_TESTPREFIX}/$1" ] then echo $1": NOT EXECUTABLE"; else echo $1": ERROR: "$3; echo $1": error: "$3 1>&2 ; fi ;; - 127) if [ ! -z ${PBM_TESTPREFIX} ] && [ ! -f ${PBM_TESTPREFIX}/$1 ] + 127) if [ ! -z "${PBM_TESTPREFIX}" ] && \ + [ -d "${PBM_TESTPREFIX}" ] && \ + [ ! -f "${PBM_TESTPREFIX}/$1" ] then echo $1": NO SUCH FILE"; else echo $1": ERROR: "$3; echo $1": error: "$3 1>&2 ; fi ;; @@ -29,70 +33,350 @@ function testExitStatus () { } -# Test programs which support the --version flag. +# Test programs which have the --version flag. # See showVersion() in lib/libpm.c for the standard version announcement. -for i in \ - 411toppm asciitopgm atktopbm avstopam bioradtopgm \ - bmptopnm bmptoppm brushtopbm cameratopam cistopbm \ - cmuwmtopbm ddbugtopbm escp2topbm eyuvtoppm fitstopnm \ - fstopgm g3topbm gemtopbm gemtopnm giftopnm gouldtoppm \ - hdifftopam hipstopgm icontopbm ilbmtoppm imgtoppm infotopam \ - jbigtopnm jpeg2ktopam jpegtopnm leaftoppm lispmtopgm \ - macptopbm mdatopbm mgrtopbm mrftopbm mtvtoppm neotoppm \ - palmtopnm pamaddnoise pamarith pambackground pambayer \ - pamchannel pamcomp pamcut pamdeinterlace pamdepth \ - pamdice pamditherbw pamedge pamendian pamenlarge \ - pamexec pamfile pamfixtrunc pamflip pamfunc pamgauss \ - pamgradient pamlookup pammasksharpen pammixinterlace \ - pammosaicknit pamoil pampaintspill pamperspective pampick \ - pampop9 pamrecolor pamrgbatopng pamrubber pamscale pamseq \ - pamsharpmap pamsharpness pamsistoaglyph pamslice pamsplit \ - pamstack pamstereogram pamstretch pamsumm pamsummcol \ - pamthreshold pamtilt pamtoavs pamtodjvurle pamtofits \ - pamtogif pamtohdiff pamtohtmltbl pamtojpeg2k pamtompfont \ - pamtooctaveimg pamtopam pamtopdbimg pamtopfm pamtopnm \ - pamtosrf pamtosvg pamtotga pamtotiff pamtouil pamtoxvmini \ - pamundice pamwipeout pamx pbmclean pbmlife pbmmake pbmmask \ - pbmminkowski pbmpage pbmpscale pbmreduce pbmtext pbmtextps \ - pbmto10x pbmto4425 pbmtoascii pbmtoatk pbmtobbnbg pbmtocis \ - pbmtocmuwm pbmtodjvurle pbmtoepsi pbmtoepson pbmtoescp2 \ - pbmtog3 pbmtogem pbmtogo pbmtoibm23xx pbmtoicon pbmtolj \ - pbmtoln03 pbmtolps pbmtomacp pbmtomatrixorbital pbmtomda \ - pbmtomgr pbmtomrf pbmtonokia pbmtopgm pbmtopi3 pbmtopk \ - pbmtoplot pbmtoppa pbmtopsg3 pbmtoptx pbmtosunicon \ - pbmtowbmp pbmtoxbm pbmtoybm pbmtozinc pbmupc pc1toppm \ - pcxtoppm pdbimgtopam pfmtopam pgmabel pgmbentley pgmcrater \ - pgmdeshadow pgmedge pgmenhance pgmhist pgmkernel pgmmake \ - pgmmedian pgmminkowski pgmmorphconv pgmnoise pgmnorm \ - pgmoil pgmramp pgmslice pgmtexture pgmtofs pgmtolispm \ - pgmtopbm pgmtopgm pgmtoppm pi1toppm pi3topbm picttoppm \ - pjtoppm pktopbm pngtopam pngtopnm pnmalias pnmarith pnmcat \ - pnmcolormap pnmcomp pnmconvol pnmcrop pnmcut pnmdepth \ - pnmenlarge pnmfile pnmflip pnmgamma pnmhisteq pnmhistmap \ - pnmindex pnminterp pnminvert pnmmercator pnmmontage \ - pnmnlfilt pnmnorm pnmpad pnmpaste pnmpsnr pnmremap \ - pnmrotate pnmscale pnmscalefixed pnmshear pnmsmooth \ - pnmsplit pnmstitch pnmtile pnmtoddif pnmtofiasco pnmtofits \ - pnmtojbig pnmtojpeg pnmtopalm pnmtopclxl pnmtopng pnmtopnm \ - pnmtops pnmtorast pnmtorle pnmtosgi pnmtosir pnmtotiff \ - pnmtotiffcmyk pnmtoxwd ppm3d ppmbrighten ppmchange \ - ppmcie ppmcolormask ppmcolors ppmdcfont ppmddumpfont \ - ppmdim ppmdist ppmdither ppmdmkfont ppmdraw ppmflash \ - ppmforge ppmglobe ppmhist ppmlabel ppmmake ppmmix ppmnorm \ - ppmntsc ppmpat ppmrelief ppmrough ppmshift \ - ppmspread ppmtoacad ppmtoapplevol ppmtoarbtxt ppmtoascii \ - ppmtobmp ppmtoeyuv ppmtogif ppmtoicr ppmtoilbm ppmtojpeg \ - ppmtoleaf ppmtolj ppmtomitsu ppmtompeg ppmtoneo ppmtopcx \ - ppmtopgm ppmtopi1 ppmtopict ppmtopj ppmtopjxl ppmtoppm \ - ppmtopuzz ppmtorgb3 ppmtosixel ppmtospu ppmtoterm ppmtouil \ - ppmtowinicon ppmtoxpm ppmtoyuv ppmtoyuvsplit ppmtv ppmwheel \ - psidtopgm pstopnm qrttoppm rasttopnm rawtopgm rawtoppm \ - rgb3toppm rlatopam rletopnm sbigtopgm sgitopnm sirtopnm \ - sldtoppm spctoppm spottopgm sputoppm srftopam sunicontopnm \ - svgtopam tgatoppm thinkjettopbm tifftopnm wbmptopbm \ - winicontoppm xbmtopbm ximtoppm xpmtoppm xvminitoppm \ - xwdtopnm ybmtopbm yuvsplittoppm yuvtoppm zeisstopnm +ordinary_testprogs="\ + 411toppm \ + asciitopgm \ + atktopbm \ + avstopam \ + bioradtopgm \ + bmptopnm \ + bmptoppm \ + brushtopbm \ + cameratopam \ + cistopbm \ + cmuwmtopbm \ + ddbugtopbm \ + escp2topbm \ + eyuvtoppm \ + fitstopnm \ + fstopgm \ + g3topbm \ + gemtopbm \ + gemtopnm \ + giftopnm \ + gouldtoppm \ + hdifftopam \ + hipstopgm \ + icontopbm \ + ilbmtoppm \ + imgtoppm \ + infotopam \ + jbigtopnm \ + jpeg2ktopam \ + jpegtopnm \ + leaftoppm \ + lispmtopgm \ + macptopbm \ + mdatopbm \ + mgrtopbm \ + mrftopbm \ + mtvtoppm \ + neotoppm \ + palmtopnm \ + pamaddnoise \ + pamarith \ + pambackground \ + pambayer \ + pamchannel \ + pamcomp \ + pamcut \ + pamdeinterlace \ + pamdepth \ + pamdice \ + pamditherbw \ + pamedge \ + pamendian \ + pamenlarge \ + pamexec \ + pamfile \ + pamfixtrunc \ + pamflip \ + pamfunc \ + pamgauss \ + pamgradient \ + pamlookup \ + pammasksharpen \ + pammixinterlace \ + pammosaicknit \ + pamoil \ + pampaintspill \ + pamperspective \ + pampick \ + pampop9 \ + pamrecolor \ + pamrgbatopng \ + pamrubber \ + pamscale \ + pamseq \ + pamsharpmap \ + pamsharpness \ + pamsistoaglyph \ + pamslice \ + pamsplit \ + pamstack \ + pamstereogram \ + pamstretch \ + pamsumm \ + pamsummcol \ + pamthreshold \ + pamtilt \ + pamtoavs \ + pamtodjvurle \ + pamtofits \ + pamtogif \ + pamtohdiff \ + pamtohtmltbl \ + pamtojpeg2k \ + pamtompfont \ + pamtooctaveimg \ + pamtopam \ + pamtopdbimg \ + pamtopfm \ + pamtopnm \ + pamtosrf \ + pamtosvg \ + pamtotga \ + pamtotiff \ + pamtouil \ + pamtowinicon \ + pamtoxvmini \ + pamundice \ + pamwipeout \ + pamx \ + pbmclean \ + pbmlife \ + pbmmake \ + pbmmask \ + pbmminkowski \ + pbmpage \ + pbmpscale \ + pbmreduce \ + pbmtext \ + pbmtextps \ + pbmto10x \ + pbmto4425 \ + pbmtoascii \ + pbmtoatk \ + pbmtobbnbg \ + pbmtocis \ + pbmtocmuwm \ + pbmtodjvurle \ + pbmtoepsi \ + pbmtoepson \ + pbmtoescp2 \ + pbmtog3 \ + pbmtogem \ + pbmtogo \ + pbmtoibm23xx \ + pbmtoicon \ + pbmtolj \ + pbmtoln03 \ + pbmtolps \ + pbmtomacp \ + pbmtomatrixorbital \ + pbmtomda \ + pbmtomgr \ + pbmtomrf \ + pbmtonokia \ + pbmtopgm \ + pbmtopi3 \ + pbmtopk \ + pbmtoplot \ + pbmtoppa \ + pbmtopsg3 \ + pbmtoptx \ + pbmtosunicon \ + pbmtowbmp \ + pbmtoxbm \ + pbmtoybm \ + pbmtozinc \ + pbmupc \ + pc1toppm \ + pcxtoppm \ + pdbimgtopam \ + pfmtopam \ + pgmabel \ + pgmbentley \ + pgmcrater \ + pgmdeshadow \ + pgmedge \ + pgmenhance \ + pgmhist \ + pgmkernel \ + pgmmake \ + pgmmedian \ + pgmminkowski \ + pgmmorphconv \ + pgmnoise \ + pgmnorm \ + pgmoil \ + pgmramp \ + pgmslice \ + pgmtexture \ + pgmtofs \ + pgmtolispm \ + pgmtopbm \ + pgmtopgm \ + pgmtoppm \ + pi1toppm \ + pi3topbm \ + picttoppm \ + pjtoppm \ + pktopbm \ + pngtopam \ + pngtopnm \ + pnmalias \ + pnmarith \ + pnmcat \ + pnmcolormap \ + pnmcomp \ + pnmconvol \ + pnmcrop \ + pnmcut \ + pnmdepth \ + pnmenlarge \ + pnmfile \ + pnmflip \ + pnmgamma \ + pnmhisteq \ + pnmhistmap \ + pnmindex \ + pnminterp \ + pnminvert \ + pnmmercator \ + pnmmontage \ + pnmnlfilt \ + pnmnorm \ + pnmpad \ + pnmpaste \ + pnmpsnr \ + pnmremap \ + pnmrotate \ + pnmscale \ + pnmscalefixed \ + pnmshear \ + pnmsmooth \ + pnmsplit \ + pnmstitch \ + pnmtile \ + pnmtoddif \ + pnmtofiasco \ + pnmtofits \ + pnmtojbig \ + pnmtojpeg \ + pnmtopalm \ + pnmtopclxl \ + pnmtopng \ + pnmtopnm \ + pnmtops \ + pnmtorast \ + pnmtorle \ + pnmtosgi \ + pnmtosir \ + pnmtotiff \ + pnmtotiffcmyk \ + pnmtoxwd \ + ppm3d \ + ppmbrighten \ + ppmchange \ + ppmcie \ + ppmcolormask \ + ppmcolors \ + ppmdcfont \ + ppmddumpfont \ + ppmdim \ + ppmdist \ + ppmdither \ + ppmdmkfont \ + ppmdraw \ + ppmflash \ + ppmforge \ + ppmglobe \ + ppmhist \ + ppmlabel \ + ppmmake \ + ppmmix \ + ppmnorm \ + ppmntsc \ + ppmpat \ + ppmrelief \ + ppmrough \ + ppmshift \ + ppmspread \ + ppmtoacad \ + ppmtoapplevol \ + ppmtoarbtxt \ + ppmtoascii \ + ppmtobmp \ + ppmtoeyuv \ + ppmtogif \ + ppmtoicr \ + ppmtoilbm \ + ppmtojpeg \ + ppmtoleaf \ + ppmtolj \ + ppmtomitsu \ + ppmtompeg \ + ppmtoneo \ + ppmtopcx \ + ppmtopgm \ + ppmtopi1 \ + ppmtopict \ + ppmtopj \ + ppmtopjxl \ + ppmtoppm \ + ppmtopuzz \ + ppmtorgb3 \ + ppmtosixel \ + ppmtospu \ + ppmtoterm \ + ppmtouil \ + ppmtowinicon \ + ppmtoxpm \ + ppmtoyuv \ + ppmtoyuvsplit \ + ppmtv \ + ppmwheel \ + psidtopgm \ + pstopnm \ + qrttoppm \ + rasttopnm \ + rawtopgm \ + rawtoppm \ + rgb3toppm \ + rlatopam \ + rletopnm \ + sbigtopgm \ + sgitopnm \ + sirtopnm \ + sldtoppm \ + spctoppm \ + spottopgm \ + sputoppm \ + srftopam \ + sunicontopnm \ + svgtopam \ + tgatoppm \ + thinkjettopbm \ + tifftopnm \ + wbmptopbm \ + winicontopam \ + winicontoppm \ + xbmtopbm \ + ximtoppm \ + xpmtoppm \ + xvminitoppm \ + xwdtopnm \ + ybmtopbm \ + yuvsplittoppm \ + yuvtoppm \ + zeisstopnm \ +" + +for i in $ordinary_testprogs do ${PBM_TESTPREFIX}$i --version 2>&1 | \ egrep -v \ @@ -110,7 +394,7 @@ ${PBM_TESTPREFIX}fiascotopnm -v 2> /dev/null -# Test manweb and pnmmargin, which support --help. +# Test manweb and pnmmargin, which have --help. ${PBM_TESTPREFIX}manweb --help > /dev/null testExitStatus manweb 0 $? @@ -159,6 +443,18 @@ ${PBM_TESTPREFIX}ppmfade -f /dev/zero -base /dev/null > /dev/null 2> /dev/null +# Test pamstretch-gen and pcdovtoppm with no input. +# These two programs write a usage message on standout in this case. +# Exit status should be 1. + +for i in pamstretch-gen pcdovtoppm + do + ${PBM_TESTPREFIX}$i > /dev/zero + testExitStatus $i 1 $? + done + + + # We do not test hpcdtoppm. # This is a stand-in which announces the absence of the real hpcdtoppm. # TODO: Provide for the case in which the real hpcdtoppm is encountered. diff --git a/test/g3-roundtrip.test b/test/g3-roundtrip.test index c5c21fa0..e00997f6 100755 --- a/test/g3-roundtrip.test +++ b/test/g3-roundtrip.test @@ -3,11 +3,13 @@ # Also requires: pnmcrop ${PBM_TESTPREFIX}pbmtog3 -nofixedwidth testgrid.pbm | \ -${PBM_TESTPREFIX}g3topbm -width=14 | diff - testgrid.pbm ; echo $? +${PBM_TESTPREFIX}g3topbm -width=14 | cmp -s - testgrid.pbm +echo $? ${PBM_TESTPREFIX}pbmtog3 -nofixedwidth -reversebits testgrid.pbm | \ -${PBM_TESTPREFIX}g3topbm -width=14 -reversebits | diff - testgrid.pbm ; echo $? +${PBM_TESTPREFIX}g3topbm -width=14 -reversebits | cmp -s - testgrid.pbm +echo $? ${PBM_TESTPREFIX}pbmtog3 testgrid.pbm | \ ${PBM_TESTPREFIX}g3topbm | ${PBM_BINPREFIX}pnmcrop -white -right -bottom | \ - diff - testgrid.pbm ; echo $? + cmp -s - testgrid.pbm ; echo $? diff --git a/test/gem-roundtrip.test b/test/gem-roundtrip.test index 9548637a..05f1d28c 100755 --- a/test/gem-roundtrip.test +++ b/test/gem-roundtrip.test @@ -1,6 +1,6 @@ #! /bin/bash # This script tests: pbmtogem gemtopbm -# Also requires: +# Also requires: gemtopnm # Should print 2425386270 41, cksum of testgrid.pbm ${PBM_TESTPREFIX}pbmtogem testgrid.pbm | ${PBM_TESTPREFIX}gemtopbm | cksum diff --git a/test/gif-quant-roundtrip.ok b/test/gif-quant-roundtrip.ok new file mode 100644 index 00000000..573541ac --- /dev/null +++ b/test/gif-quant-roundtrip.ok @@ -0,0 +1 @@ +0 diff --git a/test/gif-quant-roundtrip.test b/test/gif-quant-roundtrip.test new file mode 100644 index 00000000..5e093b29 --- /dev/null +++ b/test/gif-quant-roundtrip.test @@ -0,0 +1,15 @@ +#! /bin/bash +# This script tests: giftopnm pamtogif pnmquant +# Also requires: pnmcolormap pnmremap + +# Should print 0 + +colors=15 # any value between 2 - 256 works + +${PBM_TESTPREFIX}pnmquant $colors testimg.ppm > ${tmpdir}/quant.ppm && +${PBM_TESTPREFIX}pamtogif ${tmpdir}/quant.ppm | ${PBM_TESTPREFIX}giftopnm | \ + cmp -s - ${tmpdir}/quant.ppm > /dev/null +echo $? + +rm ${tmpdir}/quant.ppm + diff --git a/test/gif-roundtrip.ok b/test/gif-roundtrip.ok index 011ebd3a..cf95bb37 100644 --- a/test/gif-roundtrip.ok +++ b/test/gif-roundtrip.ok @@ -1,3 +1,4 @@ 2871603838 33838 1926073387 101484 2425386270 41 +P1 4 1 0101 \ No newline at end of file diff --git a/test/gif-roundtrip.test b/test/gif-roundtrip.test index 73246705..a32d574a 100755 --- a/test/gif-roundtrip.test +++ b/test/gif-roundtrip.test @@ -1,6 +1,6 @@ #! /bin/bash # This script tests: giftopnm pamtogif -# Also requires: ppmtopgm ppmtorgb3 rgb3toppm +# Also requires: ppmtopgm ppmtorgb3 rgb3toppm pbmmake # Test 1. Should produce 2871603838 33838 # which is the result of ppmtopgm testimg.ppm | cksum @@ -28,3 +28,12 @@ rm ${tmpdir}/testimg.{ppm,red,grn,blu} ${tmpdir}/out.{red,grn} # Test 3. Should produce 2425386270 41 ${PBM_TESTPREFIX}pamtogif testgrid.pbm | ${PBM_TESTPREFIX}giftopnm | cksum + +# Test 4. +# In this gif file the code length changes after the last image data. +# Image data: 3 bits, end code 4 bits. +# Should produce P1 4 1 0 1 0 1 + +${PBM_BINPREFIX}pbmmake -g 4 1 | \ + ${PBM_TESTPREFIX}pamtogif | ${PBM_TESTPREFIX}giftopnm -plain | \ + tr '\n' ' ' diff --git a/test/pad-crop-roundtrip.test b/test/pad-crop-roundtrip.test index 532b6cbd..2b3c777b 100755 --- a/test/pad-crop-roundtrip.test +++ b/test/pad-crop-roundtrip.test @@ -1,6 +1,6 @@ #! /bin/bash # This script tests: pnmcrop pnmmargin -# Also requires: +# Also requires: pnmpad ${PBM_TESTPREFIX}pnmmargin -white 10 testimg.ppm | \ ${PBM_TESTPREFIX}pnmcrop | cksum diff --git a/test/pamchannel.ok b/test/pamchannel.ok new file mode 100644 index 00000000..79317f03 --- /dev/null +++ b/test/pamchannel.ok @@ -0,0 +1,3 @@ +1571496937 33838 +394856971 33838 +3164158573 33838 diff --git a/test/pamchannel.test b/test/pamchannel.test new file mode 100644 index 00000000..f83dd3b6 --- /dev/null +++ b/test/pamchannel.test @@ -0,0 +1,32 @@ +#! /bin/bash +# This script tests: pamchannel +# Also requires: pamtopnm + + +# Extract planes one by one. +# Convert output to pgm to make it identical to ppmtorgb3 output. + +# $ ppmtorgb3 testimg.ppm ; cksum testimg.{red,grn,blu} +# +# 1571496937 33838 testimg.red +# 394856971 33838 testimg.grn +# 3164158573 33838 testimg.blu + + +# Test 1. red channel +# Should produce 1571496937 33838 + +${PBM_TESTPREFIX}pamchannel -infile testimg.ppm 0 | \ + ${PBM_BINPREFIX}pamtopnm --assume | cksum + +# Test 2. green channel +# Should produce 394856971 33838 + +${PBM_TESTPREFIX}pamchannel -infile testimg.ppm 1 | \ + ${PBM_BINPREFIX}pamtopnm --assume | cksum + +# Test 3. blue channel +# Should produce 3164158573 33838 + +${PBM_TESTPREFIX}pamchannel -infile testimg.ppm 2 | \ + ${PBM_BINPREFIX}pamtopnm --assume | cksum diff --git a/test/pamslice-roundtrip.test b/test/pamslice-roundtrip.test index fd125500..d7ca657d 100755 --- a/test/pamslice-roundtrip.test +++ b/test/pamslice-roundtrip.test @@ -1,6 +1,6 @@ #! /bin/bash # This script tests: pamslice -# Also requires: pnmtopnm +# Also requires: pnmtopnm pamtopnm # Slice rows, one by one, out of testgrid.pbm. # Add header and reconstruct pbm image. diff --git a/test/pbmclean.test b/test/pbmclean.test index 05fe9338..69081ff9 100755 --- a/test/pbmclean.test +++ b/test/pbmclean.test @@ -1,6 +1,6 @@ #! /bin/bash # This script tests: pbmclean -# Also requires: pbmmake pbmpage pnmmargin +# Also requires: pbmmake pbmpage pnmmargin pnmpad ${PBM_BINPREFIX}pbmmake -g 3 3 | ${PBM_BINPREFIX}pnmmargin -black 2 \ >${tmpdir}/test.pbm diff --git a/test/pbmminkowski.test b/test/pbmminkowski.test index d0376e9e..ccb882b1 100755 --- a/test/pbmminkowski.test +++ b/test/pbmminkowski.test @@ -1,6 +1,6 @@ #! /bin/bash # This script tests: pbmminkowski -# Also requires: pbmmake pnmmargin +# Also requires: pbmmake pnmmargin pnmpad ${PBM_BINPREFIX}pbmmake -w 1 1 | ${PBM_BINPREFIX}pnmmargin -b 1 | \ ${PBM_TESTPREFIX}pbmminkowski diff --git a/test/pgmcrater.ok b/test/pgmcrater.ok new file mode 100644 index 00000000..aa2592c9 --- /dev/null +++ b/test/pgmcrater.ok @@ -0,0 +1 @@ +3828822912 65551 diff --git a/test/pgmcrater.test b/test/pgmcrater.test new file mode 100644 index 00000000..e8df9a23 --- /dev/null +++ b/test/pgmcrater.test @@ -0,0 +1,29 @@ +#! /bin/bash +# This script tests: pgmcrater +# Also requires: pgmnoise + +# This test is sensitive to differences in floating point math. +# With GCC slight results start to appear with -number 75 in +# the following test. Compiler type (GCC, Clang, etc.), +# SSE math (by default off with x86 32 bit on with x86 64 bit) +# and --ffast-math are factors. + +# We hope that by keeping the number of craters small the +# difference will not show up. +# +# The slight differences in the image file are not discernable by +# the naked eye. + +${PBM_BINPREFIX}pgmnoise --testrandom --quiet +case $? in + 81) + # Should print: 3828822912 65551 + ${PBM_TESTPREFIX}pgmcrater -quiet -number 25 -randomseed 1 | cksum + ;; + + 8[02-9] | 90) + echo "Skipping: random number generator is not glibc." 1>&2 + exit 80;; + + *) exit 1;; # pgmnoise --testrandom failed +esac diff --git a/test/pgmnoise.ok b/test/pgmnoise.ok new file mode 100644 index 00000000..138218c2 --- /dev/null +++ b/test/pgmnoise.ok @@ -0,0 +1 @@ +2005134911 10015 diff --git a/test/pgmnoise.test b/test/pgmnoise.test new file mode 100644 index 00000000..8ee2fccc --- /dev/null +++ b/test/pgmnoise.test @@ -0,0 +1,22 @@ +#! /bin/bash +# This script tests: pgmnoise +# Also requires: + +# We first check whether random number generator is glibc rand(). +# If not, this test is skipped. + +${PBM_TESTPREFIX}pgmnoise --testrandom + +case $? in + 81) + # Should print: 1663614689 10015 + ${PBM_TESTPREFIX}pgmnoise --randomseed=0 100 100 | cksum ;; + + # Any additional tests go here. + + 8[02-9] | 90) + echo "Skipping: random number generator is not glibc." 1>&2 + exit 80;; + + *) exit 1;; # pgmnoise --testrandom failed +esac diff --git a/test/pnm-plain-roundtrip.test b/test/pnm-plain-roundtrip.test index eab0b0d7..86dbb2fb 100755 --- a/test/pnm-plain-roundtrip.test +++ b/test/pnm-plain-roundtrip.test @@ -1,6 +1,6 @@ #! /bin/bash # This script tests: pnmtopnm -# Also requires: +# Also requires: pamtopnm ${PBM_TESTPREFIX}pnmtopnm -plain testimg.ppm | \ ${PBM_TESTPREFIX}pnmtopnm | cksum diff --git a/test/pnmremap1.ok b/test/pnmremap1.ok index a6cfc2bd..07954684 100644 --- a/test/pnmremap1.ok +++ b/test/pnmremap1.ok @@ -1 +1 @@ -2667816854 101482 +3602410851 101482 diff --git a/test/pnmtile.test b/test/pnmtile.test index b0c14ca6..0f9d91df 100755 --- a/test/pnmtile.test +++ b/test/pnmtile.test @@ -7,6 +7,7 @@ ${PBM_TESTPREFIX}pnmtile 40 50 testgrid.pbm | cksum ${PBM_TESTPREFIX}pnmtile 454 298 testimg.ppm > ${tmpdir}/testimg4.ppm && ${PBM_BINPREFIX}pnmcat -lr testimg.ppm testimg.ppm > ${tmpdir}/testimg2.ppm && ${PBM_BINPREFIX}pnmcat -tb ${tmpdir}/testimg2.ppm ${tmpdir}/testimg2.ppm | \ -diff - ${tmpdir}/testimg4.ppm && echo $? +cmp -s - ${tmpdir}/testimg4.ppm +echo $? rm ${tmpdir}/testimg{2,4}.ppm diff --git a/test/pnmtopnm-plain.test b/test/pnmtopnm-plain.test index 3cbd5752..ff211e40 100755 --- a/test/pnmtopnm-plain.test +++ b/test/pnmtopnm-plain.test @@ -1,6 +1,6 @@ #! /bin/bash # This script tests: pnmtopnm -# Also requires: pgmtopgm ppmtoppm +# Also requires: pgmtopgm ppmtoppm pamtopnm ${PBM_TESTPREFIX}pnmtopnm -plain testgrid.pbm diff --git a/test/ppmcie.out-64 b/test/ppmcie.out-64 deleted file mode 100644 index 3d8ca13a..00000000 --- a/test/ppmcie.out-64 +++ /dev/null @@ -1 +0,0 @@ -2583941766 786447 diff --git a/test/ppmdfont.test b/test/ppmdfont.test index e973297b..027b8f2c 100755 --- a/test/ppmdfont.test +++ b/test/ppmdfont.test @@ -7,3 +7,13 @@ ${PBM_TESTPREFIX}ppmdmkfont | ${PBM_TESTPREFIX}ppmddumpfont 2>&1 | cksum # Test 2. Should produce: 2845495212 75033 ${PBM_TESTPREFIX}ppmdmkfont | ${PBM_TESTPREFIX}ppmdcfont | cksum + +# There is a strange glitch in output when ppmdcfont is compiled by clang: +# 3171,3173c3171,3173 +# < 0x01, +# < 95, +# < 32 +# --- +# > 0x00, +# > 0, +# > 0 diff --git a/test/ppmdim.test b/test/ppmdim.test index b8158d63..e2a1b0df 100755 --- a/test/ppmdim.test +++ b/test/ppmdim.test @@ -1,6 +1,6 @@ #! /bin/bash # This script tests: ppmdim -# Also requires: pamfunc pnmarith pamsumm +# Also requires: pamfunc pnmarith pamarith pamsumm # Compare ppmdim and pamfunc with various dim factors # Due to the difference in rounding methods, pamfunc produces slightly diff --git a/test/ppmforge.ok b/test/ppmforge.ok new file mode 100644 index 00000000..e4a4c9e2 --- /dev/null +++ b/test/ppmforge.ok @@ -0,0 +1 @@ +3634219838 196623 diff --git a/test/ppmforge.test b/test/ppmforge.test new file mode 100644 index 00000000..014dc543 --- /dev/null +++ b/test/ppmforge.test @@ -0,0 +1,20 @@ +#! /bin/bash +# This script tests: ppmforge +# Also requires: pgmnoise + +# Use small x y values to avoid floating point issues. + + +${PBM_BINPREFIX}pgmnoise --testrandom --quiet +case $? in + 81) + # Test 1: Should print: 3634219838 196623 + ${PBM_TESTPREFIX}ppmforge -night -seed 1 | cksum + ;; + + 8[02-9] | 90) + echo "Skipping: random number generator is not glibc." 1>&2 + exit 80;; + + *) exit 1;; # pgmnoise --testrandom failed +esac diff --git a/test/ppmhist.test b/test/ppmhist.test index b849e72d..55cbeac9 100755 --- a/test/ppmhist.test +++ b/test/ppmhist.test @@ -1,6 +1,6 @@ #! /bin/bash # This script tests: ppmhist -# Also requires: pgmhist pgmramp +# Also requires: pgmramp ${PBM_BINPREFIX}pgmramp -maxval=8 -lr 8 2 | ${PBM_TESTPREFIX}ppmhist ${PBM_TESTPREFIX}ppmhist -map -sort=rgb testimg.ppm | cksum diff --git a/test/ppmmake.test b/test/ppmmake.test index 02e747ab..da23095c 100755 --- a/test/ppmmake.test +++ b/test/ppmmake.test @@ -2,8 +2,6 @@ # This script tests: ppmmake # Also requires: -#export RGBDEF=/usr/share/emacs/23.2/etc/rgb.txt - ${PBM_TESTPREFIX}ppmmake rgb:ff/80/80 50 100 -maxval=5 | cksum ${PBM_TESTPREFIX}ppmmake red 50 50 | cksum diff --git a/test/ppmmix.test b/test/ppmmix.test index 0f9deedc..451aa8be 100755 --- a/test/ppmmix.test +++ b/test/ppmmix.test @@ -25,5 +25,4 @@ ${PBM_BINPREFIX}pnminvert testimg.ppm | ${PBM_TESTPREFIX}ppmmix .5 \ testimg.ppm - | tee ${tmpdir}/a3.ppm | \ ${PBM_BINPREFIX}pamsumm -brief -max && ${PBM_BINPREFIX}pamsumm -brief -min ${tmpdir}/a3.ppm && - rm ${tmpdir}/a3.ppm diff --git a/test/ppmpat.test b/test/ppmpat.test index 0fe9f25a..669cf17c 100755 --- a/test/ppmpat.test +++ b/test/ppmpat.test @@ -1,28 +1,35 @@ #! /bin/bash # This script tests: ppmpat -# Also requires: +# Also requires: pgmnoise -# TODO 1: Write tests for squig and poles. It appears that they are +# TODO: Write tests for squig and poles. It appears that they are # sensitive to differences in floating point math. -# TODO 2: Skip this test if the pseudo-random number generator is -# not glibc's rand(). +${PBM_BINPREFIX}pgmnoise --testrandom --quiet +case $? in + 81) + # Test 1. Should print: 4008533639 781 + ${PBM_TESTPREFIX}ppmpat --randomseed=0 -g2 16 16 | cksum + # Test 2. Should print: 2448908863 9613 + ${PBM_TESTPREFIX}ppmpat --randomseed=0 -g2 64 50 | cksum -# Test 1. Should print: 4008533639 781 -${PBM_TESTPREFIX}ppmpat --randomseed=0 -g2 16 16 | cksum + # Test 3. Should print: 2698433077 1549 + ${PBM_TESTPREFIX}ppmpat --randomseed=0 -madras 32 16 | cksum -# Test 2. Should print: 2448908863 9613 -${PBM_TESTPREFIX}ppmpat --randomseed=0 -g2 64 50 | cksum + # Test 4. Should print: 3705929501 781 + ${PBM_TESTPREFIX}ppmpat --randomseed=0 -tartan 16 16 | cksum -# Test 3. Should print: 2698433077 1549 -${PBM_TESTPREFIX}ppmpat --randomseed=0 -madras 32 16 | cksum + # Test 5. Should print: 2219119109 36015 + ${PBM_TESTPREFIX}ppmpat --randomseed=0 -camo 100 120 | cksum -# Test 4. Should print: 3705929501 781 -${PBM_TESTPREFIX}ppmpat --randomseed=0 -tartan 16 16 | cksum + # Test 6. Should print: 3436846137 16813 + ${PBM_TESTPREFIX}ppmpat --randomseed=0 -anticamo 80 70 | cksum + ;; -# Test 5. Should print: 2219119109 36015 -${PBM_TESTPREFIX}ppmpat --randomseed=0 -camo 100 120 | cksum + 8[02-9] | 90) + echo "Skipping: random number generator is not glibc." 1>&2 + exit 80;; -# Test 6. Should print: 3436846137 16813 -${PBM_TESTPREFIX}ppmpat --randomseed=0 -anticamo 80 70 | cksum + *) exit 1;; # pgmnoise --testrandom failed +esac diff --git a/test/ppmrough.ok b/test/ppmrough.ok new file mode 100644 index 00000000..83643849 --- /dev/null +++ b/test/ppmrough.ok @@ -0,0 +1 @@ +378403602 30015 diff --git a/test/ppmrough.test b/test/ppmrough.test new file mode 100644 index 00000000..1de8dd1b --- /dev/null +++ b/test/ppmrough.test @@ -0,0 +1,18 @@ +#! /bin/bash +# This script tests: ppmrough +# Also requires: pgmnoise + + +${PBM_BINPREFIX}pgmnoise --testrandom --quiet +case $? in + 81) + # Should print: 378403602 30015 + ${PBM_TESTPREFIX}ppmrough -randomseed 1 | cksum + ;; + + 8[02-9] | 90) + echo "Skipping: random number generator is not glibc." 1>&2 + exit 80;; + + *) exit 1;; # pgmnoise --testrandom failed +esac diff --git a/test/ps-roundtrip.ok b/test/ps-roundtrip.ok index 67f7a1fe..a267f7eb 100644 --- a/test/ps-roundtrip.ok +++ b/test/ps-roundtrip.ok @@ -1,2 +1,3 @@ 1926073387 101484 2425386270 41 +1386192571 507420 diff --git a/test/ps-roundtrip.test b/test/ps-roundtrip.test index 6d6935af..60ecf60a 100755 --- a/test/ps-roundtrip.test +++ b/test/ps-roundtrip.test @@ -1,21 +1,38 @@ #! /bin/bash # This script tests: pnmtops pstopnm -# Also requires: pnmtopnm +# Also requires: pnmtopnm pamtopnm gs +# Test 1. Should print: 1926073387 101484 ${PBM_TESTPREFIX}pnmtops -nocenter -equalpixels -dpi 72 -noturn testimg.ppm \ > ${tmpdir}/testimg.ps -xysize=`awk '/BoundingBox/ {print "-xsize="$4,"-ysize="$5}' \ +xysize1=`awk '/BoundingBox/ {print "-xsize="$4,"-ysize="$5}' \ ${tmpdir}/testimg.ps` -${PBM_TESTPREFIX}pstopnm -xborder=0 -yborder=0 $xysize -stdout \ +${PBM_TESTPREFIX}pstopnm -xborder=0 -yborder=0 $xysize1 -stdout \ -quiet ${tmpdir}/testimg.ps | \ ${PBM_BINPREFIX}pnmtopnm | cksum -${PBM_TESTPREFIX}pnmtops -nocenter -equalpixels -dpi 72 -noturn testgrid.pbm \ - > ${tmpdir}/testgrid.ps -xysize=`awk '/BoundingBox/ {print "-xsize="$4,"-ysize="$5}' \ + +# Test 2. Should print: 2425386270 41 +${PBM_TESTPREFIX}pnmtops -nocenter -equalpixels -dpi 72 -noturn \ + testgrid.pbm > ${tmpdir}/testgrid.ps && +xysize2=`awk '/BoundingBox/ {print "-xsize="$4,"-ysize="$5}' \ ${tmpdir}/testgrid.ps` -${PBM_TESTPREFIX}pstopnm -xborder=0 -yborder=0 $xysize -stdout \ +${PBM_TESTPREFIX}pstopnm -xborder=0 -yborder=0 $xysize2 -stdout \ -quiet ${tmpdir}/testgrid.ps -pbm | \ ${PBM_BINPREFIX}pnmtopnm | cksum -rm ${tmpdir}/testgrid.ps ${tmpdir}/testimg.ps + +#Test 3. Should print: 1386192571 507420 +cat testimg.ppm testimg.ppm testimg.ppm testgrid.pbm testgrid.pbm | \ +${PBM_TESTPREFIX}pnmtops -nocenter -equalpixels -dpi 72 -noturn -setpage \ + > ${tmpdir}/testimg5.ps +xysize3=`awk '/BoundingBox/ {print "-xsize="$4,"-ysize="$5 ; exit}' \ + ${tmpdir}/testimg5.ps` +${PBM_TESTPREFIX}pstopnm -xborder=0 -yborder=0 $xysize3 \ + -stdout ${tmpdir}/testimg5.ps | \ + ${PBM_BINPREFIX}pnmtopnm | cksum + + +#rm ${tmpdir}/testgrid.ps ${tmpdir}/testimg.ps ${tmpdir}/testimg5.ps +rm ${tmpdir}/testimg.ps ${tmpdir}/testimg5.ps +mv ${tmpdir}/testgrid.ps /tmp diff --git a/test/rgb3-roundtrip.ok b/test/rgb3-roundtrip.ok index 745a7c5f..64da849d 100644 --- a/test/rgb3-roundtrip.ok +++ b/test/rgb3-roundtrip.ok @@ -1,4 +1,5 @@ 1926073387 101484 +3744829044 101514 2425386270 41 0 0 diff --git a/test/rgb3-roundtrip.test b/test/rgb3-roundtrip.test index 9cc45499..d3575b95 100755 --- a/test/rgb3-roundtrip.test +++ b/test/rgb3-roundtrip.test @@ -11,6 +11,9 @@ ${PBM_TESTPREFIX}ppmtorgb3 ${tmpdir}/testimg.ppm && ${PBM_TESTPREFIX}rgb3toppm ${tmpdir}/testimg.red ${tmpdir}/testimg.grn \ ${tmpdir}/testimg.blu | cksum +cat ${tmpdir}/testimg.red ${tmpdir}/testimg.grn ${tmpdir}/testimg.blu | \ + cksum + rm ${tmpdir}/testimg.{ppm,red,grn,blu} cp testgrid.pbm ${tmpdir} && @@ -23,9 +26,9 @@ ${PBM_TESTPREFIX}rgb3toppm ${tmpdir}/testgrid.red ${tmpdir}/testgrid.grn \ # With PGM or PBM input, the three monochrome planes should be # identical. Test for this. -diff ${tmpdir}/testgrid.red ${tmpdir}/testgrid.grn ; echo $? -diff ${tmpdir}/testgrid.grn ${tmpdir}/testgrid.blu ; echo $? -${PBM_BINPREFIX}pgmtopgm < testgrid.pbm | diff - ${tmpdir}/testgrid.red; +cmp -s ${tmpdir}/testgrid.red ${tmpdir}/testgrid.grn ; echo $? +cmp -s ${tmpdir}/testgrid.grn ${tmpdir}/testgrid.blu ; echo $? +${PBM_BINPREFIX}pgmtopgm < testgrid.pbm | cmp -s - ${tmpdir}/testgrid.red echo $? rm ${tmpdir}/testgrid.{pbm,red,grn,blu} diff --git a/test/winicon-roundtrip.ok b/test/winicon-roundtrip.ok new file mode 100644 index 00000000..8334ff4e --- /dev/null +++ b/test/winicon-roundtrip.ok @@ -0,0 +1,2 @@ +71846281 6925 +326197919 137 diff --git a/test/winicon-roundtrip.test b/test/winicon-roundtrip.test new file mode 100644 index 00000000..f5f2926e --- /dev/null +++ b/test/winicon-roundtrip.test @@ -0,0 +1,13 @@ +#! /bin/bash +# This script tests: pamtowinicon winicontopam +# Also requires: pnmcut pnmtile pamtopnm ppmtopgm pgmtopbm + +${PBM_BINPREFIX}pnmcut --left=30 --width=48 --height=48 testimg.ppm | \ +${PBM_TESTPREFIX}pamtowinicon | ${PBM_TESTPREFIX}winicontopam | \ + ${PBM_BINPREFIX}pamtopnm | cksum + +${PBM_BINPREFIX}pnmtile 32 32 testgrid.pbm | \ +${PBM_TESTPREFIX}pamtowinicon | ${PBM_TESTPREFIX}winicontopam | \ + ${PBM_BINPREFIX}pamtopnm | ${PBM_BINPREFIX}ppmtopgm | \ + ${PBM_BINPREFIX}pgmtopbm -th -val=0.5 | cksum + diff --git a/test/yuv-roundtrip.test b/test/yuv-roundtrip.test index c160c480..697d86aa 100755 --- a/test/yuv-roundtrip.test +++ b/test/yuv-roundtrip.test @@ -4,5 +4,6 @@ # Should produce 1904478375 253455 -${PBM_BINPREFIX}pamgradient cyan yellow red green 352 240 | \ +${PBM_BINPREFIX}pamgradient rgb:00/ff/ff rgb:ff/ff/00 \ + rgb:ff/00/00 rgb:00/ff/00 352 240 | \ ${PBM_TESTPREFIX}ppmtoyuv | ${PBM_TESTPREFIX}yuvtoppm 352 240 | cksum diff --git a/version.mk b/version.mk index 397cdccb..70979cfd 100644 --- a/version.mk +++ b/version.mk @@ -1,4 +1,4 @@ NETPBM_MAJOR_RELEASE = 10 -NETPBM_MINOR_RELEASE = 62 -NETPBM_POINT_RELEASE = 9 +NETPBM_MINOR_RELEASE = 63 +NETPBM_POINT_RELEASE = 0 -- cgit 1.4.1