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

#include "misc.h"
#include "xf86.h"
#include "xf86_OSproc.h"

#include <X11/X.h>
#include "scrnintstr.h"
#include "pixmapstr.h"
#include "xf86str.h"
#include "xaa.h"
#include "xaalocal.h"

static void XAARenderSolidRects(GCPtr, int, BoxPtr, int, int);
static void XAARenderColor8x8Rects(GCPtr, int, BoxPtr, int, int);
static void XAARenderMono8x8Rects(GCPtr, int, BoxPtr, int, int);
static void XAARenderColorExpandRects(GCPtr, int, BoxPtr, int, int);
static void XAARenderCacheExpandRects(GCPtr, int, BoxPtr, int, int);
static void XAARenderCacheBltRects(GCPtr, int, BoxPtr, int, int);
static void XAARenderImageWriteRects(GCPtr, int, BoxPtr, int, int);
static void XAARenderPixmapCopyRects(GCPtr, int, BoxPtr, int, int);

void
XAAPolyFillRect(DrawablePtr pDraw, GCPtr pGC, int nrectFill,    /* number of rectangles to fill */
                xRectangle *prectInit   /* Pointer to first rectangle to fill */
    )
{
    XAAInfoRecPtr infoRec = GET_XAAINFORECPTR_FROM_GC(pGC);
    int xorg = pDraw->x;
    int yorg = pDraw->y;
    int type = 0;
    ClipAndRenderRectsFunc function;

    if ((nrectFill <= 0) || !pGC->planemask)
        return;

    if (!RegionNumRects(pGC->pCompositeClip))
        return;

    switch (pGC->fillStyle) {
    case FillSolid:
        type = DO_SOLID;
        break;
    case FillStippled:
        type = (*infoRec->StippledFillChooser) (pGC);
        break;
    case FillOpaqueStippled:
        if ((pGC->fgPixel == pGC->bgPixel) && infoRec->FillSolidRects &&
            CHECK_PLANEMASK(pGC, infoRec->FillSolidRectsFlags) &&
            CHECK_ROP(pGC, infoRec->FillSolidRectsFlags) &&
            CHECK_ROPSRC(pGC, infoRec->FillSolidRectsFlags) &&
            CHECK_FG(pGC, infoRec->FillSolidRectsFlags))
            type = DO_SOLID;
        else
            type = (*infoRec->OpaqueStippledFillChooser) (pGC);
        break;
    case FillTiled:
        type = (*infoRec->TiledFillChooser) (pGC);
        break;
    }

    switch (type) {
    case DO_SOLID:
        function = XAARenderSolidRects;
        break;
    case DO_COLOR_8x8:
        function = XAARenderColor8x8Rects;
        break;
    case DO_MONO_8x8:
        function = XAARenderMono8x8Rects;
        break;
    case DO_CACHE_BLT:
        function = XAARenderCacheBltRects;
        break;
    case DO_COLOR_EXPAND:
        function = XAARenderColorExpandRects;
        break;
    case DO_CACHE_EXPAND:
        function = XAARenderCacheExpandRects;
        break;
    case DO_IMAGE_WRITE:
        function = XAARenderImageWriteRects;
        break;
    case DO_PIXMAP_COPY:
        function = XAARenderPixmapCopyRects;
        break;
    default:
        (*XAAFallbackOps.PolyFillRect) (pDraw, pGC, nrectFill, prectInit);
        return;
    }

    if (xorg | yorg) {
        int n = nrectFill;
        xRectangle *prect = prectInit;

        while (n--) {
            prect->x += xorg;
            prect->y += yorg;
            prect++;
        }
    }

    XAAClipAndRenderRects(pGC, function, nrectFill, prectInit, xorg, yorg);
}

        /*********************\
	|     Solid Rects     |
	\*********************/

static void
XAARenderSolidRects(GCPtr pGC,
                    int nboxes, BoxPtr pClipBoxes, int xorg, int yorg)
{
    XAAInfoRecPtr infoRec = GET_XAAINFORECPTR_FROM_GC(pGC);

    (*infoRec->FillSolidRects) (infoRec->pScrn,
                                pGC->fgPixel, pGC->alu, pGC->planemask, nboxes,
                                pClipBoxes);
}

        /************************\
	|     Mono 8x8 Rects     |
	\************************/

