/*
 * Id: s3draw.c,v 1.1 1999/11/02 03:54:47 keithp Exp $
 *
 * Copyright 1999 SuSE, Inc.
 *
 * Permission to use, copy, modify, distribute, and sell this software and its
 * documentation for any purpose is hereby granted without fee, 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 SuSE not be used in advertising or
 * publicity pertaining to distribution of the software without specific,
 * written prior permission.  SuSE makes no representations about the
 * suitability of this software for any purpose.  It is provided "as is"
 * without express or implied warranty.
 *
 * SuSE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL SuSE
 * 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.
 *
 * Author:  Keith Packard, SuSE, Inc.
 */
/* $RCSId: xc/programs/Xserver/hw/kdrive/savage/s3draw.c,v 1.6 2001/05/29 04:54:11 keithp Exp $ */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include	"s3.h"
#include	"s3draw.h"

#include	"Xmd.h"
#include	"gcstruct.h"
#include	"scrnintstr.h"
#include	"pixmapstr.h"
#include	"regionstr.h"
#include	"mistruct.h"
#include	"fontstruct.h"
#include	"dixfontstr.h"
#include	"fb.h"
#include	"migc.h"
#include	"miline.h"

/*
 * Map X rops to S3 rops
 */

short s3alu[16] = {
    MIX_0,
    MIX_AND,
    MIX_SRC_AND_NOT_DST,
    MIX_SRC,
    MIX_NOT_SRC_AND_DST,
    MIX_DST,
    MIX_XOR,
    MIX_OR,
    MIX_NOR,
    MIX_XNOR,
    MIX_NOT_DST,
    MIX_SRC_OR_NOT_DST,
    MIX_NOT_SRC,
    MIX_NOT_SRC_OR_DST,
    MIX_NAND,
    MIX_1
};

/*
 * Handle pixel transfers
 */

#define BURST
#ifdef BURST
#define PixTransDeclare	VOL32	*pix_trans_base = (VOL32 *) (s3c->registers),\
				*pix_trans = pix_trans_base
#define PixTransStart(n)	if (pix_trans + (n) > pix_trans_base + 8192) pix_trans = pix_trans_base
#define PixTransStore(t)	*pix_trans++ = (t)
#else
#define PixTransDeclare	VOL32	*pix_trans = &s3->pix_trans
#define PixTransStart(n)	
#define PixTransStore(t)	*pix_trans = (t)
#endif

int	s3GCPrivateIndex;
int	s3WindowPrivateIndex;
int	s3Generation;

/*
  s3DoBitBlt
  =============
  Bit Blit for all window to window blits.
*/

#define sourceInvarient(alu)	(((alu) & 3) == (((alu) >> 2) & 3))

void
s3CopyNtoN (DrawablePtr	pSrcDrawable,
	    DrawablePtr	pDstDrawable,
	    GCPtr	pGC,
	    BoxPtr	pbox,
	    int		nbox,
	    int		dx,
	    int		dy,
	    Bool	reverse,
	    Bool	upsidedown,
	    Pixel	bitplane,
	    void	*closure)
{
    SetupS3(pDstDrawable->pScreen);
    int	    srcX, srcY, dstX, dstY;
    int	    w, h;
    int	    flags;
    
    if (sourceInvarient (pGC->alu))
    {
	s3FillBoxSolid (pDstDrawable, nbox, pbox, 0, pGC->alu, pGC->planemask);
	return;
    }
    
    s3SetGlobalBitmap (pDstDrawable->pScreen, s3GCMap (pGC));
    _s3SetBlt(s3,pGC->alu,pGC->planemask);
    DRAW_DEBUG ((DEBUG_RENDER, "s3CopyNtoN alu %d planemask 0x%x",
		pGC->alu, pGC->planemask));
    while (nbox--)
    {
	w = pbox->x2 - pbox->x1;
	h = pbox->y2 - pbox->y1;
	flags = 0;
	if (reverse)
	{
	    dstX = pbox->x2 - 1;
	}
	else
	{
	    dstX = pbox->x1;
	    flags |= INC_X;
	}
	srcX = dstX + dx;
	
	if (upsidedown)
	{
	    dstY = pbox->y2 - 1;
	}
	else
	{
	    dstY = pbox->y1;
	    flags |= INC_Y;
	}
	srcY = dstY + dy;
	
	_s3Blt (s3, srcX, srcY, dstX, dstY, w, h, flags);
	pbox++;
    }
    MarkSyncS3 (pSrcDrawable->pScreen);
}

RegionPtr
s3CopyArea(DrawablePtr pSrcDrawable, DrawablePtr pDstDrawable, GCPtr pGC,
	   int srcx, int srcy, int width, int height, int dstx, int dsty)
{
    SetupS3(pDstDrawable->pScreen);
    
    if (pSrcDrawable->type == DRAWABLE_WINDOW &&
	pDstDrawable->type == DRAWABLE_WINDOW)
    {
	return fbDoCopy (pSrcDrawable, pDstDrawable, pGC, 
			 srcx, srcy, width, height, 
			 dstx, dsty, s3CopyNtoN, 0, 0);
    }
    return KdCheckCopyArea (pSrcDrawable, pDstDrawable, pGC, 
			    srcx, srcy, width, height, dstx, dsty);
}

typedef struct _s31toNargs {
    unsigned long	copyPlaneFG, copyPlaneBG;
    Bool		opaque;
} s31toNargs;

void
_s3Stipple (S3CardInfo	*s3c,
	    FbStip	*psrcBase,
	    FbStride	widthSrc,
	    int		srcx,
	    int		srcy,
	    int		dstx,
	    int		dsty,
	    int		width,
	    int		height)
{
    S3Ptr	s3 = s3c->s3;
    FbStip	*psrcLine, *psrc;
    FbStride	widthRest;
    FbStip	bits, tmp, lastTmp;
    int		leftShift, rightShift;
    int		nl, nlMiddle;
    int		r;
    PixTransDeclare;
    
    /* Compute blt address and parameters */
    psrc = psrcBase + srcy * widthSrc + (srcx >> 5);
    nlMiddle = (width + 31) >> 5;
    leftShift = srcx & 0x1f;
    rightShift = 32 - leftShift;
    widthRest = widthSrc - nlMiddle;
    
    _s3PlaneBlt(s3,dstx,dsty,width,height);
    
    if (leftShift == 0)
    {
	while (height--)
	{
	    nl = nlMiddle;
	    PixTransStart(nl);
	    while (nl--)
	    {
		tmp = *psrc++;
		S3AdjustBits32 (tmp);
		PixTransStore (tmp);
	    }
	    psrc += widthRest;
	}
    }
    else
    {
	widthRest--;
	while (height--)
	{
	    bits = *psrc++;
	    nl = nlMiddle;
	    PixTransStart(nl);
	    while (nl--)
	    {
		tmp = FbStipLeft(bits, leftShift);
		bits = *psrc++;
		tmp |= FbStipRight(bits, rightShift);
		S3AdjustBits32(tmp);
		PixTransStore (tmp);
	    }
	    psrc += widthRest;
	}
    }
}
	    
void
s3Copy1toN (DrawablePtr	pSrcDrawable,
	    DrawablePtr	pDstDrawable,
	    GCPtr	pGC,
	    BoxPtr	pbox,
	    int		nbox,
	    int		dx,
	    int		dy,
	    Bool	reverse,
	    Bool	upsidedown,
	    Pixel	bitplane,
	    void	*closure)
{
    SetupS3(pDstDrawable->pScreen);
    
    s31toNargs		*args = closure;
    int			dstx, dsty;
    FbStip		*psrcBase;
    FbStride		widthSrc;
    int			srcBpp;
    int			srcXoff, srcYoff;

    if (args->opaque && sourceInvarient (pGC->alu))
    {
	s3FillBoxSolid (pDstDrawable, nbox, pbox,
			pGC->bgPixel, pGC->alu, pGC->planemask);
	return;
    }
    
    s3SetGlobalBitmap (pDstDrawable->pScreen, s3GCMap (pGC));
    fbGetStipDrawable (pSrcDrawable, psrcBase, widthSrc, srcBpp, srcXoff, srcYoff);
    
    if (args->opaque)
    {
	_s3SetOpaquePlaneBlt(s3,pGC->alu,pGC->planemask,args->copyPlaneFG,
			     args->copyPlaneBG);
    }
    else
    {
	_s3SetTransparentPlaneBlt (s3, pGC->alu, 
				   pGC->planemask, args->copyPlaneFG);
    }
    
    while (nbox--)
    {
	dstx = pbox->x1;
	dsty = pbox->y1;
	
	_s3Stipple (s3c,
		    psrcBase, widthSrc, 
		    dstx + dx - srcXoff, dsty + dy - srcYoff,
		    dstx, dsty, 
		    pbox->x2 - dstx, pbox->y2 - dsty);
	pbox++;
    }
    MarkSyncS3 (pDstDrawable->pScreen);
}

RegionPtr
s3CopyPlane(DrawablePtr pSrcDrawable, DrawablePtr pDstDrawable, GCPtr pGC,
	int srcx, int srcy, int width, int height, 
	int dstx, int dsty, unsigned long bitPlane)
{
    SetupS3 (pDstDrawable->pScreen);
    RegionPtr		ret;
    s31toNargs		args;

    if (pDstDrawable->type == DRAWABLE_WINDOW &&
	pSrcDrawable->depth == 1)
    {
	args.copyPlaneFG = pGC->fgPixel;
	args.copyPlaneBG = pGC->bgPixel;
	args.opaque = TRUE;
	return fbDoCopy (pSrcDrawable, pDstDrawable, pGC, 
			 srcx, srcy, width, height, 
			 dstx, dsty, s3Copy1toN, bitPlane, &args);
    }
    return KdCheckCopyPlane(pSrcDrawable, pDstDrawable, pGC, 
			    srcx, srcy, width, height, 
			    dstx, dsty, bitPlane);
}

void
s3PushPixels (GCPtr pGC, PixmapPtr pBitmap,
	      DrawablePtr pDrawable,
	      int w, int h, int x, int y)
{
    SetupS3 (pDrawable->pScreen);
    s31toNargs		args;
    
    if (pDrawable->type == DRAWABLE_WINDOW && pGC->fillStyle == FillSolid)
    {
	args.opaque = FALSE;
	args.copyPlaneFG = pGC->fgPixel;
	(void) fbDoCopy ((DrawablePtr) pBitmap, pDrawable, pGC,
			  0, 0, w, h, x, y, s3Copy1toN, 1, &args);
    }
    else
    {
	KdCheckPushPixels (pGC, pBitmap, pDrawable, w, h, x, y);
    }
}

void
s3FillBoxSolid (DrawablePtr pDrawable, int nBox, BoxPtr pBox, 
		unsigned long pixel, int alu, unsigned long planemask)
{
    SetupS3(pDrawable->pScreen);
    register int	r;

    s3SetGlobalBitmap (pDrawable->pScreen, s3DrawMap (pDrawable));
    _s3SetSolidFill(s3,pixel,alu,planemask);
    
    while (nBox--) {
	_s3SolidRect(s3,pBox->x1,pBox->y1,pBox->x2-pBox->x1,pBox->y2-pBox->y1);
	pBox++;
    }
    MarkSyncS3 (pDrawable->pScreen);
}

void
_s3SetPattern (ScreenPtr pScreen, int ma,
	      int alu, unsigned long planemask, s3PatternPtr pPattern)
{
    SetupS3(pScreen);
    S3PatternCache  *cache;
    
    _s3LoadPattern (pScreen, ma, pPattern);
    cache = pPattern->cache;
    
    switch (pPattern->fillStyle) {
    case FillTiled:
	_s3SetTile(s3,alu,planemask);
	break;
    case FillStippled:
	_s3SetStipple(s3,alu,planemask,pPattern->fore);
	break;
    case FillOpaqueStippled:
	_s3SetOpaqueStipple(s3,alu,planemask,pPattern->fore,pPattern->back);
	break;
    }
}

void
s3FillBoxPattern (DrawablePtr pDrawable, int nBox, BoxPtr pBox, 
		  int alu, unsigned long planemask, s3PatternPtr pPattern)
{
    SetupS3(pDrawable->pScreen);
    S3PatternCache    	*cache;
    int			patx, paty;

    s3SetGlobalBitmap (pDrawable->pScreen, s3DrawMap (pDrawable));
    _s3SetPattern (pDrawable->pScreen, s3DrawMap(pDrawable), alu, planemask, pPattern);
    cache = pPattern->cache;
    while (nBox--) 
    {
	_s3PatRect(s3,cache->x, cache->y,
		   pBox->x1, pBox->y1, 
		   pBox->x2-pBox->x1, pBox->y2-pBox->y1);
	pBox++;
    }
    MarkSyncS3 (pDrawable->pScreen);
}

