#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 "svgafuncs.h"
#include "mxfuncs.h"

#include "servermd.h"

extern int Ones(Mask);
static void Gather8Bits(u_char *, u_char *, int, int, int);

/* TODO:  XYPixmap and XPixmap of depth 1. */

void
mxPutImage(pDrawable, pGC, depth, x, y, w, h, leftPad, format, pImage)
DrawablePtr pDrawable;
GCPtr pGC;
int depth;	/* depth of pImage, not necessarily the depth of pDrawable */
int w, h, leftPad;
int format;
unsigned x, y;
u_char *pImage;
{
    int i;
    BoxRec		box;
    u_char		iswin;
    u_char		*pImage2;
    u_char		*dp;
    svgaPrivGC	*pPriv = (svgaPrivGC *)(pGC->devPrivates[svgaGCPrivateIndex].ptr);
    RegBoxPtr pbox, pboxLast;
    RegionPtr 		prgnDst;
    int			xStart, xEnd;

    if (!w || !h)
	return;

    x += pDrawable->x;
    y += pDrawable->y;

    box.x1 = x;
    box.x2 = x + w;
    box.y1 = y;
    box.y2 = y + h;

    switch(format) {
	case XYBitmap:
	    {
	    DDXPointPtr		pptFirst, ppt;
	    int	*pwidthFirst, *pwidth;
	    PixmapRec		pixmap;
	    PixmapPtr		oldStipple;
	    int			oldFillStyle;
	    DDXPointRec		oldPatOrg;

	/* This code stolen from miPutImage. */
	    ppt = pptFirst =
		(DDXPointPtr)ALLOCATE_LOCAL(h * sizeof(DDXPointRec));
	    pwidth = pwidthFirst = (int *)ALLOCATE_LOCAL(h * sizeof(int));
	    if(!pptFirst || !pwidthFirst)
	    {
	printf("putImage: ALLOCATE_LOCAL failed\n");
	       if (pwidthFirst)
		   DEALLOCATE_LOCAL(pwidthFirst);
	       if (pptFirst)
		   DEALLOCATE_LOCAL(pptFirst);
	       return;
	    }

	    for(i = 0; i < h; i++)
	    {
		ppt->x = x;
		ppt->y = y + i;
		ppt++;
		*pwidth++ = w;
	    }

	    /* Turn pImage into a pixmap. */
	    pixmap.drawable.type = DRAWABLE_PIXMAP;
	    pixmap.drawable.pScreen = pDrawable->pScreen;
	    pixmap.drawable.depth = 1;
	    pixmap.drawable.bitsPerPixel = 1;
	    pixmap.drawable.width = w + leftPad;
	    pixmap.drawable.height = h;
	    pixmap.drawable.serialNumber = NEXT_SERIAL_NUMBER;
	    pixmap.drawable.x = 0;
	    pixmap.drawable.y = 0;
	    pixmap.refcnt = 1;
	    pixmap.devKind = PixmapBytePad(pixmap.drawable.width, 1);
	    pixmap.devPrivate.ptr = pImage;

	    oldStipple = pGC->stipple;
	    pGC->stipple = &pixmap;
	    oldFillStyle = pGC->fillStyle;
	    pGC->fillStyle = FillOpaqueStippled;
	    oldPatOrg = pGC->patOrg;
	    pGC->patOrg.y = y - pDrawable->y;
	    pGC->patOrg.x = x + leftPad - pDrawable->x;

	    mxFillSpans(pDrawable, pGC, h, pptFirst, pwidthFirst, SPANS_SORTED|SPANS_RECT, &box);
	    pGC->stipple = oldStipple;
	    pGC->fillStyle = oldFillStyle;
	    pGC->patOrg = oldPatOrg;
	    }

	    break;
	case XYPixmap:
	    if (depth == 1)
		goto depth_1;
/* printf("XYPixmap: type=%d\n", pDrawable->type); */
	    pImage2 = alloca(w * h);
	    if (!pImage2)
		break;
	    Gather8Bits(pImage2, pImage, w, leftPad, h);
	    pImage = pImage2;
	    /* FALL THROUGH */
	case ZPixmap:
	    if (depth == 1)
		goto depth_1;
	    {
	    u_char *pdstBase;
	    int widthDst;
	    int nlines;
	    void (* movebytes)(u_char *, u_char *, int);

	    movebytes = pPriv->movebytes;
	    prgnDst = pPriv->pCompositeClip;

	    if (pDrawable->type == DRAWABLE_WINDOW)
	    {
		/* quick check to avoid flashing cursor */
		if (!((WindowPtr)pDrawable)->viewable)
			return;
		if (CUR_OVERLAP(box.x1, box.y1, box.x2, box.y2))
			HideCursor();
		iswin = 1;
	    }
	    else
	    {
		pdstBase = ((PixmapPtr)pDrawable)->devPrivate.ptr;
		widthDst = (int)(((PixmapPtr)pDrawable)->devKind);
		iswin = 0;
	    }

	    pbox =  REGION_RECTS(prgnDst);
	    pboxLast = pbox + REGION_NUM_RECTS(prgnDst);
	    while (pbox < pboxLast) {
		if(pbox->y1 > box.y1) {
		    /* Y is not in clip list.  Move y up to the
		     * top of this box.  If it is out of the region
		     * we want to draw then we are done.
		     */
		    pImage += w * (pbox->y1 - box.y1);
		    box.y1 = pbox->y1;
		    if (box.y1 >= box.y2)
			break;
		} else if (pbox->y2 <= box.y1) {
		    pbox++;
		    continue;
		}
		do {
		    /* This do/while loop does 1 y-band. */

		    xStart = max(pbox->x1, box.x1);
		    xEnd = min(pbox->x2, box.x2);
		    xEnd -= xStart;
		    if (xEnd <= 0)
			continue;
		    nlines = min(box.y2, pbox->y2) - box.y1;
		    pImage2 = pImage + (xStart - box.x1);
		    if (iswin) {
			u_char page;
			u_char SVGA_BASED *cp;
			u_long bytenum;

			bytenum = (u_long)box.y1 * svga_width + xStart;
			page = (u_char)(bytenum >> 16);
			if (page != svga_curpage) {
			    svga_curpage = page;
			    SetPage();
			}
			cp = vga_mem((u_short)bytenum);
			while (--nlines >= 0) {
			    if ((u_short)cp + xEnd > 0x10000) {
				i = (u_short)-(short)cp;
				(*movebytes)(cp, pImage2, i);
				cp = vga_mem(0);
				++svga_curpage;
				SetPage();
				(*movebytes)(cp, pImage2 + i, xEnd - i);
				cp = vga_mem((u_short)(svga_width - i));
			    } else {
				(*movebytes)(cp, pImage2, xEnd);
				if ((u_short)cp + svga_width > 0x10000) {
				    cp += svga_width;
				    cp = vga_mem((u_short)cp);
				    ++svga_curpage;
				    SetPage();
				} else
				    cp += svga_width;
			    }
			    pImage2 += w;
			}
		    } else {
			dp = pdstBase + (widthDst * box.y1 + xStart);
			while (--nlines >= 0) {
			    (*movebytes)(dp, pImage2, xEnd);
			    dp += widthDst;
			    pImage2 += w;
			}
		    }
		    /* If the next box is in the same y-band, continue with
		     * this loop.
		     */
		} while (++pbox < pboxLast && pbox->y1 == pbox[-1].y1);
	    }
	    }
	    break;
	depth_1:
	    {
		u_char *pdstBase;
		int widthDst;
		int nlines;
		void (* movebits)(u_char *, u_char *, int, int, int);

		movebits = pPriv->movebits;
		prgnDst = pPriv->pCompositeClip;
		pdstBase = ((PixmapPtr)pDrawable)->devPrivate.ptr;
		widthDst = (int)(((PixmapPtr)pDrawable)->devKind);

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

		/* x is the number of bytes, including pad, in a
		 * scan line of the input image.
		 */

		x = PixmapBytePad(leftPad + w, 1);
		while (pbox < pboxLast) {
		    if(pbox->y1 > box.y1) {
			/* Y is not in clip list.  Move y up to the
			 * top of this box.  If it is out of the region
			 * we want to draw then we are done.
			 */
			pImage += x * (pbox->y1 - box.y1);
			box.y1 = pbox->y1;
			if (box.y1 >= box.y2)
			    break;
		    } else if (pbox->y2 <= box.y1) {
			pbox++;
			continue;
		    }
		    /* Our line may intersect pbox.  Check x now. */
		    if (pbox->x2 <= box.x1) {
			/* This box is to the left of our draw box.
			 * move on to the next one (to the right).
			 */
			pbox++;
			continue;
		    }
		    do {
			/* This do/while loop does 1 y-band. */

			nlines = min(box.y2, pbox->y2) - box.y1;
			xStart = max(pbox->x1, box.x1);
			xEnd = min(pbox->x2, box.x2);
			xEnd -= xStart;
			if (!xEnd)
			    continue;
			/* y is the number of bits offset from the start
			 * of the input image.
			 */
			y = leftPad + xStart - box.x1;
			pImage2 = pImage;

			dp = pdstBase + (widthDst * box.y1);
			while (--nlines >= 0) {
			    (*movebits)(dp, pImage2, xStart, y, xEnd);
			    dp += widthDst;
			    pImage2 += x;
			}
			/* If the next box is in the same y-band, continue with
			 * this loop.
			 */
		    } while (++pbox < pboxLast && pbox->y1 == pbox[-1].y1);
		}
	    }
	    break;
    }
    DEALLOCATE_LOCAL(pwidthFirst);
    DEALLOCATE_LOCAL(pptFirst);
}