static void
XAARenderMono8x8Rects(GCPtr pGC,
                      int nboxes, BoxPtr pClipBoxes, int xorg, int yorg)
{
    XAAInfoRecPtr infoRec = GET_XAAINFORECPTR_FROM_GC(pGC);
    XAAPixmapPtr pPriv;
    int fg, bg;

    switch (pGC->fillStyle) {
    case FillStippled:
        pPriv = XAA_GET_PIXMAP_PRIVATE(pGC->stipple);
        fg = pGC->fgPixel;
        bg = -1;
        break;
    case FillOpaqueStippled:
        pPriv = XAA_GET_PIXMAP_PRIVATE(pGC->stipple);
        fg = pGC->fgPixel;
        bg = pGC->bgPixel;
        break;
    case FillTiled:
        pPriv = XAA_GET_PIXMAP_PRIVATE(pGC->tile.pixmap);
        fg = pPriv->fg;
        bg = pPriv->bg;
        break;
    default:                   /* Muffle compiler */
        pPriv = NULL;           /* Kaboom */
        fg = -1;
        bg = -1;
        break;
    }

    (*infoRec->FillMono8x8PatternRects) (infoRec->pScrn,
                                         fg, bg, pGC->alu, pGC->planemask,
                                         nboxes, pClipBoxes, pPriv->pattern0,
                                         pPriv->pattern1,
                                         (xorg + pGC->patOrg.x),
                                         (yorg + pGC->patOrg.y));
}

        /*************************\
	|     Color 8x8 Rects     |
	\*************************/

static void
XAARenderColor8x8Rects(GCPtr pGC,
                       int nboxes, BoxPtr pClipBoxes, int xorg, int yorg)
{
    XAAInfoRecPtr infoRec = GET_XAAINFORECPTR_FROM_GC(pGC);
    XAACacheInfoPtr pCache;
    PixmapPtr pPix;
    int fg, bg;

    switch (pGC->fillStyle) {
    case FillStippled:
        pPix = pGC->stipple;
        fg = pGC->fgPixel;
        bg = -1;
        break;
    case FillOpaqueStippled:
        pPix = pGC->stipple;
        fg = pGC->fgPixel;
        bg = pGC->bgPixel;
        break;
    case FillTiled:
        pPix = pGC->tile.pixmap;
        fg = -1;
        bg = -1;
        break;
    default:                   /* Muffle compiler */
        pPix = NULL;
        fg = -1;
        bg = -1;
        break;
    }

    pCache = (*infoRec->CacheColor8x8Pattern) (infoRec->pScrn, pPix, fg, bg);
    (*infoRec->FillColor8x8PatternRects) (infoRec->pScrn,
                                          pGC->alu, pGC->planemask, nboxes,
                                          pClipBoxes, (xorg + pGC->patOrg.x),
                                          (yorg + pGC->patOrg.y), pCache);
}

        /****************************\
	|     Color Expand Rects     |
	\****************************/

static void
XAARenderColorExpandRects(GCPtr pGC,
                          int nboxes, BoxPtr pClipBoxes, int xorg, int yorg)
{
    XAAInfoRecPtr infoRec = GET_XAAINFORECPTR_FROM_GC(pGC);
    int fg, bg;

    switch (pGC->fillStyle) {
    case FillStippled:
        fg = pGC->fgPixel;
        bg = -1;
        break;
    case FillOpaqueStippled:
        fg = pGC->fgPixel;
        bg = pGC->bgPixel;
        break;
    default:                   /* Muffle compiler */
        fg = -1;
        bg = -1;
        break;
    }

    (*infoRec->FillColorExpandRects) (infoRec->pScrn, fg, bg,
                                      pGC->alu, pGC->planemask, nboxes,
                                      pClipBoxes, (xorg + pGC->patOrg.x),
                                      (yorg + pGC->patOrg.y), pGC->stipple);
}

        /*************************\
	|     Cache Blt Rects     |
	\*************************/

static void
XAARenderCacheBltRects(GCPtr pGC,
                       int nboxes, BoxPtr pClipBoxes, int xorg, int yorg)
{
    XAAInfoRecPtr infoRec = GET_XAAINFORECPTR_FROM_GC(pGC);
    XAACacheInfoPtr pCache;

    switch (pGC->fillStyle) {
    case FillStippled:
        pCache = (*infoRec->CacheStipple) (infoRec->pScrn, pGC->stipple,
                                           pGC->fgPixel, -1);
        break;
    case FillOpaqueStippled:
        pCache = (*infoRec->CacheStipple) (infoRec->pScrn, pGC->stipple,
                                           pGC->fgPixel, pGC->bgPixel);
        break;
    case FillTiled:
        pCache = (*infoRec->CacheTile) (infoRec->pScrn, pGC->tile.pixmap);
        break;
    default:                   /* Muffle compiler */
        pCache = NULL;
        break;
    }

    (*infoRec->FillCacheBltRects) (infoRec->pScrn, pGC->alu,
                                   pGC->planemask, nboxes, pClipBoxes,
                                   (xorg + pGC->patOrg.x),
                                   (yorg + pGC->patOrg.y), pCache);
}

        /****************************\
	|     Cache Expand Rects     |
	\****************************/