void
s3FillBoxLargeStipple (DrawablePtr pDrawable, GCPtr pGC,
		       int nBox, BoxPtr pBox)
{
    SetupS3(pDrawable->pScreen);
    DrawablePtr	pStipple = &pGC->stipple->drawable;
    int		xRot = pGC->patOrg.x + pDrawable->x;
    int		yRot = pGC->patOrg.y + pDrawable->y;
    FbStip	*stip;
    FbStride	stipStride;
    int		stipBpp;
    int		stipXoff, stipYoff;
    int		stipWidth, stipHeight;
    int		dstX, dstY, width, height;
    
    stipWidth = pStipple->width;
    stipHeight = pStipple->height;
    fbGetStipDrawable (pStipple, stip, stipStride, stipBpp, stipXoff, stipYoff);

    s3SetGlobalBitmap (pDrawable->pScreen, s3DrawMap (pDrawable));
    if (pGC->fillStyle == FillOpaqueStippled)
    {
	_s3SetOpaquePlaneBlt(s3,pGC->alu,pGC->planemask,
			     pGC->fgPixel, pGC->bgPixel);
    
    }
    else
    {
	_s3SetTransparentPlaneBlt(s3,pGC->alu,pGC->planemask, pGC->fgPixel);
    }
    
    while (nBox--)
    {
	int		stipX, stipY, sx;
	int		widthTmp;
	int		h, w;
	int		x, y;
    
	dstX = pBox->x1;
	dstY = pBox->y1;
	width = pBox->x2 - pBox->x1;
	height = pBox->y2 - pBox->y1;
	pBox++;
	modulus (dstY - yRot - stipYoff, stipHeight, stipY);
	modulus (dstX - xRot - stipXoff, stipWidth, stipX);
	y = dstY;
	while (height)
	{
	    h = stipHeight - stipY;
	    if (h > height)
		h = height;
	    height -= h;
	    widthTmp = width;
	    x = dstX;
	    sx = stipX;
	    while (widthTmp)
	    {
		w = (stipWidth - sx);
		if (w > widthTmp)
		    w = widthTmp;
		widthTmp -= w;
		_s3Stipple (s3c,
			    stip,
			    stipStride,
			    sx, stipY,
			    x, y,
			    w, h);
		x += w;
		sx = 0;
	    }
	    y += h;
	    stipY = 0;
	}
    }
    MarkSyncS3 (pDrawable->pScreen);
}

#define NUM_STACK_RECTS	1024

void
s3PolyFillRect (DrawablePtr pDrawable, GCPtr pGC, 
		int nrectFill, xRectangle *prectInit)
{
    s3GCPrivate(pGC);
    xRectangle	    *prect;
    RegionPtr	    prgnClip;
    register BoxPtr pbox;
    register BoxPtr pboxClipped;
    BoxPtr	    pboxClippedBase;
    BoxPtr	    pextent;
    BoxRec	    stackRects[NUM_STACK_RECTS];
    int		    numRects;
    int		    n;
    int		    xorg, yorg;
    int		    x, y;

    prgnClip = fbGetCompositeClip(pGC);
    xorg = pDrawable->x;
    yorg = pDrawable->y;
    
    if (xorg || yorg)
    {
	prect = prectInit;
	n = nrectFill;
	while(n--)
	{
	    prect->x += xorg;
	    prect->y += yorg;
	    prect++;
	}
    }
    
    prect = prectInit;

    numRects = REGION_NUM_RECTS(prgnClip) * nrectFill;
    if (numRects > NUM_STACK_RECTS)
    {
	pboxClippedBase = (BoxPtr)ALLOCATE_LOCAL(numRects * sizeof(BoxRec));
	if (!pboxClippedBase)
	    return;
    }
    else
	pboxClippedBase = stackRects;

    pboxClipped = pboxClippedBase;
	
    if (REGION_NUM_RECTS(prgnClip) == 1)
    {
	int x1, y1, x2, y2, bx2, by2;

	pextent = REGION_RECTS(prgnClip);
	x1 = pextent->x1;
	y1 = pextent->y1;
	x2 = pextent->x2;
	y2 = pextent->y2;
    	while (nrectFill--)
    	{
	    if ((pboxClipped->x1 = prect->x) < x1)
		pboxClipped->x1 = x1;
    
	    if ((pboxClipped->y1 = prect->y) < y1)
		pboxClipped->y1 = y1;
    
	    bx2 = (int) prect->x + (int) prect->width;
	    if (bx2 > x2)
		bx2 = x2;
	    pboxClipped->x2 = bx2;
    
	    by2 = (int) prect->y + (int) prect->height;
	    if (by2 > y2)
		by2 = y2;
	    pboxClipped->y2 = by2;

	    prect++;
	    if ((pboxClipped->x1 < pboxClipped->x2) &&
		(pboxClipped->y1 < pboxClipped->y2))
	    {
		pboxClipped++;
	    }
    	}
    }
    else
    {
	int x1, y1, x2, y2, bx2, by2;

	pextent = REGION_EXTENTS(pGC->pScreen, prgnClip);
	x1 = pextent->x1;
	y1 = pextent->y1;
	x2 = pextent->x2;
	y2 = pextent->y2;
    	while (nrectFill--)
    	{
	    BoxRec box;
    
	    if ((box.x1 = prect->x) < x1)
		box.x1 = x1;
    
	    if ((box.y1 = prect->y) < y1)
		box.y1 = y1;
    
	    bx2 = (int) prect->x + (int) prect->width;
	    if (bx2 > x2)
		bx2 = x2;
	    box.x2 = bx2;
    
	    by2 = (int) prect->y + (int) prect->height;
	    if (by2 > y2)
		by2 = y2;
	    box.y2 = by2;
    
	    prect++;
    
	    if ((box.x1 >= box.x2) || (box.y1 >= box.y2))
	    	continue;
    
	    n = REGION_NUM_RECTS (prgnClip);
	    pbox = REGION_RECTS(prgnClip);
    
	    /* clip the rectangle to each box in the clip region
	       this is logically equivalent to calling Intersect()
	    */
	    while(n--)
	    {
		pboxClipped->x1 = max(box.x1, pbox->x1);
		pboxClipped->y1 = max(box.y1, pbox->y1);
		pboxClipped->x2 = min(box.x2, pbox->x2);
		pboxClipped->y2 = min(box.y2, pbox->y2);
		pbox++;

		/* see if clipping left anything */
		if(pboxClipped->x1 < pboxClipped->x2 && 
		   pboxClipped->y1 < pboxClipped->y2)
		{
		    pboxClipped++;
		}
	    }
    	}
    }
    if (pboxClipped != pboxClippedBase)
    {
	if (pGC->fillStyle == FillSolid)
	    s3FillBoxSolid(pDrawable,
			   pboxClipped-pboxClippedBase, pboxClippedBase,
			   pGC->fgPixel, pGC->alu, pGC->planemask);
	else if (s3Priv->pPattern)
	    s3FillBoxPattern (pDrawable,
			      pboxClipped-pboxClippedBase, pboxClippedBase,
			      pGC->alu, pGC->planemask,
			      s3Priv->pPattern);
	else
	    s3FillBoxLargeStipple (pDrawable, pGC,
				   pboxClipped-pboxClippedBase, 
				   pboxClippedBase);
    }
    if (pboxClippedBase != stackRects)
    	DEALLOCATE_LOCAL(pboxClippedBase);
}

void
_s3FillSpanLargeStipple (DrawablePtr pDrawable, GCPtr pGC,
			 int n, DDXPointPtr ppt, int *pwidth)
{
    SetupS3 (pDrawable->pScreen);
    DrawablePtr	pStipple = &pGC->stipple->drawable;
    int		xRot = pGC->patOrg.x + pDrawable->x;
    int		yRot = pGC->patOrg.y + pDrawable->y;
    FbStip	*stip;
    FbStride	stipStride;
    int		stipBpp;
    int		stipXoff, stipYoff;
    int		stipWidth, stipHeight;
    int		dstX, dstY, width, height;
    
    s3SetGlobalBitmap (pDrawable->pScreen, s3GCMap (pGC));
    stipWidth = pStipple->width;
    stipHeight = pStipple->height;
    fbGetStipDrawable (pStipple, stip, stipStride, stipBpp, stipXoff, stipYoff);
    if (pGC->fillStyle == FillOpaqueStippled)
    {
	_s3SetOpaquePlaneBlt(s3,pGC->alu,pGC->planemask,
			     pGC->fgPixel, pGC->bgPixel);
    
    }
    else
    {
	_s3SetTransparentPlaneBlt(s3,pGC->alu,pGC->planemask, pGC->fgPixel);
    }
    while (n--)
    {
	int		stipX, stipY, sx;
	int		w;
	int		x, y;
    
	dstX = ppt->x;
	dstY = ppt->y;
	ppt++;
	width = *pwidth++;
	modulus (dstY - yRot - stipYoff, stipHeight, stipY);
	modulus (dstX - xRot - stipXoff, stipWidth, stipX);
	y = dstY;
	x = dstX;
	sx = stipX;
	while (width)
	{
	    w = (stipWidth - sx);
	    if (w > width)
		w = width;
	    width -= w;
	    _s3Stipple (s3c,
			stip,
			stipStride,
			sx, stipY,
			x, y,
			w, 1);
	    x += w;
	    sx = 0;
	}
    }
}

void
s3FillSpans (DrawablePtr pDrawable, GCPtr pGC, int n, 
	     DDXPointPtr ppt, int *pwidth, int fSorted)
{
    s3GCPrivate(pGC);
    SetupS3(pDrawable->pScreen);
    int		    x, y, x1, y1, x2, y2;
    int		    width;
				/* next three parameters are post-clip */
    int		    nTmp;
    int		    *pwidthFree;/* copies of the pointers to free */
    DDXPointPtr	    pptFree;
    BoxPtr	    extents;
    S3PatternCache  *cache;
    RegionPtr	    pClip = fbGetCompositeClip (pGC);

    s3SetGlobalBitmap (pDrawable->pScreen, s3GCMap (pGC));
    if (REGION_NUM_RECTS(pClip) == 1 && 
	(pGC->fillStyle == FillSolid  || s3Priv->pPattern))
    {
	extents = REGION_RECTS(pClip);
	x1 = extents->x1;
	x2 = extents->x2;
	y1 = extents->y1;
	y2 = extents->y2;
	if (pGC->fillStyle == FillSolid)
	{
	    _s3SetSolidFill(s3,pGC->fgPixel,pGC->alu,pGC->planemask);
	    cache = 0;
	}
	else
	{
	    _s3SetPattern (pDrawable->pScreen, s3GCMap(pGC), pGC->alu, pGC->planemask,
			   s3Priv->pPattern);
	    cache = s3Priv->pPattern->cache;
	}
	while (n--)
	{
	    y = ppt->y;
	    if (y1 <= y && y < y2)
	    {
		x = ppt->x;
		width = *pwidth;
		if (x < x1)
		{
		    width -= (x1 - x);
		    x = x1;
		}
		if (x2 < x + width)
		    width = x2 - x;
		if (width > 0)
		{
		    if (cache)
		    {
			_s3PatRect(s3, cache->x, cache->y, x, y, width, 1);
		    }
		    else
		    {
			_s3SolidRect(s3,x,y,width,1);
		    }
		}
	    }
	    ppt++;
	    pwidth++;
	}
    }
    else
    {
	nTmp = n * miFindMaxBand(pClip);
	pwidthFree = (int *)ALLOCATE_LOCAL(nTmp * sizeof(int));
	pptFree = (DDXPointRec *)ALLOCATE_LOCAL(nTmp * sizeof(DDXPointRec));
	if(!pptFree || !pwidthFree)
	{
	    if (pptFree) DEALLOCATE_LOCAL(pptFree);
	    if (pwidthFree) DEALLOCATE_LOCAL(pwidthFree);
	    return;
	}
	n = miClipSpans(fbGetCompositeClip(pGC),
			ppt, pwidth, n,
			pptFree, pwidthFree, fSorted);
	pwidth = pwidthFree;
	ppt = pptFree;
	if (pGC->fillStyle == FillSolid)
	{
	    _s3SetSolidFill(s3,pGC->fgPixel,pGC->alu,pGC->planemask);
	    while (n--)
	    {
		x = ppt->x;
		y = ppt->y;
		ppt++;
		width = *pwidth++;
		if (width)
		{
		    _s3SolidRect(s3,x,y,width,1);
		}
	    }
	}
	else if (s3Priv->pPattern)
	{
	    _s3SetPattern (pDrawable->pScreen, s3GCMap(pGC), pGC->alu, pGC->planemask,
			   s3Priv->pPattern);
	    cache = s3Priv->pPattern->cache;
	    while (n--)
	    {
		x = ppt->x;
		y = ppt->y;
		ppt++;
		width = *pwidth++;
		if (width)
		{
		    _s3PatRect(s3, cache->x, cache->y, x, y, width, 1);
		}
	    }
	}
	else
	{
	    _s3FillSpanLargeStipple (pDrawable, pGC, n, ppt, pwidth);
	}
	DEALLOCATE_LOCAL(pptFree);
	DEALLOCATE_LOCAL(pwidthFree);
    }
    MarkSyncS3 (pDrawable->pScreen);
}

#include "mifillarc.h"

