lottie-ios 3.2.2 → 3.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (316) hide show
  1. package/.github/actions/setup/action.yml +32 -0
  2. package/.github/issue_template.md +6 -23
  3. package/.github/workflows/main.yml +98 -0
  4. package/.spi.yml +6 -0
  5. package/.swift-version +1 -0
  6. package/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata +7 -0
  7. package/.swiftpm/xcode/package.xcworkspace/xcuserdata/cal.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  8. package/.swiftpm/xcode/xcuserdata/{brandonwithrow.xcuserdatad → cal.xcuserdatad}/xcschemes/xcschememanagement.plist +25 -15
  9. package/.swiftpm/xcode/xcuserdata/calstephens.xcuserdatad/xcschemes/xcschememanagement.plist +14 -0
  10. package/Gemfile +4 -0
  11. package/Gemfile.lock +102 -0
  12. package/Lottie.xcodeproj/project.pbxproj +2003 -1972
  13. package/Lottie.xcodeproj/project.xcworkspace/contents.xcworkspacedata +1 -1
  14. package/Lottie.xcodeproj/project.xcworkspace/xcuserdata/cal.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  15. package/Lottie.xcodeproj/xcshareddata/xcschemes/{Lottie_iOS.xcscheme → Lottie (iOS).xcscheme } +15 -18
  16. package/Lottie.xcodeproj/xcshareddata/xcschemes/{Lottie_macOS.xcscheme → Lottie (macOS).xcscheme } +7 -20
  17. package/Lottie.xcodeproj/xcshareddata/xcschemes/{Lottie_tvOS.xcscheme → Lottie (tvOS).xcscheme } +5 -18
  18. package/Lottie.xcodeproj/xcuserdata/cal.xcuserdatad/xcschemes/xcschememanagement.plist +37 -0
  19. package/Lottie.xcodeproj/xcuserdata/calstephens.xcuserdatad/xcschemes/xcschememanagement.plist +24 -0
  20. package/Lottie.xcworkspace/contents.xcworkspacedata +10 -0
  21. package/Lottie.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
  22. package/Lottie.xcworkspace/xcshareddata/swiftpm/Package.resolved +115 -0
  23. package/Lottie.xcworkspace/xcuserdata/cal.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  24. package/{Lottie.xcodeproj/xcuserdata/brandonwithrow.xcuserdatad → Lottie.xcworkspace/xcuserdata/cal.xcuserdatad}/xcdebugger/Breakpoints_v2.xcbkptlist +2 -2
  25. package/Lottie.xcworkspace/xcuserdata/cal.xcuserdatad/xcdebugger/Expressions.xcexplist +153 -0
  26. package/Lottie.xcworkspace/xcuserdata/calstephens.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  27. package/Lottie.xcworkspace/xcuserdata/calstephens.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +39 -0
  28. package/Lottie.xcworkspace/xcuserdata/calstephens.xcuserdatad/xcdebugger/Expressions.xcexplist +25 -0
  29. package/Lottie.xcworkspace/xcuserdata/calstephens.xcuserdatad/xcschemes/xcschememanagement.plist +30 -0
  30. package/Package.resolved +88 -0
  31. package/Package.swift +10 -15
  32. package/README.md +40 -60
  33. package/Rakefile +121 -0
  34. package/Sources/Private/CoreAnimation/Animations/CAAnimation+TimingConfiguration.swift +81 -0
  35. package/Sources/Private/CoreAnimation/Animations/CALayer+addAnimation.swift +448 -0
  36. package/Sources/Private/CoreAnimation/Animations/CombinedShapeAnimation.swift +53 -0
  37. package/Sources/Private/CoreAnimation/Animations/CustomPathAnimation.swift +41 -0
  38. package/Sources/Private/CoreAnimation/Animations/EllipseAnimation.swift +28 -0
  39. package/Sources/Private/CoreAnimation/Animations/GradientAnimations.swift +210 -0
  40. package/Sources/Private/CoreAnimation/Animations/LayerProperty.swift +229 -0
  41. package/Sources/Private/CoreAnimation/Animations/OpacityAnimation.swift +52 -0
  42. package/Sources/Private/CoreAnimation/Animations/RectangleAnimation.swift +31 -0
  43. package/Sources/Private/CoreAnimation/Animations/ShapeAnimation.swift +246 -0
  44. package/Sources/Private/CoreAnimation/Animations/StarAnimation.swift +95 -0
  45. package/Sources/Private/CoreAnimation/Animations/StrokeAnimation.swift +70 -0
  46. package/Sources/Private/CoreAnimation/Animations/TransformAnimations.swift +205 -0
  47. package/Sources/Private/CoreAnimation/Animations/VisibilityAnimation.swift +37 -0
  48. package/Sources/Private/CoreAnimation/CompatibilityTracker.swift +130 -0
  49. package/Sources/Private/CoreAnimation/CoreAnimationLayer.swift +492 -0
  50. package/Sources/Private/CoreAnimation/Extensions/CALayer+fillBounds.swift +35 -0
  51. package/Sources/Private/CoreAnimation/Extensions/KeyframeGroup+exactlyOneKeyframe.swift +37 -0
  52. package/Sources/Private/CoreAnimation/Extensions/Keyframes+combinedIfPossible.swift +73 -0
  53. package/Sources/Private/CoreAnimation/Layers/AnimationLayer.swift +83 -0
  54. package/Sources/Private/CoreAnimation/Layers/BaseAnimationLayer.swift +33 -0
  55. package/Sources/Private/CoreAnimation/Layers/BaseCompositionLayer.swift +87 -0
  56. package/Sources/Private/CoreAnimation/Layers/CALayer+setupLayerHierarchy.swift +131 -0
  57. package/Sources/Private/CoreAnimation/Layers/GradientRenderLayer.swift +94 -0
  58. package/Sources/Private/CoreAnimation/Layers/ImageLayer.swift +79 -0
  59. package/Sources/Private/CoreAnimation/Layers/LayerModel+makeAnimationLayer.swift +60 -0
  60. package/Sources/Private/CoreAnimation/Layers/MaskCompositionLayer.swift +138 -0
  61. package/Sources/Private/CoreAnimation/Layers/PreCompLayer.swift +140 -0
  62. package/Sources/Private/CoreAnimation/Layers/ShapeItemLayer.swift +302 -0
  63. package/Sources/Private/CoreAnimation/Layers/ShapeLayer.swift +319 -0
  64. package/Sources/Private/CoreAnimation/Layers/SolidLayer.swift +47 -0
  65. package/Sources/Private/CoreAnimation/Layers/TextLayer.swift +91 -0
  66. package/Sources/Private/CoreAnimation/Layers/TransformLayer.swift +11 -0
  67. package/Sources/Private/CoreAnimation/ValueProviderStore.swift +139 -0
  68. package/{lottie-swift/src/Private → Sources/Private/MainThread}/LayerContainers/CompLayers/CompositionLayer.swift +93 -85
  69. package/{lottie-swift/src/Private → Sources/Private/MainThread}/LayerContainers/CompLayers/ImageCompositionLayer.swift +24 -21
  70. package/{lottie-swift/src/Private → Sources/Private/MainThread}/LayerContainers/CompLayers/MaskContainerLayer.swift +75 -54
  71. package/{lottie-swift/src/Private → Sources/Private/MainThread}/LayerContainers/CompLayers/NullCompositionLayer.swift +5 -5
  72. package/{lottie-swift/src/Private → Sources/Private/MainThread}/LayerContainers/CompLayers/PreCompositionLayer.swift +61 -44
  73. package/{lottie-swift/src/Private → Sources/Private/MainThread}/LayerContainers/CompLayers/ShapeCompositionLayer.swift +22 -20
  74. package/{lottie-swift/src/Private → Sources/Private/MainThread}/LayerContainers/CompLayers/SolidCompositionLayer.swift +26 -17
  75. package/{lottie-swift/src/Private → Sources/Private/MainThread}/LayerContainers/CompLayers/TextCompositionLayer.swift +42 -36
  76. package/{lottie-swift/src/Private/LayerContainers/AnimationContainer.swift → Sources/Private/MainThread/LayerContainers/MainThreadAnimationLayer.swift} +200 -140
  77. package/Sources/Private/MainThread/LayerContainers/Utility/CachedImageProvider.swift +47 -0
  78. package/{lottie-swift/src/Private → Sources/Private/MainThread}/LayerContainers/Utility/CompositionLayersInitializer.swift +33 -24
  79. package/{lottie-swift/src/Private/LayerContainers/Utility/TextLayer.swift → Sources/Private/MainThread/LayerContainers/Utility/CoreTextRenderLayer.swift} +120 -106
  80. package/{lottie-swift/src/Private → Sources/Private/MainThread}/LayerContainers/Utility/InvertedMatteLayer.swift +25 -24
  81. package/Sources/Private/MainThread/LayerContainers/Utility/LayerFontProvider.swift +41 -0
  82. package/{lottie-swift/src/Private → Sources/Private/MainThread}/LayerContainers/Utility/LayerImageProvider.swift +20 -16
  83. package/Sources/Private/MainThread/LayerContainers/Utility/LayerTextProvider.swift +40 -0
  84. package/{lottie-swift/src/Private → Sources/Private/MainThread}/LayerContainers/Utility/LayerTransformNode.swift +82 -67
  85. package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/Extensions/ItemsExtension.swift +4 -4
  86. package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/NodeProperties/NodeProperty.swift +29 -21
  87. package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/NodeProperties/Protocols/AnyNodeProperty.swift +16 -10
  88. package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/NodeProperties/Protocols/AnyValueContainer.swift +6 -6
  89. package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/NodeProperties/Protocols/KeypathSearchable.swift +5 -5
  90. package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/NodeProperties/Protocols/NodePropertyMap.swift +10 -8
  91. package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/NodeProperties/ValueContainer.swift +25 -21
  92. package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/NodeProperties/ValueProviders/GroupInterpolator.swift +23 -17
  93. package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/NodeProperties/ValueProviders/KeyframeInterpolator.swift +125 -108
  94. package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/NodeProperties/ValueProviders/SingleValueProvider.swift +22 -17
  95. package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/Nodes/ModifierNodes/TrimPathNode.swift +110 -79
  96. package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/Nodes/OutputNodes/GroupOutputNode.swift +22 -16
  97. package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/Nodes/OutputNodes/PassThroughOutputNode.swift +19 -15
  98. package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/Nodes/OutputNodes/PathOutputNode.swift +22 -20
  99. package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/Nodes/OutputNodes/Renderables/FillRenderer.swift +22 -23
  100. package/Sources/Private/MainThread/NodeRenderSystem/Nodes/OutputNodes/Renderables/GradientFillRenderer.swift +242 -0
  101. package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/Nodes/OutputNodes/Renderables/GradientStrokeRenderer.swift +30 -24
  102. package/{lottie-swift/src/Private/NodeRenderSystem/Nodes/OutputNodes/Renderables/GradientFillRenderer.swift → Sources/Private/MainThread/NodeRenderSystem/Nodes/OutputNodes/Renderables/LegacyGradientFillRenderer.swift} +93 -66
  103. package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/Nodes/OutputNodes/Renderables/StrokeRenderer.swift +23 -19
  104. package/Sources/Private/MainThread/NodeRenderSystem/Nodes/PathNodes/EllipseNode.swift +139 -0
  105. package/Sources/Private/MainThread/NodeRenderSystem/Nodes/PathNodes/PolygonNode.swift +170 -0
  106. package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/Nodes/PathNodes/RectNode.swift +95 -58
  107. package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/Nodes/PathNodes/ShapeNode.swift +42 -29
  108. package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/Nodes/PathNodes/StarNode.swift +108 -69
  109. package/Sources/Private/MainThread/NodeRenderSystem/Nodes/RenderContainers/GroupNode.swift +155 -0
  110. package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/Nodes/RenderNodes/FillNode.swift +51 -37
  111. package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/Nodes/RenderNodes/GradientFillNode.swift +54 -42
  112. package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/Nodes/RenderNodes/GradientStrokeNode.swift +65 -57
  113. package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/Nodes/RenderNodes/StrokeNode.swift +84 -58
  114. package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/Nodes/Text/TextAnimatorNode.swift +138 -119
  115. package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/Protocols/AnimatorNode.swift +80 -79
  116. package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/Protocols/PathNode.swift +5 -3
  117. package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/Protocols/RenderNode.swift +22 -17
  118. package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/RenderLayers/ShapeContainerLayer.swift +27 -24
  119. package/{lottie-swift/src/Private → Sources/Private/MainThread}/NodeRenderSystem/RenderLayers/ShapeRenderLayer.swift +34 -25
  120. package/Sources/Private/Model/Animation.swift +160 -0
  121. package/Sources/Private/Model/Assets/Asset.swift +43 -0
  122. package/{lottie-swift/src → Sources}/Private/Model/Assets/AssetLibrary.swift +40 -13
  123. package/Sources/Private/Model/Assets/ImageAsset.swift +112 -0
  124. package/{lottie-swift/src → Sources}/Private/Model/Assets/PrecompAsset.swift +20 -10
  125. package/Sources/Private/Model/DictionaryInitializable.swift +67 -0
  126. package/Sources/Private/Model/Extensions/Bundle.swift +34 -0
  127. package/{lottie-swift/src → Sources}/Private/Model/Extensions/KeyedDecodingContainerExtensions.swift +8 -4
  128. package/Sources/Private/Model/Keyframes/KeyframeData.swift +113 -0
  129. package/Sources/Private/Model/Keyframes/KeyframeGroup.swift +197 -0
  130. package/{lottie-swift/src → Sources}/Private/Model/Layers/ImageLayerModel.swift +21 -11
  131. package/Sources/Private/Model/Layers/LayerModel.swift +228 -0
  132. package/{lottie-swift/src → Sources}/Private/Model/Layers/PreCompLayerModel.swift +39 -22
  133. package/{lottie-swift/src → Sources}/Private/Model/Layers/ShapeLayerModel.swift +23 -12
  134. package/{lottie-swift/src → Sources}/Private/Model/Layers/SolidLayerModel.swift +31 -19
  135. package/{lottie-swift/src → Sources}/Private/Model/Layers/TextLayerModel.swift +33 -19
  136. package/Sources/Private/Model/Objects/DashPattern.swift +44 -0
  137. package/Sources/Private/Model/Objects/Marker.swift +33 -0
  138. package/Sources/Private/Model/Objects/Mask.swift +80 -0
  139. package/Sources/Private/Model/Objects/Transform.swift +179 -0
  140. package/Sources/Private/Model/ShapeItems/Ellipse.swift +75 -0
  141. package/Sources/Private/Model/ShapeItems/Fill.swift +74 -0
  142. package/Sources/Private/Model/ShapeItems/GradientFill.swift +124 -0
  143. package/Sources/Private/Model/ShapeItems/GradientStroke.swift +186 -0
  144. package/Sources/Private/Model/ShapeItems/Group.swift +48 -0
  145. package/{lottie-swift/src → Sources}/Private/Model/ShapeItems/Merge.swift +29 -11
  146. package/Sources/Private/Model/ShapeItems/Rectangle.swift +73 -0
  147. package/Sources/Private/Model/ShapeItems/Repeater.swift +136 -0
  148. package/Sources/Private/Model/ShapeItems/Shape.swift +56 -0
  149. package/Sources/Private/Model/ShapeItems/ShapeItem.swift +163 -0
  150. package/Sources/Private/Model/ShapeItems/ShapeTransform.swift +136 -0
  151. package/Sources/Private/Model/ShapeItems/Star.swift +132 -0
  152. package/Sources/Private/Model/ShapeItems/Stroke.swift +102 -0
  153. package/Sources/Private/Model/ShapeItems/Trim.swift +78 -0
  154. package/Sources/Private/Model/Text/Font.swift +61 -0
  155. package/{lottie-swift/src → Sources}/Private/Model/Text/Glyph.swift +63 -39
  156. package/Sources/Private/Model/Text/TextAnimator.swift +164 -0
  157. package/Sources/Private/Model/Text/TextDocument.swift +123 -0
  158. package/Sources/Private/RootAnimationLayer.swift +51 -0
  159. package/{lottie-swift/src → Sources}/Private/Utility/Debugging/AnimatorNodeDebugging.swift +7 -7
  160. package/{lottie-swift/src → Sources}/Private/Utility/Debugging/LayerDebugging.swift +72 -48
  161. package/Sources/Private/Utility/Debugging/TestHelpers.swift +10 -0
  162. package/{lottie-swift/src → Sources}/Private/Utility/Extensions/AnimationKeypathExtension.swift +69 -59
  163. package/Sources/Private/Utility/Extensions/BlendMode+Filter.swift +31 -0
  164. package/Sources/Private/Utility/Extensions/CGColor+RGB.swift +22 -0
  165. package/{lottie-swift/src → Sources}/Private/Utility/Extensions/CGFloatExtensions.swift +70 -67
  166. package/Sources/Private/Utility/Extensions/DataExtension.swift +27 -0
  167. package/Sources/Private/Utility/Extensions/MathKit.swift +450 -0
  168. package/Sources/Private/Utility/Extensions/StringExtensions.swift +38 -0
  169. package/{lottie-swift/src → Sources}/Private/Utility/Helpers/AnimationContext.swift +42 -16
  170. package/Sources/Private/Utility/Interpolatable/InterpolatableExtensions.swift +134 -0
  171. package/{lottie-swift/src → Sources}/Private/Utility/Interpolatable/KeyframeExtensions.swift +13 -10
  172. package/Sources/Private/Utility/Interpolatable/KeyframeGroup+Extensions.swift +59 -0
  173. package/{lottie-swift/src → Sources}/Private/Utility/Primitives/BezierPath.swift +229 -143
  174. package/Sources/Private/Utility/Primitives/CGPointExtension.swift +35 -0
  175. package/{lottie-swift/src → Sources}/Private/Utility/Primitives/ColorExtension.swift +46 -14
  176. package/{lottie-swift/src → Sources}/Private/Utility/Primitives/CompoundBezierPath.swift +58 -49
  177. package/Sources/Private/Utility/Primitives/CurveVertex.swift +184 -0
  178. package/Sources/Private/Utility/Primitives/PathElement.swift +75 -0
  179. package/Sources/Private/Utility/Primitives/UnitBezier.swift +115 -0
  180. package/Sources/Private/Utility/Primitives/VectorsExtensions.swift +341 -0
  181. package/Sources/Public/Animation/AnimationPublic.swift +266 -0
  182. package/Sources/Public/Animation/AnimationView.swift +1335 -0
  183. package/Sources/Public/Animation/AnimationViewInitializers.swift +109 -0
  184. package/Sources/Public/AnimationCache/AnimationCacheProvider.swift +22 -0
  185. package/{lottie-swift/src → Sources}/Public/AnimationCache/LRUAnimationCache.swift +22 -18
  186. package/Sources/Public/DynamicProperties/AnimationKeypath.swift +49 -0
  187. package/Sources/Public/DynamicProperties/AnyValueProvider.swift +132 -0
  188. package/{lottie-swift/src → Sources}/Public/DynamicProperties/ValueProviders/ColorValueProvider.swift +54 -36
  189. package/{lottie-swift/src → Sources}/Public/DynamicProperties/ValueProviders/FloatValueProvider.swift +40 -36
  190. package/Sources/Public/DynamicProperties/ValueProviders/GradientValueProvider.swift +123 -0
  191. package/{lottie-swift/src → Sources}/Public/DynamicProperties/ValueProviders/PointValueProvider.swift +40 -35
  192. package/{lottie-swift/src → Sources}/Public/DynamicProperties/ValueProviders/SizeValueProvider.swift +40 -35
  193. package/{lottie-swift/src → Sources}/Public/FontProvider/AnimationFontProvider.swift +15 -8
  194. package/Sources/Public/ImageProvider/AnimationImageProvider.swift +21 -0
  195. package/Sources/Public/Keyframes/Interpolatable.swift +253 -0
  196. package/Sources/Public/Keyframes/Keyframe.swift +92 -0
  197. package/Sources/Public/Logging/LottieLogger.swift +137 -0
  198. package/Sources/Public/LottieConfiguration.swift +143 -0
  199. package/{lottie-swift/src → Sources}/Public/Primitives/AnimationTime.swift +1 -1
  200. package/{lottie-swift/src → Sources}/Public/Primitives/Color.swift +10 -6
  201. package/{lottie-swift/src → Sources}/Public/Primitives/Vectors.swift +13 -12
  202. package/Sources/Public/TextProvider/AnimationTextProvider.swift +53 -0
  203. package/{lottie-swift/src → Sources}/Public/iOS/AnimatedButton.swift +42 -26
  204. package/{lottie-swift/src → Sources}/Public/iOS/AnimatedControl.swift +100 -91
  205. package/{lottie-swift/src → Sources}/Public/iOS/AnimatedSwitch.swift +128 -94
  206. package/{lottie-swift/src → Sources}/Public/iOS/AnimationSubview.swift +3 -3
  207. package/Sources/Public/iOS/AnimationViewBase.swift +78 -0
  208. package/{lottie-swift/src → Sources}/Public/iOS/BundleImageProvider.swift +36 -34
  209. package/{lottie-swift/src → Sources}/Public/iOS/Compatibility/CompatibleAnimationKeypath.swift +4 -0
  210. package/{lottie-swift/src → Sources}/Public/iOS/Compatibility/CompatibleAnimationView.swift +38 -29
  211. package/{lottie-swift/src → Sources}/Public/iOS/FilepathImageProvider.swift +24 -21
  212. package/{lottie-swift/src → Sources}/Public/iOS/UIColorExtension.swift +4 -4
  213. package/{lottie-swift/src/Public/MacOS/AnimationSubview.swift → Sources/Public/macOS/AnimationSubview.macOS.swift} +4 -5
  214. package/{lottie-swift/src/Public/MacOS/LottieView.swift → Sources/Public/macOS/AnimationViewBase.macOS.swift} +58 -50
  215. package/{lottie-swift/src/Public/MacOS/BundleImageProvider.swift → Sources/Public/macOS/BundleImageProvider.macOS.swift} +31 -32
  216. package/Sources/Public/macOS/FilepathImageProvider.macOS.swift +67 -0
  217. package/Tests/AnimationKeypathTests.swift +94 -0
  218. package/Tests/AutomaticEngineTests.swift +57 -0
  219. package/Tests/BundleTests.swift +25 -0
  220. package/Tests/DataURLTests.swift +64 -0
  221. package/Tests/ParsingTests.swift +43 -0
  222. package/Tests/PerformanceTests.swift +215 -0
  223. package/Tests/SnapshotConfiguration.swift +153 -0
  224. package/Tests/SnapshotTests.swift +265 -0
  225. package/Tests/Utils/Bundle+Module.swift +30 -0
  226. package/Tests/Utils/HardcodedFontProvider.swift +19 -0
  227. package/Tests/Utils/HardcodedImageProvider.swift +23 -0
  228. package/Tests/Utils/Snapshotting+presentationLayer.swift +47 -0
  229. package/Tests/ValueProvidersTests.swift +27 -0
  230. package/lottie-ios.podspec +11 -12
  231. package/package.json +1 -1
  232. package/script/test-carthage/Cartfile +1 -0
  233. package/script/test-carthage/Cartfile.resolved +1 -0
  234. package/script/test-carthage/CarthageTest/AppDelegate.swift +26 -0
  235. package/script/test-carthage/CarthageTest/Assets.xcassets/AccentColor.colorset/Contents.json +11 -0
  236. package/script/test-carthage/CarthageTest/Assets.xcassets/AppIcon.appiconset/Contents.json +98 -0
  237. package/script/test-carthage/CarthageTest/Assets.xcassets/Contents.json +6 -0
  238. package/script/test-carthage/CarthageTest/Base.lproj/LaunchScreen.storyboard +25 -0
  239. package/script/test-carthage/CarthageTest/Base.lproj/Main.storyboard +24 -0
  240. package/script/test-carthage/CarthageTest/Info.plist +66 -0
  241. package/script/test-carthage/CarthageTest/SceneDelegate.swift +10 -0
  242. package/script/test-carthage/CarthageTest/ViewController.swift +15 -0
  243. package/script/test-carthage/CarthageTest-macOS/AppDelegate.swift +7 -0
  244. package/script/test-carthage/CarthageTest-macOS/Assets.xcassets/AccentColor.colorset/Contents.json +11 -0
  245. package/script/test-carthage/CarthageTest-macOS/Assets.xcassets/AppIcon.appiconset/Contents.json +58 -0
  246. package/script/test-carthage/CarthageTest-macOS/Assets.xcassets/Contents.json +6 -0
  247. package/script/test-carthage/CarthageTest-macOS/Base.lproj/Main.storyboard +717 -0
  248. package/script/test-carthage/CarthageTest-macOS/CarthageTest_macOS.entitlements +10 -0
  249. package/script/test-carthage/CarthageTest-macOS/ViewController.swift +15 -0
  250. package/script/test-carthage/CarthageTest.xcodeproj/project.pbxproj +532 -0
  251. package/script/test-carthage/CarthageTest.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
  252. package/script/test-carthage/CarthageTest.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
  253. package/script/test-carthage/CarthageTest.xcodeproj/project.xcworkspace/xcuserdata/cal.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  254. package/script/test-carthage/CarthageTest.xcodeproj/project.xcworkspace/xcuserdata/calstephens.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  255. package/script/test-carthage/CarthageTest.xcodeproj/xcuserdata/cal.xcuserdatad/xcschemes/xcschememanagement.plist +14 -0
  256. package/script/test-carthage/CarthageTest.xcodeproj/xcuserdata/calstephens.xcuserdatad/xcschemes/xcschememanagement.plist +19 -0
  257. package/script/test-carthage/Mintfile +1 -0
  258. package/script/test-spm/LottieSPM.xcworkspace/contents.xcworkspacedata +7 -0
  259. package/script/test-spm/LottieSPM.xcworkspace/xcuserdata/cal.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  260. package/script/test-spm/Mintfile +1 -0
  261. package/.swiftpm/xcode/package.xcworkspace/xcuserdata/brandonwithrow.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  262. package/Lottie/Info.plist +0 -22
  263. package/Lottie.xcodeproj/project.xcworkspace/xcuserdata/brandonwithrow.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  264. package/Lottie.xcodeproj/xcuserdata/brandonwithrow.xcuserdatad/xcschemes/LottieLibraryMacOS.xcscheme +0 -80
  265. package/Lottie.xcodeproj/xcuserdata/brandonwithrow.xcuserdatad/xcschemes/xcschememanagement.plist +0 -57
  266. package/lottie-swift/Assets/.gitkeep +0 -0
  267. package/lottie-swift/src/Private/LayerContainers/Utility/LayerFontProvider.swift +0 -37
  268. package/lottie-swift/src/Private/LayerContainers/Utility/LayerTextProvider.swift +0 -36
  269. package/lottie-swift/src/Private/Model/Animation.swift +0 -107
  270. package/lottie-swift/src/Private/Model/Assets/Asset.swift +0 -27
  271. package/lottie-swift/src/Private/Model/Assets/ImageAsset.swift +0 -48
  272. package/lottie-swift/src/Private/Model/Keyframes/Keyframe.swift +0 -128
  273. package/lottie-swift/src/Private/Model/Keyframes/KeyframeGroup.swift +0 -108
  274. package/lottie-swift/src/Private/Model/Layers/LayerModel.swift +0 -150
  275. package/lottie-swift/src/Private/Model/Objects/DashPattern.swift +0 -24
  276. package/lottie-swift/src/Private/Model/Objects/Marker.swift +0 -23
  277. package/lottie-swift/src/Private/Model/Objects/Mask.swift +0 -48
  278. package/lottie-swift/src/Private/Model/Objects/Transform.swift +0 -105
  279. package/lottie-swift/src/Private/Model/ShapeItems/Ellipse.swift +0 -50
  280. package/lottie-swift/src/Private/Model/ShapeItems/FillI.swift +0 -49
  281. package/lottie-swift/src/Private/Model/ShapeItems/GradientFill.swift +0 -86
  282. package/lottie-swift/src/Private/Model/ShapeItems/GradientStroke.swift +0 -125
  283. package/lottie-swift/src/Private/Model/ShapeItems/Group.swift +0 -32
  284. package/lottie-swift/src/Private/Model/ShapeItems/Rectangle.swift +0 -50
  285. package/lottie-swift/src/Private/Model/ShapeItems/Repeater.swift +0 -80
  286. package/lottie-swift/src/Private/Model/ShapeItems/Shape.swift +0 -37
  287. package/lottie-swift/src/Private/Model/ShapeItems/ShapeItem.swift +0 -95
  288. package/lottie-swift/src/Private/Model/ShapeItems/ShapeTransform.swift +0 -68
  289. package/lottie-swift/src/Private/Model/ShapeItems/Star.swift +0 -86
  290. package/lottie-swift/src/Private/Model/ShapeItems/Stroke.swift +0 -67
  291. package/lottie-swift/src/Private/Model/ShapeItems/Trim.swift +0 -53
  292. package/lottie-swift/src/Private/Model/Text/Font.swift +0 -35
  293. package/lottie-swift/src/Private/Model/Text/TextAnimator.swift +0 -99
  294. package/lottie-swift/src/Private/Model/Text/TextDocument.swift +0 -70
  295. package/lottie-swift/src/Private/NodeRenderSystem/Nodes/PathNodes/EllipseNode.swift +0 -109
  296. package/lottie-swift/src/Private/NodeRenderSystem/Nodes/PathNodes/PolygonNode.swift +0 -132
  297. package/lottie-swift/src/Private/NodeRenderSystem/Nodes/RenderContainers/GroupNode.swift +0 -141
  298. package/lottie-swift/src/Private/Utility/Extensions/MathKit.swift +0 -539
  299. package/lottie-swift/src/Private/Utility/Extensions/StringExtensions.swift +0 -32
  300. package/lottie-swift/src/Private/Utility/Interpolatable/Interpolatable.swift +0 -18
  301. package/lottie-swift/src/Private/Utility/Interpolatable/InterpolatableExtensions.swift +0 -170
  302. package/lottie-swift/src/Private/Utility/Primitives/CurveVertex.swift +0 -177
  303. package/lottie-swift/src/Private/Utility/Primitives/PathElement.swift +0 -68
  304. package/lottie-swift/src/Private/Utility/Primitives/VectorsExtensions.swift +0 -218
  305. package/lottie-swift/src/Public/Animation/AnimationPublic.swift +0 -196
  306. package/lottie-swift/src/Public/Animation/AnimationView.swift +0 -1006
  307. package/lottie-swift/src/Public/Animation/AnimationViewInitializers.swift +0 -83
  308. package/lottie-swift/src/Public/AnimationCache/AnimationCacheProvider.swift +0 -24
  309. package/lottie-swift/src/Public/DynamicProperties/AnimationKeypath.swift +0 -46
  310. package/lottie-swift/src/Public/DynamicProperties/AnyValueProvider.swift +0 -29
  311. package/lottie-swift/src/Public/DynamicProperties/ValueProviders/GradientValueProvider.swift +0 -114
  312. package/lottie-swift/src/Public/ImageProvider/AnimationImageProvider.swift +0 -23
  313. package/lottie-swift/src/Public/MacOS/FilepathImageProvider.swift +0 -67
  314. package/lottie-swift/src/Public/TextProvider/AnimationTextProvider.swift +0 -39
  315. package/lottie-swift/src/Public/iOS/LottieView.swift +0 -62
  316. package/lottie-swift-testing.podspec +0 -32