static void
XAARenderCacheExpandRects(GCPtr pGC,
                          int nboxes, BoxPtr pClipBoxes, int xorg, int yorg)
{
    XAAInfoRecPtr infoRec = GET_XAAINFORECPTR_FROM_GC(pGC);
    int fg, bg;

    switch (pGC->fillStyle) {
    case FillStippled:
        fg = pGC->fgPixel;
        bg = -1;
        break;
    case FillOpaqueStippled:
        fg = pGC->fgPixel;
        bg = pGC->bgPixel;
        break;
    default:                   /* Muffle compiler */
        fg = -1;
        bg = -1;
        break;
    }

    (*infoRec->FillCacheExpandRects) (infoRec->pScrn, fg, bg,
                                      pGC->alu, pGC->planemask, nboxes,
                                      pClipBoxes, (xorg + pGC->patOrg.x),
                                      (yorg + pGC->patOrg.y), pGC->stipple);
}

        /***************************\
	|     Image Write Rects     |
	\***************************/

static void
XAARenderImageWriteRects(GCPtr pGC,
                         int nboxes, BoxPtr pClipBoxes, int xorg, int yorg)
{
    XAAInfoRecPtr infoRec = GET_XAAINFORECPTR_FROM_GC(pGC);

    (*infoRec->FillImageWriteRects) (infoRec->pScrn, pGC->alu,
                                     pGC->planemask, nboxes, pClipBoxes,
                                     (xorg + pGC->patOrg.x),
                                     (yorg + pGC->patOrg.y), pGC->tile.pixmap);
}

        /***************************\
	|     Pixmap Copy Rects     |
	\***************************/

static void
XAARenderPixmapCopyRects(GCPtr pGC,
                         int nboxes, BoxPtr pClipBoxes, int xorg, int yorg)
{
    XAAInfoRecPtr infoRec = GET_XAAINFORECPTR_FROM_GC(pGC);
    XAACacheInfoPtr pCache = &(infoRec->ScratchCacheInfoRec);
    XAAPixmapPtr pPriv = XAA_GET_PIXMAP_PRIVATE(pGC->tile.pixmap);

    pCache->x = pPriv->offscreenArea->box.x1;
    pCache->y = pPriv->offscreenArea->box.y1;
    pCache->w = pCache->orig_w = pPriv->offscreenArea->box.x2 - pCache->x;
    pCache->h = pCache->orig_h = pPriv->offscreenArea->box.y2 - pCache->y;
    pCache->trans_color = -1;

    (*infoRec->FillCacheBltRects) (infoRec->pScrn, pGC->alu,
                                   pGC->planemask, nboxes, pClipBoxes,
                                   (xorg + pGC->patOrg.x),
                                   (yorg + pGC->patOrg.y), pCache);
}

        /************\
	|   Solid    |
	\************/

void
XAAFillSolidRects(ScrnInfoPtr pScrn, int fg, int rop, unsigned int planemask, int nBox, /* number of rectangles to fill */
                  BoxPtr pBox   /* Pointer to first rectangle to fill */
    )
{
    XAAInfoRecPtr infoRec = GET_XAAINFORECPTR_FROM_SCRNINFOPTR(pScrn);

    (*infoRec->SetupForSolidFill) (pScrn, fg, rop, planemask);
    while (nBox--) {
        (*infoRec->SubsequentSolidFillRect) (pScrn, pBox->x1, pBox->y1,
                                             pBox->x2 - pBox->x1,
                                             pBox->y2 - pBox->y1);
        pBox++;
    }
    SET_SYNC_FLAG(infoRec);
}

        /*********************\
	|  8x8 Mono Patterns  |
	\*********************/

