diff options
author | giraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8> | 2017-09-30 18:51:40 +0000 |
---|---|---|
committer | giraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8> | 2017-09-30 18:51:40 +0000 |
commit | 8b2ff6a4ae59c1ab37af590a7340656f93dbddee (patch) | |
tree | 36477ce3fc89409c484ee53afa10dac9685e7700 | |
parent | eb9c9ea7fe9a04f1977bdd040c173c84f841d431 (diff) | |
download | netpbm-mirror-8b2ff6a4ae59c1ab37af590a7340656f93dbddee.tar.gz netpbm-mirror-8b2ff6a4ae59c1ab37af590a7340656f93dbddee.tar.xz netpbm-mirror-8b2ff6a4ae59c1ab37af590a7340656f93dbddee.zip |
Copy Development as new Advanced
git-svn-id: http://svn.code.sf.net/p/netpbm/code/advanced@3083 9d0c8265-081b-0410-96cb-a4ca84ce46f8
38 files changed, 1325 insertions, 708 deletions
diff --git a/GNUmakefile b/GNUmakefile index 6f9eb41c..e5914648 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -446,6 +446,7 @@ deb: CHECK_VARS = \ BUILDDIR=$(BUILDDIR) \ RGBDEF=$(RGBDEF) \ + PALMMAPDIR=$(PALMMAPDIR) \ BUILD_FIASCO=$(BUILD_FIASCO) \ JASPERLIB="$(JASPERLIB)" \ JBIGLIB="$(JBIGLIB)" \ @@ -512,6 +513,7 @@ colon :=: check-tree : PBM_TEST_PATH := $(subst $(space),$(colon),$(strip $(RBINDIRS))) check-tree : PBM_LIBRARY_PATH ?= $(BUILDDIR)/lib check-tree : RGBDEF ?= $(SRCDIR)/lib/rgb.txt +check-tree : PALMMAPDIR ?= $(SRCDIR)/converter/other/pnmtopalm # Create RESULTDIR. @@ -544,6 +546,7 @@ check-tree: $(TESTRANDOM) resultdir-backup check-package : PBM_TEST_PATH := $(PKGDIR)/bin check-package : PBM_LIBRARY_PATH := $(PKGDIR)/lib check-package : RGBDEF ?= $(PKGDIR)/misc/rgb.txt +check-package : PALMMAPDIR ?= $(PKGDIR)/misc/ check: check-package check-package: $(TESTRANDOM) resultdir-backup @@ -556,6 +559,7 @@ check-package: $(TESTRANDOM) resultdir-backup # Check after install +check-install: PALMMAPDIR ?= "" check-install: $(TESTRANDOM) resultdir-backup cd $(RESULTDIR); \ $(CHECK_VARS) \ diff --git a/buildtools/debian/README b/buildtools/debian/README index 2cf921ce..7cefb249 100644 --- a/buildtools/debian/README +++ b/buildtools/debian/README @@ -54,8 +54,8 @@ indicate you don't have them, and the build will simply omit some parts. For example, if you don't install libx11-dev, the Netpbm build process will not build the 'pamx' program. - libjpeg62-dev - libpng12-0-dev + libjpeg-dev + libpng-dev libtiff5-dev libx11-dev libxml2-dev @@ -79,9 +79,9 @@ The following Debian packages are the known prerequisites for running Netpbm (and the package created by 'mkdeb' knows this). libc6 - libjpeg8 + libjpeg62 or libjpeg8 libpng12-0 - libsvga1 + libsvga1 (available only on older systems) libtiff5 libx11-6 zlib1g diff --git a/buildtools/manpage.mk b/buildtools/manpage.mk index b0f7668c..ef1a1039 100644 --- a/buildtools/manpage.mk +++ b/buildtools/manpage.mk @@ -15,7 +15,7 @@ else MAKEFILE_DIR := $(dir $(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST))) endif -# Python script makeman should be in the same directory. +# Program 'makeman' should be in the same directory. MAKEMAN ?= $(MAKEFILE_DIR)makeman # Install location of manpages. @@ -33,10 +33,9 @@ else endif endif -# In the past the following default value was used. -# It works if you've done a SVN checkout for netpbm and userguide in the -# same directory, and you are working in a subdirectory of netpbm, say -# ./buildtools . +# In the past, the following default value was used. It works if you've done +# a Subversion checkout for source code and userguide in the same directory, +# and you are working in a subdirectory of netpbm, say ./buildtools . # USERGUIDE = ../../userguide # The files that don't get converted to manual pages. diff --git a/converter/other/ipdb.c b/converter/other/ipdb.c index 882bf187..5e4dc82e 100644 --- a/converter/other/ipdb.c +++ b/converter/other/ipdb.c @@ -283,8 +283,8 @@ ipdb_image_alloc(const char * const name, if (w != 0 && h != 0) { MALLOCARRAY(imgP->data, w * h); - if (imgP->data) { - memset(imgP->data, 0, sizeof(*(imgP->data)) * w * h); + if (imgP->data != NULL) { + memset(imgP->data, 0, sizeof(*(imgP->data)) * w * h); } else failed = true; } diff --git a/converter/other/pamtopdbimg.c b/converter/other/pamtopdbimg.c index 6454292e..ce2f7659 100644 --- a/converter/other/pamtopdbimg.c +++ b/converter/other/pamtopdbimg.c @@ -38,6 +38,7 @@ #include "mallocvar.h" #include "nstring.h" #include "shhopt.h" +#include "runlength.h" #include "pam.h" #include "ipdb.h" @@ -128,6 +129,8 @@ parseCommandLine(int argc, const char ** argv, cmdlineP->inputFileName = argv[1]; else pm_error("Program takes at most one argument: input file name"); + + free(option_def); } @@ -238,91 +241,6 @@ textWrite(TEXT * const textP, -typedef struct { - unsigned int match; - uint8_t buf[128]; - int mode; - size_t len; - size_t used; - uint8_t * p; -} RLE; -#define MODE_MATCH 0 -#define MODE_LIT 1 -#define MODE_NONE 2 - -#define reset(r) { \ - (r)->match = 0xffff; \ - (r)->mode = MODE_NONE; \ - (r)->len = 0; \ - } - - - -static void -putMatch(RLE * const rleP, - size_t const n) { - - *rleP->p++ = 0x80 + n - 1; - *rleP->p++ = rleP->match; - rleP->used += 2; - reset(rleP); -} - - - -static void -putLit(RLE * const rleP, - size_t const n) { - - *rleP->p++ = n - 1; - rleP->p = (uint8_t *)memcpy(rleP->p, rleP->buf, n) + n; - rleP->used += n + 1; - reset(rleP); -} - - - -static size_t -compress(const uint8_t * const inData, - size_t const n_in, - uint8_t * const out) { - - static void (*put[])(RLE *, size_t) = {putMatch, putLit}; - RLE rle; - size_t i; - const uint8_t * p; - - MEMSZERO(&rle); - rle.p = out; - reset(&rle); - - for (i = 0, p = &inData[0]; i < n_in; ++i, ++p) { - if (*p == rle.match) { - if (rle.mode == MODE_LIT && rle.len > 1) { - putLit(&rle, rle.len - 1); - ++rle.len; - rle.match = *p; - } - rle.mode = MODE_MATCH; - ++rle.len; - } else { - if (rle.mode == MODE_MATCH) - putMatch(&rle, rle.len); - rle.mode = MODE_LIT; - rle.match = *p; - rle.buf[rle.len++] = *p; - } - if (rle.len == 128) - put[rle.mode](&rle, rle.len); - } - if (rle.len != 0) - put[rle.mode](&rle, rle.len); - - return rle.used; -} - - - static void compressIfRequired(IPDB * const pdbP, int const comp, @@ -334,38 +252,29 @@ compressIfRequired(IPDB * const pdbP, *compressedSizeP = ipdb_img_size(pdbP->i); } else { int const uncompressedSz = ipdb_img_size(pdbP->i); - - /* Allocate for the worst case. */ - size_t const allocSz = (3 * uncompressedSz + 2)/2; - uint8_t * data; - - data = pdbP->i->data; + unsigned char * outbuf; + size_t compressedSz; - MALLOCARRAY(data, allocSz); - - if (data == NULL) - pm_error("Could not get %lu bytes of memory to decompress", - (unsigned long)allocSz); - else { - size_t compressedSz; - compressedSz = compress(pdbP->i->data, uncompressedSz, data); - if (comp == IPDB_COMPMAYBE && compressedSz >= uncompressedSz) { - /* Return the uncompressed data */ - free(data); - *compressedDataP = pdbP->i->data; - *compressedSizeP = uncompressedSz; - } else { - pdbP->i->compressed = TRUE; - if (pdbP->i->type == IMG_GRAY16) - pdbP->i->version = 9; - else - pdbP->i->version = 1; - if (pdbP->t != NULL) - pdbP->t->r->offset -= uncompressedSz - compressedSz; - *compressedDataP = data; - *compressedSizeP = compressedSz; - } + pm_rlenc_allocoutbuf(&outbuf, uncompressedSz, PM_RLE_PALMPDB); + + pm_rlenc_compressbyte(pdbP->i->data, outbuf, PM_RLE_PALMPDB, + uncompressedSz, &compressedSz); + if (comp == IPDB_COMPMAYBE && compressedSz >= uncompressedSz) { + /* Return the uncompressed data */ + free(outbuf); + *compressedDataP = pdbP->i->data; + *compressedSizeP = uncompressedSz; + } else { + pdbP->i->compressed = TRUE; + if (pdbP->i->type == IMG_GRAY16) + pdbP->i->version = 9; + else + pdbP->i->version = 1; + if (pdbP->t != NULL) + pdbP->t->r->offset -= uncompressedSz - compressedSz; + *compressedDataP = outbuf; + *compressedSizeP = compressedSz; } } } @@ -742,6 +651,14 @@ readtxt(IPDB * const pdbP, pm_error("stat of '%s' failed, errno = %d (%s)", noteFileName, errno, strerror(errno)); + /* The maximum size of a memory block that a Palm can allocate is 64K. + Abort with error if specified note file is any larger. + */ + + if (st.st_size + 1 >= 65535) + pm_error("Note file is too large: %lu bytes", + (unsigned long) st.st_size); + fP = pm_openr(noteFileName); MALLOCARRAY(fileContent, st.st_size + 1); @@ -758,6 +675,8 @@ readtxt(IPDB * const pdbP, pm_close(fP); + fileContent[st.st_size] = 0x00; /* add terminating NUL char */ + /* Chop of trailing newlines */ for (n = strlen(fileContent) - 1; n >= 0 && fileContent[n] == '\n'; --n) fileContent[n] = '\0'; @@ -787,6 +706,9 @@ main(int argc, const char **argv) { case MAYBE: comp = IPDB_COMPMAYBE; break; } + if (strlen(cmdline.title) > 31) + pm_error("Title too long. Max length is 31 characters."); + pdbP = ipdb_alloc(cmdline.title); if (pdbP == NULL) diff --git a/converter/other/pdbimgtopam.c b/converter/other/pdbimgtopam.c index 3cb4129a..67044109 100644 --- a/converter/other/pdbimgtopam.c +++ b/converter/other/pdbimgtopam.c @@ -97,6 +97,8 @@ parseCommandLine(int argc, const char ** argv, cmdlineP->inputFileName = argv[1]; else pm_error("Program takes at most one argument: input file name"); + + free(option_def); } @@ -211,7 +213,8 @@ readCompressed(IMAGE * const imgP, if (end_offset == UNKNOWN_OFFSET) { /* - * Read until EOF. Some of them have an extra zero byte + * Read sufficient amount for worst-case compressed image, + * or until EOF. Some of them have an extra zero byte * dangling off the end. I originally thought this was * an empty note record (even though there was no record * header for it); however, the release notes for Image @@ -221,12 +224,20 @@ readCompressed(IMAGE * const imgP, * this extra byte and ignore it by paying attention to * the image dimensions. */ - MALLOCARRAY(buffer, ipdb_img_size(imgP)); + size_t const maxCompressedSizeWithBloat = ipdb_img_size(imgP) * 2; + /* + * Provide a buffer large enough for the worst case. + * See note in lib/util/runlength.c . + * We do not use pm_rlenc_allocoutbuf() because there is no + * guarantee that the encoder that produced the image was + * efficient. + */ + MALLOCARRAY(buffer, maxCompressedSizeWithBloat); if (buffer == NULL) retval = ENOMEM; else { - dataSize = fread(buffer, 1, ipdb_img_size(imgP), fP); + dataSize = fread(buffer, 1, maxCompressedSizeWithBloat, fP); if (dataSize <= 0) retval = EIO; else @@ -307,6 +318,8 @@ imageReadData(FILE * const fileP, IMAGE * const imgP, uint32_t const end_offset) { + size_t const imageSize = ipdb_img_size(imgP); + int retval; size_t dataSize; uint8_t * buffer; @@ -318,14 +331,24 @@ imageReadData(FILE * const fileP, * Compressed data can cross row boundaries so we decompress * the data here to avoid messiness in the row access functions. */ - if (dataSize != ipdb_img_size(imgP)) { - decompress(buffer, dataSize, ipdb_img_size(imgP), &imgP->data); + if (dataSize < imageSize || imgP->version == 1) { + if (imgP->version == 0) + pm_message("Image header says raster data is uncompressed. " + "Encountered only %u instead of the " + "required %u bytes. Assuming compressed mode.", + (unsigned)dataSize, (unsigned)imageSize); + decompress(buffer, dataSize, imageSize, &imgP->data); if (imgP->data == NULL) retval = ENOMEM; else imgP->compressed = true; free(buffer); } else { + if (dataSize > imageSize) + pm_message("Image header says raster data is uncompressed. " + "Encountered %u instead of the required %u bytes. " + "Assuming uncompressed mode.", + (unsigned)dataSize, (unsigned)imageSize); imgP->compressed = false; imgP->data = buffer; /* Storage at 'buffer' now belongs to *imgP */ diff --git a/converter/other/pnmtopalm/palm.h b/converter/other/pnmtopalm/palm.h index 718a66cf..0edf9a28 100644 --- a/converter/other/pnmtopalm/palm.h +++ b/converter/other/pnmtopalm/palm.h @@ -28,9 +28,18 @@ #define PALM_FORMAT_565LE 0x02 /* Palm says internal use only */ #define PALM_FORMAT_INDEXEDLE 0x03 /* Palm says internal use only */ -typedef unsigned long Color_s; +typedef unsigned long ColormapEntry; + /* A entry in a Colormap. It is an encoding of 4 bytes as the integer + that those 4 bytes would represent in pure binary: -typedef Color_s * Color; + MSB 0: the color index + 1: red intensity + 2: green intensity + LSB 3: blue intensity + + The intensities are on a scale with a certain maxval (that must be + specified to interpret a ColormapEntry). + */ typedef struct { unsigned int nentries; @@ -39,24 +48,11 @@ typedef struct { /* number of colors actually in 'color_entries' -- entries are filled from 0 consecutively, one color per entry. */ - Color_s * color_entries; /* Array of colors */ -} Colormap_s; - -typedef Colormap_s * Colormap; + ColormapEntry * color_entries; /* Array of colors */ +} Colormap; qsort_comparison_fn palmcolor_compare_indices; qsort_comparison_fn palmcolor_compare_colors; -Colormap -palmcolor_build_custom_8bit_colormap(unsigned int const rows, - unsigned int const cols, - pixel ** const pixels); - -Colormap -palmcolor_build_default_8bit_colormap(void); - -Colormap -palmcolor_read_colormap (FILE * const ifP); - #endif diff --git a/converter/other/pnmtopalm/palmcolormap.c b/converter/other/pnmtopalm/palmcolormap.c index 0f47558c..1341ca2b 100644 --- a/converter/other/pnmtopalm/palmcolormap.c +++ b/converter/other/pnmtopalm/palmcolormap.c @@ -4,18 +4,46 @@ #include "pm_c_util.h" #include "mallocvar.h" #include "pnm.h" - #include "palm.h" +#include "palmcolormap.h" + + + +static pixval +scaleSample(pixval const arg, + pixval const oldMaxval, + pixval const newMaxval) { + + return (arg * newMaxval + oldMaxval/2) / oldMaxval; +} + + + +ColormapEntry +palmcolor_mapEntryColorFmPixel(pixel const color, + pixval const maxval, + pixval const newMaxval) { + + + return + 0 + | (scaleSample(PPM_GETR(color), maxval, newMaxval) << 16) + | (scaleSample(PPM_GETG(color), maxval, newMaxval) << 8) + | (scaleSample(PPM_GETB(color), maxval, newMaxval) << 0); +} + + + int palmcolor_compare_indices(const void * const p1, const void * const p2) { /*---------------------------------------------------------------------------- This is a 'qsort' collation function. -----------------------------------------------------------------------------*/ - if ((*((Color) p1) & 0xFF000000) < (*((Color) p2) & 0xFF000000)) + if ((*((ColormapEntry *) p1) & 0xFF000000) < (*((ColormapEntry *) p2) & 0xFF000000)) return -1; - else if ((*((Color) p1) & 0xFF000000) > (*((Color) p2) & 0xFF000000)) + else if ((*((ColormapEntry *) p1) & 0xFF000000) > (*((ColormapEntry *) p2) & 0xFF000000)) return 1; else return 0; @@ -165,86 +193,88 @@ static int PalmPalette8bpp[256][3] = -Colormap +Colormap * palmcolor_build_default_8bit_colormap(void) { unsigned int i; - Colormap cm; + Colormap * cmP; - MALLOCVAR_NOFAIL(cm); - cm->nentries = 232; - MALLOCARRAY_NOFAIL(cm->color_entries, cm->nentries); + MALLOCVAR_NOFAIL(cmP); + cmP->nentries = 232; + MALLOCARRAY_NOFAIL(cmP->color_entries, cmP->nentries); /* Fill in the colors */ for (i = 0; i < 231; ++i) { - cm->color_entries[i] = ((i << 24) | + cmP->color_entries[i] = ((i << 24) | (PalmPalette8bpp[i][0] << 16) | (PalmPalette8bpp[i][1] << 8) | (PalmPalette8bpp[i][2])); } - cm->color_entries[231] = 0xFF000000; - cm->ncolors = 232; + cmP->color_entries[231] = 0xFF000000; + cmP->ncolors = 232; /* now sort the table */ - qsort (cm->color_entries, cm->ncolors, sizeof(Color_s), - palmcolor_compare_colors); - return cm; + qsort(cmP->color_entries, cmP->ncolors, sizeof(ColormapEntry), + palmcolor_compare_colors); + return cmP; } -Colormap -palmcolor_build_custom_8bit_colormap(unsigned int const rows, +Colormap * +palmcolor_build_custom_8bit_colormap(pixel ** const pixels, + unsigned int const rows, unsigned int const cols, - pixel ** const pixels) { + pixval const maxval) { + unsigned int row; - Colormap colormap; + Colormap * colormapP; - MALLOCVAR_NOFAIL(colormap); - colormap->nentries = 256; - MALLOCARRAY_NOFAIL(colormap->color_entries, colormap->nentries); - colormap->ncolors = 0; /* initial value */ + MALLOCVAR_NOFAIL(colormapP); + colormapP->nentries = 256; + MALLOCARRAY_NOFAIL(colormapP->color_entries, colormapP->nentries); + colormapP->ncolors = 0; /* initial value */ for (row = 0; row < rows; ++row) { unsigned int col; for (col = 0; col < cols; ++col) { - Color found; - Color_s temp; - - temp = ((PPM_GETR(pixels[row][col]) << 16) | - (PPM_GETG(pixels[row][col]) << 8) | - PPM_GETB(pixels[row][col])); - found = (bsearch (&temp, - colormap->color_entries, colormap->ncolors, - sizeof(Color_s), palmcolor_compare_colors)); - if (!found) { - if (colormap->ncolors >= colormap->nentries) + ColormapEntry * foundEntryP; + ColormapEntry const searchTarget = + palmcolor_mapEntryColorFmPixel(pixels[row][col], maxval, 255); + + foundEntryP = + bsearch(&searchTarget, + colormapP->color_entries, colormapP->ncolors, + sizeof(ColormapEntry), palmcolor_compare_colors); + if (!foundEntryP) { + if (colormapP->ncolors >= colormapP->nentries) pm_error("Too many colors for custom colormap " - "(max 256). " + "(max %u). " "Try using pnmquant to reduce the number " - "of colors."); + "of colors.", colormapP->nentries); else { /* add the new color, and re-sort */ - temp |= ((colormap->ncolors) << 24); - colormap->color_entries[colormap->ncolors] = temp; - colormap->ncolors += 1; - qsort(colormap->color_entries, colormap->ncolors, - sizeof(Color_s), palmcolor_compare_colors); + unsigned int const colorIndex = colormapP->ncolors++; + ColormapEntry const newEntry = + searchTarget | (colorIndex << 24); + colormapP->color_entries[colorIndex] = newEntry; + qsort(colormapP->color_entries, colormapP->ncolors, + sizeof(ColormapEntry), palmcolor_compare_colors); } } } } - return colormap; + return colormapP; } -Colormap +Colormap * palmcolor_read_colormap (FILE * const ifP) { unsigned short ncolors; - Colormap retval; + Colormap * retval; int rc; rc = pm_readbigshort(ifP, (short *) &ncolors); @@ -252,13 +282,13 @@ palmcolor_read_colormap (FILE * const ifP) { retval = NULL; else { long colorentry; - Colormap colormap; + Colormap * colormapP; unsigned int i; bool error; - MALLOCVAR_NOFAIL(colormap); - colormap->nentries = ncolors; - MALLOCARRAY_NOFAIL(colormap->color_entries, colormap->nentries); + MALLOCVAR_NOFAIL(colormapP); + colormapP->nentries = ncolors; + MALLOCARRAY_NOFAIL(colormapP->color_entries, colormapP->nentries); for (i = 0, error = FALSE; i < ncolors && !error; ++i) { int rc; @@ -266,15 +296,15 @@ palmcolor_read_colormap (FILE * const ifP) { if (rc != 0) error = TRUE; else - colormap->color_entries[i] = (colorentry & 0xFFFFFFFF); + colormapP->color_entries[i] = (colorentry & 0xFFFFFFFF); } if (error) { - free (colormap->color_entries); - free (colormap); + free (colormapP->color_entries); + free (colormapP); retval = NULL; } else { - colormap->ncolors = ncolors; - retval = colormap; + colormapP->ncolors = ncolors; + retval = colormapP; } } return retval; diff --git a/converter/other/pnmtopalm/palmcolormap.h b/converter/other/pnmtopalm/palmcolormap.h new file mode 100644 index 00000000..cbdb2031 --- /dev/null +++ b/converter/other/pnmtopalm/palmcolormap.h @@ -0,0 +1,25 @@ +#ifndef PALMCOLORMAP_H_INCLUDED +#define PALMCOLORMAP_H_INCLUDED + +#include <stdio.h> +#include "ppm.h" +#include "palm.h" + +ColormapEntry +palmcolor_mapEntryColorFmPixel(pixel const color, + pixval const maxval, + pixval const newMaxval); + +Colormap * +palmcolor_build_custom_8bit_colormap(pixel ** const pixels, + unsigned int const rows, + unsigned int const cols, + pixval const maxval); + +Colormap * +palmcolor_build_default_8bit_colormap(void); + +Colormap * +palmcolor_read_colormap (FILE * const ifP); + +#endif diff --git a/converter/other/pnmtopalm/palmtopnm.c b/converter/other/pnmtopalm/palmtopnm.c index 0f76207d..b58c99bc 100644 --- a/converter/other/pnmtopalm/palmtopnm.c +++ b/converter/other/pnmtopalm/palmtopnm.c @@ -21,17 +21,18 @@ #include "mallocvar.h" #include "palm.h" +#include "palmcolormap.h" -enum palmCompressionType { +enum PalmCompressionType { COMPRESSION_NONE, COMPRESSION_RLE, COMPRESSION_SCANLINE, COMPRESSION_PACKBITS }; -struct palmHeader { +struct PalmHeader { unsigned short cols; unsigned short rows; unsigned short bytesPerRow; @@ -46,7 +47,7 @@ struct palmHeader { unsigned int pixelSize; unsigned char version; unsigned int transparentIndex; - enum palmCompressionType compressionType; + enum PalmCompressionType compressionType; /* version 3 encoding specific */ unsigned char size; unsigned char pixelFormat; @@ -56,7 +57,7 @@ struct palmHeader { -struct directPixelFormat { +struct DirectPixelFormat { unsigned int redbits; unsigned int greenbits; unsigned int bluebits; @@ -64,15 +65,15 @@ struct directPixelFormat { -struct directColorInfo { - struct directPixelFormat pixelFormat; - Color_s transparentColor; +struct DirectColorInfo { + struct DirectPixelFormat pixelFormat; + ColormapEntry transparentColor; }; -struct cmdlineInfo { +struct CmdlineInfo { /* All the information the user supplied in the command line, in a form easy for the program to use. */ @@ -85,13 +86,13 @@ struct cmdlineInfo { static void -parseCommandLine(int argc, char ** argv, - struct cmdlineInfo *cmdlineP) { +parseCommandLine(int argc, const char ** argv, + struct CmdlineInfo *cmdlineP) { /*---------------------------------------------------------------------------- Note that the file spec array we return is stored in the storage that was passed to us as the argv array. -----------------------------------------------------------------------------*/ - optEntry *option_def = malloc( 100*sizeof( optEntry ) ); + optEntry * option_def; /* Instructions to pm_optParseOptions3 on how to parse our options. */ optStruct3 opt; @@ -100,6 +101,8 @@ parseCommandLine(int argc, char ** argv, unsigned int option_def_index; + MALLOCARRAY_NOFAIL(option_def, 100); + option_def_index = 0; /* incremented by OPTENTRY */ OPTENT3(0, "verbose", OPT_FLAG, NULL, &cmdlineP->verbose, 0); @@ -114,7 +117,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. */ @@ -135,6 +138,7 @@ parseCommandLine(int argc, char ** argv, pm_error("Too many arguments (%d). The only non-option " "argument is the file name", argc-1); } + free(option_def); } @@ -183,7 +187,7 @@ skipbytes(FILE * const ifP, static void interpretCompression(unsigned char const compressionValue, - enum palmCompressionType * const compressionTypeP) { + enum PalmCompressionType * const compressionTypeP) { switch (compressionValue) { case PALM_COMPRESSION_RLE: @@ -292,7 +296,7 @@ readRestOfHeaderOld(FILE * const ifP, static void -interpretHeader(struct palmHeader * const palmHeaderP, +interpretHeader(struct PalmHeader * const palmHeaderP, short const cols, short const rows, short const bytesPerRow, @@ -345,7 +349,7 @@ interpretHeader(struct palmHeader * const palmHeaderP, static void readHeader(FILE * const ifP, unsigned int const requestedRendition, - struct palmHeader * const palmHeaderP) { + struct PalmHeader * const palmHeaderP) { /*---------------------------------------------------------------------------- Read the Palm Bitmap header from the file 'ifP'. Read past all renditions up to 'requestedRendition' and read the header of that @@ -441,8 +445,8 @@ yesno(bool const arg) { static void -reportPalmHeader(struct palmHeader const palmHeader, - struct directColorInfo const directColorInfo) { +reportPalmHeader(struct PalmHeader const palmHeader, + struct DirectColorInfo const directColorInfo) { const char *ctype; @@ -473,7 +477,7 @@ reportPalmHeader(struct palmHeader const palmHeader, if (palmHeader.hasTransparency) { if (palmHeader.directColor) { /* Copied from doTransparent(...) */ - Color_s const color = directColorInfo.transparentColor; + ColormapEntry const color = directColorInfo.transparentColor; pm_message("Transparent value: #%02x%02x%02x", (unsigned int)((color >> 16) & 0xFF), (unsigned int)((color >> 8) & 0xFF), @@ -489,7 +493,7 @@ reportPalmHeader(struct palmHeader const palmHeader, static void -determineOutputFormat(struct palmHeader const palmHeader, +determineOutputFormat(struct PalmHeader const palmHeader, int * const formatP, xelval * const maxvalP) { @@ -515,7 +519,7 @@ determineOutputFormat(struct palmHeader const palmHeader, static void readRgbFormat(FILE * const ifP, - struct directPixelFormat * const pixelFormatP) { + struct DirectPixelFormat * const pixelFormatP) { unsigned char r, g, b; @@ -537,8 +541,8 @@ readRgbFormat(FILE * const ifP, static void -readDirectTransparentColor(FILE * const ifP, - Color_s * const colorP) { +readDirectTransparentColor(FILE * const ifP, + ColormapEntry * const colorP) { unsigned char r, g, b; @@ -553,8 +557,8 @@ readDirectTransparentColor(FILE * const ifP, static void readDirectInfoType(FILE * const ifP, - struct palmHeader const palmHeader, - struct directColorInfo * const directInfoTypeP) { + struct PalmHeader const palmHeader, + struct DirectColorInfo * const directInfoTypeP) { /*---------------------------------------------------------------------------- Read the Palm Bitmap Direct Info Type section, if any. @@ -596,8 +600,8 @@ readDirectInfoType(FILE * const ifP, static void readColormap(FILE * const ifP, - struct palmHeader const palmHeader, - Colormap * const colormapP) { + struct PalmHeader const palmHeader, + Colormap ** const colormapPP) { /*---------------------------------------------------------------------------- Read the colormap, if any from the Palm Bitmap. @@ -605,18 +609,18 @@ readColormap(FILE * const ifP, return an undefined value as *colormapP. -----------------------------------------------------------------------------*/ if (palmHeader.hasColormap) - *colormapP = palmcolor_read_colormap(ifP); + *colormapPP = palmcolor_read_colormap(ifP); } static void -getColorInfo(struct palmHeader const palmHeader, - struct directColorInfo const directInfoType, - Colormap const colormapFromImage, - Colormap * const colormapP, +getColorInfo(struct PalmHeader const palmHeader, + struct DirectColorInfo const directInfoType, + Colormap * const colormapFromImageP, + Colormap ** const colormapPP, unsigned int * const ncolorsP, - struct directColorInfo * const directColorInfoP) { + struct DirectColorInfo * const directColorInfoP) { /*---------------------------------------------------------------------------- Gather color encoding information from the various sources. @@ -626,7 +630,7 @@ getColorInfo(struct palmHeader const palmHeader, If it's a version 3 direct color, the pixel format must be "565". -----------------------------------------------------------------------------*/ if (palmHeader.version == 3 && palmHeader.directColor) { - *colormapP = NULL; + *colormapPP = NULL; assert(palmHeader.pixelFormat == PALM_FORMAT_565); @@ -646,18 +650,18 @@ getColorInfo(struct palmHeader const palmHeader, ((((palmHeader.transparentValue >> 0) & 0x1F) * 255 / 0x1F) << 0); } else if (palmHeader.directColor) { - *colormapP = NULL; + *colormapPP = NULL; *directColorInfoP = directInfoType; } else if (palmHeader.hasColormap) - *colormapP = colormapFromImage; + *colormapPP = colormapFromImageP; else if (palmHeader.pixelSize >= 8) { - Colormap colormap; - colormap = palmcolor_build_default_8bit_colormap(); - qsort(colormap->color_entries, colormap->ncolors, - sizeof(Color_s), palmcolor_compare_indices); - *colormapP = colormap; + Colormap * const colormapP = + palmcolor_build_default_8bit_colormap(); + qsort(colormapP->color_entries, colormapP->ncolors, + sizeof(ColormapEntry), palmcolor_compare_indices); + *colormapPP = colormapP; } else - *colormapP = NULL; + *colormapPP = NULL; *ncolorsP = 1 << palmHeader.pixelSize; } @@ -670,8 +674,8 @@ doTransparent(FILE * const ofP, bool const directColor, unsigned char const transparentIndex, unsigned char const pixelSize, - Colormap const colormap, - struct directColorInfo const directColorInfo) { + Colormap * const colormapP, + struct DirectColorInfo const directColorInfo) { /*---------------------------------------------------------------------------- Generate a PNM comment on *ofP telling what color in the raster is supposed to be transparent. @@ -681,24 +685,25 @@ doTransparent(FILE * const ofP, RGB_ALPHA and GRAYSCALE_ALPHA tuple types). -----------------------------------------------------------------------------*/ if (hasTransparency) { - if (colormap) { - Color_s const color = transparentIndex << 24; - Color const actualColor = (bsearch(&color, - colormap->color_entries, - colormap->ncolors, - sizeof(color), - palmcolor_compare_indices)); - if (!actualColor) + if (colormapP) { + ColormapEntry const searchTarget = transparentIndex << 24; + ColormapEntry * const foundEntryP = + bsearch(&searchTarget, + colormapP->color_entries, + colormapP->ncolors, + sizeof(searchTarget), + palmcolor_compare_indices); + if (!foundEntryP) pm_error("Invalid input; transparent index %u " "is not among the %u colors in the image's colormap", - transparentIndex, colormap->ncolors); + transparentIndex, colormapP->ncolors); fprintf(ofP, "#%02x%02x%02x\n", - (unsigned int) ((*actualColor >> 16) & 0xFF), - (unsigned int) ((*actualColor >> 8) & 0xFF), - (unsigned int) ((*actualColor >> 0) & 0xFF)); + (unsigned int) ((*foundEntryP >> 16) & 0xFF), + (unsigned int) ((*foundEntryP >> 8) & 0xFF), + (unsigned int) ((*foundEntryP >> 0) & 0xFF)); } else if (directColor) { - Color_s const color = directColorInfo.transparentColor; + ColormapEntry const color = directColorInfo.transparentColor; fprintf(ofP, "#%02x%02x%02x\n", (unsigned int)((color >> 16) & 0xFF), (unsigned int)((color >> 8) & 0xFF), @@ -907,7 +912,7 @@ static void readDecompressedRow(FILE * const ifP, unsigned char * const palmrow, unsigned char * const lastrow, - enum palmCompressionType const compressionType, + enum PalmCompressionType const compressionType, unsigned int const bytesPerRow, unsigned int const pixelSize, bool const firstRow) { @@ -1000,46 +1005,50 @@ static void convertRowToPnmNotDirect(const unsigned char * const palmrow, xel * const xelrow, unsigned int const cols, - Colormap const colormap, + Colormap * const colormapP, xelval * const graymap, unsigned int * const seen, unsigned int const pixelSize) { unsigned int const mask = (1 << pixelSize) - 1; - const unsigned char *inbyte; + const unsigned char * inbyteP; unsigned int inbit; unsigned int j; + + assert(pixelSize <= 8); inbit = 8 - pixelSize; - inbyte = palmrow; + inbyteP = &palmrow[0]; for (j = 0; j < cols; ++j) { - short const color = ((*inbyte) & (mask << inbit)) >> inbit; + short const color = (*inbyteP & (mask << inbit)) >> inbit; if (seen) ++seen[color]; - if (colormap) { - Color_s const color2 = color << 24; - Color const actualColor = (bsearch (&color2, - colormap->color_entries, - colormap->ncolors, - sizeof(color2), - palmcolor_compare_indices)); - if (!actualColor) + if (colormapP) { + ColormapEntry const searchTarget = color << 24; + ColormapEntry * const foundEntryP = + bsearch(&searchTarget, + colormapP->color_entries, + colormapP->ncolors, + sizeof(searchTarget), + palmcolor_compare_indices); + + if (!foundEntryP) pm_error("Invalid input. A color index in column %u " "is %u, which is not among the %u colors " "in the colormap", - j, color, colormap->ncolors); + j, color, colormapP->ncolors); PPM_ASSIGN(xelrow[j], - (*actualColor >> 16) & 0xFF, - (*actualColor >> 8) & 0xFF, - (*actualColor >> 0) & 0xFF); + (*foundEntryP >> 16) & 0xFF, + (*foundEntryP >> 8) & 0xFF, + (*foundEntryP >> 0) & 0xFF); } else PNM_ASSIGN1(xelrow[j], graymap[color]); if (!inbit) { - ++inbyte; + ++inbyteP; inbit = 8 - pixelSize; } else inbit -= pixelSize; @@ -1050,9 +1059,9 @@ convertRowToPnmNotDirect(const unsigned char * const palmrow, static void writePnm(FILE * const ofP, - struct palmHeader const palmHeader, + struct PalmHeader const palmHeader, FILE * const ifP, - Colormap const colormap, + Colormap * const colormapP, xelval * const graymap, unsigned int const nColors, int const format, @@ -1105,7 +1114,7 @@ writePnm(FILE * const ofP, assert(palmHeader.pixelSize == 16); convertRowToPnmDirect(palmrow, xelrow, cols, maxval, seen); } else - convertRowToPnmNotDirect(palmrow, xelrow, cols, colormap, graymap, + convertRowToPnmNotDirect(palmrow, xelrow, cols, colormapP, graymap, seen, palmHeader.pixelSize); pnm_writepnmrow(ofP, xelrow, cols, maxval, format, 0); @@ -1119,28 +1128,29 @@ writePnm(FILE * const ofP, static void showHistogram(unsigned int * const seen, - Colormap const colormap, + Colormap * const colormapP, const xelval * const graymap, unsigned int const ncolors) { unsigned int colorIndex; for (colorIndex = 0; colorIndex < ncolors; ++colorIndex) { - if (!colormap) + if (!colormapP) pm_message("%.3d -> %.3d: %d", colorIndex, graymap[colorIndex], seen[colorIndex]); else { - Color_s const color = colorIndex << 24; - Color const actualColor = (bsearch(&color, - colormap->color_entries, - colormap->ncolors, - sizeof(color), - palmcolor_compare_indices)); - if (actualColor) + ColormapEntry const searchTarget = colorIndex << 24; + ColormapEntry * const foundEntryP = + bsearch(&searchTarget, + colormapP->color_entries, + colormapP->ncolors, + sizeof(searchTarget), + palmcolor_compare_indices); + if (foundEntryP) pm_message("%.3d -> %ld,%ld,%ld: %d", colorIndex, - (*actualColor >> 16) & 0xFF, - (*actualColor >> 8) & 0xFF, - (*actualColor & 0xFF), seen[colorIndex]); + (*foundEntryP >> 16) & 0xFF, + (*foundEntryP >> 8) & 0xFF, + (*foundEntryP & 0xFF), seen[colorIndex]); } } } @@ -1148,22 +1158,21 @@ showHistogram(unsigned int * const seen, int -main(int argc, char **argv) { +main(int argc, const char **argv) { - struct cmdlineInfo cmdline; + struct CmdlineInfo cmdline; - FILE* ifP; - struct palmHeader palmHeader; - struct directColorInfo directInfoType; - Colormap colormapFromImage; - Colormap colormap; - struct directColorInfo directColorInfo; + FILE * ifP; + struct PalmHeader palmHeader; + struct DirectColorInfo directInfoType; + Colormap * colormapFromImageP; + Colormap * colormapP; + struct DirectColorInfo directColorInfo; int format; xelval maxval; unsigned int nColors; - /* Parse default params */ - pnm_init(&argc, argv); + pm_proginit(&argc, argv); parseCommandLine(argc, argv, &cmdline); @@ -1173,12 +1182,12 @@ main(int argc, char **argv) { readDirectInfoType(ifP, palmHeader, &directInfoType); - readColormap(ifP, palmHeader, &colormapFromImage); + readColormap(ifP, palmHeader, &colormapFromImageP); determineOutputFormat(palmHeader, &format, &maxval); - getColorInfo(palmHeader, directInfoType, colormapFromImage, - &colormap, &nColors, &directColorInfo); + getColorInfo(palmHeader, directInfoType, colormapFromImageP, + &colormapP, &nColors, &directColorInfo); if (cmdline.verbose) reportPalmHeader(palmHeader, directColorInfo); @@ -1187,7 +1196,7 @@ main(int argc, char **argv) { doTransparent(stdout, palmHeader.hasTransparency, palmHeader.directColor, palmHeader.transparentIndex, - palmHeader.pixelSize, colormap, directColorInfo); + palmHeader.pixelSize, colormapP, directColorInfo); else { unsigned int * seen; xelval * graymap; @@ -1195,11 +1204,11 @@ main(int argc, char **argv) { graymap = createGraymap(nColors, maxval); writePnm(stdout, - palmHeader, ifP, colormap, graymap, nColors, format, maxval, + palmHeader, ifP, colormapP, graymap, nColors, format, maxval, cmdline.showhist ? &seen : NULL); if (cmdline.showhist) - showHistogram(seen, colormap, graymap, nColors); + showHistogram(seen, colormapP, graymap, nColors); free(graymap); } diff --git a/converter/other/pnmtopalm/pnmtopalm.c b/converter/other/pnmtopalm/pnmtopalm.c index a7d1fd46..25c8af2e 100644 --- a/converter/other/pnmtopalm/pnmtopalm.c +++ b/converter/other/pnmtopalm/pnmtopalm.c @@ -29,33 +29,37 @@ #include "pm_c_util.h" #include "pnm.h" -#include "palm.h" #include "shhopt.h" #include "mallocvar.h" #include "runlength.h" -enum compressionType {COMP_NONE, COMP_SCANLINE, COMP_RLE, COMP_PACKBITS}; +#include "palm.h" +#include "palmcolormap.h" + +enum CompressionType {COMP_NONE, COMP_SCANLINE, COMP_RLE, COMP_PACKBITS}; -struct cmdline_info { +struct CmdlineInfo { /* All the information the user supplied in the command line, in a form easy for the program to use. */ const char * inputFilespec; /* Filespecs of input files */ - const char * transparent; /* -transparent value. Null if unspec */ - unsigned int depth; /* -depth value. 0 if unspec */ - unsigned int maxdepth; /* -maxdepth value. 0 if unspec */ - enum compressionType compression; + const char * transparent; + unsigned int depthSpec; + unsigned int depth; + unsigned int maxdepthSpec; + unsigned int maxdepth; + enum CompressionType compression; unsigned int verbose; unsigned int colormap; - unsigned int offset; /* -offset specified */ - unsigned int density; /* screen density */ + unsigned int offset; + unsigned int density; unsigned int withdummy; }; static void -parseCommandLine(int argc, char ** argv, struct cmdline_info *cmdlineP) { +parseCommandLine(int argc, const char ** argv, struct CmdlineInfo *cmdlineP) { /*---------------------------------------------------------------------------- Note that the file spec array we return is stored in the storage that was passed to us as the argv array. @@ -64,7 +68,7 @@ parseCommandLine(int argc, char ** argv, struct cmdline_info *cmdlineP) { optEntry *option_def; unsigned int option_def_index; - unsigned int transSpec, depthSpec, maxdepthSpec, densitySpec; + unsigned int transSpec, densitySpec; unsigned int scanline_compression, rle_compression, packbits_compression; MALLOCARRAY_NOFAIL(option_def, 100); @@ -73,9 +77,9 @@ parseCommandLine(int argc, char ** argv, struct cmdline_info *cmdlineP) { OPTENT3(0, "transparent", OPT_STRING, &cmdlineP->transparent, &transSpec, 0); OPTENT3(0, "depth", OPT_UINT, - &cmdlineP->depth, &depthSpec, 0); + &cmdlineP->depth, &cmdlineP->depthSpec, 0); OPTENT3(0, "maxdepth", OPT_UINT, - &cmdlineP->maxdepth, &maxdepthSpec, 0); + &cmdlineP->maxdepth, &cmdlineP->maxdepthSpec, 0); OPTENT3(0, "scanline_compression", OPT_FLAG, NULL, &scanline_compression, 0); OPTENT3(0, "rle_compression", OPT_FLAG, @@ -97,28 +101,26 @@ parseCommandLine(int argc, char ** argv, struct cmdline_info *cmdlineP) { opt.short_allowed = FALSE; /* We have some 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 *cmdline_p and others. */ - if (depthSpec) { + if (cmdlineP->depthSpec) { if (cmdlineP->depth != 1 && cmdlineP->depth != 2 && cmdlineP->depth != 4 && cmdlineP->depth != 8 && cmdlineP->depth != 16) pm_error("invalid value for -depth: %u. Valid values are " "1, 2, 4, 8, and 16", cmdlineP->depth); - } else - cmdlineP->depth = 0; + } - if (maxdepthSpec) { + if (cmdlineP->maxdepthSpec) { if (cmdlineP->maxdepth != 1 && cmdlineP->maxdepth != 2 && cmdlineP->maxdepth != 4 && cmdlineP->maxdepth != 8 && cmdlineP->maxdepth != 16) pm_error("invalid value for -maxdepth: %u. Valid values are " "1, 2, 4, 8, and 16", cmdlineP->maxdepth); - } else - cmdlineP->maxdepth = 0; + } - if (depthSpec && maxdepthSpec && + if (cmdlineP->depthSpec && cmdlineP->maxdepthSpec && cmdlineP->depth > cmdlineP->maxdepth) pm_error("-depth value (%u) is greater than -maxdepth (%u) value.", cmdlineP->depth, cmdlineP->maxdepth); @@ -132,8 +134,8 @@ parseCommandLine(int argc, char ** argv, struct cmdline_info *cmdlineP) { cmdlineP->density != PALM_DENSITY_DOUBLE && cmdlineP->density != PALM_DENSITY_TRIPLE && cmdlineP->density != PALM_DENSITY_QUADRUPLE) - pm_error("Invalid value for -density: %d. Valid values are " - "%d, %d, %d, %d and %d.", cmdlineP->density, + pm_error("Invalid value for -density: %u. Valid values are " + "%u, %u, %u, %u and %u.", cmdlineP->density, PALM_DENSITY_LOW, PALM_DENSITY_ONEANDAHALF, PALM_DENSITY_DOUBLE, PALM_DENSITY_TRIPLE, PALM_DENSITY_QUADRUPLE); @@ -164,7 +166,7 @@ parseCommandLine(int argc, char ** argv, struct cmdline_info *cmdlineP) { if (argc-1 > 1) pm_error("This program takes at most 1 argument: the file name. " - "You specified %d", argc-1); + "You specified %u", argc-1); else if (argc-1 > 0) cmdlineP->inputFilespec = argv[1]; else @@ -173,108 +175,241 @@ parseCommandLine(int argc, char ** argv, struct cmdline_info *cmdlineP) { +static xelval +scaleSample(pixval const arg, + pixval const oldMaxval, + pixval const newMaxval) { + + return (arg * newMaxval + oldMaxval/2) / oldMaxval; +} + + + +static void +determinePalmFormatPgm(xelval const maxval, + bool const bppSpecified, + unsigned int const bpp, + bool const maxBppSpecified, + unsigned int const maxBpp, + bool const wantCustomColormap, + enum CompressionType const compression, + bool const verbose, + unsigned int * const bppP) { + + /* We can usually handle this one, but may not have enough pixels. So + check. + */ + + if (wantCustomColormap) + pm_error("You specified -colormap with a black and white input" + "image. -colormap is valid only with color."); + if (bppSpecified) + *bppP = bpp; + else if (maxBppSpecified && (maxval >= (1 << maxBpp))) + *bppP = maxBpp; + else if (compression != COMP_NONE && maxval > 255) + *bppP = 8; + else if (maxval > 16) + *bppP = 4; + else { + /* scale to minimum number of bpp needed */ + unsigned int bpp; + for (bpp = 1; (1 << bpp) < maxval; bpp *= 2) + ; + *bppP = bpp; + } + if (verbose) + pm_message("output is grayscale %u bits-per-pixel", *bppP); +} + + + +static void +validateImageAgainstStandardColormap(const Colormap * const colormapP, + xel ** const xels, + unsigned int const cols, + unsigned int const rows, + xelval const maxval) { +/*---------------------------------------------------------------------------- + Abort program if the image xels[][] (which is 'cols' x 'rows') contains a + color not in the colormap *colormapP, giving an error message assuming the + user chose the standard Palm colormap. +-----------------------------------------------------------------------------*/ + unsigned int row; + + for (row = 0; row < rows; ++row) { + unsigned int col; + + for (col = 0; col < cols; ++col) { + ColormapEntry const searchTarget = + palmcolor_mapEntryColorFmPixel(xels[row][col], maxval, 255); + + ColormapEntry * const foundEntryP = + (bsearch(&searchTarget, + colormapP->color_entries, colormapP->ncolors, + sizeof(ColormapEntry), palmcolor_compare_colors)); + if (!foundEntryP) + pm_error( + "A color in the input image is not in the standard Palm " + "8-bit color palette. Either adjust the colors in the " + "input with 'pnmremap' and the 'palmcolor8.map' file " + "(see manual) or specify -colormap or -depth=16"); + } + } +} + + + +static void +determinePalmFormatPpm(unsigned int const cols, + unsigned int const rows, + xelval const maxval, + xel ** const xels, + bool const bppSpecified, + unsigned int const bpp, + bool const maxBppSpecified, + unsigned int const maxBpp, + bool const wantCustomColormap, + enum CompressionType const compression, + bool const verbose, + unsigned int * const bppP, + bool * const directColorP, + Colormap ** const colormapPP) { + + /* We don't attempt to identify PPM files that are actually + monochrome. So there are two options here: either 8-bit with a + colormap, either the standard one or a custom one, or 16-bit direct + color. In the colormap case, if 'wantCustomColormap' is true (not + recommended by Palm) we will put in our own colormap that has the + colors of the input image; otherwise we will select the default + Palm colormap and will fail if the input image has any colors that + are not in that map (user should use Pnmremap and the + palmcolor8.map file that comes with Netpbm to avoid this). We try + for colormapped first, since it works on more PalmOS devices. + */ + if ((bppSpecified && bpp == 16) || + (!bppSpecified && maxBppSpecified && maxBpp == 16)) { + /* we do the 16-bit direct color */ + *directColorP = TRUE; + *colormapPP = NULL; + *bppP = 16; + } else if (!wantCustomColormap) { + /* colormapped with the standard colormap */ + Colormap * colormapP; + + if ((bppSpecified && bpp != 8) || (maxBppSpecified && maxBpp < 8)) + pm_error("Must use depth of 8 for color Palm Bitmap without " + "custom color table."); + colormapP = palmcolor_build_default_8bit_colormap(); + validateImageAgainstStandardColormap(colormapP, + xels, cols, rows, maxval); + + *colormapPP = colormapP; + *bppP = 8; + *directColorP = FALSE; + if (verbose) + pm_message("Output is color with default colormap at 8 bpp"); + } else { + /* colormapped with a custom colormap */ + *colormapPP = + palmcolor_build_custom_8bit_colormap(xels, rows, cols, maxval); + for (*bppP = 1; (1 << *bppP) < (*colormapPP)->ncolors; *bppP *= 2); + if (bppSpecified) { + if (bpp >= *bppP) + *bppP = bpp; + else + pm_error("Too many colors for specified depth. " + "Specified depth is %u bits; would need %u to " + "represent the %u colors in the image. " + "Use pnmquant to reduce.", + maxBpp, *bppP, (*colormapPP)->ncolors); + } else if (maxBppSpecified && maxBpp < *bppP) { + pm_error("Too many colors for specified max depth. " + "Specified maximum is %u bits; would need %u to " + "represent the %u colors in the image. " + "Use pnmquant to reduce.", + maxBpp, *bppP, (*colormapPP)->ncolors); + } else if (compression != COMP_NONE && *bppP > 8) { + pm_error("Too many colors for a compressed image. " + "Maximum is 256; the image has %u", + (*colormapPP)->ncolors); + } + *directColorP = FALSE; + if (verbose) + pm_message("Output is color with custom colormap " + "with %u colors at %u bpp", + (*colormapPP)->ncolors, *bppP); + } +} + + + static void -determinePalmFormat(unsigned int const cols, - unsigned int const rows, - xelval const maxval, - int const format, - xel ** const xels, - unsigned int const specified_bpp, - unsigned int const max_bpp, - bool const custom_colormap, - bool const verbose, - unsigned int * const bppP, - bool * const directColorP, - Colormap * const colormapP) { +determinePalmFormat(unsigned int const cols, + unsigned int const rows, + xelval const maxval, + int const format, + xel ** const xels, + bool const bppSpecified, + unsigned int const bpp, + bool const maxBppSpecified, + unsigned int const maxBpp, + bool const wantCustomColormap, + enum CompressionType const compression, + bool const verbose, + unsigned int * const bppP, + bool * const directColorP, + Colormap ** const colormapPP) { +/*---------------------------------------------------------------------------- + Determine what kind of Palm output file to make. + Also compute the colormap, if there is to be one. This could be either one + we make up, that needs to go into the image, or a standard one. +-----------------------------------------------------------------------------*/ + if (compression != COMP_NONE) { + if (bppSpecified && bpp > 8) + pm_error("You requested %u bits per pixel and compression. " + "This program does not know how to generate a " + "compressed image with more than 8 bits per pixel", + bpp); + if (maxBppSpecified && maxBpp > 8) + pm_error("You requested %u max bits per pixel and compression. " + "This program does not know how to generate a " + "compressed image with more than 8 bits per pixel", + maxBpp); + } if (PNM_FORMAT_TYPE(format) == PBM_TYPE) { - if (custom_colormap) + if (wantCustomColormap) pm_error("You specified -colormap with a black and white input " "image. -colormap is valid only with color."); - if (specified_bpp) - *bppP = specified_bpp; + if (bppSpecified) + *bppP = bpp; else *bppP = 1; /* no point in wasting bits */ *directColorP = FALSE; - *colormapP = NULL; + *colormapPP = NULL; if (verbose) pm_message("output is black and white"); } else if (PNM_FORMAT_TYPE(format) == PGM_TYPE) { - /* we can usually handle this one, but may not have enough - pixels. So check... */ - if (custom_colormap) - pm_error("You specified -colormap with a black and white input" - "image. -colormap is valid only with color."); - if (specified_bpp) - *bppP = specified_bpp; - else if (max_bpp && (maxval >= (1 << max_bpp))) - *bppP = max_bpp; - else if (maxval > 16) - *bppP = 4; - else { - /* scale to minimum number of bpp needed */ - for (*bppP = 1; (1 << *bppP) < maxval; *bppP *= 2) - ; - } - if (*bppP > 4) - *bppP = 4; - if (verbose) - pm_message("output is grayscale %d bits-per-pixel", *bppP); + determinePalmFormatPgm(maxval, + bppSpecified, bpp, maxBppSpecified, maxBpp, + wantCustomColormap, compression, + verbose, + bppP); + *directColorP = FALSE; - *colormapP = NULL; + *colormapPP = NULL; } else if (PNM_FORMAT_TYPE(format) == PPM_TYPE) { - - /* We assume that we only get a PPM if the image cannot be - represented as PBM or PGM. There are two options here: either - 8-bit with a colormap, either the standard one or a custom one, - or 16-bit direct color. In the 8-bit case, if "custom_colormap" - is specified (not recommended by Palm) we will put in our own - colormap; otherwise we will assume that the colors have been - mapped to the default Palm colormap by appropriate use of - pnmquant. We try for 8-bit color first, since it works on - more PalmOS devices. - */ - if ((specified_bpp == 16) || - (specified_bpp == 0 && max_bpp == 16)) { - /* we do the 16-bit direct color */ - *directColorP = TRUE; - *colormapP = NULL; - *bppP = 16; - } else if (!custom_colormap) { - /* standard indexed 8-bit color */ - *colormapP = palmcolor_build_default_8bit_colormap(); - *bppP = 8; - if (((specified_bpp != 0) && (specified_bpp != 8)) || - ((max_bpp != 0) && (max_bpp < 8))) - pm_error("Must use depth of 8 for color Palm Bitmap without " - "custom color table."); - *directColorP = FALSE; - if (verbose) - pm_message("Output is color with default colormap at 8 bpp"); - } else { - /* indexed 8-bit color with a custom colormap */ - *colormapP = - palmcolor_build_custom_8bit_colormap(rows, cols, xels); - for (*bppP = 1; (1 << *bppP) < (*colormapP)->ncolors; *bppP *= 2); - if (specified_bpp != 0) { - if (specified_bpp >= *bppP) - *bppP = specified_bpp; - else - pm_error("Too many colors for specified depth. " - "Use pnmquant to reduce."); - } else if ((max_bpp != 0) && (max_bpp < *bppP)) { - pm_error("Too many colors for specified max depth. " - "Use pnmquant to reduce."); - } - *directColorP = FALSE; - if (verbose) - pm_message("Output is color with custom colormap " - "with %d colors at %d bpp", - (*colormapP)->ncolors, *bppP); - } + determinePalmFormatPpm(cols, rows, maxval, xels, bppSpecified, bpp, + maxBppSpecified, maxBpp, + wantCustomColormap, compression, verbose, + bppP, directColorP, colormapPP); } else { pm_error("unknown format 0x%x on input file", (unsigned) format); } + + if (compression != COMP_NONE) + assert(*bppP <= 8); } @@ -300,25 +435,23 @@ findTransparentColor(const char * const colorSpec, pixval const newMaxval, bool const directColor, pixval const maxval, - Colormap const colormap, + Colormap * const colormapP, xel * const transcolorP, unsigned int * const transindexP) { *transcolorP = ppm_parsecolor(colorSpec, maxval); if (!directColor) { - Color_s const temp_color = - ((((PPM_GETR(*transcolorP)*newMaxval) / maxval) << 16) - | (((PPM_GETG(*transcolorP)*newMaxval) / maxval) << 8) - | ((PPM_GETB(*transcolorP)*newMaxval) / maxval)); - Color const found = - (bsearch(&temp_color, - colormap->color_entries, colormap->ncolors, - sizeof(Color_s), palmcolor_compare_colors)); - if (!found) { + ColormapEntry const searchTarget = + palmcolor_mapEntryColorFmPixel(*transcolorP, maxval, newMaxval); + ColormapEntry * const foundEntryP = + (bsearch(&searchTarget, + colormapP->color_entries, colormapP->ncolors, + sizeof(ColormapEntry), palmcolor_compare_colors)); + if (!foundEntryP) { pm_error("Specified transparent color %s not found " "in colormap.", colorSpec); } else - *transindexP = (*found >> 24) & 0xFF; + *transindexP = (*foundEntryP >> 24) & 0xFF; } } @@ -326,9 +459,9 @@ findTransparentColor(const char * const colorSpec, static unsigned int bitmapVersion(unsigned int const bpp, - bool const colormap, + bool const colormapped, bool const transparent, - enum compressionType const compression, + enum CompressionType const compression, unsigned int const density) { /*---------------------------------------------------------------------------- Return the version number of the oldest version that can represent @@ -343,7 +476,7 @@ bitmapVersion(unsigned int const bpp, version = 3; else if (transparent || compression != COMP_NONE) version = 2; - else if (bpp > 1 || colormap) + else if (bpp > 1 || colormapped) version = 1; else version = 0; @@ -357,8 +490,8 @@ static void writeCommonHeader(unsigned int const cols, unsigned int const rows, unsigned int const rowbytes, - enum compressionType const compression, - bool const colormap, + enum CompressionType const compression, + bool const colormapped, bool const transparent, bool const directColor, unsigned int const bpp, @@ -382,7 +515,7 @@ writeCommonHeader(unsigned int const cols, flags = 0; /* initial value */ if (compression != COMP_NONE) flags |= PALM_IS_COMPRESSED_FLAG; - if (colormap) + if (colormapped) flags |= PALM_HAS_COLORMAP_FLAG; if (transparent) flags |= PALM_HAS_TRANSPARENCY_FLAG; @@ -398,7 +531,7 @@ writeCommonHeader(unsigned int const cols, static unsigned char -compressionFieldValue(enum compressionType const compression) { +compressionFieldValue(enum CompressionType const compression) { unsigned char retval; @@ -424,7 +557,7 @@ compressionFieldValue(enum compressionType const compression) { static void writeRemainingHeaderLow(unsigned int const nextDepthOffset, unsigned int const transindex, - enum compressionType const compression, + enum CompressionType const compression, unsigned int const bpp) { /*---------------------------------------------------------------------------- Write last 6 bytes of a low density Palm Bitmap header. @@ -449,7 +582,7 @@ writeRemainingHeaderLow(unsigned int const nextDepthOffset, static void writeRemainingHeaderHigh(unsigned int const bpp, - enum compressionType const compression, + enum CompressionType const compression, unsigned int const density, xelval const maxval, bool const transparent, @@ -480,9 +613,9 @@ writeRemainingHeaderHigh(unsigned int const bpp, if (bpp == 16) { /* Blind guess here */ fputc(0, stdout); - fputc((PPM_GETR(transcolor) * 255) / maxval, stdout); - fputc((PPM_GETG(transcolor) * 255) / maxval, stdout); - fputc((PPM_GETB(transcolor) * 255) / maxval, stdout); + fputc(scaleSample(PPM_GETR(transcolor), maxval, 255), stdout); + fputc(scaleSample(PPM_GETG(transcolor), maxval, 255), stdout); + fputc(scaleSample(PPM_GETB(transcolor), maxval, 255), stdout); } else { assert(transindex <= UCHAR_MAX); fputc(0, stdout); @@ -523,7 +656,7 @@ writeDummy() { static void writeColormap(bool const explicitColormap, - Colormap const colormap, + Colormap * const colormapP, bool const directColor, unsigned int const bpp, bool const transparent, @@ -534,14 +667,14 @@ writeColormap(bool const explicitColormap, /* if there's a colormap, write it out */ if (explicitColormap) { unsigned int row; - if (!colormap) + if (!colormapP) pm_error("Internal error: user specified -colormap, but we did " "not generate a colormap."); - qsort (colormap->color_entries, colormap->ncolors, - sizeof(Color_s), palmcolor_compare_indices); - pm_writebigshort( stdout, colormap->ncolors ); - for (row = 0; row < colormap->ncolors; ++row) - pm_writebiglong (stdout, colormap->color_entries[row]); + qsort(colormapP->color_entries, colormapP->ncolors, + sizeof(ColormapEntry), palmcolor_compare_indices); + pm_writebigshort( stdout, colormapP->ncolors ); + for (row = 0; row < colormapP->ncolors; ++row) + pm_writebiglong (stdout, colormapP->color_entries[row]); } if (directColor && (version < 3)) { @@ -552,13 +685,13 @@ writeColormap(bool const explicitColormap, fputc(5, stdout); /* # of bits of blue */ fputc(0, stdout); /* reserved by Palm */ } else - pm_error("Don't know how to create %d bit DirectColor bitmaps.", + pm_error("Don't know how to create %u bit DirectColor bitmaps.", bpp); if (transparent) { fputc(0, stdout); - fputc((PPM_GETR(transcolor) * 255) / maxval, stdout); - fputc((PPM_GETG(transcolor) * 255) / maxval, stdout); - fputc((PPM_GETB(transcolor) * 255) / maxval, stdout); + fputc(scaleSample(PPM_GETR(transcolor) , maxval, 255), stdout); + fputc(scaleSample(PPM_GETG(transcolor) , maxval, 255), stdout); + fputc(scaleSample(PPM_GETB(transcolor) , maxval, 255), stdout); } else pm_writebiglong(stdout, 0); /* no transparent color */ } @@ -571,17 +704,29 @@ computeRawRowDirectColor(const xel * const xelrow, unsigned int const cols, xelval const maxval, unsigned char * const rowdata) { +/*---------------------------------------------------------------------------- + Compute a row of Palm data in raw (uncompressed) form for an image that + uses direct color (really, true color: each pixel contains RGB intensities + as distinct R, G, and B numbers). + In this format, each pixel is 16 bits: 5 red, 6 green, 5 blue. + + 'xelrow' is the image contents of row. It is 'cols' columns wide and + samples are based on maxval 'maxval'. + + Put the output data at 'rowdata'. +-----------------------------------------------------------------------------*/ unsigned int col; - unsigned char *outptr; + unsigned char * outCursor; - for (col = 0, outptr = rowdata; col < cols; ++col) { + for (col = 0, outCursor = &rowdata[0]; col < cols; ++col) { unsigned int const color = - ((((PPM_GETR(xelrow[col])*31)/maxval) << 11) | - (((PPM_GETG(xelrow[col])*63)/maxval) << 5) | - ((PPM_GETB(xelrow[col])*31)/maxval)); - *outptr++ = (color >> 8) & 0xFF; - *outptr++ = color & 0xFF; + (scaleSample(PPM_GETR(xelrow[col]), maxval, 31) << 11) | + (scaleSample(PPM_GETG(xelrow[col]), maxval, 63) << 5) | + (scaleSample(PPM_GETB(xelrow[col]), maxval, 31) << 0); + + *outCursor++ = (color >> 8) & 0xFF; + *outCursor++ = color & 0xFF; } } @@ -592,23 +737,40 @@ computeRawRowNonDirect(const xel * const xelrow, unsigned int const cols, xelval const maxval, unsigned int const bpp, - Colormap const colormap, + Colormap * const colormapP, unsigned int const newMaxval, unsigned char * const rowdata) { +/*---------------------------------------------------------------------------- + Compute a row of Palm data in raw (uncompressed) form for an image that + does not have a raster whose elements are explicit R, G, and B + intensities. + If 'colormapP' is non-null, the pixel is an index into that colormap. + 'newMaxval' is meaningless. + + If 'colormapP' is null, the pixel is a grayscale intensity, on a scale with + maximum value 'newMaxval'. (N.B. this is really direct color, but for some + reason it's historically lumped in with the paletted formats). + + 'xelrow' is the image contents of row. It is 'cols' columns wide and + samples are based on maxval 'maxval'. + + Put the output data at 'rowdata', using 'bpp' bits per pixel. +-----------------------------------------------------------------------------*/ unsigned int col; - unsigned char *outptr; + unsigned char * outCursor; + /* Points to next slot in 'rowdata' we will fill */ unsigned char outbyte; /* Accumulated bits to be output */ unsigned char outbit; /* The lowest bit number we want to access for this pixel */ - outbyte = 0x00; - outptr = rowdata; + outbyte = 0x00; /* initial value */ + outCursor = &rowdata[0]; /* Start at the beginning of the row */ for (outbit = 8 - bpp, col = 0; col < cols; ++col) { unsigned int color; - if (!colormap) { + if (!colormapP) { /* we assume grayscale, and use simple scaling */ color = (PNM_GET1(xelrow[col]) * newMaxval)/maxval; if (color > newMaxval) @@ -616,24 +778,23 @@ computeRawRowNonDirect(const xel * const xelrow, "color of %u.", color); color = newMaxval - color; /* note grayscale maps are inverted */ } else { - Color_s const temp_color = - ((((PPM_GETR(xelrow[col])*newMaxval)/maxval)<<16) - | (((PPM_GETG(xelrow[col])*newMaxval)/maxval)<<8) - | (((PPM_GETB(xelrow[col])*newMaxval)/maxval))); - Color const found = (bsearch (&temp_color, - colormap->color_entries, - colormap->ncolors, - sizeof(Color_s), - palmcolor_compare_colors)); - if (!found) { - pm_error("Color %d:%d:%d not found in colormap. " - "Try using pnmquant to reduce the " - "number of colors.", + ColormapEntry const searchTarget = + palmcolor_mapEntryColorFmPixel(xelrow[col], maxval, 255); + ColormapEntry * const foundEntryP = + bsearch(&searchTarget, + colormapP->color_entries, + colormapP->ncolors, + sizeof(ColormapEntry), + palmcolor_compare_colors); + if (!foundEntryP) { + pm_error("INERNAL ERROR: " + "Color (%u,%u,%u) not found in colormap, " + "though it was supposedly there before", PPM_GETR(xelrow[col]), PPM_GETG(xelrow[col]), PPM_GETB(xelrow[col])); } - color = (*found >> 24) & 0xFF; + color = (*foundEntryP >> 24) & 0xFF; } if (color > newMaxval) @@ -642,7 +803,7 @@ computeRawRowNonDirect(const xel * const xelrow, outbyte |= (color << outbit); if (outbit == 0) { /* Bit buffer is full. Flush to to rowdata. */ - *outptr++ = outbyte; + *outCursor++ = outbyte; outbyte = 0x00; outbit = 8 - bpp; } else @@ -650,7 +811,7 @@ computeRawRowNonDirect(const xel * const xelrow, } if ((cols % (8 / bpp)) != 0) { /* Flush bits remaining in the bit buffer to rowdata */ - *outptr++ = outbyte; + *outCursor++ = outbyte; } } @@ -830,7 +991,7 @@ rleCompressAndBufferRow(const unsigned char * const rowdata, unsigned int repeatcount; for (repeatcount = 1; repeatcount < (rowbytes - pos) && repeatcount < 255; - ++repeatcount) + ++repeatcount) if (rowdata[pos + repeatcount] != rowdata[pos]) break; @@ -870,7 +1031,7 @@ packbitsCompressAndBufferRow(const unsigned char * const rowdata, static void bufferRowFromRawRowdata(const unsigned char * const rowdata, unsigned int const rowbytes, - enum compressionType const compression, + enum CompressionType const compression, const unsigned char * const lastrow, struct seqBuffer * const rasterBufferP) { /*---------------------------------------------------------------------------- @@ -909,16 +1070,17 @@ bufferRow(const xel * const xelrow, unsigned int const rowbytes, unsigned int const bpp, unsigned int const newMaxval, - enum compressionType const compression, + enum CompressionType const compression, bool const directColor, - Colormap const colormap, + Colormap * const colormapP, unsigned char * const rowdata, unsigned char * const lastrow, struct seqBuffer * const rasterBufferP) { /*---------------------------------------------------------------------------- Add a row of the Palm Bitmap raster to buffer 'rasterBufferP'. - 'xelrow' is the image contents of row. It is 'cols' columns wide. + 'xelrow' is the image contents of row. It is 'cols' columns wide and + samples are based on maxval 'maxval'. If 'compression' indicates scanline compression, 'lastrow' is the row immediately preceding this one in the image (and this function @@ -930,7 +1092,7 @@ bufferRow(const xel * const xelrow, if (directColor) computeRawRowDirectColor(xelrow, cols, maxval, rowdata); else - computeRawRowNonDirect(xelrow, cols, maxval, bpp, colormap, newMaxval, + computeRawRowNonDirect(xelrow, cols, maxval, bpp, colormapP, newMaxval, rowdata); bufferRowFromRawRowdata(rowdata, rowbytes, compression, @@ -947,9 +1109,9 @@ bufferRaster(xel ** const xels, unsigned int const rowbytes, unsigned int const bpp, unsigned int const newMaxval, - enum compressionType const compression, + enum CompressionType const compression, bool const directColor, - Colormap const colormap, + Colormap * const colormapP, struct seqBuffer ** const rasterBufferPP) { unsigned char * rowdata; @@ -971,7 +1133,7 @@ bufferRaster(xel ** const xels, for (row = 0; row < rows; ++row) { bufferRow(xels[row], cols, maxval, rowbytes, bpp, newMaxval, compression, - directColor, colormap, rowdata, row > 0 ? lastrow : NULL, + directColor, colormapP, rowdata, row > 0 ? lastrow : NULL, *rasterBufferPP); if (compression == COMP_SCANLINE) @@ -988,8 +1150,8 @@ static void computeOffsetStuff(bool const offsetWanted, unsigned int const version, bool const directColor, - enum compressionType const compression, - bool const colormap, + enum CompressionType const compression, + bool const colormapped, unsigned int const colormapColorCount, unsigned int const sizePlusRasterSize, unsigned int * const nextDepthOffsetP, @@ -1003,7 +1165,7 @@ computeOffsetStuff(bool const offsetWanted, */ unsigned int const headerSize = ((version < 3) ? 16 : 24); unsigned int const colormapSize = - (colormap ? (2 + colormapColorCount * 4) : 0); + (colormapped ? (2 + colormapColorCount * 4) : 0); if (version < 3) { unsigned int const directSize = (directColor && version < 3) ? 8 : 0; @@ -1058,12 +1220,12 @@ writeBitmap(xel ** const xels, unsigned int const rowbytes, unsigned int const bpp, unsigned int const newMaxval, - enum compressionType const compression, + enum CompressionType const compression, bool const transparent, bool const directColor, bool const offsetWanted, - bool const hasColormap, - Colormap const colormap, + bool const colormapped, + Colormap * const colormapP, unsigned int const transindex, xel const transcolor, unsigned int const version, @@ -1086,11 +1248,11 @@ writeBitmap(xel ** const xels, */ struct seqBuffer * rasterBufferP; - writeCommonHeader(cols, rows, rowbytes, compression, hasColormap, + writeCommonHeader(cols, rows, rowbytes, compression, colormapped, transparent, directColor, bpp, version); bufferRaster(xels, cols, rows, maxval, rowbytes, bpp, newMaxval, - compression, directColor, colormap, &rasterBufferP); + compression, directColor, colormapP, &rasterBufferP); /* rasterSize itself takes 2 or 4 bytes */ if (version < 3) @@ -1099,7 +1261,7 @@ writeBitmap(xel ** const xels, sizePlusRasterSize = 4 + bufferLength(rasterBufferP); computeOffsetStuff(offsetWanted, version, directColor, compression, - hasColormap, hasColormap ? colormap->ncolors : 0, + colormapped, colormapped ? colormapP->ncolors : 0, sizePlusRasterSize, &nextDepthOffset, &nextBitmapOffset, &padBytesRequired); @@ -1111,7 +1273,7 @@ writeBitmap(xel ** const xels, maxval, transparent, transcolor, transindex, nextBitmapOffset); - writeColormap(hasColormap, colormap, directColor, bpp, + writeColormap(colormapped, colormapP, directColor, bpp, transparent, transcolor, maxval, version); if (compression != COMP_NONE) @@ -1134,8 +1296,8 @@ writeBitmap(xel ** const xels, int -main( int argc, char **argv ) { - struct cmdline_info cmdline; +main( int argc, const char **argv ) { + struct CmdlineInfo cmdline; unsigned int version; FILE* ifP; xel** xels; @@ -1148,10 +1310,9 @@ main( int argc, char **argv ) { unsigned int bpp; bool directColor; unsigned int newMaxval; - Colormap colormap; + Colormap * colormapP; - /* Parse default params */ - pnm_init(&argc, argv); + pm_proginit(&argc, argv); parseCommandLine(argc, argv, &cmdline); @@ -1161,18 +1322,20 @@ main( int argc, char **argv ) { pm_close(ifP); if (cmdline.verbose) - pm_message("Input is %dx%d %s, maxval %d", + pm_message("Input is %ux%u %s, maxval %u", cols, rows, formatName(format), maxval); - determinePalmFormat(cols, rows, maxval, format, xels, cmdline.depth, - cmdline.maxdepth, cmdline.colormap, cmdline.verbose, - &bpp, &directColor, &colormap); + determinePalmFormat(cols, rows, maxval, format, xels, + cmdline.depthSpec, cmdline.depth, + cmdline.maxdepthSpec, cmdline.maxdepth, + cmdline.colormap, cmdline.compression, cmdline.verbose, + &bpp, &directColor, &colormapP); newMaxval = (1 << bpp) - 1; if (cmdline.transparent) findTransparentColor(cmdline.transparent, newMaxval, directColor, - maxval, colormap, &transcolor, &transindex); + maxval, colormapP, &transcolor, &transindex); else transindex = 0; @@ -1185,7 +1348,7 @@ main( int argc, char **argv ) { writeBitmap(xels, cols, rows, maxval, rowbytes, bpp, newMaxval, cmdline.compression, !!cmdline.transparent, directColor, cmdline.offset, - cmdline.colormap, colormap, transindex, transcolor, + cmdline.colormap, colormapP, transindex, transcolor, version, cmdline.density, cmdline.withdummy); return 0; diff --git a/doc/HISTORY b/doc/HISTORY index 703d8479..d54b89be 100644 --- a/doc/HISTORY +++ b/doc/HISTORY @@ -4,49 +4,62 @@ Netpbm. CHANGE HISTORY -------------- -17.09.19 BJH Release 10.79.07 +17.09.30 BJH Release 10.80.00 + + pnmtopalm: Refuse to create a compressed image with more than 8 + bits per pixel. + + pbmtext, libnetpbm font facilities: allow glyphs in font files + that have no bitmap data; just used for their advance value to + code a space. + + pbmtext: Improve error message when there is a problem reading + the font file. + + libnetpbm font facilities: consider font invalid if a glyph is + more than 65536 pixels high or wide. + + ppmshadow: handle images with a black background and low + contrast images (i.e. little difference between foreground and + background). ppmbrighten: fix bug: red pixels change hue. Introduced in after Netpbm 10.11 (October 2002) and before Netpbm 10.18 (September 2003). -17.09.13 BJH Release 10.79.06 - palmtopnm: fix crash if invalid input contains color index that is not in the palette. Always broken (palmtopnm was new in Netpbm 9.10 (October 2001)). -17.09.10 BJH Release 10.79.05 - pnmtopalm: fix incorrect output with certain input files and -packbits_compression. Always broken. -packbits_compression was new in Netpbm 10.27 (March 2005). - pamtopdbimg: Fix incorrect output. Always broken (pamtopdbimg - was new in Netpbm 10.52.00 (October 2010)). + pnmtopalm: Correct error message recommending running + pnmquant when the real solution is to run pnmremap to modify the + image to Palm standard colors. -17.08.11 BJH Release 10.79.04 + pnmtopalm: Fix bug causing "color not in colormap" failure with + -colormap and maxval other than 255. libnetpbm: font facilities: fix invalid memory reference with certain font files. -17.08.02 BJH Release 10.79.03 - - pbmtoibm23xx: change license terms to GPL 2 or later. + libnetpbm: ppm_readcolordict: Improve error message when there is + a problem reading the color dictionary. Affects ppmhist. - Makeman: make it work with Python 3.6 II. + pgmmake: Fix bug: treats non-numeric gray-level argument as zero. + Always broken (Pgmmake was new in Netpbm 10.32, February 2006). -17.07.08 BJH Release 10.79.02 + pdbimgtopam, pamtopdbimg: fix various cases of incorrect output, + some always present (programs were new in Netpbm 10.52.00 + (October 2010)). libnetpbm: pnm_parsecolorn(), pnm_parsecolor(): fix parsing of rgb: color specifications: yields value slightly too dim. - Affects many programs. Broken in Netpbm 10.79.00 about a week - ago. + Affects many programs. Broken in Netpbm 10.79 (June 2017). -17.07.03 BJH Release 10.79.01 - - Fix 'format-security' GNU compiler warning. Introduced in - Netpbm 10.79.00 a few days ago. + Makeman: make it work with Python 3.6 II. 17.06.30 BJH Release 10.79.00 diff --git a/editor/ppmshadow b/editor/ppmshadow index a46341d7..ae6b1b0f 100755 --- a/editor/ppmshadow +++ b/editor/ppmshadow @@ -73,7 +73,7 @@ sub doVersionHack($) { sub imageDimensions($) { my ($fileName) = @_; #----------------------------------------------------------------------------- -# Return the dimensions of the Netpbm image in the named file +# Return the dimensions of the Netpbm image in the file named $fileName. #----------------------------------------------------------------------------- my ($width, $height, $depth); my $pamfileOutput = `pamfile $fileName`; @@ -86,6 +86,29 @@ sub imageDimensions($) { return ($width, $height, $depth); } +sub backgroundColor($) { + my ($fileName) = @_; +#----------------------------------------------------------------------------- +# Return the color of the backround of the image in the file named $fileName. +#----------------------------------------------------------------------------- + # We call the color of the top left pixel the background color. + + my $ppmhistOut = qx{pamcut 0 0 1 1 $fileName | ppmhist -noheader -float}; + + my ($ired, $igrn, $iblu, $lum, $count); + + if ($ppmhistOut =~ + m{\s*([01].\d+)\s*([01].\d+)\s*([01].\d+)\s*([01].\d+)\s*(\d+)}) { + ($ired, $igrn, $iblu, $lum, $count) = ($1, $2, $3, $4, $5); + } else { + die("Unrecognized format of output from 'ppmhist' shell command"); + } + my $irgb = sprintf("rgbi:%f/%f/%f", $ired, $igrn, $iblu); + + return $irgb; +} + + sub makeConvolutionKernel($$) { my ($convkernelfile, $ckern) = @_; @@ -211,19 +234,22 @@ system("ppmtoppm"); my ($sourceImageWidth, $sourceImageHeight, $sourceImageDepth) = imageDimensions($infile); -# Create an all-background-color image (same size as original image) +my $bgColorIrgb = backgroundColor($infile); + +# Create an all-background-color image (same size as original image), +# named $backgroundfile. my $backgroundfile = "$ourtmp/background.ppm"; -system("pamcut -left=0 -top=0 -width=1 -height=1 $infile | " . - "pamscale -xsize=$sourceImageWidth " . - "-ysize=$sourceImageHeight >$backgroundfile"); +system("ppmmake $bgColorIrgb $sourceImageWidth $sourceImageHeight " . + "-maxval $sourceImageDepth " . + ">$backgroundfile"); -# Create mask file for background, named $bgmaskfile. It is a PBM, white -# wherever there is background image in the input. +# Create mask file for background, named $bgmaskfile. It is a PBM, white +# wherever there is background image in the input. my $bgmaskfile = "$ourtmp/bgmask.pbm"; -system("pamarith -difference $infile $backgroundfile | pnminvert | ppmtopgm " . - "| pgmtopbm -thresh -value 1.0 >$bgmaskfile"); +system("ppmchange -remainder=black $bgColorIrgb white $infile | " . + "ppmtopgm | pgmtopbm -threshold -value=0.5 >$bgmaskfile"); my $ckern = $convolve <= 11 ? $convolve : 11; diff --git a/generator/pamgradient.c b/generator/pamgradient.c index 57e78288..95e4d8c4 100644 --- a/generator/pamgradient.c +++ b/generator/pamgradient.c @@ -77,6 +77,7 @@ parseCommandLine(int argc, const char **argv, pm_error("height argument must be a positive number. You " "specified '%s'", argv[6]); } + free(option_def); } diff --git a/generator/pbmtext.c b/generator/pbmtext.c index 357f0429..e25c6bbe 100644 --- a/generator/pbmtext.c +++ b/generator/pbmtext.c @@ -18,11 +18,13 @@ #include <math.h> #include <limits.h> #include <assert.h> +#include <setjmp.h> #include "pm_c_util.h" #include "mallocvar.h" #include "nstring.h" #include "shhopt.h" +#include "pm.h" #include "pbm.h" #include "pbmfont.h" @@ -157,6 +159,37 @@ reportFont(struct font * const fontP) { +static struct font * +fontFromFile(const char * const fileName) { + + struct font * retval; + + jmp_buf jmpbuf; + int rc; + + rc = setjmp(jmpbuf); + + if (rc == 0) { + /* This is the normal program flow */ + pm_setjmpbuf(&jmpbuf); + + retval = pbm_loadfont(fileName); + + pm_setjmpbuf(NULL); + } else { + /* This is the second pass, after pbm_loadfont does a longjmp + because it fails. + */ + pm_setjmpbuf(NULL); + + pm_error("Failed to load font from file '%s'", fileName); + } + + return retval; +} + + + static void computeFont(struct CmdlineInfo const cmdline, struct font ** const fontPP) { @@ -164,7 +197,7 @@ computeFont(struct CmdlineInfo const cmdline, struct font * fontP; if (cmdline.font) - fontP = pbm_loadfont(cmdline.font); + fontP = fontFromFile(cmdline.font); else { if (cmdline.builtin) fontP = pbm_defaultfont(cmdline.builtin); @@ -538,22 +571,26 @@ insertCharacter(const struct glyph * const glyphP, Insert one character (whose glyph is 'glyph') into the image bits[]. Its top left corner shall be row 'toprow', column 'leftcol'. -----------------------------------------------------------------------------*/ - unsigned int glyph_y; /* Y position within the glyph */ - - if (leftcol + glyphP->x < 0 || - leftcol + glyphP->x + glyphP->width > cols || - toprow < 0 || - toprow + glyphP->height >rows ) - pm_error("internal error. Rendering out of bounds"); - - for (glyph_y = 0; glyph_y < glyphP->height; ++glyph_y) { - unsigned int glyph_x; /* position within the glyph */ - - for (glyph_x = 0; glyph_x < glyphP->width; ++glyph_x) { - if (glyphP->bmap[glyph_y * glyphP->width + glyph_x]) { - unsigned int const col = leftcol + glyphP->x + glyph_x; - bits[toprow+glyph_y][col/8] |= PBM_BLACK << (7-col%8); - } + if (glyphP->width == 0 && glyphP->height == 0) { + /* No bitmap data. Some BDF files code space this way */ + } else { + unsigned int glyph_y; /* Y position within the glyph */ + + if (leftcol + glyphP->x < 0 || + leftcol + glyphP->x + glyphP->width > cols || + toprow < 0 || + toprow + glyphP->height >rows ) + pm_error("internal error. Rendering out of bounds"); + + for (glyph_y = 0; glyph_y < glyphP->height; ++glyph_y) { + unsigned int glyph_x; /* position within the glyph */ + + for (glyph_x = 0; glyph_x < glyphP->width; ++glyph_x) { + if (glyphP->bmap[glyph_y * glyphP->width + glyph_x]) { + unsigned int const col = leftcol + glyphP->x + glyph_x; + bits[toprow+glyph_y][col/8] |= PBM_BLACK << (7-col%8); + } + } } } } diff --git a/generator/pgmmake.c b/generator/pgmmake.c index f8f8b09c..3843e316 100644 --- a/generator/pgmmake.c +++ b/generator/pgmmake.c @@ -1,13 +1,18 @@ +#include <stdlib.h> +#include <string.h> + #include "pm_c_util.h" #include "mallocvar.h" #include "shhopt.h" #include "pgm.h" -struct cmdlineInfo { + + +struct CmdlineInfo { /* All the information the user supplied in the command line, in a form easy for the program to use. */ - gray grayLevel; + double grayLevel; unsigned int cols; unsigned int rows; gray maxval; @@ -15,9 +20,37 @@ struct cmdlineInfo { +static double +grayLevelFromArg(const char * const arg) { + + double retval; + + if (strlen(arg) < 1) + pm_error("Gray level argument is a null string"); + else { + char * endPtr; + + retval = strtod(arg, &endPtr); + + if (*endPtr != '\0') + pm_error("Gray level argument '%s' is not a floating point number", + arg); + + if (retval < 0.0) + pm_error("You can't have a negative gray level (%f)", retval); + if (retval > 1.0) + pm_error("Gray level must be in the range [0.0, 1.0]. " + "You specified %f", retval); + + } + return retval; +} + + + static void -parseCommandLine(int argc, char ** argv, - struct cmdlineInfo * const cmdlineP) { +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 @@ -34,7 +67,7 @@ parseCommandLine(int argc, char ** argv, unsigned int maxvalSpec; unsigned int option_def_index; - MALLOCARRAY(option_def, 100); + MALLOCARRAY_NOFAIL(option_def, 100); option_def_index = 0; /* incremented by OPTENTRY */ OPTENT3(0, "maxval", OPT_UINT, &cmdlineP->maxval, &maxvalSpec, 0); @@ -43,11 +76,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. */ - free (option_def); - if (!maxvalSpec) cmdlineP->maxval = PGM_MAXMAXVAL; else { @@ -65,37 +96,36 @@ parseCommandLine(int argc, char ** argv, pm_error("Only 3 arguments allowed: gray level, width, height. " "You specified %d", argc-1); else { - double const grayLevel = atof(argv[1]); - if (grayLevel < 0.0) - pm_error("You can't have a negative gray level (%f)", grayLevel); - if (grayLevel > 1.0) - pm_error("Gray level must be in the range [0.0, 1.0]. " - "You specified %f", grayLevel); - cmdlineP->grayLevel = ROUNDU(grayLevel * cmdlineP->maxval); + cmdlineP->grayLevel = grayLevelFromArg(argv[1]); cmdlineP->cols = pm_parse_width(argv[2]); cmdlineP->rows = pm_parse_height(argv[3]); } + free(option_def); } int -main(int argc, char *argv[]) { +main(int argc, const char ** const argv) { - struct cmdlineInfo cmdline; + struct CmdlineInfo cmdline; gray * grayrow; unsigned int col, row; + gray grayLevel; - pgm_init(&argc, argv); + pm_proginit(&argc, argv); parseCommandLine(argc, argv, &cmdline); + grayLevel = ROUNDU(cmdline.grayLevel * cmdline.maxval); + pgm_writepgminit(stdout, cmdline.cols, cmdline.rows, cmdline.maxval, 0); + grayrow = pgm_allocrow(cmdline.cols); /* All rows are identical. Fill once. */ for (col = 0; col < cmdline.cols; ++col) - grayrow[col] = cmdline.grayLevel; + grayrow[col] = grayLevel; for (row = 0; row < cmdline.rows; ++row) pgm_writepgmrow(stdout, grayrow, cmdline.cols, cmdline.maxval, 0); @@ -105,3 +135,5 @@ main(int argc, char *argv[]) { return 0; } + + diff --git a/lib/libpamcolor.c b/lib/libpamcolor.c index 336dcd6b..c0a57dee 100644 --- a/lib/libpamcolor.c +++ b/lib/libpamcolor.c @@ -31,30 +31,30 @@ -static void -computeHexTable(int * const hexit) { - - unsigned int i; - - for (i = 0; i < 256; ++i) - hexit[i] = -1; - - hexit['0'] = 0; - hexit['1'] = 1; - hexit['2'] = 2; - hexit['3'] = 3; - hexit['4'] = 4; - hexit['5'] = 5; - hexit['6'] = 6; - hexit['7'] = 7; - hexit['8'] = 8; - hexit['9'] = 9; - hexit['a'] = hexit['A'] = 10; - hexit['b'] = hexit['B'] = 11; - hexit['c'] = hexit['C'] = 12; - hexit['d'] = hexit['D'] = 13; - hexit['e'] = hexit['E'] = 14; - hexit['f'] = hexit['F'] = 15; +static unsigned int +hexDigitValue(char const digit) { + + switch (digit) { + case '0': return 0; + case '1': return 1; + case '2': return 2; + case '3': return 3; + case '4': return 4; + case '5': return 5; + case '6': return 6; + case '7': return 7; + case '8': return 8; + case '9': return 9; + case 'a': case 'A': return 10; + case 'b': case 'B': return 11; + case 'c': case 'C': return 12; + case 'd': case 'D': return 13; + case 'e': case 'E': return 14; + case 'f': case 'F': return 15; + default: + pm_error("Invalid hex digit '%c'", digit); + return 0; /* Defeat compiler warning */ + } } @@ -62,7 +62,6 @@ computeHexTable(int * const hexit) { static void parseHexDigits(const char * const string, char const delim, - const int * const hexit, samplen * const nP, unsigned int * const digitCtP) { @@ -76,11 +75,7 @@ parseHexDigits(const char * const string, if (digit == '\0') pm_error("rgb: color spec '%s' ends prematurely", string); else { - int const hexval = hexit[(unsigned int)digit]; - if (hexval == -1) - pm_error("Invalid hex digit in rgb: color spec: 0x%02x", - digit); - n = n * 16 + hexval; + n = n * 16 + hexDigitValue(digit); range *= 16; ++digitCt; } @@ -107,26 +102,22 @@ parseNewHexX11(char const colorname[], Assume colorname[] starts with "rgb:", but otherwise it might be gibberish. -----------------------------------------------------------------------------*/ - int hexit[256]; - const char * cp; unsigned int digitCt; - computeHexTable(hexit); - cp = &colorname[4]; - parseHexDigits(cp, '/', hexit, &color[PAM_RED_PLANE], &digitCt); + parseHexDigits(cp, '/', &color[PAM_RED_PLANE], &digitCt); cp += digitCt; ++cp; /* Skip the slash */ - parseHexDigits(cp, '/', hexit, &color[PAM_GRN_PLANE], &digitCt); + parseHexDigits(cp, '/', &color[PAM_GRN_PLANE], &digitCt); cp += digitCt; ++cp; /* Skip the slash */ - parseHexDigits(cp, '\0', hexit, &color[PAM_BLU_PLANE], &digitCt); + parseHexDigits(cp, '\0', &color[PAM_BLU_PLANE], &digitCt); } @@ -170,71 +161,67 @@ parseOldX11(const char * const colorname, Return as *colorP the color specified by the old X11 style color specififier colorname[] (e.g. #554055). -----------------------------------------------------------------------------*/ - int hexit[256]; - - computeHexTable(hexit); - if (!pm_strishex(&colorname[1])) pm_error("Non-hexadecimal characters in #-type color specification"); switch (strlen(colorname) - 1 /* (Number of hex digits) */) { case 3: - color[PAM_RED_PLANE] = (samplen)hexit[(unsigned int)colorname[1]]/15; - color[PAM_GRN_PLANE] = (samplen)hexit[(unsigned int)colorname[2]]/15; - color[PAM_BLU_PLANE] = (samplen)hexit[(unsigned int)colorname[3]]/15; + color[PAM_RED_PLANE] = (samplen)hexDigitValue(colorname[1])/15; + color[PAM_GRN_PLANE] = (samplen)hexDigitValue(colorname[2])/15; + color[PAM_BLU_PLANE] = (samplen)hexDigitValue(colorname[3])/15; break; case 6: color[PAM_RED_PLANE] = - ((samplen)(hexit[(unsigned int)colorname[1]] << 4) + - (samplen)(hexit[(unsigned int)colorname[2]] << 0)) + ((samplen)(hexDigitValue(colorname[1]) << 4) + + (samplen)(hexDigitValue(colorname[2]) << 0)) / 255; color[PAM_GRN_PLANE] = - ((samplen)(hexit[(unsigned int)colorname[3]] << 4) + - (samplen)(hexit[(unsigned int)colorname[4]] << 0)) + ((samplen)(hexDigitValue(colorname[3]) << 4) + + (samplen)(hexDigitValue(colorname[4]) << 0)) / 255; color[PAM_BLU_PLANE] = - ((samplen)(hexit[(unsigned int)colorname[5]] << 4) + - (samplen)(hexit[(unsigned int)colorname[6]] << 0)) + ((samplen)(hexDigitValue(colorname[5]) << 4) + + (samplen)(hexDigitValue(colorname[6]) << 0)) / 255; break; case 9: color[PAM_RED_PLANE] = - ((samplen)(hexit[(unsigned int)colorname[1]] << 8) + - (samplen)(hexit[(unsigned int)colorname[2]] << 4) + - (samplen)(hexit[(unsigned int)colorname[3]] << 0)) + ((samplen)(hexDigitValue(colorname[1]) << 8) + + (samplen)(hexDigitValue(colorname[2]) << 4) + + (samplen)(hexDigitValue(colorname[3]) << 0)) / 4095; color[PAM_GRN_PLANE] = - ((samplen)(hexit[(unsigned int)colorname[4]] << 8) + - (samplen)(hexit[(unsigned int)colorname[5]] << 4) + - (samplen)(hexit[(unsigned int)colorname[6]] << 0)) + ((samplen)(hexDigitValue(colorname[4]) << 8) + + (samplen)(hexDigitValue(colorname[5]) << 4) + + (samplen)(hexDigitValue(colorname[6]) << 0)) / 4095; color[PAM_BLU_PLANE] = - ((samplen)(hexit[(unsigned int)colorname[7]] << 8) + - (samplen)(hexit[(unsigned int)colorname[8]] << 4) + - (samplen)(hexit[(unsigned int)colorname[9]] << 0)) + ((samplen)(hexDigitValue(colorname[7]) << 8) + + (samplen)(hexDigitValue(colorname[8]) << 4) + + (samplen)(hexDigitValue(colorname[9]) << 0)) / 4095; break; case 12: color[PAM_RED_PLANE] = - ((samplen)(hexit[(unsigned int)colorname[1]] << 12) + - (samplen)(hexit[(unsigned int)colorname[2]] << 8) + - (samplen)(hexit[(unsigned int)colorname[3]] << 4) + - (samplen)(hexit[(unsigned int)colorname[4]] << 0)) + ((samplen)(hexDigitValue(colorname[1]) << 12) + + (samplen)(hexDigitValue(colorname[2]) << 8) + + (samplen)(hexDigitValue(colorname[3]) << 4) + + (samplen)(hexDigitValue(colorname[4]) << 0)) / 65535; color[PAM_GRN_PLANE] = - ((samplen)(hexit[(unsigned int)colorname[5]] << 12) + - (samplen)(hexit[(unsigned int)colorname[6]] << 8) + - (samplen)(hexit[(unsigned int)colorname[7]] << 4) + - (samplen)(hexit[(unsigned int)colorname[8]] << 0)) + ((samplen)(hexDigitValue(colorname[5]) << 12) + + (samplen)(hexDigitValue(colorname[6]) << 8) + + (samplen)(hexDigitValue(colorname[7]) << 4) + + (samplen)(hexDigitValue(colorname[8]) << 0)) / 65535; color[PAM_BLU_PLANE] = - ((samplen)(hexit[(unsigned int)colorname[9]] << 12) + - (samplen)(hexit[(unsigned int)colorname[10]] << 8) + - (samplen)(hexit[(unsigned int)colorname[11]] << 4) + - (samplen)(hexit[(unsigned int)colorname[12]] << 0)) + ((samplen)(hexDigitValue(colorname[ 9]) << 12) + + (samplen)(hexDigitValue(colorname[10]) << 8) + + (samplen)(hexDigitValue(colorname[11]) << 4) + + (samplen)(hexDigitValue(colorname[12]) << 0)) / 65535; break; diff --git a/lib/libpbmfont.c b/lib/libpbmfont.c index 77724127..8e854aa6 100644 --- a/lib/libpbmfont.c +++ b/lib/libpbmfont.c @@ -307,10 +307,13 @@ pbm_loadfont(const char * const filename) { FILE * fileP; struct font * fontP; - char line[256]; + char line[10] = "\0\0\0\0\0\0\0\0\0\0"; + /* Initialize to suppress Valgrind error which is reported when file + is empty or very small. + */ fileP = pm_openr(filename); - fgets(line, 256, fileP); + fgets(line, 10, fileP); pm_close(fileP); if (line[0] == PBM_MAGIC1 && @@ -339,8 +342,23 @@ pbm_loadpbmfont(const char * const filename) { int fcols, frows; ifP = pm_openr(filename); + font = pbm_readpbm(ifP, &fcols, &frows); + + if ((fcols - 1) / 16 >= pbm_maxfontwidth() || + (frows - 1) / 12 >= pbm_maxfontheight()) + pm_error("Absurdly large PBM font file: %s", filename); + else if (fcols < 31 || frows < 23) { + /* Need at least one pixel per character, and this is too small to + have that. + */ + pm_error("PBM font file '%s' too small to be a font file: %u x %u. " + "Minimum sensible size is 31 x 23", + filename, fcols, frows); + } + pm_close(ifP); + return pbm_dissectfont((const bit **)font, frows, fcols); } @@ -406,10 +424,23 @@ pbm_dumpfont(struct font * const fontP, } +/*---------------------------------------------------------------------------- + Routines for loading a BDF font file +-----------------------------------------------------------------------------*/ -/* Routines for loading a BDF font file */ -#define MAXBDFLINE 1024 +/* The following are not recognized in individual glyph data; library + routines do a pm_error if they see one: + + Vertical writing systems: DWIDTH1, SWIDTH1, VVECTOR, METRICSET, + CONTENTVERSION. + + The following is not recognized and is thus ignored at the global level: + DWIDTH +*/ + + +#define MAXBDFLINE 1024 /* Official Adobe document says max length of string is 65535 characters. However the value 1024 is sufficient for practical uses. @@ -428,7 +459,7 @@ typedef struct { It also functions as a work area for readline_read(). */ - const char * arg[32]; + const char * arg[6]; /* These are the words; each entry is a pointer into line[] (above) */ } Readline; @@ -448,15 +479,17 @@ readline_init(Readline * const readlineP, static void tokenize(char * const s, const char ** const words, - unsigned int const maxWordCt) { + unsigned int const wordsSz) { /*---------------------------------------------------------------------------- - Chop up 's' into words by changing space characters to NUL. Return - as 'words' pointer to the beginning of those words in 's'. + Chop up 's' into words by changing space characters to NUL. Return as + 'words' an array of pointers to the beginnings of those words in 's'. + Terminate the words[] list with a null pointer. - If there are more than 'maxWordCt' words in 's', ignore the excess on - the right. + 'wordsSz' is the number of elements of space in 'words'. If there are more + words in 's' than will fit in that space (including the terminating null + pointer), ignore the excess on the right. -----------------------------------------------------------------------------*/ - unsigned int n; + unsigned int n; /* Number of words in words[] so far */ char * p; p = &s[0]; @@ -467,12 +500,13 @@ tokenize(char * const s, *p++ = '\0'; else { words[n++] = p; - if (n >= maxWordCt-1) + if (n >= wordsSz - 1) break; while (*p && !ISSPACE(*p)) ++p; } } + assert(n <= wordsSz - 1); words[n] = NULL; } @@ -634,6 +668,9 @@ createBmap(unsigned int const glyphWidth, "BDF font file."); if (streq(readlineP->arg[0], "ATTRIBUTES")) { + /* ATTRIBUTES is defined in Glyph Bitmap Distribution Format (BDF) + Specification Version 2.1, but not in Version 2.2. + */ bool eof; readline_read(readlineP, &eof); if (eof) @@ -754,7 +791,7 @@ readEncoding(Readline * const readlineP, static void validateFontLimits(const struct font * const fontP) { - assert( pbm_maxfontheight() > 0 && pbm_maxfontwidth() > 0 ); + assert(pbm_maxfontheight() > 0 && pbm_maxfontwidth() > 0); if (fontP->maxwidth <= 0 || fontP->maxheight <= 0 || @@ -765,7 +802,7 @@ validateFontLimits(const struct font * const fontP) { fontP->x > fontP->maxwidth || fontP->y > fontP->maxheight || fontP->x + fontP->maxwidth > pbm_maxfontwidth() || - fontP->y + fontP->maxheight > pbm_maxfontheight() + fontP->y + fontP->maxheight > pbm_maxfontheight() ) { pm_error("Global font metric(s) out of bounds.\n"); @@ -779,8 +816,13 @@ validateGlyphLimits(const struct font * const fontP, const struct glyph * const glyphP, const char * const charName) { - if (glyphP->width == 0 || - glyphP->height == 0 || + /* Some BDF files code space with zero width and height, + no bitmap data and just the xadd value. + We allow zero width and height, iff both are zero. + */ + + if (((glyphP->width == 0 || glyphP->height == 0) && + !(glyphP->width == 0 && glyphP->height == 0)) || glyphP->width > fontP->maxwidth || glyphP->height > fontP->maxheight || glyphP->x < fontP->x || diff --git a/lib/libpm.c b/lib/libpm.c index dbfcac12..f9aa1aef 100644 --- a/lib/libpm.c +++ b/lib/libpm.c @@ -68,7 +68,7 @@ static pm_usererrormsgfn * userErrorMsgFn = NULL; */ static pm_usermessagefn * userMessageFn = NULL; - /* A function to call to issue an error message. + /* A function to call to issue a non-error message. NULL means use the library default: print to Standard Error */ diff --git a/lib/libppmcolor.c b/lib/libppmcolor.c index 9a1ee5c1..3378e076 100644 --- a/lib/libppmcolor.c +++ b/lib/libppmcolor.c @@ -539,15 +539,14 @@ openColornameFile(const char * const fileName, if (setjmp(jmpbuf) != 0) { pm_asprintf(errorP, "Failed to open color name file"); - pm_setjmpbuf(origJmpbufP); - pm_longjmp(); } else { + pm_setjmpbufsave(&jmpbuf, &origJmpbufP); + *filePP = pm_openColornameFile(fileName, mustOpen); *errorP = NULL; /* Would have longjmped if there were a problem */ - - pm_setjmpbuf(origJmpbufP); } + pm_setjmpbuf(origJmpbufP); } @@ -733,7 +732,7 @@ ppm_readcolordict(const char * const fileName, if (error) { pm_errormsg("%s", error); pm_strfree(error); - ppm_freecolorhash(cht); + pm_longjmp(); } else { if (chtP) *chtP = cht; diff --git a/lib/util/runlength.c b/lib/util/runlength.c index e5c60db0..a4bb9057 100644 --- a/lib/util/runlength.c +++ b/lib/util/runlength.c @@ -12,8 +12,8 @@ documentation. This software is provided "as is" without express or implied warranty. - Functions pm_rlenc_byte() and pm_rlenc_word() are based on algorithm - originally by Robert A. Knop (rknop@mop.caltech.edu). + Functions pm_rlenc_compressbyte() and pm_rlenc_compressword() are based + on algorithm originally by Robert A. Knop (rknop@mop.caltech.edu). Those who wish to incorporate the code herein into their own programs are strongly discouraged from removing the comments within borders consisting @@ -41,12 +41,12 @@ code. This redundancy bloated the Netpbm package and made maintenance difficult. This maintenance difficulty surfaced as an issue when tests with valgrind revealed multiple memory-related problems in the above programs. - Indeed, just determining which programs do this compression by this method + Indeed, just determining which programs do compression by this method was not simple because of differences in terminology. "Packbits" is often called "run length encoding." In ppmtoilbm the name is "byterun1." - Today, all Netpbm programs that do Packbits compression use the facilities - in this file for it. + Today, all Netpbm programs that do Packbits compression with the exception + of pbmtoppa and pbmtogo use the facilities in this file for it. =============================================================================*/ #include <string.h> @@ -68,11 +68,11 @@ static const char * const errorUndefinedMode = In this simple run-length encoding scheme, compressed and uncompressed strings follow a single index or "flag" byte N. There are several - variations, differing in the format of the flag byte, maximum string length - and element size (byte or word). + variations, differing in the format of the flag byte, maximum string + length and element size (byte or word). - In the most widely used version, Packbits, the meaning of the flag byte N - is defined as follows: + In the most widely used version, Packbits, the meaning of the flag byte + N is defined as follows: 0-127 means the next N+1 bytes are uncompressed. 129-255 means the next byte is to be repeated 257-N times. @@ -99,7 +99,7 @@ pm_rlenc_compressbyte(const unsigned char * const inbuf, Always encode 3-byte repeat sequences. Encode 2-byte repeat sequences only when they are at the start of the block. - This ensures that the output is never unnecessary bloated. + This ensures that the output is never unnecessarily bloated. Original algorithm by Robert A. Knop (rknop@mop.caltech.edu) -----------------------------------------------------------------------------*/ @@ -107,8 +107,17 @@ pm_rlenc_compressbyte(const unsigned char * const inbuf, size_t inCurs, outCurs; - if (mode != PM_RLE_PACKBITS) - pm_error(errorUndefinedMode, mode); + int packBase; + int packSign; + + switch (mode) { + case PM_RLE_PACKBITS: + packBase = 257 ; packSign = -1; break; + case PM_RLE_PALMPDB: + packBase = 127 ; packSign = +1; break; + default: + pm_error(errorUndefinedMode, mode); + } for (inCurs = 0, outCurs = 0; inCurs < inSize; ) { if ((inCurs < inSize - 1) && (inbuf[inCurs] == inbuf[inCurs+1])) { @@ -121,8 +130,7 @@ pm_rlenc_compressbyte(const unsigned char * const inbuf, count < maxRun; ++inCurs, ++count) ; - - outbuf[outCurs++] = (unsigned char)(257 - count); + outbuf[outCurs++] = (unsigned char) (packBase + packSign * count); outbuf[outCurs++] = inbuf[hold]; } else { /*Do a non-run*/ @@ -258,6 +266,8 @@ pm_rlenc_compressword(const uint16_t * const inbuf, number of flag bytes. The worst case happens when there are no repeat sequences in the input. + The encoder in this file is an efficient one. + The key to an efficient encoder is correct handling of short, inefficient sequences. The following algorithm (not actually used in this file) describes the idea. @@ -288,6 +298,16 @@ pm_rlenc_compressword(const uint16_t * const inbuf, - Whenever savings are larger than the added overhead, encode the run as a compressed block. + + ----------------------------------------------------------------------- + + MS-DOS/Windows BMP format note + + The compression method for BMP is a variant of packbits. + We could support the 8-bit version with some modifications to functions + in this file. Determining the worst-case output size of an efficient + coder is rather complicated because uncompressed blocks may not be less + than three bytes long and are indicated by two-byte flags. +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ @@ -318,15 +338,22 @@ pm_rlenc_maxbytes(size_t const inSize, /* number of elements */ switch (mode) { case PM_RLE_PACKBITS: - blockSize = 128; flagSize = 1; itemSize = 1; miscSize = 0; break; + case PM_RLE_PALMPDB: + blockSize = 128; flagSize = 1; itemSize = 1; miscSize = 0; + break; case PM_RLE_SGI8: - blockSize = 127; flagSize = 1; itemSize = 1; miscSize = 0; break; - case PM_RLE_GRAPHON: case PM_RLE_PPA: - blockSize = 64; flagSize = 1; itemSize = 1; miscSize = 0; break; + blockSize = 127; flagSize = 1; itemSize = 1; miscSize = 0; + break; + case PM_RLE_GRAPHON: + case PM_RLE_PPA: + blockSize = 64; flagSize = 1; itemSize = 1; miscSize = 0; + break; case PM_RLE_SGI16: - blockSize = 127; flagSize = 2; itemSize = 2; miscSize = 2; break; + blockSize = 127; flagSize = 2; itemSize = 2; miscSize = 2; + break; case PM_RLE_PALM16: - blockSize = 128; flagSize = 1; itemSize = 2; miscSize = 0; break; + blockSize = 128; flagSize = 1; itemSize = 2; miscSize = 0; + break; default: pm_error(errorUndefinedMode, mode); } diff --git a/lib/util/runlength.h b/lib/util/runlength.h index 4857ae61..de921650 100644 --- a/lib/util/runlength.h +++ b/lib/util/runlength.h @@ -18,7 +18,8 @@ enum pm_RleMode { PM_RLE_PACKBITS, /* most common mode */ PM_RLE_PPA, /* reserved */ PM_RLE_SGI8, /* reserved */ PM_RLE_SGI16, - PM_RLE_PALM16 + PM_RLE_PALM16, + PM_RLE_PALMPDB }; size_t diff --git a/pm_config.in.h b/pm_config.in.h index d156127e..2b92c617 100644 --- a/pm_config.in.h +++ b/pm_config.in.h @@ -123,7 +123,7 @@ #endif /* WIN32 is a macro that some older compilers predefine (compilers aren't - supposed to because it doesn't start with an underscore, hence the change. + supposed to because it doesn't start with an underscore, hence the change). Many build systems (project files, etc.) set WIN32 explicitly for backward compatibility. Netpbm doesn't use it. */ diff --git a/test/Test-Order b/test/Test-Order index d67c7f29..9b280d12 100644 --- a/test/Test-Order +++ b/test/Test-Order @@ -82,6 +82,7 @@ symmetry.test # Format converter tests +pamtopdbimg.test pbmtog3.test 411toppm.test eyuvtoppm.test @@ -132,7 +133,10 @@ macp-roundtrip.test mda-roundtrip.test mgr-roundtrip.test mrf-roundtrip.test +palm-roundtrip.test +palm-roundtrip2.test pcx-roundtrip.test +pdb-roundtrip.test pfm-roundtrip.test pi3-roundtrip.test pict-roundtrip.test diff --git a/test/g3-roundtrip.ok b/test/g3-roundtrip.ok index 4521d575..853d06d2 100644 --- a/test/g3-roundtrip.ok +++ b/test/g3-roundtrip.ok @@ -8,3 +8,6 @@ 0 0 0 +1777627284 265 +2985378006 3135 +3651878552 3135 diff --git a/test/g3-roundtrip.test b/test/g3-roundtrip.test index baa6ac31..10174733 100755 --- a/test/g3-roundtrip.test +++ b/test/g3-roundtrip.test @@ -19,12 +19,15 @@ pbmtog3 testgrid.pbm | \ g3topbm | pnmcrop -white -right -bottom | \ cmp -s - testgrid.pbm ; echo $? -awk 'BEGIN { print "P4"; # header - print "8 256"; - for (i=0;i<256;++i) # raster - printf("%c",i) }' > ${complete256_pbm} +# works with gawk and mawk +# produce all possible 8-bit patterns -pbmtog3 -nofixedwidth ${complete256_pbm} | g3topbm -width=8 | tee /tmp/z1 | \ +LC_ALL=C awk 'BEGIN { print "P4"; # header + print "8 256"; + for (i=0;i<256;++i) # raster + printf("%c",i) }' > ${complete256_pbm} + +pbmtog3 -nofixedwidth ${complete256_pbm} | g3topbm -width=8 | \ cmp -s - ${complete256_pbm} ; echo $? pbmtog3 -reverse -nofixedwidth ${complete256_pbm} | \ @@ -39,16 +42,20 @@ pbmtog3 -align16 ${complete256_pbm} | \ g3topbm -width=1728 | pnmcrop -white -right | \ cmp -s - ${complete256_pbm} ; echo $? -pbmmake -w 5000 5 | tee ${widew_pbm} | pbmtog3 -nofixedwidth | \ -g3topbm | \ +pbmmake -w 5000 5 > ${widew_pbm} +pbmtog3 -nofixedwidth ${widew_pbm} | g3topbm | \ cmp -s - ${widew_pbm} ; echo $? pbmtog3 -nofixedwidth ${widew_pbm} | \ g3topbm -width=5000 | \ cmp -s - ${widew_pbm} ; echo $? -pbmmake -b 5000 5 | tee ${wideb_pbm} | pbmtog3 -nofixedwidth | \ -g3topbm | \ +pbmmake -b 5000 5 > ${wideb_pbm} +pbmtog3 -nofixedwidth ${wideb_pbm} | g3topbm | \ cmp -s - ${wideb_pbm} ; echo $? +cat ${complete256_pbm} | cksum +cat ${wideb_pbm} | cksum +cat ${widew_pbm} | cksum + rm ${complete256_pbm} ${wideb_pbm} ${widew_pbm} diff --git a/test/palm-roundtrip.ok b/test/palm-roundtrip.ok new file mode 100644 index 00000000..6c9114a4 --- /dev/null +++ b/test/palm-roundtrip.ok @@ -0,0 +1,10 @@ +584219238 236 +584219238 236 +584219238 236 +584219238 236 +584219238 236 +600373046 101484 +600373046 101484 +600373046 101484 +600373046 101484 +600373046 101484 diff --git a/test/palm-roundtrip.test b/test/palm-roundtrip.test new file mode 100755 index 00000000..5570e968 --- /dev/null +++ b/test/palm-roundtrip.test @@ -0,0 +1,32 @@ +#! /bin/bash +# This script tests: pnmtopalm palmtopnm +# Also requires: pamdepth pnmquant + +tmpdir=${tmpdir:-/tmp} +test4bit_pgm=${tmpdir}/test4bit.pgm +test256color_ppm=${tmpdir}/test256color.ppm + +# Test 1. Should print 584219238 236 5 times + +pamdepth 15 testgrid.pbm | tee ${test4bit_pgm} | cksum + +for flags in "" \ + "-scanline_compression" \ + "-rle_compression" \ + "-packbits_compression" + do pnmtopalm $flags ${test4bit_pgm} | palmtopnm | cksum; done + +rm ${test4bit_pgm} + + +# Test 2. Should print 600373046 101484 5 times + +pnmquant 256 testimg.ppm | tee ${test256color_ppm} | cksum + +for flags in "" \ + "-scanline_compression" \ + "-rle_compression" \ + "-packbits_compression" + do pnmtopalm -colormap $flags ${test256color_ppm} | palmtopnm | cksum; done + +rm ${test256color_ppm} diff --git a/test/palm-roundtrip2.ok b/test/palm-roundtrip2.ok new file mode 100644 index 00000000..0060d7b6 --- /dev/null +++ b/test/palm-roundtrip2.ok @@ -0,0 +1,5 @@ +438301914 101484 +438301914 101484 +438301914 101484 +438301914 101484 +438301914 101484 diff --git a/test/palm-roundtrip2.test b/test/palm-roundtrip2.test new file mode 100755 index 00000000..4c94df8c --- /dev/null +++ b/test/palm-roundtrip2.test @@ -0,0 +1,31 @@ +#! /bin/bash +# This script tests: pnmtopalm palmtopnm +# Also requires: pnmremap + +tmpdir=${tmpdir:-/tmp} +test256color_ppm=${tmpdir}/test256color.ppm + +if [ ${CHECK_TYPE} = install ] + then mapfile=$(netpbm-config --datadir)/palmcolor8.map + else mapfile=${PALMMAPDIR}/palmcolor8.map +fi + +if [ ! -f ${mapfile} ] + then + echo "Cannot find palmcolor8.map. Skipping." 1>&2 + exit 80; +fi + + +# Test. Should print 438301914 101484 +# 5 times + +pnmremap -mapfile=${mapfile} testimg.ppm | tee ${test256color_ppm} | cksum + +for flags in "" \ + "-scanline_compression" \ + "-rle_compression" \ + "-packbits_compression" + do pnmtopalm -colormap $flags ${test256color_ppm} | palmtopnm | cksum; done + +rm ${test256color_ppm} diff --git a/test/pamtopdbimg.ok b/test/pamtopdbimg.ok new file mode 100644 index 00000000..57258940 --- /dev/null +++ b/test/pamtopdbimg.ok @@ -0,0 +1,20 @@ +high compression +2 +1 +poor compression +1 +2 +long titles +268 +0 +268 +0 +0 +1 +large notefile +3344 +0 +68885 +0 +0 +1 diff --git a/test/pamtopdbimg.test b/test/pamtopdbimg.test new file mode 100755 index 00000000..267f6bc8 --- /dev/null +++ b/test/pamtopdbimg.test @@ -0,0 +1,86 @@ +#! /bin/bash +# This script tests: pamtopdbimg +# Also requires: pgmmake pgmnoise + +tmpdir=${tmpdir:-/tmp} +mono_pgm=${tmpdir}/graymono.pgm +noise_pgm=${tmpdir}/graynoise.pgm +text65533=${tmpdir}/ascii65533.txt +text65534=${tmpdir}/ascii65534.txt +text65597=${tmpdir}/ascii65597.txt + +# The PDB image format has a time stamp. This causes pamtopdbimg +# to produce slightly different output files with each invocation. + +# Test 1. High compression +# -maybecompressed should produce a compressed file +# Should print: +# 2 +# 1 +echo high compression +pgmmake 0.5 -maxval=15 160 160 > ${mono_pgm} +for flag in "-compressed" "-maybecompressed" "-uncompressed" + do + pamtopdbimg $flag ${mono_pgm} | wc -c + done | uniq -c | awk '{print $1}' +rm ${mono_pgm} + + +# Test 2. Poor compression +# -maybecompressed should produce a uncompressed file +# Should print: +# 1 +# 2 +echo poor compression +pgmnoise -maxval=15 -randomseed=0 160 160 > ${noise_pgm} +for flag in "-compressed" "-maybecompressed" "-uncompressed" + do + pamtopdbimg $flag ${noise_pgm} | wc -c + done | uniq -c | awk '{print $1}' +rm ${noise_pgm} + + +# Test 3. long titles +# Should succeed twice and fail once, producing: +# 268 +# 0 +# 268 +# 0 +# 1 +# 0 +echo long titles +for flag in "" "-title=0123456789012345678901234567890" \ + "-title=01234567890123456789012345678901" + do + pamtopdbimg $flag testgrid.pbm | wc -c ; echo ${PIPESTATUS[0]} + done + + +# Test 4. large notefile +# Should succeed twice and fail once, producing: +# 3344 +# 0 +# 68886 +# 0 +# 1 +# 0 +echo large notefile +awk 'BEGIN { ABC="ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + abc="abcdefghijklmnopqrstuvwxyz"; + digits="0123456789"; string =""; + + while ( length(string) < 65597 ) + string = string ABC abc digits; + + print string }' > ${text65597} + +head -c 65533 ${text65597} > ${text65533} +head -c 65534 ${text65597} > ${text65534} +pamtopdbimg -uncompressed testgrid.pbm | wc -c + echo ${PIPESTATUS[0]} +pamtopdbimg -uncompressed -notefile=${text65533} testgrid.pbm | wc -c + echo ${PIPESTATUS[0]} +pamtopdbimg -uncompressed -notefile=${text65534} testgrid.pbm | wc -c + echo ${PIPESTATUS[0]} + +rm ${text65533} ${text65534} ${text65597} \ No newline at end of file diff --git a/test/pdb-roundtrip.ok b/test/pdb-roundtrip.ok new file mode 100644 index 00000000..cb920669 --- /dev/null +++ b/test/pdb-roundtrip.ok @@ -0,0 +1,18 @@ +pbm grid +2224198737 25671 +2224198737 25671 +2224198737 25671 +pbm tiled +0 0 0 +0 0 0 +0 0 0 +0 0 0 +pgm ellipse +0 0 0 +0 0 0 +0 0 0 +0 0 0 +pgm ellipse -4depth +0 0 0 +0 0 0 +0 0 0 diff --git a/test/pdb-roundtrip.test b/test/pdb-roundtrip.test new file mode 100755 index 00000000..a6de93d2 --- /dev/null +++ b/test/pdb-roundtrip.test @@ -0,0 +1,55 @@ +#! /bin/bash +# This script tests: pamtopdbimg pdbimgtopam +# Also requires: pnmtile pgmramp pamtopnm + +tmpdir=${tmpdir:-/tmp} +tiled_pbm=${tmpdir}/tiled.pbm +ellipse_pgm=${tmpdir}/ellipse.pgm + + +# Test 1. Should produce 2224198737 25671 +# 3 times +echo pbm grid +for flag in "-compressed" "-maybecompressed" "-uncompressed" + do + pamtopdbimg ${flag} testgrid.pbm | pdbimgtopam | cksum + done + + +# Test 2. Should produce 0 0 0 +# 4 times +echo pbm tiled +pnmtile 160 160 testgrid.pbm > ${tiled_pbm} +for flag in "" "-compressed" "-maybecompressed" "-uncompressed" + do + pamtopdbimg ${flag} ${tiled_pbm} | pdbimgtopam | pamtopnm | \ + cmp -s - ${tiled_pbm} + echo ${PIPESTATUS[0]} ${PIPESTATUS[1]} $? + done +rm ${tiled_pbm} + + +# Test 3. Should produce 0 0 0 +# 4 times +echo pgm ellipse +pgmramp -ellipse 160 160 -maxval=3 > ${ellipse_pgm} +for flag in "" "-compressed" "-maybecompressed" "-uncompressed" + do + pamtopdbimg ${flag} ${ellipse_pgm} | pdbimgtopam | \ + pamtopnm | pamdepth 3 | cmp -s - ${ellipse_pgm} + echo ${PIPESTATUS[0]} ${PIPESTATUS[1]} $? + done +rm ${ellipse_pgm} + + +# Test 3. Should produce 0 0 0 +# 3 times +echo pgm ellipse -4depth +pgmramp -ellipse 160 160 -maxval=15 > ${ellipse_pgm} +for flag in "-compressed" "-maybecompressed" "-uncompressed" + do + pamtopdbimg -4depth ${flag} ${ellipse_pgm} | pdbimgtopam | \ + pamtopnm | pamdepth 15 | cmp -s - ${ellipse_pgm} + echo ${PIPESTATUS[0]} ${PIPESTATUS[1]} $? + done +rm ${ellipse_pgm} \ No newline at end of file diff --git a/test/pnmquantall.ok b/test/pnmquantall.ok index bd5bbc05..172eb0aa 100644 --- a/test/pnmquantall.ok +++ b/test/pnmquantall.ok @@ -1,5 +1,5 @@ got color map -2373957371 33838 testimg.red -3892560659 33838 testimg.grn -1383839923 33838 testimg.blu +2373957371 33838 +3892560659 33838 +1383839923 33838 1 diff --git a/test/pnmquantall.test b/test/pnmquantall.test index 7d9ddf82..cd01bb8d 100755 --- a/test/pnmquantall.test +++ b/test/pnmquantall.test @@ -2,16 +2,26 @@ # This script tests: pnmquantall # Also requires: ppmtorgb3 pgmhist -ppmtorgb3 testimg.ppm +tmpdir=${tmpdir:-/tmp} +test_ppm=${tmpdir}/testimg.ppm -pnmquantall 20 testimg.red testimg.grn testimg.blu +cp testimg.ppm ${tmpdir} && +ppmtorgb3 ${test_ppm} -for i in testimg.red testimg.grn testimg.blu +test_red=${tmpdir}/testimg.red +test_grn=${tmpdir}/testimg.grn +test_blu=${tmpdir}/testimg.blu + +pnmquantall 20 ${test_red} ${test_grn} ${test_blu} + +for i in ${test_red} ${test_grn} ${test_blu} do -cksum $i +cat $i | cksum done # Should print 1 -pnmcat testimg.red testimg.grn testimg.blu -tb | \ +pnmcat ${test_red} ${test_grn} ${test_blu} -tb | \ pgmhist -m | awk '$2>0 {s++}; END { print (s<=20) }' + +rm ${test_red} ${test_grn} ${test_blu} ${test_ppm} \ No newline at end of file diff --git a/test/ppmbrighten.ok b/test/ppmbrighten.ok index 65feb812..0ce9f722 100644 --- a/test/ppmbrighten.ok +++ b/test/ppmbrighten.ok @@ -1,3 +1,3 @@ -2737989845 101484 -1059247992 101484 -32344911 101484 +2634278866 101484 +2791274519 101484 +2464564658 101484 diff --git a/version.mk b/version.mk index 169bc3a5..04bdfba1 100644 --- a/version.mk +++ b/version.mk @@ -1,3 +1,3 @@ NETPBM_MAJOR_RELEASE = 10 -NETPBM_MINOR_RELEASE = 79 -NETPBM_POINT_RELEASE = 7 +NETPBM_MINOR_RELEASE = 80 +NETPBM_POINT_RELEASE = 0 |