/*
   Copyright (C) 1999.  The XFree86 Project Inc.

   Written by Mark Vojkovich (mvojkovi@ucsd.edu)

   Pre-fb-write callbacks and RENDER support - Nolan Leake (nolan@vmware.com)
*/

/* $XFree86: xc/programs/Xserver/hw/xfree86/shadowfb/shadow.c,v 1.20tsi Exp $ */

#ifdef HAVE_XORG_CONFIG_H
#include <xorg-config.h>
#endif

#include <X11/X.h>
#include <X11/Xproto.h>
#include "misc.h"
#include "pixmapstr.h"
#include "input.h"
#include <X11/fonts/font.h>
#include "mi.h"
#include "scrnintstr.h"
#include "windowstr.h"
#include "gcstruct.h"
#include "dixfontstr.h"
#include <X11/fonts/fontstruct.h>
#include "xf86.h"
#include "xf86str.h"
#include "shadowfb.h"

#ifdef RENDER
# include "picturestr.h"
#endif

#define MIN(a,b) (((a)<(b))?(a):(b))
#define MAX(a,b) (((a)>(b))?(a):(b))

static Bool ShadowCloseScreen (int i, ScreenPtr pScreen);
static void ShadowRestoreAreas (    
    PixmapPtr pPixmap,
    RegionPtr prgn,
    int       xorg,
    int       yorg,
    WindowPtr pWin 
);
static void ShadowPaintWindow (
    WindowPtr pWin,
    RegionPtr prgn,
    int what 
);
static void ShadowCopyWindow(
    WindowPtr pWin,
    DDXPointRec ptOldOrg,
    RegionPtr prgn 
);
static Bool ShadowCreateGC(GCPtr pGC);
static Bool ShadowModifyPixmapHeader(
    PixmapPtr pPixmap,
    int width,
    int height,
    int depth,
    int bitsPerPixel,
    int devKind,
    pointer pPixData
);

static Bool ShadowEnterVT(int index, int flags);
static void ShadowLeaveVT(int index, int flags);

#ifdef RENDER
static void ShadowComposite(
    CARD8 op,
    PicturePtr pSrc,
    PicturePtr pMask,
    PicturePtr pDst,
    INT16 xSrc,
    INT16 ySrc,
    INT16 xMask,
    INT16 yMask,
    INT16 xDst,
    INT16 yDst,
    CARD16 width,
    CARD16 height
);
#endif /* RENDER */


typedef struct {
  ScrnInfoPtr 				pScrn;
  RefreshAreaFuncPtr			preRefresh;
  RefreshAreaFuncPtr                    postRefresh;
  CloseScreenProcPtr			CloseScreen;
  PaintWindowBackgroundProcPtr		PaintWindowBackground;
  PaintWindowBorderProcPtr		PaintWindowBorder;
  CopyWindowProcPtr			CopyWindow;
  CreateGCProcPtr			CreateGC;
  BackingStoreRestoreAreasProcPtr	RestoreAreas;  
  ModifyPixmapHeaderProcPtr		ModifyPixmapHeader;
#ifdef RENDER
  CompositeProcPtr Composite;
#endif /* RENDER */
  Bool				(*EnterVT)(int, int);
  void				(*LeaveVT)(int, int);
  Bool				vtSema;
} ShadowScreenRec, *ShadowScreenPtr;

typedef struct {
   GCOps   *ops;
   GCFuncs *funcs;
} ShadowGCRec, *ShadowGCPtr;


static int ShadowScreenIndex = -1;
static int ShadowGCIndex = -1;
static unsigned long ShadowGeneration = 0;

#define GET_SCREEN_PRIVATE(pScreen) \
	(ShadowScreenPtr)((pScreen)->devPrivates[ShadowScreenIndex].ptr)
#define GET_GC_PRIVATE(pGC) \
	(ShadowGCPtr)((pGC)->devPrivates[ShadowGCIndex].ptr)

#define SHADOW_GC_FUNC_PROLOGUE(pGC)\
    ShadowGCPtr pGCPriv = GET_GC_PRIVATE(pGC);\
    (pGC)->funcs = pGCPriv->funcs;\
    if(pGCPriv->ops)\
        (pGC)->ops = pGCPriv->ops

#define SHADOW_GC_FUNC_EPILOGUE(pGC)\
    pGCPriv->funcs = (pGC)->funcs;\
    (pGC)->funcs = &ShadowGCFuncs;\
    if(pGCPriv->ops) {\
        pGCPriv->ops = (pGC)->ops;\
        (pGC)->ops = &ShadowGCOps;\
    }

#define SHADOW_GC_OP_PROLOGUE(pGC)\
    ShadowScreenPtr pPriv = GET_SCREEN_PRIVATE(pGC->pScreen); \
    ShadowGCPtr pGCPriv = GET_GC_PRIVATE(pGC);\
    GCFuncs *oldFuncs = pGC->funcs;\
    pGC->funcs = pGCPriv->funcs;\
    pGC->ops = pGCPriv->ops

    
#define SHADOW_GC_OP_EPILOGUE(pGC)\
    pGCPriv->ops = pGC->ops;\
    pGC->funcs = oldFuncs;\
    pGC->ops   = &ShadowGCOps

#define IS_VISIBLE(pWin) (pPriv->vtSema && \
    (((WindowPtr)pWin)->visibility != VisibilityFullyObscured))

#define TRIM_BOX(box, pGC) { \
    BoxPtr extents = &pGC->pCompositeClip->extents;\
    if(box.x1 < extents->x1) box.x1 = extents->x1; \
    if(box.x2 > extents->x2) box.x2 = extents->x2; \
    if(box.y1 < extents->y1) box.y1 = extents->y1; \
    if(box.y2 > extents->y2) box.y2 = extents->y2; \
    }

#define TRANSLATE_BOX(box, pDraw) { \
    box.x1 += pDraw->x; \
    box.x2 += pDraw->x; \
    box.y1 += pDraw->y; \
    box.y2 += pDraw->y; \
    }

#define TRIM_AND_TRANSLATE_BOX(box, pDraw, pGC) { \
    TRANSLATE_BOX(box, pDraw); \
    TRIM_BOX(box, pGC); \
    }

#define BOX_NOT_EMPTY(box) \
    (((box.x2 - box.x1) > 0) && ((box.y2 - box.y1) > 0))



Bool
ShadowFBInit2 (
    ScreenPtr		pScreen,
    RefreshAreaFuncPtr  preRefreshArea,
    RefreshAreaFuncPtr  postRefreshArea
){
    ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
    ShadowScreenPtr pPriv;
#ifdef RENDER
    PictureScreenPtr ps = GetPictureScreenIfSet(pScreen);
#endif /* RENDER */

    if(!preRefreshArea && !postRefreshArea) return FALSE;
    
    if (ShadowGeneration != serverGeneration) {
	if(((ShadowScreenIndex = AllocateScreenPrivateIndex ()) < 0) ||
	   ((ShadowGCIndex = AllocateGCPrivateIndex()) < 0))
	    return FALSE;
	ShadowGeneration = serverGeneration;
    }

    if(!AllocateGCPrivate(pScreen, ShadowGCIndex, sizeof(ShadowGCRec)))
	return FALSE;

    if(!(pPriv = (ShadowScreenPtr)xalloc(sizeof(ShadowScreenRec))))
	return FALSE;

    pScreen->devPrivates[ShadowScreenIndex].ptr = (pointer)pPriv;  

    pPriv->pScrn = pScrn;
    pPriv->preRefresh = preRefreshArea;
    pPriv->postRefresh = postRefreshArea;
    pPriv->vtSema = TRUE;

    pPriv->CloseScreen = pScreen->CloseScreen;
    pPriv->PaintWindowBackground = pScreen->PaintWindowBackground;
    pPriv->PaintWindowBorder = pScreen->PaintWindowBorder;
    pPriv->CopyWindow = pScreen->CopyWindow;
    pPriv->CreateGC = pScreen->CreateGC;
    pPriv->RestoreAreas = pScreen->BackingStoreFuncs.RestoreAreas;
    pPriv->ModifyPixmapHeader = pScreen->ModifyPixmapHeader;

    pPriv->EnterVT = pScrn->EnterVT;
    pPriv->LeaveVT = pScrn->LeaveVT;

    pScreen->CloseScreen = ShadowCloseScreen;
    pScreen->PaintWindowBackground = ShadowPaintWindow;
    pScreen->PaintWindowBorder = ShadowPaintWindow;
    pScreen->CopyWindow = ShadowCopyWindow;
    pScreen->CreateGC = ShadowCreateGC;
    pScreen->BackingStoreFuncs.RestoreAreas = ShadowRestoreAreas;
    pScreen->ModifyPixmapHeader = ShadowModifyPixmapHeader;

    pScrn->EnterVT = ShadowEnterVT;
    pScrn->LeaveVT = ShadowLeaveVT;

#ifdef RENDER
    if(ps) {
      pPriv->Composite = ps->Composite;
      ps->Composite = ShadowComposite;
    }
#endif /* RENDER */

    return TRUE;
}

