about summary refs log tree commit diff
path: root/converter/pbm/atktopbm.c
diff options
context:
space:
mode:
Diffstat (limited to 'converter/pbm/atktopbm.c')
-rw-r--r--converter/pbm/atktopbm.c362
1 files changed, 362 insertions, 0 deletions
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 <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+
+#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 );
+}
+