void
XAAFillMono8x8PatternRectsScreenOrigin(ScrnInfoPtr pScrn,
                                       int fg, int bg, int rop,
                                       unsigned int planemask,
                                       int nBox,
                                       BoxPtr pBox,
                                       int pattern0, int pattern1,
                                       int xorigin, int yorigin)
{
    XAAInfoRecPtr infoRec = GET_XAAINFORECPTR_FROM_SCRNINFOPTR(pScrn);
    int patx = pattern0, paty = pattern1;
    int xorg = (-xorigin) & 0x07;
    int yorg = (-yorigin) & 0x07;

    if (infoRec->Mono8x8PatternFillFlags & HARDWARE_PATTERN_PROGRAMMED_BITS) {
        if (!(infoRec->Mono8x8PatternFillFlags &
              HARDWARE_PATTERN_PROGRAMMED_ORIGIN)) {
            XAARotateMonoPattern(&patx, &paty, xorg, yorg,
                                 (infoRec->Mono8x8PatternFillFlags &
                                  BIT_ORDER_IN_BYTE_MSBFIRST));
            xorg = patx;
            yorg = paty;
        }
    }
    else {
        XAACacheInfoPtr pCache =
            (*infoRec->CacheMono8x8Pattern) (pScrn, pattern0, pattern1);
        patx = pCache->x;
        paty = pCache->y;
        if (!(infoRec->Mono8x8PatternFillFlags &
              HARDWARE_PATTERN_PROGRAMMED_ORIGIN)) {
            int slot = (yorg << 3) + xorg;

            patx += pCache->offsets[slot].x;
            paty += pCache->offsets[slot].y;
            xorg = patx;
            yorg = paty;
        }
    }

    (*infoRec->SetupForMono8x8PatternFill) (pScrn, patx, paty,
                                            fg, bg, rop, planemask);

    while (nBox--) {
        (*infoRec->SubsequentMono8x8PatternFillRect) (pScrn,
                                                      xorg, yorg, pBox->x1,
                                                      pBox->y1,
                                                      pBox->x2 - pBox->x1,
                                                      pBox->y2 - pBox->y1);
        pBox++;
    }
    SET_SYNC_FLAG(infoRec);
}

void
XAAFillMono8x8PatternRects(ScrnInfoPtr pScrn,
                           int fg, int bg, int rop,
                           unsigned int planemask,
                           int nBox,
                           BoxPtr pBox,
                           int pattern0, int pattern1, int xorigin, int yorigin)
{
    XAAInfoRecPtr infoRec = GET_XAAINFORECPTR_FROM_SCRNINFOPTR(pScrn);
    int patx = pattern0, paty = pattern1;
    int xorg, yorg;
    XAACacheInfoPtr pCache = NULL;

    if (!(infoRec->Mono8x8PatternFillFlags & HARDWARE_PATTERN_PROGRAMMED_BITS)) {
        pCache = (*infoRec->CacheMono8x8Pattern) (pScrn, pattern0, pattern1);
        patx = pCache->x;
        paty = pCache->y;
    }

    (*infoRec->SetupForMono8x8PatternFill) (pScrn, patx, paty,
                                            fg, bg, rop, planemask);

    while (nBox--) {
        xorg = (pBox->x1 - xorigin) & 0x07;
        yorg = (pBox->y1 - yorigin) & 0x07;

        if (!(infoRec->Mono8x8PatternFillFlags &
              HARDWARE_PATTERN_PROGRAMMED_ORIGIN)) {
            if (infoRec->Mono8x8PatternFillFlags &
                HARDWARE_PATTERN_PROGRAMMED_BITS) {
                patx = pattern0;
                paty = pattern1;
                XAARotateMonoPattern(&patx, &paty, xorg, yorg,
                                     (infoRec->Mono8x8PatternFillFlags &
                                      BIT_ORDER_IN_BYTE_MSBFIRST));
                xorg = patx;
                yorg = paty;
            }
            else {
                int slot = (yorg << 3) + xorg;

                xorg = patx + pCache->offsets[slot].x;
                yorg = paty + pCache->offsets[slot].y;
            }
        }

        (*infoRec->SubsequentMono8x8PatternFillRect) (pScrn,
                                                      xorg, yorg, pBox->x1,
                                                      pBox->y1,
                                                      pBox->x2 - pBox->x1,
                                                      pBox->y2 - pBox->y1);
        pBox++;
    }

    SET_SYNC_FLAG(infoRec);
}

        /**********************\
	|  8x8 Color Patterns  |
	\**********************/

