#include <dos.h>
#include "X.h"
#include "Xmd.h"

#include "misc.h"
#include "regionstr.h"
#include "gcstruct.h"
#include "windowstr.h"
#include "pixmapstr.h"
#include "scrnintstr.h"

#include "svga.h"

#include "servermd.h"
#include "../../../os/msdos/msdos.h"
#include "svgafuncs.h"
#include "mxfuncs.h"

union place {
	u_char *pm;
	u_char SVGA_BASED *win;
};
static void svgaFillSolidWin(u_char SVGA_BASED *, int, int, int,
	void (*)(u_char *, u_char, int, u_char), u_char);
static void svgaFillSolidBitmap(u_char *, int, int, u_char);
static void svgaFillTiled(int, union place, int, int, int, int, GCPtr,
	void (*)(u_char *, u_char *, int, int, int),
	void (*)(u_char *, u_char *, int));
static void svgaFillStippled(int, union place, int, int, int, int, GCPtr,
	int, void (*)(u_char *, u_char, int, u_char));
static void mstipple(int, int, int, int, u_char *, int, int, int, int, u_char, u_char);

/* SetSpans -- for each span copy pwidth[i] bits from psrc to pDrawable at
 * ppt[i] using the raster op from the GC.  If fSorted is TRUE, the scanlines
 * are in increasing Y order.
 * Source bit lines are server scanline padded so that they always begin
 * on a word boundary.
 */ 
void
mxSetSpans(pDrawable, pGC, psrc, ppt, pwidth, nspans, fSorted, limits)
    DrawablePtr		pDrawable;
    GCPtr		pGC;
    unsigned char	*psrc;
    DDXPointPtr ppt;
    int	*pwidth;
    int			nspans;
    int			fSorted;
    BoxPtr		limits;
{
    pDrawable = pDrawable;
    pGC = pGC;
    psrc = psrc;
    ppt = ppt;
    pwidth = pwidth;
    nspans = nspans;
    fSorted = fSorted;
    limits = limits;
printf("mxSetSpans -\n");
}

/* FillSpans -- for each span copy pwidth[i] bits from psrc to pDrawable at
 * ppt[i] using the raster op from the GC.  If fSorted is TRUE, the scanlines
 * are in increasing Y order.
 * Source bit lines are server scanline padded so that they always begin
 * on a word boundary.
 */ 