#define FILLSPAN(s3,y,__x1,__x2) {\
    DRAW_DEBUG ((DEBUG_ARCS, "FILLSPAN %d: %d->%d", y, __x1, __x2)); \
    if ((__x2) >= (__x1)) {\
	_s3SolidRect(s3,(__x1),(y),(__x2)-(__x1)+1,1); \
    } \
}

#define FILLSLICESPANS(flip,__y) \
    if (!flip) \
    { \
	FILLSPAN(s3,__y,xl,xr) \
    } \
    else \
    { \
	xc = xorg - x; \
	FILLSPAN(s3, __y, xc, xr) \
	xc += slw - 1; \
	FILLSPAN(s3, __y, xl, xc) \
    }

static void
_s3FillEllipse (DrawablePtr pDraw, S3Ptr s3, xArc *arc)
{
    KdScreenPriv(pDraw->pScreen);
    int x, y, e;
    int yk, xk, ym, xm, dx, dy, xorg, yorg;
    int	y_top, y_bot;
    miFillArcRec info;
    register int xpos;
    int	slw;

    s3SetGlobalBitmap (pDraw->pScreen, s3DrawMap (pDraw));
    miFillArcSetup(arc, &info);
    MIFILLARCSETUP();
    y_top = pDraw->y + yorg - y;
    y_bot = pDraw->y + yorg + y + dy;
    xorg += pDraw->x;
    while (y)
    {
	y_top++;
	y_bot--;
	MIFILLARCSTEP(slw);
	if (!slw)
	    continue;
	xpos = xorg - x;
	_s3SolidRect (s3,xpos,y_top,slw,1);
	if (miFillArcLower(slw))
	    _s3SolidRect (s3,xpos,y_bot,slw,1);
    }
}


static void
_s3FillArcSlice (DrawablePtr pDraw, GCPtr pGC, S3Ptr s3, xArc *arc)
{
    KdScreenPriv(pDraw->pScreen);
    int yk, xk, ym, xm, dx, dy, xorg, yorg, slw;
    register int x, y, e;
    miFillArcRec info;
    miArcSliceRec slice;
    int xl, xr, xc;
    int	y_top, y_bot;

    s3SetGlobalBitmap (pDraw->pScreen, s3DrawMap (pDraw));
    DRAW_DEBUG ((DEBUG_ARCS, "slice %dx%d+%d+%d %d->%d",
		 arc->width, arc->height, arc->x, arc->y,
		 arc->angle1, arc->angle2));
    miFillArcSetup(arc, &info);
    miFillArcSliceSetup(arc, &slice, pGC);
    DRAW_DEBUG ((DEBUG_ARCS, "edge1.x %d edge2.x %d", 
		slice.edge1.x, slice.edge2.x));
    MIFILLARCSETUP();
    DRAW_DEBUG ((DEBUG_ARCS, "xorg %d yorg %d",
		xorg, yorg));
    xorg += pDraw->x;
    yorg += pDraw->y;
    y_top = yorg - y;
    y_bot = yorg + y + dy;
    slice.edge1.x += pDraw->x;
    slice.edge2.x += pDraw->x;
    DRAW_DEBUG ((DEBUG_ARCS, "xorg %d y_top %d y_bot %d",
		 xorg, y_top, y_bot));
    while (y > 0)
    {
	y_top++;
	y_bot--;
	MIFILLARCSTEP(slw);
	MIARCSLICESTEP(slice.edge1);
	MIARCSLICESTEP(slice.edge2);
	if (miFillSliceUpper(slice))
	{
	    MIARCSLICEUPPER(xl, xr, slice, slw);
	    FILLSLICESPANS(slice.flip_top, y_top);
	}
	if (miFillSliceLower(slice))
	{
	    MIARCSLICELOWER(xl, xr, slice, slw);
	    FILLSLICESPANS(slice.flip_bot, y_bot);
	}
    }
}

void
s3PolyFillArcSolid (DrawablePtr pDraw, GCPtr pGC, int narcs, xArc *parcs)
{
    SetupS3(pDraw->pScreen);
    xArc	    *arc;
    int		    i;
    int		    x, y;
    BoxRec	    box;
    RegionPtr	    pClip = fbGetCompositeClip(pGC);
    BOOL	    set;

    set = FALSE;
    for (; --narcs >= 0; parcs++)
    {
	if (miFillArcEmpty(parcs))
	    continue;
	if (miCanFillArc(parcs))
	{
	    box.x1 = parcs->x + pDraw->x;
	    box.y1 = parcs->y + pDraw->y;
	    box.x2 = box.x1 + (int)parcs->width + 1;
	    box.y2 = box.y1 + (int)parcs->height + 1;
	    switch (RECT_IN_REGION(pDraw->pScreen, pClip, &box))
	    {
	    case rgnIN:
		if (!set)
		{
		    _s3SetSolidFill (s3, pGC->fgPixel, pGC->alu, pGC->planemask);
		    set = TRUE;
		}
		if ((parcs->angle2 >= FULLCIRCLE) ||
		    (parcs->angle2 <= -FULLCIRCLE))
		{
		    DRAW_DEBUG ((DEBUG_ARCS, "Full circle ellipse %dx%d",
				 parcs->width, parcs->height));
		    _s3FillEllipse (pDraw, s3, parcs);
		}
		else
		{
		    DRAW_DEBUG ((DEBUG_ARCS, "Partial ellipse %dx%d",
				 parcs->width, parcs->height));
		    _s3FillArcSlice (pDraw, pGC, s3, parcs);
		}
		/* fall through ... */
	    case rgnOUT:
		continue;
	    case rgnPART:
		break;
	    }
	}
	if (set)
	{
	    MarkSyncS3 (pDraw->pScreen);
	    set = FALSE;
	}
	KdCheckPolyFillArc(pDraw, pGC, 1, parcs);
    }
    if (set)
    {
	MarkSyncS3 (pDraw->pScreen);
	set = FALSE;
    }
}

void
s3FillPoly (DrawablePtr pDrawable, GCPtr pGC, int shape, 
	    int mode, int countInit, DDXPointPtr ptsIn)
{
    SetupS3(pDrawable->pScreen);
    int		    nwidth;
    int		    maxy;
    int		    origin;
    int		    count;
    register int    vertex1, vertex2;
    int		    c;
    RegionPtr	    pClip = fbGetCompositeClip(pGC);    
    BoxPtr	    extents;
    int		    clip;
    int		    y, sy;
    int		    *vertex1p, *vertex2p;
    int		    *endp;
    int		    x1, x2, sx;
    int		    dx1, dx2;
    int		    dy1, dy2;
    int		    e1, e2;
    int		    step1, step2;
    int		    sign1, sign2;
    int		    h;
    int		    l, r;
    int		    nmiddle;

    if (mode == CoordModePrevious || REGION_NUM_RECTS(pClip) != 1)
    {
	KdCheckFillPolygon (pDrawable, pGC, shape, mode, countInit, ptsIn);
	return;
    }
    
    s3SetGlobalBitmap (pDrawable->pScreen, s3GCMap (pGC));
    sy = pDrawable->y;
    sx = pDrawable->x;
    origin = *((int *) &pDrawable->x);
    origin -= (origin & 0x8000) << 1;
    extents = &pClip->extents;
    vertex1 = *((int *) &extents->x1) - origin;
    vertex2 = *((int *) &extents->x2) - origin - 0x00010001;
    clip = 0;
    
    y = 32767;
    maxy = 0;
    vertex2p = (int *) ptsIn;
    endp = vertex2p + countInit;
    if (shape == Convex)
    {
	count = countInit;
    	while (count--)
    	{
	    c = *vertex2p;
	    clip |= (c - vertex1) | (vertex2 - c);
	    c = intToY(c);
	    DRAW_DEBUG ((DEBUG_POLYGON, "Y coordinate %d", c));
	    if (c < y) 
	    {
	    	y = c;
	    	vertex1p = vertex2p;
	    }
	    vertex2p++;
	    if (c > maxy)
	    	maxy = c;
    	}
    }
    else
    {
	int yFlip = 0;
	dx1 = 1;
	x2 = -1;
	x1 = -1;
	count = countInit;
    	while (count--)
    	{
	    c = *vertex2p;
	    clip |= (c - vertex1) | (vertex2 - c);
	    c = intToY(c);
	    DRAW_DEBUG ((DEBUG_POLYGON, "Y coordinate %d", c));
	    if (c < y) 
	    {
	    	y = c;
	    	vertex1p = vertex2p;
	    }
	    vertex2p++;
	    if (c > maxy)
	    	maxy = c;
	    if (c == x1)
		continue;
	    if (dx1 > 0)
	    {
		if (x2 < 0)
		    x2 = c;
		else
		    dx2 = dx1 = (c - x1) >> 31;
	    }
	    else
		if ((c - x1) >> 31 != dx1) 
		{
		    dx1 = ~dx1;
		    yFlip++;
		}
	    x1 = c;
       	}
	x1 = (x2 - c) >> 31;
	if (x1 != dx1)
	    yFlip++;
	if (x1 != dx2)
	    yFlip++;
	if (yFlip != 2) 
	    clip = 0x8000;
    }
    if (y == maxy)
	return;

    if (clip & 0x80008000)
    {
	KdCheckFillPolygon (pDrawable, pGC, shape, mode, countInit, ptsIn);
	return;
    }
    _s3SetSolidFill(s3,pGC->fgPixel,pGC->alu,pGC->planemask);
    
    vertex2p = vertex1p;
    vertex2 = vertex1 = *vertex2p++;
    if (vertex2p == endp)
	vertex2p = (int *) ptsIn;
#define Setup(c,x,vertex,dx,dy,e,sign,step) {\
    x = intToX(vertex); \
    if (dy = intToY(c) - y) { \
    	dx = intToX(c) - x; \
	step = 0; \
    	if (dx >= 0) \
    	{ \
	    e = 0; \
	    sign = 1; \
	    if (dx >= dy) {\
	    	step = dx / dy; \
	    	dx = dx % dy; \
	    } \
    	} \
    	else \
    	{ \
	    e = 1 - dy; \
	    sign = -1; \
	    dx = -dx; \
	    if (dx >= dy) { \
		step = - (dx / dy); \
		dx = dx % dy; \
	    } \
    	} \
    } \
    x += sx; \
    vertex = c; \
}

#define Step(x,dx,dy,e,sign,step) {\
    x += step; \
    if ((e += dx) > 0) \
    { \
	x += sign; \
	e -= dy; \
    } \
}
    sy += y;
    DRAW_DEBUG ((DEBUG_POLYGON, "Starting polygon at %d", sy));
    for (;;)
    {
	DRAW_DEBUG ((DEBUG_POLYGON, "vertex1 0x%x vertex2 0x%x y %d vy1 %d vy2 %d",
		     vertex1, vertex2,
		     y, intToY(vertex1), intToY (vertex2)));
	if (y == intToY(vertex1))
	{
	    DRAW_DEBUG ((DEBUG_POLYGON, "Find next -- vertext"));
	    do
	    {
	    	if (vertex1p == (int *) ptsIn)
		    vertex1p = endp;
	    	c = *--vertex1p;
	    	Setup (c,x1,vertex1,dx1,dy1,e1,sign1,step1);
		DRAW_DEBUG ((DEBUG_POLYGON, "-- vertex 0x%x y %d",
			     vertex1, intToY(vertex1)));
	    } while (y >= intToY(vertex1));
	    h = dy1;
	}
	else
	{
	    Step(x1,dx1,dy1,e1,sign1,step1)
	    h = intToY(vertex1) - y;
	}
	if (y == intToY(vertex2))
	{
	    DRAW_DEBUG ((DEBUG_POLYGON, "Find next ++ vertext"));
	    do
	    {
	    	c = *vertex2p++;
	    	if (vertex2p == endp)
		    vertex2p = (int *) ptsIn;
	    	Setup (c,x2,vertex2,dx2,dy2,e2,sign2,step2)
		DRAW_DEBUG ((DEBUG_POLYGON, "++ vertex 0x%x y %d",
			     vertex1, intToY(vertex1)));
	    } while (y >= intToY(vertex2));
	    if (dy2 < h)
		h = dy2;
	}
	else
	{
	    Step(x2,dx2,dy2,e2,sign2,step2)
	    if ((c = (intToY(vertex2) - y)) < h)
		h = c;
	}
	DRAW_DEBUG ((DEBUG_POLYGON, "This band %d", h));
	/* fill spans for this segment */
	for (;;)
	{
	    nmiddle = x2 - x1;
	    DRAW_DEBUG ((DEBUG_POLYGON, "This span %d->%d", x1, x2));
	    if (nmiddle)
	    {
		l = x1;
		if (nmiddle < 0)
		{
		    nmiddle = -nmiddle;
		    l = x2;
		}
		_s3SolidRect(s3,l,sy,nmiddle,1);
	    }
	    y++;
	    sy++;
	    if (!--h)
		break;
	    Step(x1,dx1,dy1,e1,sign1,step1)
	    Step(x2,dx2,dy2,e2,sign2,step2)
	}
	if (y == maxy)
	    break;
    }
    MarkSyncS3 (pDrawable->pScreen);
}

