xenocara/xserver/fb/fbblt.c
2011-11-05 13:32:40 +00:00

949 lines
20 KiB
C

/*
* 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;
int careful;
FbDeclareMergeRop ();
if (bpp == 24 && !FbCheck24Pix (pm))
{
fbBlt24 (srcLine, srcStride, srcX, dstLine, dstStride, dstX,
width, height, alu, pm, reverse, upsidedown);
return;
}
careful = !((srcLine < dstLine && srcLine + width * (bpp>>3) > dstLine) ||
(dstLine < srcLine && dstLine + width * (bpp>>3) > srcLine)) ||
(bpp & 7);
if (alu == GXcopy && pm == FB_ALLONES && !careful &&
!(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);
}
}
}
}
}
#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
}
#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));
}
}
}
}
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
#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);
if (bpp == 24 && !FbCheck24Pix (pm))
{
fbBltOdd24 (s, srcStrideEven, srcStrideOdd,
srcXEven, srcXOdd,
d, dstStrideEven, dstStrideOdd,
dstXEven, dstXOdd,
width, height, alu, pm);
}
else
{
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);
}
}