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.
2146 lines
53 KiB
2146 lines
53 KiB
/**************************************************************************\
|
|
*
|
|
* 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;
|
|
|
|
// Initialize pointer members so that we don't delete garbage
|
|
Brush = NULL;
|
|
DevicePen.Brush = NULL;
|
|
DevicePen.DashArray = NULL;
|
|
DevicePen.CompoundArray = NULL;
|
|
DevicePen.CustomStartCap = NULL;
|
|
DevicePen.CustomEndCap = NULL;
|
|
|
|
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;
|
|
}
|
|
|