void
s3PolyGlyphBltClipped (DrawablePtr pDrawable,
		       GCPtr pGC,
		       int x, int y, 
		       unsigned int nglyph,
		       CharInfoPtr *ppciInit, 
		       pointer pglyphBase)
{
    SetupS3(pDrawable->pScreen);
    int		    h;
    int		    w;
    int		    xBack, yBack;
    int		    hBack, wBack;
    int		    lw;
    FontPtr	    pfont = pGC->font;
    CharInfoPtr	    pci;
    unsigned long   *bits;
    BoxPtr	    extents;
    BoxRec	    bbox;
    CARD32	    b;
    CharInfoPtr	    *ppci;
    FbGCPrivPtr	    fbPriv = fbGetGCPrivate(pGC);
    RegionPtr	    pClip = fbGetCompositeClip(pGC);
    BoxPtr	    pBox;
    int		    nbox;
    int		    x1, y1, x2, y2;
    unsigned char   alu;
    Bool	    set;
    PixTransDeclare;

    s3SetGlobalBitmap (pDrawable->pScreen, s3GCMap (pGC));
    x += pDrawable->x;
    y += pDrawable->y;

    if (pglyphBase == (pointer) 1)
    {
	xBack = x;
	yBack = y - FONTASCENT(pGC->font);
	wBack = 0;
	hBack = FONTASCENT(pGC->font) + FONTDESCENT(pGC->font);
	if (hBack)
	{
	    h = nglyph;
	    ppci = ppciInit;
	    while (h--)
		wBack += (*ppci++)->metrics.characterWidth;
	}
	if (wBack < 0)
	{
	    xBack = xBack + wBack;
	    wBack = -wBack;
	}
	if (hBack < 0)
	{
	    yBack = yBack + hBack;
	    hBack = -hBack;
	}
	alu = GXcopy;
    }
    else
    {
	wBack = 0;
	alu = pGC->alu;
    }
    
    if (wBack)
    {
	_s3SetSolidFill (s3, pGC->bgPixel, GXcopy, pGC->planemask);
	for (nbox = REGION_NUM_RECTS (pClip),
	     pBox = REGION_RECTS (pClip);
	     nbox--;
	     pBox++)
	{
	    x1 = xBack;
	    x2 = xBack + wBack;
	    y1 = yBack;
	    y2 = yBack + hBack;
	    if (x1 < pBox->x1) x1 = pBox->x1;
	    if (x2 > pBox->x2) x2 = pBox->x2;
	    if (y1 < pBox->y1) y1 = pBox->y1;
	    if (y2 > pBox->y2) y2 = pBox->y2;
	    if (x1 < x2 && y1 < y2)
	    {
		_s3SolidRect (s3, x1, y1, x2 - x1, y2 - y1);
	    }
	}
	MarkSyncS3 (pDrawable->pScreen);
    }
    ppci = ppciInit;
    set = FALSE;
    while (nglyph--)
    {
	pci = *ppci++;
	h = pci->metrics.ascent + pci->metrics.descent;
	w = pci->metrics.rightSideBearing - pci->metrics.leftSideBearing;
	x1 = x + pci->metrics.leftSideBearing;
	y1 = y - pci->metrics.ascent;
	bbox.x1 = x1;
	bbox.y1 = y1;
	bbox.x2 = x1 + w;
	bbox.y2 = y1 + h;
	switch (RECT_IN_REGION(pGC->pScreen, pClip, &bbox))
	{
	case rgnIN:
#if 1
	    lw = h * ((w + 31) >> 5);
	    if (lw)
	    {
		if (!set)
		{
		    _s3SetTransparentPlaneBlt (s3, alu, pGC->planemask, pGC->fgPixel);
		    set = TRUE;
		}
		_s3PlaneBlt(s3,
			    x + pci->metrics.leftSideBearing,
			    y - pci->metrics.ascent,
			    w, h);
		bits = (unsigned long *) pci->bits;
		PixTransStart (lw);
		while (lw--) 
		{
		    b = *bits++;
		    S3AdjustBits32 (b);
		    PixTransStore(b);
		}
		MarkSyncS3 (pDrawable->pScreen);
	    }
	    break;
#endif
	case rgnPART:
	    set = FALSE;
	    CheckSyncS3 (pDrawable->pScreen);
	    fbPutXYImage (pDrawable,
			  pClip,
			  fbPriv->fg,
			  fbPriv->bg,
			  fbPriv->pm,
			  alu,
			  FALSE,
			  x1, y1,
			  w, h,
			  (FbStip *) pci->bits,
			  (w + 31) >> 5,
			  0);
	    break;
	case rgnOUT:
	    break;
	}
	x += pci->metrics.characterWidth;
    }
}
		       
/*
 * Blt glyphs using S3 image transfer register, this does both
 * poly glyph blt and image glyph blt (when pglyphBase == 1)
 */

void
s3PolyGlyphBlt (DrawablePtr pDrawable, 
		GCPtr pGC, 
		int x, int y, 
		unsigned int nglyph,
		CharInfoPtr *ppciInit, 
		pointer pglyphBase)
{
    SetupS3(pDrawable->pScreen);
    int		    h;
    int		    w;
    int		    xBack, yBack;
    int		    hBack, wBack;
    int		    lw;
    FontPtr	    pfont = pGC->font;
    CharInfoPtr	    pci;
    unsigned long   *bits;
    BoxPtr	    extents;
    BoxRec	    bbox;
    CARD32	    b;
    CharInfoPtr	    *ppci;
    unsigned char   alu;
    PixTransDeclare;

    s3SetGlobalBitmap (pDrawable->pScreen, s3GCMap (pGC));
    x += pDrawable->x;
    y += pDrawable->y;

    /* compute an approximate (but covering) bounding box */
    ppci = ppciInit;
    w = 0;
    h = nglyph;
    while (h--)
	w += (*ppci++)->metrics.characterWidth;
    if (w < 0)
    {
	bbox.x1 = x + w;
	bbox.x2 = x;
    }
    else
    {
	bbox.x1 = x;
	bbox.x2 = x + w;
    }
    w = FONTMINBOUNDS(pfont,leftSideBearing);
    if (w < 0)
	bbox.x1 += w;
    w = FONTMAXBOUNDS(pfont, rightSideBearing) - FONTMINBOUNDS(pfont, characterWidth);
    if (w > 0)
	bbox.x2 += w;
    bbox.y1 = y - FONTMAXBOUNDS(pfont,ascent);
    bbox.y2 = y + FONTMAXBOUNDS(pfont,descent);
    
    DRAW_DEBUG ((DEBUG_TEXT, "PolyGlyphBlt %d box is %d %d", nglyph,
		 bbox.x1, bbox.x2));
    switch (RECT_IN_REGION(pGC->pScreen, fbGetCompositeClip(pGC), &bbox))
    {
    case rgnIN:
	break;
    case rgnPART:
	s3PolyGlyphBltClipped(pDrawable, pGC, x - pDrawable->x,
			      y - pDrawable->y,
			      nglyph, ppciInit, pglyphBase);
    case rgnOUT:
	return;
    }
    
    if (pglyphBase == (pointer) 1)
    {
	xBack = x;
	yBack = y - FONTASCENT(pGC->font);
	wBack = 0;
	hBack = FONTASCENT(pGC->font) + FONTDESCENT(pGC->font);
	if (hBack)
	{
	    h = nglyph;
	    ppci = ppciInit;
	    while (h--)
		wBack += (*ppci++)->metrics.characterWidth;
	}
	if (wBack < 0)
	{
	    xBack = xBack + wBack;
	    wBack = -wBack;
	}
	if (hBack < 0)
	{
	    yBack = yBack + hBack;
	    hBack = -hBack;
	}
	alu = GXcopy;
    }
    else
    {
	wBack = 0;
	alu = pGC->alu;
    }
    
    if (wBack)
    {
	_s3SetSolidFill (s3, pGC->bgPixel, GXcopy, pGC->planemask);
	_s3SolidRect (s3, xBack, yBack, wBack, hBack);
    }
    _s3SetTransparentPlaneBlt (s3, alu, pGC->planemask, pGC->fgPixel);
    ppci = ppciInit;
    while (nglyph--)
    {
	pci = *ppci++;
	h = pci->metrics.ascent + pci->metrics.descent;
	w = pci->metrics.rightSideBearing - pci->metrics.leftSideBearing;
	lw = h * ((w + 31) >> 5);
	if (lw)
	{
	    _s3PlaneBlt(s3,
			x + pci->metrics.leftSideBearing,
			y - pci->metrics.ascent,
			w, h);
	    bits = (unsigned long *) pci->bits;
	    PixTransStart(lw);
	    while (lw--) 
	    {
		b = *bits++;
		S3AdjustBits32 (b);
		PixTransStore(b);
	    }
	}
	x += pci->metrics.characterWidth;
    }
    MarkSyncS3 (pDrawable->pScreen);
}

void
s3ImageGlyphBlt (DrawablePtr pDrawable, 
		GCPtr pGC, 
		int x, int y, 
		unsigned int nglyph, 
		CharInfoPtr *ppci, 
		pointer pglyphBase)
{
    s3PolyGlyphBlt (pDrawable, pGC, x, y, nglyph, ppci, (pointer) 1);
}

/*
 * Blt TE fonts using S3 image transfer.  Differs from
 * above in that it doesn't need to fill a solid rect for
 * the background and it can draw multiple characters at a time
 */

void
s3ImageTEGlyphBlt (DrawablePtr pDrawable, GCPtr pGC,
		   int xInit, int yInit,
		   unsigned int nglyph,
		   CharInfoPtr *ppci,
		   pointer pglyphBase)
{
    SetupS3(pDrawable->pScreen);
    int		    x, y;
    int		    h, lw, lwTmp;
    int		    w;
    FontPtr	    pfont = pGC->font;
    unsigned long   *char1, *char2, *char3, *char4;
    int		    widthGlyphs, widthGlyph;
    BoxRec	    bbox;
    CARD32	    tmp;
    PixTransDeclare;

    s3SetGlobalBitmap (pDrawable->pScreen, s3GCMap (pGC));
    widthGlyph = FONTMAXBOUNDS(pfont,characterWidth);
    if (!widthGlyph)
	return;
    
    h = FONTASCENT(pfont) + FONTDESCENT(pfont);
    if (!h)
	return;
    
    DRAW_DEBUG ((DEBUG_TEXT, "ImageTEGlyphBlt chars are %d %d",
		 widthGlyph, h));
    
    x = xInit + FONTMAXBOUNDS(pfont,leftSideBearing) + pDrawable->x;
    y = yInit - FONTASCENT(pfont) + pDrawable->y;
    
    bbox.x1 = x;
    bbox.x2 = x + (widthGlyph * nglyph);
    bbox.y1 = y;
    bbox.y2 = y + h;

    switch (RECT_IN_REGION(pGC->pScreen, fbGetCompositeClip(pGC), &bbox))
    {
      case rgnIN:
	break;
      case rgnPART:
	if (pglyphBase == (pointer) 1)
	    pglyphBase = 0;
	else
	    pglyphBase = (pointer) 1;
	s3PolyGlyphBltClipped(pDrawable, pGC, 
			      xInit,
			      yInit,
			      nglyph, ppci, 
			      pglyphBase);
      case rgnOUT:
	return;
    }

    if (pglyphBase == (pointer) 1)
    {
	_s3SetTransparentPlaneBlt (s3, pGC->alu, pGC->planemask, pGC->fgPixel);
    }
    else
    {
	_s3SetOpaquePlaneBlt (s3, GXcopy, pGC->planemask, pGC->fgPixel, pGC->bgPixel);
    }

#if BITMAP_BIT_ORDER == LSBFirst
#define SHIFT	<<
#else
#define SHIFT	>>
#endif
    
#define LoopIt(count, w, loadup, fetch) \
    while (nglyph >= count) \
    { \
	nglyph -= count; \
	_s3PlaneBlt (s3, x, y, w, h); \
	x += w; \
	loadup \
	lwTmp = h; \
	PixTransStart(h); \
	while (lwTmp--) { \
	    tmp = fetch; \
	    S3AdjustBits32(tmp); \
	    PixTransStore(tmp); \
	} \
    }

    if (widthGlyph <= 8)
    {
	widthGlyphs = widthGlyph << 2;
	LoopIt(4, widthGlyphs,
	       char1 = (unsigned long *) (*ppci++)->bits;
	       char2 = (unsigned long *) (*ppci++)->bits;
	       char3 = (unsigned long *) (*ppci++)->bits;
	       char4 = (unsigned long *) (*ppci++)->bits;,
	       (*char1++ | ((*char2++ | ((*char3++ | (*char4++
						      SHIFT widthGlyph))
					 SHIFT widthGlyph))
			    SHIFT widthGlyph)))
    }
    else if (widthGlyph <= 10)
    {
	widthGlyphs = (widthGlyph << 1) + widthGlyph;
	LoopIt(3, widthGlyphs,
	       char1 = (unsigned long *) (*ppci++)->bits;
	       char2 = (unsigned long *) (*ppci++)->bits;
	       char3 = (unsigned long *) (*ppci++)->bits;,
	       (*char1++ | ((*char2++ | (*char3++ SHIFT widthGlyph)) SHIFT widthGlyph)))
    }
    else if (widthGlyph <= 16)
    {
	widthGlyphs = widthGlyph << 1;
	LoopIt(2, widthGlyphs,
	       char1 = (unsigned long *) (*ppci++)->bits;
	       char2 = (unsigned long *) (*ppci++)->bits;,
	       (*char1++ | (*char2++ SHIFT widthGlyph)))
    }
    lw = h * ((widthGlyph + 31) >> 5);
    while (nglyph--) 
    {
	_s3PlaneBlt (s3, x, y, widthGlyph, h);
	x += widthGlyph;
	char1 = (unsigned long *) (*ppci++)->bits;
	lwTmp = lw;
	PixTransStart(lw);
	while (lwTmp--)
	{
	    tmp = *char1++;
	    S3AdjustBits32(tmp);
	    PixTransStore(tmp);
	}
    }
    MarkSyncS3 (pDrawable->pScreen);
}

