about summary refs log tree commit diff
path: root/converter/pbm/pbmtoascii.c
diff options
context:
space:
mode:
authorgiraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8>2019-06-28 23:07:55 +0000
committergiraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8>2019-06-28 23:07:55 +0000
commit11fd0bc3fdbe7b5eb9266a728a81d0bcac91fe32 (patch)
tree7c40f096dd973943ef563ec87b2a68d8205db4fb /converter/pbm/pbmtoascii.c
parent89c6ec14eb7514630aea5abc4b90b51d1473d33a (diff)
downloadnetpbm-mirror-11fd0bc3fdbe7b5eb9266a728a81d0bcac91fe32.tar.gz
netpbm-mirror-11fd0bc3fdbe7b5eb9266a728a81d0bcac91fe32.tar.xz
netpbm-mirror-11fd0bc3fdbe7b5eb9266a728a81d0bcac91fe32.zip
Promote Stable to Super_stable
git-svn-id: http://svn.code.sf.net/p/netpbm/code/super_stable@3640 9d0c8265-081b-0410-96cb-a4ca84ce46f8
Diffstat (limited to 'converter/pbm/pbmtoascii.c')
-rw-r--r--converter/pbm/pbmtoascii.c324
1 files changed, 218 insertions, 106 deletions
diff --git a/converter/pbm/pbmtoascii.c b/converter/pbm/pbmtoascii.c
index 0472f809..976b188f 100644
--- a/converter/pbm/pbmtoascii.c
+++ b/converter/pbm/pbmtoascii.c
@@ -1,4 +1,4 @@
-/* pbmtoascii.c - read a portable bitmap and produce ASCII graphics
+/* pbmtoascii.c - read a PBM image and produce ASCII graphics
 **
 ** Copyright (C) 1988, 1992 by Jef Poskanzer.
 **
@@ -10,8 +10,33 @@
 ** implied warranty.
 */
 
+#include <assert.h>
+#include "mallocvar.h"
 #include "pbm.h"
 
+/*
+  The algorithm is based on describing the 2 or 8 pixels in a cell with a
+  single integer called a signature, which we use as an index into an array to
+  get the character whose glyph best matches those pixels.  The encoding is as
+  follows.  Make a string of bits, with each bit being one pixel of the cell,
+  1 = black, 0 = white.  The order of the string is left to right across the
+  top row, then the next row down, etc.  Considering that string to be a
+  binary cipher, the integer it represents is the signature.
+
+  Example: the 2x4 cell consists of these pixels: (* = black)
+
+      * *
+      *
+
+        *
+  The bit string to represent this is 11100001.  So the signature for this
+  cell is the integer 0xE1 (225).
+
+  You index the array carr2x4 with 0xE1, and get '?' as the character to
+  represent that cell.  (We don't really try very hard to match the shape;
+  it's mostly important to match the density).
+*/
+
 #define SQQ '\''
 #define BSQ '\\'
 
@@ -19,7 +44,7 @@
 **     1
 **     2
 */