Bool
ShadowFBInit (
    ScreenPtr		pScreen,
    RefreshAreaFuncPtr  refreshArea
){
    return ShadowFBInit2(pScreen, NULL, refreshArea);
}

/**********************************************************/

static Bool
ShadowEnterVT(int index, int flags)
{
    ScrnInfoPtr pScrn = xf86Screens[index];
    ShadowScreenPtr pPriv = GET_SCREEN_PRIVATE(pScrn->pScreen);

    if((*pPriv->EnterVT)(index, flags)) {
	pPriv->vtSema = TRUE;
        return TRUE;
    }

    return FALSE;
}

static void
ShadowLeaveVT(int index, int flags)
{
    ShadowScreenPtr pPriv = GET_SCREEN_PRIVATE(xf86Screens[index]->pScreen);

    pPriv->vtSema = FALSE;

    (*pPriv->LeaveVT)(index, flags);
}

/**********************************************************/


static Bool
ShadowCloseScreen (int i, ScreenPtr pScreen)
{
    ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
    ShadowScreenPtr pPriv = GET_SCREEN_PRIVATE(pScreen);
#ifdef RENDER
    PictureScreenPtr ps = GetPictureScreenIfSet(pScreen);
#endif /* RENDER */

    pScreen->CloseScreen = pPriv->CloseScreen;
    pScreen->PaintWindowBackground = pPriv->PaintWindowBackground;
    pScreen->PaintWindowBorder = pPriv->PaintWindowBorder;
    pScreen->CopyWindow = pPriv->CopyWindow;
    pScreen->CreateGC = pPriv->CreateGC;
    pScreen->BackingStoreFuncs.RestoreAreas = pPriv->RestoreAreas;
    pScreen->ModifyPixmapHeader = pPriv->ModifyPixmapHeader;

    pScrn->EnterVT = pPriv->EnterVT;
    pScrn->LeaveVT = pPriv->LeaveVT;

#ifdef RENDER
    if(ps) {
        ps->Composite = pPriv->Composite;
    }
#endif /* RENDER */

    xfree((pointer)pPriv);

    return (*pScreen->CloseScreen) (i, pScreen);
}


static void
ShadowRestoreAreas (    
    PixmapPtr pPixmap,
    RegionPtr prgn,
    int       xorg,
    int       yorg,
    WindowPtr pWin 
){
    ScreenPtr pScreen = pWin->drawable.pScreen;
    ShadowScreenPtr pPriv = GET_SCREEN_PRIVATE(pScreen);
    int num = 0;

    if(pPriv->vtSema && (num = REGION_NUM_RECTS(prgn)))
        if(pPriv->preRefresh)
            (*pPriv->preRefresh)(pPriv->pScrn, num, REGION_RECTS(prgn));

    pScreen->BackingStoreFuncs.RestoreAreas = pPriv->RestoreAreas;
    (*pScreen->BackingStoreFuncs.RestoreAreas) (
                pPixmap, prgn, xorg, yorg, pWin);
    pScreen->BackingStoreFuncs.RestoreAreas = ShadowRestoreAreas;

    if(num && pPriv->postRefresh)
	(*pPriv->postRefresh)(pPriv->pScrn, num, REGION_RECTS(prgn));
}


static void
ShadowPaintWindow(
  WindowPtr pWin,
  RegionPtr prgn,
  int what 
){
    ScreenPtr pScreen = pWin->drawable.pScreen;
    ShadowScreenPtr pPriv = GET_SCREEN_PRIVATE(pScreen);
    int num = 0;

    if(pPriv->vtSema && (num = REGION_NUM_RECTS(prgn)))
        if(pPriv->preRefresh)
            (*pPriv->preRefresh)(pPriv->pScrn, num, REGION_RECTS(prgn));

    if(what == PW_BACKGROUND) {
	pScreen->PaintWindowBackground = pPriv->PaintWindowBackground;
	(*pScreen->PaintWindowBackground) (pWin, prgn, what);
	pScreen->PaintWindowBackground = ShadowPaintWindow;
    } else {
	pScreen->PaintWindowBorder = pPriv->PaintWindowBorder;
	(*pScreen->PaintWindowBorder) (pWin, prgn, what);
	pScreen->PaintWindowBorder = ShadowPaintWindow;
    }

    if(num && pPriv->postRefresh)
        (*pPriv->postRefresh)(pPriv->pScrn, num, REGION_RECTS(prgn));    
}


static void 
ShadowCopyWindow(
   WindowPtr pWin,
   DDXPointRec ptOldOrg,
   RegionPtr prgn 
){
    ScreenPtr pScreen = pWin->drawable.pScreen;
    ShadowScreenPtr pPriv = GET_SCREEN_PRIVATE(pScreen);
    int num = 0;
    RegionRec rgnDst;

    if (pPriv->vtSema) {
        REGION_NULL(pWin->drawable.pScreen, &rgnDst);
	REGION_COPY(pWin->drawable.pScreen, &rgnDst, prgn);
        
        REGION_TRANSLATE(pWin->drawable.pScreen, &rgnDst,
                         pWin->drawable.x - ptOldOrg.x,
                         pWin->drawable.y - ptOldOrg.y);
        REGION_INTERSECT(pScreen, &rgnDst, &pWin->borderClip, &rgnDst);
        if ((num = REGION_NUM_RECTS(&rgnDst))) {
            if(pPriv->preRefresh)
                (*pPriv->preRefresh)(pPriv->pScrn, num, REGION_RECTS(&rgnDst));
        } else {
            REGION_UNINIT(pWin->drawable.pScreen, &rgnDst);
        }
    }
    
    pScreen->CopyWindow = pPriv->CopyWindow;
    (*pScreen->CopyWindow) (pWin, ptOldOrg, prgn);
    pScreen->CopyWindow = ShadowCopyWindow;
    
    if (num) {
        if (pPriv->postRefresh)
            (*pPriv->postRefresh)(pPriv->pScrn, num, REGION_RECTS(&rgnDst));
        REGION_UNINIT(pWin->drawable.pScreen, &rgnDst);
    }
}

static Bool
ShadowModifyPixmapHeader(
    PixmapPtr pPixmap,
    int width,
    int height,
    int depth,
    int bitsPerPixel,
    int devKind,
    pointer pPixData
)
{
    ScreenPtr pScreen;
    ScrnInfoPtr pScrn;
    ShadowScreenPtr pPriv;
    Bool retval;
    PixmapPtr pScreenPix;

    if (!pPixmap)
	return FALSE;

    pScreen = pPixmap->drawable.pScreen;
    pScrn = xf86Screens[pScreen->myNum];

    pScreenPix = (*pScreen->GetScreenPixmap)(pScreen);
    
    if (pPixmap == pScreenPix && !pScrn->vtSema)
	pScreenPix->devPrivate = pScrn->pixmapPrivate;
    
    pPriv = GET_SCREEN_PRIVATE(pScreen);

    pScreen->ModifyPixmapHeader = pPriv->ModifyPixmapHeader;
    retval = (*pScreen->ModifyPixmapHeader)(pPixmap,
	width, height, depth, bitsPerPixel, devKind, pPixData);
    pScreen->ModifyPixmapHeader = ShadowModifyPixmapHeader;

    if (pPixmap == pScreenPix && !pScrn->vtSema)
    {
	pScrn->pixmapPrivate = pScreenPix->devPrivate;
	pScreenPix->devPrivate.ptr = 0;
    }
    return retval;
}