void
mxFillSpans(pDrawable, pGC, nspans, ppt, pwidth, fSorted, limits)
    DrawablePtr		pDrawable;
    GCPtr		pGC;
    int			nspans;
    DDXPointPtr		ppt;
    int	*pwidth;
    Bool		fSorted;
    BoxPtr		limits;
{
    int 		widthDst;	/* width of bitmap in words */
    register RegBoxPtr pbox;
    RegBoxPtr pboxTest, pboxLast;
    DDXPointPtr pptLast;
    unsigned		xStart, xEnd;
    svgaPrivGC		*pPriv = (svgaPrivGC *)(pGC->devPrivates[svgaGCPrivateIndex].ptr);
    RegionPtr 		prgnDst = pPriv->pCompositeClip;
    int			depth;
    int			xSrc, ySrc;
    int			alu = pGC->alu;
    int			fgPixel = (int)pGC->fgPixel;
    int			style = pGC->fillStyle;
    int			nlines = 0;
    u_char		pmask = (u_char)pGC->planemask;
    union place place;
    u_long		bytenum;
    u_char		page;
    u_char		*pdstBase;
    void (* movebytes)(u_char *, u_char *, int) = pPriv->movebytes;
    void (* movebyte)(u_char *, u_char, int, u_char) = pPriv->movebyte;
    void (* movebits)(u_char *, u_char *, int, int, int) = pPriv->movebits;

    pptLast = ppt + nspans;

    if (pDrawable->type == DRAWABLE_WINDOW)
    {
	int x1, x2, y1, y2;

	/* quick check to avoid flashing cursor */
	if (!((WindowPtr)pDrawable)->viewable)
		return;
	depth = 0;
	widthDst = svga_width;
	xSrc = pDrawable->x;
	ySrc = pDrawable->y;
	if (limits) {
		x1 = limits->x1;
		y1 = limits->y1;
		x2 = limits->x2;
		y2 = limits->y2;
	} else if (fSorted & (SPANS_RECT|SPANS_TRECT)) {
		x1 = ppt->x;
		y1 = ppt->y;
		x2 = x1 + *pwidth;
		y2 = y1 + nspans;
	} else {
		x1 = xSrc;
		y1 = ySrc;
		x2 = xSrc + pDrawable->width;
		y2 = ySrc + pDrawable->height;
	}
	if (CUR_OVERLAP(x1, y1, x2, y2))
		HideCursor();
	if (style != FillOpaqueStippled || alu != GXcopy || pmask != 0xff)
		fSorted &= ~(SPANS_RECT|SPANS_TRECT);
    } else {
	depth = pDrawable->depth;
	pdstBase = ((PixmapPtr)pDrawable)->devPrivate.ptr;
	widthDst = (int)(((PixmapPtr)pDrawable)->devKind);
	xSrc = 0;
	ySrc = 0;
	fSorted &= ~(SPANS_RECT|SPANS_TRECT);
    }

    pbox =  REGION_RECTS(prgnDst);
    pboxLast = pbox + REGION_NUM_RECTS(prgnDst);

    if (fSorted)
    {
    /* scan lines sorted in ascending order. Because they are sorted, we
     * don't have to check each scanline against each clip box.  We can be
     * sure that this scanline only has to be clipped to boxes at or after the
     * beginning of this y-band 
     */
	pboxTest = pbox;
	while(ppt < pptLast)
	{
	    pbox = pboxTest;
	    while(pbox < pboxLast)
	    {
		if(pbox->y1 > ppt->y)
		{
		    /* scanline is before clip box */
		    break;
		}
		else if(pbox->y2 <= ppt->y)
		{
		    /* clip box is before scanline */
		    pboxTest = ++pbox;
		    continue;
		}
		else if(pbox->x1 >= ppt->x + *pwidth) 
		{
		    /* clip box is to right of scanline */
		    break;
		}
		else if(pbox->x2 <= ppt->x)
		{
		    /* scanline is to right of clip box */
		    pbox++;
		    continue;
		}

		/* at least some of the scanline is in the current clip box */
		xStart = max(pbox->x1, ppt->x);
		xEnd = min(ppt->x + *pwidth, pbox->x2);
		if (fSorted & (SPANS_RECT|SPANS_TRECT)) {
			nlines = min(pbox->y2 - ppt->y, pptLast - ppt);
		}
		if (!depth) {
			bytenum = (u_long)ppt->y * svga_width;
			page = (u_char)(bytenum >> 16);
			if (svga_curpage != page) {
			    svga_curpage = page;
			    SetPage();
			}
			place.win = vga_mem((u_short)bytenum);
		} else {
			place.pm = pdstBase + ppt->y * widthDst;
		}

		if (style == FillSolid) {
		    if (!depth)
			svgaFillSolidWin(place.win, fgPixel, xStart,
			    xEnd-xStart, movebyte, pmask);
		    else if (depth == 1)
			svgaFillSolidBitmap(place.pm, xStart,
			    xEnd-xStart, pPriv->rop);
		    else
			(*movebyte)(place.pm + xStart, fgPixel, xEnd-xStart,
			    pmask);
		} else {
		    int srcy = ppt->y - ySrc - pGC->patOrg.y;
		    int srcx = xStart - xSrc - pGC->patOrg.x;

		    if (srcy >= 0) {
			srcy %= pGC->stipple->drawable.height;
		    } else {
			srcy = (-srcy) % pGC->stipple->drawable.height;
			if (srcy)
			    srcy = pGC->stipple->drawable.height - srcy;
		    }
		    if (srcx >= 0) {
			srcx %= pGC->stipple->drawable.width;
		    } else {
			srcx = (-srcx) % pGC->stipple->drawable.width;
			if (srcx)
			    srcx = pGC->stipple->drawable.width - srcx;
		    }
		    if (nlines) {
			if (fSorted & SPANS_TRECT)
			    mstipple(xStart, ppt->y,
				xEnd-xStart, nlines,
				pGC->stipple->devPrivate.ptr,
				srcx, srcy,
				pGC->stipple->drawable.width,
				pGC->stipple->drawable.height,
				(u_char)fgPixel, (u_char)pGC->bgPixel);
			else
			    VGAstipple(xStart, ppt->y,
				xEnd-xStart, nlines,
				pGC->stipple->devPrivate.ptr,
				pGC->stipple->drawable.width, srcx, srcy,
				(u_char)fgPixel, (u_char)pGC->bgPixel);
		    }
		    else if (style == FillTiled) {
			svgaFillTiled(depth, place, srcx, xStart, xEnd-xStart,
			   srcy, pGC, movebits, movebytes);
		    } else {
			svgaFillStippled(depth, place, srcx, xStart,
			    xEnd-xStart, srcy, pGC, style, movebyte);
		    }
		}
		if (ppt->x + *pwidth <= pbox->x2)
		{
		    /* End of the line, as it were */
		    break;
		}
		else
		    pbox++;
	    }
	    /* We've tried this line against every box; it must be outside them
	     * all.  move on to the next point */
	    if (nlines) {
		ppt += nlines;
		pwidth += nlines;
	    } else {
		ppt++;
		pwidth++;
	    }
	}
	if (nlines)	/* If we called VGAstipple, then the current page
			 * may be wrong. */
	    SetPage();
    } else {
    /* scan lines not sorted. We must clip each line against all the boxes */
	pboxTest = pbox;
	while(ppt < pptLast)
	{
	    if(ppt->y >= 0)
	    {
		
		for(pbox = pboxTest; pbox< pboxLast; pbox++)
		{
		    if (pbox->y1 > ppt->y)
		    {
			/* rest of clip region is above this scanline,
			 * skip it */
			break;
		    }
		    if (pbox->y2 <= ppt->y)
		    {
			/* clip box is below scanline */
			continue;
		    }
		    if (pbox->x1 < ppt->x + *pwidth &&
		       pbox->x2 > ppt->x) {

			xStart = max(pbox->x1, ppt->x);
			xEnd = min(pbox->x2, ppt->x + *pwidth);
			if (!depth) {
			    bytenum = (u_long)ppt->y * svga_width;
			    page = (u_char)(bytenum >> 16);
			    if (svga_curpage != page) {
				svga_curpage = page;
				SetPage();
			    }
			    place.win = vga_mem((u_short)bytenum);
			} else {
			    place.pm = pdstBase + ppt->y * widthDst;
			}

			if (style == FillSolid) {
			    if (!depth)
				svgaFillSolidWin(place.win, fgPixel, xStart,
				    xEnd-xStart, movebyte, pmask);
			    else if (depth == 1)
				svgaFillSolidBitmap(place.pm,
				    xStart, xEnd-xStart, pPriv->rop);
			    else
				(*movebyte)(place.pm + xStart, fgPixel,
				    xEnd-xStart, pmask);
			} else {
			    int srcy = ppt->y - ySrc - pGC->patOrg.y;
			    int srcx = xStart - xSrc - pGC->patOrg.x;
			    if (style == FillTiled) {
				svgaFillTiled(depth, place, srcx, xStart,
				    xEnd-xStart, srcy, pGC, movebits, movebytes);
			    } else {
				svgaFillStippled(depth, place, srcx, xStart,
				    xEnd-xStart, srcy, pGC, style, movebyte);
			    }
			}
		    }
		}
	    }
	ppt++;
	pwidth++;
	}
    }
}

