[alt.sources] beta version of squig.c -- graphics hack for Suns

pokey@well.UUCP (Jef Poskanzer) (06/23/89)

If you liked the spf3 program I recently posted in comp.sources.sun,
then you'll love this.  However, I need a little help from the net
before I can officially release it.  If you have a color Sun with
other than a cg2 frame buffer, see below.

The other color frame buffer types are cg1, cg3, cg4, cg5, and gp1.
I don't know anything about these; no doubt some of them can't handle
8-bit color mode and so can't run this program at all.
---
Jef

    Jef Poskanzer  pokey@well.sf.ca.us  {ucbvax, apple, hplabs}!well!pokey
 "Every new technology carries with it an opportunity to invent a new crime."
                             -- Laurence Urgenson

/*
** squig.c - draw a squiggley tubular pattern
**
** Version 0.8, 23jun89.
**
** Compile: cc -O squig.c -lpixrect -o squig
**
** Run:
**   squig
** It should work on any frame buffer that supports 8-bit color, but it will
** be unacceptably slow on any frame buffer except the cg2.  See note below.
**
** Comments to:
**   pokey@well.sf.ca.us
**   {ucbvax, lll-crg, sun!pacbell, apple, hplabs}!well!pokey
**
** Important Note:
**   Because Sun's pr_polypoint routine doesn't take a list of colors as well
**   as a list of points, I can't use it here.  The only portable alternative
**   is to call pr_put for each and every point, which is very slow.  The
**   non-portable alternative, accessing the frame buffer directly, is nice
**   and fast, but must be implemented separately for each different type of
**   frame buffer.  Currently, only the cg2 has been done.  If you have a
**   different frame buffer and you would like to get squig going fast, contact
**   me and we'll work out the necessary code.
**
** Copyright (C) 1989 by Jef Poskanzer.
**
** 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.
*/

#define MIN_CIRCLE_RATIO 40
#define MAX_CIRCLE_RATIO 20

#define SIZE_CYCLES 100

#define COLOR_CYCLES 10
#define MAX_D_COLOR 3

#define MIN_COLOR_OFFSET_CYCLES 3
#define MAX_COLOR_OFFSET_CYCLES 15

#include <stdio.h>
#include <sys/file.h>
#include <signal.h>
#include <sys/types.h>
#include <pixrect/pixrect_hs.h>
#include <pixrect/memreg.h>
#include <pixrect/cg2reg.h>

#define max(a,b) ((a) > (b) ? (a) : (b))
#define min(a,b) ((a) < (b) ? (a) : (b))

long random();

Pixrect *disp_pr;
int width, height;
u_char *fb_base;
int fb_linebytes = 0;

u_char oldred[256], oldgreen[256], oldblue[256];
int oldplanes;

u_char red[254], green[254], blue[254];
int cred[3], cgreen[3], cblue[3];
int dred[3], dgreen[3], dblue[3];

struct circle_point {
    int x, y;
    u_char color;
    int offset;
    };
struct circle_point **circles;
int *circle_counts;

int min_circle_radius, max_circle_radius, circle_radii;
int circle_index, circle_radius, circle_number, color_offset;
int color_offset_cycles;
int d_circle_radius, d_color_offset;

void
count_circle_drawproc( x, y )
int x, y;
    {
    circle_counts[circle_radius]++;
    }

void
save_circle_drawproc( x, y )
int x, y;
    {
    circles[circle_radius][circle_index].x = x;
    circles[circle_radius][circle_index].y = y;
    circles[circle_radius][circle_index].color =
	254 * circle_index / circle_counts[circle_radius] + 2;
    if ( fb_linebytes != 0 )
	circles[circle_radius][circle_index].offset = y * fb_linebytes + x;
    circle_index++;
    }

