You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
954 lines
27 KiB
954 lines
27 KiB
/**************************************************************************\
|
|
*
|
|
* Copyright (c) 1998 Microsoft Corporation
|
|
*
|
|
* Module Name:
|
|
*
|
|
* drawimage.cpp
|
|
*
|
|
* Abstract:
|
|
*
|
|
* Software Rasterizer DrawImage routine and supporting functionality.
|
|
*
|
|
* Revision History:
|
|
*
|
|
* 10/20/1999 asecchia
|
|
* Created it.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
#include "precomp.hpp"
|
|
|
|
// Include the template class definitions for the stretch
|
|
// filter modes.
|
|
|
|
#include "stretch.inc"
|
|
|
|
namespace DpDriverActiveEdge {
|
|
|
|
// We make an array of these for use in the dda computation.
|
|
|
|
struct PointFIX4
|
|
{
|
|
FIX4 X;
|
|
FIX4 Y;
|
|
};
|
|
|
|
// Vertex iterator.
|
|
// Has two Proxy methods for accessing the dda
|
|
|
|
class DdaIterator
|
|
{
|
|
private:
|
|
GpYDda dda;
|
|
PointFIX4 *vertices;
|
|
INT numVertices;
|
|
INT direction;
|
|
INT idx; // keep this so we don't infinite loop on
|
|
// degenerate case
|
|
INT idx1, idx2;
|
|
BOOL valid;
|
|
|
|
public:
|
|
|
|
// GpYDda Proxy-like semantics
|
|
|
|
INT GetX()
|
|
{
|
|
return dda.GetX();
|
|
}
|
|
|
|
// Initialize the dda and traversal direction
|
|
|
|
DdaIterator(PointFIX4 *v, INT n, INT d, INT idx)
|
|
{
|
|
vertices=v;
|
|
numVertices=n;
|
|
ASSERT( (d==-1)||(d==1) );
|
|
direction = d;
|
|
ASSERT( (idx>=0)&&(idx<n) );
|
|
this->idx=idx;
|
|
idx1=idx;
|
|
idx2=idx;
|
|
valid = AdvanceEdge();
|
|
}
|
|
|
|
BOOL IsValid() { return valid; }
|
|
|
|
// Advance to the next edge and initialize the dda.
|
|
// Return FALSE if we're done.
|
|
|
|
BOOL Next(INT y)
|
|
{
|
|
if(dda.DoneWithVector(y))
|
|
{
|
|
return AdvanceEdge();
|
|
}
|
|
|
|
// TRUE indicates more to do.
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
private:
|
|
|
|
// Advance the internal state to the next edge.
|
|
// Ignore horizontal edges.
|
|
// Return FALSE if we're done.
|
|
|
|
BOOL AdvanceEdge()
|
|
{
|
|
do {
|
|
idx2 = idx1;
|
|
if(direction==1)
|
|
{
|
|
idx1++;
|
|
if(idx1>=numVertices) { idx1 = 0; }
|
|
}
|
|
else
|
|
{
|
|
idx1--;
|
|
if(idx1<0) { idx1 = numVertices-1; }
|
|
}
|
|
|
|
// Loop till we get a non-horizontal edge.
|
|
// Make sure we don't have an infinite loop on all horizontal edges.
|
|
// The Ceiling is used to make almost horizontal lines appear to be
|
|
// horizontal - this allows the algorithm to correctly compute the
|
|
// end terminating case.
|
|
|
|
} while(( GpFix4Ceiling(vertices[idx1].Y) ==
|
|
GpFix4Ceiling(vertices[idx2].Y) ) &&
|
|
(idx1!=idx));
|
|
|
|
if(GpFix4Ceiling(vertices[idx1].Y) >
|
|
GpFix4Ceiling(vertices[idx2].Y) )
|
|
{
|
|
// Initialize the dda
|
|
|
|
dda.Init(
|
|
vertices[idx2].X,
|
|
vertices[idx2].Y,
|
|
vertices[idx1].X,
|
|
vertices[idx1].Y
|
|
);
|
|
return TRUE;
|
|
}
|
|
|
|
// terminate if we've wrapped around and started to come back up.
|
|
// I.e return FALSE if we should stop.
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
};
|
|
|
|
} // End namespace DpDriverActiveEdge
|
|
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* This handles axis aligned drawing. The cases include identity,
|
|
* integer translation, general translation and scaling.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* output - span class to output the scanlines to.
|
|
* dstTL - top left destination point.
|
|
* dstBR - bottom right destination point.
|
|
*
|
|
* History:
|
|
* 10/19/1999 asecchia created it.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
VOID StretchBitsMainLoop(
|
|
DpOutputSpan *output,
|
|
GpPoint *dstTL,
|
|
GpPoint *dstBR
|
|
)
|
|
{
|
|
// Input coordinates must be correctly ordered. This assumtion is required
|
|
// by the output span routines which must have the spans come in strictly
|
|
// increasing y order.
|
|
|
|
ASSERT(dstTL->X < dstBR->X);
|
|
ASSERT(dstTL->Y < dstBR->Y);
|
|
|
|
// Main loop - output each scanline.
|
|
|
|
const INT left = dstTL->X;
|
|
const INT right = dstBR->X;
|
|
|
|
for(INT y=dstTL->Y; y<(dstBR->Y); y++)
|
|
{
|
|
output->OutputSpan(y, left, right);
|
|
}
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* CreateBilinearOutputSpan
|
|
* Creates a bilinear or identity outputspan based on our hierarchy
|
|
* of span classes.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* bitmap - driver surface
|
|
* scan - scan class
|
|
* xForm - source rect to destination parallelogram transform
|
|
* imageAttributes - encapsulates the wrap mode settings.
|
|
*
|
|
* Return Value:
|
|
*
|
|
* DpOutputSpan - returns the created output span (NULL for failure)
|
|
*
|
|
* History:
|
|
*
|
|
* 09/03/2000 asecchia
|
|
* borrowed this from the brush code.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
DpOutputSpan*
|
|
CreateBilinearOutputSpan(
|
|
IN DpBitmap *bitmap,
|
|
IN DpScanBuffer *scan,
|
|
IN GpMatrix *xForm, // source rectangle to destination coordinates in
|
|
// device space.
|
|
IN DpContext *context,
|
|
IN DpImageAttributes *imageAttributes,
|
|
IN bool fLargeImage // need to handle really large stretches.
|
|
// usually used for stretch algorithms that punted
|
|
// due to overflow in internal computation.
|
|
)
|
|
{
|
|
// Validate input parameters.
|
|
|
|
ASSERT(bitmap);
|
|
ASSERT(scan);
|
|
ASSERT(xForm);
|
|
ASSERT(context);
|
|
ASSERT(imageAttributes);
|
|
|
|
DpOutputBilinearSpan *textureSpan;
|
|
GpMatrix brushTransform;
|
|
GpMatrix worldToDevice;
|
|
|
|
// Go through our heirarchy of scan drawers:
|
|
|
|
if ((!fLargeImage) &&
|
|
xForm->IsIntegerTranslate() &&
|
|
((imageAttributes->wrapMode == WrapModeTile) ||
|
|
(imageAttributes->wrapMode == WrapModeClamp)))
|
|
{
|
|
textureSpan = new DpOutputBilinearSpan_Identity(
|
|
bitmap,
|
|
scan,
|
|
xForm,
|
|
context,
|
|
imageAttributes
|
|
);
|
|
}
|
|
else if ((!fLargeImage) &&
|
|
OSInfo::HasMMX &&
|
|
GpValidFixed16(bitmap->Width) &&
|
|
GpValidFixed16(bitmap->Height))
|
|
{
|
|
textureSpan = new DpOutputBilinearSpan_MMX(
|
|
bitmap,
|
|
scan,
|
|
xForm,
|
|
context,
|
|
imageAttributes
|
|
);
|
|
}
|
|
else
|
|
{
|
|
textureSpan = new DpOutputBilinearSpan(
|
|
bitmap,
|
|
scan,
|
|
xForm,
|
|
context,
|
|
imageAttributes
|
|
);
|
|
}
|
|
|
|
if ((textureSpan) && !textureSpan->IsValid())
|
|
{
|
|
delete textureSpan;
|
|
textureSpan = NULL;
|
|
}
|
|
|
|
return textureSpan;
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* CreateOutputSpan
|
|
* Creates an outputspan based on our hierarchy of span classes.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* bitmap - driver surface
|
|
* scan - scan class
|
|
* xForm - source rect to destination parallelogram transform
|
|
* imageAttributes - encapsulates the wrap mode settings.
|
|
* filterMode - which InterpolationMode setting to use
|
|
*
|
|
* Notes:
|
|
*
|
|
* The long term plan is to make this and the similar routines in the
|
|
* texture brush code converge. We'd like one routine doing this for
|
|
* all the texture output spans and have both the texture brush and the
|
|
* drawimage reuse the same code and support all the same filter/wrap
|
|
* modes.
|
|
*
|
|
* Return Value:
|
|
*
|
|
* DpOutputSpan - returns the created output span (NULL for failure)
|
|
*
|
|
* History:
|
|
*
|
|
* 09/03/2000 asecchia created it
|
|
*
|
|
\**************************************************************************/
|
|
|
|
DpOutputSpan *CreateOutputSpan(
|
|
IN DpBitmap *bitmap,
|
|
IN DpScanBuffer *scan,
|
|
IN GpMatrix *xForm, // source rectangle to destination coordinates in
|
|
// device space.
|
|
IN DpImageAttributes *imageAttributes,
|
|
IN InterpolationMode filterMode,
|
|
|
|
// !!! [asecchia] shouldn't need any of this following stuff - the above
|
|
// bitmap and xForm should be sufficient.
|
|
// The possible exception is the srcRect which may be required if we
|
|
// ever implement the clamp-to-srcRect feature.
|
|
|
|
IN DpContext *context,
|
|
IN const GpRectF *srcRect,
|
|
IN const GpRectF *dstRect,
|
|
IN const GpPointF *dstPoints,
|
|
IN const INT numPoints
|
|
)
|
|
{
|
|
// Validate input parameters.
|
|
|
|
ASSERT(bitmap);
|
|
ASSERT(scan);
|
|
ASSERT(xForm);
|
|
ASSERT(imageAttributes);
|
|
|
|
// Validate the stuff we had to pass through for the
|
|
// OutputSpan routines that can't handle the xForm.
|
|
|
|
ASSERT(context);
|
|
ASSERT(srcRect);
|
|
ASSERT(dstRect);
|
|
ASSERT(dstPoints);
|
|
ASSERT(numPoints == 3);
|
|
|
|
bool fPunted = false;
|
|
|
|
// Initialize up front so that all the error-out paths are covered.
|
|
|
|
DpOutputSpan *output = NULL;
|
|
|
|
// Copy to local so that we can modify it without breaking the
|
|
// input parameter consistency.
|
|
|
|
InterpolationMode theFilterMode = filterMode;
|
|
|
|
// The so-called 'identity' transform which counter-intuitively includes
|
|
// integer only translation.
|
|
|
|
if(xForm->IsIntegerTranslate())
|
|
{
|
|
// Use a much simplified output span class for
|
|
// special case CopyBits.
|
|
// The big win is due to the fact that integer
|
|
// translation only cases do not require filtering.
|
|
|
|
// Note, we set InterpolationModeBilinear because we
|
|
// will detect the identity in the bilinear span creation.
|
|
|
|
theFilterMode = InterpolationModeBilinear;
|
|
}
|
|
|
|
switch(theFilterMode)
|
|
{
|
|
|
|
// Nearest neighbor filtering. Used mainly for printing scenarios.
|
|
// Aliases badly - only really looks good on high-dpi output devices,
|
|
// however it's the fastest reconstruction filter.
|
|
|
|
case InterpolationModeNearestNeighbor:
|
|
output = new DpOutputNearestNeighborSpan(
|
|
bitmap,
|
|
scan,
|
|
context,
|
|
*imageAttributes,
|
|
numPoints,
|
|
dstPoints,
|
|
srcRect
|
|
);
|
|
break;
|
|
|
|
// High quality bicubic filter convolution.
|
|
|
|
case InterpolationModeHighQuality:
|
|
case InterpolationModeHighQualityBicubic:
|
|
|
|
// !!! [asecchia] the high quality bicubic filter code doesn't
|
|
// know how to do rotation yet.
|
|
|
|
if(xForm->IsTranslateScale())
|
|
{
|
|
|
|
output = new DpOutputSpanStretch<HighQualityBicubic>(
|
|
bitmap,
|
|
scan,
|
|
context,
|
|
*imageAttributes,
|
|
dstRect,
|
|
srcRect
|
|
);
|
|
|
|
if(output && !output->IsValid())
|
|
{
|
|
// Failed to create the output span, try fall through to the
|
|
// regular bilinear output code.
|
|
|
|
delete output;
|
|
output = NULL;
|
|
fPunted = true;
|
|
goto FallbackCreation;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
// else fall through to the regular bicubic code.
|
|
|
|
// Bicubic filter kernel.
|
|
|
|
case InterpolationModeBicubic:
|
|
output = new DpOutputBicubicImageSpan(
|
|
bitmap,
|
|
scan,
|
|
context,
|
|
*imageAttributes,
|
|
numPoints,
|
|
dstPoints,
|
|
srcRect
|
|
);
|
|
break;
|
|
|
|
// High quality bilinear (tent) convolution filter
|
|
|
|
case InterpolationModeHighQualityBilinear:
|
|
|
|
// !!! [asecchia] the high quality bilinear filter code doesn't
|
|
// know how to do rotation yet.
|
|
|
|
if(xForm->IsTranslateScale())
|
|
{
|
|
output = new DpOutputSpanStretch<HighQualityBilinear>(
|
|
bitmap,
|
|
scan,
|
|
context,
|
|
*imageAttributes,
|
|
dstRect,
|
|
srcRect
|
|
);
|
|
|
|
if(output && !output->IsValid())
|
|
{
|
|
// Failed to create the output span, try fall through to the
|
|
// regular bilinear output code.
|
|
|
|
delete output;
|
|
output = NULL;
|
|
fPunted = true;
|
|
goto FallbackCreation;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
// else fall through to the regular bilinear code.
|
|
|
|
// Bilinear filter kernel - default case.
|
|
|
|
case InterpolationModeDefault:
|
|
case InterpolationModeLowQuality:
|
|
case InterpolationModeBilinear:
|
|
default:
|
|
|
|
FallbackCreation:
|
|
|
|
// Create a bilinear span or an identity span.
|
|
|
|
output = CreateBilinearOutputSpan(
|
|
bitmap,
|
|
scan,
|
|
xForm,
|
|
context,
|
|
imageAttributes,
|
|
fPunted // somebody failed and this is the fallback.
|
|
);
|
|
}
|
|
|
|
// Check to see that the constructor for the output span class succeeded.
|
|
|
|
if(output && !output->IsValid())
|
|
{
|
|
delete output;
|
|
output = NULL;
|
|
}
|
|
|
|
// This will be NULL on an error path.
|
|
|
|
return output;
|
|
}
|
|
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Draws an image.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* [IN] context - the context (matrix and clipping)
|
|
* [IN] srcSurface - the source surface
|
|
* [IN] dstSurface - the image to fill
|
|
* [IN] drawBounds - the surface bounds
|
|
* [IN] mapMode - the mapping mode of the image
|
|
* [IN] numPoints - the number of points in dstPoints array (<= 4)
|
|
* [IN] dstPoints - the array of points for affine or quad transform.
|
|
* [IN] srcRect - the bounds of the src image. If this is NULL,
|
|
* the whole image is used.
|
|
*
|
|
* Return Value:
|
|
*
|
|
* GpStatus - Ok or failure status
|
|
*
|
|
* History:
|
|
*
|
|
* 01/09/1999 ikkof Created it.
|
|
* 10/19/1999 asecchia rewrite to support rotation.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
GpStatus
|
|
DpDriver::DrawImage(
|
|
DpContext * context,
|
|
DpBitmap * srcSurface,
|
|
DpBitmap * dstSurface,
|
|
const GpRect * drawBounds,
|
|
const DpImageAttributes * imgAttributes,
|
|
INT numPoints,
|
|
const GpPointF * dstPoints,
|
|
const GpRectF * srcRect,
|
|
DriverDrawImageFlags flags
|
|
)
|
|
{
|
|
// Get the infrastructure to do active edge table stuff.
|
|
|
|
using namespace DpDriverActiveEdge;
|
|
|
|
// !!! [asecchia] Why do we have this if we don't use it?
|
|
|
|
GpStatus status = Ok;
|
|
|
|
// The caller is responsible for padding out the dstPoints structure so
|
|
// that it has at least 3 valid points.
|
|
|
|
ASSERT((numPoints==3)||(numPoints==4));
|
|
|
|
// We need to do some reordering of points for warping (numPoints==4)
|
|
// to work. For now we require numPoints == 3.
|
|
|
|
ASSERT(numPoints==3);
|
|
|
|
// Make a local copy so we don't end up modifying our callers' data.
|
|
|
|
GpPointF fDst[4];
|
|
GpMemcpy(fDst, dstPoints, sizeof(GpPointF)*numPoints);
|
|
|
|
// Need to infer the transform for banding code.
|
|
|
|
// !!! PERF: [asecchia] This transform actually gets computed by the Engine
|
|
// before calling the Driver. We should have a way of passing it down
|
|
// so that we don't have to recompute it.
|
|
|
|
GpMatrix xForm;
|
|
xForm.InferAffineMatrix(fDst, *srcRect);
|
|
xForm.Append(context->WorldToDevice);
|
|
|
|
// This is the source rectangle band.
|
|
|
|
GpPointF fDst2[4];
|
|
|
|
// If we are in HalfPixelMode Offset, we want to be able to read half
|
|
// a pixel to the left of the image, to be able to center the drawing
|
|
|
|
fDst2[0].X = srcRect->X;
|
|
fDst2[0].Y = srcRect->Y;
|
|
fDst2[1].X = srcRect->X+srcRect->Width;
|
|
fDst2[1].Y = srcRect->Y;
|
|
fDst2[2].X = srcRect->X;
|
|
fDst2[2].Y = srcRect->Y+srcRect->Height;
|
|
|
|
// Transform the points to the destination.
|
|
|
|
xForm.Transform(fDst2, 3);
|
|
|
|
if(numPoints==3)
|
|
{
|
|
// Force the four point destination format
|
|
|
|
fDst[0].X = fDst2[0].X;
|
|
fDst[0].Y = fDst2[0].Y;
|
|
fDst[1].X = fDst2[2].X;
|
|
fDst[1].Y = fDst2[2].Y;
|
|
fDst[2].X = fDst2[1].X+fDst2[2].X-fDst2[0].X;
|
|
fDst[2].Y = fDst2[1].Y+fDst2[2].Y-fDst2[0].Y;
|
|
fDst[3].X = fDst2[1].X;
|
|
fDst[3].Y = fDst2[1].Y;
|
|
|
|
} else if (numPoints==4) {
|
|
|
|
// !!! [asecchia] This code branch doesn't work yet.
|
|
// The transforms required for correct banding need to be worked out
|
|
// for the warp transform case.
|
|
// This is a V2 feature.
|
|
|
|
ASSERT(FALSE);
|
|
}
|
|
|
|
// Convert the transformed rectangle to fix point notation.
|
|
|
|
PointFIX4 fix4Dst[4];
|
|
fix4Dst[0].X = GpRealToFix4(fDst[0].X);
|
|
fix4Dst[0].Y = GpRealToFix4(fDst[0].Y);
|
|
fix4Dst[1].X = GpRealToFix4(fDst[1].X);
|
|
fix4Dst[1].Y = GpRealToFix4(fDst[1].Y);
|
|
fix4Dst[2].X = GpRealToFix4(fDst[2].X);
|
|
fix4Dst[2].Y = GpRealToFix4(fDst[2].Y);
|
|
fix4Dst[3].X = GpRealToFix4(fDst[3].X);
|
|
fix4Dst[3].Y = GpRealToFix4(fDst[3].Y);
|
|
|
|
// !!! [agodfrey] Perf: May want to add the noTransparentPixels parameter.
|
|
// I guess we'd have to check that the coordinates are integer (after
|
|
// translation and scaling), that there's no rotation, and that
|
|
// the image contains no transparent pixels.
|
|
|
|
DpScanBuffer scan(
|
|
dstSurface->Scan,
|
|
this,
|
|
context,
|
|
dstSurface
|
|
);
|
|
|
|
if(!scan.IsValid())
|
|
{
|
|
return(GenericError);
|
|
}
|
|
|
|
// Only valid if xForm->IsTranslateScale()
|
|
|
|
GpRectF dstRect(
|
|
fDst[0].X,
|
|
fDst[0].Y,
|
|
fDst[2].X-fDst[0].X,
|
|
fDst[2].Y-fDst[0].Y
|
|
);
|
|
|
|
DpOutputSpan* output = CreateOutputSpan(
|
|
srcSurface,
|
|
&scan,
|
|
&xForm,
|
|
const_cast<DpImageAttributes*>(imgAttributes),
|
|
context->FilterType,
|
|
context,
|
|
srcRect,
|
|
&dstRect,
|
|
dstPoints,
|
|
numPoints
|
|
);
|
|
|
|
// if output is NULL, we failed to allocate the memory for the
|
|
// output span class.
|
|
|
|
if(output == NULL)
|
|
{
|
|
return(OutOfMemory);
|
|
}
|
|
|
|
// Set up the clipping.
|
|
|
|
DpRegion::Visibility visibility = DpRegion::TotallyVisible;
|
|
DpClipRegion *clipRegion = NULL;
|
|
|
|
if (context->VisibleClip.GetRectVisibility(
|
|
drawBounds->X,
|
|
drawBounds->Y,
|
|
drawBounds->GetRight(),
|
|
drawBounds->GetBottom()
|
|
) != DpRegion::TotallyVisible
|
|
)
|
|
{
|
|
clipRegion = &(context->VisibleClip);
|
|
clipRegion->InitClipping(output, drawBounds->Y);
|
|
}
|
|
|
|
GpRect clippedRect;
|
|
|
|
if(clipRegion)
|
|
{
|
|
visibility = clipRegion->GetRectVisibility(
|
|
drawBounds->X,
|
|
drawBounds->Y,
|
|
drawBounds->GetRight(),
|
|
drawBounds->GetBottom(),
|
|
&clippedRect
|
|
);
|
|
}
|
|
|
|
// Decide on our clipping strategy.
|
|
|
|
DpOutputSpan *outspan;
|
|
switch (visibility)
|
|
{
|
|
case DpRegion::TotallyVisible: // no clipping is needed
|
|
outspan = output;
|
|
break;
|
|
|
|
case DpRegion::ClippedVisible: //
|
|
case DpRegion::PartiallyVisible: // some clipping is needed
|
|
outspan = clipRegion;
|
|
break;
|
|
|
|
case DpRegion::Invisible: // nothing on screen - quit
|
|
goto DrawImage_Done;
|
|
}
|
|
|
|
if(xForm.IsTranslateScale() || // stretch
|
|
xForm.IsIntegerTranslate()) // copybits
|
|
{
|
|
// Do the stretch/translate case
|
|
|
|
GpPoint dstTL, dstBR;
|
|
|
|
// Round to fixed point to eliminate the very close to integer
|
|
// numbers that can result from transformation.
|
|
// E.g. 300.0000000001 should become 300 after the ceiling operation
|
|
// and not 301 (not the classical definition of ceiling).
|
|
|
|
// Top Left corner.
|
|
|
|
dstTL.X = GpFix4Ceiling(fix4Dst[0].X);
|
|
dstTL.Y = GpFix4Ceiling(fix4Dst[0].Y);
|
|
|
|
// Bottom Right corner
|
|
|
|
dstBR.X = GpFix4Ceiling(fix4Dst[2].X);
|
|
dstBR.Y = GpFix4Ceiling(fix4Dst[2].Y);
|
|
|
|
// Swap coordinates if necessary. StretchBitsMainLoop
|
|
// assumes that TL corner is less than BR corner.
|
|
|
|
if (dstTL.X > dstBR.X)
|
|
{
|
|
INT xTmp = dstTL.X;
|
|
dstTL.X = dstBR.X;
|
|
dstBR.X = xTmp;
|
|
}
|
|
if (dstTL.Y > dstBR.Y)
|
|
{
|
|
INT yTmp = dstTL.Y;
|
|
dstTL.Y = dstBR.Y;
|
|
dstBR.Y = yTmp;
|
|
}
|
|
|
|
// Due to the fixed point calculations used for image stretching,
|
|
// we are limited to how large an image can be stretched.
|
|
// If it is out of bounds, return an error.
|
|
if (srcRect->Width > 32767.0f || srcRect->Height > 32767.0f)
|
|
{
|
|
WARNING(("Image width or height > 32767"));
|
|
status = InvalidParameter;
|
|
goto DrawImage_Done;
|
|
}
|
|
|
|
// This handles both the stretch and the copy case
|
|
|
|
// Don't draw anything if there are no scanlines to draw or if
|
|
// there are no pixels in the scanlines.
|
|
|
|
if( (dstBR.X != dstTL.X) &&
|
|
(dstBR.Y != dstTL.Y) )
|
|
{
|
|
StretchBitsMainLoop(outspan, &dstTL, &dstBR);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Default case - handles generic drawing including
|
|
// rotation, shear, etc.
|
|
|
|
INT yMinIdx = 0; // index of the smallest y coordinate.
|
|
INT y; // current scanline.
|
|
|
|
// Number of points - used for wrap computation.
|
|
|
|
const INT points = 4;
|
|
|
|
// search for the minimum y coordinate index.
|
|
|
|
for(y=1;y<points;y++)
|
|
{
|
|
if(fix4Dst[y].Y < fix4Dst[yMinIdx].Y)
|
|
{
|
|
yMinIdx = y;
|
|
}
|
|
}
|
|
y = GpFix4Ceiling(fix4Dst[yMinIdx].Y);
|
|
|
|
// DDA for left and right edges.
|
|
// ASSUMPTION: Convex polygon => two edges only.
|
|
|
|
// Work out which edge is left and which is right.
|
|
|
|
INT index1, index2;
|
|
REAL det;
|
|
|
|
index1 = yMinIdx-1;
|
|
if(index1<0)
|
|
{
|
|
index1=points-1;
|
|
}
|
|
|
|
index2 = yMinIdx+1;
|
|
if(index2>=points)
|
|
{
|
|
index2=0;
|
|
}
|
|
|
|
// Compute the determinant.
|
|
// The sign of the determinant formed by the first two edges
|
|
// will tell us if the polygon is specified clockwise
|
|
// or anticlockwise.
|
|
|
|
if( (fix4Dst[index1].Y==fix4Dst[yMinIdx].Y) &&
|
|
(fix4Dst[index2].Y==fix4Dst[yMinIdx].Y) )
|
|
{
|
|
// Both initial edges are horizontal - compare x coordinates.
|
|
// You get this formula by "cancelling out" the zero y terms
|
|
// in the determinant formula below.
|
|
// This part of the formula only works because we know that
|
|
// yMinIdx is the index of the minimum y coordinate in the
|
|
// polygon.
|
|
|
|
det = (REAL)(fix4Dst[index1].X-fix4Dst[index2].X);
|
|
}
|
|
else
|
|
{
|
|
// Full determinant computation
|
|
|
|
det = (REAL)
|
|
(fix4Dst[index2].Y-fix4Dst[yMinIdx].Y)*
|
|
(fix4Dst[index1].X-fix4Dst[yMinIdx].X)-
|
|
(REAL)
|
|
(fix4Dst[index1].Y-fix4Dst[yMinIdx].Y)*
|
|
(fix4Dst[index2].X-fix4Dst[yMinIdx].X);
|
|
}
|
|
|
|
// Even though we've discarded all the empty rectangle cases, it's
|
|
// still possible for really small non-zero matrix coefficients to
|
|
// be multiplied together giving zero - due to rounding error at
|
|
// the precision limit of the real number representation.
|
|
// If the det is zero (or really close) the quad has no area and
|
|
// we succeed the call immediately.
|
|
|
|
if(REALABS(det) < REAL_EPSILON)
|
|
{
|
|
goto DrawImage_Done;
|
|
}
|
|
|
|
{
|
|
// Initialize the iterators with the direction dependent on the
|
|
// sign of the determinant.
|
|
// These are scoped because of the exit branches above (goto)
|
|
|
|
DdaIterator left(fix4Dst, points, (det>0.0f)?1:-1, yMinIdx);
|
|
DdaIterator right(fix4Dst, points, (det>0.0f)?-1:1, yMinIdx);
|
|
|
|
// If both iterators are valid, start the loop.
|
|
|
|
INT xLeft, xRight;
|
|
|
|
if(left.IsValid() && right.IsValid())
|
|
{
|
|
do {
|
|
// Output the data. We know we only have one span because
|
|
// we're drawing a convex quad.
|
|
|
|
xLeft = left.GetX();
|
|
xRight = right.GetX();
|
|
|
|
// If this ever happens, we've broken a fundumental
|
|
// assumption of the OutputSpan code. Our x coordinates
|
|
// must be ordered.
|
|
|
|
ASSERT(xLeft <= xRight);
|
|
|
|
// Trivially reject any scanlines that don't have any
|
|
// pixels.
|
|
|
|
if(xRight>xLeft)
|
|
{
|
|
outspan->OutputSpan(y, xLeft, xRight);
|
|
}
|
|
|
|
// Update the y value to the new scanline
|
|
|
|
y++;
|
|
|
|
// Incrementaly update DDAs for this new scanline.
|
|
// End the loop if we're done with the last edge.
|
|
|
|
} while(left.Next(y-1) && right.Next(y-1));
|
|
} // end if valid iterators
|
|
} // end scope
|
|
} // end else (rotation block)
|
|
|
|
// We're done - clean up and return status.
|
|
|
|
DrawImage_Done:
|
|
|
|
output->End();
|
|
|
|
if (clipRegion != NULL)
|
|
{
|
|
clipRegion->EndClipping();
|
|
}
|
|
|
|
|
|
delete output;
|
|
return status;
|
|
}
|
|
|
|
|
|
|