|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
//
//
// Description: Implementation of the class StreamGeometry
//
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 MS.Win32;
using SR=MS.Internal.PresentationCore.SR;
using UnsafeNativeMethods=MS.Win32.PresentationCore.UnsafeNativeMethods;
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();
data.FillRule = FillRule;
data.Matrix = CompositionResourceManager.TransformToMilMatrix3x2D(Transform);
data.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 == null ?
Geometry.GetEmptyPathGeometryData().SerializedData :
_data;
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, true /* skip "on channel" check - 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
}
|