[comp.windows.open-look] Halftone Filters for Sun Raster Files Wanted

leue@galen.crd.ge.com (Bill Leue) (06/20/91)

Does anyone know of a halftone filter which could be used to take
an 8-bit-deep sun Raster file and print it on an ordinary Apple
LaserWriter or other PS printer?  I would like to generate some
"lab notebook quality" prints of grayscale information which I am
rendering on an X canvas.  I would prefer to have a Floyd-Steinberg
Error Diffusion-type halftoning algorithm, but beggars can't be
choosers.  Source code would be preferred, since then I could 
incorporate the print snapshot capability directly into my X display
tool, but an executable filter which could be inserted into a
pipeline like "cat snapshot.rs | halftone | suntops | lpr" or
some such would be fine.

Thanks!
-Bill Leue
leue@crd.ge.com

falk@peregrine.Sun.COM (Ed Falk) (06/22/91)

In article <20764@crdgw1.crd.ge.com> leue@galen.crd.ge.com (Bill Leue) writes:
>Does anyone know of a halftone filter which could be used to take
>an 8-bit-deep sun Raster file and print it on an ordinary Apple
>LaserWriter or other PS printer?

Don't halftone it at all!  The laser printer can do a better job of
halftoning than any offline filter ever will.  There's half a dozen
programs out on the net (see the pbm and fbm packages for starters)
that will convert an 8-bit image to a (big!) postscript file.  Just
print that postscript file (takes about 20-40 minutes) and there you are.

		-ed falk, sun microsystems
		 sun!falk, falk@sun.com

In the future, somebody will quote Andy Warhol every 15 minutes.

steve@hanauma.stanford.edu (Steve Cole) (06/22/91)

In article <15643@exodus.Eng.Sun.COM> falk@peregrine.Sun.COM (Ed Falk) writes:
>In article <20764@crdgw1.crd.ge.com> leue@galen.crd.ge.com (Bill Leue) writes:
>>Does anyone know of a halftone filter which could be used to take
>>an 8-bit-deep sun Raster file and print it on an ordinary Apple
>>LaserWriter or other PS printer?
>
>Don't halftone it at all!  The laser printer can do a better job of
>halftoning than any offline filter ever will.  There's half a dozen
>programs out on the net (see the pbm and fbm packages for starters)
>that will convert an 8-bit image to a (big!) postscript file.  Just
>print that postscript file (takes about 20-40 minutes) and there you are.
>

Ed, you have missed a chance to plug your own company. A big 8 bit
raster that might take that long to come out on some laser printers
will come out in 2-3 minutes on the Sun SPARCprinter.

But generally speaking, halftoning it yourself is not such a bad idea.
If you do have a slow printer, then perhaps you can do the halftoning
yourself much faster than it can, and not wait 20 minutes. This is the 
principle behind the SPARCprinter; the workstation does the halftoning. 
After all, it has a better cpu than most laser printers.

Halftoning it yourself also opens up new possibilities. Bill Leue
asked about Floyd-Steinberg dithering in his original posting. I wrote
the following routine as part of the vplot graphics package, developed
at Stanford. (Aside: vplot is a device-independent graphics package that 
includes a library of routines for application programs to call, and filters 
that then can display the output on a wide variety of devices. It is
available for anonymous ftp from hanauma.stanford.edu) Anyway, this 
routine does the 8 bit to 1 bit halftoning conversion using one of
four methods - random dither, ordered dither, Floyd-Steinberg, and
a halftoning scheme similar to what a PostScript printer will do. The
latter two are the best methods. A good reference for such work:
Digital Halftoning by Robert Ulichney, MIT Press (hope I have that right).
Here is the code for anyone who is interested. There are some
vplot-isms, such as the fact that the program returns 8 bits/pixel,
but with the values 0 or 7. And you have to call the routine for
each line in the raster instead of just once. There are reasons for
such weirdnesses, but if you want to use the code I'm sure you can
deal with these things.

----------begin dither.c-----------
/*
 * Copyright 1987 the Board of Trustees of the Leland Stanford Junior
 * University. Official permission to use this software is included in
 * the documentation. It authorizes you to use this file for any
 * non-commercial purpose, provided that this copyright notice is not
 * removed and that any modifications made to this file are commented
 * and dated in the style of my example below.
 */

/*
 *
 *  source file:   ./filters/utilities/dither.c
 *
 * Joe Dellinger (SEP), June 11 1987
 *	Inserted this sample edit history entry.
 *	Please log any further modifications made to this file:
 * Steve Cole (SEP), September 1 1987
 *      Added method 4 (digital halftoning).
 * Joe Dellinger March 28 1988
 *	Move invras out of here and into greycorr, where it belonged
 *	in the first place.
 * Steve Cole (SEP), June 10 1988
 *      Replaced if blocks for each dithering method with a single switch.
 */

/*
 * this subroutine converts a single raster line to single bit
 * using one of the following algorithms:
 *
 * 1 random threshold
 * 2 256 element ordered dither (oriented at 0 degrees)
 * 3 Floyd-Steinberg minimized average error method
 * 4 32 element halftone (oriented at 45 degrees)
 *
 * Steve Cole, April 1987
 *
 */