void
XAAFillColor8x8PatternRectsScreenOrigin(ScrnInfoPtr pScrn,
                                        int rop,
                                        unsigned int planemask,
                                        int nBox,
                                        BoxPtr pBox,
                                        int xorigin, int yorigin,
                                        XAACacheInfoPtr pCache)
{
    XAAInfoRecPtr infoRec = GET_XAAINFORECPTR_FROM_SCRNINFOPTR(pScrn);
    int patx = pCache->x, paty = pCache->y;
    int xorg = (-xorigin) & 0x07;
    int yorg = (-yorigin) & 0x07;

    if (!(infoRec->Color8x8PatternFillFlags &
          HARDWARE_PATTERN_PROGRAMMED_ORIGIN)) {
        int slot = (yorg << 3) + xorg;

        paty += pCache->offsets[slot].y;
        patx += pCache->offsets[slot].x;
        xorg = patx;
        yorg = paty;
    }

    (*infoRec->SetupForColor8x8PatternFill) (pScrn, patx, paty,
                                             rop, planemask,
                                             pCache->trans_color);

    while (nBox--) {
        (*infoRec->SubsequentColor8x8PatternFillRect) (pScrn,
                                                       xorg, yorg, pBox->x1,
                                                       pBox->y1,
                                                       pBox->x2 - pBox->x1,
                                                       pBox->y2 - pBox->y1);
        pBox++;
    }
    SET_SYNC_FLAG(infoRec);
}

void
XAAFillColor8x8PatternRects(ScrnInfoPtr pScrn,
                            int rop,
                            unsigned int planemask,
                            int nBox,
                            BoxPtr pBox,
                            int xorigin, int yorigin, XAACacheInfoPtr pCache)
{
    XAAInfoRecPtr infoRec = GET_XAAINFORECPTR_FROM_SCRNINFOPTR(pScrn);
    int xorg, yorg;

    (*infoRec->SetupForColor8x8PatternFill) (pScrn, pCache->x, pCache->y,
                                             rop, planemask,
                                             pCache->trans_color);

    while (nBox--) {
        xorg = (pBox->x1 - xorigin) & 0x07;
        yorg = (pBox->y1 - yorigin) & 0x07;

        if (!(infoRec->Color8x8PatternFillFlags &
              HARDWARE_PATTERN_PROGRAMMED_ORIGIN)) {
            int slot = (yorg << 3) + xorg;

            yorg = pCache->y + pCache->offsets[slot].y;
            xorg = pCache->x + pCache->offsets[slot].x;
        }

        (*infoRec->SubsequentColor8x8PatternFillRect) (pScrn,
                                                       xorg, yorg, pBox->x1,
                                                       pBox->y1,
                                                       pBox->x2 - pBox->x1,
                                                       pBox->y2 - pBox->y1);
        pBox++;
    }

    SET_SYNC_FLAG(infoRec);
}

        /***************\
	|  Cache Blits  |
	\***************/

void
XAAFillCacheBltRects(ScrnInfoPtr pScrn,
                     int rop,
                     unsigned int planemask,
                     int nBox,
                     BoxPtr pBox, int xorg, int yorg, XAACacheInfoPtr pCache)
{
    XAAInfoRecPtr infoRec = GET_XAAINFORECPTR_FROM_SCRNINFOPTR(pScrn);
    int x, y, phaseY, phaseX, skipleft, height, width, w, blit_w, blit_h;

    (*infoRec->SetupForScreenToScreenCopy) (pScrn, 1, 1, rop, planemask,
                                            pCache->trans_color);

    while (nBox--) {
        y = pBox->y1;
        phaseY = (y - yorg) % pCache->orig_h;
        if (phaseY < 0)
            phaseY += pCache->orig_h;
        phaseX = (pBox->x1 - xorg) % pCache->orig_w;
        if (phaseX < 0)
            phaseX += pCache->orig_w;
        height = pBox->y2 - y;
        width = pBox->x2 - pBox->x1;

#if 0
        if (rop == GXcopy) {
            while (1) {
                w = width;
                skipleft = phaseX;
                x = pBox->x1;
                blit_h = pCache->h - phaseY;
                if (blit_h > height)
                    blit_h = height;

                while (1) {
                    blit_w = pCache->w - skipleft;
                    if (blit_w > w)
                        blit_w = w;
                    (*infoRec->SubsequentScreenToScreenCopy) (pScrn,
                                                              pCache->x +
                                                              skipleft,
                                                              pCache->y +
                                                              phaseY, x, y,
                                                              blit_w, blit_h);
                    w -= blit_w;
                    if (!w)
                        break;
                    x += blit_w;
                    skipleft = (skipleft + blit_w) % pCache->orig_w;
                    if (blit_w >= pCache->orig_w)
                        break;
                }

                /* Expand horizontally */
                if (w) {
                    skipleft -= phaseX;
                    if (skipleft < 0)
                        skipleft += pCache->orig_w;
                    blit_w = x - pBox->x1 - skipleft;
                    while (w) {
                        if (blit_w > w)
                            blit_w = w;
                        (*infoRec->SubsequentScreenToScreenCopy) (pScrn,
                                                                  pBox->x1 +
                                                                  skipleft, y,
                                                                  x, y, blit_w,
                                                                  blit_h);
                        w -= blit_w;
                        x += blit_w;
                        blit_w <<= 1;
                    }
                }

                height -= blit_h;
                if (!height)
                    break;
                y += blit_h;
                phaseY = (phaseY + blit_h) % pCache->orig_h;
                if (blit_h >= pCache->orig_h)
                    break;
            }

            /* Expand vertically */
            if (height) {
                blit_w = pBox->x2 - pBox->x1;
                phaseY -= (pBox->y1 - yorg) % pCache->orig_h;
                if (phaseY < 0)
                    phaseY += pCache->orig_h;
                blit_h = y - pBox->y1 - phaseY;
                while (height) {
                    if (blit_h > height)
                        blit_h = height;
                    (*infoRec->SubsequentScreenToScreenCopy) (pScrn, pBox->x1,
                                                              pBox->y1 + phaseY,
                                                              pBox->x1, y,
                                                              blit_w, blit_h);
                    height -= blit_h;
                    y += blit_h;
                    blit_h <<= 1;
                }
            }
        }
        else
#endif
        {
            while (1) {
                w = width;
                skipleft = phaseX;
                x = pBox->x1;
                blit_h = pCache->h - phaseY;
                if (blit_h > height)
                    blit_h = height;

                while (1) {
                    blit_w = pCache->w - skipleft;
                    if (blit_w > w)
                        blit_w = w;
                    (*infoRec->SubsequentScreenToScreenCopy) (pScrn,
                                                              pCache->x +
                                                              skipleft,
                                                              pCache->y +
                                                              phaseY, x, y,
                                                              blit_w, blit_h);
                    w -= blit_w;
                    if (!w)
                        break;
                    x += blit_w;
                    skipleft = (skipleft + blit_w) % pCache->orig_w;
                }
                height -= blit_h;
                if (!height)
                    break;
                y += blit_h;
                phaseY = (phaseY + blit_h) % pCache->orig_h;
            }
        }
        pBox++;
    }

    SET_SYNC_FLAG(infoRec);
}

        /*******************\
	|  Cache Expansion  |
	\*******************/

