diff --git a/README.md b/README.md
index ae833acf..af1b5d51 100644
--- a/README.md
+++ b/README.md
@@ -10,6 +10,8 @@ This repository is an unofficial conversion of the [Lottie-android](https://gith
It works on Android, iOS, macOS, linux, windows and web.
+
+
## Usage
### Simple animation
diff --git a/README.template.md b/README.template.md
index a0950698..9b61778c 100644
--- a/README.template.md
+++ b/README.template.md
@@ -10,6 +10,8 @@ This repository is an unofficial conversion of the [Lottie-android](https://gith
It works on Android, iOS, macOS, linux, windows and web.
+
+
## Usage
### Simple animation
diff --git a/example/assets/Tests/GradientColorKeyframeAnimation.json b/example/assets/Tests/GradientColorKeyframeAnimation.json
new file mode 100644
index 00000000..80b1f32c
--- /dev/null
+++ b/example/assets/Tests/GradientColorKeyframeAnimation.json
@@ -0,0 +1,522 @@
+{
+ "v": "4.8.0",
+ "meta": {
+ "g": "LottieFiles AE 3.5.2",
+ "a": "",
+ "k": "",
+ "d": "",
+ "tc": "#000000"
+ },
+ "fr": 24,
+ "ip": 0,
+ "op": 6912,
+ "w": 312,
+ "h": 312,
+ "nm": "Master_1-4a- GREEN",
+ "ddd": 0,
+ "assets": [],
+ "layers": [
+ {
+ "ddd": 0,
+ "ind": 30,
+ "ty": 4,
+ "nm": "Background_Layers",
+ "sr": 1,
+ "ks": {
+ "o": {
+ "a": 0,
+ "k": 100,
+ "ix": 11
+ },
+ "r": {
+ "a": 0,
+ "k": 0,
+ "ix": 10
+ },
+ "p": {
+ "a": 0,
+ "k": [
+ 158,
+ 154,
+ 0
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0,
+ 0
+ ],
+ "ix": 1
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 50,
+ 50,
+ 100
+ ],
+ "ix": 6
+ }
+ },
+ "ao": 0,
+ "shapes": [
+ {
+ "ty": "gr",
+ "it": [
+ {
+ "ind": 0,
+ "ty": "sh",
+ "ix": 1,
+ "ks": {
+ "a": 0,
+ "k": {
+ "i": [
+ [
+ 149.841,
+ 0
+ ],
+ [
+ 0,
+ -149.841
+ ],
+ [
+ -149.841,
+ 0
+ ],
+ [
+ 0,
+ 149.841
+ ]
+ ],
+ "o": [
+ [
+ -149.841,
+ 0
+ ],
+ [
+ 0,
+ 149.841
+ ],
+ [
+ 149.841,
+ 0
+ ],
+ [
+ 0,
+ -149.841
+ ]
+ ],
+ "v": [
+ [
+ 0,
+ -271.311
+ ],
+ [
+ -271.311,
+ 0
+ ],
+ [
+ 0,
+ 271.311
+ ],
+ [
+ 271.311,
+ 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": 0,
+ "ix": 5
+ },
+ "lc": 1,
+ "lj": 1,
+ "ml": 4,
+ "bm": 0,
+ "nm": "Stroke 1",
+ "mn": "ADBE Vector Graphic - Stroke",
+ "hd": false
+ },
+ {
+ "ty": "gf",
+ "o": {
+ "a": 0,
+ "k": 60,
+ "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": 898.499,
+ "s": [
+ 0.799,
+ 0.988,
+ 0.875,
+ 0.435,
+ 0.9,
+ 0.994,
+ 0.937,
+ 0.718,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0.072,
+ 1,
+ 0.536,
+ 0.5,
+ 1,
+ 0
+ ]
+ },
+ {
+ "i": {
+ "x": 0.833,
+ "y": 0.833
+ },
+ "o": {
+ "x": 0.167,
+ "y": 0.167
+ },
+ "t": 1112.312,
+ "s": [
+ 0.799,
+ 0.988,
+ 0.875,
+ 0.435,
+ 0.9,
+ 0.994,
+ 0.937,
+ 0.718,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0.072,
+ 1,
+ 0.536,
+ 0.5,
+ 1,
+ 0
+ ]
+ },
+ {
+ "i": {
+ "x": 0.833,
+ "y": 0.833
+ },
+ "o": {
+ "x": 0.167,
+ "y": 0.167
+ },
+ "t": 1330.13,
+ "s": [
+ 0.799,
+ 0.828,
+ 0.855,
+ 0.554,
+ 0.9,
+ 0.914,
+ 0.928,
+ 0.777,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0.072,
+ 1,
+ 0.536,
+ 0.5,
+ 1,
+ 0
+ ]
+ },
+ {
+ "i": {
+ "x": 0.833,
+ "y": 0.833
+ },
+ "o": {
+ "x": 0.167,
+ "y": 0.167
+ },
+ "t": 1420.621,
+ "s": [
+ 0.799,
+ 0.761,
+ 0.847,
+ 0.604,
+ 0.9,
+ 0.88,
+ 0.924,
+ 0.802,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0.072,
+ 1,
+ 0.536,
+ 0.5,
+ 1,
+ 0
+ ]
+ },
+ {
+ "i": {
+ "x": 0.833,
+ "y": 0.833
+ },
+ "o": {
+ "x": 0.167,
+ "y": 0.167
+ },
+ "t": 4054.455,
+ "s": [
+ 0.799,
+ 0.761,
+ 0.847,
+ 0.604,
+ 0.9,
+ 0.88,
+ 0.924,
+ 0.802,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0.072,
+ 1,
+ 0.536,
+ 0.5,
+ 1,
+ 0
+ ]
+ },
+ {
+ "i": {
+ "x": 0.833,
+ "y": 0.833
+ },
+ "o": {
+ "x": 0.167,
+ "y": 0.167
+ },
+ "t": 4311.512,
+ "s": [
+ 0.799,
+ 0.675,
+ 0.859,
+ 0.894,
+ 0.9,
+ 0.837,
+ 0.929,
+ 0.947,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0.072,
+ 1,
+ 0.536,
+ 0.5,
+ 1,
+ 0
+ ]
+ },
+ {
+ "i": {
+ "x": 0.833,
+ "y": 0.833
+ },
+ "o": {
+ "x": 0.167,
+ "y": 0.167
+ },
+ "t": 6439.239,
+ "s": [
+ 0.799,
+ 0.675,
+ 0.859,
+ 0.894,
+ 0.9,
+ 0.837,
+ 0.929,
+ 0.947,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0.072,
+ 1,
+ 0.536,
+ 0.5,
+ 1,
+ 0
+ ]
+ },
+ {
+ "t": 6672.2724609375,
+ "s": [
+ 0.799,
+ 0.71,
+ 0.098,
+ 0.392,
+ 0.9,
+ 0.855,
+ 0.549,
+ 0.696,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0.072,
+ 1,
+ 0.536,
+ 0.5,
+ 1,
+ 0
+ ]
+ }
+ ],
+ "ix": 9
+ }
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 0.798,
+ 1.032
+ ],
+ "ix": 5
+ },
+ "e": {
+ "a": 0,
+ "k": [
+ 244.484,
+ 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": [
+ -4.865,
+ 2.221
+ ],
+ "ix": 2
+ },
+ "a": {
+ "a": 0,
+ "k": [
+ 0,
+ 0
+ ],
+ "ix": 1
+ },
+ "s": {
+ "a": 0,
+ "k": [
+ 108.71,
+ 108.71
+ ],
+ "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": "Transformieren"
+ }
+ ],
+ "nm": "Ellipse 1",
+ "np": 3,
+ "cix": 2,
+ "bm": 0,
+ "ix": 1,
+ "mn": "ADBE Vector Group",
+ "hd": false
+ }
+ ],
+ "ip": -8.80880880880881,
+ "op": 7462.66266266266,
+ "st": -8.80880880880881,
+ "bm": 0
+ }
+ ],
+ "markers": []
+}
diff --git a/example/assets/Tests/LargeSquare.json b/example/assets/Tests/LargeSquare.json
new file mode 100644
index 00000000..0c38da80
--- /dev/null
+++ b/example/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/example/assets/Tests/NullEndShape.json b/example/assets/Tests/NullEndShape.json
new file mode 100644
index 00000000..222f1d28
--- /dev/null
+++ b/example/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/example/pubspec.lock b/example/pubspec.lock
index 7637356d..14de9f35 100644
--- a/example/pubspec.lock
+++ b/example/pubspec.lock
@@ -5,10 +5,10 @@ packages:
dependency: transitive
description:
name: archive
- sha256: "7b875fd4a20b165a3084bd2d210439b22ebc653f21cea4842729c0c30c82596b"
+ sha256: "22600aa1e926be775fa5fe7e6894e7fb3df9efda8891c73f70fb3262399a432d"
url: "https://pub.dev"
source: hosted
- version: "3.4.9"
+ version: "3.4.10"
async:
dependency: transitive
description:
@@ -161,7 +161,7 @@ packages:
path: ".."
relative: true
source: path
- version: "3.0.0-alpha.4"
+ version: "3.0.0"
matcher:
dependency: transitive
description:
@@ -198,18 +198,18 @@ packages:
dependency: "direct main"
description:
name: path_provider
- sha256: a1aa8aaa2542a6bc57e381f132af822420216c80d4781f7aa085ca3229208aaa
+ sha256: b27217933eeeba8ff24845c34003b003b2b22151de3c908d0e679e8fe1aa078b
url: "https://pub.dev"
source: hosted
- version: "2.1.1"
+ version: "2.1.2"
path_provider_android:
dependency: transitive
description:
name: path_provider_android
- sha256: e595b98692943b4881b219f0a9e3945118d3c16bd7e2813f98ec6e532d905f72
+ sha256: "477184d672607c0a3bf68fbbf601805f92ef79c82b64b4d6eb318cbca4c48668"
url: "https://pub.dev"
source: hosted
- version: "2.2.1"
+ version: "2.2.2"
path_provider_foundation:
dependency: transitive
description:
@@ -230,10 +230,10 @@ packages:
dependency: transitive
description:
name: path_provider_platform_interface
- sha256: "94b1e0dd80970c1ce43d5d4e050a9918fce4f4a775e6142424c30a29a363265c"
+ sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334"
url: "https://pub.dev"
source: hosted
- version: "2.1.1"
+ version: "2.1.2"
path_provider_windows:
dependency: transitive
description:
@@ -246,18 +246,18 @@ packages:
dependency: transitive
description:
name: platform
- sha256: "0a279f0707af40c890e80b1e9df8bb761694c074ba7e1d4ab1bc4b728e200b59"
+ sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec"
url: "https://pub.dev"
source: hosted
- version: "3.1.3"
+ version: "3.1.4"
plugin_platform_interface:
dependency: transitive
description:
name: plugin_platform_interface
- sha256: f4f88d4a900933e7267e2b353594774fc0d07fb072b47eedcd5b54e1ea3269f8
+ sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
url: "https://pub.dev"
source: hosted
- version: "2.1.7"
+ version: "2.1.8"
pointycastle:
dependency: transitive
description:
@@ -347,18 +347,18 @@ packages:
dependency: transitive
description:
name: win32
- sha256: b0f37db61ba2f2e9b7a78a1caece0052564d1bc70668156cf3a29d676fe4e574
+ sha256: "464f5674532865248444b4c3daca12bd9bf2d7c47f759ce2617986e7229494a8"
url: "https://pub.dev"
source: hosted
- version: "5.1.1"
+ version: "5.2.0"
xdg_directories:
dependency: transitive
description:
name: xdg_directories
- sha256: "589ada45ba9e39405c198fe34eb0f607cddb2108527e658136120892beac46d2"
+ sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d
url: "https://pub.dev"
source: hosted
- version: "1.0.3"
+ version: "1.0.4"
sdks:
dart: ">=3.2.0 <4.0.0"
flutter: ">=3.16.0"
diff --git a/lib/src/animation/content/base_stroke_content.dart b/lib/src/animation/content/base_stroke_content.dart
index 9694509f..1681128c 100644
--- a/lib/src/animation/content/base_stroke_content.dart
+++ b/lib/src/animation/content/base_stroke_content.dart
@@ -14,7 +14,6 @@ import '../../model/layer/base_layer.dart';
import '../../utils.dart';
import '../../utils/dash_path.dart';
import '../../utils/misc.dart';
-import '../../utils/path_factory.dart';
import '../../utils/utils.dart';
import '../../value/drop_shadow.dart';
import '../../value/lottie_value_callback.dart';
@@ -29,8 +28,8 @@ import 'trim_path_content.dart';
abstract class BaseStrokeContent
implements KeyPathElementContent, DrawingContent {
- final Path _path = PathFactory.create();
- final Path _trimPathPath = PathFactory.create();
+ final Path _path = Path();
+ final Path _trimPathPath = Path();
final LottieDrawable lottieDrawable;
final BaseLayer layer;
final List<_PathGroup> _pathGroups = <_PathGroup>[];
diff --git a/lib/src/animation/content/content_group.dart b/lib/src/animation/content/content_group.dart
index b1969ce1..a3347aae 100644
--- a/lib/src/animation/content/content_group.dart
+++ b/lib/src/animation/content/content_group.dart
@@ -8,7 +8,6 @@ import '../../model/key_path.dart';
import '../../model/key_path_element.dart';
import '../../model/layer/base_layer.dart';
import '../../utils.dart';
-import '../../utils/path_factory.dart';
import '../../value/lottie_value_callback.dart';
import '../keyframe/transform_keyframe_animation.dart';
import 'content.dart';
@@ -42,7 +41,7 @@ class ContentGroup implements DrawingContent, PathContent, KeyPathElement {
}
final Matrix4 _matrix = Matrix4.identity();
- final Path _path = PathFactory.create();
+ final Path _path = Path();
@override
final String? name;
diff --git a/lib/src/animation/content/ellipse_content.dart b/lib/src/animation/content/ellipse_content.dart
index 142cbcf1..1c622b9a 100644
--- a/lib/src/animation/content/ellipse_content.dart
+++ b/lib/src/animation/content/ellipse_content.dart
@@ -7,7 +7,6 @@ import '../../model/key_path.dart';
import '../../model/layer/base_layer.dart';
import '../../utils.dart';
import '../../utils/misc.dart';
-import '../../utils/path_factory.dart';
import '../../value/lottie_value_callback.dart';
import '../keyframe/base_keyframe_animation.dart';
import 'compound_trim_path_content.dart';
@@ -19,7 +18,7 @@ import 'trim_path_content.dart';
class EllipseContent implements PathContent, KeyPathElementContent {
static const _ellipseControlPointPercentage = 0.55228;
- final Path _path = PathFactory.create();
+ final Path _path = Path();
@override
final String? name;
diff --git a/lib/src/animation/content/fill_content.dart b/lib/src/animation/content/fill_content.dart
index 6edc00b5..6978c194 100644
--- a/lib/src/animation/content/fill_content.dart
+++ b/lib/src/animation/content/fill_content.dart
@@ -9,7 +9,6 @@ import '../../model/key_path.dart';
import '../../model/layer/base_layer.dart';
import '../../utils.dart';
import '../../utils/misc.dart';
-import '../../utils/path_factory.dart';
import '../../value/drop_shadow.dart';
import '../../value/lottie_value_callback.dart';
import '../keyframe/base_keyframe_animation.dart';
@@ -21,7 +20,7 @@ import 'key_path_element_content.dart';
import 'path_content.dart';
class FillContent implements DrawingContent, KeyPathElementContent {
- final Path _path = PathFactory.create();
+ final Path _path = Path();
final BaseLayer layer;
@override
final String? name;
diff --git a/lib/src/animation/content/gradient_fill_content.dart b/lib/src/animation/content/gradient_fill_content.dart
index 29242cd0..a03633ac 100644
--- a/lib/src/animation/content/gradient_fill_content.dart
+++ b/lib/src/animation/content/gradient_fill_content.dart
@@ -11,7 +11,6 @@ import '../../model/key_path.dart';
import '../../model/layer/base_layer.dart';
import '../../utils.dart';
import '../../utils/misc.dart';
-import '../../utils/path_factory.dart';
import '../../value/drop_shadow.dart';
import '../../value/lottie_value_callback.dart';
import '../keyframe/base_keyframe_animation.dart';
@@ -29,7 +28,7 @@ class GradientFillContent implements DrawingContent, KeyPathElementContent {
final GradientFill _fill;
final _linearGradientCache = {};
final _radialGradientCache = {};
- final _path = PathFactory.create();
+ final _path = Path();
final _paint = Paint();
final _paths = [];
final BaseKeyframeAnimation _colorAnimation;
diff --git a/lib/src/animation/content/merge_paths_content.dart b/lib/src/animation/content/merge_paths_content.dart
index 1a00d612..b73347f8 100644
--- a/lib/src/animation/content/merge_paths_content.dart
+++ b/lib/src/animation/content/merge_paths_content.dart
@@ -1,16 +1,15 @@
import 'dart:ui';
import '../../model/content/merge_paths.dart';
import '../../utils.dart';
-import '../../utils/path_factory.dart';
import 'content.dart';
import 'content_group.dart';
import 'greedy_content.dart';
import 'path_content.dart';
class MergePathsContent implements PathContent, GreedyContent {
- final Path _firstPath = PathFactory.create();
- final Path _remainderPath = PathFactory.create();
- final Path _path = PathFactory.create();
+ final Path _firstPath = Path();
+ final Path _remainderPath = Path();
+ final Path _path = Path();
final List _pathContents = [];
final MergePaths _mergePaths;
diff --git a/lib/src/animation/content/polystar_content.dart b/lib/src/animation/content/polystar_content.dart
index 50a26b0d..03cab509 100644
--- a/lib/src/animation/content/polystar_content.dart
+++ b/lib/src/animation/content/polystar_content.dart
@@ -8,7 +8,6 @@ import '../../model/content/shape_trim_path.dart';
import '../../model/key_path.dart';
import '../../model/layer/base_layer.dart';
import '../../utils/misc.dart';
-import '../../utils/path_factory.dart';
import '../../value/lottie_value_callback.dart';
import '../keyframe/base_keyframe_animation.dart';
import 'compound_trim_path_content.dart';
@@ -24,7 +23,7 @@ class PolystarContent implements PathContent, KeyPathElementContent {
/// work otherwise.
static const _polystarMagicNumber = .47829;
static const _polygonMagicNumber = .25;
- final _path = PathFactory.create();
+ final _path = Path();
final LottieDrawable lottieDrawable;
final PolystarShape _polystarShape;
diff --git a/lib/src/animation/content/rectangle_content.dart b/lib/src/animation/content/rectangle_content.dart
index db7dc0ae..1986cd1a 100644
--- a/lib/src/animation/content/rectangle_content.dart
+++ b/lib/src/animation/content/rectangle_content.dart
@@ -8,7 +8,6 @@ import '../../model/content/shape_trim_path.dart';
import '../../model/key_path.dart';
import '../../model/layer/base_layer.dart';
import '../../utils/misc.dart';
-import '../../utils/path_factory.dart';
import '../../value/lottie_value_callback.dart';
import '../keyframe/base_keyframe_animation.dart';
import 'compound_trim_path_content.dart';
@@ -19,7 +18,7 @@ import 'rounded_corners_content.dart';
import 'trim_path_content.dart';
class RectangleContent implements KeyPathElementContent, PathContent {
- final _path = PathFactory.create();
+ final _path = Path();
@override
final String? name;
diff --git a/lib/src/animation/content/repeater_content.dart b/lib/src/animation/content/repeater_content.dart
index 19eb82bb..71ee6490 100644
--- a/lib/src/animation/content/repeater_content.dart
+++ b/lib/src/animation/content/repeater_content.dart
@@ -7,7 +7,6 @@ import '../../model/key_path.dart';
import '../../model/layer/base_layer.dart';
import '../../utils.dart';
import '../../utils/misc.dart';
-import '../../utils/path_factory.dart';
import '../../value/lottie_value_callback.dart';
import '../keyframe/base_keyframe_animation.dart';
import '../keyframe/transform_keyframe_animation.dart';
@@ -25,7 +24,7 @@ class RepeaterContent
GreedyContent,
KeyPathElementContent {
final Matrix4 _matrix = Matrix4.identity();
- final _path = PathFactory.create();
+ final _path = Path();
final LottieDrawable lottieDrawable;
final BaseLayer layer;
diff --git a/lib/src/animation/content/shape_content.dart b/lib/src/animation/content/shape_content.dart
index 38d5067b..1c550356 100644
--- a/lib/src/animation/content/shape_content.dart
+++ b/lib/src/animation/content/shape_content.dart
@@ -4,7 +4,6 @@ import '../../model/content/shape_path.dart';
import '../../model/content/shape_trim_path.dart';
import '../../model/layer/base_layer.dart';
import '../../utils.dart';
-import '../../utils/path_factory.dart';
import '../keyframe/shape_keyframe_animation.dart';
import 'compound_trim_path_content.dart';
import 'content.dart';
@@ -13,7 +12,7 @@ import 'shape_modifier_content.dart';
import 'trim_path_content.dart';
class ShapeContent implements PathContent {
- final _path = PathFactory.create();
+ final _path = Path();
final ShapePath _shape;
diff --git a/lib/src/animation/keyframe/shape_keyframe_animation.dart b/lib/src/animation/keyframe/shape_keyframe_animation.dart
index c5c6c434..9d0f94a7 100644
--- a/lib/src/animation/keyframe/shape_keyframe_animation.dart
+++ b/lib/src/animation/keyframe/shape_keyframe_animation.dart
@@ -1,14 +1,13 @@
import 'dart:ui';
import '../../model/content/shape_data.dart';
import '../../utils/misc.dart';
-import '../../utils/path_factory.dart';
import '../../value/keyframe.dart';
import '../content/shape_modifier_content.dart';
import 'base_keyframe_animation.dart';
class ShapeKeyframeAnimation extends BaseKeyframeAnimation {
final ShapeData _tempShapeData = ShapeData.empty();
- final Path _tempPath = PathFactory.create();
+ final Path _tempPath = Path();
List? _shapeModifiers;
ShapeKeyframeAnimation(super.keyframes);
@@ -16,7 +15,7 @@ class ShapeKeyframeAnimation extends BaseKeyframeAnimation {
@override
Path getValue(Keyframe keyframe, double keyframeProgress) {
var startShapeData = keyframe.startValue!;
- var endShapeData = keyframe.endValue!;
+ var endShapeData = keyframe.endValue ?? startShapeData;
_tempShapeData.interpolateBetween(
startShapeData, endShapeData, keyframeProgress);
diff --git a/lib/src/model/content/gradient_color.dart b/lib/src/model/content/gradient_color.dart
index d3884de0..ef427527 100644
--- a/lib/src/model/content/gradient_color.dart
+++ b/lib/src/model/content/gradient_color.dart
@@ -1,16 +1,32 @@
import 'dart:ui';
+import '../../utils.dart';
import '../../utils/collection.dart';
import '../../utils/gamma_evaluator.dart';
+// ignore_for_file: avoid_equals_and_hash_code_on_mutable_classes
+
class GradientColor {
final List positions;
final List colors;
- GradientColor(this.positions, this.colors);
+ const GradientColor(this.positions, this.colors);
int get size => colors.length;
void lerp(GradientColor gc1, GradientColor gc2, double 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 == gc2) {
+ _copyFrom(gc1);
+ return;
+ } else if (progress <= 0) {
+ _copyFrom(gc1);
+ return;
+ } else if (progress >= 1) {
+ _copyFrom(gc2);
+ return;
+ }
+
if (gc1.colors.length != gc2.colors.length) {
throw Exception('Cannot interpolate between gradients. '
'Lengths vary (${gc1.colors.length} vs ${gc2.colors.length})');
@@ -59,4 +75,30 @@ class GradientColor {
var fraction = (position - startPosition) / (endPosition - startPosition);
return GammaEvaluator.evaluate(fraction, startColor, endColor);
}
+
+ @override
+ bool operator ==(Object other) {
+ if (identical(this, other)) {
+ return true;
+ }
+ if (other is! GradientColor) {
+ return false;
+ }
+ return const ListEquality().equals(positions, other.positions) &&
+ const ListEquality().equals(colors, other.colors);
+ }
+
+ @override
+ int get hashCode {
+ var result = Object.hashAll(positions);
+ result = 31 * result + Object.hashAll(colors);
+ return result;
+ }
+
+ void _copyFrom(GradientColor other) {
+ for (var i = 0; i < other.colors.length; i++) {
+ positions[i] = other.positions[i];
+ colors[i] = other.colors[i];
+ }
+ }
}
diff --git a/lib/src/model/layer/solid_layer.dart b/lib/src/model/layer/solid_layer.dart
index ad9bfd2d..9441379b 100644
--- a/lib/src/model/layer/solid_layer.dart
+++ b/lib/src/model/layer/solid_layer.dart
@@ -5,14 +5,13 @@ import '../../animation/keyframe/value_callback_keyframe_animation.dart';
import '../../lottie_drawable.dart';
import '../../lottie_property.dart';
import '../../utils.dart';
-import '../../utils/path_factory.dart';
import '../../value/lottie_value_callback.dart';
import 'base_layer.dart';
import 'layer.dart';
class SolidLayer extends BaseLayer {
final Paint paint = Paint()..style = PaintingStyle.fill;
- final Path path = PathFactory.create();
+ final Path path = Path();
BaseKeyframeAnimation? _colorFilterAnimation;
BaseKeyframeAnimation? _colorAnimation;
@@ -29,6 +28,8 @@ class SolidLayer extends BaseLayer {
return;
}
+ paint.color = _colorAnimation?.value ?? layerModel.solidColor;
+
var opacity = transform.opacity?.value ?? 100;
var alpha = (parentAlpha /
255.0 *
@@ -36,9 +37,7 @@ class SolidLayer extends BaseLayer {
255.0)
.round();
paint.setAlpha(alpha);
- if (_colorAnimation?.value case var color?) {
- paint.color = color;
- }
+
if (_colorFilterAnimation != null) {
paint.colorFilter = _colorFilterAnimation!.value;
}
diff --git a/lib/src/utils/dash_path.dart b/lib/src/utils/dash_path.dart
index 5922f3eb..63778955 100644
--- a/lib/src/utils/dash_path.dart
+++ b/lib/src/utils/dash_path.dart
@@ -1,6 +1,5 @@
import 'dart:math';
import 'dart:ui';
-import 'path_factory.dart';
Path dashPath(
Path source, {
@@ -10,7 +9,7 @@ Path dashPath(
assert(intervals.length >= 2);
phase ??= 0;
- var dest = PathFactory.create();
+ var dest = Path();
for (final metric in source.computeMetrics()) {
for (var dash in _dashes(metric.length, intervals, phase)) {
dest.addPath(metric.extractPath(dash.left, dash.right), Offset.zero);
diff --git a/lib/src/utils/gamma_evaluator.dart b/lib/src/utils/gamma_evaluator.dart
index d2fae25f..70768051 100644
--- a/lib/src/utils/gamma_evaluator.dart
+++ b/lib/src/utils/gamma_evaluator.dart
@@ -26,9 +26,16 @@ class GammaEvaluator {
}
static Color evaluate(double fraction, Color startColor, Color endColor) {
+ // Fast return in case start and end is the same
+ // or if fraction is at start/end or out of [0,1] bounds
if (startColor == endColor) {
return startColor;
+ } else if (fraction <= 0) {
+ return startColor;
+ } else if (fraction >= 1) {
+ return endColor;
}
+
var startA = startColor.alpha / 255.0;
var startR = startColor.red / 255.0;
var startG = startColor.green / 255.0;
diff --git a/lib/src/utils/path_factory.dart b/lib/src/utils/path_factory.dart
deleted file mode 100644
index b5696caf..00000000
--- a/lib/src/utils/path_factory.dart
+++ /dev/null
@@ -1,164 +0,0 @@
-import 'dart:typed_data';
-import 'dart:ui';
-
-class PathFactory {
- static Path create() {
- return Path();
- }
-}
-
-class FakePath implements Path {
- @override
- PathFillType fillType = PathFillType.nonZero;
-
- @override
- void addArc(Rect oval, double startAngle, double sweepAngle) {
- // TODO: implement addArc
- }
-
- @override
- void addOval(Rect oval) {
- // TODO: implement addOval
- }
-
- @override
- void addPath(Path path, Offset offset, {Float64List? matrix4}) {
- // TODO: implement addPath
- }
-
- @override
- void addPolygon(List points, bool close) {
- // TODO: implement addPolygon
- }
-
- @override
- void addRRect(RRect rrect) {
- // TODO: implement addRRect
- }
-
- @override
- void addRect(Rect rect) {
- // TODO: implement addRect
- }
-
- @override
- void arcTo(
- Rect rect, double startAngle, double sweepAngle, bool forceMoveTo) {
- // TODO: implement arcTo
- }
-
- @override
- void arcToPoint(Offset arcEnd,
- {Radius radius = Radius.zero,
- double rotation = 0.0,
- bool largeArc = false,
- bool clockwise = true}) {
- // TODO: implement arcToPoint
- }
-
- @override
- void close() {
- // TODO: implement close
- }
-
- @override
- PathMetrics computeMetrics({bool forceClosed = false}) {
- // TODO: implement computeMetrics
- throw UnimplementedError();
- }
-
- @override
- void conicTo(double x1, double y1, double x2, double y2, double w) {
- // TODO: implement conicTo
- }
-
- @override
- bool contains(Offset point) {
- // TODO: implement contains
- throw UnimplementedError();
- }
-
- @override
- void cubicTo(
- double x1, double y1, double x2, double y2, double x3, double y3) {
- // TODO: implement cubicTo
- }
-
- @override
- void extendWithPath(Path path, Offset offset, {Float64List? matrix4}) {
- // TODO: implement extendWithPath
- }
-
- @override
- Rect getBounds() {
- // TODO: implement getBounds
- throw UnimplementedError();
- }
-
- @override
- void lineTo(double x, double y) {
- // TODO: implement lineTo
- }
-
- @override
- void moveTo(double x, double y) {
- // TODO: implement moveTo
- }
-
- @override
- void quadraticBezierTo(double x1, double y1, double x2, double y2) {
- // TODO: implement quadraticBezierTo
- }
-
- @override
- void relativeArcToPoint(Offset arcEndDelta,
- {Radius radius = Radius.zero,
- double rotation = 0.0,
- bool largeArc = false,
- bool clockwise = true}) {
- // TODO: implement relativeArcToPoint
- }
-
- @override
- void relativeConicTo(double x1, double y1, double x2, double y2, double w) {
- // TODO: implement relativeConicTo
- }
-
- @override
- void relativeCubicTo(
- double x1, double y1, double x2, double y2, double x3, double y3) {
- // TODO: implement relativeCubicTo
- }
-
- @override
- void relativeLineTo(double dx, double dy) {
- // TODO: implement relativeLineTo
- }
-
- @override
- void relativeMoveTo(double dx, double dy) {
- // TODO: implement relativeMoveTo
- }
-
- @override
- void relativeQuadraticBezierTo(double x1, double y1, double x2, double y2) {
- // TODO: implement relativeQuadraticBezierTo
- }
-
- @override
- void reset() {
- // TODO: implement reset
- }
-
- @override
- Path shift(Offset offset) {
- // TODO: implement shift
- throw UnimplementedError();
- }
-
- @override
- Path transform(Float64List matrix4) {
- // TODO: implement transform
- throw UnimplementedError();
- }
-}
diff --git a/lib/src/utils/path_interpolator.dart b/lib/src/utils/path_interpolator.dart
index e1375a60..5bc28734 100644
--- a/lib/src/utils/path_interpolator.dart
+++ b/lib/src/utils/path_interpolator.dart
@@ -1,5 +1,5 @@
+import 'dart:ui';
import 'package:flutter/animation.dart';
-import 'path_factory.dart';
// ignore: must_be_immutable
class PathInterpolator extends Curve {
@@ -24,7 +24,7 @@ class PathInterpolator extends Curve {
}
void _initialize() {
- final path = PathFactory.create();
+ final path = Path();
path.moveTo(0.0, 0.0);
path.cubicTo(controlX1, controlY1, controlX2, controlY2, 1.0, 1.0);
diff --git a/lib/src/utils/utils.dart b/lib/src/utils/utils.dart
index 840c153b..74aabe3c 100644
--- a/lib/src/utils/utils.dart
+++ b/lib/src/utils/utils.dart
@@ -4,12 +4,11 @@ import '../animation/content/trim_path_content.dart';
import '../l.dart';
import '../utils.dart';
import 'misc.dart';
-import 'path_factory.dart';
class Utils {
static Path createPath(
Offset startPoint, Offset endPoint, Offset? cp1, Offset? cp2) {
- var path = PathFactory.create();
+ var path = Path();
path.moveTo(startPoint.dx, startPoint.dy);
if (cp1 != null &&
diff --git a/pubspec.lock b/pubspec.lock
index a833af15..d6aa2ddc 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -21,10 +21,10 @@ packages:
dependency: "direct main"
description:
name: archive
- sha256: "7b875fd4a20b165a3084bd2d210439b22ebc653f21cea4842729c0c30c82596b"
+ sha256: "22600aa1e926be775fa5fe7e6894e7fb3df9efda8891c73f70fb3262399a432d"
url: "https://pub.dev"
source: hosted
- version: "3.4.9"
+ version: "3.4.10"
args:
dependency: transitive
description:
diff --git a/pubspec.yaml b/pubspec.yaml
index 2130b7f6..ff1d85d9 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,8 +1,11 @@
name: lottie
description: Render After Effects animations natively on Flutter. This package is a pure Dart implementation of a Lottie player.
-version: 3.0.0-alpha.4
+version: 3.0.0
repository: https://github.com/xvrh/lottie-flutter
+funding:
+ - https://www.buymeacoffee.com/xvrh
+
environment:
sdk: '^3.2.0'
flutter: '>=3.16.0'
diff --git a/test/goldens/all/Tests/gradientcolorkeyframeanimation.png b/test/goldens/all/Tests/gradientcolorkeyframeanimation.png
new file mode 100644
index 00000000..aec4458d
Binary files /dev/null and b/test/goldens/all/Tests/gradientcolorkeyframeanimation.png differ
diff --git a/test/goldens/all/Tests/largesquare.png b/test/goldens/all/Tests/largesquare.png
new file mode 100644
index 00000000..f2853a81
Binary files /dev/null and b/test/goldens/all/Tests/largesquare.png differ
diff --git a/test/goldens/all/Tests/nullendshape.png b/test/goldens/all/Tests/nullendshape.png
new file mode 100644
index 00000000..d6bc8911
Binary files /dev/null and b/test/goldens/all/Tests/nullendshape.png differ