void
s3PolyTEGlyphBlt (DrawablePtr pDrawable, GCPtr pGC, 
		  int x, int y, 
		  unsigned int nglyph, CharInfoPtr *ppci, 
		  pointer pglyphBase)
{
    s3ImageTEGlyphBlt (pDrawable, pGC, x, y, nglyph, ppci, (pointer) 1);
}

Bool
_s3Segment (DrawablePtr	pDrawable,
	    GCPtr	pGC,
	    int		x1,
	    int		y1,
	    int		x2,
	    int		y2,
	    Bool	drawLast,
	    Bool	s3Set)
{
    SetupS3(pDrawable->pScreen);
    FbGCPrivPtr	pPriv = fbGetGCPrivate(pGC);
    RegionPtr	pClip = fbGetCompositeClip(pGC);
    BoxPtr	pBox;
    int		nBox;
    int		adx;		/* abs values of dx and dy */
    int		ady;
    int		signdx;		/* sign of dx and dy */
    int		signdy;
    int		e, e1, e2;		/* bresenham error and increments */
    int		len;			/* length of segment */
    int		axis;			/* major axis */
    int		octant;
    int		cmd;
    unsigned int bias = miGetZeroLineBias(pDrawable->pScreen);
    unsigned int oc1;	/* outcode of point 1 */
    unsigned int oc2;	/* outcode of point 2 */

    CalcLineDeltas(x1, y1, x2, y2, adx, ady, signdx, signdy,
		   1, 1, octant);
    
    cmd = LASTPIX;
    
    if (adx > ady)
    {
	axis = X_AXIS;
	e1 = ady << 1;
	e2 = e1 - (adx << 1);
	e = e1 - adx;
	len = adx;
    }
    else
    {
	cmd |= YMAJAXIS;
	axis = Y_AXIS;
	e1 = adx << 1;
	e2 = e1 - (ady << 1);
	e = e1 - ady;
	SetYMajorOctant(octant);
	len = ady;
    }

    /* S3 line drawing hardware has limited resolution for error terms */
    if (len >= 4096)
    {
	int dashOff = 0;
	
	KdCheckSync (pDrawable->pScreen);
	fbSegment (pDrawable, pGC, x1, y1, x2, y2, drawLast, &dashOff);
	return FALSE;
    }

    FIXUP_ERROR (e, octant, bias);
    
    nBox = REGION_NUM_RECTS (pClip);
    pBox = REGION_RECTS (pClip);

    if (signdx > 0)
	cmd |= INC_X;
    if (signdy > 0)
	cmd |= INC_Y;
	
    /* we have bresenham parameters and two points.
       all we have to do now is clip and draw.
    */

    if (drawLast)
	len++;
    while(nBox--)
    {
	oc1 = 0;
	oc2 = 0;
	OUTCODES(oc1, x1, y1, pBox);
	OUTCODES(oc2, x2, y2, pBox);
	if ((oc1 | oc2) == 0)
	{
	    if (!s3Set)
	    {
		s3SetGlobalBitmap (pDrawable->pScreen, s3GCMap (pGC));
		_s3SetSolidFill (s3, pGC->fgPixel, pGC->alu, pGC->planemask);
		s3Set = TRUE;
	    }
	    _s3SetCur (s3, x1, y1);
	    _s3ClipLine (s3, cmd, e1, e2, e, len);
	    break;
	}
	else if (oc1 & oc2)
	{
	    pBox++;
	}
	else
	{
	    int new_x1 = x1, new_y1 = y1, new_x2 = x2, new_y2 = y2;
	    int clip1 = 0, clip2 = 0;
	    int clipdx, clipdy;
	    int err;
	    
	    if (miZeroClipLine(pBox->x1, pBox->y1, pBox->x2-1,
			       pBox->y2-1,
			       &new_x1, &new_y1, &new_x2, &new_y2,
			       adx, ady, &clip1, &clip2,
			       octant, bias, oc1, oc2) == -1)
	    {
		pBox++;
		continue;
	    }

	    if (axis == X_AXIS)
		len = abs(new_x2 - new_x1);
	    else
		len = abs(new_y2 - new_y1);
	    if (clip2 != 0 || drawLast)
		len++;
	    if (len)
	    {
		/* unwind bresenham error term to first point */
		err = e;
		if (clip1)
		{
		    clipdx = abs(new_x1 - x1);
		    clipdy = abs(new_y1 - y1);
		    if (axis == X_AXIS)
			err  += (e2 - e1) * clipdy + e1 * clipdx;
		    else
			err  += (e2 - e1) * clipdx + e1 * clipdy;
		}
		if (!s3Set)
		{
		    s3SetGlobalBitmap (pDrawable->pScreen, s3GCMap (pGC));
		    _s3SetSolidFill (s3, pGC->fgPixel, pGC->alu, pGC->planemask);
		    s3Set = TRUE;
		}
		_s3SetCur (s3, new_x1, new_y1);
		_s3ClipLine (s3, cmd, e1, e2, err, len);
	    }
	    pBox++;
	}
    } /* while (nBox--) */
    return s3Set;
}

void
s3Polylines (DrawablePtr pDrawable, GCPtr pGC,
	     int mode, int npt, DDXPointPtr ppt)
{
    SetupS3(pDrawable->pScreen);
    int		x, y, nx, ny;
    int		ox = pDrawable->x, oy = pDrawable->y;
    Bool	s3Set = FALSE;
    
    if (!npt)
	return;
    
    x = ppt->x + ox;
    y = ppt->y + oy;
    while (--npt)
    {
	++ppt;
	if (mode == CoordModePrevious)
	{
	    nx = x + ppt->x;
	    ny = y + ppt->y;
	}
	else
	{
	    nx = ppt->x + ox;
	    ny = ppt->y + oy;
	}
	s3Set = _s3Segment (pDrawable, pGC, x, y, nx, ny,
			    npt == 1 && pGC->capStyle != CapNotLast, 
			    s3Set);
	x = nx;
	y = ny;
    }
    if (s3Set)
	MarkSyncS3 (pDrawable->pScreen);
}

void
s3PolySegment (DrawablePtr pDrawable, GCPtr pGC, 
	       int nsegInit, xSegment *pSegInit)
{
    SetupS3(pDrawable->pScreen);
    int		x, y;
    int		ox = pDrawable->x, oy = pDrawable->y;
    RegionPtr	pClip = fbGetCompositeClip (pGC);
    BoxPtr	pBox;
    int		nbox;
    int		nseg;
    xSegment	*pSeg;
    int		dx, dy;
    int		maj, min, len, inc;
    int		t;
    CARD32	cmd;
    CARD32	init_cmd;
    Bool	drawLast;
    Bool	s3Set = FALSE;
    
    drawLast = pGC->capStyle != CapNotLast;
    
    for (nseg = nsegInit, pSeg = pSegInit; nseg--; pSeg++)
    {
	s3Set = _s3Segment (pDrawable, pGC, pSeg->x1 + ox, pSeg->y1 + oy,
			    pSeg->x2 + ox, pSeg->y2 + oy, drawLast, s3Set);
		    
    }
    if (s3Set)
	MarkSyncS3 (pDrawable->pScreen);
}

/*
 * Check to see if a pattern can be painted with the S3
 */

#define _s3CheckPatternSize(s)	((s) <= S3_TILE_SIZE && ((s) & ((s) - 1)) == 0)
#define s3CheckPattern(w,h) (_s3CheckPatternSize(w) && _s3CheckPatternSize(h))
			     
Bool
s3AllocPattern (ScreenPtr pScreen,
		int ma,
		PixmapPtr pPixmap, 
		int xorg, int yorg,
		int fillStyle, Pixel fg, Pixel bg,
		s3PatternPtr *ppPattern)
{
    KdScreenPriv(pScreen);
    s3ScreenInfo(pScreenPriv);
    s3PatternPtr    pPattern;
    
    if (s3s->fb[ma].patterns.cache && fillStyle != FillSolid &&
	s3CheckPattern (pPixmap->drawable.width, pPixmap->drawable.height))
    {
	if (!(pPattern = *ppPattern))
	{
	    pPattern = (s3PatternPtr) xalloc (sizeof (s3PatternRec));
	    if (!pPattern)
		return FALSE;
	    *ppPattern = pPattern;
	}
	
	pPattern->cache = 0;
	pPattern->id = 0;
	pPattern->pPixmap = pPixmap;
	pPattern->fillStyle = fillStyle;
	pPattern->xrot = (-xorg) & (S3_TILE_SIZE-1);
	pPattern->yrot = (-yorg) & (S3_TILE_SIZE-1);
	pPattern->fore = fg;
	pPattern->back = bg;
	return TRUE;
    }
    else
    {
	if (*ppPattern)
	{
	    xfree (*ppPattern);
	    *ppPattern = 0;
	}
	return FALSE;
    }
}

void
s3CheckGCFill (GCPtr pGC)
{
    s3PrivGCPtr	    s3Priv = s3GetGCPrivate (pGC);
    PixmapPtr	    pPixmap;

    switch (pGC->fillStyle) {
    case FillSolid:
	pPixmap = 0;
	break;
    case FillOpaqueStippled:
    case FillStippled:
	pPixmap = pGC->stipple;
	break;
    case FillTiled:
	pPixmap = pGC->tile.pixmap;
	break;
    }
    s3AllocPattern (pGC->pScreen,
		    s3GCMap(pGC),
		    pPixmap,
		    pGC->patOrg.x + pGC->lastWinOrg.x,
		    pGC->patOrg.y + pGC->lastWinOrg.y,
		    pGC->fillStyle, pGC->fgPixel, pGC->bgPixel,
		    &s3Priv->pPattern);
}

void
s3MoveGCFill (GCPtr pGC)
{
    s3PrivGCPtr	    s3Priv = s3GetGCPrivate (pGC);
    int		    xorg, yorg;
    s3PatternPtr    pPattern;
    
    if (pPattern = s3Priv->pPattern)
    {
	/*
	 * Reset origin
	 */
	xorg = pGC->patOrg.x + pGC->lastWinOrg.x;
	yorg = pGC->patOrg.y + pGC->lastWinOrg.y;
	pPattern->xrot = (-xorg) & (S3_TILE_SIZE - 1);
	pPattern->yrot = (-yorg) & (S3_TILE_SIZE - 1);
	/*
	 * Invalidate cache entry
	 */
	pPattern->id = 0;
	pPattern->cache = 0;
    }
}

/*
 * S3 Patterns.  These are always full-depth images, stored in off-screen
 * memory.
 */

Pixel
s3FetchPatternPixel (s3PatternPtr pPattern, int x, int y)
{
    CARD8	*src;
    CARD16	*src16;
    CARD32	*src32;
    PixmapPtr	pPixmap = pPattern->pPixmap;
    
    x = (x + pPattern->xrot) % pPixmap->drawable.width;
    y = (y + pPattern->yrot) % pPixmap->drawable.height;
    src = (CARD8 *) pPixmap->devPrivate.ptr + y * pPixmap->devKind;
    switch (pPixmap->drawable.bitsPerPixel) {
    case 1:
	return (src[x>>3] >> (x & 7)) & 1 ? 0xffffffff : 0x00;
    case 4:
	if (x & 1)
	    return src[x>>1] >> 4;
	else
	    return src[x>>1] & 0xf;
    case 8:
	return src[x];
    case 16:
	src16 = (CARD16 *) src;
	return src16[x];
    case 32:
	src32 = (CARD32 *) src;
	return src32[x];
    }
}

/*
 * Place pattern image on screen; done with S3 locked
 */