void
XAAFillCacheExpandRects(ScrnInfoPtr pScrn,
                        int fg, int bg, int rop,
                        unsigned int planemask,
                        int nBox,
                        BoxPtr pBox, int xorg, int yorg, PixmapPtr pPix)
{
    XAAInfoRecPtr infoRec = GET_XAAINFORECPTR_FROM_SCRNINFOPTR(pScrn);
    int x, y, phaseY, phaseX, skipleft, height, width, w, blit_w, blit_h;
    int cacheWidth;
    XAACacheInfoPtr pCache;

    pCache = (*infoRec->CacheMonoStipple) (pScrn, pPix);

    cacheWidth = (pCache->w * pScrn->bitsPerPixel) /
        infoRec->CacheColorExpandDensity;

    (*infoRec->SetupForScreenToScreenColorExpandFill) (pScrn, fg, bg, rop,
                                                       planemask);

    while (nBox--) {
        y = pBox->y1;
        phaseY = (y - yorg) % pCache->orig_h;
        if (phaseY < 0)
            phaseY += pCache->orig_h;
        phaseX = (pBox->x1 - xorg) % pCache->orig_w;
        if (phaseX < 0)
            phaseX += pCache->orig_w;
        height = pBox->y2 - y;
        width = pBox->x2 - pBox->x1;

        while (1) {
            w = width;
            skipleft = phaseX;
            x = pBox->x1;
            blit_h = pCache->h - phaseY;
            if (blit_h > height)
                blit_h = height;

            while (1) {
                blit_w = cacheWidth - skipleft;
                if (blit_w > w)
                    blit_w = w;
                (*infoRec->SubsequentScreenToScreenColorExpandFill) (pScrn, x,
                                                                     y, blit_w,
                                                                     blit_h,
                                                                     pCache->x,
                                                                     pCache->y +
                                                                     phaseY,
                                                                     skipleft);
                w -= blit_w;
                if (!w)
                    break;
                x += blit_w;
                skipleft = (skipleft + blit_w) % pCache->orig_w;
            }
            height -= blit_h;
            if (!height)
                break;
            y += blit_h;
            phaseY = (phaseY + blit_h) % pCache->orig_h;
        }
        pBox++;
    }

    SET_SYNC_FLAG(infoRec);
}

        /******************\
	|   Image Writes   |
	\******************/

/*  This requires all LEFT_EDGE clipping.  You get too many problems
    with reading past the edge of the pattern otherwise */

