| File: System\Windows\Media\StreamGeometry.cs | Web Access |
| Project: src\wpf\src\Microsoft.DotNet.Wpf\src\PresentationCore\PresentationCore.csproj (PresentationCore) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // // // Description: Implementation of the class StreamGeometry // using MS.Internal; using System.ComponentModel; using System.Windows.Media.Composition; namespace System.Windows.Media { #region StreamGeometry /// <summary> /// StreamGeometry /// </summary> [TypeConverter(typeof(GeometryConverter))] public sealed partial class StreamGeometry : Geometry { #region Constructors /// <summary> /// /// </summary> public StreamGeometry() { } #endregion /// <summary> /// Opens the StreamGeometry for population. /// </summary> public StreamGeometryContext Open() { WritePreamble(); return new StreamGeometryCallbackContext(this); } /// <summary> /// Remove all figures /// </summary> public void Clear() { WritePreamble(); _data = null; SetDirty(); RegisterForAsyncUpdateResource(); } /// <summary> /// Returns true if this geometry is empty /// </summary> public override bool IsEmpty() { ReadPreamble(); if ((_data == null) || (_data.Length <= 0)) { return true; } unsafe { Invariant.Assert((_data != null) && (_data.Length >= sizeof(MIL_PATHGEOMETRY))); fixed (byte *pbPathData = _data) { MIL_PATHGEOMETRY* pPathGeometry = (MIL_PATHGEOMETRY*)pbPathData; return pPathGeometry->FigureCount <= 0; } } } /// <summary> /// AreBoundsValid Property - returns true if the bounds are valid, false otherwise. /// If true, the bounds are stored in the bounds param. /// </summary> private bool AreBoundsValid(ref MilRectD bounds) { if (IsEmpty()) { return false; } unsafe { Debug.Assert((_data != null) && (_data.Length >= sizeof(MIL_PATHGEOMETRY))); fixed (byte* pbPathData = _data) { MIL_PATHGEOMETRY* pGeometry = (MIL_PATHGEOMETRY*)pbPathData; bool areBoundsValid = (pGeometry->Flags & MilPathGeometryFlags.BoundsValid) != 0; if (areBoundsValid) { bounds = pGeometry->Bounds; } return areBoundsValid; } } } /// <summary> /// CacheBounds - store the calculated bounds in the data stream. /// </summary> private void CacheBounds(ref MilRectD bounds) { unsafe { Debug.Assert((_data != null) && (_data.Length >= sizeof(MIL_PATHGEOMETRY))); fixed (byte* pbPathData = _data) { MIL_PATHGEOMETRY* pGeometry = (MIL_PATHGEOMETRY*)pbPathData; pGeometry->Flags |= MilPathGeometryFlags.BoundsValid; pGeometry->Bounds = bounds; } } } /// <summary> /// SetDirty - indicate that the cached bounds on this Geometry are not valid. /// </summary> internal void SetDirty() { if (!IsEmpty()) { unsafe { Debug.Assert((_data != null) && (_data.Length >= sizeof(MIL_PATHGEOMETRY))); fixed (byte* pbPathData = _data) { MIL_PATHGEOMETRY* pGeometry = (MIL_PATHGEOMETRY*)pbPathData; pGeometry->Flags &= ~MilPathGeometryFlags.BoundsValid; } } } } /// <summary> /// Gets the bounds of this StreamGeometry as an axis-aligned bounding box /// </summary> public override Rect Bounds { get { ReadPreamble(); if (IsEmpty()) { return Rect.Empty; } else { MilRectD bounds = new MilRectD(); if (!AreBoundsValid(ref bounds)) { // Update the cached bounds bounds = PathGeometry.GetPathBoundsAsRB( GetPathGeometryData(), null, // pen Matrix.Identity, StandardFlatteningTolerance, ToleranceType.Absolute, false); // Do not skip non-fillable figures CacheBounds(ref bounds); } return bounds.AsRect; } } } /// <summary> /// Returns true if this geometry may have curved segments /// </summary> public override bool MayHaveCurves() { // IsEmpty() calls ReadPreamble() if (IsEmpty()) { return false; } unsafe { Invariant.Assert((_data != null) && (_data.Length >= sizeof(MIL_PATHGEOMETRY))); fixed (byte* pbPathData = (this._data)) { MIL_PATHGEOMETRY* pPathGeometryData = (MIL_PATHGEOMETRY*)pbPathData; return (pPathGeometryData->Flags & MilPathGeometryFlags.HasCurves) != 0; } } } #region Internal /// <summary> /// Returns true if this geometry may have curved segments /// </summary> internal bool HasHollows() { // IsEmpty() calls ReadPreamble() if (IsEmpty()) { return false; } unsafe { Invariant.Assert((_data != null) && (_data.Length >= sizeof(MIL_PATHGEOMETRY))); fixed (byte* pbPathData = (this._data)) { MIL_PATHGEOMETRY* pPathGeometryData = (MIL_PATHGEOMETRY*)pbPathData; return (pPathGeometryData->Flags & MilPathGeometryFlags.HasHollows) != 0; } } } /// <summary> /// Returns true if this geometry may have curved segments /// </summary> internal bool HasGaps() { // IsEmpty() calls ReadPreamble() if (IsEmpty()) { return false; } unsafe { Invariant.Assert((_data != null) && (_data.Length >= sizeof(MIL_PATHGEOMETRY))); fixed (byte* pbPathData = (this._data)) { MIL_PATHGEOMETRY* pPathGeometryData = (MIL_PATHGEOMETRY*)pbPathData; return (pPathGeometryData->Flags & MilPathGeometryFlags.HasGaps) != 0; } } } /// <summary> /// Called from the StreamGeometryContext when it is closed. /// </summary> internal void Close(byte[] _buffer) { SetDirty(); _data = _buffer; RegisterForAsyncUpdateResource(); } /// <summary> /// Implementation of <see cref="System.Windows.Freezable.OnChanged">Freezable.OnChanged</see>. /// </summary> protected override void OnChanged() { SetDirty(); base.OnChanged(); } /// <summary> /// GetAsPathGeometry - return a PathGeometry version of this Geometry /// </summary> internal override PathGeometry GetAsPathGeometry() { PathStreamGeometryContext ctx = new PathStreamGeometryContext(FillRule, Transform); PathGeometry.ParsePathGeometryData(GetPathGeometryData(), ctx); return ctx.GetPathGeometry(); } /// <summary> /// GetTransformedFigureCollection - inherited from Geometry. /// Basically, this is used to collapse this Geometry into an existing PathGeometry. /// </summary> internal override PathFigureCollection GetTransformedFigureCollection(Transform transform) { PathGeometry thisAsPathGeometry = GetAsPathGeometry(); if (null != thisAsPathGeometry) { return thisAsPathGeometry.GetTransformedFigureCollection(transform); } else { return null; } } /// <summary> /// Can serialize "this" to a string /// </summary> internal override bool CanSerializeToString() { Transform transform = Transform; return (((transform == null) || transform.IsIdentity) && !HasHollows() && !HasGaps()); } /// <summary> /// 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. /// </summary> /// <returns> /// A string representation of this object. /// </returns> internal override string ConvertToString(string format, IFormatProvider provider) { // Consider serializing Data more efficiently. return GetAsPathGeometry().ConvertToString(format, provider); } private void InvalidateResourceFigures(object sender, EventArgs args) { // This is necessary to invalidate the cached bounds. SetDirty(); RegisterForAsyncUpdateResource(); } /// <summary> /// GetPathGeometryData - returns a struct which contains this Geometry represented /// as a path geometry's serialized format. /// </summary> internal override PathGeometryData GetPathGeometryData() { if (IsEmpty()) { return Geometry.GetEmptyPathGeometryData(); } PathGeometryData data = new PathGeometryData { FillRule = FillRule, Matrix = CompositionResourceManager.TransformToMilMatrix3x2D(Transform), SerializedData = _data }; return data; } internal override void TransformPropertyChangedHook(DependencyPropertyChangedEventArgs e) { SetDirty(); } #endregion #region DUCE private unsafe int GetFigureSize(byte* pbPathData) { MIL_PATHGEOMETRY* pPathGeometryData = (MIL_PATHGEOMETRY*)pbPathData; return pPathGeometryData == null ? 0 : (int)pPathGeometryData->Size; } internal override void UpdateResource(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; byte[] pathDataToMarshal = _data ?? GetEmptyPathGeometryData().SerializedData; unsafe { fixed (byte* pbPathData = pathDataToMarshal) { data.FiguresSize = (uint)GetFigureSize(pbPathData); channel.BeginCommand( (byte*)&data, sizeof(DUCE.MILCMD_PATHGEOMETRY), (int)data.FiguresSize ); channel.AppendCommandData(pbPathData, (int)data.FiguresSize); } channel.EndCommand(); } } } base.UpdateResource(channel, skipOnChannelCheck); } internal override DUCE.ResourceHandle AddRefOnChannelCore(DUCE.Channel channel) { if (_duceResource.CreateOrAddRefOnChannel(this, channel, System.Windows.Media.Composition.DUCE.ResourceType.TYPE_PATHGEOMETRY)) { Transform vTransform = Transform; if (vTransform != null) ((DUCE.IResource)vTransform).AddRefOnChannel(channel); UpdateResource(channel, skipOnChannelCheck: true /* We already know that we're on channel */ ); } return _duceResource.GetHandle(channel); } internal override void ReleaseOnChannelCore(DUCE.Channel channel) { Debug.Assert(_duceResource.IsOnChannel(channel)); if (_duceResource.ReleaseOnChannel(channel)) { Transform vTransform = Transform; if (vTransform != null) ((DUCE.IResource)vTransform).ReleaseOnChannel(channel); } } internal override DUCE.ResourceHandle GetHandleCore(DUCE.Channel channel) { // Note that we are in a lock here already. return _duceResource.GetHandle(channel); } internal override int GetChannelCountCore() { return _duceResource.GetChannelCount(); } internal override DUCE.Channel GetChannelCore(int index) { return _duceResource.GetChannel(index); } /// <summary> /// Implementation of Freezable.CloneCore() /// </summary> protected override void CloneCore(Freezable source) { base.CloneCore(source); StreamGeometry sourceStream = (StreamGeometry) source; if ((sourceStream._data != null) && (sourceStream._data.Length > 0)) { _data = new byte[sourceStream._data.Length]; sourceStream._data.CopyTo(_data, 0); } } /// <summary> /// Implementation of Freezable.CloneCurrentValueCore() /// </summary> protected override void CloneCurrentValueCore(Freezable source) { base.CloneCurrentValueCore(source); StreamGeometry sourceStream = (StreamGeometry) source; if ((sourceStream._data != null) && (sourceStream._data.Length > 0)) { _data = new byte[sourceStream._data.Length]; sourceStream._data.CopyTo(_data, 0); } } /// <summary> /// Implementation of Freezable.GetAsFrozenCore() /// </summary> protected override void GetAsFrozenCore(Freezable source) { base.GetAsFrozenCore(source); StreamGeometry sourceStream = (StreamGeometry) source; if ((sourceStream._data != null) && (sourceStream._data.Length > 0)) { _data = new byte[sourceStream._data.Length]; sourceStream._data.CopyTo(_data, 0); } } /// <summary> /// Implementation of Freezable.GetCurrentValueAsFrozenCore() /// </summary> protected override void GetCurrentValueAsFrozenCore(Freezable source) { base.GetCurrentValueAsFrozenCore(source); StreamGeometry sourceStream = (StreamGeometry) source; if ((sourceStream._data != null) && (sourceStream._data.Length > 0)) { _data = new byte[sourceStream._data.Length]; sourceStream._data.CopyTo(_data, 0); } } #endregion DUCE #region Data private byte[] _data; internal System.Windows.Media.Composition.DUCE.MultiChannelResource _duceResource = new System.Windows.Media.Composition.DUCE.MultiChannelResource(); #endregion } #endregion #region StreamGeometryCallbackContext internal class StreamGeometryCallbackContext: ByteStreamGeometryContext { /// <summary> /// Creates a geometry stream context which is associated with a given owner /// </summary> internal StreamGeometryCallbackContext(StreamGeometry owner) { _owner = owner; } /// <summary> /// CloseCore - This method is implemented by derived classes to hand off the content /// to its eventual destination. /// </summary> protected override void CloseCore(byte[] data) { _owner.Close(data); } private StreamGeometry _owner; } #endregion StreamGeometryCallbackContext }