/*
 * Copyright © 1998 Keith Packard
 *
 * Permission to use, copy, modify, distribute, and sell this software and its
 * documentation for any purpose is hereby granted without fee, 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 name of Keith Packard not be used in
 * advertising or publicity pertaining to distribution of the software without
 * specific, written prior permission.  Keith Packard makes no
 * representations about the suitability of this software for any purpose.  It
 * is provided "as is" without express or implied warranty.
 *
 * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
 * EVENT SHALL KEITH PACKARD 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.
 */

#ifdef HAVE_DIX_CONFIG_H
#include <dix-config.h>
#endif

#include <stdlib.h>

#include "fb.h"

const GCFuncs fbGCFuncs = {
    fbValidateGC,
    miChangeGC,
    miCopyGC,
    miDestroyGC,
    miChangeClip,
    miDestroyClip,
    miCopyClip,
};

const GCOps	fbGCOps = {
    fbFillSpans,
    fbSetSpans,
    fbPutImage,
    fbCopyArea,
    fbCopyPlane,
    fbPolyPoint,
    fbPolyLine,
    fbPolySegment,
    fbPolyRectangle,
    fbPolyArc,
    miFillPolygon,
    fbPolyFillRect,
    fbPolyFillArc,
    miPolyText8,
    miPolyText16,
    miImageText8,
    miImageText16,
    fbImageGlyphBlt,
    fbPolyGlyphBlt,
    fbPushPixels
};

Bool
fbCreateGC(GCPtr pGC)
{
    pGC->clientClip = NULL;
    pGC->clientClipType = CT_NONE;

    pGC->ops = (GCOps *) &fbGCOps;
    pGC->funcs = (GCFuncs *) &fbGCFuncs;

    /* fb wants to translate before scan conversion */
    pGC->miTranslate = 1;

    fbGetRotatedPixmap(pGC) = 0;
    fbGetExpose(pGC) = 1;
    fbGetFreeCompClip(pGC) = 0;
    fbGetCompositeClip(pGC) = 0;
    fbGetGCPrivate(pGC)->bpp = BitsPerPixel (pGC->depth);
    return TRUE;
}

/*
 * Pad pixmap to FB_UNIT bits wide
 */
void
fbPadPixmap (PixmapPtr pPixmap)
{
    int	    width;
    FbBits  *bits;
    FbBits  b;
    FbBits  mask;
    int	    height;
    int	    w;
    int     stride;
    int     bpp;
    int     xOff, yOff;

    fbGetDrawable (&pPixmap->drawable, bits, stride, bpp, xOff, yOff);

    width = pPixmap->drawable.width * pPixmap->drawable.bitsPerPixel;
    height = pPixmap->drawable.height;
    mask = FbBitsMask (0, width);
    while (height--)
    {
	b = READ(bits) & mask;
	w = width;
	while (w < FB_UNIT)
	{
	    b = b | FbScrRight(b, w);
	    w <<= 1;
	}
	WRITE(bits, b);
	bits += stride;
    }

    fbFinishAccess (&pPixmap->drawable);
}

/*
 * Verify that 'bits' repeats every 'len' bits
 */
static Bool
fbBitsRepeat (FbBits bits, int len, int width)
{
    FbBits  mask = FbBitsMask(0, len);
    FbBits  orig = bits & mask;
    int	    i;
    
    if (width > FB_UNIT)
	width = FB_UNIT;
    for (i = 0; i < width / len; i++)
    {
	if ((bits & mask) != orig)
	    return FALSE;
	bits = FbScrLeft(bits,len);
    }
    return TRUE;
}

/*
 * Check whether an entire bitmap line is a repetition of
 * the first 'len' bits
 */
static Bool
fbLineRepeat (FbBits *bits, int len, int width)
{
    FbBits  first = bits[0];
    
    if (!fbBitsRepeat (first, len, width))
	return FALSE;
    width = (width + FB_UNIT-1) >> FB_SHIFT;
    bits++;
    while (--width)
	if (READ(bits) != first)
	    return FALSE;
    return TRUE;
}

/*
 * The even stipple code wants the first FB_UNIT/bpp bits on
 * each scanline to represent the entire stipple
 */
static Bool
fbCanEvenStipple (PixmapPtr pStipple, int bpp)
{
    int	    len = FB_UNIT / bpp;
    FbBits  *bits;
    int	    stride;
    int	    stip_bpp;
    int	    stipXoff, stipYoff;
    int	    h;

    /* can't even stipple 24bpp drawables */
    if ((bpp & (bpp-1)) != 0)
	return FALSE;
    /* make sure the stipple width is a multiple of the even stipple width */
    if (pStipple->drawable.width % len != 0)
	return FALSE;
    fbGetDrawable (&pStipple->drawable, bits, stride, stip_bpp, stipXoff, stipYoff);
    h = pStipple->drawable.height;
    /* check to see that the stipple repeats horizontally */
    while (h--)
    {
	if (!fbLineRepeat (bits, len, pStipple->drawable.width)) {
	    fbFinishAccess (&pStipple->drawable);
	    return FALSE;
	}
	bits += stride;
    }
    fbFinishAccess (&pStipple->drawable);
    return TRUE;
}

