diff --git a/source/UIData/Tools/Graph.cs b/source/UIData/Tools/Graph.cs index 234752b..c8d7bde 100644 --- a/source/UIData/Tools/Graph.cs +++ b/source/UIData/Tools/Graph.cs @@ -51,6 +51,39 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.UIData.Tools /// public int Position { get; private set; } + /// + /// Returns True iff this node is reachable from the given node. + /// + /// The node to test. + /// True if this node is reachable from the given node. + public bool IsReachableFrom(Node node) => node is null ? false : IsReachableFrom(node, new HashSet>()); + + bool IsReachableFrom(Node targetNode, HashSet> alreadyVisited) + { + // Walk the tree of references to this node, ignoring any that have already + // been visited. + foreach (var vertex in targetNode.InReferences) + { + // inRef is a node that directly references this node. + var inRef = vertex.Node; + + if (alreadyVisited.Add(inRef)) + { + // We haven't examined the inRef node yet. + if (inRef == targetNode || inRef.IsReachableFrom(targetNode, alreadyVisited)) + { + // inRef is the targetNode, or it's reachable from targetNode. + return true; + } + + // This node is not reachable from the targetNode. + } + } + + // Not reachable from targetNode. + return false; + } + public struct Vertex { /// diff --git a/source/UIDataCodeGen/CodeGen/InstantiatorGeneratorBase.cs b/source/UIDataCodeGen/CodeGen/InstantiatorGeneratorBase.cs index 29d7d36..6b45bf2 100644 --- a/source/UIDataCodeGen/CodeGen/InstantiatorGeneratorBase.cs +++ b/source/UIDataCodeGen/CodeGen/InstantiatorGeneratorBase.cs @@ -2797,20 +2797,28 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.UIData.CodeGen bool GenerateCompositionPathGeometryFactory(CodeBuilder builder, CompositionPathGeometry obj, ObjectData node) { - WriteObjectFactoryStart(builder, node); - if (obj.Path is null) + var path = obj.Path is null ? null : _objectGraph[obj.Path]; + var createPathText = path is null ? string.Empty : CallFactoryFromFor(node, path); + var createPathGeometryText = $"_c{Deref}CreatePathGeometry({createPathText})"; + + if (obj.Animators.Count == 0 && + obj.Properties.Names.Count == 0 && + !obj.TrimEnd.HasValue && + !obj.TrimOffset.HasValue && + !obj.TrimStart.HasValue && + !node.IsReachableFrom(path)) { - WriteCreateAssignment(builder, node, $"_c{Deref}CreatePathGeometry()"); + WriteSimpleObjectFactory(builder, node, createPathGeometryText); } else { - var path = _objectGraph[obj.Path]; - WriteCreateAssignment(builder, node, $"_c{Deref}CreatePathGeometry({CallFactoryFromFor(node, path)})"); + WriteObjectFactoryStart(builder, node); + WriteCreateAssignment(builder, node, createPathGeometryText); + InitializeCompositionGeometry(builder, obj, node); + StartAnimationsOnResult(builder, obj, node); + WriteObjectFactoryEnd(builder); } - InitializeCompositionGeometry(builder, obj, node); - StartAnimationsOnResult(builder, obj, node); - WriteObjectFactoryEnd(builder); return true; } @@ -3009,27 +3017,51 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.UIData.CodeGen bool GenerateCompositionSurfaceBrushFactory(CodeBuilder builder, CompositionSurfaceBrush obj, ObjectData node) { - WriteObjectFactoryStart(builder, node); - WriteCreateAssignment(builder, node, $"_c{Deref}CreateSurfaceBrush()"); - InitializeCompositionBrush(builder, obj, node); - - if (obj.Surface != null) + var surfaceNode = obj.Surface switch { - switch (obj.Surface) + CompositionObject compositionObject => NodeFor(compositionObject), + Wmd.LoadedImageSurface loadedImageSurface => NodeFor(loadedImageSurface), + _ => null, + }; + + // Create the code that initializes the Surface. + var surfaceInitializationText = obj.Surface switch + { + CompositionObject compositionObject => CallFactoryFromFor(node, compositionObject), + Wmd.LoadedImageSurface _ => surfaceNode.FieldName, + null => string.Empty, + _ => throw new InvalidOperationException(), + }; + + var isReachableFromSurfaceNode = node.IsReachableFrom(surfaceNode); + + if (obj.Animators.Count == 0 && + obj.Properties.Names.Count == 0 && + !isReachableFromSurfaceNode) + { + WriteSimpleObjectFactory(builder, node, $"_c{Deref}CreateSurfaceBrush({surfaceInitializationText})"); + } + else + { + WriteObjectFactoryStart(builder, node); + + if (isReachableFromSurfaceNode) { - case CompositionObject compositionObject: - WriteSetPropertyStatement(builder, "Surface", CallFactoryFromFor(node, compositionObject)); - break; - case Wmd.LoadedImageSurface loadedImageSurface: - WriteSetPropertyStatement(builder, "Surface", NodeFor(loadedImageSurface).FieldName); - break; - default: - throw new InvalidOperationException(); + // The Surface depends on the brush, so the brush needs to be created and assigned + // before the Surface. + WriteCreateAssignment(builder, node, $"_c{Deref}CreateSurfaceBrush()"); + WriteSetPropertyStatement(builder, "Surface", surfaceInitializationText); } + else + { + WriteCreateAssignment(builder, node, $"_c{Deref}CreateSurfaceBrush({surfaceInitializationText})"); + } + + InitializeCompositionBrush(builder, obj, node); + StartAnimationsOnResult(builder, obj, node); + WriteObjectFactoryEnd(builder); } - StartAnimationsOnResult(builder, obj, node); - WriteObjectFactoryEnd(builder); return true; }