[net.sources] IMAGEN Halftone for Raytrace2.0

geof@imagen.UUCP (Geoffrey Cooper) (08/14/86)

Here are better programs for converting the raytracing stuff to
black and white levels suitable for printing on a laser printer.
The following two programs produce a halftone image of the ray
tracing, formatted as a bitmap for sending to IMAGEN printers.
The code is based on the stuff that Steve Hawley kindly posted
to the net, but I found the masks Steve used for his MAC didn't
look very good at higher resolutions.  I developed a mask of
my own that works reasonably well (although DDL's is better, but
I can't post that).

The programs work with raytrace version 2.0 -- they take the size
of the pixel map from the input file.  If you don't have version2.0
running, you can fix this by the following hack to the output:
	echo "451 451" > foo.tmp
	cat foo.tmp data.dis > data2-0.dis
	rayht data2-0.dis > foo.imp

The first program, rayhtimp.c, generates an IMPRESS bitmap.  The
second program generates a raster scan suitable for the IMAGEN
BITARRAY emulator that exists on ImageServer products running
software at or beyond V2.2.  The bitarray-producing code could
easily be modified for other file formats.  (For those Imagen
owners who don't know about it, bitarray is a very limited but
fast emulator that allows bitmaps which are aligned to multiples
of 32 pixels to be transmitted to the printer without the overhead
of impress and without needing to translate to the more cumbersome
impress format for bitmaps.  Bitarray is most useful on printers
with communications options that operate faster than 70 kilobits/s
(TCP, XNS, parallel).  Otherwise, impress is about as fast, because
communications becomes the bottleneck.  I am informed that the
ImageStation products (whose parallel is slow, anyway) don't
support bitarray.)

-----------------------------------------------
#!/bin/sh
: "This is a shell archive, meaning:                              "
: "1. Remove everything above the #! /bin/sh line.                "
: "2. Save the resulting test in a file.                          "
: "3. Execute the file with /bin/sh (not csh) to create the files:"
: "	rayhtimp.c"
: "	rayht.c"
: "This archive created:  Wed Aug 13 16:32:19 PDT 1986 "
echo file: rayhtimp.c
sed 's/^X//' >rayhtimp.c << 'END-of-rayhtimp.c'
X/*
X * traceconv.c - a program to convert 256 level gray files to halftone
X *               pictures.  Produces output in format for printing on
X *               IMAGEN printers using language IMPRESS.
X * Written by: Steve Hawley   Aztec C V1.06H
X * Modified to produce language BITARRAY for imagen printers by
X *   Geoffrey Cooper at IMAGEN (August 12, 1986).  The main procedure
X *   is basically new.  The dither function is borrowed from Steve's
X *   code.
X */
X#include <stdio.h>
X
X/* Magnification - must be a power of two (fix it if you don't like it) */
X/* (and send me the fixes :-).  MAGSHFT is log base 2 of MAG */
X#define MAG 4
X#define MAGSHFT 2
Xunsigned char line[1000];
X
X/* 316 = 8.4 inches at 300 dpi (long must be 32 bits) */
Xunsigned long swath[32][79];
X
Xmain (argc, argv)
X    char **argv;
X{
X    int height, width, wremainder, wpatches, hpatches, pwidth;
X    register int y1, x, y, x1, mx, my;
X    register int level;
X    FILE *fopen(), *fp;
X    register unsigned long b;
X    
X    int perc25, percDone, percD;
X
X    /* put up a new window */
X    if ( (fp = fopen(argv[1], "r")) == NULL ) {
X            perror("on file open");
X            exit(1);
X    }
X    /* Get dimensions */
X    fscanf(fp, "%d %d\n", &width, &height);
X    wremainder = width & 31;
X    pwidth =  width & ~31;
X    width  = (width & ~31) << MAGSHFT;
X    height = (height & ~31) << MAGSHFT;
X    wpatches = width >> 5;
X    hpatches = height >> 5;
X
X    perc25 = height / 4;
X    percD = 25;
X    percDone = perc25;
X
X    fprintf(stdout, "@document(language impress, file \"%s\")", argv[1]);
X
X    putc(213, stdout);    /* PAGE message- sets up parameters for Impress pg. */
X    putc(135, stdout);    /* SET-ABS-H: horiz. offset in points-req. 2 bytes */
X    putc(00, stdout) ;        /*offset currently set to 250/300 of inch */
X    putc(250, stdout);
X    putc(137, stdout);    /* SET-ABS-V: horiz. offset in points-req. 2 bytes */
X    putc(00, stdout) ;
X    putc(250, stdout);    /* offset set to 250/300 of an inch. */
X    putc(235, stdout);    /* Impress code for BITMAP */
X    putc(07, stdout) ;    /* operation-type = 'OR' mapping of bits */
X    putc(wpatches, stdout);    /* no. of patches horizontally */
X    putc(hpatches, stdout);    /* no. of vertical lines of HPATCHES */
X
X    b = 0;
X    for ( y = 0; y < height; y += 32 ) {
X        /* compute a swath */
X        for ( y1 = 0; y1 < 32; y1 += MAG ) {
X            /* read in a strip */
X            for ( x = 0; x < pwidth; x++ )
X                line[x] = getc(fp);
X            for ( x = 0; x < wremainder; x++ ) getc(fp);
X
X            /* compute a strip */
X            for ( x = 0; x < width; x += 32 ) {
X                /* replicate the strip in the y direction */
X                for ( my = 0; my < MAG; my++ ) {
X                    /* compute a longword */
X                    b = 0;
X                    for ( x1 = 0; x1 < 32; x1 += MAG ) {
X                        level = line[(x+x1) >> MAGSHFT];
X                        for ( mx = 0; mx < MAG; mx++ )
X                            b = (b << 1) | dither(x+x1+mx, y+y1+my, level);
X                    }
X                    /* save the longword */
X                    swath[y1+my][x >> 5] = b;
X                }
X            }
X        }
X        if ( y > percDone ) {
X            fprintf(stderr, "%3d%% ...\n", percD);
X            percDone += perc25;
X            percD += 25;
X        }
X
X        /* output the swath */
X        for ( x1 = 0; x1 < wpatches; x1++ ) {
X            for ( y1 = 0; y1 < 32; y1++ ) {
X                b = swath[y1][x1];
X                putc(b >> 24, stdout);
X                putc(b >> 16, stdout);
X                putc(b >>  8, stdout);
X                putc(b      , stdout);
X            }
X        }
X    }
X    putc(219, stdout);    /* ENDPAGE message for Impress  */
X    putc(255, stdout);    /* EOF message for Impress  */
X    fprintf(stderr, "100% ... Done.\n");
X}
X
X#define MAXLEV
X#ifdef MAXLEV
X/*
X * The following dithering function creates little boxes whose
X * size is proportional to the level and x/y position of the
X * point, quantized to an 8X8 grid.  An exponential scale of gray levels
X * is used, corresponding closer to the human eye's luminance
X * response than a linear scale.
X *
X * The result is a little light; probably it can be improved by using
X * a less steep exponential than I did (I mapped the curve of exp(x)
X * from -5 to 0 -- the curve of exp(x/1.5) looks a bit better).
X * 
X * If you change the map, don't forget to make sure that 0 and 255 are
X * left out.  That way the whites and blacks can be solid.
X *
X * The dmask is the cascade of two functions ( grid * expluminance ).
X * the cascading has been done manually, to save (miniscule) time.
X * the two functions are reproduced below:
X *
X *    static unsigned char expluminance[64] = {
X *      254, 235, 217, 201, 186, 172, 159, 147, 
X *      136, 125, 116, 107,  99,  91,  84,  78, 
X *       72,  66,  61,  57,  52,  48,  44,  41, 
X *       38,  35,  32,  30,  27,  25,  23,  21, 
X *       20,  18,  16,  15,  14,  13,  12,  11, 
X *       10,   9,   8,   7,   7,   6,   6,   5, 
X *        5,   4,   4,   3,   3,   3,   2,   2, 
X *        2,   1,   1,   1,   1,   1,   1,   1
X *     };
X *     static unsigned char grid[64] = {
X *       63,  62,  61,  60,  59,  58,  57,  56, 
X *       36,  35,  34,  33,  32,  31,  30,  55, 
X *       37,  16,  15,  14,  13,  12,  29,  54, 
X *       38,  17,   4,   3,   2,  11,  28,  53, 
X *       39,  18,   5,   0,   1,  10,  27,  52, 
X *       40,  19,   6,   7,   8,   9,  26,  51, 
X *       41,  20,  21,  22,  23,  24,  25,  50, 
X *       42,  43,  44,  45,  46,  47,  48,  49
X *     };
X */
Xdither(x, y, level)
X    int x, y, level;
X{
X    int dlev;   /* dither level */
X    static unsigned char dmask[64] = {
X       1,   1,   1,   1,   1,   1,   1,   2, 
X      14,  15,  16,  18,  20,  21,  23,   2, 
X      13,  72,  78,  84,  91,  99,  25,   2, 
X      12,  66, 186, 201, 217, 107,  27,   3, 
X      11,  61, 172, 254, 235, 116,  30,   3, 
X      10,  57, 159, 147, 136, 125,  32,   3, 
X       9,  52,  48,  44,  41,  38,  35,   4, 
X       8,   7,   7,   6,   6,   5,   5,   4
X    };
X
X    /* apply an 8x8 grid: */
X    dlev = ((y & 7) << 3) + (x & 7);
X
X    /* apply dithering function and compare */
X    return ( level < dmask[dlev] );
X}
X#endif
X
X/*
X * The following is Steve Hawley's mask.  Looks to me like a "sinx/x" type
X * function.  It is pleasing to the eye, but comes out rather dark on
X * the printer.
X */
X/*#define ORIG*/
X#ifdef ORIG
Xdither (x,y,level) /* dithering function */
Xregister int x, y, level;
X{
X     /* 8 x 8 dithering matrix */
X    static int matrix[8][8] = {
X        {  0, 128,  32, 160,   8, 136,  40, 168}, 
X        {192,  64, 224,  96, 200,  72, 232, 104}, 
X        { 48, 176,  16, 144,  56, 184,  24, 152}, 
X        {240, 112, 208,  80, 248, 120, 216,  88}, 
X        { 12, 140,  44, 174,   4, 132,  36, 164}, 
X        {204,  76, 236, 108, 196,  68, 228, 100}, 
X        { 60, 188,  28, 156,  52, 180,  20, 148}, 
X        {252, 124, 210,  92, 244, 116, 212,  84}
X    };
X
X/* dithering by area access to matrix by using
X    the MOD function on the x and y coordinates */
X    return(level < matrix[x & 7][y & 7]);
X}
X#endif
END-of-rayhtimp.c
echo file: rayht.c
sed 's/^X//' >rayht.c << 'END-of-rayht.c'
X/*
X * traceconv.c - a program to convert 256 level gray files to halftone
X *               pictures.  Produces output in format for printing on
X *               IMAGEN printers using language BITARRAY (compatible with
X *               imagen IMAGESERVER printers running V2.2 and later sw).
X * Written by: Steve Hawley   Aztec C V1.06H
X * Modified to produce language BITARRAY for imagen printers by
X *   Geoffrey Cooper at IMAGEN (August 12, 1986).  The main procedure
X *   is basically new.  The dither function is borrowed from Steve's
X *   code.
X */
X#include <stdio.h>
X
X/* Magnification - must be a power of two (fix it if you don't like it) */
X/* (and send me the fixes :-).  MAGSHFT is log base 2 of MAG */
X#define MAG 4
X#define MAGSHFT 2
Xunsigned char line[1000];
X
Xmain (argc, argv)
X    char **argv;
X{
X    int height, width, wremainder;
X    register int x, y, x1, mx, my;
X    register int level;
X    FILE *fopen(), *fp;
X    register unsigned long b;
X    
X    /* put up a new window */
X    if ( (fp = fopen(argv[1], "r")) == NULL ) {
X            perror("on file open");
X            exit(1);
X    }
X    /* Get dimensions */
X    fscanf(fp, "%d %d\n", &width, &height);
X    wremainder = width & 31;
X    width  &= ~31;
X    height &= ~31;
X
X    fprintf(stdout, "@document(language bitarray, width %d, height %d, leftoffset 256, topoffset 256, File \"%s\")",
X                    width<<MAGSHFT, height<<MAGSHFT, argv[1]);
X    b = 0;
X    for ( y = 0; y < height; y++ ) {
X        for ( x = 0; x < width; x++ )
X            line[x] = getc(fp);
X        for ( x = 0; x < wremainder; x++ ) getc(fp);
X        for ( my = 0; my < MAG; my++ ) {
X            for ( x = 0; x < width; x += (32>>MAGSHFT) ) {
X                b = 0;
X                for ( x1 = 0; x1 < (32>>MAGSHFT); x1++ ) {
X                    for ( mx = 0; mx < MAG; mx++ ) {
X                        level = line[x+x1];
X                        b = (b << 1) | dither(((x+x1)<<MAGSHFT)+mx, (y<<MAGSHFT)+my, level);
X                    }
X                }
X                putc(b>>24, stdout);
X                putc(b>>16, stdout);
X                putc(b>>8 , stdout);
X                putc(b    , stdout);
X            }
X        }
X    }
X}
X
X#define MAXLEV
X#ifdef MAXLEV
X/*
X * The following dithering function creates little boxes whose
X * size is proportional to the level and x/y position of the
X * point, quantized to an 8X8 grid.  An exponential scale of gray levels
X * is used, corresponding closer to the human eye's luminance
X * response than a linear scale.
X *
X * The result is a little light; probably it can be improved by using
X * a less steep exponential than I did (I mapped the curve of exp(x)
X * from -5 to 0 -- the curve of exp(x/1.5) looks a bit better).
X * 
X * If you change the map, don't forget to make sure that 0 and 255 are
X * left out.  That way the whites and blacks can be solid.
X *
X * The dmask is the cascade of two functions ( grid * expluminance ).
X * the cascading has been done manually, to save (miniscule) time.
X * the two functions are reproduced below:
X *
X *    static unsigned char expluminance[64] = {
X *      254, 235, 217, 201, 186, 172, 159, 147, 
X *      136, 125, 116, 107,  99,  91,  84,  78, 
X *       72,  66,  61,  57,  52,  48,  44,  41, 
X *       38,  35,  32,  30,  27,  25,  23,  21, 
X *       20,  18,  16,  15,  14,  13,  12,  11, 
X *       10,   9,   8,   7,   7,   6,   6,   5, 
X *        5,   4,   4,   3,   3,   3,   2,   2, 
X *        2,   1,   1,   1,   1,   1,   1,   1
X *     };
X *     static unsigned char grid[64] = {
X *       63,  62,  61,  60,  59,  58,  57,  56, 
X *       36,  35,  34,  33,  32,  31,  30,  55, 
X *       37,  16,  15,  14,  13,  12,  29,  54, 
X *       38,  17,   4,   3,   2,  11,  28,  53, 
X *       39,  18,   5,   0,   1,  10,  27,  52, 
X *       40,  19,   6,   7,   8,   9,  26,  51, 
X *       41,  20,  21,  22,  23,  24,  25,  50, 
X *       42,  43,  44,  45,  46,  47,  48,  49
X *     };
X */
Xdither(x, y, level)
X    int x, y, level;
X{
X    int dlev;   /* dither level */
X    static unsigned char dmask[64] = {
X       1,   1,   1,   1,   1,   1,   1,   2, 
X      14,  15,  16,  18,  20,  21,  23,   2, 
X      13,  72,  78,  84,  91,  99,  25,   2, 
X      12,  66, 186, 201, 217, 107,  27,   3, 
X      11,  61, 172, 254, 235, 116,  30,   3, 
X      10,  57, 159, 147, 136, 125,  32,   3, 
X       9,  52,  48,  44,  41,  38,  35,   4, 
X       8,   7,   7,   6,   6,   5,   5,   4
X    };
X
X    /* apply an 8x8 grid: */
X    dlev = ((y & 7) << 3) + (x & 7);
X
X    /* apply dithering function and compare */
X    return ( level < dmask[dlev] );
X}
X#endif
X
X/*
X * The following is Steve Hawley's mask.  Looks to me like a "sinx/x" type
X * function.  It is pleasing to the eye, but comes out rather dark on
X * the printer.
X */
X/*#define ORIG*/
X#ifdef ORIG
Xdither (x,y,level) /* dithering function */
Xregister int x, y, level;
X{
X     /* 8 x 8 dithering matrix */
X    static int matrix[8][8] = {
X        {  0, 128,  32, 160,   8, 136,  40, 168}, 
X        {192,  64, 224,  96, 200,  72, 232, 104}, 
X        { 48, 176,  16, 144,  56, 184,  24, 152}, 
X        {240, 112, 208,  80, 248, 120, 216,  88}, 
X        { 12, 140,  44, 174,   4, 132,  36, 164}, 
X        {204,  76, 236, 108, 196,  68, 228, 100}, 
X        { 60, 188,  28, 156,  52, 180,  20, 148}, 
X        {252, 124, 210,  92, 244, 116, 212,  84}
X    };
X
X/* dithering by area access to matrix by using
X    the MOD function on the x and y coordinates */
X    return(level < matrix[x & 7][y & 7]);
X}
X#endif
END-of-rayht.c
exit