void
_s3PutPattern (ScreenPtr pScreen, int ma, s3PatternPtr pPattern)
{
    SetupS3(pScreen);
    s3ScreenInfo(pScreenPriv);
    int		    x, y;
    CARD8	    *dstLine, *dst8;
    CARD16	    *dst16;
    CARD32	    *dst32;
    S3PatternCache  *cache = pPattern->cache;
#ifdef S3_TRIO
    int		    fb = 0;
#else
    int		    fb = s3s->fbmap[ma];
#endif
    
    DRAW_DEBUG ((DEBUG_PATTERN, "_s3PutPattern 0x%x id %d to %d %d",
		pPattern, pPattern->id, cache->x, cache->y));
    
    dstLine = (pScreenPriv->screen->fb[fb].frameBuffer + 
	       cache->y * pScreenPriv->screen->fb[fb].byteStride + 
	       cache->x * pScreenPriv->bytesPerPixel[fb]);
    
    CheckSyncS3 (pScreen);
    
    for (y = 0; y < S3_TILE_SIZE; y++)
    {
	switch (pScreenPriv->screen->fb[fb].bitsPerPixel) {
	case 8:
	    dst8 = dstLine;
	    for (x = 0; x < S3_TILE_SIZE; x++)
		*dst8++ = s3FetchPatternPixel (pPattern, x, y);
	    DRAW_DEBUG ((DEBUG_PATTERN, "%c%c%c%c%c%c%c%c",
			 dstLine[0] ? 'X' : ' ',
			 dstLine[1] ? 'X' : ' ',
			 dstLine[2] ? 'X' : ' ',
			 dstLine[3] ? 'X' : ' ',
			 dstLine[4] ? 'X' : ' ',
			 dstLine[5] ? 'X' : ' ',
			 dstLine[6] ? 'X' : ' ',
			 dstLine[7] ? 'X' : ' '));
	    break;
	case 16:
	    dst16 = (CARD16 *) dstLine;
	    for (x = 0; x < S3_TILE_SIZE; x++)
		*dst16++ = s3FetchPatternPixel (pPattern, x, y);
	    break;
	case 32:
	    dst32 = (CARD32 *) dstLine;
	    for (x = 0; x < S3_TILE_SIZE; x++)
		*dst32++ = s3FetchPatternPixel (pPattern, x, y);
	    break;
	}
	dstLine += pScreenPriv->screen->fb[fb].byteStride;
    }
}

/*
 * Load a stipple to off-screen memory; done with S3 locked
 */
void
_s3LoadPattern (ScreenPtr pScreen, int ma, s3PatternPtr pPattern)
{
    SetupS3(pScreen);
    s3ScreenInfo(pScreenPriv);
    S3PatternCache  *cache;

    DRAW_DEBUG((DEBUG_PATTERN,
	       "s3LoadPattern 0x%x id %d cache 0x%x cacheid %d",
	       pPattern, pPattern->id, pPattern->cache, 
	       pPattern->cache ? pPattern->cache->id : -1));
    /*
     * Check to see if its still loaded
     */
    cache = pPattern->cache;
    if (cache && cache->id == pPattern->id)
	return;
    /*
     * Lame replacement strategy; assume we'll have plenty of room.
     */
    cache = &s3s->fb[ma].patterns.cache[s3s->fb[ma].patterns.last_used];
    if (++s3s->fb[ma].patterns.last_used == s3s->fb[ma].patterns.ncache)
	s3s->fb[ma].patterns.last_used = 0;
    cache->id = ++s3s->fb[ma].patterns.last_id;
    pPattern->id = cache->id;
    pPattern->cache = cache;
    _s3PutPattern (pScreen, ma, pPattern);
}

void
s3DestroyGC (GCPtr pGC)
{
    s3PrivGCPtr	    s3Priv = s3GetGCPrivate (pGC);

    if (s3Priv->pPattern)
	xfree (s3Priv->pPattern);
    miDestroyGC (pGC);
}

GCFuncs	s3GCFuncs = {
    s3ValidateGC,
    miChangeGC,
    miCopyGC,
    s3DestroyGC,
    miChangeClip,
    miDestroyClip,
    miCopyClip
};

int
s3CreateGC (GCPtr pGC)
{
    KdScreenPriv(pGC->pScreen);
    s3ScreenInfo(pScreenPriv);
    s3PrivGCPtr  s3Priv;
    
    if (!fbCreateGC (pGC))
	return FALSE;

    if (pGC->depth != 1)
	pGC->funcs = &s3GCFuncs;
    
    s3Priv = s3GetGCPrivate(pGC);
    s3Priv->type = DRAWABLE_PIXMAP;
    s3Priv->pPattern = 0;
#ifndef S3_TRIO
    if (pGC->depth == s3s->primary_depth)
	s3Priv->ma = 0;
    else
	s3Priv->ma = 1;
#endif
    return TRUE;
}

Bool
s3CreateWindow (WindowPtr pWin)
{
    KdScreenPriv(pWin->drawable.pScreen);
    s3ScreenInfo(pScreenPriv);
    
    pWin->devPrivates[s3WindowPrivateIndex].ptr = 0;
    return KdCreateWindow (pWin);
}

Bool
s3DestroyWindow (WindowPtr pWin)
{
    s3PatternPtr pPattern;
    if (pPattern = s3GetWindowPrivate(pWin))
	xfree (pPattern);
    return fbDestroyWindow (pWin);
}

Bool
s3ChangeWindowAttributes (WindowPtr pWin, Mask mask)
{
    KdScreenPriv(pWin->drawable.pScreen);
    Bool	    ret;
    s3PatternPtr    pPattern;
    PixmapPtr	    pPixmap;
    int		    fillStyle;

    ret = fbChangeWindowAttributes (pWin, mask);
    if (mask & CWBackPixmap)
    {
	if (pWin->backgroundState == BackgroundPixmap)
	{
	    pPixmap = pWin->background.pixmap;
	    fillStyle = FillTiled;
	}
	else
	{
	    pPixmap = 0;
	    fillStyle = FillSolid;
	}
	pPattern = s3GetWindowPrivate(pWin);
	s3AllocPattern (pWin->drawable.pScreen,
			s3DrawMap (&pWin->drawable),
			pPixmap, 
			pWin->drawable.x, pWin->drawable.y,
			fillStyle, 0, 0, &pPattern);
	DRAW_DEBUG ((DEBUG_PAINT_WINDOW, "Background pattern 0x%x pixmap 0x%x style %d",
		    pPattern, pPixmap, fillStyle));
	s3SetWindowPrivate (pWin, pPattern);
    }
    return ret;
}


#ifndef S3_TRIO
void
s3PaintKey (DrawablePtr	pDrawable,
	    RegionPtr	pRegion,
	    CARD32	pixel,
	    int		fb)
{
    SetupS3 (pDrawable->pScreen);
    s3ScreenInfo (pScreenPriv);
    int	    nBox = REGION_NUM_RECTS(pRegion);
    BoxPtr  pBox = REGION_RECTS(pRegion);
    int	    ma;
    
    if (!nBox)
	return;
    
    for (ma = 0; s3s->fbmap[ma] >= 0; ma++)
	if (s3s->fbmap[ma] == fb)
	    break;
    s3SetGlobalBitmap (pDrawable->pScreen, ma);
    _s3SetSolidFill (s3, pixel, GXcopy, 0xffffffff);
    while (nBox--) 
    {
	_s3SolidRect(s3,pBox->x1,pBox->y1,pBox->x2-pBox->x1,pBox->y2-pBox->y1);
	pBox++;
    }
    MarkSyncS3 (pDrawable->pScreen);
}
#endif

void
s3PaintWindow(WindowPtr pWin, RegionPtr pRegion, int what)
{
    SetupS3(pWin->drawable.pScreen);
    s3ScreenInfo(pScreenPriv);
    s3PatternPtr    pPattern;

    DRAW_DEBUG ((DEBUG_PAINT_WINDOW, "s3PaintWindow 0x%x extents %d %d %d %d n %d",
		 pWin->drawable.id,
		 pRegion->extents.x1, pRegion->extents.y1,
		 pRegion->extents.x2, pRegion->extents.y2,
		 REGION_NUM_RECTS(pRegion)));
    if (!REGION_NUM_RECTS(pRegion)) 
	return;
    switch (what) {
    case PW_BACKGROUND:
	switch (pWin->backgroundState) {
	case None:
	    return;
	case ParentRelative:
	    do {
		pWin = pWin->parent;
	    } while (pWin->backgroundState == ParentRelative);
	    (*pWin->drawable.pScreen->PaintWindowBackground)(pWin, pRegion,
							     what);
	    return;
	case BackgroundPixmap:
	    pPattern = s3GetWindowPrivate(pWin);
	    if (pPattern)
	    {
		s3FillBoxPattern ((DrawablePtr)pWin,
				  (int)REGION_NUM_RECTS(pRegion),
				  REGION_RECTS(pRegion),
				  GXcopy, ~0, pPattern);
		return;
	    }
	    break;
	case BackgroundPixel:
	    s3FillBoxSolid((DrawablePtr)pWin,
			     (int)REGION_NUM_RECTS(pRegion),
			     REGION_RECTS(pRegion),
			     pWin->background.pixel, GXcopy, ~0);
	    return;
    	}
    	break;
    case PW_BORDER:
#ifndef S3_TRIO
	if (s3s->fbmap[1] >= 0)
	    fbOverlayUpdateLayerRegion (pWin->drawable.pScreen,
					fbOverlayWindowLayer (pWin),
					pRegion);
#endif
	if (pWin->borderIsPixel)
	{
	    s3FillBoxSolid((DrawablePtr)pWin,
			     (int)REGION_NUM_RECTS(pRegion),
			     REGION_RECTS(pRegion),
			     pWin->border.pixel, GXcopy, ~0);
	    return;
	}
	break;
    }
    KdCheckPaintWindow (pWin, pRegion, what);
}

void
s3CopyWindowProc (DrawablePtr pSrcDrawable,
		  DrawablePtr pDstDrawable,
		  GCPtr       pGC,
		  BoxPtr      pboxOrig,
		  int         nboxOrig,
		  int         dx,
		  int         dy,
		  Bool        reverse,
		  Bool        upsidedown,
		  Pixel       bitplane,
		  void        *closure)
{
    SetupS3(pDstDrawable->pScreen);
    s3ScreenInfo(pScreenPriv);
    KdScreenInfo    *screen = pScreenPriv->screen;
    int		    srcX, srcY, dstX, dstY;
    int		    x1, x2;
    int		    w, h;
    int		    flags;
    int		    fb = (int) closure;
    int		    ma;
    BoxPtr	    pbox;
    int		    nbox;
    int		    bitsPerPixel;
    
#ifdef S3_TRIO
    ma = 0;
#else
    for (ma = 0; s3s->fbmap[ma] >= 0; ma++)
	if (s3s->fbmap[ma] == fb)
	    break;
#endif
    bitsPerPixel = screen->fb[fb].bitsPerPixel;
    if (bitsPerPixel == 24)
	dx *= 3;
    nbox = nboxOrig;
    pbox = pboxOrig;
    s3SetGlobalBitmap (pDstDrawable->pScreen, ma);
    _s3SetBlt(s3,GXcopy,~0);
    while (nbox--)
    {
	x1 = pbox->x1;
	x2 = pbox->x2;
	if (bitsPerPixel == 24)
	{
	    x1 *= 3;
	    x2 *= 3;
	}
	
	w = x2 - x1;
	h = pbox->y2 - pbox->y1;
	flags = 0;
	if (reverse)
	{
	    dstX = x2 - 1;
	}
	else
	{
	    dstX = x1;
	    flags |= INC_X;
	}
	srcX = dstX + dx;
	
	if (upsidedown)
	{
	    dstY = pbox->y2 - 1;
	}
	else
	{
	    dstY = pbox->y1;
	    flags |= INC_Y;
	}
	srcY = dstY + dy;
	
	_s3Blt (s3, srcX, srcY, dstX, dstY, w, h, flags);
	pbox++;
    }
    MarkSyncS3 (pDstDrawable->pScreen);
}

void 
s3CopyWindow(WindowPtr pWin, DDXPointRec ptOldOrg, RegionPtr prgnSrc)
{
    ScreenPtr	    pScreen = pWin->drawable.pScreen;
    KdScreenPriv (pScreen);
    s3ScreenInfo (pScreenPriv);
    KdScreenInfo    *screen = pScreenPriv->screen;
    RegionRec	    rgnDst;
    int		    dx, dy;
    WindowPtr	    pwinRoot;

    pwinRoot = WindowTable[pWin->drawable.pScreen->myNum];

    dx = ptOldOrg.x - pWin->drawable.x;
    dy = ptOldOrg.y - pWin->drawable.y;

    REGION_TRANSLATE(pWin->drawable.pScreen, prgnSrc, -dx, -dy);

    REGION_INIT (pWin->drawable.pScreen, &rgnDst, NullBox, 0);

    REGION_INTERSECT(pWin->drawable.pScreen, &rgnDst,
		     &pWin->borderClip, prgnSrc);

    fbCopyRegion ((DrawablePtr)pwinRoot, (DrawablePtr)pwinRoot,
		  0,
		  &rgnDst, dx, dy, s3CopyWindowProc, 0, 0);
    
    REGION_UNINIT(pWin->drawable.pScreen, &rgnDst);
}

void
s3_24FillBoxSolid (DrawablePtr pDrawable, int nBox, BoxPtr pBox, 
		  unsigned long pixel, int alu, unsigned long planemask)
{
    SetupS3(pDrawable->pScreen);
    register int	r;
    int			x1, x2;

    s3SetGlobalBitmap (pDrawable->pScreen, s3DrawMap (pDrawable));
    _s3SetSolidFill(s3,pixel,alu,planemask);
    
    while (nBox--) {
	x1 = pBox->x1 * 3;
	x2 = pBox->x2 * 3;
	_s3SolidRect(s3,x1,pBox->y1,x2-x1,pBox->y2-pBox->y1);
	pBox++;
    }
    MarkSyncS3 (pDrawable->pScreen);
}

#define ok24(p)	(((p) & 0xffffff) == ((((p) & 0xff) << 16) | (((p) >> 8) & 0xffff)))

void
s3_24FillSpans (DrawablePtr pDrawable, GCPtr pGC, int n, 
		 DDXPointPtr ppt, int *pwidth, int fSorted)
{
    SetupS3(pDrawable->pScreen);
    int		    x, y, x1, y1, x2, y2;
    int		    width;
				/* next three parameters are post-clip */
    int		    nTmp;
    int		    *pwidthFree;/* copies of the pointers to free */
    DDXPointPtr	    pptFree;
    BoxPtr	    extents;
    RegionPtr	    pClip = fbGetCompositeClip (pGC);

    if (pGC->fillStyle != FillSolid || !ok24 (pGC->fgPixel) || !ok24(pGC->planemask))
    {
	KdCheckFillSpans (pDrawable,  pGC, n, ppt, pwidth, fSorted);
	return;
    }
    
    s3SetGlobalBitmap (pDrawable->pScreen, s3GCMap (pGC));
    if (REGION_NUM_RECTS(pClip) == 1)
    {
	extents = REGION_RECTS(pClip);
	x1 = extents->x1;
	x2 = extents->x2;
	y1 = extents->y1;
	y2 = extents->y2;
	_s3SetSolidFill(s3,pGC->fgPixel,pGC->alu,pGC->planemask);
	while (n--)
	{
	    y = ppt->y;
	    if (y1 <= y && y < y2)
	    {
		x = ppt->x;
		width = *pwidth;
		if (x < x1)
		{
		    width -= (x1 - x);
		    x = x1;
		}
		if (x2 < x + width)
		    width = x2 - x;
		if (width > 0)
		{
		    _s3SolidRect(s3,x*3,y,width*3,1);
		}
	    }
	    ppt++;
	    pwidth++;
	}
    }
    else
    {
	nTmp = n * miFindMaxBand(pClip);
	pwidthFree = (int *)ALLOCATE_LOCAL(nTmp * sizeof(int));
	pptFree = (DDXPointRec *)ALLOCATE_LOCAL(nTmp * sizeof(DDXPointRec));
	if(!pptFree || !pwidthFree)
	{
	    if (pptFree) DEALLOCATE_LOCAL(pptFree);
	    if (pwidthFree) DEALLOCATE_LOCAL(pwidthFree);
	    return;
	}
	n = miClipSpans(fbGetCompositeClip(pGC),
			ppt, pwidth, n,
			pptFree, pwidthFree, fSorted);
	pwidth = pwidthFree;
	ppt = pptFree;
	_s3SetSolidFill(s3,pGC->fgPixel,pGC->alu,pGC->planemask);
	while (n--)
	{
	    x = ppt->x;
	    y = ppt->y;
	    ppt++;
	    width = *pwidth++;
	    if (width)
	    {
		_s3SolidRect(s3,x*3,y,width*3,1);
	    }
	}
	DEALLOCATE_LOCAL(pptFree);
	DEALLOCATE_LOCAL(pwidthFree);
    }
    MarkSyncS3 (pDrawable->pScreen);
}

void
s3_24CopyNtoN (DrawablePtr  pSrcDrawable,
	       DrawablePtr  pDstDrawable,
	       GCPtr	    pGC,
	       BoxPtr	    pbox,
	       int	    nbox,
	       int	    dx,
	       int	    dy,
	       Bool	    reverse,
	       Bool	    upsidedown,
	       Pixel	    bitplane,
	       void	    *closure)
{
    SetupS3(pDstDrawable->pScreen);
    int	    srcX, srcY, dstX, dstY;
    int	    w, h;
    int	    flags;
    int	    x1, x2;
    
    if (sourceInvarient (pGC->alu))
    {
	s3_24FillBoxSolid (pDstDrawable, nbox, pbox, 0, pGC->alu, pGC->planemask);
	return;
    }
    
    s3SetGlobalBitmap (pDstDrawable->pScreen, s3GCMap (pGC));
    _s3SetBlt(s3,pGC->alu,pGC->planemask);
    DRAW_DEBUG ((DEBUG_RENDER, "s3CopyNtoN alu %d planemask 0x%x",
		 pGC->alu, pGC->planemask));
    dx *= 3;
    while (nbox--)
    {
	x1 = pbox->x1 * 3;
	x2 = pbox->x2 * 3;
	w = x2 - x1;
	h = pbox->y2 - pbox->y1;
	flags = 0;
	if (reverse)
	{
	    dstX = x2 - 1;
	}
	else
	{
	    dstX = x1;
	    flags |= INC_X;
	}
	srcX = dstX + dx;
	
	if (upsidedown)
	{
	    dstY = pbox->y2 - 1;
	}
	else
	{
	    dstY = pbox->y1;
	    flags |= INC_Y;
	}
	srcY = dstY + dy;
	
	_s3Blt (s3, srcX, srcY, dstX, dstY, w, h, flags);
	pbox++;
    }
    MarkSyncS3 (pSrcDrawable->pScreen);
}

RegionPtr
s3_24CopyArea(DrawablePtr pSrcDrawable, DrawablePtr pDstDrawable, GCPtr pGC,
	   int srcx, int srcy, int width, int height, int dstx, int dsty)
{
    SetupS3(pDstDrawable->pScreen);
    
    if (pSrcDrawable->type == DRAWABLE_WINDOW &&
	pDstDrawable->type == DRAWABLE_WINDOW &&
	ok24(pGC->planemask))
    {
	return fbDoCopy (pSrcDrawable, pDstDrawable, pGC, 
			 srcx, srcy, width, height, 
			 dstx, dsty, s3_24CopyNtoN, 0, 0);
    }
    return KdCheckCopyArea (pSrcDrawable, pDstDrawable, pGC, 
			    srcx, srcy, width, height, dstx, dsty);
}


#define NUM_STACK_RECTS	1024

void
s3_24PolyFillRect (DrawablePtr pDrawable, GCPtr pGC, 
		   int nrectFill, xRectangle *prectInit)
{
    s3GCPrivate(pGC);
    xRectangle	    *prect;
    RegionPtr	    prgnClip;
    register BoxPtr pbox;
    register BoxPtr pboxClipped;
    BoxPtr	    pboxClippedBase;
    BoxPtr	    pextent;
    BoxRec	    stackRects[NUM_STACK_RECTS];
    int		    numRects;
    int		    n;
    int		    xorg, yorg;
    int		    x, y;

    if (pGC->fillStyle != FillSolid || !ok24 (pGC->fgPixel) || !ok24(pGC->planemask))
    {
	KdCheckPolyFillRect (pDrawable, pGC, nrectFill, prectInit);
	return;
    }
    
    prgnClip = fbGetCompositeClip(pGC);
    xorg = pDrawable->x;
    yorg = pDrawable->y;
    
    if (xorg || yorg)
    {
	prect = prectInit;
	n = nrectFill;
	while(n--)
	{
	    prect->x += xorg;
	    prect->y += yorg;
	    prect++;
	}
    }
    
    prect = prectInit;

    numRects = REGION_NUM_RECTS(prgnClip) * nrectFill;
    if (numRects > NUM_STACK_RECTS)
    {
	pboxClippedBase = (BoxPtr)ALLOCATE_LOCAL(numRects * sizeof(BoxRec));
	if (!pboxClippedBase)
	    return;
    }
    else
	pboxClippedBase = stackRects;

    pboxClipped = pboxClippedBase;
	
    if (REGION_NUM_RECTS(prgnClip) == 1)
    {
	int x1, y1, x2, y2, bx2, by2;

	pextent = REGION_RECTS(prgnClip);
	x1 = pextent->x1;
	y1 = pextent->y1;
	x2 = pextent->x2;
	y2 = pextent->y2;
    	while (nrectFill--)
    	{
	    if ((pboxClipped->x1 = prect->x) < x1)
		pboxClipped->x1 = x1;
    
	    if ((pboxClipped->y1 = prect->y) < y1)
		pboxClipped->y1 = y1;
    
	    bx2 = (int) prect->x + (int) prect->width;
	    if (bx2 > x2)
		bx2 = x2;
	    pboxClipped->x2 = bx2;
    
	    by2 = (int) prect->y + (int) prect->height;
	    if (by2 > y2)
		by2 = y2;
	    pboxClipped->y2 = by2;

	    prect++;
	    if ((pboxClipped->x1 < pboxClipped->x2) &&
		(pboxClipped->y1 < pboxClipped->y2))
	    {
		pboxClipped++;
	    }
    	}
    }
    else
    {
	int x1, y1, x2, y2, bx2, by2;

	pextent = REGION_EXTENTS(pGC->pScreen, prgnClip);
	x1 = pextent->x1;
	y1 = pextent->y1;
	x2 = pextent->x2;
	y2 = pextent->y2;
    	while (nrectFill--)
    	{
	    BoxRec box;
    
	    if ((box.x1 = prect->x) < x1)
		box.x1 = x1;
    
	    if ((box.y1 = prect->y) < y1)
		box.y1 = y1;
    
	    bx2 = (int) prect->x + (int) prect->width;
	    if (bx2 > x2)
		bx2 = x2;
	    box.x2 = bx2;
    
	    by2 = (int) prect->y + (int) prect->height;
	    if (by2 > y2)
		by2 = y2;
	    box.y2 = by2;
    
	    prect++;
    
	    if ((box.x1 >= box.x2) || (box.y1 >= box.y2))
	    	continue;
    
	    n = REGION_NUM_RECTS (prgnClip);
	    pbox = REGION_RECTS(prgnClip);
    
	    /* clip the rectangle to each box in the clip region
	       this is logically equivalent to calling Intersect()
	    */
	    while(n--)
	    {
		pboxClipped->x1 = max(box.x1, pbox->x1);
		pboxClipped->y1 = max(box.y1, pbox->y1);
		pboxClipped->x2 = min(box.x2, pbox->x2);
		pboxClipped->y2 = min(box.y2, pbox->y2);
		pbox++;

		/* see if clipping left anything */
		if(pboxClipped->x1 < pboxClipped->x2 && 
		   pboxClipped->y1 < pboxClipped->y2)
		{
		    pboxClipped++;
		}
	    }
    	}
    }
    if (pboxClipped != pboxClippedBase)
    {
	s3_24FillBoxSolid(pDrawable,
			  pboxClipped-pboxClippedBase, pboxClippedBase,
			  pGC->fgPixel, pGC->alu, pGC->planemask);
    }
    if (pboxClippedBase != stackRects)
    	DEALLOCATE_LOCAL(pboxClippedBase);
}

void
s3_24SolidBoxClipped (DrawablePtr	pDrawable,
		      RegionPtr		pClip,
		      int		x1,
		      int		y1,
		      int		x2,
		      int		y2,
		      FbBits		fg)
{
    SetupS3 (pDrawable->pScreen);
    BoxPtr	pbox;
    int		nbox;
    int		partX1, partX2, partY1, partY2;
    
    s3SetGlobalBitmap (pDrawable->pScreen, s3DrawMap (pDrawable));
    _s3SetSolidFill(s3,fg,GXcopy,~0);
    
    for (nbox = REGION_NUM_RECTS(pClip), pbox = REGION_RECTS(pClip); 
	 nbox--; 
	 pbox++)
    {
	partX1 = pbox->x1;
	if (partX1 < x1)
	    partX1 = x1;
	
	partX2 = pbox->x2;
	if (partX2 > x2)
	    partX2 = x2;
	
	if (partX2 <= partX1)
	    continue;
	
	partY1 = pbox->y1;
	if (partY1 < y1)
	    partY1 = y1;
	
	partY2 = pbox->y2;
	if (partY2 > y2)
	    partY2 = y2;
	
	if (partY2 <= partY1)
	    continue;
	
	partX1 *= 3;
	partX2 *= 3;
	_s3SolidRect(s3,partX1, partY1, partX2-partX1, partY2-partY1);
    }
    MarkSyncS3(pDrawable->pScreen);
}

