/*
* dither -- use a reduced set of grey values to represent an image
*
* Copyright (C) 1988 by Dale Schumacher.
*
* 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.
*
*
* Notes:
*
* [1] Floyd-Steinberg dither:
* I should point out that the actual fractions we used were, assuming
* you are at X, moving left to right:
*
* X 7/16
* 3/16 5/16 1/16
*
* Note that the error goes to four neighbors, not three. I think this
* will probably do better (at least for black and white) than the
* 3/8-3/8-1/4 distribution, at the cost of greater processing. I have
* seen the 3/8-3/8-1/4 distribution described as "our" algorithm before,
* but I have no idea who the credit really belongs to.
* Also, I should add that if you do zig-zag scanning (see my immediately
* previous message), it is sufficient (but not quite as good) to send
* half the error one pixel ahead (e.g. to the right on lines you scan
* left to right), and half one pixel straight down. Again, this is for
* black and white; I've not tried it with color.
* --
* Lou Steinberg
*/
static char _Program[] = "dither";
static char _Version[] = "1.3";
static char _Copyright[] = "Copyright 1988 by Dale Schumacher";
#include
#include
#include "pxm.h"
#define DEBUG(x) if(0)/* x */
#define range 255 /* maximum pixel output value */
int levels = 2; /* number of output levels */
banner()
{
printf("%s v%s -- %s\n", _Program, _Version, _Copyright);
}
usage()
{
fprintf(stderr,
"usage: %s [-n levels] inpxm outpxm\n",
_Program);
exit(1);
}
main(argc, argv)
int argc;
char *argv[];
{
register PX_DEF *ipx, *opx;
FILE *f;
register int c;
register char *p;
extern int optind;
extern char *optarg;
while((c = getopt(argc, argv, "n:V")) != EOF)
{
switch(c)
{
case 'n':
levels = atoi(optarg);
break;
case 'V':
banner();
exit(0);
case '?':
default:
usage();
}
}
if((argc - optind) < 2)
usage();
else
{
ipx = px_ropen(argv[optind++]);
opx = px_wopen(argv[optind++],
(PXT_PIX | PXT_BIN),
ipx->px_width, ipx->px_height, 8, NULL);
dither(ipx, opx);
px_close(ipx);
px_close(opx);
}
exit(0);
}
dither(ipx, opx)
PX_DEF *ipx, *opx;
{
unsigned int xsize, ysize;
unsigned int y, x;
register int xslop, *yslop, dslop;
register int i, j, k, t, q;
register PX_BYTE *ibuf, *obuf;
xsize = ipx->px_width;
ysize = ipx->px_height;
ibuf = px_rowalloc(xsize, ipx->px_psize);
obuf = px_rowalloc(xsize, opx->px_psize);
yslop = (int *) px_alloc(xsize * sizeof(int));
t = ((range + 1) * 2) / levels; /* threshold factor */
q = range / (levels - 1); /* quantization factor */
j = (9 * range) / 32;
for(x=0; x range) /* quick hack to fix overflow */
j = range; /* which shouldn't happen :-) */
px_wgrey(opx, obuf, x, j);
i = i - j;
k = (i >> 4); /* (i / 16) */
xslop = 7 * k;
yslop[x] = (5 * k) + dslop;
if(x > 0)
yslop[x-1] += 3 * k;
dslop = i - (15 * k);
}
px_wrow(opx, obuf);
}
}
Discuss this article in the forums
Date this article was posted to GameDev.net: 7/16/1999
(Note that this date does not necessarily correspond to the date the article was written)
See Also:
Dithering
© 1999-2011 Gamedev.net. All rights reserved. Terms of Use Privacy Policy
Comments? Questions? Feedback? Click here!
|