WindowsXP-SP1/windows/advcore/gdiplus/engine/entry/xpath.cpp

859 lines
20 KiB
C++
Raw Normal View History

2001-01-01 00:00:00 +01:00
/**************************************************************************\
*
* Copyright (c) 1999 Microsoft Corporation
*
* Abstract:
*
* Implementation of XBezier class and its DDA.
*
* History:
*
* 11/08/1999 ikkof
* Created it.
*
\**************************************************************************/
#include "precomp.hpp"
// Path types used for advanced path.
// !!! [asecchia]
// this is a very clumsy hack to enable the XPath stuff to work.
// we should have an internal set of enum values that are different
// from the external ones.
#define PathPointTypeBezier2 2 // quadratic Beizer
#define PathPointTypeBezier4 4 // quartic (4th order) Beizer
#define PathPointTypeBezier5 5 // quintic (5th order) Bezier
#define PathPointTypeBezier6 6 // hexaic (6th order) Bezier
GpXPath::GpXPath(const GpPath* path)
{
InitDefaultState();
if(!path || !path->IsValid())
return;
INT count = path->GetPointCount();
const GpPointF *pts = ((GpPath*) path)->GetPathPoints();
const BYTE *types = ((GpPath*) path)->GetPathTypes();
if(pts && types && count > 0)
{
Types = (BYTE*) GpMalloc(count);
XPoints.Count = count;
XPoints.Dimension = 2;
XPoints.Data = (REALD*) GpMalloc(2*count*sizeof(REALD));
XPoints.IsDataAllocated = TRUE;
if(Types && XPoints.Data)
{
GpMemcpy(Types, types, count);
REALD* data = XPoints.Data;
for(INT i = 0; i < count; i++)
{
*data++ = pts->X;
*data++ = pts->Y;
pts++;
}
SetValid(TRUE);
}
FillMode = ((GpPath*) path)->GetFillMode();
}
}
GpXPath::GpXPath(
const GpPath* path,
const GpRectF& rect,
const GpPointF* points,
INT count,
WarpMode warpMode
)
{
InitDefaultState();
if(warpMode == WarpModePerspective)
ConvertToPerspectivePath(path, rect, points, count);
else if(warpMode == WarpModeBilinear)
ConvertToBilinearPath(path, rect, points, count);
}
GpStatus
GpXPath::ConvertToPerspectivePath(
const GpPath* path,
const GpRectF& rect,
const GpPointF* points,
INT count
)
{
ASSERT(path && path->IsValid());
if(!path || !path->IsValid())
return InvalidParameter;
// Obtain the path points.
const GpPointF* pathPts = ((GpPath*) path)->GetPathPoints();
const BYTE* pathTypes = ((GpPath*) path)->GetPathTypes();
INT pathCount = path->GetPointCount();
BYTE* types = (BYTE*) GpMalloc(pathCount);
if(!types)
return OutOfMemory;
GpMemcpy(types, pathTypes, pathCount);
// Set the perspective transform.
GpPerspectiveTransform trans(rect, points, count);
// Convert the path points to 3D perspective points.
REALD* data = (REALD*) GpMalloc(3*pathCount*sizeof(REALD));
if(!data)
return OutOfMemory;
// Use this data for xpoints.
XPoints.Count = pathCount;
XPoints.Dimension = 3;
XPoints.Data = data;
XPoints.IsDataAllocated = TRUE;
Types = types;
GpStatus status = trans.ConvertPoints(pathPts, pathCount, &XPoints);
if(status == Ok)
SetValid(TRUE);
return status;
}
GpStatus
GpXPath::ConvertToBilinearPath(
const GpPath* path,
const GpRectF& rect,
const GpPointF* points,
INT count
)
{
ASSERT(path && path->IsValid());
if(!path || !path->IsValid())
return InvalidParameter;
// Obtain the path points.
const GpPointF* pathPts = ((GpPath*) path)->GetPathPoints();
const BYTE* pathTypes = ((GpPath*) path)->GetPathTypes();
INT pathCount = path->GetPointCount();
// The maximum data size of the bilinear transform is
// 2*pathCount - 1. Here set it as 2*pathCount.
INT dimension = 2;
INT maxCount = 2*pathCount;
REALD* data = (REALD*) GpMalloc(dimension*maxCount*sizeof(REALD));
BYTE* types = (BYTE*) GpMalloc(maxCount);
if(!data || !types)
{
GpFree(data);
GpFree(types);
return OutOfMemory;
}
GpMemset(types, 0, maxCount);
// Set the bilinear transform.
GpBilinearTransform trans(rect, points, count);
DpPathIterator iter(pathPts, pathTypes, pathCount);
INT startIndex, endIndex;
BOOL isClosed;
GpStatus status = Ok;
REALD* dataPtr = data;
INT totalCount = 0; // Number of control points.
while(
iter.NextSubpath(
&startIndex, &endIndex, &isClosed)
&& status == Ok
)
{
INT typeStartIndex, typeEndIndex;
BYTE pathType;
BOOL isFirstPoint = TRUE;
while(
iter.NextPathType(&pathType, &typeStartIndex, &typeEndIndex)
&& status == Ok
)
{
// Starting point of the current suptype
// and the number of points.
const GpPointF* pts = pathPts + typeStartIndex;
INT typeCount = typeEndIndex - typeStartIndex + 1;
switch(pathType)
{
case PathPointTypeBezier3:
trans.ConvertCubicBeziers(pts, typeCount, dataPtr);
pts += typeCount - 1;
dataPtr += dimension*2*(typeCount - 1);
if(isFirstPoint)
{
*(types + totalCount) = PathPointTypeStart;
totalCount++;
}
GpMemset(types + totalCount, PathPointTypeBezier6, 2*(typeCount - 1));
totalCount += 2*(typeCount - 1);
isFirstPoint = FALSE;
break;
case PathPointTypeLine:
trans.ConvertLines(pts, typeCount, dataPtr);
pts += typeCount - 1;
dataPtr += dimension*2*(typeCount - 1);
if(isFirstPoint)
{
*(types + totalCount) = PathPointTypeStart;
totalCount++;
}
GpMemset(types + totalCount, PathPointTypeBezier2, 2*(typeCount - 1));
totalCount += 2*(typeCount - 1);
isFirstPoint = FALSE;
break;
case PathPointTypeStart:
case PathPointTypeBezier2:
case PathPointTypeBezier4:
case PathPointTypeBezier5:
case PathPointTypeBezier6:
default:
// Should not have any of those types in GpPath.
ASSERT(0);
break;
}
}
}
Types = types;
XPoints.Count = totalCount;
XPoints.Dimension = dimension;
XPoints.Data = data;
XPoints.IsDataAllocated = TRUE;
SetValid(TRUE);
return Ok;
}
GpStatus
GpXPath::Flatten(
DynByteArray* flattenTypes,
DynPointFArray* flattenPoints,
const GpMatrix *matrix
)
{
GpStatus status = Ok;
ASSERT(matrix);
FPUStateSaver fpuState; // Setup the FPU state.
flattenPoints->Reset(FALSE);
flattenTypes->Reset(FALSE);
INT dimension = XPoints.Dimension;
REALD* data = XPoints.Data;
GpXPathIterator iter(this);
INT startIndex, endIndex;
BOOL isClosed;
GpPointF* ptsBuffer = NULL;
while(
iter.NextSubpath(
&startIndex, &endIndex, &isClosed)
&& status == Ok
)
{
INT typeStartIndex, typeEndIndex;
BYTE pathType;
BOOL isFirstPoint = TRUE;
while(
iter.NextPathType(&pathType, &typeStartIndex, &typeEndIndex)
&& status == Ok
)
{
INT count, index;
BYTE* types;
GpPointF* pts;
switch(pathType)
{
case PathPointTypeStart:
break;
case PathPointTypeBezier2:
case PathPointTypeBezier3:
case PathPointTypeBezier4:
case PathPointTypeBezier5:
case PathPointTypeBezier6:
{
BOOL dontCopy = FALSE;
GpXBezier bezier;
GpXPoints xpoints;
INT order;
xpoints.SetData(
data + typeStartIndex*dimension,
dimension,
typeEndIndex - typeStartIndex + 1,
dontCopy // Don't copy the data.
);
order = (INT) pathType;
if(bezier.SetBeziers(order, xpoints) == Ok)
{
// Flatten() flattens Bezier.
// The flattened points are already transformed.
DynPointFArray bezierFlattenPts;
bezier.Flatten(&bezierFlattenPts, matrix);
// count = bezier.GetFlattenCount();
count = bezierFlattenPts.GetCount();
// Check if there is already the first point.
if(!isFirstPoint)
count--; // Don't add the first point.
if (count > 0)
{
if((types = flattenTypes->AddMultiple(count)) != NULL)
{
// pts = bezier.GetFlattenData();
pts = bezierFlattenPts.GetDataBuffer();
if(!isFirstPoint)
pts++; // Skip the first point.
flattenPoints->AddMultiple(pts, count);
GpMemset(types, PathPointTypeLine, count);
if(isFirstPoint)
types[0] = PathPointTypeStart;
isFirstPoint = FALSE;
}
else
status = OutOfMemory;
}
}
else
status =InvalidParameter;
}
break;
case PathPointTypeLine:
default:
count = typeEndIndex - typeStartIndex + 1;
if(!isFirstPoint)
count--;
if((types = flattenTypes->AddMultiple(count)) != NULL)
{
// Set the type.
GpMemset(types, PathPointTypeLine, count);
if(isFirstPoint)
types[0] = PathPointTypeStart;
// Get the first data.
REALD* dataPtr = data + typeStartIndex*dimension;
if(!isFirstPoint)
dataPtr += dimension; // Skip the first point.
// Allocate the point buffer to save
// for the flatten points.
pts = (GpPointF*) GpRealloc(ptsBuffer,
count*sizeof(GpPointF));
if(!pts)
{
status = OutOfMemory;
break;
}
else
ptsBuffer = pts;
// Copy the data
GpPointF* ptsPtr = pts;
for(INT k = 0; k < count; k++)
{
// Simply copy the first 2 elments
// of the data as the x and y component.
REALD x, y, w;
x = *dataPtr++;
y = *dataPtr++;
// Do the perspective projection if
// dimension is higher than 2.
if(dimension > 2)
{
w = *dataPtr;
x /= w;
y /= w;
}
ptsPtr->X = TOREAL(x);
ptsPtr->Y = TOREAL(y);
ptsPtr++;
// Skip the rest.
if(dimension > 2)
dataPtr += (dimension - 2);
}
// Add to the flatten points.
index = flattenPoints->GetCount();
flattenPoints->AddMultiple(pts, count);
// Get the data biffer of the flatten points.
pts = flattenPoints->GetDataBuffer();
// Transform the newly added points.
matrix->Transform(pts + index, count);
isFirstPoint = FALSE;
}
break;
}
}
// This is the end of the current subpath. Close subpath
// if necessary.
if(isClosed)
{
BYTE* typeBuffer = flattenTypes->GetDataBuffer();
INT lastCount = flattenTypes->GetCount();
typeBuffer[lastCount - 1] |= PathPointTypeCloseSubpath;
}
}
if(ptsBuffer)
GpFree(ptsBuffer);
return status;
}
/**************************************************************************\
*
* GpXPathIterator class
*
* 11/08/1999 ikkof
* Created it.
*
\**************************************************************************/
GpXPathIterator::GpXPathIterator(GpXPath* xpath)
{
Initialize();
if(xpath && xpath->IsValid())
{
TotalCount = xpath->GetPointCount();
XPoints.SetData(
xpath->GetPathPoints(),
xpath->GetPointDimension(),
TotalCount,
FALSE // Don't copy data.
);
Types = xpath->GetPathTypes();
}
// Check if this path is valid.
if(
XPoints.Data
&& XPoints.Dimension > 0
&& XPoints.Count > 0
&& Types
&& TotalCount > 0
)
{
SetValid(TRUE);
}
}
VOID
GpXPathIterator::Initialize()
{
// XPath = NULL;
Types = NULL;
TotalCount = 0;
Index = 0;
SubpathStartIndex = 0;
SubpathEndIndex = 0;
TypeStartIndex = 0;
TypeEndIndex = 0;
SetValid(FALSE);
}
INT
GpXPathIterator::Enumerate(GpXPoints* xpoints, BYTE* types)
{
if(!IsValid())
return 0;
ASSERT(xpoints && types);
if(xpoints == NULL || types == NULL)
return 0;
INT inputSize = xpoints->Dimension*xpoints->Count;
ASSERT(xpoints->Data && inputSize > 0);
if(xpoints->Data == NULL || inputSize <= 0)
return 0;
if(Index >= TotalCount)
return 0;
// Make sure the resultant dimension is the same as
// the internal dimension.
INT dimension = XPoints.Dimension;
if(xpoints->Dimension != dimension)
{
xpoints->Dimension = dimension;
xpoints->Count = inputSize/dimension;
}
INT number = min(TotalCount - Index, xpoints->Count);
GpMemcpy(
xpoints->Data,
XPoints.Data + Index*dimension,
number*dimension*sizeof(REALD));
GpMemcpy(types, Types + Index, number*sizeof(BYTE));
Index += number;
return number;
}
INT
GpXPathIterator::NextSubpath(INT* startIndex, INT* endIndex, BOOL *isClosed)
{
if(!IsValid())
return 0;
INT count = TotalCount;
if(SubpathEndIndex >= count - 1)
return 0;
const BYTE* types = Types;
INT i;
// Set the starting index of the current subpath.
if(SubpathEndIndex == 0)
{
SubpathStartIndex = 0;
i = 1;
}
else
{
SubpathStartIndex = SubpathEndIndex + 1;
SubpathEndIndex = SubpathStartIndex;
i = SubpathStartIndex + 1;
}
BOOL hasData = FALSE;
INT segmentCount = 0;
while(i < count)
{
// Do the move segments.
segmentCount = 0;
while(i < count && (types[i] & PathPointTypePathTypeMask) == PathPointTypeStart)
{
segmentCount++;
if(hasData)
{
break;
}
else
{
SubpathStartIndex = i;
SubpathEndIndex = SubpathStartIndex;
i++;
}
}
if(segmentCount > 0 && hasData)
{
SubpathEndIndex = i - 1;
break;
}
// Do the non-move segments.
segmentCount = 0;
BYTE nextType = types[i] & PathPointTypePathTypeMask;
while(i < count && (types[i] & PathPointTypePathTypeMask) == nextType)
{
i++;
segmentCount++;
}
if(segmentCount > 0)
{
hasData = TRUE;
}
}
*startIndex = SubpathStartIndex;
if(i >= count)
SubpathEndIndex = count - 1; // The last subpath.
*endIndex = SubpathEndIndex;
segmentCount = SubpathEndIndex - SubpathStartIndex + 1;
if(segmentCount <= 1) // Start and end point is the same.
segmentCount = 0;
if(segmentCount > 1)
{
// If there is the close flag or the start and end points match,
// this subpath is closed.
if(
(types[SubpathEndIndex] & PathPointTypeCloseSubpath)
|| XPoints.AreEqualPoints(SubpathStartIndex, SubpathEndIndex)
)
{
*isClosed = TRUE;
}
else
*isClosed = FALSE;
}
else
*isClosed = FALSE;
// Set the current index to the starting index of the current subpath.
Index = SubpathStartIndex;
// Set the start and end index of type to be the starting index of
// the current subpath. NextPathType() will start from the
// beginning of the current subpath.
TypeStartIndex = TypeEndIndex = SubpathStartIndex;
return segmentCount;
}
INT
GpXPathIterator::EnumerateSubpath(
GpXPoints* xpoints,
BYTE* types
)
{
if(!IsValid())
return 0;
ASSERT(xpoints && types);
if(xpoints == NULL || types == NULL)
return 0;
INT inputSize = xpoints->Dimension*xpoints->Count;
ASSERT(xpoints->Data && inputSize > 0);
if(xpoints->Data == NULL || inputSize <= 0)
return 0;
if(Index > SubpathEndIndex)
return 0;
// Make sure the resultant dimension is the same as
// the internal dimension.
INT dimension = XPoints.Dimension;
if(xpoints->Dimension != dimension)
{
xpoints->Dimension = dimension;
xpoints->Count = inputSize/dimension;
}
INT number = min(SubpathEndIndex - Index + 1, xpoints->Count);
GpMemcpy(
xpoints->Data,
XPoints.Data + Index*dimension,
number*dimension*sizeof(REALD));
GpMemcpy(types, Types + Index, number*sizeof(BYTE));
Index += number;
return number;
}
INT
GpXPathIterator::NextPathType(
BYTE* pathType,
INT* startIndex,
INT* endIndex
)
{
if(!IsValid())
return 0;
if(TypeEndIndex >= SubpathEndIndex)
return 0; // There is no more segment in the current subpath.
INT count = SubpathEndIndex + 1; // Limit for the ending index.
const BYTE* types = Types;
TypeStartIndex = TypeEndIndex;
INT i = TypeStartIndex;
INT segmentCount = 0;
i++; // Go to the next point.
while(i < count)
{
// Do the move segments.
segmentCount = 0;
while(i < count && (types[i] & PathPointTypePathTypeMask) == PathPointTypeStart)
{
// Move the start and end index.
TypeStartIndex = i;
TypeEndIndex = TypeStartIndex;
i++;
segmentCount++;
}
// Do the non-move segments.
segmentCount = 0;
BYTE nextType = types[i] & PathPointTypePathTypeMask;
while(i < count && (types[i] & PathPointTypePathTypeMask) == nextType)
{
i++;
segmentCount++;
}
if(segmentCount > 0)
{
TypeEndIndex = TypeStartIndex + segmentCount;
*pathType = nextType;
break;
}
}
*startIndex = TypeStartIndex;
*endIndex = TypeEndIndex;
segmentCount = TypeEndIndex - TypeStartIndex + 1;
if(segmentCount <= 1)
{
// Start and End type index is the same. This means there is
// no more segment left.
segmentCount = 0;
}
// Set the current index to the starting index of the current subpath.
Index = TypeStartIndex;
return segmentCount;
}
INT
GpXPathIterator::EnumeratePathType(
GpXPoints* xpoints,
BYTE* types
)
{
if(!IsValid())
return 0;
ASSERT(xpoints && types);
if(xpoints == NULL || types == NULL)
return 0;
INT inputSize = xpoints->Dimension*xpoints->Count;
ASSERT(xpoints->Data && inputSize > 0);
if(xpoints->Data == NULL || inputSize <= 0)
return 0;
if(Index > TypeEndIndex)
return 0;
// Make sure the resultant dimension is the same as
// the internal dimension.
INT dimension = XPoints.Dimension;
if(xpoints->Dimension != dimension)
{
xpoints->Dimension = dimension;
xpoints->Count = inputSize/dimension;
}
INT number = min(TypeEndIndex - Index + 1, xpoints->Count);
GpMemcpy(
xpoints->Data,
XPoints.Data + Index*dimension,
number*dimension*sizeof(REALD));
GpMemcpy(types, Types + Index, number*sizeof(BYTE));
Index += number;
return number;
}