/**************************************************************************\
*
* Copyright (c) 1999  Microsoft Corporation
*
* Module Name:
*
*   PathWidener.hpp
*
* Abstract:
*
*   Class used for Path widening
*
* Revision History:
*
*   11/24/99 ikkof
*       Created it.
*
\**************************************************************************/

#ifndef _PATHWIDENER_HPP
#define _PATHWIDENER_HPP

enum GpLineCapMode
{
    LineCapDefaultMode = 0,
    LineCapDashMode = 1
};

class GpPathWidener
{
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 ? ObjectTagPathWidener : ObjectTagInvalid;
    }

public:

    GpPathWidener(
        const GpPointF* points,
        const BYTE* types,
        INT count,
        const DpPen* pen,
        const GpMatrix* matrix,
        REAL dpiX,
        REAL dpiY,
        BOOL isAntiAliased,
        BOOL isInsetPen = FALSE
        )
    {
        Initialize(
            points, 
            types, 
            count, 
            pen, 
            matrix, 
            dpiX, 
            dpiY,
            isAntiAliased, 
            isInsetPen
        );
    }
    
    GpPathWidener(
        GpPath *path,
        const DpPen* pen,
        const GpMatrix* matrix,
        REAL dpiX,
        REAL dpiY,
        BOOL isAntiAliased,
        BOOL isInsetPen = FALSE
        )
    {
        const GpPointF* points = path->GetPathPoints();
        const BYTE* types = path->GetPathTypes();
        INT count = path->GetPointCount();

        Initialize(
            points, 
            types, 
            count, 
            pen, 
            matrix, 
            dpiX, 
            dpiY,
            isAntiAliased, 
            isInsetPen
        );
    }

    GpPathWidener(
        const GpPointF* points,
        const BYTE* types,
        INT count,
        const DpPen* pen,
        const GpMatrix* matrix,
        REAL dpiX,
        REAL dpiY,
        BOOL isAntiAliased,
        BYTE* centerTypesBuffer,
        GpPointF* centerPointsBuffer,
        GpPointF* gradientsBuffer,
        GpPointF* normalsBuffer,
        BYTE* leftTypesBuffer,
        GpPointF* leftPointsBuffer,
        BYTE* rightTypesBuffer,
        GpPointF* rightPointsBuffer,
        INT bufferCount,
        BOOL isInsetPen = FALSE
        ) : CenterTypes(centerTypesBuffer, bufferCount),
            CenterPoints(centerPointsBuffer, bufferCount),
            Gradients(gradientsBuffer, bufferCount),
            Normals(normalsBuffer, bufferCount),
            LeftTypes(leftTypesBuffer, bufferCount),
            LeftPoints(leftPointsBuffer, bufferCount),
            RightTypes(rightTypesBuffer, bufferCount),
            RightPoints(rightPointsBuffer, bufferCount)
    {
        Initialize(points, types, count, pen, matrix, dpiX, dpiY,
            isAntiAliased, isInsetPen);
    }            

    ~GpPathWidener()
    {
        SetValid(FALSE);    // so we don't use a deleted object
    }

    GpStatus Widen(
        DynPointFArray* widenedPoints,
        DynByteArray* widenedTypes
        );
    
    GpStatus Widen(GpPath **path);

    BOOL IsValid() const
    {
        ASSERT((Tag == ObjectTagPathWidener) || (Tag == ObjectTagInvalid)); 
    #if DBG
        if (Tag == ObjectTagInvalid)
        {
            WARNING1("Invalid PathWidener");
        }
    #endif

        return (Tag == ObjectTagPathWidener);
    }

    REAL GetPenDelta();


protected:
    VOID Initialize(
        const GpPointF* points,
        const BYTE* types,
        INT count,
        const DpPen* pen,
        const GpMatrix* matrix,
        REAL dpiX,
        REAL dpiY,
        BOOL isAntiAliased,
        BOOL isInsetPen = FALSE
        );

    GpStatus WidenSubpath(
        DynPointFArray* widenedPoints,
        DynByteArray* widenedTypes,
        REAL leftWidth,
        REAL rightWidth,
        INT startIndex,
        INT endIndex,
        BOOL isClosed,
        GpLineCap startCap,
        GpLineCap endCap,
        BOOL useBevelJoinInside
        );

    GpStatus CalculateGradients(
        INT startIndex,
        INT endIndex
        );

    GpStatus CalculateNormals(
        REAL leftWidth,
        REAL rightWidth
        );

    GpStatus SetPolygonJoin(
        REAL leftWidth,
        REAL rightWidth,
        BOOL isAntialiased
        );

    GpStatus SetStartCapInset(
        REAL inset
        )
    {
        Inset1 = inset;

        return Ok;
    }

    GpStatus SetEndCapInset(
        REAL inset
        )
    {
        Inset2 = inset;

        return Ok;
    }
        
    VOID WidenFirstPoint(
        REAL leftWidth,
        REAL rightWidth,
        GpLineJoin lineJoin,
        REAL miterLimit2,
        GpPointF* leftPoints,
        BYTE* leftTypes,
        INT* addedLeftCount,
        GpPointF* rightPoints,
        BYTE* rightTypes,
        INT* addedRightCount,
        GpPointF* leftEndPt,
        GpPointF* rightEndPt,
        const GpPointF* grad,
        const GpPointF* norm,
        const GpPointF* dataPoints,
        INT dataCount,
        GpPointF* lastPt,
        const REAL* firstInsets,
        INT flag
    );

    GpStatus
    WidenEachPathType(
        BYTE pathType,
        REAL leftWidth,
        REAL rightWidth,
        GpLineJoin lineJoin,
        REAL miterLimit2,
        GpPointF* leftPoints,
        BYTE* leftTypes,
        INT* addedLeftCount,
        GpPointF* rightPoints,
        BYTE* rightTypes,
        INT* addedRightCount,
        const GpPointF* grad,
        const GpPointF* norm,
        const GpPointF* dataPoints,
        INT dataCount,
        GpPointF* lastPt,
        const REAL* lastInsets,
        INT flag
        );

    GpStatus
    WidenLinePoints(
        REAL leftWidth,
        REAL rightWidth,
        GpLineJoin lineJoin,
        REAL miterLimit2,
        GpPointF* leftPoints,
        BYTE* leftTypes,
        INT* addedLeftCount,
        GpPointF* rightPoints,
        BYTE* rightTypes,
        INT* addedRightCount,
        const GpPointF* grad,
        const GpPointF* norm,
        const GpPointF* dataPoints,
        INT dataCount,
        GpPointF* lastPt,
        const REAL* lastInsets,
        INT flag
        );

    GpStatus
    WidenBezierPoints(
        REAL leftWidth,
        REAL rightWidth,
        GpLineJoin lineJoin,
        REAL miterLimit2,
        GpPointF* leftPoints,
        BYTE* leftTypes,
        INT* addedLeftCount,
        GpPointF* rightPoints,
        BYTE* rightTypes,
        INT* addedRightCount,
        const GpPointF* grad,
        const GpPointF* norm,
        const GpPointF* dataPoints,
        INT dataCount,
        GpPointF* lastPt,
        const REAL* lastInsets,
        INT flag
        );
    
    GpStatus SetCaps(
        GpLineCap startCap,
        GpLineCap endCap,
        const GpPointF& startPoint,
        const GpPointF& startGrad,
        const GpPointF& startNorm,
        const GpPointF& endPoint,
        const GpPointF& endGrad,
        const GpPointF& endNorm,
        REAL leftWidth,
        REAL rightWidth,
        const GpPointF *points,
        INT pointCount
        );

    GpStatus SetCustomFillCaps(
        GpCustomLineCap* customStartCap,
        GpCustomLineCap* customEndCap,
        const GpPointF& startPoint,
        const GpPointF& endPoint,
        REAL leftWidth,
        REAL rightWidth,
        const GpPointF *centerPoints,
        const BYTE *centerTypes,
        INT centerPointCount,
        DynPointFArray *startCapPoints,
        DynPointFArray *endCapPoints,
        DynByteArray *startCapTypes,
        DynByteArray *endCapTypes
        );

    GpStatus SetCustomStrokeCaps(
        GpCustomLineCap* customStartCap,
        GpCustomLineCap* customEndCap,
        const GpPointF& startPoint,
        const GpPointF& endPoint,
        REAL leftWidth,
        REAL rightWidth,
        const GpPointF *centerPoints,
        const BYTE *centerTypes,
        INT centerPointCount,
        DynPointFArray *startCapPoints,
        DynPointFArray *endCapPoints,
        DynByteArray *startCapTypes,
        DynByteArray *endCapTypes
        );

    GpStatus SetRoundCap(
        const GpPointF& point,
        const GpPointF& grad,
        BOOL isStartCap,
        REAL leftWidth,
        REAL rightWidth
        );
    
    GpStatus SetDoubleRoundCap(
        const GpPointF& point,
        const GpPointF& grad,
        BOOL isStartCap,
        REAL leftWidth,
        REAL rightWidth
        );

    GpStatus SetTriangleCap(
        const GpPointF& point,
        const GpPointF& grad,
        BOOL isStartCap,
        REAL leftWidth,
        REAL rightWidth,
        const GpPointF *points,
        INT pointCount
        );

    GpStatus CombineSubpathOutlines(
        DynPointFArray* widenedPoints,
        DynByteArray* widenedTypes,
        BOOL isClosed,
        BOOL closeStartCap = FALSE,
        BOOL closeEndCap = FALSE
        );
    
    GpStatus CombineClosedCaps(
        DynPointFArray* widenedPoints,
        DynByteArray* widenedTypes,
        DynPointFArray *daStartCapPoints,
        DynPointFArray *daEndCapPoints,
        DynByteArray *daStartCapTypes,
        DynByteArray *daEndCapTypes
        );

    GpStatus AddCompoundCaps(
        DynPointFArray* widenedPoints,
        DynByteArray* widenedTypes,
        REAL leftWidth,
        REAL rightWidth,
        INT startIndex,
        INT endIndex,
        GpLineCap startCap,
        GpLineCap endCap
        );
    
    REAL GetSubpathPenMiterDelta(BOOL isClosed);

