PathGeometry.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Dotnetfx_Vista_SP2 / Dotnetfx_Vista_SP2 / 8.0.50727.4016 / DEVDIV / depot / DevDiv / releases / Orcas / QFE / wpf / src / Core / CSharp / System / Windows / Media / PathGeometry.cs / 1 / PathGeometry.cs

                            //---------------------------------------------------------------------------- 
//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
// Description: Implementation of the class PathGeometry 
//
//--------------------------------------------------------------------------- 
 
using System;
using MS.Internal; 
using MS.Internal.PresentationCore;
using System.ComponentModel;
using System.ComponentModel.Design.Serialization;
using System.Diagnostics; 
using System.Reflection;
using System.Collections; 
using System.Collections.Generic; 
using System.Text;
using System.Globalization; 
using System.Windows.Media;
using System.Windows.Media.Composition;
using System.Windows;
using System.Text.RegularExpressions; 
using System.Windows.Media.Animation;
using System.Windows.Markup; 
using System.Windows.Converters; 
using System.Runtime.InteropServices;
using System.Security; 
using System.Security.Permissions;
using MS.Win32;

using SR=MS.Internal.PresentationCore.SR; 
using SRID=MS.Internal.PresentationCore.SRID;
using UnsafeNativeMethods=MS.Win32.PresentationCore.UnsafeNativeMethods; 
 
namespace System.Windows.Media
{ 
    #region PathGeometryInternalFlags
    [System.Flags]
    internal enum PathGeometryInternalFlags
    { 
        None            = 0x0,
        Invalid         = 0x1, 
        Dirty           = 0x2, 
        BoundsValid     = 0x4
    } 
    #endregion

    #region PathGeometry
    ///  
    /// PathGeometry
    ///  
    [ContentProperty("Figures")] 
    public sealed partial class PathGeometry : Geometry
    { 
        #region Constructors
        /// 
        ///
        ///  
        public PathGeometry()
        { 
        } 

        ///  
        /// Constructor
        /// 
        /// A collection of figures
        public PathGeometry(IEnumerable figures) 
        {
            if (figures != null) 
            { 
                foreach (PathFigure item in figures)
                { 
                    Figures.Add(item);
                }
            }
            else 
            {
                throw new ArgumentNullException("figures"); 
 
            }
 
            SetDirty();
        }

        ///  
        /// Constructor
        ///  
        /// A collection of figures 
        /// The fill rule (OddEven or NonZero)
        /// A transformation to apply to the input 
        public PathGeometry(IEnumerable figures, FillRule fillRule, Transform transform)
        {
            Transform = transform;
            if (ValidateEnums.IsFillRuleValid(fillRule)) 
            {
                FillRule = fillRule; 
 
                if (figures != null)
                { 
                    foreach (PathFigure item in figures)
                    {
                        Figures.Add(item);
                    } 
                }
                else 
                { 
                    throw new ArgumentNullException("figures");
                } 

                SetDirty();
            }
        } 

        ///  
        /// Static "CreateFromGeometry" method which creates a new PathGeometry from the Geometry specified. 
        /// 
        ///  
        /// Geometry - The Geometry which will be used as the basis for the newly created
        /// PathGeometry.  The new Geometry will be based on the current value of all properties.
        /// 
        public static PathGeometry CreateFromGeometry(Geometry geometry) 
        {
            if (geometry == null) 
            { 
                return null;
            } 

            return geometry.GetAsPathGeometry();
        }
 