/* Called for filling a window (not Pixmap) with a Tiled background. */
void
svgaFillRect(pPixmap, srcx, srcy, rect)
    PixmapPtr pPixmap;
    int	srcx, srcy;
    BoxRec		rect;
{
    u_char SVGA_BASED *pdst;
    u_long		bytenum;
    u_char		page;
    u_char		*psrcBase, *psrc;
    u_short		srcWidth, srcHeight;

    psrcBase = pPixmap->devPrivate.ptr;
    srcWidth = pPixmap->devKind;
    srcHeight = pPixmap->drawable.height;

    if (CUR_OVERLAP(rect.x1, rect.y1, rect.x2, rect.y2))
	    HideCursor();
    rect.y2 -= rect.y1;
    rect.x2 -= rect.x1;

    srcy = rect.y1 - srcy;
    srcx = rect.x1 - srcx;

/* Not needed if this is used only for background. */
#ifdef notdef
    if (srcx >= 0)
	srcx %= srcWidth;
    else {
	srcx = (-srcx) % srcWidth;
	if (srcx)
	    srcx = srcWidth - srcx;
    }
#endif
    if (srcy >= 0)
	srcy %= srcHeight;
    else {
	srcy = (-srcy) % srcHeight;
	if (srcy)
	    srcy = srcHeight - srcy;
    }

    psrc = psrcBase + srcWidth * srcy + srcx;
    srcy = srcHeight - srcy;
    bytenum = (u_long)rect.y1 * svga_width + rect.x1;
    page = (u_char)(bytenum >> 16);
    if (svga_curpage != page) {
	svga_curpage = page;
	SetPage();
    }
    pdst = vga_mem((u_short)bytenum);

    while (rect.y2--) {
	if ((u_short)pdst + rect.x2 > 0x10000) {
	    /* Note: (short)pdst is negative. */
	    rect.y1 = (u_short)-(short)pdst;
	    vga_memcpy(pdst, psrc, rect.y1);
	    pdst = vga_mem(0);
	    ++svga_curpage;
	    SetPage();
	    vga_memcpy(pdst, psrc + rect.y1, rect.x2 - rect.y1);
	    pdst = vga_mem(svga_width - rect.y1);
	} else {
	    vga_memcpy(pdst, psrc, rect.x2);
	    if ((u_short)pdst + svga_width >= 0x10000) {
		pdst += svga_width;
		pdst = vga_mem((u_short)pdst);
		++svga_curpage;
		SetPage();
	    } else
		pdst += svga_width;
	}

	srcy--;
	if (!srcy) {
	    srcy = srcHeight;
	    psrc = psrcBase + srcx;
	} else
	    psrc += srcWidth;
    }
}

