/* $XFree86: xc/programs/Xserver/hw/xfree86/xf4bpp/emulTile.c,v 1.3tsi Exp $ */
/*
 * Copyright IBM Corporation 1987,1988,1989
 *
 * All Rights Reserved
 *
 * 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, and that the name of IBM not be
 * used in advertising or publicity pertaining to distribution of the
 * software without specific, written prior permission.
 *
 * IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
 * IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
 * SOFTWARE.
 *
*/
/* $XConsortium: emulTile.c /main/4 1996/02/21 17:56:23 kaleb $ */

/* ppc Tile
 * P. Shupak 11/87
 * Modified From original ppc Tile
 * T. Paquin 9/87
 * Uses private imageFill a bunch of times
 */

#include "xf4bpp.h"
#include "OScompiler.h"
#include "ibmTrace.h"

static void
DrawFirstTile
(
	WindowPtr pWin, /* GJA */
	register PixmapPtr pTile,
	register int x,
	register int y,
	int w,
	int h,
	int alu,
	unsigned long int planes,
	int xOffset,
	int yOffset
)
{
register int htarget ;
register int vtarget ;

	if ( xOffset ) { /* Not X-Aligned */
		if ( yOffset ) { /* Nor Y-Aligned */
			htarget = MIN( pTile->drawable.width - xOffset, w ),
			vtarget = MIN( pTile->drawable.height - yOffset, h ),
			yOffset *= pTile->devKind ;
			xf4bppDrawColorImage( pWin,x, y,
				 htarget,
				 vtarget,
				 (unsigned char *)pTile->devPrivate.ptr + yOffset + xOffset,
				 pTile->devKind,
				 alu, planes ) ;
			if ( w > htarget ) {
				w = MIN( w, pTile->drawable.width ) ;
				if ( h > vtarget ) {
					h = MIN( h, pTile->drawable.height ) ;
					xf4bppDrawColorImage( pWin, x, y + vtarget,
						 htarget,
						 h - vtarget,
						 (unsigned char *)pTile->devPrivate.ptr + xOffset,
						 pTile->devKind,
						 alu, planes ) ;
					xf4bppDrawColorImage( pWin, x + htarget, y,
						 w - htarget,
						 vtarget,
						 (unsigned char *)pTile->devPrivate.ptr + yOffset,
						 pTile->devKind,
						 alu, planes ) ;
					xf4bppDrawColorImage( pWin, x + htarget,
						 y + vtarget,
						 w - htarget,
						 h - vtarget,
						 pTile->devPrivate.ptr,
						 pTile->devKind,
						 alu, planes ) ;
				}
				else { /* h <= vtarget */
					xf4bppDrawColorImage( pWin, x + htarget, y,
						 w - htarget,
						 vtarget,
						 (unsigned char *)pTile->devPrivate.ptr + yOffset,
						 pTile->devKind,
						 alu, planes ) ;
				}
			}
			else if ( h > vtarget ) {
				xf4bppDrawColorImage( pWin, x, y + vtarget,
					 htarget,
					 MIN( h, pTile->drawable.height ) - vtarget,
					 (unsigned char *)pTile->devPrivate.ptr + xOffset,
					 pTile->devKind,
					 alu, planes ) ;
				vtarget = pTile->drawable.height ;
			}
		}
		else { /* No Y Offset */
			xf4bppDrawColorImage( pWin, x, y,
				 htarget = MIN( pTile->drawable.width - xOffset, w ),
				 vtarget = MIN( pTile->drawable.height, h ),
				 (unsigned char *)pTile->devPrivate.ptr + xOffset,
				 pTile->devKind,
				 alu, planes ) ;
			if ( w > htarget ) {
				xf4bppDrawColorImage( pWin, x + htarget, y,
					 MIN( pTile->drawable.width, w ) - htarget,
					 vtarget,
					 pTile->devPrivate.ptr,
					 pTile->devKind,
					 alu, planes ) ;
			}
		}
	}
	else if ( yOffset ) {
		xf4bppDrawColorImage( pWin, x, y,
			 htarget = MIN( pTile->drawable.width, w ),
			 vtarget = MIN( pTile->drawable.height - yOffset, h ),
			 (unsigned char *)pTile->devPrivate.ptr + ( yOffset * pTile->devKind ),
			 pTile->devKind,
			 alu, planes ) ;
		if ( h > vtarget ) {
			xf4bppDrawColorImage( pWin, x, y + vtarget,
				 htarget,
				 MIN( pTile->drawable.height, h ) - vtarget,
				 pTile->devPrivate.ptr,
				 pTile->devKind,
				 alu, planes ) ;
		}
	}
	else { /* NO Offset */
		xf4bppDrawColorImage( pWin, x, y,
			 htarget = MIN( pTile->drawable.width, w ),
			 vtarget = MIN( pTile->drawable.height, h ),
			 pTile->devPrivate.ptr,
			 pTile->devKind,
			 alu, planes ) ;
	}

	return ;
}

