From 31fc573df625b06a5f7b8b8e769e9f35cbfdaf91 Mon Sep 17 00:00:00 2001 From: giraffedata Date: Sun, 31 Dec 2006 08:08:29 +0000 Subject: Release 10.37.0 git-svn-id: http://svn.code.sf.net/p/netpbm/code/advanced@187 9d0c8265-081b-0410-96cb-a4ca84ce46f8 --- converter/other/Makefile | 2 +- converter/other/giftopnm.c | 4 +- converter/other/hdifftopam.c | 2 +- converter/other/jpeg2000/libjasper/Makefile.common | 2 +- converter/other/pamtogif.c | 1864 ++++++++++++++++++++ converter/other/pamtopnm.c | 59 +- converter/other/pamtosvg/curve.h | 8 +- converter/other/pamtosvg/fit.c | 1391 +++++++-------- converter/other/pgmtopbm.c | 5 +- converter/other/pgmtoppm.c | 343 ++-- converter/other/pngtopnm.c | 22 - converter/other/pnmtoddif.c | 4 - converter/other/pnmtojpeg.c | 880 +++++---- converter/other/pnmtopalm/Makefile | 3 +- converter/other/pnmtopng.c | 16 +- converter/pbm/Makefile | 4 +- converter/pbm/cmuwmtopbm.c | 181 +- converter/pbm/mgrtopbm.c | 171 +- converter/pbm/pbmtocmuwm.c | 167 +- converter/pbm/pbmtogem.c | 6 - converter/pbm/pbmtogo.c | 10 +- converter/pbm/pbmtoicon.c | 252 +-- converter/pbm/pbmtomgr.c | 192 +- converter/pbm/pbmtopi3.c | 6 - converter/pbm/pbmtoptx.c | 6 - converter/pbm/pbmtox10bm | 45 + converter/pbm/pbmtox10bm.c | 120 -- converter/pbm/pbmtoxbm.c | 394 ++++- converter/pbm/pbmtoybm.c | 6 - converter/pbm/xbmtopbm.c | 567 +++--- converter/pgm/pgmtofs.c | 6 - converter/pgm/pgmtolispm.c | 6 - converter/ppm/ppmtogif.c | 1544 ++-------------- converter/ppm/ppmtoilbm.c | 42 - converter/ppm/ppmtomitsu.c | 844 +++++---- converter/ppm/ppmtomitsu.test | 12 + converter/ppm/ppmtompeg/Makefile | 10 +- converter/ppm/ppmtompeg/bframe.c | 1 - converter/ppm/ppmtompeg/bsearch.c | 1 + converter/ppm/ppmtompeg/frames.c | 58 + converter/ppm/ppmtompeg/headers/bitio.h | 4 +- converter/ppm/ppmtompeg/headers/block.h | 44 + converter/ppm/ppmtompeg/headers/frame.h | 16 +- converter/ppm/ppmtompeg/headers/frames.h | 168 +- converter/ppm/ppmtompeg/headers/iframe.h | 118 ++ converter/ppm/ppmtompeg/headers/motion_search.h | 3 + converter/ppm/ppmtompeg/headers/mpeg.h | 5 +- converter/ppm/ppmtompeg/headers/mproto.h | 8 - converter/ppm/ppmtompeg/headers/parallel.h | 2 +- converter/ppm/ppmtompeg/headers/prototypes.h | 9 +- converter/ppm/ppmtompeg/headers/subsample.h | 39 + converter/ppm/ppmtompeg/iframe.c | 209 +-- converter/ppm/ppmtompeg/jpeg.c | 1 + converter/ppm/ppmtompeg/mheaders.c | 712 ++++---- converter/ppm/ppmtompeg/mpeg.c | 116 +- converter/ppm/ppmtompeg/pframe.c | 2 - converter/ppm/ppmtompeg/psearch.c | 2 + converter/ppm/ppmtompeg/subsample.c | 1 + converter/ppm/ppmtopict.c | 7 - converter/ppm/sldtoppm.c | 726 ++++---- converter/ppm/spctoppm.c | 7 - 61 files changed, 6285 insertions(+), 5170 deletions(-) create mode 100644 converter/other/pamtogif.c create mode 100644 converter/pbm/pbmtox10bm delete mode 100644 converter/pbm/pbmtox10bm.c create mode 100644 converter/ppm/ppmtomitsu.test create mode 100644 converter/ppm/ppmtompeg/frames.c create mode 100644 converter/ppm/ppmtompeg/headers/iframe.h create mode 100644 converter/ppm/ppmtompeg/headers/subsample.h (limited to 'converter') diff --git a/converter/other/Makefile b/converter/other/Makefile index 6cde424d..0273c523 100644 --- a/converter/other/Makefile +++ b/converter/other/Makefile @@ -79,7 +79,7 @@ endif PORTBINARIES = bmptopnm fitstopnm \ gemtopnm giftopnm hdifftopam infotopam \ - pamtodjvurle pamtofits \ + pamtodjvurle pamtofits pamtogif \ pamtohdiff pamtohtmltbl pamtopfm pamtopnm pamtouil \ pamtoxvmini \ pbmtopgm pfmtopam \ diff --git a/converter/other/giftopnm.c b/converter/other/giftopnm.c index d3d02fde..d7d54775 100644 --- a/converter/other/giftopnm.c +++ b/converter/other/giftopnm.c @@ -652,7 +652,7 @@ termStack(struct stack * const stackP) { max_dataVal. The first byte in the stream tells you what dataWidth is. - LZW codes 0 - max_dataVal are direct codes. Each on represents + LZW codes 0 - max_dataVal are direct codes. Each one represents the true data element whose value is that of the LZW code itself. No decompression is required. @@ -805,7 +805,7 @@ expandCodeOntoStack(struct decompressor * const decompP, 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 stringCount; stringCount = 0; diff --git a/converter/other/hdifftopam.c b/converter/other/hdifftopam.c index 444d6068..0f1a5ca3 100644 --- a/converter/other/hdifftopam.c +++ b/converter/other/hdifftopam.c @@ -96,7 +96,7 @@ main(int argc, char *argv[]) { tuple * outrow; tuple * prevrow; - pnm_init( &argc, argv ); + pnm_init(&argc, argv); parseCommandLine(argc, argv, &cmdline); diff --git a/converter/other/jpeg2000/libjasper/Makefile.common b/converter/other/jpeg2000/libjasper/Makefile.common index 7737f2c0..d2cb7d42 100644 --- a/converter/other/jpeg2000/libjasper/Makefile.common +++ b/converter/other/jpeg2000/libjasper/Makefile.common @@ -26,7 +26,7 @@ INCLUDES = -I$(JASPERSRCDIR)/include -Iimportinc DEFS = -DHAVE_LIBM=1 -DSTDC_HEADERS=1 -DHAVE_FCNTL_H=1 -DHAVE_LIMITS_H=1 -DHAVE_UNISTD_H=1 -DHAVE_SYS_TYPES_H=1 -DHAVE_STDLIB_H=1 -DHAVE_STDDEF_H=1 -DEXCLUDE_BMP_SUPPORT -DEXCLUDE_RAS_SUPPORT -DEXCLUDE_MIF_SUPPORT -DEXCLUDE_JPG_SUPPORT -DEXCLUDE_PGX_SUPPORT -DEXCLUDE_PNM_SUPPORT $(LIB_OBJECTS):%.o:%.c - $(CC) -c $(CFLAGS) $(INCLUDES) $(DEFS) $(CADD) $< + $(CC) -c $(CPPFLAGS) $(CFLAGS) $(INCLUDES) $(DEFS) $(CADD) $< $(LIB_OBJECTS): importinc diff --git a/converter/other/pamtogif.c b/converter/other/pamtogif.c new file mode 100644 index 00000000..ef3441cb --- /dev/null +++ b/converter/other/pamtogif.c @@ -0,0 +1,1864 @@ +/*============================================================================= + pamtogif +=============================================================================== + Convert a Netpbm image to GIF + + History and copyright information is at the end of the file. +=============================================================================*/ + +#include +#include + +#include "mallocvar.h" +#include "shhopt.h" +#include "nstring.h" +#include "pam.h" +#include "pammap.h" + +#define MAXCMAPSIZE 256 + +static unsigned int const gifMaxval = 255; + +static bool verbose; + +typedef int stringCode; + /* A code to be place in the GIF raster. It represents + a string of one or more pixels. You interpret this in the context + of a current code size. The lower half of the values representable + in the current code size represent singleton strings and the value + is simply the value of the one pixel in the string. The first two + values in the upper half of the range are the clear code and EOF + code, respectively. The rest of the values represent multi-pixel + strings. The mapping between value and the sequence of pixels + changes throughout the image. + + A variable of this type sometimes has the value -1 instead of + a string code due to cheesy programming. + + Ergo, this data structure must be signed and at least BITS bits + wide plus sign bit. + + TODO: Some variables that should be of this type are defined as + "long". + */ + + +struct cmap { + /* This is the information for the GIF colormap (aka palette). */ + + struct pam pam; + /* Gives depth and maxval for colors in color[] */ + tuple color[MAXCMAPSIZE]; + /* Maps a color index, as is found in the raster part of the + GIF, to color. + */ + int perm[MAXCMAPSIZE], permi[MAXCMAPSIZE]; + /* perm[i] is the position in the sorted colormap of the color which + is at position i in the unsorted colormap. permi[] is the inverse + function of perm[]. + + The sort colormap is the one that goes into the GIF. The + unsorted one is the one that is hashed so we can find colors + in it. + */ + unsigned int cmapSize; + /* Number of entries in the GIF colormap. I.e. number of colors + in the image, plus possibly one fake transparency color. + */ + bool haveTransparent; + /* The colormap contains an entry for transparent pixels */ + unsigned int transparent; + /* color index number in GIF palette of the color that is to + be transparent. This is a post-sort index. + + Meaningful only if 'haveTransparent' is true. + */ + tuplehash tuplehash; + /* A hash table to translate color to GIF (presort) colormap index. */ +}; + +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; /* Filespec of input file */ + const char *alphacolor; /* -alphacolor option value or default */ + unsigned int interlace; /* -interlace option value */ + unsigned int sort; /* -sort option value */ + const char *mapfile; /* -mapfile option value. NULL if none. */ + const char *transparent; /* -transparent option value. NULL if none. */ + const char *comment; /* -comment option value; NULL if none */ + unsigned int nolzw; /* -nolzw option */ + unsigned int verbose; +}; + + +static unsigned int +nSignificantBits(unsigned int const arg) { + + unsigned int i; + + i = 0; + + while (arg >> i != 0) + ++i; + + return i; +} + + + +static unsigned int +pamAlphaPlane(struct pam * const pamP) { + + unsigned int alphaPlane; + + if (STREQ(pamP->tuple_type, "RGB_ALPHA")) + alphaPlane = 3; + else if (STREQ(pamP->tuple_type, "GRAYSCALE_ALPHA")) + alphaPlane = 2; + else if (STREQ(pamP->tuple_type, "BLACKANDWHITE_ALPHA")) + alphaPlane = 2; + else + alphaPlane = 0; + + if (alphaPlane >= pamP->depth) + pm_error("Tuple type is '%s', but depth (%u) is less than %u", + pamP->tuple_type, pamP->depth, alphaPlane + 1); + + return alphaPlane; +} + + + +static void +parseCommandLine(int argc, char ** argv, + struct cmdlineInfo * const cmdlineP) { +/*---------------------------------------------------------------------------- + Parse the program arguments (given by argc and argv) into a form + the program can deal with more easily -- a cmdline_info structure. + If the syntax is invalid, issue a message and exit the program via + pm_error(). + + Note that the file spec array we return is stored in the storage that + was passed to us as the argv array. +-----------------------------------------------------------------------------*/ + optEntry * option_def; /* malloc'ed */ + optStruct3 opt; /* set by OPTENT3 */ + unsigned int option_def_index; + + MALLOCARRAY_NOFAIL(option_def, 100); + + option_def_index = 0; /* incremented by OPTENT3 */ + OPTENT3(0, "interlace", OPT_FLAG, + NULL, &cmdlineP->interlace, 0); + OPTENT3(0, "sort", OPT_FLAG, + NULL, &cmdlineP->sort, 0); + OPTENT3(0, "nolzw", OPT_FLAG, + NULL, &cmdlineP->nolzw, 0); + OPTENT3(0, "mapfile", OPT_STRING, + &cmdlineP->mapfile, NULL, 0); + OPTENT3(0, "transparent", OPT_STRING, + &cmdlineP->transparent, NULL, 0); + OPTENT3(0, "comment", OPT_STRING, + &cmdlineP->comment, NULL, 0); + OPTENT3(0, "alphacolor", OPT_STRING, + &cmdlineP->alphacolor, NULL, 0); + OPTENT3(0, "verbose", OPT_FLAG, + NULL, &cmdlineP->verbose, 0); + + /* Set the defaults */ + cmdlineP->mapfile = NULL; + cmdlineP->transparent = NULL; /* no transparency */ + cmdlineP->comment = NULL; /* no comment */ + cmdlineP->alphacolor = "rgb:0/0/0"; + /* We could say "black" here, but then we depend on the color names + database existing. + */ + + opt.opt_table = option_def; + opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ + opt.allowNegNum = FALSE; /* We have no parms that are negative numbers */ + + optParseOptions3(&argc, argv, opt, sizeof(opt), 0); + /* Uses and sets argc, argv, and some of *cmdlineP and others. */ + + if (argc-1 == 0) + cmdlineP->input_filespec = "-"; + 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]; +} + + + +/* + * Write out a word to the GIF file + */ +static void +Putword(int const w, FILE * const fp) { + + fputc( w & 0xff, fp ); + fputc( (w / 256) & 0xff, fp ); +} + + + +static int +closestColor(tuple const color, + struct pam * const pamP, + struct cmap * const cmapP) { +/*---------------------------------------------------------------------------- + Return the pre-sort colormap index of the color in the colormap *cmapP + that is closest to the color 'color', whose format is specified by + *pamP. + + Also add 'color' to the colormap hash, with the colormap index we + are returning. Caller must ensure that the color is not already in + there. +-----------------------------------------------------------------------------*/ + unsigned int const nComp = pamP->depth >= 3 ? 3 : 1; + /* Number of color components (not alpha) in 'color' */ + + unsigned int i; + unsigned int imin, dmin; + bool fits; + + dmin = UINT_MAX; + imin = 0; + for (i = 0; i < cmapP->cmapSize; ++i) { + unsigned int distance; + unsigned int plane; + + for (distance = 0, plane = 0; plane < nComp; ++plane) + /* Divide by 4 is to avoid arithmetic overflow */ + distance += SQR(color[plane] - cmapP->color[i][plane]) / 4; + + if (distance < dmin) { + dmin = distance; + imin = i; + } + } + pnm_addtotuplehash(pamP, cmapP->tuplehash, color, imin, &fits); + + return imin; +} + + + +enum pass {MULT8PLUS0, MULT8PLUS4, MULT4PLUS2, MULT2PLUS1}; + + +typedef struct { + struct pam pam; + /* Description of input file/image. The position of the file + is also part of the state of this rowReader. + */ + pm_filepos rasterPos; + /* Position in file fileP of the start of the raster */ + bool interlace; + /* We're accessing the image in interlace fashion */ + bool eof; + /* The image is at EOF (we have returned all of the rows) */ + unsigned int nextRow; + /* Number of row to which input file is positioned; + meaningless if 'eof'. + */ + enum pass pass; + /* The interlace pass. Undefined if !interlace */ + tuple * discardBuffer; + /* A bitbucket for rows we read in order to advance the file + position. + */ +} rowReader; + + + +static rowReader * +rowReader_create(struct pam * const pamP, + pm_filepos const rasterPos, + bool const interlace) { + + rowReader * rdrP; + + MALLOCVAR_NOFAIL(rdrP); + + rdrP->pam = *pamP; + rdrP->rasterPos = rasterPos; + rdrP->interlace = interlace; + rdrP->eof = FALSE; + rdrP->pass = MULT8PLUS0; + + pm_seek2(rdrP->pam.file, &rasterPos, sizeof(rasterPos)); + rdrP->nextRow = 0; + + rdrP->discardBuffer = pnm_allocpamrow(&rdrP->pam); + + return rdrP; +} + + + +static void +rowReader_destroy(rowReader * const rdrP) { + + pnm_freepamrow(rdrP->discardBuffer); + free(rdrP); +} + + + +static void +rowReaderSkipRows(rowReader * const rdrP, + unsigned int const rowCount, + bool * const eofP) { +/*---------------------------------------------------------------------------- + Skip over the next 'rowCount' rows of the input file. + + Iff there aren't at least 'rowCount' rows left, return *eofP == TRUE. +-----------------------------------------------------------------------------*/ + if (rdrP->nextRow + rowCount >= rdrP->pam.height) + *eofP = TRUE; + else { + /* This could be made faster if need be by adding a libnetpbm + row skip function. Except with the plain formats, that could + just compute the next row position and fseek() to it. + pnm_readpamrow() with NULL for the output pointer would be a + good interface for a row skip function. + */ + unsigned int i; + + *eofP = FALSE; + + for (i = 0; i < rowCount; ++i) + pnm_readpamrow(&rdrP->pam, rdrP->discardBuffer); + + rdrP->nextRow += rowCount; + } +} + + + +static void +rowReaderGotoNextInterlaceRow(rowReader * const rdrP) { +/*---------------------------------------------------------------------------- + Position reader to the next row in the interlace pattern, assuming it + is now positioned immediately after the current row. +-----------------------------------------------------------------------------*/ + bool endOfPass; + + /* There are 4 passes: + MULT8PLUS0: Rows 0, 8, 16, 24, 32, etc. + MULT8PLUS4: Rows 4, 12, 20, 28, etc. + MULT4PLUS2: Rows 2, 6, 10, 14, etc. + MULT2PLUS1: Rows 1, 3, 5, 7, 9, etc. + */ + + switch (rdrP->pass) { + case MULT8PLUS0: + rowReaderSkipRows(rdrP, 7, &endOfPass); + break; + case MULT8PLUS4: + rowReaderSkipRows(rdrP, 7, &endOfPass); + break; + case MULT4PLUS2: + rowReaderSkipRows(rdrP, 3, &endOfPass); + break; + case MULT2PLUS1: + rowReaderSkipRows(rdrP, 1, &endOfPass); + break; + } + + /* Note that if there are more than 4 rows, the sequence of passes + is sequential, but when there are fewer than 4, reading may skip + e.g. from MULT8PLUS0 to MULT4PLUS2. + */ + while (endOfPass && !rdrP->eof) { + pm_seek2(rdrP->pam.file, &rdrP->rasterPos, sizeof(rdrP->rasterPos)); + rdrP->nextRow = 0; + + switch (rdrP->pass) { + case MULT8PLUS0: + rdrP->pass = MULT8PLUS4; + rowReaderSkipRows(rdrP, 4, &endOfPass); + break; + case MULT8PLUS4: + rdrP->pass = MULT4PLUS2; + rowReaderSkipRows(rdrP, 2, &endOfPass); + break; + case MULT4PLUS2: + rdrP->pass = MULT2PLUS1; + rowReaderSkipRows(rdrP, 1, &endOfPass); + break; + case MULT2PLUS1: + rdrP->eof = TRUE; + break; + } + } +} + + + + +static void +rowReaderGotoNextStraightRow(rowReader * const rdrP) { +/*---------------------------------------------------------------------------- + Position reader to the next row in a straight, non-interlace + pattern, assuming the file is now positioned immediately after the + current row. + + This is trivial, since the next row _is_ immediately after the current + row, except in the case that there are no more rows. +-----------------------------------------------------------------------------*/ + if (rdrP->nextRow >= rdrP->pam.height) + rdrP->eof = TRUE; +} + + + +static void +rowReader_read(rowReader * const rdrP, + tuple * const tuplerow) { + + if (rdrP->eof) + pm_error("INTERNAL ERROR: rowReader attempted to read beyond end " + "of image"); + + pnm_readpamrow(&rdrP->pam, tuplerow); + ++rdrP->nextRow; + + if (rdrP->interlace) + rowReaderGotoNextInterlaceRow(rdrP); + else + rowReaderGotoNextStraightRow(rdrP); +} + + + +static unsigned int +gifPixel(struct pam * const pamP, + tuple const tuple, + unsigned int const alphaPlane, + sample const alphaThreshold, + struct cmap * const cmapP) { +/*---------------------------------------------------------------------------- + Return as *colorIndexP the colormap index of the tuple 'tuple', + whose format is described by *pamP, using colormap *cmapP. + + 'alphaThreshold' is the alpha level below which we consider a + pixel transparent for GIF purposes. +-----------------------------------------------------------------------------*/ + unsigned int colorIndex; + + if (alphaPlane && tuple[alphaPlane] < alphaThreshold && + cmapP->haveTransparent) + colorIndex = cmapP->transparent; + else { + int presortColorindex; + int found; + + pnm_lookuptuple(pamP, cmapP->tuplehash, tuple, + &found, &presortColorindex); + + if (!found) + presortColorindex = closestColor(tuple, pamP, cmapP); + + colorIndex = cmapP->perm[presortColorindex]; + } + return colorIndex; +} + + + +static void +writeTransparentColorIndexExtension(FILE * const ofP, + unsigned int const transColorIndex) { +/*---------------------------------------------------------------------------- + Write out extension for transparent color index. +-----------------------------------------------------------------------------*/ + fputc('!', ofP); + fputc(0xf9, ofP); + fputc(4, ofP); + fputc(1, ofP); + fputc(0, ofP); + fputc(0, ofP); + fputc(transColorIndex, ofP); + fputc(0, ofP); +} + + + +static void +writeCommentExtension(FILE * const ofP, + char const comment[]) { +/*---------------------------------------------------------------------------- + Write out extension for a comment +-----------------------------------------------------------------------------*/ + unsigned int const maxSegmentSize = 255; + + const char * segment; + + fputc('!', ofP); /* Identifies an extension */ + fputc(0xfe, ofP); /* Identifies a comment */ + + /* Write it out in segments no longer than 255 characters */ + for (segment = &comment[0]; + segment < comment + strlen(comment); + segment += maxSegmentSize) { + + unsigned int const lengthThisSegment = + MIN(maxSegmentSize, strlen(segment)); + + fputc(lengthThisSegment, ofP); + + fwrite(segment, 1, lengthThisSegment, ofP); + } + + fputc(0, ofP); /* No more comment blocks in this extension */ +} + + + +/*************************************************************************** + * + * GIFCOMPR.C - GIF Image compression routines + * + * Lempel-Ziv compression based on 'compress'. GIF modifications by + * David Rowley (mgardi@watdcsu.waterloo.edu) + * + ***************************************************************************/ + +/* + * General DEFINEs + */ + +#define BITS 12 + +#define HSIZE 5003 /* 80% occupancy */ + +/* + * + * GIF Image compression - modified 'compress' + * + * Based on: compress.c - File compression ala IEEE Computer, June 1984. + * + * By Authors: Spencer W. Thomas (decvax!harpo!utah-cs!utah-gr!thomas) + * Jim McKie (decvax!mcvax!jim) + * Steve Davies (decvax!vax135!petsd!peora!srd) + * Ken Turkowski (decvax!decwrl!turtlevax!ken) + * James A. Woods (decvax!ihnp4!ames!jaw) + * Joe Orost (decvax!vax135!petsd!joe) + * + */ + + +static stringCode const maxCodeLimitLzw = (stringCode)1 << BITS; + /* One beyond the largest string code that can exist in GIF */ + /* Used only in assertions */ + + +struct hashTableEntry { + long fcode; /* -1 means unused slot */ + unsigned int ent; +}; + + + +/*************************************************************************** +* BYTE OUTPUTTER +***************************************************************************/ + +typedef struct { + FILE * fileP; /* The file to which to output */ + unsigned int count; + /* Number of bytes so far in the current data block */ + unsigned char buffer[256]; + /* The current data block, under construction */ +} byteBuffer; + + + +static byteBuffer * +byteBuffer_create(FILE * const fileP) { + + byteBuffer * byteBufferP; + + MALLOCVAR_NOFAIL(byteBufferP); + + byteBufferP->fileP = fileP; + byteBufferP->count = 0; + + return byteBufferP; +} + + + +static void +byteBuffer_destroy(byteBuffer * const byteBufferP) { + + free(byteBufferP); +} + + + +static void +byteBuffer_flush(byteBuffer * const byteBufferP) { +/*---------------------------------------------------------------------------- + Write the current data block to the output file, then reset the current + data block to empty. +-----------------------------------------------------------------------------*/ + if (byteBufferP->count > 0 ) { + if (verbose) + pm_message("Writing %u byte block", byteBufferP->count); + fputc(byteBufferP->count, byteBufferP->fileP); + fwrite(byteBufferP->buffer, 1, byteBufferP->count, byteBufferP->fileP); + byteBufferP->count = 0; + } +} + + + +static void +byteBuffer_flushFile(byteBuffer * const byteBufferP) { + + fflush(byteBufferP->fileP); + + if (ferror(byteBufferP->fileP)) + pm_error("error writing output file"); +} + + + +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 + characters, flush the data block to the output file. +-----------------------------------------------------------------------------*/ + byteBufferP->buffer[byteBufferP->count++] = c; + if (byteBufferP->count >= 254) + byteBuffer_flush(byteBufferP); +} + + + +/*************************************************************************** +* GIF CODE OUTPUTTER +***************************************************************************/ + +typedef struct { + byteBuffer * byteBufferP; + unsigned int initBits; + unsigned int nBits; + /* Number of bits to put in output for each code */ + stringCode maxCode; /* maximum code, given n_bits */ + stringCode maxCodeLimit; + /* LZW: One beyond the largest string code that can exist in GIF. + Uncompressed: a ceiling to prevent code size from ratcheting up. + In either case, output code never reaches this value. + */ + unsigned long curAccum; + int curBits; + unsigned int codeCount; + /* Number of codes that have been output to this buffer (doesn't + matter if they have gone out the other side yet or not) since + the last flush (or ever, if no last flush). The main use of this + is debugging -- when something fails, you can see in a debugger + where in the image it was, then set a trap for there. + */ +} codeBuffer; + + + +static codeBuffer * +codeBuffer_create(FILE * const ofP, + unsigned int const initBits, + bool const lzw) { + + codeBuffer * codeBufferP; + + MALLOCVAR_NOFAIL(codeBufferP); + + codeBufferP->initBits = initBits; + codeBufferP->nBits = codeBufferP->initBits; + codeBufferP->maxCode = (1 << codeBufferP->nBits) - 1; + codeBufferP->maxCodeLimit = lzw ? + (stringCode)1 << BITS : (stringCode) (1 << codeBufferP->nBits) - 1; + codeBufferP->byteBufferP = byteBuffer_create(ofP); + codeBufferP->curAccum = 0; + codeBufferP->curBits = 0; + codeBufferP->codeCount = 0; + + return codeBufferP; +} + + + +static void +codeBuffer_destroy(codeBuffer * const codeBufferP) { + + byteBuffer_destroy(codeBufferP->byteBufferP); + + free(codeBufferP); +} + + + +static void +codeBuffer_resetCodeSize(codeBuffer * const codeBufferP) { + + codeBufferP->nBits = codeBufferP->initBits; + + assert(codeBufferP->nBits <= BITS); + + codeBufferP->maxCode = (1 << codeBufferP->nBits) - 1; +} + + + +static void +codeBuffer_increaseCodeSize(codeBuffer * const codeBufferP) { + + ++codeBufferP->nBits; + + assert(codeBufferP->nBits <= BITS); + + codeBufferP->maxCode = (1 << codeBufferP->nBits) - 1; +} + + + +static unsigned long const masks[] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, + 0x001F, 0x003F, 0x007F, 0x00FF, + 0x01FF, 0x03FF, 0x07FF, 0x0FFF, + 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF }; + +static void +codeBuffer_output(codeBuffer * const codeBufferP, + stringCode const code) { +/*---------------------------------------------------------------------------- + Output one GIF code to the file, through the code buffer. + + The code is represented as N bits in the file -- the lower + N bits of 'code'. N is a the current code size of *codeBufferP. + + Id 'code' is the maximum possible code for the current code size + for *codeBufferP, increase that code size (unless it's already + maxed out). +-----------------------------------------------------------------------------*/ + assert (code <= codeBufferP->maxCode); + + codeBufferP->curAccum &= masks[codeBufferP->curBits]; + + if (codeBufferP->curBits > 0) + codeBufferP->curAccum |= ((long)code << codeBufferP->curBits); + else + codeBufferP->curAccum = code; + + codeBufferP->curBits += codeBufferP->nBits; + + while (codeBufferP->curBits >= 8) { + byteBuffer_out(codeBufferP->byteBufferP, + codeBufferP->curAccum & 0xff); + codeBufferP->curAccum >>= 8; + codeBufferP->curBits -= 8; + } + + ++codeBufferP->codeCount; +} + + + +static void +codeBuffer_flush(codeBuffer * const codeBufferP) { + + /* Output the possible partial byte in the buffer */ + + if (codeBufferP->curBits > 0) { + byteBuffer_out(codeBufferP->byteBufferP, + codeBufferP->curAccum & 0xff); + codeBufferP->curBits = 0; + } + byteBuffer_flush(codeBufferP->byteBufferP); + + byteBuffer_flushFile(codeBufferP->byteBufferP); + + if (verbose) + pm_message("%u strings of pixels written to file", + codeBufferP->codeCount); + codeBufferP->codeCount = 0; +} + + + +typedef struct { + codeBuffer * codeBufferP; + /* The place to which we write our string codes. + + Constant. + */ + bool lzw; + /* We're actually doing LZW compression. False means we follow + the algorithm enough tht an LZW decompressor will recover the + proper data, but always using one code per pixel, and therefore + not effecting any compression and not using the LZW patent. + */ + unsigned int hshift; + /* This is how many bits we shift left a string code in forming the + primary hash of the concatenation of that string with another. + Constant. + */ + + /* Codes less than 'clearCode' are singleton pixel codes - each + represents the pixel value equal to it. + + Codes greater than 'eofCode' are multipixel string codes. Each + represents a string of pixels that is defined by the preceding + stream. + */ + stringCode clearCode; + /* The code in an LZW stream that means to clear the string + dictionary and start fresh. + + Constant. + */ + stringCode eofCode; + /* The code in an LZW stream that means there's no more coming + + Constant. + */ + stringCode initCodeLimit; + /* The value of 'codeLimit' at the start of a block. + + Constant. + */ + + stringCode codeLimit; + /* One beyond the maximum code possible with the current code + size. + */ + + struct hashTableEntry * hashTable; + stringCode nextUnusedCode; + /* Numerically next code available to assign to a a multi-pixel + string. Note that codes for multi-pixel strings are in the + upper half of the range of codes, always greater than + 'clearCode'. + */ + + stringCode stringSoFar; + /* The code for the string we have built so far. This code indicates + one or more pixels that we have encoded but not yet output + because we're hoping to match an even longer string. + + Valid only when 'buildingString' is true. + + In the non-lzw case the single pixel to output. + */ + bool buildingString; + /* We are in the middle of building a string; 'stringSoFar' describes + the pixels in it so far. The only time this is false is at the + very beginning of the stream. + + Ignored in the non-lzw case. + */ +} lzwCompressor; + + + +static lzwCompressor * +lzw_create(FILE * const ofP, + unsigned int const initBits, + bool const lzw) { + + lzwCompressor * lzwP; + + MALLOCVAR_NOFAIL(lzwP); + + MALLOCARRAY(lzwP->hashTable, HSIZE); + + if (lzwP->hashTable == NULL) + pm_error("Couldn't get memory for %u-entry hash table.", HSIZE); + + /* Constants */ + lzwP->lzw = lzw; + lzwP->hshift = + nSignificantBits(HSIZE-1) - 1 - nSignificantBits(MAXCMAPSIZE-1); + lzwP->clearCode = 1 << (initBits - 1); + lzwP->eofCode = lzwP->clearCode + 1; + lzwP->initCodeLimit = 1 << initBits; + + lzwP->buildingString = FALSE; + + lzwP->codeBufferP = codeBuffer_create(ofP, initBits, lzw); + + return lzwP; +} + + + +static void +lzw_destroy(lzwCompressor * const lzwP) { + + codeBuffer_destroy(lzwP->codeBufferP); + + free(lzwP->hashTable); + + free(lzwP); +} + + + +static void +lzwHashClear(lzwCompressor * const lzwP) { + + /* Empty the code table */ + + unsigned int i; + + for (i = 0; i < HSIZE; ++i) + lzwP->hashTable[i].fcode = -1; + + lzwP->nextUnusedCode = lzwP->clearCode + 2; + + +} + + + +static void +lzw_clearBlock(lzwCompressor * const lzwP) { +/*---------------------------------------------------------------------------- + +-----------------------------------------------------------------------------*/ + lzwHashClear(lzwP); + + codeBuffer_output(lzwP->codeBufferP, lzwP->clearCode); + + codeBuffer_resetCodeSize(lzwP->codeBufferP); + + lzwP->codeLimit = lzwP->initCodeLimit; +} + + + +static void +lzwAdjustCodeSize(lzwCompressor * const lzwP, + stringCode const newCode) { +/*---------------------------------------------------------------------------- + Assuming we just defined code 'newCode', increase the code size as + required so that this code fits. + + The decompressor is mimicking our assignment of that code, so knows that + we are making this adjustment, so expects codes of the new size. +-----------------------------------------------------------------------------*/ + assert(newCode <= lzwP->codeLimit); + + if (newCode == lzwP->codeLimit) { + lzwP->codeLimit *= 2; + codeBuffer_increaseCodeSize(lzwP->codeBufferP); + + assert(lzwP->codeLimit <= maxCodeLimitLzw); + } +} + + + +static void +lzwOutputCurrentString(lzwCompressor * const lzwP) { +/*---------------------------------------------------------------------------- + Put a code for the currently built-up string in the output stream. + + Doing this causes a new string code to be defined (code is + lzwP->nextUnusedCode), so Caller must add that to the hash. If + that code's size is beyond the overall limit, we reset the hash + (which means future codes will start back at the minimum size) and + put a clear code in the stream to tell the decompressor to do the + same. So Caller must add it to the hash _before_ calling us. + + Note that in the non-compressing case, the overall limit is small + enough to prevent us from ever defining string codes; we'll always + reset the hash. + + There's an odd case that always screws up any attempt to make this + code cleaner: At the end of the LZW stream, you have to output the + code for the final string even though you don't have a following + pixel that would make a longer string. So there's nothing to add + to the hash table and no point in allocating a new string code. + But the decompressor doesn't know that we're done, so he allocates + the next string code and may therefore increase his code length. + If we don't do the same, we will write our one last code -- the EOF + code -- in a code length smaller than what the decompressor is + expecting, and he will have a premature end of stream. + + So this subroutine does run for that final code flush and does some + of the motions of defining a new string code, but this subroutine + can't update the hash because in that particular case, there's + nothing to add. +-----------------------------------------------------------------------------*/ + codeBuffer_output(lzwP->codeBufferP, lzwP->stringSoFar); + if (lzwP->nextUnusedCode < lzwP->codeBufferP->maxCodeLimit) { + /* Allocate the code for the extended string, which Caller + should have already put in the hash so he can use it in the + future. Decompressor knows when it sees the code output + above to define a string on its end too, using the same + string code we do. + */ + stringCode const newCode = lzwP->nextUnusedCode++; + + /* This code may be too big to fit in the current code size, in + which case we have to increase the code size (and decompressor + will do the same). + */ + lzwAdjustCodeSize(lzwP, newCode); + } else { + /* Forget all the strings so far; start building again; tell + decompressor to do the same. + */ + lzw_clearBlock(lzwP); + } +} + + + +static void +lzw_flush(lzwCompressor * const lzwP) { + + if (lzwP->lzw) + lzwOutputCurrentString(lzwP); + /* Put out the code for the final string. */ + + codeBuffer_output(lzwP->codeBufferP, lzwP->eofCode); + + codeBuffer_flush(lzwP->codeBufferP); +} + + + +static unsigned int +primaryHash(stringCode const baseString, + stringCode const additionalPixel, + unsigned int const hshift) { + + unsigned int hash; + + assert(baseString < maxCodeLimitLzw); + assert(additionalPixel < MAXCMAPSIZE); + + hash = (additionalPixel << hshift) ^ baseString; + + assert(hash < HSIZE); + + return hash; +} + + + +static void +lookupInHash(lzwCompressor * const lzwP, + unsigned int const gifPixel, + long const fcode, + bool * const foundP, + unsigned short * const codeP, + unsigned int * const hashP) { + + int disp; + /* secondary hash stride (after G. Knott) */ + int i; + /* Index into hash table */ + + i = primaryHash(lzwP->stringSoFar, gifPixel, lzwP->hshift); + disp = (i == 0) ? 1 : HSIZE - i; + + while (lzwP->hashTable[i].fcode != fcode && + lzwP->hashTable[i].fcode >= 0) { + i -= disp; + if (i < 0) + i += HSIZE; + } + + if (lzwP->hashTable[i].fcode == fcode) { + /* Found fcode in hash table */ + *foundP = TRUE; + *codeP = lzwP->hashTable[i].ent; + } else { + /* Found where it _should_ be (but it's not) with primary hash */ + *foundP = FALSE; + *hashP = i; + } +} + + + +static void +lzw_encodePixel(lzwCompressor * const lzwP, + unsigned int const gifPixel) { + + bool found; + unsigned short code; + unsigned int hash; + /* Index into hash table where the value should go */ + + assert(gifPixel < 256); + + if (!lzwP->buildingString) { + /* Start a new string with just this pixel */ + lzwP->stringSoFar = gifPixel; + lzwP->buildingString = TRUE; + } else { + long const fcode = ((long) gifPixel << BITS) + lzwP->stringSoFar; + /* The encoding of the string we've already recognized plus the + instant pixel, to be looked up in the hash of knows strings. + */ + + lookupInHash(lzwP, gifPixel, fcode, &found, &code, &hash); + + if (found) + /* With this new pixel, it is still a known string; 'code' is + its code + */ + lzwP->stringSoFar = code; + else { + /* It's no longer a known string. Output the code for the + known prefix of the string, thus defining a new string + code for possible later use. Warning: + lzwOutputCurrentString() does more than you think. + */ + + lzwP->hashTable[hash].ent = lzwP->nextUnusedCode; + lzwP->hashTable[hash].fcode = fcode; + + lzwOutputCurrentString(lzwP); + + /* This singleton pixel starts the next string */ + lzwP->stringSoFar = gifPixel; + } + } +} + + + +/* + * compress stdin to stdout + * + * Algorithm: use open addressing double hashing (no chaining) on the + * prefix code / next character combination. We do a variant of Knuth's + * algorithm D (vol. 3, sec. 6.4) along with G. Knott's relatively-prime + * secondary probe. Here, the modular division first probe is gives way + * to a faster exclusive-or manipulation. Also do block compression with + * an adaptive reset, whereby the code table is cleared when the compression + * ratio decreases, but after the table fills. The variable-length output + * codes are re-sized at this point, and a special CLEAR code is generated + * for the decompressor. Late addition: construct the table according to + * file size for noticeable speed improvement on small files. Please direct + * questions about this implementation to ames!jaw. + */ + +static void +writePixelUncompressed(lzwCompressor * const lzwP, + unsigned int const gifPixel) { + + lzwP->stringSoFar = gifPixel; + lzwOutputCurrentString(lzwP); + +} + +static void +writeRaster(struct pam * const pamP, + rowReader * const rowReaderP, + unsigned int const alphaPlane, + unsigned int const alphaThreshold, + struct cmap * const cmapP, + unsigned int const initBits, + FILE * const ofP, + bool const lzw) { +/*---------------------------------------------------------------------------- + Write the raster to file 'ofP'. + + Get the raster to write from 'rowReaderP', which gives tuples whose + format is described by 'pamP'. + + Use the colormap 'cmapP' to generate the raster ('rowReaderP' gives + pixel values as RGB samples; the GIF raster is colormap indices). + + Write the raster using LZW compression, or uncompressed depending + on 'lzw'. +-----------------------------------------------------------------------------*/ + lzwCompressor * lzwP; + tuple * tuplerow; + unsigned int nRowsDone; + /* Number of rows we have read so far from the the input (the + last of which is the one we're working on now). Note that + in case of interlace, this is not the same thing as the row + number of the current row. + */ + + lzwP = lzw_create(ofP, initBits, lzw); + + tuplerow = pnm_allocpamrow(pamP); + + lzw_clearBlock(lzwP); + + nRowsDone = 0; + + while (nRowsDone < pamP->height) { + unsigned int col; + + rowReader_read(rowReaderP, tuplerow); + + for (col = 0; col < pamP->width; ++col) { + unsigned int const colorIndex = + gifPixel(pamP, tuplerow[col], alphaPlane, alphaThreshold, + cmapP); + + /* The value for the pixel in the GIF image. I.e. the colormap + index. + */ + if (lzw) + lzw_encodePixel(lzwP, colorIndex); + else + writePixelUncompressed(lzwP, colorIndex); + } + ++nRowsDone; + } + /* Gif is no good with no pixels; fortunately, that's impossible: */ + assert(nRowsDone > 0); + + lzw_flush(lzwP); + + pnm_freepamrow(tuplerow); + + lzw_destroy(lzwP); +} + + + + + + + + + +static void +writeGlobalColorMap(FILE * const ofP, + const struct cmap * const cmapP, + unsigned int const bitsPerPixel) { +/*---------------------------------------------------------------------------- + Write out the Global Color Map + + Note that the Global Color Map is always a power of two colors + in size, but *cmapP could be smaller than that. So we pad with + black. +-----------------------------------------------------------------------------*/ + unsigned int const colorMapSize = 1 << bitsPerPixel; + + struct pam pam; + unsigned int i; + tuple tupleRgb255; + + if (verbose) + pm_message("Writing %u-entry global colormap for %u colors", + colorMapSize, cmapP->cmapSize); + + pam = cmapP->pam; + pam.size = PAM_STRUCT_SIZE(allocation_depth); + pam.len = pam.size; + pnm_setminallocationdepth(&pam, 3); + + tupleRgb255 = pnm_allocpamtuple(&pam); + + for (i = 0; i < colorMapSize; ++i) { + if (i < cmapP->cmapSize) { + tuple const color = cmapP->color[cmapP->permi[i]]; + + assert(cmapP->permi[i] < cmapP->cmapSize); + + pnm_scaletuple(&pam, tupleRgb255, color, 255); + pnm_maketuplergb(&pam, tupleRgb255); + + fputc(tupleRgb255[PAM_RED_PLANE], ofP); + fputc(tupleRgb255[PAM_GRN_PLANE], ofP); + fputc(tupleRgb255[PAM_BLU_PLANE], ofP); + } else { + fputc(0, ofP); + fputc(0, ofP); + fputc(0, ofP); + } + } + pnm_freepamtuple(tupleRgb255); +} + + + +static void +writeGifHeader(FILE * const ofP, + unsigned int const width, + unsigned int const height, + unsigned int const background, + unsigned int const bitsPerPixel, + const struct cmap * const cmapP, + char const comment[]) { + + unsigned int const resolution = bitsPerPixel; + + unsigned char b; + + /* Write the Magic header */ + if (cmapP->haveTransparent || comment) + fwrite("GIF89a", 1, 6, ofP); + else + fwrite("GIF87a", 1, 6, ofP); + + /* Write out the screen width and height */ + Putword(width, ofP); + Putword(height, ofP); + + /* Indicate that there is a global color map */ + b = 0x80; /* Yes, there is a color map */ + + /* OR in the resolution */ + b |= (resolution - 1) << 4; + + /* OR in the Bits per Pixel */ + b |= (bitsPerPixel - 1); + + /* Write it out */ + fputc(b, ofP); + + /* Write out the Background color */ + assert((unsigned char)background == background); + fputc(background, ofP); + + /* Byte of 0's (future expansion) */ + fputc(0x00, ofP); + + writeGlobalColorMap(ofP, cmapP, bitsPerPixel); + + if (cmapP->haveTransparent) + writeTransparentColorIndexExtension(ofP, cmapP->transparent); + + if (comment) + writeCommentExtension(ofP, comment); +} + + + +static void +writeImageHeader(FILE * const ofP, + unsigned int const leftOffset, + unsigned int const topOffset, + unsigned int const gWidth, + unsigned int const gHeight, + bool const gInterlace, + unsigned int const initCodeSize) { + + Putword(leftOffset, ofP); + Putword(topOffset, ofP); + Putword(gWidth, ofP); + Putword(gHeight, ofP); + + /* Write out whether or not the image is interlaced */ + if (gInterlace) + fputc(0x40, ofP); + else + fputc(0x00, ofP); + + /* Write out the initial code size */ + fputc(initCodeSize, ofP); +} + + + +static void +reportImageInfo(bool const interlace, + unsigned int const background, + unsigned int const bitsPerPixel) { + + if (verbose) { + if (interlace) + pm_message("interlaced"); + else + pm_message("not interlaced"); + pm_message("Background color index = %u", background); + pm_message("%u bits per pixel", bitsPerPixel); + } +} + + + +static void +gifEncode(struct pam * const pamP, + FILE * const ofP, + pm_filepos const rasterPos, + bool const gInterlace, + int const background, + unsigned int const bitsPerPixel, + struct cmap * const cmapP, + char const comment[], + bool const lzw) { + + unsigned int const leftOffset = 0; + unsigned int const topOffset = 0; + + unsigned int const initCodeSize = bitsPerPixel <= 1 ? 2 : bitsPerPixel; + /* The initial code size */ + + sample const alphaThreshold = (pamP->maxval + 1) / 2; + /* Levels below this in the alpha plane indicate transparent + pixels in the output image. + */ + + unsigned int const alphaPlane = pamAlphaPlane(pamP); + + rowReader * rowReaderP; + + reportImageInfo(gInterlace, background, bitsPerPixel); + + writeGifHeader(ofP, pamP->width, pamP->height, background, + bitsPerPixel, cmapP, comment); + + /* Write an Image separator */ + fputc(',', ofP); + + writeImageHeader(ofP, leftOffset, topOffset, pamP->width, pamP->height, + gInterlace, initCodeSize); + + rowReaderP = rowReader_create(pamP, rasterPos, gInterlace); + + /* Write the actual raster */ + + writeRaster(pamP, rowReaderP, alphaPlane, alphaThreshold, + cmapP, initCodeSize + 1, ofP, lzw); + + rowReader_destroy(rowReaderP); + + /* Write out a zero length data block (to end the series) */ + fputc(0, ofP); + + /* Write the GIF file terminator */ + fputc(';', ofP); +} + + + +static void +reportTransparent(struct cmap * const cmapP) { + + if (verbose) { + if (cmapP->haveTransparent) { + tuple const color = cmapP->color[cmapP->permi[cmapP->transparent]]; + pm_message("Color %u (%lu, %lu, %lu) is transparent", + cmapP->transparent, + color[PAM_RED_PLANE], + color[PAM_GRN_PLANE], + color[PAM_BLU_PLANE]); + } else + pm_message("No transparent color"); + } +} + + + +static void +computeTransparent(char const colorarg[], + bool const usingFakeTrans, + unsigned int const fakeTransparent, + struct cmap * const cmapP) { +/*---------------------------------------------------------------------------- + Figure out the color index (index into the colormap) of the color + that is to be transparent in the GIF. + + colorarg[] is the string that specifies the color the user wants to + be transparent (e.g. "red", "#fefefe"). Its maxval is the maxval + of the colormap. 'cmap' is the full colormap except that its + 'transparent' component isn't valid. + + colorarg[] is a standard Netpbm color specification, except that + may have a "=" prefix, which means it specifies a particular exact + color, as opposed to without the "=", which means "the color that + is closest to this and actually in the image." + + colorarg[] null means the color didn't ask for a particular color + to be transparent. + + Establish no transparent color if colorarg[] specifies an exact + color and that color is not in the image. Also issue an + informational message. + + 'usingFakeTrans' means pixels will be transparent because of something + other than their foreground color, and 'fakeTransparent' is the + color map index for transparent colors. +-----------------------------------------------------------------------------*/ + if (colorarg) { + const char * colorspec; + bool exact; + tuple transcolor; + bool found; + int presortColorindex; + + if (colorarg[0] == '=') { + colorspec = &colorarg[1]; + exact = TRUE; + } else { + colorspec = colorarg; + exact = FALSE; + } + + transcolor = pnm_parsecolor(colorspec, cmapP->pam.maxval); + pnm_lookuptuple(&cmapP->pam, cmapP->tuplehash, transcolor, &found, + &presortColorindex); + + if (found) { + cmapP->haveTransparent = TRUE; + cmapP->transparent = cmapP->perm[presortColorindex]; + } else if (!exact) { + cmapP->haveTransparent = TRUE; + cmapP->transparent = + cmapP->perm[closestColor(transcolor, &cmapP->pam, cmapP)]; + } else { + cmapP->haveTransparent = FALSE; + pm_message("Warning: specified transparent color " + "does not occur in image."); + } + } else if (usingFakeTrans) { + cmapP->haveTransparent = TRUE; + cmapP->transparent = fakeTransparent; + } else + cmapP->haveTransparent = FALSE; + + reportTransparent(cmapP); +} + + + +static unsigned int +sortOrder(tuple const tuple, + struct pam * const pamP) { + + if (pamP->depth < 3) + return tuple[0]; + else + return ((tuple[0] * MAXCMAPSIZE) + tuple[1]) * MAXCMAPSIZE + tuple[2]; +} + + + + +static void +sortColormap(bool const sort, + struct cmap * const cmapP) { +/*---------------------------------------------------------------------------- + Sort (in place) the colormap *cmapP. + + Create the perm[] and permi[] mappings for the colormap. + + 'sort' is logical: true means to sort the colormap by red intensity, + then by green intensity, then by blue intensity. False means a null + sort -- leave it in the same order in which we found it. +-----------------------------------------------------------------------------*/ + tuple * const color = cmapP->color; + int * const perm = cmapP->perm; + int * const permi = cmapP->permi; + struct pam * const pamP = &cmapP->pam; + unsigned int const cmapSize = cmapP->cmapSize; + + unsigned int i; + + /* We will sort permi[]. So we first set up permi[] to reflect the + original, unsorted order. + */ + for (i=0; i < cmapSize; i++) + permi[i] = i; + + if (sort) { + pm_message("sorting colormap"); + for (i = 0; i < cmapSize; ++i) { + unsigned int j; + for (j = i+1; j < cmapSize; ++j) { + if (sortOrder(color[permi[i]], pamP) > + sortOrder(color[permi[j]], pamP)) { + + sample tmp; + + tmp = permi[i]; permi[i] = permi[j]; permi[j] = tmp; + } + } + } + } + for (i = 0; i < cmapSize; ++i) + perm[permi[i]] = i; +} + + + +static void +addToColormap(struct cmap * const cmapP, + const char * const colorspec, + unsigned int * const newIndexP) { +/*---------------------------------------------------------------------------- + Add a new entry to the colormap. Make the color that specified by + 'colorspec', and return the index of the new entry as *newIndexP. + + 'colorspec' is a color specification given by the user, e.g. + "red" or "rgb:ff/03/0d". The maxval for this color specification is + that for the colormap *cmapP. +-----------------------------------------------------------------------------*/ + tuple const transcolor = pnm_parsecolor(colorspec, cmapP->pam.maxval); + + unsigned int const colorIndex = cmapP->cmapSize++; + + cmapP->color[colorIndex] = pnm_allocpamtuple(&cmapP->pam); + + if (cmapP->pam.depth < 3) { + if (!pnm_rgbtupleisgray(transcolor)) + pm_error("Image is grayscale, but color '%s' is not gray. " + "It is (%lu, %lu, %lu)", + colorspec, + transcolor[PAM_RED_PLANE], + transcolor[PAM_GRN_PLANE], + transcolor[PAM_BLU_PLANE]); + else + cmapP->color[colorIndex][0] = transcolor[0]; + } else { + pnm_assigntuple(&cmapP->pam, cmapP->color[colorIndex], transcolor); + } + *newIndexP = colorIndex; +} + + + +static void +colormapFromFile(char const filespec[], + unsigned int const maxcolors, + tupletable * const tupletableP, + struct pam * const mapPamP, + unsigned int * const colorCountP) { +/*---------------------------------------------------------------------------- + Read a colormap from the Netpbm file filespec[]. Return a + tupletable of the colors in it (which is practically a colormap) as + *tupletableP and the format of those tuples as *mapPamP. Return + the number of colors as *colorsCountP. +-----------------------------------------------------------------------------*/ + FILE * mapfileP; + tuple ** colors; + unsigned int colorCount; + + mapfileP = pm_openr(filespec); + colors = pnm_readpam(mapfileP, mapPamP, PAM_STRUCT_SIZE(tuple_type)); + pm_close(mapfileP); + + pm_message("computing other colormap ..."); + + pnm_computetuplefreqtable(mapPamP, colors, maxcolors, &colorCount); + + *colorCountP = colorCount; + + pnm_freepamarray(colors, mapPamP); +} + + + +static void +computeColormapFromInput(struct pam * const pamP, + unsigned int const maxcolors, + unsigned int const nInputComp, + struct pam * const mapPamP, + unsigned int * const colorCountP, + tupletable * const tuplefreqP) { + + tupletable tuplefreq; + + pm_message("computing colormap..."); + + tuplefreq = pnm_computetuplefreqtable3( + pamP, NULL, maxcolors, nInputComp, pamP->maxval, colorCountP); + + *mapPamP = *pamP; + mapPamP->depth = nInputComp; + + *tuplefreqP = tuplefreq; +} + + + +static void +computeLibnetpbmColormap(struct pam * const pamP, + bool const haveAlpha, + const char * const mapfile, + tuple * const color, + tuplehash * const tuplehashP, + struct pam * const mapPamP, + unsigned int * const colorCountP) { +/*---------------------------------------------------------------------------- + Compute a colormap, libnetpbm style, for the image described by + 'pamP', which is positioned to the raster. + + If 'mapfile' is non-null, Use the colors in that (Netpbm) file for + the color map instead of the colors in 'pamP'. + + Return the colormap as color[] and *tuplehashP. Return the format + of those tuples as *mapPamP. + + The tuples of the color map have a meaningful depth of 1 (grayscale) or 3 + (color) and *mapPamP reflects that. + + While we're at it, count the colors and validate that there aren't + too many. Return the count as *colorCountP. In determining if there are + too many, allow one slot for a fake transparency color if 'haveAlpha' + is true. If there are too many, issue an error message and abort the + program. +-----------------------------------------------------------------------------*/ + unsigned int const maxcolors = haveAlpha ? MAXCMAPSIZE - 1 : MAXCMAPSIZE; + /* The most colors we can tolerate in the image. If we have + our own made-up entry in the colormap for transparency, it + isn't included in this count. + */ + unsigned int const nInputComp = haveAlpha ? pamP->depth - 1 : pamP->depth; + /* Number of color components (not alpha) in the input image */ + + unsigned int i; + tupletable tuplefreq; + unsigned int colorCount; + + if (mapfile) { + /* Read the colormap from a separate colormap file. */ + colormapFromFile(mapfile, maxcolors, &tuplefreq, mapPamP, + &colorCount); + + if (mapPamP->depth != nInputComp) + pm_error("Depth of map file (%u) does not match number of " + "color components in input file (%u)", + mapPamP->depth, pamP->depth); + if (mapPamP->maxval != pamP->maxval) + pm_error("Maxval of map file (%lu) does not match maxval of " + "input file (%lu)", mapPamP->maxval, pamP->maxval); + } else + computeColormapFromInput(pamP, maxcolors, nInputComp, + mapPamP, &colorCount, &tuplefreq); + + if (tuplefreq == NULL) + pm_error("too many colors - try doing a 'pnmquant %u'", maxcolors); + + pm_message("%u colors found", colorCount); + + for (i = 0; i < colorCount; ++i) { + color[i] = pnm_allocpamtuple(mapPamP); + pnm_assigntuple(mapPamP, color[i], tuplefreq[i]->tuple); + } + + /* And make a hash table for fast lookup. */ + *tuplehashP = + pnm_computetupletablehash(mapPamP, tuplefreq, colorCount); + + *colorCountP = colorCount; + + pnm_freetupletable(mapPamP, tuplefreq); +} + + + +static void +destroyCmap(struct cmap * const cmapP) { + + unsigned int colorIndex; + + for (colorIndex = 0; colorIndex < cmapP->cmapSize; ++colorIndex) + pnm_freepamtuple(cmapP->color[colorIndex]); + + pnm_destroytuplehash(cmapP->tuplehash); +} + + + +int +main(int argc, char *argv[]) { + struct cmdlineInfo cmdline; + FILE * ifP; + struct pam pam; + unsigned int bitsPerPixel; + pm_filepos rasterPos; + + struct cmap cmap; + /* The colormap, with all its accessories */ + unsigned int fakeTransparent; + /* colormap index of the fake transparency color we're using to + implement the alpha mask. Undefined if we're not doing an alpha + mask. + */ + + pnm_init(&argc, argv); + + parseCommandLine(argc, argv, &cmdline); + + verbose = cmdline.verbose; + + ifP = pm_openr_seekable(cmdline.input_filespec); + + pnm_readpaminit(ifP, &pam, PAM_STRUCT_SIZE(tuple_type)); + + pm_tell2(ifP, &rasterPos, sizeof(rasterPos)); + + computeLibnetpbmColormap(&pam, !!pamAlphaPlane(&pam), cmdline.mapfile, + cmap.color, &cmap.tuplehash, + &cmap.pam, &cmap.cmapSize); + + assert(cmap.pam.maxval == pam.maxval); + + if (pamAlphaPlane(&pam)) { + /* Add a fake entry to the end of the colormap for transparency. + Make its color black. + */ + addToColormap(&cmap, cmdline.alphacolor, &fakeTransparent); + } + sortColormap(cmdline.sort, &cmap); + + bitsPerPixel = pm_maxvaltobits(cmap.cmapSize-1); + + computeTransparent(cmdline.transparent, + !!pamAlphaPlane(&pam), fakeTransparent, &cmap); + + /* All set, let's do it. */ + gifEncode(&pam, stdout, rasterPos, + cmdline.interlace, 0, bitsPerPixel, &cmap, cmdline.comment, + !cmdline.nolzw); + + destroyCmap(&cmap); + + pm_close(ifP); + pm_close(stdout); + + return 0; +} + + + +/*============================================================================ + Original version, named 'ppmgif' was by Jef Poskanzer in 1989, based + on GIFENCOD by David Rowley .A Lempel-Zim + compression based on "compress". + + Switched to use libnetpbm PAM facilities (ergo process PAM images) + and renamed 'pamtogif' by Bryan Henderson November 2006. + + The non-LZW GIF generation stuff was adapted from the Independent + JPEG Group's djpeg on 2001.09.29. In 2006.12 the output subroutines + were rewritten; now no uncompressed output subroutines are derived from + the Independent JPEG Group's source code. + + + Copyright (C) 1989 by Jef Poskanzer. + + Permission to use, copy, modify, and distribute this software and its + documentation for any purpose and without fee is hereby granted, provided + that the above copyright notice appear in all copies and that both that + copyright notice and this permission notice appear in supporting + documentation. This software is provided "as is" without express or + implied warranty. + + The Graphics Interchange Format(c) is the Copyright property of + CompuServe Incorporated. GIF(sm) is a Service Mark property of + CompuServe Incorporated. +============================================================================*/ diff --git a/converter/other/pamtopnm.c b/converter/other/pamtopnm.c index cc1164da..6835b43f 100644 --- a/converter/other/pamtopnm.c +++ b/converter/other/pamtopnm.c @@ -108,6 +108,7 @@ main(int argc, char *argv[]) { struct cmdlineInfo cmdline; FILE* ifP; + bool eof; /* no more images in input stream */ struct pam inpam; /* Input PAM image */ struct pam outpam; /* Output PNM image */ @@ -117,39 +118,45 @@ main(int argc, char *argv[]) { ifP = pm_openr(cmdline.inputFilespec); - pnm_readpaminit(ifP, &inpam, PAM_STRUCT_SIZE(tuple_type)); + eof = FALSE; + while (!eof) { + pnm_readpaminit(ifP, &inpam, PAM_STRUCT_SIZE(tuple_type)); - validateTupleType(inpam, cmdline.assume); + validateTupleType(inpam, cmdline.assume); - outpam = inpam; - outpam.file = stdout; - - if (inpam.depth < 3) { - outpam.depth = 1; - if (inpam.maxval == 1) - outpam.format = PBM_FORMAT; - else - outpam.format = PGM_FORMAT; - } else { - outpam.depth = 3; - outpam.format = PPM_FORMAT; - } + outpam = inpam; + outpam.file = stdout; + + if (inpam.depth < 3) { + outpam.depth = 1; + if (inpam.maxval == 1) + outpam.format = PBM_FORMAT; + else + outpam.format = PGM_FORMAT; + } else { + outpam.depth = 3; + outpam.format = PPM_FORMAT; + } - pnm_writepaminit(&outpam); + pnm_writepaminit(&outpam); - { - tuple *tuplerow; - - tuplerow = pnm_allocpamrow(&inpam); - { - int row; + { + tuple *tuplerow; - for (row = 0; row < inpam.height; row++) { - pnm_readpamrow(&inpam, tuplerow); - pnm_writepamrow(&outpam, tuplerow); + tuplerow = pnm_allocpamrow(&inpam); + { + int row; + + for (row = 0; row < inpam.height; row++) { + pnm_readpamrow(&inpam, tuplerow); + pnm_writepamrow(&outpam, tuplerow); + } } + pnm_freepamrow(tuplerow); } - pnm_freepamrow(tuplerow); + + pnm_nextimage(ifP, &eof); } + return 0; } diff --git a/converter/other/pamtosvg/curve.h b/converter/other/pamtosvg/curve.h index ee046620..2f010d45 100644 --- a/converter/other/pamtosvg/curve.h +++ b/converter/other/pamtosvg/curve.h @@ -77,14 +77,16 @@ extern curve_type copy_most_of_curve (curve_type c); /* Free the memory C uses. */ extern void free_curve (curve_type c); +/* Like `append_pixel', for a point in real coordinates. */ +void +append_point(curve_type const curve, + float_coord const coord); + /* Append the point P to the end of C's list. */ void append_pixel(curve_type const c, pm_pixelcoord const p); -/* Like `append_pixel', for a point in real coordinates. */ -extern void append_point (curve_type const c, float_coord const p); - /* Write some or all, respectively, of the curve C in human-readable form to the log file, if logging is enabled. */ extern void log_curve (curve_type c, bool print_t); diff --git a/converter/other/pamtosvg/fit.c b/converter/other/pamtosvg/fit.c index 08db41db..2fa64500 100644 --- a/converter/other/pamtosvg/fit.c +++ b/converter/other/pamtosvg/fit.c @@ -54,21 +54,6 @@ typedef struct index_list #define INDEX_LIST_LENGTH(i_l) ((i_l).length) #define GET_LAST_INDEX(i_l) ((i_l).data[INDEX_LIST_LENGTH (i_l) - 1]) -static void append_index (index_list_type *, unsigned); -static void free_index_list (index_list_type *); -static index_list_type new_index_list (void); -static void remove_adjacent_corners (index_list_type *, unsigned, bool, - at_exception_type * exception); -static void filter (curve_type, fitting_opts_type *); -static void find_vectors - (unsigned const, pixel_outline_type const, vector_type * const, vector_type * const, unsigned const); -static float find_error (curve_type, spline_type, unsigned *, - at_exception_type * exception); -static vector_type find_half_tangent (curve_type, bool start, unsigned *, unsigned); -static void find_tangent (curve_type, bool, bool, unsigned); -static void remove_knee_points (curve_type const, bool const); -static void set_initial_parameter_values (curve_type); -static float distance (float_coord, float_coord); static pm_pixelcoord @@ -86,6 +71,50 @@ real_to_int_coord(float_coord const real_coord) { } +/* Lists of array indices (well, that is what we use it for). */ + +static index_list_type +new_index_list (void) +{ + index_list_type index_list; + + index_list.data = NULL; + INDEX_LIST_LENGTH (index_list) = 0; + + return index_list; +} + +static void +free_index_list (index_list_type *index_list) +{ + if (INDEX_LIST_LENGTH (*index_list) > 0) + { + free (index_list->data); + index_list->data = NULL; + INDEX_LIST_LENGTH (*index_list) = 0; + } +} + +static void +append_index (index_list_type *list, unsigned new_index) +{ + INDEX_LIST_LENGTH (*list)++; + REALLOCARRAY_NOFAIL(list->data, INDEX_LIST_LENGTH(*list)); + list->data[INDEX_LIST_LENGTH (*list) - 1] = new_index; +} + + +/* Return the Euclidean distance between P1 and P2. */ + +static float +distance (float_coord p1, float_coord p2) +{ + float x = p1.x - p2.x, y = p1.y - p2.y, z = p1.z - p2.z; + return (float) sqrt (SQR(x) + SQR(y) + SQR(z)); +} + + + static void appendCorner(index_list_type * const cornerListP, unsigned int const pixelSeq, @@ -101,6 +130,45 @@ appendCorner(index_list_type * const cornerListP, +static void +find_vectors(unsigned int const test_index, + pixel_outline_type const outline, + vector_type * const in, + vector_type * const out, + unsigned int const corner_surround) { +/*---------------------------------------------------------------------------- + Return the difference vectors coming in and going out of the outline + OUTLINE at the point whose index is TEST_INDEX. In Phoenix, + Schneider looks at a single point on either side of the point we're + considering. That works for him because his points are not touching. + But our points *are* touching, and so we have to look at + `corner_surround' points on either side, to get a better picture of + the outline's shape. +-----------------------------------------------------------------------------*/ + int i; + unsigned n_done; + pm_pixelcoord const candidate = O_COORDINATE(outline, test_index); + + in->dx = in->dy = in->dz = 0.0; + out->dx = out->dy = out->dz = 0.0; + + /* Add up the differences from p of the `corner_surround' points + before p. + */ + for (i = O_PREV(outline, test_index), n_done = 0; + n_done < corner_surround; + i = O_PREV(outline, i), ++n_done) + *in = Vadd(*in, IPsubtract(O_COORDINATE(outline, i), candidate)); + + /* And the points after p. */ + for (i = O_NEXT (outline, test_index), n_done = 0; + n_done < corner_surround; + i = O_NEXT(outline, i), ++n_done) + *out = Vadd(*out, IPsubtract(O_COORDINATE(outline, i), candidate)); +} + + + static void lookAheadForBetterCorner(pixel_outline_type const outline, unsigned int const basePixelSeq, @@ -210,120 +278,401 @@ establishCornerSearchLimits(pixel_outline_type const outline, static void -removeAdjacent(index_list_type * const cornerListP, - pixel_outline_type const outline, - fitting_opts_type * const fittingOptsP, - at_exception_type * const exception) { - - /* We never want two corners next to each other, since the - only way to fit such a ``curve'' would be with a straight - line, which usually interrupts the continuity dreadfully. - */ +remove_adjacent_corners(index_list_type * const list, + unsigned int const last_index, + bool const remove_adj_corners, + at_exception_type * const exception) { +/*---------------------------------------------------------------------------- + Remove adjacent points from the index list LIST. We do this by first + sorting the list and then running through it. Since these lists are + quite short, a straight selection sort (e.g., p.139 of the Art of + Computer Programming, vol.3) is good enough. LAST_INDEX is the index + of the last pixel on the outline, i.e., the next one is the first + pixel. We need this for checking the adjacency of the last corner. - if (INDEX_LIST_LENGTH(*cornerListP) > 0) - remove_adjacent_corners( - cornerListP, - O_LENGTH(outline) - (outline.open ? 2 : 1), - fittingOptsP->remove_adjacent_corners, - exception); -} + We need to do this because the adjacent corners turn into + two-pixel-long curves, which can only be fit by straight lines. +-----------------------------------------------------------------------------*/ + unsigned int j; + unsigned int last; + index_list_type new_list = new_index_list (); + for (j = INDEX_LIST_LENGTH (*list) - 1; j > 0; j--) + { + unsigned search; + unsigned temp; + /* Find maximal element below `j'. */ + unsigned max_index = j; + for (search = 0; search < j; search++) + if (GET_INDEX (*list, search) > GET_INDEX (*list, max_index)) + max_index = search; -static index_list_type -find_corners(pixel_outline_type const outline, - fitting_opts_type * const fittingOptsP, - at_exception_type * const exceptionP) { + if (max_index != j) + { + temp = GET_INDEX (*list, j); + GET_INDEX (*list, j) = GET_INDEX (*list, max_index); + GET_INDEX (*list, max_index) = temp; + + /* xx -- really have to sort? */ + LOG ("needed exchange"); + at_exception_warning(exception, "needed exchange"); + } + } - /* We consider a point to be a corner if (1) the angle defined by - the `corner_surround' points coming into it and going out from - it is less than `corner_threshold' degrees, and no point within - `corner_surround' points has a smaller angle; or (2) the angle - is less than `corner_always_threshold' degrees. - */ - unsigned int p; - unsigned int firstPixelSeq, lastPixelSeq; - index_list_type cornerList; + /* The list is sorted. Now look for adjacent entries. Each time + through the loop we insert the current entry and, if appropriate, + the next entry. */ + for (j = 0; j < INDEX_LIST_LENGTH (*list) - 1; j++) + { + unsigned current = GET_INDEX (*list, j); + unsigned next = GET_INDEX (*list, j + 1); - cornerList = new_index_list(); + /* We should never have inserted the same element twice. */ + /* assert (current != next); */ - if (O_LENGTH(outline) <= fittingOptsP->corner_surround * 2 + 1) - return cornerList; + if ((remove_adj_corners) && ((next == current + 1) || (next == current))) + j++; - establishCornerSearchLimits(outline, fittingOptsP, - &firstPixelSeq, &lastPixelSeq); - - /* Consider each pixel on the outline in turn. */ - for (p = firstPixelSeq; p <= lastPixelSeq;) { - vector_type inVector, outVector; - float cornerAngle; + append_index (&new_list, current); + } - /* Check if the angle is small enough. */ - find_vectors(p, outline, &inVector, &outVector, - fittingOptsP->corner_surround); - cornerAngle = Vangle(inVector, outVector, exceptionP); - if (at_exception_got_fatal(exceptionP)) - goto cleanup; + /* Don't append the last element if it is 1) adjacent to the previous + one; or 2) adjacent to the very first one. */ + last = GET_LAST_INDEX (*list); + if (INDEX_LIST_LENGTH (new_list) == 0 + || !(last == GET_LAST_INDEX (new_list) + 1 + || (last == last_index && GET_INDEX (*list, 0) == 0))) + append_index (&new_list, last); - if (fabs(cornerAngle) <= fittingOptsP->corner_threshold) { - /* We want to keep looking, instead of just appending the - first pixel we find with a small enough angle, since there - might be another corner within `corner_surround' pixels, with - a smaller angle. If that is the case, we want that one. + free_index_list (list); + *list = new_list; +} - If we come across a corner that is just as good as the - best one, we should make it a corner, too. This - happens, for example, at the points on the `W' in some - typefaces, where the "points" are flat. - */ - float bestCornerAngle; - unsigned bestCornerIndex; - index_list_type equallyGoodList; - unsigned int q; +/* A ``knee'' is a point which forms a ``right angle'' with its + predecessor and successor. See the documentation (the `Removing + knees' section) for an example and more details. - if (cornerAngle <= fittingOptsP->corner_always_threshold) - /* The angle is sufficiently small that we want to - consider this a corner, even if it's not the best. - */ - appendCorner(&cornerList, p, outline, cornerAngle, '\\'); + The argument CLOCKWISE tells us which direction we're moving. (We + can't figure that information out from just the single segment with + which we are given to work.) - lookAheadForBetterCorner(outline, p, cornerAngle, - fittingOptsP->corner_surround, - fittingOptsP->corner_always_threshold, - &q, - &bestCornerAngle, &bestCornerIndex, - &equallyGoodList, - &cornerList, - exceptionP); + We should never find two consecutive knees. - if (at_exception_got_fatal(exceptionP)) - goto cleanup; + Since the first and last points are corners (unless the curve is + cyclic), it doesn't make sense to remove those. */ - /* `q' is the index of the last point lookAhead checked. - He added the corner if `bestCornerAngle' is less than - `corner_always_threshold'. If we've wrapped around, we - added the corner on the first pass. Otherwise, we add - the corner now. - */ - if (bestCornerAngle > fittingOptsP->corner_always_threshold - && bestCornerIndex >= p) { +/* This evaluates to true if the vector V is zero in one direction and + nonzero in the other. */ +#define ONLY_ONE_ZERO(v) \ + (((v).dx == 0.0 && (v).dy != 0.0) || ((v).dy == 0.0 && (v).dx != 0.0)) - unsigned int j; - - appendCorner(&cornerList, bestCornerIndex, - outline, bestCornerAngle, '/'); - - for (j = 0; j < INDEX_LIST_LENGTH (equallyGoodList); ++j) - appendCorner(&cornerList, GET_INDEX(equallyGoodList, j), - outline, bestCornerAngle, '@'); - } - free_index_list(&equallyGoodList); +/* There are four possible cases for knees, one for each of the four + corners of a rectangle; and then the cases differ depending on which + direction we are going around the curve. The tests are listed here + in the order of upper left, upper right, lower right, lower left. + Perhaps there is some simple pattern to the + clockwise/counterclockwise differences, but I don't see one. */ +#define CLOCKWISE_KNEE(prev_delta, next_delta) \ + ((prev_delta.dx == -1.0 && next_delta.dy == 1.0) \ + || (prev_delta.dy == 1.0 && next_delta.dx == 1.0) \ + || (prev_delta.dx == 1.0 && next_delta.dy == -1.0) \ + || (prev_delta.dy == -1.0 && next_delta.dx == -1.0)) - /* If we wrapped around in our search, we're done; - otherwise, we move on to the pixel after the highest - one we just checked. - */ - p = (q < p) ? O_LENGTH(outline) : q + 1; +#define COUNTERCLOCKWISE_KNEE(prev_delta, next_delta) \ + ((prev_delta.dy == 1.0 && next_delta.dx == -1.0) \ + || (prev_delta.dx == 1.0 && next_delta.dy == 1.0) \ + || (prev_delta.dy == -1.0 && next_delta.dx == 1.0) \ + || (prev_delta.dx == -1.0 && next_delta.dy == -1.0)) + + + +static void +remove_knee_points(curve_type const curve, + bool const clockwise) { + + unsigned const offset = (CURVE_CYCLIC(curve) == true) ? 0 : 1; + curve_type const trimmed_curve = copy_most_of_curve(curve); + + pm_pixelcoord previous; + unsigned i; + + if (!CURVE_CYCLIC(curve)) + append_pixel(trimmed_curve, + real_to_int_coord(CURVE_POINT(curve, 0))); + + previous = real_to_int_coord(CURVE_POINT(curve, + CURVE_PREV(curve, offset))); + + for (i = offset; i < CURVE_LENGTH (curve) - offset; ++i) { + pm_pixelcoord const current = + real_to_int_coord(CURVE_POINT(curve, i)); + pm_pixelcoord const next = + real_to_int_coord(CURVE_POINT(curve, CURVE_NEXT(curve, i))); + vector_type const prev_delta = IPsubtract(previous, current); + vector_type const next_delta = IPsubtract(next, current); + + if (ONLY_ONE_ZERO(prev_delta) && ONLY_ONE_ZERO(next_delta) + && ((clockwise && CLOCKWISE_KNEE(prev_delta, next_delta)) + || (!clockwise + && COUNTERCLOCKWISE_KNEE(prev_delta, next_delta)))) + LOG2(" (%d,%d)", current.col, current.row); + else { + previous = current; + append_pixel(trimmed_curve, current); + } + } + + if (!CURVE_CYCLIC(curve)) + append_pixel(trimmed_curve, + real_to_int_coord(LAST_CURVE_POINT(curve))); + + if (CURVE_LENGTH(trimmed_curve) == CURVE_LENGTH(curve)) + LOG(" (none)"); + + LOG(".\n"); + + free_curve(curve); + *curve = *trimmed_curve; + free(trimmed_curve); /* free_curve? --- Masatake */ +} + + + +/* Smooth the curve by adding in neighboring points. Do this + `filter_iterations' times. But don't change the corners. */ + +static void +filter(curve_type const curve, + fitting_opts_type * const fitting_opts) { + + unsigned iteration, this_point; + unsigned offset = (CURVE_CYCLIC (curve) == true) ? 0 : 1; + float_coord prev_new_point; + + /* We must have at least three points---the previous one, the current + one, and the next one. But if we don't have at least five, we will + probably collapse the curve down onto a single point, which means + we won't be able to fit it with a spline. + */ + if (CURVE_LENGTH (curve) < 5) { + LOG1 ("Length is %u, not enough to filter.\n", CURVE_LENGTH (curve)); + return; + } + + prev_new_point.x = FLT_MAX; + prev_new_point.y = FLT_MAX; + prev_new_point.z = FLT_MAX; + + for (iteration = 0; + iteration < fitting_opts->filter_iterations; + ++iteration) { + curve_type newcurve = copy_most_of_curve (curve); + bool collapsed = false; + + /* Keep the first point on the curve. */ + if (offset) + append_point (newcurve, CURVE_POINT (curve, 0)); + + for (this_point = offset; + this_point < CURVE_LENGTH (curve) - offset; + ++this_point) { + vector_type in, out, sum; + float_coord new_point; + + /* Calculate the vectors in and out, computed by looking + at n points on either side of this_point. Experimental + it was found that 2 is optimal. + */ + + signed int prev, prevprev; /* have to be signed */ + unsigned int next, nextnext; + float_coord candidate = CURVE_POINT (curve, this_point); + + prev = CURVE_PREV (curve, this_point); + prevprev = CURVE_PREV (curve, prev); + next = CURVE_NEXT (curve, this_point); + nextnext = CURVE_NEXT (curve, next); + + /* Add up the differences from p of the `surround' points + before p. + */ + in.dx = in.dy = in.dz = 0.0; + + in = Vadd (in, Psubtract (CURVE_POINT (curve, prev), candidate)); + if (prevprev >= 0) + in = Vadd(in, + Psubtract(CURVE_POINT (curve, prevprev), candidate)); + + /* And the points after p. Don't use more points after p than we + ended up with before it. + */ + out.dx = out.dy = out.dz = 0.0; + + out = Vadd (out, Psubtract (CURVE_POINT (curve, next), candidate)); + if (nextnext < CURVE_LENGTH (curve)) + out = Vadd(out, + Psubtract(CURVE_POINT (curve, nextnext), + candidate)); + + /* Start with the old point. */ + new_point = candidate; + sum = Vadd (in, out); + /* We added 2*n+2 points, so we have to divide the sum by 2*n+2 */ + new_point.x += sum.dx / 6; + new_point.y += sum.dy / 6; + new_point.z += sum.dz / 6; + if (fabs (prev_new_point.x - new_point.x) < 0.3 + && fabs (prev_new_point.y - new_point.y) < 0.3 + && fabs (prev_new_point.z - new_point.z) < 0.3) { + collapsed = true; + break; + } + + + /* Put the newly computed point into a separate curve, so it + doesn't affect future computation (on this iteration). + */ + append_point (newcurve, prev_new_point = new_point); + } + + if (collapsed) + free_curve (newcurve); + else { + /* Just as with the first point, we have to keep the last + point. + */ + if (offset) + append_point (newcurve, LAST_CURVE_POINT (curve)); + + /* Set the original curve to the newly filtered one, and go + again. + */ + free_curve (curve); + *curve = *newcurve; + } + free (newcurve); + } + log_curve (curve, false); +} + + + +static void +removeAdjacent(index_list_type * const cornerListP, + pixel_outline_type const outline, + fitting_opts_type * const fittingOptsP, + at_exception_type * const exception) { + + /* We never want two corners next to each other, since the + only way to fit such a ``curve'' would be with a straight + line, which usually interrupts the continuity dreadfully. + */ + + if (INDEX_LIST_LENGTH(*cornerListP) > 0) + remove_adjacent_corners( + cornerListP, + O_LENGTH(outline) - (outline.open ? 2 : 1), + fittingOptsP->remove_adjacent_corners, + exception); +} + + + +static index_list_type +find_corners(pixel_outline_type const outline, + fitting_opts_type * const fittingOptsP, + at_exception_type * const exceptionP) { + + /* We consider a point to be a corner if (1) the angle defined by + the `corner_surround' points coming into it and going out from + it is less than `corner_threshold' degrees, and no point within + `corner_surround' points has a smaller angle; or (2) the angle + is less than `corner_always_threshold' degrees. + */ + unsigned int p; + unsigned int firstPixelSeq, lastPixelSeq; + index_list_type cornerList; + + cornerList = new_index_list(); + + if (O_LENGTH(outline) <= fittingOptsP->corner_surround * 2 + 1) + return cornerList; + + establishCornerSearchLimits(outline, fittingOptsP, + &firstPixelSeq, &lastPixelSeq); + + /* Consider each pixel on the outline in turn. */ + for (p = firstPixelSeq; p <= lastPixelSeq;) { + vector_type inVector, outVector; + float cornerAngle; + + /* Check if the angle is small enough. */ + find_vectors(p, outline, &inVector, &outVector, + fittingOptsP->corner_surround); + cornerAngle = Vangle(inVector, outVector, exceptionP); + if (at_exception_got_fatal(exceptionP)) + goto cleanup; + + if (fabs(cornerAngle) <= fittingOptsP->corner_threshold) { + /* We want to keep looking, instead of just appending the + first pixel we find with a small enough angle, since there + might be another corner within `corner_surround' pixels, with + a smaller angle. If that is the case, we want that one. + + If we come across a corner that is just as good as the + best one, we should make it a corner, too. This + happens, for example, at the points on the `W' in some + typefaces, where the "points" are flat. + */ + float bestCornerAngle; + unsigned bestCornerIndex; + index_list_type equallyGoodList; + unsigned int q; + + if (cornerAngle <= fittingOptsP->corner_always_threshold) + /* The angle is sufficiently small that we want to + consider this a corner, even if it's not the best. + */ + appendCorner(&cornerList, p, outline, cornerAngle, '\\'); + + lookAheadForBetterCorner(outline, p, cornerAngle, + fittingOptsP->corner_surround, + fittingOptsP->corner_always_threshold, + &q, + &bestCornerAngle, &bestCornerIndex, + &equallyGoodList, + &cornerList, + exceptionP); + + if (at_exception_got_fatal(exceptionP)) + goto cleanup; + + /* `q' is the index of the last point lookAhead checked. + He added the corner if `bestCornerAngle' is less than + `corner_always_threshold'. If we've wrapped around, we + added the corner on the first pass. Otherwise, we add + the corner now. + */ + if (bestCornerAngle > fittingOptsP->corner_always_threshold + && bestCornerIndex >= p) { + + unsigned int j; + + appendCorner(&cornerList, bestCornerIndex, + outline, bestCornerAngle, '/'); + + for (j = 0; j < INDEX_LIST_LENGTH (equallyGoodList); ++j) + appendCorner(&cornerList, GET_INDEX(equallyGoodList, j), + outline, bestCornerAngle, '@'); + } + free_index_list(&equallyGoodList); + + /* If we wrapped around in our search, we're done; + otherwise, we move on to the pixel after the highest + one we just checked. + */ + p = (q < p) ? O_LENGTH(outline) : q + 1; } else ++p; } @@ -951,47 +1300,251 @@ logSplineFit(spline_type const spline) { -static spline_list_type * -fit_with_least_squares(curve_type const curve, - const fitting_opts_type * const fitting_opts, - at_exception_type * const exception) { +static vector_type +find_half_tangent(curve_type const c, + bool const to_start_point, + unsigned int * const n_points, + unsigned int const tangent_surround) { /*---------------------------------------------------------------------------- - The least squares method is well described in Schneider's thesis. - Briefly, we try to fit the entire curve with one spline. If that - fails, we subdivide the curve. + Find the change in y and change in x for `tangent_surround' (a global) + points along CURVE. Increment N_POINTS by the number of points we + actually look at. -----------------------------------------------------------------------------*/ - float error; - float best_error; - spline_type spline; - spline_type best_spline; - spline_list_type * spline_list; - unsigned int worst_point; - float previous_error; + unsigned int p; + int factor = to_start_point ? 1 : -1; + unsigned tangent_index = to_start_point ? 0 : c->length - 1; + float_coord tangent_point = CURVE_POINT (c, tangent_index); + vector_type tangent = { 0.0, 0.0 }; + unsigned int surround; + + if ((surround = CURVE_LENGTH(c) / 2) > tangent_surround) + surround = tangent_surround; - best_error = FLT_MAX; /* initial value */ - previous_error = FLT_MAX; /* initial value */ - spline_list = NULL; /* initial value */ - worst_point = 0; /* initial value */ + for (p = 1; p <= surround; ++p) { + int const this_index = p * factor + tangent_index; + float_coord this_point; + + if (this_index < 0 || this_index >= (int) c->length) + break; - LOG ("\nFitting with least squares:\n"); - - /* Phoenix reduces the number of points with a ``linear spline - technique''. But for fitting letterforms, that is - inappropriate. We want all the points we can get. - */ - - /* It makes no difference whether we first set the `t' values or - find the tangents. This order makes the documentation a little - more coherent. - */ + this_point = CURVE_POINT(c, p * factor + tangent_index); - LOG("Finding tangents:\n"); - find_tangent(curve, /* to_start */ true, /* cross_curve */ false, - fitting_opts->tangent_surround); - find_tangent(curve, /* to_start */ false, /* cross_curve */ false, - fitting_opts->tangent_surround); + /* Perhaps we should weight the tangent from `this_point' by some + factor dependent on the distance from the tangent point. + */ + tangent = Vadd (tangent, + Vmult_scalar(Psubtract(this_point, tangent_point), + (float) factor)); + ++*n_points; + } - set_initial_parameter_values(curve); + return tangent; +} + + + +static void +find_tangent(curve_type const curve, + bool const to_start_point, + bool const cross_curve, + unsigned int const tangent_surround_arg) { +/*---------------------------------------------------------------------------- + Find an approximation to the tangent to an endpoint of CURVE (to the + first point if TO_START_POINT is true, else the last). If + CROSS_CURVE is true, consider points on the adjacent curve to CURVE. + + It is important to compute an accurate approximation, because the + control points that we eventually decide upon to fit the curve will + be placed on the half-lines defined by the tangents and + endpoints...and we never recompute the tangent after this. +-----------------------------------------------------------------------------*/ + vector_type ** curve_tangent; + vector_type tangent; + unsigned n_points; + + LOG1(" tangent to %s: ", (to_start_point == true) ? "start" : "end"); + + n_points = 0; /* initial value */ + + curve_tangent = to_start_point ? + &(CURVE_START_TANGENT(curve)) : &(CURVE_END_TANGENT(curve)); + + if (*curve_tangent == NULL) { + unsigned int tangent_surround; + + tangent_surround = tangent_surround_arg; /* initial value */ + MALLOCVAR_NOFAIL(*curve_tangent); + do { + tangent = find_half_tangent(curve, to_start_point, &n_points, + tangent_surround); + + if (cross_curve || CURVE_CYCLIC(curve)) { + curve_type const adjacent_curve = to_start_point ? + PREVIOUS_CURVE(curve) : NEXT_CURVE(curve); + vector_type const tangent2 = !to_start_point ? + find_half_tangent(adjacent_curve, true, &n_points, + tangent_surround) : + find_half_tangent(adjacent_curve, true, &n_points, + tangent_surround); + + LOG3("(adjacent curve half tangent (%.3f,%.3f,%.3f)) ", + tangent2.dx, tangent2.dy, tangent2.dz); + tangent = Vadd (tangent, tangent2); + } + --tangent_surround; + } while (tangent.dx == 0.0 && tangent.dy == 0.0); + + assert(n_points > 0); + **curve_tangent = Vmult_scalar (tangent, (float)(1.0 / n_points)); + if (CURVE_CYCLIC(curve) && CURVE_START_TANGENT(curve)) + *CURVE_START_TANGENT(curve) = **curve_tangent; + if (CURVE_CYCLIC(curve) && CURVE_END_TANGENT(curve)) + *CURVE_END_TANGENT(curve) = **curve_tangent; + } else + LOG("(already computed) "); + + LOG3("(%.3f,%.3f,%.3f).\n", (*curve_tangent)->dx, + (*curve_tangent)->dy, (*curve_tangent)->dz); +} + + + +static float +find_error(curve_type const curve, + spline_type const spline, + unsigned int * const worst_point, + at_exception_type * const exception) { +/*---------------------------------------------------------------------------- + When this routine is called, we have computed a spline representation + for the digitized curve. The question is, how good is it? If the + fit is very good indeed, we might have an error of zero on each + point, and then WORST_POINT becomes irrelevant. But normally, we + return the error at the worst point, and the index of that point in + WORST_POINT. The error computation itself is the Euclidean distance + from the original curve CURVE to the fitted spline SPLINE. +-----------------------------------------------------------------------------*/ + unsigned int this_point; + float total_error; + float worst_error; + + total_error = 0.0; /* initial value */ + worst_error = FLT_MIN; /* initial value */ + + *worst_point = CURVE_LENGTH(curve) + 1; /* A sentinel value. */ + + for (this_point = 0; this_point < CURVE_LENGTH(curve); ++this_point) { + float_coord const curve_point = CURVE_POINT(curve, this_point); + float const t = CURVE_T(curve, this_point); + float_coord const spline_point = evaluate_spline(spline, t); + float const this_error = distance(curve_point, spline_point); + if (this_error >= worst_error) { + *worst_point = this_point; + worst_error = this_error; + } + total_error += this_error; + } + + if (*worst_point == CURVE_LENGTH(curve) + 1) { + /* Didn't have any ``worst point''; the error should be zero. */ + if (epsilon_equal(total_error, 0.0)) + LOG (" Every point fit perfectly.\n"); + else { + LOG("No worst point found; something is wrong"); + at_exception_warning(exception, + "No worst point found; something is wrong"); + } + } else { + if (epsilon_equal(total_error, 0.0)) + LOG (" Every point fit perfectly.\n"); + else { + LOG5(" Worst error (at (%.3f,%.3f,%.3f), point #%u) was %.3f.\n", + CURVE_POINT(curve, *worst_point).x, + CURVE_POINT(curve, *worst_point).y, + CURVE_POINT(curve, *worst_point).z, + *worst_point, worst_error); + LOG1(" Total error was %.3f.\n", total_error); + LOG2(" Average error (over %u points) was %.3f.\n", + CURVE_LENGTH(curve), total_error / CURVE_LENGTH(curve)); + } + } + return worst_error; +} + + + +static void +set_initial_parameter_values(curve_type const curve) { +/*---------------------------------------------------------------------------- + Find reasonable values for t for each point on CURVE. The method is + called chord-length parameterization, which is described in Plass & + Stone. The basic idea is just to use the distance from one point to + the next as the t value, normalized to produce values that increase + from zero for the first point to one for the last point. +-----------------------------------------------------------------------------*/ + unsigned int p; + + LOG("\nAssigning initial t values:\n "); + + CURVE_T(curve, 0) = 0.0; + + for (p = 1; p < CURVE_LENGTH (curve); ++p) { + float_coord const point = CURVE_POINT(curve, p); + float_coord const previous_p = CURVE_POINT(curve, p - 1); + float const d = distance (point, previous_p); + CURVE_T (curve, p) = CURVE_T (curve, p - 1) + d; + } + + assert(LAST_CURVE_T (curve) != 0.0); + + for (p = 1; p < CURVE_LENGTH(curve); ++p) + CURVE_T(curve, p) = CURVE_T(curve, p) / LAST_CURVE_T(curve); + + log_entire_curve(curve); +} + + + +static spline_list_type * +fit_with_least_squares(curve_type const curve, + const fitting_opts_type * const fitting_opts, + at_exception_type * const exception) { +/*---------------------------------------------------------------------------- + The least squares method is well described in Schneider's thesis. + Briefly, we try to fit the entire curve with one spline. If that + fails, we subdivide the curve. +-----------------------------------------------------------------------------*/ + float error; + float best_error; + spline_type spline; + spline_type best_spline; + spline_list_type * spline_list; + unsigned int worst_point; + float previous_error; + + best_error = FLT_MAX; /* initial value */ + previous_error = FLT_MAX; /* initial value */ + spline_list = NULL; /* initial value */ + worst_point = 0; /* initial value */ + + LOG ("\nFitting with least squares:\n"); + + /* Phoenix reduces the number of points with a ``linear spline + technique''. But for fitting letterforms, that is + inappropriate. We want all the points we can get. + */ + + /* It makes no difference whether we first set the `t' values or + find the tangents. This order makes the documentation a little + more coherent. + */ + + LOG("Finding tangents:\n"); + find_tangent(curve, /* to_start */ true, /* cross_curve */ false, + fitting_opts->tangent_surround); + find_tangent(curve, /* to_start */ false, /* cross_curve */ false, + fitting_opts->tangent_surround); + + set_initial_parameter_values(curve); /* Now we loop, subdividing, until CURVE has been fit. */ while (true) { @@ -1377,547 +1930,3 @@ fit_outlines_to_splines(pixel_outline_list_type const pixelOutlineList, - -static void -find_vectors(unsigned int const test_index, - pixel_outline_type const outline, - vector_type * const in, - vector_type * const out, - unsigned int const corner_surround) { -/*---------------------------------------------------------------------------- - Return the difference vectors coming in and going out of the outline - OUTLINE at the point whose index is TEST_INDEX. In Phoenix, - Schneider looks at a single point on either side of the point we're - considering. That works for him because his points are not touching. - But our points *are* touching, and so we have to look at - `corner_surround' points on either side, to get a better picture of - the outline's shape. ------------------------------------------------------------------------------*/ - int i; - unsigned n_done; - pm_pixelcoord const candidate = O_COORDINATE(outline, test_index); - - in->dx = in->dy = in->dz = 0.0; - out->dx = out->dy = out->dz = 0.0; - - /* Add up the differences from p of the `corner_surround' points - before p. - */ - for (i = O_PREV(outline, test_index), n_done = 0; - n_done < corner_surround; - i = O_PREV(outline, i), ++n_done) - *in = Vadd(*in, IPsubtract(O_COORDINATE(outline, i), candidate)); - - /* And the points after p. */ - for (i = O_NEXT (outline, test_index), n_done = 0; - n_done < corner_surround; - i = O_NEXT(outline, i), ++n_done) - *out = Vadd(*out, IPsubtract(O_COORDINATE(outline, i), candidate)); -} - - - -/* Remove adjacent points from the index list LIST. We do this by first - sorting the list and then running through it. Since these lists are - quite short, a straight selection sort (e.g., p.139 of the Art of - Computer Programming, vol.3) is good enough. LAST_INDEX is the index - of the last pixel on the outline, i.e., the next one is the first - pixel. We need this for checking the adjacency of the last corner. - - We need to do this because the adjacent corners turn into - two-pixel-long curves, which can only be fit by straight lines. */ - -static void -remove_adjacent_corners (index_list_type *list, unsigned last_index, - bool remove_adj_corners, - at_exception_type * exception) - -{ - unsigned j; - unsigned last; - index_list_type new_list = new_index_list (); - - for (j = INDEX_LIST_LENGTH (*list) - 1; j > 0; j--) - { - unsigned search; - unsigned temp; - /* Find maximal element below `j'. */ - unsigned max_index = j; - - for (search = 0; search < j; search++) - if (GET_INDEX (*list, search) > GET_INDEX (*list, max_index)) - max_index = search; - - if (max_index != j) - { - temp = GET_INDEX (*list, j); - GET_INDEX (*list, j) = GET_INDEX (*list, max_index); - GET_INDEX (*list, max_index) = temp; - - /* xx -- really have to sort? */ - LOG ("needed exchange"); - at_exception_warning(exception, "needed exchange"); - } - } - - /* The list is sorted. Now look for adjacent entries. Each time - through the loop we insert the current entry and, if appropriate, - the next entry. */ - for (j = 0; j < INDEX_LIST_LENGTH (*list) - 1; j++) - { - unsigned current = GET_INDEX (*list, j); - unsigned next = GET_INDEX (*list, j + 1); - - /* We should never have inserted the same element twice. */ - /* assert (current != next); */ - - if ((remove_adj_corners) && ((next == current + 1) || (next == current))) - j++; - - append_index (&new_list, current); - } - - /* Don't append the last element if it is 1) adjacent to the previous - one; or 2) adjacent to the very first one. */ - last = GET_LAST_INDEX (*list); - if (INDEX_LIST_LENGTH (new_list) == 0 - || !(last == GET_LAST_INDEX (new_list) + 1 - || (last == last_index && GET_INDEX (*list, 0) == 0))) - append_index (&new_list, last); - - free_index_list (list); - *list = new_list; -} - -/* A ``knee'' is a point which forms a ``right angle'' with its - predecessor and successor. See the documentation (the `Removing - knees' section) for an example and more details. - - The argument CLOCKWISE tells us which direction we're moving. (We - can't figure that information out from just the single segment with - which we are given to work.) - - We should never find two consecutive knees. - - Since the first and last points are corners (unless the curve is - cyclic), it doesn't make sense to remove those. */ - -/* This evaluates to true if the vector V is zero in one direction and - nonzero in the other. */ -#define ONLY_ONE_ZERO(v) \ - (((v).dx == 0.0 && (v).dy != 0.0) || ((v).dy == 0.0 && (v).dx != 0.0)) - -/* There are four possible cases for knees, one for each of the four - corners of a rectangle; and then the cases differ depending on which - direction we are going around the curve. The tests are listed here - in the order of upper left, upper right, lower right, lower left. - Perhaps there is some simple pattern to the - clockwise/counterclockwise differences, but I don't see one. */ -#define CLOCKWISE_KNEE(prev_delta, next_delta) \ - ((prev_delta.dx == -1.0 && next_delta.dy == 1.0) \ - || (prev_delta.dy == 1.0 && next_delta.dx == 1.0) \ - || (prev_delta.dx == 1.0 && next_delta.dy == -1.0) \ - || (prev_delta.dy == -1.0 && next_delta.dx == -1.0)) - -#define COUNTERCLOCKWISE_KNEE(prev_delta, next_delta) \ - ((prev_delta.dy == 1.0 && next_delta.dx == -1.0) \ - || (prev_delta.dx == 1.0 && next_delta.dy == 1.0) \ - || (prev_delta.dy == -1.0 && next_delta.dx == 1.0) \ - || (prev_delta.dx == -1.0 && next_delta.dy == -1.0)) - - - -static void -remove_knee_points(curve_type const curve, - bool const clockwise) { - - unsigned const offset = (CURVE_CYCLIC(curve) == true) ? 0 : 1; - curve_type const trimmed_curve = copy_most_of_curve(curve); - - pm_pixelcoord previous; - unsigned i; - - if (!CURVE_CYCLIC(curve)) - append_pixel(trimmed_curve, - real_to_int_coord(CURVE_POINT(curve, 0))); - - previous = real_to_int_coord(CURVE_POINT(curve, - CURVE_PREV(curve, offset))); - - for (i = offset; i < CURVE_LENGTH (curve) - offset; ++i) { - pm_pixelcoord const current = - real_to_int_coord(CURVE_POINT(curve, i)); - pm_pixelcoord const next = - real_to_int_coord(CURVE_POINT(curve, CURVE_NEXT(curve, i))); - vector_type const prev_delta = IPsubtract(previous, current); - vector_type const next_delta = IPsubtract(next, current); - - if (ONLY_ONE_ZERO(prev_delta) && ONLY_ONE_ZERO(next_delta) - && ((clockwise && CLOCKWISE_KNEE(prev_delta, next_delta)) - || (!clockwise - && COUNTERCLOCKWISE_KNEE(prev_delta, next_delta)))) - LOG2(" (%d,%d)", current.col, current.row); - else { - previous = current; - append_pixel(trimmed_curve, current); - } - } - - if (!CURVE_CYCLIC(curve)) - append_pixel(trimmed_curve, - real_to_int_coord(LAST_CURVE_POINT(curve))); - - if (CURVE_LENGTH(trimmed_curve) == CURVE_LENGTH(curve)) - LOG(" (none)"); - - LOG(".\n"); - - free_curve(curve); - *curve = *trimmed_curve; - free(trimmed_curve); /* free_curve? --- Masatake */ -} - - - -/* Smooth the curve by adding in neighboring points. Do this - `filter_iterations' times. But don't change the corners. */ - -static void -filter (curve_type curve, fitting_opts_type *fitting_opts) -{ - unsigned iteration, this_point; - unsigned offset = (CURVE_CYCLIC (curve) == true) ? 0 : 1; - float_coord prev_new_point; - - /* We must have at least three points---the previous one, the current - one, and the next one. But if we don't have at least five, we will - probably collapse the curve down onto a single point, which means - we won't be able to fit it with a spline. */ - if (CURVE_LENGTH (curve) < 5) - { - LOG1 ("Length is %u, not enough to filter.\n", CURVE_LENGTH (curve)); - return; - } - - prev_new_point.x = FLT_MAX; - prev_new_point.y = FLT_MAX; - prev_new_point.z = FLT_MAX; - - for (iteration = 0; iteration < fitting_opts->filter_iterations; - iteration++) - { - curve_type newcurve = copy_most_of_curve (curve); - bool collapsed = false; - - /* Keep the first point on the curve. */ - if (offset) - append_point (newcurve, CURVE_POINT (curve, 0)); - - for (this_point = offset; this_point < CURVE_LENGTH (curve) - offset; - this_point++) - { - vector_type in, out, sum; - float_coord new_point; - - /* Calculate the vectors in and out, computed by looking at n points - on either side of this_point. Experimental it was found that 2 is - optimal. */ - - signed int prev, prevprev; /* have to be signed */ - unsigned int next, nextnext; - float_coord candidate = CURVE_POINT (curve, this_point); - - prev = CURVE_PREV (curve, this_point); - prevprev = CURVE_PREV (curve, prev); - next = CURVE_NEXT (curve, this_point); - nextnext = CURVE_NEXT (curve, next); - - /* Add up the differences from p of the `surround' points - before p. */ - in.dx = in.dy = in.dz = 0.0; - - in = Vadd (in, Psubtract (CURVE_POINT (curve, prev), candidate)); - if (prevprev >= 0) - in = Vadd (in, Psubtract (CURVE_POINT (curve, prevprev), candidate)); - - /* And the points after p. Don't use more points after p than we - ended up with before it. */ - out.dx = out.dy = out.dz = 0.0; - - out = Vadd (out, Psubtract (CURVE_POINT (curve, next), candidate)); - if (nextnext < CURVE_LENGTH (curve)) - out = Vadd (out, Psubtract (CURVE_POINT (curve, nextnext), candidate)); - - /* Start with the old point. */ - new_point = candidate; - sum = Vadd (in, out); - /* We added 2*n+2 points, so we have to divide the sum by 2*n+2 */ - new_point.x += sum.dx / 6; - new_point.y += sum.dy / 6; - new_point.z += sum.dz / 6; - if (fabs (prev_new_point.x - new_point.x) < 0.3 - && fabs (prev_new_point.y - new_point.y) < 0.3 - && fabs (prev_new_point.z - new_point.z) < 0.3) - { - collapsed = true; - break; - } - - - /* Put the newly computed point into a separate curve, so it - doesn't affect future computation (on this iteration). */ - append_point (newcurve, prev_new_point = new_point); - } - - if (collapsed) - free_curve (newcurve); - else - { - /* Just as with the first point, we have to keep the last point. */ - if (offset) - append_point (newcurve, LAST_CURVE_POINT (curve)); - - /* Set the original curve to the newly filtered one, and go again. */ - free_curve (curve); - *curve = *newcurve; - } - free (newcurve); - } - - log_curve (curve, false); -} - - - -/* Find reasonable values for t for each point on CURVE. The method is - called chord-length parameterization, which is described in Plass & - Stone. The basic idea is just to use the distance from one point to - the next as the t value, normalized to produce values that increase - from zero for the first point to one for the last point. */ - -static void -set_initial_parameter_values (curve_type curve) -{ - unsigned p; - - LOG ("\nAssigning initial t values:\n "); - - CURVE_T (curve, 0) = 0.0; - - for (p = 1; p < CURVE_LENGTH (curve); p++) - { - float_coord point = CURVE_POINT (curve, p), - previous_p = CURVE_POINT (curve, p - 1); - float d = distance (point, previous_p); - CURVE_T (curve, p) = CURVE_T (curve, p - 1) + d; - } - - assert (LAST_CURVE_T (curve) != 0.0); - - for (p = 1; p < CURVE_LENGTH (curve); p++) - CURVE_T (curve, p) = CURVE_T (curve, p) / LAST_CURVE_T (curve); - - log_entire_curve (curve); -} - -/* Find an approximation to the tangent to an endpoint of CURVE (to the - first point if TO_START_POINT is true, else the last). If - CROSS_CURVE is true, consider points on the adjacent curve to CURVE. - - It is important to compute an accurate approximation, because the - control points that we eventually decide upon to fit the curve will - be placed on the half-lines defined by the tangents and - endpoints...and we never recompute the tangent after this. */ - -static void -find_tangent (curve_type curve, bool to_start_point, bool cross_curve, - unsigned tangent_surround) -{ - vector_type tangent; - vector_type **curve_tangent = (to_start_point == true) ? &(CURVE_START_TANGENT (curve)) - : &(CURVE_END_TANGENT (curve)); - unsigned n_points = 0; - - LOG1 (" tangent to %s: ", (to_start_point == true) ? "start" : "end"); - - if (*curve_tangent == NULL) - { - MALLOCVAR_NOFAIL(*curve_tangent); - do - { - tangent = find_half_tangent (curve, to_start_point, &n_points, - tangent_surround); - - if ((cross_curve == true) || (CURVE_CYCLIC (curve) == true)) - { - curve_type adjacent_curve - = (to_start_point == true) ? PREVIOUS_CURVE (curve) : NEXT_CURVE (curve); - vector_type tangent2 - = (to_start_point == false) ? find_half_tangent (adjacent_curve, true, &n_points, - tangent_surround) : find_half_tangent (adjacent_curve, true, &n_points, - tangent_surround); - - LOG3 ("(adjacent curve half tangent (%.3f,%.3f,%.3f)) ", - tangent2.dx, tangent2.dy, tangent2.dz); - tangent = Vadd (tangent, tangent2); - } - tangent_surround--; - - } - while (tangent.dx == 0.0 && tangent.dy == 0.0); - - assert (n_points > 0); - **curve_tangent = Vmult_scalar (tangent, (float)(1.0 / n_points)); - if ((CURVE_CYCLIC (curve) == true) && CURVE_START_TANGENT (curve)) - *CURVE_START_TANGENT (curve) = **curve_tangent; - if ((CURVE_CYCLIC (curve) == true) && CURVE_END_TANGENT (curve)) - *CURVE_END_TANGENT (curve) = **curve_tangent; - } - else - LOG ("(already computed) "); - - LOG3 ("(%.3f,%.3f,%.3f).\n", (*curve_tangent)->dx, (*curve_tangent)->dy, (*curve_tangent)->dz); -} - -/* Find the change in y and change in x for `tangent_surround' (a global) - points along CURVE. Increment N_POINTS by the number of points we - actually look at. */ - -static vector_type -find_half_tangent (curve_type c, bool to_start_point, unsigned *n_points, - unsigned tangent_surround) -{ - unsigned p; - int factor = to_start_point ? 1 : -1; - unsigned tangent_index = to_start_point ? 0 : c->length - 1; - float_coord tangent_point = CURVE_POINT (c, tangent_index); - vector_type tangent = { 0.0, 0.0 }; - unsigned int surround; - - if ((surround = CURVE_LENGTH (c) / 2) > tangent_surround) - surround = tangent_surround; - - for (p = 1; p <= surround; p++) - { - int this_index = p * factor + tangent_index; - float_coord this_point; - - if (this_index < 0 || this_index >= (int) c->length) - break; - - this_point = CURVE_POINT (c, p * factor + tangent_index); - - /* Perhaps we should weight the tangent from `this_point' by some - factor dependent on the distance from the tangent point. */ - tangent = Vadd (tangent, - Vmult_scalar (Psubtract (this_point, tangent_point), - (float) factor)); - (*n_points)++; - } - - return tangent; -} - -/* When this routine is called, we have computed a spline representation - for the digitized curve. The question is, how good is it? If the - fit is very good indeed, we might have an error of zero on each - point, and then WORST_POINT becomes irrelevant. But normally, we - return the error at the worst point, and the index of that point in - WORST_POINT. The error computation itself is the Euclidean distance - from the original curve CURVE to the fitted spline SPLINE. */ - -static float -find_error (curve_type curve, spline_type spline, unsigned *worst_point, - at_exception_type * exception) -{ - unsigned this_point; - float total_error = 0.0; - float worst_error = FLT_MIN; - - *worst_point = CURVE_LENGTH (curve) + 1; /* A sentinel value. */ - - for (this_point = 0; this_point < CURVE_LENGTH (curve); this_point++) - { - float_coord curve_point = CURVE_POINT (curve, this_point); - float t = CURVE_T (curve, this_point); - float_coord spline_point = evaluate_spline (spline, t); - float this_error = distance (curve_point, spline_point); - if (this_error >= worst_error) - { - *worst_point = this_point; - worst_error = this_error; - } - total_error += this_error; - } - - if (*worst_point == CURVE_LENGTH (curve) + 1) - { /* Didn't have any ``worst point''; the error should be zero. */ - if (epsilon_equal (total_error, 0.0)) - LOG (" Every point fit perfectly.\n"); - else - { - LOG("No worst point found; something is wrong"); - at_exception_warning(exception, "No worst point found; something is wrong"); - } - } - else - { - if (epsilon_equal (total_error, 0.0)) - LOG (" Every point fit perfectly.\n"); - else - { - LOG5 (" Worst error (at (%.3f,%.3f,%.3f), point #%u) was %.3f.\n", - CURVE_POINT (curve, *worst_point).x, - CURVE_POINT (curve, *worst_point).y, - CURVE_POINT (curve, *worst_point).z, *worst_point, worst_error); - LOG1 (" Total error was %.3f.\n", total_error); - LOG2 (" Average error (over %u points) was %.3f.\n", - CURVE_LENGTH (curve), total_error / CURVE_LENGTH (curve)); - } - } - - return worst_error; -} - - -/* Lists of array indices (well, that is what we use it for). */ - -static index_list_type -new_index_list (void) -{ - index_list_type index_list; - - index_list.data = NULL; - INDEX_LIST_LENGTH (index_list) = 0; - - return index_list; -} - -static void -free_index_list (index_list_type *index_list) -{ - if (INDEX_LIST_LENGTH (*index_list) > 0) - { - free (index_list->data); - index_list->data = NULL; - INDEX_LIST_LENGTH (*index_list) = 0; - } -} - -static void -append_index (index_list_type *list, unsigned new_index) -{ - INDEX_LIST_LENGTH (*list)++; - REALLOCARRAY_NOFAIL(list->data, INDEX_LIST_LENGTH(*list)); - list->data[INDEX_LIST_LENGTH (*list) - 1] = new_index; -} - - -/* Return the Euclidean distance between P1 and P2. */ - -static float -distance (float_coord p1, float_coord p2) -{ - float x = p1.x - p2.x, y = p1.y - p2.y, z = p1.z - p2.z; - return (float) sqrt (SQR(x) + SQR(y) + SQR(z)); -} diff --git a/converter/other/pgmtopbm.c b/converter/other/pgmtopbm.c index 98bdc332..36b39017 100644 --- a/converter/other/pgmtopbm.c +++ b/converter/other/pgmtopbm.c @@ -11,10 +11,11 @@ */ #include + +#include "shhopt.h" #include "pgm.h" #include "dithers.h" #include "mallocvar.h" -#include "shhopt.h" enum halftone {QT_FS, QT_THRESH, QT_DITHER8, QT_CLUSTER, QT_HILBERT}; @@ -445,7 +446,7 @@ createFsConverter(unsigned int const cols, /* Initialize Floyd-Steinberg error vectors. */ MALLOCARRAY_NOFAIL(stateP->thiserr, cols + 2); MALLOCARRAY_NOFAIL(stateP->nexterr, cols + 2); - srand((int)(time(NULL) ^ getpid())); + srand(pm_randseed()); { /* (random errors in [-fs_scale/8 .. fs_scale/8]) */ diff --git a/converter/other/pgmtoppm.c b/converter/other/pgmtoppm.c index 7693fe0a..cface024 100644 --- a/converter/other/pgmtoppm.c +++ b/converter/other/pgmtoppm.c @@ -11,146 +11,229 @@ */ #include + +#include "mallocvar.h" +#include "shhopt.h" #include "ppm.h" -int -main( argc, argv ) - int argc; - char* argv[]; - { - FILE* ifp; - gray* grayrow; - register gray* gP; - pixel p; - pixel* pixelrow; - register pixel* pP; - pixel** mappixels; - int argn, rows, cols, format, maprows, mapcols, mapmaxcolor, row; - register int col; - gray maxval; +struct cmdlineInfo { + /* All the information the user supplied in the command line, + in a form easy for the program to use. + */ + const char * inputFilename; /* '-' if stdin */ + const char * map; + const char * colorBlack; + const char * colorWhite; +}; + + + +static void +parseCommandLine(int argc, char ** argv, + struct cmdlineInfo * const cmdlineP) { +/*---------------------------------------------------------------------------- + parse program command line described in Unix standard form by argc + and argv. Return the information in the options as *cmdlineP. + + If command line is internally inconsistent (invalid options, etc.), + issue error message to stderr and abort program. + + Note that the strings we return are stored in the storage that + was passed to us as the argv array. We also trash *argv. +-----------------------------------------------------------------------------*/ + optEntry *option_def; + /* Instructions to optParseOptions3 on how to parse our options. + */ + optStruct3 opt; + + unsigned int option_def_index; + + unsigned int mapSpec; + + MALLOCARRAY_NOFAIL(option_def, 100); + + option_def_index = 0; /* incremented by OPTENT3 */ + OPTENT3(0, "map", OPT_STRING, &cmdlineP->map, + &mapSpec, 0); + + opt.opt_table = option_def; + opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ + opt.allowNegNum = FALSE; /* We have no parms that are negative numbers */ + + optParseOptions3(&argc, argv, opt, sizeof(opt), 0); + /* Uses and sets argc, argv, and some of *cmdlineP and others. */ + + if (!mapSpec) + cmdlineP->map = NULL; + + if (mapSpec) { + /* No color argument; only argument is file name */ + if (argc-1 < 1) + cmdlineP->inputFilename = "-"; + else { + cmdlineP->inputFilename = argv[1]; + if (argc-1 > 1) + pm_error("With -map option, there is at most one argument: " + "the file name. You specified %u", argc-1); + } + } else { + /* Arguments are color or color range and file name */ + if (argc-1 < 1) { + cmdlineP->colorBlack = "black"; + cmdlineP->colorWhite = "white"; + } else { + char * buffer = strdup(argv[1]); + char * hyphenPos = strchr(buffer, '-'); + if (hyphenPos) { + *hyphenPos = '\0'; + cmdlineP->colorBlack = buffer; + cmdlineP->colorWhite = hyphenPos+1; + } else { + cmdlineP->colorBlack = "black"; + cmdlineP->colorWhite = buffer; + } + } + if (argc-1 < 2) + cmdlineP->inputFilename = "-"; + else + cmdlineP->inputFilename = argv[2]; + + if (argc-1 > 2) + pm_error("Program takes at most 2 arguments: " + "color name/range and input file name. " + "You specified %u", argc-1); + } +} + + + +static void +convertWithMap(FILE * const ifP, + unsigned int const cols, + unsigned int const rows, + gray const maxval, + int const format, + const char * const mapFileName, + FILE * const ofP, + gray * const grayrow, + pixel * const pixelrow) { + + unsigned int row; + FILE * mapFileP; + int mapcols, maprows; pixval mapmaxval; - char* color0; - char* color1; + pixel ** mappixels; + unsigned int mapmaxcolor; + + mapFileP = pm_openr(mapFileName); + mappixels = ppm_readppm(mapFileP, &mapcols, &maprows, &mapmaxval); + pm_close(mapFileP); + mapmaxcolor = maprows * mapcols - 1; + + ppm_writeppminit(ofP, cols, rows, mapmaxval, 0); + + for (row = 0; row < rows; ++row) { + unsigned int col; + + pgm_readpgmrow(ifP, grayrow, cols, maxval, format); + + for (col = 0; col < cols; ++col) { + unsigned int c; + if (maxval == mapmaxcolor) + c = grayrow[col]; + else + c = grayrow[col] * mapmaxcolor / maxval; + pixelrow[col] = mappixels[c / mapcols][c % mapcols]; + } + ppm_writeppmrow(ofP, pixelrow, cols, mapmaxval, 0); + } + ppm_freearray(mappixels, maprows); +} + + + +static void +convertLinear(FILE * const ifP, + unsigned int const cols, + unsigned int const rows, + gray const maxval, + int const format, + const char * const colorNameBlack, + const char * const colorNameWhite, + FILE * const ofP, + gray * const grayrow, + pixel * const pixelrow) { + + pixel colorBlack, colorWhite; pixval red0, grn0, blu0, red1, grn1, blu1; - const char* const usage = " [pgmfile]\n , [pgmfile]\n -map mapfile [pgmfile]"; - - - ppm_init( &argc, argv ); - - argn = 1; - mappixels = (pixel**) 0; - - if ( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' ) - { - if ( pm_keymatch( argv[argn], "-map", 2 ) ) - { - ++argn; - if ( argn == argc ) - pm_usage( usage ); - ifp = pm_openr( argv[argn] ); - mappixels = ppm_readppm( ifp, &mapcols, &maprows, &mapmaxval ); - pm_close( ifp ); - mapmaxcolor = maprows * mapcols - 1; - } - else - pm_usage( usage ); - ++argn; - } - - if ( mappixels == (pixel**) 0 ) - { - if ( argn == argc ) - pm_usage( usage ); - color0 = argv[argn]; - ++argn; - } - - if ( argn != argc ) - { - ifp = pm_openr( argv[argn] ); - ++argn; - } - else - ifp = stdin; + unsigned int row; + + ppm_writeppminit(ofP, cols, rows, maxval, 0); + + colorBlack = ppm_parsecolor(colorNameBlack, maxval); + colorWhite = ppm_parsecolor(colorNameWhite, maxval); + + red0 = PPM_GETR(colorBlack); + grn0 = PPM_GETG(colorBlack); + blu0 = PPM_GETB(colorBlack); + red1 = PPM_GETR(colorWhite); + grn1 = PPM_GETG(colorWhite); + blu1 = PPM_GETB(colorWhite); + + for (row = 0; row < rows; ++row) { + unsigned int col; + + pgm_readpgmrow(ifP, grayrow, cols, maxval, format); + + for (col = 0; col < cols; ++col) { + gray const input = grayrow[col]; + PPM_ASSIGN( + pixelrow[col], + (red0 * (maxval - input) + red1 * input) / maxval, + (grn0 * (maxval - input) + grn1 * input) / maxval, + (blu0 * (maxval - input) + blu1 * input) / maxval); + } + ppm_writeppmrow(ofP, pixelrow, cols, maxval, 0); + } +} - if ( argn != argc ) - pm_usage( usage ); - pgm_readpgminit( ifp, &cols, &rows, &maxval, &format ); - grayrow = pgm_allocrow( cols ); - if ( mappixels == (pixel**) 0 ) - ppm_writeppminit( stdout, cols, rows, (pixval) maxval, 0 ); - else - ppm_writeppminit( stdout, cols, rows, mapmaxval, 0 ); - pixelrow = ppm_allocrow( cols ); - - if ( mappixels == (pixel**) 0 ) - { - color1 = strchr( color0, '-' ); - if ( color1 == 0 ) - { - color1 = color0; - red0 = 0; - grn0 = 0; - blu0 = 0; - } - else - { - *color1 = '\0'; - ++color1; - p = ppm_parsecolor( color0, (pixval) maxval ); - red0 = PPM_GETR( p ); - grn0 = PPM_GETG( p ); - blu0 = PPM_GETB( p ); - } - p = ppm_parsecolor( color1, (pixval) maxval ); - red1 = PPM_GETR( p ); - grn1 = PPM_GETG( p ); - blu1 = PPM_GETB( p ); - } - - for ( row = 0; row < rows; ++row ) - { - pgm_readpgmrow( ifp, grayrow, cols, maxval, format ); - - if ( mappixels == (pixel**) 0 ) - { - for ( col = 0, gP = grayrow, pP = pixelrow; - col < cols; - ++col, ++gP, ++pP ) - PPM_ASSIGN( - *pP, - ( red0 * ( maxval - *gP ) + red1 * *gP ) / maxval, - ( grn0 * ( maxval - *gP ) + grn1 * *gP ) / maxval, - ( blu0 * ( maxval - *gP ) + blu1 * *gP ) / maxval ); - - } - else - { - register int c; - - for ( col = 0, gP = grayrow, pP = pixelrow; - col < cols; - ++col, ++gP, ++pP ) - { - if ( maxval == mapmaxcolor ) - c = *gP; - else - c = *gP * mapmaxcolor / maxval; - *pP = mappixels[c / mapcols][c % mapcols]; - } - } - - if (!mappixels) - ppm_writeppmrow( stdout, pixelrow, cols, (pixval) maxval, 0 ); + +int +main(int argc, + char * argv[]) { + + FILE * ifP; + struct cmdlineInfo cmdline; + gray * grayrow; + pixel * pixelrow; + int rows, cols, format; + gray maxval; + + ppm_init(&argc, argv); + + parseCommandLine(argc, argv, &cmdline); + + ifP = pm_openr(cmdline.inputFilename); + + pgm_readpgminit(ifP, &cols, &rows, &maxval, &format); + grayrow = pgm_allocrow(cols); + pixelrow = ppm_allocrow(cols); + + if (cmdline.map) + convertWithMap(ifP, cols, rows, maxval, format, cmdline.map, + stdout, grayrow, pixelrow); else - ppm_writeppmrow( stdout, pixelrow, cols, mapmaxval, 0 ); - } + convertLinear(ifP, cols, rows, maxval, format, + cmdline.colorBlack, cmdline.colorWhite, stdout, + grayrow, pixelrow); - pm_close( ifp ); + ppm_freerow(pixelrow); + pgm_freerow(grayrow); + pm_close(ifP); /* If the program failed, it previously aborted with nonzero completion code, via various function calls. */ return 0; - } +} diff --git a/converter/other/pngtopnm.c b/converter/other/pngtopnm.c index bb8afb8d..2b88bd1e 100644 --- a/converter/other/pngtopnm.c +++ b/converter/other/pngtopnm.c @@ -240,23 +240,12 @@ gamma_correct(png_uint_16 const v, -#ifdef __STDC__ static int iscolor (png_color c) -#else -static int iscolor (c) -png_color c; -#endif { return c.red != c.green || c.green != c.blue; } -#ifdef __STDC__ static void save_text (png_info *info_ptr, FILE *tfp) -#else -static void save_text (info_ptr, tfp) -png_info *info_ptr; -FILE *tfp; -#endif { int i, j, k; @@ -285,12 +274,7 @@ FILE *tfp; } } -#ifdef __STDC__ static void show_time (png_info *info_ptr) -#else -static void show_time (info_ptr) -png_info *info_ptr; -#endif { static const char * const month[] = { "", "January", "February", "March", "April", "May", "June", @@ -305,13 +289,7 @@ png_info *info_ptr; } } -#ifdef __STDC__ static void pngtopnm_error_handler (png_structp png_ptr, png_const_charp msg) -#else -static void pngtopnm_error_handler (png_ptr, msg) -png_structp png_ptr; -png_const_charp msg; -#endif { jmpbuf_wrapper *jmpbuf_ptr; diff --git a/converter/other/pnmtoddif.c b/converter/other/pnmtoddif.c index 65152865..3a910a7b 100644 --- a/converter/other/pnmtoddif.c +++ b/converter/other/pnmtoddif.c @@ -166,11 +166,7 @@ wr_int(unsigned char ** buffer, int val) } else { sign = val < 0 ? 0xff : 0x00; /* Sign bits */ length = 4; -#ifdef __STDC__ mask = 0xffu << 24; -#else - mask = 0xff << 24; -#endif while ((val & mask) == sign) { /* Find the smallest representation */ length--; mask >>= 8; diff --git a/converter/other/pnmtojpeg.c b/converter/other/pnmtojpeg.c index 9a247633..5a4c5511 100644 --- a/converter/other/pnmtojpeg.c +++ b/converter/other/pnmtojpeg.c @@ -330,23 +330,6 @@ parseCommandLine(const int argc, char ** argv, } -static void -compute_rescaling_array(JSAMPLE ** const rescale_p, const pixval maxval, - const struct jpeg_compress_struct cinfo); -static void -convert_scanlines(struct jpeg_compress_struct * const cinfo_p, FILE * const input_file, - const pixval maxval, const int input_fmt, - JSAMPLE xlate_table[]); - -static boolean read_quant_tables (j_compress_ptr cinfo, char * filename, - int scale_factor, boolean force_baseline); - -static boolean read_scan_script (j_compress_ptr cinfo, char * filename); - -static boolean set_quant_slots (j_compress_ptr cinfo, char *arg); - -static boolean set_sample_factors (j_compress_ptr cinfo, char *arg); - static void report_compressor(const struct jpeg_compress_struct cinfo) { @@ -423,118 +406,461 @@ setup_jpeg_density(struct jpeg_compress_struct * const cinfoP, -static void -setup_jpeg(struct jpeg_compress_struct * const cinfoP, - struct jpeg_error_mgr * const jerrP, - struct cmdlineInfo const cmdline, - int const width, - int const height, - pixval const maxval, - int const input_fmt, - FILE * const output_file) { - - int quality; - int q_scale_factor; - - /* Initialize the JPEG compression object with default error handling. */ - cinfoP->err = jpeg_std_error(jerrP); - jpeg_create_compress(cinfoP); +/*---------------------------------------------------------------------------- + The functions below here are essentially the file rdswitch.c from + the JPEG library. They perform the functions specifed by the following + pnmtojpeg options: - setup_jpeg_source_parameters(cinfoP, width, height, input_fmt); + -qtables file Read quantization tables from text file + -scans file Read scan script from text file + -qslots N[,N,...] Set component quantization table selectors + -sample HxV[,HxV,...] Set component sampling factors +-----------------------------------------------------------------------------*/ - jpeg_set_defaults(cinfoP); +static int +text_getc (FILE * file) +/* Read next char, skipping over any comments (# to end of line) */ +/* A comment/newline sequence is returned as a newline */ +{ + register int ch; + + ch = getc(file); + if (ch == '#') { + do { + ch = getc(file); + } while (ch != '\n' && ch != EOF); + } + return ch; +} - cinfoP->data_precision = BITS_IN_JSAMPLE; - /* we always rescale data to this */ - cinfoP->image_width = (unsigned int) width; - cinfoP->image_height = (unsigned int) height; - cinfoP->arith_code = cmdline.arith_code; - cinfoP->dct_method = cmdline.dct_method; - if (cmdline.trace_level == 0 && cmdline.verbose) - cinfoP->err->trace_level = 1; - else cinfoP->err->trace_level = cmdline.trace_level; - if (cmdline.grayscale) - jpeg_set_colorspace(cinfoP, JCS_GRAYSCALE); - else if (cmdline.rgb) - /* This is not legal if the input is not JCS_RGB too, i.e. it's PPM */ - jpeg_set_colorspace(cinfoP, JCS_RGB); - else - /* This default will be based on the in_color_space set above */ - jpeg_default_colorspace(cinfoP); - if (cmdline.max_memory_to_use != -1) - cinfoP->mem->max_memory_to_use = cmdline.max_memory_to_use; - cinfoP->optimize_coding = cmdline.optimize; - if (cmdline.quality == -1) { - quality = 75; - q_scale_factor = 100; - } else { - quality = cmdline.quality; - q_scale_factor = jpeg_quality_scaling(cmdline.quality); +static boolean +read_text_integer (FILE * file, long * result, int * termchar) +/* Read an unsigned decimal integer from a file, store it in result */ +/* Reads one trailing character after the integer; returns it in termchar */ +{ + register int ch; + register long val; + + /* Skip any leading whitespace, detect EOF */ + do { + ch = text_getc(file); + if (ch == EOF) { + *termchar = ch; + return FALSE; + } + } while (isspace(ch)); + + if (! isdigit(ch)) { + *termchar = ch; + return FALSE; } - if (cmdline.smoothing_factor != -1) - cinfoP->smoothing_factor = cmdline.smoothing_factor; - /* Set quantization tables for selected quality. */ - /* Some or all may be overridden if user specified --qtables. */ - jpeg_set_quality(cinfoP, quality, cmdline.force_baseline); - - if (cmdline.qtablefile != NULL) { - if (! read_quant_tables(cinfoP, cmdline.qtablefile, - q_scale_factor, cmdline.force_baseline)) - pm_error("Can't use quantization table file '%s'.", - cmdline.qtablefile); - } - - if (cmdline.qslots != NULL) { - if (! set_quant_slots(cinfoP, cmdline.qslots)) - pm_error("Bad quantization-table-selectors parameter string '%s'.", - cmdline.qslots); - } - - if (cmdline.sample != NULL) { - if (! set_sample_factors(cinfoP, cmdline.sample)) - pm_error("Bad sample-factors parameters string '%s'.", - cmdline.sample); + val = ch - '0'; + while ((ch = text_getc(file)) != EOF) { + if (! isdigit(ch)) + break; + val *= 10; + val += ch - '0'; } + *result = val; + *termchar = ch; + return TRUE; +} - if (cmdline.progressive) - jpeg_simple_progression(cinfoP); - if (cmdline.density_spec) - setup_jpeg_density(cinfoP, cmdline.density); +static boolean +read_scan_integer (FILE * file, long * result, int * termchar) +/* Variant of read_text_integer that always looks for a non-space termchar; + * this simplifies parsing of punctuation in scan scripts. + */ +{ + register int ch; - if (cmdline.scans != NULL) { - if (! read_scan_script(cinfoP, cmdline.scans)) { - pm_message("Error in scan script '%s'.", cmdline.scans); - } + if (! read_text_integer(file, result, termchar)) + return FALSE; + ch = *termchar; + while (ch != EOF && isspace(ch)) + ch = text_getc(file); + if (isdigit(ch)) { /* oops, put it back */ + if (ungetc(ch, file) == EOF) + return FALSE; + ch = ' '; + } else { + /* Any separators other than ';' and ':' are ignored; + * this allows user to insert commas, etc, if desired. + */ + if (ch != EOF && ch != ';' && ch != ':') + ch = ' '; } + *termchar = ch; + return TRUE; +} - /* Specify data destination for compression */ - jpeg_stdio_dest(cinfoP, output_file); - if (cmdline.verbose) report_compressor(*cinfoP); - /* Start compressor */ - jpeg_start_compress(cinfoP, TRUE); +static boolean +read_scan_script(j_compress_ptr const cinfo, + const char * const filename) { +/*---------------------------------------------------------------------------- + Read a scan script from the specified text file. + Each entry in the file defines one scan to be emitted. + Entries are separated by semicolons ';'. + An entry contains one to four component indexes, + optionally followed by a colon ':' and four progressive-JPEG parameters. + The component indexes denote which component(s) are to be transmitted + in the current scan. The first component has index 0. + Sequential JPEG is used if the progressive-JPEG parameters are omitted. + The file is free format text: any whitespace may appear between numbers + and the ':' and ';' punctuation marks. Also, other punctuation (such + as commas or dashes) can be placed between numbers if desired. + Comments preceded by '#' may be included in the file. + Note: we do very little validity checking here; + jcmaster.c will validate the script parameters. +-----------------------------------------------------------------------------*/ + FILE * fp; + unsigned int nscans; + unsigned int ncomps; + int termchar; + long val; +#define MAX_SCANS 100 /* quite arbitrary limit */ + jpeg_scan_info scans[MAX_SCANS]; -} + fp = fopen(filename, "r"); + if (fp == NULL) { + pm_message("Can't open scan definition file %s", filename); + return FALSE; + } + nscans = 0; + while (read_scan_integer(fp, &val, &termchar)) { + ++nscans; /* We got another scan */ + if (nscans > MAX_SCANS) { + pm_message("Too many scans defined in file %s", filename); + fclose(fp); + return FALSE; + } + scans[nscans-1].component_index[0] = (int) val; + ncomps = 1; + while (termchar == ' ') { + if (ncomps >= MAX_COMPS_IN_SCAN) { + pm_message("Too many components in one scan in file %s", + filename); + fclose(fp); + return FALSE; + } + if (! read_scan_integer(fp, &val, &termchar)) + goto bogus; + scans[nscans-1].component_index[ncomps] = (int) val; + ++ncomps; + } + scans[nscans-1].comps_in_scan = ncomps; + if (termchar == ':') { + if (! read_scan_integer(fp, &val, &termchar) || termchar != ' ') + goto bogus; + scans[nscans-1].Ss = (int) val; + if (! read_scan_integer(fp, &val, &termchar) || termchar != ' ') + goto bogus; + scans[nscans-1].Se = (int) val; + if (! read_scan_integer(fp, &val, &termchar) || termchar != ' ') + goto bogus; + scans[nscans-1].Ah = (int) val; + if (! read_scan_integer(fp, &val, &termchar)) + goto bogus; + scans[nscans-1].Al = (int) val; + } else { + /* set non-progressive parameters */ + scans[nscans-1].Ss = 0; + scans[nscans-1].Se = DCTSIZE2-1; + scans[nscans-1].Ah = 0; + scans[nscans-1].Al = 0; + } + if (termchar != ';' && termchar != EOF) { + bogus: + pm_message("Invalid scan entry format in file %s", filename); + fclose(fp); + return FALSE; + } + } + if (termchar != EOF) { + pm_message("Non-numeric data in file %s", filename); + fclose(fp); + return FALSE; + } -static void -write_exif_header(struct jpeg_compress_struct * const cinfoP, - const char * const exif_filespec) { -/*---------------------------------------------------------------------------- - Generate an APP1 marker in the JFIF output that is an Exif header. + if (nscans > 0) { + /* Stash completed scan list in cinfo structure. NOTE: in + this program, JPOOL_IMAGE is the right lifetime for this + data, but if you want to compress multiple images you'd + want JPOOL_PERMANENT. + */ + const unsigned int scan_info_size = nscans * sizeof(jpeg_scan_info); + jpeg_scan_info * const scan_info = + (jpeg_scan_info *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + scan_info_size); + memcpy(scan_info, scans, scan_info_size); + cinfo->scan_info = scan_info; + cinfo->num_scans = nscans; + } - The contents of the Exif header are in the file with filespec - 'exif_filespec' (file spec and contents are not validated). + fclose(fp); + return TRUE; +} - exif_filespec = "-" means Standard Input. - If the file contains just two bytes of zero, don't write any marker - but don't recognize any error either. + +static boolean +read_quant_tables (j_compress_ptr cinfo, char * filename, + int scale_factor, boolean force_baseline) +/* Read a set of quantization tables from the specified file. + * The file is plain ASCII text: decimal numbers with whitespace between. + * Comments preceded by '#' may be included in the file. + * There may be one to NUM_QUANT_TBLS tables in the file, each of 64 values. + * The tables are implicitly numbered 0,1,etc. + * NOTE: does not affect the qslots mapping, which will default to selecting + * table 0 for luminance (or primary) components, 1 for chrominance components. + * You must use -qslots if you want a different component->table mapping. + */ +{ + FILE * fp; + int tblno, i, termchar; + long val; + unsigned int table[DCTSIZE2]; + + if ((fp = fopen(filename, "rb")) == NULL) { + pm_message("Can't open table file %s", filename); + return FALSE; + } + tblno = 0; + + while (read_text_integer(fp, &val, &termchar)) { + /* read 1st element of table */ + if (tblno >= NUM_QUANT_TBLS) { + pm_message("Too many tables in file %s", filename); + fclose(fp); + return FALSE; + } + table[0] = (unsigned int) val; + for (i = 1; i < DCTSIZE2; i++) { + if (! read_text_integer(fp, &val, &termchar)) { + pm_message("Invalid table data in file %s", filename); + fclose(fp); + return FALSE; + } + table[i] = (unsigned int) val; + } + jpeg_add_quant_table(cinfo, tblno, table, scale_factor, + force_baseline); + tblno++; + } + + if (termchar != EOF) { + pm_message("Non-numeric data in file %s", filename); + fclose(fp); + return FALSE; + } + + fclose(fp); + return TRUE; +} + + + +static boolean +set_quant_slots (j_compress_ptr cinfo, char *arg) +/* Process a quantization-table-selectors parameter string, of the form + * N[,N,...] + * If there are more components than parameters, the last value is replicated. + */ +{ + int val = 0; /* default table # */ + int ci; + char ch; + + for (ci = 0; ci < MAX_COMPONENTS; ci++) { + if (*arg) { + ch = ','; /* if not set by sscanf, will be ',' */ + if (sscanf(arg, "%d%c", &val, &ch) < 1) + return FALSE; + if (ch != ',') /* syntax check */ + return FALSE; + if (val < 0 || val >= NUM_QUANT_TBLS) { + pm_message("Invalid quantization table number: %d. " + "JPEG quantization tables are numbered 0..%d", + val, NUM_QUANT_TBLS - 1); + return FALSE; + } + cinfo->comp_info[ci].quant_tbl_no = val; + while (*arg && *arg++ != ',') + /* advance to next segment of arg string */ + ; + } else { + /* reached end of parameter, set remaining components to last tbl*/ + cinfo->comp_info[ci].quant_tbl_no = val; + } + } + return TRUE; +} + + +static boolean +set_sample_factors (j_compress_ptr cinfo, char *arg) +/* Process a sample-factors parameter string, of the form + * HxV[,HxV,...] + * If there are more components than parameters, "1x1" is assumed for the rest. + */ +{ + int ci, val1, val2; + char ch1, ch2; + + for (ci = 0; ci < MAX_COMPONENTS; ci++) { + if (*arg) { + ch2 = ','; /* if not set by sscanf, will be ',' */ + if (sscanf(arg, "%d%c%d%c", &val1, &ch1, &val2, &ch2) < 3) + return FALSE; + if ((ch1 != 'x' && ch1 != 'X') || ch2 != ',') /* syntax check */ + return FALSE; + if (val1 <= 0 || val1 > 4) { + pm_message("Invalid sampling factor: %d. " + "JPEG sampling factors must be 1..4", val1); + return FALSE; + } + if (val2 <= 0 || val2 > 4) { + pm_message("Invalid sampling factor: %d. " + "JPEG sampling factors must be 1..4", val2); + return FALSE; + } + cinfo->comp_info[ci].h_samp_factor = val1; + cinfo->comp_info[ci].v_samp_factor = val2; + while (*arg && *arg++ != ',') + /* advance to next segment of arg string */ + ; + } else { + /* reached end of parameter, set remaining components + to 1x1 sampling */ + cinfo->comp_info[ci].h_samp_factor = 1; + cinfo->comp_info[ci].v_samp_factor = 1; + } + } + return TRUE; +} + + + +static void +setup_jpeg(struct jpeg_compress_struct * const cinfoP, + struct jpeg_error_mgr * const jerrP, + struct cmdlineInfo const cmdline, + int const width, + int const height, + pixval const maxval, + int const input_fmt, + FILE * const output_file) { + + int quality; + int q_scale_factor; + + /* Initialize the JPEG compression object with default error handling. */ + cinfoP->err = jpeg_std_error(jerrP); + jpeg_create_compress(cinfoP); + + setup_jpeg_source_parameters(cinfoP, width, height, input_fmt); + + jpeg_set_defaults(cinfoP); + + cinfoP->data_precision = BITS_IN_JSAMPLE; + /* we always rescale data to this */ + cinfoP->image_width = (unsigned int) width; + cinfoP->image_height = (unsigned int) height; + + cinfoP->arith_code = cmdline.arith_code; + cinfoP->dct_method = cmdline.dct_method; + if (cmdline.trace_level == 0 && cmdline.verbose) + cinfoP->err->trace_level = 1; + else cinfoP->err->trace_level = cmdline.trace_level; + if (cmdline.grayscale) + jpeg_set_colorspace(cinfoP, JCS_GRAYSCALE); + else if (cmdline.rgb) + /* This is not legal if the input is not JCS_RGB too, i.e. it's PPM */ + jpeg_set_colorspace(cinfoP, JCS_RGB); + else + /* This default will be based on the in_color_space set above */ + jpeg_default_colorspace(cinfoP); + if (cmdline.max_memory_to_use != -1) + cinfoP->mem->max_memory_to_use = cmdline.max_memory_to_use; + cinfoP->optimize_coding = cmdline.optimize; + if (cmdline.quality == -1) { + quality = 75; + q_scale_factor = 100; + } else { + quality = cmdline.quality; + q_scale_factor = jpeg_quality_scaling(cmdline.quality); + } + if (cmdline.smoothing_factor != -1) + cinfoP->smoothing_factor = cmdline.smoothing_factor; + + /* Set quantization tables for selected quality. */ + /* Some or all may be overridden if user specified --qtables. */ + jpeg_set_quality(cinfoP, quality, cmdline.force_baseline); + + if (cmdline.qtablefile != NULL) { + if (! read_quant_tables(cinfoP, cmdline.qtablefile, + q_scale_factor, cmdline.force_baseline)) + pm_error("Can't use quantization table file '%s'.", + cmdline.qtablefile); + } + + if (cmdline.qslots != NULL) { + if (! set_quant_slots(cinfoP, cmdline.qslots)) + pm_error("Bad quantization-table-selectors parameter string '%s'.", + cmdline.qslots); + } + + if (cmdline.sample != NULL) { + if (! set_sample_factors(cinfoP, cmdline.sample)) + pm_error("Bad sample-factors parameters string '%s'.", + cmdline.sample); + } + + if (cmdline.progressive) + jpeg_simple_progression(cinfoP); + + if (cmdline.density_spec) + setup_jpeg_density(cinfoP, cmdline.density); + + if (cmdline.scans != NULL) { + if (! read_scan_script(cinfoP, cmdline.scans)) { + pm_message("Error in scan script '%s'.", cmdline.scans); + } + } + + /* Specify data destination for compression */ + jpeg_stdio_dest(cinfoP, output_file); + + if (cmdline.verbose) report_compressor(*cinfoP); + + /* Start compressor */ + jpeg_start_compress(cinfoP, TRUE); + +} + + + +static void +write_exif_header(struct jpeg_compress_struct * const cinfoP, + const char * const exif_filespec) { +/*---------------------------------------------------------------------------- + Generate an APP1 marker in the JFIF output that is an Exif header. + + The contents of the Exif header are in the file with filespec + 'exif_filespec' (file spec and contents are not validated). + + exif_filespec = "-" means Standard Input. + + If the file contains just two bytes of zero, don't write any marker + but don't recognize any error either. -----------------------------------------------------------------------------*/ FILE * exif_file; unsigned short length; @@ -692,348 +1018,16 @@ convert_scanlines(struct jpeg_compress_struct * const cinfo_p, } -/*---------------------------------------------------------------------------- - The functions below here are essentially the file rdswitch.c from - the JPEG library. They perform the functions specifed by the following - pnmtojpeg options: - - -qtables file Read quantization tables from text file - -scans file Read scan script from text file - -qslots N[,N,...] Set component quantization table selectors - -sample HxV[,HxV,...] Set component sampling factors ------------------------------------------------------------------------------*/ - -static int -text_getc (FILE * file) -/* Read next char, skipping over any comments (# to end of line) */ -/* A comment/newline sequence is returned as a newline */ -{ - register int ch; - - ch = getc(file); - if (ch == '#') { - do { - ch = getc(file); - } while (ch != '\n' && ch != EOF); - } - return ch; -} - - -static boolean -read_text_integer (FILE * file, long * result, int * termchar) -/* Read an unsigned decimal integer from a file, store it in result */ -/* Reads one trailing character after the integer; returns it in termchar */ -{ - register int ch; - register long val; - - /* Skip any leading whitespace, detect EOF */ - do { - ch = text_getc(file); - if (ch == EOF) { - *termchar = ch; - return FALSE; - } - } while (isspace(ch)); - - if (! isdigit(ch)) { - *termchar = ch; - return FALSE; - } - - val = ch - '0'; - while ((ch = text_getc(file)) != EOF) { - if (! isdigit(ch)) - break; - val *= 10; - val += ch - '0'; - } - *result = val; - *termchar = ch; - return TRUE; -} - - -static boolean -read_quant_tables (j_compress_ptr cinfo, char * filename, - int scale_factor, boolean force_baseline) -/* Read a set of quantization tables from the specified file. - * The file is plain ASCII text: decimal numbers with whitespace between. - * Comments preceded by '#' may be included in the file. - * There may be one to NUM_QUANT_TBLS tables in the file, each of 64 values. - * The tables are implicitly numbered 0,1,etc. - * NOTE: does not affect the qslots mapping, which will default to selecting - * table 0 for luminance (or primary) components, 1 for chrominance components. - * You must use -qslots if you want a different component->table mapping. - */ -{ - FILE * fp; - int tblno, i, termchar; - long val; - unsigned int table[DCTSIZE2]; - - if ((fp = fopen(filename, "rb")) == NULL) { - pm_message("Can't open table file %s", filename); - return FALSE; - } - tblno = 0; - - while (read_text_integer(fp, &val, &termchar)) { /* read 1st element of table */ - if (tblno >= NUM_QUANT_TBLS) { - pm_message("Too many tables in file %s", filename); - fclose(fp); - return FALSE; - } - table[0] = (unsigned int) val; - for (i = 1; i < DCTSIZE2; i++) { - if (! read_text_integer(fp, &val, &termchar)) { - pm_message("Invalid table data in file %s", filename); - fclose(fp); - return FALSE; - } - table[i] = (unsigned int) val; - } - jpeg_add_quant_table(cinfo, tblno, table, scale_factor, - force_baseline); - tblno++; - } - - if (termchar != EOF) { - pm_message("Non-numeric data in file %s", filename); - fclose(fp); - return FALSE; - } - - fclose(fp); - return TRUE; -} - - -static boolean -read_scan_integer (FILE * file, long * result, int * termchar) -/* Variant of read_text_integer that always looks for a non-space termchar; - * this simplifies parsing of punctuation in scan scripts. - */ -{ - register int ch; - - if (! read_text_integer(file, result, termchar)) - return FALSE; - ch = *termchar; - while (ch != EOF && isspace(ch)) - ch = text_getc(file); - if (isdigit(ch)) { /* oops, put it back */ - if (ungetc(ch, file) == EOF) - return FALSE; - ch = ' '; - } else { - /* Any separators other than ';' and ':' are ignored; - * this allows user to insert commas, etc, if desired. - */ - if (ch != EOF && ch != ';' && ch != ':') - ch = ' '; - } - *termchar = ch; - return TRUE; -} - - -boolean -read_scan_script (j_compress_ptr cinfo, char * filename) -/* Read a scan script from the specified text file. - * Each entry in the file defines one scan to be emitted. - * Entries are separated by semicolons ';'. - * An entry contains one to four component indexes, - * optionally followed by a colon ':' and four progressive-JPEG parameters. - * The component indexes denote which component(s) are to be transmitted - * in the current scan. The first component has index 0. - * Sequential JPEG is used if the progressive-JPEG parameters are omitted. - * The file is free format text: any whitespace may appear between numbers - * and the ':' and ';' punctuation marks. Also, other punctuation (such - * as commas or dashes) can be placed between numbers if desired. - * Comments preceded by '#' may be included in the file. - * Note: we do very little validity checking here; - * jcmaster.c will validate the script parameters. - */ -{ - FILE * fp; - int nscans, ncomps, termchar; - long val; -#define MAX_SCANS 100 /* quite arbitrary limit */ - jpeg_scan_info scans[MAX_SCANS]; - - if ((fp = fopen(filename, "r")) == NULL) { - pm_message("Can't open scan definition file %s", filename); - return FALSE; - } - nscans = 0; - - while (read_scan_integer(fp, &val, &termchar)) { - nscans++; /* We got another scan */ - if (nscans > MAX_SCANS) { - pm_message("Too many scans defined in file %s", filename); - fclose(fp); - return FALSE; - } - scans[nscans-1].component_index[0] = (int) val; - ncomps = 1; - while (termchar == ' ') { - if (ncomps >= MAX_COMPS_IN_SCAN) { - pm_message("Too many components in one scan in file %s", - filename); - fclose(fp); - return FALSE; - } - if (! read_scan_integer(fp, &val, &termchar)) - goto bogus; - scans[nscans-1].component_index[ncomps] = (int) val; - ncomps++; - } - scans[nscans-1].comps_in_scan = ncomps; - if (termchar == ':') { - if (! read_scan_integer(fp, &val, &termchar) || termchar != ' ') - goto bogus; - scans[nscans-1].Ss = (int) val; - if (! read_scan_integer(fp, &val, &termchar) || termchar != ' ') - goto bogus; - scans[nscans-1].Se = (int) val; - if (! read_scan_integer(fp, &val, &termchar) || termchar != ' ') - goto bogus; - scans[nscans-1].Ah = (int) val; - if (! read_scan_integer(fp, &val, &termchar)) - goto bogus; - scans[nscans-1].Al = (int) val; - } else { - /* set non-progressive parameters */ - scans[nscans-1].Ss = 0; - scans[nscans-1].Se = DCTSIZE2-1; - scans[nscans-1].Ah = 0; - scans[nscans-1].Al = 0; - } - if (termchar != ';' && termchar != EOF) { - bogus: - pm_message("Invalid scan entry format in file %s", filename); - fclose(fp); - return FALSE; - } - } - - if (termchar != EOF) { - pm_message("Non-numeric data in file %s", filename); - fclose(fp); - return FALSE; - } - - if (nscans > 0) { - /* Stash completed scan list in cinfo structure. NOTE: in - * this program, JPOOL_IMAGE is the right lifetime for this - * data, but if you want to compress multiple images you'd - * want JPOOL_PERMANENT. - */ - const unsigned int scan_info_size = nscans * sizeof(jpeg_scan_info); - jpeg_scan_info * const scan_info = - (jpeg_scan_info *) - (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, - scan_info_size); - memcpy(scan_info, scans, scan_info_size); - cinfo->scan_info = scan_info; - cinfo->num_scans = nscans; - } - - fclose(fp); - return TRUE; -} - - -static boolean -set_quant_slots (j_compress_ptr cinfo, char *arg) -/* Process a quantization-table-selectors parameter string, of the form - * N[,N,...] - * If there are more components than parameters, the last value is replicated. - */ -{ - int val = 0; /* default table # */ - int ci; - char ch; - - for (ci = 0; ci < MAX_COMPONENTS; ci++) { - if (*arg) { - ch = ','; /* if not set by sscanf, will be ',' */ - if (sscanf(arg, "%d%c", &val, &ch) < 1) - return FALSE; - if (ch != ',') /* syntax check */ - return FALSE; - if (val < 0 || val >= NUM_QUANT_TBLS) { - pm_message("Invalid quantization table number: %d. " - "JPEG quantization tables are numbered 0..%d", - val, NUM_QUANT_TBLS - 1); - return FALSE; - } - cinfo->comp_info[ci].quant_tbl_no = val; - while (*arg && *arg++ != ',') - /* advance to next segment of arg string */ - ; - } else { - /* reached end of parameter, set remaining components to last tbl*/ - cinfo->comp_info[ci].quant_tbl_no = val; - } - } - return TRUE; -} - - -static boolean -set_sample_factors (j_compress_ptr cinfo, char *arg) -/* Process a sample-factors parameter string, of the form - * HxV[,HxV,...] - * If there are more components than parameters, "1x1" is assumed for the rest. - */ -{ - int ci, val1, val2; - char ch1, ch2; - - for (ci = 0; ci < MAX_COMPONENTS; ci++) { - if (*arg) { - ch2 = ','; /* if not set by sscanf, will be ',' */ - if (sscanf(arg, "%d%c%d%c", &val1, &ch1, &val2, &ch2) < 3) - return FALSE; - if ((ch1 != 'x' && ch1 != 'X') || ch2 != ',') /* syntax check */ - return FALSE; - if (val1 <= 0 || val1 > 4) { - pm_message("Invalid sampling factor: %d. " - "JPEG sampling factors must be 1..4", val1); - return FALSE; - } - if (val2 <= 0 || val2 > 4) { - pm_message("Invalid sampling factor: %d. " - "JPEG sampling factors must be 1..4", val2); - return FALSE; - } - cinfo->comp_info[ci].h_samp_factor = val1; - cinfo->comp_info[ci].v_samp_factor = val2; - while (*arg && *arg++ != ',') - /* advance to next segment of arg string */ - ; - } else { - /* reached end of parameter, set remaining components - to 1x1 sampling */ - cinfo->comp_info[ci].h_samp_factor = 1; - cinfo->comp_info[ci].v_samp_factor = 1; - } - } - return TRUE; -} - int -main(int argc, char ** argv) { +main(int argc, + char ** argv) { struct cmdlineInfo cmdline; struct jpeg_compress_struct cinfo; struct jpeg_error_mgr jerr; - FILE *input_file; + FILE * input_file; FILE * output_file; int height; /* height of the input image in rows, as specified by its header */ diff --git a/converter/other/pnmtopalm/Makefile b/converter/other/pnmtopalm/Makefile index d7bf2829..3d802ed6 100644 --- a/converter/other/pnmtopalm/Makefile +++ b/converter/other/pnmtopalm/Makefile @@ -25,7 +25,8 @@ $(BINARIES): %: %.o palmcolormap.o $(NETPBMLIB) $(LIBOPT) $(MATHLIB) $(LDLIBS) $(RPATH) $(LADD) gen_palm_colormap : % : %.c palmcolormap.o - $(CC) -I importinc $(CFLAGS) $(LDFLAGS) -o $@ $< palmcolormap.o \ + $(CC) -I importinc $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ \ + $< palmcolormap.o \ $(LIBOPTS) $(MATHLIB) $(LDLIBS) $(LADD) diff --git a/converter/other/pnmtopng.c b/converter/other/pnmtopng.c index ed245b93..4e267b99 100644 --- a/converter/other/pnmtopng.c +++ b/converter/other/pnmtopng.c @@ -383,7 +383,7 @@ parseCommandLine(int argc, char ** argv, opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ opt.allowNegNum = FALSE; /* We have no parms that are negative numbers */ - optParseOptions3( &argc, argv, opt, sizeof(opt), 0); + optParseOptions3(&argc, argv, opt, sizeof(opt), 0); /* Uses and sets argc, argv, and some of *cmdlineP and others. */ @@ -962,7 +962,7 @@ analyzeAlpha(FILE * const ifp, gray const alphaMaxval, bool * const allOpaqueP, bool * const singleColorIsTransP, - pixel* const alphaTranscolorP) { + pixel * const alphaTranscolorP) { /*---------------------------------------------------------------------------- Get information about the alpha mask, in combination with the masked image, that Caller can use to choose the most efficient way to @@ -993,7 +993,7 @@ analyzeAlpha(FILE * const ifp, */ foundTransparentPixel = FALSE; /* initial assumption */ pm_seek2(ifp, &rasterPos, sizeof(rasterPos)); - for (row = 0 ; row < rows && !foundTransparentPixel ; ++row) { + for (row = 0; row < rows && !foundTransparentPixel; ++row) { int col; pnm_readpnmrow(ifp, xelrow, cols, maxval, format); for (col = 0; col < cols && !foundTransparentPixel; ++col) { @@ -2238,12 +2238,12 @@ convertpnm(struct cmdlineInfo const cmdline, of the input image. */ int transexact; - /* boolean: the user wants only the exact color he specified to be - transparent; not just something close to it. - */ + /* boolean: the user wants only the exact color he specified to be + transparent; not just something close to it. + */ int transparent; bool alpha; - /* There will be an alpha mask */ + /* There will be an alpha mask */ unsigned int pnm_meaningful_bits; pixel backcolor; /* The background color, with maxval equal to that of the input @@ -2700,7 +2700,7 @@ main(int argc, char *argv[]) { int errorlevel; - pnm_init (&argc, argv); + pnm_init(&argc, argv); parseCommandLine(argc, argv, &cmdline); diff --git a/converter/pbm/Makefile b/converter/pbm/Makefile index 318b8e9e..f1533ab2 100644 --- a/converter/pbm/Makefile +++ b/converter/pbm/Makefile @@ -17,7 +17,7 @@ PORTBINARIES = atktopbm brushtopbm cmuwmtopbm ddbugtopbm g3topbm escp2topbm \ pbmtomacp pbmtomatrixorbital pbmtomda pbmtomgr pbmtomrf \ pbmtonokia \ pbmtopi3 pbmtoplot pbmtopsg3 pbmtoptx pbmtowbmp \ - pbmtox10bm pbmtoxbm pbmtoybm pbmtozinc \ + pbmtoxbm pbmtoybm pbmtozinc \ pi3topbm pktopbm \ wbmptopbm xbmtopbm ybmtopbm @@ -29,7 +29,7 @@ endif #in libm? MATHBINARIES = pbmtopk BINARIES = $(PORTBINARIES) $(MATHBINARIES) -SCRIPTS = +SCRIPTS = pbmtox10bm OBJECTS = $(BINARIES:%=%.o) diff --git a/converter/pbm/cmuwmtopbm.c b/converter/pbm/cmuwmtopbm.c index dce1d449..ce59491e 100644 --- a/converter/pbm/cmuwmtopbm.c +++ b/converter/pbm/cmuwmtopbm.c @@ -1,4 +1,4 @@ -/* cmuwmtopbm.c - read a CMU window manager bitmap and produce a portable bitmap +/* cmuwmtopbm.c - read a CMU window manager bitmap and produce a PBM image. ** ** Copyright (C) 1989 by Jef Poskanzer. ** @@ -10,105 +10,106 @@ ** implied warranty. */ +/* 2006.10 (afu) + Changed bitrow from plain to raw, read function from getc() to fread(), + write function from pbm_writepbmrow() to pbm_writepbmrow_packed(). + Retired bitwise transformation functions. + + This program does not check the pad bits at the end of each row. +*/ + + #include "pbm.h" #include "cmuwm.h" -static void getinit ARGS(( FILE* file, int* colsP, int* rowsP, short* depthP, int* padrightP )); -static bit getbit ARGS(( FILE* file )); -int -main( argc, argv ) - int argc; - char* argv[]; - { - FILE* ifp; - bit* bitrow; - register bit* bP; - int rows, cols, padright, row, col; - short depth; +static void +readCmuwmHeader(FILE * const ifP, + unsigned int * const colsP, + unsigned int * const rowsP, + unsigned int * const depthP) { - pbm_init( &argc, argv ); + const char * const initReadError = + "CMU window manager header EOF / read error"; - if ( argc > 2 ) - pm_usage( "[cmuwmfile]" ); + long l; + short s; + int rc; + + rc = pm_readbiglong(ifP, &l); + if (rc == -1 ) + pm_error(initReadError); + if (l != CMUWM_MAGIC) + pm_error("bad magic number in CMU window manager file"); + rc = pm_readbiglong(ifP, &l); + if (rc == -1) + pm_error(initReadError); + *colsP = l; + rc = pm_readbiglong(ifP, &l); + if (rc == -1 ) + pm_error(initReadError); + *rowsP = l; + rc = pm_readbigshort(ifP, &s); + if (rc == -1) + pm_error(initReadError); + *depthP = s; +} - if ( argc == 2 ) - ifp = pm_openr( argv[1] ); - else - ifp = stdin; - - getinit( ifp, &cols, &rows, &depth, &padright ); - if ( depth != 1 ) - pm_error( - "CMU window manager file has depth of %d, must be 1", - (int) depth ); - - pbm_writepbminit( stdout, cols, rows, 0 ); - bitrow = pbm_allocrow( cols ); - - for ( row = 0; row < rows; ++row ) - { - /* Get data. */ - for ( col = 0, bP = bitrow; col < cols; ++col, ++bP ) - *bP = getbit( ifp ); - /* Discard line padding */ - for ( col = 0; col < padright; ++col ) - (void) getbit( ifp ); - pbm_writepbmrow( stdout, bitrow, cols, 0 ); - } - - pm_close( ifp ); - pm_close( stdout ); - - exit( 0 ); - } -static int item, bitsperitem, bitshift; -static void -getinit( file, colsP, rowsP, depthP, padrightP ) - FILE* file; - int* colsP; - int* rowsP; - short* depthP; - int* padrightP; - { - long l; +int +main(int argc, + char * argv[]) { - if ( pm_readbiglong( file, &l ) == -1 ) - pm_error( "EOF / read error" ); - if ( l != CMUWM_MAGIC ) - pm_error( "bad magic number in CMU window manager file" ); - if ( pm_readbiglong( file, &l ) == -1 ) - pm_error( "EOF / read error" ); - *colsP = (int) l; - if ( pm_readbiglong( file, &l ) == -1 ) - pm_error( "EOF / read error" ); - *rowsP = (int) l; - if ( pm_readbigshort( file, depthP ) == -1 ) - pm_error( "EOF / read error" ); - *padrightP = ( ( *colsP + 7 ) / 8 ) * 8 - *colsP; - - bitsperitem = 0; - } + FILE * ifP; + unsigned char * bitrow; + unsigned int rows, cols, depth; + unsigned int row; + + const char * inputFileName; -static bit -getbit( file ) - FILE* file; - { - bit b; - - if ( bitsperitem == 0 ) - { - item = getc( file ); - if ( item == EOF ) - pm_error( "EOF / read error" ); - bitsperitem = 8; - bitshift = 7; - } - b = ( ( item >> bitshift) & 1 ) ? PBM_WHITE : PBM_BLACK; - --bitsperitem; - --bitshift; - return b; + pbm_init(&argc, argv); + + if (argc-1 > 1) + pm_error("Too many arguments (%u). " + "Only argument is optional input file", argc-1); + if (argc-1 == 1) + inputFileName = argv[1]; + else + inputFileName = "-"; + + ifP = pm_openr(inputFileName); + + readCmuwmHeader(ifP, &cols, &rows, &depth); + if (depth != 1) + pm_error("CMU window manager file has depth of %u, must be 1", depth); + + pbm_writepbminit(stdout, cols, rows, 0); + bitrow = pbm_allocrow_packed(cols); + + for (row = 0; row < rows; ++row) { + unsigned int const bytesPerRow = pbm_packed_bytes(cols); + unsigned int byteSeq; + size_t bytesRead; + + bytesRead = fread(bitrow, 1, bytesPerRow, ifP); + if (bytesRead != bytesPerRow) + pm_error("CWU window manager bitmap EOF / read error"); + + /* Invert all bits in row - raster formats are similar. + CMUWM Black:0 White:1 End of row padded with 1 + PBM Black:1 White:0 End preferably padded with 0 + */ + + for (byteSeq = 0; byteSeq < bytesPerRow; ++byteSeq) + bitrow[byteSeq] = ~bitrow[byteSeq]; + + pbm_writepbmrow_packed(stdout, bitrow, cols, 0); } + + pm_close(ifP); + pm_close(stdout); + + return 0; +} diff --git a/converter/pbm/mgrtopbm.c b/converter/pbm/mgrtopbm.c index cea4be48..9f7004a1 100644 --- a/converter/pbm/mgrtopbm.c +++ b/converter/pbm/mgrtopbm.c @@ -1,13 +1,10 @@ -/* mgrtopbm.c - read a MGR bitmap and produce a portable bitmap -** -** Copyright (C) 1989 by Jef Poskanzer. -** -** Permission to use, copy, modify, and distribute this software and its -** documentation for any purpose and without fee is hereby granted, provided -** that the above copyright notice appear in all copies and that both that -** copyright notice and this permission notice appear in supporting -** documentation. This software is provided "as is" without express or -** implied warranty. +/* mgrtopbm.c - read a MGR bitmap and produce a PBM image. + + Copyright information is at end of file. + + You can find MGR and some MGR format test images at + ftp://sunsite.unc.edu/pub/Linux/apps/MGR/!INDEX.html + */ #include @@ -17,29 +14,29 @@ #include "mgr.h" -static unsigned char item; -static int bitsperitem, bitshift; static void -getinit(FILE * const file, - int * const colsP, - int * const rowsP, - int * const depthP, - int * const padrightP, - int * const bitsperitemP) { - +readMgrHeader(FILE * const ifP, + unsigned int * const colsP, + unsigned int * const rowsP, + unsigned int * const depthP, + unsigned int * const padrightP ) { + struct b_header head; - int pad; + unsigned int pad; + size_t bytesRead; - if (fread(&head, sizeof(struct old_b_header), 1, file ) != 1) + bytesRead = fread(&head, sizeof(struct old_b_header), 1, ifP); + if (bytesRead != 1) pm_error("Unable to read 1st byte of file. " "fread() returns errno %d (%s)", errno, strerror(errno)); if (head.magic[0] == 'y' && head.magic[1] == 'z') { /* new style bitmap */ - if (fread(&head.depth, - sizeof(head) - sizeof(struct old_b_header), 1, file) - != 1 ) + size_t bytesRead; + bytesRead = fread(&head.depth, + sizeof(head) - sizeof(struct old_b_header), 1, ifP); + if (bytesRead != 1 ) pm_error("Unable to read header after 1st byte. " "fread() returns errno %d (%s)", errno, strerror(errno)); @@ -60,7 +57,7 @@ getinit(FILE * const file, } else { pm_error("bad magic chars in MGR file: '%c%c'", head.magic[0], head.magic[1] ); - pad = -1; /* should never reach here */ + pad = 0; /* should never reach here */ } if (head.h_wide < ' ' || head.l_wide < ' ') @@ -71,75 +68,79 @@ getinit(FILE * const file, *colsP = (((int)head.h_wide - ' ') << 6) + ((int)head.l_wide - ' '); *rowsP = (((int)head.h_high - ' ') << 6) + ((int) head.l_high - ' '); *padrightP = ( ( *colsP + pad - 1 ) / pad ) * pad - *colsP; - - *bitsperitemP = 8; } -static bit -getbit( file ) - FILE* file; - { - bit b; - - if ( bitsperitem == 8 ) - { - item = getc( file ); - bitsperitem = 0; - bitshift = 7; - } - bitsperitem++; - b = ( ( item >> bitshift) & 1 ) ? PBM_BLACK : PBM_WHITE; - bitshift--; - return b; - } - +int +main(int argc, + char * argv[]) { + + FILE * ifP; + unsigned char * bitrow; + unsigned int rows, cols, depth; + unsigned int padright; + unsigned int row; + unsigned int itemCount; + const char * inputFileName; + + pbm_init(&argc, argv); + + if (argc-1 > 1) + pm_error("Too many arguments (%u). " + "Only argument is optional input file", argc-1); + if (argc-1 == 1) + inputFileName = argv[1]; + else + inputFileName = "-"; + + ifP = pm_openr(inputFileName); + readMgrHeader(ifP, &cols, &rows, &depth, &padright); + if (depth != 1) + pm_error("MGR file has depth of %u, must be 1", depth); -int -main( argc, argv ) - int argc; - char* argv[]; - { - FILE* ifp; - bit* bitrow; - register bit* bP; - int rows, cols, depth, padright, row, col; + pbm_writepbminit(stdout, cols, rows, 0); + bitrow = pbm_allocrow_packed(cols + padright); + + itemCount = (cols + padright ) / 8; + + for (row = 0; row < rows; ++row) { + /* The raster formats are nearly identical. + MGR may have rows padded to 16 or 32 bit boundaries. + */ + size_t bytesRead; + bytesRead = fread(bitrow, 1, itemCount, ifP); + if (bytesRead < itemCount) + pm_error("fread() failed to read mgr bitmap data"); + + pbm_writepbmrow_packed(stdout, bitrow, cols, 0); + } + pm_close(ifP); + pm_close(stdout); + return 0; +} - pbm_init( &argc, argv ); - if ( argc > 2 ) - pm_usage( "[mgrfile]" ); - if ( argc == 2 ) - ifp = pm_openr( argv[1] ); - else - ifp = stdin; - - getinit( ifp, &cols, &rows, &depth, &padright, &bitsperitem ); - if ( depth != 1 ) - pm_error( "MGR file has depth of %d, must be 1", depth ); - - pbm_writepbminit( stdout, cols, rows, 0 ); - bitrow = pbm_allocrow( cols ); - - for ( row = 0; row < rows; row++ ) - { - /* Get data, bit-reversed within each byte. */ - for ( col = 0, bP = bitrow; col < cols; col++, bP++ ) - *bP = getbit( ifp ); - /* Discard line padding */ - for ( col = 0; col < padright; col ++ ) - (void) getbit( ifp ); - pbm_writepbmrow( stdout, bitrow, cols, 0 ); - } - - pm_close( ifp ); - pm_close( stdout ); - - exit( 0 ); - } +/* 2006.10 (afu) + Changed bitrow from plain to raw, read function from getc() to fread(), + write function from pbm_writepbmrow() to pbm_writepbmrow_packed(). + Retired bitwise transformation functions. + + NOT tested for old-style format files. Only one zz file in mgrsrc-0.69 . + +*/ +/* +** Copyright (C) 1989 by Jef Poskanzer. +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +*/ diff --git a/converter/pbm/pbmtocmuwm.c b/converter/pbm/pbmtocmuwm.c index 64d7af40..773d988b 100644 --- a/converter/pbm/pbmtocmuwm.c +++ b/converter/pbm/pbmtocmuwm.c @@ -1,4 +1,4 @@ -/* pbmtocmuwm.c - read a portable bitmap and produce a CMU window manager bitmap +/* pbmtocmuwm.c - read a PBM image and produce a CMU window manager bitmap ** ** Copyright (C) 1989 by Jef Poskanzer. ** @@ -10,108 +10,95 @@ ** implied warranty. */ -#include "pbm.h" -#include "cmuwm.h" +/* 2006.10 (afu) + Changed bitrow from plain to raw, read function from pbm_readpbmrow() to + pbm_readpbmrow_packed(), write function from putc() to fwrite(). -static void putinit ARGS(( int rows, int cols )); -static void putbit ARGS(( bit b )); -static void putrest ARGS(( void )); -static void putitem ARGS(( void )); - -int -main( argc, argv ) - int argc; - char* argv[]; - { - FILE* ifp; - bit* bitrow; - register bit* bP; - int rows, cols, format, padright, row, col; + Retired bitwise transformation functions. +*/ +#include "pbm.h" +#include "cmuwm.h" - pbm_init( &argc, argv ); +static void +putinit(unsigned int const rows, + unsigned int const cols) { - if ( argc > 2 ) - pm_usage( "[pbmfile]" ); + const char * const initWriteError = + "CMU window manager header write error"; - if ( argc == 2 ) - ifp = pm_openr( argv[1] ); - else - ifp = stdin; + int rc; - pbm_readpbminit( ifp, &cols, &rows, &format ); - bitrow = pbm_allocrow( cols ); - - /* Round cols up to the nearest multiple of 8. */ - padright = ( ( cols + 7 ) / 8 ) * 8 - cols; + rc = pm_writebiglong(stdout, CMUWM_MAGIC); + if (rc == -1) + pm_error(initWriteError); + rc = pm_writebiglong(stdout, cols); + if (rc == -1) + pm_error(initWriteError); + rc = pm_writebiglong(stdout, rows); + if (rc == -1) + pm_error(initWriteError); + rc = pm_writebigshort(stdout, (short) 1); + if (rc == -1) + pm_error(initWriteError); +} - putinit( rows, cols ); - for ( row = 0; row < rows; row++ ) - { - pbm_readpbmrow( ifp, bitrow, cols, format ); - for ( col = 0, bP = bitrow; col < cols; col++, bP++ ) - putbit( *bP ); - for ( col = 0; col < padright; col++ ) - putbit( 0 ); - } - pm_close( ifp ); - putrest( ); +int +main(int argc, + char * argv[]) { - exit( 0 ); - } + FILE * ifP; + unsigned char * bitrow; + int rows, cols; + int format; + unsigned int row; + const char * inputFileName; -static unsigned char item; -static int bitsperitem, bitshift; + pbm_init(&argc, argv); -static void -putinit( rows, cols ) - int rows, cols; - { - if ( pm_writebiglong( stdout, CMUWM_MAGIC ) == -1 ) - pm_error( "write error" ); - if ( pm_writebiglong( stdout, cols ) == -1 ) - pm_error( "write error" ); - if ( pm_writebiglong( stdout, rows ) == -1 ) - pm_error( "write error" ); - if ( pm_writebigshort( stdout, (short) 1 ) == -1 ) - pm_error( "write error" ); - - item = 0; - bitsperitem = 0; - bitshift = 7; - } + if (argc-1 > 1) + pm_error("Too many arguments (%u). " + "Only argument is optional input file", argc-1); + if (argc-1 == 1) + inputFileName = argv[1]; + else + inputFileName = "-"; + + ifP = pm_openr(inputFileName); -#if __STDC__ -static void -putbit( bit b ) -#else /*__STDC__*/ -static void -putbit( b ) - bit b; -#endif /*__STDC__*/ - { - if ( bitsperitem == 8 ) - putitem( ); - if ( b == PBM_WHITE ) - item += 1 << bitshift; - bitsperitem++; - bitshift--; - } + pbm_readpbminit(ifP, &cols, &rows, &format); + bitrow = pbm_allocrow_packed(cols); -static void -putrest( ) - { - if ( bitsperitem > 0 ) - putitem( ); + putinit(rows, cols); + + /* Convert PBM raster data to CMUWM and write */ + for (row = 0; row < rows; ++row) { + unsigned int const bytesPerRow = pbm_packed_bytes(cols); + unsigned char const padding = + (cols % 8 == 0) ? 0x00 : ((unsigned char) ~0 >> (cols % 8)); + + unsigned int i; + size_t bytesWritten; + + pbm_readpbmrow_packed(ifP, bitrow, cols, format); + + /* Invert all bits in row - raster formats are similar. + PBM Black:1 White:0 "Don't care" bits at end of row + CMUWM Black:0 White:1 End of row padded with 1 + */ + + for (i = 0; i < bytesPerRow; ++i) + bitrow[i] = ~bitrow[i]; + + bitrow[bytesPerRow-1] |= padding; /* Set row end pad bits */ + + bytesWritten = fwrite(bitrow, 1, bytesPerRow, stdout); + if (bytesWritten != bytesPerRow) + pm_error("fwrite() failed to write CMU window manager bitmap"); } -static void -putitem( ) - { - (void) putc( item, stdout ); - item = 0; - bitsperitem = 0; - bitshift = 7; - } + pm_close(ifP); + return 0; +} diff --git a/converter/pbm/pbmtogem.c b/converter/pbm/pbmtogem.c index 59f2b9cf..cefbdc95 100644 --- a/converter/pbm/pbmtogem.c +++ b/converter/pbm/pbmtogem.c @@ -129,14 +129,8 @@ putinit (rows, cols) linerepeat = -1; } -#if __STDC__ static void putbit( bit b ) -#else /*__STDC__*/ -static void -putbit( b ) - bit b; -#endif /*__STDC__*/ { if ( bitsperitem == 8 ) putitem( ); diff --git a/converter/pbm/pbmtogo.c b/converter/pbm/pbmtogo.c index b7c12373..67fe5821 100644 --- a/converter/pbm/pbmtogo.c +++ b/converter/pbm/pbmtogo.c @@ -214,7 +214,7 @@ main( argc, argv ) fflush(stdout); /* Output the plot data */ - write(1, outbuffer, nout); + fwrite(outbuffer, 1, nout, stdout); /* Reset the counters */ linerepeat = 0; @@ -225,7 +225,7 @@ main( argc, argv ) putchar(linerepeat); printf("%d/", nout+1); fflush(stdout); - write(1, outbuffer, nout); + fwrite(outbuffer, 1, nout, stdout); linerepeat = 0; } } @@ -264,14 +264,8 @@ putinit() bitshift = 7; } -#if __STDC__ static void putbit(bit b) -#else /*__STDC__*/ -static void -putbit(b) -bit b; -#endif /*__STDC__*/ { if (b == PBM_BLACK) item += 1 << bitshift; diff --git a/converter/pbm/pbmtoicon.c b/converter/pbm/pbmtoicon.c index 0e21c202..d5fefb76 100644 --- a/converter/pbm/pbmtoicon.c +++ b/converter/pbm/pbmtoicon.c @@ -1,4 +1,4 @@ -/* pbmtoicon.c - read a portable bitmap and produce a Sun icon file +/* pbmtoicon.c - read a PBM image and produce a Sun icon file ** ** Copyright (C) 1988 by Jef Poskanzer. ** @@ -10,125 +10,177 @@ ** implied warranty. */ +/* 2006.10 (afu) + Changed bitrow from plain to raw, read function from pbm_readpbmrow() to + pbm_readpbmrow_packed. Applied wordint to scoop up 16 bit output items. + putitem changed to better express the output format. + Retired bitwise transformation functions. +*/ + +#include "wordaccess.h" #include "pbm.h" -static void putinit ARGS(( void )); -static void putbit ARGS(( bit b )); -static void putrest ARGS(( void )); -static void putitem ARGS(( void )); +static unsigned short int itemBuff[8]; +static unsigned int itemCnt; /* takes values 0 to 8 */ +FILE * putFp; -int -main( argc, argv ) - int argc; - char* argv[]; - { - FILE* ifp; - bit* bitrow; - register bit* bP; - int rows, cols, format, pad, padleft, padright, row, col; - pbm_init( &argc, argv ); +static void +putinit(FILE * const ofP) { + putFp = ofP; + itemCnt = 0; +} - if ( argc > 2 ) - pm_usage( "[pbmfile]" ); - if ( argc == 2 ) - ifp = pm_openr( argv[1] ); - else - ifp = stdin; - pbm_readpbminit( ifp, &cols, &rows, &format ); - bitrow = pbm_allocrow( cols ); +static void +putitem(wordint const item) { + + if (itemCnt == 8 ) { + /* Buffer is full. Write out one line. */ + int rc; - /* Round cols up to the nearest multiple of 16. */ - pad = ( ( cols + 15 ) / 16 ) * 16 - cols; - padleft = pad / 2; - padright = pad - padleft; - - printf( "/* Format_version=1, Width=%d, Height=%d", cols + pad, rows ); - printf( ", Depth=1, Valid_bits_per_item=16\n */\n" ); - - putinit( ); - for ( row = 0; row < rows; ++row ) - { - pbm_readpbmrow( ifp, bitrow, cols, format ); - for ( col = 0; col < padleft; ++col ) - putbit( 0 ); - for ( col = 0, bP = bitrow; col < cols; ++col, ++bP ) - putbit( *bP ); - for ( col = 0; col < padright; ++col ) - putbit( 0 ); - } + rc = fprintf(putFp, + "\t0x%04x,0x%04x,0x%04x,0x%04x," + "0x%04x,0x%04x,0x%04x,0x%04x,\n", + itemBuff[0],itemBuff[1],itemBuff[2],itemBuff[3], + itemBuff[4],itemBuff[5],itemBuff[6],itemBuff[7]); + if (rc < 0) + pm_error("fprintf() failed to write Icon bitmap"); + + itemCnt = 0; + } + itemBuff[itemCnt++] = item & 0xffff; /* Only lower 16 bits are used */ +} - pm_close( ifp ); - putrest( ); - exit( 0 ); - } +static void +putterm(void) { -static int item, bitsperitem, bitshift, itemsperline, firstitem; + unsigned int i; -static void -putinit( ) - { - itemsperline = 0; - bitsperitem = 0; - item = 0; - bitshift = 15; - firstitem = 1; + for (i = 0; i < itemCnt; ++i) { + int rc; + rc = fprintf(putFp, "%s0x%04x%c", i == 0 ? "\t" : "", itemBuff[i], + i == itemCnt - 1 ? '\n' : ','); + if (rc < 0) + pm_error("fprintf() failed to write Icon bitmap"); } +} + + -#if __STDC__ -static void -putbit( bit b ) -#else /*__STDC__*/ static void -putbit( b ) -bit b; -#endif /*__STDC__*/ - { - if ( bitsperitem == 16 ) - putitem( ); - ++bitsperitem; - if ( b == PBM_BLACK ) - item += 1 << bitshift; - --bitshift; - } +writeIconHeader(FILE * const ofP, + unsigned int const width, + unsigned int const height) { + + int rc; + + rc = fprintf(ofP, + "/* Format_version=1, Width=%u, Height=%u", width, height); + if (rc < 0) + pm_error("fprintf() failed to write Icon header"); + + rc = fprintf(ofP, ", Depth=1, Valid_bits_per_item=16\n */\n"); + if (rc < 0) + pm_error("fprintf() failed to write Icon header"); +} + + static void -putrest( ) - { - if ( bitsperitem > 0 ) - putitem( ); - putchar( '\n' ); +writeIcon(FILE * const ifP, + unsigned int const cols, + unsigned int const rows, + int const format, + FILE * const ofP) { + + unsigned int const wordintSize = sizeof(wordint) * 8; + /* wordintSize is usually 32 or 64 bits. Must be at least 24. */ + unsigned int const items = (cols + 15) / 16; + unsigned int const bitrowBytes = pbm_packed_bytes(cols); + unsigned int const pad = items * 16 - cols; + /* 'padleft' is added to the output. 'padbyte' is for cleaning + the input + */ + unsigned int const padleft = pad / 2; + unsigned int const padbyte = bitrowBytes * 8 - cols; + unsigned int const shift = (wordintSize - 24) + padleft; + + unsigned char * bitbuffer; + unsigned char * bitrow; + unsigned int row; + + bitbuffer = pbm_allocrow_packed(cols + wordintSize); + bitrow = &bitbuffer[1]; + bitbuffer[0] = 0; + bitrow[bitrowBytes] = 0; + + writeIconHeader(ofP, cols + pad, rows); + + putinit(ofP); + + for (row = 0; row < rows; ++row) { + unsigned int itemSeq; + pbm_readpbmrow_packed(ifP, bitrow, cols, format); + + /* Clear post-data junk in final partial byte */ + if (padbyte > 0) { + bitrow[bitrowBytes-1] >>= padbyte; + bitrow[bitrowBytes-1] <<= padbyte; + } + + for (itemSeq = 0; itemSeq < items; ++itemSeq) { + /* Scoop up bits, shift-align, send to format & print function. + + An item is 16 bits, typically spread over 3 bytes due to + left-padding. We use wordint here to scoop up 4 (or more) + consecutive bytes. An item always resides within the higher + 24 bits of each scoop. It is essential to use wordint + (or rather the wordaccess function bytesToWordInt() ); + simple long, uint_32t, etc. do not work for they are not + shift-tolerant. + */ + + wordint const scoop = bytesToWordint(&bitbuffer[itemSeq*2]); + putitem (scoop >> shift); + } } + putterm(); +} -static void -putitem( ) - { - const char* hexits = "0123456789abcdef"; - if ( firstitem ) - firstitem = 0; + +int +main(int argc, + char * argv[]) { + + FILE * ifP; + int rows, cols; + int format; + const char * inputFileName; + + pbm_init(&argc, argv); + + if (argc-1 > 1) + pm_error("Too many arguments (%u). " + "Only argument is optional input file", argc-1); + if (argc-1 == 1) + inputFileName = argv[1]; else - putchar( ',' ); - if ( itemsperline == 8 ) - { - putchar( '\n' ); - itemsperline = 0; - } - if ( itemsperline == 0 ) - putchar( '\t' ); - putchar( '0' ); - putchar( 'x' ); - putchar( hexits[item >> 12] ); - putchar( hexits[( item >> 8 ) & 15] ); - putchar( hexits[( item >> 4 ) & 15] ); - putchar( hexits[item & 15] ); - ++itemsperline; - bitsperitem = 0; - item = 0; - bitshift = 15; - } + inputFileName = "-"; + + ifP = pm_openr(inputFileName); + + pbm_readpbminit(ifP, &cols, &rows, &format); + + writeIcon(ifP, cols, rows, format, stdout); + + pm_close(ifP); + + return 0; +} + diff --git a/converter/pbm/pbmtomgr.c b/converter/pbm/pbmtomgr.c index 7a6e7fc1..2ca7c7d0 100644 --- a/converter/pbm/pbmtomgr.c +++ b/converter/pbm/pbmtomgr.c @@ -1,120 +1,114 @@ -/* pbmtomgr.c - read a portable bitmap and produce a MGR bitmap -** -** Copyright (C) 1989 by Jef Poskanzer. -** -** Permission to use, copy, modify, and distribute this software and its -** documentation for any purpose and without fee is hereby granted, provided -** that the above copyright notice appear in all copies and that both that -** copyright notice and this permission notice appear in supporting -** documentation. This software is provided "as is" without express or -** implied warranty. +/* pbmtomgr.c - read a PBM image and produce a MGR bitmap + + Copyright information is at end of file. + + You can find MGR and some MGR format test images at + ftp://sunsite.unc.edu/pub/Linux/apps/MGR/!INDEX.html */ #include "pbm.h" #include "mgr.h" -static void putinit ARGS(( int rows, int cols )); -static void putbit ARGS(( bit b )); -static void putrest ARGS(( void )); -static void putitem ARGS(( void )); +static void +putinit(unsigned int const rows, + unsigned int const cols) { -int -main( argc, argv ) - int argc; - char* argv[]; - { - FILE* ifp; - bit* bitrow; - register bit* bP; - int rows, cols, format, padright, row, col; + struct b_header head; + size_t writtenCount; + head.magic[0] = 'y'; + head.magic[1] = 'z'; + head.h_wide = ((cols >> 6) & 0x3f) + ' '; + head.l_wide = (cols & 0x3f) + ' '; + head.h_high = (( rows >> 6) & 0x3f) + ' '; + head.l_high = (rows & 0x3f) + ' '; + head.depth = (1 & 0x3f) + ' '; + head._reserved = ' '; + writtenCount = fwrite(&head, sizeof(head), 1, stdout); + if (writtenCount != 1) + pm_error("fwrite() failed to write the MGR header."); +} - pbm_init( &argc, argv ); - if ( argc > 2 ) - pm_usage( "[pbmfile]" ); - if ( argc == 2 ) - ifp = pm_openr( argv[1] ); +int +main(int argc, + char * argv[]) { + + FILE * ifP; + unsigned char * bitrow; + int rows; + int cols; + int format; + unsigned int row; + unsigned int bytesPerRow; + /* Number of packed bytes (8 columns per byte) in a row. */ + unsigned int padright; + /* Number of columns added to the right of each row to get up to + a multiple of 8, i.e. an integral number of packed bytes. + */ + const char * inputFileName; + + pbm_init(&argc, argv); + + if (argc-1 > 1) + pm_error("Too many arguments (%u). " + "Only argument is optional input file", argc-1); + if (argc-1 == 1) + inputFileName = argv[1]; else - ifp = stdin; - - pbm_readpbminit( ifp, &cols, &rows, &format ); - bitrow = pbm_allocrow( cols ); + inputFileName = "-"; - /* Round cols up to the nearest multiple of 8. */ - padright = ( ( cols + 7 ) / 8 ) * 8 - cols; - - putinit( rows, cols ); - for ( row = 0; row < rows; ++row ) - { - pbm_readpbmrow( ifp, bitrow, cols, format ); - for ( col = 0, bP = bitrow; col < cols; ++col, ++bP ) - putbit( *bP ); - for ( col = 0; col < padright; ++col ) - putbit( 0 ); - } + ifP = pm_openr(inputFileName); - pm_close( ifp ); + pbm_readpbminit(ifP, &cols, &rows, &format); + + bitrow = pbm_allocrow_packed(cols); + bytesPerRow = pbm_packed_bytes(cols); + padright = bytesPerRow * 8 - cols; - putrest( ); + putinit(rows, cols); + + for (row = 0; row < rows; ++row) { + /* The raster formats are identical. + The row end pad bits are set to 0 in mgr. + */ + size_t bytesWritten; + + pbm_readpbmrow_packed(ifP, bitrow, cols, format); + + if (padright > 0) { + bitrow[bytesPerRow-1] >>= padright; + bitrow[bytesPerRow-1] <<= padright; + } - exit( 0 ); + bytesWritten = fwrite(bitrow, 1, bytesPerRow, stdout); + if (bytesWritten != bytesPerRow ) + pm_error("fwrite() failed to write MGR bitmap " + "to Standard Output."); } + pm_close(ifP); + return 0; +} -static unsigned char item; -static int bitsperitem, bitshift; - -static void -putinit( rows, cols ) - int rows, cols; - { - struct b_header head; - - head.magic[0] = 'y'; - head.magic[1] = 'z'; - head.h_wide = ( ( cols >> 6 ) & 0x3f ) + ' '; - head.l_wide = ( cols & 0x3f ) + ' '; - head.h_high = ( ( rows >> 6 ) & 0x3f ) + ' '; - head.l_high = ( rows & 0x3f ) + ' '; - head.depth = ( 1 & 0x3f ) + ' '; - head._reserved = ' '; - fwrite( &head, sizeof(head), 1, stdout ); - item = 0; - bitsperitem = 0; - bitshift = 7; - } -#if __STDC__ -static void -putbit( bit b ) -#else /*__STDC__*/ -static void -putbit( b ) - bit b; -#endif /*__STDC__*/ - { - if ( bitsperitem == 8 ) - putitem( ); - ++bitsperitem; - if ( b == PBM_BLACK ) - item += 1 << bitshift; - --bitshift; - } +/* 2006.10 (afu) + Changed bitrow from plain to raw, read function from pbm_readpbmrow() to + pbm_readpbmrow_packed(), write function from putc() to fwrite(). -static void -putrest( ) - { - if ( bitsperitem > 0 ) - putitem( ); - } + Retired bitwise transformation functions. + + Produces only new style bitmap (8 bit padding.) See mgrtopbm.c . +*/ -static void -putitem( ) - { - fwrite( &item, sizeof(item), 1, stdout ); - item = 0; - bitsperitem = 0; - bitshift = 7; - } +/* +** Copyright (C) 1989 by Jef Poskanzer. +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +*/ diff --git a/converter/pbm/pbmtopi3.c b/converter/pbm/pbmtopi3.c index 06023d7a..6a60af62 100644 --- a/converter/pbm/pbmtopi3.c +++ b/converter/pbm/pbmtopi3.c @@ -89,14 +89,8 @@ putinit( ) bitshift = 7; } -#if __STDC__ static void putbit( bit b ) -#else /*__STDC__*/ -static void -putbit( b ) - bit b; -#endif /*__STDC__*/ { if (bitsperitem == 8) putitem( ); diff --git a/converter/pbm/pbmtoptx.c b/converter/pbm/pbmtoptx.c index 5031efcb..8cd60326 100644 --- a/converter/pbm/pbmtoptx.c +++ b/converter/pbm/pbmtoptx.c @@ -67,14 +67,8 @@ putinit( ) bitshift = 0; } -#if __STDC__ static void putbit( bit b ) -#else /*__STDC__*/ -static void -putbit( b ) - bit b; -#endif /*__STDC__*/ { if ( bitsperitem == 6 ) putitem( ); diff --git a/converter/pbm/pbmtox10bm b/converter/pbm/pbmtox10bm new file mode 100644 index 00000000..9a1a7286 --- /dev/null +++ b/converter/pbm/pbmtox10bm @@ -0,0 +1,45 @@ +#! /usr/bin/perl + +#============================================================================ +# This is a compatibility interface to Pbmtoxbm. +# +# It exists so existing programs and procedures that rely on Pbmtox10bm +# syntax continue to work. You should not make new use of Pbmtox10bm and +# if you modify an old use, you should upgrade it to use Pbmtoxbm. +# +# Pbmtoxbm with the -x10 option is backward compatible with Pbmtox10bm. +#============================================================================ + +use strict; +use File::Basename; +use Cwd 'abs_path'; + +my $infile; + +foreach (@ARGV) { + if (/^-/) { + # It's an option. But Pbmtox10bm didn't have any options. + print(STDERR "Invalid option '$_'\n"); + exit(10); + } else { + # It's a parameter + if (defined($infile)) { + print(STDERR + "You may specify at most one non-option parameter.\n"); + exit(10); + } else { + $infile = $_; + } + } +} + +my $infileParm = defined($infile) ? $infile : "-"; + +# We want to get Pbmtoxbm from the same directory we came from if +# it's there. Frequently, the directory containing Netpbm programs is +# not in the PATH and we were invoked by absolute path. + +my $my_directory = abs_path(dirname($0)); +$ENV{"PATH"} = $my_directory . ":" . $ENV{"PATH"}; + +exec('pbmtoxbm', '-x10', $infileParm); diff --git a/converter/pbm/pbmtox10bm.c b/converter/pbm/pbmtox10bm.c deleted file mode 100644 index ef31fb9b..00000000 --- a/converter/pbm/pbmtox10bm.c +++ /dev/null @@ -1,120 +0,0 @@ -/* pbmtox10bm.c - read a portable bitmap and produce an X10 bitmap file -** -** Copyright (C) 1988 by Jef Poskanzer. -** -** Permission to use, copy, modify, and distribute this software and its -** documentation for any purpose and without fee is hereby granted, provided -** that the above copyright notice appear in all copies and that both that -** copyright notice and this permission notice appear in supporting -** documentation. This software is provided "as is" without express or -** implied warranty. -*/ - -#include - -#include "nstring.h" -#include "pbm.h" - -int -main(int argc, char * argv[]) { - - FILE* ifp; - bit* bitrow; - bit * bP; - int rows, cols, format, padright, row; - int col; - char name[100]; - char* cp; - int itemsperline; - int bitsperitem; - int item; - int firstitem; - const char* const hexchar = "0123456789abcdef"; - - - pbm_init( &argc, argv ); - - if ( argc > 2 ) - pm_usage( "[pbmfile]" ); - - if ( argc == 2 ) - { - ifp = pm_openr( argv[1] ); - strcpy( name, argv[1] ); - if (STREQ( name, "-" )) - strcpy( name, "noname" ); - - if ( ( cp = strchr( name, '.' ) ) != 0 ) - *cp = '\0'; - } - else - { - ifp = stdin; - strcpy( name, "noname" ); - } - - pbm_readpbminit( ifp, &cols, &rows, &format ); - bitrow = pbm_allocrow( cols ); - - /* Compute padding to round cols up to the nearest multiple of 16. */ - padright = ( ( cols + 15 ) / 16 ) * 16 - cols; - - printf( "#define %s_width %d\n", name, cols ); - printf( "#define %s_height %d\n", name, rows ); - printf( "static short %s_bits[] = {\n", name ); - - itemsperline = 0; - bitsperitem = 0; - item = 0; - firstitem = 1; - -#define PUTITEM \ - { \ - if ( firstitem ) \ - firstitem = 0; \ - else \ - putchar( ',' ); \ - if ( itemsperline == 11 ) \ - { \ - putchar( '\n' ); \ - itemsperline = 0; \ - } \ - if ( itemsperline == 0 ) \ - putchar( ' ' ); \ - ++itemsperline; \ - putchar('0'); \ - putchar('x'); \ - putchar(hexchar[item >> 12]); \ - putchar(hexchar[(item >> 8) & 15]); \ - putchar(hexchar[(item >> 4) & 15]); \ - putchar(hexchar[item & 15]); \ - bitsperitem = 0; \ - item = 0; \ - } - -#define PUTBIT(b) \ - { \ - if ( bitsperitem == 16 ) \ - PUTITEM; \ - if ( (b) == PBM_BLACK ) \ - item += 1 << bitsperitem; \ - ++bitsperitem; \ - } - - for ( row = 0; row < rows; ++row ) - { - pbm_readpbmrow( ifp, bitrow, cols, format ); - for ( col = 0, bP = bitrow; col < cols; ++col, ++bP ) - PUTBIT(*bP); - for ( col = 0; col < padright; ++col ) - PUTBIT(0); - } - - pm_close( ifp ); - - if ( bitsperitem > 0 ) - PUTITEM; - printf( "};\n" ); - - return 0; -} diff --git a/converter/pbm/pbmtoxbm.c b/converter/pbm/pbmtoxbm.c index 96830a0c..eb45f11b 100644 --- a/converter/pbm/pbmtoxbm.c +++ b/converter/pbm/pbmtoxbm.c @@ -1,4 +1,4 @@ -/* pbmtoxbm.c - read a portable bitmap and produce an X11 bitmap file +/* pbmtoxbm.c - read a PBM image and produce an X11/X10 bitmap file ** ** Copyright (C) 1988 by Jef Poskanzer. ** @@ -10,17 +10,113 @@ ** implied warranty. */ +/* 2006.10 (afu) + Changed bitrow from plain to raw, read function from pbm_readpbmrow() to + pbm_readpbmrow_packed(). Retired bitwise transformation functions. + + Output function putitem rewritten to handle both X10 and X11. + + Added -name option. There is no check for the string thus given. + +*/ + #define _BSD_SOURCE 1 /* Make sure strdup() is in string.h */ #define _XOPEN_SOURCE 500 /* Make sure strdup() is in string.h */ #include #include "pbm.h" +#include "shhopt.h" +#include "mallocvar.h" +#include "bitreverse.h" #include "nstring.h" +enum xbmVersion { X10, X11 }; + +struct cmdlineInfo { + /* All the information the user supplied in the command line, + in a form easy for the program to use. + */ + const char * inputFileName; + const char * name; + enum xbmVersion xbmVersion; +}; + static void -generateName(char const filenameArg[], const char ** const nameP) { +parseCommandLine(int argc, + char ** argv, + struct cmdlineInfo *cmdlineP ) { +/*---------------------------------------------------------------------------- + Parse program command line described in Unix standard form by argc + and argv. Return the information in the options as *cmdlineP. + + If command line is internally inconsistent (invalid options, etc.), + issue error message to stderr and abort program. + + Note that the strings we return are stored in the storage that + was passed to us as the argv array. We also trash *argv. +-----------------------------------------------------------------------------*/ + optEntry *option_def; + /* Instructions to optParseOptions3 on how to parse our options. */ + + optStruct3 opt; + unsigned int option_def_index; + unsigned int x10, x11, nameSpec; + MALLOCARRAY_NOFAIL(option_def, 100); + + option_def_index = 0; /* incremented by OPTENT3 */ + + OPTENT3(0, "name", OPT_STRING, &cmdlineP->name, &nameSpec, 0); + OPTENT3(0, "x10" , OPT_FLAG, NULL, &x10, 0); + OPTENT3(0, "x11" , OPT_FLAG, NULL, &x11, 0); + + opt.opt_table = option_def; + opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ + opt.allowNegNum = FALSE; /* We have no parms that are negative numbers */ + + optParseOptions3( &argc, argv, opt, sizeof(opt), 0); + /* Uses and sets argc, argv, and some of *cmdlineP and others. */ + + if (!nameSpec) + cmdlineP->name = NULL; + else if (strlen(cmdlineP->name) > 56) + pm_error("Image name too long: %d chars. (max 56)", + strlen(cmdlineP->name)); + else if (!ISALPHA(cmdlineP->name[0]) && cmdlineP->name[0] !='_') + pm_error("Image name '%s' starts with non-alphabet character.", + cmdlineP->name); + else { + unsigned int i; + for (i = 0 ; i < strlen(cmdlineP->name); ++i) + if (!ISALNUM(cmdlineP->name[i]) && cmdlineP->name[i] != '_') + pm_error("Image name '%s' contains invalid character (%c).", + cmdlineP->name, cmdlineP->name[i]); + } + + if (x10 && x11) + pm_error("You can't specify both -x10 and -x11"); + else if (x10) + cmdlineP->xbmVersion = X10; + else + cmdlineP->xbmVersion = X11; + + if (argc-1 < 1) + cmdlineP->inputFileName = "-"; + else { + cmdlineP->inputFileName = argv[1]; + + if (argc-1 > 1) + pm_error("Program takes zero or one argument (filename). You " + "specified %u", argc-1); + } +} + + + +static void +generateName(char const filenameArg[], + const char ** const nameP) { /*---------------------------------------------------------------------------- Generate a name for the image to put in the bitmap file. Derive it from the filename argument filenameArg[] and return it as a null-terminated @@ -67,99 +163,235 @@ generateName(char const filenameArg[], const char ** const nameP) { -int -main(int argc, char * argv[]) { +static unsigned short int itemBuff[22]; +static int itemCnt; /* takes values 0 to 15 (x11) or 21 (x10) */ +static enum xbmVersion itemVersion; - FILE* ifp; - bit* bitrow; - int rows, cols, format; - int padright; - int row; - const char * inputFilename; - const char *name; - int itemsperline; - int bitsperitem; - int item; - int firstitem; - const char hexchar[] = "0123456789abcdef"; - pbm_init(&argc, argv); - if (argc-1 > 1) - pm_error("Too many arguments (%d). The only valid argument is an " - "input file name.", argc-1); - else if (argc-1 == 1) - inputFilename = argv[1]; - else - inputFilename = "-"; +static void +putitemX10(unsigned char const item) { - generateName(inputFilename, &name); - ifp = pm_openr(inputFilename); - - pbm_readpbminit(ifp, &cols, &rows, &format); - bitrow = pbm_allocrow(cols); - - /* Compute padding to round cols up to the nearest multiple of 8. */ - padright = ((cols + 7)/8) * 8 - cols; - - printf("#define %s_width %d\n", name, cols); - printf("#define %s_height %d\n", name, rows); - printf("static char %s_bits[] = {\n", name); - - itemsperline = 0; - bitsperitem = 0; - item = 0; - firstitem = 1; - -#define PUTITEM \ - { \ - if ( firstitem ) \ - firstitem = 0; \ - else \ - putchar( ',' ); \ - if ( itemsperline == 15 ) \ - { \ - putchar( '\n' ); \ - itemsperline = 0; \ - } \ - if ( itemsperline == 0 ) \ - putchar( ' ' ); \ - ++itemsperline; \ - putchar('0'); \ - putchar('x'); \ - putchar(hexchar[item >> 4]); \ - putchar(hexchar[item & 15]); \ - bitsperitem = 0; \ - item = 0; \ + if (itemCnt == 22) { + /* Buffer is full. Write out one line. */ + int rc; + rc = printf(" 0x%02x%02x,0x%02x%02x,0x%02x%02x,0x%02x%02x," + "0x%02x%02x,0x%02x%02x,0x%02x%02x,0x%02x%02x," + "0x%02x%02x,0x%02x%02x,0x%02x%02x,\n", + itemBuff[ 1], itemBuff[ 0], itemBuff[ 3], itemBuff[ 2], + itemBuff[ 5], itemBuff[ 4], itemBuff[ 7], itemBuff[ 6], + itemBuff[ 9], itemBuff[ 8], itemBuff[11], itemBuff[10], + itemBuff[13], itemBuff[12], itemBuff[15], itemBuff[14], + itemBuff[17], itemBuff[16], itemBuff[19], itemBuff[18], + itemBuff[21], itemBuff[20] + ); + + if (rc < 0) + pm_error("Error writing X10 bitmap raster item. " + "printf() failed with errno %d (%s)", + errno, strerror(errno)); + + itemCnt = 0; } + itemBuff[itemCnt++] = bitreverse[item]; +} + -#define PUTBIT(b) \ - { \ - if ( bitsperitem == 8 ) \ - PUTITEM; \ - if ( (b) == PBM_BLACK ) \ - item += 1 << bitsperitem; \ - ++bitsperitem; \ + +static void +putitemX11(unsigned char const item) { + + if (itemCnt == 15 ) { + /* Buffer is full. Write out one line. */ + int rc; + rc = printf(" 0x%02x,0x%02x,0x%02x,0x%02x," + "0x%02x,0x%02x,0x%02x,0x%02x," + "0x%02x,0x%02x,0x%02x,0x%02x," + "0x%02x,0x%02x,0x%02x,\n", + itemBuff[0], itemBuff[1], itemBuff[2], itemBuff[3], + itemBuff[4], itemBuff[5], itemBuff[6], itemBuff[7], + itemBuff[8], itemBuff[9], itemBuff[10],itemBuff[11], + itemBuff[12],itemBuff[13],itemBuff[14] + ); + if (rc < 0) + pm_error("Error writing X11 bitmap raster item. " + "printf() failed with errno %d (%s)", + errno, strerror(errno)); + + itemCnt = 0; } + itemBuff[itemCnt++] = bitreverse[item]; +} - for (row = 0; row < rows; ++row) { - int col; - pbm_readpbmrow(ifp, bitrow, cols, format); - for (col = 0; col < cols; ++col) - PUTBIT(bitrow[col]); - for (col = 0; col < padright; ++col) - PUTBIT(0); + + +static void +putitem(unsigned char const item) { + + switch (itemVersion) { + case X10: putitemX10(item); break; + case X11: putitemX11(item); break; + } +} + + + +static void +puttermX10(void) { + + unsigned int i; + + for (i = 0; i < itemCnt; i += 2) { + int rc; + + rc = printf("%s0x%02x%02x%s", + (i == 0) ? " " : "", + itemBuff[i+1], + itemBuff[i], + (i == itemCnt - 2) ? "};\n" : ","); + if (rc < 0) + pm_error("Error writing end of X10 bitmap raster. " + "printf() failed with errno %d (%s)", + errno, strerror(errno)); + } +} + + + +static void +puttermX11(void) { + + unsigned int i; + + for (i = 0; i < itemCnt; ++i) { + int rc; + + rc = printf("%s0x%02x%s", + (i == 0) ? " " : "", + itemBuff[i], + (i == itemCnt - 1) ? "};\n" : ","); + + if (rc < 0) + pm_error("Error writing end of X11 bitmap raster. " + "printf() failed with errno %d (%s)", + errno, strerror(errno)); } +} + + + +static void +putinit(enum xbmVersion const xbmVersion) { + + itemCnt = 0; + itemVersion = xbmVersion; +} + - pm_close(ifp); - if (bitsperitem > 0) - PUTITEM; - printf("};\n"); +static void +putterm(void) { + + switch (itemVersion) { + case X10: puttermX10(); break; + case X11: puttermX11(); break; + } +} + + + +static void +writeXbmHeader(enum xbmVersion const xbmVersion, + const char * const name, + unsigned int const width, + unsigned int const height, + FILE * const ofP) { + + printf("#define %s_width %d\n", name, width); + printf("#define %s_height %d\n", name, height); + printf("static %s %s_bits[] = {\n", + xbmVersion == X10 ? "short" : "char", + name); +} + + + +static void +convertRaster(FILE * const ifP, + unsigned int const cols, + unsigned int const rows, + int const format, + FILE * const ofP, + enum xbmVersion const xbmVersion) { + + unsigned int const bitsPerUnit = xbmVersion == X10 ? 16 : 8; + unsigned int const padright = + ((cols + bitsPerUnit - 1 ) / bitsPerUnit) * bitsPerUnit - cols; + /* Amount of padding to round cols up to the nearest multiple of + 8 (if x11) or 16 (if x10). + */ + unsigned int const bitrowBytes = (cols + padright) / 8; + + unsigned char * bitrow; + unsigned int row; + + putinit(xbmVersion); + + bitrow = pbm_allocrow_packed(cols + padright); + bitrow[bitrowBytes-1] = 0; + + for (row = 0; row < rows; ++row) { + int const bitrowInBytes = pbm_packed_bytes(cols); + int const padrightIn = bitrowInBytes * 8 - cols; + + unsigned int i; + + pbm_readpbmrow_packed(ifP, bitrow, cols, format); + + if (padrightIn > 0) { + bitrow[bitrowInBytes - 1] >>= padrightIn; + bitrow[bitrowInBytes - 1] <<= padrightIn; + } + + for (i = 0; i < bitrowBytes; ++i) + putitem(bitrow[i]); + } + + putterm(); pbm_freerow(bitrow); +} + + + +int +main(int argc, + char * argv[]) { + + struct cmdlineInfo cmdline; + FILE * ifP; + int rows, cols, format; + const char * name; + + pbm_init(&argc, argv); + + parseCommandLine(argc, argv, &cmdline); + if (cmdline.name == NULL) + generateName(cmdline.inputFileName, &name); + else + name = strdup(cmdline.name); + + ifP = pm_openr(cmdline.inputFileName); + + pbm_readpbminit(ifP, &cols, &rows, &format); + + writeXbmHeader(cmdline.xbmVersion, name, cols, rows, stdout); + + convertRaster(ifP, cols, rows, format, stdout, cmdline.xbmVersion); strfree(name); + pm_close(ifP); - exit(0); + return 0; } + diff --git a/converter/pbm/pbmtoybm.c b/converter/pbm/pbmtoybm.c index 1d2be3d9..508e8e92 100644 --- a/converter/pbm/pbmtoybm.c +++ b/converter/pbm/pbmtoybm.c @@ -80,14 +80,8 @@ putinit( cols, rows ) bitshift = 0; } -#if __STDC__ static void putbit( bit b ) -#else /*__STDC__*/ -static void -putbit( b ) - bit b; -#endif /*__STDC__*/ { if ( bitsperitem == 16 ) putitem( ); diff --git a/converter/pbm/xbmtopbm.c b/converter/pbm/xbmtopbm.c index 7779a9b5..63be16ef 100644 --- a/converter/pbm/xbmtopbm.c +++ b/converter/pbm/xbmtopbm.c @@ -1,4 +1,4 @@ -/* xbmtopbm.c - read an X bitmap file and produce a portable bitmap +/* xbmtopbm.c - read an X bitmap file and produce a PBM image ** ** Copyright (C) 1988 by Jef Poskanzer. ** @@ -10,246 +10,381 @@ ** implied warranty. */ + +#include #include +#include "pm_c_util.h" +#include "mallocvar.h" #include "nstring.h" #include "pbm.h" +#include "bitreverse.h" -#define TRUE 1 -#define FALSE 0 -static void ReadBitmapFile ARGS(( FILE* stream, int* widthP, int* heightP, char** dataP )); -int -main( argc, argv ) - int argc; - char* argv[]; - { - FILE* ifp; - bit* bitrow; - register bit* bP; - int rows, cols, row, col, charcount; - char* data; - char mask; - - pbm_init( &argc, argv ); - - if ( argc > 2 ) - pm_usage( "[bitmapfile]" ); - - if ( argc == 2 ) - ifp = pm_openr( argv[1] ); - else - ifp = stdin; - - ReadBitmapFile( ifp, &cols, &rows, &data ); - - pm_close( ifp ); - - pbm_writepbminit( stdout, cols, rows, 0 ); - bitrow = pbm_allocrow( cols ); - - for ( row = 0; row < rows; ++row ) - { - charcount = 0; - mask = 1; - for ( col = 0, bP = bitrow; col < cols; ++col, ++bP ) - { - if ( charcount >= 8 ) - { - ++data; - charcount = 0; - mask = 1; - } - *bP = ( *data & mask ) ? PBM_BLACK : PBM_WHITE; - ++charcount; - mask = mask << 1; - } - ++data; - pbm_writepbmrow( stdout, bitrow, cols, 0 ); - } +#define MAX_LINE 500 + +static unsigned int hexTable[256]; + /* Hexadecimal ASCII translation table. Constant */ + +static void +initHexTable(void) { + + unsigned int i; + + for (i = 0; i < 256; ++i) + hexTable[i] = 256; + + hexTable['0'] = 0; + hexTable['1'] = 1; + hexTable['2'] = 2; + hexTable['3'] = 3; + hexTable['4'] = 4; + hexTable['5'] = 5; + hexTable['6'] = 6; + hexTable['7'] = 7; + hexTable['8'] = 8; + hexTable['9'] = 9; + hexTable['A'] = 10; + hexTable['B'] = 11; + hexTable['C'] = 12; + hexTable['D'] = 13; + hexTable['E'] = 14; + hexTable['F'] = 15; + hexTable['a'] = 10; + hexTable['b'] = 11; + hexTable['c'] = 12; + hexTable['d'] = 13; + hexTable['e'] = 14; + hexTable['f'] = 15; +} - pm_close( stdout ); - exit( 0 ); - } -#define MAX_LINE 500 static void -ReadBitmapFile( stream, widthP, heightP, dataP ) - FILE* stream; - int* widthP; - int* heightP; - char** dataP; -{ - char line[MAX_LINE], name_and_type[MAX_LINE]; - char* ptr; - char* t; - int version10, raster_length, v; - register int bytes, bytes_per_line, padding; - register int c1, c2, value1, value2; - int hex_table[256]; - int found_declaration; - /* In scanning through the bitmap file, we have found the first - line of the C declaration of the array (the "static char ..." - or whatever line) - */ - int eof; - /* We've encountered end of file while searching file */ - - *widthP = *heightP = -1; - - found_declaration = FALSE; /* Haven't found it yet; haven't even looked*/ - eof = FALSE; /* Haven't encountered end of file yet */ - - while (!found_declaration && !eof) { - if ( fgets( line, MAX_LINE, stream ) == NULL ) - eof = TRUE; - else { - if ( strlen( line ) == MAX_LINE - 1 ) - pm_error( "line too long" ); - - if ( sscanf( line, "#define %s %d", name_and_type, &v ) == 2 ) { - if ( ( t = strrchr( name_and_type, '_' ) ) == NULL ) - t = name_and_type; +parseWidthHeightLine(const char * const line, + bool * const gotWidthP, + unsigned int * const widthP, + bool * const gotHeightP, + unsigned int * const heightP) { + + int rc; + char nameAndType[MAX_LINE]; + unsigned int value; + + rc = sscanf(line, "#define %s %u", nameAndType, &value); + if (rc == 2) { + const char * underscorePos = strrchr(nameAndType, '_'); + const char * type; + if (underscorePos) + type = underscorePos + 1; else - ++t; - if ( STREQ( "width", t ) ) - *widthP = v; - else if ( STREQ( "height", t ) ) - *heightP = v; - continue; - } + type = nameAndType; + if (STREQ(type, "width")) { + *gotWidthP = TRUE; + *widthP = value; + } else if (STREQ(type, "height")) { + *gotHeightP = TRUE; + *heightP = value; + } + } +} + + + +static void +parseDeclaration(const char * const line, + bool * const isDeclarationP, + bool * const version10P) { +/*---------------------------------------------------------------------------- + Parse the XBM file line 'line' as the first line of the data structure + declaration, i.e. the one that looks like this: + + static unsigned char myImage = { + + Return as *isDeclarationP whether the line actually is such a line, + and if so, return as nameAndType what the variable name ('myImage' + in the example) is and as *version10P whether it's of the type used + by X10 as opposed to X11. +-----------------------------------------------------------------------------*/ + char nameAndType[MAX_LINE]; + int rc; - if ( sscanf( line, "static short %s = {", name_and_type ) == 1 ) { - version10 = TRUE; - found_declaration = TRUE; - } - else if ( sscanf( line, "static char %s = {", name_and_type ) == 1 ) { - version10 = FALSE; - found_declaration = TRUE; - } - else if (sscanf(line, - "static unsigned char %s = {", name_and_type ) == 1 ) { - version10 = FALSE; - found_declaration = TRUE; - } + rc = sscanf(line, "static short %s = {", nameAndType); + if (rc == 1) { + *version10P = TRUE; + *isDeclarationP = TRUE; + } else { + int rc; + rc = sscanf(line, "static char %s = {", nameAndType); + if (rc == 1) { + *version10P = FALSE; + *isDeclarationP = TRUE; + } else { + int rc; + rc = sscanf(line, "static unsigned char %s = {", nameAndType); + if (rc == 1) { + *version10P = FALSE; + *isDeclarationP = TRUE; + } else + *isDeclarationP = FALSE; + } } - } - - if (!found_declaration) - pm_error("Unable to find a line in the file containing the start " - "of C array declaration (\"static char\" or whatever)"); - - if ( *widthP == -1 ) - pm_error( "invalid width" ); - if ( *heightP == -1 ) - pm_error( "invalid height" ); - - padding = 0; - if ( ((*widthP % 16) >= 1) && ((*widthP % 16) <= 8) && version10 ) - padding = 1; - - bytes_per_line = (*widthP+7)/8 + padding; +} + + + +static void +getXbmHeader(FILE * const ifP, + unsigned int * const widthP, + unsigned int * const heightP, + bool * const version10P) { + + bool foundDeclaration; + /* In scanning through the bitmap file, we have found the first + line of the C declaration of the array (the "static char ..." + or whatever line) + */ + bool gotWidth, gotHeight; + /* We found the line in the bitmap file that gives the width + or height, respectively, of the image (and have set + *widthP or *heightP to the value in it). + */ + + bool eof; + /* We've encountered end of file while searching file */ + + gotWidth = FALSE; + gotHeight = FALSE; + foundDeclaration = FALSE; /* Haven't found it yet; haven't even looked*/ + eof = FALSE; /* Haven't encountered end of file yet */ + + while (!foundDeclaration && !eof) { + char * rc; + char line[MAX_LINE]; + + rc = fgets(line, MAX_LINE, ifP); + if (rc == NULL) + eof = TRUE; + else { + if (strlen(line) == MAX_LINE - 1) + pm_error("A line in the input file is %u characters long. " + "%u is the maximum we can handle", + strlen(line), MAX_LINE-1); + + parseWidthHeightLine(line, &gotWidth, widthP, &gotHeight, heightP); + + parseDeclaration(line, &foundDeclaration, version10P); + } + } + + if (!foundDeclaration) + pm_error("Unable to find a line in the file containing the start " + "of C array declaration (\"static char\" or whatever)"); + + if (!gotWidth) + pm_error("Unable to find the #define statement that gives the " + "width of the image, before the data structure " + "declaration."); + if (!gotHeight) + pm_error("Unable to find the #define statement that gives the " + "height of the image, before the data structure " + "declaration."); +} + + + +static void +getHexByte(FILE * const ifP, + unsigned int * const valueP) { + + int c1, c2; + unsigned int value; + + c1 = getc(ifP); + c2 = getc(ifP); + if (c1 == EOF || c2 == EOF) + pm_error("EOF / read error"); + + assert(c1 >= 0); assert(c1 < 256); + assert(c2 >= 0); assert(c2 < 256); - raster_length = bytes_per_line * *heightP; - *dataP = (char*) malloc( raster_length ); - if ( *dataP == (char*) 0 ) - pm_error( "out of memory" ); - - /* Initialize hex_table. */ - for ( c1 = 0; c1 < 256; ++c1 ) - hex_table[c1] = 256; - hex_table['0'] = 0; - hex_table['1'] = 1; - hex_table['2'] = 2; - hex_table['3'] = 3; - hex_table['4'] = 4; - hex_table['5'] = 5; - hex_table['6'] = 6; - hex_table['7'] = 7; - hex_table['8'] = 8; - hex_table['9'] = 9; - hex_table['A'] = 10; - hex_table['B'] = 11; - hex_table['C'] = 12; - hex_table['D'] = 13; - hex_table['E'] = 14; - hex_table['F'] = 15; - hex_table['a'] = 10; - hex_table['b'] = 11; - hex_table['c'] = 12; - hex_table['d'] = 13; - hex_table['e'] = 14; - hex_table['f'] = 15; - - if ( version10 ) - for ( bytes = 0, ptr = *dataP; bytes < raster_length; bytes += 2 ) { - while ( ( c1 = getc( stream ) ) != 'x' ) - if ( c1 == EOF ) - pm_error( "EOF / read error" ); - c1 = getc( stream ); - c2 = getc( stream ); - if ( c1 == EOF || c2 == EOF ) - pm_error( "EOF / read error" ); - value1 = ( hex_table[c1] << 4 ) + hex_table[c2]; - if ( value1 >= 256 ) - pm_error( "syntax error" ); - c1 = getc( stream ); - c2 = getc( stream ); - if ( c1 == EOF || c2 == EOF ) - pm_error( "EOF / read error" ); - value2 = ( hex_table[c1] << 4 ) + hex_table[c2]; - if ( value2 >= 256 ) - pm_error( "syntax error" ); - *ptr++ = value2; - if ( ( ! padding ) || ( ( bytes + 2 ) % bytes_per_line ) ) - *ptr++ = value1; + value = (hexTable[c1] << 4) + hexTable[c2]; + if (value >= 256) + pm_error("Invalid XBM input. What should be a two digit " + "hexadecimal cipher is instead '%c%c'", c1, c2); + + *valueP = value; +} + + + +static void +readX10Raster(FILE * const ifP, + unsigned int const rasterLength, + unsigned char * const data, + unsigned int const bytesPerLine, + bool const mustPad) { + + unsigned int bytesDone; + unsigned char * p; + + for (bytesDone = 0, p = &data[0]; + bytesDone < rasterLength; + bytesDone += 2) { + + unsigned int value1; + unsigned int value2; + + while (getc(ifP) != 'x') {} /* Read up through the 'x' in 0x1234 */ + + getHexByte(ifP, &value1); /* Read first two hex digits */ + getHexByte(ifP, &value2); /* Read last two hex digits */ + + *p++ = value2; + + if (!mustPad || ((bytesDone + 2) % bytesPerLine)) + *p++ = value1; } - else - for ( bytes = 0, ptr = *dataP; bytes < raster_length; ++bytes ) { - /* - ** Skip until digit is found. - */ - for ( ; ; ) - { - c1 = getc( stream ); - if ( c1 == EOF ) - pm_error( "EOF / read error" ); - value1 = hex_table[c1]; - if ( value1 != 256 ) - break; - } - /* - ** Loop on digits. - */ - for ( ; ; ) { - c2 = getc( stream ); - if ( c2 == EOF ) - pm_error( "EOF / read error" ); - value2 = hex_table[c2]; - if ( value2 != 256 ) { - value1 = (value1 << 4) | value2; - if ( value1 >= 256 ) - pm_error( "syntax error" ); +} + + + +static void +readX11Raster(FILE * const ifP, + unsigned int const rasterLength, + unsigned char * data) { + + unsigned int i; + + for (i = 0; i < rasterLength; ++i) { + unsigned int value; + int c; + + /* Read up through the 'x' in 0x12 */ + while ((c = getc(ifP))) { + if (c == EOF) + pm_error("EOF where 0x expected"); + else if (toupper(c) == 'X') + break; } - else if ( c2 == 'x' || c2 == 'X' ) - if ( value1 == 0 ) - continue; - else pm_error( "syntax error" ); - else break; - } - *ptr++ = value1; + + getHexByte(ifP, &value); /* Read the two hex digits */ + + assert(value < 256); + + data[i] = value; } } -/* CHANGE HISTORY: - 99.09.08 bryanh Recognize "static unsigned char" declaration. +static void +readBitmapFile(FILE * const ifP, + unsigned int * const widthP, + unsigned int * const heightP, + unsigned char ** const dataP) { + + bool version10; + unsigned int rasterLength; + unsigned int width, height; + unsigned char * data; + unsigned int bytesPerLine; + bool mustPad; + getXbmHeader(ifP, &width, &height, &version10); + *widthP = width; + *heightP = height; + mustPad = (width % 16 >= 1 && width % 16 <= 8 && version10); + + bytesPerLine = (width + 7) / 8 + (mustPad ? 1 : 0); + + rasterLength = bytesPerLine * height; + + MALLOCARRAY(data, rasterLength); + if (data == NULL) + pm_error("Unable to allocate memory for the %u-byte raster", + rasterLength); + + if (version10) + readX10Raster(ifP, rasterLength, data, bytesPerLine, mustPad); + else + readX11Raster(ifP, rasterLength, data); + + *dataP = data; +} + + + +int +main(int argc, + char * argv[]) { + + FILE * ifP; + bit * bitrow; + unsigned int rows, cols; + unsigned int row; + unsigned char * data; + const char * inputFileName; + unsigned char * p; + /* Cursor in raster data data[] */ + + initHexTable(); + + pbm_init(&argc, argv); + + if (argc-1 > 1) + pm_error("The only possible argument is the input file name. " + "You specified %u arguments", argc-1); + + if (argc-1 > 0) + inputFileName = argv[1]; + else + inputFileName = "-"; + + ifP = pm_openr(inputFileName); + + readBitmapFile(ifP, &cols, &rows, &data); + + pm_close(ifP); + + pbm_writepbminit(stdout, cols, rows, 0); + bitrow = pbm_allocrow_packed(cols); + + p = &data[0]; /* Start at beginning of raster */ + + for (row = 0; row < rows; ++row) { + unsigned int const bytesPerRow = pbm_packed_bytes(cols); + unsigned int i; + + for (i = 0; i < bytesPerRow; ++i) + bitrow[i] = bitreverse[*p++]; + + if (cols % 8 > 0) { + bitrow[bytesPerRow] >>= 8 - cols % 8; + bitrow[bytesPerRow] <<= 8 - cols % 8; + } + + pbm_writepbmrow_packed(stdout, bitrow, cols, 0); + } + + free(data); + pm_close(stdout); + + return 0; +} + +/* CHANGE HISTORY: + + 99.09.08 bryanh Recognize "static unsigned char" declaration. + + 06.10 (afu) + Changed bitrow from plain to raw, write function from pbm_writepbmrow() + to pbm_writepbmrow_packed(). + Retired bitwise transformation functions. */ + diff --git a/converter/pgm/pgmtofs.c b/converter/pgm/pgmtofs.c index b34d77c4..53a2e7b3 100644 --- a/converter/pgm/pgmtofs.c +++ b/converter/pgm/pgmtofs.c @@ -128,14 +128,8 @@ putitem( ) bitshift = 8 - bitspersample; } -#if __STDC__ static void putgray( gray g ) -#else /*__STDC__*/ - static void -putgray( g ) - gray g; -#endif /*__STDC__*/ { if ( bitsperitem == 8 ) putitem( ); diff --git a/converter/pgm/pgmtolispm.c b/converter/pgm/pgmtolispm.c index 02f2fd1e..abb85494 100644 --- a/converter/pgm/pgmtolispm.c +++ b/converter/pgm/pgmtolispm.c @@ -115,14 +115,8 @@ depth_to_word_size (depth) /* Lispm architecture specific - if a bitmap is writt -#if __STDC__ static void putval( gray b ) -#else /*__STDC__*/ -static void -putval( b ) -gray b; -#endif /*__STDC__*/ { if ( bitsperitem == 32 ) putitem( ); diff --git a/converter/ppm/ppmtogif.c b/converter/ppm/ppmtogif.c index 3100fea1..2b8a2596 100644 --- a/converter/ppm/ppmtogif.c +++ b/converter/ppm/ppmtogif.c @@ -1,101 +1,56 @@ -/* ppmtogif.c - read a portable pixmap and produce a GIF file -** -** Based on GIFENCOD by David Rowley .A -** Lempel-Zim compression based on "compress". -** -** Modified by Marcel Wijkstra -** -** The non-LZW GIF generation stuff was adapted from the Independent -** JPEG Group's djpeg on 2001.09.29. The uncompressed output subroutines -** are derived directly from the corresponding subroutines in djpeg's -** wrgif.c source file. Its copyright notice say: - - * Copyright (C) 1991-1997, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - The reference README file is README.JPEG in the Netpbm package. -** -** Copyright (C) 1989 by Jef Poskanzer. -** -** Permission to use, copy, modify, and distribute this software and its -** documentation for any purpose and without fee is hereby granted, provided -** that the above copyright notice appear in all copies and that both that -** copyright notice and this permission notice appear in supporting -** documentation. This software is provided "as is" without express or -** implied warranty. -** -** The Graphics Interchange Format(c) is the Copyright property of -** CompuServe Incorporated. GIF(sm) is a Service Mark property of -** CompuServe Incorporated. -*/ +/* This is a backward compatibility interface to Pamtogif. + + Pamtogif replaced Ppmtogif in Netpbm 10.37 (December 2006). -/* TODO: merge the LZW and uncompressed subroutines. They are separate - only because they had two different lineages and the code is too - complicated for me quickly to rewrite it. + The only significant way Pamtogif is not backward compatible with + old Ppmtogif is that Pamtogif does not have a -alpha option. */ -#include +#define _BSD_SOURCE /* Make sure strdup() is in string.h */ +#define _XOPEN_SOURCE 500 /* Make sure strdup() is in string.h */ + #include +#include +#include +#include #include "mallocvar.h" #include "shhopt.h" -#include "ppm.h" +#include "nstring.h" +#include "pam.h" -#define MAXCMAPSIZE 256 -static unsigned int const gifMaxval = 255; -static bool verbose; -/* - * a code_int must be able to hold 2**BITS values of type int, and also -1 - */ -typedef int code_int; +static const char * +dirname(const char * const fileName) { -typedef long int count_int; + char * buffer; + char * slashPos; + buffer = strdup(fileName); + + slashPos = strchr(buffer, '/'); + + if (slashPos) + *slashPos = '\0'; + + return buffer; +} -struct cmap { - /* This is the information for the GIF colormap (aka palette). */ - int red[MAXCMAPSIZE], green[MAXCMAPSIZE], blue[MAXCMAPSIZE]; - /* These arrays arrays map a color index, as is found in - the raster part of the GIF, to an intensity value for the indicated - RGB component. - */ - int perm[MAXCMAPSIZE], permi[MAXCMAPSIZE]; - /* perm[i] is the position in the sorted colormap of the color which - is at position i in the unsorted colormap. permi[] is the inverse - function of perm[]. - */ - int cmapsize; - /* Number of entries in the GIF colormap. I.e. number of colors - in the image, plus possibly one fake transparency color. - */ - int transparent; - /* color index number in GIF palette of the color that is to be - transparent. -1 if no color is transparent. - */ - colorhash_table cht; - /* A hash table that relates a PPM pixel value to to a pre-sort - GIF colormap index. - */ - pixval maxval; - /* The maxval for the colors in 'cht'. */ -}; 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; /* Filespec of input file */ - const char *alpha_filespec; /* Filespec of alpha file; NULL if none */ - const char *alphacolor; /* -alphacolor option value or default */ - unsigned int interlace; /* -interlace option value */ - unsigned int sort; /* -sort option value */ + const char *input_filespec; /* Filespec of input file */ + const char *alpha_filespec; /* Filespec of alpha file; NULL if none */ + const char *alphacolor; /* -alphacolor option value or default */ + unsigned int interlace; /* -interlace option value */ + unsigned int sort; /* -sort option value */ const char *mapfile; /* -mapfile option value. NULL if none. */ const char *transparent; /* -transparent option value. NULL if none. */ const char *comment; /* -comment option value; NULL if none */ - unsigned int nolzw; /* -nolzw option */ + unsigned int nolzw; /* -nolzw option */ unsigned int verbose; }; @@ -122,8 +77,8 @@ handleLatex2htmlHack(void) { -Bryan 2001.11.14 -----------------------------------------------------------------------------*/ pm_error("latex2html, you should just try the -interlace and " - "-transparent options to see if they work instead of " - "expecting a 'usage' message from -h"); + "-transparent options to see if they work instead of " + "expecting a 'usage' message from -h"); } @@ -204,1354 +159,223 @@ parseCommandLine(int argc, char ** argv, -/* - * Write out a word to the GIF file - */ -static void -Putword(int const w, FILE * const fp) { - - fputc( w & 0xff, fp ); - fputc( (w / 256) & 0xff, fp ); -} - - -static int -closestcolor(pixel const color, - pixval const maxval, - struct cmap * const cmapP) { -/*---------------------------------------------------------------------------- - Return the pre-sort colormap index of the color in the colormap *cmapP - that is closest to the color 'color', whose maxval is 'maxval'. - - Also add 'color' to the colormap hash, with the colormap index we - are returning. Caller must ensure that the color is not already in - there. ------------------------------------------------------------------------------*/ - unsigned int i; - unsigned int imin, dmin; - - pixval const r = PPM_GETR(color) * gifMaxval / maxval; - pixval const g = PPM_GETG(color) * gifMaxval / maxval; - pixval const b = PPM_GETB(color) * gifMaxval / maxval; - - dmin = SQR(255) * 3; - imin = 0; - for (i=0;i < cmapP->cmapsize; i++) { - int const d = SQR(r-cmapP->red[i]) + - SQR(g-cmapP->green[i]) + - SQR(b-cmapP->blue[i]); - if (d < dmin) { - dmin = d; - imin = i; - } - } - ppm_addtocolorhash(cmapP->cht, &color, cmapP->permi[imin]); - - return cmapP->permi[imin]; -} - - - -enum pass {MULT8PLUS0, MULT8PLUS4, MULT4PLUS2, MULT2PLUS1}; - - -struct pixelCursor { - unsigned int width; - /* Width of the image, in columns */ - unsigned int height; - /* Height of the image, in rows */ - bool interlace; - /* We're accessing the image in interlace fashion */ - unsigned int nPixelsLeft; - /* Number of pixels left to be read */ - unsigned int curCol; - /* Location of pointed-to pixel, column */ - unsigned int curRow; - /* Location of pointed-to pixel, row */ - enum pass pass; - /* The interlace pass. Undefined if !interlace */ -}; +static const char * +pamtogifCommand(const char * const arg0, + struct cmdlineInfo const cmdline) { + const char * const pamtogifName = "pamtogif"; + const char * retval; -static struct pixelCursor pixelCursor; - /* Current location in the input pixels. */ + const char * commandVerb; + const char * mapfileOpt; + const char * transparentOpt; + const char * commentOpt; + if (strchr(arg0, '/')) { + const char * const arg0DirName = dirname(arg0); + const char * progName; -static void -initPixelCursor(unsigned int const width, - unsigned int const height, - bool const interlace) { - - pixelCursor.width = width; - pixelCursor.height = height; - pixelCursor.interlace = interlace; - pixelCursor.pass = MULT8PLUS0; - pixelCursor.curCol = 0; - pixelCursor.curRow = 0; - pixelCursor.nPixelsLeft = width * height; -} + struct stat statbuf; + asprintfN(&progName, "%s/%s", arg0DirName, pamtogifName); - -static void -getPixel(pixel ** const pixels, - pixval const inputMaxval, - gray ** const alpha, - gray const alphaThreshold, - struct cmap * const cmapP, - struct pixelCursor const pixelCursor, - int * const retvalP) { -/*---------------------------------------------------------------------------- - Return as *retvalP the colormap index of the pixel at location - pointed to by 'pixelCursor' in the PPM raster 'pixels', using - colormap *cmapP. ------------------------------------------------------------------------------*/ - unsigned int const x = pixelCursor.curCol; - unsigned int const y = pixelCursor.curRow; - - int colorindex; - - if (alpha && alpha[y][x] < alphaThreshold) - colorindex = cmapP->transparent; - else { - int presortColorindex; - - presortColorindex = ppm_lookupcolor(cmapP->cht, &pixels[y][x]); - if (presortColorindex == -1) - presortColorindex = - closestcolor(pixels[y][x], inputMaxval, cmapP); - colorindex = cmapP->perm[presortColorindex]; - } - *retvalP = colorindex; -} - - - -static void -bumpRowInterlace(struct pixelCursor * const pixelCursorP) { -/*---------------------------------------------------------------------------- - Move *pixelCursorP to the next row in the interlace pattern. ------------------------------------------------------------------------------*/ - /* There are 4 passes: - MULT8PLUS0: Rows 8, 16, 24, 32, etc. - MULT8PLUS4: Rows 4, 12, 20, 28, etc. - MULT4PLUS2: Rows 2, 6, 10, 14, etc. - MULT2PLUS1: Rows 1, 3, 5, 7, 9, etc. - */ - - switch (pixelCursorP->pass) { - case MULT8PLUS0: - pixelCursorP->curRow += 8; - break; - case MULT8PLUS4: - pixelCursorP->curRow += 8; - break; - case MULT4PLUS2: - pixelCursorP->curRow += 4; - break; - case MULT2PLUS1: - pixelCursorP->curRow += 2; - break; - } - /* Set the proper pass for the next read. Note that if there are - more than 4 rows, the sequence of passes is sequential, but - when there are fewer than 4, we may skip e.g. from MULT8PLUS0 - to MULT4PLUS2. - */ - while (pixelCursorP->curRow >= pixelCursorP->height) { - switch (pixelCursorP->pass) { - case MULT8PLUS0: - pixelCursorP->pass = MULT8PLUS4; - pixelCursorP->curRow = 4; - break; - case MULT8PLUS4: - pixelCursorP->pass = MULT4PLUS2; - pixelCursorP->curRow = 2; - break; - case MULT4PLUS2: - pixelCursorP->pass = MULT2PLUS1; - pixelCursorP->curRow = 1; - break; - case MULT2PLUS1: - /* We've read the entire image; pass and current row are - now undefined. - */ - pixelCursorP->curRow = 0; - break; - } - } -} - - - -static void -bumpPixel(struct pixelCursor * const pixelCursorP) { -/*---------------------------------------------------------------------------- - Bump *pixelCursorP to point to the next pixel to go into the GIF - - Must not call when there are no pixels left. ------------------------------------------------------------------------------*/ - assert(pixelCursorP->nPixelsLeft > 0); - - /* Move one column to the right */ - ++pixelCursorP->curCol; - - if (pixelCursorP->curCol >= pixelCursorP->width) { - /* That pushed us past the end of a row. */ - /* Reset to the left edge ... */ - pixelCursorP->curCol = 0; - - /* ... of the next row */ - if (!pixelCursorP->interlace) - /* Go to the following row */ - ++pixelCursorP->curRow; + if (stat(progName, &statbuf) == 0) + commandVerb = progName; else - bumpRowInterlace(pixelCursorP); - } - --pixelCursorP->nPixelsLeft; -} - - - -static int -gifNextPixel(pixel ** const pixels, - pixval const inputMaxval, - gray ** const alpha, - gray const alphaThreshold, - struct cmap * const cmapP) { -/*---------------------------------------------------------------------------- - Return the pre-sort color index (index into the unsorted GIF color map) - of the next pixel to be processed from the input image. - - 'alpha_threshold' is the gray level such that a pixel in the alpha - map whose value is less that that represents a transparent pixel - in the output. ------------------------------------------------------------------------------*/ - int retval; - - if (pixelCursor.nPixelsLeft == 0 ) - retval = EOF; - else { - getPixel(pixels, inputMaxval, alpha, alphaThreshold, cmapP, - pixelCursor, &retval); - - bumpPixel(&pixelCursor); - } - return retval; -} - - - -static void -write_transparent_color_index_extension(FILE *fp, const int Transparent) { -/*---------------------------------------------------------------------------- - Write out extension for transparent color index. ------------------------------------------------------------------------------*/ - - fputc( '!', fp ); - fputc( 0xf9, fp ); - fputc( 4, fp ); - fputc( 1, fp ); - fputc( 0, fp ); - fputc( 0, fp ); - fputc( Transparent, fp ); - fputc( 0, fp ); -} - - - -static void -write_comment_extension(FILE *fp, const char comment[]) { -/*---------------------------------------------------------------------------- - Write out extension for a comment ------------------------------------------------------------------------------*/ - char *segment; - - fputc('!', fp); /* Identifies an extension */ - fputc(0xfe, fp); /* Identifies a comment */ - - /* Write it out in segments no longer than 255 characters */ - for (segment = (char *) comment; - segment < comment+strlen(comment); - segment += 255) { - - const int length_this_segment = MIN(255, strlen(segment)); - - fputc(length_this_segment, fp); - - fwrite(segment, 1, length_this_segment, fp); - } - - fputc(0, fp); /* No more comment blocks in this extension */ -} - - - -/*************************************************************************** - * - * GIFCOMPR.C - GIF Image compression routines - * - * Lempel-Ziv compression based on 'compress'. GIF modifications by - * David Rowley (mgardi@watdcsu.waterloo.edu) - * - ***************************************************************************/ - -/* - * General DEFINEs - */ - -#define BITS 12 - -#define HSIZE 5003 /* 80% occupancy */ - -#ifdef NO_UCHAR - typedef char char_type; -#else /*NO_UCHAR*/ - typedef unsigned char char_type; -#endif /*NO_UCHAR*/ - -/* - * - * GIF Image compression - modified 'compress' - * - * Based on: compress.c - File compression ala IEEE Computer, June 1984. - * - * By Authors: Spencer W. Thomas (decvax!harpo!utah-cs!utah-gr!thomas) - * Jim McKie (decvax!mcvax!jim) - * Steve Davies (decvax!vax135!petsd!peora!srd) - * Ken Turkowski (decvax!decwrl!turtlevax!ken) - * James A. Woods (decvax!ihnp4!ames!jaw) - * Joe Orost (decvax!vax135!petsd!joe) - * - */ -#include - -#define ARGVAL() (*++(*argv) || (--argc && *++argv)) - -static code_int const maxmaxcode = (code_int)1 << BITS; - /* should NEVER generate this code */ -#define MAXCODE(n_bits) (((code_int) 1 << (n_bits)) - 1) - -static long htab [HSIZE]; -static unsigned short codetab [HSIZE]; -#define HashTabOf(i) htab[i] -#define CodeTabOf(i) codetab[i] - -/* - * To save much memory, we overlay the table used by compress() with those - * used by decompress(). The tab_prefix table is the same size and type - * as the codetab. The tab_suffix table needs 2**BITS characters. We - * get this from the beginning of htab. The output stack uses the rest - * of htab, and contains characters. There is plenty of room for any - * possible stack (stack used to be 8000 characters). - */ - -#define tab_prefixof(i) CodeTabOf(i) -#define tab_suffixof(i) ((char_type*)(htab))[i] -#define de_stack ((char_type*)&tab_suffixof((code_int)1<fileP = fileP; - byteBufferP->count = 0; - - return byteBufferP; -} - - - -static void -byteBuffer_destroy(byteBuffer * const byteBufferP) { - - free(byteBufferP); -} - - - -static void -byteBuffer_flush(byteBuffer * const byteBufferP) { -/*---------------------------------------------------------------------------- - Write the current data block to the output file, then reset the current - data block to empty. ------------------------------------------------------------------------------*/ - if (byteBufferP->count > 0 ) { - if (verbose) - pm_message("Writing %u byte block", byteBufferP->count); - fputc(byteBufferP->count, byteBufferP->fileP); - fwrite(byteBufferP->buffer, 1, byteBufferP->count, byteBufferP->fileP); - byteBufferP->count = 0; - } -} - - - -static void -byteBuffer_flushFile(byteBuffer * const byteBufferP) { - - fflush(byteBufferP->fileP); - - if (ferror(byteBufferP->fileP)) - pm_error("error writing output file"); -} - - + commandVerb = strdup(pamtogifName); -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 - characters, flush the data block to the output file. ------------------------------------------------------------------------------*/ - byteBufferP->buffer[byteBufferP->count++] = c; - if (byteBufferP->count >= 254) - byteBuffer_flush(byteBufferP); -} - - - -struct gif_dest { - /* This structure controls output of uncompressed GIF raster */ - - byteBuffer * byteBufferP; /* Where the full bytes go */ - - /* State for packing variable-width codes into a bitstream */ - int n_bits; /* current number of bits/code */ - int maxcode; /* maximum code, given n_bits */ - int cur_accum; /* holds bits not yet output */ - int cur_bits; /* # of bits in cur_accum */ - - /* State for GIF code assignment */ - int ClearCode; /* clear code (doesn't change) */ - int EOFCode; /* EOF code (ditto) */ - int code_counter; /* counts output symbols */ -}; - - - -static unsigned long const masks[] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, - 0x001F, 0x003F, 0x007F, 0x00FF, - 0x01FF, 0x03FF, 0x07FF, 0x0FFF, - 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF }; - -typedef struct { - byteBuffer * byteBufferP; - unsigned int initBits; - unsigned int n_bits; /* number of bits/code */ - code_int maxcode; /* maximum code, given n_bits */ - unsigned long curAccum; - int curBits; -} codeBuffer; - - - -static codeBuffer * -codeBuffer_create(FILE * const ofP, - unsigned int const initBits) { - - codeBuffer * codeBufferP; - - MALLOCVAR_NOFAIL(codeBufferP); - - codeBufferP->initBits = initBits; - codeBufferP->n_bits = codeBufferP->initBits; - codeBufferP->maxcode = MAXCODE(codeBufferP->n_bits); - codeBufferP->byteBufferP = byteBuffer_create(ofP); - codeBufferP->curAccum = 0; - codeBufferP->curBits = 0; - - return codeBufferP; -} - - - -static void -codeBuffer_destroy(codeBuffer * const codeBufferP) { - - byteBuffer_destroy(codeBufferP->byteBufferP); - - free(codeBufferP); -} - - - -static void -codeBuffer_output(codeBuffer * const codeBufferP, - code_int const code) { -/*---------------------------------------------------------------------------- - Output one GIF code to the file, through the code buffer. - - The code is represented as n_bits bits in the file -- the lower - n_bits bits of 'code'. - - If the code is EOF, flush the code buffer to the file. - - In some cases, change n_bits and recalculate maxcode to go with it. ------------------------------------------------------------------------------*/ - /* - Algorithm: - Maintain a BITS character long buffer (so that 8 codes will - fit in it exactly). Use the VAX insv instruction to insert each - code in turn. When the buffer fills up empty it and start over. - */ - - codeBufferP->curAccum &= masks[codeBufferP->curBits]; + strfree(arg0DirName); + } else + commandVerb = strdup(pamtogifName); - if (codeBufferP->curBits > 0) - codeBufferP->curAccum |= ((long)code << codeBufferP->curBits); + if (cmdline.mapfile) + asprintfN(&mapfileOpt, "-mapfile=%s", cmdline.mapfile); else - codeBufferP->curAccum = code; + mapfileOpt = strdup(""); - codeBufferP->curBits += codeBufferP->n_bits; - - while (codeBufferP->curBits >= 8) { - byteBuffer_out(codeBufferP->byteBufferP, - codeBufferP->curAccum & 0xff); - codeBufferP->curAccum >>= 8; - codeBufferP->curBits -= 8; - } + if (cmdline.transparent) + asprintfN(&transparentOpt, "-transparent=%s", cmdline.transparent); + else + transparentOpt = strdup(""); - if (clear_flg) { - codeBufferP->n_bits = codeBufferP->initBits; - codeBufferP->maxcode = MAXCODE(codeBufferP->n_bits); - clear_flg = 0; - } else if (free_ent > codeBufferP->maxcode) { - /* The next entry is going to be too big for the code size, so - increase it, if possible. - */ - ++codeBufferP->n_bits; - if (codeBufferP->n_bits == BITS) - codeBufferP->maxcode = maxmaxcode; - else - codeBufferP->maxcode = MAXCODE(codeBufferP->n_bits); - } + if (cmdline.comment) + asprintfN(&commentOpt, "-comment=%s", cmdline.comment); + else + commentOpt = strdup(""); + + asprintfN(&retval, "%s - -alphacolor=%s %s %s %s %s %s %s %s", + commandVerb, + cmdline.alphacolor, + cmdline.interlace ? "-interlace" : "", + cmdline.sort ? "-sort" : "", + mapfileOpt, + transparentOpt, + commentOpt, + cmdline.nolzw ? "-nolzw" : "", + cmdline.verbose ? "-verbose" : ""); - if (code == EOFCode) { - /* We're at EOF. Output the possible partial byte in the buffer */ - if (codeBufferP->curBits > 0) { - byteBuffer_out(codeBufferP->byteBufferP, - codeBufferP->curAccum & 0xff); - codeBufferP->curBits = 0; - } - byteBuffer_flush(codeBufferP->byteBufferP); - - byteBuffer_flushFile(codeBufferP->byteBufferP); - } -} - + strfree(mapfileOpt); + strfree(transparentOpt); + strfree(commentOpt); - -static void -cl_hash(long const hsize) { - /* reset code table */ - - long const m1 = -1; - - long * htab_p; - long i; - - htab_p = htab + hsize; /* initial value */ - - i = hsize - 16; - do { /* might use Sys V memset(3) here */ - *(htab_p-16) = m1; - *(htab_p-15) = m1; - *(htab_p-14) = m1; - *(htab_p-13) = m1; - *(htab_p-12) = m1; - *(htab_p-11) = m1; - *(htab_p-10) = m1; - *(htab_p-9) = m1; - *(htab_p-8) = m1; - *(htab_p-7) = m1; - *(htab_p-6) = m1; - *(htab_p-5) = m1; - *(htab_p-4) = m1; - *(htab_p-3) = m1; - *(htab_p-2) = m1; - *(htab_p-1) = m1; - htab_p -= 16; - } while ((i -= 16) >= 0); - - for (i += 16; i > 0; --i) - *--htab_p = m1; + return retval; } static void -cl_block(codeBuffer * const codeBufferP) { -/*---------------------------------------------------------------------------- - Clear out the hash table ------------------------------------------------------------------------------*/ - cl_hash(HSIZE); - free_ent = ClearCode + 2; - clear_flg = 1; +feedPamtogifNoAlpha(struct pam * const inPamP, + FILE * const pipeToPamtogif) { - codeBuffer_output(codeBufferP, (code_int)ClearCode); -} - - - -static void -write_raster_LZW(pixel ** const pixels, - pixval const input_maxval, - gray ** const alpha, - gray const alpha_maxval, - struct cmap * const cmapP, - int const initBits, - FILE * const ofP) { -/*---------------------------------------------------------------------------- - Write the raster to file 'ofP'. - - The raster to write is 'pixels', which has maxval 'input_maxval', - modified by alpha mask 'alpha', which has maxval 'alpha_maxval'. + unsigned int row; + struct pam outPam; + tuple * tuplerow; - Use the colormap 'cmapP' to generate the raster ('pixels' is - composed of RGB samples; the GIF raster is colormap indices). + tuplerow = pnm_allocpamrow(inPamP); - Write the raster using LZW compression. ------------------------------------------------------------------------------*/ - gray const alpha_threshold = (alpha_maxval + 1) / 2; - /* gray levels below this in the alpha mask indicate transparent - pixels in the output image. - */ - code_int ent; - code_int disp; - int hshift; - bool eof; - codeBuffer * codeBufferP; - - codeBufferP = codeBuffer_create(ofP, initBits); + outPam = *inPamP; + outPam.file = pipeToPamtogif; - /* - * Set up the necessary values - */ - offset = 0; - out_count = 0; - clear_flg = 0; - in_count = 1; - - ClearCode = (1 << (initBits - 1)); - EOFCode = ClearCode + 1; - free_ent = ClearCode + 2; - - ent = gifNextPixel(pixels, input_maxval, alpha, alpha_threshold, cmapP); - - { - long fcode; - hshift = 0; - for (fcode = HSIZE; fcode < 65536L; fcode *= 2L) - ++hshift; - hshift = 8 - hshift; /* set hash code range bound */ - } - cl_hash(HSIZE); /* clear hash table */ - - codeBuffer_output(codeBufferP, (code_int)ClearCode); - - eof = FALSE; - while (!eof) { - int gifpixel; - /* The value for the pixel in the GIF image. I.e. the colormap - index. Or -1 to indicate "no more pixels." - */ - gifpixel = gifNextPixel(pixels, - input_maxval, alpha, alpha_threshold, cmapP); - if (gifpixel == EOF) eof = TRUE; - if (!eof) { - long const fcode = (long) (((long) gifpixel << BITS) + ent); - code_int i; - /* xor hashing */ - - ++in_count; - - i = (((code_int)gifpixel << hshift) ^ ent); - - if (HashTabOf (i) == fcode) { - ent = CodeTabOf (i); - continue; - } else if ((long)HashTabOf(i) < 0) /* empty slot */ - goto nomatch; - disp = HSIZE - i; /* secondary hash (after G. Knott) */ - if (i == 0) - disp = 1; - probe: - if ((i -= disp) < 0) - i += HSIZE; - - if (HashTabOf(i) == fcode) { - ent = CodeTabOf(i); - continue; - } - if ((long)HashTabOf(i) > 0) - goto probe; - nomatch: - codeBuffer_output(codeBufferP, (code_int)ent); - ++out_count; - ent = gifpixel; - if (free_ent < maxmaxcode) { - CodeTabOf(i) = free_ent++; /* code -> hashtable */ - HashTabOf(i) = fcode; - } else - cl_block(codeBufferP); - } - } - /* Put out the final code. */ - codeBuffer_output(codeBufferP, (code_int)ent); - ++out_count; - codeBuffer_output(codeBufferP, (code_int) EOFCode); - - codeBuffer_destroy(codeBufferP); -} - + pnm_writepaminit(&outPam); + for (row = 0; row < inPamP->height; ++row) { + pnm_readpamrow(inPamP, tuplerow); -/* Routine to convert variable-width codes into a byte stream */ - -static void -outputUncompressed(struct gif_dest * const dinfoP, - int const code) { - - /* Emit a code of n_bits bits */ - /* Uses cur_accum and cur_bits to reblock into 8-bit bytes */ - dinfoP->cur_accum |= ((int) code) << dinfoP->cur_bits; - dinfoP->cur_bits += dinfoP->n_bits; - - while (dinfoP->cur_bits >= 8) { - byteBuffer_out(dinfoP->byteBufferP, dinfoP->cur_accum & 0xFF); - dinfoP->cur_accum >>= 8; - dinfoP->cur_bits -= 8; + pnm_writepamrow(&outPam, tuplerow); } -} - - -static void -writeRasterUncompressedInit(FILE * const ofP, - struct gif_dest * const dinfoP, - int const i_bits) { -/*---------------------------------------------------------------------------- - Initialize pseudo-compressor ------------------------------------------------------------------------------*/ - - /* init all the state variables */ - dinfoP->n_bits = i_bits; - dinfoP->maxcode = MAXCODE(dinfoP->n_bits); - dinfoP->ClearCode = (1 << (i_bits - 1)); - dinfoP->EOFCode = dinfoP->ClearCode + 1; - dinfoP->code_counter = dinfoP->ClearCode + 2; - /* init output buffering vars */ - dinfoP->byteBufferP = byteBuffer_create(ofP); - dinfoP->cur_accum = 0; - dinfoP->cur_bits = 0; - /* GIF specifies an initial Clear code */ - outputUncompressed(dinfoP, dinfoP->ClearCode); + pnm_freepamrow(tuplerow); } static void -writeRasterUncompressedPixel(struct gif_dest * const dinfoP, - unsigned int const colormapIndex) { -/*---------------------------------------------------------------------------- - "Compress" one pixel value and output it as a symbol. - - 'colormapIndex' must be less than dinfoP->n_bits wide. ------------------------------------------------------------------------------*/ - assert(colormapIndex >> dinfoP->n_bits == 0); - - outputUncompressed(dinfoP, colormapIndex); - /* Issue Clear codes often enough to keep the reader from ratcheting up - * its symbol size. - */ - if (dinfoP->code_counter < dinfoP->maxcode) { - ++dinfoP->code_counter; - } else { - outputUncompressed(dinfoP, dinfoP->ClearCode); - dinfoP->code_counter = dinfoP->ClearCode + 2; /* reset the counter */ - } -} - - - -static void -writeRasterUncompressedTerm(struct gif_dest * const dinfoP) { - - outputUncompressed(dinfoP, dinfoP->EOFCode); - - if (dinfoP->cur_bits > 0) - byteBuffer_out(dinfoP->byteBufferP, dinfoP->cur_accum & 0xFF); - - byteBuffer_flush(dinfoP->byteBufferP); - - byteBuffer_destroy(dinfoP->byteBufferP); -} - - - -static void -writeRasterUncompressed(FILE * const ofP, - pixel ** const pixels, - pixval const inputMaxval, - gray ** const alpha, - gray const alphaMaxval, - struct cmap * const cmapP, - int const initBits) { -/*---------------------------------------------------------------------------- - Write the raster to file 'ofP'. - - Same as write_raster_LZW(), except written out one code per - pixel (plus some clear codes), so no compression. And no use - of the LZW patent. ------------------------------------------------------------------------------*/ - gray const alphaThreshold = (alphaMaxval + 1) / 2; - /* gray levels below this in the alpha mask indicate transparent - pixels in the output image. - */ - bool eof; - - struct gif_dest gifDest; - - writeRasterUncompressedInit(ofP, &gifDest, initBits); - - eof = FALSE; - while (!eof) { - int gifpixel; - /* The value for the pixel in the GIF image. I.e. the colormap - index. Or -1 to indicate "no more pixels." - */ - gifpixel = gifNextPixel(pixels, - inputMaxval, alpha, alphaThreshold, cmapP); - if (gifpixel == EOF) - eof = TRUE; - else - writeRasterUncompressedPixel(&gifDest, gifpixel); - } - writeRasterUncompressedTerm(&gifDest); -} - - - -/****************************************************************************** - * - * GIF Specific routines - * - *****************************************************************************/ - -static void -writeGifHeader(FILE * const fp, - int const Width, int const Height, - int const GInterlace, int const Background, - int const BitsPerPixel, struct cmap * const cmapP, - const char comment[]) { - - int B; - int const Resolution = BitsPerPixel; - int const ColorMapSize = 1 << BitsPerPixel; - - /* Write the Magic header */ - if (cmapP->transparent != -1 || comment) - fwrite("GIF89a", 1, 6, fp); - else - fwrite("GIF87a", 1, 6, fp); - - /* Write out the screen width and height */ - Putword( Width, fp ); - Putword( Height, fp ); - - /* Indicate that there is a global color map */ - B = 0x80; /* Yes, there is a color map */ - - /* OR in the resolution */ - B |= (Resolution - 1) << 4; - - /* OR in the Bits per Pixel */ - B |= (BitsPerPixel - 1); - - /* Write it out */ - fputc( B, fp ); - - /* Write out the Background color */ - fputc( Background, fp ); - - /* Byte of 0's (future expansion) */ - fputc( 0, fp ); - - { - /* Write out the Global Color Map */ - /* Note that the Global Color Map is always a power of two colors - in size, but *cmapP could be smaller than that. So we pad with - black. - */ - int i; - for ( i=0; i < ColorMapSize; ++i ) { - if ( i < cmapP->cmapsize ) { - fputc( cmapP->red[i], fp ); - fputc( cmapP->green[i], fp ); - fputc( cmapP->blue[i], fp ); - } else { - fputc( 0, fp ); - fputc( 0, fp ); - fputc( 0, fp ); - } +copyRasterWithAlpha(struct pam * const inPamP, + struct pam * const alphaPamP, + struct pam * const outPamP, + unsigned int const alphaPlane) { + + tuple * tuplerow; + tuple * alpharow; + unsigned int row; + + inPamP->allocation_depth = outPamP->depth; + + tuplerow = pnm_allocpamrow(inPamP); + alpharow = pnm_allocpamrow(alphaPamP); + + for (row = 0; row < inPamP->height; ++row) { + unsigned int col; + + pnm_readpamrow(inPamP, tuplerow); + pnm_readpamrow(alphaPamP, alpharow); + + for (col = 0; col < inPamP->width; ++col) { + tuplerow[col][alphaPlane] = pnm_scalesample(alpharow[col][0], + alphaPamP->maxval, + inPamP->maxval); } + pnm_writepamrow(outPamP, tuplerow); } - - if ( cmapP->transparent >= 0 ) - write_transparent_color_index_extension(fp, cmapP->transparent); - - if ( comment ) - write_comment_extension(fp, comment); -} - - - -static void -writeImageHeader(FILE * const ofP, - unsigned int const leftOffset, - unsigned int const topOffset, - unsigned int const gWidth, - unsigned int const gHeight, - unsigned int const gInterlace, - unsigned int const initCodeSize) { - - Putword(leftOffset, ofP); - Putword(topOffset, ofP); - Putword(gWidth, ofP); - Putword(gHeight, ofP); - - /* Write out whether or not the image is interlaced */ - if (gInterlace) - fputc(0x40, ofP); - else - fputc(0x00, ofP); - - /* Write out the initial code size */ - fputc(initCodeSize, ofP); -} - - - -static void -gifEncode(FILE * const ofP, - pixel ** const pixels, - pixval const inputMaxval, - int const gWidth, - int const gHeight, - gray ** const alpha, - gray const alphaMaxval, - int const gInterlace, - int const background, - int const bitsPerPixel, - struct cmap * const cmapP, - char const comment[], - bool const nolzw) { - - unsigned int const leftOffset = 0; - unsigned int const topOffset = 0; - - unsigned int const initCodeSize = bitsPerPixel <= 1 ? 2 : bitsPerPixel; - /* The initial code size */ - - writeGifHeader(ofP, gWidth, gHeight, gInterlace, background, - bitsPerPixel, cmapP, comment); - - /* Write an Image separator */ - fputc(',', ofP); - - writeImageHeader(ofP, leftOffset, topOffset, gWidth, gHeight, gInterlace, - initCodeSize); - - initPixelCursor(gWidth, gHeight, gInterlace); - - /* Write the actual raster */ - if (nolzw) - writeRasterUncompressed(ofP, pixels, - inputMaxval, alpha, alphaMaxval, cmapP, - initCodeSize + 1); - else - write_raster_LZW(pixels, - inputMaxval, alpha, alphaMaxval, cmapP, - initCodeSize + 1, ofP); - - /* Write out a zero length data block (to end the series) */ - fputc(0, ofP); - - /* Write the GIF file terminator */ - fputc(';', ofP); -} - - - -static int -compute_transparent(const char colorarg[], - struct cmap * const cmapP) { -/*---------------------------------------------------------------------------- - Figure out the color index (index into the colormap) of the color - that is to be transparent in the GIF. - - colorarg[] is the string that specifies the color the user wants to - be transparent (e.g. "red", "#fefefe"). Its maxval is the maxval - of the colormap. 'cmap' is the full colormap except that its - 'transparent' component isn't valid. - - colorarg[] is a standard Netpbm color specification, except that - may have a "=" prefix, which means it specifies a particular exact - color, as opposed to without the "=", which means "the color that - is closest to this and actually in the image." - - Return -1 if colorarg[] specifies an exact color and that color is not - in the image. Also issue an informational message. ------------------------------------------------------------------------------*/ - int retval; - - const char *colorspec; - bool exact; - int presort_colorindex; - pixel transcolor; - - if (colorarg[0] == '=') { - colorspec = &colorarg[1]; - exact = TRUE; - } else { - colorspec = colorarg; - exact = FALSE; - } - - transcolor = ppm_parsecolor((char*)colorspec, cmapP->maxval); - presort_colorindex = ppm_lookupcolor(cmapP->cht, &transcolor); - - if (presort_colorindex != -1) - retval = cmapP->perm[presort_colorindex]; - else if (!exact) - retval = cmapP->perm[closestcolor(transcolor, cmapP->maxval, cmapP)]; - else { - retval = -1; - pm_message( - "Warning: specified transparent color does not occur in image."); - } - return retval; + pnm_freepamrow(alpharow); + pnm_freepamrow(tuplerow); } static void -sort_colormap(int const sort, struct cmap * const cmapP) { -/*---------------------------------------------------------------------------- - Sort (in place) the colormap *cmapP. - - Create the perm[] and permi[] mappings for the colormap. - - 'sort' is logical: true means to sort the colormap by red intensity, - then by green intensity, then by blue intensity. False means a null - sort -- leave it in the same order in which we found it. ------------------------------------------------------------------------------*/ - int * const Red = cmapP->red; - int * const Blue = cmapP->blue; - int * const Green = cmapP->green; - int * const perm = cmapP->perm; - int * const permi = cmapP->permi; - unsigned int const cmapsize = cmapP->cmapsize; - - int i; - - for (i=0; i < cmapsize; i++) - permi[i] = i; - - if (sort) { - pm_message("sorting colormap"); - for (i=0; i < cmapsize; i++) { - int j; - for (j=i+1; j < cmapsize; j++) - if (((Red[i]*MAXCMAPSIZE)+Green[i])*MAXCMAPSIZE+Blue[i] > - ((Red[j]*MAXCMAPSIZE)+Green[j])*MAXCMAPSIZE+Blue[j]) { - int tmp; - - tmp=permi[i]; permi[i]=permi[j]; permi[j]=tmp; - tmp=Red[i]; Red[i]=Red[j]; Red[j]=tmp; - tmp=Green[i]; Green[i]=Green[j]; Green[j]=tmp; - tmp=Blue[i]; Blue[i]=Blue[j]; Blue[j]=tmp; } } +feedPamtogifWithAlpha(struct pam * const inPamP, + struct pam * const alphaPamP, + FILE * const pipeToPamtogif) { + + unsigned int alphaPlane; + struct pam outPam; + + if (inPamP->width != alphaPamP->width || + inPamP->height != alphaPamP->height) + pm_error("-alpha image dimensions (%u w x %u h) do not match " + "the input image dimensions (%u x %u)", + alphaPamP->width, alphaPamP->height, + inPamP->width, inPamP->height); + + outPam = *inPamP; + outPam.file = pipeToPamtogif; + outPam.format = PAM_FORMAT; + outPam.plainformat = 0; + + if (inPamP->depth == 1) { + alphaPlane = 1; + strcpy(outPam.tuple_type, "GRAYSCALE_ALPHA"); + } else if (inPamP->depth == 3) { + alphaPlane = 3; + strcpy(outPam.tuple_type, "RGB_ALPHA"); } + outPam.depth = alphaPlane + 1; - for (i=0; i < cmapsize; i++) - perm[permi[i]] = i; -} + pnm_writepaminit(&outPam); - - -static void -normalize_to_255(colorhist_vector const chv, struct cmap * const cmapP) { -/*---------------------------------------------------------------------------- - With a PPM color histogram vector 'chv' as input, produce a colormap - of integers 0-255 as output in *cmapP. ------------------------------------------------------------------------------*/ - int i; - pixval const maxval = cmapP->maxval; - - if ( maxval != 255 ) - pm_message( - "maxval is not 255 - automatically rescaling colors" ); - - for ( i = 0; i < cmapP->cmapsize; ++i ) { - if ( maxval == 255 ) { - cmapP->red[i] = (int) PPM_GETR( chv[i].color ); - cmapP->green[i] = (int) PPM_GETG( chv[i].color ); - cmapP->blue[i] = (int) PPM_GETB( chv[i].color ); - } else { - cmapP->red[i] = (int) PPM_GETR( chv[i].color ) * 255 / maxval; - cmapP->green[i] = (int) PPM_GETG( chv[i].color ) * 255 / maxval; - cmapP->blue[i] = (int) PPM_GETB( chv[i].color ) * 255 / maxval; - } - } -} - - - -static void add_to_colormap(struct cmap * const cmapP, - const char * const colorspec, - int * const new_indexP) { -/*---------------------------------------------------------------------------- - Add a new entry to the colormap. Make the color that specified by - 'colorspec', and return the index of the new entry as *new_indexP. - - 'colorspec' is a color specification given by the user, e.g. - "red" or "rgb:ff/03.0d". The maxval for this color specification is - that for the colormap *cmapP. ------------------------------------------------------------------------------*/ - pixel const transcolor = ppm_parsecolor((char*)colorspec, cmapP->maxval); - - *new_indexP = cmapP->cmapsize++; - - cmapP->red[*new_indexP] = PPM_GETR(transcolor); - cmapP->green[*new_indexP] = PPM_GETG(transcolor); - cmapP->blue[*new_indexP] = PPM_GETB(transcolor); + copyRasterWithAlpha(inPamP, alphaPamP, &outPam, alphaPlane); } static void -colormap_from_file(const char filespec[], unsigned int const maxcolors, - colorhist_vector * const chvP, pixval * const maxvalP, - int * const colorsP) { -/*---------------------------------------------------------------------------- - Read a colormap from the PPM file filespec[]. Return the color histogram - vector (which is practically a colormap) of the input image as *cvhP - and the maxval for that histogram as *maxvalP. ------------------------------------------------------------------------------*/ - FILE *mapfile; - int cols, rows; - pixel ** colormap_ppm; - - mapfile = pm_openr(filespec); - colormap_ppm = ppm_readppm(mapfile, &cols, &rows, maxvalP); - pm_close(mapfile); +feedPamtogif(struct pam * const inPamP, + const char * const alphaFilespec, + FILE * const pipeToPamtogif) { - /* Figure out the colormap from the . */ - pm_message("computing other colormap..."); - *chvP = - ppm_computecolorhist(colormap_ppm, cols, rows, maxcolors, colorsP); - - ppm_freearray(colormap_ppm, rows); -} - - - -static void -get_alpha(const char * const alpha_filespec, int const cols, int const rows, - gray *** const alphaP, gray * const maxvalP) { - - if (alpha_filespec) { - int alpha_cols, alpha_rows; - *alphaP = pgm_readpgm(pm_openr(alpha_filespec), - &alpha_cols, &alpha_rows, maxvalP); - if (alpha_cols != cols || alpha_rows != rows) - pm_error("alpha mask is not the same dimensions as the " - "input file (alpha is %dW x %dH; image is %dW x %dH)", - alpha_cols, alpha_rows, cols, rows); - } else - *alphaP = NULL; -} - - - -static void -compute_ppm_colormap(pixel ** const pixels, int const cols, int const rows, - int const input_maxval, bool const have_alpha, - const char * const mapfile, colorhist_vector * const chvP, - colorhash_table * const chtP, - pixval * const colormap_maxvalP, - int * const colorsP) { -/*---------------------------------------------------------------------------- - Compute a colormap, PPM style, for the image 'pixels', which is - 'cols' by 'rows' with maxval 'input_maxval'. If 'mapfile' is - non-null, Use the colors in that (PPM) file for the color map - instead of the colors in 'pixels'. - - Return the colormap as *chvP and *chtP. Return the maxval for that - colormap as *colormap_maxvalP. - - While we're at it, count the colors and validate that there aren't - too many. Return the count as *colorsP. In determining if there are - too many, allow one slot for a fake transparency color if 'have_alpha' - is true. If there are too many, issue an error message and abort the - program. ------------------------------------------------------------------------------*/ - unsigned int maxcolors; - /* The most colors we can tolerate in the image. If we have - our own made-up entry in the colormap for transparency, it - isn't included in this count. - */ - - if (have_alpha) - maxcolors = MAXCMAPSIZE - 1; - else - maxcolors = MAXCMAPSIZE; - - if (mapfile) { - /* Read the colormap from a separate colormap file. */ - colormap_from_file(mapfile, maxcolors, chvP, colormap_maxvalP, - colorsP); - } else { - /* Figure out the color map from the input file */ - pm_message("computing colormap..."); - *chvP = ppm_computecolorhist(pixels, cols, rows, maxcolors, colorsP); - *colormap_maxvalP = input_maxval; - } - - if (*chvP == NULL) - pm_error("too many colors - try doing a 'pnmquant %d'", maxcolors); - pm_message("%d colors found", *colorsP); - - /* And make a hash table for fast lookup. */ - *chtP = ppm_colorhisttocolorhash(*chvP, *colorsP); + if (alphaFilespec) { + FILE * afP; + struct pam alphaPam; + afP = pm_openr(alphaFilespec); + pnm_readpaminit(afP, &alphaPam, PAM_STRUCT_SIZE(tuple_type)); + feedPamtogifWithAlpha(inPamP, &alphaPam, pipeToPamtogif); + pm_close(afP); + } else + feedPamtogifNoAlpha(inPamP, pipeToPamtogif); } int -main(int argc, char *argv[]) { +main(int argc, + char * argv[]) { + struct cmdlineInfo cmdline; FILE * ifP; - int rows, cols; - int BitsPerPixel; - pixel ** pixels; /* The input image, in PPM format */ - pixval input_maxval; /* Maxval for 'pixels' */ - gray ** alpha; /* The supplied alpha mask; NULL if none */ - gray alpha_maxval; /* Maxval for 'alpha' */ - - struct cmap cmap; - /* The colormap, with all its accessories */ - colorhist_vector chv; - int fake_transparent; - /* colormap index of the fake transparency color we're using to - implement the alpha mask. Undefined if we're not doing an alpha - mask. - */ + struct pam inPam; + const char * command; + FILE * pipeToPamtogif; + int rc; - ppm_init( &argc, argv ); + pnm_init(&argc, argv); parseCommandLine(argc, argv, &cmdline); - verbose = cmdline.verbose; - - ifP = pm_openr(cmdline.input_filespec); - - pixels = ppm_readppm(ifP, &cols, &rows, &input_maxval); + command = pamtogifCommand(argv[0], cmdline); - pm_close(ifP); - - get_alpha(cmdline.alpha_filespec, cols, rows, &alpha, &alpha_maxval); + if (cmdline.verbose) + pm_message("Executing shell command '%s'", command); + + pipeToPamtogif = popen(command, "w"); - compute_ppm_colormap(pixels, cols, rows, input_maxval, (alpha != NULL), - cmdline.mapfile, - &chv, &cmap.cht, &cmap.maxval, &cmap.cmapsize); + if (pipeToPamtogif == NULL) + pm_error("Shell command '%s', via popen(), failed.", command); - /* Now turn the ppm colormap into the appropriate GIF colormap. */ + ifP = pm_openr(cmdline.input_filespec); + pnm_readpaminit(ifP, &inPam, PAM_STRUCT_SIZE(allocation_depth)); - normalize_to_255(chv, &cmap); + feedPamtogif(&inPam, cmdline.alpha_filespec, pipeToPamtogif); - ppm_freecolorhist(chv); + rc = pclose(pipeToPamtogif); - if (alpha) { - /* Add a fake entry to the end of the colormap for transparency. - Make its color black. - */ - add_to_colormap(&cmap, cmdline.alphacolor, &fake_transparent); - } - sort_colormap(cmdline.sort, &cmap); - - BitsPerPixel = pm_maxvaltobits(cmap.cmapsize-1); - - if (alpha) { - cmap.transparent = cmap.perm[fake_transparent]; - } else { - if (cmdline.transparent) - cmap.transparent = - compute_transparent(cmdline.transparent, &cmap); - else - cmap.transparent = -1; - } - /* All set, let's do it. */ - gifEncode(stdout, pixels, input_maxval, cols, rows, - alpha, alpha_maxval, - cmdline.interlace, 0, BitsPerPixel, &cmap, cmdline.comment, - cmdline.nolzw); + if (rc != 0) + pm_error("Pamtogif process failed. pclose() failed."); - ppm_freearray(pixels, rows); - if (alpha) - pgm_freearray(alpha, rows); + strfree(command); - fclose(stdout); + pm_close(ifP); + pm_close(stdout); return 0; } diff --git a/converter/ppm/ppmtoilbm.c b/converter/ppm/ppmtoilbm.c index 6c04c9be..c0d58edb 100644 --- a/converter/ppm/ppmtoilbm.c +++ b/converter/ppm/ppmtoilbm.c @@ -635,20 +635,9 @@ ppm_to_ham(fp, cols, rows, maxval, colormap, colors, cmapmaxval, hamplanes) static long -#ifdef __STDC__ do_ham_body(FILE *ifP, FILE *ofp, int cols, int rows, pixval maxval, pixval hammaxval, int nPlanes, pixel *colormap, int colors) -#else -do_ham_body(ifP, ofp, cols, rows, maxval, hammaxval, nPlanes, colormap, colors) - FILE *ifP, *ofp; - int cols, rows; - pixval maxval; /* maxval of image color components */ - pixval hammaxval; /* maxval of HAM color changes */ - int nPlanes; - pixel *colormap; - int colors; -#endif { register int col, row, i; rawtype *raw_rowbuf; @@ -876,16 +865,8 @@ ppm_to_deep(fp, cols, rows, maxval, bitspercolor) static long -#if __STDC__ do_deep_body(FILE *ifP, FILE *ofp, int cols, int rows, pixval maxval, int bitspercolor) -#else -do_deep_body(ifP, ofp, cols, rows, maxval, bitspercolor) - FILE *ifP, *ofp; - int cols, rows; - pixval maxval; - int bitspercolor; -#endif { register int row, col; pixel *pP; @@ -1014,16 +995,8 @@ ppm_to_dcol(fp, cols, rows, maxval, dcol) static long -#if __STDC__ do_dcol_body(FILE *ifP, FILE *ofp, int cols, int rows, pixval maxval, DirectColor *dcol) -#else -do_dcol_body(ifP, ofp, cols, rows, maxval, dcol) - FILE *ifP, *ofp; - int cols, rows; - pixval maxval; - DirectColor *dcol; -#endif { register int row, col; pixel *pP; @@ -1161,18 +1134,8 @@ ppm_to_std(fp, cols, rows, maxval, colormap, colors, cmapmaxval, static long -#if __STDC__ do_std_body(FILE *ifP, FILE *ofp, int cols, int rows, pixval maxval, pixel *colormap, int colors, int nPlanes) -#else -do_std_body(ifP, ofp, cols, rows, maxval, colormap, colors, nPlanes) - FILE *ifP, *ofp; - int cols, rows; - pixval maxval; - pixel *colormap; - int colors; - int nPlanes; -#endif { register int row, col, i; pixel *pP; @@ -1783,12 +1746,7 @@ runbyte1(size) /************ other utility functions ************/ static void -#if __STDC__ put_big_short(short s) -#else -put_big_short(s) - short s; -#endif { if ( pm_writebigshort( stdout, s ) == -1 ) pm_error( "write error" ); diff --git a/converter/ppm/ppmtomitsu.c b/converter/ppm/ppmtomitsu.c index 3934ae45..837d9e2b 100644 --- a/converter/ppm/ppmtomitsu.c +++ b/converter/ppm/ppmtomitsu.c @@ -20,362 +20,70 @@ x** - mymalloc() and check_and_rotate() functions for often used ** implied warranty. */ +#include #include +#include #include "pm_c_util.h" -#include "ppm.h" #include "nstring.h" #include "mallocvar.h" +#include "ppm.h" #include "mitsu.h" -#include - #define HASHSIZE 2048 #define myhash(x) ((PPM_GETR(x)*3 + PPM_GETG(x)*5 + PPM_GETB(x)*7) % HASHSIZE) typedef struct hashinfo { pixel color; long flag; - struct hashinfo *next; + struct hashinfo * next; } hashinfo; -#ifdef __STDC__ -static void lineputinit(int cols, int rows, int sharpness, int enlarge, int - copy, struct mediasize medias); -static void frametransferinit(int cols, int rows, int sharpness, int enlarge, - int copy, struct mediasize medias); -static void lookuptableinit(int sharpness, int enlarge, int copy, - struct mediasize medias); -static void lookuptabledata(int cols, int rows, int enlarge, - struct mediasize medias); -static void check_and_rotate(int cols, int rows, int enlarge, - struct mediasize medias); -#define CONST const -#else /*__STDC__*/ -static int lineputinit(); -static int lookuptableinit(); -static int lookuptabledata(); -static int frametransferinit(); -static int check_and_rotate(); -#define CONST -#endif - #define cmd(arg) fputc((arg), stdout) #define datum(arg) fputc((char)(arg), stdout) #define data(arg,num) fwrite((arg), sizeof(char), (num), stdout) -#ifdef __STDC__ -int main(int argc, char *argv[] ) -#else -int main( argc, argv ) - int argc; - char* argv[]; -#endif - { - FILE *ifp; - /*hashinfo colorhashtable[HASHSIZE];*/ - struct hashinfo *hashrun; - pixel *xP; - int argn; - bool dpi300; - int cols, rows, format, col, row; - int sharpness, enlarge, copy, tiny; - pixval maxval; - struct mediasize medias; - char media[16]; - const char * const usage = "[-sharpness <1-4>] [-enlarge <1-3>] [-media ] [-copy <1-9>] [-tiny] [-dpi300] [ppmfile]"; - - ppm_init(&argc, argv); - - dpi300 = FALSE; - argn = 1; - sharpness = 32; - enlarge = 1; - copy = 1; - memset(media, '\0', 16); - tiny = FALSE; - - /* check for flags */ - while (argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0') { - if (pm_keymatch(argv[argn], "-sharpness", 2)) { - ++argn; - if (argn == argc || sscanf(argv[argn], "%d", &sharpness) != 1) - pm_usage(usage); - else if (sharpness < 1 || sharpness > 4) - pm_usage(usage); - } - else if (pm_keymatch(argv[argn], "-enlarge", 2)) { - ++argn; - if (argn == argc || sscanf(argv[argn], "%d", &enlarge) != 1) - pm_usage(usage); - else if (enlarge < 1 || enlarge > 3) - pm_usage(usage); - } - else if (pm_keymatch(argv[argn], "-media", 2)) { - ++argn; - if (argn == argc || sscanf(argv[argn], "%15s", media) < 1) - pm_usage(usage); - else if (TOUPPER(media[0]) != 'A') - pm_usage(usage); - } - else if (pm_keymatch(argv[argn], "-copy", 2)) { - ++argn; - if (argn == argc || sscanf(argv[argn], "%d", ©) != 1) - pm_usage(usage); - else if (copy < 1 || copy > 9) - pm_usage(usage); - } - else if (pm_keymatch(argv[argn], "-dpi300", 2)) - dpi300 = TRUE; - else if (pm_keymatch(argv[argn], "-tiny", 2)) - tiny = TRUE; - else - pm_usage(usage); - ++argn; - } - - if (argn < argc) { - ifp = pm_openr(argv[argn]); - ++argn; - } - else - ifp = stdin; - - if (argn != argc) - pm_usage(usage); - - if (TOUPPER(media[0]) == 'A') - switch (TOUPPER(media[1])) { - case 'S': - medias = MSize_AS; - break; - case '4': - if(TOUPPER(media[2]) == 'S') - medias = MSize_A4S; - else { - medias = MSize_A4; - } - break; - default: - medias = MSize_A; - } - else - medias = MSize_User; - if (dpi300) { - medias.maxcols *= 2; - medias.maxrows *= 2; - } +static void +check_and_rotate(int const cols, + int const rows, + int const enlarge, + struct mediasize const medias) { - if (tiny) { - pixel *pixelrow; - char *redrow, *greenrow, *bluerow; - - ppm_readppminit(ifp, &cols, &rows, &maxval, &format); - pixelrow = (pixel *) ppm_allocrow(cols); - MALLOCARRAY_NOFAIL(redrow, cols); - MALLOCARRAY_NOFAIL(greenrow, cols); - MALLOCARRAY_NOFAIL(bluerow, cols); - lineputinit(cols, rows, sharpness, enlarge, copy, medias); - - for ( row = 0; row < rows; ++row ) { - ppm_readppmrow(ifp, pixelrow, cols, maxval, format); - switch(PPM_FORMAT_TYPE(format)) { - /* color */ - case PPM_TYPE: - for (col = 0, xP = pixelrow; col < cols; col++, xP++) { - /* First red. */ - redrow[col] = PPM_GETR(*xP); - /* Then green. */ - greenrow[col] = PPM_GETG(*xP); - /* And blue. */ - bluerow[col] = PPM_GETB(*xP); - } - data(redrow, cols); - data(greenrow, cols); - data(bluerow, cols); - break; - /* grayscale */ - default: - for (col = 0, xP = pixelrow; col < cols; col++, xP++) - bluerow[col] = PPM_GETB(*xP); - data(bluerow, cols); - data(bluerow, cols); - data(bluerow, cols); - break; - } - } - pm_close(ifp); + if (cols > rows) { + ROTATEIMG(DOROTATE); /* rotate image */ + if (enlarge * rows > medias.maxcols || enlarge * cols > medias.maxrows) + pm_error("Image too large, MaxPixels = %u x %u", + medias.maxrows, medias.maxcols); + HPIXELS(cols); + VPIXELS(rows); + HPIXELSOFF((medias.maxcols/enlarge - rows)/2); + VPIXELSOFF((medias.maxrows/enlarge - cols)/2); + pm_message("rotating image for output"); + } else { + ROTATEIMG(DONTROTATE); + if (enlarge * rows > medias.maxrows || enlarge * cols > medias.maxcols) + pm_error("Image too large, MaxPixels = %u x %u", + medias.maxrows, medias.maxcols); + HPIXELS(cols); + VPIXELS(rows); + HPIXELSOFF((medias.maxcols/enlarge - cols)/2); + VPIXELSOFF((medias.maxrows/enlarge - rows)/2); } - else { - pixel **pixelpic; - int colanz, colval; - int i; - colorhist_vector table; - - ppm_readppminit( ifp, &cols, &rows, &maxval, &format ); - pixelpic = ppm_allocarray( cols, rows ); - for (row = 0; row < rows; row++) - ppm_readppmrow( ifp, pixelpic[row], cols, maxval, format ); - pm_close(ifp); - - /* first check wether we can use the lut transfer */ +} - table = ppm_computecolorhist(pixelpic, cols, rows, MAXLUTCOL+1, - &colanz); - if (table != NULL) { - hashinfo *colorhashtable; - MALLOCARRAY_NOFAIL(colorhashtable, HASHSIZE); - for (i=0; iflag == -1) { - hashrun->color = (table[colval]).color; - hashrun->flag = colval; - } - else { - while (hashrun->next != NULL) - hashrun = hashrun->next; - MALLOCVAR_NOFAIL(hashrun->next); - hashrun = hashrun->next; - hashrun->color = (table[colval]).color; - hashrun->flag = colval; - hashrun->next = NULL; - } - } - break; - /* other */ - default: - for (colval=0; colvalflag == -1) { - hashrun->color = (table[colval]).color; - hashrun->flag = colval; - } - else { - while (hashrun->next != NULL) - hashrun = hashrun->next; - MALLOCVAR_NOFAIL(hashrun->next); - hashrun = hashrun->next; - hashrun->color = (table[colval]).color; - hashrun->flag = colval; - hashrun->next = NULL; - } - } - } - lookuptabledata(cols, rows, enlarge, medias); - for (row=0; rowcolor), *xP)) - if (hashrun->next != NULL) - hashrun = hashrun->next; - else { - pm_error("you just found a lethal bug."); - } - datum(hashrun->flag); - } - } - free(colorhashtable); - } - else { - /* $#%@^!& no lut possible, so send the pic as 24bit */ - pm_message("found too many colors for fast lookuptable mode"); - frametransferinit(cols, rows, sharpness, enlarge, copy, medias); - switch(PPM_FORMAT_TYPE(format)) { - /* color */ - case PPM_TYPE: - COLORDES(RED); - DATASTART; /* red coming */ - for (row=0; rowflag == -1) { + hashrun->color = (table[colval]).color; + hashrun->flag = colval; + } else { + while (hashrun->next != NULL) + hashrun = hashrun->next; + MALLOCVAR_NOFAIL(hashrun->next); + hashrun = hashrun->next; + hashrun->color = (table[colval]).color; + hashrun->flag = colval; + hashrun->next = NULL; + } + } +} + + + static void -check_and_rotate(int cols, int rows, int enlarge, struct mediasize medias) -#else -static int -check_and_rotate(cols, rows, enlarge, medias) - int cols, rows, enlarge; +grayRow(colorhist_vector const table, + unsigned int const colanz, + hashinfo * const colorhashtable) { + + unsigned int colval; + for (colval = 0; colval < colanz; ++colval) { + struct hashinfo * const hashchain = + &colorhashtable[myhash((table[colval]).color)]; + struct hashinfo * hashrun; + + cmd('$'); + datum(colval); + datum(PPM_GETB((table[colval]).color)); + datum(PPM_GETB((table[colval]).color)); + datum(PPM_GETB((table[colval]).color)); + + hashrun = hashchain; /* start at beginning of chain */ + + if (hashrun->flag == -1) { + hashrun->color = (table[colval]).color; + hashrun->flag = colval; + } + else { + while (hashrun->next != NULL) + hashrun = hashrun->next; + MALLOCVAR_NOFAIL(hashrun->next); + hashrun = hashrun->next; + hashrun->color = (table[colval]).color; + hashrun->flag = colval; + hashrun->next = NULL; + } + } +} + + + +static void +useLookupTable(pixel ** const pixels, + colorhist_vector const table, + int const sharpness, + int const enlarge, + int const copy, + struct mediasize const medias, + unsigned int const cols, + unsigned int const rows, + int const format, + unsigned int const colanz) { + + hashinfo * colorhashtable; + + pm_message("found %u colors - using the lookuptable-method", colanz); + + MALLOCARRAY_NOFAIL(colorhashtable, HASHSIZE); + { + unsigned int i; + for (i = 0; i < HASHSIZE; ++i) { + colorhashtable[i].flag = -1; + colorhashtable[i].next = NULL; + } + } + + lookuptableinit(sharpness, enlarge, copy, medias); + switch(PPM_FORMAT_TYPE(format)) { + case PPM_TYPE: + colorRow(table, colanz, colorhashtable); + break; + default: + grayRow(table, colanz, colorhashtable); + } + lookuptabledata(cols, rows, enlarge, medias); + { + unsigned int row; + for (row = 0; row < rows; ++row) { + unsigned int col; + for (col = 0; col < cols; ++col) { + pixel * const pixrow = pixels[row]; + struct hashinfo * const hashchain = + &colorhashtable[myhash(pixrow[col])]; + struct hashinfo * p; + + p = hashchain; + while (!PPM_EQUAL((p->color), pixrow[col])) { + assert(p->next); + p = p->next; + } + datum(p->flag); + } + } + } + free(colorhashtable); +} + + + +static void +noLookupColor(pixel ** const pixels, + unsigned int const cols, + unsigned int const rows) { + + unsigned int row; + COLORDES(RED); + DATASTART; /* red coming */ + for (row = 0; row < rows; ++row) { + pixel * const pixrow = pixels[row]; + unsigned int col; + for (col = 0; col < cols; ++col) + datum(PPM_GETR(pixrow[col])); + } + COLORDES(GREEN); + DATASTART; /* green coming */ + for (row = 0; row < rows; ++row) { + pixel * const pixrow = pixels[row]; + unsigned int col; + for (col = 0; col < cols; ++col) + datum(PPM_GETG(pixrow[col])); + } + COLORDES(BLUE); + DATASTART; /* blue coming */ + for (row = 0; row < rows; ++row) { + pixel * const pixrow = pixels[row]; + unsigned int col; + for (col = 0; col < cols; ++col) + datum(PPM_GETB(pixrow[col])); + } +} + + + +static void +noLookupGray(pixel ** const pixels, + unsigned int const cols, + unsigned int const rows) { + + unsigned int row; + COLORDES(RED); + DATASTART; /* red coming */ + for (row = 0; row < rows; ++row) { + pixel * const pixrow = pixels[row]; + unsigned int col; + for (col = 0; col < cols; ++col) + datum(PPM_GETB(pixrow[col])); + } + COLORDES(GREEN); + DATASTART; /* green coming */ + for (row = 0; row < rows; ++row) { + pixel * const pixrow = pixels[row]; + unsigned int col; + for (col = 0; col < cols; ++col) + datum(PPM_GETB(pixrow[col])); + } + COLORDES(BLUE); + DATASTART; /* blue coming */ + for (row = 0; row < rows; ++row) { + pixel * const pixrow = pixels[row]; + unsigned int col; + for (col = 0; col < cols; ++col) + datum(PPM_GETB(pixrow[col])); + } +} + + + +static void +useNoLookupTable(pixel ** const pixels, + int const sharpness, + int const enlarge, + int const copy, + struct mediasize const medias, + unsigned int const cols, + unsigned int const rows, + int const format) { + + /* $#%@^!& no lut possible, so send the pic as 24bit */ + + pm_message("found too many colors for fast lookuptable mode"); + + frametransferinit(cols, rows, sharpness, enlarge, copy, medias); + switch(PPM_FORMAT_TYPE(format)) { + case PPM_TYPE: + noLookupColor(pixels, cols, rows); + break; + default: + noLookupGray(pixels, cols, rows); + } +} + + + +static void +doTiny(FILE * const ifP, + unsigned int const cols, + unsigned int const rows, + pixval const maxval, + int const format, + int const sharpness, + int const enlarge, + int const copy, + struct mediasize const medias) { + + pixel * pixelrow; + unsigned char * redrow; + unsigned char * grnrow; + unsigned char * blurow; + unsigned int row; + + pixelrow = ppm_allocrow(cols); + MALLOCARRAY_NOFAIL(redrow, cols); + MALLOCARRAY_NOFAIL(grnrow, cols); + MALLOCARRAY_NOFAIL(blurow, cols); + lineputinit(cols, rows, sharpness, enlarge, copy, medias); + + for (row = 0; row < rows; ++row) { + ppm_readppmrow(ifP, pixelrow, cols, maxval, format); + switch(PPM_FORMAT_TYPE(format)) { + case PPM_TYPE: { /* color */ + unsigned int col; + for (col = 0; col < cols; ++col) { + redrow[col] = PPM_GETR(pixelrow[col]); + grnrow[col] = PPM_GETG(pixelrow[col]); + blurow[col] = PPM_GETB(pixelrow[col]); + } + data(redrow, cols); + data(grnrow, cols); + data(blurow, cols); + } break; + default: { /* grayscale */ + unsigned int col; + for (col = 0; col < cols; ++col) + blurow[col] = PPM_GETB(pixelrow[col]); + data(blurow, cols); + data(blurow, cols); + data(blurow, cols); + } + } + } +} + + + +int +main(int argc, char * argv[]) { + FILE * ifP; + int argn; + bool dpi300; + int cols, rows, format; + pixval maxval; + int sharpness, enlarge, copy, tiny; struct mediasize medias; -#endif -{ - if (cols > rows) { - ROTATEIMG(DOROTATE); /* rotate image */ - if (enlarge*rows > medias.maxcols || enlarge*cols > medias.maxrows) { - pm_error("Image too large, MaxPixels = %d x %d", medias.maxrows, medias.maxcols); + char media[16]; + const char * const usage = "[-sharpness <1-4>] [-enlarge <1-3>] [-media ] [-copy <1-9>] [-tiny] [-dpi300] [ppmfile]"; + + ppm_init(&argc, argv); + + dpi300 = FALSE; + argn = 1; + sharpness = 32; + enlarge = 1; + copy = 1; + memset(media, '\0', 16); + tiny = FALSE; + + /* check for flags */ + while (argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0') { + if (pm_keymatch(argv[argn], "-sharpness", 2)) { + ++argn; + if (argn == argc || sscanf(argv[argn], "%d", &sharpness) != 1) + pm_usage(usage); + else if (sharpness < 1 || sharpness > 4) + pm_usage(usage); } - HPIXELS(cols); - VPIXELS(rows); - HPIXELSOFF((medias.maxcols/enlarge - rows)/2); - VPIXELSOFF((medias.maxrows/enlarge - cols)/2); - pm_message("rotating image for output"); + else if (pm_keymatch(argv[argn], "-enlarge", 2)) { + ++argn; + if (argn == argc || sscanf(argv[argn], "%d", &enlarge) != 1) + pm_usage(usage); + else if (enlarge < 1 || enlarge > 3) + pm_usage(usage); + } + else if (pm_keymatch(argv[argn], "-media", 2)) { + ++argn; + if (argn == argc || sscanf(argv[argn], "%15s", media) < 1) + pm_usage(usage); + else if (TOUPPER(media[0]) != 'A') + pm_usage(usage); } - else { - ROTATEIMG(DONTROTATE); - if (enlarge*rows > medias.maxrows || enlarge*cols > medias.maxcols) { - pm_error("Image too large, MaxPixels = %d x %d", medias.maxrows, medias.maxcols); + else if (pm_keymatch(argv[argn], "-copy", 2)) { + ++argn; + if (argn == argc || sscanf(argv[argn], "%d", ©) != 1) + pm_usage(usage); + else if (copy < 1 || copy > 9) + pm_usage(usage); } - HPIXELS(cols); - VPIXELS(rows); - HPIXELSOFF((medias.maxcols/enlarge - cols)/2); - VPIXELSOFF((medias.maxrows/enlarge - rows)/2); + else if (pm_keymatch(argv[argn], "-dpi300", 2)) + dpi300 = TRUE; + else if (pm_keymatch(argv[argn], "-tiny", 2)) + tiny = TRUE; + else + pm_usage(usage); + ++argn; } + + if (argn < argc) { + ifP = pm_openr(argv[argn]); + ++argn; + } + else + ifP = stdin; + + if (argn != argc) + pm_usage(usage); + + if (TOUPPER(media[0]) == 'A') + switch (TOUPPER(media[1])) { + case 'S': + medias = MSize_AS; + break; + case '4': + if(TOUPPER(media[2]) == 'S') + medias = MSize_A4S; + else { + medias = MSize_A4; + } + break; + default: + medias = MSize_A; + } + else + medias = MSize_User; + + if (dpi300) { + medias.maxcols *= 2; + medias.maxrows *= 2; + } + + ppm_readppminit(ifP, &cols, &rows, &maxval, &format); + + if (tiny) { + doTiny(ifP, cols, rows, maxval, format, + sharpness, enlarge, copy, medias); + + } else { + pixel ** pixels; + int colanz; + colorhist_vector table; + unsigned int row; + + pixels = ppm_allocarray(cols, rows); + for (row = 0; row < rows; row++) + ppm_readppmrow(ifP, pixels[row], cols, maxval, format); + + /* first check wether we can use the lut transfer */ + + table = ppm_computecolorhist(pixels, cols, rows, MAXLUTCOL+1, + &colanz); + if (table) + useLookupTable(pixels, table, sharpness, enlarge, copy, medias, + cols, rows, format, colanz); + else + useNoLookupTable(pixels, sharpness, enlarge, copy, medias, + cols, rows, format); + ppm_freearray(pixels, rows); + } + PRINTIT; + pm_close(ifP); + return 0; } diff --git a/converter/ppm/ppmtomitsu.test b/converter/ppm/ppmtomitsu.test new file mode 100644 index 00000000..1805838a --- /dev/null +++ b/converter/ppm/ppmtomitsu.test @@ -0,0 +1,12 @@ +echo Test 1. Should print 3110813682 101562 +./ppmtomitsu ../../testimg.ppm | cksum +echo Test 2. Should print 239186803 34399 +pnmquant 100 ../../testimg.ppm | ./ppmtomitsu | cksum +echo Test 3. Should print 816221676 310 +./ppmtomitsu ../../testgrid.pbm | cksum +echo Test 4. Should print 629834989 752 +./ppmtomitsu -tiny ../../testgrid.pbm | cksum +echo Test 5. Should print 3999654426 101549 +./ppmtomitsu -tiny ../../testimg.ppm | cksum +echo Test 2. Should print 3078685526 101549 +pnmquant 100 ../../testimg.ppm | ./ppmtomitsu -tiny | cksum diff --git a/converter/ppm/ppmtompeg/Makefile b/converter/ppm/ppmtompeg/Makefile index 6db72ea9..e5ad6c58 100644 --- a/converter/ppm/ppmtompeg/Makefile +++ b/converter/ppm/ppmtompeg/Makefile @@ -39,7 +39,15 @@ endif # MP_BASE_OBJS = mfwddct.o postdct.o huff.o bitio.o mheaders.o -MP_ENCODE_OBJS = iframe.o pframe.o bframe.o psearch.o bsearch.o block.o +MP_ENCODE_OBJS = \ + frames.o \ + iframe.o \ + pframe.o \ + bframe.o \ + psearch.o \ + bsearch.o \ + block.o + MP_OTHER_OBJS = mpeg.o subsample.o param.o rgbtoycc.o \ readframe.o combine.o jrevdct.o frame.o fsize.o frametype.o \ specifics.o rate.o opts.o input.o gethostname.o diff --git a/converter/ppm/ppmtompeg/bframe.c b/converter/ppm/ppmtompeg/bframe.c index 5dfb76d3..1dbc1846 100644 --- a/converter/ppm/ppmtompeg/bframe.c +++ b/converter/ppm/ppmtompeg/bframe.c @@ -84,7 +84,6 @@ static struct bframeStats { *====================*/ extern Block **dct, **dctr, **dctb; -extern dct_data_type **dct_data; #define NO_MOTION 0 #define MOTION 1 #define SKIP 2 /* used in useMotion in dct_data */ diff --git a/converter/ppm/ppmtompeg/bsearch.c b/converter/ppm/ppmtompeg/bsearch.c index 142987f5..70edfef6 100644 --- a/converter/ppm/ppmtompeg/bsearch.c +++ b/converter/ppm/ppmtompeg/bsearch.c @@ -77,6 +77,7 @@ #include "frames.h" #include "motion_search.h" #include "fsize.h" +#include "block.h" /*==================* diff --git a/converter/ppm/ppmtompeg/frames.c b/converter/ppm/ppmtompeg/frames.c new file mode 100644 index 00000000..a0764890 --- /dev/null +++ b/converter/ppm/ppmtompeg/frames.c @@ -0,0 +1,58 @@ +#include "mallocvar.h" +#include "fsize.h" + +#include "frames.h" + + +Block **dct=NULL, **dctr=NULL, **dctb=NULL; +dct_data_type **dct_data; /* used in p/bframe.c */ + + +/*===========================================================================* + * + * AllocDctBlocks + * + * allocate memory for dct blocks + * + * RETURNS: nothing + * + * SIDE EFFECTS: creates dct, dctr, dctb + * + *===========================================================================*/ +void +AllocDctBlocks(void) { + + int dctx, dcty; + int i; + + dctx = Fsize_x / DCTSIZE; + dcty = Fsize_y / DCTSIZE; + + MALLOCARRAY(dct, dcty); + ERRCHK(dct, "malloc"); + for (i = 0; i < dcty; ++i) { + dct[i] = (Block *) malloc(sizeof(Block) * dctx); + ERRCHK(dct[i], "malloc"); + } + + MALLOCARRAY(dct_data, dcty); + ERRCHK(dct_data, "malloc"); + for (i = 0; i < dcty; ++i) { + MALLOCARRAY(dct_data[i], dctx); + ERRCHK(dct[i], "malloc"); + } + + MALLOCARRAY(dctr, dcty/2); + ERRCHK(dctr, "malloc"); + MALLOCARRAY(dctb, dcty/2); + ERRCHK(dctb, "malloc"); + for (i = 0; i < dcty/2; ++i) { + MALLOCARRAY(dctr[i], dctx/2); + ERRCHK(dctr[i], "malloc"); + MALLOCARRAY(dctb[i], dctx/2); + ERRCHK(dctb[i], "malloc"); + } +} + + + diff --git a/converter/ppm/ppmtompeg/headers/bitio.h b/converter/ppm/ppmtompeg/headers/bitio.h index 89e61fbb..a24c21cd 100644 --- a/converter/ppm/ppmtompeg/headers/bitio.h +++ b/converter/ppm/ppmtompeg/headers/bitio.h @@ -60,9 +60,7 @@ #define BIT_IO_INCLUDED -/*==============* - * HEADER FILES * - *==============*/ +#include #include "general.h" #include "ansi.h" diff --git a/converter/ppm/ppmtompeg/headers/block.h b/converter/ppm/ppmtompeg/headers/block.h index 46050492..22d306a1 100644 --- a/converter/ppm/ppmtompeg/headers/block.h +++ b/converter/ppm/ppmtompeg/headers/block.h @@ -1,3 +1,46 @@ +#ifndef BLOCK_H_INCLUDED + +#include "frame.h" +#include "mtypes.h" + +/* DIFFERENCE FUNCTIONS */ + +int32 +LumBlockMAD(const LumBlock * const currentBlockP, + const LumBlock * const motionBlockP, + int32 const bestSoFar); + +int32 +LumBlockMSE(const LumBlock * const currentBlockP, + const LumBlock * const motionBlockP, + int32 const bestSoFar); + +int32 +LumMotionError(const LumBlock * const currentBlockP, + MpegFrame * const prev, + int const by, + int const bx, + vector const m, + int32 const bestSoFar); + +int32 +LumAddMotionError(const LumBlock * const currentBlockP, + const LumBlock * const blockSoFarP, + MpegFrame * const prev, + int const by, + int const bx, + vector const m, + int32 const bestSoFar); + +int32 +LumMotionErrorSubSampled(const LumBlock * const currentBlockP, + MpegFrame * const prevFrame, + int const by, + int const bx, + vector const m, + int const startY, + int const startX); + void ComputeDiffDCTs(MpegFrame * const current, MpegFrame * const prev, @@ -51,3 +94,4 @@ AddBMotionBlock(Block block, void BlockifyFrame(MpegFrame * const frameP); +#endif diff --git a/converter/ppm/ppmtompeg/headers/frame.h b/converter/ppm/ppmtompeg/headers/frame.h index 6df3d19f..acd74419 100644 --- a/converter/ppm/ppmtompeg/headers/frame.h +++ b/converter/ppm/ppmtompeg/headers/frame.h @@ -130,18 +130,4 @@ Frame_Resize(MpegFrame * const omf, int const outsize_x, int const outsize_y); - -extern void Frame_Free _ANSI_ARGS_((MpegFrame * const frame)); -extern void Frame_Exit _ANSI_ARGS_((void)); -extern void Frame_AllocPPM _ANSI_ARGS_((MpegFrame * frame)); -extern void Frame_AllocYCC _ANSI_ARGS_((MpegFrame * const mf)); -extern void Frame_AllocDecoded _ANSI_ARGS_((MpegFrame * const frame, - boolean const makeReference)); -extern void Frame_AllocHalf _ANSI_ARGS_((MpegFrame * const frame)); -extern void Frame_AllocBlocks _ANSI_ARGS_((MpegFrame * const mf)); -extern void Frame_Resize _ANSI_ARGS_((MpegFrame * const omf, MpegFrame * const mf, - int const insize_x, int const insize_y, - int const outsize_x, int const outsize_y)); - - -#endif /* FRAME_INCLUDED */ +#endif diff --git a/converter/ppm/ppmtompeg/headers/frames.h b/converter/ppm/ppmtompeg/headers/frames.h index 14304c48..17820127 100644 --- a/converter/ppm/ppmtompeg/headers/frames.h +++ b/converter/ppm/ppmtompeg/headers/frames.h @@ -16,6 +16,7 @@ #include "ansi.h" #include "mtypes.h" #include "mheaders.h" +#include "iframe.h" #include "frame.h" @@ -86,11 +87,13 @@ typedef struct dct_data_tye_struct { int fmotionX, fmotionY, bmotionX, bmotionY; } dct_data_type; -void EncodeYDC _ANSI_ARGS_((int32 const dc_term, int32 * const pred_term, BitBucket * const bb)); -void -EncodeCDC(int32 const dc_term, - int32 * const pred_term, - BitBucket * const bb); + +/*==================* + * GLOBAL VARIABLES * + *==================*/ + +extern Block **dct, **dctr, **dctb; +extern dct_data_type **dct_data; /*========* @@ -101,63 +104,6 @@ EncodeCDC(int32 const dc_term, #define int_ceil_div(a,b,c) ((b*(c = a/b) < a) ? (c+1) : c) #define int_floor_div(a,b,c) ((b*(c = a/b) > a) ? (c-1) : c) -/* assumes many things: - * block indices are (y,x) - * variables y_dc_pred, cr_dc_pred, and cb_dc_pred - * flat block fb exists - */ -#define GEN_I_BLOCK(frameType, frame, bb, mbAI, qscale) { \ - boolean overflow, overflowChange=FALSE; \ - int overflowValue = 0; \ - do { \ - overflow = Mpost_QuantZigBlock(dct[y][x], fb[0], \ - qscale, TRUE)==MPOST_OVERFLOW; \ - overflow |= Mpost_QuantZigBlock(dct[y][x+1], fb[1], \ - qscale, TRUE)==MPOST_OVERFLOW; \ - overflow |= Mpost_QuantZigBlock(dct[y+1][x], fb[2], \ - qscale, TRUE)==MPOST_OVERFLOW; \ - overflow |= Mpost_QuantZigBlock(dct[y+1][x+1], fb[3], \ - qscale, TRUE)==MPOST_OVERFLOW; \ - overflow |= Mpost_QuantZigBlock(dctb[y >> 1][x >> 1], \ - fb[4], qscale, TRUE)==MPOST_OVERFLOW; \ - overflow |= Mpost_QuantZigBlock(dctr[y >> 1][x >> 1], \ - fb[5], qscale, TRUE)==MPOST_OVERFLOW; \ - if ((overflow) && (qscale!=31)) { \ - overflowChange = TRUE; overflowValue++; \ - qscale++; \ - } else overflow = FALSE; \ - } while (overflow); \ - Mhead_GenMBHeader(bb, \ - frameType /* pict_code_type */, mbAI /* addr_incr */, \ - qscale /* q_scale */, \ - 0 /* forw_f_code */, 0 /* back_f_code */, \ - 0 /* horiz_forw_r */, 0 /* vert_forw_r */, \ - 0 /* horiz_back_r */, 0 /* vert_back_r */, \ - 0 /* motion_forw */, 0 /* m_horiz_forw */, \ - 0 /* m_vert_forw */, 0 /* motion_back */, \ - 0 /* m_horiz_back */, 0 /* m_vert_back */, \ - 0 /* mb_pattern */, TRUE /* mb_intra */); \ - \ - /* Y blocks */ \ - EncodeYDC(fb[0][0], &y_dc_pred, bb); \ - Mpost_RLEHuffIBlock(fb[0], bb); \ - EncodeYDC(fb[1][0], &y_dc_pred, bb); \ - Mpost_RLEHuffIBlock(fb[1], bb); \ - EncodeYDC(fb[2][0], &y_dc_pred, bb); \ - Mpost_RLEHuffIBlock(fb[2], bb); \ - EncodeYDC(fb[3][0], &y_dc_pred, bb); \ - Mpost_RLEHuffIBlock(fb[3], bb); \ - \ - /* CB block */ \ - EncodeCDC(fb[4][0], &cb_dc_pred, bb); \ - Mpost_RLEHuffIBlock(fb[4], bb); \ - \ - /* CR block */ \ - EncodeCDC(fb[5][0], &cr_dc_pred, bb); \ - Mpost_RLEHuffIBlock(fb[5], bb); \ - if (overflowChange) qscale -= overflowValue; \ - } - #define BLOCK_TO_FRAME_COORD(bx1, bx2, x1, x2) { \ x1 = (bx1)*DCTSIZE; \ x2 = (bx2)*DCTSIZE; \ @@ -253,6 +199,9 @@ encodeMotionVector(int const x, * EXTERNAL PROCEDURE prototypes * *===============================*/ +void +AllocDctBlocks(void); + void ComputeBMotionLumBlock(MpegFrame * const prev, MpegFrame * const next, @@ -271,8 +220,6 @@ BMotionSearch(const LumBlock * const currentBlockP, motion * const motionP, int const oldMode); -void GenIFrame (BitBucket * const bb, - MpegFrame * const mf); void GenPFrame (BitBucket * const bb, MpegFrame * const current, MpegFrame * const prev); @@ -280,11 +227,6 @@ void GenBFrame (BitBucket * const bb, MpegFrame * const curr, MpegFrame * const prev, MpegFrame * const next); -void AllocDctBlocks _ANSI_ARGS_((void )); - - -float -IFrameTotalTime(void); float PFrameTotalTime(void); @@ -292,11 +234,6 @@ PFrameTotalTime(void); float BFrameTotalTime(void); -void -ShowIFrameSummary(unsigned int const inputFrameBits, - unsigned int const totalBits, - FILE * const fpointer); - void ShowPFrameSummary(unsigned int const inputFrameBits, unsigned int const totalBits, @@ -307,87 +244,6 @@ ShowBFrameSummary(unsigned int const inputFrameBits, unsigned int const totalBits, FILE * const fpointer); -/* DIFFERENCE FUNCTIONS */ - -int32 -LumBlockMAD(const LumBlock * const currentBlockP, - const LumBlock * const motionBlockP, - int32 const bestSoFar); - -int32 -LumBlockMSE(const LumBlock * const currentBlockP, - const LumBlock * const motionBlockP, - int32 const bestSoFar); - -int32 -LumMotionError(const LumBlock * const currentBlockP, - MpegFrame * const prev, - int const by, - int const bx, - vector const m, - int32 const bestSoFar); - -int32 -LumAddMotionError(const LumBlock * const currentBlockP, - const LumBlock * const blockSoFarP, - MpegFrame * const prev, - int const by, - int const bx, - vector const m, - int32 const bestSoFar); - -int32 -LumMotionErrorA(const LumBlock * const currentBlockP, - MpegFrame * const prevFrame, - int const by, - int const bx, - vector const m, - int32 const bestSoFar); - -int32 -LumMotionErrorB(const LumBlock * const currentP, - MpegFrame * const prevFrame, - int const by, - int const bx, - vector const m, - int32 const bestSoFar); - -int32 -LumMotionErrorC(const LumBlock * const currentP, - MpegFrame * const prevFrame, - int const by, - int const bx, - vector const m, - int32 const bestSoFar); - -int32 -LumMotionErrorD(const LumBlock * const currentP, - MpegFrame * const prevFrame, - int const by, - int const bx, - vector const m, - int32 const bestSoFar); - -int32 -LumMotionErrorSubSampled(const LumBlock * const currentBlockP, - MpegFrame * const prevFrame, - int const by, - int const bx, - vector const m, - int const startY, - int const startX); - -void -BlockComputeSNR(MpegFrame * const current, - float * const snr, - float * const psnr); - -int32 -time_elapsed(void); - -void -AllocDctBlocks(void); - /*==================* * GLOBAL VARIABLES * *==================*/ @@ -413,7 +269,7 @@ extern int fCodeI,fCodeP,fCodeB; extern boolean forceEncodeLast; extern int TIME_RATE; -#endif /* FRAMES_INCLUDED */ +#endif /* diff --git a/converter/ppm/ppmtompeg/headers/iframe.h b/converter/ppm/ppmtompeg/headers/iframe.h new file mode 100644 index 00000000..c4f77c74 --- /dev/null +++ b/converter/ppm/ppmtompeg/headers/iframe.h @@ -0,0 +1,118 @@ +#ifndef IFRAME_H_INCLUDED +#define IFRAME_H_INCLUDED + +#include "frame.h" + +void +SetFCode(void); + +void +SetSlicesPerFrame(int const number); + +void +SetBlocksPerSlice(void); + +void +SetIQScale(int const qI); + +int +GetIQScale(void); + +void +GenIFrame(BitBucket * const bb, + MpegFrame * const current); + +void +ResetIFrameStats(void); + +float +IFrameTotalTime(void); + + +void +ShowIFrameSummary(unsigned int const inputFrameBits, + unsigned int const totalBits, + FILE * const fpointer); + +void +EncodeYDC(int32 const dc_term, + int32 * const pred_term, + BitBucket * const bb); + +void +EncodeCDC(int32 const dc_term, + int32 * const pred_term, + BitBucket * const bb); + +void +BlockComputeSNR(MpegFrame * const current, + float * const snr, + float * const psnr); + +void +WriteDecodedFrame(MpegFrame * const frame); + +void +PrintItoIBitRate(int const numBits, + int const frameNum); + +int32 time_elapsed(void); + +/* assumes many things: + * block indices are (y,x) + * variables y_dc_pred, cr_dc_pred, and cb_dc_pred + * flat block fb exists + */ +#define GEN_I_BLOCK(frameType, frame, bb, mbAI, qscale) { \ + boolean overflow, overflowChange=FALSE; \ + int overflowValue = 0; \ + do { \ + overflow = Mpost_QuantZigBlock(dct[y][x], fb[0], \ + qscale, TRUE)==MPOST_OVERFLOW; \ + overflow |= Mpost_QuantZigBlock(dct[y][x+1], fb[1], \ + qscale, TRUE)==MPOST_OVERFLOW; \ + overflow |= Mpost_QuantZigBlock(dct[y+1][x], fb[2], \ + qscale, TRUE)==MPOST_OVERFLOW; \ + overflow |= Mpost_QuantZigBlock(dct[y+1][x+1], fb[3], \ + qscale, TRUE)==MPOST_OVERFLOW; \ + overflow |= Mpost_QuantZigBlock(dctb[y >> 1][x >> 1], \ + fb[4], qscale, TRUE)==MPOST_OVERFLOW; \ + overflow |= Mpost_QuantZigBlock(dctr[y >> 1][x >> 1], \ + fb[5], qscale, TRUE)==MPOST_OVERFLOW; \ + if ((overflow) && (qscale!=31)) { \ + overflowChange = TRUE; overflowValue++; \ + qscale++; \ + } else overflow = FALSE; \ + } while (overflow); \ + Mhead_GenMBHeader(bb, \ + frameType /* pict_code_type */, mbAI /* addr_incr */, \ + qscale /* q_scale */, \ + 0 /* forw_f_code */, 0 /* back_f_code */, \ + 0 /* horiz_forw_r */, 0 /* vert_forw_r */, \ + 0 /* horiz_back_r */, 0 /* vert_back_r */, \ + 0 /* motion_forw */, 0 /* m_horiz_forw */, \ + 0 /* m_vert_forw */, 0 /* motion_back */, \ + 0 /* m_horiz_back */, 0 /* m_vert_back */, \ + 0 /* mb_pattern */, TRUE /* mb_intra */); \ + \ + /* Y blocks */ \ + EncodeYDC(fb[0][0], &y_dc_pred, bb); \ + Mpost_RLEHuffIBlock(fb[0], bb); \ + EncodeYDC(fb[1][0], &y_dc_pred, bb); \ + Mpost_RLEHuffIBlock(fb[1], bb); \ + EncodeYDC(fb[2][0], &y_dc_pred, bb); \ + Mpost_RLEHuffIBlock(fb[2], bb); \ + EncodeYDC(fb[3][0], &y_dc_pred, bb); \ + Mpost_RLEHuffIBlock(fb[3], bb); \ + \ + /* CB block */ \ + EncodeCDC(fb[4][0], &cb_dc_pred, bb); \ + Mpost_RLEHuffIBlock(fb[4], bb); \ + \ + /* CR block */ \ + EncodeCDC(fb[5][0], &cr_dc_pred, bb); \ + Mpost_RLEHuffIBlock(fb[5], bb); \ + if (overflowChange) qscale -= overflowValue; \ + } + +#endif diff --git a/converter/ppm/ppmtompeg/headers/motion_search.h b/converter/ppm/ppmtompeg/headers/motion_search.h index ab83cbca..62f3abab 100644 --- a/converter/ppm/ppmtompeg/headers/motion_search.h +++ b/converter/ppm/ppmtompeg/headers/motion_search.h @@ -100,6 +100,9 @@ PMotionSearch(const LumBlock * const currentBlockP, int const bx, vector * const motionP); +void +MotionSearchPreComputation(MpegFrame * const frameP); + /*==================* * GLOBAL VARIABLES * *==================*/ diff --git a/converter/ppm/ppmtompeg/headers/mpeg.h b/converter/ppm/ppmtompeg/headers/mpeg.h index 23875c2c..56862c42 100644 --- a/converter/ppm/ppmtompeg/headers/mpeg.h +++ b/converter/ppm/ppmtompeg/headers/mpeg.h @@ -90,8 +90,9 @@ void ReadDecodedRefFrame(MpegFrame * const frameP, unsigned int const frameNumber); -extern void WriteDecodedFrame _ANSI_ARGS_((MpegFrame * const frame)); -extern void SetBitRateFileName _ANSI_ARGS_((char *fileName)); +void +SetBitRateFileName(const char * const fileName); + extern void SetFrameRate _ANSI_ARGS_((void)); diff --git a/converter/ppm/ppmtompeg/headers/mproto.h b/converter/ppm/ppmtompeg/headers/mproto.h index c3b0f4b3..d8fefd84 100644 --- a/converter/ppm/ppmtompeg/headers/mproto.h +++ b/converter/ppm/ppmtompeg/headers/mproto.h @@ -74,7 +74,6 @@ #include "bitio.h" -#define DCTSIZE2 DCTSIZE*DCTSIZE typedef short DCTELEM; typedef DCTELEM DCTBLOCK[DCTSIZE2]; @@ -111,9 +110,6 @@ void ReadEYUV _ANSI_ARGS_((MpegFrame * mf, FILE *fpointer, int width, boolean ReadPPM _ANSI_ARGS_((MpegFrame *mf, FILE *fpointer)); void PPMtoYCC _ANSI_ARGS_((MpegFrame * mf)); -void MotionSearchPreComputation _ANSI_ARGS_((MpegFrame *frame)); -boolean PMotionSearch _ANSI_ARGS_((LumBlock currentBlock, MpegFrame *prev, - int by, int bx, int *motionY, int *motionX)); void ComputeHalfPixelData _ANSI_ARGS_((MpegFrame *frame)); void mp_validate_size _ANSI_ARGS_((int *x, int *y)); void AllocYCC _ANSI_ARGS_((MpegFrame * mf)); @@ -126,7 +122,3 @@ void j_rev_dct _ANSI_ARGS_((DCTBLOCK data )); void j_rev_dct_sparse _ANSI_ARGS_((DCTBLOCK data , int pos )); void j_rev_dct _ANSI_ARGS_((DCTBLOCK data )); -/* block.c */ -void BlockToData _ANSI_ARGS_((uint8 **data, Block block, int by, int bx)); -void AddMotionBlock _ANSI_ARGS_((Block block, uint8 **prev, int by, int bx, - int my, int mx)); diff --git a/converter/ppm/ppmtompeg/headers/parallel.h b/converter/ppm/ppmtompeg/headers/parallel.h index 90edd874..cf27ce66 100644 --- a/converter/ppm/ppmtompeg/headers/parallel.h +++ b/converter/ppm/ppmtompeg/headers/parallel.h @@ -87,7 +87,7 @@ void NoteFrameDone(int frameStart, int frameEnd); void -SetIOConvert(boolean const separate); +SetIOConvert(bool const separate); void SetRemoteShell(const char * const shell); diff --git a/converter/ppm/ppmtompeg/headers/prototypes.h b/converter/ppm/ppmtompeg/headers/prototypes.h index a284888f..b421af35 100644 --- a/converter/ppm/ppmtompeg/headers/prototypes.h +++ b/converter/ppm/ppmtompeg/headers/prototypes.h @@ -44,33 +44,26 @@ void ResetBFrameStats _ANSI_ARGS_((void)); void ResetPFrameStats _ANSI_ARGS_((void)); void SetSearchRange (int const pixelsP, int const pixelsB); -void ResetIFrameStats _ANSI_ARGS_((void)); void SetPixelSearch(const char * const searchType); -void SetIQScale _ANSI_ARGS_((int const qI)); void SetPQScale _ANSI_ARGS_((int qP)); void SetBQScale _ANSI_ARGS_((int qB)); -float EstimateSecondsPerIFrame _ANSI_ARGS_((void)); float EstimateSecondsPerPFrame _ANSI_ARGS_((void)); float EstimateSecondsPerBFrame _ANSI_ARGS_((void)); void SetGOPSize _ANSI_ARGS_((int size)); void SetStatFileName(const char * const fileName); -void SetSlicesPerFrame _ANSI_ARGS_((int const number)); -void SetBlocksPerSlice _ANSI_ARGS_((void)); void DCTFrame _ANSI_ARGS_((MpegFrame * mf)); void PPMtoYCC _ANSI_ARGS_((MpegFrame * mf)); -void MotionSearchPreComputation _ANSI_ARGS_((MpegFrame * const frame)); +void MotionSearchPreComputation _ANSI_ARGS_((MpegFrame *frame)); void ComputeHalfPixelData _ANSI_ARGS_((MpegFrame *frame)); void mp_validate_size _ANSI_ARGS_((int *x, int *y)); -extern void SetFCode _ANSI_ARGS_((void)); - /* psearch.c */ void ShowPMVHistogram _ANSI_ARGS_((FILE *fpointer)); diff --git a/converter/ppm/ppmtompeg/headers/subsample.h b/converter/ppm/ppmtompeg/headers/subsample.h new file mode 100644 index 00000000..deedf251 --- /dev/null +++ b/converter/ppm/ppmtompeg/headers/subsample.h @@ -0,0 +1,39 @@ +#ifndef SUBSAMPLE_H_INCLUDED +#define SUBSAMPLE_H_INCLUDED + +#include "frame.h" +#include "mtypes.h" + +int32 +LumMotionErrorA(const LumBlock * const currentBlockP, + MpegFrame * const prevFrame, + int const by, + int const bx, + vector const m, + int32 const bestSoFar); + +int32 +LumMotionErrorB(const LumBlock * const currentP, + MpegFrame * const prevFrame, + int const by, + int const bx, + vector const m, + int32 const bestSoFar); + +int32 +LumMotionErrorC(const LumBlock * const currentP, + MpegFrame * const prevFrame, + int const by, + int const bx, + vector const m, + int32 const bestSoFar); + +int32 +LumMotionErrorD(const LumBlock * const currentP, + MpegFrame * const prevFrame, + int const by, + int const bx, + vector const m, + int32 const bestSoFar); + +#endif diff --git a/converter/ppm/ppmtompeg/iframe.c b/converter/ppm/ppmtompeg/iframe.c index f4d7665a..c6735505 100644 --- a/converter/ppm/ppmtompeg/iframe.c +++ b/converter/ppm/ppmtompeg/iframe.c @@ -11,10 +11,9 @@ * GetIQScale * * ResetIFrameStats * * ShowIFrameSummary * - * EstimateSecondsPerIFrame * * EncodeYDC * * EncodeCDC * - * time_elapsed * + * time_elapsed * * * *===========================================================================*/ @@ -67,6 +66,8 @@ #include "specifics.h" #include "opts.h" +#include "iframe.h" + /*==================* * STATIC VARIABLES * *==================*/ @@ -128,24 +129,11 @@ int fCodeI, fCodeP, fCodeB; boolean printSNR = FALSE; boolean printMSE = FALSE; boolean decodeRefFrames = FALSE; -Block **dct=NULL, **dctr=NULL, **dctb=NULL; -dct_data_type **dct_data; /* used in p/bframe.c */ int TIME_RATE; -/*=====================* - * EXPORTED PROCEDURES * - *=====================*/ -extern void PrintItoIBitRate _ANSI_ARGS_((int const numBits, int const frameNum)); - -/*===============================* - * INTERNAL PROCEDURE prototypes * - *===============================*/ -void AllocDctBlocks(void ); -int SetFCodeHelper (int const sr); -void CalcDistortion (MpegFrame * const current, int const y, int const x); -int +static int SetFCodeHelper(int const SR) { int range,fCode; @@ -296,6 +284,68 @@ GetIQScale(void) { +static void +CalcDistortion(MpegFrame * const current, + int const y, + int const x) { + + int qscale, distort=0; + Block decblk; + FlatBlock fblk; + int datarate = 0; + + for (qscale = 1; qscale < 32; qscale ++) { + distort = 0; + datarate = 0; + Mpost_QuantZigBlock(dct[y][x], fblk, qscale, TRUE); + Mpost_UnQuantZigBlock(fblk, decblk, qscale, TRUE); + if (collect_distortion_detailed) datarate += CalcRLEHuffLength(fblk); + mpeg_jrevdct((int16 *)decblk); + distort += mse(current->y_blocks[y][x], decblk); + + Mpost_QuantZigBlock(dct[y][x+1], fblk, qscale, TRUE); + Mpost_UnQuantZigBlock(fblk, decblk, qscale, TRUE); + if (collect_distortion_detailed) datarate += CalcRLEHuffLength(fblk); + mpeg_jrevdct((int16 *)decblk); + distort += mse(current->y_blocks[y][x+1], decblk); + + Mpost_QuantZigBlock(dct[y+1][x], fblk, qscale, TRUE); + Mpost_UnQuantZigBlock(fblk, decblk, qscale, TRUE); + if (collect_distortion_detailed) datarate += CalcRLEHuffLength(fblk); + mpeg_jrevdct((int16 *)decblk); + distort += mse(current->y_blocks[y+1][x], decblk); + + Mpost_QuantZigBlock(dct[y+1][x+1], fblk, qscale, TRUE); + Mpost_UnQuantZigBlock(fblk, decblk, qscale, TRUE); + if (collect_distortion_detailed) datarate += CalcRLEHuffLength(fblk); + mpeg_jrevdct((int16 *)decblk); + distort += mse(current->y_blocks[y+1][x+1], decblk); + + Mpost_QuantZigBlock(dctb[y >> 1][x >> 1], fblk, qscale, TRUE); + Mpost_UnQuantZigBlock(fblk, decblk, qscale, TRUE); + if (collect_distortion_detailed) datarate += CalcRLEHuffLength(fblk); + mpeg_jrevdct((int16 *)decblk); + distort += mse(current->cb_blocks[y>>1][x>>1], decblk); + + Mpost_QuantZigBlock(dctr[y >> 1][x >> 1], fblk, qscale, TRUE); + Mpost_UnQuantZigBlock(fblk, decblk, qscale, TRUE); + if (collect_distortion_detailed) datarate += CalcRLEHuffLength(fblk); + mpeg_jrevdct((int16 *)decblk); + distort += mse(current->cr_blocks[y >> 1][x >> 1], decblk); + + if (!collect_distortion_detailed) { + fprintf(distortion_fp, "\t%d\n", distort); + } else if (collect_distortion_detailed == 1) { + fprintf(distortion_fp, "\t%d\t%d\n", distort, datarate); + } else { + fprintf(fp_table_rate[qscale-1], "%d\n", datarate); + fprintf(fp_table_dist[qscale-1], "%d\n", distort); + } + } +} + + + /*===========================================================================* * * GenIFrame @@ -616,24 +666,6 @@ ShowIFrameSummary(unsigned int const inputFrameBits, -/*===========================================================================* - * - * EstimateSecondsPerIFrame - * - * estimates the number of seconds required per I-frame - * - * RETURNS: seconds (floating point value) - * - * SIDE EFFECTS: none - * - *===========================================================================*/ -float -EstimateSecondsPerIFrame() -{ - return (float)totalTime/((float)TIME_RATE*(float)numFrames); -} - - /*===========================================================================* * * EncodeYDC @@ -926,53 +958,6 @@ PrintItoIBitRate(int const numBits, -/*===========================================================================* - * - * AllocDctBlocks - * - * allocate memory for dct blocks - * - * RETURNS: nothing - * - * SIDE EFFECTS: creates dct, dctr, dctb - * - *===========================================================================*/ -void -AllocDctBlocks(void) { - int dctx, dcty; - int i; - - dctx = Fsize_x / DCTSIZE; - dcty = Fsize_y / DCTSIZE; - - dct = (Block **) malloc(sizeof(Block *) * dcty); - ERRCHK(dct, "malloc"); - for (i = 0; i < dcty; i++) { - dct[i] = (Block *) malloc(sizeof(Block) * dctx); - ERRCHK(dct[i], "malloc"); - } - - dct_data = (dct_data_type **) malloc(sizeof(dct_data_type *) * dcty); - ERRCHK(dct_data, "malloc"); - for (i = 0; i < dcty; i++) { - dct_data[i] = (dct_data_type *) malloc(sizeof(dct_data_type) * dctx); - ERRCHK(dct[i], "malloc"); - } - - dctr = (Block **) malloc(sizeof(Block *) * (dcty >> 1)); - dctb = (Block **) malloc(sizeof(Block *) * (dcty >> 1)); - ERRCHK(dctr, "malloc"); - ERRCHK(dctb, "malloc"); - for (i = 0; i < (dcty >> 1); i++) { - dctr[i] = (Block *) malloc(sizeof(Block) * (dctx >> 1)); - dctb[i] = (Block *) malloc(sizeof(Block) * (dctx >> 1)); - ERRCHK(dctr[i], "malloc"); - ERRCHK(dctb[i], "malloc"); - } -} - - - /*======================================================================* * * time_elapsed @@ -997,66 +982,4 @@ int32 time_elapsed(void) { -void -CalcDistortion(MpegFrame * const current, - int const y, - int const x) { - - int qscale, distort=0; - Block decblk; - FlatBlock fblk; - int datarate = 0; - - for (qscale = 1; qscale < 32; qscale ++) { - distort = 0; - datarate = 0; - Mpost_QuantZigBlock(dct[y][x], fblk, qscale, TRUE); - Mpost_UnQuantZigBlock(fblk, decblk, qscale, TRUE); - if (collect_distortion_detailed) datarate += CalcRLEHuffLength(fblk); - mpeg_jrevdct((int16 *)decblk); - distort += mse(current->y_blocks[y][x], decblk); - - Mpost_QuantZigBlock(dct[y][x+1], fblk, qscale, TRUE); - Mpost_UnQuantZigBlock(fblk, decblk, qscale, TRUE); - if (collect_distortion_detailed) datarate += CalcRLEHuffLength(fblk); - mpeg_jrevdct((int16 *)decblk); - distort += mse(current->y_blocks[y][x+1], decblk); - - Mpost_QuantZigBlock(dct[y+1][x], fblk, qscale, TRUE); - Mpost_UnQuantZigBlock(fblk, decblk, qscale, TRUE); - if (collect_distortion_detailed) datarate += CalcRLEHuffLength(fblk); - mpeg_jrevdct((int16 *)decblk); - distort += mse(current->y_blocks[y+1][x], decblk); - - Mpost_QuantZigBlock(dct[y+1][x+1], fblk, qscale, TRUE); - Mpost_UnQuantZigBlock(fblk, decblk, qscale, TRUE); - if (collect_distortion_detailed) datarate += CalcRLEHuffLength(fblk); - mpeg_jrevdct((int16 *)decblk); - distort += mse(current->y_blocks[y+1][x+1], decblk); - - Mpost_QuantZigBlock(dctb[y >> 1][x >> 1], fblk, qscale, TRUE); - Mpost_UnQuantZigBlock(fblk, decblk, qscale, TRUE); - if (collect_distortion_detailed) datarate += CalcRLEHuffLength(fblk); - mpeg_jrevdct((int16 *)decblk); - distort += mse(current->cb_blocks[y>>1][x>>1], decblk); - - Mpost_QuantZigBlock(dctr[y >> 1][x >> 1], fblk, qscale, TRUE); - Mpost_UnQuantZigBlock(fblk, decblk, qscale, TRUE); - if (collect_distortion_detailed) datarate += CalcRLEHuffLength(fblk); - mpeg_jrevdct((int16 *)decblk); - distort += mse(current->cr_blocks[y >> 1][x >> 1], decblk); - - if (!collect_distortion_detailed) { - fprintf(distortion_fp, "\t%d\n", distort); - } else if (collect_distortion_detailed == 1) { - fprintf(distortion_fp, "\t%d\t%d\n", distort, datarate); - } else { - fprintf(fp_table_rate[qscale-1], "%d\n", datarate); - fprintf(fp_table_dist[qscale-1], "%d\n", distort); - } - } -} - - - diff --git a/converter/ppm/ppmtompeg/jpeg.c b/converter/ppm/ppmtompeg/jpeg.c index 3aad6364..a6e1eced 100644 --- a/converter/ppm/ppmtompeg/jpeg.c +++ b/converter/ppm/ppmtompeg/jpeg.c @@ -15,6 +15,7 @@ * HEADER FILES * *==============*/ #define _XOPEN_SOURCE /* Make sure stdio.h contains fileno() */ +#include #include #include "all.h" /* With the lossless jpeg patch applied to the Jpeg library diff --git a/converter/ppm/ppmtompeg/mheaders.c b/converter/ppm/ppmtompeg/mheaders.c index 8a51c089..4cfe49ac 100644 --- a/converter/ppm/ppmtompeg/mheaders.c +++ b/converter/ppm/ppmtompeg/mheaders.c @@ -278,24 +278,350 @@ const double VidRateNum[9]={1.0, 23.976, 24.0, 25.0, 29.97, 30.0, 50.0 ,59.94, 60.0}; -/*===============================* - * INTERNAL PROCEDURE prototypes * - *===============================*/ - -static void GenMBAddrIncr _ANSI_ARGS_((BitBucket *bb, uint32 addr_incr)); -static void GenPictHead _ANSI_ARGS_((BitBucket *bb, uint32 temp_ref, - uint32 code_type, uint32 vbv_delay, - int32 full_pel_forw_flag, uint32 forw_f_code, - int32 full_pel_back_flag, uint32 back_f_code, - uint8 *extra_info, uint32 extra_info_size, - uint8 *ext_data, uint32 ext_data_size, - uint8 *user_data, uint32 user_data_size)); -static void GenMBType _ANSI_ARGS_((BitBucket *bb, uint32 pict_code_type, - uint32 mb_quant, uint32 motion_forw, uint32 motion_back, - uint32 mb_pattern, uint32 mb_intra)); -static void GenMotionCode _ANSI_ARGS_((BitBucket * const bb, int32 const vector)); -static void GenBlockPattern _ANSI_ARGS_((BitBucket *bb, - uint32 mb_pattern)); +/*=====================* + * INTERNAL PROCEDURES * + *=====================*/ + +/*===========================================================================* + * + * GenMBType + * + * generate macroblock type with given attributes + * append result to the specified bitstream + * + * RETURNS: nothing + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +static void +GenMBType(bbPtr, pict_code_type, mb_quant, motion_forw, motion_back, + mb_pattern, mb_intra) + BitBucket *bbPtr; + uint32 pict_code_type; + uint32 mb_quant; + uint32 motion_forw; + uint32 motion_back; + uint32 mb_pattern; + uint32 mb_intra; +{ + int code; + + switch (pict_code_type) { + case 1: + if ((motion_forw != 0) || (motion_back != 0) || (mb_pattern != 0) || (mb_intra != 1)) { + perror("Illegal parameters for macroblock type."); + exit(-1); + } + if (mb_quant) { + Bitio_Write(bbPtr, 0x1, 2); + } else { + Bitio_Write(bbPtr, 0x1, 1); + } + break; + + case 2: + code = 0; + if (mb_quant) { + code += 16; + } + if (motion_forw) { + code += 8; + } + if (motion_back) { + code += 4; + } + if (mb_pattern) { + code += 2; + } + if (mb_intra) { + code += 1; + } + + switch (code) { + case 1: + Bitio_Write(bbPtr, 0x3, 5); + break; + case 2: + Bitio_Write(bbPtr, 0x1, 2); + break; + case 8: + Bitio_Write(bbPtr, 0x1, 3); + break; + case 10: + Bitio_Write(bbPtr, 0x1, 1); + break; + case 17: + Bitio_Write(bbPtr, 0x1, 6); + break; + case 18: + Bitio_Write(bbPtr, 0x1, 5); + break; + case 26: + Bitio_Write(bbPtr, 0x2, 5); + break; + default: + perror("Illegal parameters for macroblock type."); + exit(-1); + break; + } + break; + + case 3: + code = 0; + if (mb_quant) { + code += 16; + } + if (motion_forw) { + code += 8; + } + if (motion_back) { + code += 4; + } + if (mb_pattern) { + code += 2; + } + if (mb_intra) { + code += 1; + } + + switch (code) { + case 12: + Bitio_Write(bbPtr, 0x2, 2); + break; + case 14: + Bitio_Write(bbPtr, 0x3, 2); + break; + case 4: + Bitio_Write(bbPtr, 0x2, 3); + break; + case 6: + Bitio_Write(bbPtr, 0x3, 3); + break; + case 8: + Bitio_Write(bbPtr, 0x2, 4); + break; + case 10: + Bitio_Write(bbPtr, 0x3, 4); + break; + case 1: + Bitio_Write(bbPtr, 0x3, 5); + break; + case 30: + Bitio_Write(bbPtr, 0x2, 5); + break; + case 26: + Bitio_Write(bbPtr, 0x3, 6); + break; + case 22: + Bitio_Write(bbPtr, 0x2, 6); + break; + case 17: + Bitio_Write(bbPtr, 0x1, 6); + break; + default: + perror("Illegal parameters for macroblock type."); + exit(-1); + break; + } + break; + } +} + + +/*===========================================================================* + * + * GenMotionCode + * + * generate motion vector output with given value + * append result to the specified bitstream + * + * RETURNS: nothing + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +static void +GenMotionCode(BitBucket * const bbPtr, + int32 const vector) { + + uint32 code, num; + + if ((vector < -16) || (vector > 16)) { + perror("Motion vector out of range."); + fprintf(stderr, "Motion vector out of range: vector = %d\n", vector); + exit(-1); + } + code = mbMotionVectorTable[vector + 16][0]; + num = mbMotionVectorTable[vector + 16][1]; + + Bitio_Write(bbPtr, code, num); +} + + +/*===========================================================================* + * + * GenBlockPattern + * + * generate macroblock pattern output + * append result to the specified bitstream + * + * RETURNS: nothing + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +static void +GenBlockPattern(bbPtr, mb_pattern) + BitBucket *bbPtr; + uint32 mb_pattern; +{ + uint32 code, num; + + code = mbPatTable[mb_pattern][0]; + num = mbPatTable[mb_pattern][1]; + + Bitio_Write(bbPtr, code, num); +} + + +/*===========================================================================* + * + * GenMBAddrIncr + * + * generate macroblock address increment output + * append result to the specified bitstream + * + * RETURNS: nothing + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +static void +GenMBAddrIncr(bbPtr, addr_incr) + BitBucket *bbPtr; + uint32 addr_incr; +{ + uint32 code; + uint32 num; + + code = mbAddrIncrTable[addr_incr][0]; + num = mbAddrIncrTable[addr_incr][1]; + + Bitio_Write(bbPtr, code, num); +} + + +/*===========================================================================* + * + * GenPictHead + * + * generate picture header with given attributes + * append result to the specified bitstream + * + * RETURNS: nothing + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +static void +GenPictHead(bbPtr, temp_ref, code_type, vbv_delay, full_pel_forw_flag, + forw_f_code, full_pel_back_flag, back_f_code, extra_info, + extra_info_size, ext_data, ext_data_size, user_data, + user_data_size) + BitBucket *bbPtr; + uint32 temp_ref; + uint32 code_type; + uint32 vbv_delay; + int32 full_pel_forw_flag; + uint32 forw_f_code; + int32 full_pel_back_flag; + uint32 back_f_code; + uint8 *extra_info; + uint32 extra_info_size; + uint8 *ext_data; + uint32 ext_data_size; + uint8 *user_data; + uint32 user_data_size; +{ + /* Write picture start code. */ + Bitio_Write(bbPtr, PICT_START_CODE, 32); + + /* Temp reference. */ + Bitio_Write(bbPtr, temp_ref, 10); + + /* Code_type. */ + if (code_type == 0) + code_type = 1; + + Bitio_Write(bbPtr, code_type, 3); + + /* vbv_delay. */ + vbv_delay = 0xffff; /* see page 36 (section 2.4.3.4) */ + Bitio_Write(bbPtr, vbv_delay, 16); + + if ((code_type == 2) || (code_type == 3)) { + + /* Full pel forw flag. */ + + if (full_pel_forw_flag) + Bitio_Write(bbPtr, 0x01, 1); + else + Bitio_Write(bbPtr, 0x00, 1); + + /* Forw f code. */ + + Bitio_Write(bbPtr, forw_f_code, 3); + } + if (code_type == 3) { + + /* Full pel back flag. */ + + if (full_pel_back_flag) + Bitio_Write(bbPtr, 0x01, 1); + else + Bitio_Write(bbPtr, 0x00, 1); + + /* Back f code. */ + + Bitio_Write(bbPtr, back_f_code, 3); + } + /* Extra bit picture info. */ + + if (extra_info != NULL) { + unsigned int i; + for (i = 0; i < extra_info_size; ++i) { + Bitio_Write(bbPtr, 0x01, 1); + Bitio_Write(bbPtr, extra_info[i], 8); + } + } + Bitio_Write(bbPtr, 0x00, 1); + + /* next start code */ + Bitio_BytePad(bbPtr); + + /* Write ext data if present. */ + + if (ext_data != NULL) { + unsigned int i; + + Bitio_Write(bbPtr, EXT_START_CODE, 32); + + for (i = 0; i < ext_data_size; ++i) + Bitio_Write(bbPtr, ext_data[i], 8); + Bitio_BytePad(bbPtr); + } + /* Write user data if present. */ + if (user_data != NULL) { + unsigned int i; + Bitio_Write(bbPtr, USER_START_CODE, 32); + + for (i = 0; i < user_data_size; ++i) + Bitio_Write(bbPtr, user_data[i], 8); + + Bitio_BytePad(bbPtr); + } +} /*=====================* @@ -767,7 +1093,8 @@ if ( addr_incr != 1 ) } /* Generate mb type code. */ - GenMBType(bbPtr, pict_code_type, mb_quant, motion_forw, motion_back, mb_pattern, mb_intra); + GenMBType(bbPtr, pict_code_type, mb_quant, + motion_forw, motion_back, mb_pattern, mb_intra); /* MB quant. */ if (mb_quant) { @@ -831,353 +1158,6 @@ if ( addr_incr != 1 ) } -/*=====================* - * INTERNAL PROCEDURES * - *=====================*/ - -/*===========================================================================* - * - * GenMBType - * - * generate macroblock type with given attributes - * append result to the specified bitstream - * - * RETURNS: nothing - * - * SIDE EFFECTS: none - * - *===========================================================================*/ -static void -GenMBType(bbPtr, pict_code_type, mb_quant, motion_forw, motion_back, - mb_pattern, mb_intra) - BitBucket *bbPtr; - uint32 pict_code_type; - uint32 mb_quant; - uint32 motion_forw; - uint32 motion_back; - uint32 mb_pattern; - uint32 mb_intra; -{ - int code; - - switch (pict_code_type) { - case 1: - if ((motion_forw != 0) || (motion_back != 0) || (mb_pattern != 0) || (mb_intra != 1)) { - perror("Illegal parameters for macroblock type."); - exit(-1); - } - if (mb_quant) { - Bitio_Write(bbPtr, 0x1, 2); - } else { - Bitio_Write(bbPtr, 0x1, 1); - } - break; - - case 2: - code = 0; - if (mb_quant) { - code += 16; - } - if (motion_forw) { - code += 8; - } - if (motion_back) { - code += 4; - } - if (mb_pattern) { - code += 2; - } - if (mb_intra) { - code += 1; - } - - switch (code) { - case 1: - Bitio_Write(bbPtr, 0x3, 5); - break; - case 2: - Bitio_Write(bbPtr, 0x1, 2); - break; - case 8: - Bitio_Write(bbPtr, 0x1, 3); - break; - case 10: - Bitio_Write(bbPtr, 0x1, 1); - break; - case 17: - Bitio_Write(bbPtr, 0x1, 6); - break; - case 18: - Bitio_Write(bbPtr, 0x1, 5); - break; - case 26: - Bitio_Write(bbPtr, 0x2, 5); - break; - default: - perror("Illegal parameters for macroblock type."); - exit(-1); - break; - } - break; - - case 3: - code = 0; - if (mb_quant) { - code += 16; - } - if (motion_forw) { - code += 8; - } - if (motion_back) { - code += 4; - } - if (mb_pattern) { - code += 2; - } - if (mb_intra) { - code += 1; - } - - switch (code) { - case 12: - Bitio_Write(bbPtr, 0x2, 2); - break; - case 14: - Bitio_Write(bbPtr, 0x3, 2); - break; - case 4: - Bitio_Write(bbPtr, 0x2, 3); - break; - case 6: - Bitio_Write(bbPtr, 0x3, 3); - break; - case 8: - Bitio_Write(bbPtr, 0x2, 4); - break; - case 10: - Bitio_Write(bbPtr, 0x3, 4); - break; - case 1: - Bitio_Write(bbPtr, 0x3, 5); - break; - case 30: - Bitio_Write(bbPtr, 0x2, 5); - break; - case 26: - Bitio_Write(bbPtr, 0x3, 6); - break; - case 22: - Bitio_Write(bbPtr, 0x2, 6); - break; - case 17: - Bitio_Write(bbPtr, 0x1, 6); - break; - default: - perror("Illegal parameters for macroblock type."); - exit(-1); - break; - } - break; - } -} - - -/*===========================================================================* - * - * GenMotionCode - * - * generate motion vector output with given value - * append result to the specified bitstream - * - * RETURNS: nothing - * - * SIDE EFFECTS: none - * - *===========================================================================*/ -static void -GenMotionCode(BitBucket * const bbPtr, - int32 const vector) { - - uint32 code, num; - - if ((vector < -16) || (vector > 16)) { - perror("Motion vector out of range."); - fprintf(stderr, "Motion vector out of range: vector = %d\n", vector); - exit(-1); - } - code = mbMotionVectorTable[vector + 16][0]; - num = mbMotionVectorTable[vector + 16][1]; - - Bitio_Write(bbPtr, code, num); -} - - -/*===========================================================================* - * - * GenBlockPattern - * - * generate macroblock pattern output - * append result to the specified bitstream - * - * RETURNS: nothing - * - * SIDE EFFECTS: none - * - *===========================================================================*/ -static void -GenBlockPattern(bbPtr, mb_pattern) - BitBucket *bbPtr; - uint32 mb_pattern; -{ - uint32 code, num; - - code = mbPatTable[mb_pattern][0]; - num = mbPatTable[mb_pattern][1]; - - Bitio_Write(bbPtr, code, num); -} - - -/*===========================================================================* - * - * GenMBAddrIncr - * - * generate macroblock address increment output - * append result to the specified bitstream - * - * RETURNS: nothing - * - * SIDE EFFECTS: none - * - *===========================================================================*/ -static void -GenMBAddrIncr(bbPtr, addr_incr) - BitBucket *bbPtr; - uint32 addr_incr; -{ - uint32 code; - uint32 num; - - code = mbAddrIncrTable[addr_incr][0]; - num = mbAddrIncrTable[addr_incr][1]; - - Bitio_Write(bbPtr, code, num); -} - - -/*===========================================================================* - * - * GenPictHead - * - * generate picture header with given attributes - * append result to the specified bitstream - * - * RETURNS: nothing - * - * SIDE EFFECTS: none - * - *===========================================================================*/ -static void -GenPictHead(bbPtr, temp_ref, code_type, vbv_delay, full_pel_forw_flag, - forw_f_code, full_pel_back_flag, back_f_code, extra_info, - extra_info_size, ext_data, ext_data_size, user_data, - user_data_size) - BitBucket *bbPtr; - uint32 temp_ref; - uint32 code_type; - uint32 vbv_delay; - int32 full_pel_forw_flag; - uint32 forw_f_code; - int32 full_pel_back_flag; - uint32 back_f_code; - uint8 *extra_info; - uint32 extra_info_size; - uint8 *ext_data; - uint32 ext_data_size; - uint8 *user_data; - uint32 user_data_size; -{ - int i; - - /* Write picture start code. */ - Bitio_Write(bbPtr, PICT_START_CODE, 32); - - /* Temp reference. */ - Bitio_Write(bbPtr, temp_ref, 10); - - /* Code_type. */ - if (code_type == 0) { - code_type = 1; - } - Bitio_Write(bbPtr, code_type, 3); - - /* vbv_delay. */ - vbv_delay = 0xffff; /* see page 36 (section 2.4.3.4) */ - Bitio_Write(bbPtr, vbv_delay, 16); - - if ((code_type == 2) || (code_type == 3)) { - - /* Full pel forw flag. */ - - if (full_pel_forw_flag) { - Bitio_Write(bbPtr, 0x01, 1); - } else { - Bitio_Write(bbPtr, 0x00, 1); - } - - /* Forw f code. */ - - Bitio_Write(bbPtr, forw_f_code, 3); - } - if (code_type == 3) { - - /* Full pel back flag. */ - - if (full_pel_back_flag) { - Bitio_Write(bbPtr, 0x01, 1); - } else { - Bitio_Write(bbPtr, 0x00, 1); - } - - /* Back f code. */ - - Bitio_Write(bbPtr, back_f_code, 3); - } - /* Extra bit picture info. */ - - if (extra_info != NULL) { - for (i = 0; i < extra_info_size; i++) { - Bitio_Write(bbPtr, 0x01, 1); - Bitio_Write(bbPtr, extra_info[i], 8); - } - } - Bitio_Write(bbPtr, 0x00, 1); - - /* next start code */ - Bitio_BytePad(bbPtr); - - /* Write ext data if present. */ - - if (ext_data != NULL) { - Bitio_Write(bbPtr, EXT_START_CODE, 32); - - for (i = 0; i < ext_data_size; i++) { - Bitio_Write(bbPtr, ext_data[i], 8); - } - Bitio_BytePad(bbPtr); - } - /* Write user data if present. */ - if (user_data != NULL) { - Bitio_Write(bbPtr, USER_START_CODE, 32); - - for (i = 0; i < user_data_size; i++) { - Bitio_Write(bbPtr, user_data[i], 8); - } - Bitio_BytePad(bbPtr); - } -} - - #ifdef UNUSED_PROCEDURES /* GenMBEnd only used for `D` pictures. Shouldn't really ever be called. */ diff --git a/converter/ppm/ppmtompeg/mpeg.c b/converter/ppm/ppmtompeg/mpeg.c index fafbb97a..de662e88 100644 --- a/converter/ppm/ppmtompeg/mpeg.c +++ b/converter/ppm/ppmtompeg/mpeg.c @@ -276,9 +276,8 @@ SetReferenceFrameType(const char * const type) { void -SetBitRateFileName(fileName) - char *fileName; -{ +SetBitRateFileName(const char * const fileName) { + strcpy(bitRateFileName, fileName); } @@ -318,7 +317,7 @@ finishFrameOutput(MpegFrame * const frameP, static void outputIFrame(MpegFrame * const frameP, - BitBucket * const bb, + BitBucket * const bbP, int const realStart, int const realEnd, MpegFrame * const pastRefFrameP, @@ -326,7 +325,7 @@ outputIFrame(MpegFrame * const frameP, /* only start a new GOP with I */ /* don't start GOP if only doing frames */ - if ((!separateFiles) && (currentGOP >= gopSize)) { + if (!separateFiles && currentGOP >= gopSize) { boolean const closed = (totalFramesSent == frameP->id || pastRefFrameP == NULL); @@ -344,7 +343,7 @@ outputIFrame(MpegFrame * const frameP, } Mhead_GenSequenceHeader( - bb, Fsize_x, Fsize_y, + bbP, Fsize_x, Fsize_y, /* pratio */ aspectRatio, /* pict_rate */ frameRate, /* bit_rate */ bit_rate, /* buf_size */ buf_size, /* c_param_flag */ 1, @@ -359,7 +358,7 @@ outputIFrame(MpegFrame * const frameP, closed ? "YES" : "NO", frameP->id); ++num_gop; - Mhead_GenGOPHeader(bb, /* drop_frame_flag */ 0, + Mhead_GenGOPHeader(bbP, /* drop_frame_flag */ 0, tc_hrs, tc_min, tc_sec, tc_pict, closed, /* broken_link */ 0, /* ext_data */ NULL, /* ext_data_size */ 0, @@ -368,16 +367,16 @@ outputIFrame(MpegFrame * const frameP, if (pastRefFrameP == NULL) SetGOPStartTime(0); else - SetGOPStartTime(pastRefFrameP->id+1); + SetGOPStartTime(pastRefFrameP->id + 1); } - if ((frameP->id >= realStart) && (frameP->id <= realEnd)) - GenIFrame(bb, frameP); + if (frameP->id >= realStart && frameP->id <= realEnd) + GenIFrame(bbP, frameP); - numI--; + --numI; timeMask &= 0x6; - currentGOP++; + ++currentGOP; IncrementTCTime(); } @@ -393,10 +392,10 @@ outputPFrame(MpegFrame * const frameP, if ((frameP->id >= realStart) && (frameP->id <= realEnd)) GenPFrame(bbP, frameP, pastRefFrameP); - numP--; + --numP; timeMask &= 0x5; - currentGOP++; + ++currentGOP; IncrementTCTime(); } @@ -498,6 +497,9 @@ processBFrames(MpegFrame * const pastRefFrameP, But do only those B frames whose frame numbers are within the range 'realStart' through 'realEnd'. + + Output the frames to the output stream 'wholeStreamBbP'. If NULL, + output each frame to its own individual file instead. -----------------------------------------------------------------------------*/ boolean const separateFiles = (wholeStreamBbP == NULL); unsigned int const firstBFrameNum = pastRefFrameP->id + 1; @@ -551,7 +553,7 @@ processBFrames(MpegFrame * const pastRefFrameP, static void processRefFrame(MpegFrame * const frameP, - BitBucket * const bb_arg, + BitBucket * const wholeStreamBbP, int const realStart, int const realEnd, MpegFrame * const pastRefFrameP, @@ -564,26 +566,28 @@ processRefFrame(MpegFrame * const frameP, But only if its frame number is within the range 'realStart' through 'realEnd'. + + Output the frame to the output stream 'wholeStreamBbP'. If NULL, + output it to its own individual file instead. -----------------------------------------------------------------------------*/ if (frameP->id >= realStart && frameP->id <= realEnd) { - boolean separateFiles; - BitBucket * bb; + bool const separateFiles = (wholeStreamBbP == NULL); - separateFiles = (bb_arg == NULL); + BitBucket * bbP; - if ( separateFiles ) - bb = bitioNew(outputFileName, frameP->id, remoteIO); + if (separateFiles) + bbP = bitioNew(outputFileName, frameP->id, remoteIO); else - bb = bb_arg; + bbP = wholeStreamBbP; /* first, output this reference frame */ switch (frameP->type) { case TYPE_IFRAME: - outputIFrame(frameP, bb, realStart, realEnd, pastRefFrameP, + outputIFrame(frameP, bbP, realStart, realEnd, pastRefFrameP, separateFiles); break; case TYPE_PFRAME: - outputPFrame(frameP, bb, realStart, realEnd, pastRefFrameP); + outputPFrame(frameP, bbP, realStart, realEnd, pastRefFrameP); ShowRemainingTime(childProcess); break; default: @@ -593,7 +597,7 @@ processRefFrame(MpegFrame * const frameP, ++(*framesOutputP); - finishFrameOutput(frameP, bb, separateFiles, referenceFrame, + finishFrameOutput(frameP, bbP, separateFiles, referenceFrame, childProcess, remoteIO); } } @@ -702,7 +706,7 @@ readAndSaveFrame(struct inputSource * const inputSourceP, static void doFirstFrameStuff(enum frameContext const context, const char * const userDataFileName, - BitBucket * const bb, + BitBucket * const bbP, int const fsize_x, int const fsize_y, int const aspectRatio, @@ -713,8 +717,12 @@ doFirstFrameStuff(enum frameContext const context, /*---------------------------------------------------------------------------- Do stuff we have to do after reading the first frame in a sequence of frames requested of GenMPEGStream(). + + *bbP is the output stream to which to write any header stuff we have + to write. If 'context' is such that there is no header stuff to write, + then 'bbP' is irrelevant. -----------------------------------------------------------------------------*/ - *inputFrameBitsP = 24*Fsize_x*Fsize_y; + *inputFrameBitsP = 24 * Fsize_x * Fsize_y; SetBlocksPerSlice(); if (context == CONTEXT_WHOLESTREAM) { @@ -722,7 +730,7 @@ doFirstFrameStuff(enum frameContext const context, char * userData; unsigned int userDataSize; - assert(bb != NULL); + assert(bbP != NULL); DBG_PRINT(("Generating sequence header\n")); if (bitstreamMode == FIXED_RATE) { @@ -770,7 +778,7 @@ doFirstFrameStuff(enum frameContext const context, userDataSize = strlen(userData); strfree(userDataString); } - Mhead_GenSequenceHeader(bb, Fsize_x, Fsize_y, + Mhead_GenSequenceHeader(bbP, Fsize_x, Fsize_y, /* pratio */ aspectRatio, /* pict_rate */ frameRate, /* bit_rate */ bit_rate, @@ -990,13 +998,21 @@ doAFrame(unsigned int const frameNumber, unsigned int * const framesReadP, unsigned int * const framesOutputP, bool * const firstFrameDoneP, - BitBucket * const bbP, + BitBucket * const wholeStreamBbP, unsigned int * const inputFrameBitsP, bool * const endOfStreamP) { /*---------------------------------------------------------------------------- *endOfStreamP returned means we were unable to do a frame because the input stream has ended. In that case, none of the other outputs are valid. + + Process an input frame. This can involve writing its description + to the output stream, saving it for later use, and/or writing + descriptions of previously saved frames to the output stream + because we now have enough information to do so. + + Output the frames to the output stream 'wholeStreamBbP'. If NULL, + output each frame to its own individual file instead. -----------------------------------------------------------------------------*/ char const frameType = FType_Type(frameNumber); @@ -1026,21 +1042,21 @@ doAFrame(unsigned int const frameNumber, *endOfStreamP = FALSE; if (!*firstFrameDoneP) { - doFirstFrameStuff(context, userDataFileName, - bbP, Fsize_x, Fsize_y, aspectRatio, + doFirstFrameStuff(context, userDataFileName, wholeStreamBbP, + Fsize_x, Fsize_y, aspectRatio, frameRate, qtable, niqtable, inputFrameBitsP); *firstFrameDoneP = TRUE; } - processRefFrame(frameP, bbP, frameStart, frameEnd, + processRefFrame(frameP, wholeStreamBbP, frameStart, frameEnd, pastRefFrameP, childProcess, outputFileName, framesReadP, framesOutputP); if (pastRefFrameP) { processBFrames(pastRefFrameP, frameP, realStart, realEnd, inputSourceP, remoteIO, childProcess, - &IOtime, bbP, outputFileName, + &IOtime, wholeStreamBbP, outputFileName, framesReadP, framesOutputP, ¤tGOP); } if (pastRefFrameP != NULL) @@ -1086,17 +1102,20 @@ GenMPEGStream(struct inputSource * const inputSourceP, we stop where the stream ends if that is before 'frameEnd'. -----------------------------------------------------------------------------*/ - BitBucket * bbP; + BitBucket * streamBbP; + /* The output stream to which we write all the frames. NULL + means the frames are going to individual frame files. + */ unsigned int frameNumber; bool endOfStream; bool firstFrameDone; int numBits; unsigned int firstFrame, lastFrame; - /* Frame numbers of the first and last frames we look at. This - could be more than the the frames we actually encode because - we may need context (i.e. to encode a B frame, we need the subsequent - I or P frame). - */ + /* Frame numbers of the first and last frames we look at. + This could be more than the the frames we actually encode + because we may need context (i.e. to encode a B frame, we + need the subsequent I or P frame). + */ unsigned int framesRead; /* Number of frames we have read; for statistical purposes */ MpegFrame * pastRefFrameP; @@ -1150,10 +1169,10 @@ GenMPEGStream(struct inputSource * const inputSourceP, if (showBitRatePerFrame) OpenBitRateFile(); /* May modify showBitRatePerFrame */ - if (context == CONTEXT_WHOLESTREAM || context == CONTEXT_GOP) - bbP = Bitio_New(ofP); + if (context == CONTEXT_JUSTFRAMES) + streamBbP = NULL; else - bbP = NULL; + streamBbP = Bitio_New(ofP); initTCTime(firstFrame); @@ -1171,7 +1190,7 @@ GenMPEGStream(struct inputSource * const inputSourceP, frameStart, frameEnd, realStart, realEnd, childProcess, outputFileName, pastRefFrameP, &pastRefFrameP, - &framesRead, &framesOutput, &firstFrameDone, bbP, + &framesRead, &framesOutput, &firstFrameDone, streamBbP, inputFrameBitsP, &endOfStream); } @@ -1180,10 +1199,10 @@ GenMPEGStream(struct inputSource * const inputSourceP, /* SEQUENCE END CODE */ if (context == CONTEXT_WHOLESTREAM) - Mhead_GenSequenceEnder(bbP); + Mhead_GenSequenceEnder(streamBbP); - if (context == CONTEXT_WHOLESTREAM) - numBits = bbP->cumulativeBits; + if (streamBbP) + numBits = streamBbP->cumulativeBits; else { /* What should the correct value be? Most likely 1. "numBits" is used below, so we need to make sure it's properly initialized @@ -1192,9 +1211,8 @@ GenMPEGStream(struct inputSource * const inputSourceP, numBits = 1; } - if (context != CONTEXT_JUSTFRAMES) { - Bitio_Flush(bbP); - bbP = NULL; + if (streamBbP) { + Bitio_Flush(streamBbP); fclose(ofP); } handleBitRate(realEnd, numBits, childProcess, showBitRatePerFrame); diff --git a/converter/ppm/ppmtompeg/pframe.c b/converter/ppm/ppmtompeg/pframe.c index e72fe5d6..de91e32c 100644 --- a/converter/ppm/ppmtompeg/pframe.c +++ b/converter/ppm/ppmtompeg/pframe.c @@ -60,8 +60,6 @@ static int32 totalTime = 0; static int qscaleP; static float totalSNR = 0.0; static float totalPSNR = 0.0; -extern Block **dct, **dctr, **dctb; -extern dct_data_type **dct_data; /*=====================* * INTERNAL PROCEDURES * diff --git a/converter/ppm/ppmtompeg/psearch.c b/converter/ppm/ppmtompeg/psearch.c index aea1a29b..83c62d04 100644 --- a/converter/ppm/ppmtompeg/psearch.c +++ b/converter/ppm/ppmtompeg/psearch.c @@ -16,6 +16,8 @@ #include "prototypes.h" #include "fsize.h" #include "param.h" +#include "subsample.h" +#include "block.h" /*==================* diff --git a/converter/ppm/ppmtompeg/subsample.c b/converter/ppm/ppmtompeg/subsample.c index 5ec71814..69401a1d 100644 --- a/converter/ppm/ppmtompeg/subsample.c +++ b/converter/ppm/ppmtompeg/subsample.c @@ -43,6 +43,7 @@ #include "bitio.h" #include "prototypes.h" +#include "subsample.h" static void diff --git a/converter/ppm/ppmtopict.c b/converter/ppm/ppmtopict.c index e2428fb6..22456857 100644 --- a/converter/ppm/ppmtopict.c +++ b/converter/ppm/ppmtopict.c @@ -282,15 +282,8 @@ int i; (void) putc(i & 0xff, fd); } -#if __STDC__ static void putLong( FILE *fd, long i ) -#else /*__STDC__*/ -static void -putLong(fd, i) -FILE *fd; -long i; -#endif /*__STDC__*/ { (void) putc((int)((i >> 24) & 0xff), fd); (void) putc(((int)(i >> 16) & 0xff), fd); diff --git a/converter/ppm/sldtoppm.c b/converter/ppm/sldtoppm.c index 6b51f291..e0760448 100644 --- a/converter/ppm/sldtoppm.c +++ b/converter/ppm/sldtoppm.c @@ -66,15 +66,9 @@ struct svector { struct spoint t; /* To point */ }; -static int extend ARGS((smallint ch)); -static int sli ARGS((void)); -static int slib ARGS((void)); -static void vscale ARGS((int *px, int *py)); -static void slider ARGS((void (*slvec) ARGS((struct svector *vec, int color)), - void (*slflood) ARGS((struct spolygon *poly, int color)) )); -static void slidefind ARGS((char *sname, int dironly, int ucasen)); -static void draw ARGS((struct svector *vec, int color)); -static void flood ARGS((struct spolygon *const poly, int const color)); +typedef void (slvecfn)(struct svector * vec, int color); +typedef void (slfloodfn)(struct spolygon * poly, int color); + static int ixdots, iydots; /* Screen size in dots */ static FILE *slfile; /* Slide file descriptor */ @@ -104,21 +98,26 @@ static int adjust = FALSE; /* Adjust to correct aspect ratio ? */ static struct slhead slfrof; /* Slide file header */ static long xfac, yfac; /* Aspect ratio scale factors */ -static int sdrawkcab; /* Slide drawing kinematic conversion of - ass-backwards data flag */ +static int sdrawkcab; + /* Slide drawing kinematic conversion of ass-backwards data flag */ + + /* EXTEND -- Turn a smallint into an int with sign extension, whether - or not that happens automatically. */ + or not that happens automatically. +*/ -static int extend(smallint ch) -{ +static int +extend(smallint ch) { return ((int) ((ch & 0x80) ? (ch | ~0xFF) : ch)); } + + /* SLI -- Input word from slide file */ -static int sli() -{ +static int +sli(void) { short wd; if (fread(&wd, sizeof wd, 1, slfile) != 1) { @@ -131,10 +130,12 @@ static int sli() return wd; } + + /* SLIB -- Input byte from slide file */ -static int slib() -{ +static int +slib(void) { smallint ch = 0; if (fread(&ch, sizeof ch, 1, slfile) != 1) { @@ -143,25 +144,171 @@ static int slib() return extend(ch); } + + /* VSCALE -- scale screen coordinates for mismatched display. */ -static void vscale(px, py) - int *px, *py; -{ +static void +vscale(int * const px, + int * const py) { + *px = (((unsigned) *px) * xfac) >> 16; *py = (((unsigned) *py) * yfac) >> 16; } + + +/* SLIDEFIND -- Find a slide in a library or, if DIRONLY is + nonzero, print a directory listing of the library. + If UCASEN is nonzero, the requested slide name is + converted to upper case. */ + +static void +slidefind(const char * const sname, + bool const dironly, + bool const ucasen) { + + char uname[32]; + unsigned char libent[36]; + long pos; + + if (dironly) + pm_message("Slides in library:"); + else { + int i; + const char * ip; + + ip = sname; /* initial value */ + + for (i = 0; i < 31; i++) { + char ch = *ip++; + if (ch == EOS) + break; + if (ucasen && ISLOWER(ch)) + ch = TOUPPER(ch); + uname[i] = ch; + } + uname[i] = EOS; + } + + /* Read slide library header and verify. */ + + if ((fread(libent, 32, 1, slfile) != 1) || + (!STREQ((char *)libent, "AutoCAD Slide Library 1.0\015\012\32"))) { + pm_error("not an AutoCAD slide library file."); + } + pos = 32; + + /* Search for a slide with the requested name. */ + + while (TRUE) { + if ((fread(libent, 36, 1, slfile) != 1) || + (strlen((char *)libent) == 0)) { + if (dironly) { + return; + } + pm_error("slide %s not in library.", sname); + } + pos += 36; + if (dironly) { + pm_message(" %s", libent); + } else if (STREQ((char *)libent, uname)) { + long dpos = (((((libent[35] << 8) | libent[34]) << 8) | + libent[33]) << 8) | libent[32]; + if ((slfile == stdin) || (fseek(slfile, dpos, 0) == -1)) { + dpos -= pos; + + while (dpos-- > 0) + getc(slfile); + } + break; + } + } +} + + + +/* DRAW -- Draw a vector in the given AutoCAD color. */ + +static slvecfn draw; + +static void +draw(struct svector * vec, + int color) { + + pixel rgbcolor; + + if (blither) { + pm_message("Vector (%d, %d) - (%d, %d) Color %d", + vec->f.x, vec->f.y, vec->t.x, vec->t.y, color); + } + assert(vec->f.x >= 0 && vec->f.x < pixcols); + assert(vec->f.y >= 0 && vec->f.y < pixrows); + assert(vec->t.x >= 0 && vec->t.x < pixcols); + assert(vec->t.y >= 0 && vec->t.y < pixrows); + PPM_ASSIGN(rgbcolor, + acadcol[color][0], acadcol[color][1], acadcol[color][2]); + ppmd_line(pixels, pixcols, pixrows, pixmaxval, + vec->f.x, iydots - vec->f.y, vec->t.x, iydots - vec->t.y, + PPMD_NULLDRAWPROC, + (char *) &rgbcolor); +} + + + +/* FLOOD -- Draw a filled polygon. */ + +static slfloodfn flood; + +static void +flood(struct spolygon * const poly, + int const color) { + + unsigned int i; + struct fillobj * handle; + pixel rgbcolor; + + handle = ppmd_fill_create(); + + if (blither) { + unsigned int i; + pm_message("Polygon: %d points, fill type %d, color %d", + poly->npoints, poly->fill, color); + for (i = 0; i < poly->npoints; i++) { + pm_message(" Point %d: (%d, %d)", i + 1, + poly->pt[i].x, poly->pt[i].y); + } + } + + PPM_ASSIGN(rgbcolor, + acadcol[color][0], acadcol[color][1], acadcol[color][2]); + for (i = 0; i < poly->npoints; i++) { + assert(poly->pt[i].x >= 0 && poly->pt[i].x < pixcols); + assert(poly->pt[i].y >= 0 && poly->pt[i].y < pixrows); + ppmd_line(pixels, pixcols, pixrows, pixmaxval, + poly->pt[i].x, iydots - poly->pt[i].y, + poly->pt[(i + 1) % poly->npoints].x, + iydots - poly->pt[(i + 1) % poly->npoints].y, + ppmd_fill_drawproc, handle); + } + ppmd_fill(pixels, pixcols, pixrows, pixmaxval, + handle, PPMD_NULLDRAWPROC, (char *) &rgbcolor); + + ppmd_fill_destroy(handle); +} + + + /* SLIDER -- Read slide file. This is called with the name of the file to be read and function pointers to the routines which process vectors and polygon fill requests respectively. */ -static void slider(slvec, slflood) - void (*slvec) ARGS((struct svector *vec, int color)); - void (*slflood) ARGS((struct spolygon *poly, int color)); -{ +static void +slider(slvecfn slvec, + slfloodfn slflood) { + int i, rescale; unsigned char ubfr[4]; /* Utility character buffer */ int lx, ly; /* Last x and y point */ @@ -174,14 +321,14 @@ static void slider(slvec, slflood) short rtest; /* Value to test byte reversal */ short btest = 0x1234; /* Value to test byte-reversal */ static struct slhead slhi = /* Master slide header sample */ - {"AutoCAD Slide\r\n\32", 86,2, 0,0, 0.0, 0}; + {"AutoCAD Slide\r\n\32", 86,2, 0,0, 0.0, 0}; int curcolor = 7; /* Current vector color */ pixel rgbcolor; /* Pixel used to clear pixmap */ - + lx = ly = 32000; - + /* Process the header of the slide file. */ - + sdrawkcab = FALSE; /* Initially guess byte order is OK */ fread(slfrof.slh, 17, 1, slfile); fread(&slfrof.sntype, sizeof(char), 1, slfile); @@ -194,48 +341,46 @@ static void slider(slvec, slflood) /* Verify that slide format is compatible with this program. */ - if (STREQ(slfrof.slh, slhi.slh)) { + if (STREQ(slfrof.slh, slhi.slh)) pm_error("this is not an AutoCAD slide file."); - } /* Verify that the number format and file level in the header are compatible. All slides written by versions of AutoCAD released since September of 1987 are compatible with this format. */ - if ((slfrof.sntype != slhi.sntype) || (slfrof.slevel != slhi.slevel)) { + if ((slfrof.sntype != slhi.sntype) || (slfrof.slevel != slhi.slevel)) pm_error("incompatible slide file format"); - } /* Build SDSAR value from long scaled version. */ - + ldsar = 0L; - for (i = 3; i >= 0; i--) { - ldsar = (ldsar << 8) | ubfr[i]; - } + for (i = 3; i >= 0; --i) + ldsar = (ldsar << 8) | ubfr[i]; slfrof.sdsar = ((double) ldsar) / 1E7; - + /* Examine the byte order test value. If it's backwards, set the byte-reversal flag and correct all of the values we've read in - so far. */ + so far. + */ if (btest != rtest) { - sdrawkcab = TRUE; + sdrawkcab = TRUE; #define rshort(x) x = ((x >> 8) & 0xFF) | (x << 8) - rshort(slfrof.sxdots); - rshort(slfrof.sydots); - rshort(slfrof.shwfill); + rshort(slfrof.sxdots); + rshort(slfrof.sydots); + rshort(slfrof.shwfill); #undef rshort } - + /* Dump the header if we're blithering. */ if (blither || info) { pm_message("Slide file type %d, level %d, hwfill type %d.", - slfrof.sntype, slfrof.slevel, slfrof.shwfill); + slfrof.sntype, slfrof.slevel, slfrof.shwfill); pm_message("Original screen size %dx%d, aspect ratio %.3f.", - slfrof.sxdots + 1, slfrof.sydots + 1, slfrof.sdsar); + slfrof.sxdots + 1, slfrof.sydots + 1, slfrof.sdsar); pm_message("Byte order is %s.", - sdrawkcab ? "being reversed" : "the same"); + sdrawkcab ? "being reversed" : "the same"); } /* If the display aspect ratio indicates that the pixels on the @@ -246,405 +391,284 @@ static void slider(slvec, slflood) We always correct the aspect ratio by adjusting the width of the image. This guarantees that output from the SHADE command, which is essentially scan-line data written in vector form, - will not be corrupted. */ + will not be corrupted. + */ dsar = ((double) slfrof.sxdots) / slfrof.sydots; if (fabs(slfrof.sdsar - dsar) > 0.0001) { - if (adjust) { - ixdots = slfrof.sxdots * (slfrof.sdsar / dsar) + 0.5; - iydots = slfrof.sydots; - dsar = ((double) ixdots) / iydots; + if (adjust) { + ixdots = slfrof.sxdots * (slfrof.sdsar / dsar) + 0.5; + iydots = slfrof.sydots; + dsar = ((double) ixdots) / iydots; + } else { + pm_message("Warning - pixels on source screen were non-square. " + "Specifying -adjust will correct image width " + "to compensate."); + ixdots = slfrof.sxdots; + iydots = slfrof.sydots; + dsar = slfrof.sdsar; + } } else { - pm_message("Warning - pixels on source screen were non-square."); - pm_message(" Specifying -adjust will correct image width to compensate."); + /* Source pixels were square. */ ixdots = slfrof.sxdots; iydots = slfrof.sydots; dsar = slfrof.sdsar; - } - } else { - /* Source pixels were square. */ - ixdots = slfrof.sxdots; - iydots = slfrof.sydots; - dsar = slfrof.sdsar; - adjust = FALSE; /* Mark no adjustment needed */ + adjust = FALSE; /* Mark no adjustment needed */ } /* If there's a uniform scale factor specified, apply it. */ if (uscale > 0) { - ixdots = (ixdots * uscale) + 0.5; - iydots = (iydots * uscale) + 0.5; + ixdots = (ixdots * uscale) + 0.5; + iydots = (iydots * uscale) + 0.5; } /* If the image is to be stretched to a given width, set the output image sizes accordingly. If only a height or width is given, scale the other direction proportionally to preserve the - aspect ratio. */ + aspect ratio. + */ if (sxsize > 0) { - if (sysize > 0) { - iydots = sysize - 1; - } else { - iydots = ((((long) iydots) * (sxsize - 1)) + - (iydots / 2)) / ixdots; - } - ixdots = sxsize - 1; - } else if (sysize > 0) { - if (sxsize > 0) { + if (sysize > 0) { + iydots = sysize - 1; + } else { + iydots = ((((long) iydots) * (sxsize - 1)) + + (iydots / 2)) / ixdots; + } ixdots = sxsize - 1; - } else { - ixdots = ((((long) ixdots) * (sysize - 1)) + - (ixdots / 2)) / iydots; - } - iydots = sysize - 1; + } else if (sysize > 0) { + if (sxsize > 0) { + ixdots = sxsize - 1; + } else { + ixdots = ((((long) ixdots) * (sysize - 1)) + + (ixdots / 2)) / iydots; + } + iydots = sysize - 1; } - + if (adjust) { - pm_message( + pm_message( "Resized from %dx%d to %dx%d to correct pixel aspect ratio.", - slfrof.sxdots + 1, slfrof.sydots + 1, ixdots + 1, iydots + 1); + slfrof.sxdots + 1, slfrof.sydots + 1, ixdots + 1, iydots + 1); } - + /* Allocate image buffer and clear it to black. */ - + pixels = ppm_allocarray(pixcols = ixdots + 1, pixrows = iydots + 1); PPM_ASSIGN(rgbcolor, 0, 0, 0); ppmd_filledrectangle(pixels, pixcols, pixrows, pixmaxval, 0, 0, pixcols, pixrows, PPMD_NULLDRAWPROC, (char *) &rgbcolor); - + if ((rescale = slfrof.sxdots != ixdots || - slfrof.sydots != iydots || - slfrof.sdsar != dsar) != 0) { - + slfrof.sydots != iydots || + slfrof.sdsar != dsar) != 0) { + /* Rescale all coords. so they'll look (more or less) - right on this display. */ - - xfac = (ixdots + 1) * 0x10000L; - xfac /= (long) (slfrof.sxdots + 1); - yfac = (iydots + 1) * 0x10000L; - yfac /= (long) (slfrof.sydots + 1); - if (dsar < slfrof.sdsar) { - yfac = yfac * dsar / slfrof.sdsar; - } else { - xfac = xfac * slfrof.sdsar / dsar; - } + right on this display. + */ + + xfac = (ixdots + 1) * 0x10000L; + xfac /= (long) (slfrof.sxdots + 1); + yfac = (iydots + 1) * 0x10000L; + yfac /= (long) (slfrof.sydots + 1); + if (dsar < slfrof.sdsar) { + yfac = yfac * dsar / slfrof.sdsar; + } else { + xfac = xfac * slfrof.sdsar / dsar; + } } poly.npoints = 0; /* No flood in progress. */ - + while ((cw = sli()) != 0xFC00) { - switch (cw & 0xFF00) { + switch (cw & 0xFF00) { case 0xFB00: /* Short vector compressed */ - vec.f.x = lx + extend(cw & 0xFF); - vec.f.y = ly + slib(); - vec.t.x = lx + slib(); - vec.t.y = ly + slib(); - lx = vec.f.x; - ly = vec.f.y; - if (rescale) { - vscale(&vec.f.x, &vec.f.y); - vscale(&vec.t.x, &vec.t.y); - } - (*slvec)(&vec, curcolor);/* Draw vector on screen */ - slx = vec.f.x; /* Save scaled point */ - sly = vec.f.y; - break; - + vec.f.x = lx + extend(cw & 0xFF); + vec.f.y = ly + slib(); + vec.t.x = lx + slib(); + vec.t.y = ly + slib(); + lx = vec.f.x; + ly = vec.f.y; + if (rescale) { + vscale(&vec.f.x, &vec.f.y); + vscale(&vec.t.x, &vec.t.y); + } + (*slvec)(&vec, curcolor);/* Draw vector on screen */ + slx = vec.f.x; /* Save scaled point */ + sly = vec.f.y; + break; + case 0xFC00: /* End of file */ - break; - + break; + case 0xFD00: /* Flood command */ - vec.f.x = sli(); - vec.f.y = sli(); - if ((int) vec.f.y < 0) { /* start or end */ - if (poly.npoints != 0) { /* end? */ - if (poly.npoints > 2 && poly.npoints < 11) { - (*slflood)(&poly, curcolor); - } else { - pm_error("Bad polygon vertex count (%d)", - poly.npoints); - } - poly.npoints = 0; - } else { - poly.fill = -vec.f.y; /* Start */ + vec.f.x = sli(); + vec.f.y = sli(); + if ((int) vec.f.y < 0) { /* start or end */ + if (poly.npoints != 0) { /* end? */ + if (poly.npoints > 2 && poly.npoints < 11) { + (*slflood)(&poly, curcolor); + } else { + pm_error("Bad polygon vertex count (%d)", + poly.npoints); + } + poly.npoints = 0; + } else { + poly.fill = -vec.f.y; /* Start */ + } + } else { /* Polygon vertex */ + if (poly.npoints < 10) { + if (rescale) { + vscale(&vec.f.x, &vec.f.y); + } + poly.pt[poly.npoints].x = vec.f.x; + poly.pt[poly.npoints].y = vec.f.y; + } + poly.npoints++; } - } else { /* Polygon vertex */ - if (poly.npoints < 10) { + break; + + case 0xFE00: /* Common endpoint compressed */ + vec.f.x = lx + extend(cw & 0xFF); + vec.f.y = ly + slib(); + lx = vec.f.x; + ly = vec.f.y; + vec.t.x = slx; + vec.t.y = sly; if (rescale) { vscale(&vec.f.x, &vec.f.y); } - poly.pt[poly.npoints].x = vec.f.x; - poly.pt[poly.npoints].y = vec.f.y; - } - poly.npoints++; - } - break; - - case 0xFE00: /* Common endpoint compressed */ - vec.f.x = lx + extend(cw & 0xFF); - vec.f.y = ly + slib(); - lx = vec.f.x; - ly = vec.f.y; - vec.t.x = slx; - vec.t.y = sly; - if (rescale) { - vscale(&vec.f.x, &vec.f.y); - } - (*slvec)(&vec, curcolor);/* Draw vector */ - slx = vec.f.x; /* Save scaled point */ - sly = vec.f.y; - break; - + (*slvec)(&vec, curcolor);/* Draw vector */ + slx = vec.f.x; /* Save scaled point */ + sly = vec.f.y; + break; + case 0xFF00: /* Change color */ - curcolor = cw & 0xFF; - break; + curcolor = cw & 0xFF; + break; default: /* Co-ordinates */ - lx = vec.f.x = cw; - ly = vec.f.y = sli(); - vec.t.x = sli(); - vec.t.y = sli(); - if (rescale) { - vscale(&vec.f.x, &vec.f.y); - vscale(&vec.t.x, &vec.t.y); - } - (*slvec)(&vec, curcolor); - slx = vec.f.x; /* Save scaled point */ - sly = vec.f.y; - break; - } - } -} - -/* SLIDEFIND -- Find a slide in a library or, if DIRONLY is - nonzero, print a directory listing of the library. - If UCASEN is nonzero, the requested slide name is - converted to upper case. */ - -static void slidefind(sname, dironly, ucasen) - char *sname; - int dironly, ucasen; -{ - char uname[32]; - unsigned char libent[36]; - long pos; - - if (dironly) { - pm_message("Slides in library:"); - } else { - int i; - char *ip = sname; - - for (i = 0; i < 31; i++) { - char ch = *ip++; - if (ch == EOS) { - break; - } - if (ucasen && ISLOWER(ch)) { - ch = TOUPPER(ch); - } - uname[i] = ch; - } - uname[i] = EOS; - } - - /* Read slide library header and verify. */ - - if ((fread(libent, 32, 1, slfile) != 1) || - (!STREQ((char *)libent, "AutoCAD Slide Library 1.0\015\012\32"))) { - pm_error("not an AutoCAD slide library file."); - } - pos = 32; - - /* Search for a slide with the requested name. */ - - while (TRUE) { - if ((fread(libent, 36, 1, slfile) != 1) || - (strlen((char *)libent) == 0)) { - if (dironly) { - return; - } - pm_error("slide %s not in library.", sname); - } - pos += 36; - if (dironly) { - pm_message(" %s", libent); - } else if (STREQ((char *)libent, uname)) { - long dpos = (((((libent[35] << 8) | libent[34]) << 8) | - libent[33]) << 8) | libent[32]; - if ((slfile == stdin) || (fseek(slfile, dpos, 0) == -1)) { - dpos -= pos; - - while (dpos-- > 0) { - (void) getc(slfile); - } + lx = vec.f.x = cw; + ly = vec.f.y = sli(); + vec.t.x = sli(); + vec.t.y = sli(); + if (rescale) { + vscale(&vec.f.x, &vec.f.y); + vscale(&vec.t.x, &vec.t.y); + } + (*slvec)(&vec, curcolor); + slx = vec.f.x; /* Save scaled point */ + sly = vec.f.y; + break; } - break; - } } } -/* DRAW -- Draw a vector in the given AutoCAD color. */ -static void draw(vec, color) - struct svector *vec; - int color; -{ - pixel rgbcolor; - - if (blither) { - pm_message("Vector (%d, %d) - (%d, %d) Color %d", - vec->f.x, vec->f.y, vec->t.x, vec->t.y, color); - } - assert(vec->f.x >= 0 && vec->f.x < pixcols); - assert(vec->f.y >= 0 && vec->f.y < pixrows); - assert(vec->t.x >= 0 && vec->t.x < pixcols); - assert(vec->t.y >= 0 && vec->t.y < pixrows); - PPM_ASSIGN(rgbcolor, - acadcol[color][0], acadcol[color][1], acadcol[color][2]); - ppmd_line(pixels, pixcols, pixrows, pixmaxval, - vec->f.x, iydots - vec->f.y, vec->t.x, iydots - vec->t.y, - PPMD_NULLDRAWPROC, - (char *) &rgbcolor); -} - -/* FLOOD -- Draw a filled polygon. */ - -static void -flood(struct spolygon * const poly, - int const color) { - - int i; - struct fillobj * handle; - pixel rgbcolor; - - handle = ppmd_fill_create(); - - if (blither) { - pm_message("Polygon: %d points, fill type %d, color %d", - poly->npoints, poly->fill, color); - for (i = 0; i < poly->npoints; i++) { - pm_message(" Point %d: (%d, %d)", i + 1, - poly->pt[i].x, poly->pt[i].y); - } - } - - PPM_ASSIGN(rgbcolor, - acadcol[color][0], acadcol[color][1], acadcol[color][2]); - for (i = 0; i < poly->npoints; i++) { - assert(poly->pt[i].x >= 0 && poly->pt[i].x < pixcols); - assert(poly->pt[i].y >= 0 && poly->pt[i].y < pixrows); - ppmd_line(pixels, pixcols, pixrows, pixmaxval, - poly->pt[i].x, iydots - poly->pt[i].y, - poly->pt[(i + 1) % poly->npoints].x, - iydots - poly->pt[(i + 1) % poly->npoints].y, - ppmd_fill_drawproc, handle); - } - ppmd_fill(pixels, pixcols, pixrows, pixmaxval, - handle, PPMD_NULLDRAWPROC, (char *) &rgbcolor); - - ppmd_fill_destroy(handle); -} /* Main program. */ -int main(argc, argv) - int argc; - char *argv[]; -{ +int +main(int argc, + char * argv[]) { + int argn; const char * const usage = "[-verbose] [-info] [-adjust] [-scale ]\n\ - [-dir] [-lib|-Lib ]\n\ - [-xsize|-width ] [-ysize|-height ] [sldfile]"; +[-dir] [-lib|-Lib ]\n\ +[-xsize|-width ] [-ysize|-height ] [sldfile]"; int scalespec = FALSE, widspec = FALSE, hgtspec = FALSE, dironly = FALSE, - ucasen; - char *slobber = (char *) 0; /* Slide library item */ + ucasen; + const char * slobber; /* Slide library item */ + slobber = NULL; + ppm_init(&argc, argv); argn = 1; while (argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0') { if (pm_keymatch(argv[argn], "-verbose", 2)) { - blither = TRUE; + blither = TRUE; } else if (pm_keymatch(argv[argn], "-adjust", 2)) { - adjust = TRUE; + adjust = TRUE; } else if (pm_keymatch(argv[argn], "-dir", 2)) { - dironly = TRUE; + dironly = TRUE; } else if (pm_keymatch(argv[argn], "-info", 2)) { - info = TRUE; + info = TRUE; } else if (pm_keymatch(argv[argn], "-lib", 2)) { - if (slobber != (char *) 0) { + if (slobber) pm_error("already specified a library item"); - } ucasen = argv[argn][1] != 'L'; - argn++; - if (argn == argc) { - pm_usage(usage); - } - slobber = argv[argn]; + argn++; + if (argn == argc) { + pm_usage(usage); + } + slobber = argv[argn]; } else if (pm_keymatch(argv[argn], "-scale", 2)) { - if (scalespec) { + if (scalespec) { pm_error("already specified a scale factor"); - } - argn++; + } + argn++; if ((argn == argc) || (sscanf(argv[argn], "%lf", &uscale) != 1)) - pm_usage(usage); - if (uscale <= 0.0) { + pm_usage(usage); + if (uscale <= 0.0) { pm_error("scale factor must be greater than 0"); - } - scalespec = TRUE; + } + scalespec = TRUE; } else if (pm_keymatch(argv[argn], "-xsize", 2) || pm_keymatch(argv[argn], "-width", 2)) { - if (widspec) { + if (widspec) { pm_error("already specified a width/xsize"); - } - argn++; + } + argn++; if ((argn == argc) || (sscanf(argv[argn], "%d", &sxsize) != 1)) - pm_usage(usage); - widspec = TRUE; + pm_usage(usage); + widspec = TRUE; } else if (pm_keymatch(argv[argn], "-ysize", 2) || pm_keymatch(argv[argn], "-height", 2)) { - if (hgtspec) { + if (hgtspec) { pm_error("already specified a height/ysize"); + } + argn++; + if ((argn == argc) || (sscanf(argv[argn], "%d", &sysize) != 1)) + pm_usage(usage); + hgtspec = TRUE; + } else { + pm_usage(usage); } argn++; - if ((argn == argc) || (sscanf(argv[argn], "%d", &sysize) != 1)) - pm_usage(usage); - hgtspec = TRUE; - } else { - pm_usage(usage); - } - argn++; } /* If a file name is specified, open it. Otherwise read from - standard input. */ + standard input. + */ if (argn < argc) { - slfile = pm_openr(argv[argn]); - argn++; + slfile = pm_openr(argv[argn]); + argn++; } else { - slfile = stdin; + slfile = stdin; } - + if (argn != argc) { /* Extra bogus arguments ? */ - pm_usage(usage); + pm_usage(usage); } - + /* If we're extracting an item from a slide library, position the - input stream to the start of the chosen slide. */ - - if (dironly || (slobber != (char *) 0)) { - slidefind(slobber, dironly, ucasen); - } - + input stream to the start of the chosen slide. + */ + + if (dironly || slobber) + slidefind(slobber, dironly, ucasen); + if (!dironly) { - slider(draw, flood); - ppm_writeppm(stdout, pixels, pixcols, pixrows, pixmaxval, FALSE); + slider(draw, flood); + ppm_writeppm(stdout, pixels, pixcols, pixrows, pixmaxval, FALSE); } pm_close(slfile); pm_close(stdout); - exit(0); + + return 0; } diff --git a/converter/ppm/spctoppm.c b/converter/ppm/spctoppm.c index 3eea7821..d2a48187 100644 --- a/converter/ppm/spctoppm.c +++ b/converter/ppm/spctoppm.c @@ -165,15 +165,8 @@ DoBitmap( ifp ) sscreen[i] = ( screen[i<<1] << 8 ) + ( 0xff & screen[(i<<1)+1] ); } -#if __STDC__ static void DoChar( int n, char c ) -#else /*__STDC__*/ -static void -DoChar( n, c ) - int n; - char c; -#endif /*__STDC__*/ { int i; -- cgit 1.4.1