[alt.sys.sun] Rotating bitmat 90 degrees

jef@well.sf.ca.us (Jef Poskanzer) (04/21/91)

In the referenced message, rsbx@cbmvax.commodore.com (Raymond S. Brand) wrote:
}The request was for a way to rotate a bitmap 90 degrees without having to do
}a readpixel/writepixel for each pixel in the area to rotate. What follows is
}a shar file with the source (Amiga but you can understand it anyway :-) to 2
}rotate functions and 2 test programs.

Appended is a version for Suns using raw pixrect calls.  It's embedded
in a screen-rot program of the same genre as meltdown and termite, but
the rotation routine may be useful by itself.
---
Jef

  Jef Poskanzer  jef@well.sf.ca.us  {apple, ucbvax, hplabs}!well!jef
  If you eat a live frog in the morning, nothing worse will happen to
                either of you for the rest of the day.

/*
** sunrot - rot a Sun screen by rotating random squares
**
** Compile with: cc -O sunrot.c -lpixrect -s -o sunrot
**
** The rotation algorithm is similar to the one in "Smalltalk-80: The
** Language and Implementation", page 408.  In particular, the mask
** refinement step is the same.  However, the rest is different, simpler,
** doesn't flash white bars on the screen, and runs slightly faster (same
** number of blits but smaller area), at the cost of three off-screen
** temp areas instead of two.  I consider this modified version fairly
** obvious, in case anyone is getting any stupid ideas about patenting
** it.  The original version was non-obvious, but memory has gotten a
** lot cheaper since 1983.
**
** Copyright (C) 1991 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.
*/

#include <stdio.h>
#include <sys/types.h>
#include <sys/timeb.h>
#include <sys/file.h>
#include <pixrect/pixrect_hs.h>
#define PIX_AND ( PIX_SRC & PIX_DST )
#define PIX_OR ( PIX_SRC | PIX_DST )

Pixrect* my_pr_open();
void pr_rotatepow2square();

void
main( argc, argv )
    int argc;
    char* argv[];
    {
    int argn, delay;
    struct timeb tb;
    Pixrect* disp_pr;
    int wid, hgt, maxpow2;
    int x, y, pow2, cw;
    char* fb;
    char* usage = "usage:  %s [-fb <framebuffer>] [-delay <msec>]\n";

    argn = 1;
    fb = "/dev/fb";
    delay = 0;
    if ( argn < argc && argv[argn][0] == '-' )
	{
	if ( strcmp( argv[argn], "-fb" ) == 0 ||
	     strcmp( argv[argn], "-f" ) == 0 )
	    {
	    ++argn;
	    fb = argv[argn];
	    }
	else if ( strcmp( argv[argn], "-delay" ) == 0 ||
		  strcmp( argv[argn], "-dela" ) == 0 ||
		  strcmp( argv[argn], "-del" ) == 0 ||
		  strcmp( argv[argn], "-de" ) == 0 ||
		  strcmp( argv[argn], "-d" ) == 0 )
	    {
	    ++argn;
	    delay = atoi( argv[argn] );
	    }
	else
	    {
	    fprintf( stderr, usage, argv[0] );
	    exit( 1 );
	    }
	++argn;
	}
    
    if ( argn != argc )
	{
	fprintf( stderr, usage, argv[0] );
	exit( 1 );
	}

    if ( ( disp_pr = my_pr_open( fb ) ) == (Pixrect*) 0 )
	{
	fprintf( stderr, "%s: error opening display\n", argv[0] );
	exit( 1 );
	}

    wid = disp_pr->pr_size.x;
    hgt = disp_pr->pr_size.y;
    for ( maxpow2 = 1;
	 ( 2 << maxpow2 ) <= wid && ( 2 << maxpow2 ) <= hgt;
	 ++maxpow2 )
	;

    ftime( &tb );
    srandom( (int) ( tb.time ^ tb.millitm ^ getpid( ) ) );

    for ( ; ; )
	{
	pow2 = random() % ( maxpow2 + 1 );
	x = random() % ( wid - ( 1 << pow2 ) );
	y = random() % ( hgt - ( 1 << pow2 ) );
	cw = random() % 2;
	pr_rotatepow2square( disp_pr, x, y, pow2, cw );
	if ( delay != 0 )
	    usleep( delay * 1000 );
	}
    }

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;
    close( fd );
    return pr_open( fb );
    }