        /// 
        /// Static method which parses a PathGeometryData and makes calls into the provided context sink. 
        /// This can be used to build a PathGeometry, for readback, etc. 
        /// 
        /// 
        ///     Critical - calls code that performs an elevation.
        ///     TreatAsSafe - This method reads from a pinned byte array.
        ///
        [SecurityCritical, SecurityTreatAsSafe] 
        internal static void ParsePathGeometryData(PathGeometryData pathData, CapacityStreamGeometryContext ctx)
        { 
            if (pathData.IsEmpty()) 
            {
                return; 
            }

            unsafe
            { 
                int currentOffset = 0;
 
                fixed (byte* pbData = pathData.SerializedData) 
                {
                    // This assert is a logical correctness test 
                    Debug.Assert(pathData.Size >= currentOffset + sizeof(MIL_PATHGEOMETRY));

                    // ... while this assert tests "physical" correctness (i.e. are we running out of buffer).
                    Invariant.Assert(pathData.SerializedData.Length >= currentOffset + sizeof(MIL_PATHGEOMETRY)); 

                    MIL_PATHGEOMETRY *pPathGeometry = (MIL_PATHGEOMETRY*)pbData; 
 
                    // Move the current offset to after the Path's data
                    currentOffset += sizeof(MIL_PATHGEOMETRY); 

                    // Are there any Figures to add?
                    if (pPathGeometry->FigureCount > 0)
                    { 
                        // Allocate the correct number of Figures up front
                        ctx.SetFigureCount((int)pPathGeometry->FigureCount); 
 
                        // ... and iterate on the Figures.
                        for (int i = 0; i < pPathGeometry->FigureCount; i++) 
                        {
                            // We only expect well-formed data, but we should assert that we're not reading
                            // too much data.
                            Debug.Assert(pathData.SerializedData.Length >= currentOffset + sizeof(MIL_PATHFIGURE)); 

                            MIL_PATHFIGURE *pPathFigure = (MIL_PATHFIGURE*)(pbData + currentOffset); 
 
                            // Move the current offset to the after of the Figure's data
                            currentOffset += sizeof(MIL_PATHFIGURE); 

                            ctx.BeginFigure(pPathFigure->StartPoint,
                                            ((pPathFigure->Flags & MilPathFigureFlags.IsFillable) != 0),
                                            ((pPathFigure->Flags & MilPathFigureFlags.IsClosed) != 0)); 

                            if (pPathFigure->Count > 0) 
                            { 
                                // Allocate the correct number of Segments up front
                                ctx.SetSegmentCount((int)pPathFigure->Count); 

                                // ... and iterate on the Segments.
                                for (int j = 0; j < pPathFigure->Count; j++)
                                { 
                                    // We only expect well-formed data, but we should assert that we're not reading too much data.
                                    Debug.Assert(pathData.SerializedData.Length >= currentOffset + sizeof(MIL_SEGMENT)); 
                                    Debug.Assert(pathData.Size >= currentOffset + sizeof(MIL_SEGMENT)); 

                                    MIL_SEGMENT *pSegment = (MIL_SEGMENT*)(pbData + currentOffset); 

                                    switch (pSegment->Type)
                                    {
                                    case MIL_SEGMENT_TYPE.MilSegmentLine: 
                                        {
                                            // We only expect well-formed data, but we should assert that we're not reading too much data. 
                                            Debug.Assert(pathData.SerializedData.Length >= currentOffset + sizeof(MIL_SEGMENT_LINE)); 
                                            Debug.Assert(pathData.Size >= currentOffset + sizeof(MIL_SEGMENT_LINE));
 
                                            MIL_SEGMENT_LINE *pSegmentLine = (MIL_SEGMENT_LINE*)(pbData + currentOffset);

                                            ctx.LineTo(pSegmentLine->Point,
                                                       ((pSegmentLine->Flags & MILCoreSegFlags.SegIsAGap) == 0), 
                                                       ((pSegmentLine->Flags & MILCoreSegFlags.SegSmoothJoin) != 0));
 
                                            currentOffset += sizeof(MIL_SEGMENT_LINE); 
                                        }
                                        break; 
                                    case MIL_SEGMENT_TYPE.MilSegmentBezier:
                                        {
                                            // We only expect well-formed data, but we should assert that we're not reading too much data.
                                            Debug.Assert(pathData.SerializedData.Length >= currentOffset + sizeof(MIL_SEGMENT_BEZIER)); 
                                            Debug.Assert(pathData.Size >= currentOffset + sizeof(MIL_SEGMENT_BEZIER));
 
                                            MIL_SEGMENT_BEZIER *pSegmentBezier = (MIL_SEGMENT_BEZIER*)(pbData + currentOffset); 

                                            ctx.BezierTo(pSegmentBezier->Point1, 
                                                         pSegmentBezier->Point2,
                                                         pSegmentBezier->Point3,
                                                         ((pSegmentBezier->Flags & MILCoreSegFlags.SegIsAGap) == 0),
                                                         ((pSegmentBezier->Flags & MILCoreSegFlags.SegSmoothJoin) != 0)); 

                                            currentOffset += sizeof(MIL_SEGMENT_BEZIER); 
                                        } 
                                        break;
                                    case MIL_SEGMENT_TYPE.MilSegmentQuadraticBezier: 
                                        {
                                            // We only expect well-formed data, but we should assert that we're not reading too much data.
                                            Debug.Assert(pathData.SerializedData.Length >= currentOffset + sizeof(MIL_SEGMENT_QUADRATICBEZIER));
                                            Debug.Assert(pathData.Size >= currentOffset + sizeof(MIL_SEGMENT_QUADRATICBEZIER)); 

                                            MIL_SEGMENT_QUADRATICBEZIER *pSegmentQuadraticBezier = (MIL_SEGMENT_QUADRATICBEZIER*)(pbData + currentOffset); 
 
                                            ctx.QuadraticBezierTo(pSegmentQuadraticBezier->Point1,
                                                                  pSegmentQuadraticBezier->Point2, 
                                                                  ((pSegmentQuadraticBezier->Flags & MILCoreSegFlags.SegIsAGap) == 0),
                                                                  ((pSegmentQuadraticBezier->Flags & MILCoreSegFlags.SegSmoothJoin) != 0));

                                            currentOffset += sizeof(MIL_SEGMENT_QUADRATICBEZIER); 
                                        }
                                        break; 
                                    case MIL_SEGMENT_TYPE.MilSegmentArc: 
                                        {
                                            // We only expect well-formed data, but we should assert that we're not reading too much data. 
                                            Debug.Assert(pathData.SerializedData.Length >= currentOffset + sizeof(MIL_SEGMENT_ARC));
                                            Debug.Assert(pathData.Size >= currentOffset + sizeof(MIL_SEGMENT_ARC));

                                            MIL_SEGMENT_ARC *pSegmentArc = (MIL_SEGMENT_ARC*)(pbData + currentOffset); 

                                            ctx.ArcTo(pSegmentArc->Point, 
                                                      pSegmentArc->Size, 
                                                      pSegmentArc->XRotation,
                                                      (pSegmentArc->LargeArc != 0), 
                                                      (pSegmentArc->Sweep == 0) ? SweepDirection.Counterclockwise : SweepDirection.Clockwise,
                                                      ((pSegmentArc->Flags & MILCoreSegFlags.SegIsAGap) == 0),
                                                      ((pSegmentArc->Flags & MILCoreSegFlags.SegSmoothJoin) != 0));
 
                                            currentOffset += sizeof(MIL_SEGMENT_ARC);
                                        } 
                                        break; 
                                    case MIL_SEGMENT_TYPE.MilSegmentPolyLine:
                                    case MIL_SEGMENT_TYPE.MilSegmentPolyBezier: 
                                    case MIL_SEGMENT_TYPE.MilSegmentPolyQuadraticBezier:
                                        {
                                            // We only expect well-formed data, but we should assert that we're not reading too much data.
                                            Debug.Assert(pathData.SerializedData.Length >= currentOffset + sizeof(MIL_SEGMENT_POLY)); 
                                            Debug.Assert(pathData.Size >= currentOffset + sizeof(MIL_SEGMENT_POLY));
 
                                            MIL_SEGMENT_POLY *pSegmentPoly = (MIL_SEGMENT_POLY*)(pbData + currentOffset); 

                                            Debug.Assert(pSegmentPoly->Count <= Int32.MaxValue); 

                                            if (pSegmentPoly->Count > 0)
                                            {
                                                List points = new List((int)pSegmentPoly->Count); 

                                                // We only expect well-formed data, but we should assert that we're not reading too much data. 
                                                Debug.Assert(pathData.SerializedData.Length >= 
                                                             currentOffset +
                                                             sizeof(MIL_SEGMENT_POLY) + 
                                                             (int)pSegmentPoly->Count * sizeof(Point));
                                                Debug.Assert(pathData.Size >=
                                                             currentOffset +
                                                             sizeof(MIL_SEGMENT_POLY) + 
                                                             (int)pSegmentPoly->Count * sizeof(Point));
 
                                                Point* pPoint = (Point*)(pbData + currentOffset + sizeof(MIL_SEGMENT_POLY)); 

                                                for (uint k = 0; k < pSegmentPoly->Count; k++) 
                                                {
                                                    points.Add(*pPoint);
                                                    pPoint++;
                                                } 

                                                switch (pSegment->Type) 
                                                { 
                                                case MIL_SEGMENT_TYPE.MilSegmentPolyLine:
                                                    ctx.PolyLineTo(points, 
                                                                   ((pSegmentPoly->Flags & MILCoreSegFlags.SegIsAGap) == 0),
                                                                   ((pSegmentPoly->Flags & MILCoreSegFlags.SegSmoothJoin) != 0));
                                                    break;
                                                case MIL_SEGMENT_TYPE.MilSegmentPolyBezier: 
                                                    ctx.PolyBezierTo(points,
                                                                     ((pSegmentPoly->Flags & MILCoreSegFlags.SegIsAGap) == 0), 
                                                                     ((pSegmentPoly->Flags & MILCoreSegFlags.SegSmoothJoin) != 0)); 
                                                    break;
                                                case MIL_SEGMENT_TYPE.MilSegmentPolyQuadraticBezier: 
                                                    ctx.PolyQuadraticBezierTo(points,
                                                                   ((pSegmentPoly->Flags & MILCoreSegFlags.SegIsAGap) == 0),
                                                                   ((pSegmentPoly->Flags & MILCoreSegFlags.SegSmoothJoin) != 0));
                                                    break; 
                                                }
                                            } 
 
                                            currentOffset += sizeof(MIL_SEGMENT_POLY) + (int)pSegmentPoly->Count * sizeof(Point);
                                        } 
                                        break;
#if DEBUG
                                    case MIL_SEGMENT_TYPE.MilSegmentNone:
                                        throw new System.InvalidOperationException(); 
                                    default:
                                        throw new System.InvalidOperationException(); 
#endif 
                                    }
                                } 
                            }
                        }
                    }
                } 
            }
        } 
 
        #endregion
 
        /// 
        /// Implementation of Freezable.OnChanged.
        /// 
        protected override void OnChanged() 
        {
            SetDirty(); 
 
            base.OnChanged();
        } 

        #region GetTransformedFigureCollection
        internal override PathFigureCollection GetTransformedFigureCollection(Transform transform)
        { 
            // Combine the transform argument with the internal transform
            Matrix matrix = GetCombinedMatrix(transform); 
 
            // Get the figure collection
            PathFigureCollection result; 

            if (matrix.IsIdentity)
            {
                // There is no need to transform, return the figure collection 
                result = Figures;
                if (result == null) 
                { 
                    result = new PathFigureCollection();
                } 
            }
            else
            {
                // Return a transformed copy of the figure collection 
                result = new PathFigureCollection();
                PathFigureCollection figures = Figures; 
                int count = figures != null ? figures.Count : 0; 
                for (int i = 0; i < count; ++i)
                { 
                    PathFigure figure = figures.Internal_GetItem(i);
                    result.Add(figure.GetTransformedCopy(matrix));
                }
            } 

            Debug.Assert(result != null); 
            return result; 
        }
        #endregion 

        #region PathFigure/Geometry
        /// 
        /// 
        /// 
        public void AddGeometry(Geometry geometry) 
        { 
            if (geometry == null)
            { 
                throw new System.ArgumentNullException("geometry");
            }

            if (geometry.IsEmpty()) 
            {
                return; 
            } 

            PathFigureCollection figureCollection = geometry.GetPathFigureCollection(); 
            Debug.Assert(figureCollection != null);

            PathFigureCollection figures = Figures;
 
            if (figures == null)
            { 
                figures = Figures = new PathFigureCollection(); 
            }
 
            for (int i = 0; i < figureCollection.Count; ++i)
            {
                figures.Add(figureCollection.Internal_GetItem(i));
            } 
        }
 
        #endregion 

        #region FigureList class 
        ///
        /// List of figures, populated by callbacks from unmanaged code
        ///
        internal class FigureList 
        {
            /// 
            /// Constructor 
            ///
            internal FigureList() 
            {
                _figures = new PathFigureCollection();
            }
 
            ///
            /// Figures - the array of figures 
            /// 
            internal PathFigureCollection Figures
            { 
                get
                {
                    return _figures;
                } 
            }
 
            #endregion FigureList class 

