/* pbmreduce.c - read a portable bitmap and reduce it N times ** ** 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. */ #include #include "pbm.h" #include "mallocvar.h" int main( argc, argv ) int argc; char* argv[]; { FILE* ifp; register bit** bitslice; register bit* newbitrow; register bit* nbP; int argn, n, rows, cols, format, newrows, newcols; int row, col, limitcol, subrow, subcol, count, direction; const char* const usage = "[-floyd|-fs | -threshold] [-value ] N [pbmfile]"; int halftone; #define QT_FS 1 #define QT_THRESH 2 #define SCALE 1024 #define HALFSCALE 512 long threshval, sum; long* thiserr; /* used for Floyd-Steinberg stuff */ long* nexterr; /* used for Floyd-Steinberg stuff */ long* temperr; pbm_init( &argc, argv ); argn = 1; halftone = QT_FS; threshval = HALFSCALE; while ( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' ) { if ( pm_keymatch( argv[argn], "-fs", 2 ) || pm_keymatch( argv[argn], "-floyd", 2 ) ) halftone = QT_FS; else if ( pm_keymatch( argv[argn], "-threshold", 2 ) ) halftone = QT_THRESH; else if ( pm_keymatch( argv[argn], "-value", 2 ) ) { float f; ++argn; if ( argn == argc || sscanf( argv[argn], "%f", &f ) != 1 || f < 0.0 || f > 1.0 ) pm_usage( usage ); threshval = f * SCALE; } else pm_usage( usage ); ++argn; } if ( argn == argc ) pm_usage( usage ); if ( sscanf( argv[argn], "%d", &n ) != 1 ) pm_usage( usage ); if ( n < 2 ) pm_error( "N must be greater than 1" ); if (n > INT_MAX / n) pm_error("Scale argument too large. You specified %d", n); ++argn; if ( argn == argc ) ifp = stdin; else { ifp = pm_openr( argv[argn] ); ++argn; } if ( argn != argc ) pm_usage( usage ); pbm_readpbminit( ifp, &cols, &rows, &format ); bitslice = pbm_allocarray( cols, n ); newrows = rows / n; newcols = cols / n; pbm_writepbminit( stdout, newcols, newrows, 0 ); newbitrow = pbm_allocrow( newcols ); if (halftone == QT_FS) { unsigned int col; /* Initialize Floyd-Steinberg. */ MALLOCARRAY(thiserr, newcols + 2); MALLOCARRAY(nexterr, newcols + 2); if (thiserr == NULL || nexterr == NULL) pm_error("out of memory"); srand(pm_randseed()); for (col = 0; col < newcols + 2; ++col) thiserr[col] = (rand() % SCALE - HALFSCALE) / 4; /* (random errors in [-SCALE/8 .. SCALE/8]) */ } else { /* These variables are meaningless in this case, and the values should never be used. */ thiserr = NULL; nexterr = NULL; } direction = 1; for ( row = 0; row < newrows; ++row ) { for ( subrow = 0; subrow < n; ++subrow ) pbm_readpbmrow( ifp, bitslice[subrow], cols, format ); if ( halftone == QT_FS ) for ( col = 0; col < newcols + 2; ++col ) nexterr[col] = 0; if ( direction ) { col = 0; limitcol = newcols; nbP = newbitrow; } else { col = newcols - 1; limitcol = -1; nbP = &(newbitrow[col]); } do { sum = 0; count = 0; for ( subrow = 0; subrow < n; ++subrow ) for ( subcol = 0; subcol < n; ++subcol ) if ( row * n + subrow < rows && col * n + subcol < cols ) { count += 1; if ( bitslice[subrow][col * n + subcol] == PBM_WHITE ) sum += 1; } sum = ( sum * SCALE ) / count; if ( halftone == QT_FS ) sum += thiserr[col + 1]; if ( sum >= threshval ) { *nbP = PBM_WHITE; if ( halftone == QT_FS ) sum = sum - threshval - HALFSCALE; } else *nbP = PBM_BLACK; if ( halftone == QT_FS ) { if ( direction ) { thiserr[col + 2] += ( sum * 7 ) / 16; nexterr[col ] += ( sum * 3 ) / 16; nexterr[col + 1] += ( sum * 5 ) / 16; nexterr[col + 2] += ( sum ) / 16; } else { thiserr[col ] += ( sum * 7 ) / 16; nexterr[col + 2] += ( sum * 3 ) / 16; nexterr[col + 1] += ( sum * 5 ) / 16; nexterr[col ] += ( sum ) / 16; } } if ( direction ) { ++col; ++nbP; } else { --col; --nbP; } } while ( col != limitcol ); pbm_writepbmrow( stdout, newbitrow, newcols, 0 ); if ( halftone == QT_FS ) { temperr = thiserr; thiserr = nexterr; nexterr = temperr; direction = ! direction; } } pm_close( ifp ); pm_close( stdout ); exit( 0 ); }