using System; using System.ComponentModel; using System.Windows; using System.Windows.Media; using System.Windows.Threading; namespace HandyControl.Expression.Media; [TypeConverter(typeof(GeometryEffectConverter))] public abstract class GeometryEffect : Freezable { // ReSharper disable once InconsistentNaming private static GeometryEffect defaultGeometryEffect; public static readonly DependencyProperty GeometryEffectProperty = DependencyProperty.RegisterAttached("GeometryEffect", typeof(GeometryEffect), typeof(GeometryEffect), new DrawingPropertyMetadata(DefaultGeometryEffect, DrawingPropertyMetadataOptions.AffectsRender, OnGeometryEffectChanged)); protected Geometry CachedGeometry; private bool _effectInvalidated; static GeometryEffect() { DrawingPropertyMetadata.DrawingPropertyChanged += delegate (object sender, DrawingPropertyChangedEventArgs args) { if (sender is GeometryEffect effect && args.Metadata.AffectsRender) effect.InvalidateGeometry(InvalidateGeometryReasons.PropertyChanged); }; } public static GeometryEffect DefaultGeometryEffect => defaultGeometryEffect ?? (defaultGeometryEffect = new NoGeometryEffect()); public Geometry OutputGeometry => CachedGeometry; protected internal DependencyObject Parent { get; private set; } protected internal virtual void Attach(DependencyObject obj) { if (Parent != null) Detach(); _effectInvalidated = true; CachedGeometry = null; if (InvalidateParent(obj)) Parent = obj; } public new GeometryEffect CloneCurrentValue() { return (GeometryEffect) base.CloneCurrentValue(); } protected override Freezable CreateInstanceCore() { return (Freezable) Activator.CreateInstance(GetType()); } protected abstract GeometryEffect DeepCopy(); protected internal virtual void Detach() { _effectInvalidated = true; CachedGeometry = null; if (Parent != null) { InvalidateParent(Parent); Parent = null; } } public abstract bool Equals(GeometryEffect geometryEffect); public static GeometryEffect GetGeometryEffect(DependencyObject obj) { return (GeometryEffect) obj.GetValue(GeometryEffectProperty); } public bool InvalidateGeometry(InvalidateGeometryReasons reasons) { if (_effectInvalidated) return false; _effectInvalidated = true; if (reasons != InvalidateGeometryReasons.ParentInvalidated) InvalidateParent(Parent); return true; } private static bool InvalidateParent(DependencyObject parent) { if (parent is IShape shape) { shape.InvalidateGeometry(InvalidateGeometryReasons.ChildInvalidated); return true; } if (parent is GeometryEffect effect) { effect.InvalidateGeometry(InvalidateGeometryReasons.ChildInvalidated); return true; } return false; } private static void OnGeometryEffectChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e) { var oldValue = e.OldValue as GeometryEffect; var newValue = e.NewValue as GeometryEffect; if (!Equals(oldValue, newValue)) { if (oldValue != null && obj.Equals(oldValue.Parent)) oldValue.Detach(); if (newValue != null) { if (newValue.Parent != null) { obj.Dispatcher.BeginInvoke(new Action(() => { var effect = newValue.CloneCurrentValue(); obj.SetValue(GeometryEffectProperty, effect); }), DispatcherPriority.Send, null); } else { newValue.Attach(obj); } } } } public bool ProcessGeometry(Geometry input) { var flag = false; if (_effectInvalidated) { flag |= UpdateCachedGeometry(input); _effectInvalidated = false; } return flag; } public static void SetGeometryEffect(DependencyObject obj, GeometryEffect value) { obj.SetValue(GeometryEffectProperty, value); } protected abstract bool UpdateCachedGeometry(Geometry input); private class NoGeometryEffect : GeometryEffect { protected override GeometryEffect DeepCopy() { return new NoGeometryEffect(); } public override bool Equals(GeometryEffect geometryEffect) { if (geometryEffect != null) return geometryEffect is NoGeometryEffect; return true; } protected override bool UpdateCachedGeometry(Geometry input) { CachedGeometry = input; return false; } } }