#ifdef RENDER
static void
ShadowComposite(
    CARD8 op,
    PicturePtr pSrc,
    PicturePtr pMask,
    PicturePtr pDst,
    INT16 xSrc,
    INT16 ySrc,
    INT16 xMask,
    INT16 yMask,
    INT16 xDst,
    INT16 yDst,
    CARD16 width,
    CARD16 height
){
    ScreenPtr pScreen = pDst->pDrawable->pScreen;
    ShadowScreenPtr pPriv = GET_SCREEN_PRIVATE(pScreen);
    PictureScreenPtr ps = GetPictureScreen(pScreen);
    BoxRec box;
    BoxPtr extents;
    Bool boxNotEmpty = FALSE;

    if (pPriv->vtSema
	&& pDst->pDrawable->type == DRAWABLE_WINDOW) {

	box.x1 = pDst->pDrawable->x + xDst;
	box.y1 = pDst->pDrawable->y + yDst;
	box.x2 = box.x1 + width;
	box.y2 = box.y1 + height;

	extents = &pDst->pCompositeClip->extents;
	if(box.x1 < extents->x1) box.x1 = extents->x1;
	if(box.x2 > extents->x2) box.x2 = extents->x2;
	if(box.y1 < extents->y1) box.y1 = extents->y1;
	if(box.y2 > extents->y2) box.y2 = extents->y2;
	
	if (BOX_NOT_EMPTY(box)) {
	    if (pPriv->preRefresh)
		(*pPriv->preRefresh)(pPriv->pScrn, 1, &box);
	    boxNotEmpty = TRUE;
	}
    }
    
    ps->Composite = pPriv->Composite;
    (*ps->Composite)(op, pSrc, pMask, pDst, xSrc, ySrc,
		     xMask, yMask, xDst, yDst, width, height);
    ps->Composite = ShadowComposite;

    if (pPriv->postRefresh && boxNotEmpty) {
        (*pPriv->postRefresh)(pPriv->pScrn, 1, &box);
    }
}
#endif /* RENDER */

/**********************************************************/

static void ShadowValidateGC(GCPtr, unsigned long, DrawablePtr);
static void ShadowChangeGC(GCPtr, unsigned long);
static void ShadowCopyGC(GCPtr, unsigned long, GCPtr);
static void ShadowDestroyGC(GCPtr);
static void ShadowChangeClip(GCPtr, int, pointer, int);
static void ShadowDestroyClip(GCPtr);
static void ShadowCopyClip(GCPtr, GCPtr);

GCFuncs ShadowGCFuncs = {
    ShadowValidateGC, ShadowChangeGC, ShadowCopyGC, ShadowDestroyGC,
    ShadowChangeClip, ShadowDestroyClip, ShadowCopyClip
};


extern GCOps ShadowGCOps;

static Bool
ShadowCreateGC(GCPtr pGC)
{
    ScreenPtr pScreen = pGC->pScreen;
    ShadowScreenPtr pPriv = GET_SCREEN_PRIVATE(pScreen);
    ShadowGCPtr pGCPriv = GET_GC_PRIVATE(pGC);
    Bool ret;
   
    pScreen->CreateGC = pPriv->CreateGC;
    if((ret = (*pScreen->CreateGC) (pGC))) {
	pGCPriv->ops = NULL;
	pGCPriv->funcs = pGC->funcs;
	pGC->funcs = &ShadowGCFuncs;
    }
    pScreen->CreateGC = ShadowCreateGC;

    return ret;
}


static void
ShadowValidateGC(
   GCPtr         pGC,
   unsigned long changes,
   DrawablePtr   pDraw 
){
    SHADOW_GC_FUNC_PROLOGUE (pGC);
    (*pGC->funcs->ValidateGC)(pGC, changes, pDraw);
    if(pDraw->type == DRAWABLE_WINDOW)
	pGCPriv->ops = pGC->ops;  /* just so it's not NULL */
    else 
	pGCPriv->ops = NULL;
    SHADOW_GC_FUNC_EPILOGUE (pGC);
}


static void
ShadowDestroyGC(GCPtr pGC)
{
    SHADOW_GC_FUNC_PROLOGUE (pGC);
    (*pGC->funcs->DestroyGC)(pGC);
    SHADOW_GC_FUNC_EPILOGUE (pGC);
}

static void
ShadowChangeGC (
    GCPtr	    pGC,
    unsigned long   mask
){
    SHADOW_GC_FUNC_PROLOGUE (pGC);
    (*pGC->funcs->ChangeGC) (pGC, mask);
    SHADOW_GC_FUNC_EPILOGUE (pGC);
}

static void
ShadowCopyGC (
    GCPtr	    pGCSrc, 
    unsigned long   mask,
    GCPtr	    pGCDst
){
    SHADOW_GC_FUNC_PROLOGUE (pGCDst);
    (*pGCDst->funcs->CopyGC) (pGCSrc, mask, pGCDst);
    SHADOW_GC_FUNC_EPILOGUE (pGCDst);
}

static void
ShadowChangeClip (
    GCPtr   pGC,
    int		type,
    pointer	pvalue,
    int		nrects 
){
    SHADOW_GC_FUNC_PROLOGUE (pGC);
    (*pGC->funcs->ChangeClip) (pGC, type, pvalue, nrects);
    SHADOW_GC_FUNC_EPILOGUE (pGC);
}

static void
ShadowCopyClip(GCPtr pgcDst, GCPtr pgcSrc)
{
    SHADOW_GC_FUNC_PROLOGUE (pgcDst);
    (* pgcDst->funcs->CopyClip)(pgcDst, pgcSrc);
    SHADOW_GC_FUNC_EPILOGUE (pgcDst);
}

static void
ShadowDestroyClip(GCPtr pGC)
{
    SHADOW_GC_FUNC_PROLOGUE (pGC);
    (* pGC->funcs->DestroyClip)(pGC);
    SHADOW_GC_FUNC_EPILOGUE (pGC);
}




/**********************************************************/


static void
ShadowFillSpans(
    DrawablePtr pDraw,
    GC		*pGC,
    int		nInit,	
    DDXPointPtr pptInit,	
    int 	*pwidthInit,		
    int 	fSorted 
){
    SHADOW_GC_OP_PROLOGUE(pGC);    

    if(IS_VISIBLE(pDraw) && nInit) {
	DDXPointPtr ppt = pptInit;
	int *pwidth = pwidthInit;
	int i = nInit;
	BoxRec box;
        Bool boxNotEmpty = FALSE;

	box.x1 = ppt->x;
	box.x2 = box.x1 + *pwidth;
	box.y2 = box.y1 = ppt->y;

	while(--i) {
	   ppt++;
	   pwidth++;
	   if(box.x1 > ppt->x) box.x1 = ppt->x;
	   if(box.x2 < (ppt->x + *pwidth)) 
		box.x2 = ppt->x + *pwidth;
	   if(box.y1 > ppt->y) box.y1 = ppt->y;
	   else if(box.y2 < ppt->y) box.y2 = ppt->y;
	}

	box.y2++;

        if(!pGC->miTranslate) {
           TRANSLATE_BOX(box, pDraw);
        }
        TRIM_BOX(box, pGC); 

	if(BOX_NOT_EMPTY(box)) {
            if(pPriv->preRefresh)
                (*pPriv->preRefresh)(pPriv->pScrn, 1, &box);
            boxNotEmpty = TRUE;
        }

	(*pGC->ops->FillSpans)(pDraw, pGC, nInit, pptInit, pwidthInit, fSorted);

        if(boxNotEmpty && pPriv->postRefresh)
	   (*pPriv->postRefresh)(pPriv->pScrn, 1, &box);
    } else
	(*pGC->ops->FillSpans)(pDraw, pGC, nInit, pptInit, pwidthInit, fSorted);

    SHADOW_GC_OP_EPILOGUE(pGC);
}