static void
WriteColumn(ScrnInfoPtr pScrn,
            unsigned char *pSrc,
            int x, int y, int w, int h,
            int xoff, int yoff, int pHeight, int srcwidth, int Bpp)
{
    XAAInfoRecPtr infoRec = GET_XAAINFORECPTR_FROM_SCRNINFOPTR(pScrn);
    unsigned char *src;
    Bool PlusOne = FALSE;
    int skipleft, dwords;

    pSrc += (Bpp * xoff);

    if ((skipleft = (long) pSrc & 0x03L)) {
        if (Bpp == 3)
            skipleft = 4 - skipleft;
        else
            skipleft /= Bpp;

        x -= skipleft;
        w += skipleft;

        if (Bpp == 3)
            pSrc -= 3 * skipleft;
        else                    /* is this Alpha friendly ? */
            pSrc = (unsigned char *) ((long) pSrc & ~0x03L);
    }

    src = pSrc + (yoff * srcwidth);

    dwords = bytes_to_int32(w * Bpp);

    if ((infoRec->ImageWriteFlags & CPU_TRANSFER_PAD_QWORD) &&
        ((dwords * h) & 0x01)) {
        PlusOne = TRUE;
    }

    (*infoRec->SubsequentImageWriteRect) (pScrn, x, y, w, h, skipleft);

    if (dwords > infoRec->ImageWriteRange) {
        while (h--) {
            XAAMoveDWORDS_FixedBase((CARD32 *) infoRec->ImageWriteBase,
                                    (CARD32 *) src, dwords);
            src += srcwidth;
            yoff++;
            if (yoff >= pHeight) {
                yoff = 0;
                src = pSrc;
            }
        }
    }
    else {
        if (srcwidth == (dwords << 2)) {
            int maxLines = infoRec->ImageWriteRange / dwords;
            int step;

            while (h) {
                step = pHeight - yoff;
                if (step > maxLines)
                    step = maxLines;
                if (step > h)
                    step = h;

                XAAMoveDWORDS((CARD32 *) infoRec->ImageWriteBase,
                              (CARD32 *) src, dwords * step);

                src += (srcwidth * step);
                yoff += step;
                if (yoff >= pHeight) {
                    yoff = 0;
                    src = pSrc;
                }
                h -= step;
            }
        }
        else {
            while (h--) {
                XAAMoveDWORDS((CARD32 *) infoRec->ImageWriteBase,
                              (CARD32 *) src, dwords);
                src += srcwidth;
                yoff++;
                if (yoff >= pHeight) {
                    yoff = 0;
                    src = pSrc;
                }
            }
        }
    }

    if (PlusOne) {
        CARD32 *base = (CARD32 *) infoRec->ImageWriteBase;

        *base = 0x00000000;
    }
}

void
XAAFillImageWriteRects(ScrnInfoPtr pScrn,
                       int rop,
                       unsigned int planemask,
                       int nBox,
                       BoxPtr pBox, int xorg, int yorg, PixmapPtr pPix)
{
    XAAInfoRecPtr infoRec = GET_XAAINFORECPTR_FROM_SCRNINFOPTR(pScrn);
    int x, phaseY, phaseX, height, width, blit_w;
    int pHeight = pPix->drawable.height;
    int pWidth = pPix->drawable.width;
    int Bpp = pPix->drawable.bitsPerPixel >> 3;
    int srcwidth = pPix->devKind;

    (*infoRec->SetupForImageWrite) (pScrn, rop, planemask, -1,
                                    pPix->drawable.bitsPerPixel,
                                    pPix->drawable.depth);

    while (nBox--) {
        x = pBox->x1;
        phaseY = (pBox->y1 - yorg) % pHeight;
        if (phaseY < 0)
            phaseY += pHeight;
        phaseX = (x - xorg) % pWidth;
        if (phaseX < 0)
            phaseX += pWidth;
        height = pBox->y2 - pBox->y1;
        width = pBox->x2 - x;

        while (1) {
            blit_w = pWidth - phaseX;
            if (blit_w > width)
                blit_w = width;

            WriteColumn(pScrn, pPix->devPrivate.ptr, x, pBox->y1,
                        blit_w, height, phaseX, phaseY, pHeight, srcwidth, Bpp);

            width -= blit_w;
            if (!width)
                break;
            x += blit_w;
            phaseX = (phaseX + blit_w) % pWidth;
        }
        pBox++;
    }

    if (infoRec->ImageWriteFlags & SYNC_AFTER_IMAGE_WRITE)
        (*infoRec->Sync) (pScrn);
    else
        SET_SYNC_FLAG(infoRec);
}

        /*************\
	|  Utilities  |
	\*************/

