/* pbmmask.c - create a mask bitmap from a portable bitmap
**
** Copyright (C) 1989, 1991 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 "pbm.h"
#include "mallocvar.h"
static bit ** bits;
static bit ** mask;
static bit backcolor;
static int rows, cols;
static short * fcols;
static short * frows;
static int fstacksize = 0;
static int fstackp = 0;
static void
addflood(int const col,
int const row) {
if ( bits[row][col] == backcolor && mask[row][col] == PBM_BLACK ) {
if ( fstackp >= fstacksize ) {
if ( fstacksize == 0 ) {
fstacksize = 1000;
MALLOCARRAY(fcols, fstacksize);
MALLOCARRAY(frows, fstacksize);
if ( fcols == NULL || frows == NULL )
pm_error( "out of memory" );
} else {
fstacksize *= 2;
fcols = (short*) realloc(
(char*) fcols, fstacksize * sizeof(short) );
frows = (short*) realloc(
(char*) frows, fstacksize * sizeof(short) );
if ( fcols == (short*) 0 || frows == (short*) 0 )
pm_error( "out of memory" );
}
}
fcols[fstackp] = col;
frows[fstackp] = row;
++fstackp;
}
}
static void
flood(void) {
while ( fstackp > 0 ) {
int col, row;
--fstackp;
col = fcols[fstackp];
row = frows[fstackp];
if ( bits[row][col] == backcolor && mask[row][col] == PBM_BLACK ) {
int c;
mask[row][col] = PBM_WHITE;
if ( row - 1 >= 0 )
addflood( col, row - 1 );
if ( row + 1 < rows )
addflood( col, row + 1 );
for ( c = col + 1; c < cols; ++c ) {
if ( bits[row][c] == backcolor && mask[row][c] == PBM_BLACK ) {
mask[row][c] = PBM_WHITE;
if ( row - 1 >= 0 &&
( bits[row - 1][c - 1] != backcolor ||
mask[row - 1][c - 1] != PBM_BLACK ) )
addflood( c, row - 1 );
if ( row + 1 < rows &&
( bits[row + 1][c - 1] != backcolor ||
mask[row + 1][c - 1] != PBM_BLACK ) )
addflood( c, row + 1 );
}
else
break;
}
for ( c = col - 1; c >= 0; --c ) {
if ( bits[row][c] == backcolor && mask[row][c] == PBM_BLACK ) {
mask[row][c] = PBM_WHITE;
if ( row - 1 >= 0 &&
( bits[row - 1][c + 1] != backcolor ||
mask[row - 1][c + 1] != PBM_BLACK ) )
addflood( c, row - 1 );
if ( row + 1 < rows &&
( bits[row + 1][c + 1] != backcolor ||
mask[row + 1][c + 1] != PBM_BLACK ) )
addflood( c, row + 1 );
} else
break;
}
}
}
}
int
main(int argc, char * argv[]) {
FILE* ifp;
int argn, expand, wcount;
register int row, col;
const char* const usage = "[-expand] [pbmfile]";
pbm_init( &argc, argv );
argn = 1;
expand = 0;
if ( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' )
{
if ( pm_keymatch( argv[argn], "-expand", 2 ) )
expand = 1;
else if ( pm_keymatch( argv[argn], "-noexpand", 2 ) )
expand = 0;
else
pm_usage( usage );
++argn;
}
if ( argn == argc )
ifp = stdin;
else
{
ifp = pm_openr( argv[argn] );
++argn;
}
if ( argn != argc )
pm_usage( usage );
bits = pbm_readpbm( ifp, &cols, &rows );
pm_close( ifp );
mask = pbm_allocarray( cols, rows );
/* Clear out the mask. */
for ( row = 0; row < rows; ++row )
for ( col = 0; col < cols; ++col )
mask[row][col] = PBM_BLACK;
/* Figure out the background color, by counting along the edge. */
wcount = 0;
for ( row = 0; row < rows; ++row ) {
if ( bits[row][0] == PBM_WHITE )
++wcount;
if ( bits[row][cols - 1] == PBM_WHITE )
++wcount;
}
for ( col = 1; col < cols - 1; ++col ) {
if ( bits[0][col] == PBM_WHITE )
++wcount;
if ( bits[rows - 1][col] == PBM_WHITE )
++wcount;
}
if ( wcount >= rows + cols - 2 )
backcolor = PBM_WHITE;
else
backcolor = PBM_BLACK;
/* Flood the entire edge. Probably the first call will be enough, but
might as well be sure.
*/
for ( col = cols - 3; col >= 2; col -= 2 ) {
addflood( col, rows - 1 );
addflood( col, 0 );
}
for ( row = rows - 1; row >= 0; row -= 2 ) {
addflood( cols - 1, row );
addflood( 0, row );
}
flood( );
if ( ! expand )
/* Done. */
pbm_writepbm( stdout, mask, cols, rows, 0 );
else {
/* Expand by one pixel. */
int srow, scol;
unsigned int row;
bit ** emask;
emask = pbm_allocarray( cols, rows );
for ( row = 0; row < rows; ++row ) {
unsigned int col;
for ( col = 0; col < cols; ++col )
if ( mask[row][col] == PBM_BLACK )
emask[row][col] = PBM_BLACK;
else {
emask[row][col] = PBM_WHITE;
for ( srow = row - 1; srow <= row + 1; ++srow )
for ( scol = col - 1; scol <= col + 1; ++scol )
if ( srow >= 0 && srow < rows &&
scol >= 0 && scol < cols &&
mask[srow][scol] == PBM_BLACK ) {
emask[row][col] = PBM_BLACK;
break;
}
}
}
pbm_writepbm( stdout, emask, cols, rows, 0 );
}
pm_close( stdout );
return 0;
}