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

#define InitializeShifts(sx,dx,ls,rs) { \
    if (sx != dx) { \
	if (sx > dx) { \
	    ls = sx - dx; \
	    rs = FB_UNIT - ls; \
	} else { \
	    rs = dx - sx; \
	    ls = FB_UNIT - rs; \
	} \
    } \
}

void
fbBlt (FbBits   *srcLine,
       FbStride	srcStride,
       int	srcX,
       
       FbBits   *dstLine,
       FbStride dstStride,
       int	dstX,
       
       int	width, 
       int	height,
       
       int	alu,
       FbBits	pm,
       int	bpp,
       
       Bool	reverse,
       Bool	upsidedown)
{
    FbBits  *src, *dst;
    int	    leftShift, rightShift;
    FbBits  startmask, endmask;
    FbBits  bits, bits1;
    int	    n, nmiddle;
    Bool    destInvarient;
    int	    startbyte, endbyte;
    FbDeclareMergeRop ();

#ifdef FB_24BIT
    if (bpp == 24 && !FbCheck24Pix (pm))
    {
	fbBlt24 (srcLine, srcStride, srcX, dstLine, dstStride, dstX,
		 width, height, alu, pm, reverse, upsidedown);
	return;
    }
#endif

    if (alu == GXcopy && pm == FB_ALLONES && !reverse &&
            !(srcX & 7) && !(dstX & 7) && !(width & 7)) {
        int i;
        CARD8 *src = (CARD8 *) srcLine;
        CARD8 *dst = (CARD8 *) dstLine;
        
        srcStride *= sizeof(FbBits);
        dstStride *= sizeof(FbBits);
        width >>= 3;
        src += (srcX >> 3);
        dst += (dstX >> 3);

        if (!upsidedown)
            for (i = 0; i < height; i++)
                MEMCPY_WRAPPED(dst + i * dstStride, src + i * srcStride, width);
        else
            for (i = height - 1; i >= 0; i--)
                MEMCPY_WRAPPED(dst + i * dstStride, src + i * srcStride, width);

        return;
    }

    FbInitializeMergeRop(alu, pm);
    destInvarient = FbDestInvarientMergeRop();
    if (upsidedown)
    {
	srcLine += (height - 1) * (srcStride);
	dstLine += (height - 1) * (dstStride);
	srcStride = -srcStride;
	dstStride = -dstStride;
    }
    FbMaskBitsBytes (dstX, width, destInvarient, startmask, startbyte,
		     nmiddle, endmask, endbyte);
    if (reverse)
    {
	srcLine += ((srcX + width - 1) >> FB_SHIFT) + 1;
	dstLine += ((dstX + width - 1) >> FB_SHIFT) + 1;
	srcX = (srcX + width - 1) & FB_MASK;
	dstX = (dstX + width - 1) & FB_MASK;
    }
    else
    {
	srcLine += srcX >> FB_SHIFT;
	dstLine += dstX >> FB_SHIFT;
	srcX &= FB_MASK;
	dstX &= FB_MASK;
    }
    if (srcX == dstX)
    {
	while (height--)
	{
	    src = srcLine;
	    srcLine += srcStride;
	    dst = dstLine;
	    dstLine += dstStride;
	    if (reverse)
	    {
		if (endmask)
		{
		    bits = READ(--src);
		    --dst;
		    FbDoRightMaskByteMergeRop(dst, bits, endbyte, endmask);
		}
		n = nmiddle;
		if (destInvarient)
		{
		    while (n--)
			WRITE(--dst, FbDoDestInvarientMergeRop(READ(--src)));
		}
		else
		{
		    while (n--)
		    {
			bits = READ(--src);
			--dst;
			WRITE(dst, FbDoMergeRop (bits, READ(dst)));
		    }
		}
		if (startmask)
		{
		    bits = READ(--src);
		    --dst;
		    FbDoLeftMaskByteMergeRop(dst, bits, startbyte, startmask);
		}
	    }
	    else
	    {
		if (startmask)
		{
		    bits = READ(src++);
		    FbDoLeftMaskByteMergeRop(dst, bits, startbyte, startmask);
		    dst++;
		}
		n = nmiddle;
		if (destInvarient)
		{
#if 0
		    /*
		     * This provides some speedup on screen->screen blts
		     * over the PCI bus, usually about 10%.  But fb
		     * isn't usually used for this operation...
		     */
		    if (_ca2 + 1 == 0 && _cx2 == 0)
		    {
			FbBits	t1, t2, t3, t4;
			while (n >= 4)
			{
			    t1 = *src++;
			    t2 = *src++;
			    t3 = *src++;
			    t4 = *src++;
			    *dst++ = t1;
			    *dst++ = t2;
			    *dst++ = t3;
			    *dst++ = t4;
			    n -= 4;
			}
		    }
#endif
		    while (n--)
			WRITE(dst++, FbDoDestInvarientMergeRop(READ(src++)));
		}
		else
		{
		    while (n--)
		    {
			bits = READ(src++);
			WRITE(dst, FbDoMergeRop (bits, READ(dst)));
			dst++;
		    }
		}
		if (endmask)
		{
		    bits = READ(src);
		    FbDoRightMaskByteMergeRop(dst, bits, endbyte, endmask);
		}
	    }
	}
    }
    else
    {
	if (srcX > dstX)
	{
	    leftShift = srcX - dstX;
	    rightShift = FB_UNIT - leftShift;
	}
	else
	{
	    rightShift = dstX - srcX;
	    leftShift = FB_UNIT - rightShift;
	}
	while (height--)
	{
	    src = srcLine;
	    srcLine += srcStride;
	    dst = dstLine;
	    dstLine += dstStride;
	    
	    bits1 = 0;
	    if (reverse)
	    {
		if (srcX < dstX)
		    bits1 = READ(--src);
		if (endmask)
		{
		    bits = FbScrRight(bits1, rightShift); 
		    if (FbScrRight(endmask, leftShift))
		    {
			bits1 = READ(--src);
			bits |= FbScrLeft(bits1, leftShift);
		    }
		    --dst;
		    FbDoRightMaskByteMergeRop(dst, bits, endbyte, endmask);
		}
		n = nmiddle;
		if (destInvarient)
		{
		    while (n--)
		    {
			bits = FbScrRight(bits1, rightShift); 
			bits1 = READ(--src);
			bits |= FbScrLeft(bits1, leftShift);
			--dst;
			WRITE(dst, FbDoDestInvarientMergeRop(bits));
		    }
		}
		else
		{
		    while (n--)
		    {
			bits = FbScrRight(bits1, rightShift); 
			bits1 = READ(--src);
			bits |= FbScrLeft(bits1, leftShift);
			--dst;
			WRITE(dst, FbDoMergeRop(bits, READ(dst)));
		    }
		}
		if (startmask)
		{
		    bits = FbScrRight(bits1, rightShift); 
		    if (FbScrRight(startmask, leftShift))
		    {
			bits1 = READ(--src);
			bits |= FbScrLeft(bits1, leftShift);
		    }
		    --dst;
		    FbDoLeftMaskByteMergeRop (dst, bits, startbyte, startmask);
		}
	    }
	    else
	    {
		if (srcX > dstX)
		    bits1 = READ(src++);
		if (startmask)
		{
		    bits = FbScrLeft(bits1, leftShift); 
		    if (FbScrLeft(startmask, rightShift))
		    {
			bits1 = READ(src++);
			bits |= FbScrRight(bits1, rightShift);
		    }
		    FbDoLeftMaskByteMergeRop (dst, bits, startbyte, startmask);
		    dst++;
		}
		n = nmiddle;
		if (destInvarient)
		{
		    while (n--)
		    {
			bits = FbScrLeft(bits1, leftShift); 
			bits1 = READ(src++);
			bits |= FbScrRight(bits1, rightShift);
			WRITE(dst, FbDoDestInvarientMergeRop(bits));
			dst++;
		    }
		}
		else
		{
		    while (n--)
		    {
			bits = FbScrLeft(bits1, leftShift); 
			bits1 = READ(src++);
			bits |= FbScrRight(bits1, rightShift);
			WRITE(dst, FbDoMergeRop(bits, READ(dst)));
			dst++;
		    }
		}
		if (endmask)
		{
		    bits = FbScrLeft(bits1, leftShift); 
		    if (FbScrLeft(endmask, rightShift))
		    {
			bits1 = READ(src);
			bits |= FbScrRight(bits1, rightShift);
		    }
		    FbDoRightMaskByteMergeRop (dst, bits, endbyte, endmask);
		}
	    }
	}
    }
}