            /// 
            /// Callback method, used for adding a figure to the list
            ///
            ///
            /// The figure is filled 
            ///
            /// 
            /// The figure is closed 
            ///
            /// 
            /// The array of the figure's defining points
            ///
            ///
            /// The size of the points array 
            ///
            /// 
            /// The array of the figure's defining segment types 
            ///
            /// 
            /// The size of the types array
            ///
            /// 
            ///    Critical: This code is critical because it is an unsafe code block 
            ///  
            [SecurityCritical] 
            internal unsafe void AddFigureToList(bool isFilled, bool isClosed, MilPoint2F* pPoints, UInt32 pointCount, byte* pSegTypes, UInt32 segmentCount) 
            {
                if (pointCount >=1 && segmentCount >= 1) 
                {
                    PathFigure figure = new PathFigure();

                    figure.IsFilled = isFilled; 
                    figure.StartPoint = new Point(pPoints->X, pPoints->Y);
 
                    int pointIndex = 1; 
                    int sameSegCount = 0;
 
                    for (int segIndex=0; segIndex pointCount)
                            {
                                throw new System.InvalidOperationException(SR.Get(SRID.PathGeometry_InternalReadBackError));
                            } 

                            if (sameSegCount>1) 
                            { 
                                PointCollection ptCollection = new PointCollection();
                                for (int i=0; i pointCount)
                            { 
                                throw new System.InvalidOperationException(SR.Get(SRID.PathGeometry_InternalReadBackError)); 
                            }
 
                            if (sameSegCount>1)
                            {
                                PointCollection ptCollection = new PointCollection();
                                for (int i=0; i
            /// The array of figures
            ///  
            internal PathFigureCollection _figures;
        }; 
 
        internal unsafe delegate void AddFigureToListDelegate(bool isFilled, bool isClosed, MilPoint2F *pPoints, UInt32 pointCount, byte *pTypes, UInt32 typeCount);
 
        #region GetPointAtFractionLength
        /// 
        /// 
        /// 
        ///     Critical - calls MilUtility_GetPointAtLengthFraction that performs an elevation.
        ///     PublicOK - This computes the location of the point x% along the way of a path (and its direction). 
        ///                Progress is normalized between 0 and 1.  This math is considered safe. 
        ///
        [SecurityCritical] 
        public void GetPointAtFractionLength(
            double progress,
            out Point point,
            out Point tangent) 
        {
            if (IsEmpty()) 
            { 
                point = new Point();
                tangent = new Point(); 
                return;
            }

            unsafe 
            {
                PathGeometryData pathData = GetPathGeometryData(); 
 
                fixed (byte *pbPathData = pathData.SerializedData)
                { 
                    Debug.Assert(pbPathData != (byte*)0);

                    HRESULT.Check(MilCoreApi.MilUtility_GetPointAtLengthFraction(
                        &pathData.Matrix, 
                        pathData.FillRule,
                        pbPathData, 
                        pathData.Size, 
                        progress,
                        out point, 
                        out tangent));
                }
            }
        } 
        #endregion
 
        #region Combine 
        /// 
        /// Returns the result of a Boolean combination of two Geometry objects. 
        /// 
        /// The first Geometry object
        /// The second Geometry object
        /// The mode in which the objects will be combined 
        /// A transformation to apply to the result, or null
        /// The computational error tolerance 
        /// The way the error tolerance will be interpreted - relative or absolute 
        ///
        ///     Critical - calls code that perfoms an elevation ( MilUtility_PathGeometryCombine ) 
        ///     TreatAsSafe - the net effect of this function is to return a new PathGeometry given a transform and a combine operation.
        ///                          Considered safe.
        ///
        ///                          Although we call code within an unsafe block - managed objects are used to construct the unmanaged data. 
        ///                          unsafe code will have to be reviewed
        /// 
        [SecurityCritical, SecurityTreatAsSafe] 
        internal static PathGeometry InternalCombine(
            Geometry geometry1, 
            Geometry geometry2,
            GeometryCombineMode mode,
            Transform transform,
            double tolerance, 
            ToleranceType type)
        { 
            PathGeometry resultGeometry = null; 

            unsafe 
            {
                MilMatrix3x2D matrix = CompositionResourceManager.TransformToMilMatrix3x2D(transform);

                PathGeometryData data1 = geometry1.GetPathGeometryData(); 
                PathGeometryData data2 = geometry2.GetPathGeometryData();
 
                fixed (byte* pPathData1 = data1.SerializedData) 
                {
                    Debug.Assert(pPathData1 != (byte*)0); 

                    fixed (byte* pPathData2 = data2.SerializedData)
                    {
                        Debug.Assert(pPathData2 != (byte*)0); 

                        FillRule fillRule = FillRule.Nonzero; 
 
                        FigureList list = new FigureList();
                        int hr = UnsafeNativeMethods.MilCoreApi.MilUtility_PathGeometryCombine( 
                            &matrix,
                            &data1.Matrix,
                            data1.FillRule,
                            pPathData1, 
                            data1.Size,
                            &data2.Matrix, 
                            data2.FillRule, 
                            pPathData2,
                            data2.Size, 
                            tolerance,
                            type == ToleranceType.Relative,
                            new AddFigureToListDelegate(list.AddFigureToList),
                            mode, 
                            out fillRule);
 
                        if (hr == (int)MILErrors.WGXERR_BADNUMBER) 
                        {
                            // When we encounter NaNs in the renderer, we absorb the error and draw 
                            // nothing. To be consistent, we return an empty geometry.
                            resultGeometry = new PathGeometry();
                        }
                        else 
                        {
                            HRESULT.Check(hr); 
 
                            resultGeometry = new PathGeometry(list.Figures, fillRule, null);
                        } 
                    }
                }
            }
 
            return resultGeometry;
        } 
        #endregion Combine 

        ///  
        /// Remove all figures
        /// 
        #region Clear
        public void Clear() 
        {
            PathFigureCollection figures = Figures; 
 
            if (figures != null)
            { 
                figures.Clear();
            }
        }
        #endregion 

        #region Bounds 
        ///  
        /// Gets the bounds of this PathGeometry as an axis-aligned bounding box
        ///  
        public override Rect Bounds
        {
            get
            { 
                ReadPreamble();
 
                if (IsEmpty()) 
                {
                    return Rect.Empty; 
                }
                else
                {
                    if ((_flags & PathGeometryInternalFlags.BoundsValid) == 0) 
                    {
                        // Update the cached bounds 
                        _bounds = GetPathBoundsAsRB( 
                            GetPathGeometryData(),
                            null,   // pen 
                            Matrix.Identity,
                            StandardFlatteningTolerance,
                            ToleranceType.Absolute,
                            false);  // Do not skip non-fillable figures 

                        _flags |= PathGeometryInternalFlags.BoundsValid; 
                    } 

                    return _bounds.AsRect; 
                }
            }
        }
 
        /// 
        /// Gets the bounds of this PathGeometry as an axis-aligned bounding box with pen and/or transform 
        ///  
        internal static Rect GetPathBounds(
            PathGeometryData pathData, 
            Pen pen,
            Matrix worldMatrix,
            double tolerance,
            ToleranceType type, 
            bool skipHollows)
        { 
            if (pathData.IsEmpty()) 
            {
                return Rect.Empty; 
            }
            else
            {
                MilRectD bounds = PathGeometry.GetPathBoundsAsRB( 
                    pathData,
                    pen, 
                    worldMatrix, 
                    tolerance,
                    type, 
                    skipHollows);

                return bounds.AsRect;
            } 
        }
 
        ///  
        /// Gets the bounds of this PathGeometry as an axis-aligned bounding box with pen and/or transform
        /// 
        /// This function should not be called with a PathGeometryData that's known to be empty, since MilRectD
        /// does not offer a standard way of representing this.
        /// 
        /// 
        ///     Critical as this code performs an elevation ( SUC on MilUtility_PathGeometryBounds)
        ///     TreatAsSafe - the net effect of this function is to return a rect for the Path's bounds. Considered safe. 
        ///                          although we call code within an unsafe block - managed objects are used to construct the unmanaged data. 
        ///
        [SecurityCritical, SecurityTreatAsSafe] 
        internal static MilRectD GetPathBoundsAsRB(
            PathGeometryData pathData,
            Pen pen,
            Matrix worldMatrix, 
            double tolerance,
            ToleranceType type, 
            bool skipHollows) 
        {
            // This method can't handle the empty geometry case, as it's impossible for us to 
            // return Rect.Empty. Callers should do their own check.
            Debug.Assert(!pathData.IsEmpty());

            unsafe 
            {
                MIL_PEN_DATA penData; 
                double[] dashArray = null; 

                // If we have a pen, populate the CMD struct 
                if (pen != null)
                {
                    pen.GetBasicPenData(&penData, out dashArray);
                } 

                MilMatrix3x2D worldMatrix3X2 = CompositionResourceManager.MatrixToMilMatrix3x2D(ref worldMatrix); 
 
                fixed (byte *pbPathData = pathData.SerializedData)
                { 
                    MilRectD bounds;

                    Debug.Assert(pbPathData != (byte*)0);
 
                    fixed (double *pDashArray = dashArray)
                    { 
                        int hr = UnsafeNativeMethods.MilCoreApi.MilUtility_PathGeometryBounds( 
                            (pen == null) ? null : &penData,
                            pDashArray, 
                            &worldMatrix3X2,
                            pathData.FillRule,
                            pbPathData,
                            pathData.Size, 
                            &pathData.Matrix,
                            tolerance, 
                            type == ToleranceType.Relative, 
                            skipHollows,
                            &bounds 
                            );

                        if (hr == (int)MILErrors.WGXERR_BADNUMBER)
                        { 
                            // When we encounter NaNs in the renderer, we absorb the error and draw
                            // nothing. To be consistent, we report that the geometry has empty bounds 
                            // (NaN will get transformed into Rect.Empty higher up). 

                            bounds = MilRectD.NaN; 
                        }
                        else
                        {
                            HRESULT.Check(hr); 
                        }
                    } 
 
                    return bounds;
                } 
            }
        }

        #endregion 

 
        #region HitTestWithPathGeometry 
        ///
        /// Critical as this calls a method that elevates (MilUtility_PathGeometryHitTestPathGeometry) 
        /// TreatAsSafe - net effect of this is to checking the relationship between two geometries. So it's considered safe.
        ///
        [SecurityCritical, SecurityTreatAsSafe]
        internal static IntersectionDetail HitTestWithPathGeometry( 
            Geometry geometry1,
            Geometry geometry2, 
            double tolerance, 
            ToleranceType type)
        { 
            IntersectionDetail detail = IntersectionDetail.NotCalculated;

            unsafe
            { 
                PathGeometryData data1 = geometry1.GetPathGeometryData();
                PathGeometryData data2 = geometry2.GetPathGeometryData(); 
 
                fixed (byte *pbPathData1 = data1.SerializedData)
                { 
                    Debug.Assert(pbPathData1 != (byte*)0);

                    fixed (byte *pbPathData2 = data2.SerializedData)
                    { 
                        Debug.Assert(pbPathData2 != (byte*)0);
 
                        int hr = MilCoreApi.MilUtility_PathGeometryHitTestPathGeometry( 
                            &data1.Matrix,
                            data1.FillRule, 
                            pbPathData1,
                            data1.Size,
                            &data2.Matrix,
                            data2.FillRule, 
                            pbPathData2,
                            data2.Size, 
                            tolerance, 
                            type == ToleranceType.Relative,
                            &detail); 

                        if (hr == (int)MILErrors.WGXERR_BADNUMBER)
                        {
                            // When we encounter NaNs in the renderer, we absorb the error and draw 
                            // nothing. To be consistent, we report that the geometry is never hittable.
                            detail = IntersectionDetail.Empty; 
                        } 
                        else
                        { 
                            HRESULT.Check(hr);
                        }
                    }
                } 
            }
 
            Debug.Assert(detail != IntersectionDetail.NotCalculated); 

            return detail; 
        }
        #endregion

        #region IsEmpty 

        ///  
        /// Returns true if this geometry is empty 
        /// 
        public override bool IsEmpty() 
        {
            PathFigureCollection figures = Figures;
            return (figures == null) || (figures.Count <= 0);
        } 

        #endregion 
 
        /// 
        /// Returns true if this geometry may have curved segments 
        /// 
        public override bool MayHaveCurves()
        {
            PathFigureCollection figures = Figures; 

            int count = (figures != null) ? figures.Count : 0; 
 
            for (int i=0; i
        /// GetAsPathGeometry - return a PathGeometry version of this Geometry 
        /// 
        internal override PathGeometry GetAsPathGeometry() 
        { 
            return CloneCurrentValue();
        } 

        /// 
        /// Creates a string representation of this object based on the format string
        /// and IFormatProvider passed in. 
        /// If the provider is null, the CurrentCulture is used.
        /// See the documentation for IFormattable for more information. 
        ///  
        /// 
        /// A string representation of this object. 
        /// 
        internal override string ConvertToString(string format, IFormatProvider provider)
        {
            PathFigureCollection figures = Figures; 
            FillRule fillRule = FillRule;
 
            string figuresString = String.Empty; 

            if (figures != null) 
            {
                figuresString = figures.ConvertToString(format, provider);
            }
 
            if (fillRule != FillRule.EvenOdd)
            { 
                return "F1" + figuresString; 
            }
            else 
            {
                return figuresString;
            }
        } 

        internal void SetDirty() 
        { 
            _flags = PathGeometryInternalFlags.Dirty;
        } 

        /// 
        /// GetPathGeometryData - returns a struct which contains this Geometry represented
        /// as a path geometry's serialized format. 
        /// 
        internal override PathGeometryData GetPathGeometryData() 
        { 
            PathGeometryData data = new PathGeometryData();
            data.FillRule = FillRule; 
            data.Matrix = CompositionResourceManager.TransformToMilMatrix3x2D(Transform);

            if (IsObviouslyEmpty())
            { 
                return Geometry.GetEmptyPathGeometryData();
            } 
 
            ByteStreamGeometryContext ctx = new ByteStreamGeometryContext();
 
            PathFigureCollection figures = Figures;

            int figureCount = figures == null ? 0 : figures.Count;
 
            for (int i = 0; i < figureCount; i++)
            { 
                figures.Internal_GetItem(i).SerializeData(ctx); 
            }
 
            ctx.Close();
            data.SerializedData = ctx.GetData();

            return data; 
        }
 
        ///  
        ///     Critical: This code accesses unsafe code blocks
        ///     TreatAsSafe: This code does is safe to call and calling a channel with pointers is ok 
        /// 
        [SecurityCritical,SecurityTreatAsSafe]
        private void ManualUpdateResource(DUCE.Channel channel, bool skipOnChannelCheck)
        { 
            // If we're told we can skip the channel check, then we must be on channel
            Debug.Assert(!skipOnChannelCheck || _duceResource.IsOnChannel(channel)); 
 
            if (skipOnChannelCheck || _duceResource.IsOnChannel(channel))
            { 
                checked
                {
                    Transform vTransform = Transform;
 
                    // Obtain handles for properties that implement DUCE.IResource
                    DUCE.ResourceHandle hTransform; 
                    if (vTransform == null || 
                        Object.ReferenceEquals(vTransform, Transform.Identity)
                       ) 
                    {
                        hTransform = DUCE.ResourceHandle.Null;
                    }
                    else 
                    {
                        hTransform = ((DUCE.IResource)vTransform).GetHandle(channel); 
                    } 

                    DUCE.MILCMD_PATHGEOMETRY data; 
                    data.Type = MILCMD.MilCmdPathGeometry;
                    data.Handle = _duceResource.GetHandle(channel);
                    data.hTransform = hTransform;
                    data.FillRule = FillRule; 

                    PathGeometryData pathData = GetPathGeometryData(); 
 
                    data.FiguresSize = pathData.Size;
 
                    unsafe
                    {
                        channel.BeginCommand(
                            (byte*)&data, 
                            sizeof(DUCE.MILCMD_PATHGEOMETRY),
                            (int)data.FiguresSize 
                            ); 

                        fixed (byte *pPathData = pathData.SerializedData) 
                        {
                            channel.AppendCommandData(pPathData, (int)data.FiguresSize);
                        }
                    } 

                    channel.EndCommand(); 
                } 
            }
        } 

        internal override void TransformPropertyChangedHook(DependencyPropertyChangedEventArgs e)
        {
            // PathGeometry caches the transformed bounds.  We hook the changed event 
            // on the Transformed bounds so we can clear the cache.
            if ((_flags & PathGeometryInternalFlags.BoundsValid) != 0) 
            { 
                SetDirty();
 
                // The UCE slave already has a notifier registered on its transform to
                // invalidate its cache.  No need to call InvalidateResource() here to
                // marshal the MIL_PATHGEOMETRY.Flags.
            } 
        }
 
        internal void FiguresPropertyChangedHook(DependencyPropertyChangedEventArgs e) 
        {
            // This is necessary to invalidate the cached bounds. 
            SetDirty();
        }

        #endregion 

        #region Data 
 
        internal PathGeometryInternalFlags _flags = PathGeometryInternalFlags.None;
        internal MilRectD _bounds;                  // Cached Bounds 

        #endregion
    }
    #endregion 
}
 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
//---------------------------------------------------------------------------- 
//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
// Description: Implementation of the class PathGeometry 
//
//--------------------------------------------------------------------------- 
 
using System;
using MS.Internal; 
using MS.Internal.PresentationCore;
using System.ComponentModel;
using System.ComponentModel.Design.Serialization;
using System.Diagnostics; 
using System.Reflection;
using System.Collections; 
using System.Collections.Generic; 
using System.Text;
using System.Globalization; 
using System.Windows.Media;
using System.Windows.Media.Composition;
using System.Windows;
using System.Text.RegularExpressions; 
using System.Windows.Media.Animation;
using System.Windows.Markup; 
using System.Windows.Converters; 
using System.Runtime.InteropServices;
using System.Security; 
using System.Security.Permissions;
using MS.Win32;

using SR=MS.Internal.PresentationCore.SR; 
using SRID=MS.Internal.PresentationCore.SRID;
using UnsafeNativeMethods=MS.Win32.PresentationCore.UnsafeNativeMethods; 
 
namespace System.Windows.Media
{ 
    #region PathGeometryInternalFlags
    [System.Flags]
    internal enum PathGeometryInternalFlags
    { 
        None            = 0x0,
        Invalid         = 0x1, 
        Dirty           = 0x2, 
        BoundsValid     = 0x4
    } 
    #endregion

    #region PathGeometry
    ///  
    /// PathGeometry
    ///  
    [ContentProperty("Figures")] 
    public sealed partial class PathGeometry : Geometry
    { 
        #region Constructors
        /// 
        ///
        ///  
        public PathGeometry()
        { 
        } 

        ///  
        /// Constructor
        /// 
        /// A collection of figures
        public PathGeometry(IEnumerable figures) 
        {
            if (figures != null) 
            { 
                foreach (PathFigure item in figures)
                { 
                    Figures.Add(item);
                }
            }
            else 
            {
                throw new ArgumentNullException("figures"); 
 
            }
 
            SetDirty();
        }

        ///  
        /// Constructor
        ///  
        /// A collection of figures 
        /// The fill rule (OddEven or NonZero)
        /// A transformation to apply to the input 
        public PathGeometry(IEnumerable figures, FillRule fillRule, Transform transform)
        {
            Transform = transform;
            if (ValidateEnums.IsFillRuleValid(fillRule)) 
            {
                FillRule = fillRule; 
 
                if (figures != null)
                { 
                    foreach (PathFigure item in figures)
                    {
                        Figures.Add(item);
                    } 
                }
                else 
                { 
                    throw new ArgumentNullException("figures");
                } 

                SetDirty();
            }
        } 

        ///  
        /// Static "CreateFromGeometry" method which creates a new PathGeometry from the Geometry specified. 
        /// 
        ///  
        /// Geometry - The Geometry which will be used as the basis for the newly created
        /// PathGeometry.  The new Geometry will be based on the current value of all properties.
        /// 
        public static PathGeometry CreateFromGeometry(Geometry geometry) 
        {
            if (geometry == null) 
            { 
                return null;
            } 

            return geometry.GetAsPathGeometry();
        }
 
        /// 
        /// Static method which parses a PathGeometryData and makes calls into the provided context sink. 
        /// This can be used to build a PathGeometry, for readback, etc. 
        /// 
        /// 
        ///     Critical - calls code that performs an elevation.
        ///     TreatAsSafe - This method reads from a pinned byte array.
        ///
        [SecurityCritical, SecurityTreatAsSafe] 
        internal static void ParsePathGeometryData(PathGeometryData pathData, CapacityStreamGeometryContext ctx)
        { 
            if (pathData.IsEmpty()) 
            {
                return; 
            }

            unsafe
            { 
                int currentOffset = 0;
 
                fixed (byte* pbData = pathData.SerializedData) 
                {
                    // This assert is a logical correctness test 
                    Debug.Assert(pathData.Size >= currentOffset + sizeof(MIL_PATHGEOMETRY));

                    // ... while this assert tests "physical" correctness (i.e. are we running out of buffer).
                    Invariant.Assert(pathData.SerializedData.Length >= currentOffset + sizeof(MIL_PATHGEOMETRY)); 

                    MIL_PATHGEOMETRY *pPathGeometry = (MIL_PATHGEOMETRY*)pbData; 
 
                    // Move the current offset to after the Path's data
                    currentOffset += sizeof(MIL_PATHGEOMETRY); 

                    // Are there any Figures to add?
                    if (pPathGeometry->FigureCount > 0)
                    { 
                        // Allocate the correct number of Figures up front
                        ctx.SetFigureCount((int)pPathGeometry->FigureCount); 
 
                        // ... and iterate on the Figures.
                        for (int i = 0; i < pPathGeometry->FigureCount; i++) 
                        {
                            // We only expect well-formed data, but we should assert that we're not reading
                            // too much data.
                            Debug.Assert(pathData.SerializedData.Length >= currentOffset + sizeof(MIL_PATHFIGURE)); 

                            MIL_PATHFIGURE *pPathFigure = (MIL_PATHFIGURE*)(pbData + currentOffset); 
 
                            // Move the current offset to the after of the Figure's data
                            currentOffset += sizeof(MIL_PATHFIGURE); 

                            ctx.BeginFigure(pPathFigure->StartPoint,
                                            ((pPathFigure->Flags & MilPathFigureFlags.IsFillable) != 0),
                                            ((pPathFigure->Flags & MilPathFigureFlags.IsClosed) != 0)); 

                            if (pPathFigure->Count > 0) 
                            { 
                                // Allocate the correct number of Segments up front
                                ctx.SetSegmentCount((int)pPathFigure->Count); 

                                // ... and iterate on the Segments.
                                for (int j = 0; j < pPathFigure->Count; j++)
                                { 
                                    // We only expect well-formed data, but we should assert that we're not reading too much data.
                                    Debug.Assert(pathData.SerializedData.Length >= currentOffset + sizeof(MIL_SEGMENT)); 
                                    Debug.Assert(pathData.Size >= currentOffset + sizeof(MIL_SEGMENT)); 

                                    MIL_SEGMENT *pSegment = (MIL_SEGMENT*)(pbData + currentOffset); 

                                    switch (pSegment->Type)
                                    {
                                    case MIL_SEGMENT_TYPE.MilSegmentLine: 
                                        {
                                            // We only expect well-formed data, but we should assert that we're not reading too much data. 
                                            Debug.Assert(pathData.SerializedData.Length >= currentOffset + sizeof(MIL_SEGMENT_LINE)); 
                                            Debug.Assert(pathData.Size >= currentOffset + sizeof(MIL_SEGMENT_LINE));
 
                                            MIL_SEGMENT_LINE *pSegmentLine = (MIL_SEGMENT_LINE*)(pbData + currentOffset);

                                            ctx.LineTo(pSegmentLine->Point,
                                                       ((pSegmentLine->Flags & MILCoreSegFlags.SegIsAGap) == 0), 
                                                       ((pSegmentLine->Flags & MILCoreSegFlags.SegSmoothJoin) != 0));
 
                                            currentOffset += sizeof(MIL_SEGMENT_LINE); 
                                        }
                                        break; 
                                    case MIL_SEGMENT_TYPE.MilSegmentBezier:
                                        {
                                            // We only expect well-formed data, but we should assert that we're not reading too much data.
                                            Debug.Assert(pathData.SerializedData.Length >= currentOffset + sizeof(MIL_SEGMENT_BEZIER)); 
                                            Debug.Assert(pathData.Size >= currentOffset + sizeof(MIL_SEGMENT_BEZIER));
 
                                            MIL_SEGMENT_BEZIER *pSegmentBezier = (MIL_SEGMENT_BEZIER*)(pbData + currentOffset); 

                                            ctx.BezierTo(pSegmentBezier->Point1, 
                                                         pSegmentBezier->Point2,
                                                         pSegmentBezier->Point3,
                                                         ((pSegmentBezier->Flags & MILCoreSegFlags.SegIsAGap) == 0),
                                                         ((pSegmentBezier->Flags & MILCoreSegFlags.SegSmoothJoin) != 0)); 

                                            currentOffset += sizeof(MIL_SEGMENT_BEZIER); 
                                        } 
                                        break;
                                    case MIL_SEGMENT_TYPE.MilSegmentQuadraticBezier: 
                                        {
                                            // We only expect well-formed data, but we should assert that we're not reading too much data.
                                            Debug.Assert(pathData.SerializedData.Length >= currentOffset + sizeof(MIL_SEGMENT_QUADRATICBEZIER));
                                            Debug.Assert(pathData.Size >= currentOffset + sizeof(MIL_SEGMENT_QUADRATICBEZIER)); 

                                            MIL_SEGMENT_QUADRATICBEZIER *pSegmentQuadraticBezier = (MIL_SEGMENT_QUADRATICBEZIER*)(pbData + currentOffset); 
 
                                            ctx.QuadraticBezierTo(pSegmentQuadraticBezier->Point1,
                                                                  pSegmentQuadraticBezier->Point2, 
                                                                  ((pSegmentQuadraticBezier->Flags & MILCoreSegFlags.SegIsAGap) == 0),
                                                                  ((pSegmentQuadraticBezier->Flags & MILCoreSegFlags.SegSmoothJoin) != 0));

                                            currentOffset += sizeof(MIL_SEGMENT_QUADRATICBEZIER); 
                                        }
                                        break; 
                                    case MIL_SEGMENT_TYPE.MilSegmentArc: 
                                        {
                                            // We only expect well-formed data, but we should assert that we're not reading too much data. 
                                            Debug.Assert(pathData.SerializedData.Length >= currentOffset + sizeof(MIL_SEGMENT_ARC));
                                            Debug.Assert(pathData.Size >= currentOffset + sizeof(MIL_SEGMENT_ARC));

                                            MIL_SEGMENT_ARC *pSegmentArc = (MIL_SEGMENT_ARC*)(pbData + currentOffset); 

                                            ctx.ArcTo(pSegmentArc->Point, 
                                                      pSegmentArc->Size, 
                                                      pSegmentArc->XRotation,
                                                      (pSegmentArc->LargeArc != 0), 
                                                      (pSegmentArc->Sweep == 0) ? SweepDirection.Counterclockwise : SweepDirection.Clockwise,
                                                      ((pSegmentArc->Flags & MILCoreSegFlags.SegIsAGap) == 0),
                                                      ((pSegmentArc->Flags & MILCoreSegFlags.SegSmoothJoin) != 0));
 
                                            currentOffset += sizeof(MIL_SEGMENT_ARC);
                                        } 
                                        break; 
                                    case MIL_SEGMENT_TYPE.MilSegmentPolyLine:
                                    case MIL_SEGMENT_TYPE.MilSegmentPolyBezier: 
                                    case MIL_SEGMENT_TYPE.MilSegmentPolyQuadraticBezier:
                                        {
                                            // We only expect well-formed data, but we should assert that we're not reading too much data.
                                            Debug.Assert(pathData.SerializedData.Length >= currentOffset + sizeof(MIL_SEGMENT_POLY)); 
                                            Debug.Assert(pathData.Size >= currentOffset + sizeof(MIL_SEGMENT_POLY));
 
                                            MIL_SEGMENT_POLY *pSegmentPoly = (MIL_SEGMENT_POLY*)(pbData + currentOffset); 

                                            Debug.Assert(pSegmentPoly->Count <= Int32.MaxValue); 

                                            if (pSegmentPoly->Count > 0)
                                            {
                                                List points = new List((int)pSegmentPoly->Count); 

                                                // We only expect well-formed data, but we should assert that we're not reading too much data. 
                                                Debug.Assert(pathData.SerializedData.Length >= 
                                                             currentOffset +
                                                             sizeof(MIL_SEGMENT_POLY) + 
                                                             (int)pSegmentPoly->Count * sizeof(Point));
                                                Debug.Assert(pathData.Size >=
                                                             currentOffset +
                                                             sizeof(MIL_SEGMENT_POLY) + 
                                                             (int)pSegmentPoly->Count * sizeof(Point));
 
                                                Point* pPoint = (Point*)(pbData + currentOffset + sizeof(MIL_SEGMENT_POLY)); 

                                                for (uint k = 0; k < pSegmentPoly->Count; k++) 
                                                {
                                                    points.Add(*pPoint);
                                                    pPoint++;
                                                } 

                                                switch (pSegment->Type) 
                                                { 
                                                case MIL_SEGMENT_TYPE.MilSegmentPolyLine:
                                                    ctx.PolyLineTo(points, 
                                                                   ((pSegmentPoly->Flags & MILCoreSegFlags.SegIsAGap) == 0),
                                                                   ((pSegmentPoly->Flags & MILCoreSegFlags.SegSmoothJoin) != 0));
                                                    break;
                                                case MIL_SEGMENT_TYPE.MilSegmentPolyBezier: 
                                                    ctx.PolyBezierTo(points,
                                                                     ((pSegmentPoly->Flags & MILCoreSegFlags.SegIsAGap) == 0), 
                                                                     ((pSegmentPoly->Flags & MILCoreSegFlags.SegSmoothJoin) != 0)); 
                                                    break;
                                                case MIL_SEGMENT_TYPE.MilSegmentPolyQuadraticBezier: 
                                                    ctx.PolyQuadraticBezierTo(points,
                                                                   ((pSegmentPoly->Flags & MILCoreSegFlags.SegIsAGap) == 0),
                                                                   ((pSegmentPoly->Flags & MILCoreSegFlags.SegSmoothJoin) != 0));
                                                    break; 
                                                }
                                            } 
 
                                            currentOffset += sizeof(MIL_SEGMENT_POLY) + (int)pSegmentPoly->Count * sizeof(Point);
                                        } 
                                        break;
#if DEBUG
                                    case MIL_SEGMENT_TYPE.MilSegmentNone:
                                        throw new System.InvalidOperationException(); 
                                    default:
                                        throw new System.InvalidOperationException(); 
#endif 
                                    }
                                } 
                            }
                        }
                    }
                } 
            }
        } 
 
        #endregion
 
        /// 
        /// Implementation of Freezable.OnChanged.
        /// 
        protected override void OnChanged() 
        {
            SetDirty(); 
 
            base.OnChanged();
        } 

        #region GetTransformedFigureCollection
        internal override PathFigureCollection GetTransformedFigureCollection(Transform transform)
        { 
            // Combine the transform argument with the internal transform
            Matrix matrix = GetCombinedMatrix(transform); 
 
            // Get the figure collection
            PathFigureCollection result; 

            if (matrix.IsIdentity)
            {
                // There is no need to transform, return the figure collection 
                result = Figures;
                if (result == null) 
                { 
                    result = new PathFigureCollection();
                } 
            }
            else
            {
                // Return a transformed copy of the figure collection 
                result = new PathFigureCollection();
                PathFigureCollection figures = Figures; 
                int count = figures != null ? figures.Count : 0; 
                for (int i = 0; i < count; ++i)
                { 
                    PathFigure figure = figures.Internal_GetItem(i);
                    result.Add(figure.GetTransformedCopy(matrix));
                }
            } 

            Debug.Assert(result != null); 
            return result; 
        }
        #endregion 

        #region PathFigure/Geometry
        /// 
        /// 
        /// 
        public void AddGeometry(Geometry geometry) 
        { 
            if (geometry == null)
            { 
                throw new System.ArgumentNullException("geometry");
            }

            if (geometry.IsEmpty()) 
            {
                return; 
            } 

            PathFigureCollection figureCollection = geometry.GetPathFigureCollection(); 
            Debug.Assert(figureCollection != null);

            PathFigureCollection figures = Figures;
 
            if (figures == null)
            { 
                figures = Figures = new PathFigureCollection(); 
            }
 
            for (int i = 0; i < figureCollection.Count; ++i)
            {
                figures.Add(figureCollection.Internal_GetItem(i));
            } 
        }
 
        #endregion 

        #region FigureList class 
        ///
        /// List of figures, populated by callbacks from unmanaged code
        ///
        internal class FigureList 
        {
            /// 
            /// Constructor 
            ///
            internal FigureList() 
            {
                _figures = new PathFigureCollection();
            }
 
            ///
            /// Figures - the array of figures 
            /// 
            internal PathFigureCollection Figures
            { 
                get
                {
                    return _figures;
                } 
            }
 
            #endregion FigureList class 

            /// 
            /// Callback method, used for adding a figure to the list
            ///
            ///
            /// The figure is filled 
            ///
            /// 
            /// The figure is closed 
            ///
            /// 
            /// The array of the figure's defining points
            ///
            ///
            /// The size of the points array 
            ///
            /// 
            /// The array of the figure's defining segment types 
            ///
            /// 
            /// The size of the types array
            ///
            /// 
            ///    Critical: This code is critical because it is an unsafe code block 
            ///  
            [SecurityCritical] 
            internal unsafe void AddFigureToList(bool isFilled, bool isClosed, MilPoint2F* pPoints, UInt32 pointCount, byte* pSegTypes, UInt32 segmentCount) 
            {
                if (pointCount >=1 && segmentCount >= 1) 
                {
                    PathFigure figure = new PathFigure();

                    figure.IsFilled = isFilled; 
                    figure.StartPoint = new Point(pPoints->X, pPoints->Y);
 
                    int pointIndex = 1; 
                    int sameSegCount = 0;
 
                    for (int segIndex=0; segIndex pointCount)
                            {
                                throw new System.InvalidOperationException(SR.Get(SRID.PathGeometry_InternalReadBackError));
                            } 

                            if (sameSegCount>1) 
                            { 
                                PointCollection ptCollection = new PointCollection();
                                for (int i=0; i pointCount)
                            { 
                                throw new System.InvalidOperationException(SR.Get(SRID.PathGeometry_InternalReadBackError)); 
                            }
 
                            if (sameSegCount>1)
                            {
                                PointCollection ptCollection = new PointCollection();
                                for (int i=0; i
            /// The array of figures
            ///  
            internal PathFigureCollection _figures;
        }; 
 
        internal unsafe delegate void AddFigureToListDelegate(bool isFilled, bool isClosed, MilPoint2F *pPoints, UInt32 pointCount, byte *pTypes, UInt32 typeCount);
 
        #region GetPointAtFractionLength
        /// 
        /// 
        /// 
        ///     Critical - calls MilUtility_GetPointAtLengthFraction that performs an elevation.
        ///     PublicOK - This computes the location of the point x% along the way of a path (and its direction). 
        ///                Progress is normalized between 0 and 1.  This math is considered safe. 
        ///
        [SecurityCritical] 
        public void GetPointAtFractionLength(
            double progress,
            out Point point,
            out Point tangent) 
        {
            if (IsEmpty()) 
            { 
                point = new Point();
                tangent = new Point(); 
                return;
            }

            unsafe 
            {
                PathGeometryData pathData = GetPathGeometryData(); 
 
                fixed (byte *pbPathData = pathData.SerializedData)
                { 
                    Debug.Assert(pbPathData != (byte*)0);

                    HRESULT.Check(MilCoreApi.MilUtility_GetPointAtLengthFraction(
                        &pathData.Matrix, 
                        pathData.FillRule,
                        pbPathData, 
                        pathData.Size, 
                        progress,
                        out point, 
                        out tangent));
                }
            }
        } 
        #endregion
 
        #region Combine 
        /// 
        /// Returns the result of a Boolean combination of two Geometry objects. 
        /// 
        /// The first Geometry object
        /// The second Geometry object
        /// The mode in which the objects will be combined 
        /// A transformation to apply to the result, or null
        /// The computational error tolerance 
        /// The way the error tolerance will be interpreted - relative or absolute 
        ///
        ///     Critical - calls code that perfoms an elevation ( MilUtility_PathGeometryCombine ) 
        ///     TreatAsSafe - the net effect of this function is to return a new PathGeometry given a transform and a combine operation.
        ///                          Considered safe.
        ///
        ///                          Although we call code within an unsafe block - managed objects are used to construct the unmanaged data. 
        ///                          unsafe code will have to be reviewed
        /// 
        [SecurityCritical, SecurityTreatAsSafe] 
        internal static PathGeometry InternalCombine(
            Geometry geometry1, 
            Geometry geometry2,
            GeometryCombineMode mode,
            Transform transform,
            double tolerance, 
            ToleranceType type)
        { 
            PathGeometry resultGeometry = null; 

            unsafe 
            {
                MilMatrix3x2D matrix = CompositionResourceManager.TransformToMilMatrix3x2D(transform);

                PathGeometryData data1 = geometry1.GetPathGeometryData(); 
                PathGeometryData data2 = geometry2.GetPathGeometryData();
 
                fixed (byte* pPathData1 = data1.SerializedData) 
                {
                    Debug.Assert(pPathData1 != (byte*)0); 

                    fixed (byte* pPathData2 = data2.SerializedData)
                    {
                        Debug.Assert(pPathData2 != (byte*)0); 

                        FillRule fillRule = FillRule.Nonzero; 
 
                        FigureList list = new FigureList();
                        int hr = UnsafeNativeMethods.MilCoreApi.MilUtility_PathGeometryCombine( 
                            &matrix,
                            &data1.Matrix,
                            data1.FillRule,
                            pPathData1, 
                            data1.Size,
                            &data2.Matrix, 
                            data2.FillRule, 
                            pPathData2,
                            data2.Size, 
                            tolerance,
                            type == ToleranceType.Relative,
                            new AddFigureToListDelegate(list.AddFigureToList),
                            mode, 
                            out fillRule);
 
                        if (hr == (int)MILErrors.WGXERR_BADNUMBER) 
                        {
                            // When we encounter NaNs in the renderer, we absorb the error and draw 
                            // nothing. To be consistent, we return an empty geometry.
                            resultGeometry = new PathGeometry();
                        }
                        else 
                        {
                            HRESULT.Check(hr); 
 
                            resultGeometry = new PathGeometry(list.Figures, fillRule, null);
                        } 
                    }
                }
            }
 
            return resultGeometry;
        } 
        #endregion Combine 

        ///  
        /// Remove all figures
        /// 
        #region Clear
        public void Clear() 
        {
            PathFigureCollection figures = Figures; 
 
            if (figures != null)
            { 
                figures.Clear();
            }
        }
        #endregion 

        #region Bounds 
        ///  
        /// Gets the bounds of this PathGeometry as an axis-aligned bounding box
        ///  
        public override Rect Bounds
        {
            get
            { 
                ReadPreamble();
 
                if (IsEmpty()) 
                {
                    return Rect.Empty; 
                }
                else
                {
                    if ((_flags & PathGeometryInternalFlags.BoundsValid) == 0) 
                    {
                        // Update the cached bounds 
                        _bounds = GetPathBoundsAsRB( 
                            GetPathGeometryData(),
                            null,   // pen 
                            Matrix.Identity,
                            StandardFlatteningTolerance,
                            ToleranceType.Absolute,
                            false);  // Do not skip non-fillable figures 

                        _flags |= PathGeometryInternalFlags.BoundsValid; 
                    } 

                    return _bounds.AsRect; 
                }
            }
        }
 
        /// 
        /// Gets the bounds of this PathGeometry as an axis-aligned bounding box with pen and/or transform 
        ///  
        internal static Rect GetPathBounds(
            PathGeometryData pathData, 
            Pen pen,
            Matrix worldMatrix,
            double tolerance,
            ToleranceType type, 
            bool skipHollows)
        { 
            if (pathData.IsEmpty()) 
            {
                return Rect.Empty; 
            }
            else
            {
                MilRectD bounds = PathGeometry.GetPathBoundsAsRB( 
                    pathData,
                    pen, 
                    worldMatrix, 
                    tolerance,
                    type, 
                    skipHollows);

                return bounds.AsRect;
            } 
        }
 
        ///  
        /// Gets the bounds of this PathGeometry as an axis-aligned bounding box with pen and/or transform
        /// 
        /// This function should not be called with a PathGeometryData that's known to be empty, since MilRectD
        /// does not offer a standard way of representing this.
        /// 
        /// 
        ///     Critical as this code performs an elevation ( SUC on MilUtility_PathGeometryBounds)
        ///     TreatAsSafe - the net effect of this function is to return a rect for the Path's bounds. Considered safe. 
        ///                          although we call code within an unsafe block - managed objects are used to construct the unmanaged data. 
        ///
        [SecurityCritical, SecurityTreatAsSafe] 
        internal static MilRectD GetPathBoundsAsRB(
            PathGeometryData pathData,
            Pen pen,
            Matrix worldMatrix, 
            double tolerance,
            ToleranceType type, 
            bool skipHollows) 
        {
            // This method can't handle the empty geometry case, as it's impossible for us to 
            // return Rect.Empty. Callers should do their own check.
            Debug.Assert(!pathData.IsEmpty());

            unsafe 
            {
                MIL_PEN_DATA penData; 
                double[] dashArray = null; 

                // If we have a pen, populate the CMD struct 
                if (pen != null)
                {
                    pen.GetBasicPenData(&penData, out dashArray);
                } 

                MilMatrix3x2D worldMatrix3X2 = CompositionResourceManager.MatrixToMilMatrix3x2D(ref worldMatrix); 
 
                fixed (byte *pbPathData = pathData.SerializedData)
                { 
                    MilRectD bounds;

                    Debug.Assert(pbPathData != (byte*)0);
 
                    fixed (double *pDashArray = dashArray)
                    { 
                        int hr = UnsafeNativeMethods.MilCoreApi.MilUtility_PathGeometryBounds( 
                            (pen == null) ? null : &penData,
                            pDashArray, 
                            &worldMatrix3X2,
                            pathData.FillRule,
                            pbPathData,
                            pathData.Size, 
                            &pathData.Matrix,
                            tolerance, 
                            type == ToleranceType.Relative, 
                            skipHollows,
                            &bounds 
                            );

                        if (hr == (int)MILErrors.WGXERR_BADNUMBER)
                        { 
                            // When we encounter NaNs in the renderer, we absorb the error and draw
                            // nothing. To be consistent, we report that the geometry has empty bounds 
                            // (NaN will get transformed into Rect.Empty higher up). 

                            bounds = MilRectD.NaN; 
                        }
                        else
                        {
                            HRESULT.Check(hr); 
                        }
                    } 
 
                    return bounds;
                } 
            }
        }

        #endregion 

 
        #region HitTestWithPathGeometry 
        ///
        /// Critical as this calls a method that elevates (MilUtility_PathGeometryHitTestPathGeometry) 
        /// TreatAsSafe - net effect of this is to checking the relationship between two geometries. So it's considered safe.
        ///
        [SecurityCritical, SecurityTreatAsSafe]
        internal static IntersectionDetail HitTestWithPathGeometry( 
            Geometry geometry1,
            Geometry geometry2, 
            double tolerance, 
            ToleranceType type)
        { 
            IntersectionDetail detail = IntersectionDetail.NotCalculated;

            unsafe
            { 
                PathGeometryData data1 = geometry1.GetPathGeometryData();
                PathGeometryData data2 = geometry2.GetPathGeometryData(); 
 
                fixed (byte *pbPathData1 = data1.SerializedData)
                { 
                    Debug.Assert(pbPathData1 != (byte*)0);

                    fixed (byte *pbPathData2 = data2.SerializedData)
                    { 
                        Debug.Assert(pbPathData2 != (byte*)0);
 
                        int hr = MilCoreApi.MilUtility_PathGeometryHitTestPathGeometry( 
                            &data1.Matrix,
                            data1.FillRule, 
                            pbPathData1,
                            data1.Size,
                            &data2.Matrix,
                            data2.FillRule, 
                            pbPathData2,
                            data2.Size, 
                            tolerance, 
                            type == ToleranceType.Relative,
                            &detail); 

                        if (hr == (int)MILErrors.WGXERR_BADNUMBER)
                        {
                            // When we encounter NaNs in the renderer, we absorb the error and draw 
                            // nothing. To be consistent, we report that the geometry is never hittable.
                            detail = IntersectionDetail.Empty; 
                        } 
                        else
                        { 
                            HRESULT.Check(hr);
                        }
                    }
                } 
            }
 
            Debug.Assert(detail != IntersectionDetail.NotCalculated); 

            return detail; 
        }
        #endregion

        #region IsEmpty 

        ///  
        /// Returns true if this geometry is empty 
        /// 
        public override bool IsEmpty() 
        {
            PathFigureCollection figures = Figures;
            return (figures == null) || (figures.Count <= 0);
        } 

        #endregion 
 
        /// 
        /// Returns true if this geometry may have curved segments 
        /// 
        public override bool MayHaveCurves()
        {
            PathFigureCollection figures = Figures; 

            int count = (figures != null) ? figures.Count : 0; 
 
            for (int i=0; i
        /// GetAsPathGeometry - return a PathGeometry version of this Geometry 
        /// 
        internal override PathGeometry GetAsPathGeometry() 
        { 
            return CloneCurrentValue();
        } 

        /// 
        /// Creates a string representation of this object based on the format string
        /// and IFormatProvider passed in. 
        /// If the provider is null, the CurrentCulture is used.
        /// See the documentation for IFormattable for more information. 
        ///  
        /// 
        /// A string representation of this object. 
        /// 
        internal override string ConvertToString(string format, IFormatProvider provider)
        {
            PathFigureCollection figures = Figures; 
            FillRule fillRule = FillRule;
 
            string figuresString = String.Empty; 

            if (figures != null) 
            {
                figuresString = figures.ConvertToString(format, provider);
            }
 
            if (fillRule != FillRule.EvenOdd)
            { 
                return "F1" + figuresString; 
            }
            else 
            {
                return figuresString;
            }
        } 

        internal void SetDirty() 
        { 
            _flags = PathGeometryInternalFlags.Dirty;
        } 

        /// 
        /// GetPathGeometryData - returns a struct which contains this Geometry represented
        /// as a path geometry's serialized format. 
        /// 
        internal override PathGeometryData GetPathGeometryData() 
        { 
            PathGeometryData data = new PathGeometryData();
            data.FillRule = FillRule; 
            data.Matrix = CompositionResourceManager.TransformToMilMatrix3x2D(Transform);

            if (IsObviouslyEmpty())
            { 
                return Geometry.GetEmptyPathGeometryData();
            } 
 
            ByteStreamGeometryContext ctx = new ByteStreamGeometryContext();
 
            PathFigureCollection figures = Figures;

            int figureCount = figures == null ? 0 : figures.Count;
 
            for (int i = 0; i < figureCount; i++)
            { 
                figures.Internal_GetItem(i).SerializeData(ctx); 
            }
 
            ctx.Close();
            data.SerializedData = ctx.GetData();

            return data; 
        }
 
        ///  
        ///     Critical: This code accesses unsafe code blocks
        ///     TreatAsSafe: This code does is safe to call and calling a channel with pointers is ok 
        /// 
        [SecurityCritical,SecurityTreatAsSafe]
        private void ManualUpdateResource(DUCE.Channel channel, bool skipOnChannelCheck)
        { 
            // If we're told we can skip the channel check, then we must be on channel
            Debug.Assert(!skipOnChannelCheck || _duceResource.IsOnChannel(channel)); 
 
            if (skipOnChannelCheck || _duceResource.IsOnChannel(channel))
            { 
                checked
                {
                    Transform vTransform = Transform;
 
                    // Obtain handles for properties that implement DUCE.IResource
                    DUCE.ResourceHandle hTransform; 
                    if (vTransform == null || 
                        Object.ReferenceEquals(vTransform, Transform.Identity)
                       ) 
                    {
                        hTransform = DUCE.ResourceHandle.Null;
                    }
                    else 
                    {
                        hTransform = ((DUCE.IResource)vTransform).GetHandle(channel); 
                    } 

                    DUCE.MILCMD_PATHGEOMETRY data; 
                    data.Type = MILCMD.MilCmdPathGeometry;
                    data.Handle = _duceResource.GetHandle(channel);
                    data.hTransform = hTransform;
                    data.FillRule = FillRule; 

                    PathGeometryData pathData = GetPathGeometryData(); 
 
                    data.FiguresSize = pathData.Size;
 
                    unsafe
                    {
                        channel.BeginCommand(
                            (byte*)&data, 
                            sizeof(DUCE.MILCMD_PATHGEOMETRY),
                            (int)data.FiguresSize 
                            ); 

                        fixed (byte *pPathData = pathData.SerializedData) 
                        {
                            channel.AppendCommandData(pPathData, (int)data.FiguresSize);
                        }
                    } 

                    channel.EndCommand(); 
                } 
            }
        } 

        internal override void TransformPropertyChangedHook(DependencyPropertyChangedEventArgs e)
        {
            // PathGeometry caches the transformed bounds.  We hook the changed event 
            // on the Transformed bounds so we can clear the cache.
            if ((_flags & PathGeometryInternalFlags.BoundsValid) != 0) 
            { 
                SetDirty();
 
                // The UCE slave already has a notifier registered on its transform to
                // invalidate its cache.  No need to call InvalidateResource() here to
                // marshal the MIL_PATHGEOMETRY.Flags.
            } 
        }
 
        internal void FiguresPropertyChangedHook(DependencyPropertyChangedEventArgs e) 
        {
            // This is necessary to invalidate the cached bounds. 
            SetDirty();
        }

        #endregion 

        #region Data 
 
        internal PathGeometryInternalFlags _flags = PathGeometryInternalFlags.None;
        internal MilRectD _bounds;                  // Cached Bounds 

        #endregion
    }
    #endregion 
}
 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK