Code:
/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / Orcas / SP / 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

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- SessionParameter.cs
- StringReader.cs
- CommandBindingCollection.cs
- FixedSOMTextRun.cs
- ReadWriteObjectLock.cs
- ObjectStateManager.cs
- BamlLocalizerErrorNotifyEventArgs.cs
- PtsPage.cs
- SmiGettersStream.cs
- DataGridViewBand.cs
- GridViewUpdatedEventArgs.cs
- SymmetricKeyWrap.cs
- RpcAsyncResult.cs
- TextBoxBase.cs
- CommittableTransaction.cs
- MutexSecurity.cs
- JsonCollectionDataContract.cs
- GenerateTemporaryAssemblyTask.cs
- _FtpControlStream.cs
- PropertyGeneratedEventArgs.cs
- DynamicContractTypeBuilder.cs
- XmlDataImplementation.cs
- ServerValidateEventArgs.cs
- InputScope.cs
- ClientOperationFormatterProvider.cs
- LongPath.cs
- KeyEventArgs.cs
- PageThemeBuildProvider.cs
- XPathBinder.cs
- SQLBinary.cs
- RevocationPoint.cs
- TreeViewImageGenerator.cs
- PresentationUIStyleResources.cs
- ObjectAssociationEndMapping.cs
- DataContractSerializerOperationFormatter.cs
- SchemaInfo.cs
- MessageQueueException.cs
- PropertyMap.cs
- PropertyTabAttribute.cs
- XmlValueConverter.cs
- ProjectionPlanCompiler.cs
- ParserStack.cs
- HttpBrowserCapabilitiesBase.cs
- ScaleTransform3D.cs
- Win32Exception.cs
- recordstatescratchpad.cs
- DATA_BLOB.cs
- IgnorePropertiesAttribute.cs
- SystemWebCachingSectionGroup.cs
- LayoutDump.cs
- PageFunction.cs
- EventHandlerList.cs
- ExpressionParser.cs
- COSERVERINFO.cs
- MSG.cs
- ServiceOperationParameter.cs
- PaintEvent.cs
- EmptyQuery.cs
- CatalogPartChrome.cs
- InstanceLockLostException.cs
- XmlNamespaceDeclarationsAttribute.cs
- PropertySegmentSerializationProvider.cs
- InkCanvasFeedbackAdorner.cs
- ObjectDataSourceStatusEventArgs.cs
- OwnerDrawPropertyBag.cs
- TrackingRecord.cs
- METAHEADER.cs
- XmlChoiceIdentifierAttribute.cs
- ConversionHelper.cs
- HostingEnvironment.cs
- ToolboxItem.cs
- SchemaObjectWriter.cs
- LookupNode.cs
- FtpWebRequest.cs
- IdentifierCreationService.cs
- ParameterElementCollection.cs
- DelegateSerializationHolder.cs
- SqlReferenceCollection.cs
- _SslState.cs
- DBConnection.cs
- DrawingBrush.cs
- OleDbSchemaGuid.cs
- IMembershipProvider.cs
- RepeaterCommandEventArgs.cs
- MatrixConverter.cs
- SqlDataAdapter.cs
- NameValueConfigurationCollection.cs
- ChangeBlockUndoRecord.cs
- PenLineJoinValidation.cs
- HttpConfigurationContext.cs
- GetImportedCardRequest.cs
- TypeDelegator.cs
- SynchronizedInputAdaptor.cs
- TextTreeNode.cs
- Propagator.JoinPropagator.cs
- DesignerView.cs
- StagingAreaInputItem.cs
- LicenseContext.cs
- NamedPipeProcessProtocolHandler.cs
- ParamArrayAttribute.cs