WindowsXP-SP1/windows/advcore/gdiplus/engine/render/texturefill.cpp
2020-09-30 16:53:49 +02:00

1574 lines
43 KiB
C++

/**************************************************************************\
*
* Copyright (c) 1998 Microsoft Corporation
*
* Module Name:
*
* TextureFill.cpp
*
* Abstract:
*
* texture fill routines.
*
* Revision History:
*
* 01/21/1999 ikkof
* Created it.
*
\**************************************************************************/
#include "precomp.hpp"
/**************************************************************************\
*
* Function Description:
*
* Initializes the fixed point variables needed for texture mapping.
*
* Created:
*
* 03/14/2000 andrewgo
*
\**************************************************************************/
VOID
DpOutputBilinearSpan_MMX::InitializeFixedPointState()
{
// If the numbers are too large for 16.16 fixed, we'll do the computation
// in floating-point, per call to OutputSpan:
ScaleMatrixValid = GpValidFixed16(DeviceToWorld.GetM11()) &&
GpValidFixed16(DeviceToWorld.GetM12()) &&
GpValidFixed16(DeviceToWorld.GetM21()) &&
GpValidFixed16(DeviceToWorld.GetM22());
if (ScaleMatrixValid)
{
GpPointF vector(1.0f, 0.0f);
DeviceToWorld.VectorTransform(&vector);
UIncrement = GpRound(vector.X * (1L << 16));
VIncrement = GpRound(vector.Y * (1L << 16));
TranslateMatrixValid = GpValidFixed16(DeviceToWorld.GetDx()) &&
GpValidFixed16(DeviceToWorld.GetDy());
if (TranslateMatrixValid)
{
M11 = GpRound(DeviceToWorld.GetM11() * (1L << 16));
M12 = GpRound(DeviceToWorld.GetM12() * (1L << 16));
M21 = GpRound(DeviceToWorld.GetM21() * (1L << 16));
M22 = GpRound(DeviceToWorld.GetM22() * (1L << 16));
Dx = GpRound(DeviceToWorld.GetDx() * (1L << 16));
Dy = GpRound(DeviceToWorld.GetDy() * (1L << 16));
}
ModulusWidth = (BmpData.Width << 16);
ModulusHeight = (BmpData.Height << 16);
// When the u,v coordinates have the pixel in the last row or column
// of the texture space, the offset of the pixel to the right and the
// pixel below (for bilinear filtering) is the following (for tile modes)
// because they wrap around the texture space.
// The XEdgeIncrement is the byte increment of the pixel to the right of
// the pixel on the far right hand column of the texture. In tile mode,
// we want the pixel on the same scanline, but in the first column of the
// texture hence 4bytes - stride
XEdgeIncrement = 4-BmpData.Stride;
// The YEdgeIncrement is the byte increment of the pixel below the current
// pixel when the current pixel is in the last scanline of the texture.
// In tile mode the correct pixel is the one directly above this one in
// the first scanline - hence the increment below:
YEdgeIncrement = -(INT)(BmpData.Height-1)*(INT)(BmpData.Stride);
if ((BilinearWrapMode == WrapModeTileFlipX) ||
(BilinearWrapMode == WrapModeTileFlipXY))
{
ModulusWidth *= 2;
// Wrap increment is zero for Flip mode
XEdgeIncrement = 0;
}
if ((BilinearWrapMode == WrapModeTileFlipY) ||
(BilinearWrapMode == WrapModeTileFlipXY))
{
ModulusHeight *= 2;
// Wrap increment is zero for Flip mode
YEdgeIncrement = 0;
}
}
}
/**************************************************************************\
*
* Function Description:
*
* Texture brush constructor.
*
* Arguments:
*
* Created:
*
* 04/26/1999 ikkof
*
\**************************************************************************/
DpOutputBilinearSpan::DpOutputBilinearSpan(
const GpTexture *textureBrush,
DpScanBuffer *scan,
GpMatrix *worldToDevice,
DpContext *context
)
{
ASSERT(textureBrush);
dBitmap = NULL;
Scan = scan;
// Initialize the wrap state.
BilinearWrapMode = textureBrush->GetWrapMode();
ClampColor = 0;
SrcRectClamp = FALSE;
if(textureBrush->GetImageType() != ImageTypeBitmap)
{
// Currently, Metafile is not implemented yet.
Bitmap = NULL;
return;
}
Bitmap = textureBrush->GetBitmap();
// on bad bitmap, we return with Valid = FALSE
if (Bitmap == NULL ||
!Bitmap->IsValid() ||
Bitmap->LockBits(
NULL,
IMGLOCK_READ,
PixelFormat32bppPARGB,
&BmpData
) != Ok)
{
Bitmap = NULL;
return;
}
Size size;
Bitmap->GetSize(&size);
SrcRect.X = SrcRect.Y = 0;
SrcRect.Width = (REAL)size.Width;
SrcRect.Height = (REAL)size.Height;
WorldToDevice = *worldToDevice;
// If we have a PixelOffset of Half then offset the srcRect by -0.5
// need to change the WorldToDevice to take this into account.
// We need to do this here because texture brushes don't have enough
// information to handle the PixelOffset at a higher level. We construct
// the SrcRect here, so we apply the PixelOffset immediately.
if (context->PixelOffset == PixelOffsetModeHalf ||
context->PixelOffset == PixelOffsetModeHighQuality)
{
SrcRect.Offset(-0.5f, -0.5f);
WorldToDevice.Translate(0.5f, 0.5f, MatrixOrderPrepend);
}
if(WorldToDevice.IsInvertible())
{
// !!![andrewgo] This failure case is bad, we will fall over
// later with an uninitialized DeviceToWorld.
DeviceToWorld = WorldToDevice;
DeviceToWorld.Invert();
}
}
DpOutputBilinearSpan::DpOutputBilinearSpan(
const DpBitmap *bitmap,
DpScanBuffer *scan,
GpMatrix *worldToDevice,
DpContext *context,
DpImageAttributes *imageAttributes
)
{
ASSERT(bitmap);
dBitmap = bitmap;
Scan = scan;
// Set the imageAttributes state that's relevant to the bilinear span.
BilinearWrapMode = imageAttributes->wrapMode;
ClampColor = imageAttributes->clampColor;
SrcRectClamp = imageAttributes->srcRectClamp;
Bitmap = NULL;
// on bad bitmap, we return with Valid = FALSE
if (dBitmap == NULL || !dBitmap->IsValid() )
{
dBitmap = NULL;
return;
}
else
{
BmpData.Width = dBitmap->Width;
BmpData.Height = dBitmap->Height;
BmpData.PixelFormat = PIXFMT_32BPP_PARGB;
BmpData.Stride = dBitmap->Delta;
BmpData.Scan0 = dBitmap->Bits;
}
// NOTE: SrcRect is not used.
// The HalfPixelOffset is already incorporated into the
// wordToDevice matrix passed in - which is not the same matrix as
// context->WorldToDevice
SrcRect.X = 0;
SrcRect.Y = 0;
SrcRect.Width = (REAL)dBitmap->Width;
SrcRect.Height = (REAL)dBitmap->Height;
WorldToDevice = *worldToDevice;
if(WorldToDevice.IsInvertible())
{
// !!![andrewgo] This failure case is bad, we will fall over
// later with an uninitialized DeviceToWorld.
DeviceToWorld = WorldToDevice;
DeviceToWorld.Invert();
}
}
/**************************************************************************\
*
* Function Description:
*
* Texture brush constructor.
*
* Arguments:
*
* Created:
*
* 04/26/1999 ikkof
*
\**************************************************************************/
DpOutputBilinearSpan::DpOutputBilinearSpan(
DpBitmap* bitmap,
DpScanBuffer * scan,
DpContext* context,
DpImageAttributes imageAttributes,
INT numPoints,
const GpPointF *dstPoints,
const GpRectF *srcRect
)
{
// NOTE: This constructor is not used.
// I have no idea if it even works anymore.
WARNING(("DpOutputBilinearSpan: unsupported constructor"));
Scan = scan;
BilinearWrapMode = imageAttributes.wrapMode;
ClampColor = imageAttributes.clampColor;
SrcRectClamp = imageAttributes.srcRectClamp;
dBitmap = bitmap;
Bitmap = NULL;
// on bad bitmap, we return with Valid = FALSE
if (dBitmap == NULL || !dBitmap->IsValid() )
{
dBitmap = NULL;
return;
}
else
{
BmpData.Width = dBitmap->Width;
BmpData.Height = dBitmap->Height;
BmpData.PixelFormat = PIXFMT_32BPP_PARGB;
BmpData.Stride = dBitmap->Delta;
BmpData.Scan0 = dBitmap->Bits;
}
WorldToDevice = context->WorldToDevice;
context->GetDeviceToWorld(&DeviceToWorld);
// If we have a srcRect then it's already taking the PixelOffset mode into
// account.
if(srcRect)
SrcRect = *srcRect;
else
{
SrcRect.X = 0;
SrcRect.Y = 0;
SrcRect.Width = (REAL)dBitmap->Width;
SrcRect.Height = (REAL)dBitmap->Height;
if (context->PixelOffset == PixelOffsetModeHalf ||
context->PixelOffset == PixelOffsetModeHighQuality)
{
SrcRect.Offset(-0.5f, -0.5f);
}
}
GpPointF points[4];
GpMatrix xForm;
BOOL existsTransform = TRUE;
switch(numPoints)
{
case 0:
points[0].X = 0;
points[0].Y = 0;
points[1].X = (REAL) SrcRect.Width;
points[1].Y = 0;
points[2].X = 0;
points[2].Y = (REAL) SrcRect.Height;
break;
case 1:
points[0] = dstPoints[0];
points[1].X = (REAL) (points[0].X + SrcRect.Width);
points[1].Y = points[0].Y;
points[2].X = points[0].X;
points[2].Y = (REAL) (points[0].Y + SrcRect.Height);
break;
case 3:
case 4:
GpMemcpy(&points[0], dstPoints, numPoints*sizeof(GpPointF));
break;
default:
existsTransform = FALSE;
}
if(existsTransform)
{
xForm.InferAffineMatrix(points, SrcRect);
}
WorldToDevice = context->WorldToDevice;
WorldToDevice.Prepend(xForm);
if(WorldToDevice.IsInvertible())
{
DeviceToWorld = WorldToDevice;
DeviceToWorld.Invert();
}
}
/**************************************************************************\
*
* Function Description:
*
* From the ARGB value of the four corners, this returns
* the bilinearly interpolated ARGB value.
*
* Arguments:
*
* [IN] colors - ARGB values at the four corners.
* [IN] xFrac - the fractional value of the x-coordinates.
* [IN] yFrac - the fractional value of the y-coordinates.
* [IN] one, shift. half2, shift2 - the extra arguments used in the
* calculations.
*
* Return Value:
*
* ARGB: returns the biliearly interpolated ARGB.
*
* Created:
*
* 04/26/1999 ikkof
*
\**************************************************************************/
inline ARGB
getBilinearFilteredARGB(
ARGB* colors,
INT xFrac,
INT yFrac,
INT one,
INT shift,
INT half2,
INT shift2)
{
INT a[4], r[4], g[4], b[4];
INT alpha, red, green, blue;
for(INT k = 0; k < 4; k++)
{
ARGB c = colors[k];
a[k] = GpColor::GetAlphaARGB(c);
r[k] = GpColor::GetRedARGB(c);
g[k] = GpColor::GetGreenARGB(c);
b[k] = GpColor::GetBlueARGB(c);
}
alpha =
(
(one - yFrac)*((a[0] << shift)
+ (a[1] - a[0])*xFrac)
+ yFrac*((a[2] << shift)
+ (a[3] - a[2])*xFrac)
+ half2
) >> shift2;
red =
(
(one - yFrac)*((r[0] << shift)
+ (r[1] - r[0])*xFrac)
+ yFrac*((r[2] << shift)
+ (r[3] - r[2])*xFrac)
+ half2
) >> shift2;
green =
(
(one - yFrac)*((g[0] << shift)
+ (g[1] - g[0])*xFrac)
+ yFrac*((g[2] << shift)
+ (g[3] - g[2])*xFrac)
+ half2
) >> shift2;
blue =
(
(one - yFrac)*((b[0] << shift)
+ (b[1] - b[0])*xFrac)
+ yFrac*((b[2] << shift)
+ (b[3] - b[2])*xFrac)
+ half2
) >> shift2;
return GpColor::MakeARGB
(
(BYTE) alpha,
(BYTE) red,
(BYTE) green,
(BYTE) blue
);
}
/**************************************************************************\
*
* Function Description:
* virtual destructor for DpOutputBilinearSpan
*
\**************************************************************************/
DpOutputBilinearSpan::~DpOutputBilinearSpan()
{
if (Bitmap != NULL)
{
Bitmap->UnlockBits(&BmpData);
}
}
/**************************************************************************\
*
* Function Description:
*
* Applies the correct wrap mode to a set of coordinates
*
* Created:
*
* 03/10/2000 asecchia
*
\**************************************************************************/
void ApplyWrapMode(INT WrapMode, INT &x, INT &y, INT w, INT h)
{
INT xm, ym;
switch(WrapMode) {
case WrapModeTile:
x = RemainderI(x, w);
y = RemainderI(y, h);
break;
case WrapModeTileFlipX:
xm = RemainderI(x, w);
if(((x-xm)/w) & 1) {
x = w-1-xm;
}
else
{
x = xm;
}
y = RemainderI(y, h);
break;
case WrapModeTileFlipY:
x = RemainderI(x, w);
ym = RemainderI(y, h);
if(((y-ym)/h) & 1) {
y = h-1-ym;
}
else
{
y = ym;
}
break;
case WrapModeTileFlipXY:
xm = RemainderI(x, w);
if(((x-xm)/w) & 1) {
x = w-1-xm;
}
else
{
x = xm;
}
ym = RemainderI(y, h);
if(((y-ym)/h) & 1) {
y = h-1-ym;
}
else
{
y = ym;
}
break;
/*
// WrapModeExtrapolate is no longer used.
case WrapModeExtrapolate:
// Clamp the coordinates to the edge pixels of the source
if(x<0) x=0;
if(x>w-1) x=w-1;
if(y<0) y=0;
if(y>h-1) y=h-1;
break;
*/
case WrapModeClamp:
// Don't do anything - the filter code will substitute the clamp
// color when it detects clamp.
default:
break;
}
}
/**************************************************************************\
*
* Function Description:
*
* Outputs a single span within a raster with a texture.
* Is called by the rasterizer.
*
* Arguments:
*
* [IN] y - the Y value of the raster being output
* [IN] leftEdge - the DDA class of the left edge
* [IN] rightEdge - the DDA class of the right edge
*
* Return Value:
*
* GpStatus - Ok
*
* Created:
*
* 01/21/1999 ikkof
*
\**************************************************************************/
GpStatus
DpOutputBilinearSpan::OutputSpan(
INT y,
INT xMin,
INT xMax // xMax is exclusive
)
{
// Nothing to do.
if(xMin==xMax)
{
return Ok;
}
ASSERT(xMin < xMax);
ARGB argb;
INT width = xMax - xMin;
ARGB * buffer = Scan->NextBuffer(xMin, y, width);
GpPointF pt1, pt2;
pt1.X = (REAL) xMin;
pt1.Y = pt2.Y = (REAL) y;
pt2.X = (REAL) xMax;
DeviceToWorld.Transform(&pt1);
DeviceToWorld.Transform(&pt2);
ARGB *srcPtr0 = static_cast<ARGB*> (BmpData.Scan0);
INT stride = BmpData.Stride/sizeof(ARGB);
INT i;
REAL dx, dy, x0, y0;
INT ix, iy;
x0 = pt1.X;
y0 = pt1.Y;
ASSERT(width > 0);
dx = (pt2.X - pt1.X)/width;
dy = (pt2.Y - pt1.Y)/width;
// Filtered image stretch.
ARGB *srcPtr1, *srcPtr2;
INT shift = 11; // (2*shift + 8 < 32 bits --> shift < 12)
INT shift2 = shift + shift;
INT one = 1 << shift;
INT half2 = 1 << (shift2 - 1);
INT xFrac, yFrac;
REAL real;
ARGB colors[4];
INT x1, y1, x2, y2;
for(i = 0; i < width; i++)
{
iy = GpFloor(y0);
ix = GpFloor(x0);
xFrac = GpRound((x0 - ix)*one);
yFrac = GpRound((y0 - iy)*one);
x1=ix;
x2=ix+1;
y1=iy;
y2=iy+1;
if( ((UINT)ix >= (UINT)(BmpData.Width-1) ) ||
((UINT)iy >= (UINT)(BmpData.Height-1)) )
{
ApplyWrapMode(BilinearWrapMode, x1, y1, BmpData.Width, BmpData.Height);
ApplyWrapMode(BilinearWrapMode, x2, y2, BmpData.Width, BmpData.Height);
}
if(y1 >= 0 && y1 < (INT) BmpData.Height)
{
srcPtr1 = srcPtr0 + stride*y1;
}
else
{
srcPtr1 = NULL;
}
if(y2 >= 0 && y2 < (INT) BmpData.Height)
{
srcPtr2 = srcPtr0 + stride*y2;
}
else
{
srcPtr2 = NULL;
}
if(x1 >= 0 && x1 < (INT) BmpData.Width)
{
if(srcPtr1)
{
colors[0] = *(srcPtr1 + x1);
}
else
{
colors[0] = ClampColor;
}
if(srcPtr2)
{
colors[2] = *(srcPtr2 + x1);
}
else
{
colors[2] = ClampColor;
}
}
else
{
colors[0] = ClampColor;
colors[2] = ClampColor;
}
if(x2 >= 0 && x2 < (INT) BmpData.Width)
{
if(srcPtr1)
{
colors[1] = *(srcPtr1 + x2);
}
else
{
colors[1] = ClampColor;
}
if(srcPtr2)
{
colors[3] = *(srcPtr2 + x2);
}
else
{
colors[3] = ClampColor;
}
}
else
{
colors[1] = ClampColor;
colors[3] = ClampColor;
}
if((x2 >= 0) &&
(x1 < (INT) BmpData.Width) &&
(y2 >= 0) &&
(y1 < (INT) BmpData.Height))
{
*buffer++ = ::getBilinearFilteredARGB(
colors,
xFrac,
yFrac,
one,
shift,
half2,
shift2
);
}
else
{
*buffer++ = ClampColor;
}
x0 += dx;
y0 += dy;
}
return Ok;
}
/**************************************************************************\
*
* Function Description:
*
* Handles bilinear texture drawing with arbitrary rotation using MMX.
*
* Arguments:
*
* [IN] y - the Y value of the raster being output
* [IN] leftEdge - the DDA class of the left edge
* [IN] rightEdge - the DDA class of the right edge
*
* Return Value:
*
* GpStatus - Ok
*
* Created:
*
* 01/06/2000 andrewgo
*
\**************************************************************************/
GpStatus
DpOutputBilinearSpan_MMX::OutputSpan(
INT y,
INT xMin,
INT xMax // xMax is exclusive
)
{
// Be a little paranoid in checking some state.
ASSERT((((ULONG_PTR) BmpData.Scan0) & 3) == 0);
ASSERT((BmpData.Stride & 3) == 0);
ASSERT(xMax > xMin);
#if defined(_X86_)
INT count = xMax - xMin;
ARGB *buffer = Scan->NextBuffer(xMin, y, count);
// Transform an array of points using the matrix v' = v M:
//
// ( M11 M12 0 )
// (vx', vy', 1) = (vx, vy, 1) ( M21 M22 0 )
// ( dx dy 1 )
//
// All (u, v) calculations are done in 16.16 fixed point.
INT u;
INT v;
// If the values were out of range for fixed point, compute u and v in
// floating-point, then convert:
if (TranslateMatrixValid)
{
u = M11 * xMin + M21 * y + Dx;
v = M12 * xMin + M22 * y + Dy;
}
else
{
GpPointF point(TOREAL(xMin), TOREAL(y));
DeviceToWorld.Transform(&point, 1);
u = GpRound(point.X * (1 << 16));
v = GpRound(point.Y * (1 << 16));
}
INT uIncrement = UIncrement;
INT vIncrement = VIncrement;
INT modulusWidth = ModulusWidth;
INT modulusHeight = ModulusHeight;
VOID *scan0 = BmpData.Scan0;
INT stride = BmpData.Stride;
INT width = BmpData.Width;
INT height = BmpData.Height;
INT xEdgeIncrement = XEdgeIncrement;
INT yEdgeIncrement = YEdgeIncrement;
INT widthMinus1 = width - 1;
INT heightMinus1 = height - 1;
UINT uMax = widthMinus1 << 16;
UINT vMax = heightMinus1 << 16;
BOOL clampMode = (BilinearWrapMode == WrapModeClamp);
ARGB clampColor = ClampColor;
static ULONGLONG Half8dot8 = 0x0080008000800080;
_asm
{
mov eax, u
mov ebx, v
mov ecx, stride
mov edi, buffer
pxor mm0, mm0
movq mm3, Half8dot8
; edx = scratch
; esi = source pixel
PixelLoop:
; Most of the time, our texture coordinate will be from the interior
; of the texture. Things only really get tricky when we have to
; span the texture edges.
;
; Fortunately, the interior case will happen most of the time,
; so we make that as fast as possible. We pop out-of-line to
; handle the tricky cases.
cmp eax, uMax
jae HandleTiling ; Note unsigned compare
cmp ebx, vMax
jae HandleTiling ; Note unsigned compare
mov edx, eax
shr edx, 14
and edx, 0xfffffffc
mov esi, ebx
shr esi, 16
imul esi, ecx
add esi, edx
add esi, scan0 ; esi = upper left pixel
; Stall city. Write first, then reorder with VTune.
movd mm4, [esi]
movd mm5, [esi+4]
movd mm6, [esi+ecx]
movd mm7, [esi+ecx+4]
ContinueLoop:
movd mm1, eax
punpcklwd mm1, mm1
punpckldq mm1, mm1
psrlw mm1, 8 ; mm1 = x fraction in low bytes
movd mm2, ebx
punpcklwd mm2, mm2
punpckldq mm2, mm2
psrlw mm2, 8 ; mm2 = y fraction in low bytes
punpcklbw mm4, mm0
punpcklbw mm5, mm0 ; unpack pixels A & B to low bytes
psubw mm5, mm4
pmullw mm5, mm1
paddw mm5, mm3
psrlw mm5, 8
paddb mm5, mm4 ; mm5 = A' = A + xFrac * (B - A)
punpcklbw mm6, mm0
punpcklbw mm7, mm0 ; unpack pixels C & D to low bytes
psubw mm7, mm6
pmullw mm7, mm1
paddw mm7, mm3
psrlw mm7, 8
paddb mm7, mm6 ; mm7 = B' = C + xFrac * (D - C)
psubw mm7, mm5
pmullw mm7, mm2
paddw mm7, mm3
psrlw mm7, 8
paddb mm7, mm5 ; mm7 = A' + yFrac * (B' - A')
packuswb mm7, mm7
movd [edi], mm7 ; write the final pixel
add eax, uIncrement
add ebx, vIncrement
add edi, 4
dec count
jnz PixelLoop
jmp AllDone
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; Handle tiling cases here
;
; All the tough edge cases are handled here, where we deal with
; texture coordinates that span the texture boundary.
HandleTiling:
cmp clampMode, 0
jnz HandleClamping
; Get 'u' in the range 0 <= u < modulusWidth:
cmp eax, modulusWidth
jb WidthInRange ; note unsigned compare
cdq
idiv modulusWidth
mov eax, edx ; u %= modulusWidth
cmp eax, 0
jge WidthInRange
add eax, modulusWidth ; 0 <= u < modulusWidth
WidthInRange:
; Get 'v' in the range 0 <= v < modulusHeight:
cmp ebx, modulusHeight
jb HeightInRange
push eax
mov eax, ebx
cdq
idiv modulusHeight
mov ebx, edx ; v %= modulusHeight
pop eax
cmp ebx, 0
jge HeightInRange
add ebx, modulusHeight ; 0 <= v < modulusHeight
HeightInRange:
; Now we're going to need to convert our 'u' and 'v' values
; to integers, so save the 16.16 versions:
push eax
push ebx
push ecx
push edi
sar eax, 16
sar ebx, 16 ; note arithmetic shift
; Handle 'flipping'. Note that edi hold flipping flags, where
; the bits have the following meanings:
; 1 = X flip in progress
; 2 = Y flip in progress
; 4 = X flip end boundary not yet reached
; 8 = Y flip end boundary not yet reached.
xor edi, edi
cmp eax, width
jb XFlipHandled
; u is in the range (width <= u < 2*width).
;
; We want to flip it such that (0 <= u' < width), which we do by
; u' = 2*width - u - 1. Don't forget ~u = -u - 1.
or edi, 1 ; mark the flip
not eax
add eax, width
add eax, width
jz XFlipHandled
sub eax, 1
or edi, 4 ; mark flip where adjacent pixels available
XFlipHandled:
cmp ebx, height
jb YFlipHandled
; v is in the range (height <= v < 2*height).
;
; We want to flip it such that (0 <= v' < height), which we do by
; v' = 2*height - v - 1. Don't forget ~v = -v - 1.
or edi, 2 ; mark the flip
not ebx
add ebx, height
add ebx, height
jz YFlipHandled
sub ebx, 1
or edi, 8 ; mark flip where adjacent pixels available
YFlipHandled:
mov esi, ebx
imul esi, ecx ; esi = y * stride
; Set 'edx' to the byte offset to the pixel one to the right, accounting
; for wrapping past the edge of the bitmap. Only set the byte offset to
; point to right pixel for non edge cases.
mov edx, 4
test edi, 4
jnz RightIncrementCalculated
test edi, 1
jnz SetXEdgeInc
cmp eax, widthMinus1
jb RightIncrementCalculated
SetXEdgeInc:
mov edx, xEdgeIncrement
; When we flipX and the current pixel is the last pixel in the texture
; line, wrapping past the end of the bitmap wraps back in the same side
; of the bitmap. I.e. for this one specific pixel we can set the pixel
; on-the-right to be the same as this pixel (increment of zero).
; Only valid because this is the edge condition.
; Note that this will occur for two successive pixels as the texture
; wrap occurs - first at width-1 and then at width-1 after wrapping.
;
; A | B
; --+--
; C | D
;
; At this point, pixel A has been computed correctly accounting for the
; flip/tile and wrapping beyond the edge of the texture. We work out
; the offset of B from A, but we again need to take into account the
; possible flipX mode if pixel A happens to be the last pixel in the
; texture scanline (the code immediately above takes into account
; tiling across the texture boundary, but not the flip)
RightIncrementCalculated:
; Set 'ecx' to the byte offset to the pixel one down, accounting for
; wrapping past the edge of the bitmap. Only set the byte offset to
; point to one pixel down for non edge cases.
test edi, 8
jnz DownIncrementCalculated
test edi, 2
jnz SetYEdgeInc
cmp ebx, heightMinus1
jb DownIncrementCalculated
SetYEdgeInc:
mov ecx, yEdgeIncrement
; When we flipY and the current pixel is in the last scanline in the
; texture, wrapping past the end of the bitmap wraps back in the same
; side of the bitmap. I.e. for this one specific scanline we can set
; the pixel offset one down to be the same as this pixel
; (increment of zero).
; Only valid because this is the edge condition.
; (see comment above RightIncrementCalculated:)
DownIncrementCalculated:
; Finish calculating the upper-left pixel address:
add esi, scan0
shl eax, 2
add esi, eax ; esi = upper left pixel
; Load the 4 pixels:
movd mm4, [esi]
movd mm5, [esi+edx]
add esi, ecx
movd mm6, [esi]
movd mm7, [esi+edx]
; Finish handling the flip:
test edi, 1
jz XSwapDone
movq mm1, mm5
movq mm5, mm4
movq mm4, mm1 ; swap pixels A and B
movq mm1, mm6
movq mm6, mm7
movq mm7, mm1 ; swap pixels C and D
XSwapDone:
test edi, 2
jz YSwapDone
movq mm1, mm4
movq mm4, mm6
movq mm6, mm1 ; swap pixels A and C
movq mm1, mm5
movq mm5, mm7
movq mm7, mm1 ; swap pixels B and D
YSwapDone:
; Restore everything and get out:
pop edi
pop ecx
pop ebx
pop eax
jmp ContinueLoop
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; Clamp mode.
;
; Set the pixel values to 0 for any not on the texture.
HandleClamping:
push eax
push ebx
movd mm4, clampColor
movq mm5, mm4
movq mm6, mm4 ; initialize invisible pixels to
movq mm7, mm4 ; the clampColor.
sar eax, 16
sar ebx, 16 ; note these are arithmetic shifts
; We need to look at a 2x2 square of pixels in the texture, where
; (eax, ebx) represents the (x, y) texture coordinates. First we
; check for the case where none of the four pixel locations are
; actually anywhere on the texture.
cmp eax, -1
jl FinishClamp
cmp eax, width
jge FinishClamp ; early out if (x < -1) or (x >= width)
cmp ebx, -1
jl FinishClamp
cmp ebx, height
jge FinishClamp ; handle trivial rejection
; Okay, now we know that we have to pull at least one pixel from
; the texture. Find the address of the upper-left pixel:
mov edx, eax
shl edx, 2
mov esi, ebx
imul esi, ecx
add esi, edx
add esi, scan0 ; esi = upper left pixel
; Our pixel nomenclature for the 2x2 square is as follows:
;
; A | B
; ---+---
; C | D
cmp ebx, 0 ; if (y < 0), we can't look at
jl Handle_CD ; row y
cmp eax, 0 ; if (x < 0), we can't look at
jl Done_A ; column x
movd mm4, [esi] ; read pixel (x, y)
Done_A:
cmp eax, widthMinus1 ; if (x >= width - 1), we can't
jge Handle_CD ; look at column x
movd mm5, [esi+4] ; read pixel (x+1, y)
Handle_CD:
cmp ebx, heightMinus1 ; if (y >= height - 1), we can't
jge FinishClamp ; look at row y
cmp eax, 0 ; if (x < 0), we can't look at
jl Done_C ; column x
movd mm6, [esi+ecx] ; read pixel (x, y+1)
Done_C:
cmp eax, widthMinus1 ; if (x >= width - 1), we can't
jge FinishClamp ; look at column x
movd mm7, [esi+ecx+4] ; read pixel (x+1, y+1)
FinishClamp:
pop ebx
pop eax
jmp ContinueLoop
AllDone:
emms
}
#endif
return Ok;
}
/**************************************************************************\
*
* Function Description:
*
* Output routine for handling texture brushes with indentity transforms
* and either 'Tile' or 'Clamp' wrap modes.
*
* Created:
*
* 03/14/2000 andrewgo
*
\**************************************************************************/
GpStatus
DpOutputBilinearSpan_Identity::OutputSpan(
INT y,
INT xMin,
INT xMax // xMax is exclusive
)
{
ASSERT(xMax > xMin);
INT count = xMax - xMin;
ARGB *buffer = Scan->NextBuffer(xMin, y, count);
INT u = xMin + Dx;
INT v = y + Dy;
INT width = BmpData.Width;
INT height = BmpData.Height;
INT i;
if (BilinearWrapMode == WrapModeTile)
{
if (PowerOfTwo)
{
u &= (width - 1);
v &= (height - 1);
}
else
{
// Single unsigned compare handles (u < 0) and (u >= width)
if (static_cast<unsigned>(u) >= static_cast<unsigned>(width))
{
u = RemainderI(u, width);
}
// Single unsigned compare handles (v < 0) and (v >= width)
if (static_cast<unsigned>(v) >= static_cast<unsigned>(height))
{
v = RemainderI(v, height);
}
}
ARGB *row = reinterpret_cast<ARGB*>
(static_cast<BYTE*>(BmpData.Scan0) + (v * BmpData.Stride));
ARGB *src;
src = row + u;
i = min(width - u, count);
count -= i;
// We don't call GpMemcpy here because by doing the copy explicitly,
// the compiler still converts to a 'rep movsd', but it doesn't have
// to add to the destination 'buffer' pointer when done:
do {
*buffer++ = *src++;
} while (--i != 0);
while (count > 0)
{
src = row;
i = min(width, count);
count -= i;
do {
*buffer++ = *src++;
} while (--i != 0);
}
}
else
{
ASSERT(BilinearWrapMode == WrapModeClamp);
ARGB borderColor = ClampColor;
// Check for trivial rejection. Unsigned compare handles
// (v < 0) and (v >= height).
if ((static_cast<unsigned>(v) >= static_cast<unsigned>(height)) ||
(u >= width) ||
(u + count <= 0))
{
// The whole scan should be the border color:
i = count;
do {
*buffer++ = borderColor;
} while (--i != 0);
}
else
{
ARGB *src = reinterpret_cast<ARGB*>
(static_cast<BYTE*>(BmpData.Scan0) + (v * BmpData.Stride));
if (u < 0)
{
i = -u;
count -= i;
do {
*buffer++ = borderColor;
} while (--i != 0);
}
else
{
src += u;
width -= u;
}
i = min(count, width);
ASSERT(i > 0); // Trivial rejection ensures this
count -= i;
/*
The compiler was generating particularly stupid code
for this loop.
do {
*buffer++ = *src++;
} while (--i != 0);
*/
GpMemcpy(buffer, src, i*sizeof(ARGB));
buffer += i;
while (count-- > 0)
{
*buffer++ = borderColor;
}
}
}
return(Ok);
}
/**************************************************************************\
*
* Function Description:
*
* Hatch brush constructor.
*
* Arguments:
*
* Created:
*
* 04/15/1999 ikkof
*
\**************************************************************************/
DpOutputHatchSpan::DpOutputHatchSpan(
const GpHatch *hatchBrush,
DpScanBuffer * scan,
DpContext* context
)
{
Scan = scan;
ForeARGB = hatchBrush->DeviceBrush.Colors[0].GetPremultipliedValue();
BackARGB = hatchBrush->DeviceBrush.Colors[1].GetPremultipliedValue();
// Store the context rendering origin for the brush origin.
m_BrushOriginX = context->RenderingOriginX;
m_BrushOriginY = context->RenderingOriginY;
INT a[3], r[3], g[3], b[3];
a[0] = (BYTE) GpColor::GetAlphaARGB(ForeARGB);
r[0] = (BYTE) GpColor::GetRedARGB(ForeARGB);
g[0] = (BYTE) GpColor::GetGreenARGB(ForeARGB);
b[0] = (BYTE) GpColor::GetBlueARGB(ForeARGB);
a[1] = (BYTE) GpColor::GetAlphaARGB(BackARGB);
r[1] = (BYTE) GpColor::GetRedARGB(BackARGB);
g[1] = (BYTE) GpColor::GetGreenARGB(BackARGB);
b[1] = (BYTE) GpColor::GetBlueARGB(BackARGB);
a[2] = (a[0] + 3*a[1]) >> 2;
r[2] = (r[0] + 3*r[1]) >> 2;
g[2] = (g[0] + 3*g[1]) >> 2;
b[2] = (b[0] + 3*b[1]) >> 2;
AverageARGB = GpColor::MakeARGB((BYTE) a[2], (BYTE) r[2],
(BYTE) g[2], (BYTE) b[2]);
// Antialiase diagonal hatches.
if(hatchBrush->DeviceBrush.Style == HatchStyleForwardDiagonal ||
hatchBrush->DeviceBrush.Style == HatchStyleBackwardDiagonal ||
hatchBrush->DeviceBrush.Style == HatchStyleDiagonalCross)
{
REAL temp;
// occupied = (2*sqrt(2) - 1)/2
REAL occupied = TOREAL(0.914213562);
if(a[0] != 255 || a[1] != 255)
{
temp = TOREAL(occupied*(a[0] - a[1]) + a[1]);
a[0] = (BYTE) temp;
}
temp = TOREAL(occupied*(r[0] - r[1]) + r[1]);
if(temp > 255)
r[0] = 255;
else
r[0] = (BYTE) temp;
temp = TOREAL(occupied*(g[0] - g[1]) + g[1]);
if(temp > 255)
g[0] = 255;
else
g[0] = (BYTE) temp;
temp = TOREAL(occupied*(b[0] - b[1]) + b[1]);
if(temp > 255)
b[0] = 255;
else
b[0] = (BYTE) temp;
ForeARGB = GpColor::MakeARGB((BYTE) a[0], (BYTE) r[0],
(BYTE) g[0], (BYTE) b[0]);
}
for(INT i = 0; i < 8; i++)
{
for(INT j= 0; j < 8; j++)
{
Data[i][j] = hatchBrush->DeviceBrush.Data[i][j];
}
}
}
/**************************************************************************\
*
* Function Description:
*
* Outputs a single span within a raster with a hatch brush
* Is called by the rasterizer.
*
* Arguments:
*
* [IN] y - the Y value of the raster being output
* [IN] leftEdge - the DDA class of the left edge
* [IN] rightEdge - the DDA class of the right edge
*
* Return Value:
*
* GpStatus - Ok
*
* Created:
*
* 04/15/1999 ikkof
*
\**************************************************************************/
GpStatus
DpOutputHatchSpan::OutputSpan(
INT y,
INT xMin,
INT xMax // xMax is exclusive
)
{
ARGB argb;
INT width = xMax - xMin;
ARGB* buffer = Scan->NextBuffer(xMin, y, width);
INT yMod = (y - m_BrushOriginY) & 0x7;
for(INT x = xMin; x < xMax; x++)
{
INT xMod = (x - m_BrushOriginX) & 0x7;
INT value = Data[yMod][xMod];
if(value == 255)
*buffer++ = ForeARGB;
else if(value == 0)
*buffer++ = BackARGB;
else
*buffer++ = AverageARGB; // for antialising.
}
return Ok;
}
/**************************************************************************\
*
* Function Description:
*
* Outputs a single span within a raster with a hatch brush
* Is called by the rasterizer. This is a special version which stretches
* up the size of the hatch span to device resolution.
*
* Arguments:
*
* [IN] y - the Y value of the raster being output
* [IN] leftEdge - the DDA class of the left edge
* [IN] rightEdge - the DDA class of the right edge
*
* Return Value:
*
* GpStatus - Ok
*
* Created:
*
* 2/20/1 - ericvan
*
\**************************************************************************/
GpStatus
DpOutputStretchedHatchSpan::OutputSpan(
INT y,
INT xMin,
INT xMax // xMax is exclusive
)
{
ARGB argb;
INT width = xMax - xMin;
ARGB* buffer = Scan->NextBuffer(xMin, y, width);
INT yMod = (y - m_BrushOriginY) % (8*ScaleFactor);
for(INT x = xMin; x < xMax; x++)
{
INT xMod = (x - m_BrushOriginX) % (8*ScaleFactor);
INT value = Data[yMod/ScaleFactor][xMod/ScaleFactor];
if(value == 255)
*buffer++ = ForeARGB;
else if(value == 0)
*buffer++ = BackARGB;
else
*buffer++ = AverageARGB; // for antialising.
}
return Ok;
}