/***********************************************************
Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts,
and the Massachusetts Institute of Technology, Cambridge, Massachusetts.

                        All Rights Reserved

Permission to use, copy, modify, and distribute this software and its 
documentation for any purpose and without fee is hereby granted, 
provided that the above copyright notice appear in all copies and that
both that copyright notice and this permission notice appear in 
supporting documentation, and that the names of Digital or MIT not be
used in advertising or publicity pertaining to distribution of the
software without specific, written prior permission.  

DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
SOFTWARE.

******************************************************************/
/* $Header: mibitblt.c,v 1.30 87/06/14 19:00:30 todd Exp $ */

#include "X.h"
#include "Xprotostr.h"

#include "misc.h"
#include "gcstruct.h"
#include "pixmapstr.h"
#include "windowstr.h"
#include "scrnintstr.h"
#include "mi.h"
#include "regionstr.h"
#include "Xmd.h"
#include "maskbits.h"
#include "opaque.h"

#if BITMAP←BIT←ORDER == LSBFirst
#define SHIFTOP <<
#else
#define SHIFTOP >>
#endif

/* CopyArea and CopyPlane for generic display


    build a list of source points, 
    get the scanlines into a buffer
    set the scanlines from the buffer
    do graphics exposures
    free the buffer and lists of src points and widths
*/
extern void miOpqStipDrawable();

void
miCopyArea(pSrcDrawable, pDstDrawable,
	    pGC, xIn, yIn, widthSrc, heightSrc, xOut, yOut)
    register DrawablePtr 	pSrcDrawable;
    register DrawablePtr 	pDstDrawable;
    GCPtr 			pGC;
    int 			xIn, yIn;
    int 			widthSrc, heightSrc;
    int 			xOut, yOut;
{
    DDXPointPtr		ppt, pptFirst;
    unsigned int	*pwidthFirst, *pwidth, *pbits;
    BoxRec 		srcBox, *prect;
    			/* may be a new region, or just a copy */
    RegionPtr 		prgnSrcClip;
    			/* non-0 if we've created a src clip */
    int 		realSrcClip = 0;
    int			srcx, srcy, dstx, dsty, i, j, y, width, height,
    			xMin, xMax, yMin, yMax;


    /* clip the left and top edges of the source */
    if (xIn < 0)
    {
        widthSrc += xIn;
        srcx = 0;
    }
    else
	srcx = xIn;
    if (yIn < 0)
    {
        heightSrc += yIn;
        yIn = 0;
    }
    else
	srcy = yIn;

    /* clip the source */

    if (pSrcDrawable->type == DRAWABLE←PIXMAP)
    {
	BoxRec box;

	box.x1 = 0;
	box.y1 = 0;
	box.x2 = ((PixmapPtr)pSrcDrawable)->width;
	box.y2 = ((PixmapPtr)pSrcDrawable)->height;

	prgnSrcClip = (*pGC->pScreen->RegionCreate)(&box, 1);
	realSrcClip = 1;
    }
    else
    {
	srcx += ((WindowPtr)pSrcDrawable)->absCorner.x;
	srcy += ((WindowPtr)pSrcDrawable)->absCorner.y;
	prgnSrcClip = ((WindowPtr)pSrcDrawable)->clipList;
    }

    srcBox.x1 = srcx;
    srcBox.y1 = srcy;
    srcBox.x2 = srcx + widthSrc;
    srcBox.y2 = srcy + heightSrc;

    if (pGC->miTranslate && (pDstDrawable->type == DRAWABLE←WINDOW) )
    {
	dstx = xOut + ((WindowPtr)pDstDrawable)->absCorner.x;
	dsty = yOut + ((WindowPtr)pDstDrawable)->absCorner.y;
    }
    else
    {
	dstx = xOut;
	dsty = yOut;
    }



    pptFirst = ppt = (DDXPointPtr)
        ALLOCATE←LOCAL(heightSrc * sizeof(DDXPointRec));
    pwidthFirst = pwidth = (unsigned int *)
        ALLOCATE←LOCAL(heightSrc * sizeof(unsigned int));
    if(!pptFirst || !pwidthFirst)
    {
       if (pptFirst)
           DEALLOCATE←LOCAL(pptFirst);
       if (pwidthFirst)
           DEALLOCATE←LOCAL(pwidthFirst);
       return;
    }

    prect = prgnSrcClip->rects;
    for(i = 0; i < prgnSrcClip->numRects; i++, prect++)
    {
	xMin = max(prect->x1, srcBox.x1);
	xMax = min(prect->x2, srcBox.x2);
	yMin = max(prect->y1, srcBox.y1);
	yMax = min(prect->y2, srcBox.y2);
	/* is there anything visible here? */
	if(xMax <= xMin || yMax <= yMin)
	    continue;

        ppt = pptFirst;
	pwidth = pwidthFirst;
	y = yMin;
	height = yMax - yMin;
	width = xMax - xMin;

	for(j = 0; j < height; j++)
	{
	    ppt->x = xMin;
	    ppt++->y = y++;
	    *pwidth++ = width;
	}
	pbits = (*pSrcDrawable->pScreen->GetSpans)(pSrcDrawable, width, 
					       pptFirst, pwidthFirst, height);
        ppt = pptFirst;
	pwidth = pwidthFirst;
	xMin -= (srcx - dstx);
	y = yMin - (srcy - dsty);
	for(j = 0; j < height; j++)
	{
	    ppt->x = xMin;
	    ppt++->y = y++;
	    *pwidth++ = width;
	}
	
        (*pGC->SetSpans)(pDstDrawable, pGC, pbits, pptFirst, pwidthFirst,
	                 height, TRUE);
        Xfree(pbits);
    }
    miHandleExposures(pSrcDrawable, pDstDrawable, pGC, xIn, yIn,
		      widthSrc, heightSrc, xOut, yOut);
    if(realSrcClip)
	(*pGC->pScreen->RegionDestroy)(prgnSrcClip);
		
    DEALLOCATE←LOCAL(pptFirst);
    DEALLOCATE←LOCAL(pwidthFirst);
}