static void
ShadowSetSpans(
    DrawablePtr		pDraw,
    GCPtr		pGC,
    char		*pcharsrc,
    DDXPointPtr 	pptInit,
    int			*pwidthInit,
    int			nspans,
    int			fSorted 
){
    SHADOW_GC_OP_PROLOGUE(pGC);

    if(IS_VISIBLE(pDraw) && nspans) {
	DDXPointPtr ppt = pptInit;
	int *pwidth = pwidthInit;
	int i = nspans;
	BoxRec box;
        Bool boxNotEmpty = FALSE;

	box.x1 = ppt->x;
	box.x2 = box.x1 + *pwidth;
	box.y2 = box.y1 = ppt->y;

	while(--i) {
	   ppt++;
	   pwidth++;
	   if(box.x1 > ppt->x) box.x1 = ppt->x;
	   if(box.x2 < (ppt->x + *pwidth)) 
		box.x2 = ppt->x + *pwidth;
	   if(box.y1 > ppt->y) box.y1 = ppt->y;
	   else if(box.y2 < ppt->y) box.y2 = ppt->y;
	}

	box.y2++;

        if(!pGC->miTranslate) {
           TRANSLATE_BOX(box, pDraw);
        }
        TRIM_BOX(box, pGC);

	if(BOX_NOT_EMPTY(box)) {
           if(pPriv->preRefresh)
	      (*pPriv->preRefresh)(pPriv->pScrn, 1, &box);
           boxNotEmpty = TRUE;
        }

	(*pGC->ops->SetSpans)(pDraw, pGC, pcharsrc, pptInit, 
				pwidthInit, nspans, fSorted);

	if(boxNotEmpty && pPriv->postRefresh)
	   (*pPriv->postRefresh)(pPriv->pScrn, 1, &box);
    } else
	(*pGC->ops->SetSpans)(pDraw, pGC, pcharsrc, pptInit, 
				pwidthInit, nspans, fSorted);

    SHADOW_GC_OP_EPILOGUE(pGC);
}

static void
ShadowPutImage(
    DrawablePtr pDraw,
    GCPtr	pGC,
    int		depth, 
    int x, int y, int w, int h,
    int		leftPad,
    int		format,
    char 	*pImage 
){
    BoxRec box;
    Bool boxNotEmpty = FALSE;
    
    SHADOW_GC_OP_PROLOGUE(pGC);

    if(IS_VISIBLE(pDraw)) {
	box.x1 = x + pDraw->x;
	box.x2 = box.x1 + w;
	box.y1 = y + pDraw->y;
	box.y2 = box.y1 + h;

	TRIM_BOX(box, pGC);
	if(BOX_NOT_EMPTY(box)) {
           if(pPriv->preRefresh)
	      (*pPriv->preRefresh)(pPriv->pScrn, 1, &box);
           boxNotEmpty = TRUE;
        }
    }

    (*pGC->ops->PutImage)(pDraw, pGC, depth, x, y, w, h, 
		leftPad, format, pImage);
                
    if(boxNotEmpty && pPriv->postRefresh)
        (*pPriv->postRefresh)(pPriv->pScrn, 1, &box);
    
    SHADOW_GC_OP_EPILOGUE(pGC);

}

static RegionPtr
ShadowCopyArea(
    DrawablePtr pSrc,
    DrawablePtr pDst,
    GC *pGC,
    int srcx, int srcy,
    int width, int height,
    int dstx, int dsty 
){
    RegionPtr ret;
    BoxRec box;
    Bool boxNotEmpty = FALSE;
    
    SHADOW_GC_OP_PROLOGUE(pGC);

    if(IS_VISIBLE(pDst)) {
	box.x1 = dstx + pDst->x;
	box.x2 = box.x1 + width;
	box.y1 = dsty + pDst->y;
	box.y2 = box.y1 + height;

	TRIM_BOX(box, pGC);
	if(BOX_NOT_EMPTY(box)) {
           if(pPriv->preRefresh)
	      (*pPriv->preRefresh)(pPriv->pScrn, 1, &box);
           boxNotEmpty = TRUE;
        }
    }
    
    ret = (*pGC->ops->CopyArea)(pSrc, pDst,
            pGC, srcx, srcy, width, height, dstx, dsty);

    if(boxNotEmpty && pPriv->postRefresh)
        (*pPriv->postRefresh)(pPriv->pScrn, 1, &box);
    
    SHADOW_GC_OP_EPILOGUE(pGC);

    return ret;
}

static RegionPtr
ShadowCopyPlane(
    DrawablePtr	pSrc,
    DrawablePtr	pDst,
    GCPtr pGC,
    int	srcx, int srcy,
    int	width, int height,
    int	dstx, int dsty,
    unsigned long bitPlane 
){
    RegionPtr ret;
    BoxRec box;
    Bool boxNotEmpty = FALSE;
    
    SHADOW_GC_OP_PROLOGUE(pGC);

    if(IS_VISIBLE(pDst)) {
	box.x1 = dstx + pDst->x;
	box.x2 = box.x1 + width;
	box.y1 = dsty + pDst->y;
	box.y2 = box.y1 + height;

	TRIM_BOX(box, pGC);
	if(BOX_NOT_EMPTY(box)) {
           if(pPriv->preRefresh)
	      (*pPriv->preRefresh)(pPriv->pScrn, 1, &box);
           boxNotEmpty = TRUE;
        }
    }
    
    ret = (*pGC->ops->CopyPlane)(pSrc, pDst,
	       pGC, srcx, srcy, width, height, dstx, dsty, bitPlane);
    
    if(boxNotEmpty && pPriv->postRefresh)
        (*pPriv->postRefresh)(pPriv->pScrn, 1, &box);
    
    SHADOW_GC_OP_EPILOGUE(pGC);

    return ret;
}

static void
ShadowPolyPoint(
    DrawablePtr pDraw,
    GCPtr pGC,
    int mode,
    int nptInit,
    xPoint *pptInit 
){
    BoxRec box;
    Bool boxNotEmpty = FALSE;
    
    SHADOW_GC_OP_PROLOGUE(pGC);

    if(IS_VISIBLE(pDraw) && nptInit) {
        xPoint *ppt = pptInit;
        int npt = nptInit;

	box.x2 = box.x1 = pptInit->x;
	box.y2 = box.y1 = pptInit->y;

	/* this could be slow if the points were spread out */

	while(--npt) {
	   ppt++;
	   if(box.x1 > ppt->x) box.x1 = ppt->x;
	   else if(box.x2 < ppt->x) box.x2 = ppt->x;
	   if(box.y1 > ppt->y) box.y1 = ppt->y;
	   else if(box.y2 < ppt->y) box.y2 = ppt->y;
	}

	box.x2++;
	box.y2++;

	TRIM_AND_TRANSLATE_BOX(box, pDraw, pGC);
	if(BOX_NOT_EMPTY(box)) {
           if(pPriv->preRefresh)
	      (*pPriv->preRefresh)(pPriv->pScrn, 1, &box);
           boxNotEmpty = TRUE;
        }
    }
    
    (*pGC->ops->PolyPoint)(pDraw, pGC, mode, nptInit, pptInit);
    
    if(boxNotEmpty && pPriv->postRefresh)
        (*pPriv->postRefresh)(pPriv->pScrn, 1, &box);

    SHADOW_GC_OP_EPILOGUE(pGC);
}