#ifdef FB_24BIT

#undef DEBUG_BLT24
#ifdef DEBUG_BLT24

static unsigned long
getPixel (char *src, int x)
{
    unsigned long   l;

    l = 0;
    memcpy (&l, src + x * 3, 3);
    return l;
}
#endif

static void
fbBlt24Line (FbBits	    *src,
	     int	    srcX,

	     FbBits	    *dst,
	     int	    dstX,

	     int	    width,

	     int	    alu,
	     FbBits	    pm,
	 
	     Bool	    reverse)
{
#ifdef DEBUG_BLT24
    char    *origDst = (char *) dst;
    FbBits  *origLine = dst + ((dstX >> FB_SHIFT) - 1);
    int	    origNlw = ((width + FB_MASK) >> FB_SHIFT) + 3;
    int	    origX = dstX / 24;
#endif
    
    int	    leftShift, rightShift;
    FbBits  startmask, endmask;
    int	    n;
    
    FbBits  bits, bits1;
    FbBits  mask;

    int	    rot;
    FbDeclareMergeRop ();
    
    FbInitializeMergeRop (alu, FB_ALLONES);
    FbMaskBits(dstX, width, startmask, n, endmask);
#ifdef DEBUG_BLT24
    ErrorF ("dstX %d width %d reverse %d\n", dstX, width, reverse);
#endif
    if (reverse)
    {
	src += ((srcX + width - 1) >> FB_SHIFT) + 1;
	dst += ((dstX + width - 1) >> FB_SHIFT) + 1;
	rot = FbFirst24Rot (((dstX + width - 8) & FB_MASK));
	rot = FbPrev24Rot(rot);
#ifdef DEBUG_BLT24
	ErrorF ("dstX + width - 8: %d rot: %d\n", (dstX + width - 8) & FB_MASK, rot);
#endif
	srcX = (srcX + width - 1) & FB_MASK;
	dstX = (dstX + width - 1) & FB_MASK;
    }
    else
    {
	src += srcX >> FB_SHIFT;
	dst += dstX >> FB_SHIFT;
	srcX &= FB_MASK;
	dstX &= FB_MASK;
	rot = FbFirst24Rot (dstX);
#ifdef DEBUG_BLT24
	ErrorF ("dstX: %d rot: %d\n", dstX, rot);
#endif
    }
    mask = FbRot24(pm,rot);
#ifdef DEBUG_BLT24
    ErrorF ("pm 0x%x mask 0x%x\n", pm, mask);
#endif
    if (srcX == dstX)
    {
	if (reverse)
	{
	    if (endmask)
	    {
		bits = READ(--src);
		--dst;
		WRITE(dst, FbDoMaskMergeRop (bits, READ(dst), mask & endmask));
		mask = FbPrev24Pix (mask);
	    }
	    while (n--)
	    {
		bits = READ(--src);
		--dst;
		WRITE(dst, FbDoMaskMergeRop (bits, READ(dst), mask));
		mask = FbPrev24Pix (mask);
	    }
	    if (startmask)
	    {
		bits = READ(--src);
		--dst;
		WRITE(dst, FbDoMaskMergeRop(bits, READ(dst), mask & startmask));
	    }
	}
	else
	{
	    if (startmask)
	    {
		bits = READ(src++);
		WRITE(dst, FbDoMaskMergeRop (bits, READ(dst), mask & startmask));
		dst++;
		mask = FbNext24Pix(mask);
	    }
	    while (n--)
	    {
		bits = READ(src++);
		WRITE(dst, FbDoMaskMergeRop (bits, READ(dst), mask));
		dst++;
		mask = FbNext24Pix(mask);
	    }
	    if (endmask)
	    {
		bits = READ(src);
		WRITE(dst, FbDoMaskMergeRop(bits, READ(dst), mask & endmask));
	    }
	}
    }
    else
    {
	if (srcX > dstX)
	{
	    leftShift = srcX - dstX;
	    rightShift = FB_UNIT - leftShift;
	}
	else
	{
	    rightShift = dstX - srcX;
	    leftShift = FB_UNIT - rightShift;
	}
	
	bits1 = 0;
	if (reverse)
	{
	    if (srcX < dstX)
		bits1 = READ(--src);
	    if (endmask)
	    {
		bits = FbScrRight(bits1, rightShift); 
		if (FbScrRight(endmask, leftShift))
		{
		    bits1 = READ(--src);
		    bits |= FbScrLeft(bits1, leftShift);
		}
		--dst;
		WRITE(dst, FbDoMaskMergeRop (bits, READ(dst), mask & endmask));
		mask = FbPrev24Pix(mask);
	    }
	    while (n--)
	    {
		bits = FbScrRight(bits1, rightShift); 
		bits1 = READ(--src);
		bits |= FbScrLeft(bits1, leftShift);
		--dst;
		WRITE(dst, FbDoMaskMergeRop(bits, READ(dst), mask));
		mask = FbPrev24Pix(mask);
	    }
	    if (startmask)
	    {
		bits = FbScrRight(bits1, rightShift); 
		if (FbScrRight(startmask, leftShift))
		{
		    bits1 = READ(--src);
		    bits |= FbScrLeft(bits1, leftShift);
		}
		--dst;
		WRITE(dst, FbDoMaskMergeRop (bits, READ(dst), mask & startmask));
	    }
	}
	else
	{
	    if (srcX > dstX)
		bits1 = READ(src++);
	    if (startmask)
	    {
		bits = FbScrLeft(bits1, leftShift); 
		bits1 = READ(src++);
		bits |= FbScrRight(bits1, rightShift);
		WRITE(dst, FbDoMaskMergeRop (bits, READ(dst), mask & startmask));
		dst++;
		mask = FbNext24Pix(mask);
	    }
	    while (n--)
	    {
		bits = FbScrLeft(bits1, leftShift); 
		bits1 = READ(src++);
		bits |= FbScrRight(bits1, rightShift);
		WRITE(dst, FbDoMaskMergeRop(bits, READ(dst), mask));
		dst++;
		mask = FbNext24Pix(mask);
	    }
	    if (endmask)
	    {
		bits = FbScrLeft(bits1, leftShift); 
		if (FbScrLeft(endmask, rightShift))
		{
		    bits1 = READ(src);
		    bits |= FbScrRight(bits1, rightShift);
		}
		WRITE(dst, FbDoMaskMergeRop (bits, READ(dst), mask & endmask));
	    }
	}
    }
#ifdef DEBUG_BLT24
    {
	int firstx, lastx, x;

	firstx = origX;
	if (firstx)
	    firstx--;
	lastx = origX + width/24 + 1;
	for (x = firstx; x <= lastx; x++)
	    ErrorF ("%06x ", getPixel (origDst, x));
	ErrorF ("\n");
	while (origNlw--)
	    ErrorF ("%08x ", *origLine++);
	ErrorF ("\n");
    }
#endif
}