void
rainbow_circle_drawproc( x, y )
int x, y;
    {
    register long r;
    register int newcolormap = 0;

    circle_number++;
    r = random();

    /* Adjust radius. */
    if ( circle_number % SIZE_CYCLES == 0 )
	{
	register int t;

	/* First draw with new position and old size, to avoid gaps. */
	draw_rainbow_circle( x, y );

	if ( ( r % 46301 ) % ( circle_radii / 2 ) == 0 )
	    d_circle_radius = random() % 3 - 1;
	while ( (t = circle_radius + d_circle_radius ) < min_circle_radius ||
	        t > max_circle_radius )
	    d_circle_radius = random() % 3 - 1;

	circle_radius = t;
	}

    /* Adjust color cycles and offsets. */
    if ( circle_number % MAX_COLOR_OFFSET_CYCLES == 0 )
	{
	if ( ( r % 46307 ) % ( circle_radii / 2 ) == 0 )
	    {
	    register int t;

	    t = random() % 2 * 2 - 1;
	    if ( color_offset_cycles == MAX_COLOR_OFFSET_CYCLES )
		if ( t > 0 )
		    d_color_offset = -d_color_offset;
		else
		    color_offset_cycles += t;
	    else if ( color_offset_cycles == MIN_COLOR_OFFSET_CYCLES )
		if ( t < 0 )
		    ;
		else
		    color_offset_cycles += t;
	    else
		color_offset_cycles += t;
	    }
	}
    if ( circle_number % color_offset_cycles == 0 )
	{
	color_offset += d_color_offset;
	if ( color_offset < 0 )
	    color_offset += 254;
	/* Got to be careful about staying positive, since % is NOT modulus. */
	newcolormap = 1;
	}

    /* Adjust colors. */
    if ( circle_number % COLOR_CYCLES == 0 )
	{
	register int i, t;

	for ( i = 0; i < 3; i++ )
	    {
	    for ( ; ; )
		{
		t = cred[i] + dred[i];
		if ( t >= 0 && t < 256 ) break;
		dred[i] = random() % ( MAX_D_COLOR * 2 ) - MAX_D_COLOR;
		if ( dred[i] <= 0 ) dred[i]--;
		}
	    cred[i] = t;
	    for ( ; ; )
		{
		t = cgreen[i] + dgreen[i];
		if ( t >= 0 && t < 256 ) break;
		dgreen[i] = random() % ( MAX_D_COLOR * 2 ) - MAX_D_COLOR;
		if ( dgreen[i] <= 0 ) dgreen[i]--;
		}
	    cgreen[i] = t;
	    for ( ; ; )
		{
		t = cblue[i] + dblue[i];
		if ( t >= 0 && t < 256 ) break;
		dblue[i] = random() % ( MAX_D_COLOR * 2 ) - MAX_D_COLOR;
		if ( dblue[i] <= 0 ) dblue[i]--;
		}
	    cblue[i] = t;
	    }

	for ( i = 0; i < 254; i++ )
	    if ( i < 85 )
		{
		red[i] =
		    cred[0] + ( cred[1] - cred[0] ) * ( i / 85.0 );
		blue[i] =
		    cblue[0] + ( cblue[1] - cblue[0] ) * ( i / 85.0 );
		green[i] =
		    cgreen[0] + ( cgreen[1] - cgreen[0] ) * ( i / 85.0 );
		}
	    else if ( i < 170 )
		{
		red[i] =
		    cred[1] + ( cred[2] - cred[1] ) * ( (i-85) / 85.0 );
		blue[i] =
		    cblue[1] + ( cblue[2] - cblue[1] ) * ( (i-85) / 85.0 );
		green[i] =
		    cgreen[1] + ( cgreen[2] - cgreen[1] ) * ( (i-85) / 85.0 );
		}
	    else
		{
		red[i] =
		    cred[2] + ( cred[0] - cred[2] ) * ( (i-170) / 84.0 );
		blue[i] =
		    cblue[2] + ( cblue[0] - cblue[2] ) * ( (i-170) / 84.0 );
		green[i] =
		    cgreen[2] + ( cgreen[0] - cgreen[2] ) * ( (i-170) / 84.0 );
		}

	newcolormap = 1;
	}

    /* Store new colors. */
    if ( newcolormap )
	putcolormap();

    /* And draw circle. */
    draw_rainbow_circle( x, y );
    }

