From 1fd361a1ea06e44286c213ca1f814f49306fdc43 Mon Sep 17 00:00:00 2001 From: giraffedata Date: Sat, 19 Aug 2006 03:12:28 +0000 Subject: Create Subversion repository git-svn-id: http://svn.code.sf.net/p/netpbm/code/trunk@1 9d0c8265-081b-0410-96cb-a4ca84ce46f8 --- converter/pbm/atktopbm.c | 362 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 362 insertions(+) create mode 100644 converter/pbm/atktopbm.c (limited to 'converter/pbm/atktopbm.c') diff --git a/converter/pbm/atktopbm.c b/converter/pbm/atktopbm.c new file mode 100644 index 00000000..c4a81808 --- /dev/null +++ b/converter/pbm/atktopbm.c @@ -0,0 +1,362 @@ +/* atktopbm.c - convert Andrew Toolkit raster object to portable bitmap +** +** Copyright (C) 1991 by Bill Janssen +** +** 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 +#include + +#include "nstring.h" +#include "pbm.h" +#include "mallocvar.h" + + +/* readatkraster +** +** Routine for reading rasters in .raster form. (BE2 rasters version 2.) +*/ + +/* codes for data stream */ +#define WHITEZERO 'f' +#define WHITETWENTY 'z' +#define BLACKZERO 'F' +#define BLACKTWENTY 'Z' +#define OTHERZERO 0x1F + +#define WHITEBYTE 0x00 +#define BLACKBYTE 0xFF + +/* error codes (copied from $ANDREW/atk/basics/common/dataobj.ch) */ +/* return values from Read */ +#define dataobject_NOREADERROR 0 +#define dataobject_PREMATUREEOF 1 +#define dataobject_NOTBE2DATASTREAM 2 /* backward compatibility */ +#define dataobject_NOTATKDATASTREAM 2 /* preferred version */ +#define dataobject_MISSINGENDDATAMARKER 3 +#define dataobject_OBJECTCREATIONFAILED 4 +#define dataobject_BADFORMAT 5 + +/* ReadRow(file, row, length) +** Reads from 'file' the encoding of bytes to fill in 'row'. Row will be +** truncated or padded (with WHITE) to exactly 'length' bytes. +** +** Returns the code that terminated the row. This may be +** '|' correct end of line +** '\0' if the length was satisfied (before a terminator) +** EOF if the file ended +** '\' '{' other recognized ends. +** The '|' is the expected end and pads the row with WHITE. +** The '\' and '{' are error conditions and may indicate the +** beginning of some other portion of the data stream. +** If the terminator is '\' or '{', it is left at the front of the input. +** '|' is gobbled up. +*/ + +/* macros to generate case entries for switch statement */ +#define case1(v) case v +#define case4(v) case v: case (v)+1: case (v)+2: case(v)+3 +#define case6(v) case4(v): case ((v)+4): case ((v)+5) +#define case8(v) case4(v): case4((v)+4) + +static long +ReadRow(FILE * const file, unsigned char * const row, long const length) { +/*---------------------------------------------------------------------------- + 'file' is where to get them from. + 'row' is where to put bytes. + 'length' is how many bytes in row must be filled. +-----------------------------------------------------------------------------*/ + /* Each input character is processed by the central loop. There are + ** some input codes which require two or three characters for + ** completion; these are handled by advancing the state machine. + ** Errors are not processed; instead the state machine is reset + ** to the Ready state whenever a character unacceptable to the + ** current state is read. + */ + enum stateCode { + Ready, /* any input code is allowed */ + HexDigitPending, /* have seen the first of a hex digit pair */ + RepeatPending, /* repeat code has been seen: + must be followed by two hex digits */ + RepeatAndDigit}; /* have seen repeat code and its first + following digit */ + enum stateCode InputState; /* current state */ + register int c; /* the current input character */ + register long repeatcount = 0; /* current repeat value */ + register long hexval; /* current hex value */ + long pendinghex = 0; /* the first of a pair of hex characters */ + int lengthRemaining; + unsigned char * cursor; + + /* We cannot exit when length becomes zero because we need to check + ** to see if a row ending character follows. Thus length is checked + ** only when we get a data generating byte. If length then is + ** zero, we ungetc the byte. + */ + + lengthRemaining = length; + cursor = row; + + InputState = Ready; + while ((c=getc(file)) != EOF) switch (c) { + + case8(0x0): + case8(0x8): + case8(0x10): + case8(0x18): + case1(' '): + /* control characters and space are legal and ignored */ + break; + case1(0x40): /* '@' */ + case1(0x5B): /* '[' */ + case4(0x5D): /* ']' '^' '_' '`' */ + case4(0x7D): /* '}' '~' DEL 0x80 */ + default: /* all above 0x80 */ + /* error code: Ignored at present. Reset InputState. */ + InputState = Ready; + break; + + case1(0x7B): /* '{' */ + case1(0x5C): /* '\\' */ + /* illegal end of line: exit anyway */ + ungetc(c, file); /* retain terminator in stream */ + /* DROP THROUGH */ + case1(0x7C): /* '|' */ + /* legal end of row: may have to pad */ + while (lengthRemaining-- > 0) + *cursor++ = WHITEBYTE; + return c; + + case1(0x21): + case6(0x22): + case8(0x28): + /* punctuation characters: repeat byte given by two + ** succeeding hex chars + */ + if (lengthRemaining <= 0) { + ungetc(c, file); + return('\0'); + } + repeatcount = c - OTHERZERO; + InputState = RepeatPending; + break; + + case8(0x30): + case8(0x38): + /* digit (or following punctuation) - hex digit */ + hexval = c - 0x30; + goto hexdigit; + case6(0x41): + /* A ... F - hex digit */ + hexval = c - (0x41 - 0xA); + goto hexdigit; + case6(0x61): + /* a ... f - hex digit */ + hexval = c - (0x61 - 0xA); + goto hexdigit; + + case8(0x67): + case8(0x6F): + case4(0x77): + /* g ... z - multiple WHITE bytes */ + if (lengthRemaining <= 0) { + ungetc(c, file); + return('\0'); + } + repeatcount = c - WHITEZERO; + hexval = WHITEBYTE; + goto store; + case8(0x47): + case8(0x4F): + case4(0x57): + /* G ... Z - multiple BLACK bytes */ + if (lengthRemaining <= 0) { + ungetc(c, file); + return('\0'); + } + repeatcount = c - BLACKZERO; + hexval = BLACKBYTE; + goto store; + +hexdigit: + /* process a hex digit. Use InputState to determine + what to do with it. */ + if (lengthRemaining <= 0) { + ungetc(c, file); + return('\0'); + } + switch(InputState) { + case Ready: + InputState = HexDigitPending; + pendinghex = hexval << 4; + break; + case HexDigitPending: + hexval |= pendinghex; + repeatcount = 1; + goto store; + case RepeatPending: + InputState = RepeatAndDigit; + pendinghex = hexval << 4; + break; + case RepeatAndDigit: + hexval |= pendinghex; + goto store; + } + break; + +store: + /* generate byte(s) into the output row + Use repeatcount, depending on state. */ + if (lengthRemaining < repeatcount) + /* reduce repeat count if it would exceed + available space */ + repeatcount = lengthRemaining; + lengthRemaining -= repeatcount; /* do this before repeatcount-- */ + while (repeatcount-- > 0) + *cursor++ = hexval; + InputState = Ready; + break; + + } /* end of while( - )switch( - ) */ + return EOF; +} + + + +#undef case1 +#undef case4 +#undef case6 +#undef case8 + + + +static void +ReadATKRaster(FILE * const file, + int * const rwidth, + int * const rheight, + unsigned char ** const destaddrP) { + + int row, rowlen; /* count rows; byte length of row */ + int version; + char keyword[6]; + int discardid; + int objectid; /* id read for the incoming pixel image */ + long tc; /* temp */ + int width, height; /* dimensions of image */ + + if (fscanf(file, "\\begindata{raster,%d", &discardid) != 1 + || getc(file) != '}' || getc(file) != '\n') + pm_error ("input file not Andrew raster object"); + + fscanf(file, " %d ", &version); + if (version < 2) + pm_error ("version too old to parse"); + + { + unsigned int options; + long xscale, yscale; + long xoffset, yoffset, subwidth, subheight; + /* ignore all these features: */ + fscanf(file, " %u %ld %ld %ld %ld %ld %ld", + &options, &xscale, &yscale, &xoffset, + &yoffset, &subwidth, &subheight); + } + /* scan to end of line in case this is actually something beyond V2 */ + while (((tc=getc(file)) != '\n') && (tc != '\\') && (tc != EOF)) {} + + /* read the keyword */ + fscanf(file, " %5s", keyword); + if (!STREQ(keyword, "bits")) + pm_error ("keyword is not 'bits'!"); + + fscanf(file, " %d %d %d ", &objectid, &width, &height); + + if (width < 1 || height < 1 || width > 1000000 || height > 1000000) + pm_error ("bad width or height"); + + *rwidth = width; + *rheight = height; + rowlen = (width + 7) / 8; + MALLOCARRAY(*destaddrP, height * rowlen); + if (destaddrP == NULL) + pm_error("Unable to allocate %u bytes for the input image.", + height * rowlen); + for (row = 0; row < height; row++) + { + long c; + + c = ReadRow(file, *destaddrP + (row * rowlen), rowlen); + if (c != '|') + { + if (c == EOF) + pm_error ("premature EOF"); + else + pm_error ("bad format"); + break; + } + } + while (! feof(file) && getc(file) != '\\') {}; /* scan for \enddata */ + if (fscanf(file, "enddata{raster,%d", &discardid) != 1 + || getc(file) != '}' || getc(file) != '\n') + pm_error ("missing end-of-object marker"); +} + + + +int +main(int argc, char **argv) { + + FILE *ifp; + register bit *bitrow, *bP; + int rows, cols, row, col, charcount; + unsigned char *data, mask; + + + pbm_init ( &argc, argv ); + + if ( argc > 2 ) + pm_usage( "[raster obj]" ); + + if ( argc == 2 ) + ifp = pm_openr( argv[1] ); + else + ifp = stdin; + + ReadATKRaster( 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 = 0x80; + for ( col = 0, bP = bitrow; col < cols; ++col, ++bP ) + { + if ( charcount >= 8 ) + { + ++data; + charcount = 0; + mask = 0x80; + } + *bP = ( *data & mask ) ? PBM_BLACK : PBM_WHITE; + ++charcount; + mask >>= 1; + } + ++data; + pbm_writepbmrow( stdout, bitrow, cols, 0 ); + } + + pm_close( stdout ); + exit( 0 ); +} + -- cgit 1.4.1