unsigned char	*
miGetPlane(pDraw, bitPlane, sx, sy, w, h, result)
    DrawablePtr		pDraw;
    int			bitPlane;
    int			sx, sy, w, h;
    unsigned char	*result;
{
    int			i, j, k, depth, width, bitsPerPixel, widthInBytes;
    DDXPointRec 	pt;
    unsigned int 	*pline;
    unsigned int	bit;
    unsigned char	*pCharsOut;
    CARD16		*pShortsOut;
    CARD32		*pLongsOut;

    depth = pDraw->depth;
    if(pDraw->type == DRAWABLE←PIXMAP)
    {
	w = min(w, ((PixmapPtr)pDraw)->width);
	h = min(h, ((PixmapPtr)pDraw)->height);
    }
    else
    {
	w = min(w, ((WindowPtr)pDraw)->clientWinSize.width);
	h = min(h, ((WindowPtr)pDraw)->clientWinSize.height);
    }
    widthInBytes = PixmapBytePad(w, depth);
    if(!result)
        result = (unsigned char *)Xalloc(h * widthInBytes);
    bitsPerPixel = GetBitsPerPixel(depth);
    if (pDraw->type == DRAWABLE←WINDOW) 
    {
	sx += ((WindowPtr)pDraw)->absCorner.x;
	sy += ((WindowPtr)pDraw)->absCorner.y;
    }
    bzero(result, h * widthInBytes);
    if(BITMAP←SCANLINE←UNIT == 8)
	pCharsOut = (unsigned char *) result;
    else if(BITMAP←SCANLINE←UNIT == 16)
	pShortsOut = (CARD16 *) result;
    else if(BITMAP←SCANLINE←UNIT == 32)
	pLongsOut = (CARD32 *) result;
    if(bitsPerPixel == 1)
	pCharsOut = (unsigned char *) result;
    for(i = sy; i < sy + h; i++)
    {
	if(bitsPerPixel == 1)
	{
	    pt.x = sx;
	    pt.y = i;
	    width = w;
            pline = (*pDraw->pScreen->GetSpans)(pDraw, width, &pt, &width, 1);	
	    bcopy(pline, pCharsOut, (w + 7)/8);
	    pCharsOut += widthInBytes;
	    Xfree(pline);
	}
	else
	{
	    k = 0;
	    for(j = 0; j < w; j++)
	    {
		pt.x = sx + j;
		pt.y = i;
		width = 1;
		/* Fetch the next pixel */
		pline = (*pDraw->pScreen->GetSpans)(pDraw, width, &pt,
		                                   &width, 1);
		/* Use a macro in Xmd.h to grab the appropriate bit */
		bit = (unsigned int) GetBitFromPixel(*pline, bitPlane, depth);
		/* Now insert that bit into a bitmap in XY format */
		SCRRIGHT(bit, k);
		if(BITMAP←SCANLINE←UNIT == 8)
		{
		    *pCharsOut |= (unsigned char) bit;
		    k = (++k == 8) ? pCharsOut++, 0 : k;
		}
		else if(BITMAP←SCANLINE←UNIT == 16)
		{
		    *pShortsOut |= (CARD16) bit;
		    k = (++k == 16) ? pShortsOut++, 0 : k;
		}
		if(BITMAP←SCANLINE←UNIT == 32)
		{
		    *pLongsOut |= (CARD32) bit;
		    k = (++k == 32) ? pLongsOut++, 0 : k;
		}
	        Xfree(pline);
	    }

	}
    }
    return(result);    

}