protected:
    DpPathIterator Iterator;
	DynByteArray CenterTypes;
    DynPointFArray CenterPoints;
    DynPointFArray Gradients;
    DynPointFArray Normals;

    DynByteArray LeftTypes;
    DynPointFArray LeftPoints;
    DynByteArray RightTypes;
    DynPointFArray RightPoints;

    BOOL InsetPenMode;           // are we doing inset pen using a center pen.
    const DpPen* Pen;
    GpMatrix XForm;
    GpMatrix InvXForm;
    REAL UnitScale;             // Scale factor for Page to Device units
    REAL StrokeWidth;
    REAL OriginalStrokeWidth;   // StrokeWidth is clamped to a minimum value
                                // but OriginalStrokeWidth is actual transformed
                                // pen width.
    REAL MinimumWidth;
    REAL MaximumWidth;
    BOOL IsAntiAliased;
    BOOL NeedsToTransform;
    BOOL NeedsToAdjustNormals;

    REAL DpiX;
    REAL DpiY;

    DynPointFArray JoinPolygonPoints;
    DynRealArray JoinPolygonAngles;

    // CapTypes1 and CapPoints1 are used for the start cap and left join.

    DynByteArray CapTypes1;
    DynPointFArray CapPoints1;
    REAL Inset1;    // Inset value for the starting position.
    
    // CapTypes2 and CapPoints2 are used for the end cap and right join.

    DynByteArray CapTypes2;
    DynPointFArray CapPoints2;
    REAL Inset2;    // Inset value for the ending position.
};

#endif