@@ -0,0 +1,138 @@
1
+ // Created by Cal Stephens on 1/6/22.
2
+ // Copyright © 2022 Airbnb Inc. All rights reserved.
3
+
4
+ import QuartzCore
5
+
6
+ // MARK: - MaskCompositionLayer
7
+
8
+ /// The CALayer type responsible for rendering the `Mask` of a `BaseCompositionLayer`
9
+ final class MaskCompositionLayer: CALayer {
10
+
11
+ // MARK: Lifecycle
12
+
13
+ init(masks: [Mask]) {
14
+ maskLayers = masks.map(MaskLayer.init(mask:))
15
+ super.init()
16
+
17
+ var containerLayer = BaseAnimationLayer()
18
+ var firstObject = true
19
+ for maskLayer in maskLayers {
20
+ if maskLayer.maskModel.mode.usableMode == .none {
21
+ continue
22
+ } else if maskLayer.maskModel.mode.usableMode == .add || firstObject {
23
+ firstObject = false
24
+ containerLayer.addSublayer(maskLayer)
25
+ } else {
26
+ containerLayer.mask = maskLayer
27
+ let newContainer = BaseAnimationLayer()
28
+ newContainer.addSublayer(containerLayer)
29
+ containerLayer = newContainer
30
+ }
31
+ }
32
+
33
+ addSublayer(containerLayer)
34
+ }
35
+
36
+ required init?(coder _: NSCoder) {
37
+ fatalError("init(coder:) has not been implemented")
38
+ }
39
+
40
+ /// Called by CoreAnimation to create a shadow copy of this layer
41
+ /// More details: https://developer.apple.com/documentation/quartzcore/calayer/1410842-init
42
+ override init(layer: Any) {
43
+ guard let typedLayer = layer as? Self else {
44
+ fatalError("\(Self.self).init(layer:) incorrectly called with \(type(of: layer))")
45
+ }
46
+
47
+ maskLayers = typedLayer.maskLayers
48
+ super.init(layer: typedLayer)
49
+ }
50
+
51
+ // MARK: Internal
52
+
53
+ override func layoutSublayers() {
54
+ super.layoutSublayers()
55
+
56
+ for sublayer in sublayers ?? [] {
57
+ sublayer.fillBoundsOfSuperlayer()
58
+ }
59
+ }
60
+
61
+ // MARK: Private
62
+
63
+ private let maskLayers: [MaskLayer]
64
+
65
+ }
66
+
67
+ // MARK: AnimationLayer
68
+
69
+ extension MaskCompositionLayer: AnimationLayer {
70
+ func setupAnimations(context: LayerAnimationContext) throws {
71
+ for maskLayer in maskLayers {
72
+ try maskLayer.setupAnimations(context: context)
73
+ }
74
+ }
75
+ }
76
+
77
+ // MARK: - MaskLayer
78
+
79
+ extension MaskCompositionLayer {
80
+ final class MaskLayer: CAShapeLayer {
81
+
82
+ // MARK: Lifecycle
83
+
84
+ init(mask: Mask) {
85
+ maskModel = mask
86
+ super.init()
87
+
88
+ fillRule = .evenOdd
89
+ }
90
+
91
+ required init?(coder _: NSCoder) {
92
+ fatalError("init(coder:) has not been implemented")
93
+ }
94
+
95
+ /// Called by CoreAnimation to create a shadow copy of this layer
96
+ /// More details: https://developer.apple.com/documentation/quartzcore/calayer/1410842-init
97
+ override init(layer: Any) {
98
+ guard let typedLayer = layer as? Self else {
99
+ fatalError("\(Self.self).init(layer:) incorrectly called with \(type(of: layer))")
100
+ }
101
+
102
+ maskModel = typedLayer.maskModel
103
+ super.init(layer: typedLayer)
104
+ }
105
+
106
+ // MARK: Internal
107
+
108
+ let maskModel: Mask
109
+
110
+ }
111
+ }
112
+
113
+ // MARK: - MaskCompositionLayer.MaskLayer + AnimationLayer
114
+
115
+ extension MaskCompositionLayer.MaskLayer: AnimationLayer {
116
+ func setupAnimations(context: LayerAnimationContext) throws {
117
+ let shouldInvertMask = (maskModel.mode.usableMode == .subtract && !maskModel.inverted)
118
+ || (maskModel.mode.usableMode == .add && maskModel.inverted)
119
+
120
+ try addAnimations(
121
+ for: maskModel.shape,
122
+ context: context,
123
+ transformPath: { maskPath in
124
+ // If the mask is using `MaskMode.subtract` or has `inverted: true`,
125
+ // we have to invert the area filled by the path. We can do that by
126
+ // drawing a rectangle, and then adding a path (which is subtracted
127
+ // from the rectangle based on the .evenOdd fill mode).
128
+ if shouldInvertMask {
129
+ let path = CGMutablePath()
130
+ path.addRect(.veryLargeRect)
131
+ path.addPath(maskPath)
132
+ return path
133
+ } else {
134
+ return maskPath
135
+ }
136
+ })
137
+ }
138
+ }
@@ -0,0 +1,140 @@
1
+ // Created by Cal Stephens on 12/14/21.
2
+ // Copyright © 2021 Airbnb Inc. All rights reserved.
3
+
4
+ import QuartzCore
5
+
6
+ // MARK: - PreCompLayer
7
+
8
+ /// The `CALayer` type responsible for rendering `PreCompLayerModel`s
9
+ final class PreCompLayer: BaseCompositionLayer {
10
+
11
+ // MARK: Lifecycle
12
+
13
+ init(preCompLayer: PreCompLayerModel) {
14
+ self.preCompLayer = preCompLayer
15
+ super.init(layerModel: preCompLayer)
16
+ }
17
+
18
+ required init?(coder _: NSCoder) {
19
+ fatalError("init(coder:) has not been implemented")
20
+ }
21
+
22
+ /// Called by CoreAnimation to create a shadow copy of this layer
23
+ /// More details: https://developer.apple.com/documentation/quartzcore/calayer/1410842-init
24
+ override init(layer: Any) {
25
+ guard let typedLayer = layer as? Self else {
26
+ fatalError("\(Self.self).init(layer:) incorrectly called with \(type(of: layer))")
27
+ }
28
+
29
+ preCompLayer = typedLayer.preCompLayer
30
+ timeRemappingInterpolator = typedLayer.timeRemappingInterpolator
31
+ super.init(layer: typedLayer)
32
+ }
33
+
34
+ // MARK: Internal
35
+
36
+ /// Post-init setup for `PreCompLayer`s.
37
+ /// Should always be called after `PreCompLayer.init(preCompLayer:)`.
38
+ ///
39
+ /// This is a workaround for a hard-to-reproduce crash that was
40
+ /// triggered when `PreCompLayer.init` was called reentantly. We didn't
41
+ /// have any consistent repro steps for this crash (it happened 100% of
42
+ /// the time for some testers, and 0% of the time for other testers),
43
+ /// but moving this code out of `PreCompLayer.init` does seem to fix it.
44
+ ///
45
+ /// The stack trace looked like:
46
+ /// - `_os_unfair_lock_recursive_abort`
47
+ /// - `-[CALayerAccessibility__UIKit__QuartzCore dealloc]`
48
+ /// - `PreCompLayer.__allocating_init(preCompLayer:context:)` <- reentrant init call
49
+ /// - ...
50
+ /// - `CALayer.setupLayerHierarchy(for:context:)`
51
+ /// - `PreCompLayer.init(preCompLayer:context:)`
52
+ ///
53
+ func setup(context: LayerContext) throws {
54
+ if let timeRemappingKeyframes = preCompLayer.timeRemapping {
55
+ timeRemappingInterpolator = try .timeRemapping(keyframes: timeRemappingKeyframes, context: context)
56
+ } else {
57
+ timeRemappingInterpolator = nil
58
+ }
59
+
60
+ try setupLayerHierarchy(
61
+ for: context.animation.assetLibrary?.precompAssets[preCompLayer.referenceID]?.layers ?? [],
62
+ context: context)
63
+ }
64
+
65
+ override func setupAnimations(context: LayerAnimationContext) throws {
66
+ var context = context
67
+ context = context.addingKeypathComponent(preCompLayer.name)
68
+ try setupLayerAnimations(context: context)
69
+
70
+ // Precomp layers can adjust the local time of their child layers (relative to the
71
+ // animation's global time) via `timeRemapping` or a custom `startTime`
72
+ let contextForChildren = context.withTimeRemapping { [preCompLayer, timeRemappingInterpolator] layerLocalFrame in
73
+ if let timeRemappingInterpolator = timeRemappingInterpolator {
74
+ return timeRemappingInterpolator.value(frame: layerLocalFrame) as? AnimationFrameTime ?? layerLocalFrame
75
+ } else {
76
+ return layerLocalFrame + AnimationFrameTime(preCompLayer.startTime)
77
+ }
78
+ }
79
+
80
+ try setupChildAnimations(context: contextForChildren)
81
+ }
82
+
83
+ // MARK: Private
84
+
85
+ private let preCompLayer: PreCompLayerModel
86
+ private var timeRemappingInterpolator: KeyframeInterpolator<AnimationFrameTime>?
87
+
88
+ }
89
+
90
+ // MARK: CustomLayoutLayer
91
+
92
+ extension PreCompLayer: CustomLayoutLayer {
93
+ func layout(superlayerBounds: CGRect) {
94
+ anchorPoint = .zero
95
+
96
+ // Pre-comp layers use a size specified in the layer model,
97
+ // and clip the composition to that bounds
98
+ bounds = CGRect(
99
+ x: superlayerBounds.origin.x,
100
+ y: superlayerBounds.origin.y,
101
+ width: CGFloat(preCompLayer.width),
102
+ height: CGFloat(preCompLayer.height))
103
+
104
+ masksToBounds = true
105
+ }
106
+ }
107
+
108
+ extension KeyframeInterpolator where ValueType == AnimationFrameTime {
109
+ /// A `KeyframeInterpolator` for the given `timeRemapping` keyframes
110
+ static func timeRemapping(
111
+ keyframes timeRemappingKeyframes: KeyframeGroup<Vector1D>,
112
+ context: LayerContext)
113
+ throws
114
+ -> KeyframeInterpolator<AnimationFrameTime>
115
+ {
116
+ try context.logCompatibilityIssue("""
117
+ The Core Animation rendering engine partially supports time remapping keyframes,
118
+ but this is somewhat experimental and has some known issues. Since it doesn't work
119
+ in all cases, we have to fall back to using the main thread engine when using
120
+ `RenderingEngineOption.automatic`.
121
+ """)
122
+
123
+ // `timeRemapping` is a mapping from the animation's global time to the layer's local time.
124
+ // In the Core Animation engine, we need to perform the opposite calculation -- convert
125
+ // the layer's local time into the animation's global time. We can get this by inverting
126
+ // the time remapping, swapping the x axis (global time) and the y axis (local time).
127
+ let localTimeToGlobalTimeMapping = timeRemappingKeyframes.keyframes.map { keyframe in
128
+ Keyframe(
129
+ value: keyframe.time,
130
+ time: keyframe.value.cgFloatValue * CGFloat(context.animation.framerate),
131
+ isHold: keyframe.isHold,
132
+ inTangent: keyframe.inTangent,
133
+ outTangent: keyframe.outTangent,
134
+ spatialInTangent: keyframe.spatialInTangent,
135
+ spatialOutTangent: keyframe.spatialOutTangent)
136
+ }
137
+
138
+ return KeyframeInterpolator(keyframes: .init(localTimeToGlobalTimeMapping))
139
+ }
140
+ }
@@ -0,0 +1,302 @@
1
+ // Created by Cal Stephens on 12/13/21.
2
+ // Copyright © 2021 Airbnb Inc. All rights reserved.
3
+
4
+ import QuartzCore
5
+
6
+ // MARK: - ShapeItemLayer
7
+
8
+ /// A CALayer type that renders an array of `[ShapeItem]`s,
9
+ /// from a `Group` in a `ShapeLayerModel`.
10
+ final class ShapeItemLayer: BaseAnimationLayer {
11
+
12
+ // MARK: Lifecycle
13
+
14
+ /// Initializes a `ShapeItemLayer` that renders a `Group` from a `ShapeLayerModel`
15
+ /// - Parameters:
16
+ /// - shape: The `ShapeItem` in this group that renders a `GGPath`
17
+ /// - otherItems: Other items in this group that affect the appearance of the shape
18
+ init(shape: Item, otherItems: [Item], context: LayerContext) throws {
19
+ self.shape = shape
20
+ self.otherItems = otherItems
21
+
22
+ try context.compatibilityAssert(
23
+ shape.item.drawsCGPath,
24
+ "`ShapeItemLayer` must contain exactly one `ShapeItem` that draws a `GPPath`")
25
+
26
+ try context.compatibilityAssert(
27
+ !otherItems.contains(where: { $0.item.drawsCGPath }),
28
+ "`ShapeItemLayer` must contain exactly one `ShapeItem` that draws a `GPPath`")
29
+
30
+ super.init()
31
+
32
+ setupLayerHierarchy()
33
+ }
34
+
35
+ required init?(coder _: NSCoder) {
36
+ fatalError("init(coder:) has not been implemented")
37
+ }
38
+
39
+ /// Called by CoreAnimation to create a shadow copy of this layer
40
+ /// More details: https://developer.apple.com/documentation/quartzcore/calayer/1410842-init
41
+ override init(layer: Any) {
42
+ guard let typedLayer = layer as? Self else {
43
+ fatalError("\(Self.self).init(layer:) incorrectly called with \(type(of: layer))")
44
+ }
45
+
46
+ shape = typedLayer.shape
47
+ otherItems = typedLayer.otherItems
48
+ super.init(layer: typedLayer)
49
+ }
50
+
51
+ // MARK: Internal
52
+
53
+ /// An item that can be displayed by this layer
54
+ struct Item {
55
+ /// A `ShapeItem` that should be rendered by this layer
56
+ let item: ShapeItem
57
+
58
+ /// The group that contains this `ShapeItem`, if applicable
59
+ let parentGroup: Group?
60
+ }
61
+
62
+ override func setupAnimations(context: LayerAnimationContext) throws {
63
+ try super.setupAnimations(context: context)
64
+
65
+ guard let sublayerConfiguration = sublayerConfiguration else { return }
66
+
67
+ switch sublayerConfiguration.fill {
68
+ case .solidFill(let shapeLayer):
69
+ try setupSolidFillAnimations(shapeLayer: shapeLayer, context: context)
70
+
71
+ case .gradientFill(let gradientLayers):
72
+ try setupGradientFillAnimations(layers: gradientLayers, context: context)
73
+ }
74
+
75
+ if let gradientStrokeConfiguration = sublayerConfiguration.gradientStroke {
76
+ try setupGradientStrokeAnimations(layers: gradientStrokeConfiguration, context: context)
77
+ }
78
+ }
79
+
80
+ // MARK: Private
81
+
82
+ private struct GradientLayers {
83
+ /// The `CALayer` that renders the RGB components of the gradient
84
+ let gradientColorLayer: GradientRenderLayer
85
+ /// The `CALayer` that renders the alpha components of the gradient,
86
+ /// masking the `gradientColorLayer`
87
+ let gradientAlphaLayer: GradientRenderLayer?
88
+ /// The `CAShapeLayer` that clips the gradient layers to the expected shape
89
+ let shapeMaskLayer: CAShapeLayer
90
+ }
91
+
92
+ /// The configuration of this layer's `fill` sublayers
93
+ private enum FillLayerConfiguration {
94
+ /// This layer displays a single `CAShapeLayer`
95
+ case solidFill(CAShapeLayer)
96
+
97
+ /// This layer displays a `GradientRenderLayer` masked by a `CAShapeLayer`.
98
+ case gradientFill(GradientLayers)
99
+ }
100
+
101
+ /// The `ShapeItem` in this group that renders a `GGPath`
102
+ private let shape: Item
103
+
104
+ /// Other items in this group that affect the appearance of the shape
105
+ private let otherItems: [Item]
106
+
107
+ /// The current configuration of this layer's sublayer(s)
108
+ private var sublayerConfiguration: (fill: FillLayerConfiguration, gradientStroke: GradientLayers?)?
109
+
110
+ private func setupLayerHierarchy() {
111
+ // We have to build a different layer hierarchy depending on if
112
+ // we're rendering a gradient (a `CAGradientLayer` masked by a `CAShapeLayer`)
113
+ // or a solid shape (a simple `CAShapeLayer`).
114
+ let fillLayerConfiguration: FillLayerConfiguration
115
+ if let gradientFill = otherItems.first(GradientFill.self) {
116
+ fillLayerConfiguration = setupGradientFillLayerHierarchy(for: gradientFill)
117
+ } else {
118
+ fillLayerConfiguration = setupSolidFillLayerHierarchy()
119
+ }
120
+
121
+ let gradientStrokeConfiguration: GradientLayers?
122
+ if let gradientStroke = otherItems.first(GradientStroke.self) {
123
+ gradientStrokeConfiguration = setupGradientStrokeLayerHierarchy(for: gradientStroke)
124
+ } else {
125
+ gradientStrokeConfiguration = nil
126
+ }
127
+
128
+ sublayerConfiguration = (fillLayerConfiguration, gradientStrokeConfiguration)
129
+ }
130
+
131
+ private func setupSolidFillLayerHierarchy() -> FillLayerConfiguration {
132
+ let shapeLayer = CAShapeLayer()
133
+ addSublayer(shapeLayer)
134
+
135
+ // `CAShapeLayer.fillColor` defaults to black, so we have to
136
+ // nil out the background color if there isn't an expected fill color
137
+ if !otherItems.contains(where: { $0.item is Fill }) {
138
+ shapeLayer.fillColor = nil
139
+ }
140
+
141
+ return .solidFill(shapeLayer)
142
+ }
143
+
144
+ private func setupGradientFillLayerHierarchy(
145
+ for gradientFill: GradientFill)
146
+ -> FillLayerConfiguration
147
+ {
148
+ let pathMask = CAShapeLayer()
149
+ pathMask.fillColor = .rgb(0, 0, 0)
150
+ mask = pathMask
151
+
152
+ let rgbGradientLayer = GradientRenderLayer()
153
+ addSublayer(rgbGradientLayer)
154
+
155
+ let alphaGradientLayer: GradientRenderLayer?
156
+ if gradientFill.hasAlphaComponent {
157
+ alphaGradientLayer = GradientRenderLayer()
158
+ rgbGradientLayer.mask = alphaGradientLayer
159
+ } else {
160
+ alphaGradientLayer = nil
161
+ }
162
+
163
+ return .gradientFill(GradientLayers(
164
+ gradientColorLayer: rgbGradientLayer,
165
+ gradientAlphaLayer: alphaGradientLayer,
166
+ shapeMaskLayer: pathMask))
167
+ }
168
+
169
+ private func setupGradientStrokeLayerHierarchy(
170
+ for gradientStroke: GradientStroke)
171
+ -> GradientLayers
172
+ {
173
+ let container = BaseAnimationLayer()
174
+
175
+ let pathMask = CAShapeLayer()
176
+ pathMask.fillColor = nil
177
+ pathMask.strokeColor = .rgb(0, 0, 0)
178
+ container.mask = pathMask
179
+
180
+ let rgbGradientLayer = GradientRenderLayer()
181
+ container.addSublayer(rgbGradientLayer)
182
+ addSublayer(container)
183
+
184
+ let alphaGradientLayer: GradientRenderLayer?
185
+ if gradientStroke.hasAlphaComponent {
186
+ alphaGradientLayer = GradientRenderLayer()
187
+ rgbGradientLayer.mask = alphaGradientLayer
188
+ } else {
189
+ alphaGradientLayer = nil
190
+ }
191
+
192
+ return GradientLayers(
193
+ gradientColorLayer: rgbGradientLayer,
194
+ gradientAlphaLayer: alphaGradientLayer,
195
+ shapeMaskLayer: pathMask)
196
+ }
197
+
198
+ private func setupSolidFillAnimations(
199
+ shapeLayer: CAShapeLayer,
200
+ context: LayerAnimationContext)
201
+ throws
202
+ {
203
+ var trimPathMultiplier: PathMultiplier? = nil
204
+ if let (trim, context) = otherItems.first(Trim.self, context: context) {
205
+ trimPathMultiplier = try shapeLayer.addAnimations(for: trim, context: context)
206
+ }
207
+
208
+ try shapeLayer.addAnimations(for: shape.item, context: context.for(shape), pathMultiplier: trimPathMultiplier ?? 1)
209
+
210
+ if let (fill, context) = otherItems.first(Fill.self, context: context) {
211
+ try shapeLayer.addAnimations(for: fill, context: context)
212
+ }
213
+
214
+ if let (stroke, context) = otherItems.first(Stroke.self, context: context) {
215
+ try shapeLayer.addStrokeAnimations(for: stroke, context: context)
216
+ }
217
+ }
218
+
219
+ private func setupGradientFillAnimations(
220
+ layers: GradientLayers,
221
+ context: LayerAnimationContext)
222
+ throws
223
+ {
224
+ try layers.shapeMaskLayer.addAnimations(
225
+ for: shape.item,
226
+ context: context.for(shape),
227
+ pathMultiplier: 1)
228
+
229
+ if let (gradientFill, context) = otherItems.first(GradientFill.self, context: context) {
230
+ try layers.gradientColorLayer.addGradientAnimations(for: gradientFill, type: .rgb, context: context)
231
+ try layers.gradientAlphaLayer?.addGradientAnimations(for: gradientFill, type: .alpha, context: context)
232
+ }
233
+ }
234
+
235
+ private func setupGradientStrokeAnimations(
236
+ layers: GradientLayers,
237
+ context: LayerAnimationContext)
238
+ throws
239
+ {
240
+ var trimPathMultiplier: PathMultiplier? = nil
241
+ if let (trim, context) = otherItems.first(Trim.self, context: context) {
242
+ trimPathMultiplier = try layers.shapeMaskLayer.addAnimations(for: trim, context: context)
243
+ }
244
+
245
+ try layers.shapeMaskLayer.addAnimations(
246
+ for: shape.item,
247
+ context: context.for(shape),
248
+ pathMultiplier: trimPathMultiplier ?? 1)
249
+
250
+ if let (gradientStroke, context) = otherItems.first(GradientStroke.self, context: context) {
251
+ try layers.gradientColorLayer.addGradientAnimations(for: gradientStroke, type: .rgb, context: context)
252
+ try layers.gradientAlphaLayer?.addGradientAnimations(for: gradientStroke, type: .alpha, context: context)
253
+
254
+ try layers.shapeMaskLayer.addStrokeAnimations(for: gradientStroke, context: context)
255
+ }
256
+ }
257
+
258
+ }
259
+
260
+ // MARK: - [ShapeItem] helpers
261
+
262
+ extension Array where Element == ShapeItemLayer.Item {
263
+ /// The first `ShapeItem` in this array of the given type
264
+ func first<ItemType: ShapeItem>(
265
+ _: ItemType.Type, context: LayerAnimationContext)
266
+ -> (item: ItemType, context: LayerAnimationContext)?
267
+ {
268
+ for item in self {
269
+ if let match = item.item as? ItemType {
270
+ return (match, context.for(item))
271
+ }
272
+ }
273
+
274
+ return nil
275
+ }
276
+
277
+ /// The first `ShapeItem` in this array of the given type
278
+ func first<ItemType: ShapeItem>(_: ItemType.Type) -> ItemType? {
279
+ for item in self {
280
+ if let match = item.item as? ItemType {
281
+ return match
282
+ }
283
+ }
284
+
285
+ return nil
286
+ }
287
+ }
288
+
289
+ extension LayerAnimationContext {
290
+ /// An updated `LayerAnimationContext` with the`AnimationKeypath`
291
+ /// that refers to this specific `ShapeItem`.
292
+ func `for`(_ item: ShapeItemLayer.Item) -> LayerAnimationContext {
293
+ var context = self
294
+
295
+ if let group = item.parentGroup {
296
+ context.currentKeypath.keys.append(group.name)
297
+ }
298
+
299
+ context.currentKeypath.keys.append(item.item.name)
300
+ return context
301
+ }
302
+ }