draw_rainbow_circle( x, y )
int x, y;
    {
    register int i;
    register struct circle_point *cp;

    if ( fb_linebytes != 0 )
	{
	register u_char *center;

	center = fb_base + y * fb_linebytes + x;
	for ( i = circle_counts[circle_radius] + 1, cp = circles[circle_radius];
	      --i; cp++ )
	    *( center + cp->offset ) = cp->color;
	}
    else
	{
	/* Fall back on slow but portable method. */
	for ( i = circle_counts[circle_radius] + 1, cp = circles[circle_radius];
	      --i; cp++ )
	    pr_put( disp_pr, x + cp->x, y + cp->y, cp->color );
	}
    }

main( argc, argv )
int argc;
char *argv[];
    {
    int i;
    int thiscx, thiscy, nextcx, nextcy, prevex, prevey, nextex, nextey;
#define MAXGROUPS 10
    char groups[MAXGROUPS];
    Pixrect *my_pr_open();
    int terminate();
    char *my_malloc();
    char *usage = "usage:  %s\n";

    if ( argc != 1 )
	{
	(void) fprintf( stderr, usage, argv[0] );
	exit( 1 );
	}

    if ( (disp_pr = my_pr_open( "/dev/cgfive0" )) == (Pixrect *) 0 )
      if ( (disp_pr = my_pr_open( "/dev/cgthree0" )) == (Pixrect *) 0 )
	if ( (disp_pr = my_pr_open( "/dev/gpone0a" )) == (Pixrect *) 0 )
	  if ( (disp_pr = my_pr_open( "/dev/cgtwo0" )) == (Pixrect *) 0 )
	    if ( (disp_pr = my_pr_open( "/dev/cgfour0" )) == (Pixrect *) 0 )
	      if ( (disp_pr = my_pr_open( "/dev/cgone0" )) == (Pixrect *) 0 )
	        if ( (disp_pr = my_pr_open( "/dev/fb" )) == (Pixrect *) 0 )
	        {
	        (void) fprintf(
		    stderr, "%s: error opening display\n", argv[0] );
	        exit( 1 );
	        }

    srandom( (int) time( 0 ) );

    /* Save old state. */
    pr_getcolormap( disp_pr, 0, 256, oldred, oldblue, oldgreen );
    oldplanes = pr_get_plane_group( disp_pr );
    (void) signal( SIGHUP, terminate );
    (void) signal( SIGINT, terminate );
    (void) signal( SIGTERM, terminate );

    /* Set up colormap. */
    (void) pr_available_plane_groups( disp_pr, MAXGROUPS, groups );
    if ( ! groups[PIXPG_8BIT_COLOR] )
	{
	(void) fprintf(
	    stderr, "%s: display must implement 8-bit color\n", argv[0] );
	exit( 1 );
	}
    pr_set_plane_group( disp_pr, PIXPG_8BIT_COLOR );
    for ( i = 0; i < 254; i++ )
	red[i] = green[i] = blue[i] = 0;
    for ( i = 0; i < 3; i++ )
	{
	cred[i] = cgreen[i] = cblue[i] = 0;
	dred[i] = random() % ( MAX_D_COLOR - 1 ) + 1;
	dgreen[i] = random() % ( MAX_D_COLOR - 1 ) + 1;
	dblue[i] = random() % ( MAX_D_COLOR - 1 ) + 1;
	}
    color_offset = 0;
    color_offset_cycles = MAX_COLOR_OFFSET_CYCLES;
    d_color_offset = 1;
    putcolormap();

    width = disp_pr->pr_size.x;
    height = disp_pr->pr_size.y;
    if ( disp_pr->pr_ops->pro_put == cg2_put )
	{
	struct cg2pr *cgd = (struct cg2pr *) disp_pr->pr_data;
	struct cg2fb *fb = (struct cg2fb *) cgd->cgpr_va;

	fb_base = cg2_roppixaddr( fb, 0, 0 );
	fb_linebytes = cg2_roppixaddr( fb, 0, 1 ) - fb_base;
	}

    /* Clear to black and sign the corner. */
    pr_rop( disp_pr, 0, 0, width, height, PIX_SRC | PIX_COLOR(1), 0, 0, 0 );
    signit( disp_pr );

    /* Measure circles. */
    min_circle_radius = ( width + height ) / 2 / MIN_CIRCLE_RATIO;
    max_circle_radius = ( width + height ) / 2 / MAX_CIRCLE_RATIO;
    circle_radii = max_circle_radius - min_circle_radius + 1;
    circle_counts = (int *) my_malloc(
	( max_circle_radius + 1 ) * sizeof(int) );
    circles = (struct circle_point **) my_malloc(
	( max_circle_radius + 1 ) * sizeof(struct circle_point *) );
    for ( circle_radius = min_circle_radius;
	  circle_radius <= max_circle_radius;
	  circle_radius++ )
	{
	circle_counts[circle_radius] = 0;
	circle( 0, 0, circle_radius, count_circle_drawproc );
	circles[circle_radius] = (struct circle_point *) my_malloc(
	    circle_counts[circle_radius] * sizeof(struct circle_point) );
	circle_index = 0;
	circle( 0, 0, circle_radius, save_circle_drawproc );
	}

    /* Initialize spline points. */
    thiscx = random() % ( width - 2 * max_circle_radius ) + max_circle_radius;
    thiscy = random() % ( height - 2 * max_circle_radius ) + max_circle_radius;
    nextcx = random() % ( width - 2 * max_circle_radius ) + max_circle_radius;
    nextcy = random() % ( height - 2 * max_circle_radius ) + max_circle_radius;
    nextex = ( nextcx + thiscx ) / 2;
    nextey = ( nextcy + thiscy ) / 2;

    circle_radius = min_circle_radius;
    d_circle_radius = 1;
    circle_number = 0;

    /* Poke the frame buffer once using the "approved" method, to get the
    ** mode bits set correctly. */
    pr_put( disp_pr, 0, 0, 1 );

    /* Main loop. */
    for ( ; ; )
	{
	thiscx = nextcx;
	thiscy = nextcy;
	nextcx =
	    random() % ( width - 2 * max_circle_radius ) + max_circle_radius;
	nextcy =
	    random() % ( height - 2 * max_circle_radius ) + max_circle_radius;
	prevex = nextex;
	prevey = nextey;
	nextex = ( nextcx + thiscx ) / 2;
	nextey = ( nextcy + thiscy ) / 2;
	spline3(
	    prevex, prevey, thiscx, thiscy, nextex, nextey,
	    rainbow_circle_drawproc );
	}
    }