GetBitsPerPixel(depth)
    int		depth;
{
    int 	i;

    for(i = 0; i < screenInfo.numPixmapFormats; i++)
    {
        if(screenInfo.formats[i].depth == depth)
	{
	    return (screenInfo.formats[i].bitsPerPixel);
	}
    }
    return(1);
}

void
miCopyPlane(pSrcDrawable, pDstDrawable,
	    pGC, srcx, srcy, width, height, dstx, dsty, bitPlane)
    DrawablePtr pSrcDrawable;
    DrawablePtr	pDstDrawable;
    GCPtr	pGC;
    int 	srcx, srcy;
    int 	width, height;
    int 	dstx, dsty;
    int		bitPlane;
{
    unsigned char	*ptile;
    BoxRec 		box;
    RegionPtr		prgnSrc;

    /* strategy: 
     * First build up a bitmap out of the bits requested 
     * build a source clip
     * Use the bitmap we've built up as a Stipple for the destination */


    /* clip the left and top edges of the source */
    if (srcx < 0)
    {
        width += srcx;
        srcx = 0;
    }
    if (srcy < 0)
    {
        height += srcy;
        srcy = 0;
    }

    /* incorporate the source clip */

    if (pSrcDrawable->type != DRAWABLE←PIXMAP)
    {
        box.x1 = ((WindowPtr)pSrcDrawable)->absCorner.x;
        box.y1 = ((WindowPtr)pSrcDrawable)->absCorner.y;
        box.x2 = box.x1 + width;
        box.y2 = box.y1 + height;
        prgnSrc = (*pGC->pScreen->RegionCreate)(&box, 1);
	(*pGC->pScreen->Intersect)
	    (prgnSrc, prgnSrc, ((WindowPtr)pSrcDrawable)->clipList);
	(*pGC->pScreen->TranslateRegion)(prgnSrc,
			-((WindowPtr)pSrcDrawable)->absCorner.x,
			-((WindowPtr)pSrcDrawable)->absCorner.y);
    }
    else
    {
        box.x1 = 0;
        box.y1 = 0;
        box.x2 = ((PixmapPtr)pSrcDrawable)->width;
        box.y2 = ((PixmapPtr)pSrcDrawable)->height;
        prgnSrc = (*pGC->pScreen->RegionCreate)(&box, 1);
    }

    ptile = miGetPlane(pSrcDrawable, bitPlane, srcx, srcy,
   		       width, height, (unsigned char *) NULL);
    miOpqStipDrawable(pDstDrawable, pGC, prgnSrc, (unsigned int *)ptile, 0,
                      width, height, dstx, dsty);
    miHandleExposures(pSrcDrawable, pDstDrawable, pGC, srcx, srcy,
		      width, height, dstx, dsty);
    Xfree(ptile);
    (*pGC->pScreen->RegionDestroy)(prgnSrc);
}

