Utah Raster Toolkit  9999-git
URT Development version (post-3.1b)
rledither.c
Go to the documentation of this file.
1 /*
2  * This software is copyrighted as noted below. It may be freely copied,
3  * modified, and redistributed, provided that the copyright notice is
4  * preserved on all copies.
5  *
6  * There is no warranty or other guarantee of fitness for this software,
7  * it is provided solely "as is". Bug reports or fixes may be sent
8  * to the author, who may or may not act on them as he desires.
9  *
10  * You may not include this software in a program or other software product
11  * without supplying the source, or without informing the end-user that the
12  * source is available for no extra charge.
13  *
14  * If you modify this software, you should include a notice giving the
15  * name of the person performing the modification, the date of modification,
16  * and the reason for such modification.
17  */
18 /*
19  * rledither.c - Do Floyd Steinberg dithering on an image. Edge enhancement
20  * can be tuned with command line argument.
21  *
22  * Author: Rod Bogart
23  * University of Michigan
24  * Date: Fri Dec 1 1989
25  * Copyright (c) 1989, The Regents of the University of Michigan
26  *
27  * -e edge_factor Zero means no edge enhancement, 1.0 looks pretty good.
28  * -l nchan length Number of channels in the colormap, and num entries.
29  * Defaults to 3 channels of 256 entries each.
30  * -{tf} mapfile -t is a colormap listed as R G B R G B R G B ...
31  * -f is listed as R R R... G G G... B B B...
32  * -o outfile Output RLE file (stdout default)
33  * infile Input RLE file (stdin default)
34  */
35 #ifndef lint
36 static char rcs_ident[] = "$Id: rledither.c,v 3.0.1.1 1992/01/23 16:42:22 spencer Exp $";
37 #endif
38 
39 #include <stdio.h>
40 #include "rle.h"
41 
42 #define MALLOC_ERR RLE_CHECK_ALLOC( progname, 0, 0 )
43 
44 rle_map **allocmap();
46 void fsdither(), find_closest();
47 
48 char *progname;
49 
50 void
52 int argc;
53 char *argv[];
54 {
55  int i, temp, y;
56  int oflag = 0;
57  int blend, blend_divisor = 256;
58  float edge_factor = 0.0;
59  int top, middle, bottom, edgetop, edgebottom;
60  rle_hdr in_hdr, out_hdr;
61  rle_pixel **cleanrows[3], **edgerows[2], **outrow;
62  short *shortrows[2];
63  char *infname = NULL, *outfname = NULL, *mapfname = NULL;
64  FILE *outfile = stdout;
65  int nchan = 3, length = 256, tflag = 0;
66  rle_map **amap, **map;
67  int rle_cnt, rle_err;
68  char cmap_comment[80];
69 
70  progname = cmd_name( argv );
71  in_hdr = *rle_hdr_init( NULL );
72  out_hdr = *rle_hdr_init( NULL );
73 
74 
75  if ( scanargs( argc, argv,
76  "% e%-edge_factor!f l%-nchan!dlength!d \n\
77 \ttf!-mapfile!s o%-outfile!s infile%s",
78  &i, &edge_factor, &i, &nchan, &length,
79  &tflag, &mapfname,
80  &oflag, &outfname, &infname ) == 0 )
81  exit( 1 );
82 
83  in_hdr.rle_file = rle_open_f( progname, infname, "r" );
84  rle_names( &in_hdr, progname, infname, 0 );
85  rle_names( &out_hdr, progname, outfname, 0 );
86 
87  /* Allocate space for the applied map */
88  amap = allocmap( nchan, 256, NULL );
89  map = allocmap( nchan, 256, NULL );
90  filemap( tflag, mapfname, nchan, length, amap );
91  copymap( amap, nchan, 256, map );
92  shiftmap( amap, nchan, 256, 8 );
93 
94  for ( rle_cnt = 0;
95  (rle_err = rle_get_setup( &in_hdr )) == RLE_SUCCESS;
96  rle_cnt++ )
97  {
98  (void)rle_hdr_cp( &in_hdr, &out_hdr );
99  if ( rle_cnt == 0 )
100  outfile = rle_open_f( cmd_name( argv ), outfname, "w" );
101  out_hdr.rle_file = outfile;
102 
103  rle_addhist( argv, &in_hdr, &out_hdr );
104 
105  /* alpha on dithered stuff is too weird... */
106  out_hdr.alpha = 0;
107  out_hdr.ncolors = 1; /* Just one chan, the cmap index */
108  for (i = 1; i < in_hdr.ncolors; i++)
109  RLE_CLR_BIT( out_hdr, i ); /* Kill extra output channels */
110  out_hdr.ncmap = nchan;
111  out_hdr.cmaplen = 8; /* == 256 entries */
112  out_hdr.cmap = (rle_map *) amap[0];
113  sprintf( cmap_comment, "color_map_length=%d", length );
114  rle_putcom( cmap_comment, &out_hdr );
115  rle_put_setup( &out_hdr );
116 
117  in_hdr.xmax -= in_hdr.xmin;
118  in_hdr.xmin = 0;
119 
120  /* Oink. */
121  for (i = 0; i < 3; i++)
122  if (rle_row_alloc( &in_hdr, &cleanrows[i] ) < 0)
123  MALLOC_ERR;
124 
125  if (rle_row_alloc( &in_hdr, &edgerows[0] ) < 0)
126  MALLOC_ERR;
127 
128  if (rle_row_alloc( &in_hdr, &edgerows[1] ) < 0)
129  MALLOC_ERR;
130 
131  if (rle_row_alloc( &out_hdr, &outrow ) < 0)
132  MALLOC_ERR;
133 
134  if ( (shortrows[0] = (short *)malloc(in_hdr.ncolors
135  * (in_hdr.xmax+1)
136  * sizeof(short) )) == 0 )
137  MALLOC_ERR;
138  if ( (shortrows[1] = (short *)malloc(in_hdr.ncolors
139  * (in_hdr.xmax+1)
140  * sizeof(short) )) == 0 )
141  MALLOC_ERR;
142 
143  if (edge_factor < 0.0)
144  blend = 0;
145  else
146  blend = (int) (edge_factor * (float) blend_divisor);
147 
148  rle_getrow(&in_hdr, cleanrows[2]);
149  rle_getrow(&in_hdr, cleanrows[1]);
150  bottom = 0;
151  middle = 1;
152  top = 2;
153  edgetop = 1;
154  edgebottom = 0;
155  /* just copy the top row, it gets no edge enhancement */
156  copy_into_shortrow( &in_hdr,
157  cleanrows[top], shortrows[edgetop] );
158 
159  for (y = in_hdr.ymin+2; y <= in_hdr.ymax; y++)
160  {
161  rle_getrow(&in_hdr, cleanrows[bottom]);
162  edge_enhance(&in_hdr,
163  cleanrows[bottom], cleanrows[middle], cleanrows[top],
164  edgerows[edgebottom],
165  blend, blend_divisor);
166  copy_into_shortrow( &in_hdr,
167  edgerows[edgebottom], shortrows[edgebottom] );
168  fsdither(&in_hdr, map, length,
169  shortrows[edgebottom], shortrows[edgetop], outrow);
170  rle_putrow( outrow, in_hdr.xmax+1, &out_hdr);
171  /* swap the row pointers */
172  temp = top; top = middle; middle = bottom; bottom = temp;
173  temp = edgetop; edgetop = edgebottom; edgebottom = temp;
174  }
175  fsdither(&in_hdr, map, length,
176  shortrows[edgebottom], shortrows[edgetop], outrow);
177  rle_putrow( outrow, in_hdr.xmax+1, &out_hdr);
178  /* just copy the middle row, it gets no edge enhancement */
179  copy_into_shortrow( &in_hdr,
180  cleanrows[middle], shortrows[edgetop] );
181  fsdither(&in_hdr, map, length, NULL, shortrows[edgetop], outrow);
182  rle_putrow( outrow, in_hdr.xmax+1, &out_hdr);
183 
184  rle_puteof( &out_hdr );
185 
186  /* Release storage. */
187  for (i = 0; i < 3; i++)
188  rle_row_free( &in_hdr, cleanrows[i] );
189  rle_row_free( &in_hdr, edgerows[0] );
190  rle_row_free( &in_hdr, edgerows[1] );
191  rle_row_free( &out_hdr, outrow );
192  free( shortrows[0] );
193  free( shortrows[1] );
194  }
195 
196  /* Check for an error. EOF or EMPTY is ok if at least one image
197  * has been read. Otherwise, print an error message.
198  */
199  if ( rle_cnt == 0 || (rle_err != RLE_EOF && rle_err != RLE_EMPTY) )
200  rle_get_error( rle_err, cmd_name( argv ), infname );
201 
202  exit( 0 );
203 }
204 
205 void
207 rle_hdr *in_hdr;
208 rle_pixel **rlerow;
209 short *shortrow;
210 {
211  int chan,i;
212 
213  for (i=0; i <= in_hdr->xmax; i++)
214  {
215  for (chan = 0; chan < in_hdr->ncolors; chan++)
216  {
217  *shortrow++ = rlerow[chan][i];
218  }
219  }
220 }
221 
222 void
224 rle_hdr *in_hdr;
225 rle_pixel **row_b, **row_m, **row_t, **edge_row;
226 int blend_divisor, blend;
227 {
228  int chan, i;
229  int total, diff, avg, result;
230  for (chan = 0; chan < in_hdr->ncolors; chan++)
231  {
232  /* i = 0 case, ignore left column of pixels */
233  total = row_b[chan][0] + row_b[chan][1] +
234  row_m[chan][1] +
235  row_t[chan][0] + row_t[chan][1];
236  avg = total / 5;
237  diff = avg - row_m[chan][0];
238  result = row_m[chan][0] - (diff * blend / blend_divisor);
239  if (result < 0)
240  edge_row[chan][0] = 0;
241  else if (result > 255)
242  edge_row[chan][0] = 255;
243  else
244  edge_row[chan][0] = result;
245 
246  /* all the nice cases */
247  for (i=1; i < in_hdr->xmax; i++)
248  {
249  total = row_b[chan][i-1] + row_b[chan][i] + row_b[chan][i+1] +
250  row_m[chan][i-1] + row_m[chan][i+1] +
251  row_t[chan][i-1] + row_t[chan][i] + row_t[chan][i+1];
252  avg = total >> 3; /* divide by 8 */
253  diff = avg - row_m[chan][i];
254  result = row_m[chan][i] - (diff * blend / blend_divisor);
255  if (result < 0)
256  edge_row[chan][i] = 0;
257  else if (result > 255)
258  edge_row[chan][i] = 255;
259  else
260  edge_row[chan][i] = result;
261  }
262  /* i = in_hdr.xmax case, ignore right column of pixels */
263  i = in_hdr->xmax;
264  total = row_b[chan][i-1] + row_b[chan][i] +
265  row_m[chan][i-1] +
266  row_t[chan][i-1] + row_t[chan][i];
267  avg = total / 5;
268  diff = avg - row_m[chan][i];
269  result = row_m[chan][i] - (diff * blend / blend_divisor);
270  if (result < 0)
271  edge_row[chan][i] = 0;
272  else if (result > 255)
273  edge_row[chan][i] = 255;
274  else
275  edge_row[chan][i] = result;
276  }
277 }
278 
279 void
281 rle_hdr *in_hdr;
282 rle_map **map;
283 int maplen;
284 short *row_bottom, *row_top;
285 rle_pixel **outrow;
286 {
287  register rle_pixel *optr;
288  register int j;
289  register short *thisptr, *nextptr = NULL;
290  int chan;
291  static int numchan = 0;
292  int lastline = 0, lastpixel ;
293  static int *cval=0;
294  static rle_pixel *pixel=0;
295 
296  if ( numchan != in_hdr->ncolors )
297  if ( cval )
298  {
299  free( cval );
300  free( pixel );
301  }
302 
303  numchan = in_hdr->ncolors;
304  if (!cval) {
305  if ((cval = (int *) malloc( numchan * sizeof(int) )) == 0)
306  MALLOC_ERR;
307  if ((pixel = (rle_pixel *) malloc( numchan * sizeof(rle_pixel) )) == 0)
308  MALLOC_ERR;
309  }
310  optr = outrow[RLE_RED];
311 
312  thisptr = row_top;
313  if (row_bottom)
314  nextptr = row_bottom;
315  else
316  lastline = 1;
317 
318  for(j=0; j <= in_hdr->xmax ; j++)
319  {
320  int cmap_index=0;
321 
322  lastpixel = (j == in_hdr->xmax) ;
323 
324  for (chan = 0; chan < numchan; chan++)
325  {
326  cval[chan] = *thisptr++ ;
327 
328  /* Current channel value has been accumulating error, it could be
329  * out of range.
330  */
331  if( cval[chan] < 0 ) cval[chan] = 0 ;
332  else if( cval[chan] > 255 ) cval[chan] = 255 ;
333 
334  pixel[chan] = cval[chan];
335  }
336 
337  /* find closest color */
338  find_closest(map, numchan, maplen, pixel, &cmap_index);
339  *optr++ = cmap_index;
340 
341  /* thisptr is now looking at pixel to the right of current pixel
342  * nextptr is looking at pixel below current pixel
343  * So, increment thisptr as stuff gets stored. nextptr gets moved
344  * by one, and indexing is done +/- numchan.
345  */
346  for (chan = 0; chan < numchan; chan++)
347  {
348  cval[chan] -= map[chan][cmap_index];
349 
350  if( !lastpixel )
351  {
352  thisptr[chan] += cval[chan] * 7 / 16 ;
353  }
354  if( !lastline )
355  {
356  if( j != 0 )
357  {
358  nextptr[-numchan] += cval[chan] * 3 / 16 ;
359  }
360  nextptr[0] += cval[chan] * 5 / 16 ;
361  if( !lastpixel )
362  {
363  nextptr[numchan] += cval[chan] / 16 ;
364  }
365  nextptr ++;
366  }
367  }
368  }
369 }
370 
371 /*****************************************************************
372  * TAG( filemap )
373  *
374  * NOTE: this code is stolen from rleldmap.
375  * Read a color map from a file
376  * Inputs:
377  * tflag: Flag for type of file: 1 means all entries for a
378  * channel are together (-f), 2 means all entries for
379  * a given index are together (-t).
380  * mapfname: Name of file to read map from.
381  * nchan: Number of color channels.
382  * length: Length of each channel.
383  * Outputs:
384  * amap: Result map.
385  * Assumptions:
386  * [None]
387  * Algorithm:
388  * [None]
389  */
390 void
392 int tflag, nchan, length;
393 char *mapfname;
394 rle_map **amap;
395 {
396  FILE * mapfile;
397  register int c, i;
398  int ent;
399 
400  if ( ( mapfile = fopen( mapfname, "r" ) ) == NULL )
401  {
402  fprintf( stderr, "%s: Couldn't open map file %s: ",
403  progname, mapfname );
404  perror("");
405  exit(-1);
406  }
407 
408  if ( tflag == 1 ) /* channel-major order */
409  for ( c = 0; c < nchan; c++ )
410  for ( i = 0; i < length; i++ )
411  switch ( fscanf( mapfile, "%d", &ent ) )
412  {
413  case EOF: /* EOF */
414  fprintf( stderr,
415  "%s: Premature end of file reading map %s at channel %d, entry %d\n",
416  progname, mapfname, c, i );
417  exit(-1);
418  /* NOTREACHED */
419  case 1: /* Got it */
420  amap[c][i] = ent;
421  break;
422  case 0: /* no conversion? */
423  fprintf( stderr,
424  "%s: Bad data in map %s at channel %d, entry %d\n",
425  progname, mapfname, c, i );
426  exit(-1);
427  /* NOTREACHED */
428  default: /* error */
429  fprintf( stderr,
430  "%s: Error reading map %s at channel %d, entry %d\n",
431  progname, mapfname, c, i );
432  exit(-1);
433  /* NOTREACHED */
434  }
435  else /* Entry-major order */
436  for ( i = 0; i < length; i++ )
437  for ( c = 0; c < nchan; c++ )
438  switch ( fscanf( mapfile, "%d", &ent ) )
439  {
440  case EOF: /* EOF */
441  fprintf( stderr,
442  "%s: Premature end of file reading map %s at entry %d, channel %d\n",
443  progname, mapfname, i, c );
444  exit(-1);
445  /* NOTREACHED */
446  case 1: /* Got it */
447  amap[c][i] = ent;
448  break;
449  case 0: /* no conversion? */
450  fprintf( stderr,
451  "%s: Bad data in map %s at entry %d, channel %d\n",
452  progname, mapfname, i, c );
453  exit(-1);
454  /* NOTREACHED */
455  default: /* error */
456  fprintf( stderr,
457  "%s: Error reading map %s at entry %d, channel %d: ",
458  progname, mapfname, i, c );
459  perror("");
460  exit(-1);
461  /* NOTREACHED */
462  }
463  fclose( mapfile );
464 }
465 
466 /*****************************************************************
467  * TAG( allocmap )
468  *
469  * Allocate a color map of a given size.
470  * Inputs:
471  * nchan: Number of channels in the map.
472  * length: Length of each channel of the map.
473  * cmap: If non-null, the storage to be used.
474  * Outputs:
475  * Returns a pointer to an array[nchan] of pointers to
476  * array[length] of rle_map. The rle_map array can also
477  * be addressed contiguously as return[0][chan*length+i].
478  * Assumptions:
479  * If cmap is supplied, it is an array of nchan*length rle_maps.
480  * Algorithm:
481  * [None]
482  */
483 rle_map **
485 int nchan;
486 int length;
487 rle_map * cmap;
488 {
489  rle_map ** map;
490  register int i;
491 
492  map = (rle_map **)malloc( nchan * sizeof( rle_map * ) );
493  RLE_CHECK_ALLOC( progname, map, 0 );
494  if ( cmap == NULL )
495  {
496  map[0] = (rle_map *)malloc( nchan * length * sizeof( rle_map ) );
497  RLE_CHECK_ALLOC( progname, map[0], 0 );
498  }
499  else
500  map[0] = cmap;
501  for ( i = 1; i < nchan; i++ )
502  map[i] = &map[i-1][length];
503  return map;
504 }
505 
506 /*****************************************************************
507  * TAG( shiftmap )
508  *
509  * Shift the entries in the color map to left justify them.
510  * Inputs:
511  * map: The color map.
512  * nchan: Number of color channels in the map.
513  * length: Number of entries in each channel.
514  * bits: Number of bits in each entry.
515  * Outputs:
516  * map: Left justified map (modified in place).
517  * Assumptions:
518  * map[0] points to a contiguous array of nchan*length rle_maps
519  * (as set up by allocmap).
520  * Algorithm:
521  * [None]
522  */
523 void
525 int nchan, length, bits;
526 rle_map **map;
527 {
528  register rle_map * e;
529  register int i;
530 
531  bits = 16 - bits;
532  if ( bits == 0 )
533  return; /* no work! */
534 
535  for ( i = nchan * length, e = map[0]; i > 0; i--, e++ )
536  *e <<= bits;
537 }
538 
539 void
541 rle_map **inmap, **outmap;
542 int nchan, length;
543 {
544  register rle_map *ie, *oe;
545  register int i;
546 
547  for ( i = nchan * length, ie = inmap[0], oe = outmap[0];
548  i > 0; i--, ie++, oe++ )
549  *oe = *ie;
550 }
551 
552 void
554 rle_map ** map;
555 int nchan, maplen;
556 rle_pixel *pixel;
557 int *index;
558 {
559  int i, closest, chan;
560  long bestdist, dist;
561 
562  closest = -1;
563  bestdist = 256*256*nchan + 1;
564  for (i=0; i < maplen; i++)
565  {
566  dist = 0L;
567  for (chan = 0; chan < nchan; chan++)
568  {
569  int tmp = map[chan][i] - pixel[chan];
570  dist += tmp * tmp;
571  }
572  if (dist < bestdist)
573  {
574  closest = i;
575  bestdist = dist;
576  }
577  }
578  *index = closest;
579 }
FILE * rle_open_f(char *prog_name, char *file_name, char *mode)
Definition: rle_open_f.c:216
int xmin
Definition: rle.h:100
rle_hdr * rle_hdr_cp(rle_hdr *from_hdr, rle_hdr *to_hdr)
Definition: rle_hdr.c:119
void rle_names(rle_hdr *the_hdr, const char *pgmname, const char *fname, int img_num)
Definition: rle_hdr.c:48
void rle_row_free(rle_hdr *the_hdr, rle_pixel **scanp)
Definition: rle_row_alc.c:114
#define RLE_EMPTY
Definition: rle.h:73
char * cmd_name(char **argv)
Definition: cmd_name.c:31
void main(int argc, char **argv)
Definition: aliastorle.c:121
int rle_get_setup(rle_hdr *the_hdr)
Definition: rle_getrow.c:74
void copy_into_shortrow(rle_hdr *in_hdr, rle_pixel **rlerow, short *shortrow)
Definition: rledither.c:206
rle_map * cmap
Definition: rle.h:112
int rle_row_alloc(rle_hdr *the_hdr, rle_pixel ***scanp)
Definition: rle_row_alc.c:56
int rle_getrow(rle_hdr *the_hdr, scanline)
Definition: rle_getrow.c:333
#define RLE_SUCCESS
Definition: rle.h:70
int ymin
Definition: rle.h:100
int rle_get_error(int code, const char *pgmname, const char *fname)
Definition: rle_error.c:76
char * progname
Definition: unslice.c:51
void edge_enhance(rle_hdr *in_hdr, rle_pixel **row_b, rle_pixel **row_m, rle_pixel **row_t, rle_pixel **edge_row, int blend, int blend_divisor)
Definition: rledither.c:223
int scanargs(int argc, char **argv, const char *format,...)
Definition: scanargs.c:94
void rle_puteof(rle_hdr *the_hdr)
Definition: rle_putrow.c:474
#define RLE_RED
Definition: rle.h:62
void find_closest(rle_map **map, int nchan, int maplen, rle_pixel *pixel, strchr)
Definition: rledither.c:553
void rle_putrow(rows, int rowlen, rle_hdr *the_hdr)
Definition: rle_putrow.c:96
int xmax
Definition: rle.h:100
#define MALLOC_ERR
Definition: rlecomp.c:38
#define index
Definition: rle_config.h:96
static char rcs_ident[]
Definition: rledither.c:36
#define RLE_EOF
Definition: rle.h:74
void rle_addhist(argv, rle_hdr *in_hdr, rle_hdr *out_hdr)
Definition: rle_addhist.c:54
void shiftmap(rle_map **map, int nchan, int length, int bits)
Definition: rleldmap.c:413
#define RLE_CLR_BIT(glob, bit)
Definition: rle.h:124
int ncmap
Definition: rle.h:100
int ymax
Definition: rle.h:100
unsigned char rle_pixel
Definition: rle.h:56
void rle_put_setup(rle_hdr *the_hdr)
Definition: rle_putrow.c:453
const char * rle_putcom(char *value, rle_hdr *the_hdr) const
Definition: rle_putcom.c:82
int cmaplen
Definition: rle.h:100
int alpha
Definition: rle.h:100
void filemap(int tflag, char *mapfname, int nchan, int length, rle_map **amap)
Definition: rleldmap.c:606
rle_map ** allocmap(int nchan, int length, rle_map *cmap)
Definition: rleldmap.c:373
unsigned short rle_map
Definition: rle.h:57
rle_hdr * rle_hdr_init(rle_hdr *the_hdr)
Definition: rle_hdr.c:267
void fsdither(rle_hdr *in_hdr, rle_map **map, int maplen, short *row_bottom, short *row_top, rle_pixel **outrow)
Definition: rledither.c:280
FILE * rle_file
Definition: rle.h:114
int ncolors
Definition: rle.h:100
#define RLE_CHECK_ALLOC(pgm, ptr, name)
Definition: rle.h:86
void copymap(rle_map **inmap, int nchan, int length, rle_map **outmap)
Definition: rledither.c:540