2020-08-27 00:04:40 +03:00
|
|
|
|
// 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.
|
|
|
|
|
|
2020-10-20 03:38:40 +03:00
|
|
|
|
#nullable enable
|
|
|
|
|
|
2020-08-27 00:04:40 +03:00
|
|
|
|
using System;
|
2020-10-24 01:28:58 +03:00
|
|
|
|
using System.Collections.Generic;
|
2020-08-27 00:04:40 +03:00
|
|
|
|
using System.Diagnostics;
|
2020-10-13 20:21:17 +03:00
|
|
|
|
using System.Diagnostics.CodeAnalysis;
|
2020-09-01 03:20:26 +03:00
|
|
|
|
using System.Linq;
|
2022-05-25 20:18:30 +03:00
|
|
|
|
using CommunityToolkit.WinUI.Lottie.UIData.Tools;
|
2020-08-27 00:04:40 +03:00
|
|
|
|
using Microsoft.UI.Xaml.Controls;
|
2022-05-25 20:18:30 +03:00
|
|
|
|
|
|
|
|
|
#if WINAPPSDK
|
|
|
|
|
using Microsoft.UI.Composition;
|
|
|
|
|
#else
|
2020-08-27 00:04:40 +03:00
|
|
|
|
using Windows.UI.Composition;
|
2022-05-25 20:18:30 +03:00
|
|
|
|
#endif
|
2020-08-27 00:04:40 +03:00
|
|
|
|
|
2022-05-25 20:18:30 +03:00
|
|
|
|
namespace CommunityToolkit.WinUI.Lottie
|
2020-08-27 00:04:40 +03:00
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Information from which a composition's content can be instantiated. Contains the WinCompData
|
|
|
|
|
/// translation of a composition and some metadata. This allows multiple instances of the translation
|
|
|
|
|
/// to be instantiated without requiring repeated translations.
|
|
|
|
|
/// </summary>
|
2020-10-24 01:28:58 +03:00
|
|
|
|
sealed class AnimatedVisualFactory
|
|
|
|
|
: IAnimatedVisualSource
|
2020-08-27 00:04:40 +03:00
|
|
|
|
{
|
2020-10-24 01:28:58 +03:00
|
|
|
|
readonly Dictionary<Uri, ICompositionSurface?> _imageCache = new Dictionary<Uri, ICompositionSurface?>();
|
2020-10-13 20:21:17 +03:00
|
|
|
|
readonly LottieVisualDiagnostics? _diagnostics;
|
2020-10-24 01:28:58 +03:00
|
|
|
|
Loader? _loader;
|
2020-10-13 20:21:17 +03:00
|
|
|
|
WinCompData.Visual? _wincompDataRootVisual;
|
|
|
|
|
WinCompData.CompositionPropertySet? _wincompDataThemingPropertySet;
|
2023-02-28 00:51:11 +03:00
|
|
|
|
IEnumerable<WinCompData.AnimationController>? _wincompDataAnimationControllers;
|
2020-10-13 20:21:17 +03:00
|
|
|
|
CompositionPropertySet? _themingPropertySet;
|
2020-08-27 00:04:40 +03:00
|
|
|
|
double _width;
|
|
|
|
|
double _height;
|
|
|
|
|
TimeSpan _duration;
|
|
|
|
|
|
2020-10-24 01:28:58 +03:00
|
|
|
|
internal AnimatedVisualFactory(Loader loader, LottieVisualDiagnostics? diagnostics)
|
2020-08-27 00:04:40 +03:00
|
|
|
|
{
|
2020-10-24 01:28:58 +03:00
|
|
|
|
_loader = loader;
|
2020-08-27 00:04:40 +03:00
|
|
|
|
_diagnostics = diagnostics;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal void SetDimensions(double width, double height, TimeSpan duration)
|
|
|
|
|
{
|
|
|
|
|
_width = width;
|
|
|
|
|
_height = height;
|
|
|
|
|
_duration = duration;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal void SetRootVisual(WinCompData.Visual rootVisual)
|
|
|
|
|
{
|
2020-09-01 03:20:26 +03:00
|
|
|
|
// Save the root visual.
|
2020-08-27 00:04:40 +03:00
|
|
|
|
_wincompDataRootVisual = rootVisual;
|
2020-09-01 03:20:26 +03:00
|
|
|
|
|
|
|
|
|
// Find the theming property set, if any.
|
|
|
|
|
var graph = ObjectGraph<Graph.Node>.FromCompositionObject(_wincompDataRootVisual, includeVertices: false);
|
|
|
|
|
_wincompDataThemingPropertySet = graph.
|
|
|
|
|
CompositionObjectNodes.
|
|
|
|
|
Where(n => n.Object is WinCompData.CompositionPropertySet cps && cps.Owner is null).
|
|
|
|
|
Select(n => (WinCompData.CompositionPropertySet)n.Object).FirstOrDefault();
|
2023-02-28 00:51:11 +03:00
|
|
|
|
_wincompDataAnimationControllers = graph.CompositionObjectNodes.Where(n => (n.Object is WinCompData.AnimationController) && ((WinCompData.AnimationController)n.Object).IsCustom).Select(n => (WinCompData.AnimationController)n.Object);
|
2020-08-27 00:04:40 +03:00
|
|
|
|
}
|
|
|
|
|
|
2021-03-06 03:38:42 +03:00
|
|
|
|
internal bool CanInstantiate => _wincompDataRootVisual is not null;
|
2020-08-27 00:04:40 +03:00
|
|
|
|
|
2020-10-13 20:21:17 +03:00
|
|
|
|
public IAnimatedVisual? TryCreateAnimatedVisual(Compositor compositor, [MaybeNull] out object diagnostics)
|
2020-08-27 00:04:40 +03:00
|
|
|
|
{
|
2020-09-01 03:20:26 +03:00
|
|
|
|
// Clone the Diagnostics object so that the data from the translation is captured, then we
|
|
|
|
|
// will update the clone with information about this particular instantiation.
|
2020-08-27 00:04:40 +03:00
|
|
|
|
var diags = _diagnostics?.Clone();
|
|
|
|
|
diagnostics = diags;
|
|
|
|
|
|
|
|
|
|
if (!CanInstantiate)
|
|
|
|
|
{
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
var sw = Stopwatch.StartNew();
|
2020-09-01 03:20:26 +03:00
|
|
|
|
|
2020-10-24 01:28:58 +03:00
|
|
|
|
var instantiator = new Instantiator(compositor, surfaceResolver: LoadImageFromUri);
|
2020-09-01 03:20:26 +03:00
|
|
|
|
|
2021-03-06 03:38:42 +03:00
|
|
|
|
// _wincompDataRootVisual is not null is implied by CanInstantiate.
|
2023-02-28 00:51:11 +03:00
|
|
|
|
Visual rootVisual = (Visual)instantiator.GetInstance(_wincompDataRootVisual!);
|
|
|
|
|
IEnumerable<AnimationController> animationControllers = _wincompDataAnimationControllers!.Select(o => (AnimationController)instantiator.GetInstance(o));
|
|
|
|
|
|
|
|
|
|
var result = new DisposableAnimatedVisual(rootVisual, animationControllers)
|
2020-08-27 00:04:40 +03:00
|
|
|
|
{
|
|
|
|
|
Size = new System.Numerics.Vector2((float)_width, (float)_height),
|
|
|
|
|
Duration = _duration,
|
|
|
|
|
};
|
|
|
|
|
|
2021-03-06 03:38:42 +03:00
|
|
|
|
if (diags is not null)
|
2020-08-27 00:04:40 +03:00
|
|
|
|
{
|
2021-03-06 03:38:42 +03:00
|
|
|
|
if (_wincompDataThemingPropertySet is not null && _themingPropertySet is null)
|
2020-09-01 03:20:26 +03:00
|
|
|
|
{
|
|
|
|
|
// Instantiate the theming property set. This is shared by all of the instantiations.
|
|
|
|
|
_themingPropertySet = (CompositionPropertySet)instantiator.GetInstance(_wincompDataThemingPropertySet);
|
2020-10-13 20:21:17 +03:00
|
|
|
|
|
2021-03-06 03:38:42 +03:00
|
|
|
|
// _diagnostics is not null is implied by diags is not null;
|
2020-10-13 20:21:17 +03:00
|
|
|
|
diags.ThemingPropertySet = _diagnostics!.ThemingPropertySet = _themingPropertySet;
|
2020-09-01 03:20:26 +03:00
|
|
|
|
}
|
|
|
|
|
|
2020-08-27 00:04:40 +03:00
|
|
|
|
diags.InstantiationTime = sw.Elapsed;
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-24 01:28:58 +03:00
|
|
|
|
// After the first instantiation, all the images are cached so the
|
|
|
|
|
// loader is no longer needed.
|
|
|
|
|
_loader?.Dispose();
|
|
|
|
|
_loader = null;
|
|
|
|
|
|
2020-08-27 00:04:40 +03:00
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-10-24 01:28:58 +03:00
|
|
|
|
|
|
|
|
|
ICompositionSurface? LoadImageFromUri(Uri uri)
|
|
|
|
|
{
|
|
|
|
|
if (!_imageCache.TryGetValue(uri, out var result))
|
|
|
|
|
{
|
|
|
|
|
// The loader will not be null, because either this is the
|
|
|
|
|
// first instantiation of the animated visual in which case the
|
|
|
|
|
// image loader hasn't been set to null, or it's a second instantiation
|
|
|
|
|
// so the images are already cached.
|
|
|
|
|
result = _loader!.LoadImage(uri);
|
|
|
|
|
|
|
|
|
|
// Cache the result so we can share the surfaces.
|
|
|
|
|
_imageCache.Add(uri, result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
2020-08-27 00:04:40 +03:00
|
|
|
|
}
|
|
|
|
|
}
|