/* GJA --
 * After finding three kinds of errors in this single function,
 * (requiring modifications to be made at at least 10 places,
 * I decided to REWRITE the body of the xf4bppTileRect function from scratch.
 * This version simply computes all relevant margins in advance, and does
 * not try to reuse temporary variables. I leave that to the compiler.
 * (This was a maintenance and robustness nightmare anyway.)
 * The code is pretty obvious: all margins, coordinates, and numbers of tiles
 * are computed before drawing starts.
 * Notice that the margins consist of incompletely drawn tiles. Therefore
 * we need offsets in the data for the left and upper margins.
 * The right and lower margins are also incomplete, but start at offset 0
 * in the data. They just end at awkward offsets. 
 * The center block, by definition, consists of fully drawn tiles.
 * Perhaps we could leave out some if's. But why bother? It would decrease
 * robustness.
 */
void
xf4bppTileRect( pWin, pTile, alu, planes, x0, y0, w, h, xSrc, ySrc )
WindowPtr pWin; /* GJA */
register PixmapPtr pTile ;
const int alu ;
const unsigned long int planes ;
register int x0, y0, w, h ;
int xSrc ;
int ySrc ;
{
int xOffset ;
int yOffset ;
int width, height;

TRACE( ( "xf4bppTileRect(pTile=x%x,alu=x%x,planes=x%02x,x0=%d,y0=%d,w=%d,h=%d,xSrc=%d,ySrc=%d\n",
		pTile, alu, planes, x0, y0, w, h, xSrc, ySrc ) ) ;

     switch ( alu ) {
	case GXclear:		/* 0x0 Zero 0 */
	case GXinvert:		/* 0xa NOT dst */
	case GXset:		/* 0xf 1 */
		xf4bppFillSolid
			( pWin, 0xFF, alu, planes, x0, y0, w, h ) ;
	case GXnoop:		/* 0x5 dst */
		return ;
	default:
		break ;
}

    width = pTile->drawable.width;
    if ( ( xOffset = ( x0 - xSrc ) ) > 0 )
	xOffset %= width ;
    else
	xOffset = width - (( - xOffset ) % width ) ;
    if ( xOffset == width ) xOffset = 0; /* For else case */

    height = pTile->drawable.height;
    if ( ( yOffset = ( y0 - ySrc ) ) > 0 )
	yOffset %= height ;
    else
	yOffset = height - (( - yOffset ) % height ) ;
    if ( yOffset == height ) yOffset = 0; /* For else case */

     switch ( alu ) {
	case GXcopyInverted:	/* 0xc NOT src */
	case GXcopy:		/* 0x3 src */
		/* Special Case Code */
		DrawFirstTile( pWin, pTile, x0, y0, w, h,
			       alu, planes, xOffset, yOffset ) ;
		/* Here We Double The Size Of The BLIT Each Iteration */
		xf4bppReplicateArea( pWin, x0, y0, planes, w, h,
			    MIN( w, pTile->drawable.width ),
			    MIN( h, pTile->drawable.height ) ) ;
		break ;
	case GXnor:		/* 0x8 NOT src AND NOT dst */
	case GXandReverse:	/* 0x2 src AND NOT dst */
	case GXorReverse:	/* 0xb src OR NOT dst */
	case GXnand:		/* 0xe NOT src OR NOT dst */
	case GXandInverted:	/* 0x4 NOT src AND dst */
	case GXand:		/* 0x1 src AND dst */
	case GXequiv:		/* 0x9 NOT src XOR dst */
	case GXxor:		/* 0x6 src XOR dst */
	case GXorInverted:	/* 0xd NOT src OR dst */
	case GXor:		/* 0x7 src OR dst */
	default:
		{
		register unsigned char *data ;
		register int hcount, vcount ; /* Number of tiles in center */
		int xcount, ycount;	/* Temporaries */
		int x1, y1;	/* Left upper corner of center */
		int x2, y2;	/* Left upper corner of lower right margin */
		int leftmgn, rightmgn, topmgn, botmgn; /* Margins */

		int htarget, vtarget ;

		data = pTile->devPrivate.ptr;
	
		/* Compute the various sizes and coordinates. */
		leftmgn = MIN( w, width - xOffset ) ;
		x1 = x0 + leftmgn;
		topmgn = MIN( h, height - yOffset ) ;
		y1 = y0 + topmgn;

		rightmgn = (w - leftmgn) % width;
		hcount = (w - leftmgn) / width;
		x2 = x0 + w - rightmgn;
		botmgn = (h - topmgn) % height;
		vcount = (h - topmgn) / height;
		y2 = y0 + h - botmgn;
		
		/* We'll use yOffset as offset in data.
		 * This requires yOffset != height (ditto xOffset).
                 */
		yOffset *= pTile->devKind;

		/* Draw top margin, including corners */
		if ( topmgn ) {
			if ( leftmgn ) {
				xf4bppDrawColorImage( pWin, x0, y0, leftmgn, topmgn,
					 data + yOffset + xOffset,
					 pTile->devKind, alu, planes ) ;
			}
			for (	xcount = hcount, htarget = x1;
				xcount ;
				xcount--, htarget += width )
			{
				xf4bppDrawColorImage( pWin, htarget, y0, width, topmgn,
					 data + yOffset,
					 pTile->devKind, alu, planes ) ;
			}
			if ( rightmgn ) {
				xf4bppDrawColorImage( pWin, x2, y0, rightmgn, topmgn,
					 data + yOffset,
					 pTile->devKind, alu, planes ) ;
			}
		}
	
		/* Draw bottom margin, including corners */
		if ( botmgn ) {
			if ( leftmgn ) {
				xf4bppDrawColorImage( pWin, x0, y2, leftmgn, botmgn,
					 data + xOffset,
					 pTile->devKind, alu, planes ) ;
			}
			for (	xcount = hcount, htarget = x1;
				xcount ;
				xcount--, htarget += width )
			{
				xf4bppDrawColorImage( pWin, htarget, y2, width, botmgn,
					 data,
					 pTile->devKind, alu, planes ) ;
			}
			if ( rightmgn ) {
				xf4bppDrawColorImage( pWin, x2, y2, rightmgn, botmgn,
					 data,
					 pTile->devKind, alu, planes ) ;
			}
		}
	
		/* Draw left margin, excluding corners */
		if ( leftmgn ) {
			for (	ycount = vcount, vtarget = y1 ;
				ycount ;
				ycount--, vtarget += height )
			{
				xf4bppDrawColorImage( pWin, x0, vtarget, leftmgn, height,
					 data + xOffset,
					 pTile->devKind, alu, planes ) ;
			}
		}

		/* Draw right margin, excluding corners */
		if ( rightmgn ) {
			for (	ycount = vcount, vtarget = y1 ;
				ycount ;
				ycount--, vtarget += height )
			{
				xf4bppDrawColorImage( pWin, x2, vtarget, rightmgn, height,
					 data,
					 pTile->devKind, alu, planes ) ;
			}
		}

		/* Draw center consisting of full tiles */
		for (		ycount = vcount, vtarget = y1 ;
				ycount ;
				ycount--, vtarget += height )
		{
			for (	xcount = hcount, htarget = x1 ;
				xcount ;
				xcount--, htarget += width )
			{
				xf4bppDrawColorImage( pWin, htarget, vtarget, width, height,
					 data,
					 pTile->devKind, alu, planes ) ;
				
			}
		}
	} } /* Block + switch */
}