void
miOpqStipDrawable(pDraw, pGC, prgnSrc, pbits, srcx, w, h, dstx, dsty)
    DrawablePtr pDraw;
    GCPtr	pGC;
    RegionPtr	prgnSrc;
    unsigned int	*pbits;
    int		srcx, w, h, dstx, dsty;
{
    int		oldfill, oldfg, i;
    PixmapPtr	pStipple, pPixmap;
    DDXPointRec	oldOrg;
    GCPtr	pGCT;
    DDXPointPtr ppt, pptFirst;
    int		*pwidth, *pwidthFirst;
    xRectangle rect;
    long	gcv[6];

    pPixmap = (PixmapPtr)(*pDraw->pScreen->CreatePixmap)
			   (pDraw->pScreen, w, h, 1);
    if (!pPixmap)
    {
	ErrorF( "miOpqStipDrawable can't make temp pixmap\n");
	return;
    }

    /* Put the image into a 1 bit deep pixmap */
    pGCT = GetScratchGC(1, pDraw->pScreen);
    /* First set the whole pixmap to 0 */
    ValidateGC((DrawablePtr)pPixmap, pGCT);
    ClearDrawable((DrawablePtr)pPixmap, pGCT, w, h);
    ppt = pptFirst = (DDXPointPtr)ALLOCATE←LOCAL(h * sizeof(DDXPointRec));
    pwidth = pwidthFirst = (int *)ALLOCATE←LOCAL(h * sizeof(int));
    if(!ppt || !pwidth)
    {
	DEALLOCATE←LOCAL(pwidthFirst);
	DEALLOCATE←LOCAL(pptFirst);
	FreeScratchGC(pGCT);
	return;
    }

    (*pGCT->ChangeClip)(pGCT, CT←REGION, prgnSrc, 0);
    ValidateGC((DrawablePtr)pPixmap, pGCT);

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

    (*pGC->SetSpans)(pPixmap, pGCT, pbits, pptFirst, pwidthFirst, h, TRUE);


    /* Save current values from the client GC */
    oldfill = pGC->fillStyle;
    pStipple = pGC->stipple;
    oldOrg = pGC->patOrg;

    /* Set a new stipple in the drawable */
    gcv[0] = FillStippled;
    gcv[1] = (int) pPixmap;
    gcv[2] = dstx;
    gcv[3] = dsty;

    ChangeGC(pGC,
             GCFillStyle | GCStipple | GCTileStipXOrigin | GCTileStipYOrigin,
	     gcv);
    ValidateGC(pDraw, pGC);

    /* Fill the drawable with the stipple.  This will draw the
     * foreground color whereever 1 bits are set, leaving everything
     * with 0 bits untouched.  Note that the part outside the clip
     * region is all 0s.  */
    rect.x = dstx;
    rect.y = dsty;
    rect.width = w;
    rect.height = h;
    (*pGC->PolyFillRect)(pDraw, pGC, 1, &rect);

    /* Invert the tiling pixmap. This sets 0s for 1s and 1s for 0s, only
     * within the clipping region, the part outside is still all 0s */
    ChangeGC(pGCT, GCFunction, gcv);
    ValidateGC((DrawablePtr)pPixmap, pGCT);
    (*pGCT->CopyArea)(pPixmap, pPixmap, pGCT, 0, 0, w, h, 0, 0);

    /* Swap foreground and background colors on the GC for the drawable.
     * Now when we fill the drawable, we will fill in the "Background"
     * values */
    oldfg = pGC->fgPixel;
    gcv[0] = pGC->bgPixel;
    gcv[1] = oldfg;
    gcv[2] = (int) pPixmap;
    ChangeGC(pGC, GCForeground | GCBackground | GCStipple, gcv); 
    ValidateGC(pDraw, pGC);
    /* PolyFillRect might have bashed the rectangle */
    rect.x = dstx;
    rect.y = dsty;
    rect.width = w;
    rect.height = h;
    (*pGC->PolyFillRect)(pDraw, pGC, 1, &rect);

    /* Now put things back */
    gcv[0] = oldfg;
    gcv[1] = pGC->fgPixel;
    gcv[2] = oldfill;
    gcv[3] = (int) pStipple;
    gcv[4] = oldOrg.x;
    gcv[5] = oldOrg.y;
    ChangeGC(pGC, 
        GCForeground | GCBackground | GCFillStyle | GCStipple | 
	GCTileStipXOrigin | GCTileStipYOrigin, gcv);

    ValidateGC(pDraw, pGC);
    Xfree(pPixmap);
    FreeScratchGC(pGCT);

}


