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.
1886 lines
56 KiB
1886 lines
56 KiB
/**************************************************************************\
|
|
*
|
|
* Copyright (c) 1998 Microsoft Corporation
|
|
*
|
|
* Module Name:
|
|
*
|
|
* One-pixel-wide solid anti-aliased lines
|
|
*
|
|
* Abstract:
|
|
*
|
|
* Draws anti-aliased solid-color lines which are one pixel wide.
|
|
* Supports clipping against complex clipping regions.
|
|
*
|
|
* History:
|
|
*
|
|
* 3/31/1999 AMatos
|
|
* Created it.
|
|
* 08/17/1999 AGodfrey
|
|
* Separated aliased from antialiased.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
#include "precomp.hpp"
|
|
|
|
#pragma optimize("a", on)
|
|
|
|
// 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
|
|
|
|
//------------------------------------------------------------------------
|
|
// 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 (OnePixelLineDDAAntiAliased::*DDAFunc)(DpScanBuffer*);
|
|
|
|
DDAFunc gDrawFunctions[] = {
|
|
OnePixelLineDDAAntiAliased::DrawXMajor,
|
|
OnePixelLineDDAAntiAliased::DrawYMajor,
|
|
OnePixelLineDDAAntiAliased::DrawXMajorClip,
|
|
OnePixelLineDDAAntiAliased::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
|
|
|
|
// Antialiasing constants
|
|
|
|
#define MAXALPHA 255
|
|
#define MAXERROR 0x08000000
|
|
#define TESTABOVE 0xf8000000
|
|
#define TESTBELOW 0x07ffffff
|
|
#define MAXHALF 0x04000000
|
|
#define CONVERTALPHA 19
|
|
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* 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
|
|
OnePixelLineDDAAntiAliased::SetupCommon(
|
|
GpPointF *point1,
|
|
GpPointF *point2,
|
|
BOOL drawLast
|
|
)
|
|
{
|
|
// 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
|
|
|
|
InvDelta = 1.0F/rDeltaY;
|
|
|
|
// 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*InvDelta;
|
|
|
|
// Initialize the Start and End points
|
|
|
|
IsXMajor = FALSE;
|
|
MajorStart = y1;
|
|
MajorEnd = y2;
|
|
MinorStart = x1;
|
|
MinorEnd = x2;
|
|
MinorDir = xDir;
|
|
|
|
// This will help us for the AntiAliased x-major case.
|
|
|
|
SwitchFirstLast = 1;
|
|
|
|
// Mark that we'll use the y-major functions.
|
|
|
|
DrawFuncIndex = FUNC_Y_MAJOR;
|
|
}
|
|
else
|
|
{
|
|
// x-major
|
|
|
|
InvDelta = 1.0F/rDeltaX;
|
|
|
|
// 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*InvDelta;
|
|
|
|
// Initialize the rest
|
|
|
|
IsXMajor = TRUE;
|
|
MajorStart = x1;
|
|
MajorEnd = x2;
|
|
MinorStart = y1;
|
|
MinorEnd = y2;
|
|
MinorDir = yDir;
|
|
|
|
// This will help us for the AntiAliased x-major case.
|
|
|
|
SwitchFirstLast = MinorDir;
|
|
|
|
// 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;
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Does the part of the DDA setup that is specific for anti-aliased lines.
|
|
*
|
|
* Arguments:
|
|
|
|
* Return Value:
|
|
*
|
|
* Always returns TRUE. It must return a BOOL because it must have the
|
|
* same signature as the aliased case.
|
|
*
|
|
* Created:
|
|
*
|
|
* 03/31/1999 AMatos
|
|
*
|
|
\**************************************************************************/
|
|
|
|
BOOL
|
|
OnePixelLineDDAAntiAliased::SetupAntiAliased()
|
|
{
|
|
const REAL maxError = MAXERROR;
|
|
|
|
// Find the integer major positions for the beginning and
|
|
// the end of the line.
|
|
|
|
INT major, minor;
|
|
INT majorEnd, minorEnd;
|
|
|
|
major = (MajorStart + FHALF) >> FBITS;
|
|
majorEnd = (MajorEnd + FHALF) >> FBITS;
|
|
|
|
// Check for the simple case of a one pixel long line
|
|
|
|
if(majorEnd == major)
|
|
{
|
|
AlphaFirst = (MAXALPHA*(MajorEnd - MajorStart)*MinorDir) >> FBITS;
|
|
MajorStart = major;
|
|
MajorEnd = majorEnd;
|
|
MinorStart = (MinorStart + FHALF) >> FBITS;
|
|
return TRUE;
|
|
}
|
|
|
|
// Store the fraction of the first pixel covered due to
|
|
// the start point.
|
|
|
|
FracStart = (major << FBITS) - MajorStart;
|
|
|
|
// Advance the minor coordinate to the integer major
|
|
|
|
MinorStart += GpFloor(Slope*FracStart);
|
|
|
|
// Calculate the length across the line in the minor direction
|
|
|
|
INT halfWidth = RealToFix(LineLength*InvDelta) >> 1;
|
|
|
|
// Make sure thar startX and endX don't end up being the
|
|
// same pixel, which our code does not handle. Theoretically
|
|
// this cannot happen when the width of the line is 1, but
|
|
// let's make sure it doesn't happen because of some roundoff
|
|
// Error.
|
|
|
|
if( halfWidth < FHALF )
|
|
{
|
|
halfWidth = FHALF;
|
|
}
|
|
|
|
INT endMinor = MinorEnd + MinorDir*halfWidth;
|
|
|
|
// Calculate the Error up from the Slope. It needs to be that
|
|
// way so that the Error up will work when the 0-1 interval
|
|
// is mapped to the interval 0 to 0x8000000. See comments below.
|
|
|
|
ErrorUp = GpFloor(Slope*maxError);
|
|
ErrorDown = MinorDir*MAXERROR;
|
|
|
|
// For a given aa one pixel wide line, there can be up to three pixels
|
|
// baing painted across the line. We call these the first, middle and
|
|
// last lines. So all variable with such prefixes refer to one
|
|
// of these three. firstX and lastX are the positions of these lines.
|
|
// In the x-major case, unlike the y-major, we might need to switch
|
|
// who is the first and who is the second line depending on the
|
|
// direction, so that the order that each line fills the scan
|
|
// remains the same. That's why we multiply halfWidth by yDir.
|
|
|
|
halfWidth *= SwitchFirstLast;
|
|
|
|
MinorFirst = MinorStart - halfWidth;
|
|
MinorLast = MinorStart + halfWidth;
|
|
|
|
// Calculate the initial Error. The Error is mapped so that 1 is
|
|
// taken to MAXERROR. So we find how mush we are into the
|
|
// pixel in X, which is a number between 0 and 16 (n.4). We then
|
|
// multiply this by MAXERROR and shift it from fized point. Finally we add
|
|
// MAXHALF so that the 0-1 interval is mapped to 0 to MAXERROR
|
|
// instead of from -MAXHALF and MAXHALF .
|
|
|
|
const INT convError = MAXERROR >> FBITS;
|
|
|
|
ErrorFirst = (MinorFirst - ((MinorFirst + FHALF) & FINVMASK))*
|
|
convError + MAXHALF;
|
|
ErrorLast = (MinorLast - ((MinorLast + FHALF) & FINVMASK))*
|
|
convError + MAXHALF ;
|
|
|
|
// Now calculate the alpha's for the first pixel. This is
|
|
// done from the Error. Since the Error is between
|
|
// 0 and MAXERROR-1, if we shift it back by 19 (CONVERTALPHA)
|
|
// we have a number between 0 and 255. We the multiply by
|
|
// yFrac which takes into account that the end of the line
|
|
// also cuts the coverage down. At the end we convert from
|
|
// 28.4. alphaFirst is the alpha of for the first pixel across the
|
|
// aa line, alpha Mid is for the middle if there is one, and
|
|
// AlphaLast is for the last pixel.
|
|
|
|
FracStart = FracStart + FHALF;
|
|
|
|
// Convert from 28.4 rounding
|
|
|
|
MinorFirst = (MinorFirst + FHALF) >> FBITS;
|
|
MinorLast = (MinorLast + FHALF) >> FBITS;
|
|
|
|
// Store the fraction for the last pixel
|
|
|
|
FracEnd = MajorEnd - (majorEnd << FBITS) + FHALF;
|
|
|
|
// Store the initial values in integer coordinates
|
|
|
|
MajorStart = major;
|
|
MajorEnd = majorEnd;
|
|
MinorStart = MinorFirst;
|
|
MinorEnd = (endMinor + FHALF) >> FBITS;
|
|
|
|
// Now do some initializations specific for the x-major and
|
|
// y-major cases. These can't be done in the drawing routine
|
|
// because those are reused during clipping.
|
|
|
|
if(!IsXMajor)
|
|
{
|
|
// Calculate the coverage values at the initial pixel.
|
|
|
|
AlphaFirst = ((MAXALPHA - (ErrorFirst >> CONVERTALPHA))*
|
|
FracStart) >> FBITS;
|
|
AlphaLast = ((ErrorLast >> CONVERTALPHA)*FracStart) >> FBITS;
|
|
AlphaMid = (MAXALPHA*FracStart) >> FBITS;
|
|
}
|
|
else
|
|
{
|
|
// Depending if we are going up or down, the alpha is calculated
|
|
// a different way from the coverage. In each case we want to
|
|
// estimate the coverage as the area from the current position to
|
|
// the end of the pixel, but which end varies. This is stored
|
|
// in the following biases. We don't have to do this for the
|
|
// y-major line because of the switch between first and last line
|
|
// explained above.
|
|
|
|
AlphaBiasLast = ((1 - MinorDir) >> 1)*TESTBELOW;
|
|
AlphaBiasFirst = ((1 + MinorDir) >> 1)*TESTBELOW;
|
|
|
|
AlphaFirst = ((AlphaBiasFirst - MinorDir*ErrorFirst)*FracStart) >> FBITS;
|
|
AlphaLast = ((AlphaBiasLast + MinorDir*ErrorLast)*FracStart) >> FBITS;
|
|
|
|
// If there is a middle line on the first X value, take xFrac into
|
|
// account. Otherwise, the middle line's alpha is always MAXALPHA.
|
|
|
|
if(MinorDir*(MinorLast - MinorFirst) < 2)
|
|
{
|
|
AlphaMid = MAXALPHA;
|
|
}
|
|
else
|
|
{
|
|
AlphaMid = MAXALPHA*FracStart >> FBITS;
|
|
}
|
|
|
|
// Both the first and last DDAs start with the same
|
|
// major positions, given by the first pixel.
|
|
|
|
MajorFirst = MajorLast = MajorStart;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Draws a y major anti-aliased 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
|
|
OnePixelLineDDAAntiAliased::DrawYMajor(
|
|
DpScanBuffer *scan
|
|
)
|
|
{
|
|
ARGB *buffer;
|
|
|
|
// Treat the special case where the line is just
|
|
// one pixel long.
|
|
|
|
if( MajorEnd == MajorStart)
|
|
{
|
|
buffer = scan->NextBuffer( MinorStart, MajorStart, 1);
|
|
*buffer = GpColor::PremultiplyWithCoverage(Color, static_cast<BYTE>(AlphaFirst));
|
|
return;
|
|
}
|
|
|
|
// Get the number of pixels not counting the last one.
|
|
// Which requires special endpoint treatment.
|
|
|
|
INT numPixels = MajorEnd - MajorStart;
|
|
BOOL endDone = FALSE;
|
|
|
|
// There can be two or three pixels across the line
|
|
|
|
INT pixelWidth = MinorLast - MinorFirst + 1;
|
|
|
|
while(numPixels)
|
|
{
|
|
numPixels--;
|
|
|
|
last_pixel:
|
|
|
|
// Get the scanline buffer buffer
|
|
|
|
buffer = scan->NextBuffer(MinorFirst, MajorStart, pixelWidth);
|
|
|
|
// Write the value of the first DDA
|
|
|
|
*buffer++ = GpColor::PremultiplyWithCoverage(Color, static_cast<BYTE>(AlphaFirst));
|
|
|
|
// If there is a middle line, write its value.
|
|
|
|
if(pixelWidth > 2)
|
|
{
|
|
*buffer++ = GpColor::PremultiplyWithCoverage(Color, static_cast<BYTE>(AlphaMid));
|
|
}
|
|
|
|
// Write the value of the last (2nd or 3rd) DDA
|
|
|
|
*buffer++ = GpColor::PremultiplyWithCoverage(Color, static_cast<BYTE>(AlphaLast));
|
|
|
|
// Update the errors of both DDAs
|
|
|
|
ErrorFirst+= ErrorUp;
|
|
ErrorLast += ErrorUp;
|
|
MajorStart++;
|
|
|
|
if(ErrorFirst & TESTABOVE)
|
|
{
|
|
ErrorFirst -= ErrorDown;
|
|
MinorFirst += MinorDir;
|
|
}
|
|
if(ErrorLast & TESTABOVE)
|
|
{
|
|
ErrorLast -= ErrorDown;
|
|
MinorLast += MinorDir;
|
|
}
|
|
|
|
// Calculate the new alphas for the next scan, and
|
|
// the new line width.
|
|
|
|
AlphaFirst = MAXALPHA - (ErrorFirst >> CONVERTALPHA);
|
|
AlphaLast = (ErrorLast >> CONVERTALPHA);
|
|
AlphaMid = MAXALPHA;
|
|
|
|
pixelWidth = MinorLast - MinorFirst + 1;
|
|
}
|
|
|
|
// The last scan requires special treatment since its coverage
|
|
// must be multiplied my the stored end coverage. So so this
|
|
// multiplication and go back to the body of the loop above
|
|
// to draw the last scan.
|
|
|
|
if(!endDone)
|
|
{
|
|
AlphaFirst = (AlphaFirst*FracEnd) >> FBITS;
|
|
AlphaLast = (AlphaLast*FracEnd) >> FBITS;
|
|
AlphaMid = (AlphaMid*FracEnd) >> FBITS;
|
|
|
|
endDone = TRUE;
|
|
goto last_pixel;
|
|
}
|
|
}
|
|
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Draws a x major anti-aliased 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
|
|
OnePixelLineDDAAntiAliased::DrawXMajor(
|
|
DpScanBuffer *scan
|
|
)
|
|
{
|
|
ARGB *buffer;
|
|
INT maxWidth = scan->GetSurface()->Width;
|
|
|
|
// Treat the special case where the line is just
|
|
// one pixel long.
|
|
|
|
if( MajorEnd == MajorStart)
|
|
{
|
|
buffer = scan->NextBuffer( MajorStart, MinorStart, 1);
|
|
*buffer = GpColor::PremultiplyWithCoverage(Color, static_cast<BYTE>(AlphaFirst));
|
|
return;
|
|
}
|
|
|
|
// For an x-major one-pixel wide line, there can be up to
|
|
// three different scans being painted for the same x
|
|
// position. But in our case we can't draw to these all at
|
|
// the same time since some surfaces can only be accessed
|
|
// one scan at a time. So the algorithm used here does all the
|
|
// drawing to one scan at each time. But on the first scan, only
|
|
// the first line should be drawn, on the second one both the
|
|
// first and middle (if there is a middle) and only then all
|
|
// the lines. So correct the Error of the last line so that
|
|
// it'll only be drawn when we are at the second or third scan line.
|
|
// Also correct the alpha since it'll also be crecremented for
|
|
// each scan line.
|
|
|
|
ErrorLast += MinorDir*(MinorLast - MinorFirst)*ErrorDown;
|
|
AlphaLast += (MinorLast - MinorFirst)*ErrorDown;
|
|
|
|
// Get the pointer to the buffer
|
|
|
|
buffer = scan->NextBuffer(MajorLast, MinorStart, maxWidth);
|
|
|
|
INT width = 0;
|
|
INT alpha;
|
|
INT middleMajor;
|
|
|
|
while(MajorLast <= MajorEnd)
|
|
{
|
|
// Fill the scan with the portion corresponding to the
|
|
// last line, which shoudl comes first on the scan. This is
|
|
// why we use the class member SwitchFirstLast, so we can decide
|
|
// based on the line direction which DDA will be the first and last
|
|
// so that the last one (paradoxically) always comes first on the
|
|
// scan. Keep doing it untill the last line chages scan. Check for
|
|
// the end to multiply by the last pixel's coverage.
|
|
|
|
while(!(ErrorLast & TESTABOVE))
|
|
{
|
|
if(MajorLast == MajorEnd)
|
|
{
|
|
AlphaLast = (AlphaLast*FracEnd) >> FBITS;
|
|
|
|
// Increment the error to correct for the
|
|
// decrementing below, since we didn't leave the
|
|
// loop because the error became above 0.
|
|
|
|
ErrorLast += ErrorDown;
|
|
}
|
|
*buffer++ = GpColor::PremultiplyWithCoverage(Color,
|
|
static_cast<BYTE>(AlphaLast >> CONVERTALPHA));
|
|
ErrorLast += ErrorUp;
|
|
AlphaLast = AlphaBiasLast + MinorDir*ErrorLast;
|
|
width++;
|
|
MajorLast++;
|
|
}
|
|
|
|
// We changed scans on the last DDA, so update the errors
|
|
|
|
ErrorLast -= ErrorDown;
|
|
AlphaLast -= MinorDir*ErrorDown;
|
|
|
|
// Fill in the middle part if there is one
|
|
|
|
middleMajor = MajorLast;
|
|
|
|
while(middleMajor < MajorFirst)
|
|
{
|
|
if( middleMajor == MajorEnd)
|
|
{
|
|
AlphaMid = (AlphaMid*FracEnd) >> FBITS;
|
|
}
|
|
*buffer++ = GpColor::PremultiplyWithCoverage(Color, static_cast<BYTE>(AlphaMid));
|
|
AlphaMid = MAXALPHA;
|
|
width++;
|
|
middleMajor++;
|
|
}
|
|
|
|
// Fill the scan with the portion corresponding to the
|
|
// first line, which comes last. Keep doing it untill the
|
|
// last line chages scan.
|
|
|
|
while(!(ErrorFirst & TESTABOVE))
|
|
{
|
|
if(MajorFirst == MajorEnd)
|
|
{
|
|
AlphaFirst = (AlphaFirst*FracEnd) >> FBITS;
|
|
|
|
// Since we can have at most three more scans
|
|
// increment ErrorFirst so that we never go in here again
|
|
|
|
ErrorFirst += 4*ErrorDown;
|
|
}
|
|
|
|
*buffer++ = GpColor::PremultiplyWithCoverage(
|
|
Color,
|
|
static_cast<BYTE>(AlphaFirst >> CONVERTALPHA));
|
|
ErrorFirst += ErrorUp;
|
|
AlphaFirst = AlphaBiasFirst - MinorDir*ErrorFirst;
|
|
width++;
|
|
MajorFirst++;
|
|
}
|
|
|
|
// Update the errors on the first scan
|
|
|
|
ErrorFirst -= ErrorDown;
|
|
AlphaFirst += MinorDir*ErrorDown;
|
|
|
|
// Write the buffer and update the minor variables
|
|
|
|
scan->UpdateWidth(width);
|
|
MinorStart += MinorDir;
|
|
if (MajorLast <= MajorEnd)
|
|
{
|
|
buffer = scan->NextBuffer(MajorLast, 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
|
|
OnePixelLineDDAAntiAliased::DrawYMajorClip(
|
|
DpScanBuffer *scan
|
|
)
|
|
{
|
|
ARGB *buffer;
|
|
|
|
// Treat the special case where the line is just
|
|
// one pixel long.
|
|
|
|
if( MajorEnd == MajorStart)
|
|
{
|
|
// Check if the point is inside the rectangle
|
|
|
|
if((MajorStart >= MajorIn) &&
|
|
(MajorStart <= MajorOut) &&
|
|
((MinorStart - MinorIn)*MinorDir >= 0) &&
|
|
((MinorOut - MinorStart)*MinorDir >= 0))
|
|
{
|
|
buffer = scan->NextBuffer( MinorStart, MajorStart, 1);
|
|
*buffer = GpColor::PremultiplyWithCoverage(Color, static_cast<BYTE>(AlphaFirst));
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Align the major start coordinate with the edge of the
|
|
// cliprectangle
|
|
|
|
INT numScans = MajorIn - MajorStart;
|
|
|
|
while(numScans > 0)
|
|
{
|
|
|
|
ErrorFirst+= ErrorUp;
|
|
ErrorLast += ErrorUp;
|
|
MajorStart++;
|
|
numScans--;
|
|
|
|
if(ErrorFirst & MAXERROR)
|
|
{
|
|
ErrorFirst -= ErrorDown;
|
|
MinorFirst += MinorDir;
|
|
}
|
|
if(ErrorLast & MAXERROR)
|
|
{
|
|
ErrorLast -= ErrorDown;
|
|
MinorLast += MinorDir;
|
|
}
|
|
|
|
// Calculate the new alphas for the next line, and
|
|
// the width.
|
|
|
|
AlphaFirst = MAXALPHA - (ErrorFirst >> CONVERTALPHA);
|
|
AlphaLast = (ErrorLast >> CONVERTALPHA);
|
|
AlphaMid = MAXALPHA;
|
|
}
|
|
|
|
// Save the end values
|
|
|
|
INT saveMajor2 = MajorEnd;
|
|
INT saveFracEnd = FracEnd;
|
|
|
|
// If the end major coordinate is outside of the rectangle,
|
|
// mark that the DDA should stop at the edge
|
|
|
|
if(MajorEnd > MajorOut)
|
|
{
|
|
MajorEnd = MajorOut;
|
|
FracEnd = FSIZE;
|
|
}
|
|
|
|
// Number of pixels to draw, not counting the last
|
|
|
|
INT numPixels = MajorEnd - MajorStart;
|
|
BOOL endDone = FALSE;
|
|
|
|
// There can be two or three pixels across the line
|
|
|
|
INT pixelWidth = MinorLast - MinorFirst + 1;
|
|
|
|
// Do the DDA loop. Two loops are implemented here. The
|
|
// first one is used in the case that the x coordinate of
|
|
// the rectangle is close enough to the constant-y edges
|
|
// of the clip rectangle. In this case, it's a pain, since
|
|
// we have to check each pixel that we are writing if it's
|
|
// not outside. Thus, as soon as we notice that we are
|
|
// far from the edges we go to the other loop that doesn't
|
|
// check all that. All it checks is if it got close enough
|
|
// to the other edge, in which case it comes back to this
|
|
// loop, using the label last_part. firstOutDist, firstInDist,
|
|
// lastOutDist and lastInDist keeps track of the number of
|
|
// pixels between the first and last DDAs and the In and
|
|
// Out y-constant edges of the rectangle.
|
|
|
|
INT firstOutDist = (MinorOut - MinorFirst)*MinorDir;
|
|
|
|
last_part:
|
|
|
|
INT firstInDist = (MinorFirst - MinorIn)*MinorDir;
|
|
INT lastInDist = (MinorLast - MinorIn)*MinorDir;
|
|
INT lastOutDist = (MinorOut - MinorLast)*MinorDir;
|
|
|
|
while(numPixels > 0)
|
|
{
|
|
numPixels--;
|
|
|
|
last_pixel:
|
|
|
|
// Check if it's ok to write the first pixel
|
|
|
|
if(firstInDist >= 0 && firstOutDist >= 0)
|
|
{
|
|
buffer = scan->NextBuffer(MinorFirst, MajorStart, 1);
|
|
*buffer++ = GpColor::PremultiplyWithCoverage(Color, static_cast<BYTE>(AlphaFirst));
|
|
}
|
|
else
|
|
{
|
|
// If the first DDA is out, and we are going in the
|
|
// positive direction, then the whole line is out and
|
|
// we are done
|
|
|
|
if(firstOutDist < 0 && MinorDir == 1)
|
|
{
|
|
goto end;
|
|
}
|
|
}
|
|
|
|
// If the line has 3 pixels across
|
|
|
|
if(pixelWidth > 2)
|
|
{
|
|
// Check if it's ok to write the second pixel
|
|
|
|
if(firstInDist >= -MinorDir && firstOutDist >= MinorDir)
|
|
{
|
|
buffer = scan->NextBuffer(MinorFirst+1, MajorStart, 1);
|
|
*buffer++ = GpColor::PremultiplyWithCoverage(Color, static_cast<BYTE>(AlphaMid));
|
|
}
|
|
}
|
|
|
|
// Now check if it's ok to write the last one
|
|
|
|
if(lastInDist >= 0 && lastOutDist >= 0)
|
|
{
|
|
buffer = scan->NextBuffer(MinorLast, MajorStart, 1);
|
|
*buffer++ = GpColor::PremultiplyWithCoverage(Color, static_cast<BYTE>(AlphaLast));
|
|
}
|
|
else
|
|
{
|
|
// If the first DDA is out, and we are going in the
|
|
// negative direction, then the whole line is out and
|
|
// we are done
|
|
|
|
if(lastOutDist < 0 && MinorDir == -1)
|
|
{
|
|
goto end;
|
|
}
|
|
}
|
|
|
|
// Update the errors
|
|
|
|
ErrorFirst+= ErrorUp;
|
|
ErrorLast += ErrorUp;
|
|
MajorStart++;
|
|
|
|
if(ErrorFirst & TESTABOVE)
|
|
{
|
|
ErrorFirst -= ErrorDown;
|
|
MinorFirst += MinorDir;
|
|
firstInDist++;
|
|
firstOutDist--;
|
|
}
|
|
if(ErrorLast & TESTABOVE)
|
|
{
|
|
ErrorLast -= ErrorDown;
|
|
MinorLast += MinorDir;
|
|
lastInDist++;
|
|
lastOutDist--;
|
|
}
|
|
|
|
// Calculate the new alphas for the next line, and
|
|
// the width.
|
|
|
|
AlphaFirst = MAXALPHA - (ErrorFirst >> CONVERTALPHA);
|
|
AlphaLast = (ErrorLast >> CONVERTALPHA);
|
|
AlphaMid = MAXALPHA;
|
|
|
|
pixelWidth = MinorLast - MinorFirst + 1;
|
|
|
|
// Check to see if we can 'upgrade' to the next loop
|
|
|
|
if(firstInDist >= 3 && firstOutDist >= 3)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
while(numPixels > 0)
|
|
{
|
|
numPixels--;
|
|
|
|
// Get the scanline buffer buffer
|
|
|
|
buffer = scan->NextBuffer(MinorFirst, MajorStart, pixelWidth);
|
|
|
|
// Write the value of the first DDA
|
|
|
|
*buffer++ = GpColor::PremultiplyWithCoverage(Color, static_cast<BYTE>(AlphaFirst));
|
|
|
|
// If there is a middle line, write its value.
|
|
|
|
if(pixelWidth > 2)
|
|
{
|
|
*buffer++ = GpColor::PremultiplyWithCoverage(Color, static_cast<BYTE>(AlphaMid));
|
|
}
|
|
|
|
// Write the value of the last (2nd or 3rd) DDA
|
|
|
|
*buffer++ = GpColor::PremultiplyWithCoverage(Color, static_cast<BYTE>(AlphaLast));
|
|
|
|
// Update the DDA
|
|
|
|
ErrorFirst+= ErrorUp;
|
|
ErrorLast += ErrorUp;
|
|
MajorStart++;
|
|
|
|
if(ErrorFirst & TESTABOVE)
|
|
{
|
|
ErrorFirst -= ErrorDown;
|
|
MinorFirst += MinorDir;
|
|
firstOutDist--;
|
|
}
|
|
if(ErrorLast & TESTABOVE)
|
|
{
|
|
ErrorLast -= ErrorDown;
|
|
MinorLast += MinorDir;
|
|
}
|
|
|
|
// Calculate the new alphas for the next line, and
|
|
// the width.
|
|
|
|
AlphaFirst = MAXALPHA - (ErrorFirst >> CONVERTALPHA);
|
|
AlphaLast = (ErrorLast >> CONVERTALPHA);
|
|
AlphaMid = MAXALPHA;
|
|
|
|
pixelWidth = MinorLast - MinorFirst + 1;
|
|
|
|
// Now check if it's time to go to the other loop
|
|
// because we are too close to the out edge
|
|
|
|
if(firstOutDist < 3)
|
|
{
|
|
goto last_part;
|
|
}
|
|
}
|
|
|
|
// Now if we haven't gotten here yet, do the last pixel
|
|
// and go once more through the loop.
|
|
|
|
if(!endDone)
|
|
{
|
|
AlphaFirst = (AlphaFirst*FracEnd) >> FBITS;
|
|
AlphaLast = (AlphaLast*FracEnd) >> FBITS;
|
|
AlphaMid = (AlphaMid*FracEnd) >> FBITS;
|
|
|
|
endDone = TRUE;
|
|
goto last_pixel;
|
|
}
|
|
|
|
end:
|
|
|
|
MajorEnd = saveMajor2;
|
|
FracEnd = saveFracEnd;
|
|
}
|
|
|
|
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* 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
|
|
OnePixelLineDDAAntiAliased::DrawXMajorClip(
|
|
DpScanBuffer *scan
|
|
)
|
|
{
|
|
ARGB *buffer;
|
|
INT maxWidth = scan->GetSurface()->Width;
|
|
|
|
// Treat the special case where the line is just
|
|
// one pixel long.
|
|
|
|
if( MajorEnd == MajorStart)
|
|
{
|
|
// Check to see if the point is inside the rectangle
|
|
|
|
if((MajorStart >= MajorIn) &&
|
|
(MajorStart <= MajorOut) &&
|
|
((MinorStart - MinorIn)*MinorDir >= 0) &&
|
|
((MinorOut - MinorStart)*MinorDir >= 0))
|
|
{
|
|
buffer = scan->NextBuffer( MajorStart, MinorStart, 1);
|
|
*buffer = GpColor::PremultiplyWithCoverage(Color, static_cast<BYTE>(AlphaFirst));
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Save the real end and its fraction
|
|
|
|
INT saveMajor2 = MajorEnd;
|
|
INT saveFracEnd = FracEnd;
|
|
|
|
// If the end major coordinate is out, mark that we must stop
|
|
// before. Also make the fraction be one, since the last
|
|
// one drawn now should not have a fraction
|
|
|
|
if(MajorOut < MajorEnd)
|
|
{
|
|
MajorEnd = MajorOut;
|
|
FracEnd = FSIZE;
|
|
}
|
|
|
|
// Advance until the last DDA is in the right scan line and
|
|
// is aligned with the In y-constant edge of the rectnagle
|
|
|
|
INT numScans = (MinorIn - MinorLast)*MinorDir;
|
|
|
|
while((numScans > 0 && MajorLast <= MajorEnd) || MajorLast < MajorIn)
|
|
{
|
|
ErrorLast += ErrorUp;
|
|
if(ErrorLast & TESTABOVE)
|
|
{
|
|
ErrorLast -= ErrorDown;
|
|
MinorLast += MinorDir;
|
|
numScans--;
|
|
}
|
|
|
|
MajorLast++;
|
|
|
|
// Calculate the alpha for the current pixel
|
|
|
|
AlphaLast = AlphaBiasLast + MinorDir*ErrorLast;
|
|
}
|
|
|
|
// Do the same for the first DDA
|
|
|
|
numScans = (MinorIn - MinorFirst)*MinorDir;
|
|
|
|
while((numScans > 0 && MajorFirst <= MajorEnd) || MajorFirst < MajorIn)
|
|
{
|
|
ErrorFirst += ErrorUp;
|
|
if(ErrorFirst & TESTABOVE)
|
|
{
|
|
ErrorFirst -= ErrorDown;
|
|
MinorFirst += MinorDir;
|
|
numScans--;
|
|
}
|
|
|
|
MajorFirst++;
|
|
|
|
AlphaFirst = AlphaBiasFirst - MinorDir*ErrorFirst;
|
|
}
|
|
|
|
// If there is no middle line in the first x-position,
|
|
// make the middle alpha full, since the start coverage
|
|
// won't apply
|
|
|
|
if((MinorLast - MinorFirst) < 2)
|
|
{
|
|
AlphaMid = MAXALPHA;
|
|
}
|
|
|
|
MinorStart = MinorFirst;
|
|
|
|
// The same way that was done in the non-clipping case,
|
|
// mock arround with the error so we won't draw the
|
|
// last DDA until the first DDA is in the same scan line,
|
|
// or has caught up. We need to adjust the alpha and minor
|
|
// positions for this DDA to, so that when we start
|
|
// drawing they will have the right value
|
|
|
|
ErrorLast += MinorDir*(MinorLast - MinorFirst)*ErrorDown;
|
|
AlphaLast += (MinorLast - MinorFirst)*ErrorDown;
|
|
MinorLast -= (MinorLast - MinorFirst);
|
|
|
|
// Get the pointer to the buffer
|
|
|
|
buffer = scan->NextBuffer(MajorLast, MinorStart, maxWidth);
|
|
|
|
INT width = 0;
|
|
INT alpha;
|
|
INT middleMajor;
|
|
|
|
while(MajorLast <= MajorEnd)
|
|
{
|
|
// Fill the scan with the portion corresponding to the
|
|
// last line, which should come first. Keep doing it
|
|
// until the last line changes scan.
|
|
|
|
while(!(ErrorLast & TESTABOVE))
|
|
{
|
|
// Check if we passed or are at the last pixel
|
|
if(MajorLast >= MajorEnd)
|
|
{
|
|
if(MajorLast == MajorEnd)
|
|
{
|
|
// If we are at, just update the alpha
|
|
|
|
AlphaLast = (AlphaLast*FracEnd) >> FBITS;
|
|
}
|
|
else
|
|
{
|
|
// If we passed, we don't want to draw anymore.
|
|
// Just adjust the error, alpha and minor so they
|
|
// will be right when they are corrected after this
|
|
// loop for the next scan
|
|
|
|
ErrorLast += ErrorDown;
|
|
AlphaLast -= MinorDir*ErrorDown;
|
|
MinorLast -= MinorDir;
|
|
break;
|
|
}
|
|
}
|
|
*buffer++ = GpColor::PremultiplyWithCoverage(Color,
|
|
static_cast<BYTE>(AlphaLast >> CONVERTALPHA));
|
|
ErrorLast += ErrorUp;
|
|
AlphaLast = AlphaBiasLast + MinorDir*ErrorLast;
|
|
width++;
|
|
MajorLast++;
|
|
}
|
|
|
|
// Correct the values for the next scan
|
|
|
|
ErrorLast -= ErrorDown;
|
|
AlphaLast -= MinorDir*ErrorDown;
|
|
MinorLast += MinorDir;
|
|
|
|
// Fill in the middle part.
|
|
|
|
middleMajor = MajorLast;
|
|
|
|
while(middleMajor < MajorFirst)
|
|
{
|
|
if( middleMajor == MajorEnd)
|
|
{
|
|
AlphaMid = (AlphaMid*FracEnd) >> FBITS;
|
|
}
|
|
*buffer++ = GpColor::PremultiplyWithCoverage(Color, static_cast<BYTE>(AlphaMid));
|
|
AlphaMid = MAXALPHA;
|
|
width++;
|
|
middleMajor++;
|
|
}
|
|
|
|
// Fill the scan with the portion corresponding to the
|
|
// first line, which should come first. Keep doing it
|
|
// until the last line changes scan.
|
|
|
|
while(!(ErrorFirst & TESTABOVE))
|
|
{
|
|
// Check for the end pixel, just like we
|
|
// did for the last DDA
|
|
|
|
if(MajorFirst >= MajorEnd)
|
|
{
|
|
if(MajorFirst == MajorEnd)
|
|
{
|
|
AlphaFirst = (AlphaFirst*FracEnd) >> FBITS;
|
|
}
|
|
else
|
|
{
|
|
ErrorFirst += ErrorDown;
|
|
AlphaFirst -= MinorDir*ErrorDown;
|
|
MinorFirst -= MinorDir;
|
|
break;
|
|
}
|
|
}
|
|
*buffer++ = GpColor::PremultiplyWithCoverage(
|
|
Color,
|
|
static_cast<BYTE>(AlphaFirst >> CONVERTALPHA));
|
|
ErrorFirst += ErrorUp;
|
|
AlphaFirst = AlphaBiasFirst - MinorDir*ErrorFirst;
|
|
width++;
|
|
MajorFirst++;
|
|
}
|
|
|
|
// Correct the values for the next scan
|
|
|
|
ErrorFirst -= ErrorDown;
|
|
AlphaFirst += MinorDir*ErrorDown;
|
|
MinorFirst += MinorDir;
|
|
|
|
scan->UpdateWidth(width);
|
|
|
|
// Check to see if we have come to the end of the rectangle
|
|
// through the minor coordinate crossing the Out edge
|
|
// in the x-constant direction
|
|
|
|
if(MinorStart == MinorOut)
|
|
{
|
|
MinorStart += MinorDir;
|
|
break;
|
|
}
|
|
|
|
// Update the minor coordinate and get the next buffer
|
|
// if we aren't done yet.
|
|
|
|
MinorStart += MinorDir;
|
|
if (MajorLast <= MajorEnd)
|
|
{
|
|
buffer = scan->NextBuffer(MajorLast, MinorStart, maxWidth);
|
|
}
|
|
width = 0;
|
|
}
|
|
|
|
scan->UpdateWidth(width);
|
|
|
|
// Restore the old values
|
|
|
|
MajorEnd = saveMajor2;
|
|
FracEnd = saveFracEnd;
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
// 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
|
|
OnePixelLineDDAAntiAliased::ClipRectangle(
|
|
const GpRect* clipRect
|
|
)
|
|
{
|
|
|
|
INT clipBottom, clipTop, clipLeft, clipRight;
|
|
|
|
// Set the major and minor edges ef 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 lihgting the next pixel outside of the clipping
|
|
// area. That's why we add/subtract 7 instead of 8.
|
|
// The right and bottom are exclusive.
|
|
|
|
INT majorMin = (clipRect->GetLeft() << FBITS) - FHALFMASK;
|
|
INT majorMax = ((clipRect->GetRight() - 1) << FBITS) + FHALFMASK;
|
|
INT minorMax = ((clipRect->GetBottom() - 1) << FBITS) + FHALFMASK;
|
|
INT minorMin = (clipRect->GetTop() << FBITS) - FHALFMASK;
|
|
|
|
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:
|
|
*
|
|
* Draws a one-pixe-wide line with a solid color. Calls on the
|
|
* OnePixelLineDDAAntiAliased 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.
|
|
* [IN] antiAliased - TRUE if the line should be antialiased.
|
|
*
|
|
* Return Value:
|
|
*
|
|
* GpStatus - Ok or failure status
|
|
*
|
|
* Created:
|
|
*
|
|
* 03/31/1999 AMatos
|
|
*
|
|
\**************************************************************************/
|
|
|
|
GpStatus
|
|
DrawSolidLineOnePixelAntiAliased(
|
|
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
|
|
|
|
OnePixelLineDDAAntiAliased dda;
|
|
|
|
if(!dda.SetupCommon(point1, point2, drawLast))
|
|
{
|
|
return Ok;
|
|
}
|
|
|
|
// Calculate the length of the line. Since we only use
|
|
// it to determine the width, it shouldn't matter that
|
|
// we convert the deltas from 28.4 before the multiplication.
|
|
|
|
INT d1 = dda.DMajor >> FBITS;
|
|
INT d2 = dda.DMinor >> FBITS;
|
|
|
|
dda.LineLength = (REAL)sqrt((double)(d1*d1 + d2*d2));
|
|
|
|
// Store the color, not premultiplied
|
|
|
|
dda.Color = inColor;
|
|
|
|
// Now handle the different clipping cases
|
|
|
|
if(!clipRect)
|
|
{
|
|
// This is easy, there is no clipping so just draw.
|
|
|
|
if(!dda.SetupAntiAliased())
|
|
{
|
|
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.SetupAntiAliased())
|
|
{
|
|
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;
|
|
|
|
xAdvanceRate = 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];
|
|
}
|
|
|
|
xEnd = xStart + (clipBounds[yOut] - yStart)*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)
|
|
{
|
|
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)*xAdvanceRate;
|
|
}
|
|
|
|
yStart = (REAL)clipBounds[yIn];
|
|
xEnd = xStart + (clipBounds[yOut] - yStart)*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;
|
|
}
|
|
|
|
#endif // AAONEPIXELLINE_SUPPORT
|
|
|
|
#pragma optimize("a", off)
|