diff options
author | giraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8> | 2012-06-28 05:40:29 +0000 |
---|---|---|
committer | giraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8> | 2012-06-28 05:40:29 +0000 |
commit | 55126ce6413d25c273b8d83b6cfde80a636cc452 (patch) | |
tree | 54617ff8926e9ce199d05fd1bf3bc29633ff1180 | |
parent | 04aa5295047a9bdfb538f7321beae49c960e3a8c (diff) | |
parent | f368d452fdc78a76bb9f57cdd758e4ea38711e0f (diff) | |
download | netpbm-mirror-55126ce6413d25c273b8d83b6cfde80a636cc452.tar.gz netpbm-mirror-55126ce6413d25c273b8d83b6cfde80a636cc452.tar.xz netpbm-mirror-55126ce6413d25c273b8d83b6cfde80a636cc452.zip |
Update to 10.59.00
git-svn-id: http://svn.code.sf.net/p/netpbm/code/advanced@1705 9d0c8265-081b-0410-96cb-a4ca84ce46f8
-rw-r--r-- | config.mk.in | 11 | ||||
-rw-r--r-- | converter/other/Makefile | 6 | ||||
-rw-r--r-- | converter/other/giftopnm.c | 1393 | ||||
-rw-r--r-- | converter/other/pamtogif.c | 4 | ||||
-rw-r--r-- | doc/HISTORY | 20 | ||||
-rw-r--r-- | doc/USERDOC | 5 | ||||
-rw-r--r-- | editor/pnmconvol.c | 2 | ||||
-rwxr-xr-x | editor/pnmquant | 2 | ||||
-rw-r--r-- | editor/ppmdraw.c | 46 | ||||
-rw-r--r-- | generator/pamstereogram.c | 72 | ||||
-rw-r--r-- | version.mk | 4 |
11 files changed, 1021 insertions, 544 deletions
diff --git a/config.mk.in b/config.mk.in index 490363aa..53950a5f 100644 --- a/config.mk.in +++ b/config.mk.in @@ -375,12 +375,11 @@ TIFFHDR_DIR = #TIFFHDR_DIR = /usr/local1/DEC/include # Some TIFF libraries do Jpeg and/or Z (flate) compression and thus any -# program linked with the TIFF library needs a Jpeg and/or Z library. -# Some TIFF libraries have such library statically linked in, but others -# need it to be dynamically linked at program load time. -# Make this 'N' if youf TIFF library doesn't need such dynamic linking. -# As of 2005.01, the most usual build of the TIFF library appears to require -# both. +# program linked with the TIFF library needs a Jpeg and/or Z library. Some +# TIFF libraries have such library statically linked in, but others need it to +# be linked with the program at link-edit time or dynamically at program load +# time. Make this 'N' if your TIFF library doesn't need such linking. As of +# 2005.01, the most usual build of the TIFF library appears to require both. TIFFLIB_NEEDS_JPEG = Y TIFFLIB_NEEDS_Z = Y diff --git a/converter/other/Makefile b/converter/other/Makefile index 75346115..294eefc4 100644 --- a/converter/other/Makefile +++ b/converter/other/Makefile @@ -95,6 +95,12 @@ ifeq ($(TIFFLIB_NEEDS_Z),Y) endif endif +# TIFFLIB_USERLIBS is for the user to set manually, on the make command line +# or manually added to config.mk. There are arcane situations where the TIFF +# library refers to additional libraries not handled above. + +TIFFLIB_EXTRALIBS += $(TIFFLIB_USERLIBS) + # Pnmtops's "flate" compression function requires libz. But if we don't # have libz, we still build Pnmtops; we just omit the flate compression # capability. diff --git a/converter/other/giftopnm.c b/converter/other/giftopnm.c index faab1086..cb75088b 100644 --- a/converter/other/giftopnm.c +++ b/converter/other/giftopnm.c @@ -8,8 +8,9 @@ /* | provided "as is" without express or implied warranty. | */ /* +-------------------------------------------------------------------+ */ -/* There is a copy of the GIF89 specification, as defined by its - inventor, Compuserve, in 1989, at http://members.aol.com/royalef/gif89a.txt +/* There is a copy of the GIF89 specification, as defined by its inventor, + Compuserve, in 1990 at: + http://www.w3.org/Graphics/GIF/spec-gif89a.txt This covers the high level format, but does not cover how the "data" contents of a GIF image represent the raster of color table indices. @@ -18,7 +19,6 @@ describe the Lempel-Ziv base. */ -#define _BSD_SOURCE /* Make sure strcasecmp() is in string.h */ #include <string.h> #include <assert.h> @@ -48,18 +48,18 @@ #define LITTLE_ENDIAN 1 #endif +#ifndef FASTPBMRENDER + #define FASTPBMRENDER TRUE +#endif -static __inline__ bool -ReadOK(FILE * const fileP, - unsigned char * const buffer, - size_t const len) { - - size_t bytesRead; +static const bool useFastPbmRender = FASTPBMRENDER; - bytesRead = fread(buffer, len, 1, fileP); +#ifndef REPORTLZWCODES + #define REPORTLZWCODES FALSE +#endif - return (bytesRead != 0); -} +static const bool wantLzwCodes = REPORTLZWCODES; + /* In verbose output, include all the LZW codes */ @@ -71,7 +71,7 @@ readFile(FILE * const ifP, size_t bytesRead; - bytesRead = fread(buffer, len, 1, ifP); + bytesRead = fread(buffer, 1, len, ifP); if (bytesRead == len) *errorP = NULL; @@ -89,23 +89,19 @@ readFile(FILE * const ifP, -#define LM_to_uint(a,b) (((b)<<8)|(a)) - -static int const maxnum_lzwCode = (1<<MAX_LZW_BITS); - struct cmdlineInfo { /* All the information the user supplied in the command line, in a form easy for the program to use. */ - const char * input_filespec; /* Filespecs of input files */ + const char * inputFilespec; /* Filespecs of input files */ unsigned int verbose; /* -verbose option */ unsigned int comments; /* -comments option */ - bool all_images; /* He wants all the images */ - unsigned int image_no; + bool allImages; /* He wants all the images */ + unsigned int imageNum; /* image number he wants from input, starting at 0. Undefined - if all_images is TRUE + if allImages is TRUE */ - const char * alpha_filename; + const char * alphaFileName; unsigned int quitearly; unsigned int repair; }; @@ -143,7 +139,7 @@ parseCommandLine(int argc, char ** argv, &cmdlineP->repair, 0); OPTENT3(0, "image", OPT_STRING, &image, &imageSpec, 0); - OPTENT3(0, "alphaout", OPT_STRING, &cmdlineP->alpha_filename, + OPTENT3(0, "alphaout", OPT_STRING, &cmdlineP->alphaFileName, &alphaSpec, 0); opt.opt_table = option_def; @@ -153,110 +149,171 @@ parseCommandLine(int argc, char ** argv, pm_optParseOptions3( &argc, argv, opt, sizeof(opt), 0); /* Uses and sets argc, argv, and some of *cmdlineP and others. */ + free(option_def); + if (!imageSpec) { - cmdlineP->image_no = 0; - cmdlineP->all_images = FALSE; + cmdlineP->imageNum = 0; + cmdlineP->allImages = FALSE; } else { - if (strcasecmp(image, "all") == 0) - cmdlineP->all_images = TRUE; - else { + if (strcaseeq(image, "all")) { + cmdlineP->allImages = TRUE; + } else { char * tailptr; - long const imageNo = strtol(image, &tailptr, 10); + long const imageNum = strtol(image, &tailptr, 10); if (*tailptr != '\0') pm_error("Invalid value for '-image' option. Must be either " "a number or 'all'. You specified '%s'", image); - else if (imageNo < 0) + else if (imageNum < 0) pm_error("Invalid value for '-image' option. Must be " - "positive. You specified %ld", imageNo); - else if (imageNo == 0) + "positive. You specified %ld", imageNum); + else if (imageNum == 0) pm_error("Invalid value for 'image' option. You specified " "zero. The first image is 1."); - cmdlineP->all_images = FALSE; - cmdlineP->image_no = (unsigned int) imageNo - 1; + cmdlineP->allImages = FALSE; + cmdlineP->imageNum = (unsigned int) imageNum - 1; } } if (argc-1 == 0) - cmdlineP->input_filespec = "-"; + cmdlineP->inputFilespec = "-"; else if (argc-1 != 1) pm_error("Program takes zero or one argument (filename). You " "specified %d", argc-1); else - cmdlineP->input_filespec = argv[1]; + cmdlineP->inputFilespec = argv[1]; if (!alphaSpec) - cmdlineP->alpha_filename = NULL; + cmdlineP->alphaFileName = NULL; } -typedef unsigned char gifColorMap[3][MAXCOLORMAPSIZE]; + +typedef struct { + unsigned char map[MAXCOLORMAPSIZE][3]; + unsigned int size; +} GifColorMap; + +/*---------------------------------------------------------------------- + On GIF color maps: + + The color map can have any number of colors up to 256. If the color map + size is not a power of 2 the table is padded up. The LZW "clear" control + code is always placed at a power of 2. + + The color map and code table of an image with three colors (black, white + and red) will look like this: + + 0: black + 1: white + 2: red + 3: (unused) + 4: clear code + 5: end code + 6: first LZW string code + 7: second LZW string code + ... + 4095: last LZW string code + + Some GIFs have odd color maps. + + (1) Some encoders use fixed color maps. A GIF image produced by this + kind of encoder may have colors in the table which never appear in + the image. + + Note that we make the decision on whether the output should be PBM, + PGM or PPM by scanning through the color map, not the entire image. + Any unused colors will influence our decision. + + (2) There are GIF editors which allow one to rewrite the color map. + These programs will produce color maps with multiple entries for the + same color. + + (3) Some encoders put the transparent code outside the color map. + (In the above example, the unused value 3.) Around 2000 there were + several encoders that did this, including "Animation Gif Maker + (GifAnim)". As of 2012, such images are rare. We reject them with + an error message unless -repair is specified. +-----------------------------------------------------------------------*/ + struct gifScreen { unsigned int Width; unsigned int Height; - gifColorMap ColorMap; - unsigned int ColorMapSize; - /* Number of colors in the color map. */ + GifColorMap ColorMap; unsigned int ColorResolution; unsigned int Background; unsigned int AspectRatio; /* Aspect ratio of each pixel, times 64, minus 15. (i.e. 1 => 1:4). But Zero means 1:1. */ - int hasGray; + bool hasGray; /* Boolean: global colormap has at least one gray color (not counting black and white) */ - int hasColor; + bool hasColor; /* Boolean: global colormap has at least one non-gray, non-black, non-white color */ }; struct gif89 { - int transparent; - int delayTime; - int inputFlag; - int disposal; + bool haveTransColor; + /* The GIF specifies a transparent background color */ + unsigned int transparentIndex; + /* The color index of the color which is the transparent + background color. + + Meaningful only when 'haveTransColor' is true + */ + bool haveDelayTime; + unsigned int delayTime; + bool haveInputFlag; + unsigned char inputFlag; + bool haveDisposal; + unsigned char disposal; }; static void initGif89(struct gif89 * const gif89P) { - gif89P->transparent = -1; - gif89P->delayTime = -1; - gif89P->inputFlag = -1; - gif89P->disposal = -1; + gif89P->haveTransColor = false; + gif89P->haveDelayTime = false; + gif89P->haveInputFlag = false; + gif89P->haveDisposal = false; } -static int verbose; -int showComment; +static bool verbose; +static bool showComment; static void -readColorMap(FILE *ifP, const int colormapsize, - unsigned char colormap[3][MAXCOLORMAPSIZE], - int *hasGrayP, int * const hasColorP) { +readColorMap(FILE * const ifP, + unsigned int const cmapSize, + GifColorMap * const cmapP, + bool * const hasGrayP, + bool * const hasColorP) { - int i; - unsigned char rgb[3]; + unsigned int i; + unsigned char rgb[3]; - assert(colormapsize <= MAXCOLORMAPSIZE); + assert(cmapSize <= MAXCOLORMAPSIZE); *hasGrayP = FALSE; /* initial assumption */ *hasColorP = FALSE; /* initial assumption */ - for (i = 0; i < colormapsize; ++i) { - if (! ReadOK(ifP, rgb, sizeof(rgb))) - pm_error("Unable to read Color %d from colormap", i); + for (i = 0; i < cmapSize; ++i) { + const char * error; + readFile(ifP, rgb, sizeof(rgb), &error); + if (error) + pm_error("Unable to read Color %u from colormap. %s", i, error); - colormap[CM_RED][i] = rgb[0] ; - colormap[CM_GRN][i] = rgb[1] ; - colormap[CM_BLU][i] = rgb[2] ; + cmapP->map[i][CM_RED] = rgb[0] ; + cmapP->map[i][CM_GRN] = rgb[1] ; + cmapP->map[i][CM_BLU] = rgb[2] ; if (rgb[0] == rgb[1] && rgb[1] == rgb[2]) { if (rgb[0] != 0 && rgb[0] != GIFMAXVAL) @@ -264,6 +321,7 @@ readColorMap(FILE *ifP, const int colormapsize, } else *hasColorP = TRUE; } + cmapP->size = cmapSize; } @@ -295,13 +353,17 @@ getDataBlock(FILE * const ifP, If we hit EOF or have an I/O error reading the data portion of the DataBlock, we exit the program with pm_error(). -----------------------------------------------------------------------------*/ + long const pos = ftell(ifP); + unsigned char count; - bool successfulRead; + const char * error; - long const pos = ftell(ifP); - successfulRead = ReadOK(ifP, &count, 1); - if (!successfulRead) { - pm_message("EOF or error in reading DataBlock size from file" ); + readFile(ifP, &count, sizeof(count), &error); + + if (error) { + pm_message("EOF or error in reading DataBlock size from file. %s", + error); + pm_strfree(error); *errorP = FALSE; *eofP = TRUE; *lengthP = 0; @@ -315,17 +377,18 @@ getDataBlock(FILE * const ifP, *errorP = NULL; zeroDataBlock = TRUE; } else { - bool successfulRead; + const char * error; zeroDataBlock = FALSE; - successfulRead = ReadOK(ifP, buf, count); + readFile(ifP, buf, count, &error); - if (successfulRead) - *errorP = NULL; - else + if (error) { pm_asprintf(errorP, - "EOF or error reading data portion of %u byte " - "DataBlock from file", count); + "Unable to read data portion of %u byte " + "DataBlock from file. %s", count, error); + pm_strfree(error); + } else + *errorP = NULL; } } } @@ -341,7 +404,7 @@ readThroughEod(FILE * const ifP) { If there is no EOD marker between the present file position and EOF, we read to EOF and issue warning message about a missing EOD marker. -----------------------------------------------------------------------------*/ - unsigned char buf[260]; + unsigned char buf[256]; bool eod; eod = FALSE; /* initial value */ @@ -364,6 +427,27 @@ readThroughEod(FILE * const ifP) { static void +doPlainTextExtension(FILE * const ifP) { +#if 0 + /* incomplete code fragment, attempt to handle Plain Text Extension */ + GetDataBlock(ifP, (unsigned char*) buf, &eof, &length); + + lpos = LM_to_uint(buf[0], buf[1]); + tpos = LM_to_uint(buf[2], buf[3]); + width = LM_to_uint(buf[4], buf[5]); + height = LM_to_uint(buf[6], buf[7]); + cellw = buf[8]; + cellh = buf[9]; + foreground = buf[10]; + background = buf[11]; +#else + readThroughEod(ifP); +#endif +} + + + +static void doCommentExtension(FILE * const ifP) { /*---------------------------------------------------------------------------- Read the rest of a comment extension from the input file 'ifP' and handle @@ -373,7 +457,7 @@ doCommentExtension(FILE * const ifP) { it could have nonprintable characters or embedded nulls. I don't know if the GIF spec requires regular text or not. -----------------------------------------------------------------------------*/ - char buf[255+1]; + char buf[256]; unsigned int blocklen; bool done; @@ -398,13 +482,22 @@ doCommentExtension(FILE * const ifP) { +static unsigned int +LM_to_uint(unsigned char const a, + unsigned char const b) { + + return ((unsigned int)b << 8) | ((unsigned int) a << 0); +} + + + static void doGraphicControlExtension(FILE * const ifP, struct gif89 * const gif89P) { bool eof; unsigned int length; - static unsigned char buf[256]; + unsigned char buf[256]; const char * error; getDataBlock(ifP, buf, &eof, &length, &error); @@ -418,11 +511,16 @@ doGraphicControlExtension(FILE * const ifP, "It must be at least 4 bytes; it is %d bytes.", length); else { + gif89P->haveDisposal = true; gif89P->disposal = (buf[0] >> 2) & 0x7; + gif89P->haveInputFlag = true; gif89P->inputFlag = (buf[0] >> 1) & 0x1; - gif89P->delayTime = LM_to_uint(buf[1],buf[2]); - if ((buf[0] & 0x1) != 0) - gif89P->transparent = buf[3]; + gif89P->haveDelayTime = true; + gif89P->delayTime = LM_to_uint(buf[1], buf[2]); + if ((buf[0] & 0x1) != 0) { + gif89P->haveTransColor = true; + gif89P->transparentIndex = buf[3]; + } readThroughEod(ifP); } } @@ -430,34 +528,16 @@ doGraphicControlExtension(FILE * const ifP, static void -doExtension(FILE * const ifP, int const label, struct gif89 * const gif89P) { +doExtension(FILE * const ifP, + unsigned char const label, + struct gif89 * const gif89P) { + const char * str; switch (label) { case 0x01: /* Plain Text Extension */ str = "Plain Text"; -#ifdef notdef - GetDataBlock(ifP, (unsigned char*) buf, &eof, &length); - - lpos = LM_to_uint(buf[0], buf[1]); - tpos = LM_to_uint(buf[2], buf[3]); - width = LM_to_uint(buf[4], buf[5]); - height = LM_to_uint(buf[6], buf[7]); - cellw = buf[8]; - cellh = buf[9]; - foreground = buf[10]; - background = buf[11]; - - while (GetDataBlock(ifP, (unsigned char*) buf) != 0) { - PPM_ASSIGN(xels[ypos][xpos], - cmap[CM_RED][v], - cmap[CM_GRN][v], - cmap[CM_BLU][v]); - ++index; - } -#else - readThroughEod(ifP); -#endif + doPlainTextExtension(ifP); break; case 0xff: /* Application Extension */ str = "Application"; @@ -472,16 +552,15 @@ doExtension(FILE * const ifP, int const label, struct gif89 * const gif89P) { doGraphicControlExtension(ifP, gif89P); break; default: { - static char buf[256]; - str = buf; + char buf[256]; sprintf(buf, "UNKNOWN (0x%02x)", label); + str = buf; pm_message("Ignoring unrecognized extension (type 0x%02x)", label); readThroughEod(ifP); - } - break; + } break; } if (verbose) - pm_message(" got a '%s' extension", str ); + pm_message(" got a '%s' extension", str); } @@ -516,7 +595,7 @@ getAnotherBlock(FILE * const ifP, const char ** const errorP) { unsigned int count; - unsigned int assumed_count; + unsigned int assumedCount; bool eof; /* Shift buffer down so last two bytes are now the @@ -541,13 +620,13 @@ getAnotherBlock(FILE * const ifP, "file is malformed, but we are proceeding " "anyway as if an EOD marker were at the end " "of the file."); - assumed_count = 0; + assumedCount = 0; } else - assumed_count = count; + assumedCount = count; - gsP->streamExhausted = (assumed_count == 0); + gsP->streamExhausted = (assumedCount == 0); - gsP->bufCount += assumed_count; + gsP->bufCount += assumedCount; } } @@ -661,6 +740,9 @@ getCode_get(struct getCodeState * const gsP, } else { *codeP = bitsOfLeBuffer(gsP->buf, gsP->curbit, codeSize); + if (verbose && wantLzwCodes) + pm_message("LZW code=0x%03x [%d]", *codeP, codeSize); + gsP->curbit += codeSize; *eofP = FALSE; } @@ -669,12 +751,11 @@ getCode_get(struct getCodeState * const gsP, - struct stack { /* Stack grows from low addresses to high addresses */ - int * stack; /* malloc'ed array */ - int * sp; /* stack pointer */ - int * top; /* next word above top of stack */ + unsigned char * stack; /* malloc'ed array */ + unsigned char * sp; /* stack pointer */ + unsigned char * top; /* next word above top of stack */ }; @@ -692,7 +773,7 @@ initStack(struct stack * const stackP, unsigned int const size) { static void -pushStack(struct stack * const stackP, int const value) { +pushStack(struct stack * const stackP, unsigned char const value) { if (stackP->sp >= stackP->top) pm_error("stack overflow"); @@ -709,7 +790,7 @@ stackIsEmpty(const struct stack * const stackP) { -static int +static unsigned char popStack(struct stack * const stackP) { if (stackP->sp <= stackP->stack) @@ -767,34 +848,43 @@ termStack(struct stack * const stackP) { -----------------------------------------------------------------------------*/ +static int const maxLzwCodeCt = (1<<MAX_LZW_BITS); struct decompressor { struct stack stack; - int fresh; + bool fresh; /* The stream is right after a clear code or at the very beginning */ - int codeSize; + unsigned int codeSize; /* The current code size -- each LZW code in this part of the image is this many bits. Ergo, we read this many bits at a time from the stream. */ - int maxnum_code; + unsigned int maxCodeCt; /* The maximum number of LZW codes that can be represented with the current code size. (1 << codeSize) */ - int next_tableSlot; + unsigned int nextTableSlot; /* Index in the code translation table of the next free entry */ unsigned int firstcode; /* This is always a true data element code */ - int prevcode; + unsigned int prevcode; /* The code just before, in the image, the one we're processing now */ - int table[2][(1 << MAX_LZW_BITS)]; /* The following are constant for the life of the decompressor */ FILE * ifP; - int init_codeSize; - int max_dataVal; - int clear_code; - int end_code; + unsigned int initCodeSize; + unsigned int cmapSize; + unsigned int maxDataVal; + unsigned int clearCode; + unsigned int endCode; + bool haveTransColor; + unsigned int transparentIndex; + /* meaningful only when 'haveTransColor' is true */ + bool tolerateBadInput; + /* We are to tolerate bad input data as best we can, rather than + just declaring an error and bailing out. + */ + unsigned int table[(1 << MAX_LZW_BITS)][2]; /* LZW code table */ }; @@ -802,10 +892,35 @@ struct decompressor { static void resetDecompressor(struct decompressor * const decompP) { - decompP->codeSize = decompP->init_codeSize+1; - decompP->maxnum_code = 1 << decompP->codeSize; - decompP->next_tableSlot = decompP->max_dataVal + 3; - decompP->fresh = 1; + decompP->codeSize = decompP->initCodeSize+1; + decompP->maxCodeCt = 1 << decompP->codeSize; + decompP->nextTableSlot = decompP->maxDataVal + 3; + decompP->fresh = TRUE; +} + + + +static void +validateTransparentIndex(unsigned int const transparentIndex, + bool const tolerateBadInput, + unsigned int const cmapSize, + unsigned int const maxDataVal) { + + if (transparentIndex >= cmapSize) { + if (tolerateBadInput) { + if (transparentIndex > maxDataVal) + pm_error("Invalid transparent index value: %d", + transparentIndex); + } else { + pm_error("Invalid transparent index value %d in image with " + "only %u colors. %s", + transparentIndex, cmapSize, + transparentIndex <= maxDataVal ? + "" : + "Use the -repair option to try to render the " + "image overriding this error."); + } + } } @@ -813,46 +928,78 @@ resetDecompressor(struct decompressor * const decompP) { static void lzwInit(struct decompressor * const decompP, FILE * const ifP, - int const init_codeSize) { + int const initCodeSize, + unsigned int const cmapSize, + bool const haveTransColor, + unsigned int const transparentIndex, + bool const tolerateBadInput) { + unsigned int const maxDataVal = (1 << initCodeSize) - 1; + if (verbose) pm_message("Image says the initial compression code size is " "%d bits", - init_codeSize); + initCodeSize); - decompP->ifP = ifP; - decompP->init_codeSize = init_codeSize; - - assert(decompP->init_codeSize < sizeof(decompP->max_dataVal) * 8); - - decompP->max_dataVal = (1 << init_codeSize) - 1; - decompP->clear_code = decompP->max_dataVal + 1; - decompP->end_code = decompP->max_dataVal + 2; + decompP->ifP = ifP; + decompP->initCodeSize = initCodeSize; + decompP->cmapSize = cmapSize; + decompP->tolerateBadInput = tolerateBadInput; + decompP->maxDataVal = maxDataVal; + decompP->clearCode = maxDataVal + 1; + decompP->endCode = maxDataVal + 2; if (verbose) - pm_message("Initial code size is %u bits; clear code = 0x%x, " - "end code = 0x%x", - decompP->init_codeSize, - decompP->clear_code, decompP->end_code); + pm_message("Initial code size is %u bits; clear code = 0x%03x, " + "end code = 0x%03x", + decompP->initCodeSize, + decompP->clearCode, decompP->endCode); /* The entries in the translation table for true data codes are - constant throughout the stream. We set them now and they never - change. + constant throughout the image. For PBM output we make an + adjustment later. Once set entries never change. */ { unsigned int i; - for (i = 0; i <= decompP->max_dataVal; ++i) { - decompP->table[0][i] = 0; - decompP->table[1][i] = i; + for (i = 0; i <= maxDataVal; ++i) { + decompP->table[i][0] = 0; + decompP->table[i][1] = i < cmapSize ? i : 0; } } + decompP->haveTransColor = haveTransColor; + decompP->transparentIndex = transparentIndex; + + if (haveTransColor) + validateTransparentIndex(transparentIndex, tolerateBadInput, + cmapSize, maxDataVal); + resetDecompressor(decompP); getCode_init(&getCodeState); decompP->fresh = TRUE; - initStack(&decompP->stack, maxnum_lzwCode * 2); + initStack(&decompP->stack, maxLzwCodeCt); + + assert(decompP->initCodeSize < sizeof(decompP->maxDataVal) * 8); +} + + + +static void +lzwAdjustForPBM(struct decompressor * const decompP, + GifColorMap const cmap) { +/*---------------------------------------------------------------------------- + In the PBM case we use the table index value directly instead of looking up + the color map for each pixel. + + Note that cmap.size is not always 2. + + Similar logic should work for PGM. +----------------------------------------------------------------------------*/ + unsigned int i; + for (i = 0; i < cmap.size; ++i) + decompP->table[i][1] = cmap.map[i][0] == 0 ? PBM_BLACK : PBM_WHITE; } @@ -866,8 +1013,32 @@ lzwTerm(struct decompressor * const decompP) { static void +pushWholeStringOnStack(struct decompressor * const decompP, + unsigned int const code0) { +/*---------------------------------------------------------------------------- + Get the whole string that compression code 'code0' represents and push + it onto the code stack so the leftmost code is on top. Set + decompP->firstcode to the first (leftmost) code in that string. +-----------------------------------------------------------------------------*/ + unsigned int code; + unsigned int stringCount; + + for (stringCount = 0, code = code0; + code > decompP->maxDataVal; + ++stringCount, code = decompP->table[code][0] + ) { + + pushStack(&decompP->stack, decompP->table[code][1]); + } + decompP->firstcode = decompP->table[code][1]; + pushStack(&decompP->stack, decompP->firstcode); +} + + + +static void expandCodeOntoStack(struct decompressor * const decompP, - int const incode, + unsigned int const incode, const char ** const errorP) { /*---------------------------------------------------------------------------- 'incode' is an LZW string code. It represents a string of true data @@ -882,64 +1053,60 @@ expandCodeOntoStack(struct decompressor * const decompP, from which it was built is invalid), fail (return text explanation as *errorP). -----------------------------------------------------------------------------*/ - int code; - const char * error; - - error = NULL; /* Initial value */ - - if (incode < decompP->next_tableSlot) - code = incode; - else { - /* It's a code that isn't in our translation table yet */ - pushStack(&decompP->stack, decompP->firstcode); - code = decompP->prevcode; + unsigned int code; + + *errorP = NULL; /* Initial value */ + + if (incode <= decompP->maxDataVal) { + if (incode < decompP->cmapSize) + code = incode; /* Direct index */ + else if (decompP->tolerateBadInput && + decompP->haveTransColor && + decompP->table[incode][1] == decompP->transparentIndex) + /* transparent code outside cmap exceptional case */ + code = incode; + else + pm_asprintf(errorP, "Error in GIF image: invalid color code %u. " + "Valid color values are 0 - %u", + incode, decompP->cmapSize - 1); } - - { - /* Get the whole string that this compression code - represents and push it onto the code stack so the - leftmost code is on top. Set decompP->firstcode to the - first (leftmost) code in that string. + else if (incode < decompP->nextTableSlot) + /* LZW string, defined */ + code = incode; + else if (incode == decompP->nextTableSlot && !decompP->fresh) { + /* It's a code that isn't in our translation table yet. + This does not happen with the decoder in a fresh state. */ + if (wantLzwCodes && verbose) + pm_message ("LZW code valid, but not in decoder table"); - unsigned int stringCount; - stringCount = 0; - - while (code > decompP->max_dataVal && !error) { - if (stringCount > maxnum_lzwCode) { - pm_asprintf(&error, - "Error in GIF image: contains LZW string loop"); - } else { - ++stringCount; - pushStack(&decompP->stack, decompP->table[1][code]); - code = decompP->table[0][code]; - } - } - decompP->firstcode = decompP->table[1][code]; pushStack(&decompP->stack, decompP->firstcode); - } + code = decompP->prevcode; + } else + pm_asprintf(errorP, "Error in GIF image: invalid LZW code"); - if (decompP->next_tableSlot < maxnum_lzwCode) { - decompP->table[0][decompP->next_tableSlot] = decompP->prevcode; - decompP->table[1][decompP->next_tableSlot] = decompP->firstcode; - ++decompP->next_tableSlot; - if (decompP->next_tableSlot >= decompP->maxnum_code) { - /* We've used up all the codes of the current code size. - Future codes in the stream will have codes one bit longer. - But there's an exception if we're already at the LZW - maximum, in which case the codes will simply continue - the same size. - */ - if (decompP->codeSize < MAX_LZW_BITS) { - ++decompP->codeSize; - decompP->maxnum_code = 1 << decompP->codeSize; + if (!*errorP) { + pushWholeStringOnStack(decompP, code); + + if (decompP->nextTableSlot < maxLzwCodeCt) { + decompP->table[decompP->nextTableSlot][0] = decompP->prevcode; + decompP->table[decompP->nextTableSlot][1] = decompP->firstcode; + ++decompP->nextTableSlot; + if (decompP->nextTableSlot >= decompP->maxCodeCt) { + /* We've used up all the codes of the current code size. + Future codes in the stream will have codes one bit longer. + But there's an exception if we're already at the LZW + maximum, in which case the codes will simply continue + the same size. + */ + if (decompP->codeSize < MAX_LZW_BITS) { + ++decompP->codeSize; + decompP->maxCodeCt = 1 << decompP->codeSize; + } } } + decompP->prevcode = incode; } - - *errorP = error; - - decompP->prevcode = incode; } @@ -948,39 +1115,63 @@ static void lzwReadByteFresh(struct getCodeState * const getCodeStateP, struct decompressor * const decompP, bool * const endOfImageP, - unsigned int * const dataReadP, + unsigned char * const dataReadP, const char ** const errorP) { - - /* Read off all initial clear codes, read the first non-clear code, - and return it. There are no strings in the table yet, so the next - code must be a direct true data code. - */ +/*---------------------------------------------------------------------------- + Read off all initial clear codes, read the first non-clear code, and return + it as *dataReadP. + + Iff we hit end of image in so doing, return *endOfImageP true and nothing as + *dataReadP. One way we hit end of image is for that first non-clear code to + be an end code. + + Assume the decompressor is fresh, i.e. there are no strings in the table + yet, so the next code must be a direct true data code. +-----------------------------------------------------------------------------*/ + unsigned int code; bool eof; + + assert(decompP->fresh); /* Entry requirement */ + + decompP->fresh = FALSE; + do { getCode_get(getCodeStateP, decompP->ifP, decompP->codeSize, - &eof, &decompP->firstcode, errorP); - decompP->prevcode = decompP->firstcode; - } while (decompP->firstcode == decompP->clear_code && !*errorP && !eof); + &eof, &code, errorP); + } while (!*errorP && !eof && code == decompP->clearCode); if (!*errorP) { if (eof) *endOfImageP = TRUE; - else if (decompP->firstcode == decompP->end_code) { + else if (code == decompP->endCode) { if (!zeroDataBlock) readThroughEod(decompP->ifP); *endOfImageP = TRUE; - } else { + } else if (code >= decompP->cmapSize) { + pm_asprintf(errorP, "Error in GIF image: invalid color code %u. " + "Valid color values are: 0 - %u", + code, decompP->cmapSize-1); + /* Set these values in order to avoid errors in the -repair + case + */ + decompP->prevcode = decompP->firstcode = 0; + *endOfImageP = FALSE; + } else { /* valid code */ + decompP->prevcode = code; + decompP->firstcode = decompP->table[code][1]; *dataReadP = decompP->firstcode; + *endOfImageP = FALSE; } } } + static void lzwReadByte(struct decompressor * const decompP, - unsigned int * const dataReadP, + unsigned char * const dataReadP, bool * const endOfImageP, const char ** const errorP) { /*---------------------------------------------------------------------------- @@ -1004,8 +1195,6 @@ lzwReadByte(struct decompressor * const decompP, *endOfImageP = FALSE; *dataReadP = popStack(&decompP->stack); } else if (decompP->fresh) { - decompP->fresh = FALSE; - lzwReadByteFresh(&getCodeState, decompP, endOfImageP, dataReadP, errorP); } else { @@ -1018,11 +1207,12 @@ lzwReadByte(struct decompressor * const decompP, pm_asprintf(errorP, "Premature end of file; no proper GIF closing"); else { - if (code == decompP->clear_code) { + if (code == decompP->clearCode) { resetDecompressor(decompP); - lzwReadByte(decompP, dataReadP, endOfImageP, errorP); + lzwReadByteFresh(&getCodeState, decompP, endOfImageP, + dataReadP, errorP); } else { - if (code == decompP->end_code) { + if (code == decompP->endCode) { if (!zeroDataBlock) readThroughEod(decompP->ifP); *endOfImageP = TRUE; @@ -1043,8 +1233,8 @@ lzwReadByte(struct decompressor * const decompP, enum pass {MULT8PLUS0, MULT8PLUS4, MULT4PLUS2, MULT2PLUS1}; static void -bumpRowInterlace(unsigned int * const rowP, - unsigned int const rows, +bumpRowInterlace(unsigned int const rows, + unsigned int * const rowP, enum pass * const passP) { /*---------------------------------------------------------------------------- Move *pixelCursorP to the next row in the interlace pattern. @@ -1097,47 +1287,57 @@ bumpRowInterlace(unsigned int * const rowP, } +static void +renderRow(unsigned char * const cmapIndexRow, + unsigned int const cols, + GifColorMap const cmap, + bool const haveTransColor, + unsigned int const transparentIndex, + FILE * const imageOutfile, + int const format, + xel * const xelrow, + FILE * const alphaFileP, + bit * const alphabits) { +/*---------------------------------------------------------------------------- + Convert one row of cmap indexes to PPM/PGM/PBM output. + + Render the alpha row to *alphaFileP iff 'alphabits' is non-NULL. If + 'haveTransColor' is false, render all zeroes (i.e. the row is + opaque). 'alphabits' is otherwise just a one-row buffer for us to use + in rendering the alpha row. + + imageOutfile is NULL if user wants only the alpha file. +----------------------------------------------------------------------------*/ + if (alphabits) { + unsigned int col; + + for (col=0; col < cols; ++col) { + alphabits[col] = + (haveTransColor && cmapIndexRow[col] == transparentIndex) ? + PBM_BLACK : PBM_WHITE; + } + pbm_writepbmrow(alphaFileP, alphabits, cols, false); + } -struct pnmBuffer { - xel ** xels; - unsigned int col; - unsigned int row; -}; + if (imageOutfile) { + if (useFastPbmRender && format == PBM_FORMAT && !haveTransColor) { -static void -addPixelToRaster(unsigned int const cmapIndex, - struct pnmBuffer * const pnmBufferP, - unsigned int const cols, - unsigned int const rows, - gifColorMap cmap, - unsigned int const cmapSize, - bool const interlace, - int const transparentIndex, - bit ** const alphabits, - enum pass * const passP) { - - if (cmapIndex >= cmapSize) - pm_error("Invalid color index %u in an image that has only " - "%u colors in the color map.", cmapIndex, cmapSize); - - assert(cmapIndex < MAXCOLORMAPSIZE); - - PPM_ASSIGN(pnmBufferP->xels[pnmBufferP->row][pnmBufferP->col], - cmap[CM_RED][cmapIndex], - cmap[CM_GRN][cmapIndex], - cmap[CM_BLU][cmapIndex]); - - if (alphabits) - alphabits[pnmBufferP->row][pnmBufferP->col] = - (cmapIndex == transparentIndex) ? PBM_BLACK : PBM_WHITE; - - ++pnmBufferP->col; - if (pnmBufferP->col == cols) { - pnmBufferP->col = 0; - if (interlace) - bumpRowInterlace(&pnmBufferP->row, rows, passP); - else - ++pnmBufferP->row; + bit * const bitrow = cmapIndexRow; + + pbm_writepbmrow(imageOutfile, bitrow, cols, false); + } else { + /* PPM, PGM and PBM with transparent */ + unsigned int col; + for (col = 0; col < cols; ++col) { + unsigned char const cmapIndex = cmapIndexRow[col]; + const unsigned char * const color = cmap.map[cmapIndex]; + assert(cmapIndex < cmap.size); + PPM_ASSIGN(xelrow[col], + color[CM_RED], color[CM_GRN],color[CM_BLU]); + } + pnm_writepnmrow(imageOutfile, xelrow, cols, + GIFMAXVAL, format, false); + } } } @@ -1148,19 +1348,18 @@ verifyPixelRead(bool const endOfImage, const char * const readError, unsigned int const cols, unsigned int const rows, - unsigned int const failedRowNum, const char ** const errorP) { if (readError) - *errorP = strdup(readError); + *errorP = pm_strdup(readError); else { if (endOfImage) pm_asprintf(errorP, "Error in GIF image: Not enough raster data to fill " - "%u x %u dimensions. Ran out of raster data in " - "row %u. The image has proper ending sequence, so " + "%u x %u dimensions. " + "The image has proper ending sequence, so " "this is not just a truncated file.", - cols, rows, failedRowNum); + cols, rows); else *errorP = NULL; } @@ -1168,67 +1367,184 @@ verifyPixelRead(bool const endOfImage, +static int +pnmFormat(bool const hasGray, + bool const hasColor) { +/*---------------------------------------------------------------------------- + The proper PNM format (PBM, PGM, or PPM) for an image described + by 'hasGray' and 'hasColor'. +-----------------------------------------------------------------------------*/ + int format; + const char * formatName; + + if (hasColor) { + format = PPM_FORMAT; + formatName = "PPM"; + } else if (hasGray) { + format = PGM_FORMAT; + formatName = "PGM"; + } else { + format = PBM_FORMAT; + formatName = "PBM"; + } + if (verbose) + pm_message("writing a %s file", formatName); + + return format; +} + + + static void -readRaster(struct decompressor * const decompP, - xel ** const xels, +makePnmRow(struct decompressor * const decompP, unsigned int const cols, unsigned int const rows, - gifColorMap cmap, - unsigned int const cmapSize, - bool const interlace, - int const transparentIndex, - bit ** const alphabits, - bool const tolerateBadInput) { - - struct pnmBuffer pnmBuffer; - enum pass pass; - bool fillingMissingPixels; + bool const fillWithZero, + unsigned char * const cmapIndexRow, + const char ** const errorP) { - pass = MULT8PLUS0; - pnmBuffer.xels = xels; - pnmBuffer.col = 0; - pnmBuffer.row = 0; - fillingMissingPixels = false; /* initial value */ + bool fillingWithZero; + unsigned int col; - while (pnmBuffer.row < rows) { - unsigned int colorIndex; + *errorP = NULL; /* initial value */ - if (fillingMissingPixels) - colorIndex = 0; - else { - const char * error; + for (col = 0, fillingWithZero = fillWithZero; + col < cols; + ++col) { - const char * readError; - unsigned int readColorIndex; - bool endOfImage; + unsigned char colorIndex; + + if (fillingWithZero) + colorIndex = 0; + else { + const char * readError; + unsigned char readColorIndex; + bool endOfImage; lzwReadByte(decompP, &readColorIndex, &endOfImage, &readError); - verifyPixelRead(endOfImage, readError, cols, rows, pnmBuffer.row, - &error); + assert(*errorP == NULL); + + verifyPixelRead(endOfImage, readError, cols, rows, errorP); if (readError) pm_strfree(readError); - if (error) { - if (tolerateBadInput) { - pm_message("WARNING: %s. " - "Filling bottom %u rows with arbitrary color", - error, rows - pnmBuffer.row); - fillingMissingPixels = true; - } else - pm_error("Unable to read input image. %s. Use the " - "-repair option to try to salvage some of " - "the image", - error); - + if (*errorP) { + /* Caller may want to try to ignore this error, so we + fill out the row with zeroes. Note that we can't possibly + have another error while doing that. + */ + fillingWithZero = true; colorIndex = 0; } else colorIndex = readColorIndex; } - addPixelToRaster(colorIndex, &pnmBuffer, cols, rows, cmap, cmapSize, - interlace, transparentIndex, alphabits, &pass); + cmapIndexRow[col] = colorIndex; + } +} + + + +static void +convertRaster(struct decompressor * const decompP, + unsigned int const cols, + unsigned int const rows, + GifColorMap const cmap, + bool const interlace, + FILE * const imageOutFileP, + FILE * const alphaFileP, + bool const hasGray, + bool const hasColor) { +/*---------------------------------------------------------------------------- + Read the raster from the GIF decompressor *decompP, and write it as a + complete PNM stream (starting with the header) on *imageOutFileP and + *alphaFileP. + + Assume that raster is 'cols' x 'rows', refers to colormap 'cmap', and is + interlaced iff 'interlace' is true. + + Assume the image has gray levels and/or color per 'hasGray' and 'hasColor'. +-----------------------------------------------------------------------------*/ + int const format = pnmFormat(hasGray, hasColor); + + enum pass pass; + bool fillingMissingPixels; + unsigned int row; + unsigned char ** cmapIndexArray; + bit * alphabits; + xel * xelrow; + unsigned int outrow; + /* Non-interlace: outrow is always 0: cmapIndexRow keeps pointing + to the single row in array. + + Interlace: outrow is modified with each call to bumpRowInterface(). + */ + + MALLOCARRAY2(cmapIndexArray, interlace ? rows : 1 , cols); + + if (imageOutFileP) + pnm_writepnminit(imageOutFileP, cols, rows, GIFMAXVAL, format, FALSE); + if (alphaFileP) + pbm_writepbminit(alphaFileP, cols, rows, FALSE); + + xelrow = pnm_allocrow(cols); + if (!xelrow) + pm_error("couldn't alloc space for image" ); + + if (alphaFileP) { + alphabits = pbm_allocrow(cols); + if (!alphabits) + pm_error("couldn't alloc space for alpha image" ); + } else + alphabits = NULL; + + fillingMissingPixels = false; /* initial value */ + pass = MULT8PLUS0; + outrow = 0; + + for (row = 0; row < rows; ++row) { + const char * problem; + makePnmRow(decompP, cols, rows, fillingMissingPixels, + cmapIndexArray[outrow], &problem); + + if (problem) { + /* makePnmRow() recovered from the problem and produced an output + row, stuffed with zeroes as necessary + */ + if (decompP->tolerateBadInput) { + pm_message("WARNING: %s. " + "Filling bottom %u rows with arbitrary color", + problem, rows - row); + fillingMissingPixels = true; + } else + pm_error("Unable to read input image. %s " + "(Output row: %u). " + "Use the -repair option to try to salvage " + "some of the image", + problem, interlace ? outrow : row); + } + + if (interlace) + bumpRowInterlace(rows, &outrow, &pass); + else + renderRow(cmapIndexArray[outrow], cols, cmap, + decompP->haveTransColor, decompP->transparentIndex, + imageOutFileP, format, xelrow, alphaFileP, alphabits); + } + /* All rows decompressed (and rendered and output if non-interlaced) */ + if (interlace) { + unsigned int row; + for (row = 0; row < rows; ++row) + renderRow(cmapIndexArray[row], cols, cmap, + decompP->haveTransColor, decompP->transparentIndex, + imageOutFileP, format, xelrow, alphaFileP, alphabits); } + + pnm_freerow(xelrow); + if (alphabits) + pbm_freerow(alphabits); + pm_freearray2((void **)cmapIndexArray); } @@ -1236,25 +1552,29 @@ readRaster(struct decompressor * const decompP, static void skipExtraneousData(struct decompressor * const decompP) { - unsigned int byteRead; + unsigned char byteRead; bool endOfImage; const char * error; + endOfImage = FALSE; /* initial value */ + lzwReadByte(decompP, &byteRead, &endOfImage, &error); if (error) pm_strfree(error); - else if (!endOfImage) { - pm_message("Extraneous data at end of image. " - "Skipped to end of image"); + else { + if (!endOfImage) { + pm_message("Extraneous data at end of image. " + "Skipped to end of image"); - while (!endOfImage && !error) - lzwReadByte(decompP, &byteRead, &endOfImage, &error); + while (!endOfImage && !error) + lzwReadByte(decompP, &byteRead, &endOfImage, &error); - if (error) { - pm_message("Error encountered skipping to end of image: %s", - error); - pm_strfree(error); + if (error) { + pm_message("Error encountered skipping to end of image: %s", + error); + pm_strfree(error); + } } } } @@ -1262,36 +1582,89 @@ skipExtraneousData(struct decompressor * const decompP) { static void +issueTransparencyMessage(bool const haveTransColor, + unsigned int const transparentIndex, + GifColorMap const cmap) { +/*---------------------------------------------------------------------------- + If user wants verbose output, tell him whether there is a transparent + background color ('haveTransColor') and if so what it is + ('transparentIndex'). + + Some GIFs put transparentIndex outside the color map. Allow this only + with "-repair", checked in lzwInit(). Here we issue a warning and report + the substitute color. +-----------------------------------------------------------------------------*/ + if (verbose) { + if (haveTransColor) { + if (transparentIndex >= cmap.size) { + const unsigned char * const color = cmap.map[0]; + pm_message("WARNING: Transparent index %u " + "is outside color map. " + "substitute background color: rgb:%02x/%02x/%02x ", + transparentIndex, + color[CM_RED], + color[CM_GRN], + color[CM_BLU] + ); + } else { + const unsigned char * const color = cmap.map[transparentIndex]; + pm_message("transparent background color: rgb:%02x/%02x/%02x " + "Index %u", + color[CM_RED], + color[CM_GRN], + color[CM_BLU], + transparentIndex + ); + } + } else + pm_message("no transparency"); + } +} + + + +static void readImageData(FILE * const ifP, - xel ** const xels, unsigned int const cols, unsigned int const rows, - gifColorMap cmap, - unsigned int const cmapSize, + GifColorMap const cmap, bool const interlace, - int const transparentIndex, - bit ** const alphabits, + bool const haveTransColor, + unsigned int const transparentIndex, + FILE * const imageOutFileP, + FILE * const alphaFileP, + bool const hasGray, + bool const hasColor, bool const tolerateBadInput) { unsigned char lzwMinCodeSize; struct decompressor decomp; - bool gotMinCodeSize; + const char * error; - gotMinCodeSize = ReadOK(ifP, &lzwMinCodeSize, 1); - if (!gotMinCodeSize) - pm_error("GIF stream ends (or read error) " + readFile(ifP, &lzwMinCodeSize, sizeof(lzwMinCodeSize), &error); + if (error) + pm_error("Can't read GIF stream " "right after an image separator; no " - "image data follows."); + "image data follows. %s", error); if (lzwMinCodeSize > MAX_LZW_BITS) pm_error("Invalid minimum code size value in image data: %u. " "Maximum allowable code size in GIF is %u", lzwMinCodeSize, MAX_LZW_BITS); - lzwInit(&decomp, ifP, lzwMinCodeSize); + lzwInit(&decomp, ifP, lzwMinCodeSize, cmap.size, + haveTransColor, transparentIndex, tolerateBadInput); - readRaster(&decomp, xels, cols, rows, cmap, cmapSize, interlace, - transparentIndex, alphabits, tolerateBadInput); + issueTransparencyMessage(haveTransColor, transparentIndex, cmap); + + if (useFastPbmRender && !hasGray && ! hasColor && !haveTransColor) { + if (verbose) + pm_message("Using fast PBM rendering"); + lzwAdjustForPBM(&decomp, cmap); + } + convertRaster(&decomp, cols, rows, cmap, interlace, + imageOutFileP, alphaFileP, + hasGray, hasColor); skipExtraneousData(&decomp); @@ -1301,80 +1674,23 @@ readImageData(FILE * const ifP, static void -writePnm(FILE * const outfileP, - xel ** const xels, - int const cols, - int const rows, - int const hasGray, - int const hasColor) { -/*---------------------------------------------------------------------------- - Write a PNM image to the current position of file *outfileP with - dimensions 'cols' x 'rows' and raster 'xels'. - - Make it PBM, PGM, or PBM according to 'hasGray' and 'hasColor'. ------------------------------------------------------------------------------*/ - int format; - const char * formatName; - - if (hasColor) { - format = PPM_FORMAT; - formatName = "PPM"; - } else if (hasGray) { - format = PGM_FORMAT; - formatName = "PGM"; - } else { - format = PBM_FORMAT; - formatName = "PBM"; - } - if (verbose) - pm_message("writing a %s file", formatName); - - if (outfileP) - pnm_writepnm(outfileP, xels, cols, rows, - (xelval) GIFMAXVAL, format, FALSE); -} - - - -static void -transparencyMessage(int const transparentIndex, - gifColorMap cmap) { +readGifHeader(FILE * const gifFileP, + struct gifScreen * const gifScreenP) { /*---------------------------------------------------------------------------- - If user wants verbose output, tell him that the color with index - 'transparentIndex' is supposed to be a transparent background color. - - If transparentIndex == -1, tell him there is no transparent background - color. ------------------------------------------------------------------------------*/ - if (verbose) { - if (transparentIndex == -1) - pm_message("no transparency"); - else - pm_message("transparent background color: rgb:%02x/%02x/%02x " - "Index %d", - cmap[CM_RED][transparentIndex], - cmap[CM_GRN][transparentIndex], - cmap[CM_BLU][transparentIndex], - transparentIndex - ); - } -} - -static void -readGifHeader(FILE * const gifFile, struct gifScreen * const gifScreenP) { -/*---------------------------------------------------------------------------- - Read the GIF stream header off the file gifFile, which is present + Read the GIF stream header off the file *gifFileP, which is present positioned to the beginning of a GIF stream. Return the info from it as *gifScreenP. -----------------------------------------------------------------------------*/ - unsigned char buf[16]; - char version[4]; - + unsigned char buf[16]; + char version[4]; + unsigned int cmapSize; + const char * error; - if (! ReadOK(gifFile,buf,6)) - pm_error("error reading magic number" ); + readFile(gifFileP, buf, 6, &error); + if (error) + pm_error("Error reading magic number. %s", error); - if (strncmp((char *)buf,"GIF",3) != 0) + if (!strneq((char *)buf, "GIF", 3)) pm_error("File does not contain a GIF stream. It does not start " "with 'GIF'."); @@ -1385,14 +1701,15 @@ readGifHeader(FILE * const gifFile, struct gifScreen * const gifScreenP) { pm_message("GIF format version is '%s'", version); if ((!streq(version, "87a")) && (!streq(version, "89a"))) - pm_error("bad version number, not '87a' or '89a'" ); + pm_error("Bad version number, not '87a' or '89a'" ); - if (! ReadOK(gifFile,buf,7)) - pm_error("failed to read screen descriptor" ); + readFile(gifFileP, buf, 7, &error); + if (error) + pm_error("Failed to read screen descriptor. %s", error); gifScreenP->Width = LM_to_uint(buf[0],buf[1]); gifScreenP->Height = LM_to_uint(buf[2],buf[3]); - gifScreenP->ColorMapSize = 1 << ((buf[4] & 0x07) + 1); + cmapSize = 1 << ((buf[4] & 0x07) + 1); gifScreenP->ColorResolution = (buf[4] & 0x70 >> 3) + 1; gifScreenP->Background = buf[5]; gifScreenP->AspectRatio = buf[6]; @@ -1405,10 +1722,10 @@ readGifHeader(FILE * const gifFile, struct gifScreen * const gifScreenP) { gifScreenP->AspectRatio == 0 ? 1 : (gifScreenP->AspectRatio + 15) / 64.0); pm_message("Colors = %d Color Resolution = %d", - gifScreenP->ColorMapSize, gifScreenP->ColorResolution); + cmapSize, gifScreenP->ColorResolution); } if (BitSet(buf[4], LOCALCOLORMAP)) { /* Global Colormap */ - readColorMap(gifFile, gifScreenP->ColorMapSize, gifScreenP->ColorMap, + readColorMap(gifFileP, cmapSize, &gifScreenP->ColorMap, &gifScreenP->hasGray, &gifScreenP->hasColor); if (verbose) { pm_message("Color map %s grays, %s colors", @@ -1460,7 +1777,7 @@ readExtensions(FILE* const ifP, unsigned char c; const char * error; - readFile(ifP, &c, 1, &error); + readFile(ifP, &c, sizeof(c), &error); if (error) { pm_asprintf(errorP, "File read error where start of image " @@ -1487,7 +1804,8 @@ readExtensions(FILE* const ifP, } else if (c == ',') imageStart = TRUE; else - pm_message("bogus character 0x%02x, ignoring", (int)c); + pm_message("Encountered invalid character 0x%02x while " + "seeking extension block, ignoring", (int)c); } } *eodP = eod; @@ -1495,20 +1813,99 @@ readExtensions(FILE* const ifP, +struct GifImageHeader { +/*---------------------------------------------------------------------------- + Information in the header (first 9 bytes) of a GIF image. +-----------------------------------------------------------------------------*/ + bool useGlobalColormap; + + /* Position of the image (max 65535) */ + unsigned int lpos; + unsigned int tpos; + + /* Dimensions of the image (max 65535) */ + unsigned int cols; + unsigned int rows; + unsigned int localColorMapSize; + bool interlaced; +}; + + + static void -reportImageInfo(unsigned int const cols, - unsigned int const rows, - bool const useGlobalColormap, - unsigned int const localColorMapSize, - bool const interlaced) { +reportImageHeader(struct GifImageHeader const imageHeader) { pm_message("reading %u by %u%s GIF image", - cols, rows, interlaced ? " interlaced" : "" ); + imageHeader.cols, imageHeader.rows, + imageHeader.interlaced ? " interlaced" : "" ); - if (useGlobalColormap) + if (imageHeader.lpos > 0 || imageHeader.tpos > 0) + pm_message(" Image left position: %u top position: %u", + imageHeader.lpos, imageHeader.tpos); + + if (imageHeader.useGlobalColormap) pm_message(" Uses global colormap"); else - pm_message(" Uses local colormap of %u colors", localColorMapSize); + pm_message(" Uses local colormap of %u colors", + imageHeader.localColorMapSize); +} + + + +static void +readImageHeader(FILE * const ifP, + struct GifImageHeader * const imageHeaderP) { + + unsigned char buf[16]; + const char * error; + + readFile(ifP, buf, 9, &error); + if (error) + pm_error("couldn't read left/top/width/height. %s", error); + + imageHeaderP->useGlobalColormap = ! BitSet(buf[8], LOCALCOLORMAP); + imageHeaderP->localColorMapSize = 1u << ((buf[8] & 0x07) + 1); + imageHeaderP->lpos = LM_to_uint(buf[0], buf[1]); + imageHeaderP->tpos = LM_to_uint(buf[2], buf[3]); + imageHeaderP->cols = LM_to_uint(buf[4], buf[5]); + imageHeaderP->rows = LM_to_uint(buf[6], buf[7]); + imageHeaderP->interlaced = !!BitSet(buf[8], INTERLACE); + + if (verbose) + reportImageHeader(*imageHeaderP); +} + + + +static void +validateWithinGlobalScreen(struct GifImageHeader const imageHeader, + struct gifScreen const gifScreen) { + + unsigned long int const rpos = imageHeader.lpos + imageHeader.cols; + unsigned long int const bpos = imageHeader.tpos + imageHeader.rows; + + if (rpos > gifScreen.Width) + pm_error("Image right end (%lu) is outside global screen: %u x %u", + rpos, gifScreen.Width, gifScreen.Height); + if (bpos > gifScreen.Height) + pm_error("Image bottom end (%lu) is outside global screen: " + "%u x %u", + bpos, gifScreen.Width, gifScreen.Height); +} + + + +static void +skipImageData(FILE * const ifP) { + unsigned char lzwMinCodeSize; + const char * error; + + readFile(ifP, &lzwMinCodeSize, sizeof(lzwMinCodeSize), &error); + if (error) { + pm_message("Unable to read file to skip image DataBlock. %s", error); + pm_strfree(error); + } + readThroughEod(ifP); } @@ -1516,85 +1913,47 @@ reportImageInfo(unsigned int const cols, static void convertImage(FILE * const ifP, bool const skipIt, - FILE * const imageout_file, - FILE * const alphafile, - struct gifScreen gifScreen, + FILE * const imageoutFileP, + FILE * const alphafileP, + struct gifScreen const gifScreen, struct gif89 const gif89, bool const tolerateBadInput) { /*---------------------------------------------------------------------------- Read a single GIF image from the current position of file 'ifP'. If 'skipIt' is TRUE, don't do anything else. Otherwise, write the - image to the current position of files 'imageout_file' and 'alphafile'. - If 'alphafile' is NULL, though, don't write any alpha information. + image to the current position of files *imageoutFileP and *alphafileP. + If *alphafileP is NULL, though, don't write any alpha information. -----------------------------------------------------------------------------*/ - unsigned char buf[16]; - bool useGlobalColormap; - xel **xels; /* The image raster, in libpnm format */ - bit **alphabits; - /* The image alpha mask, in libpbm format. NULL if we aren't computing - an alpha mask. - */ - unsigned int cols, rows; /* Dimensions of the image */ - gifColorMap localColorMap; - unsigned int localColorMapSize; - bool interlaced; + struct GifImageHeader imageHeader; + GifColorMap localColorMap; + const GifColorMap * currentColorMapP; + bool hasGray, hasColor; - if (! ReadOK(ifP,buf,9)) - pm_error("couldn't read left/top/width/height"); + readImageHeader(ifP, &imageHeader); - useGlobalColormap = ! BitSet(buf[8], LOCALCOLORMAP); - localColorMapSize = 1u << ((buf[8] & 0x07) + 1); - cols = LM_to_uint(buf[4], buf[5]); - rows = LM_to_uint(buf[6], buf[7]); - interlaced = !!BitSet(buf[8], INTERLACE); + validateWithinGlobalScreen(imageHeader, gifScreen); - if (verbose) - reportImageInfo(cols, rows, useGlobalColormap, localColorMapSize, - interlaced); - - xels = pnm_allocarray(cols, rows); - if (!xels) - pm_error("couldn't alloc space for image" ); - - if (alphafile) { - alphabits = pbm_allocarray(cols, rows); - if (!alphabits) - pm_error("couldn't alloc space for alpha image" ); - } else - alphabits = NULL; - - if (!useGlobalColormap) { - int hasGray, hasColor; - - readColorMap(ifP, localColorMapSize, localColorMap, - &hasGray, &hasColor); - transparencyMessage(gif89.transparent, localColorMap); - readImageData(ifP, xels, cols, rows, localColorMap, localColorMapSize, - interlaced, gif89.transparent, alphabits, - tolerateBadInput); - if (!skipIt) { - writePnm(imageout_file, xels, cols, rows, - hasGray, hasColor); - } + if (imageHeader.useGlobalColormap) { + currentColorMapP = &gifScreen.ColorMap; + hasGray = gifScreen.hasGray; + hasColor = gifScreen.hasColor; } else { - transparencyMessage(gif89.transparent, gifScreen.ColorMap); - readImageData(ifP, xels, cols, rows, - gifScreen.ColorMap, gifScreen.ColorMapSize, - interlaced, gif89.transparent, alphabits, - tolerateBadInput); - if (!skipIt) { - writePnm(imageout_file, xels, cols, rows, - gifScreen.hasGray, gifScreen.hasColor); - } + readColorMap(ifP, imageHeader.localColorMapSize, &localColorMap, + &hasGray, &hasColor); + currentColorMapP = &localColorMap; } - if (!skipIt && alphafile && alphabits) - pbm_writepbm(alphafile, alphabits, cols, rows, FALSE); - - pnm_freearray(xels, rows); - if (alphabits) - pbm_freearray(alphabits, rows); + if (!skipIt) { + readImageData(ifP, imageHeader.cols, imageHeader.rows, + *currentColorMapP, + imageHeader.interlaced, + gif89.haveTransColor, gif89.transparentIndex, + imageoutFileP, alphafileP, + hasGray, hasColor, + tolerateBadInput); + } else + skipImageData(ifP); } @@ -1620,17 +1979,17 @@ disposeOfReadExtensionsError(const char * const error, static void -convertImages(FILE * const ifP, - bool const allImages, - int const requestedImageSeq, - bool const drainStream, - FILE * const imageout_file, - FILE * const alphafile, - bool const tolerateBadInput) { +convertImages(FILE * const ifP, + bool const allImages, + unsigned int const requestedImageSeq, + bool const drainStream, + FILE * const imageOutFileP, + FILE * const alphaFileP, + bool const tolerateBadInput) { /*---------------------------------------------------------------------------- Read a GIF stream from file 'ifP' and write one or more images from - it as PNM images to file 'imageout_file'. If the images have transparency - and 'alphafile' is non-NULL, write PGM alpha masks to file 'alphafile'. + it as PNM images to file 'imageOutFileP'. If the images have transparency + and 'alphafile' is non-NULL, write PGM alpha masks to file 'alphaFileP'. 'allImages' means Caller wants all the images in the stream. @@ -1644,7 +2003,7 @@ convertImages(FILE * const ifP, format in the tail of the stream and there may yet be more stuff in the file when we return. -----------------------------------------------------------------------------*/ - int imageSeq; + unsigned int imageSeq; /* Sequence within GIF stream of image we are currently processing. First is 0. */ @@ -1653,12 +2012,15 @@ convertImages(FILE * const ifP, bool eod; /* We've read through the GIF terminator character */ + /* Set 'gif89' to initial values, to be updated as we encounter the + relevant extensions in the GIF stream. + */ initGif89(&gif89); readGifHeader(ifP, &gifScreen); for (imageSeq = 0, eod = FALSE; - !eod && (imageSeq <= requestedImageSeq || allImages || drainStream); + !eod && (allImages || imageSeq <= requestedImageSeq || drainStream); ++imageSeq) { const char * error; @@ -1676,9 +2038,10 @@ convertImages(FILE * const ifP, imageSeq, imageSeq > 1 ? "s" : ""); } else { if (verbose) - pm_message("Reading Image Sequence %d", imageSeq); + pm_message("Reading Image Sequence %u", imageSeq); + convertImage(ifP, !allImages && (imageSeq != requestedImageSeq), - imageout_file, alphafile, gifScreen, gif89, + imageOutFileP, alphaFileP, gifScreen, gif89, tolerateBadInput); } } @@ -1690,8 +2053,9 @@ int main(int argc, char **argv) { struct cmdlineInfo cmdline; - FILE *ifP; - FILE *alpha_file, *imageout_file; + FILE * ifP; + FILE * alphaFileP; + FILE * imageOutFileP; pnm_init(&argc, argv); @@ -1699,27 +2063,30 @@ main(int argc, char **argv) { verbose = cmdline.verbose; showComment = cmdline.comments; - ifP = pm_openr(cmdline.input_filespec); + ifP = pm_openr(cmdline.inputFilespec); - if (cmdline.alpha_filename == NULL) - alpha_file = NULL; + if (cmdline.alphaFileName == NULL) + alphaFileP = NULL; else - alpha_file = pm_openw(cmdline.alpha_filename); + alphaFileP = pm_openw(cmdline.alphaFileName); - if (alpha_file && streq(cmdline.alpha_filename, "-")) - imageout_file = NULL; + if (alphaFileP && streq(cmdline.alphaFileName, "-")) + imageOutFileP = NULL; else - imageout_file = stdout; + imageOutFileP = stdout; - convertImages(ifP, cmdline.all_images, cmdline.image_no, - !cmdline.quitearly, imageout_file, alpha_file, + convertImages(ifP, cmdline.allImages, cmdline.imageNum, + !cmdline.quitearly, imageOutFileP, alphaFileP, cmdline.repair); pm_close(ifP); - if (imageout_file != NULL) - pm_close(imageout_file); - if (alpha_file != NULL) - pm_close(alpha_file); + if (imageOutFileP != NULL) + pm_close(imageOutFileP); + if (alphaFileP != NULL) + pm_close(alphaFileP); return 0; } + + + diff --git a/converter/other/pamtogif.c b/converter/other/pamtogif.c index 1fc564ed..4dac8923 100644 --- a/converter/other/pamtogif.c +++ b/converter/other/pamtogif.c @@ -627,11 +627,11 @@ static void byteBuffer_out(byteBuffer * const byteBufferP, unsigned char const c) { /*---------------------------------------------------------------------------- - Add a byte to the end of the current data block, and if it is now 254 + Add a byte to the end of the current data block, and if it is now 255 characters, flush the data block to the output file. -----------------------------------------------------------------------------*/ byteBufferP->buffer[byteBufferP->count++] = c; - if (byteBufferP->count >= 254) + if (byteBufferP->count >= 255) byteBuffer_flush(byteBufferP); } diff --git a/doc/HISTORY b/doc/HISTORY index dabf87c6..717aea1f 100644 --- a/doc/HISTORY +++ b/doc/HISTORY @@ -4,15 +4,20 @@ Netpbm. CHANGE HISTORY -------------- -12.06.21 BJH Release 10.58.03 +12.06.28 BJH Release 10.59.00 - Build: move -lm to end of -l's. Broken at least since 10.35. + pamtogif: Make data blocks 255 bytes instead of 254. 255 is + the maximum allowed by GIF. 254 was intended to not trigger + potential bugs in programs that read the file. -12.06.10 BJH Release 10.58.02 + ppmdraw: Add 'filledcircle' command class. Thanks + Elijah Griffin <eli@panix.com>. - Build: fix bug: declines to build pnmtops. Broken in 10.58.00 + pamstereogram: Add -planes . Thanks Scott Pakin + (scott@pakin.org). -12.04.08 BJH Release 10.58.01 + pamstereogram: improve verbose output. Thanks Scott Pakin + (scott@pakin.org). pamstereogram: fix crash introduced in 10.54 (March 2011). @@ -20,6 +25,8 @@ CHANGE HISTORY Build: fix bug: declines to build pnmtops. Broken in 10.58. + Build: move -lm to end of -l's. Broken at least since 10.35. + 12.03.29 BJH Release 10.58.00 Add ppmtospu, 22 years late. @@ -51,6 +58,9 @@ CHANGE HISTORY configure: work around Perl bug that causes 'configure' to falsely conclude Svgalib is installed. + Build: Use rand/srand instead of random/srandom because the + latter are not present in Mingw builds. + Build: fix bug finding X libraries for merge buildl Build: redo conditional compilation of Windows to use diff --git a/doc/USERDOC b/doc/USERDOC index e1589689..fa0ac574 100644 --- a/doc/USERDOC +++ b/doc/USERDOC @@ -127,7 +127,9 @@ for an automated process to do it for you with any significant integrity. The examples are guidelines and you shouldn't expect them to work literally in your situtation. -Here is an example of making troff pages: +Here is an old example of making troff pages, which doesn't actually +work anymore, but might be helpful. You'll probably want to use a Subversion +export of the manual instead of a Wget download (see above). mkdir netpbmdoc cd netpbmdoc @@ -135,7 +137,6 @@ Here is an example of making troff pages: cd netpbm.sourceforge.net/doc make MAKEMAN=/usr/src/netpbm/buildtools/makeman \ -f /usr/src/netpbm/buildtools/manpage.mk manpages - make -f /usr/src/netpbm/buildtools/manpage.mk manpages make -f /usr/src/netpbm/buildtools/manpage.mk installman cd ../../.. rm -r netpbmdoc diff --git a/editor/pnmconvol.c b/editor/pnmconvol.c index d0b3b307..e5c05ebc 100644 --- a/editor/pnmconvol.c +++ b/editor/pnmconvol.c @@ -899,7 +899,7 @@ getKernel(struct cmdlineInfo const cmdline, We generate a kernel object in standard form (free of any indication of where it came from) and return a handle to it as *convKernelPP. ------------------------------------------------------------------------------*/ +----------------------------------------------------------------------------*/ struct convKernel * convKernelP; if (cmdline.pnmMatrixFileName) diff --git a/editor/pnmquant b/editor/pnmquant index 0b89dd57..ae461a3d 100755 --- a/editor/pnmquant +++ b/editor/pnmquant @@ -250,7 +250,7 @@ openSeekableAsStdin($cmdlineR->{infile}); # Save Standard Output for our eventual output open(OLDOUT, ">&STDOUT"); -select(OLDOUT); # avoids Perl bug where it says we never use STDOUT +select(OLDOUT); # avoids Perl bug where it says we never use OLDOUT my $mapfileSpec = makeColormap($cmdlineR->{ncolors}, diff --git a/editor/ppmdraw.c b/editor/ppmdraw.c index 1305a313..ba513581 100644 --- a/editor/ppmdraw.c +++ b/editor/ppmdraw.c @@ -189,6 +189,7 @@ enum drawVerb { VERB_LINE_HERE, VERB_SPLINE3, VERB_CIRCLE, + VERB_FILLEDCIRCLE, VERB_FILLEDRECTANGLE, VERB_TEXT, VERB_TEXT_HERE @@ -301,6 +302,8 @@ freeDrawCommand(const struct drawCommand * const commandP) { break; case VERB_CIRCLE: break; + case VERB_FILLEDCIRCLE: + break; case VERB_FILLEDRECTANGLE: break; case VERB_TEXT: @@ -346,6 +349,35 @@ freeScript(struct script * const scriptP) { static void +doFilledCircle(pixel ** const pixels, + unsigned int const cols, + unsigned int const rows, + pixval const maxval, + const struct drawCommand * const commandP, + const struct drawState * const drawStateP) { + + struct fillobj * fhP; + + fhP = ppmd_fill_create(); + + ppmd_circle(pixels, cols, rows, maxval, + commandP->u.circleArg.cx, + commandP->u.circleArg.cy, + commandP->u.circleArg.radius, + ppmd_fill_drawproc, + fhP); + + ppmd_fill(pixels, cols, rows, maxval, + fhP, + PPMD_NULLDRAWPROC, + &drawStateP->color); + + ppmd_fill_destroy(fhP); +} + + + +static void doTextHere(pixel ** const pixels, unsigned int const cols, unsigned int const rows, @@ -463,6 +495,9 @@ executeScript(struct script * const scriptP, PPMD_NULLDRAWPROC, &drawState.color); break; + case VERB_FILLEDCIRCLE: + doFilledCircle(pixels, cols, rows, maxval, commandP, &drawState); + break; case VERB_FILLEDRECTANGLE: ppmd_filledrectangle(pixels, cols, rows, maxval, commandP->u.filledrectangleArg.x, @@ -611,6 +646,17 @@ parseDrawCommand(struct tokenSet const commandTokens, argP->cy = atoi(commandTokens.token[2]); argP->radius = atoi(commandTokens.token[3]); } + } else if (streq(verb, "filledcircle")) { + drawCommandP->verb = VERB_FILLEDCIRCLE; + if (commandTokens.count < 4) + pm_error("Not enough tokens for a 'filledcircle' command. " + "Need %u. Got %u", 4, commandTokens.count); + else { + struct circleArg * const argP = &drawCommandP->u.circleArg; + argP->cx = atoi(commandTokens.token[1]); + argP->cy = atoi(commandTokens.token[2]); + argP->radius = atoi(commandTokens.token[3]); + } } else if (streq(verb, "filledrectangle")) { drawCommandP->verb = VERB_FILLEDRECTANGLE; if (commandTokens.count < 5) diff --git a/generator/pamstereogram.c b/generator/pamstereogram.c index 7c930894..8be4f9cf 100644 --- a/generator/pamstereogram.c +++ b/generator/pamstereogram.c @@ -91,6 +91,38 @@ struct cmdlineInfo { static void +parseNearFarPlanes(const char ** const nearFarPlanes, + float * const nearPlaneP, + float * const farPlaneP) { +/*---------------------------------------------------------------------------- + Parse nearFarPlanes option value into exactly two positive numbers +-----------------------------------------------------------------------------*/ + float nearPlane, farPlane; + + if (nearFarPlanes == NULL || nearFarPlanes[0] == NULL || + nearFarPlanes[1] == NULL || nearFarPlanes[2] != NULL) + pm_error("-planes requires exactly two positive numbers"); + + errno = 0; + nearPlane = strtof(nearFarPlanes[0], NULL); + if (errno != 0 || nearPlane <= 0.0) + pm_error("-planes requires exactly two positive numbers"); + + farPlane = strtof(nearFarPlanes[1], NULL); + if (errno != 0 || farPlane <= 0.0) + pm_error("-planes requires exactly two positive numbers"); + + if (nearPlane >= farPlane) + pm_error("-planes requires the near-plane value " + "to be less than the far-plane value"); + + *nearPlaneP = nearPlane; + *farPlaneP = farPlane; +} + + + +static void parseCommandLine(int argc, const char ** argv, struct cmdlineInfo * cmdlineP ) { @@ -113,10 +145,10 @@ parseCommandLine(int argc, unsigned int patfileSpec, texfileSpec, dpiSpec, eyesepSpec, depthSpec, guidesizeSpec, magnifypatSpec, xshiftSpec, yshiftSpec, randomseedSpec, - bgColorNameSpec, smoothingSpec; + bgColorNameSpec, smoothingSpec, planesSpec; unsigned int blackandwhite, grayscale, color; - + const char ** nearFarPlanes; MALLOCARRAY_NOFAIL(option_def, 100); @@ -159,6 +191,8 @@ parseCommandLine(int argc, &randomseedSpec, 0); OPTENT3(0, "smoothing", OPT_UINT, &cmdlineP->smoothing, &smoothingSpec, 0); + OPTENT3(0, "planes", OPT_STRINGLIST, &nearFarPlanes, + &planesSpec, 0); opt.opt_table = option_def; opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ @@ -263,6 +297,17 @@ parseCommandLine(int argc, pm_error("-color is not valid with -texfile"); if (cmdlineP->texFilespec && cmdlineP->maxvalSpec) pm_error("-maxval is not valid with -texfile"); + if (planesSpec && eyesepSpec) + pm_error("-planes is not valid with -eyesep"); + if (planesSpec && depthSpec) + pm_error("-planes is not valid with -depth"); + + if (planesSpec) { + float nearPlane, farPlane; + parseNearFarPlanes(nearFarPlanes, &nearPlane, &farPlane); + cmdlineP->eyesep = 2.0*farPlane/cmdlineP->dpi; + cmdlineP->depth = 2.0*(farPlane-nearPlane) / (2.0*farPlane-nearPlane); + } if (argc-1 < 1) cmdlineP->inputFilespec = "-"; @@ -907,7 +952,7 @@ smoothOutSpeckles(struct pam * const pamP, colors. -----------------------------------------------------------------------------*/ unsigned int i; - + for (i = 0; i < smoothing; ++i) { int col; tuple * const scratchrow = rowBuffer; @@ -1083,7 +1128,7 @@ makeImageRows(const struct pam * const inPamP, to have the same color as the one in column N */ int * sameFp; /* Fixed point of same[] */ - unsigned int * tuplesPerCol; + unsigned int * tuplesPerCol; /* Number of tuples averaged together in each column */ tuple * rowBuffer; /* Scratch row needed for texture manipulation */ int row; /* Current row in the input and output files */ @@ -1129,7 +1174,7 @@ makeImageRows(const struct pam * const inPamP, /* Write the resulting row. */ pnm_writepamrow(&outputGeneratorP->pam, outRow); } - + pnm_freepamrow(rowBuffer); free(tuplesPerCol); free(sameFp); @@ -1190,7 +1235,12 @@ produceStereogram(FILE * const ifP, static void reportParameters(struct cmdlineInfo const cmdline) { - unsigned int const pixelEyesep = round2int(cmdline.eyesep * cmdline.dpi); + unsigned int const pixelEyesep = + round2int(cmdline.eyesep * cmdline.dpi); + unsigned int const sep0 = + separation(0, cmdline.eyesep, cmdline.dpi, cmdline.depth); + unsigned int const sep1 = + separation(1, cmdline.eyesep, cmdline.dpi, cmdline.depth); pm_message("Eye separation: %.4g inch * %d DPI = %u pixels", cmdline.eyesep, cmdline.dpi, pixelEyesep); @@ -1198,12 +1248,12 @@ reportParameters(struct cmdlineInfo const cmdline) { if (cmdline.magnifypat > 1) pm_message("Background magnification: %uX * %uX", cmdline.magnifypat, cmdline.magnifypat); - pm_message("\"Optimal\" pattern width: %u / (%u * 2) = %u pixels", + pm_message("\"Optimal\" (far) pattern width: %u / (%u * 2) = %u pixels", pixelEyesep, cmdline.magnifypat, pixelEyesep/(cmdline.magnifypat * 2)); - pm_message("Unique 3-D depth levels possible: %u", - separation(0, cmdline.eyesep, cmdline.dpi, cmdline.depth) - - separation(1, cmdline.eyesep, cmdline.dpi, cmdline.depth) + 1); + pm_message("Near pattern width: %u / %u = %u pixels", + sep1, cmdline.magnifypat, sep1 / cmdline.magnifypat); + pm_message("Unique 3-D depth levels possible: %u", sep0 - sep1 + 1); if (cmdline.patFilespec && (cmdline.xshift || cmdline.yshift)) pm_message("Pattern shift: (%u, %u)", cmdline.xshift, cmdline.yshift); } @@ -1234,5 +1284,3 @@ main(int argc, const char *argv[]) { return 0; } - - diff --git a/version.mk b/version.mk index a4f04373..7beb00e8 100644 --- a/version.mk +++ b/version.mk @@ -1,4 +1,4 @@ NETPBM_MAJOR_RELEASE = 10 -NETPBM_MINOR_RELEASE = 58 -NETPBM_POINT_RELEASE = 3 +NETPBM_MINOR_RELEASE = 59 +NETPBM_POINT_RELEASE = 0 |