static void
ShadowPolylines(
    DrawablePtr pDraw,
    GCPtr	pGC,
    int		mode,		
    int		nptInit,		
    DDXPointPtr pptInit 
){
    BoxRec box;
    Bool boxNotEmpty = FALSE;
    
    SHADOW_GC_OP_PROLOGUE(pGC);

    if(IS_VISIBLE(pDraw) && nptInit) {
        DDXPointPtr ppt = pptInit;
        int npt = nptInit;
	int extra = pGC->lineWidth >> 1;

	box.x2 = box.x1 = pptInit->x;
	box.y2 = box.y1 = pptInit->y;

	if(npt > 1) {
	   if(pGC->joinStyle == JoinMiter)
		extra = 6 * pGC->lineWidth;
	   else if(pGC->capStyle == CapProjecting)
		extra = pGC->lineWidth;
        }

	if(mode == CoordModePrevious) {
	   int x = box.x1;
	   int y = box.y1;
	   while(--npt) {
		ppt++;
		x += ppt->x;
		y += ppt->y;
		if(box.x1 > x) box.x1 = x;
		else if(box.x2 < x) box.x2 = x;
		if(box.y1 > y) box.y1 = y;
		else if(box.y2 < y) box.y2 = y;
	    }
	} else {
	   while(--npt) {
		ppt++;
		if(box.x1 > ppt->x) box.x1 = ppt->x;
		else if(box.x2 < ppt->x) box.x2 = ppt->x;
		if(box.y1 > ppt->y) box.y1 = ppt->y;
		else if(box.y2 < ppt->y) box.y2 = ppt->y;
	    }
	}

	box.x2++;
	box.y2++;

	if(extra) {
	   box.x1 -= extra;
	   box.x2 += extra;
	   box.y1 -= extra;
	   box.y2 += extra;
        }

	TRIM_AND_TRANSLATE_BOX(box, pDraw, pGC);
	if(BOX_NOT_EMPTY(box)) {
           if(pPriv->preRefresh)
	      (*pPriv->preRefresh)(pPriv->pScrn, 1, &box);
           boxNotEmpty = TRUE;
        }
    }
    
    (*pGC->ops->Polylines)(pDraw, pGC, mode, nptInit, pptInit);

    if(boxNotEmpty && pPriv->postRefresh)
       (*pPriv->postRefresh)(pPriv->pScrn, 1, &box);

    SHADOW_GC_OP_EPILOGUE(pGC);
}

static void 
ShadowPolySegment(
    DrawablePtr	pDraw,
    GCPtr	pGC,
    int		nsegInit,
    xSegment	*pSegInit 
){
    BoxRec box;
    Bool boxNotEmpty = FALSE;
   
    SHADOW_GC_OP_PROLOGUE(pGC);

    if(IS_VISIBLE(pDraw) && nsegInit) {
	int extra = pGC->lineWidth;
        xSegment *pSeg = pSegInit;
        int nseg = nsegInit;

        if(pGC->capStyle != CapProjecting)	
	   extra >>= 1;

	if(pSeg->x2 > pSeg->x1) {
	    box.x1 = pSeg->x1;
	    box.x2 = pSeg->x2;
	} else {
	    box.x2 = pSeg->x1;
	    box.x1 = pSeg->x2;
	}

	if(pSeg->y2 > pSeg->y1) {
	    box.y1 = pSeg->y1;
	    box.y2 = pSeg->y2;
	} else {
	    box.y2 = pSeg->y1;
	    box.y1 = pSeg->y2;
	}

	while(--nseg) {
	    pSeg++;
	    if(pSeg->x2 > pSeg->x1) {
		if(pSeg->x1 < box.x1) box.x1 = pSeg->x1;
		if(pSeg->x2 > box.x2) box.x2 = pSeg->x2;
	    } else {
		if(pSeg->x2 < box.x1) box.x1 = pSeg->x2;
		if(pSeg->x1 > box.x2) box.x2 = pSeg->x1;
	    }
	    if(pSeg->y2 > pSeg->y1) {
		if(pSeg->y1 < box.y1) box.y1 = pSeg->y1;
		if(pSeg->y2 > box.y2) box.y2 = pSeg->y2;
	    } else {
		if(pSeg->y2 < box.y1) box.y1 = pSeg->y2;
		if(pSeg->y1 > box.y2) box.y2 = pSeg->y1;
	    }
	}

	box.x2++;
	box.y2++;

	if(extra) {
	   box.x1 -= extra;
	   box.x2 += extra;
	   box.y1 -= extra;
	   box.y2 += extra;
        }

	TRIM_AND_TRANSLATE_BOX(box, pDraw, pGC);
	if(BOX_NOT_EMPTY(box)) {
           if(pPriv->preRefresh)
              (*pPriv->preRefresh)(pPriv->pScrn, 1, &box);
           boxNotEmpty = TRUE;
        }
    }
    
    (*pGC->ops->PolySegment)(pDraw, pGC, nsegInit, pSegInit);

    if(boxNotEmpty && pPriv->postRefresh)
       (*pPriv->postRefresh)(pPriv->pScrn, 1, &box);

    SHADOW_GC_OP_EPILOGUE(pGC);
}

static void
ShadowPolyRectangle(
    DrawablePtr  pDraw,
    GCPtr        pGC,
    int	         nRectsInit,
    xRectangle  *pRectsInit 
){
    BoxRec box;
    BoxPtr pBoxInit = NULL;
    Bool boxNotEmpty = FALSE;
    int num = 0;
    
    SHADOW_GC_OP_PROLOGUE(pGC);

    if(IS_VISIBLE(pDraw) && nRectsInit) {
        xRectangle *pRects = pRectsInit;
        int nRects = nRectsInit;

	if(nRects >= 32) {
	    int extra = pGC->lineWidth >> 1;

	    box.x1 = pRects->x;
	    box.x2 = box.x1 + pRects->width;
	    box.y1 = pRects->y;
	    box.y2 = box.y1 + pRects->height;

	    while(--nRects) {
		pRects++;
		if(box.x1 > pRects->x) box.x1 = pRects->x;
		if(box.x2 < (pRects->x + pRects->width))
			box.x2 = pRects->x + pRects->width;
		if(box.y1 > pRects->y) box.y1 = pRects->y;
		if(box.y2 < (pRects->y + pRects->height))
			box.y2 = pRects->y + pRects->height;
	    }

	    if(extra) {
		box.x1 -= extra;
		box.x2 += extra;
		box.y1 -= extra;
		box.y2 += extra;
	    }

	    box.x2++;
	    box.y2++;

	    TRIM_AND_TRANSLATE_BOX(box, pDraw, pGC);
	    if(BOX_NOT_EMPTY(box)) {
                if(pPriv->preRefresh)
                   (*pPriv->preRefresh)(pPriv->pScrn, 1, &box);
                boxNotEmpty = TRUE;
            }
	} else {
	    BoxPtr pbox;
	    int offset1, offset2, offset3;

	    offset2 = pGC->lineWidth;
	    if(!offset2) offset2 = 1;
	    offset1 = offset2 >> 1;
	    offset3 = offset2 - offset1;

	    pBoxInit = (BoxPtr)ALLOCATE_LOCAL(nRects * 4 * sizeof(BoxRec));
	    pbox = pBoxInit;

	    while(nRects--) {
		pbox->x1 = pRects->x - offset1;
		pbox->y1 = pRects->y - offset1;
		pbox->x2 = pbox->x1 + pRects->width + offset2;
		pbox->y2 = pbox->y1 + offset2;		
		TRIM_AND_TRANSLATE_BOX((*pbox), pDraw, pGC);
		if(BOX_NOT_EMPTY((*pbox))) {
		   num++;
		   pbox++;
		}

		pbox->x1 = pRects->x - offset1;
		pbox->y1 = pRects->y + offset3;
		pbox->x2 = pbox->x1 + offset2;
		pbox->y2 = pbox->y1 + pRects->height - offset2;		
		TRIM_AND_TRANSLATE_BOX((*pbox), pDraw, pGC);
		if(BOX_NOT_EMPTY((*pbox))) {
		   num++;
		   pbox++;
		}

		pbox->x1 = pRects->x + pRects->width - offset1;
		pbox->y1 = pRects->y + offset3;
		pbox->x2 = pbox->x1 + offset2;
		pbox->y2 = pbox->y1 + pRects->height - offset2;		
		TRIM_AND_TRANSLATE_BOX((*pbox), pDraw, pGC);
		if(BOX_NOT_EMPTY((*pbox))) {
		   num++;
		   pbox++;
		}

		pbox->x1 = pRects->x - offset1;
		pbox->y1 = pRects->y + pRects->height - offset1;
		pbox->x2 = pbox->x1 + pRects->width + offset2;
		pbox->y2 = pbox->y1 + offset2;		
		TRIM_AND_TRANSLATE_BOX((*pbox), pDraw, pGC);
		if(BOX_NOT_EMPTY((*pbox))) {
		   num++;
		   pbox++;
		}

		pRects++;
	    }
	    
	    if(num) {
                if(pPriv->preRefresh)
                    (*pPriv->preRefresh)(pPriv->pScrn, num, pBoxInit);
            } else {
                DEALLOCATE_LOCAL(pBoxInit);
            }                
	}
    }

    (*pGC->ops->PolyRectangle)(pDraw, pGC, nRectsInit, pRectsInit);

    if(boxNotEmpty && pPriv->postRefresh) {
       (*pPriv->postRefresh)(pPriv->pScrn, 1, &box);
    } else if(num) {
       if(pPriv->postRefresh)
          (*pPriv->postRefresh)(pPriv->pScrn, num, pBoxInit);
       DEALLOCATE_LOCAL(pBoxInit);
    }
    
    SHADOW_GC_OP_EPILOGUE(pGC);

}