/* for used with depth 1 pixmaps */

extern char ReduceRop[16][2];

static void
svgaFillSolidWin(pdst, pixel, doff, n, movebyte, pmask)
u_char SVGA_BASED *pdst;
int pixel;
int doff, n;
void (*movebyte)(u_char *, u_char, int, u_char);
u_char pmask;
{
    if ((u_short)pdst + doff >= 0x10000) {
	pdst += doff;
	pdst = vga_mem((u_short)pdst);
	svga_curpage++;
	SetPage();
    } else
	pdst += doff;
    if ((u_short)pdst + n > 0x10000) {
	doff = (u_short) -(unsigned)pdst;
	(*movebyte)(pdst, pixel, doff, pmask);
	pdst = vga_mem(0);
	++svga_curpage;
	SetPage();
	(*movebyte)(pdst, pixel, n-doff, pmask);
    } else {
	(*movebyte)(pdst, pixel, n, pmask);
    }
}

static void
svgaFillSolidBitmap(pdst, doff, n, rop)
u_char *pdst;
int doff, n;
u_char rop;
{
    u_char mask;

    if (rop == RROP_NOP)
	return;
    pdst += doff >> 3;
    doff &= 7;
    if (doff) {
	mask = (u_char)(0xff >> doff);
	doff = 8 - doff;
	if (n < doff) {
	    mask &= (u_char)(0xff << (doff - n));
	    n = 0;
	} else
	    n -= doff;
	if (rop == RROP_WHITE)
	    *pdst++ |= mask;
	else if (rop == RROP_BLACK)
	    *pdst++ &= ~mask;
	else
	    *pdst++ ^= mask;
    }
    doff = n >> 3;
    if (doff) {
	if (rop == RROP_WHITE) {
	    memset(pdst, 0xff, doff);
	    pdst += doff;
	} else if (rop == RROP_BLACK) {
	    memset(pdst, 0, doff);
	    pdst += doff;
	} else {
	    while (doff--) {
		*pdst = (u_char)~*pdst;
		pdst++;
	    }
	}
    }
    n &= 7;
    if (n) {
	mask = (u_char)(0xff >> n);
	if (rop == RROP_WHITE)
	    *pdst |= ~mask;
	else if (rop == RROP_BLACK)
	    *pdst &= mask;
	else
	    *pdst ^= ~mask;
    }
}

/*
 * Copy one scan line of a tile into a drawable, which may be another
 * pixmap or a window.
 */