void
s3_24ImageGlyphBlt (DrawablePtr	pDrawable,
		    GCPtr		pGC,
		    int		x, 
		    int		y,
		    unsigned int	nglyph,
		    CharInfoPtr	*ppciInit,
		    pointer	pglyphBase)
{
    FbGCPrivPtr	    pPriv = fbGetGCPrivate(pGC);
    CharInfoPtr	    *ppci;
    CharInfoPtr	    pci;
    unsigned char   *pglyph;		/* pointer bits in glyph */
    int		    gWidth, gHeight;	/* width and height of glyph */
    FbStride	    gStride;		/* stride of glyph */
    Bool	    opaque;
    int		    n;
    int		    gx, gy;
    FbBits	    *dst;
    FbStride	    dstStride;
    int		    dstBpp;
    int		    dstXoff, dstYoff;
    FbBits	    depthMask;
    int		    xBack, widthBack;
    int		    yBack, heightBack;
    
    depthMask = FbFullMask(pDrawable->depth);
    if (!ok24 (pGC->fgPixel) || 
	!ok24(pGC->bgPixel) || 
	!ok24(pGC->planemask))
    {
	KdCheckImageGlyphBlt(pDrawable, pGC, x, y, nglyph, ppciInit, pglyphBase);
	return;
    }
    fbGetDrawable (pDrawable, dst, dstStride, dstBpp, dstXoff, dstYoff);
    
    x += pDrawable->x;
    y += pDrawable->y;

    ppci = ppciInit;
    n = nglyph;
    widthBack = 0;
    while (n--)
	widthBack += (*ppci++)->metrics.characterWidth;
    
    xBack = x;
    if (widthBack < 0)
    {
	xBack += widthBack;
	widthBack = -widthBack;
    }
    yBack = y - FONTASCENT(pGC->font);
    heightBack = FONTASCENT(pGC->font) + FONTDESCENT(pGC->font);
    s3_24SolidBoxClipped (pDrawable,
			  fbGetCompositeClip(pGC),
			  xBack,
			  yBack,
			  xBack + widthBack,
			  yBack + heightBack,
			  pPriv->bg);

    KdCheckSync (pDrawable->pScreen);
    
    ppci = ppciInit;
    while (nglyph--)
    {
	pci = *ppci++;
	pglyph = FONTGLYPHBITS(pglyphBase, pci);
	gWidth = GLYPHWIDTHPIXELS(pci);
	gHeight = GLYPHHEIGHTPIXELS(pci);
	if (gWidth && gHeight)
	{
	    gx = x + pci->metrics.leftSideBearing;
	    gy = y - pci->metrics.ascent; 
	    if (gWidth <= sizeof (FbStip) * 8 &&
		fbGlyphIn (fbGetCompositeClip(pGC), gx, gy, gWidth, gHeight))
	    {
		fbGlyph24 (dst + (gy - dstYoff) * dstStride,
			  dstStride,
			  dstBpp,
			  (FbStip *) pglyph,
			  pPriv->fg,
			  gx - dstXoff,
			  gHeight);
	    }
	    else
	    {
		gStride = GLYPHWIDTHBYTESPADDED(pci) / sizeof (FbStip);
		fbPutXYImage (pDrawable,
			      fbGetCompositeClip(pGC),
			      pPriv->fg,
			      pPriv->bg,
			      pPriv->pm,
			      GXcopy,
			      FALSE,
    
			      gx,
			      gy,
			      gWidth, gHeight,
    
			      (FbStip *) pglyph,
			      gStride,
			      0);
	    }
	}
	x += pci->metrics.characterWidth;
    }
}

static const GCOps	s3_24GCOps = {
    s3_24FillSpans,
    KdCheckSetSpans,
    KdCheckPutImage,
    KdCheckCopyArea,
    KdCheckCopyPlane,
    KdCheckPolyPoint,
    KdCheckPolylines,
    KdCheckPolySegment,
    KdCheckPolyRectangle,
    KdCheckPolyArc,
    KdCheckFillPolygon,
    s3_24PolyFillRect,
    KdCheckPolyFillArc,
    miPolyText8,
    miPolyText16,
    miImageText8,
    miImageText16,
    s3_24ImageGlyphBlt,
    KdCheckPolyGlyphBlt,
    KdCheckPushPixels,
#ifdef NEED_LINEHELPER
    ,NULL
#endif
};

void
s3_24ValidateGC (GCPtr pGC, Mask changes, DrawablePtr pDrawable)
{
    if (pDrawable->type != DRAWABLE_WINDOW)
	pGC->ops = (GCOps *) &kdAsyncPixmapGCOps;
    else
	pGC->ops = (GCOps *) &s3_24GCOps;
    fbValidateGC (pGC, changes, pDrawable);
}

GCFuncs	s3_24GCFuncs = {
    s3_24ValidateGC,
    miChangeGC,
    miCopyGC,
    miDestroyGC,
    miChangeClip,
    miDestroyClip,
    miCopyClip
};

Bool
s3_24CreateGC (GCPtr pGC)
{
    if (!fbCreateGC (pGC))
	return FALSE;

    if (pGC->depth != 1)
	pGC->funcs = &s3_24GCFuncs;
    
    return TRUE;
}

Bool
s3_24CreateWindow(WindowPtr pWin)
{
    return fbCreateWindow (pWin);
}

void
s3_24PaintWindow(WindowPtr pWin, RegionPtr pRegion, int what)
{
    SetupS3(pWin->drawable.pScreen);
    s3PatternPtr    pPattern;

    DRAW_DEBUG ((DEBUG_PAINT_WINDOW, "s3PaintWindow 0x%x extents %d %d %d %d n %d",
		 pWin->drawable.id,
		 pRegion->extents.x1, pRegion->extents.y1,
		 pRegion->extents.x2, pRegion->extents.y2,
		 REGION_NUM_RECTS(pRegion)));
    if (!REGION_NUM_RECTS(pRegion)) 
	return;
    switch (what) {
    case PW_BACKGROUND:
	switch (pWin->backgroundState) {
	case None:
	    return;
	case ParentRelative:
	    do {
		pWin = pWin->parent;
	    } while (pWin->backgroundState == ParentRelative);
	    (*pWin->drawable.pScreen->PaintWindowBackground)(pWin, pRegion,
							     what);
	    return;
	case BackgroundPixel:
	    if (ok24(pWin->background.pixel))
	    {
		s3_24FillBoxSolid((DrawablePtr)pWin,
				  (int)REGION_NUM_RECTS(pRegion),
				  REGION_RECTS(pRegion),
				  pWin->background.pixel, GXcopy, ~0);
		return;
	    }
    	}
    	break;
    case PW_BORDER:
	if (pWin->borderIsPixel && ok24(pWin->border.pixel))
	{
	    s3_24FillBoxSolid((DrawablePtr)pWin,
			      (int)REGION_NUM_RECTS(pRegion),
			      REGION_RECTS(pRegion),
			      pWin->border.pixel, GXcopy, ~0);
	    return;
	}
	break;
    }
    KdCheckPaintWindow (pWin, pRegion, what);
}

Bool
s3DrawInit (ScreenPtr pScreen)
{
    KdScreenPriv(pScreen);
    s3ScreenInfo(pScreenPriv);
    int		    ncache_w, ncache_h, ncache;
    int		    px, py;
    S3PatternCache  *cache;
    Bool	    dumb = FALSE;
    int		    ma;

    switch (pScreenPriv->screen->fb[0].bitsPerPixel) {
    case 8:
    case 16:
    case 32:
	break;
    case 24:
	dumb = TRUE;
	break;
    default:
	return FALSE;
    }
    /*
     * Hook up asynchronous drawing
     */
    RegisterSync (pScreen);
    /*
     * Replace various fb screen functions
     */
    if (dumb)
    {
	pScreen->CreateGC = s3_24CreateGC;
	pScreen->CreateWindow = s3_24CreateWindow;
	pScreen->PaintWindowBackground = s3_24PaintWindow;
	pScreen->PaintWindowBorder = s3_24PaintWindow;
	pScreen->CopyWindow = s3CopyWindow;
    }
    else
    {
	if (serverGeneration != s3Generation)
	{
	    s3GCPrivateIndex = AllocateGCPrivateIndex ();
	    s3WindowPrivateIndex = AllocateWindowPrivateIndex ();
	    s3Generation = serverGeneration;
	}
	if (!AllocateWindowPrivate(pScreen, s3WindowPrivateIndex, 0))
	    return FALSE;
	if (!AllocateGCPrivate(pScreen, s3GCPrivateIndex, sizeof (s3PrivGCRec)))
	    return FALSE;
	pScreen->CreateGC = s3CreateGC;
	pScreen->CreateWindow = s3CreateWindow;
	pScreen->ChangeWindowAttributes = s3ChangeWindowAttributes;
	pScreen->DestroyWindow = s3DestroyWindow;
	pScreen->PaintWindowBackground = s3PaintWindow;
	pScreen->PaintWindowBorder = s3PaintWindow;
#ifndef S3_TRIO
	if (pScreenPriv->screen->fb[1].depth)
	{
	    FbOverlayScrPrivPtr pScrPriv = fbOverlayGetScrPriv(pScreen);

	    pScrPriv->PaintKey = s3PaintKey;
	    pScrPriv->CopyWindow = s3CopyWindowProc;
	    pScreen->CopyWindow = fbOverlayCopyWindow;
	}
	else
#endif
	    pScreen->CopyWindow = s3CopyWindow;
	
	/*
	 * Initialize patterns
	 */
#ifdef S3_TRIO
	ma = 0;
#else
	for (ma = 0; s3s->fbmap[ma] >= 0; ma++)
#endif
	{
	    ncache_w = s3s->fb[ma].offscreen_width / S3_TILE_SIZE;
	    ncache_h = s3s->fb[ma].offscreen_height / S3_TILE_SIZE;
	    ncache = ncache_w * ncache_h;
	    if (ncache > 64)
		ncache = 64;
	    DRAW_DEBUG ((DEBUG_S3INIT, "ncache_w %d ncache_h %d ncache %d",
			 ncache_w, ncache_h, ncache));
	    s3s->fb[ma].patterns.cache = (S3PatternCache *) xalloc (ncache * sizeof (S3PatternCache));
	    if (s3s->fb[ma].patterns.cache)
	    {
		DRAW_DEBUG ((DEBUG_S3INIT, "Have pattern cache"));
		s3s->fb[ma].patterns.ncache = ncache;
		s3s->fb[ma].patterns.last_used = 0;
		s3s->fb[ma].patterns.last_id = 0;
		cache = s3s->fb[ma].patterns.cache;
		for (py = 0; py < ncache_h && ncache; py++)
		    for (px = 0; px < ncache_w && ncache; px++)
		    {
			cache->id = 0;
			cache->x = s3s->fb[ma].offscreen_x + px * S3_TILE_SIZE;
			cache->y = s3s->fb[ma].offscreen_y + py * S3_TILE_SIZE;
			cache++;
			ncache--;
		    }
	    }
	}
    }
    return TRUE;
}

void
s3DrawEnable (ScreenPtr pScreen)
{
    SetupS3(pScreen);
    s3ScreenInfo(pScreenPriv);
    int	    c;
    int	    ma;
    
    s3SetGlobalBitmap (pScreen, 0);
    _s3WaitIdleEmpty (s3);
    if (pScreenPriv->screen->fb[0].bitsPerPixel == 24)
    {
	_s3SetScissorsTl(s3, 0, 0);
	_s3SetScissorsBr(s3, pScreenPriv->screen->width*3 - 1, pScreenPriv->screen->height - 1);
	_s3SetSolidFill(s3, pScreen->whitePixel, GXcopy, ~0);
	_s3SolidRect (s3, 0, 0, pScreenPriv->screen->width*3, pScreenPriv->screen->height);
    }
    else
    {
	/*
	 * Flush pattern cache
	 */
#ifdef S3_TRIO
	ma = 0;
#else
	for (ma = 0; s3s->fbmap[ma] >= 0; ma++)
#endif
	{
	    for (c = 0; c < s3s->fb[ma].patterns.ncache; c++)
		s3s->fb[ma].patterns.cache[c].id = 0;
	}

	_s3SetScissorsTl(s3, 0, 0);
	_s3SetScissorsBr(s3, pScreenPriv->screen->width - 1, pScreenPriv->screen->height - 1);
	_s3SetSolidFill(s3, pScreen->blackPixel, GXcopy, ~0);
	_s3SolidRect (s3, 0, 0, pScreenPriv->screen->width, pScreenPriv->screen->height);
    }
    MarkSyncS3 (pScreen);
}

void
s3DrawDisable (ScreenPtr pScreen)
{
    SetupS3 (pScreen);
    _s3WaitIdleEmpty (s3);
}

void
s3DrawFini (ScreenPtr pScreen)
{
    SetupS3(pScreen);
    s3ScreenInfo(pScreenPriv);
    int	    ma;
    
#ifdef S3_TRIO
    ma = 0;
#else
    for (ma = 0; s3s->fbmap[ma] >= 0; ma++)
#endif
    {
	if (s3s->fb[ma].patterns.cache)
	{
	    xfree (s3s->fb[ma].patterns.cache);
	    s3s->fb[ma].patterns.cache = 0;
	    s3s->fb[ma].patterns.ncache = 0;
	}
    }
}

void
s3DrawSync (ScreenPtr pScreen)
{
    SetupS3(pScreen);
    
    _s3WaitIdleEmpty(s3c->s3);
}