int
terminate( sig, code, scp )
int sig, code;
struct sigcontext *scp;
    {
    pr_putcolormap( disp_pr, 0, 256, oldred, oldgreen, oldblue );
    pr_set_plane_group( disp_pr, oldplanes );
    pr_rop(
	disp_pr, 0, 0, disp_pr->pr_size.x, disp_pr->pr_size.y,
	PIX_CLR, 0, 0, 0 );
    exit( 0 );
    }

Pixrect *
my_pr_open( fb )
char *fb;
    {
    int fd;

    /* Test with open first, to avoid stupid error messages from pr_open(). */
    if ( (fd = open(fb, O_RDWR)) == -1 )
	return (Pixrect *) 0;
    (void) close( fd );
    return pr_open( fb );
    }

char *
my_malloc( size )
unsigned size;
    {
    char *p, *malloc();

    p = malloc( size );
    if ( p == (char *) 0 )
	{
	(void) fprintf( stderr, "out of memory\n" );
	exit( 1 );
	}
    return p;
    }

putcolormap( )
    {
    register int i;
    u_char rred[256], rgreen[256], rblue[256];

    rred[0] = rgreen[0] = rblue[0] = 255;
    rred[1] = rgreen[1] = rblue[1] = 0;
    for ( i = 0; i < 254; i++ )
	{
	rred[i + 2] = red[( i + color_offset ) % 254];
	rgreen[i + 2] = green[( i + color_offset ) % 254];
	rblue[i + 2] = blue[( i + color_offset ) % 254];
	}
    pr_putcolormap( disp_pr, 0, 256, rred, rgreen, rblue );
    }