void
fbValidateGC(GCPtr pGC, unsigned long changes, DrawablePtr pDrawable)
{
    FbGCPrivPtr	pPriv = fbGetGCPrivate(pGC);
    FbBits	mask;

    pGC->lastWinOrg.x = pDrawable->x;
    pGC->lastWinOrg.y = pDrawable->y;

    /*
     * if the client clip is different or moved OR the subwindowMode has
     * changed OR the window's clip has changed since the last validation
     * we need to recompute the composite clip 
     */

    if ((changes & (GCClipXOrigin|GCClipYOrigin|GCClipMask|GCSubwindowMode)) ||
	(pDrawable->serialNumber != (pGC->serialNumber & DRAWABLE_SERIAL_BITS))
	)
    {
	miComputeCompositeClip (pGC, pDrawable);
	pPriv->oneRect = REGION_NUM_RECTS(fbGetCompositeClip(pGC)) == 1;
    }
    
#ifdef FB_24_32BIT    
    if (pPriv->bpp != pDrawable->bitsPerPixel)
    {
	changes |= GCStipple|GCForeground|GCBackground|GCPlaneMask;
	pPriv->bpp = pDrawable->bitsPerPixel;
    }
    if ((changes & GCTile) && fbGetRotatedPixmap(pGC))
    {
	(*pGC->pScreen->DestroyPixmap) (fbGetRotatedPixmap(pGC));
	fbGetRotatedPixmap(pGC) = 0;
    }
	
    if (pGC->fillStyle == FillTiled)
    {
	PixmapPtr	pOldTile, pNewTile;

	pOldTile = pGC->tile.pixmap;
	if (pOldTile->drawable.bitsPerPixel != pDrawable->bitsPerPixel)
	{
	    pNewTile = fbGetRotatedPixmap(pGC);
	    if (!pNewTile || pNewTile ->drawable.bitsPerPixel != pDrawable->bitsPerPixel)
	    {
		if (pNewTile)
		    (*pGC->pScreen->DestroyPixmap) (pNewTile);
		pNewTile = fb24_32ReformatTile (pOldTile, pDrawable->bitsPerPixel);
	    }
	    if (pNewTile)
	    {
		fbGetRotatedPixmap(pGC) = pOldTile;
		pGC->tile.pixmap = pNewTile;
		changes |= GCTile;
	    }
	}
    }
#endif
    if (changes & GCTile)
    {
	if (!pGC->tileIsPixel && 
	    FbEvenTile (pGC->tile.pixmap->drawable.width *
			pDrawable->bitsPerPixel))
	    fbPadPixmap (pGC->tile.pixmap);
    }
    if (changes & GCStipple)
    {
	pPriv->evenStipple = FALSE;

	if (pGC->stipple) {

	    /* can we do an even stipple ?? */
	    if (FbEvenStip (pGC->stipple->drawable.width,
						pDrawable->bitsPerPixel) &&
	       (fbCanEvenStipple (pGC->stipple, pDrawable->bitsPerPixel)))
	   	pPriv->evenStipple = TRUE;

	    if (pGC->stipple->drawable.width * pDrawable->bitsPerPixel < FB_UNIT)
		fbPadPixmap (pGC->stipple);
	}
    }
    /*
     * Recompute reduced rop values
     */
    if (changes & (GCForeground|GCBackground|GCPlaneMask|GCFunction))
    {
	int	s;
	FbBits	depthMask;
	
	mask = FbFullMask(pDrawable->bitsPerPixel);
	depthMask = FbFullMask(pDrawable->depth);
	
	pPriv->fg = pGC->fgPixel & mask;
	pPriv->bg = pGC->bgPixel & mask;
	
	if ((pGC->planemask & depthMask) == depthMask)
	    pPriv->pm = mask;
	else
	    pPriv->pm = pGC->planemask & mask;    
	
	s = pDrawable->bitsPerPixel;
	while (s < FB_UNIT)
	{
	    pPriv->fg |= pPriv->fg << s;
	    pPriv->bg |= pPriv->bg << s;
	    pPriv->pm |= pPriv->pm << s;
	    s <<= 1;
	}
	pPriv->and = fbAnd(pGC->alu, pPriv->fg, pPriv->pm);
	pPriv->xor = fbXor(pGC->alu, pPriv->fg, pPriv->pm);
	pPriv->bgand = fbAnd(pGC->alu, pPriv->bg, pPriv->pm);
	pPriv->bgxor = fbXor(pGC->alu, pPriv->bg, pPriv->pm);
    }
    if (changes & GCDashList)
    {
	unsigned short	n = pGC->numInDashList;
	unsigned char	*dash = pGC->dash;
	unsigned int	dashLength = 0;

	while (n--)
	    dashLength += (unsigned int ) *dash++;
	pPriv->dashLength = dashLength;
    }
}