void
XAAClipAndRenderRects(GCPtr pGC,
                      ClipAndRenderRectsFunc BoxFunc,
                      int nrectFill, xRectangle *prect, int xorg, int yorg)
{
    XAAInfoRecPtr infoRec = GET_XAAINFORECPTR_FROM_GC(pGC);
    int Right, Bottom, MaxBoxes;
    BoxPtr pextent, pboxClipped, pboxClippedBase;

    MaxBoxes = infoRec->PreAllocSize / sizeof(BoxRec);
    pboxClippedBase = (BoxPtr) infoRec->PreAllocMem;
    pboxClipped = pboxClippedBase;

    if (RegionNumRects(pGC->pCompositeClip) == 1) {
        pextent = RegionRects(pGC->pCompositeClip);
        while (nrectFill--) {
            pboxClipped->x1 = max(pextent->x1, prect->x);
            pboxClipped->y1 = max(pextent->y1, prect->y);

            Right = (int) prect->x + (int) prect->width;
            pboxClipped->x2 = min(pextent->x2, Right);

            Bottom = (int) prect->y + (int) prect->height;
            pboxClipped->y2 = min(pextent->y2, Bottom);

            prect++;
            if ((pboxClipped->x1 < pboxClipped->x2) &&
                (pboxClipped->y1 < pboxClipped->y2)) {
                pboxClipped++;
                if (pboxClipped >= (pboxClippedBase + MaxBoxes)) {
                    (*BoxFunc) (pGC, MaxBoxes, pboxClippedBase, xorg, yorg);
                    pboxClipped = pboxClippedBase;
                }
            }
        }
    }
    else {
        pextent = RegionExtents(pGC->pCompositeClip);
        while (nrectFill--) {
            int n;
            BoxRec box, *pbox;

            box.x1 = max(pextent->x1, prect->x);
            box.y1 = max(pextent->y1, prect->y);

            Right = (int) prect->x + (int) prect->width;
            box.x2 = min(pextent->x2, Right);

            Bottom = (int) prect->y + (int) prect->height;
            box.y2 = min(pextent->y2, Bottom);

            prect++;

            if ((box.x1 >= box.x2) || (box.y1 >= box.y2))
                continue;

            n = RegionNumRects(pGC->pCompositeClip);
            pbox = RegionRects(pGC->pCompositeClip);

            /* 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 + MaxBoxes)) {
                        (*BoxFunc) (pGC, MaxBoxes, pboxClippedBase, xorg, yorg);
                        pboxClipped = pboxClippedBase;
                    }
                }
            }
        }
    }

    if (pboxClipped != pboxClippedBase)
        (*BoxFunc) (pGC, pboxClipped - pboxClippedBase, pboxClippedBase,
                    xorg, yorg);
}

int
XAAGetRectClipBoxes(GCPtr pGC,
                    BoxPtr pboxClippedBase,
                    int nrectFill, xRectangle *prectInit)
{
    int Right, Bottom;
    BoxPtr pextent, pboxClipped = pboxClippedBase;
    xRectangle *prect = prectInit;
    RegionPtr prgnClip = pGC->pCompositeClip;

    if (RegionNumRects(prgnClip) == 1) {
        pextent = RegionRects(prgnClip);
        while (nrectFill--) {
            pboxClipped->x1 = max(pextent->x1, prect->x);
            pboxClipped->y1 = max(pextent->y1, prect->y);

            Right = (int) prect->x + (int) prect->width;
            pboxClipped->x2 = min(pextent->x2, Right);

            Bottom = (int) prect->y + (int) prect->height;
            pboxClipped->y2 = min(pextent->y2, Bottom);

            prect++;
            if ((pboxClipped->x1 < pboxClipped->x2) &&
                (pboxClipped->y1 < pboxClipped->y2)) {
                pboxClipped++;
            }
        }
    }
    else {
        pextent = RegionExtents(prgnClip);
        while (nrectFill--) {
            int n;
            BoxRec box, *pbox;

            box.x1 = max(pextent->x1, prect->x);
            box.y1 = max(pextent->y1, prect->y);

            Right = (int) prect->x + (int) prect->width;
            box.x2 = min(pextent->x2, Right);

            Bottom = (int) prect->y + (int) prect->height;
            box.y2 = min(pextent->y2, Bottom);

            prect++;

            if ((box.x1 >= box.x2) || (box.y1 >= box.y2))
                continue;

            n = RegionNumRects(prgnClip);
            pbox = RegionRects(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++;
                }
            }
        }
    }

    return pboxClipped - pboxClippedBase;
}