#include <stdio.h>
#include "../include/err.h"
#include "../include/params.h"
#include "../include/extern.h"
static int      pix_on = 0, pix_off = 7;
char           *malloc ();
static float   *errline;
static int      ialloc;
static float    alpha = 0.4375;
static float    beta = 0.1875;
static float    gamma = 0.3125;
static float    delta = 0.0625;
static int      dith256[256] = {
	1, 128, 32, 160, 8, 136, 40, 168, 2, 130, 34, 162, 10, 138, 42, 170,
   192, 64, 224, 96, 200, 72, 232, 104, 194, 66, 226, 98, 202, 74, 234, 106,
     48, 176, 16, 144, 56, 184, 24, 152, 50, 178, 18, 146, 58, 186, 26, 154,
 240, 112, 208, 80, 248, 120, 216, 88, 242, 114, 210, 82, 250, 122, 218, 90,
       12, 140, 44, 172, 4, 132, 36, 164, 14, 142, 46, 174, 6, 134, 38, 166,
 204, 76, 236, 108, 196, 68, 228, 100, 206, 78, 238, 110, 198, 70, 230, 102,
     60, 188, 28, 156, 52, 180, 20, 148, 62, 190, 30, 158, 54, 182, 22, 150,
 252, 124, 220, 92, 244, 116, 212, 84, 254, 126, 222, 94, 246, 118, 214, 86,
	3, 131, 35, 163, 11, 139, 43, 171, 1, 129, 33, 161, 9, 137, 41, 169,
   195, 67, 227, 99, 203, 75, 235, 107, 193, 65, 225, 97, 201, 73, 233, 105,
     51, 179, 19, 147, 59, 187, 27, 155, 49, 177, 17, 145, 57, 185, 25, 153,
 243, 115, 211, 83, 251, 123, 219, 91, 241, 113, 209, 81, 249, 121, 217, 89,
       15, 143, 47, 175, 7, 135, 39, 167, 13, 141, 45, 173, 5, 133, 37, 165,
 207, 79, 239, 111, 199, 71, 231, 103, 205, 77, 237, 109, 197, 69, 229, 101,
     63, 191, 31, 159, 55, 183, 23, 151, 61, 189, 29, 157, 53, 181, 21, 149,
  254, 127, 223, 95, 247, 119, 215, 87, 253, 125, 221, 93, 245, 117, 213, 85
};
static int      halftone32[64] = {
				  92, 100, 124, 148, 164, 156, 132, 108,
				  28, 20, 76, 220, 228, 236, 180, 36,
				  4, 12, 84, 212, 252, 244, 172, 44,
				  52, 60, 116, 188, 204, 196, 140, 68,
				  164, 156, 132, 108, 92, 100, 124, 148,
				  228, 236, 180, 36, 28, 20, 76, 220,
				  252, 244, 172, 44, 4, 12, 84, 212,
				  204, 196, 140, 68, 52, 60, 116, 188
};


dithline (inpline, outline, npixels, linenum, imethod)
    unsigned char  *inpline, *outline;
    int             npixels, linenum, imethod;
{
int             greydata;
int             i1, ipoint, jpoint;
float           pixel, pixerr, nexterr;
int             irand;

    switch (imethod)
    {
/* Random Dither */
    case 1:
	for (i1 = 0; i1 < npixels; i1++)
	{
	    greydata = inpline[i1];
	    irand = (random () & 255);
	    if (greydata > irand)
	    {
		outline[i1] = pix_off;
	    }
	    else
	    {
		outline[i1] = pix_on;
	    }
	}
	break;

/* Ordered Dither */
    case 2:
	for (i1 = 0; i1 < npixels; i1++)
	{
	    greydata = inpline[i1];
	    ipoint = i1 % 16;
	    jpoint = linenum % 16;
	    ipoint = ipoint * 16 + jpoint;
	    if (greydata > dith256[ipoint])
	    {
		outline[i1] = pix_off;
	    }
	    else
	    {
		outline[i1] = pix_on;
	    }
	}
	break;

/* Floyd-Steinberg */
    case 3:
	if (ialloc != 1)
	{
	    if ((errline = (float *) malloc ((unsigned) npixels * sizeof (float))) == NULL)
	    {
		ERR (FATAL, name, "Can't allocate space for Floyd-Steinberg\n");
		return;
	    }
	    ialloc = 1;
	    for (i1 = 0; i1 < npixels; i1++)
	    {
		errline[i1] = 0.;
	    }
	}
	nexterr = errline[0];
	for (i1 = 0; i1 < npixels; i1++)
	{
	    pixel = inpline[i1];
	    pixel += nexterr;
	    if (pixel < 128)
	    {
		outline[i1] = pix_on;
		pixerr = pixel;
	    }
	    else
	    {
		outline[i1] = pix_off;
		pixerr = pixel - 255;
	    }
	    if (i1 < npixels)
	    {
		nexterr = errline[i1 + 1] + pixerr * alpha;
		errline[i1 + 1] = pixerr * delta;
	    }
	    if (i1 > 0)
	    {
		errline[i1 - 1] += pixerr * beta;
	    }
	    errline[i1] += pixerr * gamma;
	    if (i1 == 0)
		errline[i1] = pixerr * gamma;
	}
	break;

/* 32 element halftone at 45 degrees */
    case 4:
    default:
	for (i1 = 0; i1 < npixels; i1++)
	{
	    greydata = inpline[i1];
	    ipoint = i1 % 8;
	    jpoint = linenum % 8;
	    ipoint = ipoint * 8 + jpoint;
	    if (greydata > halftone32[ipoint])
	    {
		outline[i1] = pix_off;
	    }
	    else
	    {
		outline[i1] = pix_on;
	    }
	}
	break;
    }
}
----------end dither.c-----------
-----------------------------------------------------------------
Steve Cole  (steve@hanauma.stanford.edu, apple!hanauma!steve)
Department of Geophysics, Stanford University, Stanford, CA 94305