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

421 lines
9.9 KiB
C++

/**************************************************************************\
*
* Copyright (c) 1998 Microsoft Corporation
*
* Module Name:
*
* XBezier.hpp
*
* Abstract:
*
* Interface of GpXBezier and its DDA classes
*
* Revision History:
*
* 11/05/1999 ikkof
* Created it.
*
\**************************************************************************/
#ifndef _XBEZIER_HPP
#define _XBEZIER_HPP
#define FLATNESS_LIMIT 0.25
#define DISTANCE_LIMIT 2.0
#define BZ_BUFF_SIZE 32
class GpXPoints
{
friend class GpXPath;
friend class GpXBezier;
public:
INT Dimension;
INT Count; // Number of Points
REALD* Data;
protected:
BOOL IsDataAllocated;
public:
GpXPoints()
{
Initialize();
}
// When XPoints is created from a given data with copyData = FALSE,
// the caller is responsible for deleting the data after XPoints is no longer
// used.
GpXPoints(REALD* data, INT dimension, INT count, BOOL copyData = TRUE)
{
Initialize();
SetData(data, dimension, count, copyData);
}
GpXPoints(GpPointF* points, INT count)
{
Initialize();
if(points && count > 0)
{
Data = (REALD*) GpMalloc(2*count*sizeof(REALD));
if(Data)
{
INT i = 0, j = 0;
while(j < count)
{
Data[i++] = points[j].X;
Data[i++] = points[j].Y;
j++;
}
Dimension = 2;
Count = count;
IsDataAllocated = TRUE;
}
}
}
GpXPoints(GpPointD* points, INT count)
{
Initialize();
if(points && count > 0)
{
Data = (REALD*) GpMalloc(2*count*sizeof(REALD));
if(Data)
{
INT i = 0, j = 0;
while(j < count)
{
Data[i++] = points[j].X;
Data[i++] = points[j].Y;
j++;
}
Dimension = 2;
Count = count;
IsDataAllocated = TRUE;
}
}
}
REALD* GetData() {return Data;}
// When XPoints is created from a given data with copyData = FALSE,
// the caller is responsible for deleting the data after XPoints is no longer
// used.
GpStatus
SetData(REALD* data, INT dimension, INT count, BOOL copyData = TRUE)
{
GpStatus status = Ok;
if(data && dimension > 0 || count > 0)
{
REALD* newData = NULL;
if(copyData)
{
INT totalSize = dimension*count*sizeof(REALD);
if(IsDataAllocated)
newData = (REALD*) GpRealloc(Data, totalSize);
else
newData = (REALD*) GpMalloc(totalSize);
if(newData)
{
GpMemcpy(newData, data, totalSize);
IsDataAllocated;
}
else
status = OutOfMemory;
}
else
{
if(Data && IsDataAllocated)
GpFree(Data);
newData = data;
IsDataAllocated = FALSE;
}
if(status == Ok)
{
Dimension = dimension;
Count = count;
Data = newData;
}
}
else
status = InvalidParameter;
return status;
}
GpStatus Transform(const GpMatrix* matrix);
BOOL AreEqualPoints(INT index1, INT index2)
{
if(index1 < 0 || index1 >= Count
|| index2 < 0 || index2 >= Count || Data == NULL)
return FALSE; // either index is out of the range or no data.
BOOL areEqual = TRUE;
if(index1 != index2)
{
REALD* data1 = Data + index1*Dimension;
REALD* data2 = Data + index2*Dimension;
INT k = 0;
while(k < Dimension && areEqual)
{
if(*data1++ != *data2++)
areEqual = FALSE;
k++;
}
}
return areEqual;
}
static GpStatus
GpXPoints::TransformPoints(
const GpMatrix* matrix,
REALD* data,
INT dimension,
INT count
);
~GpXPoints()
{
if(Data && IsDataAllocated)
GpFree(Data);
}
protected:
VOID Initialize()
{
Dimension = 0;
Count = 0;
Data = NULL;
IsDataAllocated = FALSE;
}
};
//********************************************************
// GpXBezierDDA class
//********************************************************
class GpXBezierConstants
{
friend class GpXBezierDDA;
private:
REALD H[7][7]; // Half step
REALD D[7][7]; // Double step
REALD S[7][7]; // One step
REALD F[7][7]; // Polynomical transform.
REALD H6[7][7]; // Poly to Bez transform in 6th order.
REALD G6[7][7]; // Bez to Poly transform in 6th order.
public:
GpXBezierConstants();
};
class GpXBezierDDA
{
protected:
GpXBezierConstants C;
protected:
REALD T;
REALD Dt;
REALD Q[16];
REALD P[16];
INT NthOrder;
INT Dimension;
INT NSteps;
REAL FlatnessLimit;
REAL DistanceLimit;
public:
public:
GpXBezierDDA() { Initialize(); }
GpXBezierDDA(
const GpXPoints& xpoints,
REAL flatnessLimit = FLATNESS_LIMIT,
REAL distanceLimit = DISTANCE_LIMIT
)
{
Initialize();
SetBezier(xpoints, flatnessLimit, distanceLimit);
}
VOID
SetBezier(
const GpXPoints& xpoints,
REAL flatnessLimit = FLATNESS_LIMIT,
REAL distanceLimit = DISTANCE_LIMIT
);
INT GetSteps() { return NSteps; }
VOID InitDDA(GpPointF* pt);
VOID HalveStepSize();
VOID DoubleStepSize();
VOID FastShrinkStepSize(INT shift);
VOID TakeStep();
BOOL NeedsSubdivide(REAL itsFlatnessLimit);
BOOL GetNextPoint(GpPointF* pt);
VOID MoveForward();
INT GetControlPoints(GpXPoints* xpoints);
protected:
VOID Initialize();
VOID SetPolynomicalCoefficients();
VOID TakeConvergentStep();
BOOL Get2DDistanceVector(REALD* dx, REALD* dy, INT from, INT to);
};
//************************************
// XBezier class
//************************************
#define NthOrderMax 6
class GpXBezier
{
private:
// We now use an ObjectTag to determine if the object is valid
// instead of using a BOOL. This is much more robust and helps
// with debugging. It also enables us to version our objects
// more easily with a version number in the ObjectTag.
ObjectTag Tag; // Keep this as the 1st value in the object!
protected:
VOID SetValid(BOOL valid)
{
Tag = valid ? ObjectTagGpBezier : ObjectTagInvalid;
}
public:
BOOL IsValid() const
{
ASSERT((Tag == ObjectTagGpBezier) || (Tag == ObjectTagInvalid));
#if DBG
if (Tag == ObjectTagInvalid)
{
WARNING1("Invalid GpBezier");
}
#endif
return (Tag == ObjectTagGpBezier);
}
GpXBezier()
{
Initialize();
}
GpXBezier(INT order, const GpPointF* points, INT count)
{
Initialize();
SetValid(SetBeziers(order, points, count));
}
GpXBezier(INT order, const GpXPoints& xpoints)
{
Initialize();
SetValid(SetBeziers(order, xpoints));
}
~GpXBezier();
GpStatus SetBeziers(INT order, const GpPointF* points, INT count);
GpStatus SetBeziers(INT order, const GpXPoints& xpoints);
virtual INT GetControlCount() {return Count;}
virtual VOID GetBounds(GpMatrix* matrix, GpRect* bounds);
virtual VOID Transform(GpMatrix* matrix);
virtual GpStatus Flatten(
DynPointFArray* flattenPts,
const GpMatrix* matrix);
protected:
VOID Initialize()
{
NthOrder = 0;
Dimension = 0;
Count = 0;
Data = NULL;
FlatnessLimit = FLATNESS_LIMIT;
DistanceLimit = DISTANCE_LIMIT;
SetValid(TRUE);
}
GpStatus
FlattenEachBezier(
DynPointFArray* flattenPts,
GpXBezierDDA& dda,
BOOL isFirstBezier,
const GpMatrix* matrix,
const REALD* bezierData
);
GpStatus
Get2DPoints(
GpPointF* points,
INT count,
const REALD* dataPoints,
const GpMatrix* matrix = NULL);
GpStatus CheckInputData(const GpPointF* points, INT count)
{
GpStatus status = InvalidParameter;
if(NthOrder > 0)
{
if(count > NthOrder)
{
INT reminder = count % NthOrder;
if(reminder == 1 && points !=NULL)
status = Ok;
}
}
else // NthOrder <= 0
{
if(count > 1 && points != NULL)
{
if(count <= NthOrderMax + 1)
{
NthOrder = count - 1;
status = Ok;
}
}
}
return status;
}
protected: // GDI+ INTERNAL
// Following are the two values to determin the flatness.
REAL FlatnessLimit; // The maximum flateness.
REAL DistanceLimit; // The minimum distance.
private: // GDI+ INTERNAL
INT NthOrder;
INT Dimension;
INT Count;
REALD* Data;
};
#endif