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

2139 lines
53 KiB
C++

/**************************************************************************\
*
* Copyright (c) 1998 Microsoft Corporation
*
* Abstract:
*
* Implementation of GpPen class
*
* Revision History:
*
* 12/08/1998 andrewgo
* Initial placeholders.
*
* 01/06/1999 ikkof
* Added the implementation of GpGeometricPen.
\**************************************************************************/
#include "precomp.hpp"
//-------------------------------------------------------------
// GetMajorAndMinorAxis() is defined in PathWidener.cpp.
//-------------------------------------------------------------
extern GpStatus
GetMajorAndMinorAxis(
REAL* majorR,
REAL* minorR,
const GpMatrix* matrix
);
/**************************************************************************\
*
* Function Description:
*
* This converts the given width with the given physical unit to
* the device unit. You cannot use this function when
* unit is WorldUnit.
*
* Arguments:
*
* [IN] width - the width in the given unit.
* [IN] unit - the unit of the width (must not be WorldUnit).
* [IN] dpi - dots per inch of the device.
*
* Return Value:
*
* The device width.
*
* 04/15/1999 ikkof
* Created it.
*
\**************************************************************************/
VOID GpPen::Set(const GpColor& color, REAL penWidth, GpUnit unit)
{
// UnitDisplay is device-dependent and cannot be used for a pen size
ASSERT(unit != UnitDisplay);
if(DevicePen.CustomStartCap)
delete DevicePen.CustomStartCap;
if(DevicePen.CustomEndCap)
delete DevicePen.CustomEndCap;
if(DevicePen.DashArray)
GpFree(DevicePen.DashArray);
if(DevicePen.CompoundArray)
GpFree(DevicePen.CompoundArray);
InitDefaultState(penWidth, unit);
if(Brush)
{
SetColor((GpColor *) &color);
}
else
{
Brush = new GpSolidFill(color);
if(Brush)
{
DevicePen.Brush = Brush->GetDeviceBrush();
}
else
{
SetValid(FALSE);
}
}
UpdateUid();
}
GpPen::GpPen(const GpColor& color, REAL penWidth, GpUnit unit)
{
// UnitDisplay is device-dependent and cannot be used for a pen size
ASSERT(unit != UnitDisplay);
InitDefaultState(penWidth, unit);
Brush = new GpSolidFill(color);
if(Brush)
{
DevicePen.Brush = Brush->GetDeviceBrush();
}
else
{
SetValid(FALSE);
}
}
GpPen::GpPen(const GpBrush* brush, REAL penWidth, GpUnit unit)
{
// UnitDisplay is device-dependent and cannot be used for a pen size
ASSERT(unit != UnitDisplay);
InitDefaultState(penWidth, unit);
Brush = brush->Clone();
if(Brush)
{
DevicePen.Brush = Brush->GetDeviceBrush();
}
else
{
SetValid(FALSE);
}
}
GpPen::GpPen(GpLineTexture* lineTexture, REAL penWidth, GpUnit unit)
{
// UnitDisplay is device-dependent and cannot be used for a pen size
ASSERT(unit != UnitDisplay);
// !!! Needs to be implemented.
// !!! Remember to change GdipCreatePen3 - it currently just returns
// NotImplemented.
RIP(("GpPen with line texture not implemented"));
SetValid(FALSE);
}
VOID GpPen::InitDefaultState(REAL penWidth, GpUnit unit)
{
// UnitDisplay is device-dependent and cannot be used for a pen size
ASSERT(unit != UnitDisplay);
// !! Look at DeviceBrush.Type
DevicePen.Type = PenTypeSolidColor;
DevicePen.Width = penWidth;
DevicePen.Unit = unit;
DevicePen.StartCap = LineCapFlat;
DevicePen.EndCap = LineCapFlat;
DevicePen.Join = LineJoinMiter;
DevicePen.MiterLimit = 10; // PS's default miter limit.
DevicePen.PenAlignment = PenAlignmentCenter;
DevicePen.DashStyle = DashStyleSolid;
DevicePen.DashCap = LineCapFlat;
DevicePen.DashCount = 0;
DevicePen.DashOffset = 0;
DevicePen.DashArray = NULL;
DevicePen.CompoundCount = 0;
DevicePen.CompoundArray = NULL;
DevicePen.CustomStartCap = NULL;
DevicePen.CustomEndCap = NULL;
DevicePen.Xform.Reset();
SetValid(TRUE);
UpdateUid();
}
GpPen::GpPen(const GpPen* pen)
{
GpStatus status = Ok;
if(pen && pen->IsValid())
{
// Copy the base state.
DevicePen = pen->DevicePen;
// Don't copy pointer references to other objects.
Brush = NULL;
DevicePen.Brush = NULL;
DevicePen.DashArray = NULL;
DevicePen.CompoundArray = NULL;
DevicePen.CustomStartCap = NULL;
DevicePen.CustomEndCap = NULL;
// Explicitly clone the pointer references to other objects.
if(pen->Brush)
{
Brush = pen->Brush->Clone();
DevicePen.Brush = Brush->GetDeviceBrush();
}
else
{
status = GenericError;
}
if( status == Ok )
{
if( (pen->DevicePen.DashArray) &&
(DevicePen.DashCount > 0)
)
{
DevicePen.DashArray = (REAL*) GpMalloc(DevicePen.DashCount*sizeof(REAL));
if(DevicePen.DashArray)
{
GpMemcpy(DevicePen.DashArray, pen->DevicePen.DashArray, DevicePen.DashCount*sizeof(REAL));
}
else
{
status = OutOfMemory;
}
}
else
{
// If there is no dash array data, this must be a solid line.
ASSERT(DevicePen.DashStyle == DashStyleSolid);
DevicePen.DashCount = 0;
DevicePen.DashArray = NULL;
}
}
// Set the compound array if necessary.
if( status == Ok )
{
if( (pen->DevicePen.CompoundArray) &&
(DevicePen.CompoundCount > 0)
)
{
DevicePen.CompoundArray = (REAL*) GpMalloc(DevicePen.CompoundCount*sizeof(REAL));
if(DevicePen.CompoundArray)
{
GpMemcpy(DevicePen.CompoundArray, pen->DevicePen.CompoundArray, DevicePen.CompoundCount*sizeof(REAL));
}
else
{
status = OutOfMemory;
}
}
else
{
DevicePen.CompoundCount = 0;
DevicePen.CompoundArray = NULL;
}
}
// Copy the start custom cap.
if( status == Ok )
{
if( DevicePen.StartCap == LineCapCustom )
{
// This could happen with our metafile recorder,
// because saving Custom Line Caps was not implemented.
if (pen->DevicePen.CustomStartCap == NULL)
{
WARNING1("CustomStartCap type with NULL pointer");
DevicePen.StartCap = LineCapFlat;
}
else
{
GpCustomLineCap* clonedCap = static_cast<GpCustomLineCap*>
(pen->DevicePen.CustomStartCap)->Clone();
if(clonedCap)
{
DevicePen.CustomStartCap = clonedCap;
}
else
{
status = OutOfMemory;
}
}
}
}
// Copy the end custom cap.
if( status == Ok )
{
if( DevicePen.EndCap == LineCapCustom )
{
// This could happen with our metafile recorder,
// because saving Custom Line Caps was not implemented.
if (pen->DevicePen.CustomEndCap == NULL)
{
WARNING1("CustomEndCap type with NULL pointer");
DevicePen.EndCap = LineCapFlat;
}
else
{
GpCustomLineCap* clonedCap = static_cast<GpCustomLineCap*>
(pen->DevicePen.CustomEndCap)->Clone();
if(clonedCap)
{
DevicePen.CustomEndCap = clonedCap;
}
else
{
status = OutOfMemory;
}
}
}
}
}
else
{
// Can't make a valid pen from an invalid input pen.
status = GenericError;
}
if(status == Ok)
{
SetValid(TRUE);
}
else
{
// Failed cloning the pen.
// Clean up possible memory allocation so we don't leak even under
// low memory conditions. Note we rely on GpFree and delete handling
// NULL pointers here.
delete Brush;
Brush = NULL; // InitializeDefaultState() does not set
DevicePen.Brush = NULL; // these fields - clear them explicitly.
GpFree(DevicePen.DashArray);
GpFree(DevicePen.CompoundArray);
delete DevicePen.CustomStartCap;
delete DevicePen.CustomEndCap;
// Clean the pen.
InitDefaultState(1.0f, UnitWorld);
// This is not a valid object.
SetValid(FALSE);
}
}
// Clone() return NULL if the cloning fails.
GpPen* GpPen::Clone()
{
GpPen* clonedPen = new GpPen(this);
if(clonedPen && clonedPen->IsValid())
return clonedPen;
else
{
if(clonedPen)
delete clonedPen;
return NULL;
}
}
GpStatus
GpPen::GetMaximumWidth(
REAL* width,
const GpMatrix* matrix) const
{
if(DevicePen.Unit != UnitWorld)
return InvalidParameter;
GpMatrix trans;
if(matrix)
trans = *matrix;
if(!DevicePen.Xform.IsTranslate())
trans.Prepend(DevicePen.Xform);
REAL majorR, minorR;
::GetMajorAndMinorAxis(&majorR, &minorR, &trans);
majorR *= DevicePen.Width;
minorR *= DevicePen.Width;
if(minorR < 1.42f) // This is a litte bit larger than sqrt(2).
{
minorR = 1.42f;
majorR = 1.42f;
}
*width = majorR;
return Ok;
}
/**************************************************************************\
*
* Function Description:
*
* This function takes a join angle and computes the length of the miter
* based on this angle and a given miter length limit.
* This can be scaled by the pen width to give the length of an arbitrary
* pen miter.
*
* In this picture, 2a is the angle of the join. The pen width is w and the
* desired output is the length of the miter join (l).
*
* Note that the line labled w is perpendecular to the inside and outside
* widended lines. Then the formula is derived as follows:
*
* sin(a) = w/l [opposite over hypotenuse on right angled triangle]
* <=> l = w/sin(a)
*
*
* /|\
* /a|a\
* / | \
* / | \
* / |l \
* / | \ <-- right angle
* /--__ | __--\
* / w --|-- w \
* / / \ \
* / / \ \
* outside inside outside
*
* NOTE:
*
* This routine returns the miter length (l) for a pen width w==1.0f.
* The caller is responsible for scaling length by the pen width.
*
* If the length of 1/sin(a) is greater than the miterLimit, the miterLimit
* is returned. (including infinite length joins).
*
* Arguments:
*
* [IN] angle - join angle in radians
* [IN] miterLimit - maximum miter length (not scaled by pen width).
*
* Return Value:
*
* Pen width independent miter length.
*
* 10/02/2000 asecchia
* Created it.
*
\**************************************************************************/
REAL GpPen::ComputeMiterLength(
REAL angle,
REAL miterLimit
)
{
// use the simple miter join formula
// length = (penwidth)/sin(angle/2)
// because we're pen independent, use 1.0 for pen width and rely
// on the caller to scale by the pen width.
REAL length = (REAL)sin(0.5*angle);
// Check for an infinite miter...
if(REALABS(length) < REAL_EPSILON)
{
return miterLimit;
}
length = 1.0f / length;
return min(miterLimit, length);
}
REAL
GpPen::GetMaximumJoinWidth(
REAL sharpestAngle,
const GpMatrix* matrix,
REAL dpiX,
REAL dpiY) const
{
REAL delta;
if ((matrix != NULL) && (DevicePen.IsOnePixelWideSolid(matrix, dpiX)))
{
delta = 0.5;
}
else
{
REAL maximumWidth;
REAL delta0;
REAL scale = 1.0;
switch(DevicePen.PenAlignment)
{
case PenAlignmentCenter:
scale = 0.5f;
break;
// use 1.0 for the inset pen. If the path is open, we render with a
// center pen.
// NOTE: a scale of 0.0 is sufficient for all inset pen rendering
// provided all subpaths are closed. In the widener, we detect the
// open subpaths and render them with a center pen. To accommodate
// this, we increase the scale here. Theoretically we could use
// scale = 0.5f for the inset pen (same as center pen), but this
// bounds is an overestimate anyway and being wrong by one pixel too
// small is way worse (crash) than being wrong and too big.
case PenAlignmentInset:
scale = 1.0f;
break;
}
if(GetMaximumWidth(&maximumWidth, matrix) == Ok)
{
delta0 = maximumWidth;
}
else
{
maximumWidth = ::GetDeviceWidth(
DevicePen.Width,
DevicePen.Unit,
dpiX);
delta0 = maximumWidth;
}
if(DevicePen.Join == LineJoinMiter ||
DevicePen.Join == LineJoinMiterClipped)
{
REAL miterLimit = DevicePen.MiterLimit;
delta = delta0*miterLimit;
if(delta > 20)
{
delta = ComputeMiterLength(
sharpestAngle,
miterLimit
);
// scale by the pen width.
delta *= delta0;
}
}
else
{
delta = delta0;
}
delta *= scale;
}
return delta;
}
REAL
GpPen::GetMaximumCapWidth(
const GpMatrix* matrix,
REAL dpiX,
REAL dpiY) const
{
REAL maximumWidth;
REAL delta0;
if(GetMaximumWidth(&maximumWidth, matrix) == Ok)
{
delta0 = maximumWidth;
}
else
{
maximumWidth = ::GetDeviceWidth(
DevicePen.Width,
DevicePen.Unit,
dpiX);
delta0 = maximumWidth;
}
REAL delta = delta0;
GpLineCap startCap = DevicePen.StartCap;
GpLineCap endCap = DevicePen.EndCap;
REAL delta1;
GpCustomLineCap* customCap = NULL;
if(startCap == LineCapCustom && DevicePen.CustomStartCap)
{
customCap = static_cast<GpCustomLineCap *> (DevicePen.CustomStartCap);
delta1 = customCap->GetRadius(delta0, 1.0f);
}
else
{
if(!(startCap & LineCapAnchorMask))
delta1 = 0.5f*delta0;
else
delta1 = 2.0f*(delta0 + 1);
}
if(delta < delta1)
delta = delta1;
if(endCap == LineCapCustom && DevicePen.CustomEndCap)
{
customCap = static_cast<GpCustomLineCap *> (DevicePen.CustomEndCap);
delta1 = customCap->GetRadius(delta0, 1.0f);
}
else
{
if(!(endCap & LineCapAnchorMask))
delta1 = 0.5f*delta0;
else
delta1 = 2.0f*(delta0 + 2);
}
if(delta < delta1)
delta = delta1;
return delta;
}
VOID
GpPen::SetDashCap(GpDashCap dashCap)
{
// Note: Internally we use a GpLineCap type to store the dash cap type.
// So we need to convert between GpLineCap and GpDashCap.
// However, we should change the internal usage to GpDashCap in v2.
// - JBronsk
GpLineCap lineCap = LineCapFlat;
switch (dashCap)
{
case DashCapRound:
lineCap = LineCapRound;
break;
case DashCapTriangle:
lineCap = LineCapTriangle;
break;
// all others map to LineCapFlat
}
GpStatus status = SetDashStyleWithDashCap(DevicePen.DashStyle, lineCap);
if(status == Ok)
{
DevicePen.DashCap = lineCap;
}
}
GpStatus
GpPen::SetDashStyle(
GpDashStyle dashStyle
)
{
return SetDashStyleWithDashCap(dashStyle, DevicePen.DashCap);
}
GpStatus
GpPen::SetDashStyleWithDashCap(
GpDashStyle dashStyle,
GpLineCap dashCap
)
{
GpStatus status = Ok;
REAL style[6];
INT count;
switch(dashStyle)
{
case DashStyleSolid:
count = 0;
break;
case DashStyleDash:
count = 2;
style[0] = 3; // a dash
style[1] = 1; // a space
break;
case DashStyleDot:
count = 2;
style[0] = 1; // a dot
style[1] = 1; // a space
break;
case DashStyleDashDot:
count = 4;
style[0] = 3; // a dash
style[1] = 1; // a space
style[2] = 1; // a dot
style[3] = 1; // a space
break;
case DashStyleDashDotDot:
count = 6;
style[0] = 3; // a dash
style[1] = 1; // a space
style[2] = 1; // a dot
style[3] = 1; // a space
style[4] = 1; // a dot
style[5] = 1; // a space
break;
case DashStyleCustom:
// We assume that the custom dash has been set at the API.
// The remaining code in this routine is for initializing an appropriate
// dash array, which we already have in this case, so we're done.
DevicePen.DashStyle = dashStyle;
return Ok;
default:
// The dash style must be one of the predefined ones.
status = InvalidParameter;
}
if(status != Ok)
{
return status;
}
if(DevicePen.DashCount < count)
{
REAL* newArray = (REAL*) GpMalloc(count*sizeof(REAL));
if(newArray)
{
GpFree(DevicePen.DashArray);
DevicePen.DashArray = newArray;
}
else
{
status = OutOfMemory;
}
}
if(status == Ok)
{
// initialize the DashArray.
GpMemcpy(DevicePen.DashArray, &style[0], count*sizeof(REAL));
DevicePen.DashStyle = dashStyle;
DevicePen.DashCount = count;
UpdateUid();
}
return status;
}
GpStatus
GpPen::SetDashArray(
const REAL* dashArray,
INT count
)
{
ASSERT(dashArray && count > 0);
// Make sure the all elements are positive.
INT i = 0;
GpStatus status = Ok;
while(status == Ok && i < count)
{
if(dashArray[i++] <= 0)
status = InvalidParameter;
}
if(status != Ok)
return status;
REAL* newArray = (REAL*) GpRealloc(DevicePen.DashArray, count*sizeof(REAL));
if(!newArray)
return OutOfMemory;
GpMemcpy(newArray, dashArray, count*sizeof(REAL));
DevicePen.DashStyle = DashStyleCustom;
DevicePen.DashArray = newArray;
DevicePen.DashCount = count;
UpdateUid();
return Ok;
}
GpStatus
GpPen::GetDashArray(
REAL* dashArray,
INT count
) const
{
ASSERT(dashArray != NULL && count <= DevicePen.DashCount);
GpStatus status = Ok;
if(dashArray == NULL || count > DevicePen.DashCount)
return InvalidParameter;
if(DevicePen.DashArray)
GpMemcpy(dashArray, DevicePen.DashArray, count*sizeof(REAL));
else
status = OutOfMemory;
return status;
}
GpStatus
GpPen::SetCompoundArray(
const REAL* compoundArray,
INT count
)
{
// count must be a positive even number.
if(compoundArray == NULL || count <= 0 || (count & 0x01))
{
return InvalidParameter;
}
// count is 2 or more here...
// Compound Inset pens aren't implemented yet.
// The code for correctly handling minimum width compound sub lines
// is missing.
if(DevicePen.PenAlignment == PenAlignmentInset)
{
return NotImplemented;
}
// Make sure the all elements are monitonically increasing
// and its values are between 0 and 1.
GpStatus status = Ok;
REAL lastValue, nextValue;
lastValue = compoundArray[0];
if(lastValue < 0.0f || lastValue > 1.0f)
status = InvalidParameter;
INT i = 1;
while(status == Ok && i < count)
{
nextValue = compoundArray[i++];
if(nextValue < lastValue || nextValue > 1.0f)
status = InvalidParameter;
lastValue = nextValue;
}
if(status != Ok)
return status;
REAL* newArray = (REAL*) GpRealloc(DevicePen.CompoundArray, count*sizeof(REAL));
if(!newArray)
return OutOfMemory;
GpMemcpy(newArray, compoundArray, count*sizeof(REAL));
DevicePen.CompoundArray = newArray;
DevicePen.CompoundCount = count;
UpdateUid();
return Ok;
}
GpStatus
GpPen::GetCompoundArray(
REAL* compoundArray,
INT count
)
{
ASSERT(compoundArray != NULL && count <= DevicePen.CompoundCount);
if(compoundArray == NULL || count > DevicePen.CompoundCount)
return InvalidParameter;
if(DevicePen.CompoundArray && count > 0)
GpMemcpy(compoundArray, DevicePen.CompoundArray, count*sizeof(REAL));
return Ok;
}
GpStatus
GpPen::SetCustomStartCap(
const GpCustomLineCap* customCap
)
{
if(DevicePen.CustomStartCap)
delete DevicePen.CustomStartCap;
// Reset the standard start cap to the default one.
DevicePen.CustomStartCap = NULL;
DevicePen.StartCap = LineCapFlat;
if(customCap)
{
DevicePen.CustomStartCap = customCap->Clone();
DevicePen.StartCap = LineCapCustom;
}
UpdateUid();
return Ok;
}
GpStatus
GpPen::GetCustomStartCap(
GpCustomLineCap** customCap
)
{
if(DevicePen.CustomStartCap)
*customCap = static_cast<GpCustomLineCap*>
(DevicePen.CustomStartCap)->Clone();
else
*customCap = NULL;
return Ok;
}
GpStatus
GpPen::SetCustomEndCap(
const GpCustomLineCap* customCap
)
{
if(DevicePen.CustomEndCap)
delete DevicePen.CustomEndCap;
// Reset the standard start cap to the default one.
DevicePen.CustomEndCap = NULL;
DevicePen.EndCap = LineCapFlat;
if(customCap)
{
DevicePen.CustomEndCap = customCap->Clone();
DevicePen.EndCap = LineCapCustom;
}
UpdateUid();
return Ok;
}
GpStatus
GpPen::GetCustomEndCap(
GpCustomLineCap** customCap
)
{
if(DevicePen.CustomEndCap)
*customCap = static_cast<GpCustomLineCap*>
(DevicePen.CustomEndCap)->Clone();
else
*customCap = NULL;
return Ok;
}
GpStatus
GpPen::MultiplyTransform(const GpMatrix& matrix,
GpMatrixOrder order)
{
GpStatus status = Ok;
if (matrix.IsInvertible())
{
if (order == MatrixOrderPrepend)
{
DevicePen.Xform.Prepend(matrix);
}
else
{
DevicePen.Xform.Append(matrix);
}
}
else
status = InvalidParameter;
return status;
}
/**************************************************************************\
*
* Function Description:
*
* Answer true if the two pen instances are equivalent, meaning they
* are indistinguishable when rendering.
*
* Arguments:
*
* [IN] pen - pen to compare this against
* Return Value:
*
* TRUE if equivalent.
*
* Created:
*
* 6/14/1999 peterost
*
\**************************************************************************/
BOOL
GpPen::IsEqual(
const GpPen * pen
)
const
{
ASSERT(pen != NULL);
if (pen == this)
return TRUE;
BOOL isEqual = TRUE;
if (DevicePen.IsEqual(&pen->DevicePen) &&
DevicePen.DashStyle == pen->DevicePen.DashStyle &&
DevicePen.CompoundCount == pen->DevicePen.CompoundCount &&
Brush->IsEqual(pen->Brush) &&
DevicePen.Xform.IsEqual(&pen->DevicePen.Xform))
{
// We need to check the equality further if the dash style
// is not a solid line.
if (DevicePen.DashStyle != DashStyleSolid)
{
if(DevicePen.DashStyle != DashStyleCustom)
{
// A case of the preset dash pattern.
// Check only for the offset difference.
if(DevicePen.DashOffset != pen->DevicePen.DashOffset)
isEqual = FALSE;
}
else
{
if (DevicePen.DashCount == pen->DevicePen.DashCount &&
DevicePen.DashOffset == pen->DevicePen.DashOffset &&
DevicePen.DashArray != NULL &&
pen->DevicePen.DashArray != NULL)
{
INT i = 0;
while(i < DevicePen.DashCount && isEqual)
{
if (DevicePen.DashArray[i] != pen->DevicePen.DashArray[i])
{
isEqual = FALSE;
}
i++;
}
}
else
{
isEqual = FALSE;
}
}
}
// Check for the compound lines.
if(isEqual && DevicePen.CompoundCount > 0)
{
if(DevicePen.CompoundArray && pen->DevicePen.CompoundArray)
{
INT j = 0;
while(j < DevicePen.CompoundCount && isEqual)
{
if(DevicePen.CompoundArray[j] != pen->DevicePen.CompoundArray[j])
{
isEqual = FALSE;
}
j++;
}
}
else
{
isEqual = FALSE;
}
}
}
else
{
isEqual = FALSE;
}
return isEqual;
}
// For GetData and SetData methods
#define GDIP_PENFLAGS_TRANSFORM 0x00000001
#define GDIP_PENFLAGS_STARTCAP 0x00000002
#define GDIP_PENFLAGS_ENDCAP 0x00000004
#define GDIP_PENFLAGS_JOIN 0x00000008
#define GDIP_PENFLAGS_MITERLIMIT 0x00000010
#define GDIP_PENFLAGS_DASHSTYLE 0x00000020
#define GDIP_PENFLAGS_DASHCAP 0x00000040
#define GDIP_PENFLAGS_DASHOFFSET 0x00000080
#define GDIP_PENFLAGS_DASHARRAY 0x00000100
#define GDIP_PENFLAGS_NONCENTER 0x00000200
#define GDIP_PENFLAGS_COMPOUNDARRAY 0x00000400
#define GDIP_PENFLAGS_CUSTOMSTARTCAP 0x00000800
#define GDIP_PENFLAGS_CUSTOMENDCAP 0x00001000
class PenData : public ObjectTypeData
{
public:
INT32 Flags;
INT32 Unit;
REAL Width;
};
/**************************************************************************\
*
* Function Description:
*
* Get the pen data.
*
* Arguments:
*
* [IN] dataBuffer - fill this buffer with the data
* [IN/OUT] size - IN - size of buffer; OUT - number bytes written
*
* Return Value:
*
* GpStatus - Ok or error code
*
* Created:
*
* 9/13/1999 DCurtis
*
\**************************************************************************/
GpStatus
GpPen::GetData(
IStream * stream
) const
{
if (Brush == NULL)
{
WARNING(("Brush is NULL"));
return Ok;
}
ASSERT (stream != NULL);
INT flags = 0;
if (!DevicePen.Xform.IsIdentity())
{
flags |= GDIP_PENFLAGS_TRANSFORM;
}
INT customStartCapSize = 0;
INT customEndCapSize = 0;
if (DevicePen.StartCap != LineCapFlat)
{
if (DevicePen.StartCap == LineCapCustom)
{
if ((DevicePen.CustomStartCap != NULL) &&
DevicePen.CustomStartCap->IsValid() &&
((customStartCapSize = DevicePen.CustomStartCap->GetDataSize()) > 0))
{
flags |= GDIP_PENFLAGS_STARTCAP | GDIP_PENFLAGS_CUSTOMSTARTCAP;
}
}
else
{
flags |= GDIP_PENFLAGS_STARTCAP;
}
}
if (DevicePen.EndCap != LineCapFlat)
{
if (DevicePen.EndCap == LineCapCustom)
{
if ((DevicePen.CustomEndCap != NULL) &&
DevicePen.CustomEndCap->IsValid() &&
((customEndCapSize = DevicePen.CustomEndCap->GetDataSize()) > 0))
{
flags |= GDIP_PENFLAGS_ENDCAP | GDIP_PENFLAGS_CUSTOMENDCAP;
}
}
else
{
flags |= GDIP_PENFLAGS_ENDCAP;
}
}
if (DevicePen.Join != LineJoinMiter)
{
flags |= GDIP_PENFLAGS_JOIN;
}
if (DevicePen.MiterLimit != 10)
{
flags |= GDIP_PENFLAGS_MITERLIMIT;
}
// DashStyleCustom is handled by hasDashArray
if ((DevicePen.DashStyle != DashStyleSolid) && (DevicePen.DashStyle != DashStyleCustom))
{
flags |= GDIP_PENFLAGS_DASHSTYLE;
}
if (DevicePen.DashCap != LineCapFlat)
{
flags |= GDIP_PENFLAGS_DASHCAP;
}
if (DevicePen.DashOffset != 0)
{
flags |= GDIP_PENFLAGS_DASHOFFSET;
}
if ((DevicePen.DashStyle == DashStyleCustom) &&
(DevicePen.DashArray != NULL) &&
(DevicePen.DashCount > 0))
{
flags |= GDIP_PENFLAGS_DASHARRAY;
}
if (DevicePen.PenAlignment != PenAlignmentCenter)
{
flags |= GDIP_PENFLAGS_NONCENTER;
}
if ((DevicePen.CompoundArray != NULL) && (DevicePen.CompoundCount > 0))
{
flags |= GDIP_PENFLAGS_COMPOUNDARRAY;
}
PenData penData;
penData.Type = DevicePen.Type;
penData.Flags = flags;
penData.Unit = DevicePen.Unit;
penData.Width = DevicePen.Width;
stream->Write(&penData, sizeof(penData), NULL);
if (flags & GDIP_PENFLAGS_TRANSFORM)
{
DevicePen.Xform.WriteMatrix(stream);
}
if (flags & GDIP_PENFLAGS_STARTCAP)
{
stream->Write(&DevicePen.StartCap, sizeof(INT32), NULL);
}
if (flags & GDIP_PENFLAGS_ENDCAP)
{
stream->Write(&DevicePen.EndCap, sizeof(INT32), NULL);
}
if (flags & GDIP_PENFLAGS_JOIN)
{
stream->Write(&DevicePen.Join, sizeof(INT32), NULL);
}
if (flags & GDIP_PENFLAGS_MITERLIMIT)
{
stream->Write(&DevicePen.MiterLimit, sizeof(REAL), NULL);
}
if (flags & GDIP_PENFLAGS_DASHSTYLE)
{
stream->Write(&DevicePen.DashStyle, sizeof(INT32), NULL);
}
if (flags & GDIP_PENFLAGS_DASHCAP)
{
stream->Write(&DevicePen.DashCap, sizeof(INT32), NULL);
}
if (flags & GDIP_PENFLAGS_DASHOFFSET)
{
stream->Write(&DevicePen.DashOffset, sizeof(REAL), NULL);
}
if (flags & GDIP_PENFLAGS_DASHARRAY)
{
stream->Write(&DevicePen.DashCount, sizeof(INT32), NULL);
stream->Write(DevicePen.DashArray, DevicePen.DashCount * sizeof(REAL), NULL);
}
if (flags & GDIP_PENFLAGS_NONCENTER)
{
stream->Write(&DevicePen.PenAlignment, sizeof(INT32), NULL);
}
if (flags & GDIP_PENFLAGS_COMPOUNDARRAY)
{
stream->Write(&DevicePen.CompoundCount, sizeof(INT32), NULL);
stream->Write(DevicePen.CompoundArray, DevicePen.CompoundCount * sizeof(REAL), NULL);
}
GpStatus status;
if (flags & GDIP_PENFLAGS_CUSTOMSTARTCAP)
{
stream->Write(&customStartCapSize, sizeof(INT32), NULL);
if ((status = DevicePen.CustomStartCap->GetData(stream)) != Ok)
{
return status;
}
}
if (flags & GDIP_PENFLAGS_CUSTOMENDCAP)
{
stream->Write(&customEndCapSize, sizeof(INT32), NULL);
if ((status = DevicePen.CustomEndCap->GetData(stream)) != Ok)
{
return status;
}
}
status = Brush->GetData(stream);
return status;
}
UINT
GpPen::GetDataSize() const
{
if (Brush == NULL)
{
WARNING(("Brush is NULL"));
return 0;
}
UINT dataSize = sizeof(PenData);
if (!DevicePen.Xform.IsIdentity())
{
dataSize += GDIP_MATRIX_SIZE;
}
INT customStartCapSize = 0;
INT customEndCapSize = 0;
if (DevicePen.StartCap != LineCapFlat)
{
if (DevicePen.StartCap == LineCapCustom)
{
if ((DevicePen.CustomStartCap != NULL) &&
DevicePen.CustomStartCap->IsValid() &&
((customStartCapSize = DevicePen.CustomStartCap->GetDataSize()) > 0))
{
// startcap + sizeof custom cap + custom cap
dataSize += sizeof(INT32) + sizeof(INT32) + customStartCapSize;
}
}
else
{
dataSize += sizeof(INT32);
}
}
if (DevicePen.EndCap != LineCapFlat)
{
if (DevicePen.EndCap == LineCapCustom)
{
if ((DevicePen.CustomEndCap != NULL) &&
DevicePen.CustomEndCap->IsValid() &&
((customEndCapSize = DevicePen.CustomEndCap->GetDataSize()) > 0))
{
// endcap + sizeof custom cap + custom cap
dataSize += sizeof(INT32) + sizeof(INT32) + customEndCapSize;
}
}
else
{
dataSize += sizeof(INT32);
}
}
if (DevicePen.Join != LineJoinMiter)
{
dataSize += sizeof(INT32);
}
if (DevicePen.MiterLimit != 10)
{
dataSize += sizeof(REAL);
}
// DashStyleCustom is handled by hasDashArray
if ((DevicePen.DashStyle != DashStyleSolid) && (DevicePen.DashStyle != DashStyleCustom))
{
dataSize += sizeof(INT32);
}
if (DevicePen.DashCap != LineCapFlat)
{
dataSize += sizeof(INT32);
}
if (DevicePen.DashOffset != 0)
{
dataSize += sizeof(REAL);
}
if ((DevicePen.DashStyle == DashStyleCustom) &&
(DevicePen.DashArray != NULL) &&
(DevicePen.DashCount > 0))
{
dataSize += sizeof(INT32) + (DevicePen.DashCount * sizeof(REAL));
}
if (DevicePen.PenAlignment != PenAlignmentCenter)
{
dataSize += sizeof(INT32);
}
if ((DevicePen.CompoundArray != NULL) && (DevicePen.CompoundCount > 0))
{
dataSize += sizeof(INT32) + (DevicePen.CompoundCount * sizeof(REAL));
}
dataSize += Brush->GetDataSize();
return dataSize;
}
/**************************************************************************\
*
* Function Description:
*
* Read the pen object from memory.
*
* Arguments:
*
* [IN] dataBuffer - the data that was read from the stream
* [IN] size - the size of the data
*
* Return Value:
*
* GpStatus - Ok or failure status
*
* Created:
*
* 4/26/1999 DCurtis
*
\**************************************************************************/
GpStatus
GpPen::SetData(
const BYTE * dataBuffer,
UINT size
)
{
if (dataBuffer == NULL)
{
WARNING(("dataBuffer is NULL"));
return InvalidParameter;
}
if (size < sizeof(PenData))
{
WARNING(("size too small"));
return InvalidParameter;
}
const PenData * penData = reinterpret_cast<const PenData *>(dataBuffer);
if (!penData->MajorVersionMatches())
{
WARNING(("Version number mismatch"));
return InvalidParameter;
}
InitDefaultState(penData->Width, static_cast<GpUnit>(penData->Unit));
dataBuffer += sizeof(PenData);
size -= sizeof(PenData);
if (penData->Flags & GDIP_PENFLAGS_TRANSFORM)
{
if (size < GDIP_MATRIX_SIZE)
{
WARNING(("size too small"));
goto ErrorExit;
}
DevicePen.Xform.SetMatrix((REAL *)dataBuffer);
dataBuffer += GDIP_MATRIX_SIZE;
size -= GDIP_MATRIX_SIZE;
}
if (penData->Flags & GDIP_PENFLAGS_STARTCAP)
{
if (size < sizeof(INT32))
{
WARNING(("size too small"));
goto ErrorExit;
}
DevicePen.StartCap = (GpLineCap) ((INT32 *)dataBuffer)[0];
dataBuffer += sizeof(INT32);
size -= sizeof(INT32);
}
if (penData->Flags & GDIP_PENFLAGS_ENDCAP)
{
if (size < sizeof(INT32))
{
WARNING(("size too small"));
goto ErrorExit;
}
DevicePen.EndCap = (GpLineCap) ((INT32 *)dataBuffer)[0];
dataBuffer += sizeof(INT32);
size -= sizeof(INT32);
}
if (penData->Flags & GDIP_PENFLAGS_JOIN)
{
if (size < sizeof(INT32))
{
WARNING(("size too small"));
goto ErrorExit;
}
DevicePen.Join = (GpLineJoin) ((INT32 *)dataBuffer)[0];
dataBuffer += sizeof(INT32);
size -= sizeof(INT32);
}
if (penData->Flags & GDIP_PENFLAGS_MITERLIMIT)
{
if (size < sizeof(REAL))
{
WARNING(("size too small"));
goto ErrorExit;
}
DevicePen.MiterLimit = ((REAL *)dataBuffer)[0];
dataBuffer += sizeof(REAL);
size -= sizeof(REAL);
}
if (penData->Flags & GDIP_PENFLAGS_DASHSTYLE)
{
if (size < sizeof(INT32))
{
WARNING(("size too small"));
goto ErrorExit;
}
this->SetDashStyle((GpDashStyle)((INT32 *)dataBuffer)[0]);
dataBuffer += sizeof(INT32);
size -= sizeof(INT32);
}
if (penData->Flags & GDIP_PENFLAGS_DASHCAP)
{
if (size < sizeof(INT32))
{
WARNING(("size too small"));
goto ErrorExit;
}
DevicePen.DashCap = (GpLineCap) ((INT32 *)dataBuffer)[0];
dataBuffer += sizeof(INT32);
size -= sizeof(INT32);
}
if (penData->Flags & GDIP_PENFLAGS_DASHOFFSET)
{
if (size < sizeof(REAL))
{
WARNING(("size too small"));
goto ErrorExit;
}
DevicePen.DashOffset = ((REAL *)dataBuffer)[0];
dataBuffer += sizeof(REAL);
size -= sizeof(REAL);
}
if (penData->Flags & GDIP_PENFLAGS_DASHARRAY)
{
if (size < sizeof(INT32))
{
WARNING(("size too small"));
goto ErrorExit;
}
INT count = ((INT32 *)dataBuffer)[0];
dataBuffer += sizeof(INT32);
size -= sizeof(INT32);
if (size < (count * sizeof(REAL)))
{
WARNING(("size too small"));
goto ErrorExit;
}
this->SetDashArray((REAL *)dataBuffer, count);
dataBuffer += (count * sizeof(REAL));
size -= (count * sizeof(REAL));
}
if (penData->Flags & GDIP_PENFLAGS_NONCENTER)
{
if (size < sizeof(INT32))
{
WARNING(("size too small"));
goto ErrorExit;
}
DevicePen.PenAlignment = (GpPenAlignment) ((INT32 *)dataBuffer)[0];
dataBuffer += sizeof(INT32);
size -= sizeof(INT32);
}
if (penData->Flags & GDIP_PENFLAGS_COMPOUNDARRAY)
{
if (size < sizeof(INT32))
{
WARNING(("size too small"));
goto ErrorExit;
}
INT count = ((INT32 *)dataBuffer)[0];
dataBuffer += sizeof(INT32);
size -= sizeof(INT32);
if (size < (count * sizeof(REAL)))
{
WARNING(("size too small"));
goto ErrorExit;
}
this->SetCompoundArray((REAL *)dataBuffer, count);
dataBuffer += (count * sizeof(REAL));
size -= (count * sizeof(REAL));
}
if (penData->Flags & GDIP_PENFLAGS_CUSTOMSTARTCAP)
{
if (size < sizeof(INT32))
{
WARNING(("size too small"));
goto ErrorExit;
}
UINT capSize = ((INT32 *)dataBuffer)[0];
dataBuffer += sizeof(INT32);
size -= sizeof(INT32);
if ((size < capSize) || (capSize < sizeof(ObjectTypeData)))
{
WARNING(("size too small"));
goto ErrorExit;
}
ASSERT(DevicePen.CustomStartCap == NULL);
DevicePen.CustomStartCap = (GpCustomLineCap *)GpObject::Factory(ObjectTypeCustomLineCap, (const ObjectData *)dataBuffer, capSize);
if ((DevicePen.CustomStartCap == NULL) ||
(DevicePen.CustomStartCap->SetData(dataBuffer, capSize) != Ok) ||
!DevicePen.CustomStartCap->IsValid())
{
WARNING(("Failure getting CustomStartCap"));
goto ErrorExit;
}
dataBuffer += capSize;
size -= capSize;
}
if (penData->Flags & GDIP_PENFLAGS_CUSTOMENDCAP)
{
if (size < sizeof(INT32))
{
WARNING(("size too small"));
goto ErrorExit;
}
UINT capSize = ((INT32 *)dataBuffer)[0];
dataBuffer += sizeof(INT32);
size -= sizeof(INT32);
if ((size < capSize) || (capSize < sizeof(ObjectTypeData)))
{
WARNING(("size too small"));
goto ErrorExit;
}
ASSERT(DevicePen.CustomEndCap == NULL);
DevicePen.CustomEndCap = (GpCustomLineCap *)GpObject::Factory(ObjectTypeCustomLineCap, (const ObjectData *)dataBuffer, capSize);
if ((DevicePen.CustomEndCap == NULL) ||
(DevicePen.CustomEndCap->SetData(dataBuffer, capSize) != Ok) ||
!DevicePen.CustomEndCap->IsValid())
{
WARNING(("Failure getting CustomEndCap"));
goto ErrorExit;
}
dataBuffer += capSize;
size -= capSize;
}
if (Brush != NULL)
{
Brush->Dispose();
Brush = NULL;
}
if (size >= sizeof(ObjectTypeData))
{
Brush = (GpBrush *)GpObject::Factory(ObjectTypeBrush, (const ObjectData *)dataBuffer, size);
if (Brush != NULL)
{
if ((Brush->SetData(dataBuffer, size) == Ok) && Brush->IsValid())
{
DevicePen.Brush = Brush->GetDeviceBrush();
SetValid(TRUE);
UpdateUid();
return Ok;
}
Brush->Dispose();
Brush = NULL;
}
}
WARNING(("Failure getting brush"));
ErrorExit:
SetValid(FALSE);
return GenericError;
}
GpStatus
GpPen::ColorAdjust(
GpRecolor * recolor,
ColorAdjustType type
)
{
ASSERT(recolor != NULL);
if (type == ColorAdjustTypeDefault)
{
type = ColorAdjustTypePen;
}
if (Brush != NULL)
{
Brush->ColorAdjust(recolor, type);
}
return Ok;
}
GpStatus
GpPen::GetColor(
ARGB *argb
) const
{
if (Brush->GetBrushType() == BrushTypeSolidColor)
{
GpSolidFill * solidBrush = (GpSolidFill *) Brush;
*argb = solidBrush->GetColor().GetValue();
return Ok;
}
return InvalidParameter;
}
GpStatus
GpPen::SetColor(
GpColor * color
)
{
if (Brush->GetBrushType() == BrushTypeSolidColor)
{
GpSolidFill * solidBrush = (GpSolidFill *) Brush;
if (solidBrush->GetColor().GetValue() == color->GetValue())
{
return Ok;
}
// !!! bhouse why do we allocate another brush just to change the
// pen's color !!!!
}
GpSolidFill *newBrush = new GpSolidFill(*color);
if (newBrush != NULL)
{
if (newBrush->IsValid())
{
delete Brush;
Brush = newBrush;
DevicePen.Brush = Brush->GetDeviceBrush();
UpdateUid();
return Ok;
}
delete newBrush;
}
return GenericError;
}
GpStatus
GpPen::SetBrush(
GpBrush * brush
)
{
// Don't set the brush if it is the same color as the current one,
// because that makes metafiles unnecessarily large.
if ((Brush->GetBrushType() == BrushTypeSolidColor) &&
(brush->GetBrushType() == BrushTypeSolidColor))
{
GpSolidFill * solidBrush = (GpSolidFill *) Brush;
GpSolidFill * newSolidBrush = (GpSolidFill *) brush;
if(solidBrush->GetColor().GetValue() ==
newSolidBrush->GetColor().GetValue())
{
return Ok;
}
}
GpBrush * newBrush = brush->Clone();
if (newBrush != NULL)
{
if (newBrush->IsValid())
{
delete Brush;
Brush = newBrush;
DevicePen.Brush = Brush->GetDeviceBrush();
UpdateUid();
return Ok;
}
delete newBrush;
}
return GenericError;
}
GpPenType
GpPen::GetPenType(
)
{
GpPenType type = PenTypeUnknown;
if(Brush)
{
switch(Brush->GetBrushType())
{
case BrushTypeSolidColor:
type = PenTypeSolidColor;
break;
case BrushTypeHatchFill:
type = PenTypeHatchFill;
break;
case BrushTypeTextureFill:
type = PenTypeTextureFill;
break;
/*
case BrushRectGrad:
type = PenFillRectGrad;
break;
case BrushRadialGrad:
type = PenFillRadialGrad;
break;
case BrushTriangleGrad:
type = PenFillTriangleGrad;
break;
*/
case BrushTypePathGradient:
type = PenTypePathGradient;
break;
case BrushTypeLinearGradient:
type = PenTypeLinearGradient;
break;
default:
break;
}
}
// We must implement LineTexture case.
return type;
}
/**************************************************************************\
*
* Function Description:
*
* Adjust the dash array for dash caps if present.
*
* Note that unlike line caps, dash caps do not extend the length
* of the subpath, they are inset. So we shorten the dash segments
* that draw a line and lengthen the dash segments that are spaces
* by a factor of 2x the dash unit in order to leave space for the
* caps that will be added by the widener.
*
* This fixes Whistler bug #126476.
*
* Arguments:
*
* [IN] dashCap - dash cap type
* [IN] dashUnit - dash size - typically the pen width
* [IN/OUT] dashArray - array containing the dash pattern that is adjusted.
* [IN] dashCount - count of elements in the dash array
*
* Return Value:
*
* None.
*
* History:
*
* 9/27/2000 jbronsk
* Created.
*
* 12/06/2000 aaronlie
* Moved from GpPath
*
\**************************************************************************/
VOID
GpPen::AdjustDashArrayForCaps(
REAL dashUnit,
REAL *dashArray,
INT dashCount
) const
{
REAL adjustmentLength = 2.0f *
GetDashCapInsetLength(dashUnit);
if (adjustmentLength > 0.0f)
{
const REAL minimumDashValue = dashUnit * 0.001f; // a small number
for (int i = 0; i < dashCount; i++)
{
if (i & 0x1) // index is odd - so this is a space
{
// lengthen the spaces
dashArray[i] += adjustmentLength;
}
else // index is even - so this is a line
{
// shorten the lines
dashArray[i] -= adjustmentLength;
// check if we have made the dash too small
// (as in the case of 'dots')
if (dashArray[i] < minimumDashValue)
{
dashArray[i] = minimumDashValue;
}
}
}
}
}
/**************************************************************************\
*
* Function Description:
*
* Computes the length of the inset required to accomodate a particular
* dash cap type, since dash caps are contained within the dash length.
*
* Arguments:
*
* [IN] dashUnit - pen width
*
* Return Value:
*
* The amount that a dash needs to be inset on each end in order to
* accomodate any dash caps.
*
* History:
*
* 9/27/2000 jbronsk
* Created.
*
* 12/06/2000 aaronlie
* Moved from GpPath
*
\**************************************************************************/
REAL
GpPen::GetDashCapInsetLength(
REAL dashUnit
) const
{
REAL insetLength = 0.0f;
// dash caps can only be flat, round, or triangle
switch(GetDashCap())
{
case LineCapFlat:
insetLength = 0.0f;
break;
case LineCapRound:
case LineCapTriangle:
insetLength = dashUnit * 0.5f;
break;
}
return insetLength;
}
/**************************************************************************\
*
* Function Description:
*
* Does a quick check to see if the path can be rendered as a solid
* pixel wide line.
*
* Arguments:
*
* [IN] cappedDpiX - the resolution of the x direction
* [IN] worldToDevice - World transform
*
* Return Value:
*
* TRUE if okay to be rendered as a one pixel line
*
* History:
*
* 12/17/1999 ikkof
* Created it.
*
\**************************************************************************/
BOOL
DpPen::IsOnePixelWideSolid(
const GpMatrix *worldToDevice,
REAL dpiX
) const
{
return this->IsSimple() && this->IsOnePixelWide(worldToDevice, dpiX);
}
/**************************************************************************\
*
* Function Description:
*
* Does a quick check to see if the path can be rendered as a one
* pixel wide line.
*
* Arguments:
*
* [IN] cappedDpiX - the resolution of the x direction
* [IN] worldToDevice - World transform
*
* Return Value:
*
* TRUE if okay to be rendered as a one pixel line
*
* History:
*
* 10/6/2000 - peterost - factored out fron IsOnePixelWideSolid
*
\**************************************************************************/
BOOL
DpPen::IsOnePixelWide(
const GpMatrix *worldToDevice,
REAL dpiX
) const
{
BOOL useOnePixelPath = FALSE;
const REAL minimumPenWidth = 1.5f;
// !!![andrewgo] This determination of a single pixel wide line is
// unbelievably expensive
// !!![andrewgo] This width check should be done simply using
// the world-to-device transform! It would be
// faster and simpler!
REAL width = this->Width;
GpUnit unit = this->Unit;
if(unit == UnitWorld)
{
if(worldToDevice == NULL || worldToDevice->IsTranslate())
{
if(width <= minimumPenWidth)
useOnePixelPath = TRUE;
}
else if(worldToDevice->IsTranslateScale())
{
REAL m11 = worldToDevice->GetM11();
REAL m22 = worldToDevice->GetM22();
REAL maxScale = max(REALABS(m11), REALABS(m22));
if(width*maxScale <= minimumPenWidth)
useOnePixelPath = TRUE;
}
else
{
// This is a general transform.
REAL majorR, minorR; // Radii for major and minor axis.
if(::GetMajorAndMinorAxis(
&majorR,
&minorR,
worldToDevice) == Ok)
{
if(width*majorR <= minimumPenWidth)
useOnePixelPath = TRUE;
}
}
}
else
{
// Since GDI+ only uses the World Uinit, this code is not called
// any more.
width = ::GetDeviceWidth(width, unit, dpiX);
if(width <= minimumPenWidth)
useOnePixelPath = TRUE;
}
return useOnePixelPath;
}