-static char carr1x2[4] = {
+static char const carr1x2[4] = {
 /*   0    1    2    3   */
     ' ', '"', 'o', 'M' };
 
@@ -35,47 +60,177 @@ static char carr1x2[4] = {
 #define D06 '&'
 #define D05 '$'
 #define D04 '?'
-static char carr2x4[256] = {
+static char const carr2x4[256] = {
 /*0  1    2   3    4   5    6   7    8   9    A   B    C   D    E   F  */
-' ',SQQ, '`','"', '-',SQQ, SQQ,SQQ, '-','`', '`','`', '-','^', '^','"',/*00-0F*/
-'.',':', ':',':', '|','|', '/',D04, '/','>', '/','>', '~','+', '/','*',/*10-1F*/
-'.',':', ':',':', BSQ,BSQ, '<','<', '|',BSQ, '|',D04, '~',BSQ, '+','*',/*20-2F*/
-'-',':', ':',':', '~',D04, '<','<', '~','>', D04,'>', '=','b', 'd','#',/*30-3F*/
-'.',':', ':',':', ':','!', '/',D04, ':',':', '/',D04, ':',D04, D04,'P',/*40-4F*/
-',','i', '/',D04, '|','|', '|','T', '/',D04, '/','7', 'r','}', '/','P',/*50-5F*/
-',',':', ';',D04, '>',D04, 'S','S', '/',')', '|','7', '>',D05, D05,D06,/*60-6F*/
-'v',D04, D04,D05, '+','}', D05,'F', '/',D05, '/',D06, 'p','D', D06,D07,/*70-7F*/
-'.',':', ':',':', ':',BSQ, ':',D04, ':',BSQ, '!',D04, ':',D04, D04,D05,/*80-8F*/
-BSQ,BSQ, ':',D04, BSQ,'|', '(',D05, '<','%', D04,'Z', '<',D05, D05,D06,/*90-9F*/
-',',BSQ, 'i',D04, BSQ,BSQ, D04,BSQ, '|','|', '|','T', D04,BSQ, '4','9',/*A0-AF*/
-'v',D04, D04,D05, BSQ,BSQ, D05,D06, '+',D05, '{',D06, 'q',D06, D06,D07,/*B0-BF*/
-'_',':', ':',D04, ':',D04, D04,D05, ':',D04, D04,D05, ':',D05, D05,D06,/*C0-CF*/
-BSQ,D04, D04,D05, D04,'L', D05,'[', '<','Z', '/','Z', 'c','k', D06,'R',/*D0-DF*/
-',',D04, D04,D05, '>',BSQ, 'S','S', D04,D05, 'J',']', '>',D06, '1','9',/*E0-EF*/
-'o','b', 'd',D06, 'b','b', D06,'6', 'd',D06, 'd',D07, '#',D07, D07,D08 /*F0-FF*/
-    };
+' ',SQQ, '`','"', '-',SQQ, SQQ,SQQ, '-','`', '`','`', '-','^','^','"',/*00-0F*/
+'.',':', ':',':', '|','|', '/',D04, '/','>', '/','>', '~','+','/','*',/*10-1F*/
+'.',':', ':',':', BSQ,BSQ, '<','<', '|',BSQ, '|',D04, '~',BSQ,'+','*',/*20-2F*/
+'-',':', ':',':', '~',D04, '<','<', '~','>', D04,'>', '=','b','d','#',/*30-3F*/
+'.',':', ':',':', ':','!', '/',D04, ':',':', '/',D04, ':',D04,D04,'P',/*40-4F*/
+',','i', '/',D04, '|','|', '|','T', '/',D04, '/','7', 'r','}','/','P',/*50-5F*/
+',',':', ';',D04, '>',D04, 'S','S', '/',')', '|','7', '>',D05,D05,D06,/*60-6F*/
+'v',D04, D04,D05, '+','}', D05,'F', '/',D05, '/',D06, 'p','D',D06,D07,/*70-7F*/
+'.',':', ':',':', ':',BSQ, ':',D04, ':',BSQ, '!',D04, ':',D04,D04,D05,/*80-8F*/
+BSQ,BSQ, ':',D04, BSQ,'|', '(',D05, '<','%', D04,'Z', '<',D05,D05,D06,/*90-9F*/
+',',BSQ, 'i',D04, BSQ,BSQ, D04,BSQ, '|','|', '|','T', D04,BSQ,'4','9',/*A0-AF*/
+'v',D04, D04,D05, BSQ,BSQ, D05,D06, '+',D05, '{',D06, 'q',D06,D06,D07,/*B0-BF*/
+'_',':', ':',D04, ':',D04, D04,D05, ':',D04, D04,D05, ':',D05,D05,D06,/*C0-CF*/
+BSQ,D04, D04,D05, D04,'L', D05,'[', '<','Z', '/','Z', 'c','k',D06,'R',/*D0-DF*/
+',',D04, D04,D05, '>',BSQ, 'S','S', D04,D05, 'J',']', '>',D06,'1','9',/*E0-EF*/
+'o','b', 'd',D06, 'b','b', D06,'6', 'd',D06, 'd',D07, '#',D07,D07,D08 /*F0-FF*/
+};
 
 
 
-int
-main( argc, argv )
-int argc;
-char* argv[];
+static void
+makeRowOfSigs(FILE *         const ifP,
+              unsigned int   const cols,
+              unsigned int   const rows,
+              int            const format,
+              unsigned int   const cellWidth,
+              unsigned int   const cellHeight,
+              unsigned int   const row,
+              unsigned int * const sig,
+              unsigned int   const ccols) {
+/*----------------------------------------------------------------------------
+   Compute the signatures for every cell in a row.
+
+   Read the pixels from *ifP, which is positioned to the first pixel row
+   of the cell row, which is row number 'row'.  The image dimensions are
+   'cols' x 'rows' pixels.
+
+   Each cell is 'cellWidth' x 'cellHeight'.
+
+   Return the signatures as sig[], which is 'ccols' wide because that's how
+   many cells you get from 'cols' pixels divided into cells 'cellWidth' pixels
+   wide.
+-----------------------------------------------------------------------------*/
+    unsigned int b;
+    unsigned int subrow;  /* row within cell */
+    bit * bitrow;        /* malloc'ed array */
+
+    bitrow = pbm_allocrow(cols);
     {
-    FILE* ifp;
-    int argn, gridx, gridy, rows, cols, format;
-    int ccols, lastcol, row, subrow, subcol;
-    register int col, b;
-    bit* bitrow;
-    register bit* bP;
-    int* sig;
-    register int* sP;
-    char* line;
-    register char* lP;
-    char* carr;
+        unsigned int col;
+        for (col = 0; col < ccols; ++col)
+            sig[col] = 0;
+    }
+
+    b = 0x1;  /* initial value */
+    for (subrow = 0; subrow < cellHeight; ++subrow) {
+        if (row + subrow < rows) {
+            unsigned int subcol;  /* col within cell */
+            pbm_readpbmrow(ifP, bitrow, cols, format);
+            for (subcol = 0; subcol < cellWidth; ++subcol) {
+                unsigned int col, ccol;
+                for (col = subcol, ccol = 0;
+                     col < cols;
+                     col += cellWidth, ++ccol) {
+                    if (bitrow[col] == PBM_BLACK)
+                        sig[ccol] |= b;
+                }
+                b <<= 1;
+            }
+        }
+    }
+    pbm_freerow(bitrow);
+}
+
+
+
+static void
+findRightMargin(const unsigned int * const sig,
+                unsigned int         const ccols,
+                const char *         const carr,
+                unsigned int *       const endColP) {
+/*----------------------------------------------------------------------------
+   Find the first cell of the right margin, i.e. a contiguous set of
+   all-white cells at the right end of the row.
+-----------------------------------------------------------------------------*/
+    unsigned int endcol;
+
+    for (endcol = ccols; endcol > 0; --endcol) {
+        if (carr[sig[endcol-1]] != ' ')
+            break;
+    }
+    *endColP = endcol;
+}
+
+
+
+static void
+assembleCellRow(const unsigned int * const sig,
+                unsigned int         const ccols,
+                const char *         const carr,
+                char *               const line) {
+/*----------------------------------------------------------------------------
+   Return as line[] the line of ASCII codes for the characters of one
+   row of cells, ready for printing.
+-----------------------------------------------------------------------------*/
+    unsigned int col;
+
+    for (col = 0; col < ccols; ++col)
+        line[col] = carr[sig[col]];
+
+    line[ccols] = '\0';
+}
+
+
+
+static void
+pbmtoascii(FILE *       const ifP,
+           unsigned int const cellWidth,
+           unsigned int const cellHeight,
+           const char * const carr) {
+
+    int cols, rows, format;
+    unsigned int ccols;
+    char * line;         /* malloc'ed array */
+    unsigned int row;
+    unsigned int * sig;  /* malloc'ed array */
+        /* This describes in a single integer the pixels of a cell,
+           as described above.
+        */
+    assert(cellWidth * cellHeight <= sizeof(sig[0])*8);
+
+    pbm_readpbminit(ifP, &cols, &rows, &format);
+
+    ccols = (cols + cellWidth - 1) / cellWidth;
+
+    MALLOCARRAY(sig, ccols);
+    if (sig == NULL)
+        pm_error("No memory for %u columns", ccols);
+    MALLOCARRAY_NOFAIL(line, ccols+1);
+    if (line == NULL)
+        pm_error("No memory for %u columns", ccols);
+
+    for (row = 0; row < rows; row += cellHeight) {
+        unsigned int endCol;
+
+        makeRowOfSigs(ifP, cols, rows, format, cellWidth, cellHeight,
+                      row, sig, ccols);
+
+        findRightMargin(sig, ccols, carr, &endCol);
+
+        assembleCellRow(sig, endCol, carr, line);
+
+        puts(line);
+    }
+    free(sig);
+    free(line);
+}
+
+
+
+int
+main(int argc, const char ** argv) {
+
+    FILE * ifP;
+    int argn, gridx, gridy;
+    const char * carr;
     const char* usage = "[-1x2|-2x4] [pbmfile]";
 
-    pbm_init( &argc, argv );
+    pm_proginit(&argc, argv);
 
     /* Set up default parameters. */
     argn = 1;
@@ -85,81 +240,38 @@ char* argv[];
 
     /* Check for flags. */
     while ( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' )
-	{
-	if ( pm_keymatch( argv[argn], "-1x2", 2 ) )
-	    {
-	    gridx = 1;
-	    gridy = 2;
-	    carr = carr1x2;
-	    }
-	else if ( pm_keymatch( argv[argn], "-2x4", 2 ) )
-	    {
-	    gridx = 2;
-	    gridy = 4;
-	    carr = carr2x4;
-	    }
-	else
-	    pm_usage( usage );
-	++argn;
-	}
+    {
+        if ( pm_keymatch( argv[argn], "-1x2", 2 ) )
+        {
+            gridx = 1;
+            gridy = 2;
+            carr = carr1x2;
+        }
+        else if ( pm_keymatch( argv[argn], "-2x4", 2 ) )
+        {
+            gridx = 2;
+            gridy = 4;
+            carr = carr2x4;
+        }
+        else
+            pm_usage( usage );
+        ++argn;
+    }
 
     if ( argn < argc )
-	{
-	ifp = pm_openr( argv[argn] );
-	++argn;
-	}
+    {
+        ifP = pm_openr( argv[argn] );
+        ++argn;
+    }
     else
-	ifp = stdin;
+        ifP = stdin;
     
     if ( argn != argc )
         pm_usage( usage );
 
-    pbm_readpbminit( ifp, &cols, &rows, &format );
-    ccols = ( cols + gridx - 1 ) / gridx;
-    bitrow = pbm_allocrow( cols );
-    sig = (int*) pm_allocrow( ccols, sizeof(int) );
-    line = (char*) pm_allocrow( ccols + 1, sizeof(char) );
-
-    for ( row = 0; row < rows; row += gridy )
-	{
-	/* Get a character-row's worth of sigs. */
-	for ( col = 0; col < ccols; ++col )
-	    sig[col] = 0;
-	b = 1;
-	for ( subrow = 0; subrow < gridy; ++subrow )
-	    {
-	    if ( row + subrow < rows )
-		{
-		pbm_readpbmrow( ifp, bitrow, cols, format );
-		for ( subcol = 0; subcol < gridx; ++subcol )
-		    {
-		    for ( col = subcol, bP = &(bitrow[subcol]), sP = sig;
-			  col < cols;
-			  col += gridx, bP += gridx, ++sP )
-			if ( *bP == PBM_BLACK )
-			    *sP |= b;
-		    b <<= 1;
-		    }
-		}
-	    }
-	/* Ok, now remove trailing blanks.  */
-	for ( lastcol = ccols - 1; lastcol >= 0; --lastcol )
-	    if ( carr[sig[lastcol]] != ' ' )
-		break;
-	/* Copy chars to an array and print. */
-	for ( col = 0, sP = sig, lP = line; col <= lastcol; ++col, ++sP, ++lP )
-	    *lP = carr[*sP];
-	*lP++ = '\0';
-	puts( line );
-	}
-
-    pm_close( ifp );
-    pbm_freerow( bitrow );
-    pm_freerow( (char*) sig );
-    pm_freerow( (char*) line );
-
-    /* If the program failed, it previously aborted with nonzero completion
-       code, via various function calls.
-    */
+    pbmtoascii(ifP, gridx, gridy, carr);
+
+    pm_close(ifP);
+
     return 0;
-    }
+}