/*
 * 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 "fb.h"

/*
 * This is a slight abuse of the preprocessor to generate repetitive
 * code, the idea is to generate code for each case of a copy-mode
 * transparent stipple
 */
#define LaneCases1(c,a)	    case c: \
				while (n--) { FbLaneCase(c,a); a++; } \
				break
#define LaneCases2(c,a)	    LaneCases1(c,a); LaneCases1(c+1,a)
#define LaneCases4(c,a)	    LaneCases2(c,a); LaneCases2(c+2,a)
#define LaneCases8(c,a)	    LaneCases4(c,a); LaneCases4(c+4,a)
#define LaneCases16(c,a)    LaneCases8(c,a); LaneCases8(c+8,a)
#define LaneCases32(c,a)    LaneCases16(c,a); LaneCases16(c+16,a)
#define LaneCases64(c,a)    LaneCases32(c,a); LaneCases32(c+32,a)
#define LaneCases128(c,a)   LaneCases64(c,a); LaneCases64(c+64,a)
#define LaneCases256(c,a)   LaneCases128(c,a); LaneCases128(c+128,a)
    
#if FB_SHIFT == 6
#define LaneCases(a)	    LaneCases256(0,a)
#endif
    
#if FB_SHIFT == 5
#define LaneCases(a)	    LaneCases16(0,a)
#endif
							   
/*
 * Repeat a transparent stipple across a scanline n times
 */

void
fbTransparentSpan (FbBits   *dst,
		   FbBits   stip,
		   FbBits   fgxor,
		   int	    n)
{
    FbStip  s;

    s  = ((FbStip) (stip      ) & 0x01);
    s |= ((FbStip) (stip >>  8) & 0x02);
    s |= ((FbStip) (stip >> 16) & 0x04);
    s |= ((FbStip) (stip >> 24) & 0x08);
#if FB_SHIFT > 5
    s |= ((FbStip) (stip >> 32) & 0x10);
    s |= ((FbStip) (stip >> 40) & 0x20);
    s |= ((FbStip) (stip >> 48) & 0x40);
    s |= ((FbStip) (stip >> 56) & 0x80);
#endif
    switch (s) {
	LaneCases(dst);
    }
}

void
fbEvenStipple (FbBits	*dst,
	       FbStride	dstStride,
	       int	dstX,
	       int	dstBpp,

	       int	width,
	       int	height,

	       FbStip	*stip,
	       FbStride	stipStride,
	       int	stipHeight,

	       FbBits	fgand,
	       FbBits	fgxor,
	       FbBits	bgand,
	       FbBits	bgxor,

	       int	xRot,
	       int	yRot)
{
    FbBits	startmask, endmask;
    FbBits	mask, and, xor;
    int		nmiddle, n;
    FbStip	*s, *stipEnd, bits;
    int		rot, stipX, stipY;
    int		pixelsPerDst;
    const FbBits    *fbBits;
    Bool	transparent;
    int		startbyte, endbyte;
    
    /*
     * Check for a transparent stipple (stencil)
     */
    transparent = FALSE;
    if (dstBpp >= 8 &&
	fgand == 0 && bgand == FB_ALLONES && bgxor == 0)
	transparent = TRUE;
    
    pixelsPerDst = FB_UNIT / dstBpp;
    /*
     * Adjust dest pointers
     */
    dst += dstX >> FB_SHIFT;
    dstX &= FB_MASK;
    FbMaskBitsBytes (dstX, width, fgand == 0 && bgand == 0,
		     startmask, startbyte, nmiddle, endmask, endbyte);
		     
    if (startmask)
	dstStride--;
    dstStride -= nmiddle;
    
    xRot *= dstBpp;
    /*
     * Compute stip start scanline and rotation parameters
     */
    stipEnd = stip + stipStride * stipHeight;
    modulus (- yRot, stipHeight, stipY);
    s = stip + stipStride * stipY;
    modulus (- xRot, FB_UNIT, stipX);
    rot = stipX;
    
    /*
     * Get pointer to stipple mask array for this depth
     */
    /* fbStippleTable covers all valid bpp (4,8,16,32) */
    fbBits = fbStippleTable[pixelsPerDst];
    
    while (height--)
    {
	/*
	 * Extract stipple bits for this scanline;
	 */
	bits = READ(s);
	s += stipStride;
	if (s == stipEnd)
	    s = stip;
#if FB_UNIT > 32
	if (pixelsPerDst == 16)
	    mask = FbStipple16Bits(FbLeftStipBits(bits,16));
	else
#endif
	    mask = fbBits[FbLeftStipBits(bits,pixelsPerDst)];
	/*
	 * Rotate into position and compute reduced rop values
	 */
	mask = FbRotLeft(mask, rot);
	and = (fgand & mask) | (bgand & ~mask);
	xor = (fgxor & mask) | (bgxor & ~mask);
	
	if (transparent)
	{
	    if (startmask)
	    {
		fbTransparentSpan(dst, mask&startmask, fgxor, 1);
		dst++;
	    }
	    fbTransparentSpan (dst, mask, fgxor, nmiddle);
	    dst += nmiddle;
	    if (endmask)
		fbTransparentSpan(dst, mask&endmask, fgxor, 1);
	}
	else
	{
	    /*
	     * Fill scanline
	     */
	    if (startmask)
	    {
		FbDoLeftMaskByteRRop (dst, startbyte, startmask, and, xor);
		dst++;
	    }
	    n = nmiddle;
	    if (!and)
		while (n--)
		    WRITE(dst++, xor);
	    else
	    {
		while (n--)
		{
		    WRITE(dst, FbDoRRop (READ(dst), and, xor));
		    dst++;
		}
	    }
	    if (endmask)
		FbDoRightMaskByteRRop(dst, endbyte, endmask, and, xor);
	}
	dst += dstStride;
    }
}

