Leaked source code of windows server 2003
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.
 
 
 
 
 
 

717 lines
22 KiB

/**************************************************************************\
*
* Copyright (c) 1998 Microsoft Corporation
*
* Abstract:
*
* Engine solid fill routines.
*
* Revision History:
*
* 12/11/1998 andrewgo
* Created it.
*
\**************************************************************************/
#include "precomp.hpp"
/**************************************************************************\
*
* Function Description:
*
* Outputs a single span within a raster as a solid color.
* 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:
*
* 12/15/1998 DCurtis
*
\**************************************************************************/
GpStatus
DpOutputSolidColorSpan::OutputSpan(
INT y,
INT xMin,
INT xMax // xMax is exclusive
)
{
INT width = xMax - xMin;
FillMemoryInt32(Scan->NextBuffer(xMin, y, width), width, Argb);
return Ok;
}
/**************************************************************************\
*
* Function Description:
*
* Fills a path. This distributes to the individual brush fill method.
*
* Arguments:
*
* [IN] context - the context (matrix and clipping)
* [IN] surface - the surface to fill
* [IN] drawBounds - the surface bounds
* [IN] path - the path to fill
* [IN] brush - the brush to use
*
* Return Value:
*
* GpStatus - Ok or failure status
*
* Created:
*
* 01/21/1999 ikkof
*
\**************************************************************************/
GpStatus
DpDriver::FillPath(
DpContext *context,
DpBitmap *surface,
const GpRect *drawBounds,
const DpPath *path,
const DpBrush *brush
)
{
GpStatus status = GenericError;
const GpBrush *gpBrush = GpBrush::GetBrush(brush);
BOOL noTransparentPixels = (!context->AntiAliasMode) && (gpBrush->IsOpaque());
DpScanBuffer scan(
surface->Scan,
this,
context,
surface,
noTransparentPixels);
if (scan.IsValid())
{
if (brush->Type == BrushTypeSolidColor)
{
GpColor color(brush->SolidColor.GetValue());
DpOutputSolidColorSpan output(color.GetPremultipliedValue(), &scan);
status = RasterizePath(path,
&context->WorldToDevice,
path->GetFillMode(),
context->AntiAliasMode,
FALSE,
&output,
&context->VisibleClip,
drawBounds);
}
else
{
// If there is a shrinking world to device transform when using
// a path gradient, then scale the brush and the path to be
// in device units. This eliminates the need to create potentially
// very large gradients or textures.
// Only handle positive scale because some of our driver rectangle
// filling code can't handle negative rects. Doing ABS preserves
// the sign of the input world coordinate rectangles/path.
REAL scaleX = REALABS(context->WorldToDevice.GetM11());
REAL scaleY = REALABS(context->WorldToDevice.GetM22());
DpOutputSpan * output = NULL;
if (brush->Type == BrushTypePathGradient &&
context->WorldToDevice.IsTranslateScale() &&
REALABS(scaleX) > REAL_EPSILON &&
REALABS(scaleY) > REAL_EPSILON &&
(REALABS(scaleX) < 1.0f || REALABS(scaleY) < 1.0f))
{
// I don't like the following hack for magically getting
// a GpBrush from a DpBrush, but DpOutputSpan already does this...
GpBrush * gpbrush = GpBrush::GetBrush( (DpBrush *)(brush));
GpPathGradient *scaledBrush = (GpPathGradient*)(gpbrush->Clone());
if (scaledBrush == NULL)
{
return OutOfMemory;
}
// Scale the cloned brush's path and bounding rect into
// device units.
scaledBrush->ScalePath(scaleX,scaleY);
REAL mOrig[6];
context->WorldToDevice.GetMatrix(mOrig);
context->WorldToDevice.Scale(1.0f/scaleX, 1.0f/scaleY);
output = DpOutputSpan::Create(scaledBrush->GetDeviceBrush(), &scan, context, drawBounds);
if (output != NULL)
{
GpPath *scalePath = ((GpPath*)path)->Clone();
if (scalePath != NULL)
{
GpMatrix scaleMatrix (scaleX, 0.0f, 0.0f, scaleY, 0.0f, 0.0f);
scalePath->Transform(&scaleMatrix);
status = RasterizePath(scalePath,
&context->WorldToDevice,
path->GetFillMode(),
context->AntiAliasMode,
FALSE,
output,
&context->VisibleClip,
drawBounds);
delete scalePath;
}
else
{
status = OutOfMemory;
}
delete output;
}
else
{
status = OutOfMemory;
}
delete scaledBrush;
context->WorldToDevice.SetMatrix(mOrig);
}
else
{
output = DpOutputSpan::Create(brush, &scan, context, drawBounds);
if (output != NULL)
{
status = RasterizePath(path,
&context->WorldToDevice,
path->GetFillMode(),
context->AntiAliasMode,
FALSE,
output,
&context->VisibleClip,
drawBounds);
delete output;
}
}
}
}
return status;
}
/**************************************************************************\
*
* Function Description:
*
* Draws a path. This distributes to the individual pen draw method.
*
* Arguments:
*
* [IN] context - the context (matrix and clipping)
* [IN] surface - the surface to draw to
* [IN] drawBounds - the surface bounds
* [IN] path - the path to stroke
* [IN] pen - the pen to use
*
* Return Value:
*
* GpStatus - Ok or failure status
*
* Created:
*
* 01/06/1999 ikkof
*
\**************************************************************************/
GpStatus
DpDriver::StrokePath(
DpContext *context,
DpBitmap *surface,
const GpRect *drawBounds,
const DpPath *path,
const DpPen *pen
)
{
GpStatus status = GenericError;
const DpBrush *brush = pen->Brush;
REAL dpiX = (context->GetDpiX() > 0)
? (context->GetDpiX())
: (Globals::DesktopDpiX);
BOOL isOnePixelWide = pen->IsOnePixelWide(&context->WorldToDevice, dpiX) &&
pen->IsCenterNoAnchor();
BOOL isOnePixelWideOpaque = isOnePixelWide &&
(brush->Type == BrushTypeSolidColor) &&
(brush->SolidColor.IsOpaque()) &&
!(context->AntiAliasMode);
BOOL isOnePixelWideSolid = isOnePixelWide &&
pen->IsSimple();
// We have a special fast-path for doing single-pixel-wide,
// solid color, opaque, aliased lines:
// !!! [asecchia] RAID 239905.
// The single pixel wide optimized code has significant rounding problems
// that are particularly problematic on bezier curves.
// Bezier curves tend to be enumerated in a particular way that causes
// the SolidStrokePathOnePixel code to enumerate the line segments backward
// hitting badly tested end point conditions, though the problem seems
// to be pervasive.
// The general rasterizer does not have these problems but is about
// 30% slower for single pixel solid lines.
// Turn on the optimization only for polylines until this is fixed.
if (isOnePixelWideOpaque && isOnePixelWideSolid && !path->HasCurve())
{
return SolidStrokePathOnePixel(
context,
surface,
drawBounds,
path,
pen,
TRUE
);
}
const DpPath* widenedPath;
const DpPath* allocatedPath;
GpMatrix *transform;
GpMatrix identityTransform;
if (isOnePixelWideSolid)
{
// Our RasterizePath code can directly draw a one-pixel-wide solid
// line directly:
widenedPath = path;
allocatedPath = NULL;
transform = &context->WorldToDevice;
}
else
{
// We have to widen the path before we can give it to the
// rasterizer. Generate new path now:
REAL dpiX = context->GetDpiX();
REAL dpiY = context->GetDpiY();
if ((dpiX <= 0) || (dpiY <= 0))
{
dpiX = Globals::DesktopDpiX;
dpiY = Globals::DesktopDpiY;
}
widenedPath = path->GetFlattenedPath(
isOnePixelWideOpaque ? NULL : &context->WorldToDevice,
isOnePixelWideOpaque ? Flattened : Widened,
pen
);
allocatedPath = widenedPath;
transform = &identityTransform;
if (!widenedPath)
return OutOfMemory;
// If this line is aliased, opaque and dashed, dash it now and pass the
// dashed path to the single pixel stroking code.
if (isOnePixelWideOpaque && pen->DashStyle != DashStyleSolid)
{
DpPath *dashPath = NULL;
dashPath = ((GpPath*)widenedPath)->CreateDashedPath(pen,
NULL,
dpiX,
dpiY,
1.0f,
FALSE /* don't need caps in 1 px wide case */);
if (!dashPath)
{
delete widenedPath;
return OutOfMemory;
}
Status status = SolidStrokePathOnePixel(context,
surface,
drawBounds,
dashPath,
pen,
FALSE);
delete dashPath;
delete widenedPath;
return status;
}
}
const GpBrush *gpBrush = GpBrush::GetBrush(brush);
BOOL noTransparentPixels = (!context->AntiAliasMode) && (gpBrush->IsOpaque());
DpScanBuffer scan(surface->Scan, this, context, surface, noTransparentPixels);
if (scan.IsValid())
{
if (brush->Type == BrushTypeSolidColor)
{
GpColor color(brush->SolidColor.GetValue());
DpOutputSolidColorSpan output(color.GetPremultipliedValue(), &scan);
status = RasterizePath(widenedPath,
transform,
widenedPath->GetFillMode(),
context->AntiAliasMode,
isOnePixelWideSolid,
&output,
&context->VisibleClip,
drawBounds);
}
else
{
DpOutputSpan * output = DpOutputSpan::Create(brush, &scan, context,
drawBounds);
if (output != NULL)
{
status = RasterizePath(widenedPath,
transform,
widenedPath->GetFillMode(),
context->AntiAliasMode,
isOnePixelWideSolid,
output,
&context->VisibleClip,
drawBounds);
delete output;
}
}
}
if (allocatedPath)
{
delete allocatedPath;
}
return status;
}
/**************************************************************************\
*
* Function Description:
*
* Fills a region. This distributes to the individual brush fill method.
*
* Arguments:
*
* [IN] context - the context (matrix and clipping)
* [IN] surface - the surface to fill
* [IN] drawBounds - the surface bounds
* [IN] region - the region to fill
* [IN] brush - the brush to use
*
* Return Value:
*
* GpStatus - Ok or failure status
*
* Created:
*
* 02/25/1999 DCurtis
*
\**************************************************************************/
GpStatus
DpDriver::FillRegion(
DpContext *context,
DpBitmap *surface,
const GpRect *drawBounds,
const DpRegion *region,
const DpBrush *brush
)
{
GpStatus status = GenericError;
const GpBrush *gpBrush = GpBrush::GetBrush(brush);
DpScanBuffer scan(
surface->Scan,
this,
context,
surface,
gpBrush->IsOpaque());
if (scan.IsValid())
{
DpOutputSpan * output = DpOutputSpan::Create(brush, &scan,
context, drawBounds);
if (output != NULL)
{
DpClipRegion * clipRegion = &(context->VisibleClip);
GpRect clipBounds;
GpRect * clipBoundsPointer = NULL;
DpRegion::Visibility visibility;
visibility = clipRegion->GetRectVisibility(
drawBounds->X,
drawBounds->Y,
drawBounds->X + drawBounds->Width,
drawBounds->Y + drawBounds->Height);
switch (visibility)
{
default: // Need to clip
clipRegion->GetBounds(&clipBounds);
clipBoundsPointer = &clipBounds;
clipRegion->InitClipping(output, drawBounds->Y);
status = region->Fill(clipRegion, clipBoundsPointer);
break;
case DpRegion::TotallyVisible: // No clipping needed
status = region->Fill(output, clipBoundsPointer);
break;
case DpRegion::Invisible:
status = Ok;
break;
}
delete output;
clipRegion->EndClipping();
}
}
return status;
}
GpStatus
DpDriver::MoveBits(
DpContext *context,
DpBitmap *surface,
const GpRect *drawBounds,
const GpRect *dstRect,
const GpPoint *srcPoint
)
{
return(GenericError);
}
GpStatus
DpDriver::Lock(
DpBitmap *surface,
const GpRect *drawBounds,
INT *stride, // [OUT] - Returned stride
VOID **bits // [OUT] - Returned pointer to bits
)
{
return(Ok);
}
VOID
DpDriver::Unlock(
DpBitmap *surface
)
{
}
/**************************************************************************\
*
* Function Description:
*
* Engine version of routine to fill rectangles.
* This is not limited to filling solid color.
*
* Arguments:
*
* [IN] - DDI parameters.
*
* Return Value:
*
* TRUE if successful.
*
* History:
*
* 01/13/1999 ikkof
* Created it.
*
\**************************************************************************/
// !!![andrewgo] What is this doing in a file called "solidfill.cpp"?
GpStatus
DpDriver::FillRects(
DpContext *context,
DpBitmap *surface,
const GpRect *drawBounds,
INT numRects,
const GpRectF *rects,
const DpBrush *brush
)
{
GpStatus status = Ok;
GpBrushType type = brush->Type;
const GpBrush *gpBrush = GpBrush::GetBrush(brush);
DpScanBuffer scan(
surface->Scan,
this,
context,
surface,
gpBrush->IsOpaque());
if(!scan.IsValid())
{
return(GenericError);
}
DpOutputSpan * output = DpOutputSpan::Create(brush, &scan,
context, drawBounds);
if(output == NULL)
return(GenericError);
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);
}
GpMatrix *worldToDevice = &context->WorldToDevice;
const GpRectF * rect = rects;
INT y;
for (INT i = numRects; i != 0; i--, rect++)
{
// We have to check for empty rectangles in world space (because
// after the transform they might have flipped):
if ((rect->Width > 0) && (rect->Height > 0))
{
GpPointF points[4];
points[0].X = rect->X;
points[0].Y = rect->Y;
points[1].X = rect->X + rect->Width;
points[1].Y = rect->Y + rect->Height;
// FillRects only ever gets called when a scaling transform:
// !!![ericvan] printing code calls this to render the brush onto a rectangle,
// but the transform in effect may not be TranslateScale
// !!![andrewgo] Yeah but then isn't the printer case completely
// broken when there is an arbitrary transform?!?
ASSERT(context->IsPrinter ||
worldToDevice->IsTranslateScale());
worldToDevice->Transform(points, 2);
INT left;
INT right;
// convert to INT the same way the GDI+ rasterizer does
// so we get the same rounding error in both places.
if (points[0].X <= points[1].X)
{
left = RasterizerCeiling(points[0].X);
right = RasterizerCeiling(points[1].X); // exclusive
}
else
{
left = RasterizerCeiling(points[1].X);
right = RasterizerCeiling(points[0].X); // exclusive
}
// Since right is exclusive, we don't draw anything
// if left >= right.
INT width = right - left;
INT top;
INT bottom;
if (points[0].Y <= points[1].Y)
{
top = RasterizerCeiling(points[0].Y);
bottom = RasterizerCeiling(points[1].Y); // exclusive
}
else
{
top = RasterizerCeiling(points[1].Y);
bottom = RasterizerCeiling(points[0].Y); // exclusive
}
// Since bottom is exclusive, we don't draw anything
// if top >= bottom.
if ((width > 0) && (top < bottom))
{
GpRect clippedRect;
if(clipRegion)
{
visibility =
clipRegion->GetRectVisibility(
left, top,
right, bottom, &clippedRect);
}
switch (visibility)
{
case DpRegion::ClippedVisible:
left = clippedRect.X;
top = clippedRect.Y;
right = clippedRect.GetRight();
bottom = clippedRect.GetBottom();
width = right - left;
// FALLTHRU
case DpRegion::TotallyVisible:
for (y = top; y < bottom; y++)
{
output->OutputSpan(y, left, right);
}
break;
case DpRegion::PartiallyVisible:
for (y = top; y < bottom; y++)
{
clipRegion->OutputSpan(y, left, right);
}
break;
case DpRegion::Invisible:
break;
}
}
}
}
if (clipRegion != NULL)
{
clipRegion->EndClipping();
}
delete output;
return status;
}