From 456c7c10c24555725392a70e3467f47b3b630a16 Mon Sep 17 00:00:00 2001 From: Jumar Macato Date: Wed, 11 Apr 2018 21:54:27 +0800 Subject: [PATCH] - Initial implementation of ColorTransition.cs - Added System.Numerics.Vectors in Avalonia.Base. - Replaced AnimationPlayState in Animatable.cs with a DirectProperty. --- src/Avalonia.Animation/Animatable.cs | 16 ++-- .../ColorInterpolationMode.cs | 34 ++++++++ src/Avalonia.Animation/Keyframes/KeyFrames.cs | 1 + src/Avalonia.Animation/Timing.cs | 2 +- .../Transitions/Transition.cs | 4 +- .../Animation/Transitions/ColorTransition.cs | 81 +++++++++++++++++++ src/Avalonia.Visuals/Media/Color.cs | 27 +++++++ 7 files changed, 157 insertions(+), 8 deletions(-) create mode 100644 src/Avalonia.Animation/ColorInterpolationMode.cs create mode 100644 src/Avalonia.Visuals/Animation/Transitions/ColorTransition.cs diff --git a/src/Avalonia.Animation/Animatable.cs b/src/Avalonia.Animation/Animatable.cs index b06586dd72..f630ff05dc 100644 --- a/src/Avalonia.Animation/Animatable.cs +++ b/src/Avalonia.Animation/Animatable.cs @@ -27,19 +27,25 @@ namespace Avalonia.Animation /// /// Defines the property. /// - public static readonly StyledProperty AnimationPlayStateProperty = - AvaloniaProperty.Register(nameof(AnimationPlayState)); + public static readonly DirectProperty AnimationPlayStateProperty = + AvaloniaProperty.RegisterDirect( + nameof(AnimationPlayState), + o => o.AnimationPlayState, + (o, v) => o.AnimationPlayState = v); /// /// Gets or sets the state of the animation for this /// control. /// public AnimationPlayState AnimationPlayState - { - get { return GetValue(AnimationPlayStateProperty); } - set { SetValue(AnimationPlayStateProperty, value); } + { + get { return _animationPlayState; } + set { SetAndRaise(AnimationPlayStateProperty, ref _animationPlayState, value); } + } + private AnimationPlayState _animationPlayState; + /// /// Defines the property. /// diff --git a/src/Avalonia.Animation/ColorInterpolationMode.cs b/src/Avalonia.Animation/ColorInterpolationMode.cs new file mode 100644 index 0000000000..bfe4e5080e --- /dev/null +++ b/src/Avalonia.Animation/ColorInterpolationMode.cs @@ -0,0 +1,34 @@ +namespace Avalonia.Animation +{ + /// + /// Defines the intermediate color space + /// in which the color interpolation will be done. + /// + public enum ColorInterpolationMode + { + /// + /// Premultiply the alpha component and interpolate from RGB color space. + /// Fast but relatively accurate, perception-wise. + /// + PremultipliedRGB, + + /// + /// Directly interpolate from RGB color space. + /// Fastest but the least accurate, perception-wise. + /// + RGB, + + /// + /// Converts RGB into HSV color space and interpolates. + /// Slow but accurate, perception-wise. + /// + HSV, + + /// + /// Converts RGB into HSV color space and interpolates. + /// Slowest but the most accurate, perception-wise. + /// + LAB, + + } +} \ No newline at end of file diff --git a/src/Avalonia.Animation/Keyframes/KeyFrames.cs b/src/Avalonia.Animation/Keyframes/KeyFrames.cs index a31becaccf..814a67be14 100644 --- a/src/Avalonia.Animation/Keyframes/KeyFrames.cs +++ b/src/Avalonia.Animation/Keyframes/KeyFrames.cs @@ -37,6 +37,7 @@ namespace Avalonia.Animation.Keyframes return obsMatch .Where(p => p == true) + // Ignore triggers when global timers are paused. .Where(p=> Timing.GetGlobalPlayState() != AnimationPlayState.Paused) .Subscribe(_ => { diff --git a/src/Avalonia.Animation/Timing.cs b/src/Avalonia.Animation/Timing.cs index fc75e2c7c3..39caeb52cc 100644 --- a/src/Avalonia.Animation/Timing.cs +++ b/src/Avalonia.Animation/Timing.cs @@ -143,7 +143,7 @@ namespace Avalonia.Animation /// 0 being the start and 1 being the end. The observable is guaranteed to fire 0 /// immediately on subscribe and 1 at the end of the duration. /// - public static IObservable GetTransitionsTimer(Animatable control, TimeSpan duration, TimeSpan delay) + public static IObservable GetTransitionsTimer(Animatable control, TimeSpan duration, TimeSpan delay = default(TimeSpan)) { var startTime = _transitionsFrameCount; var _duration = (ulong)(duration.Ticks / Tick.Ticks); diff --git a/src/Avalonia.Animation/Transitions/Transition.cs b/src/Avalonia.Animation/Transitions/Transition.cs index 66fda063b6..c382118d81 100644 --- a/src/Avalonia.Animation/Transitions/Transition.cs +++ b/src/Avalonia.Animation/Transitions/Transition.cs @@ -11,7 +11,7 @@ namespace Avalonia.Animation.Transitions /// /// Defines how a property should be animated using a transition. /// - public abstract class Transition : ITransition + public abstract class Transition : AvaloniaObject, ITransition { private AvaloniaProperty _prop; private Easing _easing; @@ -59,7 +59,7 @@ namespace Avalonia.Animation.Transitions public abstract IObservable DoTransition(IObservable progress, T oldValue, T newValue); /// - public IDisposable Apply(Animatable control, object oldValue, object newValue) + public virtual IDisposable Apply(Animatable control, object oldValue, object newValue) { var transition = DoTransition(Timing.GetTransitionsTimer(control, Duration, TimeSpan.Zero), (T)oldValue, (T)newValue).Select(p => (object)p); return control.Bind(Property, transition, Data.BindingPriority.Animation); diff --git a/src/Avalonia.Visuals/Animation/Transitions/ColorTransition.cs b/src/Avalonia.Visuals/Animation/Transitions/ColorTransition.cs new file mode 100644 index 0000000000..118f17a427 --- /dev/null +++ b/src/Avalonia.Visuals/Animation/Transitions/ColorTransition.cs @@ -0,0 +1,81 @@ +// Copyright (c) The Avalonia Project. All rights reserved. +// Licensed under the MIT license. See licence.md file in the project root for full license information. + +using Avalonia.Metadata; +using System; +using System.Reactive.Linq; +using Avalonia.Media; +using System.Numerics; + +namespace Avalonia.Animation.Transitions +{ + /// + /// Transition class that handles with types. + /// TODO: Must be refactored to handle object creation. + /// + public class ColorTransition : Transition + { + + /// + /// Defines the property. + /// + public static readonly DirectProperty InterpolationModeProperty = + AvaloniaProperty.RegisterDirect( + nameof(InterpolationMode), + o => o.InterpolationMode, + (o, v) => o.InterpolationMode = v); + + /// + /// Gets or sets the state of the animation for this + /// control. + /// + public ColorInterpolationMode InterpolationMode + { + get { return _interpolationMode; } + set { SetAndRaise(InterpolationModeProperty, ref _interpolationMode, value); } + } + + private ColorInterpolationMode _interpolationMode; + + public override IObservable DoTransition(IObservable progress, SolidColorBrush oldValue, SolidColorBrush newValue) + { + var oldColor = new Vector4(oldValue.Color.A, oldValue.Color.R, oldValue.Color.G, oldValue.Color.B); + var newColor = new Vector4(newValue.Color.A, newValue.Color.R, newValue.Color.G, newValue.Color.B); + oldColor = oldColor / 255f; + newColor = newColor / 255f; + var deltaColor = newColor - oldColor; + + switch (InterpolationMode) + { + case ColorInterpolationMode.PremultipliedRGB: + + var premultOV = new Vector4(1, oldColor.W, oldColor.W, oldColor.W); + var premultNV = new Vector4(1, newColor.W, newColor.W, newColor.W); + + oldColor *= premultOV; + newColor *= premultNV; + + return progress + .Select(p => + { + var time = (float)Easing.Ease(p); + var interpolatedColor = (deltaColor * time) + oldColor; + return new SolidColorBrush(Color.FromVector4(interpolatedColor, true)); + }); + + case ColorInterpolationMode.RGB: + return progress + .Select(p => + { + var time = (float)Easing.Ease(p); + var interpolatedColor = (deltaColor * time) + oldColor; + return new SolidColorBrush(Color.FromVector4(interpolatedColor, true)); + }); + default: + throw new NotImplementedException($"{Enum.GetName(typeof(ColorInterpolationMode), InterpolationMode)} color interpolation is not supported."); + } + + } + + } +} diff --git a/src/Avalonia.Visuals/Media/Color.cs b/src/Avalonia.Visuals/Media/Color.cs index cbf5a86fd6..f37d529944 100644 --- a/src/Avalonia.Visuals/Media/Color.cs +++ b/src/Avalonia.Visuals/Media/Color.cs @@ -4,6 +4,7 @@ using System; using System.Globalization; using System.Linq; +using System.Numerics; using System.Reflection; namespace Avalonia.Media @@ -81,6 +82,32 @@ namespace Avalonia.Media ); } + /// + /// Creates a from a . + /// + /// The value. + /// True if the color vector is normalized floats, false if it's bytes (0-255) + /// The color. + public static Color FromVector4(Vector4 value, bool normalized) + { + if (normalized) + { + return new Color( + (byte)(value.W * 255), + (byte)(value.X * 255), + (byte)(value.Y * 255), + (byte)(value.Z * 255) + ); + } else { + return new Color( + (byte)(value.W), + (byte)(value.X), + (byte)(value.Y), + (byte)(value.Z) + ); + } + + } /// /// Parses a color string. ///