diff options
author | giraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8> | 2012-05-25 03:08:20 +0000 |
---|---|---|
committer | giraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8> | 2012-05-25 03:08:20 +0000 |
commit | 703ed293ca74a3e20430fdc9fb527c53ea866b85 (patch) | |
tree | a268ddd87aa390676e32deabc16d43c9c8b615c0 | |
parent | da956c85f18ea1251b95374fcad7bc99d4da0678 (diff) | |
download | netpbm-mirror-703ed293ca74a3e20430fdc9fb527c53ea866b85.tar.gz netpbm-mirror-703ed293ca74a3e20430fdc9fb527c53ea866b85.tar.xz netpbm-mirror-703ed293ca74a3e20430fdc9fb527c53ea866b85.zip |
Undo bogus commit
git-svn-id: http://svn.code.sf.net/p/netpbm/code/trunk@1688 9d0c8265-081b-0410-96cb-a4ca84ce46f8
-rw-r--r-- | converter/other/giftopnm.c | 1315 |
1 files changed, 475 insertions, 840 deletions
diff --git a/converter/other/giftopnm.c b/converter/other/giftopnm.c index e14fb961..faab1086 100644 --- a/converter/other/giftopnm.c +++ b/converter/other/giftopnm.c @@ -8,9 +8,8 @@ /* | provided "as is" without express or implied warranty. | */ /* +-------------------------------------------------------------------+ */ -/* 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 +/* There is a copy of the GIF89 specification, as defined by its + inventor, Compuserve, in 1989, at http://members.aol.com/royalef/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. @@ -19,6 +18,7 @@ describe the Lempel-Ziv base. */ +#define _BSD_SOURCE /* Make sure strcasecmp() is in string.h */ #include <string.h> #include <assert.h> @@ -48,22 +48,8 @@ #define LITTLE_ENDIAN 1 #endif -#ifndef FASTPBMRENDER - #define FASTPBMRENDER TRUE -#endif - -static const bool useFastPbmRender = FASTPBMRENDER; - -#ifndef REPORTLZWCODES - #define REPORTLZWCODES FALSE -#endif - -static const bool wantLzwCodes = REPORTLZWCODES; - /* In verbose output, include all the LZW codes */ - - -static bool +static __inline__ bool ReadOK(FILE * const fileP, unsigned char * const buffer, size_t const len) { @@ -103,19 +89,23 @@ 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 * inputFilespec; /* Filespecs of input files */ + const char * input_filespec; /* Filespecs of input files */ unsigned int verbose; /* -verbose option */ unsigned int comments; /* -comments option */ - bool allImages; /* He wants all the images */ - unsigned int imageNum; + bool all_images; /* He wants all the images */ + unsigned int image_no; /* image number he wants from input, starting at 0. Undefined - if allImages is TRUE + if all_images is TRUE */ - const char * alphaFileName; + const char * alpha_filename; unsigned int quitearly; unsigned int repair; }; @@ -153,7 +143,7 @@ parseCommandLine(int argc, char ** argv, &cmdlineP->repair, 0); OPTENT3(0, "image", OPT_STRING, &image, &imageSpec, 0); - OPTENT3(0, "alphaout", OPT_STRING, &cmdlineP->alphaFileName, + OPTENT3(0, "alphaout", OPT_STRING, &cmdlineP->alpha_filename, &alphaSpec, 0); opt.opt_table = option_def; @@ -163,169 +153,110 @@ 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->imageNum = 0; - cmdlineP->allImages = FALSE; + cmdlineP->image_no = 0; + cmdlineP->all_images = FALSE; } else { - if (strcaseeq(image, "all")) { - cmdlineP->allImages = TRUE; - } else { + if (strcasecmp(image, "all") == 0) + cmdlineP->all_images = TRUE; + else { char * tailptr; - long const imageNum = strtol(image, &tailptr, 10); + long const imageNo = 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 (imageNum < 0) + else if (imageNo < 0) pm_error("Invalid value for '-image' option. Must be " - "positive. You specified %ld", imageNum); - else if (imageNum == 0) + "positive. You specified %ld", imageNo); + else if (imageNo == 0) pm_error("Invalid value for 'image' option. You specified " "zero. The first image is 1."); - cmdlineP->allImages = FALSE; - cmdlineP->imageNum = (unsigned int) imageNum - 1; + cmdlineP->all_images = FALSE; + cmdlineP->image_no = (unsigned int) imageNo - 1; } } if (argc-1 == 0) - cmdlineP->inputFilespec = "-"; + cmdlineP->input_filespec = "-"; else if (argc-1 != 1) pm_error("Program takes zero or one argument (filename). You " "specified %d", argc-1); else - cmdlineP->inputFilespec = argv[1]; + cmdlineP->input_filespec = argv[1]; if (!alphaSpec) - cmdlineP->alphaFileName = NULL; + cmdlineP->alpha_filename = NULL; } - -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. ------------------------------------------------------------------------*/ - +typedef unsigned char gifColorMap[3][MAXCOLORMAPSIZE]; struct gifScreen { unsigned int Width; unsigned int Height; - GifColorMap ColorMap; + gifColorMap ColorMap; + unsigned int ColorMapSize; + /* Number of colors in the color map. */ 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. */ - bool hasGray; + int hasGray; /* Boolean: global colormap has at least one gray color (not counting black and white) */ - bool hasColor; + int hasColor; /* Boolean: global colormap has at least one non-gray, non-black, non-white color */ }; struct gif89 { - 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; + int transparent; + int delayTime; + int inputFlag; + int disposal; }; static void initGif89(struct gif89 * const gif89P) { - gif89P->haveTransColor = false; - gif89P->haveDelayTime = false; - gif89P->haveInputFlag = false; - gif89P->haveDisposal = false; + gif89P->transparent = -1; + gif89P->delayTime = -1; + gif89P->inputFlag = -1; + gif89P->disposal = -1; } -static bool verbose; -static bool showComment; +static int verbose; +int showComment; static void -readColorMap(FILE * const ifP, - unsigned int const cmapSize, - GifColorMap * const cmapP, - bool * const hasGrayP, - bool * const hasColorP) { +readColorMap(FILE *ifP, const int colormapsize, + unsigned char colormap[3][MAXCOLORMAPSIZE], + int *hasGrayP, int * const hasColorP) { int i; unsigned char rgb[3]; - assert(cmapSize <= MAXCOLORMAPSIZE); + assert(colormapsize <= MAXCOLORMAPSIZE); *hasGrayP = FALSE; /* initial assumption */ *hasColorP = FALSE; /* initial assumption */ - for (i = 0; i < cmapSize; ++i) { + for (i = 0; i < colormapsize; ++i) { if (! ReadOK(ifP, rgb, sizeof(rgb))) pm_error("Unable to read Color %d from colormap", i); - cmapP->map[i][CM_RED] = rgb[0] ; - cmapP->map[i][CM_GRN] = rgb[1] ; - cmapP->map[i][CM_BLU] = rgb[2] ; + colormap[CM_RED][i] = rgb[0] ; + colormap[CM_GRN][i] = rgb[1] ; + colormap[CM_BLU][i] = rgb[2] ; if (rgb[0] == rgb[1] && rgb[1] == rgb[2]) { if (rgb[0] != 0 && rgb[0] != GIFMAXVAL) @@ -333,7 +264,6 @@ readColorMap(FILE * const ifP, } else *hasColorP = TRUE; } - cmapP->size = cmapSize; } @@ -411,7 +341,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[256]; + unsigned char buf[260]; bool eod; eod = FALSE; /* initial value */ @@ -434,27 +364,6 @@ 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 @@ -464,7 +373,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[256]; + char buf[255+1]; unsigned int blocklen; bool done; @@ -489,22 +398,13 @@ 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; - unsigned char buf[256]; + static unsigned char buf[256]; const char * error; getDataBlock(ifP, buf, &eof, &length, &error); @@ -518,16 +418,11 @@ 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->haveDelayTime = true; - gif89P->delayTime = LM_to_uint(buf[1], buf[2]); - if ((buf[0] & 0x1) != 0) { - gif89P->haveTransColor = true; - gif89P->transparentIndex = buf[3]; - } + gif89P->delayTime = LM_to_uint(buf[1],buf[2]); + if ((buf[0] & 0x1) != 0) + gif89P->transparent = buf[3]; readThroughEod(ifP); } } @@ -535,16 +430,34 @@ doGraphicControlExtension(FILE * const ifP, static void -doExtension(FILE * const ifP, - unsigned char const label, - struct gif89 * const gif89P) { - +doExtension(FILE * const ifP, int const label, struct gif89 * const gif89P) { const char * str; switch (label) { case 0x01: /* Plain Text Extension */ str = "Plain Text"; - doPlainTextExtension(ifP); +#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 break; case 0xff: /* Application Extension */ str = "Application"; @@ -559,15 +472,16 @@ doExtension(FILE * const ifP, doGraphicControlExtension(ifP, gif89P); break; default: { - char buf[256]; - sprintf(buf, "UNKNOWN (0x%02x)", label); + static char buf[256]; str = buf; + sprintf(buf, "UNKNOWN (0x%02x)", label); 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 ); } @@ -747,9 +661,6 @@ 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; } @@ -758,11 +669,12 @@ getCode_get(struct getCodeState * const gsP, + struct stack { /* Stack grows from low addresses to high addresses */ - unsigned char * stack; /* malloc'ed array */ - unsigned char * sp; /* stack pointer */ - unsigned char * top; /* next word above top of stack */ + int * stack; /* malloc'ed array */ + int * sp; /* stack pointer */ + int * top; /* next word above top of stack */ }; @@ -780,7 +692,7 @@ initStack(struct stack * const stackP, unsigned int const size) { static void -pushStack(struct stack * const stackP, unsigned char const value) { +pushStack(struct stack * const stackP, int const value) { if (stackP->sp >= stackP->top) pm_error("stack overflow"); @@ -797,7 +709,7 @@ stackIsEmpty(const struct stack * const stackP) { -static unsigned char +static int popStack(struct stack * const stackP) { if (stackP->sp <= stackP->stack) @@ -855,43 +767,34 @@ termStack(struct stack * const stackP) { -----------------------------------------------------------------------------*/ -static int const maxLzwCodeCt = (1<<MAX_LZW_BITS); struct decompressor { struct stack stack; - bool fresh; + int fresh; /* The stream is right after a clear code or at the very beginning */ - unsigned int codeSize; + 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. */ - unsigned int maxCodeCt; + int maxnum_code; /* The maximum number of LZW codes that can be represented with the current code size. (1 << codeSize) */ - unsigned int nextTableSlot; + int next_tableSlot; /* Index in the code translation table of the next free entry */ unsigned int firstcode; /* This is always a true data element code */ - unsigned int prevcode; + 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; - 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 */ + int init_codeSize; + int max_dataVal; + int clear_code; + int end_code; }; @@ -899,35 +802,10 @@ struct decompressor { static void resetDecompressor(struct decompressor * const decompP) { - 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."); - } - } + decompP->codeSize = decompP->init_codeSize+1; + decompP->maxnum_code = 1 << decompP->codeSize; + decompP->next_tableSlot = decompP->max_dataVal + 3; + decompP->fresh = 1; } @@ -935,78 +813,46 @@ validateTransparentIndex(unsigned int const transparentIndex, static void lzwInit(struct decompressor * const decompP, FILE * const ifP, - int const initCodeSize, - unsigned int const cmapSize, - bool const haveTransColor, - unsigned int const transparentIndex, - bool const tolerateBadInput) { + int const init_codeSize) { - unsigned int const maxDataVal = (1 << initCodeSize) - 1; - if (verbose) pm_message("Image says the initial compression code size is " "%d bits", - initCodeSize); + init_codeSize); - decompP->ifP = ifP; - decompP->initCodeSize = initCodeSize; - decompP->cmapSize = cmapSize; - decompP->tolerateBadInput = tolerateBadInput; - decompP->maxDataVal = maxDataVal; - decompP->clearCode = maxDataVal + 1; - decompP->endCode = maxDataVal + 2; + 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; if (verbose) - pm_message("Initial code size is %u bits; clear code = 0x%03x, " - "end code = 0x%03x", - decompP->initCodeSize, - decompP->clearCode, decompP->endCode); + 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); /* The entries in the translation table for true data codes are - constant throughout the image. For PBM output we make an - adjustment later. Once set entries never change. + constant throughout the stream. We set them now and they never + change. */ { unsigned int i; - for (i = 0; i <= maxDataVal; ++i) { - decompP->table[i][0] = 0; - decompP->table[i][1] = i < cmapSize ? i : 0; + for (i = 0; i <= decompP->max_dataVal; ++i) { + decompP->table[0][i] = 0; + decompP->table[1][i] = i; } } - decompP->haveTransColor = haveTransColor; - decompP->transparentIndex = transparentIndex; - - if (haveTransColor) - validateTransparentIndex(transparentIndex, tolerateBadInput, - cmapSize, maxDataVal); - resetDecompressor(decompP); getCode_init(&getCodeState); decompP->fresh = TRUE; - 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; + initStack(&decompP->stack, maxnum_lzwCode * 2); } @@ -1020,32 +866,8 @@ 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, - unsigned int const incode, + int const incode, const char ** const errorP) { /*---------------------------------------------------------------------------- 'incode' is an LZW string code. It represents a string of true data @@ -1060,60 +882,64 @@ expandCodeOntoStack(struct decompressor * const decompP, from which it was built is invalid), fail (return text explanation as *errorP). -----------------------------------------------------------------------------*/ - 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); - } - else if (incode < decompP->nextTableSlot) - /* LZW string, defined */ + int code; + const char * error; + + error = NULL; /* Initial value */ + + if (incode < decompP->next_tableSlot) 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. + else { + /* It's a code that isn't in our translation table yet */ + pushStack(&decompP->stack, decompP->firstcode); + code = decompP->prevcode; + } + + { + /* 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. */ - 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 (!*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; - } + 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; } } - decompP->prevcode = incode; } + + *errorP = error; + + decompP->prevcode = incode; } @@ -1122,62 +948,39 @@ static void lzwReadByteFresh(struct getCodeState * const getCodeStateP, struct decompressor * const decompP, bool * const endOfImageP, - unsigned char * const dataReadP, + unsigned int * const dataReadP, const char ** const errorP) { -/*---------------------------------------------------------------------------- - 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. - - 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; + + /* 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. + */ bool eof; - - assert(decompP->fresh); /* Entry requirement */ - - decompP->fresh = FALSE; - do { getCode_get(getCodeStateP, decompP->ifP, decompP->codeSize, - &eof, &code, errorP); - } while (!*errorP && !eof && code == decompP->clearCode); + &eof, &decompP->firstcode, errorP); + decompP->prevcode = decompP->firstcode; + } while (decompP->firstcode == decompP->clear_code && !*errorP && !eof); if (!*errorP) { if (eof) *endOfImageP = TRUE; - else if (code == decompP->endCode) { + else if (decompP->firstcode == decompP->end_code) { if (!zeroDataBlock) readThroughEod(decompP->ifP); *endOfImageP = TRUE; - } 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; - + } else { *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 char * const dataReadP, + unsigned int * const dataReadP, bool * const endOfImageP, const char ** const errorP) { /*---------------------------------------------------------------------------- @@ -1201,6 +1004,8 @@ 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 { @@ -1213,12 +1018,11 @@ lzwReadByte(struct decompressor * const decompP, pm_asprintf(errorP, "Premature end of file; no proper GIF closing"); else { - if (code == decompP->clearCode) { + if (code == decompP->clear_code) { resetDecompressor(decompP); - lzwReadByteFresh(&getCodeState, decompP, endOfImageP, - dataReadP, errorP); + lzwReadByte(decompP, dataReadP, endOfImageP, errorP); } else { - if (code == decompP->endCode) { + if (code == decompP->end_code) { if (!zeroDataBlock) readThroughEod(decompP->ifP); *endOfImageP = TRUE; @@ -1239,8 +1043,8 @@ lzwReadByte(struct decompressor * const decompP, enum pass {MULT8PLUS0, MULT8PLUS4, MULT4PLUS2, MULT2PLUS1}; static void -bumpRowInterlace(unsigned int const rows, - unsigned int * const rowP, +bumpRowInterlace(unsigned int * const rowP, + unsigned int const rows, enum pass * const passP) { /*---------------------------------------------------------------------------- Move *pixelCursorP to the next row in the interlace pattern. @@ -1293,57 +1097,47 @@ bumpRowInterlace(unsigned int const rows, } -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); - } - - if (imageOutfile) { - if (useFastPbmRender && format == PBM_FORMAT && !haveTransColor) { - bit * const bitrow = cmapIndexRow; +struct pnmBuffer { + xel ** xels; + unsigned int col; + unsigned int row; +}; - 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); - } +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; } } @@ -1354,18 +1148,19 @@ 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 = pm_strdup(readError); + *errorP = strdup(readError); else { if (endOfImage) pm_asprintf(errorP, "Error in GIF image: Not enough raster data to fill " - "%u x %u dimensions. " - "The image has proper ending sequence, so " + "%u x %u dimensions. Ran out of raster data in " + "row %u. The image has proper ending sequence, so " "this is not just a truncated file.", - cols, rows); + cols, rows, failedRowNum); else *errorP = NULL; } @@ -1373,214 +1168,93 @@ 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 -makePnmRow(struct decompressor * const decompP, +readRaster(struct decompressor * const decompP, + xel ** const xels, unsigned int const cols, unsigned int const rows, - bool const fillWithZero, - unsigned char * const cmapIndexRow, - const char ** const errorP) { - - bool fillingWithZero; - unsigned int col; - - *errorP = NULL; /* initial value */ + 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; - for (col = 0, fillingWithZero = fillWithZero; - col < cols; - ++col) { + pass = MULT8PLUS0; + pnmBuffer.xels = xels; + pnmBuffer.col = 0; + pnmBuffer.row = 0; + fillingMissingPixels = false; /* initial value */ - unsigned char colorIndex; + while (pnmBuffer.row < rows) { + unsigned int colorIndex; - if (fillingWithZero) + if (fillingMissingPixels) colorIndex = 0; - else { - const char * readError; - unsigned char readColorIndex; - bool endOfImage; + else { + const char * error; - lzwReadByte(decompP, &readColorIndex, &endOfImage, &readError); + const char * readError; + unsigned int readColorIndex; + bool endOfImage; - assert(*errorP == NULL); + lzwReadByte(decompP, &readColorIndex, &endOfImage, &readError); - verifyPixelRead(endOfImage, readError, cols, rows, errorP); + verifyPixelRead(endOfImage, readError, cols, rows, pnmBuffer.row, + &error); if (readError) pm_strfree(readError); - 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; + 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); + colorIndex = 0; } else colorIndex = readColorIndex; } - cmapIndexRow[col] = colorIndex; + addPixelToRaster(colorIndex, &pnmBuffer, cols, rows, cmap, cmapSize, + interlace, transparentIndex, alphabits, &pass); } } 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); -} - - - -static void skipExtraneousData(struct decompressor * const decompP) { - unsigned char byteRead; + unsigned int 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); } } } @@ -1588,66 +1262,22 @@ 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 const cmap, + gifColorMap cmap, + unsigned int const cmapSize, bool const interlace, - bool const haveTransColor, - unsigned int const transparentIndex, - FILE * const imageOutFileP, - FILE * const alphaFileP, - bool const hasGray, - bool const hasColor, + int const transparentIndex, + bit ** const alphabits, bool const tolerateBadInput) { unsigned char lzwMinCodeSize; struct decompressor decomp; bool gotMinCodeSize; - gotMinCodeSize = ReadOK(ifP, &lzwMinCodeSize, 1); + gotMinCodeSize = ReadOK(ifP, &lzwMinCodeSize, 1); if (!gotMinCodeSize) pm_error("GIF stream ends (or read error) " "right after an image separator; no " @@ -1658,19 +1288,10 @@ readImageData(FILE * const ifP, "Maximum allowable code size in GIF is %u", lzwMinCodeSize, MAX_LZW_BITS); - lzwInit(&decomp, ifP, lzwMinCodeSize, cmap.size, - haveTransColor, transparentIndex, tolerateBadInput); + lzwInit(&decomp, ifP, lzwMinCodeSize); - 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); + readRaster(&decomp, xels, cols, rows, cmap, cmapSize, interlace, + transparentIndex, alphabits, tolerateBadInput); skipExtraneousData(&decomp); @@ -1680,21 +1301,80 @@ readImageData(FILE * const ifP, static void -readGifHeader(FILE * const gifFileP, - struct gifScreen * const gifScreenP) { +writePnm(FILE * const outfileP, + xel ** const xels, + int const cols, + int const rows, + int const hasGray, + int const hasColor) { /*---------------------------------------------------------------------------- - Read the GIF stream header off the file *gifFileP, which is present + 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) { +/*---------------------------------------------------------------------------- + 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 positioned to the beginning of a GIF stream. Return the info from it as *gifScreenP. -----------------------------------------------------------------------------*/ - unsigned char buf[16]; - char version[4]; - unsigned int cmapSize; + unsigned char buf[16]; + char version[4]; + - if (!ReadOK(gifFileP, buf, 6)) + if (! ReadOK(gifFile,buf,6)) pm_error("error reading magic number" ); - if (!strneq((char *)buf, "GIF", 3)) + if (strncmp((char *)buf,"GIF",3) != 0) pm_error("File does not contain a GIF stream. It does not start " "with 'GIF'."); @@ -1707,12 +1387,12 @@ readGifHeader(FILE * const gifFileP, if ((!streq(version, "87a")) && (!streq(version, "89a"))) pm_error("bad version number, not '87a' or '89a'" ); - if (!ReadOK(gifFileP, buf, 7)) + if (! ReadOK(gifFile,buf,7)) pm_error("failed to read screen descriptor" ); gifScreenP->Width = LM_to_uint(buf[0],buf[1]); gifScreenP->Height = LM_to_uint(buf[2],buf[3]); - cmapSize = 1 << ((buf[4] & 0x07) + 1); + gifScreenP->ColorMapSize = 1 << ((buf[4] & 0x07) + 1); gifScreenP->ColorResolution = (buf[4] & 0x70 >> 3) + 1; gifScreenP->Background = buf[5]; gifScreenP->AspectRatio = buf[6]; @@ -1725,10 +1405,10 @@ readGifHeader(FILE * const gifFileP, gifScreenP->AspectRatio == 0 ? 1 : (gifScreenP->AspectRatio + 15) / 64.0); pm_message("Colors = %d Color Resolution = %d", - cmapSize, gifScreenP->ColorResolution); + gifScreenP->ColorMapSize, gifScreenP->ColorResolution); } if (BitSet(buf[4], LOCALCOLORMAP)) { /* Global Colormap */ - readColorMap(gifFileP, cmapSize, &gifScreenP->ColorMap, + readColorMap(gifFile, gifScreenP->ColorMapSize, gifScreenP->ColorMap, &gifScreenP->hasGray, &gifScreenP->hasColor); if (verbose) { pm_message("Color map %s grays, %s colors", @@ -1807,8 +1487,7 @@ readExtensions(FILE* const ifP, } else if (c == ',') imageStart = TRUE; else - pm_message("Encountered invalid character 0x%02x while " - "seeking extension block, ignoring", (int)c); + pm_message("bogus character 0x%02x, ignoring", (int)c); } } *eodP = eod; @@ -1816,94 +1495,20 @@ 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 -reportImageHeader(struct GifImageHeader const imageHeader) { +reportImageInfo(unsigned int const cols, + unsigned int const rows, + bool const useGlobalColormap, + unsigned int const localColorMapSize, + bool const interlaced) { pm_message("reading %u by %u%s GIF image", - imageHeader.cols, imageHeader.rows, - imageHeader.interlaced ? " interlaced" : "" ); + cols, rows, interlaced ? " interlaced" : "" ); - if (imageHeader.lpos > 0 || imageHeader.tpos > 0) - pm_message(" Image left position: %u top position: %u", - imageHeader.lpos, imageHeader.tpos); - - if (imageHeader.useGlobalColormap) + if (useGlobalColormap) pm_message(" Uses global colormap"); else - pm_message(" Uses local colormap of %u colors", - imageHeader.localColorMapSize); -} - - - -static void -readImageHeader(FILE * const ifP, - struct GifImageHeader * const imageHeaderP) { - - unsigned char buf[16]; - - if (!ReadOK(ifP, buf, 9)) - pm_error("couldn't read left/top/width/height"); - - 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; - - if (!ReadOK(ifP, &lzwMinCodeSize, 1)) - pm_message("EOF or error while skipping image DataBlock" ); - - readThroughEod(ifP); + pm_message(" Uses local colormap of %u colors", localColorMapSize); } @@ -1911,47 +1516,85 @@ skipImageData(FILE * const ifP) { static void convertImage(FILE * const ifP, bool const skipIt, - FILE * const imageoutFileP, - FILE * const alphafileP, - struct gifScreen const gifScreen, + FILE * const imageout_file, + FILE * const alphafile, + struct gifScreen 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 *imageoutFileP and *alphafileP. - If *alphafileP is NULL, though, don't write any alpha information. + image to the current position of files 'imageout_file' and 'alphafile'. + If 'alphafile' is NULL, though, don't write any alpha information. -----------------------------------------------------------------------------*/ - struct GifImageHeader imageHeader; - GifColorMap localColorMap; - const GifColorMap * currentColorMapP; - bool hasGray, hasColor; + 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; - readImageHeader(ifP, &imageHeader); + if (! ReadOK(ifP,buf,9)) + pm_error("couldn't read left/top/width/height"); - validateWithinGlobalScreen(imageHeader, gifScreen); + 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); - if (imageHeader.useGlobalColormap) { - currentColorMapP = &gifScreen.ColorMap; - hasGray = gifScreen.hasGray; - hasColor = gifScreen.hasColor; - } else { - readColorMap(ifP, imageHeader.localColorMapSize, &localColorMap, + 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); - currentColorMapP = &localColorMap; + 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); + } + } 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); + } } - if (!skipIt) { - readImageData(ifP, imageHeader.cols, imageHeader.rows, - *currentColorMapP, - imageHeader.interlaced, - gif89.haveTransColor, gif89.transparentIndex, - imageoutFileP, alphafileP, - hasGray, hasColor, - tolerateBadInput); - } else - skipImageData(ifP); + if (!skipIt && alphafile && alphabits) + pbm_writepbm(alphafile, alphabits, cols, rows, FALSE); + + pnm_freearray(xels, rows); + if (alphabits) + pbm_freearray(alphabits, rows); } @@ -1977,17 +1620,17 @@ disposeOfReadExtensionsError(const char * const error, static void -convertImages(FILE * const ifP, - bool const allImages, - unsigned int const requestedImageSeq, - bool const drainStream, - FILE * const imageOutFileP, - FILE * const alphaFileP, - bool const tolerateBadInput) { +convertImages(FILE * const ifP, + bool const allImages, + int const requestedImageSeq, + bool const drainStream, + FILE * const imageout_file, + FILE * const alphafile, + bool const tolerateBadInput) { /*---------------------------------------------------------------------------- Read a GIF stream from file 'ifP' and write one or more images from - it as PNM images to file 'imageOutFileP'. If the images have transparency - and 'alphafile' is non-NULL, write PGM alpha masks to file 'alphaFileP'. + 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'. 'allImages' means Caller wants all the images in the stream. @@ -2001,7 +1644,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. -----------------------------------------------------------------------------*/ - unsigned int imageSeq; + int imageSeq; /* Sequence within GIF stream of image we are currently processing. First is 0. */ @@ -2010,15 +1653,12 @@ 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 && (allImages || imageSeq <= requestedImageSeq || drainStream); + !eod && (imageSeq <= requestedImageSeq || allImages || drainStream); ++imageSeq) { const char * error; @@ -2036,10 +1676,9 @@ convertImages(FILE * const ifP, imageSeq, imageSeq > 1 ? "s" : ""); } else { if (verbose) - pm_message("Reading Image Sequence %u", imageSeq); - + pm_message("Reading Image Sequence %d", imageSeq); convertImage(ifP, !allImages && (imageSeq != requestedImageSeq), - imageOutFileP, alphaFileP, gifScreen, gif89, + imageout_file, alphafile, gifScreen, gif89, tolerateBadInput); } } @@ -2051,9 +1690,8 @@ int main(int argc, char **argv) { struct cmdlineInfo cmdline; - FILE * ifP; - FILE * alphaFileP; - FILE * imageOutFileP; + FILE *ifP; + FILE *alpha_file, *imageout_file; pnm_init(&argc, argv); @@ -2061,30 +1699,27 @@ main(int argc, char **argv) { verbose = cmdline.verbose; showComment = cmdline.comments; - ifP = pm_openr(cmdline.inputFilespec); + ifP = pm_openr(cmdline.input_filespec); - if (cmdline.alphaFileName == NULL) - alphaFileP = NULL; + if (cmdline.alpha_filename == NULL) + alpha_file = NULL; else - alphaFileP = pm_openw(cmdline.alphaFileName); + alpha_file = pm_openw(cmdline.alpha_filename); - if (alphaFileP && streq(cmdline.alphaFileName, "-")) - imageOutFileP = NULL; + if (alpha_file && streq(cmdline.alpha_filename, "-")) + imageout_file = NULL; else - imageOutFileP = stdout; + imageout_file = stdout; - convertImages(ifP, cmdline.allImages, cmdline.imageNum, - !cmdline.quitearly, imageOutFileP, alphaFileP, + convertImages(ifP, cmdline.all_images, cmdline.image_no, + !cmdline.quitearly, imageout_file, alpha_file, cmdline.repair); pm_close(ifP); - if (imageOutFileP != NULL) - pm_close(imageOutFileP); - if (alphaFileP != NULL) - pm_close(alphaFileP); + if (imageout_file != NULL) + pm_close(imageout_file); + if (alpha_file != NULL) + pm_close(alpha_file); return 0; } - - - |