static void
ShadowPolyArc(
    DrawablePtr	pDraw,
    GCPtr	pGC,
    int		narcsInit,
    xArc	*parcsInit 
){
    BoxRec box;
    Bool boxNotEmpty = FALSE;
   
    SHADOW_GC_OP_PROLOGUE(pGC);

    if(IS_VISIBLE(pDraw) && narcsInit) {
        int narcs = narcsInit;
        xArc *parcs = parcsInit;
        int extra = pGC->lineWidth >> 1;

	box.x1 = parcs->x;
	box.x2 = box.x1 + parcs->width;
	box.y1 = parcs->y;
	box.y2 = box.y1 + parcs->height;

	/* should I break these up instead ? */

	while(--narcs) {
	   parcs++;
	   if(box.x1 > parcs->x) box.x1 = parcs->x;
	   if(box.x2 < (parcs->x + parcs->width))
		box.x2 = parcs->x + parcs->width;
	   if(box.y1 > parcs->y) box.y1 = parcs->y;
	   if(box.y2 < (parcs->y + parcs->height))
		box.y2 = parcs->y + parcs->height;
        }

	if(extra) {
	   box.x1 -= extra;
	   box.x2 += extra;
	   box.y1 -= extra;
	   box.y2 += extra;
        }

	box.x2++;
	box.y2++;

	TRIM_AND_TRANSLATE_BOX(box, pDraw, pGC);
	if(BOX_NOT_EMPTY(box)) {
           if(pPriv->preRefresh)
              (*pPriv->preRefresh)(pPriv->pScrn, 1, &box);
           boxNotEmpty = TRUE;
        }
    }
    
    (*pGC->ops->PolyArc)(pDraw, pGC, narcsInit, parcsInit);

    if(boxNotEmpty && pPriv->postRefresh)
       (*pPriv->postRefresh)(pPriv->pScrn, 1, &box);
    
    SHADOW_GC_OP_EPILOGUE(pGC);

}

static void
ShadowFillPolygon(
    DrawablePtr	pDraw,
    GCPtr	pGC,
    int		shape,
    int		mode,
    int		count,
    DDXPointPtr	pptInit 
){
    SHADOW_GC_OP_PROLOGUE(pGC);

    if(IS_VISIBLE(pDraw) && (count > 2)) {
	DDXPointPtr ppt = pptInit;
	int i = count;
	BoxRec box;
        Bool boxNotEmpty = FALSE;

	box.x2 = box.x1 = ppt->x;
	box.y2 = box.y1 = ppt->y;

	if(mode != CoordModeOrigin) {
	   int x = box.x1;
	   int y = box.y1;
	   while(--i) {
		ppt++;
		x += ppt->x;
		y += ppt->y;
		if(box.x1 > x) box.x1 = x;
		else if(box.x2 < x) box.x2 = x;
		if(box.y1 > y) box.y1 = y;
		else if(box.y2 < y) box.y2 = y;
	    }
	} else {
	   while(--i) {
		ppt++;
		if(box.x1 > ppt->x) box.x1 = ppt->x;
		else if(box.x2 < ppt->x) box.x2 = ppt->x;
		if(box.y1 > ppt->y) box.y1 = ppt->y;
		else if(box.y2 < ppt->y) box.y2 = ppt->y;
	    }
	}

	box.x2++;
	box.y2++;

	TRIM_AND_TRANSLATE_BOX(box, pDraw, pGC);
	if(BOX_NOT_EMPTY(box)) {
           if(pPriv->preRefresh)
              (*pPriv->preRefresh)(pPriv->pScrn, 1, &box);
           boxNotEmpty = TRUE;
        }

	(*pGC->ops->FillPolygon)(pDraw, pGC, shape, mode, count, pptInit);

        if(boxNotEmpty && pPriv->postRefresh)
           (*pPriv->postRefresh)(pPriv->pScrn, 1, &box);        
    } else
	(*pGC->ops->FillPolygon)(pDraw, pGC, shape, mode, count, pptInit);

    SHADOW_GC_OP_EPILOGUE(pGC);
}


static void 
ShadowPolyFillRect(
    DrawablePtr	pDraw,
    GCPtr	pGC,
    int		nRectsInit, 
    xRectangle	*pRectsInit 
){
    SHADOW_GC_OP_PROLOGUE(pGC);

    if(IS_VISIBLE(pDraw) && nRectsInit) {
	BoxRec box;
        Bool boxNotEmpty = FALSE;
	xRectangle *pRects = pRectsInit;
	int nRects = nRectsInit;

	box.x1 = pRects->x;
	box.x2 = box.x1 + pRects->width;
	box.y1 = pRects->y;
	box.y2 = box.y1 + pRects->height;

	while(--nRects) {
	    pRects++;
	    if(box.x1 > pRects->x) box.x1 = pRects->x;
	    if(box.x2 < (pRects->x + pRects->width))
		box.x2 = pRects->x + pRects->width;
	    if(box.y1 > pRects->y) box.y1 = pRects->y;
	    if(box.y2 < (pRects->y + pRects->height))
		box.y2 = pRects->y + pRects->height;
	}

	/* cfb messes with the pRectsInit so we have to do our
	   calculations first */

	TRIM_AND_TRANSLATE_BOX(box, pDraw, pGC);
	if(BOX_NOT_EMPTY(box)) {
            if(pPriv->preRefresh)
                (*pPriv->preRefresh)(pPriv->pScrn, 1, &box);
            boxNotEmpty = TRUE;
        }

	(*pGC->ops->PolyFillRect)(pDraw, pGC, nRectsInit, pRectsInit);

        if(boxNotEmpty && pPriv->postRefresh)
            (*pPriv->postRefresh)(pPriv->pScrn, 1, &box);
    } else
	(*pGC->ops->PolyFillRect)(pDraw, pGC, nRectsInit, pRectsInit);

    SHADOW_GC_OP_EPILOGUE(pGC);
}


