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

1193 lines
29 KiB
C++

/**************************************************************************\
*
* Copyright (c) 2000 Microsoft Corporation
*
* Module Name:
*
* CustomLineCap.cpp
*
* Abstract:
*
* Implementation of custom line cap class
*
* Revision History:
*
* 02/21/00 ikkof
* Created it
*
\**************************************************************************/
#include "precomp.hpp"
/**************************************************************************\
*
* Function Description:
*
* ComputeCapLength
*
* Compute the length of the cap from zero along the + y axis
* Typically custom caps will return a negative length.
*
* Arguments:
*
* GpPointF *points, the points representing the cap path.
* BYTE *types, the types "
* INT pointCount how many points in the above arrays.
*
* Return Value:
* REAL -- the length.
*
* 08/25/2000 [asecchia]
* Created it
*
\**************************************************************************/
static REAL ComputeCapLength(
DpPath * path
)
{
const GpPointF *points = path->GetPathPoints();
const BYTE *types = path->GetPathTypes();
INT pointCount = path->GetPointCount();
REAL length = 0.0f;
BOOL isClosed = (types[pointCount-1] & PathPointTypeCloseSubpath) != 0;
// Eliminate degenerate paths and uninitialized paths.
if( points && (pointCount>1) )
{
REAL curlength = 0.0f;
GpArrayIterator<GpPointF> pIt(const_cast<GpPointF *>(points), pointCount);
// Get the last item in the list.
pIt.SeekLast();
GpPointF *lastPoint = pIt.CurrentItem();
// Begin at the first item.
pIt.SeekFirst();
GpPointF *curPoint;
if(!isClosed)
{
// if it's not a closed path, skip the last-to-first point line.
lastPoint = pIt.CurrentItem();
}
while(!pIt.IsDone())
{
curPoint = pIt.CurrentItem();
if(intersect_line_yaxis(*curPoint, *lastPoint, &curlength))
{
length = min(length, curlength);
}
lastPoint = curPoint;
pIt.Next();
}
}
return length;
}
GpStatus GpCustomLineCap::ComputeFillCapLength()
{
FillLength = -ComputeCapLength(FillPath);
// Fill paths cannot have a length of zero or less.
if(FillLength < REAL_EPSILON)
{
return NotImplemented;
}
return Ok;
}
GpStatus GpCustomLineCap::ComputeStrokeCapLength()
{
StrokeLength = -ComputeCapLength(StrokePath);
// Stroke paths can have a length of zero - we explicitly check for
// this and handle it.
if(StrokeLength < -REAL_EPSILON)
{
return NotImplemented;
}
return Ok;
}
GpCustomLineCap::GpCustomLineCap(
const DpPath* fillPath,
const DpPath* strokePath,
GpLineCap baseCap,
REAL baseInset
) : GpFillPath (NULL, 0, PointsBuffer1, TypesBuffer1, CLCAP_BUFFER_SIZE, FillModeWinding, DpPath::PossiblyNonConvex),
GpStrokePath(NULL, 0, PointsBuffer2, TypesBuffer2, CLCAP_BUFFER_SIZE, FillModeWinding, DpPath::PossiblyNonConvex)
{
Initialize();
GpStatus status = Ok;
if(fillPath)
{
GpPath* gpFillPath = GpPath::GetPath(fillPath);
status = SetFillPath(gpFillPath);
}
if(status == Ok && strokePath)
{
GpPath* gpStrokePath = GpPath::GetPath(strokePath);
status = SetStrokePath(gpStrokePath);
}
if(status == Ok)
{
switch(baseCap)
{
case LineCapFlat:
case LineCapSquare:
case LineCapRound:
case LineCapTriangle:
BaseCap = baseCap;
break;
default:
BaseCap = LineCapFlat;
break;
}
BaseInset = baseInset;
}
else
{
Reset();
SetValid(FALSE);
m_creationStatus = status; // this defaults to Ok.
}
}
VOID
GpCustomLineCap::ResetFillPath()
{
GpFillPath.Reset(FillModeWinding);
}
VOID
GpCustomLineCap::ResetStrokePath()
{
GpStrokePath.Reset(FillModeWinding);
}
VOID
GpCustomLineCap::ReverseFillPath()
{
GpFillPath.Reverse();
}
VOID
GpCustomLineCap::ReverseStrokePath()
{
GpStrokePath.Reverse();
}
VOID
GpCustomLineCap::Reset()
{
// Clean up and reset to the default state.
Initialize();
ResetFillPath();
ResetStrokePath();
}
GpCustomLineCap::GpCustomLineCap(
const GpCustomLineCap* customCap
) : GpFillPath (NULL, 0, PointsBuffer1, TypesBuffer1, CLCAP_BUFFER_SIZE, FillModeWinding, DpPath::PossiblyNonConvex),
GpStrokePath(NULL, 0, PointsBuffer2, TypesBuffer2, CLCAP_BUFFER_SIZE, FillModeWinding, DpPath::PossiblyNonConvex)
{
Initialize();
if(customCap == NULL)
return;
GpStatus status = Ok;
status = SetFillPath(customCap->FillPath);
if(status == Ok)
status = SetStrokePath(customCap->StrokePath);
if(status == Ok)
{
GpLineCap baseCap = customCap->BaseCap;
switch(baseCap)
{
case LineCapFlat:
case LineCapSquare:
case LineCapRound:
case LineCapTriangle:
BaseCap = baseCap;
break;
default:
BaseCap = LineCapFlat;
break;
}
BaseInset = customCap->BaseInset;
StrokeStartCap = customCap->StrokeStartCap;
StrokeEndCap = customCap->StrokeEndCap;
StrokeJoin = customCap->StrokeJoin;
WidthScale = customCap->WidthScale;
}
else
{
Reset();
SetValid(FALSE);
m_creationStatus = status; // this defaults to Ok.
}
}
GpCustomLineCap::~GpCustomLineCap()
{
}
GpStatus
GpCustomLineCap::SetFillPath(
const DpPath* path
)
{
// If the given path is NULL, empty the fill path.
if(path == NULL)
{
ResetFillPath();
return Ok;
}
INT count = path->GetPointCount();
return SetFillPath(path->GetPathPoints(), path->GetPathTypes(), count);
}
GpStatus
GpCustomLineCap::SetFillPath(
const GpPointF* fillPoints,
const BYTE* fillTypes,
INT fillCount)
{
if(fillCount == 0)
{
ResetFillPath();
return Ok;
}
if(fillCount <= 2 || fillPoints == NULL || fillTypes == NULL)
return InvalidParameter;
GpPathData pathData;
pathData.Points = const_cast<GpPointF *>(fillPoints);
pathData.Types = const_cast<BYTE *>(fillTypes);
pathData.Count = fillCount;
GpStatus status = FillPath->SetPathData(&pathData);
if(status == Ok)
{
status = ComputeFillCapLength();
}
return status;
}
GpStatus
GpCustomLineCap::GetFillPath(
GpPath* path
) const
{
if(!path)
return InvalidParameter;
GpPathData pathData;
pathData.Points = const_cast<GpPointF *>(FillPath->GetPathPoints());
pathData.Types = const_cast<BYTE *>(FillPath->GetPathTypes());
pathData.Count = FillPath->GetPointCount();
return path->SetPathData(&pathData);
}
GpStatus
GpCustomLineCap::SetStrokePath(
const DpPath* path
)
{
// If the given path is NULL, empty the stroke path.
if(path == NULL)
{
ResetStrokePath();
return Ok;
}
INT count = path->GetPointCount();
return SetStrokePath(path->GetPathPoints(), path->GetPathTypes(), count);
}
GpStatus
GpCustomLineCap::SetStrokePath(
const GpPointF* strokePoints,
const BYTE* strokeTypes,
INT strokeCount)
{
if(strokeCount == 0)
{
ResetStrokePath();
return Ok;
}
if(strokeCount <= 1 || strokePoints == NULL || strokeTypes == NULL)
return InvalidParameter;
GpPathData pathData;
pathData.Points = const_cast<GpPointF *>(strokePoints);
pathData.Types = const_cast<BYTE *>(strokeTypes);
pathData.Count = strokeCount;
GpStatus status = StrokePath->SetPathData(&pathData);
if(status == Ok)
{
status = ComputeStrokeCapLength();
}
return status;
}
GpStatus
GpCustomLineCap::GetStrokePath(
GpPath* path
) const
{
if(!path)
return InvalidParameter;
GpPathData pathData;
pathData.Points = const_cast<GpPointF *>(StrokePath->GetPathPoints());
pathData.Types = const_cast<BYTE *>(StrokePath->GetPathTypes());
pathData.Count = StrokePath->GetPointCount();
return path->SetPathData(&pathData);
}
BOOL
GpCustomLineCap::IsEqual(
const DpCustomLineCap* customLineCap
) const
{
if(!customLineCap)
return FALSE;
const GpCustomLineCap* otherCap;
otherCap = static_cast<const GpCustomLineCap*>(customLineCap);
return (
(BaseCap == otherCap->BaseCap) &&
(BaseInset == otherCap->BaseInset) &&
(StrokeStartCap == otherCap->StrokeStartCap) &&
(StrokeEndCap == otherCap->StrokeEndCap) &&
(StrokeJoin == otherCap->StrokeJoin) &&
(WidthScale == otherCap->WidthScale) &&
GpFillPath.IsEqual(&(otherCap->GpFillPath)) &&
GpStrokePath.IsEqual(&(otherCap->GpStrokePath))
);
}
INT getTransformedPoints(
GpPointF* points,
BYTE* types,
INT count,
const GpPointF* srcPoints,
const BYTE* srcTypes,
INT srcCount,
const GpPointF& origin,
const GpPointF& tangent,
REAL lineWidth,
REAL minLineWidth,
const GpPointF& hotSpot
)
{
if(points == NULL && types == NULL)
{
return 0;
}
ASSERT(srcPoints && srcTypes);
if(srcPoints == NULL || srcTypes == NULL)
{
return 0;
}
INT count1 = srcCount;
if(count1 > count)
{
count1 = count;
}
if(types)
{
GpMemcpy(types, srcTypes, count1);
}
// Make sure the line width used for the cap path is
// larger than the minimum line width.
REAL width = lineWidth;
if(lineWidth < minLineWidth)
{
width = minLineWidth;
}
if(points)
{
GpPointF* dstPts = points;
const GpPointF* srcPts = srcPoints;
REAL m11, m12, m21, m22, tx, ty;
m11 = width*tangent.Y;
m21 = width*tangent.X;
m12 = - width*tangent.X;
m22 = width*tangent.Y;
// Adjust the origin according to the hot spot.
tx = hotSpot.X*(1.0f - width);
ty = hotSpot.Y*(1.0f - width);
REAL savedTx = tx;
tx = tx*tangent.Y + ty*tangent.X + origin.X;
ty = - savedTx*tangent.X + ty*tangent.Y + origin.Y;
// Transform the points.
for(INT i = 0; i < count1; i++)
{
dstPts->X = m11*srcPts->X + m21*srcPts->Y + tx;
dstPts->Y = m12*srcPts->X + m22*srcPts->Y + ty;
dstPts++;
srcPts++;
}
}
return count1;
}
INT
GpCustomLineCap::GetTransformedFillCap(
GpPointF* points,
BYTE* types,
INT count,
const GpPointF& origin,
const GpPointF& tangent,
REAL lineWidth,
REAL minimumWidth
) const
{
INT fillCount = GetFillPointCount();
if(fillCount <= 0)
return 0;
// Calculate the minimum line width and hot spot.
// FillHotSpot is defined relative to the minimumWidth.
REAL minLineWidth = minimumWidth;
GpPointF hotSpot;
hotSpot.X = minimumWidth*FillHotSpot.X;
hotSpot.Y = minimumWidth*FillHotSpot.Y;
return getTransformedPoints(
points,
types,
count,
GetFillPoints(),
GetFillTypes(),
fillCount,
origin,
tangent,
lineWidth,
minLineWidth,
hotSpot);
}
INT
GpCustomLineCap::GetTransformedStrokeCap(
INT cCapacity, // In, initial pPoints & pTypes capacity
GpPointF ** pPoints, // In/out, may be reallocated here
BYTE ** pTypes, // In/out, may be reallocated here
INT * pCount, // In/out, may change here if flattened
const GpPointF& origin,
const GpPointF& tangent,
REAL lineWidth,
REAL minimumWidth
) const
{
INT strokeCount = GetStrokePointCount();
if(strokeCount <= 0 || lineWidth <= 0)
return 0;
if (!pPoints || !pTypes || !pCount)
return 0;
// Calculate the minimum line width and hot spot.
// StrokeHotSpot is defined relative to the minimumWidth.
GpPointF hotSpot;
hotSpot.X = minimumWidth*StrokeHotSpot.X;
hotSpot.Y = minimumWidth*StrokeHotSpot.Y;
strokeCount = getTransformedPoints(
*pPoints,
*pTypes,
*pCount,
GetStrokePoints(),
GetStrokeTypes(),
strokeCount,
origin,
tangent,
lineWidth,
minimumWidth,
hotSpot);
// The widener expects a flattened path
GpPath path(*pPoints, *pTypes, strokeCount, FillModeWinding);
if (Ok == path.Flatten(NULL, FlatnessDefault))
{
// Flattening succeeded
strokeCount = path.GetPointCount();
if (strokeCount > cCapacity)
{
// Reallocate the points and types arrays
GpPointF * ptfTemp = (GpPointF*) GpRealloc(*pPoints,
strokeCount*sizeof(GpPointF));
if (ptfTemp)
*pPoints = ptfTemp;
else
strokeCount = 0;
BYTE * pbTemp = (BYTE*)GpRealloc(*pTypes, strokeCount);
if (pbTemp)
*pTypes = pbTemp;
else
strokeCount = 0;
}
if (strokeCount)
{
// Replace with the flattened points
GpMemcpy(*pPoints, path.GetPathPoints(), strokeCount*sizeof(GpPointF));
GpMemcpy(*pTypes, path.GetPathTypes(), strokeCount);
}
*pCount = strokeCount;
} // end if flattening succeeded
return strokeCount;
}
REAL
GpCustomLineCap::GetRadius(
REAL lineWidth,
REAL minimumWidth
) const
{
INT fillCount = GetFillPointCount();
INT strokeCount = GetStrokePointCount();
if((fillCount <= 0 && strokeCount <= 0) || lineWidth <= 0)
return 0;
INT maxCount = max(fillCount, strokeCount);
const INT buffCount = 32;
GpPointF pointBuff[buffCount];
BYTE typeBuff[buffCount];
GpPointF* points = NULL;
BYTE* types = NULL;
if(maxCount <= buffCount)
{
points = &pointBuff[0];
types = &typeBuff[0];
}
else
{
points = (GpPointF*) GpMalloc(maxCount*sizeof(GpPointF));
types = (BYTE*) GpMalloc(maxCount);
}
REAL maxR = 0;
if(points && types)
{
GpPointF origin(0, 0);
GpPointF tangent(0, 1);
REAL minLineWidth;
GpPointF hotSpot;
REAL d;
INT count;
if(fillCount > 0)
{
// Calculate the minimum line width and hot spot.
// FillHotSpot is defined relative to the minimumWidth.
minLineWidth = minimumWidth;
hotSpot.X = minimumWidth*FillHotSpot.X;
hotSpot.Y = minimumWidth*FillHotSpot.Y;
count = getTransformedPoints(
points,
types,
fillCount,
GetFillPoints(),
GetFillTypes(),
fillCount,
origin,
tangent,
lineWidth,
minLineWidth,
hotSpot);
REAL i;
GpPointF* pts = points;
maxR = pts->X*pts->X + pts->Y*pts->Y;
for(i = 1, pts++; i < count; i++, pts++)
{
d = pts->X*pts->X + pts->Y*pts->Y;
if(d > maxR)
maxR = d;
}
}
if(strokeCount > 0)
{
// Calculate the minimum line width and hot spot.
// FillHotSpot is defined relative to the minimumWidth.
minLineWidth = minimumWidth;
hotSpot.X = minimumWidth*StrokeHotSpot.X;
hotSpot.Y = minimumWidth*StrokeHotSpot.Y;
count = getTransformedPoints(
points,
types,
strokeCount,
GetStrokePoints(),
GetStrokeTypes(),
strokeCount,
origin,
tangent,
lineWidth,
minLineWidth,
hotSpot);
GpPath strokePath(points, types, count, FillModeWinding);
GpRectF rect;
strokePath.GetBounds(&rect);
REAL sharpestAngle = strokePath.GetSharpestAngle();
REAL delta0 = max(lineWidth*WidthScale, minimumWidth);
REAL delta = delta0/2;
if(StrokeJoin == LineJoinMiter ||
StrokeJoin == LineJoinMiterClipped)
{
REAL miterLimit = StrokeMiterLimit;
delta = delta0*miterLimit;
if(delta > 20)
{
delta = GpPen::ComputeMiterLength(
sharpestAngle,
miterLimit
);
delta *= delta0;
}
}
REAL left, right, top, bottom;
left = rect.X - delta;
right = rect.X + delta;
top = rect.Y - delta;
bottom = rect.Y + delta;
d = left*left + top*top;
if(d > maxR)
maxR = d;
d = left*left + bottom*bottom;
if(d > maxR)
maxR = d;
d = right*right + top*top;
if(d > maxR)
maxR = d;
d = right*right + bottom*bottom;
if(d > maxR)
maxR = d;
}
}
else
{
//!!! Do something when the memory is not available.
}
if(points != &pointBuff[0])
GpFree(points);
if(types != &typeBuff[0])
GpFree(types);
if(maxR > 0)
maxR = REALSQRT(maxR);
return maxR;
}
GpAdjustableArrowCap::GpAdjustableArrowCap(
const GpAdjustableArrowCap* arrowCap
) : GpCustomLineCap(arrowCap)
{
if(arrowCap)
{
Height = arrowCap->Height;
Width = arrowCap->Width;
MiddleInset = arrowCap->MiddleInset;
FillState = arrowCap->FillState;
}
else
{
SetDefaultValue();
}
Update();
}
GpStatus
GpAdjustableArrowCap::GetPathData(
GpPathData* pathData,
REAL height,
REAL width,
REAL middleInset,
BOOL isFilled
)
{
if(pathData == NULL)
return InvalidParameter;
GpPointF* points = pathData->Points;
BYTE* types = pathData->Types;
points[0].X = width/2;
points[0].Y = - height;
points[1].X = 0;
points[1].Y = 0;
points[2].X = - width/2;
points[2].Y = - height;
points[3].X = 0;
points[3].Y = - height + middleInset;
types[0] = PathPointTypeStart;
types[1] = PathPointTypeLine;
types[2] = PathPointTypeLine;
types[3] = PathPointTypeLine;
INT lastIndex = 2;
if(middleInset != 0 && isFilled)
lastIndex = 3;
if(isFilled)
types[lastIndex] |= PathPointTypeCloseSubpath;
pathData->Count = lastIndex + 1;
return Ok;
}
GpStatus
GpAdjustableArrowCap::Update()
{
GpPointF points[4];
BYTE types[4];
GpPathData pathData;
pathData.Points = &points[0];
pathData.Types = &types[0];
pathData.Count = 3;
BaseCap = LineCapTriangle;
BaseInset = (Width != 0) ? (Height / Width) : 0;
GetPathData(&pathData, Height, Width, MiddleInset, FillState);
GpPath path(FillModeWinding);
path.SetPathData(&pathData);
if(FillState)
{
// Fill path only.
SetFillPath(&path);
SetStrokePath(NULL);
}
else
{
// Stroke path only.
SetStrokePath(&path);
SetFillPath(NULL);
}
return Ok;
}
ObjectType
GpCustomLineCap::GetObjectType() const
{
return ObjectTypeCustomLineCap;
}
#define GDIP_CAPFLAGS_FILLPATH 0x00000001
#define GDIP_CAPFLAGS_STROKEPATH 0x00000002
class CustomLineCapData : public ObjectTypeData
{
public:
INT32 Flags;
INT32 BaseCap;
REAL BaseInset;
INT32 StrokeStartCap;
INT32 StrokeEndCap;
INT32 StrokeJoin;
REAL StrokeMiterLimit;
REAL WidthScale;
GpPointF FillHotSpot;
GpPointF StrokeHotSpot;
};
UINT
GpCustomLineCap::GetDataSize() const
{
ASSERT(IsValid());
UINT size = sizeof(CustomLineCapData);
INT fillPathSize = 0;
INT strokePathSize = 0;
if ((GetFillPointCount() > 2) &&
((fillPathSize = FillPath->GetDataSize()) > 0))
{
ASSERT((fillPathSize & 0x03) == 0);
size += sizeof(INT32) + fillPathSize;
}
if ((GetStrokePointCount() > 2) &&
((strokePathSize = StrokePath->GetDataSize()) > 0))
{
ASSERT((strokePathSize & 0x03) == 0);
size += sizeof(INT32) + strokePathSize;
}
return size;
}
GpStatus
GpCustomLineCap::GetData(
IStream * stream
) const
{
ASSERT(IsValid());
INT flags = 0;
INT fillPathSize = 0;
INT strokePathSize = 0;
if ((GetFillPointCount() > 2) &&
((fillPathSize = FillPath->GetDataSize()) > 0))
{
ASSERT((fillPathSize & 0x03) == 0);
flags |= GDIP_CAPFLAGS_FILLPATH;
}
if ((GetStrokePointCount() > 2) &&
((strokePathSize = StrokePath->GetDataSize()) > 0))
{
ASSERT((strokePathSize & 0x03) == 0);
flags |= GDIP_CAPFLAGS_STROKEPATH;
}
CustomLineCapData capData;
capData.Type = GetType();
capData.Flags = flags;
capData.BaseCap = BaseCap;
capData.BaseInset = BaseInset;
capData.StrokeStartCap = StrokeStartCap;
capData.StrokeEndCap = StrokeEndCap;
capData.StrokeJoin = StrokeJoin;
capData.StrokeMiterLimit = StrokeMiterLimit;
capData.WidthScale = WidthScale;
capData.FillHotSpot = FillHotSpot;
capData.StrokeHotSpot = StrokeHotSpot;
stream->Write(&capData, sizeof(capData), NULL);
if (flags & GDIP_CAPFLAGS_FILLPATH)
{
stream->Write(&fillPathSize, sizeof(INT32), NULL);
FillPath->GetData(stream);
}
if (flags & GDIP_CAPFLAGS_STROKEPATH)
{
stream->Write(&strokePathSize, sizeof(INT32), NULL);
StrokePath->GetData(stream);
}
return Ok;
}
GpStatus
GpCustomLineCap::SetData(
const BYTE * dataBuffer,
UINT size
)
{
this->Reset();
if (dataBuffer == NULL)
{
WARNING(("dataBuffer is NULL"));
return InvalidParameter;
}
if (size < sizeof(CustomLineCapData))
{
WARNING(("size too small"));
return InvalidParameter;
}
const CustomLineCapData * capData;
capData = reinterpret_cast<const CustomLineCapData *>(dataBuffer);
ASSERT((CustomLineCapType)(capData->Type) == CustomLineCapTypeDefault);
if (!capData->MajorVersionMatches())
{
WARNING(("Version number mismatch"));
return InvalidParameter;
}
BaseCap = (GpLineCap)capData->BaseCap;
BaseInset = capData->BaseInset;
StrokeStartCap = (GpLineCap)capData->StrokeStartCap;
StrokeEndCap = (GpLineCap)capData->StrokeEndCap;
StrokeJoin = (GpLineJoin)capData->StrokeJoin;
StrokeMiterLimit = capData->StrokeMiterLimit;
WidthScale = capData->WidthScale;
FillHotSpot = capData->FillHotSpot;
StrokeHotSpot = capData->StrokeHotSpot;
dataBuffer += sizeof(CustomLineCapData);
size -= sizeof(CustomLineCapData);
GpStatus status = Ok;
if (capData->Flags & GDIP_CAPFLAGS_FILLPATH)
{
if (size < sizeof(INT32))
{
WARNING(("size too small"));
return InvalidParameter;
}
UINT pathSize = ((INT32 *)dataBuffer)[0];
dataBuffer += sizeof(INT32);
size -= sizeof(INT32);
if (size < pathSize)
{
WARNING(("size too small"));
return InvalidParameter;
}
if ((status = FillPath->SetData(dataBuffer, pathSize)) != Ok)
{
return status;
}
if(Ok == status)
{
status = ComputeFillCapLength();
}
dataBuffer += pathSize;
size -= pathSize;
}
if (capData->Flags & GDIP_CAPFLAGS_STROKEPATH)
{
if (size < sizeof(INT32))
{
WARNING(("size too small"));
return InvalidParameter;
}
UINT pathSize = ((INT32 *)dataBuffer)[0];
dataBuffer += sizeof(INT32);
size -= sizeof(INT32);
if (size < pathSize)
{
WARNING(("size too small"));
return InvalidParameter;
}
status = StrokePath->SetData(dataBuffer, pathSize);
if(Ok == status)
{
status = ComputeStrokeCapLength();
}
dataBuffer += pathSize;
size -= pathSize;
}
UpdateUid();
return status;
}
class AdjustableArrowCapData : public ObjectTypeData
{
public:
REAL Width;
REAL Height;
REAL MiddleInset;
INT32 FillState;
INT32 StrokeStartCap;
INT32 StrokeEndCap;
INT32 StrokeJoin;
REAL StrokeMiterLimit;
REAL WidthScale;
GpPointF FillHotSpot;
GpPointF StrokeHotSpot;
};
UINT
GpAdjustableArrowCap::GetDataSize() const
{
ASSERT(IsValid());
return sizeof(AdjustableArrowCapData);
}
GpStatus
GpAdjustableArrowCap::GetData(
IStream * stream
) const
{
ASSERT(IsValid());
AdjustableArrowCapData arrowCapData;
arrowCapData.Type = GetType();
arrowCapData.Width = Width;
arrowCapData.Height = Height;
arrowCapData.MiddleInset = MiddleInset;
arrowCapData.FillState = FillState;
arrowCapData.StrokeStartCap = StrokeStartCap;
arrowCapData.StrokeEndCap = StrokeEndCap;
arrowCapData.StrokeJoin = StrokeJoin;
arrowCapData.StrokeMiterLimit = StrokeMiterLimit;
arrowCapData.WidthScale = WidthScale;
arrowCapData.FillHotSpot = FillHotSpot;
arrowCapData.StrokeHotSpot = StrokeHotSpot;
stream->Write(&arrowCapData, sizeof(arrowCapData), NULL);
return Ok;
}
GpStatus
GpAdjustableArrowCap::SetData(
const BYTE * dataBuffer,
UINT size
)
{
this->Reset();
if (dataBuffer == NULL)
{
WARNING(("dataBuffer is NULL"));
return InvalidParameter;
}
if (size < sizeof(AdjustableArrowCapData))
{
WARNING(("size too small"));
return InvalidParameter;
}
const AdjustableArrowCapData * arrowCapData;
arrowCapData = reinterpret_cast<const AdjustableArrowCapData *>(dataBuffer);
ASSERT((CustomLineCapType)(arrowCapData->Type) == CustomLineCapTypeAdjustableArrow);
if (!arrowCapData->MajorVersionMatches())
{
WARNING(("Version number mismatch"));
return InvalidParameter;
}
Width = arrowCapData->Width;
Height = arrowCapData->Height;
MiddleInset = arrowCapData->MiddleInset;
FillState = arrowCapData->FillState;
StrokeStartCap = (GpLineCap)arrowCapData->StrokeStartCap;
StrokeEndCap = (GpLineCap)arrowCapData->StrokeEndCap;
StrokeJoin = (GpLineJoin)arrowCapData->StrokeJoin;
StrokeMiterLimit = arrowCapData->StrokeMiterLimit;
WidthScale = arrowCapData->WidthScale;
FillHotSpot = arrowCapData->FillHotSpot;
StrokeHotSpot = arrowCapData->StrokeHotSpot;
this->Update();
UpdateUid();
return Ok;
}