diff --git a/LottieSample/screenshots/LoopPlayOnce 100.png b/LottieSample/screenshots/LoopPlayOnce 100.png index e993793f40..3c3118d169 100644 Binary files a/LottieSample/screenshots/LoopPlayOnce 100.png and b/LottieSample/screenshots/LoopPlayOnce 100.png differ diff --git a/LottieSample/src/main/assets/AParenting.json b/LottieSample/src/main/assets/AParenting.json new file mode 100644 index 0000000000..d891a5018c --- /dev/null +++ b/LottieSample/src/main/assets/AParenting.json @@ -0,0 +1 @@ +{"assets":[],"layers":[{"ddd":0,"ind":0,"ty":4,"nm":"Shape Layer 2","parent":1,"ks":{"o":{"k":100},"r":{"k":0},"p":{"k":[100,3,0]},"a":{"k":[0,0,0]},"s":{"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"k":[67.102,70.805]},"p":{"k":[0,0]},"r":{"k":0},"nm":"Rectangle Path 1"},{"ty":"st","fillEnabled":true,"c":{"k":[1,1,1,1]},"o":{"k":100},"w":{"k":2},"lc":1,"lj":1,"ml":4,"nm":"Stroke 1"},{"ty":"fl","fillEnabled":true,"c":{"k":[0,1,0.23,1]},"o":{"k":100},"nm":"Fill 1"},{"ty":"tr","p":{"k":[0,0],"ix":2},"a":{"k":[0,0],"ix":1},"s":{"k":[100,100],"ix":3},"r":{"k":0,"ix":6},"o":{"k":100,"ix":7},"sk":{"k":0,"ix":4},"sa":{"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1"}],"ip":0,"op":120,"st":0,"bm":0,"sr":1},{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","ks":{"o":{"k":100},"r":{"k":0},"p":{"k":[48,102,0]},"a":{"k":[0,0,0]},"s":{"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"k":[67.102,70.805]},"p":{"k":[0,0]},"r":{"k":0},"nm":"Rectangle Path 1"},{"ty":"st","fillEnabled":true,"c":{"k":[1,1,1,1]},"o":{"k":100},"w":{"k":2},"lc":1,"lj":1,"ml":4,"nm":"Stroke 1"},{"ty":"fl","fillEnabled":true,"c":{"k":[1,0,0,1]},"o":{"k":100},"nm":"Fill 1"},{"ty":"tr","p":{"k":[0,0],"ix":2},"a":{"k":[0,0],"ix":1},"s":{"k":[100,100],"ix":3},"r":{"k":0,"ix":6},"o":{"k":100,"ix":7},"sk":{"k":0,"ix":4},"sa":{"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1"}],"ip":0,"op":120,"st":0,"bm":0,"sr":1}],"v":"4.4.26","ddd":0,"ip":0,"op":120,"fr":60,"w":200,"h":200} \ No newline at end of file diff --git a/LottieSample/src/main/assets/Amelie_A.json b/LottieSample/src/main/assets/Amelie_A.json new file mode 100644 index 0000000000..69158a2db0 --- /dev/null +++ b/LottieSample/src/main/assets/Amelie_A.json @@ -0,0 +1 @@ +{"assets":[],"layers":[{"ddd":0,"ind":0,"ty":4,"nm":"Shape Layer 3","ks":{"o":{"k":100},"r":{"k":0},"p":{"k":[275,325,0]},"a":{"k":[0,0,0]},"s":{"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"k":[{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"n":"0p833_1_0p167_0","t":5,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-5,118],[-0.5,117.5],[4,118]],"c":false}],"e":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-2,118],[-0.5,117.5],[2,118]],"c":false}]},{"i":{"x":0.34,"y":1},"o":{"x":0.333,"y":0},"n":"0p34_1_0p333_0","t":6,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-2,118],[-0.5,117.5],[2,118]],"c":false}],"e":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-0.5,118],[-0.5,-136.579],[-0.5,118]],"c":false}]},{"i":{"x":0.468,"y":1},"o":{"x":0.583,"y":0},"n":"0p468_1_0p583_0","t":11,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-0.5,118],[-0.5,-136.579],[-0.5,118]],"c":false}],"e":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-108.5,118],[-0.5,-107.539],[108.5,118]],"c":false}]},{"i":{"x":0.115,"y":1},"o":{"x":0.504,"y":0},"n":"0p115_1_0p504_0","t":17,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-108.5,118],[-0.5,-107.539],[108.5,118]],"c":false}],"e":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-101,118],[-0.5,-115.5],[101,118]],"c":false}]},{"t":25}]},"nm":"Path 1"},{"ty":"st","fillEnabled":true,"c":{"k":[0.15,0.8,0.55,1]},"o":{"k":100},"w":{"k":20},"lc":2,"lj":2,"nm":"Stroke 1"},{"ty":"tr","p":{"k":[0,0],"ix":2},"a":{"k":[0,0],"ix":1},"s":{"k":[100,100],"ix":3},"r":{"k":0,"ix":6},"o":{"k":100,"ix":7},"sk":{"k":0,"ix":4},"sa":{"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1"}],"ip":5,"op":51,"st":-1,"bm":0,"sr":1},{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","ks":{"o":{"k":100},"r":{"k":0},"p":{"k":[275,355,0]},"a":{"k":[0,0,0]},"s":{"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":12,"s":[{"i":[[0,0],[2.385,-18]],"o":[[0,0],[-60,0]],"v":[[2.385,24.5],[-3.135,24.5]],"c":false}],"e":[{"i":[[0,0],[14.777,-8]],"o":[[0,0],[-60,0]],"v":[[12.027,24.5],[-12.777,24.5]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":13,"s":[{"i":[[0,0],[14.777,-8]],"o":[[0,0],[-60,0]],"v":[[12.027,24.5],[-12.777,24.5]],"c":false}],"e":[{"i":[[0,0],[73.412,-0.5]],"o":[[0,0],[-60,0]],"v":[[34.162,24.5],[-34.412,24.5]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":14,"s":[{"i":[[0,0],[73.412,-0.5]],"o":[[0,0],[-60,0]],"v":[[34.162,24.5],[-34.412,24.5]],"c":false}],"e":[{"i":[[0,0],[72.346,10.21]],"o":[[0,0],[-60,0]],"v":[[62.096,24.5],[-62.346,24.5]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":15,"s":[{"i":[[0,0],[72.346,10.21]],"o":[[0,0],[-60,0]],"v":[[62.096,24.5],[-62.346,24.5]],"c":false}],"e":[{"i":[[0,0],[71.879,17.097]],"o":[[0,0],[-60,0]],"v":[[74.129,24.5],[-74.629,24.5]],"c":false}]},{"i":{"x":0.362,"y":1},"o":{"x":0.167,"y":0.167},"n":"0p362_1_0p167_0p167","t":16,"s":[{"i":[[0,0],[71.879,17.097]],"o":[[0,0],[-60,0]],"v":[[74.129,24.5],[-74.629,24.5]],"c":false}],"e":[{"i":[[0,0],[71.75,19]],"o":[[0,0],[-60,0]],"v":[[77.25,24.5],[-77.75,24.5]],"c":false}]},{"i":{"x":0.31,"y":1},"o":{"x":0.333,"y":0},"n":"0p31_1_0p333_0","t":17,"s":[{"i":[[0,0],[71.75,19]],"o":[[0,0],[-60,0]],"v":[[77.25,24.5],[-77.75,24.5]],"c":false}],"e":[{"i":[[0,0],[75.75,0]],"o":[[0,0],[-60,0]],"v":[[71.5,24.5],[-72.75,24.5]],"c":false}]},{"t":25}]},"nm":"Path 1"},{"ty":"st","fillEnabled":true,"c":{"k":[0.15,0.8,0.55,1]},"o":{"k":100},"w":{"k":10},"lc":2,"lj":2,"nm":"Stroke 1"},{"ty":"tr","p":{"k":[0,0],"ix":2},"a":{"k":[0,0],"ix":1},"s":{"k":[100,100],"ix":3},"r":{"k":0,"ix":6},"o":{"k":100,"ix":7},"sk":{"k":0,"ix":4},"sa":{"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1"}],"ip":12,"op":51,"st":-7,"bm":0,"sr":1},{"ddd":0,"ind":2,"ty":4,"nm":"shoot 2","ks":{"o":{"k":100},"r":{"k":0},"p":{"k":[274.75,441.25,0]},"a":{"k":[68,81,0]},"s":{"k":[-100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"k":{"i":[[0,0],[22.25,-151]],"o":[[0,0],[0,0]],"v":[[68,81.5],[-32,81.25]],"c":false}},"nm":"Path 1"},{"ty":"st","fillEnabled":true,"c":{"k":[0.95,0.57,0,1]},"o":{"k":100},"w":{"k":10},"lc":2,"lj":2,"nm":"Stroke 1"},{"ty":"tr","p":{"k":[0,0],"ix":2},"a":{"k":[0,0],"ix":1},"s":{"k":[100,100],"ix":3},"r":{"k":0,"ix":6},"o":{"k":100,"ix":7},"sk":{"k":0,"ix":4},"sa":{"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1"},{"ty":"tm","s":{"k":[{"i":{"x":[0.139],"y":[1]},"o":{"x":[0.333],"y":[0]},"n":["0p139_1_0p333_0"],"t":7,"s":[0],"e":[100]},{"t":14}],"ix":1},"e":{"k":[{"i":{"x":[0.214],"y":[1]},"o":{"x":[0.098],"y":[0]},"n":["0p214_1_0p098_0"],"t":5,"s":[0],"e":[100]},{"t":14}],"ix":2},"o":{"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1"}],"ip":5,"op":14,"st":0,"bm":0,"sr":1},{"ddd":0,"ind":3,"ty":4,"nm":"shoot 1","ks":{"o":{"k":100},"r":{"k":0},"p":{"k":[274.75,441.25,0]},"a":{"k":[68,81,0]},"s":{"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"k":{"i":[[0,0],[22.25,-151]],"o":[[0,0],[0,0]],"v":[[68,81.5],[-32,81.25]],"c":false}},"nm":"Path 1"},{"ty":"st","fillEnabled":true,"c":{"k":[0.95,0.57,0,1]},"o":{"k":100},"w":{"k":10},"lc":2,"lj":2,"nm":"Stroke 1"},{"ty":"tr","p":{"k":[0,0],"ix":2},"a":{"k":[0,0],"ix":1},"s":{"k":[100,100],"ix":3},"r":{"k":0,"ix":6},"o":{"k":100,"ix":7},"sk":{"k":0,"ix":4},"sa":{"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1"},{"ty":"tm","s":{"k":[{"i":{"x":[0.139],"y":[1]},"o":{"x":[0.333],"y":[0]},"n":["0p139_1_0p333_0"],"t":7,"s":[0],"e":[100]},{"t":14}],"ix":1},"e":{"k":[{"i":{"x":[0.214],"y":[1]},"o":{"x":[0.098],"y":[0]},"n":["0p214_1_0p098_0"],"t":5,"s":[0],"e":[100]},{"t":14}],"ix":2},"o":{"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1"}],"ip":5,"op":14,"st":0,"bm":0,"sr":1},{"ddd":0,"ind":4,"ty":4,"nm":"drop line 2","ks":{"o":{"k":100},"r":{"k":0},"p":{"k":[275,351,0]},"a":{"k":[0,0,0]},"s":{"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0,114.667],[0,-214.667]],"c":false}},"nm":"Path 1"},{"ty":"st","fillEnabled":true,"c":{"k":[0.95,0.57,0,1]},"o":{"k":100},"w":{"k":10},"lc":2,"lj":2,"nm":"Stroke 1"},{"ty":"tm","s":{"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"n":["0p667_1_0p333_0"],"t":0,"s":[100],"e":[0]},{"t":6}],"ix":1},"e":{"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"n":["0p667_1_0p333_0"],"t":1,"s":[100],"e":[0]},{"t":6}],"ix":2},"o":{"k":0,"ix":3},"m":1,"ix":3,"nm":"Trim Paths 1"},{"ty":"tr","p":{"k":[0,0],"ix":2},"a":{"k":[0,0],"ix":1},"s":{"k":[100,100],"ix":3},"r":{"k":0,"ix":6},"o":{"k":100,"ix":7},"sk":{"k":0,"ix":4},"sa":{"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1"}],"ip":0,"op":6,"st":-5,"bm":0,"sr":1}],"v":"4.4.26","ddd":0,"ip":0,"op":51,"fr":25,"w":550,"h":650} \ No newline at end of file diff --git a/LottieSample/src/main/assets/Mobilo_C3.json b/LottieSample/src/main/assets/Mobilo_C3.json new file mode 100644 index 0000000000..28c7b1893a --- /dev/null +++ b/LottieSample/src/main/assets/Mobilo_C3.json @@ -0,0 +1 @@ +{"assets":[],"v":"4.3.1","ddd":0,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"goop_17","ks":{"o":{"k":100},"r":{"k":0},"p":{"k":[250,300,0]},"a":{"k":[0,0,0]},"s":{"k":[100,100,100]}},"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","closed":true,"ks":{"k":{"i":[[4.5,3.5],[0,0],[0,0],[4.924,-1.97],[4,0],[4.25,0],[3.75,0.25],[4,-0.5],[5,2.5],[3,5.5],[2.5,-1.5]],"o":[[-4.5,-3.5],[0,0],[0,0],[-5,2],[-4,0],[-4.25,0],[-3.75,-0.25],[-4,0.5],[-5,-2.5],[-3,-5.5],[-2.5,1.5]],"v":[[-32.25,38],[-8.5,69],[53.25,55],[51,47.5],[40.75,49.25],[28.25,52.25],[18.5,50.25],[8.25,57.25],[-6.5,53.5],[-16.75,44],[-24,38.75]]}},"nm":"Path 1"},{"ind":1,"ty":"sh","closed":true,"ks":{"k":{"i":[[3.75,0.25],[3.5,-0.75],[5.25,0],[5.5,-0.25],[3.75,-1],[4.25,-1.25],[6,-4],[-38.5,6.25],[2.75,1]],"o":[[-3.75,-0.25],[-3.5,0.75],[-5.25,0],[-5.5,0.25],[-3.75,1],[-4.25,1.25],[-6,4],[38.5,-6.25],[-2.75,-1]],"v":[[63.5,-153.75],[54,-149],[19.75,-147.5],[6.25,-149.75],[-10.75,-141.25],[-23.75,-139.5],[-36.75,-130],[23.25,-130.75],[71,-149.25]]}},"nm":"Path 2"},{"ind":2,"ty":"sh","closed":true,"ks":{"k":{"i":[[-9,0.5],[25.5,-14],[-5.5,5.25],[-4.5,4],[-5.5,3],[-4.5,1.25],[-6,0.75],[-5,0.75]],"o":[[9,-0.5],[-25.5,14],[5.5,-5.25],[4.5,-4],[5.5,-3],[4.5,-1.25],[6,-0.75],[5,-0.75]],"v":[[32,-104.75],[-15.75,-104],[-42,-65.25],[-27.25,-74.75],[-18.25,-90.25],[-3.5,-93],[5,-99.75],[16,-99.25]]}},"nm":"Path 3"},{"ty":"fl","fillEnabled":true,"c":{"k":[0,0.19,0.3,1]},"o":{"k":100},"nm":"Fill 1"},{"ty":"tr","p":{"k":[0,0],"ix":2},"a":{"k":[0,0],"ix":1},"s":{"k":[100,100],"ix":3},"r":{"k":0,"ix":6},"o":{"k":100,"ix":7},"sk":{"k":0,"ix":4},"sa":{"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1"}],"bounds":{"l":-44,"t":-154,"b":69,"r":72},"ip":21,"op":23,"st":1,"bm":0,"sr":1},{"ddd":0,"ind":2,"ty":4,"nm":"goop_16","ks":{"o":{"k":100},"r":{"k":0},"p":{"k":[250,300,0]},"a":{"k":[0,0,0]},"s":{"k":[100,100,100]}},"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","closed":true,"ks":{"k":{"i":[[1.5,0],[0.062,-1.125],[-2.812,-0.125],[0.562,1.375]],"o":[[-1.5,0],[-0.111,1.997],[2.812,0.125],[-0.562,-1.375]],"v":[[56.688,-88.625],[53.812,-87],[59.188,-82.562],[61.25,-85.75]]}},"nm":"Path 1"},{"ind":1,"ty":"sh","closed":true,"ks":{"k":[{"t":21,"s":[{"i":[[0.688,0.125],[0.062,-1.5],[-0.5,0],[0,1.062]],"o":[[-0.688,-0.125],[-0.062,1.5],[0.5,0],[0,-1.062]],"v":[[67.938,-35.5],[66.438,-33.312],[67.812,-30.938],[69.062,-33.125]]}],"h":1},{"t":23,"s":[{"i":[[0.467,0.085],[0.042,-1.02],[-0.34,0],[0,0.723]],"o":[[-0.467,-0.085],[-0.043,1.02],[0.34,0],[0,-0.723]],"v":[[68.031,-28.228],[67.011,-26.74],[67.946,-25.125],[68.796,-26.613]]}],"h":1}]},"nm":"Path 2"},{"ind":2,"ty":"sh","closed":true,"ks":{"k":[{"t":21,"s":[{"i":[[0.25,-1.75],[-2.375,-2.312],[-2.312,0.125],[2.254,2.681]],"o":[[-0.25,1.75],[2.375,2.312],[2.312,-0.125],[-3.625,-4.312]],"v":[[19.5,-26.375],[24.625,-21.75],[29.625,-15.125],[28,-24.188]]}],"h":1},{"t":23,"s":[{"i":[[0.25,-1.75],[-2.375,-2.312],[-2.312,0.125],[2.254,2.681]],"o":[[-0.25,1.75],[2.375,2.312],[2.312,-0.125],[-3.625,-4.312]],"v":[[20.25,-21.625],[26.625,-18],[30.375,-10.375],[26.688,-17.875]]}],"h":1}]},"nm":"Path 3"},{"ind":3,"ty":"sh","closed":true,"ks":{"k":[{"t":21,"s":[{"i":[[0.438,-0.094],[-0.125,-2.375],[-0.5,0],[0.25,2.75]],"o":[[-0.438,0.094],[0.125,2.375],[0.5,0],[-0.25,-2.75]],"v":[[13.125,-17.125],[12.125,-13.875],[14,-10.375],[15,-14.5]]}],"h":1},{"t":23,"s":[{"i":[[0.29,-0.062],[-0.083,-1.576],[-0.332,0],[0.166,1.825]],"o":[[-0.29,0.062],[0.083,1.576],[0.332,0],[-0.166,-1.825]],"v":[[13.247,-11.542],[12.583,-9.385],[13.827,-7.062],[14.491,-9.8]]}],"h":1}]},"nm":"Path 4"},{"ind":4,"ty":"sh","closed":true,"ks":{"k":[{"t":21,"s":[{"i":[[1,-0.75],[-0.125,-1.375],[-2.25,0.125],[0.5,3]],"o":[[-1,0.75],[0.125,1.375],[2.25,-0.125],[-0.5,-3]],"v":[[7.5,-42],[8.375,-38],[10.375,-31.625],[12.125,-38.125]]}],"h":1},{"t":23,"s":[{"i":[[0.714,-0.536],[-0.089,-0.982],[-1.607,0.089],[0.357,2.143]],"o":[[-0.714,0.536],[0.089,0.982],[1.607,-0.089],[-0.357,-2.143]],"v":[[10.214,-35.125],[9.402,-32.08],[10.643,-28.714],[12.018,-32.92]]}],"h":1}]},"nm":"Path 5"},{"ind":5,"ty":"sh","closed":true,"ks":{"k":[{"t":21,"s":[{"i":[[0.625,-0.125],[-0.125,-2.375],[-1.125,0],[0.25,1.625]],"o":[[-0.625,0.125],[0.125,2.375],[1.125,0],[-0.25,-1.625]],"v":[[6.375,-58.875],[4.875,-55.5],[6.75,-51.5],[8.25,-55.75]]}],"h":1},{"t":23,"s":[{"i":[[0.344,-0.069],[-0.069,-1.306],[-0.619,0],[0.137,0.894]],"o":[[-0.344,0.069],[0.069,1.306],[0.619,0],[-0.137,-0.894]],"v":[[6.394,-53.681],[5.569,-51.825],[6.6,-49.625],[7.425,-51.963]]}],"h":1}]},"nm":"Path 6"},{"ind":6,"ty":"sh","closed":true,"ks":{"k":[{"t":21,"s":[{"i":[[1.48,-0.555],[-0.875,-3.625],[0,2.125]],"o":[[-1,0.375],[0.875,3.625],[0,-2.125]],"v":[[-25.25,-14.25],[-25.25,-7.625],[-22.25,-9.375]]}],"h":1},{"t":23,"s":[{"i":[[0.873,-0.328],[-0.516,-2.139],[0,1.254]],"o":[[-0.59,0.221],[0.516,2.139],[0,-1.254]],"v":[[-24.785,-6.172],[-24.785,-2.264],[-23.015,-3.296]]}],"h":1}]},"nm":"Path 7"},{"ty":"fl","fillEnabled":true,"c":{"k":[0,0.19,0.3,1]},"o":{"k":100},"nm":"Fill 1"},{"ty":"tr","p":{"k":[0,0],"ix":2},"a":{"k":[0,0],"ix":1},"s":{"k":[100,100],"ix":3},"r":{"k":0,"ix":6},"o":{"k":100,"ix":7},"sk":{"k":0,"ix":4},"sa":{"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1"}],"bounds":{"l":-26,"t":-89,"b":-5,"r":70},"ip":21,"op":25,"st":1,"bm":0,"sr":1},{"ddd":0,"ind":3,"ty":4,"nm":"goop_15","ks":{"o":{"k":100},"r":{"k":0},"p":{"k":[250,300,0]},"a":{"k":[0,0,0]},"s":{"k":[100,100,100]}},"shapes":[{"ind":0,"ty":"sh","closed":true,"ks":{"k":{"i":[[-1,1.125],[1.375,-0.938],[-1.75,0.5],[-1.812,1.125]],"o":[[1,-1.125],[-1.375,0.938],[1.75,-0.5],[1.812,-1.125]],"v":[[94.688,70.375],[90.75,71.625],[88.688,75.438],[92.75,73.938]]}},"nm":"Path 1"},{"ind":1,"ty":"sh","closed":true,"ks":{"k":{"i":[[2.438,0],[-1.625,0],[-2,0]],"o":[[-2.438,0],[1.625,0],[2,0]],"v":[[97.938,103],[95.375,104.562],[100.062,104.562]]}},"nm":"Path 2"},{"ty":"gr","it":[{"ty":"tr","p":{"k":[0,0],"ix":2},"a":{"k":[0,0],"ix":1},"s":{"k":[100,100],"ix":3},"r":{"k":0,"ix":6},"o":{"k":100,"ix":7},"sk":{"k":0,"ix":4},"sa":{"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1"},{"ty":"fl","fillEnabled":true,"c":{"k":[0,0.19,0.3,1]},"o":{"k":100},"nm":"Fill 1"}],"bounds":{"l":88,"t":69,"b":105,"r":101},"ip":18,"op":20,"st":1,"bm":0,"sr":1},{"ddd":0,"ind":4,"ty":4,"nm":"goop_14","ks":{"o":{"k":100},"r":{"k":0},"p":{"k":[250,300,0]},"a":{"k":[0,0,0]},"s":{"k":[100,100,100]}},"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","closed":true,"ks":{"k":{"i":[[0.719,-0.312],[-0.938,-0.5],[-0.062,0.969]],"o":[[-0.719,0.312],[0.629,0.336],[0.062,-0.969]],"v":[[-43.062,-7.906],[-43.094,-4.312],[-41.594,-6.156]]}},"nm":"Path 1"},{"ind":1,"ty":"sh","closed":true,"ks":{"k":{"i":[[0.75,-0.188],[-0.844,-0.438],[0.031,0.844]],"o":[[-0.75,0.188],[0.5,0.156],[-0.031,-0.844]],"v":[[-47.438,-33.469],[-47.375,-30.094],[-46.094,-31.531]]}},"nm":"Path 2"},{"ind":2,"ty":"sh","closed":true,"ks":{"k":{"i":[[0.656,-0.219],[-0.594,-0.188],[0.031,0.562]],"o":[[-0.656,0.219],[0.594,0.188],[-0.031,-0.562]],"v":[[-48.156,-38.562],[-48.312,-36.969],[-47.031,-37.781]]}},"nm":"Path 3"},{"ind":3,"ty":"sh","closed":true,"ks":{"k":{"i":[[-0.938,3.312],[0,0],[-1.062,0.562],[-1.375,0.562]],"o":[[0.938,-3.312],[0,0],[1.062,-0.562],[1.375,-0.562]],"v":[[-50.438,-73.5],[-54.125,-68.188],[-54.125,-60.188],[-51.25,-59.625]]}},"nm":"Path 4"},{"ty":"fl","fillEnabled":true,"c":{"k":[0,0.19,0.3,1]},"o":{"k":100},"nm":"Fill 1"},{"ty":"tr","p":{"k":[0,0],"ix":2},"a":{"k":[0,0],"ix":1},"s":{"k":[100,100],"ix":3},"r":{"k":0,"ix":6},"o":{"k":100,"ix":7},"sk":{"k":0,"ix":4},"sa":{"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1"}],"bounds":{"l":-55,"t":-75,"b":-4,"r":-41},"ip":18,"op":21,"st":1,"bm":0,"sr":1},{"ddd":0,"ind":5,"ty":4,"nm":"goop_13","ks":{"o":{"k":100},"r":{"k":0},"p":{"k":[250,300,0]},"a":{"k":[0,0,0]},"s":{"k":[100,100,100]}},"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","closed":true,"ks":{"k":{"i":[[-0.25,1.75],[0,-5],[-2,4.75],[-2.5,-0.75],[-0.75,-3.25],[-2.375,2.625],[0.25,2.75],[3.75,1.375],[0.75,0.125]],"o":[[0.25,-1.75],[0,5],[2,-4.75],[2.5,0.75],[0.75,3.25],[2.375,-2.625],[-0.25,-2.75],[-3.75,-1.375],[-0.75,-0.125]],"v":[[53.25,-146.375],[44.25,-142],[47.375,-124.875],[54.375,-132.625],[57.125,-126.625],[62.875,-125],[60.5,-134.625],[58.75,-143.375],[53.5,-142]]}},"nm":"Path 1"},{"ind":1,"ty":"sh","closed":true,"ks":{"k":{"i":[[1.75,0.125],[0.125,-2.5],[-1.625,-1.375],[0.375,3.125]],"o":[[-1.75,-0.125],[-0.05,1.007],[1.625,1.375],[-0.375,-3.125]],"v":[[55.625,-91.25],[50.5,-89.375],[54.75,-85.125],[60.625,-86.5]]}},"nm":"Path 2"},{"ind":2,"ty":"sh","closed":true,"ks":{"k":{"i":[[1.455,0.364],[0,-2],[-1.375,-0.375],[-0.125,1.375]],"o":[[-0.875,-0.25],[0,2],[1.375,0.375],[0.125,-1.375]],"v":[[65.625,-45.25],[64.375,-41.375],[65.75,-36],[67.875,-40.375]]}},"nm":"Path 3"},{"ind":3,"ty":"sh","closed":true,"ks":{"k":{"i":[[2.75,2.25],[1.5,-1.5],[1.625,0.75],[0,0],[0,0],[2.75,5.125]],"o":[[-2.75,-2.25],[-1.5,1.5],[-1.625,-0.75],[0,0],[0,0],[-2.75,-5.125]],"v":[[76.875,30.75],[70.375,31.25],[66.625,32.125],[72.125,43.875],[89.25,52.875],[84.25,45.75]]}},"nm":"Path 4"},{"ty":"fl","fillEnabled":true,"c":{"k":[0,0.19,0.3,1]},"o":{"k":100},"nm":"Fill 1"},{"ty":"tr","p":{"k":[0,0],"ix":2},"a":{"k":[0,0],"ix":1},"s":{"k":[100,100],"ix":3},"r":{"k":0,"ix":6},"o":{"k":100,"ix":7},"sk":{"k":0,"ix":4},"sa":{"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1"}],"bounds":{"l":44,"t":-148,"b":53,"r":90},"ip":18,"op":21,"st":1,"bm":0,"sr":1},{"ddd":0,"ind":6,"ty":4,"nm":"goop_12","ks":{"o":{"k":100},"r":{"k":0},"p":{"k":[250,300,0]},"a":{"k":[0,0,0]},"s":{"k":[100,100,100]}},"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","closed":true,"ks":{"k":{"i":[[-1.875,2],[0,0],[-3.25,0.375],[-0.812,-1.312],[-2.125,1.062],[1.188,2.312],[-1.125,0.562],[-1.312,1]],"o":[[1.875,-2],[0,0],[3.25,-0.375],[0.812,1.312],[2.125,-1.062],[-1.188,-2.312],[1.125,-0.562],[1.312,-1]],"v":[[94.125,73.875],[86.688,74.5],[79.875,84.188],[85.875,86.688],[89.875,90],[88.5,84.812],[87.25,79.375],[92.688,79.625]]}},"nm":"Path 1"},{"ind":1,"ty":"sh","closed":true,"ks":{"k":{"i":[[4.062,0],[-2.25,0],[-2.312,0]],"o":[[-4.062,0],[2.25,0],[2.312,0]],"v":[[97.625,101.938],[93.312,104.562],[101.5,104.562]]}},"nm":"Path 2"},{"ind":2,"ty":"sh","closed":true,"ks":{"k":{"i":[[3.125,-0.75],[-2.375,0.062],[-0.625,-1.125],[0.688,1.812],[-1.625,1]],"o":[[-3.125,0.75],[2.375,-0.062],[0.625,1.125],[-0.688,-1.812],[1.625,-1]],"v":[[70.5,86.312],[68.562,90.688],[73.625,92.5],[74.375,91.312],[76.062,86.438]]}},"nm":"Path 3"},{"ind":3,"ty":"sh","closed":true,"ks":{"k":{"i":[[2.75,0],[-1.688,0],[-1.438,0]],"o":[[-2.75,0],[1.688,0],[1.438,0]],"v":[[79.375,102.25],[76.75,104.625],[81.625,104.625]]}},"nm":"Path 4"},{"ind":4,"ty":"sh","closed":true,"ks":{"k":{"i":[[0,0],[-2,-0.5],[-0.75,-2.812],[0.562,0],[1,0],[0.625,2.75],[-1.125,0.938]],"o":[[0,0],[2,0.5],[0.75,2.812],[-0.562,0],[-1,0],[-0.625,-2.75],[1.125,-0.938]],"v":[[59.688,92.25],[59.188,94.938],[63.125,99.875],[62.812,104.812],[65.625,104.75],[63.25,98.562],[63.438,92.812]]}},"nm":"Path 5"},{"ind":5,"ty":"sh","closed":true,"ks":{"k":{"i":[[1,0],[0.25,-0.625],[-3.625,0],[0.438,0.812]],"o":[[-1,0],[-0.25,0.625],[3.625,0],[-0.438,-0.812]],"v":[[-29.688,102.688],[-32.062,103.75],[-29.75,104.75],[-27.438,103.688]]}},"nm":"Path 6"},{"ind":6,"ty":"sh","closed":true,"ks":{"k":{"i":[[0,0],[-3.562,-4],[-3.625,-0.312],[-3.625,-0.688]],"o":[[0,0],[3.562,4],[3.625,0.312],[3.625,0.688]],"v":[[-36.125,82.125],[-42.938,83.812],[-30.125,95.812],[-22.125,95]]}},"nm":"Path 7"},{"ty":"fl","fillEnabled":true,"c":{"k":[0,0.19,0.3,1]},"o":{"k":100},"nm":"Fill 1"},{"ty":"tr","p":{"k":[0,0],"ix":2},"a":{"k":[0,0],"ix":1},"s":{"k":[100,100],"ix":3},"r":{"k":0,"ix":6},"o":{"k":100,"ix":7},"sk":{"k":0,"ix":4},"sa":{"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1"}],"bounds":{"l":-44,"t":73,"b":105,"r":103},"ip":16,"op":18,"st":1,"bm":0,"sr":1},{"ddd":0,"ind":7,"ty":4,"nm":"goop_11","ks":{"o":{"k":100},"r":{"k":0},"p":{"k":[250,300,0]},"a":{"k":[0,0,0]},"s":{"k":[100,100,100]}},"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","closed":true,"ks":{"k":{"i":[[-1.312,3.75],[0,0],[-1.25,1.438],[-0.062,-2.375],[-0.625,0.375],[0.188,2]],"o":[[1.312,-3.75],[0,0],[1.25,-1.438],[0.062,2.375],[0.625,-0.375],[-0.188,-2]],"v":[[-50,-69.938],[-56.188,-64.062],[-53.312,-56.688],[-51.438,-54],[-49.625,-51.438],[-50.125,-59.75]]}},"nm":"Path 1"},{"ind":1,"ty":"sh","closed":true,"ks":{"k":{"i":[[1.125,-0.438],[-0.312,-2.25],[-0.875,-0.875],[0.125,2.188]],"o":[[-1.125,0.438],[0.312,2.25],[0.875,0.875],[-0.125,-2.188]],"v":[[-49,-42.062],[-48.875,-37.5],[-47.875,-31.812],[-46.375,-37.688]]}},"nm":"Path 2"},{"ind":2,"ty":"sh","closed":true,"ks":{"k":{"i":[[1,-0.25],[-0.25,-1.562],[-0.562,0],[0.062,1.75]],"o":[[-1,0.25],[0.25,1.562],[0.562,0],[-0.062,-1.75]],"v":[[-43.938,-13.375],[-43.75,-10.125],[-42.75,-6.812],[-41.812,-10]]}},"nm":"Path 3"},{"ind":3,"ty":"sh","closed":true,"ks":{"k":{"i":[[0.5,0],[0,-0.562],[-0.375,-0.062],[0.062,0.75]],"o":[[-0.5,0],[0,0.562],[0.375,0.062],[-0.062,-0.75]],"v":[[-41.438,2.375],[-42,3.75],[-41.375,5.25],[-40.625,3.75]]}},"nm":"Path 4"},{"ind":4,"ty":"sh","closed":true,"ks":{"k":{"i":[[0.75,1.125],[-1.875,-1.625],[1.875,4.5],[3.75,2.375]],"o":[[-0.75,-1.125],[1.875,1.625],[-1.875,-4.5],[-3.75,-2.375]],"v":[[-42.375,18.375],[-39.25,30.625],[-31.75,33.375],[-37.75,19.25]]}},"nm":"Path 5"},{"ty":"fl","fillEnabled":true,"c":{"k":[0,0.19,0.3,1]},"o":{"k":100},"nm":"Fill 1"},{"ty":"tr","p":{"k":[0,0],"ix":2},"a":{"k":[0,0],"ix":1},"s":{"k":[100,100],"ix":3},"r":{"k":0,"ix":6},"o":{"k":100,"ix":7},"sk":{"k":0,"ix":4},"sa":{"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1"}],"bounds":{"l":-57,"t":-71,"b":36,"r":-31},"ip":16,"op":18,"st":1,"bm":0,"sr":1},{"ddd":0,"ind":8,"ty":4,"nm":"goop_10","ks":{"o":{"k":100},"r":{"k":0},"p":{"k":[250,300,0]},"a":{"k":[0,0,0]},"s":{"k":[100,100,100]}},"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","closed":true,"ks":{"k":{"i":[[-2.5,5.75],[0,0],[-1.5,1.625],[-1.5,-4.25],[-0.125,-3],[-0.874,0.049],[0.875,11.75]],"o":[[2.5,-5.75],[0,0],[1.5,-1.625],[1.5,4.25],[0.125,3],[2.25,-0.125],[-0.875,-11.75]],"v":[[54,-135],[47.125,-132.875],[45,-115.25],[50.875,-112.625],[50.875,-98.5],[53.75,-94.5],[55.75,-113.125]]}},"nm":"Path 1"},{"ind":1,"ty":"sh","closed":true,"ks":{"k":{"i":[[0.75,-1.625],[-0.75,-0.125],[0,1]],"o":[[-0.524,1.135],[0.75,0.125],[0,-1]],"v":[[56.625,-74.375],[58.375,-72],[60,-73.875]]}},"nm":"Path 2"},{"ind":2,"ty":"sh","closed":true,"ks":{"k":{"i":[[2.386,-0.716],[-0.5,-4.5],[-1,0],[0.625,4.625]],"o":[[-1.25,0.375],[0.5,4.5],[1,0],[-0.625,-4.625]],"v":[[62.625,-55],[62.625,-46.5],[66.125,-37.875],[67,-46.25]]}},"nm":"Path 3"},{"ind":3,"ty":"sh","closed":true,"ks":{"k":{"i":[[2,1.75],[0.5,-6.5],[0.75,-5.25],[0,0],[0,0],[3.5,4.75],[1.75,8],[0.5,7.25]],"o":[[-2,-1.75],[-0.5,6.5],[-0.75,5.25],[0,0],[0,0],[-3.5,-4.75],[-1.75,-8],[-0.5,-7.25]],"v":[[74.25,-7],[69.5,7.25],[73.5,30.75],[67.25,34.25],[71.5,45.75],[82,45.75],[78.25,28.5],[71.75,9]]}},"nm":"Path 4"},{"ty":"fl","fillEnabled":true,"c":{"k":[0,0.19,0.3,1]},"o":{"k":100},"nm":"Fill 1"},{"ty":"tr","p":{"k":[0,0],"ix":2},"a":{"k":[0,0],"ix":1},"s":{"k":[100,100],"ix":3},"r":{"k":0,"ix":6},"o":{"k":100,"ix":7},"sk":{"k":0,"ix":4},"sa":{"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1"}],"bounds":{"l":44,"t":-138,"b":48,"r":83},"ip":16,"op":18,"st":1,"bm":0,"sr":1},{"ddd":0,"ind":9,"ty":4,"nm":"goop_09","ks":{"o":{"k":100},"r":{"k":0},"p":{"k":[250,300,0]},"a":{"k":[0,0,0]},"s":{"k":[100,100,100]}},"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","closed":true,"ks":{"k":{"i":[[-2.688,3.062],[3.188,-1.25],[0,0],[-3.125,-7.812],[0,0],[0,0],[3.25,8.312]],"o":[[2.688,-3.062],[-3.188,1.25],[0,0],[3.125,7.812],[0,0],[0,0],[-1.923,-4.919]],"v":[[96,76.938],[90,77.562],[86.312,83],[95.688,93.25],[99.062,104.375],[104.812,104.375],[96.812,93.125]]}},"nm":"Path 1"},{"ind":1,"ty":"sh","closed":true,"ks":{"k":{"i":[[-0.062,-0.188],[-3.375,0.125],[-1.541,-3.918],[0,0],[0,0],[1.5,5.188],[-1.688,1.938]],"o":[[0.062,0.188],[3.375,-0.125],[3,7.625],[0,0],[0,0],[-1.5,-5.188],[1.688,-1.938]],"v":[[86.188,79.562],[83,85.75],[91.875,94.938],[91.188,104.5],[99.625,104.5],[92.562,93.938],[91.75,80.25]]}},"nm":"Path 2"},{"ind":2,"ty":"sh","closed":true,"ks":{"k":{"i":[[-1.438,0.562],[0,0],[-1.509,0.424],[-0.375,-2],[0,0],[0,0],[0.438,3]],"o":[[1.438,-0.562],[0,0],[2,-0.562],[0.375,2],[0,0],[0,0],[-0.438,-3]],"v":[[65.375,93.438],[61.062,93.438],[60.062,95.562],[63.562,99.938],[64,104.562],[66.75,104.562],[64.062,99.812]]}},"nm":"Path 3"},{"ind":3,"ty":"sh","closed":true,"ks":{"k":{"i":[[-1,0.562],[-3.938,0.562],[-1.875,-4.375],[0,0],[0,0],[2.188,5.125]],"o":[[1,-0.562],[1.517,-0.217],[1.875,4.375],[0,0],[0,0],[-2.188,-5.125]],"v":[[76.75,88.5],[69.812,91.875],[76.25,97.562],[78,104.625],[82.188,104.625],[77.188,97.062]]}},"nm":"Path 4"},{"ind":4,"ty":"sh","closed":true,"ks":{"k":{"i":[[0,0],[-3,-4.188],[-0.375,-3.938],[0,0],[0,0],[0.25,3.688],[-2.188,-0.375]],"o":[[0,0],[3,4.188],[0.375,3.938],[0,0],[0,0],[-0.25,-3.688],[2.188,0.375]],"v":[[-29.312,85.25],[-39,87.25],[-31.062,99.188],[-32.875,104.625],[-25.812,104.625],[-28.938,98.562],[-25.938,93.812]]}},"nm":"Path 5"},{"ty":"fl","fillEnabled":true,"c":{"k":[0,0.19,0.3,1]},"o":{"k":100},"nm":"Fill 1"},{"ty":"tr","p":{"k":[0,0],"ix":2},"a":{"k":[0,0],"ix":1},"s":{"k":[100,100],"ix":3},"r":{"k":0,"ix":6},"o":{"k":100,"ix":7},"sk":{"k":0,"ix":4},"sa":{"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1"}],"bounds":{"l":-40,"t":75,"b":105,"r":105},"ip":14,"op":16,"st":1,"bm":0,"sr":1},{"ddd":0,"ind":10,"ty":4,"nm":"goop_08","ks":{"o":{"k":100},"r":{"k":0},"p":{"k":[250,300,0]},"a":{"k":[0,0,0]},"s":{"k":[100,100,100]}},"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","closed":true,"ks":{"k":{"i":[[3.25,-0.562],[-2.562,-2],[-0.688,-5.812],[0,0],[0,0],[1.125,4.5],[-2.75,2.938]],"o":[[-3.25,0.562],[2.562,2],[0.688,5.812],[0,0],[0,0],[-1.125,-4.5],[2.75,-2.938]],"v":[[99.312,82.375],[95.188,88.625],[100.812,97.688],[100.875,104.438],[108.25,104.438],[102.25,96.375],[105.25,83.375]]}},"nm":"Path 1"},{"ind":1,"ty":"sh","closed":true,"ks":{"k":{"i":[[0,0],[-3.75,0.062],[-0.562,-3.375],[0,0],[0,0],[0.75,5.5],[-2.375,2.125]],"o":[[0,0],[3.75,-0.062],[0.562,3.375],[0,0],[0,0],[-0.75,-5.5],[2.375,-2.125]],"v":[[95,85.062],[83.688,93.375],[92.688,99.25],[90.062,104.5],[101.125,104.5],[97.438,96.938],[100.875,85.938]]}},"nm":"Path 2"},{"ind":2,"ty":"sh","closed":true,"ks":{"k":{"i":[[0,0],[-3.125,-0.375],[-0.188,-2.25],[0,0],[0,0],[0.438,3.062],[-1.875,0.812]],"o":[[0,0],[3.125,0.375],[0.188,2.25],[0,0],[0,0],[-0.438,-3.062],[1.875,-0.812]],"v":[[81.25,89.562],[74.625,96.25],[78.312,101.062],[77,104.625],[83.125,104.625],[80.312,100.312],[82.938,93.5]]}},"nm":"Path 3"},{"ind":3,"ty":"sh","closed":true,"ks":{"k":{"i":[[0.25,-0.125],[-1.125,-0.188],[-0.062,-1.25],[0,0],[0,0],[-0.062,1.938],[-0.562,0.188]],"o":[[-0.25,0.125],[1.125,0.188],[0.062,1.25],[0,0],[0,0],[0.062,-1.938],[0.562,-0.188]],"v":[[66.688,96.75],[65.188,98.125],[66.875,101.062],[66.688,104.375],[68.375,104.375],[67.625,101],[68.75,97.688]]}},"nm":"Path 4"},{"ind":4,"ty":"sh","closed":true,"ks":{"k":{"i":[[0,0],[-1.812,-0.438],[0,-1],[0,0],[0,0],[0,1.812],[-1.188,0.438]],"o":[[0,0],[1.812,0.438],[0,1],[0,0],[0,0],[0,-1.812],[1.188,-0.438]],"v":[[63.375,96.812],[60.25,99.625],[62.688,102.062],[61.812,104.438],[65.875,104.438],[64.375,101.438],[66.125,98.188]]}},"nm":"Path 5"},{"ind":5,"ty":"sh","closed":true,"ks":{"k":{"i":[[-4.082,-4.26],[-1,-4.5],[0,0],[0,0],[-0.125,2.875],[-2.25,-0.625],[0,0]],"o":[[5.75,6],[1,4.5],[0,0],[0,0],[0.125,-2.875],[2.25,0.625],[0,0]],"v":[[-47.75,81.75],[-34.25,96.75],[-34.875,104.25],[-23.75,104.25],[-26.375,99.5],[-21.25,96.125],[-27.375,85.875]]}},"nm":"Path 6"},{"ty":"fl","fillEnabled":true,"c":{"k":[0,0.19,0.3,1]},"o":{"k":100},"nm":"Fill 1"},{"ty":"tr","p":{"k":[0,0],"ix":2},"a":{"k":[0,0],"ix":1},"s":{"k":[100,100],"ix":3},"r":{"k":0,"ix":6},"o":{"k":100,"ix":7},"sk":{"k":0,"ix":4},"sa":{"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1"}],"bounds":{"l":-49,"t":80,"b":105,"r":109},"ip":12,"op":14,"st":1,"bm":0,"sr":1},{"ddd":0,"ind":11,"ty":4,"nm":"goop_07","ks":{"o":{"k":100},"r":{"k":0},"p":{"k":[250,300,0]},"a":{"k":[0,0,0]},"s":{"k":[100,100,100]}},"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","closed":true,"ks":{"k":[{"t":12,"s":[{"i":[[-1,4.125],[1.375,-2.125],[-0.75,2.125],[-3.875,-21.125],[2.625,4.375],[0,0],[6.75,9.625],[3.125,19.375]],"o":[[1,-4.125],[-1.375,2.125],[0.75,-2.125],[3.875,21.125],[-2.625,-4.375],[0,0],[-6.75,-9.625],[-3.125,-19.375]],"v":[[-48.875,-64],[-53.375,-60.625],[-52.5,-50.5],[-46.5,-26.875],[-43.25,7.5],[-41.75,21.25],[-29,30.875],[-45.125,-26.25]]}],"h":1},{"t":14,"s":[{"i":[[-1,4.125],[1.375,-2.125],[-0.75,2.125],[-3.257,-21.229],[2.625,4.375],[0,0],[9.849,6.419],[3.125,19.375]],"o":[[1,-4.125],[-1.375,2.125],[0.75,-2.125],[3.375,22],[-2.625,-4.375],[0,0],[-11.125,-7.25],[-3.125,-19.375]],"v":[[-50.375,-64.25],[-53.375,-60.625],[-52.125,-53],[-45.125,-22.5],[-40,20.125],[-38.125,37.625],[-27,36.625],[-44.375,-22.625]]}],"h":1}]},"nm":"Path 1"},{"ty":"fl","fillEnabled":true,"c":{"k":[0,0.19,0.3,1]},"o":{"k":100},"nm":"Fill 1"},{"ty":"tr","p":{"k":[0,0],"ix":2},"a":{"k":[0,0],"ix":1},"s":{"k":[100,100],"ix":3},"r":{"k":0,"ix":6},"o":{"k":100,"ix":7},"sk":{"k":0,"ix":4},"sa":{"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1"}],"bounds":{"l":-54,"t":-66,"b":34,"r":-27},"ip":12,"op":16,"st":1,"bm":0,"sr":1},{"ddd":0,"ind":12,"ty":4,"nm":"goop_06","ks":{"o":{"k":100},"r":{"k":0},"p":{"k":[250,300,0]},"a":{"k":[0,0,0]},"s":{"k":[100,100,100]}},"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","closed":true,"ks":{"k":[{"t":12,"s":[{"i":[[1.125,-1],[-3.25,0.25],[-0.875,-9],[-5,0.875],[2,12.75],[-1.75,0.5]],"o":[[-1.125,1],[3.25,-0.25],[0.875,9],[5,-0.875],[-2,-12.75],[1.75,-0.5]],"v":[[73.125,-111.125],[70,-105.25],[76.75,-89.5],[81.25,-63.75],[78.875,-90.875],[80,-110]]}],"h":1},{"t":14,"s":[{"i":[[1.171,-0.295],[-2.153,1.61],[-0.201,-1.34],[-5.009,-0.246],[-0.336,3.735],[-1.247,1.676]],"o":[[-1.171,0.295],[2.198,-1.643],[0.267,1.777],[5.009,0.246],[0.293,-3.254],[1.247,-1.676]],"v":[[61.452,-136.777],[60.778,-129.11],[63.233,-128.777],[66.531,-120.371],[66.167,-133.36],[69.461,-140.05]]}],"h":1}]},"nm":"Path 1"},{"ty":"fl","fillEnabled":true,"c":{"k":[0,0.19,0.3,1]},"o":{"k":100},"nm":"Fill 1"},{"ty":"tr","p":{"k":[0,0],"ix":2},"a":{"k":[0,0],"ix":1},"s":{"k":[100,100],"ix":3},"r":{"k":0,"ix":6},"o":{"k":100,"ix":7},"sk":{"k":0,"ix":4},"sa":{"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1"}],"bounds":{"l":68,"t":-112,"b":-63,"r":84},"ip":12,"op":16,"st":1,"bm":0,"sr":1},{"ddd":0,"ind":13,"ty":4,"nm":"goop_05","ks":{"o":{"k":100},"r":{"k":0},"p":{"k":[250,300,0]},"a":{"k":[0,0,0]},"s":{"k":[100,100,100]}},"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","closed":true,"ks":{"k":{"i":[[0.125,-0.875],[-0.25,-2.125],[1.125,-2.25],[0.75,-3.125],[0,0],[0,0],[1.125,2.25],[0,2.625],[-0.25,4.125],[0.625,1.5]],"o":[[-0.125,0.875],[0.25,2.125],[-1.125,2.25],[-0.75,3.125],[0,0],[0,0],[-1.125,-2.25],[0,-2.625],[0.25,-4.125],[-0.625,-1.5]],"v":[[113.625,71.75],[115.75,76.5],[114.25,87.75],[112.5,100],[111.125,104.875],[119.375,104.875],[118,100.25],[115.75,92.5],[118,84],[117.125,73.125]]}},"nm":"Path 1"},{"ind":1,"ty":"sh","closed":true,"ks":{"k":{"i":[[3,-1.375],[-2.75,-0.5],[0.125,-4.875],[-0.25,-5.25],[-0.625,-1.625],[-1.625,0.625],[1.75,1.75],[-1.375,4],[1,7.875]],"o":[[-3,1.375],[2.75,0.5],[-0.125,4.875],[0.25,5.25],[0.625,1.625],[1.625,-0.625],[-1.75,-1.75],[1.375,-4],[-1,-7.875]],"v":[[111.25,12.875],[107.25,17.375],[111.5,25.125],[108,43.625],[111.875,51],[114.625,54.375],[113.375,47.875],[111.375,40.75],[115.75,16.875]]}},"nm":"Path 2"},{"ind":2,"ty":"sh","closed":true,"ks":{"k":{"i":[[4,-0.25],[-2.625,0],[-2.123,-34.473],[3.375,0],[-2.75,-0.75],[2.375,1],[2.375,41.5],[-2.5,0]],"o":[[-4,0.25],[2.625,0],[2.125,34.5],[-3.375,0],[2.75,0.75],[-2.375,-1],[-2.375,-41.5],[2.5,0]],"v":[[96,13.875],[90.25,18],[97.5,59],[96.375,99.75],[100.5,103.25],[109.375,104.125],[98.875,59],[104,16.875]]}},"nm":"Path 3"},{"ty":"fl","fillEnabled":true,"c":{"k":[0,0.19,0.3,1]},"o":{"k":100},"nm":"Fill 1"},{"ty":"tr","p":{"k":[0,0],"ix":2},"a":{"k":[0,0],"ix":1},"s":{"k":[100,100],"ix":3},"r":{"k":0,"ix":6},"o":{"k":100,"ix":7},"sk":{"k":0,"ix":4},"sa":{"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1"}],"bounds":{"l":89,"t":11,"b":105,"r":120},"ip":10,"op":12,"st":1,"bm":0,"sr":1},{"ddd":0,"ind":14,"ty":4,"nm":"goop_04","ks":{"o":{"k":100},"r":{"k":0},"p":{"k":[250,300,0]},"a":{"k":[0,0,0]},"s":{"k":[100,100,100]}},"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","closed":true,"ks":{"k":[{"t":10,"s":[{"i":[[5.125,0.25],[-1.375,-0.375],[-4.625,-33.375],[7.75,-0.875],[0,0],[3,1],[1.375,23.375],[-6.375,1.25]],"o":[[-5.125,-0.25],[1.375,0.375],[4.61,33.267],[-7.75,0.875],[0,0],[-11.972,-3.991],[-1.375,-23.375],[6.375,-1.25]],"v":[[81.5,10.875],[69,17.125],[80.75,56.75],[70.625,99.125],[88.625,103.125],[102.25,103],[87.125,59.75],[92.375,17.25]]}],"h":1},{"t":12,"s":[{"i":[[5.125,0.447],[-1.444,0.506],[-10.156,-58.957],[12.125,-9.788],[0,0],[1.868,2.95],[7.875,41.831],[-6.375,2.234]],"o":[[-5.125,-0.447],[9.25,-3.241],[8,46.443],[-6.152,4.966],[0,0],[-9,-14.213],[-7.733,-41.076],[6.375,-2.234]],"v":[[54.5,-109.678],[46,-93.759],[68.5,-25.193],[62.375,51.788],[81.375,59.436],[96.25,62.463],[72.375,-25.331],[70.375,-105.035]]}],"h":1},{"t":14,"s":[{"i":[[5.125,0.447],[-1.528,0.059],[-10.156,-58.957],[3.875,-4.538],[0,0],[2.383,2.552],[5.875,39.581],[-8.375,9.535]],"o":[[-5.125,-0.447],[6.25,-0.241],[8,46.443],[-5.134,6.012],[0,0],[-3,-3.213],[-6.137,-41.344],[4.458,-5.075]],"v":[[42.5,-129.678],[42,-108.009],[62.25,-37.443],[68.625,41.538],[81.375,59.436],[83.25,47.963],[64.875,-38.331],[58.625,-127.285]]}],"h":1}]},"nm":"Path 1"},{"ty":"fl","fillEnabled":true,"c":{"k":[0,0.19,0.3,1]},"o":{"k":100},"nm":"Fill 1"},{"ty":"tr","p":{"k":[0,0],"ix":2},"a":{"k":[0,0],"ix":1},"s":{"k":[100,100],"ix":3},"r":{"k":0,"ix":6},"o":{"k":100,"ix":7},"sk":{"k":0,"ix":4},"sa":{"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1"}],"bounds":{"l":45,"t":-110,"b":104,"r":103},"ip":10,"op":16,"st":1,"bm":0,"sr":1},{"ddd":0,"ind":15,"ty":4,"nm":"goop_03","ks":{"o":{"k":100},"r":{"k":0},"p":{"k":[250,300,0]},"a":{"k":[0,0,0]},"s":{"k":[100,100,100]}},"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","closed":true,"ks":{"k":[{"t":10,"s":[{"i":[[6.75,-2.5],[-3,4.75],[-3,-40.5],[4.875,-0.125],[-4.5,0.25],[6.766,0.199],[0.75,40.5],[-4.25,0.5]],"o":[[-6.75,2.5],[3,-4.75],[3,40.5],[-3.134,0.08],[4.5,-0.25],[-8.5,-0.25],[-0.75,-40.5],[4.25,-0.5]],"v":[[3.25,-18.75],[-0.25,7],[7.5,50.5],[3.375,99.375],[13.75,103.25],[22.5,99.75],[10,38.5],[12.75,-15.75]]}],"h":1},{"t":12,"s":[{"i":[[6.588,-2.899],[-4.064,3.879],[-5.149,-40.969],[5.125,2.411],[-4.477,0.519],[5.698,3.653],[10.402,59.073],[-11.849,0.39]],"o":[[-6.588,2.899],[3.767,-3.596],[5.68,45.195],[-2.837,-1.334],[4.477,-0.519],[-6.738,-4.32],[-7.025,-39.893],[4.277,-0.141]],"v":[[-37.813,-105.067],[-46.517,-74.404],[-28.93,-21.195],[-29.625,30.839],[-15.537,59.837],[-2.262,56.07],[-21.902,-21.573],[-18.651,-96.14]]}],"h":1},{"t":14,"s":[{"i":[[6.588,-2.899],[-4.064,3.879],[-5.149,-40.969],[7.875,3.161],[-4.477,0.519],[5.698,3.653],[10.402,59.073],[-16.099,-0.36]],"o":[[-6.588,2.899],[3.767,-3.596],[5.68,45.195],[-2.909,-1.168],[4.477,-0.519],[-6.738,-4.32],[-7.025,-39.893],[4.278,0.096]],"v":[[-37.813,-105.067],[-45.767,-76.654],[-29.18,-23.445],[-29.625,35.839],[-15.537,59.837],[-3.012,56.57],[-23.402,-23.573],[-18.401,-103.39]]}],"h":1},{"t":16,"s":[{"i":[[6.588,-2.899],[-4.064,3.879],[-5.149,-40.969],[5.375,3.161],[-4.477,0.519],[5.698,3.653],[7.132,61.45],[-12.349,5.89]],"o":[[-6.588,2.899],[3.767,-3.596],[5.68,45.195],[-2.702,-1.589],[4.477,-0.519],[-6.738,-4.32],[-5.098,-43.927],[3.862,-1.842]],"v":[[-37.813,-105.067],[-47.267,-78.154],[-30.18,-28.695],[-26.875,40.089],[-15.537,59.837],[-6.012,54.57],[-25.902,-27.573],[-22.901,-106.89]]}],"h":1},{"t":18,"s":[{"i":[[6.588,-2.899],[-4.563,3.277],[-5.149,-40.969],[5.375,3.161],[-4.477,0.519],[6.006,3.121],[7.132,61.45],[-8.849,5.89]],"o":[[-6.588,2.899],[3.267,-2.346],[5.68,45.195],[-2.702,-1.589],[4.477,-0.519],[-10.238,-5.32],[-5.098,-43.927],[3.562,-2.371]],"v":[[-37.813,-105.067],[-45.517,-84.154],[-29.68,-28.195],[-26.375,39.839],[-15.537,59.837],[-7.012,53.07],[-28.402,-28.323],[-27.401,-105.39]]}],"h":1}]},"nm":"Path 1"},{"ty":"fl","fillEnabled":true,"c":{"k":[0,0.19,0.3,1]},"o":{"k":100},"nm":"Fill 1"},{"ty":"tr","p":{"k":[0,0],"ix":2},"a":{"k":[0,0],"ix":1},"s":{"k":[100,100],"ix":3},"r":{"k":0,"ix":6},"o":{"k":100,"ix":7},"sk":{"k":0,"ix":4},"sa":{"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1"}],"bounds":{"l":-49,"t":-108,"b":104,"r":25},"ip":10,"op":21,"st":1,"bm":0,"sr":1},{"ddd":0,"ind":16,"ty":4,"nm":"goop_02","ks":{"o":{"k":100},"r":{"k":0},"p":{"k":[250,300,0]},"a":{"k":[0,0,0]},"s":{"k":[100,100,100]}},"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","closed":true,"ks":{"k":[{"t":10,"s":[{"i":[[24.5,-1.25],[-5.393,2.825],[-3.002,-48.722],[6.75,-0.25],[-17.258,1.726],[5.75,2.5],[2.103,47.519],[-5.5,0]],"o":[[-24.5,1.25],[5.25,-2.75],[3.25,52.75],[-10.543,0.39],[5,-0.5],[-3.076,-1.337],[-2.5,-56.5],[-0.25,0.5]],"v":[[28,-27.5],[7.5,-16.75],[26.75,35.25],[20.25,100.25],[42,102.25],[58.25,99.5],[45.5,45.75],[56.25,-7]]}],"h":1},{"t":12,"s":[{"i":[[24.5,-1.25],[-5.75,2],[-11.25,-57.5],[10.25,-2.5],[-16.75,4.5],[13.75,-1.75],[7.001,61.759],[-5.5,0]],"o":[[-24.5,1.25],[5.75,-2],[11.25,57.5],[-10.25,2.5],[16.75,-4.5],[-13.75,1.75],[-7,-61.75],[-0.25,0.5]],"v":[[9.5,-109.5],[-22.75,-96.75],[5.25,-33],[10.75,60.5],[33.5,72.5],[58.5,54.25],[29.5,-18],[38.25,-90.75]]}],"h":1},{"t":14,"s":[{"i":[[24.5,-1.25],[-5.75,2],[-11.25,-57.5],[10.25,-2.5],[-16.75,4.5],[13.844,-0.675],[10.25,64],[-6.5,-0.25]],"o":[[-24.5,1.25],[5.75,-2],[11.25,57.5],[-10.25,2.5],[16.75,-4.5],[-10.25,0.5],[-9.828,-61.364],[-0.25,0.5]],"v":[[-0.25,-124],[-23.5,-103.75],[4.75,-43.5],[11,60.75],[33.5,72.5],[50.5,53.25],[23.75,-31.5],[26.75,-112.75]]}],"h":1},{"t":16,"s":[{"i":[[24.5,-1.25],[-5.75,2],[-9.593,-57.799],[9.5,-0.25],[-16.75,4.5],[13.624,-2.554],[10.25,64],[-6.5,-0.25]],"o":[[-24.5,1.25],[5.75,-2],[10,60.25],[-10.547,0.278],[16.75,-4.5],[-8,1.5],[-9.828,-61.364],[-0.25,0.5]],"v":[[-0.25,-124],[-23.25,-107.5],[5.25,-43.75],[12.75,60],[33.5,72.5],[46,52],[21.75,-31.5],[17,-120.25]]}],"h":1},{"t":18,"s":[{"i":[[24.5,-1.25],[-5.75,2],[-10.25,-55],[9.5,-0.25],[-16.75,4.5],[13.624,-2.554],[10.25,64],[-6.5,-0.25]],"o":[[-24.5,1.25],[5.75,-2],[11.189,60.04],[-10.547,0.278],[16.75,-4.5],[-8,1.5],[-9.828,-61.364],[-0.25,0.5]],"v":[[-0.5,-129.25],[-20.75,-110.5],[6,-42.75],[13.75,59.25],[33.5,72.5],[43.75,51.75],[21,-33.5],[17.75,-123.75]]}],"h":1}]},"nm":"Path 1"},{"ty":"tr","p":{"k":[0,0],"ix":2},"a":{"k":[0,0],"ix":1},"s":{"k":[100,100],"ix":3},"r":{"k":0,"ix":6},"o":{"k":100,"ix":7},"sk":{"k":0,"ix":4},"sa":{"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1"},{"ty":"gr","it":[{"ind":0,"ty":"sh","closed":true,"ks":{"k":[{"t":10,"s":[{"i":[[2.063,0.026],[0.19,-7.45],[-1.891,-0.169],[-0.06,7.78]],"o":[[-2.063,-0.026],[-0.19,7.45],[1.891,0.169],[0.06,-7.78]],"v":[[37.358,-12.819],[34.741,0.451],[37.902,16.622],[40.201,1.575]]}],"h":1},{"t":12,"s":[{"i":[[3.25,-0.25],[-0.75,-11.75],[-3,0],[1,12.25]],"o":[[-3.25,0.25],[0.75,11.75],[3,0],[-1,-12.25]],"v":[[15.75,-91.5],[13.5,-70.25],[20.75,-45.25],[22.25,-69.25]]}],"h":1},{"t":14,"s":[{"i":[[3.25,-0.259],[-0.75,-12.192],[-3,0],[1,12.711]],"o":[[-3.25,0.259],[0.75,12.192],[3,0],[-1,-12.711]],"v":[[8.5,-107.241],[6.25,-85.191],[13.5,-59.25],[15,-84.153]]}],"h":1},{"t":16,"s":[{"i":[[3.242,-0.342],[-1.058,-12.169],[-2.999,0.076],[1.322,12.682]],"o":[[-3.242,0.342],[1.058,12.169],[2.999,-0.076],[-1.321,-12.682]],"v":[[2.647,-113.923],[1.706,-89.823],[10.36,-63.574],[10.979,-88.507]]}],"h":1},{"t":18,"s":[{"i":[[3.242,-0.342],[-1.058,-12.169],[-2.999,0.076],[1.322,12.682]],"o":[[-3.242,0.342],[1.058,12.169],[2.999,-0.076],[-1.321,-12.682]],"v":[[50.897,-90.173],[49.956,-66.073],[58.61,-39.824],[59.229,-64.757]]}],"h":1}]},"nm":"Path 1"},{"ind":1,"ty":"sh","closed":true,"ks":{"k":[{"t":10,"s":[{"i":[[2.229,0.064],[-0.228,-17.339],[-2.078,-0.19],[0.231,12.73]],"o":[[-2.229,-0.064],[0.228,17.339],[2.078,0.19],[-0.231,-12.73]],"v":[[34.222,39.563],[31.343,63.133],[33.987,90.906],[39.021,63.403]]}],"h":1},{"t":12,"s":[{"i":[[4,-0.25],[-3.25,-31],[-3.75,0],[2.5,22.75]],"o":[[-4,0.25],[3.25,31],[3.75,0],[-2.5,-22.75]],"v":[[16,-36.5],[16.5,6],[27.25,56.5],[26.75,6]]}],"h":1},{"t":14,"s":[{"i":[[2.75,0.25],[-3.25,-31],[-3.75,0],[2.5,22.75]],"o":[[-2.75,-0.25],[3.25,31],[3.75,0],[-2.5,-22.75]],"v":[[13.75,-46.5],[14.75,-1.25],[27.75,52],[24.5,-2.75]]}],"h":1},{"t":16,"s":[{"i":[[2.75,0.25],[-5.201,-30.733],[-2.75,0.25],[2.5,22.75]],"o":[[-2.75,-0.25],[5.5,32.5],[3.735,-0.34],[-2.5,-22.75]],"v":[[8.75,-57.5],[13.25,-7.25],[26.25,47.5],[22.5,-9]]}],"h":1},{"t":18,"s":[{"i":[[4.25,0],[-5.201,-30.733],[-2.75,0.25],[2.5,22.75]],"o":[[-7.603,0],[5.5,32.5],[3.735,-0.34],[-2.5,-22.75]],"v":[[1,-113.75],[9.5,-40.25],[27.5,49],[17,-43.25]]}],"h":1}]},"nm":"Path 2"},{"ty":"tr","p":{"k":[0,0],"ix":2},"a":{"k":[0,0],"ix":1},"s":{"k":[100,100],"ix":3},"r":{"k":0,"ix":6},"o":{"k":100,"ix":7},"sk":{"k":0,"ix":4},"sa":{"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 3"},{"ty":"mm","mm":3,"nm":"Merge Paths 1"},{"ty":"fl","fillEnabled":true,"c":{"k":[0,0.19,0.3,1]},"o":{"k":100},"nm":"Fill 1"},{"ty":"tr","p":{"k":[0,0],"ix":2},"a":{"k":[0,0],"ix":1},"s":{"k":[100,100],"ix":3},"r":{"k":0,"ix":6},"o":{"k":100,"ix":7},"sk":{"k":0,"ix":4},"sa":{"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1"}],"bounds":{"l":-26,"t":-125,"b":103,"r":63},"ip":10,"op":21,"st":1,"bm":0,"sr":1},{"ddd":0,"ind":17,"ty":4,"nm":"goop_01","ks":{"o":{"k":100},"r":{"k":0},"p":{"k":[250,300,0]},"a":{"k":[0,0,0]},"s":{"k":[100,100,100]}},"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","closed":true,"ks":{"k":{"i":[[-2.25,-1.375],[-0.875,-8.625],[1.75,0],[-2,0.125],[2.125,0.5],[0.5,2.625],[0.25,3.25],[-2.75,0.875]],"o":[[2.25,1.375],[0.875,8.625],[-1.275,0],[2,-0.125],[-2.125,-0.5],[-0.5,-2.625],[-0.25,-3.25],[2.75,-0.875]],"v":[[41,61.125],[45.625,78.25],[44.75,91],[49.125,95.375],[52,93.25],[47.875,87.25],[45.625,70.25],[48.75,63.375]]}},"nm":"Path 1"},{"ind":1,"ty":"sh","closed":true,"ks":{"k":{"i":[[5,-1],[-3.625,-0.125],[-0.375,-5.25],[2.625,0.5],[-6.875,-1.75],[3.5,1],[1.25,8.5],[-2.625,0.125]],"o":[[-5,1],[3.625,0.125],[0.375,5.25],[-2.625,-0.5],[6.875,1.75],[-3.5,-1],[-1.25,-8.5],[2.625,-0.125]],"v":[[34.875,58.375],[29.75,61.5],[35.25,72.625],[31.75,86.875],[42.125,94.875],[46,91.5],[37.75,72.5],[42.375,62.125]]}},"nm":"Path 2"},{"ty":"fl","fillEnabled":true,"c":{"k":[0,0.19,0.3,1]},"o":{"k":100},"nm":"Fill 1"},{"ty":"tr","p":{"k":[0,0],"ix":2},"a":{"k":[0,0],"ix":1},"s":{"k":[100,100],"ix":3},"r":{"k":0,"ix":6},"o":{"k":100,"ix":7},"sk":{"k":0,"ix":4},"sa":{"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1"}],"bounds":{"l":28,"t":58,"b":96,"r":53},"ip":8,"op":10,"st":1,"bm":0,"sr":1},{"ddd":0,"ind":18,"ty":4,"nm":"C_shape_01","ks":{"o":{"k":100},"r":{"k":0},"p":{"k":[250,300,0]},"a":{"k":[0,0,0]},"s":{"k":[100,100,100]}},"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","closed":true,"ks":{"k":{"i":[[13,0],[16.5,0],[5.956,-0.726],[0,0],[0,0],[11.5,0.75]],"o":[[-13,0],[-16.5,0],[-10.25,1.25],[0,0],[0,0],[-11.5,-0.75]],"v":[[43.75,97],[-5,101],[-67.25,100.75],[-69.75,104.75],[101.5,104.75],[79.5,102.25]]}},"nm":"Path 1"},{"ty":"fl","fillEnabled":true,"c":{"k":[0,0.19,0.3,1]},"o":{"k":100},"nm":"Fill 1"},{"ty":"tr","p":{"k":[0,0],"ix":2},"a":{"k":[0,0],"ix":1},"s":{"k":[100,100],"ix":3},"r":{"k":0,"ix":6},"o":{"k":100,"ix":7},"sk":{"k":0,"ix":4},"sa":{"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1"}],"bounds":{"l":-73,"t":97,"b":105,"r":102},"ip":1,"op":2,"st":1,"bm":0,"sr":1},{"ddd":0,"ind":19,"ty":4,"nm":"C_shape_02","ks":{"o":{"k":100},"r":{"k":0},"p":{"k":[250,300,0]},"a":{"k":[0,0,0]},"s":{"k":[100,100,100]}},"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","closed":true,"ks":{"k":{"i":[[33.5,0],[12.512,-3.204],[0,0],[0,0],[0,1.75],[18,2.5],[12.5,0.5]],"o":[[-33.5,0],[-20.5,5.25],[0,0],[0,0],[0,-1.75],[-18,-2.5],[-12.5,-0.5]],"v":[[-16.25,87.25],[-78,93.5],[-87.75,104],[137.5,104],[141.5,102.5],[107,91.75],[65,90.75]]}},"nm":"Path 1"},{"ty":"fl","fillEnabled":true,"c":{"k":[0,0.19,0.3,1]},"o":{"k":100},"nm":"Fill 1"},{"ty":"tr","p":{"k":[0,0],"ix":2},"a":{"k":[0,0],"ix":1},"s":{"k":[100,100],"ix":3},"r":{"k":0,"ix":6},"o":{"k":100,"ix":7},"sk":{"k":0,"ix":4},"sa":{"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1"}],"bounds":{"l":-91,"t":87,"b":105,"r":142},"ip":2,"op":4,"st":1,"bm":0,"sr":1},{"ddd":0,"ind":20,"ty":4,"nm":"C_shape_03","ks":{"o":{"k":100},"r":{"k":0},"p":{"k":[250,300,0]},"a":{"k":[0,0,0]},"s":{"k":[100,100,100]}},"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","closed":true,"ks":{"k":{"i":[[59,0],[5.5,-2.25],[0,-2.75],[0,0],[0,0],[0,3.25],[8.5,5]],"o":[[-55,0],[-5.5,2.25],[0,2.75],[0,0],[0,0],[0,-3.25],[-8.5,-5]],"v":[[17.25,55.75],[-71,95.25],[-82.5,101.75],[-77,104.5],[136.75,104.5],[140.75,100.75],[122,89.75]]}},"nm":"Path 1"},{"ty":"fl","fillEnabled":true,"c":{"k":[0,0.19,0.3,1]},"o":{"k":100},"nm":"Fill 1"},{"ty":"tr","p":{"k":[0,0],"ix":2},"a":{"k":[0,0],"ix":1},"s":{"k":[100,100],"ix":3},"r":{"k":0,"ix":6},"o":{"k":100,"ix":7},"sk":{"k":0,"ix":4},"sa":{"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1"}],"bounds":{"l":-83,"t":55,"b":105,"r":141},"ip":4,"op":6,"st":1,"bm":0,"sr":1},{"ddd":0,"ind":21,"ty":4,"nm":"C_shape_04","ks":{"o":{"k":100},"r":{"k":0},"p":{"k":[250,300,0]},"a":{"k":[0,0,0]},"s":{"k":[100,100,100]}},"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","closed":true,"ks":{"k":{"i":[[19.25,0],[15.25,-12],[0,-2.75],[0,-2.75],[0,0],[0,0],[0,2],[1.25,3.5],[22,13.5]],"o":[[-19.25,0],[-16.268,12.801],[0,2.75],[0,2.75],[0,0],[0,0],[0,-2],[-1.25,-3.5],[-22,-13.5]],"v":[[15.75,41.5],[-44.5,62.75],[-58.5,94.75],[-61,100.5],[-57.25,104],[121.5,104],[126,101.5],[121.5,95],[88.25,55.75]]}},"nm":"Path 1"},{"ty":"fl","fillEnabled":true,"c":{"k":[0,0.19,0.3,1]},"o":{"k":100},"nm":"Fill 1"},{"ty":"tr","p":{"k":[0,0],"ix":2},"a":{"k":[0,0],"ix":1},"s":{"k":[100,100],"ix":3},"r":{"k":0,"ix":6},"o":{"k":100,"ix":7},"sk":{"k":0,"ix":4},"sa":{"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1"}],"bounds":{"l":-61,"t":41,"b":104,"r":126},"ip":6,"op":8,"st":1,"bm":0,"sr":1},{"ddd":0,"ind":22,"ty":4,"nm":"C_shape_05","ks":{"o":{"k":100},"r":{"k":0},"p":{"k":[250,300,0]},"a":{"k":[0,0,0]},"s":{"k":[100,100,100]}},"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","closed":true,"ks":{"k":{"i":[[18.5,-10.75],[2.5,-10.75],[0,-9.5],[0,0],[-10.75,0],[0,7.25],[49.25,28.75]],"o":[[-18.5,10.75],[-2.5,10.75],[0,9.5],[0,0],[5.5,0],[0,-7.25],[-28.385,-16.57]],"v":[[-21.5,7.25],[-43.75,59.5],[-57.75,95.25],[-53.5,104.5],[124.75,104.5],[125.25,88.75],[55.75,14.25]]}},"nm":"Path 1"},{"ind":1,"ty":"sh","closed":true,"ks":{"k":{"i":[[7.044,7.208],[0,-11.25],[-8,-2.75],[-2.25,2.25]],"o":[[-10.75,-11],[0,11.25],[8,2.75],[2.25,-2.25]],"v":[[54,69.25],[23,70.25],[44,90.5],[66,92.75]]}},"nm":"Path 2"},{"ty":"mm","mm":3,"nm":"Merge Paths 1"},{"ty":"fl","fillEnabled":true,"c":{"k":[0,0.19,0.3,1]},"o":{"k":100},"nm":"Fill 1"},{"ty":"tr","p":{"k":[0,0],"ix":2},"a":{"k":[0,0],"ix":1},"s":{"k":[100,100],"ix":3},"r":{"k":0,"ix":6},"o":{"k":100,"ix":7},"sk":{"k":0,"ix":4},"sa":{"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1"}],"bounds":{"l":-58,"t":0,"b":105,"r":128},"ip":8,"op":10,"st":1,"bm":0,"sr":1},{"ddd":0,"ind":23,"ty":4,"nm":"C_shape_06","ks":{"o":{"k":100},"r":{"k":0},"p":{"k":[250,300,0]},"a":{"k":[0,0,0]},"s":{"k":[100,100,100]}},"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","closed":true,"ks":{"k":{"i":[[19,1.5],[13.25,-18.25],[-2.5,-20.5],[1.5,-9.5],[-0.25,-7],[0,0],[0,0],[18,0],[17.5,0],[0,4.75],[0,8.75],[-33.25,0],[0,0],[0,0],[31.738,30.227]],"o":[[-19,-1.5],[-13.25,18.25],[2.5,20.5],[-1.5,9.5],[0.25,7],[0,0],[0,0],[-18,0],[-17.5,0],[0,-4.75],[0,-8.75],[33.25,0],[0,0],[0,0],[-26.25,-25]],"v":[[13.75,-84],[-51,-67],[-54.25,1.75],[-50.5,83],[-56.75,98],[-47.25,104.625],[112.5,104.5],[66.75,98.5],[17.5,100],[-3.75,92],[0.5,54.25],[23.25,-24],[67.5,17.25],[116.75,17.25],[83.25,-47]]}},"nm":"Path 1"},{"ty":"fl","fillEnabled":true,"c":{"k":[0,0.19,0.3,1]},"o":{"k":100},"nm":"Fill 1"},{"ty":"tr","p":{"k":[0,0],"ix":2},"a":{"k":[0,0],"ix":1},"s":{"k":[100,100],"ix":3},"r":{"k":0,"ix":6},"o":{"k":100,"ix":7},"sk":{"k":0,"ix":4},"sa":{"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1"}],"bounds":{"l":-59,"t":-85,"b":105,"r":117},"ip":10,"op":12,"st":1,"bm":0,"sr":1},{"ddd":0,"ind":24,"ty":4,"nm":"C","ks":{"o":{"k":100},"r":{"k":0},"p":{"k":[267.5,286.5,0]},"a":{"k":[17.5,-113.5,0]},"s":{"k":[100,100,100]}},"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","closed":true,"ks":{"k":[{"t":12,"s":[{"i":[[-75.371,0],[-29.102,19.63],[0,0],[27.758,0],[0,39.88],[-14.871,8.889],[-17.399,-15.135],[0,0],[33.005,-27.323],[0,-56.954]],"o":[[28.379,0],[0,0],[-21.399,14.728],[-33.492,0],[0,-39.88],[18.412,-11.006],[0,0],[-43.102,-64.537],[-20.371,16.864],[0,65.546]],"v":[[17.871,3.021],[110.102,-19.63],[79.399,-59.228],[16.492,-39.454],[-52.722,-133.297],[-22.129,-196.889],[38.649,-190.115],[81.602,-210.463],[-55.879,-235.614],[-93.697,-126.546]]}],"h":1},{"t":14,"s":[{"i":[[-73.121,0],[-23.352,24.88],[0,0],[21.451,0],[0,39.88],[-25.621,13.139],[-14.399,-9.885],[0,0],[34.879,-24.886],[0,-56.954]],"o":[[28.379,0],[0,0],[-13.399,13.228],[-41.371,0],[0,-39.88],[19.087,-9.788],[0,0],[-60.852,-29.037],[-21.382,15.256],[0,65.546]],"v":[[17.871,3.021],[107.352,-33.13],[70.149,-63.478],[15.492,-40.704],[-52.972,-128.796],[-15.879,-208.639],[41.649,-205.615],[74.102,-245.463],[-54.379,-238.114],[-96.197,-128.546]]}],"h":1},{"t":16,"s":[{"i":[[-73.121,0],[-23.352,24.88],[0,0],[21.451,0],[0,39.88],[-27.571,14.639],[-17.399,-8.235],[0,0],[35.183,-27.163],[0,-61.393]],"o":[[28.379,0],[0,0],[-12.973,14.355],[-41.371,0],[0,-39.88],[18.952,-10.046],[0,0],[-43.902,-18.237],[-18.075,13.821],[0,65.546]],"v":[[17.871,3.021],[104.052,-35.08],[67.449,-66.628],[15.42,-41.304],[-55.222,-131.646],[-13.629,-212.839],[44.199,-213.565],[64.352,-254.913],[-55.579,-240.064],[-98.897,-133.197]]}],"h":1},{"t":18,"s":[{"i":[[-73.121,0],[-23.352,24.88],[0,0],[21.451,0],[0,39.88],[-28.871,15.639],[-19.399,-7.135],[0,0],[35.385,-28.682],[0,-64.352]],"o":[[28.379,0],[0,0],[-12.689,15.106],[-41.371,0],[0,-39.88],[18.861,-10.217],[0,0],[-32.602,-11.037],[-15.871,12.864],[0,65.547]],"v":[[17.871,3.021],[101.852,-36.38],[65.649,-68.728],[15.371,-41.704],[-56.722,-133.547],[-12.129,-215.639],[45.899,-218.865],[57.852,-261.213],[-56.379,-241.364],[-100.697,-136.297]]}],"h":1},{"t":21,"s":[{"i":[[-65.621,0],[-23.352,24.88],[0,0],[21.451,0],[0,39.88],[-38.121,11.389],[-21.899,0],[0,0],[37.129,-26.386],[0,-64.352]],"o":[[35.348,0],[0,0],[-12.689,15.106],[-43.371,0],[0,-39.88],[20.553,-6.141],[0,0],[-32.602,0],[-25.979,18.462],[0,66.797]],"v":[[17.871,3.021],[105.102,-35.88],[68.399,-68.478],[17.871,-43.204],[-53.972,-116.297],[4.871,-200.639],[71.899,-204.115],[73.602,-249.713],[-44.879,-225.114],[-97.447,-116.297]]}],"h":1},{"t":23,"s":[{"i":[[-68.806,0],[-28.5,22.214],[0,0],[22.279,0],[0,39.261],[-41.419,0],[-13.91,-13.396],[0,0],[41.444,0],[0,-63.353]],"o":[[36.713,0],[0,0],[-14.91,13.625],[-41.419,0],[0,-39.261],[22.279,0],[0,0],[-21.965,-25.876],[-67.15,0],[0,59.979]],"v":[[18.306,3.021],[114.75,-36.214],[73.41,-67.125],[18.306,-42.486],[-57.241,-107.989],[14.806,-177.743],[74.66,-153.604],[111.75,-183.265],[12.056,-228],[-104,-111.479]]}],"h":1},{"t":25,"s":[{"i":[[-64.655,0],[-21.149,25.983],[0,0],[21.451,0],[0,39.88],[-39.88,0],[-12.689,-15.408],[0,0],[35.349,0],[0,-64.352]],"o":[[35.349,0],[0,0],[-12.689,15.106],[-39.88,0],[0,-39.88],[21.451,0],[0,0],[-21.149,-26.285],[-64.655,0],[0,64.352]],"v":[[17.871,3.021],[107.602,-39.88],[71.649,-67.978],[17.871,-43.204],[-52.222,-113.297],[17.871,-183.389],[71.649,-158.615],[107.602,-186.713],[17.871,-229.614],[-98.447,-113.297]]}],"h":1}]},"nm":"C"},{"ty":"fl","fillEnabled":true,"c":{"k":[0,0.19,0.3,1]},"o":{"k":100},"nm":"Fill 1"},{"ty":"tr","p":{"k":[0,0],"ix":2},"a":{"k":[0,0],"ix":1},"s":{"k":[100,100],"ix":3},"r":{"k":0,"ix":6},"o":{"k":100,"ix":7},"sk":{"k":0,"ix":4},"sa":{"k":0,"ix":5},"nm":"Transform"}],"nm":"C"}],"bounds":{"l":-104,"t":-268,"b":4,"r":115},"ip":12,"op":750,"st":1,"bm":0,"sr":1}],"ip":0,"op":101,"fr":25,"w":500,"h":600} \ No newline at end of file diff --git a/lottie/src/main/java/com/airbnb/lottie/L.java b/lottie/src/main/java/com/airbnb/lottie/L.java index d60e2103d3..fea138e015 100644 --- a/lottie/src/main/java/com/airbnb/lottie/L.java +++ b/lottie/src/main/java/com/airbnb/lottie/L.java @@ -1,5 +1,5 @@ package com.airbnb.lottie; public class L { - public static final boolean DBG = false; + public static final boolean DBG = true; } diff --git a/lottie/src/main/java/com/airbnb/lottie/LottieAnimationView.java b/lottie/src/main/java/com/airbnb/lottie/LottieAnimationView.java index 71597d213c..2c4584974e 100644 --- a/lottie/src/main/java/com/airbnb/lottie/LottieAnimationView.java +++ b/lottie/src/main/java/com/airbnb/lottie/LottieAnimationView.java @@ -17,6 +17,7 @@ import android.support.annotation.VisibleForTesting; import android.text.TextUtils; import android.util.AttributeSet; +import android.util.Log; import android.util.LongSparseArray; import android.widget.ImageView; @@ -27,7 +28,7 @@ import org.json.JSONObject; -import java.util.Collections; +import java.util.ArrayList; import java.util.List; /** @@ -41,7 +42,7 @@ * You may manually set the progress of the animation with {@link #setProgress(float)} */ public class LottieAnimationView extends ImageView { - + private static final String TAG = LottieAnimationView.class.getSimpleName(); /** * Returns a {@link LottieAnimationView} that will allow it to be used without being attached to a window. @@ -62,7 +63,6 @@ public void onCompositionLoaded(LottieComposition composition) { } }; - private final LongSparseArray layerMap = new LongSparseArray<>(); private final RootLayer rootLayer = new RootLayer(this); @FloatRange(from=0f, to=1f) private float progress; private String animationName; @@ -231,6 +231,10 @@ public void setComposition(@NonNull LottieComposition composition) { return; } + if (L.DBG) { + Log.v(TAG, "Set Composition \n" + composition); + } + isAnimationLoading = false; clearComposition(); @@ -245,7 +249,7 @@ public void setComposition(@NonNull LottieComposition composition) { this.composition = composition; rootLayer.setCompDuration(composition.getDuration()); rootLayer.setBounds(0, 0, composition.getBounds().width(), composition.getBounds().height()); - buildSubviewsForComposition(); + buildLayersForComposition(); requestLayout(); setImageDrawable(rootLayer); @@ -255,11 +259,10 @@ public void setComposition(@NonNull LottieComposition composition) { } } - private void buildSubviewsForComposition() { - //noinspection ConstantConditions - List reversedLayers = composition.getLayers(); - Collections.reverse(reversedLayers); - + private void buildLayersForComposition() { + if (composition == null) { + throw new IllegalStateException("Composition is null"); + } Rect bounds = composition.getBounds(); if (composition.hasMasks() || composition.hasMattes()) { mainBitmap = Bitmap.createBitmap(bounds.width(), bounds.height(), Bitmap.Config.ARGB_8888); @@ -270,9 +273,11 @@ private void buildSubviewsForComposition() { if (composition.hasMattes()) { matteBitmap = Bitmap.createBitmap(bounds.width(), bounds.height(), Bitmap.Config.ARGB_8888); } + LongSparseArray layerMap = new LongSparseArray<>(composition.getLayers().size()); + List layers = new ArrayList<>(composition.getLayers().size()); LayerView maskedLayer = null; - for (int i = 0; i < reversedLayers.size(); i++) { - Layer layer = reversedLayers.get(i); + for (int i = composition.getLayers().size() - 1; i >= 0; i--) { + Layer layer = composition.getLayers().get(i); LayerView layerView; if (maskedLayer == null) { layerView = new LayerView(layer, composition, this, mainBitmap, maskBitmap, matteBitmap); @@ -288,13 +293,27 @@ private void buildSubviewsForComposition() { } layerMap.put(layerView.getId(), layerView); if (maskedLayer != null) { - maskedLayer.setMatte(layerView); + maskedLayer.setMatteLayer(layerView); maskedLayer = null; } else { + layers.add(layerView); if (layer.getMatteType() == Layer.MatteType.Add) { maskedLayer = layerView; } - rootLayer.addLayer(layerView); + } + } + + for (int i = 0; i < layers.size(); i++) { + LayerView layerView = layers.get(i); + rootLayer.addLayer(layerView); + } + + for (int i = 0; i < layerMap.size(); i++) { + long key = layerMap.keyAt(i); + LayerView layerView = layerMap.get(key); + LayerView parentLayer = layerMap.get(layerView.getLayerModel().getParentId()); + if (parentLayer != null) { + layerView.setParentLayer(parentLayer); } } } @@ -303,7 +322,6 @@ private void clearComposition() { composition = null; recycleBitmaps(); rootLayer.clearLayers(); - layerMap.clear(); } diff --git a/lottie/src/main/java/com/airbnb/lottie/LottieViewAnimator.java b/lottie/src/main/java/com/airbnb/lottie/LottieViewAnimator.java index dcc75f57fc..15d2e788ce 100644 --- a/lottie/src/main/java/com/airbnb/lottie/LottieViewAnimator.java +++ b/lottie/src/main/java/com/airbnb/lottie/LottieViewAnimator.java @@ -23,6 +23,7 @@ public static LottieViewAnimator of(Context context, String fileName, View... vi return new LottieViewAnimator(context, fileName, views); } + @SuppressWarnings("FieldCanBeLocal") private final LottieComposition.OnCompositionLoadedListener loadedListener = new LottieComposition.OnCompositionLoadedListener() { @Override public void onCompositionLoaded(LottieComposition composition) { @@ -66,13 +67,13 @@ private void setComposition(LottieComposition composition) { animator.setDuration(composition.getDuration()); for (final Layer layer : composition.getLayers()) { - final View view = viewsMap.get(layer.getLayerName()); + final View view = viewsMap.get(layer.getName()); if (view == null) { continue; } if (layer.getPosition().hasAnimation()) { - KeyframeAnimation position = layer.getPosition().animationForKeyPath(); + KeyframeAnimation position = layer.getPosition().createAnimation(); position.addUpdateListener(new KeyframeAnimation.AnimationListener() { @Override public void onValueChanged(PointF progress) { @@ -87,7 +88,7 @@ public void onValueChanged(PointF progress) { view.setTranslationY(initialPosition.y); if (layer.getScale().hasAnimation()) { - KeyframeAnimation scale = layer.getScale().animationForKeyPath(); + KeyframeAnimation scale = layer.getScale().createAnimation(); scale.addUpdateListener(new KeyframeAnimation.AnimationListener() { @Override public void onValueChanged(ScaleXY scale) { @@ -102,7 +103,7 @@ public void onValueChanged(ScaleXY scale) { view.setScaleY(initialScale.getScaleY()); if (layer.getRotation().hasAnimation()) { - KeyframeAnimation rotation = layer.getRotation().animationForKeyPath(); + KeyframeAnimation rotation = layer.getRotation().createAnimation(); rotation.addUpdateListener(new KeyframeAnimation.AnimationListener() { @Override public void onValueChanged(Float rotation) { @@ -114,7 +115,7 @@ public void onValueChanged(Float rotation) { view.setRotation(layer.getRotation().getInitialValue()); if (layer.getAnchor().hasAnimation()) { - KeyframeAnimation anchor = layer.getAnchor().animationForKeyPath(); + KeyframeAnimation anchor = layer.getAnchor().createAnimation(); anchor.addUpdateListener(new KeyframeAnimation.AnimationListener() { @Override public void onValueChanged(PointF anchor) { diff --git a/lottie/src/main/java/com/airbnb/lottie/animatable/AnimatableColorValue.java b/lottie/src/main/java/com/airbnb/lottie/animatable/AnimatableColorValue.java index 25bdccbb8a..b29e192420 100644 --- a/lottie/src/main/java/com/airbnb/lottie/animatable/AnimatableColorValue.java +++ b/lottie/src/main/java/com/airbnb/lottie/animatable/AnimatableColorValue.java @@ -4,6 +4,7 @@ import com.airbnb.lottie.animation.ColorKeyframeAnimation; import com.airbnb.lottie.animation.KeyframeAnimation; +import com.airbnb.lottie.animation.StaticKeyframeAnimation; import com.airbnb.lottie.model.LottieComposition; import org.json.JSONArray; @@ -40,18 +41,12 @@ protected Integer valueFromObject(Object object, float scale) throws JSONExcepti @Override - public KeyframeAnimation animationForKeyPath() { + public KeyframeAnimation createAnimation() { if (!hasAnimation()) { - return null; + return new StaticKeyframeAnimation<>(initialValue); } ColorKeyframeAnimation animation = new ColorKeyframeAnimation(duration, composition, keyTimes, keyValues, interpolators); animation.setStartDelay(delay); - animation.addUpdateListener(new KeyframeAnimation.AnimationListener() { - @Override - public void onValueChanged(Integer progress) { - observable.setValue(progress); - } - }); return animation; } diff --git a/lottie/src/main/java/com/airbnb/lottie/animatable/AnimatableFloatValue.java b/lottie/src/main/java/com/airbnb/lottie/animatable/AnimatableFloatValue.java index 95d15440b7..c25ec65f7a 100644 --- a/lottie/src/main/java/com/airbnb/lottie/animatable/AnimatableFloatValue.java +++ b/lottie/src/main/java/com/airbnb/lottie/animatable/AnimatableFloatValue.java @@ -2,6 +2,7 @@ import com.airbnb.lottie.animation.KeyframeAnimation; import com.airbnb.lottie.animation.NumberKeyframeAnimation; +import com.airbnb.lottie.animation.StaticKeyframeAnimation; import com.airbnb.lottie.model.LottieComposition; import org.json.JSONArray; @@ -34,15 +35,13 @@ protected Float valueFromObject(Object object, float scale) throws JSONException } @Override - public KeyframeAnimation animationForKeyPath() { + public KeyframeAnimation createAnimation() { + if (!hasAnimation()) { + return new StaticKeyframeAnimation<>(initialValue); + } + KeyframeAnimation animation = new NumberKeyframeAnimation<>(duration, composition, keyTimes, Float.class, keyValues, interpolators); animation.setStartDelay(delay); - animation.addUpdateListener(new KeyframeAnimation.AnimationListener() { - @Override - public void onValueChanged(Float progress) { - observable.setValue(progress); - } - }); return animation; } diff --git a/lottie/src/main/java/com/airbnb/lottie/animatable/AnimatableIntegerValue.java b/lottie/src/main/java/com/airbnb/lottie/animatable/AnimatableIntegerValue.java index c4482f69fa..4e87066b5b 100644 --- a/lottie/src/main/java/com/airbnb/lottie/animatable/AnimatableIntegerValue.java +++ b/lottie/src/main/java/com/airbnb/lottie/animatable/AnimatableIntegerValue.java @@ -2,6 +2,7 @@ import com.airbnb.lottie.animation.KeyframeAnimation; import com.airbnb.lottie.animation.NumberKeyframeAnimation; +import com.airbnb.lottie.animation.StaticKeyframeAnimation; import com.airbnb.lottie.model.LottieComposition; import org.json.JSONArray; @@ -15,7 +16,6 @@ public AnimatableIntegerValue(JSONObject json, int frameRate, LottieComposition super(json, frameRate, composition, isDp); if (remap100To255) { initialValue = initialValue * 255 / 100; - getObservable().setValue(initialValue); for (int i = 0; i < keyValues.size(); i++) { keyValues.set(i, keyValues.get(i) * 255 / 100); } @@ -33,15 +33,13 @@ protected Integer valueFromObject(Object object, float scale) throws JSONExcepti } @Override - public KeyframeAnimation animationForKeyPath() { + public KeyframeAnimation createAnimation() { + if (!hasAnimation()) { + return new StaticKeyframeAnimation<>(initialValue); + } + KeyframeAnimation animation = new NumberKeyframeAnimation<>(duration, composition, keyTimes, Integer.class, keyValues, interpolators); animation.setStartDelay(delay); - animation.addUpdateListener(new KeyframeAnimation.AnimationListener() { - @Override - public void onValueChanged(Integer progress) { - observable.setValue(progress); - } - }); return animation; } diff --git a/lottie/src/main/java/com/airbnb/lottie/animatable/AnimatablePathValue.java b/lottie/src/main/java/com/airbnb/lottie/animatable/AnimatablePathValue.java index ecd79bb144..8a5d3581a7 100644 --- a/lottie/src/main/java/com/airbnb/lottie/animatable/AnimatablePathValue.java +++ b/lottie/src/main/java/com/airbnb/lottie/animatable/AnimatablePathValue.java @@ -7,6 +7,7 @@ import com.airbnb.lottie.animation.KeyframeAnimation; import com.airbnb.lottie.animation.PathKeyframeAnimation; +import com.airbnb.lottie.animation.StaticKeyframeAnimation; import com.airbnb.lottie.model.LottieComposition; import com.airbnb.lottie.utils.JsonUtils; import com.airbnb.lottie.utils.SegmentedPath; @@ -18,9 +19,8 @@ import java.util.ArrayList; import java.util.List; -public class AnimatablePathValue implements AnimatableValue { +public class AnimatablePathValue implements AnimatableValue { - private final Observable observable = new Observable<>(); private final List keyTimes = new ArrayList<>(); private final List interpolators = new ArrayList<>(); private final int frameRate; @@ -58,7 +58,6 @@ public AnimatablePathValue(JSONObject pointValues, int frameRate, LottieComposit } else { // Single Value, no animation initialPoint = JsonUtils.pointFromJsonArray((JSONArray) value, composition.getScale()); - observable.setValue(initialPoint); } } @@ -111,7 +110,6 @@ private void buildAnimationForKeyframes(JSONArray keyframes) { if (i == 0) { animationPath.moveTo(startPoint.x, startPoint.y); initialPoint = startPoint; - observable.setValue(initialPoint); } else { animationPath.lineTo(startPoint.x, startPoint.y); interpolators.add(new LinearInterpolator()); @@ -165,24 +163,13 @@ private void buildAnimationForKeyframes(JSONArray keyframes) { } @Override - public Observable getObservable() { - return observable; - } - - @Override - public KeyframeAnimation animationForKeyPath() { + public KeyframeAnimation createAnimation() { if (!hasAnimation()) { - return null; + return new StaticKeyframeAnimation<>(initialPoint); } KeyframeAnimation animation = new PathKeyframeAnimation(duration, composition, keyTimes, animationPath, interpolators); animation.setStartDelay(delay); - animation.addUpdateListener(new KeyframeAnimation.AnimationListener() { - @Override - public void onValueChanged(PointF progress) { - observable.setValue(progress); - } - }); return animation; } @@ -197,6 +184,6 @@ public PointF getInitialPoint() { @Override public String toString() { - return "AnimatablePathValue{" + "initialPoint=" + initialPoint + '}'; + return "initialPoint=" + initialPoint; } } diff --git a/lottie/src/main/java/com/airbnb/lottie/animatable/AnimatablePointValue.java b/lottie/src/main/java/com/airbnb/lottie/animatable/AnimatablePointValue.java index a56372fc43..9211ef6726 100644 --- a/lottie/src/main/java/com/airbnb/lottie/animatable/AnimatablePointValue.java +++ b/lottie/src/main/java/com/airbnb/lottie/animatable/AnimatablePointValue.java @@ -2,10 +2,11 @@ import android.graphics.PointF; -import com.airbnb.lottie.model.LottieComposition; -import com.airbnb.lottie.utils.JsonUtils; import com.airbnb.lottie.animation.KeyframeAnimation; import com.airbnb.lottie.animation.PointKeyframeAnimation; +import com.airbnb.lottie.animation.StaticKeyframeAnimation; +import com.airbnb.lottie.model.LottieComposition; +import com.airbnb.lottie.utils.JsonUtils; import org.json.JSONArray; import org.json.JSONException; @@ -28,19 +29,13 @@ protected PointF valueFromObject(Object object, float scale) throws JSONExceptio } @Override - public KeyframeAnimation animationForKeyPath() { + public KeyframeAnimation createAnimation() { if (!hasAnimation()) { - return null; + return new StaticKeyframeAnimation<>(initialValue); } KeyframeAnimation animation = new PointKeyframeAnimation(duration, composition, keyTimes, keyValues, interpolators); animation.setStartDelay(delay); - animation.addUpdateListener(new KeyframeAnimation.AnimationListener() { - @Override - public void onValueChanged(PointF progress) { - observable.setValue(progress); - } - }); return animation; } diff --git a/lottie/src/main/java/com/airbnb/lottie/animatable/AnimatableScaleValue.java b/lottie/src/main/java/com/airbnb/lottie/animatable/AnimatableScaleValue.java index 04412e64b7..7b247c64d9 100644 --- a/lottie/src/main/java/com/airbnb/lottie/animatable/AnimatableScaleValue.java +++ b/lottie/src/main/java/com/airbnb/lottie/animatable/AnimatableScaleValue.java @@ -1,9 +1,10 @@ package com.airbnb.lottie.animatable; import com.airbnb.lottie.animation.KeyframeAnimation; +import com.airbnb.lottie.animation.ScaleKeyframeAnimation; +import com.airbnb.lottie.animation.StaticKeyframeAnimation; import com.airbnb.lottie.model.LottieComposition; import com.airbnb.lottie.utils.ScaleXY; -import com.airbnb.lottie.animation.ScaleKeyframeAnimation; import org.json.JSONArray; import org.json.JSONException; @@ -29,15 +30,13 @@ protected ScaleXY valueFromObject(Object object, float scale) throws JSONExcepti } @Override - public KeyframeAnimation animationForKeyPath() { + public KeyframeAnimation createAnimation() { + if (!hasAnimation()) { + return new StaticKeyframeAnimation<>(initialValue); + } + ScaleKeyframeAnimation animation = new ScaleKeyframeAnimation(duration, composition, keyTimes, keyValues, interpolators); animation.setStartDelay(delay); - animation.addUpdateListener(new KeyframeAnimation.AnimationListener() { - @Override - public void onValueChanged(ScaleXY progress) { - observable.setValue(progress); - } - }); return animation; } } diff --git a/lottie/src/main/java/com/airbnb/lottie/animatable/AnimatableShapeValue.java b/lottie/src/main/java/com/airbnb/lottie/animatable/AnimatableShapeValue.java index 981afed94e..4b2fc72a22 100644 --- a/lottie/src/main/java/com/airbnb/lottie/animatable/AnimatableShapeValue.java +++ b/lottie/src/main/java/com/airbnb/lottie/animatable/AnimatableShapeValue.java @@ -3,6 +3,7 @@ import android.graphics.Path; import android.graphics.PointF; +import com.airbnb.lottie.animation.StaticKeyframeAnimation; import com.airbnb.lottie.model.CubicCurveData; import com.airbnb.lottie.model.LottieComposition; import com.airbnb.lottie.model.ShapeData; @@ -18,6 +19,8 @@ @SuppressWarnings({"EmptyCatchBlock"}) public class AnimatableShapeValue extends BaseAnimatableValue { + private final Path convertTypePath = new Path(); + private boolean closed; public AnimatableShapeValue(JSONObject json, int frameRate, LottieComposition composition, boolean closed) { @@ -134,24 +137,20 @@ private PointF vertexAtIndex(int idx, JSONArray points) { } @Override - public KeyframeAnimation animationForKeyPath() { + public KeyframeAnimation createAnimation() { + if (!hasAnimation()) { + return new StaticKeyframeAnimation<>(convertType(initialValue)); + } + ShapeKeyframeAnimation animation = new ShapeKeyframeAnimation(duration, composition, keyTimes, keyValues, interpolators); animation.setStartDelay(delay); - animation.addUpdateListener(new KeyframeAnimation.AnimationListener() { - @Override - public void onValueChanged(Path progress) { - observable.setValue(progress); - } - }); return animation; } @Override Path convertType(ShapeData shapeData) { - if (observable.getValue() == null) { - observable.setValue(new Path()); - } - MiscUtils.getPathFromData(shapeData, observable.getValue()); - return observable.getValue(); + convertTypePath.reset(); + MiscUtils.getPathFromData(shapeData, convertTypePath); + return convertTypePath; } } diff --git a/lottie/src/main/java/com/airbnb/lottie/animatable/AnimatableValue.java b/lottie/src/main/java/com/airbnb/lottie/animatable/AnimatableValue.java index 8c76b10577..19c36e703d 100644 --- a/lottie/src/main/java/com/airbnb/lottie/animatable/AnimatableValue.java +++ b/lottie/src/main/java/com/airbnb/lottie/animatable/AnimatableValue.java @@ -2,9 +2,8 @@ import com.airbnb.lottie.animation.KeyframeAnimation; -public interface AnimatableValue { +interface AnimatableValue { - KeyframeAnimation animationForKeyPath(); + KeyframeAnimation createAnimation(); boolean hasAnimation(); - Observable getObservable(); } diff --git a/lottie/src/main/java/com/airbnb/lottie/animatable/AnimationGroup.java b/lottie/src/main/java/com/airbnb/lottie/animatable/AnimationGroup.java deleted file mode 100644 index 0630378a33..0000000000 --- a/lottie/src/main/java/com/airbnb/lottie/animatable/AnimationGroup.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.airbnb.lottie.animatable; - -import android.support.annotation.FloatRange; - -import com.airbnb.lottie.animation.KeyframeAnimation; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -public class AnimationGroup { - - public static AnimationGroup forAnimatableValues(AnimatableValue... animatableValues) { - List animations = new ArrayList<>(animatableValues.length); - - for (AnimatableValue animatableValue : animatableValues) { - if (animatableValue.hasAnimation()) { - KeyframeAnimation animation = animatableValue.animationForKeyPath(); - animations.add(animation); - } - } - return new AnimationGroup(animations); - } - - public static AnimationGroup forKeyframeAnimations(KeyframeAnimation... animations) { - return new AnimationGroup(Arrays.asList(animations)); - } - - private final List animations; - - private AnimationGroup(List animations) { - this.animations = animations; - } - - public void setProgress(@FloatRange(from=0f, to=1f) float progress) { - for (int i = 0; i < animations.size(); i++) { - animations.get(i).setProgress(progress); - } - } -} diff --git a/lottie/src/main/java/com/airbnb/lottie/animatable/BaseAnimatableValue.java b/lottie/src/main/java/com/airbnb/lottie/animatable/BaseAnimatableValue.java index 29372a4a9c..aa168a3366 100644 --- a/lottie/src/main/java/com/airbnb/lottie/animatable/BaseAnimatableValue.java +++ b/lottie/src/main/java/com/airbnb/lottie/animatable/BaseAnimatableValue.java @@ -16,11 +16,10 @@ import org.json.JSONObject; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; -abstract class BaseAnimatableValue implements AnimatableValue { - - final Observable observable = new Observable<>(); +abstract class BaseAnimatableValue implements AnimatableValue { final List keyValues = new ArrayList<>(); final List keyTimes = new ArrayList<>(); final List interpolators = new ArrayList<>(); @@ -54,7 +53,6 @@ void init(JSONObject json) { buildAnimationForKeyframes((JSONArray) value); } else { initialValue = valueFromObject(value, getScale()); - observable.setValue(convertType(initialValue)); } } catch (JSONException e) { throw new IllegalStateException("Unable to parse json " + json, e); @@ -107,7 +105,6 @@ private void buildAnimationForKeyframes(JSONArray keyframes) { if (i == 0) { //noinspection ResourceAsColor initialValue = startValue; - observable.setValue(convertType(initialValue)); } keyValues.add(startValue); if (!interpolators.isEmpty()) { @@ -174,11 +171,17 @@ public O getInitialValue() { return convertType(initialValue); } - public Observable getObservable() { - return observable; - } - protected abstract V valueFromObject(Object object, float scale) throws JSONException; - public abstract KeyframeAnimation animationForKeyPath(); + public abstract KeyframeAnimation createAnimation(); + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder(); + sb.append("initialValue=").append(initialValue); + if (!keyValues.isEmpty()) { + sb.append(", values=").append(Arrays.toString(keyTimes.toArray())); + } + return sb.toString(); + } } diff --git a/lottie/src/main/java/com/airbnb/lottie/animatable/Observable.java b/lottie/src/main/java/com/airbnb/lottie/animatable/Observable.java deleted file mode 100644 index 6a46076fa1..0000000000 --- a/lottie/src/main/java/com/airbnb/lottie/animatable/Observable.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.airbnb.lottie.animatable; - -import java.util.ArrayList; -import java.util.List; - -public class Observable { - public interface OnChangedListener { - void onChanged(); - } - - private final List listeners = new ArrayList<>(1); - private T value; - - public Observable() { - } - - public Observable(T value) { - this.value = value; - } - - public void addChangeListener(OnChangedListener listener) { - if (listeners.contains(listener)) { - throw new IllegalArgumentException("Listener already added."); - } - listeners.add(listener); - } - - public void removeChangeListener(OnChangedListener listener) { - if (!listeners.remove(listener)) { - throw new IllegalArgumentException("Listener not added."); - } - } - - public void setValue(T value) { - this.value = value; - for (int i = 0; i < listeners.size(); i++) { - listeners.get(i).onChanged(); - } - } - - public T getValue() { - return value; - } -} diff --git a/lottie/src/main/java/com/airbnb/lottie/animation/KeyframeAnimation.java b/lottie/src/main/java/com/airbnb/lottie/animation/KeyframeAnimation.java index e21e7c6442..a9ea5f3c1c 100644 --- a/lottie/src/main/java/com/airbnb/lottie/animation/KeyframeAnimation.java +++ b/lottie/src/main/java/com/airbnb/lottie/animation/KeyframeAnimation.java @@ -53,6 +53,10 @@ public void addUpdateListener(AnimationListener listener) { listeners.add(listener); } + public void removeUpdateListener(AnimationListener listener) { + listeners.remove(listener); + } + public void setProgress(@FloatRange(from = 0f, to = 1f) float progress) { if (progress < getStartDelayProgress()) { progress = 0f; @@ -109,5 +113,5 @@ private float getDurationRangeProgress() { return (float) duration / (float) composition.getDuration(); } - protected abstract T getValue(); + public abstract T getValue(); } diff --git a/lottie/src/main/java/com/airbnb/lottie/animation/StaticKeyframeAnimation.java b/lottie/src/main/java/com/airbnb/lottie/animation/StaticKeyframeAnimation.java new file mode 100644 index 0000000000..0b1aa57c6d --- /dev/null +++ b/lottie/src/main/java/com/airbnb/lottie/animation/StaticKeyframeAnimation.java @@ -0,0 +1,27 @@ +package com.airbnb.lottie.animation; + +import android.support.annotation.FloatRange; +import android.view.animation.Interpolator; + +import java.util.Collections; + +public class StaticKeyframeAnimation extends KeyframeAnimation { + + + private final T initialValue; + + public StaticKeyframeAnimation(T initialValue) { + super(0, null, Collections.emptyList(), Collections.emptyList()); + this.initialValue = initialValue; + } + + @Override + public void setProgress(@FloatRange(from = 0f, to = 1f) float progress) { + // Do nothing + } + + @Override + public T getValue() { + return initialValue; + } +} diff --git a/lottie/src/main/java/com/airbnb/lottie/layers/AnimatableLayer.java b/lottie/src/main/java/com/airbnb/lottie/layers/AnimatableLayer.java index 9c72777f2c..d83696b7ce 100644 --- a/lottie/src/main/java/com/airbnb/lottie/layers/AnimatableLayer.java +++ b/lottie/src/main/java/com/airbnb/lottie/layers/AnimatableLayer.java @@ -10,9 +10,9 @@ import android.support.annotation.ColorInt; import android.support.annotation.FloatRange; import android.support.annotation.NonNull; +import android.support.annotation.Nullable; -import com.airbnb.lottie.animatable.AnimationGroup; -import com.airbnb.lottie.animatable.Observable; +import com.airbnb.lottie.animation.KeyframeAnimation; import com.airbnb.lottie.utils.ScaleXY; import java.util.ArrayList; @@ -20,25 +20,45 @@ public class AnimatableLayer extends Drawable { - final List layers = new ArrayList<>(); - private final Observable.OnChangedListener changedListener = new Observable.OnChangedListener() { + private final KeyframeAnimation.AnimationListener integerChangedListener = new KeyframeAnimation.AnimationListener() { + @Override + public void onValueChanged(Integer progress) { + invalidateSelf(); + } + }; + private final KeyframeAnimation.AnimationListener floatChangedListener = new KeyframeAnimation.AnimationListener() { + @Override + public void onValueChanged(Float progress) { + invalidateSelf(); + } + }; + private final KeyframeAnimation.AnimationListener scaleChangedListener = new KeyframeAnimation.AnimationListener() { @Override - public void onChanged() { + public void onValueChanged(ScaleXY progress) { + invalidateSelf(); + } + }; + private final KeyframeAnimation.AnimationListener pointChangedListener = new KeyframeAnimation.AnimationListener() { + @Override + public void onValueChanged(PointF progress) { invalidateSelf(); } }; - private Observable position; - private Observable anchorPoint; + final List layers = new ArrayList<>(); + + + private KeyframeAnimation position; + private KeyframeAnimation anchorPoint; /** This should mimic CALayer#transform */ - private Observable transform; - private Observable alpha = null; - private Observable rotation; + private KeyframeAnimation transform; + private KeyframeAnimation alpha = null; + private KeyframeAnimation rotation; final long compDuration; private final Paint solidBackgroundPaint = new Paint(); @ColorInt private int backgroundColor; - private final List animations = new ArrayList<>(); + private final List> animations = new ArrayList<>(); @FloatRange(from = 0f, to = 1f) private float progress; AnimatableLayer(long compDuration, Drawable.Callback callback) { @@ -55,36 +75,18 @@ void setBackgroundColor(@ColorInt int color) { invalidateSelf(); } - void addAnimation(AnimationGroup animation) { - animations.add(animation); + void addAnimation(KeyframeAnimation newAnimation) { + animations.add(newAnimation); + } + + void removeAnimation(KeyframeAnimation animation) { + animations.remove(animation); } @Override public void draw(@NonNull Canvas canvas) { - canvas.save(); - if (position != null && position.getValue() != null) { - if (position.getValue().x != 0 || position.getValue().y != 0) { - canvas.translate(position.getValue().x, position.getValue().y); - } - } - if (transform != null && transform.getValue() != null) { - if (transform.getValue().getScaleX() != 1f || transform.getValue().getScaleY() != 1f) { - canvas.scale(transform.getValue().getScaleX(), transform.getValue().getScaleY()); - } - } - - if (rotation != null && rotation.getValue() != null) { - float rotation = this.rotation.getValue(); - if (rotation != 0f) { - canvas.rotate(rotation); - } - } - - if (anchorPoint != null && anchorPoint.getValue() != null) { - if (anchorPoint.getValue().x != 0 || anchorPoint.getValue().y != 0) { - canvas.translate(-anchorPoint.getValue().x, -anchorPoint.getValue().y); - } - } + int saveCount = canvas.save(); + applyTransformForLayer(canvas, this); int backgroundAlpha = Color.alpha(backgroundColor); if (backgroundAlpha != 0) { @@ -98,20 +100,71 @@ public void draw(@NonNull Canvas canvas) { for (int i = 0; i < layers.size(); i++) { layers.get(i).draw(canvas); } - canvas.restore(); + canvas.restoreToCount(saveCount); + } + + int saveCanvas(@Nullable Canvas canvas) { + if (canvas == null) { + return 0; + } + return canvas.save(); + } + + void restoreCanvas(@Nullable Canvas canvas, int count) { + if (canvas == null) { + return; + } + canvas.restoreToCount(count); + } + + void applyTransformForLayer(@Nullable Canvas canvas, AnimatableLayer layer) { + if (canvas == null) { + return; + } + // TODO: Determine if these null checks are necessary. + if (layer.position != null) { + PointF position = layer.position.getValue(); + if (position.x != 0 || position.y != 0) { + canvas.translate(position.x, position.y); + } + } + + if (layer.transform != null) { + ScaleXY scale = layer.transform.getValue(); + if (scale.getScaleX() != 1f || scale.getScaleY() != 1f) { + canvas.scale(scale.getScaleX(), scale.getScaleY()); + } + } + + if (layer.rotation != null) { + float rotation = layer.rotation.getValue(); + if (rotation != 0f) { + canvas.rotate(rotation); + } + } + + if (layer.anchorPoint != null) { + PointF anchorPoint = layer.anchorPoint.getValue(); + if (anchorPoint.x != 0 || anchorPoint.y != 0) { + canvas.translate(-anchorPoint.x, -anchorPoint.y); + } + } } + @Override public void setAlpha(int alpha) { throw new IllegalArgumentException("This shouldn't be used."); } - void setAlpha(Observable alpha) { + void setAlpha(KeyframeAnimation alpha) { if (this.alpha != null) { - this.alpha.removeChangeListener(changedListener); + removeAnimation(this.alpha); + this.alpha.removeUpdateListener(integerChangedListener); } this.alpha = alpha; - alpha.addChangeListener(changedListener); + addAnimation(alpha); + alpha.addUpdateListener(integerChangedListener); for (AnimatableLayer layer : layers) { layer.setAlpha(alpha); } @@ -129,36 +182,44 @@ public void setColorFilter(ColorFilter colorFilter) { } - void setAnchorPoint(Observable anchorPoint) { + void setAnchorPoint(KeyframeAnimation anchorPoint) { if (this.anchorPoint != null) { - this.anchorPoint.removeChangeListener(changedListener); + removeAnimation(this.anchorPoint); + this.anchorPoint.removeUpdateListener(pointChangedListener); } this.anchorPoint = anchorPoint; - anchorPoint.addChangeListener(changedListener); + addAnimation(anchorPoint); + anchorPoint.addUpdateListener(pointChangedListener); } - void setPosition(Observable position) { + void setPosition(KeyframeAnimation position) { if (this.position != null) { - this.position.removeChangeListener(changedListener); + removeAnimation(this.position); + this.position.removeUpdateListener(pointChangedListener); } this.position = position; - position.addChangeListener(changedListener); + addAnimation(position); + position.addUpdateListener(pointChangedListener); } - void setTransform(Observable transform) { + void setTransform(KeyframeAnimation transform) { if (this.transform != null) { - this.transform.removeChangeListener(changedListener); + removeAnimation(this.transform); + this.transform.removeUpdateListener(scaleChangedListener); } this.transform = transform; - transform.addChangeListener(changedListener); + addAnimation(this.transform); + transform.addUpdateListener(scaleChangedListener); } - void setRotation(Observable rotation) { + void setRotation(KeyframeAnimation rotation) { if (this.rotation != null) { - this.rotation.removeChangeListener(changedListener); + removeAnimation(this.rotation); + this.rotation.removeUpdateListener(floatChangedListener); } this.rotation = rotation; - rotation.addChangeListener(changedListener); + addAnimation(this.rotation); + rotation.addUpdateListener(floatChangedListener); } @Override diff --git a/lottie/src/main/java/com/airbnb/lottie/layers/EllipseShapeLayer.java b/lottie/src/main/java/com/airbnb/lottie/layers/EllipseShapeLayer.java index 22daf68d87..6379eb3ca1 100644 --- a/lottie/src/main/java/com/airbnb/lottie/layers/EllipseShapeLayer.java +++ b/lottie/src/main/java/com/airbnb/lottie/layers/EllipseShapeLayer.java @@ -5,131 +5,112 @@ import android.graphics.PointF; import android.graphics.drawable.Drawable; +import com.airbnb.lottie.animatable.AnimatableFloatValue; +import com.airbnb.lottie.animation.KeyframeAnimation; +import com.airbnb.lottie.animation.StaticKeyframeAnimation; import com.airbnb.lottie.model.CircleShape; import com.airbnb.lottie.model.ShapeFill; import com.airbnb.lottie.model.ShapeStroke; import com.airbnb.lottie.model.ShapeTransform; import com.airbnb.lottie.model.ShapeTrimPath; -import com.airbnb.lottie.animatable.Observable; -class EllipseShapeLayer extends AnimatableLayer { - - private final CircleShape circleShape; - private final ShapeFill fill; - private final ShapeStroke stroke; - private final ShapeTrimPath trim; - private final ShapeTransform transformModel; +import java.util.ArrayList; +import java.util.List; - private CircleShapeLayer fillLayer; - private CircleShapeLayer strokeLayer; +class EllipseShapeLayer extends AnimatableLayer { EllipseShapeLayer(CircleShape circleShape, ShapeFill fill, ShapeStroke stroke, ShapeTrimPath trim, ShapeTransform transform, long duration, Drawable.Callback callback) { super(duration, callback); - this.circleShape = circleShape; - this.fill = fill; - this.stroke = stroke; - this.trim = trim; - this.transformModel = transform; setBounds(transform.getCompBounds()); - setAnchorPoint(transform.getAnchor().getObservable()); - setAlpha(transform.getOpacity().getObservable()); - setPosition(transform.getPosition().getObservable()); - setTransform(transform.getScale().getObservable()); - setRotation(transform.getRotation().getObservable()); + setAnchorPoint(transform.getAnchor().createAnimation()); + setAlpha(transform.getOpacity().createAnimation()); + setPosition(transform.getPosition().createAnimation()); + setTransform(transform.getScale().createAnimation()); + setRotation(transform.getRotation().createAnimation()); if (fill != null) { - fillLayer = new CircleShapeLayer(getCallback()); - fillLayer.setColor(fill.getColor().getObservable()); - fillLayer.setAlpha(fill.getOpacity().getObservable()); + CircleShapeLayer fillLayer = new CircleShapeLayer(getCallback()); + fillLayer.setColor(fill.getColor().createAnimation()); + fillLayer.setAlpha(fill.getOpacity().createAnimation()); fillLayer.updateCircle( - circleShape.getPosition().getObservable(), - circleShape.getSize().getObservable()); + circleShape.getPosition().createAnimation(), + circleShape.getSize().createAnimation()); addLayer(fillLayer); } if (stroke != null) { - strokeLayer = new CircleShapeLayer(getCallback()); + CircleShapeLayer strokeLayer = new CircleShapeLayer(getCallback()); strokeLayer.setIsStroke(); - strokeLayer.setColor(stroke.getColor().getObservable()); - strokeLayer.setAlpha(stroke.getOpacity().getObservable()); - strokeLayer.setLineWidth(stroke.getWidth().getObservable()); - strokeLayer.setDashPattern(stroke.getLineDashPattern(), stroke.getDashOffset()); + strokeLayer.setColor(stroke.getColor().createAnimation()); + strokeLayer.setAlpha(stroke.getOpacity().createAnimation()); + strokeLayer.setLineWidth(stroke.getWidth().createAnimation()); + if (!stroke.getLineDashPattern().isEmpty()) { + List> dashPatternAnimations = new ArrayList<>(stroke.getLineDashPattern().size()); + for (AnimatableFloatValue dashPattern : stroke.getLineDashPattern()) { + dashPatternAnimations.add(dashPattern.createAnimation()); + } + strokeLayer.setDashPattern(dashPatternAnimations, stroke.getDashOffset().createAnimation()); + } strokeLayer.setLineCapType(stroke.getCapType()); strokeLayer.updateCircle( - circleShape.getPosition().getObservable(), - circleShape.getSize().getObservable()); + circleShape.getPosition().createAnimation(), + circleShape.getSize().createAnimation()); if (trim != null) { - strokeLayer.setTrimPath(trim.getStart().getObservable(), trim.getEnd().getObservable()); + strokeLayer.setTrimPath(trim.getStart().createAnimation(), trim.getEnd().createAnimation(), trim.getOffset().createAnimation()); } addLayer(strokeLayer); } - - buildAnimation(); - } - - private void buildAnimation() { - if (transformModel != null) { - addAnimation(transformModel.createAnimation()); - } - - if (stroke != null && strokeLayer != null) { - strokeLayer.addAnimation(stroke.createAnimation()); - strokeLayer.addAnimation(circleShape.createAnimation()); - if (trim != null) { - strokeLayer.addAnimation(trim.createAnimation()); - } - } - - if (fill != null && fillLayer != null) { - fillLayer.addAnimation(fill.createAnimation()); - fillLayer.addAnimation(circleShape.createAnimation()); - } } private static final class CircleShapeLayer extends ShapeLayer { private static final float ELLIPSE_CONTROL_POINT_PERCENTAGE = 0.55228f; - private final Observable.OnChangedListener circleSizeChangedListener = new Observable.OnChangedListener() { + private final KeyframeAnimation.AnimationListener circleSizeChangedListener = new KeyframeAnimation.AnimationListener() { @Override - public void onChanged() { + public void onValueChanged(PointF progress) { onCircleSizeChanged(); } }; - private final Observable.OnChangedListener circlePositionListener = new Observable.OnChangedListener() { + private final KeyframeAnimation.AnimationListener circlePositionChangedListener = new KeyframeAnimation.AnimationListener() { @Override - public void onChanged() { + public void onValueChanged(PointF progress) { invalidateSelf(); } }; private final Paint paint = new Paint(); private final Path path = new Path(); - private final Observable observable = new Observable<>(path); - private Observable circleSize; - private Observable circlePosition; + private KeyframeAnimation circleSize; + private KeyframeAnimation circlePosition; CircleShapeLayer(Drawable.Callback callback) { super(callback); paint.setAntiAlias(true); paint.setStyle(Paint.Style.FILL); - setPath(observable); + + setPath(new StaticKeyframeAnimation<>(path)); } - void updateCircle(Observable circlePosition, Observable circleSize) { + void updateCircle(KeyframeAnimation circlePosition, KeyframeAnimation circleSize) { if (this.circleSize != null) { - this.circleSize.removeChangeListener(circleSizeChangedListener); + removeAnimation(this.circleSize); + this.circleSize.removeUpdateListener(circleSizeChangedListener); + } + if (this.circlePosition != null) { + removeAnimation(this.circlePosition); + this.circlePosition.removeUpdateListener(circlePositionChangedListener); } - if (this.circlePosition != null) - this.circlePosition.removeChangeListener(circlePositionListener); this.circleSize = circleSize; this.circlePosition = circlePosition; - circleSize.addChangeListener(circleSizeChangedListener); - circlePosition.addChangeListener(circlePositionListener); + addAnimation(circleSize); + circleSize.addUpdateListener(circleSizeChangedListener); + addAnimation(circlePosition); + circlePosition.addUpdateListener(circlePositionChangedListener); onCircleSizeChanged(); } @@ -147,7 +128,6 @@ private void onCircleSizeChanged() { path.cubicTo(halfWidth, 0 + cpH, 0 + cpW, halfHeight, 0, halfHeight); path.cubicTo(0 - cpW, halfHeight, -halfWidth, 0 + cpH, -halfWidth, 0); path.cubicTo(-halfWidth, 0 - cpH, 0 - cpW, -halfHeight, 0, -halfHeight); - observable.setValue(path); onPathChanged(); invalidateSelf(); diff --git a/lottie/src/main/java/com/airbnb/lottie/layers/GroupLayerView.java b/lottie/src/main/java/com/airbnb/lottie/layers/GroupLayerView.java index 2d394bb380..3f689c7d68 100644 --- a/lottie/src/main/java/com/airbnb/lottie/layers/GroupLayerView.java +++ b/lottie/src/main/java/com/airbnb/lottie/layers/GroupLayerView.java @@ -34,11 +34,11 @@ private void setupShapeGroupWithFill(ShapeFill previousFill, ShapeStroke previousStroke, ShapeTrimPath previousTrimPath) { if (shapeTransform != null) { setBounds(shapeTransform.getCompBounds()); - setAnchorPoint(shapeTransform.getAnchor().getObservable()); - setPosition(shapeTransform.getPosition().getObservable()); - setAlpha(shapeTransform.getOpacity().getObservable()); - setTransform(shapeTransform.getScale().getObservable()); - setRotation(shapeTransform.getRotation().getObservable()); + setAnchorPoint(shapeTransform.getAnchor().createAnimation()); + setPosition(shapeTransform.getPosition().createAnimation()); + setAlpha(shapeTransform.getOpacity().createAnimation()); + setTransform(shapeTransform.getScale().createAnimation()); + setRotation(shapeTransform.getRotation().createAnimation()); } List reversedItems = new ArrayList<>(shapeGroup.getItems()); @@ -78,13 +78,5 @@ private void setupShapeGroupWithFill(ShapeFill previousFill, } } - - buildAnimation(); - } - - private void buildAnimation() { - if (shapeTransform != null) { - addAnimation(shapeTransform.createAnimation()); - } } } diff --git a/lottie/src/main/java/com/airbnb/lottie/layers/LayerView.java b/lottie/src/main/java/com/airbnb/lottie/layers/LayerView.java index 71835daa5b..7eaa833d40 100644 --- a/lottie/src/main/java/com/airbnb/lottie/layers/LayerView.java +++ b/lottie/src/main/java/com/airbnb/lottie/layers/LayerView.java @@ -6,18 +6,14 @@ import android.graphics.Color; import android.graphics.Paint; import android.graphics.Path; -import android.graphics.PointF; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.graphics.Shader; -import android.graphics.drawable.Drawable; import android.support.annotation.FloatRange; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.view.animation.Interpolator; -import com.airbnb.lottie.animatable.AnimationGroup; -import com.airbnb.lottie.animatable.Observable; import com.airbnb.lottie.animation.KeyframeAnimation; import com.airbnb.lottie.animation.NumberKeyframeAnimation; import com.airbnb.lottie.model.Layer; @@ -27,7 +23,6 @@ import com.airbnb.lottie.model.ShapeStroke; import com.airbnb.lottie.model.ShapeTransform; import com.airbnb.lottie.model.ShapeTrimPath; -import com.airbnb.lottie.utils.ScaleXY; import java.util.ArrayList; import java.util.Collections; @@ -35,10 +30,10 @@ public class LayerView extends AnimatableLayer { - /** CALayer#mask */ private MaskLayer mask; - private LayerView matte; + private LayerView matteLayer; + private final List transformLayers = new ArrayList<>(); private final Paint mainCanvasPaint = new Paint(); @Nullable private final Bitmap contentBitmap; @Nullable private final Bitmap maskBitmap; @@ -53,8 +48,7 @@ public class LayerView extends AnimatableLayer { private final Layer layerModel; private final LottieComposition composition; - private long parentId = -1; - private AnimatableLayer childContainerLayer; + @Nullable private LayerView parentLayer; public LayerView(Layer layerModel, LottieComposition composition, Callback callback, @Nullable Bitmap mainBitmap, @Nullable Bitmap maskBitmap, @Nullable Bitmap matteBitmap) { @@ -73,47 +67,18 @@ public LayerView(Layer layerModel, LottieComposition composition, Callback callb } } - setupForModel(callback); + setupForModel(); } - private void setupForModel(Drawable.Callback callback) { - Observable anchorPoint = new Observable<>(); - anchorPoint.setValue(new PointF()); - setAnchorPoint(anchorPoint); - - childContainerLayer = new AnimatableLayer(composition.getDuration(), getCallback()); - childContainerLayer.setCallback(callback); - childContainerLayer.setBackgroundColor(layerModel.getSolidColor()); - childContainerLayer.setBounds(0, 0, layerModel.getSolidWidth(), layerModel.getSolidHeight()); - - long parentId = layerModel.getParentId(); - AnimatableLayer currentChild = childContainerLayer; - while (parentId >= 0) { - if (parentId >= 0) { - this.parentId = parentId; - } - Layer parentModel = composition.layerModelForId(parentId); - ParentLayer parentLayer = new ParentLayer(parentModel, composition, getCallback()); - parentLayer.setCallback(callback); - parentLayer.addLayer(currentChild); - currentChild = parentLayer; - parentId = parentModel.getParentId(); - } - addLayer(currentChild); - - childContainerLayer.setPosition(layerModel.getPosition().getObservable()); - childContainerLayer.setAnchorPoint(layerModel.getAnchor().getObservable()); - childContainerLayer.setTransform(layerModel.getScale().getObservable()); - childContainerLayer.setRotation(layerModel.getRotation().getObservable()); - setAlpha(layerModel.getOpacity().getObservable()); - layerModel.getOpacity().getObservable().addChangeListener(new Observable.OnChangedListener() { - @Override - public void onChanged() { - mainCanvasPaint.setAlpha(Math.round(layerModel.getOpacity().getObservable().getValue())); - invalidateSelf(); - } - }); - mainCanvasPaint.setAlpha(Math.round(layerModel.getOpacity().getObservable().getValue())); + private void setupForModel() { + setBackgroundColor(layerModel.getSolidColor()); + setBounds(0, 0, layerModel.getSolidWidth(), layerModel.getSolidHeight()); + + setPosition(layerModel.getPosition().createAnimation()); + setAnchorPoint(layerModel.getAnchor().createAnimation()); + setTransform(layerModel.getScale().createAnimation()); + setRotation(layerModel.getRotation().createAnimation()); + setAlpha(layerModel.getOpacity().createAnimation()); setVisible(layerModel.hasInAnimation(), false); @@ -129,7 +94,7 @@ public void onChanged() { if (item instanceof ShapeGroup) { GroupLayerView groupLayer = new GroupLayerView((ShapeGroup) item, currentFill, currentStroke, currentTrimPath, currentTransform, compDuration, getCallback()); - childContainerLayer.addLayer(groupLayer); + addLayer(groupLayer); } else if (item instanceof ShapeTransform) { currentTransform = (ShapeTransform) item; } else if (item instanceof ShapeFill) { @@ -142,15 +107,13 @@ public void onChanged() { } if (maskBitmap != null && layerModel.getMasks() != null && !layerModel.getMasks().isEmpty()) { - mask = new MaskLayer(layerModel.getMasks(), composition, getCallback()); + setMask(new MaskLayer(layerModel.getMasks(), composition, getCallback())); maskCanvas = new Canvas(maskBitmap); } buildAnimations(); } private void buildAnimations() { - childContainerLayer.addAnimation(layerModel.createAnimation()); - if (layerModel.hasInOutAnimation()) { NumberKeyframeAnimation inOutAnimation = new NumberKeyframeAnimation<>( layerModel.getComposition().getDuration(), @@ -167,17 +130,44 @@ public void onValueChanged(Float progress) { } }); setVisible(inOutAnimation.getValue() == 1f, false); - addAnimation(AnimationGroup.forKeyframeAnimations(inOutAnimation)); + addAnimation(inOutAnimation); } else { setVisible(true, false); } } - public void setMatte(LayerView matte) { + public Layer getLayerModel() { + return layerModel; + } + + public void setParentLayer(@Nullable LayerView parentLayer) { + this.parentLayer = parentLayer; + } + + @Nullable + private LayerView getParentLayer() { + return parentLayer; + } + + private void setMask(MaskLayer mask) { + this.mask = mask; + // TODO: make this a field like other animation listeners and remove existing ones. + for (KeyframeAnimation animation : mask.getMasks()) { + addAnimation(animation); + animation.addUpdateListener(new KeyframeAnimation.AnimationListener() { + @Override + public void onValueChanged(Path progress) { + invalidateSelf(); + } + }); + } + } + + public void setMatteLayer(LayerView matteLayer) { if (matteBitmap == null) { - throw new IllegalArgumentException("Cannot set a matte if no matte contentBitmap was given!"); + throw new IllegalArgumentException("Cannot set a matte if no matte bitmap was given!"); } - this.matte = matte; + this.matteLayer = matteLayer; matteCanvas = new Canvas(matteBitmap); } @@ -198,76 +188,80 @@ public void draw(@NonNull Canvas mainCanvas) { if (!isVisible() || mainCanvasPaint.getAlpha() == 0) { return; } + + // Make a list of all parent layers. + transformLayers.clear(); + LayerView parent = parentLayer; + while (parent != null) { + transformLayers.add(parent); + parent = parent.getParentLayer(); + } + Collections.reverse(transformLayers); + if (contentCanvas == null || contentBitmap == null) { + int mainCanvasCount = saveCanvas(mainCanvas); + // Now apply the parent transformations from the top down. + for (LayerView layer : transformLayers) { + applyTransformForLayer(mainCanvas, layer); + } super.draw(mainCanvas); + mainCanvas.restoreToCount(mainCanvasCount); return; } - super.draw(contentCanvas); - Bitmap mainBitmap; - if (maskBitmap != null && maskCanvas != null && mask != null && !mask.getMasks().isEmpty()) { - int maskSaveCount = maskCanvas.save(); - long parentId = this.parentId; - while (parentId >= 0) { - Layer parent = composition.layerModelForId(parentId); - applyTransformForLayer(maskCanvas, parent); - parentId = parent.getParentId(); - } + int contentCanvasCount = saveCanvas(contentCanvas); + int maskCanvasCount = saveCanvas(maskCanvas); + // Now apply the parent transformations from the top down. + for (LayerView layer : transformLayers) { + applyTransformForLayer(contentCanvas, layer); + applyTransformForLayer(maskCanvas, layer); + } + // We only have to apply the transformation to the mask because it's normally handed in AnimatableLayer#draw but masks don't go through that. + applyTransformForLayer(maskCanvas, this); - applyTransformForLayer(maskCanvas, layerModel); + super.draw(contentCanvas); + Bitmap mainBitmap; + if (hasMasks()) { for (int i = 0; i < mask.getMasks().size(); i++) { - Path path = mask.getMasks().get(i).getMaskPath().getObservable().getValue(); + Path path = mask.getMasks().get(i).getValue(); maskCanvas.drawPath(path, maskShapePaint); } - maskCanvas.restoreToCount(maskSaveCount); - if (matte == null) { + if (!hasMattes()) { mainCanvas.drawBitmap(maskBitmap, 0, 0, maskPaint); } mainBitmap = maskBitmap; } else { - if (matte == null) { - //noinspection ConstantConditions + if (!hasMattes()) { mainCanvas.drawBitmap(contentBitmap, 0, 0, mainCanvasPaint); } mainBitmap = contentBitmap; } - if (matteCanvas != null && matteBitmap != null && matte != null) { - matte.draw(matteCanvas); + restoreCanvas(contentCanvas, contentCanvasCount); + restoreCanvas(maskCanvas, maskCanvasCount); + + if (hasMattes()) { //noinspection ConstantConditions + matteLayer.draw(matteCanvas); matteCanvas.drawBitmap(mainBitmap, 0, 0, mattePaint); mainCanvas.drawBitmap(matteBitmap, 0, 0, mainCanvasPaint); } } - private void applyTransformForLayer(Canvas canvas, Layer layer) { - PointF position = layer.getPosition().getObservable().getValue(); - if (position.x != 0 || position.y != 0) { - canvas.translate(position.x, position.y); - } - - ScaleXY scale = layer.getScale().getObservable().getValue(); - if (scale.getScaleX() != 1f || scale.getScaleY() != 1f) { - canvas.scale(scale.getScaleX(), scale.getScaleY()); - } - - float rotation = layer.getRotation().getObservable().getValue(); - if (rotation != 0f) { - canvas.rotate(rotation); - } + private boolean hasMattes() { + return matteCanvas != null && matteBitmap != null && matteLayer != null; + } - PointF translation = layer.getAnchor().getObservable().getValue(); - if (translation.x != 0 || translation.y != 0) { - canvas.translate(-translation.x, -translation.y); - } + private boolean hasMasks() { + return maskBitmap != null && maskCanvas != null && mask != null && !mask.getMasks().isEmpty(); } @Override public void setProgress(@FloatRange(from = 0f, to = 1f) float progress) { super.setProgress(progress); - if (matte != null) { - matte.setProgress(progress); + if (matteLayer != null) { + matteLayer.setProgress(progress); } } diff --git a/lottie/src/main/java/com/airbnb/lottie/layers/MaskLayer.java b/lottie/src/main/java/com/airbnb/lottie/layers/MaskLayer.java index 2754d93af2..7ed46f4293 100644 --- a/lottie/src/main/java/com/airbnb/lottie/layers/MaskLayer.java +++ b/lottie/src/main/java/com/airbnb/lottie/layers/MaskLayer.java @@ -1,22 +1,28 @@ package com.airbnb.lottie.layers; +import android.graphics.Path; import android.graphics.drawable.Drawable; +import com.airbnb.lottie.animation.KeyframeAnimation; import com.airbnb.lottie.model.LottieComposition; import com.airbnb.lottie.model.Mask; +import java.util.ArrayList; import java.util.List; class MaskLayer extends AnimatableLayer { - private final List masks; + private final List> masks; MaskLayer(List masks, LottieComposition composition, Drawable.Callback callback) { super(composition.getDuration(), callback); - this.masks = masks; + this.masks = new ArrayList<>(masks.size()); + for (int i = 0; i < masks.size(); i++) { + this.masks.add(masks.get(i).getMaskPath().createAnimation()); + } } - List getMasks() { + List> getMasks() { return masks; } } diff --git a/lottie/src/main/java/com/airbnb/lottie/layers/ParentLayer.java b/lottie/src/main/java/com/airbnb/lottie/layers/ParentLayer.java deleted file mode 100644 index 2e5f07316a..0000000000 --- a/lottie/src/main/java/com/airbnb/lottie/layers/ParentLayer.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.airbnb.lottie.layers; - -import android.graphics.drawable.Drawable; -import android.util.Log; - -import com.airbnb.lottie.L; -import com.airbnb.lottie.model.LottieComposition; -import com.airbnb.lottie.model.Layer; - -class ParentLayer extends AnimatableLayer { - private static final String TAG = ParentLayer.class.getSimpleName(); - - private final Layer parentModel; - - ParentLayer(Layer parent, LottieComposition composition, Drawable.Callback callback) { - super(composition.getDuration(), callback); - setBounds(parent.getComposition().getBounds()); - this.parentModel = parent; - setupLayerFromModel(); - if (L.DBG) Log.d(TAG, "Creating parentModel layer for " + parent.getLayerName()); - } - - private void setupLayerFromModel() { - setPosition(parentModel.getPosition().getObservable()); - setAnchorPoint(parentModel.getAnchor().getObservable()); - setTransform(parentModel.getScale().getObservable()); - setRotation(parentModel.getRotation().getObservable()); - buildAnimations(); - } - - private void buildAnimations() { - addAnimation(parentModel.createAnimation()); - } -} diff --git a/lottie/src/main/java/com/airbnb/lottie/layers/RectLayer.java b/lottie/src/main/java/com/airbnb/lottie/layers/RectLayer.java index 5674d2ab0e..eaec4b5fb4 100644 --- a/lottie/src/main/java/com/airbnb/lottie/layers/RectLayer.java +++ b/lottie/src/main/java/com/airbnb/lottie/layers/RectLayer.java @@ -10,83 +10,63 @@ import android.support.annotation.Nullable; import com.airbnb.lottie.animatable.AnimatableFloatValue; -import com.airbnb.lottie.model.ShapeFill; +import com.airbnb.lottie.animation.KeyframeAnimation; import com.airbnb.lottie.model.RectangleShape; +import com.airbnb.lottie.model.ShapeFill; import com.airbnb.lottie.model.ShapeStroke; import com.airbnb.lottie.model.ShapeTransform; -import com.airbnb.lottie.animatable.Observable; +import java.util.ArrayList; import java.util.List; class RectLayer extends AnimatableLayer { - private final ShapeTransform transformModel; - private final ShapeStroke stroke; - private final ShapeFill fill; - private final RectangleShape rectShape; - @Nullable private RoundRectLayer fillLayer; @Nullable private RoundRectLayer strokeLayer; RectLayer(RectangleShape rectShape, @Nullable ShapeFill fill, @Nullable ShapeStroke stroke, ShapeTransform transform, long duration, Drawable.Callback callback) { super(duration, callback); - this.rectShape = rectShape; - this.fill = fill; - this.stroke = stroke; - this.transformModel = transform; setBounds(transform.getCompBounds()); - setAnchorPoint(transform.getAnchor().getObservable()); - setAlpha(transform.getOpacity().getObservable()); - setPosition(transform.getPosition().getObservable()); - setTransform(transform.getScale().getObservable()); - setRotation(transform.getRotation().getObservable()); + setAnchorPoint(transform.getAnchor().createAnimation()); + setAlpha(transform.getOpacity().createAnimation()); + setPosition(transform.getPosition().createAnimation()); + setTransform(transform.getScale().createAnimation()); + setRotation(transform.getRotation().createAnimation()); if (fill != null) { fillLayer = new RoundRectLayer(duration, getCallback()); - fillLayer.setColor(fill.getColor().getObservable()); - fillLayer.setShapeAlpha(fill.getOpacity().getObservable()); - fillLayer.setTransformAlpha(transformModel.getOpacity().getObservable()); - fillLayer.setRectCornerRadius(rectShape.getCornerRadius().getObservable()); - fillLayer.setRectSize(rectShape.getSize().getObservable()); - fillLayer.setRectPosition(rectShape.getPosition().getObservable()); + fillLayer.setColor(fill.getColor().createAnimation()); + fillLayer.setShapeAlpha(fill.getOpacity().createAnimation()); + fillLayer.setTransformAlpha(transform.getOpacity().createAnimation()); + fillLayer.setRectCornerRadius(rectShape.getCornerRadius().createAnimation()); + fillLayer.setRectSize(rectShape.getSize().createAnimation()); + fillLayer.setRectPosition(rectShape.getPosition().createAnimation()); addLayer(fillLayer); } if (stroke != null) { strokeLayer = new RoundRectLayer(duration, getCallback()); strokeLayer.setIsStroke(); - strokeLayer.setColor(stroke.getColor().getObservable()); - strokeLayer.setShapeAlpha(stroke.getOpacity().getObservable()); - strokeLayer.setTransformAlpha(transformModel.getOpacity().getObservable()); - strokeLayer.setLineWidth(stroke.getWidth().getObservable()); - strokeLayer.setDashPattern(stroke.getLineDashPattern(), stroke.getDashOffset()); + strokeLayer.setColor(stroke.getColor().createAnimation()); + strokeLayer.setShapeAlpha(stroke.getOpacity().createAnimation()); + strokeLayer.setTransformAlpha(transform.getOpacity().createAnimation()); + strokeLayer.setLineWidth(stroke.getWidth().createAnimation()); + if (!stroke.getLineDashPattern().isEmpty()) { + List> dashPatternAnimations = new ArrayList<>(stroke.getLineDashPattern().size()); + for (AnimatableFloatValue dashPattern : stroke.getLineDashPattern()) { + dashPatternAnimations.add(dashPattern.createAnimation()); + } + strokeLayer.setDashPattern(dashPatternAnimations, stroke.getDashOffset().createAnimation()); + } strokeLayer.setLineCapType(stroke.getCapType()); - strokeLayer.setRectCornerRadius(rectShape.getCornerRadius().getObservable()); - strokeLayer.setRectSize(rectShape.getSize().getObservable()); - strokeLayer.setRectPosition(rectShape.getPosition().getObservable()); + strokeLayer.setRectCornerRadius(rectShape.getCornerRadius().createAnimation()); + strokeLayer.setRectSize(rectShape.getSize().createAnimation()); + strokeLayer.setRectPosition(rectShape.getPosition().createAnimation()); strokeLayer.setLineJoinType(stroke.getJoinType()); addLayer(strokeLayer); } - - buildAnimation(); - } - - private void buildAnimation() { - if (transformModel != null) { - addAnimation(transformModel.createAnimation()); - } - - if (stroke != null && strokeLayer != null) { - strokeLayer.addAnimation(stroke.createAnimation()); - strokeLayer.addAnimation(rectShape.createAnimation()); - } - - if (fill != null && fillLayer != null) { - fillLayer.addAnimation(fill.createAnimation()); - fillLayer.addAnimation(rectShape.createAnimation()); - } } @Override @@ -101,55 +81,68 @@ public void setAlpha(int alpha) { } private static class RoundRectLayer extends AnimatableLayer { + private final KeyframeAnimation.AnimationListener alphaChangedListener = new KeyframeAnimation.AnimationListener() { + @Override + public void onValueChanged(Integer value) { + onAlphaChanged(); + } + }; - private final Observable.OnChangedListener changedListener = new Observable.OnChangedListener() { + private final KeyframeAnimation.AnimationListener colorChangedListener = new KeyframeAnimation.AnimationListener() { @Override - public void onChanged() { - invalidateSelf(); + public void onValueChanged(Integer value) { + onColorChanged(); } }; - private final Observable.OnChangedListener alphaChangedListener = new Observable.OnChangedListener() { + private final KeyframeAnimation.AnimationListener lineWidthChangedListener = new KeyframeAnimation.AnimationListener() { @Override - public void onChanged() { - onAlphaChanged(); + public void onValueChanged(Float value) { + onLineWidthChanged(); } }; - private final Observable.OnChangedListener colorChangedListener = new Observable.OnChangedListener() { + private final KeyframeAnimation.AnimationListener dashPatternChangedListener = new KeyframeAnimation.AnimationListener() { @Override - public void onChanged() { - onColorChanged(); + public void onValueChanged(Float value) { + onDashPatternChanged(); } }; - private final Observable.OnChangedListener lineWidthChangedListener = new Observable.OnChangedListener() { + private final KeyframeAnimation.AnimationListener cornerRadiusChangedListener = new KeyframeAnimation.AnimationListener() { @Override - public void onChanged() { - onLineWidthChanged(); + public void onValueChanged(Float value) { + invalidateSelf(); } }; - private final Observable.OnChangedListener dashPatternChangedListener = new Observable.OnChangedListener() { + private final KeyframeAnimation.AnimationListener rectPositionChangedListener = new KeyframeAnimation.AnimationListener() { @Override - public void onChanged() { - onDashPatternChanged(); + public void onValueChanged(PointF value) { + invalidateSelf(); + } + }; + + private final KeyframeAnimation.AnimationListener rectSizeChangedListener = new KeyframeAnimation.AnimationListener() { + @Override + public void onValueChanged(PointF value) { + invalidateSelf(); } }; private final Paint paint = new Paint(); private final RectF fillRect = new RectF(); - private Observable color; - private Observable lineWidth; - private Observable shapeAlpha; - private Observable transformAlpha; - private Observable rectCornerRadius; - private Observable rectPosition; - private Observable rectSize; + private KeyframeAnimation color; + private KeyframeAnimation lineWidth; + private KeyframeAnimation shapeAlpha; + private KeyframeAnimation transformAlpha; + private KeyframeAnimation rectCornerRadius; + private KeyframeAnimation rectPosition; + private KeyframeAnimation rectSize; - @Nullable private List lineDashPattern; - @Nullable private AnimatableFloatValue lineDashPatternOffset; + @Nullable private List> lineDashPattern; + @Nullable private KeyframeAnimation lineDashPatternOffset; RoundRectLayer(long duration, Drawable.Callback callback) { super(duration, callback); @@ -157,21 +150,25 @@ public void onChanged() { paint.setStyle(Paint.Style.FILL); } - void setShapeAlpha(Observable alpha) { + void setShapeAlpha(KeyframeAnimation shapeAlpha) { if (this.shapeAlpha != null) { - this.shapeAlpha.removeChangeListener(alphaChangedListener); + removeAnimation(this.shapeAlpha); + this.shapeAlpha.removeUpdateListener(alphaChangedListener); } - this.shapeAlpha = alpha; - alpha.addChangeListener(alphaChangedListener); + this.shapeAlpha = shapeAlpha; + addAnimation(shapeAlpha); + shapeAlpha.addUpdateListener(alphaChangedListener); onAlphaChanged(); } - void setTransformAlpha(Observable alpha) { + void setTransformAlpha(KeyframeAnimation transformAlpha) { if (this.transformAlpha != null) { - this.transformAlpha.removeChangeListener(alphaChangedListener); + removeAnimation(this.transformAlpha); + this.transformAlpha.removeUpdateListener(alphaChangedListener); } - transformAlpha = alpha; - alpha.addChangeListener(alphaChangedListener); + this.transformAlpha = transformAlpha; + addAnimation(transformAlpha); + transformAlpha.addUpdateListener(alphaChangedListener); onAlphaChanged(); } @@ -191,12 +188,14 @@ public int getAlpha() { return paint.getAlpha(); } - public void setColor(Observable color) { + public void setColor(KeyframeAnimation color) { if (this.color != null) { - this.color.removeChangeListener(colorChangedListener); + removeAnimation(this.color); + this.color.removeUpdateListener(colorChangedListener); } this.color = color; - color.addChangeListener(colorChangedListener); + addAnimation(color); + color.addUpdateListener(colorChangedListener); onColorChanged(); } @@ -210,12 +209,14 @@ private void setIsStroke() { invalidateSelf(); } - void setLineWidth(Observable lineWidth) { + void setLineWidth(KeyframeAnimation lineWidth) { if (this.lineWidth != null) { - this.lineWidth.removeChangeListener(lineWidthChangedListener); + removeAnimation(this.lineWidth); + this.lineWidth.removeUpdateListener(lineWidthChangedListener); } this.lineWidth = lineWidth; - lineWidth.addChangeListener(lineWidthChangedListener); + addAnimation(lineWidth); + lineWidth.addUpdateListener(lineWidthChangedListener); onLineWidthChanged(); } @@ -224,24 +225,30 @@ private void onLineWidthChanged() { invalidateSelf(); } - void setDashPattern(List lineDashPattern, AnimatableFloatValue offset) { + void setDashPattern(List> lineDashPattern, KeyframeAnimation offset) { if (this.lineDashPattern != null) { - this.lineDashPattern.get(0).getObservable().removeChangeListener(dashPatternChangedListener); - this.lineDashPattern.get(1).getObservable().removeChangeListener(dashPatternChangedListener); + removeAnimation(this.lineDashPattern.get(0)); + this.lineDashPattern.get(0).removeUpdateListener(dashPatternChangedListener); + removeAnimation(this.lineDashPattern.get(1)); + this.lineDashPattern.get(1).removeUpdateListener(dashPatternChangedListener); } if (this.lineDashPatternOffset != null) { - this.lineDashPatternOffset.getObservable().removeChangeListener(dashPatternChangedListener); + removeAnimation(this.lineDashPatternOffset); + this.lineDashPatternOffset.removeUpdateListener(dashPatternChangedListener); } if (lineDashPattern.isEmpty()) { return; } this.lineDashPattern = lineDashPattern; this.lineDashPatternOffset = offset; - lineDashPattern.get(0).getObservable().addChangeListener(dashPatternChangedListener); + addAnimation(lineDashPattern.get(0)); + addAnimation(lineDashPattern.get(1)); + lineDashPattern.get(0).addUpdateListener(dashPatternChangedListener); if (!lineDashPattern.get(1).equals(lineDashPattern.get(1))) { - lineDashPattern.get(1).getObservable().addChangeListener(dashPatternChangedListener); + lineDashPattern.get(1).addUpdateListener(dashPatternChangedListener); } - offset.getObservable().addChangeListener(dashPatternChangedListener); + addAnimation(offset); + offset.addUpdateListener(dashPatternChangedListener); onDashPatternChanged(); } @@ -251,9 +258,9 @@ private void onDashPatternChanged() { } float[] values = new float[lineDashPattern.size()]; for (int i = 0; i < lineDashPattern.size(); i++) { - values[i] = lineDashPattern.get(i).getObservable().getValue(); + values[i] = lineDashPattern.get(i).getValue(); } - paint.setPathEffect(new DashPathEffect(values, lineDashPatternOffset.getObservable().getValue())); + paint.setPathEffect(new DashPathEffect(values, lineDashPatternOffset.getValue())); invalidateSelf(); } @@ -282,30 +289,36 @@ void setLineJoinType(ShapeStroke.LineJoinType lineJoinType) { } } - void setRectCornerRadius(Observable rectCornerRadius) { + void setRectCornerRadius(KeyframeAnimation rectCornerRadius) { if (this.rectCornerRadius != null) { - this.rectCornerRadius.removeChangeListener(changedListener); + removeAnimation(rectCornerRadius); + this.rectCornerRadius.removeUpdateListener(cornerRadiusChangedListener); } this.rectCornerRadius = rectCornerRadius; - rectCornerRadius.addChangeListener(changedListener); + addAnimation(rectCornerRadius); + rectCornerRadius.addUpdateListener(cornerRadiusChangedListener); invalidateSelf(); } - void setRectPosition(Observable rectPosition) { + void setRectPosition(KeyframeAnimation rectPosition) { if (this.rectPosition != null) { - this.rectPosition.removeChangeListener(changedListener); + removeAnimation(this.rectPosition); + this.rectPosition.removeUpdateListener(rectPositionChangedListener); } this.rectPosition = rectPosition; - rectPosition.addChangeListener(changedListener); + addAnimation(rectPosition); + rectPosition.addUpdateListener(rectPositionChangedListener); invalidateSelf(); } - void setRectSize(Observable rectSize) { + void setRectSize(KeyframeAnimation rectSize) { if (this.rectSize != null) { - this.rectSize.removeChangeListener(changedListener); + removeAnimation(this.rectSize); + this.rectSize.removeUpdateListener(rectSizeChangedListener); } this.rectSize = rectSize; - this.rectSize.addChangeListener(changedListener); + addAnimation(rectSize); + rectSize.addUpdateListener(rectSizeChangedListener); invalidateSelf(); } @@ -314,7 +327,6 @@ public void draw(@NonNull Canvas canvas) { if (paint.getStyle() == Paint.Style.STROKE && paint.getStrokeWidth() == 0f) { return; } - super.draw(canvas); float halfWidth = rectSize.getValue().x / 2f; float halfHeight = rectSize.getValue().y / 2f; diff --git a/lottie/src/main/java/com/airbnb/lottie/layers/ShapeLayer.java b/lottie/src/main/java/com/airbnb/lottie/layers/ShapeLayer.java index d8c06bea77..87f1002c3c 100644 --- a/lottie/src/main/java/com/airbnb/lottie/layers/ShapeLayer.java +++ b/lottie/src/main/java/com/airbnb/lottie/layers/ShapeLayer.java @@ -13,59 +13,65 @@ import android.support.annotation.IntRange; import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import android.util.Log; -import com.airbnb.lottie.animatable.AnimatableFloatValue; +import com.airbnb.lottie.animation.KeyframeAnimation; import com.airbnb.lottie.model.ShapeStroke; import com.airbnb.lottie.utils.ScaleXY; -import com.airbnb.lottie.animatable.Observable; import java.util.List; class ShapeLayer extends AnimatableLayer { - private final Observable.OnChangedListener pathChangedListener = new Observable.OnChangedListener() { + private final KeyframeAnimation.AnimationListener pathChangedListener = new KeyframeAnimation.AnimationListener() { @Override - public void onChanged() { + public void onValueChanged(Path value) { onPathChanged(); } }; - private final Observable.OnChangedListener alphaChangedListener = new Observable.OnChangedListener() { + private final KeyframeAnimation.AnimationListener alphaChangedListener = new KeyframeAnimation.AnimationListener() { @Override - public void onChanged() { + public void onValueChanged(Integer value) { onAlphaChanged(); } }; - private final Observable.OnChangedListener colorChangedListener = new Observable.OnChangedListener() { + private final KeyframeAnimation.AnimationListener colorChangedListener = new KeyframeAnimation.AnimationListener() { @Override - public void onChanged() { + public void onValueChanged(Integer value) { onColorChanged(); } }; - private final Observable.OnChangedListener lineWidthChangedListener = new Observable.OnChangedListener() { + private final KeyframeAnimation.AnimationListener lineWidthChangedListener = new KeyframeAnimation.AnimationListener() { @Override - public void onChanged() { + public void onValueChanged(Float value) { onLineWidthChanged(); } }; - private final Observable.OnChangedListener dashPatternChangedListener = new Observable.OnChangedListener() { + private final KeyframeAnimation.AnimationListener dashPatternChangedListener = new KeyframeAnimation.AnimationListener() { @Override - public void onChanged() { + public void onValueChanged(Float value) { onDashPatternChanged(); } }; - private final Observable.OnChangedListener pathPropertyChangedListener = new Observable.OnChangedListener() { + private final KeyframeAnimation.AnimationListener strokeChangedListener = new KeyframeAnimation.AnimationListener() { @Override - public void onChanged() { + public void onValueChanged(Float value) { + onPathPropertiesChanged(); + } + }; + + private final KeyframeAnimation.AnimationListener scaleChangedListener = new KeyframeAnimation.AnimationListener() { + @Override + public void onValueChanged(ScaleXY value) { onPathPropertiesChanged(); } }; - private final RectF bounds = new RectF(); private final Paint paint = new Paint(); private final Path tempPath = new Path(); private final Path currentPath = new Path(); @@ -75,21 +81,23 @@ public void onChanged() { private float currentPathScaleY; private float currentPathStrokeStart; private float currentPathStrokeEnd = 100; + private float currentPathStrokeOffset = 0; - @Nullable private Observable scale; + @Nullable private KeyframeAnimation scale; private final RectF scaleRect = new RectF(); private final Matrix scaleMatrix = new Matrix(); - private Observable path; - private Observable color; - private Observable lineWidth; - @Nullable private Observable strokeStart; - @Nullable private Observable strokeEnd; + private KeyframeAnimation path; + private KeyframeAnimation color; + private KeyframeAnimation lineWidth; + @Nullable private KeyframeAnimation strokeStart; + @Nullable private KeyframeAnimation strokeEnd; + @Nullable private KeyframeAnimation strokeOffset; - private Observable shapeAlpha; - private Observable transformAlpha; - private List lineDashPattern; - private AnimatableFloatValue lineDashPatternOffset; + private KeyframeAnimation shapeAlpha; + private KeyframeAnimation transformAlpha; + private List> lineDashPattern; + private KeyframeAnimation lineDashPatternOffset; ShapeLayer(Drawable.Callback callback) { super(0, callback); @@ -102,12 +110,14 @@ void setIsStroke() { invalidateSelf(); } - public void setColor(Observable color) { + public void setColor(KeyframeAnimation color) { if (this.color != null) { - this.color.removeChangeListener(colorChangedListener); + removeAnimation(this.color); + this.color.removeUpdateListener(colorChangedListener); } this.color = color; - color.addChangeListener(colorChangedListener); + addAnimation(color); + color.addUpdateListener(colorChangedListener); onColorChanged(); } @@ -116,14 +126,16 @@ private void onColorChanged() { invalidateSelf(); } - public void setPath(Observable path) { + public void setPath(KeyframeAnimation path) { if (this.path != null) { - this.path.removeChangeListener(pathChangedListener); + removeAnimation(this.path); + this.path.removeUpdateListener(pathChangedListener); } this.path = path; + addAnimation(path); // TODO: When the path changes, we probably have to scale it again. - path.addChangeListener(pathChangedListener); + path.addUpdateListener(pathChangedListener); onPathChanged(); } @@ -137,10 +149,11 @@ void onPathChanged() { private void onPathPropertiesChanged() { boolean needsStrokeStart = strokeStart != null && strokeStart.getValue() != currentPathStrokeStart; boolean needsStrokeEnd = strokeEnd != null && strokeEnd.getValue() != currentPathStrokeEnd; + boolean needsStrokeOffset = strokeOffset != null && strokeOffset.getValue() != currentPathStrokeOffset; boolean needsScaleX = scale != null && scale.getValue().getScaleX() != currentPathScaleX; boolean needsScaleY = scale != null && scale.getValue().getScaleY() != currentPathScaleY; - if (!needsStrokeStart && !needsStrokeEnd && !needsScaleX && !needsScaleY) { + if (!needsStrokeStart && !needsStrokeEnd && !needsScaleX && !needsScaleY && !needsStrokeOffset) { return; } currentPath.set(path.getValue()); @@ -153,24 +166,28 @@ private void onPathPropertiesChanged() { currentPath.transform(scaleMatrix, currentPath); } - if (needsStrokeStart || needsStrokeEnd) { + if (needsStrokeStart || needsStrokeEnd || needsStrokeOffset) { tempPath.set(currentPath); pathMeasure.setPath(tempPath, false); + currentPathStrokeOffset = strokeOffset.getValue(); + currentPathStrokeStart = strokeStart.getValue(); + currentPathStrokeEnd = strokeEnd.getValue(); float length = pathMeasure.getLength(); - float start = length * strokeStart.getValue() / 100f; - float end = length * strokeEnd.getValue() / 100f; - - currentPath.reset(); - // Workaround to get hardware acceleration on KitKat - // https://developer.android.com/reference/android/graphics/PathMeasure.html#getSegment(float, float, android.graphics.Path, boolean) - currentPath.rLineTo(0, 0); - currentPathStrokeStart = Math.min(start, end); - currentPathStrokeEnd = Math.max(start, end); - pathMeasure.getSegment( - currentPathStrokeStart, - currentPathStrokeEnd, - currentPath, - true); + if (length > 0) { + float offset = length * currentPathStrokeOffset / 100f; + float start = (length * currentPathStrokeStart / 100f + offset); + float end = (length * currentPathStrokeEnd / 100f + offset); + + currentPath.reset(); + // Workaround to get hardware acceleration on KitKat + // https://developer.android.com/reference/android/graphics/PathMeasure.html#getSegment(float, float, android.graphics.Path, boolean) + currentPath.rLineTo(0, 0); + pathMeasure.getSegment( + Math.min(start, end), + Math.max(start, end), + currentPath, + true); + } } invalidateSelf(); @@ -189,21 +206,25 @@ public int getAlpha() { return paint.getAlpha(); } - void setShapeAlpha(Observable shapeAlpha) { + void setShapeAlpha(KeyframeAnimation shapeAlpha) { if (this.shapeAlpha != null) { - this.shapeAlpha.removeChangeListener(alphaChangedListener); + removeAnimation(this.shapeAlpha); + this.shapeAlpha.removeUpdateListener(alphaChangedListener); } this.shapeAlpha = shapeAlpha; - shapeAlpha.addChangeListener(alphaChangedListener); + addAnimation(shapeAlpha); + shapeAlpha.addUpdateListener(alphaChangedListener); onAlphaChanged(); } - void setTransformAlpha(Observable transformAlpha) { + void setTransformAlpha(KeyframeAnimation transformAlpha) { if (this.transformAlpha != null) { - this.transformAlpha.removeChangeListener(alphaChangedListener); + removeAnimation(this.transformAlpha); + this.transformAlpha.removeUpdateListener(alphaChangedListener); } this.transformAlpha = transformAlpha; - transformAlpha.addChangeListener(alphaChangedListener); + addAnimation(transformAlpha); + transformAlpha.addUpdateListener(alphaChangedListener); onAlphaChanged(); } @@ -229,12 +250,14 @@ public int getOpacity() { return PixelFormat.TRANSLUCENT; } - void setLineWidth(Observable lineWidth) { + void setLineWidth(KeyframeAnimation lineWidth) { if (this.lineWidth != null) { - this.lineWidth.removeChangeListener(lineWidthChangedListener); + removeAnimation(this.lineWidth); + this.lineWidth.removeUpdateListener(lineWidthChangedListener); } this.lineWidth = lineWidth; - lineWidth.addChangeListener(lineWidthChangedListener); + addAnimation(lineWidth); + lineWidth.addUpdateListener(lineWidthChangedListener); onLineWidthChanged(); } @@ -243,36 +266,42 @@ private void onLineWidthChanged() { invalidateSelf(); } - void setDashPattern(List lineDashPattern, AnimatableFloatValue offset) { + void setDashPattern(List> lineDashPattern, KeyframeAnimation offset) { if (this.lineDashPattern != null) { - this.lineDashPattern.get(0).getObservable().removeChangeListener(dashPatternChangedListener); - this.lineDashPattern.get(1).getObservable().removeChangeListener(dashPatternChangedListener); + removeAnimation(this.lineDashPattern.get(0)); + this.lineDashPattern.get(0).removeUpdateListener(dashPatternChangedListener); + removeAnimation(this.lineDashPattern.get(1)); + this.lineDashPattern.get(1).removeUpdateListener(dashPatternChangedListener); } if (this.lineDashPatternOffset != null) { - this.lineDashPatternOffset.getObservable().removeChangeListener(dashPatternChangedListener); + removeAnimation(this.lineDashPatternOffset); + this.lineDashPatternOffset.removeUpdateListener(dashPatternChangedListener); } if (lineDashPattern.isEmpty()) { return; } this.lineDashPattern = lineDashPattern; this.lineDashPatternOffset = offset; - lineDashPattern.get(0).getObservable().addChangeListener(dashPatternChangedListener); + addAnimation(lineDashPattern.get(0)); + lineDashPattern.get(0).addUpdateListener(dashPatternChangedListener); if (!lineDashPattern.get(1).equals(lineDashPattern.get(1))) { - lineDashPattern.get(1).getObservable().addChangeListener(dashPatternChangedListener); + addAnimation(lineDashPattern.get(1)); + lineDashPattern.get(1).addUpdateListener(dashPatternChangedListener); } - offset.getObservable().addChangeListener(dashPatternChangedListener); + addAnimation(offset); + offset.addUpdateListener(dashPatternChangedListener); onDashPatternChanged(); } private void onDashPatternChanged() { float[] values = new float[lineDashPattern.size()]; for (int i = 0; i < lineDashPattern.size(); i++) { - values[i] = lineDashPattern.get(i).getObservable().getValue(); + values[i] = lineDashPattern.get(i).getValue(); if (values[i] == 0) { values[i] = 0.01f; } } - paint.setPathEffect(new DashPathEffect(values, lineDashPatternOffset.getObservable().getValue())); + paint.setPathEffect(new DashPathEffect(values, lineDashPatternOffset.getValue())); invalidateSelf(); } @@ -302,26 +331,39 @@ void setLineJoinType(ShapeStroke.LineJoinType lineJoinType) { } } - void setTrimPath(Observable strokeStart, Observable strokeEnd) { + void setTrimPath(KeyframeAnimation strokeStart, KeyframeAnimation strokeEnd, KeyframeAnimation strokeOffset) { if (this.strokeStart != null) { - this.strokeStart.removeChangeListener(pathPropertyChangedListener); + removeAnimation(this.strokeStart); + this.strokeStart.removeUpdateListener(strokeChangedListener); } if (this.strokeEnd != null) { - this.strokeEnd.removeChangeListener(pathPropertyChangedListener); + removeAnimation(this.strokeEnd); + this.strokeEnd.removeUpdateListener(strokeChangedListener); + } + if (this.strokeOffset != null) { + removeAnimation(this.strokeOffset); + this.strokeOffset.removeUpdateListener(strokeChangedListener); } this.strokeStart = strokeStart; this.strokeEnd = strokeEnd; - strokeStart.addChangeListener(pathPropertyChangedListener); - strokeEnd.addChangeListener(pathPropertyChangedListener); + this.strokeOffset = strokeOffset; + addAnimation(strokeStart); + strokeStart.addUpdateListener(strokeChangedListener); + addAnimation(strokeEnd); + strokeEnd.addUpdateListener(strokeChangedListener); + addAnimation(strokeOffset); + strokeOffset.addUpdateListener(strokeChangedListener); onPathPropertiesChanged(); } - void setScale(@SuppressWarnings("NullableProblems") Observable scale) { + void setScale(@SuppressWarnings("NullableProblems") KeyframeAnimation scale) { if (this.scale != null) { - this.scale.removeChangeListener(pathPropertyChangedListener); + removeAnimation(this.scale); + this.scale.removeUpdateListener(scaleChangedListener); } this.scale = scale; - scale.addChangeListener(pathPropertyChangedListener); + addAnimation(scale); + scale.addUpdateListener(scaleChangedListener); onPathPropertiesChanged(); } } diff --git a/lottie/src/main/java/com/airbnb/lottie/layers/ShapeLayerView.java b/lottie/src/main/java/com/airbnb/lottie/layers/ShapeLayerView.java index 054a64c699..8480d47d0e 100644 --- a/lottie/src/main/java/com/airbnb/lottie/layers/ShapeLayerView.java +++ b/lottie/src/main/java/com/airbnb/lottie/layers/ShapeLayerView.java @@ -3,21 +3,19 @@ import android.graphics.drawable.Drawable; import android.support.annotation.Nullable; +import com.airbnb.lottie.animatable.AnimatableFloatValue; +import com.airbnb.lottie.animatable.AnimatableScaleValue; +import com.airbnb.lottie.animation.KeyframeAnimation; import com.airbnb.lottie.model.ShapeFill; import com.airbnb.lottie.model.ShapePath; import com.airbnb.lottie.model.ShapeStroke; import com.airbnb.lottie.model.ShapeTransform; import com.airbnb.lottie.model.ShapeTrimPath; -import com.airbnb.lottie.utils.ScaleXY; -import com.airbnb.lottie.animatable.Observable; -class ShapeLayerView extends AnimatableLayer { +import java.util.ArrayList; +import java.util.List; - private final ShapePath path; - private final ShapeFill fill; - private final ShapeStroke stroke; - private final ShapeTrimPath trim; - private final ShapeTransform transformModel; +class ShapeLayerView extends AnimatableLayer { @Nullable private ShapeLayer fillLayer; @Nullable private ShapeLayer strokeLayer; @@ -26,67 +24,46 @@ class ShapeLayerView extends AnimatableLayer { @Nullable ShapeStroke stroke, @Nullable ShapeTrimPath trim, ShapeTransform transformModel, long duration, Drawable.Callback callback) { super(duration, callback); - path = shape; - this.fill = fill; - this.stroke = stroke; - this.trim = trim; - this.transformModel = transformModel; - setBounds(transformModel.getCompBounds()); - setAnchorPoint(transformModel.getAnchor().getObservable()); - setPosition(transformModel.getPosition().getObservable()); - setRotation(transformModel.getRotation().getObservable()); + setAnchorPoint(transformModel.getAnchor().createAnimation()); + setPosition(transformModel.getPosition().createAnimation()); + setRotation(transformModel.getRotation().createAnimation()); - Observable scale = transformModel.getScale().getObservable(); - setTransform(transformModel.getScale().getObservable()); + AnimatableScaleValue scale = transformModel.getScale(); + setTransform(transformModel.getScale().createAnimation()); if (fill != null) { fillLayer = new ShapeLayer(getCallback()); - fillLayer.setPath(path.getShapePath().getObservable()); - fillLayer.setColor(fill.getColor().getObservable()); - fillLayer.setShapeAlpha(fill.getOpacity().getObservable()); - fillLayer.setTransformAlpha(transformModel.getOpacity().getObservable()); - fillLayer.setScale(scale); + fillLayer.setPath(shape.getShapePath().createAnimation()); + fillLayer.setColor(fill.getColor().createAnimation()); + fillLayer.setShapeAlpha(fill.getOpacity().createAnimation()); + fillLayer.setTransformAlpha(transformModel.getOpacity().createAnimation()); + fillLayer.setScale(scale.createAnimation()); addLayer(fillLayer); } if (stroke != null) { strokeLayer = new ShapeLayer(getCallback()); strokeLayer.setIsStroke(); - strokeLayer.setPath(path.getShapePath().getObservable()); - strokeLayer.setColor(stroke.getColor().getObservable()); - strokeLayer.setShapeAlpha(stroke.getOpacity().getObservable()); - strokeLayer.setTransformAlpha(transformModel.getOpacity().getObservable()); - strokeLayer.setLineWidth(stroke.getWidth().getObservable()); - strokeLayer.setDashPattern(stroke.getLineDashPattern(), stroke.getDashOffset()); + strokeLayer.setPath(shape.getShapePath().createAnimation()); + strokeLayer.setColor(stroke.getColor().createAnimation()); + strokeLayer.setShapeAlpha(stroke.getOpacity().createAnimation()); + strokeLayer.setTransformAlpha(transformModel.getOpacity().createAnimation()); + strokeLayer.setLineWidth(stroke.getWidth().createAnimation()); + if (!stroke.getLineDashPattern().isEmpty()) { + List> dashPatternAnimations = new ArrayList<>(stroke.getLineDashPattern().size()); + for (AnimatableFloatValue dashPattern : stroke.getLineDashPattern()) { + dashPatternAnimations.add(dashPattern.createAnimation()); + } + strokeLayer.setDashPattern(dashPatternAnimations, stroke.getDashOffset().createAnimation()); + } strokeLayer.setLineCapType(stroke.getCapType()); strokeLayer.setLineJoinType(stroke.getJoinType()); - strokeLayer.setScale(scale); + strokeLayer.setScale(scale.createAnimation()); if (trim != null) { - strokeLayer.setTrimPath(trim.getStart().getObservable(), trim.getEnd().getObservable()); + strokeLayer.setTrimPath(trim.getStart().createAnimation(), trim.getEnd().createAnimation(), trim.getOffset().createAnimation()); } addLayer(strokeLayer); } - - buildAnimation(); - } - - private void buildAnimation() { - if (transformModel != null) { - addAnimation(transformModel.createAnimation()); - } - - if (stroke != null && strokeLayer != null) { - strokeLayer.addAnimation(stroke.createAnimation()); - strokeLayer.addAnimation(path.createAnimation()); - if (trim != null) { - strokeLayer.addAnimation(trim.createAnimation()); - } - } - - if (fill != null && fillLayer != null) { - fillLayer.addAnimation(fill.createAnimation()); - fillLayer.addAnimation(path.createAnimation()); - } } @Override diff --git a/lottie/src/main/java/com/airbnb/lottie/model/CircleShape.java b/lottie/src/main/java/com/airbnb/lottie/model/CircleShape.java index c00034c7da..0b46ed2ab4 100644 --- a/lottie/src/main/java/com/airbnb/lottie/model/CircleShape.java +++ b/lottie/src/main/java/com/airbnb/lottie/model/CircleShape.java @@ -2,7 +2,6 @@ import com.airbnb.lottie.animatable.AnimatablePathValue; import com.airbnb.lottie.animatable.AnimatablePointValue; -import com.airbnb.lottie.animatable.AnimationGroup; import org.json.JSONException; import org.json.JSONObject; @@ -27,8 +26,4 @@ public AnimatablePathValue getPosition() { public AnimatablePointValue getSize() { return size; } - - public AnimationGroup createAnimation() { - return AnimationGroup.forAnimatableValues(getPosition(), getSize()); - } } diff --git a/lottie/src/main/java/com/airbnb/lottie/model/Layer.java b/lottie/src/main/java/com/airbnb/lottie/model/Layer.java index 985c6eff81..77f66b5b89 100644 --- a/lottie/src/main/java/com/airbnb/lottie/model/Layer.java +++ b/lottie/src/main/java/com/airbnb/lottie/model/Layer.java @@ -9,7 +9,6 @@ import com.airbnb.lottie.animatable.AnimatableIntegerValue; import com.airbnb.lottie.animatable.AnimatablePathValue; import com.airbnb.lottie.animatable.AnimatableScaleValue; -import com.airbnb.lottie.animatable.AnimationGroup; import org.json.JSONArray; import org.json.JSONException; @@ -18,6 +17,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Locale; @SuppressWarnings({"EmptyCatchBlock"}) public class Layer { @@ -65,7 +65,7 @@ static Layer fromJson(JSONObject json, LottieComposition composition) { List parentNames = new ArrayList<>(); Layer parent = composition.layerModelForId(parentId); while (parent != null) { - parentNames.add(parent.getLayerName()); + parentNames.add(parent.getName()); parent = composition.layerModelForId(parent.getParentId()); } Log.d(TAG, "\tParents=" + Arrays.toString(parentNames.toArray())); @@ -278,7 +278,7 @@ public long getId() { return layerId; } - public String getLayerName() { + public String getName() { return layerName; } @@ -314,10 +314,6 @@ public List getShapes() { return shapes; } - public AnimationGroup createAnimation() { - return AnimationGroup.forAnimatableValues(getOpacity(), getPosition(), getAnchor(), getScale(), getRotation()); - } - public int getSolidColor() { return solidColor; } @@ -332,30 +328,46 @@ public int getSolidWidth() { @Override public String toString() { - return "Layer{" + "layerName='" + layerName + - ", anchor=" + anchor + - ", shapes=" + shapes + - ", layerId=" + layerId + - ", layerType=" + layerType + - ", parentId=" + parentId + - ", inFrame=" + inFrame + - ", outFrame=" + outFrame + - ", composition=" + composition + - ", frameRate=" + frameRate + - ", masks=" + masks + - ", solidWidth=" + solidWidth + - ", solidHeight=" + solidHeight + - ", solidColor=" + solidColor + - ", opacity=" + opacity + - ", rotation=" + rotation + - ", position=" + position + - ", scale=" + scale + - ", hasOutAnimation=" + hasOutAnimation + - ", hasInAnimation=" + hasInAnimation + - ", hasInOutAnimation=" + hasInOutAnimation + - ", inOutKeyFrames=" + inOutKeyFrames + - ", inOutKeyTimes=" + inOutKeyTimes + - ", matteType=" + matteType + - '}'; + return toString(""); + } + + public String toString(String prefix) { + StringBuilder sb = new StringBuilder(); + sb.append(prefix).append(getName()).append("\n"); + Layer parent = composition.layerModelForId(getParentId()); + if (parent != null) { + sb.append("\t\tParents: ").append(parent.getName()); + parent = composition.layerModelForId(parent.getParentId()); + while (parent != null) { + sb.append("->").append(parent.getName()); + parent = composition.layerModelForId(parent.getParentId()); + } + sb.append(prefix).append("\n"); + } + if (getPosition().hasAnimation() || getPosition().getInitialPoint().length() != 0) { + sb.append(prefix).append("\tPosition: ").append(getPosition()).append("\n"); + } + if (getRotation().hasAnimation() || getRotation().getInitialValue() != 0f) { + sb.append(prefix).append("\tRotation: ").append(getRotation()).append("\n"); + } + if (getScale().hasAnimation() || !getScale().getInitialValue().isDefault()) { + sb.append(prefix).append("\tScale: ").append(getScale()).append("\n"); + } + if (getAnchor().hasAnimation() || getAnchor().getInitialPoint().length() != 0) { + sb.append(prefix).append("\tAnchor: ").append(getAnchor()).append("\n"); + } + if (!getMasks().isEmpty()) { + sb.append(prefix).append("\tMasks: ").append(getMasks().size()).append("\n"); + } + if (getSolidWidth() != 0 && getSolidHeight() != 0) { + sb.append(prefix).append("\tBackground: ").append(String.format(Locale.US, "%dx%d %X\n", getSolidWidth(), getSolidHeight(), getSolidColor())); + } + if (!shapes.isEmpty()) { + sb.append(prefix).append("\tShapes:\n"); + for (Object shape : shapes) { + sb.append(prefix).append("\t\t").append(shape).append("\n"); + } + } + return sb.toString(); } } diff --git a/lottie/src/main/java/com/airbnb/lottie/model/LottieComposition.java b/lottie/src/main/java/com/airbnb/lottie/model/LottieComposition.java index 88795e6050..2cf6c66114 100644 --- a/lottie/src/main/java/com/airbnb/lottie/model/LottieComposition.java +++ b/lottie/src/main/java/com/airbnb/lottie/model/LottieComposition.java @@ -192,6 +192,15 @@ public float getScale() { return scale; } + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("LottieComposition:\n"); + for (Layer layer : layers) { + sb.append(layer.toString("\t")); + } + return sb.toString(); + } + private static final class FileCompositionLoader extends CompositionLoader { private final Resources res; diff --git a/lottie/src/main/java/com/airbnb/lottie/model/RectangleShape.java b/lottie/src/main/java/com/airbnb/lottie/model/RectangleShape.java index a2bf920735..e9db3d187f 100644 --- a/lottie/src/main/java/com/airbnb/lottie/model/RectangleShape.java +++ b/lottie/src/main/java/com/airbnb/lottie/model/RectangleShape.java @@ -4,9 +4,8 @@ import com.airbnb.lottie.L; import com.airbnb.lottie.animatable.AnimatableFloatValue; -import com.airbnb.lottie.animatable.AnimatablePointValue; import com.airbnb.lottie.animatable.AnimatablePathValue; -import com.airbnb.lottie.animatable.AnimationGroup; +import com.airbnb.lottie.animatable.AnimatablePointValue; import org.json.JSONException; import org.json.JSONObject; @@ -55,10 +54,6 @@ public AnimatablePathValue getPosition() { return position; } - public AnimationGroup createAnimation() { - return AnimationGroup.forAnimatableValues(getCornerRadius(), getSize(), getPosition()); - } - @Override public String toString() { return "RectangleShape{" + "cornerRadius=" + cornerRadius.getInitialValue() + diff --git a/lottie/src/main/java/com/airbnb/lottie/model/ShapeFill.java b/lottie/src/main/java/com/airbnb/lottie/model/ShapeFill.java index bd3bdda5f5..a251b03be7 100644 --- a/lottie/src/main/java/com/airbnb/lottie/model/ShapeFill.java +++ b/lottie/src/main/java/com/airbnb/lottie/model/ShapeFill.java @@ -5,7 +5,6 @@ import com.airbnb.lottie.L; import com.airbnb.lottie.animatable.AnimatableColorValue; import com.airbnb.lottie.animatable.AnimatableIntegerValue; -import com.airbnb.lottie.animatable.AnimationGroup; import org.json.JSONException; import org.json.JSONObject; @@ -49,10 +48,6 @@ public AnimatableIntegerValue getOpacity() { return opacity; } - public AnimationGroup createAnimation() { - return AnimationGroup.forAnimatableValues(getColor(), getOpacity()); - } - @Override public String toString() { return "ShapeFill{" + "color=" + Integer.toHexString(color.getInitialValue()) + diff --git a/lottie/src/main/java/com/airbnb/lottie/model/ShapeGroup.java b/lottie/src/main/java/com/airbnb/lottie/model/ShapeGroup.java index 856ac7a41f..c1943f59ce 100644 --- a/lottie/src/main/java/com/airbnb/lottie/model/ShapeGroup.java +++ b/lottie/src/main/java/com/airbnb/lottie/model/ShapeGroup.java @@ -8,6 +8,7 @@ import org.json.JSONObject; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; @SuppressWarnings({"EmptyCatchBlock"}) @@ -85,6 +86,6 @@ public List getItems() { @Override public String toString() { - return "ShapeGroup{" + "name='" + name + '\'' + '}'; + return "ShapeGroup{" + "name='" + name + "\' Shapes: " + Arrays.toString(items.toArray()) + '}'; } } diff --git a/lottie/src/main/java/com/airbnb/lottie/model/ShapePath.java b/lottie/src/main/java/com/airbnb/lottie/model/ShapePath.java index 0da9ff7ed8..11ca3e4c8b 100644 --- a/lottie/src/main/java/com/airbnb/lottie/model/ShapePath.java +++ b/lottie/src/main/java/com/airbnb/lottie/model/ShapePath.java @@ -4,7 +4,6 @@ import com.airbnb.lottie.L; import com.airbnb.lottie.animatable.AnimatableShapeValue; -import com.airbnb.lottie.animatable.AnimationGroup; import org.json.JSONException; import org.json.JSONObject; @@ -51,10 +50,6 @@ public AnimatableShapeValue getShapePath() { return shapePath; } - public AnimationGroup createAnimation() { - return AnimationGroup.forAnimatableValues(getShapePath()); - } - @Override public String toString() { return "ShapePath{" + "name=" + name + diff --git a/lottie/src/main/java/com/airbnb/lottie/model/ShapeStroke.java b/lottie/src/main/java/com/airbnb/lottie/model/ShapeStroke.java index 816bf7e96c..cf828c7804 100644 --- a/lottie/src/main/java/com/airbnb/lottie/model/ShapeStroke.java +++ b/lottie/src/main/java/com/airbnb/lottie/model/ShapeStroke.java @@ -3,7 +3,6 @@ import com.airbnb.lottie.animatable.AnimatableColorValue; import com.airbnb.lottie.animatable.AnimatableFloatValue; import com.airbnb.lottie.animatable.AnimatableIntegerValue; -import com.airbnb.lottie.animatable.AnimationGroup; import org.json.JSONArray; import org.json.JSONException; @@ -99,13 +98,4 @@ public LineCapType getCapType() { public LineJoinType getJoinType() { return joinType; } - - public AnimationGroup createAnimation() { - if (getLineDashPattern().isEmpty()) { - return AnimationGroup.forAnimatableValues(getColor(), getOpacity(), getWidth()); - } else { - return AnimationGroup.forAnimatableValues(getColor(), getOpacity(), getWidth(), - getLineDashPattern().get(0), getLineDashPattern().get(1), getDashOffset()); - } - } } diff --git a/lottie/src/main/java/com/airbnb/lottie/model/ShapeTransform.java b/lottie/src/main/java/com/airbnb/lottie/model/ShapeTransform.java index b7a9bb90d8..b01ae38b56 100644 --- a/lottie/src/main/java/com/airbnb/lottie/model/ShapeTransform.java +++ b/lottie/src/main/java/com/airbnb/lottie/model/ShapeTransform.java @@ -5,11 +5,10 @@ import com.airbnb.lottie.L; import com.airbnb.lottie.animatable.AnimatableFloatValue; +import com.airbnb.lottie.animatable.AnimatableIntegerValue; import com.airbnb.lottie.animatable.AnimatablePathValue; import com.airbnb.lottie.animatable.AnimatablePointValue; -import com.airbnb.lottie.animatable.AnimatableIntegerValue; import com.airbnb.lottie.animatable.AnimatableScaleValue; -import com.airbnb.lottie.animatable.AnimationGroup; import org.json.JSONException; import org.json.JSONObject; @@ -94,10 +93,6 @@ public AnimatableIntegerValue getOpacity() { return opacity; } - public AnimationGroup createAnimation() { - return AnimationGroup.forAnimatableValues(getOpacity(), getPosition(), getAnchor(), getScale(), getRotation()); - } - @Override public String toString() { return "ShapeTransform{" + "anchor=" + anchor.toString() + diff --git a/lottie/src/main/java/com/airbnb/lottie/model/ShapeTrimPath.java b/lottie/src/main/java/com/airbnb/lottie/model/ShapeTrimPath.java index f20f49c6bc..f282e91a6e 100644 --- a/lottie/src/main/java/com/airbnb/lottie/model/ShapeTrimPath.java +++ b/lottie/src/main/java/com/airbnb/lottie/model/ShapeTrimPath.java @@ -1,7 +1,6 @@ package com.airbnb.lottie.model; import com.airbnb.lottie.animatable.AnimatableFloatValue; -import com.airbnb.lottie.animatable.AnimationGroup; import org.json.JSONException; import org.json.JSONObject; @@ -25,15 +24,11 @@ public AnimatableFloatValue getEnd() { return end; } - private AnimatableFloatValue getOffset() { - return offset; - } - public AnimatableFloatValue getStart() { return start; } - public AnimationGroup createAnimation() { - return AnimationGroup.forAnimatableValues(getStart(), getEnd(), getOffset()); + public AnimatableFloatValue getOffset() { + return offset; } } diff --git a/lottie/src/main/java/com/airbnb/lottie/utils/ScaleXY.java b/lottie/src/main/java/com/airbnb/lottie/utils/ScaleXY.java index e4483ed16f..9087d5af3c 100644 --- a/lottie/src/main/java/com/airbnb/lottie/utils/ScaleXY.java +++ b/lottie/src/main/java/com/airbnb/lottie/utils/ScaleXY.java @@ -18,9 +18,12 @@ public float getScaleY() { return scaleY; } + public boolean isDefault() { + return scaleX == 1f && scaleY == 1f; + } + @Override public String toString() { - return "ScaleXY{" + "scale=" + getScaleX() + "x" + getScaleY() + '}'; - + return getScaleX() + "x" + getScaleY(); } }