void
fbOddStipple (FbBits	*dst,
	      FbStride	dstStride,
	      int	dstX,
	      int	dstBpp,

	      int	width,
	      int	height,

	      FbStip	*stip,
	      FbStride	stipStride,
	      int	stipWidth,
	      int	stipHeight,

	      FbBits	fgand,
	      FbBits	fgxor,
	      FbBits	bgand,
	      FbBits	bgxor,

	      int	xRot,
	      int	yRot)
{
    int		stipX, stipY, sx;
    int		widthTmp;
    int		h, w;
    int		x, y;

    modulus (- yRot, stipHeight, stipY);
    modulus (dstX / dstBpp - xRot, stipWidth, stipX);
    y = 0;
    while (height)
    {
	h = stipHeight - stipY;
	if (h > height)
	    h = height;
	height -= h;
	widthTmp = width;
	x = dstX;
	sx = stipX;
	while (widthTmp)
	{
	    w = (stipWidth - sx) * dstBpp;
	    if (w > widthTmp)
		w = widthTmp;
	    widthTmp -= w;
	    fbBltOne (stip + stipY * stipStride,
		      stipStride,
		      sx,

		      dst + y * dstStride,
		      dstStride,
		      x,
		      dstBpp,

		      w, h,

		      fgand, fgxor, bgand, bgxor);
	    x += w;
	    sx = 0;
	}
	y += h;
	stipY = 0;
    }
}

void
fbStipple (FbBits	*dst,
	   FbStride	dstStride,
	   int		dstX,
	   int		dstBpp,

	   int		width,
	   int		height,

	   FbStip	*stip,
	   FbStride	stipStride,
	   int		stipWidth,
	   int		stipHeight,
	   Bool		even,

	   FbBits	fgand,
	   FbBits	fgxor,
	   FbBits	bgand,
	   FbBits	bgxor,

	   int		xRot,
	   int		yRot)
{
    if (even)
	fbEvenStipple (dst, dstStride, dstX, dstBpp, width, height,
		       stip, stipStride, stipHeight,
		       fgand, fgxor, bgand, bgxor, xRot, yRot);
    else
	fbOddStipple (dst, dstStride, dstX, dstBpp, width, height,
		      stip, stipStride, stipWidth, stipHeight,
		      fgand, fgxor, bgand, bgxor, xRot, yRot);
}
