This is rledither.c in view mode; [Download] [Up]
/* * This software is copyrighted as noted below. It may be freely copied, * modified, and redistributed, provided that the copyright notice is * preserved on all copies. * * There is no warranty or other guarantee of fitness for this software, * it is provided solely "as is". Bug reports or fixes may be sent * to the author, who may or may not act on them as he desires. * * You may not include this software in a program or other software product * without supplying the source, or without informing the end-user that the * source is available for no extra charge. * * If you modify this software, you should include a notice giving the * name of the person performing the modification, the date of modification, * and the reason for such modification. */ /* * rledither.c - Do Floyd Steinberg dithering on an image. Edge enhancement * can be tuned with command line argument. * * Author: Rod Bogart * University of Michigan * Date: Fri Dec 1 1989 * Copyright (c) 1989, The Regents of the University of Michigan * * -e edge_factor Zero means no edge enhancement, 1.0 looks pretty good. * -l nchan length Number of channels in the colormap, and num entries. * Defaults to 3 channels of 256 entries each. * -{tf} mapfile -t is a colormap listed as R G B R G B R G B ... * -f is listed as R R R... G G G... B B B... * -o outfile Output RLE file (stdout default) * infile Input RLE file (stdin default) */ #ifndef lint static char rcs_ident[] = "$Id: rledither.c,v 3.0 90/08/03 15:22:48 spencer Exp $"; #endif #include <stdio.h> #include <rle.h> #ifdef USE_STDLIB_H #include <stdlib.h> #else #ifdef VOID_STAR extern void *malloc(); #else extern char *malloc(); #endif extern void free(); #endif /* USE_STDLIB_H */ #define MALLOC_ERR {fprintf(stderr, "%s: ran out of heap space\n", \ progname);exit(-2);} rle_map **allocmap(); void filemap(), copymap(), shiftmap(), copy_into_shortrow(), edge_enhance(); void fsdither(), find_closest(); char *progname; void main(argc, argv) int argc; char *argv[]; { int i, temp, y; int oflag = 0; int blend, blend_divisor = 256; float edge_factor = 0.0; int top, middle, bottom, edgetop, edgebottom; rle_hdr in_hdr, out_hdr; rle_pixel **cleanrows[3], **edgerows[2], **outrow; short *shortrows[2]; char *infname = NULL, *outfname = NULL, *mapfname = NULL; FILE *outfile = stdout; int nchan = 3, length = 256, tflag = 0; rle_map **amap, **map; int rle_cnt, rle_err; char cmap_comment[80]; progname = cmd_name( argv ); if ( scanargs( argc, argv, "% e%-edge_factor!f l%-nchan!dlength!d \n\ \ttf!-mapfile!s o%-outfile!s infile%s", &i, &edge_factor, &i, &nchan, &length, &tflag, &mapfname, &oflag, &outfname, &infname ) == 0 ) exit( 1 ); in_hdr.rle_file = rle_open_f( progname, infname, "r" ); /* Allocate space for the applied map */ amap = allocmap( nchan, 256, NULL ); map = allocmap( nchan, 256, NULL ); filemap( tflag, mapfname, nchan, length, amap ); copymap( amap, nchan, 256, map ); shiftmap( amap, nchan, 256, 8 ); for ( rle_cnt = 0; (rle_err = rle_get_setup( &in_hdr )) == RLE_SUCCESS; rle_cnt++ ) { out_hdr = in_hdr; if ( rle_cnt == 0 ) outfile = rle_open_f( cmd_name( argv ), outfname, "w" ); out_hdr.rle_file = outfile; rle_addhist( argv, &in_hdr, &out_hdr ); /* alpha on dithered stuff is too weird... */ out_hdr.alpha = 0; out_hdr.ncolors = 1; /* Just one chan, the cmap index */ for (i = 1; i < in_hdr.ncolors; i++) RLE_CLR_BIT( out_hdr, i ); /* Kill extra output channels */ out_hdr.ncmap = nchan; out_hdr.cmaplen = 8; /* == 256 entries */ out_hdr.cmap = (rle_map *) amap[0]; sprintf( cmap_comment, "color_map_length=%d", length ); rle_putcom( cmap_comment, &out_hdr ); rle_put_setup( &out_hdr ); in_hdr.xmax -= in_hdr.xmin; in_hdr.xmin = 0; /* Oink. */ for (i = 0; i < 3; i++) if (rle_row_alloc( &in_hdr, &cleanrows[i] )) MALLOC_ERR; if (rle_row_alloc( &in_hdr, &edgerows[0] )) MALLOC_ERR; if (rle_row_alloc( &in_hdr, &edgerows[1] )) MALLOC_ERR; if (rle_row_alloc( &out_hdr, &outrow )) MALLOC_ERR; if ( (shortrows[0] = (short *)malloc(in_hdr.ncolors * (in_hdr.xmax+1) * sizeof(short) )) == 0 ) MALLOC_ERR; if ( (shortrows[1] = (short *)malloc(in_hdr.ncolors * (in_hdr.xmax+1) * sizeof(short) )) == 0 ) MALLOC_ERR; if (edge_factor < 0.0) blend = 0; else blend = (int) (edge_factor * (float) blend_divisor); rle_getrow(&in_hdr, cleanrows[2]); rle_getrow(&in_hdr, cleanrows[1]); bottom = 0; middle = 1; top = 2; edgetop = 1; edgebottom = 0; /* just copy the top row, it gets no edge enhancement */ copy_into_shortrow( &in_hdr, cleanrows[top], shortrows[edgetop] ); for (y = in_hdr.ymin+2; y <= in_hdr.ymax; y++) { rle_getrow(&in_hdr, cleanrows[bottom]); edge_enhance(&in_hdr, cleanrows[bottom], cleanrows[middle], cleanrows[top], edgerows[edgebottom], blend, blend_divisor); copy_into_shortrow( &in_hdr, edgerows[edgebottom], shortrows[edgebottom] ); fsdither(&in_hdr, map, length, shortrows[edgebottom], shortrows[edgetop], outrow); rle_putrow( outrow, in_hdr.xmax+1, &out_hdr); /* swap the row pointers */ temp = top; top = middle; middle = bottom; bottom = temp; temp = edgetop; edgetop = edgebottom; edgebottom = temp; } fsdither(&in_hdr, map, length, shortrows[edgebottom], shortrows[edgetop], outrow); rle_putrow( outrow, in_hdr.xmax+1, &out_hdr); /* just copy the middle row, it gets no edge enhancement */ copy_into_shortrow( &in_hdr, cleanrows[middle], shortrows[edgetop] ); fsdither(&in_hdr, map, length, NULL, shortrows[edgetop], outrow); rle_putrow( outrow, in_hdr.xmax+1, &out_hdr); rle_puteof( &out_hdr ); /* Release storage. */ for (i = 0; i < 3; i++) rle_row_free( &in_hdr, cleanrows[i] ); rle_row_free( &in_hdr, edgerows[0] ); rle_row_free( &in_hdr, edgerows[1] ); rle_row_free( &out_hdr, outrow ); free( shortrows[0] ); free( shortrows[1] ); } /* Check for an error. EOF or EMPTY is ok if at least one image * has been read. Otherwise, print an error message. */ if ( rle_cnt == 0 || (rle_err != RLE_EOF && rle_err != RLE_EMPTY) ) rle_get_error( rle_err, cmd_name( argv ), infname ); exit( 0 ); } void copy_into_shortrow( in_hdr, rlerow, shortrow ) rle_hdr *in_hdr; rle_pixel **rlerow; short *shortrow; { int chan,i; for (i=0; i <= in_hdr->xmax; i++) { for (chan = 0; chan < in_hdr->ncolors; chan++) { *shortrow++ = rlerow[chan][i]; } } } void edge_enhance(in_hdr, row_b, row_m, row_t, edge_row, blend, blend_divisor) rle_hdr *in_hdr; rle_pixel **row_b, **row_m, **row_t, **edge_row; int blend_divisor, blend; { int chan, i; int total, diff, avg, result; for (chan = 0; chan < in_hdr->ncolors; chan++) { /* i = 0 case, ignore left column of pixels */ total = row_b[chan][0] + row_b[chan][1] + row_m[chan][1] + row_t[chan][0] + row_t[chan][1]; avg = total / 5; diff = avg - row_m[chan][0]; result = row_m[chan][0] - (diff * blend / blend_divisor); if (result < 0) edge_row[chan][0] = 0; else if (result > 255) edge_row[chan][0] = 255; else edge_row[chan][0] = result; /* all the nice cases */ for (i=1; i < in_hdr->xmax; i++) { total = row_b[chan][i-1] + row_b[chan][i] + row_b[chan][i+1] + row_m[chan][i-1] + row_m[chan][i+1] + row_t[chan][i-1] + row_t[chan][i] + row_t[chan][i+1]; avg = total >> 3; /* divide by 8 */ diff = avg - row_m[chan][i]; result = row_m[chan][i] - (diff * blend / blend_divisor); if (result < 0) edge_row[chan][i] = 0; else if (result > 255) edge_row[chan][i] = 255; else edge_row[chan][i] = result; } /* i = in_hdr.xmax case, ignore right column of pixels */ i = in_hdr->xmax; total = row_b[chan][i-1] + row_b[chan][i] + row_m[chan][i-1] + row_t[chan][i-1] + row_t[chan][i]; avg = total / 5; diff = avg - row_m[chan][i]; result = row_m[chan][i] - (diff * blend / blend_divisor); if (result < 0) edge_row[chan][i] = 0; else if (result > 255) edge_row[chan][i] = 255; else edge_row[chan][i] = result; } } void fsdither(in_hdr, map, maplen, row_bottom, row_top, outrow) rle_hdr *in_hdr; rle_map **map; int maplen; short *row_bottom, *row_top; rle_pixel **outrow; { register rle_pixel *optr; register int j; register short *thisptr, *nextptr = NULL; int chan; static int numchan = 0; int lastline = 0, lastpixel ; static int *cval=0; static rle_pixel *pixel=0; if ( numchan != in_hdr->ncolors ) if ( cval ) { free( cval ); free( pixel ); } numchan = in_hdr->ncolors; if (!cval) { if ((cval = (int *) malloc( numchan * sizeof(int) )) == 0) MALLOC_ERR; if ((pixel = (rle_pixel *) malloc( numchan * sizeof(rle_pixel) )) == 0) MALLOC_ERR; } optr = outrow[RLE_RED]; thisptr = row_top; if (row_bottom) nextptr = row_bottom; else lastline = 1; for(j=0; j <= in_hdr->xmax ; j++) { int cmap_index=0; lastpixel = (j == in_hdr->xmax) ; for (chan = 0; chan < numchan; chan++) { cval[chan] = *thisptr++ ; /* Current channel value has been accumulating error, it could be * out of range. */ if( cval[chan] < 0 ) cval[chan] = 0 ; else if( cval[chan] > 255 ) cval[chan] = 255 ; pixel[chan] = cval[chan]; } /* find closest color */ find_closest(map, numchan, maplen, pixel, &cmap_index); *optr++ = cmap_index; /* thisptr is now looking at pixel to the right of current pixel * nextptr is looking at pixel below current pixel * So, increment thisptr as stuff gets stored. nextptr gets moved * by one, and indexing is done +/- numchan. */ for (chan = 0; chan < numchan; chan++) { cval[chan] -= map[chan][cmap_index]; if( !lastpixel ) { thisptr[chan] += cval[chan] * 7 / 16 ; } if( !lastline ) { if( j != 0 ) { nextptr[-numchan] += cval[chan] * 3 / 16 ; } nextptr[0] += cval[chan] * 5 / 16 ; if( !lastpixel ) { nextptr[numchan] += cval[chan] / 16 ; } nextptr ++; } } } } /***************************************************************** * TAG( filemap ) * * NOTE: this code is stolen from rleldmap. * Read a color map from a file * Inputs: * tflag: Flag for type of file: 1 means all entries for a * channel are together (-f), 2 means all entries for * a given index are together (-t). * mapfname: Name of file to read map from. * nchan: Number of color channels. * length: Length of each channel. * Outputs: * amap: Result map. * Assumptions: * [None] * Algorithm: * [None] */ void filemap( tflag, mapfname, nchan, length, amap ) int tflag, nchan, length; char *mapfname; rle_map **amap; { FILE * mapfile; register int c, i; int ent; if ( ( mapfile = fopen( mapfname, "r" ) ) == NULL ) { fprintf( stderr, "%s: Couldn't open map file %s: ", progname, mapfname ); perror(""); exit(-1); } if ( tflag == 1 ) /* channel-major order */ for ( c = 0; c < nchan; c++ ) for ( i = 0; i < length; i++ ) switch ( fscanf( mapfile, "%d", &ent ) ) { case EOF: /* EOF */ fprintf( stderr, "%s: Premature end of file reading map %s at channel %d, entry %d\n", progname, mapfname, c, i ); exit(-1); /* NOTREACHED */ case 1: /* Got it */ amap[c][i] = ent; break; case 0: /* no conversion? */ fprintf( stderr, "%s: Bad data in map %s at channel %d, entry %d\n", progname, mapfname, c, i ); exit(-1); /* NOTREACHED */ default: /* error */ fprintf( stderr, "%s: Error reading map %s at channel %d, entry %d\n", progname, mapfname, c, i ); exit(-1); /* NOTREACHED */ } else /* Entry-major order */ for ( i = 0; i < length; i++ ) for ( c = 0; c < nchan; c++ ) switch ( fscanf( mapfile, "%d", &ent ) ) { case EOF: /* EOF */ fprintf( stderr, "%s: Premature end of file reading map %s at entry %d, channel %d\n", progname, mapfname, i, c ); exit(-1); /* NOTREACHED */ case 1: /* Got it */ amap[c][i] = ent; break; case 0: /* no conversion? */ fprintf( stderr, "%s: Bad data in map %s at entry %d, channel %d\n", progname, mapfname, i, c ); exit(-1); /* NOTREACHED */ default: /* error */ fprintf( stderr, "%s: Error reading map %s at entry %d, channel %d: ", progname, mapfname, i, c ); perror(""); exit(-1); /* NOTREACHED */ } fclose( mapfile ); } /***************************************************************** * TAG( allocmap ) * * Allocate a color map of a given size. * Inputs: * nchan: Number of channels in the map. * length: Length of each channel of the map. * cmap: If non-null, the storage to be used. * Outputs: * Returns a pointer to an array[nchan] of pointers to * array[length] of rle_map. The rle_map array can also * be addressed contiguously as return[0][chan*length+i]. * Assumptions: * If cmap is supplied, it is an array of nchan*length rle_maps. * Algorithm: * [None] */ rle_map ** allocmap( nchan, length, cmap ) int nchan; int length; rle_map * cmap; { rle_map ** map; register int i; map = (rle_map **)malloc( nchan * sizeof( rle_map * ) ); if ( cmap == NULL ) map[0] = (rle_map *)malloc( nchan * length * sizeof( rle_map ) ); else map[0] = cmap; for ( i = 1; i < nchan; i++ ) map[i] = &map[i-1][length]; return map; } /***************************************************************** * TAG( shiftmap ) * * Shift the entries in the color map to left justify them. * Inputs: * map: The color map. * nchan: Number of color channels in the map. * length: Number of entries in each channel. * bits: Number of bits in each entry. * Outputs: * map: Left justified map (modified in place). * Assumptions: * map[0] points to a contiguous array of nchan*length rle_maps * (as set up by allocmap). * Algorithm: * [None] */ void shiftmap( map, nchan, length, bits ) int nchan, length, bits; rle_map **map; { register rle_map * e; register int i; bits = 16 - bits; if ( bits == 0 ) return; /* no work! */ for ( i = nchan * length, e = map[0]; i > 0; i--, e++ ) *e <<= bits; } void copymap( inmap, nchan, length, outmap ) rle_map **inmap, **outmap; int nchan, length; { register rle_map *ie, *oe; register int i; for ( i = nchan * length, ie = inmap[0], oe = outmap[0]; i > 0; i--, ie++, oe++ ) *oe = *ie; } void find_closest(map, nchan, maplen, pixel, index) rle_map ** map; int nchan, maplen; rle_pixel *pixel; int *index; { int i, closest, chan; long bestdist, dist; closest = -1; bestdist = 256*256*nchan + 1; for (i=0; i < maplen; i++) { dist = 0L; for (chan = 0; chan < nchan; chan++) { int tmp = map[chan][i] - pixel[chan]; dist += tmp * tmp; } if (dist < bestdist) { closest = i; bestdist = dist; } } *index = closest; }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.