void
mxGetImage(pDrawable, sx, sy, w, h, format, planeMask, pdstLine)
    DrawablePtr 	pDrawable;
    int			w, h;
    unsigned		sx, sy;
    unsigned int 	format;
    unsigned long 	planeMask;
    pointer             pdstLine;
{
    int i, j;
    DDXPointPtr		pptFirst, ppt;
    int	*pwidthFirst, *pwidth;
    u_char		*data, *dp;
    u_char		byte, smask, dmask;

    if (!w || !h)
	return;

    ppt = pptFirst = (DDXPointPtr)ALLOCATE_LOCAL(h * sizeof(DDXPointRec));
    pwidth = pwidthFirst = (int *)ALLOCATE_LOCAL(h * sizeof(int));
    if(!pptFirst || !pwidthFirst)
    {
printf("getImage: ALLOCATE_LOCAL failed\n");
       if (pwidthFirst)
	   DEALLOCATE_LOCAL(pwidthFirst);
       if (pptFirst)
	   DEALLOCATE_LOCAL(pptFirst);
       return;
    }

    /* These will be 0 for a pixmap. */
    sx += pDrawable->x;
    sy += pDrawable->y;

    for(i = 0; i < h; i++)
    {
	ppt->x = sx;
	ppt->y = sy + i;
	ppt++;
	*pwidth++ = w;
    }

    if (format == ZPixmap) {
L_ZPixmap:
	mxGetSpans(pDrawable, w, pptFirst, pwidthFirst, h, pdstLine);
    } else {
	if (pDrawable->depth == 1)
	    goto L_ZPixmap;
	data = alloca(w * h);
	if (!data)
	    return;
	mxGetSpans(pDrawable, w, pptFirst, pwidthFirst, h, (pointer)data);
	/* Convert the ZPixmap to XYPixmap */
	for (smask = 0x80; smask; smask >>= 1) {
	    if (!(smask & planeMask))
		continue;
	    j = h;
	    while (--j >= 0) {
		dmask = 0x80;
		dp = pdstLine;
		byte = 0;
		i = w;
		while (--i >= 0) {
		    if (*data & smask)
			byte |= dmask;
		    data++;
		    dmask >>= 1;
		    if (!dmask) {
			dmask = 0x80;
			*dp++ = byte;
			byte = 0;
		    }
		}
		if (dmask != 0x80)
		    *dp++ = byte;
		pdstLine = (char *)pdstLine + PixmapBytePad(w, 1);
	    }
	}
    }
    DEALLOCATE_LOCAL(pwidthFirst);
    DEALLOCATE_LOCAL(pptFirst);
}

