home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: 10 Tools
/
10-Tools.zip
/
netpbma.zip
/
pgm
/
pgmtopbm.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-02-18
|
11KB
|
478 lines
/* pgmtopbm.c - read a portable graymap and write a portable bitmap
**
** 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 "pgm.h"
#include "dithers.h"
static void init_hilbert ARGS((int w, int h));
static int hilbert ARGS((int *px, int *py));
int
main( argc, argv )
int argc;
char* argv[];
{
FILE* ifp;
gray* grayrow;
register gray* gP;
bit* bitrow;
register bit* bP;
int argn, rows, cols, format, row, col, limitcol;
float fthreshval;
int clump_size;
gray maxval;
char* usage = "[-floyd|-fs | -hilbert | -threshold | -dither8|-d8 |\n -cluster3|-c3|-cluster4|-c4|-cluster8|-c8] [-value <val>] [-clump <size>] [pgmfile]";
int halftone;
#define QT_FS 1
#define QT_THRESH 2
#define QT_DITHER8 3
#define QT_CLUSTER3 4
#define QT_CLUSTER4 5
#define QT_CLUSTER8 6
#define QT_HILBERT 7
long threshval, sum;
long* thiserr;
long* nexterr;
long* temperr;
#define FS_SCALE 1024
#define HALF_FS_SCALE 512
int fs_direction;
pgm_init( &argc, argv );
argn = 1;
halftone = QT_FS; /* default quantization is Floyd-Steinberg */
fthreshval = 0.5;
clump_size = 5; /* default hilbert curve clump size */
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], "-hilbert", 2 ))
halftone = QT_HILBERT;
else if ( pm_keymatch( argv[argn], "-threshold", 2 ) )
halftone = QT_THRESH;
else if ( pm_keymatch( argv[argn], "-dither8", 2 ) ||
pm_keymatch( argv[argn], "-d8", 3 ) )
halftone = QT_DITHER8;
else if ( pm_keymatch( argv[argn], "-cluster3", 9 ) ||
pm_keymatch( argv[argn], "-c3", 3 ) )
halftone = QT_CLUSTER3;
else if ( pm_keymatch( argv[argn], "-cluster4", 9 ) ||
pm_keymatch( argv[argn], "-c4", 3 ) )
halftone = QT_CLUSTER4;
else if ( pm_keymatch( argv[argn], "-cluster8", 9 ) ||
pm_keymatch( argv[argn], "-c8", 3 ) )
halftone = QT_CLUSTER8;
else if ( pm_keymatch( argv[argn], "-value", 2 ) )
{
++argn;
if ( argn == argc || sscanf( argv[argn], "%f", &fthreshval ) != 1 ||
fthreshval < 0.0 || fthreshval > 1.0 )
pm_usage( usage );
}
else if ( pm_keymatch( argv[argn], "-clump", 2 ) )
{
++argn;
if ( argn == argc || sscanf( argv[argn], "%d", &clump_size ) != 1 ||
clump_size < 2)
pm_usage( usage );
}
else
pm_usage( usage );
++argn;
}
if ( argn != argc )
{
ifp = pm_openr( argv[argn] );
++argn;
}
else
ifp = stdin;
if ( argn != argc )
pm_usage( usage );
if ( halftone != QT_HILBERT )
{
pgm_readpgminit( ifp, &cols, &rows, &maxval, &format );
grayrow = pgm_allocrow( cols );
pbm_writepbminit( stdout, cols, rows, 0 );
bitrow = pbm_allocrow( cols );
/* Initialize. */
switch ( halftone )
{
case QT_FS:
/* Initialize Floyd-Steinberg error vectors. */
thiserr = (long*) pm_allocrow( cols + 2, sizeof(long) );
nexterr = (long*) pm_allocrow( cols + 2, sizeof(long) );
srandom( (int) ( time( 0 ) ^ getpid( ) ) );
for ( col = 0; col < cols + 2; ++col )
thiserr[col] = ( random( ) % FS_SCALE - HALF_FS_SCALE ) / 4;
/* (random errors in [-FS_SCALE/8 .. FS_SCALE/8]) */
fs_direction = 1;
threshval = fthreshval * FS_SCALE;
break;
case QT_HILBERT:
break;
case QT_THRESH:
threshval = fthreshval * maxval + 0.999999;
break;
case QT_DITHER8:
/* Scale dither matrix. */
for ( row = 0; row < 16; ++row )
for ( col = 0; col < 16; ++col )
dither8[row][col] = dither8[row][col] * ( maxval + 1 ) / 256;
break;
case QT_CLUSTER3:
/* Scale order-3 clustered dither matrix. */
for ( row = 0; row < 6; ++row )
for ( col = 0; col < 6; ++col )
cluster3[row][col] = cluster3[row][col] * ( maxval + 1 ) / 18;
break;
case QT_CLUSTER4:
/* Scale order-4 clustered dither matrix. */
for ( row = 0; row < 8; ++row )
for ( col = 0; col < 8; ++col )
cluster4[row][col] = cluster4[row][col] * ( maxval + 1 ) / 32;
break;
case QT_CLUSTER8:
/* Scale order-8 clustered dither matrix. */
for ( row = 0; row < 16; ++row )
for ( col = 0; col < 16; ++col )
cluster8[row][col] = cluster8[row][col] * ( maxval + 1 ) / 128;
break;
default:
pm_error( "can't happen" );
exit( 1 );
}
for ( row = 0; row < rows; ++row )
{
pgm_readpgmrow( ifp, grayrow, cols, maxval, format );
switch ( halftone )
{
case QT_FS:
for ( col = 0; col < cols + 2; ++col )
nexterr[col] = 0;
if ( fs_direction )
{
col = 0;
limitcol = cols;
gP = grayrow;
bP = bitrow;
}
else
{
col = cols - 1;
limitcol = -1;
gP = &(grayrow[col]);
bP = &(bitrow[col]);
}
do
{
sum = ( (long) *gP * FS_SCALE ) / maxval + thiserr[col + 1];
if ( sum >= threshval )
{
*bP = PBM_WHITE;
sum = sum - threshval - HALF_FS_SCALE;
}
else
*bP = PBM_BLACK;
if ( fs_direction )
{
thiserr[col + 2] += ( sum * 7 ) / 16;
nexterr[col ] += ( sum * 3 ) / 16;
nexterr[col + 1] += ( sum * 5 ) / 16;
nexterr[col + 2] += ( sum ) / 16;
++col;
++gP;
++bP;
}
else
{
thiserr[col ] += ( sum * 7 ) / 16;
nexterr[col + 2] += ( sum * 3 ) / 16;
nexterr[col + 1] += ( sum * 5 ) / 16;
nexterr[col ] += ( sum ) / 16;
--col;
--gP;
--bP;
}
}
while ( col != limitcol );
temperr = thiserr;
thiserr = nexterr;
nexterr = temperr;
fs_direction = ! fs_direction;
break;
case QT_THRESH:
for ( col = 0, gP = grayrow, bP = bitrow; col < cols; ++col, ++gP, ++bP )
if ( *gP >= threshval )
*bP = PBM_WHITE;
else
*bP = PBM_BLACK;
break;
case QT_DITHER8:
for ( col = 0, gP = grayrow, bP = bitrow; col < cols; ++col, ++gP, ++bP )
if ( *gP >= dither8[row % 16][col % 16] )
*bP = PBM_WHITE;
else
*bP = PBM_BLACK;
break;
case QT_CLUSTER3:
for ( col = 0, gP = grayrow, bP = bitrow; col < cols; ++col, ++gP, ++bP )
if ( *gP >= cluster3[row % 6][col % 6] )
*bP = PBM_WHITE;
else
*bP = PBM_BLACK;
break;
case QT_CLUSTER4:
for ( col = 0, gP = grayrow, bP = bitrow; col < cols; ++col, ++gP, ++bP )
if ( *gP >= cluster4[row % 8][col % 8] )
*bP = PBM_WHITE;
else
*bP = PBM_BLACK;
break;
case QT_CLUSTER8:
for ( col = 0, gP = grayrow, bP = bitrow; col < cols; ++col, ++gP, ++bP )
if ( *gP >= cluster8[row % 16][col % 16] )
*bP = PBM_WHITE;
else
*bP = PBM_BLACK;
break;
default:
pm_error( "can't happen" );
exit( 1 );
}
pbm_writepbmrow( stdout, bitrow, cols, 0 );
}
}
else /* else use hilbert space filling curve dithering */
/*
* This is taken from the article "Digital Halftoning with
* Space Filling Curves" by Luiz Velho, proceedings of
* SIGRAPH '91, page 81.
*
* This is not a terribly efficient or quick version of
* this algorithm, but it seems to work. - Graeme Gill.
* graeme@labtam.labtam.OZ.AU
*
*/
{
gray **grays;
bit **bits;
int xx,yy;
int end;
int *x,*y;
int sum = 0;
grays = pgm_readpgm( ifp, &cols,&rows, &maxval );
bits = pbm_allocarray(cols,rows);
x = (int *) malloc( sizeof(int) * clump_size);
y = (int *) malloc( sizeof(int) * clump_size);
if (!x || !y)
pm_error( "Run out of memory" );
init_hilbert(cols,rows);
end = clump_size;
while (end == clump_size)
{
int i;
/* compute the next clust co-ordinates along hilbert path */
for (i = 0; i < end; i++)
{
if (hilbert(&x[i],&y[i])==0)
end = i; /* we reached the end */
}
/* sum levels */
for (i = 0; i < end; i++)
sum += grays[y[i]][x[i]];
/* dither half and half along path */
for (i = 0; i < end; i++)
{
if (sum >= maxval)
{
bits[y[i]][x[i]] = PBM_WHITE;
sum -= maxval;
}
else
bits[y[i]][x[i]] = PBM_BLACK;
}
}
pbm_writepbm( stdout, bits, cols, rows, 0 );
}
pm_close( ifp );
exit( 0 );
}
/* Hilbert curve tracer */
#define MAXORD 18
static int hil_order,hil_ord;
static int hil_turn;
static int hil_dx,hil_dy;
static int hil_x,hil_y;
static int hil_stage[MAXORD];
static int hil_width,hil_height;
/* Initialise the Hilbert curve tracer */
static void init_hilbert(w,h)
int w,h;
{
int big,ber;
hil_width = w;
hil_height = h;
big = w > h ? w : h;
for(ber = 2, hil_order = 1; ber < big; ber <<= 1, hil_order++);
if (hil_order > MAXORD)
pm_error( "Sorry, hilbert order is too large" );
hil_ord = hil_order;
hil_order--;
}
/* Return non-zero if got another point */
static int hilbert(px,py)
int *px,*py;
{
int temp;
if (hil_ord > hil_order) /* have to do first point */
{
hil_ord--;
hil_stage[hil_ord] = 0;
hil_turn = -1;
hil_dy = 1;
hil_dx = hil_x = hil_y = 0;
*px = *py = 0;
return 1;
}
for(;;) /* Operate the state machine */
{
switch (hil_stage[hil_ord])
{
case 0:
hil_turn = -hil_turn;
temp = hil_dy;
hil_dy = -hil_turn * hil_dx;
hil_dx = hil_turn * temp;
if (hil_ord > 0)
{
hil_stage[hil_ord] = 1;
hil_ord--;
hil_stage[hil_ord]=0;
continue;
}
case 1:
hil_x += hil_dx;
hil_y += hil_dy;
if (hil_x < hil_width && hil_y < hil_height)
{
hil_stage[hil_ord] = 2;
*px = hil_x;
*py = hil_y;
return 1;
}
case 2:
hil_turn = -hil_turn;
temp = hil_dy;
hil_dy = -hil_turn * hil_dx;
hil_dx = hil_turn * temp;
if (hil_ord > 0) /* recurse */
{
hil_stage[hil_ord] = 3;
hil_ord--;
hil_stage[hil_ord]=0;
continue;
}
case 3:
hil_x += hil_dx;
hil_y += hil_dy;
if (hil_x < hil_width && hil_y < hil_height)
{
hil_stage[hil_ord] = 4;
*px = hil_x;
*py = hil_y;
return 1;
}
case 4:
if (hil_ord > 0) /* recurse */
{
hil_stage[hil_ord] = 5;
hil_ord--;
hil_stage[hil_ord]=0;
continue;
}
case 5:
temp = hil_dy;
hil_dy = -hil_turn * hil_dx;
hil_dx = hil_turn * temp;
hil_turn = -hil_turn;
hil_x += hil_dx;
hil_y += hil_dy;
if (hil_x < hil_width && hil_y < hil_height)
{
hil_stage[hil_ord] = 6;
*px = hil_x;
*py = hil_y;
return 1;
}
case 6:
if (hil_ord > 0) /* recurse */
{
hil_stage[hil_ord] = 7;
hil_ord--;
hil_stage[hil_ord]=0;
continue;
}
case 7:
temp = hil_dy;
hil_dy = -hil_turn * hil_dx;
hil_dx = hil_turn * temp;
hil_turn = -hil_turn;
/* Return from a recursion */
if (hil_ord < hil_order)
hil_ord++;
else
return 0;
}
}
}