void
miGetImage(pDraw, sx, sy, w, h, format, planeMask, pdstLine)
    DrawablePtr 	pDraw;
    int			sx, sy, w, h;
    unsigned int 	format;
    unsigned int 	planeMask;
    unsigned char	*pdstLine;
{
    int			depth, i, linelength, fg, width;
    DDXPointRec		pt;
    unsigned int	*pbits, curplane;
    long		gcv[2];
    PixmapPtr		pPixmap;
    xRectangle		rect;
    GCPtr		pGC;

    depth = pDraw->depth;
    if(format == ZPixmap)
    {
	pGC = GetScratchGC(depth, pDraw->pScreen);
        pPixmap = (PixmapPtr)(*pDraw->pScreen->CreatePixmap)
			   (pDraw->pScreen, w, h, depth);
	fg = pGC->fgPixel;

	rect.x = 0;
	rect.y = 0;
	rect.width = w;
	rect.height = h;
	gcv[0] = GXcopy;
	gcv[1] = 0;
	ChangeGC(pGC, GCFunction | GCForeground, gcv);
	ValidateGC((DrawablePtr)pPixmap, pGC);
	(*pGC->PolyFillRect)(pPixmap, pGC, 1, &rect);

	gcv[0] = planeMask;
	gcv[1] = fg;
	ChangeGC(pGC, GCPlaneMask | GCForeground, gcv);
	ValidateGC((DrawablePtr)pPixmap, pGC);
        (*pGC->CopyArea) (pDraw, pPixmap, pGC, sx, sy, w, h, 0, 0);

        linelength = PixmapBytePad(w, depth);
	for(i = 0; i < h; i++)
	{
	    pt.x = 0;
	    pt.y = i;
	    width = w;
            pbits = (*pDraw->pScreen->GetSpans)(pPixmap, w, &pt, &width, 1);
	    bcopy(pbits, pdstLine, linelength);
	    pdstLine += linelength;
	    Xfree(pbits);
	}
	(*pGC->pScreen->DestroyPixmap)(pPixmap);
    }
    else
    {
        linelength = PixmapBytePad(w, 1);
	curplane = 1 << (depth - 1);
	while(curplane)
	{
	    if(curplane & planeMask)
	    {
		pdstLine = miGetPlane(pDraw, depth - 1, sx, sy, w, h, pdstLine);
		pdstLine += linelength;
	    }
	    curplane >>= 1;
	}
    }
}


void
miPutImage(pDraw, pGC, depth, x, y, w, h, leftPad, format, pImage)
    DrawablePtr		pDraw;
    GCPtr		pGC;
    int 		depth, x, y, w, h, leftPad;
    unsigned int	format;
    unsigned char	*pImage;
{
    DDXPointPtr		pptFirst, ppt;
    int			*pwidthFirst, *pwidth, i;
    RegionPtr		prgnSrc;
    BoxRec		box;
    int			oldFg, oldBg, oldPlanemask;
    long		gcv[3];

    switch(format)
    {
      case XYBitmap:

	box.x1 = 0;
	box.y1 = 0;
	box.x2 = w;
	box.y2 = h;
	prgnSrc = (*pGC->pScreen->RegionCreate)(&box, 1);

        miOpqStipDrawable(pDraw, pGC, prgnSrc, (unsigned int *)pImage, leftPad, 
                          (w - leftPad), h, x, y);
	(*pGC->pScreen->RegionDestroy)(prgnSrc);
	break;

      case XYPixmap:
	depth = pGC->depth;
	oldPlanemask = pGC->planemask;
	oldFg = pGC->fgPixel;
	oldBg = pGC->bgPixel;
	gcv[0] = ~0;
	gcv[1] = 0;
	ChangeGC(pGC, GCForeground | GCBackground, gcv);

	for (i = depth-1; i >=0; i--)
	{
	    ChangeGC(pGC, GCPlaneMask, gcv);
	    ValidateGC(pDraw, pGC);
	    (*pGC->PutImage)(pDraw, pGC, 1, x, y, w, h, leftPad,
			     XYBitmap, pImage);
	    pImage += h * PixmapBytePad(w, 1);
	}
	gcv[0] = oldPlanemask;
	gcv[1] = oldFg;
	gcv[2] = oldBg;
	ChangeGC(pGC, GCPlaneMask | GCForeground | GCBackground, gcv);
	break;

      case ZPixmap:
    	ppt = pptFirst = (DDXPointPtr)ALLOCATE←LOCAL(h * sizeof(DDXPointRec));
    	pwidth = pwidthFirst = (int *)ALLOCATE←LOCAL(h * sizeof(int));
	if(!ppt || !pwidth)
        {
           if (ppt)
               DEALLOCATE←LOCAL(ppt);
           else if (pwidth)
               DEALLOCATE←LOCAL(pwidthFirst);
           return;
        }
	if ((pDraw->type == DRAWABLE←WINDOW) &&
	    (pGC->miTranslate))
	{
	    x += ((WindowPtr)(pDraw))->absCorner.x;
	    y += ((WindowPtr)(pDraw))->absCorner.y;
	}

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

	(*pGC->SetSpans)(pDraw, pGC, pImage, pptFirst, pwidthFirst, h, TRUE);
	DEALLOCATE←LOCAL(pptFirst);
	DEALLOCATE←LOCAL(pwidthFirst);
	break;
    }
}