void
fbBlt24 (FbBits	    *srcLine,
	 FbStride   srcStride,
	 int	    srcX,

	 FbBits	    *dstLine,
	 FbStride   dstStride,
	 int	    dstX,

	 int	    width, 
	 int	    height,

	 int	    alu,
	 FbBits	    pm,

	 Bool	    reverse,
	 Bool	    upsidedown)
{
    if (upsidedown)
    {
	srcLine += (height-1) * srcStride;
	dstLine += (height-1) * dstStride;
	srcStride = -srcStride;
	dstStride = -dstStride;
    }
    while (height--)
    {
	fbBlt24Line (srcLine, srcX, dstLine, dstX, width, alu, pm, reverse);
	srcLine += srcStride;
	dstLine += dstStride;
    }
#ifdef DEBUG_BLT24
    ErrorF ("\n");
#endif
}
#endif /* FB_24BIT */

#if FB_SHIFT == FB_STIP_SHIFT + 1

/*
 * Could be generalized to FB_SHIFT > FB_STIP_SHIFT + 1 by
 * creating an ring of values stepped through for each line
 */

void
fbBltOdd (FbBits    *srcLine,
	  FbStride  srcStrideEven,
	  FbStride  srcStrideOdd,
	  int	    srcXEven,
	  int	    srcXOdd,

	  FbBits    *dstLine,
	  FbStride  dstStrideEven,
	  FbStride  dstStrideOdd,
	  int	    dstXEven,
	  int	    dstXOdd,

	  int	    width,
	  int	    height,

	  int	    alu,
	  FbBits    pm,
	  int	    bpp)
{
    FbBits  *src;
    int	    leftShiftEven, rightShiftEven;
    FbBits  startmaskEven, endmaskEven;
    int	    nmiddleEven;
    
    FbBits  *dst;
    int	    leftShiftOdd, rightShiftOdd;
    FbBits  startmaskOdd, endmaskOdd;
    int	    nmiddleOdd;

    int	    leftShift, rightShift;
    FbBits  startmask, endmask;
    int	    nmiddle;
    
    int	    srcX, dstX;
    
    FbBits  bits, bits1;
    int	    n;
    
    Bool    destInvarient;
    Bool    even;
    FbDeclareMergeRop ();

    FbInitializeMergeRop (alu, pm);
    destInvarient = FbDestInvarientMergeRop();

    srcLine += srcXEven >> FB_SHIFT;
    dstLine += dstXEven >> FB_SHIFT;
    srcXEven &= FB_MASK;
    dstXEven &= FB_MASK;
    srcXOdd &= FB_MASK;
    dstXOdd &= FB_MASK;

    FbMaskBits(dstXEven, width, startmaskEven, nmiddleEven, endmaskEven);
    FbMaskBits(dstXOdd, width, startmaskOdd, nmiddleOdd, endmaskOdd);
    
    even = TRUE;
    InitializeShifts(srcXEven, dstXEven, leftShiftEven, rightShiftEven);
    InitializeShifts(srcXOdd, dstXOdd, leftShiftOdd, rightShiftOdd);
    while (height--)
    {
	src = srcLine;
	dst = dstLine;
	if (even)
	{
	    srcX = srcXEven;
	    dstX = dstXEven;
	    startmask = startmaskEven;
	    endmask = endmaskEven;
	    nmiddle = nmiddleEven;
	    leftShift = leftShiftEven;
	    rightShift = rightShiftEven;
	    srcLine += srcStrideEven;
	    dstLine += dstStrideEven;
	    even = FALSE;
	}
	else
	{
	    srcX = srcXOdd;
	    dstX = dstXOdd;
	    startmask = startmaskOdd;
	    endmask = endmaskOdd;
	    nmiddle = nmiddleOdd;
	    leftShift = leftShiftOdd;
	    rightShift = rightShiftOdd;
	    srcLine += srcStrideOdd;
	    dstLine += dstStrideOdd;
	    even = TRUE;
	}
	if (srcX == dstX)
	{
	    if (startmask)
	    {
		bits = READ(src++);
		WRITE(dst, FbDoMaskMergeRop (bits, READ(dst), startmask));
		dst++;
	    }
	    n = nmiddle;
	    if (destInvarient)
	    {
		while (n--)
		{
		    bits = READ(src++);
		    WRITE(dst, FbDoDestInvarientMergeRop(bits));
		    dst++;
		}
	    }
	    else
	    {
		while (n--)
		{
		    bits = READ(src++);
		    WRITE(dst, FbDoMergeRop (bits, READ(dst)));
		    dst++;
		}
	    }
	    if (endmask)
	    {
		bits = READ(src);
		WRITE(dst, FbDoMaskMergeRop(bits, READ(dst), endmask));
	    }
	}
	else
	{
	    bits = 0;
	    if (srcX > dstX)
		bits = READ(src++);
	    if (startmask)
	    {
		bits1 = FbScrLeft(bits, leftShift);
		bits = READ(src++);
		bits1 |= FbScrRight(bits, rightShift);
		WRITE(dst, FbDoMaskMergeRop (bits1, READ(dst), startmask));
		dst++;
	    }
	    n = nmiddle;
	    if (destInvarient)
	    {
		while (n--)
		{
		    bits1 = FbScrLeft(bits, leftShift);
		    bits = READ(src++);
		    bits1 |= FbScrRight(bits, rightShift);
		    WRITE(dst, FbDoDestInvarientMergeRop(bits1));
		    dst++;
		}
	    }
	    else
	    {
		while (n--)
		{
		    bits1 = FbScrLeft(bits, leftShift);
		    bits = READ(src++);
		    bits1 |= FbScrRight(bits, rightShift);
		    WRITE(dst, FbDoMergeRop(bits1, READ(dst)));
		    dst++;
		}
	    }
	    if (endmask)
	    {
		bits1 = FbScrLeft(bits, leftShift);
		if (FbScrLeft(endmask, rightShift))
		{
		    bits = READ(src);
		    bits1 |= FbScrRight(bits, rightShift);
		}
		WRITE(dst, FbDoMaskMergeRop (bits1, READ(dst), endmask));
	    }
	}
    }
}

#ifdef FB_24BIT
void
fbBltOdd24 (FbBits	*srcLine,
	    FbStride	srcStrideEven,
	    FbStride	srcStrideOdd,
	    int		srcXEven,
	    int		srcXOdd,

	    FbBits	*dstLine,
	    FbStride	dstStrideEven,
	    FbStride	dstStrideOdd,
	    int		dstXEven,
	    int		dstXOdd,

	    int		width,
	    int		height,

	    int		alu,
	    FbBits	pm)
{
    Bool    even = TRUE;
    
    while (height--)
    {
	if (even)
	{
	    fbBlt24Line (srcLine, srcXEven, dstLine, dstXEven,
			 width, alu, pm, FALSE);
	    srcLine += srcStrideEven;
	    dstLine += dstStrideEven;
	    even = FALSE;
	}
	else
	{
	    fbBlt24Line (srcLine, srcXOdd, dstLine, dstXOdd,
			 width, alu, pm, FALSE);
	    srcLine += srcStrideOdd;
	    dstLine += dstStrideOdd;
	    even = TRUE;
	}
    }
}
#endif

#endif

#if FB_STIP_SHIFT != FB_SHIFT
void
fbSetBltOdd (FbStip	*stip,
	     FbStride	stipStride,
	     int	srcX,
	     FbBits	**bits,
	     FbStride	*strideEven,
	     FbStride	*strideOdd,
	     int	*srcXEven,
	     int	*srcXOdd)
{
    int	    srcAdjust;
    int	    strideAdjust;

    /*
     * bytes needed to align source
     */
    srcAdjust = (((int) stip) & (FB_MASK >> 3));
    /*
     * FbStip units needed to align stride
     */
    strideAdjust = stipStride & (FB_MASK >> FB_STIP_SHIFT);

    *bits = (FbBits *) ((char *) stip - srcAdjust);
    if (srcAdjust)
    {
	*strideEven = FbStipStrideToBitsStride (stipStride + 1);
	*strideOdd = FbStipStrideToBitsStride (stipStride);

	*srcXEven = srcX + (srcAdjust << 3);
	*srcXOdd = srcX + (srcAdjust << 3) - (strideAdjust << FB_STIP_SHIFT);
    }
    else
    {
	*strideEven = FbStipStrideToBitsStride (stipStride);
	*strideOdd = FbStipStrideToBitsStride (stipStride + 1);
	
	*srcXEven = srcX;
	*srcXOdd = srcX + (strideAdjust << FB_STIP_SHIFT);
    }
}
#endif