static Pixrect* temp1 = (Pixrect*) 0;
static Pixrect* temp2 = (Pixrect*) 0;
static Pixrect* mask = (Pixrect*) 0;

void
pr_rotatepow2square( pr, x, y, pow2, cw )
    Pixrect* pr;
    int x, y, pow2, cw;
    {
    int s, h, q, ts, th, tq;

    if ( pow2 < 2 )
	return;
    s = 1 << pow2;
    h = s / 2;
    q = h / 2;

    /* Make sure that temps and mask are big enough and the right depth. */
    if ( temp1 != (Pixrect*) 0 )
	if ( temp1->pr_size.x < s || temp1->pr_size.y < s ||
	     temp1->pr_depth != pr->pr_depth )
	    {
	    pr_destroy( temp1 );
	    temp1 = (Pixrect*) 0;
	    pr_destroy( temp2 );
	    temp2 = (Pixrect*) 0;
	    pr_destroy( mask );
	    mask = (Pixrect*) 0;
	    }
    if ( temp1 == (Pixrect*) 0 )
	{
	temp1 = mem_create( s, s, pr->pr_depth );
	temp2 = mem_create( s, s, pr->pr_depth );
	mask = mem_create( s, s, 1 );
	}

    /* Initialize mask to the upper left quadrant. */
    pr_rop( mask, 0, 0, s, s, PIX_CLR, (Pixrect*) 0, 0, 0 );
    pr_rop( mask, 0, 0, h, h, PIX_SET, (Pixrect*) 0, 0, 0 );

    for ( ts = s, th = ts / 2, tq = th / 2;
	  pow2 > 0;
	  --pow2, ts = th, th = tq, tq /= 2 )
	{
	if ( cw )
	    pr_rop( temp1,  0,  0, s-th, s-th, PIX_SRC,    pr,    x, y+th );
	else
	    pr_rop( temp1,  0,  0, s-th, s-th, PIX_SRC,    pr, x+th,    y );
	pr_rop( temp1,  0,  0,    s,    s, PIX_AND,  mask,    0,    0 );

	if ( cw )
	    pr_rop( temp2, th,  0, s-th, s-th, PIX_SRC,    pr,    x,    y );
	else
	    pr_rop( temp2, th,  0, s-th, s-th, PIX_SRC,    pr, x+th, y+th );
	pr_rop( temp2, th,  0, s-th, s-th, PIX_AND,  mask,    0,    0 );
	pr_rop( temp1, th,  0, s-th, s-th,  PIX_OR, temp2,   th,    0 );

	if ( cw )
	    pr_rop( temp2, th, th, s-th, s-th, PIX_SRC,    pr, x+th,    y );
	else
	    pr_rop( temp2, th, th, s-th, s-th, PIX_SRC,    pr,    x, y+th );
	pr_rop( temp2, th, th, s-th, s-th, PIX_AND,  mask,    0,    0 );
	pr_rop( temp1, th, th, s-th, s-th,  PIX_OR, temp2,   th,   th );

	if ( cw )
	    pr_rop( temp2,  0, th, s-th, s-th, PIX_SRC,    pr, x+th, y+th );
	else
	    pr_rop( temp2,  0, th, s-th, s-th, PIX_SRC,    pr,    x,    y );
	pr_rop( temp2,  0, th, s-th, s-th, PIX_AND,  mask,    0,    0 );
	pr_rop( temp1,  0, th, s-th, s-th,  PIX_OR, temp2,    0,   th );

	/* And copy back to the screen. */
	pr_rop(    pr,  x,  y,    s,    s, PIX_SRC, temp1,    0,    0 );

	/* Refine mask. */
	pr_rop( mask,  0,  0, s-tq, s-tq, PIX_AND, mask, tq, tq );
	pr_rop( mask,  0, th,    s, s-th,  PIX_OR, mask,  0,  0 );
	pr_rop( mask, th,  0, s-th,    s,  PIX_OR, mask,  0,  0 );
	}
    }