static void
ShadowPolyFillArc(
    DrawablePtr	pDraw,
    GCPtr	pGC,
    int		narcsInit,
    xArc	*parcsInit 
){
    BoxRec box;
    Bool boxNotEmpty = FALSE;
   
    SHADOW_GC_OP_PROLOGUE(pGC);

    if(IS_VISIBLE(pDraw) && narcsInit) {
        xArc *parcs = parcsInit;
        int narcs = narcsInit;

	box.x1 = parcs->x;
	box.x2 = box.x1 + parcs->width;
	box.y1 = parcs->y;
	box.y2 = box.y1 + parcs->height;

	/* should I break these up instead ? */

	while(--narcs) {
	   parcs++;
	   if(box.x1 > parcs->x) box.x1 = parcs->x;
	   if(box.x2 < (parcs->x + parcs->width))
		box.x2 = parcs->x + parcs->width;
	   if(box.y1 > parcs->y) box.y1 = parcs->y;
	   if(box.y2 < (parcs->y + parcs->height))
		box.y2 = parcs->y + parcs->height;
        }

	TRIM_AND_TRANSLATE_BOX(box, pDraw, pGC);
	if(BOX_NOT_EMPTY(box)) {
           if(pPriv->preRefresh)
              (*pPriv->preRefresh)(pPriv->pScrn, 1, &box);
           boxNotEmpty = TRUE;
        }
    }

    (*pGC->ops->PolyFillArc)(pDraw, pGC, narcsInit, parcsInit);

    if(boxNotEmpty && pPriv->postRefresh)
       (*pPriv->postRefresh)(pPriv->pScrn, 1, &box);       
    
    SHADOW_GC_OP_EPILOGUE(pGC);
}

static void
ShadowTextExtent(FontPtr pFont, int count, char* chars,
                 FontEncoding fontEncoding, BoxPtr box)
{
    unsigned long n, i;
    int w;
    CharInfoPtr charinfo[255];	/* encoding only has 1 byte for count */

    GetGlyphs(pFont, (unsigned long)count, (unsigned char *)chars,
	      fontEncoding, &n, charinfo);
    w = 0;
    for (i=0; i < n; i++) {
        w += charinfo[i]->metrics.characterWidth;
    }
    if (i) {
    	w += charinfo[i - 1]->metrics.rightSideBearing;
    }
    
    box->x1 = 0;
    if (n) {
	if (charinfo[0]->metrics.leftSideBearing < 0) {
            box->x1 = charinfo[0]->metrics.leftSideBearing;
        }
    }
    box->x2 = w;
    box->y1 = -FONTMAXBOUNDS(pFont,ascent);
    box->y2 = FONTMAXBOUNDS(pFont,descent);
}



static void
ShadowFontToBox(BoxPtr BB, DrawablePtr pDrawable, GCPtr pGC, int x, int y,
                int count, char *chars, int wide)
{
    FontPtr pFont;

    pFont = pGC->font;
    if (pFont->info.constantWidth) {
        int ascent, descent, left, right = 0;

	ascent = MAX(pFont->info.fontAscent, pFont->info.maxbounds.ascent);
	descent = MAX(pFont->info.fontDescent, pFont->info.maxbounds.descent);
	left = pFont->info.maxbounds.leftSideBearing;
	if (count > 0) {
	    right = (count - 1) * pFont->info.maxbounds.characterWidth;
	}
	right += pFont->info.maxbounds.rightSideBearing;
	BB->x1 =
	    MAX(pDrawable->x + x - left, (REGION_EXTENTS(pGC->pScreen,
		&((WindowPtr) pDrawable)->winSize))->x1);
	BB->y1 =
	    MAX(pDrawable->y + y - ascent,
	    (REGION_EXTENTS(pGC->pScreen,
             &((WindowPtr) pDrawable)->winSize))->y1);
	BB->x2 =
	    MIN(pDrawable->x + x + right,
	    (REGION_EXTENTS(pGC->pScreen,
             &((WindowPtr) pDrawable)->winSize))->x2);
	BB->y2 =
	    MIN(pDrawable->y + y + descent,
	    (REGION_EXTENTS(pGC->pScreen,
             &((WindowPtr) pDrawable)->winSize))->y2);
    } else {
    	ShadowTextExtent(pFont, count, chars, wide ? (FONTLASTROW(pFont) == 0)
                         ? Linear16Bit : TwoD16Bit : Linear8Bit, BB);
	BB->x1 =
	    MAX(pDrawable->x + x + BB->x1, (REGION_EXTENTS(pGC->pScreen,
		&((WindowPtr) pDrawable)->winSize))->x1);
	BB->y1 =
	    MAX(pDrawable->y + y + BB->y1,
	    (REGION_EXTENTS(pGC->pScreen,
             &((WindowPtr) pDrawable)->winSize))->y1);
	BB->x2 =
	    MIN(pDrawable->x + x + BB->x2,
	    (REGION_EXTENTS(pGC->pScreen,
	     &((WindowPtr) pDrawable)->winSize))->x2);
	BB->y2 =
	    MIN(pDrawable->y + y + BB->y2,
	    (REGION_EXTENTS(pGC->pScreen, 
	     &((WindowPtr) pDrawable)->winSize))->y2);
    }
}

static int
ShadowPolyText8(
    DrawablePtr pDraw,
    GCPtr	pGC,
    int		x, 
    int 	y,
    int 	count,
    char	*chars 
){
    int width;
    BoxRec box;
    Bool boxNotEmpty = FALSE;
    
    SHADOW_GC_OP_PROLOGUE(pGC);

    if(IS_VISIBLE(pDraw)) {
        ShadowFontToBox(&box, pDraw, pGC, x, y, count, chars, 0);
       
        TRIM_BOX(box, pGC);
        if(BOX_NOT_EMPTY(box)) {
           if(pPriv->preRefresh)
              (*pPriv->preRefresh)(pPriv->pScrn, 1, &box);
           boxNotEmpty = TRUE;
        }
    }
    
    width = (*pGC->ops->PolyText8)(pDraw, pGC, x, y, count, chars);

    if(boxNotEmpty && pPriv->postRefresh)
       (*pPriv->postRefresh)(pPriv->pScrn, 1, &box);
    
    SHADOW_GC_OP_EPILOGUE(pGC);

    return width;
}

static int
ShadowPolyText16(
    DrawablePtr pDraw,
    GCPtr	pGC,
    int		x,
    int		y,
    int 	count,
    unsigned short *chars 
){
    int width;
    BoxRec box;
    Bool boxNotEmpty = FALSE;

    SHADOW_GC_OP_PROLOGUE(pGC);

    if(IS_VISIBLE(pDraw)) {
        ShadowFontToBox(&box, pDraw, pGC, x, y, count, (char*)chars, 1);
       
        TRIM_BOX(box, pGC);
        if(BOX_NOT_EMPTY(box)) {
           if(pPriv->preRefresh)
              (*pPriv->preRefresh)(pPriv->pScrn, 1, &box);
           boxNotEmpty = TRUE;
        }
    }

    width = (*pGC->ops->PolyText16)(pDraw, pGC, x, y, count, chars);

    if(boxNotEmpty && pPriv->postRefresh)
       (*pPriv->postRefresh)(pPriv->pScrn, 1, &box);

    SHADOW_GC_OP_EPILOGUE(pGC);

    return width;
}