void
fbBltStip (FbStip   *src,
	   FbStride srcStride,	    /* in FbStip units, not FbBits units */
	   int	    srcX,
	   
	   FbStip   *dst,
	   FbStride dstStride,	    /* in FbStip units, not FbBits units */
	   int	    dstX,

	   int	    width, 
	   int	    height,

	   int	    alu,
	   FbBits   pm,
	   int	    bpp)
{
#if FB_STIP_SHIFT != FB_SHIFT
    if (FB_STIP_ODDSTRIDE(srcStride) || FB_STIP_ODDPTR(src) ||
	FB_STIP_ODDSTRIDE(dstStride) || FB_STIP_ODDPTR(dst))
    {
	FbStride    srcStrideEven, srcStrideOdd;
	FbStride    dstStrideEven, dstStrideOdd;
	int	    srcXEven, srcXOdd;
	int	    dstXEven, dstXOdd;
	FbBits	    *s, *d;
	int	    sx, dx;
	
	src += srcX >> FB_STIP_SHIFT;
	srcX &= FB_STIP_MASK;
	dst += dstX >> FB_STIP_SHIFT;
	dstX &= FB_STIP_MASK;
	
	fbSetBltOdd (src, srcStride, srcX,
		     &s,
		     &srcStrideEven, &srcStrideOdd,
		     &srcXEven, &srcXOdd);
		     
	fbSetBltOdd (dst, dstStride, dstX,
		     &d,
		     &dstStrideEven, &dstStrideOdd,
		     &dstXEven, &dstXOdd);
		     
#ifdef FB_24BIT
	if (bpp == 24 && !FbCheck24Pix (pm))
	{
	    fbBltOdd24  (s, srcStrideEven, srcStrideOdd,
			 srcXEven, srcXOdd,

			 d, dstStrideEven, dstStrideOdd,
			 dstXEven, dstXOdd,

			 width, height, alu, pm);
	}
	else
#endif
	{
	    fbBltOdd (s, srcStrideEven, srcStrideOdd,
		      srcXEven, srcXOdd,
    
		      d, dstStrideEven, dstStrideOdd,
		      dstXEven, dstXOdd,
    
		      width, height, alu, pm, bpp);
	}
    }
    else
#endif
    {
	fbBlt ((FbBits *) src, FbStipStrideToBitsStride (srcStride), 
	       srcX, 
	       (FbBits *) dst, FbStipStrideToBitsStride (dstStride), 
	       dstX, 
	       width, height,
	       alu, pm, bpp, FALSE, FALSE);
    }
}