void
svgaFillTiled(depth, place, soff, doff, n, srcy, pGC, movebits, movebytes)
int depth;
union place place;	/* where to put it */
int soff, doff, n, srcy;
GCPtr pGC;
void (*movebits)(u_char *, u_char *, int, int, int);
void (*movebytes)(u_char *, u_char *, int);
{
    PixmapPtr pixmap = pGC->tile.pixmap;
    int alu = pGC->alu;
    u_char *psrcBase, *psrc;
    int width = pixmap->drawable.width;

    if (soff >= 0)
	soff %= width;
    else {
	soff = (-soff) % width;
	if (soff)
	    soff = width - soff;
    }
    if (srcy >= 0)
	srcy %= pixmap->drawable.height;
    else {
	srcy = (-srcy) % pixmap->drawable.height;
	if (srcy)
	    srcy = pixmap->drawable.height - srcy;
    }

    psrcBase = pixmap->devPrivate.ptr;
    psrc = psrcBase + pixmap->devKind * srcy;

    if (!depth) {	/* Window */
	u_char SVGA_BASED *pdst = place.win;

	if ((u_short)pdst + doff >= 0x10000) {
	    pdst += doff;
	    pdst = vga_mem((u_short)pdst);
	    svga_curpage++;
	    SetPage();
	} else
	    pdst += doff;
	if (soff) {
	    doff = min(n, width - soff);
	    n -= doff;
	    /* This test is >= to take care of the case where the first
	     * line of the pixmap will go into one page exactly.  Then
	     * the next page will be set up for the while loop below.
	     */
	    if ((u_short)pdst + doff >= 0x10000) {
		int i = 0x10000 - (u_short)pdst;
		(*movebytes)(pdst, psrc + soff, i);
		++svga_curpage;
		SetPage();
		doff -= i;
		soff += i;
		pdst = vga_mem(0);
		if (doff)
		    (*movebytes)(pdst, psrc + soff, doff);
	    } else {
		(*movebytes)(pdst, psrc + soff, doff);
	    }
	    pdst += doff;
	}
	while (n) {
	    doff = min(n, width);
	    n -= doff;
	    if ((u_short)pdst + doff >= 0x10000) {
		soff = 0x10000 - (u_short)pdst;
		(*movebytes)(pdst, psrc, soff);
		++svga_curpage;
		SetPage();
		doff -= soff;
		pdst = vga_mem(0);
		if (doff)
		    (*movebytes)(pdst, psrc + soff, doff);
	    } else
		(*movebytes)(pdst, psrc, doff);
	    pdst += doff;
	}
    } else if (depth == 1) {		/* Bitmap */
	int i;

	if (soff) {
		i = min(n, width - soff);
		(*movebits)(place.pm, psrc, doff, soff, i);
		n -= i;
		doff += i;
	}
	while (n) {
		i = min(n, width);
		(*movebits)(place.pm, psrc, doff, 0, i);
		n -= i;
		doff += i;
	}
    } else {			/* Pixmap */

	place.pm += doff;
	if (soff) {
		doff = min(n, width - soff);
		(*movebytes)(place.pm, psrc + soff, doff);
		n -= doff;
		place.pm += doff;
	}
	while (n) {
		soff = min(n, width);
		if (alu == GXcopy)
		    memcpy(place.pm, psrc, soff);
		else
		    (*movebytes)(place.pm, psrc, soff);
		n -= soff;
		place.pm += soff;
	}
    }
}

extern void (*bitmove[16])(u_char *, u_char *, int, int, int);

/*
 * pixmap is of depth 1.
 */