/* Signature stuff. */

static short posk_image[] = {
    0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
    0xffff,0xffff,0xffff,0xffff,0xffe7,0xcfff,0xffe7,0xcfff,
    0xffe7,0x9fff,0xffcf,0xbfff,0xffcf,0x3fff,0xffdf,0x507f,
    0xff8e,0x7e3f,0xff6e,0x7e7f,0xffcf,0x7e3f,0xffcf,0x7c7f,
    0xffcf,0x067f,0xffcf,0x0e7f,0xffce,0x7e7f,0xffcf,0x7c7f,
    0xffce,0x2a7f,0xffcf,0x14ff,0xffcf,0xfcff,0xffcf,0xffff,
    0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xfffc,0x7fff,
    0xfffe,0x3fff,0xfffe,0x3fff,0xfffe,0x3fff,0xfffe,0x3fff,
    0xffff,0x3fff,0xfffe,0x107f,0xffa0,0x007f,0xfc1e,0x3fff,
    0xffff,0x3fff,0xfffe,0x1fff,0xffff,0x3fff,0xfffe,0x3fff,
    0xfffe,0x3fff,0xffff,0x3fff,0xfffe,0x2bff,0xffe8,0x01ff,
    0xff05,0xd8ff,0xff7f,0xffff,0xffff,0xffff,0xff83,0xfcff,
    0xfe67,0xe0ff,0xfe47,0x9dff,0xfe13,0xf9ff,0xfe7b,0xb3ff,
    0xfe43,0x87ff,0xfe27,0xc7ff,0xfe65,0xc3ff,0xfc00,0x707f,
    0xfe3e,0x7c3f,0xffff,0x3fff,0xffff,0x2fff,0xfff8,0x1fff,
    0xfffe,0x2fff,0xffe0,0x01ff,0xff82,0xa8ff,0xffff,0xffff,
    0xffff,0xffff,0xffff,0xefff,0xffff,0xe7ff,0xffc7,0xe7ff,
    0xfff3,0xe7ff,0xffff,0xe7ff,0xffdf,0x667f,0xff1f,0x263f,
    0xffdf,0x27ff,0xffff,0x69ff,0xfffd,0xc1ff,0xfffb,0xe1ff,
    0xfffb,0xf1ff,0xfff7,0xf3ff,0xfff7,0xe7ff,0xffe7,0xc7ff,
    0xffef,0xcfff,0xffcf,0x9fff,0xff8f,0x3fff,0xff9e,0x7fff,
    0xffdc,0xffff,0xfffb,0xffff,0xffff,0xffff,0xffff,0xffff,
    0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff };
mpr_static( posk, 32, 92, 1, posk_image );

signit( pr )
Pixrect *pr;
    {
#ifdef sun386
    pr_flip( &posk );
#endif sun386

    pr_rop(
	pr, pr->pr_size.x - posk.pr_size.x, pr->pr_size.y - posk.pr_size.y,
	posk.pr_size.x, posk.pr_size.y,
	PIX_SRC | PIX_COLOR(1), &posk, 0, 0 );
    }

/* Call-back DDAs taken from libppm. */

#define DDA_SCALE 8192
#define abs(x) ((x) < 0 ? -(x) : (x))

