172 lines
5.5 KiB
C#
172 lines
5.5 KiB
C#
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;
|
|
}
|
|
}
|
|
}
|
|
} |