static void
svgaFillStippled(depth, place, soff, doff, n, srcy, pGC, style, movebyte)
int depth;
union place place;
int soff, doff, n, srcy;
GCPtr pGC;
int style;
void (*movebyte)(u_char *, u_char, int, u_char);
{
    PixmapPtr bitmap = pGC->stipple;
    int alu = pGC->alu;
    u_char *psrc;
    int width = bitmap->drawable.width;
    u_char fgPixel = (u_char)pGC->fgPixel;
    u_char bgPixel = (u_char)pGC->bgPixel;
    u_char pmask = (u_char)pGC->planemask;

    if (soff >= 0)
	soff %= width;
    else {
	soff = (-soff) % width;
	if (soff)
	    soff = width - soff;
    }
    if (srcy >= 0)
	srcy %= bitmap->drawable.height;
    else {
	srcy = (-srcy) % bitmap->drawable.height;
	if (srcy)
	    srcy = bitmap->drawable.height - srcy;
    }

    psrc = (char *)bitmap->devPrivate.ptr + srcy * bitmap->devKind;

    if (!depth) {	/* Window */
	u_char SVGA_BASED *pdst = place.win;
	u_char mask;
	u_char *pSrc;
	u_char fast;

	if ((u_short)pdst + doff >= 0x10000) {
	    pdst += doff;
	    pdst = vga_mem((u_short)pdst);
	    svga_curpage++;
	    SetPage();
	} else
	    pdst += doff;

	fast = (pmask == 0xff && alu == GXcopy);

	pSrc = psrc + (soff >> 3);
	mask = masktab[soff & 7];
	while (n) {
	    soff = min(n, width - soff);
	    n -= soff;
	    while (soff--) {
		if (*pSrc & mask) {
		    if (fast)
			*pdst = fgPixel;
		    else
			(*movebyte)(pdst, fgPixel, 1, pmask);
		} else if (style == FillOpaqueStippled) {
		    if (fast)
			*pdst = bgPixel;
		    else
			(*movebyte)(pdst, bgPixel, 1, pmask);
		}
		pdst++;
		if ((u_short)pdst == 0) {
		    ++svga_curpage;
		    SetPage();
		    pdst = vga_mem(0);
		}
		mask >>= 1;
		if (!mask) {
			mask = 0x80;
			pSrc++;
		}
	    }
	    soff = 0;
	    mask = 0x80;
	    pSrc = psrc;
	}
    } else if (depth == 1) {		/* Bitmap */
    svgaPrivGC	*pPriv = (svgaPrivGC *)(pGC->devPrivates[svgaGCPrivateIndex].ptr);
	void (*movebits)(u_char *, u_char *, int, int, int);

	int fgRrop, bgRrop;

	place.pm += (doff >> 3);
	doff &= 7;
	fgRrop = ReduceRop[alu][fgPixel ? 1 : 0];
	bgRrop = style == FillOpaqueStippled ?
	    ReduceRop[alu][bgPixel ? 1 : 0] : RROP_NOP;
	if (fgRrop == bgRrop) {
	    svgaFillSolidBitmap(place.pm, doff, n, pPriv->rop);
	} else {
	    int i;

	    if (style == FillOpaqueStippled) {
		if (bgPixel)
			alu = InverseAlu[alu];
	    } else {
		alu = fgRrop == RROP_WHITE  ? GXor :
			 fgRrop == RROP_BLACK  ? GXandInverted :
			 fgRrop == RROP_INVERT ? GXxor :
			 GXnoop;
	    }
	    movebits = bitmove[alu];
	    if (soff) {
		i = min(n, width - soff);
		(*movebits)(place.pm, psrc, doff, soff, i);
		n -= i;
		doff += i;
	    }
	    while (n) {
		i = min(n, width);
		(*movebits)(place.pm, psrc, doff, 0, i);
		n -= i;
		doff += i;
	    }
	}
    } else {			/* Pixmap */
	u_char mask;
	u_char *pSrc;

	place.pm += doff;
	pSrc = psrc + (soff >> 3);
	mask = masktab[soff & 7];
	if (pmask == 0xff && alu == GXcopy) {
	    while (n) {
		soff = min(n, width - soff);
		n -= soff;
		while (soff--) {
		    if (*pSrc & mask)
			*place.pm = fgPixel;
		    else if (style == FillOpaqueStippled)
			*place.pm = bgPixel;
		    place.pm++;
		    mask >>= 1;
		    if (!mask) {
			    mask = 0x80;
			    pSrc++;
		    }
		}
		soff = 0;
		mask = 0x80;
		pSrc = psrc;
	    }
	} else {
	    while (n) {
		soff = min(n, width - soff);
		n -= soff;
		while (soff--) {
		    if (*pSrc & mask)
			(*movebyte)(place.pm, fgPixel, 1, pmask);
		    else if (style == FillOpaqueStippled)
			(*movebyte)(place.pm, bgPixel, 1, pmask);
		    place.pm++;
		    mask >>= 1;
		    if (!mask) {
			    mask = 0x80;
			    pSrc++;
		    }
		}
		soff = 0;
		mask = 0x80;
		pSrc = psrc;
	    }
	}
    }
}

static void
mstipple(dstx, dsty, dstwidth, dstheight, psrc, xoff, yoff, srcwidth, srcheight, fgpixel, bgpixel)
int dstx, dsty, dstwidth, dstheight;
int xoff, yoff, srcwidth, srcheight;
u_char *psrc;
u_char fgpixel, bgpixel;
{
    int width, height;
    int dheight, doff, ddsty;

    while (dstwidth) {
	width = min(dstwidth, srcwidth - xoff);
	dstwidth -= width;
	doff = yoff;
	ddsty = dsty;
	dheight = dstheight;
	while (dheight) {
	    height = min(dheight, srcheight - doff);
	    dheight -= height;
	    VGAstipple(dstx, ddsty, width, height, psrc, srcwidth,
		xoff, doff, fgpixel, bgpixel);
	    ddsty += height;
	    doff = 0;
	}
	dstx += width;
	xoff = 0;
    }
}