static void
Gather8Bits(dp, sp, width, leftPad, h)
unsigned char *dp;	/* dest 'packed' array */
unsigned char *sp;	/* src XYPixmap */
int width;		/* width of dest */
int leftPad;
int h;			/* number of scan lines */
{
    int x, d, dh, l;
    int padw = PixmapBytePad(width + leftPad, 1);
    unsigned char mask;
    unsigned char byte;
    unsigned char p0, p1, p2, p3, p4, p5, p6, p7;

    dh = h;
    while (dh--) {
	x = padw;
	d = width;
	l = leftPad;
	while (x--) {
	    p0 = sp[0];
	    p1 = sp[1 * padw * h];
	    p2 = sp[2 * padw * h];
	    p3 = sp[3 * padw * h];
	    p4 = sp[4 * padw * h];
	    p5 = sp[5 * padw * h];
	    p6 = sp[6 * padw * h];
	    p7 = sp[7 * padw * h];
	    mask = 0x80;
	    while (mask && l-- > 0)
		mask >>= 1;
	    while (mask && d-- > 0) {
		byte = 0;
		if (p0 & mask)
		    byte |= 0x80;
		if (p1 & mask)
		    byte |= 0x40;
		if (p2 & mask)
		    byte |= 0x20;
		if (p3 & mask)
		    byte |= 0x10;
		if (p4 & mask)
		    byte |= 0x08;
		if (p5 & mask)
		    byte |= 0x04;
		if (p6 & mask)
		    byte |= 0x02;
		if (p7 & mask)
		    byte |= 0x01;
		*dp++ = byte;
		mask >>= 1;
	    }
	    sp++;
	}
    }
}
