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.
 
 
 
 
 
 

1710 lines
47 KiB

/**************************************************************************\
*
* Copyright (c) 1999 Microsoft Corporation
*
* Module Name:
*
* One-pixel-wide solid aliased lines
*
* Abstract:
*
* Draws aliased solid-color lines which are one pixel wide.
* Supports clipping against complex clipping regions.
*
* History:
*
* 03/31/1999 AMatos
* Created it.
* 08/17/1999 AGodfrey
* Separated aliased from antialiased.
*
\**************************************************************************/
#include "precomp.hpp"
#pragma optimize("a",on)
//------------------------------------------------------------------------
// Global array that stores all the different options of drawing functions.
// If the order of the functions change, the offset constants must also
// change.
//------------------------------------------------------------------------
#define FUNC_X_MAJOR 0
#define FUNC_Y_MAJOR 1
#define FUNC_CLIP_OFFSET 2
typedef VOID (OnePixelLineDDAAliased::*DDAFunc)(DpScanBuffer*);
static DDAFunc gDrawFunctions[] = {
OnePixelLineDDAAliased::DrawXMajor,
OnePixelLineDDAAliased::DrawYMajor,
OnePixelLineDDAAliased::DrawXMajorClip,
OnePixelLineDDAAliased::DrawYMajorClip,
};
//------------------------------------------------------------------------
// Constants used for manipulating fixed point and doing all the bitwise
// operations on the aliased and antialiased DDA. I know some of these
// are already defined elsewhere, but I do it again here as it might be nice to
// keep this independent of the rest of gdiplus.
//------------------------------------------------------------------------
// Fixed point
#define RealToFix GpRealToFix4
#define FBITS 4
#define FMASK 0xf
#define FINVMASK 0xfffffff0
#define FSIZE 16
#define FHALF 8
#define FHALFMASK 7
/**************************************************************************\
*
* Function Description:
*
* Does all the DDA setup that is common to aliased and antialiased
* lines.
*
* Arguments:
*
* [IN] point1 - end point
* [IN] point2 - end point
* [IN] drawLast - FALSE if the line is to be end-exclusive
* Return Value:
*
* Returns TRUE if the drawing should continue, meaning the line
* has non-zero length.
*
* Created:
*
* 03/31/1999 AMatos
*
\**************************************************************************/
BOOL
OnePixelLineDDAAliased::SetupCommon(
GpPointF *point1,
GpPointF *point2,
BOOL drawLast,
INT width
)
{
MaximumWidth = width;
// Turn the points into fixed 28.4
INT x1 = RealToFix(point1->X);
INT x2 = RealToFix(point2->X);
REAL rDeltaX = point2->X - point1->X;
REAL rDeltaY = point2->Y - point1->Y;
if( rDeltaX == 0 && rDeltaY == 0 )
{
return FALSE;
}
INT xDir = 1;
if(rDeltaX < 0)
{
rDeltaX = -rDeltaX;
xDir = -1;
}
INT y1 = RealToFix(point1->Y);
INT y2 = RealToFix(point2->Y);
INT yDir = 1;
if( rDeltaY < 0)
{
rDeltaY = -rDeltaY;
yDir = -1;
}
Flipped = FALSE;
if( rDeltaY >= rDeltaX )
{
// y-major
// Invert the endpoints if necessary
if(yDir == -1)
{
INT tmp = y1;
y1 = y2;
y2 = tmp;
tmp = x1;
x1 = x2;
x2 = tmp;
xDir = -xDir;
Flipped = TRUE;
}
// Determine the Slope
Slope = xDir*rDeltaX/rDeltaY;
// Initialize the Start and End points
IsXMajor = FALSE;
MajorStart = y1;
MajorEnd = y2;
MinorStart = x1;
MinorEnd = x2;
MinorDir = xDir;
// Mark that we'll use the y-major functions.
DrawFuncIndex = FUNC_Y_MAJOR;
}
else
{
// x-major
// Invert the endpoints if necessary
if(xDir == -1)
{
INT tmp = x1;
x1 = x2;
x2 = tmp;
tmp = y1;
y1 = y2;
y2 = tmp;
yDir = -yDir;
Flipped = TRUE;
}
Slope = yDir*rDeltaY/rDeltaX;
// Initialize the rest
IsXMajor = TRUE;
MajorStart = x1;
MajorEnd = x2;
MinorStart = y1;
MinorEnd = y2;
MinorDir = yDir;
// Mark that we'll use the x-major functions.
DrawFuncIndex = FUNC_X_MAJOR;
}
// Initialize the Deltas. In fixed point.
DMajor = MajorEnd - MajorStart;
DMinor = (MinorEnd - MinorStart)*MinorDir;
// Mark if we're drawing end-exclusive
IsEndExclusive = !drawLast;
return TRUE;
}
//------------------------------------------------------------------------
// Functions specific to the aliased lines
//------------------------------------------------------------------------
/**************************************************************************\
*
* Function Description:
*
* Does the part of the DDA setup that is specific for aliased lines.
*
* Basically it uses the diamond rule to find the integer endpoints
* based on the fixed point values and substitutes these for the
* integer results coordinates. Also calculates the error values.
*
* Arguments:
*
* Return Value:
*
* Returns FALSE if the drawing should not continue, meaning the line
* has a length of less than 1, and should not be drawn by the GIQ rule.
*
* Created:
*
* 03/31/1999 AMatos
*
\**************************************************************************/
BOOL
OnePixelLineDDAAliased::SetupAliased()
{
// Do the GIQ rule to determine which pixel to start at.
BOOL SlopeIsOne = (DMajor == DMinor);
BOOL SlopeIsPosOne = SlopeIsOne && (1 == MinorDir);
// These will store the integer values.
INT major, minor;
INT majorEnd, minorEnd;
// Find the rounded values in fixed point. The rounding
// rules when a coordinate is halfway between two
// integers is given by the GIQ rule.
minor = (MinorStart + FHALFMASK) & FINVMASK;
minorEnd = (MinorEnd + FHALFMASK) & FINVMASK;
BOOL isEndIn, isStartIn;
if(IsXMajor)
{
if(SlopeIsPosOne)
{
major = (MajorStart + FHALF) & FINVMASK;
majorEnd = (MajorEnd + FHALF) & FINVMASK;
}
else
{
major = (MajorStart + FHALFMASK) & FINVMASK;
majorEnd = (MajorEnd + FHALFMASK) & FINVMASK;
}
isStartIn = IsInDiamond(MajorStart - major, MinorStart - minor,
SlopeIsOne, SlopeIsPosOne);
isEndIn = IsInDiamond(MajorEnd - majorEnd, MinorEnd - minorEnd,
SlopeIsOne, SlopeIsPosOne);
}
else
{
major = (MajorStart + FHALFMASK) & FINVMASK;
majorEnd = (MajorEnd + FHALFMASK) & FINVMASK;
isStartIn = IsInDiamond(MinorStart - minor, MajorStart - major,
FALSE, FALSE);
isEndIn = IsInDiamond(MinorEnd - minorEnd, MajorEnd - majorEnd,
FALSE, FALSE);
}
// Determine if we need to advance the initial point.
if(!(Flipped && IsEndExclusive))
{
if(((MajorStart & FMASK) <= FHALF) && !isStartIn)
{
major += FSIZE;
}
}
else
{
if(isStartIn || ((MajorStart & FMASK) <= FHALF))
{
major += FSIZE;
}
}
// Adjust the initial minor coordinate accodingly
minor = GpFloor(MinorStart + (major - MajorStart)*Slope);
// Bring the initial major coordinate to integer
major = major >> FBITS;
// Do the same for the end point.
if(!Flipped && IsEndExclusive)
{
if(((MajorEnd & FMASK) > FHALF) || isEndIn)
{
majorEnd -= FSIZE;
}
}
else
{
if(!isEndIn && ((MajorEnd & FMASK) > FHALF))
{
majorEnd -= FSIZE;
}
}
minorEnd = GpFloor(MinorEnd + (majorEnd - MajorEnd)*Slope);
majorEnd = majorEnd >> FBITS;
// If End is smaller than Start, that means we have a line that
// is smaller than a pixel and bu the diamond rule it should
// not be drawn.
if(majorEnd < major)
{
return FALSE;
}
// Get the error correction values.
ErrorUp = DMinor << 1;
ErrorDown = DMajor << 1;
INT MinorInt;
// Take out the fractions from the DDA. GDI's rounding
// doesn't depend on direction, so for compatability
// round down as GDI does when LINEADJUST281816 is
// defined (see Office 10 bug 281816). Otherwise the rounding
// rule for the minor coordinate depends on the direction
// we are going.
#ifdef LINEADJUST281816
MinorInt = (minor + FHALFMASK) & FINVMASK;
minorEnd = (minorEnd + FHALFMASK) >> FBITS;
#else
if(MinorDir == 1)
{
MinorInt = (minor + FHALF) & FINVMASK;
minorEnd = (minorEnd + FHALF) >> FBITS;
}
else
{
MinorInt = (minor + FHALFMASK) & FINVMASK;
minorEnd = (minorEnd + FHALFMASK) >> FBITS;
}
#endif
// Calculate the initial error based on our fractional
// position in fixed point and convert to integer.
Error = -ErrorDown*(FHALF + MinorDir*(MinorInt - minor));
minor = MinorInt >> FBITS;
Error >>= FBITS;
// Update the class variables
MinorStart = minor;
MinorEnd = minorEnd;
MajorStart = major;
MajorEnd = majorEnd;
return TRUE;
}
/**************************************************************************\
*
* Function Description:
*
* Draws a y major line. Does not support clipping, it assumes that
* it is completely inside any clipping area.
*
* Arguments:
*
* [IN] DpScanBuffer - The scan buffer for accessing the surface.
* Return Value:
*
*
* Created:
*
* 03/31/1999 AMatos
*
\**************************************************************************/
VOID
OnePixelLineDDAAliased::DrawYMajor(
DpScanBuffer *scan
)
{
// Do the DDA loop for the case where there is no complex
// clipping region.
ARGB *buffer;
INT numPixels = MajorEnd - MajorStart;
while(numPixels >= 0)
{
buffer = scan->NextBuffer(MinorStart, MajorStart, 1);
*buffer = Color;
MajorStart++;
Error += ErrorUp;
if( Error > 0 )
{
MinorStart += MinorDir;
Error -= ErrorDown;
}
numPixels--;
}
}
/**************************************************************************\
*
* Function Description:
*
* Draws a x major line. Does not support clipping, it assumes that
* it is completely inside any clipping area.
*
* Arguments:
*
* [IN] DpScanBuffer - The scan buffer for accessing the surface.
* Return Value:
*
*
* Created:
*
* 03/31/1999 AMatos
*
\**************************************************************************/
VOID
OnePixelLineDDAAliased::DrawXMajor(
DpScanBuffer *scan
)
{
INT numPixels = MajorEnd - MajorStart + 1;
ARGB *buffer;
INT width = 0;
const INT maxWidth = MaximumWidth;
// Run the DDA. First accumulate the width, and when
// the scanline changes write the whole scanline at
// once.
buffer = scan->NextBuffer(MajorStart, MinorStart, maxWidth);
while(numPixels--)
{
MajorStart++;
*buffer++ = Color;
width++;
Error += ErrorUp;
if( Error > 0 && numPixels)
{
MinorStart += MinorDir;
Error -= ErrorDown;
scan->UpdateWidth(width);
buffer = scan->NextBuffer(MajorStart, MinorStart, maxWidth);
width = 0;
}
}
scan->UpdateWidth(width);
}
/**************************************************************************\
*
* Function Description:
*
* Draws a y major line taking clipping into account. It uses the member
* variables MajorIn, MajorOut, MinorIn, MinorOut of the class as the
* clip rectangle. It advances untill the line is in the clip rectangle and
* draws untill it gets out or the end point is reached. In the first case,
* it leaves the DDA in a state so that it can be called again with another
* clipping rectangle.
*
* Arguments:
*
* [IN] DpScanBuffer - The scan buffer for accessing the surface.
* Return Value:
*
*
* Created:
*
* 03/31/1999 AMatos
*
\**************************************************************************/
VOID
OnePixelLineDDAAliased::DrawYMajorClip(
DpScanBuffer *scan
)
{
INT saveMajor2 = MajorEnd;
INT saveMinor2 = MinorEnd;
// Do the DDA if all the clipping left the line
// valid.
if(StepUpAliasedClip())
{
// The length given by the minor coord. Whichever length
// comes to 0 first, minor or major, we stop.
INT minorDiff = (MinorEnd - MinorStart)*MinorDir;
ARGB *buffer;
INT numPixels = MajorEnd - MajorStart;
while((minorDiff >= 0) && (numPixels >= 0))
{
buffer = scan->NextBuffer(MinorStart, MajorStart, 1);
*buffer = Color;
MajorStart++;
Error += ErrorUp;
if( Error > 0 )
{
MinorStart += MinorDir;
Error -= ErrorDown;
minorDiff--;
}
numPixels--;
}
}
// Restore the saved end values
MajorEnd = saveMajor2;
MinorEnd = saveMinor2;
}
/**************************************************************************\
*
* Function Description:
*
* Draws a x major line taking clipping into account. It uses the member
* variables MajorIn, MajorOut, MinorIn, MinorOut of the class as the
* clip rectangle. It advances untill the line is in the clip rectangle and
* draws untill it gets out or the end point is reached. In the first case,
* it leaves the DDA in a state so that it can be called again with another
* clipping rectangle.
*
* Arguments:
*
* [IN] DpScanBuffer - The scan buffer for accessing the surface.
* Return Value:
*
*
* Created:
*
* 03/31/1999 AMatos
*
\**************************************************************************/
VOID
OnePixelLineDDAAliased::DrawXMajorClip(
DpScanBuffer *scan
)
{
INT saveMajor2 = MajorEnd;
INT saveMinor2 = MinorEnd;
const INT maxWidth = MaximumWidth;
if(StepUpAliasedClip())
{
INT minorDiff = (MinorEnd - MinorStart)*MinorDir;
INT numPixels = MajorEnd - MajorStart + 1;
ARGB *buffer;
INT width = 0;
// Run the DDA for the case where there is no
// complex clipping region, which is a lot easier.
buffer = scan->NextBuffer(MajorStart, MinorStart, maxWidth);
while(numPixels--)
{
MajorStart++;
width++;
*buffer++ = Color;
Error += ErrorUp;
if( Error > 0 && numPixels)
{
MinorStart += MinorDir;
Error -= ErrorDown;
minorDiff--;
scan->UpdateWidth(width);
// If all of the scanlines in the minor direction have
// been filled, then quit now.
if(minorDiff < 0)
{
break;
}
buffer = scan->NextBuffer(MajorStart, MinorStart, maxWidth);
width = 0;
}
}
scan->UpdateWidth(width);
}
MajorEnd = saveMajor2;
MinorEnd = saveMinor2;
}
/**************************************************************************\
*
* Function Description:
*
* Steps the DDA until the start point is at the edge of the
* clipping rectangle. Also, correct the end values so that
* they stop at the end of the rectangle. The caller must save
* these values to restore at the end of the loop.
*
* Arguments:
*
* Return Value:
*
*
*
* Created:
*
* 03/31/1999 AMatos
*
\**************************************************************************/
BOOL
OnePixelLineDDAAliased::StepUpAliasedClip()
{
// Step up on the DDA untill the major coordinate
// is aligned with the rectangles edge
while(MajorStart < MajorIn)
{
MajorStart++;
Error += ErrorUp;
if( Error > 0 )
{
MinorStart += MinorDir;
Error -= ErrorDown;
}
}
// If the minor coordinate is still out, step up untill
// this one is also aligned. In doing that we might pass
// the on the major coordinate, in which case we are done
// and there is no intersection.
INT minorDiff = (MinorIn - MinorStart)*MinorDir;
while(minorDiff > 0 && MajorStart <= MajorOut)
{
MajorStart++;
Error += ErrorUp;
if( Error > 0 )
{
MinorStart += MinorDir;
minorDiff--;
Error -= ErrorDown;
}
}
minorDiff = (MinorEnd - MinorOut)*MinorDir;
if(minorDiff > 0)
{
if((MinorStart - MinorOut)*MinorDir > 0)
{
return FALSE;
}
MinorEnd = MinorOut;
}
if(MajorOut < MajorEnd)
{
MajorEnd = MajorOut;
}
// Return if the line is still valid.
return(MajorStart <= MajorEnd);
}
//--------------------------------------------------------------------
// Auxiliary functions
//--------------------------------------------------------------------
/**************************************************************************\
*
* Function Description:
*
* Clips the line against a rectangle. It assumes that the line endpoints
* are stored in the class in floating point format. This sets an
* order in which this function can be called. It must be after the
* SetupCommon function and before the specific setups for antialiasing
* and aliasing. This is a pain, but it's better than requirering on of
* these to have to know about clipping. The clipping here is done by
* using the Slope and InvSlope members of the class to advance the
* endpoints to the rectangle edges. Thus the function also assumes that
* Slope and InvSlope have been calculated.
*
* Arguments:
*
* [IN] clipRect - The rectangle to clip against
* Return Value:
*
*
* Created:
*
* 03/31/1999 AMatos
*
\**************************************************************************/
BOOL
OnePixelLineDDAAliased::ClipRectangle(
const GpRect* clipRect
)
{
INT clipBottom, clipTop, clipLeft, clipRight;
// Set the major and minor edges of the clipping
// region, converting to fixed point 28.4. Note that
// we don't convert to the pixel center, but to a
// that goes all the way up to the pixel edges. This
// makes a difference for antialiasing. We don't go all
// the way to the edge since some rounding rules could
// endup lighting the next pixel outside of the clipping
// area. That's why we add/subtract 7 instead of 8 for the
// bottom and right, since these are exclusive.
// For the left and top, subtract 8 (1/2 pixel), since here
// we are inclusive.
INT majorMin = (clipRect->GetLeft() << FBITS) - FHALF;
INT majorMax = ((clipRect->GetRight() - 1) << FBITS) + FHALFMASK;
INT minorMax = ((clipRect->GetBottom() - 1) << FBITS) + FHALFMASK;
INT minorMin = (clipRect->GetTop() << FBITS) - FHALF;
if(!IsXMajor)
{
INT tmp;
tmp = majorMin;
majorMin = minorMin;
minorMin = tmp;
tmp = majorMax;
majorMax = minorMax;
minorMax = tmp;
}
// First clip in the major coordinate
BOOL minOut, maxOut;
minOut = MajorStart < majorMin;
maxOut = MajorEnd > majorMax;
if( minOut || maxOut )
{
if(MajorStart > majorMax || MajorEnd < majorMin)
{
return FALSE;
}
if(minOut)
{
MinorStart += GpFloor((majorMin - MajorStart)*Slope);
MajorStart = majorMin;
}
if(maxOut)
{
MinorEnd += GpFloor((majorMax - MajorEnd)*Slope);
MajorEnd = majorMax;
// If we clipped the last point, we don't need to be IsEndExclusive
// anymore, as the last point now is not the line's last
// point but some in the middle.
IsEndExclusive = FALSE;
}
}
// Now clip the minor coordinate
INT *pMajor1, *pMinor1, *pMajor2, *pMinor2;
if(MinorDir == 1)
{
pMajor1 = &MajorStart;
pMajor2 = &MajorEnd;
pMinor1 = &MinorStart;
pMinor2 = &MinorEnd;
}
else
{
pMajor1 = &MajorEnd;
pMajor2 = &MajorStart;
pMinor1 = &MinorEnd;
pMinor2 = &MinorStart;
}
minOut = *pMinor1 < minorMin;
maxOut = *pMinor2 > minorMax;
if(minOut || maxOut)
{
if(*pMinor1 > minorMax || *pMinor2 < minorMin)
{
return FALSE;
}
if(minOut)
{
*pMajor1 += GpFloor((minorMin - *pMinor1)*InvSlope);
*pMinor1 = minorMin;
}
if(maxOut)
{
*pMajor2 += GpFloor((minorMax - *pMinor2)*InvSlope);
*pMinor2 = minorMax;
// If we clipped the last point, we don't need to be endExclusive
// anymore, as the last point now is not the line's last
// point but some in the middle.
IsEndExclusive = FALSE;
}
}
return(TRUE);
}
/**************************************************************************\
*
* Function Description:
*
* Given the fractional parts of a coordinate in fixed point, this
* function returns if the coordinate is inside the diamond at the
* nearest integer position.
*
* Arguments:
*
* [IN] xFrac - Fractional part of the x coordinate
* [IN] yFrac - Fractional part of the y coordinate
* [IN] SlopeIsOne - TRUE if the line has Slope +/- 1
* [IN] SlopeIsPosOne - TRUE if the line has Slope +1
* Return Value:
*
* TRUE if the coordinate is inside the diamond
*
* Created:
*
* 03/31/1999 AMatos
*
\**************************************************************************/
BOOL
OnePixelLineDDAAliased::IsInDiamond(
INT xFrac,
INT yFrac,
BOOL SlopeIsOne,
BOOL SlopeIsPosOne
)
{
// Get the fractional parts of the fix points, and the
// sum of their absolute values.
INT fracSum = 0;
if(xFrac > 0)
{
fracSum += xFrac;
}
else
{
fracSum -= xFrac;
}
if(yFrac > 0)
{
fracSum += yFrac;
}
else
{
fracSum -= yFrac;
}
// Return true if the point is inside the diamond.
if(fracSum < FHALF)
{
return TRUE;
}
// Check the cases where we are at the two vertices of the
// diamond which are considered inside.
if(yFrac == 0)
{
if((SlopeIsPosOne && xFrac == -FHALF) ||
(!SlopeIsPosOne && xFrac == FHALF))
{
return TRUE;
}
}
if((xFrac == 0) && (yFrac == FHALF))
{
return TRUE;
}
// Check for the cases where we are at the edges of the
// diamond with a Slope of one.
if (SlopeIsOne && (fracSum == FHALF))
{
if (SlopeIsPosOne && (xFrac < 0) && (yFrac > 0))
{
return TRUE;
}
if (!SlopeIsPosOne && (xFrac > 0) && (yFrac > 0))
{
return TRUE;
}
}
return FALSE;
}
typedef GpStatus DrawSolidLineFunc(
DpScanBuffer *scan,
const GpRect *clipRect,
const DpClipRegion* clipRegionIn,
GpPointF *point1,
GpPointF *point2,
ARGB inColor,
BOOL drawLast
);
DrawSolidLineFunc DrawSolidLineOnePixelAliased;
DrawSolidLineFunc DrawSolidLineOnePixelAntiAliased;
/**************************************************************************\
*
* Function Description:
*
* Called back by the path enumeration function, this draws a list
* of lines.
*
* Return Value:
*
* GpStatus - Ok or failure status
*
* Created:
*
* 03/31/2000 andrewgo
*
\**************************************************************************/
struct EpSolidStrokeOnePixelContext
{
DrawSolidLineFunc *DrawLineFunction;
DpScanBuffer *Scan;
GpRect *ClipBoundsPointer;
DpClipRegion *ClipRegion;
ARGB Argb;
BOOL DrawLast; // TRUE if draw last pixel in subpaths
};
BOOL
DrawSolidStrokeOnePixel(
VOID *voidContext,
POINT *point, // 28.4 format, an arary of size 'count'
INT vertexCount,
PathEnumerateTermination lastSubpath
)
{
EpSolidStrokeOnePixelContext *context
= static_cast<EpSolidStrokeOnePixelContext*>(voidContext);
ASSERT(vertexCount >= 2);
for (INT i = vertexCount - 1; i != 0; i--, point++)
{
PointF pointOne(TOREAL((point)->x) / 16, TOREAL((point)->y) / 16);
PointF pointTwo(TOREAL((point + 1)->x) / 16, TOREAL((point + 1)->y) / 16) ;
// Note that we're always drawing the last pixel, which is
// fine when we're 100% opaque, because we'll be re-drawing
// the same pixel for consecutive joined lines (it's a little
// more work, but the cost is small and is actually comparable
// to the overhead of deciding whether to do the last pixel
// or not).
//
// This is the wrong thing to do for non-opaque lines, because
// of the redraw issue. But we shouldn't be coming through
// here for the non-opaque case anyway, since any self-overlaps
// of the lines will cause pixel overdraw, which produces the
// 'wrong' result (or at least different from the 'right' result
// as defined by the widener code).
(context->DrawLineFunction)(
context->Scan,
context->ClipBoundsPointer,
context->ClipRegion,
&pointOne,
&pointTwo,
context->Argb,
(lastSubpath==PathEnumerateCloseSubpath) || context->DrawLast
);
}
return(TRUE);
}
/**************************************************************************\
*
* Function Description:
*
* Draws a one-pixel wide path with a solid color.
*
* 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] pen - the pen to use
* [IN] drawLast - TRUE if last pixels in subpaths are to be drawn.
*
* Return Value:
*
* GpStatus - Ok or failure status
*
* Created:
*
* 03/31/1999 AMatos
*
\**************************************************************************/
GpStatus
DpDriver::SolidStrokePathOnePixel(
DpContext *context,
DpBitmap *surface,
const GpRect *drawBounds,
const DpPath *path,
const DpPen *pen,
BOOL drawLast
)
{
GpBrush *brush = GpBrush::GetBrush(pen->Brush);
ASSERT(pen->Brush->Type == BrushTypeSolidColor);
ASSERT(pen->Brush->SolidColor.IsOpaque());
// Antialiased lines are usually drawn using aarasterizer.cpp
// rather than aaline.cpp. If aaline.cpp is to be used, define
// AAONEPIXELLINE_SUPPORT
#ifdef AAONEPIXELLINE_SUPPORT
DrawSolidLineFunc *drawLineFunc = context->AntiAliasMode
? DrawSolidLineOnePixelAntiAliased
: DrawSolidLineOnePixelAliased;
#else
ASSERT(context->AntiAliasMode == 0);
DrawSolidLineFunc *drawLineFunc = DrawSolidLineOnePixelAliased;
#endif
// Determine if alpha blending is necessary
BOOL noTransparentPixels;
noTransparentPixels = (!context->AntiAliasMode) &&
(brush->IsOpaque());
DpScanBuffer scan(
surface->Scan,
this,
context,
surface,
noTransparentPixels);
if (!scan.IsValid())
{
return(GenericError);
}
GpSolidFill * solidBrush = static_cast<GpSolidFill *>(brush);
ARGB argb = solidBrush->GetColor().GetValue();
DpClipRegion *clipRegion = &context->VisibleClip;
GpRect clipBounds;
GpRect *clipBoundsPointer;
RECT clipRect;
RECT *clipRectPointer;
DpRegion::Visibility visibility;
visibility = clipRegion->GetRectVisibility(
drawBounds->X,
drawBounds->Y,
drawBounds->X + drawBounds->Width,
drawBounds->Y + drawBounds->Height);
if (visibility == DpRegion::TotallyVisible)
{
clipBoundsPointer = NULL;
clipRectPointer = NULL;
clipRegion = NULL;
}
else
{
// !!![andrewgo] Is clipBoundsPointer actually needed?
clipRegion->GetBounds(&clipBounds);
clipBoundsPointer = &clipBounds;
// Scale the clip bounds rectangle by 16 to account for our scaling
// to 28.4 coordinates:
clipRect.left = clipBounds.GetLeft() << 4;
clipRect.top = clipBounds.GetTop() << 4;
clipRect.right = clipBounds.GetRight() << 4;
clipRect.bottom = clipBounds.GetBottom() << 4;
clipRectPointer = &clipRect;
// !!![andrewgo] Why is this needed? Why wasn't this covered in
// GetRectVisibility?
if (clipRegion->IsSimple())
{
clipRegion = NULL;
}
}
EpSolidStrokeOnePixelContext drawContext;
drawContext.DrawLineFunction = drawLineFunc;
drawContext.Scan = &scan;
drawContext.ClipBoundsPointer = clipBoundsPointer;
drawContext.ClipRegion = clipRegion;
drawContext.Argb = argb;
drawContext.DrawLast = drawLast;
// Scale the transform by 16 to get 28.4 units:
GpMatrix transform = context->WorldToDevice;
transform.AppendScale(TOREAL(16), TOREAL(16));
FixedPointPathEnumerate(path,
&transform,
clipRectPointer,
PathEnumerateTypeStroke,
DrawSolidStrokeOnePixel,
&drawContext);
return(Ok);
}
/**************************************************************************\
*
* Function Description:
*
* Draws a one-pixe-wide line with a solid color. Calls on the
* OnePixelLineDDAAliased class to do the actual drawing.
*
* Arguments:
*
* [IN] scan - The DpScanBuffer to access the drawing surface
* [IN] clipRect - A single rectangle that includes all the clipping
* region. If there is no clipping, should be set to NULL.
* [IN] clipRegionIn - A complex clipping region. If the clipping region is
* simple, this should be NULL, and clipRect will be used.
* [IN] point1 - line end point
* [IN] point2 - line end point
* [IN] inColor - the solid color
* [IN] drawLast - FALSE if the line is to be end-exclusive.
*
* Return Value:
*
* GpStatus - Ok or failure status
*
* Created:
*
* 03/31/1999 AMatos
*
\**************************************************************************/
GpStatus
DrawSolidLineOnePixelAliased(
DpScanBuffer *scan,
const GpRect *clipRect,
const DpClipRegion* clipRegionIn,
GpPointF *point1,
GpPointF *point2,
ARGB inColor,
BOOL drawLast
)
{
// Take out the const for now because the Enumeration method
// is not const.
DpClipRegion *clipRegion = const_cast<DpClipRegion*>(clipRegionIn);
// Setup the common part of the DDA
OnePixelLineDDAAliased dda;
INT width = scan->GetSurface()->Width;
if(clipRect)
{
// We have a bug in the driver architecture which allows the
// surface associated with the scan to be smaller than the actual
// surface in the screen case (EpScanGdiDci).
// Therefore we need to look at the visible clip bounds also.
// If it turns out that the visible clip is bigger, our maximum
// width needs to be expanded.
// 350997 Aliased line is not clipped to surface
width = max(width, clipRect->Width);
}
if(!dda.SetupCommon(point1, point2, drawLast, width))
{
return Ok;
}
dda.Color = GpColor::ConvertToPremultiplied(inColor);
// Now handle the different clipping cases
if(!clipRect)
{
// This is easy, there is no clipping so just draw.
if(!dda.SetupAliased())
{
return Ok;
}
(dda.*(gDrawFunctions[dda.DrawFuncIndex]))(scan);
return Ok;
}
else
{
// The inverse of the Slope might be needed.
// Can't use the inverse slope if the slope is zero.
if(dda.Slope==0.0F)
{
dda.InvSlope=0.0F;
}
else
{
dda.InvSlope = (1.0F/dda.Slope);
}
// First of all clip against the bounding rectangle
if(!dda.ClipRectangle(clipRect))
{
return Ok;
}
// Do the specific setup
if(!dda.SetupAliased())
{
return Ok;
}
// For each clip rectangle we store it's limits in
// an array of four elements. We then index this array using
// the variables below which depend on the slope and
// direction of the line in the following way: majorIn is edge crossed
// to go into the rect in the major direction, majorOut is the edge
// crossed to go out of the rect in the major direction, and so on.
// The same for xIn, xOut, yIn, yOut.
INT majorIn, majorOut, minorIn, minorOut;
INT xIn, xOut, yIn, yOut;
// Direction to enumerate the rectangles which depends on the
// line
DpClipRegion::Direction enumDirection;
INT clipBounds[4];
// We store all our info in terms of major and minor
// direction, but to deal with cliping rectangles we
// need to know them in terms of x and y, so calculate
// xDir, yDir, the advance slope.
REAL xAdvanceRate;
INT xDir, yDir;
INT yEndLine;
// If the line crosses a span completely, (xStart, yStart)
// is the position where it enters the span and (xEnd, yEnd)
// is the position that it leaves. If it starts inside the
// span, then (xStart, yStart) is the start point
REAL yStart, xStart, xEnd, yEnd;
if(dda.IsXMajor)
{
// Calculate the in-out indices
majorIn = xIn = 0;
majorOut = xOut = 2;
if(dda.MinorDir == 1)
{
minorIn = 1;
minorOut = 3;
enumDirection = DpClipRegion::TopLeftToBottomRight;
}
else
{
minorIn = 3;
minorOut = 1;
enumDirection = DpClipRegion::BottomLeftToTopRight;
}
yIn = minorIn;
yOut = minorOut;
// Make (xStart, yStart) be the initial point
yStart = (REAL)dda.MinorStart;
xStart = (REAL)dda.MajorStart;
// Always advance in positive direction when X is major
xAdvanceRate = REALABS(dda.InvSlope);
xDir = 1;
yDir = dda.MinorDir;
yEndLine = dda.MinorEnd;
}
else
{
majorIn = yIn = 1;
majorOut = yOut = 3;
if(dda.MinorDir == 1)
{
minorIn = 0;
minorOut = 2;
enumDirection = DpClipRegion::TopLeftToBottomRight;
}
else
{
minorIn = 2;
minorOut = 0;
enumDirection = DpClipRegion::TopRightToBottomLeft;
}
xIn = minorIn;
xOut = minorOut;
// Make (xStart, yStart) be the initial point
yStart = (REAL)dda.MajorStart;
xStart = (REAL)dda.MinorStart;
xAdvanceRate = dda.Slope;
xDir = dda.MinorDir;
yDir = 1;
yEndLine = dda.MajorEnd;
}
// Update the drawing function to the correct
// slipping version
dda.DrawFuncIndex += FUNC_CLIP_OFFSET;
if(!clipRegion)
{
// In this case there is only a single rect, so just
// draw clipped to that
// Store the rectangle in an array so we can atribute the
// right values to the MajorIn, majorOut, etc... variables.
// Remember that bottom and right are exclusive.
clipBounds[0] = clipRect->GetLeft();
clipBounds[1] = clipRect->GetTop();
clipBounds[2] = clipRect->GetRight() - 1;
clipBounds[3] = clipRect->GetBottom() - 1;
dda.MajorIn = clipBounds[majorIn];
dda.MajorOut = clipBounds[majorOut];
dda.MinorIn = clipBounds[minorIn];
dda.MinorOut = clipBounds[minorOut];
(dda.*(gDrawFunctions[dda.DrawFuncIndex]))(scan);
return Ok;
}
else
{
BOOL agregating = FALSE;
INT agregateBounds[4];
// We have a complex clipping region. So what we'll do
// is clip against each individual rectangle in the
// cliping region.
clipRegion->StartEnumeration(GpFloor(yStart), enumDirection);
GpRect rect;
// Get the first rectangle.
INT numRects = 1;
clipRegion->Enumerate(&rect, numRects);
clipBounds[0] = rect.GetLeft();
clipBounds[1] = rect.GetTop();
clipBounds[2] = rect.GetRight() - 1;
clipBounds[3] = rect.GetBottom() - 1;
// Store the y position into the span
INT currSpanYMin = clipBounds[yIn];
// We need some special treatment for the case where the
// line is horizontal, since is this case it's not going
// to cross different spans. And it it's not in the current
// span, it's totally clipped out.
if(dda.IsXMajor && dda.ErrorUp == 0)
{
if(yStart >= clipBounds[1] && yStart <= clipBounds[3])
{
xStart = (REAL)dda.MajorStart;
xEnd = (REAL)dda.MajorEnd;
}
else
{
return Ok;
}
}
else
{
if(yStart < clipBounds[1] || yStart > clipBounds[3])
{
xStart = xStart + (clipBounds[yIn] - yStart)*xAdvanceRate;
yStart = (REAL)clipBounds[yIn];
}
// Account for initial DDA error when calculating xEnd so that clipping
// will track what the DDA is actually drawing.
xEnd = xStart + ((clipBounds[yOut] - yStart)*yDir - ((REAL)dda.Error / (REAL)dda.ErrorDown))*xAdvanceRate;
}
yEnd = (REAL)clipBounds[yOut];
while(1)
{
// Get to the first rectangle on the span that crosses the
// line
while((xStart - clipBounds[xOut])*xDir > 0)
{
numRects = 1;
clipRegion->Enumerate(&rect, numRects);
clipBounds[0] = rect.GetLeft();
clipBounds[1] = rect.GetTop();
clipBounds[2] = rect.GetRight() - 1;
clipBounds[3] = rect.GetBottom() - 1;
if(numRects != 1)
{
goto draw_agregated;
}
if(clipBounds[yIn] != currSpanYMin)
{
// There may be pending aggregated drawing operations. If so
// perform them now before doing the next span.
if (agregating)
break;
else
goto process_next_span;
}
}
// Draw on all the rectangles that intersect the
// line
if((xStart - clipBounds[xIn])*xDir > 0 &&
(clipBounds[xOut] - xEnd)*xDir > 0)
{
if(agregating)
{
if((clipBounds[xIn] - agregateBounds[xIn])*xDir < 0)
{
agregateBounds[xIn] = clipBounds[xIn];
}
if((clipBounds[xOut] - agregateBounds[xOut])*xDir > 0)
{
agregateBounds[xOut] = clipBounds[xOut];
}
agregateBounds[yOut] = clipBounds[yOut];
}
else
{
agregateBounds[0] = clipBounds[0];
agregateBounds[1] = clipBounds[1];
agregateBounds[2] = clipBounds[2];
agregateBounds[3] = clipBounds[3];
agregating = TRUE;
}
}
else
{
if(agregating)
{
dda.MajorIn = agregateBounds[majorIn];
dda.MajorOut = agregateBounds[majorOut];
dda.MinorIn = agregateBounds[minorIn];
dda.MinorOut = agregateBounds[minorOut];
(dda.*(gDrawFunctions[dda.DrawFuncIndex]))(scan);
agregating = FALSE;
}
while((xEnd - clipBounds[xIn])*xDir > 0)
{
dda.MajorIn = clipBounds[majorIn];
dda.MajorOut = clipBounds[majorOut];
dda.MinorIn = clipBounds[minorIn];
dda.MinorOut = clipBounds[minorOut];
(dda.*(gDrawFunctions[dda.DrawFuncIndex]))(scan);
if(dda.MajorStart > dda.MajorEnd)
{
return Ok;
}
numRects = 1;
clipRegion->Enumerate(&rect, numRects);
clipBounds[0] = rect.GetLeft();
clipBounds[1] = rect.GetTop();
clipBounds[2] = rect.GetRight() - 1;
clipBounds[3] = rect.GetBottom() - 1;
if(numRects != 1)
{
goto draw_agregated;
}
if(clipBounds[yIn] != currSpanYMin)
{
goto process_next_span;
}
}
}
// Get to the next span
while(clipBounds[yIn] == currSpanYMin)
{
numRects = 1;
clipRegion->Enumerate(&rect, numRects);
clipBounds[0] = rect.GetLeft();
clipBounds[1] = rect.GetTop();
clipBounds[2] = rect.GetRight() - 1;
clipBounds[3] = rect.GetBottom() - 1;
if(numRects != 1)
{
goto draw_agregated;
}
}
process_next_span:
if((clipBounds[yIn] - yEndLine)*yDir > 0)
{
// We are done.
goto draw_agregated;
}
if((clipBounds[yIn] - yEnd)*yDir == 1)
{
xStart = xEnd;
}
else
{
if(agregating)
{
dda.MajorIn = agregateBounds[majorIn];
dda.MajorOut = agregateBounds[majorOut];
dda.MinorIn = agregateBounds[minorIn];
dda.MinorOut = agregateBounds[minorOut];
(dda.*(gDrawFunctions[dda.DrawFuncIndex]))(scan);
if(dda.MajorStart > dda.MajorEnd)
{
return Ok;
}
agregating = FALSE;
}
xStart = xStart + (clipBounds[yIn] - yStart)*yDir*xAdvanceRate;
}
yStart = (REAL)clipBounds[yIn];
// Add 1 to make the amount added to xStart proportional to height of
// the clipping rectangle, since clipBounds are inset by 1.
xEnd = xStart + ((clipBounds[yOut] - yStart)*yDir + 1)*xAdvanceRate;
yEnd = (REAL)clipBounds[yOut];
currSpanYMin = GpFloor(yStart);
}
draw_agregated:
if(agregating)
{
dda.MajorIn = agregateBounds[majorIn];
dda.MajorOut = agregateBounds[majorOut];
dda.MinorIn = agregateBounds[minorIn];
dda.MinorOut = agregateBounds[minorOut];
(dda.*(gDrawFunctions[dda.DrawFuncIndex]))(scan);
}
}
}
return Ok;
}
#pragma optimize("a", off)