line( x0, y0, x1, y1, drawprocP )
int x0, y0, x1, y1;
void (*drawprocP)();
    {
    /* Special case zero-length lines. */
    if ( x0 == x1 && y0 == y1 )
	{
	(*drawprocP)( x0, y0 );
	return;
	}

    /* Draw, using a simple DDA. */
    if ( abs( x1 - x0 ) > abs( y1 - y0 ) )
	{ /* Loop over X domain. */
	register long dy, srow;
	register int dx, col, row, prevrow;

	if ( x1 > x0 )
	    dx = 1;
	else
	    dx = -1;
	dy = ( y1 - y0 ) * DDA_SCALE / abs( x1 - x0 );
	prevrow = row = y0;
	srow = row * DDA_SCALE + DDA_SCALE / 2;
	col = x0;
	for ( ; ; )
	    {
	    if ( row != prevrow )
		{
		(*drawprocP)( col, prevrow );
		prevrow = row;
		}
	    (*drawprocP)( col, row );
	    if ( col == x1 )
		break;
	    srow += dy;
	    row = srow / DDA_SCALE;
	    col += dx;
	    }
	}
    else
	{ /* Loop over Y domain. */
	register long dx, scol;
	register int dy, col, row, prevcol;

	if ( y1 > y0 )
	    dy = 1;
	else
	    dy = -1;
	dx = ( x1 - x0 ) * DDA_SCALE / abs( y1 - y0 );
	row = y0;
	prevcol = col = x0;
	scol = col * DDA_SCALE + DDA_SCALE / 2;
	for ( ; ; )
	    {
	    if ( col != prevcol )
		{
		(*drawprocP)( prevcol, row );
		prevcol = col;
		}
	    (*drawprocP)( col, row );
	    if ( row == y1 )
		break;
	    row += dy;
	    scol += dx;
	    col = scol / DDA_SCALE;
	    }
	}
    }

#define SPLINE_THRESH 3
spline3( x0, y0, x1, y1, x2, y2, drawprocP )
int x0, y0, x1, y1, x2, y2;
void (*drawprocP)();
    {
    register int xa, ya, xb, yb, xc, yc, xp, yp;

    xa = ( x0 + x1 ) / 2;
    ya = ( y0 + y1 ) / 2;
    xc = ( x1 + x2 ) / 2;
    yc = ( y1 + y2 ) / 2;
    xb = ( xa + xc ) / 2;
    yb = ( ya + yc ) / 2;

    xp = ( x0 + xb ) / 2;
    yp = ( y0 + yb ) / 2;
    if ( abs( xa - xp ) + abs( ya - yp ) > SPLINE_THRESH )
	spline3( x0, y0, xa, ya, xb, yb, drawprocP );
    else
	line( x0, y0, xb, yb, drawprocP );

    xp = ( x2 + xb ) / 2;
    yp = ( y2 + yb ) / 2;
    if ( abs( xc - xp ) + abs( yc - yp ) > SPLINE_THRESH )
	spline3( xb, yb, xc, yc, x2, y2, drawprocP );
    else
	line( xb, yb, x2, y2, drawprocP );
    }

circle( cx, cy, radius, drawprocP )
int cx, cy, radius;
void (*drawprocP)();
    {
    register int x0, y0, x, y, prevx, prevy, nopointsyet;
    register long sx, sy, e;

    x0 = x = radius;
    y0 = y = 0;
    sx = x * DDA_SCALE + DDA_SCALE / 2;
    sy = y * DDA_SCALE + DDA_SCALE / 2;
    e = DDA_SCALE / radius;
    (*drawprocP)( x + cx, y + cy );
    nopointsyet = 1;
    do
	{
	prevx = x;
	prevy = y;
	sx += e * sy / DDA_SCALE;
	sy -= e * sx / DDA_SCALE;
	x = sx / DDA_SCALE;
	y = sy / DDA_SCALE;
	if ( x != prevx || y != prevy )
	    {
	    nopointsyet = 0;
	    (*drawprocP)( x + cx, y + cy );
	    }
	}
    while ( nopointsyet || x != x0 || y != y0 );
    }