static void
ShadowImageText8(
    DrawablePtr pDraw,
    GCPtr	pGC,
    int		x, 
    int		y,
    int 	count,
    char	*chars 
){
    BoxRec box;
    Bool boxNotEmpty = FALSE;
    SHADOW_GC_OP_PROLOGUE(pGC);

    if(IS_VISIBLE(pDraw) && count) {
	int top, bot, Min, Max;

	top = max(FONTMAXBOUNDS(pGC->font, ascent), FONTASCENT(pGC->font));
	bot = max(FONTMAXBOUNDS(pGC->font, descent), FONTDESCENT(pGC->font));

	Min = count * FONTMINBOUNDS(pGC->font, characterWidth);
	if(Min > 0) Min = 0;
	Max = count * FONTMAXBOUNDS(pGC->font, characterWidth);	
	if(Max < 0) Max = 0;

	/* ugh */
	box.x1 = pDraw->x + x + Min +
		FONTMINBOUNDS(pGC->font, leftSideBearing);
	box.x2 = pDraw->x + x + Max + 
		FONTMAXBOUNDS(pGC->font, rightSideBearing);

	box.y1 = pDraw->y + y - top;
	box.y2 = pDraw->y + y + bot;

	TRIM_BOX(box, pGC);
	if(BOX_NOT_EMPTY(box)) {
            if(pPriv->preRefresh) 
               (*pPriv->preRefresh)(pPriv->pScrn, 1, &box);
            boxNotEmpty = TRUE;
        }
    }
    
    (*pGC->ops->ImageText8)(pDraw, pGC, x, y, count, chars);

    if(boxNotEmpty && pPriv->postRefresh)
        (*pPriv->postRefresh)(pPriv->pScrn, 1, &box);
    
    SHADOW_GC_OP_EPILOGUE(pGC);
}
static void
ShadowImageText16(
    DrawablePtr pDraw,
    GCPtr	pGC,
    int		x,
    int		y,
    int 	count,
    unsigned short *chars 
){
    BoxRec box;
    Bool boxNotEmpty = FALSE;
    SHADOW_GC_OP_PROLOGUE(pGC);

    if(IS_VISIBLE(pDraw) && count) {
	int top, bot, Min, Max;

	top = max(FONTMAXBOUNDS(pGC->font, ascent), FONTASCENT(pGC->font));
	bot = max(FONTMAXBOUNDS(pGC->font, descent), FONTDESCENT(pGC->font));

	Min = count * FONTMINBOUNDS(pGC->font, characterWidth);
	if(Min > 0) Min = 0;
	Max = count * FONTMAXBOUNDS(pGC->font, characterWidth);	
	if(Max < 0) Max = 0;

	/* ugh */
	box.x1 = pDraw->x + x + Min +
		FONTMINBOUNDS(pGC->font, leftSideBearing);
	box.x2 = pDraw->x + x + Max + 
		FONTMAXBOUNDS(pGC->font, rightSideBearing);

	box.y1 = pDraw->y + y - top;
	box.y2 = pDraw->y + y + bot;

	TRIM_BOX(box, pGC);
	if(BOX_NOT_EMPTY(box)) {
           if(pPriv->preRefresh)
              (*pPriv->preRefresh)(pPriv->pScrn, 1, &box);
           boxNotEmpty = TRUE;
        }
    }
    
    (*pGC->ops->ImageText16)(pDraw, pGC, x, y, count, chars);

    if(boxNotEmpty && pPriv->postRefresh)
       (*pPriv->postRefresh)(pPriv->pScrn, 1, &box);   
    
    SHADOW_GC_OP_EPILOGUE(pGC);
}


static void
ShadowImageGlyphBlt(
    DrawablePtr pDraw,
    GCPtr pGC,
    int x, int y,
    unsigned int nglyphInit,
    CharInfoPtr *ppciInit,
    pointer pglyphBase 
){
    BoxRec box;
    Bool boxNotEmpty = FALSE;
    SHADOW_GC_OP_PROLOGUE(pGC);

    if(IS_VISIBLE(pDraw) && nglyphInit) {
        CharInfoPtr *ppci = ppciInit;
        unsigned int nglyph = nglyphInit;
	int top, bot, width = 0;

	top = max(FONTMAXBOUNDS(pGC->font, ascent), FONTASCENT(pGC->font));
	bot = max(FONTMAXBOUNDS(pGC->font, descent), FONTDESCENT(pGC->font));

	box.x1 = ppci[0]->metrics.leftSideBearing;
	if(box.x1 > 0) box.x1 = 0;
	box.x2 = ppci[nglyph - 1]->metrics.rightSideBearing - 
		ppci[nglyph - 1]->metrics.characterWidth;
	if(box.x2 < 0) box.x2 = 0;

	box.x2 += pDraw->x + x;
	box.x1 += pDraw->x + x;
	   
	while(nglyph--) {
	    width += (*ppci)->metrics.characterWidth;
	    ppci++;
	}

	if(width > 0) 
	   box.x2 += width;
	else 
	   box.x1 += width;

	box.y1 = pDraw->y + y - top;
	box.y2 = pDraw->y + y + bot;

	TRIM_BOX(box, pGC);
	if(BOX_NOT_EMPTY(box)) {
           if(pPriv->preRefresh)
              (*pPriv->preRefresh)(pPriv->pScrn, 1, &box);
           boxNotEmpty = TRUE;
        }
    }

    (*pGC->ops->ImageGlyphBlt)(pDraw, pGC, x, y, nglyphInit, 
					ppciInit, pglyphBase);

    if(boxNotEmpty && pPriv->postRefresh)
       (*pPriv->postRefresh)(pPriv->pScrn, 1, &box);

    SHADOW_GC_OP_EPILOGUE(pGC);
}

static void
ShadowPolyGlyphBlt(
    DrawablePtr pDraw,
    GCPtr pGC,
    int x, int y,
    unsigned int nglyphInit,
    CharInfoPtr *ppciInit,
    pointer pglyphBase 
){
    BoxRec box;
    Bool boxNotEmpty = FALSE;

    SHADOW_GC_OP_PROLOGUE(pGC);

    if(IS_VISIBLE(pDraw) && nglyphInit) {
        CharInfoPtr *ppci = ppciInit;
        unsigned int nglyph = nglyphInit;

	/* ugh */
	box.x1 = pDraw->x + x + ppci[0]->metrics.leftSideBearing;
	box.x2 = pDraw->x + x + ppci[nglyph - 1]->metrics.rightSideBearing;

	if(nglyph > 1) {
	    int width = 0;

	    while(--nglyph) { 
		width += (*ppci)->metrics.characterWidth;
		ppci++;
	    }
	
	    if(width > 0) box.x2 += width;
	    else box.x1 += width;
	}

	box.y1 = pDraw->y + y - FONTMAXBOUNDS(pGC->font, ascent);
	box.y2 = pDraw->y + y + FONTMAXBOUNDS(pGC->font, descent);

	TRIM_BOX(box, pGC);
	if(BOX_NOT_EMPTY(box)) {
           if(pPriv->preRefresh)
              (*pPriv->preRefresh)(pPriv->pScrn, 1, &box);
           boxNotEmpty = TRUE;
        }
    }
    
    (*pGC->ops->PolyGlyphBlt)(pDraw, pGC, x, y, nglyphInit, 
				ppciInit, pglyphBase);

    if(boxNotEmpty && pPriv->postRefresh)
       (*pPriv->postRefresh)(pPriv->pScrn, 1, &box);

    SHADOW_GC_OP_EPILOGUE(pGC);
}

static void
ShadowPushPixels(
    GCPtr	pGC,
    PixmapPtr	pBitMap,
    DrawablePtr pDraw,
    int	dx, int dy, int xOrg, int yOrg 
){
    BoxRec box;
    Bool boxNotEmpty = FALSE;
    
    SHADOW_GC_OP_PROLOGUE(pGC);

    if(IS_VISIBLE(pDraw)) {
	box.x1 = xOrg;
	box.y1 = yOrg;

        if(!pGC->miTranslate) {
           box.x1 += pDraw->x;          
           box.y1 += pDraw->y;          
        }

	box.x2 = box.x1 + dx;
	box.y2 = box.y1 + dy;

	TRIM_BOX(box, pGC);
	if(BOX_NOT_EMPTY(box)) {
           if(pPriv->preRefresh)
              (*pPriv->preRefresh)(pPriv->pScrn, 1, &box);
           boxNotEmpty = TRUE;
        }
    }

    (*pGC->ops->PushPixels)(pGC, pBitMap, pDraw, dx, dy, xOrg, yOrg);

    if(boxNotEmpty && pPriv->postRefresh)
       (*pPriv->postRefresh)(pPriv->pScrn, 1, &box);

    SHADOW_GC_OP_EPILOGUE(pGC);
}


GCOps ShadowGCOps = {
    ShadowFillSpans, ShadowSetSpans, 
    ShadowPutImage, ShadowCopyArea, 
    ShadowCopyPlane, ShadowPolyPoint, 
    ShadowPolylines, ShadowPolySegment, 
    ShadowPolyRectangle, ShadowPolyArc, 
    ShadowFillPolygon, ShadowPolyFillRect, 
    ShadowPolyFillArc, ShadowPolyText8, 
    ShadowPolyText16, ShadowImageText8, 
    ShadowImageText16, ShadowImageGlyphBlt, 
    ShadowPolyGlyphBlt, ShadowPushPixels,
#ifdef NEED_LINEHELPER
    NULL,
#endif
    {NULL}		/* devPrivate */
};

