aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.editorconfig19
-rw-r--r--.github/workflows/post_snapshot_comment.yml51
-rw-r--r--.github/workflows/validate.yml87
-rw-r--r--.gitignore7
-rw-r--r--.idea/codeStyles/Project.xml68
-rw-r--r--.idea/inspectionProfiles/Project_Default.xml7
-rw-r--r--.idea/kotlinc.xml6
-rw-r--r--After Effects Samples/Benchmark.aepbin0 -> 130133 bytes
-rw-r--r--After Effects Samples/Multiline.aepbin0 -> 355583 bytes
-rw-r--r--After Effects Samples/PointText.aepbin0 -> 254951 bytes
-rw-r--r--Android.bp27
-rw-r--r--CHANGELOG.md91
-rw-r--r--CHANGELOG_COMPOSE.md18
-rw-r--r--METADATA24
l---------NOTICE1
-rw-r--r--README.md18
-rwxr-xr-xapp-benchmark/build.gradle50
-rw-r--r--app-benchmark/proguard-rules.pro7
-rwxr-xr-xapp-benchmark/src/main/AndroidManifest.xml19
-rw-r--r--app-benchmark/src/main/kotlin/com/airbnb/lottie/benchmark/app/BenchmarkActivity.kt28
-rwxr-xr-xapp-benchmark/src/main/res/mipmap-hdpi/ic_launcher.pngbin0 -> 2963 bytes
-rwxr-xr-xapp-benchmark/src/main/res/mipmap-mdpi/ic_launcher.pngbin0 -> 2060 bytes
-rwxr-xr-xapp-benchmark/src/main/res/mipmap-xhdpi/ic_launcher.pngbin0 -> 4490 bytes
-rwxr-xr-xapp-benchmark/src/main/res/mipmap-xxhdpi/ic_launcher.pngbin0 -> 6387 bytes
-rwxr-xr-xapp-benchmark/src/main/res/mipmap-xxxhdpi/ic_launcher.pngbin0 -> 9128 bytes
-rw-r--r--app-benchmark/src/main/res/raw/benchmark.json1
-rwxr-xr-xapp-benchmark/src/main/res/values/strings.xml3
-rw-r--r--baselineprofile/.gitignore1
-rw-r--r--baselineprofile/build.gradle43
-rw-r--r--baselineprofile/src/main/AndroidManifest.xml1
-rw-r--r--baselineprofile/src/main/java/com/airbnb/lottie/baselineprofile/BaselineProfileGenerator.kt32
-rw-r--r--baselineprofile/src/main/java/com/airbnb/lottie/baselineprofile/StartupBenchmarks.kt69
-rw-r--r--benchmark/build.gradle52
-rw-r--r--benchmark/src/main/AndroidManifest.xml (renamed from sample-compose-benchmark/src/main/AndroidManifest.xml)7
-rw-r--r--benchmark/src/main/java/com/airbnb/lottie/sample/compose/benchmark/LottieBaselineBenchmark.kt (renamed from sample-compose-benchmark/src/main/java/com/airbnb/lottie/sample/compose/benchmark/LottieBaselineBenchmark.kt)9
-rw-r--r--benchmark/src/main/java/com/airbnb/lottie/sample/compose/benchmark/LottieBaselineProfiles.kt40
-rw-r--r--build.gradle70
-rwxr-xr-xdecrypt.sh6
-rwxr-xr-xgcloud_run.sh43
-rwxr-xr-xgcloud_setup.sh18
-rw-r--r--gradle.properties4
-rw-r--r--gradle/libs.versions.toml67
-rw-r--r--gradle/wrapper/gradle-wrapper.properties2
-rw-r--r--images/emerge.pngbin0 -> 69863 bytes
-rwxr-xr-xissue-repro-compose/build.gradle31
-rwxr-xr-xissue-repro-compose/src/main/AndroidManifest.xml7
-rwxr-xr-xissue-repro-compose/src/main/kotlin/com/airbnb/lottie/issues/compose/ComposeIssueReproActivity.kt (renamed from issue-repro-compose/src/main/java/com/airbnb/lottie/issues/compose/ComposeIssueReproActivity.kt)0
-rwxr-xr-xissue-repro/build.gradle13
-rwxr-xr-xissue-repro/src/main/AndroidManifest.xml7
-rwxr-xr-xissue-repro/src/main/kotlin/com/airbnb/lottie/issues/IssueReproActivity.kt (renamed from issue-repro/src/main/java/com/airbnb/lottie/issues/IssueReproActivity.kt)0
-rwxr-xr-xissue-repro/src/main/res/layout/issue_repro_activity.xml2
-rw-r--r--issue-repro/src/main/res/raw/intro.json (renamed from credentials.tar.gz)0
-rw-r--r--lint.xml20
-rw-r--r--lottie-compose/api/lottie-compose.api269
-rw-r--r--lottie-compose/build.gradle55
-rw-r--r--lottie-compose/src/main/AndroidManifest.xml2
-rw-r--r--lottie-compose/src/main/generated/baselineProfiles/baseline-prof.txt176
-rw-r--r--lottie-compose/src/main/java/com/airbnb/lottie/compose/LottieAnimatable.kt76
-rw-r--r--lottie-compose/src/main/java/com/airbnb/lottie/compose/LottieAnimation.kt71
-rw-r--r--lottie-compose/src/main/java/com/airbnb/lottie/compose/LottieAnimationSizeNode.kt107
-rw-r--r--lottie-compose/src/main/java/com/airbnb/lottie/compose/LottieAnimationState.kt4
-rw-r--r--lottie-compose/src/main/java/com/airbnb/lottie/compose/LottieDynamicProperties.kt11
-rw-r--r--lottie-compose/src/main/java/com/airbnb/lottie/compose/LottiePainter.kt132
-rw-r--r--lottie-compose/src/main/java/com/airbnb/lottie/compose/animateLottieCompositionAsState.kt7
-rw-r--r--lottie-compose/src/main/java/com/airbnb/lottie/compose/rememberLottieComposition.kt1
-rw-r--r--lottie-compose/src/test/java/com/airbnb/lottie/compose/LottieAnimatableImplTest.kt92
-rw-r--r--lottie-compose/src/test/java/com/airbnb/lottie/compose/LottieClipSpecTest.kt3
-rw-r--r--lottie/build.gradle63
-rw-r--r--lottie/src/main/AndroidManifest.xml4
-rw-r--r--lottie/src/main/generated/baselineProfiles/baseline-prof.txt (renamed from lottie/src/main/baseline-prof.txt)1044
-rw-r--r--lottie/src/main/java/com/airbnb/lottie/AsyncUpdates.java54
-rw-r--r--lottie/src/main/java/com/airbnb/lottie/FontAssetDelegate.java14
-rw-r--r--lottie/src/main/java/com/airbnb/lottie/L.java90
-rw-r--r--lottie/src/main/java/com/airbnb/lottie/Lottie.java5
-rw-r--r--lottie/src/main/java/com/airbnb/lottie/LottieAnimationView.java241
-rw-r--r--lottie/src/main/java/com/airbnb/lottie/LottieComposition.java22
-rw-r--r--lottie/src/main/java/com/airbnb/lottie/LottieCompositionFactory.java332
-rw-r--r--lottie/src/main/java/com/airbnb/lottie/LottieConfig.java50
-rw-r--r--lottie/src/main/java/com/airbnb/lottie/LottieDrawable.java410
-rw-r--r--lottie/src/main/java/com/airbnb/lottie/LottieImageAsset.java13
-rw-r--r--lottie/src/main/java/com/airbnb/lottie/LottieProperty.java12
-rw-r--r--lottie/src/main/java/com/airbnb/lottie/LottieTask.java90
-rw-r--r--lottie/src/main/java/com/airbnb/lottie/LottieTaskIdleListener.java11
-rw-r--r--lottie/src/main/java/com/airbnb/lottie/animation/content/ContentGroup.java13
-rw-r--r--lottie/src/main/java/com/airbnb/lottie/animation/content/FillContent.java4
-rw-r--r--lottie/src/main/java/com/airbnb/lottie/animation/content/GradientFillContent.java5
-rw-r--r--lottie/src/main/java/com/airbnb/lottie/animation/content/PolystarContent.java28
-rw-r--r--lottie/src/main/java/com/airbnb/lottie/animation/content/RepeaterContent.java6
-rw-r--r--lottie/src/main/java/com/airbnb/lottie/animation/content/RoundedCornersContent.java8
-rw-r--r--lottie/src/main/java/com/airbnb/lottie/animation/content/ShapeContent.java21
-rw-r--r--lottie/src/main/java/com/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation.java19
-rw-r--r--lottie/src/main/java/com/airbnb/lottie/animation/keyframe/ColorKeyframeAnimation.java5
-rw-r--r--lottie/src/main/java/com/airbnb/lottie/animation/keyframe/GradientColorKeyframeAnimation.java14
-rw-r--r--lottie/src/main/java/com/airbnb/lottie/animation/keyframe/PathKeyframeAnimation.java15
-rw-r--r--lottie/src/main/java/com/airbnb/lottie/animation/keyframe/ShapeKeyframeAnimation.java18
-rw-r--r--lottie/src/main/java/com/airbnb/lottie/animation/keyframe/TextKeyframeAnimation.java4
-rw-r--r--lottie/src/main/java/com/airbnb/lottie/animation/keyframe/TransformKeyframeAnimation.java46
-rw-r--r--lottie/src/main/java/com/airbnb/lottie/manager/FontAssetManager.java28
-rw-r--r--lottie/src/main/java/com/airbnb/lottie/manager/ImageAssetManager.java32
-rw-r--r--lottie/src/main/java/com/airbnb/lottie/model/DocumentData.java20
-rw-r--r--lottie/src/main/java/com/airbnb/lottie/model/FontCharacter.java3
-rw-r--r--lottie/src/main/java/com/airbnb/lottie/model/animatable/AnimatableGradientColorValue.java42
-rw-r--r--lottie/src/main/java/com/airbnb/lottie/model/animatable/AnimatableTransform.java16
-rw-r--r--lottie/src/main/java/com/airbnb/lottie/model/content/CircleShape.java3
-rw-r--r--lottie/src/main/java/com/airbnb/lottie/model/content/ContentModel.java3
-rw-r--r--lottie/src/main/java/com/airbnb/lottie/model/content/GradientColor.java79
-rw-r--r--lottie/src/main/java/com/airbnb/lottie/model/content/GradientFill.java5
-rw-r--r--lottie/src/main/java/com/airbnb/lottie/model/content/GradientStroke.java3
-rw-r--r--lottie/src/main/java/com/airbnb/lottie/model/content/LBlendMode.java68
-rw-r--r--lottie/src/main/java/com/airbnb/lottie/model/content/MergePaths.java3
-rw-r--r--lottie/src/main/java/com/airbnb/lottie/model/content/PolystarShape.java3
-rw-r--r--lottie/src/main/java/com/airbnb/lottie/model/content/RectangleShape.java3
-rw-r--r--lottie/src/main/java/com/airbnb/lottie/model/content/Repeater.java3
-rw-r--r--lottie/src/main/java/com/airbnb/lottie/model/content/RoundedCorners.java3
-rw-r--r--lottie/src/main/java/com/airbnb/lottie/model/content/ShapeFill.java3
-rw-r--r--lottie/src/main/java/com/airbnb/lottie/model/content/ShapeGroup.java5
-rw-r--r--lottie/src/main/java/com/airbnb/lottie/model/content/ShapePath.java3
-rw-r--r--lottie/src/main/java/com/airbnb/lottie/model/content/ShapeStroke.java3
-rw-r--r--lottie/src/main/java/com/airbnb/lottie/model/content/ShapeTrimPath.java3
-rw-r--r--lottie/src/main/java/com/airbnb/lottie/model/layer/BaseLayer.java34
-rw-r--r--lottie/src/main/java/com/airbnb/lottie/model/layer/CompositionLayer.java9
-rw-r--r--lottie/src/main/java/com/airbnb/lottie/model/layer/Layer.java25
-rw-r--r--lottie/src/main/java/com/airbnb/lottie/model/layer/ShapeLayer.java5
-rw-r--r--lottie/src/main/java/com/airbnb/lottie/model/layer/SolidLayer.java17
-rw-r--r--lottie/src/main/java/com/airbnb/lottie/model/layer/TextLayer.java277
-rw-r--r--lottie/src/main/java/com/airbnb/lottie/network/FileExtension.java3
-rw-r--r--lottie/src/main/java/com/airbnb/lottie/network/NetworkCache.java48
-rw-r--r--lottie/src/main/java/com/airbnb/lottie/network/NetworkFetcher.java69
-rw-r--r--lottie/src/main/java/com/airbnb/lottie/parser/AnimatableValueParser.java5
-rw-r--r--lottie/src/main/java/com/airbnb/lottie/parser/DocumentDataParser.java20
-rw-r--r--lottie/src/main/java/com/airbnb/lottie/parser/GradientColorParser.java24
-rw-r--r--lottie/src/main/java/com/airbnb/lottie/parser/KeyframeParser.java20
-rw-r--r--lottie/src/main/java/com/airbnb/lottie/parser/LayerParser.java62
-rw-r--r--lottie/src/main/java/com/airbnb/lottie/parser/LottieCompositionMoshiParser.java2
-rw-r--r--lottie/src/main/java/com/airbnb/lottie/parser/ShapeStrokeParser.java4
-rw-r--r--lottie/src/main/java/com/airbnb/lottie/utils/BaseLottieAnimator.java26
-rw-r--r--lottie/src/main/java/com/airbnb/lottie/utils/GammaEvaluator.java9
-rw-r--r--lottie/src/main/java/com/airbnb/lottie/utils/LottieThreadFactory.java27
-rw-r--r--lottie/src/main/java/com/airbnb/lottie/utils/LottieTrace.java42
-rw-r--r--lottie/src/main/java/com/airbnb/lottie/utils/LottieValueAnimator.java36
-rw-r--r--lottie/src/main/java/com/airbnb/lottie/value/Keyframe.java15
-rw-r--r--lottie/src/main/java/com/airbnb/lottie/value/LottieValueCallback.java2
-rw-r--r--lottie/src/main/res/values/attrs.xml11
-rw-r--r--lottie/src/test/java/com/airbnb/lottie/LottieCompositionFactoryTest.java15
-rw-r--r--lottie/src/test/java/com/airbnb/lottie/LottieDrawableTest.java8
-rw-r--r--lottie/src/test/java/com/airbnb/lottie/LottieInitializeTest.java128
-rw-r--r--lottie/src/test/java/com/airbnb/lottie/LottieTaskTest.java25
-rw-r--r--lottie/src/test/java/com/airbnb/lottie/LottieValueAnimatorUnitTest.java8
-rw-r--r--lottie/src/test/java/com/airbnb/lottie/model/animatable/AnimatableGradientColorValueTest.java22
-rw-r--r--lottie/src/test/java/com/airbnb/lottie/model/content/GradientColorTest.java46
-rw-r--r--lottie/src/test/java/com/airbnb/lottie/utils/LottieValueAnimatorTest.java47
-rw-r--r--lottie/src/test/resources/test1.json116
-rw-r--r--sample-compose-benchmark/build.gradle48
-rw-r--r--sample-compose-benchmark/src/main/java/com/airbnb/lottie/sample/compose/benchmark/LottieBaselineProfiles.kt98
-rw-r--r--sample-compose/build.gradle73
-rw-r--r--sample-compose/src/androidTest/java/com/airbnb/lottie/samples/InfiniteAnimationTest.kt5
-rw-r--r--sample-compose/src/androidTest/java/com/airbnb/lottie/samples/WalkthroughAnimationTest.kt8
-rw-r--r--sample-compose/src/main/AndroidManifest.xml14
-rw-r--r--sample-compose/src/main/java/com/airbnb/lottie/sample/compose/ComposeActivity.kt2
-rw-r--r--sample-compose/src/main/java/com/airbnb/lottie/sample/compose/composables/AnimationRow.kt4
-rw-r--r--sample-compose/src/main/java/com/airbnb/lottie/sample/compose/examples/AnimatableExamplesPage.kt20
-rw-r--r--sample-compose/src/main/java/com/airbnb/lottie/sample/compose/examples/DynamicPropertiesExamplesPage.kt4
-rw-r--r--sample-compose/src/main/java/com/airbnb/lottie/sample/compose/examples/ImagesExamplesPage.kt2
-rw-r--r--sample-compose/src/main/java/com/airbnb/lottie/sample/compose/examples/ViewPagerExample.kt44
-rw-r--r--sample-compose/src/main/java/com/airbnb/lottie/sample/compose/player/PlayerPage.kt21
-rw-r--r--sample-compose/src/main/java/com/airbnb/lottie/sample/compose/showcase/ShowcasePage.kt3
-rw-r--r--sample/build.gradle87
-rw-r--r--sample/lint-baseline.xml1
-rw-r--r--sample/src/androidTest/java/com/airbnb/lottie/samples/FragmentVisibilityTests.kt45
-rw-r--r--sample/src/androidTest/java/com/airbnb/lottie/samples/LottieIdlingResource.kt20
-rw-r--r--sample/src/main/AndroidManifest.xml29
-rwxr-xr-xsample/src/main/assets/fonts/Comic Neue.ttfbin0 -> 82012 bytes
-rw-r--r--sample/src/main/kotlin/com/airbnb/lottie/samples/LottieApplication.kt9
-rw-r--r--sample/src/main/kotlin/com/airbnb/lottie/samples/LottiefilesFragment.kt161
-rw-r--r--sample/src/main/kotlin/com/airbnb/lottie/samples/LottiefilesMode.kt7
-rw-r--r--sample/src/main/kotlin/com/airbnb/lottie/samples/MainActivity.kt11
-rw-r--r--sample/src/main/kotlin/com/airbnb/lottie/samples/OnPageChangeListenerAdapter.kt18
-rw-r--r--sample/src/main/kotlin/com/airbnb/lottie/samples/PlayerActivity.kt5
-rw-r--r--sample/src/main/kotlin/com/airbnb/lottie/samples/PlayerFragment.kt87
-rw-r--r--sample/src/main/kotlin/com/airbnb/lottie/samples/PlayerViewModel.kt16
-rw-r--r--sample/src/main/kotlin/com/airbnb/lottie/samples/PreviewFragment.kt2
-rw-r--r--sample/src/main/kotlin/com/airbnb/lottie/samples/ShowcaseFragment.kt90
-rw-r--r--sample/src/main/kotlin/com/airbnb/lottie/samples/api/LottiefilesApi.kt21
-rw-r--r--sample/src/main/kotlin/com/airbnb/lottie/samples/model/AnimationData.kt18
-rw-r--r--sample/src/main/kotlin/com/airbnb/lottie/samples/model/AnimationDataV2.kt17
-rw-r--r--sample/src/main/kotlin/com/airbnb/lottie/samples/model/AnimationResponse.kt20
-rw-r--r--sample/src/main/kotlin/com/airbnb/lottie/samples/model/AnimationResponseV2.kt5
-rw-r--r--sample/src/main/kotlin/com/airbnb/lottie/samples/model/CompositionArgs.kt7
-rw-r--r--sample/src/main/kotlin/com/airbnb/lottie/samples/model/ShowcaseItem.kt11
-rw-r--r--sample/src/main/kotlin/com/airbnb/lottie/samples/model/UserInfo.kt19
-rw-r--r--sample/src/main/kotlin/com/airbnb/lottie/samples/utils/BaseEpoxyFragment.kt8
-rw-r--r--sample/src/main/kotlin/com/airbnb/lottie/samples/utils/BaseFragment.kt7
-rw-r--r--sample/src/main/kotlin/com/airbnb/lottie/samples/utils/FragmentViewBindingDelegate.kt10
-rw-r--r--sample/src/main/kotlin/com/airbnb/lottie/samples/utils/MvRxViewModel.kt28
-rw-r--r--sample/src/main/kotlin/com/airbnb/lottie/samples/utils/TypeExtensions.kt22
-rw-r--r--sample/src/main/kotlin/com/airbnb/lottie/samples/views/AnimationItemView.kt47
-rw-r--r--sample/src/main/kotlin/com/airbnb/lottie/samples/views/InterceptingFrameLayout.kt2
-rw-r--r--sample/src/main/kotlin/com/airbnb/lottie/samples/views/LoadingView.kt20
-rw-r--r--sample/src/main/kotlin/com/airbnb/lottie/samples/views/LottiefilesTabBar.kt41
-rw-r--r--sample/src/main/kotlin/com/airbnb/lottie/samples/views/SearchInputItemView.kt27
-rw-r--r--sample/src/main/kotlin/com/airbnb/lottie/samples/views/SectionHeaderView.kt29
-rw-r--r--sample/src/main/kotlin/com/airbnb/lottie/samples/views/ShowcaseCarousel.kt43
-rw-r--r--sample/src/main/kotlin/com/airbnb/lottie/samples/views/ShowcaseDemoItemView.kt28
-rw-r--r--sample/src/main/res/drawable-nodpi/get_it_on_play.pngbin0 -> 14165 bytes
-rw-r--r--sample/src/main/res/drawable-nodpi/lottiefiles_logo.pngbin0 -> 1931 bytes
-rw-r--r--sample/src/main/res/drawable-nodpi/lottiefiles_screen.pngbin0 -> 41371 bytes
-rw-r--r--sample/src/main/res/drawable/ic_device.xml2
-rw-r--r--sample/src/main/res/drawable/ic_learn.xml8
-rw-r--r--sample/src/main/res/drawable/ic_lottiefiles.xml27
-rw-r--r--sample/src/main/res/layout/choose_asset_fragment.xml11
-rw-r--r--sample/src/main/res/layout/control_bar_player_controls.xml30
-rw-r--r--sample/src/main/res/layout/empty_fragment.xml4
-rw-r--r--sample/src/main/res/layout/font_fragment.xml41
-rw-r--r--sample/src/main/res/layout/item_view_search_input.xml18
-rw-r--r--sample/src/main/res/layout/item_view_showcase_animation.xml42
-rw-r--r--sample/src/main/res/layout/item_view_showcase_demo.xml33
-rw-r--r--sample/src/main/res/layout/list_item_loader.xml17
-rw-r--r--sample/src/main/res/layout/lottiefiles_fragment.xml81
-rw-r--r--sample/src/main/res/layout/lottiefiles_tab_bar.xml33
-rw-r--r--sample/src/main/res/layout/main_activity.xml13
-rw-r--r--sample/src/main/res/layout/player_fragment.xml3
-rw-r--r--sample/src/main/res/layout/section_header_view.xml17
-rw-r--r--sample/src/main/res/layout/view_holder_grid_item.xml24
-rw-r--r--sample/src/main/res/layout/view_holder_letter.xml4
-rw-r--r--sample/src/main/res/layout/view_holder_warning.xml21
-rw-r--r--sample/src/main/res/layout/warnings_fragment.xml45
-rw-r--r--sample/src/main/res/menu/bottom_bar.xml13
-rw-r--r--sample/src/main/res/menu/fragment_animation.xml24
-rw-r--r--sample/src/main/res/raw/material_wave_loading.json1
-rw-r--r--sample/src/main/res/values/strings.xml9
-rw-r--r--secrets.tar.encbin10256 -> 0 bytes
-rw-r--r--settings.gradle30
-rw-r--r--snapshot-tests/build.gradle70
-rw-r--r--snapshot-tests/src/androidTest/java/com/airbnb/lottie/snapshots/LottieSnapshotTest.kt45
-rw-r--r--snapshot-tests/src/androidTest/java/com/airbnb/lottie/snapshots/SnapshotTestCaseContext.kt17
-rw-r--r--snapshot-tests/src/androidTest/java/com/airbnb/lottie/snapshots/tests/AssetsTestCase.kt4
-rw-r--r--snapshot-tests/src/androidTest/java/com/airbnb/lottie/snapshots/tests/ClipTextToBoundingBoxTestCase.kt39
-rw-r--r--snapshot-tests/src/androidTest/java/com/airbnb/lottie/snapshots/tests/ComposeDynamicPropertiesTestCase.kt28
-rw-r--r--snapshot-tests/src/androidTest/java/com/airbnb/lottie/snapshots/tests/ComposeScaleTypesTestCase.kt83
-rw-r--r--snapshot-tests/src/androidTest/java/com/airbnb/lottie/snapshots/tests/CompositionFrameRate.kt69
-rw-r--r--snapshot-tests/src/androidTest/java/com/airbnb/lottie/snapshots/tests/DisabledAnimationsTestCase.kt25
-rw-r--r--snapshot-tests/src/androidTest/java/com/airbnb/lottie/snapshots/tests/DynamicPropertiesTestCase.kt149
-rw-r--r--snapshot-tests/src/androidTest/java/com/airbnb/lottie/snapshots/tests/PainterTestCase.kt56
-rw-r--r--snapshot-tests/src/androidTest/java/com/airbnb/lottie/snapshots/tests/PolygonStrokeTestCase.kt13
-rw-r--r--snapshot-tests/src/androidTest/java/com/airbnb/lottie/snapshots/tests/ProdAnimationsTestCase.kt13
-rw-r--r--snapshot-tests/src/androidTest/java/com/airbnb/lottie/snapshots/tests/SeekBarTestCase.kt34
-rw-r--r--snapshot-tests/src/androidTest/java/com/airbnb/lottie/snapshots/tests/TextTestCase.kt10
-rw-r--r--snapshot-tests/src/androidTest/java/com/airbnb/lottie/snapshots/utils/HappoSnapshotter.kt35
-rw-r--r--snapshot-tests/src/main/AndroidManifest.xml6
-rw-r--r--snapshot-tests/src/main/assets/Tests/AutoOrient.json1
-rw-r--r--snapshot-tests/src/main/assets/Tests/BounceEasings.json898
-rw-r--r--snapshot-tests/src/main/assets/Tests/BoxPosition.json1
-rw-r--r--snapshot-tests/src/main/assets/Tests/DefaultLineJoinCap.json5855
-rw-r--r--snapshot-tests/src/main/assets/Tests/Dynamic1.json1
-rw-r--r--snapshot-tests/src/main/assets/Tests/Dynamic2.json1
-rw-r--r--snapshot-tests/src/main/assets/Tests/EmbeddedFont.zipbin0 -> 35352 bytes
-rw-r--r--snapshot-tests/src/main/assets/Tests/Framerate.json1
-rw-r--r--snapshot-tests/src/main/assets/Tests/GradientColorInterpolation.json441
-rw-r--r--snapshot-tests/src/main/assets/Tests/GradientColorKeyframeAnimation.zipbin0 -> 1514 bytes
-rw-r--r--snapshot-tests/src/main/assets/Tests/GradientWithVaryingOpacityStops.json264
-rwxr-xr-xsnapshot-tests/src/main/assets/Tests/InterpolateBetweenOpacityStops.json1038
-rw-r--r--snapshot-tests/src/main/assets/Tests/LargeSquare.json222
-rw-r--r--snapshot-tests/src/main/assets/Tests/LayerBlend_0.json1
-rw-r--r--snapshot-tests/src/main/assets/Tests/LayerBlend_1.json1
-rw-r--r--snapshot-tests/src/main/assets/Tests/LayerBlend_10.json1
-rw-r--r--snapshot-tests/src/main/assets/Tests/LayerBlend_11.json1
-rw-r--r--snapshot-tests/src/main/assets/Tests/LayerBlend_12.json1
-rw-r--r--snapshot-tests/src/main/assets/Tests/LayerBlend_13.json1
-rw-r--r--snapshot-tests/src/main/assets/Tests/LayerBlend_14.json1
-rw-r--r--snapshot-tests/src/main/assets/Tests/LayerBlend_15.json1
-rw-r--r--snapshot-tests/src/main/assets/Tests/LayerBlend_16.json1
-rw-r--r--snapshot-tests/src/main/assets/Tests/LayerBlend_17.json1
-rw-r--r--snapshot-tests/src/main/assets/Tests/LayerBlend_2.json1
-rw-r--r--snapshot-tests/src/main/assets/Tests/LayerBlend_3.json1
-rw-r--r--snapshot-tests/src/main/assets/Tests/LayerBlend_4.json1
-rw-r--r--snapshot-tests/src/main/assets/Tests/LayerBlend_5.json1
-rw-r--r--snapshot-tests/src/main/assets/Tests/LayerBlend_6.json1
-rw-r--r--snapshot-tests/src/main/assets/Tests/LayerBlend_7.json1
-rw-r--r--snapshot-tests/src/main/assets/Tests/LayerBlend_8.json1
-rw-r--r--snapshot-tests/src/main/assets/Tests/LayerBlend_9.json1
-rw-r--r--snapshot-tests/src/main/assets/Tests/Multiline.json1
-rw-r--r--snapshot-tests/src/main/assets/Tests/NullEndShape.json1
-rw-r--r--snapshot-tests/src/main/assets/Tests/Polygon.json1
-rw-r--r--snapshot-tests/src/main/assets/Tests/PrecompBlurDecimapPrecompSize.json1
-rw-r--r--snapshot-tests/src/main/assets/Tests/ReducedMotion.json1
-rw-r--r--snapshot-tests/src/main/assets/Tests/RoundedNonClosed.json1
-rw-r--r--snapshot-tests/src/main/assets/Tests/RoundedWithAlreadyRoundedCorners.json386
-rw-r--r--snapshot-tests/src/main/assets/Tests/SzFont.json1
-rw-r--r--snapshot-tests/src/main/assets/Tests/SzGlyph.json1
-rw-r--r--snapshot-tests/src/main/assets/Tests/TextWithParentAlpha.json1
-rw-r--r--snapshot-tests/src/main/assets/Tests/TextWithPsCenter.json2039
-rw-r--r--snapshot-tests/src/main/assets/Tests/TextWithPsLeft.json1626
-rw-r--r--snapshot-tests/src/main/assets/Tests/Thumb.json1
-rw-r--r--snapshot-tests/src/main/assets/Tests/TriangleLargeStroke.json1
-rw-r--r--snapshot-tests/src/main/assets/Tests/ZipInlineImage.zipbin0 -> 154500 bytes
-rw-r--r--snapshot-tests/src/main/assets/Tests/text-offset-example.json1
-rw-r--r--snapshot-tests/src/main/assets/Tests/winners.tgsbin0 -> 14103 bytes
-rw-r--r--snapshot-tests/src/main/java/com/airbnb/lottie/snapshots/FilmStripView.kt10
-rw-r--r--snapshot-tests/src/main/res/layout/seek_bar.xml12
-rwxr-xr-xupdate-baseline-profiles.sh16
-rwxr-xr-xupload_release.sh2
-rw-r--r--versions.properties387
302 files changed, 19136 insertions, 2990 deletions
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 00000000..3fdcc186
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,19 @@
+root = true
+
+[*]
+insert_final_newline = true
+
+[*.{kt,kts}]
+max_line_length = 140
+indent_size = 4
+ij_kotlin_allow_trailing_comma = true
+ij_kotlin_allow_trailing_comma_on_call_site = true
+ij_kotlin_name_count_to_use_star_import = 2147483647
+ij_kotlin_name_count_to_use_star_import_for_members = 2147483647
+
+[*.java]
+ij_java_class_count_to_use_import_on_demand = 2147483647
+ij_java_names_count_to_use_import_on_demand = 2147483647
+ij_java_packages_to_use_import_on_demand = 2147483647
+ij_java_blank_lines_after_class_header = 1
+ij_java_imports_layout = *,|,javax.**,java.**,|,$*
diff --git a/.github/workflows/post_snapshot_comment.yml b/.github/workflows/post_snapshot_comment.yml
new file mode 100644
index 00000000..2d86c150
--- /dev/null
+++ b/.github/workflows/post_snapshot_comment.yml
@@ -0,0 +1,51 @@
+name: Post PR comment
+
+on:
+ workflow_run:
+ workflows: [Validate]
+ types:
+ - completed
+
+jobs:
+ post-comment:
+ runs-on: ubuntu-latest
+ if: github.event.workflow_run.event == 'pull_request'
+ steps:
+ - uses: haya14busa/action-workflow_run-status@v1
+ - name: 'Download artifact'
+ uses: actions/github-script@v6
+ with:
+ script: |
+ let allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ run_id: context.payload.workflow_run.id,
+ });
+ let matchArtifact = allArtifacts.data.artifacts.filter((artifact) => {
+ return artifact.name == "env"
+ })[0];
+ let download = await github.rest.actions.downloadArtifact({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ artifact_id: matchArtifact.id,
+ archive_format: 'zip',
+ });
+ let fs = require('fs');
+ fs.writeFileSync(`${process.env.GITHUB_WORKSPACE}/env.zip`, Buffer.from(download.data));
+ - name: 'Unzip artifact'
+ run: |
+ unzip env.zip
+ ls -ltR
+ while read line; do
+ echo "$line" >> $GITHUB_ENV
+ done < env
+ - name: 'Post PR comment'
+ uses: mshick/add-pr-comment@v2
+ if: github.event.workflow_run.event == 'pull_request'
+ with:
+ issue: ${{ env.PR_NUMBER }}
+ message-id: ${{ env.GITHUB_SHA }}
+ message: |
+ **Snapshot Tests**
+ **API 23**: [Report](https://happo.io/a/27/report/${{ env.GITHUB_SHA }}-android23) [Diff](https://happo.io/a/27/p/27/compare/master-android23/${{ env.GITHUB_SHA }}-android23)
+ **API 31**: [Report](https://happo.io/a/27/report/${{ env.GITHUB_SHA }}-android31) [Diff](https://happo.io/a/27/p/27/compare/master-android31/${{ env.GITHUB_SHA }}-android31) \ No newline at end of file
diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml
index 07db2573..5f6a0a7b 100644
--- a/.github/workflows/validate.yml
+++ b/.github/workflows/validate.yml
@@ -10,20 +10,21 @@ jobs:
gradle-wrapper:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v3
- uses: gradle/wrapper-validation-action@v1
lint:
runs-on: ubuntu-latest
steps:
- name: Checkout the code
- uses: actions/checkout@v2
+ uses: actions/checkout@v3
- name: Setup JDK
uses: actions/setup-java@v2
with:
- distribution: 'zulu'
- java-version: 11
+ distribution: "zulu"
+ java-version: 17
+ - uses: gradle/gradle-build-action@v2
- name: Run Lint
- run: ./gradlew lintDebug
+ run: ./gradlew lintDebug --no-daemon
- name: Zip reports
if: always()
run: zip -r reports.zip . -i '**/reports/*.xml' '**/reports/*.html'
@@ -37,14 +38,15 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout the code
- uses: actions/checkout@v2
+ uses: actions/checkout@v3
- name: Setup JDK
uses: actions/setup-java@v2
with:
- distribution: 'zulu'
- java-version: 11
+ distribution: "zulu"
+ java-version: 17
+ - uses: gradle/gradle-build-action@v2
- name: Run Unit Tests
- run: ./gradlew testDebugUnitTest
+ run: ./gradlew testDebugUnitTest --no-daemon
- name: Zip reports
if: always()
run: zip -r reports.zip . -i '**/reports/*.xml' '**/reports/*.html'
@@ -54,21 +56,78 @@ jobs:
with:
name: unit_test_reports
path: reports.zip
+ api-compatibility:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout the code
+ uses: actions/checkout@v3
+ - name: Setup JDK
+ uses: actions/setup-java@v2
+ with:
+ distribution: "zulu"
+ java-version: 17
+ - uses: gradle/gradle-build-action@v2
+ - name: Run API compatibility check
+ run: ./gradlew apiCheck --no-daemon
+
+ snapshot-tests:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout the code
+ uses: actions/checkout@v3
+ - name: Setup env
+ shell: bash
+ run: |
+ curl https://us-central1-lottie-snapshots.cloudfunctions.net/snapshot-env-v1/emulator > snapshot-env
+ while read line; do
+ echo "$line" >> $GITHUB_ENV
+ done < snapshot-env
+ - name: Setup JDK
+ uses: actions/setup-java@v2
+ with:
+ distribution: "zulu"
+ java-version: 17
+ - uses: gradle/gradle-build-action@v2
+ - name: Build app
+ run: ./gradlew snapshot-tests:assembleDebug snapshot-tests:assembleDebugAndroidTest --no-daemon
+ - name: Run tests
+ uses: emulator-wtf/run-tests@v0.9.6
+ with:
+ api-token: ${{ env.EW_API_TOKEN }}
+ version: 0.9.19
+ app: snapshot-tests/build/outputs/apk/debug/snapshot-tests-debug.apk
+ test: snapshot-tests/build/outputs/apk/androidTest/debug/snapshot-tests-debug-androidTest.apk
+ devices: |
+ model=Pixel2,version=23,gpu=auto
+ model=Pixel2,version=31,gpu=auto
+ outputs-dir: build/test-results
+ - name: Save PR number
+ if: github.event_name == 'pull_request'
+ env:
+ PR_NUMBER: ${{ github.event.number }}
+ run: |
+ mkdir -p ./env
+ echo PR_NUMBER=$PR_NUMBER > ./env/env
+ echo GITHUB_SHA=${{ github.sha }} >> ./env/env
+ - uses: actions/upload-artifact@v3
+ with:
+ name: env
+ path: env/
deploy:
if: github.event_name == 'push' && github.repository == 'airbnb/lottie-android' && github.ref == 'refs/heads/master'
runs-on: ubuntu-latest
- needs: [lint, unit-test]
+ needs: [lint, unit-test, gradle-wrapper, snapshot-tests]
steps:
- name: Checkout the code
- uses: actions/checkout@v2
+ uses: actions/checkout@v3
- name: Setup JDK
uses: actions/setup-java@v2
with:
- distribution: 'zulu'
- java-version: 11
+ distribution: "zulu"
+ java-version: 17
+ - uses: gradle/gradle-build-action@v2
- name: "Deploy Snapshot"
env:
SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }}
SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }}
run: ./deploy_snapshot.sh
-
diff --git a/.gitignore b/.gitignore
index 1df66c6a..0dbd2e37 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,11 +5,13 @@
# From https://github.com/github/gitignore/blob/master/Global/JetBrains.gitignore
# User-specific stuff
+.idea/kotlinc.xml
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf
+.idea/runConfigurations
# Generated files
.idea/**/contentModel.xml
@@ -30,6 +32,10 @@
.idea/modules.xml
.idea/codeStyleSettings.xml
.idea/compiler.xml
+.idea/androidTestResultsUserPreferences.xml
+.idea/appInsightsSettings.xml
+.idea/migrations.xml
+.idea/deploymentTargetSelector.xml
# Gradle
.idea/**/gradle.xml
@@ -43,3 +49,4 @@ build
*auto-save*
credentials/*
secring.gpg
+snapshot-env
diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
index cfaf1ffa..0e53a141 100644
--- a/.idea/codeStyles/Project.xml
+++ b/.idea/codeStyles/Project.xml
@@ -5,10 +5,6 @@
<option name="ANNOTATION_PARAMETER_WRAP" value="1" />
</JavaCodeStyleSettings>
<JetCodeStyleSettings>
- <option name="PACKAGES_TO_USE_STAR_IMPORTS">
- <value />
- </option>
- <option name="NAME_COUNT_TO_USE_STAR_IMPORT" value="2147483647" />
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</JetCodeStyleSettings>
<codeStyleSettings language="Groovy">
@@ -71,6 +67,7 @@
<match>
<AND>
<NAME>xmlns:android</NAME>
+ <XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
@@ -81,6 +78,7 @@
<match>
<AND>
<NAME>xmlns:.*</NAME>
+ <XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
@@ -92,6 +90,7 @@
<match>
<AND>
<NAME>.*:id</NAME>
+ <XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
@@ -102,6 +101,7 @@
<match>
<AND>
<NAME>.*:name</NAME>
+ <XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
@@ -112,6 +112,7 @@
<match>
<AND>
<NAME>name</NAME>
+ <XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
@@ -122,6 +123,7 @@
<match>
<AND>
<NAME>style</NAME>
+ <XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
@@ -132,6 +134,7 @@
<match>
<AND>
<NAME>.*</NAME>
+ <XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
@@ -142,64 +145,12 @@
<rule>
<match>
<AND>
- <NAME>.*:layout_width</NAME>
- <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
- </AND>
- </match>
- </rule>
- </section>
- <section>
- <rule>
- <match>
- <AND>
- <NAME>.*:layout_height</NAME>
- <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
- </AND>
- </match>
- </rule>
- </section>
- <section>
- <rule>
- <match>
- <AND>
- <NAME>.*:layout_.*</NAME>
- <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
- </AND>
- </match>
- <order>BY_NAME</order>
- </rule>
- </section>
- <section>
- <rule>
- <match>
- <AND>
- <NAME>.*:width</NAME>
- <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
- </AND>
- </match>
- <order>BY_NAME</order>
- </rule>
- </section>
- <section>
- <rule>
- <match>
- <AND>
- <NAME>.*:height</NAME>
- <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
- </AND>
- </match>
- <order>BY_NAME</order>
- </rule>
- </section>
- <section>
- <rule>
- <match>
- <AND>
<NAME>.*</NAME>
+ <XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
- <order>BY_NAME</order>
+ <order>ANDROID_ATTRIBUTE_ORDER</order>
</rule>
</section>
<section>
@@ -207,6 +158,7 @@
<match>
<AND>
<NAME>.*</NAME>
+ <XML_ATTRIBUTE />
<XML_NAMESPACE>.*</XML_NAMESPACE>
</AND>
</match>
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
index 601ecd99..9b5f4f6a 100644
--- a/.idea/inspectionProfiles/Project_Default.xml
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -12,12 +12,17 @@
<option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool>
+ <inspection_tool class="PreviewApiLevelMustBeValid" enabled="true" level="ERROR" enabled_by_default="true">
+ <option name="composableFile" value="true" />
+ <option name="previewFile" value="true" />
+ </inspection_tool>
<inspection_tool class="PreviewDimensionRespectsLimit" enabled="true" level="WARNING" enabled_by_default="true">
<option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewFontScaleMustBeGreaterThanZero" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
+ <option name="previewFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewMultipleParameterProviders" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
@@ -33,9 +38,11 @@
</inspection_tool>
<inspection_tool class="PreviewNotSupportedInUnitTestFiles" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
+ <option name="previewFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewPickerAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
+ <option name="previewFile" value="true" />
</inspection_tool>
<inspection_tool class="SameParameterValue" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SwitchStatementWithTooFewBranches" enabled="false" level="WARNING" enabled_by_default="false">
diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml
new file mode 100644
index 00000000..8d81632f
--- /dev/null
+++ b/.idea/kotlinc.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="KotlinJpsPluginSettings">
+ <option name="version" value="1.9.22" />
+ </component>
+</project> \ No newline at end of file
diff --git a/After Effects Samples/Benchmark.aep b/After Effects Samples/Benchmark.aep
new file mode 100644
index 00000000..e059a565
--- /dev/null
+++ b/After Effects Samples/Benchmark.aep
Binary files differ
diff --git a/After Effects Samples/Multiline.aep b/After Effects Samples/Multiline.aep
new file mode 100644
index 00000000..b4393ee1
--- /dev/null
+++ b/After Effects Samples/Multiline.aep
Binary files differ
diff --git a/After Effects Samples/PointText.aep b/After Effects Samples/PointText.aep
new file mode 100644
index 00000000..66ca95b2
--- /dev/null
+++ b/After Effects Samples/PointText.aep
Binary files differ
diff --git a/Android.bp b/Android.bp
index a7fe752a..5f5a48e6 100644
--- a/Android.bp
+++ b/Android.bp
@@ -47,6 +47,21 @@ filegroup {
licenses: ["external_lottie_code_of_conduct_license"],
}
+android_manifest_package_attribute = "\"com.airbnb.lottie\""
+
+genrule {
+ name: "AddPackageAttributeToAndroidManifest",
+ srcs: [
+ "lottie/src/main/AndroidManifest.xml"
+ ],
+ out: [
+ "lottie/src/main/AndroidManifestGen.xml",
+ ],
+ cmd: "sed -E 's/<manifest>/<manifest package="
+ + android_manifest_package_attribute
+ + ">/g' $(in) > $(out)",
+}
+
android_library {
name: "lottie",
srcs: [
@@ -61,28 +76,30 @@ android_library {
"androidx.appcompat_appcompat",
"okio-lib",
],
- manifest: "lottie/src/main/AndroidManifest.xml",
+ manifest: ":AddPackageAttributeToAndroidManifest",
sdk_version: "31",
min_sdk_version: "19",
java_version: "1.8",
optimize: {
proguard_flags_files: ["proguard.flags"],
- }
+ },
+ // https://github.com/airbnb/lottie-android/issues/2502
+ errorprone: {
+ enabled: false,
+ },
}
-android_library {
+java_library {
name: "lottie_compose",
srcs: [
"lottie-compose/src/main/java/**/*.kt",
],
- resource_dirs: ["lottie/src/main/res/"],
static_libs: [
"lottie",
"androidx.compose.foundation_foundation",
"androidx.compose.runtime_runtime",
"androidx.compose.ui_ui",
],
- manifest: "lottie-compose/src/main/AndroidManifest.xml",
sdk_version: "current",
min_sdk_version: "21",
kotlincflags: [
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6a449417..3a93b05f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,94 @@
+# 6.4.0
+### New Features
+* Add support for reduced motion marker names ([#2451](https://github.com/airbnb/lottie-android/pull/2451))
+* Support GZIP and TGS network downloads ([#2454](https://github.com/airbnb/lottie-android/pull/2454))
+### Bugs Fixed
+* Allow easings to go <0 and >1 ([#2457](https://github.com/airbnb/lottie-android/pull/2457))
+* Fix a memory leak in LottieTask ([#2465](https://github.com/airbnb/lottie-android/pull/2465))
+* Prevent play from working after a non-Lottie drawable was set and then returned back ([#2468](https://github.com/airbnb/lottie-android/pull/2468))
+* Respect autoPlay in LottieAnimationView when setting a new composition ([#2469](https://github.com/airbnb/lottie-android/pull/2469))
+* Call LottieTask synchronously when already on the main thread ([#2470](https://github.com/airbnb/lottie-android/pull/2470))
+* Properly rescale bitmaps when the system scale changes ([#2475](https://github.com/airbnb/lottie-android/pull/2475))
+
+# 6.3.0
+### New Features
+* Add support dynamic path properties on shape contents ([#2439](https://github.com/airbnb/lottie-android/pull/2439))
+* Add support for gzipped and tgs files ([#2435](https://github.com/airbnb/lottie-android/pull/2435))
+* Add an option to clip text if it extends beyond its bounding box ([#2412](https://github.com/airbnb/lottie-android/pull/2412))
+
+### Bugs Fixed
+* Make all LottieAnimationView setters idempotent ([#2441](https://github.com/airbnb/lottie-android/pull/2441))
+* Fix a rendering artifact for polygons with large strokes ([#2440](https://github.com/airbnb/lottie-android/pull/2440))
+* Re-scale bitmaps if the system scale changes ([#2438](https://github.com/airbnb/lottie-android/pull/2438))
+* Handle null color callbacks in solid layer ([#2434](https://github.com/airbnb/lottie-android/pull/2434))
+* Handle null shape data end values ([#2433](https://github.com/airbnb/lottie-android/pull/2433))
+* Fix gradient colors when the progress is <0 or > 1 ([#2427](https://github.com/airbnb/lottie-android/pull/2427))
+
+# 6.2.0
+### New Features
+* Implement screen, overlay, darken, lighten, and add blend modes ([#2408](https://github.com/airbnb/lottie-android/pull/2408))
+* Implement auto-orient ([#2416](https://github.com/airbnb/lottie-android/pull/2416))
+* Allow globally configuring asyncUpdates ([#2356](https://github.com/airbnb/lottie-android/pull/2356))
+* Add an optional `close` param to LottieCompositionFactory.fromJsonReader ([#2342](https://github.com/airbnb/lottie-android/pull/2342))
+* Allow dynamic properties for solid layer colors ([#2378](https://github.com/airbnb/lottie-android/pull/2378))
+* Update baseline profiles ([#2404](https://github.com/airbnb/lottie-android/pull/2404))
+* Add a ZipInputStream overload to LottieAnimationView.setAnimation ([#2411](https://github.com/airbnb/lottie-android/pull/2411))
+
+### Bugs Fixed
+* Upgrade okio ([#2418](https://github.com/airbnb/lottie-android/pull/2418))
+* Improve cache hits for synchronous LottieCompositionFactory methods ([#2379](https://github.com/airbnb/lottie-android/pull/2379))
+* Fix gradient interpolation for opacity stops beyond the last color stop ([#2377](https://github.com/airbnb/lottie-android/pull/2377))
+* Fix Potential NPE In NetworkCache.clearCache ([#2364](https://github.com/airbnb/lottie-android/pull/2364))
+* Fix an IllegalArgumentException when creating a bitmap ([#2351](https://github.com/airbnb/lottie-android/pull/2351))
+* Fix rounded corners for non-closed paths ([#2405](https://github.com/airbnb/lottie-android/pull/2405))
+* Fix varying opacity stops across keyframes in the same gradient ([#2406](https://github.com/airbnb/lottie-android/pull/2406))
+* Fix a NullPointerException in ColorKeyframeAnimation ([#2407](https://github.com/airbnb/lottie-android/pull/2407))
+
+# 6.1.0
+### New Features
+* New multithreaded `asyncUpdates` feature which moves the entire update phase of an animation off of the main thread. For more information, refer to [this blog post](https://gpeal.medium.com/lottie-android-6-1-lottie-goes-multithreaded-67c09c091fd7). ([#2276](https://github.com/airbnb/lottie-android/pull/2276))
+* Allow `LottieCompositionFactory` to not close input streams ([#2286](https://github.com/airbnb/lottie-android/pull/2286) and [#2319](https://github.com/airbnb/lottie-android/pull/2319))
+* Allow Lottie to be initialized multiple times ([#2323](https://github.com/airbnb/lottie-android/pull/2323))
+* Add an additional null check to TransformKeyframeAnimation ([#2381](https://github.com/airbnb/lottie-android/pull/2381))
+* Fix asyncUpdates for Nougat and below ([#2380](https://github.com/airbnb/lottie-android/pull/2380))
+
+### Bugs Fixed
+* Close input streams for cache hits ([#2253](https://github.com/airbnb/lottie-android/pull/2253))
+* Always use ApplicationContext in ImageAssetManager to ensure it can be reused ([#2289](https://github.com/airbnb/lottie-android/pull/2289))
+* Hold weak references to success/failure listeners ([#2293](https://github.com/airbnb/lottie-android/pull/2293))
+* Add default values for line join and cap types ([#2337](https://github.com/airbnb/lottie-android/pull/2337))
+* Apply layer parent opacity to text ([#2336](https://github.com/airbnb/lottie-android/pull/2336))
+
+# 6.0.1
+### Bugs Fixed
+* Allow loading URLs with a length of greater than 255 chars ([#2311](https://github.com/airbnb/lottie-android/pull/2311))
+
+# 6.0.0
+### New Features
+* Major overhaul of text layout. Text layout should be more consistent across the board ([#2162](https://github.com/airbnb/lottie-android/pull/2162))
+* Allow animations in zip files to contain embedded base64 encoded images ([#2110](https://github.com/airbnb/lottie-android/pull/2110))
+* Allow zip files to contain embedded fonts. Context was added to some LottieCompositionFactory APIs to support this ([#2102](https://github.com/airbnb/lottie-android/pull/2102))
+* Add fontStyle and fontName as parameters in new overloads in FontAssetDelegate ([#2103](https://github.com/airbnb/lottie-android/pull/2103))
+* Allow decimal values for precomp size ([#2138](https://github.com/airbnb/lottie-android/pull/2138))
+* Allow interpolating in between gradients that have different numbers of opacity stops ([#2160](https://github.com/airbnb/lottie-android/pull/2160))
+* Support box position in document data ([#2139](https://github.com/airbnb/lottie-android/pull/2139))
+* Allow repeater contents to be the target of dynamic properties ([#2164](https://github.com/airbnb/lottie-android/pull/2164))
+* Provide a global LottieTask listener to aid in Espresso idle resources ([#2161](https://github.com/airbnb/lottie-android/pull/2161))
+* Allow setting a default font extension ([#2166](https://github.com/airbnb/lottie-android/pull/2166))
+* Add an option to completely disable Lottie's network cache ([#2158](https://github.com/airbnb/lottie-android/pull/2158))
+* Allow setting a font map for custom fonts ([#2180](https://github.com/airbnb/lottie-android/pull/2180))
+* Allow ImageAssetDelegate to be used when a drawable doesn't have a callback ([#2183](https://github.com/airbnb/lottie-android/pull/2072))
+* Make Layer name and refId public ([#2188](https://github.com/airbnb/lottie-android/pull/2188))
+* Allow rendering at the composition frame rate ([#2184](https://github.com/airbnb/lottie-android/pull/2184))
+### Bugs Fixed
+* Fixed an NPE when decoding an invalid bitmap and for transform opacity, and transform anchor position ([#2117](https://github.com/airbnb/lottie-android/pull/2117), [#2179](https://github.com/airbnb/lottie-android/pull/2179), and [#2197](https://github.com/airbnb/lottie-android/pull/2197))
+* Only store application context in ImageAssetManager ([#2163](https://github.com/airbnb/lottie-android/pull/2163))
+* Prevent rounded corner effects from trying to round a shape that has control points on its vertices already ([#2165](https://github.com/airbnb/lottie-android/pull/2165))
+* Pass LottieComposition directly while building layers to avoid race conditions ([#2167](https://github.com/airbnb/lottie-android/pull/2167))
+* Allow progress to be restored from saved state ([#2072](https://github.com/airbnb/lottie-android/pull/2072))
+* Take top and left Drawable bounds into account to support things like SeekBar thumbs ([#2182](https://github.com/airbnb/lottie-android/pull/2182))
+* Use the correct cache key for network animations ([#2198](https://github.com/airbnb/lottie-android/pull/2198))
+
# 5.2.0
### Bugs Fixed
* De-dupe gradient stops. On pre-Oreo devices, if you had color and opacity stops in the same place and used hardware acceleration, you may have seen artifacts at the stop positions as of 5.1.1 [#20814](https://github.com/airbnb/lottie-android/pull/2081)
diff --git a/CHANGELOG_COMPOSE.md b/CHANGELOG_COMPOSE.md
index 22d5cdcd..c63dce2d 100644
--- a/CHANGELOG_COMPOSE.md
+++ b/CHANGELOG_COMPOSE.md
@@ -1,3 +1,21 @@
+# 6.4.0
+### New Features
+* Add safe mode ([#2455](https://github.com/airbnb/lottie-android/pull/2455))
+* Clarify clipToCompositionBounds docs ([#2473](https://github.com/airbnb/lottie-android/pull/2473))
+
+# 6.3.0
+* Add LottiePainter and rememberLottiePainter to use Lottie anywhere a Painter can be used ([#2442](https://github.com/airbnb/lottie-android/pull/2442))
+* Constrain unconstrainted bounds when the other dimension is constrained ([#2437](https://github.com/airbnb/lottie-android/pull/2437))
+
+# 6.1.0
+* Key dynamic properties on composition to ensure that they are set if the composition changes ([#2290](https://github.com/airbnb/lottie-android/pull/2290))
+* Add `@JvmOverloads` to `LottieAnimation` to improve binary compatibility ([#2320](https://github.com/airbnb/lottie-android/pull/2320))
+
+# 6.0.0
+* Add reverseOnRepeat ([#2128](https://github.com/airbnb/lottie-android/pull/2128))
+* Allow setting a font map for custom fonts ([#2180](https://github.com/airbnb/lottie-android/pull/2180))
+* Allow images to be rendered ([#2183](https://github.com/airbnb/lottie-android/pull/2072))
+
# 5.2.0
* [BREAKING CHANGE]
LottieAnimation now takes progress as a `() -> Float` rather than a `Float`. This allows Lottie to redraw without triggering a recomposition every time progress updates. For more information, refer to the Compose [phase docs](https://developer.android.com/jetpack/compose/phases). The existing API will exist as deprecated for one more release but will then be removed. For the vast majority of use cases:
diff --git a/METADATA b/METADATA
index 4771daa8..6027271a 100644
--- a/METADATA
+++ b/METADATA
@@ -1,16 +1,20 @@
+# This project was upgraded with external_updater.
+# Usage: tools/external_updater/updater.sh update external/lottie
+# For more info, check https://cs.android.com/android/platform/superproject/+/main:tools/external_updater/README.md
+
name: "lottie"
description: "Lottie is a mobile library for Android and iOS that parses Adobe After Effects animations exported as json with bodymovin and renders the vector animations natively on mobile and through React Native! See: https://github.com/airbnb/lottie-android"
third_party {
- url {
- type: GIT
- value: "https://github.com/airbnb/lottie-android"
- }
- version: "v5.2.0"
+ license_type: RESTRICTED
+ license_note: "would be NOTICE save for CC-BY-SA in CODE_OF_CONDUCT.md"
last_upgrade_date {
- year: 2022
- month: 8
- day: 19
+ year: 2024
+ month: 5
+ day: 16
+ }
+ identifier {
+ type: "Git"
+ value: "https://github.com/airbnb/lottie-android"
+ version: "v6.4.0"
}
- license_note: "would be NOTICE save for CC-BY-SA in CODE_OF_CONDUCT.md"
- license_type: RESTRICTED
}
diff --git a/NOTICE b/NOTICE
deleted file mode 120000
index 7a694c96..00000000
--- a/NOTICE
+++ /dev/null
@@ -1 +0,0 @@
-LICENSE \ No newline at end of file
diff --git a/README.md b/README.md
index 7aee24a2..6f77f2f1 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# Lottie for Android, [iOS](https://github.com/airbnb/lottie-ios), [React Native](https://github.com/airbnb/lottie-react-native), [Web](https://github.com/airbnb/lottie-web), and [Windows](https://aka.ms/lottie)
-![Build Status](https://github.com/airbnb/lottie-android/workflows/Verify/badge.svg)
+![Build Status](https://github.com/airbnb/lottie-android/workflows/Verify/badge.svg)
<a href='https://play.google.com/store/apps/details?id=com.airbnb.lottie'><img alt='Get it on Google Play' src='https://play.google.com/intl/en_us/badges/images/generic/en_badge_web_generic.png' height="50px"/></a>
@@ -9,43 +9,38 @@ Lottie is a mobile library for Android and iOS that parses [Adobe After Effects]
For the first time, designers can create **and ship** beautiful animations without an engineer painstakingly recreating it by hand. They say a picture is worth 1,000 words so here are 13,000:
# Sponsors
+
Lottie is maintained and improved on nights and weekends. If you use Lottie in your app, please consider sponsoring it to help ensure that we can continue to improve the project we love.
Click the sponsor button above to learn more
<img src="gifs/Sponsor.png" alt="Sponsor Button" width="100"/>
## Lead Sponsors
+
<a href="https://www.lottiefiles.com/"><img src="images/lottiefiles.svg" alt="Lottiefiles" width="300" /></a>
<a href="https://lottielab.io/"><img src="images/lottielab.png" alt="Lottie Lab" width="300" /></a>
<a href="https://www.airbnb.com/"><img src="images/airbnb.svg" alt="Airbnb" width="300" /></a>
-<a href="https://www.tonal.com/"><img src="images/tonal.svg" alt="Tonal" width="300" /></a>
+<a href="https://getstream.io/chat/?utm_source=OpenCollective_Lottie&utm_medium=Github_Repo_Content_Ad&utm_content=Developer&utm_campaign=Lottie_July2022_Chat_klmh22"><img src="images/stream.png" alt="Stream" width="300" /></a>
-<a href="https://getstream.io/chat/sdk/android/?utm_source=OpenCollective&utm_medium=Github_Repo_Content_Ad&utm_content=Developer&utm_campaign=OpenCollective_Jan2022_AndroidChatSDK"><img src="images/stream.png" alt="Stream" width="300" /></a>
+<a href="https://www.emergetools.com/"><img src="images/emerge.png" alt="Emerge Tools" width="300" /></a>
<a href="https://www.coinbase.com/"><img src="images/coinbase.svg" alt="Coinbase" width="300" /></a>
## View documentation, FAQ, help, examples, and more at [airbnb.io/lottie](http://airbnb.io/lottie/)
-
-
![Example1](gifs/Example1.gif)
-
![Example2](gifs/Example2.gif)
-
![Example3](gifs/Example3.gif)
-
![Community](gifs/Community%202_3.gif)
-
![Example4](gifs/Example4.gif)
-
## Download
Gradle is the only supported build configuration, so just add the dependency to your project `build.gradle` file:
@@ -55,6 +50,7 @@ dependencies {
implementation 'com.airbnb.android:lottie:$lottieVersion'
}
```
+
The latest Lottie version is:
![lottieVersion](https://maven-badges.herokuapp.com/maven-central/com.airbnb.android/lottie/badge.svg)
@@ -66,4 +62,4 @@ Lottie 2.8.0 and above only supports projects that have been migrated to [androi
# Contributing
-Because development has started for Lottie Compose, Gradle, and the Android Gradle Plugin will be kept up to date with the latest canaries. This also requires you to use Android Studio Canary builds. [Preview builds](https://developer.android.com/studio/preview) can be installed side by side with stable versions.
+Because development has started for Lottie Compose, Gradle, and the Android Gradle Plugin will be kept up to date with the latest canaries. This also requires you to use Android Studio Canary builds. [Preview builds](https://developer.android.com/studio/preview) can be installed side by side with stable versions.
diff --git a/app-benchmark/build.gradle b/app-benchmark/build.gradle
new file mode 100755
index 00000000..a06f954e
--- /dev/null
+++ b/app-benchmark/build.gradle
@@ -0,0 +1,50 @@
+import static de.fayard.refreshVersions.core.Versions.versionFor
+
+plugins {
+ id 'com.android.application'
+ id "org.jetbrains.kotlin.android"
+ id 'androidx.baselineprofile'
+}
+
+android {
+ namespace 'com.airbnb.lottie.benchmark.app'
+ compileSdk 34
+ defaultConfig {
+ applicationId "com.airbnb.lottie.benchmark.app"
+ minSdk 21
+ targetSdk 34
+ versionCode 1
+ versionName "1.0"
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ signingConfig signingConfigs.debug
+ debuggable false
+ proguardFiles("proguard-rules.pro")
+ }
+ create("benchmark") {
+ initWith(release)
+ signingConfig = signingConfigs.getByName("debug")
+ }
+ }
+ buildFeatures {
+ compose true
+ }
+ composeOptions {
+ kotlinCompilerExtensionVersion = versionFor(project, AndroidX.compose.compiler)
+ }
+}
+
+dependencies {
+ implementation project(':lottie-compose')
+ implementation libs.androidx.appcompat
+ implementation libs.androidx.activity.compose
+ implementation platform(libs.compose.bom)
+ implementation libs.compose.ui
+ implementation libs.compose.material
+ implementation libs.compose.material.icons.extended
+ implementation libs.compose.ui.tooling
+ // Need this to side load a Baseline Profile when Benchmarking
+ implementation libs.profileinstaller
+}
diff --git a/app-benchmark/proguard-rules.pro b/app-benchmark/proguard-rules.pro
new file mode 100644
index 00000000..7ca82548
--- /dev/null
+++ b/app-benchmark/proguard-rules.pro
@@ -0,0 +1,7 @@
+# Proguard rules that are applied to your test apk/code.
+-dontoptimize
+-dontobfuscate
+-dontshrink
+-ignorewarnings
+-dontwarn *
+-dontnote *
diff --git a/app-benchmark/src/main/AndroidManifest.xml b/app-benchmark/src/main/AndroidManifest.xml
new file mode 100755
index 00000000..dcb38f39
--- /dev/null
+++ b/app-benchmark/src/main/AndroidManifest.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <application
+ android:icon="@mipmap/ic_launcher"
+ android:label="@string/app_name"
+ android:theme="@style/Theme.AppCompat.Light.NoActionBar">
+ <profileable android:shell="true" />
+
+ <activity
+ android:name=".BenchmarkActivity"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/app-benchmark/src/main/kotlin/com/airbnb/lottie/benchmark/app/BenchmarkActivity.kt b/app-benchmark/src/main/kotlin/com/airbnb/lottie/benchmark/app/BenchmarkActivity.kt
new file mode 100644
index 00000000..95298854
--- /dev/null
+++ b/app-benchmark/src/main/kotlin/com/airbnb/lottie/benchmark/app/BenchmarkActivity.kt
@@ -0,0 +1,28 @@
+package com.airbnb.lottie.benchmark.app
+
+import android.os.Bundle
+import androidx.activity.compose.setContent
+import androidx.appcompat.app.AppCompatActivity
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import com.airbnb.lottie.compose.LottieAnimation
+import com.airbnb.lottie.compose.LottieCompositionSpec
+import com.airbnb.lottie.compose.LottieConstants
+import com.airbnb.lottie.compose.animateLottieCompositionAsState
+import com.airbnb.lottie.compose.rememberLottieComposition
+
+class BenchmarkActivity : AppCompatActivity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContent {
+ Content()
+ }
+ }
+
+ @Composable
+ fun Content() {
+ val composition by rememberLottieComposition(LottieCompositionSpec.RawRes(R.raw.benchmark))
+ val progress by animateLottieCompositionAsState(composition, iterations = LottieConstants.IterateForever)
+ LottieAnimation(composition, { progress })
+ }
+}
diff --git a/app-benchmark/src/main/res/mipmap-hdpi/ic_launcher.png b/app-benchmark/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100755
index 00000000..898f3ed5
--- /dev/null
+++ b/app-benchmark/src/main/res/mipmap-hdpi/ic_launcher.png
Binary files differ
diff --git a/app-benchmark/src/main/res/mipmap-mdpi/ic_launcher.png b/app-benchmark/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100755
index 00000000..64ba76f7
--- /dev/null
+++ b/app-benchmark/src/main/res/mipmap-mdpi/ic_launcher.png
Binary files differ
diff --git a/app-benchmark/src/main/res/mipmap-xhdpi/ic_launcher.png b/app-benchmark/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100755
index 00000000..e5ed4659
--- /dev/null
+++ b/app-benchmark/src/main/res/mipmap-xhdpi/ic_launcher.png
Binary files differ
diff --git a/app-benchmark/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app-benchmark/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100755
index 00000000..b0907cac
--- /dev/null
+++ b/app-benchmark/src/main/res/mipmap-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/app-benchmark/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app-benchmark/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100755
index 00000000..2c18de9e
--- /dev/null
+++ b/app-benchmark/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Binary files differ
diff --git a/app-benchmark/src/main/res/raw/benchmark.json b/app-benchmark/src/main/res/raw/benchmark.json
new file mode 100644
index 00000000..8ab630b5
--- /dev/null
+++ b/app-benchmark/src/main/res/raw/benchmark.json
@@ -0,0 +1 @@
+{"v":"5.10.2","fr":60,"ip":0,"op":60,"w":400,"h":400,"nm":"Benchmark","ddd":0,"assets":[{"id":"image_0","w":80,"h":80,"u":"","p":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAACXBIWXMAAAABAAAAAQBPJcTWAAAAJHpUWHRDcmVhdG9yAAAImXNMyU9KVXBMK0ktUnBNS0tNLikGAEF6Bs5qehXFAAAF8klEQVR4nO2ca6gUZRjHf8+e3ZnxqKWB2IUQ6oNiH7qIGZUXJKhMU7tKUJEoRdKHLnQhwuiDaPVRKyqV/FKJWWIpGAhSQtgpi1LRJCIqKsGCTOed3ZmnD3sU252dM7s7M3vGMz84LLyX5/3v/8zMe52FgoKCgoKCgoJcIkkFUrAol6dTLk9F9QpEJgI+8CNBcAj4RjzvUFLtRWoZNepSfH8aMAWYQqnkoPoXcBCRQ7jugMDfSbTVlYE6ZswEPG85IjcC1wATh6jyHar7Edkixmzvpu0mLZXKdEqlB1G9GpHrgFJE8X+AL4EBYIMYc7jTdjsyUPv7L8b3XwSWAGM7a1n2obpJjFnXUf3TWixrISIPA7d2EWYrQbBGqtV97VZs20C1rJWIPAWMabduC/YSBE+0K15Hj55IrbYOuDMhHQBvY8yjAtW4FWIbqDAWx9mK6k2daRuqAX1OPG91rKKWdRcibwHjUlBylCC4R6rV/XEKxzJQ4RJsey8ik1DtTl4068WYZZFaLOtpRNakKQKRGrXazVKr7R6y6FAFFM7DcQ4Cl6Rs3qAieV9cd0moFsd5AdWXMtBQ//T96VKtDkQVjeqp6ljWLrIyD0D1XrXtN5qSK5VHMjGvrqH+WSrtVjg/qmikgdrfvxKYkZl5pxF5WG17xRkdlcp19PW9nqmG+ncei+O8F1Ws5S2sMAnb/ilZVW1iTL/AKXWc48D4zP+Rp6nV5onv7wzLKresZFmvpiYoLqNGbVY4gmrvzAMol9fi+5eHZYVegQoXYtu/AH2pCssLIlCtLhLf39aYFf4MtKzHESnMO5tyeXlYcqtOZFZPb5nhRt2LGxQqjVlNBiqMR2RaFrpyxjhs+5bGxOYr0LJmE+J0AQBzGhPCbuEr09eRU0QuakxqNlDk0jNTmYL/o9q0AhVm4JiiA2mJ1Zgw9Fy4IJJmA4Mg9mLiCMRvTAi7hU9kIiWfeI0JYQb+mYmUPCLyb2NSs4Gqv2ciJo+onmpMCutEjmYgJX+IhF5czQb29R3IRFA+afKmyUA5efI34EgmcvKEKpRKXzQmtxoH/pCynDxyWFz3p8bEcANVDxXTubOoP/8OhmW1ugI3FdO5BkT2hCa3Kq+WdYBSaWphJAAuxkwQaJpkRM2Ft6QoKD/UH2W7wsyD6G3Nsdj2McBOSVp+8P1ZUqt9FpbV8gqU+hm6d0Z0Z1LvPL5sZR4McTZG6/sAx4jaPz7X8f2ZUqt93io7cj1w8Bjss4mLyg8fRJkHcY+32fZX1I/wjiROYMxEgZNRheKtSBuzAJGRM54RAd+/YyjzIKaBAr8RBItHUIeySmq1T+MUjL0nIp63jSBYOQJM3Cqu+3zcwu0fMrftNxFZfs7NUOpDlr1izI1tVeukLbWsDYg81EndYYvIPlz3BoFaW9U6bU8dZzOqd3daf5ixH2NmtZquRdHxvrC47j2IvNtp/WGDyADGzOzEPOhyY11c9z5gfY47lj247kyBpt22uHR9MkGMWYbqK93GyRzVbWLMHAG3mzCJHO0QY55G9ZkkYmWC6kbxvEVJhErsbIx43suo3p9UvNQQWSWetzSxcEkFOo06zlxUdzAc1xGDYIVUq68lGTKVp7/a9mTgEyD01YAe4AOLxJiPkw6cWvepcAGOsxPVa9NqIyZ/EATzpFr9Oo3gqZ0PFDgurjsD1Y/SaiMG31IqTUvLPMjggKV43mJgbdrthLADY66VU6d+TbORTE6oijGPZTzMWS/G3CYh5/mSJtMphFrW3YhsTrcRXSmel81rsWRsIICWy9fT1/cJabyuHwRLpVrdmHjcCHoyiVXHuWxwrDg5oZAucLsYE2sVOUl6tgow+FMCH6I6t8tQP6N6m3je94kIa5OeL6OobW8H5ndY/eDgUtTxJDW1Q8/fExFjFqDayXPrM4y5qpfmDSvUcVarbWvMv14OzocvattPDmmeZa3vtc5hjVYqD0QYmL+F216glrUw5MqLvVdbAGi5PPssAx/ttZ5corY9/+wf4CkoKCgoKCgoKEiM/wDSaB3QR6Qn0wAAAABJRU5ErkJggg==","e":1}],"fonts":{"list":[{"fName":"Helvetica","fFamily":"Helvetica","fStyle":"Regular","ascent":72.8994140625}]},"layers":[{"ddd":0,"ind":1,"ty":5,"nm":"Benchmark","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[200,57,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"t":{"d":{"k":[{"s":{"s":72,"f":"Helvetica","t":"Benchmark","ca":0,"j":2,"tr":0,"lh":112,"ls":0,"fc":[0,0,0],"sc":[1,0,0],"sw":1,"of":true},"t":0}]},"p":{},"m":{"g":1,"a":{"a":0,"k":[0,0],"ix":2}},"a":[]},"ip":0,"op":60,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Shape Layer 2","td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[233,170,0],"to":[-11.167,11,0],"ti":[11.167,-11,0]},{"t":59,"s":[166,236,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[332.711,332.711],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0.355,0.355],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":60,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Shape Layer 1","parent":2,"tt":1,"tp":2,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"sr","sy":1,"d":1,"pt":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[5]},{"t":59,"s":[6]}],"ix":3},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[0,0],"to":[0.833,1.333],"ti":[-0.833,-1.333]},{"t":59,"s":[5,8]}],"ix":4},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[187.839]},{"t":59,"s":[204.839]}],"ix":5},"ir":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[54.99]},{"t":59,"s":[61.99]}],"ix":6},"is":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":59,"s":[5]}],"ix":8},"or":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[109.981]},{"t":59,"s":[123.981]}],"ix":7},"os":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":59,"s":[40]}],"ix":9},"ix":1,"nm":"Polystar Path 1","mn":"ADBE Vector Shape - Star","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[104.031,-124.953],"to":[-36.333,40.167],"ti":[36.333,-40.167]},{"t":59,"s":[-113.969,116.047]}],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[67.556,62.26],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Polystar 2","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"sr","sy":2,"d":1,"pt":{"a":0,"k":5,"ix":3},"p":{"a":0,"k":[0,0],"ix":4},"r":{"a":0,"k":0,"ix":5},"or":{"a":0,"k":74.562,"ix":7},"os":{"a":0,"k":0,"ix":9},"ix":1,"nm":"Polystar Path 1","mn":"ADBE Vector Shape - Star","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":10,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"rp","c":{"a":0,"k":3,"ix":1},"o":{"a":0,"k":0,"ix":2},"m":1,"ix":4,"tr":{"ty":"tr","p":{"a":0,"k":[100,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":4},"so":{"a":0,"k":100,"ix":5},"eo":{"a":0,"k":100,"ix":6},"nm":"Transform"},"nm":"Repeater 1","mn":"ADBE Vector Filter - Repeater","hd":false},{"ty":"tr","p":{"a":0,"k":[-124.641,3.805],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Polystar 1","np":4,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[89.898,89.898],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-9.051,-127.051],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[101.414,91.422],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-135.293,-134.289],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":60,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":2,"nm":"Heart-80.png","cl":"png","refId":"image_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[352,357,0],"ix":2,"l":2},"a":{"a":0,"k":[40,40,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":60,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":1,"nm":"Yellow Solid 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[200,200,0],"ix":2,"l":2},"a":{"a":0,"k":[200,200,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"sw":400,"sh":400,"sc":"#ffe100","ip":0,"op":60,"st":0,"bm":0}],"markers":[],"chars":[{"ch":"B","size":72,"style":"Regular","w":66.7,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[-2.171,-0.911],[0,-4.688],[3.618,-1.79],[4.144,0]],"o":[[0,0],[0,0],[4.276,0],[3.848,1.628],[0,4.655],[-2.303,1.14],[0,0]],"v":[[16.895,-41.406],[16.895,-63.623],[34.41,-63.623],[44.081,-62.256],[49.854,-52.783],[44.426,-43.115],[34.756,-41.406]],"c":true},"ix":2},"nm":"B","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[-2.498,-1.009],[0,-5.208],[1.61,-2.18],[6.013,0]],"o":[[0,0],[0,0],[4.108,0],[4.699,1.888],[0,3.093],[-2.563,3.451],[0,0]],"v":[[16.895,-8.301],[16.895,-33.545],[36.364,-33.545],[46.272,-32.031],[53.32,-21.387],[50.905,-13.477],[38.04,-8.301]],"c":true},"ix":2},"nm":"B","mn":"ADBE Vector Shape - Group","hd":false},{"ind":2,"ty":"sh","ix":3,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[-4.356,5.599],[0,4.623],[3.223,3.125],[3.678,1.4],[-1.335,1.53],[0,4.525],[2.097,2.962],[8.453,0],[0,0]],"o":[[0,0],[9.498,0],[2.914,-3.743],[0,-5.501],[-1.823,-1.758],[2.506,-1.27],[2.571,-2.897],[0,-3.873],[-3.572,-5.013],[0,0],[0,0]],"v":[[7.373,0],[37.883,0],[58.665,-8.398],[63.037,-20.947],[58.203,-33.887],[49.951,-38.623],[55.713,-42.822],[59.57,-53.955],[56.425,-64.209],[38.387,-71.729],[7.373,-71.729]],"c":true},"ix":2},"nm":"B","mn":"ADBE Vector Shape - Group","hd":false}],"nm":"B","np":6,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}]},"fFamily":"Helvetica"},{"ch":"e","size":72,"style":"Regular","w":55.62,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[3.688,0],[4.529,-5.241],[0,-8.398],[-4.497,-4.736],[-6.47,0],[-2.1,0.52],[-2.65,2.605],[-1.286,2.361],[-0.228,1.921],[0,0],[1.518,-1.765],[4.554,0],[2.325,3.215],[0.162,5.321],[0,0],[0.517,2.409],[1.747,2.637],[3.461,1.742]],"o":[[-7.312,0],[-4.53,5.241],[0,8.529],[4.497,4.736],[2.649,0],[3.909,-0.912],[1.582,-1.497],[1.286,-2.36],[0,0],[-0.633,2.322],[-2.713,3.041],[-4.877,0],[-2.325,-3.215],[0,0],[0,-5.273],[-0.583,-3.516],[-1.812,-2.766],[-3.462,-1.741]],"v":[[28.022,-53.467],[10.261,-45.605],[3.467,-25.146],[10.211,-5.249],[26.661,1.855],[33.784,1.074],[43.622,-4.199],[47.925,-9.985],[50.195,-16.406],[41.553,-16.406],[38.326,-10.275],[27.425,-5.713],[16.621,-10.535],[12.891,-23.34],[50.928,-23.34],[50.151,-34.863],[46.657,-44.092],[38.747,-50.854]],"c":true},"ix":2},"nm":"e","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[0,0],[-2.711,2.914],[-4.002,0],[-2.389,-4.231],[-0.356,-3.809]],"o":[[0.161,-4.492],[2.711,-2.913],[5.584,0],[1.291,2.279],[0,0]],"v":[[13.086,-30.322],[17.395,-41.431],[27.466,-45.801],[39.425,-39.453],[41.895,-30.322]],"c":true},"ix":2},"nm":"e","mn":"ADBE Vector Shape - Group","hd":false}],"nm":"e","np":5,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}]},"fFamily":"Helvetica"},{"ch":"n","size":72,"style":"Regular","w":55.62,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[-0.439,1.839],[-1.465,1.726],[-2.409,0.716],[-2.084,0],[-1.562,-3.255],[0,-3.19],[0,0],[0,0],[0,0],[1.432,2.832],[7.422,0],[2.766,-1.334],[2.473,-3.059],[0,0],[0,0]],"o":[[0,0],[0,0],[0,-3.418],[0.439,-1.839],[1.823,-2.148],[1.334,-0.423],[4.102,0],[0.944,1.953],[0,0],[0,0],[0,0],[0,-5.273],[-2.605,-5.176],[-3.386,0],[-2.767,1.335],[0,0],[0,0],[0,0]],"v":[[6.445,0],[15.234,0],[15.234,-27.393],[15.894,-35.278],[18.75,-40.625],[25.098,-44.922],[30.225,-45.557],[38.721,-40.674],[40.137,-32.959],[40.137,0],[49.072,0],[49.072,-33.545],[46.924,-45.703],[31.885,-53.467],[22.656,-51.465],[14.795,-44.873],[14.795,-52.295],[6.445,-52.295]],"c":true},"ix":2},"nm":"n","mn":"ADBE Vector Shape - Group","hd":false}],"nm":"n","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}]},"fFamily":"Helvetica"},{"ch":"c","size":72,"style":"Regular","w":50,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[5.891,0],[4.459,-5.11],[0,-9.18],[-4.07,-4.996],[-6.836,0],[-3.906,3.207],[-0.977,6.934],[0,0],[2.132,-2.221],[3.645,0],[2.213,3.577],[0,5.235],[-1.562,3.544],[-5.599,0],[-1.855,-2.132],[-0.521,-3.223],[0,0],[3.694,2.865]],"o":[[-6.902,0],[-4.46,5.111],[0,7.487],[4.069,4.997],[6.087,0],[3.906,-3.206],[0,0],[-0.814,3.859],[-2.132,2.222],[-4.753,0],[-2.214,-3.577],[0,-5.202],[2.409,-5.462],[4.102,0],[1.855,2.132],[0,0],[-0.749,-6.998],[-3.695,-2.864]],"v":[[26.611,-53.809],[9.57,-46.143],[2.881,-24.707],[8.984,-5.981],[25.342,1.514],[40.332,-3.296],[47.656,-18.506],[39.111,-18.506],[34.692,-9.386],[26.025,-6.055],[15.576,-11.419],[12.256,-24.635],[14.6,-37.754],[26.611,-45.947],[35.547,-42.749],[39.111,-34.717],[47.656,-34.717],[40.991,-49.512]],"c":true},"ix":2},"nm":"c","mn":"ADBE Vector Shape - Group","hd":false}],"nm":"c","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}]},"fFamily":"Helvetica"},{"ch":"h","size":72,"style":"Regular","w":55.62,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[-2.962,2.507],[-3.646,0],[-1.595,-2.864],[0,-3.841],[0,0],[0,0],[0,0],[1.465,2.898],[7.584,0],[2.832,-1.855],[2.083,-2.637],[0,0],[0,0]],"o":[[0,0],[0,0],[0,-6.966],[2.962,-2.506],[4.395,0],[0.977,1.791],[0,0],[0,0],[0,0],[0,-5.143],[-2.702,-5.305],[-4.232,0],[-1.66,1.074],[0,0],[0,0],[0,0]],"v":[[6.445,0],[15.234,0],[15.234,-27.734],[19.678,-41.943],[29.59,-45.703],[38.574,-41.406],[40.039,-32.959],[40.039,0],[49.072,0],[49.072,-33.545],[46.875,-45.605],[31.445,-53.564],[20.85,-50.781],[15.234,-45.215],[15.234,-71.973],[6.445,-71.973]],"c":true},"ix":2},"nm":"h","mn":"ADBE Vector Shape - Group","hd":false}],"nm":"h","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}]},"fFamily":"Helvetica"},{"ch":"m","size":72,"style":"Regular","w":83.3,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[-2.718,2.572],[-3.32,0],[-1.335,-2.441],[0,-3.548],[0,0],[0,0],[0,0],[-2.49,2.312],[-3.451,0],[-1.742,-1.302],[0,-3.483],[0,0],[0,0],[0,0],[1.367,2.734],[7.031,0],[2.637,-1.35],[1.953,-2.799],[1.432,1.172],[4.166,0],[2.897,-1.985],[2.083,-2.571],[0,0],[0,0]],"o":[[0,0],[0,0],[0,-6.738],[2.718,-2.571],[3.645,0],[0.846,1.628],[0,0],[0,0],[0,0],[0,-5.403],[2.49,-2.311],[2.506,0],[1.741,1.302],[0,0],[0,0],[0,0],[0,-4.622],[-2.539,-5.078],[-3.288,0],[-2.637,1.351],[-1.172,-2.278],[-2.539,-2.051],[-3.679,0],[-1.693,1.172],[0,0],[0,0],[0,0]],"v":[[6.445,0],[15.234,0],[15.234,-27.734],[19.312,-41.699],[28.369,-45.557],[35.84,-41.895],[37.109,-34.131],[37.109,0],[46.045,0],[46.045,-30.42],[49.78,-41.992],[58.691,-45.459],[65.063,-43.506],[67.676,-36.328],[67.676,0],[76.807,0],[76.807,-34.814],[74.756,-45.85],[60.4,-53.467],[51.514,-51.44],[44.629,-45.215],[40.723,-50.391],[30.664,-53.467],[20.801,-50.488],[15.137,-44.873],[15.137,-52.295],[6.445,-52.295]],"c":true},"ix":2},"nm":"m","mn":"ADBE Vector Shape - Group","hd":false}],"nm":"m","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}]},"fFamily":"Helvetica"},{"ch":"a","size":72,"style":"Regular","w":55.62,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,2.533],[-2.718,1.527],[-3.184,0.423],[0,0],[-1.689,0.458],[-1.073,0.686],[0,0],[4.883,-2.373],[3.092,0],[1.855,1.462]],"o":[[0,-3.345],[1.604,-0.909],[0,0],[1.624,-0.195],[1.689,-0.457],[0,0],[0,5.396],[-2.898,1.43],[-2.539,0],[-1.855,-1.462]],"v":[[13.184,-13.897],[17.261,-21.204],[24.443,-23.203],[29.755,-23.885],[34.725,-24.863],[38.867,-26.578],[38.867,-19.509],[31.543,-7.858],[22.559,-5.713],[15.967,-7.905]],"c":true},"ix":2},"nm":"a","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[0,0],[3.027,-2.666],[0,-4.844],[-3.076,-2.942],[-4.851,0],[-3.353,1.724],[-1.791,2.344],[-0.716,-1.139],[-3.027,0],[-0.716,0.098],[-1.367,0.391],[0,0],[0.488,-0.049],[0.391,0],[0.374,0.552],[0,0.912],[0,0],[3.804,2.344],[6.145,0],[4.015,-2.53],[0.163,-6.464],[0,0],[-1.072,1.375],[-4.581,0],[-2.128,-1.356],[0,-3.006],[0.39,-0.784],[2.018,-0.259]],"o":[[-4.916,0.618],[-3.027,2.666],[0,4.422],[3.076,2.942],[4.036,0],[3.352,-1.725],[0.293,2.083],[1.367,2.148],[1.237,0],[0.716,-0.098],[0,0],[-0.554,0.098],[-0.488,0.049],[-1.205,0],[-0.375,-0.552],[0,0],[0,-4.948],[-3.837,-2.344],[-5.299,0],[-4.015,2.53],[0,0],[0.325,-2.715],[1.917,-2.486],[3.963,0],[2.128,1.357],[0,1.471],[-0.684,1.43],[0,0]],"v":[[20.459,-29.893],[8.545,-24.966],[4.004,-13.702],[8.618,-2.656],[20.508,1.758],[31.592,-0.829],[39.307,-6.932],[40.82,-2.099],[47.412,1.123],[50.342,0.977],[53.467,0.244],[53.467,-6.249],[51.904,-6.03],[50.586,-5.957],[48.218,-6.786],[47.656,-8.982],[47.656,-39.111],[41.95,-50.049],[26.978,-53.564],[13.005,-49.77],[6.738,-36.279],[14.941,-36.279],[17.036,-42.413],[26.783,-46.143],[35.919,-44.109],[39.111,-37.565],[38.526,-34.183],[34.473,-31.648]],"c":true},"ix":2},"nm":"a","mn":"ADBE Vector Shape - Group","hd":false}],"nm":"a","np":5,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}]},"fFamily":"Helvetica"},{"ch":"r","size":72,"style":"Regular","w":33.3,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[-2.377,2.849],[-4.427,0],[-0.439,-0.032],[-0.521,-0.098],[0,0],[0.391,0.033],[0.163,0],[2.669,-2.522],[0.684,-1.758],[0,0],[0,0]],"o":[[0,0],[0,0],[0,-3.711],[2.376,-2.848],[0.52,0],[0.439,0.033],[0,0],[-0.945,-0.098],[-0.391,-0.032],[-3.484,0],[-2.67,2.523],[0,0],[0,0],[0,0]],"v":[[6.689,0],[15.479,0],[15.479,-30.078],[19.043,-39.917],[29.248,-44.189],[30.688,-44.141],[32.129,-43.945],[32.129,-53.223],[30.127,-53.418],[29.297,-53.467],[20.068,-49.683],[15.039,-43.262],[15.039,-52.295],[6.689,-52.295]],"c":true},"ix":2},"nm":"r","mn":"ADBE Vector Shape - Group","hd":false}],"nm":"r","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}]},"fFamily":"Helvetica"},{"ch":"k","size":72,"style":"Regular","w":50,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[6.25,0],[14.697,0],[14.697,-19.629],[21.914,-26.611],[38.362,0],[49.59,0],[28.364,-32.851],[48.463,-52.295],[37.235,-52.295],[14.697,-30.083],[14.697,-71.729],[6.25,-71.729]],"c":true},"ix":2},"nm":"k","mn":"ADBE Vector Shape - Group","hd":false}],"nm":"k","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}]},"fFamily":"Helvetica"}]} \ No newline at end of file
diff --git a/app-benchmark/src/main/res/values/strings.xml b/app-benchmark/src/main/res/values/strings.xml
new file mode 100755
index 00000000..1ec0afc7
--- /dev/null
+++ b/app-benchmark/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+<resources>
+ <string name="app_name">Lottie Benchmark</string>
+</resources>
diff --git a/baselineprofile/.gitignore b/baselineprofile/.gitignore
new file mode 100644
index 00000000..42afabfd
--- /dev/null
+++ b/baselineprofile/.gitignore
@@ -0,0 +1 @@
+/build \ No newline at end of file
diff --git a/baselineprofile/build.gradle b/baselineprofile/build.gradle
new file mode 100644
index 00000000..bd34c216
--- /dev/null
+++ b/baselineprofile/build.gradle
@@ -0,0 +1,43 @@
+import com.android.build.api.dsl.ManagedVirtualDevice
+
+plugins {
+ id 'com.android.test'
+ id 'org.jetbrains.kotlin.android'
+ id 'androidx.baselineprofile'
+}
+
+android {
+ namespace 'com.airbnb.lottie.baselineprofile'
+ compileSdk 34
+
+ defaultConfig {
+ minSdk 28
+ targetSdk 34
+
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ testInstrumentationRunnerArguments["androidx.benchmark.suppressErrors"] = "EMULATOR"
+ }
+
+ targetProjectPath = ":app-benchmark"
+
+ testOptions.managedDevices.devices {
+ pixel6Api34(ManagedVirtualDevice) {
+ device = "Pixel 6"
+ apiLevel = 34
+ systemImageSource = "google"
+ }
+ }
+}
+
+baselineProfile {
+ managedDevices += "pixel6Api34"
+ useConnectedDevices = false
+}
+
+dependencies {
+ implementation libs.okio
+ implementation libs.androidx.test.junit
+ implementation libs.androidx.test.espresso
+ implementation libs.androidx.test.uiautomator
+ implementation libs.androidx.test.macrobenchmark
+}
diff --git a/baselineprofile/src/main/AndroidManifest.xml b/baselineprofile/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..227314ee
--- /dev/null
+++ b/baselineprofile/src/main/AndroidManifest.xml
@@ -0,0 +1 @@
+<manifest /> \ No newline at end of file
diff --git a/baselineprofile/src/main/java/com/airbnb/lottie/baselineprofile/BaselineProfileGenerator.kt b/baselineprofile/src/main/java/com/airbnb/lottie/baselineprofile/BaselineProfileGenerator.kt
new file mode 100644
index 00000000..8c358307
--- /dev/null
+++ b/baselineprofile/src/main/java/com/airbnb/lottie/baselineprofile/BaselineProfileGenerator.kt
@@ -0,0 +1,32 @@
+package com.airbnb.lottie.baselineprofile
+
+import androidx.benchmark.macro.junit4.BaselineProfileRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.LargeTest
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * You can run the generator with the Generate Baseline Profile gradle task.
+ * ```
+ * ./gradlew :lottie(-compose):generateReleaseBaselineProfile -Pandroid.testInstrumentationRunnerArguments.androidx.benchmark.enabledRules=BaselineProfile
+ * ```
+ *
+ * After you run the generator, you can verify the improvements running the [StartupBenchmarks] benchmark.
+ **/
+@RunWith(AndroidJUnit4::class)
+@LargeTest
+class BaselineProfileGenerator {
+
+ @get:Rule
+ val rule = BaselineProfileRule()
+
+ @Test
+ fun generate() {
+ rule.collect("com.airbnb.lottie.benchmark.app") {
+ pressHome()
+ startActivityAndWait()
+ }
+ }
+}
diff --git a/baselineprofile/src/main/java/com/airbnb/lottie/baselineprofile/StartupBenchmarks.kt b/baselineprofile/src/main/java/com/airbnb/lottie/baselineprofile/StartupBenchmarks.kt
new file mode 100644
index 00000000..f55d70cc
--- /dev/null
+++ b/baselineprofile/src/main/java/com/airbnb/lottie/baselineprofile/StartupBenchmarks.kt
@@ -0,0 +1,69 @@
+package com.airbnb.lottie.baselineprofile
+
+import androidx.benchmark.macro.BaselineProfileMode
+import androidx.benchmark.macro.CompilationMode
+import androidx.benchmark.macro.StartupMode
+import androidx.benchmark.macro.StartupTimingMetric
+import androidx.benchmark.macro.junit4.MacrobenchmarkRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.LargeTest
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * This test class benchmarks the speed of app startup.
+ * Run this benchmark to verify how effective a Baseline Profile is.
+ * It does this by comparing [CompilationMode.None], which represents the app with no Baseline
+ * Profiles optimizations, and [CompilationMode.Partial], which uses Baseline Profiles.
+ *
+ * Run this benchmark to see startup measurements and captured system traces for verifying
+ * the effectiveness of your Baseline Profiles. You can run it directly from Android
+ * Studio as an instrumentation test, or run all benchmarks with this Gradle task:
+ * ```
+ * ./gradlew :baselineprofile:connectedAndroidTest -Pandroid.testInstrumentationRunnerArguments.androidx.benchmark.enabledRules=Macrobenchmark
+ * ```
+ *
+ * You should run the benchmarks on a physical device, not an Android emulator, because the
+ * emulator doesn't represent real world performance and shares system resources with its host.
+ *
+ * For more information, see the [Macrobenchmark documentation](https://d.android.com/macrobenchmark#create-macrobenchmark)
+ * and the [instrumentation arguments documentation](https://d.android.com/topic/performance/benchmarking/macrobenchmark-instrumentation-args).
+ *
+ * Note that the performance impact of this test is only tangentially related to the impact of the baseline profile for Lottie.
+ * The benefit of the baseline profile for Lottie is less about startup time and more about time spent running the hot path
+ * of the Lottie rendering code.
+ *
+ * Ideally, this test would be updated to reflect that rather than just startup time but that's a task for another time…
+ **/
+@RunWith(AndroidJUnit4::class)
+@LargeTest
+class StartupBenchmarks {
+
+ @get:Rule
+ val rule = MacrobenchmarkRule()
+
+ @Test
+ fun startupCompilationNone() =
+ benchmark(CompilationMode.None())
+
+ @Test
+ fun startupCompilationBaselineProfiles() =
+ benchmark(CompilationMode.Partial(BaselineProfileMode.Require))
+
+ private fun benchmark(compilationMode: CompilationMode) {
+ rule.measureRepeated(
+ packageName = "com.airbnb.lottie.benchmark.app",
+ metrics = listOf(StartupTimingMetric()),
+ compilationMode = compilationMode,
+ startupMode = StartupMode.COLD,
+ iterations = 10,
+ setupBlock = {
+ pressHome()
+ },
+ measureBlock = {
+ startActivityAndWait()
+ }
+ )
+ }
+}
diff --git a/benchmark/build.gradle b/benchmark/build.gradle
new file mode 100644
index 00000000..4ce7907e
--- /dev/null
+++ b/benchmark/build.gradle
@@ -0,0 +1,52 @@
+plugins {
+ id 'com.android.test'
+ id 'org.jetbrains.kotlin.android'
+ id 'androidx.baselineprofile'
+}
+
+android {
+ namespace 'com.airbnb.lottie.benchmark'
+ compileSdk 34
+
+ kotlinOptions {
+ freeCompilerArgs += "-opt-in=kotlin.RequiresOptIn"
+ }
+
+ defaultConfig {
+ minSdk 30
+ targetSdk 34
+
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ }
+
+ buildTypes {
+ release {
+ debuggable = true
+ signingConfig = debug.signingConfig
+ }
+ }
+
+ targetProjectPath = ":app-benchmark"
+ experimentalProperties["android.experimental.self-instrumenting"] = true
+
+ testOptions.managedDevices.devices {
+ pixel6Api31(com.android.build.api.dsl.ManagedVirtualDevice) {
+ device = "Pixel 6"
+ apiLevel = 31
+ systemImageSource = "aosp"
+ }
+ }
+}
+
+baselineProfile {
+ managedDevices += "pixel6Api31"
+ useConnectedDevices = false
+}
+
+dependencies {
+ implementation libs.androidx.test.junit
+ implementation libs.androidx.test.espresso
+ implementation libs.androidx.test.uiautomator
+ implementation libs.androidx.test.macrobenchmark
+ implementation libs.compose.ui.test.junit
+}
diff --git a/sample-compose-benchmark/src/main/AndroidManifest.xml b/benchmark/src/main/AndroidManifest.xml
index 4029f812..2dbdaabc 100644
--- a/sample-compose-benchmark/src/main/AndroidManifest.xml
+++ b/benchmark/src/main/AndroidManifest.xml
@@ -1,12 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- package="com.airbnb.lottie.sample.compose.benchmark">
+ xmlns:tools="http://schemas.android.com/tools">
<queries>
- <package android:name="com.airbnb.lottie.sample.compose" />
+ <package android:name="com.airbnb.lottie.benchmark.app" />
</queries>
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
tools:ignore="ScopedStorage" />
-</manifest> \ No newline at end of file
+</manifest>
diff --git a/sample-compose-benchmark/src/main/java/com/airbnb/lottie/sample/compose/benchmark/LottieBaselineBenchmark.kt b/benchmark/src/main/java/com/airbnb/lottie/sample/compose/benchmark/LottieBaselineBenchmark.kt
index fef0b962..bb8da9f2 100644
--- a/sample-compose-benchmark/src/main/java/com/airbnb/lottie/sample/compose/benchmark/LottieBaselineBenchmark.kt
+++ b/benchmark/src/main/java/com/airbnb/lottie/sample/compose/benchmark/LottieBaselineBenchmark.kt
@@ -19,6 +19,7 @@ import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import kotlin.time.Duration
+import kotlin.time.Duration.Companion.seconds
import kotlin.time.DurationUnit
import kotlin.time.ExperimentalTime
@@ -48,7 +49,6 @@ class LottieBaselineBenchmark {
benchmark(CompilationMode.Partial(baselineProfileMode = BaselineProfileMode.Require))
}
- @OptIn(ExperimentalTime::class)
fun benchmark(compilationMode: CompilationMode) {
benchmarkRule.measureRepeated(
packageName = PACKAGE_NAME,
@@ -78,7 +78,6 @@ class LottieBaselineBenchmark {
}
}
- @OptIn(ExperimentalTime::class)
private fun UiScrollable.waitUntilReady() {
this.waitUntil {
// We know that there are at least 9 children
@@ -86,8 +85,7 @@ class LottieBaselineBenchmark {
}
}
- @OptIn(ExperimentalTime::class)
- private fun UiObject.clickAndWait(maxWaitTime: Duration = Duration.seconds(5)) {
+ private fun UiObject.clickAndWait(maxWaitTime: Duration = 5.seconds) {
val maxWaitTimeMs = maxWaitTime.toLong(DurationUnit.MILLISECONDS)
click()
device.waitForIdle(maxWaitTimeMs)
@@ -95,8 +93,7 @@ class LottieBaselineBenchmark {
device.pressBack()
}
- @OptIn(ExperimentalTime::class)
- private fun <T> T.waitUntil(maxWaitTime: Duration = Duration.seconds(5), condition: (T) -> Boolean) {
+ private fun <T> T.waitUntil(maxWaitTime: Duration = 5.seconds, condition: (T) -> Boolean) {
var waitTime = 0L
val maxWaitTimeMs = maxWaitTime.toLong(DurationUnit.MILLISECONDS)
val incrementalDelay = 150L
diff --git a/benchmark/src/main/java/com/airbnb/lottie/sample/compose/benchmark/LottieBaselineProfiles.kt b/benchmark/src/main/java/com/airbnb/lottie/sample/compose/benchmark/LottieBaselineProfiles.kt
new file mode 100644
index 00000000..c246cb3e
--- /dev/null
+++ b/benchmark/src/main/java/com/airbnb/lottie/sample/compose/benchmark/LottieBaselineProfiles.kt
@@ -0,0 +1,40 @@
+package com.airbnb.lottie.sample.compose.benchmark
+
+import android.content.Context
+import androidx.benchmark.macro.junit4.BaselineProfileRule
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class LottieBaselineProfiles {
+
+ @get:Rule
+ val baselineProfileRule = BaselineProfileRule()
+
+ private lateinit var context: Context
+ private lateinit var device: UiDevice
+
+ @Before
+ fun setUp() {
+ val instrumentation = InstrumentationRegistry.getInstrumentation()
+ context = ApplicationProvider.getApplicationContext()
+ device = UiDevice.getInstance(instrumentation)
+ }
+
+ @Test
+ fun baselineProfiles() {
+ baselineProfileRule.collect(
+ packageName = "com.airbnb.lottie.benchmark.app",
+ ) {
+ pressHome()
+ startActivityAndWait()
+ Thread.sleep(5_000L)
+ }
+ }
+}
diff --git a/build.gradle b/build.gradle
index 5bfee23f..f67b24a3 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,46 +1,38 @@
import org.ajoberstar.grgit.Grgit
+import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
-buildscript {
- ext {
- coroutinesVersion = '1.6.2'
- coreVersion = '1.6.0'
- appcompatVersion = '1.3.1'
- activityVersion = '1.3.1'
- lifecycleVersion = '2.3.1'
- composeVersion = '1.1.1'
- kotlinVersion = '1.6.10'
- daggerVersion = '2.38.1'
- awsVersion = '2.8.3'
- mockitoVersion = '3.12.4'
- robolectricVersion = '4.6.1'
- retrofitVersion = '2.9.0'
- materialVersion = '1.4.0'
- epoxyVersion = '4.6.4'
- junitVersion = '4.13.2'
- extJunitVersion = '1.1.3'
- espressoVersion = '3.3.0'
- startupVersion = '1.2.0-alpha01'
- }
-
- repositories {
- google()
- gradlePluginPortal()
- }
- dependencies {
- classpath 'org.ajoberstar:grgit:2.3.0'
- classpath 'com.android.tools.build:gradle:7.0.4'
- classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
- classpath "net.ltgt.gradle:gradle-errorprone-plugin:2.0.2"
- classpath 'com.vanniktech:gradle-maven-publish-plugin:0.18.0'
- classpath 'org.jetbrains.dokka:dokka-gradle-plugin:1.5.30'
- }
+plugins {
+ id "com.android.tools.build" apply false
+ id 'net.ltgt.errorprone' apply false
+ id "com.google.devtools.ksp" apply false
+ id "org.ajoberstar.grgit" apply false
+ id "org.jetbrains.kotlin.jvm" apply false
+ id "com.vanniktech.maven.publish" apply false
+ id "org.jetbrains.dokka" apply false
+ id "androidx.baselineprofile" apply false
+ id "org.jetbrains.kotlinx.binary-compatibility-validator" apply false
}
allprojects {
- repositories {
- google()
- mavenCentral()
- maven { url "https://jitpack.io" }
+ def compileJavaVersion = JavaVersion.VERSION_17
+ def targetJavaVersion = JavaVersion.VERSION_17
+ pluginManager.withPlugin("java") {
+ java {
+ toolchain {
+ languageVersion.set(JavaLanguageVersion.of(compileJavaVersion.majorVersion))
+ }
+ }
+ tasks.withType(JavaCompile).configureEach {
+ options.release.set(targetJavaVersion.majorVersion)
+ }
+ }
+ pluginManager.withPlugin("org.jetbrains.kotlin.android") {
+ kotlin {
+ jvmToolchain(compileJavaVersion.majorVersion.toInteger())
+ }
+ tasks.withType(KotlinCompile).configureEach {
+ kotlinOptions.jvmTarget = targetJavaVersion.toString()
+ }
}
}
@@ -50,6 +42,6 @@ ext {
gitBranch = git.branch.current().name
}
-task clean(type: Delete) {
+tasks.register('clean', Delete) {
delete rootProject.buildDir
}
diff --git a/decrypt.sh b/decrypt.sh
deleted file mode 100755
index 2b7610d3..00000000
--- a/decrypt.sh
+++ /dev/null
@@ -1,6 +0,0 @@
-if [ "$TRAVIS_REPO_SLUG" != "airbnb/lottie-android" ]; then
- echo "Skipping decrypt because api keys are not available from forks."
- exit 0
-fi
-openssl aes-256-cbc -K $encrypted_7f6a0d70974a_key -iv $encrypted_7f6a0d70974a_iv -in secrets.tar.enc -out secrets.tar -d
-tar xvf secrets.tar \ No newline at end of file
diff --git a/gcloud_run.sh b/gcloud_run.sh
deleted file mode 100755
index 9dc68427..00000000
--- a/gcloud_run.sh
+++ /dev/null
@@ -1,43 +0,0 @@
-#! /bin/bash
-set -e
-if [ -z $TRAVIS_PULL_REQUEST_SLUG ] && [ "$TRAVIS_REPO_SLUG" != "airbnb/lottie-android" ]; then
- echo "Skipping gcloud run for PR because api keys are not available from forks."
- exit 0
-fi
-
-if [ ! -f ${HOME}/google-cloud-sdk/install.sh ]; then
- mkdir $HOME/.cache
- echo "File not found!"
- curl https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/google-cloud-sdk-209.0.0-linux-x86_64.tar.gz -o gcloud.tar.gz
- tar xzf gcloud.tar.gz -C ${HOME}
- ${HOME}/google-cloud-sdk/install.sh --quiet --usage-reporting false
-fi
-echo $GCLOUD_SERVICE_KEY | base64 --decode --ignore-garbage > ${HOME}/.cache/gcloud-service-key.json
-gcloud auth activate-service-account --key-file ${HOME}/.cache/gcloud-service-key.json
-gcloud config set project lottie-snapshots
-
-RunTests()
-{
-gcloud firebase test android run --no-auto-google-login --type instrumentation --device model=Nexus5X,version=26 --app LottieSample/build/outputs/apk/debug/LottieSample-debug.apk --test LottieSample/build/outputs/apk/androidTest/debug/LottieSample-debug-androidTest.apk
-result=$?
-}
-
-./gradlew :LottieSample:assembleDebug :LottieSample:assembleAndroidTest
-RunTests
-
-if [ "$result" -ne "0" ]; then
- # Retry if it fails. Sometimes the tests fail on Firebase with a native error
- echo "Firebase tests failed. Trying again."
- RunTests
-fi
-
-if [ "$result" -ne "0" ]; then
- # Retry if it fails. Sometimes the tests fail on Firebase with a native error
- echo "Firebase tests failed. Trying again."
- RunTests
-fi
-
-if [ "$result" -eq "0" ]; then
- ./post_pr_comment.js
-fi
-exit $result \ No newline at end of file
diff --git a/gcloud_setup.sh b/gcloud_setup.sh
deleted file mode 100755
index e29b36e4..00000000
--- a/gcloud_setup.sh
+++ /dev/null
@@ -1,18 +0,0 @@
-#! /bin/bash
-echo Slug $TRAVIS_PULL_REQUEST_SLUG
-if [ -z $TRAVIS_PULL_REQUEST_SLUG ] && [ "$TRAVIS_PULL_REQUEST_SLUG" != "airbnb/lottie-android" ]; then
- echo "Skipping gcloud setup for PR because api keys are not available from forks."
- exit 0
-fi
-echo $GCLOUD_SERVICE_KEY | base64 --decode --ignore-garbage > ${HOME}/gcloud-service-key.json
-curl https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/google-cloud-sdk-182.0.0-linux-x86_64.tar.gz -o gcloud.tar.gz
-tar xzf gcloud.tar.gz -C ${HOME}
-${HOME}/google-cloud-sdk/install.sh --quiet --usage-reporting false
-gcloud auth activate-service-account --key-file ${HOME}/gcloud-service-key.json
- # - gcloud components update
-gcloud config set project lottie-snapshots
-export TRAVIS_GIT_BRANCH=$(if [ "$TRAVIS_PULL_REQUEST" == "false" ]; then echo $TRAVIS_BRANCH; else echo $TRAVIS_PULL_REQUEST_BRANCH; fi)
-export GIT_SHA=$(git rev-parse HEAD)
-export GIT_MERGE_BASE=$(git merge-base master)
-echo GIT_SHA $GIT_SHA
-echo GIT_MERGE_BASE $GIT_MERGE_BASE \ No newline at end of file
diff --git a/gradle.properties b/gradle.properties
index 39919d3d..014bbbfc 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,4 +1,4 @@
-VERSION_NAME=5.2.0
+VERSION_NAME=6.4.0
GROUP=com.airbnb.android
POM_DESCRIPTION=Lottie is an animation library that renders Adobe After Effects animations natively in realtime.
@@ -15,7 +15,7 @@ POM_DEVELOPER_EMAIL=lottie@airbnb.com
POM_INCEPTION_YEAR=2017
android.useAndroidX=true
-android.enableJetifier=true
+android.enableJetifier=false
org.gradle.caching=true
org.gradle.jvmargs=-Xmx4g -XX:+UseParallelGC -Dfile.encoding=UTF-8
org.gradle.daemon=true
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
new file mode 100644
index 00000000..30e34993
--- /dev/null
+++ b/gradle/libs.versions.toml
@@ -0,0 +1,67 @@
+## Generated by $ ./gradlew refreshVersionsCatalog
+
+[libraries]
+
+androidx-activity-compose = "androidx.activity:activity-compose:_"
+androidx-appcompat = "androidx.appcompat:appcompat:_"
+androidx-fragment = "androidx.fragment:fragment-ktx:_"
+androidx-browser = "androidx.browser:browser:_"
+androidx-cardview = "androidx.cardview:cardview:_"
+androidx-collection-ktx = "androidx.collection:collection:_"
+androidx-constraintlayout = "androidx.constraintlayout:constraintlayout:_"
+androidx-core-ktx = "androidx.core:core-ktx:_"
+androidx-fragment-testing = "androidx.fragment:fragment-testing:_"
+androidx-multidex = "androidx.multidex:multidex:_"
+androidx-navigation-compose = "androidx.navigation:navigation-compose:_"
+androidx-navigation-ui = "androidx.navigation:navigation-ui-ktx:_"
+androidx-paging-runtime-ktx = "androidx.paging:paging-runtime-ktx:_"
+androidx-recyclerview = "androidx.recyclerview:recyclerview:_"
+androidx-test-core = "androidx.test:core:_"
+androidx-test-espresso = "androidx.test.espresso:espresso-core:_"
+androidx-test-espresso-idling = "androidx.test.espresso:espresso-idling-resource:_"
+androidx-test-junit = "androidx.test.ext:junit:_"
+androidx-test-rules = "androidx.test:rules:_"
+androidx-test-uiautomator = "androidx.test.uiautomator:uiautomator:_"
+androidx-test-macrobenchmark = "androidx.benchmark:benchmark-macro-junit4:_"
+androidx-viewmodel-ktx = "androidx.lifecycle:lifecycle-viewmodel-ktx:_"
+aws-android-sdk-auth-userpools = "com.amazonaws:aws-android-sdk-auth-userpools:_"
+aws-android-sdk-mobile-client = "com.amazonaws:aws-android-sdk-mobile-client:_"
+aws-android-sdk-s3 = "com.amazonaws:aws-android-sdk-s3:_"
+coil-compose = "io.coil-kt:coil-compose:_"
+compose-bom = "androidx.compose:compose-bom:_"
+# Compose deps need the group/name structure without version because version is pulled from the bom.
+compose-foundation = { group = "androidx.compose.foundation", name = "foundation" }
+compose-material = { group = "androidx.compose.material", name = "material" }
+compose-material-icons-extended = { group = "androidx.compose.material", name = "material-icons-extended" }
+compose-ui = { group = "androidx.compose.ui", name = "ui" }
+compose-ui-test-junit = "androidx.compose.ui:ui-test-junit4:_"
+compose-ui-test-manifest = "androidx.compose.ui:ui-test-manifest:_"
+compose-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }
+dagger = "com.google.dagger:dagger:_"
+dagger-compiler = "com.google.dagger:dagger-compiler:_"
+epoxy = "com.airbnb.android:epoxy:_"
+epoxy-processor = "com.airbnb.android:epoxy-processor:_"
+errorprone-core = "com.google.errorprone:error_prone_core:_"
+glide = "com.github.bumptech.glide:glide:_"
+google-material = "com.google.android.material:material:_"
+gson = "com.google.code.gson:gson:_"
+jjwt = "io.jsonwebtoken:jjwt:_"
+junit4 = "junit:junit:_"
+kotlinx-coroutines-android = "org.jetbrains.kotlinx:kotlinx-coroutines-android:_"
+kotlinx-coroutines-test = "org.jetbrains.kotlinx:kotlinx-coroutines-test:_"
+mavericks = "com.airbnb.android:mavericks:_"
+mavericks-compose = "com.airbnb.android:mavericks-compose:_"
+mockito-android = "com.nhaarman.mockitokotlin2:mockito-kotlin:_"
+mockito-core = "org.mockito:mockito-core:_"
+mockito-kotlin = "org.mockito:mockito-android:_"
+mpandroidchart = "com.github.PhilJay:MPAndroidChart:_"
+nullaway = "com.uber.nullaway:nullaway:_"
+okhttp = "com.squareup.okhttp3:okhttp:_"
+okio = "com.squareup.okio:okio:_"
+profileinstaller = "androidx.profileinstaller:profileinstaller:_"
+qrcodereaderview = "com.dlazaro66.qrcodereaderview:qrcodereaderview:_"
+retrofit = "com.squareup.retrofit2:retrofit:_"
+retrofit-gson = "com.squareup.retrofit2:converter-gson:_"
+retrofit-moshi = "com.squareup.retrofit2:converter-moshi:_"
+retrofit-rxjava = "com.squareup.retrofit2:adapter-rxjava2:_"
+robolectric = "org.robolectric:robolectric:_"
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index ffed3a25..17655d0e 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/images/emerge.png b/images/emerge.png
new file mode 100644
index 00000000..76f77274
--- /dev/null
+++ b/images/emerge.png
Binary files differ
diff --git a/issue-repro-compose/build.gradle b/issue-repro-compose/build.gradle
index d3e20bcf..ff497213 100755
--- a/issue-repro-compose/build.gradle
+++ b/issue-repro-compose/build.gradle
@@ -1,38 +1,35 @@
+import static de.fayard.refreshVersions.core.Versions.versionFor
+
plugins {
id 'com.android.application'
- id "kotlin-android"
+ id "org.jetbrains.kotlin.android"
}
android {
- compileSdk 31
+ namespace 'com.airbnb.lottie.issues.compose'
+ compileSdk 34
defaultConfig {
applicationId "com.airbnb.lottie.issues.compose"
minSdk 21
- targetSdk 30
+ targetSdk 34
versionCode 1
versionName "1.0"
}
- compileOptions {
- sourceCompatibility JavaVersion.VERSION_1_8
- targetCompatibility JavaVersion.VERSION_1_8
- }
- kotlinOptions {
- jvmTarget = JavaVersion.VERSION_1_8.toString()
- }
buildFeatures {
compose true
}
composeOptions {
- kotlinCompilerExtensionVersion composeVersion
+ kotlinCompilerExtensionVersion = versionFor(project, AndroidX.compose.compiler)
}
}
dependencies {
implementation project(':lottie-compose')
- implementation "androidx.appcompat:appcompat:$appcompatVersion"
- implementation "androidx.activity:activity-compose:$activityVersion"
- implementation "androidx.compose.ui:ui:$composeVersion"
- implementation "androidx.compose.material:material:$composeVersion"
- implementation "androidx.compose.material:material-icons-extended:$composeVersion"
- implementation "androidx.compose.ui:ui-tooling:$composeVersion"
+ implementation libs.androidx.appcompat
+ implementation libs.androidx.activity.compose
+ implementation platform(libs.compose.bom)
+ implementation libs.compose.ui
+ implementation libs.compose.material
+ implementation libs.compose.material.icons.extended
+ implementation libs.compose.ui.tooling
}
diff --git a/issue-repro-compose/src/main/AndroidManifest.xml b/issue-repro-compose/src/main/AndroidManifest.xml
index 3a75e49f..02d78ec5 100755
--- a/issue-repro-compose/src/main/AndroidManifest.xml
+++ b/issue-repro-compose/src/main/AndroidManifest.xml
@@ -1,12 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.airbnb.lottie.issues.compose">
+<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/Theme.AppCompat.Light.NoActionBar">
- <activity android:name=".ComposeIssueReproActivity">
+ <activity
+ android:name=".ComposeIssueReproActivity"
+ android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
diff --git a/issue-repro-compose/src/main/java/com/airbnb/lottie/issues/compose/ComposeIssueReproActivity.kt b/issue-repro-compose/src/main/kotlin/com/airbnb/lottie/issues/compose/ComposeIssueReproActivity.kt
index abf6dc23..abf6dc23 100755
--- a/issue-repro-compose/src/main/java/com/airbnb/lottie/issues/compose/ComposeIssueReproActivity.kt
+++ b/issue-repro-compose/src/main/kotlin/com/airbnb/lottie/issues/compose/ComposeIssueReproActivity.kt
diff --git a/issue-repro/build.gradle b/issue-repro/build.gradle
index 474f6511..73c77a05 100755
--- a/issue-repro/build.gradle
+++ b/issue-repro/build.gradle
@@ -1,21 +1,18 @@
plugins {
id 'com.android.application'
- id 'kotlin-android'
+ id 'org.jetbrains.kotlin.android'
}
android {
- compileSdk 31
+ namespace 'com.airbnb.lottie.issues'
+ compileSdk 34
defaultConfig {
applicationId "com.airbnb.lottie.issues"
minSdk 16
- targetSdk 30
+ targetSdk 34
versionCode 1
versionName "1.0"
}
- compileOptions {
- sourceCompatibility JavaVersion.VERSION_1_8
- targetCompatibility JavaVersion.VERSION_1_8
- }
buildFeatures {
viewBinding true
}
@@ -23,5 +20,5 @@ android {
dependencies {
implementation project(':lottie')
- implementation "androidx.appcompat:appcompat:$appcompatVersion"
+ implementation libs.androidx.appcompat
}
diff --git a/issue-repro/src/main/AndroidManifest.xml b/issue-repro/src/main/AndroidManifest.xml
index 68264122..10a59c4a 100755
--- a/issue-repro/src/main/AndroidManifest.xml
+++ b/issue-repro/src/main/AndroidManifest.xml
@@ -1,12 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.airbnb.lottie.issues">
+<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/Theme.AppCompat.Light.NoActionBar">
- <activity android:name=".IssueReproActivity">
+ <activity
+ android:name=".IssueReproActivity"
+ android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
diff --git a/issue-repro/src/main/java/com/airbnb/lottie/issues/IssueReproActivity.kt b/issue-repro/src/main/kotlin/com/airbnb/lottie/issues/IssueReproActivity.kt
index 495438c2..495438c2 100755
--- a/issue-repro/src/main/java/com/airbnb/lottie/issues/IssueReproActivity.kt
+++ b/issue-repro/src/main/kotlin/com/airbnb/lottie/issues/IssueReproActivity.kt
diff --git a/issue-repro/src/main/res/layout/issue_repro_activity.xml b/issue-repro/src/main/res/layout/issue_repro_activity.xml
index dc479bb7..05ea4fbf 100755
--- a/issue-repro/src/main/res/layout/issue_repro_activity.xml
+++ b/issue-repro/src/main/res/layout/issue_repro_activity.xml
@@ -11,5 +11,5 @@
android:layout_gravity="center"
app:lottie_rawRes="@raw/heart"
app:lottie_autoPlay="true"
- app:lottie_loop="true" />
+ app:lottie_loop="true"/>
</FrameLayout> \ No newline at end of file
diff --git a/credentials.tar.gz b/issue-repro/src/main/res/raw/intro.json
index e69de29b..e69de29b 100644
--- a/credentials.tar.gz
+++ b/issue-repro/src/main/res/raw/intro.json
diff --git a/lint.xml b/lint.xml
new file mode 100644
index 00000000..fa26a5a1
--- /dev/null
+++ b/lint.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<lint>
+ <!-- Generated by `./gradlew refreshVersions` to avoid errors when using _ as a version. -->
+ <issue id="GradlePluginVersion" severity="ignore" />
+ <issue id="GradleDependency" severity="ignore" />
+ <!-- There are no plans to 18n this app right now -->
+ <issue id="RtlSymmetry" severity="ignore" />
+ <issue id="RtlHardcoded" severity="ignore" />
+ <issue id="HardcodedText" severity="ignore" />
+ <issue id="LabelFor" severity="ignore" />
+ <issue id="ContentDescription" severity="ignore" />
+ <issue id="Autofill" severity="ignore" />
+ <issue id="NotificationPermission">
+ <!-- https://github.com/bumptech/glide/issues/4940 -->
+ <ignore regexp="com.bumptech.glide.request.target.NotificationTarget" />
+ </issue>
+ <issue id="MonochromeLauncherIcon" severity="ignore" />
+ <issue id="VectorPath" severity="ignore" />
+ <issue id="Overdraw" severity="ignore" />
+</lint> \ No newline at end of file
diff --git a/lottie-compose/api/lottie-compose.api b/lottie-compose/api/lottie-compose.api
new file mode 100644
index 00000000..fbecccd9
--- /dev/null
+++ b/lottie-compose/api/lottie-compose.api
@@ -0,0 +1,269 @@
+public final class com/airbnb/lottie/compose/AnimateLottieCompositionAsStateKt {
+ public static final fun animateLottieCompositionAsState (Lcom/airbnb/lottie/LottieComposition;ZZZLcom/airbnb/lottie/compose/LottieClipSpec;FILcom/airbnb/lottie/compose/LottieCancellationBehavior;ZZLandroidx/compose/runtime/Composer;II)Lcom/airbnb/lottie/compose/LottieAnimationState;
+}
+
+public abstract interface class com/airbnb/lottie/compose/LottieAnimatable : com/airbnb/lottie/compose/LottieAnimationState {
+ public abstract fun animate (Lcom/airbnb/lottie/LottieComposition;IIZFLcom/airbnb/lottie/compose/LottieClipSpec;FZLcom/airbnb/lottie/compose/LottieCancellationBehavior;ZZLkotlin/coroutines/Continuation;)Ljava/lang/Object;
+ public abstract fun snapTo (Lcom/airbnb/lottie/LottieComposition;FIZLkotlin/coroutines/Continuation;)Ljava/lang/Object;
+}
+
+public final class com/airbnb/lottie/compose/LottieAnimatable$DefaultImpls {
+ public static synthetic fun animate$default (Lcom/airbnb/lottie/compose/LottieAnimatable;Lcom/airbnb/lottie/LottieComposition;IIZFLcom/airbnb/lottie/compose/LottieClipSpec;FZLcom/airbnb/lottie/compose/LottieCancellationBehavior;ZZLkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object;
+ public static fun getLastFrameNanos (Lcom/airbnb/lottie/compose/LottieAnimatable;)J
+ public static synthetic fun snapTo$default (Lcom/airbnb/lottie/compose/LottieAnimatable;Lcom/airbnb/lottie/LottieComposition;FIZLkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object;
+}
+
+public final class com/airbnb/lottie/compose/LottieAnimatableKt {
+ public static final fun LottieAnimatable ()Lcom/airbnb/lottie/compose/LottieAnimatable;
+ public static final fun rememberLottieAnimatable (Landroidx/compose/runtime/Composer;I)Lcom/airbnb/lottie/compose/LottieAnimatable;
+ public static final fun resetToBeginning (Lcom/airbnb/lottie/compose/LottieAnimatable;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
+}
+
+public final class com/airbnb/lottie/compose/LottieAnimationKt {
+ public static final fun LottieAnimation (Lcom/airbnb/lottie/LottieComposition;FLandroidx/compose/ui/Modifier;ZZZLcom/airbnb/lottie/RenderMode;ZLcom/airbnb/lottie/compose/LottieDynamicProperties;Landroidx/compose/ui/Alignment;Landroidx/compose/ui/layout/ContentScale;ZZLcom/airbnb/lottie/AsyncUpdates;Landroidx/compose/runtime/Composer;III)V
+ public static final fun LottieAnimation (Lcom/airbnb/lottie/LottieComposition;Landroidx/compose/ui/Modifier;ZZLcom/airbnb/lottie/compose/LottieClipSpec;FIZZZLcom/airbnb/lottie/RenderMode;ZZLcom/airbnb/lottie/compose/LottieDynamicProperties;Landroidx/compose/ui/Alignment;Landroidx/compose/ui/layout/ContentScale;ZZLjava/util/Map;ZLcom/airbnb/lottie/AsyncUpdates;Landroidx/compose/runtime/Composer;IIII)V
+ public static final fun LottieAnimation (Lcom/airbnb/lottie/LottieComposition;Lkotlin/jvm/functions/Function0;Landroidx/compose/ui/Modifier;ZZZLcom/airbnb/lottie/RenderMode;ZLcom/airbnb/lottie/compose/LottieDynamicProperties;Landroidx/compose/ui/Alignment;Landroidx/compose/ui/layout/ContentScale;ZZLjava/util/Map;Lcom/airbnb/lottie/AsyncUpdates;ZLandroidx/compose/runtime/Composer;III)V
+}
+
+public abstract interface class com/airbnb/lottie/compose/LottieAnimationState : androidx/compose/runtime/State {
+ public abstract fun getClipSpec ()Lcom/airbnb/lottie/compose/LottieClipSpec;
+ public abstract fun getComposition ()Lcom/airbnb/lottie/LottieComposition;
+ public abstract fun getIteration ()I
+ public abstract fun getIterations ()I
+ public abstract fun getLastFrameNanos ()J
+ public abstract fun getProgress ()F
+ public abstract fun getReverseOnRepeat ()Z
+ public abstract fun getSpeed ()F
+ public abstract fun getUseCompositionFrameRate ()Z
+ public abstract fun isAtEnd ()Z
+ public abstract fun isPlaying ()Z
+}
+
+public final class com/airbnb/lottie/compose/LottieAnimationState$DefaultImpls {
+ public static fun getLastFrameNanos (Lcom/airbnb/lottie/compose/LottieAnimationState;)J
+}
+
+public final class com/airbnb/lottie/compose/LottieCancellationBehavior : java/lang/Enum {
+ public static final field Immediately Lcom/airbnb/lottie/compose/LottieCancellationBehavior;
+ public static final field OnIterationFinish Lcom/airbnb/lottie/compose/LottieCancellationBehavior;
+ public static fun getEntries ()Lkotlin/enums/EnumEntries;
+ public static fun valueOf (Ljava/lang/String;)Lcom/airbnb/lottie/compose/LottieCancellationBehavior;
+ public static fun values ()[Lcom/airbnb/lottie/compose/LottieCancellationBehavior;
+}
+
+public abstract class com/airbnb/lottie/compose/LottieClipSpec {
+ public static final field $stable I
+}
+
+public final class com/airbnb/lottie/compose/LottieClipSpec$Frame : com/airbnb/lottie/compose/LottieClipSpec {
+ public static final field $stable I
+ public fun <init> ()V
+ public fun <init> (Ljava/lang/Integer;Ljava/lang/Integer;Z)V
+ public synthetic fun <init> (Ljava/lang/Integer;Ljava/lang/Integer;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V
+ public final fun component1 ()Ljava/lang/Integer;
+ public final fun component2 ()Ljava/lang/Integer;
+ public final fun component3 ()Z
+ public final fun copy (Ljava/lang/Integer;Ljava/lang/Integer;Z)Lcom/airbnb/lottie/compose/LottieClipSpec$Frame;
+ public static synthetic fun copy$default (Lcom/airbnb/lottie/compose/LottieClipSpec$Frame;Ljava/lang/Integer;Ljava/lang/Integer;ZILjava/lang/Object;)Lcom/airbnb/lottie/compose/LottieClipSpec$Frame;
+ public fun equals (Ljava/lang/Object;)Z
+ public final fun getMax ()Ljava/lang/Integer;
+ public final fun getMaxInclusive ()Z
+ public final fun getMin ()Ljava/lang/Integer;
+ public fun hashCode ()I
+ public fun toString ()Ljava/lang/String;
+}
+
+public final class com/airbnb/lottie/compose/LottieClipSpec$Marker : com/airbnb/lottie/compose/LottieClipSpec {
+ public static final field $stable I
+ public fun <init> (Ljava/lang/String;)V
+ public final fun component1 ()Ljava/lang/String;
+ public final fun copy (Ljava/lang/String;)Lcom/airbnb/lottie/compose/LottieClipSpec$Marker;
+ public static synthetic fun copy$default (Lcom/airbnb/lottie/compose/LottieClipSpec$Marker;Ljava/lang/String;ILjava/lang/Object;)Lcom/airbnb/lottie/compose/LottieClipSpec$Marker;
+ public fun equals (Ljava/lang/Object;)Z
+ public final fun getMarker ()Ljava/lang/String;
+ public fun hashCode ()I
+ public fun toString ()Ljava/lang/String;
+}
+
+public final class com/airbnb/lottie/compose/LottieClipSpec$Markers : com/airbnb/lottie/compose/LottieClipSpec {
+ public static final field $stable I
+ public fun <init> ()V
+ public fun <init> (Ljava/lang/String;Ljava/lang/String;Z)V
+ public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V
+ public final fun component1 ()Ljava/lang/String;
+ public final fun component2 ()Ljava/lang/String;
+ public final fun component3 ()Z
+ public final fun copy (Ljava/lang/String;Ljava/lang/String;Z)Lcom/airbnb/lottie/compose/LottieClipSpec$Markers;
+ public static synthetic fun copy$default (Lcom/airbnb/lottie/compose/LottieClipSpec$Markers;Ljava/lang/String;Ljava/lang/String;ZILjava/lang/Object;)Lcom/airbnb/lottie/compose/LottieClipSpec$Markers;
+ public fun equals (Ljava/lang/Object;)Z
+ public final fun getMax ()Ljava/lang/String;
+ public final fun getMaxInclusive ()Z
+ public final fun getMin ()Ljava/lang/String;
+ public fun hashCode ()I
+ public fun toString ()Ljava/lang/String;
+}
+
+public final class com/airbnb/lottie/compose/LottieClipSpec$Progress : com/airbnb/lottie/compose/LottieClipSpec {
+ public static final field $stable I
+ public fun <init> ()V
+ public fun <init> (FF)V
+ public synthetic fun <init> (FFILkotlin/jvm/internal/DefaultConstructorMarker;)V
+ public final fun component1 ()F
+ public final fun component2 ()F
+ public final fun copy (FF)Lcom/airbnb/lottie/compose/LottieClipSpec$Progress;
+ public static synthetic fun copy$default (Lcom/airbnb/lottie/compose/LottieClipSpec$Progress;FFILjava/lang/Object;)Lcom/airbnb/lottie/compose/LottieClipSpec$Progress;
+ public fun equals (Ljava/lang/Object;)Z
+ public final fun getMax ()F
+ public final fun getMin ()F
+ public fun hashCode ()I
+ public fun toString ()Ljava/lang/String;
+}
+
+public abstract interface class com/airbnb/lottie/compose/LottieCompositionResult : androidx/compose/runtime/State {
+ public abstract fun await (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
+ public abstract fun getError ()Ljava/lang/Throwable;
+ public abstract fun getValue ()Lcom/airbnb/lottie/LottieComposition;
+ public abstract fun isComplete ()Z
+ public abstract fun isFailure ()Z
+ public abstract fun isLoading ()Z
+ public abstract fun isSuccess ()Z
+}
+
+public final class com/airbnb/lottie/compose/LottieCompositionResultKt {
+ public static final fun awaitOrNull (Lcom/airbnb/lottie/compose/LottieCompositionResult;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
+}
+
+public abstract interface class com/airbnb/lottie/compose/LottieCompositionSpec {
+}
+
+public final class com/airbnb/lottie/compose/LottieCompositionSpec$Asset : com/airbnb/lottie/compose/LottieCompositionSpec {
+ public static final synthetic fun box-impl (Ljava/lang/String;)Lcom/airbnb/lottie/compose/LottieCompositionSpec$Asset;
+ public static fun constructor-impl (Ljava/lang/String;)Ljava/lang/String;
+ public fun equals (Ljava/lang/Object;)Z
+ public static fun equals-impl (Ljava/lang/String;Ljava/lang/Object;)Z
+ public static final fun equals-impl0 (Ljava/lang/String;Ljava/lang/String;)Z
+ public final fun getAssetName ()Ljava/lang/String;
+ public fun hashCode ()I
+ public static fun hashCode-impl (Ljava/lang/String;)I
+ public fun toString ()Ljava/lang/String;
+ public static fun toString-impl (Ljava/lang/String;)Ljava/lang/String;
+ public final synthetic fun unbox-impl ()Ljava/lang/String;
+}
+
+public final class com/airbnb/lottie/compose/LottieCompositionSpec$ContentProvider : com/airbnb/lottie/compose/LottieCompositionSpec {
+ public static final synthetic fun box-impl (Landroid/net/Uri;)Lcom/airbnb/lottie/compose/LottieCompositionSpec$ContentProvider;
+ public static fun constructor-impl (Landroid/net/Uri;)Landroid/net/Uri;
+ public fun equals (Ljava/lang/Object;)Z
+ public static fun equals-impl (Landroid/net/Uri;Ljava/lang/Object;)Z
+ public static final fun equals-impl0 (Landroid/net/Uri;Landroid/net/Uri;)Z
+ public final fun getUri ()Landroid/net/Uri;
+ public fun hashCode ()I
+ public static fun hashCode-impl (Landroid/net/Uri;)I
+ public fun toString ()Ljava/lang/String;
+ public static fun toString-impl (Landroid/net/Uri;)Ljava/lang/String;
+ public final synthetic fun unbox-impl ()Landroid/net/Uri;
+}
+
+public final class com/airbnb/lottie/compose/LottieCompositionSpec$File : com/airbnb/lottie/compose/LottieCompositionSpec {
+ public static final synthetic fun box-impl (Ljava/lang/String;)Lcom/airbnb/lottie/compose/LottieCompositionSpec$File;
+ public static fun constructor-impl (Ljava/lang/String;)Ljava/lang/String;
+ public fun equals (Ljava/lang/Object;)Z
+ public static fun equals-impl (Ljava/lang/String;Ljava/lang/Object;)Z
+ public static final fun equals-impl0 (Ljava/lang/String;Ljava/lang/String;)Z
+ public final fun getFileName ()Ljava/lang/String;
+ public fun hashCode ()I
+ public static fun hashCode-impl (Ljava/lang/String;)I
+ public fun toString ()Ljava/lang/String;
+ public static fun toString-impl (Ljava/lang/String;)Ljava/lang/String;
+ public final synthetic fun unbox-impl ()Ljava/lang/String;
+}
+
+public final class com/airbnb/lottie/compose/LottieCompositionSpec$JsonString : com/airbnb/lottie/compose/LottieCompositionSpec {
+ public static final synthetic fun box-impl (Ljava/lang/String;)Lcom/airbnb/lottie/compose/LottieCompositionSpec$JsonString;
+ public static fun constructor-impl (Ljava/lang/String;)Ljava/lang/String;
+ public fun equals (Ljava/lang/Object;)Z
+ public static fun equals-impl (Ljava/lang/String;Ljava/lang/Object;)Z
+ public static final fun equals-impl0 (Ljava/lang/String;Ljava/lang/String;)Z
+ public final fun getJsonString ()Ljava/lang/String;
+ public fun hashCode ()I
+ public static fun hashCode-impl (Ljava/lang/String;)I
+ public fun toString ()Ljava/lang/String;
+ public static fun toString-impl (Ljava/lang/String;)Ljava/lang/String;
+ public final synthetic fun unbox-impl ()Ljava/lang/String;
+}
+
+public final class com/airbnb/lottie/compose/LottieCompositionSpec$RawRes : com/airbnb/lottie/compose/LottieCompositionSpec {
+ public static final synthetic fun box-impl (I)Lcom/airbnb/lottie/compose/LottieCompositionSpec$RawRes;
+ public static fun constructor-impl (I)I
+ public fun equals (Ljava/lang/Object;)Z
+ public static fun equals-impl (ILjava/lang/Object;)Z
+ public static final fun equals-impl0 (II)Z
+ public final fun getResId ()I
+ public fun hashCode ()I
+ public static fun hashCode-impl (I)I
+ public fun toString ()Ljava/lang/String;
+ public static fun toString-impl (I)Ljava/lang/String;
+ public final synthetic fun unbox-impl ()I
+}
+
+public final class com/airbnb/lottie/compose/LottieCompositionSpec$Url : com/airbnb/lottie/compose/LottieCompositionSpec {
+ public static final synthetic fun box-impl (Ljava/lang/String;)Lcom/airbnb/lottie/compose/LottieCompositionSpec$Url;
+ public static fun constructor-impl (Ljava/lang/String;)Ljava/lang/String;
+ public fun equals (Ljava/lang/Object;)Z
+ public static fun equals-impl (Ljava/lang/String;Ljava/lang/Object;)Z
+ public static final fun equals-impl0 (Ljava/lang/String;Ljava/lang/String;)Z
+ public final fun getUrl ()Ljava/lang/String;
+ public fun hashCode ()I
+ public static fun hashCode-impl (Ljava/lang/String;)I
+ public fun toString ()Ljava/lang/String;
+ public static fun toString-impl (Ljava/lang/String;)Ljava/lang/String;
+ public final synthetic fun unbox-impl ()Ljava/lang/String;
+}
+
+public final class com/airbnb/lottie/compose/LottieConstants {
+ public static final field $stable I
+ public static final field INSTANCE Lcom/airbnb/lottie/compose/LottieConstants;
+ public static final field IterateForever I
+}
+
+public final class com/airbnb/lottie/compose/LottieDynamicProperties {
+ public static final field $stable I
+ public fun <init> (Ljava/util/List;)V
+}
+
+public final class com/airbnb/lottie/compose/LottieDynamicPropertiesKt {
+ public static final fun rememberLottieDynamicProperties ([Lcom/airbnb/lottie/compose/LottieDynamicProperty;Landroidx/compose/runtime/Composer;I)Lcom/airbnb/lottie/compose/LottieDynamicProperties;
+ public static final fun rememberLottieDynamicProperty (Ljava/lang/Object;Ljava/lang/Object;[Ljava/lang/String;Landroidx/compose/runtime/Composer;I)Lcom/airbnb/lottie/compose/LottieDynamicProperty;
+ public static final fun rememberLottieDynamicProperty (Ljava/lang/Object;[Ljava/lang/String;Lkotlin/jvm/functions/Function1;Landroidx/compose/runtime/Composer;I)Lcom/airbnb/lottie/compose/LottieDynamicProperty;
+}
+
+public final class com/airbnb/lottie/compose/LottieDynamicProperty {
+ public static final field $stable I
+ public fun <init> (Ljava/lang/Object;Lcom/airbnb/lottie/model/KeyPath;Ljava/lang/Object;)V
+}
+
+public final class com/airbnb/lottie/compose/LottiePainter : androidx/compose/ui/graphics/painter/Painter {
+ public static final field $stable I
+ public fun <init> ()V
+ public fun getIntrinsicSize-NH-jbRc ()J
+}
+
+public final class com/airbnb/lottie/compose/LottiePainterKt {
+ public static final fun rememberLottiePainter (Lcom/airbnb/lottie/LottieComposition;FZZZLcom/airbnb/lottie/RenderMode;ZLcom/airbnb/lottie/compose/LottieDynamicProperties;ZZLjava/util/Map;Lcom/airbnb/lottie/AsyncUpdates;Landroidx/compose/runtime/Composer;III)Lcom/airbnb/lottie/compose/LottiePainter;
+}
+
+public final class com/airbnb/lottie/compose/LottieRetrySignal {
+ public static final field $stable I
+ public final fun awaitRetry (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
+ public final fun isAwaitingRetry ()Z
+ public final fun retry ()V
+}
+
+public final class com/airbnb/lottie/compose/LottieRetrySignalKt {
+ public static final fun rememberLottieRetrySignal (Landroidx/compose/runtime/Composer;I)Lcom/airbnb/lottie/compose/LottieRetrySignal;
+}
+
+public final class com/airbnb/lottie/compose/RememberLottieCompositionKt {
+ public static final fun rememberLottieComposition (Lcom/airbnb/lottie/compose/LottieCompositionSpec;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function3;Landroidx/compose/runtime/Composer;II)Lcom/airbnb/lottie/compose/LottieCompositionResult;
+}
+
diff --git a/lottie-compose/build.gradle b/lottie-compose/build.gradle
index 21f3d2d5..69c7973b 100644
--- a/lottie-compose/build.gradle
+++ b/lottie-compose/build.gradle
@@ -1,14 +1,20 @@
+import com.vanniktech.maven.publish.SonatypeHost
+import static de.fayard.refreshVersions.core.Versions.versionFor
+
plugins {
id 'com.android.library'
- id 'kotlin-android'
+ id 'org.jetbrains.kotlin.android'
id 'com.vanniktech.maven.publish'
+ id 'androidx.baselineprofile'
+ id 'org.jetbrains.kotlinx.binary-compatibility-validator'
}
android {
- compileSdk 31
+ namespace 'com.airbnb.lottie.compose'
+ compileSdk 34
defaultConfig {
minSdk 21
- targetSdk 30
+ targetSdk 34
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
@@ -16,35 +22,42 @@ android {
minifyEnabled false
}
}
- compileOptions {
- sourceCompatibility JavaVersion.VERSION_1_8
- targetCompatibility JavaVersion.VERSION_1_8
- }
kotlinOptions {
- jvmTarget = JavaVersion.VERSION_1_8.toString()
freeCompilerArgs += [
- "-Xallow-jvm-ir-dependencies",
- "-Xskip-prerelease-check",
- "-Xuse-experimental=kotlinx.coroutines.ExperimentalCoroutinesApi",
+ "-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi",
]
}
buildFeatures {
compose true
}
composeOptions {
- kotlinCompilerExtensionVersion composeVersion
+ kotlinCompilerExtensionVersion = versionFor(project, AndroidX.compose.compiler)
+ }
+}
+
+mavenPublishing {
+ publishToMavenCentral(SonatypeHost.DEFAULT)
+ signAllPublications()
+}
+
+baselineProfile {
+ filter {
+ include 'com.airbnb.lottie.compose.**'
}
}
dependencies {
api project(':lottie')
- implementation "androidx.compose.foundation:foundation:$composeVersion"
- implementation "androidx.compose.ui:ui:$composeVersion"
+ implementation platform(libs.compose.bom)
+ implementation libs.compose.foundation
+ implementation libs.compose.ui
- testImplementation "org.robolectric:robolectric:$robolectricVersion"
- testImplementation 'androidx.collection:collection-ktx:1.1.0'
- testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutinesVersion"
- testImplementation "junit:junit:$junitVersion"
- androidTestImplementation "androidx.test.ext:junit:$extJunitVersion"
- androidTestImplementation "androidx.test.espresso:espresso-core:$espressoVersion"
-} \ No newline at end of file
+ baselineProfile project(':baselineprofile')
+
+ testImplementation libs.robolectric
+ testImplementation libs.androidx.collection.ktx
+ testImplementation libs.kotlinx.coroutines.test
+ testImplementation libs.junit4
+ androidTestImplementation libs.androidx.test.junit
+ androidTestImplementation libs.androidx.test.espresso
+}
diff --git a/lottie-compose/src/main/AndroidManifest.xml b/lottie-compose/src/main/AndroidManifest.xml
deleted file mode 100644
index aca44676..00000000
--- a/lottie-compose/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,2 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<manifest package="com.airbnb.lottie.compose" /> \ No newline at end of file
diff --git a/lottie-compose/src/main/generated/baselineProfiles/baseline-prof.txt b/lottie-compose/src/main/generated/baselineProfiles/baseline-prof.txt
new file mode 100644
index 00000000..3948d284
--- /dev/null
+++ b/lottie-compose/src/main/generated/baselineProfiles/baseline-prof.txt
@@ -0,0 +1,176 @@
+Lcom/airbnb/lottie/compose/AnimateLottieCompositionAsStateKt;
+HSPLcom/airbnb/lottie/compose/AnimateLottieCompositionAsStateKt;->access$animateLottieCompositionAsState$lambda$3(Landroidx/compose/runtime/MutableState;)Z
+HSPLcom/airbnb/lottie/compose/AnimateLottieCompositionAsStateKt;->access$animateLottieCompositionAsState$lambda$4(Landroidx/compose/runtime/MutableState;Z)V
+HSPLcom/airbnb/lottie/compose/AnimateLottieCompositionAsStateKt;->animateLottieCompositionAsState$lambda$3(Landroidx/compose/runtime/MutableState;)Z
+HSPLcom/airbnb/lottie/compose/AnimateLottieCompositionAsStateKt;->animateLottieCompositionAsState$lambda$4(Landroidx/compose/runtime/MutableState;Z)V
+HSPLcom/airbnb/lottie/compose/AnimateLottieCompositionAsStateKt;->animateLottieCompositionAsState(Lcom/airbnb/lottie/LottieComposition;ZZZLcom/airbnb/lottie/compose/LottieClipSpec;FILcom/airbnb/lottie/compose/LottieCancellationBehavior;ZZLandroidx/compose/runtime/Composer;II)Lcom/airbnb/lottie/compose/LottieAnimationState;
+Lcom/airbnb/lottie/compose/AnimateLottieCompositionAsStateKt$animateLottieCompositionAsState$3;
+HSPLcom/airbnb/lottie/compose/AnimateLottieCompositionAsStateKt$animateLottieCompositionAsState$3;-><init>(ZZLcom/airbnb/lottie/compose/LottieAnimatable;Lcom/airbnb/lottie/LottieComposition;IZFLcom/airbnb/lottie/compose/LottieClipSpec;Lcom/airbnb/lottie/compose/LottieCancellationBehavior;ZLandroidx/compose/runtime/MutableState;Lkotlin/coroutines/Continuation;)V
+HSPLcom/airbnb/lottie/compose/AnimateLottieCompositionAsStateKt$animateLottieCompositionAsState$3;->create(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;
+HSPLcom/airbnb/lottie/compose/AnimateLottieCompositionAsStateKt$animateLottieCompositionAsState$3;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
+Lcom/airbnb/lottie/compose/LottieAnimatable;
+Lcom/airbnb/lottie/compose/LottieAnimatable$DefaultImpls;
+HSPLcom/airbnb/lottie/compose/LottieAnimatable$DefaultImpls;->animate$default(Lcom/airbnb/lottie/compose/LottieAnimatable;Lcom/airbnb/lottie/LottieComposition;IIZFLcom/airbnb/lottie/compose/LottieClipSpec;FZLcom/airbnb/lottie/compose/LottieCancellationBehavior;ZZLkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object;
+Lcom/airbnb/lottie/compose/LottieAnimatableImpl;
+HSPLcom/airbnb/lottie/compose/LottieAnimatableImpl;-><init>()V
+HSPLcom/airbnb/lottie/compose/LottieAnimatableImpl;->access$doFrame(Lcom/airbnb/lottie/compose/LottieAnimatableImpl;ILkotlin/coroutines/Continuation;)Ljava/lang/Object;
+HSPLcom/airbnb/lottie/compose/LottieAnimatableImpl;->access$onFrame(Lcom/airbnb/lottie/compose/LottieAnimatableImpl;IJ)Z
+HSPLcom/airbnb/lottie/compose/LottieAnimatableImpl;->access$setClipSpec(Lcom/airbnb/lottie/compose/LottieAnimatableImpl;Lcom/airbnb/lottie/compose/LottieClipSpec;)V
+HSPLcom/airbnb/lottie/compose/LottieAnimatableImpl;->access$setComposition(Lcom/airbnb/lottie/compose/LottieAnimatableImpl;Lcom/airbnb/lottie/LottieComposition;)V
+HSPLcom/airbnb/lottie/compose/LottieAnimatableImpl;->access$setIteration(Lcom/airbnb/lottie/compose/LottieAnimatableImpl;I)V
+HSPLcom/airbnb/lottie/compose/LottieAnimatableImpl;->access$setIterations(Lcom/airbnb/lottie/compose/LottieAnimatableImpl;I)V
+HSPLcom/airbnb/lottie/compose/LottieAnimatableImpl;->access$setLastFrameNanos(Lcom/airbnb/lottie/compose/LottieAnimatableImpl;J)V
+HSPLcom/airbnb/lottie/compose/LottieAnimatableImpl;->access$setPlaying(Lcom/airbnb/lottie/compose/LottieAnimatableImpl;Z)V
+HSPLcom/airbnb/lottie/compose/LottieAnimatableImpl;->access$setReverseOnRepeat(Lcom/airbnb/lottie/compose/LottieAnimatableImpl;Z)V
+HSPLcom/airbnb/lottie/compose/LottieAnimatableImpl;->access$setSpeed(Lcom/airbnb/lottie/compose/LottieAnimatableImpl;F)V
+HSPLcom/airbnb/lottie/compose/LottieAnimatableImpl;->access$setUseCompositionFrameRate(Lcom/airbnb/lottie/compose/LottieAnimatableImpl;Z)V
+HSPLcom/airbnb/lottie/compose/LottieAnimatableImpl;->access$updateProgress(Lcom/airbnb/lottie/compose/LottieAnimatableImpl;F)V
+HSPLcom/airbnb/lottie/compose/LottieAnimatableImpl;->animate(Lcom/airbnb/lottie/LottieComposition;IIZFLcom/airbnb/lottie/compose/LottieClipSpec;FZLcom/airbnb/lottie/compose/LottieCancellationBehavior;ZZLkotlin/coroutines/Continuation;)Ljava/lang/Object;
+HPLcom/airbnb/lottie/compose/LottieAnimatableImpl;->doFrame(ILkotlin/coroutines/Continuation;)Ljava/lang/Object;
+HPLcom/airbnb/lottie/compose/LottieAnimatableImpl;->getClipSpec()Lcom/airbnb/lottie/compose/LottieClipSpec;
+HPLcom/airbnb/lottie/compose/LottieAnimatableImpl;->getComposition()Lcom/airbnb/lottie/LottieComposition;
+HPLcom/airbnb/lottie/compose/LottieAnimatableImpl;->getFrameSpeed()F
+HSPLcom/airbnb/lottie/compose/LottieAnimatableImpl;->getIteration()I
+HPLcom/airbnb/lottie/compose/LottieAnimatableImpl;->getLastFrameNanos()J
+HPLcom/airbnb/lottie/compose/LottieAnimatableImpl;->getProgress()F
+HPLcom/airbnb/lottie/compose/LottieAnimatableImpl;->getProgressRaw()F
+HSPLcom/airbnb/lottie/compose/LottieAnimatableImpl;->getReverseOnRepeat()Z
+HSPLcom/airbnb/lottie/compose/LottieAnimatableImpl;->getSpeed()F
+HPLcom/airbnb/lottie/compose/LottieAnimatableImpl;->getUseCompositionFrameRate()Z
+HPLcom/airbnb/lottie/compose/LottieAnimatableImpl;->getValue()Ljava/lang/Float;
+HPLcom/airbnb/lottie/compose/LottieAnimatableImpl;->getValue()Ljava/lang/Object;
+HPLcom/airbnb/lottie/compose/LottieAnimatableImpl;->onFrame(IJ)Z
+HSPLcom/airbnb/lottie/compose/LottieAnimatableImpl;->setClipSpec(Lcom/airbnb/lottie/compose/LottieClipSpec;)V
+HSPLcom/airbnb/lottie/compose/LottieAnimatableImpl;->setComposition(Lcom/airbnb/lottie/LottieComposition;)V
+HSPLcom/airbnb/lottie/compose/LottieAnimatableImpl;->setIteration(I)V
+HSPLcom/airbnb/lottie/compose/LottieAnimatableImpl;->setIterations(I)V
+HPLcom/airbnb/lottie/compose/LottieAnimatableImpl;->setLastFrameNanos(J)V
+HSPLcom/airbnb/lottie/compose/LottieAnimatableImpl;->setPlaying(Z)V
+HPLcom/airbnb/lottie/compose/LottieAnimatableImpl;->setProgress(F)V
+HPLcom/airbnb/lottie/compose/LottieAnimatableImpl;->setProgressRaw(F)V
+HSPLcom/airbnb/lottie/compose/LottieAnimatableImpl;->setReverseOnRepeat(Z)V
+HSPLcom/airbnb/lottie/compose/LottieAnimatableImpl;->setSpeed(F)V
+HSPLcom/airbnb/lottie/compose/LottieAnimatableImpl;->setUseCompositionFrameRate(Z)V
+HPLcom/airbnb/lottie/compose/LottieAnimatableImpl;->updateProgress(F)V
+Lcom/airbnb/lottie/compose/LottieAnimatableImpl$animate$2;
+HSPLcom/airbnb/lottie/compose/LottieAnimatableImpl$animate$2;-><init>(Lcom/airbnb/lottie/compose/LottieAnimatableImpl;IIZFLcom/airbnb/lottie/compose/LottieClipSpec;Lcom/airbnb/lottie/LottieComposition;FZZLcom/airbnb/lottie/compose/LottieCancellationBehavior;Lkotlin/coroutines/Continuation;)V
+HSPLcom/airbnb/lottie/compose/LottieAnimatableImpl$animate$2;->create(Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;
+HSPLcom/airbnb/lottie/compose/LottieAnimatableImpl$animate$2;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
+HSPLcom/airbnb/lottie/compose/LottieAnimatableImpl$animate$2;->invoke(Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
+HSPLcom/airbnb/lottie/compose/LottieAnimatableImpl$animate$2;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
+Lcom/airbnb/lottie/compose/LottieAnimatableImpl$animate$2$1;
+HSPLcom/airbnb/lottie/compose/LottieAnimatableImpl$animate$2$1;-><init>(Lcom/airbnb/lottie/compose/LottieCancellationBehavior;Lkotlinx/coroutines/Job;IILcom/airbnb/lottie/compose/LottieAnimatableImpl;Lkotlin/coroutines/Continuation;)V
+HSPLcom/airbnb/lottie/compose/LottieAnimatableImpl$animate$2$1;->create(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;
+HSPLcom/airbnb/lottie/compose/LottieAnimatableImpl$animate$2$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
+HSPLcom/airbnb/lottie/compose/LottieAnimatableImpl$animate$2$1;->invoke(Lkotlinx/coroutines/CoroutineScope;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
+HPLcom/airbnb/lottie/compose/LottieAnimatableImpl$animate$2$1;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
+Lcom/airbnb/lottie/compose/LottieAnimatableImpl$animate$2$1$WhenMappings;
+HSPLcom/airbnb/lottie/compose/LottieAnimatableImpl$animate$2$1$WhenMappings;-><clinit>()V
+Lcom/airbnb/lottie/compose/LottieAnimatableImpl$animate$2$WhenMappings;
+HSPLcom/airbnb/lottie/compose/LottieAnimatableImpl$animate$2$WhenMappings;-><clinit>()V
+Lcom/airbnb/lottie/compose/LottieAnimatableImpl$doFrame$2;
+HPLcom/airbnb/lottie/compose/LottieAnimatableImpl$doFrame$2;-><init>(Lcom/airbnb/lottie/compose/LottieAnimatableImpl;I)V
+HPLcom/airbnb/lottie/compose/LottieAnimatableImpl$doFrame$2;->invoke(J)Ljava/lang/Boolean;
+HPLcom/airbnb/lottie/compose/LottieAnimatableImpl$doFrame$2;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
+Lcom/airbnb/lottie/compose/LottieAnimatableImpl$endProgress$2;
+HSPLcom/airbnb/lottie/compose/LottieAnimatableImpl$endProgress$2;-><init>(Lcom/airbnb/lottie/compose/LottieAnimatableImpl;)V
+Lcom/airbnb/lottie/compose/LottieAnimatableImpl$frameSpeed$2;
+HSPLcom/airbnb/lottie/compose/LottieAnimatableImpl$frameSpeed$2;-><init>(Lcom/airbnb/lottie/compose/LottieAnimatableImpl;)V
+HSPLcom/airbnb/lottie/compose/LottieAnimatableImpl$frameSpeed$2;->invoke()Ljava/lang/Float;
+HSPLcom/airbnb/lottie/compose/LottieAnimatableImpl$frameSpeed$2;->invoke()Ljava/lang/Object;
+Lcom/airbnb/lottie/compose/LottieAnimatableImpl$isAtEnd$2;
+HSPLcom/airbnb/lottie/compose/LottieAnimatableImpl$isAtEnd$2;-><init>(Lcom/airbnb/lottie/compose/LottieAnimatableImpl;)V
+Lcom/airbnb/lottie/compose/LottieAnimatableKt;
+HSPLcom/airbnb/lottie/compose/LottieAnimatableKt;->LottieAnimatable()Lcom/airbnb/lottie/compose/LottieAnimatable;
+HSPLcom/airbnb/lottie/compose/LottieAnimatableKt;->rememberLottieAnimatable(Landroidx/compose/runtime/Composer;I)Lcom/airbnb/lottie/compose/LottieAnimatable;
+Lcom/airbnb/lottie/compose/LottieAnimationKt;
+HPLcom/airbnb/lottie/compose/LottieAnimationKt;->LottieAnimation$lambda$3(Landroidx/compose/runtime/MutableState;)Lcom/airbnb/lottie/compose/LottieDynamicProperties;
+HSPLcom/airbnb/lottie/compose/LottieAnimationKt;->LottieAnimation(Lcom/airbnb/lottie/LottieComposition;Lkotlin/jvm/functions/Function0;Landroidx/compose/ui/Modifier;ZZZLcom/airbnb/lottie/RenderMode;ZLcom/airbnb/lottie/compose/LottieDynamicProperties;Landroidx/compose/ui/Alignment;Landroidx/compose/ui/layout/ContentScale;ZLjava/util/Map;Lcom/airbnb/lottie/AsyncUpdates;Landroidx/compose/runtime/Composer;III)V
+HSPLcom/airbnb/lottie/compose/LottieAnimationKt;->access$LottieAnimation$lambda$3(Landroidx/compose/runtime/MutableState;)Lcom/airbnb/lottie/compose/LottieDynamicProperties;
+HPLcom/airbnb/lottie/compose/LottieAnimationKt;->access$times-UQTWf7w(JJ)J
+HPLcom/airbnb/lottie/compose/LottieAnimationKt;->times-UQTWf7w(JJ)J
+Lcom/airbnb/lottie/compose/LottieAnimationKt$LottieAnimation$2;
+HSPLcom/airbnb/lottie/compose/LottieAnimationKt$LottieAnimation$2;-><init>(Lcom/airbnb/lottie/LottieComposition;Landroidx/compose/ui/layout/ContentScale;Landroidx/compose/ui/Alignment;Landroid/graphics/Matrix;Lcom/airbnb/lottie/LottieDrawable;ZLcom/airbnb/lottie/RenderMode;Lcom/airbnb/lottie/AsyncUpdates;Ljava/util/Map;Lcom/airbnb/lottie/compose/LottieDynamicProperties;ZZZZLkotlin/jvm/functions/Function0;Landroidx/compose/runtime/MutableState;)V
+HPLcom/airbnb/lottie/compose/LottieAnimationKt$LottieAnimation$2;->invoke(Landroidx/compose/ui/graphics/drawscope/DrawScope;)V
+HPLcom/airbnb/lottie/compose/LottieAnimationKt$LottieAnimation$2;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
+Lcom/airbnb/lottie/compose/LottieAnimationState;
+Lcom/airbnb/lottie/compose/LottieCancellationBehavior;
+HSPLcom/airbnb/lottie/compose/LottieCancellationBehavior;->$values()[Lcom/airbnb/lottie/compose/LottieCancellationBehavior;
+HSPLcom/airbnb/lottie/compose/LottieCancellationBehavior;-><clinit>()V
+HSPLcom/airbnb/lottie/compose/LottieCancellationBehavior;-><init>(Ljava/lang/String;I)V
+HSPLcom/airbnb/lottie/compose/LottieCancellationBehavior;->values()[Lcom/airbnb/lottie/compose/LottieCancellationBehavior;
+Lcom/airbnb/lottie/compose/LottieClipSpec;
+Lcom/airbnb/lottie/compose/LottieCompositionResult;
+Lcom/airbnb/lottie/compose/LottieCompositionResultImpl;
+HSPLcom/airbnb/lottie/compose/LottieCompositionResultImpl;-><init>()V
+HSPLcom/airbnb/lottie/compose/LottieCompositionResultImpl;->complete$lottie_compose_release(Lcom/airbnb/lottie/LottieComposition;)V
+HSPLcom/airbnb/lottie/compose/LottieCompositionResultImpl;->getError()Ljava/lang/Throwable;
+HSPLcom/airbnb/lottie/compose/LottieCompositionResultImpl;->getValue()Lcom/airbnb/lottie/LottieComposition;
+HSPLcom/airbnb/lottie/compose/LottieCompositionResultImpl;->getValue()Ljava/lang/Object;
+HSPLcom/airbnb/lottie/compose/LottieCompositionResultImpl;->isComplete()Z
+HSPLcom/airbnb/lottie/compose/LottieCompositionResultImpl;->isSuccess()Z
+HSPLcom/airbnb/lottie/compose/LottieCompositionResultImpl;->setValue(Lcom/airbnb/lottie/LottieComposition;)V
+Lcom/airbnb/lottie/compose/LottieCompositionResultImpl$isComplete$2;
+HSPLcom/airbnb/lottie/compose/LottieCompositionResultImpl$isComplete$2;-><init>(Lcom/airbnb/lottie/compose/LottieCompositionResultImpl;)V
+HSPLcom/airbnb/lottie/compose/LottieCompositionResultImpl$isComplete$2;->invoke()Ljava/lang/Boolean;
+HSPLcom/airbnb/lottie/compose/LottieCompositionResultImpl$isComplete$2;->invoke()Ljava/lang/Object;
+Lcom/airbnb/lottie/compose/LottieCompositionResultImpl$isFailure$2;
+HSPLcom/airbnb/lottie/compose/LottieCompositionResultImpl$isFailure$2;-><init>(Lcom/airbnb/lottie/compose/LottieCompositionResultImpl;)V
+Lcom/airbnb/lottie/compose/LottieCompositionResultImpl$isLoading$2;
+HSPLcom/airbnb/lottie/compose/LottieCompositionResultImpl$isLoading$2;-><init>(Lcom/airbnb/lottie/compose/LottieCompositionResultImpl;)V
+Lcom/airbnb/lottie/compose/LottieCompositionResultImpl$isSuccess$2;
+HSPLcom/airbnb/lottie/compose/LottieCompositionResultImpl$isSuccess$2;-><init>(Lcom/airbnb/lottie/compose/LottieCompositionResultImpl;)V
+HSPLcom/airbnb/lottie/compose/LottieCompositionResultImpl$isSuccess$2;->invoke()Ljava/lang/Boolean;
+HSPLcom/airbnb/lottie/compose/LottieCompositionResultImpl$isSuccess$2;->invoke()Ljava/lang/Object;
+Lcom/airbnb/lottie/compose/LottieCompositionSpec;
+Lcom/airbnb/lottie/compose/LottieCompositionSpec$RawRes;
+HSPLcom/airbnb/lottie/compose/LottieCompositionSpec$RawRes;-><init>(I)V
+HSPLcom/airbnb/lottie/compose/LottieCompositionSpec$RawRes;->box-impl(I)Lcom/airbnb/lottie/compose/LottieCompositionSpec$RawRes;
+HSPLcom/airbnb/lottie/compose/LottieCompositionSpec$RawRes;->constructor-impl(I)I
+HSPLcom/airbnb/lottie/compose/LottieCompositionSpec$RawRes;->equals(Ljava/lang/Object;)Z
+HSPLcom/airbnb/lottie/compose/LottieCompositionSpec$RawRes;->equals-impl(ILjava/lang/Object;)Z
+HSPLcom/airbnb/lottie/compose/LottieCompositionSpec$RawRes;->unbox-impl()I
+Lcom/airbnb/lottie/compose/LottieDynamicProperties;
+Lcom/airbnb/lottie/compose/RememberLottieCompositionKt;
+HSPLcom/airbnb/lottie/compose/RememberLottieCompositionKt;->access$ensureLeadingPeriod(Ljava/lang/String;)Ljava/lang/String;
+HSPLcom/airbnb/lottie/compose/RememberLottieCompositionKt;->access$ensureTrailingSlash(Ljava/lang/String;)Ljava/lang/String;
+HSPLcom/airbnb/lottie/compose/RememberLottieCompositionKt;->access$lottieComposition(Landroid/content/Context;Lcom/airbnb/lottie/compose/LottieCompositionSpec;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
+HSPLcom/airbnb/lottie/compose/RememberLottieCompositionKt;->access$lottieTask(Landroid/content/Context;Lcom/airbnb/lottie/compose/LottieCompositionSpec;Ljava/lang/String;Z)Lcom/airbnb/lottie/LottieTask;
+HSPLcom/airbnb/lottie/compose/RememberLottieCompositionKt;->access$maybeDecodeBase64Image(Lcom/airbnb/lottie/LottieImageAsset;)V
+HSPLcom/airbnb/lottie/compose/RememberLottieCompositionKt;->access$maybeLoadImageFromAsset(Landroid/content/Context;Lcom/airbnb/lottie/LottieImageAsset;Ljava/lang/String;)V
+HSPLcom/airbnb/lottie/compose/RememberLottieCompositionKt;->access$maybeLoadTypefaceFromAssets(Landroid/content/Context;Lcom/airbnb/lottie/model/Font;Ljava/lang/String;Ljava/lang/String;)V
+HSPLcom/airbnb/lottie/compose/RememberLottieCompositionKt;->access$rememberLottieComposition$lambda$1(Landroidx/compose/runtime/MutableState;)Lcom/airbnb/lottie/compose/LottieCompositionResultImpl;
+HSPLcom/airbnb/lottie/compose/RememberLottieCompositionKt;->await(Lcom/airbnb/lottie/LottieTask;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
+HSPLcom/airbnb/lottie/compose/RememberLottieCompositionKt;->ensureLeadingPeriod(Ljava/lang/String;)Ljava/lang/String;
+HSPLcom/airbnb/lottie/compose/RememberLottieCompositionKt;->ensureTrailingSlash(Ljava/lang/String;)Ljava/lang/String;
+HSPLcom/airbnb/lottie/compose/RememberLottieCompositionKt;->loadFontsFromAssets(Landroid/content/Context;Lcom/airbnb/lottie/LottieComposition;Ljava/lang/String;Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
+HSPLcom/airbnb/lottie/compose/RememberLottieCompositionKt;->loadImagesFromAssets(Landroid/content/Context;Lcom/airbnb/lottie/LottieComposition;Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
+HSPLcom/airbnb/lottie/compose/RememberLottieCompositionKt;->lottieComposition(Landroid/content/Context;Lcom/airbnb/lottie/compose/LottieCompositionSpec;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
+HSPLcom/airbnb/lottie/compose/RememberLottieCompositionKt;->lottieTask(Landroid/content/Context;Lcom/airbnb/lottie/compose/LottieCompositionSpec;Ljava/lang/String;Z)Lcom/airbnb/lottie/LottieTask;
+HSPLcom/airbnb/lottie/compose/RememberLottieCompositionKt;->maybeDecodeBase64Image(Lcom/airbnb/lottie/LottieImageAsset;)V
+HSPLcom/airbnb/lottie/compose/RememberLottieCompositionKt;->maybeLoadImageFromAsset(Landroid/content/Context;Lcom/airbnb/lottie/LottieImageAsset;Ljava/lang/String;)V
+HSPLcom/airbnb/lottie/compose/RememberLottieCompositionKt;->maybeLoadTypefaceFromAssets(Landroid/content/Context;Lcom/airbnb/lottie/model/Font;Ljava/lang/String;Ljava/lang/String;)V
+HSPLcom/airbnb/lottie/compose/RememberLottieCompositionKt;->rememberLottieComposition$lambda$1(Landroidx/compose/runtime/MutableState;)Lcom/airbnb/lottie/compose/LottieCompositionResultImpl;
+HSPLcom/airbnb/lottie/compose/RememberLottieCompositionKt;->rememberLottieComposition(Lcom/airbnb/lottie/compose/LottieCompositionSpec;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function3;Landroidx/compose/runtime/Composer;II)Lcom/airbnb/lottie/compose/LottieCompositionResult;
+Lcom/airbnb/lottie/compose/RememberLottieCompositionKt$await$2$1;
+HSPLcom/airbnb/lottie/compose/RememberLottieCompositionKt$await$2$1;-><init>(Lkotlinx/coroutines/CancellableContinuation;)V
+HSPLcom/airbnb/lottie/compose/RememberLottieCompositionKt$await$2$1;->onResult(Ljava/lang/Object;)V
+Lcom/airbnb/lottie/compose/RememberLottieCompositionKt$await$2$2;
+HSPLcom/airbnb/lottie/compose/RememberLottieCompositionKt$await$2$2;-><init>(Lkotlinx/coroutines/CancellableContinuation;)V
+Lcom/airbnb/lottie/compose/RememberLottieCompositionKt$loadFontsFromAssets$2;
+HSPLcom/airbnb/lottie/compose/RememberLottieCompositionKt$loadFontsFromAssets$2;-><init>(Lcom/airbnb/lottie/LottieComposition;Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;Lkotlin/coroutines/Continuation;)V
+HSPLcom/airbnb/lottie/compose/RememberLottieCompositionKt$loadFontsFromAssets$2;->create(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;
+HSPLcom/airbnb/lottie/compose/RememberLottieCompositionKt$loadFontsFromAssets$2;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
+Lcom/airbnb/lottie/compose/RememberLottieCompositionKt$loadImagesFromAssets$2;
+HSPLcom/airbnb/lottie/compose/RememberLottieCompositionKt$loadImagesFromAssets$2;-><init>(Lcom/airbnb/lottie/LottieComposition;Landroid/content/Context;Ljava/lang/String;Lkotlin/coroutines/Continuation;)V
+HSPLcom/airbnb/lottie/compose/RememberLottieCompositionKt$loadImagesFromAssets$2;->create(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;
+HSPLcom/airbnb/lottie/compose/RememberLottieCompositionKt$loadImagesFromAssets$2;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
+Lcom/airbnb/lottie/compose/RememberLottieCompositionKt$lottieComposition$1;
+HSPLcom/airbnb/lottie/compose/RememberLottieCompositionKt$lottieComposition$1;-><init>(Lkotlin/coroutines/Continuation;)V
+HSPLcom/airbnb/lottie/compose/RememberLottieCompositionKt$lottieComposition$1;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
+Lcom/airbnb/lottie/compose/RememberLottieCompositionKt$rememberLottieComposition$1;
+HSPLcom/airbnb/lottie/compose/RememberLottieCompositionKt$rememberLottieComposition$1;-><init>(Lkotlin/coroutines/Continuation;)V
+Lcom/airbnb/lottie/compose/RememberLottieCompositionKt$rememberLottieComposition$3;
+HSPLcom/airbnb/lottie/compose/RememberLottieCompositionKt$rememberLottieComposition$3;-><init>(Lkotlin/jvm/functions/Function3;Landroid/content/Context;Lcom/airbnb/lottie/compose/LottieCompositionSpec;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Landroidx/compose/runtime/MutableState;Lkotlin/coroutines/Continuation;)V
+HSPLcom/airbnb/lottie/compose/RememberLottieCompositionKt$rememberLottieComposition$3;->create(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;
+HSPLcom/airbnb/lottie/compose/RememberLottieCompositionKt$rememberLottieComposition$3;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object; \ No newline at end of file
diff --git a/lottie-compose/src/main/java/com/airbnb/lottie/compose/LottieAnimatable.kt b/lottie-compose/src/main/java/com/airbnb/lottie/compose/LottieAnimatable.kt
index 6403bca5..262bd983 100644
--- a/lottie-compose/src/main/java/com/airbnb/lottie/compose/LottieAnimatable.kt
+++ b/lottie-compose/src/main/java/com/airbnb/lottie/compose/LottieAnimatable.kt
@@ -127,19 +127,28 @@ interface LottieAnimatable : LottieAnimationState {
* you will want it to cancel immediately. However, if you have a state based
* transition and you want an animation to finish playing before moving on to
* the next one then you may want to set this to [LottieCancellationBehavior.OnIterationFinish].
- * @param ignoreSystemAnimationsDisabled When set to true, the animation will animate even if animations are disabled at the OS level.
+ * @param ignoreSystemAnimationsDisabled When set to true, the animation will animate even if animations
+ * are disabled at the OS level.
* Defaults to false.
+ * @param useCompositionFrameRate Lottie files can specify a target frame rate. By default, Lottie ignores it
+ * and re-renders on every frame. If that behavior is undesirable, you can set
+ * this to true to use the composition frame rate instead.
+ * Note: composition frame rates are usually lower than display frame rates
+ * so this will likely make your animation feel janky. However, it may be desirable
+ * for specific situations such as pixel art that are intended to have low frame rates.
*/
suspend fun animate(
composition: LottieComposition?,
iteration: Int = this.iteration,
iterations: Int = this.iterations,
+ reverseOnRepeat: Boolean = this.reverseOnRepeat,
speed: Float = this.speed,
clipSpec: LottieClipSpec? = this.clipSpec,
initialProgress: Float = defaultProgress(composition, clipSpec, speed),
continueFromPreviousAnimate: Boolean = false,
cancellationBehavior: LottieCancellationBehavior = LottieCancellationBehavior.Immediately,
ignoreSystemAnimationsDisabled: Boolean = false,
+ useCompositionFrameRate: Boolean = false,
)
}
@@ -148,9 +157,6 @@ private class LottieAnimatableImpl : LottieAnimatable {
override var isPlaying: Boolean by mutableStateOf(false)
private set
- override var progress: Float by mutableStateOf(0f)
- private set
-
override val value: Float
get() = progress
@@ -160,15 +166,33 @@ private class LottieAnimatableImpl : LottieAnimatable {
override var iterations: Int by mutableStateOf(1)
private set
+ override var reverseOnRepeat: Boolean by mutableStateOf(false)
+ private set
+
override var clipSpec: LottieClipSpec? by mutableStateOf(null)
private set
override var speed: Float by mutableStateOf(1f)
private set
+ override var useCompositionFrameRate: Boolean by mutableStateOf(false)
+ private set
+
+ /**
+ * Inverse speed value is used to play the animation in reverse when [reverseOnRepeat] is true.
+ */
+ private val frameSpeed: Float by derivedStateOf {
+ if (reverseOnRepeat && iteration % 2 == 0) -speed else speed
+ }
+
override var composition: LottieComposition? by mutableStateOf(null)
private set
+ private var progressRaw: Float by mutableStateOf(0f)
+
+ override var progress: Float by mutableStateOf(0f)
+ private set
+
override var lastFrameNanos: Long by mutableStateOf(AnimationConstants.UnspecifiedTime)
private set
@@ -193,7 +217,7 @@ private class LottieAnimatableImpl : LottieAnimatable {
) {
mutex.mutate {
this.composition = composition
- this.progress = progress
+ updateProgress(progress)
this.iteration = iteration
isPlaying = false
if (resetLastFrameNanos) {
@@ -206,26 +230,30 @@ private class LottieAnimatableImpl : LottieAnimatable {
composition: LottieComposition?,
iteration: Int,
iterations: Int,
+ reverseOnRepeat: Boolean,
speed: Float,
clipSpec: LottieClipSpec?,
initialProgress: Float,
continueFromPreviousAnimate: Boolean,
cancellationBehavior: LottieCancellationBehavior,
ignoreSystemAnimationsDisabled: Boolean,
+ useCompositionFrameRate: Boolean,
) {
mutex.mutate {
this.iteration = iteration
this.iterations = iterations
+ this.reverseOnRepeat = reverseOnRepeat
this.speed = speed
this.clipSpec = clipSpec
this.composition = composition
- this.progress = initialProgress
+ updateProgress(initialProgress)
+ this.useCompositionFrameRate = useCompositionFrameRate
if (!continueFromPreviousAnimate) lastFrameNanos = AnimationConstants.UnspecifiedTime
if (composition == null) {
isPlaying = false
return@mutate
} else if (speed.isInfinite()) {
- progress = endProgress
+ updateProgress(endProgress)
isPlaying = false
this.iteration = iterations
return@mutate
@@ -278,32 +306,46 @@ private class LottieAnimatableImpl : LottieAnimatable {
val minProgress = clipSpec?.getMinProgress(composition) ?: 0f
val maxProgress = clipSpec?.getMaxProgress(composition) ?: 1f
- val dProgress = dNanos / 1_000_000 / composition.duration * speed
+ val dProgress = dNanos / 1_000_000 / composition.duration * frameSpeed
val progressPastEndOfIteration = when {
- speed < 0 -> minProgress - (progress + dProgress)
- else -> progress + dProgress - maxProgress
+ frameSpeed < 0 -> minProgress - (progressRaw + dProgress)
+ else -> progressRaw + dProgress - maxProgress
}
if (progressPastEndOfIteration < 0f) {
- progress = progress.coerceIn(minProgress, maxProgress) + dProgress
+ updateProgress(progressRaw.coerceIn(minProgress, maxProgress) + dProgress)
} else {
val durationProgress = maxProgress - minProgress
val dIterations = (progressPastEndOfIteration / durationProgress).toInt() + 1
if (iteration + dIterations > iterations) {
- progress = endProgress
+ updateProgress(endProgress)
iteration = iterations
return false
}
iteration += dIterations
val progressPastEndRem = progressPastEndOfIteration - (dIterations - 1) * durationProgress
- progress = when {
- speed < 0 -> maxProgress - progressPastEndRem
- else -> minProgress + progressPastEndRem
- }
+ updateProgress(
+ when {
+ frameSpeed < 0 -> maxProgress - progressPastEndRem
+ else -> minProgress + progressPastEndRem
+ }
+ )
}
return true
}
+
+ private fun Float.roundToCompositionFrameRate(composition: LottieComposition?): Float {
+ composition ?: return this
+ val frameRate = composition.frameRate
+ val interval = 1 / frameRate
+ return this - this % interval
+ }
+
+ private fun updateProgress(progress: Float) {
+ this.progressRaw = progress
+ this.progress = if (useCompositionFrameRate) progress.roundToCompositionFrameRate(composition) else progress
+ }
}
private fun defaultProgress(composition: LottieComposition?, clipSpec: LottieClipSpec?, speed: Float): Float {
@@ -313,4 +355,4 @@ private fun defaultProgress(composition: LottieComposition?, clipSpec: LottieCli
speed < 0 -> clipSpec?.getMaxProgress(composition) ?: 1f
else -> clipSpec?.getMinProgress(composition) ?: 0f
}
-} \ No newline at end of file
+}
diff --git a/lottie-compose/src/main/java/com/airbnb/lottie/compose/LottieAnimation.kt b/lottie-compose/src/main/java/com/airbnb/lottie/compose/LottieAnimation.kt
index fb9edece..3f8de0fe 100644
--- a/lottie-compose/src/main/java/com/airbnb/lottie/compose/LottieAnimation.kt
+++ b/lottie-compose/src/main/java/com/airbnb/lottie/compose/LottieAnimation.kt
@@ -1,10 +1,10 @@
package com.airbnb.lottie.compose
import android.graphics.Matrix
+import android.graphics.Typeface
import androidx.annotation.FloatRange
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.size
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -18,11 +18,10 @@ import androidx.compose.ui.graphics.nativeCanvas
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.layout.ScaleFactor
import androidx.compose.ui.unit.IntSize
-import androidx.compose.ui.unit.dp
+import com.airbnb.lottie.AsyncUpdates
import com.airbnb.lottie.LottieComposition
import com.airbnb.lottie.LottieDrawable
import com.airbnb.lottie.RenderMode
-import com.airbnb.lottie.utils.Utils
import kotlin.math.roundToInt
/**
@@ -65,8 +64,17 @@ import kotlin.math.roundToInt
* size than this composable.
* @param contentScale Define how the animation should be scaled if it has a different size than this Composable.
* @param clipToCompositionBounds Determines whether or not Lottie will clip the animation to the original animation composition bounds.
+ * The composition bounds refers to the Lottie animation composition, not the Compose composition.
+ * @param clipTextToBoundingBox When true, if there is a bounding box set on a text layer (paragraph text), any text
+ * that overflows past its height will not be drawn.
+ * @param fontMap A map of keys to Typefaces. The key can be: "fName", "fFamily", or "fFamily-fStyle" as specified in your Lottie file.
+ * @param asyncUpdates When set to true, some parts of animation updates will be done off of the main thread.
+ * For more details, refer to the docs of [AsyncUpdates].
+ * @param safeMode If set to true, draw will be wrapped with a try/catch which will cause Lottie to
+ * render an empty frame rather than crash your app.
*/
@Composable
+@JvmOverloads
fun LottieAnimation(
composition: LottieComposition?,
progress: () -> Float,
@@ -80,20 +88,24 @@ fun LottieAnimation(
alignment: Alignment = Alignment.Center,
contentScale: ContentScale = ContentScale.Fit,
clipToCompositionBounds: Boolean = true,
+ clipTextToBoundingBox: Boolean = false,
+ fontMap: Map<String, Typeface>? = null,
+ asyncUpdates: AsyncUpdates = AsyncUpdates.AUTOMATIC,
+ safeMode: Boolean = false,
) {
val drawable = remember { LottieDrawable() }
val matrix = remember { Matrix() }
- var setDynamicProperties: LottieDynamicProperties? by remember { mutableStateOf(null) }
+ var setDynamicProperties: LottieDynamicProperties? by remember(composition) { mutableStateOf(null) }
if (composition == null || composition.duration == 0f) return Box(modifier)
- val dpScale = Utils.dpScale()
+ val bounds = composition.bounds
Canvas(
modifier = modifier
- .size((composition.bounds.width() / dpScale).dp, (composition.bounds.height() / dpScale).dp)
+ .lottieSize(bounds.width(), bounds.height())
) {
drawIntoCanvas { canvas ->
- val compositionSize = Size(composition.bounds.width().toFloat(), composition.bounds.height().toFloat())
+ val compositionSize = Size(bounds.width().toFloat(), bounds.height().toFloat())
val intSize = IntSize(size.width.roundToInt(), size.height.roundToInt())
val scale = contentScale.computeScaleFactor(compositionSize, size)
@@ -103,8 +115,11 @@ fun LottieAnimation(
matrix.preScale(scale.scaleX, scale.scaleY)
drawable.enableMergePathsForKitKatAndAbove(enableMergePaths)
+ drawable.setSafeMode(safeMode)
drawable.renderMode = renderMode
+ drawable.asyncUpdates = asyncUpdates
drawable.composition = composition
+ drawable.setFontMap(fontMap)
if (dynamicProperties !== setDynamicProperties) {
setDynamicProperties?.removeFrom(drawable)
dynamicProperties?.addTo(drawable)
@@ -114,8 +129,9 @@ fun LottieAnimation(
drawable.isApplyingOpacityToLayersEnabled = applyOpacityToLayers
drawable.maintainOriginalImageBounds = maintainOriginalImageBounds
drawable.clipToCompositionBounds = clipToCompositionBounds
+ drawable.clipTextToBoundingBox = clipTextToBoundingBox
drawable.progress = progress()
- drawable.setBounds(0, 0, composition.bounds.width(), composition.bounds.height())
+ drawable.setBounds(0, 0, bounds.width(), bounds.height())
drawable.draw(canvas.nativeCanvas, matrix)
}
}
@@ -141,20 +157,24 @@ fun LottieAnimation(
alignment: Alignment = Alignment.Center,
contentScale: ContentScale = ContentScale.Fit,
clipToCompositionBounds: Boolean = true,
+ safeMode: Boolean = false,
+ asyncUpdates: AsyncUpdates = AsyncUpdates.AUTOMATIC,
) {
LottieAnimation(
- composition,
- { progress },
- modifier,
- outlineMasksAndMattes,
- applyOpacityToLayers,
- enableMergePaths,
- renderMode,
- maintainOriginalImageBounds,
- dynamicProperties,
- alignment,
- contentScale,
- clipToCompositionBounds,
+ composition = composition,
+ progress = { progress },
+ modifier = modifier,
+ outlineMasksAndMattes = outlineMasksAndMattes,
+ applyOpacityToLayers = applyOpacityToLayers,
+ enableMergePaths = enableMergePaths,
+ renderMode = renderMode,
+ maintainOriginalImageBounds = maintainOriginalImageBounds,
+ dynamicProperties = dynamicProperties,
+ alignment = alignment,
+ contentScale = contentScale,
+ clipToCompositionBounds = clipToCompositionBounds,
+ asyncUpdates = asyncUpdates,
+ safeMode = safeMode
)
}
@@ -166,6 +186,7 @@ fun LottieAnimation(
* @see animateLottieCompositionAsState
*/
@Composable
+@JvmOverloads
fun LottieAnimation(
composition: LottieComposition?,
modifier: Modifier = Modifier,
@@ -178,16 +199,22 @@ fun LottieAnimation(
applyOpacityToLayers: Boolean = false,
enableMergePaths: Boolean = false,
renderMode: RenderMode = RenderMode.AUTOMATIC,
+ reverseOnRepeat: Boolean = false,
maintainOriginalImageBounds: Boolean = false,
dynamicProperties: LottieDynamicProperties? = null,
alignment: Alignment = Alignment.Center,
contentScale: ContentScale = ContentScale.Fit,
clipToCompositionBounds: Boolean = true,
+ clipTextToBoundingBox: Boolean = false,
+ fontMap: Map<String, Typeface>? = null,
+ safeMode: Boolean = false,
+ asyncUpdates: AsyncUpdates = AsyncUpdates.AUTOMATIC,
) {
val progress by animateLottieCompositionAsState(
composition,
isPlaying,
restartOnPlay,
+ reverseOnRepeat,
clipSpec,
speed,
iterations,
@@ -205,6 +232,10 @@ fun LottieAnimation(
alignment = alignment,
contentScale = contentScale,
clipToCompositionBounds = clipToCompositionBounds,
+ clipTextToBoundingBox = clipTextToBoundingBox,
+ fontMap = fontMap,
+ asyncUpdates = asyncUpdates,
+ safeMode = safeMode
)
}
diff --git a/lottie-compose/src/main/java/com/airbnb/lottie/compose/LottieAnimationSizeNode.kt b/lottie-compose/src/main/java/com/airbnb/lottie/compose/LottieAnimationSizeNode.kt
new file mode 100644
index 00000000..820a2c0f
--- /dev/null
+++ b/lottie-compose/src/main/java/com/airbnb/lottie/compose/LottieAnimationSizeNode.kt
@@ -0,0 +1,107 @@
+package com.airbnb.lottie.compose
+
+import androidx.compose.runtime.Stable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Size
+import androidx.compose.ui.layout.Measurable
+import androidx.compose.ui.layout.MeasureResult
+import androidx.compose.ui.layout.MeasureScope
+import androidx.compose.ui.layout.ScaleFactor
+import androidx.compose.ui.node.LayoutModifierNode
+import androidx.compose.ui.node.ModifierNodeElement
+import androidx.compose.ui.platform.InspectorInfo
+import androidx.compose.ui.unit.Constraints
+import androidx.compose.ui.unit.IntSize
+import androidx.compose.ui.unit.constrain
+
+/**
+ * Custom layout modifier that Lottie uses instead of the normal size modifier.
+ *
+ * This modifier will:
+ * * Attempt to size the composable to width/height (which is set to the composition bounds)
+ * * Constrain the size to the incoming constraints
+ *
+ * However, if the incoming constraints are unbounded in exactly one dimension, it will constrain that
+ * dimension to maintain the correct aspect ratio of the composition.
+ */
+@Stable
+internal fun Modifier.lottieSize(
+ width: Int,
+ height: Int,
+) = this.then(LottieAnimationSizeElement(width, height))
+
+internal data class LottieAnimationSizeElement(
+ val width: Int,
+ val height: Int,
+) : ModifierNodeElement<LottieAnimationSizeNode>() {
+ override fun create(): LottieAnimationSizeNode {
+ return LottieAnimationSizeNode(width, height)
+ }
+
+ override fun update(node: LottieAnimationSizeNode) {
+ node.width = width
+ node.height = height
+ }
+
+ override fun InspectorInfo.inspectableProperties() {
+ name = "Lottie Size"
+ properties["width"] = width
+ properties["height"] = height
+ }
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other !is LottieAnimationSizeElement) return false
+
+ if (width != other.width) return false
+ if (height != other.height) return false
+ return true
+ }
+
+ override fun hashCode(): Int {
+ var result = width.hashCode()
+ result = 31 * result + height.hashCode()
+ return result
+ }
+}
+
+internal class LottieAnimationSizeNode(
+ var width: Int,
+ var height: Int,
+) : Modifier.Node(), LayoutModifierNode {
+ override fun MeasureScope.measure(measurable: Measurable, constraints: Constraints): MeasureResult {
+ val constrainedSize = constraints.constrain(IntSize(width, height))
+ val wrappedConstraints = when {
+ // We are constrained in the width dimension but not the height dimension.
+ constraints.maxHeight == Constraints.Infinity && constraints.maxWidth != Constraints.Infinity -> Constraints(
+ minWidth = constrainedSize.width,
+ maxWidth = constrainedSize.width,
+ minHeight = constrainedSize.width * height / width,
+ maxHeight = constrainedSize.width * height / width,
+ )
+ // We are constrained in the height dimension but not the width dimension.
+ constraints.maxWidth == Constraints.Infinity && constraints.maxHeight != Constraints.Infinity -> Constraints(
+ minWidth = constrainedSize.height * width / height,
+ maxWidth = constrainedSize.height * width / height,
+ minHeight = constrainedSize.height,
+ maxHeight = constrainedSize.height,
+ )
+ // We are constrained in both or neither dimension. Use the constrained size.
+ else -> Constraints(
+ minWidth = constrainedSize.width,
+ maxWidth = constrainedSize.width,
+ minHeight = constrainedSize.height,
+ maxHeight = constrainedSize.height,
+ )
+ }
+
+ val placeable = measurable.measure(wrappedConstraints)
+ return layout(placeable.width, placeable.height) {
+ placeable.placeRelative(0, 0)
+ }
+ }
+}
+
+private operator fun Size.times(scale: ScaleFactor): IntSize {
+ return IntSize((width * scale.scaleX).toInt(), (height * scale.scaleY).toInt())
+}
diff --git a/lottie-compose/src/main/java/com/airbnb/lottie/compose/LottieAnimationState.kt b/lottie-compose/src/main/java/com/airbnb/lottie/compose/LottieAnimationState.kt
index 0cbf1ac1..23303e27 100644
--- a/lottie-compose/src/main/java/com/airbnb/lottie/compose/LottieAnimationState.kt
+++ b/lottie-compose/src/main/java/com/airbnb/lottie/compose/LottieAnimationState.kt
@@ -25,10 +25,14 @@ interface LottieAnimationState : State<Float> {
val iterations: Int
+ val reverseOnRepeat: Boolean
+
val clipSpec: LottieClipSpec?
val speed: Float
+ val useCompositionFrameRate: Boolean
+
val composition: LottieComposition?
val lastFrameNanos: Long get() = AnimationConstants.UnspecifiedTime
diff --git a/lottie-compose/src/main/java/com/airbnb/lottie/compose/LottieDynamicProperties.kt b/lottie-compose/src/main/java/com/airbnb/lottie/compose/LottieDynamicProperties.kt
index 5edd3994..837a6250 100644
--- a/lottie-compose/src/main/java/com/airbnb/lottie/compose/LottieDynamicProperties.kt
+++ b/lottie-compose/src/main/java/com/airbnb/lottie/compose/LottieDynamicProperties.kt
@@ -2,6 +2,7 @@ package com.airbnb.lottie.compose
import android.graphics.Bitmap
import android.graphics.ColorFilter
+import android.graphics.Path
import android.graphics.PointF
import android.graphics.Typeface
import androidx.compose.runtime.Composable
@@ -102,6 +103,7 @@ class LottieDynamicProperties internal constructor(
private val typefaceProperties: List<LottieDynamicProperty<Typeface>>,
private val bitmapProperties: List<LottieDynamicProperty<Bitmap>>,
private val charSequenceProperties: List<LottieDynamicProperty<CharSequence>>,
+ private val pathProperties: List<LottieDynamicProperty<Path>>,
) {
@Suppress("UNCHECKED_CAST")
constructor(properties: List<LottieDynamicProperty<*>>) : this(
@@ -114,6 +116,7 @@ class LottieDynamicProperties internal constructor(
properties.filter { it.property is Typeface } as List<LottieDynamicProperty<Typeface>>,
properties.filter { it.property is Bitmap } as List<LottieDynamicProperty<Bitmap>>,
properties.filter { it.property is CharSequence } as List<LottieDynamicProperty<CharSequence>>,
+ properties.filter { it.property is Path } as List<LottieDynamicProperty<Path>>,
)
internal fun addTo(drawable: LottieDrawable) {
@@ -144,6 +147,9 @@ class LottieDynamicProperties internal constructor(
charSequenceProperties.forEach { p ->
drawable.addValueCallback(p.keyPath, p.property, p.callback.toValueCallback())
}
+ pathProperties.forEach { p ->
+ drawable.addValueCallback(p.keyPath, p.property, p.callback.toValueCallback())
+ }
}
internal fun removeFrom(drawable: LottieDrawable) {
@@ -174,6 +180,9 @@ class LottieDynamicProperties internal constructor(
charSequenceProperties.forEach { p ->
drawable.addValueCallback(p.keyPath, p.property, null as LottieValueCallback<CharSequence>?)
}
+ pathProperties.forEach { p ->
+ drawable.addValueCallback(p.keyPath, p.property, null as LottieValueCallback<Path>?)
+ }
}
}
@@ -181,4 +190,4 @@ private fun <T> ((frameInfo: LottieFrameInfo<T>) -> T).toValueCallback() = objec
override fun getValue(frameInfo: LottieFrameInfo<T>): T {
return invoke(frameInfo)
}
-} \ No newline at end of file
+}
diff --git a/lottie-compose/src/main/java/com/airbnb/lottie/compose/LottiePainter.kt b/lottie-compose/src/main/java/com/airbnb/lottie/compose/LottiePainter.kt
new file mode 100644
index 00000000..e0e3e118
--- /dev/null
+++ b/lottie-compose/src/main/java/com/airbnb/lottie/compose/LottiePainter.kt
@@ -0,0 +1,132 @@
+package com.airbnb.lottie.compose
+
+import android.graphics.Matrix
+import android.graphics.Typeface
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableFloatStateOf
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.geometry.Size
+import androidx.compose.ui.graphics.drawscope.DrawScope
+import androidx.compose.ui.graphics.drawscope.drawIntoCanvas
+import androidx.compose.ui.graphics.nativeCanvas
+import androidx.compose.ui.graphics.painter.Painter
+import androidx.compose.ui.layout.ScaleFactor
+import androidx.compose.ui.unit.IntSize
+import com.airbnb.lottie.AsyncUpdates
+import com.airbnb.lottie.LottieComposition
+import com.airbnb.lottie.LottieDrawable
+import com.airbnb.lottie.RenderMode
+import kotlin.math.roundToInt
+
+/**
+ * A composable that makes it easy to create a [LottiePainter] and update its properties.
+ */
+@Composable
+fun rememberLottiePainter(
+ composition: LottieComposition? = null,
+ progress: Float = 0f,
+ outlineMasksAndMattes: Boolean = false,
+ applyOpacityToLayers: Boolean = false,
+ enableMergePaths: Boolean = false,
+ renderMode: RenderMode = RenderMode.AUTOMATIC,
+ maintainOriginalImageBounds: Boolean = false,
+ dynamicProperties: LottieDynamicProperties? = null,
+ clipToCompositionBounds: Boolean = true,
+ clipTextToBoundingBox: Boolean = false,
+ fontMap: Map<String, Typeface>? = null,
+ asyncUpdates: AsyncUpdates = AsyncUpdates.AUTOMATIC,
+): LottiePainter {
+ val painter = remember { LottiePainter() }
+ painter.composition = composition
+ painter.progress = progress
+ painter.outlineMasksAndMattes = outlineMasksAndMattes
+ painter.applyOpacityToLayers = applyOpacityToLayers
+ painter.enableMergePaths = enableMergePaths
+ painter.renderMode = renderMode
+ painter.maintainOriginalImageBounds = maintainOriginalImageBounds
+ painter.dynamicProperties = dynamicProperties
+ painter.clipToCompositionBounds = clipToCompositionBounds
+ painter.clipTextToBoundingBox = clipTextToBoundingBox
+ painter.fontMap = fontMap
+ painter.asyncUpdates = asyncUpdates
+ return painter
+}
+
+/**
+ * A [Painter] that renders a [LottieComposition].
+ */
+class LottiePainter internal constructor(
+ composition: LottieComposition? = null,
+ progress: Float = 0f,
+ outlineMasksAndMattes: Boolean = false,
+ applyOpacityToLayers: Boolean = false,
+ enableMergePaths: Boolean = false,
+ renderMode: RenderMode = RenderMode.AUTOMATIC,
+ maintainOriginalImageBounds: Boolean = false,
+ dynamicProperties: LottieDynamicProperties? = null,
+ clipToCompositionBounds: Boolean = true,
+ clipTextToBoundingBox: Boolean = false,
+ fontMap: Map<String, Typeface>? = null,
+ asyncUpdates: AsyncUpdates = AsyncUpdates.AUTOMATIC,
+) : Painter() {
+ internal var composition by mutableStateOf(composition)
+ internal var progress by mutableFloatStateOf(progress)
+ internal var outlineMasksAndMattes by mutableStateOf(outlineMasksAndMattes)
+ internal var applyOpacityToLayers by mutableStateOf(applyOpacityToLayers)
+ internal var enableMergePaths by mutableStateOf(enableMergePaths)
+ internal var renderMode by mutableStateOf(renderMode)
+ internal var maintainOriginalImageBounds by mutableStateOf(maintainOriginalImageBounds)
+ internal var dynamicProperties by mutableStateOf(dynamicProperties)
+ internal var clipToCompositionBounds by mutableStateOf(clipToCompositionBounds)
+ internal var fontMap by mutableStateOf(fontMap)
+ internal var asyncUpdates by mutableStateOf(asyncUpdates)
+ internal var clipTextToBoundingBox by mutableStateOf(clipTextToBoundingBox)
+
+ private var setDynamicProperties: LottieDynamicProperties? = null
+
+ private val drawable = LottieDrawable()
+ private val matrix = Matrix()
+ override val intrinsicSize: Size
+ get() {
+ val composition = composition ?: return Size.Unspecified
+ return Size(composition.bounds.width().toFloat(), composition.bounds.height().toFloat())
+ }
+
+ override fun DrawScope.onDraw() {
+ val composition = composition ?: return
+ drawIntoCanvas { canvas ->
+ val compositionSize = Size(composition.bounds.width().toFloat(), composition.bounds.height().toFloat())
+ val intSize = IntSize(size.width.roundToInt(), size.height.roundToInt())
+
+ matrix.reset()
+ matrix.preScale(intSize.width / compositionSize.width, intSize.height / compositionSize.height)
+
+ drawable.enableMergePathsForKitKatAndAbove(enableMergePaths)
+ drawable.renderMode = renderMode
+ drawable.asyncUpdates = asyncUpdates
+ drawable.composition = composition
+ drawable.setFontMap(fontMap)
+ if (dynamicProperties !== setDynamicProperties) {
+ setDynamicProperties?.removeFrom(drawable)
+ dynamicProperties?.addTo(drawable)
+ setDynamicProperties = dynamicProperties
+ }
+ drawable.setOutlineMasksAndMattes(outlineMasksAndMattes)
+ drawable.isApplyingOpacityToLayersEnabled = applyOpacityToLayers
+ drawable.maintainOriginalImageBounds = maintainOriginalImageBounds
+ drawable.clipToCompositionBounds = clipToCompositionBounds
+ drawable.clipTextToBoundingBox = clipTextToBoundingBox
+ drawable.progress = progress
+ drawable.setBounds(0, 0, composition.bounds.width(), composition.bounds.height())
+ drawable.draw(canvas.nativeCanvas, matrix)
+ }
+
+ }
+}
+
+private operator fun Size.times(scale: ScaleFactor): IntSize {
+ return IntSize((width * scale.scaleX).toInt(), (height * scale.scaleY).toInt())
+}
diff --git a/lottie-compose/src/main/java/com/airbnb/lottie/compose/animateLottieCompositionAsState.kt b/lottie-compose/src/main/java/com/airbnb/lottie/compose/animateLottieCompositionAsState.kt
index 78211ea7..b1ab992c 100644
--- a/lottie-compose/src/main/java/com/airbnb/lottie/compose/animateLottieCompositionAsState.kt
+++ b/lottie-compose/src/main/java/com/airbnb/lottie/compose/animateLottieCompositionAsState.kt
@@ -23,6 +23,9 @@ import com.airbnb.lottie.utils.Utils
* is still playing.
* @param restartOnPlay If isPlaying switches from false to true, restartOnPlay determines whether
* the progress and iteration gets reset.
+ * @param reverseOnRepeat Defines what this animation should do when it reaches the end. This setting
+ * is applied only when [iterations] is either greater than 0 or [LottieConstants.IterateForever].
+ * Defaults to `false`.
* @param clipSpec A [LottieClipSpec] that specifies the bound the animation playback
* should be clipped to.
* @param speed The speed the animation should play at. Numbers larger than one will speed it up.
@@ -42,11 +45,13 @@ fun animateLottieCompositionAsState(
composition: LottieComposition?,
isPlaying: Boolean = true,
restartOnPlay: Boolean = true,
+ reverseOnRepeat: Boolean = false,
clipSpec: LottieClipSpec? = null,
speed: Float = 1f,
iterations: Int = 1,
cancellationBehavior: LottieCancellationBehavior = LottieCancellationBehavior.Immediately,
ignoreSystemAnimatorScale: Boolean = false,
+ useCompositionFrameRate: Boolean = false,
): LottieAnimationState {
require(iterations > 0) { "Iterations must be a positive number ($iterations)." }
require(speed.isFinite()) { "Speed must be a finite number. It is $speed." }
@@ -73,11 +78,13 @@ fun animateLottieCompositionAsState(
animatable.animate(
composition,
iterations = iterations,
+ reverseOnRepeat = reverseOnRepeat,
speed = actualSpeed,
clipSpec = clipSpec,
initialProgress = animatable.progress,
continueFromPreviousAnimate = false,
cancellationBehavior = cancellationBehavior,
+ useCompositionFrameRate = useCompositionFrameRate,
)
}
diff --git a/lottie-compose/src/main/java/com/airbnb/lottie/compose/rememberLottieComposition.kt b/lottie-compose/src/main/java/com/airbnb/lottie/compose/rememberLottieComposition.kt
index b64c6db8..dfa486c0 100644
--- a/lottie-compose/src/main/java/com/airbnb/lottie/compose/rememberLottieComposition.kt
+++ b/lottie-compose/src/main/java/com/airbnb/lottie/compose/rememberLottieComposition.kt
@@ -71,6 +71,7 @@ private const val DefaultCacheKey = "__LottieInternalDefaultCacheKey__"
* retrying again. [rememberLottieRetrySignal] can be used to handle explicit retires.
*/
@Composable
+@JvmOverloads
fun rememberLottieComposition(
spec: LottieCompositionSpec,
imageAssetsFolder: String? = null,
diff --git a/lottie-compose/src/test/java/com/airbnb/lottie/compose/LottieAnimatableImplTest.kt b/lottie-compose/src/test/java/com/airbnb/lottie/compose/LottieAnimatableImplTest.kt
index fa024d90..8a9259e0 100644
--- a/lottie-compose/src/test/java/com/airbnb/lottie/compose/LottieAnimatableImplTest.kt
+++ b/lottie-compose/src/test/java/com/airbnb/lottie/compose/LottieAnimatableImplTest.kt
@@ -6,9 +6,11 @@ import com.airbnb.lottie.LottieCompositionFactory
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.cancelAndJoin
import kotlinx.coroutines.launch
-import kotlinx.coroutines.test.runBlockingTest
-import kotlinx.coroutines.withContext
-import org.junit.Assert.*
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runTest
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -30,7 +32,7 @@ class LottieAnimatableImplTest {
}
@Test
- fun testSingleIterationProgress() = runTest {
+ fun testSingleIterationProgress() = runTestWithClock {
launch {
anim.animate(composition)
}
@@ -47,7 +49,7 @@ class LottieAnimatableImplTest {
}
@Test
- fun testJumpFromOneIterationToEndOfNext() = runTest {
+ fun testJumpFromOneIterationToEndOfNext() = runTestWithClock {
launch {
anim.animate(composition, iterations = 2)
}
@@ -60,7 +62,7 @@ class LottieAnimatableImplTest {
}
@Test
- fun testTwoIterations() = runTest {
+ fun testTwoIterations() = runTestWithClock {
launch {
anim.animate(composition, iterations = 2)
}
@@ -75,7 +77,7 @@ class LottieAnimatableImplTest {
}
@Test
- fun testJumpsFromOneIterationToThree() = runTest {
+ fun testJumpsFromOneIterationToThree() = runTestWithClock {
val job = launch {
anim.animate(composition, iterations = 3)
}
@@ -87,7 +89,7 @@ class LottieAnimatableImplTest {
}
@Test
- fun testCancels() = runTest {
+ fun testCancels() = runTestWithClock {
val job = launch {
anim.animate(composition)
}
@@ -100,7 +102,7 @@ class LottieAnimatableImplTest {
}
@Test
- fun testReverse() = runTest {
+ fun testReverse() = runTestWithClock {
launch {
anim.animate(composition, speed = -1f)
}
@@ -111,7 +113,7 @@ class LottieAnimatableImplTest {
}
@Test
- fun testClipSpec() = runTest {
+ fun testClipSpec() = runTestWithClock {
val clipSpec = LottieClipSpec.Progress(0.25f, 0.75f)
launch {
anim.animate(composition, clipSpec = clipSpec)
@@ -122,7 +124,7 @@ class LottieAnimatableImplTest {
}
@Test
- fun testClipSpecWithTwoIterations() = runTest {
+ fun testClipSpecWithTwoIterations() = runTestWithClock {
val clipSpec = LottieClipSpec.Progress(0.25f, 0.75f)
launch {
anim.animate(composition, clipSpec = clipSpec, iterations = 2)
@@ -134,7 +136,7 @@ class LottieAnimatableImplTest {
}
@Test
- fun testNegativeSpeedWithClipSpec() = runTest {
+ fun testNegativeSpeedWithClipSpec() = runTestWithClock {
val clipSpec = LottieClipSpec.Progress(0.25f, 0.75f)
launch {
anim.animate(composition, clipSpec = clipSpec, speed = -1f)
@@ -145,7 +147,7 @@ class LottieAnimatableImplTest {
}
@Test
- fun testChangingEndClipSpec() = runTest {
+ fun testChangingEndClipSpec() = runTestWithClock {
launch {
anim.animate(composition)
}
@@ -161,7 +163,7 @@ class LottieAnimatableImplTest {
}
@Test
- fun testChangingBeginningClipSpec() = runTest {
+ fun testChangingBeginningClipSpec() = runTestWithClock {
launch {
anim.animate(composition, iterations = 2)
}
@@ -178,7 +180,7 @@ class LottieAnimatableImplTest {
}
@Test
- fun testResumingAnimation() = runTest {
+ fun testResumingAnimation() = runTestWithClock {
launch {
anim.animate(composition)
}
@@ -199,7 +201,7 @@ class LottieAnimatableImplTest {
}
@Test
- fun testReRunAnimation() = runTest {
+ fun testReRunAnimation() = runTestWithClock {
launch {
anim.animate(composition)
}
@@ -214,7 +216,7 @@ class LottieAnimatableImplTest {
}
@Test
- fun testSnapNoopToThenResume() = runTest {
+ fun testSnapNoopToThenResume() = runTestWithClock {
launch {
anim.animate(composition)
}
@@ -230,7 +232,7 @@ class LottieAnimatableImplTest {
}
@Test
- fun testSnapToThenResume() = runTest {
+ fun testSnapToThenResume() = runTestWithClock {
launch {
anim.animate(composition)
}
@@ -247,7 +249,7 @@ class LottieAnimatableImplTest {
}
@Test
- fun testSnapToAnotherIterationThenResume() = runTest {
+ fun testSnapToAnotherIterationThenResume() = runTestWithClock {
launch {
anim.animate(composition, iterations = 3)
}
@@ -264,7 +266,7 @@ class LottieAnimatableImplTest {
}
@Test
- fun testChangeSpeed() = runTest {
+ fun testChangeSpeed() = runTestWithClock {
launch {
anim.animate(composition)
}
@@ -281,7 +283,7 @@ class LottieAnimatableImplTest {
@Test
fun testInfiniteSpeed() {
val clipSpec = LottieClipSpec.Progress(0.33f, 0.57f)
- runTest {
+ runTestWithClock {
launch {
anim.animate(composition, clipSpec = clipSpec, speed = Float.POSITIVE_INFINITY, iterations = LottieConstants.IterateForever)
}
@@ -302,7 +304,7 @@ class LottieAnimatableImplTest {
@Test
fun testInfiniteSpeedWithIterations() {
val clipSpec = LottieClipSpec.Progress(0.33f, 0.57f)
- runTest {
+ runTestWithClock {
launch {
anim.animate(composition, clipSpec = clipSpec, speed = Float.POSITIVE_INFINITY, iterations = 3)
}
@@ -323,7 +325,7 @@ class LottieAnimatableImplTest {
@Test
fun testNegativeInfiniteSpeed() {
val clipSpec = LottieClipSpec.Progress(0.33f, 0.57f)
- runTest {
+ runTestWithClock {
launch {
anim.animate(composition, clipSpec = clipSpec, speed = Float.NEGATIVE_INFINITY, iterations = LottieConstants.IterateForever)
}
@@ -341,9 +343,37 @@ class LottieAnimatableImplTest {
}
}
+ @Test
+ fun testReverseOnRepeat() = runTestWithClock {
+ val job = launch {
+ anim.animate(
+ composition,
+ reverseOnRepeat = true,
+ iterations = LottieConstants.IterateForever,
+ )
+ }
+ assertFrame(0, progress = 0f, iteration = 1, iterations = LottieConstants.IterateForever)
+
+ mapOf(
+ 0L to 0.0f,
+ 300L to 0.5f,
+ 598L to 0.99f,
+ 599L to 1.0f,
+ 601L to 0.99f, // start reversing animation
+ 899L to 0.5f,
+ 1199L to 0.0f,
+ ).forEach { (frameTime, expectedProgress) ->
+ clock.frameMs(frameTime)
+ assertEquals(
+ "Expecting progress $expectedProgress @ frame $frameTime, but was ${anim.progress}",
+ expectedProgress, anim.progress, 0.01f
+ )
+ }
+ job.cancel()
+ }
@Test
- fun testNonCancellable() = runTest {
+ fun testNonCancellable() = runTestWithClock {
val job = launch {
anim.animate(composition, cancellationBehavior = LottieCancellationBehavior.OnIterationFinish)
}
@@ -354,7 +384,7 @@ class LottieAnimatableImplTest {
}
@Test
- fun testCancelWithMultipleIterations() = runTest {
+ fun testCancelWithMultipleIterations() = runTestWithClock {
val job = launch {
anim.animate(composition, cancellationBehavior = LottieCancellationBehavior.OnIterationFinish, iterations = 3)
}
@@ -365,7 +395,7 @@ class LottieAnimatableImplTest {
}
@Test
- fun testCompositionCreated() = runTest {
+ fun testCompositionCreated() = runTestWithClock {
val clipSpec = LottieClipSpec.Frame(20, 25)
val job1 = launch {
anim.animate(null, clipSpec = clipSpec)
@@ -403,11 +433,7 @@ class LottieAnimatableImplTest {
assertEquals("lastFrameNanos at %d".format(frameTimeMs), lastFrameNanos, anim.lastFrameNanos)
}
- private fun runTest(test: suspend CoroutineScope.() -> Unit) {
- runBlockingTest {
- withContext(clock) {
- test()
- }
- }
+ private fun runTestWithClock(test: suspend CoroutineScope.() -> Unit) = runTest(context = clock + UnconfinedTestDispatcher()) {
+ test()
}
-} \ No newline at end of file
+}
diff --git a/lottie-compose/src/test/java/com/airbnb/lottie/compose/LottieClipSpecTest.kt b/lottie-compose/src/test/java/com/airbnb/lottie/compose/LottieClipSpecTest.kt
index 8457f54b..346c95d7 100644
--- a/lottie-compose/src/test/java/com/airbnb/lottie/compose/LottieClipSpecTest.kt
+++ b/lottie-compose/src/test/java/com/airbnb/lottie/compose/LottieClipSpecTest.kt
@@ -135,10 +135,11 @@ class LottieClipSpecTest {
LongSparseArray(),
emptyMap(),
emptyMap(),
+ 1f,
SparseArrayCompat(),
emptyMap(),
markers,
)
return composition
}
-} \ No newline at end of file
+}
diff --git a/lottie/build.gradle b/lottie/build.gradle
index 8ba8dcfe..7ba4d7cb 100644
--- a/lottie/build.gradle
+++ b/lottie/build.gradle
@@ -1,17 +1,20 @@
+import com.vanniktech.maven.publish.SonatypeHost
import net.ltgt.gradle.errorprone.CheckSeverity
plugins {
id 'com.android.library'
id 'net.ltgt.errorprone'
id 'com.vanniktech.maven.publish'
+ id 'androidx.baselineprofile'
}
android {
+ namespace 'com.airbnb.lottie'
resourcePrefix 'lottie_'
- compileSdk 31
+ compileSdk 34
defaultConfig {
minSdk 16
- targetSdk 30
+ targetSdk 34
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
lintOptions {
@@ -19,10 +22,6 @@ android {
textReport true
textOutput 'stdout'
}
- compileOptions {
- sourceCompatibility JavaVersion.VERSION_1_8
- targetCompatibility JavaVersion.VERSION_1_8
- }
testOptions {
unitTests {
includeAndroidResources = true
@@ -30,43 +29,37 @@ android {
}
}
+mavenPublishing {
+ publishToMavenCentral(SonatypeHost.DEFAULT)
+ signAllPublications()
+}
+
+baselineProfile {
+ filter {
+ include 'com.airbnb.lottie.**'
+ exclude 'com.airbnb.lottie.compose.**'
+ }
+}
+
dependencies {
- implementation "androidx.appcompat:appcompat:$appcompatVersion"
+ implementation libs.androidx.appcompat
// Do not upgrade to 2.0 because it will bring in Kotlin as a transitive dependency.
//noinspection GradleDependency
- implementation("com.squareup.okio:okio:1.17.4")
+ implementation libs.okio
- annotationProcessor "com.uber.nullaway:nullaway:0.9.2"
- errorprone "com.google.errorprone:error_prone_core:2.9.0"
- //noinspection GradleDynamicVersion
- errorproneJavac "com.google.errorprone:javac:9+181-r4173-1"
+ annotationProcessor libs.nullaway
+ errorprone libs.errorprone.core
- testImplementation "org.mockito:mockito-core:$mockitoVersion"
- testImplementation "org.robolectric:robolectric:$robolectricVersion"
- testImplementation "junit:junit:$junitVersion"
- androidTestImplementation "androidx.test.ext:junit:$extJunitVersion"
- androidTestImplementation "androidx.test.espresso:espresso-core:$espressoVersion"
-}
-
-task sourcesJar(type: Jar) {
- from android.sourceSets.main.java.srcDirs
- archiveClassifier.set('sources')
-}
-
-task javadoc(type: Javadoc) {
- source = android.sourceSets.main.java.srcDirs
- configurations.implementation.setCanBeResolved(true)
- classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) + configurations.implementation
-}
+ baselineProfile project(':baselineprofile')
-tasks.withType(Javadoc) {
- // This started failing with the following when upgrading to AGP 7 and JDK 11.
- // TODO: investigate why once AGP 7 is stable.
- // javadoc: error - The code being documented uses modules but the packages defined in https://developer.android.com/reference/ are in the unnamed module.
- failOnError false
+ testImplementation libs.mockito.core
+ testImplementation libs.robolectric
+ testImplementation libs.junit4
+ androidTestImplementation libs.androidx.test.junit
+ androidTestImplementation libs.androidx.test.espresso
}
-tasks.withType(JavaCompile) {
+tasks.withType(JavaCompile).configureEach {
// remove the if condition if you want to run NullAway on test code
if (!name.toLowerCase().contains("test")) {
options.errorprone {
diff --git a/lottie/src/main/AndroidManifest.xml b/lottie/src/main/AndroidManifest.xml
index f2b05f44..48652056 100644
--- a/lottie/src/main/AndroidManifest.xml
+++ b/lottie/src/main/AndroidManifest.xml
@@ -1,5 +1,3 @@
-<manifest package="com.airbnb.lottie" >
-
+<manifest>
<application />
-
</manifest>
diff --git a/lottie/src/main/baseline-prof.txt b/lottie/src/main/generated/baselineProfiles/baseline-prof.txt
index 478844b5..ce045186 100644
--- a/lottie/src/main/baseline-prof.txt
+++ b/lottie/src/main/generated/baselineProfiles/baseline-prof.txt
@@ -1,197 +1,217 @@
-HPLcom/airbnb/lottie/LottieDrawable;->convertRect(Landroid/graphics/RectF;Landroid/graphics/Rect;)V
-HPLcom/airbnb/lottie/LottieDrawable;->ensureSoftwareRenderingBitmap(II)V
-HPLcom/airbnb/lottie/LottieDrawable;->ensureSoftwareRenderingObjectsInitialized()V
-HPLcom/airbnb/lottie/LottieDrawable;->getImageAssetManager()Lcom/airbnb/lottie/manager/ImageAssetManager;
-HPLcom/airbnb/lottie/LottieDrawable;->renderAndDrawAsBitmap(Landroid/graphics/Canvas;Lcom/airbnb/lottie/model/layer/CompositionLayer;)V
-HPLcom/airbnb/lottie/animation/content/BaseStrokeContent;->onValueChanged()V
-HPLcom/airbnb/lottie/animation/content/ContentGroup;->getBounds(Landroid/graphics/RectF;Landroid/graphics/Matrix;Z)V
-HPLcom/airbnb/lottie/animation/content/EllipseContent;->onValueChanged()V
-HPLcom/airbnb/lottie/animation/content/FillContent;->getBounds(Landroid/graphics/RectF;Landroid/graphics/Matrix;Z)V
-HPLcom/airbnb/lottie/animation/content/FillContent;->onValueChanged()V
-HPLcom/airbnb/lottie/animation/content/GradientFillContent;->applyDynamicColorsIfNeeded([I)[I
-HPLcom/airbnb/lottie/animation/content/GradientFillContent;->draw(Landroid/graphics/Canvas;Landroid/graphics/Matrix;I)V
-HPLcom/airbnb/lottie/animation/content/GradientFillContent;->getGradientHash()I
-HPLcom/airbnb/lottie/animation/content/GradientFillContent;->getLinearGradient()Landroid/graphics/LinearGradient;
-HPLcom/airbnb/lottie/animation/content/RectangleContent;->getPath()Landroid/graphics/Path;
-HPLcom/airbnb/lottie/animation/content/ShapeContent;->onValueChanged()V
-HPLcom/airbnb/lottie/animation/content/TrimPathContent;-><init>(Lcom/airbnb/lottie/model/layer/BaseLayer;Lcom/airbnb/lottie/model/content/ShapeTrimPath;)V
-HPLcom/airbnb/lottie/animation/content/TrimPathContent;->onValueChanged()V
-HPLcom/airbnb/lottie/animation/keyframe/MaskKeyframeAnimation;-><init>(Ljava/util/List;)V
-HPLcom/airbnb/lottie/animation/keyframe/MaskKeyframeAnimation;->getMaskAnimations()Ljava/util/List;
-HPLcom/airbnb/lottie/animation/keyframe/ScaleKeyframeAnimation;->getValue(Lcom/airbnb/lottie/value/Keyframe;F)Lcom/airbnb/lottie/value/ScaleXY;
-HPLcom/airbnb/lottie/animation/keyframe/ScaleKeyframeAnimation;->getValue(Lcom/airbnb/lottie/value/Keyframe;F)Ljava/lang/Object;
-HPLcom/airbnb/lottie/animation/keyframe/SplitDimensionPathKeyframeAnimation;->getValue()Landroid/graphics/PointF;
-HPLcom/airbnb/lottie/animation/keyframe/SplitDimensionPathKeyframeAnimation;->getValue()Ljava/lang/Object;
-HPLcom/airbnb/lottie/animation/keyframe/SplitDimensionPathKeyframeAnimation;->getValue(Lcom/airbnb/lottie/value/Keyframe;F)Landroid/graphics/PointF;
-HPLcom/airbnb/lottie/animation/keyframe/SplitDimensionPathKeyframeAnimation;->setProgress(F)V
-HPLcom/airbnb/lottie/model/layer/BaseLayer$$ExternalSyntheticLambda0;->onValueChanged()V
-HPLcom/airbnb/lottie/model/layer/BaseLayer;->applyAddMask(Landroid/graphics/Canvas;Landroid/graphics/Matrix;Lcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation;Lcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation;)V
-HPLcom/airbnb/lottie/model/layer/BaseLayer;->applyMasks(Landroid/graphics/Canvas;Landroid/graphics/Matrix;)V
-HPLcom/airbnb/lottie/model/layer/BaseLayer;->clearCanvas(Landroid/graphics/Canvas;)V
-HPLcom/airbnb/lottie/model/layer/BaseLayer;->getBounds(Landroid/graphics/RectF;Landroid/graphics/Matrix;Z)V
-HPLcom/airbnb/lottie/model/layer/BaseLayer;->intersectBoundsWithMask(Landroid/graphics/RectF;Landroid/graphics/Matrix;)V
-HPLcom/airbnb/lottie/model/layer/BaseLayer;->intersectBoundsWithMatte(Landroid/graphics/RectF;Landroid/graphics/Matrix;)V
-HPLcom/airbnb/lottie/model/layer/BaseLayer;->lambda$setupInOutAnimations$0$BaseLayer()V
-HPLcom/airbnb/lottie/model/layer/ImageLayer;->drawLayer(Landroid/graphics/Canvas;Landroid/graphics/Matrix;I)V
-HPLcom/airbnb/lottie/model/layer/ImageLayer;->getBitmap()Landroid/graphics/Bitmap;
-HPLcom/airbnb/lottie/model/layer/NullLayer;->drawLayer(Landroid/graphics/Canvas;Landroid/graphics/Matrix;I)V
-HPLcom/airbnb/lottie/model/layer/ShapeLayer;->getBounds(Landroid/graphics/RectF;Landroid/graphics/Matrix;Z)V
-HPLcom/airbnb/lottie/parser/MaskParser;->parse(Lcom/airbnb/lottie/parser/moshi/JsonReader;Lcom/airbnb/lottie/LottieComposition;)Lcom/airbnb/lottie/model/content/Mask;
-HPLcom/airbnb/lottie/utils/GammaEvaluator;->OECF_sRGB(F)F
-HPLcom/airbnb/lottie/utils/Utils;->applyTrimPathIfNeeded(Landroid/graphics/Path;FFF)V
-HPLcom/airbnb/lottie/utils/Utils;->applyTrimPathIfNeeded(Landroid/graphics/Path;Lcom/airbnb/lottie/animation/content/TrimPathContent;)V
-HSPLcom/airbnb/lottie/L$1;-><init>(Landroid/content/Context;)V
-HSPLcom/airbnb/lottie/L$1;->getCacheDir()Ljava/io/File;
+Lcom/airbnb/lottie/AsyncUpdates;
+HSPLcom/airbnb/lottie/AsyncUpdates;->$values()[Lcom/airbnb/lottie/AsyncUpdates;
+HSPLcom/airbnb/lottie/AsyncUpdates;-><clinit>()V
+HSPLcom/airbnb/lottie/AsyncUpdates;-><init>(Ljava/lang/String;I)V
+Lcom/airbnb/lottie/ImageAssetDelegate;
+Lcom/airbnb/lottie/L;
HSPLcom/airbnb/lottie/L;-><clinit>()V
-HSPLcom/airbnb/lottie/L;->beginSection(Ljava/lang/String;)V
-HSPLcom/airbnb/lottie/L;->endSection(Ljava/lang/String;)F
-HSPLcom/airbnb/lottie/L;->networkCache(Landroid/content/Context;)Lcom/airbnb/lottie/network/NetworkCache;
-HSPLcom/airbnb/lottie/L;->networkFetcher(Landroid/content/Context;)Lcom/airbnb/lottie/network/NetworkFetcher;
-HSPLcom/airbnb/lottie/L;->setTraceEnabled(Z)V
+HPLcom/airbnb/lottie/L;->beginSection(Ljava/lang/String;)V
+HPLcom/airbnb/lottie/L;->endSection(Ljava/lang/String;)F
+HSPLcom/airbnb/lottie/L;->getDisablePathInterpolatorCache()Z
+Lcom/airbnb/lottie/LottieComposition;
HSPLcom/airbnb/lottie/LottieComposition;-><init>()V
-HSPLcom/airbnb/lottie/LottieComposition;->addWarning(Ljava/lang/String;)V
-HSPLcom/airbnb/lottie/LottieComposition;->getBounds()Landroid/graphics/Rect;
-HSPLcom/airbnb/lottie/LottieComposition;->getDuration()F
-HSPLcom/airbnb/lottie/LottieComposition;->getDurationFrames()F
+HPLcom/airbnb/lottie/LottieComposition;->getBounds()Landroid/graphics/Rect;
+HPLcom/airbnb/lottie/LottieComposition;->getCharacters()Landroidx/collection/SparseArrayCompat;
+HPLcom/airbnb/lottie/LottieComposition;->getDuration()F
+HPLcom/airbnb/lottie/LottieComposition;->getDurationFrames()F
HSPLcom/airbnb/lottie/LottieComposition;->getEndFrame()F
HSPLcom/airbnb/lottie/LottieComposition;->getFonts()Ljava/util/Map;
-HSPLcom/airbnb/lottie/LottieComposition;->getFrameForProgress(F)F
+HPLcom/airbnb/lottie/LottieComposition;->getFrameForProgress(F)F
HSPLcom/airbnb/lottie/LottieComposition;->getImages()Ljava/util/Map;
HSPLcom/airbnb/lottie/LottieComposition;->getLayers()Ljava/util/List;
HSPLcom/airbnb/lottie/LottieComposition;->getMaskAndMatteCount()I
-HSPLcom/airbnb/lottie/LottieComposition;->getPerformanceTracker()Lcom/airbnb/lottie/PerformanceTracker;
-HSPLcom/airbnb/lottie/LottieComposition;->getPrecomps(Ljava/lang/String;)Ljava/util/List;
+HPLcom/airbnb/lottie/LottieComposition;->getPerformanceTracker()Lcom/airbnb/lottie/PerformanceTracker;
HSPLcom/airbnb/lottie/LottieComposition;->getStartFrame()F
-HSPLcom/airbnb/lottie/LottieComposition;->getWarnings()Ljava/util/ArrayList;
HSPLcom/airbnb/lottie/LottieComposition;->hasDashPattern()Z
HSPLcom/airbnb/lottie/LottieComposition;->hasImages()Z
+HSPLcom/airbnb/lottie/LottieComposition;->incrementMatteOrMaskCount(I)V
HSPLcom/airbnb/lottie/LottieComposition;->init(Landroid/graphics/Rect;FFFLjava/util/List;Landroidx/collection/LongSparseArray;Ljava/util/Map;Ljava/util/Map;Landroidx/collection/SparseArrayCompat;Ljava/util/Map;Ljava/util/List;)V
HSPLcom/airbnb/lottie/LottieComposition;->setPerformanceTrackingEnabled(Z)V
-HSPLcom/airbnb/lottie/LottieCompositionFactory$$ExternalSyntheticLambda0;-><init>(Ljava/lang/String;Ljava/util/concurrent/atomic/AtomicBoolean;)V
-HSPLcom/airbnb/lottie/LottieCompositionFactory$$ExternalSyntheticLambda0;->onResult(Ljava/lang/Object;)V
-HSPLcom/airbnb/lottie/LottieCompositionFactory$$ExternalSyntheticLambda2;-><init>(Ljava/lang/String;Ljava/util/concurrent/atomic/AtomicBoolean;)V
-HSPLcom/airbnb/lottie/LottieCompositionFactory$$ExternalSyntheticLambda4;-><init>(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;)V
-HSPLcom/airbnb/lottie/LottieCompositionFactory$$ExternalSyntheticLambda4;->call()Ljava/lang/Object;
-HSPLcom/airbnb/lottie/LottieCompositionFactory$$ExternalSyntheticLambda5;-><init>(Lcom/airbnb/lottie/LottieComposition;)V
-HSPLcom/airbnb/lottie/LottieCompositionFactory$$ExternalSyntheticLambda5;->call()Ljava/lang/Object;
-HSPLcom/airbnb/lottie/LottieCompositionFactory$$ExternalSyntheticLambda9;-><init>(Ljava/lang/ref/WeakReference;Landroid/content/Context;ILjava/lang/String;)V
-HSPLcom/airbnb/lottie/LottieCompositionFactory$$ExternalSyntheticLambda9;->call()Ljava/lang/Object;
+Lcom/airbnb/lottie/LottieCompositionFactory;
HSPLcom/airbnb/lottie/LottieCompositionFactory;-><clinit>()V
-HSPLcom/airbnb/lottie/LottieCompositionFactory;->cache(Ljava/lang/String;Ljava/util/concurrent/Callable;)Lcom/airbnb/lottie/LottieTask;
+HSPLcom/airbnb/lottie/LottieCompositionFactory;->cache(Ljava/lang/String;Ljava/util/concurrent/Callable;Ljava/lang/Runnable;)Lcom/airbnb/lottie/LottieTask;
HSPLcom/airbnb/lottie/LottieCompositionFactory;->fromJsonInputStreamSync(Ljava/io/InputStream;Ljava/lang/String;)Lcom/airbnb/lottie/LottieResult;
HSPLcom/airbnb/lottie/LottieCompositionFactory;->fromJsonInputStreamSync(Ljava/io/InputStream;Ljava/lang/String;Z)Lcom/airbnb/lottie/LottieResult;
-HSPLcom/airbnb/lottie/LottieCompositionFactory;->fromJsonReaderSync(Lcom/airbnb/lottie/parser/moshi/JsonReader;Ljava/lang/String;)Lcom/airbnb/lottie/LottieResult;
+HSPLcom/airbnb/lottie/LottieCompositionFactory;->fromJsonReaderSync(Lcom/airbnb/lottie/parser/moshi/JsonReader;Ljava/lang/String;Z)Lcom/airbnb/lottie/LottieResult;
HSPLcom/airbnb/lottie/LottieCompositionFactory;->fromJsonReaderSyncInternal(Lcom/airbnb/lottie/parser/moshi/JsonReader;Ljava/lang/String;Z)Lcom/airbnb/lottie/LottieResult;
HSPLcom/airbnb/lottie/LottieCompositionFactory;->fromRawRes(Landroid/content/Context;I)Lcom/airbnb/lottie/LottieTask;
HSPLcom/airbnb/lottie/LottieCompositionFactory;->fromRawRes(Landroid/content/Context;ILjava/lang/String;)Lcom/airbnb/lottie/LottieTask;
HSPLcom/airbnb/lottie/LottieCompositionFactory;->fromRawResSync(Landroid/content/Context;ILjava/lang/String;)Lcom/airbnb/lottie/LottieResult;
-HSPLcom/airbnb/lottie/LottieCompositionFactory;->fromUrl(Landroid/content/Context;Ljava/lang/String;)Lcom/airbnb/lottie/LottieTask;
-HSPLcom/airbnb/lottie/LottieCompositionFactory;->fromUrl(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;)Lcom/airbnb/lottie/LottieTask;
HSPLcom/airbnb/lottie/LottieCompositionFactory;->isNightMode(Landroid/content/Context;)Z
HSPLcom/airbnb/lottie/LottieCompositionFactory;->isZipCompressed(Lokio/BufferedSource;)Ljava/lang/Boolean;
-HSPLcom/airbnb/lottie/LottieCompositionFactory;->lambda$cache$8(Lcom/airbnb/lottie/LottieComposition;)Lcom/airbnb/lottie/LottieResult;
-HSPLcom/airbnb/lottie/LottieCompositionFactory;->lambda$cache$9(Ljava/lang/String;Ljava/util/concurrent/atomic/AtomicBoolean;Lcom/airbnb/lottie/LottieComposition;)V
+HSPLcom/airbnb/lottie/LottieCompositionFactory;->lambda$cache$15(Lcom/airbnb/lottie/LottieComposition;)Lcom/airbnb/lottie/LottieResult;
+HSPLcom/airbnb/lottie/LottieCompositionFactory;->lambda$cache$16(Ljava/lang/String;Ljava/util/concurrent/atomic/AtomicBoolean;Lcom/airbnb/lottie/LottieComposition;)V
HSPLcom/airbnb/lottie/LottieCompositionFactory;->lambda$fromRawRes$2(Ljava/lang/ref/WeakReference;Landroid/content/Context;ILjava/lang/String;)Lcom/airbnb/lottie/LottieResult;
-HSPLcom/airbnb/lottie/LottieCompositionFactory;->lambda$fromUrl$0(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;)Lcom/airbnb/lottie/LottieResult;
+HSPLcom/airbnb/lottie/LottieCompositionFactory;->notifyTaskCacheIdleListeners(Z)V
HSPLcom/airbnb/lottie/LottieCompositionFactory;->rawResCacheKey(Landroid/content/Context;I)Ljava/lang/String;
-HSPLcom/airbnb/lottie/LottieDrawable$1;-><init>(Lcom/airbnb/lottie/LottieDrawable;)V
-HSPLcom/airbnb/lottie/LottieDrawable$1;->onAnimationUpdate(Landroid/animation/ValueAnimator;)V
-HSPLcom/airbnb/lottie/LottieDrawable$OnVisibleAction;-><clinit>()V
-HSPLcom/airbnb/lottie/LottieDrawable$OnVisibleAction;-><init>(Ljava/lang/String;I)V
+Lcom/airbnb/lottie/LottieCompositionFactory$$ExternalSyntheticLambda4;
+HSPLcom/airbnb/lottie/LottieCompositionFactory$$ExternalSyntheticLambda4;-><init>(Lcom/airbnb/lottie/LottieComposition;)V
+HSPLcom/airbnb/lottie/LottieCompositionFactory$$ExternalSyntheticLambda4;->call()Ljava/lang/Object;
+Lcom/airbnb/lottie/LottieCompositionFactory$$ExternalSyntheticLambda5;
+HSPLcom/airbnb/lottie/LottieCompositionFactory$$ExternalSyntheticLambda5;-><init>(Ljava/lang/String;Ljava/util/concurrent/atomic/AtomicBoolean;)V
+HSPLcom/airbnb/lottie/LottieCompositionFactory$$ExternalSyntheticLambda5;->onResult(Ljava/lang/Object;)V
+Lcom/airbnb/lottie/LottieCompositionFactory$$ExternalSyntheticLambda6;
+HSPLcom/airbnb/lottie/LottieCompositionFactory$$ExternalSyntheticLambda6;-><init>(Ljava/lang/String;Ljava/util/concurrent/atomic/AtomicBoolean;)V
+Lcom/airbnb/lottie/LottieCompositionFactory$$ExternalSyntheticLambda8;
+HSPLcom/airbnb/lottie/LottieCompositionFactory$$ExternalSyntheticLambda8;-><init>(Ljava/lang/ref/WeakReference;Landroid/content/Context;ILjava/lang/String;)V
+HSPLcom/airbnb/lottie/LottieCompositionFactory$$ExternalSyntheticLambda8;->call()Ljava/lang/Object;
+Lcom/airbnb/lottie/LottieDrawable;
+HSPLcom/airbnb/lottie/LottieDrawable;-><clinit>()V
HSPLcom/airbnb/lottie/LottieDrawable;-><init>()V
-HSPLcom/airbnb/lottie/LottieDrawable;->access$000(Lcom/airbnb/lottie/LottieDrawable;)Lcom/airbnb/lottie/model/layer/CompositionLayer;
-HSPLcom/airbnb/lottie/LottieDrawable;->access$100(Lcom/airbnb/lottie/LottieDrawable;)Lcom/airbnb/lottie/utils/LottieValueAnimator;
HSPLcom/airbnb/lottie/LottieDrawable;->buildCompositionLayer()V
HSPLcom/airbnb/lottie/LottieDrawable;->clearComposition()V
-HSPLcom/airbnb/lottie/LottieDrawable;->draw(Landroid/graphics/Canvas;Landroid/graphics/Matrix;)V
+HPLcom/airbnb/lottie/LottieDrawable;->computeRenderMode()V
+HPLcom/airbnb/lottie/LottieDrawable;->draw(Landroid/graphics/Canvas;Landroid/graphics/Matrix;)V
HSPLcom/airbnb/lottie/LottieDrawable;->enableMergePathsForKitKatAndAbove(Z)V
-HSPLcom/airbnb/lottie/LottieDrawable;->getClipToCompositionBounds()Z
-HSPLcom/airbnb/lottie/LottieDrawable;->getComposition()Lcom/airbnb/lottie/LottieComposition;
-HSPLcom/airbnb/lottie/LottieDrawable;->invalidateSelf()V
-HSPLcom/airbnb/lottie/LottieDrawable;->isApplyingOpacityToLayersEnabled()Z
-HSPLcom/airbnb/lottie/LottieDrawable;->setApplyingOpacityToLayersEnabled(Z)V
-HSPLcom/airbnb/lottie/LottieDrawable;->setClipToCompositionBounds(Z)V
+HSPLcom/airbnb/lottie/LottieDrawable;->getAsyncUpdates()Lcom/airbnb/lottie/AsyncUpdates;
+HPLcom/airbnb/lottie/LottieDrawable;->getAsyncUpdatesEnabled()Z
+HPLcom/airbnb/lottie/LottieDrawable;->getBitmapForId(Ljava/lang/String;)Landroid/graphics/Bitmap;
+HPLcom/airbnb/lottie/LottieDrawable;->getComposition()Lcom/airbnb/lottie/LottieComposition;
+HSPLcom/airbnb/lottie/LottieDrawable;->getContext()Landroid/content/Context;
+HPLcom/airbnb/lottie/LottieDrawable;->getImageAssetManager()Lcom/airbnb/lottie/manager/ImageAssetManager;
+HSPLcom/airbnb/lottie/LottieDrawable;->getLottieImageAssetForId(Ljava/lang/String;)Lcom/airbnb/lottie/LottieImageAsset;
+HSPLcom/airbnb/lottie/LottieDrawable;->getMaintainOriginalImageBounds()Z
+HPLcom/airbnb/lottie/LottieDrawable;->invalidateSelf()V
+HPLcom/airbnb/lottie/LottieDrawable;->isApplyingOpacityToLayersEnabled()Z
+HPLcom/airbnb/lottie/LottieDrawable;->lambda$new$0$com-airbnb-lottie-LottieDrawable(Landroid/animation/ValueAnimator;)V
+HPLcom/airbnb/lottie/LottieDrawable;->setApplyingOpacityToLayersEnabled(Z)V
+HPLcom/airbnb/lottie/LottieDrawable;->setAsyncUpdates(Lcom/airbnb/lottie/AsyncUpdates;)V
+HPLcom/airbnb/lottie/LottieDrawable;->setClipToCompositionBounds(Z)V
HSPLcom/airbnb/lottie/LottieDrawable;->setComposition(Lcom/airbnb/lottie/LottieComposition;)Z
-HSPLcom/airbnb/lottie/LottieDrawable;->setMaintainOriginalImageBounds(Z)V
-HSPLcom/airbnb/lottie/LottieDrawable;->setOutlineMasksAndMattes(Z)V
-HSPLcom/airbnb/lottie/LottieDrawable;->setProgress(F)V
-HSPLcom/airbnb/lottie/LottieDrawable;->setScale(F)V
-HSPLcom/airbnb/lottie/LottieDrawable;->useSoftwareRendering(Z)V
+HPLcom/airbnb/lottie/LottieDrawable;->setFontMap(Ljava/util/Map;)V
+HPLcom/airbnb/lottie/LottieDrawable;->setMaintainOriginalImageBounds(Z)V
+HPLcom/airbnb/lottie/LottieDrawable;->setOutlineMasksAndMattes(Z)V
+HPLcom/airbnb/lottie/LottieDrawable;->setProgress(F)V
+HPLcom/airbnb/lottie/LottieDrawable;->setRenderMode(Lcom/airbnb/lottie/RenderMode;)V
+HPLcom/airbnb/lottie/LottieDrawable;->useTextGlyphs()Z
+Lcom/airbnb/lottie/LottieDrawable$$ExternalSyntheticLambda0;
+HSPLcom/airbnb/lottie/LottieDrawable$$ExternalSyntheticLambda0;-><init>(Lcom/airbnb/lottie/LottieDrawable;)V
+HPLcom/airbnb/lottie/LottieDrawable$$ExternalSyntheticLambda0;->onAnimationUpdate(Landroid/animation/ValueAnimator;)V
+Lcom/airbnb/lottie/LottieDrawable$$ExternalSyntheticLambda9;
+HSPLcom/airbnb/lottie/LottieDrawable$$ExternalSyntheticLambda9;-><init>(Lcom/airbnb/lottie/LottieDrawable;)V
+Lcom/airbnb/lottie/LottieDrawable$OnVisibleAction;
+HSPLcom/airbnb/lottie/LottieDrawable$OnVisibleAction;->$values()[Lcom/airbnb/lottie/LottieDrawable$OnVisibleAction;
+HSPLcom/airbnb/lottie/LottieDrawable$OnVisibleAction;-><clinit>()V
+HSPLcom/airbnb/lottie/LottieDrawable$OnVisibleAction;-><init>(Ljava/lang/String;I)V
+Lcom/airbnb/lottie/LottieImageAsset;
+HSPLcom/airbnb/lottie/LottieImageAsset;-><init>(IILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+HSPLcom/airbnb/lottie/LottieImageAsset;->getBitmap()Landroid/graphics/Bitmap;
+HSPLcom/airbnb/lottie/LottieImageAsset;->getFileName()Ljava/lang/String;
+HSPLcom/airbnb/lottie/LottieImageAsset;->getId()Ljava/lang/String;
+HSPLcom/airbnb/lottie/LottieImageAsset;->setBitmap(Landroid/graphics/Bitmap;)V
+Lcom/airbnb/lottie/LottieListener;
+Lcom/airbnb/lottie/LottieLogger;
+Lcom/airbnb/lottie/LottieResult;
HSPLcom/airbnb/lottie/LottieResult;-><init>(Ljava/lang/Object;)V
HSPLcom/airbnb/lottie/LottieResult;->getException()Ljava/lang/Throwable;
HSPLcom/airbnb/lottie/LottieResult;->getValue()Ljava/lang/Object;
-HSPLcom/airbnb/lottie/LottieTask$$ExternalSyntheticLambda0;-><init>(Lcom/airbnb/lottie/LottieTask;)V
-HSPLcom/airbnb/lottie/LottieTask$$ExternalSyntheticLambda0;->run()V
-HSPLcom/airbnb/lottie/LottieTask$LottieFutureTask;-><init>(Lcom/airbnb/lottie/LottieTask;Ljava/util/concurrent/Callable;)V
-HSPLcom/airbnb/lottie/LottieTask$LottieFutureTask;->done()V
+Lcom/airbnb/lottie/LottieTask;
HSPLcom/airbnb/lottie/LottieTask;-><clinit>()V
HSPLcom/airbnb/lottie/LottieTask;-><init>(Ljava/util/concurrent/Callable;)V
HSPLcom/airbnb/lottie/LottieTask;-><init>(Ljava/util/concurrent/Callable;Z)V
HSPLcom/airbnb/lottie/LottieTask;->access$000(Lcom/airbnb/lottie/LottieTask;Lcom/airbnb/lottie/LottieResult;)V
HSPLcom/airbnb/lottie/LottieTask;->addFailureListener(Lcom/airbnb/lottie/LottieListener;)Lcom/airbnb/lottie/LottieTask;
HSPLcom/airbnb/lottie/LottieTask;->addListener(Lcom/airbnb/lottie/LottieListener;)Lcom/airbnb/lottie/LottieTask;
-HSPLcom/airbnb/lottie/LottieTask;->lambda$notifyListeners$0$LottieTask()V
+HSPLcom/airbnb/lottie/LottieTask;->lambda$notifyListeners$0$com-airbnb-lottie-LottieTask()V
HSPLcom/airbnb/lottie/LottieTask;->notifyListeners()V
HSPLcom/airbnb/lottie/LottieTask;->notifySuccessListeners(Ljava/lang/Object;)V
HSPLcom/airbnb/lottie/LottieTask;->setResult(Lcom/airbnb/lottie/LottieResult;)V
-HSPLcom/airbnb/lottie/PerformanceTracker$1;-><init>(Lcom/airbnb/lottie/PerformanceTracker;)V
+Lcom/airbnb/lottie/LottieTask$$ExternalSyntheticLambda0;
+HSPLcom/airbnb/lottie/LottieTask$$ExternalSyntheticLambda0;-><init>(Lcom/airbnb/lottie/LottieTask;)V
+HSPLcom/airbnb/lottie/LottieTask$$ExternalSyntheticLambda0;->run()V
+Lcom/airbnb/lottie/LottieTask$LottieFutureTask;
+HSPLcom/airbnb/lottie/LottieTask$LottieFutureTask;-><init>(Lcom/airbnb/lottie/LottieTask;Ljava/util/concurrent/Callable;)V
+HSPLcom/airbnb/lottie/LottieTask$LottieFutureTask;->done()V
+Lcom/airbnb/lottie/PerformanceTracker;
HSPLcom/airbnb/lottie/PerformanceTracker;-><init>()V
-HSPLcom/airbnb/lottie/PerformanceTracker;->recordRenderTime(Ljava/lang/String;F)V
+HPLcom/airbnb/lottie/PerformanceTracker;->recordRenderTime(Ljava/lang/String;F)V
HSPLcom/airbnb/lottie/PerformanceTracker;->setEnabled(Z)V
-HSPLcom/airbnb/lottie/RenderMode$1;-><clinit>()V
+Lcom/airbnb/lottie/PerformanceTracker$1;
+HSPLcom/airbnb/lottie/PerformanceTracker$1;-><init>(Lcom/airbnb/lottie/PerformanceTracker;)V
+Lcom/airbnb/lottie/RenderMode;
+HSPLcom/airbnb/lottie/RenderMode;->$values()[Lcom/airbnb/lottie/RenderMode;
HSPLcom/airbnb/lottie/RenderMode;-><clinit>()V
HSPLcom/airbnb/lottie/RenderMode;-><init>(Ljava/lang/String;I)V
-HSPLcom/airbnb/lottie/RenderMode;->useSoftwareRendering(IZI)Z
+HPLcom/airbnb/lottie/RenderMode;->useSoftwareRendering(IZI)Z
HSPLcom/airbnb/lottie/RenderMode;->values()[Lcom/airbnb/lottie/RenderMode;
+Lcom/airbnb/lottie/RenderMode$1;
+HSPLcom/airbnb/lottie/RenderMode$1;-><clinit>()V
+Lcom/airbnb/lottie/animation/LPaint;
HSPLcom/airbnb/lottie/animation/LPaint;-><init>()V
HSPLcom/airbnb/lottie/animation/LPaint;-><init>(I)V
HSPLcom/airbnb/lottie/animation/LPaint;-><init>(ILandroid/graphics/PorterDuff$Mode;)V
HSPLcom/airbnb/lottie/animation/LPaint;-><init>(Landroid/graphics/PorterDuff$Mode;)V
-HSPLcom/airbnb/lottie/animation/LPaint;->setAlpha(I)V
+HPLcom/airbnb/lottie/animation/LPaint;->setAlpha(I)V
HSPLcom/airbnb/lottie/animation/LPaint;->setTextLocales(Landroid/os/LocaleList;)V
-HSPLcom/airbnb/lottie/animation/content/BaseStrokeContent$PathGroup;-><init>(Lcom/airbnb/lottie/animation/content/TrimPathContent;)V
-HSPLcom/airbnb/lottie/animation/content/BaseStrokeContent$PathGroup;-><init>(Lcom/airbnb/lottie/animation/content/TrimPathContent;Lcom/airbnb/lottie/animation/content/BaseStrokeContent$1;)V
-HSPLcom/airbnb/lottie/animation/content/BaseStrokeContent$PathGroup;->access$100(Lcom/airbnb/lottie/animation/content/BaseStrokeContent$PathGroup;)Ljava/util/List;
-HSPLcom/airbnb/lottie/animation/content/BaseStrokeContent$PathGroup;->access$200(Lcom/airbnb/lottie/animation/content/BaseStrokeContent$PathGroup;)Lcom/airbnb/lottie/animation/content/TrimPathContent;
+Lcom/airbnb/lottie/animation/content/BaseStrokeContent;
HSPLcom/airbnb/lottie/animation/content/BaseStrokeContent;-><init>(Lcom/airbnb/lottie/LottieDrawable;Lcom/airbnb/lottie/model/layer/BaseLayer;Landroid/graphics/Paint$Cap;Landroid/graphics/Paint$Join;FLcom/airbnb/lottie/model/animatable/AnimatableIntegerValue;Lcom/airbnb/lottie/model/animatable/AnimatableFloatValue;Ljava/util/List;Lcom/airbnb/lottie/model/animatable/AnimatableFloatValue;)V
-HSPLcom/airbnb/lottie/animation/content/BaseStrokeContent;->applyDashPatternIfNeeded(Landroid/graphics/Matrix;)V
+HPLcom/airbnb/lottie/animation/content/BaseStrokeContent;->applyDashPatternIfNeeded(Landroid/graphics/Matrix;)V
HSPLcom/airbnb/lottie/animation/content/BaseStrokeContent;->draw(Landroid/graphics/Canvas;Landroid/graphics/Matrix;I)V
+HSPLcom/airbnb/lottie/animation/content/BaseStrokeContent;->getBounds(Landroid/graphics/RectF;Landroid/graphics/Matrix;Z)V
HSPLcom/airbnb/lottie/animation/content/BaseStrokeContent;->setContents(Ljava/util/List;Ljava/util/List;)V
+Lcom/airbnb/lottie/animation/content/BaseStrokeContent$PathGroup;
+HSPLcom/airbnb/lottie/animation/content/BaseStrokeContent$PathGroup;-><init>(Lcom/airbnb/lottie/animation/content/TrimPathContent;)V
+HSPLcom/airbnb/lottie/animation/content/BaseStrokeContent$PathGroup;-><init>(Lcom/airbnb/lottie/animation/content/TrimPathContent;Lcom/airbnb/lottie/animation/content/BaseStrokeContent$1;)V
+HPLcom/airbnb/lottie/animation/content/BaseStrokeContent$PathGroup;->access$100(Lcom/airbnb/lottie/animation/content/BaseStrokeContent$PathGroup;)Ljava/util/List;
+HSPLcom/airbnb/lottie/animation/content/BaseStrokeContent$PathGroup;->access$200(Lcom/airbnb/lottie/animation/content/BaseStrokeContent$PathGroup;)Lcom/airbnb/lottie/animation/content/TrimPathContent;
+Lcom/airbnb/lottie/animation/content/CompoundTrimPathContent;
HSPLcom/airbnb/lottie/animation/content/CompoundTrimPathContent;-><init>()V
HSPLcom/airbnb/lottie/animation/content/CompoundTrimPathContent;->apply(Landroid/graphics/Path;)V
-HSPLcom/airbnb/lottie/animation/content/ContentGroup;-><init>(Lcom/airbnb/lottie/LottieDrawable;Lcom/airbnb/lottie/model/layer/BaseLayer;Lcom/airbnb/lottie/model/content/ShapeGroup;)V
+Lcom/airbnb/lottie/animation/content/Content;
+Lcom/airbnb/lottie/animation/content/ContentGroup;
+HSPLcom/airbnb/lottie/animation/content/ContentGroup;-><init>(Lcom/airbnb/lottie/LottieDrawable;Lcom/airbnb/lottie/model/layer/BaseLayer;Lcom/airbnb/lottie/model/content/ShapeGroup;Lcom/airbnb/lottie/LottieComposition;)V
HSPLcom/airbnb/lottie/animation/content/ContentGroup;-><init>(Lcom/airbnb/lottie/LottieDrawable;Lcom/airbnb/lottie/model/layer/BaseLayer;Ljava/lang/String;ZLjava/util/List;Lcom/airbnb/lottie/model/animatable/AnimatableTransform;)V
-HSPLcom/airbnb/lottie/animation/content/ContentGroup;->contentsFromModels(Lcom/airbnb/lottie/LottieDrawable;Lcom/airbnb/lottie/model/layer/BaseLayer;Ljava/util/List;)Ljava/util/List;
+HSPLcom/airbnb/lottie/animation/content/ContentGroup;->contentsFromModels(Lcom/airbnb/lottie/LottieDrawable;Lcom/airbnb/lottie/LottieComposition;Lcom/airbnb/lottie/model/layer/BaseLayer;Ljava/util/List;)Ljava/util/List;
HSPLcom/airbnb/lottie/animation/content/ContentGroup;->draw(Landroid/graphics/Canvas;Landroid/graphics/Matrix;I)V
HSPLcom/airbnb/lottie/animation/content/ContentGroup;->findTransform(Ljava/util/List;)Lcom/airbnb/lottie/model/animatable/AnimatableTransform;
+HSPLcom/airbnb/lottie/animation/content/ContentGroup;->getBounds(Landroid/graphics/RectF;Landroid/graphics/Matrix;Z)V
+HSPLcom/airbnb/lottie/animation/content/ContentGroup;->getPath()Landroid/graphics/Path;
HSPLcom/airbnb/lottie/animation/content/ContentGroup;->onValueChanged()V
HSPLcom/airbnb/lottie/animation/content/ContentGroup;->setContents(Ljava/util/List;Ljava/util/List;)V
+Lcom/airbnb/lottie/animation/content/DrawingContent;
+Lcom/airbnb/lottie/animation/content/EllipseContent;
HSPLcom/airbnb/lottie/animation/content/EllipseContent;-><init>(Lcom/airbnb/lottie/LottieDrawable;Lcom/airbnb/lottie/model/layer/BaseLayer;Lcom/airbnb/lottie/model/content/CircleShape;)V
-HSPLcom/airbnb/lottie/animation/content/EllipseContent;->getPath()Landroid/graphics/Path;
+HPLcom/airbnb/lottie/animation/content/EllipseContent;->getPath()Landroid/graphics/Path;
HSPLcom/airbnb/lottie/animation/content/EllipseContent;->setContents(Ljava/util/List;Ljava/util/List;)V
+Lcom/airbnb/lottie/animation/content/FillContent;
HSPLcom/airbnb/lottie/animation/content/FillContent;-><init>(Lcom/airbnb/lottie/LottieDrawable;Lcom/airbnb/lottie/model/layer/BaseLayer;Lcom/airbnb/lottie/model/content/ShapeFill;)V
HSPLcom/airbnb/lottie/animation/content/FillContent;->draw(Landroid/graphics/Canvas;Landroid/graphics/Matrix;I)V
+HPLcom/airbnb/lottie/animation/content/FillContent;->getBounds(Landroid/graphics/RectF;Landroid/graphics/Matrix;Z)V
HSPLcom/airbnb/lottie/animation/content/FillContent;->setContents(Ljava/util/List;Ljava/util/List;)V
+Lcom/airbnb/lottie/animation/content/GreedyContent;
+Lcom/airbnb/lottie/animation/content/KeyPathElementContent;
+Lcom/airbnb/lottie/animation/content/ModifierContent;
+Lcom/airbnb/lottie/animation/content/PathContent;
+Lcom/airbnb/lottie/animation/content/PolystarContent;
+HSPLcom/airbnb/lottie/animation/content/PolystarContent;-><init>(Lcom/airbnb/lottie/LottieDrawable;Lcom/airbnb/lottie/model/layer/BaseLayer;Lcom/airbnb/lottie/model/content/PolystarShape;)V
+HSPLcom/airbnb/lottie/animation/content/PolystarContent;->createPolygonPath()V
+HSPLcom/airbnb/lottie/animation/content/PolystarContent;->createStarPath()V
+HSPLcom/airbnb/lottie/animation/content/PolystarContent;->getPath()Landroid/graphics/Path;
+HPLcom/airbnb/lottie/animation/content/PolystarContent;->invalidate()V
+HSPLcom/airbnb/lottie/animation/content/PolystarContent;->onValueChanged()V
+HSPLcom/airbnb/lottie/animation/content/PolystarContent;->setContents(Ljava/util/List;Ljava/util/List;)V
+Lcom/airbnb/lottie/animation/content/PolystarContent$1;
+HSPLcom/airbnb/lottie/animation/content/PolystarContent$1;-><clinit>()V
+Lcom/airbnb/lottie/animation/content/RectangleContent;
+HSPLcom/airbnb/lottie/animation/content/RectangleContent;-><init>(Lcom/airbnb/lottie/LottieDrawable;Lcom/airbnb/lottie/model/layer/BaseLayer;Lcom/airbnb/lottie/model/content/RectangleShape;)V
+HPLcom/airbnb/lottie/animation/content/RectangleContent;->getPath()Landroid/graphics/Path;
+HSPLcom/airbnb/lottie/animation/content/RectangleContent;->setContents(Ljava/util/List;Ljava/util/List;)V
+Lcom/airbnb/lottie/animation/content/RepeaterContent;
+HSPLcom/airbnb/lottie/animation/content/RepeaterContent;-><init>(Lcom/airbnb/lottie/LottieDrawable;Lcom/airbnb/lottie/model/layer/BaseLayer;Lcom/airbnb/lottie/model/content/Repeater;)V
+HSPLcom/airbnb/lottie/animation/content/RepeaterContent;->absorbContent(Ljava/util/ListIterator;)V
+HPLcom/airbnb/lottie/animation/content/RepeaterContent;->draw(Landroid/graphics/Canvas;Landroid/graphics/Matrix;I)V
+HPLcom/airbnb/lottie/animation/content/RepeaterContent;->getBounds(Landroid/graphics/RectF;Landroid/graphics/Matrix;Z)V
+HSPLcom/airbnb/lottie/animation/content/RepeaterContent;->setContents(Ljava/util/List;Ljava/util/List;)V
+Lcom/airbnb/lottie/animation/content/RoundedCornersContent;
+Lcom/airbnb/lottie/animation/content/ShapeContent;
HSPLcom/airbnb/lottie/animation/content/ShapeContent;-><init>(Lcom/airbnb/lottie/LottieDrawable;Lcom/airbnb/lottie/model/layer/BaseLayer;Lcom/airbnb/lottie/model/content/ShapePath;)V
-HSPLcom/airbnb/lottie/animation/content/ShapeContent;->getPath()Landroid/graphics/Path;
-HSPLcom/airbnb/lottie/animation/content/ShapeContent;->setContents(Ljava/util/List;Ljava/util/List;)V
+HPLcom/airbnb/lottie/animation/content/ShapeContent;->getPath()Landroid/graphics/Path;
+Lcom/airbnb/lottie/animation/content/ShapeModifierContent;
+Lcom/airbnb/lottie/animation/content/StrokeContent;
HSPLcom/airbnb/lottie/animation/content/StrokeContent;-><init>(Lcom/airbnb/lottie/LottieDrawable;Lcom/airbnb/lottie/model/layer/BaseLayer;Lcom/airbnb/lottie/model/content/ShapeStroke;)V
-HSPLcom/airbnb/lottie/animation/content/StrokeContent;->draw(Landroid/graphics/Canvas;Landroid/graphics/Matrix;I)V
-HSPLcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation$KeyframesWrapperImpl;-><init>(Ljava/util/List;)V
-HSPLcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation$KeyframesWrapperImpl;->findKeyframe(F)Lcom/airbnb/lottie/value/Keyframe;
-HSPLcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation$KeyframesWrapperImpl;->getCurrentKeyframe()Lcom/airbnb/lottie/value/Keyframe;
-HSPLcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation$KeyframesWrapperImpl;->getEndProgress()F
-HSPLcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation$KeyframesWrapperImpl;->getStartDelayProgress()F
-HSPLcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation$KeyframesWrapperImpl;->isCachedValueEnabled(F)Z
-HSPLcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation$KeyframesWrapperImpl;->isEmpty()Z
-HSPLcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation$KeyframesWrapperImpl;->isValueChanged(F)Z
-HSPLcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation$SingleKeyframeWrapper;-><init>(Ljava/util/List;)V
-HSPLcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation$SingleKeyframeWrapper;->getCurrentKeyframe()Lcom/airbnb/lottie/value/Keyframe;
-HSPLcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation$SingleKeyframeWrapper;->getEndProgress()F
-HSPLcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation$SingleKeyframeWrapper;->getStartDelayProgress()F
-HSPLcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation$SingleKeyframeWrapper;->isCachedValueEnabled(F)Z
-HSPLcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation$SingleKeyframeWrapper;->isEmpty()Z
-HSPLcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation$SingleKeyframeWrapper;->isValueChanged(F)Z
+HPLcom/airbnb/lottie/animation/content/StrokeContent;->draw(Landroid/graphics/Canvas;Landroid/graphics/Matrix;I)V
+Lcom/airbnb/lottie/animation/content/TrimPathContent;
+Lcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation;
HSPLcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation;-><init>(Ljava/util/List;)V
HSPLcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation;->addUpdateListener(Lcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation$AnimationListener;)V
HSPLcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation;->getCurrentKeyframe()Lcom/airbnb/lottie/value/Keyframe;
@@ -200,44 +220,113 @@ HSPLcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation;->getInterpolated
HSPLcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation;->getLinearCurrentKeyframeProgress()F
HSPLcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation;->getStartDelayProgress()F
HSPLcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation;->getValue()Ljava/lang/Object;
-HSPLcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation;->notifyListeners()V
+HPLcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation;->notifyListeners()V
HSPLcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation;->setIsDiscrete()V
HSPLcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation;->setProgress(F)V
HSPLcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation;->wrap(Ljava/util/List;)Lcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation$KeyframesWrapper;
+Lcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation$AnimationListener;
+Lcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation$KeyframesWrapper;
+Lcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation$KeyframesWrapperImpl;
+HSPLcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation$KeyframesWrapperImpl;-><init>(Ljava/util/List;)V
+HSPLcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation$KeyframesWrapperImpl;->findKeyframe(F)Lcom/airbnb/lottie/value/Keyframe;
+HSPLcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation$KeyframesWrapperImpl;->getCurrentKeyframe()Lcom/airbnb/lottie/value/Keyframe;
+HSPLcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation$KeyframesWrapperImpl;->getEndProgress()F
+HSPLcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation$KeyframesWrapperImpl;->getStartDelayProgress()F
+HSPLcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation$KeyframesWrapperImpl;->isCachedValueEnabled(F)Z
+HSPLcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation$KeyframesWrapperImpl;->isEmpty()Z
+HPLcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation$KeyframesWrapperImpl;->isValueChanged(F)Z
+Lcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation$SingleKeyframeWrapper;
+HSPLcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation$SingleKeyframeWrapper;-><init>(Ljava/util/List;)V
+HPLcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation$SingleKeyframeWrapper;->getCurrentKeyframe()Lcom/airbnb/lottie/value/Keyframe;
+HSPLcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation$SingleKeyframeWrapper;->getEndProgress()F
+HSPLcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation$SingleKeyframeWrapper;->getStartDelayProgress()F
+HPLcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation$SingleKeyframeWrapper;->isCachedValueEnabled(F)Z
+HPLcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation$SingleKeyframeWrapper;->isEmpty()Z
+HPLcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation$SingleKeyframeWrapper;->isValueChanged(F)Z
+Lcom/airbnb/lottie/animation/keyframe/ColorKeyframeAnimation;
HSPLcom/airbnb/lottie/animation/keyframe/ColorKeyframeAnimation;-><init>(Ljava/util/List;)V
-HSPLcom/airbnb/lottie/animation/keyframe/ColorKeyframeAnimation;->getIntValue()I
-HSPLcom/airbnb/lottie/animation/keyframe/ColorKeyframeAnimation;->getIntValue(Lcom/airbnb/lottie/value/Keyframe;F)I
+HPLcom/airbnb/lottie/animation/keyframe/ColorKeyframeAnimation;->getIntValue()I
+HPLcom/airbnb/lottie/animation/keyframe/ColorKeyframeAnimation;->getIntValue(Lcom/airbnb/lottie/value/Keyframe;F)I
+Lcom/airbnb/lottie/animation/keyframe/DropShadowKeyframeAnimation;
+Lcom/airbnb/lottie/animation/keyframe/FloatKeyframeAnimation;
HSPLcom/airbnb/lottie/animation/keyframe/FloatKeyframeAnimation;-><init>(Ljava/util/List;)V
-HSPLcom/airbnb/lottie/animation/keyframe/FloatKeyframeAnimation;->getFloatValue()F
+HPLcom/airbnb/lottie/animation/keyframe/FloatKeyframeAnimation;->getFloatValue()F
HSPLcom/airbnb/lottie/animation/keyframe/FloatKeyframeAnimation;->getFloatValue(Lcom/airbnb/lottie/value/Keyframe;F)F
-HSPLcom/airbnb/lottie/animation/keyframe/FloatKeyframeAnimation;->getValue(Lcom/airbnb/lottie/value/Keyframe;F)Ljava/lang/Float;
-HSPLcom/airbnb/lottie/animation/keyframe/FloatKeyframeAnimation;->getValue(Lcom/airbnb/lottie/value/Keyframe;F)Ljava/lang/Object;
+HPLcom/airbnb/lottie/animation/keyframe/FloatKeyframeAnimation;->getValue(Lcom/airbnb/lottie/value/Keyframe;F)Ljava/lang/Float;
+HPLcom/airbnb/lottie/animation/keyframe/FloatKeyframeAnimation;->getValue(Lcom/airbnb/lottie/value/Keyframe;F)Ljava/lang/Object;
+Lcom/airbnb/lottie/animation/keyframe/IntegerKeyframeAnimation;
HSPLcom/airbnb/lottie/animation/keyframe/IntegerKeyframeAnimation;-><init>(Ljava/util/List;)V
-HSPLcom/airbnb/lottie/animation/keyframe/IntegerKeyframeAnimation;->getIntValue()I
-HSPLcom/airbnb/lottie/animation/keyframe/IntegerKeyframeAnimation;->getIntValue(Lcom/airbnb/lottie/value/Keyframe;F)I
+HPLcom/airbnb/lottie/animation/keyframe/IntegerKeyframeAnimation;->getIntValue()I
+HPLcom/airbnb/lottie/animation/keyframe/IntegerKeyframeAnimation;->getIntValue(Lcom/airbnb/lottie/value/Keyframe;F)I
HSPLcom/airbnb/lottie/animation/keyframe/IntegerKeyframeAnimation;->getValue(Lcom/airbnb/lottie/value/Keyframe;F)Ljava/lang/Integer;
HSPLcom/airbnb/lottie/animation/keyframe/IntegerKeyframeAnimation;->getValue(Lcom/airbnb/lottie/value/Keyframe;F)Ljava/lang/Object;
+Lcom/airbnb/lottie/animation/keyframe/KeyframeAnimation;
HSPLcom/airbnb/lottie/animation/keyframe/KeyframeAnimation;-><init>(Ljava/util/List;)V
+Lcom/airbnb/lottie/animation/keyframe/MaskKeyframeAnimation;
+Lcom/airbnb/lottie/animation/keyframe/PathKeyframe;
HSPLcom/airbnb/lottie/animation/keyframe/PathKeyframe;-><init>(Lcom/airbnb/lottie/LottieComposition;Lcom/airbnb/lottie/value/Keyframe;)V
HSPLcom/airbnb/lottie/animation/keyframe/PathKeyframe;->createPath()V
HSPLcom/airbnb/lottie/animation/keyframe/PathKeyframe;->getPath()Landroid/graphics/Path;
+Lcom/airbnb/lottie/animation/keyframe/PathKeyframeAnimation;
HSPLcom/airbnb/lottie/animation/keyframe/PathKeyframeAnimation;-><init>(Ljava/util/List;)V
-HSPLcom/airbnb/lottie/animation/keyframe/PathKeyframeAnimation;->getValue(Lcom/airbnb/lottie/value/Keyframe;F)Landroid/graphics/PointF;
+HPLcom/airbnb/lottie/animation/keyframe/PathKeyframeAnimation;->getValue(Lcom/airbnb/lottie/value/Keyframe;F)Landroid/graphics/PointF;
HSPLcom/airbnb/lottie/animation/keyframe/PathKeyframeAnimation;->getValue(Lcom/airbnb/lottie/value/Keyframe;F)Ljava/lang/Object;
+Lcom/airbnb/lottie/animation/keyframe/PointKeyframeAnimation;
HSPLcom/airbnb/lottie/animation/keyframe/PointKeyframeAnimation;-><init>(Ljava/util/List;)V
HSPLcom/airbnb/lottie/animation/keyframe/PointKeyframeAnimation;->getValue(Lcom/airbnb/lottie/value/Keyframe;F)Landroid/graphics/PointF;
HSPLcom/airbnb/lottie/animation/keyframe/PointKeyframeAnimation;->getValue(Lcom/airbnb/lottie/value/Keyframe;F)Ljava/lang/Object;
HSPLcom/airbnb/lottie/animation/keyframe/PointKeyframeAnimation;->getValue(Lcom/airbnb/lottie/value/Keyframe;FFF)Landroid/graphics/PointF;
+Lcom/airbnb/lottie/animation/keyframe/ScaleKeyframeAnimation;
+HSPLcom/airbnb/lottie/animation/keyframe/ScaleKeyframeAnimation;-><init>(Ljava/util/List;)V
+HSPLcom/airbnb/lottie/animation/keyframe/ScaleKeyframeAnimation;->getValue(Lcom/airbnb/lottie/value/Keyframe;F)Lcom/airbnb/lottie/value/ScaleXY;
+HSPLcom/airbnb/lottie/animation/keyframe/ScaleKeyframeAnimation;->getValue(Lcom/airbnb/lottie/value/Keyframe;F)Ljava/lang/Object;
+Lcom/airbnb/lottie/animation/keyframe/ShapeKeyframeAnimation;
HSPLcom/airbnb/lottie/animation/keyframe/ShapeKeyframeAnimation;-><init>(Ljava/util/List;)V
HSPLcom/airbnb/lottie/animation/keyframe/ShapeKeyframeAnimation;->getValue(Lcom/airbnb/lottie/value/Keyframe;F)Landroid/graphics/Path;
HSPLcom/airbnb/lottie/animation/keyframe/ShapeKeyframeAnimation;->getValue(Lcom/airbnb/lottie/value/Keyframe;F)Ljava/lang/Object;
-HSPLcom/airbnb/lottie/animation/keyframe/ShapeKeyframeAnimation;->setShapeModifiers(Ljava/util/List;)V
-HSPLcom/airbnb/lottie/animation/keyframe/TransformKeyframeAnimation;-><init>(Lcom/airbnb/lottie/model/animatable/AnimatableTransform;)V
+Lcom/airbnb/lottie/animation/keyframe/TextKeyframeAnimation;
+HSPLcom/airbnb/lottie/animation/keyframe/TextKeyframeAnimation;-><init>(Ljava/util/List;)V
+HPLcom/airbnb/lottie/animation/keyframe/TextKeyframeAnimation;->getValue(Lcom/airbnb/lottie/value/Keyframe;F)Lcom/airbnb/lottie/model/DocumentData;
+HSPLcom/airbnb/lottie/animation/keyframe/TextKeyframeAnimation;->getValue(Lcom/airbnb/lottie/value/Keyframe;F)Ljava/lang/Object;
+Lcom/airbnb/lottie/animation/keyframe/TransformKeyframeAnimation;
+HPLcom/airbnb/lottie/animation/keyframe/TransformKeyframeAnimation;-><init>(Lcom/airbnb/lottie/model/animatable/AnimatableTransform;)V
HSPLcom/airbnb/lottie/animation/keyframe/TransformKeyframeAnimation;->addAnimationsToLayer(Lcom/airbnb/lottie/model/layer/BaseLayer;)V
HSPLcom/airbnb/lottie/animation/keyframe/TransformKeyframeAnimation;->addListener(Lcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation$AnimationListener;)V
+HSPLcom/airbnb/lottie/animation/keyframe/TransformKeyframeAnimation;->getEndOpacity()Lcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation;
HSPLcom/airbnb/lottie/animation/keyframe/TransformKeyframeAnimation;->getMatrix()Landroid/graphics/Matrix;
-HSPLcom/airbnb/lottie/animation/keyframe/TransformKeyframeAnimation;->getOpacity()Lcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation;
-HSPLcom/airbnb/lottie/animation/keyframe/TransformKeyframeAnimation;->setProgress(F)V
+HPLcom/airbnb/lottie/animation/keyframe/TransformKeyframeAnimation;->getMatrixForRepeater(F)Landroid/graphics/Matrix;
+HPLcom/airbnb/lottie/animation/keyframe/TransformKeyframeAnimation;->getOpacity()Lcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation;
+HPLcom/airbnb/lottie/animation/keyframe/TransformKeyframeAnimation;->getStartOpacity()Lcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation;
+HPLcom/airbnb/lottie/animation/keyframe/TransformKeyframeAnimation;->setProgress(F)V
+Lcom/airbnb/lottie/animation/keyframe/ValueCallbackKeyframeAnimation;
+Lcom/airbnb/lottie/benchmark/app/BenchmarkActivity;
+HSPLcom/airbnb/lottie/benchmark/app/BenchmarkActivity;-><clinit>()V
+HSPLcom/airbnb/lottie/benchmark/app/BenchmarkActivity;-><init>()V
+HSPLcom/airbnb/lottie/benchmark/app/BenchmarkActivity;->Content$lambda$0(Lcom/airbnb/lottie/compose/LottieCompositionResult;)Lcom/airbnb/lottie/LottieComposition;
+HPLcom/airbnb/lottie/benchmark/app/BenchmarkActivity;->Content$lambda$1(Lcom/airbnb/lottie/compose/LottieAnimationState;)F
+HSPLcom/airbnb/lottie/benchmark/app/BenchmarkActivity;->Content(Landroidx/compose/runtime/Composer;I)V
+HPLcom/airbnb/lottie/benchmark/app/BenchmarkActivity;->access$Content$lambda$1(Lcom/airbnb/lottie/compose/LottieAnimationState;)F
+HSPLcom/airbnb/lottie/benchmark/app/BenchmarkActivity;->onCreate(Landroid/os/Bundle;)V
+Lcom/airbnb/lottie/benchmark/app/BenchmarkActivity$Content$1$1;
+HSPLcom/airbnb/lottie/benchmark/app/BenchmarkActivity$Content$1$1;-><init>(Lcom/airbnb/lottie/compose/LottieAnimationState;)V
+HPLcom/airbnb/lottie/benchmark/app/BenchmarkActivity$Content$1$1;->invoke()Ljava/lang/Float;
+HPLcom/airbnb/lottie/benchmark/app/BenchmarkActivity$Content$1$1;->invoke()Ljava/lang/Object;
+Lcom/airbnb/lottie/benchmark/app/BenchmarkActivity$Content$2;
+HSPLcom/airbnb/lottie/benchmark/app/BenchmarkActivity$Content$2;-><init>(Lcom/airbnb/lottie/benchmark/app/BenchmarkActivity;I)V
+HSPLcom/airbnb/lottie/benchmark/app/BenchmarkActivity$Content$2;->invoke(Landroidx/compose/runtime/Composer;I)V
+HSPLcom/airbnb/lottie/benchmark/app/BenchmarkActivity$Content$2;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
+Lcom/airbnb/lottie/benchmark/app/BenchmarkActivity$onCreate$1;
+HSPLcom/airbnb/lottie/benchmark/app/BenchmarkActivity$onCreate$1;-><init>(Lcom/airbnb/lottie/benchmark/app/BenchmarkActivity;)V
+HSPLcom/airbnb/lottie/benchmark/app/BenchmarkActivity$onCreate$1;->invoke(Landroidx/compose/runtime/Composer;I)V
+HSPLcom/airbnb/lottie/benchmark/app/BenchmarkActivity$onCreate$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
+Lcom/airbnb/lottie/benchmark/app/R$raw;
+Lcom/airbnb/lottie/manager/ImageAssetManager;
+HSPLcom/airbnb/lottie/manager/ImageAssetManager;-><clinit>()V
+HSPLcom/airbnb/lottie/manager/ImageAssetManager;-><init>(Landroid/graphics/drawable/Drawable$Callback;Ljava/lang/String;Lcom/airbnb/lottie/ImageAssetDelegate;Ljava/util/Map;)V
+HPLcom/airbnb/lottie/manager/ImageAssetManager;->bitmapForId(Ljava/lang/String;)Landroid/graphics/Bitmap;
+HPLcom/airbnb/lottie/manager/ImageAssetManager;->hasSameContext(Landroid/content/Context;)Z
+HSPLcom/airbnb/lottie/manager/ImageAssetManager;->setDelegate(Lcom/airbnb/lottie/ImageAssetDelegate;)V
+Lcom/airbnb/lottie/model/CubicCurveData;
HSPLcom/airbnb/lottie/model/CubicCurveData;-><init>()V
HSPLcom/airbnb/lottie/model/CubicCurveData;-><init>(Landroid/graphics/PointF;Landroid/graphics/PointF;Landroid/graphics/PointF;)V
HSPLcom/airbnb/lottie/model/CubicCurveData;->getControlPoint1()Landroid/graphics/PointF;
@@ -246,30 +335,65 @@ HSPLcom/airbnb/lottie/model/CubicCurveData;->getVertex()Landroid/graphics/PointF
HSPLcom/airbnb/lottie/model/CubicCurveData;->setControlPoint1(FF)V
HSPLcom/airbnb/lottie/model/CubicCurveData;->setControlPoint2(FF)V
HSPLcom/airbnb/lottie/model/CubicCurveData;->setVertex(FF)V
+Lcom/airbnb/lottie/model/DocumentData;
+HSPLcom/airbnb/lottie/model/DocumentData;-><init>(Ljava/lang/String;Ljava/lang/String;FLcom/airbnb/lottie/model/DocumentData$Justification;IFFIIFZLandroid/graphics/PointF;Landroid/graphics/PointF;)V
+HSPLcom/airbnb/lottie/model/DocumentData;->set(Ljava/lang/String;Ljava/lang/String;FLcom/airbnb/lottie/model/DocumentData$Justification;IFFIIFZLandroid/graphics/PointF;Landroid/graphics/PointF;)V
+Lcom/airbnb/lottie/model/DocumentData$Justification;
+HSPLcom/airbnb/lottie/model/DocumentData$Justification;->$values()[Lcom/airbnb/lottie/model/DocumentData$Justification;
+HSPLcom/airbnb/lottie/model/DocumentData$Justification;-><clinit>()V
+HSPLcom/airbnb/lottie/model/DocumentData$Justification;-><init>(Ljava/lang/String;I)V
+HSPLcom/airbnb/lottie/model/DocumentData$Justification;->values()[Lcom/airbnb/lottie/model/DocumentData$Justification;
+Lcom/airbnb/lottie/model/Font;
+HSPLcom/airbnb/lottie/model/Font;-><init>(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;F)V
+HPLcom/airbnb/lottie/model/Font;->getFamily()Ljava/lang/String;
+HSPLcom/airbnb/lottie/model/Font;->getName()Ljava/lang/String;
+HPLcom/airbnb/lottie/model/Font;->getStyle()Ljava/lang/String;
+Lcom/airbnb/lottie/model/FontCharacter;
+HSPLcom/airbnb/lottie/model/FontCharacter;-><init>(Ljava/util/List;CDDLjava/lang/String;Ljava/lang/String;)V
+HSPLcom/airbnb/lottie/model/FontCharacter;->getShapes()Ljava/util/List;
+HPLcom/airbnb/lottie/model/FontCharacter;->getWidth()D
+HPLcom/airbnb/lottie/model/FontCharacter;->hashCode()I
+HPLcom/airbnb/lottie/model/FontCharacter;->hashFor(CLjava/lang/String;Ljava/lang/String;)I
+Lcom/airbnb/lottie/model/KeyPathElement;
+Lcom/airbnb/lottie/model/LottieCompositionCache;
HSPLcom/airbnb/lottie/model/LottieCompositionCache;-><clinit>()V
HSPLcom/airbnb/lottie/model/LottieCompositionCache;-><init>()V
HSPLcom/airbnb/lottie/model/LottieCompositionCache;->get(Ljava/lang/String;)Lcom/airbnb/lottie/LottieComposition;
HSPLcom/airbnb/lottie/model/LottieCompositionCache;->getInstance()Lcom/airbnb/lottie/model/LottieCompositionCache;
HSPLcom/airbnb/lottie/model/LottieCompositionCache;->put(Ljava/lang/String;Lcom/airbnb/lottie/LottieComposition;)V
+Lcom/airbnb/lottie/model/animatable/AnimatableColorValue;
HSPLcom/airbnb/lottie/model/animatable/AnimatableColorValue;-><init>(Ljava/util/List;)V
HSPLcom/airbnb/lottie/model/animatable/AnimatableColorValue;->createAnimation()Lcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation;
+Lcom/airbnb/lottie/model/animatable/AnimatableFloatValue;
HSPLcom/airbnb/lottie/model/animatable/AnimatableFloatValue;-><init>(Ljava/util/List;)V
HSPLcom/airbnb/lottie/model/animatable/AnimatableFloatValue;->createAnimation()Lcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation;
HSPLcom/airbnb/lottie/model/animatable/AnimatableFloatValue;->getKeyframes()Ljava/util/List;
HSPLcom/airbnb/lottie/model/animatable/AnimatableFloatValue;->isStatic()Z
+Lcom/airbnb/lottie/model/animatable/AnimatableIntegerValue;
HSPLcom/airbnb/lottie/model/animatable/AnimatableIntegerValue;-><init>(Ljava/util/List;)V
HSPLcom/airbnb/lottie/model/animatable/AnimatableIntegerValue;->createAnimation()Lcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation;
+Lcom/airbnb/lottie/model/animatable/AnimatablePathValue;
HSPLcom/airbnb/lottie/model/animatable/AnimatablePathValue;-><init>(Ljava/util/List;)V
HSPLcom/airbnb/lottie/model/animatable/AnimatablePathValue;->createAnimation()Lcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation;
HSPLcom/airbnb/lottie/model/animatable/AnimatablePathValue;->getKeyframes()Ljava/util/List;
HSPLcom/airbnb/lottie/model/animatable/AnimatablePathValue;->isStatic()Z
+Lcom/airbnb/lottie/model/animatable/AnimatablePointValue;
HSPLcom/airbnb/lottie/model/animatable/AnimatablePointValue;-><init>(Ljava/util/List;)V
HSPLcom/airbnb/lottie/model/animatable/AnimatablePointValue;->createAnimation()Lcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation;
+Lcom/airbnb/lottie/model/animatable/AnimatableScaleValue;
HSPLcom/airbnb/lottie/model/animatable/AnimatableScaleValue;-><init>(Ljava/util/List;)V
+HSPLcom/airbnb/lottie/model/animatable/AnimatableScaleValue;->createAnimation()Lcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation;
HSPLcom/airbnb/lottie/model/animatable/AnimatableScaleValue;->getKeyframes()Ljava/util/List;
HSPLcom/airbnb/lottie/model/animatable/AnimatableScaleValue;->isStatic()Z
+Lcom/airbnb/lottie/model/animatable/AnimatableShapeValue;
HSPLcom/airbnb/lottie/model/animatable/AnimatableShapeValue;-><init>(Ljava/util/List;)V
HSPLcom/airbnb/lottie/model/animatable/AnimatableShapeValue;->createAnimation()Lcom/airbnb/lottie/animation/keyframe/ShapeKeyframeAnimation;
+Lcom/airbnb/lottie/model/animatable/AnimatableSplitDimensionPathValue;
+Lcom/airbnb/lottie/model/animatable/AnimatableTextFrame;
+HSPLcom/airbnb/lottie/model/animatable/AnimatableTextFrame;-><init>(Ljava/util/List;)V
+HSPLcom/airbnb/lottie/model/animatable/AnimatableTextFrame;->createAnimation()Lcom/airbnb/lottie/animation/keyframe/TextKeyframeAnimation;
+Lcom/airbnb/lottie/model/animatable/AnimatableTextProperties;
+Lcom/airbnb/lottie/model/animatable/AnimatableTransform;
HSPLcom/airbnb/lottie/model/animatable/AnimatableTransform;-><init>()V
HSPLcom/airbnb/lottie/model/animatable/AnimatableTransform;-><init>(Lcom/airbnb/lottie/model/animatable/AnimatablePathValue;Lcom/airbnb/lottie/model/animatable/AnimatableValue;Lcom/airbnb/lottie/model/animatable/AnimatableScaleValue;Lcom/airbnb/lottie/model/animatable/AnimatableFloatValue;Lcom/airbnb/lottie/model/animatable/AnimatableIntegerValue;Lcom/airbnb/lottie/model/animatable/AnimatableFloatValue;Lcom/airbnb/lottie/model/animatable/AnimatableFloatValue;Lcom/airbnb/lottie/model/animatable/AnimatableFloatValue;Lcom/airbnb/lottie/model/animatable/AnimatableFloatValue;)V
HSPLcom/airbnb/lottie/model/animatable/AnimatableTransform;->createAnimation()Lcom/airbnb/lottie/animation/keyframe/TransformKeyframeAnimation;
@@ -282,53 +406,101 @@ HSPLcom/airbnb/lottie/model/animatable/AnimatableTransform;->getScale()Lcom/airb
HSPLcom/airbnb/lottie/model/animatable/AnimatableTransform;->getSkew()Lcom/airbnb/lottie/model/animatable/AnimatableFloatValue;
HSPLcom/airbnb/lottie/model/animatable/AnimatableTransform;->getSkewAngle()Lcom/airbnb/lottie/model/animatable/AnimatableFloatValue;
HSPLcom/airbnb/lottie/model/animatable/AnimatableTransform;->getStartOpacity()Lcom/airbnb/lottie/model/animatable/AnimatableFloatValue;
-HSPLcom/airbnb/lottie/model/animatable/AnimatableTransform;->toContent(Lcom/airbnb/lottie/LottieDrawable;Lcom/airbnb/lottie/model/layer/BaseLayer;)Lcom/airbnb/lottie/animation/content/Content;
+HSPLcom/airbnb/lottie/model/animatable/AnimatableTransform;->isAutoOrient()Z
+HSPLcom/airbnb/lottie/model/animatable/AnimatableTransform;->toContent(Lcom/airbnb/lottie/LottieDrawable;Lcom/airbnb/lottie/LottieComposition;Lcom/airbnb/lottie/model/layer/BaseLayer;)Lcom/airbnb/lottie/animation/content/Content;
+Lcom/airbnb/lottie/model/animatable/AnimatableValue;
+Lcom/airbnb/lottie/model/animatable/BaseAnimatableValue;
HSPLcom/airbnb/lottie/model/animatable/BaseAnimatableValue;-><init>(Ljava/util/List;)V
HSPLcom/airbnb/lottie/model/animatable/BaseAnimatableValue;->getKeyframes()Ljava/util/List;
HSPLcom/airbnb/lottie/model/animatable/BaseAnimatableValue;->isStatic()Z
+Lcom/airbnb/lottie/model/content/BlurEffect;
+Lcom/airbnb/lottie/model/content/CircleShape;
HSPLcom/airbnb/lottie/model/content/CircleShape;-><init>(Ljava/lang/String;Lcom/airbnb/lottie/model/animatable/AnimatableValue;Lcom/airbnb/lottie/model/animatable/AnimatablePointValue;ZZ)V
HSPLcom/airbnb/lottie/model/content/CircleShape;->getName()Ljava/lang/String;
HSPLcom/airbnb/lottie/model/content/CircleShape;->getPosition()Lcom/airbnb/lottie/model/animatable/AnimatableValue;
HSPLcom/airbnb/lottie/model/content/CircleShape;->getSize()Lcom/airbnb/lottie/model/animatable/AnimatablePointValue;
HSPLcom/airbnb/lottie/model/content/CircleShape;->isHidden()Z
HSPLcom/airbnb/lottie/model/content/CircleShape;->isReversed()Z
-HSPLcom/airbnb/lottie/model/content/CircleShape;->toContent(Lcom/airbnb/lottie/LottieDrawable;Lcom/airbnb/lottie/model/layer/BaseLayer;)Lcom/airbnb/lottie/animation/content/Content;
+HSPLcom/airbnb/lottie/model/content/CircleShape;->toContent(Lcom/airbnb/lottie/LottieDrawable;Lcom/airbnb/lottie/LottieComposition;Lcom/airbnb/lottie/model/layer/BaseLayer;)Lcom/airbnb/lottie/animation/content/Content;
+Lcom/airbnb/lottie/model/content/ContentModel;
+Lcom/airbnb/lottie/model/content/LBlendMode;
+HSPLcom/airbnb/lottie/model/content/LBlendMode;->$values()[Lcom/airbnb/lottie/model/content/LBlendMode;
+HSPLcom/airbnb/lottie/model/content/LBlendMode;-><clinit>()V
+HSPLcom/airbnb/lottie/model/content/LBlendMode;-><init>(Ljava/lang/String;I)V
+HSPLcom/airbnb/lottie/model/content/LBlendMode;->toNativeBlendMode()Landroidx/core/graphics/BlendModeCompat;
+HSPLcom/airbnb/lottie/model/content/LBlendMode;->values()[Lcom/airbnb/lottie/model/content/LBlendMode;
+Lcom/airbnb/lottie/model/content/LBlendMode$1;
+HSPLcom/airbnb/lottie/model/content/LBlendMode$1;-><clinit>()V
+Lcom/airbnb/lottie/model/content/Mask$MaskMode;
+HSPLcom/airbnb/lottie/model/content/Mask$MaskMode;->$values()[Lcom/airbnb/lottie/model/content/Mask$MaskMode;
HSPLcom/airbnb/lottie/model/content/Mask$MaskMode;-><clinit>()V
HSPLcom/airbnb/lottie/model/content/Mask$MaskMode;-><init>(Ljava/lang/String;I)V
HSPLcom/airbnb/lottie/model/content/Mask$MaskMode;->values()[Lcom/airbnb/lottie/model/content/Mask$MaskMode;
+Lcom/airbnb/lottie/model/content/PolystarShape;
+HSPLcom/airbnb/lottie/model/content/PolystarShape;-><init>(Ljava/lang/String;Lcom/airbnb/lottie/model/content/PolystarShape$Type;Lcom/airbnb/lottie/model/animatable/AnimatableFloatValue;Lcom/airbnb/lottie/model/animatable/AnimatableValue;Lcom/airbnb/lottie/model/animatable/AnimatableFloatValue;Lcom/airbnb/lottie/model/animatable/AnimatableFloatValue;Lcom/airbnb/lottie/model/animatable/AnimatableFloatValue;Lcom/airbnb/lottie/model/animatable/AnimatableFloatValue;Lcom/airbnb/lottie/model/animatable/AnimatableFloatValue;ZZ)V
+HSPLcom/airbnb/lottie/model/content/PolystarShape;->getInnerRadius()Lcom/airbnb/lottie/model/animatable/AnimatableFloatValue;
+HSPLcom/airbnb/lottie/model/content/PolystarShape;->getInnerRoundedness()Lcom/airbnb/lottie/model/animatable/AnimatableFloatValue;
+HSPLcom/airbnb/lottie/model/content/PolystarShape;->getName()Ljava/lang/String;
+HSPLcom/airbnb/lottie/model/content/PolystarShape;->getOuterRadius()Lcom/airbnb/lottie/model/animatable/AnimatableFloatValue;
+HSPLcom/airbnb/lottie/model/content/PolystarShape;->getOuterRoundedness()Lcom/airbnb/lottie/model/animatable/AnimatableFloatValue;
+HSPLcom/airbnb/lottie/model/content/PolystarShape;->getPoints()Lcom/airbnb/lottie/model/animatable/AnimatableFloatValue;
+HSPLcom/airbnb/lottie/model/content/PolystarShape;->getPosition()Lcom/airbnb/lottie/model/animatable/AnimatableValue;
+HSPLcom/airbnb/lottie/model/content/PolystarShape;->getRotation()Lcom/airbnb/lottie/model/animatable/AnimatableFloatValue;
+HSPLcom/airbnb/lottie/model/content/PolystarShape;->getType()Lcom/airbnb/lottie/model/content/PolystarShape$Type;
+HSPLcom/airbnb/lottie/model/content/PolystarShape;->isHidden()Z
+HSPLcom/airbnb/lottie/model/content/PolystarShape;->isReversed()Z
+HSPLcom/airbnb/lottie/model/content/PolystarShape;->toContent(Lcom/airbnb/lottie/LottieDrawable;Lcom/airbnb/lottie/LottieComposition;Lcom/airbnb/lottie/model/layer/BaseLayer;)Lcom/airbnb/lottie/animation/content/Content;
+Lcom/airbnb/lottie/model/content/PolystarShape$Type;
+HSPLcom/airbnb/lottie/model/content/PolystarShape$Type;->$values()[Lcom/airbnb/lottie/model/content/PolystarShape$Type;
+HSPLcom/airbnb/lottie/model/content/PolystarShape$Type;-><clinit>()V
+HSPLcom/airbnb/lottie/model/content/PolystarShape$Type;-><init>(Ljava/lang/String;II)V
+HSPLcom/airbnb/lottie/model/content/PolystarShape$Type;->forValue(I)Lcom/airbnb/lottie/model/content/PolystarShape$Type;
+HSPLcom/airbnb/lottie/model/content/PolystarShape$Type;->values()[Lcom/airbnb/lottie/model/content/PolystarShape$Type;
+Lcom/airbnb/lottie/model/content/RectangleShape;
+HSPLcom/airbnb/lottie/model/content/RectangleShape;-><init>(Ljava/lang/String;Lcom/airbnb/lottie/model/animatable/AnimatableValue;Lcom/airbnb/lottie/model/animatable/AnimatableValue;Lcom/airbnb/lottie/model/animatable/AnimatableFloatValue;Z)V
+HSPLcom/airbnb/lottie/model/content/RectangleShape;->getCornerRadius()Lcom/airbnb/lottie/model/animatable/AnimatableFloatValue;
+HSPLcom/airbnb/lottie/model/content/RectangleShape;->getName()Ljava/lang/String;
+HSPLcom/airbnb/lottie/model/content/RectangleShape;->getPosition()Lcom/airbnb/lottie/model/animatable/AnimatableValue;
+HSPLcom/airbnb/lottie/model/content/RectangleShape;->getSize()Lcom/airbnb/lottie/model/animatable/AnimatableValue;
+HSPLcom/airbnb/lottie/model/content/RectangleShape;->isHidden()Z
+HSPLcom/airbnb/lottie/model/content/RectangleShape;->toContent(Lcom/airbnb/lottie/LottieDrawable;Lcom/airbnb/lottie/LottieComposition;Lcom/airbnb/lottie/model/layer/BaseLayer;)Lcom/airbnb/lottie/animation/content/Content;
+Lcom/airbnb/lottie/model/content/Repeater;
+HSPLcom/airbnb/lottie/model/content/Repeater;-><init>(Ljava/lang/String;Lcom/airbnb/lottie/model/animatable/AnimatableFloatValue;Lcom/airbnb/lottie/model/animatable/AnimatableFloatValue;Lcom/airbnb/lottie/model/animatable/AnimatableTransform;Z)V
+HSPLcom/airbnb/lottie/model/content/Repeater;->getCopies()Lcom/airbnb/lottie/model/animatable/AnimatableFloatValue;
+HSPLcom/airbnb/lottie/model/content/Repeater;->getName()Ljava/lang/String;
+HSPLcom/airbnb/lottie/model/content/Repeater;->getOffset()Lcom/airbnb/lottie/model/animatable/AnimatableFloatValue;
+HSPLcom/airbnb/lottie/model/content/Repeater;->getTransform()Lcom/airbnb/lottie/model/animatable/AnimatableTransform;
+HSPLcom/airbnb/lottie/model/content/Repeater;->isHidden()Z
+HSPLcom/airbnb/lottie/model/content/Repeater;->toContent(Lcom/airbnb/lottie/LottieDrawable;Lcom/airbnb/lottie/LottieComposition;Lcom/airbnb/lottie/model/layer/BaseLayer;)Lcom/airbnb/lottie/animation/content/Content;
+Lcom/airbnb/lottie/model/content/ShapeData;
HSPLcom/airbnb/lottie/model/content/ShapeData;-><init>()V
HSPLcom/airbnb/lottie/model/content/ShapeData;-><init>(Landroid/graphics/PointF;ZLjava/util/List;)V
HSPLcom/airbnb/lottie/model/content/ShapeData;->getCurves()Ljava/util/List;
HSPLcom/airbnb/lottie/model/content/ShapeData;->getInitialPoint()Landroid/graphics/PointF;
-HSPLcom/airbnb/lottie/model/content/ShapeData;->interpolateBetween(Lcom/airbnb/lottie/model/content/ShapeData;Lcom/airbnb/lottie/model/content/ShapeData;F)V
+HPLcom/airbnb/lottie/model/content/ShapeData;->interpolateBetween(Lcom/airbnb/lottie/model/content/ShapeData;Lcom/airbnb/lottie/model/content/ShapeData;F)V
HSPLcom/airbnb/lottie/model/content/ShapeData;->isClosed()Z
HSPLcom/airbnb/lottie/model/content/ShapeData;->setInitialPoint(FF)V
+Lcom/airbnb/lottie/model/content/ShapeFill;
HSPLcom/airbnb/lottie/model/content/ShapeFill;-><init>(Ljava/lang/String;ZLandroid/graphics/Path$FillType;Lcom/airbnb/lottie/model/animatable/AnimatableColorValue;Lcom/airbnb/lottie/model/animatable/AnimatableIntegerValue;Z)V
HSPLcom/airbnb/lottie/model/content/ShapeFill;->getColor()Lcom/airbnb/lottie/model/animatable/AnimatableColorValue;
HSPLcom/airbnb/lottie/model/content/ShapeFill;->getFillType()Landroid/graphics/Path$FillType;
HSPLcom/airbnb/lottie/model/content/ShapeFill;->getName()Ljava/lang/String;
HSPLcom/airbnb/lottie/model/content/ShapeFill;->getOpacity()Lcom/airbnb/lottie/model/animatable/AnimatableIntegerValue;
HSPLcom/airbnb/lottie/model/content/ShapeFill;->isHidden()Z
-HSPLcom/airbnb/lottie/model/content/ShapeFill;->toContent(Lcom/airbnb/lottie/LottieDrawable;Lcom/airbnb/lottie/model/layer/BaseLayer;)Lcom/airbnb/lottie/animation/content/Content;
+HSPLcom/airbnb/lottie/model/content/ShapeFill;->toContent(Lcom/airbnb/lottie/LottieDrawable;Lcom/airbnb/lottie/LottieComposition;Lcom/airbnb/lottie/model/layer/BaseLayer;)Lcom/airbnb/lottie/animation/content/Content;
+Lcom/airbnb/lottie/model/content/ShapeGroup;
HSPLcom/airbnb/lottie/model/content/ShapeGroup;-><init>(Ljava/lang/String;Ljava/util/List;Z)V
HSPLcom/airbnb/lottie/model/content/ShapeGroup;->getItems()Ljava/util/List;
HSPLcom/airbnb/lottie/model/content/ShapeGroup;->getName()Ljava/lang/String;
HSPLcom/airbnb/lottie/model/content/ShapeGroup;->isHidden()Z
-HSPLcom/airbnb/lottie/model/content/ShapeGroup;->toContent(Lcom/airbnb/lottie/LottieDrawable;Lcom/airbnb/lottie/model/layer/BaseLayer;)Lcom/airbnb/lottie/animation/content/Content;
+HSPLcom/airbnb/lottie/model/content/ShapeGroup;->toContent(Lcom/airbnb/lottie/LottieDrawable;Lcom/airbnb/lottie/LottieComposition;Lcom/airbnb/lottie/model/layer/BaseLayer;)Lcom/airbnb/lottie/animation/content/Content;
+Lcom/airbnb/lottie/model/content/ShapePath;
HSPLcom/airbnb/lottie/model/content/ShapePath;-><init>(Ljava/lang/String;ILcom/airbnb/lottie/model/animatable/AnimatableShapeValue;Z)V
HSPLcom/airbnb/lottie/model/content/ShapePath;->getName()Ljava/lang/String;
HSPLcom/airbnb/lottie/model/content/ShapePath;->getShapePath()Lcom/airbnb/lottie/model/animatable/AnimatableShapeValue;
HSPLcom/airbnb/lottie/model/content/ShapePath;->isHidden()Z
-HSPLcom/airbnb/lottie/model/content/ShapePath;->toContent(Lcom/airbnb/lottie/LottieDrawable;Lcom/airbnb/lottie/model/layer/BaseLayer;)Lcom/airbnb/lottie/animation/content/Content;
-HSPLcom/airbnb/lottie/model/content/ShapeStroke$1;-><clinit>()V
-HSPLcom/airbnb/lottie/model/content/ShapeStroke$LineCapType;-><clinit>()V
-HSPLcom/airbnb/lottie/model/content/ShapeStroke$LineCapType;-><init>(Ljava/lang/String;I)V
-HSPLcom/airbnb/lottie/model/content/ShapeStroke$LineCapType;->toPaintCap()Landroid/graphics/Paint$Cap;
-HSPLcom/airbnb/lottie/model/content/ShapeStroke$LineCapType;->values()[Lcom/airbnb/lottie/model/content/ShapeStroke$LineCapType;
-HSPLcom/airbnb/lottie/model/content/ShapeStroke$LineJoinType;-><clinit>()V
-HSPLcom/airbnb/lottie/model/content/ShapeStroke$LineJoinType;-><init>(Ljava/lang/String;I)V
-HSPLcom/airbnb/lottie/model/content/ShapeStroke$LineJoinType;->toPaintJoin()Landroid/graphics/Paint$Join;
-HSPLcom/airbnb/lottie/model/content/ShapeStroke$LineJoinType;->values()[Lcom/airbnb/lottie/model/content/ShapeStroke$LineJoinType;
+HSPLcom/airbnb/lottie/model/content/ShapePath;->toContent(Lcom/airbnb/lottie/LottieDrawable;Lcom/airbnb/lottie/LottieComposition;Lcom/airbnb/lottie/model/layer/BaseLayer;)Lcom/airbnb/lottie/animation/content/Content;
+Lcom/airbnb/lottie/model/content/ShapeStroke;
HSPLcom/airbnb/lottie/model/content/ShapeStroke;-><init>(Ljava/lang/String;Lcom/airbnb/lottie/model/animatable/AnimatableFloatValue;Ljava/util/List;Lcom/airbnb/lottie/model/animatable/AnimatableColorValue;Lcom/airbnb/lottie/model/animatable/AnimatableIntegerValue;Lcom/airbnb/lottie/model/animatable/AnimatableFloatValue;Lcom/airbnb/lottie/model/content/ShapeStroke$LineCapType;Lcom/airbnb/lottie/model/content/ShapeStroke$LineJoinType;FZ)V
HSPLcom/airbnb/lottie/model/content/ShapeStroke;->getCapType()Lcom/airbnb/lottie/model/content/ShapeStroke$LineCapType;
HSPLcom/airbnb/lottie/model/content/ShapeStroke;->getColor()Lcom/airbnb/lottie/model/animatable/AnimatableColorValue;
@@ -340,85 +512,136 @@ HSPLcom/airbnb/lottie/model/content/ShapeStroke;->getName()Ljava/lang/String;
HSPLcom/airbnb/lottie/model/content/ShapeStroke;->getOpacity()Lcom/airbnb/lottie/model/animatable/AnimatableIntegerValue;
HSPLcom/airbnb/lottie/model/content/ShapeStroke;->getWidth()Lcom/airbnb/lottie/model/animatable/AnimatableFloatValue;
HSPLcom/airbnb/lottie/model/content/ShapeStroke;->isHidden()Z
-HSPLcom/airbnb/lottie/model/content/ShapeStroke;->toContent(Lcom/airbnb/lottie/LottieDrawable;Lcom/airbnb/lottie/model/layer/BaseLayer;)Lcom/airbnb/lottie/animation/content/Content;
-HSPLcom/airbnb/lottie/model/layer/BaseLayer$$ExternalSyntheticLambda0;-><init>(Lcom/airbnb/lottie/model/layer/BaseLayer;)V
-HSPLcom/airbnb/lottie/model/layer/BaseLayer$1;-><clinit>()V
+HSPLcom/airbnb/lottie/model/content/ShapeStroke;->toContent(Lcom/airbnb/lottie/LottieDrawable;Lcom/airbnb/lottie/LottieComposition;Lcom/airbnb/lottie/model/layer/BaseLayer;)Lcom/airbnb/lottie/animation/content/Content;
+Lcom/airbnb/lottie/model/content/ShapeStroke$1;
+HSPLcom/airbnb/lottie/model/content/ShapeStroke$1;-><clinit>()V
+Lcom/airbnb/lottie/model/content/ShapeStroke$LineCapType;
+HSPLcom/airbnb/lottie/model/content/ShapeStroke$LineCapType;->$values()[Lcom/airbnb/lottie/model/content/ShapeStroke$LineCapType;
+HSPLcom/airbnb/lottie/model/content/ShapeStroke$LineCapType;-><clinit>()V
+HSPLcom/airbnb/lottie/model/content/ShapeStroke$LineCapType;-><init>(Ljava/lang/String;I)V
+HSPLcom/airbnb/lottie/model/content/ShapeStroke$LineCapType;->toPaintCap()Landroid/graphics/Paint$Cap;
+HSPLcom/airbnb/lottie/model/content/ShapeStroke$LineCapType;->values()[Lcom/airbnb/lottie/model/content/ShapeStroke$LineCapType;
+Lcom/airbnb/lottie/model/content/ShapeStroke$LineJoinType;
+HSPLcom/airbnb/lottie/model/content/ShapeStroke$LineJoinType;->$values()[Lcom/airbnb/lottie/model/content/ShapeStroke$LineJoinType;
+HSPLcom/airbnb/lottie/model/content/ShapeStroke$LineJoinType;-><clinit>()V
+HSPLcom/airbnb/lottie/model/content/ShapeStroke$LineJoinType;-><init>(Ljava/lang/String;I)V
+HSPLcom/airbnb/lottie/model/content/ShapeStroke$LineJoinType;->toPaintJoin()Landroid/graphics/Paint$Join;
+HSPLcom/airbnb/lottie/model/content/ShapeStroke$LineJoinType;->values()[Lcom/airbnb/lottie/model/content/ShapeStroke$LineJoinType;
+Lcom/airbnb/lottie/model/layer/BaseLayer;
HSPLcom/airbnb/lottie/model/layer/BaseLayer;-><init>(Lcom/airbnb/lottie/LottieDrawable;Lcom/airbnb/lottie/model/layer/Layer;)V
HSPLcom/airbnb/lottie/model/layer/BaseLayer;->addAnimation(Lcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation;)V
-HSPLcom/airbnb/lottie/model/layer/BaseLayer;->buildParentLayerListIfNeeded()V
-HSPLcom/airbnb/lottie/model/layer/BaseLayer;->draw(Landroid/graphics/Canvas;Landroid/graphics/Matrix;I)V
+HPLcom/airbnb/lottie/model/layer/BaseLayer;->buildParentLayerListIfNeeded()V
+HPLcom/airbnb/lottie/model/layer/BaseLayer;->clearCanvas(Landroid/graphics/Canvas;)V
+HPLcom/airbnb/lottie/model/layer/BaseLayer;->draw(Landroid/graphics/Canvas;Landroid/graphics/Matrix;I)V
HSPLcom/airbnb/lottie/model/layer/BaseLayer;->forModel(Lcom/airbnb/lottie/model/layer/CompositionLayer;Lcom/airbnb/lottie/model/layer/Layer;Lcom/airbnb/lottie/LottieDrawable;Lcom/airbnb/lottie/LottieComposition;)Lcom/airbnb/lottie/model/layer/BaseLayer;
+HSPLcom/airbnb/lottie/model/layer/BaseLayer;->getBlendMode()Lcom/airbnb/lottie/model/content/LBlendMode;
HSPLcom/airbnb/lottie/model/layer/BaseLayer;->getBlurEffect()Lcom/airbnb/lottie/model/content/BlurEffect;
+HPLcom/airbnb/lottie/model/layer/BaseLayer;->getBounds(Landroid/graphics/RectF;Landroid/graphics/Matrix;Z)V
HSPLcom/airbnb/lottie/model/layer/BaseLayer;->getDropShadowEffect()Lcom/airbnb/lottie/parser/DropShadowEffect;
HSPLcom/airbnb/lottie/model/layer/BaseLayer;->getLayerModel()Lcom/airbnb/lottie/model/layer/Layer;
-HSPLcom/airbnb/lottie/model/layer/BaseLayer;->hasMasksOnThisLayer()Z
-HSPLcom/airbnb/lottie/model/layer/BaseLayer;->hasMatteOnThisLayer()Z
+HPLcom/airbnb/lottie/model/layer/BaseLayer;->hasMasksOnThisLayer()Z
+HPLcom/airbnb/lottie/model/layer/BaseLayer;->hasMatteOnThisLayer()Z
+HPLcom/airbnb/lottie/model/layer/BaseLayer;->intersectBoundsWithMask(Landroid/graphics/RectF;Landroid/graphics/Matrix;)V
+HPLcom/airbnb/lottie/model/layer/BaseLayer;->intersectBoundsWithMatte(Landroid/graphics/RectF;Landroid/graphics/Matrix;)V
HSPLcom/airbnb/lottie/model/layer/BaseLayer;->invalidateSelf()V
HSPLcom/airbnb/lottie/model/layer/BaseLayer;->onValueChanged()V
-HSPLcom/airbnb/lottie/model/layer/BaseLayer;->recordRenderTime(F)V
+HPLcom/airbnb/lottie/model/layer/BaseLayer;->recordRenderTime(F)V
+HSPLcom/airbnb/lottie/model/layer/BaseLayer;->setMatteLayer(Lcom/airbnb/lottie/model/layer/BaseLayer;)V
HSPLcom/airbnb/lottie/model/layer/BaseLayer;->setParentLayer(Lcom/airbnb/lottie/model/layer/BaseLayer;)V
HSPLcom/airbnb/lottie/model/layer/BaseLayer;->setProgress(F)V
HSPLcom/airbnb/lottie/model/layer/BaseLayer;->setVisible(Z)V
HSPLcom/airbnb/lottie/model/layer/BaseLayer;->setupInOutAnimations()V
-HSPLcom/airbnb/lottie/model/layer/CompositionLayer$1;-><clinit>()V
+Lcom/airbnb/lottie/model/layer/BaseLayer$$ExternalSyntheticLambda0;
+HSPLcom/airbnb/lottie/model/layer/BaseLayer$$ExternalSyntheticLambda0;-><init>(Lcom/airbnb/lottie/model/layer/BaseLayer;)V
+Lcom/airbnb/lottie/model/layer/BaseLayer$1;
+HSPLcom/airbnb/lottie/model/layer/BaseLayer$1;-><clinit>()V
+Lcom/airbnb/lottie/model/layer/CompositionLayer;
HSPLcom/airbnb/lottie/model/layer/CompositionLayer;-><init>(Lcom/airbnb/lottie/LottieDrawable;Lcom/airbnb/lottie/model/layer/Layer;Ljava/util/List;Lcom/airbnb/lottie/LottieComposition;)V
-HSPLcom/airbnb/lottie/model/layer/CompositionLayer;->drawLayer(Landroid/graphics/Canvas;Landroid/graphics/Matrix;I)V
-HSPLcom/airbnb/lottie/model/layer/CompositionLayer;->setProgress(F)V
-HSPLcom/airbnb/lottie/model/layer/Layer$LayerType;-><clinit>()V
-HSPLcom/airbnb/lottie/model/layer/Layer$LayerType;-><init>(Ljava/lang/String;I)V
-HSPLcom/airbnb/lottie/model/layer/Layer$LayerType;->values()[Lcom/airbnb/lottie/model/layer/Layer$LayerType;
-HSPLcom/airbnb/lottie/model/layer/Layer$MatteType;-><clinit>()V
-HSPLcom/airbnb/lottie/model/layer/Layer$MatteType;-><init>(Ljava/lang/String;I)V
-HSPLcom/airbnb/lottie/model/layer/Layer$MatteType;->values()[Lcom/airbnb/lottie/model/layer/Layer$MatteType;
-HSPLcom/airbnb/lottie/model/layer/Layer;-><init>(Ljava/util/List;Lcom/airbnb/lottie/LottieComposition;Ljava/lang/String;JLcom/airbnb/lottie/model/layer/Layer$LayerType;JLjava/lang/String;Ljava/util/List;Lcom/airbnb/lottie/model/animatable/AnimatableTransform;IIIFFIILcom/airbnb/lottie/model/animatable/AnimatableTextFrame;Lcom/airbnb/lottie/model/animatable/AnimatableTextProperties;Ljava/util/List;Lcom/airbnb/lottie/model/layer/Layer$MatteType;Lcom/airbnb/lottie/model/animatable/AnimatableFloatValue;ZLcom/airbnb/lottie/model/content/BlurEffect;Lcom/airbnb/lottie/parser/DropShadowEffect;)V
+HPLcom/airbnb/lottie/model/layer/CompositionLayer;->drawLayer(Landroid/graphics/Canvas;Landroid/graphics/Matrix;I)V
+HSPLcom/airbnb/lottie/model/layer/CompositionLayer;->setClipToCompositionBounds(Z)V
+HPLcom/airbnb/lottie/model/layer/CompositionLayer;->setProgress(F)V
+Lcom/airbnb/lottie/model/layer/CompositionLayer$1;
+HSPLcom/airbnb/lottie/model/layer/CompositionLayer$1;-><clinit>()V
+Lcom/airbnb/lottie/model/layer/ImageLayer;
+HSPLcom/airbnb/lottie/model/layer/ImageLayer;-><init>(Lcom/airbnb/lottie/LottieDrawable;Lcom/airbnb/lottie/model/layer/Layer;)V
+HPLcom/airbnb/lottie/model/layer/ImageLayer;->drawLayer(Landroid/graphics/Canvas;Landroid/graphics/Matrix;I)V
+HPLcom/airbnb/lottie/model/layer/ImageLayer;->getBitmap()Landroid/graphics/Bitmap;
+Lcom/airbnb/lottie/model/layer/Layer;
+HSPLcom/airbnb/lottie/model/layer/Layer;-><init>(Ljava/util/List;Lcom/airbnb/lottie/LottieComposition;Ljava/lang/String;JLcom/airbnb/lottie/model/layer/Layer$LayerType;JLjava/lang/String;Ljava/util/List;Lcom/airbnb/lottie/model/animatable/AnimatableTransform;IIIFFFFLcom/airbnb/lottie/model/animatable/AnimatableTextFrame;Lcom/airbnb/lottie/model/animatable/AnimatableTextProperties;Ljava/util/List;Lcom/airbnb/lottie/model/layer/Layer$MatteType;Lcom/airbnb/lottie/model/animatable/AnimatableFloatValue;ZLcom/airbnb/lottie/model/content/BlurEffect;Lcom/airbnb/lottie/parser/DropShadowEffect;Lcom/airbnb/lottie/model/content/LBlendMode;)V
+HSPLcom/airbnb/lottie/model/layer/Layer;->getBlendMode()Lcom/airbnb/lottie/model/content/LBlendMode;
HSPLcom/airbnb/lottie/model/layer/Layer;->getBlurEffect()Lcom/airbnb/lottie/model/content/BlurEffect;
+HSPLcom/airbnb/lottie/model/layer/Layer;->getComposition()Lcom/airbnb/lottie/LottieComposition;
HSPLcom/airbnb/lottie/model/layer/Layer;->getDropShadowEffect()Lcom/airbnb/lottie/parser/DropShadowEffect;
HSPLcom/airbnb/lottie/model/layer/Layer;->getId()J
HSPLcom/airbnb/lottie/model/layer/Layer;->getInOutKeyframes()Ljava/util/List;
HSPLcom/airbnb/lottie/model/layer/Layer;->getLayerType()Lcom/airbnb/lottie/model/layer/Layer$LayerType;
HSPLcom/airbnb/lottie/model/layer/Layer;->getMasks()Ljava/util/List;
-HSPLcom/airbnb/lottie/model/layer/Layer;->getMatteType()Lcom/airbnb/lottie/model/layer/Layer$MatteType;
-HSPLcom/airbnb/lottie/model/layer/Layer;->getName()Ljava/lang/String;
+HPLcom/airbnb/lottie/model/layer/Layer;->getMatteType()Lcom/airbnb/lottie/model/layer/Layer$MatteType;
+HPLcom/airbnb/lottie/model/layer/Layer;->getName()Ljava/lang/String;
HSPLcom/airbnb/lottie/model/layer/Layer;->getParentId()J
-HSPLcom/airbnb/lottie/model/layer/Layer;->getPreCompHeight()I
-HSPLcom/airbnb/lottie/model/layer/Layer;->getPreCompWidth()I
+HSPLcom/airbnb/lottie/model/layer/Layer;->getPreCompHeight()F
+HPLcom/airbnb/lottie/model/layer/Layer;->getPreCompWidth()F
HSPLcom/airbnb/lottie/model/layer/Layer;->getRefId()Ljava/lang/String;
HSPLcom/airbnb/lottie/model/layer/Layer;->getShapes()Ljava/util/List;
-HSPLcom/airbnb/lottie/model/layer/Layer;->getStartProgress()F
+HSPLcom/airbnb/lottie/model/layer/Layer;->getSolidColor()I
+HSPLcom/airbnb/lottie/model/layer/Layer;->getSolidHeight()I
+HPLcom/airbnb/lottie/model/layer/Layer;->getSolidWidth()I
+HPLcom/airbnb/lottie/model/layer/Layer;->getStartProgress()F
+HSPLcom/airbnb/lottie/model/layer/Layer;->getText()Lcom/airbnb/lottie/model/animatable/AnimatableTextFrame;
+HSPLcom/airbnb/lottie/model/layer/Layer;->getTextProperties()Lcom/airbnb/lottie/model/animatable/AnimatableTextProperties;
HSPLcom/airbnb/lottie/model/layer/Layer;->getTimeRemapping()Lcom/airbnb/lottie/model/animatable/AnimatableFloatValue;
HSPLcom/airbnb/lottie/model/layer/Layer;->getTimeStretch()F
HSPLcom/airbnb/lottie/model/layer/Layer;->getTransform()Lcom/airbnb/lottie/model/animatable/AnimatableTransform;
-HSPLcom/airbnb/lottie/model/layer/Layer;->isHidden()Z
-HSPLcom/airbnb/lottie/model/layer/ShapeLayer;-><init>(Lcom/airbnb/lottie/LottieDrawable;Lcom/airbnb/lottie/model/layer/Layer;Lcom/airbnb/lottie/model/layer/CompositionLayer;)V
-HSPLcom/airbnb/lottie/model/layer/ShapeLayer;->drawLayer(Landroid/graphics/Canvas;Landroid/graphics/Matrix;I)V
+HPLcom/airbnb/lottie/model/layer/Layer;->isHidden()Z
+Lcom/airbnb/lottie/model/layer/Layer$LayerType;
+HSPLcom/airbnb/lottie/model/layer/Layer$LayerType;->$values()[Lcom/airbnb/lottie/model/layer/Layer$LayerType;
+HSPLcom/airbnb/lottie/model/layer/Layer$LayerType;-><clinit>()V
+HSPLcom/airbnb/lottie/model/layer/Layer$LayerType;-><init>(Ljava/lang/String;I)V
+HSPLcom/airbnb/lottie/model/layer/Layer$LayerType;->values()[Lcom/airbnb/lottie/model/layer/Layer$LayerType;
+Lcom/airbnb/lottie/model/layer/Layer$MatteType;
+HSPLcom/airbnb/lottie/model/layer/Layer$MatteType;->$values()[Lcom/airbnb/lottie/model/layer/Layer$MatteType;
+HSPLcom/airbnb/lottie/model/layer/Layer$MatteType;-><clinit>()V
+HSPLcom/airbnb/lottie/model/layer/Layer$MatteType;-><init>(Ljava/lang/String;I)V
+HSPLcom/airbnb/lottie/model/layer/Layer$MatteType;->values()[Lcom/airbnb/lottie/model/layer/Layer$MatteType;
+Lcom/airbnb/lottie/model/layer/ShapeLayer;
+HSPLcom/airbnb/lottie/model/layer/ShapeLayer;-><init>(Lcom/airbnb/lottie/LottieDrawable;Lcom/airbnb/lottie/model/layer/Layer;Lcom/airbnb/lottie/model/layer/CompositionLayer;Lcom/airbnb/lottie/LottieComposition;)V
+HPLcom/airbnb/lottie/model/layer/ShapeLayer;->drawLayer(Landroid/graphics/Canvas;Landroid/graphics/Matrix;I)V
HSPLcom/airbnb/lottie/model/layer/ShapeLayer;->getBlurEffect()Lcom/airbnb/lottie/model/content/BlurEffect;
+HPLcom/airbnb/lottie/model/layer/ShapeLayer;->getBounds(Landroid/graphics/RectF;Landroid/graphics/Matrix;Z)V
HSPLcom/airbnb/lottie/model/layer/ShapeLayer;->getDropShadowEffect()Lcom/airbnb/lottie/parser/DropShadowEffect;
-HSPLcom/airbnb/lottie/network/DefaultLottieFetchResult;-><init>(Ljava/net/HttpURLConnection;)V
-HSPLcom/airbnb/lottie/network/DefaultLottieFetchResult;->bodyByteStream()Ljava/io/InputStream;
-HSPLcom/airbnb/lottie/network/DefaultLottieFetchResult;->close()V
-HSPLcom/airbnb/lottie/network/DefaultLottieFetchResult;->contentType()Ljava/lang/String;
-HSPLcom/airbnb/lottie/network/DefaultLottieFetchResult;->isSuccessful()Z
-HSPLcom/airbnb/lottie/network/DefaultLottieNetworkFetcher;-><init>()V
-HSPLcom/airbnb/lottie/network/DefaultLottieNetworkFetcher;->fetchSync(Ljava/lang/String;)Lcom/airbnb/lottie/network/LottieFetchResult;
-HSPLcom/airbnb/lottie/network/FileExtension;-><clinit>()V
-HSPLcom/airbnb/lottie/network/FileExtension;-><init>(Ljava/lang/String;ILjava/lang/String;)V
-HSPLcom/airbnb/lottie/network/FileExtension;->tempExtension()Ljava/lang/String;
-HSPLcom/airbnb/lottie/network/NetworkCache;-><init>(Lcom/airbnb/lottie/network/LottieNetworkCacheProvider;)V
-HSPLcom/airbnb/lottie/network/NetworkCache;->fetch(Ljava/lang/String;)Landroid/util/Pair;
-HSPLcom/airbnb/lottie/network/NetworkCache;->filenameForUrl(Ljava/lang/String;Lcom/airbnb/lottie/network/FileExtension;Z)Ljava/lang/String;
-HSPLcom/airbnb/lottie/network/NetworkCache;->getCachedFile(Ljava/lang/String;)Ljava/io/File;
-HSPLcom/airbnb/lottie/network/NetworkCache;->parentDir()Ljava/io/File;
-HSPLcom/airbnb/lottie/network/NetworkCache;->renameTempFile(Ljava/lang/String;Lcom/airbnb/lottie/network/FileExtension;)V
-HSPLcom/airbnb/lottie/network/NetworkCache;->writeTempCacheFile(Ljava/lang/String;Ljava/io/InputStream;Lcom/airbnb/lottie/network/FileExtension;)Ljava/io/File;
-HSPLcom/airbnb/lottie/network/NetworkFetcher;-><init>(Lcom/airbnb/lottie/network/NetworkCache;Lcom/airbnb/lottie/network/LottieNetworkFetcher;)V
-HSPLcom/airbnb/lottie/network/NetworkFetcher;->fetchFromCache(Ljava/lang/String;Ljava/lang/String;)Lcom/airbnb/lottie/LottieComposition;
-HSPLcom/airbnb/lottie/network/NetworkFetcher;->fetchFromNetwork(Ljava/lang/String;Ljava/lang/String;)Lcom/airbnb/lottie/LottieResult;
-HSPLcom/airbnb/lottie/network/NetworkFetcher;->fetchSync(Ljava/lang/String;Ljava/lang/String;)Lcom/airbnb/lottie/LottieResult;
-HSPLcom/airbnb/lottie/network/NetworkFetcher;->fromInputStream(Ljava/lang/String;Ljava/io/InputStream;Ljava/lang/String;Ljava/lang/String;)Lcom/airbnb/lottie/LottieResult;
-HSPLcom/airbnb/lottie/network/NetworkFetcher;->fromJsonStream(Ljava/lang/String;Ljava/io/InputStream;Ljava/lang/String;)Lcom/airbnb/lottie/LottieResult;
+Lcom/airbnb/lottie/model/layer/SolidLayer;
+HSPLcom/airbnb/lottie/model/layer/SolidLayer;-><init>(Lcom/airbnb/lottie/LottieDrawable;Lcom/airbnb/lottie/model/layer/Layer;)V
+HPLcom/airbnb/lottie/model/layer/SolidLayer;->drawLayer(Landroid/graphics/Canvas;Landroid/graphics/Matrix;I)V
+Lcom/airbnb/lottie/model/layer/TextLayer;
+HSPLcom/airbnb/lottie/model/layer/TextLayer;-><init>(Lcom/airbnb/lottie/LottieDrawable;Lcom/airbnb/lottie/model/layer/Layer;)V
+HPLcom/airbnb/lottie/model/layer/TextLayer;->configurePaint(Lcom/airbnb/lottie/model/DocumentData;I)V
+HSPLcom/airbnb/lottie/model/layer/TextLayer;->drawCharacterAsGlyph(Lcom/airbnb/lottie/model/FontCharacter;FLcom/airbnb/lottie/model/DocumentData;Landroid/graphics/Canvas;)V
+HPLcom/airbnb/lottie/model/layer/TextLayer;->drawGlyph(Landroid/graphics/Path;Landroid/graphics/Paint;Landroid/graphics/Canvas;)V
+HPLcom/airbnb/lottie/model/layer/TextLayer;->drawGlyphTextLine(Ljava/lang/String;Lcom/airbnb/lottie/model/DocumentData;Lcom/airbnb/lottie/model/Font;Landroid/graphics/Canvas;FFF)V
+HPLcom/airbnb/lottie/model/layer/TextLayer;->drawLayer(Landroid/graphics/Canvas;Landroid/graphics/Matrix;I)V
+HPLcom/airbnb/lottie/model/layer/TextLayer;->drawTextWithGlyphs(Lcom/airbnb/lottie/model/DocumentData;Landroid/graphics/Matrix;Lcom/airbnb/lottie/model/Font;Landroid/graphics/Canvas;)V
+HPLcom/airbnb/lottie/model/layer/TextLayer;->ensureEnoughSubLines(I)Lcom/airbnb/lottie/model/layer/TextLayer$TextSubLine;
+HPLcom/airbnb/lottie/model/layer/TextLayer;->getContentsForCharacter(Lcom/airbnb/lottie/model/FontCharacter;)Ljava/util/List;
+HPLcom/airbnb/lottie/model/layer/TextLayer;->getTextLines(Ljava/lang/String;)Ljava/util/List;
+HPLcom/airbnb/lottie/model/layer/TextLayer;->offsetCanvas(Landroid/graphics/Canvas;Lcom/airbnb/lottie/model/DocumentData;IF)V
+HPLcom/airbnb/lottie/model/layer/TextLayer;->splitGlyphTextIntoLines(Ljava/lang/String;FLcom/airbnb/lottie/model/Font;FFZ)Ljava/util/List;
+Lcom/airbnb/lottie/model/layer/TextLayer$1;
+HSPLcom/airbnb/lottie/model/layer/TextLayer$1;-><init>(Lcom/airbnb/lottie/model/layer/TextLayer;I)V
+Lcom/airbnb/lottie/model/layer/TextLayer$2;
+HSPLcom/airbnb/lottie/model/layer/TextLayer$2;-><init>(Lcom/airbnb/lottie/model/layer/TextLayer;I)V
+Lcom/airbnb/lottie/model/layer/TextLayer$3;
+HSPLcom/airbnb/lottie/model/layer/TextLayer$3;-><clinit>()V
+Lcom/airbnb/lottie/model/layer/TextLayer$TextSubLine;
+HSPLcom/airbnb/lottie/model/layer/TextLayer$TextSubLine;-><init>()V
+HSPLcom/airbnb/lottie/model/layer/TextLayer$TextSubLine;-><init>(Lcom/airbnb/lottie/model/layer/TextLayer$1;)V
+HPLcom/airbnb/lottie/model/layer/TextLayer$TextSubLine;->access$000(Lcom/airbnb/lottie/model/layer/TextLayer$TextSubLine;)F
+HPLcom/airbnb/lottie/model/layer/TextLayer$TextSubLine;->access$100(Lcom/airbnb/lottie/model/layer/TextLayer$TextSubLine;)Ljava/lang/String;
+HPLcom/airbnb/lottie/model/layer/TextLayer$TextSubLine;->set(Ljava/lang/String;F)V
+Lcom/airbnb/lottie/parser/AnimatablePathValueParser;
HSPLcom/airbnb/lottie/parser/AnimatablePathValueParser;-><clinit>()V
HSPLcom/airbnb/lottie/parser/AnimatablePathValueParser;->parse(Lcom/airbnb/lottie/parser/moshi/JsonReader;Lcom/airbnb/lottie/LottieComposition;)Lcom/airbnb/lottie/model/animatable/AnimatablePathValue;
HSPLcom/airbnb/lottie/parser/AnimatablePathValueParser;->parseSplitPath(Lcom/airbnb/lottie/parser/moshi/JsonReader;Lcom/airbnb/lottie/LottieComposition;)Lcom/airbnb/lottie/model/animatable/AnimatableValue;
+Lcom/airbnb/lottie/parser/AnimatableTransformParser;
HSPLcom/airbnb/lottie/parser/AnimatableTransformParser;-><clinit>()V
HSPLcom/airbnb/lottie/parser/AnimatableTransformParser;->isAnchorPointIdentity(Lcom/airbnb/lottie/model/animatable/AnimatablePathValue;)Z
HSPLcom/airbnb/lottie/parser/AnimatableTransformParser;->isPositionIdentity(Lcom/airbnb/lottie/model/animatable/AnimatableValue;)Z
@@ -427,100 +650,150 @@ HSPLcom/airbnb/lottie/parser/AnimatableTransformParser;->isScaleIdentity(Lcom/ai
HSPLcom/airbnb/lottie/parser/AnimatableTransformParser;->isSkewAngleIdentity(Lcom/airbnb/lottie/model/animatable/AnimatableFloatValue;)Z
HSPLcom/airbnb/lottie/parser/AnimatableTransformParser;->isSkewIdentity(Lcom/airbnb/lottie/model/animatable/AnimatableFloatValue;)Z
HSPLcom/airbnb/lottie/parser/AnimatableTransformParser;->parse(Lcom/airbnb/lottie/parser/moshi/JsonReader;Lcom/airbnb/lottie/LottieComposition;)Lcom/airbnb/lottie/model/animatable/AnimatableTransform;
+Lcom/airbnb/lottie/parser/AnimatableValueParser;
HSPLcom/airbnb/lottie/parser/AnimatableValueParser;->parse(Lcom/airbnb/lottie/parser/moshi/JsonReader;FLcom/airbnb/lottie/LottieComposition;Lcom/airbnb/lottie/parser/ValueParser;)Ljava/util/List;
HSPLcom/airbnb/lottie/parser/AnimatableValueParser;->parse(Lcom/airbnb/lottie/parser/moshi/JsonReader;Lcom/airbnb/lottie/LottieComposition;Lcom/airbnb/lottie/parser/ValueParser;)Ljava/util/List;
HSPLcom/airbnb/lottie/parser/AnimatableValueParser;->parseColor(Lcom/airbnb/lottie/parser/moshi/JsonReader;Lcom/airbnb/lottie/LottieComposition;)Lcom/airbnb/lottie/model/animatable/AnimatableColorValue;
+HSPLcom/airbnb/lottie/parser/AnimatableValueParser;->parseDocumentData(Lcom/airbnb/lottie/parser/moshi/JsonReader;Lcom/airbnb/lottie/LottieComposition;)Lcom/airbnb/lottie/model/animatable/AnimatableTextFrame;
HSPLcom/airbnb/lottie/parser/AnimatableValueParser;->parseFloat(Lcom/airbnb/lottie/parser/moshi/JsonReader;Lcom/airbnb/lottie/LottieComposition;)Lcom/airbnb/lottie/model/animatable/AnimatableFloatValue;
HSPLcom/airbnb/lottie/parser/AnimatableValueParser;->parseFloat(Lcom/airbnb/lottie/parser/moshi/JsonReader;Lcom/airbnb/lottie/LottieComposition;Z)Lcom/airbnb/lottie/model/animatable/AnimatableFloatValue;
HSPLcom/airbnb/lottie/parser/AnimatableValueParser;->parseInteger(Lcom/airbnb/lottie/parser/moshi/JsonReader;Lcom/airbnb/lottie/LottieComposition;)Lcom/airbnb/lottie/model/animatable/AnimatableIntegerValue;
HSPLcom/airbnb/lottie/parser/AnimatableValueParser;->parsePoint(Lcom/airbnb/lottie/parser/moshi/JsonReader;Lcom/airbnb/lottie/LottieComposition;)Lcom/airbnb/lottie/model/animatable/AnimatablePointValue;
HSPLcom/airbnb/lottie/parser/AnimatableValueParser;->parseScale(Lcom/airbnb/lottie/parser/moshi/JsonReader;Lcom/airbnb/lottie/LottieComposition;)Lcom/airbnb/lottie/model/animatable/AnimatableScaleValue;
HSPLcom/airbnb/lottie/parser/AnimatableValueParser;->parseShapeData(Lcom/airbnb/lottie/parser/moshi/JsonReader;Lcom/airbnb/lottie/LottieComposition;)Lcom/airbnb/lottie/model/animatable/AnimatableShapeValue;
+Lcom/airbnb/lottie/parser/CircleShapeParser;
HSPLcom/airbnb/lottie/parser/CircleShapeParser;-><clinit>()V
HSPLcom/airbnb/lottie/parser/CircleShapeParser;->parse(Lcom/airbnb/lottie/parser/moshi/JsonReader;Lcom/airbnb/lottie/LottieComposition;I)Lcom/airbnb/lottie/model/content/CircleShape;
+Lcom/airbnb/lottie/parser/ColorParser;
HSPLcom/airbnb/lottie/parser/ColorParser;-><clinit>()V
HSPLcom/airbnb/lottie/parser/ColorParser;-><init>()V
HSPLcom/airbnb/lottie/parser/ColorParser;->parse(Lcom/airbnb/lottie/parser/moshi/JsonReader;F)Ljava/lang/Integer;
HSPLcom/airbnb/lottie/parser/ColorParser;->parse(Lcom/airbnb/lottie/parser/moshi/JsonReader;F)Ljava/lang/Object;
+Lcom/airbnb/lottie/parser/ContentModelParser;
HSPLcom/airbnb/lottie/parser/ContentModelParser;-><clinit>()V
HSPLcom/airbnb/lottie/parser/ContentModelParser;->parse(Lcom/airbnb/lottie/parser/moshi/JsonReader;Lcom/airbnb/lottie/LottieComposition;)Lcom/airbnb/lottie/model/content/ContentModel;
+Lcom/airbnb/lottie/parser/DocumentDataParser;
+HSPLcom/airbnb/lottie/parser/DocumentDataParser;-><clinit>()V
+HSPLcom/airbnb/lottie/parser/DocumentDataParser;-><init>()V
+HSPLcom/airbnb/lottie/parser/DocumentDataParser;->parse(Lcom/airbnb/lottie/parser/moshi/JsonReader;F)Lcom/airbnb/lottie/model/DocumentData;
+HSPLcom/airbnb/lottie/parser/DocumentDataParser;->parse(Lcom/airbnb/lottie/parser/moshi/JsonReader;F)Ljava/lang/Object;
+Lcom/airbnb/lottie/parser/DropShadowEffect;
+Lcom/airbnb/lottie/parser/FloatParser;
HSPLcom/airbnb/lottie/parser/FloatParser;-><clinit>()V
HSPLcom/airbnb/lottie/parser/FloatParser;-><init>()V
HSPLcom/airbnb/lottie/parser/FloatParser;->parse(Lcom/airbnb/lottie/parser/moshi/JsonReader;F)Ljava/lang/Float;
HSPLcom/airbnb/lottie/parser/FloatParser;->parse(Lcom/airbnb/lottie/parser/moshi/JsonReader;F)Ljava/lang/Object;
+Lcom/airbnb/lottie/parser/FontCharacterParser;
+HSPLcom/airbnb/lottie/parser/FontCharacterParser;-><clinit>()V
+HSPLcom/airbnb/lottie/parser/FontCharacterParser;->parse(Lcom/airbnb/lottie/parser/moshi/JsonReader;Lcom/airbnb/lottie/LottieComposition;)Lcom/airbnb/lottie/model/FontCharacter;
+Lcom/airbnb/lottie/parser/FontParser;
+HSPLcom/airbnb/lottie/parser/FontParser;-><clinit>()V
+HSPLcom/airbnb/lottie/parser/FontParser;->parse(Lcom/airbnb/lottie/parser/moshi/JsonReader;)Lcom/airbnb/lottie/model/Font;
+Lcom/airbnb/lottie/parser/IntegerParser;
HSPLcom/airbnb/lottie/parser/IntegerParser;-><clinit>()V
HSPLcom/airbnb/lottie/parser/IntegerParser;-><init>()V
HSPLcom/airbnb/lottie/parser/IntegerParser;->parse(Lcom/airbnb/lottie/parser/moshi/JsonReader;F)Ljava/lang/Integer;
HSPLcom/airbnb/lottie/parser/IntegerParser;->parse(Lcom/airbnb/lottie/parser/moshi/JsonReader;F)Ljava/lang/Object;
-HSPLcom/airbnb/lottie/parser/JsonUtils$1;-><clinit>()V
+Lcom/airbnb/lottie/parser/JsonUtils;
HSPLcom/airbnb/lottie/parser/JsonUtils;-><clinit>()V
HSPLcom/airbnb/lottie/parser/JsonUtils;->jsonArrayToPoint(Lcom/airbnb/lottie/parser/moshi/JsonReader;F)Landroid/graphics/PointF;
HSPLcom/airbnb/lottie/parser/JsonUtils;->jsonNumbersToPoint(Lcom/airbnb/lottie/parser/moshi/JsonReader;F)Landroid/graphics/PointF;
HSPLcom/airbnb/lottie/parser/JsonUtils;->jsonObjectToPoint(Lcom/airbnb/lottie/parser/moshi/JsonReader;F)Landroid/graphics/PointF;
+HSPLcom/airbnb/lottie/parser/JsonUtils;->jsonToColor(Lcom/airbnb/lottie/parser/moshi/JsonReader;)I
HSPLcom/airbnb/lottie/parser/JsonUtils;->jsonToPoint(Lcom/airbnb/lottie/parser/moshi/JsonReader;F)Landroid/graphics/PointF;
HSPLcom/airbnb/lottie/parser/JsonUtils;->jsonToPoints(Lcom/airbnb/lottie/parser/moshi/JsonReader;F)Ljava/util/List;
HSPLcom/airbnb/lottie/parser/JsonUtils;->valueFromObject(Lcom/airbnb/lottie/parser/moshi/JsonReader;)F
+Lcom/airbnb/lottie/parser/JsonUtils$1;
+HSPLcom/airbnb/lottie/parser/JsonUtils$1;-><clinit>()V
+Lcom/airbnb/lottie/parser/KeyframeParser;
HSPLcom/airbnb/lottie/parser/KeyframeParser;-><clinit>()V
-HSPLcom/airbnb/lottie/parser/KeyframeParser;->getInterpolator(I)Ljava/lang/ref/WeakReference;
HSPLcom/airbnb/lottie/parser/KeyframeParser;->interpolatorFor(Landroid/graphics/PointF;Landroid/graphics/PointF;)Landroid/view/animation/Interpolator;
HSPLcom/airbnb/lottie/parser/KeyframeParser;->parse(Lcom/airbnb/lottie/parser/moshi/JsonReader;Lcom/airbnb/lottie/LottieComposition;FLcom/airbnb/lottie/parser/ValueParser;ZZ)Lcom/airbnb/lottie/value/Keyframe;
HSPLcom/airbnb/lottie/parser/KeyframeParser;->parseKeyframe(Lcom/airbnb/lottie/LottieComposition;Lcom/airbnb/lottie/parser/moshi/JsonReader;FLcom/airbnb/lottie/parser/ValueParser;)Lcom/airbnb/lottie/value/Keyframe;
HSPLcom/airbnb/lottie/parser/KeyframeParser;->parseStaticValue(Lcom/airbnb/lottie/parser/moshi/JsonReader;FLcom/airbnb/lottie/parser/ValueParser;)Lcom/airbnb/lottie/value/Keyframe;
-HSPLcom/airbnb/lottie/parser/KeyframeParser;->pathInterpolatorCache()Landroidx/collection/SparseArrayCompat;
-HSPLcom/airbnb/lottie/parser/KeyframeParser;->putInterpolator(ILjava/lang/ref/WeakReference;)V
+Lcom/airbnb/lottie/parser/KeyframesParser;
HSPLcom/airbnb/lottie/parser/KeyframesParser;-><clinit>()V
HSPLcom/airbnb/lottie/parser/KeyframesParser;->parse(Lcom/airbnb/lottie/parser/moshi/JsonReader;Lcom/airbnb/lottie/LottieComposition;FLcom/airbnb/lottie/parser/ValueParser;Z)Ljava/util/List;
HSPLcom/airbnb/lottie/parser/KeyframesParser;->setEndFrames(Ljava/util/List;)V
+Lcom/airbnb/lottie/parser/LayerParser;
HSPLcom/airbnb/lottie/parser/LayerParser;-><clinit>()V
HSPLcom/airbnb/lottie/parser/LayerParser;->parse(Lcom/airbnb/lottie/LottieComposition;)Lcom/airbnb/lottie/model/layer/Layer;
HSPLcom/airbnb/lottie/parser/LayerParser;->parse(Lcom/airbnb/lottie/parser/moshi/JsonReader;Lcom/airbnb/lottie/LottieComposition;)Lcom/airbnb/lottie/model/layer/Layer;
+Lcom/airbnb/lottie/parser/LayerParser$1;
+HSPLcom/airbnb/lottie/parser/LayerParser$1;-><clinit>()V
+Lcom/airbnb/lottie/parser/LottieCompositionMoshiParser;
HSPLcom/airbnb/lottie/parser/LottieCompositionMoshiParser;-><clinit>()V
HSPLcom/airbnb/lottie/parser/LottieCompositionMoshiParser;->parse(Lcom/airbnb/lottie/parser/moshi/JsonReader;)Lcom/airbnb/lottie/LottieComposition;
HSPLcom/airbnb/lottie/parser/LottieCompositionMoshiParser;->parseAssets(Lcom/airbnb/lottie/parser/moshi/JsonReader;Lcom/airbnb/lottie/LottieComposition;Ljava/util/Map;Ljava/util/Map;)V
+HSPLcom/airbnb/lottie/parser/LottieCompositionMoshiParser;->parseChars(Lcom/airbnb/lottie/parser/moshi/JsonReader;Lcom/airbnb/lottie/LottieComposition;Landroidx/collection/SparseArrayCompat;)V
+HSPLcom/airbnb/lottie/parser/LottieCompositionMoshiParser;->parseFonts(Lcom/airbnb/lottie/parser/moshi/JsonReader;Ljava/util/Map;)V
HSPLcom/airbnb/lottie/parser/LottieCompositionMoshiParser;->parseLayers(Lcom/airbnb/lottie/parser/moshi/JsonReader;Lcom/airbnb/lottie/LottieComposition;Ljava/util/List;Landroidx/collection/LongSparseArray;)V
HSPLcom/airbnb/lottie/parser/LottieCompositionMoshiParser;->parseMarkers(Lcom/airbnb/lottie/parser/moshi/JsonReader;Ljava/util/List;)V
+Lcom/airbnb/lottie/parser/PathKeyframeParser;
HSPLcom/airbnb/lottie/parser/PathKeyframeParser;->parse(Lcom/airbnb/lottie/parser/moshi/JsonReader;Lcom/airbnb/lottie/LottieComposition;)Lcom/airbnb/lottie/animation/keyframe/PathKeyframe;
+Lcom/airbnb/lottie/parser/PathParser;
HSPLcom/airbnb/lottie/parser/PathParser;-><clinit>()V
HSPLcom/airbnb/lottie/parser/PathParser;-><init>()V
HSPLcom/airbnb/lottie/parser/PathParser;->parse(Lcom/airbnb/lottie/parser/moshi/JsonReader;F)Landroid/graphics/PointF;
HSPLcom/airbnb/lottie/parser/PathParser;->parse(Lcom/airbnb/lottie/parser/moshi/JsonReader;F)Ljava/lang/Object;
+Lcom/airbnb/lottie/parser/PointFParser;
HSPLcom/airbnb/lottie/parser/PointFParser;-><clinit>()V
HSPLcom/airbnb/lottie/parser/PointFParser;-><init>()V
HSPLcom/airbnb/lottie/parser/PointFParser;->parse(Lcom/airbnb/lottie/parser/moshi/JsonReader;F)Landroid/graphics/PointF;
HSPLcom/airbnb/lottie/parser/PointFParser;->parse(Lcom/airbnb/lottie/parser/moshi/JsonReader;F)Ljava/lang/Object;
+Lcom/airbnb/lottie/parser/PolystarShapeParser;
+HSPLcom/airbnb/lottie/parser/PolystarShapeParser;-><clinit>()V
+HSPLcom/airbnb/lottie/parser/PolystarShapeParser;->parse(Lcom/airbnb/lottie/parser/moshi/JsonReader;Lcom/airbnb/lottie/LottieComposition;I)Lcom/airbnb/lottie/model/content/PolystarShape;
+Lcom/airbnb/lottie/parser/RectangleShapeParser;
+HSPLcom/airbnb/lottie/parser/RectangleShapeParser;-><clinit>()V
+HSPLcom/airbnb/lottie/parser/RectangleShapeParser;->parse(Lcom/airbnb/lottie/parser/moshi/JsonReader;Lcom/airbnb/lottie/LottieComposition;)Lcom/airbnb/lottie/model/content/RectangleShape;
+Lcom/airbnb/lottie/parser/RepeaterParser;
+HSPLcom/airbnb/lottie/parser/RepeaterParser;-><clinit>()V
+HSPLcom/airbnb/lottie/parser/RepeaterParser;->parse(Lcom/airbnb/lottie/parser/moshi/JsonReader;Lcom/airbnb/lottie/LottieComposition;)Lcom/airbnb/lottie/model/content/Repeater;
+Lcom/airbnb/lottie/parser/ScaleXYParser;
HSPLcom/airbnb/lottie/parser/ScaleXYParser;-><clinit>()V
HSPLcom/airbnb/lottie/parser/ScaleXYParser;-><init>()V
HSPLcom/airbnb/lottie/parser/ScaleXYParser;->parse(Lcom/airbnb/lottie/parser/moshi/JsonReader;F)Lcom/airbnb/lottie/value/ScaleXY;
HSPLcom/airbnb/lottie/parser/ScaleXYParser;->parse(Lcom/airbnb/lottie/parser/moshi/JsonReader;F)Ljava/lang/Object;
+Lcom/airbnb/lottie/parser/ShapeDataParser;
HSPLcom/airbnb/lottie/parser/ShapeDataParser;-><clinit>()V
HSPLcom/airbnb/lottie/parser/ShapeDataParser;-><init>()V
HSPLcom/airbnb/lottie/parser/ShapeDataParser;->parse(Lcom/airbnb/lottie/parser/moshi/JsonReader;F)Lcom/airbnb/lottie/model/content/ShapeData;
HSPLcom/airbnb/lottie/parser/ShapeDataParser;->parse(Lcom/airbnb/lottie/parser/moshi/JsonReader;F)Ljava/lang/Object;
+Lcom/airbnb/lottie/parser/ShapeFillParser;
HSPLcom/airbnb/lottie/parser/ShapeFillParser;-><clinit>()V
HSPLcom/airbnb/lottie/parser/ShapeFillParser;->parse(Lcom/airbnb/lottie/parser/moshi/JsonReader;Lcom/airbnb/lottie/LottieComposition;)Lcom/airbnb/lottie/model/content/ShapeFill;
+Lcom/airbnb/lottie/parser/ShapeGroupParser;
HSPLcom/airbnb/lottie/parser/ShapeGroupParser;-><clinit>()V
HSPLcom/airbnb/lottie/parser/ShapeGroupParser;->parse(Lcom/airbnb/lottie/parser/moshi/JsonReader;Lcom/airbnb/lottie/LottieComposition;)Lcom/airbnb/lottie/model/content/ShapeGroup;
+Lcom/airbnb/lottie/parser/ShapePathParser;
HSPLcom/airbnb/lottie/parser/ShapePathParser;-><clinit>()V
HSPLcom/airbnb/lottie/parser/ShapePathParser;->parse(Lcom/airbnb/lottie/parser/moshi/JsonReader;Lcom/airbnb/lottie/LottieComposition;)Lcom/airbnb/lottie/model/content/ShapePath;
+Lcom/airbnb/lottie/parser/ShapeStrokeParser;
HSPLcom/airbnb/lottie/parser/ShapeStrokeParser;-><clinit>()V
HSPLcom/airbnb/lottie/parser/ShapeStrokeParser;->parse(Lcom/airbnb/lottie/parser/moshi/JsonReader;Lcom/airbnb/lottie/LottieComposition;)Lcom/airbnb/lottie/model/content/ShapeStroke;
-HSPLcom/airbnb/lottie/parser/moshi/JsonReader$Options;-><init>([Ljava/lang/String;Lokio/Options;)V
-HSPLcom/airbnb/lottie/parser/moshi/JsonReader$Options;->of([Ljava/lang/String;)Lcom/airbnb/lottie/parser/moshi/JsonReader$Options;
-HSPLcom/airbnb/lottie/parser/moshi/JsonReader$Token;-><clinit>()V
-HSPLcom/airbnb/lottie/parser/moshi/JsonReader$Token;-><init>(Ljava/lang/String;I)V
-HSPLcom/airbnb/lottie/parser/moshi/JsonReader$Token;->values()[Lcom/airbnb/lottie/parser/moshi/JsonReader$Token;
+Lcom/airbnb/lottie/parser/ValueParser;
+Lcom/airbnb/lottie/parser/moshi/JsonReader;
HSPLcom/airbnb/lottie/parser/moshi/JsonReader;-><clinit>()V
HSPLcom/airbnb/lottie/parser/moshi/JsonReader;-><init>()V
HSPLcom/airbnb/lottie/parser/moshi/JsonReader;->access$000(Lokio/BufferedSink;Ljava/lang/String;)V
HSPLcom/airbnb/lottie/parser/moshi/JsonReader;->of(Lokio/BufferedSource;)Lcom/airbnb/lottie/parser/moshi/JsonReader;
HSPLcom/airbnb/lottie/parser/moshi/JsonReader;->pushScope(I)V
HSPLcom/airbnb/lottie/parser/moshi/JsonReader;->string(Lokio/BufferedSink;Ljava/lang/String;)V
+Lcom/airbnb/lottie/parser/moshi/JsonReader$Options;
+HSPLcom/airbnb/lottie/parser/moshi/JsonReader$Options;-><init>([Ljava/lang/String;Lokio/Options;)V
+HSPLcom/airbnb/lottie/parser/moshi/JsonReader$Options;->of([Ljava/lang/String;)Lcom/airbnb/lottie/parser/moshi/JsonReader$Options;
+Lcom/airbnb/lottie/parser/moshi/JsonReader$Token;
+HSPLcom/airbnb/lottie/parser/moshi/JsonReader$Token;->$values()[Lcom/airbnb/lottie/parser/moshi/JsonReader$Token;
+HSPLcom/airbnb/lottie/parser/moshi/JsonReader$Token;-><clinit>()V
+HSPLcom/airbnb/lottie/parser/moshi/JsonReader$Token;-><init>(Ljava/lang/String;I)V
+HSPLcom/airbnb/lottie/parser/moshi/JsonReader$Token;->values()[Lcom/airbnb/lottie/parser/moshi/JsonReader$Token;
+Lcom/airbnb/lottie/parser/moshi/JsonUtf8Reader;
HSPLcom/airbnb/lottie/parser/moshi/JsonUtf8Reader;-><clinit>()V
HSPLcom/airbnb/lottie/parser/moshi/JsonUtf8Reader;-><init>(Lokio/BufferedSource;)V
HSPLcom/airbnb/lottie/parser/moshi/JsonUtf8Reader;->beginArray()V
HSPLcom/airbnb/lottie/parser/moshi/JsonUtf8Reader;->beginObject()V
HSPLcom/airbnb/lottie/parser/moshi/JsonUtf8Reader;->close()V
-HSPLcom/airbnb/lottie/parser/moshi/JsonUtf8Reader;->doPeek()I
HSPLcom/airbnb/lottie/parser/moshi/JsonUtf8Reader;->endArray()V
HSPLcom/airbnb/lottie/parser/moshi/JsonUtf8Reader;->endObject()V
HSPLcom/airbnb/lottie/parser/moshi/JsonUtf8Reader;->findName(Ljava/lang/String;Lcom/airbnb/lottie/parser/moshi/JsonReader$Options;)I
@@ -535,326 +808,87 @@ HSPLcom/airbnb/lottie/parser/moshi/JsonUtf8Reader;->nextQuotedValue(Lokio/ByteSt
HSPLcom/airbnb/lottie/parser/moshi/JsonUtf8Reader;->nextString()Ljava/lang/String;
HSPLcom/airbnb/lottie/parser/moshi/JsonUtf8Reader;->peek()Lcom/airbnb/lottie/parser/moshi/JsonReader$Token;
HSPLcom/airbnb/lottie/parser/moshi/JsonUtf8Reader;->peekKeyword()I
-HSPLcom/airbnb/lottie/parser/moshi/JsonUtf8Reader;->peekNumber()I
-HSPLcom/airbnb/lottie/parser/moshi/JsonUtf8Reader;->readEscapeCharacter()C
HSPLcom/airbnb/lottie/parser/moshi/JsonUtf8Reader;->selectName(Lcom/airbnb/lottie/parser/moshi/JsonReader$Options;)I
HSPLcom/airbnb/lottie/parser/moshi/JsonUtf8Reader;->skipName()V
HSPLcom/airbnb/lottie/parser/moshi/JsonUtf8Reader;->skipQuotedValue(Lokio/ByteString;)V
HSPLcom/airbnb/lottie/parser/moshi/JsonUtf8Reader;->skipValue()V
+Lcom/airbnb/lottie/utils/BaseLottieAnimator;
HSPLcom/airbnb/lottie/utils/BaseLottieAnimator;-><init>()V
HSPLcom/airbnb/lottie/utils/BaseLottieAnimator;->addUpdateListener(Landroid/animation/ValueAnimator$AnimatorUpdateListener;)V
-HSPLcom/airbnb/lottie/utils/BaseLottieAnimator;->notifyUpdate()V
-HSPLcom/airbnb/lottie/utils/GammaEvaluator;->evaluate(FII)I
+HPLcom/airbnb/lottie/utils/BaseLottieAnimator;->notifyUpdate()V
+Lcom/airbnb/lottie/utils/GammaEvaluator;
+HPLcom/airbnb/lottie/utils/GammaEvaluator;->evaluate(FII)I
+Lcom/airbnb/lottie/utils/LogcatLogger;
HSPLcom/airbnb/lottie/utils/LogcatLogger;-><clinit>()V
HSPLcom/airbnb/lottie/utils/LogcatLogger;-><init>()V
-HSPLcom/airbnb/lottie/utils/LogcatLogger;->debug(Ljava/lang/String;)V
-HSPLcom/airbnb/lottie/utils/LogcatLogger;->debug(Ljava/lang/String;Ljava/lang/Throwable;)V
-HSPLcom/airbnb/lottie/utils/LogcatLogger;->warning(Ljava/lang/String;)V
-HSPLcom/airbnb/lottie/utils/LogcatLogger;->warning(Ljava/lang/String;Ljava/lang/Throwable;)V
+HSPLcom/airbnb/lottie/utils/LogcatLogger;->error(Ljava/lang/String;Ljava/lang/Throwable;)V
+Lcom/airbnb/lottie/utils/Logger;
HSPLcom/airbnb/lottie/utils/Logger;-><clinit>()V
-HSPLcom/airbnb/lottie/utils/Logger;->debug(Ljava/lang/String;)V
-HSPLcom/airbnb/lottie/utils/Logger;->warning(Ljava/lang/String;)V
+HSPLcom/airbnb/lottie/utils/Logger;->error(Ljava/lang/String;Ljava/lang/Throwable;)V
+Lcom/airbnb/lottie/utils/LottieThreadFactory;
+HSPLcom/airbnb/lottie/utils/LottieThreadFactory;-><clinit>()V
+HSPLcom/airbnb/lottie/utils/LottieThreadFactory;-><init>()V
+Lcom/airbnb/lottie/utils/LottieTrace;
+Lcom/airbnb/lottie/utils/LottieValueAnimator;
HSPLcom/airbnb/lottie/utils/LottieValueAnimator;-><init>()V
HSPLcom/airbnb/lottie/utils/LottieValueAnimator;->clearComposition()V
HSPLcom/airbnb/lottie/utils/LottieValueAnimator;->getAnimatedFraction()F
-HSPLcom/airbnb/lottie/utils/LottieValueAnimator;->getAnimatedValueAbsolute()F
-HSPLcom/airbnb/lottie/utils/LottieValueAnimator;->getMaxFrame()F
-HSPLcom/airbnb/lottie/utils/LottieValueAnimator;->getMinFrame()F
+HPLcom/airbnb/lottie/utils/LottieValueAnimator;->getAnimatedValueAbsolute()F
+HPLcom/airbnb/lottie/utils/LottieValueAnimator;->getMaxFrame()F
+HPLcom/airbnb/lottie/utils/LottieValueAnimator;->getMinFrame()F
HSPLcom/airbnb/lottie/utils/LottieValueAnimator;->getSpeed()F
HSPLcom/airbnb/lottie/utils/LottieValueAnimator;->isReversed()Z
HSPLcom/airbnb/lottie/utils/LottieValueAnimator;->isRunning()Z
HSPLcom/airbnb/lottie/utils/LottieValueAnimator;->setComposition(Lcom/airbnb/lottie/LottieComposition;)V
-HSPLcom/airbnb/lottie/utils/LottieValueAnimator;->setFrame(F)V
+HPLcom/airbnb/lottie/utils/LottieValueAnimator;->setFrame(F)V
HSPLcom/airbnb/lottie/utils/LottieValueAnimator;->setMinAndMaxFrames(FF)V
+Lcom/airbnb/lottie/utils/MiscUtils;
HSPLcom/airbnb/lottie/utils/MiscUtils;-><clinit>()V
HSPLcom/airbnb/lottie/utils/MiscUtils;->addPoints(Landroid/graphics/PointF;Landroid/graphics/PointF;)Landroid/graphics/PointF;
-HSPLcom/airbnb/lottie/utils/MiscUtils;->clamp(FFF)F
-HSPLcom/airbnb/lottie/utils/MiscUtils;->clamp(III)I
-HSPLcom/airbnb/lottie/utils/MiscUtils;->getPathFromData(Lcom/airbnb/lottie/model/content/ShapeData;Landroid/graphics/Path;)V
-HSPLcom/airbnb/lottie/utils/MiscUtils;->lerp(FFF)F
+HPLcom/airbnb/lottie/utils/MiscUtils;->clamp(FFF)F
+HPLcom/airbnb/lottie/utils/MiscUtils;->clamp(III)I
+HPLcom/airbnb/lottie/utils/MiscUtils;->getPathFromData(Lcom/airbnb/lottie/model/content/ShapeData;Landroid/graphics/Path;)V
+HPLcom/airbnb/lottie/utils/MiscUtils;->lerp(FFF)F
HSPLcom/airbnb/lottie/utils/MiscUtils;->lerp(IIF)I
-HSPLcom/airbnb/lottie/utils/Utils$1;-><init>()V
-HSPLcom/airbnb/lottie/utils/Utils$2;-><init>()V
-HSPLcom/airbnb/lottie/utils/Utils$3;-><init>()V
-HSPLcom/airbnb/lottie/utils/Utils$4;-><init>()V
-HSPLcom/airbnb/lottie/utils/Utils$4;->initialValue()Ljava/lang/Object;
-HSPLcom/airbnb/lottie/utils/Utils$4;->initialValue()[F
+Lcom/airbnb/lottie/utils/Utils;
HSPLcom/airbnb/lottie/utils/Utils;-><clinit>()V
HSPLcom/airbnb/lottie/utils/Utils;->closeQuietly(Ljava/io/Closeable;)V
HSPLcom/airbnb/lottie/utils/Utils;->createPath(Landroid/graphics/PointF;Landroid/graphics/PointF;Landroid/graphics/PointF;Landroid/graphics/PointF;)Landroid/graphics/Path;
-HSPLcom/airbnb/lottie/utils/Utils;->dpScale()F
+HPLcom/airbnb/lottie/utils/Utils;->dpScale()F
HSPLcom/airbnb/lottie/utils/Utils;->getAnimationScale(Landroid/content/Context;)F
-HSPLcom/airbnb/lottie/utils/Utils;->getScale(Landroid/graphics/Matrix;)F
-HSPLcom/airbnb/lottie/utils/Utils;->hasZeroScaleAxis(Landroid/graphics/Matrix;)Z
+HPLcom/airbnb/lottie/utils/Utils;->getScale(Landroid/graphics/Matrix;)F
+HPLcom/airbnb/lottie/utils/Utils;->hasZeroScaleAxis(Landroid/graphics/Matrix;)Z
HSPLcom/airbnb/lottie/utils/Utils;->hashFor(FFFF)I
HSPLcom/airbnb/lottie/utils/Utils;->isAtLeastVersion(IIIIII)Z
-HSPLcom/airbnb/lottie/value/Keyframe;-><init>(Lcom/airbnb/lottie/LottieComposition;Ljava/lang/Object;Ljava/lang/Object;Landroid/view/animation/Interpolator;FLjava/lang/Float;)V
-HSPLcom/airbnb/lottie/value/Keyframe;-><init>(Lcom/airbnb/lottie/LottieComposition;Ljava/lang/Object;Ljava/lang/Object;Landroid/view/animation/Interpolator;Landroid/view/animation/Interpolator;Landroid/view/animation/Interpolator;FLjava/lang/Float;)V
-HSPLcom/airbnb/lottie/value/Keyframe;-><init>(Ljava/lang/Object;)V
-HSPLcom/airbnb/lottie/value/Keyframe;->containsProgress(F)Z
-HSPLcom/airbnb/lottie/value/Keyframe;->getEndProgress()F
-HSPLcom/airbnb/lottie/value/Keyframe;->getEndValueFloat()F
-HSPLcom/airbnb/lottie/value/Keyframe;->getEndValueInt()I
-HSPLcom/airbnb/lottie/value/Keyframe;->getStartProgress()F
-HSPLcom/airbnb/lottie/value/Keyframe;->getStartValueFloat()F
-HSPLcom/airbnb/lottie/value/Keyframe;->getStartValueInt()I
-HSPLcom/airbnb/lottie/value/Keyframe;->isStatic()Z
-HSPLcom/airbnb/lottie/value/ScaleXY;-><init>(FF)V
-HSPLcom/airbnb/lottie/value/ScaleXY;->equals(FF)Z
-Lcom/airbnb/lottie/L$1;
-Lcom/airbnb/lottie/L;
-Lcom/airbnb/lottie/LottieComposition;
-Lcom/airbnb/lottie/LottieCompositionFactory$$ExternalSyntheticLambda0;
-Lcom/airbnb/lottie/LottieCompositionFactory$$ExternalSyntheticLambda2;
-Lcom/airbnb/lottie/LottieCompositionFactory$$ExternalSyntheticLambda4;
-Lcom/airbnb/lottie/LottieCompositionFactory$$ExternalSyntheticLambda5;
-Lcom/airbnb/lottie/LottieCompositionFactory$$ExternalSyntheticLambda9;
-Lcom/airbnb/lottie/LottieCompositionFactory;
-Lcom/airbnb/lottie/LottieDrawable$1;
-Lcom/airbnb/lottie/LottieDrawable$OnVisibleAction;
-Lcom/airbnb/lottie/LottieDrawable;
-Lcom/airbnb/lottie/LottieListener;
-Lcom/airbnb/lottie/LottieLogger;
-Lcom/airbnb/lottie/LottieResult;
-Lcom/airbnb/lottie/LottieTask$$ExternalSyntheticLambda0;
-Lcom/airbnb/lottie/LottieTask$LottieFutureTask;
-Lcom/airbnb/lottie/LottieTask;
-Lcom/airbnb/lottie/PerformanceTracker$1;
-Lcom/airbnb/lottie/PerformanceTracker;
-Lcom/airbnb/lottie/RenderMode$1;
-Lcom/airbnb/lottie/RenderMode;
-Lcom/airbnb/lottie/animation/LPaint;
-Lcom/airbnb/lottie/animation/content/BaseStrokeContent$PathGroup;
-Lcom/airbnb/lottie/animation/content/BaseStrokeContent;
-Lcom/airbnb/lottie/animation/content/CompoundTrimPathContent;
-Lcom/airbnb/lottie/animation/content/Content;
-Lcom/airbnb/lottie/animation/content/ContentGroup;
-Lcom/airbnb/lottie/animation/content/DrawingContent;
-Lcom/airbnb/lottie/animation/content/EllipseContent;
-Lcom/airbnb/lottie/animation/content/FillContent;
-Lcom/airbnb/lottie/animation/content/GreedyContent;
-Lcom/airbnb/lottie/animation/content/KeyPathElementContent;
-Lcom/airbnb/lottie/animation/content/ModifierContent;
-Lcom/airbnb/lottie/animation/content/PathContent;
-Lcom/airbnb/lottie/animation/content/ShapeContent;
-Lcom/airbnb/lottie/animation/content/ShapeModifierContent;
-Lcom/airbnb/lottie/animation/content/StrokeContent;
-Lcom/airbnb/lottie/animation/content/TrimPathContent;
-Lcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation$AnimationListener;
-Lcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation$EmptyKeyframeWrapper;
-Lcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation$KeyframesWrapper;
-Lcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation$KeyframesWrapperImpl;
-Lcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation$SingleKeyframeWrapper;
-Lcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation;
-Lcom/airbnb/lottie/animation/keyframe/ColorKeyframeAnimation;
-Lcom/airbnb/lottie/animation/keyframe/DropShadowKeyframeAnimation;
-Lcom/airbnb/lottie/animation/keyframe/FloatKeyframeAnimation;
-Lcom/airbnb/lottie/animation/keyframe/IntegerKeyframeAnimation;
-Lcom/airbnb/lottie/animation/keyframe/KeyframeAnimation;
-Lcom/airbnb/lottie/animation/keyframe/MaskKeyframeAnimation;
-Lcom/airbnb/lottie/animation/keyframe/PathKeyframe;
-Lcom/airbnb/lottie/animation/keyframe/PathKeyframeAnimation;
-Lcom/airbnb/lottie/animation/keyframe/PointKeyframeAnimation;
-Lcom/airbnb/lottie/animation/keyframe/ShapeKeyframeAnimation;
-Lcom/airbnb/lottie/animation/keyframe/TransformKeyframeAnimation;
-Lcom/airbnb/lottie/model/CubicCurveData;
-Lcom/airbnb/lottie/model/KeyPathElement;
-Lcom/airbnb/lottie/model/LottieCompositionCache;
-Lcom/airbnb/lottie/model/animatable/AnimatableColorValue;
-Lcom/airbnb/lottie/model/animatable/AnimatableFloatValue;
-Lcom/airbnb/lottie/model/animatable/AnimatableIntegerValue;
-Lcom/airbnb/lottie/model/animatable/AnimatablePathValue;
-Lcom/airbnb/lottie/model/animatable/AnimatablePointValue;
-Lcom/airbnb/lottie/model/animatable/AnimatableScaleValue;
-Lcom/airbnb/lottie/model/animatable/AnimatableShapeValue;
-Lcom/airbnb/lottie/model/animatable/AnimatableSplitDimensionPathValue;
-Lcom/airbnb/lottie/model/animatable/AnimatableTransform;
-Lcom/airbnb/lottie/model/animatable/AnimatableValue;
-Lcom/airbnb/lottie/model/animatable/BaseAnimatableValue;
-Lcom/airbnb/lottie/model/content/BlurEffect;
-Lcom/airbnb/lottie/model/content/CircleShape;
-Lcom/airbnb/lottie/model/content/ContentModel;
-Lcom/airbnb/lottie/model/content/Mask$MaskMode;
-Lcom/airbnb/lottie/model/content/ShapeData;
-Lcom/airbnb/lottie/model/content/ShapeFill;
-Lcom/airbnb/lottie/model/content/ShapeGroup;
-Lcom/airbnb/lottie/model/content/ShapePath;
-Lcom/airbnb/lottie/model/content/ShapeStroke$1;
-Lcom/airbnb/lottie/model/content/ShapeStroke$LineCapType;
-Lcom/airbnb/lottie/model/content/ShapeStroke$LineJoinType;
-Lcom/airbnb/lottie/model/content/ShapeStroke;
-Lcom/airbnb/lottie/model/layer/BaseLayer$$ExternalSyntheticLambda0;
-Lcom/airbnb/lottie/model/layer/BaseLayer$1;
-Lcom/airbnb/lottie/model/layer/BaseLayer;
-Lcom/airbnb/lottie/model/layer/CompositionLayer$1;
-Lcom/airbnb/lottie/model/layer/CompositionLayer;
-Lcom/airbnb/lottie/model/layer/Layer$LayerType;
-Lcom/airbnb/lottie/model/layer/Layer$MatteType;
-Lcom/airbnb/lottie/model/layer/Layer;
-Lcom/airbnb/lottie/model/layer/ShapeLayer;
-Lcom/airbnb/lottie/network/DefaultLottieFetchResult;
-Lcom/airbnb/lottie/network/DefaultLottieNetworkFetcher;
-Lcom/airbnb/lottie/network/FileExtension;
-Lcom/airbnb/lottie/network/LottieFetchResult;
-Lcom/airbnb/lottie/network/LottieNetworkCacheProvider;
-Lcom/airbnb/lottie/network/LottieNetworkFetcher;
-Lcom/airbnb/lottie/network/NetworkCache;
-Lcom/airbnb/lottie/network/NetworkFetcher;
-Lcom/airbnb/lottie/parser/AnimatablePathValueParser;
-Lcom/airbnb/lottie/parser/AnimatableTransformParser;
-Lcom/airbnb/lottie/parser/AnimatableValueParser;
-Lcom/airbnb/lottie/parser/CircleShapeParser;
-Lcom/airbnb/lottie/parser/ColorParser;
-Lcom/airbnb/lottie/parser/ContentModelParser;
-Lcom/airbnb/lottie/parser/FloatParser;
-Lcom/airbnb/lottie/parser/IntegerParser;
-Lcom/airbnb/lottie/parser/JsonUtils$1;
-Lcom/airbnb/lottie/parser/JsonUtils;
-Lcom/airbnb/lottie/parser/KeyframeParser;
-Lcom/airbnb/lottie/parser/KeyframesParser;
-Lcom/airbnb/lottie/parser/LayerParser;
-Lcom/airbnb/lottie/parser/LottieCompositionMoshiParser;
-Lcom/airbnb/lottie/parser/PathKeyframeParser;
-Lcom/airbnb/lottie/parser/PathParser;
-Lcom/airbnb/lottie/parser/PointFParser;
-Lcom/airbnb/lottie/parser/ScaleXYParser;
-Lcom/airbnb/lottie/parser/ShapeDataParser;
-Lcom/airbnb/lottie/parser/ShapeFillParser;
-Lcom/airbnb/lottie/parser/ShapeGroupParser;
-Lcom/airbnb/lottie/parser/ShapePathParser;
-Lcom/airbnb/lottie/parser/ShapeStrokeParser;
-Lcom/airbnb/lottie/parser/ValueParser;
-Lcom/airbnb/lottie/parser/moshi/JsonDataException;
-Lcom/airbnb/lottie/parser/moshi/JsonEncodingException;
-Lcom/airbnb/lottie/parser/moshi/JsonReader$Options;
-Lcom/airbnb/lottie/parser/moshi/JsonReader$Token;
-Lcom/airbnb/lottie/parser/moshi/JsonReader;
-Lcom/airbnb/lottie/parser/moshi/JsonScope;
-Lcom/airbnb/lottie/parser/moshi/JsonUtf8Reader;
-Lcom/airbnb/lottie/utils/BaseLottieAnimator;
-Lcom/airbnb/lottie/utils/GammaEvaluator;
-Lcom/airbnb/lottie/utils/LogcatLogger;
-Lcom/airbnb/lottie/utils/Logger;
-Lcom/airbnb/lottie/utils/LottieValueAnimator;
-Lcom/airbnb/lottie/utils/MiscUtils;
+HSPLcom/airbnb/lottie/utils/Utils;->saveLayerCompat(Landroid/graphics/Canvas;Landroid/graphics/RectF;Landroid/graphics/Paint;)V
+HPLcom/airbnb/lottie/utils/Utils;->saveLayerCompat(Landroid/graphics/Canvas;Landroid/graphics/RectF;Landroid/graphics/Paint;I)V
Lcom/airbnb/lottie/utils/Utils$1;
+HSPLcom/airbnb/lottie/utils/Utils$1;-><init>()V
Lcom/airbnb/lottie/utils/Utils$2;
+HSPLcom/airbnb/lottie/utils/Utils$2;-><init>()V
Lcom/airbnb/lottie/utils/Utils$3;
+HSPLcom/airbnb/lottie/utils/Utils$3;-><init>()V
Lcom/airbnb/lottie/utils/Utils$4;
-Lcom/airbnb/lottie/utils/Utils;
+HSPLcom/airbnb/lottie/utils/Utils$4;-><init>()V
+HSPLcom/airbnb/lottie/utils/Utils$4;->initialValue()Ljava/lang/Object;
+HSPLcom/airbnb/lottie/utils/Utils$4;->initialValue()[F
Lcom/airbnb/lottie/value/Keyframe;
+HSPLcom/airbnb/lottie/value/Keyframe;-><init>(Lcom/airbnb/lottie/LottieComposition;Ljava/lang/Object;Ljava/lang/Object;Landroid/view/animation/Interpolator;FLjava/lang/Float;)V
+HSPLcom/airbnb/lottie/value/Keyframe;-><init>(Lcom/airbnb/lottie/LottieComposition;Ljava/lang/Object;Ljava/lang/Object;Landroid/view/animation/Interpolator;Landroid/view/animation/Interpolator;Landroid/view/animation/Interpolator;FLjava/lang/Float;)V
+HSPLcom/airbnb/lottie/value/Keyframe;-><init>(Ljava/lang/Object;)V
+HSPLcom/airbnb/lottie/value/Keyframe;->containsProgress(F)Z
+HPLcom/airbnb/lottie/value/Keyframe;->getEndProgress()F
+HPLcom/airbnb/lottie/value/Keyframe;->getEndValueFloat()F
+HPLcom/airbnb/lottie/value/Keyframe;->getEndValueInt()I
+HPLcom/airbnb/lottie/value/Keyframe;->getStartProgress()F
+HPLcom/airbnb/lottie/value/Keyframe;->getStartValueFloat()F
+HPLcom/airbnb/lottie/value/Keyframe;->getStartValueInt()I
+HPLcom/airbnb/lottie/value/Keyframe;->isStatic()Z
+Lcom/airbnb/lottie/value/LottieValueCallback;
Lcom/airbnb/lottie/value/ScaleXY;
-PLcom/airbnb/lottie/LottieComposition;->setHasDashPattern(Z)V
-PLcom/airbnb/lottie/LottieDrawable;->enableMergePathsForKitKatAndAbove()Z
-PLcom/airbnb/lottie/LottieDrawable;->getBitmapForId(Ljava/lang/String;)Landroid/graphics/Bitmap;
-PLcom/airbnb/lottie/LottieDrawable;->getIntrinsicHeight()I
-PLcom/airbnb/lottie/LottieDrawable;->getIntrinsicWidth()I
-PLcom/airbnb/lottie/LottieDrawable;->getLottieImageAssetForId(Ljava/lang/String;)Lcom/airbnb/lottie/LottieImageAsset;
-PLcom/airbnb/lottie/LottieDrawable;->getMaintainOriginalImageBounds()Z
-PLcom/airbnb/lottie/LottieDrawable;->getScale()F
-PLcom/airbnb/lottie/LottieImageAsset;-><init>(IILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-PLcom/airbnb/lottie/LottieImageAsset;->getBitmap()Landroid/graphics/Bitmap;
-PLcom/airbnb/lottie/LottieImageAsset;->getFileName()Ljava/lang/String;
-PLcom/airbnb/lottie/LottieImageAsset;->getId()Ljava/lang/String;
-PLcom/airbnb/lottie/LottieImageAsset;->setBitmap(Landroid/graphics/Bitmap;)V
-PLcom/airbnb/lottie/animation/content/CompoundTrimPathContent;->addTrimPath(Lcom/airbnb/lottie/animation/content/TrimPathContent;)V
-PLcom/airbnb/lottie/animation/content/EllipseContent;->invalidate()V
-PLcom/airbnb/lottie/animation/content/GradientFillContent;-><init>(Lcom/airbnb/lottie/LottieDrawable;Lcom/airbnb/lottie/model/layer/BaseLayer;Lcom/airbnb/lottie/model/content/GradientFill;)V
-PLcom/airbnb/lottie/animation/content/GradientFillContent;->setContents(Ljava/util/List;Ljava/util/List;)V
-PLcom/airbnb/lottie/animation/content/RectangleContent;-><init>(Lcom/airbnb/lottie/LottieDrawable;Lcom/airbnb/lottie/model/layer/BaseLayer;Lcom/airbnb/lottie/model/content/RectangleShape;)V
-PLcom/airbnb/lottie/animation/content/RectangleContent;->setContents(Ljava/util/List;Ljava/util/List;)V
-PLcom/airbnb/lottie/animation/content/ShapeContent;->invalidate()V
-PLcom/airbnb/lottie/animation/content/TrimPathContent;->addListener(Lcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation$AnimationListener;)V
-PLcom/airbnb/lottie/animation/content/TrimPathContent;->getEnd()Lcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation;
-PLcom/airbnb/lottie/animation/content/TrimPathContent;->getOffset()Lcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation;
-PLcom/airbnb/lottie/animation/content/TrimPathContent;->getStart()Lcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation;
-PLcom/airbnb/lottie/animation/content/TrimPathContent;->getType()Lcom/airbnb/lottie/model/content/ShapeTrimPath$Type;
-PLcom/airbnb/lottie/animation/content/TrimPathContent;->isHidden()Z
-PLcom/airbnb/lottie/animation/content/TrimPathContent;->setContents(Ljava/util/List;Ljava/util/List;)V
-PLcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation$EmptyKeyframeWrapper;-><init>()V
-PLcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation$EmptyKeyframeWrapper;-><init>(Lcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation$1;)V
-PLcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation;->getProgress()F
-PLcom/airbnb/lottie/animation/keyframe/GradientColorKeyframeAnimation;-><init>(Ljava/util/List;)V
-PLcom/airbnb/lottie/animation/keyframe/GradientColorKeyframeAnimation;->getValue(Lcom/airbnb/lottie/value/Keyframe;F)Lcom/airbnb/lottie/model/content/GradientColor;
-PLcom/airbnb/lottie/animation/keyframe/GradientColorKeyframeAnimation;->getValue(Lcom/airbnb/lottie/value/Keyframe;F)Ljava/lang/Object;
-PLcom/airbnb/lottie/animation/keyframe/MaskKeyframeAnimation;->getMasks()Ljava/util/List;
-PLcom/airbnb/lottie/animation/keyframe/MaskKeyframeAnimation;->getOpacityAnimations()Ljava/util/List;
-PLcom/airbnb/lottie/animation/keyframe/ScaleKeyframeAnimation;-><init>(Ljava/util/List;)V
-PLcom/airbnb/lottie/animation/keyframe/SplitDimensionPathKeyframeAnimation;-><init>(Lcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation;Lcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation;)V
-PLcom/airbnb/lottie/model/Marker;-><init>(Ljava/lang/String;FF)V
-PLcom/airbnb/lottie/model/animatable/AnimatableGradientColorValue;-><init>(Ljava/util/List;)V
-PLcom/airbnb/lottie/model/animatable/AnimatableGradientColorValue;->createAnimation()Lcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation;
-PLcom/airbnb/lottie/model/animatable/AnimatableScaleValue;->createAnimation()Lcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation;
-PLcom/airbnb/lottie/model/animatable/AnimatableSplitDimensionPathValue;-><init>(Lcom/airbnb/lottie/model/animatable/AnimatableFloatValue;Lcom/airbnb/lottie/model/animatable/AnimatableFloatValue;)V
-PLcom/airbnb/lottie/model/animatable/AnimatableSplitDimensionPathValue;->createAnimation()Lcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation;
-PLcom/airbnb/lottie/model/content/GradientColor;-><init>([F[I)V
-PLcom/airbnb/lottie/model/content/GradientColor;->getColors()[I
-PLcom/airbnb/lottie/model/content/GradientColor;->getPositions()[F
-PLcom/airbnb/lottie/model/content/GradientColor;->getSize()I
-PLcom/airbnb/lottie/model/content/GradientColor;->lerp(Lcom/airbnb/lottie/model/content/GradientColor;Lcom/airbnb/lottie/model/content/GradientColor;F)V
-PLcom/airbnb/lottie/model/content/GradientFill;-><init>(Ljava/lang/String;Lcom/airbnb/lottie/model/content/GradientType;Landroid/graphics/Path$FillType;Lcom/airbnb/lottie/model/animatable/AnimatableGradientColorValue;Lcom/airbnb/lottie/model/animatable/AnimatableIntegerValue;Lcom/airbnb/lottie/model/animatable/AnimatablePointValue;Lcom/airbnb/lottie/model/animatable/AnimatablePointValue;Lcom/airbnb/lottie/model/animatable/AnimatableFloatValue;Lcom/airbnb/lottie/model/animatable/AnimatableFloatValue;Z)V
-PLcom/airbnb/lottie/model/content/GradientFill;->getEndPoint()Lcom/airbnb/lottie/model/animatable/AnimatablePointValue;
-PLcom/airbnb/lottie/model/content/GradientFill;->getFillType()Landroid/graphics/Path$FillType;
-PLcom/airbnb/lottie/model/content/GradientFill;->getGradientColor()Lcom/airbnb/lottie/model/animatable/AnimatableGradientColorValue;
-PLcom/airbnb/lottie/model/content/GradientFill;->getGradientType()Lcom/airbnb/lottie/model/content/GradientType;
-PLcom/airbnb/lottie/model/content/GradientFill;->getName()Ljava/lang/String;
-PLcom/airbnb/lottie/model/content/GradientFill;->getOpacity()Lcom/airbnb/lottie/model/animatable/AnimatableIntegerValue;
-PLcom/airbnb/lottie/model/content/GradientFill;->getStartPoint()Lcom/airbnb/lottie/model/animatable/AnimatablePointValue;
-PLcom/airbnb/lottie/model/content/GradientFill;->isHidden()Z
-PLcom/airbnb/lottie/model/content/GradientFill;->toContent(Lcom/airbnb/lottie/LottieDrawable;Lcom/airbnb/lottie/model/layer/BaseLayer;)Lcom/airbnb/lottie/animation/content/Content;
-PLcom/airbnb/lottie/model/content/GradientType;-><clinit>()V
-PLcom/airbnb/lottie/model/content/GradientType;-><init>(Ljava/lang/String;I)V
-PLcom/airbnb/lottie/model/content/Mask;-><init>(Lcom/airbnb/lottie/model/content/Mask$MaskMode;Lcom/airbnb/lottie/model/animatable/AnimatableShapeValue;Lcom/airbnb/lottie/model/animatable/AnimatableIntegerValue;Z)V
-PLcom/airbnb/lottie/model/content/Mask;->getMaskMode()Lcom/airbnb/lottie/model/content/Mask$MaskMode;
-PLcom/airbnb/lottie/model/content/Mask;->getMaskPath()Lcom/airbnb/lottie/model/animatable/AnimatableShapeValue;
-PLcom/airbnb/lottie/model/content/Mask;->getOpacity()Lcom/airbnb/lottie/model/animatable/AnimatableIntegerValue;
-PLcom/airbnb/lottie/model/content/Mask;->isInverted()Z
-PLcom/airbnb/lottie/model/content/MergePaths$MergePathsMode;-><clinit>()V
-PLcom/airbnb/lottie/model/content/MergePaths$MergePathsMode;-><init>(Ljava/lang/String;I)V
-PLcom/airbnb/lottie/model/content/MergePaths$MergePathsMode;->forId(I)Lcom/airbnb/lottie/model/content/MergePaths$MergePathsMode;
-PLcom/airbnb/lottie/model/content/MergePaths;-><init>(Ljava/lang/String;Lcom/airbnb/lottie/model/content/MergePaths$MergePathsMode;Z)V
-PLcom/airbnb/lottie/model/content/MergePaths;->toContent(Lcom/airbnb/lottie/LottieDrawable;Lcom/airbnb/lottie/model/layer/BaseLayer;)Lcom/airbnb/lottie/animation/content/Content;
-PLcom/airbnb/lottie/model/content/RectangleShape;-><init>(Ljava/lang/String;Lcom/airbnb/lottie/model/animatable/AnimatableValue;Lcom/airbnb/lottie/model/animatable/AnimatableValue;Lcom/airbnb/lottie/model/animatable/AnimatableFloatValue;Z)V
-PLcom/airbnb/lottie/model/content/RectangleShape;->getCornerRadius()Lcom/airbnb/lottie/model/animatable/AnimatableFloatValue;
-PLcom/airbnb/lottie/model/content/RectangleShape;->getName()Ljava/lang/String;
-PLcom/airbnb/lottie/model/content/RectangleShape;->getPosition()Lcom/airbnb/lottie/model/animatable/AnimatableValue;
-PLcom/airbnb/lottie/model/content/RectangleShape;->getSize()Lcom/airbnb/lottie/model/animatable/AnimatableValue;
-PLcom/airbnb/lottie/model/content/RectangleShape;->isHidden()Z
-PLcom/airbnb/lottie/model/content/RectangleShape;->toContent(Lcom/airbnb/lottie/LottieDrawable;Lcom/airbnb/lottie/model/layer/BaseLayer;)Lcom/airbnb/lottie/animation/content/Content;
-PLcom/airbnb/lottie/model/content/ShapeTrimPath$Type;-><clinit>()V
-PLcom/airbnb/lottie/model/content/ShapeTrimPath$Type;-><init>(Ljava/lang/String;I)V
-PLcom/airbnb/lottie/model/content/ShapeTrimPath$Type;->forId(I)Lcom/airbnb/lottie/model/content/ShapeTrimPath$Type;
-PLcom/airbnb/lottie/model/content/ShapeTrimPath;-><init>(Ljava/lang/String;Lcom/airbnb/lottie/model/content/ShapeTrimPath$Type;Lcom/airbnb/lottie/model/animatable/AnimatableFloatValue;Lcom/airbnb/lottie/model/animatable/AnimatableFloatValue;Lcom/airbnb/lottie/model/animatable/AnimatableFloatValue;Z)V
-PLcom/airbnb/lottie/model/content/ShapeTrimPath;->getEnd()Lcom/airbnb/lottie/model/animatable/AnimatableFloatValue;
-PLcom/airbnb/lottie/model/content/ShapeTrimPath;->getName()Ljava/lang/String;
-PLcom/airbnb/lottie/model/content/ShapeTrimPath;->getOffset()Lcom/airbnb/lottie/model/animatable/AnimatableFloatValue;
-PLcom/airbnb/lottie/model/content/ShapeTrimPath;->getStart()Lcom/airbnb/lottie/model/animatable/AnimatableFloatValue;
-PLcom/airbnb/lottie/model/content/ShapeTrimPath;->getType()Lcom/airbnb/lottie/model/content/ShapeTrimPath$Type;
-PLcom/airbnb/lottie/model/content/ShapeTrimPath;->isHidden()Z
-PLcom/airbnb/lottie/model/content/ShapeTrimPath;->toContent(Lcom/airbnb/lottie/LottieDrawable;Lcom/airbnb/lottie/model/layer/BaseLayer;)Lcom/airbnb/lottie/animation/content/Content;
-PLcom/airbnb/lottie/model/layer/ImageLayer;-><init>(Lcom/airbnb/lottie/LottieDrawable;Lcom/airbnb/lottie/model/layer/Layer;)V
-PLcom/airbnb/lottie/model/layer/NullLayer;-><init>(Lcom/airbnb/lottie/LottieDrawable;Lcom/airbnb/lottie/model/layer/Layer;)V
-PLcom/airbnb/lottie/parser/AnimatableValueParser;->parseGradientColor(Lcom/airbnb/lottie/parser/moshi/JsonReader;Lcom/airbnb/lottie/LottieComposition;I)Lcom/airbnb/lottie/model/animatable/AnimatableGradientColorValue;
-PLcom/airbnb/lottie/parser/GradientColorParser;-><init>(I)V
-PLcom/airbnb/lottie/parser/GradientColorParser;->addOpacityStopsToGradientIfNeeded(Lcom/airbnb/lottie/model/content/GradientColor;Ljava/util/List;)V
-PLcom/airbnb/lottie/parser/GradientColorParser;->parse(Lcom/airbnb/lottie/parser/moshi/JsonReader;F)Lcom/airbnb/lottie/model/content/GradientColor;
-PLcom/airbnb/lottie/parser/GradientColorParser;->parse(Lcom/airbnb/lottie/parser/moshi/JsonReader;F)Ljava/lang/Object;
-PLcom/airbnb/lottie/parser/GradientFillParser;-><clinit>()V
-PLcom/airbnb/lottie/parser/GradientFillParser;->parse(Lcom/airbnb/lottie/parser/moshi/JsonReader;Lcom/airbnb/lottie/LottieComposition;)Lcom/airbnb/lottie/model/content/GradientFill;
-PLcom/airbnb/lottie/parser/MergePathsParser;-><clinit>()V
-PLcom/airbnb/lottie/parser/MergePathsParser;->parse(Lcom/airbnb/lottie/parser/moshi/JsonReader;)Lcom/airbnb/lottie/model/content/MergePaths;
-PLcom/airbnb/lottie/parser/RectangleShapeParser;-><clinit>()V
-PLcom/airbnb/lottie/parser/RectangleShapeParser;->parse(Lcom/airbnb/lottie/parser/moshi/JsonReader;Lcom/airbnb/lottie/LottieComposition;)Lcom/airbnb/lottie/model/content/RectangleShape;
-PLcom/airbnb/lottie/parser/ShapeTrimPathParser;-><clinit>()V
-PLcom/airbnb/lottie/parser/ShapeTrimPathParser;->parse(Lcom/airbnb/lottie/parser/moshi/JsonReader;Lcom/airbnb/lottie/LottieComposition;)Lcom/airbnb/lottie/model/content/ShapeTrimPath;
-PLcom/airbnb/lottie/utils/Utils$1;->initialValue()Landroid/graphics/PathMeasure;
-PLcom/airbnb/lottie/utils/Utils$1;->initialValue()Ljava/lang/Object;
-PLcom/airbnb/lottie/utils/Utils$2;->initialValue()Landroid/graphics/Path;
-PLcom/airbnb/lottie/utils/Utils$2;->initialValue()Ljava/lang/Object;
-PLcom/airbnb/lottie/utils/Utils$3;->initialValue()Landroid/graphics/Path;
-PLcom/airbnb/lottie/utils/Utils$3;->initialValue()Ljava/lang/Object;
-PLcom/airbnb/lottie/utils/Utils;->saveLayerCompat(Landroid/graphics/Canvas;Landroid/graphics/RectF;Landroid/graphics/Paint;I)V
-PLcom/airbnb/lottie/value/ScaleXY;-><init>()V
-PLcom/airbnb/lottie/value/ScaleXY;->getScaleX()F
-PLcom/airbnb/lottie/value/ScaleXY;->getScaleY()F
-PLcom/airbnb/lottie/value/ScaleXY;->set(FF)V
+HSPLcom/airbnb/lottie/value/ScaleXY;-><init>()V
+HSPLcom/airbnb/lottie/value/ScaleXY;-><init>(FF)V
+HSPLcom/airbnb/lottie/value/ScaleXY;->equals(FF)Z
+HSPLcom/airbnb/lottie/value/ScaleXY;->getScaleX()F
+HSPLcom/airbnb/lottie/value/ScaleXY;->getScaleY()F
+HSPLcom/airbnb/lottie/value/ScaleXY;->set(FF)V \ No newline at end of file
diff --git a/lottie/src/main/java/com/airbnb/lottie/AsyncUpdates.java b/lottie/src/main/java/com/airbnb/lottie/AsyncUpdates.java
new file mode 100644
index 00000000..842b3257
--- /dev/null
+++ b/lottie/src/main/java/com/airbnb/lottie/AsyncUpdates.java
@@ -0,0 +1,54 @@
+package com.airbnb.lottie;
+
+/**
+ * **Note: this API is experimental and may changed.**
+ * <p/>
+ * When async updates are enabled, parts of animation updates will happen off of the main thread.
+ * <p/>
+ * At a high level, during the animation loop, there are two main code paths:
+ * 1. setProgress
+ * 2. draw
+ * <p/>
+ * setProgress is called on every frame when the internal animator updates or if you manually call setProgress.
+ * setProgress must then iterate through every single node in the animation (every shape, fill, mask, stroke, etc.)
+ * and call setProgress on it. When progress is set on a node, it will:
+ * 1. Call the dynamic property value callback if one has been set by you.
+ * 2. Recalculate what its own progress is. Various animation features like interpolators or time remapping
+ * will cause the progress value for a given node to be different than the top level progress.
+ * 3. If a node's progress has changed, it will call invalidate which will invalidate values that are
+ * cached and derived from that node's progress and then bubble up the invalidation to LottieDrawable
+ * to ensure that Android renders a new frame.
+ * <p/>
+ * draw is what actually draws your animation to a canvas. Many of Lottie's operations are completed or
+ * cached in the setProgress path. However, there are a few things (like parentMatrix) that Lottie only has access
+ * to in the draw path and it, of course, needs to actually execute the canvas operations to draw the animation.
+ * <p/>
+ * Without async updates, in a single main thread frame, Lottie will call setProgress immediately followed by draw.
+ * <p/>
+ * With async updates, Lottie will determine if the most recent setProgress is still close enough to be considered
+ * valid. An existing progress will be considered valid if it is within LottieDrawable.MAX_DELTA_MS_ASYNC_SET_PROGRESS
+ * milliseconds from the current actual progress.
+ * If the calculated progress is close enough, it will only execute draw. Once draw completes, it will schedule a
+ * setProgress to be run on a background thread immediately after draw finishes and it will likely complete well
+ * before the next frame starts.
+ * <p/>
+ * The background thread is created via LottieDrawable.setProgressExecutor. You can refer to it for the current default
+ * thread pool configuration.
+ */
+public enum AsyncUpdates {
+ /**
+ * Default value.
+ * <p/>
+ * This will default to DISABLED until this feature has had time to incubate.
+ * The behavior of AUTOMATIC may change over time.
+ */
+ AUTOMATIC,
+ /**
+ * Use the async update path. Refer to the docs for {@link AsyncUpdates} for more details.
+ */
+ ENABLED,
+ /**
+ * Do not use the async update path. Refer to the docs for {@link AsyncUpdates} for more details.
+ */
+ DISABLED,
+}
diff --git a/lottie/src/main/java/com/airbnb/lottie/FontAssetDelegate.java b/lottie/src/main/java/com/airbnb/lottie/FontAssetDelegate.java
index 271f0cf6..bea7afd2 100644
--- a/lottie/src/main/java/com/airbnb/lottie/FontAssetDelegate.java
+++ b/lottie/src/main/java/com/airbnb/lottie/FontAssetDelegate.java
@@ -18,9 +18,23 @@ import android.graphics.Typeface;
}
/**
+ * Override this if you want to return a Typeface from a font family and style.
+ */
+ public Typeface fetchFont(String fontFamily, String fontStyle, String fontName) {
+ return null;
+ }
+
+ /**
* Override this if you want to specify the asset path for a given font family.
*/
public String getFontPath(String fontFamily) {
return null;
}
+
+ /**
+ * Override this if you want to specify the asset path for a given font family and style.
+ */
+ public String getFontPath(String fontFamily, String fontStyle, String fontName) {
+ return null;
+ }
}
diff --git a/lottie/src/main/java/com/airbnb/lottie/L.java b/lottie/src/main/java/com/airbnb/lottie/L.java
index 180bfbae..c22cb114 100644
--- a/lottie/src/main/java/com/airbnb/lottie/L.java
+++ b/lottie/src/main/java/com/airbnb/lottie/L.java
@@ -3,14 +3,15 @@ package com.airbnb.lottie;
import android.content.Context;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
-import androidx.core.os.TraceCompat;
import com.airbnb.lottie.network.DefaultLottieNetworkFetcher;
import com.airbnb.lottie.network.LottieNetworkCacheProvider;
import com.airbnb.lottie.network.LottieNetworkFetcher;
import com.airbnb.lottie.network.NetworkCache;
import com.airbnb.lottie.network.NetworkFetcher;
+import com.airbnb.lottie.utils.LottieTrace;
import java.io.File;
@@ -20,18 +21,17 @@ public class L {
public static boolean DBG = false;
public static final String TAG = "LOTTIE";
- private static final int MAX_DEPTH = 20;
private static boolean traceEnabled = false;
- private static String[] sections;
- private static long[] startTimeNs;
- private static int traceDepth = 0;
- private static int depthPastMaxDepth = 0;
+ private static boolean networkCacheEnabled = true;
+ private static boolean disablePathInterpolatorCache = true;
+ private static AsyncUpdates defaultAsyncUpdates = AsyncUpdates.AUTOMATIC;
private static LottieNetworkFetcher fetcher;
private static LottieNetworkCacheProvider cacheProvider;
private static volatile NetworkFetcher networkFetcher;
private static volatile NetworkCache networkCache;
+ private static ThreadLocal<LottieTrace> lottieTrace;
private L() {
}
@@ -41,52 +41,54 @@ public class L {
return;
}
traceEnabled = enabled;
- if (traceEnabled) {
- sections = new String[MAX_DEPTH];
- startTimeNs = new long[MAX_DEPTH];
+ if (traceEnabled && lottieTrace == null) {
+ lottieTrace = new ThreadLocal<>();
}
}
+ public static void setNetworkCacheEnabled(boolean enabled) {
+ networkCacheEnabled = enabled;
+ }
+
public static void beginSection(String section) {
if (!traceEnabled) {
return;
}
- if (traceDepth == MAX_DEPTH) {
- depthPastMaxDepth++;
- return;
- }
- sections[traceDepth] = section;
- startTimeNs[traceDepth] = System.nanoTime();
- TraceCompat.beginSection(section);
- traceDepth++;
+ getTrace().beginSection(section);
}
public static float endSection(String section) {
- if (depthPastMaxDepth > 0) {
- depthPastMaxDepth--;
- return 0;
- }
if (!traceEnabled) {
return 0;
}
- traceDepth--;
- if (traceDepth == -1) {
- throw new IllegalStateException("Can't end trace section. There are none.");
- }
- if (!section.equals(sections[traceDepth])) {
- throw new IllegalStateException("Unbalanced trace call " + section +
- ". Expected " + sections[traceDepth] + ".");
+ return getTrace().endSection(section);
+ }
+
+ private static LottieTrace getTrace() {
+ LottieTrace trace = lottieTrace.get();
+ if (trace == null) {
+ trace = new LottieTrace();
+ lottieTrace.set(trace);
}
- TraceCompat.endSection();
- return (System.nanoTime() - startTimeNs[traceDepth]) / 1000000f;
+ return trace;
}
public static void setFetcher(LottieNetworkFetcher customFetcher) {
+ if ((fetcher == null && customFetcher == null) || (fetcher != null && fetcher.equals(customFetcher))) {
+ return;
+ }
+
fetcher = customFetcher;
+ networkFetcher = null;
}
public static void setCacheProvider(LottieNetworkCacheProvider customProvider) {
+ if ((cacheProvider == null && customProvider == null) || (cacheProvider != null && cacheProvider.equals(customProvider))) {
+ return;
+ }
+
cacheProvider = customProvider;
+ networkCache = null;
}
@NonNull
@@ -103,22 +105,38 @@ public class L {
return local;
}
- @NonNull
+ @Nullable
public static NetworkCache networkCache(@NonNull final Context context) {
+ if (!networkCacheEnabled) {
+ return null;
+ }
final Context appContext = context.getApplicationContext();
NetworkCache local = networkCache;
if (local == null) {
synchronized (NetworkCache.class) {
local = networkCache;
if (local == null) {
- networkCache = local = new NetworkCache(cacheProvider != null ? cacheProvider : new LottieNetworkCacheProvider() {
- @Override @NonNull public File getCacheDir() {
- return new File(appContext.getCacheDir(), "lottie_network_cache");
- }
- });
+ networkCache = local = new NetworkCache(cacheProvider != null ? cacheProvider :
+ () -> new File(appContext.getCacheDir(), "lottie_network_cache"));
}
}
}
return local;
}
+
+ public static void setDisablePathInterpolatorCache(boolean disablePathInterpolatorCache) {
+ L.disablePathInterpolatorCache = disablePathInterpolatorCache;
+ }
+
+ public static boolean getDisablePathInterpolatorCache() {
+ return disablePathInterpolatorCache;
+ }
+
+ public static void setDefaultAsyncUpdates(AsyncUpdates asyncUpdates) {
+ L.defaultAsyncUpdates = asyncUpdates;
+ }
+
+ public static AsyncUpdates getDefaultAsyncUpdates() {
+ return L.defaultAsyncUpdates;
+ }
}
diff --git a/lottie/src/main/java/com/airbnb/lottie/Lottie.java b/lottie/src/main/java/com/airbnb/lottie/Lottie.java
index af577c76..c6274a39 100644
--- a/lottie/src/main/java/com/airbnb/lottie/Lottie.java
+++ b/lottie/src/main/java/com/airbnb/lottie/Lottie.java
@@ -19,5 +19,8 @@ public class Lottie {
L.setFetcher(lottieConfig.networkFetcher);
L.setCacheProvider(lottieConfig.cacheProvider);
L.setTraceEnabled(lottieConfig.enableSystraceMarkers);
+ L.setNetworkCacheEnabled(lottieConfig.enableNetworkCache);
+ L.setDisablePathInterpolatorCache(lottieConfig.disablePathInterpolatorCache);
+ L.setDefaultAsyncUpdates(lottieConfig.defaultAsyncUpdates);
}
-} \ No newline at end of file
+}
diff --git a/lottie/src/main/java/com/airbnb/lottie/LottieAnimationView.java b/lottie/src/main/java/com/airbnb/lottie/LottieAnimationView.java
index 53d05aee..273ba703 100644
--- a/lottie/src/main/java/com/airbnb/lottie/LottieAnimationView.java
+++ b/lottie/src/main/java/com/airbnb/lottie/LottieAnimationView.java
@@ -7,6 +7,7 @@ import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.ColorFilter;
+import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Parcel;
@@ -14,7 +15,6 @@ import android.os.Parcelable;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
-
import androidx.annotation.AttrRes;
import androidx.annotation.DrawableRes;
import androidx.annotation.FloatRange;
@@ -25,7 +25,6 @@ import androidx.annotation.RawRes;
import androidx.annotation.RequiresApi;
import androidx.appcompat.content.res.AppCompatResources;
import androidx.appcompat.widget.AppCompatImageView;
-
import com.airbnb.lottie.model.KeyPath;
import com.airbnb.lottie.utils.Logger;
import com.airbnb.lottie.utils.Utils;
@@ -35,13 +34,16 @@ import com.airbnb.lottie.value.SimpleLottieValueCallback;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
+import java.lang.ref.WeakReference;
import java.util.HashSet;
import java.util.List;
+import java.util.Map;
import java.util.Set;
+import java.util.zip.ZipInputStream;
/**
* This view will load, deserialize, and display an After Effects animation exported with
- * bodymovin (https://github.com/bodymovin/bodymovin).
+ * bodymovin (<a href="https://github.com/airbnb/lottie-web">github.com/airbnb/lottie-web</a>).
* <p>
* You may set the animation in one of two ways:
* 1) Attrs: {@link R.styleable#LottieAnimationView_lottie_fileName}
@@ -72,18 +74,49 @@ import java.util.Set;
throw new IllegalStateException("Unable to parse composition", throwable);
};
- private final LottieListener<LottieComposition> loadedListener = this::setComposition;
+ private final LottieListener<LottieComposition> loadedListener = new WeakSuccessListener(this);
- private final LottieListener<Throwable> wrappedFailureListener = new LottieListener<Throwable>() {
- @Override
- public void onResult(Throwable result) {
- if (fallbackResource != 0) {
- setImageResource(fallbackResource);
+ private static class WeakSuccessListener implements LottieListener<LottieComposition> {
+
+ private final WeakReference<LottieAnimationView> targetReference;
+
+ public WeakSuccessListener(LottieAnimationView target) {
+ this.targetReference = new WeakReference<>(target);
+ }
+
+ @Override public void onResult(LottieComposition result) {
+ LottieAnimationView targetView = targetReference.get();
+ if (targetView == null) {
+ return;
}
- LottieListener<Throwable> l = failureListener == null ? DEFAULT_FAILURE_LISTENER : failureListener;
+ targetView.setComposition(result);
+ }
+ }
+
+ private final LottieListener<Throwable> wrappedFailureListener = new WeakFailureListener(this);
+
+ private static class WeakFailureListener implements LottieListener<Throwable> {
+
+ private final WeakReference<LottieAnimationView> targetReference;
+
+ public WeakFailureListener(LottieAnimationView target) {
+ this.targetReference = new WeakReference<>(target);
+ }
+
+ @Override public void onResult(Throwable result) {
+ LottieAnimationView targetView = targetReference.get();
+ if (targetView == null) {
+ return;
+ }
+
+ if (targetView.fallbackResource != 0) {
+ targetView.setImageResource(targetView.fallbackResource);
+ }
+ LottieListener<Throwable> l = targetView.failureListener == null ? DEFAULT_FAILURE_LISTENER : targetView.failureListener;
l.onResult(result);
}
- };
+ }
+
@Nullable private LottieListener<Throwable> failureListener;
@DrawableRes private int fallbackResource = 0;
@@ -106,10 +139,6 @@ import java.util.Set;
private final Set<LottieOnCompositionLoadedListener> lottieOnCompositionLoadedListeners = new HashSet<>();
@Nullable private LottieTask<LottieComposition> compositionTask;
- /**
- * Can be null because it is created async
- */
- @Nullable private LottieComposition composition;
public LottieAnimationView(Context context) {
super(context);
@@ -179,8 +208,19 @@ import java.util.Set;
setClipToCompositionBounds(ta.getBoolean(R.styleable.LottieAnimationView_lottie_clipToCompositionBounds, true));
}
+ if (ta.hasValue(R.styleable.LottieAnimationView_lottie_clipTextToBoundingBox)) {
+ setClipTextToBoundingBox(ta.getBoolean(R.styleable.LottieAnimationView_lottie_clipTextToBoundingBox, false));
+ }
+
+ if (ta.hasValue(R.styleable.LottieAnimationView_lottie_defaultFontFileExtension)) {
+ setDefaultFontFileExtension(ta.getString(R.styleable.LottieAnimationView_lottie_defaultFontFileExtension));
+ }
+
setImageAssetsFolder(ta.getString(R.styleable.LottieAnimationView_lottie_imageAssetsFolder));
- setProgress(ta.getFloat(R.styleable.LottieAnimationView_lottie_progress, 0));
+
+ boolean hasProgress = ta.hasValue(R.styleable.LottieAnimationView_lottie_progress);
+ setProgressInternal(ta.getFloat(R.styleable.LottieAnimationView_lottie_progress, 0f), hasProgress);
+
enableMergePathsForKitKatAndAbove(ta.getBoolean(
R.styleable.LottieAnimationView_lottie_enableMergePathsForKitKatAndAbove, false));
if (ta.hasValue(R.styleable.LottieAnimationView_lottie_colorFilter)) {
@@ -200,6 +240,14 @@ import java.util.Set;
setRenderMode(RenderMode.values()[renderModeOrdinal]);
}
+ if (ta.hasValue(R.styleable.LottieAnimationView_lottie_asyncUpdates)) {
+ int asyncUpdatesOrdinal = ta.getInt(R.styleable.LottieAnimationView_lottie_asyncUpdates, AsyncUpdates.AUTOMATIC.ordinal());
+ if (asyncUpdatesOrdinal >= RenderMode.values().length) {
+ asyncUpdatesOrdinal = AsyncUpdates.AUTOMATIC.ordinal();
+ }
+ setAsyncUpdates(AsyncUpdates.values()[asyncUpdatesOrdinal]);
+ }
+
setIgnoreDisabledSystemAnimations(
ta.getBoolean(
R.styleable.LottieAnimationView_lottie_ignoreDisabledSystemAnimations,
@@ -207,22 +255,32 @@ import java.util.Set;
)
);
+ if (ta.hasValue(R.styleable.LottieAnimationView_lottie_useCompositionFrameRate)) {
+ setUseCompositionFrameRate(ta.getBoolean(R.styleable.LottieAnimationView_lottie_useCompositionFrameRate, false));
+ }
+
ta.recycle();
lottieDrawable.setSystemAnimationsAreEnabled(Utils.getAnimationScale(getContext()) != 0f);
}
@Override public void setImageResource(int resId) {
+ this.animationResId = 0;
+ animationName = null;
cancelLoaderTask();
super.setImageResource(resId);
}
@Override public void setImageDrawable(Drawable drawable) {
+ this.animationResId = 0;
+ animationName = null;
cancelLoaderTask();
super.setImageDrawable(drawable);
}
@Override public void setImageBitmap(Bitmap bm) {
+ this.animationResId = 0;
+ animationName = null;
cancelLoaderTask();
super.setImageBitmap(bm);
}
@@ -290,7 +348,7 @@ import java.util.Set;
setAnimation(animationResId);
}
if (!userActionsTaken.contains(UserActionTaken.SET_PROGRESS)) {
- setProgress(ss.progress);
+ setProgressInternal(ss.progress, false);
}
if (!userActionsTaken.contains(UserActionTaken.PLAY_OPTION) && ss.isAnimating) {
playAnimation();
@@ -325,6 +383,19 @@ import java.util.Set;
}
/**
+ * Lottie files can specify a target frame rate. By default, Lottie ignores it and re-renders
+ * on every frame. If that behavior is undesirable, you can set this to true to use the composition
+ * frame rate instead.
+ * <p>
+ * Note: composition frame rates are usually lower than display frame rates
+ * so this will likely make your animation feel janky. However, it may be desirable
+ * for specific situations such as pixel art that are intended to have low frame rates.
+ */
+ public void setUseCompositionFrameRate(boolean useCompositionFrameRate) {
+ lottieDrawable.setUseCompositionFrameRate(useCompositionFrameRate);
+ }
+
+ /**
* Enable this to get merge path support for devices running KitKat (19) and above.
* <p>
* Merge paths currently don't work if the the operand shape is entirely contained within the
@@ -344,9 +415,9 @@ import java.util.Set;
/**
* Sets whether or not Lottie should clip to the original animation composition bounds.
- *
+ * <p>
* When set to true, the parent view may need to disable clipChildren so Lottie can render outside of the LottieAnimationView bounds.
- *
+ * <p>
* Defaults to true.
*/
public void setClipToCompositionBounds(boolean clipToCompositionBounds) {
@@ -355,7 +426,7 @@ import java.util.Set;
/**
* Gets whether or not Lottie should clip to the original animation composition bounds.
- *
+ * <p>
* Defaults to true.
*/
public boolean getClipToCompositionBounds() {
@@ -442,14 +513,32 @@ import java.util.Set;
* Sets the animation from an arbitrary InputStream.
* This will load and deserialize the file asynchronously.
* <p>
+ * If this is a Zip file, wrap your InputStream with a ZipInputStream to use the overload
+ * designed for zip files.
+ * <p>
* This is particularly useful for animations loaded from the network. You can fetch the
* bodymovin json from the network and pass it directly here.
+ * <p>
+ * Auto-closes the stream.
*/
public void setAnimation(InputStream stream, @Nullable String cacheKey) {
setCompositionTask(LottieCompositionFactory.fromJsonInputStream(stream, cacheKey));
}
/**
+ * Sets the animation from a ZipInputStream.
+ * This will load and deserialize the file asynchronously.
+ * <p>
+ * This is particularly useful for animations loaded from the network. You can fetch the
+ * bodymovin json from the network and pass it directly here.
+ * <p>
+ * Auto-closes the stream.
+ */
+ public void setAnimation(ZipInputStream stream, @Nullable String cacheKey) {
+ setCompositionTask(LottieCompositionFactory.fromZipStream(stream, cacheKey));
+ }
+
+ /**
* Load a lottie animation from a url. The url can be a json file or a zip file. Use a zip file if you have images. Simply zip them together and
* lottie
* will unzip and link the images automatically.
@@ -519,6 +608,11 @@ import java.util.Set;
}
private void setCompositionTask(LottieTask<LottieComposition> compositionTask) {
+ LottieResult<LottieComposition> result = compositionTask.getResult();
+ LottieDrawable lottieDrawable = this.lottieDrawable;
+ if (result != null && lottieDrawable == getDrawable() && lottieDrawable.getComposition() == result.getValue()) {
+ return;
+ }
userActionsTaken.add(UserActionTaken.SET_ANIMATION);
clearComposition();
cancelLoaderTask();
@@ -545,9 +639,11 @@ import java.util.Set;
}
lottieDrawable.setCallback(this);
- this.composition = composition;
ignoreUnschedule = true;
boolean isNewComposition = lottieDrawable.setComposition(composition);
+ if (autoPlay) {
+ lottieDrawable.playAnimation();
+ }
ignoreUnschedule = false;
if (getDrawable() == lottieDrawable && !isNewComposition) {
// We can avoid re-setting the drawable, and invalidating the view, since the composition
@@ -572,7 +668,7 @@ import java.util.Set;
}
@Nullable public LottieComposition getComposition() {
- return composition;
+ return getDrawable() == lottieDrawable ? lottieDrawable.getComposition() : null;
}
/**
@@ -839,7 +935,7 @@ import java.util.Set;
* Be wary if you are using many images, however. Lottie is designed to work with vector shapes
* from After Effects. If your images look like they could be represented with vector shapes,
* see if it is possible to convert them to shape layers and re-export your animation. Check
- * the documentation at http://airbnb.io/lottie for more information about importing shapes from
+ * the documentation at <a href="http://airbnb.io/lottie">airbnb.io/lottie</a> for more information about importing shapes from
* Sketch or Illustrator to avoid this.
*/
public void setImageAssetsFolder(String imageAssetsFolder) {
@@ -854,7 +950,7 @@ import java.util.Set;
/**
* When true, dynamically set bitmaps will be drawn with the exact bounds of the original animation, regardless of the bitmap size.
* When false, dynamically set bitmaps will be drawn at the top left of the original image but with its own bounds.
- *
+ * <p>
* Defaults to false.
*/
public void setMaintainOriginalImageBounds(boolean maintainOriginalImageBounds) {
@@ -864,7 +960,7 @@ import java.util.Set;
/**
* When true, dynamically set bitmaps will be drawn with the exact bounds of the original animation, regardless of the bitmap size.
* When false, dynamically set bitmaps will be drawn at the top left of the original image but with its own bounds.
- *
+ * <p>
* Defaults to false.
*/
public boolean getMaintainOriginalImageBounds() {
@@ -890,7 +986,7 @@ import java.util.Set;
* Be wary if you are using many images, however. Lottie is designed to work with vector shapes
* from After Effects. If your images look like they could be represented with vector shapes,
* see if it is possible to convert them to shape layers and re-export your animation. Check
- * the documentation at http://airbnb.io/lottie for more information about importing shapes from
+ * the documentation at <a href="http://airbnb.io/lottie">airbnb.io/lottie</a> for more information about importing shapes from
* Sketch or Illustrator to avoid this.
*/
public void setImageAssetDelegate(ImageAssetDelegate assetDelegate) {
@@ -898,6 +994,21 @@ import java.util.Set;
}
/**
+ * By default, Lottie will look in src/assets/fonts/FONT_NAME.ttf
+ * where FONT_NAME is the fFamily specified in your Lottie file.
+ * If your fonts have a different extension, you can override the
+ * default here.
+ * <p>
+ * Alternatively, you can use {@link #setFontAssetDelegate(FontAssetDelegate)}
+ * for more control.
+ *
+ * @see #setFontAssetDelegate(FontAssetDelegate)
+ */
+ public void setDefaultFontFileExtension(String extension) {
+ lottieDrawable.setDefaultFontFileExtension(extension);
+ }
+
+ /**
* Use this to manually set fonts.
*/
public void setFontAssetDelegate(FontAssetDelegate assetDelegate) {
@@ -905,6 +1016,21 @@ import java.util.Set;
}
/**
+ * Set a map from font name keys to Typefaces.
+ * The keys can be in the form:
+ * * fontFamily
+ * * fontFamily-fontStyle
+ * * fontName
+ * All 3 are defined as fName, fFamily, and fStyle in the Lottie file.
+ * <p>
+ * If you change a value in fontMap, create a new map or call
+ * {@link #invalidate()}. Setting the same map again will noop.
+ */
+ public void setFontMap(@Nullable Map<String, Typeface> fontMap) {
+ lottieDrawable.setFontMap(fontMap);
+ }
+
+ /**
* Set this to replace animation text with custom text at runtime
*/
public void setTextDelegate(TextDelegate textDelegate) {
@@ -924,6 +1050,13 @@ import java.util.Set;
}
/**
+ * Clear the value callback for all nodes that match the given {@link KeyPath} and property.
+ */
+ public <T> void clearValueCallback(KeyPath keyPath, T property) {
+ lottieDrawable.addValueCallback(keyPath, property, (LottieValueCallback<T>) null);
+ }
+
+ /**
* Add a property callback for the specified {@link KeyPath}. This {@link KeyPath} can resolve
* to multiple contents. In that case, the callback's value will apply to all of them.
* <p>
@@ -950,6 +1083,7 @@ import java.util.Set;
@MainThread
public void cancelAnimation() {
+ autoPlay = false;
userActionsTaken.add(UserActionTaken.PLAY_OPTION);
lottieDrawable.cancelAnimation();
}
@@ -977,7 +1111,15 @@ import java.util.Set;
}
public void setProgress(@FloatRange(from = 0f, to = 1f) float progress) {
- userActionsTaken.add(UserActionTaken.SET_PROGRESS);
+ setProgressInternal(progress, true);
+ }
+
+ private void setProgressInternal(
+ @FloatRange(from = 0f, to = 1f) float progress,
+ boolean fromUser) {
+ if (fromUser) {
+ userActionsTaken.add(UserActionTaken.SET_PROGRESS);
+ }
lottieDrawable.setProgress(progress);
}
@@ -986,6 +1128,7 @@ import java.util.Set;
}
public long getDuration() {
+ LottieComposition composition = getComposition();
return composition != null ? (long) composition.getDuration() : 0;
}
@@ -999,7 +1142,6 @@ import java.util.Set;
}
private void clearComposition() {
- composition = null;
lottieDrawable.clearComposition();
}
@@ -1022,7 +1164,7 @@ import java.util.Set;
* Call this to set whether or not to render with hardware or software acceleration.
* Lottie defaults to Automatic which will use hardware acceleration unless:
* 1) There are dash paths and the device is pre-Pie.
- * 2) There are more than 4 masks and mattes and the device is pre-Pie.
+ * 2) There are more than 4 masks and mattes.
* Hardware acceleration is generally faster for those devices unless
* there are many large mattes and masks in which case there is a lot
* of GPU uploadTexture thrashing which makes it much slower.
@@ -1047,6 +1189,30 @@ import java.util.Set;
}
/**
+ * Returns the current value of {@link AsyncUpdates}. Refer to the docs for {@link AsyncUpdates} for more info.
+ */
+ public AsyncUpdates getAsyncUpdates() {
+ return lottieDrawable.getAsyncUpdates();
+ }
+
+ /**
+ * Similar to {@link #getAsyncUpdates()} except it returns the actual
+ * boolean value for whether async updates are enabled or not.
+ */
+ public boolean getAsyncUpdatesEnabled() {
+ return lottieDrawable.getAsyncUpdatesEnabled();
+ }
+
+ /**
+ * **Note: this API is experimental and may changed.**
+ * <p/>
+ * Sets the current value for {@link AsyncUpdates}. Refer to the docs for {@link AsyncUpdates} for more info.
+ */
+ public void setAsyncUpdates(AsyncUpdates asyncUpdates) {
+ lottieDrawable.setAsyncUpdates(asyncUpdates);
+ }
+
+ /**
* Sets whether to apply opacity to the each layer instead of shape.
* <p>
* Opacity is normally applied directly to a shape. In cases where translucent shapes overlap, applying opacity to a layer will be more accurate
@@ -1063,6 +1229,21 @@ import java.util.Set;
}
/**
+ * @see #setClipTextToBoundingBox(boolean)
+ */
+ public boolean getClipTextToBoundingBox() {
+ return lottieDrawable.getClipTextToBoundingBox();
+ }
+
+ /**
+ * When true, if there is a bounding box set on a text layer (paragraph text), any text
+ * that overflows past its height will not be drawn.
+ */
+ public void setClipTextToBoundingBox(boolean clipTextToBoundingBox) {
+ lottieDrawable.setClipTextToBoundingBox(clipTextToBoundingBox);
+ }
+
+ /**
* This API no longer has any effect.
*/
@Deprecated
@@ -1072,7 +1253,7 @@ import java.util.Set;
}
public boolean addLottieOnCompositionLoadedListener(@NonNull LottieOnCompositionLoadedListener lottieOnCompositionLoadedListener) {
- LottieComposition composition = this.composition;
+ LottieComposition composition = getComposition();
if (composition != null) {
lottieOnCompositionLoadedListener.onCompositionLoaded(composition);
}
diff --git a/lottie/src/main/java/com/airbnb/lottie/LottieComposition.java b/lottie/src/main/java/com/airbnb/lottie/LottieComposition.java
index a5a2a8ff..a03e3921 100644
--- a/lottie/src/main/java/com/airbnb/lottie/LottieComposition.java
+++ b/lottie/src/main/java/com/airbnb/lottie/LottieComposition.java
@@ -19,15 +19,18 @@ import com.airbnb.lottie.model.layer.Layer;
import com.airbnb.lottie.parser.moshi.JsonReader;
import com.airbnb.lottie.utils.Logger;
import com.airbnb.lottie.utils.MiscUtils;
+import com.airbnb.lottie.utils.Utils;
import org.json.JSONObject;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Set;
/**
* After Effects/Bodymovin composition model. This is the serialized model from which the
@@ -44,6 +47,7 @@ public class LottieComposition {
private final HashSet<String> warnings = new HashSet<>();
private Map<String, List<Layer>> precomps;
private Map<String, LottieImageAsset> images;
+ private float imagesDpScale;
/**
* Map of font names to fonts
*/
@@ -71,7 +75,7 @@ public class LottieComposition {
@RestrictTo(RestrictTo.Scope.LIBRARY)
public void init(Rect bounds, float startFrame, float endFrame, float frameRate,
List<Layer> layers, LongSparseArray<Layer> layerMap, Map<String,
- List<Layer>> precomps, Map<String, LottieImageAsset> images,
+ List<Layer>> precomps, Map<String, LottieImageAsset> images, float imagesDpScale,
SparseArrayCompat<FontCharacter> characters, Map<String, Font> fonts,
List<Marker> markers) {
this.bounds = bounds;
@@ -82,6 +86,7 @@ public class LottieComposition {
this.layerMap = layerMap;
this.precomps = precomps;
this.images = images;
+ this.imagesDpScale = imagesDpScale;
this.characters = characters;
this.fonts = fonts;
this.markers = markers;
@@ -208,8 +213,19 @@ public class LottieComposition {
* Returns a map of image asset id to {@link LottieImageAsset}. These assets contain image metadata exported
* from After Effects or other design tool. The resulting Bitmaps can be set directly on the image asset so
* they can be loaded once and reused across compositions.
+ *
+ * If the context dp scale has changed since the last time images were retrieved, images will be rescaled.
*/
public Map<String, LottieImageAsset> getImages() {
+ float dpScale = Utils.dpScale();
+ if (dpScale != imagesDpScale) {
+ Set<Map.Entry<String, LottieImageAsset>> entries = images.entrySet();
+
+ for (Map.Entry<String, LottieImageAsset> entry : entries) {
+ images.put(entry.getKey(), entry.getValue().copyWithScale(imagesDpScale / dpScale));
+ }
+ }
+ imagesDpScale = dpScale;
return images;
}
@@ -237,6 +253,7 @@ public class LottieComposition {
*/
@Deprecated
public static class Factory {
+
private Factory() {
}
@@ -363,6 +380,7 @@ public class LottieComposition {
@SuppressWarnings("deprecation")
private static final class ListenerAdapter implements LottieListener<LottieComposition>, Cancellable {
+
private final OnCompositionLoadedListener listener;
private boolean cancelled = false;
@@ -382,4 +400,4 @@ public class LottieComposition {
}
}
}
-} \ No newline at end of file
+}
diff --git a/lottie/src/main/java/com/airbnb/lottie/LottieCompositionFactory.java b/lottie/src/main/java/com/airbnb/lottie/LottieCompositionFactory.java
index 1ff727ba..7b0f9e77 100644
--- a/lottie/src/main/java/com/airbnb/lottie/LottieCompositionFactory.java
+++ b/lottie/src/main/java/com/airbnb/lottie/LottieCompositionFactory.java
@@ -9,12 +9,17 @@ import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
+import android.graphics.Typeface;
+import android.util.Base64;
+import android.util.Log;
import androidx.annotation.Nullable;
import androidx.annotation.RawRes;
import androidx.annotation.WorkerThread;
+import com.airbnb.lottie.model.Font;
import com.airbnb.lottie.model.LottieCompositionCache;
+import com.airbnb.lottie.network.NetworkCache;
import com.airbnb.lottie.parser.LottieCompositionMoshiParser;
import com.airbnb.lottie.parser.moshi.JsonReader;
import com.airbnb.lottie.utils.Logger;
@@ -23,13 +28,21 @@ import com.airbnb.lottie.utils.Utils;
import org.json.JSONObject;
import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.io.OutputStream;
import java.lang.ref.WeakReference;
+import java.util.ArrayList;
import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.zip.GZIPInputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
@@ -45,18 +58,21 @@ import okio.Okio;
*/
@SuppressWarnings({"WeakerAccess", "unused", "NullAway"})
public class LottieCompositionFactory {
+
/**
* Keep a map of cache keys to in-progress tasks and return them for new requests.
* Without this, simultaneous requests to parse a composition will trigger multiple parallel
* parse tasks prior to the cache getting populated.
*/
private static final Map<String, LottieTask<LottieComposition>> taskCache = new HashMap<>();
+ private static final Set<LottieTaskIdleListener> taskIdleListeners = new HashSet<>();
/**
* reference magic bytes for zip compressed files.
* useful to determine if an InputStream is a zip file or not
*/
- private static final byte[] MAGIC = new byte[]{0x50, 0x4b, 0x03, 0x04};
+ private static final byte[] ZIP_MAGIC = new byte[]{0x50, 0x4b, 0x03, 0x04};
+ private static final byte[] GZIP_MAGIC = new byte[]{0x1f, (byte) 0x8b, 0x08};
private LottieCompositionFactory() {
@@ -73,7 +89,25 @@ public class LottieCompositionFactory {
public static void clearCache(Context context) {
taskCache.clear();
LottieCompositionCache.getInstance().clear();
- L.networkCache(context).clear();
+ final NetworkCache networkCache = L.networkCache(context);
+ if (networkCache != null) {
+ networkCache.clear();
+ }
+ }
+
+ /**
+ * Use this to register a callback for when the composition factory is idle or not.
+ * This can be used to provide data to an espresso idling resource.
+ * Refer to FragmentVisibilityTests and its LottieIdlingResource in the Lottie repo for
+ * an example.
+ */
+ public static void registerLottieTaskIdleListener(LottieTaskIdleListener listener) {
+ taskIdleListeners.add(listener);
+ listener.onIdleChanged(taskCache.size() == 0);
+ }
+
+ public static void unregisterLottieTaskIdleListener(LottieTaskIdleListener listener) {
+ taskIdleListeners.remove(listener);
}
/**
@@ -94,12 +128,12 @@ public class LottieCompositionFactory {
*/
public static LottieTask<LottieComposition> fromUrl(final Context context, final String url, @Nullable final String cacheKey) {
return cache(cacheKey, () -> {
- LottieResult<LottieComposition> result = L.networkFetcher(context).fetchSync(url, cacheKey);
+ LottieResult<LottieComposition> result = L.networkFetcher(context).fetchSync(context, url, cacheKey);
if (cacheKey != null && result.getValue() != null) {
LottieCompositionCache.getInstance().put(cacheKey, result.getValue());
}
return result;
- });
+ }, null);
}
/**
@@ -120,7 +154,11 @@ public class LottieCompositionFactory {
*/
@WorkerThread
public static LottieResult<LottieComposition> fromUrlSync(Context context, String url, @Nullable String cacheKey) {
- LottieResult<LottieComposition> result = L.networkFetcher(context).fetchSync(url, cacheKey);
+ final LottieComposition cachedComposition = cacheKey == null ? null : LottieCompositionCache.getInstance().get(cacheKey);
+ if (cachedComposition != null) {
+ return new LottieResult<>(cachedComposition);
+ }
+ LottieResult<LottieComposition> result = L.networkFetcher(context).fetchSync(context, url, cacheKey);
if (cacheKey != null && result.getValue() != null) {
LottieCompositionCache.getInstance().put(cacheKey, result.getValue());
}
@@ -153,7 +191,7 @@ public class LottieCompositionFactory {
public static LottieTask<LottieComposition> fromAsset(Context context, final String fileName, @Nullable final String cacheKey) {
// Prevent accidentally leaking an Activity.
final Context appContext = context.getApplicationContext();
- return cache(cacheKey, () -> fromAssetSync(appContext, fileName, cacheKey));
+ return cache(cacheKey, () -> fromAssetSync(appContext, fileName, cacheKey), null);
}
/**
@@ -163,7 +201,7 @@ public class LottieCompositionFactory {
* <p>
* To skip the cache, add null as a third parameter.
*
- * @see #fromZipStreamSync(ZipInputStream, String)
+ * @see #fromZipStreamSync(Context, ZipInputStream, String)
*/
@WorkerThread
public static LottieResult<LottieComposition> fromAssetSync(Context context, String fileName) {
@@ -178,15 +216,22 @@ public class LottieCompositionFactory {
* <p>
* Pass null as the cache key to skip the cache.
*
- * @see #fromZipStreamSync(ZipInputStream, String)
+ * @see #fromZipStreamSync(Context, ZipInputStream, String)
*/
@WorkerThread
public static LottieResult<LottieComposition> fromAssetSync(Context context, String fileName, @Nullable String cacheKey) {
+ final LottieComposition cachedComposition = cacheKey == null ? null : LottieCompositionCache.getInstance().get(cacheKey);
+ if (cachedComposition != null) {
+ return new LottieResult<>(cachedComposition);
+ }
try {
- if (fileName.endsWith(".zip") || fileName.endsWith(".lottie")) {
- return fromZipStreamSync(new ZipInputStream(context.getAssets().open(fileName)), cacheKey);
+ BufferedSource source = Okio.buffer(source(context.getAssets().open(fileName)));
+ if (isZipCompressed(source)) {
+ return fromZipStreamSync(context, new ZipInputStream(source.inputStream()), cacheKey);
+ } else if (isGzipCompressed(source)) {
+ return fromJsonInputStreamSync(new GZIPInputStream(source.inputStream()), cacheKey);
}
- return fromJsonInputStreamSync(context.getAssets().open(fileName), cacheKey);
+ return fromJsonInputStreamSync(source.inputStream(), cacheKey);
} catch (IOException e) {
return new LottieResult<>(e);
}
@@ -223,7 +268,7 @@ public class LottieCompositionFactory {
@Nullable Context originalContext = contextRef.get();
Context context1 = originalContext != null ? originalContext : appContext;
return fromRawResSync(context1, rawRes, cacheKey);
- });
+ }, null);
}
/**
@@ -251,10 +296,21 @@ public class LottieCompositionFactory {
*/
@WorkerThread
public static LottieResult<LottieComposition> fromRawResSync(Context context, @RawRes int rawRes, @Nullable String cacheKey) {
+ final LottieComposition cachedComposition = cacheKey == null ? null : LottieCompositionCache.getInstance().get(cacheKey);
+ if (cachedComposition != null) {
+ return new LottieResult<>(cachedComposition);
+ }
try {
BufferedSource source = Okio.buffer(source(context.getResources().openRawResource(rawRes)));
if (isZipCompressed(source)) {
- return fromZipStreamSync(new ZipInputStream(source.inputStream()), cacheKey);
+ return fromZipStreamSync(context, new ZipInputStream(source.inputStream()), cacheKey);
+ } else if (isGzipCompressed(source)) {
+ try {
+ return fromJsonInputStreamSync(new GZIPInputStream(source.inputStream()), cacheKey);
+ } catch (IOException e) {
+ // This shouldn't happen because we check the header for magic bytes.
+ return new LottieResult<>(e);
+ }
}
return fromJsonInputStreamSync(source.inputStream(), cacheKey);
} catch (Resources.NotFoundException e) {
@@ -280,7 +336,18 @@ public class LottieCompositionFactory {
* @see #fromJsonInputStreamSync(InputStream, String, boolean)
*/
public static LottieTask<LottieComposition> fromJsonInputStream(final InputStream stream, @Nullable final String cacheKey) {
- return cache(cacheKey, () -> fromJsonInputStreamSync(stream, cacheKey));
+ return cache(cacheKey, () -> fromJsonInputStreamSync(stream, cacheKey), () -> closeQuietly(stream));
+ }
+
+ /**
+ * @see #fromJsonInputStreamSync(InputStream, String, boolean)
+ */
+ public static LottieTask<LottieComposition> fromJsonInputStream(final InputStream stream, @Nullable final String cacheKey, boolean close) {
+ return cache(cacheKey, () -> fromJsonInputStreamSync(stream, cacheKey, close), () -> {
+ if (close) {
+ closeQuietly(stream);
+ }
+ });
}
/**
@@ -291,19 +358,14 @@ public class LottieCompositionFactory {
return fromJsonInputStreamSync(stream, cacheKey, true);
}
-
+ /**
+ * Return a LottieComposition for the given InputStream to json.
+ */
@WorkerThread
- private static LottieResult<LottieComposition> fromJsonInputStreamSync(InputStream stream, @Nullable String cacheKey, boolean close) {
- try {
- return fromJsonReaderSync(JsonReader.of(buffer(source(stream))), cacheKey);
- } finally {
- if (close) {
- closeQuietly(stream);
- }
- }
+ public static LottieResult<LottieComposition> fromJsonInputStreamSync(InputStream stream, @Nullable String cacheKey, boolean close) {
+ return fromJsonReaderSync(JsonReader.of(buffer(source(stream))), cacheKey, close);
}
-
/**
* @see #fromJsonSync(JSONObject, String)
*/
@@ -312,7 +374,7 @@ public class LottieCompositionFactory {
return cache(cacheKey, () -> {
//noinspection deprecation
return fromJsonSync(json, cacheKey);
- });
+ }, null);
}
/**
@@ -330,7 +392,7 @@ public class LottieCompositionFactory {
* @see #fromJsonStringSync(String, String)
*/
public static LottieTask<LottieComposition> fromJsonString(final String json, @Nullable final String cacheKey) {
- return cache(cacheKey, () -> fromJsonStringSync(json, cacheKey));
+ return cache(cacheKey, () -> fromJsonStringSync(json, cacheKey), null);
}
/**
@@ -339,26 +401,32 @@ public class LottieCompositionFactory {
*/
@WorkerThread
public static LottieResult<LottieComposition> fromJsonStringSync(String json, @Nullable String cacheKey) {
-
-
ByteArrayInputStream stream = new ByteArrayInputStream(json.getBytes());
return fromJsonReaderSync(JsonReader.of(buffer(source(stream))), cacheKey);
}
public static LottieTask<LottieComposition> fromJsonReader(final JsonReader reader, @Nullable final String cacheKey) {
- return cache(cacheKey, () -> fromJsonReaderSync(reader, cacheKey));
+ return cache(cacheKey, () -> fromJsonReaderSync(reader, cacheKey), () -> Utils.closeQuietly(reader));
}
-
@WorkerThread
public static LottieResult<LottieComposition> fromJsonReaderSync(com.airbnb.lottie.parser.moshi.JsonReader reader, @Nullable String cacheKey) {
- return fromJsonReaderSyncInternal(reader, cacheKey, true);
+ return fromJsonReaderSync(reader, cacheKey, true);
}
+ @WorkerThread
+ public static LottieResult<LottieComposition> fromJsonReaderSync(com.airbnb.lottie.parser.moshi.JsonReader reader, @Nullable String cacheKey,
+ boolean close) {
+ return fromJsonReaderSyncInternal(reader, cacheKey, close);
+ }
private static LottieResult<LottieComposition> fromJsonReaderSyncInternal(
com.airbnb.lottie.parser.moshi.JsonReader reader, @Nullable String cacheKey, boolean close) {
try {
+ final LottieComposition cachedComposition = cacheKey == null ? null : LottieCompositionCache.getInstance().get(cacheKey);
+ if (cachedComposition != null) {
+ return new LottieResult<>(cachedComposition);
+ }
LottieComposition composition = LottieCompositionMoshiParser.parse(reader);
if (cacheKey != null) {
LottieCompositionCache.getInstance().put(cacheKey, composition);
@@ -373,31 +441,113 @@ public class LottieCompositionFactory {
}
}
-
+ /**
+ * In this overload, embedded fonts will NOT be parsed. If your zip file has custom fonts, use the overload
+ * that takes Context as the first parameter.
+ */
public static LottieTask<LottieComposition> fromZipStream(final ZipInputStream inputStream, @Nullable final String cacheKey) {
- return cache(cacheKey, () -> fromZipStreamSync(inputStream, cacheKey));
+ return fromZipStream(null, inputStream, cacheKey);
+ }
+
+ /**
+ * In this overload, embedded fonts will NOT be parsed. If your zip file has custom fonts, use the overload
+ * that takes Context as the first parameter.
+ */
+ public static LottieTask<LottieComposition> fromZipStream(final ZipInputStream inputStream, @Nullable final String cacheKey, boolean close) {
+ return fromZipStream(null, inputStream, cacheKey, close);
+ }
+
+ /**
+ * @see #fromZipStreamSync(Context, ZipInputStream, String)
+ */
+ public static LottieTask<LottieComposition> fromZipStream(Context context, final ZipInputStream inputStream, @Nullable final String cacheKey) {
+ return cache(cacheKey, () -> fromZipStreamSync(context, inputStream, cacheKey), () -> closeQuietly(inputStream));
+ }
+
+ /**
+ * @see #fromZipStreamSync(Context, ZipInputStream, String)
+ */
+ public static LottieTask<LottieComposition> fromZipStream(Context context, final ZipInputStream inputStream,
+ @Nullable final String cacheKey, boolean close) {
+ return cache(cacheKey, () -> fromZipStreamSync(context, inputStream, cacheKey), close ? () -> closeQuietly(inputStream) : null);
}
/**
* Parses a zip input stream into a Lottie composition.
* Your zip file should just be a folder with your json file and images zipped together.
* It will automatically store and configure any images inside the animation if they exist.
+ * <p>
+ * In this overload, embedded fonts will NOT be parsed. If your zip file has custom fonts, use the overload
+ * that takes Context as the first parameter.
+ * <p>
+ * The ZipInputStream will be automatically closed at the end. If you would like to keep it open, use the overload
+ * with a close parameter and pass in false.
*/
- @WorkerThread
public static LottieResult<LottieComposition> fromZipStreamSync(ZipInputStream inputStream, @Nullable String cacheKey) {
+ return fromZipStreamSync(inputStream, cacheKey, true);
+ }
+
+ /**
+ * Parses a zip input stream into a Lottie composition.
+ * Your zip file should just be a folder with your json file and images zipped together.
+ * It will automatically store and configure any images inside the animation if they exist.
+ * <p>
+ * In this overload, embedded fonts will NOT be parsed. If your zip file has custom fonts, use the overload
+ * that takes Context as the first parameter.
+ */
+ public static LottieResult<LottieComposition> fromZipStreamSync(ZipInputStream inputStream, @Nullable String cacheKey, boolean close) {
+ return fromZipStreamSync(null, inputStream, cacheKey, close);
+ }
+
+ /**
+ * Parses a zip input stream into a Lottie composition.
+ * Your zip file should just be a folder with your json file and images zipped together.
+ * It will automatically store and configure any images inside the animation if they exist.
+ * <p>
+ * The ZipInputStream will be automatically closed at the end. If you would like to keep it open, use the overload
+ * with a close parameter and pass in false.
+ *
+ * @param context is optional and only needed if your zip file contains ttf or otf fonts. If yours doesn't, you may pass null.
+ * Embedded fonts may be .ttf or .otf files, can be in subdirectories, but must have the same name as the
+ * font family (fFamily) in your animation file.
+ */
+ @WorkerThread
+ public static LottieResult<LottieComposition> fromZipStreamSync(@Nullable Context context, ZipInputStream inputStream, @Nullable String cacheKey) {
+ return fromZipStreamSync(context, inputStream, cacheKey, true);
+ }
+
+ /**
+ * Parses a zip input stream into a Lottie composition.
+ * Your zip file should just be a folder with your json file and images zipped together.
+ * It will automatically store and configure any images inside the animation if they exist.
+ *
+ * @param context is optional and only needed if your zip file contains ttf or otf fonts. If yours doesn't, you may pass null.
+ * Embedded fonts may be .ttf or .otf files, can be in subdirectories, but must have the same name as the
+ * font family (fFamily) in your animation file.
+ */
+ @WorkerThread
+ public static LottieResult<LottieComposition> fromZipStreamSync(@Nullable Context context, ZipInputStream inputStream,
+ @Nullable String cacheKey, boolean close) {
try {
- return fromZipStreamSyncInternal(inputStream, cacheKey);
+ return fromZipStreamSyncInternal(context, inputStream, cacheKey);
} finally {
- closeQuietly(inputStream);
+ if (close) {
+ closeQuietly(inputStream);
+ }
}
}
@WorkerThread
- private static LottieResult<LottieComposition> fromZipStreamSyncInternal(ZipInputStream inputStream, @Nullable String cacheKey) {
+ private static LottieResult<LottieComposition> fromZipStreamSyncInternal(Context context, ZipInputStream inputStream, @Nullable String cacheKey) {
LottieComposition composition = null;
Map<String, Bitmap> images = new HashMap<>();
+ Map<String, Typeface> fonts = new HashMap<>();
try {
+ final LottieComposition cachedComposition = cacheKey == null ? null : LottieCompositionCache.getInstance().get(cacheKey);
+ if (cachedComposition != null) {
+ return new LottieResult<>(cachedComposition);
+ }
ZipEntry entry = inputStream.getNextEntry();
while (entry != null) {
final String entryName = entry.getName();
@@ -412,6 +562,29 @@ public class LottieCompositionFactory {
String[] splitName = entryName.split("/");
String name = splitName[splitName.length - 1];
images.put(name, BitmapFactory.decodeStream(inputStream));
+ } else if (entryName.contains(".ttf") || entryName.contains(".otf")) {
+ String[] splitName = entryName.split("/");
+ String fileName = splitName[splitName.length - 1];
+ String fontFamily = fileName.split("\\.")[0];
+ File tempFile = new File(context.getCacheDir(), fileName);
+ FileOutputStream fos = new FileOutputStream(tempFile);
+ try {
+ try (OutputStream output = new FileOutputStream(tempFile)) {
+ byte[] buffer = new byte[4 * 1024];
+ int read;
+ while ((read = inputStream.read(buffer)) != -1) {
+ output.write(buffer, 0, read);
+ }
+ output.flush();
+ }
+ } catch (Throwable e) {
+ Logger.warning("Unable to save font " + fontFamily + " to the temporary file: " + fileName + ". ", e);
+ }
+ Typeface typeface = Typeface.createFromFile(tempFile);
+ if (!tempFile.delete()) {
+ Logger.warning("Failed to delete temp font file " + tempFile.getAbsolutePath() + ".");
+ }
+ fonts.put(fontFamily, typeface);
} else {
inputStream.closeEntry();
}
@@ -434,10 +607,41 @@ public class LottieCompositionFactory {
}
}
- // Ensure that all bitmaps have been set.
- for (Map.Entry<String, LottieImageAsset> entry : composition.getImages().entrySet()) {
- if (entry.getValue().getBitmap() == null) {
- return new LottieResult<>(new IllegalStateException("There is no image for " + entry.getValue().getFileName()));
+ for (Map.Entry<String, Typeface> e : fonts.entrySet()) {
+ boolean found = false;
+ for (Font font : composition.getFonts().values()) {
+ if (font.getFamily().equals(e.getKey())) {
+ found = true;
+ font.setTypeface(e.getValue());
+ }
+ }
+ if (!found) {
+ Logger.warning("Parsed font for " + e.getKey() + " however it was not found in the animation.");
+ }
+ }
+
+ if (images.isEmpty()) {
+ for (Map.Entry<String, LottieImageAsset> entry : composition.getImages().entrySet()) {
+ LottieImageAsset asset = entry.getValue();
+ if (asset == null) {
+ return null;
+ }
+ String filename = asset.getFileName();
+ BitmapFactory.Options opts = new BitmapFactory.Options();
+ opts.inScaled = true;
+ opts.inDensity = 160;
+
+ if (filename.startsWith("data:") && filename.indexOf("base64,") > 0) {
+ // Contents look like a base64 data URI, with the format data:image/png;base64,<data>.
+ byte[] data;
+ try {
+ data = Base64.decode(filename.substring(filename.indexOf(',') + 1), Base64.DEFAULT);
+ } catch (IllegalArgumentException e) {
+ Logger.warning("data URL did not have correct base64 format.", e);
+ return null;
+ }
+ asset.setBitmap(BitmapFactory.decodeByteArray(data, 0, data.length, opts));
+ }
}
}
@@ -451,9 +655,20 @@ public class LottieCompositionFactory {
* Check if a given InputStream points to a .zip compressed file
*/
private static Boolean isZipCompressed(BufferedSource inputSource) {
+ return matchesMagicBytes(inputSource, ZIP_MAGIC);
+ }
+
+ /**
+ * Check if a given InputStream points to a .gzip compressed file
+ */
+ private static Boolean isGzipCompressed(BufferedSource inputSource) {
+ return matchesMagicBytes(inputSource, GZIP_MAGIC);
+ }
+
+ private static Boolean matchesMagicBytes(BufferedSource inputSource, byte[] magic) {
try {
BufferedSource peek = inputSource.peek();
- for (byte b : MAGIC) {
+ for (byte b : magic) {
if (peek.readByte() != b) {
return false;
}
@@ -484,26 +699,39 @@ public class LottieCompositionFactory {
* If not, create a new task for the callable.
* Then, add the new task to the task cache and set up listeners so it gets cleared when done.
*/
- private static LottieTask<LottieComposition> cache(
- @Nullable final String cacheKey, Callable<LottieResult<LottieComposition>> callable) {
+ private static LottieTask<LottieComposition> cache(@Nullable final String cacheKey, Callable<LottieResult<LottieComposition>> callable,
+ @Nullable Runnable onCached) {
+ LottieTask<LottieComposition> task = null;
final LottieComposition cachedComposition = cacheKey == null ? null : LottieCompositionCache.getInstance().get(cacheKey);
if (cachedComposition != null) {
- return new LottieTask<>(() -> new LottieResult<>(cachedComposition));
+ task = new LottieTask<>(cachedComposition);
}
if (cacheKey != null && taskCache.containsKey(cacheKey)) {
- return taskCache.get(cacheKey);
+ task = taskCache.get(cacheKey);
+ }
+ if (task != null) {
+ if (onCached != null) {
+ onCached.run();
+ }
+ return task;
}
- LottieTask<LottieComposition> task = new LottieTask<>(callable);
+ task = new LottieTask<>(callable);
if (cacheKey != null) {
AtomicBoolean resultAlreadyCalled = new AtomicBoolean(false);
task.addListener(result -> {
taskCache.remove(cacheKey);
resultAlreadyCalled.set(true);
+ if (taskCache.size() == 0) {
+ notifyTaskCacheIdleListeners(true);
+ }
});
task.addFailureListener(result -> {
taskCache.remove(cacheKey);
resultAlreadyCalled.set(true);
+ if (taskCache.size() == 0) {
+ notifyTaskCacheIdleListeners(true);
+ }
});
// It is technically possible for the task to finish and for the listeners to get called
// before this code runs. If this happens, the task will be put in taskCache but never removed.
@@ -511,8 +739,18 @@ public class LottieCompositionFactory {
// for long enough for the task to finish and call the listeners. Unlikely but not impossible.
if (!resultAlreadyCalled.get()) {
taskCache.put(cacheKey, task);
+ if (taskCache.size() == 1) {
+ notifyTaskCacheIdleListeners(false);
+ }
}
}
return task;
}
+
+ private static void notifyTaskCacheIdleListeners(boolean idle) {
+ List<LottieTaskIdleListener> listeners = new ArrayList<>(taskIdleListeners);
+ for (int i = 0; i < listeners.size(); i++) {
+ listeners.get(i).onIdleChanged(idle);
+ }
+ }
}
diff --git a/lottie/src/main/java/com/airbnb/lottie/LottieConfig.java b/lottie/src/main/java/com/airbnb/lottie/LottieConfig.java
index 6a6fa8da..09f5e57b 100644
--- a/lottie/src/main/java/com/airbnb/lottie/LottieConfig.java
+++ b/lottie/src/main/java/com/airbnb/lottie/LottieConfig.java
@@ -18,12 +18,19 @@ public class LottieConfig {
@Nullable final LottieNetworkFetcher networkFetcher;
@Nullable final LottieNetworkCacheProvider cacheProvider;
final boolean enableSystraceMarkers;
+ final boolean enableNetworkCache;
+ final boolean disablePathInterpolatorCache;
+ final AsyncUpdates defaultAsyncUpdates;
private LottieConfig(@Nullable LottieNetworkFetcher networkFetcher, @Nullable LottieNetworkCacheProvider cacheProvider,
- boolean enableSystraceMarkers) {
+ boolean enableSystraceMarkers, boolean enableNetworkCache, boolean disablePathInterpolatorCache,
+ AsyncUpdates defaultAsyncUpdates) {
this.networkFetcher = networkFetcher;
this.cacheProvider = cacheProvider;
this.enableSystraceMarkers = enableSystraceMarkers;
+ this.enableNetworkCache = enableNetworkCache;
+ this.disablePathInterpolatorCache = disablePathInterpolatorCache;
+ this.defaultAsyncUpdates = defaultAsyncUpdates;
}
public static final class Builder {
@@ -33,6 +40,9 @@ public class LottieConfig {
@Nullable
private LottieNetworkCacheProvider cacheProvider;
private boolean enableSystraceMarkers = false;
+ private boolean enableNetworkCache = true;
+ private boolean disablePathInterpolatorCache = true;
+ private AsyncUpdates defaultAsyncUpdates = AsyncUpdates.AUTOMATIC;
/**
* Lottie has a default network fetching stack built on {@link java.net.HttpURLConnection}. However, if you would like to hook into your own
@@ -98,9 +108,43 @@ public class LottieConfig {
return this;
}
+ /**
+ * Disable this if you want to completely disable internal Lottie cache for retrieving network animations.
+ * Internal network cache is enabled by default.
+ */
+ @NonNull
+ public Builder setEnableNetworkCache(boolean enable) {
+ enableNetworkCache = enable;
+ return this;
+ }
+
+ /**
+ * When parsing animations, Lottie has a path interpolator cache. This cache allows Lottie to reuse PathInterpolators
+ * across an animation. This is desirable in most cases. However, when shared across screenshot tests, it can cause slight
+ * deviations in the rendering due to underlying approximations in the PathInterpolator.
+ *
+ * The cache is enabled by default and should probably only be disabled for screenshot tests.
+ */
+ @NonNull
+ public Builder setDisablePathInterpolatorCache(boolean disable) {
+ disablePathInterpolatorCache = disable;
+ return this;
+ }
+
+ /**
+ * Sets the default value for async updates.
+ * @see LottieDrawable#setAsyncUpdates(AsyncUpdates)
+ */
+ @NonNull
+ public Builder setDefaultAsyncUpdates(AsyncUpdates asyncUpdates) {
+ defaultAsyncUpdates = asyncUpdates;
+ return this;
+ }
+
@NonNull
public LottieConfig build() {
- return new LottieConfig(networkFetcher, cacheProvider, enableSystraceMarkers);
+ return new LottieConfig(networkFetcher, cacheProvider, enableSystraceMarkers, enableNetworkCache, disablePathInterpolatorCache,
+ defaultAsyncUpdates);
}
}
-} \ No newline at end of file
+}
diff --git a/lottie/src/main/java/com/airbnb/lottie/LottieDrawable.java b/lottie/src/main/java/com/airbnb/lottie/LottieDrawable.java
index 59500a00..37395fa6 100644
--- a/lottie/src/main/java/com/airbnb/lottie/LottieDrawable.java
+++ b/lottie/src/main/java/com/airbnb/lottie/LottieDrawable.java
@@ -16,6 +16,8 @@ import android.graphics.Typeface;
import android.graphics.drawable.Animatable;
import android.graphics.drawable.Drawable;
import android.os.Build;
+import android.os.Handler;
+import android.os.Looper;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
@@ -33,11 +35,13 @@ import androidx.annotation.RestrictTo;
import com.airbnb.lottie.animation.LPaint;
import com.airbnb.lottie.manager.FontAssetManager;
import com.airbnb.lottie.manager.ImageAssetManager;
+import com.airbnb.lottie.model.Font;
import com.airbnb.lottie.model.KeyPath;
import com.airbnb.lottie.model.Marker;
import com.airbnb.lottie.model.layer.CompositionLayer;
import com.airbnb.lottie.parser.LayerParser;
import com.airbnb.lottie.utils.Logger;
+import com.airbnb.lottie.utils.LottieThreadFactory;
import com.airbnb.lottie.utils.LottieValueAnimator;
import com.airbnb.lottie.utils.MiscUtils;
import com.airbnb.lottie.value.LottieFrameInfo;
@@ -47,9 +51,16 @@ import com.airbnb.lottie.value.SimpleLottieValueCallback;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Executor;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
/**
* This can be used to show an lottie animation in any place that would normally take a drawable.
@@ -74,6 +85,28 @@ public class LottieDrawable extends Drawable implements Drawable.Callback, Anima
RESUME,
}
+ /**
+ * Prior to Oreo, you could only call invalidateDrawable() from the main thread.
+ * This means that when async updates are enabled, we must post the invalidate call to the main thread.
+ * Newer devices can call invalidate directly from whatever thread asyncUpdates runs on.
+ */
+ private static final boolean invalidateSelfOnMainThread = Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1;
+
+ /**
+ * The marker to use if "reduced motion" is enabled.
+ * Supported marker names are case insensitive, and include:
+ * - reduced motion
+ * - reducedMotion
+ * - reduced_motion
+ * - reduced-motion
+ */
+ private static final List<String> ALLOWED_REDUCED_MOTION_MARKERS = Arrays.asList(
+ "reduced motion",
+ "reduced_motion",
+ "reduced-motion",
+ "reducedmotion"
+ );
+
private LottieComposition composition;
private final LottieValueAnimator animator = new LottieValueAnimator();
@@ -85,14 +118,6 @@ public class LottieDrawable extends Drawable implements Drawable.Callback, Anima
private OnVisibleAction onVisibleAction = OnVisibleAction.NONE;
private final ArrayList<LazyCompositionTask> lazyCompositionTasks = new ArrayList<>();
- private final ValueAnimator.AnimatorUpdateListener progressUpdateListener = new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- if (compositionLayer != null) {
- compositionLayer.setProgress(animator.getAnimatedValueAbsolute());
- }
- }
- };
/**
* ImageAssetManager created automatically by Lottie for views.
@@ -106,6 +131,14 @@ public class LottieDrawable extends Drawable implements Drawable.Callback, Anima
@Nullable
private FontAssetManager fontAssetManager;
@Nullable
+ private Map<String, Typeface> fontMap;
+ /**
+ * Will be set if manually overridden by {@link #setDefaultFontFileExtension(String)}.
+ * This must be stored as a field in case it is set before the font asset delegate
+ * has been created.
+ */
+ @Nullable String defaultFontFileExtension;
+ @Nullable
FontAssetDelegate fontAssetDelegate;
@Nullable
TextDelegate textDelegate;
@@ -118,6 +151,7 @@ public class LottieDrawable extends Drawable implements Drawable.Callback, Anima
private boolean performanceTrackingEnabled;
private boolean outlineMasksAndMattes;
private boolean isApplyingOpacityToLayersEnabled;
+ private boolean clipTextToBoundingBox = false;
private RenderMode renderMode = RenderMode.AUTOMATIC;
/**
@@ -144,6 +178,74 @@ public class LottieDrawable extends Drawable implements Drawable.Callback, Anima
*/
private boolean isDirty = false;
+ /** Use the getter so that it can fall back to {@link L#getDefaultAsyncUpdates()}. */
+ @Nullable private AsyncUpdates asyncUpdates;
+ private final ValueAnimator.AnimatorUpdateListener progressUpdateListener = animation -> {
+ if (getAsyncUpdatesEnabled()) {
+ // Render a new frame.
+ // If draw is called while lastDrawnProgress is still recent enough, it will
+ // draw straight away and then enqueue a background setProgress immediately after draw
+ // finishes.
+ invalidateSelf();
+ } else if (compositionLayer != null) {
+ compositionLayer.setProgress(animator.getAnimatedValueAbsolute());
+ }
+ };
+
+ /**
+ * Ensures that setProgress and draw will never happen at the same time on different threads.
+ * If that were to happen, parts of the animation may be on one frame while other parts would
+ * be on another.
+ */
+ private final Semaphore setProgressDrawLock = new Semaphore(1);
+ /**
+ * The executor that {@link AsyncUpdates} will be run on.
+ * <p/>
+ * Defaults to a core size of 0 so that when no animations are playing, there will be no
+ * idle cores consuming resources.
+ * <p/>
+ * Allows up to two active threads so that if there are many animations, they can all work in parallel.
+ * Two was arbitrarily chosen but should be sufficient for most uses cases. In the case of a single
+ * animation, this should never exceed one.
+ * <p/>
+ * Each thread will timeout after 35ms which gives it enough time to persist for one frame, one dropped frame
+ * and a few extra ms just in case.
+ */
+ private static final Executor setProgressExecutor = new ThreadPoolExecutor(0, 2, 35, TimeUnit.MILLISECONDS,
+ new LinkedBlockingQueue<>(), new LottieThreadFactory());
+ private Handler mainThreadHandler;
+ private Runnable invalidateSelfRunnable;
+
+ private final Runnable updateProgressRunnable = () -> {
+ CompositionLayer compositionLayer = this.compositionLayer;
+ if (compositionLayer == null) {
+ return;
+ }
+ try {
+ setProgressDrawLock.acquire();
+ compositionLayer.setProgress(animator.getAnimatedValueAbsolute());
+ // Refer to invalidateSelfOnMainThread for more info.
+ if (invalidateSelfOnMainThread && isDirty) {
+ if (mainThreadHandler == null) {
+ mainThreadHandler = new Handler(Looper.getMainLooper());
+ invalidateSelfRunnable = () -> {
+ final Callback callback = getCallback();
+ if (callback != null) {
+ callback.invalidateDrawable(this);
+ }
+ };
+ }
+ mainThreadHandler.post(invalidateSelfRunnable);
+ }
+ } catch (InterruptedException e) {
+ // Do nothing.
+ } finally {
+ setProgressDrawLock.release();
+ }
+ };
+ private float lastDrawnProgress = -Float.MAX_VALUE;
+ private static final float MAX_DELTA_MS_ASYNC_SET_PROGRESS = 3 / 60f * 1000;
+
@IntDef({RESTART, REVERSE})
@Retention(RetentionPolicy.SOURCE)
public @interface RepeatMode {
@@ -215,7 +317,7 @@ public class LottieDrawable extends Drawable implements Drawable.Callback, Anima
/**
* Sets whether or not Lottie should clip to the original animation composition bounds.
- *
+ * <p>
* Defaults to true.
*/
public void setClipToCompositionBounds(boolean clipToCompositionBounds) {
@@ -231,7 +333,7 @@ public class LottieDrawable extends Drawable implements Drawable.Callback, Anima
/**
* Gets whether or not Lottie should clip to the original animation composition bounds.
- *
+ * <p>
* Defaults to true.
*/
public boolean getClipToCompositionBounds() {
@@ -250,7 +352,7 @@ public class LottieDrawable extends Drawable implements Drawable.Callback, Anima
* Be wary if you are using many images, however. Lottie is designed to work with vector shapes
* from After Effects. If your images look like they could be represented with vector shapes,
* see if it is possible to convert them to shape layers and re-export your animation. Check
- * the documentation at http://airbnb.io/lottie for more information about importing shapes from
+ * the documentation at <a href="http://airbnb.io/lottie">airbnb.io/lottie</a> for more information about importing shapes from
* Sketch or Illustrator to avoid this.
*/
public void setImagesAssetsFolder(@Nullable String imageAssetsFolder) {
@@ -349,6 +451,36 @@ public class LottieDrawable extends Drawable implements Drawable.Callback, Anima
}
/**
+ * Returns the current value of {@link AsyncUpdates}. Refer to the docs for {@link AsyncUpdates} for more info.
+ */
+ public AsyncUpdates getAsyncUpdates() {
+ AsyncUpdates asyncUpdates = this.asyncUpdates;
+ if (asyncUpdates != null) {
+ return asyncUpdates;
+ }
+ return L.getDefaultAsyncUpdates();
+ }
+
+ /**
+ * Similar to {@link #getAsyncUpdates()} except it returns the actual
+ * boolean value for whether async updates are enabled or not.
+ * This is useful when the mode is automatic and you want to know
+ * whether automatic is defaulting to enabled or not.
+ */
+ public boolean getAsyncUpdatesEnabled() {
+ return getAsyncUpdates() == AsyncUpdates.ENABLED;
+ }
+
+ /**
+ * **Note: this API is experimental and may changed.**
+ * <p/>
+ * Sets the current value for {@link AsyncUpdates}. Refer to the docs for {@link AsyncUpdates} for more info.
+ */
+ public void setAsyncUpdates(@Nullable AsyncUpdates asyncUpdates) {
+ this.asyncUpdates = asyncUpdates;
+ }
+
+ /**
* Returns the actual render mode being used. It will always be {@link RenderMode#HARDWARE} or {@link RenderMode#SOFTWARE}.
* When the render mode is set to AUTOMATIC, the value will be derived from {@link RenderMode#useSoftwareRendering(int, boolean, int)}.
*/
@@ -424,6 +556,24 @@ public class LottieDrawable extends Drawable implements Drawable.Callback, Anima
return isApplyingOpacityToLayersEnabled;
}
+ /**
+ * @see #setClipTextToBoundingBox(boolean)
+ */
+ public boolean getClipTextToBoundingBox() {
+ return clipTextToBoundingBox;
+ }
+
+ /**
+ * When true, if there is a bounding box set on a text layer (paragraph text), any text
+ * that overflows past its height will not be drawn.
+ */
+ public void setClipTextToBoundingBox(boolean clipTextToBoundingBox) {
+ if (clipTextToBoundingBox != this.clipTextToBoundingBox) {
+ this.clipTextToBoundingBox = clipTextToBoundingBox;
+ invalidateSelf();
+ }
+ }
+
private void buildCompositionLayer() {
LottieComposition composition = this.composition;
if (composition == null) {
@@ -447,6 +597,7 @@ public class LottieDrawable extends Drawable implements Drawable.Callback, Anima
composition = null;
compositionLayer = null;
imageAssetManager = null;
+ lastDrawnProgress = -Float.MAX_VALUE;
animator.clearComposition();
invalidateSelf();
}
@@ -469,6 +620,11 @@ public class LottieDrawable extends Drawable implements Drawable.Callback, Anima
return;
}
isDirty = true;
+
+ // Refer to invalidateSelfOnMainThread for more info.
+ if (invalidateSelfOnMainThread && Looper.getMainLooper() != Looper.myLooper()) {
+ return;
+ }
final Callback callback = getCallback();
if (callback != null) {
callback.invalidateDrawable(this);
@@ -496,30 +652,77 @@ public class LottieDrawable extends Drawable implements Drawable.Callback, Anima
return PixelFormat.TRANSLUCENT;
}
+ /**
+ * Helper for the async execution path to potentially call setProgress
+ * before drawing if the current progress has drifted sufficiently far
+ * from the last set progress.
+ *
+ * @see AsyncUpdates
+ * @see #setAsyncUpdates(AsyncUpdates)
+ */
+ private boolean shouldSetProgressBeforeDrawing() {
+ LottieComposition composition = this.composition;
+ if (composition == null) {
+ return false;
+ }
+ float lastDrawnProgress = this.lastDrawnProgress;
+ float currentProgress = animator.getAnimatedValueAbsolute();
+ this.lastDrawnProgress = currentProgress;
+
+ float duration = composition.getDuration();
+
+ float deltaProgress = Math.abs(currentProgress - lastDrawnProgress);
+ float deltaMs = deltaProgress * duration;
+ return deltaMs >= MAX_DELTA_MS_ASYNC_SET_PROGRESS;
+ }
+
@Override
public void draw(@NonNull Canvas canvas) {
- L.beginSection("Drawable#draw");
+ CompositionLayer compositionLayer = this.compositionLayer;
+ if (compositionLayer == null) {
+ return;
+ }
+ boolean asyncUpdatesEnabled = getAsyncUpdatesEnabled();
+ try {
+ if (asyncUpdatesEnabled) {
+ setProgressDrawLock.acquire();
+ }
+ L.beginSection("Drawable#draw");
+
+ if (asyncUpdatesEnabled && shouldSetProgressBeforeDrawing()) {
+ setProgress(animator.getAnimatedValueAbsolute());
+ }
- if (safeMode) {
- try {
+ if (safeMode) {
+ try {
+ if (useSoftwareRendering) {
+ renderAndDrawAsBitmap(canvas, compositionLayer);
+ } else {
+ drawDirectlyToCanvas(canvas);
+ }
+ } catch (Throwable e) {
+ Logger.error("Lottie crashed in draw!", e);
+ }
+ } else {
if (useSoftwareRendering) {
renderAndDrawAsBitmap(canvas, compositionLayer);
} else {
drawDirectlyToCanvas(canvas);
}
- } catch (Throwable e) {
- Logger.error("Lottie crashed in draw!", e);
}
- } else {
- if (useSoftwareRendering) {
- renderAndDrawAsBitmap(canvas, compositionLayer);
- } else {
- drawDirectlyToCanvas(canvas);
+
+ isDirty = false;
+ } catch (InterruptedException e) {
+ // Do nothing.
+ } finally {
+ L.endSection("Drawable#draw");
+ if (asyncUpdatesEnabled) {
+ setProgressDrawLock.release();
+ if (compositionLayer.getProgress() != animator.getAnimatedValueAbsolute()) {
+ setProgressExecutor.execute(updateProgressRunnable);
+ }
}
}
-
- isDirty = false;
- L.endSection("Drawable#draw");
}
/**
@@ -532,16 +735,34 @@ public class LottieDrawable extends Drawable implements Drawable.Callback, Anima
if (compositionLayer == null || composition == null) {
return;
}
+ boolean asyncUpdatesEnabled = getAsyncUpdatesEnabled();
+ try {
+ if (asyncUpdatesEnabled) {
+ setProgressDrawLock.acquire();
+ if (shouldSetProgressBeforeDrawing()) {
+ setProgress(animator.getAnimatedValueAbsolute());
+ }
+ }
- if (useSoftwareRendering) {
- canvas.save();
- canvas.concat(matrix);
- renderAndDrawAsBitmap(canvas, compositionLayer);
- canvas.restore();
- } else {
- compositionLayer.draw(canvas, matrix, alpha);
+ if (useSoftwareRendering) {
+ canvas.save();
+ canvas.concat(matrix);
+ renderAndDrawAsBitmap(canvas, compositionLayer);
+ canvas.restore();
+ } else {
+ compositionLayer.draw(canvas, matrix, alpha);
+ }
+ isDirty = false;
+ } catch (InterruptedException e) {
+ // Do nothing.
+ } finally {
+ if (asyncUpdatesEnabled) {
+ setProgressDrawLock.release();
+ if (compositionLayer.getProgress() != animator.getAnimatedValueAbsolute()) {
+ setProgressExecutor.execute(updateProgressRunnable);
+ }
+ }
}
- isDirty = false;
}
// <editor-fold desc="animator">
@@ -589,7 +810,12 @@ public class LottieDrawable extends Drawable implements Drawable.Callback, Anima
}
}
if (!animationsEnabled()) {
- setFrame((int) (getSpeed() < 0 ? getMinFrame() : getMaxFrame()));
+ Marker markerForAnimationsDisabled = getMarkerForAnimationsDisabled();
+ if (markerForAnimationsDisabled != null) {
+ setFrame((int) markerForAnimationsDisabled.startFrame);
+ } else {
+ setFrame((int) (getSpeed() < 0 ? getMinFrame() : getMaxFrame()));
+ }
animator.endAnimation();
if (!isVisible()) {
onVisibleAction = OnVisibleAction.NONE;
@@ -597,6 +823,25 @@ public class LottieDrawable extends Drawable implements Drawable.Callback, Anima
}
}
+
+ /**
+ * This method is used to get the marker for animations when system animations are disabled.
+ * It iterates over the list of allowed reduced motion markers and returns the first non-null marker it finds.
+ * If no non-null marker is found, it returns null.
+ *
+ * @return The first non-null marker from the list of allowed reduced motion markers, or null if no such marker is found.
+ */
+ private Marker getMarkerForAnimationsDisabled() {
+ Marker marker = null;
+ for (String markerName : ALLOWED_REDUCED_MOTION_MARKERS) {
+ marker = composition.getMarker(markerName);
+ if (marker != null) {
+ break;
+ }
+ }
+ return marker;
+ }
+
@MainThread
public void endAnimation() {
lazyCompositionTasks.clear();
@@ -980,7 +1225,12 @@ public class LottieDrawable extends Drawable implements Drawable.Callback, Anima
/**
* Tell Lottie that system animations are disabled. When using {@link LottieAnimationView} or Compose {@code LottieAnimation}, this is done
* automatically. However, if you are using LottieDrawable on its own, you should set this to false when
- * {@link com.airbnb.lottie.utils.Utils#getAnimationScale(Context)} is 0.
+ * {@link com.airbnb.lottie.utils.Utils#getAnimationScale(Context)} is 0. If the animation is provided a "reduced motion"
+ * marker name, they will be shown instead of the first or last frame. Supported marker names are case insensitive, and include:
+ * - reduced motion
+ * - reducedMotion
+ * - reduced_motion
+ * - reduced-motion
*/
public void setSystemAnimationsAreEnabled(Boolean areEnabled) {
systemAnimationsEnabled = areEnabled;
@@ -1000,6 +1250,19 @@ public class LottieDrawable extends Drawable implements Drawable.Callback, Anima
}
/**
+ * Lottie files can specify a target frame rate. By default, Lottie ignores it and re-renders
+ * on every frame. If that behavior is undesirable, you can set this to true to use the composition
+ * frame rate instead.
+ * <p>
+ * Note: composition frame rates are usually lower than display frame rates
+ * so this will likely make your animation feel janky. However, it may be desirable
+ * for specific situations such as pixel art that are intended to have low frame rates.
+ */
+ public void setUseCompositionFrameRate(boolean useCompositionFrameRate) {
+ animator.setUseCompositionFrameRate(useCompositionFrameRate);
+ }
+
+ /**
* Use this if you can't bundle images with your app. This may be useful if you download the
* animations from the network or have the images saved to an SD Card. In that case, Lottie
* will defer the loading of the bitmap to this delegate.
@@ -1007,7 +1270,7 @@ public class LottieDrawable extends Drawable implements Drawable.Callback, Anima
* Be wary if you are using many images, however. Lottie is designed to work with vector shapes
* from After Effects. If your images look like they could be represented with vector shapes,
* see if it is possible to convert them to shape layers and re-export your animation. Check
- * the documentation at http://airbnb.io/lottie for more information about importing shapes from
+ * the documentation at <a href="http://airbnb.io/lottie">http://airbnb.io/lottie</a> for more information about importing shapes from
* Sketch or Illustrator to avoid this.
*/
public void setImageAssetDelegate(ImageAssetDelegate assetDelegate) {
@@ -1027,6 +1290,25 @@ public class LottieDrawable extends Drawable implements Drawable.Callback, Anima
}
}
+ /**
+ * Set a map from font name keys to Typefaces.
+ * The keys can be in the form:
+ * * fontFamily
+ * * fontFamily-fontStyle
+ * * fontName
+ * All 3 are defined as fName, fFamily, and fStyle in the Lottie file.
+ * <p>
+ * If you change a value in fontMap, create a new map or call
+ * {@link #invalidateSelf()}. Setting the same map again will noop.
+ */
+ public void setFontMap(@Nullable Map<String, Typeface> fontMap) {
+ if (fontMap == this.fontMap) {
+ return;
+ }
+ this.fontMap = fontMap;
+ invalidateSelf();
+ }
+
public void setTextDelegate(@SuppressWarnings("NullableProblems") TextDelegate textDelegate) {
this.textDelegate = textDelegate;
}
@@ -1037,7 +1319,7 @@ public class LottieDrawable extends Drawable implements Drawable.Callback, Anima
}
public boolean useTextGlyphs() {
- return textDelegate == null && composition.getCharacters().size() > 0;
+ return fontMap == null && textDelegate == null && composition.getCharacters().size() > 0;
}
public LottieComposition getComposition() {
@@ -1099,6 +1381,8 @@ public class LottieDrawable extends Drawable implements Drawable.Callback, Anima
* <p>
* Internally, this will check if the {@link KeyPath} has already been resolved with
* {@link #resolveKeyPath(KeyPath)} and will resolve it if it hasn't.
+ * <p>
+ * Set the callback to null to clear it.
*/
public <T> void addValueCallback(
final KeyPath keyPath, final T property, @Nullable final LottieValueCallback<T> callback) {
@@ -1226,11 +1510,6 @@ public class LottieDrawable extends Drawable implements Drawable.Callback, Anima
}
private ImageAssetManager getImageAssetManager() {
- if (getCallback() == null) {
- // We can't get a bitmap since we can't get a Context from the callback.
- return null;
- }
-
if (imageAssetManager != null && !imageAssetManager.hasSameContext(getContext())) {
imageAssetManager = null;
}
@@ -1244,10 +1523,27 @@ public class LottieDrawable extends Drawable implements Drawable.Callback, Anima
}
@Nullable
- public Typeface getTypeface(String fontFamily, String style) {
+ @RestrictTo(RestrictTo.Scope.LIBRARY)
+ public Typeface getTypeface(Font font) {
+ Map<String, Typeface> fontMap = this.fontMap;
+ if (fontMap != null) {
+ String key = font.getFamily();
+ if (fontMap.containsKey(key)) {
+ return fontMap.get(key);
+ }
+ key = font.getName();
+ if (fontMap.containsKey(key)) {
+ return fontMap.get(key);
+ }
+ key = font.getFamily() + "-" + font.getStyle();
+ if (fontMap.containsKey(key)) {
+ return fontMap.get(key);
+ }
+ }
+
FontAssetManager assetManager = getFontAssetManager();
if (assetManager != null) {
- return assetManager.getTypeface(fontFamily, style);
+ return assetManager.getTypeface(font);
}
return null;
}
@@ -1260,11 +1556,34 @@ public class LottieDrawable extends Drawable implements Drawable.Callback, Anima
if (fontAssetManager == null) {
fontAssetManager = new FontAssetManager(getCallback(), fontAssetDelegate);
+ String defaultExtension = this.defaultFontFileExtension;
+ if (defaultExtension != null) {
+ fontAssetManager.setDefaultFontFileExtension(defaultFontFileExtension);
+ }
}
return fontAssetManager;
}
+ /**
+ * By default, Lottie will look in src/assets/fonts/FONT_NAME.ttf
+ * where FONT_NAME is the fFamily specified in your Lottie file.
+ * If your fonts have a different extension, you can override the
+ * default here.
+ * <p>
+ * Alternatively, you can use {@link #setFontAssetDelegate(FontAssetDelegate)}
+ * for more control.
+ *
+ * @see #setFontAssetDelegate(FontAssetDelegate)
+ */
+ public void setDefaultFontFileExtension(String extension) {
+ defaultFontFileExtension = extension;
+ FontAssetManager fam = getFontAssetManager();
+ if (fam != null) {
+ fam.setDefaultFontFileExtension(extension);
+ }
+ }
+
@Nullable
private Context getContext() {
Callback callback = getCallback();
@@ -1350,13 +1669,14 @@ public class LottieDrawable extends Drawable implements Drawable.Callback, Anima
float scaleY = bounds.height() / (float) composition.getBounds().height();
renderingMatrix.preScale(scaleX, scaleY);
+ renderingMatrix.preTranslate(bounds.left, bounds.top);
}
compositionLayer.draw(canvas, renderingMatrix, alpha);
}
/**
* Software accelerated render path.
- *
+ * <p>
* This draws the animation to an internally managed bitmap and then draws the bitmap to the original canvas.
*
* @see LottieAnimationView#setRenderMode(RenderMode)
@@ -1401,7 +1721,7 @@ public class LottieDrawable extends Drawable implements Drawable.Callback, Anima
int renderWidth = (int) Math.ceil(softwareRenderingTransformedBounds.width());
int renderHeight = (int) Math.ceil(softwareRenderingTransformedBounds.height());
- if (renderWidth == 0 || renderHeight == 0) {
+ if (renderWidth <= 0 || renderHeight <= 0) {
return;
}
diff --git a/lottie/src/main/java/com/airbnb/lottie/LottieImageAsset.java b/lottie/src/main/java/com/airbnb/lottie/LottieImageAsset.java
index 39606060..0184f416 100644
--- a/lottie/src/main/java/com/airbnb/lottie/LottieImageAsset.java
+++ b/lottie/src/main/java/com/airbnb/lottie/LottieImageAsset.java
@@ -71,6 +71,19 @@ public class LottieImageAsset {
}
/**
+ * Returns a new {@link LottieImageAsset} with the same properties as this one but with the
+ * dimensions and bitmap scaled.
+ */
+ public LottieImageAsset copyWithScale(float scale) {
+ LottieImageAsset newAsset = new LottieImageAsset((int) (width * scale), (int) (height * scale), id, fileName, dirName);
+ if (bitmap != null) {
+ Bitmap scaledBitmap = Bitmap.createScaledBitmap(bitmap, newAsset.width, newAsset.height, true);
+ newAsset.setBitmap(scaledBitmap);
+ }
+ return newAsset;
+ }
+
+ /**
* Returns whether this asset has an embedded Bitmap or whether the fileName is a base64 encoded bitmap.
*/
public boolean hasBitmap() {
diff --git a/lottie/src/main/java/com/airbnb/lottie/LottieProperty.java b/lottie/src/main/java/com/airbnb/lottie/LottieProperty.java
index 6ab6257c..5f75e9e8 100644
--- a/lottie/src/main/java/com/airbnb/lottie/LottieProperty.java
+++ b/lottie/src/main/java/com/airbnb/lottie/LottieProperty.java
@@ -2,6 +2,7 @@ package com.airbnb.lottie;
import android.graphics.Bitmap;
import android.graphics.ColorFilter;
+import android.graphics.Path;
import android.graphics.PointF;
import android.graphics.Typeface;
@@ -220,4 +221,15 @@ public interface LottieProperty {
* Replace the text for a text layer.
*/
CharSequence TEXT = "dynamic_text";
+
+ /**
+ * Replace a path. This can only be used on path contents. For other shapes such as rectangles and polystars,
+ * use LottieProperties corresponding to their specific properties.
+ * <p>
+ * If you need to do any operations on the path such as morphing, use the Jetpack androidx.graphics.path library.
+ * <p>
+ * In After Effects, any of those other shapes can be converted to a bezier path by right clicking it and
+ * selecting "Convert To Bezier Path".
+ */
+ Path PATH = new Path();
}
diff --git a/lottie/src/main/java/com/airbnb/lottie/LottieTask.java b/lottie/src/main/java/com/airbnb/lottie/LottieTask.java
index 6d1ae568..8a62b3b5 100644
--- a/lottie/src/main/java/com/airbnb/lottie/LottieTask.java
+++ b/lottie/src/main/java/com/airbnb/lottie/LottieTask.java
@@ -7,6 +7,7 @@ import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
import com.airbnb.lottie.utils.Logger;
+import com.airbnb.lottie.utils.LottieThreadFactory;
import java.util.ArrayList;
import java.util.LinkedHashSet;
@@ -25,7 +26,8 @@ import java.util.concurrent.FutureTask;
* <p>
* A task will produce a single result or a single failure.
*/
-@SuppressWarnings("UnusedReturnValue") public class LottieTask<T> {
+@SuppressWarnings("UnusedReturnValue")
+public class LottieTask<T> {
/**
* Set this to change the executor that LottieTasks are run on. This will be the executor that composition parsing and url
@@ -34,7 +36,7 @@ import java.util.concurrent.FutureTask;
* You may change this to run deserialization synchronously for testing.
*/
@SuppressWarnings("WeakerAccess")
- public static Executor EXECUTOR = Executors.newCachedThreadPool();
+ public static Executor EXECUTOR = Executors.newCachedThreadPool(new LottieThreadFactory());
/* Preserve add order. */
private final Set<LottieListener<T>> successListeners = new LinkedHashSet<>(1);
@@ -48,6 +50,10 @@ import java.util.concurrent.FutureTask;
this(runnable, false);
}
+ public LottieTask(T result) {
+ setResult(new LottieResult<>(result));
+ }
+
/**
* runNow is only used for testing.
*/
@@ -59,7 +65,7 @@ import java.util.concurrent.FutureTask;
setResult(new LottieResult<>(e));
}
} else {
- EXECUTOR.execute(new LottieFutureTask(runnable));
+ EXECUTOR.execute(new LottieFutureTask<T>(this, runnable));
}
}
@@ -124,20 +130,31 @@ import java.util.concurrent.FutureTask;
return this;
}
+ @Nullable
+ public LottieResult<T> getResult() {
+ return result;
+ }
+
private void notifyListeners() {
// Listeners should be called on the main thread.
- handler.post(() -> {
- // Local reference in case it gets set on a background thread.
- LottieResult<T> result = LottieTask.this.result;
- if (result == null) {
- return;
- }
- if (result.getValue() != null) {
- notifySuccessListeners(result.getValue());
- } else {
- notifyFailureListeners(result.getException());
- }
- });
+ if (Looper.myLooper() == Looper.getMainLooper()) {
+ notifyListenersInternal();
+ } else {
+ handler.post(this::notifyListenersInternal);
+ }
+ }
+
+ private void notifyListenersInternal() {
+ // Local reference in case it gets set on a background thread.
+ LottieResult<T> result = LottieTask.this.result;
+ if (result == null) {
+ return;
+ }
+ if (result.getValue() != null) {
+ notifySuccessListeners(result.getValue());
+ } else {
+ notifyFailureListeners(result.getException());
+ }
}
private synchronized void notifySuccessListeners(T value) {
@@ -163,22 +180,45 @@ import java.util.concurrent.FutureTask;
}
}
- private class LottieFutureTask extends FutureTask<LottieResult<T>> {
- LottieFutureTask(Callable<LottieResult<T>> callable) {
+ private static class LottieFutureTask<T> extends FutureTask<LottieResult<T>> {
+
+ private LottieTask<T> lottieTask;
+
+ LottieFutureTask(LottieTask<T> task, Callable<LottieResult<T>> callable) {
super(callable);
+ lottieTask = task;
}
@Override
protected void done() {
- if (isCancelled()) {
- // We don't need to notify and listeners if the task is cancelled.
- return;
- }
-
try {
- setResult(get());
- } catch (InterruptedException | ExecutionException e) {
- setResult(new LottieResult<>(e));
+ if (isCancelled()) {
+ // We don't need to notify and listeners if the task is cancelled.
+ return;
+ }
+
+ try {
+ lottieTask.setResult(get());
+ } catch (InterruptedException | ExecutionException e) {
+ lottieTask.setResult(new LottieResult<>(e));
+ }
+ } finally {
+ // LottieFutureTask can be held in memory for up to 60 seconds after the task is done, which would
+ // result in holding on to the associated LottieTask instance and leaking its listeners. To avoid
+ // that, we clear our the reference to the LottieTask instance.
+ //
+ // How is LottieFutureTask held for up to 60 seconds? It's a bug in how the VM cleans up stack
+ // local variables. When you have a loop that polls a blocking queue and assigns the result
+ // to a local variable, after looping the local variable will still reference the previous value
+ // until the queue returns the next result.
+ //
+ // Executors.newCachedThreadPool() relies on a SynchronousQueue and creates a cached thread pool
+ // with a default keep alice of 60 seconds. After a given worker thread runs a task, that thread
+ // will wait for up to 60 seconds for a new task to come, and while waiting it's also accidentally
+ // keeping a reference to the previous task.
+ //
+ // See commit d577e728e9bccbafc707af3060ea914caa73c14f in AOSP for how that was fixed for Looper.
+ lottieTask = null;
}
}
}
diff --git a/lottie/src/main/java/com/airbnb/lottie/LottieTaskIdleListener.java b/lottie/src/main/java/com/airbnb/lottie/LottieTaskIdleListener.java
new file mode 100644
index 00000000..b57e44e9
--- /dev/null
+++ b/lottie/src/main/java/com/airbnb/lottie/LottieTaskIdleListener.java
@@ -0,0 +1,11 @@
+package com.airbnb.lottie;
+
+/**
+ * Register this listener via {@link LottieCompositionFactory#registerLottieTaskIdleListener(LottieTaskIdleListener)}.
+ *
+ * Can be used to create an espresso idle resource. Refer to {@link LottieCompositionFactory#registerLottieTaskIdleListener(LottieTaskIdleListener)}
+ * for more information.
+ */
+public interface LottieTaskIdleListener {
+ void onIdleChanged(boolean idle);
+}
diff --git a/lottie/src/main/java/com/airbnb/lottie/animation/content/ContentGroup.java b/lottie/src/main/java/com/airbnb/lottie/animation/content/ContentGroup.java
index 1db7f55d..cc3d1661 100644
--- a/lottie/src/main/java/com/airbnb/lottie/animation/content/ContentGroup.java
+++ b/lottie/src/main/java/com/airbnb/lottie/animation/content/ContentGroup.java
@@ -8,6 +8,7 @@ import android.graphics.RectF;
import androidx.annotation.Nullable;
+import com.airbnb.lottie.LottieComposition;
import com.airbnb.lottie.LottieDrawable;
import com.airbnb.lottie.animation.LPaint;
import com.airbnb.lottie.animation.keyframe.BaseKeyframeAnimation;
@@ -30,11 +31,11 @@ public class ContentGroup implements DrawingContent, PathContent,
private final Paint offScreenPaint = new LPaint();
private final RectF offScreenRectF = new RectF();
- private static List<Content> contentsFromModels(LottieDrawable drawable, BaseLayer layer,
+ private static List<Content> contentsFromModels(LottieDrawable drawable, LottieComposition composition, BaseLayer layer,
List<ContentModel> contentModels) {
List<Content> contents = new ArrayList<>(contentModels.size());
for (int i = 0; i < contentModels.size(); i++) {
- Content content = contentModels.get(i).toContent(drawable, layer);
+ Content content = contentModels.get(i).toContent(drawable, composition, layer);
if (content != null) {
contents.add(content);
}
@@ -63,9 +64,9 @@ public class ContentGroup implements DrawingContent, PathContent,
@Nullable private List<PathContent> pathContents;
@Nullable private TransformKeyframeAnimation transformAnimation;
- public ContentGroup(final LottieDrawable lottieDrawable, BaseLayer layer, ShapeGroup shapeGroup) {
+ public ContentGroup(final LottieDrawable lottieDrawable, BaseLayer layer, ShapeGroup shapeGroup, LottieComposition composition) {
this(lottieDrawable, layer, shapeGroup.getName(),
- shapeGroup.isHidden(), contentsFromModels(lottieDrawable, layer, shapeGroup.getItems()),
+ shapeGroup.isHidden(), contentsFromModels(lottieDrawable, composition, layer, shapeGroup.getItems()),
findTransform(shapeGroup.getItems()));
}
@@ -115,6 +116,10 @@ public class ContentGroup implements DrawingContent, PathContent,
}
}
+ public List<Content> getContents() {
+ return contents;
+ }
+
List<PathContent> getPathList() {
if (pathContents == null) {
pathContents = new ArrayList<>();
diff --git a/lottie/src/main/java/com/airbnb/lottie/animation/content/FillContent.java b/lottie/src/main/java/com/airbnb/lottie/animation/content/FillContent.java
index 46732a81..0e89c78e 100644
--- a/lottie/src/main/java/com/airbnb/lottie/animation/content/FillContent.java
+++ b/lottie/src/main/java/com/airbnb/lottie/animation/content/FillContent.java
@@ -11,6 +11,7 @@ import android.graphics.Path;
import android.graphics.RectF;
import androidx.annotation.Nullable;
+import androidx.core.graphics.PaintCompat;
import com.airbnb.lottie.L;
import com.airbnb.lottie.LottieDrawable;
@@ -31,6 +32,7 @@ import java.util.List;
public class FillContent
implements DrawingContent, BaseKeyframeAnimation.AnimationListener, KeyPathElementContent {
+
private final Path path = new Path();
private final Paint paint = new LPaint(Paint.ANTI_ALIAS_FLAG);
private final BaseLayer layer;
@@ -66,6 +68,8 @@ public class FillContent
return;
}
+ PaintCompat.setBlendMode(paint, layer.getBlendMode().toNativeBlendMode());
+
path.setFillType(fill.getFillType());
colorAnimation = fill.getColor().createAnimation();
diff --git a/lottie/src/main/java/com/airbnb/lottie/animation/content/GradientFillContent.java b/lottie/src/main/java/com/airbnb/lottie/animation/content/GradientFillContent.java
index 272a3a02..16def664 100644
--- a/lottie/src/main/java/com/airbnb/lottie/animation/content/GradientFillContent.java
+++ b/lottie/src/main/java/com/airbnb/lottie/animation/content/GradientFillContent.java
@@ -19,6 +19,7 @@ import androidx.annotation.Nullable;
import androidx.collection.LongSparseArray;
import com.airbnb.lottie.L;
+import com.airbnb.lottie.LottieComposition;
import com.airbnb.lottie.LottieDrawable;
import com.airbnb.lottie.LottieProperty;
import com.airbnb.lottie.animation.LPaint;
@@ -64,14 +65,14 @@ public class GradientFillContent
float blurMaskFilterRadius = 0f;
@Nullable private DropShadowKeyframeAnimation dropShadowAnimation;
- public GradientFillContent(final LottieDrawable lottieDrawable, BaseLayer layer, GradientFill fill) {
+ public GradientFillContent(final LottieDrawable lottieDrawable, LottieComposition composition, BaseLayer layer, GradientFill fill) {
this.layer = layer;
name = fill.getName();
hidden = fill.isHidden();
this.lottieDrawable = lottieDrawable;
type = fill.getGradientType();
path.setFillType(fill.getFillType());
- cacheSteps = (int) (lottieDrawable.getComposition().getDuration() / CACHE_STEPS_MS);
+ cacheSteps = (int) (composition.getDuration() / CACHE_STEPS_MS);
colorAnimation = fill.getGradientColor().createAnimation();
colorAnimation.addUpdateListener(this);
diff --git a/lottie/src/main/java/com/airbnb/lottie/animation/content/PolystarContent.java b/lottie/src/main/java/com/airbnb/lottie/animation/content/PolystarContent.java
index cfa95236..9c5fc83c 100644
--- a/lottie/src/main/java/com/airbnb/lottie/animation/content/PolystarContent.java
+++ b/lottie/src/main/java/com/airbnb/lottie/animation/content/PolystarContent.java
@@ -1,10 +1,9 @@
package com.airbnb.lottie.animation.content;
import android.graphics.Path;
+import android.graphics.PathMeasure;
import android.graphics.PointF;
-
import androidx.annotation.Nullable;
-
import com.airbnb.lottie.LottieDrawable;
import com.airbnb.lottie.LottieProperty;
import com.airbnb.lottie.animation.keyframe.BaseKeyframeAnimation;
@@ -28,6 +27,9 @@ public class PolystarContent
private static final float POLYSTAR_MAGIC_NUMBER = .47829f;
private static final float POLYGON_MAGIC_NUMBER = .25f;
private final Path path = new Path();
+ private final Path lastSegmentPath = new Path();
+ private final PathMeasure lastSegmentPathMeasure = new PathMeasure();
+ private final float[] lastSegmentPosition = new float[2];
private final String name;
private final LottieDrawable lottieDrawable;
@@ -291,8 +293,28 @@ public class PolystarContent
float cp1y = radius * roundedness * POLYGON_MAGIC_NUMBER * cp1Dy;
float cp2x = radius * roundedness * POLYGON_MAGIC_NUMBER * cp2Dx;
float cp2y = radius * roundedness * POLYGON_MAGIC_NUMBER * cp2Dy;
- path.cubicTo(previousX - cp1x, previousY - cp1y, x + cp2x, y + cp2y, x, y);
+
+ if (i == numPoints - 1) {
+ // When there is a huge stroke, it will flash if the path ends where it starts.
+ // We want the final bezier curve to end *slightly* before the start.
+ // The close() call at the end will complete the polystar.
+ // https://github.com/airbnb/lottie-android/issues/2329
+ lastSegmentPath.reset();
+ lastSegmentPath.moveTo(previousX, previousY);
+ lastSegmentPath.cubicTo(previousX - cp1x, previousY - cp1y, x + cp2x, y + cp2y, x, y);
+ lastSegmentPathMeasure.setPath(lastSegmentPath, false);
+ lastSegmentPathMeasure.getPosTan(lastSegmentPathMeasure.getLength() * 0.9999f, lastSegmentPosition, null);
+ path.cubicTo(previousX - cp1x, previousY - cp1y, x + cp2x, y + cp2y,lastSegmentPosition[0], lastSegmentPosition[1]);
+ } else {
+ path.cubicTo(previousX - cp1x, previousY - cp1y, x + cp2x, y + cp2y, x, y);
+ }
} else {
+ if (i == numPoints - 1) {
+ // When there is a huge stroke, it will flash if the path ends where it starts.
+ // The close() call should make the path effectively equivalent.
+ // https://github.com/airbnb/lottie-android/issues/2329
+ continue;
+ }
path.lineTo(x, y);
}
diff --git a/lottie/src/main/java/com/airbnb/lottie/animation/content/RepeaterContent.java b/lottie/src/main/java/com/airbnb/lottie/animation/content/RepeaterContent.java
index 8b6d1226..4a721fb5 100644
--- a/lottie/src/main/java/com/airbnb/lottie/animation/content/RepeaterContent.java
+++ b/lottie/src/main/java/com/airbnb/lottie/animation/content/RepeaterContent.java
@@ -131,6 +131,12 @@ public class RepeaterContent implements DrawingContent, PathContent, GreedyConte
@Override public void resolveKeyPath(
KeyPath keyPath, int depth, List<KeyPath> accumulator, KeyPath currentPartialKeyPath) {
MiscUtils.resolveKeyPath(keyPath, depth, accumulator, currentPartialKeyPath, this);
+ for (int i = 0; i < contentGroup.getContents().size(); i++) {
+ Content content = contentGroup.getContents().get(i);
+ if (content instanceof KeyPathElementContent) {
+ MiscUtils.resolveKeyPath(keyPath, depth, accumulator, currentPartialKeyPath, (KeyPathElementContent) content);
+ }
+ }
}
@SuppressWarnings("unchecked")
diff --git a/lottie/src/main/java/com/airbnb/lottie/animation/content/RoundedCornersContent.java b/lottie/src/main/java/com/airbnb/lottie/animation/content/RoundedCornersContent.java
index 06a3bb15..d4453be0 100644
--- a/lottie/src/main/java/com/airbnb/lottie/animation/content/RoundedCornersContent.java
+++ b/lottie/src/main/java/com/airbnb/lottie/animation/content/RoundedCornersContent.java
@@ -112,7 +112,7 @@ public class RoundedCornersContent implements ShapeModifierContent, BaseKeyframe
PointF nextVertex = startingCurve.getVertex();
// We can't round the corner of the end of a non-closed curve.
- boolean isEndOfCurve = !startingShapeData.isClosed() && (i == 0 && i == startingCurves.size() - 1);
+ boolean isEndOfCurve = !startingShapeData.isClosed() && (i == 0 || i == startingCurves.size() - 1);
if (inPoint.equals(vertex) && outPoint.equals(vertex) && !isEndOfCurve) {
// This vertex is a point. Round its corners
float dxToPreviousVertex = vertex.x - previousVertex.x;
@@ -161,9 +161,9 @@ public class RoundedCornersContent implements ShapeModifierContent, BaseKeyframe
// oriented point to CubicCurveData (path segments).
CubicCurveData previousCurveData = modifiedCurves.get(floorMod(modifiedCurvesIndex - 1, modifiedCurves.size()));
CubicCurveData currentCurveData = modifiedCurves.get(modifiedCurvesIndex);
- previousCurveData.setControlPoint2(previousCurve.getVertex().x, previousCurve.getVertex().y);
+ previousCurveData.setControlPoint2(previousCurve.getControlPoint2().x, previousCurve.getControlPoint2().y);
previousCurveData.setVertex(previousCurve.getVertex().x, previousCurve.getVertex().y);
- currentCurveData.setControlPoint1(startingCurve.getVertex().x, startingCurve.getVertex().y);
+ currentCurveData.setControlPoint1(startingCurve.getControlPoint1().x, startingCurve.getControlPoint1().y);
modifiedCurvesIndex++;
}
}
@@ -186,7 +186,7 @@ public class RoundedCornersContent implements ShapeModifierContent, BaseKeyframe
PointF inPoint = (i == 0 && !isClosed) ? vertex : previousCurve.getControlPoint2();
PointF outPoint = startingCurve.getControlPoint1();
- boolean isEndOfCurve = !startingShapeData.isClosed() && (i == 0 && i == startingCurves.size() - 1);
+ boolean isEndOfCurve = !startingShapeData.isClosed() && (i == 0 || i == startingCurves.size() - 1);
if (inPoint.equals(vertex) && outPoint.equals(vertex) && !isEndOfCurve) {
vertices += 2;
} else {
diff --git a/lottie/src/main/java/com/airbnb/lottie/animation/content/ShapeContent.java b/lottie/src/main/java/com/airbnb/lottie/animation/content/ShapeContent.java
index 1f54ac09..03869458 100644
--- a/lottie/src/main/java/com/airbnb/lottie/animation/content/ShapeContent.java
+++ b/lottie/src/main/java/com/airbnb/lottie/animation/content/ShapeContent.java
@@ -5,16 +5,20 @@ import android.graphics.Path;
import androidx.annotation.Nullable;
import com.airbnb.lottie.LottieDrawable;
+import com.airbnb.lottie.LottieProperty;
import com.airbnb.lottie.animation.keyframe.BaseKeyframeAnimation;
import com.airbnb.lottie.animation.keyframe.ShapeKeyframeAnimation;
+import com.airbnb.lottie.model.KeyPath;
import com.airbnb.lottie.model.content.ShapePath;
import com.airbnb.lottie.model.content.ShapeTrimPath;
import com.airbnb.lottie.model.layer.BaseLayer;
+import com.airbnb.lottie.utils.MiscUtils;
+import com.airbnb.lottie.value.LottieValueCallback;
import java.util.ArrayList;
import java.util.List;
-public class ShapeContent implements PathContent, BaseKeyframeAnimation.AnimationListener {
+public class ShapeContent implements PathContent, BaseKeyframeAnimation.AnimationListener, KeyPathElementContent {
private final Path path = new Path();
private final String name;
@@ -65,7 +69,7 @@ public class ShapeContent implements PathContent, BaseKeyframeAnimation.Animatio
}
@Override public Path getPath() {
- if (isPathValid) {
+ if (isPathValid && !shapeAnimation.hasValueCallback()) {
return path;
}
@@ -94,4 +98,17 @@ public class ShapeContent implements PathContent, BaseKeyframeAnimation.Animatio
@Override public String getName() {
return name;
}
+
+ @Override public void resolveKeyPath(
+ KeyPath keyPath, int depth, List<KeyPath> accumulator, KeyPath currentPartialKeyPath) {
+ MiscUtils.resolveKeyPath(keyPath, depth, accumulator, currentPartialKeyPath, this);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public <T> void addValueCallback(T property, @Nullable LottieValueCallback<T> callback) {
+ if (property == LottieProperty.PATH) {
+ shapeAnimation.setValueCallback((LottieValueCallback<Path>) callback);
+ }
+ }
}
diff --git a/lottie/src/main/java/com/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation.java b/lottie/src/main/java/com/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation.java
index 8f13143e..1f8ec208 100644
--- a/lottie/src/main/java/com/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation.java
+++ b/lottie/src/main/java/com/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation.java
@@ -1,5 +1,6 @@
package com.airbnb.lottie.animation.keyframe;
+import android.annotation.SuppressLint;
import androidx.annotation.FloatRange;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -16,7 +17,9 @@ import java.util.List;
* @param <A> Animation type
*/
public abstract class BaseKeyframeAnimation<K, A> {
+
public interface AnimationListener {
+
void onValueChanged();
}
@@ -46,7 +49,9 @@ public abstract class BaseKeyframeAnimation<K, A> {
}
public void setProgress(@FloatRange(from = 0f, to = 1f) float progress) {
+ L.beginSection("BaseKeyframeAnimation#setProgress");
if (keyframesWrapper.isEmpty()) {
+ L.endSection("BaseKeyframeAnimation#setProgress");
return;
}
if (progress < getStartDelayProgress()) {
@@ -56,18 +61,22 @@ public abstract class BaseKeyframeAnimation<K, A> {
}
if (progress == this.progress) {
+ L.endSection("BaseKeyframeAnimation#setProgress");
return;
}
this.progress = progress;
if (keyframesWrapper.isValueChanged(progress)) {
notifyListeners();
}
+ L.endSection("BaseKeyframeAnimation#setProgress");
}
public void notifyListeners() {
+ L.beginSection("BaseKeyframeAnimation#notifyListeners");
for (int i = 0; i < listeners.size(); i++) {
listeners.get(i).onValueChanged();
}
+ L.endSection("BaseKeyframeAnimation#notifyListeners");
}
protected Keyframe<K> getCurrentKeyframe() {
@@ -110,6 +119,7 @@ public abstract class BaseKeyframeAnimation<K, A> {
return keyframe.interpolator.getInterpolation(getLinearCurrentKeyframeProgress());
}
+ @SuppressLint("Range")
@FloatRange(from = 0f, to = 1f)
private float getStartDelayProgress() {
if (cachedStartDelayProgress == -1f) {
@@ -118,6 +128,7 @@ public abstract class BaseKeyframeAnimation<K, A> {
return cachedStartDelayProgress;
}
+ @SuppressLint("Range")
@FloatRange(from = 0f, to = 1f)
float getEndProgress() {
if (cachedEndProgress == -1f) {
@@ -162,6 +173,10 @@ public abstract class BaseKeyframeAnimation<K, A> {
}
}
+ public boolean hasValueCallback() {
+ return valueCallback != null;
+ }
+
/**
* keyframeProgress will be [0, 1] unless the interpolator has overshoot in which case, this
* should be able to handle values outside of that range.
@@ -186,6 +201,7 @@ public abstract class BaseKeyframeAnimation<K, A> {
}
private interface KeyframesWrapper<T> {
+
boolean isEmpty();
boolean isValueChanged(float progress);
@@ -202,6 +218,7 @@ public abstract class BaseKeyframeAnimation<K, A> {
}
private static final class EmptyKeyframeWrapper<T> implements KeyframesWrapper<T> {
+
@Override
public boolean isEmpty() {
return true;
@@ -234,6 +251,7 @@ public abstract class BaseKeyframeAnimation<K, A> {
}
private static final class SingleKeyframeWrapper<T> implements KeyframesWrapper<T> {
+
@NonNull
private final Keyframe<T> keyframe;
private float cachedInterpolatedProgress = -1f;
@@ -278,6 +296,7 @@ public abstract class BaseKeyframeAnimation<K, A> {
}
private static final class KeyframesWrapperImpl<T> implements KeyframesWrapper<T> {
+
private final List<? extends Keyframe<T>> keyframes;
@NonNull
private Keyframe<T> currentKeyframe;
diff --git a/lottie/src/main/java/com/airbnb/lottie/animation/keyframe/ColorKeyframeAnimation.java b/lottie/src/main/java/com/airbnb/lottie/animation/keyframe/ColorKeyframeAnimation.java
index 3c83b268..93bf9448 100644
--- a/lottie/src/main/java/com/airbnb/lottie/animation/keyframe/ColorKeyframeAnimation.java
+++ b/lottie/src/main/java/com/airbnb/lottie/animation/keyframe/ColorKeyframeAnimation.java
@@ -25,7 +25,10 @@ public class ColorKeyframeAnimation extends KeyframeAnimation<Integer> {
throw new IllegalStateException("Missing values for keyframe.");
}
- if (valueCallback != null) {
+ // keyframe.endFrame should not be null under normal operation.
+ // It is not clear why this would be null and when it does, it seems to be extremely rare.
+ // https://github.com/airbnb/lottie-android/issues/2361
+ if (valueCallback != null && keyframe.endFrame != null) {
//noinspection ConstantConditions
Integer value = valueCallback.getValueInternal(keyframe.startFrame, keyframe.endFrame, keyframe.startValue,
keyframe.endValue, keyframeProgress, getLinearCurrentKeyframeProgress(), getProgress());
diff --git a/lottie/src/main/java/com/airbnb/lottie/animation/keyframe/GradientColorKeyframeAnimation.java b/lottie/src/main/java/com/airbnb/lottie/animation/keyframe/GradientColorKeyframeAnimation.java
index c4c1a51d..2ce54d8d 100644
--- a/lottie/src/main/java/com/airbnb/lottie/animation/keyframe/GradientColorKeyframeAnimation.java
+++ b/lottie/src/main/java/com/airbnb/lottie/animation/keyframe/GradientColorKeyframeAnimation.java
@@ -10,12 +10,22 @@ public class GradientColorKeyframeAnimation extends KeyframeAnimation<GradientCo
public GradientColorKeyframeAnimation(List<Keyframe<GradientColor>> keyframes) {
super(keyframes);
- GradientColor startValue = keyframes.get(0).startValue;
- int size = startValue == null ? 0 : startValue.getSize();
+ // Not all keyframes that this GradientColor are used for will have the same length.
+ // AnimatableGradientColorValue.ensureInterpolatableKeyframes may add extra positions
+ // for some keyframes but not others to ensure that it is interpolatable.
+ // Ensure that there is enough space for the largest keyframe.
+ int size = 0;
+ for (int i = 0; i < keyframes.size(); i++) {
+ GradientColor startValue = keyframes.get(i).startValue;
+ if (startValue != null) {
+ size = Math.max(size, startValue.getSize());
+ }
+ }
gradientColor = new GradientColor(new float[size], new int[size]);
}
@Override GradientColor getValue(Keyframe<GradientColor> keyframe, float keyframeProgress) {
+ //noinspection DataFlowIssue
gradientColor.lerp(keyframe.startValue, keyframe.endValue, keyframeProgress);
return gradientColor;
}
diff --git a/lottie/src/main/java/com/airbnb/lottie/animation/keyframe/PathKeyframeAnimation.java b/lottie/src/main/java/com/airbnb/lottie/animation/keyframe/PathKeyframeAnimation.java
index b4f16636..7418405d 100644
--- a/lottie/src/main/java/com/airbnb/lottie/animation/keyframe/PathKeyframeAnimation.java
+++ b/lottie/src/main/java/com/airbnb/lottie/animation/keyframe/PathKeyframeAnimation.java
@@ -11,6 +11,7 @@ import java.util.List;
public class PathKeyframeAnimation extends KeyframeAnimation<PointF> {
private final PointF point = new PointF();
private final float[] pos = new float[2];
+ private final float[] tangent = new float[2];
private final PathMeasure pathMeasure = new PathMeasure();
private PathKeyframe pathMeasureKeyframe;
@@ -39,8 +40,20 @@ public class PathKeyframeAnimation extends KeyframeAnimation<PointF> {
pathMeasureKeyframe = pathKeyframe;
}
- pathMeasure.getPosTan(keyframeProgress * pathMeasure.getLength(), pos, null);
+ // allow bounce easings to calculate positions outside the path
+ // by using the tangent at the extremities
+
+ float length = pathMeasure.getLength();
+
+ float distance = keyframeProgress * length;
+ pathMeasure.getPosTan(distance, pos, tangent);
point.set(pos[0], pos[1]);
+
+ if (distance < 0) {
+ point.offset(tangent[0] * distance, tangent[1] * distance);
+ } else if (distance > length) {
+ point.offset(tangent[0] * (distance - length), tangent[1] * (distance - length));
+ }
return point;
}
}
diff --git a/lottie/src/main/java/com/airbnb/lottie/animation/keyframe/ShapeKeyframeAnimation.java b/lottie/src/main/java/com/airbnb/lottie/animation/keyframe/ShapeKeyframeAnimation.java
index 0abd3029..43cc1857 100644
--- a/lottie/src/main/java/com/airbnb/lottie/animation/keyframe/ShapeKeyframeAnimation.java
+++ b/lottie/src/main/java/com/airbnb/lottie/animation/keyframe/ShapeKeyframeAnimation.java
@@ -14,6 +14,8 @@ import java.util.List;
public class ShapeKeyframeAnimation extends BaseKeyframeAnimation<ShapeData, Path> {
private final ShapeData tempShapeData = new ShapeData();
private final Path tempPath = new Path();
+ private Path valueCallbackStartPath;
+ private Path valueCallbackEndPath;
private List<ShapeModifierContent> shapeModifiers;
@@ -25,7 +27,7 @@ public class ShapeKeyframeAnimation extends BaseKeyframeAnimation<ShapeData, Pat
ShapeData startShapeData = keyframe.startValue;
ShapeData endShapeData = keyframe.endValue;
- tempShapeData.interpolateBetween(startShapeData, endShapeData, keyframeProgress);
+ tempShapeData.interpolateBetween(startShapeData, endShapeData == null ? startShapeData : endShapeData, keyframeProgress);
ShapeData modifiedShapeData = tempShapeData;
if (shapeModifiers != null) {
for (int i = shapeModifiers.size() - 1; i >= 0; i--) {
@@ -33,6 +35,20 @@ public class ShapeKeyframeAnimation extends BaseKeyframeAnimation<ShapeData, Pat
}
}
MiscUtils.getPathFromData(modifiedShapeData, tempPath);
+ if (valueCallback != null) {
+ if (valueCallbackStartPath == null) {
+ valueCallbackStartPath = new Path();
+ valueCallbackEndPath = new Path();
+ }
+ MiscUtils.getPathFromData(startShapeData, valueCallbackStartPath);
+ if (endShapeData != null) {
+ MiscUtils.getPathFromData(endShapeData, valueCallbackEndPath);
+ }
+
+ return valueCallback.getValueInternal(keyframe.startFrame, keyframe.endFrame,
+ valueCallbackStartPath, endShapeData == null ? valueCallbackStartPath : valueCallbackEndPath,
+ keyframeProgress, getLinearCurrentKeyframeProgress(), getProgress());
+ }
return tempPath;
}
diff --git a/lottie/src/main/java/com/airbnb/lottie/animation/keyframe/TextKeyframeAnimation.java b/lottie/src/main/java/com/airbnb/lottie/animation/keyframe/TextKeyframeAnimation.java
index 4ce09ce0..e271585c 100644
--- a/lottie/src/main/java/com/airbnb/lottie/animation/keyframe/TextKeyframeAnimation.java
+++ b/lottie/src/main/java/com/airbnb/lottie/animation/keyframe/TextKeyframeAnimation.java
@@ -1,7 +1,5 @@
package com.airbnb.lottie.animation.keyframe;
-import androidx.annotation.Nullable;
-
import com.airbnb.lottie.model.DocumentData;
import com.airbnb.lottie.value.Keyframe;
import com.airbnb.lottie.value.LottieFrameInfo;
@@ -39,7 +37,7 @@ public class TextKeyframeAnimation extends KeyframeAnimation<DocumentData> {
DocumentData baseDocumentData = frameInfo.getInterpolatedKeyframeProgress() == 1f ? frameInfo.getEndValue() : frameInfo.getStartValue();
documentData.set(text, baseDocumentData.fontName, baseDocumentData.size, baseDocumentData.justification, baseDocumentData.tracking,
baseDocumentData.lineHeight, baseDocumentData.baselineShift, baseDocumentData.color, baseDocumentData.strokeColor,
- baseDocumentData.strokeWidth, baseDocumentData.strokeOverFill);
+ baseDocumentData.strokeWidth, baseDocumentData.strokeOverFill, baseDocumentData.boxPosition, baseDocumentData.boxSize);
return documentData;
}
});
diff --git a/lottie/src/main/java/com/airbnb/lottie/animation/keyframe/TransformKeyframeAnimation.java b/lottie/src/main/java/com/airbnb/lottie/animation/keyframe/TransformKeyframeAnimation.java
index fcab96c1..13a63ee6 100644
--- a/lottie/src/main/java/com/airbnb/lottie/animation/keyframe/TransformKeyframeAnimation.java
+++ b/lottie/src/main/java/com/airbnb/lottie/animation/keyframe/TransformKeyframeAnimation.java
@@ -44,12 +44,16 @@ public class TransformKeyframeAnimation {
@Nullable private BaseKeyframeAnimation<?, Float> startOpacity;
@Nullable private BaseKeyframeAnimation<?, Float> endOpacity;
+ private final boolean autoOrient;
+
+
public TransformKeyframeAnimation(AnimatableTransform animatableTransform) {
anchorPoint = animatableTransform.getAnchorPoint() == null ? null : animatableTransform.getAnchorPoint().createAnimation();
position = animatableTransform.getPosition() == null ? null : animatableTransform.getPosition().createAnimation();
scale = animatableTransform.getScale() == null ? null : animatableTransform.getScale().createAnimation();
rotation = animatableTransform.getRotation() == null ? null : animatableTransform.getRotation().createAnimation();
skew = animatableTransform.getSkew() == null ? null : (FloatKeyframeAnimation) animatableTransform.getSkew().createAnimation();
+ autoOrient = animatableTransform.isAutoOrient();
if (skew != null) {
skewMatrix1 = new Matrix();
skewMatrix2 = new Matrix();
@@ -174,16 +178,36 @@ public class TransformKeyframeAnimation {
}
}
- BaseKeyframeAnimation<Float, Float> rotation = this.rotation;
- if (rotation != null) {
- float rotationValue;
- if (rotation instanceof ValueCallbackKeyframeAnimation) {
- rotationValue = rotation.getValue();
- } else {
- rotationValue = ((FloatKeyframeAnimation) rotation).getFloatValue();
+ // If autoOrient is true, the rotation should follow the derivative of the position rather
+ // than the rotation property.
+ if (autoOrient) {
+ if (position != null) {
+ float currentProgress = position.getProgress();
+ PointF startPosition = position.getValue();
+ // Store the start X and Y values because the pointF will be overwritten by the next getValue call.
+ float startX = startPosition.x;
+ float startY = startPosition.y;
+ // 1) Find the next position value.
+ // 2) Create a vector from the current position to the next position.
+ // 3) Find the angle of that vector to the X axis (0 degrees).
+ position.setProgress(currentProgress + 0.0001f);
+ PointF nextPosition = position.getValue();
+ position.setProgress(currentProgress);
+ double rotationValue = Math.toDegrees(Math.atan2(nextPosition.y - startY, nextPosition.x - startX));
+ matrix.preRotate((float) rotationValue);
}
- if (rotationValue != 0f) {
- matrix.preRotate(rotationValue);
+ } else {
+ BaseKeyframeAnimation<Float, Float> rotation = this.rotation;
+ if (rotation != null) {
+ float rotationValue;
+ if (rotation instanceof ValueCallbackKeyframeAnimation) {
+ rotationValue = rotation.getValue();
+ } else {
+ rotationValue = ((FloatKeyframeAnimation) rotation).getFloatValue();
+ }
+ if (rotationValue != 0f) {
+ matrix.preRotate(rotationValue);
+ }
}
}
@@ -221,7 +245,7 @@ public class TransformKeyframeAnimation {
BaseKeyframeAnimation<ScaleXY, ScaleXY> scale = this.scale;
if (scale != null) {
ScaleXY scaleTransform = scale.getValue();
- if (scaleTransform.getScaleX() != 1f || scaleTransform.getScaleY() != 1f) {
+ if (scaleTransform != null && (scaleTransform.getScaleX() != 1f || scaleTransform.getScaleY() != 1f)) {
matrix.preScale(scaleTransform.getScaleX(), scaleTransform.getScaleY());
}
}
@@ -229,7 +253,7 @@ public class TransformKeyframeAnimation {
BaseKeyframeAnimation<PointF, PointF> anchorPoint = this.anchorPoint;
if (anchorPoint != null) {
PointF anchorPointValue = anchorPoint.getValue();
- if (anchorPointValue.x != 0 || anchorPointValue.y != 0) {
+ if (anchorPointValue != null && (anchorPointValue.x != 0 || anchorPointValue.y != 0)) {
matrix.preTranslate(-anchorPointValue.x, -anchorPointValue.y);
}
}
diff --git a/lottie/src/main/java/com/airbnb/lottie/manager/FontAssetManager.java b/lottie/src/main/java/com/airbnb/lottie/manager/FontAssetManager.java
index 9e0de5ef..d2b80e8a 100644
--- a/lottie/src/main/java/com/airbnb/lottie/manager/FontAssetManager.java
+++ b/lottie/src/main/java/com/airbnb/lottie/manager/FontAssetManager.java
@@ -8,6 +8,7 @@ import android.view.View;
import androidx.annotation.Nullable;
import com.airbnb.lottie.FontAssetDelegate;
+import com.airbnb.lottie.model.Font;
import com.airbnb.lottie.model.MutablePair;
import com.airbnb.lottie.utils.Logger;
@@ -55,36 +56,49 @@ public class FontAssetManager {
this.defaultFontFileExtension = defaultFontFileExtension;
}
- public Typeface getTypeface(String fontFamily, String style) {
- tempPair.set(fontFamily, style);
+ public Typeface getTypeface(Font font) {
+ tempPair.set(font.getFamily(), font.getStyle());
Typeface typeface = fontMap.get(tempPair);
if (typeface != null) {
return typeface;
}
- Typeface typefaceWithDefaultStyle = getFontFamily(fontFamily);
- typeface = typefaceForStyle(typefaceWithDefaultStyle, style);
+ Typeface typefaceWithDefaultStyle = getFontFamily(font);
+ typeface = typefaceForStyle(typefaceWithDefaultStyle, font.getStyle());
fontMap.put(tempPair, typeface);
return typeface;
}
- private Typeface getFontFamily(String fontFamily) {
+ private Typeface getFontFamily(Font font) {
+ String fontFamily = font.getFamily();
Typeface defaultTypeface = fontFamilies.get(fontFamily);
if (defaultTypeface != null) {
return defaultTypeface;
}
Typeface typeface = null;
+ String fontStyle = font.getStyle();
+ String fontName = font.getName();
if (delegate != null) {
- typeface = delegate.fetchFont(fontFamily);
+ typeface = delegate.fetchFont(fontFamily, fontStyle, fontName);
+ if (typeface == null) {
+ typeface = delegate.fetchFont(fontFamily);
+ }
}
if (delegate != null && typeface == null) {
- String path = delegate.getFontPath(fontFamily);
+ String path = delegate.getFontPath(fontFamily, fontStyle, fontName);
+ if (path == null) {
+ path = delegate.getFontPath(fontFamily);
+ }
if (path != null) {
typeface = Typeface.createFromAsset(assetManager, path);
}
}
+ if (font.getTypeface() != null) {
+ return font.getTypeface();
+ }
+
if (typeface == null) {
String path = "fonts/" + fontFamily + defaultFontFileExtension;
typeface = Typeface.createFromAsset(assetManager, path);
diff --git a/lottie/src/main/java/com/airbnb/lottie/manager/ImageAssetManager.java b/lottie/src/main/java/com/airbnb/lottie/manager/ImageAssetManager.java
index 06343570..51bd27e2 100644
--- a/lottie/src/main/java/com/airbnb/lottie/manager/ImageAssetManager.java
+++ b/lottie/src/main/java/com/airbnb/lottie/manager/ImageAssetManager.java
@@ -1,25 +1,28 @@
package com.airbnb.lottie.manager;
+
+import android.app.Application;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.Drawable;
-import androidx.annotation.Nullable;
import android.text.TextUtils;
import android.util.Base64;
import android.view.View;
+import androidx.annotation.Nullable;
+
import com.airbnb.lottie.ImageAssetDelegate;
import com.airbnb.lottie.LottieImageAsset;
import com.airbnb.lottie.utils.Logger;
import com.airbnb.lottie.utils.Utils;
+
import java.io.IOException;
import java.io.InputStream;
-import java.util.HashMap;
import java.util.Map;
public class ImageAssetManager {
private static final Object bitmapHashLock = new Object();
- private final Context context;
+ @Nullable private final Context context;
private final String imagesFolder;
@Nullable private ImageAssetDelegate delegate;
private final Map<String, LottieImageAsset> imageAssets;
@@ -31,16 +34,14 @@ public class ImageAssetManager {
} else {
this.imagesFolder = imagesFolder;
}
+ this.imageAssets = imageAssets;
+ setDelegate(delegate);
if (!(callback instanceof View)) {
- Logger.warning("LottieDrawable must be inside of a view for images to work.");
- this.imageAssets = new HashMap<>();
context = null;
return;
}
- context = ((View) callback).getContext();
- this.imageAssets = imageAssets;
- setDelegate(delegate);
+ context = ((View) callback).getContext().getApplicationContext();
}
public void setDelegate(@Nullable ImageAssetDelegate assetDelegate) {
@@ -84,6 +85,12 @@ public class ImageAssetManager {
}
return bitmap;
}
+ Context context = this.context;
+ if (context == null) {
+ // If there is no context, the image has to be embedded or provided via
+ // a delegate.
+ return null;
+ }
String filename = asset.getFileName();
BitmapFactory.Options opts = new BitmapFactory.Options();
@@ -118,7 +125,11 @@ public class ImageAssetManager {
try {
bitmap = BitmapFactory.decodeStream(is, null, opts);
} catch (IllegalArgumentException e) {
- Logger.warning("Unable to decode image.", e);
+ Logger.warning("Unable to decode image `" + id + "`.", e);
+ return null;
+ }
+ if (bitmap == null) {
+ Logger.warning("Decoded image `" + id + "` is null.");
return null;
}
bitmap = Utils.resizeBitmapIfNeeded(bitmap, asset.getWidth(), asset.getHeight());
@@ -126,7 +137,8 @@ public class ImageAssetManager {
}
public boolean hasSameContext(Context context) {
- return context == null && this.context == null || this.context.equals(context);
+ Context contextToCompare = this.context instanceof Application ? context.getApplicationContext() : context;
+ return contextToCompare == this.context;
}
private Bitmap putBitmap(String key, @Nullable Bitmap bitmap) {
diff --git a/lottie/src/main/java/com/airbnb/lottie/model/DocumentData.java b/lottie/src/main/java/com/airbnb/lottie/model/DocumentData.java
index e948d077..3b6cda15 100644
--- a/lottie/src/main/java/com/airbnb/lottie/model/DocumentData.java
+++ b/lottie/src/main/java/com/airbnb/lottie/model/DocumentData.java
@@ -2,7 +2,10 @@ package com.airbnb.lottie.model;
import static androidx.annotation.RestrictTo.Scope.LIBRARY;
+import android.graphics.PointF;
+
import androidx.annotation.ColorInt;
+import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
@RestrictTo(LIBRARY)
@@ -15,22 +18,25 @@ public class DocumentData {
}
public String text;
- @SuppressWarnings("WeakerAccess") public String fontName;
+ public String fontName;
public float size;
- @SuppressWarnings("WeakerAccess") public Justification justification;
+ public Justification justification;
public int tracking;
- @SuppressWarnings("WeakerAccess") public float lineHeight;
+ /** Extra space in between lines. */
+ public float lineHeight;
public float baselineShift;
@ColorInt public int color;
@ColorInt public int strokeColor;
public float strokeWidth;
public boolean strokeOverFill;
+ @Nullable public PointF boxPosition;
+ @Nullable public PointF boxSize;
public DocumentData(String text, String fontName, float size, Justification justification, int tracking,
float lineHeight, float baselineShift, @ColorInt int color, @ColorInt int strokeColor,
- float strokeWidth, boolean strokeOverFill) {
- set(text, fontName, size, justification, tracking, lineHeight, baselineShift, color, strokeColor, strokeWidth, strokeOverFill);
+ float strokeWidth, boolean strokeOverFill, PointF boxPosition, PointF boxSize) {
+ set(text, fontName, size, justification, tracking, lineHeight, baselineShift, color, strokeColor, strokeWidth, strokeOverFill, boxPosition, boxSize);
}
public DocumentData() {
@@ -38,7 +44,7 @@ public class DocumentData {
public void set(String text, String fontName, float size, Justification justification, int tracking,
float lineHeight, float baselineShift, @ColorInt int color, @ColorInt int strokeColor,
- float strokeWidth, boolean strokeOverFill) {
+ float strokeWidth, boolean strokeOverFill, PointF boxPosition, PointF boxSize) {
this.text = text;
this.fontName = fontName;
this.size = size;
@@ -50,6 +56,8 @@ public class DocumentData {
this.strokeColor = strokeColor;
this.strokeWidth = strokeWidth;
this.strokeOverFill = strokeOverFill;
+ this.boxPosition = boxPosition;
+ this.boxSize = boxSize;
}
@Override public int hashCode() {
diff --git a/lottie/src/main/java/com/airbnb/lottie/model/FontCharacter.java b/lottie/src/main/java/com/airbnb/lottie/model/FontCharacter.java
index d394d7f2..12c60eb6 100644
--- a/lottie/src/main/java/com/airbnb/lottie/model/FontCharacter.java
+++ b/lottie/src/main/java/com/airbnb/lottie/model/FontCharacter.java
@@ -12,8 +12,7 @@ import java.util.List;
public class FontCharacter {
public static int hashFor(char character, String fontFamily, String style) {
- int result = 0;
- result = 31 * result + (int) character;
+ int result = (int) character;
result = 31 * result + fontFamily.hashCode();
result = 31 * result + style.hashCode();
return result;
diff --git a/lottie/src/main/java/com/airbnb/lottie/model/animatable/AnimatableGradientColorValue.java b/lottie/src/main/java/com/airbnb/lottie/model/animatable/AnimatableGradientColorValue.java
index 027c6c61..963b7ff1 100644
--- a/lottie/src/main/java/com/airbnb/lottie/model/animatable/AnimatableGradientColorValue.java
+++ b/lottie/src/main/java/com/airbnb/lottie/model/animatable/AnimatableGradientColorValue.java
@@ -5,13 +5,49 @@ import com.airbnb.lottie.animation.keyframe.GradientColorKeyframeAnimation;
import com.airbnb.lottie.model.content.GradientColor;
import com.airbnb.lottie.value.Keyframe;
+import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
public class AnimatableGradientColorValue extends BaseAnimatableValue<GradientColor,
GradientColor> {
- public AnimatableGradientColorValue(
- List<Keyframe<GradientColor>> keyframes) {
- super(keyframes);
+ public AnimatableGradientColorValue(List<Keyframe<GradientColor>> keyframes) {
+ super(ensureInterpolatableKeyframes(keyframes));
+ }
+
+ private static List<Keyframe<GradientColor>> ensureInterpolatableKeyframes(List<Keyframe<GradientColor>> keyframes) {
+ for (int i = 0; i < keyframes.size(); i++) {
+ keyframes.set(i, ensureInterpolatableKeyframe(keyframes.get(i)));
+ }
+ return keyframes;
+ }
+
+ private static Keyframe<GradientColor> ensureInterpolatableKeyframe(Keyframe<GradientColor> keyframe) {
+ GradientColor startValue = keyframe.startValue;
+ GradientColor endValue = keyframe.endValue;
+ if (startValue == null || endValue == null || startValue.getPositions().length == endValue.getPositions().length) {
+ return keyframe;
+ }
+ float[] mergedPositions = mergePositions(startValue.getPositions(), endValue.getPositions());
+ // The start/end has opacity stops which required adding extra positions in between the existing colors.
+ return keyframe.copyWith(startValue.copyWithPositions(mergedPositions), endValue.copyWithPositions(mergedPositions));
+ }
+
+ static float[] mergePositions(float[] startPositions, float[] endPositions) {
+ float[] mergedArray = new float[startPositions.length + endPositions.length];
+ System.arraycopy(startPositions, 0, mergedArray, 0, startPositions.length);
+ System.arraycopy(endPositions, 0, mergedArray, startPositions.length, endPositions.length);
+ Arrays.sort(mergedArray);
+ int uniqueValues = 0;
+ float lastValue = Float.NaN;
+ for (int i = 0; i < mergedArray.length; i++) {
+ if (mergedArray[i] != lastValue) {
+ mergedArray[uniqueValues] = mergedArray[i];
+ uniqueValues++;
+ lastValue = mergedArray[i];
+ }
+ }
+ return Arrays.copyOfRange(mergedArray, 0, uniqueValues);
}
@Override public BaseKeyframeAnimation<GradientColor, GradientColor> createAnimation() {
diff --git a/lottie/src/main/java/com/airbnb/lottie/model/animatable/AnimatableTransform.java b/lottie/src/main/java/com/airbnb/lottie/model/animatable/AnimatableTransform.java
index 2fb5ac93..d88e11f4 100644
--- a/lottie/src/main/java/com/airbnb/lottie/model/animatable/AnimatableTransform.java
+++ b/lottie/src/main/java/com/airbnb/lottie/model/animatable/AnimatableTransform.java
@@ -4,6 +4,7 @@ import android.graphics.PointF;
import androidx.annotation.Nullable;
+import com.airbnb.lottie.LottieComposition;
import com.airbnb.lottie.LottieDrawable;
import com.airbnb.lottie.animation.content.Content;
import com.airbnb.lottie.animation.content.ModifierContent;
@@ -33,6 +34,8 @@ public class AnimatableTransform implements ModifierContent, ContentModel {
@Nullable
private final AnimatableFloatValue endOpacity;
+ private boolean autoOrient = false;
+
public AnimatableTransform() {
this(null, null, null, null, null, null, null, null, null);
}
@@ -53,6 +56,13 @@ public class AnimatableTransform implements ModifierContent, ContentModel {
this.skewAngle = skewAngle;
}
+ /**
+ * This is set as a property of the layer so it is parsed and set separately.
+ */
+ public void setAutoOrient(boolean autoOrient) {
+ this.autoOrient = autoOrient;
+ }
+
@Nullable
public AnimatablePathValue getAnchorPoint() {
return anchorPoint;
@@ -98,13 +108,17 @@ public class AnimatableTransform implements ModifierContent, ContentModel {
return skewAngle;
}
+ public boolean isAutoOrient() {
+ return autoOrient;
+ }
+
public TransformKeyframeAnimation createAnimation() {
return new TransformKeyframeAnimation(this);
}
@Nullable
@Override
- public Content toContent(LottieDrawable drawable, BaseLayer layer) {
+ public Content toContent(LottieDrawable drawable, LottieComposition composition, BaseLayer layer) {
return null;
}
}
diff --git a/lottie/src/main/java/com/airbnb/lottie/model/content/CircleShape.java b/lottie/src/main/java/com/airbnb/lottie/model/content/CircleShape.java
index 9d7add69..fd7cd23b 100644
--- a/lottie/src/main/java/com/airbnb/lottie/model/content/CircleShape.java
+++ b/lottie/src/main/java/com/airbnb/lottie/model/content/CircleShape.java
@@ -2,6 +2,7 @@ package com.airbnb.lottie.model.content;
import android.graphics.PointF;
+import com.airbnb.lottie.LottieComposition;
import com.airbnb.lottie.LottieDrawable;
import com.airbnb.lottie.animation.content.Content;
import com.airbnb.lottie.animation.content.EllipseContent;
@@ -25,7 +26,7 @@ public class CircleShape implements ContentModel {
this.hidden = hidden;
}
- @Override public Content toContent(LottieDrawable drawable, BaseLayer layer) {
+ @Override public Content toContent(LottieDrawable drawable, LottieComposition composition, BaseLayer layer) {
return new EllipseContent(drawable, layer, this);
}
diff --git a/lottie/src/main/java/com/airbnb/lottie/model/content/ContentModel.java b/lottie/src/main/java/com/airbnb/lottie/model/content/ContentModel.java
index 2e926857..a5060471 100644
--- a/lottie/src/main/java/com/airbnb/lottie/model/content/ContentModel.java
+++ b/lottie/src/main/java/com/airbnb/lottie/model/content/ContentModel.java
@@ -3,10 +3,11 @@ package com.airbnb.lottie.model.content;
import androidx.annotation.Nullable;
+import com.airbnb.lottie.LottieComposition;
import com.airbnb.lottie.LottieDrawable;
import com.airbnb.lottie.animation.content.Content;
import com.airbnb.lottie.model.layer.BaseLayer;
public interface ContentModel {
- @Nullable Content toContent(LottieDrawable drawable, BaseLayer layer);
+ @Nullable Content toContent(LottieDrawable drawable, LottieComposition composition, BaseLayer layer);
}
diff --git a/lottie/src/main/java/com/airbnb/lottie/model/content/GradientColor.java b/lottie/src/main/java/com/airbnb/lottie/model/content/GradientColor.java
index 8385c158..41e1e29e 100644
--- a/lottie/src/main/java/com/airbnb/lottie/model/content/GradientColor.java
+++ b/lottie/src/main/java/com/airbnb/lottie/model/content/GradientColor.java
@@ -3,6 +3,8 @@ package com.airbnb.lottie.model.content;
import com.airbnb.lottie.utils.GammaEvaluator;
import com.airbnb.lottie.utils.MiscUtils;
+import java.util.Arrays;
+
public class GradientColor {
private final float[] positions;
@@ -26,6 +28,19 @@ public class GradientColor {
}
public void lerp(GradientColor gc1, GradientColor gc2, float progress) {
+ // Fast return in case start and end is the same
+ // or if progress is at start/end or out of [0,1] bounds
+ if (gc1.equals(gc2)) {
+ copyFrom(gc1);
+ return;
+ } else if (progress <= 0f) {
+ copyFrom(gc1);
+ return;
+ } else if (progress >= 1f) {
+ copyFrom(gc2);
+ return;
+ }
+
if (gc1.colors.length != gc2.colors.length) {
throw new IllegalArgumentException("Cannot interpolate between gradients. Lengths vary (" +
gc1.colors.length + " vs " + gc2.colors.length + ")");
@@ -35,5 +50,69 @@ public class GradientColor {
positions[i] = MiscUtils.lerp(gc1.positions[i], gc2.positions[i], progress);
colors[i] = GammaEvaluator.evaluate(progress, gc1.colors[i], gc2.colors[i]);
}
+
+ // Not all keyframes that this GradientColor are used for will have the same length.
+ // AnimatableGradientColorValue.ensureInterpolatableKeyframes may add extra positions
+ // for some keyframes but not others to ensure that it is interpolatable.
+ // If there are extra positions here, just duplicate the last value in the gradient.
+ for (int i = gc1.colors.length; i < positions.length; i++) {
+ positions[i] = positions[gc1.colors.length - 1];
+ colors[i] = colors[gc1.colors.length - 1];
+ }
+ }
+
+ public GradientColor copyWithPositions(float[] positions) {
+ int[] colors = new int[positions.length];
+ for (int i = 0; i < positions.length; i++) {
+ colors[i] = getColorForPosition(positions[i]);
+ }
+ return new GradientColor(positions, colors);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ GradientColor that = (GradientColor) o;
+ return Arrays.equals(positions, that.positions) && Arrays.equals(colors, that.colors);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = Arrays.hashCode(positions);
+ result = 31 * result + Arrays.hashCode(colors);
+ return result;
+ }
+
+ private int getColorForPosition(float position) {
+ int existingIndex = Arrays.binarySearch(positions, position);
+ if (existingIndex >= 0) {
+ return colors[existingIndex];
+ }
+ // binarySearch returns -insertionPoint - 1 if it is not found.
+ int insertionPoint = -(existingIndex + 1);
+ if (insertionPoint == 0) {
+ return colors[0];
+ } else if (insertionPoint == colors.length - 1) {
+ return colors[colors.length - 1];
+ }
+ float startPosition = positions[insertionPoint - 1];
+ float endPosition = positions[insertionPoint];
+ int startColor = colors[insertionPoint - 1];
+ int endColor = colors[insertionPoint];
+
+ float fraction = (position - startPosition) / (endPosition - startPosition);
+ return GammaEvaluator.evaluate(fraction, startColor, endColor);
+ }
+
+ private void copyFrom(GradientColor other) {
+ for (int i = 0; i < other.colors.length; i++) {
+ positions[i] = other.positions[i];
+ colors[i] = other.colors[i];
+ }
}
}
diff --git a/lottie/src/main/java/com/airbnb/lottie/model/content/GradientFill.java b/lottie/src/main/java/com/airbnb/lottie/model/content/GradientFill.java
index 87ecf3a0..9a082809 100644
--- a/lottie/src/main/java/com/airbnb/lottie/model/content/GradientFill.java
+++ b/lottie/src/main/java/com/airbnb/lottie/model/content/GradientFill.java
@@ -4,6 +4,7 @@ import android.graphics.Path;
import androidx.annotation.Nullable;
+import com.airbnb.lottie.LottieComposition;
import com.airbnb.lottie.LottieDrawable;
import com.airbnb.lottie.animation.content.Content;
import com.airbnb.lottie.animation.content.GradientFillContent;
@@ -75,8 +76,8 @@ public class GradientFill implements ContentModel {
return hidden;
}
- @Override public Content toContent(LottieDrawable drawable, BaseLayer layer) {
- return new GradientFillContent(drawable, layer, this);
+ @Override public Content toContent(LottieDrawable drawable, LottieComposition composition, BaseLayer layer) {
+ return new GradientFillContent(drawable, composition, layer, this);
}
}
diff --git a/lottie/src/main/java/com/airbnb/lottie/model/content/GradientStroke.java b/lottie/src/main/java/com/airbnb/lottie/model/content/GradientStroke.java
index 837277c3..6e4b2029 100644
--- a/lottie/src/main/java/com/airbnb/lottie/model/content/GradientStroke.java
+++ b/lottie/src/main/java/com/airbnb/lottie/model/content/GradientStroke.java
@@ -2,6 +2,7 @@ package com.airbnb.lottie.model.content;
import androidx.annotation.Nullable;
+import com.airbnb.lottie.LottieComposition;
import com.airbnb.lottie.LottieDrawable;
import com.airbnb.lottie.animation.content.Content;
import com.airbnb.lottie.animation.content.GradientStrokeContent;
@@ -103,7 +104,7 @@ public class GradientStroke implements ContentModel {
return hidden;
}
- @Override public Content toContent(LottieDrawable drawable, BaseLayer layer) {
+ @Override public Content toContent(LottieDrawable drawable, LottieComposition composition, BaseLayer layer) {
return new GradientStrokeContent(drawable, layer, this);
}
}
diff --git a/lottie/src/main/java/com/airbnb/lottie/model/content/LBlendMode.java b/lottie/src/main/java/com/airbnb/lottie/model/content/LBlendMode.java
new file mode 100644
index 00000000..e1c91a0a
--- /dev/null
+++ b/lottie/src/main/java/com/airbnb/lottie/model/content/LBlendMode.java
@@ -0,0 +1,68 @@
+package com.airbnb.lottie.model.content;
+
+import androidx.annotation.Nullable;
+import androidx.core.graphics.BlendModeCompat;
+
+/**
+ * Lottie BlendMode,
+ * not to be confused with Paint.BlendMode in android graphics core,
+ * which we will rely on for rendering.
+ */
+public enum LBlendMode {
+ NORMAL,
+ MULTIPLY,
+ SCREEN,
+ OVERLAY,
+ DARKEN,
+ LIGHTEN,
+ COLOR_DODGE,
+ COLOR_BURN,
+ HARD_LIGHT,
+ SOFT_LIGHT,
+ DIFFERENCE,
+ EXCLUSION,
+ HUE,
+ SATURATION,
+ COLOR,
+ LUMINOSITY,
+ ADD,
+ HARD_MIX;
+
+ @Nullable
+ public BlendModeCompat toNativeBlendMode() {
+ switch (this) {
+ case NORMAL:
+ return null;
+ case SCREEN:
+ return BlendModeCompat.SCREEN;
+ case OVERLAY:
+ return BlendModeCompat.OVERLAY;
+ case DARKEN:
+ return BlendModeCompat.DARKEN;
+ case LIGHTEN:
+ return BlendModeCompat.LIGHTEN;
+ case ADD:
+ return BlendModeCompat.PLUS;
+
+ // Blend modes below were not added to the platform until Q.
+ // To prevent unexpected issues where animations look correct
+ // during development but silently break for users with older devices
+ // we won't support any of these until Q is widely used.
+ case MULTIPLY:
+ case COLOR_DODGE:
+ case COLOR_BURN:
+ case HARD_LIGHT:
+ case SOFT_LIGHT:
+ case DIFFERENCE:
+ case EXCLUSION:
+ case HUE:
+ case SATURATION:
+ case COLOR:
+ case LUMINOSITY:
+ case HARD_MIX:
+ default:
+ return null;
+ }
+ }
+
+}
diff --git a/lottie/src/main/java/com/airbnb/lottie/model/content/MergePaths.java b/lottie/src/main/java/com/airbnb/lottie/model/content/MergePaths.java
index f8d4453f..315add2d 100644
--- a/lottie/src/main/java/com/airbnb/lottie/model/content/MergePaths.java
+++ b/lottie/src/main/java/com/airbnb/lottie/model/content/MergePaths.java
@@ -2,6 +2,7 @@ package com.airbnb.lottie.model.content;
import androidx.annotation.Nullable;
+import com.airbnb.lottie.LottieComposition;
import com.airbnb.lottie.LottieDrawable;
import com.airbnb.lottie.animation.content.Content;
import com.airbnb.lottie.animation.content.MergePathsContent;
@@ -58,7 +59,7 @@ public class MergePaths implements ContentModel {
return hidden;
}
- @Override @Nullable public Content toContent(LottieDrawable drawable, BaseLayer layer) {
+ @Override @Nullable public Content toContent(LottieDrawable drawable, LottieComposition composition, BaseLayer layer) {
if (!drawable.enableMergePathsForKitKatAndAbove()) {
Logger.warning("Animation contains merge paths but they are disabled.");
return null;
diff --git a/lottie/src/main/java/com/airbnb/lottie/model/content/PolystarShape.java b/lottie/src/main/java/com/airbnb/lottie/model/content/PolystarShape.java
index e7d805ad..45144221 100644
--- a/lottie/src/main/java/com/airbnb/lottie/model/content/PolystarShape.java
+++ b/lottie/src/main/java/com/airbnb/lottie/model/content/PolystarShape.java
@@ -2,6 +2,7 @@ package com.airbnb.lottie.model.content;
import android.graphics.PointF;
+import com.airbnb.lottie.LottieComposition;
import com.airbnb.lottie.LottieDrawable;
import com.airbnb.lottie.animation.content.Content;
import com.airbnb.lottie.animation.content.PolystarContent;
@@ -104,7 +105,7 @@ public class PolystarShape implements ContentModel {
return isReversed;
}
- @Override public Content toContent(LottieDrawable drawable, BaseLayer layer) {
+ @Override public Content toContent(LottieDrawable drawable, LottieComposition composition, BaseLayer layer) {
return new PolystarContent(drawable, layer, this);
}
}
diff --git a/lottie/src/main/java/com/airbnb/lottie/model/content/RectangleShape.java b/lottie/src/main/java/com/airbnb/lottie/model/content/RectangleShape.java
index 6edd0303..13b1f4d4 100644
--- a/lottie/src/main/java/com/airbnb/lottie/model/content/RectangleShape.java
+++ b/lottie/src/main/java/com/airbnb/lottie/model/content/RectangleShape.java
@@ -2,6 +2,7 @@ package com.airbnb.lottie.model.content;
import android.graphics.PointF;
+import com.airbnb.lottie.LottieComposition;
import com.airbnb.lottie.LottieDrawable;
import com.airbnb.lottie.animation.content.Content;
import com.airbnb.lottie.animation.content.RectangleContent;
@@ -45,7 +46,7 @@ public class RectangleShape implements ContentModel {
return hidden;
}
- @Override public Content toContent(LottieDrawable drawable, BaseLayer layer) {
+ @Override public Content toContent(LottieDrawable drawable, LottieComposition composition, BaseLayer layer) {
return new RectangleContent(drawable, layer, this);
}
diff --git a/lottie/src/main/java/com/airbnb/lottie/model/content/Repeater.java b/lottie/src/main/java/com/airbnb/lottie/model/content/Repeater.java
index f031aa73..f336391e 100644
--- a/lottie/src/main/java/com/airbnb/lottie/model/content/Repeater.java
+++ b/lottie/src/main/java/com/airbnb/lottie/model/content/Repeater.java
@@ -2,6 +2,7 @@ package com.airbnb.lottie.model.content;
import androidx.annotation.Nullable;
+import com.airbnb.lottie.LottieComposition;
import com.airbnb.lottie.LottieDrawable;
import com.airbnb.lottie.animation.content.Content;
import com.airbnb.lottie.animation.content.RepeaterContent;
@@ -45,7 +46,7 @@ public class Repeater implements ContentModel {
return hidden;
}
- @Nullable @Override public Content toContent(LottieDrawable drawable, BaseLayer layer) {
+ @Nullable @Override public Content toContent(LottieDrawable drawable, LottieComposition composition, BaseLayer layer) {
return new RepeaterContent(drawable, layer, this);
}
}
diff --git a/lottie/src/main/java/com/airbnb/lottie/model/content/RoundedCorners.java b/lottie/src/main/java/com/airbnb/lottie/model/content/RoundedCorners.java
index 0541e468..8daa5378 100644
--- a/lottie/src/main/java/com/airbnb/lottie/model/content/RoundedCorners.java
+++ b/lottie/src/main/java/com/airbnb/lottie/model/content/RoundedCorners.java
@@ -2,6 +2,7 @@ package com.airbnb.lottie.model.content;
import androidx.annotation.Nullable;
+import com.airbnb.lottie.LottieComposition;
import com.airbnb.lottie.LottieDrawable;
import com.airbnb.lottie.animation.content.Content;
import com.airbnb.lottie.animation.content.RoundedCornersContent;
@@ -25,7 +26,7 @@ public class RoundedCorners implements ContentModel {
return cornerRadius;
}
- @Nullable @Override public Content toContent(LottieDrawable drawable, BaseLayer layer) {
+ @Nullable @Override public Content toContent(LottieDrawable drawable, LottieComposition composition, BaseLayer layer) {
return new RoundedCornersContent(drawable, layer, this);
}
}
diff --git a/lottie/src/main/java/com/airbnb/lottie/model/content/ShapeFill.java b/lottie/src/main/java/com/airbnb/lottie/model/content/ShapeFill.java
index f992d107..a3364ac2 100644
--- a/lottie/src/main/java/com/airbnb/lottie/model/content/ShapeFill.java
+++ b/lottie/src/main/java/com/airbnb/lottie/model/content/ShapeFill.java
@@ -4,6 +4,7 @@ import android.graphics.Path;
import androidx.annotation.Nullable;
+import com.airbnb.lottie.LottieComposition;
import com.airbnb.lottie.LottieDrawable;
import com.airbnb.lottie.animation.content.Content;
import com.airbnb.lottie.animation.content.FillContent;
@@ -49,7 +50,7 @@ public class ShapeFill implements ContentModel {
return hidden;
}
- @Override public Content toContent(LottieDrawable drawable, BaseLayer layer) {
+ @Override public Content toContent(LottieDrawable drawable, LottieComposition composition, BaseLayer layer) {
return new FillContent(drawable, layer, this);
}
diff --git a/lottie/src/main/java/com/airbnb/lottie/model/content/ShapeGroup.java b/lottie/src/main/java/com/airbnb/lottie/model/content/ShapeGroup.java
index ed332bc1..5865d6ed 100644
--- a/lottie/src/main/java/com/airbnb/lottie/model/content/ShapeGroup.java
+++ b/lottie/src/main/java/com/airbnb/lottie/model/content/ShapeGroup.java
@@ -1,5 +1,6 @@
package com.airbnb.lottie.model.content;
+import com.airbnb.lottie.LottieComposition;
import com.airbnb.lottie.LottieDrawable;
import com.airbnb.lottie.animation.content.Content;
import com.airbnb.lottie.animation.content.ContentGroup;
@@ -31,8 +32,8 @@ public class ShapeGroup implements ContentModel {
return hidden;
}
- @Override public Content toContent(LottieDrawable drawable, BaseLayer layer) {
- return new ContentGroup(drawable, layer, this);
+ @Override public Content toContent(LottieDrawable drawable, LottieComposition composition, BaseLayer layer) {
+ return new ContentGroup(drawable, layer, this, composition);
}
@Override public String toString() {
diff --git a/lottie/src/main/java/com/airbnb/lottie/model/content/ShapePath.java b/lottie/src/main/java/com/airbnb/lottie/model/content/ShapePath.java
index af161998..9ae952b2 100644
--- a/lottie/src/main/java/com/airbnb/lottie/model/content/ShapePath.java
+++ b/lottie/src/main/java/com/airbnb/lottie/model/content/ShapePath.java
@@ -1,5 +1,6 @@
package com.airbnb.lottie.model.content;
+import com.airbnb.lottie.LottieComposition;
import com.airbnb.lottie.LottieDrawable;
import com.airbnb.lottie.animation.content.Content;
import com.airbnb.lottie.animation.content.ShapeContent;
@@ -27,7 +28,7 @@ public class ShapePath implements ContentModel {
return shapePath;
}
- @Override public Content toContent(LottieDrawable drawable, BaseLayer layer) {
+ @Override public Content toContent(LottieDrawable drawable, LottieComposition composition, BaseLayer layer) {
return new ShapeContent(drawable, layer, this);
}
diff --git a/lottie/src/main/java/com/airbnb/lottie/model/content/ShapeStroke.java b/lottie/src/main/java/com/airbnb/lottie/model/content/ShapeStroke.java
index dacb6bea..4c900585 100644
--- a/lottie/src/main/java/com/airbnb/lottie/model/content/ShapeStroke.java
+++ b/lottie/src/main/java/com/airbnb/lottie/model/content/ShapeStroke.java
@@ -4,6 +4,7 @@ import android.graphics.Paint;
import androidx.annotation.Nullable;
+import com.airbnb.lottie.LottieComposition;
import com.airbnb.lottie.LottieDrawable;
import com.airbnb.lottie.animation.content.Content;
import com.airbnb.lottie.animation.content.StrokeContent;
@@ -78,7 +79,7 @@ public class ShapeStroke implements ContentModel {
this.hidden = hidden;
}
- @Override public Content toContent(LottieDrawable drawable, BaseLayer layer) {
+ @Override public Content toContent(LottieDrawable drawable, LottieComposition composition, BaseLayer layer) {
return new StrokeContent(drawable, layer, this);
}
diff --git a/lottie/src/main/java/com/airbnb/lottie/model/content/ShapeTrimPath.java b/lottie/src/main/java/com/airbnb/lottie/model/content/ShapeTrimPath.java
index bf75948c..b86e5ed2 100644
--- a/lottie/src/main/java/com/airbnb/lottie/model/content/ShapeTrimPath.java
+++ b/lottie/src/main/java/com/airbnb/lottie/model/content/ShapeTrimPath.java
@@ -1,5 +1,6 @@
package com.airbnb.lottie.model.content;
+import com.airbnb.lottie.LottieComposition;
import com.airbnb.lottie.LottieDrawable;
import com.airbnb.lottie.animation.content.Content;
import com.airbnb.lottie.animation.content.TrimPathContent;
@@ -65,7 +66,7 @@ public class ShapeTrimPath implements ContentModel {
return hidden;
}
- @Override public Content toContent(LottieDrawable drawable, BaseLayer layer) {
+ @Override public Content toContent(LottieDrawable drawable, LottieComposition composition, BaseLayer layer) {
return new TrimPathContent(layer, this);
}
diff --git a/lottie/src/main/java/com/airbnb/lottie/model/layer/BaseLayer.java b/lottie/src/main/java/com/airbnb/lottie/model/layer/BaseLayer.java
index 14b1c667..427cb65d 100644
--- a/lottie/src/main/java/com/airbnb/lottie/model/layer/BaseLayer.java
+++ b/lottie/src/main/java/com/airbnb/lottie/model/layer/BaseLayer.java
@@ -28,6 +28,7 @@ import com.airbnb.lottie.animation.keyframe.TransformKeyframeAnimation;
import com.airbnb.lottie.model.KeyPath;
import com.airbnb.lottie.model.KeyPathElement;
import com.airbnb.lottie.model.content.BlurEffect;
+import com.airbnb.lottie.model.content.LBlendMode;
import com.airbnb.lottie.model.content.Mask;
import com.airbnb.lottie.model.content.ShapeData;
import com.airbnb.lottie.parser.DropShadowEffect;
@@ -55,7 +56,7 @@ public abstract class BaseLayer
CompositionLayer compositionLayer, Layer layerModel, LottieDrawable drawable, LottieComposition composition) {
switch (layerModel.getLayerType()) {
case SHAPE:
- return new ShapeLayer(drawable, layerModel, compositionLayer);
+ return new ShapeLayer(drawable, layerModel, compositionLayer, composition);
case PRE_COMP:
return new CompositionLayer(drawable, layerModel,
composition.getPrecomps(layerModel.getRefId()), composition);
@@ -241,9 +242,18 @@ public abstract class BaseLayer
matrix.preConcat(parentLayers.get(i).transform.getMatrix());
}
L.endSection("Layer#parentMatrix");
- int opacity = transform.getOpacity() == null ? 100 : transform.getOpacity().getValue();
- int alpha = (int)
- ((parentAlpha / 255f * (float) opacity / 100f) * 255);
+ // It is unclear why but getting the opacity here would sometimes NPE.
+ // The extra code here is designed to avoid this.
+ // https://github.com/airbnb/lottie-android/issues/2083
+ int opacity = 100;
+ BaseKeyframeAnimation<?, Integer> opacityAnimation = transform.getOpacity();
+ if (opacityAnimation != null) {
+ Integer opacityValue = opacityAnimation.getValue();
+ if (opacityValue != null) {
+ opacity = opacityValue;
+ }
+ }
+ int alpha = (int) ((parentAlpha / 255f * (float) opacity / 100f) * 255);
if (!hasMatteOnThisLayer() && !hasMasksOnThisLayer()) {
matrix.preConcat(transform.getMatrix());
L.beginSection("Layer#drawLayer");
@@ -562,22 +572,34 @@ public abstract class BaseLayer
}
void setProgress(@FloatRange(from = 0f, to = 1f) float progress) {
+ L.beginSection("BaseLayer#setProgress");
// Time stretch should not be applied to the layer transform.
+ L.beginSection("BaseLayer#setProgress.transform");
transform.setProgress(progress);
+ L.endSection("BaseLayer#setProgress.transform");
if (mask != null) {
+ L.beginSection("BaseLayer#setProgress.mask");
for (int i = 0; i < mask.getMaskAnimations().size(); i++) {
mask.getMaskAnimations().get(i).setProgress(progress);
}
+ L.endSection("BaseLayer#setProgress.mask");
}
if (inOutAnimation != null) {
+ L.beginSection("BaseLayer#setProgress.inout");
inOutAnimation.setProgress(progress);
+ L.endSection("BaseLayer#setProgress.inout");
}
if (matteLayer != null) {
+ L.beginSection("BaseLayer#setProgress.matte");
matteLayer.setProgress(progress);
+ L.endSection("BaseLayer#setProgress.matte");
}
+ L.beginSection("BaseLayer#setProgress.animations." + animations.size());
for (int i = 0; i < animations.size(); i++) {
animations.get(i).setProgress(progress);
}
+ L.endSection("BaseLayer#setProgress.animations." + animations.size());
+ L.endSection("BaseLayer#setProgress");
}
private void buildParentLayerListIfNeeded() {
@@ -607,6 +629,10 @@ public abstract class BaseLayer
return layerModel.getBlurEffect();
}
+ public LBlendMode getBlendMode() {
+ return layerModel.getBlendMode();
+ }
+
public BlurMaskFilter getBlurMaskFilter(float radius) {
if (blurMaskFilterRadius == radius) {
return blurMaskFilter;
diff --git a/lottie/src/main/java/com/airbnb/lottie/model/layer/CompositionLayer.java b/lottie/src/main/java/com/airbnb/lottie/model/layer/CompositionLayer.java
index 6ddd5f74..7c923a65 100644
--- a/lottie/src/main/java/com/airbnb/lottie/model/layer/CompositionLayer.java
+++ b/lottie/src/main/java/com/airbnb/lottie/model/layer/CompositionLayer.java
@@ -4,6 +4,7 @@ import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.RectF;
+import android.util.Log;
import androidx.annotation.FloatRange;
import androidx.annotation.Nullable;
@@ -32,6 +33,7 @@ public class CompositionLayer extends BaseLayer {
@Nullable private Boolean hasMatte;
@Nullable private Boolean hasMasks;
+ private float progress;
private boolean clipToCompositionBounds = true;
@@ -142,6 +144,8 @@ public class CompositionLayer extends BaseLayer {
}
@Override public void setProgress(@FloatRange(from = 0f, to = 1f) float progress) {
+ L.beginSection("CompositionLayer#setProgress");
+ this.progress = progress;
super.setProgress(progress);
if (timeRemapping != null) {
// The duration has 0.01 frame offset to show end of animation properly.
@@ -162,6 +166,11 @@ public class CompositionLayer extends BaseLayer {
for (int i = layers.size() - 1; i >= 0; i--) {
layers.get(i).setProgress(progress);
}
+ L.endSection("CompositionLayer#setProgress");
+ }
+
+ public float getProgress() {
+ return progress;
}
public boolean hasMasks() {
diff --git a/lottie/src/main/java/com/airbnb/lottie/model/layer/Layer.java b/lottie/src/main/java/com/airbnb/lottie/model/layer/Layer.java
index e2fa79d1..edea4fa2 100644
--- a/lottie/src/main/java/com/airbnb/lottie/model/layer/Layer.java
+++ b/lottie/src/main/java/com/airbnb/lottie/model/layer/Layer.java
@@ -9,6 +9,7 @@ import com.airbnb.lottie.model.animatable.AnimatableTextProperties;
import com.airbnb.lottie.model.animatable.AnimatableTransform;
import com.airbnb.lottie.model.content.BlurEffect;
import com.airbnb.lottie.model.content.ContentModel;
+import com.airbnb.lottie.model.content.LBlendMode;
import com.airbnb.lottie.model.content.Mask;
import com.airbnb.lottie.parser.DropShadowEffect;
import com.airbnb.lottie.value.Keyframe;
@@ -51,8 +52,8 @@ public class Layer {
private final int solidColor;
private final float timeStretch;
private final float startFrame;
- private final int preCompWidth;
- private final int preCompHeight;
+ private final float preCompWidth;
+ private final float preCompHeight;
@Nullable private final AnimatableTextFrame text;
@Nullable private final AnimatableTextProperties textProperties;
@Nullable private final AnimatableFloatValue timeRemapping;
@@ -61,15 +62,17 @@ public class Layer {
private final boolean hidden;
@Nullable private final BlurEffect blurEffect;
@Nullable private final DropShadowEffect dropShadowEffect;
+ private final LBlendMode blendMode;
+
public Layer(List<ContentModel> shapes, LottieComposition composition, String layerName, long layerId,
LayerType layerType, long parentId, @Nullable String refId, List<Mask> masks,
AnimatableTransform transform, int solidWidth, int solidHeight, int solidColor,
- float timeStretch, float startFrame, int preCompWidth, int preCompHeight,
+ float timeStretch, float startFrame, float preCompWidth, float preCompHeight,
@Nullable AnimatableTextFrame text, @Nullable AnimatableTextProperties textProperties,
List<Keyframe<Float>> inOutKeyframes, MatteType matteType,
@Nullable AnimatableFloatValue timeRemapping, boolean hidden, @Nullable BlurEffect blurEffect,
- @Nullable DropShadowEffect dropShadowEffect) {
+ @Nullable DropShadowEffect dropShadowEffect, LBlendMode blendMode) {
this.shapes = shapes;
this.composition = composition;
this.layerName = layerName;
@@ -94,6 +97,7 @@ public class Layer {
this.hidden = hidden;
this.blurEffect = blurEffect;
this.dropShadowEffect = dropShadowEffect;
+ this.blendMode = blendMode;
}
LottieComposition getComposition() {
@@ -116,19 +120,19 @@ public class Layer {
return layerId;
}
- String getName() {
+ public String getName() {
return layerName;
}
- @Nullable String getRefId() {
+ public @Nullable String getRefId() {
return refId;
}
- int getPreCompWidth() {
+ float getPreCompWidth() {
return preCompWidth;
}
- int getPreCompHeight() {
+ float getPreCompHeight() {
return preCompHeight;
}
@@ -188,6 +192,11 @@ public class Layer {
return hidden;
}
+ @Nullable
+ public LBlendMode getBlendMode() {
+ return blendMode;
+ }
+
@Nullable public BlurEffect getBlurEffect() {
return blurEffect;
}
diff --git a/lottie/src/main/java/com/airbnb/lottie/model/layer/ShapeLayer.java b/lottie/src/main/java/com/airbnb/lottie/model/layer/ShapeLayer.java
index 4250d8ed..3a82138f 100644
--- a/lottie/src/main/java/com/airbnb/lottie/model/layer/ShapeLayer.java
+++ b/lottie/src/main/java/com/airbnb/lottie/model/layer/ShapeLayer.java
@@ -7,6 +7,7 @@ import android.graphics.RectF;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.airbnb.lottie.LottieComposition;
import com.airbnb.lottie.LottieDrawable;
import com.airbnb.lottie.animation.content.Content;
import com.airbnb.lottie.animation.content.ContentGroup;
@@ -22,13 +23,13 @@ public class ShapeLayer extends BaseLayer {
private final ContentGroup contentGroup;
private final CompositionLayer compositionLayer;
- ShapeLayer(LottieDrawable lottieDrawable, Layer layerModel, CompositionLayer compositionLayer) {
+ ShapeLayer(LottieDrawable lottieDrawable, Layer layerModel, CompositionLayer compositionLayer, LottieComposition composition) {
super(lottieDrawable, layerModel);
this.compositionLayer = compositionLayer;
// Naming this __container allows it to be ignored in KeyPath matching.
ShapeGroup shapeGroup = new ShapeGroup("__container", layerModel.getShapes(), false);
- contentGroup = new ContentGroup(lottieDrawable, this, shapeGroup);
+ contentGroup = new ContentGroup(lottieDrawable, this, shapeGroup, composition);
contentGroup.setContents(Collections.<Content>emptyList(), Collections.<Content>emptyList());
}
diff --git a/lottie/src/main/java/com/airbnb/lottie/model/layer/SolidLayer.java b/lottie/src/main/java/com/airbnb/lottie/model/layer/SolidLayer.java
index 993e1b3a..19e7d53b 100644
--- a/lottie/src/main/java/com/airbnb/lottie/model/layer/SolidLayer.java
+++ b/lottie/src/main/java/com/airbnb/lottie/model/layer/SolidLayer.java
@@ -18,12 +18,14 @@ import com.airbnb.lottie.animation.keyframe.ValueCallbackKeyframeAnimation;
import com.airbnb.lottie.value.LottieValueCallback;
public class SolidLayer extends BaseLayer {
+
private final RectF rect = new RectF();
private final Paint paint = new LPaint();
private final float[] points = new float[8];
private final Path path = new Path();
private final Layer layerModel;
@Nullable private BaseKeyframeAnimation<ColorFilter, ColorFilter> colorFilterAnimation;
+ @Nullable private BaseKeyframeAnimation<Integer, Integer> colorAnimation;
SolidLayer(LottieDrawable lottieDrawable, Layer layerModel) {
super(lottieDrawable, layerModel);
@@ -40,9 +42,17 @@ public class SolidLayer extends BaseLayer {
return;
}
+ Integer color = colorAnimation == null ? null : colorAnimation.getValue();
+ if (color != null) {
+ paint.setColor(color);
+ } else {
+ paint.setColor(layerModel.getSolidColor());
+ }
+
int opacity = transform.getOpacity() == null ? 100 : transform.getOpacity().getValue();
int alpha = (int) (parentAlpha / 255f * (backgroundAlpha / 255f * opacity / 100f) * 255);
paint.setAlpha(alpha);
+
if (colorFilterAnimation != null) {
paint.setColorFilter(colorFilterAnimation.getValue());
}
@@ -88,6 +98,13 @@ public class SolidLayer extends BaseLayer {
colorFilterAnimation =
new ValueCallbackKeyframeAnimation<>((LottieValueCallback<ColorFilter>) callback);
}
+ } else if (property == LottieProperty.COLOR) {
+ if (callback == null) {
+ colorAnimation = null;
+ paint.setColor(layerModel.getSolidColor());
+ } else {
+ colorAnimation = new ValueCallbackKeyframeAnimation<>((LottieValueCallback<Integer>) callback);
+ }
}
}
}
diff --git a/lottie/src/main/java/com/airbnb/lottie/model/layer/TextLayer.java b/lottie/src/main/java/com/airbnb/lottie/model/layer/TextLayer.java
index 9963a0f3..211f1d6a 100644
--- a/lottie/src/main/java/com/airbnb/lottie/model/layer/TextLayer.java
+++ b/lottie/src/main/java/com/airbnb/lottie/model/layer/TextLayer.java
@@ -5,6 +5,7 @@ import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
+import android.graphics.PointF;
import android.graphics.RectF;
import android.graphics.Typeface;
@@ -20,7 +21,6 @@ import com.airbnb.lottie.animation.keyframe.BaseKeyframeAnimation;
import com.airbnb.lottie.animation.keyframe.TextKeyframeAnimation;
import com.airbnb.lottie.animation.keyframe.ValueCallbackKeyframeAnimation;
import com.airbnb.lottie.model.DocumentData;
-import com.airbnb.lottie.model.DocumentData.Justification;
import com.airbnb.lottie.model.Font;
import com.airbnb.lottie.model.FontCharacter;
import com.airbnb.lottie.model.animatable.AnimatableTextProperties;
@@ -35,6 +35,7 @@ import java.util.List;
import java.util.Map;
public class TextLayer extends BaseLayer {
+
// Capacity is 2 because emojis are 2 characters. Some are longer in which case, the capacity will
// be expanded but that should be pretty rare.
private final StringBuilder stringBuilder = new StringBuilder(2);
@@ -48,6 +49,10 @@ public class TextLayer extends BaseLayer {
}};
private final Map<FontCharacter, List<ContentGroup>> contentsForCharacter = new HashMap<>();
private final LongSparseArray<String> codePointCache = new LongSparseArray<>();
+ /**
+ * If this is paragraph text, one line may wrap depending on the size of the document data box.
+ */
+ private final List<TextSubLine> textSubLines = new ArrayList<>();
private final TextKeyframeAnimation textAnimation;
private final LottieDrawable lottieDrawable;
private final LottieComposition composition;
@@ -116,18 +121,26 @@ public class TextLayer extends BaseLayer {
@Override
void drawLayer(Canvas canvas, Matrix parentMatrix, int parentAlpha) {
- canvas.save();
- if (!lottieDrawable.useTextGlyphs()) {
- canvas.concat(parentMatrix);
- }
DocumentData documentData = textAnimation.getValue();
Font font = composition.getFonts().get(documentData.fontName);
if (font == null) {
- // Something is wrong.
- canvas.restore();
return;
}
+ canvas.save();
+ canvas.concat(parentMatrix);
+
+ configurePaint(documentData, parentAlpha);
+
+ if (lottieDrawable.useTextGlyphs()) {
+ drawTextWithGlyphs(documentData, parentMatrix, font, canvas);
+ } else {
+ drawTextWithFont(documentData, font, canvas);
+ }
+ canvas.restore();
+ }
+
+ private void configurePaint(DocumentData documentData, int parentAlpha) {
if (colorCallbackAnimation != null) {
fillPaint.setColor(colorCallbackAnimation.getValue());
} else if (colorAnimation != null) {
@@ -144,7 +157,7 @@ public class TextLayer extends BaseLayer {
strokePaint.setColor(documentData.strokeColor);
}
int opacity = transform.getOpacity() == null ? 100 : transform.getOpacity().getValue();
- int alpha = opacity * 255 / 100;
+ int alpha = opacity * 255 / 100 * parentAlpha / 255;
fillPaint.setAlpha(alpha);
strokePaint.setAlpha(alpha);
@@ -153,20 +166,11 @@ public class TextLayer extends BaseLayer {
} else if (strokeWidthAnimation != null) {
strokePaint.setStrokeWidth(strokeWidthAnimation.getValue());
} else {
- float parentScale = Utils.getScale(parentMatrix);
- strokePaint.setStrokeWidth(documentData.strokeWidth * Utils.dpScale() * parentScale);
+ strokePaint.setStrokeWidth(documentData.strokeWidth * Utils.dpScale());
}
-
- if (lottieDrawable.useTextGlyphs()) {
- drawTextGlyphs(documentData, parentMatrix, font, canvas);
- } else {
- drawTextWithFont(documentData, font, canvas);
- }
-
- canvas.restore();
}
- private void drawTextGlyphs(
+ private void drawTextWithGlyphs(
DocumentData documentData, Matrix parentMatrix, Font font, Canvas canvas) {
float textSize;
if (textSizeCallbackAnimation != null) {
@@ -179,37 +183,38 @@ public class TextLayer extends BaseLayer {
String text = documentData.text;
- // Line height
- float lineHeight = documentData.lineHeight * Utils.dpScale();
-
// Split full text in multiple lines
List<String> textLines = getTextLines(text);
int textLineCount = textLines.size();
- for (int l = 0; l < textLineCount; l++) {
-
- String textLine = textLines.get(l);
- float textLineWidth = getTextLineWidthForGlyphs(textLine, font, fontScale, parentScale);
-
- canvas.save();
-
- // Apply horizontal justification
- applyJustification(documentData.justification, canvas, textLineWidth);
+ // Add tracking
+ float tracking = documentData.tracking / 10f;
+ if (trackingCallbackAnimation != null) {
+ tracking += trackingCallbackAnimation.getValue();
+ } else if (trackingAnimation != null) {
+ tracking += trackingAnimation.getValue();
+ }
+ int lineIndex = -1;
+ for (int i = 0; i < textLineCount; i++) {
+ String textLine = textLines.get(i);
+ float boxWidth = documentData.boxSize == null ? 0f : documentData.boxSize.x;
+ List<TextSubLine> lines = splitGlyphTextIntoLines(textLine, boxWidth, font, fontScale, tracking, true);
+ for (int j = 0; j < lines.size(); j++) {
+ TextSubLine line = lines.get(j);
+ lineIndex++;
- // Center text vertically
- float multilineTranslateY = (textLineCount - 1) * lineHeight / 2;
- float translateY = l * lineHeight - multilineTranslateY;
- canvas.translate(0, translateY);
+ canvas.save();
- // Draw each line
- drawGlyphTextLine(textLine, documentData, parentMatrix, font, canvas, parentScale, fontScale);
+ if (offsetCanvas(canvas, documentData, lineIndex, line.width)) {
+ drawGlyphTextLine(line.text, documentData, font, canvas, parentScale, fontScale, tracking);
+ }
- // Reset canvas
- canvas.restore();
+ canvas.restore();
+ }
}
}
- private void drawGlyphTextLine(String text, DocumentData documentData, Matrix parentMatrix,
- Font font, Canvas canvas, float parentScale, float fontScale) {
+ private void drawGlyphTextLine(String text, DocumentData documentData,
+ Font font, Canvas canvas, float parentScale, float fontScale, float tracking) {
for (int i = 0; i < text.length(); i++) {
char c = text.charAt(i);
int characterHash = FontCharacter.hashFor(c, font.getFamily(), font.getStyle());
@@ -218,16 +223,8 @@ public class TextLayer extends BaseLayer {
// Something is wrong. Potentially, they didn't export the text as a glyph.
continue;
}
- drawCharacterAsGlyph(character, parentMatrix, fontScale, documentData, canvas);
- float tx = (float) character.getWidth() * fontScale * Utils.dpScale() * parentScale;
- // Add tracking
- float tracking = documentData.tracking / 10f;
- if (trackingCallbackAnimation != null) {
- tracking += trackingCallbackAnimation.getValue();
- } else if (trackingAnimation != null) {
- tracking += trackingAnimation.getValue();
- }
- tx += tracking * parentScale;
+ drawCharacterAsGlyph(character, fontScale, documentData, canvas);
+ float tx = (float) character.getWidth() * fontScale * Utils.dpScale() + tracking;
canvas.translate(tx, 0);
}
}
@@ -253,9 +250,6 @@ public class TextLayer extends BaseLayer {
strokePaint.setTypeface(fillPaint.getTypeface());
strokePaint.setTextSize(fillPaint.getTextSize());
- // Line height
- float lineHeight = documentData.lineHeight * Utils.dpScale();
-
// Calculate tracking
float tracking = documentData.tracking / 10f;
if (trackingCallbackAnimation != null) {
@@ -268,28 +262,49 @@ public class TextLayer extends BaseLayer {
// Split full text in multiple lines
List<String> textLines = getTextLines(text);
int textLineCount = textLines.size();
- for (int l = 0; l < textLineCount; l++) {
-
- String textLine = textLines.get(l);
- // We have to manually add the tracking between characters as the strokePaint ignores it
- float textLineWidth = strokePaint.measureText(textLine) + (textLine.length() - 1) * tracking;
-
- canvas.save();
-
- // Apply horizontal justification
- applyJustification(documentData.justification, canvas, textLineWidth);
-
- // Center text vertically
- float multilineTranslateY = (textLineCount - 1) * lineHeight / 2;
- float translateY = l * lineHeight - multilineTranslateY;
- canvas.translate(0, translateY);
-
- // Draw each line
- drawFontTextLine(textLine, documentData, canvas, tracking);
+ int lineIndex = -1;
+ for (int i = 0; i < textLineCount; i++) {
+ String textLine = textLines.get(i);
+ float boxWidth = documentData.boxSize == null ? 0f : documentData.boxSize.x;
+ List<TextSubLine> lines = splitGlyphTextIntoLines(textLine, boxWidth, font, 0f, tracking, false);
+ for (int j = 0; j < lines.size(); j++) {
+ TextSubLine line = lines.get(j);
+ lineIndex++;
+
+ canvas.save();
+
+ if (offsetCanvas(canvas, documentData, lineIndex, line.width)) {
+ drawFontTextLine(line.text, documentData, canvas, tracking);
+ }
+
+ canvas.restore();
+ }
+ }
+ }
- // Reset canvas
- canvas.restore();
+ private boolean offsetCanvas(Canvas canvas, DocumentData documentData, int lineIndex, float lineWidth) {
+ PointF position = documentData.boxPosition;
+ PointF size = documentData.boxSize;
+ float dpScale = Utils.dpScale();
+ float lineStartY = position == null ? 0f : documentData.lineHeight * dpScale + position.y;
+ float lineOffset = (lineIndex * documentData.lineHeight * dpScale) + lineStartY;
+ if (lottieDrawable.getClipTextToBoundingBox() && size != null && position != null && lineOffset >= position.y + size.y + documentData.size) {
+ return false;
+ }
+ float lineStart = position == null ? 0f : position.x;
+ float boxWidth = size == null ? 0f : size.x;
+ switch (documentData.justification) {
+ case LEFT_ALIGN:
+ canvas.translate(lineStart, lineOffset);
+ break;
+ case RIGHT_ALIGN:
+ canvas.translate(lineStart + boxWidth - lineWidth, lineOffset);
+ break;
+ case CENTER:
+ canvas.translate(lineStart + boxWidth / 2f - lineWidth / 2f, lineOffset);
+ break;
}
+ return true;
}
@Nullable
@@ -300,7 +315,7 @@ public class TextLayer extends BaseLayer {
return callbackTypeface;
}
}
- Typeface drawableTypeface = lottieDrawable.getTypeface(font.getFamily(), font.getStyle());
+ Typeface drawableTypeface = lottieDrawable.getTypeface(font);
if (drawableTypeface != null) {
return drawableTypeface;
}
@@ -310,6 +325,7 @@ public class TextLayer extends BaseLayer {
private List<String> getTextLines(String text) {
// Split full text by carriage return character
String formattedText = text.replaceAll("\r\n", "\r")
+ .replaceAll("\u0003", "\r")
.replaceAll("\n", "\r");
String[] textLinesArray = formattedText.split("\r");
return Arrays.asList(textLinesArray);
@@ -326,38 +342,92 @@ public class TextLayer extends BaseLayer {
}
}
- private float getTextLineWidthForGlyphs(
- String textLine, Font font, float fontScale, float parentScale) {
- float textLineWidth = 0;
+ private List<TextSubLine> splitGlyphTextIntoLines(String textLine, float boxWidth, Font font, float fontScale, float tracking,
+ boolean usingGlyphs) {
+ int lineCount = 0;
+
+ float currentLineWidth = 0;
+ int currentLineStartIndex = 0;
+
+ int currentWordStartIndex = 0;
+ float currentWordWidth = 0f;
+ boolean nextCharacterStartsWord = false;
+
+ // The measured size of a space.
+ float spaceWidth = 0f;
+
for (int i = 0; i < textLine.length(); i++) {
char c = textLine.charAt(i);
- int characterHash = FontCharacter.hashFor(c, font.getFamily(), font.getStyle());
- FontCharacter character = composition.getCharacters().get(characterHash);
- if (character == null) {
- continue;
+ float currentCharWidth;
+ if (usingGlyphs) {
+ int characterHash = FontCharacter.hashFor(c, font.getFamily(), font.getStyle());
+ FontCharacter character = composition.getCharacters().get(characterHash);
+ if (character == null) {
+ continue;
+ }
+ currentCharWidth = (float) character.getWidth() * fontScale * Utils.dpScale() + tracking;
+ } else {
+ currentCharWidth = fillPaint.measureText(textLine.substring(i, i + 1)) + tracking;
+ }
+
+ if (c == ' ') {
+ spaceWidth = currentCharWidth;
+ nextCharacterStartsWord = true;
+ } else if (nextCharacterStartsWord) {
+ nextCharacterStartsWord = false;
+ currentWordStartIndex = i;
+ currentWordWidth = currentCharWidth;
+ } else {
+ currentWordWidth += currentCharWidth;
}
- textLineWidth += character.getWidth() * fontScale * Utils.dpScale() * parentScale;
+ currentLineWidth += currentCharWidth;
+
+ if (boxWidth > 0f && currentLineWidth >= boxWidth) {
+ if (c == ' ') {
+ // Spaces at the end of a line don't do anything. Ignore it.
+ // The next non-space character will hit the conditions below.
+ continue;
+ }
+ TextSubLine subLine = ensureEnoughSubLines(++lineCount);
+ if (currentWordStartIndex == currentLineStartIndex) {
+ // Only word on line is wider than box, start wrapping mid-word.
+ String substr = textLine.substring(currentLineStartIndex, i);
+ String trimmed = substr.trim();
+ float trimmedSpace = (trimmed.length() - substr.length()) * spaceWidth;
+ subLine.set(trimmed, currentLineWidth - currentCharWidth - trimmedSpace);
+ currentLineStartIndex = i;
+ currentLineWidth = currentCharWidth;
+ currentWordStartIndex = currentLineStartIndex;
+ currentWordWidth = currentCharWidth;
+ } else {
+ String substr = textLine.substring(currentLineStartIndex, currentWordStartIndex - 1);
+ String trimmed = substr.trim();
+ float trimmedSpace = (substr.length() - trimmed.length()) * spaceWidth;
+ subLine.set(trimmed, currentLineWidth - currentWordWidth - trimmedSpace - spaceWidth);
+ currentLineStartIndex = currentWordStartIndex;
+ currentLineWidth = currentWordWidth;
+ }
+ }
+ }
+ if (currentLineWidth > 0f) {
+ TextSubLine line = ensureEnoughSubLines(++lineCount);
+ line.set(textLine.substring(currentLineStartIndex), currentLineWidth);
}
- return textLineWidth;
+ return textSubLines.subList(0, lineCount);
}
- private void applyJustification(Justification justification, Canvas canvas, float textLineWidth) {
- switch (justification) {
- case LEFT_ALIGN:
- // Do nothing. Default is left aligned.
- break;
- case RIGHT_ALIGN:
- canvas.translate(-textLineWidth, 0);
- break;
- case CENTER:
- canvas.translate(-textLineWidth / 2, 0);
- break;
+ /**
+ * Elements are reused and not deleted to save allocations.
+ */
+ private TextSubLine ensureEnoughSubLines(int numLines) {
+ for (int i = textSubLines.size(); i < numLines; i++) {
+ textSubLines.add(new TextSubLine());
}
+ return textSubLines.get(numLines - 1);
}
private void drawCharacterAsGlyph(
FontCharacter character,
- Matrix parentMatrix,
float fontScale,
DocumentData documentData,
Canvas canvas) {
@@ -365,7 +435,7 @@ public class TextLayer extends BaseLayer {
for (int j = 0; j < contentGroups.size(); j++) {
Path path = contentGroups.get(j).getPath();
path.computeBounds(rectF, false);
- matrix.set(parentMatrix);
+ matrix.reset();
matrix.preTranslate(0, -documentData.baselineShift * Utils.dpScale());
matrix.preScale(fontScale, fontScale);
path.transform(matrix);
@@ -418,7 +488,7 @@ public class TextLayer extends BaseLayer {
List<ContentGroup> contents = new ArrayList<>(size);
for (int i = 0; i < size; i++) {
ShapeGroup sg = shapes.get(i);
- contents.add(new ContentGroup(lottieDrawable, this, sg));
+ contents.add(new ContentGroup(lottieDrawable, this, sg, composition));
}
contentsForCharacter.put(character, contents);
return contents;
@@ -543,4 +613,15 @@ public class TextLayer extends BaseLayer {
textAnimation.setStringValueCallback((LottieValueCallback<String>) callback);
}
}
+
+ private static class TextSubLine {
+
+ private String text = "";
+ private float width = 0f;
+
+ void set(String text, float width) {
+ this.text = text;
+ this.width = width;
+ }
+ }
}
diff --git a/lottie/src/main/java/com/airbnb/lottie/network/FileExtension.java b/lottie/src/main/java/com/airbnb/lottie/network/FileExtension.java
index 2c352708..7a505626 100644
--- a/lottie/src/main/java/com/airbnb/lottie/network/FileExtension.java
+++ b/lottie/src/main/java/com/airbnb/lottie/network/FileExtension.java
@@ -8,7 +8,8 @@ import androidx.annotation.RestrictTo;
@RestrictTo(RestrictTo.Scope.LIBRARY)
public enum FileExtension {
JSON(".json"),
- ZIP(".zip");
+ ZIP(".zip"),
+ GZIP(".gz");
public final String extension;
diff --git a/lottie/src/main/java/com/airbnb/lottie/network/NetworkCache.java b/lottie/src/main/java/com/airbnb/lottie/network/NetworkCache.java
index 6b59dbca..65e9a02d 100644
--- a/lottie/src/main/java/com/airbnb/lottie/network/NetworkCache.java
+++ b/lottie/src/main/java/com/airbnb/lottie/network/NetworkCache.java
@@ -17,6 +17,8 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
/**
* Helper class to save and restore animations fetched from an URL to the app disk cache.
@@ -36,7 +38,7 @@ public class NetworkCache {
if (parentDir.exists()) {
File[] files = parentDir.listFiles();
if (files != null && files.length > 0) {
- for (File file : parentDir.listFiles()) {
+ for (File file : files) {
file.delete();
}
}
@@ -74,6 +76,8 @@ public class NetworkCache {
FileExtension extension;
if (cachedFile.getAbsolutePath().endsWith(".zip")) {
extension = FileExtension.ZIP;
+ } else if (cachedFile.getAbsolutePath().endsWith(".gz")) {
+ extension = FileExtension.GZIP;
} else {
extension = FileExtension.JSON;
}
@@ -141,6 +145,10 @@ public class NetworkCache {
if (zipFile.exists()) {
return zipFile;
}
+ File gzipFile = new File(parentDir(), filenameForUrl(url, FileExtension.GZIP, false));
+ if (gzipFile.exists()) {
+ return gzipFile;
+ }
return null;
}
@@ -156,6 +164,42 @@ public class NetworkCache {
}
private static String filenameForUrl(String url, FileExtension extension, boolean isTemp) {
- return "lottie_cache_" + url.replaceAll("\\W+", "") + (isTemp ? extension.tempExtension() : extension.extension);
+ String prefix = "lottie_cache_";
+ String suffix = (isTemp ? extension.tempExtension() : extension.extension);
+ String sanitizedUrl = url.replaceAll("\\W+", "");
+ // The max filename on Android is 255 chars.
+ int maxUrlLength = 255 - prefix.length() - suffix.length();
+ if (sanitizedUrl.length() > maxUrlLength) {
+ // If the url is too long, use md5 as the cache key instead.
+ // md5 is preferable to substring because it is impossible to know
+ // which parts of the url are significant. If it is the end chars
+ // then substring could cause multiple animations to use the same
+ // cache key.
+ // md5 is probably better for everything but:
+ // 1. It is slower and unnecessary in most cases.
+ // 2. Upon upgrading, if the cache key algorithm changes,
+ // all old cached animations will get orphaned.
+ sanitizedUrl = getMD5(sanitizedUrl, maxUrlLength);
+ }
+
+ return prefix + sanitizedUrl + suffix;
+ }
+
+ private static String getMD5(String input, int maxLength) {
+ MessageDigest md;
+ try {
+ md = MessageDigest.getInstance("MD5");
+ } catch (NoSuchAlgorithmException e) {
+ // For some reason, md5 doesn't exist, return a substring.
+ // This should never happen.
+ return input.substring(0, maxLength);
+ }
+ byte[] messageDigest = md.digest(input.getBytes());
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < messageDigest.length; i++) {
+ byte b = messageDigest[i];
+ sb.append(String.format("%02x", b));
+ }
+ return sb.toString();
}
}
diff --git a/lottie/src/main/java/com/airbnb/lottie/network/NetworkFetcher.java b/lottie/src/main/java/com/airbnb/lottie/network/NetworkFetcher.java
index c98caaaa..9fa2c7af 100644
--- a/lottie/src/main/java/com/airbnb/lottie/network/NetworkFetcher.java
+++ b/lottie/src/main/java/com/airbnb/lottie/network/NetworkFetcher.java
@@ -1,5 +1,6 @@
package com.airbnb.lottie.network;
+import android.content.Context;
import android.util.Pair;
import androidx.annotation.NonNull;
@@ -16,38 +17,39 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.util.zip.GZIPInputStream;
import java.util.zip.ZipInputStream;
@RestrictTo(RestrictTo.Scope.LIBRARY)
public class NetworkFetcher {
- @NonNull
+ @Nullable
private final NetworkCache networkCache;
@NonNull
private final LottieNetworkFetcher fetcher;
- public NetworkFetcher(@NonNull NetworkCache networkCache, @NonNull LottieNetworkFetcher fetcher) {
+ public NetworkFetcher(@Nullable NetworkCache networkCache, @NonNull LottieNetworkFetcher fetcher) {
this.networkCache = networkCache;
this.fetcher = fetcher;
}
@NonNull
@WorkerThread
- public LottieResult<LottieComposition> fetchSync(@NonNull String url, @Nullable String cacheKey) {
- LottieComposition result = fetchFromCache(url, cacheKey);
+ public LottieResult<LottieComposition> fetchSync(Context context, @NonNull String url, @Nullable String cacheKey) {
+ LottieComposition result = fetchFromCache(context, url, cacheKey);
if (result != null) {
return new LottieResult<>(result);
}
Logger.debug("Animation for " + url + " not found in cache. Fetching from network.");
- return fetchFromNetwork(url, cacheKey);
+ return fetchFromNetwork(context, url, cacheKey);
}
@Nullable
@WorkerThread
- private LottieComposition fetchFromCache(@NonNull String url, @Nullable String cacheKey) {
- if (cacheKey == null) {
+ private LottieComposition fetchFromCache(Context context, @NonNull String url, @Nullable String cacheKey) {
+ if (cacheKey == null || networkCache == null) {
return null;
}
Pair<FileExtension, InputStream> cacheResult = networkCache.fetch(url);
@@ -58,10 +60,19 @@ public class NetworkFetcher {
FileExtension extension = cacheResult.first;
InputStream inputStream = cacheResult.second;
LottieResult<LottieComposition> result;
- if (extension == FileExtension.ZIP) {
- result = LottieCompositionFactory.fromZipStreamSync(new ZipInputStream(inputStream), url);
- } else {
- result = LottieCompositionFactory.fromJsonInputStreamSync(inputStream, url);
+ switch (extension) {
+ case ZIP:
+ result = LottieCompositionFactory.fromZipStreamSync(context, new ZipInputStream(inputStream), cacheKey);
+ break;
+ case GZIP:
+ try {
+ result = LottieCompositionFactory.fromJsonInputStreamSync(new GZIPInputStream(inputStream), cacheKey);
+ } catch (IOException e) {
+ result = new LottieResult<>(e);
+ }
+ break;
+ default:
+ result = LottieCompositionFactory.fromJsonInputStreamSync(inputStream, cacheKey);
}
if (result.getValue() != null) {
return result.getValue();
@@ -71,7 +82,7 @@ public class NetworkFetcher {
@NonNull
@WorkerThread
- private LottieResult<LottieComposition> fetchFromNetwork(@NonNull String url, @Nullable String cacheKey) {
+ private LottieResult<LottieComposition> fetchFromNetwork(Context context, @NonNull String url, @Nullable String cacheKey) {
Logger.debug("Fetching " + url);
LottieFetchResult fetchResult = null;
@@ -80,7 +91,7 @@ public class NetworkFetcher {
if (fetchResult.isSuccessful()) {
InputStream inputStream = fetchResult.bodyByteStream();
String contentType = fetchResult.contentType();
- LottieResult<LottieComposition> result = fromInputStream(url, inputStream, contentType, cacheKey);
+ LottieResult<LottieComposition> result = fromInputStream(context, url, inputStream, contentType, cacheKey);
Logger.debug("Completed fetch from network. Success: " + (result.getValue() != null));
return result;
} else {
@@ -100,7 +111,7 @@ public class NetworkFetcher {
}
@NonNull
- private LottieResult<LottieComposition> fromInputStream(@NonNull String url, @NonNull InputStream inputStream, @Nullable String contentType,
+ private LottieResult<LottieComposition> fromInputStream(Context context, @NonNull String url, @NonNull InputStream inputStream, @Nullable String contentType,
@Nullable String cacheKey) throws IOException {
FileExtension extension;
LottieResult<LottieComposition> result;
@@ -115,14 +126,20 @@ public class NetworkFetcher {
url.split("\\?")[0].endsWith(".lottie")) {
Logger.debug("Handling zip response.");
extension = FileExtension.ZIP;
- result = fromZipStream(url, inputStream, cacheKey);
+ result = fromZipStream(context, url, inputStream, cacheKey);
+ } else if (contentType.contains("application/gzip") ||
+ contentType.contains("application/x-gzip") ||
+ url.split("\\?")[0].endsWith(".tgs")) {
+ Logger.debug("Handling gzip response.");
+ extension = FileExtension.GZIP;
+ result = fromGzipStream(url, inputStream, cacheKey);
} else {
Logger.debug("Received json response.");
extension = FileExtension.JSON;
result = fromJsonStream(url, inputStream, cacheKey);
}
- if (cacheKey != null && result.getValue() != null) {
+ if (cacheKey != null && result.getValue() != null && networkCache != null) {
networkCache.renameTempFile(url, extension);
}
@@ -130,19 +147,29 @@ public class NetworkFetcher {
}
@NonNull
- private LottieResult<LottieComposition> fromZipStream(@NonNull String url, @NonNull InputStream inputStream, @Nullable String cacheKey)
+ private LottieResult<LottieComposition> fromZipStream(Context context, @NonNull String url, @NonNull InputStream inputStream, @Nullable String cacheKey)
throws IOException {
- if (cacheKey == null) {
- return LottieCompositionFactory.fromZipStreamSync(new ZipInputStream(inputStream), null);
+ if (cacheKey == null || networkCache == null) {
+ return LottieCompositionFactory.fromZipStreamSync(context, new ZipInputStream(inputStream), null);
}
File file = networkCache.writeTempCacheFile(url, inputStream, FileExtension.ZIP);
- return LottieCompositionFactory.fromZipStreamSync(new ZipInputStream(new FileInputStream(file)), url);
+ return LottieCompositionFactory.fromZipStreamSync(context, new ZipInputStream(new FileInputStream(file)), url);
+ }
+
+ @NonNull
+ private LottieResult<LottieComposition> fromGzipStream(@NonNull String url, @NonNull InputStream inputStream, @Nullable String cacheKey)
+ throws IOException {
+ if (cacheKey == null || networkCache == null) {
+ return LottieCompositionFactory.fromJsonInputStreamSync(new GZIPInputStream(inputStream), null);
+ }
+ File file = networkCache.writeTempCacheFile(url, inputStream, FileExtension.GZIP);
+ return LottieCompositionFactory.fromJsonInputStreamSync(new GZIPInputStream(new FileInputStream(file)), url);
}
@NonNull
private LottieResult<LottieComposition> fromJsonStream(@NonNull String url, @NonNull InputStream inputStream, @Nullable String cacheKey)
throws IOException {
- if (cacheKey == null) {
+ if (cacheKey == null || networkCache == null) {
return LottieCompositionFactory.fromJsonInputStreamSync(inputStream, null);
}
File file = networkCache.writeTempCacheFile(url, inputStream, FileExtension.JSON);
diff --git a/lottie/src/main/java/com/airbnb/lottie/parser/AnimatableValueParser.java b/lottie/src/main/java/com/airbnb/lottie/parser/AnimatableValueParser.java
index 01ea02cb..f4b9eaa8 100644
--- a/lottie/src/main/java/com/airbnb/lottie/parser/AnimatableValueParser.java
+++ b/lottie/src/main/java/com/airbnb/lottie/parser/AnimatableValueParser.java
@@ -55,7 +55,7 @@ public class AnimatableValueParser {
static AnimatableTextFrame parseDocumentData(
JsonReader reader, LottieComposition composition) throws IOException {
- return new AnimatableTextFrame(parse(reader, composition, DocumentDataParser.INSTANCE));
+ return new AnimatableTextFrame(parse(reader, Utils.dpScale(), composition, DocumentDataParser.INSTANCE));
}
static AnimatableColorValue parseColor(
@@ -65,8 +65,9 @@ public class AnimatableValueParser {
static AnimatableGradientColorValue parseGradientColor(
JsonReader reader, LottieComposition composition, int points) throws IOException {
- return new AnimatableGradientColorValue(
+ AnimatableGradientColorValue animatableGradientColorValue = new AnimatableGradientColorValue(
parse(reader, composition, new GradientColorParser(points)));
+ return animatableGradientColorValue;
}
/**
diff --git a/lottie/src/main/java/com/airbnb/lottie/parser/DocumentDataParser.java b/lottie/src/main/java/com/airbnb/lottie/parser/DocumentDataParser.java
index dfbffbdd..c659d11d 100644
--- a/lottie/src/main/java/com/airbnb/lottie/parser/DocumentDataParser.java
+++ b/lottie/src/main/java/com/airbnb/lottie/parser/DocumentDataParser.java
@@ -1,6 +1,8 @@
package com.airbnb.lottie.parser;
+import android.graphics.PointF;
+
import com.airbnb.lottie.model.DocumentData;
import com.airbnb.lottie.model.DocumentData.Justification;
import com.airbnb.lottie.parser.moshi.JsonReader;
@@ -20,7 +22,9 @@ public class DocumentDataParser implements ValueParser<DocumentData> {
"fc", // 7
"sc", // 8
"sw", // 9
- "of" // 10
+ "of", // 10
+ "ps", // 11
+ "sz" // 12
);
private DocumentDataParser() {
@@ -39,6 +43,8 @@ public class DocumentDataParser implements ValueParser<DocumentData> {
int strokeColor = 0;
float strokeWidth = 0f;
boolean strokeOverFill = true;
+ PointF boxPosition = null;
+ PointF boxSize = null;
reader.beginObject();
while (reader.hasNext()) {
@@ -81,6 +87,16 @@ public class DocumentDataParser implements ValueParser<DocumentData> {
case 10:
strokeOverFill = reader.nextBoolean();
break;
+ case 11:
+ reader.beginArray();
+ boxPosition = new PointF((float) reader.nextDouble() * scale, (float) reader.nextDouble() * scale);
+ reader.endArray();
+ break;
+ case 12:
+ reader.beginArray();
+ boxSize = new PointF((float) reader.nextDouble() * scale, (float) reader.nextDouble() * scale);
+ reader.endArray();
+ break;
default:
reader.skipName();
reader.skipValue();
@@ -89,6 +105,6 @@ public class DocumentDataParser implements ValueParser<DocumentData> {
reader.endObject();
return new DocumentData(text, fontName, size, justification, tracking, lineHeight,
- baselineShift, fillColor, strokeColor, strokeWidth, strokeOverFill);
+ baselineShift, fillColor, strokeColor, strokeWidth, strokeOverFill, boxPosition, boxSize);
}
}
diff --git a/lottie/src/main/java/com/airbnb/lottie/parser/GradientColorParser.java b/lottie/src/main/java/com/airbnb/lottie/parser/GradientColorParser.java
index 50a52e3f..5d779790 100644
--- a/lottie/src/main/java/com/airbnb/lottie/parser/GradientColorParser.java
+++ b/lottie/src/main/java/com/airbnb/lottie/parser/GradientColorParser.java
@@ -4,6 +4,7 @@ import android.graphics.Color;
import com.airbnb.lottie.model.content.GradientColor;
import com.airbnb.lottie.parser.moshi.JsonReader;
+import com.airbnb.lottie.utils.GammaEvaluator;
import com.airbnb.lottie.utils.MiscUtils;
import java.io.IOException;
@@ -167,7 +168,7 @@ public class GradientColorParser implements com.airbnb.lottie.parser.ValueParser
return new GradientColor(newPositions, newColors);
}
- private int getColorInBetweenColorStops(float position, float opacity, float[] colorStopPositions, int[] colorStopColors) {
+ int getColorInBetweenColorStops(float position, float opacity, float[] colorStopPositions, int[] colorStopColors) {
if (colorStopColors.length < 2 || position == colorStopPositions[0]) {
return colorStopColors[0];
}
@@ -176,16 +177,28 @@ public class GradientColorParser implements com.airbnb.lottie.parser.ValueParser
if (colorStopPosition < position && i != colorStopPositions.length - 1) {
continue;
}
+ if (i == colorStopPositions.length - 1 && position >= colorStopPosition) {
+ return Color.argb(
+ (int) (opacity * 255),
+ Color.red(colorStopColors[i]),
+ Color.green(colorStopColors[i]),
+ Color.blue(colorStopColors[i])
+ );
+ }
// We found the position in which position is between i - 1 and i.
float distanceBetweenColors = colorStopPositions[i] - colorStopPositions[i - 1];
float distanceToLowerColor = position - colorStopPositions[i - 1];
float percentage = distanceToLowerColor / distanceBetweenColors;
+
int upperColor = colorStopColors[i];
int lowerColor = colorStopColors[i - 1];
+ int intermediateColor = GammaEvaluator.evaluate(percentage, lowerColor, upperColor);
+
int a = (int) (opacity * 255);
- int r = MiscUtils.lerp(Color.red(lowerColor), Color.red(upperColor), percentage);
- int g = MiscUtils.lerp(Color.green(lowerColor), Color.green(upperColor), percentage);
- int b = MiscUtils.lerp(Color.blue(lowerColor), Color.blue(upperColor), percentage);
+ int r = Color.red(intermediateColor);
+ int g = Color.green(intermediateColor);
+ int b = Color.blue(intermediateColor);
+
return Color.argb(a, r, g, b);
}
throw new IllegalArgumentException("Unreachable code.");
@@ -260,7 +273,6 @@ public class GradientColorParser implements com.airbnb.lottie.parser.ValueParser
return mergedNotTruncated;
}
-
return Arrays.copyOf(mergedNotTruncated, mergedNotTruncated.length - numDuplicates);
}
-} \ No newline at end of file
+}
diff --git a/lottie/src/main/java/com/airbnb/lottie/parser/KeyframeParser.java b/lottie/src/main/java/com/airbnb/lottie/parser/KeyframeParser.java
index 4f0bd87a..219b5545 100644
--- a/lottie/src/main/java/com/airbnb/lottie/parser/KeyframeParser.java
+++ b/lottie/src/main/java/com/airbnb/lottie/parser/KeyframeParser.java
@@ -8,6 +8,8 @@ import androidx.annotation.Nullable;
import androidx.collection.SparseArrayCompat;
import androidx.core.view.animation.PathInterpolatorCompat;
+import com.airbnb.lottie.L;
+import com.airbnb.lottie.Lottie;
import com.airbnb.lottie.LottieComposition;
import com.airbnb.lottie.parser.moshi.JsonReader;
import com.airbnb.lottie.utils.MiscUtils;
@@ -332,7 +334,7 @@ class KeyframeParser {
cp2.x = MiscUtils.clamp(cp2.x, -1f, 1f);
cp2.y = MiscUtils.clamp(cp2.y, -MAX_CP_VALUE, MAX_CP_VALUE);
int hash = Utils.hashFor(cp1.x, cp1.y, cp2.x, cp2.y);
- WeakReference<Interpolator> interpolatorRef = getInterpolator(hash);
+ WeakReference<Interpolator> interpolatorRef = L.getDisablePathInterpolatorCache() ? null : getInterpolator(hash);
if (interpolatorRef != null) {
interpolator = interpolatorRef.get();
}
@@ -350,13 +352,15 @@ class KeyframeParser {
interpolator = new LinearInterpolator();
}
}
- try {
- putInterpolator(hash, new WeakReference<>(interpolator));
- } catch (ArrayIndexOutOfBoundsException e) {
- // It is not clear why but SparseArrayCompat sometimes fails with this:
- // https://github.com/airbnb/lottie-android/issues/452
- // Because this is not a critical operation, we can safely just ignore it.
- // I was unable to repro this to attempt a proper fix.
+ if (!L.getDisablePathInterpolatorCache()) {
+ try {
+ putInterpolator(hash, new WeakReference<>(interpolator));
+ } catch (ArrayIndexOutOfBoundsException e) {
+ // It is not clear why but SparseArrayCompat sometimes fails with this:
+ // https://github.com/airbnb/lottie-android/issues/452
+ // Because this is not a critical operation, we can safely just ignore it.
+ // I was unable to repro this to attempt a proper fix.
+ }
}
}
return interpolator;
diff --git a/lottie/src/main/java/com/airbnb/lottie/parser/LayerParser.java b/lottie/src/main/java/com/airbnb/lottie/parser/LayerParser.java
index 6881a026..c56e2139 100644
--- a/lottie/src/main/java/com/airbnb/lottie/parser/LayerParser.java
+++ b/lottie/src/main/java/com/airbnb/lottie/parser/LayerParser.java
@@ -8,6 +8,7 @@ import com.airbnb.lottie.model.animatable.AnimatableFloatValue;
import com.airbnb.lottie.model.animatable.AnimatableTextFrame;
import com.airbnb.lottie.model.animatable.AnimatableTextProperties;
import com.airbnb.lottie.model.animatable.AnimatableTransform;
+import com.airbnb.lottie.model.content.LBlendMode;
import com.airbnb.lottie.model.content.BlurEffect;
import com.airbnb.lottie.model.content.ContentModel;
import com.airbnb.lottie.model.content.Mask;
@@ -27,29 +28,31 @@ public class LayerParser {
}
private static final JsonReader.Options NAMES = JsonReader.Options.of(
- "nm", // 0
- "ind", // 1
- "refId", // 2
- "ty", // 3
+ "nm", // 0
+ "ind", // 1
+ "refId", // 2
+ "ty", // 3
"parent", // 4
- "sw", // 5
- "sh", // 6
- "sc", // 7
- "ks", // 8
- "tt", // 9
+ "sw", // 5
+ "sh", // 6
+ "sc", // 7
+ "ks", // 8
+ "tt", // 9
"masksProperties", // 10
"shapes", // 11
- "t", // 12
+ "t", // 12
"ef", // 13
"sr", // 14
"st", // 15
- "w", // 16
- "h", // 17
+ "w", // 16
+ "h", // 17
"ip", // 18
"op", // 19
"tm", // 20
"cl", // 21
- "hd" // 22
+ "hd", // 22
+ "ao", // 23
+ "bm" // 24
);
public static Layer parse(LottieComposition composition) {
@@ -59,7 +62,8 @@ public class LayerParser {
Layer.LayerType.PRE_COMP, -1, null, Collections.<Mask>emptyList(),
new AnimatableTransform(), 0, 0, 0, 0, 0,
bounds.width(), bounds.height(), null, null, Collections.<Keyframe<Float>>emptyList(),
- Layer.MatteType.NONE, null, false, null, null);
+ Layer.MatteType.NONE, null, false, null, null,
+ LBlendMode.NORMAL);
}
private static final JsonReader.Options TEXT_NAMES = JsonReader.Options.of(
@@ -82,8 +86,8 @@ public class LayerParser {
int solidWidth = 0;
int solidHeight = 0;
int solidColor = 0;
- int preCompWidth = 0;
- int preCompHeight = 0;
+ float preCompWidth = 0;
+ float preCompHeight = 0;
long parentId = -1;
float timeStretch = 1f;
float startFrame = 0f;
@@ -93,8 +97,10 @@ public class LayerParser {
boolean hidden = false;
BlurEffect blurEffect = null;
DropShadowEffect dropShadowEffect = null;
+ boolean autoOrient = false;
Layer.MatteType matteType = Layer.MatteType.NONE;
+ LBlendMode blendMode = LBlendMode.NORMAL;
AnimatableTransform transform = null;
AnimatableTextFrame text = null;
AnimatableTextProperties textProperties = null;
@@ -236,10 +242,10 @@ public class LayerParser {
startFrame = (float) reader.nextDouble();
break;
case 16:
- preCompWidth = (int) (reader.nextInt() * Utils.dpScale());
+ preCompWidth = (float) (reader.nextDouble() * Utils.dpScale());
break;
case 17:
- preCompHeight = (int) (reader.nextInt() * Utils.dpScale());
+ preCompHeight = (float) (reader.nextDouble() * Utils.dpScale());
break;
case 18:
inFrame = (float) reader.nextDouble();
@@ -256,6 +262,18 @@ public class LayerParser {
case 22:
hidden = reader.nextBoolean();
break;
+ case 23:
+ autoOrient = reader.nextInt() == 1;
+ break;
+ case 24:
+ int blendModeIndex = reader.nextInt();
+ if (blendModeIndex >= LBlendMode.values().length) {
+ composition.addWarning("Unsupported Blend Mode: " + blendModeIndex);
+ blendMode = LBlendMode.NORMAL;
+ break;
+ }
+ blendMode = LBlendMode.values()[blendModeIndex];
+ break;
default:
reader.skipName();
reader.skipValue();
@@ -284,9 +302,15 @@ public class LayerParser {
composition.addWarning("Convert your Illustrator layers to shape layers.");
}
+ if (autoOrient) {
+ if (transform == null) {
+ transform = new AnimatableTransform();
+ }
+ transform.setAutoOrient(autoOrient);
+ }
return new Layer(shapes, composition, layerName, layerId, layerType, parentId, refId,
masks, transform, solidWidth, solidHeight, solidColor, timeStretch, startFrame,
preCompWidth, preCompHeight, text, textProperties, inOutKeyframes, matteType,
- timeRemapping, hidden, blurEffect, dropShadowEffect);
+ timeRemapping, hidden, blurEffect, dropShadowEffect, blendMode);
}
}
diff --git a/lottie/src/main/java/com/airbnb/lottie/parser/LottieCompositionMoshiParser.java b/lottie/src/main/java/com/airbnb/lottie/parser/LottieCompositionMoshiParser.java
index 4950753f..ee972df6 100644
--- a/lottie/src/main/java/com/airbnb/lottie/parser/LottieCompositionMoshiParser.java
+++ b/lottie/src/main/java/com/airbnb/lottie/parser/LottieCompositionMoshiParser.java
@@ -107,7 +107,7 @@ public class LottieCompositionMoshiParser {
Rect bounds = new Rect(0, 0, scaledWidth, scaledHeight);
composition.init(bounds, startFrame, endFrame, frameRate, layers, layerMap, precomps,
- images, characters, fonts, markers);
+ images, Utils.dpScale(), characters, fonts, markers);
return composition;
}
diff --git a/lottie/src/main/java/com/airbnb/lottie/parser/ShapeStrokeParser.java b/lottie/src/main/java/com/airbnb/lottie/parser/ShapeStrokeParser.java
index 0cd0ce0b..33f46a0a 100644
--- a/lottie/src/main/java/com/airbnb/lottie/parser/ShapeStrokeParser.java
+++ b/lottie/src/main/java/com/airbnb/lottie/parser/ShapeStrokeParser.java
@@ -123,6 +123,10 @@ class ShapeStrokeParser {
// Telegram sometimes omits opacity.
// https://github.com/airbnb/lottie-android/issues/1600
opacity = opacity == null ? new AnimatableIntegerValue(Collections.singletonList(new Keyframe<>(100))) : opacity;
+ // Unclear why these are omitted sometimes but default to After Effects default value
+ // https://github.com/airbnb/lottie-android/issues/2325
+ capType = capType == null ? ShapeStroke.LineCapType.BUTT : capType;
+ joinType = joinType == null ? ShapeStroke.LineJoinType.MITER : joinType;
return new ShapeStroke(
name, offset, lineDashPattern, color, opacity, width, capType, joinType, miterLimit, hidden);
}
diff --git a/lottie/src/main/java/com/airbnb/lottie/utils/BaseLottieAnimator.java b/lottie/src/main/java/com/airbnb/lottie/utils/BaseLottieAnimator.java
index 8a7da29e..619ddc25 100644
--- a/lottie/src/main/java/com/airbnb/lottie/utils/BaseLottieAnimator.java
+++ b/lottie/src/main/java/com/airbnb/lottie/utils/BaseLottieAnimator.java
@@ -11,6 +11,8 @@ import java.util.concurrent.CopyOnWriteArraySet;
public abstract class BaseLottieAnimator extends ValueAnimator {
private final Set<ValueAnimator.AnimatorUpdateListener> updateListeners = new CopyOnWriteArraySet<>();
private final Set<AnimatorListener> listeners = new CopyOnWriteArraySet<>();
+ private final Set<Animator.AnimatorPauseListener> pauseListeners = new CopyOnWriteArraySet<>();
+
@Override public long getStartDelay() {
throw new UnsupportedOperationException("LottieAnimator does not support getStartDelay.");
@@ -62,6 +64,14 @@ public abstract class BaseLottieAnimator extends ValueAnimator {
}
}
+ @Override public void addPauseListener(AnimatorPauseListener listener) {
+ pauseListeners.add(listener);
+ }
+
+ @Override public void removePauseListener(AnimatorPauseListener listener) {
+ pauseListeners.remove(listener);
+ }
+
void notifyRepeat() {
for (Animator.AnimatorListener listener : listeners) {
listener.onAnimationRepeat(this);
@@ -89,4 +99,20 @@ public abstract class BaseLottieAnimator extends ValueAnimator {
listener.onAnimationUpdate(this);
}
}
+
+ void notifyPause() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+ for (AnimatorPauseListener pauseListener : pauseListeners) {
+ pauseListener.onAnimationPause(this);
+ }
+ }
+ }
+
+ void notifyResume() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+ for (AnimatorPauseListener pauseListener : pauseListeners) {
+ pauseListener.onAnimationResume(this);
+ }
+ }
+ }
}
diff --git a/lottie/src/main/java/com/airbnb/lottie/utils/GammaEvaluator.java b/lottie/src/main/java/com/airbnb/lottie/utils/GammaEvaluator.java
index d509fcf0..3580d13d 100644
--- a/lottie/src/main/java/com/airbnb/lottie/utils/GammaEvaluator.java
+++ b/lottie/src/main/java/com/airbnb/lottie/utils/GammaEvaluator.java
@@ -25,9 +25,16 @@ public class GammaEvaluator {
}
public static int evaluate(float fraction, int startInt, int endInt) {
+ // Fast return in case start and end is the same
+ // or if fraction is at start/end or out of [0,1] bounds
if (startInt == endInt) {
return startInt;
+ } else if (fraction <= 0f) {
+ return startInt;
+ } else if (fraction >= 1f) {
+ return endInt;
}
+
float startA = ((startInt >> 24) & 0xff) / 255.0f;
float startR = ((startInt >> 16) & 0xff) / 255.0f;
float startG = ((startInt >> 8) & 0xff) / 255.0f;
@@ -61,4 +68,4 @@ public class GammaEvaluator {
return Math.round(a) << 24 | Math.round(r) << 16 | Math.round(g) << 8 | Math.round(b);
}
-} \ No newline at end of file
+}
diff --git a/lottie/src/main/java/com/airbnb/lottie/utils/LottieThreadFactory.java b/lottie/src/main/java/com/airbnb/lottie/utils/LottieThreadFactory.java
new file mode 100644
index 00000000..4d59f5ac
--- /dev/null
+++ b/lottie/src/main/java/com/airbnb/lottie/utils/LottieThreadFactory.java
@@ -0,0 +1,27 @@
+package com.airbnb.lottie.utils;
+
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class LottieThreadFactory implements ThreadFactory {
+ private static final AtomicInteger poolNumber = new AtomicInteger(1);
+ private final ThreadGroup group;
+ private final AtomicInteger threadNumber = new AtomicInteger(1);
+ private final String namePrefix;
+
+ public LottieThreadFactory() {
+ SecurityManager s = System.getSecurityManager();
+ group = (s == null) ? Thread.currentThread().getThreadGroup() : s.getThreadGroup();
+ namePrefix = "lottie-" + poolNumber.getAndIncrement() + "-thread-";
+ }
+
+ public Thread newThread(Runnable r) {
+ Thread t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0);
+ // Don't prevent this thread from letting Android kill the app process if it wants to.
+ t.setDaemon(false);
+ // This will block the main thread if it isn't high enough priority
+ // so this thread should be as close to the main thread priority as possible.
+ t.setPriority(Thread.MAX_PRIORITY);
+ return t;
+ }
+} \ No newline at end of file
diff --git a/lottie/src/main/java/com/airbnb/lottie/utils/LottieTrace.java b/lottie/src/main/java/com/airbnb/lottie/utils/LottieTrace.java
new file mode 100644
index 00000000..3223e781
--- /dev/null
+++ b/lottie/src/main/java/com/airbnb/lottie/utils/LottieTrace.java
@@ -0,0 +1,42 @@
+package com.airbnb.lottie.utils;
+
+import androidx.core.os.TraceCompat;
+
+public class LottieTrace {
+ private static final int MAX_DEPTH = 5;
+
+ private final String[] sections = new String[MAX_DEPTH];
+ private final long[] startTimeNs = new long[MAX_DEPTH];
+ private int traceDepth = 0;
+ private int depthPastMaxDepth = 0;
+
+ public void beginSection(String section) {
+ if (traceDepth == MAX_DEPTH) {
+ depthPastMaxDepth++;
+ return;
+ }
+ sections[traceDepth] = section;
+ startTimeNs[traceDepth] = System.nanoTime();
+ //noinspection deprecation
+ TraceCompat.beginSection(section);
+ traceDepth++;
+ }
+
+ public float endSection(String section) {
+ if (depthPastMaxDepth > 0) {
+ depthPastMaxDepth--;
+ return 0;
+ }
+ traceDepth--;
+ if (traceDepth == -1) {
+ throw new IllegalStateException("Can't end trace section. There are none.");
+ }
+ if (!section.equals(sections[traceDepth])) {
+ throw new IllegalStateException("Unbalanced trace call " + section +
+ ". Expected " + sections[traceDepth] + ".");
+ }
+ //noinspection deprecation
+ TraceCompat.endSection();
+ return (System.nanoTime() - startTimeNs[traceDepth]) / 1000000f;
+ }
+}
diff --git a/lottie/src/main/java/com/airbnb/lottie/utils/LottieValueAnimator.java b/lottie/src/main/java/com/airbnb/lottie/utils/LottieValueAnimator.java
index a8901682..aa953a33 100644
--- a/lottie/src/main/java/com/airbnb/lottie/utils/LottieValueAnimator.java
+++ b/lottie/src/main/java/com/airbnb/lottie/utils/LottieValueAnimator.java
@@ -21,12 +21,14 @@ public class LottieValueAnimator extends BaseLottieAnimator implements Choreogra
private float speed = 1f;
private boolean speedReversedForRepeatMode = false;
private long lastFrameTimeNs = 0;
+ private float frameRaw = 0;
private float frame = 0;
private int repeatCount = 0;
private float minFrame = Integer.MIN_VALUE;
private float maxFrame = Integer.MAX_VALUE;
@Nullable private LottieComposition composition;
@VisibleForTesting protected boolean running = false;
+ private boolean useCompositionFrameRate = false;
public LottieValueAnimator() {
}
@@ -78,6 +80,10 @@ public class LottieValueAnimator extends BaseLottieAnimator implements Choreogra
return running;
}
+ public void setUseCompositionFrameRate(boolean useCompositionFrameRate) {
+ this.useCompositionFrameRate = useCompositionFrameRate;
+ }
+
@Override public void doFrame(long frameTimeNanos) {
postFrameCallback();
if (composition == null || !isRunning()) {
@@ -89,16 +95,21 @@ public class LottieValueAnimator extends BaseLottieAnimator implements Choreogra
float frameDuration = getFrameDurationNs();
float dFrames = timeSinceFrame / frameDuration;
- frame += isReversed() ? -dFrames : dFrames;
- boolean ended = !MiscUtils.contains(frame, getMinFrame(), getMaxFrame());
- frame = MiscUtils.clamp(frame, getMinFrame(), getMaxFrame());
+ float newFrameRaw = frameRaw + (isReversed() ? -dFrames : dFrames);
+ boolean ended = !MiscUtils.contains(newFrameRaw, getMinFrame(), getMaxFrame());
+ float previousFrameRaw = frameRaw;
+ frameRaw = MiscUtils.clamp(newFrameRaw, getMinFrame(), getMaxFrame());
+ frame = useCompositionFrameRate ? (float) Math.floor(frameRaw) : frameRaw;
lastFrameTimeNs = frameTimeNanos;
- notifyUpdate();
+ if (!useCompositionFrameRate || frameRaw != previousFrameRaw) {
+ notifyUpdate();
+ }
if (ended) {
if (getRepeatCount() != INFINITE && repeatCount >= getRepeatCount()) {
- frame = speed < 0 ? getMinFrame() : getMaxFrame();
+ frameRaw = speed < 0 ? getMinFrame() : getMaxFrame();
+ frame = frameRaw;
removeFrameCallback();
notifyEnd(isReversed());
} else {
@@ -108,7 +119,8 @@ public class LottieValueAnimator extends BaseLottieAnimator implements Choreogra
speedReversedForRepeatMode = !speedReversedForRepeatMode;
reverseAnimationSpeed();
} else {
- frame = isReversed() ? getMaxFrame() : getMinFrame();
+ frameRaw = isReversed() ? getMaxFrame() : getMinFrame();
+ frame = frameRaw;
}
lastFrameTimeNs = frameTimeNanos;
}
@@ -146,15 +158,17 @@ public class LottieValueAnimator extends BaseLottieAnimator implements Choreogra
}
float frame = this.frame;
this.frame = 0f;
+ this.frameRaw = 0f;
setFrame((int) frame);
notifyUpdate();
}
public void setFrame(float frame) {
- if (this.frame == frame) {
+ if (this.frameRaw == frame) {
return;
}
- this.frame = MiscUtils.clamp(frame, getMinFrame(), getMaxFrame());
+ this.frameRaw = MiscUtils.clamp(frame, getMinFrame(), getMaxFrame());
+ this.frame = useCompositionFrameRate ? ((float) Math.floor(frameRaw)) : frameRaw;
lastFrameTimeNs = 0;
notifyUpdate();
}
@@ -224,6 +238,7 @@ public class LottieValueAnimator extends BaseLottieAnimator implements Choreogra
@MainThread
public void pauseAnimation() {
removeFrameCallback();
+ notifyPause();
}
@MainThread
@@ -232,10 +247,11 @@ public class LottieValueAnimator extends BaseLottieAnimator implements Choreogra
postFrameCallback();
lastFrameTimeNs = 0;
if (isReversed() && getFrame() == getMinFrame()) {
- frame = getMaxFrame();
+ setFrame(getMaxFrame());
} else if (!isReversed() && getFrame() == getMaxFrame()) {
- frame = getMinFrame();
+ setFrame(getMinFrame());
}
+ notifyResume();
}
@MainThread
diff --git a/lottie/src/main/java/com/airbnb/lottie/value/Keyframe.java b/lottie/src/main/java/com/airbnb/lottie/value/Keyframe.java
index 78f74db5..6d110b78 100644
--- a/lottie/src/main/java/com/airbnb/lottie/value/Keyframe.java
+++ b/lottie/src/main/java/com/airbnb/lottie/value/Keyframe.java
@@ -90,6 +90,21 @@ public class Keyframe<T> {
endFrame = Float.MAX_VALUE;
}
+ private Keyframe(T startValue, T endValue) {
+ composition = null;
+ this.startValue = startValue;
+ this.endValue = endValue;
+ interpolator = null;
+ xInterpolator = null;
+ yInterpolator = null;
+ startFrame = Float.MIN_VALUE;
+ endFrame = Float.MAX_VALUE;
+ }
+
+ public Keyframe<T> copyWith(T startValue, T endValue) {
+ return new Keyframe<T>(startValue, endValue);
+ }
+
public float getStartProgress() {
if (composition == null) {
return 0f;
diff --git a/lottie/src/main/java/com/airbnb/lottie/value/LottieValueCallback.java b/lottie/src/main/java/com/airbnb/lottie/value/LottieValueCallback.java
index 1cf5d205..60270009 100644
--- a/lottie/src/main/java/com/airbnb/lottie/value/LottieValueCallback.java
+++ b/lottie/src/main/java/com/airbnb/lottie/value/LottieValueCallback.java
@@ -14,7 +14,7 @@ import com.airbnb.lottie.animation.keyframe.BaseKeyframeAnimation;
*
* If your dynamic property does the following, you must call {@link LottieAnimationView#invalidate()} or
* {@link LottieDrawable#invalidateSelf()} each time you want to update this value.
- * 1. Use {@link com.airbnb.lottie.RenderMode.SOFTWARE}
+ * 1. Use {@link com.airbnb.lottie.RenderMode#SOFTWARE}
* 2. Rendering a static image (the animation is either paused or there are no values
* changing within the animation itself)
* When using software rendering, Lottie caches the internal rendering bitmap. Whenever the animation changes
diff --git a/lottie/src/main/res/values/attrs.xml b/lottie/src/main/res/values/attrs.xml
index 024c323c..31af44a5 100644
--- a/lottie/src/main/res/values/attrs.xml
+++ b/lottie/src/main/res/values/attrs.xml
@@ -21,12 +21,21 @@
<attr name="lottie_speed" format="float" />
<attr name="lottie_cacheComposition" format="boolean" />
<attr name="lottie_ignoreDisabledSystemAnimations" format="boolean" />
+ <attr name="lottie_useCompositionFrameRate" format="boolean" />
<attr name="lottie_clipToCompositionBounds" format="boolean" />
+ <attr name="lottie_clipTextToBoundingBox" format="boolean" />
+ <!-- The default file extension that Lottie will use when finding fonts in assets/fonts/fontFamily.* -->
+ <attr name="lottie_defaultFontFileExtension" format="string" />
<!-- These values must be kept in sync with the RenderMode enum -->
<attr name="lottie_renderMode" format="enum">
<enum name="automatic" value="0" />
<enum name="hardware" value="1" />
<enum name="software" value="2" />
</attr>
+ <attr name="lottie_asyncUpdates" format="enum">
+ <enum name="automatic" value="0" />
+ <enum name="enabled" value="1" />
+ <enum name="disabled" value="2" />
+ </attr>
</declare-styleable>
-</resources> \ No newline at end of file
+</resources>
diff --git a/lottie/src/test/java/com/airbnb/lottie/LottieCompositionFactoryTest.java b/lottie/src/test/java/com/airbnb/lottie/LottieCompositionFactoryTest.java
index de746356..38140999 100644
--- a/lottie/src/test/java/com/airbnb/lottie/LottieCompositionFactoryTest.java
+++ b/lottie/src/test/java/com/airbnb/lottie/LottieCompositionFactoryTest.java
@@ -18,6 +18,7 @@ import static junit.framework.Assert.assertNotSame;
import static junit.framework.Assert.assertNull;
import static okio.Okio.buffer;
import static okio.Okio.source;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertSame;
@SuppressWarnings("ReferenceEquality")
@@ -45,6 +46,20 @@ public class LottieCompositionFactoryTest extends BaseTest {
assertNotNull(result.getValue());
}
+ @Test
+ public void testLoadJsonStringHitsCache() {
+ LottieResult<LottieComposition> result1 = LottieCompositionFactory.fromJsonStringSync(JSON, "json");
+ LottieResult<LottieComposition> result2 = LottieCompositionFactory.fromJsonStringSync(JSON, "json");
+ assertEquals(result1, result2);
+ }
+
+ @Test
+ public void testLoadDifferentJsonStringsDoesntHitsCache() {
+ LottieResult<LottieComposition> result1 = LottieCompositionFactory.fromJsonStringSync(JSON, "jso1");
+ LottieResult<LottieComposition> result2 = LottieCompositionFactory.fromJsonStringSync(JSON, "json2");
+ assertNotEquals(result1, result2);
+ }
+
@Test
public void testLoadInvalidJsonString() {
LottieResult<LottieComposition> result = LottieCompositionFactory.fromJsonStringSync(NOT_JSON, "not_json");
diff --git a/lottie/src/test/java/com/airbnb/lottie/LottieDrawableTest.java b/lottie/src/test/java/com/airbnb/lottie/LottieDrawableTest.java
index 0132d8d5..3e158ec7 100644
--- a/lottie/src/test/java/com/airbnb/lottie/LottieDrawableTest.java
+++ b/lottie/src/test/java/com/airbnb/lottie/LottieDrawableTest.java
@@ -36,10 +36,10 @@ public class LottieDrawableTest extends BaseTest {
@SuppressWarnings("SameParameterValue")
private LottieComposition createComposition(int startFrame, int endFrame) {
LottieComposition composition = new LottieComposition();
- composition.init(new Rect(), startFrame, endFrame, 1000, new ArrayList<Layer>(),
- new LongSparseArray<Layer>(0), new HashMap<String, List<Layer>>(0),
- new HashMap<String, LottieImageAsset>(0), new SparseArrayCompat<FontCharacter>(0),
- new HashMap<String, Font>(0), new ArrayList<Marker>());
+ composition.init(new Rect(), startFrame, endFrame, 1000, new ArrayList<>(),
+ new LongSparseArray<>(0), new HashMap<>(0),
+ new HashMap<>(0), 1f, new SparseArrayCompat<>(0),
+ new HashMap<>(0), new ArrayList<>());
return composition;
}
diff --git a/lottie/src/test/java/com/airbnb/lottie/LottieInitializeTest.java b/lottie/src/test/java/com/airbnb/lottie/LottieInitializeTest.java
new file mode 100644
index 00000000..688dd73c
--- /dev/null
+++ b/lottie/src/test/java/com/airbnb/lottie/LottieInitializeTest.java
@@ -0,0 +1,128 @@
+package com.airbnb.lottie;
+
+import static org.junit.Assert.assertNotNull;
+
+import android.content.Context;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.airbnb.lottie.network.LottieFetchResult;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+import java.io.InputStream;
+import java.util.Objects;
+
+public class LottieInitializeTest extends BaseTest {
+
+ @Rule
+ public final TemporaryFolder temporaryFolder1 = new TemporaryFolder();
+
+ @Rule
+ public final TemporaryFolder temporaryFolder2 = new TemporaryFolder();
+
+ private final Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+
+ @Before
+ public void setExecutor() {
+ LottieTask.EXECUTOR = Runnable::run;
+ }
+
+ @Test
+ public void fetchAfterSecondInitialize() {
+ initializeLottie(temporaryFolder1);
+ // Fetching here causes the resource to be cached in temporaryFolder1:
+ LottieResult<LottieComposition> result1 = LottieCompositionFactory.fromUrlSync(context, "resources://test1.json");
+ assertNotNull(result1.getValue());
+
+ // Manually delete to simulate the end of a test:
+ temporaryFolder1.delete();
+
+ initializeLottie(temporaryFolder2);
+ // Fetching here fails if L.setCacheProvider doesn't reset both its internal networkFetcher and its internal networkCache, because
+ // temporaryFolder1 has been deleted:
+ LottieResult<LottieComposition> result2 = LottieCompositionFactory.fromUrlSync(context, "resources://test1.json");
+ assertNotNull(result2.getValue());
+ }
+
+ private void initializeLottie(TemporaryFolder temporaryFolder) {
+ LottieConfig lottieConfig = new LottieConfig.Builder()
+ .setNetworkCacheDir(temporaryFolder.getRoot())
+ .setNetworkFetcher(url -> {
+ if (url.startsWith("resources://")) {
+ InputStream stream = Objects.requireNonNull(getClass().getClassLoader())
+ .getResourceAsStream(url.substring("resources://".length()));
+ if (stream != null) {
+ return new LottieFetchSuccess(stream);
+ }
+ }
+
+ return new LottieFetchFailure("Could not load <$url>");
+ })
+ .build();
+ Lottie.initialize(lottieConfig);
+ }
+
+ private static class LottieFetchSuccess implements LottieFetchResult {
+
+ @NonNull private final InputStream jsonStream;
+
+ LottieFetchSuccess(@NonNull InputStream jsonStream) {
+ this.jsonStream = jsonStream;
+ }
+
+ @Override public boolean isSuccessful() {
+ return true;
+ }
+
+ @Override @NonNull public InputStream bodyByteStream() {
+ return jsonStream;
+ }
+
+ @Override public String contentType() {
+ return "application/json";
+ }
+
+ @Override @Nullable public String error() {
+ return null;
+ }
+
+ @Override public void close() {
+ // No-op
+ }
+ }
+
+ private static class LottieFetchFailure implements LottieFetchResult {
+
+ @NonNull private final String errorMessage;
+
+ LottieFetchFailure(@NonNull String errorMessage) {
+ this.errorMessage = errorMessage;
+ }
+
+ @Override public boolean isSuccessful() {
+ return false;
+ }
+
+ @Override @NonNull public InputStream bodyByteStream() {
+ throw new RuntimeException("LottieFetchFailure has no body");
+ }
+
+ @Override @Nullable public String contentType() {
+ return null;
+ }
+
+ @Override public String error() {
+ return errorMessage;
+ }
+
+ @Override public void close() {
+ // No-op
+ }
+ }
+}
diff --git a/lottie/src/test/java/com/airbnb/lottie/LottieTaskTest.java b/lottie/src/test/java/com/airbnb/lottie/LottieTaskTest.java
index 804f9613..91eeea16 100644
--- a/lottie/src/test/java/com/airbnb/lottie/LottieTaskTest.java
+++ b/lottie/src/test/java/com/airbnb/lottie/LottieTaskTest.java
@@ -1,15 +1,18 @@
package com.airbnb.lottie;
-import org.junit.Before;
import org.junit.Ignore;
+import org.junit.Rule;
import org.junit.Test;
import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
import java.util.concurrent.Callable;
import java.util.concurrent.Semaphore;
-import static org.mockito.Mockito.*;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoInteractions;
public class LottieTaskTest extends BaseTest {
@@ -18,10 +21,8 @@ public class LottieTaskTest extends BaseTest {
@Mock
public LottieListener<Throwable> failureListener;
- @Before
- public void setup() {
- MockitoAnnotations.initMocks(this);
- }
+ @Rule
+ public MockitoRule rule = MockitoJUnit.rule();
@Test
public void testListener() {
@@ -29,7 +30,7 @@ public class LottieTaskTest extends BaseTest {
.addListener(successListener)
.addFailureListener(failureListener);
verify(successListener, times(1)).onResult(5);
- verifyZeroInteractions(failureListener);
+ verifyNoInteractions(failureListener);
}
@Test
@@ -40,7 +41,7 @@ public class LottieTaskTest extends BaseTest {
}, true)
.addListener(successListener)
.addFailureListener(failureListener);
- verifyZeroInteractions(successListener);
+ verifyNoInteractions(successListener);
verify(failureListener, times(1)).onResult(exception);
}
@@ -69,8 +70,8 @@ public class LottieTaskTest extends BaseTest {
} catch (InterruptedException e) {
throw new IllegalStateException(e);
}
- verifyZeroInteractions(successListener);
- verifyZeroInteractions(failureListener);
+ verifyNoInteractions(successListener);
+ verifyNoInteractions(failureListener);
}
@Test
@@ -84,6 +85,6 @@ public class LottieTaskTest extends BaseTest {
task.addListener(successListener);
task.addFailureListener(failureListener);
verify(successListener, times(1)).onResult(5);
- verifyZeroInteractions(failureListener);
+ verifyNoInteractions(failureListener);
}
}
diff --git a/lottie/src/test/java/com/airbnb/lottie/LottieValueAnimatorUnitTest.java b/lottie/src/test/java/com/airbnb/lottie/LottieValueAnimatorUnitTest.java
index 08b0b8c1..102087b6 100644
--- a/lottie/src/test/java/com/airbnb/lottie/LottieValueAnimatorUnitTest.java
+++ b/lottie/src/test/java/com/airbnb/lottie/LottieValueAnimatorUnitTest.java
@@ -60,10 +60,10 @@ public class LottieValueAnimatorUnitTest extends BaseTest {
private LottieComposition createComposition(int startFrame, int endFrame) {
LottieComposition composition = new LottieComposition();
- composition.init(new Rect(), startFrame, endFrame, 1000, new ArrayList<Layer>(),
- new LongSparseArray<Layer>(0), new HashMap<String, List<Layer>>(0),
- new HashMap<String, LottieImageAsset>(0), new SparseArrayCompat<FontCharacter>(0),
- new HashMap<String, Font>(0), new ArrayList<Marker>());
+ composition.init(new Rect(), startFrame, endFrame, 1000, new ArrayList<>(),
+ new LongSparseArray<>(0), new HashMap<>(0),
+ new HashMap<>(0), 1f, new SparseArrayCompat<>(0),
+ new HashMap<>(0), new ArrayList<>());
return composition;
}
diff --git a/lottie/src/test/java/com/airbnb/lottie/model/animatable/AnimatableGradientColorValueTest.java b/lottie/src/test/java/com/airbnb/lottie/model/animatable/AnimatableGradientColorValueTest.java
new file mode 100644
index 00000000..0b5004c5
--- /dev/null
+++ b/lottie/src/test/java/com/airbnb/lottie/model/animatable/AnimatableGradientColorValueTest.java
@@ -0,0 +1,22 @@
+package com.airbnb.lottie.model.animatable;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+
+public class AnimatableGradientColorValueTest {
+ @Test
+ public void testMergeTheSame() {
+ assertArrayEquals(new float[]{1, 2}, AnimatableGradientColorValue.mergePositions(new float[]{1, 2}, new float[]{1, 2}), 0f);
+ }
+
+ @Test
+ public void testMergeDifferent() {
+ assertArrayEquals(new float[]{1, 2, 3, 4}, AnimatableGradientColorValue.mergePositions(new float[]{1, 2}, new float[]{3, 4}), 0f);
+ }
+
+ @Test
+ public void testMergeOneOverlap() {
+ assertArrayEquals(new float[]{1, 2, 3}, AnimatableGradientColorValue.mergePositions(new float[]{1, 2}, new float[]{2, 3}), 0f);
+ }
+} \ No newline at end of file
diff --git a/lottie/src/test/java/com/airbnb/lottie/model/content/GradientColorTest.java b/lottie/src/test/java/com/airbnb/lottie/model/content/GradientColorTest.java
new file mode 100644
index 00000000..719a5fca
--- /dev/null
+++ b/lottie/src/test/java/com/airbnb/lottie/model/content/GradientColorTest.java
@@ -0,0 +1,46 @@
+package com.airbnb.lottie.model.content;
+
+import junit.framework.TestCase;
+import org.junit.Test;
+
+import java.util.Arrays;
+
+public class GradientColorTest extends TestCase {
+
+ private final GradientColor start = new GradientColor(new float[]{0f, 1f}, new int[]{0xFF000000, 0xFF020202});
+
+ private final GradientColor end = new GradientColor(new float[]{0f, 1f}, new int[]{0xFF020202, 0xFF040404});
+
+ private final GradientColor gradient = new GradientColor(new float[2], new int[2]);
+
+ @Test
+ public void testLerpWithOutOfBoundsNegativeProgress() {
+ gradient.lerp(start, end, -42f);
+ assertEquals(start, gradient);
+ }
+
+ @Test
+ public void testLerpWithZeroProgress() {
+ gradient.lerp(start, end, 0f);
+ assertEquals(start, gradient);
+ }
+
+ @Test
+ public void testLerpWithHalfProgress() {
+ gradient.lerp(start, end, 0.5f);
+ GradientColor half = new GradientColor(new float[]{0f, 1f}, new int[]{0xFF010101, 0xFF030303});
+ assertEquals(half, gradient);
+ }
+
+ @Test
+ public void testLerpWithOneProgress() {
+ gradient.lerp(start, end, 1f);
+ assertEquals(end, gradient);
+ }
+
+ @Test
+ public void testLerpWithOutOfBoundsPositiveProgress() {
+ gradient.lerp(start, end, 42f);
+ assertEquals(end, gradient);
+ }
+}
diff --git a/lottie/src/test/java/com/airbnb/lottie/utils/LottieValueAnimatorTest.java b/lottie/src/test/java/com/airbnb/lottie/utils/LottieValueAnimatorTest.java
new file mode 100644
index 00000000..f218fd62
--- /dev/null
+++ b/lottie/src/test/java/com/airbnb/lottie/utils/LottieValueAnimatorTest.java
@@ -0,0 +1,47 @@
+package com.airbnb.lottie.utils;
+
+import android.animation.Animator;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import com.airbnb.lottie.BaseTest;
+
+import org.junit.Test;
+
+public class LottieValueAnimatorTest extends BaseTest {
+ @Test
+ public void callOfPauseAnimationShouldFireCallbackOnAnimationPause() {
+ // Set
+ Animator.AnimatorPauseListener listener = mock(Animator.AnimatorPauseListener.class);
+
+ LottieValueAnimator animator = new LottieValueAnimator();
+ animator.addPauseListener(listener);
+
+ // Do
+ animator.pauseAnimation();
+
+ // Check
+ verify(listener, times(1)).onAnimationPause(eq(animator));
+ verify(listener, times(0)).onAnimationResume(any());
+ }
+
+ @Test
+ public void callOfResumeAnimationShouldFireCallbackOnAnimationResume() {
+ // Set
+ Animator.AnimatorPauseListener listener = mock(Animator.AnimatorPauseListener.class);
+
+ LottieValueAnimator animator = new LottieValueAnimator();
+ animator.addPauseListener(listener);
+
+ // Do
+ animator.resumeAnimation();
+
+ // Check
+ verify(listener, times(0)).onAnimationPause(any());
+ verify(listener, times(1)).onAnimationResume(eq(animator));
+ }
+}
diff --git a/lottie/src/test/resources/test1.json b/lottie/src/test/resources/test1.json
new file mode 100644
index 00000000..95394ce8
--- /dev/null
+++ b/lottie/src/test/resources/test1.json
@@ -0,0 +1,116 @@
+{
+ "v": "4.11.1",
+ "fr": 60,
+ "ip": 0,
+ "op": 180,
+ "w": 300,
+ "h": 300,
+ "nm": "Comp 1",
+ "ddd": 0,
+ "assets": [],
+ "layers": [
+ {
+ "ddd": 0,
+ "ind": 1,
+ "ty": 4,
+ "nm": "Shape Layer 1",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 11
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 10
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 150,
+ 150,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0,
+ 0
+ ],
+ "ix": 1
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100,
+ 100
+ ],
+ "ix": 6
+ }
+ },
+ "ao": 0,
+ "shapes": [
+ {
+ "ty": "rc",
+ "d": 1,
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 3
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 4
+ },
+ "nm": "Rectangle Path 1",
+ "mn": "ADBE Vector Shape - Rect",
+ "hd": false
+ },
+ {
+ "ty": "fl",
+ "c": {
+ "a": 0,
+ "k": [
+ 0.928262987324,
+ 0,
+ 0,
+ 1
+ ],
+ "ix": 4
+ },
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 5
+ },
+ "r": 1,
+ "nm": "Fill 1",
+ "mn": "ADBE Vector Graphic - Fill",
+ "hd": false
+ }
+ ],
+ "ip": 0,
+ "op": 180,
+ "st": 0,
+ "bm": 0
+ }
+ ]
+}
diff --git a/sample-compose-benchmark/build.gradle b/sample-compose-benchmark/build.gradle
deleted file mode 100644
index 742600ed..00000000
--- a/sample-compose-benchmark/build.gradle
+++ /dev/null
@@ -1,48 +0,0 @@
-plugins {
- id 'com.android.test'
- id 'org.jetbrains.kotlin.android'
-}
-
-android {
- compileSdk 31
-
- compileOptions {
- sourceCompatibility = JavaVersion.VERSION_1_8
- targetCompatibility = JavaVersion.VERSION_1_8
- }
-
- kotlinOptions {
- jvmTarget = "1.8"
- freeCompilerArgs += "-Xopt-in=kotlin.RequiresOptIn"
- }
-
- defaultConfig {
- minSdk 30
- targetSdk 31
-
- testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
- }
-
- buildTypes {
- release {
- debuggable = true
- signingConfig = debug.signingConfig
- }
- }
-
- targetProjectPath = ":sample-compose"
- experimentalProperties["android.experimental.self-instrumenting"] = true
-}
-
-dependencies {
- implementation 'androidx.test.ext:junit:1.1.3'
- implementation 'androidx.test.espresso:espresso-core:3.4.0'
- implementation 'androidx.test.uiautomator:uiautomator:2.2.0'
- implementation 'androidx.benchmark:benchmark-macro-junit4:1.1.0-beta01'
-}
-
-androidComponents {
- beforeVariants(selector().all()) {
- enabled = buildType == "release"
- }
-}
diff --git a/sample-compose-benchmark/src/main/java/com/airbnb/lottie/sample/compose/benchmark/LottieBaselineProfiles.kt b/sample-compose-benchmark/src/main/java/com/airbnb/lottie/sample/compose/benchmark/LottieBaselineProfiles.kt
deleted file mode 100644
index 9413efca..00000000
--- a/sample-compose-benchmark/src/main/java/com/airbnb/lottie/sample/compose/benchmark/LottieBaselineProfiles.kt
+++ /dev/null
@@ -1,98 +0,0 @@
-package com.airbnb.lottie.sample.compose.benchmark
-
-import android.content.Context
-import androidx.benchmark.macro.ExperimentalBaselineProfilesApi
-import androidx.benchmark.macro.junit4.BaselineProfileRule
-import androidx.test.core.app.ApplicationProvider
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.platform.app.InstrumentationRegistry
-import androidx.test.uiautomator.UiDevice
-import androidx.test.uiautomator.UiObject
-import androidx.test.uiautomator.UiScrollable
-import androidx.test.uiautomator.UiSelector
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-import kotlin.time.Duration
-import kotlin.time.DurationUnit
-import kotlin.time.ExperimentalTime
-
-@RunWith(AndroidJUnit4::class)
-@OptIn(ExperimentalBaselineProfilesApi::class)
-class LottieBaselineProfiles {
-
- @get:Rule
- val baselineProfileRule = BaselineProfileRule()
-
- private lateinit var context: Context
- private lateinit var device: UiDevice
-
- @Before
- fun setUp() {
- val instrumentation = InstrumentationRegistry.getInstrumentation()
- context = ApplicationProvider.getApplicationContext()
- device = UiDevice.getInstance(instrumentation)
- }
-
- @Test
- @OptIn(ExperimentalTime::class)
- fun baselineProfiles() {
- baselineProfileRule.collectBaselineProfile(
- packageName = PACKAGE_NAME,
- ) {
- pressHome()
- startActivityAndWait()
- // Find the ScrollView in the Showcase route
- val selector = UiSelector().className("android.widget.ScrollView")
- val scrollable = UiScrollable(selector)
- scrollable.waitUntilReady()
- for (i in 0 until scrollable.childCount) {
- val childSelector = UiSelector()
- .enabled(true)
- .clickable(true)
- .instance(i)
- val child = scrollable.getChild(childSelector)
- scrollable.scrollIntoView(child)
- child.clickAndWait()
- }
- }
- }
-
- @OptIn(ExperimentalTime::class)
- private fun UiScrollable.waitUntilReady() {
- this.waitUntil {
- // We know that there are at least 9 children
- childCount >= EXPECTED_ITEM_INDEX_COUNT
- }
- }
-
- @OptIn(ExperimentalTime::class)
- private fun UiObject.clickAndWait(maxWaitTime: Duration = Duration.seconds(5)) {
- val maxWaitTimeMs = maxWaitTime.toLong(DurationUnit.MILLISECONDS)
- click()
- device.waitForIdle(maxWaitTimeMs)
- Thread.sleep(maxWaitTimeMs)
- device.pressBack()
- }
-
- @OptIn(ExperimentalTime::class)
- private fun <T> T.waitUntil(maxWaitTime: Duration = Duration.seconds(5), condition: (T) -> Boolean) {
- var waitTime = 0L
- val maxWaitTimeMs = maxWaitTime.toLong(DurationUnit.MILLISECONDS)
- val incrementalDelay = 150L
- while (waitTime <= maxWaitTimeMs) {
- val ready = condition(this)
- if (ready) {
- break
- }
- Thread.sleep(incrementalDelay)
- waitTime += incrementalDelay
- }
- }
-
- companion object {
- private const val PACKAGE_NAME = "com.airbnb.lottie.sample.compose"
- private const val EXPECTED_ITEM_INDEX_COUNT = 9
- }
-}
diff --git a/sample-compose/build.gradle b/sample-compose/build.gradle
index c2f6061b..279dd7e2 100644
--- a/sample-compose/build.gradle
+++ b/sample-compose/build.gradle
@@ -1,15 +1,18 @@
+import static de.fayard.refreshVersions.core.Versions.versionFor
+
plugins {
id 'com.android.application'
- id "kotlin-android"
+ id "org.jetbrains.kotlin.android"
id 'kotlin-kapt'
}
android {
- compileSdk 31
+ namespace 'com.airbnb.lottie.sample.compose'
+ compileSdk 34
defaultConfig {
applicationId "com.airbnb.lottie.sample.compose"
minSdk 21
- targetSdk 30
+ targetSdk 34
versionCode 1
versionName VERSION_NAME
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
@@ -21,27 +24,20 @@ android {
debuggable false
}
}
- compileOptions {
- sourceCompatibility JavaVersion.VERSION_1_8
- targetCompatibility JavaVersion.VERSION_1_8
- }
kotlinOptions {
- jvmTarget = JavaVersion.VERSION_1_8.toString()
freeCompilerArgs += [
- "-Xallow-jvm-ir-dependencies",
"-Xskip-prerelease-check",
- "-Xuse-experimental=kotlinx.coroutines.ExperimentalCoroutinesApi",
- "-Xuse-experimental=androidx.compose.animation.ExperimentalAnimationApi",
- "-Xopt-in=androidx.compose.material.ExperimentalMaterialApi",
- "-Xopt-in=com.google.accompanist.pager.ExperimentalPagerApi",
- "-Xopt-in=kotlin.RequiresOptIn",
+ "-opt-in=androidx.compose.foundation.ExperimentalFoundationApi",
+ "-opt-in=androidx.compose.material.ExperimentalMaterialApi",
+ "-opt-in=kotlin.RequiresOptIn",
]
}
buildFeatures {
compose true
+ buildConfig true
}
composeOptions {
- kotlinCompilerExtensionVersion composeVersion
+ kotlinCompilerExtensionVersion = versionFor(project, AndroidX.compose.compiler)
}
packagingOptions {
exclude 'META-INF/AL2.0'
@@ -55,34 +51,35 @@ kapt {
dependencies {
implementation project(':lottie-compose')
- implementation "androidx.core:core-ktx:$coreVersion"
- implementation "androidx.activity:activity-compose:$activityVersion"
- implementation 'androidx.appcompat:appcompat:1.4.0-beta01'
- implementation "com.google.android.material:material:$materialVersion"
- implementation "androidx.compose.ui:ui:$composeVersion"
- implementation "androidx.compose.material:material:$composeVersion"
- implementation "androidx.compose.material:material-icons-extended:$composeVersion"
- implementation "androidx.compose.ui:ui-tooling:$composeVersion"
- implementation "androidx.navigation:navigation-compose:2.4.0-alpha10"
- implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycleVersion"
+ implementation libs.androidx.appcompat
+ implementation libs.androidx.core.ktx
+ implementation libs.androidx.activity.compose
+ implementation libs.google.material
+ implementation platform(libs.compose.bom)
+ implementation libs.compose.ui
+ implementation libs.compose.foundation
+ implementation libs.compose.material
+ implementation libs.compose.material.icons.extended
+ implementation libs.compose.ui.tooling
+ implementation libs.androidx.navigation.ui
+ implementation libs.androidx.navigation.compose
+ implementation libs.androidx.viewmodel.ktx
// Need this to side load a Baseline Profile when Benchmarking
- implementation "androidx.profileinstaller:profileinstaller:$startupVersion"
+ implementation libs.profileinstaller
- implementation "androidx.navigation:navigation-ui-ktx:2.3.5"
- implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutinesVersion"
+ implementation libs.kotlinx.coroutines.android
- implementation "com.google.dagger:dagger:$daggerVersion"
- kapt "com.google.dagger:dagger-compiler:$daggerVersion"
+ implementation libs.dagger
+ kapt libs.dagger.compiler
- implementation "com.squareup.retrofit2:retrofit:$retrofitVersion"
- implementation "com.squareup.retrofit2:converter-moshi:$retrofitVersion"
- implementation "com.google.accompanist:accompanist-coil:0.15.0"
- implementation "com.google.accompanist:accompanist-pager-indicators:0.18.0"
- implementation 'com.airbnb.android:mavericks:2.3.0'
- implementation 'com.airbnb.android:mavericks-compose:2.1.0-alpha02'
+ implementation libs.retrofit
+ implementation libs.retrofit.moshi
+ implementation libs.coil.compose
+ implementation libs.mavericks
+ implementation libs.mavericks.compose
- debugImplementation "androidx.compose.ui:ui-test-manifest:$composeVersion"
+ debugImplementation libs.compose.ui.test.manifest
- androidTestImplementation "androidx.compose.ui:ui-test-junit4:$composeVersion"
+ androidTestImplementation libs.compose.ui.test.junit
}
diff --git a/sample-compose/src/androidTest/java/com/airbnb/lottie/samples/InfiniteAnimationTest.kt b/sample-compose/src/androidTest/java/com/airbnb/lottie/samples/InfiniteAnimationTest.kt
index 1ebb7c6f..195b718f 100644
--- a/sample-compose/src/androidTest/java/com/airbnb/lottie/samples/InfiniteAnimationTest.kt
+++ b/sample-compose/src/androidTest/java/com/airbnb/lottie/samples/InfiniteAnimationTest.kt
@@ -1,5 +1,6 @@
package com.airbnb.lottie.samples
+import androidx.activity.ComponentActivity
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
@@ -23,7 +24,7 @@ import org.junit.Test
class InfiniteAnimationTest {
@get:Rule
- val composeTestRule = createAndroidComposeRule(ComposeActivity::class.java)
+ val composeTestRule = createAndroidComposeRule(ComponentActivity::class.java)
@Test
fun testInfiniteAnimation() {
@@ -54,4 +55,4 @@ class InfiniteAnimationTest {
.onNodeWithText("Composition Loaded!")
.assertIsDisplayed()
}
-} \ No newline at end of file
+}
diff --git a/sample-compose/src/androidTest/java/com/airbnb/lottie/samples/WalkthroughAnimationTest.kt b/sample-compose/src/androidTest/java/com/airbnb/lottie/samples/WalkthroughAnimationTest.kt
index 6bdb3f5f..d5fe3731 100644
--- a/sample-compose/src/androidTest/java/com/airbnb/lottie/samples/WalkthroughAnimationTest.kt
+++ b/sample-compose/src/androidTest/java/com/airbnb/lottie/samples/WalkthroughAnimationTest.kt
@@ -1,5 +1,6 @@
package com.airbnb.lottie.samples
+import androidx.activity.ComponentActivity
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.getValue
@@ -8,7 +9,6 @@ import androidx.compose.ui.test.junit4.createAndroidComposeRule
import com.airbnb.lottie.LottieCompositionFactory
import com.airbnb.lottie.compose.LottieAnimation
import com.airbnb.lottie.compose.animateLottieCompositionAsState
-import com.airbnb.lottie.sample.compose.ComposeActivity
import com.airbnb.lottie.sample.compose.R
import org.junit.Rule
import org.junit.Test
@@ -16,12 +16,12 @@ import org.junit.Test
class WalkthroughAnimationTest {
@get:Rule
- val composeTestRule = createAndroidComposeRule(ComposeActivity::class.java)
+ val composeTestRule = createAndroidComposeRule(ComponentActivity::class.java)
@Test
fun testWalkthroughCompletes() {
val composition = LottieCompositionFactory.fromRawResSync(composeTestRule.activity, R.raw.walkthrough).value!!
- var animationCompleted = true
+ var animationCompleted = false
composeTestRule.setContent {
val progress by animateLottieCompositionAsState(composition, iterations = 1)
@@ -43,4 +43,4 @@ class WalkthroughAnimationTest {
composeTestRule.mainClock.advanceTimeUntil { animationCompleted }
}
-} \ No newline at end of file
+}
diff --git a/sample-compose/src/main/AndroidManifest.xml b/sample-compose/src/main/AndroidManifest.xml
index 139b4a3b..e0557ace 100644
--- a/sample-compose/src/main/AndroidManifest.xml
+++ b/sample-compose/src/main/AndroidManifest.xml
@@ -1,22 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.airbnb.lottie.sample.compose">
-
- <uses-permission android:name="android.permission.INTERNET" />
+<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
- android:allowBackup="true"
android:name=".LottieComposeApplication"
+ android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.LottieCompose">
- <profileable android:shell="true"/>
-
<activity
android:name=".ComposeActivity"
+ android:exported="true"
android:label="@string/app_name"
android:theme="@style/Theme.LottieCompose.NoActionBar">
<intent-filter>
@@ -25,6 +21,10 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
+
+ <profileable android:shell="true" />
</application>
+ <uses-permission android:name="android.permission.INTERNET" />
+
</manifest>
diff --git a/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/ComposeActivity.kt b/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/ComposeActivity.kt
index 44c4030b..bb7ab556 100644
--- a/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/ComposeActivity.kt
+++ b/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/ComposeActivity.kt
@@ -136,4 +136,4 @@ class ComposeActivity : AppCompatActivity() {
LottieFiles(Route.LottieFiles, R.drawable.ic_lottie_files, R.string.bottom_tab_lottie_files),
Docs(Route.Examples, R.drawable.ic_examples, R.string.bottom_tab_examples),
}
-} \ No newline at end of file
+}
diff --git a/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/composables/AnimationRow.kt b/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/composables/AnimationRow.kt
index 96fcfdc7..8b342153 100644
--- a/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/composables/AnimationRow.kt
+++ b/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/composables/AnimationRow.kt
@@ -15,7 +15,7 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
-import coil.compose.rememberImagePainter
+import coil.compose.rememberAsyncImagePainter
@Composable
fun AnimationRow(
@@ -34,7 +34,7 @@ fun AnimationRow(
.fillMaxWidth()
) {
Image(
- painter = rememberImagePainter(previewUrl),
+ painter = rememberAsyncImagePainter(previewUrl),
modifier = Modifier
.padding(end = 16.dp)
.size(40.dp)
diff --git a/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/examples/AnimatableExamplesPage.kt b/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/examples/AnimatableExamplesPage.kt
index f52b15dd..47a2d3c7 100644
--- a/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/examples/AnimatableExamplesPage.kt
+++ b/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/examples/AnimatableExamplesPage.kt
@@ -50,6 +50,9 @@ fun AnimatableExamplesPage() {
ExampleCard("Example 5", "Click to toggle playback") {
Example5()
}
+ ExampleCard("Example 6", "Reverse Animation on Repeat") {
+ Example6()
+ }
}
}
}
@@ -166,4 +169,19 @@ private fun Example5() {
modifier = Modifier
.clickable { shouldPlay = !shouldPlay }
)
-} \ No newline at end of file
+}
+
+@Composable
+private fun Example6() {
+ val anim = rememberLottieAnimatable()
+ val composition by rememberLottieComposition(LottieCompositionSpec.RawRes(R.raw.heart))
+ LaunchedEffect(composition) {
+ anim.animate(
+ composition,
+ iterations = LottieConstants.IterateForever,
+ reverseOnRepeat = true,
+ )
+ }
+ LottieAnimation(anim.composition, { anim.progress })
+}
+
diff --git a/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/examples/DynamicPropertiesExamplesPage.kt b/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/examples/DynamicPropertiesExamplesPage.kt
index 47723188..5d6934c4 100644
--- a/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/examples/DynamicPropertiesExamplesPage.kt
+++ b/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/examples/DynamicPropertiesExamplesPage.kt
@@ -60,7 +60,7 @@ private fun HeartColor() {
)
}
var colorIndex by remember { mutableStateOf(0) }
- val color by derivedStateOf { colors[colorIndex] }
+ val color by remember { derivedStateOf { colors[colorIndex] } }
val blurRadius = with(LocalDensity.current) { 12.dp.toPx() }
val dynamicProperties = rememberLottieDynamicProperties(
@@ -100,7 +100,7 @@ private fun JumpHeight() {
val composition by rememberLottieComposition(LottieCompositionSpec.Asset("AndroidWave.json"))
val extraJumpHeights = remember { listOf(0.dp, 24.dp, 48.dp, 128.dp) }
var extraJumpIndex by remember { mutableStateOf(0) }
- val extraJumpHeight by derivedStateOf { extraJumpHeights[extraJumpIndex] }
+ val extraJumpHeight by remember { derivedStateOf { extraJumpHeights[extraJumpIndex] } }
val extraJumpHeightPx = with(LocalDensity.current) { extraJumpHeight.toPx() }
val point = remember { PointF() }
diff --git a/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/examples/ImagesExamplesPage.kt b/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/examples/ImagesExamplesPage.kt
index 0bedae94..b9d5129a 100644
--- a/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/examples/ImagesExamplesPage.kt
+++ b/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/examples/ImagesExamplesPage.kt
@@ -106,7 +106,7 @@ fun StoredOnImageAsset() {
LottieCompositionSpec.RawRes(R.raw.we_accept),
cacheKey = null,
)
- val imageAsset by derivedStateOf { composition?.images?.get("image_0") }
+ val imageAsset by remember { derivedStateOf { composition?.images?.get("image_0") } }
val bitmap = rememberBitmapFromAssets("Images/android.png")
LaunchedEffect(imageAsset, bitmap) {
if (imageAsset != null && bitmap != null) {
diff --git a/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/examples/ViewPagerExample.kt b/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/examples/ViewPagerExample.kt
index 79945bef..ab8dba34 100644
--- a/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/examples/ViewPagerExample.kt
+++ b/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/examples/ViewPagerExample.kt
@@ -1,67 +1,49 @@
package com.airbnb.lottie.sample.compose.examples
-import androidx.compose.animation.core.exponentialDecay
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.pager.HorizontalPager
+import androidx.compose.foundation.pager.PagerState
+import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
-import androidx.compose.ui.Alignment
+import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.unit.dp
import com.airbnb.lottie.compose.LottieAnimation
import com.airbnb.lottie.compose.LottieCompositionSpec
import com.airbnb.lottie.compose.rememberLottieComposition
import com.airbnb.lottie.sample.compose.R
-import com.google.accompanist.pager.HorizontalPager
-import com.google.accompanist.pager.HorizontalPagerIndicator
-import com.google.accompanist.pager.PagerDefaults
-import com.google.accompanist.pager.PagerState
-import com.google.accompanist.pager.rememberPagerState
@Composable
fun ViewPagerExamplePage() {
val colors = listOf(Color.Red, Color.Green, Color.Blue, Color.Magenta)
- val pagerState = rememberPagerState(pageCount = colors.size)
+ val pagerState = rememberPagerState { colors.size }
Box(
modifier = Modifier
- .fillMaxSize()
+ .fillMaxSize(),
) {
- HorizontalPager(
- pagerState,
- flingBehavior = PagerDefaults.rememberPagerFlingConfig(
- pagerState,
- decayAnimationSpec = exponentialDecay(frictionMultiplier = 0.05f)
- )
- ) { page ->
+ HorizontalPager(pagerState) { page ->
Box(
modifier = Modifier
.fillMaxSize()
- .background(colors[page])
+ .background(colors[page]),
)
}
- WalkthroughAnimation(pagerState)
- HorizontalPagerIndicator(
- pagerState,
- modifier = Modifier
- .align(Alignment.BottomCenter)
- .padding(bottom = 32.dp)
- )
-
+ WalkthroughAnimation(pagerState, colors.size)
}
}
@Composable
-private fun WalkthroughAnimation(pagerState: PagerState) {
+private fun WalkthroughAnimation(pagerState: PagerState, size: Int) {
val composition by rememberLottieComposition(LottieCompositionSpec.RawRes(R.raw.walkthrough))
- val progress by derivedStateOf { (pagerState.currentPage + pagerState.currentPageOffset) / (pagerState.pageCount - 1f) }
+ val progress by remember { derivedStateOf { (pagerState.currentPage + pagerState.currentPageOffsetFraction) / (size - 1f) } }
LottieAnimation(
composition,
{ progress },
modifier = Modifier
- .fillMaxSize()
+ .fillMaxSize(),
)
-} \ No newline at end of file
+}
diff --git a/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/player/PlayerPage.kt b/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/player/PlayerPage.kt
index 3a640f27..f53464a4 100644
--- a/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/player/PlayerPage.kt
+++ b/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/player/PlayerPage.kt
@@ -134,13 +134,18 @@ fun PlayerPage(
Scaffold(
scaffoldState = scaffoldState,
topBar = { PlayerPageTopAppBar(state, compositionResult.value) },
- ) {
- PlayerPageContent(
- state,
- compositionResult.value,
- compositionResult.isLoading,
- animationBackgroundColor,
- )
+ ) { padding ->
+ Box(
+ modifier = Modifier
+ .padding(padding)
+ ) {
+ PlayerPageContent(
+ state,
+ compositionResult.value,
+ compositionResult.isLoading,
+ animationBackgroundColor,
+ )
+ }
}
if (state.showWarningsDialog) {
@@ -306,7 +311,7 @@ private fun PlayerControlsRow(
state: PlayerPageState,
composition: LottieComposition?,
) {
- val totalTime = ((composition?.duration ?: 0L / state.animatable.speed) / 1000.0)
+ val totalTime = (((composition?.duration ?: (0L / state.animatable.speed)) / 1000.0))
val totalTimeFormatted = ("%.1f").format(totalTime)
val progressFormatted = ("%.1f").format(state.animatable.progress * totalTime)
diff --git a/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/showcase/ShowcasePage.kt b/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/showcase/ShowcasePage.kt
index e3bfe7b1..c47a64c2 100644
--- a/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/showcase/ShowcasePage.kt
+++ b/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/showcase/ShowcasePage.kt
@@ -10,6 +10,7 @@ import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.testTag
import androidx.compose.ui.tooling.preview.Preview
import androidx.navigation.NavController
import androidx.navigation.compose.rememberNavController
@@ -58,4 +59,4 @@ fun DefaultPreview() {
LottieTheme {
ShowcasePage(navController)
}
-} \ No newline at end of file
+}
diff --git a/sample/build.gradle b/sample/build.gradle
index afb90ffa..54dc7b52 100644
--- a/sample/build.gradle
+++ b/sample/build.gradle
@@ -1,20 +1,22 @@
+import static de.fayard.refreshVersions.core.Versions.versionFor
+
plugins {
id 'com.android.application'
- id "kotlin-android"
- id 'kotlin-kapt'
- id 'kotlin-parcelize'
+ id "org.jetbrains.kotlin.android"
+ id 'org.jetbrains.kotlin.plugin.parcelize'
+ id 'com.google.devtools.ksp'
}
android {
- compileSdk 31
+ namespace 'com.airbnb.lottie.samples'
+ compileSdk 34
defaultConfig {
applicationId "com.airbnb.lottie"
minSdk 16
- targetSdk 30
+ targetSdk 34
versionCode 70
versionName VERSION_NAME
multiDexEnabled true
- buildConfigField("String", "BITRISE_GIT_BRANCH", "\"" + System.getenv("BITRISE_GIT_BRANCH") + "\"")
buildConfigField("String", "GIT_SHA", "\"" + gitSha + "\"")
buildConfigField("String", "GIT_BRANCH", "\"" + gitBranch + "\"")
vectorDrawables.useSupportLibrary = true
@@ -22,6 +24,7 @@ android {
}
buildFeatures {
viewBinding true
+ buildConfig true
}
buildTypes {
debug {
@@ -39,16 +42,8 @@ android {
textOutput 'stdout'
baseline file("lint-baseline.xml")
}
- compileOptions {
- sourceCompatibility JavaVersion.VERSION_1_8
- targetCompatibility JavaVersion.VERSION_1_8
- }
kotlinOptions {
- jvmTarget = JavaVersion.VERSION_1_8.toString()
- freeCompilerArgs += ["-Xuse-experimental=kotlinx.coroutines.ExperimentalCoroutinesApi"]
- }
- sourceSets {
- main.java.srcDirs += 'src/main/kotlin'
+ freeCompilerArgs += ["-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi"]
}
packagingOptions {
exclude 'META-INF/LICENSE.txt'
@@ -60,39 +55,41 @@ android {
dependencies {
implementation project(':lottie')
- implementation 'androidx.multidex:multidex:2.0.1'
+ implementation libs.androidx.multidex
- implementation "androidx.appcompat:appcompat:$appcompatVersion"
- implementation "androidx.recyclerview:recyclerview:1.2.1"
- implementation "androidx.paging:paging-runtime-ktx:3.0.1"
- implementation "androidx.cardview:cardview:1.0.0"
- implementation "androidx.core:core-ktx:$coreVersion"
- implementation 'androidx.constraintlayout:constraintlayout:2.1.1'
- implementation "androidx.browser:browser:1.3.0"
- implementation "com.google.android.material:material:$materialVersion"
+ implementation libs.androidx.appcompat
+ implementation libs.androidx.fragment
+ implementation libs.androidx.recyclerview
+ implementation libs.androidx.paging.runtime.ktx
+ implementation libs.androidx.viewmodel.ktx
+ implementation libs.androidx.cardview
+ implementation libs.androidx.core.ktx
+ implementation libs.androidx.constraintlayout
+ implementation libs.androidx.browser
+ implementation libs.google.material
- implementation "com.airbnb.android:epoxy:$epoxyVersion"
- kapt "com.airbnb.android:epoxy-processor:$epoxyVersion"
- implementation 'com.airbnb.android:mvrx:1.5.1'
+ implementation libs.epoxy
+ ksp libs.epoxy.processor
+ implementation libs.mavericks
- implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutinesVersion"
- implementation 'com.dlazaro66.qrcodereaderview:qrcodereaderview:2.0.2'
- implementation 'com.github.PhilJay:MPAndroidChart:3.1.0'
- implementation 'com.google.code.gson:gson:2.8.8'
- implementation 'com.squareup.okhttp3:okhttp:4.9.1'
- implementation "com.squareup.retrofit2:retrofit:$retrofitVersion"
- implementation "com.squareup.retrofit2:adapter-rxjava2:$retrofitVersion"
- implementation "com.squareup.retrofit2:converter-gson:$retrofitVersion"
- implementation 'com.github.bumptech.glide:glide:4.12.0'
+ implementation libs.kotlinx.coroutines.android
+ implementation libs.qrcodereaderview
+ implementation libs.mpandroidchart
+ implementation libs.gson
+ implementation libs.okhttp
+ implementation libs.retrofit
+ implementation libs.retrofit.rxjava
+ implementation libs.retrofit.gson
+ implementation libs.glide
- debugImplementation 'androidx.fragment:fragment-testing:1.3.6'
+ debugImplementation libs.androidx.fragment.testing
- testImplementation "junit:junit:$junitVersion"
- androidTestImplementation "androidx.test.ext:junit:$extJunitVersion"
- androidTestImplementation "androidx.test.espresso:espresso-core:$espressoVersion"
- androidTestImplementation "androidx.test.espresso:espresso-idling-resource:$espressoVersion"
- androidTestImplementation 'androidx.test:core:1.3.0'
- androidTestImplementation 'androidx.test:rules:1.4.0'
- androidTestImplementation "org.mockito:mockito-android:$mockitoVersion"
- androidTestImplementation "com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0"
+ testImplementation libs.junit4
+ androidTestImplementation libs.androidx.test.junit
+ androidTestImplementation libs.androidx.test.espresso
+ androidTestImplementation libs.androidx.test.espresso.idling
+ androidTestImplementation libs.androidx.test.core
+ androidTestImplementation libs.androidx.test.rules
+ androidTestImplementation libs.mockito.android
+ androidTestImplementation libs.mockito.kotlin
}
diff --git a/sample/lint-baseline.xml b/sample/lint-baseline.xml
index a7149f3f..da9ad55b 100644
--- a/sample/lint-baseline.xml
+++ b/sample/lint-baseline.xml
@@ -1639,5 +1639,4 @@
line="39"
column="33"/>
</issue>
-
</issues>
diff --git a/sample/src/androidTest/java/com/airbnb/lottie/samples/FragmentVisibilityTests.kt b/sample/src/androidTest/java/com/airbnb/lottie/samples/FragmentVisibilityTests.kt
index e4b586fd..59cc86e2 100644
--- a/sample/src/androidTest/java/com/airbnb/lottie/samples/FragmentVisibilityTests.kt
+++ b/sample/src/androidTest/java/com/airbnb/lottie/samples/FragmentVisibilityTests.kt
@@ -29,26 +29,37 @@ import com.airbnb.lottie.LottieAnimationView
import com.airbnb.lottie.LottieDrawable
import com.airbnb.lottie.model.LottieCompositionCache
import com.nhaarman.mockitokotlin2.mock
+import org.junit.After
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import kotlin.random.Random
+import androidx.appcompat.R as AppcompatR
@RunWith(AndroidJUnit4::class)
@LargeTest
class FragmentVisibilityTests {
+ lateinit var idlingResource: LottieIdlingResource
+
@Before
fun setup() {
LottieCompositionCache.getInstance().clear()
+ idlingResource = LottieIdlingResource()
+ IdlingRegistry.getInstance().register(idlingResource)
+ }
+
+ @After
+ fun teardown() {
+ IdlingRegistry.getInstance().unregister(idlingResource)
}
@Test
fun testRecyclerViewCanAutoPlayInOnBindRebind() {
class TestFragment : Fragment() {
- override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
return RecyclerView(requireContext()).apply {
layoutManager = LinearLayoutManager(requireContext(), LinearLayoutManager.VERTICAL, false)
// Setting itemAnimator to null is important for this test in order to
@@ -60,7 +71,6 @@ class FragmentVisibilityTests {
return object : RecyclerView.ViewHolder(LottieAnimationView(parent.context).apply {
id = R.id.animation_view
setAnimation(R.raw.heart)
- IdlingRegistry.getInstance().register(LottieIdlingResource(this))
}) {}
}
@@ -96,10 +106,6 @@ class FragmentVisibilityTests {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.auto_play, container, false)
}
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- IdlingRegistry.getInstance().register(LottieIdlingResource(view.findViewById(R.id.animation_view)))
- }
}
launchFragmentInContainer<TestFragment>()
onView(withId(R.id.animation_view)).check(matches(isAnimating()))
@@ -118,7 +124,6 @@ class FragmentVisibilityTests {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
val animationView = requireView().findViewById<LottieAnimationView>(R.id.animation_view)
animationView.pauseAnimation()
- IdlingRegistry.getInstance().register(LottieIdlingResource(animationView))
}
}
launchFragmentInContainer<TestFragment>()
@@ -131,10 +136,6 @@ class FragmentVisibilityTests {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.auto_play_gone, container, false)
}
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- IdlingRegistry.getInstance().register(LottieIdlingResource(view.findViewById(R.id.animation_view)))
- }
}
val scenario = launchFragmentInContainer<TestFragment>()
@@ -153,12 +154,11 @@ class FragmentVisibilityTests {
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- IdlingRegistry.getInstance().register(LottieIdlingResource(view.findViewById(R.id.animation_view)))
AlertDialog.Builder(requireContext()).setTitle("This is a dialog").show()
}
}
- val scenario = launchFragmentInContainer<TestFragment>(themeResId = R.style.Theme_AppCompat)
+ val scenario = launchFragmentInContainer<TestFragment>(themeResId = AppcompatR.style.Theme_AppCompat)
onIdle()
scenario.onFragment { fragment ->
val animationView = fragment.requireView().findViewById<LottieAnimationView>(R.id.animation_view)
@@ -181,7 +181,6 @@ class FragmentVisibilityTests {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
animationView = view.findViewById(R.id.animation_view)
animationView.addAnimatorListener(animationListener)
- IdlingRegistry.getInstance().register(LottieIdlingResource(animationView))
}
}
@@ -226,7 +225,6 @@ class FragmentVisibilityTests {
repeatMode = LottieDrawable.RESTART
setAnimation(R.raw.heart)
playAnimation()
- IdlingRegistry.getInstance().register(LottieIdlingResource(this))
}
}
}
@@ -244,10 +242,6 @@ class FragmentVisibilityTests {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.no_auto_play, container, false)
}
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- IdlingRegistry.getInstance().register(LottieIdlingResource(view.findViewById(R.id.animation_view)))
- }
}
launchFragmentInContainer<TestFragment>()
onView(withId(R.id.animation_view)).check(matches(isNotAnimating()))
@@ -259,10 +253,6 @@ class FragmentVisibilityTests {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.auto_play, container, false)
}
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- IdlingRegistry.getInstance().register(LottieIdlingResource(view.findViewById(R.id.animation_view)))
- }
}
val scenario = launchFragmentInContainer<TestFragment>()
@@ -287,7 +277,6 @@ class FragmentVisibilityTests {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
val animationView = view.findViewById<LottieAnimationView>(R.id.animation_view)
animationView.playAnimation()
- IdlingRegistry.getInstance().register(LottieIdlingResource(animationView))
}
}
launchFragmentInContainer<TestFragment>()
@@ -304,7 +293,6 @@ class FragmentVisibilityTests {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
val animationView = view.findViewById<LottieAnimationView>(R.id.animation_view)
animationView.playAnimation()
- IdlingRegistry.getInstance().register(LottieIdlingResource(animationView))
}
}
@@ -326,7 +314,6 @@ class FragmentVisibilityTests {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
view.findViewById<View>(R.id.container).isVisible = false
- IdlingRegistry.getInstance().register(LottieIdlingResource(view.findViewById(R.id.animation_view)))
}
}
@@ -373,7 +360,6 @@ class FragmentVisibilityTests {
setAnimation(R.raw.heart)
playAnimation()
animationWasPlayed = true
- IdlingRegistry.getInstance().register(LottieIdlingResource(this, name = "Lottie ${Random.nextFloat()}"))
}
}
}
@@ -435,7 +421,6 @@ class FragmentVisibilityTests {
setAnimation(R.raw.heart)
playAnimation()
animationWasPlayed = true
- IdlingRegistry.getInstance().register(LottieIdlingResource(this, name = "Lottie ${Random.nextFloat()}"))
}
}
}
@@ -469,10 +454,6 @@ class FragmentVisibilityTests {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.auto_play, container, false)
}
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- IdlingRegistry.getInstance().register(LottieIdlingResource(view.findViewById(R.id.animation_view)))
- }
}
val scenario = launchFragmentInContainer<TestFragment>()
diff --git a/sample/src/androidTest/java/com/airbnb/lottie/samples/LottieIdlingResource.kt b/sample/src/androidTest/java/com/airbnb/lottie/samples/LottieIdlingResource.kt
index 42bf31e4..0fdf100c 100644
--- a/sample/src/androidTest/java/com/airbnb/lottie/samples/LottieIdlingResource.kt
+++ b/sample/src/androidTest/java/com/airbnb/lottie/samples/LottieIdlingResource.kt
@@ -1,22 +1,22 @@
package com.airbnb.lottie.samples
-import androidx.test.espresso.IdlingRegistry
import androidx.test.espresso.IdlingResource
-import com.airbnb.lottie.LottieAnimationView
+import com.airbnb.lottie.LottieCompositionFactory
-class LottieIdlingResource(animationView: LottieAnimationView? = null, private val name: String = "Lottie") : IdlingResource {
+class LottieIdlingResource(private val name: String = "Lottie") : IdlingResource {
+
+ private var callback: IdlingResource.ResourceCallback? = null
+ private var isIdle = false
init {
- animationView?.addLottieOnCompositionLoadedListener {
- isIdle = true
- callback?.onTransitionToIdle()
- IdlingRegistry.getInstance().unregister(this)
+ LottieCompositionFactory.registerLottieTaskIdleListener { idle ->
+ isIdle = idle
+ if (idle) {
+ callback?.onTransitionToIdle()
+ }
}
}
- private var callback: IdlingResource.ResourceCallback? = null
- private var isIdle = false
-
override fun getName() = name
override fun isIdleNow() = isIdle
diff --git a/sample/src/main/AndroidManifest.xml b/sample/src/main/AndroidManifest.xml
index f57a13da..7727cfa0 100644
--- a/sample/src/main/AndroidManifest.xml
+++ b/sample/src/main/AndroidManifest.xml
@@ -1,22 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.airbnb.lottie.samples">
+<manifest xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <uses-feature
+ android:name="android.hardware.camera"
+ android:required="false" />
- <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.VIBRATE" />
+ <uses-permission android:name="android.permission.INTERNET" />
+
<application
android:name=".LottieApplication"
- android:allowBackup="false"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity
android:name=".MainActivity"
+ android:exported="true"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@@ -30,17 +34,21 @@
</activity>
<activity
android:name=".TypographyDemoActivity"
+ android:exported="false"
android:screenOrientation="portrait"
android:windowSoftInputMode="stateVisible" />
<activity
android:name=".BullseyeActivity"
+ android:exported="false"
android:screenOrientation="portrait" />
<activity
android:name=".QRScanActivity"
+ android:exported="false"
android:screenOrientation="portrait" />
<activity android:name=".DynamicActivity" />
<activity
android:name=".PlayerActivity"
+ android:exported="true"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
@@ -55,14 +63,19 @@
android:scheme="content" />
</intent-filter>
</activity>
- <activity android:name=".DynamicTextActivity" />
- <activity android:name=".WishListActivity" />
+ <activity
+ android:name=".DynamicTextActivity"
+ android:exported="false" />
+ <activity
+ android:name=".WishListActivity"
+ android:exported="false" />
<activity
android:name=".SimpleAnimationActivity"
android:exported="true" />
- <activity android:name=".EmptyActivity" />
+ <activity
+ android:name=".EmptyActivity"
+ android:exported="false" />
</application>
-
</manifest> \ No newline at end of file
diff --git a/sample/src/main/assets/fonts/Comic Neue.ttf b/sample/src/main/assets/fonts/Comic Neue.ttf
new file mode 100755
index 00000000..ab3a4176
--- /dev/null
+++ b/sample/src/main/assets/fonts/Comic Neue.ttf
Binary files differ
diff --git a/sample/src/main/kotlin/com/airbnb/lottie/samples/LottieApplication.kt b/sample/src/main/kotlin/com/airbnb/lottie/samples/LottieApplication.kt
index 269ae98a..f662c7ac 100644
--- a/sample/src/main/kotlin/com/airbnb/lottie/samples/LottieApplication.kt
+++ b/sample/src/main/kotlin/com/airbnb/lottie/samples/LottieApplication.kt
@@ -2,7 +2,7 @@ package com.airbnb.lottie.samples
import androidx.multidex.MultiDexApplication
import com.airbnb.lottie.L
-import com.airbnb.lottie.samples.api.LottiefilesApi
+import com.airbnb.mvrx.Mavericks
import com.google.gson.FieldNamingPolicy
import com.google.gson.GsonBuilder
import okhttp3.OkHttpClient
@@ -33,12 +33,9 @@ class LottieApplication : MultiDexApplication() {
.build()
}
- val lottiefilesService: LottiefilesApi by lazy { retrofit.create(LottiefilesApi::class.java) }
-
override fun onCreate() {
super.onCreate()
+ Mavericks.initialize(this)
L.DBG = true
- @Suppress("RestrictedApi")
- L.setTraceEnabled(true)
}
-} \ No newline at end of file
+}
diff --git a/sample/src/main/kotlin/com/airbnb/lottie/samples/LottiefilesFragment.kt b/sample/src/main/kotlin/com/airbnb/lottie/samples/LottiefilesFragment.kt
index 9d747dfd..febf5430 100644
--- a/sample/src/main/kotlin/com/airbnb/lottie/samples/LottiefilesFragment.kt
+++ b/sample/src/main/kotlin/com/airbnb/lottie/samples/LottiefilesFragment.kt
@@ -1,166 +1,21 @@
package com.airbnb.lottie.samples
-import android.content.Context
+import android.content.Intent
+import android.net.Uri
import android.os.Bundle
import android.view.View
-import android.view.ViewGroup
-import androidx.core.view.isVisible
-import androidx.lifecycle.lifecycleScope
-import androidx.lifecycle.viewModelScope
-import androidx.paging.Pager
-import androidx.paging.PagingConfig
-import androidx.paging.PagingDataAdapter
-import androidx.paging.PagingSource
-import androidx.paging.PagingState
-import androidx.paging.cachedIn
-import androidx.recyclerview.widget.DiffUtil
-import androidx.recyclerview.widget.RecyclerView
-import com.airbnb.lottie.samples.api.LottiefilesApi
+import androidx.fragment.app.Fragment
import com.airbnb.lottie.samples.databinding.LottiefilesFragmentBinding
-import com.airbnb.lottie.samples.model.AnimationData
-import com.airbnb.lottie.samples.model.AnimationResponse
-import com.airbnb.lottie.samples.model.CompositionArgs
-import com.airbnb.lottie.samples.utils.MvRxViewModel
-import com.airbnb.lottie.samples.utils.hideKeyboard
import com.airbnb.lottie.samples.utils.viewBinding
-import com.airbnb.lottie.samples.views.AnimationItemView
-import com.airbnb.mvrx.BaseMvRxFragment
-import com.airbnb.mvrx.MvRxState
-import com.airbnb.mvrx.MvRxViewModelFactory
-import com.airbnb.mvrx.ViewModelContext
-import com.airbnb.mvrx.fragmentViewModel
-import com.airbnb.mvrx.withState
-import kotlinx.coroutines.flow.collectLatest
-import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.flow.onEach
-import kotlinx.coroutines.launch
-data class LottiefilesState(
- val mode: LottiefilesMode = LottiefilesMode.Recent,
- val query: String = ""
-) : MvRxState
-class LottiefilesViewModel(initialState: LottiefilesState, private val api: LottiefilesApi) : MvRxViewModel<LottiefilesState>(initialState) {
-
- private var mode = initialState.mode
- private var query = initialState.query
-
- private var dataSource: LottiefilesDataSource? = null
- val pager = Pager(PagingConfig(pageSize = 16)) {
- LottiefilesDataSource(api, mode, query).also { dataSource = it }
- }.flow.cachedIn(viewModelScope)
-
- init {
- selectSubscribe(LottiefilesState::mode, LottiefilesState::query) { mode, query ->
- this.mode = mode
- this.query = query
- dataSource?.invalidate()
- }
- }
-
- fun setMode(mode: LottiefilesMode) = setState { copy(mode = mode) }
-
- fun setQuery(query: String) = setState { copy(query = query) }
-
- companion object : MvRxViewModelFactory<LottiefilesViewModel, LottiefilesState> {
- override fun create(viewModelContext: ViewModelContext, state: LottiefilesState): LottiefilesViewModel {
- val service = viewModelContext.app<LottieApplication>().lottiefilesService
- return LottiefilesViewModel(state, service)
- }
- }
-}
-
-class LottiefilesDataSource(
- private val api: LottiefilesApi,
- val mode: LottiefilesMode,
- private val query: String
-) : PagingSource<Int, AnimationData>() {
-
- override suspend fun load(params: LoadParams<Int>): LoadResult<Int, AnimationData> {
- val page = params.key ?: 1
- return try {
- val response = when (mode) {
- LottiefilesMode.Popular -> api.getPopular(page)
- LottiefilesMode.Recent -> api.getRecent(page)
- LottiefilesMode.Search -> {
- if (query.isBlank()) {
- AnimationResponse(page, emptyList(), "", page, null, "", 0, "", 0, 0)
- } else {
- api.search(query, page)
- }
- }
- }
-
- LoadResult.Page(
- response.data,
- if (page == 1) null else page + 1,
- (page + 1).takeIf { page < response.lastPage }
- )
- } catch (e: Exception) {
- LoadResult.Error(e)
- }
- }
-
- override fun getRefreshKey(state: PagingState<Int, AnimationData>): Int? {
- return state.closestItemToPosition(state.anchorPosition ?: return null)?.id as Int?
- }
-}
-
-class LottiefilesFragment : BaseMvRxFragment(R.layout.lottiefiles_fragment) {
+class LottiefilesFragment : Fragment(R.layout.lottiefiles_fragment) {
private val binding: LottiefilesFragmentBinding by viewBinding()
- private val viewModel: LottiefilesViewModel by fragmentViewModel()
-
- private object AnimationItemDataDiffCallback : DiffUtil.ItemCallback<AnimationData>() {
- override fun areItemsTheSame(oldItem: AnimationData, newItem: AnimationData) = oldItem.id == newItem.id
-
- override fun areContentsTheSame(oldItem: AnimationData, newItem: AnimationData) = oldItem == newItem
- }
-
- private class AnimationItemViewHolder(context: Context) : RecyclerView.ViewHolder(AnimationItemView(context)) {
- fun bind(data: AnimationData?) {
- val view = itemView as AnimationItemView
- view.setTitle(data?.title)
- view.setPreviewUrl(data?.preview)
- view.setPreviewBackgroundColor(data?.bgColorInt)
- view.setOnClickListener {
- val intent = PlayerActivity.intent(view.context, CompositionArgs(animationData = data))
- view.context.startActivity(intent)
- }
- }
- }
-
-
- private val adapter = object : PagingDataAdapter<AnimationData, AnimationItemViewHolder>(AnimationItemDataDiffCallback) {
- override fun onBindViewHolder(holder: AnimationItemViewHolder, position: Int) = holder.bind(getItem(position))
-
- override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = AnimationItemViewHolder(parent.context)
-
- }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- binding.recyclerView.adapter = adapter
- viewLifecycleOwner.lifecycleScope.launch {
- viewModel.pager.collectLatest(adapter::submitData)
- }
- binding.tabBar.setRecentClickListener {
- viewModel.setMode(LottiefilesMode.Recent)
- requireContext().hideKeyboard()
- }
- binding.tabBar.setPopularClickListener {
- viewModel.setMode(LottiefilesMode.Popular)
- requireContext().hideKeyboard()
+ binding.getItOnPlay.setOnClickListener {
+ val uri = Uri.parse("https://play.google.com/store/apps/details?id=com.lottiefiles.LottiePreview")
+ startActivity(Intent(Intent.ACTION_VIEW, uri))
}
- binding.tabBar.setSearchClickListener {
- viewModel.setMode(LottiefilesMode.Search)
- requireContext().hideKeyboard()
- }
- binding.searchView.query.onEach { query ->
- viewModel.setQuery(query)
- }.launchIn(viewLifecycleOwner.lifecycleScope)
- }
-
- override fun invalidate(): Unit = withState(viewModel) { state ->
- binding.searchView.isVisible = state.mode == LottiefilesMode.Search
- binding.tabBar.setMode(state.mode)
}
-} \ No newline at end of file
+}
diff --git a/sample/src/main/kotlin/com/airbnb/lottie/samples/LottiefilesMode.kt b/sample/src/main/kotlin/com/airbnb/lottie/samples/LottiefilesMode.kt
deleted file mode 100644
index 6b27d219..00000000
--- a/sample/src/main/kotlin/com/airbnb/lottie/samples/LottiefilesMode.kt
+++ /dev/null
@@ -1,7 +0,0 @@
-package com.airbnb.lottie.samples
-
-enum class LottiefilesMode {
- Popular,
- Recent,
- Search
-} \ No newline at end of file
diff --git a/sample/src/main/kotlin/com/airbnb/lottie/samples/MainActivity.kt b/sample/src/main/kotlin/com/airbnb/lottie/samples/MainActivity.kt
index 2c5e89a4..51b489b8 100644
--- a/sample/src/main/kotlin/com/airbnb/lottie/samples/MainActivity.kt
+++ b/sample/src/main/kotlin/com/airbnb/lottie/samples/MainActivity.kt
@@ -6,6 +6,7 @@ import androidx.appcompat.app.AppCompatDelegate
import androidx.browser.customtabs.CustomTabsIntent
import androidx.core.net.toUri
import androidx.fragment.app.Fragment
+import androidx.fragment.app.commit
import com.airbnb.lottie.samples.databinding.MainActivityBinding
import com.airbnb.lottie.samples.utils.viewBinding
@@ -17,16 +18,16 @@ class MainActivity : AppCompatActivity() {
super.onCreate(savedInstanceState)
binding.bottomNavigation.setOnItemSelectedListener listener@{ item ->
when (item.itemId) {
- R.id.showcase -> showFragment(ShowcaseFragment())
R.id.preview -> showFragment(PreviewFragment())
R.id.lottiefiles -> showFragment(LottiefilesFragment())
R.id.learn -> showShowcase()
}
true
}
+ binding.bottomNavigation.itemIconTintList = null
if (savedInstanceState == null) {
- showFragment(ShowcaseFragment())
+ showFragment(PreviewFragment())
}
}
@@ -36,8 +37,8 @@ class MainActivity : AppCompatActivity() {
}
private fun showFragment(fragment: Fragment) {
- supportFragmentManager.beginTransaction()
- .replace(R.id.content, fragment)
- .commit()
+ supportFragmentManager.commit {
+ replace(R.id.content, fragment)
+ }
}
}
diff --git a/sample/src/main/kotlin/com/airbnb/lottie/samples/OnPageChangeListenerAdapter.kt b/sample/src/main/kotlin/com/airbnb/lottie/samples/OnPageChangeListenerAdapter.kt
deleted file mode 100644
index 80a6a7ff..00000000
--- a/sample/src/main/kotlin/com/airbnb/lottie/samples/OnPageChangeListenerAdapter.kt
+++ /dev/null
@@ -1,18 +0,0 @@
-package com.airbnb.lottie.samples
-
-import androidx.viewpager.widget.ViewPager
-
-internal open class OnPageChangeListenerAdapter(
- private val onPageScrollStateChanged: ((state: Int) -> Unit)? = null,
- private val onPageScrolled:
- ((position: Int, positionOffset: Float, positionOffsetPixels: Int) -> Unit)? = null,
- private val onPageSelected: ((position: Int) -> Unit)? = null
-) : ViewPager.OnPageChangeListener {
- override fun onPageScrollStateChanged(state: Int) =
- onPageScrollStateChanged?.invoke(state) ?: Unit
-
- override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) =
- onPageScrolled?.invoke(position, positionOffset, positionOffsetPixels) ?: Unit
-
- override fun onPageSelected(position: Int) = onPageSelected?.invoke(position) ?: Unit
-} \ No newline at end of file
diff --git a/sample/src/main/kotlin/com/airbnb/lottie/samples/PlayerActivity.kt b/sample/src/main/kotlin/com/airbnb/lottie/samples/PlayerActivity.kt
index 3059b4c9..b91c9544 100644
--- a/sample/src/main/kotlin/com/airbnb/lottie/samples/PlayerActivity.kt
+++ b/sample/src/main/kotlin/com/airbnb/lottie/samples/PlayerActivity.kt
@@ -5,6 +5,7 @@ import android.content.Intent
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.airbnb.lottie.samples.model.CompositionArgs
+import com.airbnb.lottie.samples.utils.getParcelableExtraCompat
class PlayerActivity : AppCompatActivity(R.layout.player_activity) {
@@ -12,7 +13,9 @@ class PlayerActivity : AppCompatActivity(R.layout.player_activity) {
super.onCreate(savedInstanceState)
if (savedInstanceState == null) {
- val args = intent.getParcelableExtra(PlayerFragment.EXTRA_ANIMATION_ARGS) ?: CompositionArgs(fileUri = intent.data)
+ val args =
+ intent.getParcelableExtraCompat(PlayerFragment.EXTRA_ANIMATION_ARGS, CompositionArgs::class.java)
+ ?: CompositionArgs(fileUri = intent.data)
supportFragmentManager.beginTransaction()
.add(R.id.content, PlayerFragment.forAsset(args))
.commit()
diff --git a/sample/src/main/kotlin/com/airbnb/lottie/samples/PlayerFragment.kt b/sample/src/main/kotlin/com/airbnb/lottie/samples/PlayerFragment.kt
index e715f5fa..eea0a1f4 100644
--- a/sample/src/main/kotlin/com/airbnb/lottie/samples/PlayerFragment.kt
+++ b/sample/src/main/kotlin/com/airbnb/lottie/samples/PlayerFragment.kt
@@ -14,6 +14,8 @@ import android.view.View
import android.widget.EditText
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
+import androidx.core.view.MenuHost
+import androidx.core.view.MenuProvider
import androidx.core.view.children
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
@@ -30,11 +32,12 @@ import com.airbnb.lottie.RenderMode
import com.airbnb.lottie.model.KeyPath
import com.airbnb.lottie.samples.databinding.PlayerFragmentBinding
import com.airbnb.lottie.samples.model.CompositionArgs
+import com.airbnb.lottie.samples.utils.BaseFragment
+import com.airbnb.lottie.samples.utils.getParcelableCompat
import com.airbnb.lottie.samples.utils.viewBinding
import com.airbnb.lottie.samples.views.BottomSheetItemView
import com.airbnb.lottie.samples.views.BottomSheetItemViewModel_
import com.airbnb.lottie.samples.views.ControlBarItemToggleView
-import com.airbnb.mvrx.BaseMvRxFragment
import com.airbnb.mvrx.fragmentViewModel
import com.airbnb.mvrx.withState
import com.github.mikephil.charting.components.LimitLine
@@ -47,7 +50,7 @@ import com.google.android.material.snackbar.Snackbar
import kotlin.math.abs
import kotlin.math.roundToInt
-class PlayerFragment : BaseMvRxFragment(R.layout.player_fragment) {
+class PlayerFragment : BaseFragment(R.layout.player_fragment) {
private val binding: PlayerFragmentBinding by viewBinding()
private val viewModel: PlayerViewModel by fragmentViewModel()
@@ -100,33 +103,40 @@ class PlayerFragment : BaseMvRxFragment(R.layout.player_fragment) {
super.onViewCreated(view, savedInstanceState)
(requireActivity() as AppCompatActivity).setSupportActionBar(binding.toolbar)
(requireActivity() as AppCompatActivity).supportActionBar?.setDisplayShowTitleEnabled(false)
- setHasOptionsMenu(true)
+ (requireActivity() as MenuHost).addMenuProvider(object : MenuProvider {
+
+ override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
+ menuInflater.inflate(R.menu.fragment_player, menu)
+ }
+
+ override fun onMenuItemSelected(item: MenuItem): Boolean {
+ if (item.isCheckable) item.isChecked = !item.isChecked
+ when (item.itemId) {
+ android.R.id.home -> requireActivity().finish()
+ R.id.visibility -> {
+ viewModel.setDistractionFree(item.isChecked)
+ val menuIcon = if (item.isChecked) R.drawable.ic_eye_teal else R.drawable.ic_eye_selector
+ item.icon = ContextCompat.getDrawable(requireContext(), menuIcon)
+ }
+ }
+ return true
+ }
+ }, viewLifecycleOwner, Lifecycle.State.RESUMED)
binding.controlBarPlayerControls.lottieVersionView.text = getString(R.string.lottie_version, BuildConfig.VERSION_NAME)
binding.animationView.setFontAssetDelegate(object : FontAssetDelegate() {
- override fun fetchFont(fontFamily: String?): Typeface {
+ override fun fetchFont(fontFamily: String?, fontStyle: String?, fontName: String?): Typeface {
return Typeface.DEFAULT
}
})
- val args = arguments?.getParcelable<CompositionArgs>(EXTRA_ANIMATION_ARGS)
+ val args = arguments?.getParcelableCompat(EXTRA_ANIMATION_ARGS, CompositionArgs::class.java)
?: throw IllegalArgumentException("No composition args specified")
- args.animationData?.bgColorInt?.let {
- binding.controlBarBackgroundColor.backgroundButton1.setBackgroundColor(it)
- binding.animationContainer.setBackgroundColor(it)
- invertColor(it)
- }
-
- args.animationDataV2?.bgColorInt?.let {
- binding.controlBarBackgroundColor.backgroundButton1.setBackgroundColor(it)
- binding.animationContainer.setBackgroundColor(it)
- invertColor(it)
- }
binding.controlBarTrim.minFrameView.setOnClickListener { showMinFrameDialog() }
binding.controlBarTrim.maxFrameView.setOnClickListener { showMaxFrameDialog() }
- viewModel.selectSubscribe(PlayerState::minFrame, PlayerState::maxFrame) { minFrame, maxFrame ->
+ viewModel.onEach(PlayerState::minFrame, PlayerState::maxFrame) { minFrame, maxFrame ->
binding.animationView.setMinAndMaxFrame(minFrame, maxFrame)
// I think this is a lint bug. It complains about int being <ErrorType>
//noinspection StringFormatMatches
@@ -136,7 +146,7 @@ class PlayerFragment : BaseMvRxFragment(R.layout.player_fragment) {
}
viewModel.fetchAnimation(args)
- viewModel.asyncSubscribe(PlayerState::composition, onFail = {
+ viewModel.onAsync(PlayerState::composition, onFail = {
Snackbar.make(binding.coordinatorLayout, R.string.composition_load_error, Snackbar.LENGTH_LONG).show()
Log.w(L.TAG, "Error loading composition.", it)
}) {
@@ -145,7 +155,7 @@ class PlayerFragment : BaseMvRxFragment(R.layout.player_fragment) {
}
binding.controlBar.borderToggle.setOnClickListener { viewModel.toggleBorderVisible() }
- viewModel.selectSubscribe(PlayerState::borderVisible) {
+ viewModel.onEach(PlayerState::borderVisible) {
binding.controlBar.borderToggle.isActivated = it
binding.controlBar.borderToggle.setImageResource(
if (it) R.drawable.ic_border_on
@@ -170,12 +180,12 @@ class PlayerFragment : BaseMvRxFragment(R.layout.player_fragment) {
binding.controlBar.enableApplyingOpacityToLayers.isActivated = isApplyingOpacityToLayersEnabled
}
- viewModel.selectSubscribe(PlayerState::controlsVisible) { binding.controlBarPlayerControls.controlsContainer.animateVisible(it) }
+ viewModel.onEach(PlayerState::controlsVisible) { binding.controlBarPlayerControls.controlsContainer.animateVisible(it) }
- viewModel.selectSubscribe(PlayerState::controlBarVisible) { binding.controlBar.root.animateVisible(it) }
+ viewModel.onEach(PlayerState::controlBarVisible) { binding.controlBar.root.animateVisible(it) }
binding.controlBar.renderGraphToggle.setOnClickListener { viewModel.toggleRenderGraphVisible() }
- viewModel.selectSubscribe(PlayerState::renderGraphVisible) {
+ viewModel.onEach(PlayerState::renderGraphVisible) {
binding.controlBar.renderGraphToggle.isActivated = it
binding.controlBarPlayerControls.renderTimesGraphContainer.animateVisible(it)
binding.controlBarPlayerControls.renderTimesPerLayerButton.animateVisible(it)
@@ -183,38 +193,38 @@ class PlayerFragment : BaseMvRxFragment(R.layout.player_fragment) {
}
binding.controlBar.masksAndMattesToggle.setOnClickListener { viewModel.toggleOutlineMasksAndMattes() }
- viewModel.selectSubscribe(PlayerState::outlineMasksAndMattes) {
+ viewModel.onEach(PlayerState::outlineMasksAndMattes) {
binding.controlBar.masksAndMattesToggle.isActivated = it
binding.animationView.setOutlineMasksAndMattes(it)
}
binding.controlBar.backgroundColorToggle.setOnClickListener { viewModel.toggleBackgroundColorVisible() }
binding.controlBarBackgroundColor.closeBackgroundColorButton.setOnClickListener { viewModel.setBackgroundColorVisible(false) }
- viewModel.selectSubscribe(PlayerState::backgroundColorVisible) {
+ viewModel.onEach(PlayerState::backgroundColorVisible) {
binding.controlBar.backgroundColorToggle.isActivated = it
binding.controlBarBackgroundColor.backgroundColorContainer.animateVisible(it)
}
binding.controlBar.trimToggle.setOnClickListener { viewModel.toggleTrimVisible() }
binding.controlBarTrim.closeTrimButton.setOnClickListener { viewModel.setTrimVisible(false) }
- viewModel.selectSubscribe(PlayerState::trimVisible) {
+ viewModel.onEach(PlayerState::trimVisible) {
binding.controlBar.trimToggle.isActivated = it
binding.controlBarTrim.trimContainer.animateVisible(it)
}
binding.controlBar.mergePathsToggle.setOnClickListener { viewModel.toggleMergePaths() }
- viewModel.selectSubscribe(PlayerState::useMergePaths) {
+ viewModel.onEach(PlayerState::useMergePaths) {
binding.animationView.enableMergePathsForKitKatAndAbove(it)
binding.controlBar.mergePathsToggle.isActivated = it
}
binding.controlBar.speedToggle.setOnClickListener { viewModel.toggleSpeedVisible() }
binding.controlBarSpeed.closeSpeedButton.setOnClickListener { viewModel.setSpeedVisible(false) }
- viewModel.selectSubscribe(PlayerState::speedVisible) {
+ viewModel.onEach(PlayerState::speedVisible) {
binding.controlBar.speedToggle.isActivated = it
binding.controlBarSpeed.speedContainer.isVisible = it
}
- viewModel.selectSubscribe(PlayerState::speed) {
+ viewModel.onEach(PlayerState::speed) {
binding.animationView.speed = it
binding.controlBarSpeed.speedButtonsContainer
.children
@@ -238,7 +248,7 @@ class PlayerFragment : BaseMvRxFragment(R.layout.player_fragment) {
binding.controlBarPlayerControls.loopButton.setOnClickListener { viewModel.toggleLoop() }
- viewModel.selectSubscribe(PlayerState::repeatCount) {
+ viewModel.onEach(PlayerState::repeatCount) {
binding.animationView.repeatCount = it
binding.controlBarPlayerControls.loopButton.isActivated = binding.animationView.repeatCount == ValueAnimator.INFINITE
}
@@ -394,25 +404,6 @@ class PlayerFragment : BaseMvRxFragment(R.layout.player_fragment) {
super.onDestroyView()
}
- override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
- inflater.inflate(R.menu.fragment_player, menu)
- super.onCreateOptionsMenu(menu, inflater)
- }
-
- override fun onOptionsItemSelected(item: MenuItem): Boolean {
- if (item.isCheckable) item.isChecked = !item.isChecked
- when (item.itemId) {
- android.R.id.home -> requireActivity().finish()
- R.id.info -> Unit
- R.id.visibility -> {
- viewModel.setDistractionFree(item.isChecked)
- val menuIcon = if (item.isChecked) R.drawable.ic_eye_teal else R.drawable.ic_eye_selector
- item.icon = ContextCompat.getDrawable(requireContext(), menuIcon)
- }
- }
- return true
- }
-
private fun onCompositionLoaded(composition: LottieComposition?) {
composition ?: return
diff --git a/sample/src/main/kotlin/com/airbnb/lottie/samples/PlayerViewModel.kt b/sample/src/main/kotlin/com/airbnb/lottie/samples/PlayerViewModel.kt
index 592573e8..d68bb363 100644
--- a/sample/src/main/kotlin/com/airbnb/lottie/samples/PlayerViewModel.kt
+++ b/sample/src/main/kotlin/com/airbnb/lottie/samples/PlayerViewModel.kt
@@ -7,11 +7,11 @@ import com.airbnb.lottie.LottieComposition
import com.airbnb.lottie.LottieCompositionFactory
import com.airbnb.lottie.LottieTask
import com.airbnb.lottie.samples.model.CompositionArgs
-import com.airbnb.lottie.samples.utils.MvRxViewModel
import com.airbnb.mvrx.Async
import com.airbnb.mvrx.Fail
-import com.airbnb.mvrx.MvRxState
-import com.airbnb.mvrx.MvRxViewModelFactory
+import com.airbnb.mvrx.MavericksState
+import com.airbnb.mvrx.MavericksViewModel
+import com.airbnb.mvrx.MavericksViewModelFactory
import com.airbnb.mvrx.Success
import com.airbnb.mvrx.Uninitialized
import com.airbnb.mvrx.ViewModelContext
@@ -34,15 +34,15 @@ data class PlayerState(
val maxFrame: Int = 0,
val speed: Float = 1f,
val repeatCount: Int = ValueAnimator.INFINITE
-) : MvRxState
+) : MavericksState
class PlayerViewModel(
initialState: PlayerState,
private val application: Application
-) : MvRxViewModel<PlayerState>(initialState) {
+) : MavericksViewModel<PlayerState>(initialState) {
fun fetchAnimation(args: CompositionArgs) {
- val url = args.url ?: args.animationDataV2?.file ?: args.animationData?.lottieLink
+ val url = args.url
when {
url != null -> LottieCompositionFactory.fromUrl(application, url, null)
@@ -112,9 +112,9 @@ class PlayerViewModel(
)
}
- companion object : MvRxViewModelFactory<PlayerViewModel, PlayerState> {
+ companion object : MavericksViewModelFactory<PlayerViewModel, PlayerState> {
override fun create(viewModelContext: ViewModelContext, state: PlayerState): PlayerViewModel {
return PlayerViewModel(state, viewModelContext.app())
}
}
-} \ No newline at end of file
+}
diff --git a/sample/src/main/kotlin/com/airbnb/lottie/samples/PreviewFragment.kt b/sample/src/main/kotlin/com/airbnb/lottie/samples/PreviewFragment.kt
index 96b4f093..ebeb2c99 100644
--- a/sample/src/main/kotlin/com/airbnb/lottie/samples/PreviewFragment.kt
+++ b/sample/src/main/kotlin/com/airbnb/lottie/samples/PreviewFragment.kt
@@ -115,4 +115,4 @@ class PreviewFragment : BaseEpoxyFragment() {
}
}
}
-} \ No newline at end of file
+}
diff --git a/sample/src/main/kotlin/com/airbnb/lottie/samples/ShowcaseFragment.kt b/sample/src/main/kotlin/com/airbnb/lottie/samples/ShowcaseFragment.kt
deleted file mode 100644
index 8a58ccc4..00000000
--- a/sample/src/main/kotlin/com/airbnb/lottie/samples/ShowcaseFragment.kt
+++ /dev/null
@@ -1,90 +0,0 @@
-package com.airbnb.lottie.samples
-
-import android.content.Intent
-import com.airbnb.epoxy.EpoxyController
-import com.airbnb.lottie.samples.api.LottiefilesApi
-import com.airbnb.lottie.samples.model.AnimationResponseV2
-import com.airbnb.lottie.samples.model.CompositionArgs
-import com.airbnb.lottie.samples.model.ShowcaseItem
-import com.airbnb.lottie.samples.utils.BaseEpoxyFragment
-import com.airbnb.lottie.samples.utils.MvRxViewModel
-import com.airbnb.lottie.samples.views.animationItemView
-import com.airbnb.lottie.samples.views.loadingView
-import com.airbnb.lottie.samples.views.marquee
-import com.airbnb.lottie.samples.views.showcaseCarousel
-import com.airbnb.mvrx.Async
-import com.airbnb.mvrx.MvRxState
-import com.airbnb.mvrx.MvRxViewModelFactory
-import com.airbnb.mvrx.Uninitialized
-import com.airbnb.mvrx.ViewModelContext
-import com.airbnb.mvrx.fragmentViewModel
-import com.airbnb.mvrx.withState
-
-data class ShowcaseState(val response: Async<AnimationResponseV2> = Uninitialized) : MvRxState
-
-class ShowcaseViewModel(initialState: ShowcaseState, api: LottiefilesApi) : MvRxViewModel<ShowcaseState>(initialState) {
- init {
- suspend {
- api.getCollection()
- }.execute { copy(response = it) }
- }
-
- companion object : MvRxViewModelFactory<ShowcaseViewModel, ShowcaseState> {
- override fun create(viewModelContext: ViewModelContext, state: ShowcaseState): ShowcaseViewModel {
- val service = viewModelContext.app<LottieApplication>().lottiefilesService
- return ShowcaseViewModel(state, service)
- }
- }
-}
-
-class ShowcaseFragment : BaseEpoxyFragment() {
-
- private val showcaseItems = listOf(
- ShowcaseItem(R.drawable.showcase_preview_lottie, R.string.showcase_item_dynamic_properties) {
- it.startActivity(Intent(it, DynamicActivity::class.java))
- },
- ShowcaseItem(R.drawable.gilbert_animated, R.string.showcase_item_animated_text) {
- it.startActivity(Intent(it, TypographyDemoActivity::class.java))
- },
- ShowcaseItem(R.drawable.gilbert_animated, R.string.showcase_item_dynamic_text) {
- it.startActivity(Intent(it, DynamicTextActivity::class.java))
- },
- ShowcaseItem(R.drawable.showcase_preview_lottie, R.string.showcase_item_bullseye) {
- it.startActivity(Intent(it, BullseyeActivity::class.java))
- },
- ShowcaseItem(R.drawable.showcase_preview_lottie, R.string.showcase_item_recycler_view) {
- it.startActivity(Intent(it, WishListActivity::class.java))
- }
- )
- private val viewModel: ShowcaseViewModel by fragmentViewModel()
-
- override fun EpoxyController.buildModels() = withState(viewModel) { state ->
- marquee {
- id("showcase")
- title("Showcase")
- }
- showcaseCarousel {
- id("carousel")
- showcaseItems(showcaseItems)
- }
-
- val collectionItems = state.response()?.data
-
- if (collectionItems == null) {
- loadingView {
- id("loading")
- }
- } else {
- collectionItems.forEach {
- val activityContext = requireActivity()
- animationItemView {
- id(it.id)
- title(it.title)
- if (it.preview != null) previewUrl("https://assets9.lottiefiles.com/${it.preview}")
- previewBackgroundColor(it.bgColorInt)
- onClickListener { _ -> activityContext.startActivity(PlayerActivity.intent(activityContext, CompositionArgs(animationDataV2 = it))) }
- }
- }
- }
- }
-} \ No newline at end of file
diff --git a/sample/src/main/kotlin/com/airbnb/lottie/samples/api/LottiefilesApi.kt b/sample/src/main/kotlin/com/airbnb/lottie/samples/api/LottiefilesApi.kt
deleted file mode 100644
index 8f26bc81..00000000
--- a/sample/src/main/kotlin/com/airbnb/lottie/samples/api/LottiefilesApi.kt
+++ /dev/null
@@ -1,21 +0,0 @@
-package com.airbnb.lottie.samples.api
-
-import com.airbnb.lottie.samples.model.AnimationResponse
-import com.airbnb.lottie.samples.model.AnimationResponseV2
-import retrofit2.http.GET
-import retrofit2.http.Path
-import retrofit2.http.Query
-
-interface LottiefilesApi {
- @GET("v1/recent")
- suspend fun getRecent(@Query("page") page: Int): AnimationResponse
-
- @GET("v1/popular")
- suspend fun getPopular(@Query("page") page: Int): AnimationResponse
-
- @GET("v2/featured")
- suspend fun getCollection(): AnimationResponseV2
-
- @GET("v1/search/{query}")
- suspend fun search(@Path("query") query: String, @Query("page") page: Int): AnimationResponse
-} \ No newline at end of file
diff --git a/sample/src/main/kotlin/com/airbnb/lottie/samples/model/AnimationData.kt b/sample/src/main/kotlin/com/airbnb/lottie/samples/model/AnimationData.kt
deleted file mode 100644
index 07194dee..00000000
--- a/sample/src/main/kotlin/com/airbnb/lottie/samples/model/AnimationData.kt
+++ /dev/null
@@ -1,18 +0,0 @@
-package com.airbnb.lottie.samples.model
-
-import android.os.Parcelable
-import com.airbnb.lottie.samples.utils.toColorIntSafe
-import kotlinx.parcelize.Parcelize
-
-@Parcelize
-data class AnimationData(
- val id: Long,
- val title: String,
- val description: String?,
- private val bgColor: String?,
- val preview: String?,
- val lottieLink: String,
- val userInfo: UserInfo?
-) : Parcelable {
- val bgColorInt get() = bgColor.toColorIntSafe()
-} \ No newline at end of file
diff --git a/sample/src/main/kotlin/com/airbnb/lottie/samples/model/AnimationDataV2.kt b/sample/src/main/kotlin/com/airbnb/lottie/samples/model/AnimationDataV2.kt
deleted file mode 100644
index fa810355..00000000
--- a/sample/src/main/kotlin/com/airbnb/lottie/samples/model/AnimationDataV2.kt
+++ /dev/null
@@ -1,17 +0,0 @@
-package com.airbnb.lottie.samples.model
-
-import android.os.Parcelable
-import com.airbnb.lottie.samples.utils.toColorIntSafe
-import com.google.gson.annotations.SerializedName
-import kotlinx.parcelize.Parcelize
-
-@Parcelize
-data class AnimationDataV2(
- @SerializedName("bg_color") val bgColor: String = "",
- @SerializedName("file") val file: String = "",
- @SerializedName("id") val id: Int,
- @SerializedName("preview") val preview: String? = "",
- @SerializedName("title") val title: String = "",
-) : Parcelable {
- val bgColorInt get() = bgColor.toColorIntSafe()
-} \ No newline at end of file
diff --git a/sample/src/main/kotlin/com/airbnb/lottie/samples/model/AnimationResponse.kt b/sample/src/main/kotlin/com/airbnb/lottie/samples/model/AnimationResponse.kt
deleted file mode 100644
index d23991e0..00000000
--- a/sample/src/main/kotlin/com/airbnb/lottie/samples/model/AnimationResponse.kt
+++ /dev/null
@@ -1,20 +0,0 @@
-package com.airbnb.lottie.samples.model
-
-import android.os.Parcelable
-import kotlinx.parcelize.Parcelize
-
-// This is a lint bug
-@SuppressWarnings("ParcelCreator")
-@Parcelize
-data class AnimationResponse(
- val currentPage: Int,
- val data: List<AnimationData>,
- val from: String,
- val lastPage: Int,
- val nextPageUrl: String?,
- val path: String,
- val perPage: Int,
- val prevPageUrl: String,
- val to: Int,
- val total: Int
-) : Parcelable \ No newline at end of file
diff --git a/sample/src/main/kotlin/com/airbnb/lottie/samples/model/AnimationResponseV2.kt b/sample/src/main/kotlin/com/airbnb/lottie/samples/model/AnimationResponseV2.kt
deleted file mode 100644
index 3a8381d2..00000000
--- a/sample/src/main/kotlin/com/airbnb/lottie/samples/model/AnimationResponseV2.kt
+++ /dev/null
@@ -1,5 +0,0 @@
-package com.airbnb.lottie.samples.model
-
-data class AnimationResponseV2(
- val data: List<AnimationDataV2> = emptyList(),
-) \ No newline at end of file
diff --git a/sample/src/main/kotlin/com/airbnb/lottie/samples/model/CompositionArgs.kt b/sample/src/main/kotlin/com/airbnb/lottie/samples/model/CompositionArgs.kt
index 61d461e7..a538cd13 100644
--- a/sample/src/main/kotlin/com/airbnb/lottie/samples/model/CompositionArgs.kt
+++ b/sample/src/main/kotlin/com/airbnb/lottie/samples/model/CompositionArgs.kt
@@ -13,9 +13,4 @@ data class CompositionArgs(
val url: String? = null,
val fileUri: Uri? = null,
val asset: String? = null,
- val animationData: AnimationData? = null,
- val animationDataV2: AnimationDataV2? = null
-) : Parcelable {
- @IgnoredOnParcel
- val isJson = (url ?: animationData?.lottieLink ?: animationDataV2?.file)?.endsWith("json") == true
-} \ No newline at end of file
+) : Parcelable
diff --git a/sample/src/main/kotlin/com/airbnb/lottie/samples/model/ShowcaseItem.kt b/sample/src/main/kotlin/com/airbnb/lottie/samples/model/ShowcaseItem.kt
deleted file mode 100644
index f27d9667..00000000
--- a/sample/src/main/kotlin/com/airbnb/lottie/samples/model/ShowcaseItem.kt
+++ /dev/null
@@ -1,11 +0,0 @@
-package com.airbnb.lottie.samples.model
-
-import android.content.Context
-import androidx.annotation.DrawableRes
-import androidx.annotation.StringRes
-
-data class ShowcaseItem(
- @DrawableRes val drawableRes: Int,
- @StringRes val titleRes: Int,
- val clickListener: (context: Context) -> Unit
-) \ No newline at end of file
diff --git a/sample/src/main/kotlin/com/airbnb/lottie/samples/model/UserInfo.kt b/sample/src/main/kotlin/com/airbnb/lottie/samples/model/UserInfo.kt
deleted file mode 100644
index c6ef9124..00000000
--- a/sample/src/main/kotlin/com/airbnb/lottie/samples/model/UserInfo.kt
+++ /dev/null
@@ -1,19 +0,0 @@
-package com.airbnb.lottie.samples.model
-
-import android.os.Parcelable
-import kotlinx.parcelize.Parcelize
-
-// This is a lint bug
-@SuppressWarnings("ParcelCreator")
-@Parcelize
-data class UserInfo(
- val id: Long,
- val name: String,
- val bio: String?,
- val location: String?,
- val city: String?,
- val social_twitter: String?,
- val social_dribbble: String?,
- val social_behance: String?,
- val url: String?
-) : Parcelable \ No newline at end of file
diff --git a/sample/src/main/kotlin/com/airbnb/lottie/samples/utils/BaseEpoxyFragment.kt b/sample/src/main/kotlin/com/airbnb/lottie/samples/utils/BaseEpoxyFragment.kt
index 805cf4cd..8fa54693 100644
--- a/sample/src/main/kotlin/com/airbnb/lottie/samples/utils/BaseEpoxyFragment.kt
+++ b/sample/src/main/kotlin/com/airbnb/lottie/samples/utils/BaseEpoxyFragment.kt
@@ -6,8 +6,6 @@ import com.airbnb.epoxy.AsyncEpoxyController
import com.airbnb.epoxy.EpoxyController
import com.airbnb.lottie.samples.R
import com.airbnb.lottie.samples.databinding.BaseFragmentBinding
-import com.airbnb.mvrx.BaseMvRxFragment
-
private class BaseEpoxyController(
private val buildModelsCallback: EpoxyController.() -> Unit
@@ -17,11 +15,11 @@ private class BaseEpoxyController(
}
}
-abstract class BaseEpoxyFragment : BaseMvRxFragment(R.layout.base_fragment) {
+abstract class BaseEpoxyFragment : BaseFragment(R.layout.base_fragment) {
protected val binding: BaseFragmentBinding by viewBinding()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- binding.recyclerView.setController(BaseEpoxyController { buildModels() })
+ binding.recyclerView.setControllerAndBuildModels(BaseEpoxyController { buildModels() })
}
override fun invalidate() {
@@ -29,4 +27,4 @@ abstract class BaseEpoxyFragment : BaseMvRxFragment(R.layout.base_fragment) {
}
abstract fun EpoxyController.buildModels()
-} \ No newline at end of file
+}
diff --git a/sample/src/main/kotlin/com/airbnb/lottie/samples/utils/BaseFragment.kt b/sample/src/main/kotlin/com/airbnb/lottie/samples/utils/BaseFragment.kt
new file mode 100644
index 00000000..d5c148df
--- /dev/null
+++ b/sample/src/main/kotlin/com/airbnb/lottie/samples/utils/BaseFragment.kt
@@ -0,0 +1,7 @@
+package com.airbnb.lottie.samples.utils
+
+import androidx.annotation.LayoutRes
+import androidx.fragment.app.Fragment
+import com.airbnb.mvrx.MavericksView
+
+abstract class BaseFragment(@LayoutRes contentLayoutId: Int) : Fragment(contentLayoutId), MavericksView
diff --git a/sample/src/main/kotlin/com/airbnb/lottie/samples/utils/FragmentViewBindingDelegate.kt b/sample/src/main/kotlin/com/airbnb/lottie/samples/utils/FragmentViewBindingDelegate.kt
index 46635ac5..cded27e6 100644
--- a/sample/src/main/kotlin/com/airbnb/lottie/samples/utils/FragmentViewBindingDelegate.kt
+++ b/sample/src/main/kotlin/com/airbnb/lottie/samples/utils/FragmentViewBindingDelegate.kt
@@ -4,9 +4,9 @@ import android.os.Handler
import android.os.Looper
import android.view.View
import androidx.fragment.app.Fragment
+import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.LifecycleObserver
-import androidx.lifecycle.OnLifecycleEvent
+import androidx.lifecycle.LifecycleOwner
import androidx.viewbinding.ViewBinding
import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KProperty
@@ -31,9 +31,9 @@ class FragmentViewBindingDelegate<T : ViewBinding>(
init {
fragment.viewLifecycleOwnerLiveData.observe(fragment) { viewLifecycleOwner ->
- viewLifecycleOwner.lifecycle.addObserver(object : LifecycleObserver {
- @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
- fun onDestroy() {
+ viewLifecycleOwner.lifecycle.addObserver(object : DefaultLifecycleObserver {
+
+ override fun onDestroy(owner: LifecycleOwner) {
// Lifecycle listeners are called before onDestroyView in a Fragment.
// However, we want views to be able to use bindings in onDestroyView
// to do cleanup so we clear the reference one frame later.
diff --git a/sample/src/main/kotlin/com/airbnb/lottie/samples/utils/MvRxViewModel.kt b/sample/src/main/kotlin/com/airbnb/lottie/samples/utils/MvRxViewModel.kt
deleted file mode 100644
index bcda8a88..00000000
--- a/sample/src/main/kotlin/com/airbnb/lottie/samples/utils/MvRxViewModel.kt
+++ /dev/null
@@ -1,28 +0,0 @@
-package com.airbnb.lottie.samples.utils
-
-import androidx.lifecycle.viewModelScope
-import com.airbnb.lottie.samples.BuildConfig
-import com.airbnb.mvrx.*
-import kotlinx.coroutines.*
-
-abstract class MvRxViewModel<S : MvRxState>(initialState: S) : BaseMvRxViewModel<S>(initialState, BuildConfig.DEBUG) {
- /**
- * This uses [Dispatchers.Main.immediate] by default to mimic [viewModelScope].
- */
- fun <T : Any?> (suspend () -> T).execute(
- dispatcher: CoroutineDispatcher = Dispatchers.Main.immediate,
- reducer: S.(Async<T>) -> S
- ): Job {
- setState { reducer(Loading()) }
- return viewModelScope.launch(dispatcher) {
- try {
- val result = invoke()
- setState { reducer(Success(result)) }
- } catch (e: CancellationException) {
- throw e
- } catch (e: Exception) {
- setState { reducer(Fail(e)) }
- }
- }
- }
-} \ No newline at end of file
diff --git a/sample/src/main/kotlin/com/airbnb/lottie/samples/utils/TypeExtensions.kt b/sample/src/main/kotlin/com/airbnb/lottie/samples/utils/TypeExtensions.kt
index 4c7a6609..66d70b0a 100644
--- a/sample/src/main/kotlin/com/airbnb/lottie/samples/utils/TypeExtensions.kt
+++ b/sample/src/main/kotlin/com/airbnb/lottie/samples/utils/TypeExtensions.kt
@@ -7,6 +7,8 @@ import android.content.pm.PackageManager
import android.graphics.Color
import android.net.Uri
import android.os.Build
+import android.os.Bundle
+import android.os.Parcelable
import android.os.VibrationEffect
import android.os.Vibrator
import android.util.Log
@@ -94,12 +96,14 @@ fun String?.toColorIntSafe(): Int {
bgColor[2], bgColor[2],
bgColor[3], bgColor[3]
)
+
5 -> "#%c%c%c%c%c%c%c%c".format(
bgColor[1], bgColor[1],
bgColor[2], bgColor[2],
bgColor[3], bgColor[3],
bgColor[4], bgColor[4]
)
+
else -> bgColor
}.toColorInt()
} catch (e: IllegalArgumentException) {
@@ -111,4 +115,22 @@ fun String?.toColorIntSafe(): Int {
fun Context.hideKeyboard() {
val inputMethodManager = getSystemService<InputMethodManager>()!!
inputMethodManager.hideSoftInputFromWindow((this as Activity).currentFocus?.windowToken, 0)
+}
+
+fun <T : Parcelable> Intent.getParcelableExtraCompat(key: String, klass: Class<T>): T? {
+ return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ getParcelableExtra(key, klass)
+ } else {
+ @Suppress("DEPRECATION")
+ getParcelableExtra(key)
+ }
+}
+
+fun <T : Parcelable> Bundle.getParcelableCompat(key: String, klass: Class<T>): T? {
+ return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ getParcelable(key, klass)
+ } else {
+ @Suppress("DEPRECATION")
+ getParcelable(key)
+ }
} \ No newline at end of file
diff --git a/sample/src/main/kotlin/com/airbnb/lottie/samples/views/AnimationItemView.kt b/sample/src/main/kotlin/com/airbnb/lottie/samples/views/AnimationItemView.kt
deleted file mode 100644
index 9df8142d..00000000
--- a/sample/src/main/kotlin/com/airbnb/lottie/samples/views/AnimationItemView.kt
+++ /dev/null
@@ -1,47 +0,0 @@
-package com.airbnb.lottie.samples.views
-
-import android.content.Context
-import android.util.AttributeSet
-import android.widget.FrameLayout
-import androidx.annotation.ColorInt
-import com.airbnb.epoxy.ModelProp
-import com.airbnb.epoxy.ModelView
-import com.airbnb.epoxy.TextProp
-import com.airbnb.lottie.samples.R
-import com.airbnb.lottie.samples.databinding.ItemViewShowcaseAnimationBinding
-import com.airbnb.lottie.samples.utils.setImageUrl
-import com.airbnb.lottie.samples.utils.viewBinding
-
-@ModelView(autoLayout = ModelView.Size.MATCH_WIDTH_WRAP_HEIGHT)
-class AnimationItemView @JvmOverloads constructor(
- context: Context,
- attrs: AttributeSet? = null,
- defStyleAttr: Int = 0
-) : FrameLayout(context, attrs, defStyleAttr) {
- private val binding: ItemViewShowcaseAnimationBinding by viewBinding()
-
- @ModelProp
- fun setPreviewUrl(url: String?) {
- binding.imageView.setImageUrl(url)
- }
-
- @TextProp
- fun setTitle(title: CharSequence?) {
- binding.titleView.text = title
- }
-
- @ModelProp
- fun setPreviewBackgroundColor(@ColorInt bgColor: Int?) {
- if (bgColor == null) {
- binding.imageView.setBackgroundResource(R.color.loading_placeholder)
- binding.imageView.setImageDrawable(null)
- } else {
- binding.imageView.setBackgroundColor(bgColor)
- }
- }
-
- @ModelProp(options = [ModelProp.Option.DoNotHash])
- override fun setOnClickListener(l: OnClickListener?) {
- binding.container.setOnClickListener(l)
- }
-} \ No newline at end of file
diff --git a/sample/src/main/kotlin/com/airbnb/lottie/samples/views/InterceptingFrameLayout.kt b/sample/src/main/kotlin/com/airbnb/lottie/samples/views/InterceptingFrameLayout.kt
index d078e4e8..2dd5caa1 100644
--- a/sample/src/main/kotlin/com/airbnb/lottie/samples/views/InterceptingFrameLayout.kt
+++ b/sample/src/main/kotlin/com/airbnb/lottie/samples/views/InterceptingFrameLayout.kt
@@ -1,5 +1,6 @@
package com.airbnb.lottie.samples.views
+import android.annotation.SuppressLint
import android.content.Context
import android.util.AttributeSet
import android.view.MotionEvent
@@ -19,6 +20,7 @@ class InterceptingFrameLayout @JvmOverloads constructor(
return super.onInterceptTouchEvent(ev)
}
+ @SuppressLint("ClickableViewAccessibility")
override fun onTouchEvent(event: MotionEvent): Boolean {
viewDragHelper?.processTouchEvent(event)
return true
diff --git a/sample/src/main/kotlin/com/airbnb/lottie/samples/views/LoadingView.kt b/sample/src/main/kotlin/com/airbnb/lottie/samples/views/LoadingView.kt
deleted file mode 100644
index cf804014..00000000
--- a/sample/src/main/kotlin/com/airbnb/lottie/samples/views/LoadingView.kt
+++ /dev/null
@@ -1,20 +0,0 @@
-package com.airbnb.lottie.samples.views
-
-import android.content.Context
-import android.util.AttributeSet
-import android.widget.FrameLayout
-import com.airbnb.epoxy.ModelView
-import com.airbnb.lottie.samples.R
-import com.airbnb.lottie.samples.utils.inflate
-
-@ModelView(autoLayout = ModelView.Size.MATCH_WIDTH_WRAP_HEIGHT)
-class LoadingView @JvmOverloads constructor(
- context: Context,
- attrs: AttributeSet? = null,
- defStyleAttr: Int = 0
-) : FrameLayout(context, attrs, defStyleAttr) {
-
- init {
- inflate(R.layout.list_item_loader)
- }
-} \ No newline at end of file
diff --git a/sample/src/main/kotlin/com/airbnb/lottie/samples/views/LottiefilesTabBar.kt b/sample/src/main/kotlin/com/airbnb/lottie/samples/views/LottiefilesTabBar.kt
deleted file mode 100644
index 9daef53f..00000000
--- a/sample/src/main/kotlin/com/airbnb/lottie/samples/views/LottiefilesTabBar.kt
+++ /dev/null
@@ -1,41 +0,0 @@
-package com.airbnb.lottie.samples.views
-
-import android.content.Context
-import android.util.AttributeSet
-import android.widget.LinearLayout
-import com.airbnb.epoxy.ModelProp
-import com.airbnb.epoxy.ModelView
-import com.airbnb.lottie.samples.LottiefilesMode
-import com.airbnb.lottie.samples.databinding.LottiefilesTabBarBinding
-import com.airbnb.lottie.samples.utils.viewBinding
-
-@ModelView(autoLayout = ModelView.Size.MATCH_WIDTH_WRAP_HEIGHT)
-class LottiefilesTabBar @JvmOverloads constructor(
- context: Context,
- attrs: AttributeSet? = null,
- defStyleAttr: Int = 0
-) : LinearLayout(context, attrs, defStyleAttr) {
- private val binding: LottiefilesTabBarBinding by viewBinding()
-
- @ModelProp
- fun setMode(mode: LottiefilesMode) {
- binding.popularView.isActivated = mode == LottiefilesMode.Popular
- binding.recentView.isActivated = mode == LottiefilesMode.Recent
- binding.searchView.isActivated = mode == LottiefilesMode.Search
- }
-
- @ModelProp(options = [ModelProp.Option.DoNotHash])
- fun setPopularClickListener(listener: OnClickListener) {
- binding.popularView.setOnClickListener(listener)
- }
-
- @ModelProp(options = [ModelProp.Option.DoNotHash])
- fun setRecentClickListener(listener: OnClickListener) {
- binding.recentView.setOnClickListener(listener)
- }
-
- @ModelProp(options = [ModelProp.Option.DoNotHash])
- fun setSearchClickListener(listener: OnClickListener) {
- binding.searchView.setOnClickListener(listener)
- }
-} \ No newline at end of file
diff --git a/sample/src/main/kotlin/com/airbnb/lottie/samples/views/SearchInputItemView.kt b/sample/src/main/kotlin/com/airbnb/lottie/samples/views/SearchInputItemView.kt
deleted file mode 100644
index b79971c4..00000000
--- a/sample/src/main/kotlin/com/airbnb/lottie/samples/views/SearchInputItemView.kt
+++ /dev/null
@@ -1,27 +0,0 @@
-package com.airbnb.lottie.samples.views
-
-import android.content.Context
-import android.util.AttributeSet
-import android.widget.FrameLayout
-import androidx.core.widget.doAfterTextChanged
-import com.airbnb.lottie.samples.databinding.ItemViewSearchInputBinding
-import com.airbnb.lottie.samples.utils.viewBinding
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.StateFlow
-
-class SearchInputItemView @JvmOverloads constructor(
- context: Context,
- attrs: AttributeSet? = null,
- defStyleAttr: Int = 0
-) : FrameLayout(context, attrs, defStyleAttr) {
- private val binding: ItemViewSearchInputBinding by viewBinding()
-
- private val _query = MutableStateFlow("")
- val query: StateFlow<String> = _query
-
- init {
- binding.searchEditText.doAfterTextChanged { text ->
- _query.value = text?.toString() ?: ""
- }
- }
-} \ No newline at end of file
diff --git a/sample/src/main/kotlin/com/airbnb/lottie/samples/views/SectionHeaderView.kt b/sample/src/main/kotlin/com/airbnb/lottie/samples/views/SectionHeaderView.kt
deleted file mode 100644
index 985e631a..00000000
--- a/sample/src/main/kotlin/com/airbnb/lottie/samples/views/SectionHeaderView.kt
+++ /dev/null
@@ -1,29 +0,0 @@
-package com.airbnb.lottie.samples.views
-
-import android.content.Context
-import android.util.AttributeSet
-import android.widget.FrameLayout
-import com.airbnb.epoxy.ModelProp
-import com.airbnb.epoxy.ModelView
-import com.airbnb.epoxy.TextProp
-import com.airbnb.lottie.samples.databinding.SectionHeaderViewBinding
-import com.airbnb.lottie.samples.utils.viewBinding
-
-@ModelView(autoLayout = ModelView.Size.MATCH_WIDTH_WRAP_HEIGHT)
-class SectionHeaderView @JvmOverloads constructor(
- context: Context,
- attrs: AttributeSet? = null,
- defStyleAttr: Int = 0
-) : FrameLayout(context, attrs, defStyleAttr) {
- private val binding: SectionHeaderViewBinding by viewBinding()
-
- @TextProp
- fun setTitle(title: CharSequence) {
- binding.titleView.text = title
- }
-
- @ModelProp(options = [ModelProp.Option.DoNotHash])
- override fun setOnClickListener(listener: OnClickListener?) {
- super.setOnClickListener(listener)
- }
-} \ No newline at end of file
diff --git a/sample/src/main/kotlin/com/airbnb/lottie/samples/views/ShowcaseCarousel.kt b/sample/src/main/kotlin/com/airbnb/lottie/samples/views/ShowcaseCarousel.kt
deleted file mode 100644
index 5680dae9..00000000
--- a/sample/src/main/kotlin/com/airbnb/lottie/samples/views/ShowcaseCarousel.kt
+++ /dev/null
@@ -1,43 +0,0 @@
-package com.airbnb.lottie.samples.views
-
-import android.content.Context
-import android.util.AttributeSet
-import androidx.recyclerview.widget.LinearLayoutManager
-import com.airbnb.epoxy.EpoxyController
-import com.airbnb.epoxy.EpoxyRecyclerView
-import com.airbnb.epoxy.ModelProp
-import com.airbnb.epoxy.ModelView
-import com.airbnb.lottie.samples.R
-import com.airbnb.lottie.samples.model.ShowcaseItem
-
-@ModelView(autoLayout = ModelView.Size.MATCH_WIDTH_WRAP_HEIGHT)
-class ShowcaseCarousel @JvmOverloads constructor(
- context: Context,
- attrs: AttributeSet? = null,
- defStyleAttr: Int = 0
-) : EpoxyRecyclerView(context, attrs, defStyleAttr), EpoxyRecyclerView.ModelBuilderCallback {
-
- private var items: List<ShowcaseItem>? = null
-
- init {
- layoutManager = LinearLayoutManager(context, HORIZONTAL, false)
- buildModelsWith(this)
- val sidePadding = resources.getDimensionPixelSize(R.dimen.showcase_carousel_padding)
- setPadding(sidePadding, 0, sidePadding, 0)
- }
-
- @ModelProp
- fun setShowcaseItems(items: List<ShowcaseItem>) {
- this.items = items
- requestModelBuild()
- }
-
- override fun buildModels(controller: EpoxyController) {
- items?.forEach {
- ShowcaseDemoItemViewModel_()
- .id(it.titleRes)
- .showcaseItem(it)
- .addTo(controller)
- }
- }
-} \ No newline at end of file
diff --git a/sample/src/main/kotlin/com/airbnb/lottie/samples/views/ShowcaseDemoItemView.kt b/sample/src/main/kotlin/com/airbnb/lottie/samples/views/ShowcaseDemoItemView.kt
deleted file mode 100644
index 5371cef9..00000000
--- a/sample/src/main/kotlin/com/airbnb/lottie/samples/views/ShowcaseDemoItemView.kt
+++ /dev/null
@@ -1,28 +0,0 @@
-package com.airbnb.lottie.samples.views
-
-import android.content.Context
-import android.util.AttributeSet
-import android.widget.FrameLayout
-import com.airbnb.epoxy.ModelProp
-import com.airbnb.epoxy.ModelView
-import com.airbnb.lottie.samples.databinding.ItemViewShowcaseDemoBinding
-import com.airbnb.lottie.samples.model.ShowcaseItem
-import com.airbnb.lottie.samples.utils.viewBinding
-
-@ModelView(autoLayout = ModelView.Size.WRAP_WIDTH_WRAP_HEIGHT)
-class ShowcaseDemoItemView @JvmOverloads constructor(
- context: Context,
- attrs: AttributeSet? = null,
- defStyleAttr: Int = 0
-) : FrameLayout(context, attrs, defStyleAttr) {
- private val binding: ItemViewShowcaseDemoBinding by viewBinding()
-
- @ModelProp
- fun setShowcaseItem(item: ShowcaseItem) {
- binding.imageView.setImageResource(item.drawableRes)
-
- binding.titleView.text = resources.getText(item.titleRes)
-
- binding.cardView.setOnClickListener { item.clickListener(this.context.applicationContext) }
- }
-} \ No newline at end of file
diff --git a/sample/src/main/res/drawable-nodpi/get_it_on_play.png b/sample/src/main/res/drawable-nodpi/get_it_on_play.png
new file mode 100644
index 00000000..0c1c4c28
--- /dev/null
+++ b/sample/src/main/res/drawable-nodpi/get_it_on_play.png
Binary files differ
diff --git a/sample/src/main/res/drawable-nodpi/lottiefiles_logo.png b/sample/src/main/res/drawable-nodpi/lottiefiles_logo.png
new file mode 100644
index 00000000..9a93a8ea
--- /dev/null
+++ b/sample/src/main/res/drawable-nodpi/lottiefiles_logo.png
Binary files differ
diff --git a/sample/src/main/res/drawable-nodpi/lottiefiles_screen.png b/sample/src/main/res/drawable-nodpi/lottiefiles_screen.png
new file mode 100644
index 00000000..b9ce60c8
--- /dev/null
+++ b/sample/src/main/res/drawable-nodpi/lottiefiles_screen.png
Binary files differ
diff --git a/sample/src/main/res/drawable/ic_device.xml b/sample/src/main/res/drawable/ic_device.xml
index 8153d700..2576b2a0 100644
--- a/sample/src/main/res/drawable/ic_device.xml
+++ b/sample/src/main/res/drawable/ic_device.xml
@@ -4,6 +4,6 @@
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
- android:fillColor="#FF4C4C4C"
+ android:fillColor="#017A87"
android:pathData="M17,1.01L7,1c-1.1,0 -1.99,0.9 -1.99,2v18c0,1.1 0.89,2 1.99,2h10c1.1,0 2,-0.9 2,-2V3c0,-1.1 -0.9,-1.99 -2,-1.99zM17,19H7V5h10v14z"/>
</vector>
diff --git a/sample/src/main/res/drawable/ic_learn.xml b/sample/src/main/res/drawable/ic_learn.xml
index 16671bf1..e81e68c7 100644
--- a/sample/src/main/res/drawable/ic_learn.xml
+++ b/sample/src/main/res/drawable/ic_learn.xml
@@ -12,17 +12,13 @@
android:translateX="303.000000"
android:translateY="9.000000">
<path
- android:fillAlpha="0.5"
android:fillType="evenOdd"
- android:strokeAlpha="0.5"
android:strokeWidth="1"
android:pathData="M 0 0 L 24 0 L 24 24 L 0 24 Z" />
<path
- android:fillColor="#000000"
- android:fillAlpha="0.5"
- android:strokeAlpha="0.5"
+ android:fillColor="#017A87"
android:strokeWidth="1"
android:pathData="M5,13.18 L5,17.18 L12,21 L19,17.18 L19,13.18 L12,17 L5,13.18 Z M12,3 L1,9 L12,15 L21,10.09 L21,17 L23,17 L23,9 L12,3 Z" />
</group>
</group>
-</vector> \ No newline at end of file
+</vector>
diff --git a/sample/src/main/res/drawable/ic_lottiefiles.xml b/sample/src/main/res/drawable/ic_lottiefiles.xml
index 063c5f6f..f04523c1 100644
--- a/sample/src/main/res/drawable/ic_lottiefiles.xml
+++ b/sample/src/main/res/drawable/ic_lottiefiles.xml
@@ -1,17 +1,12 @@
-<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="22dp"
- android:height="20dp"
- android:viewportWidth="22"
- android:viewportHeight="20">
-
- <group
- android:translateX="-1.000000"
- android:translateY="-2.000000">
- <path
- android:fillColor="#007A87"
- android:fillType="evenOdd"
- android:strokeWidth="1"
- android:pathData="M16.8611196,2.22682915 C17.6818714,2.53432455 18.4581902,3.1233189 18.8506902,3.79934658 L22.5672824,10.2006513 C23.1442549,11.1944031 23.1442235,12.8056482 22.5672824,13.799348 L20.3230946,17.6646432 C19.8494992,17.5053563 19.4129899,17.3077388 19.0183459,17.0701656 C18.0502034,16.4873426 17.4023217,16.0440801 16.8144225,15.5734839 C16.675202,15.4620436 16.5356884,15.3462655 16.3866174,15.2188133 C16.1550682,15.0208456 15.0331713,14.0198082 14.6387559,13.6788474 C14.5048546,13.5630956 14.3688442,13.4466171 14.2300744,13.3289315 C15.1361944,11.5992384 15.8311872,9.19631083 16.348273,6.37685064 C16.5142786,5.47170021 16.6583731,4.29768049 16.7834015,2.91451036 C16.8122156,2.59574827 16.8376778,2.39399762 16.8611196,2.22682915 Z M14.8531184,2 C14.8343282,2.18208107 14.8127836,2.35656004 14.7726624,2.8004113 C14.6514539,4.1413005 14.5124667,5.27374399 14.3564297,6.12454436 C13.9261995,8.47039804 13.2477538,10.4356594 12.5134237,11.9248964 C12.1753452,11.6572578 11.8163703,11.3775151 11.431267,11.0818004 C9.72027907,9.69988512 7.83974245,9.75318443 6.63931965,10.6162589 C5.3547047,11.5398583 5,12.6276279 5,14.0873785 C5,15.556366 5.84115993,17.2043232 7.50182166,17.6588584 C8.31887424,17.8824925 9.03615734,17.8273542 9.8019401,17.4612486 C10.2748449,17.2555288 11.7781579,16.3891753 13.0139241,15.0917701 C13.0723064,15.0304757 13.1300367,14.9669242 13.1871203,14.9011556 C13.2999316,14.9971119 13.4108323,15.0922243 13.520244,15.1868058 C13.8987586,15.5140158 15.0256511,16.5195139 15.2756296,16.7332437 C15.4389641,16.8728901 15.5933963,17.0010469 15.7486014,17.1252836 C16.40304,17.6491382 17.1150393,18.1362747 18.1468273,18.7574091 C18.5238882,18.9843981 18.9244431,19.1802669 19.346358,19.3469336 L18.8506902,20.2006511 C18.2737212,21.1944041 16.8655712,22 15.7098752,22 L8.2901257,22 C7.13247253,22 5.72624822,21.1943501 5.14930719,20.2006511 L1.43271744,13.799348 C0.855745162,12.8055963 0.855776548,11.1943511 1.43271744,10.2006513 L5.14930719,3.79934658 C5.72627968,2.80559489 7.13442976,2 8.2901257,2 L14.8531184,2 Z M11.5576832,13.5640433 C11.4595752,13.7039892 11.361919,13.8350217 11.2651504,13.9569245 C10.6429057,14.7407876 9.69981935,15.3269738 9.40369702,15.510086 C9.40325764,15.5103577 9.40295638,15.5114737 9.40279324,15.5134339 C9.39436973,15.5183021 9.38598828,15.5233306 9.37765197,15.5285203 C8.82553356,15.8722337 8.46556626,15.9296783 7.94160188,15.7862674 C7.20826803,15.5855471 6.78946817,14.7650564 6.78946817,14.0873785 C6.78946817,13.2074238 6.95050286,12.7135777 7.63123352,12.2241514 C7.82410809,12.0854798 8.18200401,11.9601863 8.58113718,11.9488727 C9.15158199,11.9327051 9.75279672,12.1385784 10.3767548,12.6424069 C10.801991,12.9690504 11.1930226,13.2743229 11.5576832,13.5640433 Z" />
- </group>
-</vector> \ No newline at end of file
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M17.187,1.5H6.813C3.879,1.5 1.5,3.879 1.5,6.813V17.187C1.5,20.121 3.879,22.5 6.813,22.5H17.187C20.121,22.5 22.5,20.121 22.5,17.187V6.813C22.5,3.879 20.121,1.5 17.187,1.5Z"
+ android:fillColor="#017A87"/>
+ <path
+ android:pathData="M17.461,6.203C13.846,6.203 12.506,8.784 11.429,10.858L10.725,12.184C9.585,14.383 8.733,15.72 6.537,15.72C6.4,15.72 6.265,15.747 6.139,15.799C6.013,15.851 5.899,15.928 5.802,16.024C5.706,16.121 5.63,16.235 5.577,16.361C5.525,16.487 5.498,16.622 5.498,16.758C5.499,17.033 5.608,17.297 5.803,17.492C5.998,17.687 6.261,17.796 6.537,17.796C10.153,17.796 11.492,15.215 12.569,13.142L13.272,11.815C14.414,9.616 15.266,8.28 17.461,8.28C17.597,8.28 17.732,8.253 17.858,8.201C17.984,8.149 18.099,8.073 18.195,7.976C18.292,7.88 18.369,7.765 18.421,7.639C18.473,7.513 18.5,7.378 18.5,7.242C18.5,6.966 18.39,6.702 18.195,6.507C18,6.313 17.736,6.203 17.461,6.203Z"
+ android:fillColor="#ffffff"/>
+</vector>
diff --git a/sample/src/main/res/layout/choose_asset_fragment.xml b/sample/src/main/res/layout/choose_asset_fragment.xml
deleted file mode 100644
index 2b6b6e45..00000000
--- a/sample/src/main/res/layout/choose_asset_fragment.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
- <androidx.recyclerview.widget.RecyclerView
- android:id="@+id/recyclerView"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- app:layoutManager="LinearLayoutManager" />
-</FrameLayout> \ No newline at end of file
diff --git a/sample/src/main/res/layout/control_bar_player_controls.xml b/sample/src/main/res/layout/control_bar_player_controls.xml
index 37977ec3..0babd82d 100644
--- a/sample/src/main/res/layout/control_bar_player_controls.xml
+++ b/sample/src/main/res/layout/control_bar_player_controls.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
-<FrameLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/controlsContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content">
@@ -20,24 +20,23 @@
android:layout_marginLeft="16dp"
android:gravity="center_horizontal"
android:text="@string/ms"
- android:textSize="12sp"/>
+ android:textSize="12sp" />
<com.github.mikephil.charting.charts.LineChart
android:id="@+id/renderTimesGraph"
android:layout_width="match_parent"
android:layout_height="96dp"
android:layout_marginLeft="64dp"
- android:layout_marginRight="64dp"/>
+ android:layout_marginRight="64dp" />
</FrameLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_alignParentBottom="true"
android:layout_gravity="bottom"
android:orientation="horizontal"
- android:paddingBottom="12dp"
- android:paddingTop="12dp">
+ android:paddingTop="12dp"
+ android:paddingBottom="12dp">
<RelativeLayout
android:layout_width="wrap_content"
@@ -50,7 +49,7 @@
android:layout_height="32dp"
android:layout_centerVertical="true"
android:background="?attr/selectableItemBackgroundBorderless"
- app:srcCompat="@drawable/ic_play_pause"/>
+ app:srcCompat="@drawable/ic_play_pause" />
<TextView
android:id="@+id/currentFrameView"
@@ -61,7 +60,8 @@
android:layout_marginTop="16dp"
android:text="0"
android:textColor="#464646"
- android:textSize="10sp"/>
+ android:textSize="10sp"
+ tools:ignore="SmallSp" />
</RelativeLayout>
<androidx.appcompat.widget.AppCompatSeekBar
@@ -69,7 +69,7 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
- android:layout_weight="1"/>
+ android:layout_weight="1" />
<ImageButton
android:id="@+id/loopButton"
@@ -77,7 +77,7 @@
android:layout_height="32dp"
android:layout_gravity="center_vertical"
android:background="?attr/selectableItemBackgroundBorderless"
- app:srcCompat="@drawable/ic_loop"/>
+ app:srcCompat="@drawable/ic_loop" />
</LinearLayout>
<TextView
@@ -91,7 +91,8 @@
android:text="@string/render_times_per_layer_button"
android:textColor="@color/item_selected_teal"
android:textSize="10sp"
- android:visibility="gone"/>
+ android:visibility="gone"
+ tools:ignore="SmallSp" />
<TextView
android:id="@+id/lottieVersionView"
@@ -100,11 +101,12 @@
android:layout_gravity="bottom|center_horizontal"
android:layout_marginBottom="14dp"
android:textColor="@color/text_color"
- android:textSize="10sp"/>
+ android:textSize="10sp"
+ tools:ignore="SmallSp" />
<View
android:layout_width="match_parent"
android:layout_height="@dimen/divider_height"
android:layout_gravity="bottom"
- android:background="@color/divider"/>
+ android:background="@color/divider" />
</FrameLayout>
diff --git a/sample/src/main/res/layout/empty_fragment.xml b/sample/src/main/res/layout/empty_fragment.xml
deleted file mode 100644
index e54d1885..00000000
--- a/sample/src/main/res/layout/empty_fragment.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-<FrameLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"/> \ No newline at end of file
diff --git a/sample/src/main/res/layout/font_fragment.xml b/sample/src/main/res/layout/font_fragment.xml
deleted file mode 100644
index 71ed6fac..00000000
--- a/sample/src/main/res/layout/font_fragment.xml
+++ /dev/null
@@ -1,41 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:orientation="horizontal">
-
- <com.airbnb.lottie.LottieAnimationView
- android:id="@+id/original"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginRight="16dp"
- app:lottie_rawRes="@raw/name"
- app:lottie_autoPlay="true"
- app:lottie_loop="true"/>
-
- <com.airbnb.lottie.LottieAnimationView
- android:id="@+id/dynamic_text"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- app:lottie_rawRes="@raw/name"
- app:lottie_autoPlay="true"
- app:lottie_loop="true"/>
- </LinearLayout>
-
- <EditText
- android:id="@+id/name_edit_text"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="16dp"
- android:layout_marginTop="32dp"
- android:layout_marginRight="16dp"
- android:inputType="text"/>
-
-</LinearLayout> \ No newline at end of file
diff --git a/sample/src/main/res/layout/item_view_search_input.xml b/sample/src/main/res/layout/item_view_search_input.xml
deleted file mode 100644
index b9c5e873..00000000
--- a/sample/src/main/res/layout/item_view_search_input.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<merge xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- tools:parentTag="android.widget.FrameLayout">
-
- <EditText
- android:id="@+id/searchEditText"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="16dp"
- android:layout_marginRight="16dp"
- android:layout_weight="1"
- android:backgroundTint="@color/divider"
- android:hint="@string/search"
- android:imeOptions="actionSearch"
- android:inputType="text"
- android:textColorHint="@color/text_color_placeholder" />
-</merge> \ No newline at end of file
diff --git a/sample/src/main/res/layout/item_view_showcase_animation.xml b/sample/src/main/res/layout/item_view_showcase_animation.xml
deleted file mode 100644
index 8fdd6a18..00000000
--- a/sample/src/main/res/layout/item_view_showcase_animation.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<merge xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- tools:parentTag="android.widget.FrameLayout">
-
- <LinearLayout
- android:id="@+id/container"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:background="?attr/selectableItemBackground">
-
- <ImageView
- android:id="@+id/imageView"
- android:layout_width="40dp"
- android:layout_height="40dp"
- android:layout_gravity="center_vertical"
- android:layout_marginLeft="16dp"
- android:layout_marginRight="16dp"
- android:scaleType="centerCrop"/>
-
- <TextView
- android:id="@+id/titleView"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:layout_marginBottom="20dp"
- android:layout_marginRight="12dp"
- android:layout_marginTop="20dp"
- android:layout_weight="1"
- android:ellipsize="end"
- android:maxLines="1"
- android:singleLine="true"
- android:textColor="@color/text_color_dark"
- android:textSize="16sp"/>
- </LinearLayout>
-
- <View
- android:layout_width="match_parent"
- android:layout_height="@dimen/divider_height"
- android:layout_gravity="top"
- android:background="@color/divider"/>
-</merge> \ No newline at end of file
diff --git a/sample/src/main/res/layout/item_view_showcase_demo.xml b/sample/src/main/res/layout/item_view_showcase_demo.xml
deleted file mode 100644
index c935b4fc..00000000
--- a/sample/src/main/res/layout/item_view_showcase_demo.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<merge xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- xmlns:tools="http://schemas.android.com/tools"
- tools:parentTag="android.widget.FrameLayout">
-
- <androidx.cardview.widget.CardView
- android:id="@+id/cardView"
- android:layout_width="196dp"
- android:layout_height="108dp"
- android:layout_marginBottom="16dp"
- android:layout_marginLeft="8dp"
- android:layout_marginRight="8dp"
- android:layout_marginTop="16dp"
- app:cardCornerRadius="12dp">
-
- <ImageView
- android:id="@+id/imageView"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="top|right"/>
-
- <TextView
- android:id="@+id/titleView"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="bottom"
- android:layout_marginBottom="12dp"
- android:layout_marginLeft="12dp"
- android:textColor="@color/text_color_dark"
- android:textSize="20sp"/>
- </androidx.cardview.widget.CardView>
-</merge> \ No newline at end of file
diff --git a/sample/src/main/res/layout/list_item_loader.xml b/sample/src/main/res/layout/list_item_loader.xml
deleted file mode 100644
index 3c3c5694..00000000
--- a/sample/src/main/res/layout/list_item_loader.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<merge xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
-
- <com.airbnb.lottie.LottieAnimationView
- android:layout_width="100dp"
- android:layout_height="100dp"
- android:layout_gravity="center"
- android:layout_marginBottom="16dp"
- android:layout_marginTop="16dp"
- app:lottie_autoPlay="true"
- app:lottie_loop="true"
- app:lottie_rawRes="@raw/material_wave_loading"/>
-
-</merge> \ No newline at end of file
diff --git a/sample/src/main/res/layout/lottiefiles_fragment.xml b/sample/src/main/res/layout/lottiefiles_fragment.xml
index 715791c2..b68fba2b 100644
--- a/sample/src/main/res/layout/lottiefiles_fragment.xml
+++ b/sample/src/main/res/layout/lottiefiles_fragment.xml
@@ -1,30 +1,71 @@
<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
- <com.airbnb.lottie.samples.views.Marquee
+ <androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- app:subtitleText="@string/lottiefiles_airbnb"
- app:titleText="@string/lottiefiles" />
+ android:layout_height="wrap_content">
- <com.airbnb.lottie.samples.views.LottiefilesTabBar
- android:id="@+id/tab_bar"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
+ <ImageView
+ android:id="@+id/lottiefiles_logo"
+ android:layout_width="0dp"
+ android:layout_height="32dp"
+ android:layout_marginStart="24dp"
+ android:layout_marginLeft="24dp"
+ android:layout_marginTop="16dp"
+ android:src="@drawable/lottiefiles_logo"
+ app:layout_constraintDimensionRatio="W,165:32"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent" />
- <com.airbnb.lottie.samples.views.SearchInputItemView
- android:id="@+id/search_view"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
+ <TextView
+ android:id="@+id/lottiefiles_description"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="24dp"
+ android:layout_marginTop="12dp"
+ android:layout_marginRight="24dp"
+ android:text="@string/lottiefiles_description"
+ android:textColor="#434343"
+ android:textSize="20sp"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/lottiefiles_logo" />
- <androidx.recyclerview.widget.RecyclerView
- android:id="@+id/recyclerView"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="1"
- app:layoutManager="LinearLayoutManager" />
-</LinearLayout> \ No newline at end of file
+ <ImageView
+ android:id="@+id/get_it_on_play"
+ android:layout_width="0dp"
+ android:layout_height="40dp"
+ android:layout_marginStart="24dp"
+ android:layout_marginLeft="24dp"
+ android:layout_marginTop="12dp"
+ android:src="@drawable/get_it_on_play"
+ app:layout_constraintDimensionRatio="W,564:168"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/lottiefiles_description" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="8dp"
+ android:layout_marginLeft="8dp"
+ android:text="@string/lottiefiles_airbnb"
+ android:textSize="12sp"
+ app:layout_constraintBottom_toBottomOf="@id/get_it_on_play"
+ app:layout_constraintStart_toEndOf="@id/get_it_on_play"
+ app:layout_constraintTop_toTopOf="@id/get_it_on_play" />
+
+ <ImageView
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_marginTop="18dp"
+ android:src="@drawable/lottiefiles_screen"
+ app:layout_constraintDimensionRatio="H,360:335"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/get_it_on_play" />
+ </androidx.constraintlayout.widget.ConstraintLayout>
+</ScrollView>
diff --git a/sample/src/main/res/layout/lottiefiles_tab_bar.xml b/sample/src/main/res/layout/lottiefiles_tab_bar.xml
deleted file mode 100644
index bfa86ac8..00000000
--- a/sample/src/main/res/layout/lottiefiles_tab_bar.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<merge xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- xmlns:tools="http://schemas.android.com/tools"
- tools:parentTag="android.widget.LinearLayout">
-
- <com.airbnb.lottie.samples.views.TabBarItemView
- android:id="@+id/recentView"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginLeft="16dp"
- android:layout_marginRight="24dp"
- android:background="?attr/selectableItemBackground"
- app:titleText="@string/recent"/>
-
- <com.airbnb.lottie.samples.views.TabBarItemView
- android:id="@+id/popularView"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginBottom="18dp"
- android:layout_marginRight="24dp"
- android:background="?attr/selectableItemBackground"
- app:titleText="@string/popular"/>
-
- <com.airbnb.lottie.samples.views.TabBarItemView
- android:id="@+id/searchView"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginBottom="18dp"
- android:background="?attr/selectableItemBackground"
- app:titleText="@string/search"/>
-
-</merge> \ No newline at end of file
diff --git a/sample/src/main/res/layout/main_activity.xml b/sample/src/main/res/layout/main_activity.xml
index 7cf69fa3..e65528f4 100644
--- a/sample/src/main/res/layout/main_activity.xml
+++ b/sample/src/main/res/layout/main_activity.xml
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/root"
@@ -13,17 +12,17 @@
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="0dp"
- android:layout_weight="1"/>
+ android:layout_weight="1" />
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottomNavigation"
android:layout_width="match_parent"
android:layout_height="56dp"
- app:menu="@menu/bottom_bar"
+ android:background="#F7F7F7"
+ android:elevation="8dp"
app:itemIconTint="@drawable/bottom_bar_tint_list"
app:itemTextColor="@color/bottom_bar_label"
- android:background="#F7F7F7"
- android:elevation="8dp"/>
+ app:menu="@menu/bottom_bar" />
-</LinearLayout> \ No newline at end of file
+</LinearLayout>
diff --git a/sample/src/main/res/layout/player_fragment.xml b/sample/src/main/res/layout/player_fragment.xml
index 7482c2e6..cfd17ebf 100644
--- a/sample/src/main/res/layout/player_fragment.xml
+++ b/sample/src/main/res/layout/player_fragment.xml
@@ -25,6 +25,7 @@
android:scaleType="centerInside"
android:layout_gravity="center"
android:background="@drawable/outline"
+ app:lottie_asyncUpdates="enabled"
app:lottie_autoPlay="true" />
<ProgressBar
@@ -80,4 +81,4 @@
<include
android:id="@+id/bottom_sheet_key_paths"
layout="@layout/bottom_sheet_key_paths" />
-</androidx.coordinatorlayout.widget.CoordinatorLayout> \ No newline at end of file
+</androidx.coordinatorlayout.widget.CoordinatorLayout>
diff --git a/sample/src/main/res/layout/section_header_view.xml b/sample/src/main/res/layout/section_header_view.xml
deleted file mode 100644
index 2eb8fa81..00000000
--- a/sample/src/main/res/layout/section_header_view.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<merge
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- tools:parentTag="android.widget.FrameLayout">
-
- <TextView
- android:id="@+id/titleView"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginBottom="8dp"
- android:layout_marginLeft="24dp"
- android:layout_marginTop="8dp"
- android:textSize="18sp"/>
-
-
-</merge> \ No newline at end of file
diff --git a/sample/src/main/res/layout/view_holder_grid_item.xml b/sample/src/main/res/layout/view_holder_grid_item.xml
deleted file mode 100644
index d7a77369..00000000
--- a/sample/src/main/res/layout/view_holder_grid_item.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <TextView
- android:id="@+id/name"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:singleLine="true"
- android:maxLines="1"/>
-
- <com.airbnb.lottie.LottieAnimationView
- android:id="@+id/animation_view"
- android:layout_width="match_parent"
- android:layout_height="200dp"
- android:background="@drawable/outline"
- android:orientation="vertical"
- app:lottie_autoPlay="true"
- app:lottie_loop="true" />
-</LinearLayout> \ No newline at end of file
diff --git a/sample/src/main/res/layout/view_holder_letter.xml b/sample/src/main/res/layout/view_holder_letter.xml
deleted file mode 100644
index 6dcd1c5c..00000000
--- a/sample/src/main/res/layout/view_holder_letter.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<com.airbnb.lottie.LottieAnimationView xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" /> \ No newline at end of file
diff --git a/sample/src/main/res/layout/view_holder_warning.xml b/sample/src/main/res/layout/view_holder_warning.xml
deleted file mode 100644
index 37c983f8..00000000
--- a/sample/src/main/res/layout/view_holder_warning.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingLeft="16dp"
- android:paddingRight="16dp"
- android:orientation="vertical">
-
- <TextView
- android:id="@+id/warning"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingTop="4dp"
- android:paddingBottom="4dp"/>
-
- <View
- android:id="@+id/divider"
- android:layout_width="match_parent"
- android:layout_height="@dimen/divider_height"
- android:background="@color/divider"/>
-</LinearLayout> \ No newline at end of file
diff --git a/sample/src/main/res/layout/warnings_fragment.xml b/sample/src/main/res/layout/warnings_fragment.xml
deleted file mode 100644
index b90f48a9..00000000
--- a/sample/src/main/res/layout/warnings_fragment.xml
+++ /dev/null
@@ -1,45 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:minWidth="256dp"
- android:minHeight="256dp">
-
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:textColor="@android:color/black"
- android:textSize="20sp"
- android:padding="16sp"
- android:text="Warnings"/>
-
- <View
- android:layout_width="match_parent"
- android:layout_height="@dimen/divider_height"
- android:background="@color/divider"/>
-
- <androidx.recyclerview.widget.RecyclerView
- android:id="@+id/recyclerView"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="1"
- app:layoutManager="LinearLayoutManager"/>
-
- <View
- android:layout_width="match_parent"
- android:layout_height="@dimen/divider_height"
- android:background="@color/divider"/>
-
- <Button
- android:id="@+id/okButton"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="right"
- android:background="?selectableItemBackground"
- android:textColor="@color/material_teal"
- android:textSize="16sp"
- android:text="OK"/>
-
-</LinearLayout> \ No newline at end of file
diff --git a/sample/src/main/res/menu/bottom_bar.xml b/sample/src/main/res/menu/bottom_bar.xml
index 13b211de..f7b6e699 100644
--- a/sample/src/main/res/menu/bottom_bar.xml
+++ b/sample/src/main/res/menu/bottom_bar.xml
@@ -1,27 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
- <item
- android:id="@+id/showcase"
- android:icon="@drawable/ic_showcase"
- android:title="@string/bottom_bar_showcase"
- app:showAsAction="always|withText"/>
<item
android:id="@+id/preview"
android:icon="@drawable/ic_device"
android:title="@string/bottom_bar_preview"
- app:showAsAction="always|withText"/>
+ app:showAsAction="ifRoom|withText"/>
<item
android:id="@+id/lottiefiles"
android:icon="@drawable/ic_lottiefiles"
android:title="@string/bottom_bar_lottiefiles"
- app:showAsAction="always|withText"/>
+ app:showAsAction="ifRoom|withText"/>
<item
android:id="@+id/learn"
android:icon="@drawable/ic_learn"
android:title="@string/bottom_bar_learn"
- app:showAsAction="always|withText"/>
-</menu> \ No newline at end of file
+ app:showAsAction="ifRoom|withText"/>
+</menu>
diff --git a/sample/src/main/res/menu/fragment_animation.xml b/sample/src/main/res/menu/fragment_animation.xml
deleted file mode 100644
index 3af72641..00000000
--- a/sample/src/main/res/menu/fragment_animation.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<menu xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto">
- <item
- android:id="@+id/render_times_graph"
- android:title="@string/render_times_graph"
- android:checkable="true"
- android:checked="false"
- app:showAsAction="never" />
-
- <item
- android:id="@+id/hardware_acceleration"
- android:title="@string/hardware_acceleration"
- android:checkable="true"
- android:checked="false"
- app:showAsAction="never" />
-
- <item
- android:id="@+id/merge_paths"
- android:title="@string/enable_merge_paths"
- android:checkable="true"
- android:checked="false"
- app:showAsAction="never" />
-</menu> \ No newline at end of file
diff --git a/sample/src/main/res/raw/material_wave_loading.json b/sample/src/main/res/raw/material_wave_loading.json
deleted file mode 100644
index cafcccf1..00000000
--- a/sample/src/main/res/raw/material_wave_loading.json
+++ /dev/null
@@ -1 +0,0 @@
-{"v":"4.6.8","fr":29.9700012207031,"ip":0,"op":40.0000016292334,"w":256,"h":256,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 3","ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":20,"s":[208.6,127.969,0],"e":[208.6,88,0],"to":[0,-6.66145849227905,0],"ti":[0,-0.00520833348855,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":30,"s":[208.6,88,0],"e":[208.6,128,0],"to":[0,0.00520833348855,0],"ti":[0,-6.66666650772095,0]},{"t":40.0000016292334}]},"a":{"a":0,"k":[-70,-0.5,0]},"s":{"a":0,"k":[75,75,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[33.75,34.5]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse"},{"ty":"fl","c":{"a":0,"k":[0.9843137,0.5490196,0,1]},"o":{"a":0,"k":100},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill"},{"ty":"tr","p":{"a":0,"k":[-70.125,-0.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group"}],"ip":0,"op":300.00001221925,"st":0,"bm":0,"sr":1},{"ddd":0,"ind":2,"ty":4,"nm":"Shape Layer 2","ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":15,"s":[168.6,128,0],"e":[168.6,88,0],"to":[0,-6.66666650772095,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":25,"s":[168.6,88,0],"e":[168.6,128,0],"to":[0,0,0],"ti":[0,-6.66666650772095,0]},{"t":35.0000014255792}]},"a":{"a":0,"k":[-70,-0.5,0]},"s":{"a":0,"k":[75,75,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[33.75,34.5]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse"},{"ty":"fl","c":{"a":0,"k":[0.9921569,0.8470588,0.2078431,1]},"o":{"a":0,"k":100},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill"},{"ty":"tr","p":{"a":0,"k":[-70.125,-0.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group"}],"ip":0,"op":300.00001221925,"st":0,"bm":0,"sr":1},{"ddd":0,"ind":3,"ty":4,"nm":"Shape Layer 1","ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":10,"s":[128.594,127.969,0],"e":[128.594,88,0],"to":[0,-6.66145849227905,0],"ti":[0,-0.00520833348855,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":20,"s":[128.594,88,0],"e":[128.594,128,0],"to":[0,0.00520833348855,0],"ti":[0,-6.66666650772095,0]},{"t":30.0000012219251}]},"a":{"a":0,"k":[-70,-0.5,0]},"s":{"a":0,"k":[75,75,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[33.75,34.5]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse"},{"ty":"fl","c":{"a":0,"k":[0.2627451,0.627451,0.2784314,1]},"o":{"a":0,"k":100},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill"},{"ty":"tr","p":{"a":0,"k":[-70.125,-0.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group"}],"ip":0,"op":300.00001221925,"st":0,"bm":0,"sr":1},{"ddd":0,"ind":4,"ty":4,"nm":"Shape Layer 4","ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":5,"s":[88.6,127.969,0],"e":[88.6,88,0],"to":[0,-6.66145849227905,0],"ti":[0,-0.00520833348855,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":15,"s":[88.6,88,0],"e":[88.6,128,0],"to":[0,0.00520833348855,0],"ti":[0,-6.66666650772095,0]},{"t":25.0000010182709}]},"a":{"a":0,"k":[-70,-0.5,0]},"s":{"a":0,"k":[75,75,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[33.75,34.5]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse"},{"ty":"fl","c":{"a":0,"k":[0.1176471,0.5333334,0.8980392,1]},"o":{"a":0,"k":100},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill"},{"ty":"tr","p":{"a":0,"k":[-70.125,-0.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group"}],"ip":0,"op":300.00001221925,"st":0,"bm":0,"sr":1},{"ddd":0,"ind":5,"ty":4,"nm":"Shape Layer 5","ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":0,"s":[48.6,127.969,0],"e":[48.6,88,0],"to":[0,-6.66145849227905,0],"ti":[0,-0.00520833348855,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":10,"s":[48.6,88,0],"e":[48.6,128,0],"to":[0,0.00520833348855,0],"ti":[0,-6.66666650772095,0]},{"t":20.0000008146167}]},"a":{"a":0,"k":[-70,-0.5,0]},"s":{"a":0,"k":[75,75,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[33.75,34.5]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse"},{"ty":"fl","c":{"a":0,"k":[0.8980392,0.2235294,0.2078431,1]},"o":{"a":0,"k":100},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill"},{"ty":"tr","p":{"a":0,"k":[-70.125,-0.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group"}],"ip":0,"op":300.00001221925,"st":0,"bm":0,"sr":1}]} \ No newline at end of file
diff --git a/sample/src/main/res/values/strings.xml b/sample/src/main/res/values/strings.xml
index 5ddb8a1a..f368ca3f 100644
--- a/sample/src/main/res/values/strings.xml
+++ b/sample/src/main/res/values/strings.xml
@@ -3,9 +3,6 @@
<string name="invalid_assets">Unable to load assets</string>
<string name="permission_required">Camera permissions are required to use the QR Scanner</string>
<string name="scan_prompt">Scan QR Code from lottiefiles.com</string>
- <string name="render_times_graph">Show real time render time graph</string>
- <string name="hardware_acceleration">Use hardware acceleration</string>
- <string name="enable_merge_paths">Enable merge paths (KK+ only)</string>
<string name="menu_item_visibility">Hide Controls</string>
<!-- Shortcuts -->
@@ -22,8 +19,6 @@
<string name="qr_code_overlay">QR code overlay</string>
<string name="qr_permission_denied">QR scanning requires the camera permission.</string>
- <string name="scale">Scale</string>
- <string name="background">Background</string>
<string name="ms">ms</string>
<string name="bottom_bar_showcase">Showcase</string>
@@ -76,12 +71,12 @@
<string name="recent">Recent</string>
<string name="search">Search</string>
- <string name="anonymous">Anonymous</string>
-
<string name="showcase_item_bullseye">\nBullseye</string>
<string name="showcase_item_dynamic_properties">Dynamic\nProperties</string>
<string name="showcase_item_animated_text">Animated\nText</string>
<string name="showcase_item_dynamic_text">Dynamic\nText</string>
<string name="showcase_item_recycler_view">RecyclerView</string>
<string name="bullseye_drag_dot">Drag the blue dot</string>
+
+ <string name="lottiefiles_description">Explore the largest, free-to-use Lottie animation library in the world. Seamlessly test, customize, and get your animation ready for use within minutes.</string>
</resources>
diff --git a/secrets.tar.enc b/secrets.tar.enc
deleted file mode 100644
index 93d22979..00000000
--- a/secrets.tar.enc
+++ /dev/null
Binary files differ
diff --git a/settings.gradle b/settings.gradle
index 4152c5c4..7d5c4afc 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1,3 +1,29 @@
+pluginManagement {
+ repositories {
+ gradlePluginPortal()
+ google()
+ }
+}
+
+plugins {
+ id 'de.fayard.refreshVersions' version '0.51.0'
+//// # available:'0.60.0'
+//// # available:'0.60.1'
+//// # available:'0.60.2'
+//// # available:'0.60.3'
+//// # available:'0.60.4'
+//// # available:'0.60.5'
+}
+
+dependencyResolutionManagement {
+ repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
+ repositories {
+ google()
+ mavenCentral()
+ maven { url 'https://jitpack.io' }
+ }
+}
+
include ':lottie'
include ':lottie-compose'
include ':sample'
@@ -5,4 +31,6 @@ include ':sample-compose'
include ':issue-repro'
include ':issue-repro-compose'
include ':snapshot-tests'
-include ':sample-compose-benchmark'
+include ':benchmark'
+include ':app-benchmark'
+include ':baselineprofile'
diff --git a/snapshot-tests/build.gradle b/snapshot-tests/build.gradle
index ee8ed567..ba1e5a06 100644
--- a/snapshot-tests/build.gradle
+++ b/snapshot-tests/build.gradle
@@ -1,21 +1,22 @@
+import static de.fayard.refreshVersions.core.Versions.versionFor
+
plugins {
id 'com.android.application'
- id "kotlin-android"
- id 'kotlin-kapt'
+ id "org.jetbrains.kotlin.android"
}
android {
- compileSdk 31
+ namespace 'com.airbnb.lottie.snapshots'
+ compileSdk 34
defaultConfig {
applicationId "com.airbnb.lottie.snapshots"
minSdk 21
- targetSdk 30
+ targetSdk 34
versionCode 1
versionName VERSION_NAME
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
- buildConfigField("String", "BITRISE_GIT_BRANCH", "\"" + System.getenv("BITRISE_GIT_BRANCH") + "\"")
- buildConfigField("String", "GIT_SHA", "\"" + gitSha + "\"")
- buildConfigField("String", "GIT_BRANCH", "\"" + gitBranch + "\"")
+ buildConfigField("String", "GIT_BRANCH", "\"" + System.getenv("GITHUB_REF_NAME") + "\"")
+ buildConfigField("String", "GIT_SHA", "\"" + System.getenv("GITHUB_SHA") + "\"")
}
buildTypes {
@@ -30,55 +31,46 @@ android {
}
}
- compileOptions {
- sourceCompatibility JavaVersion.VERSION_1_8
- targetCompatibility JavaVersion.VERSION_1_8
- }
-
kotlinOptions {
- jvmTarget = JavaVersion.VERSION_1_8.toString()
freeCompilerArgs += [
- "-Xallow-jvm-ir-dependencies",
"-Xskip-prerelease-check",
- "-Xuse-experimental=kotlinx.coroutines.ExperimentalCoroutinesApi",
- "-Xopt-in=kotlin.RequiresOptIn",
+ "-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi",
+ "-opt-in=kotlin.RequiresOptIn",
]
}
buildFeatures {
compose true
viewBinding true
+ buildConfig true
}
composeOptions {
- kotlinCompilerExtensionVersion composeVersion
+ kotlinCompilerExtensionVersion = versionFor(project, AndroidX.compose.compiler)
}
}
-kapt {
- correctErrorTypes = true
-}
-
dependencies {
implementation project(':lottie-compose')
- implementation "androidx.core:core-ktx:$coreVersion"
- implementation "androidx.activity:activity-compose:$activityVersion"
- implementation 'androidx.appcompat:appcompat:1.4.0-beta01'
- implementation "androidx.compose.ui:ui:$composeVersion"
- implementation "androidx.compose.ui:ui-tooling:$composeVersion"
- implementation "androidx.compose.material:material:$composeVersion"
+ implementation libs.androidx.appcompat
+ implementation libs.androidx.core.ktx
+ implementation libs.androidx.activity.compose
+ implementation platform(libs.compose.bom)
+ implementation libs.compose.ui
+ implementation libs.compose.ui.tooling
+ implementation libs.compose.material
- implementation 'com.squareup.okhttp3:okhttp:4.9.1'
+ implementation libs.okhttp
- androidTestImplementation "com.amazonaws:aws-android-sdk-s3:$awsVersion"
- androidTestImplementation("com.amazonaws:aws-android-sdk-mobile-client:$awsVersion") { transitive = true }
- androidTestImplementation("com.amazonaws:aws-android-sdk-auth-userpools:$awsVersion") { transitive = true }
+ androidTestImplementation libs.aws.android.sdk.s3
+ androidTestImplementation(libs.aws.android.sdk.mobile.client) { transitive = true }
+ androidTestImplementation(libs.aws.android.sdk.auth.userpools) { transitive = true }
- androidTestImplementation "androidx.test.ext:junit:$extJunitVersion"
- androidTestImplementation "androidx.test.espresso:espresso-core:$espressoVersion"
- androidTestImplementation "androidx.test.espresso:espresso-idling-resource:$espressoVersion"
- androidTestImplementation 'androidx.test:core:1.3.0'
- androidTestImplementation 'androidx.test:rules:1.4.0'
- androidTestImplementation 'io.jsonwebtoken:jjwt:0.9.1'
- androidTestImplementation "org.mockito:mockito-android:$mockitoVersion"
- androidTestImplementation "com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0"
+ androidTestImplementation libs.androidx.test.junit
+ androidTestImplementation libs.androidx.test.espresso
+ androidTestImplementation libs.androidx.test.espresso.idling
+ androidTestImplementation libs.androidx.test.core
+ androidTestImplementation libs.androidx.test.rules
+ androidTestImplementation libs.jjwt
+ androidTestImplementation libs.mockito.android
+ androidTestImplementation libs.mockito.kotlin
}
diff --git a/snapshot-tests/src/androidTest/java/com/airbnb/lottie/snapshots/LottieSnapshotTest.kt b/snapshot-tests/src/androidTest/java/com/airbnb/lottie/snapshots/LottieSnapshotTest.kt
index 879b65ab..decf80a4 100644
--- a/snapshot-tests/src/androidTest/java/com/airbnb/lottie/snapshots/LottieSnapshotTest.kt
+++ b/snapshot-tests/src/androidTest/java/com/airbnb/lottie/snapshots/LottieSnapshotTest.kt
@@ -11,15 +11,20 @@ import androidx.test.ext.junit.rules.ActivityScenarioRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest
import androidx.test.rule.GrantPermissionRule
+import com.airbnb.lottie.Lottie
import com.airbnb.lottie.LottieAnimationView
+import com.airbnb.lottie.LottieConfig
import com.airbnb.lottie.model.LottieCompositionCache
import com.airbnb.lottie.snapshots.tests.ApplyOpacityToLayerTestCase
import com.airbnb.lottie.snapshots.tests.AssetsTestCase
import com.airbnb.lottie.snapshots.tests.ClipChildrenTestCase
+import com.airbnb.lottie.snapshots.tests.ClipTextToBoundingBoxTestCase
import com.airbnb.lottie.snapshots.tests.ColorStateListColorFilterTestCase
import com.airbnb.lottie.snapshots.tests.ComposeDynamicPropertiesTestCase
import com.airbnb.lottie.snapshots.tests.ComposeScaleTypesTestCase
+import com.airbnb.lottie.snapshots.tests.CompositionFrameRate
import com.airbnb.lottie.snapshots.tests.CustomBoundsTestCase
+import com.airbnb.lottie.snapshots.tests.DisabledAnimationsTestCase
import com.airbnb.lottie.snapshots.tests.DynamicPropertiesTestCase
import com.airbnb.lottie.snapshots.tests.FailureTestCase
import com.airbnb.lottie.snapshots.tests.FrameBoundariesTestCase
@@ -27,9 +32,12 @@ import com.airbnb.lottie.snapshots.tests.LargeCompositionSoftwareRendering
import com.airbnb.lottie.snapshots.tests.MarkersTestCase
import com.airbnb.lottie.snapshots.tests.NightModeTestCase
import com.airbnb.lottie.snapshots.tests.OutlineMasksAndMattesTestCase
+import com.airbnb.lottie.snapshots.tests.PainterTestCase
import com.airbnb.lottie.snapshots.tests.PartialFrameProgressTestCase
+import com.airbnb.lottie.snapshots.tests.PolygonStrokeTestCase
import com.airbnb.lottie.snapshots.tests.ProdAnimationsTestCase
import com.airbnb.lottie.snapshots.tests.ScaleTypesTestCase
+import com.airbnb.lottie.snapshots.tests.SeekBarTestCase
import com.airbnb.lottie.snapshots.tests.SoftwareRenderingDynamicPropertiesInvalidationTestCase
import com.airbnb.lottie.snapshots.tests.TextTestCase
import com.airbnb.lottie.snapshots.utils.BitmapPool
@@ -39,6 +47,9 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withTimeout
+import okhttp3.OkHttpClient
+import okhttp3.Request
+import org.json.JSONObject
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@@ -61,12 +72,36 @@ class LottieSnapshotTest {
lateinit var testCaseContext: SnapshotTestCaseContext
lateinit var snapshotter: HappoSnapshotter
+ private lateinit var s3AccessKey: String
+ private lateinit var s3SecretKey: String
@Before
fun setup() {
LottieCompositionCache.getInstance().resize(1)
+ Lottie.initialize(
+ LottieConfig.Builder()
+ .setDisablePathInterpolatorCache(true)
+ .build()
+ )
val context = ApplicationProvider.getApplicationContext<Context>()
- snapshotter = HappoSnapshotter(context) { name, variant ->
+ s3AccessKey = BuildConfig.S3AccessKey
+ s3SecretKey = BuildConfig.S3SecretKey
+ var happoApiKey = BuildConfig.HappoApiKey
+ var happoSecretKey = BuildConfig.HappoSecretKey
+ @Suppress("KotlinConstantConditions")
+ if (BuildConfig.S3AccessKey == "" || BuildConfig.S3AccessKey == "null") {
+ val client = OkHttpClient()
+ val request = Request.Builder()
+ .url("https://us-central1-lottie-snapshots.cloudfunctions.net/snapshot-env-v1/snapshots")
+ .build()
+ val response = client.newCall(request).execute()
+ val json = JSONObject(response.body?.string() ?: "{}")
+ s3AccessKey = json.getString("LOTTIE_S3_API_KEY")
+ s3SecretKey = json.getString("LOTTIE_S3_SECRET_KEY")
+ happoApiKey = json.getString("LOTTIE_HAPPO_API_KEY")
+ happoSecretKey = json.getString("LOTTIE_HAPPO_SECRET_KEY")
+ }
+ snapshotter = HappoSnapshotter(context, s3AccessKey, s3SecretKey, happoApiKey, happoSecretKey) { name, variant ->
snapshotActivityRule.scenario.onActivity { activity ->
activity.updateUiForSnapshot(name, variant)
}
@@ -114,6 +149,7 @@ class LottieSnapshotTest {
FrameBoundariesTestCase(),
ScaleTypesTestCase(),
ComposeScaleTypesTestCase(),
+ PainterTestCase(),
DynamicPropertiesTestCase(),
MarkersTestCase(),
AssetsTestCase(),
@@ -124,9 +160,14 @@ class LottieSnapshotTest {
OutlineMasksAndMattesTestCase(),
LargeCompositionSoftwareRendering(),
ComposeDynamicPropertiesTestCase(),
- ProdAnimationsTestCase(),
+ ProdAnimationsTestCase(s3AccessKey, s3SecretKey),
ClipChildrenTestCase(),
SoftwareRenderingDynamicPropertiesInvalidationTestCase(),
+ SeekBarTestCase(),
+ PolygonStrokeTestCase(),
+ CompositionFrameRate(),
+ ClipTextToBoundingBoxTestCase(),
+ DisabledAnimationsTestCase(),
)
withTimeout(TimeUnit.MINUTES.toMillis(45)) {
diff --git a/snapshot-tests/src/androidTest/java/com/airbnb/lottie/snapshots/SnapshotTestCaseContext.kt b/snapshot-tests/src/androidTest/java/com/airbnb/lottie/snapshots/SnapshotTestCaseContext.kt
index bd8ed08e..92df041d 100644
--- a/snapshot-tests/src/androidTest/java/com/airbnb/lottie/snapshots/SnapshotTestCaseContext.kt
+++ b/snapshot-tests/src/androidTest/java/com/airbnb/lottie/snapshots/SnapshotTestCaseContext.kt
@@ -56,7 +56,7 @@ suspend fun SnapshotTestCaseContext.withDrawable(
assetName: String,
snapshotName: String,
snapshotVariant: String,
- callback: (LottieDrawable) -> Unit,
+ callback: suspend (LottieDrawable) -> Unit,
) {
val result = LottieCompositionFactory.fromAssetSync(context, assetName)
val composition = result.value ?: throw IllegalArgumentException("Unable to parse $assetName.", result.exception)
@@ -144,12 +144,15 @@ suspend fun SnapshotTestCaseContext.snapshotComposition(
val filmStripView = filmStripViewPool.acquire()
filmStripView.setOutlineMasksAndMattes(false)
filmStripView.setApplyingOpacityToLayersEnabled(false)
+ filmStripView.setUseCompositionFrameRate(false)
filmStripView.setImageAssetDelegate { BitmapFactory.decodeResource(context.resources, R.drawable.airbnb) }
- filmStripView.setFontAssetDelegate(object : FontAssetDelegate() {
- override fun getFontPath(fontFamily: String?): String {
- return "fonts/Roboto.ttf"
- }
- })
+ if (composition.characters.isEmpty) {
+ filmStripView.setFontAssetDelegate(object : FontAssetDelegate() {
+ override fun getFontPath(fontFamily: String?, fontStyle: String?, fontName: String?): String {
+ return "fonts/Roboto.ttf"
+ }
+ })
+ }
callback?.invoke(filmStripView)
val spec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
filmStripView.measure(spec, spec)
@@ -251,4 +254,4 @@ private suspend fun View.awaitFrame() {
cont.resume(Unit)
}
}
-} \ No newline at end of file
+}
diff --git a/snapshot-tests/src/androidTest/java/com/airbnb/lottie/snapshots/tests/AssetsTestCase.kt b/snapshot-tests/src/androidTest/java/com/airbnb/lottie/snapshots/tests/AssetsTestCase.kt
index 39be0419..6267b395 100644
--- a/snapshot-tests/src/androidTest/java/com/airbnb/lottie/snapshots/tests/AssetsTestCase.kt
+++ b/snapshot-tests/src/androidTest/java/com/airbnb/lottie/snapshots/tests/AssetsTestCase.kt
@@ -31,7 +31,7 @@ class AssetsTestCase : SnapshotTestCase {
listAssets(assets, pathWithPrefix)
return@forEach
}
- if (!animation.endsWith(".json") && !animation.endsWith(".zip")) return@forEach
+ if (!animation.endsWith(".json") && !animation.endsWith(".zip") && !animation.endsWith(".tgs")) return@forEach
assets += pathWithPrefix
}
return assets
@@ -47,4 +47,4 @@ class AssetsTestCase : SnapshotTestCase {
send(asset to composition)
}
}
-} \ No newline at end of file
+}
diff --git a/snapshot-tests/src/androidTest/java/com/airbnb/lottie/snapshots/tests/ClipTextToBoundingBoxTestCase.kt b/snapshot-tests/src/androidTest/java/com/airbnb/lottie/snapshots/tests/ClipTextToBoundingBoxTestCase.kt
new file mode 100644
index 00000000..718e15bb
--- /dev/null
+++ b/snapshot-tests/src/androidTest/java/com/airbnb/lottie/snapshots/tests/ClipTextToBoundingBoxTestCase.kt
@@ -0,0 +1,39 @@
+package com.airbnb.lottie.snapshots.tests
+
+import com.airbnb.lottie.snapshots.SnapshotTestCase
+import com.airbnb.lottie.snapshots.SnapshotTestCaseContext
+import com.airbnb.lottie.snapshots.withFilmStripView
+
+class ClipTextToBoundingBoxTestCase : SnapshotTestCase {
+ override suspend fun SnapshotTestCaseContext.run() {
+ withFilmStripView(
+ "Tests/SzGlyph.json",
+ "Clip glyph text to bounding box",
+ "Enabled"
+ ) { filmStripView ->
+ filmStripView.setClipTextToBoundingBox(true)
+ }
+ withFilmStripView(
+ "Tests/SzGlyph.json",
+ "Clip glyph text to bounding box",
+ "Disabled"
+ ) { filmStripView ->
+ filmStripView.setClipTextToBoundingBox(false)
+ }
+
+ withFilmStripView(
+ "Tests/SzFont.json",
+ "Clip font text to bounding box",
+ "Enabled"
+ ) { filmStripView ->
+ filmStripView.setClipTextToBoundingBox(true)
+ }
+ withFilmStripView(
+ "Tests/SzFont.json",
+ "Clip font text to bounding box",
+ "Disabled"
+ ) { filmStripView ->
+ filmStripView.setClipTextToBoundingBox(false)
+ }
+ }
+}
diff --git a/snapshot-tests/src/androidTest/java/com/airbnb/lottie/snapshots/tests/ComposeDynamicPropertiesTestCase.kt b/snapshot-tests/src/androidTest/java/com/airbnb/lottie/snapshots/tests/ComposeDynamicPropertiesTestCase.kt
index 00bc92d4..0d2cea53 100644
--- a/snapshot-tests/src/androidTest/java/com/airbnb/lottie/snapshots/tests/ComposeDynamicPropertiesTestCase.kt
+++ b/snapshot-tests/src/androidTest/java/com/airbnb/lottie/snapshots/tests/ComposeDynamicPropertiesTestCase.kt
@@ -5,10 +5,15 @@ import android.graphics.BitmapFactory
import android.graphics.Color
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
import com.airbnb.lottie.LottieCompositionFactory
import com.airbnb.lottie.LottieProperty
import com.airbnb.lottie.compose.LottieAnimation
import com.airbnb.lottie.compose.LottieCompositionSpec
+import com.airbnb.lottie.compose.LottieConstants
+import com.airbnb.lottie.compose.animateLottieCompositionAsState
import com.airbnb.lottie.compose.rememberLottieComposition
import com.airbnb.lottie.compose.rememberLottieDynamicProperties
import com.airbnb.lottie.compose.rememberLottieDynamicProperty
@@ -80,10 +85,31 @@ class ComposeDynamicPropertiesTestCase : SnapshotTestCase {
)
LottieAnimation(heartComposition, { 0f }, dynamicProperties = dynamicProperties, maintainOriginalImageBounds = true)
}
+
+ snapshotComposable("Compose switch composition") {
+ val snapshotReady = LocalSnapshotReady.current
+ var state by remember { mutableStateOf(1) }
+ val composition by rememberLottieComposition(LottieCompositionSpec.Asset(if (state == 1) "Tests/Dynamic1.json" else "Tests/Dynamic2.json"))
+ val progress by animateLottieCompositionAsState(composition, iterations = LottieConstants.IterateForever)
+ val dynamicProperties = rememberLottieDynamicProperties(
+ rememberLottieDynamicProperty(LottieProperty.COLOR, 0x0000FF, "**", "Fill 1")
+ )
+ val ready = state == 2 && composition != null
+ LaunchedEffect(ready) {
+ snapshotReady.value = ready
+ }
+ if (composition != null && state == 1) {
+ state = 2
+ }
+ LottieAnimation(
+ composition,
+ { progress },
+ dynamicProperties = dynamicProperties
+ )
+ }
}
private fun SnapshotTestCaseContext.getBitmapFromAssets(name: String): Bitmap {
- @Suppress("BlockingMethodInNonBlockingContext")
return BitmapFactory.decodeStream(context.assets.open(name), null, BitmapFactory.Options())!!
}
} \ No newline at end of file
diff --git a/snapshot-tests/src/androidTest/java/com/airbnb/lottie/snapshots/tests/ComposeScaleTypesTestCase.kt b/snapshot-tests/src/androidTest/java/com/airbnb/lottie/snapshots/tests/ComposeScaleTypesTestCase.kt
index 54f993db..e5149c6b 100644
--- a/snapshot-tests/src/androidTest/java/com/airbnb/lottie/snapshots/tests/ComposeScaleTypesTestCase.kt
+++ b/snapshot-tests/src/androidTest/java/com/airbnb/lottie/snapshots/tests/ComposeScaleTypesTestCase.kt
@@ -1,11 +1,29 @@
package com.airbnb.lottie.snapshots.tests
+import androidx.compose.foundation.horizontalScroll
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.verticalScroll
+import androidx.compose.material.Text
+import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.scale
import androidx.compose.ui.layout.ContentScale
+import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import com.airbnb.lottie.compose.LottieAnimation
+import com.airbnb.lottie.compose.LottieCompositionSpec
+import com.airbnb.lottie.compose.LottieConstants
+import com.airbnb.lottie.compose.animateLottieCompositionAsState
+import com.airbnb.lottie.compose.rememberLottieComposition
+import com.airbnb.lottie.snapshots.R
import com.airbnb.lottie.snapshots.SnapshotTestCase
import com.airbnb.lottie.snapshots.SnapshotTestCaseContext
import com.airbnb.lottie.snapshots.loadCompositionFromAssetsSync
@@ -28,7 +46,7 @@ class ComposeScaleTypesTestCase : SnapshotTestCase {
{ 1f },
renderMode = renderMode,
modifier = Modifier
- .size(720.dp, 1280.dp)
+ .size(720.dp, 1280.dp),
)
}
@@ -39,7 +57,7 @@ class ComposeScaleTypesTestCase : SnapshotTestCase {
renderMode = renderMode,
modifier = Modifier
.size(300.dp, 300.dp)
- .scale(2f)
+ .scale(2f),
)
}
@@ -50,7 +68,7 @@ class ComposeScaleTypesTestCase : SnapshotTestCase {
renderMode = renderMode,
modifier = Modifier
.size(300.dp, 300.dp)
- .scale(4f)
+ .scale(4f),
)
}
@@ -61,7 +79,7 @@ class ComposeScaleTypesTestCase : SnapshotTestCase {
contentScale = ContentScale.Crop,
renderMode = renderMode,
modifier = Modifier
- .size(300.dp, 300.dp)
+ .size(300.dp, 300.dp),
)
}
@@ -72,7 +90,7 @@ class ComposeScaleTypesTestCase : SnapshotTestCase {
contentScale = ContentScale.Inside,
renderMode = renderMode,
modifier = Modifier
- .size(300.dp, 300.dp)
+ .size(300.dp, 300.dp),
)
}
@@ -83,7 +101,7 @@ class ComposeScaleTypesTestCase : SnapshotTestCase {
contentScale = ContentScale.FillBounds,
renderMode = renderMode,
modifier = Modifier
- .size(300.dp, 300.dp)
+ .size(300.dp, 300.dp),
)
}
@@ -95,7 +113,7 @@ class ComposeScaleTypesTestCase : SnapshotTestCase {
renderMode = renderMode,
modifier = Modifier
.size(300.dp, 300.dp)
- .scale(2f)
+ .scale(2f),
)
}
@@ -107,7 +125,7 @@ class ComposeScaleTypesTestCase : SnapshotTestCase {
renderMode = renderMode,
modifier = Modifier
.size(300.dp, 300.dp)
- .scale(2f)
+ .scale(2f),
)
}
@@ -118,7 +136,7 @@ class ComposeScaleTypesTestCase : SnapshotTestCase {
contentScale = ContentScale.Inside,
renderMode = renderMode,
modifier = Modifier
- .size(600.dp, 600.dp)
+ .size(600.dp, 600.dp),
)
}
@@ -129,7 +147,7 @@ class ComposeScaleTypesTestCase : SnapshotTestCase {
contentScale = ContentScale.FillBounds,
renderMode = renderMode,
modifier = Modifier
- .size(600.dp, 600.dp)
+ .size(600.dp, 600.dp),
)
}
@@ -140,7 +158,7 @@ class ComposeScaleTypesTestCase : SnapshotTestCase {
contentScale = ContentScale.Fit,
renderMode = renderMode,
modifier = Modifier
- .size(600.dp, 600.dp)
+ .size(600.dp, 600.dp),
)
}
@@ -151,8 +169,47 @@ class ComposeScaleTypesTestCase : SnapshotTestCase {
contentScale = ContentScale.FillBounds,
renderMode = renderMode,
modifier = Modifier
- .size(300.dp, 600.dp)
+ .size(300.dp, 600.dp),
)
}
+
+ val largeSquareComposition = loadCompositionFromAssetsSync("Tests/LargeSquare.json")
+ snapshotComposable("Compose constrained size", "Column", renderHardwareAndSoftware = true) { renderMode ->
+ Column(
+ verticalArrangement = Arrangement.spacedBy(24.dp),
+ modifier = Modifier
+ .width(128.dp)
+ .verticalScroll(rememberScrollState()),
+ ) {
+ LottieAnimation(
+ composition = largeSquareComposition,
+ progress = { 1f },
+ renderMode = renderMode,
+ modifier = Modifier.fillMaxWidth(),
+ )
+ Text(
+ modifier = Modifier.fillMaxWidth(),
+ text = "Other content",
+ textAlign = TextAlign.Center,
+ )
+ }
+ }
+
+ snapshotComposable("Compose constrained size", "Row", renderHardwareAndSoftware = true) { renderMode ->
+ Row(
+ horizontalArrangement = Arrangement.spacedBy(24.dp),
+ modifier = Modifier
+ .height(128.dp)
+ .horizontalScroll(rememberScrollState()),
+ ) {
+ LottieAnimation(
+ composition = largeSquareComposition,
+ progress = { 1f },
+ renderMode = renderMode,
+ modifier = Modifier.fillMaxHeight(),
+ )
+ Text("Other content")
+ }
+ }
}
-} \ No newline at end of file
+}
diff --git a/snapshot-tests/src/androidTest/java/com/airbnb/lottie/snapshots/tests/CompositionFrameRate.kt b/snapshot-tests/src/androidTest/java/com/airbnb/lottie/snapshots/tests/CompositionFrameRate.kt
new file mode 100644
index 00000000..05755ea8
--- /dev/null
+++ b/snapshot-tests/src/androidTest/java/com/airbnb/lottie/snapshots/tests/CompositionFrameRate.kt
@@ -0,0 +1,69 @@
+package com.airbnb.lottie.snapshots.tests
+
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.size
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.layout.ContentScale
+import androidx.compose.ui.unit.dp
+import com.airbnb.lottie.LottieCompositionFactory
+import com.airbnb.lottie.compose.LottieAnimation
+import com.airbnb.lottie.snapshots.SnapshotTestCase
+import com.airbnb.lottie.snapshots.SnapshotTestCaseContext
+import com.airbnb.lottie.snapshots.snapshotComposable
+import com.airbnb.lottie.snapshots.withFilmStripView
+
+class CompositionFrameRate : SnapshotTestCase {
+ override suspend fun SnapshotTestCaseContext.run() {
+ withFilmStripView("Tests/Framerate.json", "Composition frame rate", "View") { filmStripView ->
+ filmStripView.setUseCompositionFrameRate(true)
+ }
+
+ val composition = LottieCompositionFactory.fromAssetSync(context, "Tests/Framerate.json").value!!
+ snapshotComposable("Composition frame rate", "Compose - 0") { renderMode ->
+ Column {
+ LottieAnimation(
+ composition,
+ { 0f },
+ contentScale = ContentScale.Crop,
+ renderMode = renderMode,
+ modifier = Modifier
+ .size(100.dp, 100.dp)
+ )
+ LottieAnimation(
+ composition,
+ { 0.15f },
+ contentScale = ContentScale.Crop,
+ renderMode = renderMode,
+ modifier = Modifier
+ .size(100.dp, 100.dp)
+ )
+
+ LottieAnimation(
+ composition,
+ { 0.5f },
+ contentScale = ContentScale.Crop,
+ renderMode = renderMode,
+ modifier = Modifier
+ .size(100.dp, 100.dp)
+ )
+
+ LottieAnimation(
+ composition,
+ { 0.9f },
+ contentScale = ContentScale.Crop,
+ renderMode = renderMode,
+ modifier = Modifier
+ .size(100.dp, 100.dp)
+ )
+ LottieAnimation(
+ composition,
+ { 1f },
+ contentScale = ContentScale.Crop,
+ renderMode = renderMode,
+ modifier = Modifier
+ .size(100.dp, 100.dp)
+ )
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/snapshot-tests/src/androidTest/java/com/airbnb/lottie/snapshots/tests/DisabledAnimationsTestCase.kt b/snapshot-tests/src/androidTest/java/com/airbnb/lottie/snapshots/tests/DisabledAnimationsTestCase.kt
new file mode 100644
index 00000000..b669635b
--- /dev/null
+++ b/snapshot-tests/src/androidTest/java/com/airbnb/lottie/snapshots/tests/DisabledAnimationsTestCase.kt
@@ -0,0 +1,25 @@
+package com.airbnb.lottie.snapshots.tests
+
+import com.airbnb.lottie.snapshots.SnapshotTestCase
+import com.airbnb.lottie.snapshots.SnapshotTestCaseContext
+import com.airbnb.lottie.snapshots.withDrawable
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
+
+class DisabledAnimationsTestCase : SnapshotTestCase {
+ override suspend fun SnapshotTestCaseContext.run() {
+ withDrawable("Tests/ReducedMotion.json", "System Animations", "Disabled") { drawable ->
+ withContext(Dispatchers.Main) {
+ drawable.setSystemAnimationsAreEnabled(false)
+ drawable.playAnimation()
+ }
+ }
+
+ withDrawable("Tests/ReducedMotion.json", "System Animations", "Enabled") { drawable ->
+ withContext(Dispatchers.Main) {
+ drawable.setSystemAnimationsAreEnabled(false)
+ drawable.playAnimation()
+ }
+ }
+ }
+}
diff --git a/snapshot-tests/src/androidTest/java/com/airbnb/lottie/snapshots/tests/DynamicPropertiesTestCase.kt b/snapshot-tests/src/androidTest/java/com/airbnb/lottie/snapshots/tests/DynamicPropertiesTestCase.kt
index f6fa19aa..1094101b 100644
--- a/snapshot-tests/src/androidTest/java/com/airbnb/lottie/snapshots/tests/DynamicPropertiesTestCase.kt
+++ b/snapshot-tests/src/androidTest/java/com/airbnb/lottie/snapshots/tests/DynamicPropertiesTestCase.kt
@@ -1,7 +1,9 @@
package com.airbnb.lottie.snapshots.tests
import android.graphics.Color
+import android.graphics.Path
import android.graphics.PointF
+import android.graphics.RectF
import com.airbnb.lottie.LottieProperty
import com.airbnb.lottie.SimpleColorFilter
import com.airbnb.lottie.model.KeyPath
@@ -21,56 +23,56 @@ class DynamicPropertiesTestCase : SnapshotTestCase {
"Fill color (Green)",
KeyPath("Shape Layer 1", "Rectangle", "Fill 1"),
LottieProperty.COLOR,
- LottieValueCallback(Color.GREEN)
+ LottieValueCallback(Color.GREEN),
)
testDynamicProperty(
"Fill color (Yellow)",
KeyPath("Shape Layer 1", "Rectangle", "Fill 1"),
LottieProperty.COLOR,
- LottieValueCallback(Color.YELLOW)
+ LottieValueCallback(Color.YELLOW),
)
testDynamicProperty(
"Fill opacity",
KeyPath("Shape Layer 1", "Rectangle", "Fill 1"),
LottieProperty.OPACITY,
- LottieValueCallback(50)
+ LottieValueCallback(50),
)
testDynamicProperty(
"Stroke color",
KeyPath("Shape Layer 1", "Rectangle", "Stroke 1"),
LottieProperty.STROKE_COLOR,
- LottieValueCallback(Color.GREEN)
+ LottieValueCallback(Color.GREEN),
)
testDynamicProperty(
"Stroke width",
KeyPath("Shape Layer 1", "Rectangle", "Stroke 1"),
LottieProperty.STROKE_WIDTH,
- LottieRelativeFloatValueCallback(50f)
+ LottieRelativeFloatValueCallback(50f),
)
testDynamicProperty(
"Stroke opacity",
KeyPath("Shape Layer 1", "Rectangle", "Stroke 1"),
LottieProperty.OPACITY,
- LottieValueCallback(50)
+ LottieValueCallback(50),
)
testDynamicProperty(
"Transform anchor point",
KeyPath("Shape Layer 1", "Rectangle"),
LottieProperty.TRANSFORM_ANCHOR_POINT,
- LottieRelativePointValueCallback(PointF(20f, 20f))
+ LottieRelativePointValueCallback(PointF(20f, 20f)),
)
testDynamicProperty(
"Transform position",
KeyPath("Shape Layer 1", "Rectangle"),
LottieProperty.TRANSFORM_POSITION,
- LottieRelativePointValueCallback(PointF(20f, 20f))
+ LottieRelativePointValueCallback(PointF(20f, 20f)),
)
@@ -82,7 +84,7 @@ class DynamicPropertiesTestCase : SnapshotTestCase {
override fun getValue(frameInfo: LottieFrameInfo<Float>) = frameInfo.startValue
},
progress = 1f,
- assetName = "Tests/SplitPathTransform.json"
+ assetName = "Tests/SplitPathTransform.json",
)
testDynamicProperty(
@@ -93,210 +95,224 @@ class DynamicPropertiesTestCase : SnapshotTestCase {
override fun getValue(frameInfo: LottieFrameInfo<Float>) = frameInfo.startValue
},
progress = 1f,
- assetName = "Tests/SplitPathTransform.json"
+ assetName = "Tests/SplitPathTransform.json",
)
testDynamicProperty(
"Transform position (relative)",
KeyPath("Shape Layer 1", "Rectangle"),
LottieProperty.TRANSFORM_POSITION,
- LottieRelativePointValueCallback(PointF(20f, 20f))
+ LottieRelativePointValueCallback(PointF(20f, 20f)),
)
testDynamicProperty(
"Transform opacity",
KeyPath("Shape Layer 1", "Rectangle"),
LottieProperty.TRANSFORM_OPACITY,
- LottieValueCallback(50)
+ LottieValueCallback(50),
)
testDynamicProperty(
"Transform rotation",
KeyPath("Shape Layer 1", "Rectangle"),
LottieProperty.TRANSFORM_ROTATION,
- LottieValueCallback(45f)
+ LottieValueCallback(45f),
)
testDynamicProperty(
"Transform scale",
KeyPath("Shape Layer 1", "Rectangle"),
LottieProperty.TRANSFORM_SCALE,
- LottieValueCallback(ScaleXY(0.5f, 0.5f))
+ LottieValueCallback(ScaleXY(0.5f, 0.5f)),
)
testDynamicProperty(
"Rectangle corner roundedness",
KeyPath("Shape Layer 1", "Rectangle", "Rectangle Path 1"),
LottieProperty.CORNER_RADIUS,
- LottieValueCallback(7f)
+ LottieValueCallback(7f),
)
testDynamicProperty(
"Rectangle position",
KeyPath("Shape Layer 1", "Rectangle", "Rectangle Path 1"),
LottieProperty.POSITION,
- LottieRelativePointValueCallback(PointF(20f, 20f))
+ LottieRelativePointValueCallback(PointF(20f, 20f)),
)
testDynamicProperty(
"Rectangle size",
KeyPath("Shape Layer 1", "Rectangle", "Rectangle Path 1"),
LottieProperty.RECTANGLE_SIZE,
- LottieRelativePointValueCallback(PointF(30f, 40f))
+ LottieRelativePointValueCallback(PointF(30f, 40f)),
)
testDynamicProperty(
"Ellipse position",
KeyPath("Shape Layer 1", "Ellipse", "Ellipse Path 1"),
LottieProperty.POSITION,
- LottieRelativePointValueCallback(PointF(20f, 20f))
+ LottieRelativePointValueCallback(PointF(20f, 20f)),
)
testDynamicProperty(
"Ellipse size",
KeyPath("Shape Layer 1", "Ellipse", "Ellipse Path 1"),
LottieProperty.ELLIPSE_SIZE,
- LottieRelativePointValueCallback(PointF(40f, 60f))
+ LottieRelativePointValueCallback(PointF(40f, 60f)),
)
testDynamicProperty(
"Star points",
KeyPath("Shape Layer 1", "Star", "Polystar Path 1"),
LottieProperty.POLYSTAR_POINTS,
- LottieValueCallback(8f)
+ LottieValueCallback(8f),
)
testDynamicProperty(
"Star rotation",
KeyPath("Shape Layer 1", "Star", "Polystar Path 1"),
LottieProperty.POLYSTAR_ROTATION,
- LottieValueCallback(10f)
+ LottieValueCallback(10f),
)
testDynamicProperty(
"Star position",
KeyPath("Shape Layer 1", "Star", "Polystar Path 1"),
LottieProperty.POSITION,
- LottieRelativePointValueCallback(PointF(20f, 20f))
+ LottieRelativePointValueCallback(PointF(20f, 20f)),
)
testDynamicProperty(
"Star inner radius",
KeyPath("Shape Layer 1", "Star", "Polystar Path 1"),
LottieProperty.POLYSTAR_INNER_RADIUS,
- LottieValueCallback(10f)
+ LottieValueCallback(10f),
)
testDynamicProperty(
"Star inner roundedness",
KeyPath("Shape Layer 1", "Star", "Polystar Path 1"),
LottieProperty.POLYSTAR_INNER_ROUNDEDNESS,
- LottieValueCallback(100f)
+ LottieValueCallback(100f),
)
testDynamicProperty(
"Star outer radius",
KeyPath("Shape Layer 1", "Star", "Polystar Path 1"),
LottieProperty.POLYSTAR_OUTER_RADIUS,
- LottieValueCallback(60f)
+ LottieValueCallback(60f),
)
testDynamicProperty(
"Star outer roundedness",
KeyPath("Shape Layer 1", "Star", "Polystar Path 1"),
LottieProperty.POLYSTAR_OUTER_ROUNDEDNESS,
- LottieValueCallback(100f)
+ LottieValueCallback(100f),
)
testDynamicProperty(
"Polygon points",
KeyPath("Shape Layer 1", "Polygon", "Polystar Path 1"),
LottieProperty.POLYSTAR_POINTS,
- LottieValueCallback(8f)
+ LottieValueCallback(8f),
)
testDynamicProperty(
"Polygon rotation",
KeyPath("Shape Layer 1", "Polygon", "Polystar Path 1"),
LottieProperty.POLYSTAR_ROTATION,
- LottieValueCallback(10f)
+ LottieValueCallback(10f),
)
testDynamicProperty(
"Polygon position",
KeyPath("Shape Layer 1", "Polygon", "Polystar Path 1"),
LottieProperty.POSITION,
- LottieRelativePointValueCallback(PointF(20f, 20f))
+ LottieRelativePointValueCallback(PointF(20f, 20f)),
)
testDynamicProperty(
"Polygon radius",
KeyPath("Shape Layer 1", "Polygon", "Polystar Path 1"),
LottieProperty.POLYSTAR_OUTER_RADIUS,
- LottieRelativeFloatValueCallback(60f)
+ LottieRelativeFloatValueCallback(60f),
)
testDynamicProperty(
"Polygon roundedness",
KeyPath("Shape Layer 1", "Polygon", "Polystar Path 1"),
LottieProperty.POLYSTAR_OUTER_ROUNDEDNESS,
- LottieValueCallback(100f)
+ LottieValueCallback(100f),
)
testDynamicProperty(
"Repeater transform position",
KeyPath("Shape Layer 1", "Repeater Shape", "Repeater 1"),
LottieProperty.TRANSFORM_POSITION,
- LottieRelativePointValueCallback(PointF(100f, 100f))
+ LottieRelativePointValueCallback(PointF(100f, 100f)),
+ )
+
+ testDynamicProperty(
+ "Repeater contents",
+ KeyPath("Shape Layer 1", "Repeater Shape", "Repeater 1"),
+ LottieProperty.TRANSFORM_POSITION,
+ LottieRelativePointValueCallback(PointF(100f, 100f)),
+ )
+
+ testDynamicProperty(
+ "Repeater sub-contents",
+ KeyPath("Shape Layer 1", "Repeater Shape", "Fill 1"),
+ LottieProperty.COLOR_FILTER,
+ LottieValueCallback(SimpleColorFilter(Color.GREEN)),
)
testDynamicProperty(
"Repeater transform start opacity",
KeyPath("Shape Layer 1", "Repeater Shape", "Repeater 1"),
LottieProperty.TRANSFORM_START_OPACITY,
- LottieValueCallback(25f)
+ LottieValueCallback(25f),
)
testDynamicProperty(
"Repeater transform end opacity",
KeyPath("Shape Layer 1", "Repeater Shape", "Repeater 1"),
LottieProperty.TRANSFORM_END_OPACITY,
- LottieValueCallback(25f)
+ LottieValueCallback(25f),
)
testDynamicProperty(
"Repeater transform rotation",
KeyPath("Shape Layer 1", "Repeater Shape", "Repeater 1"),
LottieProperty.TRANSFORM_ROTATION,
- LottieValueCallback(45f)
+ LottieValueCallback(45f),
)
testDynamicProperty(
"Repeater transform scale",
KeyPath("Shape Layer 1", "Repeater Shape", "Repeater 1"),
LottieProperty.TRANSFORM_SCALE,
- LottieValueCallback(ScaleXY(2f, 2f))
+ LottieValueCallback(ScaleXY(2f, 2f)),
)
testDynamicProperty(
"Time remapping",
KeyPath("Circle 1"),
LottieProperty.TIME_REMAP,
- LottieValueCallback(1f)
+ LottieValueCallback(1f),
)
testDynamicProperty(
"Color Filter",
KeyPath("**"),
LottieProperty.COLOR_FILTER,
- LottieValueCallback(SimpleColorFilter(Color.GREEN))
+ LottieValueCallback(SimpleColorFilter(Color.GREEN)),
)
testDynamicProperty(
"Null Color Filter",
KeyPath("**"),
LottieProperty.COLOR_FILTER,
- LottieValueCallback(null)
+ LottieValueCallback(null),
)
testDynamicProperty(
@@ -304,7 +320,7 @@ class DynamicPropertiesTestCase : SnapshotTestCase {
KeyPath("Shape Layer 1", "Rectangle"),
LottieProperty.TRANSFORM_OPACITY,
LottieInterpolatedIntegerValue(10, 100),
- 0f
+ 0f,
)
testDynamicProperty(
@@ -312,7 +328,7 @@ class DynamicPropertiesTestCase : SnapshotTestCase {
KeyPath("Shape Layer 1", "Rectangle"),
LottieProperty.TRANSFORM_OPACITY,
LottieInterpolatedIntegerValue(10, 100),
- 0.5f
+ 0.5f,
)
testDynamicProperty(
@@ -320,7 +336,7 @@ class DynamicPropertiesTestCase : SnapshotTestCase {
KeyPath("Shape Layer 1", "Rectangle"),
LottieProperty.TRANSFORM_OPACITY,
LottieInterpolatedIntegerValue(10, 100),
- 1f
+ 1f,
)
testDynamicProperty(
@@ -328,7 +344,7 @@ class DynamicPropertiesTestCase : SnapshotTestCase {
KeyPath("Shape Layer 1", "**"),
LottieProperty.DROP_SHADOW_COLOR,
LottieValueCallback(Color.RED),
- assetName = "Tests/AnimatedShadow.json"
+ assetName = "Tests/AnimatedShadow.json",
)
testDynamicProperty(
@@ -336,7 +352,7 @@ class DynamicPropertiesTestCase : SnapshotTestCase {
KeyPath("Shape Layer 1", "**"),
LottieProperty.DROP_SHADOW_DISTANCE,
LottieValueCallback(30f),
- assetName = "Tests/AnimatedShadow.json"
+ assetName = "Tests/AnimatedShadow.json",
)
testDynamicProperty(
@@ -344,7 +360,7 @@ class DynamicPropertiesTestCase : SnapshotTestCase {
KeyPath("Shape Layer 1", "**"),
LottieProperty.DROP_SHADOW_DIRECTION,
LottieValueCallback(30f),
- assetName = "Tests/AnimatedShadow.json"
+ assetName = "Tests/AnimatedShadow.json",
)
testDynamicProperty(
@@ -352,7 +368,7 @@ class DynamicPropertiesTestCase : SnapshotTestCase {
KeyPath("Shape Layer 1", "**"),
LottieProperty.DROP_SHADOW_RADIUS,
LottieValueCallback(40f),
- assetName = "Tests/AnimatedShadow.json"
+ assetName = "Tests/AnimatedShadow.json",
)
testDynamicProperty(
@@ -360,7 +376,44 @@ class DynamicPropertiesTestCase : SnapshotTestCase {
KeyPath("Shape Layer 1", "**"),
LottieProperty.DROP_SHADOW_OPACITY,
LottieValueCallback(0.2f),
- assetName = "Tests/AnimatedShadow.json"
+ assetName = "Tests/AnimatedShadow.json",
+ )
+
+ testDynamicProperty(
+ "Solid Color",
+ KeyPath("Cyan Solid 1", "**"),
+ LottieProperty.COLOR,
+ LottieValueCallback(Color.YELLOW),
+ assetName = "Tests/SolidLayerTransform.json",
+ )
+
+ testDynamicProperty(
+ "Solid Color w/ null",
+ KeyPath("Cyan Solid 1", "**"),
+ LottieProperty.COLOR,
+ LottieValueCallback(null),
+ assetName = "Tests/SolidLayerTransform.json",
+ )
+
+ testDynamicProperty(
+ "Shape",
+ KeyPath("Shape Layer 2", "Polystar 1", "Path 1"),
+ LottieProperty.PATH,
+ object : LottieValueCallback<Path>() {
+ override fun getValue(frameInfo: LottieFrameInfo<Path>): Path? {
+ val rect = RectF()
+ frameInfo.startValue.computeBounds(rect, false)
+ return Path().apply {
+ moveTo(rect.left, rect.top)
+ lineTo(rect.right, rect.top)
+ lineTo(rect.right, rect.bottom)
+ lineTo(rect.left, rect.bottom)
+ lineTo(rect.left, rect.top)
+ close()
+ }
+ }
+ },
+ assetName = "Tests/Polygon.json",
)
withDrawable("Tests/DynamicGradient.json", "Gradient Colors", "Linear Gradient Fill") { drawable ->
@@ -469,4 +522,4 @@ class DynamicPropertiesTestCase : SnapshotTestCase {
drawable.progress = progress
}
}
-} \ No newline at end of file
+}
diff --git a/snapshot-tests/src/androidTest/java/com/airbnb/lottie/snapshots/tests/PainterTestCase.kt b/snapshot-tests/src/androidTest/java/com/airbnb/lottie/snapshots/tests/PainterTestCase.kt
new file mode 100644
index 00000000..5758b99c
--- /dev/null
+++ b/snapshot-tests/src/androidTest/java/com/airbnb/lottie/snapshots/tests/PainterTestCase.kt
@@ -0,0 +1,56 @@
+package com.airbnb.lottie.snapshots.tests
+
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.layout.size
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.layout.ContentScale
+import androidx.compose.ui.unit.dp
+import com.airbnb.lottie.LottieCompositionFactory
+import com.airbnb.lottie.compose.rememberLottiePainter
+import com.airbnb.lottie.snapshots.SnapshotTestCase
+import com.airbnb.lottie.snapshots.SnapshotTestCaseContext
+import com.airbnb.lottie.snapshots.snapshotComposable
+
+class PainterTestCase : SnapshotTestCase {
+ override suspend fun SnapshotTestCaseContext.run() {
+ snapshotComposable("Compose Painter", "0%") {
+ val composition = LottieCompositionFactory.fromAssetSync(context, "Tests/Laugh4.json").value!!
+ val painter = rememberLottiePainter(composition, 0f)
+ Image(
+ painter = painter,
+ contentDescription = "",
+ contentScale = ContentScale.Fit,
+ alignment = Alignment.BottomCenter,
+ modifier = Modifier
+ .size(500.dp, 700.dp),
+ )
+ }
+
+ snapshotComposable("Compose Painter", "50%") {
+ val composition = LottieCompositionFactory.fromAssetSync(context, "Tests/Laugh4.json").value!!
+ val painter = rememberLottiePainter(composition, 0.5f)
+ Image(
+ painter = painter,
+ contentDescription = "",
+ contentScale = ContentScale.Fit,
+ alignment = Alignment.BottomCenter,
+ modifier = Modifier
+ .size(500.dp, 700.dp),
+ )
+ }
+
+ snapshotComposable("Compose Painter", "100%") {
+ val composition = LottieCompositionFactory.fromAssetSync(context, "Tests/Laugh4.json").value!!
+ val painter = rememberLottiePainter(composition, 1f)
+ Image(
+ painter = painter,
+ contentDescription = "",
+ contentScale = ContentScale.Fit,
+ alignment = Alignment.BottomCenter,
+ modifier = Modifier
+ .size(500.dp, 700.dp),
+ )
+ }
+ }
+}
diff --git a/snapshot-tests/src/androidTest/java/com/airbnb/lottie/snapshots/tests/PolygonStrokeTestCase.kt b/snapshot-tests/src/androidTest/java/com/airbnb/lottie/snapshots/tests/PolygonStrokeTestCase.kt
new file mode 100644
index 00000000..470e1793
--- /dev/null
+++ b/snapshot-tests/src/androidTest/java/com/airbnb/lottie/snapshots/tests/PolygonStrokeTestCase.kt
@@ -0,0 +1,13 @@
+package com.airbnb.lottie.snapshots.tests
+
+import com.airbnb.lottie.snapshots.SnapshotTestCase
+import com.airbnb.lottie.snapshots.SnapshotTestCaseContext
+import com.airbnb.lottie.snapshots.withDrawable
+
+class PolygonStrokeTestCase : SnapshotTestCase {
+ override suspend fun SnapshotTestCaseContext.run() {
+ withDrawable("Tests/TriangleLargeStroke.json", "Triangle", "Large Stroke") { drawable ->
+ drawable.progress = 0.40999988f
+ }
+ }
+}
diff --git a/snapshot-tests/src/androidTest/java/com/airbnb/lottie/snapshots/tests/ProdAnimationsTestCase.kt b/snapshot-tests/src/androidTest/java/com/airbnb/lottie/snapshots/tests/ProdAnimationsTestCase.kt
index 903d319e..56d280f2 100644
--- a/snapshot-tests/src/androidTest/java/com/airbnb/lottie/snapshots/tests/ProdAnimationsTestCase.kt
+++ b/snapshot-tests/src/androidTest/java/com/airbnb/lottie/snapshots/tests/ProdAnimationsTestCase.kt
@@ -25,11 +25,7 @@ import java.io.FileInputStream
import java.util.concurrent.atomic.AtomicInteger
import java.util.zip.ZipInputStream
-/**
- * TODO:
- * prod-com.eharmony-lottie-loader-data
- */
-class ProdAnimationsTestCase : SnapshotTestCase {
+class ProdAnimationsTestCase(private val s3AccessKey: String, private val s3SecretKey: String) : SnapshotTestCase {
private val filesChannel = Channel<File>(capacity = 2_048)
override suspend fun SnapshotTestCaseContext.run() = coroutineScope {
@@ -45,8 +41,7 @@ class ProdAnimationsTestCase : SnapshotTestCase {
}
}
- @Suppress("BlockingMethodInNonBlockingContext")
- fun CoroutineScope.parseCompositions(files: ReceiveChannel<File>) = produce(
+ private fun CoroutineScope.parseCompositions(files: ReceiveChannel<File>) = produce(
context = Dispatchers.IO,
capacity = 50,
) {
@@ -63,7 +58,7 @@ class ProdAnimationsTestCase : SnapshotTestCase {
suspend fun SnapshotTestCaseContext.downloadAnimations() = coroutineScope {
val transferUtility = TransferUtility.builder()
.context(context)
- .s3Client(AmazonS3Client(BasicAWSCredentials(BuildConfig.S3AccessKey, BuildConfig.S3SecretKey)))
+ .s3Client(AmazonS3Client(BasicAWSCredentials(s3AccessKey, s3SecretKey)))
.defaultBucket("lottie-prod-animations")
.build()
@@ -92,7 +87,7 @@ class ProdAnimationsTestCase : SnapshotTestCase {
private fun fetchAllObjects(bucket: String): List<S3ObjectSummary> {
val allObjects = mutableListOf<S3ObjectSummary>()
- val s3Client = AmazonS3Client(BasicAWSCredentials(BuildConfig.S3AccessKey, BuildConfig.S3SecretKey))
+ val s3Client = AmazonS3Client(BasicAWSCredentials(s3AccessKey, s3SecretKey))
var request = ListObjectsV2Request().apply {
bucketName = bucket
}
diff --git a/snapshot-tests/src/androidTest/java/com/airbnb/lottie/snapshots/tests/SeekBarTestCase.kt b/snapshot-tests/src/androidTest/java/com/airbnb/lottie/snapshots/tests/SeekBarTestCase.kt
new file mode 100644
index 00000000..4be13fa5
--- /dev/null
+++ b/snapshot-tests/src/androidTest/java/com/airbnb/lottie/snapshots/tests/SeekBarTestCase.kt
@@ -0,0 +1,34 @@
+package com.airbnb.lottie.snapshots.tests
+
+import android.graphics.Canvas
+import android.view.LayoutInflater
+import android.view.View.MeasureSpec
+import com.airbnb.lottie.LottieCompositionFactory
+import com.airbnb.lottie.LottieDrawable
+import com.airbnb.lottie.model.LottieCompositionCache
+import com.airbnb.lottie.snapshots.SnapshotTestCase
+import com.airbnb.lottie.snapshots.SnapshotTestCaseContext
+import com.airbnb.lottie.snapshots.log
+import com.airbnb.lottie.snapshots.databinding.SeekBarBinding
+
+class SeekBarTestCase : SnapshotTestCase {
+ override suspend fun SnapshotTestCaseContext.run() {
+ val composition = LottieCompositionFactory.fromAssetSync(context, "Tests/Thumb.json").value!!
+ val drawable = LottieDrawable()
+ drawable.composition = composition
+ val binding = SeekBarBinding.inflate(LayoutInflater.from(context))
+ binding.seekBar.thumb = drawable
+
+ val widthSpec = MeasureSpec.makeMeasureSpec(512, MeasureSpec.EXACTLY)
+ val heightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)
+ binding.root.measure(widthSpec, heightSpec)
+ binding.root.layout(0, 0, binding.root.measuredWidth, binding.root.measuredHeight)
+ log("Drawing seek bar ${binding.root.measuredWidth}x${binding.root.measuredHeight} -> ${binding.root.width}x${binding.root.height}")
+ val bitmap = bitmapPool.acquire(binding.root.measuredWidth, binding.root.measuredHeight)
+ val canvas = Canvas(bitmap)
+ binding.root.draw(canvas)
+ snapshotter.record(bitmap, "SeekBar", "ThumbDrawable")
+ LottieCompositionCache.getInstance().clear()
+ bitmapPool.release(bitmap)
+ }
+} \ No newline at end of file
diff --git a/snapshot-tests/src/androidTest/java/com/airbnb/lottie/snapshots/tests/TextTestCase.kt b/snapshot-tests/src/androidTest/java/com/airbnb/lottie/snapshots/tests/TextTestCase.kt
index ebb7ea86..1bef3730 100644
--- a/snapshot-tests/src/androidTest/java/com/airbnb/lottie/snapshots/tests/TextTestCase.kt
+++ b/snapshot-tests/src/androidTest/java/com/airbnb/lottie/snapshots/tests/TextTestCase.kt
@@ -1,5 +1,6 @@
package com.airbnb.lottie.snapshots.tests
+import android.graphics.Typeface
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import com.airbnb.lottie.LottieProperty
@@ -137,6 +138,15 @@ class TextTestCase : SnapshotTestCase {
textDelegate.setText("NAME", "\uD83D\uDC91")
}
+ snapshotComposable("Compose FontMap", "Text") {
+ val composition by rememberLottieComposition(LottieCompositionSpec.Asset("Tests/Text.json"))
+ val snapshotReady = LocalSnapshotReady.current
+ LaunchedEffect(snapshotReady, composition != null) {
+ snapshotReady.value = composition != null
+ }
+ LottieAnimation(composition, { 0f }, fontMap = mapOf("Helvetica" to Typeface.SERIF))
+ }
+
snapshotComposable("Compose Dynamic Text", "Emoji") {
val composition by rememberLottieComposition(LottieCompositionSpec.Asset("Tests/DynamicText.json"))
val snapshotReady = LocalSnapshotReady.current
diff --git a/snapshot-tests/src/androidTest/java/com/airbnb/lottie/snapshots/utils/HappoSnapshotter.kt b/snapshot-tests/src/androidTest/java/com/airbnb/lottie/snapshots/utils/HappoSnapshotter.kt
index d73f3060..82ea63a5 100644
--- a/snapshot-tests/src/androidTest/java/com/airbnb/lottie/snapshots/utils/HappoSnapshotter.kt
+++ b/snapshot-tests/src/androidTest/java/com/airbnb/lottie/snapshots/utils/HappoSnapshotter.kt
@@ -27,7 +27,6 @@ import java.net.URLEncoder
import java.nio.charset.Charset
import java.security.MessageDigest
import java.util.*
-import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
import kotlin.coroutines.suspendCoroutine
@@ -42,18 +41,21 @@ private const val TAG = "HappoSnapshotter"
* 2) Call finalizeAndUpload
*/
class HappoSnapshotter(
- private val context: Context,
- private val onSnapshotRecorded: (snapshotName: String, snapshotVariant: String) -> Unit,
+ private val context: Context,
+ s3AccessKey: String,
+ s3SecretKey: String,
+ private val happoApiKey: String,
+ private val happoSecretKey: String,
+ private val onSnapshotRecorded: (snapshotName: String, snapshotVariant: String) -> Unit,
) {
private val recordJob = Job()
private val recordScope = CoroutineScope(Dispatchers.IO + recordJob)
private val bucket = "lottie-happo"
- private val happoApiKey = BuildConfig.HappoApiKey
- private val happoSecretKey = BuildConfig.HappoSecretKey
- private val gitBranch = URLEncoder.encode((if (BuildConfig.BITRISE_GIT_BRANCH == "null") BuildConfig.GIT_BRANCH else BuildConfig.BITRISE_GIT_BRANCH).replace("/", "_"), "UTF-8")
+ private val gitBranch = URLEncoder.encode((BuildConfig.GIT_BRANCH).replace("/", "_"), "UTF-8")
private val androidVersion = "android${Build.VERSION.SDK_INT}"
private val reportNamePrefixes = listOf(BuildConfig.GIT_SHA, gitBranch, BuildConfig.VERSION_NAME).filter { it.isNotBlank() }
+
// Use this when running snapshots locally.
// private val reportNamePrefixes = listOf(System.currentTimeMillis().toString()).filter { it.isNotBlank() }
private val reportNames = reportNamePrefixes.map { "$it-$androidVersion" }
@@ -61,16 +63,16 @@ class HappoSnapshotter(
private val okhttp = OkHttpClient()
private val transferUtility = TransferUtility.builder()
- .context(context)
- .s3Client(AmazonS3Client(BasicAWSCredentials(BuildConfig.S3AccessKey, BuildConfig.S3SecretKey)))
- .defaultBucket(bucket)
- .build()
+ .context(context)
+ .s3Client(AmazonS3Client(BasicAWSCredentials(s3AccessKey, s3SecretKey)))
+ .defaultBucket(bucket)
+ .build()
private val snapshots = mutableListOf<Snapshot>()
suspend fun record(bitmap: Bitmap, animationName: String, variant: String) = withContext(Dispatchers.IO) {
val tempUuid = UUID.randomUUID().toString()
val file = File(context.cacheDir, "$tempUuid.png")
- @Suppress("BlockingMethodInNonBlockingContext")
+
val fileOutputStream = FileOutputStream(file)
val byteOutputStream = ByteArrayOutputStream()
val outputStream = TeeOutputStream(fileOutputStream, byteOutputStream)
@@ -108,7 +110,7 @@ class HappoSnapshotter(
}
Log.d(L.TAG, "Finished creating snapshot report")
reportNames.forEach { reportName ->
- Log.d(L.TAG, "Uploading $reportName")
+ Log.d(L.TAG, "Uploading $reportName")
upload(reportName, json)
}
}
@@ -116,16 +118,15 @@ class HappoSnapshotter(
private suspend fun upload(reportName: String, json: JsonElement) {
val body = json.toString().toRequestBody("application/json".toMediaType())
val request = Request.Builder()
- .addHeader("Authorization", Credentials.basic(happoApiKey, happoSecretKey, Charset.forName("UTF-8")))
- .url("https://happo.io/api/reports/$reportName")
- .post(body)
- .build()
+ .addHeader("Authorization", Credentials.basic(happoApiKey, happoSecretKey, Charset.forName("UTF-8")))
+ .url("https://happo.io/api/reports/$reportName")
+ .post(body)
+ .build()
val response = okhttp.executeDeferred(request)
if (response.isSuccessful) {
Log.d(TAG, "Uploaded $reportName to happo")
} else {
- @Suppress("BlockingMethodInNonBlockingContext")
throw IllegalStateException("Failed to upload $reportName to Happo. Failed with code ${response.code}. " + response.body?.string())
}
}
diff --git a/snapshot-tests/src/main/AndroidManifest.xml b/snapshot-tests/src/main/AndroidManifest.xml
index 0004b3b0..4770eaff 100644
--- a/snapshot-tests/src/main/AndroidManifest.xml
+++ b/snapshot-tests/src/main/AndroidManifest.xml
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.airbnb.lottie.snapshots">
+<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" />
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application
@@ -15,4 +15,4 @@
<activity android:name=".SnapshotTestActivity" />
</application>
-</manifest> \ No newline at end of file
+</manifest>
diff --git a/snapshot-tests/src/main/assets/Tests/AutoOrient.json b/snapshot-tests/src/main/assets/Tests/AutoOrient.json
new file mode 100644
index 00000000..14ced3e7
--- /dev/null
+++ b/snapshot-tests/src/main/assets/Tests/AutoOrient.json
@@ -0,0 +1 @@
+{"v":"5.12.1","fr":29.9700012207031,"ip":0,"op":284.000011567557,"w":400,"h":400,"nm":"ao","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"shape","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.641,"y":0.641},"o":{"x":0.167,"y":0.167},"t":0,"s":[68.5,44.5,0],"to":[2.395,1.173,0],"ti":[-52,-114,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.438,"y":0.438},"t":83.533,"s":[304.5,148.5,0],"to":[-8,108,0],"ti":[-2.164,-1.328,0]},{"i":{"x":0.575,"y":0.575},"o":{"x":0.167,"y":0.167},"t":114,"s":[340.035,204.247,0],"to":[60.399,77.002,0],"ti":[118,-2,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.407,"y":0.407},"t":189.251,"s":[240.5,270.5,0],"to":[-118,2,0],"ti":[52.68,50.729,0]},{"t":283.000011526826,"s":[14.5,222.5,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":1,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-172.23,-7.099],[-161.73,29.401],[-73.23,-21.099]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[54.125,22.142],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":284.000011567557,"st":0,"ct":1,"bm":0}],"markers":[],"props":{}} \ No newline at end of file
diff --git a/snapshot-tests/src/main/assets/Tests/BounceEasings.json b/snapshot-tests/src/main/assets/Tests/BounceEasings.json
new file mode 100644
index 00000000..6cd663d4
--- /dev/null
+++ b/snapshot-tests/src/main/assets/Tests/BounceEasings.json
@@ -0,0 +1,898 @@
+{
+ "v": "5.7.5",
+ "fr": 100,
+ "ip": 0,
+ "op": 400,
+ "w": 800,
+ "h": 1000,
+ "nm": "Comp 1",
+ "ddd": 0,
+ "assets": [],
+ "layers": [
+ {
+ "ddd": 0,
+ "ind": 0,
+ "ty": 4,
+ "nm": "Bounce out curve",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "hd": false,
+ "shapes": [
+ {
+ "ty": "gr",
+ "hd": false,
+ "it": [
+ {
+ "ty": "rc",
+ "hd": false,
+ "d": 1,
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ }
+ },
+ {
+ "ty": "fl",
+ "c": {
+ "a": 0,
+ "k": [
+ 0.21176470588235294,
+ 0.9176470588235294,
+ 1
+ ],
+ "ix": 2
+ },
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 2
+ },
+ "r": 1,
+ "bm": 0,
+ "nm": "Fill 1",
+ "mn": "ADBE Vector Graphic - Fill",
+ "hd": false
+ },
+ {
+ "ty": "tm",
+ "s": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "e": {
+ "a": 0,
+ "k": 100,
+ "ix": 2
+ },
+ "o": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "m": 1,
+ "hd": false
+ },
+ {
+ "ty": "tr",
+ "hd": false,
+ "p": {
+ "a": 1,
+ "k": [
+ {
+ "t": 0,
+ "s": [
+ 510,
+ 100
+ ],
+ "i": {
+ "x": [
+ 0.2
+ ],
+ "y": [
+ 1
+ ]
+ },
+ "o": {
+ "x": [
+ 0.5
+ ],
+ "y": [
+ -0.5
+ ]
+ },
+ "ti": [
+ -268.19047619047615,
+ -294.42857142857247
+ ],
+ "to": [
+ -268.19047619047615,
+ 238.90476190476068
+ ]
+ },
+ {
+ "t": 400,
+ "s": [
+ 510,
+ 900
+ ],
+ "i": {
+ "x": [
+ 1
+ ],
+ "y": [
+ 1
+ ]
+ },
+ "o": {
+ "x": [
+ 0
+ ],
+ "y": [
+ 0
+ ]
+ }
+ }
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 2
+ },
+ "sk": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "sa": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ }
+ }
+ ]
+ }
+ ],
+ "ip": 0,
+ "op": 401,
+ "st": 0,
+ "bm": 0
+ },
+ {
+ "ddd": 0,
+ "ind": 1,
+ "ty": 4,
+ "nm": "Bounce in curve",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "hd": false,
+ "shapes": [
+ {
+ "ty": "gr",
+ "hd": false,
+ "it": [
+ {
+ "ty": "rc",
+ "hd": false,
+ "d": 1,
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ }
+ },
+ {
+ "ty": "fl",
+ "c": {
+ "a": 0,
+ "k": [
+ 1,
+ 0.3411764705882353,
+ 0.21176470588235294
+ ],
+ "ix": 2
+ },
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 2
+ },
+ "r": 1,
+ "bm": 0,
+ "nm": "Fill 1",
+ "mn": "ADBE Vector Graphic - Fill",
+ "hd": false
+ },
+ {
+ "ty": "tm",
+ "s": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "e": {
+ "a": 0,
+ "k": 100,
+ "ix": 2
+ },
+ "o": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "m": 1,
+ "hd": false
+ },
+ {
+ "ty": "tr",
+ "hd": false,
+ "p": {
+ "a": 1,
+ "k": [
+ {
+ "t": 0,
+ "s": [
+ 360,
+ 100
+ ],
+ "i": {
+ "x": [
+ 0.5
+ ],
+ "y": [
+ 1.5
+ ]
+ },
+ "o": {
+ "x": [
+ 0.8
+ ],
+ "y": [
+ 0
+ ]
+ },
+ "ti": [
+ -268.19047619047615,
+ -294.42857142857247
+ ],
+ "to": [
+ -268.19047619047615,
+ 238.90476190476068
+ ]
+ },
+ {
+ "t": 400,
+ "s": [
+ 360,
+ 900
+ ],
+ "i": {
+ "x": [
+ 1
+ ],
+ "y": [
+ 1
+ ]
+ },
+ "o": {
+ "x": [
+ 0
+ ],
+ "y": [
+ 0
+ ]
+ }
+ }
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 2
+ },
+ "sk": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "sa": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ }
+ }
+ ]
+ }
+ ],
+ "ip": 0,
+ "op": 401,
+ "st": 0,
+ "bm": 0
+ },
+ {
+ "ddd": 0,
+ "ind": 2,
+ "ty": 4,
+ "nm": "Bounce in",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "hd": false,
+ "shapes": [
+ {
+ "ty": "gr",
+ "hd": false,
+ "it": [
+ {
+ "ty": "rc",
+ "hd": false,
+ "d": 1,
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ }
+ },
+ {
+ "ty": "fl",
+ "c": {
+ "a": 0,
+ "k": [
+ 1,
+ 0.7254901960784313,
+ 0.5764705882352941
+ ],
+ "ix": 2
+ },
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 2
+ },
+ "r": 1,
+ "bm": 0,
+ "nm": "Fill 1",
+ "mn": "ADBE Vector Graphic - Fill",
+ "hd": false
+ },
+ {
+ "ty": "tm",
+ "s": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "e": {
+ "a": 0,
+ "k": 100,
+ "ix": 2
+ },
+ "o": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "m": 1,
+ "hd": false
+ },
+ {
+ "ty": "tr",
+ "hd": false,
+ "p": {
+ "a": 1,
+ "k": [
+ {
+ "t": 0,
+ "s": [
+ 650,
+ 100
+ ],
+ "i": {
+ "x": [
+ 0.5
+ ],
+ "y": [
+ 1.5
+ ]
+ },
+ "o": {
+ "x": [
+ 0.8
+ ],
+ "y": [
+ 0
+ ]
+ }
+ },
+ {
+ "t": 400,
+ "s": [
+ 650,
+ 900
+ ],
+ "i": {
+ "x": [
+ 1
+ ],
+ "y": [
+ 1
+ ]
+ },
+ "o": {
+ "x": [
+ 0
+ ],
+ "y": [
+ 0
+ ]
+ }
+ }
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 2
+ },
+ "sk": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "sa": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ }
+ }
+ ]
+ }
+ ],
+ "ip": 0,
+ "op": 401,
+ "st": 0,
+ "bm": 0
+ },
+ {
+ "ddd": 0,
+ "ind": 3,
+ "ty": 4,
+ "nm": "Bounce out",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "hd": false,
+ "shapes": [
+ {
+ "ty": "gr",
+ "hd": false,
+ "it": [
+ {
+ "ty": "rc",
+ "hd": false,
+ "d": 1,
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ }
+ },
+ {
+ "ty": "fl",
+ "c": {
+ "a": 0,
+ "k": [
+ 1,
+ 0.8392156862745098,
+ 0.25098039215686274
+ ],
+ "ix": 2
+ },
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 2
+ },
+ "r": 1,
+ "bm": 0,
+ "nm": "Fill 1",
+ "mn": "ADBE Vector Graphic - Fill",
+ "hd": false
+ },
+ {
+ "ty": "tm",
+ "s": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "e": {
+ "a": 0,
+ "k": 100,
+ "ix": 2
+ },
+ "o": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "m": 1,
+ "hd": false
+ },
+ {
+ "ty": "tr",
+ "hd": false,
+ "p": {
+ "a": 1,
+ "k": [
+ {
+ "t": 0,
+ "s": [
+ 150,
+ 100
+ ],
+ "i": {
+ "x": [
+ 0.2
+ ],
+ "y": [
+ 1
+ ]
+ },
+ "o": {
+ "x": [
+ 0.5
+ ],
+ "y": [
+ -0.5
+ ]
+ }
+ },
+ {
+ "t": 400,
+ "s": [
+ 150,
+ 900
+ ],
+ "i": {
+ "x": [
+ 1
+ ],
+ "y": [
+ 1
+ ]
+ },
+ "o": {
+ "x": [
+ 0
+ ],
+ "y": [
+ 0
+ ]
+ }
+ }
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 2
+ },
+ "sk": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "sa": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ }
+ }
+ ]
+ }
+ ],
+ "ip": 0,
+ "op": 401,
+ "st": 0,
+ "bm": 0
+ }
+ ],
+ "markers": []
+}
diff --git a/snapshot-tests/src/main/assets/Tests/BoxPosition.json b/snapshot-tests/src/main/assets/Tests/BoxPosition.json
new file mode 100644
index 00000000..631b67db
--- /dev/null
+++ b/snapshot-tests/src/main/assets/Tests/BoxPosition.json
@@ -0,0 +1 @@
+{"v":"5.10.2","fr":29.9700012207031,"ip":0,"op":180.00000733155,"w":1920,"h":1080,"nm":"Box Position","ddd":0,"assets":[],"fonts":{"list":[{"fName":"Helvetica","fFamily":"Helvetica","fStyle":"Regular","ascent":71.8994140625}]},"layers":[{"ddd":0,"ind":1,"ty":5,"nm":"Blue","sr":1,"ks":{"o":{"a":0,"k":50,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[1000,150,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"t":{"d":{"k":[{"s":{"sz":[284,459],"ps":[-123,-186.5],"s":119,"f":"Helvetica","t":"123\u0003ABC\u0003D","ca":0,"j":2,"tr":0,"lh":89,"ls":0,"fc":[0.184,0,1]},"t":0}]},"p":{},"m":{"g":1,"a":{"a":0,"k":[0,0],"ix":2}},"a":[]},"ip":0,"op":180.00000733155,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":5,"nm":"Black","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[1000,150,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"t":{"d":{"k":[{"s":{"sz":[284,369],"ps":[-122,-95.560302734375],"s":119,"f":"Helvetica","t":"ABC\u0003D\u0003E","ca":0,"j":2,"tr":0,"lh":89,"ls":0,"fc":[0,0,0]},"t":0}]},"p":{},"m":{"g":1,"a":{"a":0,"k":[0,0],"ix":2}},"a":[]},"ip":0,"op":180.00000733155,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Shape Layer 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[1016,68,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[32.289,107.146,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[885.37,241.53],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[13.837,159.727],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,144.052],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":180.00000733155,"st":0,"ct":1,"bm":0}],"markers":[],"chars":[{"ch":"A","size":119,"style":"Regular","w":66.7,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[21.973,-29.395],[33.545,-61.084],[44.434,-29.395]],"c":true},"ix":2},"nm":"A","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[1.465,0],[11.426,0],[19.189,-21.484],[47.559,-21.484],[54.834,0],[65.479,0],[39.453,-71.729],[28.467,-71.729]],"c":true},"ix":2},"nm":"A","mn":"ADBE Vector Shape - Group","hd":false}],"nm":"A","np":5,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}]},"fFamily":"Helvetica"},{"ch":"B","size":119,"style":"Regular","w":66.7,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[-2.171,-0.911],[0,-4.688],[3.618,-1.79],[4.144,0]],"o":[[0,0],[0,0],[4.276,0],[3.848,1.628],[0,4.655],[-2.303,1.14],[0,0]],"v":[[16.895,-41.406],[16.895,-63.623],[34.41,-63.623],[44.081,-62.256],[49.854,-52.783],[44.426,-43.115],[34.756,-41.406]],"c":true},"ix":2},"nm":"B","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[-2.498,-1.009],[0,-5.208],[1.61,-2.18],[6.013,0]],"o":[[0,0],[0,0],[4.108,0],[4.699,1.888],[0,3.093],[-2.563,3.451],[0,0]],"v":[[16.895,-8.301],[16.895,-33.545],[36.364,-33.545],[46.272,-32.031],[53.32,-21.387],[50.905,-13.477],[38.04,-8.301]],"c":true},"ix":2},"nm":"B","mn":"ADBE Vector Shape - Group","hd":false},{"ind":2,"ty":"sh","ix":3,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[-4.356,5.599],[0,4.623],[3.223,3.125],[3.678,1.4],[-1.335,1.53],[0,4.525],[2.097,2.962],[8.453,0],[0,0]],"o":[[0,0],[9.498,0],[2.914,-3.743],[0,-5.501],[-1.823,-1.758],[2.506,-1.27],[2.571,-2.897],[0,-3.873],[-3.572,-5.013],[0,0],[0,0]],"v":[[7.373,0],[37.883,0],[58.665,-8.398],[63.037,-20.947],[58.203,-33.887],[49.951,-38.623],[55.713,-42.822],[59.57,-53.955],[56.425,-64.209],[38.387,-71.729],[7.373,-71.729]],"c":true},"ix":2},"nm":"B","mn":"ADBE Vector Shape - Group","hd":false}],"nm":"B","np":6,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}]},"fFamily":"Helvetica"},{"ch":"C","size":119,"style":"Regular","w":72.22,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[8.976,0],[5.984,-7.361],[0,-10.586],[-7.368,-6.677],[-8.237,0],[-5.631,6.152],[-0.837,7.552],[0,0],[1.95,-2.897],[7.035,0],[3.853,5.229],[0,8.406],[-4.268,4.968],[-6.937,0],[-3.149,-2.702],[-1.055,-4.622],[0,0],[4.955,4.785]],"o":[[-10.521,0],[-5.534,6.776],[0,13.942],[5.598,5.049],[9.557,0],[4.697,-5.11],[0,0],[-0.96,4.72],[-3.677,5.502],[-7.642,0],[-3.854,-5.229],[0,-10.262],[4.268,-4.968],[5.69,0],[3.149,2.702],[0,0],[-0.547,-6.087],[-4.955,-4.785]],"v":[[37.453,-73.682],[12.695,-62.64],[4.395,-36.597],[15.447,-5.669],[36.201,1.904],[58.984,-7.324],[67.285,-26.318],[57.812,-26.318],[53.448,-14.893],[37.379,-6.641],[20.135,-14.484],[14.355,-34.936],[20.757,-57.782],[37.564,-65.234],[50.823,-61.182],[57.129,-50.195],[66.602,-50.195],[58.349,-66.504]],"c":true},"ix":2},"nm":"C","mn":"ADBE Vector Shape - Group","hd":false}],"nm":"C","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}]},"fFamily":"Helvetica"},{"ch":"\u0003","size":119,"style":"Regular","w":0,"fFamily":"Helvetica"},{"ch":"D","size":119,"style":"Regular","w":72.22,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[-3.454,-4.915],[0,-8.887],[0.478,-2.702],[1.878,-2.897],[3.692,-1.27],[3.214,0]],"o":[[0,0],[0,0],[7.671,0],[3.453,4.916],[0,2.312],[-0.828,4.525],[-2.356,3.613],[-2.069,0.684],[0,0]],"v":[[17.871,-8.301],[17.871,-63.379],[34.725,-63.379],[51.411,-56.006],[56.592,-35.303],[55.875,-27.783],[51.817,-16.65],[42.746,-9.326],[34.821,-8.301]],"c":true},"ix":2},"nm":"D","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[-4.979,10.873],[0,7.617],[4.786,6.316],[9.734,0],[0,0]],"o":[[0,0],[12.142,0],[2.827,-6.152],[0,-9.863],[-5.365,-6.998],[0,0],[0,0]],"v":[[8.057,0],[36.63,0],[62.312,-16.309],[66.553,-36.963],[59.373,-61.23],[36.726,-71.729],[8.057,-71.729]],"c":true},"ix":2},"nm":"D","mn":"ADBE Vector Shape - Group","hd":false}],"nm":"D","np":5,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}]},"fFamily":"Helvetica"},{"ch":"E","size":119,"style":"Regular","w":66.7,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[8.545,0],[61.328,0],[61.328,-8.545],[18.018,-8.545],[18.018,-32.861],[57.373,-32.861],[57.373,-41.162],[18.018,-41.162],[18.018,-62.939],[60.596,-62.939],[60.596,-71.729],[8.545,-71.729]],"c":true},"ix":2},"nm":"E","mn":"ADBE Vector Shape - Group","hd":false}],"nm":"E","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}]},"fFamily":"Helvetica"},{"ch":"1","size":119,"style":"Regular","w":48.24,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[2.506,-1.448],[6.348,-0.618],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[-1.237,5.404],[-2.507,1.449],[0,0],[0,0]],"v":[[26.025,-49.512],[26.025,0],[35.4,0],[35.4,-69.629],[28.467,-69.629],[22.852,-59.351],[9.57,-56.25],[9.57,-49.512]],"c":true},"ix":2},"nm":"1","mn":"ADBE Vector Shape - Group","hd":false}],"nm":"1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}]},"fFamily":"Helvetica"},{"ch":"2","size":119,"style":"Regular","w":55.62,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0.325,-6.022],[0,0],[0,0],[0,0],[-3.255,2.832],[-3.191,1.758],[0,0],[-2.279,2.312],[0,5.599],[3.694,4.33],[8.008,0],[3.809,-6.77],[0.098,-6.087],[0,0],[-1.237,2.449],[-6.023,0],[-2.441,-2.397],[0,-4.077],[2.669,-2.707],[4.199,-2.441],[0,0],[2.164,-4.459]],"o":[[0,0],[0,0],[0,0],[0.846,-3.45],[1.823,-1.595],[0,0],[5.891,-3.288],[3.938,-3.971],[0,-5.305],[-3.695,-4.329],[-9.636,0],[-2.148,3.841],[0,0],[0.13,-4.341],[2.311,-4.57],[4.069,0],[2.441,2.397],[0,3.49],[-1.693,1.729],[0,0],[-6.283,3.646],[-2.165,4.46]],"v":[[3.125,0],[51.123,0],[51.123,-8.301],[12.939,-8.301],[19.092,-17.725],[26.611,-22.754],[33.301,-26.465],[45.557,-34.863],[51.465,-49.219],[45.923,-63.672],[28.369,-70.166],[8.203,-60.01],[4.834,-45.117],[13.77,-45.117],[15.82,-55.302],[28.32,-62.158],[38.086,-58.562],[41.748,-48.851],[37.744,-39.556],[28.906,-33.301],[19.531,-27.881],[6.86,-15.723]],"c":true},"ix":2},"nm":"2","mn":"ADBE Vector Shape - Group","hd":false}],"nm":"2","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}]},"fFamily":"Helvetica"},{"ch":"3","size":119,"style":"Regular","w":55.62,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-8.269,0],[-4.395,4.199],[0,6.673],[2.067,2.881],[3.678,1.009],[-1.433,1.66],[0,4.297],[3.906,3.174],[7.129,0],[3.58,-6.51],[0,-5.208],[0,0],[-1.237,2.148],[-5.859,0],[-2.474,-1.888],[0,-3.483],[3.711,-1.823],[3.288,0],[0.618,0.033],[0.911,0.098],[0,0],[-0.603,0.017],[-0.586,0],[-2.946,-1.92],[0,-4.817],[2.799,-2.473],[4.622,0],[2.278,3.679],[0.391,4.525],[0,0],[-3.728,-4.541]],"o":[[8.073,0],[4.395,-4.199],[0,-4.166],[-2.068,-2.881],[2.278,-0.944],[2.311,-2.669],[0,-6.022],[-3.906,-3.174],[-9.017,0],[-2.084,3.646],[0,0],[0.163,-3.971],[2.246,-3.906],[3.288,0],[2.473,1.888],[0,4.688],[-2.116,1.042],[-0.716,0],[-0.619,-0.032],[0,0],[0.618,-0.032],[0.602,-0.016],[5.241,0],[2.946,1.921],[0,3.906],[-2.8,2.474],[-5.957,0],[-1.302,-2.051],[0,0],[0,6.511],[3.727,4.541]],"v":[[25.977,1.904],[44.678,-4.395],[51.27,-20.703],[48.169,-31.274],[39.551,-37.109],[45.117,-41.016],[48.584,-51.465],[42.725,-65.259],[26.172,-70.02],[7.275,-60.254],[4.15,-46.973],[12.842,-46.973],[14.941,-56.152],[27.1,-62.012],[35.742,-59.18],[39.453,-51.123],[33.887,-41.357],[25.781,-39.795],[23.779,-39.844],[21.484,-40.039],[21.484,-32.275],[23.315,-32.349],[25.098,-32.373],[37.378,-29.492],[41.797,-19.385],[37.598,-9.814],[26.465,-6.104],[14.111,-11.621],[11.572,-21.484],[2.393,-21.484],[7.983,-4.907]],"c":true},"ix":2},"nm":"3","mn":"ADBE Vector Shape - Group","hd":false}],"nm":"3","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}]},"fFamily":"Helvetica"}]} \ No newline at end of file
diff --git a/snapshot-tests/src/main/assets/Tests/DefaultLineJoinCap.json b/snapshot-tests/src/main/assets/Tests/DefaultLineJoinCap.json
new file mode 100644
index 00000000..7e3e4099
--- /dev/null
+++ b/snapshot-tests/src/main/assets/Tests/DefaultLineJoinCap.json
@@ -0,0 +1,5855 @@
+{
+ "v": "5.7.5",
+ "fr": 100,
+ "ip": 0,
+ "op": 93,
+ "w": 360,
+ "h": 360,
+ "nm": "Comp 1",
+ "ddd": 0,
+ "assets": [
+ {
+ "id": "2",
+ "layers": [
+ {
+ "ddd": 0,
+ "ind": 6,
+ "ty": 3,
+ "nm": "",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 1,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 10180,
+ 10173
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0
+ },
+ {
+ "ddd": 0,
+ "ind": 8,
+ "ty": 3,
+ "nm": "",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 1,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 45,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0,
+ "parent": 6
+ },
+ {
+ "ddd": 0,
+ "ind": 7,
+ "ty": 3,
+ "nm": "",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 1,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0,
+ "parent": 8
+ },
+ {
+ "ddd": 0,
+ "refId": "1",
+ "w": 20000,
+ "h": 20000,
+ "ind": 9,
+ "ty": 0,
+ "nm": "Shape Layer 6",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": -45,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 10000,
+ 10000
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 120,
+ 120
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0,
+ "hd": false,
+ "parent": 7
+ }
+ ]
+ },
+ {
+ "id": "1",
+ "layers": [
+ {
+ "ddd": 0,
+ "ind": 1,
+ "ty": 3,
+ "nm": "",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 1,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 10000,
+ 10000
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0
+ },
+ {
+ "ddd": 0,
+ "ind": 3,
+ "ty": 3,
+ "nm": "",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 1,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 45,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0,
+ "parent": 1
+ },
+ {
+ "ddd": 0,
+ "ind": 2,
+ "ty": 3,
+ "nm": "",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 1,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0,
+ "parent": 3
+ },
+ {
+ "ddd": 0,
+ "ind": 4,
+ "ty": 3,
+ "nm": "",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": -45,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0,
+ "parent": 2
+ },
+ {
+ "ddd": 0,
+ "ind": 5,
+ "ty": 4,
+ "nm": "Path",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "hd": false,
+ "parent": 4,
+ "shapes": [
+ {
+ "ty": "gr",
+ "hd": false,
+ "it": [
+ {
+ "ty": "sh",
+ "hd": false,
+ "d": 1,
+ "ks": {
+ "a": 0,
+ "k": {
+ "c": false,
+ "v": [
+ [
+ -35.5,
+ 2.5
+ ],
+ [
+ -11,
+ 27.5
+ ],
+ [
+ 41,
+ -22
+ ]
+ ],
+ "i": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ "o": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ }
+ }
+ },
+ {
+ "ty": "st",
+ "c": {
+ "a": 0,
+ "k": [
+ 1,
+ 1,
+ 1
+ ],
+ "ix": 2
+ },
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 2
+ },
+ "w": {
+ "a": 0,
+ "k": 6,
+ "ix": 2
+ },
+ "hd": false
+ },
+ {
+ "ty": "tm",
+ "s": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "e": {
+ "a": 0,
+ "k": 100,
+ "ix": 2
+ },
+ "o": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "m": 1,
+ "hd": false
+ },
+ {
+ "ty": "tr",
+ "hd": false,
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 2
+ },
+ "sk": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "sa": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ }
+ }
+ ]
+ }
+ ],
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0
+ }
+ ]
+ },
+ {
+ "id": "4",
+ "layers": [
+ {
+ "ddd": 0,
+ "ind": 23,
+ "ty": 3,
+ "nm": "",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 1,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 10179.326,
+ 10179.326
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0
+ },
+ {
+ "ddd": 0,
+ "ind": 25,
+ "ty": 3,
+ "nm": "",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 1,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 45,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0,
+ "parent": 23
+ },
+ {
+ "ddd": 0,
+ "ind": 24,
+ "ty": 3,
+ "nm": "",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 1,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0,
+ "parent": 25
+ },
+ {
+ "ddd": 0,
+ "refId": "3",
+ "w": 20000,
+ "h": 20000,
+ "ind": 26,
+ "ty": 0,
+ "nm": "Shape Layer 5",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": -45,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 10011,
+ 9992
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 19,
+ 19
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0,
+ "hd": false,
+ "parent": 24
+ }
+ ]
+ },
+ {
+ "id": "3",
+ "layers": [
+ {
+ "ddd": 0,
+ "ind": 14,
+ "ty": 3,
+ "nm": "",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 1,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 10000,
+ 10000
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0
+ },
+ {
+ "ddd": 0,
+ "ind": 16,
+ "ty": 3,
+ "nm": "",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 1,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 45,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0,
+ "parent": 14
+ },
+ {
+ "ddd": 0,
+ "ind": 15,
+ "ty": 3,
+ "nm": "",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 1,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0,
+ "parent": 16
+ },
+ {
+ "ddd": 0,
+ "ind": 17,
+ "ty": 3,
+ "nm": "",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": -45,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0,
+ "parent": 15
+ },
+ {
+ "ddd": 0,
+ "ind": 18,
+ "ty": 3,
+ "nm": "",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 1,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 11.674,
+ -7.326
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0,
+ "parent": 17
+ },
+ {
+ "ddd": 0,
+ "ind": 20,
+ "ty": 3,
+ "nm": "",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 1,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 45,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0,
+ "parent": 18
+ },
+ {
+ "ddd": 0,
+ "ind": 19,
+ "ty": 3,
+ "nm": "",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 1,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0,
+ "parent": 20
+ },
+ {
+ "ddd": 0,
+ "ind": 21,
+ "ty": 3,
+ "nm": "",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": -45,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0,
+ "parent": 19
+ },
+ {
+ "ddd": 0,
+ "ind": 22,
+ "ty": 4,
+ "nm": "Ellipse Path 1-1",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "hd": false,
+ "parent": 21,
+ "shapes": [
+ {
+ "ty": "gr",
+ "hd": false,
+ "it": [
+ {
+ "ty": "el",
+ "hd": false,
+ "d": 1,
+ "s": {
+ "a": 0,
+ "k": [
+ 149.348,
+ 149.348
+ ],
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ }
+ },
+ {
+ "ty": "fl",
+ "c": {
+ "a": 0,
+ "k": [
+ 0.1569,
+ 0.7059000000000001,
+ 0.3882
+ ],
+ "ix": 2
+ },
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 2
+ },
+ "r": 1,
+ "bm": 0,
+ "nm": "Fill 1",
+ "mn": "ADBE Vector Graphic - Fill",
+ "hd": false
+ },
+ {
+ "ty": "tm",
+ "s": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "e": {
+ "a": 0,
+ "k": 100,
+ "ix": 2
+ },
+ "o": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "m": 1,
+ "hd": false
+ },
+ {
+ "ty": "tr",
+ "hd": false,
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 2
+ },
+ "sk": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "sa": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ }
+ }
+ ]
+ }
+ ],
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0
+ }
+ ]
+ },
+ {
+ "id": "5",
+ "layers": [
+ {
+ "ddd": 0,
+ "ind": 31,
+ "ty": 3,
+ "nm": "",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 1,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 10168.326,
+ 10187.326
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0
+ },
+ {
+ "ddd": 0,
+ "ind": 33,
+ "ty": 3,
+ "nm": "",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 1,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 45,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0,
+ "parent": 31
+ },
+ {
+ "ddd": 0,
+ "ind": 32,
+ "ty": 3,
+ "nm": "",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 1,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0,
+ "parent": 33
+ },
+ {
+ "ddd": 0,
+ "ind": 34,
+ "ty": 3,
+ "nm": "",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": -45,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0,
+ "parent": 32
+ },
+ {
+ "ddd": 0,
+ "ind": 35,
+ "ty": 3,
+ "nm": "",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 1,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0,
+ "parent": 34
+ },
+ {
+ "ddd": 0,
+ "ind": 37,
+ "ty": 3,
+ "nm": "",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 1,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 45,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0,
+ "parent": 35
+ },
+ {
+ "ddd": 0,
+ "ind": 36,
+ "ty": 3,
+ "nm": "",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 1,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0,
+ "parent": 37
+ },
+ {
+ "ddd": 0,
+ "ind": 38,
+ "ty": 3,
+ "nm": "",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": -45,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0,
+ "parent": 36
+ },
+ {
+ "ddd": 0,
+ "ind": 39,
+ "ty": 3,
+ "nm": "",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 1,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 11.674,
+ -7.326
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0,
+ "parent": 38
+ },
+ {
+ "ddd": 0,
+ "ind": 41,
+ "ty": 3,
+ "nm": "",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 1,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 45,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0,
+ "parent": 39
+ },
+ {
+ "ddd": 0,
+ "ind": 40,
+ "ty": 3,
+ "nm": "",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 1,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0,
+ "parent": 41
+ },
+ {
+ "ddd": 0,
+ "ind": 42,
+ "ty": 3,
+ "nm": "",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": -45,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0,
+ "parent": 40
+ },
+ {
+ "ddd": 0,
+ "ind": 43,
+ "ty": 4,
+ "nm": "Ellipse Path 1-2",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "hd": false,
+ "parent": 42,
+ "shapes": [
+ {
+ "ty": "gr",
+ "hd": false,
+ "it": [
+ {
+ "ty": "el",
+ "hd": false,
+ "d": 1,
+ "s": {
+ "a": 0,
+ "k": [
+ 149.348,
+ 149.348
+ ],
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ }
+ },
+ {
+ "ty": "st",
+ "c": {
+ "a": 0,
+ "k": [
+ 0.1569,
+ 0.7059000000000001,
+ 0.3882
+ ],
+ "ix": 2
+ },
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 2
+ },
+ "w": {
+ "a": 0,
+ "k": 6,
+ "ix": 2
+ },
+ "hd": false
+ },
+ {
+ "ty": "tm",
+ "s": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "e": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "o": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "m": 1,
+ "hd": false
+ },
+ {
+ "ty": "tr",
+ "hd": false,
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 2
+ },
+ "sk": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "sa": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ }
+ }
+ ]
+ }
+ ],
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0
+ }
+ ]
+ },
+ {
+ "id": "6",
+ "layers": [
+ {
+ "ddd": 0,
+ "ind": 48,
+ "ty": 3,
+ "nm": "",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 1,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 10168.326,
+ 10187.326
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0
+ },
+ {
+ "ddd": 0,
+ "ind": 50,
+ "ty": 3,
+ "nm": "",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 1,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 45,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0,
+ "parent": 48
+ },
+ {
+ "ddd": 0,
+ "ind": 49,
+ "ty": 3,
+ "nm": "",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 1,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0,
+ "parent": 50
+ },
+ {
+ "ddd": 0,
+ "ind": 51,
+ "ty": 3,
+ "nm": "",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": -45,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0,
+ "parent": 49
+ },
+ {
+ "ddd": 0,
+ "ind": 52,
+ "ty": 3,
+ "nm": "",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 1,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0,
+ "parent": 51
+ },
+ {
+ "ddd": 0,
+ "ind": 54,
+ "ty": 3,
+ "nm": "",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 1,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 45,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0,
+ "parent": 52
+ },
+ {
+ "ddd": 0,
+ "ind": 53,
+ "ty": 3,
+ "nm": "",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 1,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0,
+ "parent": 54
+ },
+ {
+ "ddd": 0,
+ "ind": 55,
+ "ty": 3,
+ "nm": "",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": -45,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0,
+ "parent": 53
+ },
+ {
+ "ddd": 0,
+ "ind": 56,
+ "ty": 3,
+ "nm": "",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 1,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 11.674,
+ -7.326
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0,
+ "parent": 55
+ },
+ {
+ "ddd": 0,
+ "ind": 58,
+ "ty": 3,
+ "nm": "",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 1,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 45,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0,
+ "parent": 56
+ },
+ {
+ "ddd": 0,
+ "ind": 57,
+ "ty": 3,
+ "nm": "",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 1,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0,
+ "parent": 58
+ },
+ {
+ "ddd": 0,
+ "ind": 59,
+ "ty": 3,
+ "nm": "",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": -45,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0,
+ "parent": 57
+ },
+ {
+ "ddd": 0,
+ "ind": 60,
+ "ty": 4,
+ "nm": "Ellipse Path 1-3",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "hd": false,
+ "parent": 59,
+ "shapes": [
+ {
+ "ty": "gr",
+ "hd": false,
+ "it": [
+ {
+ "ty": "el",
+ "hd": false,
+ "d": 1,
+ "s": {
+ "a": 0,
+ "k": [
+ 149.348,
+ 149.348
+ ],
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ }
+ },
+ {
+ "ty": "st",
+ "c": {
+ "a": 0,
+ "k": [
+ 0.7922,
+ 0.8118,
+ 0.8235
+ ],
+ "ix": 2
+ },
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 2
+ },
+ "w": {
+ "a": 0,
+ "k": 6,
+ "ix": 2
+ },
+ "hd": false
+ },
+ {
+ "ty": "tm",
+ "s": {
+ "a": 0,
+ "k": 3,
+ "ix": 2
+ },
+ "e": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "o": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "m": 1,
+ "hd": false
+ },
+ {
+ "ty": "tr",
+ "hd": false,
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 2
+ },
+ "sk": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "sa": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ }
+ }
+ ]
+ }
+ ],
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0
+ }
+ ]
+ },
+ {
+ "id": "7",
+ "layers": [
+ {
+ "ddd": 0,
+ "ind": 65,
+ "ty": 3,
+ "nm": "",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 1,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 10168.326,
+ 10187.326
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0
+ },
+ {
+ "ddd": 0,
+ "ind": 67,
+ "ty": 3,
+ "nm": "",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 1,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 45,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0,
+ "parent": 65
+ },
+ {
+ "ddd": 0,
+ "ind": 66,
+ "ty": 3,
+ "nm": "",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 1,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0,
+ "parent": 67
+ },
+ {
+ "ddd": 0,
+ "ind": 68,
+ "ty": 3,
+ "nm": "",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": -45,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0,
+ "parent": 66
+ },
+ {
+ "ddd": 0,
+ "ind": 69,
+ "ty": 3,
+ "nm": "",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 1,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0,
+ "parent": 68
+ },
+ {
+ "ddd": 0,
+ "ind": 71,
+ "ty": 3,
+ "nm": "",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 1,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 45,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0,
+ "parent": 69
+ },
+ {
+ "ddd": 0,
+ "ind": 70,
+ "ty": 3,
+ "nm": "",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 1,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0,
+ "parent": 71
+ },
+ {
+ "ddd": 0,
+ "ind": 72,
+ "ty": 3,
+ "nm": "",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": -45,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0,
+ "parent": 70
+ },
+ {
+ "ddd": 0,
+ "ind": 73,
+ "ty": 3,
+ "nm": "",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 1,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 11.674,
+ -7.326
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0,
+ "parent": 72
+ },
+ {
+ "ddd": 0,
+ "ind": 75,
+ "ty": 3,
+ "nm": "",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 1,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 45,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0,
+ "parent": 73
+ },
+ {
+ "ddd": 0,
+ "ind": 74,
+ "ty": 3,
+ "nm": "",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 1,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0,
+ "parent": 75
+ },
+ {
+ "ddd": 0,
+ "ind": 76,
+ "ty": 3,
+ "nm": "",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": -45,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0,
+ "parent": 74
+ },
+ {
+ "ddd": 0,
+ "ind": 77,
+ "ty": 4,
+ "nm": "Ellipse Path 1-4",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "hd": false,
+ "parent": 76,
+ "shapes": [
+ {
+ "ty": "gr",
+ "hd": false,
+ "it": [
+ {
+ "ty": "el",
+ "hd": false,
+ "d": 1,
+ "s": {
+ "a": 0,
+ "k": [
+ 149.348,
+ 149.348
+ ],
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ }
+ },
+ {
+ "ty": "st",
+ "c": {
+ "a": 0,
+ "k": [
+ 0.7922,
+ 0.8118,
+ 0.8235
+ ],
+ "ix": 2
+ },
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 2
+ },
+ "w": {
+ "a": 0,
+ "k": 6,
+ "ix": 2
+ },
+ "hd": false
+ },
+ {
+ "ty": "tm",
+ "s": {
+ "a": 0,
+ "k": 3,
+ "ix": 2
+ },
+ "e": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "o": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "m": 1,
+ "hd": false
+ },
+ {
+ "ty": "tr",
+ "hd": false,
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 2
+ },
+ "sk": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "sa": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ }
+ }
+ ]
+ }
+ ],
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0
+ }
+ ]
+ },
+ {
+ "id": "8",
+ "layers": [
+ {
+ "ddd": 0,
+ "ind": 82,
+ "ty": 3,
+ "nm": "",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 1,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 10168.326,
+ 10187.326
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0
+ },
+ {
+ "ddd": 0,
+ "ind": 84,
+ "ty": 3,
+ "nm": "",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 1,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 45,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0,
+ "parent": 82
+ },
+ {
+ "ddd": 0,
+ "ind": 83,
+ "ty": 3,
+ "nm": "",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 1,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0,
+ "parent": 84
+ },
+ {
+ "ddd": 0,
+ "ind": 85,
+ "ty": 3,
+ "nm": "",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": -45,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0,
+ "parent": 83
+ },
+ {
+ "ddd": 0,
+ "ind": 86,
+ "ty": 3,
+ "nm": "",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 1,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0,
+ "parent": 85
+ },
+ {
+ "ddd": 0,
+ "ind": 88,
+ "ty": 3,
+ "nm": "",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 1,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 45,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0,
+ "parent": 86
+ },
+ {
+ "ddd": 0,
+ "ind": 87,
+ "ty": 3,
+ "nm": "",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 1,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0,
+ "parent": 88
+ },
+ {
+ "ddd": 0,
+ "ind": 89,
+ "ty": 3,
+ "nm": "",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": -45,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0,
+ "parent": 87
+ },
+ {
+ "ddd": 0,
+ "ind": 90,
+ "ty": 3,
+ "nm": "",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 1,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 11.674,
+ -7.326
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0,
+ "parent": 89
+ },
+ {
+ "ddd": 0,
+ "ind": 92,
+ "ty": 3,
+ "nm": "",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 1,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 45,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0,
+ "parent": 90
+ },
+ {
+ "ddd": 0,
+ "ind": 91,
+ "ty": 3,
+ "nm": "",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 1,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0,
+ "parent": 92
+ },
+ {
+ "ddd": 0,
+ "ind": 93,
+ "ty": 3,
+ "nm": "",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": -45,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0,
+ "parent": 91
+ },
+ {
+ "ddd": 0,
+ "ind": 94,
+ "ty": 4,
+ "nm": "Ellipse Path 1-5",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "hd": false,
+ "parent": 93,
+ "shapes": [
+ {
+ "ty": "gr",
+ "hd": false,
+ "it": [
+ {
+ "ty": "el",
+ "hd": false,
+ "d": 1,
+ "s": {
+ "a": 0,
+ "k": [
+ 149.348,
+ 149.348
+ ],
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ }
+ },
+ {
+ "ty": "st",
+ "c": {
+ "a": 0,
+ "k": [
+ 0.7922,
+ 0.8118,
+ 0.8235
+ ],
+ "ix": 2
+ },
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 2
+ },
+ "w": {
+ "a": 0,
+ "k": 6,
+ "ix": 2
+ },
+ "hd": false
+ },
+ {
+ "ty": "tm",
+ "s": {
+ "a": 1,
+ "k": [
+ {
+ "t": 0,
+ "s": [
+ 3
+ ],
+ "i": {
+ "x": [
+ 1
+ ],
+ "y": [
+ 1
+ ]
+ },
+ "o": {
+ "x": [
+ 0
+ ],
+ "y": [
+ 0
+ ]
+ }
+ },
+ {
+ "t": 33,
+ "s": [
+ 71.171
+ ],
+ "i": {
+ "x": [
+ 1
+ ],
+ "y": [
+ 1
+ ]
+ },
+ "o": {
+ "x": [
+ 0
+ ],
+ "y": [
+ 0
+ ]
+ }
+ },
+ {
+ "t": 67,
+ "s": [
+ 90.083
+ ],
+ "i": {
+ "x": [
+ 1
+ ],
+ "y": [
+ 1
+ ]
+ },
+ "o": {
+ "x": [
+ 0
+ ],
+ "y": [
+ 0
+ ]
+ }
+ },
+ {
+ "t": 93,
+ "s": [
+ 100
+ ],
+ "i": {
+ "x": [
+ 1
+ ],
+ "y": [
+ 1
+ ]
+ },
+ "o": {
+ "x": [
+ 0
+ ],
+ "y": [
+ 0
+ ]
+ }
+ }
+ ],
+ "ix": 2
+ },
+ "e": {
+ "a": 1,
+ "k": [
+ {
+ "t": 0,
+ "s": [
+ 0
+ ],
+ "i": {
+ "x": [
+ 1
+ ],
+ "y": [
+ 1
+ ]
+ },
+ "o": {
+ "x": [
+ 0
+ ],
+ "y": [
+ 0
+ ]
+ }
+ },
+ {
+ "t": 93,
+ "s": [
+ 100
+ ],
+ "i": {
+ "x": [
+ 1
+ ],
+ "y": [
+ 1
+ ]
+ },
+ "o": {
+ "x": [
+ 0
+ ],
+ "y": [
+ 0
+ ]
+ }
+ }
+ ],
+ "ix": 2
+ },
+ "o": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "m": 1,
+ "hd": false
+ },
+ {
+ "ty": "tr",
+ "hd": false,
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 2
+ },
+ "sk": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "sa": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ }
+ }
+ ]
+ }
+ ],
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0
+ }
+ ]
+ }
+ ],
+ "layers": [
+ {
+ "ddd": 0,
+ "ind": 10,
+ "ty": 3,
+ "nm": "",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 1,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0
+ },
+ {
+ "ddd": 0,
+ "ind": 12,
+ "ty": 3,
+ "nm": "",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 1,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 45,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0,
+ "parent": 10
+ },
+ {
+ "ddd": 0,
+ "ind": 11,
+ "ty": 3,
+ "nm": "",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 1,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0,
+ "parent": 12
+ },
+ {
+ "ddd": 0,
+ "refId": "2",
+ "w": 20000,
+ "h": 20000,
+ "ind": 13,
+ "ty": 0,
+ "nm": "Shape Layer 6",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": -45,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 10000,
+ 10000
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0,
+ "hd": false,
+ "parent": 11
+ },
+ {
+ "ddd": 0,
+ "ind": 27,
+ "ty": 3,
+ "nm": "",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 1,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0
+ },
+ {
+ "ddd": 0,
+ "ind": 29,
+ "ty": 3,
+ "nm": "",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 1,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 45,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0,
+ "parent": 27
+ },
+ {
+ "ddd": 0,
+ "ind": 28,
+ "ty": 3,
+ "nm": "",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 1,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0,
+ "parent": 29
+ },
+ {
+ "ddd": 0,
+ "refId": "4",
+ "w": 20000,
+ "h": 20000,
+ "ind": 30,
+ "ty": 0,
+ "nm": "Shape Layer 5",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": -45,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 10000,
+ 10000
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0,
+ "hd": false,
+ "parent": 28
+ },
+ {
+ "ddd": 0,
+ "ind": 44,
+ "ty": 3,
+ "nm": "",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 1,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0
+ },
+ {
+ "ddd": 0,
+ "ind": 46,
+ "ty": 3,
+ "nm": "",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 1,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 45,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0,
+ "parent": 44
+ },
+ {
+ "ddd": 0,
+ "ind": 45,
+ "ty": 3,
+ "nm": "",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 1,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0,
+ "parent": 46
+ },
+ {
+ "ddd": 0,
+ "refId": "5",
+ "w": 20000,
+ "h": 20000,
+ "ind": 47,
+ "ty": 0,
+ "nm": "Shape Layer 4",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": -45,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 10000,
+ 10000
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0,
+ "hd": false,
+ "parent": 45
+ },
+ {
+ "ddd": 0,
+ "ind": 61,
+ "ty": 3,
+ "nm": "",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 1,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0
+ },
+ {
+ "ddd": 0,
+ "ind": 63,
+ "ty": 3,
+ "nm": "",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 1,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 45,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0,
+ "parent": 61
+ },
+ {
+ "ddd": 0,
+ "ind": 62,
+ "ty": 3,
+ "nm": "",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 1,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0,
+ "parent": 63
+ },
+ {
+ "ddd": 0,
+ "refId": "6",
+ "w": 20000,
+ "h": 20000,
+ "ind": 64,
+ "ty": 0,
+ "nm": "Shape Layer 3",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": -45,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 10000,
+ 10000
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0,
+ "hd": false,
+ "parent": 62
+ },
+ {
+ "ddd": 0,
+ "ind": 78,
+ "ty": 3,
+ "nm": "",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 1,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0
+ },
+ {
+ "ddd": 0,
+ "ind": 80,
+ "ty": 3,
+ "nm": "",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 1,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 45,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0,
+ "parent": 78
+ },
+ {
+ "ddd": 0,
+ "ind": 79,
+ "ty": 3,
+ "nm": "",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 1,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0,
+ "parent": 80
+ },
+ {
+ "ddd": 0,
+ "refId": "7",
+ "w": 20000,
+ "h": 20000,
+ "ind": 81,
+ "ty": 0,
+ "nm": "Shape Layer 2",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": -45,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 10000,
+ 10000
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0,
+ "hd": false,
+ "parent": 79
+ },
+ {
+ "ddd": 0,
+ "ind": 95,
+ "ty": 3,
+ "nm": "",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 1,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0
+ },
+ {
+ "ddd": 0,
+ "ind": 97,
+ "ty": 3,
+ "nm": "",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 1,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 45,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0,
+ "parent": 95
+ },
+ {
+ "ddd": 0,
+ "ind": 96,
+ "ty": 3,
+ "nm": "",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 1,
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0,
+ "parent": 97
+ },
+ {
+ "ddd": 0,
+ "refId": "8",
+ "w": 20000,
+ "h": 20000,
+ "ind": 98,
+ "ty": 0,
+ "nm": "Shape Layer 1",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 1,
+ "k": [
+ {
+ "t": -1,
+ "s": [
+ 0
+ ],
+ "i": {
+ "x": [
+ 1
+ ],
+ "y": [
+ 1
+ ]
+ },
+ "o": {
+ "x": [
+ 0
+ ],
+ "y": [
+ 0
+ ]
+ }
+ },
+ {
+ "t": 0,
+ "s": [
+ 100
+ ],
+ "i": {
+ "x": [
+ 1
+ ],
+ "y": [
+ 1
+ ]
+ },
+ "o": {
+ "x": [
+ 0
+ ],
+ "y": [
+ 0
+ ]
+ }
+ },
+ {
+ "t": 800,
+ "s": [
+ 100
+ ],
+ "i": {
+ "x": [
+ 1
+ ],
+ "y": [
+ 1
+ ]
+ },
+ "o": {
+ "x": [
+ 0
+ ],
+ "y": [
+ 0
+ ]
+ }
+ },
+ {
+ "t": 801,
+ "s": [
+ 0
+ ],
+ "i": {
+ "x": [
+ 1
+ ],
+ "y": [
+ 1
+ ]
+ },
+ "o": {
+ "x": [
+ 0
+ ],
+ "y": [
+ 0
+ ]
+ }
+ }
+ ],
+ "ix": 2
+ },
+ "r": {
+ "a": 0,
+ "k": -45,
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 10000,
+ 10000
+ ],
+ "ix": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 2
+ }
+ },
+ "ao": 0,
+ "ip": 0,
+ "op": 93,
+ "st": 0,
+ "bm": 0,
+ "hd": false,
+ "parent": 96
+ }
+ ],
+ "markers": []
+}
diff --git a/snapshot-tests/src/main/assets/Tests/Dynamic1.json b/snapshot-tests/src/main/assets/Tests/Dynamic1.json
new file mode 100644
index 00000000..de4f6560
--- /dev/null
+++ b/snapshot-tests/src/main/assets/Tests/Dynamic1.json
@@ -0,0 +1 @@
+{"v":"5.10.2","fr":60,"ip":0,"op":60,"w":256,"h":256,"nm":"Comp 2","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Rectangle Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[128,128,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[152.781,152.781],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-4.953,-2.969],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":60,"st":0,"ct":1,"bm":0}],"markers":[]} \ No newline at end of file
diff --git a/snapshot-tests/src/main/assets/Tests/Dynamic2.json b/snapshot-tests/src/main/assets/Tests/Dynamic2.json
new file mode 100644
index 00000000..16b3a1d9
--- /dev/null
+++ b/snapshot-tests/src/main/assets/Tests/Dynamic2.json
@@ -0,0 +1 @@
+{"v":"5.10.2","fr":60,"ip":0,"op":60,"w":256,"h":256,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Polystar Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[128,128,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"sr","sy":1,"d":1,"pt":{"a":0,"k":5,"ix":3},"p":{"a":0,"k":[0,0],"ix":4},"r":{"a":0,"k":128.082,"ix":5},"ir":{"a":0,"k":44.187,"ix":6},"is":{"a":0,"k":0,"ix":8},"or":{"a":0,"k":88.374,"ix":7},"os":{"a":0,"k":0,"ix":9},"ix":1,"nm":"Polystar Path 1","mn":"ADBE Vector Shape - Star","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-4.953,-2.969],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":60,"st":0,"ct":1,"bm":0}],"markers":[]} \ No newline at end of file
diff --git a/snapshot-tests/src/main/assets/Tests/EmbeddedFont.zip b/snapshot-tests/src/main/assets/Tests/EmbeddedFont.zip
new file mode 100644
index 00000000..74ffd4bb
--- /dev/null
+++ b/snapshot-tests/src/main/assets/Tests/EmbeddedFont.zip
Binary files differ
diff --git a/snapshot-tests/src/main/assets/Tests/Framerate.json b/snapshot-tests/src/main/assets/Tests/Framerate.json
new file mode 100644
index 00000000..9c80f607
--- /dev/null
+++ b/snapshot-tests/src/main/assets/Tests/Framerate.json
@@ -0,0 +1 @@
+{"v":"5.9.6","fr":3,"ip":0,"op":3,"w":200,"h":200,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[30,30,0],"to":[21.667,21.667,0],"ti":[-21.667,-21.667,0]},{"t":2,"s":[160,160,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[58.742,58.742],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":3,"st":0,"ct":1,"bm":0}],"markers":[]} \ No newline at end of file
diff --git a/snapshot-tests/src/main/assets/Tests/GradientColorInterpolation.json b/snapshot-tests/src/main/assets/Tests/GradientColorInterpolation.json
new file mode 100644
index 00000000..feef7267
--- /dev/null
+++ b/snapshot-tests/src/main/assets/Tests/GradientColorInterpolation.json
@@ -0,0 +1,441 @@
+{
+ "v": "4.8.0",
+ "meta": {
+ "g": "LottieFiles AE 3.1.1",
+ "a": "",
+ "k": "",
+ "d": "",
+ "tc": ""
+ },
+ "fr": 30,
+ "ip": 0,
+ "op": 151,
+ "w": 430,
+ "h": 932,
+ "nm": "Sticker Unpack",
+ "ddd": 1,
+ "assets": [
+ {
+ "id": "comp_0",
+ "layers": [
+ ]
+ }
+ ],
+ "layers": [
+ {
+ "ddd": 1,
+ "ind": 1,
+ "ty": 0,
+ "nm": "Pc_Sticker Pack",
+ "refId": "comp_0",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 11
+ },
+ "rx": {
+ "a": 1,
+ "k": [
+ {
+ "i": {
+ "x": [
+ 0.421
+ ],
+ "y": [
+ 1
+ ]
+ },
+ "o": {
+ "x": [
+ 0.55
+ ],
+ "y": [
+ 0
+ ]
+ },
+ "t": 1,
+ "s": [
+ 0
+ ]
+ },
+ {
+ "i": {
+ "x": [
+ 0.985
+ ],
+ "y": [
+ 0.168
+ ]
+ },
+ "o": {
+ "x": [
+ 0.857
+ ],
+ "y": [
+ 0
+ ]
+ },
+ "t": 21,
+ "s": [
+ -1.9
+ ]
+ },
+ {
+ "t": 41,
+ "s": [
+ 24.3
+ ]
+ }
+ ],
+ "ix": 8
+ },
+ "ry": {
+ "a": 1,
+ "k": [
+ {
+ "i": {
+ "x": [
+ 0.421
+ ],
+ "y": [
+ 1
+ ]
+ },
+ "o": {
+ "x": [
+ 0.55
+ ],
+ "y": [
+ 0
+ ]
+ },
+ "t": 1,
+ "s": [
+ 0
+ ]
+ },
+ {
+ "i": {
+ "x": [
+ 0.985
+ ],
+ "y": [
+ -0.28
+ ]
+ },
+ "o": {
+ "x": [
+ 0.857
+ ],
+ "y": [
+ 0
+ ]
+ },
+ "t": 21,
+ "s": [
+ 3.7
+ ]
+ },
+ {
+ "t": 41,
+ "s": [
+ 16
+ ]
+ }
+ ],
+ "ix": 9
+ },
+ "rz": {
+ "a": 0,
+ "k": 0,
+ "ix": 10
+ },
+ "or": {
+ "a": 0,
+ "k": [
+ 0,
+ 0,
+ 0
+ ],
+ "ix": 7
+ },
+ "p": {
+ "s": true,
+ "x": {
+ "a": 0,
+ "k": 215,
+ "ix": 3
+ },
+ "y": {
+ "a": 0,
+ "k": 466,
+ "ix": 4
+ },
+ "z": {
+ "a": 0,
+ "k": 0,
+ "ix": 5
+ }
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 221,
+ 307.5,
+ 0
+ ],
+ "ix": 1
+ },
+ "s": {
+ "a": 1,
+ "k": [
+ {
+ "i": {
+ "x": [
+ 0.421,
+ 0.421,
+ 0.667
+ ],
+ "y": [
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "o": {
+ "x": [
+ 0.55,
+ 0.55,
+ 0.333
+ ],
+ "y": [
+ 0,
+ 0,
+ 0
+ ]
+ },
+ "t": 0,
+ "s": [
+ 64.6,
+ 64.6,
+ 100
+ ]
+ },
+ {
+ "i": {
+ "x": [
+ 0.985,
+ 0.985,
+ 0.667
+ ],
+ "y": [
+ 0.192,
+ 0.192,
+ 1
+ ]
+ },
+ "o": {
+ "x": [
+ 0.857,
+ 0.857,
+ 0.333
+ ],
+ "y": [
+ 0,
+ 0,
+ 0
+ ]
+ },
+ "t": 20,
+ "s": [
+ 74.2,
+ 74.2,
+ 100
+ ]
+ },
+ {
+ "t": 40,
+ "s": [
+ 7,
+ 7,
+ 100
+ ]
+ }
+ ],
+ "ix": 6
+ }
+ },
+ "ao": 0,
+ "w": 442,
+ "h": 615,
+ "ip": 0,
+ "op": 41,
+ "st": 0,
+ "bm": 0
+ },
+ {
+ "ddd": 0,
+ "ind": 14,
+ "ty": 4,
+ "nm": "Shape Layer 2",
+ "parent": 1,
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 11
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 10
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 221,
+ 307.5,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0,
+ 0
+ ],
+ "ix": 1
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 538.543,
+ 538.543,
+ 100
+ ],
+ "ix": 6
+ }
+ },
+ "ao": 0,
+ "shapes": [
+ {
+ "ty": "gr",
+ "it": [
+ {
+ "ty": "rc",
+ "d": 1,
+ "s": {
+ "a": 0,
+ "k": [
+ 294,
+ 403
+ ],
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 3
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 4
+ },
+ "nm": "Rectangle Path 1",
+ "mn": "ADBE Vector Shape - Rect",
+ "hd": false
+ },
+ {
+ "ty": "gf",
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 10
+ },
+ "r": 1,
+ "bm": 0,
+ "g": {
+ "p": 3,
+ "k": {
+ "a": 0,
+ "k": [
+ 0,
+ 0.373,
+ 0.024,
+ 0.898,
+ 0.359,
+ 0.186,
+ 0.012,
+ 0.449,
+ 0.998,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0.5,
+ 0.5,
+ 1,
+ 0
+ ],
+ "ix": 9
+ }
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 5
+ },
+ "e": {
+ "a": 0,
+ "k": [
+ 149,
+ 0
+ ],
+ "ix": 6
+ },
+ "t": 2,
+ "h": {
+ "a": 0,
+ "k": 0,
+ "ix": 7
+ },
+ "a": {
+ "a": 0,
+ "k": 0,
+ "ix": 8
+ },
+ "nm": "Gradient Fill 1",
+ "mn": "ADBE Vector Graphic - G-Fill",
+ "hd": false
+ }
+ ],
+ "nm": "Rectangle 1",
+ "np": 3,
+ "cix": 2,
+ "bm": 0,
+ "ix": 1,
+ "mn": "ADBE Vector Group",
+ "hd": false
+ }
+ ],
+ "ip": 0,
+ "op": 42,
+ "st": 0,
+ "bm": 0
+ }
+ ],
+ "markers": []
+}
diff --git a/snapshot-tests/src/main/assets/Tests/GradientColorKeyframeAnimation.zip b/snapshot-tests/src/main/assets/Tests/GradientColorKeyframeAnimation.zip
new file mode 100644
index 00000000..d8188d10
--- /dev/null
+++ b/snapshot-tests/src/main/assets/Tests/GradientColorKeyframeAnimation.zip
Binary files differ
diff --git a/snapshot-tests/src/main/assets/Tests/GradientWithVaryingOpacityStops.json b/snapshot-tests/src/main/assets/Tests/GradientWithVaryingOpacityStops.json
new file mode 100644
index 00000000..8ad790c1
--- /dev/null
+++ b/snapshot-tests/src/main/assets/Tests/GradientWithVaryingOpacityStops.json
@@ -0,0 +1,264 @@
+{
+ "v": "5.12.1",
+ "fr": 60,
+ "ip": 0,
+ "op": 240,
+ "w": 430,
+ "h": 795,
+ "nm": "gradient",
+ "ddd": 0,
+ "assets": [
+ {
+ "id": "comp_0",
+ "nm": "gradient",
+ "fr": 60,
+ "layers": [
+ {
+ "ddd": 0,
+ "ind": 1,
+ "ty": 4,
+ "nm": "1",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 1,
+ "k": [
+ {
+ "i": { "x": [0.833], "y": [0.833] },
+ "o": { "x": [0.167], "y": [0.167] },
+ "t": 0,
+ "s": [100]
+ },
+ {
+ "i": { "x": [0.833], "y": [0.833] },
+ "o": { "x": [0.167], "y": [0.167] },
+ "t": 30,
+ "s": [75]
+ },
+ { "t": 59, "s": [100] }
+ ],
+ "ix": 11
+ },
+ "r": { "a": 0, "k": 0, "ix": 10 },
+ "p": { "a": 0, "k": [51.5, 58, 0], "ix": 2, "l": 2 },
+ "a": { "a": 0, "k": [-4.5, -130.5, 0], "ix": 1, "l": 2 },
+ "s": { "a": 0, "k": [100, 100, 100], "ix": 6, "l": 2 }
+ },
+ "ao": 0,
+ "shapes": [
+ {
+ "ty": "gr",
+ "it": [
+ {
+ "d": 1,
+ "ty": "el",
+ "s": {
+ "a": 1,
+ "k": [
+ {
+ "i": { "x": [0.833, 0.833], "y": [0.833, 0.833] },
+ "o": { "x": [0.167, 0.167], "y": [0.167, 0.167] },
+ "t": 0,
+ "s": [50, 50]
+ },
+ {
+ "i": { "x": [0.833, 0.833], "y": [0.833, 0.833] },
+ "o": { "x": [0.167, 0.167], "y": [0.167, 0.167] },
+ "t": 28,
+ "s": [46, 46]
+ },
+ {
+ "i": { "x": [0.833, 0.833], "y": [0.833, 0.833] },
+ "o": { "x": [0.167, 0.167], "y": [0.167, 0.167] },
+ "t": 59,
+ "s": [50, 50]
+ },
+ {
+ "i": { "x": [0.833, 0.833], "y": [0.833, 0.833] },
+ "o": { "x": [0.167, 0.167], "y": [0.167, 0.167] },
+ "t": 120,
+ "s": [73.252, 73.252]
+ },
+ { "t": 239, "s": [86.252, 86.252] }
+ ],
+ "ix": 2
+ },
+ "p": { "a": 0, "k": [0, 0], "ix": 3 },
+ "nm": "1",
+ "mn": "ADBE Vector Shape - Ellipse",
+ "hd": false
+ },
+ {
+ "ty": "gf",
+ "o": {
+ "a": 1,
+ "k": [
+ {
+ "i": { "x": [0.833], "y": [0.833] },
+ "o": { "x": [0.167], "y": [0.167] },
+ "t": 0,
+ "s": [0]
+ },
+ {
+ "i": { "x": [0.833], "y": [0.833] },
+ "o": { "x": [0.167], "y": [0.167] },
+ "t": 15,
+ "s": [83]
+ },
+ {
+ "i": { "x": [0.833], "y": [0.833] },
+ "o": { "x": [0.167], "y": [0.167] },
+ "t": 44,
+ "s": [100]
+ },
+ { "t": 59, "s": [0] }
+ ],
+ "ix": 10
+ },
+ "r": 1,
+ "bm": 0,
+ "g": {
+ "p": 3,
+ "k": {
+ "a": 1,
+ "k": [
+ {
+ "i": { "x": 0.833, "y": 0.833 },
+ "o": { "x": 0.167, "y": 0.167 },
+ "t": 0,
+ "s": [
+ 0, 1, 1, 1, 0.5, 1, 1, 1, 1, 1, 1, 1, 0, 0.1, 0.5,
+ 0.3, 1, 0.5
+ ]
+ },
+ {
+ "i": { "x": 0.833, "y": 0.833 },
+ "o": { "x": 0.167, "y": 0.167 },
+ "t": 15,
+ "s": [
+ 0, 0.992, 0.995, 1, 0.5, 0.996, 0.998, 1, 1, 1, 1,
+ 1, 0, 0.2, 0.5, 0.45, 1, 0.7
+ ]
+ },
+ {
+ "i": { "x": 0.833, "y": 0.833 },
+ "o": { "x": 0.167, "y": 0.167 },
+ "t": 31,
+ "s": [
+ 0, 1, 1, 1, 0.5, 1, 1, 1, 1, 1, 1, 1, 0, 0.1, 0.5,
+ 0.35, 1, 0.6
+ ]
+ },
+ {
+ "i": { "x": 0.833, "y": 0.833 },
+ "o": { "x": 0.167, "y": 0.167 },
+ "t": 59,
+ "s": [
+ 0, 1, 1, 1, 0.5, 1, 1, 1, 1, 1, 1, 1, 0, 0.2, 0.5,
+ 0.45, 1, 0.7
+ ]
+ },
+ {
+ "i": { "x": 0.833, "y": 0.833 },
+ "o": { "x": 0.167, "y": 0.167 },
+ "t": 160,
+ "s": [
+ 0, 0.302, 0.391, 0.586, 0.529, 0.17, 0.239, 0.393,
+ 1, 0.039, 0.086, 0.2, 0, 1, 0.5, 1, 1, 1
+ ]
+ },
+ {
+ "t": 239,
+ "s": [
+ 0, 0.053, 0.362, 0.77, 0.5, 0.046, 0.224, 0.485, 1,
+ 0.039, 0.086, 0.2, 0, 1, 0.5, 1, 1, 1
+ ]
+ }
+ ],
+ "ix": 9
+ }
+ },
+ "s": { "a": 0, "k": [0, 0], "ix": 5 },
+ "e": { "a": 0, "k": [29.461, 0.027], "ix": 6 },
+ "t": 2,
+ "h": { "a": 0, "k": 0, "ix": 7 },
+ "a": { "a": 0, "k": 0, "ix": 8 },
+ "nm": "1",
+ "mn": "ADBE Vector Graphic - G-Fill",
+ "hd": false
+ },
+ {
+ "ty": "tr",
+ "p": { "a": 0, "k": [-3.372, -136.224], "ix": 2 },
+ "a": { "a": 0, "k": [0, 0], "ix": 1 },
+ "s": { "a": 0, "k": [110.999, 110.999], "ix": 3 },
+ "r": { "a": 0, "k": 0, "ix": 6 },
+ "o": { "a": 0, "k": 100, "ix": 7 },
+ "sk": { "a": 0, "k": 0, "ix": 4 },
+ "sa": { "a": 0, "k": 0, "ix": 5 },
+ "nm": "Transform"
+ }
+ ],
+ "nm": "Ellipse 1",
+ "np": 3,
+ "cix": 2,
+ "bm": 0,
+ "ix": 1,
+ "mn": "ADBE Vector Group",
+ "hd": false
+ }
+ ],
+ "ip": 0,
+ "op": 60,
+ "st": 0,
+ "ct": 1,
+ "bm": 0
+ }
+ ]
+ }
+ ],
+ "layers": [
+ {
+ "ddd": 0,
+ "ind": 36,
+ "ty": 0,
+ "nm": "",
+ "refId": "comp_0",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 1,
+ "k": [
+ {
+ "i": { "x": [0.833], "y": [0.833] },
+ "o": { "x": [0.167], "y": [0.167] },
+ "t": 11,
+ "s": [0]
+ },
+ {
+ "i": { "x": [0.833], "y": [0.833] },
+ "o": { "x": [0.167], "y": [0.167] },
+ "t": 130,
+ "s": [100]
+ },
+ { "t": 213, "s": [0] }
+ ],
+ "ix": 11
+ },
+ "r": { "a": 0, "k": 0, "ix": 10 },
+ "p": { "a": 0, "k": [168, 285.5, 0], "ix": 2, "l": 2 },
+ "a": { "a": 0, "k": [50, 51, 0], "ix": 1, "l": 2 },
+ "s": { "a": 0, "k": [100, 100, 100], "ix": 6, "l": 2 }
+ },
+ "ao": 0,
+ "w": 100,
+ "h": 102,
+ "ip": 0,
+ "op": 60,
+ "st": 0,
+ "bm": 0
+ }
+ ],
+ "markers": [],
+ "props": {}
+}
diff --git a/snapshot-tests/src/main/assets/Tests/InterpolateBetweenOpacityStops.json b/snapshot-tests/src/main/assets/Tests/InterpolateBetweenOpacityStops.json
new file mode 100755
index 00000000..a049e734
--- /dev/null
+++ b/snapshot-tests/src/main/assets/Tests/InterpolateBetweenOpacityStops.json
@@ -0,0 +1,1038 @@
+{
+ "v": "5.7.4",
+ "fr": 60,
+ "ip": 0,
+ "op": 540,
+ "w": 750,
+ "h": 672,
+ "nm": "app_morning_background",
+ "ddd": 0,
+ "assets": [],
+ "layers": [
+ {
+ "ddd": 0,
+ "ind": 1,
+ "ty": 4,
+ "nm": "City state 2",
+ "td": 1,
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 11
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 10
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 375,
+ 336,
+ 0
+ ],
+ "ix": 2,
+ "l": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0,
+ 0
+ ],
+ "ix": 1,
+ "l": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100,
+ 100
+ ],
+ "ix": 6,
+ "l": 2
+ }
+ },
+ "ao": 0,
+ "shapes": [
+ {
+ "ty": "gr",
+ "it": [
+ {
+ "ty": "rc",
+ "d": 1,
+ "s": {
+ "a": 0,
+ "k": [
+ 375,
+ 336
+ ],
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 3
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 4
+ },
+ "nm": "Rectangle Path 1",
+ "mn": "ADBE Vector Shape - Rect",
+ "hd": false
+ },
+ {
+ "ty": "tr",
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 1
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 200,
+ 200
+ ],
+ "ix": 3
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 6
+ },
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 7
+ },
+ "sk": {
+ "a": 0,
+ "k": 0,
+ "ix": 4
+ },
+ "sa": {
+ "a": 0,
+ "k": 0,
+ "ix": 5
+ },
+ "nm": "Transform"
+ }
+ ],
+ "nm": "City state",
+ "np": 3,
+ "cix": 2,
+ "bm": 0,
+ "ix": 1,
+ "mn": "ADBE Vector Group",
+ "hd": false
+ }
+ ],
+ "ip": 0,
+ "op": 540,
+ "st": 0,
+ "bm": 0
+ },
+ {
+ "ddd": 0,
+ "ind": 2,
+ "ty": 4,
+ "nm": "Ellipse 3",
+ "tt": 2,
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 11
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 10
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 375,
+ 699,
+ 0
+ ],
+ "ix": 2,
+ "l": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0,
+ 0
+ ],
+ "ix": 1,
+ "l": 2
+ },
+ "s": {
+ "a": 1,
+ "k": [
+ {
+ "i": {
+ "x": [
+ 0.52,
+ 0.52,
+ 0.52
+ ],
+ "y": [
+ 0.979,
+ 0.984,
+ 1
+ ]
+ },
+ "o": {
+ "x": [
+ 0.48,
+ 0.48,
+ 0.48
+ ],
+ "y": [
+ 0.021,
+ 0.016,
+ 0
+ ]
+ },
+ "t": 0,
+ "s": [
+ 100,
+ 66,
+ 100
+ ],
+ "e": [
+ 173,
+ 147,
+ 100
+ ]
+ },
+ {
+ "i": {
+ "x": [
+ 0.52,
+ 0.52,
+ 0.52
+ ],
+ "y": [
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "o": {
+ "x": [
+ 0.48,
+ 0.48,
+ 0.48
+ ],
+ "y": [
+ 0,
+ 0,
+ 0
+ ]
+ },
+ "t": 289,
+ "s": [
+ 173,
+ 147,
+ 100
+ ],
+ "e": [
+ 199,
+ 169.092,
+ 100
+ ]
+ },
+ {
+ "i": {
+ "x": [
+ 0.52,
+ 0.52,
+ 0.52
+ ],
+ "y": [
+ 1,
+ 1,
+ 1
+ ]
+ },
+ "o": {
+ "x": [
+ 0.48,
+ 0.48,
+ 0.48
+ ],
+ "y": [
+ 0,
+ 0,
+ 0
+ ]
+ },
+ "t": 300,
+ "s": [
+ 199,
+ 169.092,
+ 100
+ ],
+ "e": [
+ 100,
+ 66,
+ 100
+ ]
+ },
+ {
+ "t": 540
+ }
+ ],
+ "ix": 6,
+ "l": 2
+ }
+ },
+ "ao": 0,
+ "shapes": [
+ {
+ "ty": "gr",
+ "it": [
+ {
+ "d": 1,
+ "ty": "el",
+ "s": {
+ "a": 0,
+ "k": [
+ 363,
+ 363
+ ],
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 3
+ },
+ "nm": "Ellipse Path 1",
+ "mn": "ADBE Vector Shape - Ellipse",
+ "hd": false
+ },
+ {
+ "ty": "gf",
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 10
+ },
+ "r": 1,
+ "bm": 0,
+ "g": {
+ "p": 5,
+ "k": {
+ "a": 1,
+ "k": [
+ {
+ "i": {
+ "x": 0.833,
+ "y": 0.833
+ },
+ "o": {
+ "x": 0.167,
+ "y": 0.167
+ },
+ "t": 0,
+ "s": [
+ 0,
+ 0.956,
+ 0.474,
+ 0.442,
+ 0.265,
+ 0.89,
+ 0.468,
+ 0.367,
+ 0.529,
+ 0.824,
+ 0.461,
+ 0.292,
+ 0.765,
+ 0.878,
+ 0.614,
+ 0.397,
+ 1,
+ 0.931,
+ 0.766,
+ 0.502,
+ 0,
+ 1,
+ 0.256,
+ 0.665,
+ 0.513,
+ 0.33,
+ 0.756,
+ 0.165,
+ 1,
+ 0
+ ],
+ "e": [
+ 0.001,
+ 1,
+ 0.798,
+ 0.325,
+ 0.293,
+ 0.988,
+ 0.775,
+ 0.278,
+ 0.584,
+ 0.975,
+ 0.752,
+ 0.23,
+ 0.79,
+ 0.988,
+ 0.844,
+ 0.437,
+ 0.996,
+ 1,
+ 0.936,
+ 0.643,
+ 0,
+ 1,
+ 0.302,
+ 0.635,
+ 0.604,
+ 0.27,
+ 0.802,
+ 0.135,
+ 1,
+ 0
+ ]
+ },
+ {
+ "i": {
+ "x": 0.833,
+ "y": 0.833
+ },
+ "o": {
+ "x": 0.167,
+ "y": 0.167
+ },
+ "t": 247,
+ "s": [
+ 0.001,
+ 1,
+ 0.798,
+ 0.325,
+ 0.293,
+ 0.988,
+ 0.775,
+ 0.278,
+ 0.584,
+ 0.975,
+ 0.752,
+ 0.23,
+ 0.79,
+ 0.988,
+ 0.844,
+ 0.437,
+ 0.996,
+ 1,
+ 0.936,
+ 0.643,
+ 0,
+ 1,
+ 0.302,
+ 0.635,
+ 0.604,
+ 0.27,
+ 0.802,
+ 0.135,
+ 1,
+ 0
+ ],
+ "e": [
+ 0,
+ 0.902,
+ 0.669,
+ 0.18,
+ 0.265,
+ 0.94,
+ 0.643,
+ 0.283,
+ 0.529,
+ 0.979,
+ 0.616,
+ 0.387,
+ 0.765,
+ 0.949,
+ 0.577,
+ 0.331,
+ 1,
+ 0.919,
+ 0.537,
+ 0.275,
+ 0,
+ 1,
+ 0.264,
+ 0.665,
+ 0.527,
+ 0.33,
+ 0.764,
+ 0.165,
+ 1,
+ 0
+ ]
+ },
+ {
+ "i": {
+ "x": 0.833,
+ "y": 0.833
+ },
+ "o": {
+ "x": 0.167,
+ "y": 0.167
+ },
+ "t": 301,
+ "s": [
+ 0,
+ 0.902,
+ 0.669,
+ 0.18,
+ 0.265,
+ 0.94,
+ 0.643,
+ 0.283,
+ 0.529,
+ 0.979,
+ 0.616,
+ 0.387,
+ 0.765,
+ 0.949,
+ 0.577,
+ 0.331,
+ 1,
+ 0.919,
+ 0.537,
+ 0.275,
+ 0,
+ 1,
+ 0.264,
+ 0.665,
+ 0.527,
+ 0.33,
+ 0.764,
+ 0.165,
+ 1,
+ 0
+ ],
+ "e": [
+ 0,
+ 0.956,
+ 0.474,
+ 0.442,
+ 0.265,
+ 0.89,
+ 0.468,
+ 0.367,
+ 0.529,
+ 0.824,
+ 0.461,
+ 0.292,
+ 0.765,
+ 0.878,
+ 0.614,
+ 0.397,
+ 1,
+ 0.931,
+ 0.766,
+ 0.502,
+ 0,
+ 1,
+ 0.256,
+ 0.665,
+ 0.513,
+ 0.33,
+ 0.756,
+ 0.165,
+ 1,
+ 0
+ ]
+ },
+ {
+ "t": 540
+ }
+ ],
+ "ix": 9
+ }
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 5
+ },
+ "e": {
+ "a": 0,
+ "k": [
+ 181.5,
+ 0
+ ],
+ "ix": 6
+ },
+ "t": 2,
+ "h": {
+ "a": 0,
+ "k": 0,
+ "ix": 7
+ },
+ "a": {
+ "a": 0,
+ "k": 0,
+ "ix": 8
+ },
+ "nm": "Gradient Fill 1",
+ "mn": "ADBE Vector Graphic - G-Fill",
+ "hd": false
+ },
+ {
+ "ty": "tr",
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 1
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 200,
+ 200
+ ],
+ "ix": 3
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 6
+ },
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 7
+ },
+ "sk": {
+ "a": 0,
+ "k": 0,
+ "ix": 4
+ },
+ "sa": {
+ "a": 0,
+ "k": 0,
+ "ix": 5
+ },
+ "nm": "Transform"
+ }
+ ],
+ "nm": "Ellipse 3",
+ "np": 2,
+ "cix": 2,
+ "bm": 0,
+ "ix": 1,
+ "mn": "ADBE Vector Group",
+ "hd": false
+ }
+ ],
+ "ip": 0,
+ "op": 540,
+ "st": 0,
+ "bm": 0
+ },
+ {
+ "ddd": 0,
+ "ind": 3,
+ "ty": 4,
+ "nm": "Shape Layer 1",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 11
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 10
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 375,
+ 336,
+ 0
+ ],
+ "ix": 2,
+ "l": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0,
+ 0
+ ],
+ "ix": 1,
+ "l": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100,
+ 100
+ ],
+ "ix": 6,
+ "l": 2
+ }
+ },
+ "ao": 0,
+ "shapes": [
+ {
+ "ty": "gr",
+ "it": [
+ {
+ "ty": "rc",
+ "d": 1,
+ "s": {
+ "a": 0,
+ "k": [
+ 750,
+ 672
+ ],
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 3
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 4
+ },
+ "nm": "Rectangle Path 1",
+ "mn": "ADBE Vector Shape - Rect",
+ "hd": false
+ },
+ {
+ "ty": "gf",
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 10
+ },
+ "r": 1,
+ "bm": 0,
+ "g": {
+ "p": 5,
+ "k": {
+ "a": 1,
+ "k": [
+ {
+ "i": {
+ "x": 0.833,
+ "y": 0.833
+ },
+ "o": {
+ "x": 0.167,
+ "y": 0.167
+ },
+ "t": 0,
+ "s": [
+ 0,
+ 0.971,
+ 0.408,
+ 0,
+ 0.377,
+ 0.956,
+ 0.515,
+ 0,
+ 0.755,
+ 0.942,
+ 0.621,
+ 0,
+ 0.877,
+ 0.971,
+ 0.671,
+ 0,
+ 1,
+ 1,
+ 0.72,
+ 0
+ ],
+ "e": [
+ 0,
+ 0.966,
+ 0.445,
+ 0,
+ 0.362,
+ 0.951,
+ 0.51,
+ 0,
+ 0.725,
+ 0.936,
+ 0.576,
+ 0,
+ 0.862,
+ 0.967,
+ 0.628,
+ 0,
+ 1,
+ 0.998,
+ 0.68,
+ 0
+ ]
+ },
+ {
+ "i": {
+ "x": 0.833,
+ "y": 0.833
+ },
+ "o": {
+ "x": 0.167,
+ "y": 0.167
+ },
+ "t": 49,
+ "s": [
+ 0,
+ 0.966,
+ 0.445,
+ 0,
+ 0.362,
+ 0.951,
+ 0.51,
+ 0,
+ 0.725,
+ 0.936,
+ 0.576,
+ 0,
+ 0.862,
+ 0.967,
+ 0.628,
+ 0,
+ 1,
+ 0.998,
+ 0.68,
+ 0
+ ],
+ "e": [
+ 0,
+ 0.946,
+ 0.606,
+ 0,
+ 0.275,
+ 0.928,
+ 0.492,
+ 0,
+ 0.55,
+ 0.91,
+ 0.378,
+ 0,
+ 0.775,
+ 0.948,
+ 0.442,
+ 0,
+ 1,
+ 0.987,
+ 0.507,
+ 0
+ ]
+ },
+ {
+ "i": {
+ "x": 0.833,
+ "y": 0.833
+ },
+ "o": {
+ "x": 0.167,
+ "y": 0.167
+ },
+ "t": 260.375,
+ "s": [
+ 0,
+ 0.946,
+ 0.606,
+ 0,
+ 0.275,
+ 0.928,
+ 0.492,
+ 0,
+ 0.55,
+ 0.91,
+ 0.378,
+ 0,
+ 0.775,
+ 0.948,
+ 0.442,
+ 0,
+ 1,
+ 0.987,
+ 0.507,
+ 0
+ ],
+ "e": [
+ 0,
+ 0.971,
+ 0.408,
+ 0,
+ 0.383,
+ 0.956,
+ 0.515,
+ 0,
+ 0.766,
+ 0.942,
+ 0.621,
+ 0,
+ 0.883,
+ 0.971,
+ 0.671,
+ 0,
+ 1,
+ 1,
+ 0.72,
+ 0
+ ]
+ },
+ {
+ "t": 540
+ }
+ ],
+ "ix": 9
+ }
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ -2.047,
+ 331.848
+ ],
+ "ix": 5
+ },
+ "e": {
+ "a": 0,
+ "k": [
+ 3.137,
+ -336.078
+ ],
+ "ix": 6
+ },
+ "t": 1,
+ "nm": "Gradient Fill 1",
+ "mn": "ADBE Vector Graphic - G-Fill",
+ "hd": false
+ },
+ {
+ "ty": "tr",
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 1
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 3
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 6
+ },
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 7
+ },
+ "sk": {
+ "a": 0,
+ "k": 0,
+ "ix": 4
+ },
+ "sa": {
+ "a": 0,
+ "k": 0,
+ "ix": 5
+ },
+ "nm": "Transform"
+ }
+ ],
+ "nm": "Rectangle 1",
+ "np": 2,
+ "cix": 2,
+ "bm": 0,
+ "ix": 1,
+ "mn": "ADBE Vector Group",
+ "hd": false
+ }
+ ],
+ "ip": 0,
+ "op": 540,
+ "st": 0,
+ "bm": 0
+ }
+ ],
+ "markers": []
+} \ No newline at end of file
diff --git a/snapshot-tests/src/main/assets/Tests/LargeSquare.json b/snapshot-tests/src/main/assets/Tests/LargeSquare.json
new file mode 100644
index 00000000..0c38da80
--- /dev/null
+++ b/snapshot-tests/src/main/assets/Tests/LargeSquare.json
@@ -0,0 +1,222 @@
+{
+ "v": "5.9.1",
+ "fr": 25,
+ "ip": 0,
+ "op": 75,
+ "w": 1200,
+ "h": 1200,
+ "nm": "square",
+ "ddd": 0,
+ "assets": [],
+ "layers": [
+ {
+ "ddd": 0,
+ "ind": 15,
+ "ty": 4,
+ "nm": "Fond Silhouettes",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 11
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 10
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 600,
+ 600,
+ 0
+ ],
+ "ix": 2,
+ "l": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 600,
+ 600,
+ 0
+ ],
+ "ix": 1,
+ "l": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100,
+ 100
+ ],
+ "ix": 6,
+ "l": 2
+ }
+ },
+ "ao": 0,
+ "shapes": [
+ {
+ "ty": "gr",
+ "it": [
+ {
+ "ind": 0,
+ "ty": "sh",
+ "ix": 1,
+ "ks": {
+ "a": 0,
+ "k": {
+ "i": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ "o": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ "v": [
+ [
+ -600,
+ 600
+ ],
+ [
+ 600,
+ 600
+ ],
+ [
+ 600,
+ -600
+ ],
+ [
+ -600,
+ -600
+ ]
+ ],
+ "c": true
+ },
+ "ix": 2
+ },
+ "nm": "Tracé 1",
+ "mn": "ADBE Vector Shape - Group",
+ "hd": false
+ },
+ {
+ "ty": "fl",
+ "c": {
+ "a": 0,
+ "k": [
+ 0.561497886508,
+ 0.699996708889,
+ 0.56712066052,
+ 1
+ ],
+ "ix": 4
+ },
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 5
+ },
+ "r": 1,
+ "bm": 0,
+ "nm": "Fond 1",
+ "mn": "ADBE Vector Graphic - Fill",
+ "hd": false
+ },
+ {
+ "ty": "tr",
+ "p": {
+ "a": 0,
+ "k": [
+ 600,
+ 600
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 1
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 3
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 6
+ },
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 7
+ },
+ "sk": {
+ "a": 0,
+ "k": 0,
+ "ix": 4
+ },
+ "sa": {
+ "a": 0,
+ "k": 0,
+ "ix": 5
+ },
+ "nm": "Transformer "
+ }
+ ],
+ "nm": "Groupe 1",
+ "np": 2,
+ "cix": 2,
+ "bm": 0,
+ "ix": 1,
+ "mn": "ADBE Vector Group",
+ "hd": false
+ }
+ ],
+ "ip": 0,
+ "op": 76,
+ "st": 0,
+ "bm": 0
+ }
+ ],
+ "markers": []
+}
diff --git a/snapshot-tests/src/main/assets/Tests/LayerBlend_0.json b/snapshot-tests/src/main/assets/Tests/LayerBlend_0.json
new file mode 100644
index 00000000..6b0b95f3
--- /dev/null
+++ b/snapshot-tests/src/main/assets/Tests/LayerBlend_0.json
@@ -0,0 +1 @@
+{"v":"5.7.1","ip":0,"op":180,"nm":"Animation","mn":"{429ff333-f31c-4124-91c5-5e861412a004}","fr":60,"w":512,"h":512,"assets":[],"layers":[{"ddd":0,"ty":4,"ind":1,"st":0,"ip":0,"op":180,"nm":"Layer","mn":"{625eab7e-4758-4d4b-b37c-d89115b1442b}","ks":{"a":{"a":0,"k":[256,256]},"p":{"a":0,"k":[256,256]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":77}},"shapes":[{"ty":"gr","nm":"Ellipse","mn":"{dd57d763-ff3b-420f-a94d-eb5503e7faa7}","it":[{"ty":"el","nm":"Ellipse","mn":"{fa5c495c-00d1-4253-b30c-cc8cb1b855b2}","p":{"a":0,"k":[400.1910447761194,240.71641791044777]},"s":{"a":0,"k":[195.15223880597017,180.53731343283584]}},{"ty":"st","hd":true,"nm":"Stroke","mn":"{89437b5f-dca9-42d4-aff9-c57ce08c8c1e}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"lc":2,"lj":2,"ml":0,"w":{"a":0,"k":30}},{"ty":"fl","nm":"Fill","mn":"{352559ca-ebe9-4b11-acdd-09e155612598}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.3411764705882353,0.01568627450980392]},"r":1},{"ty":"tr","a":{"a":0,"k":[400.1910447761194,240.71641791044777]},"p":{"a":0,"k":[400.1910447761194,240.71641791044777]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","nm":"PolyStar","mn":"{b6000853-a4d3-4b13-acdd-2e4f1a192760}","it":[{"ty":"sr","nm":"PolyStar","mn":"{d647a149-8105-4e08-b395-c8de40669fb0}","p":{"a":0,"k":[110.90149253731343,216.644776119403]},"or":{"a":0,"k":121.5619125366211},"ir":{"a":0,"k":60.78095626831055},"r":{"a":0,"k":143.04905700683594},"pt":{"a":0,"k":5},"sy":1,"os":{"a":0,"k":0},"is":{"a":0,"k":0}},{"ty":"st","hd":true,"nm":"Stroke","mn":"{67a87e2b-afff-4f55-9004-4cc274cefe07}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"lc":2,"lj":2,"ml":0,"w":{"a":0,"k":30}},{"ty":"fl","nm":"Fill","mn":"{39b8d13c-45cf-4ad7-972a-ef5169f1ffbf}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"r":1},{"ty":"tr","a":{"a":0,"k":[110.90149253731343,216.644776119403]},"p":{"a":0,"k":[159.9044776119403,247.59402985074627]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}}]}],"bm":0},{"ddd":0,"ty":4,"ind":0,"st":0,"ip":0,"op":180,"nm":"Layer 1","mn":"{d74c9dcc-e7af-45c3-9eab-554c7b93f6b6}","ks":{"a":{"a":0,"k":[256,256]},"p":{"a":0,"k":[256,256]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}},"shapes":[{"ty":"gr","nm":"Rectangle 1","mn":"{b22ddad1-738b-471a-86eb-1f072fa45799}","it":[{"ty":"rc","nm":"Rectangle 1","mn":"{0c09bd21-59ab-4f1e-bd3d-547613eb3e2a}","p":{"a":0,"k":[241.57611940298506,357.6358208955224]},"s":{"a":0,"k":[383.4268656716418,211.4865671641791]},"r":{"a":0,"k":0}},{"ty":"st","hd":true,"nm":"Stroke","mn":"{8cd4fca9-3480-49fa-947f-04bc40ed74f5}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"lc":2,"lj":2,"ml":0,"w":{"a":0,"k":30}},{"ty":"fl","nm":"Fill","mn":"{050089d8-44c4-4312-8e23-3c89df7615aa}","o":{"a":0,"k":100},"c":{"a":0,"k":[0.7686274509803922,0.8509803921568627,0.9607843137254902]},"r":1},{"ty":"tr","a":{"a":0,"k":[241.57611940298506,357.6358208955224]},"p":{"a":0,"k":[226.1014925373134,131.53432835820894]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","nm":"Rectangle","mn":"{700bfca8-0e45-42e9-8559-15ac0ebe93b2}","it":[{"ty":"rc","nm":"Rectangle","mn":"{0e3ac2ac-22c8-4310-8208-f5d5ba9cd6d9}","p":{"a":0,"k":[277.68358208955226,148.2985074626866]},"s":{"a":0,"k":[335.2835820895522,162.48358208955224]},"r":{"a":0,"k":0}},{"ty":"st","hd":true,"nm":"Stroke","mn":"{af0c691f-7815-414e-a988-ac2eb6e32128}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"lc":2,"lj":2,"ml":0,"w":{"a":0,"k":30}},{"ty":"fl","nm":"Fill","mn":"{059d8c4e-de02-4fa7-99fe-c069b73218be}","o":{"a":0,"k":100},"c":{"a":0,"k":[0.19607843137254902,0.3137254901960784,0.6901960784313725]},"r":1},{"ty":"tr","a":{"a":0,"k":[277.68358208955226,148.2985074626866]},"p":{"a":0,"k":[277.68358208955226,366.6626865671642]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}}]}]}],"meta":{"g":"Glaxnimate 0.4.6-32-gb62899be"}} \ No newline at end of file
diff --git a/snapshot-tests/src/main/assets/Tests/LayerBlend_1.json b/snapshot-tests/src/main/assets/Tests/LayerBlend_1.json
new file mode 100644
index 00000000..1b99621c
--- /dev/null
+++ b/snapshot-tests/src/main/assets/Tests/LayerBlend_1.json
@@ -0,0 +1 @@
+{"v":"5.7.1","ip":0,"op":180,"nm":"Animation","mn":"{429ff333-f31c-4124-91c5-5e861412a004}","fr":60,"w":512,"h":512,"assets":[],"layers":[{"ddd":0,"ty":4,"ind":1,"st":0,"ip":0,"op":180,"nm":"Layer","mn":"{625eab7e-4758-4d4b-b37c-d89115b1442b}","ks":{"a":{"a":0,"k":[256,256]},"p":{"a":0,"k":[256,256]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":77}},"shapes":[{"ty":"gr","nm":"Ellipse","mn":"{dd57d763-ff3b-420f-a94d-eb5503e7faa7}","it":[{"ty":"el","nm":"Ellipse","mn":"{fa5c495c-00d1-4253-b30c-cc8cb1b855b2}","p":{"a":0,"k":[400.1910447761194,240.71641791044777]},"s":{"a":0,"k":[195.15223880597017,180.53731343283584]}},{"ty":"st","hd":true,"nm":"Stroke","mn":"{89437b5f-dca9-42d4-aff9-c57ce08c8c1e}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"lc":2,"lj":2,"ml":0,"w":{"a":0,"k":30}},{"ty":"fl","nm":"Fill","mn":"{352559ca-ebe9-4b11-acdd-09e155612598}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.3411764705882353,0.01568627450980392]},"r":1},{"ty":"tr","a":{"a":0,"k":[400.1910447761194,240.71641791044777]},"p":{"a":0,"k":[400.1910447761194,240.71641791044777]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","nm":"PolyStar","mn":"{b6000853-a4d3-4b13-acdd-2e4f1a192760}","it":[{"ty":"sr","nm":"PolyStar","mn":"{d647a149-8105-4e08-b395-c8de40669fb0}","p":{"a":0,"k":[110.90149253731343,216.644776119403]},"or":{"a":0,"k":121.5619125366211},"ir":{"a":0,"k":60.78095626831055},"r":{"a":0,"k":143.04905700683594},"pt":{"a":0,"k":5},"sy":1,"os":{"a":0,"k":0},"is":{"a":0,"k":0}},{"ty":"st","hd":true,"nm":"Stroke","mn":"{67a87e2b-afff-4f55-9004-4cc274cefe07}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"lc":2,"lj":2,"ml":0,"w":{"a":0,"k":30}},{"ty":"fl","nm":"Fill","mn":"{39b8d13c-45cf-4ad7-972a-ef5169f1ffbf}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"r":1},{"ty":"tr","a":{"a":0,"k":[110.90149253731343,216.644776119403]},"p":{"a":0,"k":[159.9044776119403,247.59402985074627]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}}]}],"bm":1},{"ddd":0,"ty":4,"ind":0,"st":0,"ip":0,"op":180,"nm":"Layer 1","mn":"{d74c9dcc-e7af-45c3-9eab-554c7b93f6b6}","ks":{"a":{"a":0,"k":[256,256]},"p":{"a":0,"k":[256,256]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}},"shapes":[{"ty":"gr","nm":"Rectangle 1","mn":"{b22ddad1-738b-471a-86eb-1f072fa45799}","it":[{"ty":"rc","nm":"Rectangle 1","mn":"{0c09bd21-59ab-4f1e-bd3d-547613eb3e2a}","p":{"a":0,"k":[241.57611940298506,357.6358208955224]},"s":{"a":0,"k":[383.4268656716418,211.4865671641791]},"r":{"a":0,"k":0}},{"ty":"st","hd":true,"nm":"Stroke","mn":"{8cd4fca9-3480-49fa-947f-04bc40ed74f5}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"lc":2,"lj":2,"ml":0,"w":{"a":0,"k":30}},{"ty":"fl","nm":"Fill","mn":"{050089d8-44c4-4312-8e23-3c89df7615aa}","o":{"a":0,"k":100},"c":{"a":0,"k":[0.7686274509803922,0.8509803921568627,0.9607843137254902]},"r":1},{"ty":"tr","a":{"a":0,"k":[241.57611940298506,357.6358208955224]},"p":{"a":0,"k":[226.1014925373134,131.53432835820894]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","nm":"Rectangle","mn":"{700bfca8-0e45-42e9-8559-15ac0ebe93b2}","it":[{"ty":"rc","nm":"Rectangle","mn":"{0e3ac2ac-22c8-4310-8208-f5d5ba9cd6d9}","p":{"a":0,"k":[277.68358208955226,148.2985074626866]},"s":{"a":0,"k":[335.2835820895522,162.48358208955224]},"r":{"a":0,"k":0}},{"ty":"st","hd":true,"nm":"Stroke","mn":"{af0c691f-7815-414e-a988-ac2eb6e32128}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"lc":2,"lj":2,"ml":0,"w":{"a":0,"k":30}},{"ty":"fl","nm":"Fill","mn":"{059d8c4e-de02-4fa7-99fe-c069b73218be}","o":{"a":0,"k":100},"c":{"a":0,"k":[0.19607843137254902,0.3137254901960784,0.6901960784313725]},"r":1},{"ty":"tr","a":{"a":0,"k":[277.68358208955226,148.2985074626866]},"p":{"a":0,"k":[277.68358208955226,366.6626865671642]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}}]}]}],"meta":{"g":"Glaxnimate 0.4.6-32-gb62899be"}} \ No newline at end of file
diff --git a/snapshot-tests/src/main/assets/Tests/LayerBlend_10.json b/snapshot-tests/src/main/assets/Tests/LayerBlend_10.json
new file mode 100644
index 00000000..f49d271a
--- /dev/null
+++ b/snapshot-tests/src/main/assets/Tests/LayerBlend_10.json
@@ -0,0 +1 @@
+{"v":"5.7.1","ip":0,"op":180,"nm":"Animation","mn":"{429ff333-f31c-4124-91c5-5e861412a004}","fr":60,"w":512,"h":512,"assets":[],"layers":[{"ddd":0,"ty":4,"ind":1,"st":0,"ip":0,"op":180,"nm":"Layer","mn":"{625eab7e-4758-4d4b-b37c-d89115b1442b}","ks":{"a":{"a":0,"k":[256,256]},"p":{"a":0,"k":[256,256]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":77}},"shapes":[{"ty":"gr","nm":"Ellipse","mn":"{dd57d763-ff3b-420f-a94d-eb5503e7faa7}","it":[{"ty":"el","nm":"Ellipse","mn":"{fa5c495c-00d1-4253-b30c-cc8cb1b855b2}","p":{"a":0,"k":[400.1910447761194,240.71641791044777]},"s":{"a":0,"k":[195.15223880597017,180.53731343283584]}},{"ty":"st","hd":true,"nm":"Stroke","mn":"{89437b5f-dca9-42d4-aff9-c57ce08c8c1e}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"lc":2,"lj":2,"ml":0,"w":{"a":0,"k":30}},{"ty":"fl","nm":"Fill","mn":"{352559ca-ebe9-4b11-acdd-09e155612598}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.3411764705882353,0.01568627450980392]},"r":1},{"ty":"tr","a":{"a":0,"k":[400.1910447761194,240.71641791044777]},"p":{"a":0,"k":[400.1910447761194,240.71641791044777]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","nm":"PolyStar","mn":"{b6000853-a4d3-4b13-acdd-2e4f1a192760}","it":[{"ty":"sr","nm":"PolyStar","mn":"{d647a149-8105-4e08-b395-c8de40669fb0}","p":{"a":0,"k":[110.90149253731343,216.644776119403]},"or":{"a":0,"k":121.5619125366211},"ir":{"a":0,"k":60.78095626831055},"r":{"a":0,"k":143.04905700683594},"pt":{"a":0,"k":5},"sy":1,"os":{"a":0,"k":0},"is":{"a":0,"k":0}},{"ty":"st","hd":true,"nm":"Stroke","mn":"{67a87e2b-afff-4f55-9004-4cc274cefe07}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"lc":2,"lj":2,"ml":0,"w":{"a":0,"k":30}},{"ty":"fl","nm":"Fill","mn":"{39b8d13c-45cf-4ad7-972a-ef5169f1ffbf}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"r":1},{"ty":"tr","a":{"a":0,"k":[110.90149253731343,216.644776119403]},"p":{"a":0,"k":[159.9044776119403,247.59402985074627]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}}]}],"bm":10},{"ddd":0,"ty":4,"ind":0,"st":0,"ip":0,"op":180,"nm":"Layer 1","mn":"{d74c9dcc-e7af-45c3-9eab-554c7b93f6b6}","ks":{"a":{"a":0,"k":[256,256]},"p":{"a":0,"k":[256,256]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}},"shapes":[{"ty":"gr","nm":"Rectangle 1","mn":"{b22ddad1-738b-471a-86eb-1f072fa45799}","it":[{"ty":"rc","nm":"Rectangle 1","mn":"{0c09bd21-59ab-4f1e-bd3d-547613eb3e2a}","p":{"a":0,"k":[241.57611940298506,357.6358208955224]},"s":{"a":0,"k":[383.4268656716418,211.4865671641791]},"r":{"a":0,"k":0}},{"ty":"st","hd":true,"nm":"Stroke","mn":"{8cd4fca9-3480-49fa-947f-04bc40ed74f5}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"lc":2,"lj":2,"ml":0,"w":{"a":0,"k":30}},{"ty":"fl","nm":"Fill","mn":"{050089d8-44c4-4312-8e23-3c89df7615aa}","o":{"a":0,"k":100},"c":{"a":0,"k":[0.7686274509803922,0.8509803921568627,0.9607843137254902]},"r":1},{"ty":"tr","a":{"a":0,"k":[241.57611940298506,357.6358208955224]},"p":{"a":0,"k":[226.1014925373134,131.53432835820894]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","nm":"Rectangle","mn":"{700bfca8-0e45-42e9-8559-15ac0ebe93b2}","it":[{"ty":"rc","nm":"Rectangle","mn":"{0e3ac2ac-22c8-4310-8208-f5d5ba9cd6d9}","p":{"a":0,"k":[277.68358208955226,148.2985074626866]},"s":{"a":0,"k":[335.2835820895522,162.48358208955224]},"r":{"a":0,"k":0}},{"ty":"st","hd":true,"nm":"Stroke","mn":"{af0c691f-7815-414e-a988-ac2eb6e32128}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"lc":2,"lj":2,"ml":0,"w":{"a":0,"k":30}},{"ty":"fl","nm":"Fill","mn":"{059d8c4e-de02-4fa7-99fe-c069b73218be}","o":{"a":0,"k":100},"c":{"a":0,"k":[0.19607843137254902,0.3137254901960784,0.6901960784313725]},"r":1},{"ty":"tr","a":{"a":0,"k":[277.68358208955226,148.2985074626866]},"p":{"a":0,"k":[277.68358208955226,366.6626865671642]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}}]}]}],"meta":{"g":"Glaxnimate 0.4.6-32-gb62899be"}} \ No newline at end of file
diff --git a/snapshot-tests/src/main/assets/Tests/LayerBlend_11.json b/snapshot-tests/src/main/assets/Tests/LayerBlend_11.json
new file mode 100644
index 00000000..63298399
--- /dev/null
+++ b/snapshot-tests/src/main/assets/Tests/LayerBlend_11.json
@@ -0,0 +1 @@
+{"v":"5.7.1","ip":0,"op":180,"nm":"Animation","mn":"{429ff333-f31c-4124-91c5-5e861412a004}","fr":60,"w":512,"h":512,"assets":[],"layers":[{"ddd":0,"ty":4,"ind":1,"st":0,"ip":0,"op":180,"nm":"Layer","mn":"{625eab7e-4758-4d4b-b37c-d89115b1442b}","ks":{"a":{"a":0,"k":[256,256]},"p":{"a":0,"k":[256,256]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":77}},"shapes":[{"ty":"gr","nm":"Ellipse","mn":"{dd57d763-ff3b-420f-a94d-eb5503e7faa7}","it":[{"ty":"el","nm":"Ellipse","mn":"{fa5c495c-00d1-4253-b30c-cc8cb1b855b2}","p":{"a":0,"k":[400.1910447761194,240.71641791044777]},"s":{"a":0,"k":[195.15223880597017,180.53731343283584]}},{"ty":"st","hd":true,"nm":"Stroke","mn":"{89437b5f-dca9-42d4-aff9-c57ce08c8c1e}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"lc":2,"lj":2,"ml":0,"w":{"a":0,"k":30}},{"ty":"fl","nm":"Fill","mn":"{352559ca-ebe9-4b11-acdd-09e155612598}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.3411764705882353,0.01568627450980392]},"r":1},{"ty":"tr","a":{"a":0,"k":[400.1910447761194,240.71641791044777]},"p":{"a":0,"k":[400.1910447761194,240.71641791044777]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","nm":"PolyStar","mn":"{b6000853-a4d3-4b13-acdd-2e4f1a192760}","it":[{"ty":"sr","nm":"PolyStar","mn":"{d647a149-8105-4e08-b395-c8de40669fb0}","p":{"a":0,"k":[110.90149253731343,216.644776119403]},"or":{"a":0,"k":121.5619125366211},"ir":{"a":0,"k":60.78095626831055},"r":{"a":0,"k":143.04905700683594},"pt":{"a":0,"k":5},"sy":1,"os":{"a":0,"k":0},"is":{"a":0,"k":0}},{"ty":"st","hd":true,"nm":"Stroke","mn":"{67a87e2b-afff-4f55-9004-4cc274cefe07}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"lc":2,"lj":2,"ml":0,"w":{"a":0,"k":30}},{"ty":"fl","nm":"Fill","mn":"{39b8d13c-45cf-4ad7-972a-ef5169f1ffbf}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"r":1},{"ty":"tr","a":{"a":0,"k":[110.90149253731343,216.644776119403]},"p":{"a":0,"k":[159.9044776119403,247.59402985074627]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}}]}],"bm":11},{"ddd":0,"ty":4,"ind":0,"st":0,"ip":0,"op":180,"nm":"Layer 1","mn":"{d74c9dcc-e7af-45c3-9eab-554c7b93f6b6}","ks":{"a":{"a":0,"k":[256,256]},"p":{"a":0,"k":[256,256]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}},"shapes":[{"ty":"gr","nm":"Rectangle 1","mn":"{b22ddad1-738b-471a-86eb-1f072fa45799}","it":[{"ty":"rc","nm":"Rectangle 1","mn":"{0c09bd21-59ab-4f1e-bd3d-547613eb3e2a}","p":{"a":0,"k":[241.57611940298506,357.6358208955224]},"s":{"a":0,"k":[383.4268656716418,211.4865671641791]},"r":{"a":0,"k":0}},{"ty":"st","hd":true,"nm":"Stroke","mn":"{8cd4fca9-3480-49fa-947f-04bc40ed74f5}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"lc":2,"lj":2,"ml":0,"w":{"a":0,"k":30}},{"ty":"fl","nm":"Fill","mn":"{050089d8-44c4-4312-8e23-3c89df7615aa}","o":{"a":0,"k":100},"c":{"a":0,"k":[0.7686274509803922,0.8509803921568627,0.9607843137254902]},"r":1},{"ty":"tr","a":{"a":0,"k":[241.57611940298506,357.6358208955224]},"p":{"a":0,"k":[226.1014925373134,131.53432835820894]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","nm":"Rectangle","mn":"{700bfca8-0e45-42e9-8559-15ac0ebe93b2}","it":[{"ty":"rc","nm":"Rectangle","mn":"{0e3ac2ac-22c8-4310-8208-f5d5ba9cd6d9}","p":{"a":0,"k":[277.68358208955226,148.2985074626866]},"s":{"a":0,"k":[335.2835820895522,162.48358208955224]},"r":{"a":0,"k":0}},{"ty":"st","hd":true,"nm":"Stroke","mn":"{af0c691f-7815-414e-a988-ac2eb6e32128}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"lc":2,"lj":2,"ml":0,"w":{"a":0,"k":30}},{"ty":"fl","nm":"Fill","mn":"{059d8c4e-de02-4fa7-99fe-c069b73218be}","o":{"a":0,"k":100},"c":{"a":0,"k":[0.19607843137254902,0.3137254901960784,0.6901960784313725]},"r":1},{"ty":"tr","a":{"a":0,"k":[277.68358208955226,148.2985074626866]},"p":{"a":0,"k":[277.68358208955226,366.6626865671642]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}}]}]}],"meta":{"g":"Glaxnimate 0.4.6-32-gb62899be"}} \ No newline at end of file
diff --git a/snapshot-tests/src/main/assets/Tests/LayerBlend_12.json b/snapshot-tests/src/main/assets/Tests/LayerBlend_12.json
new file mode 100644
index 00000000..b8ca559b
--- /dev/null
+++ b/snapshot-tests/src/main/assets/Tests/LayerBlend_12.json
@@ -0,0 +1 @@
+{"v":"5.7.1","ip":0,"op":180,"nm":"Animation","mn":"{429ff333-f31c-4124-91c5-5e861412a004}","fr":60,"w":512,"h":512,"assets":[],"layers":[{"ddd":0,"ty":4,"ind":1,"st":0,"ip":0,"op":180,"nm":"Layer","mn":"{625eab7e-4758-4d4b-b37c-d89115b1442b}","ks":{"a":{"a":0,"k":[256,256]},"p":{"a":0,"k":[256,256]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":77}},"shapes":[{"ty":"gr","nm":"Ellipse","mn":"{dd57d763-ff3b-420f-a94d-eb5503e7faa7}","it":[{"ty":"el","nm":"Ellipse","mn":"{fa5c495c-00d1-4253-b30c-cc8cb1b855b2}","p":{"a":0,"k":[400.1910447761194,240.71641791044777]},"s":{"a":0,"k":[195.15223880597017,180.53731343283584]}},{"ty":"st","hd":true,"nm":"Stroke","mn":"{89437b5f-dca9-42d4-aff9-c57ce08c8c1e}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"lc":2,"lj":2,"ml":0,"w":{"a":0,"k":30}},{"ty":"fl","nm":"Fill","mn":"{352559ca-ebe9-4b11-acdd-09e155612598}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.3411764705882353,0.01568627450980392]},"r":1},{"ty":"tr","a":{"a":0,"k":[400.1910447761194,240.71641791044777]},"p":{"a":0,"k":[400.1910447761194,240.71641791044777]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","nm":"PolyStar","mn":"{b6000853-a4d3-4b13-acdd-2e4f1a192760}","it":[{"ty":"sr","nm":"PolyStar","mn":"{d647a149-8105-4e08-b395-c8de40669fb0}","p":{"a":0,"k":[110.90149253731343,216.644776119403]},"or":{"a":0,"k":121.5619125366211},"ir":{"a":0,"k":60.78095626831055},"r":{"a":0,"k":143.04905700683594},"pt":{"a":0,"k":5},"sy":1,"os":{"a":0,"k":0},"is":{"a":0,"k":0}},{"ty":"st","hd":true,"nm":"Stroke","mn":"{67a87e2b-afff-4f55-9004-4cc274cefe07}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"lc":2,"lj":2,"ml":0,"w":{"a":0,"k":30}},{"ty":"fl","nm":"Fill","mn":"{39b8d13c-45cf-4ad7-972a-ef5169f1ffbf}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"r":1},{"ty":"tr","a":{"a":0,"k":[110.90149253731343,216.644776119403]},"p":{"a":0,"k":[159.9044776119403,247.59402985074627]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}}]}],"bm":12},{"ddd":0,"ty":4,"ind":0,"st":0,"ip":0,"op":180,"nm":"Layer 1","mn":"{d74c9dcc-e7af-45c3-9eab-554c7b93f6b6}","ks":{"a":{"a":0,"k":[256,256]},"p":{"a":0,"k":[256,256]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}},"shapes":[{"ty":"gr","nm":"Rectangle 1","mn":"{b22ddad1-738b-471a-86eb-1f072fa45799}","it":[{"ty":"rc","nm":"Rectangle 1","mn":"{0c09bd21-59ab-4f1e-bd3d-547613eb3e2a}","p":{"a":0,"k":[241.57611940298506,357.6358208955224]},"s":{"a":0,"k":[383.4268656716418,211.4865671641791]},"r":{"a":0,"k":0}},{"ty":"st","hd":true,"nm":"Stroke","mn":"{8cd4fca9-3480-49fa-947f-04bc40ed74f5}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"lc":2,"lj":2,"ml":0,"w":{"a":0,"k":30}},{"ty":"fl","nm":"Fill","mn":"{050089d8-44c4-4312-8e23-3c89df7615aa}","o":{"a":0,"k":100},"c":{"a":0,"k":[0.7686274509803922,0.8509803921568627,0.9607843137254902]},"r":1},{"ty":"tr","a":{"a":0,"k":[241.57611940298506,357.6358208955224]},"p":{"a":0,"k":[226.1014925373134,131.53432835820894]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","nm":"Rectangle","mn":"{700bfca8-0e45-42e9-8559-15ac0ebe93b2}","it":[{"ty":"rc","nm":"Rectangle","mn":"{0e3ac2ac-22c8-4310-8208-f5d5ba9cd6d9}","p":{"a":0,"k":[277.68358208955226,148.2985074626866]},"s":{"a":0,"k":[335.2835820895522,162.48358208955224]},"r":{"a":0,"k":0}},{"ty":"st","hd":true,"nm":"Stroke","mn":"{af0c691f-7815-414e-a988-ac2eb6e32128}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"lc":2,"lj":2,"ml":0,"w":{"a":0,"k":30}},{"ty":"fl","nm":"Fill","mn":"{059d8c4e-de02-4fa7-99fe-c069b73218be}","o":{"a":0,"k":100},"c":{"a":0,"k":[0.19607843137254902,0.3137254901960784,0.6901960784313725]},"r":1},{"ty":"tr","a":{"a":0,"k":[277.68358208955226,148.2985074626866]},"p":{"a":0,"k":[277.68358208955226,366.6626865671642]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}}]}]}],"meta":{"g":"Glaxnimate 0.4.6-32-gb62899be"}} \ No newline at end of file
diff --git a/snapshot-tests/src/main/assets/Tests/LayerBlend_13.json b/snapshot-tests/src/main/assets/Tests/LayerBlend_13.json
new file mode 100644
index 00000000..e4adf67a
--- /dev/null
+++ b/snapshot-tests/src/main/assets/Tests/LayerBlend_13.json
@@ -0,0 +1 @@
+{"v":"5.7.1","ip":0,"op":180,"nm":"Animation","mn":"{429ff333-f31c-4124-91c5-5e861412a004}","fr":60,"w":512,"h":512,"assets":[],"layers":[{"ddd":0,"ty":4,"ind":1,"st":0,"ip":0,"op":180,"nm":"Layer","mn":"{625eab7e-4758-4d4b-b37c-d89115b1442b}","ks":{"a":{"a":0,"k":[256,256]},"p":{"a":0,"k":[256,256]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":77}},"shapes":[{"ty":"gr","nm":"Ellipse","mn":"{dd57d763-ff3b-420f-a94d-eb5503e7faa7}","it":[{"ty":"el","nm":"Ellipse","mn":"{fa5c495c-00d1-4253-b30c-cc8cb1b855b2}","p":{"a":0,"k":[400.1910447761194,240.71641791044777]},"s":{"a":0,"k":[195.15223880597017,180.53731343283584]}},{"ty":"st","hd":true,"nm":"Stroke","mn":"{89437b5f-dca9-42d4-aff9-c57ce08c8c1e}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"lc":2,"lj":2,"ml":0,"w":{"a":0,"k":30}},{"ty":"fl","nm":"Fill","mn":"{352559ca-ebe9-4b11-acdd-09e155612598}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.3411764705882353,0.01568627450980392]},"r":1},{"ty":"tr","a":{"a":0,"k":[400.1910447761194,240.71641791044777]},"p":{"a":0,"k":[400.1910447761194,240.71641791044777]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","nm":"PolyStar","mn":"{b6000853-a4d3-4b13-acdd-2e4f1a192760}","it":[{"ty":"sr","nm":"PolyStar","mn":"{d647a149-8105-4e08-b395-c8de40669fb0}","p":{"a":0,"k":[110.90149253731343,216.644776119403]},"or":{"a":0,"k":121.5619125366211},"ir":{"a":0,"k":60.78095626831055},"r":{"a":0,"k":143.04905700683594},"pt":{"a":0,"k":5},"sy":1,"os":{"a":0,"k":0},"is":{"a":0,"k":0}},{"ty":"st","hd":true,"nm":"Stroke","mn":"{67a87e2b-afff-4f55-9004-4cc274cefe07}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"lc":2,"lj":2,"ml":0,"w":{"a":0,"k":30}},{"ty":"fl","nm":"Fill","mn":"{39b8d13c-45cf-4ad7-972a-ef5169f1ffbf}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"r":1},{"ty":"tr","a":{"a":0,"k":[110.90149253731343,216.644776119403]},"p":{"a":0,"k":[159.9044776119403,247.59402985074627]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}}]}],"bm":13},{"ddd":0,"ty":4,"ind":0,"st":0,"ip":0,"op":180,"nm":"Layer 1","mn":"{d74c9dcc-e7af-45c3-9eab-554c7b93f6b6}","ks":{"a":{"a":0,"k":[256,256]},"p":{"a":0,"k":[256,256]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}},"shapes":[{"ty":"gr","nm":"Rectangle 1","mn":"{b22ddad1-738b-471a-86eb-1f072fa45799}","it":[{"ty":"rc","nm":"Rectangle 1","mn":"{0c09bd21-59ab-4f1e-bd3d-547613eb3e2a}","p":{"a":0,"k":[241.57611940298506,357.6358208955224]},"s":{"a":0,"k":[383.4268656716418,211.4865671641791]},"r":{"a":0,"k":0}},{"ty":"st","hd":true,"nm":"Stroke","mn":"{8cd4fca9-3480-49fa-947f-04bc40ed74f5}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"lc":2,"lj":2,"ml":0,"w":{"a":0,"k":30}},{"ty":"fl","nm":"Fill","mn":"{050089d8-44c4-4312-8e23-3c89df7615aa}","o":{"a":0,"k":100},"c":{"a":0,"k":[0.7686274509803922,0.8509803921568627,0.9607843137254902]},"r":1},{"ty":"tr","a":{"a":0,"k":[241.57611940298506,357.6358208955224]},"p":{"a":0,"k":[226.1014925373134,131.53432835820894]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","nm":"Rectangle","mn":"{700bfca8-0e45-42e9-8559-15ac0ebe93b2}","it":[{"ty":"rc","nm":"Rectangle","mn":"{0e3ac2ac-22c8-4310-8208-f5d5ba9cd6d9}","p":{"a":0,"k":[277.68358208955226,148.2985074626866]},"s":{"a":0,"k":[335.2835820895522,162.48358208955224]},"r":{"a":0,"k":0}},{"ty":"st","hd":true,"nm":"Stroke","mn":"{af0c691f-7815-414e-a988-ac2eb6e32128}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"lc":2,"lj":2,"ml":0,"w":{"a":0,"k":30}},{"ty":"fl","nm":"Fill","mn":"{059d8c4e-de02-4fa7-99fe-c069b73218be}","o":{"a":0,"k":100},"c":{"a":0,"k":[0.19607843137254902,0.3137254901960784,0.6901960784313725]},"r":1},{"ty":"tr","a":{"a":0,"k":[277.68358208955226,148.2985074626866]},"p":{"a":0,"k":[277.68358208955226,366.6626865671642]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}}]}]}],"meta":{"g":"Glaxnimate 0.4.6-32-gb62899be"}} \ No newline at end of file
diff --git a/snapshot-tests/src/main/assets/Tests/LayerBlend_14.json b/snapshot-tests/src/main/assets/Tests/LayerBlend_14.json
new file mode 100644
index 00000000..65afab7d
--- /dev/null
+++ b/snapshot-tests/src/main/assets/Tests/LayerBlend_14.json
@@ -0,0 +1 @@
+{"v":"5.7.1","ip":0,"op":180,"nm":"Animation","mn":"{429ff333-f31c-4124-91c5-5e861412a004}","fr":60,"w":512,"h":512,"assets":[],"layers":[{"ddd":0,"ty":4,"ind":1,"st":0,"ip":0,"op":180,"nm":"Layer","mn":"{625eab7e-4758-4d4b-b37c-d89115b1442b}","ks":{"a":{"a":0,"k":[256,256]},"p":{"a":0,"k":[256,256]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":77}},"shapes":[{"ty":"gr","nm":"Ellipse","mn":"{dd57d763-ff3b-420f-a94d-eb5503e7faa7}","it":[{"ty":"el","nm":"Ellipse","mn":"{fa5c495c-00d1-4253-b30c-cc8cb1b855b2}","p":{"a":0,"k":[400.1910447761194,240.71641791044777]},"s":{"a":0,"k":[195.15223880597017,180.53731343283584]}},{"ty":"st","hd":true,"nm":"Stroke","mn":"{89437b5f-dca9-42d4-aff9-c57ce08c8c1e}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"lc":2,"lj":2,"ml":0,"w":{"a":0,"k":30}},{"ty":"fl","nm":"Fill","mn":"{352559ca-ebe9-4b11-acdd-09e155612598}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.3411764705882353,0.01568627450980392]},"r":1},{"ty":"tr","a":{"a":0,"k":[400.1910447761194,240.71641791044777]},"p":{"a":0,"k":[400.1910447761194,240.71641791044777]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","nm":"PolyStar","mn":"{b6000853-a4d3-4b13-acdd-2e4f1a192760}","it":[{"ty":"sr","nm":"PolyStar","mn":"{d647a149-8105-4e08-b395-c8de40669fb0}","p":{"a":0,"k":[110.90149253731343,216.644776119403]},"or":{"a":0,"k":121.5619125366211},"ir":{"a":0,"k":60.78095626831055},"r":{"a":0,"k":143.04905700683594},"pt":{"a":0,"k":5},"sy":1,"os":{"a":0,"k":0},"is":{"a":0,"k":0}},{"ty":"st","hd":true,"nm":"Stroke","mn":"{67a87e2b-afff-4f55-9004-4cc274cefe07}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"lc":2,"lj":2,"ml":0,"w":{"a":0,"k":30}},{"ty":"fl","nm":"Fill","mn":"{39b8d13c-45cf-4ad7-972a-ef5169f1ffbf}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"r":1},{"ty":"tr","a":{"a":0,"k":[110.90149253731343,216.644776119403]},"p":{"a":0,"k":[159.9044776119403,247.59402985074627]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}}]}],"bm":14},{"ddd":0,"ty":4,"ind":0,"st":0,"ip":0,"op":180,"nm":"Layer 1","mn":"{d74c9dcc-e7af-45c3-9eab-554c7b93f6b6}","ks":{"a":{"a":0,"k":[256,256]},"p":{"a":0,"k":[256,256]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}},"shapes":[{"ty":"gr","nm":"Rectangle 1","mn":"{b22ddad1-738b-471a-86eb-1f072fa45799}","it":[{"ty":"rc","nm":"Rectangle 1","mn":"{0c09bd21-59ab-4f1e-bd3d-547613eb3e2a}","p":{"a":0,"k":[241.57611940298506,357.6358208955224]},"s":{"a":0,"k":[383.4268656716418,211.4865671641791]},"r":{"a":0,"k":0}},{"ty":"st","hd":true,"nm":"Stroke","mn":"{8cd4fca9-3480-49fa-947f-04bc40ed74f5}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"lc":2,"lj":2,"ml":0,"w":{"a":0,"k":30}},{"ty":"fl","nm":"Fill","mn":"{050089d8-44c4-4312-8e23-3c89df7615aa}","o":{"a":0,"k":100},"c":{"a":0,"k":[0.7686274509803922,0.8509803921568627,0.9607843137254902]},"r":1},{"ty":"tr","a":{"a":0,"k":[241.57611940298506,357.6358208955224]},"p":{"a":0,"k":[226.1014925373134,131.53432835820894]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","nm":"Rectangle","mn":"{700bfca8-0e45-42e9-8559-15ac0ebe93b2}","it":[{"ty":"rc","nm":"Rectangle","mn":"{0e3ac2ac-22c8-4310-8208-f5d5ba9cd6d9}","p":{"a":0,"k":[277.68358208955226,148.2985074626866]},"s":{"a":0,"k":[335.2835820895522,162.48358208955224]},"r":{"a":0,"k":0}},{"ty":"st","hd":true,"nm":"Stroke","mn":"{af0c691f-7815-414e-a988-ac2eb6e32128}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"lc":2,"lj":2,"ml":0,"w":{"a":0,"k":30}},{"ty":"fl","nm":"Fill","mn":"{059d8c4e-de02-4fa7-99fe-c069b73218be}","o":{"a":0,"k":100},"c":{"a":0,"k":[0.19607843137254902,0.3137254901960784,0.6901960784313725]},"r":1},{"ty":"tr","a":{"a":0,"k":[277.68358208955226,148.2985074626866]},"p":{"a":0,"k":[277.68358208955226,366.6626865671642]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}}]}]}],"meta":{"g":"Glaxnimate 0.4.6-32-gb62899be"}} \ No newline at end of file
diff --git a/snapshot-tests/src/main/assets/Tests/LayerBlend_15.json b/snapshot-tests/src/main/assets/Tests/LayerBlend_15.json
new file mode 100644
index 00000000..afaa89a5
--- /dev/null
+++ b/snapshot-tests/src/main/assets/Tests/LayerBlend_15.json
@@ -0,0 +1 @@
+{"v":"5.7.1","ip":0,"op":180,"nm":"Animation","mn":"{429ff333-f31c-4124-91c5-5e861412a004}","fr":60,"w":512,"h":512,"assets":[],"layers":[{"ddd":0,"ty":4,"ind":1,"st":0,"ip":0,"op":180,"nm":"Layer","mn":"{625eab7e-4758-4d4b-b37c-d89115b1442b}","ks":{"a":{"a":0,"k":[256,256]},"p":{"a":0,"k":[256,256]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":77}},"shapes":[{"ty":"gr","nm":"Ellipse","mn":"{dd57d763-ff3b-420f-a94d-eb5503e7faa7}","it":[{"ty":"el","nm":"Ellipse","mn":"{fa5c495c-00d1-4253-b30c-cc8cb1b855b2}","p":{"a":0,"k":[400.1910447761194,240.71641791044777]},"s":{"a":0,"k":[195.15223880597017,180.53731343283584]}},{"ty":"st","hd":true,"nm":"Stroke","mn":"{89437b5f-dca9-42d4-aff9-c57ce08c8c1e}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"lc":2,"lj":2,"ml":0,"w":{"a":0,"k":30}},{"ty":"fl","nm":"Fill","mn":"{352559ca-ebe9-4b11-acdd-09e155612598}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.3411764705882353,0.01568627450980392]},"r":1},{"ty":"tr","a":{"a":0,"k":[400.1910447761194,240.71641791044777]},"p":{"a":0,"k":[400.1910447761194,240.71641791044777]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","nm":"PolyStar","mn":"{b6000853-a4d3-4b13-acdd-2e4f1a192760}","it":[{"ty":"sr","nm":"PolyStar","mn":"{d647a149-8105-4e08-b395-c8de40669fb0}","p":{"a":0,"k":[110.90149253731343,216.644776119403]},"or":{"a":0,"k":121.5619125366211},"ir":{"a":0,"k":60.78095626831055},"r":{"a":0,"k":143.04905700683594},"pt":{"a":0,"k":5},"sy":1,"os":{"a":0,"k":0},"is":{"a":0,"k":0}},{"ty":"st","hd":true,"nm":"Stroke","mn":"{67a87e2b-afff-4f55-9004-4cc274cefe07}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"lc":2,"lj":2,"ml":0,"w":{"a":0,"k":30}},{"ty":"fl","nm":"Fill","mn":"{39b8d13c-45cf-4ad7-972a-ef5169f1ffbf}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"r":1},{"ty":"tr","a":{"a":0,"k":[110.90149253731343,216.644776119403]},"p":{"a":0,"k":[159.9044776119403,247.59402985074627]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}}]}],"bm":15},{"ddd":0,"ty":4,"ind":0,"st":0,"ip":0,"op":180,"nm":"Layer 1","mn":"{d74c9dcc-e7af-45c3-9eab-554c7b93f6b6}","ks":{"a":{"a":0,"k":[256,256]},"p":{"a":0,"k":[256,256]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}},"shapes":[{"ty":"gr","nm":"Rectangle 1","mn":"{b22ddad1-738b-471a-86eb-1f072fa45799}","it":[{"ty":"rc","nm":"Rectangle 1","mn":"{0c09bd21-59ab-4f1e-bd3d-547613eb3e2a}","p":{"a":0,"k":[241.57611940298506,357.6358208955224]},"s":{"a":0,"k":[383.4268656716418,211.4865671641791]},"r":{"a":0,"k":0}},{"ty":"st","hd":true,"nm":"Stroke","mn":"{8cd4fca9-3480-49fa-947f-04bc40ed74f5}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"lc":2,"lj":2,"ml":0,"w":{"a":0,"k":30}},{"ty":"fl","nm":"Fill","mn":"{050089d8-44c4-4312-8e23-3c89df7615aa}","o":{"a":0,"k":100},"c":{"a":0,"k":[0.7686274509803922,0.8509803921568627,0.9607843137254902]},"r":1},{"ty":"tr","a":{"a":0,"k":[241.57611940298506,357.6358208955224]},"p":{"a":0,"k":[226.1014925373134,131.53432835820894]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","nm":"Rectangle","mn":"{700bfca8-0e45-42e9-8559-15ac0ebe93b2}","it":[{"ty":"rc","nm":"Rectangle","mn":"{0e3ac2ac-22c8-4310-8208-f5d5ba9cd6d9}","p":{"a":0,"k":[277.68358208955226,148.2985074626866]},"s":{"a":0,"k":[335.2835820895522,162.48358208955224]},"r":{"a":0,"k":0}},{"ty":"st","hd":true,"nm":"Stroke","mn":"{af0c691f-7815-414e-a988-ac2eb6e32128}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"lc":2,"lj":2,"ml":0,"w":{"a":0,"k":30}},{"ty":"fl","nm":"Fill","mn":"{059d8c4e-de02-4fa7-99fe-c069b73218be}","o":{"a":0,"k":100},"c":{"a":0,"k":[0.19607843137254902,0.3137254901960784,0.6901960784313725]},"r":1},{"ty":"tr","a":{"a":0,"k":[277.68358208955226,148.2985074626866]},"p":{"a":0,"k":[277.68358208955226,366.6626865671642]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}}]}]}],"meta":{"g":"Glaxnimate 0.4.6-32-gb62899be"}} \ No newline at end of file
diff --git a/snapshot-tests/src/main/assets/Tests/LayerBlend_16.json b/snapshot-tests/src/main/assets/Tests/LayerBlend_16.json
new file mode 100644
index 00000000..49880c0a
--- /dev/null
+++ b/snapshot-tests/src/main/assets/Tests/LayerBlend_16.json
@@ -0,0 +1 @@
+{"v":"5.7.1","ip":0,"op":180,"nm":"Animation","mn":"{429ff333-f31c-4124-91c5-5e861412a004}","fr":60,"w":512,"h":512,"assets":[],"layers":[{"ddd":0,"ty":4,"ind":1,"st":0,"ip":0,"op":180,"nm":"Layer","mn":"{625eab7e-4758-4d4b-b37c-d89115b1442b}","ks":{"a":{"a":0,"k":[256,256]},"p":{"a":0,"k":[256,256]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":77}},"shapes":[{"ty":"gr","nm":"Ellipse","mn":"{dd57d763-ff3b-420f-a94d-eb5503e7faa7}","it":[{"ty":"el","nm":"Ellipse","mn":"{fa5c495c-00d1-4253-b30c-cc8cb1b855b2}","p":{"a":0,"k":[400.1910447761194,240.71641791044777]},"s":{"a":0,"k":[195.15223880597017,180.53731343283584]}},{"ty":"st","hd":true,"nm":"Stroke","mn":"{89437b5f-dca9-42d4-aff9-c57ce08c8c1e}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"lc":2,"lj":2,"ml":0,"w":{"a":0,"k":30}},{"ty":"fl","nm":"Fill","mn":"{352559ca-ebe9-4b11-acdd-09e155612598}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.3411764705882353,0.01568627450980392]},"r":1},{"ty":"tr","a":{"a":0,"k":[400.1910447761194,240.71641791044777]},"p":{"a":0,"k":[400.1910447761194,240.71641791044777]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","nm":"PolyStar","mn":"{b6000853-a4d3-4b13-acdd-2e4f1a192760}","it":[{"ty":"sr","nm":"PolyStar","mn":"{d647a149-8105-4e08-b395-c8de40669fb0}","p":{"a":0,"k":[110.90149253731343,216.644776119403]},"or":{"a":0,"k":121.5619125366211},"ir":{"a":0,"k":60.78095626831055},"r":{"a":0,"k":143.04905700683594},"pt":{"a":0,"k":5},"sy":1,"os":{"a":0,"k":0},"is":{"a":0,"k":0}},{"ty":"st","hd":true,"nm":"Stroke","mn":"{67a87e2b-afff-4f55-9004-4cc274cefe07}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"lc":2,"lj":2,"ml":0,"w":{"a":0,"k":30}},{"ty":"fl","nm":"Fill","mn":"{39b8d13c-45cf-4ad7-972a-ef5169f1ffbf}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"r":1},{"ty":"tr","a":{"a":0,"k":[110.90149253731343,216.644776119403]},"p":{"a":0,"k":[159.9044776119403,247.59402985074627]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}}]}],"bm":16},{"ddd":0,"ty":4,"ind":0,"st":0,"ip":0,"op":180,"nm":"Layer 1","mn":"{d74c9dcc-e7af-45c3-9eab-554c7b93f6b6}","ks":{"a":{"a":0,"k":[256,256]},"p":{"a":0,"k":[256,256]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}},"shapes":[{"ty":"gr","nm":"Rectangle 1","mn":"{b22ddad1-738b-471a-86eb-1f072fa45799}","it":[{"ty":"rc","nm":"Rectangle 1","mn":"{0c09bd21-59ab-4f1e-bd3d-547613eb3e2a}","p":{"a":0,"k":[241.57611940298506,357.6358208955224]},"s":{"a":0,"k":[383.4268656716418,211.4865671641791]},"r":{"a":0,"k":0}},{"ty":"st","hd":true,"nm":"Stroke","mn":"{8cd4fca9-3480-49fa-947f-04bc40ed74f5}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"lc":2,"lj":2,"ml":0,"w":{"a":0,"k":30}},{"ty":"fl","nm":"Fill","mn":"{050089d8-44c4-4312-8e23-3c89df7615aa}","o":{"a":0,"k":100},"c":{"a":0,"k":[0.7686274509803922,0.8509803921568627,0.9607843137254902]},"r":1},{"ty":"tr","a":{"a":0,"k":[241.57611940298506,357.6358208955224]},"p":{"a":0,"k":[226.1014925373134,131.53432835820894]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","nm":"Rectangle","mn":"{700bfca8-0e45-42e9-8559-15ac0ebe93b2}","it":[{"ty":"rc","nm":"Rectangle","mn":"{0e3ac2ac-22c8-4310-8208-f5d5ba9cd6d9}","p":{"a":0,"k":[277.68358208955226,148.2985074626866]},"s":{"a":0,"k":[335.2835820895522,162.48358208955224]},"r":{"a":0,"k":0}},{"ty":"st","hd":true,"nm":"Stroke","mn":"{af0c691f-7815-414e-a988-ac2eb6e32128}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"lc":2,"lj":2,"ml":0,"w":{"a":0,"k":30}},{"ty":"fl","nm":"Fill","mn":"{059d8c4e-de02-4fa7-99fe-c069b73218be}","o":{"a":0,"k":100},"c":{"a":0,"k":[0.19607843137254902,0.3137254901960784,0.6901960784313725]},"r":1},{"ty":"tr","a":{"a":0,"k":[277.68358208955226,148.2985074626866]},"p":{"a":0,"k":[277.68358208955226,366.6626865671642]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}}]}]}],"meta":{"g":"Glaxnimate 0.4.6-32-gb62899be"}} \ No newline at end of file
diff --git a/snapshot-tests/src/main/assets/Tests/LayerBlend_17.json b/snapshot-tests/src/main/assets/Tests/LayerBlend_17.json
new file mode 100644
index 00000000..6a4b5757
--- /dev/null
+++ b/snapshot-tests/src/main/assets/Tests/LayerBlend_17.json
@@ -0,0 +1 @@
+{"v":"5.7.1","ip":0,"op":180,"nm":"Animation","mn":"{429ff333-f31c-4124-91c5-5e861412a004}","fr":60,"w":512,"h":512,"assets":[],"layers":[{"ddd":0,"ty":4,"ind":1,"st":0,"ip":0,"op":180,"nm":"Layer","mn":"{625eab7e-4758-4d4b-b37c-d89115b1442b}","ks":{"a":{"a":0,"k":[256,256]},"p":{"a":0,"k":[256,256]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":77}},"shapes":[{"ty":"gr","nm":"Ellipse","mn":"{dd57d763-ff3b-420f-a94d-eb5503e7faa7}","it":[{"ty":"el","nm":"Ellipse","mn":"{fa5c495c-00d1-4253-b30c-cc8cb1b855b2}","p":{"a":0,"k":[400.1910447761194,240.71641791044777]},"s":{"a":0,"k":[195.15223880597017,180.53731343283584]}},{"ty":"st","hd":true,"nm":"Stroke","mn":"{89437b5f-dca9-42d4-aff9-c57ce08c8c1e}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"lc":2,"lj":2,"ml":0,"w":{"a":0,"k":30}},{"ty":"fl","nm":"Fill","mn":"{352559ca-ebe9-4b11-acdd-09e155612598}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.3411764705882353,0.01568627450980392]},"r":1},{"ty":"tr","a":{"a":0,"k":[400.1910447761194,240.71641791044777]},"p":{"a":0,"k":[400.1910447761194,240.71641791044777]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","nm":"PolyStar","mn":"{b6000853-a4d3-4b13-acdd-2e4f1a192760}","it":[{"ty":"sr","nm":"PolyStar","mn":"{d647a149-8105-4e08-b395-c8de40669fb0}","p":{"a":0,"k":[110.90149253731343,216.644776119403]},"or":{"a":0,"k":121.5619125366211},"ir":{"a":0,"k":60.78095626831055},"r":{"a":0,"k":143.04905700683594},"pt":{"a":0,"k":5},"sy":1,"os":{"a":0,"k":0},"is":{"a":0,"k":0}},{"ty":"st","hd":true,"nm":"Stroke","mn":"{67a87e2b-afff-4f55-9004-4cc274cefe07}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"lc":2,"lj":2,"ml":0,"w":{"a":0,"k":30}},{"ty":"fl","nm":"Fill","mn":"{39b8d13c-45cf-4ad7-972a-ef5169f1ffbf}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"r":1},{"ty":"tr","a":{"a":0,"k":[110.90149253731343,216.644776119403]},"p":{"a":0,"k":[159.9044776119403,247.59402985074627]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}}]}],"bm":17},{"ddd":0,"ty":4,"ind":0,"st":0,"ip":0,"op":180,"nm":"Layer 1","mn":"{d74c9dcc-e7af-45c3-9eab-554c7b93f6b6}","ks":{"a":{"a":0,"k":[256,256]},"p":{"a":0,"k":[256,256]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}},"shapes":[{"ty":"gr","nm":"Rectangle 1","mn":"{b22ddad1-738b-471a-86eb-1f072fa45799}","it":[{"ty":"rc","nm":"Rectangle 1","mn":"{0c09bd21-59ab-4f1e-bd3d-547613eb3e2a}","p":{"a":0,"k":[241.57611940298506,357.6358208955224]},"s":{"a":0,"k":[383.4268656716418,211.4865671641791]},"r":{"a":0,"k":0}},{"ty":"st","hd":true,"nm":"Stroke","mn":"{8cd4fca9-3480-49fa-947f-04bc40ed74f5}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"lc":2,"lj":2,"ml":0,"w":{"a":0,"k":30}},{"ty":"fl","nm":"Fill","mn":"{050089d8-44c4-4312-8e23-3c89df7615aa}","o":{"a":0,"k":100},"c":{"a":0,"k":[0.7686274509803922,0.8509803921568627,0.9607843137254902]},"r":1},{"ty":"tr","a":{"a":0,"k":[241.57611940298506,357.6358208955224]},"p":{"a":0,"k":[226.1014925373134,131.53432835820894]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","nm":"Rectangle","mn":"{700bfca8-0e45-42e9-8559-15ac0ebe93b2}","it":[{"ty":"rc","nm":"Rectangle","mn":"{0e3ac2ac-22c8-4310-8208-f5d5ba9cd6d9}","p":{"a":0,"k":[277.68358208955226,148.2985074626866]},"s":{"a":0,"k":[335.2835820895522,162.48358208955224]},"r":{"a":0,"k":0}},{"ty":"st","hd":true,"nm":"Stroke","mn":"{af0c691f-7815-414e-a988-ac2eb6e32128}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"lc":2,"lj":2,"ml":0,"w":{"a":0,"k":30}},{"ty":"fl","nm":"Fill","mn":"{059d8c4e-de02-4fa7-99fe-c069b73218be}","o":{"a":0,"k":100},"c":{"a":0,"k":[0.19607843137254902,0.3137254901960784,0.6901960784313725]},"r":1},{"ty":"tr","a":{"a":0,"k":[277.68358208955226,148.2985074626866]},"p":{"a":0,"k":[277.68358208955226,366.6626865671642]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}}]}]}],"meta":{"g":"Glaxnimate 0.4.6-32-gb62899be"}} \ No newline at end of file
diff --git a/snapshot-tests/src/main/assets/Tests/LayerBlend_2.json b/snapshot-tests/src/main/assets/Tests/LayerBlend_2.json
new file mode 100644
index 00000000..cd8a6bd6
--- /dev/null
+++ b/snapshot-tests/src/main/assets/Tests/LayerBlend_2.json
@@ -0,0 +1 @@
+{"v":"5.7.1","ip":0,"op":180,"nm":"Animation","mn":"{429ff333-f31c-4124-91c5-5e861412a004}","fr":60,"w":512,"h":512,"assets":[],"layers":[{"ddd":0,"ty":4,"ind":1,"st":0,"ip":0,"op":180,"nm":"Layer","mn":"{625eab7e-4758-4d4b-b37c-d89115b1442b}","ks":{"a":{"a":0,"k":[256,256]},"p":{"a":0,"k":[256,256]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":77}},"shapes":[{"ty":"gr","nm":"Ellipse","mn":"{dd57d763-ff3b-420f-a94d-eb5503e7faa7}","it":[{"ty":"el","nm":"Ellipse","mn":"{fa5c495c-00d1-4253-b30c-cc8cb1b855b2}","p":{"a":0,"k":[400.1910447761194,240.71641791044777]},"s":{"a":0,"k":[195.15223880597017,180.53731343283584]}},{"ty":"st","hd":true,"nm":"Stroke","mn":"{89437b5f-dca9-42d4-aff9-c57ce08c8c1e}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"lc":2,"lj":2,"ml":0,"w":{"a":0,"k":30}},{"ty":"fl","nm":"Fill","mn":"{352559ca-ebe9-4b11-acdd-09e155612598}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.3411764705882353,0.01568627450980392]},"r":1},{"ty":"tr","a":{"a":0,"k":[400.1910447761194,240.71641791044777]},"p":{"a":0,"k":[400.1910447761194,240.71641791044777]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","nm":"PolyStar","mn":"{b6000853-a4d3-4b13-acdd-2e4f1a192760}","it":[{"ty":"sr","nm":"PolyStar","mn":"{d647a149-8105-4e08-b395-c8de40669fb0}","p":{"a":0,"k":[110.90149253731343,216.644776119403]},"or":{"a":0,"k":121.5619125366211},"ir":{"a":0,"k":60.78095626831055},"r":{"a":0,"k":143.04905700683594},"pt":{"a":0,"k":5},"sy":1,"os":{"a":0,"k":0},"is":{"a":0,"k":0}},{"ty":"st","hd":true,"nm":"Stroke","mn":"{67a87e2b-afff-4f55-9004-4cc274cefe07}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"lc":2,"lj":2,"ml":0,"w":{"a":0,"k":30}},{"ty":"fl","nm":"Fill","mn":"{39b8d13c-45cf-4ad7-972a-ef5169f1ffbf}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"r":1},{"ty":"tr","a":{"a":0,"k":[110.90149253731343,216.644776119403]},"p":{"a":0,"k":[159.9044776119403,247.59402985074627]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}}]}],"bm":2},{"ddd":0,"ty":4,"ind":0,"st":0,"ip":0,"op":180,"nm":"Layer 1","mn":"{d74c9dcc-e7af-45c3-9eab-554c7b93f6b6}","ks":{"a":{"a":0,"k":[256,256]},"p":{"a":0,"k":[256,256]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}},"shapes":[{"ty":"gr","nm":"Rectangle 1","mn":"{b22ddad1-738b-471a-86eb-1f072fa45799}","it":[{"ty":"rc","nm":"Rectangle 1","mn":"{0c09bd21-59ab-4f1e-bd3d-547613eb3e2a}","p":{"a":0,"k":[241.57611940298506,357.6358208955224]},"s":{"a":0,"k":[383.4268656716418,211.4865671641791]},"r":{"a":0,"k":0}},{"ty":"st","hd":true,"nm":"Stroke","mn":"{8cd4fca9-3480-49fa-947f-04bc40ed74f5}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"lc":2,"lj":2,"ml":0,"w":{"a":0,"k":30}},{"ty":"fl","nm":"Fill","mn":"{050089d8-44c4-4312-8e23-3c89df7615aa}","o":{"a":0,"k":100},"c":{"a":0,"k":[0.7686274509803922,0.8509803921568627,0.9607843137254902]},"r":1},{"ty":"tr","a":{"a":0,"k":[241.57611940298506,357.6358208955224]},"p":{"a":0,"k":[226.1014925373134,131.53432835820894]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","nm":"Rectangle","mn":"{700bfca8-0e45-42e9-8559-15ac0ebe93b2}","it":[{"ty":"rc","nm":"Rectangle","mn":"{0e3ac2ac-22c8-4310-8208-f5d5ba9cd6d9}","p":{"a":0,"k":[277.68358208955226,148.2985074626866]},"s":{"a":0,"k":[335.2835820895522,162.48358208955224]},"r":{"a":0,"k":0}},{"ty":"st","hd":true,"nm":"Stroke","mn":"{af0c691f-7815-414e-a988-ac2eb6e32128}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"lc":2,"lj":2,"ml":0,"w":{"a":0,"k":30}},{"ty":"fl","nm":"Fill","mn":"{059d8c4e-de02-4fa7-99fe-c069b73218be}","o":{"a":0,"k":100},"c":{"a":0,"k":[0.19607843137254902,0.3137254901960784,0.6901960784313725]},"r":1},{"ty":"tr","a":{"a":0,"k":[277.68358208955226,148.2985074626866]},"p":{"a":0,"k":[277.68358208955226,366.6626865671642]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}}]}]}],"meta":{"g":"Glaxnimate 0.4.6-32-gb62899be"}} \ No newline at end of file
diff --git a/snapshot-tests/src/main/assets/Tests/LayerBlend_3.json b/snapshot-tests/src/main/assets/Tests/LayerBlend_3.json
new file mode 100644
index 00000000..13c414fa
--- /dev/null
+++ b/snapshot-tests/src/main/assets/Tests/LayerBlend_3.json
@@ -0,0 +1 @@
+{"v":"5.7.1","ip":0,"op":180,"nm":"Animation","mn":"{429ff333-f31c-4124-91c5-5e861412a004}","fr":60,"w":512,"h":512,"assets":[],"layers":[{"ddd":0,"ty":4,"ind":1,"st":0,"ip":0,"op":180,"nm":"Layer","mn":"{625eab7e-4758-4d4b-b37c-d89115b1442b}","ks":{"a":{"a":0,"k":[256,256]},"p":{"a":0,"k":[256,256]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":77}},"shapes":[{"ty":"gr","nm":"Ellipse","mn":"{dd57d763-ff3b-420f-a94d-eb5503e7faa7}","it":[{"ty":"el","nm":"Ellipse","mn":"{fa5c495c-00d1-4253-b30c-cc8cb1b855b2}","p":{"a":0,"k":[400.1910447761194,240.71641791044777]},"s":{"a":0,"k":[195.15223880597017,180.53731343283584]}},{"ty":"st","hd":true,"nm":"Stroke","mn":"{89437b5f-dca9-42d4-aff9-c57ce08c8c1e}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"lc":2,"lj":2,"ml":0,"w":{"a":0,"k":30}},{"ty":"fl","nm":"Fill","mn":"{352559ca-ebe9-4b11-acdd-09e155612598}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.3411764705882353,0.01568627450980392]},"r":1},{"ty":"tr","a":{"a":0,"k":[400.1910447761194,240.71641791044777]},"p":{"a":0,"k":[400.1910447761194,240.71641791044777]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","nm":"PolyStar","mn":"{b6000853-a4d3-4b13-acdd-2e4f1a192760}","it":[{"ty":"sr","nm":"PolyStar","mn":"{d647a149-8105-4e08-b395-c8de40669fb0}","p":{"a":0,"k":[110.90149253731343,216.644776119403]},"or":{"a":0,"k":121.5619125366211},"ir":{"a":0,"k":60.78095626831055},"r":{"a":0,"k":143.04905700683594},"pt":{"a":0,"k":5},"sy":1,"os":{"a":0,"k":0},"is":{"a":0,"k":0}},{"ty":"st","hd":true,"nm":"Stroke","mn":"{67a87e2b-afff-4f55-9004-4cc274cefe07}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"lc":2,"lj":2,"ml":0,"w":{"a":0,"k":30}},{"ty":"fl","nm":"Fill","mn":"{39b8d13c-45cf-4ad7-972a-ef5169f1ffbf}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"r":1},{"ty":"tr","a":{"a":0,"k":[110.90149253731343,216.644776119403]},"p":{"a":0,"k":[159.9044776119403,247.59402985074627]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}}]}],"bm":3},{"ddd":0,"ty":4,"ind":0,"st":0,"ip":0,"op":180,"nm":"Layer 1","mn":"{d74c9dcc-e7af-45c3-9eab-554c7b93f6b6}","ks":{"a":{"a":0,"k":[256,256]},"p":{"a":0,"k":[256,256]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}},"shapes":[{"ty":"gr","nm":"Rectangle 1","mn":"{b22ddad1-738b-471a-86eb-1f072fa45799}","it":[{"ty":"rc","nm":"Rectangle 1","mn":"{0c09bd21-59ab-4f1e-bd3d-547613eb3e2a}","p":{"a":0,"k":[241.57611940298506,357.6358208955224]},"s":{"a":0,"k":[383.4268656716418,211.4865671641791]},"r":{"a":0,"k":0}},{"ty":"st","hd":true,"nm":"Stroke","mn":"{8cd4fca9-3480-49fa-947f-04bc40ed74f5}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"lc":2,"lj":2,"ml":0,"w":{"a":0,"k":30}},{"ty":"fl","nm":"Fill","mn":"{050089d8-44c4-4312-8e23-3c89df7615aa}","o":{"a":0,"k":100},"c":{"a":0,"k":[0.7686274509803922,0.8509803921568627,0.9607843137254902]},"r":1},{"ty":"tr","a":{"a":0,"k":[241.57611940298506,357.6358208955224]},"p":{"a":0,"k":[226.1014925373134,131.53432835820894]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","nm":"Rectangle","mn":"{700bfca8-0e45-42e9-8559-15ac0ebe93b2}","it":[{"ty":"rc","nm":"Rectangle","mn":"{0e3ac2ac-22c8-4310-8208-f5d5ba9cd6d9}","p":{"a":0,"k":[277.68358208955226,148.2985074626866]},"s":{"a":0,"k":[335.2835820895522,162.48358208955224]},"r":{"a":0,"k":0}},{"ty":"st","hd":true,"nm":"Stroke","mn":"{af0c691f-7815-414e-a988-ac2eb6e32128}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"lc":2,"lj":2,"ml":0,"w":{"a":0,"k":30}},{"ty":"fl","nm":"Fill","mn":"{059d8c4e-de02-4fa7-99fe-c069b73218be}","o":{"a":0,"k":100},"c":{"a":0,"k":[0.19607843137254902,0.3137254901960784,0.6901960784313725]},"r":1},{"ty":"tr","a":{"a":0,"k":[277.68358208955226,148.2985074626866]},"p":{"a":0,"k":[277.68358208955226,366.6626865671642]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}}]}]}],"meta":{"g":"Glaxnimate 0.4.6-32-gb62899be"}} \ No newline at end of file
diff --git a/snapshot-tests/src/main/assets/Tests/LayerBlend_4.json b/snapshot-tests/src/main/assets/Tests/LayerBlend_4.json
new file mode 100644
index 00000000..c3991b39
--- /dev/null
+++ b/snapshot-tests/src/main/assets/Tests/LayerBlend_4.json
@@ -0,0 +1 @@
+{"v":"5.7.1","ip":0,"op":180,"nm":"Animation","mn":"{429ff333-f31c-4124-91c5-5e861412a004}","fr":60,"w":512,"h":512,"assets":[],"layers":[{"ddd":0,"ty":4,"ind":1,"st":0,"ip":0,"op":180,"nm":"Layer","mn":"{625eab7e-4758-4d4b-b37c-d89115b1442b}","ks":{"a":{"a":0,"k":[256,256]},"p":{"a":0,"k":[256,256]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":77}},"shapes":[{"ty":"gr","nm":"Ellipse","mn":"{dd57d763-ff3b-420f-a94d-eb5503e7faa7}","it":[{"ty":"el","nm":"Ellipse","mn":"{fa5c495c-00d1-4253-b30c-cc8cb1b855b2}","p":{"a":0,"k":[400.1910447761194,240.71641791044777]},"s":{"a":0,"k":[195.15223880597017,180.53731343283584]}},{"ty":"st","hd":true,"nm":"Stroke","mn":"{89437b5f-dca9-42d4-aff9-c57ce08c8c1e}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"lc":2,"lj":2,"ml":0,"w":{"a":0,"k":30}},{"ty":"fl","nm":"Fill","mn":"{352559ca-ebe9-4b11-acdd-09e155612598}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.3411764705882353,0.01568627450980392]},"r":1},{"ty":"tr","a":{"a":0,"k":[400.1910447761194,240.71641791044777]},"p":{"a":0,"k":[400.1910447761194,240.71641791044777]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","nm":"PolyStar","mn":"{b6000853-a4d3-4b13-acdd-2e4f1a192760}","it":[{"ty":"sr","nm":"PolyStar","mn":"{d647a149-8105-4e08-b395-c8de40669fb0}","p":{"a":0,"k":[110.90149253731343,216.644776119403]},"or":{"a":0,"k":121.5619125366211},"ir":{"a":0,"k":60.78095626831055},"r":{"a":0,"k":143.04905700683594},"pt":{"a":0,"k":5},"sy":1,"os":{"a":0,"k":0},"is":{"a":0,"k":0}},{"ty":"st","hd":true,"nm":"Stroke","mn":"{67a87e2b-afff-4f55-9004-4cc274cefe07}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"lc":2,"lj":2,"ml":0,"w":{"a":0,"k":30}},{"ty":"fl","nm":"Fill","mn":"{39b8d13c-45cf-4ad7-972a-ef5169f1ffbf}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"r":1},{"ty":"tr","a":{"a":0,"k":[110.90149253731343,216.644776119403]},"p":{"a":0,"k":[159.9044776119403,247.59402985074627]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}}]}],"bm":4},{"ddd":0,"ty":4,"ind":0,"st":0,"ip":0,"op":180,"nm":"Layer 1","mn":"{d74c9dcc-e7af-45c3-9eab-554c7b93f6b6}","ks":{"a":{"a":0,"k":[256,256]},"p":{"a":0,"k":[256,256]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}},"shapes":[{"ty":"gr","nm":"Rectangle 1","mn":"{b22ddad1-738b-471a-86eb-1f072fa45799}","it":[{"ty":"rc","nm":"Rectangle 1","mn":"{0c09bd21-59ab-4f1e-bd3d-547613eb3e2a}","p":{"a":0,"k":[241.57611940298506,357.6358208955224]},"s":{"a":0,"k":[383.4268656716418,211.4865671641791]},"r":{"a":0,"k":0}},{"ty":"st","hd":true,"nm":"Stroke","mn":"{8cd4fca9-3480-49fa-947f-04bc40ed74f5}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"lc":2,"lj":2,"ml":0,"w":{"a":0,"k":30}},{"ty":"fl","nm":"Fill","mn":"{050089d8-44c4-4312-8e23-3c89df7615aa}","o":{"a":0,"k":100},"c":{"a":0,"k":[0.7686274509803922,0.8509803921568627,0.9607843137254902]},"r":1},{"ty":"tr","a":{"a":0,"k":[241.57611940298506,357.6358208955224]},"p":{"a":0,"k":[226.1014925373134,131.53432835820894]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","nm":"Rectangle","mn":"{700bfca8-0e45-42e9-8559-15ac0ebe93b2}","it":[{"ty":"rc","nm":"Rectangle","mn":"{0e3ac2ac-22c8-4310-8208-f5d5ba9cd6d9}","p":{"a":0,"k":[277.68358208955226,148.2985074626866]},"s":{"a":0,"k":[335.2835820895522,162.48358208955224]},"r":{"a":0,"k":0}},{"ty":"st","hd":true,"nm":"Stroke","mn":"{af0c691f-7815-414e-a988-ac2eb6e32128}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"lc":2,"lj":2,"ml":0,"w":{"a":0,"k":30}},{"ty":"fl","nm":"Fill","mn":"{059d8c4e-de02-4fa7-99fe-c069b73218be}","o":{"a":0,"k":100},"c":{"a":0,"k":[0.19607843137254902,0.3137254901960784,0.6901960784313725]},"r":1},{"ty":"tr","a":{"a":0,"k":[277.68358208955226,148.2985074626866]},"p":{"a":0,"k":[277.68358208955226,366.6626865671642]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}}]}]}],"meta":{"g":"Glaxnimate 0.4.6-32-gb62899be"}} \ No newline at end of file
diff --git a/snapshot-tests/src/main/assets/Tests/LayerBlend_5.json b/snapshot-tests/src/main/assets/Tests/LayerBlend_5.json
new file mode 100644
index 00000000..fe6c1c66
--- /dev/null
+++ b/snapshot-tests/src/main/assets/Tests/LayerBlend_5.json
@@ -0,0 +1 @@
+{"v":"5.7.1","ip":0,"op":180,"nm":"Animation","mn":"{429ff333-f31c-4124-91c5-5e861412a004}","fr":60,"w":512,"h":512,"assets":[],"layers":[{"ddd":0,"ty":4,"ind":1,"st":0,"ip":0,"op":180,"nm":"Layer","mn":"{625eab7e-4758-4d4b-b37c-d89115b1442b}","ks":{"a":{"a":0,"k":[256,256]},"p":{"a":0,"k":[256,256]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":77}},"shapes":[{"ty":"gr","nm":"Ellipse","mn":"{dd57d763-ff3b-420f-a94d-eb5503e7faa7}","it":[{"ty":"el","nm":"Ellipse","mn":"{fa5c495c-00d1-4253-b30c-cc8cb1b855b2}","p":{"a":0,"k":[400.1910447761194,240.71641791044777]},"s":{"a":0,"k":[195.15223880597017,180.53731343283584]}},{"ty":"st","hd":true,"nm":"Stroke","mn":"{89437b5f-dca9-42d4-aff9-c57ce08c8c1e}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"lc":2,"lj":2,"ml":0,"w":{"a":0,"k":30}},{"ty":"fl","nm":"Fill","mn":"{352559ca-ebe9-4b11-acdd-09e155612598}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.3411764705882353,0.01568627450980392]},"r":1},{"ty":"tr","a":{"a":0,"k":[400.1910447761194,240.71641791044777]},"p":{"a":0,"k":[400.1910447761194,240.71641791044777]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","nm":"PolyStar","mn":"{b6000853-a4d3-4b13-acdd-2e4f1a192760}","it":[{"ty":"sr","nm":"PolyStar","mn":"{d647a149-8105-4e08-b395-c8de40669fb0}","p":{"a":0,"k":[110.90149253731343,216.644776119403]},"or":{"a":0,"k":121.5619125366211},"ir":{"a":0,"k":60.78095626831055},"r":{"a":0,"k":143.04905700683594},"pt":{"a":0,"k":5},"sy":1,"os":{"a":0,"k":0},"is":{"a":0,"k":0}},{"ty":"st","hd":true,"nm":"Stroke","mn":"{67a87e2b-afff-4f55-9004-4cc274cefe07}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"lc":2,"lj":2,"ml":0,"w":{"a":0,"k":30}},{"ty":"fl","nm":"Fill","mn":"{39b8d13c-45cf-4ad7-972a-ef5169f1ffbf}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"r":1},{"ty":"tr","a":{"a":0,"k":[110.90149253731343,216.644776119403]},"p":{"a":0,"k":[159.9044776119403,247.59402985074627]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}}]}],"bm":5},{"ddd":0,"ty":4,"ind":0,"st":0,"ip":0,"op":180,"nm":"Layer 1","mn":"{d74c9dcc-e7af-45c3-9eab-554c7b93f6b6}","ks":{"a":{"a":0,"k":[256,256]},"p":{"a":0,"k":[256,256]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}},"shapes":[{"ty":"gr","nm":"Rectangle 1","mn":"{b22ddad1-738b-471a-86eb-1f072fa45799}","it":[{"ty":"rc","nm":"Rectangle 1","mn":"{0c09bd21-59ab-4f1e-bd3d-547613eb3e2a}","p":{"a":0,"k":[241.57611940298506,357.6358208955224]},"s":{"a":0,"k":[383.4268656716418,211.4865671641791]},"r":{"a":0,"k":0}},{"ty":"st","hd":true,"nm":"Stroke","mn":"{8cd4fca9-3480-49fa-947f-04bc40ed74f5}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"lc":2,"lj":2,"ml":0,"w":{"a":0,"k":30}},{"ty":"fl","nm":"Fill","mn":"{050089d8-44c4-4312-8e23-3c89df7615aa}","o":{"a":0,"k":100},"c":{"a":0,"k":[0.7686274509803922,0.8509803921568627,0.9607843137254902]},"r":1},{"ty":"tr","a":{"a":0,"k":[241.57611940298506,357.6358208955224]},"p":{"a":0,"k":[226.1014925373134,131.53432835820894]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","nm":"Rectangle","mn":"{700bfca8-0e45-42e9-8559-15ac0ebe93b2}","it":[{"ty":"rc","nm":"Rectangle","mn":"{0e3ac2ac-22c8-4310-8208-f5d5ba9cd6d9}","p":{"a":0,"k":[277.68358208955226,148.2985074626866]},"s":{"a":0,"k":[335.2835820895522,162.48358208955224]},"r":{"a":0,"k":0}},{"ty":"st","hd":true,"nm":"Stroke","mn":"{af0c691f-7815-414e-a988-ac2eb6e32128}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"lc":2,"lj":2,"ml":0,"w":{"a":0,"k":30}},{"ty":"fl","nm":"Fill","mn":"{059d8c4e-de02-4fa7-99fe-c069b73218be}","o":{"a":0,"k":100},"c":{"a":0,"k":[0.19607843137254902,0.3137254901960784,0.6901960784313725]},"r":1},{"ty":"tr","a":{"a":0,"k":[277.68358208955226,148.2985074626866]},"p":{"a":0,"k":[277.68358208955226,366.6626865671642]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}}]}]}],"meta":{"g":"Glaxnimate 0.4.6-32-gb62899be"}} \ No newline at end of file
diff --git a/snapshot-tests/src/main/assets/Tests/LayerBlend_6.json b/snapshot-tests/src/main/assets/Tests/LayerBlend_6.json
new file mode 100644
index 00000000..cfd6780e
--- /dev/null
+++ b/snapshot-tests/src/main/assets/Tests/LayerBlend_6.json
@@ -0,0 +1 @@
+{"v":"5.7.1","ip":0,"op":180,"nm":"Animation","mn":"{429ff333-f31c-4124-91c5-5e861412a004}","fr":60,"w":512,"h":512,"assets":[],"layers":[{"ddd":0,"ty":4,"ind":1,"st":0,"ip":0,"op":180,"nm":"Layer","mn":"{625eab7e-4758-4d4b-b37c-d89115b1442b}","ks":{"a":{"a":0,"k":[256,256]},"p":{"a":0,"k":[256,256]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":77}},"shapes":[{"ty":"gr","nm":"Ellipse","mn":"{dd57d763-ff3b-420f-a94d-eb5503e7faa7}","it":[{"ty":"el","nm":"Ellipse","mn":"{fa5c495c-00d1-4253-b30c-cc8cb1b855b2}","p":{"a":0,"k":[400.1910447761194,240.71641791044777]},"s":{"a":0,"k":[195.15223880597017,180.53731343283584]}},{"ty":"st","hd":true,"nm":"Stroke","mn":"{89437b5f-dca9-42d4-aff9-c57ce08c8c1e}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"lc":2,"lj":2,"ml":0,"w":{"a":0,"k":30}},{"ty":"fl","nm":"Fill","mn":"{352559ca-ebe9-4b11-acdd-09e155612598}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.3411764705882353,0.01568627450980392]},"r":1},{"ty":"tr","a":{"a":0,"k":[400.1910447761194,240.71641791044777]},"p":{"a":0,"k":[400.1910447761194,240.71641791044777]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","nm":"PolyStar","mn":"{b6000853-a4d3-4b13-acdd-2e4f1a192760}","it":[{"ty":"sr","nm":"PolyStar","mn":"{d647a149-8105-4e08-b395-c8de40669fb0}","p":{"a":0,"k":[110.90149253731343,216.644776119403]},"or":{"a":0,"k":121.5619125366211},"ir":{"a":0,"k":60.78095626831055},"r":{"a":0,"k":143.04905700683594},"pt":{"a":0,"k":5},"sy":1,"os":{"a":0,"k":0},"is":{"a":0,"k":0}},{"ty":"st","hd":true,"nm":"Stroke","mn":"{67a87e2b-afff-4f55-9004-4cc274cefe07}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"lc":2,"lj":2,"ml":0,"w":{"a":0,"k":30}},{"ty":"fl","nm":"Fill","mn":"{39b8d13c-45cf-4ad7-972a-ef5169f1ffbf}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"r":1},{"ty":"tr","a":{"a":0,"k":[110.90149253731343,216.644776119403]},"p":{"a":0,"k":[159.9044776119403,247.59402985074627]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}}]}],"bm":6},{"ddd":0,"ty":4,"ind":0,"st":0,"ip":0,"op":180,"nm":"Layer 1","mn":"{d74c9dcc-e7af-45c3-9eab-554c7b93f6b6}","ks":{"a":{"a":0,"k":[256,256]},"p":{"a":0,"k":[256,256]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}},"shapes":[{"ty":"gr","nm":"Rectangle 1","mn":"{b22ddad1-738b-471a-86eb-1f072fa45799}","it":[{"ty":"rc","nm":"Rectangle 1","mn":"{0c09bd21-59ab-4f1e-bd3d-547613eb3e2a}","p":{"a":0,"k":[241.57611940298506,357.6358208955224]},"s":{"a":0,"k":[383.4268656716418,211.4865671641791]},"r":{"a":0,"k":0}},{"ty":"st","hd":true,"nm":"Stroke","mn":"{8cd4fca9-3480-49fa-947f-04bc40ed74f5}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"lc":2,"lj":2,"ml":0,"w":{"a":0,"k":30}},{"ty":"fl","nm":"Fill","mn":"{050089d8-44c4-4312-8e23-3c89df7615aa}","o":{"a":0,"k":100},"c":{"a":0,"k":[0.7686274509803922,0.8509803921568627,0.9607843137254902]},"r":1},{"ty":"tr","a":{"a":0,"k":[241.57611940298506,357.6358208955224]},"p":{"a":0,"k":[226.1014925373134,131.53432835820894]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","nm":"Rectangle","mn":"{700bfca8-0e45-42e9-8559-15ac0ebe93b2}","it":[{"ty":"rc","nm":"Rectangle","mn":"{0e3ac2ac-22c8-4310-8208-f5d5ba9cd6d9}","p":{"a":0,"k":[277.68358208955226,148.2985074626866]},"s":{"a":0,"k":[335.2835820895522,162.48358208955224]},"r":{"a":0,"k":0}},{"ty":"st","hd":true,"nm":"Stroke","mn":"{af0c691f-7815-414e-a988-ac2eb6e32128}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"lc":2,"lj":2,"ml":0,"w":{"a":0,"k":30}},{"ty":"fl","nm":"Fill","mn":"{059d8c4e-de02-4fa7-99fe-c069b73218be}","o":{"a":0,"k":100},"c":{"a":0,"k":[0.19607843137254902,0.3137254901960784,0.6901960784313725]},"r":1},{"ty":"tr","a":{"a":0,"k":[277.68358208955226,148.2985074626866]},"p":{"a":0,"k":[277.68358208955226,366.6626865671642]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}}]}]}],"meta":{"g":"Glaxnimate 0.4.6-32-gb62899be"}} \ No newline at end of file
diff --git a/snapshot-tests/src/main/assets/Tests/LayerBlend_7.json b/snapshot-tests/src/main/assets/Tests/LayerBlend_7.json
new file mode 100644
index 00000000..11f0b24e
--- /dev/null
+++ b/snapshot-tests/src/main/assets/Tests/LayerBlend_7.json
@@ -0,0 +1 @@
+{"v":"5.7.1","ip":0,"op":180,"nm":"Animation","mn":"{429ff333-f31c-4124-91c5-5e861412a004}","fr":60,"w":512,"h":512,"assets":[],"layers":[{"ddd":0,"ty":4,"ind":1,"st":0,"ip":0,"op":180,"nm":"Layer","mn":"{625eab7e-4758-4d4b-b37c-d89115b1442b}","ks":{"a":{"a":0,"k":[256,256]},"p":{"a":0,"k":[256,256]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":77}},"shapes":[{"ty":"gr","nm":"Ellipse","mn":"{dd57d763-ff3b-420f-a94d-eb5503e7faa7}","it":[{"ty":"el","nm":"Ellipse","mn":"{fa5c495c-00d1-4253-b30c-cc8cb1b855b2}","p":{"a":0,"k":[400.1910447761194,240.71641791044777]},"s":{"a":0,"k":[195.15223880597017,180.53731343283584]}},{"ty":"st","hd":true,"nm":"Stroke","mn":"{89437b5f-dca9-42d4-aff9-c57ce08c8c1e}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"lc":2,"lj":2,"ml":0,"w":{"a":0,"k":30}},{"ty":"fl","nm":"Fill","mn":"{352559ca-ebe9-4b11-acdd-09e155612598}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.3411764705882353,0.01568627450980392]},"r":1},{"ty":"tr","a":{"a":0,"k":[400.1910447761194,240.71641791044777]},"p":{"a":0,"k":[400.1910447761194,240.71641791044777]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","nm":"PolyStar","mn":"{b6000853-a4d3-4b13-acdd-2e4f1a192760}","it":[{"ty":"sr","nm":"PolyStar","mn":"{d647a149-8105-4e08-b395-c8de40669fb0}","p":{"a":0,"k":[110.90149253731343,216.644776119403]},"or":{"a":0,"k":121.5619125366211},"ir":{"a":0,"k":60.78095626831055},"r":{"a":0,"k":143.04905700683594},"pt":{"a":0,"k":5},"sy":1,"os":{"a":0,"k":0},"is":{"a":0,"k":0}},{"ty":"st","hd":true,"nm":"Stroke","mn":"{67a87e2b-afff-4f55-9004-4cc274cefe07}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"lc":2,"lj":2,"ml":0,"w":{"a":0,"k":30}},{"ty":"fl","nm":"Fill","mn":"{39b8d13c-45cf-4ad7-972a-ef5169f1ffbf}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"r":1},{"ty":"tr","a":{"a":0,"k":[110.90149253731343,216.644776119403]},"p":{"a":0,"k":[159.9044776119403,247.59402985074627]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}}]}],"bm":7},{"ddd":0,"ty":4,"ind":0,"st":0,"ip":0,"op":180,"nm":"Layer 1","mn":"{d74c9dcc-e7af-45c3-9eab-554c7b93f6b6}","ks":{"a":{"a":0,"k":[256,256]},"p":{"a":0,"k":[256,256]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}},"shapes":[{"ty":"gr","nm":"Rectangle 1","mn":"{b22ddad1-738b-471a-86eb-1f072fa45799}","it":[{"ty":"rc","nm":"Rectangle 1","mn":"{0c09bd21-59ab-4f1e-bd3d-547613eb3e2a}","p":{"a":0,"k":[241.57611940298506,357.6358208955224]},"s":{"a":0,"k":[383.4268656716418,211.4865671641791]},"r":{"a":0,"k":0}},{"ty":"st","hd":true,"nm":"Stroke","mn":"{8cd4fca9-3480-49fa-947f-04bc40ed74f5}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"lc":2,"lj":2,"ml":0,"w":{"a":0,"k":30}},{"ty":"fl","nm":"Fill","mn":"{050089d8-44c4-4312-8e23-3c89df7615aa}","o":{"a":0,"k":100},"c":{"a":0,"k":[0.7686274509803922,0.8509803921568627,0.9607843137254902]},"r":1},{"ty":"tr","a":{"a":0,"k":[241.57611940298506,357.6358208955224]},"p":{"a":0,"k":[226.1014925373134,131.53432835820894]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","nm":"Rectangle","mn":"{700bfca8-0e45-42e9-8559-15ac0ebe93b2}","it":[{"ty":"rc","nm":"Rectangle","mn":"{0e3ac2ac-22c8-4310-8208-f5d5ba9cd6d9}","p":{"a":0,"k":[277.68358208955226,148.2985074626866]},"s":{"a":0,"k":[335.2835820895522,162.48358208955224]},"r":{"a":0,"k":0}},{"ty":"st","hd":true,"nm":"Stroke","mn":"{af0c691f-7815-414e-a988-ac2eb6e32128}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"lc":2,"lj":2,"ml":0,"w":{"a":0,"k":30}},{"ty":"fl","nm":"Fill","mn":"{059d8c4e-de02-4fa7-99fe-c069b73218be}","o":{"a":0,"k":100},"c":{"a":0,"k":[0.19607843137254902,0.3137254901960784,0.6901960784313725]},"r":1},{"ty":"tr","a":{"a":0,"k":[277.68358208955226,148.2985074626866]},"p":{"a":0,"k":[277.68358208955226,366.6626865671642]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}}]}]}],"meta":{"g":"Glaxnimate 0.4.6-32-gb62899be"}} \ No newline at end of file
diff --git a/snapshot-tests/src/main/assets/Tests/LayerBlend_8.json b/snapshot-tests/src/main/assets/Tests/LayerBlend_8.json
new file mode 100644
index 00000000..a86b3e71
--- /dev/null
+++ b/snapshot-tests/src/main/assets/Tests/LayerBlend_8.json
@@ -0,0 +1 @@
+{"v":"5.7.1","ip":0,"op":180,"nm":"Animation","mn":"{429ff333-f31c-4124-91c5-5e861412a004}","fr":60,"w":512,"h":512,"assets":[],"layers":[{"ddd":0,"ty":4,"ind":1,"st":0,"ip":0,"op":180,"nm":"Layer","mn":"{625eab7e-4758-4d4b-b37c-d89115b1442b}","ks":{"a":{"a":0,"k":[256,256]},"p":{"a":0,"k":[256,256]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":77}},"shapes":[{"ty":"gr","nm":"Ellipse","mn":"{dd57d763-ff3b-420f-a94d-eb5503e7faa7}","it":[{"ty":"el","nm":"Ellipse","mn":"{fa5c495c-00d1-4253-b30c-cc8cb1b855b2}","p":{"a":0,"k":[400.1910447761194,240.71641791044777]},"s":{"a":0,"k":[195.15223880597017,180.53731343283584]}},{"ty":"st","hd":true,"nm":"Stroke","mn":"{89437b5f-dca9-42d4-aff9-c57ce08c8c1e}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"lc":2,"lj":2,"ml":0,"w":{"a":0,"k":30}},{"ty":"fl","nm":"Fill","mn":"{352559ca-ebe9-4b11-acdd-09e155612598}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.3411764705882353,0.01568627450980392]},"r":1},{"ty":"tr","a":{"a":0,"k":[400.1910447761194,240.71641791044777]},"p":{"a":0,"k":[400.1910447761194,240.71641791044777]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","nm":"PolyStar","mn":"{b6000853-a4d3-4b13-acdd-2e4f1a192760}","it":[{"ty":"sr","nm":"PolyStar","mn":"{d647a149-8105-4e08-b395-c8de40669fb0}","p":{"a":0,"k":[110.90149253731343,216.644776119403]},"or":{"a":0,"k":121.5619125366211},"ir":{"a":0,"k":60.78095626831055},"r":{"a":0,"k":143.04905700683594},"pt":{"a":0,"k":5},"sy":1,"os":{"a":0,"k":0},"is":{"a":0,"k":0}},{"ty":"st","hd":true,"nm":"Stroke","mn":"{67a87e2b-afff-4f55-9004-4cc274cefe07}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"lc":2,"lj":2,"ml":0,"w":{"a":0,"k":30}},{"ty":"fl","nm":"Fill","mn":"{39b8d13c-45cf-4ad7-972a-ef5169f1ffbf}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"r":1},{"ty":"tr","a":{"a":0,"k":[110.90149253731343,216.644776119403]},"p":{"a":0,"k":[159.9044776119403,247.59402985074627]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}}]}],"bm":8},{"ddd":0,"ty":4,"ind":0,"st":0,"ip":0,"op":180,"nm":"Layer 1","mn":"{d74c9dcc-e7af-45c3-9eab-554c7b93f6b6}","ks":{"a":{"a":0,"k":[256,256]},"p":{"a":0,"k":[256,256]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}},"shapes":[{"ty":"gr","nm":"Rectangle 1","mn":"{b22ddad1-738b-471a-86eb-1f072fa45799}","it":[{"ty":"rc","nm":"Rectangle 1","mn":"{0c09bd21-59ab-4f1e-bd3d-547613eb3e2a}","p":{"a":0,"k":[241.57611940298506,357.6358208955224]},"s":{"a":0,"k":[383.4268656716418,211.4865671641791]},"r":{"a":0,"k":0}},{"ty":"st","hd":true,"nm":"Stroke","mn":"{8cd4fca9-3480-49fa-947f-04bc40ed74f5}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"lc":2,"lj":2,"ml":0,"w":{"a":0,"k":30}},{"ty":"fl","nm":"Fill","mn":"{050089d8-44c4-4312-8e23-3c89df7615aa}","o":{"a":0,"k":100},"c":{"a":0,"k":[0.7686274509803922,0.8509803921568627,0.9607843137254902]},"r":1},{"ty":"tr","a":{"a":0,"k":[241.57611940298506,357.6358208955224]},"p":{"a":0,"k":[226.1014925373134,131.53432835820894]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","nm":"Rectangle","mn":"{700bfca8-0e45-42e9-8559-15ac0ebe93b2}","it":[{"ty":"rc","nm":"Rectangle","mn":"{0e3ac2ac-22c8-4310-8208-f5d5ba9cd6d9}","p":{"a":0,"k":[277.68358208955226,148.2985074626866]},"s":{"a":0,"k":[335.2835820895522,162.48358208955224]},"r":{"a":0,"k":0}},{"ty":"st","hd":true,"nm":"Stroke","mn":"{af0c691f-7815-414e-a988-ac2eb6e32128}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"lc":2,"lj":2,"ml":0,"w":{"a":0,"k":30}},{"ty":"fl","nm":"Fill","mn":"{059d8c4e-de02-4fa7-99fe-c069b73218be}","o":{"a":0,"k":100},"c":{"a":0,"k":[0.19607843137254902,0.3137254901960784,0.6901960784313725]},"r":1},{"ty":"tr","a":{"a":0,"k":[277.68358208955226,148.2985074626866]},"p":{"a":0,"k":[277.68358208955226,366.6626865671642]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}}]}]}],"meta":{"g":"Glaxnimate 0.4.6-32-gb62899be"}} \ No newline at end of file
diff --git a/snapshot-tests/src/main/assets/Tests/LayerBlend_9.json b/snapshot-tests/src/main/assets/Tests/LayerBlend_9.json
new file mode 100644
index 00000000..0ccd52ab
--- /dev/null
+++ b/snapshot-tests/src/main/assets/Tests/LayerBlend_9.json
@@ -0,0 +1 @@
+{"v":"5.7.1","ip":0,"op":180,"nm":"Animation","mn":"{429ff333-f31c-4124-91c5-5e861412a004}","fr":60,"w":512,"h":512,"assets":[],"layers":[{"ddd":0,"ty":4,"ind":1,"st":0,"ip":0,"op":180,"nm":"Layer","mn":"{625eab7e-4758-4d4b-b37c-d89115b1442b}","ks":{"a":{"a":0,"k":[256,256]},"p":{"a":0,"k":[256,256]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":77}},"shapes":[{"ty":"gr","nm":"Ellipse","mn":"{dd57d763-ff3b-420f-a94d-eb5503e7faa7}","it":[{"ty":"el","nm":"Ellipse","mn":"{fa5c495c-00d1-4253-b30c-cc8cb1b855b2}","p":{"a":0,"k":[400.1910447761194,240.71641791044777]},"s":{"a":0,"k":[195.15223880597017,180.53731343283584]}},{"ty":"st","hd":true,"nm":"Stroke","mn":"{89437b5f-dca9-42d4-aff9-c57ce08c8c1e}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"lc":2,"lj":2,"ml":0,"w":{"a":0,"k":30}},{"ty":"fl","nm":"Fill","mn":"{352559ca-ebe9-4b11-acdd-09e155612598}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.3411764705882353,0.01568627450980392]},"r":1},{"ty":"tr","a":{"a":0,"k":[400.1910447761194,240.71641791044777]},"p":{"a":0,"k":[400.1910447761194,240.71641791044777]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","nm":"PolyStar","mn":"{b6000853-a4d3-4b13-acdd-2e4f1a192760}","it":[{"ty":"sr","nm":"PolyStar","mn":"{d647a149-8105-4e08-b395-c8de40669fb0}","p":{"a":0,"k":[110.90149253731343,216.644776119403]},"or":{"a":0,"k":121.5619125366211},"ir":{"a":0,"k":60.78095626831055},"r":{"a":0,"k":143.04905700683594},"pt":{"a":0,"k":5},"sy":1,"os":{"a":0,"k":0},"is":{"a":0,"k":0}},{"ty":"st","hd":true,"nm":"Stroke","mn":"{67a87e2b-afff-4f55-9004-4cc274cefe07}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"lc":2,"lj":2,"ml":0,"w":{"a":0,"k":30}},{"ty":"fl","nm":"Fill","mn":"{39b8d13c-45cf-4ad7-972a-ef5169f1ffbf}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"r":1},{"ty":"tr","a":{"a":0,"k":[110.90149253731343,216.644776119403]},"p":{"a":0,"k":[159.9044776119403,247.59402985074627]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}}]}],"bm":9},{"ddd":0,"ty":4,"ind":0,"st":0,"ip":0,"op":180,"nm":"Layer 1","mn":"{d74c9dcc-e7af-45c3-9eab-554c7b93f6b6}","ks":{"a":{"a":0,"k":[256,256]},"p":{"a":0,"k":[256,256]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}},"shapes":[{"ty":"gr","nm":"Rectangle 1","mn":"{b22ddad1-738b-471a-86eb-1f072fa45799}","it":[{"ty":"rc","nm":"Rectangle 1","mn":"{0c09bd21-59ab-4f1e-bd3d-547613eb3e2a}","p":{"a":0,"k":[241.57611940298506,357.6358208955224]},"s":{"a":0,"k":[383.4268656716418,211.4865671641791]},"r":{"a":0,"k":0}},{"ty":"st","hd":true,"nm":"Stroke","mn":"{8cd4fca9-3480-49fa-947f-04bc40ed74f5}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"lc":2,"lj":2,"ml":0,"w":{"a":0,"k":30}},{"ty":"fl","nm":"Fill","mn":"{050089d8-44c4-4312-8e23-3c89df7615aa}","o":{"a":0,"k":100},"c":{"a":0,"k":[0.7686274509803922,0.8509803921568627,0.9607843137254902]},"r":1},{"ty":"tr","a":{"a":0,"k":[241.57611940298506,357.6358208955224]},"p":{"a":0,"k":[226.1014925373134,131.53432835820894]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","nm":"Rectangle","mn":"{700bfca8-0e45-42e9-8559-15ac0ebe93b2}","it":[{"ty":"rc","nm":"Rectangle","mn":"{0e3ac2ac-22c8-4310-8208-f5d5ba9cd6d9}","p":{"a":0,"k":[277.68358208955226,148.2985074626866]},"s":{"a":0,"k":[335.2835820895522,162.48358208955224]},"r":{"a":0,"k":0}},{"ty":"st","hd":true,"nm":"Stroke","mn":"{af0c691f-7815-414e-a988-ac2eb6e32128}","o":{"a":0,"k":100},"c":{"a":0,"k":[1,0.9803921568627451,0.2823529411764706]},"lc":2,"lj":2,"ml":0,"w":{"a":0,"k":30}},{"ty":"fl","nm":"Fill","mn":"{059d8c4e-de02-4fa7-99fe-c069b73218be}","o":{"a":0,"k":100},"c":{"a":0,"k":[0.19607843137254902,0.3137254901960784,0.6901960784313725]},"r":1},{"ty":"tr","a":{"a":0,"k":[277.68358208955226,148.2985074626866]},"p":{"a":0,"k":[277.68358208955226,366.6626865671642]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100}}]}]}],"meta":{"g":"Glaxnimate 0.4.6-32-gb62899be"}} \ No newline at end of file
diff --git a/snapshot-tests/src/main/assets/Tests/Multiline.json b/snapshot-tests/src/main/assets/Tests/Multiline.json
new file mode 100644
index 00000000..29c79e5a
--- /dev/null
+++ b/snapshot-tests/src/main/assets/Tests/Multiline.json
@@ -0,0 +1 @@
+{"v":"5.9.6","fr":29.9700012207031,"ip":0,"op":180.00000733155,"w":1920,"h":1080,"nm":"Multiline 2","ddd":0,"assets":[],"fonts":{"list":[{"fName":"Helvetica","fFamily":"Helvetica","fStyle":"Regular","ascent":71.8994140625}]},"layers":[{"ddd":0,"ind":1,"ty":5,"nm":"Point","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[44,724,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"t":{"d":{"k":[{"s":{"s":60,"f":"Helvetica","t":"Point","ca":0,"j":0,"tr":0,"lh":89,"ls":0,"fc":[0,0,0]},"t":0}]},"p":{},"m":{"g":1,"a":{"a":0,"k":[0,0],"ix":2}},"a":[]},"ip":0,"op":180.00000733155,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":5,"nm":"Paragraph","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[44,208,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"t":{"d":{"k":[{"s":{"s":60,"f":"Helvetica","t":"Paragraph","ca":0,"j":0,"tr":0,"lh":89,"ls":0,"fc":[0,0,0]},"t":0}]},"p":{},"m":{"g":1,"a":{"a":0,"k":[0,0],"ix":2}},"a":[]},"ip":0,"op":180.00000733155,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":5,"nm":"ABC & DEF 6","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[424,663,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"t":{"d":{"k":[{"s":{"s":119,"f":"Helvetica","t":"ABC\u0003D\rE","ca":0,"j":0,"tr":0,"lh":89,"ls":0,"fc":[0,0,0]},"t":0}]},"p":{},"m":{"g":1,"a":{"a":0,"k":[0,0],"ix":2}},"a":[]},"ip":0,"op":180.00000733155,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Shape Layer 6","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[544,580,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[32.289,84.371,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[885.37,241.53],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[13.837,159.727],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,144.052],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":180.00000733155,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":5,"nm":"ABC & DEF 5","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[1151,663,0],"ix":2,"l":2},"a":{"a":0,"k":[133,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"t":{"d":{"k":[{"s":{"s":119,"f":"Helvetica","t":"ABC\u0003D\rE","ca":0,"j":2,"tr":0,"lh":89,"ls":0,"fc":[0,0,0]},"t":0}]},"p":{},"m":{"g":1,"a":{"a":0,"k":[0,0],"ix":2}},"a":[]},"ip":0,"op":180.00000733155,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"Shape Layer 5","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[1016,580,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[32.289,84.371,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[885.37,241.53],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[13.837,159.727],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,144.052],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":180.00000733155,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":7,"ty":5,"nm":"ABC & DEF 4","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[1751,663,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"t":{"d":{"k":[{"s":{"s":119,"f":"Helvetica","t":"ABC\u0003D\rE","ca":0,"j":1,"tr":0,"lh":89,"ls":0,"fc":[0,0,0]},"t":0}]},"p":{},"m":{"g":1,"a":{"a":0,"k":[0,0],"ix":2}},"a":[]},"ip":0,"op":180.00000733155,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":"Shape Layer 4","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[1616,580,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[32.289,84.371,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[885.37,241.53],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[13.837,159.727],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,144.052],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":180.00000733155,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":9,"ty":5,"nm":"ABC & DEF 3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[1751,151,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"t":{"d":{"k":[{"s":{"sz":[266,339],"ps":[-266,-85.560302734375],"s":119,"f":"Helvetica","t":"ABC\u0003D\rE","ca":0,"j":1,"tr":0,"lh":89,"ls":0,"fc":[0,0,0]},"t":0}]},"p":{},"m":{"g":1,"a":{"a":0,"k":[0,0],"ix":2}},"a":[]},"ip":0,"op":180.00000733155,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":"Shape Layer 3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[1616,68,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[32.289,84.371,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[885.37,241.53],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[13.837,159.727],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,144.052],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":180.00000733155,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":11,"ty":5,"nm":"ABC & DEF 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[1151,151,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"t":{"d":{"k":[{"s":{"sz":[266,339],"ps":[-266,-85.560302734375],"s":119,"f":"Helvetica","t":"ABC\u0003D\rE","ca":0,"j":2,"tr":0,"lh":89,"ls":0,"fc":[0,0,0]},"t":0}]},"p":{},"m":{"g":1,"a":{"a":0,"k":[0,0],"ix":2}},"a":[]},"ip":0,"op":180.00000733155,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":"Shape Layer 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[1016,68,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[32.289,84.371,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[885.37,241.53],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[13.837,159.727],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,144.052],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":180.00000733155,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":13,"ty":5,"nm":"ABC & DEF","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[682,151,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"t":{"d":{"k":[{"s":{"sz":[266,339],"ps":[-266,-85.560302734375],"s":119,"f":"Helvetica","t":"ABC\u0003D\rE","ca":0,"j":0,"tr":0,"lh":89,"ls":0,"fc":[0,0,0]},"t":0}]},"p":{},"m":{"g":1,"a":{"a":0,"k":[0,0],"ix":2}},"a":[]},"ip":0,"op":180.00000733155,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":14,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[544,68,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[32.289,84.371,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[885.37,241.53],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[13.837,159.727],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,144.052],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":180.00000733155,"st":0,"ct":1,"bm":0}],"markers":[],"chars":[{"ch":"A","size":119,"style":"Regular","w":66.7,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[21.973,-29.395],[33.545,-61.084],[44.434,-29.395]],"c":true},"ix":2},"nm":"A","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[1.465,0],[11.426,0],[19.189,-21.484],[47.559,-21.484],[54.834,0],[65.479,0],[39.453,-71.729],[28.467,-71.729]],"c":true},"ix":2},"nm":"A","mn":"ADBE Vector Shape - Group","hd":false}],"nm":"A","np":5,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}]},"fFamily":"Helvetica"},{"ch":"B","size":119,"style":"Regular","w":66.7,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[-2.171,-0.911],[0,-4.688],[3.618,-1.79],[4.144,0]],"o":[[0,0],[0,0],[4.276,0],[3.848,1.628],[0,4.655],[-2.303,1.14],[0,0]],"v":[[16.895,-41.406],[16.895,-63.623],[34.41,-63.623],[44.081,-62.256],[49.854,-52.783],[44.426,-43.115],[34.756,-41.406]],"c":true},"ix":2},"nm":"B","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[-2.498,-1.009],[0,-5.208],[1.61,-2.18],[6.013,0]],"o":[[0,0],[0,0],[4.108,0],[4.699,1.888],[0,3.093],[-2.563,3.451],[0,0]],"v":[[16.895,-8.301],[16.895,-33.545],[36.364,-33.545],[46.272,-32.031],[53.32,-21.387],[50.905,-13.477],[38.04,-8.301]],"c":true},"ix":2},"nm":"B","mn":"ADBE Vector Shape - Group","hd":false},{"ind":2,"ty":"sh","ix":3,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[-4.356,5.599],[0,4.623],[3.223,3.125],[3.678,1.4],[-1.335,1.53],[0,4.525],[2.097,2.962],[8.453,0],[0,0]],"o":[[0,0],[9.498,0],[2.914,-3.743],[0,-5.501],[-1.823,-1.758],[2.506,-1.27],[2.571,-2.897],[0,-3.873],[-3.572,-5.013],[0,0],[0,0]],"v":[[7.373,0],[37.883,0],[58.665,-8.398],[63.037,-20.947],[58.203,-33.887],[49.951,-38.623],[55.713,-42.822],[59.57,-53.955],[56.425,-64.209],[38.387,-71.729],[7.373,-71.729]],"c":true},"ix":2},"nm":"B","mn":"ADBE Vector Shape - Group","hd":false}],"nm":"B","np":6,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}]},"fFamily":"Helvetica"},{"ch":"C","size":119,"style":"Regular","w":72.22,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[8.976,0],[5.984,-7.361],[0,-10.586],[-7.368,-6.677],[-8.237,0],[-5.631,6.152],[-0.837,7.552],[0,0],[1.95,-2.897],[7.035,0],[3.853,5.229],[0,8.406],[-4.268,4.968],[-6.937,0],[-3.149,-2.702],[-1.055,-4.622],[0,0],[4.955,4.785]],"o":[[-10.521,0],[-5.534,6.776],[0,13.942],[5.598,5.049],[9.557,0],[4.697,-5.11],[0,0],[-0.96,4.72],[-3.677,5.502],[-7.642,0],[-3.854,-5.229],[0,-10.262],[4.268,-4.968],[5.69,0],[3.149,2.702],[0,0],[-0.547,-6.087],[-4.955,-4.785]],"v":[[37.453,-73.682],[12.695,-62.64],[4.395,-36.597],[15.447,-5.669],[36.201,1.904],[58.984,-7.324],[67.285,-26.318],[57.812,-26.318],[53.448,-14.893],[37.379,-6.641],[20.135,-14.484],[14.355,-34.936],[20.757,-57.782],[37.564,-65.234],[50.823,-61.182],[57.129,-50.195],[66.602,-50.195],[58.349,-66.504]],"c":true},"ix":2},"nm":"C","mn":"ADBE Vector Shape - Group","hd":false}],"nm":"C","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}]},"fFamily":"Helvetica"},{"ch":"\u0003","size":119,"style":"Regular","w":0,"fFamily":"Helvetica"},{"ch":"D","size":119,"style":"Regular","w":72.22,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[-3.454,-4.915],[0,-8.887],[0.478,-2.702],[1.878,-2.897],[3.692,-1.27],[3.214,0]],"o":[[0,0],[0,0],[7.671,0],[3.453,4.916],[0,2.312],[-0.828,4.525],[-2.356,3.613],[-2.069,0.684],[0,0]],"v":[[17.871,-8.301],[17.871,-63.379],[34.725,-63.379],[51.411,-56.006],[56.592,-35.303],[55.875,-27.783],[51.817,-16.65],[42.746,-9.326],[34.821,-8.301]],"c":true},"ix":2},"nm":"D","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[-4.979,10.873],[0,7.617],[4.786,6.316],[9.734,0],[0,0]],"o":[[0,0],[12.142,0],[2.827,-6.152],[0,-9.863],[-5.365,-6.998],[0,0],[0,0]],"v":[[8.057,0],[36.63,0],[62.312,-16.309],[66.553,-36.963],[59.373,-61.23],[36.726,-71.729],[8.057,-71.729]],"c":true},"ix":2},"nm":"D","mn":"ADBE Vector Shape - Group","hd":false}],"nm":"D","np":5,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}]},"fFamily":"Helvetica"},{"ch":"\r","size":119,"style":"Regular","w":0,"fFamily":"Helvetica"},{"ch":"E","size":119,"style":"Regular","w":66.7,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[8.545,0],[61.328,0],[61.328,-8.545],[18.018,-8.545],[18.018,-32.861],[57.373,-32.861],[57.373,-41.162],[18.018,-41.162],[18.018,-62.939],[60.596,-62.939],[60.596,-71.729],[8.545,-71.729]],"c":true},"ix":2},"nm":"E","mn":"ADBE Vector Shape - Group","hd":false}],"nm":"E","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}]},"fFamily":"Helvetica"},{"ch":"P","size":60,"style":"Regular","w":66.7,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[-3.482,4.15],[0,5.599],[3.902,3.597],[6.345,0],[0,0]],"o":[[0,0],[0,0],[0,0],[7.175,0],[3.481,-4.15],[0,-6.51],[-3.902,-3.596],[0,0],[0,0]],"v":[[8.545,0],[18.262,0],[18.262,-30.322],[40.659,-30.322],[56.643,-36.548],[61.865,-51.172],[56.013,-66.333],[40.643,-71.729],[8.545,-71.729]],"c":true},"ix":2},"nm":"P","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[0,-5.273],[2.699,-1.855],[4.33,0],[0,0],[0,0],[0,0],[-2.133,-1.009]],"o":[[0,4.688],[-2.699,1.855],[0,0],[0,0],[0,0],[3.716,0],[3.878,1.888]],"v":[[52.051,-51.123],[48.003,-41.309],[37.459,-38.525],[18.262,-38.525],[18.262,-63.379],[37.459,-63.379],[46.233,-61.865]],"c":true},"ix":2},"nm":"P","mn":"ADBE Vector Shape - Group","hd":false}],"nm":"P","np":5,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}]},"fFamily":"Helvetica"},{"ch":"a","size":60,"style":"Regular","w":55.62,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,2.533],[-2.718,1.527],[-3.184,0.423],[0,0],[-1.689,0.458],[-1.073,0.686],[0,0],[4.883,-2.373],[3.092,0],[1.855,1.462]],"o":[[0,-3.345],[1.604,-0.909],[0,0],[1.624,-0.195],[1.689,-0.457],[0,0],[0,5.396],[-2.898,1.43],[-2.539,0],[-1.855,-1.462]],"v":[[13.184,-13.897],[17.261,-21.204],[24.443,-23.203],[29.755,-23.885],[34.725,-24.863],[38.867,-26.578],[38.867,-19.509],[31.543,-7.858],[22.559,-5.713],[15.967,-7.905]],"c":true},"ix":2},"nm":"a","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[0,0],[3.027,-2.666],[0,-4.844],[-3.076,-2.942],[-4.851,0],[-3.353,1.724],[-1.791,2.344],[-0.716,-1.139],[-3.027,0],[-0.716,0.098],[-1.367,0.391],[0,0],[0.488,-0.049],[0.391,0],[0.374,0.552],[0,0.912],[0,0],[3.804,2.344],[6.145,0],[4.015,-2.53],[0.163,-6.464],[0,0],[-1.072,1.375],[-4.581,0],[-2.128,-1.356],[0,-3.006],[0.39,-0.784],[2.018,-0.259]],"o":[[-4.916,0.618],[-3.027,2.666],[0,4.422],[3.076,2.942],[4.036,0],[3.352,-1.725],[0.293,2.083],[1.367,2.148],[1.237,0],[0.716,-0.098],[0,0],[-0.554,0.098],[-0.488,0.049],[-1.205,0],[-0.375,-0.552],[0,0],[0,-4.948],[-3.837,-2.344],[-5.299,0],[-4.015,2.53],[0,0],[0.325,-2.715],[1.917,-2.486],[3.963,0],[2.128,1.357],[0,1.471],[-0.684,1.43],[0,0]],"v":[[20.459,-29.893],[8.545,-24.966],[4.004,-13.702],[8.618,-2.656],[20.508,1.758],[31.592,-0.829],[39.307,-6.932],[40.82,-2.099],[47.412,1.123],[50.342,0.977],[53.467,0.244],[53.467,-6.249],[51.904,-6.03],[50.586,-5.957],[48.218,-6.786],[47.656,-8.982],[47.656,-39.111],[41.95,-50.049],[26.978,-53.564],[13.005,-49.77],[6.738,-36.279],[14.941,-36.279],[17.036,-42.413],[26.783,-46.143],[35.919,-44.109],[39.111,-37.565],[38.526,-34.183],[34.473,-31.648]],"c":true},"ix":2},"nm":"a","mn":"ADBE Vector Shape - Group","hd":false}],"nm":"a","np":5,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}]},"fFamily":"Helvetica"},{"ch":"r","size":60,"style":"Regular","w":33.3,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[-2.377,2.849],[-4.427,0],[-0.439,-0.032],[-0.521,-0.098],[0,0],[0.391,0.033],[0.163,0],[2.669,-2.522],[0.684,-1.758],[0,0],[0,0]],"o":[[0,0],[0,0],[0,-3.711],[2.376,-2.848],[0.52,0],[0.439,0.033],[0,0],[-0.945,-0.098],[-0.391,-0.032],[-3.484,0],[-2.67,2.523],[0,0],[0,0],[0,0]],"v":[[6.689,0],[15.479,0],[15.479,-30.078],[19.043,-39.917],[29.248,-44.189],[30.688,-44.141],[32.129,-43.945],[32.129,-53.223],[30.127,-53.418],[29.297,-53.467],[20.068,-49.683],[15.039,-43.262],[15.039,-52.295],[6.689,-52.295]],"c":true},"ix":2},"nm":"r","mn":"ADBE Vector Shape - Group","hd":false}],"nm":"r","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}]},"fFamily":"Helvetica"},{"ch":"g","size":60,"style":"Regular","w":55.62,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[4.086,0],[4.264,-4.915],[0,-8.756],[-4.231,-4.02],[-5.642,0],[-2.464,1.302],[-1.751,2.67],[1.231,-2.8],[6.702,0],[1.974,1.953],[0.42,2.344],[0,0],[-3.828,-2.523],[-5.613,0],[-3.634,7.096],[0,6.641],[0,0],[0,0],[0,0],[1.653,1.14]],"o":[[-6.031,0],[-4.264,4.916],[0,9.277],[4.231,4.021],[4.053,0],[2.464,-1.302],[0.098,7.195],[-2.072,4.752],[-4.241,0],[-1.263,-1.27],[0,0],[0.455,5.371],[3.828,2.522],[10.089,0],[1.946,-3.841],[0,0],[0,0],[0,0],[-1.719,-2.18],[-3.049,-2.018]],"v":[[24.817,-53.223],[9.374,-45.85],[2.979,-25.342],[9.325,-5.396],[24.136,0.635],[33.912,-1.318],[40.234,-7.275],[38.535,7.715],[25.375,14.844],[16.051,11.914],[13.525,6.494],[4.59,6.494],[11.015,18.335],[25.177,22.119],[45.762,11.475],[48.682,-4.248],[48.682,-51.807],[40.576,-51.807],[40.576,-45.215],[35.518,-50.195]],"c":true},"ix":2},"nm":"g","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[0,-6.477],[1.425,-3.223],[6.282,0],[2.38,3.076],[0,5.892],[-1.199,3.093],[-6.154,0],[-2.656,-3.125]],"o":[[0,4.297],[-2.559,5.697],[-4.016,0],[-2.38,-3.076],[0,-5.013],[2.267,-5.794],[4.113,0],[2.655,3.125]],"v":[[40.576,-26.367],[38.438,-15.088],[25.177,-6.543],[15.582,-11.157],[12.012,-24.609],[13.809,-36.768],[26.44,-45.459],[36.593,-40.771]],"c":true},"ix":2},"nm":"g","mn":"ADBE Vector Shape - Group","hd":false}],"nm":"g","np":5,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}]},"fFamily":"Helvetica"},{"ch":"p","size":60,"style":"Regular","w":55.62,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[4.115,0],[2.286,5.404],[0,4.037],[-1.208,3.255],[-6.108,0],[-2.287,-5.762],[0,-4.166],[2.727,-3.434]],"o":[[-6.076,0],[-1.208,-2.832],[0,-5.013],[2.254,-6.087],[6.075,0],[1.208,2.995],[0,6.836],[-2.728,3.435]],"v":[[28.516,-5.908],[15.973,-14.014],[14.16,-24.316],[15.973,-36.719],[28.516,-45.85],[41.058,-37.207],[42.871,-26.465],[38.78,-11.06]],"c":true},"ix":2},"nm":"p","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[-1.569,-0.977],[-3.954,0],[-3.791,3.092],[0,11.394],[4.144,4.541],[5.94,0],[2.969,-1.953],[1.762,-2.376],[0,0],[0,0]],"o":[[0,0],[0,0],[1.928,2.377],[2.679,1.725],[5.032,0],[5.98,-4.883],[0,-8.43],[-4.145,-4.541],[-4.015,0],[-2.089,1.302],[0,0],[0,0],[0,0]],"v":[[5.713,20.85],[14.502,20.85],[14.502,-5.859],[19.747,-0.83],[29.698,1.758],[42.934,-2.881],[51.904,-27.295],[45.687,-46.753],[30.56,-53.564],[20.084,-50.635],[14.307,-45.117],[14.307,-52.051],[5.713,-52.051]],"c":true},"ix":2},"nm":"p","mn":"ADBE Vector Shape - Group","hd":false}],"nm":"p","np":5,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}]},"fFamily":"Helvetica"},{"ch":"h","size":60,"style":"Regular","w":55.62,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[-2.962,2.507],[-3.646,0],[-1.595,-2.864],[0,-3.841],[0,0],[0,0],[0,0],[1.465,2.898],[7.584,0],[2.832,-1.855],[2.083,-2.637],[0,0],[0,0]],"o":[[0,0],[0,0],[0,-6.966],[2.962,-2.506],[4.395,0],[0.977,1.791],[0,0],[0,0],[0,0],[0,-5.143],[-2.702,-5.305],[-4.232,0],[-1.66,1.074],[0,0],[0,0],[0,0]],"v":[[6.445,0],[15.234,0],[15.234,-27.734],[19.678,-41.943],[29.59,-45.703],[38.574,-41.406],[40.039,-32.959],[40.039,0],[49.072,0],[49.072,-33.545],[46.875,-45.605],[31.445,-53.564],[20.85,-50.781],[15.234,-45.215],[15.234,-71.973],[6.445,-71.973]],"c":true},"ix":2},"nm":"h","mn":"ADBE Vector Shape - Group","hd":false}],"nm":"h","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}]},"fFamily":"Helvetica"},{"ch":"o","size":60,"style":"Regular","w":55.62,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[5.761,0],[2.414,3.706],[0,5.56],[-2.414,4.097],[-5.312,0],[-2.446,-4.812],[0,-4.877],[2.141,-4.405]],"o":[[-5.247,0],[-2.414,-3.706],[0,-5.787],[2.414,-4.097],[5.987,0],[1.545,3.056],[0,5.397],[-2.141,4.405]],"v":[[27.026,-5.713],[15.535,-11.272],[11.914,-25.172],[15.535,-39.997],[27.122,-46.143],[39.772,-38.924],[42.09,-27.025],[38.879,-12.321]],"c":true},"ix":2},"nm":"o","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[6.691,0],[4.428,-5.203],[0,-8.781],[-4.202,-4.862],[-7.144,0],[-4.073,5.496],[0,8.424],[4.622,4.488]],"o":[[-7.467,0],[-4.428,5.204],[0,8.196],[4.202,4.862],[8.566,0],[4.073,-5.496],[0,-8.716],[-4.623,-4.488]],"v":[[27.366,-53.809],[9.523,-46.003],[2.881,-25.025],[9.184,-5.437],[26.202,1.855],[45.16,-6.389],[51.27,-27.27],[44.336,-47.076]],"c":true},"ix":2},"nm":"o","mn":"ADBE Vector Shape - Group","hd":false}],"nm":"o","np":5,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}]},"fFamily":"Helvetica"},{"ch":"i","size":60,"style":"Regular","w":22.22,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[6.445,0],[15.381,0],[15.381,-52.051],[6.445,-52.051]],"c":true},"ix":2},"nm":"i","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[6.445,-61.768],[15.381,-61.768],[15.381,-71.729],[6.445,-71.729]],"c":true},"ix":2},"nm":"i","mn":"ADBE Vector Shape - Group","hd":false}],"nm":"i","np":5,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}]},"fFamily":"Helvetica"},{"ch":"n","size":60,"style":"Regular","w":55.62,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[-0.439,1.839],[-1.465,1.726],[-2.409,0.716],[-2.084,0],[-1.562,-3.255],[0,-3.19],[0,0],[0,0],[0,0],[1.432,2.832],[7.422,0],[2.766,-1.334],[2.473,-3.059],[0,0],[0,0]],"o":[[0,0],[0,0],[0,-3.418],[0.439,-1.839],[1.823,-2.148],[1.334,-0.423],[4.102,0],[0.944,1.953],[0,0],[0,0],[0,0],[0,-5.273],[-2.605,-5.176],[-3.386,0],[-2.767,1.335],[0,0],[0,0],[0,0]],"v":[[6.445,0],[15.234,0],[15.234,-27.393],[15.894,-35.278],[18.75,-40.625],[25.098,-44.922],[30.225,-45.557],[38.721,-40.674],[40.137,-32.959],[40.137,0],[49.072,0],[49.072,-33.545],[46.924,-45.703],[31.885,-53.467],[22.656,-51.465],[14.795,-44.873],[14.795,-52.295],[6.445,-52.295]],"c":true},"ix":2},"nm":"n","mn":"ADBE Vector Shape - Group","hd":false}],"nm":"n","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}]},"fFamily":"Helvetica"},{"ch":"t","size":60,"style":"Regular","w":27.78,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[-1.465,-2.1],[-4.102,0],[-1.091,0.13],[-1.009,0.293],[0,0],[0.488,-0.021],[0.423,0],[0.684,0.359],[0,1.823],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,3.353],[1.465,2.1],[1.27,0],[1.09,-0.13],[0,0],[-0.652,0.087],[-0.488,0.021],[-1.595,0],[-1.237,-0.618],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[8.203,-52.295],[1.123,-52.295],[1.123,-45.117],[8.203,-45.117],[8.203,-10.498],[10.4,-2.319],[18.75,0.83],[22.29,0.635],[25.439,0],[25.439,-6.982],[23.73,-6.819],[22.363,-6.787],[18.945,-7.324],[17.09,-10.986],[17.09,-45.117],[25.439,-45.117],[25.439,-52.295],[17.09,-52.295],[17.09,-66.895],[8.203,-66.895]],"c":true},"ix":2},"nm":"t","mn":"ADBE Vector Shape - Group","hd":false}],"nm":"t","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}]},"fFamily":"Helvetica"}]} \ No newline at end of file
diff --git a/snapshot-tests/src/main/assets/Tests/NullEndShape.json b/snapshot-tests/src/main/assets/Tests/NullEndShape.json
new file mode 100644
index 00000000..222f1d28
--- /dev/null
+++ b/snapshot-tests/src/main/assets/Tests/NullEndShape.json
@@ -0,0 +1 @@
+{"nm":"ai_look_down_start","v":"5.9.6","fr":60,"ip":0,"op":17,"w":32,"h":32,"ddd":0,"markers":[],"assets":[{"nm":"[GROUP] Vector - Null / Vector / Vector - Null / Vector / Vector - Null / Vector / Vector - Null / Vector","fr":60,"id":"llz7qmoazd7b8l6ls5","layers":[{"ty":3,"ddd":0,"ind":11,"hd":false,"nm":"AI Avatar - Null","ks":{"a":{"a":0,"k":[0,0]},"o":{"a":0,"k":100},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}},"st":0,"ip":0,"op":18,"bm":0,"sr":1},{"ty":3,"ddd":0,"ind":12,"hd":false,"nm":"Dots - Null","parent":11,"ks":{"a":{"a":0,"k":[8,8]},"o":{"a":0,"k":100},"p":{"a":0,"k":[16,16]},"r":{"a":0,"k":-315},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}},"st":0,"ip":0,"op":18,"bm":0,"sr":1},{"ty":3,"ddd":0,"ind":13,"hd":false,"nm":"Vector - Null","parent":12,"ks":{"a":{"a":0,"k":[2,2]},"o":{"a":0,"k":100},"p":{"a":1,"k":[{"t":0,"s":[4,10],"o":{"x":[0.5],"y":[0.35]},"i":{"x":[0.15],"y":[1]},"ti":[0,0],"to":[0,0]},{"t":18,"s":[7,11]}]},"r":{"a":0,"k":-135},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}},"st":0,"ip":0,"op":18,"bm":0,"sr":1},{"ty":4,"ddd":0,"ind":14,"hd":false,"nm":"Vector","parent":13,"ks":{"a":{"a":0,"k":[0,0]},"p":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"r":{"a":0,"k":0},"o":{"a":0,"k":100}},"st":0,"ip":0,"op":18,"bm":0,"sr":1,"shapes":[{"ty":"gr","nm":"Group","hd":false,"np":3,"it":[{"ty":"sh","nm":"Path","hd":false,"ks":{"a":1,"k":[{"t":0,"s":[{"c":true,"v":[[-0.75,0.75],[4.75,0.75],[6,2],[6,2],[4.75,3.25],[-0.75,3.25],[-2,2],[-2,2],[-0.75,0.75],[-0.75,0.75]],"i":[[0,0],[0,0],[0,-0.6904],[0,0],[0.6904,0],[0,0],[0,0.6904],[0,0],[-0.6904,0],[0,0]],"o":[[0,0],[0.6903600000000001,0],[0,0],[0,0.6903600000000001],[0,0],[-0.6903600000000001,0],[0,0],[0,-0.6903600000000001],[0,0],[0,0]]}]}]}},{"ty":"fl","o":{"a":0,"k":100},"c":{"a":0,"k":[0.5529411764705883,0.6705882352941176,1,1]},"nm":"Fill","hd":false,"r":1},{"ty":"tr","a":{"a":0,"k":[0,0]},"p":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"r":{"a":0,"k":0},"o":{"a":0,"k":100}}]}]},{"ty":3,"ddd":0,"ind":15,"hd":false,"nm":"Vector - Null","parent":12,"ks":{"a":{"a":0,"k":[2,2]},"o":{"a":0,"k":100},"p":{"a":1,"k":[{"t":0,"s":[10,4],"o":{"x":[0.5],"y":[0.35]},"i":{"x":[0.15],"y":[1]},"ti":[0,0],"to":[0,0]},{"t":18,"s":[13,5]}]},"r":{"a":0,"k":-135},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}},"st":0,"ip":0,"op":18,"bm":0,"sr":1},{"ty":4,"ddd":0,"ind":16,"hd":false,"nm":"Vector","parent":15,"ks":{"a":{"a":0,"k":[0,0]},"p":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"r":{"a":0,"k":0},"o":{"a":0,"k":100}},"st":0,"ip":0,"op":18,"bm":0,"sr":1,"shapes":[{"ty":"gr","nm":"Group","hd":false,"np":3,"it":[{"ty":"sh","nm":"Path","hd":false,"ks":{"a":1,"k":[{"t":0,"s":[{"c":true,"v":[[-0.75,0.75],[4.75,0.75],[6,2],[6,2],[4.75,3.25],[-0.75,3.25],[-2,2],[-2,2],[-0.75,0.75],[-0.75,0.75]],"i":[[0,0],[0,0],[0,-0.6904],[0,0],[0.6904,0],[0,0],[0,0.6904],[0,0],[-0.6904,0],[0,0]],"o":[[0,0],[0.6903600000000001,0],[0,0],[0,0.6903600000000001],[0,0],[-0.6903600000000001,0],[0,0],[0,-0.6903600000000001],[0,0],[0,0]]}]}]}},{"ty":"fl","o":{"a":0,"k":100},"c":{"a":0,"k":[0.5529411764705883,0.6705882352941176,1,1]},"nm":"Fill","hd":false,"r":1},{"ty":"tr","a":{"a":0,"k":[0,0]},"p":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"r":{"a":0,"k":0},"o":{"a":0,"k":100}}]}]},{"ty":3,"ddd":0,"ind":17,"hd":false,"nm":"Vector - Null","parent":12,"ks":{"a":{"a":0,"k":[2,2]},"o":{"a":0,"k":0},"p":{"a":0,"k":[11,5]},"r":{"a":0,"k":0},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}},"st":0,"ip":0,"op":18,"bm":0,"sr":1},{"ty":4,"ddd":0,"ind":18,"hd":false,"nm":"Vector","parent":17,"ks":{"a":{"a":0,"k":[0,0]},"p":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"r":{"a":0,"k":0},"o":{"a":0,"k":0}},"st":0,"ip":0,"op":18,"bm":0,"sr":1,"shapes":[{"ty":"gr","nm":"Group","hd":false,"np":3,"it":[{"ty":"sh","nm":"Path","hd":false,"ks":{"a":0,"k":{"c":true,"v":[[2,0],[2,0],[4,2],[4,2],[2,4],[2,4],[0,2],[0,2],[2,0],[2,0]],"i":[[0,0],[0,0],[0,-1.1046],[0,0],[1.1046,0],[0,0],[0,1.1046],[0,0],[-1.1046,0],[0,0]],"o":[[0,0],[1.1045699999999998,0],[0,0],[0,1.1045699999999998],[0,0],[-1.10457,0],[0,0],[0,-1.10457],[0,0],[0,0]]}}},{"ty":"fl","o":{"a":0,"k":100},"c":{"a":0,"k":[0.5529411764705883,0.6705882352941176,1,1]},"nm":"Fill","hd":false,"r":1},{"ty":"tr","a":{"a":0,"k":[0,0]},"p":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"r":{"a":0,"k":0},"o":{"a":0,"k":100}}]}]},{"ty":3,"ddd":0,"ind":19,"hd":false,"nm":"Vector - Null","parent":12,"ks":{"a":{"a":0,"k":[2,2]},"o":{"a":0,"k":0},"p":{"a":0,"k":[5,11]},"r":{"a":0,"k":0},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}},"st":0,"ip":0,"op":18,"bm":0,"sr":1},{"ty":4,"ddd":0,"ind":20,"hd":false,"nm":"Vector","parent":19,"ks":{"a":{"a":0,"k":[0,0]},"p":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"r":{"a":0,"k":0},"o":{"a":0,"k":0}},"st":0,"ip":0,"op":18,"bm":0,"sr":1,"shapes":[{"ty":"gr","nm":"Group","hd":false,"np":3,"it":[{"ty":"sh","nm":"Path","hd":false,"ks":{"a":0,"k":{"c":true,"v":[[2,0],[2,0],[4,2],[4,2],[2,4],[2,4],[0,2],[0,2],[2,0],[2,0]],"i":[[0,0],[0,0],[0,-1.1046],[0,0],[1.1046,0],[0,0],[0,1.1046],[0,0],[-1.1046,0],[0,0]],"o":[[0,0],[1.1045699999999998,0],[0,0],[0,1.1045699999999998],[0,0],[-1.10457,0],[0,0],[0,-1.10457],[0,0],[0,0]]}}},{"ty":"fl","o":{"a":0,"k":100},"c":{"a":0,"k":[0.5529411764705883,0.6705882352941176,1,1]},"nm":"Fill","hd":false,"r":1},{"ty":"tr","a":{"a":0,"k":[0,0]},"p":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"r":{"a":0,"k":0},"o":{"a":0,"k":100}}]}]}]},{"nm":"[FRAME] AI Avatar - Null / Dots / Vector - Null / Vector - Stroke / Vector","fr":60,"id":"llz7qmo9y32v2utmt4","layers":[{"ty":3,"ddd":0,"ind":21,"hd":false,"nm":"AI Avatar - Null","ks":{"a":{"a":0,"k":[0,0]},"o":{"a":0,"k":100},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}},"st":0,"ip":0,"op":18,"bm":0,"sr":1},{"ddd":0,"ind":22,"ty":0,"nm":"Dots","refId":"llz7qmoazd7b8l6ls5","sr":1,"ks":{"a":{"a":0,"k":[0,0]},"p":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"r":{"a":0,"k":0},"o":{"a":0,"k":100}},"ao":0,"w":32,"h":32,"ip":0,"op":18,"st":0,"hd":false,"bm":0},{"ty":3,"ddd":0,"ind":23,"hd":false,"nm":"Vector - Null","parent":21,"ks":{"a":{"a":0,"k":[0,0]},"o":{"a":0,"k":100},"p":{"a":0,"k":[2,2]},"r":{"a":0,"k":0},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}},"st":0,"ip":0,"op":18,"bm":0,"sr":1},{"ddd":0,"ind":24,"hd":false,"nm":"Vector - Stroke","parent":23,"ks":{"a":{"a":0,"k":[0,0]},"p":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"r":{"a":0,"k":0},"o":{"a":0,"k":100}},"st":0,"ip":0,"op":18,"bm":0,"sr":1,"ty":4,"shapes":[{"ty":"gr","nm":"Group","hd":false,"np":3,"it":[{"ty":"sh","nm":"Path","hd":false,"ks":{"a":0,"k":{"c":true,"v":[[28,14],[14,28],[0,14],[14,0],[28,14],[28,14]],"i":[[0,0],[7.7322,0],[0,7.7322],[-7.7322,0],[0,-7.7322],[0,0]],"o":[[0,7.732199999999999],[-7.7322,0],[0,-7.7322],[7.732199999999999,0],[0,0],[0,0]]}}},{"ty":"gs","o":{"a":0,"k":70},"w":{"a":0,"k":4},"g":{"p":2,"k":{"a":0,"k":[0,0.4627450980392157,0.8196078431372549,0.9882352941176471,1,0.5803921568627451,0.4470588235294118,1,0,1,1,1]}},"s":{"a":0,"k":[27.99999972364,70.000000442176]},"e":{"a":0,"k":[-2.763599979971332e-7,70]},"t":1,"lc":1,"lj":1,"ml":0,"nm":"Stroke","hd":false},{"ty":"tr","a":{"a":0,"k":[0,0]},"p":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"r":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","nm":"Group","hd":false,"np":3,"it":[{"ty":"rc","nm":"Rectangle","hd":false,"p":{"a":0,"k":[15,15]},"s":{"a":0,"k":[60,60]},"r":{"a":0,"k":0}},{"ty":"fl","o":{"a":0,"k":0},"c":{"a":0,"k":[0,1,0,1]},"nm":"Fill","hd":false,"r":1},{"ty":"tr","a":{"a":0,"k":[0,0]},"p":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"r":{"a":0,"k":0},"o":{"a":0,"k":100}}]}],"hasMask":true,"masksProperties":[{"nm":"Mask","pt":{"a":0,"k":{"c":true,"v":[[28,14],[14,28],[0,14],[14,0],[28,14],[28,14]],"i":[[0,0],[7.7322,0],[0,7.7322],[-7.7322,0],[0,-7.7322],[0,0]],"o":[[0,7.732199999999999],[-7.7322,0],[0,-7.7322],[7.732199999999999,0],[0,0],[0,0]]}},"o":{"a":0,"k":100},"mode":"s","x":{"a":0,"k":0}}]},{"ty":4,"ddd":0,"ind":25,"hd":false,"nm":"Vector","parent":23,"ks":{"a":{"a":0,"k":[0,0]},"p":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"r":{"a":0,"k":0},"o":{"a":0,"k":100}},"st":0,"ip":0,"op":18,"bm":0,"sr":1,"shapes":[{"ty":"gr","nm":"Group","hd":false,"np":3,"it":[{"ty":"sh","nm":"Path","hd":false,"ks":{"a":0,"k":{"c":true,"v":[[28,14],[14,28],[0,14],[14,0],[28,14],[28,14]],"i":[[0,0],[7.7322,0],[0,7.7322],[-7.7322,0],[0,-7.7322],[0,0]],"o":[[0,7.732199999999999],[-7.7322,0],[0,-7.7322],[7.732199999999999,0],[0,0],[0,0]]}}},{"ty":"gf","o":{"a":0,"k":10},"g":{"p":2,"k":{"a":0,"k":[0,0.4,0.5686274509803921,1,1,0.5803921568627451,0.4470588235294118,1,0,1,1,0]}},"s":{"a":0,"k":[872.3859635372996,-0.00013163579396459075]},"e":{"a":0,"k":[872.3859635372996,27.999868364206037]},"t":1,"nm":"Fill","hd":false,"r":1},{"ty":"tr","a":{"a":0,"k":[0,0]},"p":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"r":{"a":0,"k":0},"o":{"a":0,"k":100}}]}]}]},{"nm":"ai_look_down_start","fr":60,"id":"llz7qmo8zxzrmf6qtjq","layers":[{"ty":3,"ddd":0,"ind":26,"hd":false,"nm":"ai_look_down_start - Null","ks":{"a":{"a":0,"k":[0,0]},"o":{"a":0,"k":100},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}},"st":0,"ip":0,"op":18,"bm":0,"sr":1},{"ddd":0,"ind":27,"ty":0,"nm":"AI Avatar","refId":"llz7qmo9y32v2utmt4","sr":1,"ks":{"a":{"a":0,"k":[0,0]},"p":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"r":{"a":0,"k":0},"o":{"a":0,"k":100}},"ao":0,"w":32,"h":32,"ip":0,"op":18,"st":0,"hd":false,"bm":0}]}],"layers":[{"ty":3,"ddd":0,"ind":26,"hd":false,"nm":"ai_look_down_start - Null","ks":{"a":{"a":0,"k":[0,0]},"o":{"a":0,"k":100},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0}},"st":0,"ip":0,"op":18,"bm":0,"sr":1},{"ddd":0,"ind":2,"ty":0,"nm":"ai_look_down_start","refId":"llz7qmo8zxzrmf6qtjq","sr":1,"ks":{"a":{"a":0,"k":[0,0]},"p":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"r":{"a":0,"k":0},"o":{"a":0,"k":100}},"ao":0,"w":32,"h":32,"ip":0,"op":18,"st":0,"hd":false,"bm":0}],"meta":{"a":"","d":"","tc":"","g":"Aninix"}}
diff --git a/snapshot-tests/src/main/assets/Tests/Polygon.json b/snapshot-tests/src/main/assets/Tests/Polygon.json
new file mode 100644
index 00000000..9173daa5
--- /dev/null
+++ b/snapshot-tests/src/main/assets/Tests/Polygon.json
@@ -0,0 +1 @@
+{"v":"5.12.2","fr":29.9700012207031,"ip":0,"op":900.000036657751,"w":400,"h":400,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[394,201,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[0,-86.977],[25.562,-35.183],[82.72,-26.877],[41.36,13.439],[51.124,70.365],[0,43.488],[-51.124,70.365],[-41.36,13.439],[-82.72,-26.877],[-25.562,-35.183]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-106.035,-97.711],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Polystar 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":900.000036657751,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[200,200,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[0,-86.977],[25.562,-35.183],[82.72,-26.877],[41.36,13.439],[51.124,70.365],[0,43.488],[-51.124,70.365],[-41.36,13.439],[-82.72,-26.877],[-25.562,-35.183]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-106.035,-97.711],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Polystar 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":900.000036657751,"st":0,"ct":1,"bm":0}],"markers":[],"props":{}} \ No newline at end of file
diff --git a/snapshot-tests/src/main/assets/Tests/PrecompBlurDecimapPrecompSize.json b/snapshot-tests/src/main/assets/Tests/PrecompBlurDecimapPrecompSize.json
new file mode 100644
index 00000000..4052e178
--- /dev/null
+++ b/snapshot-tests/src/main/assets/Tests/PrecompBlurDecimapPrecompSize.json
@@ -0,0 +1 @@
+{"v":"5.7.7","fr":29.9700012207031,"ip":0,"op":178.000007250089,"w":375,"h":375,"nm":"LottieLogoPrecomp","ddd":0,"assets":[{"id":"comp_0","layers":[{"ddd":0,"ind":1,"ty":1,"nm":"MASTER","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[214.457,347.822,0],"ix":2,"l":2},"a":{"a":0,"k":[60,60,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"sw":120,"sh":120,"sc":"#ffffff","ip":11.9880004882813,"op":178.821007283529,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"S5-Y 4","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":-89.1,"ix":10},"p":{"a":0,"k":[53.205,131.606,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[142.038,29.278],[131.282,21.807]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":75.924,"s":[87]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":78.921,"s":[50.633]},{"t":82.9170033772786,"s":[0]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":75.924,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":78.921,"s":[75.856]},{"t":82.9170033772786,"s":[0]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":75.9240030924479,"op":83.9160034179687,"st":39.9600016276042,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"S4-Y 4","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":-89.1,"ix":10},"p":{"a":0,"k":[53.205,131.606,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[142.183,-5.112],[130.029,5.016]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":75.924,"s":[87]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":78.921,"s":[43.833]},{"t":82.9170033772786,"s":[0]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":75.924,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":78.921,"s":[66.356]},{"t":82.9170033772786,"s":[0]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":75.9240030924479,"op":83.9160034179687,"st":39.9600016276042,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"S3-Y 4","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":-89.1,"ix":10},"p":{"a":0,"k":[53.205,131.606,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[147.699,13.025],[133.195,13.21]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":75.924,"s":[87]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":78.921,"s":[42.133]},{"t":82.9170033772786,"s":[0]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":75.924,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":78.921,"s":[66.356]},{"t":82.9170033772786,"s":[0]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":75.9240030924479,"op":83.9160034179687,"st":39.9600016276042,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"S5-Y 3","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":97.9,"ix":10},"p":{"a":0,"k":[58.205,-39.394,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[145.677,22.22],[134.922,14.749]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":74.925,"s":[87]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":77.922,"s":[50.633]},{"t":81.9180033365885,"s":[0]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":74.925,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":77.922,"s":[75.856]},{"t":81.9180033365885,"s":[0]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":74.9250030517578,"op":82.9170033772786,"st":38.9610015869141,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"S4-Y 3","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":97.9,"ix":10},"p":{"a":0,"k":[58.205,-39.394,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[144.429,-5.397],[132.275,4.731]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":74.925,"s":[87]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":77.922,"s":[43.833]},{"t":81.9180033365885,"s":[0]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":74.925,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":77.922,"s":[66.356]},{"t":81.9180033365885,"s":[0]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":74.9250030517578,"op":82.9170033772786,"st":38.9610015869141,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":"S3-Y 3","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":97.9,"ix":10},"p":{"a":0,"k":[58.205,-39.394,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[149.624,8.244],[136.648,10.156]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":74.925,"s":[87]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":77.922,"s":[42.133]},{"t":81.9180033365885,"s":[0]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":74.925,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":77.922,"s":[66.356]},{"t":81.9180033365885,"s":[0]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":74.9250030517578,"op":82.9170033772786,"st":38.9610015869141,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":"S13","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.043,45.678,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[128,3.65],[78.25,3.5]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":84.915,"s":[87]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":89.91,"s":[21.233]},{"t":93.9060038248698,"s":[0]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":84.915,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":89.91,"s":[66.356]},{"t":93.9060038248698,"s":[0]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1.5,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":84.9150034586589,"op":94.9050038655599,"st":48.9510019938151,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":"S12","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.043,45.678,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[119.25,-20.05],[63.5,-20.5]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":83.916,"s":[87]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":86.913,"s":[21.233]},{"t":90.9090037027995,"s":[0]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":83.916,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":86.913,"s":[66.356]},{"t":90.9090037027995,"s":[0]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1.5,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":83.9160034179687,"op":93.9060038248698,"st":47.952001953125,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":"S11","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.043,45.678,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[119.5,-45.05],[82.75,-44.75]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":79.92,"s":[87]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":82.917,"s":[21.233]},{"t":86.9130035400391,"s":[0]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":79.92,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":82.917,"s":[66.356]},{"t":86.9130035400391,"s":[0]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1.5,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":79.9200032552083,"op":89.9100036621094,"st":43.9560017903646,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":"S5-Y 2","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.043,45.678,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[169.5,18.073],[137.481,11.365]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":96.903,"s":[87]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":99.9,"s":[50.633]},{"t":106.893004353841,"s":[0]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":96.903,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":99.9,"s":[75.856]},{"t":106.893004353841,"s":[0]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":96.9030039469401,"op":106.893004353841,"st":60.9390024820963,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":"S4-Y 2","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.043,45.678,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[156.45,-23.05],[132,2.75]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":96.903,"s":[87]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":99.9,"s":[43.833]},{"t":106.893004353841,"s":[0]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":96.903,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":99.9,"s":[66.356]},{"t":106.893004353841,"s":[0]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":96.9030039469401,"op":106.893004353841,"st":60.9390024820963,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":"S3-Y 2","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.043,45.678,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[166.731,-7.927],[136.731,7.115]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":96.903,"s":[87]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":99.9,"s":[42.133]},{"t":106.893004353841,"s":[0]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":96.903,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":99.9,"s":[66.356]},{"t":106.893004353841,"s":[0]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":96.9030039469401,"op":106.893004353841,"st":60.9390024820963,"bm":0},{"ddd":0,"ind":14,"ty":4,"nm":"S6-Y","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.043,45.678,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-87.5,20.95],[-48.75,54.75]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":53.946,"s":[87]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":56.943,"s":[43.933]},{"t":63.9360026041667,"s":[0]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":53.946,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":56.943,"s":[70.456]},{"t":63.9360026041667,"s":[0]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":53.9460021972656,"op":63.9360026041667,"st":17.9820007324219,"bm":0},{"ddd":0,"ind":15,"ty":4,"nm":"S5-Y","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.043,45.678,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-94.5,37.073],[-48.769,55.365]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":53.946,"s":[87]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":56.943,"s":[50.633]},{"t":63.9360026041667,"s":[0]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":53.946,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":56.943,"s":[75.856]},{"t":63.9360026041667,"s":[0]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":53.9460021972656,"op":63.9360026041667,"st":17.9820007324219,"bm":0},{"ddd":0,"ind":16,"ty":4,"nm":"S4-Y","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.043,45.678,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[7.45,21.95],[-32.75,55.75]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":53.946,"s":[87]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":56.943,"s":[43.833]},{"t":63.9360026041667,"s":[0]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":53.946,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":56.943,"s":[66.356]},{"t":63.9360026041667,"s":[0]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":53.9460021972656,"op":63.9360026041667,"st":17.9820007324219,"bm":0},{"ddd":0,"ind":17,"ty":4,"nm":"S3-Y","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.043,45.678,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[16.231,39.073],[-32.769,57.365]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":53.946,"s":[87]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":56.943,"s":[42.133]},{"t":63.9360026041667,"s":[0]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":53.946,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":56.943,"s":[66.356]},{"t":63.9360026041667,"s":[0]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":53.9460021972656,"op":63.9360026041667,"st":17.9820007324219,"bm":0},{"ddd":0,"ind":18,"ty":4,"nm":"S8","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.043,45.678,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-0.148,14.256],[10.476,0],[0,0]],"o":[[0,0],[-8.551,-8.263],[-21.454,0],[0,0]],"v":[[-3,35.95],[-1.352,-6.756],[-32.046,-20.579],[-42.25,4.25]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":64.935,"s":[87]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":69.93,"s":[21.233]},{"t":74.9250030517578,"s":[0]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":64.935,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":69.93,"s":[66.356]},{"t":74.9250030517578,"s":[0]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1.5,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":64.9350026448568,"op":74.9250030517578,"st":28.971001180013,"bm":0},{"ddd":0,"ind":19,"ty":4,"nm":"S7","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.043,45.678,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[27,1.45],[31.046,-1.421],[0,0]],"o":[[-27,-1.45],[-26.426,1.21],[0,0]],"v":[[34.5,-13.05],[-35.046,-35.579],[-62.25,-5.75]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":64.935,"s":[87]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":69.93,"s":[21.233]},{"t":74.9250030517578,"s":[0]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":64.935,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":69.93,"s":[66.356]},{"t":74.9250030517578,"s":[0]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1.5,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":64.9350026448568,"op":74.9250030517578,"st":28.971001180013,"bm":0},{"ddd":0,"ind":20,"ty":4,"nm":"S2-Y","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.043,45.678,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[1.9,-10.768],[1,-19]],"o":[[0,0],[-3.167,17.951],[-1,19]],"v":[[-67.25,-105.5],[-72.333,-84.201],[-76.5,-37.75]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":28.971,"s":[87]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":32.967,"s":[25.333]},{"t":35.9640014648437,"s":[0]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":28.971,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":32.967,"s":[69.056]},{"t":35.9640014648437,"s":[0]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1.5,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":29.9700012207031,"op":36.9630015055339,"st":-6.99300028483073,"bm":0},{"ddd":0,"ind":21,"ty":4,"nm":"S1-Y","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.043,45.678,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[1.9,-10.768],[1,-19]],"o":[[0,0],[-3.167,17.951],[-1,19]],"v":[[-67.125,-112],[-75.458,-89.951],[-80.375,-39.25]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":28.971,"s":[87]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":32.967,"s":[37.533]},{"t":35.9640014648437,"s":[0]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":28.971,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":32.967,"s":[66.356]},{"t":35.9640014648437,"s":[0]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1.5,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":29.9700012207031,"op":36.9630015055339,"st":-6.99300028483073,"bm":0},{"ddd":0,"ind":22,"ty":4,"nm":"Dot1","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.823,"y":0},"t":-2.997,"s":[295.771,108.994,0],"to":[0,0,0],"ti":[0,0,0]},{"t":15.9840006510417,"s":[35.771,108.994,0]}],"ix":2,"l":2},"a":{"a":0,"k":[196.791,266.504,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[9.4,9.4],"ix":2},"p":{"a":0,"k":[0.8,-0.5],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[196,267],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-4.99500020345052,"op":16.9830006917318,"st":-35.9640014648437,"bm":0},{"ddd":0,"ind":23,"ty":4,"nm":"L-B","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[39.043,45.678,0],"ix":2,"l":2},"a":{"a":0,"k":[250,250,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[25.671,-4.167],[1.456,6.902],[-8.481,1.863],[-47.562,13.01],[-0.501,0.133],[-71.423,-2.315]],"o":[[0,0],[-8.224,1.335],[-1.456,-6.903],[23.817,-5.233],[0.16,-0.044],[0.501,-0.133],[0,0]],"v":[[-8.837,-58.229],[-35.834,33.662],[-51.688,23.148],[-41.174,7.293],[51.797,44.178],[53.188,43.741],[140.394,43.672]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.477999985218,0.528999984264,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":9.194,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[166.029,270.643],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 8","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.703],"y":[0.821]},"o":{"x":[0.167],"y":[0.167]},"t":17.982,"s":[80]},{"i":{"x":[0.263],"y":[1]},"o":{"x":[0.037],"y":[0.168]},"t":22.977,"s":[50]},{"t":54.9450022379557,"s":[30]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.337],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":17.982,"s":[81]},{"t":28.971001180013,"s":[73.4]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":17.9820007324219,"op":178.821007283529,"st":7.99200032552083,"bm":0},{"ddd":0,"ind":24,"ty":4,"nm":"L-Y","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[39.043,45.678,0],"ix":2,"l":2},"a":{"a":0,"k":[250,250,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[25.671,-4.167],[1.456,6.902],[-8.481,1.863],[-47.562,13.01],[-0.501,0.133],[-71.423,-2.315]],"o":[[0,0],[-8.224,1.335],[-1.456,-6.903],[23.817,-5.233],[0.16,-0.044],[0.501,-0.133],[0,0]],"v":[[-8.837,-58.229],[-35.834,33.662],[-51.688,23.148],[-41.174,7.293],[51.797,44.178],[53.188,43.741],[140.394,43.672]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":8.4,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[166.029,270.643],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 8","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.703],"y":[0.857]},"o":{"x":[0.167],"y":[0.167]},"t":15.984,"s":[80]},{"i":{"x":[0.938],"y":[1]},"o":{"x":[0.333],"y":[0.202]},"t":19.98,"s":[50]},{"t":27.9720011393229,"s":[0]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.337],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":15.984,"s":[81]},{"t":26.9730010986328,"s":[73.4]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":15.9840006510417,"op":178.821007283529,"st":7.99200032552083,"bm":0},{"ddd":0,"ind":25,"ty":1,"nm":"N","parent":1,"sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.26,"y":1},"o":{"x":0.167,"y":0.167},"t":27.972,"s":[-33.667,8.182,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.74,"y":0},"t":39.96,"s":[-33.667,-72.818,0],"to":[0,0,0],"ti":[0,0,0]},{"t":53.9460021972656,"s":[-33.667,102.057,0]}],"ix":2,"l":2},"a":{"a":0,"k":[60,60,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"sw":120,"sh":120,"sc":"#ffffff","ip":27.9720011393229,"op":53.9460021972656,"st":0,"bm":0},{"ddd":0,"ind":26,"ty":4,"nm":"Dot-Y","parent":25,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":27.972,"s":[39.875,60,0],"to":[6.583,0,0],"ti":[-6.583,0,0]},{"t":53.9460021972656,"s":[79.375,60,0]}],"ix":2,"l":2},"a":{"a":0,"k":[196.791,266.504,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[9.4,9.4],"ix":2},"p":{"a":0,"k":[0.8,-0.5],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[196,267],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":27.9720011393229,"op":53.9460021972656,"st":3.99600016276042,"bm":0},{"ddd":0,"ind":27,"ty":4,"nm":"T1a-B","parent":37,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[250,250,0],"ix":2,"l":2},"a":{"a":0,"k":[250,250,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.5,9.501],[-0.048,5.655],[0.054,0.06],[0.946,1.486],[-9.967,8.05],[-40.546,0]],"o":[[0.031,-0.594],[0.076,-8.978],[-1.161,-1.3],[-5.939,-9.327],[24.677,-19.929],[0,0]],"v":[[-30.72,63.761],[-30.741,45.192],[-37.397,27.014],[-40.698,22.661],[-37.873,-7.117],[49.506,11.559]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":0,"k":24.9,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.673],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":69.93,"s":[24.9]},{"t":83.9160034179687,"s":[89.1]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":9.194,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[227.677,234.375],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 9","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":69.9300028483073,"op":178.821007283529,"st":16.9830006917318,"bm":0},{"ddd":0,"ind":28,"ty":4,"nm":"T2a-B","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[39.043,45.678,0],"ix":2,"l":2},"a":{"a":0,"k":[250,250,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[1.681,-29.992],[-1.681,29.992]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.06],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":74.925,"s":[50]},{"t":84.9150034586589,"s":[0]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.06],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":74.925,"s":[50]},{"t":84.9150034586589,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.477999985218,0.528999984264,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":9.194,"ix":5},"lc":3,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[277.698,247.258],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 7","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":74.9250030517578,"op":178.821007283529,"st":14.9850006103516,"bm":0},{"ddd":0,"ind":29,"ty":4,"nm":"T1a-Y 2","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"t":55.944,"s":[39.043,48.678,0],"to":[0,0,0],"ti":[0,0,0]},{"t":63.9360026041667,"s":[39.043,45.678,0]}],"ix":2,"l":2},"a":{"a":0,"k":[250,250,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.5,9.501],[-0.048,5.655],[0.054,0.06],[0.946,1.486],[-9.967,8.05],[-40.546,0]],"o":[[0.031,-0.594],[0.076,-8.978],[-1.161,-1.3],[-5.939,-9.327],[24.677,-19.929],[0,0]],"v":[[-30.72,63.761],[-30.741,45.192],[-37.397,27.014],[-40.698,22.661],[-37.873,-7.117],[49.506,11.559]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.301],"y":[0]},"t":53.946,"s":[0]},{"t":69.9300028483073,"s":[24.9]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.301],"y":[0]},"t":53.946,"s":[0]},{"t":77.9220031738281,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.477999985218,0.528999984264,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":8.4,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[227.677,234.375],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 9","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":58.9410024007161,"op":178.821007283529,"st":11.9880004882813,"bm":0},{"ddd":0,"ind":30,"ty":4,"nm":"O-B","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":30.969,"s":[-62.792,73.057,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.638,"y":1},"o":{"x":0.167,"y":0.198},"t":35.222,"s":[-53.792,7.557,0],"to":[0,0,0],"ti":[-19.156,1.738,0]},{"i":{"x":0.795,"y":1},"o":{"x":0.523,"y":0},"t":43.956,"s":[-33.667,-72.818,0],"to":[16.208,-1.471,0],"ti":[0,0,0]},{"i":{"x":0.348,"y":1},"o":{"x":0.18,"y":0},"t":53.946,"s":[-14.167,102.182,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.27,"y":1},"o":{"x":0.693,"y":0},"t":62.937,"s":[-14.167,59.182,0],"to":[0,0,0],"ti":[0,0,0]},{"t":72.9270029703776,"s":[-14.167,62.182,0]}],"ix":2,"l":2},"a":{"a":0,"k":[196.791,266.504,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.667,0.667],"y":[1,1]},"o":{"x":[0.333,0.333],"y":[0,0]},"t":53.946,"s":[3,3]},{"t":60.9390024820963,"s":[44.6,44.6]}],"ix":2},"p":{"a":0,"k":[0.8,-0.5],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.477999985218,0.528999984264,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":9.194,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[196,267],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":53.946,"s":[0]},{"i":{"x":[0.432],"y":[1]},"o":{"x":[0.167],"y":[1.124]},"t":62.937,"s":[30]},{"t":90.9090037027995,"s":[39.9]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":53.946,"s":[100]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":62.937,"s":[88]},{"t":90.9090037027995,"s":[88]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":53.9460021972656,"op":178.821007283529,"st":3.99600016276042,"bm":0},{"ddd":0,"ind":31,"ty":4,"nm":"O-Y","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":30.969,"s":[-62.792,73.057,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.638,"y":1},"o":{"x":0.167,"y":0.198},"t":35.222,"s":[-53.792,7.557,0],"to":[0,0,0],"ti":[-19.156,1.738,0]},{"i":{"x":0.795,"y":1},"o":{"x":0.523,"y":0},"t":43.956,"s":[-33.667,-72.818,0],"to":[16.208,-1.471,0],"ti":[0,0,0]},{"i":{"x":0.348,"y":1},"o":{"x":0.18,"y":0},"t":53.946,"s":[-14.167,102.182,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.27,"y":1},"o":{"x":0.693,"y":0},"t":62.937,"s":[-14.167,59.182,0],"to":[0,0,0],"ti":[0,0,0]},{"t":72.9270029703776,"s":[-14.167,62.182,0]}],"ix":2,"l":2},"a":{"a":0,"k":[196.791,266.504,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.667,0.667],"y":[1,1]},"o":{"x":[0.333,0.333],"y":[0,0]},"t":53.946,"s":[3,3]},{"t":60.9390024820963,"s":[44.6,44.6]}],"ix":2},"p":{"a":0,"k":[0.8,-0.5],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":8.8,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[196,267],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":53.9460021972656,"op":178.821007283529,"st":3.99600016276042,"bm":0},{"ddd":0,"ind":32,"ty":4,"nm":"T1b-B","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[39.043,45.678,0],"ix":2,"l":2},"a":{"a":0,"k":[250,250,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[1.768,-25.966],[-1.768,25.966]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.21],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":80.919,"s":[11.7]},{"t":87.9120035807292,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":9.194,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[242.756,265.581],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 10","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":80.9190032958984,"op":178.821007283529,"st":25.9740010579427,"bm":0},{"ddd":0,"ind":33,"ty":4,"nm":"T1b-Y","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[39.043,45.678,0],"ix":2,"l":2},"a":{"a":0,"k":[250,250,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[1.768,-25.966],[-1.768,25.966]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":69.93,"s":[0]},{"t":74.9250030517578,"s":[0]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":69.93,"s":[11.7]},{"t":74.9250030517578,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.477999985218,0.528999984264,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":8.4,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[242.756,265.581],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 10","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":69.9300028483073,"op":160.839006551107,"st":14.9850006103516,"bm":0},{"ddd":0,"ind":34,"ty":4,"nm":"T2b-B","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[39.043,45.678,0],"ix":2,"l":2},"a":{"a":0,"k":[250,250,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[246.65,213.814],[340.956,213.628]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.12],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":81.918,"s":[29]},{"t":90.9090037027995,"s":[0]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.12],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":81.918,"s":[41.1]},{"t":90.9090037027995,"s":[66.5]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.477999985218,0.528999984264,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":9.194,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 5","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":81.9180033365885,"op":178.821007283529,"st":-16.9830006917318,"bm":0},{"ddd":0,"ind":35,"ty":4,"nm":"T2a-Y","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[39.043,45.678,0],"ix":2,"l":2},"a":{"a":0,"k":[250,250,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[1.681,-29.992],[-1.681,29.992]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.06],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":71.928,"s":[50]},{"t":81.9180033365885,"s":[0]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.06],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":71.928,"s":[50]},{"t":81.9180033365885,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":9.194,"ix":5},"lc":3,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[277.698,247.258],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 7","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":71.9280029296875,"op":88.9110036214193,"st":11.9880004882813,"bm":0},{"ddd":0,"ind":36,"ty":4,"nm":"T2b-Y","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[39.043,45.678,0],"ix":2,"l":2},"a":{"a":0,"k":[250,250,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[246.65,213.814],[340.956,213.628]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.12],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":75.924,"s":[29]},{"t":84.9150034586589,"s":[0]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.12],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":75.924,"s":[41.1]},{"t":84.9150034586589,"s":[66.5]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":9.194,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 5","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":75.9240030924479,"op":91.9080037434896,"st":-22.9770009358724,"bm":0},{"ddd":0,"ind":37,"ty":4,"nm":"T1a-Y","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"t":55.944,"s":[39.043,48.678,0],"to":[0,0,0],"ti":[0,0,0]},{"t":63.9360026041667,"s":[39.043,45.678,0]}],"ix":2,"l":2},"a":{"a":0,"k":[250,250,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.5,9.501],[-0.048,5.655],[0.054,0.06],[0.946,1.486],[-9.967,8.05],[-40.546,0]],"o":[[0.031,-0.594],[0.076,-8.978],[-1.161,-1.3],[-5.939,-9.327],[24.677,-19.929],[0,0]],"v":[[-30.72,63.761],[-30.741,45.192],[-37.397,27.014],[-40.698,22.661],[-37.873,-7.117],[49.506,11.559]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.301],"y":[0]},"t":53.946,"s":[0]},{"t":69.9300028483073,"s":[24.9]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.301],"y":[0]},"t":53.946,"s":[0]},{"t":73.9260030110677,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":8.4,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[227.677,234.375],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 9","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":58.9410024007161,"op":155.844006347656,"st":11.9880004882813,"bm":0},{"ddd":0,"ind":38,"ty":4,"nm":"E1-B","parent":39,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[344.672,214.842,0],"ix":2,"l":2},"a":{"a":0,"k":[344.672,214.842,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-13.664,-0.145],[62.163,0.29]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":9.562,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[344.672,214.842],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.12],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":83.916,"s":[0]},{"t":92.9070037841797,"s":[0]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.12],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":83.916,"s":[0]},{"t":92.9070037841797,"s":[37.5]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":83.9160034179687,"op":178.821007283529,"st":83.9160034179687,"bm":0},{"ddd":0,"ind":39,"ty":4,"nm":"E1-Y","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.12,"y":1},"o":{"x":0.167,"y":0.167},"t":78.921,"s":[113.715,9.146,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.12,"y":1},"o":{"x":0.167,"y":0},"t":87.912,"s":[137.715,9.146,0],"to":[0,0,0],"ti":[0,0,0]},{"t":91.9080037434896,"s":[133.715,9.146,0]}],"ix":2,"l":2},"a":{"a":0,"k":[344.672,214.842,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-13.664,-0.145],[62.163,0.29]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.477999985218,0.528999984264,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":8.4,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[344.672,214.842],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.12],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":78.921,"s":[0]},{"t":87.9120035807292,"s":[0]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.12],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":78.921,"s":[0]},{"t":87.9120035807292,"s":[37.5]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":78.9210032145182,"op":93.9060038248698,"st":78.9210032145182,"bm":0},{"ddd":0,"ind":40,"ty":4,"nm":"E2-B","parent":41,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[332.05,237.932,0],"ix":2,"l":2},"a":{"a":0,"k":[332.05,237.932,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-26.67,-0.283],[99.171,0.066]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.12],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":85.914,"s":[0]},{"t":94.9050038655599,"s":[0]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.12],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":85.914,"s":[0]},{"t":94.9050038655599,"s":[43]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":9.562,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[331.664,238.14],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 3","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":85.914003499349,"op":178.821007283529,"st":85.914003499349,"bm":0},{"ddd":0,"ind":41,"ty":4,"nm":"E2-Y","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.12,"y":1},"o":{"x":0.167,"y":0.167},"t":82.917,"s":[109.092,33.61,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.12,"y":0.12},"o":{"x":0.167,"y":0.167},"t":91.908,"s":[121.092,33.61,0],"to":[0,0,0],"ti":[0,0,0]},{"t":95.90400390625,"s":[121.092,33.61,0]}],"ix":2,"l":2},"a":{"a":0,"k":[332.05,237.932,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-26.67,-0.283],[99.171,0.066]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.12],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":82.917,"s":[0]},{"t":91.9080037434896,"s":[0]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.12],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":82.917,"s":[0]},{"t":91.9080037434896,"s":[43]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.477999985218,0.528999984264,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":8.4,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[331.664,238.14],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 3","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":82.9170033772786,"op":95.90400390625,"st":82.9170033772786,"bm":0},{"ddd":0,"ind":42,"ty":4,"nm":"I-B","parent":43,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[303.802,282.182,0],"ix":2,"l":2},"a":{"a":0,"k":[303.802,282.182,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0.859,-21.143],[-4.359,70.392]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.12],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":80.919,"s":[0]},{"t":90.9090037027995,"s":[0]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.12],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":80.919,"s":[0]},{"t":90.9090037027995,"s":[45.7]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":9.194,"ix":5},"lc":3,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[304.135,282.409],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 6","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":80.9190032958984,"op":178.821007283529,"st":17.9820007324219,"bm":0},{"ddd":0,"ind":43,"ty":4,"nm":"I-Y","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.12,"y":1},"o":{"x":0.167,"y":0.167},"t":77.922,"s":[93.594,62.861,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.12,"y":1},"o":{"x":0.167,"y":0},"t":87.912,"s":[92.626,82.829,0],"to":[0,0,0],"ti":[0,0,0]},{"t":91.9080037434896,"s":[92.844,77.861,0]}],"ix":2,"l":2},"a":{"a":0,"k":[303.802,282.182,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0.859,-21.143],[-4.359,70.392]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.12],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":77.922,"s":[0]},{"t":87.9120035807292,"s":[0]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.12],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":77.922,"s":[0]},{"t":87.9120035807292,"s":[45.7]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.477999985218,0.528999984264,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":8.4,"ix":5},"lc":3,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[304.135,282.409],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 6","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":77.9220031738281,"op":92.9070037841797,"st":14.9850006103516,"bm":0},{"ddd":0,"ind":44,"ty":4,"nm":"E3-B","parent":45,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[345.189,261.801,0],"ix":2,"l":2},"a":{"a":0,"k":[345.124,261.801,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-13.664,-0.145],[75.663,0.29]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":91.908,"s":[0]},{"t":96.9030039469401,"s":[0]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":91.908,"s":[0]},{"t":96.9030039469401,"s":[31.6]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 2","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":9.562,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[344.674,261.877],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":91.9080037434896,"op":178.821007283529,"st":28.971001180013,"bm":0},{"ddd":0,"ind":45,"ty":4,"nm":"E3-Y","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"t":83.916,"s":[119.167,57.479,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0},"t":91.908,"s":[137.167,57.479,0],"to":[0,0,0],"ti":[0,0,0]},{"t":95.90400390625,"s":[134.167,57.479,0]}],"ix":2,"l":2},"a":{"a":0,"k":[345.124,261.801,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-13.664,-0.145],[75.663,0.29]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":83.916,"s":[0]},{"t":91.9080037434896,"s":[0]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":83.916,"s":[0]},{"t":91.9080037434896,"s":[31.6]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 2","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.477999985218,0.528999984264,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":9.562,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[344.674,261.877],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":83.9160034179687,"op":101.898004150391,"st":20.9790008544922,"bm":0},{"ddd":0,"ind":46,"ty":4,"nm":"Dot-Y","parent":47,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0,"y":0.812},"o":{"x":0,"y":0},"t":95.904,"s":[43.263,59.75,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.708,"y":1},"o":{"x":0.39,"y":0.707},"t":107.892,"s":[62.513,59.75,0],"to":[0,0,0],"ti":[0,0,0]},{"t":114.885004679362,"s":[63.763,59.75,0]}],"ix":2,"l":2},"a":{"a":0,"k":[196.791,266.504,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[9.2,9.2],"ix":2},"p":{"a":0,"k":[0.8,-0.5],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[196,267],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":95.90400390625,"op":181.818007405599,"st":64.9350026448568,"bm":0},{"ddd":0,"ind":47,"ty":1,"nm":"Bncr","parent":1,"sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.18,"y":1},"o":{"x":0.167,"y":0.167},"t":95.904,"s":[164.782,57.473,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.82,"y":0},"t":98.901,"s":[164.782,55.473,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.18,"y":1},"o":{"x":0.167,"y":0.167},"t":101.898,"s":[164.782,57.473,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.82,"y":0},"t":104.895,"s":[164.782,56.909,0],"to":[0,0,0],"ti":[0,0,0]},{"t":107.892004394531,"s":[164.782,57.473,0]}],"ix":2,"l":2},"a":{"a":0,"k":[60,60,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"sw":120,"sh":120,"sc":"#ffffff","ip":95.90400390625,"op":181.818007405599,"st":14.9850006103516,"bm":0},{"ddd":0,"ind":48,"ty":4,"nm":"BG","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[187.5,333.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[375,667],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0.819999992847,0.757000029087,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":178.821007283529,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":0,"nm":"LottieLogo1","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[187.5,187.5,0],"ix":2,"l":2},"a":{"a":0,"k":[187.5,333.5,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ef":[{"ty":29,"nm":"Gaussian Blur","np":5,"mn":"ADBE Gaussian Blur 2","ix":1,"en":1,"ef":[{"ty":0,"nm":"Blurriness","mn":"ADBE Gaussian Blur 2-0001","ix":1,"v":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":96,"s":[0]},{"t":119.000004846969,"s":[19.8]}],"ix":1}},{"ty":7,"nm":"Blur Dimensions","mn":"ADBE Gaussian Blur 2-0002","ix":2,"v":{"a":0,"k":1,"ix":2}},{"ty":7,"nm":"Repeat Edge Pixels","mn":"ADBE Gaussian Blur 2-0003","ix":3,"v":{"a":0,"k":0,"ix":3}}]}],"w":375.0,"h":667.0,"ip":0,"op":178.000007250089,"st":0,"bm":0}],"markers":[]} \ No newline at end of file
diff --git a/snapshot-tests/src/main/assets/Tests/ReducedMotion.json b/snapshot-tests/src/main/assets/Tests/ReducedMotion.json
new file mode 100644
index 00000000..cd595f6a
--- /dev/null
+++ b/snapshot-tests/src/main/assets/Tests/ReducedMotion.json
@@ -0,0 +1 @@
+{"v":"5.9.6","fr":60,"ip":0,"op":145,"w":376,"h":326,"nm":"Error - Memory full - Lottie","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Vector","parent":4,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.818],"y":[0.726]},"o":{"x":[0.493],"y":[0.299]},"t":84,"s":[-162.078]},{"i":{"x":[0.997],"y":[1]},"o":{"x":[0.679],"y":[0.579]},"t":95,"s":[-176.03]},{"i":{"x":[0.198],"y":[1]},"o":{"x":[0.401],"y":[0]},"t":97,"s":[-180.5]},{"i":{"x":[0.198],"y":[1]},"o":{"x":[0.401],"y":[0]},"t":102,"s":[-179]},{"t":107,"s":[-180]}],"ix":10},"p":{"a":0,"k":[4.392,17.228,0],"ix":2,"l":2},"a":{"a":0,"k":[28.646,37.522,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.1,"y":0.9},"t":76,"s":[{"i":[[2.375,0],[0,0],[0,-2.375],[0,0],[-2.375,0],[0,0],[0,2.375],[0,0]],"o":[[0,0],[-2.375,0],[0,0],[0,2.375],[0,0],[2.375,0],[0,0],[0,-2.375]],"v":[[20.155,-4.305],[20.005,-4.437],[15.705,-0.137],[15.705,-0.127],[20.005,4.173],[20.155,4.305],[24.455,0.005],[24.455,-0.005]],"c":true}]},{"t":106,"s":[{"i":[[2.375,0],[0,0],[0,-2.375],[0,0],[-2.375,0],[0,0],[0,2.375],[0,0]],"o":[[0,0],[-2.375,0],[0,0],[0,2.375],[0,0],[2.375,0],[0,0],[0,-2.375]],"v":[[20.155,-4.305],[-20.155,-4.305],[-24.455,-0.005],[-24.455,0.005],[-20.155,4.305],[20.155,4.305],[24.455,0.005],[24.455,-0.005]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.133333340287,0.133333340287,0.133333340287,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.921568632126,0.921568632126,0.921568632126,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":47.86,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Vector","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":76,"op":145,"st":74,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Big Circle","parent":6,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.46,"y":1},"o":{"x":0.17,"y":0.59},"t":68,"s":[-63.425,-73.81,0],"to":[-0.767,0.193,0],"ti":[0.767,-0.193,0]},{"t":105,"s":[-68.024,-72.649,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.1,"y":0.9},"t":68,"s":[{"i":[[-1.854,0],[0,1.854],[1.854,0],[0,-1.854]],"o":[[1.854,0],[0,-1.854],[-1.854,0],[0,1.854]],"v":[[8.588,20.414],[11.944,17.058],[8.588,13.701],[5.232,17.058]],"c":true}]},{"t":128,"s":[{"i":[[-12.034,0],[0,12.034],[12.034,0],[0,-12.034]],"o":[[12.034,0],[0,-12.034],[-12.034,0],[0,12.034]],"v":[[0,21.79],[21.79,0],[0,-21.79],[-21.79,0]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.133333340287,0.133333340287,0.133333340287,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.97000002861,0.97000002861,0.97000002861,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Vector","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":68,"op":145,"st":68,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Vector","parent":5,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.999],"y":[1]},"o":{"x":[0.72],"y":[0]},"t":67,"s":[11]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.4],"y":[0]},"t":97,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.4],"y":[0]},"t":102,"s":[1]},{"t":107,"s":[0]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.55,"y":1},"o":{"x":0.23,"y":0.59},"t":67,"s":[-8.887,-17.522,0],"to":[0,-0.75,0],"ti":[0,0.75,0]},{"t":92,"s":[-8.887,-22.022,0]}],"ix":2,"l":2},"a":{"a":0,"k":[11.5,45,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.1,"y":0.9},"t":67,"s":[{"i":[[0,0],[0,0],[0,-0.48],[0,0],[0,0]],"o":[[0,0],[-0.48,0],[0,0],[0,0],[0,0]],"v":[[8.667,20.675],[-8.793,20.675],[-9.663,21.545],[-9.165,23.245],[9.165,23.245]],"c":true}]},{"t":127,"s":[{"i":[[0,0],[0,0],[0,-0.48],[0,0],[0,0]],"o":[[0,0],[-0.48,0],[0,0],[0,0],[0,0]],"v":[[9.165,-23.245],[-8.295,-23.245],[-9.165,-22.375],[-9.165,23.245],[9.165,23.245]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.133333340287,0.133333340287,0.133333340287,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.97000002861,0.97000002861,0.97000002861,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":-23.42,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Vector","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":67,"op":145,"st":67,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Semi Circle","parent":6,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.999],"y":[1]},"o":{"x":[0.723],"y":[0]},"t":57,"s":[-25]},{"i":{"x":[0.198],"y":[1]},"o":{"x":[0.401],"y":[0]},"t":97,"s":[0]},{"i":{"x":[0.198],"y":[1]},"o":{"x":[0.401],"y":[0]},"t":102,"s":[-1]},{"t":107,"s":[0]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.999,"y":1},"o":{"x":0.723,"y":0},"t":57,"s":[-5.58,-20.451,0],"to":[1.275,0.744,0],"ti":[-1.275,-0.744,0]},{"t":97,"s":[2.072,-15.984,0]}],"ix":2,"l":2},"a":{"a":0,"k":[21.5,-15.5,0],"ix":1,"l":2},"s":{"a":0,"k":[-100,-100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.1,"y":0.9},"t":57,"s":[{"i":[[1.505,0],[0,0],[0,0],[0,-1.505],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[-1.507,0],[0,0],[0,0],[0,0],[0,-1.507]],"v":[[14.411,2.606],[13.663,2.606],[13.665,2.606],[10.935,5.336],[10.935,5.71],[17.14,5.71],[17.14,5.336]],"c":true}]},{"t":97,"s":[{"i":[[7.93,0],[0,0],[0,0],[0,-7.93],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[-7.94,0],[0,0],[0,0],[0,0],[0,-7.94]],"v":[[1.965,-8.175],[-1.975,-8.175],[-1.965,-8.175],[-16.345,6.205],[-16.345,8.175],[16.345,8.175],[16.345,6.205]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.133333340287,0.133333340287,0.133333340287,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.921568632126,0.921568632126,0.921568632126,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":-23.42,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Vector","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":57,"op":145,"st":57,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Circle","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.005,"y":0.003},"t":52,"s":[274.28,141.44,0],"to":[0,-1.583,0],"ti":[0,1.583,0]},{"t":92,"s":[274.28,131.94,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.1,"y":0.9},"t":52,"s":[{"i":[[-1.149,0],[0,1.149],[1.149,0],[0,-1.149]],"o":[[1.149,0],[0,-1.149],[-1.149,0],[0,1.149]],"v":[[-3.435,11.02],[-1.355,8.94],[-3.435,6.86],[-5.515,8.94]],"c":true}]},{"t":112,"s":[{"i":[[-6.5,0],[0,6.5],[6.5,0],[0,-6.5]],"o":[[6.5,0],[0,-6.5],[-6.5,0],[0,6.5]],"v":[[0,11.77],[11.77,0],[0,-11.77],[-11.77,0]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.133333340287,0.133333340287,0.133333340287,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.921568632126,0.921568632126,0.921568632126,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Vector","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":52,"op":145,"st":52,"ct":1,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"Rectangle","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0],"y":[0]},"t":45,"s":[-15]},{"i":{"x":[0.646],"y":[0.73]},"o":{"x":[0.304],"y":[0]},"t":48,"s":[-12]},{"i":{"x":[0.839],"y":[1]},"o":{"x":[0.397],"y":[0.389]},"t":50,"s":[-12.9]},{"i":{"x":[0.086],"y":[1]},"o":{"x":[0.124],"y":[0.414]},"t":56,"s":[-15]},{"t":105,"s":[0]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.073,"y":0.788},"o":{"x":0,"y":0},"t":37,"s":[250.784,192.669,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.617,"y":0.711},"o":{"x":0.286,"y":0.29},"t":46,"s":[243.239,174.792,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.637,"y":1},"o":{"x":0.305,"y":0.309},"t":52,"s":[241.765,171.306,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.613,"y":0.704},"o":{"x":0.358,"y":0.112},"t":57,"s":[240.784,169.169,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.377,"y":1},"o":{"x":0.143,"y":0.622},"t":65,"s":[239.332,166.678,0],"to":[0,0,0],"ti":[0,0,0]},{"t":105,"s":[237.784,164.669,0]}],"ix":2,"l":2},"a":{"a":0,"k":[48,32,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.198,"y":1},"o":{"x":0.542,"y":0},"t":37,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[30.366,-2.391],[28.303,-2.709],[28.303,12.011],[30.366,12.329]],"c":true}]},{"i":{"x":0.58,"y":1},"o":{"x":0.26,"y":0.61},"t":57,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[28.26,-7.36],[-25.519,-7.58],[-25.519,7.14],[28.26,7.36]],"c":true}]},{"t":77,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[28.26,-7.36],[-28.26,-7.36],[-28.26,7.36],[28.26,7.36]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.133333340287,0.133333340287,0.133333340287,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.921568632126,0.921568632126,0.921568632126,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":19.58,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Vector","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":37,"op":145,"st":57,"ct":1,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":"Square","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":0.2},"o":{"x":0.17,"y":0.17},"t":45,"s":[112.14,147.395,0],"to":[0,0,0],"ti":[0,0,0]},{"t":77,"s":[112.14,147.395,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.1,"y":0.9},"t":45,"s":[{"i":[[0.065,-0.025],[0,0],[-0.025,-0.065],[0,0],[-0.065,0.025],[0,0],[0.025,0.065],[0,0]],"o":[[0,0],[-0.065,0.025],[0,0],[0.025,0.065],[0,0],[0.065,-0.025],[0,0],[-0.025,-0.065]],"v":[[-15.75,10.513],[-19.622,12.039],[-19.693,12.202],[-18.257,15.844],[-18.094,15.915],[-14.222,14.389],[-14.151,14.225],[-15.586,10.584]],"c":true}]},{"t":105,"s":[{"i":[[0.392,0],[0,0],[0,-0.392],[0,0],[-0.392,0],[0,0],[0,0.392],[0,0]],"o":[[0,0],[-0.392,0],[0,0],[0,0.392],[0,0],[0.392,0],[0,0],[0,-0.392]],"v":[[11.75,-11.76],[-11.75,-11.76],[-12.46,-11.05],[-12.46,11.05],[-11.75,11.76],[11.75,11.76],[12.46,11.05],[12.46,-11.05]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.133333340287,0.133333340287,0.133333340287,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.921568632126,0.921568632126,0.921568632126,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":-23.42,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Vector","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":45,"op":145,"st":45,"ct":1,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":"Hexagon","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.17,"y":0.17},"t":32,"s":[245.76,202.169,0],"to":[2.172,-1.407,0],"ti":[-2.172,1.407,0]},{"t":77,"s":[258.79,193.73,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.1,"y":0.9},"t":32,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[6.272,7.99],[9.751,6.483],[12.795,8.743],[12.361,12.508],[8.881,14.015],[5.838,11.755]],"c":true}]},{"t":92,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-15.575,-11.555],[2.225,-19.265],[17.795,-7.705],[15.575,11.555],[-2.225,19.265],[-17.795,7.705]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.133333340287,0.133333340287,0.133333340287,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.97000002861,0.97000002861,0.97000002861,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Vector","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":32,"op":145,"st":32,"ct":1,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":"Pill","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.198],"y":[1]},"o":{"x":[0.401],"y":[0]},"t":22,"s":[45]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.17],"y":[0.17]},"t":32,"s":[18]},{"t":77,"s":[0]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.198,"y":1},"o":{"x":0.401,"y":0},"t":22,"s":[188.994,298.768,0],"to":[0,1.667,0],"ti":[-2.5,-2.833,0]},{"i":{"x":0.2,"y":1},"o":{"x":0.17,"y":0.169},"t":32,"s":[188.994,308.768,0],"to":[2.5,2.833,0],"ti":[-2.5,-1.167,0]},{"t":77,"s":[203.994,315.768,0]}],"ix":2,"l":2},"a":{"a":0,"k":[-47.274,47.274,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.198,"y":1},"o":{"x":0.401,"y":0},"t":17,"s":[{"i":[[4.103,0],[0,0],[0,-4.103],[0,0],[-4.103,0],[0,0],[0,4.103],[0,0]],"o":[[0,0],[-4.103,0],[0,0],[0,4.103],[0,0],[4.103,0],[0,0],[0,-4.103]],"v":[[0.069,14.565],[-0.261,14.565],[-7.691,21.995],[-7.595,22.755],[-0.165,30.185],[0.165,30.185],[7.595,22.755],[7.499,21.995]],"c":true}]},{"t":32,"s":[{"i":[[4.103,0],[0,0],[0,-4.103],[0,0],[-4.103,0],[0,0],[0,4.103],[0,0]],"o":[[0,0],[-4.103,0],[0,0],[0,4.103],[0,0],[4.103,0],[0,0],[0,-4.103]],"v":[[0.165,-30.185],[-0.165,-30.185],[-7.595,-22.755],[-7.595,22.755],[-0.165,30.185],[0.165,30.185],[7.595,22.755],[7.595,-22.755]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.133333340287,0.133333340287,0.133333340287,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.921568632126,0.921568632126,0.921568632126,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":45,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Vector","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":19,"op":145,"st":17,"ct":1,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":"Large circle","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[153.44,237.24,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.1,"y":0.9},"t":17,"s":[{"i":[[-1.977,0],[0,1.977],[1.977,0],[0,-1.977]],"o":[[1.977,0],[0,-1.977],[-1.977,0],[0,1.977]],"v":[[-28.8,32.13],[-25.22,28.55],[-28.8,24.97],[-32.38,28.55]],"c":true}]},{"t":77,"s":[{"i":[[-20.782,0],[0,20.782],[20.782,0],[0,-20.782]],"o":[[20.782,0],[0,-20.782],[-20.782,0],[0,20.782]],"v":[[0,37.63],[37.63,0],[0,-37.63],[-37.63,0]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.133333340287,0.133333340287,0.133333340287,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.97000002861,0.97000002861,0.97000002861,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[200,200],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Vector","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":18,"op":145,"st":17,"ct":1,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":"Device","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[188,163,0],"ix":2,"l":2},"a":{"a":0,"k":[11.621,265.965,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[240,320],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":48,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.133333333333,0.133333333333,0.133333333333,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[11.621,265.965],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":145,"st":0,"ct":1,"bm":0}],"markers":[{"tm":18,"cm":"","dr":0},{"tm":143,"cm":"ReducedMotion","dr":1}],"metadata":{"customProps":{"ReducedMotion":1}}} \ No newline at end of file
diff --git a/snapshot-tests/src/main/assets/Tests/RoundedNonClosed.json b/snapshot-tests/src/main/assets/Tests/RoundedNonClosed.json
new file mode 100644
index 00000000..dfa149fd
--- /dev/null
+++ b/snapshot-tests/src/main/assets/Tests/RoundedNonClosed.json
@@ -0,0 +1 @@
+{"v":"5.12.1","fr":60,"ip":0,"op":301,"w":850,"h":850,"nm":"Learn-icon-circle","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Path 3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[327,475.038,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[-1800,1800,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-14.672,-13.86],[1.815,-3.469],[5.3,11.535]],"o":[[0,0],[7.167,6.77],[-5.891,11.262],[-2.639,-5.743]],"v":[[-23.844,-20.05],[12.222,-18.383],[14.169,9.792],[-21.578,9.407]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"rd","nm":"Round Corners 1","r":{"a":0,"k":358.2,"ix":1},"ix":2,"mn":"ADBE Vector Filter - RC","hd":false}],"ip":0,"op":3600,"st":0,"ct":1,"bm":0}],"markers":[],"props":{}} \ No newline at end of file
diff --git a/snapshot-tests/src/main/assets/Tests/RoundedWithAlreadyRoundedCorners.json b/snapshot-tests/src/main/assets/Tests/RoundedWithAlreadyRoundedCorners.json
new file mode 100644
index 00000000..46dfd078
--- /dev/null
+++ b/snapshot-tests/src/main/assets/Tests/RoundedWithAlreadyRoundedCorners.json
@@ -0,0 +1,386 @@
+{
+ "v": "5.9.1",
+ "fr": 60,
+ "ip": 0,
+ "op": 56,
+ "w": 1080,
+ "h": 1080,
+ "nm": "Warning",
+ "ddd": 0,
+ "assets": [
+ {
+ "id": "comp_0",
+ "nm": "Warning_2",
+ "fr": 60,
+ "layers": [
+ {
+ "ddd": 0,
+ "ind": 3,
+ "ty": 4,
+ "nm": "CERCHIO contorni",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 11
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 10
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 24.5,
+ 24.5,
+ 0
+ ],
+ "ix": 2,
+ "l": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 29.5,
+ 29.5,
+ 0
+ ],
+ "ix": 1,
+ "l": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100,
+ 100
+ ],
+ "ix": 6,
+ "l": 2
+ }
+ },
+ "ao": 0,
+ "shapes": [
+ {
+ "ty": "gr",
+ "it": [
+ {
+ "ind": 0,
+ "ty": "sh",
+ "ix": 1,
+ "ks": {
+ "a": 0,
+ "k": {
+ "i": [
+ [
+ -12.15,
+ 0
+ ],
+ [
+ 0,
+ -12.15
+ ],
+ [
+ 12.15,
+ 0
+ ],
+ [
+ 0,
+ 12.15
+ ]
+ ],
+ "o": [
+ [
+ 12.15,
+ 0
+ ],
+ [
+ 0,
+ 12.15
+ ],
+ [
+ -12.15,
+ 0
+ ],
+ [
+ 0,
+ -12.15
+ ]
+ ],
+ "v": [
+ [
+ 0,
+ -22
+ ],
+ [
+ 22,
+ 0
+ ],
+ [
+ 0,
+ 22
+ ],
+ [
+ -22,
+ 0
+ ]
+ ],
+ "c": true
+ },
+ "ix": 2
+ },
+ "nm": "Tracciato 1",
+ "mn": "ADBE Vector Shape - Group",
+ "hd": false
+ },
+ {
+ "ty": "st",
+ "c": {
+ "a": 0,
+ "k": [
+ 0.956862804936,
+ 0.290196078431,
+ 0.239215701234,
+ 1
+ ],
+ "ix": 3
+ },
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 4
+ },
+ "w": {
+ "a": 0,
+ "k": 3,
+ "ix": 5
+ },
+ "lc": 2,
+ "lj": 2,
+ "bm": 0,
+ "nm": "Traccia 1",
+ "mn": "ADBE Vector Graphic - Stroke",
+ "hd": false
+ },
+ {
+ "ty": "tr",
+ "p": {
+ "a": 0,
+ "k": [
+ 29.5,
+ 29.5
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 1
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 3
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 6
+ },
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 7
+ },
+ "sk": {
+ "a": 0,
+ "k": 0,
+ "ix": 4
+ },
+ "sa": {
+ "a": 0,
+ "k": 0,
+ "ix": 5
+ },
+ "nm": "Transform"
+ }
+ ],
+ "nm": "Gruppo 1",
+ "np": 2,
+ "cix": 2,
+ "bm": 0,
+ "ix": 1,
+ "mn": "ADBE Vector Group",
+ "hd": false
+ },
+ {
+ "ty": "tm",
+ "s": {
+ "a": 1,
+ "k": [
+ {
+ "i": {
+ "x": [
+ 0.833
+ ],
+ "y": [
+ 0.833
+ ]
+ },
+ "o": {
+ "x": [
+ 0.167
+ ],
+ "y": [
+ 0.167
+ ]
+ },
+ "t": 0,
+ "s": [
+ 0
+ ]
+ },
+ {
+ "t": 36,
+ "s": [
+ 0
+ ]
+ }
+ ],
+ "ix": 1
+ },
+ "e": {
+ "a": 1,
+ "k": [
+ {
+ "i": {
+ "x": [
+ 0.833
+ ],
+ "y": [
+ 0.833
+ ]
+ },
+ "o": {
+ "x": [
+ 0.167
+ ],
+ "y": [
+ 0.167
+ ]
+ },
+ "t": 0,
+ "s": [
+ 0
+ ]
+ },
+ {
+ "t": 36,
+ "s": [
+ 100
+ ]
+ }
+ ],
+ "ix": 2
+ },
+ "o": {
+ "a": 0,
+ "k": 0,
+ "ix": 3
+ },
+ "m": 1,
+ "ix": 2,
+ "nm": "Taglia tracciati 1",
+ "mn": "ADBE Vector Filter - Trim",
+ "hd": false
+ },
+ {
+ "ty": "rd",
+ "nm": "Angoli arrotondati 1",
+ "r": {
+ "a": 0,
+ "k": 10,
+ "ix": 1
+ },
+ "ix": 3,
+ "mn": "ADBE Vector Filter - RC",
+ "hd": false
+ }
+ ],
+ "ip": 0,
+ "op": 57,
+ "st": 0,
+ "bm": 0
+ }
+ ]
+ }
+ ],
+ "layers": [
+ {
+ "ddd": 0,
+ "ind": 1,
+ "ty": 0,
+ "nm": "Warning_2",
+ "refId": "comp_0",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 11
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 10
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 530,
+ 530,
+ 0
+ ],
+ "ix": 2,
+ "l": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 24,
+ 24,
+ 0
+ ],
+ "ix": 1,
+ "l": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 2000,
+ 2000,
+ 100
+ ],
+ "ix": 6,
+ "l": 2
+ }
+ },
+ "ao": 0,
+ "w": 48,
+ "h": 48,
+ "ip": 0,
+ "op": 57,
+ "st": 0,
+ "bm": 0
+ }
+ ],
+ "markers": []
+} \ No newline at end of file
diff --git a/snapshot-tests/src/main/assets/Tests/SzFont.json b/snapshot-tests/src/main/assets/Tests/SzFont.json
new file mode 100644
index 00000000..e1b73be8
--- /dev/null
+++ b/snapshot-tests/src/main/assets/Tests/SzFont.json
@@ -0,0 +1 @@
+{"v":"5.12.1","fr":60,"ip":0,"op":60,"w":400,"h":400,"nm":"Comp 1","ddd":0,"assets":[],"fonts":{"list":[{"origin":0,"fPath":"","fClass":"","fFamily":"Helvetica","fWeight":"","fStyle":"Regular","fName":"Helvetica","ascent":72.8994140625}]},"layers":[{"ddd":0,"ind":1,"ty":5,"nm":"1 test test 2 test test 3 test test 4 test test 5 test test","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[198,252,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"t":{"d":{"k":[{"s":{"sz":[280,218],"ps":[-140,-138],"s":48,"f":"Helvetica","t":"1 test test\u00032 test test\u00033 test test\u00034 test test\u00035 test test","ca":0,"j":2,"tr":0,"lh":48,"ls":0,"fc":[0,0,0],"sc":[1,0,0],"sw":1,"of":true},"t":0}]},"p":{},"m":{"g":1,"a":{"a":0,"k":[0,0],"ix":2}},"a":[]},"ip":0,"op":60,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,226,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[83.632,89.231,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[294.844,243.43],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-8.279,-24.092],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[87.802,91.523],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":60,"st":0,"ct":1,"bm":0}],"markers":[],"props":{}} \ No newline at end of file
diff --git a/snapshot-tests/src/main/assets/Tests/SzGlyph.json b/snapshot-tests/src/main/assets/Tests/SzGlyph.json
new file mode 100644
index 00000000..9bf4c5be
--- /dev/null
+++ b/snapshot-tests/src/main/assets/Tests/SzGlyph.json
@@ -0,0 +1 @@
+{"v":"5.12.1","fr":60,"ip":0,"op":60,"w":400,"h":400,"nm":"Comp 1","ddd":0,"assets":[],"fonts":{"list":[{"fName":"Helvetica","fFamily":"Helvetica","fStyle":"Regular","ascent":72.8994140625}]},"layers":[{"ddd":0,"ind":1,"ty":5,"nm":"1 test test 2 test test 3 test test 4 test test 5 test test","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[198,252,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"t":{"d":{"k":[{"s":{"sz":[280,218],"ps":[-140,-138],"s":48,"f":"Helvetica","t":"1 test test\u00032 test test\u00033 test test\u00034 test test\u00035 test test","ca":0,"j":2,"tr":0,"lh":48,"ls":0,"fc":[0,0,0],"sc":[1,0,0],"sw":1,"of":true},"t":0}]},"p":{},"m":{"g":1,"a":{"a":0,"k":[0,0],"ix":2}},"a":[]},"ip":0,"op":60,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,226,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[83.632,89.231,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[294.844,243.43],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-8.279,-24.092],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[87.802,91.523],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":60,"st":0,"ct":1,"bm":0}],"markers":[],"props":{},"chars":[{"ch":"1","size":48,"style":"Regular","w":48.24,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[2.506,-1.448],[6.348,-0.618],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[-1.237,5.404],[-2.507,1.449],[0,0],[0,0]],"v":[[26.025,-49.512],[26.025,0],[35.4,0],[35.4,-69.629],[28.467,-69.629],[22.852,-59.351],[9.57,-56.25],[9.57,-49.512]],"c":true},"ix":2},"nm":"1","mn":"ADBE Vector Shape - Group","hd":false}],"nm":"1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}]},"fFamily":"Helvetica"},{"ch":" ","size":48,"style":"Regular","w":27.78,"data":{},"fFamily":"Helvetica"},{"ch":"t","size":48,"style":"Regular","w":27.78,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[-1.465,-2.1],[-4.102,0],[-1.091,0.13],[-1.009,0.293],[0,0],[0.488,-0.021],[0.423,0],[0.684,0.359],[0,1.823],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,3.353],[1.465,2.1],[1.27,0],[1.09,-0.13],[0,0],[-0.652,0.087],[-0.488,0.021],[-1.595,0],[-1.237,-0.618],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[8.203,-52.295],[1.123,-52.295],[1.123,-45.117],[8.203,-45.117],[8.203,-10.498],[10.4,-2.319],[18.75,0.83],[22.29,0.635],[25.439,0],[25.439,-6.982],[23.73,-6.819],[22.363,-6.787],[18.945,-7.324],[17.09,-10.986],[17.09,-45.117],[25.439,-45.117],[25.439,-52.295],[17.09,-52.295],[17.09,-66.895],[8.203,-66.895]],"c":true},"ix":2},"nm":"t","mn":"ADBE Vector Shape - Group","hd":false}],"nm":"t","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}]},"fFamily":"Helvetica"},{"ch":"e","size":48,"style":"Regular","w":55.62,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[3.688,0],[4.529,-5.241],[0,-8.398],[-4.497,-4.736],[-6.47,0],[-2.1,0.52],[-2.65,2.605],[-1.286,2.361],[-0.228,1.921],[0,0],[1.518,-1.765],[4.554,0],[2.325,3.215],[0.162,5.321],[0,0],[0.517,2.409],[1.747,2.637],[3.461,1.742]],"o":[[-7.312,0],[-4.53,5.241],[0,8.529],[4.497,4.736],[2.649,0],[3.909,-0.912],[1.582,-1.497],[1.286,-2.36],[0,0],[-0.633,2.322],[-2.713,3.041],[-4.877,0],[-2.325,-3.215],[0,0],[0,-5.273],[-0.583,-3.516],[-1.812,-2.766],[-3.462,-1.741]],"v":[[28.022,-53.467],[10.261,-45.605],[3.467,-25.146],[10.211,-5.249],[26.661,1.855],[33.784,1.074],[43.622,-4.199],[47.925,-9.985],[50.195,-16.406],[41.553,-16.406],[38.326,-10.275],[27.425,-5.713],[16.621,-10.535],[12.891,-23.34],[50.928,-23.34],[50.151,-34.863],[46.657,-44.092],[38.747,-50.854]],"c":true},"ix":2},"nm":"e","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[0,0],[-2.711,2.914],[-4.002,0],[-2.389,-4.231],[-0.356,-3.809]],"o":[[0.161,-4.492],[2.711,-2.913],[5.584,0],[1.291,2.279],[0,0]],"v":[[13.086,-30.322],[17.395,-41.431],[27.466,-45.801],[39.425,-39.453],[41.895,-30.322]],"c":true},"ix":2},"nm":"e","mn":"ADBE Vector Shape - Group","hd":false}],"nm":"e","np":5,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}]},"fFamily":"Helvetica"},{"ch":"s","size":48,"style":"Regular","w":50,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-3.279,-3.532],[-7.877,0],[-3.576,3.385],[0,4.655],[3.418,2.246],[6.434,1.53],[0,0],[1.326,0.811],[0,2.271],[-1.749,1.265],[-3.399,0],[-2.066,-2.298],[-0.167,-1.974],[0,0],[2.21,2.93],[7.983,0],[3.612,-3.059],[0,-4.948],[-3.838,-2.376],[-4.532,-1.106],[0,0],[-1.291,-0.716],[0,-2.246],[2.525,-1.416],[3.322,0],[2.259,2.832],[0.266,2.93]],"o":[[0.23,5.209],[3.279,3.532],[7.317,0],[3.576,-3.385],[0,-4.817],[-2.19,-1.432],[0,0],[-2.587,-0.618],[-2.299,-1.363],[0,-2.075],[1.749,-1.265],[5.098,0],[1.266,1.619],[0,0],[0.065,-3.385],[-3.529,-4.622],[-6.104,0],[-3.613,3.06],[0,4.199],[2.15,1.367],[0,0],[3.804,0.945],[2.018,1.172],[0,2.962],[-2.525,1.416],[-5.582,0],[-1.23,-1.562],[0,0]],"v":[[3.223,-16.406],[8.488,-3.296],[25.221,2.002],[41.56,-3.076],[46.924,-15.137],[41.797,-25.732],[28.862,-30.176],[22.942,-31.592],[17.072,-33.736],[13.623,-39.185],[16.247,-44.196],[23.969,-46.094],[34.716,-42.646],[36.865,-37.256],[45.166,-37.256],[41.948,-46.729],[24.679,-53.662],[10.106,-49.072],[4.688,-37.061],[10.444,-27.197],[20.467,-23.486],[27.563,-21.729],[35.205,-19.238],[38.232,-14.111],[34.444,-7.544],[25.674,-5.42],[13.912,-9.668],[11.67,-16.406]],"c":true},"ix":2},"nm":"s","mn":"ADBE Vector Shape - Group","hd":false}],"nm":"s","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}]},"fFamily":"Helvetica"},{"ch":"\u0003","size":48,"style":"Regular","w":0,"fFamily":"Helvetica"},{"ch":"2","size":48,"style":"Regular","w":55.62,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0.325,-6.022],[0,0],[0,0],[0,0],[-3.255,2.832],[-3.191,1.758],[0,0],[-2.279,2.312],[0,5.599],[3.694,4.33],[8.008,0],[3.809,-6.77],[0.098,-6.087],[0,0],[-1.237,2.449],[-6.023,0],[-2.441,-2.397],[0,-4.077],[2.669,-2.707],[4.199,-2.441],[0,0],[2.164,-4.459]],"o":[[0,0],[0,0],[0,0],[0.846,-3.45],[1.823,-1.595],[0,0],[5.891,-3.288],[3.938,-3.971],[0,-5.305],[-3.695,-4.329],[-9.636,0],[-2.148,3.841],[0,0],[0.13,-4.341],[2.311,-4.57],[4.069,0],[2.441,2.397],[0,3.49],[-1.693,1.729],[0,0],[-6.283,3.646],[-2.165,4.46]],"v":[[3.125,0],[51.123,0],[51.123,-8.301],[12.939,-8.301],[19.092,-17.725],[26.611,-22.754],[33.301,-26.465],[45.557,-34.863],[51.465,-49.219],[45.923,-63.672],[28.369,-70.166],[8.203,-60.01],[4.834,-45.117],[13.77,-45.117],[15.82,-55.302],[28.32,-62.158],[38.086,-58.562],[41.748,-48.851],[37.744,-39.556],[28.906,-33.301],[19.531,-27.881],[6.86,-15.723]],"c":true},"ix":2},"nm":"2","mn":"ADBE Vector Shape - Group","hd":false}],"nm":"2","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}]},"fFamily":"Helvetica"},{"ch":"3","size":48,"style":"Regular","w":55.62,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-8.269,0],[-4.395,4.199],[0,6.673],[2.067,2.881],[3.678,1.009],[-1.433,1.66],[0,4.297],[3.906,3.174],[7.129,0],[3.58,-6.51],[0,-5.208],[0,0],[-1.237,2.148],[-5.859,0],[-2.474,-1.888],[0,-3.483],[3.711,-1.823],[3.288,0],[0.618,0.033],[0.911,0.098],[0,0],[-0.603,0.017],[-0.586,0],[-2.946,-1.92],[0,-4.817],[2.799,-2.473],[4.622,0],[2.278,3.679],[0.391,4.525],[0,0],[-3.728,-4.541]],"o":[[8.073,0],[4.395,-4.199],[0,-4.166],[-2.068,-2.881],[2.278,-0.944],[2.311,-2.669],[0,-6.022],[-3.906,-3.174],[-9.017,0],[-2.084,3.646],[0,0],[0.163,-3.971],[2.246,-3.906],[3.288,0],[2.473,1.888],[0,4.688],[-2.116,1.042],[-0.716,0],[-0.619,-0.032],[0,0],[0.618,-0.032],[0.602,-0.016],[5.241,0],[2.946,1.921],[0,3.906],[-2.8,2.474],[-5.957,0],[-1.302,-2.051],[0,0],[0,6.511],[3.727,4.541]],"v":[[25.977,1.904],[44.678,-4.395],[51.27,-20.703],[48.169,-31.274],[39.551,-37.109],[45.117,-41.016],[48.584,-51.465],[42.725,-65.259],[26.172,-70.02],[7.275,-60.254],[4.15,-46.973],[12.842,-46.973],[14.941,-56.152],[27.1,-62.012],[35.742,-59.18],[39.453,-51.123],[33.887,-41.357],[25.781,-39.795],[23.779,-39.844],[21.484,-40.039],[21.484,-32.275],[23.315,-32.349],[25.098,-32.373],[37.378,-29.492],[41.797,-19.385],[37.598,-9.814],[26.465,-6.104],[14.111,-11.621],[11.572,-21.484],[2.393,-21.484],[7.983,-4.907]],"c":true},"ix":2},"nm":"3","mn":"ADBE Vector Shape - Group","hd":false}],"nm":"3","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}]},"fFamily":"Helvetica"},{"ch":"4","size":48,"style":"Regular","w":55.62,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[10.645,-24.756],[33.057,-56.515],[33.057,-24.756]],"c":true},"ix":2},"nm":"4","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[41.992,0],[41.992,-17.09],[52.295,-17.09],[52.295,-24.756],[41.992,-24.756],[41.992,-70.117],[34.57,-70.117],[2.539,-25.684],[2.539,-17.09],[33.203,-17.09],[33.203,0]],"c":true},"ix":2},"nm":"4","mn":"ADBE Vector Shape - Group","hd":false}],"nm":"4","np":5,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}]},"fFamily":"Helvetica"},{"ch":"5","size":48,"style":"Regular","w":55.62,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-4.379,-3.206],[-5.697,0],[-4.086,5.046],[0,6.609],[4.459,4.134],[6.413,0],[2.604,-1.074],[1.627,-1.237],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[-2.165,1.172],[-3.027,0],[-2.979,-2.669],[0,-4.883],[2.506,-3.32],[5.208,0],[2.083,0.977],[0.586,5.013]],"o":[[0.52,6.641],[4.378,3.206],[8.952,0],[4.085,-5.045],[0,-6.348],[-4.46,-4.134],[-3.418,0],[-1.465,0.619],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[1.53,-2.083],[2.164,-1.172],[4.166,0],[2.979,2.67],[0,4.037],[-2.507,3.32],[-2.734,0],[-4.07,-1.92],[0,0]],"v":[[3.223,-17.822],[10.571,-3.052],[25.684,1.758],[45.239,-5.811],[51.367,-23.291],[44.678,-39.014],[28.369,-45.215],[19.336,-43.604],[14.697,-40.82],[17.676,-60.254],[47.412,-60.254],[47.412,-68.75],[11.086,-68.75],[5.762,-31.104],[13.379,-30.664],[18.921,-35.547],[26.709,-37.305],[37.427,-33.301],[41.895,-21.973],[38.135,-10.938],[26.562,-5.957],[19.336,-7.422],[12.354,-17.822]],"c":true},"ix":2},"nm":"5","mn":"ADBE Vector Shape - Group","hd":false}],"nm":"5","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}]},"fFamily":"Helvetica"}]} \ No newline at end of file
diff --git a/snapshot-tests/src/main/assets/Tests/TextWithParentAlpha.json b/snapshot-tests/src/main/assets/Tests/TextWithParentAlpha.json
new file mode 100644
index 00000000..97227b0b
--- /dev/null
+++ b/snapshot-tests/src/main/assets/Tests/TextWithParentAlpha.json
@@ -0,0 +1 @@
+{"v":"5.10.2","fr":25,"ip":0,"op":107,"w":796,"h":972,"nm":"Simple Logo Reveal","ddd":0,"assets":[{"id":"comp_0","nm":"_Main Text Placeholder","fr":25,"layers":[{"ddd":0,"ind":1,"ty":5,"nm":"Text 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[498.57,80.748,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[50,50,100],"ix":6,"l":2}},"ao":0,"t":{"d":{"k":[{"s":{"s":173,"f":"Nunito-Black","t":"SAMPLE TEXT","ca":1,"j":2,"tr":0,"lh":207.600006103516,"ls":0,"fc":[1,0.69,0]},"t":0}]},"p":{},"m":{"g":1,"a":{"a":0,"k":[0,0],"ix":2}},"a":[]},"ip":0,"op":250.25025025025,"st":0,"ct":1,"bm":0}]}],"fonts":{"list":[{"fName":"Nunito-Black","fFamily":"Nunito","fStyle":"Black","ascent":71.8994140625}]},"layers":[{"ddd":0,"ind":1,"ty":0,"nm":"_Main Text Placeholder","refId":"comp_0","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.927],"y":[0.689]},"o":{"x":[1],"y":[0.007]},"t":60,"s":[13]},{"t":74,"s":[99]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.038,"y":1},"o":{"x":0.766,"y":0},"t":60,"s":[396,888,0],"to":[0,-54,0],"ti":[0,54,0]},{"t":78,"s":[396,564,0]}],"ix":2,"l":2},"a":{"a":0,"k":[500,50,0],"ix":1,"l":2},"s":{"a":0,"k":[55,55,100],"ix":6,"l":2}},"ao":0,"w":1000,"h":100,"ip":-3,"op":107,"st":-3,"bm":0}],"markers":[],"chars":[{"ch":"S","size":173,"style":"Black","w":66.7,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-4.923,-4.524],[-8.699,0],[-5.635,3.418],[0,7.943],[4.416,3.353],[4.7,1.107],[0,0],[1.861,1.042],[0,3.516],[-2.403,2.409],[-5.493,0],[-2.84,-3.743],[-0.468,-3.841],[0,0],[5.116,3.63],[7.317,0],[4.34,-4.134],[0,-6.445],[-4.415,-2.995],[-6.149,-1.432],[0,0],[-2.397,-1.627],[0,-3.906],[5.582,-2.018],[3.512,0],[3.104,4.753],[0.22,4.07]],"o":[[-0.098,7.748],[4.92,4.558],[7.46,0],[5.635,-3.418],[0,-6.38],[-2.554,-1.92],[0,0],[-6.561,-1.562],[-2.839,-1.627],[0,-3.19],[2.403,-2.409],[6.803,0],[1.529,2.051],[0,0],[0,-7.975],[-5.116,-3.629],[-7.951,0],[-4.34,4.134],[0,5.957],[2.554,1.726],[0,0],[5.109,1.205],[2.365,1.66],[0,5.209],[-2.885,1.042],[-7.839,0],[-1.63,-2.539],[0,0]],"v":[[4.834,-23.145],[12.072,-4.736],[32.5,2.1],[52.143,-3.027],[60.596,-20.068],[53.972,-34.668],[43.091,-39.209],[33.345,-41.504],[20.713,-45.41],[16.455,-53.125],[20.059,-61.523],[31.904,-65.137],[46.369,-59.521],[49.365,-50.684],[58.496,-50.684],[50.822,-68.091],[32.172,-73.535],[13.736,-67.334],[7.227,-51.465],[13.849,-38.037],[26.904,-33.301],[36.317,-31.104],[47.575,-26.855],[51.123,-18.506],[42.751,-7.666],[33.155,-6.104],[16.74,-13.232],[13.965,-23.145]],"c":true},"ix":2},"nm":"S","mn":"ADBE Vector Shape - Group","hd":false}],"nm":"S","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}]},"fFamily":"Nunito"},{"ch":"A","size":173,"style":"Black","w":66.7,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[21.973,-29.395],[33.545,-61.084],[44.434,-29.395]],"c":true},"ix":2},"nm":"A","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[1.465,0],[11.426,0],[19.189,-21.484],[47.559,-21.484],[54.834,0],[65.479,0],[39.453,-71.729],[28.467,-71.729]],"c":true},"ix":2},"nm":"A","mn":"ADBE Vector Shape - Group","hd":false}],"nm":"A","np":5,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}]},"fFamily":"Nunito"},{"ch":"M","size":173,"style":"Black","w":83.3,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0.098,3.597],[0,1.758],[0,0],[0,0],[0,0],[0,0],[0.065,-3.385],[0,-1.465],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,-1.692],[-0.098,-3.596],[0,0],[0,0],[0,0],[0,0],[0,3.874],[-0.066,3.386],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[7.373,0],[16.65,0],[16.65,-42.334],[16.504,-50.269],[16.357,-58.301],[16.357,-60.498],[37.032,0],[46.66,0],[67.139,-60.498],[67.041,-49.609],[66.943,-42.334],[66.943,0],[76.221,0],[76.221,-71.729],[62.398,-71.729],[41.919,-11.084],[21.294,-71.729],[7.373,-71.729]],"c":true},"ix":2},"nm":"M","mn":"ADBE Vector Shape - Group","hd":false}],"nm":"M","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}]},"fFamily":"Nunito"},{"ch":"P","size":173,"style":"Black","w":66.7,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[-3.482,4.15],[0,5.599],[3.902,3.597],[6.345,0],[0,0]],"o":[[0,0],[0,0],[0,0],[7.175,0],[3.481,-4.15],[0,-6.51],[-3.902,-3.596],[0,0],[0,0]],"v":[[8.545,0],[18.262,0],[18.262,-30.322],[40.659,-30.322],[56.643,-36.548],[61.865,-51.172],[56.013,-66.333],[40.643,-71.729],[8.545,-71.729]],"c":true},"ix":2},"nm":"P","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[0,-5.273],[2.699,-1.855],[4.33,0],[0,0],[0,0],[0,0],[-2.133,-1.009]],"o":[[0,4.688],[-2.699,1.855],[0,0],[0,0],[0,0],[3.716,0],[3.878,1.888]],"v":[[52.051,-51.123],[48.003,-41.309],[37.459,-38.525],[18.262,-38.525],[18.262,-63.379],[37.459,-63.379],[46.233,-61.865]],"c":true},"ix":2},"nm":"P","mn":"ADBE Vector Shape - Group","hd":false}],"nm":"P","np":5,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}]},"fFamily":"Nunito"},{"ch":"L","size":173,"style":"Black","w":55.62,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[7.617,0],[53.32,0],[53.32,-8.545],[17.334,-8.545],[17.334,-71.729],[7.617,-71.729]],"c":true},"ix":2},"nm":"L","mn":"ADBE Vector Shape - Group","hd":false}],"nm":"L","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}]},"fFamily":"Nunito"},{"ch":"E","size":173,"style":"Black","w":66.7,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[8.545,0],[61.328,0],[61.328,-8.545],[18.018,-8.545],[18.018,-32.861],[57.373,-32.861],[57.373,-41.162],[18.018,-41.162],[18.018,-62.939],[60.596,-62.939],[60.596,-71.729],[8.545,-71.729]],"c":true},"ix":2},"nm":"E","mn":"ADBE Vector Shape - Group","hd":false}],"nm":"E","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}]},"fFamily":"Nunito"},{"ch":" ","size":173,"style":"Black","w":27.78,"data":{},"fFamily":"Nunito"},{"ch":"T","size":173,"style":"Black","w":61.08,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[1.611,-71.729],[1.611,-63.184],[25.781,-63.184],[25.781,0],[35.596,0],[35.596,-63.184],[59.766,-63.184],[59.766,-71.729]],"c":true},"ix":2},"nm":"T","mn":"ADBE Vector Shape - Group","hd":false}],"nm":"T","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}]},"fFamily":"Nunito"},{"ch":"X","size":173,"style":"Black","w":66.7,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[33.3,-29.492],[52.411,0],[64.429,0],[39.481,-36.799],[63.266,-71.729],[51.735,-71.729],[33.782,-44.287],[15.685,-71.729],[3.57,-71.729],[27.367,-36.798],[1.981,0],[13.657,0]],"c":true},"ix":2},"nm":"X","mn":"ADBE Vector Shape - Group","hd":false}],"nm":"X","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}]},"fFamily":"Nunito"}]} \ No newline at end of file
diff --git a/snapshot-tests/src/main/assets/Tests/TextWithPsCenter.json b/snapshot-tests/src/main/assets/Tests/TextWithPsCenter.json
new file mode 100644
index 00000000..284d5ba7
--- /dev/null
+++ b/snapshot-tests/src/main/assets/Tests/TextWithPsCenter.json
@@ -0,0 +1,2039 @@
+{
+ "v": "5.9.2",
+ "fr": 24,
+ "ip": 0,
+ "op": 41,
+ "w": 1080,
+ "h": 1080,
+ "nm": "CenterAlignedNoScale",
+ "ddd": 0,
+ "assets": [],
+ "fonts": {
+ "list": [
+ {
+ "fName": "MaisonNeue-Book",
+ "fFamily": "Maison Neue",
+ "fStyle": "Book",
+ "ascent": 72.9995727539062
+ }
+ ]
+ },
+ "layers": [
+ {
+ "ddd": 0,
+ "ind": 1,
+ "ty": 5,
+ "nm": "CENTER ALIGNED",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 11
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 10
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 540,
+ 540,
+ 0
+ ],
+ "ix": 2,
+ "l": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0,
+ 0
+ ],
+ "ix": 1,
+ "l": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100,
+ 100
+ ],
+ "ix": 6,
+ "l": 2
+ }
+ },
+ "ao": 0,
+ "t": {
+ "d": {
+ "k": [
+ {
+ "s": {
+ "sz": [
+ 1080,
+ 100
+ ],
+ "ps": [
+ -540,
+ 0
+ ],
+ "s": 118,
+ "f": "MaisonNeue-Book",
+ "t": "CENTER ALIGNED",
+ "ca": 0,
+ "j": 2,
+ "tr": 100,
+ "lh": 27.5,
+ "ls": 0,
+ "fc": [
+ 0.379,
+ 0.379,
+ 0.379
+ ]
+ },
+ "t": 0
+ }
+ ]
+ },
+ "p": {},
+ "m": {
+ "g": 1,
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ }
+ },
+ "a": []
+ },
+ "ip": 0,
+ "op": 180,
+ "st": 0,
+ "bm": 0
+ },
+ {
+ "ddd": 0,
+ "ind": 2,
+ "ty": 4,
+ "nm": "BackgroundLayer",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 11
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 10
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 892.749,
+ 380.408,
+ 0
+ ],
+ "ix": 2,
+ "l": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0,
+ 0
+ ],
+ "ix": 1,
+ "l": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100,
+ 100
+ ],
+ "ix": 6,
+ "l": 2
+ }
+ },
+ "ao": 0,
+ "shapes": [
+ {
+ "ty": "gr",
+ "it": [
+ {
+ "ty": "rc",
+ "d": 1,
+ "s": {
+ "a": 0,
+ "k": [
+ 1920,
+ 1920
+ ],
+ "ix": 2
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 3
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 4
+ },
+ "nm": "Rectangle Path 1",
+ "mn": "ADBE Vector Shape - Rect",
+ "hd": false
+ },
+ {
+ "ty": "fl",
+ "c": {
+ "a": 0,
+ "k": [
+ 0.996078431373,
+ 0.83137254902,
+ 0.749019607843,
+ 1
+ ],
+ "ix": 4
+ },
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 5
+ },
+ "r": 1,
+ "bm": 0,
+ "nm": "Fill 1",
+ "mn": "ADBE Vector Graphic - Fill",
+ "hd": false
+ },
+ {
+ "ty": "tr",
+ "p": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 1
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100
+ ],
+ "ix": 3
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 6
+ },
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 7
+ },
+ "sk": {
+ "a": 0,
+ "k": 0,
+ "ix": 4
+ },
+ "sa": {
+ "a": 0,
+ "k": 0,
+ "ix": 5
+ },
+ "nm": "Transform"
+ }
+ ],
+ "nm": "Rectangle 1",
+ "np": 2,
+ "cix": 2,
+ "bm": 0,
+ "ix": 1,
+ "mn": "ADBE Vector Group",
+ "hd": false
+ }
+ ],
+ "ip": 0,
+ "op": 180,
+ "st": 0,
+ "bm": 0
+ }
+ ],
+ "markers": [],
+ "chars": [
+ {
+ "ch": "C",
+ "size": 118,
+ "style": "Book",
+ "w": 64.5,
+ "data": {
+ "shapes": [
+ {
+ "ty": "gr",
+ "it": [
+ {
+ "ind": 0,
+ "ty": "sh",
+ "ix": 1,
+ "ks": {
+ "a": 0,
+ "k": {
+ "i": [
+ [
+ -18.027,
+ 0
+ ],
+ [
+ -1.309,
+ 16.415
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 10.876,
+ 0
+ ],
+ [
+ 0,
+ 18.027
+ ],
+ [
+ -13.193,
+ 0
+ ],
+ [
+ -2.014,
+ -11.783
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 14.502,
+ 0
+ ],
+ [
+ 0,
+ -22.055
+ ]
+ ],
+ "o": [
+ [
+ 15.61,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1.309,
+ 12.891
+ ],
+ [
+ -13.293,
+ 0
+ ],
+ [
+ 0,
+ -17.825
+ ],
+ [
+ 10.272,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -2.014,
+ -15.005
+ ],
+ [
+ -17.825,
+ 0
+ ],
+ [
+ 0,
+ 22.357
+ ]
+ ],
+ "v": [
+ [
+ 34.241,
+ 1.511
+ ],
+ [
+ 61.432,
+ -24.976
+ ],
+ [
+ 52.872,
+ -26.083
+ ],
+ [
+ 34.241,
+ -6.345
+ ],
+ [
+ 12.891,
+ -35.248
+ ],
+ [
+ 34.241,
+ -64.151
+ ],
+ [
+ 52.972,
+ -46.024
+ ],
+ [
+ 61.23,
+ -47.433
+ ],
+ [
+ 34.241,
+ -72.006
+ ],
+ [
+ 4.532,
+ -35.248
+ ]
+ ],
+ "c": true
+ },
+ "ix": 2
+ },
+ "nm": "C",
+ "mn": "ADBE Vector Shape - Group",
+ "hd": false
+ }
+ ],
+ "nm": "C",
+ "np": 3,
+ "cix": 2,
+ "bm": 0,
+ "ix": 1,
+ "mn": "ADBE Vector Group",
+ "hd": false
+ }
+ ]
+ },
+ "fFamily": "Maison Neue"
+ },
+ {
+ "ch": "E",
+ "size": 118,
+ "style": "Book",
+ "w": 56.1,
+ "data": {
+ "shapes": [
+ {
+ "ty": "gr",
+ "it": [
+ {
+ "ind": 0,
+ "ty": "sh",
+ "ix": 1,
+ "ks": {
+ "a": 0,
+ "k": {
+ "i": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ "o": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ "v": [
+ [
+ 8.661,
+ 0
+ ],
+ [
+ 50.958,
+ 0
+ ],
+ [
+ 50.958,
+ -7.855
+ ],
+ [
+ 16.919,
+ -7.855
+ ],
+ [
+ 16.919,
+ -32.629
+ ],
+ [
+ 48.138,
+ -32.629
+ ],
+ [
+ 48.138,
+ -40.082
+ ],
+ [
+ 16.919,
+ -40.082
+ ],
+ [
+ 16.919,
+ -62.64
+ ],
+ [
+ 50.958,
+ -62.64
+ ],
+ [
+ 50.958,
+ -70.496
+ ],
+ [
+ 8.661,
+ -70.496
+ ]
+ ],
+ "c": true
+ },
+ "ix": 2
+ },
+ "nm": "E",
+ "mn": "ADBE Vector Shape - Group",
+ "hd": false
+ }
+ ],
+ "nm": "E",
+ "np": 3,
+ "cix": 2,
+ "bm": 0,
+ "ix": 1,
+ "mn": "ADBE Vector Group",
+ "hd": false
+ }
+ ]
+ },
+ "fFamily": "Maison Neue"
+ },
+ {
+ "ch": "N",
+ "size": 118,
+ "style": "Book",
+ "w": 67.2,
+ "data": {
+ "shapes": [
+ {
+ "ty": "gr",
+ "it": [
+ {
+ "ind": 0,
+ "ty": "sh",
+ "ix": 1,
+ "ks": {
+ "a": 0,
+ "k": {
+ "i": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ "o": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ "v": [
+ [
+ 8.661,
+ 0
+ ],
+ [
+ 16.919,
+ 0
+ ],
+ [
+ 16.919,
+ -57.504
+ ],
+ [
+ 17.12,
+ -57.504
+ ],
+ [
+ 49.146,
+ 0
+ ],
+ [
+ 59.015,
+ 0
+ ],
+ [
+ 59.015,
+ -70.496
+ ],
+ [
+ 50.656,
+ -70.496
+ ],
+ [
+ 50.656,
+ -12.991
+ ],
+ [
+ 50.455,
+ -12.991
+ ],
+ [
+ 18.43,
+ -70.496
+ ],
+ [
+ 8.661,
+ -70.496
+ ]
+ ],
+ "c": true
+ },
+ "ix": 2
+ },
+ "nm": "N",
+ "mn": "ADBE Vector Shape - Group",
+ "hd": false
+ }
+ ],
+ "nm": "N",
+ "np": 3,
+ "cix": 2,
+ "bm": 0,
+ "ix": 1,
+ "mn": "ADBE Vector Group",
+ "hd": false
+ }
+ ]
+ },
+ "fFamily": "Maison Neue"
+ },
+ {
+ "ch": "T",
+ "size": 118,
+ "style": "Book",
+ "w": 58.9,
+ "data": {
+ "shapes": [
+ {
+ "ty": "gr",
+ "it": [
+ {
+ "ind": 0,
+ "ty": "sh",
+ "ix": 1,
+ "ks": {
+ "a": 0,
+ "k": {
+ "i": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ "o": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ "v": [
+ [
+ 25.479,
+ 0
+ ],
+ [
+ 33.838,
+ 0
+ ],
+ [
+ 33.838,
+ -62.64
+ ],
+ [
+ 55.792,
+ -62.64
+ ],
+ [
+ 55.792,
+ -70.496
+ ],
+ [
+ 3.525,
+ -70.496
+ ],
+ [
+ 3.525,
+ -62.64
+ ],
+ [
+ 25.479,
+ -62.64
+ ]
+ ],
+ "c": true
+ },
+ "ix": 2
+ },
+ "nm": "T",
+ "mn": "ADBE Vector Shape - Group",
+ "hd": false
+ }
+ ],
+ "nm": "T",
+ "np": 3,
+ "cix": 2,
+ "bm": 0,
+ "ix": 1,
+ "mn": "ADBE Vector Group",
+ "hd": false
+ }
+ ]
+ },
+ "fFamily": "Maison Neue"
+ },
+ {
+ "ch": "R",
+ "size": 118,
+ "style": "Book",
+ "w": 60.9,
+ "data": {
+ "shapes": [
+ {
+ "ty": "gr",
+ "it": [
+ {
+ "ind": 0,
+ "ty": "sh",
+ "ix": 1,
+ "ks": {
+ "a": 0,
+ "k": {
+ "i": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 11.179
+ ],
+ [
+ 14.703,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ "o": [
+ [
+ 11.078,
+ -1.813
+ ],
+ [
+ 0,
+ -12.991
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ "v": [
+ [
+ 40.182,
+ -29.205
+ ],
+ [
+ 57.806,
+ -49.649
+ ],
+ [
+ 34.341,
+ -70.496
+ ],
+ [
+ 8.661,
+ -70.496
+ ],
+ [
+ 8.661,
+ 0
+ ],
+ [
+ 16.919,
+ 0
+ ],
+ [
+ 16.919,
+ -28.601
+ ],
+ [
+ 31.018,
+ -28.601
+ ],
+ [
+ 45.923,
+ 0
+ ],
+ [
+ 55.692,
+ 0
+ ],
+ [
+ 40.182,
+ -29.004
+ ]
+ ],
+ "c": true
+ },
+ "ix": 2
+ },
+ "nm": "R",
+ "mn": "ADBE Vector Shape - Group",
+ "hd": false
+ },
+ {
+ "ind": 1,
+ "ty": "sh",
+ "ix": 2,
+ "ks": {
+ "a": 0,
+ "k": {
+ "i": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ -8.56
+ ],
+ [
+ 10.373,
+ 0
+ ]
+ ],
+ "o": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 10.474,
+ 0
+ ],
+ [
+ 0,
+ 8.661
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ "v": [
+ [
+ 16.919,
+ -36.456
+ ],
+ [
+ 16.919,
+ -62.64
+ ],
+ [
+ 33.636,
+ -62.64
+ ],
+ [
+ 49.448,
+ -49.649
+ ],
+ [
+ 33.636,
+ -36.456
+ ]
+ ],
+ "c": true
+ },
+ "ix": 2
+ },
+ "nm": "R",
+ "mn": "ADBE Vector Shape - Group",
+ "hd": false
+ }
+ ],
+ "nm": "R",
+ "np": 5,
+ "cix": 2,
+ "bm": 0,
+ "ix": 1,
+ "mn": "ADBE Vector Group",
+ "hd": false
+ }
+ ]
+ },
+ "fFamily": "Maison Neue"
+ },
+ {
+ "ch": " ",
+ "size": 118,
+ "style": "Book",
+ "w": 27,
+ "data": {},
+ "fFamily": "Maison Neue"
+ },
+ {
+ "ch": "A",
+ "size": 118,
+ "style": "Book",
+ "w": 61.3,
+ "data": {
+ "shapes": [
+ {
+ "ty": "gr",
+ "it": [
+ {
+ "ind": 0,
+ "ty": "sh",
+ "ix": 1,
+ "ks": {
+ "a": 0,
+ "k": {
+ "i": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ "o": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ "v": [
+ [
+ 50.656,
+ 0
+ ],
+ [
+ 59.216,
+ 0
+ ],
+ [
+ 33.939,
+ -70.496
+ ],
+ [
+ 25.882,
+ -70.496
+ ],
+ [
+ 0.504,
+ 0
+ ],
+ [
+ 9.064,
+ 0
+ ],
+ [
+ 13.998,
+ -14.2
+ ],
+ [
+ 45.721,
+ -14.2
+ ]
+ ],
+ "c": true
+ },
+ "ix": 2
+ },
+ "nm": "A",
+ "mn": "ADBE Vector Shape - Group",
+ "hd": false
+ },
+ {
+ "ind": 1,
+ "ty": "sh",
+ "ix": 2,
+ "ks": {
+ "a": 0,
+ "k": {
+ "i": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ "o": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ "v": [
+ [
+ 16.617,
+ -21.451
+ ],
+ [
+ 29.81,
+ -59.116
+ ],
+ [
+ 30.011,
+ -59.116
+ ],
+ [
+ 43.204,
+ -21.451
+ ]
+ ],
+ "c": true
+ },
+ "ix": 2
+ },
+ "nm": "A",
+ "mn": "ADBE Vector Shape - Group",
+ "hd": false
+ }
+ ],
+ "nm": "A",
+ "np": 5,
+ "cix": 2,
+ "bm": 0,
+ "ix": 1,
+ "mn": "ADBE Vector Group",
+ "hd": false
+ }
+ ]
+ },
+ "fFamily": "Maison Neue"
+ },
+ {
+ "ch": "L",
+ "size": 118,
+ "style": "Book",
+ "w": 51.9,
+ "data": {
+ "shapes": [
+ {
+ "ty": "gr",
+ "it": [
+ {
+ "ind": 0,
+ "ty": "sh",
+ "ix": 1,
+ "ks": {
+ "a": 0,
+ "k": {
+ "i": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ "o": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ "v": [
+ [
+ 8.661,
+ 0
+ ],
+ [
+ 48.743,
+ 0
+ ],
+ [
+ 48.743,
+ -7.855
+ ],
+ [
+ 16.919,
+ -7.855
+ ],
+ [
+ 16.919,
+ -70.496
+ ],
+ [
+ 8.661,
+ -70.496
+ ]
+ ],
+ "c": true
+ },
+ "ix": 2
+ },
+ "nm": "L",
+ "mn": "ADBE Vector Shape - Group",
+ "hd": false
+ }
+ ],
+ "nm": "L",
+ "np": 3,
+ "cix": 2,
+ "bm": 0,
+ "ix": 1,
+ "mn": "ADBE Vector Group",
+ "hd": false
+ }
+ ]
+ },
+ "fFamily": "Maison Neue"
+ },
+ {
+ "ch": "I",
+ "size": 118,
+ "style": "Book",
+ "w": 25.4,
+ "data": {
+ "shapes": [
+ {
+ "ty": "gr",
+ "it": [
+ {
+ "ind": 0,
+ "ty": "sh",
+ "ix": 1,
+ "ks": {
+ "a": 0,
+ "k": {
+ "i": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ "o": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ "v": [
+ [
+ 8.661,
+ 0
+ ],
+ [
+ 16.919,
+ 0
+ ],
+ [
+ 16.919,
+ -70.496
+ ],
+ [
+ 8.661,
+ -70.496
+ ]
+ ],
+ "c": true
+ },
+ "ix": 2
+ },
+ "nm": "I",
+ "mn": "ADBE Vector Shape - Group",
+ "hd": false
+ }
+ ],
+ "nm": "I",
+ "np": 3,
+ "cix": 2,
+ "bm": 0,
+ "ix": 1,
+ "mn": "ADBE Vector Group",
+ "hd": false
+ }
+ ]
+ },
+ "fFamily": "Maison Neue"
+ },
+ {
+ "ch": "G",
+ "size": 118,
+ "style": "Book",
+ "w": 65.3,
+ "data": {
+ "shapes": [
+ {
+ "ty": "gr",
+ "it": [
+ {
+ "ind": 0,
+ "ty": "sh",
+ "ix": 1,
+ "ks": {
+ "a": 0,
+ "k": {
+ "i": [
+ [
+ -18.027,
+ 0
+ ],
+ [
+ 0,
+ 17.825
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 12.085,
+ 0
+ ],
+ [
+ 0,
+ 17.825
+ ],
+ [
+ -13.193,
+ 0
+ ],
+ [
+ -2.014,
+ -11.078
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 14.099,
+ 0
+ ],
+ [
+ 0,
+ -22.357
+ ]
+ ],
+ "o": [
+ [
+ 16.516,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0.302,
+ 12.79
+ ],
+ [
+ -13.193,
+ 0
+ ],
+ [
+ 0,
+ -17.825
+ ],
+ [
+ 9.97,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -2.115,
+ -14.099
+ ],
+ [
+ -18.027,
+ 0
+ ],
+ [
+ 0,
+ 22.357
+ ]
+ ],
+ "v": [
+ [
+ 34.241,
+ 1.511
+ ],
+ [
+ 61.23,
+ -27.695
+ ],
+ [
+ 61.23,
+ -34.845
+ ],
+ [
+ 35.147,
+ -34.845
+ ],
+ [
+ 35.147,
+ -26.99
+ ],
+ [
+ 53.073,
+ -26.99
+ ],
+ [
+ 34.241,
+ -6.345
+ ],
+ [
+ 12.891,
+ -35.248
+ ],
+ [
+ 34.241,
+ -64.151
+ ],
+ [
+ 52.771,
+ -47.232
+ ],
+ [
+ 61.029,
+ -48.743
+ ],
+ [
+ 34.241,
+ -72.006
+ ],
+ [
+ 4.532,
+ -35.248
+ ]
+ ],
+ "c": true
+ },
+ "ix": 2
+ },
+ "nm": "G",
+ "mn": "ADBE Vector Shape - Group",
+ "hd": false
+ }
+ ],
+ "nm": "G",
+ "np": 3,
+ "cix": 2,
+ "bm": 0,
+ "ix": 1,
+ "mn": "ADBE Vector Group",
+ "hd": false
+ }
+ ]
+ },
+ "fFamily": "Maison Neue"
+ },
+ {
+ "ch": "D",
+ "size": 118,
+ "style": "Book",
+ "w": 65.1,
+ "data": {
+ "shapes": [
+ {
+ "ty": "gr",
+ "it": [
+ {
+ "ind": 0,
+ "ty": "sh",
+ "ix": 1,
+ "ks": {
+ "a": 0,
+ "k": {
+ "i": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 24.875
+ ],
+ [
+ 17.825,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ "o": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 18.43,
+ 0
+ ],
+ [
+ 0,
+ -22.256
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ "v": [
+ [
+ 8.661,
+ 0
+ ],
+ [
+ 30.011,
+ 0
+ ],
+ [
+ 61.029,
+ -35.349
+ ],
+ [
+ 30.615,
+ -70.496
+ ],
+ [
+ 8.661,
+ -70.496
+ ]
+ ],
+ "c": true
+ },
+ "ix": 2
+ },
+ "nm": "D",
+ "mn": "ADBE Vector Shape - Group",
+ "hd": false
+ },
+ {
+ "ind": 1,
+ "ty": "sh",
+ "ix": 2,
+ "ks": {
+ "a": 0,
+ "k": {
+ "i": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ -18.027
+ ],
+ [
+ 14.301,
+ 0
+ ]
+ ],
+ "o": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 13.696,
+ 0
+ ],
+ [
+ 0,
+ 19.94
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ "v": [
+ [
+ 16.919,
+ -7.855
+ ],
+ [
+ 16.919,
+ -62.64
+ ],
+ [
+ 30.112,
+ -62.64
+ ],
+ [
+ 52.67,
+ -35.349
+ ],
+ [
+ 29.507,
+ -7.855
+ ]
+ ],
+ "c": true
+ },
+ "ix": 2
+ },
+ "nm": "D",
+ "mn": "ADBE Vector Shape - Group",
+ "hd": false
+ }
+ ],
+ "nm": "D",
+ "np": 5,
+ "cix": 2,
+ "bm": 0,
+ "ix": 1,
+ "mn": "ADBE Vector Group",
+ "hd": false
+ }
+ ]
+ },
+ "fFamily": "Maison Neue"
+ }
+ ]
+} \ No newline at end of file
diff --git a/snapshot-tests/src/main/assets/Tests/TextWithPsLeft.json b/snapshot-tests/src/main/assets/Tests/TextWithPsLeft.json
new file mode 100644
index 00000000..23f2a0b6
--- /dev/null
+++ b/snapshot-tests/src/main/assets/Tests/TextWithPsLeft.json
@@ -0,0 +1,1626 @@
+{
+ "v": "5.9.3",
+ "fr": 60,
+ "ip": 0,
+ "op": 100,
+ "w": 375,
+ "h": 625,
+ "nm": "[TEST2]",
+ "ddd": 0,
+ "assets":[],
+ "fonts": {
+ "list": [
+ {
+ "fName": "MaisonNeue-Book",
+ "fFamily": "Maison Neue",
+ "fStyle": "Book",
+ "ascent": 72.9995727539062
+ }
+ ]
+ },
+ "layers": [
+ {
+ "ddd": 0,
+ "ind": 22,
+ "ty": 5,
+ "nm": "test",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 11
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 10
+ },
+ "p": {
+ "s": true,
+ "x": {
+ "a": 0,
+ "k": 0.0,
+ "ix": 3
+ },
+ "y": {
+ "a": 0,
+ "k": 0.0,
+ "ix": 4
+ }
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ -106.775,
+ -150.686,
+ 0
+ ],
+ "ix": 1,
+ "l": 2
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 100,
+ 100,
+ 100
+ ],
+ "ix": 6,
+ "l": 2
+ }
+ },
+ "ao": 0,
+ "t": {
+ "d": {
+ "k": [
+ {
+ "s": {
+ "sz": [
+ 255,
+ 45
+ ],
+ "ps": [
+ -100.0,
+ 200.0
+ ],
+ "s": 36,
+ "f": "MaisonNeue-Book",
+ "t": "LEFT ALIGNED",
+ "ca": 0,
+ "j": 0,
+ "tr": 0,
+ "lh": 43.2000007629395,
+ "ls": 0,
+ "fc": [
+ 0,
+ 0,
+ 0
+ ]
+ },
+ "t": 0
+ }
+ ]
+ },
+ "p": {},
+ "m": {
+ "g": 1,
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 2
+ }
+ },
+ "a": []
+ },
+ "ip": 0,
+ "op": 100,
+ "st": 0,
+ "ct": 1,
+ "bm": 0
+ }
+ ],
+ "markers": [],
+ "chars": [
+ {
+ "ch": "L",
+ "size": 118,
+ "style": "Book",
+ "w": 51.9,
+ "data": {
+ "shapes": [
+ {
+ "ty": "gr",
+ "it": [
+ {
+ "ind": 0,
+ "ty": "sh",
+ "ix": 1,
+ "ks": {
+ "a": 0,
+ "k": {
+ "i": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ "o": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ "v": [
+ [
+ 8.661,
+ 0
+ ],
+ [
+ 48.743,
+ 0
+ ],
+ [
+ 48.743,
+ -7.855
+ ],
+ [
+ 16.919,
+ -7.855
+ ],
+ [
+ 16.919,
+ -70.496
+ ],
+ [
+ 8.661,
+ -70.496
+ ]
+ ],
+ "c": true
+ },
+ "ix": 2
+ },
+ "nm": "L",
+ "mn": "ADBE Vector Shape - Group",
+ "hd": false
+ }
+ ],
+ "nm": "L",
+ "np": 3,
+ "cix": 2,
+ "bm": 0,
+ "ix": 1,
+ "mn": "ADBE Vector Group",
+ "hd": false
+ }
+ ]
+ },
+ "fFamily": "Maison Neue"
+ },
+ {
+ "ch": "E",
+ "size": 118,
+ "style": "Book",
+ "w": 56.1,
+ "data": {
+ "shapes": [
+ {
+ "ty": "gr",
+ "it": [
+ {
+ "ind": 0,
+ "ty": "sh",
+ "ix": 1,
+ "ks": {
+ "a": 0,
+ "k": {
+ "i": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ "o": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ "v": [
+ [
+ 8.661,
+ 0
+ ],
+ [
+ 50.958,
+ 0
+ ],
+ [
+ 50.958,
+ -7.855
+ ],
+ [
+ 16.919,
+ -7.855
+ ],
+ [
+ 16.919,
+ -32.629
+ ],
+ [
+ 48.138,
+ -32.629
+ ],
+ [
+ 48.138,
+ -40.082
+ ],
+ [
+ 16.919,
+ -40.082
+ ],
+ [
+ 16.919,
+ -62.64
+ ],
+ [
+ 50.958,
+ -62.64
+ ],
+ [
+ 50.958,
+ -70.496
+ ],
+ [
+ 8.661,
+ -70.496
+ ]
+ ],
+ "c": true
+ },
+ "ix": 2
+ },
+ "nm": "E",
+ "mn": "ADBE Vector Shape - Group",
+ "hd": false
+ }
+ ],
+ "nm": "E",
+ "np": 3,
+ "cix": 2,
+ "bm": 0,
+ "ix": 1,
+ "mn": "ADBE Vector Group",
+ "hd": false
+ }
+ ]
+ },
+ "fFamily": "Maison Neue"
+ },
+ {
+ "ch": "F",
+ "size": 118,
+ "style": "Book",
+ "w": 55.2,
+ "data": {
+ "shapes": [
+ {
+ "ty": "gr",
+ "it": [
+ {
+ "ind": 0,
+ "ty": "sh",
+ "ix": 1,
+ "ks": {
+ "a": 0,
+ "k": {
+ "i": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ "o": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ "v": [
+ [
+ 8.661,
+ 0
+ ],
+ [
+ 16.919,
+ 0
+ ],
+ [
+ 16.919,
+ -30.011
+ ],
+ [
+ 48.239,
+ -30.011
+ ],
+ [
+ 48.239,
+ -37.463
+ ],
+ [
+ 16.919,
+ -37.463
+ ],
+ [
+ 16.919,
+ -62.64
+ ],
+ [
+ 51.059,
+ -62.64
+ ],
+ [
+ 51.059,
+ -70.496
+ ],
+ [
+ 8.661,
+ -70.496
+ ]
+ ],
+ "c": true
+ },
+ "ix": 2
+ },
+ "nm": "F",
+ "mn": "ADBE Vector Shape - Group",
+ "hd": false
+ }
+ ],
+ "nm": "F",
+ "np": 3,
+ "cix": 2,
+ "bm": 0,
+ "ix": 1,
+ "mn": "ADBE Vector Group",
+ "hd": false
+ }
+ ]
+ },
+ "fFamily": "Maison Neue"
+ },
+ {
+ "ch": "T",
+ "size": 118,
+ "style": "Book",
+ "w": 58.9,
+ "data": {
+ "shapes": [
+ {
+ "ty": "gr",
+ "it": [
+ {
+ "ind": 0,
+ "ty": "sh",
+ "ix": 1,
+ "ks": {
+ "a": 0,
+ "k": {
+ "i": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ "o": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ "v": [
+ [
+ 25.479,
+ 0
+ ],
+ [
+ 33.838,
+ 0
+ ],
+ [
+ 33.838,
+ -62.64
+ ],
+ [
+ 55.792,
+ -62.64
+ ],
+ [
+ 55.792,
+ -70.496
+ ],
+ [
+ 3.525,
+ -70.496
+ ],
+ [
+ 3.525,
+ -62.64
+ ],
+ [
+ 25.479,
+ -62.64
+ ]
+ ],
+ "c": true
+ },
+ "ix": 2
+ },
+ "nm": "T",
+ "mn": "ADBE Vector Shape - Group",
+ "hd": false
+ }
+ ],
+ "nm": "T",
+ "np": 3,
+ "cix": 2,
+ "bm": 0,
+ "ix": 1,
+ "mn": "ADBE Vector Group",
+ "hd": false
+ }
+ ]
+ },
+ "fFamily": "Maison Neue"
+ },
+ {
+ "ch": " ",
+ "size": 118,
+ "style": "Book",
+ "w": 27,
+ "data": {},
+ "fFamily": "Maison Neue"
+ },
+ {
+ "ch": "A",
+ "size": 118,
+ "style": "Book",
+ "w": 61.3,
+ "data": {
+ "shapes": [
+ {
+ "ty": "gr",
+ "it": [
+ {
+ "ind": 0,
+ "ty": "sh",
+ "ix": 1,
+ "ks": {
+ "a": 0,
+ "k": {
+ "i": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ "o": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ "v": [
+ [
+ 50.656,
+ 0
+ ],
+ [
+ 59.216,
+ 0
+ ],
+ [
+ 33.939,
+ -70.496
+ ],
+ [
+ 25.882,
+ -70.496
+ ],
+ [
+ 0.504,
+ 0
+ ],
+ [
+ 9.064,
+ 0
+ ],
+ [
+ 13.998,
+ -14.2
+ ],
+ [
+ 45.721,
+ -14.2
+ ]
+ ],
+ "c": true
+ },
+ "ix": 2
+ },
+ "nm": "A",
+ "mn": "ADBE Vector Shape - Group",
+ "hd": false
+ },
+ {
+ "ind": 1,
+ "ty": "sh",
+ "ix": 2,
+ "ks": {
+ "a": 0,
+ "k": {
+ "i": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ "o": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ "v": [
+ [
+ 16.617,
+ -21.451
+ ],
+ [
+ 29.81,
+ -59.116
+ ],
+ [
+ 30.011,
+ -59.116
+ ],
+ [
+ 43.204,
+ -21.451
+ ]
+ ],
+ "c": true
+ },
+ "ix": 2
+ },
+ "nm": "A",
+ "mn": "ADBE Vector Shape - Group",
+ "hd": false
+ }
+ ],
+ "nm": "A",
+ "np": 5,
+ "cix": 2,
+ "bm": 0,
+ "ix": 1,
+ "mn": "ADBE Vector Group",
+ "hd": false
+ }
+ ]
+ },
+ "fFamily": "Maison Neue"
+ },
+ {
+ "ch": "I",
+ "size": 118,
+ "style": "Book",
+ "w": 25.4,
+ "data": {
+ "shapes": [
+ {
+ "ty": "gr",
+ "it": [
+ {
+ "ind": 0,
+ "ty": "sh",
+ "ix": 1,
+ "ks": {
+ "a": 0,
+ "k": {
+ "i": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ "o": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ "v": [
+ [
+ 8.661,
+ 0
+ ],
+ [
+ 16.919,
+ 0
+ ],
+ [
+ 16.919,
+ -70.496
+ ],
+ [
+ 8.661,
+ -70.496
+ ]
+ ],
+ "c": true
+ },
+ "ix": 2
+ },
+ "nm": "I",
+ "mn": "ADBE Vector Shape - Group",
+ "hd": false
+ }
+ ],
+ "nm": "I",
+ "np": 3,
+ "cix": 2,
+ "bm": 0,
+ "ix": 1,
+ "mn": "ADBE Vector Group",
+ "hd": false
+ }
+ ]
+ },
+ "fFamily": "Maison Neue"
+ },
+ {
+ "ch": "G",
+ "size": 118,
+ "style": "Book",
+ "w": 65.3,
+ "data": {
+ "shapes": [
+ {
+ "ty": "gr",
+ "it": [
+ {
+ "ind": 0,
+ "ty": "sh",
+ "ix": 1,
+ "ks": {
+ "a": 0,
+ "k": {
+ "i": [
+ [
+ -18.027,
+ 0
+ ],
+ [
+ 0,
+ 17.825
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 12.085,
+ 0
+ ],
+ [
+ 0,
+ 17.825
+ ],
+ [
+ -13.193,
+ 0
+ ],
+ [
+ -2.014,
+ -11.078
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 14.099,
+ 0
+ ],
+ [
+ 0,
+ -22.357
+ ]
+ ],
+ "o": [
+ [
+ 16.516,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0.302,
+ 12.79
+ ],
+ [
+ -13.193,
+ 0
+ ],
+ [
+ 0,
+ -17.825
+ ],
+ [
+ 9.97,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -2.115,
+ -14.099
+ ],
+ [
+ -18.027,
+ 0
+ ],
+ [
+ 0,
+ 22.357
+ ]
+ ],
+ "v": [
+ [
+ 34.241,
+ 1.511
+ ],
+ [
+ 61.23,
+ -27.695
+ ],
+ [
+ 61.23,
+ -34.845
+ ],
+ [
+ 35.147,
+ -34.845
+ ],
+ [
+ 35.147,
+ -26.99
+ ],
+ [
+ 53.073,
+ -26.99
+ ],
+ [
+ 34.241,
+ -6.345
+ ],
+ [
+ 12.891,
+ -35.248
+ ],
+ [
+ 34.241,
+ -64.151
+ ],
+ [
+ 52.771,
+ -47.232
+ ],
+ [
+ 61.029,
+ -48.743
+ ],
+ [
+ 34.241,
+ -72.006
+ ],
+ [
+ 4.532,
+ -35.248
+ ]
+ ],
+ "c": true
+ },
+ "ix": 2
+ },
+ "nm": "G",
+ "mn": "ADBE Vector Shape - Group",
+ "hd": false
+ }
+ ],
+ "nm": "G",
+ "np": 3,
+ "cix": 2,
+ "bm": 0,
+ "ix": 1,
+ "mn": "ADBE Vector Group",
+ "hd": false
+ }
+ ]
+ },
+ "fFamily": "Maison Neue"
+ },
+ {
+ "ch": "N",
+ "size": 118,
+ "style": "Book",
+ "w": 67.2,
+ "data": {
+ "shapes": [
+ {
+ "ty": "gr",
+ "it": [
+ {
+ "ind": 0,
+ "ty": "sh",
+ "ix": 1,
+ "ks": {
+ "a": 0,
+ "k": {
+ "i": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ "o": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ "v": [
+ [
+ 8.661,
+ 0
+ ],
+ [
+ 16.919,
+ 0
+ ],
+ [
+ 16.919,
+ -57.504
+ ],
+ [
+ 17.12,
+ -57.504
+ ],
+ [
+ 49.146,
+ 0
+ ],
+ [
+ 59.015,
+ 0
+ ],
+ [
+ 59.015,
+ -70.496
+ ],
+ [
+ 50.656,
+ -70.496
+ ],
+ [
+ 50.656,
+ -12.991
+ ],
+ [
+ 50.455,
+ -12.991
+ ],
+ [
+ 18.43,
+ -70.496
+ ],
+ [
+ 8.661,
+ -70.496
+ ]
+ ],
+ "c": true
+ },
+ "ix": 2
+ },
+ "nm": "N",
+ "mn": "ADBE Vector Shape - Group",
+ "hd": false
+ }
+ ],
+ "nm": "N",
+ "np": 3,
+ "cix": 2,
+ "bm": 0,
+ "ix": 1,
+ "mn": "ADBE Vector Group",
+ "hd": false
+ }
+ ]
+ },
+ "fFamily": "Maison Neue"
+ },
+ {
+ "ch": "D",
+ "size": 118,
+ "style": "Book",
+ "w": 65.1,
+ "data": {
+ "shapes": [
+ {
+ "ty": "gr",
+ "it": [
+ {
+ "ind": 0,
+ "ty": "sh",
+ "ix": 1,
+ "ks": {
+ "a": 0,
+ "k": {
+ "i": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 24.875
+ ],
+ [
+ 17.825,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ "o": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 18.43,
+ 0
+ ],
+ [
+ 0,
+ -22.256
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ "v": [
+ [
+ 8.661,
+ 0
+ ],
+ [
+ 30.011,
+ 0
+ ],
+ [
+ 61.029,
+ -35.349
+ ],
+ [
+ 30.615,
+ -70.496
+ ],
+ [
+ 8.661,
+ -70.496
+ ]
+ ],
+ "c": true
+ },
+ "ix": 2
+ },
+ "nm": "D",
+ "mn": "ADBE Vector Shape - Group",
+ "hd": false
+ },
+ {
+ "ind": 1,
+ "ty": "sh",
+ "ix": 2,
+ "ks": {
+ "a": 0,
+ "k": {
+ "i": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ -18.027
+ ],
+ [
+ 14.301,
+ 0
+ ]
+ ],
+ "o": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 13.696,
+ 0
+ ],
+ [
+ 0,
+ 19.94
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ "v": [
+ [
+ 16.919,
+ -7.855
+ ],
+ [
+ 16.919,
+ -62.64
+ ],
+ [
+ 30.112,
+ -62.64
+ ],
+ [
+ 52.67,
+ -35.349
+ ],
+ [
+ 29.507,
+ -7.855
+ ]
+ ],
+ "c": true
+ },
+ "ix": 2
+ },
+ "nm": "D",
+ "mn": "ADBE Vector Shape - Group",
+ "hd": false
+ }
+ ],
+ "nm": "D",
+ "np": 5,
+ "cix": 2,
+ "bm": 0,
+ "ix": 1,
+ "mn": "ADBE Vector Group",
+ "hd": false
+ }
+ ]
+ },
+ "fFamily": "Maison Neue"
+ },
+ {
+ "ch": "\r",
+ "size": 118,
+ "style": "Book",
+ "w": 0,
+ "fFamily": "Maison Neue"
+ }
+ ]
+} \ No newline at end of file
diff --git a/snapshot-tests/src/main/assets/Tests/Thumb.json b/snapshot-tests/src/main/assets/Tests/Thumb.json
new file mode 100644
index 00000000..50e3db71
--- /dev/null
+++ b/snapshot-tests/src/main/assets/Tests/Thumb.json
@@ -0,0 +1 @@
+{"v":"5.9.6","fr":29.9700012207031,"ip":0,"op":190.000007738859,"w":24,"h":24,"nm":"Thumb","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[12,12,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"sr","sy":1,"d":1,"pt":{"a":0,"k":5,"ix":3},"p":{"a":0,"k":[0,0],"ix":4},"r":{"a":0,"k":0,"ix":5},"ir":{"a":0,"k":5,"ix":6},"is":{"a":0,"k":0,"ix":8},"or":{"a":0,"k":12,"ix":7},"os":{"a":0,"k":0,"ix":9},"ix":1,"nm":"Polystar Path 1","mn":"ADBE Vector Shape - Star","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.922702133656,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Polystar 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":190.000007738859,"st":0,"ct":1,"bm":0}],"markers":[]} \ No newline at end of file
diff --git a/snapshot-tests/src/main/assets/Tests/TriangleLargeStroke.json b/snapshot-tests/src/main/assets/Tests/TriangleLargeStroke.json
new file mode 100644
index 00000000..19c4144c
--- /dev/null
+++ b/snapshot-tests/src/main/assets/Tests/TriangleLargeStroke.json
@@ -0,0 +1 @@
+{"v":"5.12.2","fr":29.9700012207031,"ip":0,"op":60.0000024438501,"w":1200,"h":1200,"nm":"Repro 2 - Large","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[600,376,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"sr","sy":2,"d":1,"pt":{"a":0,"k":3,"ix":3},"p":{"a":0,"k":[0,0],"ix":4},"r":{"a":0,"k":0,"ix":5},"or":{"a":0,"k":301,"ix":7},"os":{"a":0,"k":30,"ix":9},"ix":1,"nm":"Polystar Path 1","mn":"ADBE Vector Shape - Star","hd":false},{"ty":"st","c":{"a":0,"k":[1,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":100,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[-205.188,19.281],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":59.0000024031193,"s":[360]}],"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Polystar 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"sr","sy":2,"d":1,"pt":{"a":0,"k":3,"ix":3},"p":{"a":0,"k":[0,0],"ix":4},"r":{"a":0,"k":0,"ix":5},"or":{"a":0,"k":301,"ix":7},"os":{"a":0,"k":0,"ix":9},"ix":1,"nm":"Polystar Path 2","mn":"ADBE Vector Shape - Star","hd":false},{"ty":"st","c":{"a":0,"k":[1,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":100,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[225,421],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":59.0000024031193,"s":[360]}],"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Polystar 2","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":60.0000024438501,"st":0,"ct":1,"bm":0}],"markers":[],"props":{}}
diff --git a/snapshot-tests/src/main/assets/Tests/ZipInlineImage.zip b/snapshot-tests/src/main/assets/Tests/ZipInlineImage.zip
new file mode 100644
index 00000000..145e91fa
--- /dev/null
+++ b/snapshot-tests/src/main/assets/Tests/ZipInlineImage.zip
Binary files differ
diff --git a/snapshot-tests/src/main/assets/Tests/text-offset-example.json b/snapshot-tests/src/main/assets/Tests/text-offset-example.json
new file mode 100644
index 00000000..e6050a2f
--- /dev/null
+++ b/snapshot-tests/src/main/assets/Tests/text-offset-example.json
@@ -0,0 +1 @@
+{"v": "5.9.0", "fr": 24, "ip": 0, "op": 192, "w": 375, "h": 640, "nm": "003_02", "ddd": 0, "assets": [{"id": "comp_0", "nm": "Fillpping coin_01", "fr": 24, "layers": [{"ddd": 0, "ind": 1, "ty": 4, "nm": "Shape Layer 16", "td": 1, "sr": 1, "ks": {"o": {"a": 0, "k": 100, "ix": 11}, "r": {"a": 0, "k": 0, "ix": 10}, "p": {"a": 1, "k": [{"i": {"x": 0.118, "y": 1}, "o": {"x": 0.95, "y": 0}, "t": 35, "s": [197.246, 320, 0], "to": [-1.708, 0, 0], "ti": [1.708, 0, 0]}, {"t": 49, "s": [187, 320, 0]}], "ix": 2, "l": 2}, "a": {"a": 0, "k": [-7.194, 40.355, 0], "ix": 1, "l": 2}, "s": {"a": 1, "k": [{"i": {"x": [0.118, 0.118, 0.667], "y": [1, 1, 1]}, "o": {"x": [0.95, 0.95, 0.333], "y": [0, 0, 0]}, "t": 35, "s": [0.038, 98.384, 100]}, {"t": 49, "s": [99.483, 98.384, 100]}], "ix": 6, "l": 2}}, "ao": 0, "shapes": [{"ty": "gr", "it": [{"ind": 0, "ty": "sh", "ix": 1, "ks": {"a": 0, "k": {"i": [[-32.529, 0], [0, -32.529], [32.529, 0], [0, 32.529]], "o": [[32.529, 0], [0, 32.529], [-32.529, 0], [0, -32.529]], "v": [[0, -58.9], [58.9, 0], [0, 58.9], [-58.9, 0]], "c": true}, "ix": 2}, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false}, {"ty": "fl", "c": {"a": 0, "k": [0.639215686275, 0.862745098039, 0.101960784314, 1], "ix": 4}, "o": {"a": 0, "k": 100, "ix": 5}, "r": 1, "bm": 0, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false}, {"ty": "tr", "p": {"a": 0, "k": [-7.194, 40.355], "ix": 2}, "a": {"a": 0, "k": [0, 0], "ix": 1}, "s": {"a": 0, "k": [103.006, 103.006], "ix": 3}, "r": {"a": 0, "k": 0, "ix": 6}, "o": {"a": 0, "k": 100, "ix": 7}, "sk": {"a": 0, "k": 0, "ix": 4}, "sa": {"a": 0, "k": 0, "ix": 5}, "nm": "Transform"}], "nm": "Ellipse 1", "np": 3, "cix": 2, "bm": 0, "ix": 1, "mn": "ADBE Vector Group", "hd": false}], "ip": 35, "op": 318, "st": -13, "bm": 0}, {"ddd": 0, "ind": 2, "ty": 4, "nm": "Shape Layer 14", "tt": 1, "sr": 1, "ks": {"o": {"a": 0, "k": 100, "ix": 11}, "r": {"a": 0, "k": 0, "ix": 10}, "p": {"a": 1, "k": [{"i": {"x": 0.155, "y": 1}, "o": {"x": 0.84, "y": 0}, "t": 46, "s": [146.262, 246.355, 0], "to": [13.875, 24.583, 0], "ti": [-13.875, -24.583, 0]}, {"t": 58, "s": [229.512, 393.855, 0]}], "ix": 2, "l": 2}, "a": {"a": 0, "k": [-16.488, -41.145, 0], "ix": 1, "l": 2}, "s": {"a": 0, "k": [100, 100, 100], "ix": 6, "l": 2}}, "ao": 0, "shapes": [{"ty": "gr", "it": [{"ind": 0, "ty": "sh", "ix": 1, "ks": {"a": 0, "k": {"i": [[0, 0], [0, 0], [0, 0], [0, 0]], "o": [[0, 0], [0, 0], [0, 0], [0, 0]], "v": [[81.726, -15.73], [81.774, 35.98], [-81.652, 123.48], [-81.701, 71.77]], "c": true}, "ix": 2}, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false}, {"ty": "fl", "c": {"a": 0, "k": [1, 1, 1, 1], "ix": 4}, "o": {"a": 0, "k": 100, "ix": 5}, "r": 1, "bm": 0, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false}, {"ty": "tr", "p": {"a": 0, "k": [-16.524, -95.02], "ix": 2}, "a": {"a": 0, "k": [0, 0], "ix": 1}, "s": {"a": 0, "k": [100, 100], "ix": 3}, "r": {"a": 0, "k": 0, "ix": 6}, "o": {"a": 0, "k": 100, "ix": 7}, "sk": {"a": 0, "k": 0, "ix": 4}, "sa": {"a": 0, "k": 0, "ix": 5}, "nm": "Transform"}], "nm": "Rectangle 1", "np": 3, "cix": 2, "bm": 0, "ix": 1, "mn": "ADBE Vector Group", "hd": false}], "ip": 46, "op": 58, "st": 46, "bm": 0}, {"ddd": 0, "ind": 4, "ty": 4, "nm": "Shape Layer 11", "sr": 1, "ks": {"o": {"a": 0, "k": 100, "ix": 11}, "r": {"a": 0, "k": 0, "ix": 10}, "p": {"a": 1, "k": [{"i": {"x": 0.118, "y": 1}, "o": {"x": 0.95, "y": 0}, "t": 35, "s": [194.582, 319.805, 0], "to": [-1.264, 0, 0], "ti": [1.264, 0, 0]}, {"t": 49, "s": [187, 319.805, 0]}], "ix": 2, "l": 2}, "a": {"a": 0, "k": [-7.194, 40.355, 0], "ix": 1, "l": 2}, "s": {"a": 1, "k": [{"i": {"x": [0.118, 0.118, 0.667], "y": [1, 1, 1]}, "o": {"x": [0.95, 0.95, 0.333], "y": [0, 0, 0]}, "t": 35, "s": [0.028, 72.804, 100]}, {"t": 49, "s": [73.617, 72.804, 100]}], "ix": 6, "l": 2}}, "ao": 0, "shapes": [{"ty": "gr", "it": [{"ind": 0, "ty": "sh", "ix": 1, "ks": {"a": 0, "k": {"i": [[-32.529, 0], [0, -32.529], [32.529, 0], [0, 32.529]], "o": [[32.529, 0], [0, 32.529], [-32.529, 0], [0, -32.529]], "v": [[0, -58.9], [58.9, 0], [0, 58.9], [-58.9, 0]], "c": true}, "ix": 2}, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false}, {"ty": "st", "c": {"a": 0, "k": [1, 1, 1, 1], "ix": 3}, "o": {"a": 0, "k": 100, "ix": 4}, "w": {"a": 0, "k": 2, "ix": 5}, "lc": 1, "lj": 1, "ml": 4, "bm": 0, "nm": "Stroke 1", "mn": "ADBE Vector Graphic - Stroke", "hd": false}, {"ty": "tr", "p": {"a": 0, "k": [-7.194, 40.355], "ix": 2}, "a": {"a": 0, "k": [0, 0], "ix": 1}, "s": {"a": 0, "k": [103.006, 103.006], "ix": 3}, "r": {"a": 0, "k": 0, "ix": 6}, "o": {"a": 0, "k": 100, "ix": 7}, "sk": {"a": 0, "k": 0, "ix": 4}, "sa": {"a": 0, "k": 0, "ix": 5}, "nm": "Transform"}], "nm": "Ellipse 1", "np": 3, "cix": 2, "bm": 0, "ix": 1, "mn": "ADBE Vector Group", "hd": false}], "ip": 42, "op": 318, "st": -13, "bm": 0}, {"ddd": 0, "ind": 5, "ty": 4, "nm": "Shape Layer 3", "sr": 1, "ks": {"o": {"a": 0, "k": 100, "ix": 11}, "r": {"a": 0, "k": 0, "ix": 10}, "p": {"a": 1, "k": [{"i": {"x": 0.118, "y": 1}, "o": {"x": 0.95, "y": 0}, "t": 35, "s": [197.246, 320, 0], "to": [-1.708, 0, 0], "ti": [1.708, 0, 0]}, {"t": 49, "s": [187, 320, 0]}], "ix": 2, "l": 2}, "a": {"a": 0, "k": [-7.194, 40.355, 0], "ix": 1, "l": 2}, "s": {"a": 1, "k": [{"i": {"x": [0.118, 0.118, 0.667], "y": [1, 1, 1]}, "o": {"x": [0.95, 0.95, 0.333], "y": [0, 0, 0]}, "t": 35, "s": [0.038, 98.384, 100]}, {"t": 49, "s": [99.483, 98.384, 100]}], "ix": 6, "l": 2}}, "ao": 0, "shapes": [{"ty": "gr", "it": [{"ind": 0, "ty": "sh", "ix": 1, "ks": {"a": 0, "k": {"i": [[-32.529, 0], [0, -32.529], [32.529, 0], [0, 32.529]], "o": [[32.529, 0], [0, 32.529], [-32.529, 0], [0, -32.529]], "v": [[0, -58.9], [58.9, 0], [0, 58.9], [-58.9, 0]], "c": true}, "ix": 2}, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false}, {"ty": "fl", "c": {"a": 0, "k": [0.639215686275, 0.862745098039, 0.101960784314, 1], "ix": 4}, "o": {"a": 0, "k": 100, "ix": 5}, "r": 1, "bm": 0, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false}, {"ty": "tr", "p": {"a": 0, "k": [-7.194, 40.355], "ix": 2}, "a": {"a": 0, "k": [0, 0], "ix": 1}, "s": {"a": 0, "k": [103.006, 103.006], "ix": 3}, "r": {"a": 0, "k": 0, "ix": 6}, "o": {"a": 0, "k": 100, "ix": 7}, "sk": {"a": 0, "k": 0, "ix": 4}, "sa": {"a": 0, "k": 0, "ix": 5}, "nm": "Transform"}], "nm": "Ellipse 1", "np": 3, "cix": 2, "bm": 0, "ix": 1, "mn": "ADBE Vector Group", "hd": false}], "ip": 35, "op": 318, "st": -13, "bm": 0}, {"ddd": 0, "ind": 6, "ty": 4, "nm": "Shape Layer 7", "sr": 1, "ks": {"o": {"a": 0, "k": 100, "ix": 11}, "r": {"a": 0, "k": 0, "ix": 10}, "p": {"a": 1, "k": [{"i": {"x": 0.667, "y": 1}, "o": {"x": 0.333, "y": 0}, "t": 43, "s": [182.301, 320, 0], "to": [-0.886, 0, 0], "ti": [1.339, 0, 0]}, {"t": 46, "s": [187, 320, 0]}], "ix": 2, "l": 2}, "a": {"a": 0, "k": [-7.194, 40.355, 0], "ix": 1, "l": 2}, "s": {"a": 1, "k": [{"i": {"x": [0.118, 0.118, 0.667], "y": [1, 1, 1]}, "o": {"x": [0.95, 0.95, 0.333], "y": [0, 0, 0]}, "t": 35, "s": [0.038, 98.384, 100]}, {"t": 49, "s": [99.483, 98.384, 100]}], "ix": 6, "l": 2}}, "ao": 0, "shapes": [{"ty": "gr", "it": [{"ind": 0, "ty": "sh", "ix": 1, "ks": {"a": 0, "k": {"i": [[-32.529, 0], [0, -32.529], [32.529, 0], [0, 32.529]], "o": [[32.529, 0], [0, 32.529], [-32.529, 0], [0, -32.529]], "v": [[0, -58.9], [58.9, 0], [0, 58.9], [-58.9, 0]], "c": true}, "ix": 2}, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false}, {"ty": "fl", "c": {"a": 0, "k": [0.227450980392, 0.286274509804, 0.125490196078, 1], "ix": 4}, "o": {"a": 0, "k": 100, "ix": 5}, "r": 1, "bm": 0, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false}, {"ty": "tr", "p": {"a": 0, "k": [-7.194, 40.355], "ix": 2}, "a": {"a": 0, "k": [0, 0], "ix": 1}, "s": {"a": 0, "k": [103.006, 103.006], "ix": 3}, "r": {"a": 0, "k": 0, "ix": 6}, "o": {"a": 0, "k": 100, "ix": 7}, "sk": {"a": 0, "k": 0, "ix": 4}, "sa": {"a": 0, "k": 0, "ix": 5}, "nm": "Transform"}], "nm": "Ellipse 1", "np": 3, "cix": 2, "bm": 0, "ix": 1, "mn": "ADBE Vector Group", "hd": false}], "ip": 35, "op": 318, "st": 35, "bm": 0}, {"ddd": 0, "ind": 7, "ty": 4, "nm": "Shape Layer 17", "td": 1, "sr": 1, "ks": {"o": {"a": 0, "k": 100, "ix": 11}, "r": {"a": 0, "k": 0, "ix": 10}, "p": {"a": 1, "k": [{"i": {"x": 0.102, "y": 1}, "o": {"x": 0.913, "y": 0}, "t": 21, "s": [187, 320, 0], "to": [-0.701, 0, 0], "ti": [0.701, 0, 0]}, {"t": 35, "s": [182.796, 320, 0]}], "ix": 2, "l": 2}, "a": {"a": 0, "k": [-7.194, 40.355, 0], "ix": 1, "l": 2}, "s": {"a": 1, "k": [{"i": {"x": [0.102, 0.102, 0.667], "y": [1, 1, 1]}, "o": {"x": [0.913, 0.913, 0.333], "y": [0, 0, 0]}, "t": 21, "s": [99.483, 98.384, 100]}, {"t": 35, "s": [0.038, 98.384, 100]}], "ix": 6, "l": 2}}, "ao": 0, "shapes": [{"ty": "gr", "it": [{"ind": 0, "ty": "sh", "ix": 1, "ks": {"a": 0, "k": {"i": [[-32.529, 0], [0, -32.529], [32.529, 0], [0, 32.529]], "o": [[32.529, 0], [0, 32.529], [-32.529, 0], [0, -32.529]], "v": [[0, -58.9], [58.9, 0], [0, 58.9], [-58.9, 0]], "c": true}, "ix": 2}, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false}, {"ty": "fl", "c": {"a": 0, "k": [0.639215686275, 0.862745098039, 0.101960784314, 1], "ix": 4}, "o": {"a": 0, "k": 100, "ix": 5}, "r": 1, "bm": 0, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false}, {"ty": "tr", "p": {"a": 0, "k": [-7.194, 40.355], "ix": 2}, "a": {"a": 0, "k": [0, 0], "ix": 1}, "s": {"a": 0, "k": [103.006, 103.006], "ix": 3}, "r": {"a": 0, "k": 0, "ix": 6}, "o": {"a": 0, "k": 100, "ix": 7}, "sk": {"a": 0, "k": 0, "ix": 4}, "sa": {"a": 0, "k": 0, "ix": 5}, "nm": "Transform"}], "nm": "Ellipse 1", "np": 3, "cix": 2, "bm": 0, "ix": 1, "mn": "ADBE Vector Group", "hd": false}], "ip": 0, "op": 35, "st": 19, "bm": 0}, {"ddd": 0, "ind": 8, "ty": 4, "nm": "Shape Layer 12", "tt": 1, "sr": 1, "ks": {"o": {"a": 0, "k": 100, "ix": 11}, "r": {"a": 0, "k": 0, "ix": 10}, "p": {"a": 1, "k": [{"i": {"x": 0.155, "y": 1}, "o": {"x": 0.84, "y": 0}, "t": 21, "s": [146.262, 246.355, 0], "to": [13.875, 24.583, 0], "ti": [-13.875, -24.583, 0]}, {"t": 31, "s": [229.512, 393.855, 0]}], "ix": 2, "l": 2}, "a": {"a": 0, "k": [-16.488, -41.145, 0], "ix": 1, "l": 2}, "s": {"a": 0, "k": [100, 100, 100], "ix": 6, "l": 2}}, "ao": 0, "shapes": [{"ty": "gr", "it": [{"ind": 0, "ty": "sh", "ix": 1, "ks": {"a": 0, "k": {"i": [[0, 0], [0, 0], [0, 0], [0, 0]], "o": [[0, 0], [0, 0], [0, 0], [0, 0]], "v": [[81.726, -15.73], [81.774, 35.98], [-81.652, 123.48], [-81.701, 71.77]], "c": true}, "ix": 2}, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false}, {"ty": "fl", "c": {"a": 0, "k": [1, 1, 1, 1], "ix": 4}, "o": {"a": 0, "k": 100, "ix": 5}, "r": 1, "bm": 0, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false}, {"ty": "tr", "p": {"a": 0, "k": [-16.524, -95.02], "ix": 2}, "a": {"a": 0, "k": [0, 0], "ix": 1}, "s": {"a": 0, "k": [100, 100], "ix": 3}, "r": {"a": 0, "k": 0, "ix": 6}, "o": {"a": 0, "k": 100, "ix": 7}, "sk": {"a": 0, "k": 0, "ix": 4}, "sa": {"a": 0, "k": 0, "ix": 5}, "nm": "Transform"}], "nm": "Rectangle 1", "np": 3, "cix": 2, "bm": 0, "ix": 1, "mn": "ADBE Vector Group", "hd": false}], "ip": 0, "op": 31, "st": 19, "bm": 0}, {"ddd": 0, "ind": 9, "ty": 4, "nm": "Shape Layer 10", "sr": 1, "ks": {"o": {"a": 0, "k": 100, "ix": 11}, "r": {"a": 0, "k": 0, "ix": 10}, "p": {"a": 1, "k": [{"i": {"x": 0.102, "y": 1}, "o": {"x": 0.913, "y": 0}, "t": 21, "s": [187, 319.805, 0], "to": [-0.519, 0, 0], "ti": [0.519, 0, 0]}, {"t": 35, "s": [183.889, 319.805, 0]}], "ix": 2, "l": 2}, "a": {"a": 0, "k": [-7.194, 40.355, 0], "ix": 1, "l": 2}, "s": {"a": 1, "k": [{"i": {"x": [0.102, 0.102, 0.667], "y": [1, 1, 1]}, "o": {"x": [0.913, 0.913, 0.333], "y": [0, 0, 0]}, "t": 21, "s": [73.617, 72.804, 100]}, {"t": 35, "s": [0.028, 72.804, 100]}], "ix": 6, "l": 2}}, "ao": 0, "shapes": [{"ty": "gr", "it": [{"ind": 0, "ty": "sh", "ix": 1, "ks": {"a": 0, "k": {"i": [[-32.529, 0], [0, -32.529], [32.529, 0], [0, 32.529]], "o": [[32.529, 0], [0, 32.529], [-32.529, 0], [0, -32.529]], "v": [[0, -58.9], [58.9, 0], [0, 58.9], [-58.9, 0]], "c": true}, "ix": 2}, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false}, {"ty": "st", "c": {"a": 0, "k": [1, 1, 1, 1], "ix": 3}, "o": {"a": 0, "k": 100, "ix": 4}, "w": {"a": 0, "k": 2, "ix": 5}, "lc": 1, "lj": 1, "ml": 4, "bm": 0, "nm": "Stroke 1", "mn": "ADBE Vector Graphic - Stroke", "hd": false}, {"ty": "tr", "p": {"a": 0, "k": [-7.194, 40.355], "ix": 2}, "a": {"a": 0, "k": [0, 0], "ix": 1}, "s": {"a": 0, "k": [103.006, 103.006], "ix": 3}, "r": {"a": 0, "k": 0, "ix": 6}, "o": {"a": 0, "k": 100, "ix": 7}, "sk": {"a": 0, "k": 0, "ix": 4}, "sa": {"a": 0, "k": 0, "ix": 5}, "nm": "Transform"}], "nm": "Ellipse 1", "np": 3, "cix": 2, "bm": 0, "ix": 1, "mn": "ADBE Vector Group", "hd": false}], "ip": 0, "op": 29, "st": 19, "bm": 0}, {"ddd": 0, "ind": 10, "ty": 4, "nm": "Shape Layer 6", "sr": 1, "ks": {"o": {"a": 0, "k": 100, "ix": 11}, "r": {"a": 0, "k": 0, "ix": 10}, "p": {"a": 1, "k": [{"i": {"x": 0.102, "y": 1}, "o": {"x": 0.913, "y": 0}, "t": 21, "s": [187, 320, 0], "to": [-0.701, 0, 0], "ti": [0.701, 0, 0]}, {"t": 35, "s": [182.796, 320, 0]}], "ix": 2, "l": 2}, "a": {"a": 0, "k": [-7.194, 40.355, 0], "ix": 1, "l": 2}, "s": {"a": 1, "k": [{"i": {"x": [0.102, 0.102, 0.667], "y": [1, 1, 1]}, "o": {"x": [0.913, 0.913, 0.333], "y": [0, 0, 0]}, "t": 21, "s": [99.483, 98.384, 100]}, {"t": 35, "s": [0.038, 98.384, 100]}], "ix": 6, "l": 2}}, "ao": 0, "shapes": [{"ty": "gr", "it": [{"ind": 0, "ty": "sh", "ix": 1, "ks": {"a": 0, "k": {"i": [[-32.529, 0], [0, -32.529], [32.529, 0], [0, 32.529]], "o": [[32.529, 0], [0, 32.529], [-32.529, 0], [0, -32.529]], "v": [[0, -58.9], [58.9, 0], [0, 58.9], [-58.9, 0]], "c": true}, "ix": 2}, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false}, {"ty": "fl", "c": {"a": 0, "k": [0.639215686275, 0.862745098039, 0.101960784314, 1], "ix": 4}, "o": {"a": 0, "k": 100, "ix": 5}, "r": 1, "bm": 0, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false}, {"ty": "tr", "p": {"a": 0, "k": [-7.194, 40.355], "ix": 2}, "a": {"a": 0, "k": [0, 0], "ix": 1}, "s": {"a": 0, "k": [103.006, 103.006], "ix": 3}, "r": {"a": 0, "k": 0, "ix": 6}, "o": {"a": 0, "k": 100, "ix": 7}, "sk": {"a": 0, "k": 0, "ix": 4}, "sa": {"a": 0, "k": 0, "ix": 5}, "nm": "Transform"}], "nm": "Ellipse 1", "np": 3, "cix": 2, "bm": 0, "ix": 1, "mn": "ADBE Vector Group", "hd": false}], "ip": 0, "op": 35, "st": 19, "bm": 0}, {"ddd": 0, "ind": 11, "ty": 4, "nm": "Shape Layer 1", "sr": 1, "ks": {"o": {"a": 0, "k": 100, "ix": 11}, "r": {"a": 0, "k": 0, "ix": 10}, "p": {"a": 1, "k": [{"i": {"x": 0.667, "y": 1}, "o": {"x": 0.333, "y": 0}, "t": 23, "s": [187, 320, 0], "to": [0.718, 0, 0], "ti": [0.819, 0, 0]}, {"t": 26, "s": [194.727, 320, 0]}], "ix": 2, "l": 2}, "a": {"a": 0, "k": [-7.194, 40.355, 0], "ix": 1, "l": 2}, "s": {"a": 1, "k": [{"i": {"x": [0.102, 0.102, 0.667], "y": [1, 1, 1]}, "o": {"x": [0.913, 0.913, 0.333], "y": [0, 0, 0]}, "t": 21, "s": [99.483, 98.384, 100]}, {"t": 35, "s": [0.038, 98.384, 100]}], "ix": 6, "l": 2}}, "ao": 0, "shapes": [{"ty": "gr", "it": [{"ind": 0, "ty": "sh", "ix": 1, "ks": {"a": 0, "k": {"i": [[-32.529, 0], [0, -32.529], [32.529, 0], [0, 32.529]], "o": [[32.529, 0], [0, 32.529], [-32.529, 0], [0, -32.529]], "v": [[0, -58.9], [58.9, 0], [0, 58.9], [-58.9, 0]], "c": true}, "ix": 2}, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false}, {"ty": "fl", "c": {"a": 0, "k": [0.227450980392, 0.286274509804, 0.125490196078, 1], "ix": 4}, "o": {"a": 0, "k": 100, "ix": 5}, "r": 1, "bm": 0, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false}, {"ty": "tr", "p": {"a": 0, "k": [-7.194, 40.355], "ix": 2}, "a": {"a": 0, "k": [0, 0], "ix": 1}, "s": {"a": 0, "k": [103.006, 103.006], "ix": 3}, "r": {"a": 0, "k": 0, "ix": 6}, "o": {"a": 0, "k": 100, "ix": 7}, "sk": {"a": 0, "k": 0, "ix": 4}, "sa": {"a": 0, "k": 0, "ix": 5}, "nm": "Transform"}], "nm": "Ellipse 1", "np": 3, "cix": 2, "bm": 0, "ix": 1, "mn": "ADBE Vector Group", "hd": false}], "ip": 0, "op": 35, "st": 19, "bm": 0}, {"ddd": 0, "ind": 12, "ty": 4, "nm": "Shape Layer 9", "sr": 1, "ks": {"o": {"a": 0, "k": 100, "ix": 11}, "r": {"a": 0, "k": 0, "ix": 10}, "p": {"a": 0, "k": [187.5, 320, 0], "ix": 2, "l": 2}, "a": {"a": 0, "k": [0, 0, 0], "ix": 1, "l": 2}, "s": {"a": 0, "k": [100, 102.19, 100], "ix": 6, "l": 2}}, "ao": 0, "shapes": [{"ty": "gr", "it": [{"ind": 0, "ty": "sh", "ix": 1, "ks": {"a": 1, "k": [{"i": {"x": 0.833, "y": 0.833}, "o": {"x": 0.167, "y": 0.167}, "t": 27, "s": [{"i": [[0, 0], [0, 0], [0, 0], [0, 0]], "o": [[0, 0], [0, 0], [0, 0], [0, 0]], "v": [[4.763, -59.32], [4.692, 57.946], [-4.5, 57.946], [-4.429, -59.32]], "c": true}]}, {"i": {"x": 0.833, "y": 0.833}, "o": {"x": 0.167, "y": 0.167}, "t": 28, "s": [{"i": [[0, 0], [0, 0], [0, 0], [0, 0]], "o": [[0, 0], [0, 0], [0, 0], [0, 0]], "v": [[4.096, -59.32], [4.025, 57.946], [-6.5, 58.004], [-6.429, -59.262]], "c": true}]}, {"i": {"x": 0.833, "y": 0.833}, "o": {"x": 0.167, "y": 0.167}, "t": 29, "s": [{"i": [[0, 0], [0, 0], [0, 0], [0, 0]], "o": [[0, 0], [0, 0], [0, 0], [0, 0]], "v": [[4.929, -59.273], [4.859, 57.993], [-5.5, 58.027], [-5.429, -59.239]], "c": true}]}, {"i": {"x": 0.833, "y": 0.833}, "o": {"x": 0.167, "y": 0.167}, "t": 30, "s": [{"i": [[0, 0], [0, 0], [0, 0], [0, 0]], "o": [[0, 0], [0, 0], [0, 0], [0, 0]], "v": [[5.804, -59.22], [5.734, 58.045], [-5.5, 58.027], [-5.429, -59.239]], "c": true}]}, {"i": {"x": 0.833, "y": 0.833}, "o": {"x": 0.167, "y": 0.167}, "t": 31, "s": [{"i": [[0, 0], [0, 0], [0, 0], [0, 0]], "o": [[0, 0], [0, 0], [0, 0], [0, 0]], "v": [[5.804, -59.22], [5.734, 58.045], [-5.5, 58.027], [-5.429, -59.239]], "c": true}]}, {"i": {"x": 0.833, "y": 0.833}, "o": {"x": 0.167, "y": 0.167}, "t": 33, "s": [{"i": [[0, 0], [0, 0], [0, 0], [0, 0]], "o": [[0, 0], [0, 0], [0, 0], [0, 0]], "v": [[5.804, -59.22], [5.734, 58.045], [-5.5, 58.027], [-5.429, -59.239]], "c": true}]}, {"i": {"x": 0.833, "y": 0.833}, "o": {"x": 0.167, "y": 0.167}, "t": 35, "s": [{"i": [[0, 0], [0, 0], [0, 0], [0, 0]], "o": [[0, 0], [0, 0], [0, 0], [0, 0]], "v": [[8.429, -59.278], [8.359, 57.988], [-5.5, 58.027], [-5.429, -59.239]], "c": true}]}, {"i": {"x": 0.833, "y": 0.833}, "o": {"x": 0.167, "y": 0.167}, "t": 36, "s": [{"i": [[0, 0], [0, 0], [0, 0], [0, 0]], "o": [[0, 0], [0, 0], [0, 0], [0, 0]], "v": [[8.429, -59.27], [8.359, 57.996], [-5.875, 58.074], [-5.804, -59.192]], "c": true}]}, {"i": {"x": 0.833, "y": 0.833}, "o": {"x": 0.167, "y": 0.167}, "t": 37, "s": [{"i": [[0, 0], [0, 0], [0, 0], [0, 0]], "o": [[0, 0], [0, 0], [0, 0], [0, 0]], "v": [[8.429, -59.27], [8.359, 57.996], [-5.875, 58.074], [-5.804, -59.192]], "c": true}]}, {"i": {"x": 0.833, "y": 0.833}, "o": {"x": 0.167, "y": 0.167}, "t": 38, "s": [{"i": [[0, 0], [0, 0], [0, 0], [0, 0]], "o": [[0, 0], [0, 0], [0, 0], [0, 0]], "v": [[8.429, -59.27], [8.359, 57.996], [-6.5, 58.046], [-6.429, -59.22]], "c": true}]}, {"i": {"x": 0.833, "y": 0.833}, "o": {"x": 0.167, "y": 0.167}, "t": 39, "s": [{"i": [[0, 0], [0, 0], [0, 0], [0, 0]], "o": [[0, 0], [0, 0], [0, 0], [0, 0]], "v": [[8.054, -59.32], [7.984, 57.946], [-6.5, 58.046], [-6.429, -59.22]], "c": true}]}, {"i": {"x": 0.833, "y": 0.833}, "o": {"x": 0.167, "y": 0.167}, "t": 40, "s": [{"i": [[0, 0], [0, 0], [0, 0], [0, 0]], "o": [[0, 0], [0, 0], [0, 0], [0, 0]], "v": [[7.929, -59.254], [7.859, 58.012], [-6.5, 58.046], [-6.429, -59.22]], "c": true}]}, {"i": {"x": 0.833, "y": 0.833}, "o": {"x": 0.167, "y": 0.167}, "t": 41, "s": [{"i": [[0, 0], [0, 0], [0, 0], [0, 0]], "o": [[0, 0], [0, 0], [0, 0], [0, 0]], "v": [[7.054, -59.222], [6.984, 58.044], [-6.5, 58.046], [-6.429, -59.22]], "c": true}]}, {"i": {"x": 0.833, "y": 0.833}, "o": {"x": 0.167, "y": 0.167}, "t": 42, "s": [{"i": [[0, 0], [0, 0], [0, 0], [0, 0]], "o": [[0, 0], [0, 0], [0, 0], [0, 0]], "v": [[6.554, -59.281], [6.484, 57.985], [-6.5, 58.046], [-6.429, -59.22]], "c": true}]}, {"i": {"x": 0.833, "y": 0.833}, "o": {"x": 0.167, "y": 0.167}, "t": 43, "s": [{"i": [[0, 0], [0, 0], [0, 0], [0, 0]], "o": [[0, 0], [0, 0], [0, 0], [0, 0]], "v": [[5.179, -59.276], [5.109, 57.99], [-6.5, 58.046], [-6.429, -59.22]], "c": true}]}, {"t": 44, "s": [{"i": [[0, 0], [0, 0], [0, 0], [0, 0]], "o": [[0, 0], [0, 0], [0, 0], [0, 0]], "v": [[2.179, -59.267], [2.109, 57.999], [-7.875, 57.989], [-7.804, -59.277]], "c": true}]}], "ix": 2}, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false}, {"ty": "fl", "c": {"a": 0, "k": [0.227450980392, 0.286274509804, 0.125490196078, 1], "ix": 4}, "o": {"a": 0, "k": 100, "ix": 5}, "r": 1, "bm": 0, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false}, {"ty": "tr", "p": {"a": 0, "k": [1.358, 0.548], "ix": 2}, "a": {"a": 0, "k": [0, 0], "ix": 1}, "s": {"a": 0, "k": [100, 100], "ix": 3}, "r": {"a": 0, "k": 0, "ix": 6}, "o": {"a": 0, "k": 100, "ix": 7}, "sk": {"a": 0, "k": 0, "ix": 4}, "sa": {"a": 0, "k": 0, "ix": 5}, "nm": "Transform"}], "nm": "Rectangle 1", "np": 3, "cix": 2, "bm": 0, "ix": 1, "mn": "ADBE Vector Group", "hd": false}], "ip": 27, "op": 45, "st": 19, "bm": 0}]}], "fonts": {"list": [{"fFamily": "Roboto", "fWeight": "", "fStyle": "Bold", "fName": "Roboto-Bold", "ascent": 75}]}, "layers": [{"ddd": 0, "ind": 1, "ty": 3, "nm": "Null 33", "sr": 1, "ks": {"o": {"a": 0, "k": 0, "ix": 11}, "r": {"a": 0, "k": 0, "ix": 10}, "p": {"a": 0, "k": [187, 338.308, 0], "ix": 2, "l": 2}, "a": {"a": 0, "k": [50, 50, 0], "ix": 1, "l": 2}, "s": {"a": 1, "k": [{"i": {"x": [0.177, 0.177, 0.667], "y": [1, 1, 1]}, "o": {"x": [0.978, 0.978, 0.333], "y": [0, 0, 0]}, "t": 181, "s": [87, 87, 100]}, {"t": 192, "s": [69, 69, 100]}], "ix": 6, "l": 2}}, "ao": 0, "ip": 0, "op": 192, "st": 0, "bm": 0}, {"ddd": 0, "ind": 2, "ty": 4, "nm": "Star 01 Outlines 6", "sr": 1, "ks": {"o": {"a": 0, "k": 100, "ix": 11}, "r": {"a": 1, "k": [{"i": {"x": [0.206], "y": [1]}, "o": {"x": [0.871], "y": [0]}, "t": 73, "s": [0]}, {"t": 86, "s": [180]}], "ix": 10}, "p": {"a": 0, "k": [301.861, 447.036, 0], "ix": 2, "l": 2}, "a": {"a": 0, "k": [214.794, 59, 0], "ix": 1, "l": 2}, "s": {"a": 1, "k": [{"i": {"x": [0.667, 0.667, 0.667], "y": [1, 1, 1]}, "o": {"x": [0.333, 0.333, 0.333], "y": [0, 0, 0]}, "t": 73, "s": [0, 0, 100]}, {"i": {"x": [0.667, 0.667, 0.667], "y": [1, 1, 1]}, "o": {"x": [0.333, 0.333, 0.333], "y": [0, 0, 0]}, "t": 81, "s": [56.55, 56.55, 100]}, {"i": {"x": [0.266, 0.266, 0.667], "y": [1, 1, 1]}, "o": {"x": [0.85, 0.85, 0.333], "y": [0, 0, 0]}, "t": 96, "s": [56.55, 56.55, 100]}, {"i": {"x": [0.204, 0.204, 0.667], "y": [1, 1, 1]}, "o": {"x": [0.817, 0.817, 0.333], "y": [0, 0, 0]}, "t": 103, "s": [76.55, 76.55, 100]}, {"t": 111, "s": [0, 0, 100]}], "ix": 6, "l": 2}}, "ao": 0, "shapes": [{"ty": "gr", "it": [{"ind": 0, "ty": "sh", "ix": 1, "ks": {"a": 0, "k": {"i": [[2.909, 14.164], [13.228, -3.377], [-1.576, -0.397], [-1.486, -0.892], [-1.413, -9.293], [-13.687, 2.692]], "o": [[-3.214, 14.29], [2.089, 0.414], [2.467, 0.621], [3.953, 2.35], [2.711, -15.002], [-13.57, -3.467]], "v": [[-0.001, -19], [-17.794, 0], [-12.318, 1.198], [-6.475, 3.359], [-0.001, 19], [17.794, 0]], "c": true}, "ix": 2}, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false}, {"ty": "fl", "c": {"a": 0, "k": [1, 1, 1, 1], "ix": 4}, "o": {"a": 0, "k": 100, "ix": 5}, "r": 1, "bm": 0, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false}, {"ty": "tr", "p": {"a": 0, "k": [214.794, 59], "ix": 2}, "a": {"a": 0, "k": [0, 0], "ix": 1}, "s": {"a": 0, "k": [100, 100], "ix": 3}, "r": {"a": 0, "k": 0, "ix": 6}, "o": {"a": 0, "k": 100, "ix": 7}, "sk": {"a": 0, "k": 0, "ix": 4}, "sa": {"a": 0, "k": 0, "ix": 5}, "nm": "Transform"}], "nm": "Group 1", "np": 2, "cix": 2, "bm": 0, "ix": 1, "mn": "ADBE Vector Group", "hd": false}], "ip": 73, "op": 112, "st": 60, "bm": 0}, {"ddd": 0, "ind": 3, "ty": 4, "nm": "Star 01 Outlines 5", "sr": 1, "ks": {"o": {"a": 0, "k": 100, "ix": 11}, "r": {"a": 1, "k": [{"i": {"x": [0.206], "y": [1]}, "o": {"x": [0.871], "y": [0]}, "t": 17, "s": [0]}, {"t": 30, "s": [180]}], "ix": 10}, "p": {"a": 0, "k": [138.822, 379.597, 0], "ix": 2, "l": 2}, "a": {"a": 0, "k": [214.794, 59, 0], "ix": 1, "l": 2}, "s": {"a": 1, "k": [{"i": {"x": [0.667, 0.667, 0.667], "y": [1, 1, 1]}, "o": {"x": [0.333, 0.333, 0.333], "y": [0, 0, 0]}, "t": 17, "s": [0, 0, 100]}, {"i": {"x": [0.667, 0.667, 0.667], "y": [1, 1, 1]}, "o": {"x": [0.333, 0.333, 0.333], "y": [0, 0, 0]}, "t": 25, "s": [56.55, 56.55, 100]}, {"i": {"x": [0.667, 0.667, 0.667], "y": [1, 1, 1]}, "o": {"x": [0.333, 0.333, 0.333], "y": [0, 0, 0]}, "t": 40, "s": [56.55, 56.55, 100]}, {"t": 48, "s": [0, 0, 100]}], "ix": 6, "l": 2}}, "ao": 0, "shapes": [{"ty": "gr", "it": [{"ind": 0, "ty": "sh", "ix": 1, "ks": {"a": 0, "k": {"i": [[2.909, 14.164], [13.228, -3.377], [-1.576, -0.397], [-1.486, -0.892], [-1.413, -9.293], [-13.687, 2.692]], "o": [[-3.214, 14.29], [2.089, 0.414], [2.467, 0.621], [3.953, 2.35], [2.711, -15.002], [-13.57, -3.467]], "v": [[-0.001, -19], [-17.794, 0], [-12.318, 1.198], [-6.475, 3.359], [-0.001, 19], [17.794, 0]], "c": true}, "ix": 2}, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false}, {"ty": "fl", "c": {"a": 0, "k": [1, 1, 1, 1], "ix": 4}, "o": {"a": 0, "k": 100, "ix": 5}, "r": 1, "bm": 0, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false}, {"ty": "tr", "p": {"a": 0, "k": [214.794, 59], "ix": 2}, "a": {"a": 0, "k": [0, 0], "ix": 1}, "s": {"a": 0, "k": [100, 100], "ix": 3}, "r": {"a": 0, "k": 0, "ix": 6}, "o": {"a": 0, "k": 100, "ix": 7}, "sk": {"a": 0, "k": 0, "ix": 4}, "sa": {"a": 0, "k": 0, "ix": 5}, "nm": "Transform"}], "nm": "Group 1", "np": 2, "cix": 2, "bm": 0, "ix": 1, "mn": "ADBE Vector Group", "hd": false}], "ip": 17, "op": 52, "st": 0, "bm": 0}, {"ddd": 0, "ind": 4, "ty": 4, "nm": "Star 01 Outlines 8", "sr": 1, "ks": {"o": {"a": 0, "k": 100, "ix": 11}, "r": {"a": 1, "k": [{"i": {"x": [0.206], "y": [1]}, "o": {"x": [0.871], "y": [0]}, "t": 124, "s": [0]}, {"t": 137, "s": [180]}], "ix": 10}, "p": {"a": 0, "k": [226.029, 193.863, 0], "ix": 2, "l": 2}, "a": {"a": 0, "k": [214.794, 59, 0], "ix": 1, "l": 2}, "s": {"a": 1, "k": [{"i": {"x": [0.667, 0.667, 0.667], "y": [1, 1, 1]}, "o": {"x": [0.333, 0.333, 0.333], "y": [0, 0, 0]}, "t": 124, "s": [0, 0, 100]}, {"i": {"x": [0.667, 0.667, 0.667], "y": [1, 1, 1]}, "o": {"x": [0.333, 0.333, 0.333], "y": [0, 0, 0]}, "t": 132, "s": [68.515, 68.515, 100]}, {"i": {"x": [0, 0, 0.667], "y": [1.017, 1.017, 1]}, "o": {"x": [0.679, 0.679, 0.333], "y": [-0.011, -0.011, 0]}, "t": 145, "s": [68.515, 68.515, 100]}, {"i": {"x": [0.08, 0.08, 0.667], "y": [1, 1, 1]}, "o": {"x": [0.705, 0.705, 0.333], "y": [0, 0, 0]}, "t": 151, "s": [75.035, 75.035, 100]}, {"t": 162, "s": [0, 0, 100]}], "ix": 6, "l": 2}}, "ao": 0, "shapes": [{"ty": "gr", "it": [{"ind": 0, "ty": "sh", "ix": 1, "ks": {"a": 0, "k": {"i": [[2.909, 14.164], [13.228, -3.377], [-1.576, -0.397], [-1.486, -0.892], [-1.413, -9.293], [-13.687, 2.692]], "o": [[-3.214, 14.29], [2.089, 0.414], [2.467, 0.621], [3.953, 2.35], [2.711, -15.002], [-13.57, -3.467]], "v": [[-0.001, -19], [-17.794, 0], [-12.318, 1.198], [-6.475, 3.359], [-0.001, 19], [17.794, 0]], "c": true}, "ix": 2}, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false}, {"ty": "fl", "c": {"a": 0, "k": [1, 1, 1, 1], "ix": 4}, "o": {"a": 0, "k": 100, "ix": 5}, "r": 1, "bm": 0, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false}, {"ty": "tr", "p": {"a": 0, "k": [214.794, 59], "ix": 2}, "a": {"a": 0, "k": [0, 0], "ix": 1}, "s": {"a": 0, "k": [100, 100], "ix": 3}, "r": {"a": 0, "k": 0, "ix": 6}, "o": {"a": 0, "k": 100, "ix": 7}, "sk": {"a": 0, "k": 0, "ix": 4}, "sa": {"a": 0, "k": 0, "ix": 5}, "nm": "Transform"}], "nm": "Group 1", "np": 2, "cix": 2, "bm": 0, "ix": 1, "mn": "ADBE Vector Group", "hd": false}], "ip": 124, "op": 163, "st": 107, "bm": 0}, {"ddd": 0, "ind": 5, "ty": 4, "nm": "Star 01 Outlines 4", "sr": 1, "ks": {"o": {"a": 0, "k": 100, "ix": 11}, "r": {"a": 1, "k": [{"i": {"x": [0.206], "y": [1]}, "o": {"x": [0.871], "y": [0]}, "t": 9, "s": [0]}, {"t": 22, "s": [180]}], "ix": 10}, "p": {"a": 0, "k": [233.537, 264.983, 0], "ix": 2, "l": 2}, "a": {"a": 0, "k": [214.794, 59, 0], "ix": 1, "l": 2}, "s": {"a": 1, "k": [{"i": {"x": [0.667, 0.667, 0.667], "y": [1, 1, 1]}, "o": {"x": [0.333, 0.333, 0.333], "y": [0, 0, 0]}, "t": 9, "s": [0, 0, 100]}, {"i": {"x": [0.667, 0.667, 0.667], "y": [1, 1, 1]}, "o": {"x": [0.333, 0.333, 0.333], "y": [0, 0, 0]}, "t": 17, "s": [68.515, 68.515, 100]}, {"i": {"x": [0, 0, 0.667], "y": [1.017, 1.017, 1]}, "o": {"x": [0.679, 0.679, 0.333], "y": [-0.011, -0.011, 0]}, "t": 30, "s": [68.515, 68.515, 100]}, {"i": {"x": [0.08, 0.08, 0.667], "y": [1, 1, 1]}, "o": {"x": [0.705, 0.705, 0.333], "y": [0, 0, 0]}, "t": 36, "s": [75.035, 75.035, 100]}, {"t": 47, "s": [0, 0, 100]}], "ix": 6, "l": 2}}, "ao": 0, "shapes": [{"ty": "gr", "it": [{"ind": 0, "ty": "sh", "ix": 1, "ks": {"a": 0, "k": {"i": [[2.909, 14.164], [13.228, -3.377], [-1.576, -0.397], [-1.486, -0.892], [-1.413, -9.293], [-13.687, 2.692]], "o": [[-3.214, 14.29], [2.089, 0.414], [2.467, 0.621], [3.953, 2.35], [2.711, -15.002], [-13.57, -3.467]], "v": [[-0.001, -19], [-17.794, 0], [-12.318, 1.198], [-6.475, 3.359], [-0.001, 19], [17.794, 0]], "c": true}, "ix": 2}, "nm": "Path 1", "mn": "ADBE Vector Shape - Group", "hd": false}, {"ty": "fl", "c": {"a": 0, "k": [1, 1, 1, 1], "ix": 4}, "o": {"a": 0, "k": 100, "ix": 5}, "r": 1, "bm": 0, "nm": "Fill 1", "mn": "ADBE Vector Graphic - Fill", "hd": false}, {"ty": "tr", "p": {"a": 0, "k": [214.794, 59], "ix": 2}, "a": {"a": 0, "k": [0, 0], "ix": 1}, "s": {"a": 0, "k": [100, 100], "ix": 3}, "r": {"a": 0, "k": 0, "ix": 6}, "o": {"a": 0, "k": 100, "ix": 7}, "sk": {"a": 0, "k": 0, "ix": 4}, "sa": {"a": 0, "k": 0, "ix": 5}, "nm": "Transform"}], "nm": "Group 1", "np": 2, "cix": 2, "bm": 0, "ix": 1, "mn": "ADBE Vector Group", "hd": false}], "ip": 9, "op": 48, "st": -8, "bm": 0}, {"ddd": 0, "ind": 6, "ty": 0, "nm": "Fillpping coin_01", "parent": 1, "refId": "comp_0", "sr": 1, "ks": {"o": {"a": 1, "k": [{"i": {"x": [0.177], "y": [1]}, "o": {"x": [0.978], "y": [0]}, "t": 184, "s": [100]}, {"t": 191, "s": [0]}], "ix": 11}, "r": {"a": 0, "k": 0, "ix": 10}, "p": {"a": 0, "k": [141.281, -28.624, 0], "ix": 2, "l": 2}, "a": {"a": 0, "k": [187.5, 320, 0], "ix": 1, "l": 2}, "s": {"a": 0, "k": [60, 60, 100], "ix": 6, "l": 2}}, "ao": 0, "hasMask": true, "masksProperties": [{"inv": false, "mode": "a", "pt": {"a": 0, "k": {"i": [[0, 0], [0, 0], [0, 0], [0, 0]], "o": [[0, 0], [0, 0], [0, 0], [0, 0]], "v": [[258.005, 249.132], [115.162, 249.132], [115.162, 391.974], [258.005, 391.974]], "c": true}, "ix": 1}, "o": {"a": 0, "k": 100, "ix": 3}, "x": {"a": 0, "k": 0, "ix": 4}, "nm": "Mask 1"}], "w": 375, "h": 640, "ip": 165, "op": 222, "st": 161, "bm": 0}, {"ddd": 0, "ind": 7, "ty": 0, "nm": "Fillpping coin_01", "parent": 1, "refId": "comp_0", "sr": 1, "ks": {"o": {"a": 1, "k": [{"i": {"x": [0.177], "y": [1]}, "o": {"x": [0.978], "y": [0]}, "t": 184, "s": [100]}, {"t": 191, "s": [0]}], "ix": 11}, "r": {"a": 0, "k": 0, "ix": 10}, "p": {"a": 0, "k": [171.947, 52.376, 0], "ix": 2, "l": 2}, "a": {"a": 0, "k": [187.5, 320, 0], "ix": 1, "l": 2}, "s": {"a": 0, "k": [60, 60, 100], "ix": 6, "l": 2}}, "ao": 0, "hasMask": true, "masksProperties": [{"inv": false, "mode": "a", "pt": {"a": 0, "k": {"i": [[0, 0], [0, 0], [0, 0], [0, 0]], "o": [[0, 0], [0, 0], [0, 0], [0, 0]], "v": [[258.005, 249.132], [115.162, 249.132], [115.162, 391.974], [258.005, 391.974]], "c": true}, "ix": 1}, "o": {"a": 0, "k": 100, "ix": 3}, "x": {"a": 0, "k": 0, "ix": 4}, "nm": "Mask 1"}], "w": 375, "h": 640, "ip": 165, "op": 222, "st": 158, "bm": 0}, {"ddd": 0, "ind": 8, "ty": 0, "nm": "Fillpping coin_01", "parent": 1, "refId": "comp_0", "sr": 1, "ks": {"o": {"a": 1, "k": [{"i": {"x": [0.177], "y": [1]}, "o": {"x": [0.978], "y": [0]}, "t": 184, "s": [100]}, {"t": 191, "s": [0]}], "ix": 11}, "r": {"a": 0, "k": 0, "ix": 10}, "p": {"a": 0, "k": [141.281, 133.043, 0], "ix": 2, "l": 2}, "a": {"a": 0, "k": [187.5, 320, 0], "ix": 1, "l": 2}, "s": {"a": 0, "k": [60, 60, 100], "ix": 6, "l": 2}}, "ao": 0, "hasMask": true, "masksProperties": [{"inv": false, "mode": "a", "pt": {"a": 0, "k": {"i": [[0, 0], [0, 0], [0, 0], [0, 0]], "o": [[0, 0], [0, 0], [0, 0], [0, 0]], "v": [[258.005, 249.132], [115.162, 249.132], [115.162, 391.974], [258.005, 391.974]], "c": true}, "ix": 1}, "o": {"a": 0, "k": 100, "ix": 3}, "x": {"a": 0, "k": 0, "ix": 4}, "nm": "Mask 1"}], "w": 375, "h": 640, "ip": 165, "op": 222, "st": 155, "bm": 0}, {"ddd": 0, "ind": 9, "ty": 0, "nm": "Fillpping coin_01", "parent": 1, "refId": "comp_0", "sr": 1, "ks": {"o": {"a": 1, "k": [{"i": {"x": [0.177], "y": [1]}, "o": {"x": [0.978], "y": [0]}, "t": 184, "s": [100]}, {"t": 191, "s": [0]}], "ix": 11}, "r": {"a": 0, "k": 0, "ix": 10}, "p": {"a": 0, "k": [50.3, 175.403, 0], "ix": 2, "l": 2}, "a": {"a": 0, "k": [187.5, 320, 0], "ix": 1, "l": 2}, "s": {"a": 0, "k": [60, 60, 100], "ix": 6, "l": 2}}, "ao": 0, "hasMask": true, "masksProperties": [{"inv": false, "mode": "a", "pt": {"a": 0, "k": {"i": [[0, 0], [0, 0], [0, 0], [0, 0]], "o": [[0, 0], [0, 0], [0, 0], [0, 0]], "v": [[258.005, 249.132], [115.162, 249.132], [115.162, 391.974], [258.005, 391.974]], "c": true}, "ix": 1}, "o": {"a": 0, "k": 100, "ix": 3}, "x": {"a": 0, "k": 0, "ix": 4}, "nm": "Mask 1"}], "w": 375, "h": 640, "ip": 165, "op": 222, "st": 152, "bm": 0}, {"ddd": 0, "ind": 10, "ty": 0, "nm": "Fillpping coin_01", "parent": 1, "refId": "comp_0", "sr": 1, "ks": {"o": {"a": 1, "k": [{"i": {"x": [0.177], "y": [1]}, "o": {"x": [0.978], "y": [0]}, "t": 184, "s": [100]}, {"t": 191, "s": [0]}], "ix": 11}, "r": {"a": 0, "k": 0, "ix": 10}, "p": {"a": 0, "k": [-41.386, 133.043, 0], "ix": 2, "l": 2}, "a": {"a": 0, "k": [187.5, 320, 0], "ix": 1, "l": 2}, "s": {"a": 0, "k": [60, 60, 100], "ix": 6, "l": 2}}, "ao": 0, "hasMask": true, "masksProperties": [{"inv": false, "mode": "a", "pt": {"a": 0, "k": {"i": [[0, 0], [0, 0], [0, 0], [0, 0]], "o": [[0, 0], [0, 0], [0, 0], [0, 0]], "v": [[258.005, 249.132], [115.162, 249.132], [115.162, 391.974], [258.005, 391.974]], "c": true}, "ix": 1}, "o": {"a": 0, "k": 100, "ix": 3}, "x": {"a": 0, "k": 0, "ix": 4}, "nm": "Mask 1"}], "w": 375, "h": 640, "ip": 165, "op": 222, "st": 149, "bm": 0}, {"ddd": 0, "ind": 11, "ty": 0, "nm": "Fillpping coin_01", "parent": 1, "refId": "comp_0", "sr": 1, "ks": {"o": {"a": 1, "k": [{"i": {"x": [0.177], "y": [1]}, "o": {"x": [0.978], "y": [0]}, "t": 184, "s": [100]}, {"t": 191, "s": [0]}], "ix": 11}, "r": {"a": 0, "k": 0, "ix": 10}, "p": {"a": 0, "k": [-70.719, 52.376, 0], "ix": 2, "l": 2}, "a": {"a": 0, "k": [187.5, 320, 0], "ix": 1, "l": 2}, "s": {"a": 0, "k": [60, 60, 100], "ix": 6, "l": 2}}, "ao": 0, "hasMask": true, "masksProperties": [{"inv": false, "mode": "a", "pt": {"a": 0, "k": {"i": [[0, 0], [0, 0], [0, 0], [0, 0]], "o": [[0, 0], [0, 0], [0, 0], [0, 0]], "v": [[258.005, 249.132], [115.162, 249.132], [115.162, 391.974], [258.005, 391.974]], "c": true}, "ix": 1}, "o": {"a": 0, "k": 100, "ix": 3}, "x": {"a": 0, "k": 0, "ix": 4}, "nm": "Mask 1"}], "w": 375, "h": 640, "ip": 165, "op": 222, "st": 146, "bm": 0}, {"ddd": 0, "ind": 12, "ty": 0, "nm": "Fillpping coin_01", "parent": 1, "refId": "comp_0", "sr": 1, "ks": {"o": {"a": 1, "k": [{"i": {"x": [0.177], "y": [1]}, "o": {"x": [0.978], "y": [0]}, "t": 184, "s": [100]}, {"t": 191, "s": [0]}], "ix": 11}, "r": {"a": 0, "k": 0, "ix": 10}, "p": {"a": 0, "k": [-41.386, -28.624, 0], "ix": 2, "l": 2}, "a": {"a": 0, "k": [187.5, 320, 0], "ix": 1, "l": 2}, "s": {"a": 0, "k": [60, 60, 100], "ix": 6, "l": 2}}, "ao": 0, "hasMask": true, "masksProperties": [{"inv": false, "mode": "a", "pt": {"a": 0, "k": {"i": [[0, 0], [0, 0], [0, 0], [0, 0]], "o": [[0, 0], [0, 0], [0, 0], [0, 0]], "v": [[258.005, 249.132], [115.162, 249.132], [115.162, 391.974], [258.005, 391.974]], "c": true}, "ix": 1}, "o": {"a": 0, "k": 100, "ix": 3}, "x": {"a": 0, "k": 0, "ix": 4}, "nm": "Mask 1"}], "w": 375, "h": 640, "ip": 165, "op": 222, "st": 143, "bm": 0}, {"ddd": 0, "ind": 13, "ty": 0, "nm": "Fillpping coin_01", "parent": 1, "refId": "comp_0", "sr": 1, "ks": {"o": {"a": 1, "k": [{"i": {"x": [0.177], "y": [1]}, "o": {"x": [0.978], "y": [0]}, "t": 184, "s": [100]}, {"t": 191, "s": [0]}], "ix": 11}, "r": {"a": 0, "k": 0, "ix": 10}, "p": {"a": 0, "k": [50.3, -67.891, 0], "ix": 2, "l": 2}, "a": {"a": 0, "k": [187.5, 320, 0], "ix": 1, "l": 2}, "s": {"a": 0, "k": [60, 60, 100], "ix": 6, "l": 2}}, "ao": 0, "hasMask": true, "masksProperties": [{"inv": false, "mode": "a", "pt": {"a": 0, "k": {"i": [[0, 0], [0, 0], [0, 0], [0, 0]], "o": [[0, 0], [0, 0], [0, 0], [0, 0]], "v": [[258.005, 249.132], [115.162, 249.132], [115.162, 391.974], [258.005, 391.974]], "c": true}, "ix": 1}, "o": {"a": 0, "k": 100, "ix": 3}, "x": {"a": 0, "k": 0, "ix": 4}, "nm": "Mask 1"}], "w": 375, "h": 640, "ip": 165, "op": 222, "st": 140, "bm": 0}, {"ddd": 0, "ind": 14, "ty": 0, "nm": "Fillpping coin_01", "refId": "comp_0", "sr": 1, "ks": {"o": {"a": 0, "k": 100, "ix": 11}, "r": {"a": 0, "k": 0, "ix": 10}, "p": {"a": 0, "k": [266.414, 269.906, 0], "ix": 2, "l": 2}, "a": {"a": 0, "k": [187.5, 320, 0], "ix": 1, "l": 2}, "s": {"a": 0, "k": [52.2, 52.2, 100], "ix": 6, "l": 2}}, "ao": 0, "hasMask": true, "masksProperties": [{"inv": false, "mode": "a", "pt": {"a": 0, "k": {"i": [[0, 0], [0, 0], [0, 0], [0, 0]], "o": [[0, 0], [0, 0], [0, 0], [0, 0]], "v": [[258.005, 249.132], [115.162, 249.132], [115.162, 391.974], [258.005, 391.974]], "c": true}, "ix": 1}, "o": {"a": 0, "k": 100, "ix": 3}, "x": {"a": 0, "k": 0, "ix": 4}, "nm": "Mask 1"}], "w": 375, "h": 640, "ip": 115, "op": 165, "st": 111, "bm": 0}, {"ddd": 0, "ind": 15, "ty": 0, "nm": "Fillpping coin_01", "refId": "comp_0", "sr": 1, "ks": {"o": {"a": 0, "k": 100, "ix": 11}, "r": {"a": 0, "k": 0, "ix": 10}, "p": {"a": 0, "k": [293.094, 340.376, 0], "ix": 2, "l": 2}, "a": {"a": 0, "k": [187.5, 320, 0], "ix": 1, "l": 2}, "s": {"a": 0, "k": [52.2, 52.2, 100], "ix": 6, "l": 2}}, "ao": 0, "hasMask": true, "masksProperties": [{"inv": false, "mode": "a", "pt": {"a": 0, "k": {"i": [[0, 0], [0, 0], [0, 0], [0, 0]], "o": [[0, 0], [0, 0], [0, 0], [0, 0]], "v": [[258.005, 249.132], [115.162, 249.132], [115.162, 391.974], [258.005, 391.974]], "c": true}, "ix": 1}, "o": {"a": 0, "k": 100, "ix": 3}, "x": {"a": 0, "k": 0, "ix": 4}, "nm": "Mask 1"}], "w": 375, "h": 640, "ip": 115, "op": 165, "st": 108, "bm": 0}, {"ddd": 0, "ind": 16, "ty": 0, "nm": "Fillpping coin_01", "refId": "comp_0", "sr": 1, "ks": {"o": {"a": 0, "k": 100, "ix": 11}, "r": {"a": 0, "k": 0, "ix": 10}, "p": {"a": 0, "k": [266.414, 410.556, 0], "ix": 2, "l": 2}, "a": {"a": 0, "k": [187.5, 320, 0], "ix": 1, "l": 2}, "s": {"a": 0, "k": [52.2, 52.2, 100], "ix": 6, "l": 2}}, "ao": 0, "hasMask": true, "masksProperties": [{"inv": false, "mode": "a", "pt": {"a": 0, "k": {"i": [[0, 0], [0, 0], [0, 0], [0, 0]], "o": [[0, 0], [0, 0], [0, 0], [0, 0]], "v": [[258.005, 249.132], [115.162, 249.132], [115.162, 391.974], [258.005, 391.974]], "c": true}, "ix": 1}, "o": {"a": 0, "k": 100, "ix": 3}, "x": {"a": 0, "k": 0, "ix": 4}, "nm": "Mask 1"}], "w": 375, "h": 640, "ip": 115, "op": 165, "st": 105, "bm": 0}, {"ddd": 0, "ind": 17, "ty": 0, "nm": "Fillpping coin_01", "refId": "comp_0", "sr": 1, "ks": {"o": {"a": 0, "k": 100, "ix": 11}, "r": {"a": 0, "k": 0, "ix": 10}, "p": {"a": 0, "k": [187.261, 447.409, 0], "ix": 2, "l": 2}, "a": {"a": 0, "k": [187.5, 320, 0], "ix": 1, "l": 2}, "s": {"a": 0, "k": [52.2, 52.2, 100], "ix": 6, "l": 2}}, "ao": 0, "hasMask": true, "masksProperties": [{"inv": false, "mode": "a", "pt": {"a": 0, "k": {"i": [[0, 0], [0, 0], [0, 0], [0, 0]], "o": [[0, 0], [0, 0], [0, 0], [0, 0]], "v": [[258.005, 249.132], [115.162, 249.132], [115.162, 391.974], [258.005, 391.974]], "c": true}, "ix": 1}, "o": {"a": 0, "k": 100, "ix": 3}, "x": {"a": 0, "k": 0, "ix": 4}, "nm": "Mask 1"}], "w": 375, "h": 640, "ip": 115, "op": 165, "st": 102, "bm": 0}, {"ddd": 0, "ind": 18, "ty": 0, "nm": "Fillpping coin_01", "refId": "comp_0", "sr": 1, "ks": {"o": {"a": 0, "k": 100, "ix": 11}, "r": {"a": 0, "k": 0, "ix": 10}, "p": {"a": 0, "k": [107.494, 410.556, 0], "ix": 2, "l": 2}, "a": {"a": 0, "k": [187.5, 320, 0], "ix": 1, "l": 2}, "s": {"a": 0, "k": [52.2, 52.2, 100], "ix": 6, "l": 2}}, "ao": 0, "hasMask": true, "masksProperties": [{"inv": false, "mode": "a", "pt": {"a": 0, "k": {"i": [[0, 0], [0, 0], [0, 0], [0, 0]], "o": [[0, 0], [0, 0], [0, 0], [0, 0]], "v": [[258.005, 249.132], [115.162, 249.132], [115.162, 391.974], [258.005, 391.974]], "c": true}, "ix": 1}, "o": {"a": 0, "k": 100, "ix": 3}, "x": {"a": 0, "k": 0, "ix": 4}, "nm": "Mask 1"}], "w": 375, "h": 640, "ip": 115, "op": 165, "st": 99, "bm": 0}, {"ddd": 0, "ind": 19, "ty": 0, "nm": "Fillpping coin_01", "refId": "comp_0", "sr": 1, "ks": {"o": {"a": 0, "k": 100, "ix": 11}, "r": {"a": 0, "k": 0, "ix": 10}, "p": {"a": 0, "k": [81.974, 340.376, 0], "ix": 2, "l": 2}, "a": {"a": 0, "k": [187.5, 320, 0], "ix": 1, "l": 2}, "s": {"a": 0, "k": [52.2, 52.2, 100], "ix": 6, "l": 2}}, "ao": 0, "hasMask": true, "masksProperties": [{"inv": false, "mode": "a", "pt": {"a": 0, "k": {"i": [[0, 0], [0, 0], [0, 0], [0, 0]], "o": [[0, 0], [0, 0], [0, 0], [0, 0]], "v": [[258.005, 249.132], [115.162, 249.132], [115.162, 391.974], [258.005, 391.974]], "c": true}, "ix": 1}, "o": {"a": 0, "k": 100, "ix": 3}, "x": {"a": 0, "k": 0, "ix": 4}, "nm": "Mask 1"}], "w": 375, "h": 640, "ip": 115, "op": 165, "st": 96, "bm": 0}, {"ddd": 0, "ind": 20, "ty": 0, "nm": "Fillpping coin_01", "refId": "comp_0", "sr": 1, "ks": {"o": {"a": 0, "k": 100, "ix": 11}, "r": {"a": 0, "k": 0, "ix": 10}, "p": {"a": 0, "k": [107.494, 269.906, 0], "ix": 2, "l": 2}, "a": {"a": 0, "k": [187.5, 320, 0], "ix": 1, "l": 2}, "s": {"a": 0, "k": [52.2, 52.2, 100], "ix": 6, "l": 2}}, "ao": 0, "hasMask": true, "masksProperties": [{"inv": false, "mode": "a", "pt": {"a": 0, "k": {"i": [[0, 0], [0, 0], [0, 0], [0, 0]], "o": [[0, 0], [0, 0], [0, 0], [0, 0]], "v": [[258.005, 249.132], [115.162, 249.132], [115.162, 391.974], [258.005, 391.974]], "c": true}, "ix": 1}, "o": {"a": 0, "k": 100, "ix": 3}, "x": {"a": 0, "k": 0, "ix": 4}, "nm": "Mask 1"}], "w": 375, "h": 640, "ip": 115, "op": 165, "st": 93, "bm": 0}, {"ddd": 0, "ind": 21, "ty": 0, "nm": "Fillpping coin_01", "refId": "comp_0", "sr": 1, "ks": {"o": {"a": 0, "k": 100, "ix": 11}, "r": {"a": 0, "k": 0, "ix": 10}, "p": {"a": 0, "k": [187.261, 235.743, 0], "ix": 2, "l": 2}, "a": {"a": 0, "k": [187.5, 320, 0], "ix": 1, "l": 2}, "s": {"a": 0, "k": [52.2, 52.2, 100], "ix": 6, "l": 2}}, "ao": 0, "hasMask": true, "masksProperties": [{"inv": false, "mode": "a", "pt": {"a": 0, "k": {"i": [[0, 0], [0, 0], [0, 0], [0, 0]], "o": [[0, 0], [0, 0], [0, 0], [0, 0]], "v": [[258.005, 249.132], [115.162, 249.132], [115.162, 391.974], [258.005, 391.974]], "c": true}, "ix": 1}, "o": {"a": 0, "k": 100, "ix": 3}, "x": {"a": 0, "k": 0, "ix": 4}, "nm": "Mask 1"}], "w": 375, "h": 640, "ip": 115, "op": 165, "st": 90, "bm": 0}, {"ddd": 0, "ind": 22, "ty": 3, "nm": "Null 33", "sr": 1, "ks": {"o": {"a": 0, "k": 0, "ix": 11}, "r": {"a": 0, "k": 0, "ix": 10}, "p": {"a": 0, "k": [187, 338.308, 0], "ix": 2, "l": 2}, "a": {"a": 0, "k": [50, 50, 0], "ix": 1, "l": 2}, "s": {"a": 1, "k": [{"i": {"x": [0.177, 0.177, 0.667], "y": [1, 1, 1]}, "o": {"x": [0.978, 0.978, 0.333], "y": [0, 0, 0]}, "t": 205, "s": [87, 87, 100]}, {"t": 216, "s": [69, 69, 100]}], "ix": 6, "l": 2}}, "ao": 0, "ip": 0, "op": 192, "st": 0, "bm": 0}, {"ddd": 0, "ind": 23, "ty": 0, "nm": "Fillpping coin_01", "parent": 22, "refId": "comp_0", "sr": 1, "ks": {"o": {"a": 1, "k": [{"i": {"x": [0.833], "y": [0.833]}, "o": {"x": [0.167], "y": [0.167]}, "t": 58, "s": [0]}, {"t": 59, "s": [100]}], "ix": 11}, "r": {"a": 0, "k": 0, "ix": 10}, "p": {"a": 1, "k": [{"i": {"x": 0, "y": 1}, "o": {"x": 0.866, "y": 0}, "t": 52, "s": [50.188, 51.856, 0], "to": [15.182, -13.413, 0], "ti": [-15.182, 13.413, 0]}, {"t": 70, "s": [141.281, -28.624, 0]}], "ix": 2, "l": 2}, "a": {"a": 0, "k": [187.5, 320, 0], "ix": 1, "l": 2}, "s": {"a": 0, "k": [60, 60, 100], "ix": 6, "l": 2}}, "ao": 0, "hasMask": true, "masksProperties": [{"inv": false, "mode": "a", "pt": {"a": 0, "k": {"i": [[0, 0], [0, 0], [0, 0], [0, 0]], "o": [[0, 0], [0, 0], [0, 0], [0, 0]], "v": [[258.005, 249.132], [115.162, 249.132], [115.162, 391.974], [258.005, 391.974]], "c": true}, "ix": 1}, "o": {"a": 0, "k": 100, "ix": 3}, "x": {"a": 0, "k": 0, "ix": 4}, "nm": "Mask 1"}], "w": 375, "h": 640, "ip": 48, "op": 115, "st": 41, "bm": 0}, {"ddd": 0, "ind": 24, "ty": 0, "nm": "Fillpping coin_01", "parent": 22, "refId": "comp_0", "sr": 1, "ks": {"o": {"a": 1, "k": [{"i": {"x": [0.833], "y": [0.833]}, "o": {"x": [0.167], "y": [0.167]}, "t": 56, "s": [0]}, {"t": 57, "s": [100]}], "ix": 11}, "r": {"a": 0, "k": 0, "ix": 10}, "p": {"a": 1, "k": [{"i": {"x": 0, "y": 1}, "o": {"x": 0.866, "y": 0}, "t": 50, "s": [50.188, 51.856, 0], "to": [20.293, 0.087, 0], "ti": [-20.293, -0.087, 0]}, {"t": 68, "s": [171.947, 52.376, 0]}], "ix": 2, "l": 2}, "a": {"a": 0, "k": [187.5, 320, 0], "ix": 1, "l": 2}, "s": {"a": 0, "k": [60, 60, 100], "ix": 6, "l": 2}}, "ao": 0, "hasMask": true, "masksProperties": [{"inv": false, "mode": "a", "pt": {"a": 0, "k": {"i": [[0, 0], [0, 0], [0, 0], [0, 0]], "o": [[0, 0], [0, 0], [0, 0], [0, 0]], "v": [[258.005, 249.132], [115.162, 249.132], [115.162, 391.974], [258.005, 391.974]], "c": true}, "ix": 1}, "o": {"a": 0, "k": 100, "ix": 3}, "x": {"a": 0, "k": 0, "ix": 4}, "nm": "Mask 1"}], "w": 375, "h": 640, "ip": 46, "op": 115, "st": 39, "bm": 0}, {"ddd": 0, "ind": 25, "ty": 0, "nm": "Fillpping coin_01", "parent": 22, "refId": "comp_0", "sr": 1, "ks": {"o": {"a": 1, "k": [{"i": {"x": [0.833], "y": [0.833]}, "o": {"x": [0.167], "y": [0.167]}, "t": 54, "s": [0]}, {"t": 55, "s": [100]}], "ix": 11}, "r": {"a": 0, "k": 0, "ix": 10}, "p": {"a": 1, "k": [{"i": {"x": 0, "y": 1}, "o": {"x": 0.866, "y": 0}, "t": 48, "s": [50.188, 51.856, 0], "to": [15.182, 13.531, 0], "ti": [-15.182, -13.531, 0]}, {"t": 66, "s": [141.281, 133.043, 0]}], "ix": 2, "l": 2}, "a": {"a": 0, "k": [187.5, 320, 0], "ix": 1, "l": 2}, "s": {"a": 0, "k": [60, 60, 100], "ix": 6, "l": 2}}, "ao": 0, "hasMask": true, "masksProperties": [{"inv": false, "mode": "a", "pt": {"a": 0, "k": {"i": [[0, 0], [0, 0], [0, 0], [0, 0]], "o": [[0, 0], [0, 0], [0, 0], [0, 0]], "v": [[258.005, 249.132], [115.162, 249.132], [115.162, 391.974], [258.005, 391.974]], "c": true}, "ix": 1}, "o": {"a": 0, "k": 100, "ix": 3}, "x": {"a": 0, "k": 0, "ix": 4}, "nm": "Mask 1"}], "w": 375, "h": 640, "ip": 44, "op": 115, "st": 37, "bm": 0}, {"ddd": 0, "ind": 26, "ty": 0, "nm": "Fillpping coin_01", "parent": 22, "refId": "comp_0", "sr": 1, "ks": {"o": {"a": 1, "k": [{"i": {"x": [0.833], "y": [0.833]}, "o": {"x": [0.167], "y": [0.167]}, "t": 52, "s": [0]}, {"t": 53, "s": [100]}], "ix": 11}, "r": {"a": 0, "k": 0, "ix": 10}, "p": {"a": 1, "k": [{"i": {"x": 0, "y": 1}, "o": {"x": 0.866, "y": 0}, "t": 46, "s": [50.188, 51.856, 0], "to": [0.019, 20.591, 0], "ti": [-0.019, -20.591, 0]}, {"t": 64, "s": [50.3, 175.403, 0]}], "ix": 2, "l": 2}, "a": {"a": 0, "k": [187.5, 320, 0], "ix": 1, "l": 2}, "s": {"a": 0, "k": [60, 60, 100], "ix": 6, "l": 2}}, "ao": 0, "hasMask": true, "masksProperties": [{"inv": false, "mode": "a", "pt": {"a": 0, "k": {"i": [[0, 0], [0, 0], [0, 0], [0, 0]], "o": [[0, 0], [0, 0], [0, 0], [0, 0]], "v": [[258.005, 249.132], [115.162, 249.132], [115.162, 391.974], [258.005, 391.974]], "c": true}, "ix": 1}, "o": {"a": 0, "k": 100, "ix": 3}, "x": {"a": 0, "k": 0, "ix": 4}, "nm": "Mask 1"}], "w": 375, "h": 640, "ip": 42, "op": 115, "st": 35, "bm": 0}, {"ddd": 0, "ind": 27, "ty": 0, "nm": "Fillpping coin_01", "parent": 22, "refId": "comp_0", "sr": 1, "ks": {"o": {"a": 1, "k": [{"i": {"x": [0.833], "y": [0.833]}, "o": {"x": [0.167], "y": [0.167]}, "t": 50, "s": [0]}, {"t": 51, "s": [100]}], "ix": 11}, "r": {"a": 0, "k": 0, "ix": 10}, "p": {"a": 1, "k": [{"i": {"x": 0, "y": 1}, "o": {"x": 0.866, "y": 0}, "t": 44, "s": [50.188, 51.856, 0], "to": [-15.262, 13.531, 0], "ti": [15.262, -13.531, 0]}, {"t": 62, "s": [-41.386, 133.043, 0]}], "ix": 2, "l": 2}, "a": {"a": 0, "k": [187.5, 320, 0], "ix": 1, "l": 2}, "s": {"a": 0, "k": [60, 60, 100], "ix": 6, "l": 2}}, "ao": 0, "hasMask": true, "masksProperties": [{"inv": false, "mode": "a", "pt": {"a": 0, "k": {"i": [[0, 0], [0, 0], [0, 0], [0, 0]], "o": [[0, 0], [0, 0], [0, 0], [0, 0]], "v": [[258.005, 249.132], [115.162, 249.132], [115.162, 391.974], [258.005, 391.974]], "c": true}, "ix": 1}, "o": {"a": 0, "k": 100, "ix": 3}, "x": {"a": 0, "k": 0, "ix": 4}, "nm": "Mask 1"}], "w": 375, "h": 640, "ip": 40, "op": 115, "st": 33, "bm": 0}, {"ddd": 0, "ind": 28, "ty": 0, "nm": "Fillpping coin_01", "parent": 22, "refId": "comp_0", "sr": 1, "ks": {"o": {"a": 1, "k": [{"i": {"x": [0.833], "y": [0.833]}, "o": {"x": [0.167], "y": [0.167]}, "t": 48, "s": [0]}, {"t": 49, "s": [100]}], "ix": 11}, "r": {"a": 0, "k": 0, "ix": 10}, "p": {"a": 1, "k": [{"i": {"x": 0, "y": 1}, "o": {"x": 0.866, "y": 0}, "t": 42, "s": [50.188, 51.856, 0], "to": [-20.151, 0.087, 0], "ti": [20.151, -0.087, 0]}, {"t": 60, "s": [-70.719, 52.376, 0]}], "ix": 2, "l": 2}, "a": {"a": 0, "k": [187.5, 320, 0], "ix": 1, "l": 2}, "s": {"a": 0, "k": [60, 60, 100], "ix": 6, "l": 2}}, "ao": 0, "hasMask": true, "masksProperties": [{"inv": false, "mode": "a", "pt": {"a": 0, "k": {"i": [[0, 0], [0, 0], [0, 0], [0, 0]], "o": [[0, 0], [0, 0], [0, 0], [0, 0]], "v": [[258.005, 249.132], [115.162, 249.132], [115.162, 391.974], [258.005, 391.974]], "c": true}, "ix": 1}, "o": {"a": 0, "k": 100, "ix": 3}, "x": {"a": 0, "k": 0, "ix": 4}, "nm": "Mask 1"}], "w": 375, "h": 640, "ip": 38, "op": 115, "st": 31, "bm": 0}, {"ddd": 0, "ind": 29, "ty": 0, "nm": "Fillpping coin_01", "parent": 22, "refId": "comp_0", "sr": 1, "ks": {"o": {"a": 1, "k": [{"i": {"x": [0.833], "y": [0.833]}, "o": {"x": [0.167], "y": [0.167]}, "t": 46, "s": [0]}, {"t": 47, "s": [100]}], "ix": 11}, "r": {"a": 0, "k": 0, "ix": 10}, "p": {"a": 1, "k": [{"i": {"x": 0, "y": 1}, "o": {"x": 0.866, "y": 0}, "t": 40, "s": [50.188, 51.856, 0], "to": [-15.262, -13.413, 0], "ti": [15.262, 13.413, 0]}, {"t": 58, "s": [-41.386, -28.624, 0]}], "ix": 2, "l": 2}, "a": {"a": 0, "k": [187.5, 320, 0], "ix": 1, "l": 2}, "s": {"a": 0, "k": [60, 60, 100], "ix": 6, "l": 2}}, "ao": 0, "hasMask": true, "masksProperties": [{"inv": false, "mode": "a", "pt": {"a": 0, "k": {"i": [[0, 0], [0, 0], [0, 0], [0, 0]], "o": [[0, 0], [0, 0], [0, 0], [0, 0]], "v": [[258.005, 249.132], [115.162, 249.132], [115.162, 391.974], [258.005, 391.974]], "c": true}, "ix": 1}, "o": {"a": 0, "k": 100, "ix": 3}, "x": {"a": 0, "k": 0, "ix": 4}, "nm": "Mask 1"}], "w": 375, "h": 640, "ip": 36, "op": 115, "st": 29, "bm": 0}, {"ddd": 0, "ind": 30, "ty": 3, "nm": "Null 32", "sr": 1, "ks": {"o": {"a": 0, "k": 0, "ix": 11}, "r": {"a": 0, "k": 0, "ix": 10}, "p": {"a": 1, "k": [{"i": {"x": 0, "y": 1}, "o": {"x": 0.866, "y": 0}, "t": 38, "s": [187, 322.38, 0], "to": [0, -14.44, 0], "ti": [0, 14.44, 0]}, {"t": 56, "s": [187, 235.743, 0]}], "ix": 2, "l": 2}, "a": {"a": 0, "k": [50, 50, 0], "ix": 1, "l": 2}, "s": {"a": 1, "k": [{"i": {"x": [0, 0, 0.667], "y": [1, 1, 1]}, "o": {"x": [0.866, 0.866, 0.333], "y": [0, 0, 0]}, "t": 38, "s": [87, 87, 100]}, {"t": 56, "s": [52.2, 52.2, 100]}], "ix": 6, "l": 2}}, "ao": 0, "ip": 0, "op": 72, "st": 0, "bm": 0}, {"ddd": 0, "ind": 31, "ty": 0, "nm": "Fillpping coin_01", "parent": 22, "refId": "comp_0", "sr": 1, "ks": {"o": {"a": 0, "k": 100, "ix": 11}, "r": {"a": 0, "k": 0, "ix": 10}, "p": {"a": 0, "k": [50.3, -67.891, 0], "ix": 2, "l": 2}, "a": {"a": 0, "k": [187.5, 320, 0], "ix": 1, "l": 2}, "s": {"a": 0, "k": [60, 60, 100], "ix": 6, "l": 2}}, "ao": 0, "hasMask": true, "masksProperties": [{"inv": false, "mode": "a", "pt": {"a": 0, "k": {"i": [[0, 0], [0, 0], [0, 0], [0, 0]], "o": [[0, 0], [0, 0], [0, 0], [0, 0]], "v": [[258.005, 249.132], [115.162, 249.132], [115.162, 391.974], [258.005, 391.974]], "c": true}, "ix": 1}, "o": {"a": 0, "k": 100, "ix": 3}, "x": {"a": 0, "k": 0, "ix": 4}, "nm": "Mask 1"}], "w": 375, "h": 640, "ip": 65, "op": 115, "st": 44, "bm": 0}, {"ddd": 0, "ind": 32, "ty": 0, "nm": "Fillpping coin_01", "parent": 30, "refId": "comp_0", "sr": 1, "ks": {"o": {"a": 1, "k": [{"i": {"x": [0.044], "y": [1]}, "o": {"x": [0.844], "y": [0]}, "t": 0, "s": [0]}, {"t": 18, "s": [100]}], "ix": 11}, "r": {"a": 0, "k": 0, "ix": 10}, "p": {"a": 1, "k": [{"i": {"x": 0.044, "y": 1}, "o": {"x": 0.844, "y": 0}, "t": 0, "s": [50.5, 141.435, 0], "to": [0, -16.551, 0], "ti": [0, 0, 0]}, {"i": {"x": 0.044, "y": 1}, "o": {"x": 0.812, "y": 0}, "t": 18, "s": [50.5, 42.132, 0], "to": [0, 0, 0], "ti": [0, -1.311, 0]}, {"t": 29, "s": [50.5, 50, 0]}], "ix": 2, "l": 2}, "a": {"a": 0, "k": [187.5, 320, 0], "ix": 1, "l": 2}, "s": {"a": 1, "k": [{"i": {"x": [0.044, 0.044, 0.667], "y": [1, 1, 1]}, "o": {"x": [0.844, 0.844, 0.333], "y": [0, 0, 0]}, "t": 0, "s": [50, 50, 100]}, {"t": 18, "s": [100, 100, 100]}], "ix": 6, "l": 2}}, "ao": 0, "hasMask": true, "masksProperties": [{"inv": false, "mode": "a", "pt": {"a": 0, "k": {"i": [[0, 0], [0, 0], [0, 0], [0, 0]], "o": [[0, 0], [0, 0], [0, 0], [0, 0]], "v": [[258.005, 249.132], [115.162, 249.132], [115.162, 391.974], [258.005, 391.974]], "c": true}, "ix": 1}, "o": {"a": 0, "k": 100, "ix": 3}, "x": {"a": 0, "k": 0, "ix": 4}, "nm": "Mask 1"}], "w": 375, "h": 640, "ip": 0, "op": 65, "st": 0, "bm": 0}, {"ddd": 0, "ind": 33, "ty": 3, "nm": "Null 26", "sr": 1, "ks": {"o": {"a": 0, "k": 0, "ix": 11}, "r": {"a": 0, "k": 0, "ix": 10}, "p": {"a": 1, "k": [{"i": {"x": 0.131, "y": 1}, "o": {"x": 0.881, "y": 0}, "t": 0, "s": [91.4, 134.33, 0], "to": [0, -8.418, 0], "ti": [0, 8.418, 0]}, {"i": {"x": 0.169, "y": 0.169}, "o": {"x": 0.167, "y": 0.167}, "t": 18, "s": [91.4, 83.825, 0], "to": [0, 0, 0], "ti": [0, 0, 0]}, {"i": {"x": 0.164, "y": 1}, "o": {"x": 0.899, "y": 0}, "t": 181, "s": [91.4, 83.825, 0], "to": [0, 0, 0], "ti": [0, 0, 0]}, {"t": 192, "s": [91.4, 66.163, 0]}], "ix": 2, "l": 2}, "a": {"a": 0, "k": [50, 50, 0], "ix": 1, "l": 2}, "s": {"a": 0, "k": [100, 100, 100], "ix": 6, "l": 2}}, "ao": 0, "ip": 0, "op": 192, "st": 0, "bm": 0}, {"ddd": 0, "ind": 35, "ty": 5, "nm": "Headline 01", "parent": 33, "sr": 1, "ks": {"o": {"a": 1, "k": [{"i": {"x": [0.833], "y": [0.833]}, "o": {"x": [0.167], "y": [0.167]}, "t": 4, "s": [0]}, {"i": {"x": [0.833], "y": [1]}, "o": {"x": [0.167], "y": [0]}, "t": 10, "s": [100]}, {"i": {"x": [0.177], "y": [1]}, "o": {"x": [0.978], "y": [0]}, "t": 184, "s": [100]}, {"t": 191, "s": [0]}], "ix": 11}, "r": {"a": 0, "k": 0, "ix": 10}, "p": {"a": 0, "k": [37.453, 15.434, 0], "ix": 2, "l": 2}, "a": {"a": 0, "k": [-2.814, 61.325, 0], "ix": 1, "l": 2}, "s": {"a": 0, "k": [100, 100, 100], "ix": 6, "l": 2}}, "ao": 0, "t": {"d": {"k": [{"s": {"sz": [289.833343505859, 102.166656494141], "ps": [-41.6666717529297, 54.1666717529297], "s": 27, "f": "Roboto-Bold", "t": "£100 put away each month, from the year 2000 until now.", "ca": 0, "j": 0, "tr": -10, "lh": 32.4000015258789, "ls": 0, "fc": [0.765, 1, 0.204]}, "t": 0}]}, "p": {}, "m": {"g": 1, "a": {"a": 0, "k": [0, 0], "ix": 2}}, "a": []}, "ip": 0, "op": 192, "st": 27, "bm": 0}], "markers": []} \ No newline at end of file
diff --git a/snapshot-tests/src/main/assets/Tests/winners.tgs b/snapshot-tests/src/main/assets/Tests/winners.tgs
new file mode 100644
index 00000000..d1112ecb
--- /dev/null
+++ b/snapshot-tests/src/main/assets/Tests/winners.tgs
Binary files differ
diff --git a/snapshot-tests/src/main/java/com/airbnb/lottie/snapshots/FilmStripView.kt b/snapshot-tests/src/main/java/com/airbnb/lottie/snapshots/FilmStripView.kt
index 5b4ae8b8..4fb9dd58 100644
--- a/snapshot-tests/src/main/java/com/airbnb/lottie/snapshots/FilmStripView.kt
+++ b/snapshot-tests/src/main/java/com/airbnb/lottie/snapshots/FilmStripView.kt
@@ -45,13 +45,21 @@ class FilmStripView @JvmOverloads constructor(
animationViews.forEach { it.setApplyingOpacityToLayersEnabled(isApplyingOpacityToLayersEnabled) }
}
+ fun setClipTextToBoundingBox(isApplyingOpacityToLayersEnabled: Boolean) {
+ animationViews.forEach { it.clipTextToBoundingBox = isApplyingOpacityToLayersEnabled }
+ }
+
fun setOutlineMasksAndMattes(outline: Boolean) {
animationViews.forEach { it.setOutlineMasksAndMattes(outline) }
}
+ fun setUseCompositionFrameRate(outline: Boolean) {
+ animationViews.forEach { it.setUseCompositionFrameRate(outline) }
+ }
+
private fun Float.round(decimals: Int): Float {
var multiplier = 1f
repeat(decimals) { multiplier *= 10 }
return round(this * multiplier) / multiplier
}
-} \ No newline at end of file
+}
diff --git a/snapshot-tests/src/main/res/layout/seek_bar.xml b/snapshot-tests/src/main/res/layout/seek_bar.xml
new file mode 100644
index 00000000..7ea3ed10
--- /dev/null
+++ b/snapshot-tests/src/main/res/layout/seek_bar.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="512dp"
+ android:layout_height="64dp">
+
+ <SeekBar
+ android:id="@+id/seek_bar"
+ android:layout_gravity="center"
+ android:progress="50"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+</FrameLayout> \ No newline at end of file
diff --git a/update-baseline-profiles.sh b/update-baseline-profiles.sh
new file mode 100755
index 00000000..d1de0478
--- /dev/null
+++ b/update-baseline-profiles.sh
@@ -0,0 +1,16 @@
+#!/usr/bin/env bash
+set -uo pipefail
+
+# If on CI, add indirect swiftshader arg
+# Source: https://developer.android.com/studio/test/gradle-managed-devices
+gpu_arg=""
+if [ "${CI:-}" == "true" ]; then
+ gpu_arg="-Pandroid.testoptions.manageddevices.emulator.gpu=swiftshader_indirect"
+fi
+
+./gradlew cleanManagedDevices --unused-only &&
+ ./gradlew lottie:generateBaselineProfile lottie-compose:generateBaselineProfile \
+ -Pandroid.testInstrumentationRunnerArguments.androidx.benchmark.enabledRules=BaselineProfile
+ -Pandroid.experimental.testOptions.managedDevices.setupTimeoutMinutes=20 \
+ "${gpu_arg}" \
+ --info
diff --git a/upload_release.sh b/upload_release.sh
index aa08990e..b1dd6028 100755
--- a/upload_release.sh
+++ b/upload_release.sh
@@ -4,4 +4,4 @@ if [ "$git_branch" != "master" ]; then
echo "You must run this from master!"
exit 1
fi
-./gradlew clean lottie:assembleRelease lottie-compose:assembleRelease lottie:publish lottie-compose:publish -DORG_GRADLE_PROJECT_mavenCentralUsername="${SONATYPE_USERNAME}" -DORG_GRADLE_PROJECT_mavenCentralPassword="${SONATYPE_PASSWORD}" --rerun-tasks --no-parallel --no-configuration-cache \ No newline at end of file
+./gradlew clean lottie:assembleRelease lottie-compose:assembleRelease lottie:publish lottie-compose:publish --rerun-tasks --no-parallel --no-configuration-cache --stacktrace
diff --git a/versions.properties b/versions.properties
new file mode 100644
index 00000000..52591e0e
--- /dev/null
+++ b/versions.properties
@@ -0,0 +1,387 @@
+#### Dependencies and Plugin versions with their available updates.
+#### Generated by `./gradlew refreshVersions` version 0.51.0
+####
+#### Don't manually edit or split the comments that start with four hashtags (####),
+#### they will be overwritten by refreshVersions.
+####
+#### suppress inspection "SpellCheckingInspection" for whole file
+#### suppress inspection "UnusedProperty" for whole file
+
+## unused
+plugin.android=8.2.0
+## # available=8.2.0-alpha01
+## # available=8.2.0-alpha02
+
+version.androidx.compose=2024.02.01
+
+version.androidx.compose.ui=1.6.2
+## # available=1.7.0-alpha01
+## # available=1.7.0-alpha02
+## # available=1.7.0-alpha03
+
+version.coil-kt=2.6.0
+
+version.robolectric=4.10.3
+## # available=4.11-beta-1
+## # available=4.11-beta-2
+## # available=4.11
+## # available=4.11.1
+
+version.retrofit2=2.9.0
+
+# Do not update to 2.0. It will pull in Kotlin as a transitive dependency into the lottie library.
+ version.okio=1.17.6
+### available=2.0.0-RC1
+### available=2.0.0
+### available=2.1.0
+### available=2.2.0
+### available=2.2.1
+### available=2.2.2
+### available=2.3.0
+### available=2.4.0
+### available=2.4.1
+### available=2.4.2
+### available=2.4.3
+### available=2.5.0
+### available=2.6.0
+### available=2.7.0-alpha.lockfree.1
+### available=2.7.0-alpha.lockfree.2
+### available=2.7.0
+### available=2.8.0
+### available=2.9.0
+### available=2.10.0
+### available=3.0.0-alpha.1
+### available=3.0.0-alpha.2
+### available=3.0.0-alpha.3
+### available=3.0.0-alpha.4
+### available=3.0.0-alpha.5
+### available=3.0.0-alpha.6
+### available=3.0.0-alpha.7
+### available=3.0.0-alpha.8
+### available=3.0.0-alpha.9
+### available=3.0.0-alpha.10
+### available=3.0.0-alpha.11
+### available=3.0.0
+### available=3.1.0
+### available=3.2.0
+### available=3.3.0
+### available=3.4.0
+### available=3.5.0
+### available=3.6.0
+### available=3.7.0
+### available=3.8.0
+
+version.okhttp3=4.12.0
+## # available=4.12.0
+## # available=5.0.0-alpha.1
+## # available=5.0.0-alpha.2
+## # available=5.0.0-alpha.3
+## # available=5.0.0-alpha.4
+## # available=5.0.0-alpha.5
+## # available=5.0.0-alpha.6
+## # available=5.0.0-alpha.7
+## # available=5.0.0-alpha.8
+## # available=5.0.0-alpha.9
+## # available=5.0.0-alpha.10
+## # available=5.0.0-alpha.11
+## # available=5.0.0-alpha.12
+
+version.mockito=5.3.1
+## # available=5.4.0
+## # available=5.5.0
+## # available=5.6.0
+## # available=5.7.0
+## # available=5.8.0
+## # available=5.9.0
+## # available=5.10.0
+## # available=5.11.0
+
+version.kotlinx.coroutines=1.8.0
+
+version.kotlin=1.9.22
+## # available=2.0.0-Beta1
+## # available=2.0.0-Beta2
+## # available=2.0.0-Beta3
+## # available=2.0.0-Beta4
+
+version.junit.junit=4.13.2
+
+version.io.jsonwebtoken..jjwt=0.9.1
+## # available=0.12.0
+## # available=0.12.1
+## # available=0.12.2
+## # available=0.12.3
+## # available=0.12.4
+## # available=0.12.5
+
+version.google.dagger=2.51
+## # available=2.48.1
+## # available=2.49
+## # available=2.50
+## # available=2.51
+
+version.google.android.material=1.11.0
+## # available=1.12.0-alpha01
+## # available=1.12.0-alpha02
+## # available=1.12.0-alpha03
+
+version.com.uber.nullaway..nullaway=0.10.10
+## # available=0.10.11
+## # available=0.10.12
+## # available=0.10.13
+## # available=0.10.14
+## # available=0.10.15
+## # available=0.10.16
+## # available=0.10.17
+## # available=0.10.18
+## # available=0.10.19
+## # available=0.10.20
+## # available=0.10.21
+## # available=0.10.22
+## # available=0.10.23
+
+version.com.nhaarman.mockitokotlin2..mockito-kotlin=2.2.0
+
+version.com.google.errorprone..error_prone_core=2.19.1
+## # available=2.20.0
+## # available=2.21.0
+## # available=2.21.1
+## # available=2.22.0
+## # available=2.23.0
+## # available=2.24.0
+## # available=2.24.1
+## # available=2.25.0
+
+version.com.google.code.gson..gson=2.10.1
+
+version.com.github.bumptech.glide..glide=4.16.0
+## # available=5.0.0-rc01
+
+version.com.github.PhilJay..MPAndroidChart=3.1.0
+
+version.com.dlazaro66.qrcodereaderview..qrcodereaderview=2.0.2
+
+version.com.amazonaws..aws-android-sdk-s3=2.71.0
+## # available=2.72.0
+## # available=2.73.0
+## # available=2.74.0
+## # available=2.75.0
+
+version.com.amazonaws..aws-android-sdk-mobile-client=2.71.0
+## # available=2.72.0
+## # available=2.73.0
+## # available=2.74.0
+## # available=2.75.0
+
+version.com.amazonaws..aws-android-sdk-auth-userpools=2.71.0
+## # available=2.72.0
+## # available=2.73.0
+## # available=2.74.0
+## # available=2.75.0
+
+
+version.com.airbnb.android..mavericks-compose=3.0.9
+
+version.com.airbnb.android..mavericks=3.0.9
+
+version.com.airbnb.android..epoxy-processor=5.1.4
+
+version.com.airbnb.android..epoxy=5.1.4
+
+version.androidx.test.uiautomator=2.2.0
+## # available=2.3.0-alpha01
+## # available=2.3.0-alpha02
+## # available=2.3.0-alpha03
+## # available=2.3.0-alpha04
+## # available=2.3.0-alpha05
+## # available=2.3.0-beta01
+## # available=2.3.0-rc01
+## # available=2.3.0
+
+version.androidx.test.rules=1.5.0
+## # available=1.6.0-alpha01
+## # available=1.6.0-alpha02
+## # available=1.6.0-alpha03
+
+version.androidx.test.ext.junit=1.1.5
+## # available=1.2.0-alpha01
+## # available=1.2.0-alpha02
+## # available=1.2.0-alpha03
+
+version.androidx.test.espresso=3.5.1
+## # available=3.6.0-alpha01
+## # available=3.6.0-alpha02
+## # available=3.6.0-alpha03
+
+version.androidx.test.core=1.5.0
+## # available=1.6.0-alpha01
+## # available=1.6.0-alpha02
+## # available=1.6.0-alpha03
+## # available=1.6.0-alpha04
+## # available=1.6.0-alpha05
+
+version.androidx.recyclerview=1.3.2
+## # available=1.4.0-alpha01
+
+version.androidx.profileinstaller=1.3.1
+## # available=1.4.0-alpha01
+
+version.androidx.paging=3.2.1
+## # available=3.3.0-alpha01
+## # available=3.3.0-alpha02
+## # available=3.3.0-alpha03
+
+version.androidx.navigation=2.7.7
+## # available=2.8.0-alpha01
+## # available=2.8.0-alpha02
+## # available=2.8.0-alpha03
+
+version.androidx.multidex=2.0.1
+
+version.androidx.lifecycle=2.5.1
+## # available=2.6.0-alpha01
+## # available=2.6.0-alpha02
+## # available=2.6.0-alpha03
+## # available=2.6.0-alpha04
+## # available=2.6.0-alpha05
+## # available=2.6.0-beta01
+## # available=2.6.0-rc01
+## # available=2.6.0
+## # available=2.6.1
+## # available=2.6.2
+## # available=2.7.0-alpha01
+## # available=2.7.0-alpha02
+## # available=2.7.0-alpha03
+
+version.androidx.fragment=1.6.2
+## # available=1.7.0-alpha01
+## # available=1.7.0-alpha02
+## # available=1.7.0-alpha03
+## # available=1.7.0-alpha04
+## # available=1.7.0-alpha05
+## # available=1.7.0-alpha06
+## # available=1.7.0-alpha07
+## # available=1.7.0-alpha08
+## # available=1.7.0-alpha09
+## # available=1.7.0-alpha10
+
+version.androidx.core=1.12.0
+## # available=1.13.0-alpha01
+## # available=1.13.0-alpha02
+## # available=1.13.0-alpha03
+## # available=1.13.0-alpha04
+## # available=1.13.0-alpha05
+
+version.androidx.constraintlayout=2.1.4
+## # available=2.2.0-alpha01
+## # available=2.2.0-alpha02
+## # available=2.2.0-alpha03
+## # available=2.2.0-alpha04
+## # available=2.2.0-alpha05
+## # available=2.2.0-alpha06
+## # available=2.2.0-alpha07
+## # available=2.2.0-alpha08
+## # available=2.2.0-alpha09
+## # available=2.2.0-alpha10
+## # available=2.2.0-alpha11
+## # available=2.2.0-alpha12
+## # available=2.2.0-alpha13
+
+## unused
+version.androidx.compose.material=1.4.3
+## # available=1.5.0-alpha01
+## # available=1.5.0-alpha02
+## # available=1.5.0-alpha03
+
+## unused
+version.androidx.compose.foundation=1.4.3
+## # available=1.5.0-alpha01
+## # available=1.5.0-alpha02
+## # available=1.5.0-alpha03
+
+version.androidx.compose.compiler=1.5.10
+
+version.androidx.collection=1.2.0
+## # available=1.3.0-dev01
+## # available=1.3.0-alpha01
+## # available=1.3.0-alpha02
+## # available=1.3.0-alpha03
+## # available=1.3.0-alpha04
+## # available=1.3.0-beta01
+## # available=1.3.0-rc01
+## # available=1.3.0
+## # available=1.4.0-alpha01
+## # available=1.4.0-alpha02
+## # available=1.4.0-beta01
+## # available=1.4.0-beta02
+## # available=1.4.0-rc01
+## # available=1.4.0
+
+version.androidx.cardview=1.0.0
+
+version.androidx.browser=1.5.0
+## # available=1.6.0-alpha01
+## # available=1.6.0-alpha02
+## # available=1.6.0-beta01
+## # available=1.6.0-rc01
+## # available=1.6.0
+## # available=1.7.0-alpha01
+## # available=1.7.0-beta01
+
+version.androidx.benchmark=1.2.0-beta05
+## # available=1.2.0-rc01
+## # available=1.2.0-rc02
+## # available=1.2.0
+## # available=1.2.1
+## # available=1.2.2
+## # available=1.2.3
+## # available=1.3.0-alpha01
+
+version.androidx.appcompat=1.6.1
+## # available=1.7.0-alpha01
+## # available=1.7.0-alpha02
+## # available=1.7.0-alpha03
+
+version.androidx.activity=1.8.2
+## # available=1.9.0-alpha01
+## # available=1.9.0-alpha02
+## # available=1.9.0-alpha03
+
+plugin.org.jetbrains.kotlinx.binary-compatibility-validator=0.14.0
+
+plugin.net.ltgt.errorprone=3.1.0
+
+plugin.com.google.devtools.ksp=1.9.22-1.0.18
+## # available=2.0.0-Beta1-1.0.14
+## # available=2.0.0-Beta1-1.0.15
+## # available=2.0.0-Beta2-1.0.16
+## # available=2.0.0-Beta3-1.0.17
+## # available=2.0.0-Beta4-1.0.17
+## # available=2.0.0-Beta4-1.0.18
+
+plugin.org.ajoberstar.grgit=5.2.0
+## # available=5.2.1
+## # available=5.2.2-rc.2
+## # available=5.2.2
+
+plugin.com.vanniktech.maven.publish=0.25.2
+## # available=0.25.3-rc1
+## # available=0.25.3
+## # available=0.26.0-rc1
+## # available=0.26.0
+## # available=0.27.0-rc1
+## # available=0.27.0-rc2
+## # available=0.27.0
+
+plugin.org.jetbrains.dokka=1.8.20
+## # available=1.9.0
+## # available=1.9.10
+
+plugin.androidx.baselineprofile=1.2.0-beta05
+## # available=1.2.0-rc01
+## # available=1.2.0-rc02
+## # available=1.2.0
+## # available=1.2.1
+## # available=1.2.2
+## # available=1.2.3
+## # available=1.3.0-alpha01