summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsubhransu sekhar mohanty <smohantty@subhransus-MacBook-Air.local>2017-12-15 19:39:05 +0900
committersubhransu mohanty <sub.mohanty@samsung.com>2018-04-12 13:51:52 +0900
commit29958de5a82cced0d80fe99b2ac7171df70d7ed8 (patch)
tree693e2bb0b0fc939a14f81badff09fbab5bfc394a
parent4e78e7b4f2ac88ff04de5fd05abf0210e63ef853 (diff)
lottie: Add a test lottie file
-rw-r--r--ssg/example/LightBulb.json1
-rw-r--r--ssg/example/lottie1.json29
-rw-r--r--ssg/example/main.cpp6
-rw-r--r--ssg/include/sgpoint.h13
-rw-r--r--ssg/meson.build2
-rw-r--r--ssg/src/lottie/jsontest.cpp707
-rw-r--r--ssg/src/lottie/lottiecomposition.h217
-rw-r--r--ssg/src/lottie/lottiemodel.cpp52
-rw-r--r--ssg/src/lottie/lottiemodel.h309
-rw-r--r--ssg/src/lottie/lottieparser.cpp1186
-rw-r--r--ssg/src/lottie/meson.build3
11 files changed, 1529 insertions, 996 deletions
diff --git a/ssg/example/LightBulb.json b/ssg/example/LightBulb.json
new file mode 100644
index 0000000000..9797b14a90
--- /dev/null
+++ b/ssg/example/LightBulb.json
@@ -0,0 +1 @@
{"assets":[],"v":"4.3.1","ddd":0,"layers":[{"ddd":0,"ind":0,"ty":4,"nm":"IL","parent":5,"ks":{"o":{"k":100},"r":{"k":0},"p":{"k":[15,15,0]},"a":{"k":[15,15,0]},"s":{"k":[100,100,100]}},"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","closed":false,"ks":{"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[12.38,24.417],[18.715,24.417]]}},"nm":"Path 1"},{"ty":"st","fillEnabled":true,"c":{"k":[0.26,0.24,0.25,1]},"o":{"k":100},"w":{"k":1},"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":"Group 2"}],"bounds":{"l":11,"t":23,"b":25,"r":20},"ip":14.5,"op":150,"st":23.5,"bm":0,"sr":1},{"ddd":0,"ind":1,"ty":4,"nm":"F","ks":{"o":{"k":100},"r":{"k":0},"p":{"k":[19.4,19.8,0]},"a":{"k":[15,15,0]},"s":{"k":[100,100,100]}},"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","closed":false,"ks":{"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-0.896,-1.738],[0.896,0.279],[0.094,1.357],[-0.892,2.698],[-0.86,26.758]]}},"nm":"Path 1"},{"ty":"st","fillEnabled":true,"c":{"k":[0.26,0.24,0.25,1]},"o":{"k":100},"w":{"k":1},"lc":2,"lj":2,"nm":"Stroke 1"},{"ty":"tm","s":{"k":[{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"n":["0p1_1_0p167_0p167"],"t":23.5,"s":[61.6],"e":[0]},{"t":36.5}],"ix":1},"e":{"k":[{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"n":["0p1_1_0p167_0p167"],"t":23.5,"s":[58.5],"e":[26]},{"t":36.5}],"ix":2},"o":{"k":0,"ix":3},"m":1,"ix":3,"nm":"Trim Paths 1"},{"ty":"tr","p":{"k":[16.423,12.685],"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 4"}],"bounds":{"l":15,"t":10,"b":40,"r":18},"ip":23.5,"op":150,"st":0,"bm":0,"sr":1},{"ddd":0,"ind":2,"ty":4,"nm":"H6","parent":5,"ks":{"o":{"k":100},"r":{"k":230},"p":{"k":[15.434,13.798,0]},"a":{"k":[15.434,13.798,0]},"s":{"k":[100,100,100]}},"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"k":[{"i":{"x":[0.1,0.1],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"n":["0p1_1_0p167_0p167","0p1_1_0p167_0p167"],"t":42.5,"s":[16.462,16.462],"e":[28.562,28.562],"__fnct":[null,null]},{"t":55.5}]},"p":{"k":[0,0]},"nm":"Ellipse Path 1","closed":true},{"d":1,"ty":"el","s":{"k":[{"i":{"x":[0.1,0.1],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"n":["0p1_1_0p167_0p167","0p1_1_0p167_0p167"],"t":42.5,"s":[16.462,16.462],"e":[28.562,28.562],"__fnct":[null,null]},{"t":55.5}]},"p":{"k":[0,0]},"nm":"Ellipse Path 1","closed":true},{"ty":"tm","s":{"k":65,"ix":1},"e":{"k":75,"ix":2},"o":{"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1"},{"ty":"st","fillEnabled":true,"c":{"k":[0.26,0.24,0.25,1]},"o":{"k":100},"w":{"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":46,"s":[1],"e":[0],"__fnct":[null]},{"t":55.5}]},"lc":2,"lj":2,"d":[{"n":"d","nm":"dash","v":{"k":0}},{"n":"g","nm":"gap","v":{"k":2.5}},{"n":"o","nm":"offset","v":{"k":0}}],"nm":"Stroke 1"},{"ty":"tr","p":{"k":[15.45,13.746],"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":"Ellipse 1"}],"bounds":{"l":-6,"t":-7,"b":35,"r":37},"ip":42.5,"op":62.5,"st":-27,"bm":0,"sr":1},{"ddd":0,"ind":3,"ty":4,"nm":"H5","parent":5,"ks":{"o":{"k":100},"r":{"k":138},"p":{"k":[15.434,13.798,0]},"a":{"k":[15.434,13.798,0]},"s":{"k":[100,100,100]}},"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"k":[{"i":{"x":[0.1,0.1],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"n":["0p1_1_0p167_0p167","0p1_1_0p167_0p167"],"t":45,"s":[16.462,16.462],"e":[28.562,28.562],"__fnct":[null,null]},{"t":58}]},"p":{"k":[0,0]},"nm":"Ellipse Path 1","closed":true},{"d":1,"ty":"el","s":{"k":[{"i":{"x":[0.1,0.1],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"n":["0p1_1_0p167_0p167","0p1_1_0p167_0p167"],"t":45,"s":[16.462,16.462],"e":[28.562,28.562],"__fnct":[null,null]},{"t":58}]},"p":{"k":[0,0]},"nm":"Ellipse Path 1","closed":true},{"ty":"tm","s":{"k":60,"ix":1},"e":{"k":80,"ix":2},"o":{"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1"},{"ty":"st","fillEnabled":true,"c":{"k":[0.26,0.24,0.25,1]},"o":{"k":100},"w":{"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":48.5,"s":[1],"e":[0],"__fnct":[null]},{"t":58}]},"lc":2,"lj":2,"d":[{"n":"d","nm":"dash","v":{"k":0}},{"n":"g","nm":"gap","v":{"k":2}},{"n":"o","nm":"offset","v":{"k":0}}],"nm":"Stroke 1"},{"ty":"tr","p":{"k":[15.45,13.746],"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":"Ellipse 1"}],"bounds":{"l":-6,"t":-7,"b":35,"r":37},"ip":45,"op":62.5,"st":-24.5,"bm":0,"sr":1},{"ddd":0,"ind":4,"ty":4,"nm":"H7","parent":5,"ks":{"o":{"k":100},"r":{"k":0},"p":{"k":[15.434,13.798,0]},"a":{"k":[15.434,13.798,0]},"s":{"k":[100,100,100]}},"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"k":[{"i":{"x":[0.1,0.1],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"n":["0p1_1_0p167_0p167","0p1_1_0p167_0p167"],"t":42.5,"s":[16.462,16.462],"e":[28.562,28.562],"__fnct":[null,null]},{"t":55.5}]},"p":{"k":[0,0]},"nm":"Ellipse Path 1","closed":true},{"d":1,"ty":"el","s":{"k":[{"i":{"x":[0.1,0.1],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"n":["0p1_1_0p167_0p167","0p1_1_0p167_0p167"],"t":42.5,"s":[16.462,16.462],"e":[28.562,28.562],"__fnct":[null,null]},{"t":55.5}]},"p":{"k":[0,0]},"nm":"Ellipse Path 1","closed":true},{"ty":"tm","s":{"k":60.7,"ix":1},"e":{"k":85,"ix":2},"o":{"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1"},{"ty":"st","fillEnabled":true,"c":{"k":[0.26,0.24,0.25,1]},"o":{"k":100},"w":{"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":46,"s":[1],"e":[0],"__fnct":[null]},{"t":55.5}]},"lc":2,"lj":2,"d":[{"n":"d","nm":"dash","v":{"k":0}},{"n":"g","nm":"gap","v":{"k":2}},{"n":"o","nm":"offset","v":{"k":0}}],"nm":"Stroke 1"},{"ty":"tr","p":{"k":[15.45,13.746],"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":"Ellipse 1"}],"bounds":{"l":-6,"t":-7,"b":35,"r":37},"ip":42.5,"op":62.5,"st":-27,"bm":0,"sr":1},{"ddd":0,"ind":5,"ty":4,"nm":"OU","ks":{"o":{"k":100},"r":{"k":0},"p":{"k":[{"i":{"x":0,"y":0},"o":{"x":0.167,"y":0.167},"n":"0_0_0p167_0p167","t":14.5,"s":[19.834,33.477,0],"e":[19.834,33.477,0],"to":[0,0,0],"ti":[0,0,0]},{"t":29.5}]},"a":{"k":[15.434,28.677,0]},"s":{"k":[{"i":{"x":[0,0,0],"y":[1,1,0]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"n":["0_1_0p167_0p167","0_1_0p167_0p167","0_0_0p167_0p167"],"t":14.5,"s":[16,16,100],"e":[100,100,100]},{"t":29.5}]}},"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","closed":false,"ks":{"k":[{"i":{"x":0,"y":1},"o":{"x":0.167,"y":0.167},"n":"0_1_0p167_0p167","t":14.5,"s":[{"i":[[0,0],[0.263,0.089],[0.733,0.619]],"o":[[-0.275,-0.06],[-0.93,-0.312],[0,0]],"v":[[1.66,0.82],[0.853,0.596],[-1.66,-0.82]]}],"e":[{"i":[[0,0],[0.263,0.089],[0.733,0.619]],"o":[[-0.275,-0.06],[-0.93,-0.312],[0,0]],"v":[[1.66,0.82],[0.853,0.596],[-1.66,-0.82]]}]},{"t":29.5}]},"nm":"Path 1"},{"ty":"st","fillEnabled":true,"c":{"k":[0.26,0.24,0.25,1]},"o":{"k":100},"w":{"k":1},"lc":2,"lj":2,"nm":"Stroke 1"},{"ty":"tr","p":{"k":[12.114,20.529],"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":"gr","it":[{"ind":0,"ty":"sh","closed":true,"ks":{"k":{"i":[[0,2.418],[4.269,0],[0,-4.27],[-1.817,-1.412],[0,-1.088],[0,0],[-0.475,-0.48],[-0.675,0],[0,0],[-0.443,0.478],[0,0.657],[0,0],[-0.875,0.712]],"o":[[0,-4.27],[-4.269,0],[0,2.479],[0.859,0.669],[0,0],[0,0.675],[0.475,0.48],[0,0],[0.652,0],[0.448,-0.481],[0,0],[0,-1.128],[1.739,-1.415]],"v":[[7.73,-3.574],[0,-11.305],[-7.73,-3.574],[-4.743,2.52],[-3.237,5.238],[-3.237,8.747],[-2.494,10.553],[-0.694,11.305],[0.86,11.305],[2.582,10.553],[3.28,8.779],[3.28,5.217],[4.878,2.412]]}},"nm":"Path 1"},{"ty":"st","fillEnabled":true,"c":{"k":[0.26,0.24,0.25,1]},"o":{"k":100},"w":{"k":1},"lc":2,"lj":2,"nm":"Stroke 2"},{"ty":"st","fillEnabled":true,"c":{"k":[0.26,0.24,0.25,1]},"o":{"k":100},"w":{"k":1},"lc":2,"lj":2,"nm":"Stroke 1"},{"ty":"tr","p":{"k":[15.434,17.373],"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 5"}],"bounds":{"l":6,"t":4,"b":31,"r":25},"ip":14.5,"op":150,"st":0,"bm":0,"sr":1},{"ddd":0,"ind":6,"ty":4,"nm":"H3","parent":5,"ks":{"o":{"k":100},"r":{"k":0},"p":{"k":[15.434,13.798,0]},"a":{"k":[15.434,13.798,0]},"s":{"k":[{"i":{"x":[0.19,0.19,0.19],"y":[1,1,0.19]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"n":["0p19_1_0p167_0p167","0p19_1_0p167_0p167","0p19_0p19_0p167_0p167"],"t":52,"s":[71.2,71.2,100],"e":[100,100,100]},{"t":78}]}},"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","closed":false,"ks":{"k":{"i":[[0,0],[-3.194,4.512]],"o":[[-4.016,-3.803],[0,0]],"v":[[2.327,7.337],[0.867,-7.337]]}},"nm":"Path 1"},{"ty":"st","fillEnabled":true,"c":{"k":[0.26,0.24,0.25,1]},"o":{"k":100},"w":{"k":1},"lc":2,"lj":2,"nm":"Stroke 1"},{"ty":"tr","p":{"k":[5.377,14.639],"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 6"}],"bounds":{"l":3,"t":6,"b":23,"r":9},"ip":51.5,"op":150,"st":34,"bm":0,"sr":1},{"ddd":0,"ind":7,"ty":4,"nm":"H2","parent":5,"ks":{"o":{"k":100},"r":{"k":0},"p":{"k":[15,15,0]},"a":{"k":[15,15,0]},"s":{"k":[{"i":{"x":[0.19,0.19,0.19],"y":[1,1,0.19]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"n":["0p19_1_0p167_0p167","0p19_1_0p167_0p167","0p19_0p19_0p167_0p167"],"t":48.5,"s":[73,73,100],"e":[100,100,100]},{"t":65.5}]}},"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","closed":false,"ks":{"k":{"i":[[0,0],[-3.358,-5.293],[-0.145,-1.74]],"o":[[5.221,-3.587],[0.936,1.475],[0,0]],"v":[[-8.792,-2.382],[7.141,1.064],[8.792,5.969]]}},"nm":"Path 1"},{"ty":"st","fillEnabled":true,"c":{"k":[0.26,0.24,0.25,1]},"o":{"k":100},"w":{"k":1},"lc":2,"lj":2,"nm":"Stroke 1"},{"ty":"tr","p":{"k":[17.856,6.903],"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 7"}],"bounds":{"l":8,"t":2,"b":14,"r":28},"ip":48,"op":150,"st":30.5,"bm":0,"sr":1},{"ddd":0,"ind":8,"ty":4,"nm":"H1","parent":5,"ks":{"o":{"k":100},"r":{"k":0},"p":{"k":[15.434,13.798,0]},"a":{"k":[15.434,13.798,0]},"s":{"k":[{"i":{"x":[0.19,0.19,0.19],"y":[1,1,0.19]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"n":["0p19_1_0p167_0p167","0p19_1_0p167_0p167","0p19_0p19_0p167_0p167"],"t":59,"s":[71.6,71.6,100],"e":[100,100,100]},{"t":84}]}},"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","closed":false,"ks":{"k":{"i":[[0,0],[1.417,-1.416]],"o":[[-0.502,1.939],[0,0]],"v":[[1.471,-2.577],[-1.471,2.577]]}},"nm":"Path 1"},{"ty":"st","fillEnabled":true,"c":{"k":[0.26,0.24,0.25,1]},"o":{"k":100},"w":{"k":1},"lc":2,"lj":2,"nm":"Stroke 1"},{"ty":"tr","p":{"k":[24.859,19.181],"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 8"}],"bounds":{"l":22,"t":16,"b":23,"r":27},"ip":58.5,"op":150,"st":41,"bm":0,"sr":1},{"ddd":0,"ind":9,"ty":4,"nm":"BG","ks":{"o":{"k":100},"r":{"k":0},"p":{"k":[{"i":{"x":0,"y":0},"o":{"x":0.167,"y":0.167},"n":"0_0_0p167_0p167","t":25,"s":[19.4,19.8,0],"e":[19.4,19.8,0],"to":[0,0,0],"ti":[0,0,0]},{"t":29}]},"a":{"k":[15,15,0]},"s":{"k":[100,100,100]}},"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","closed":true,"ks":{"k":[{"i":{"x":0,"y":1},"o":{"x":0.167,"y":0.167},"n":"0_1_0p167_0p167","t":25,"s":[{"i":[[0,-0.831],[1.196,0],[0,0.831],[-1.196,0]],"o":[[0,0.831],[-1.196,0],[0,-0.831],[1.196,0]],"v":[[2.166,8.157],[0,9.662],[-2.166,8.157],[0,6.652]]}],"e":[{"i":[[0,-3.265],[3.265,0],[0,3.265],[-3.264,0]],"o":[[0,3.265],[-3.264,0],[0,-3.265],[3.265,0]],"v":[[5.911,0],[-0.001,5.912],[-5.911,0],[-0.001,-5.912]]}]},{"t":29}]},"nm":"Path 1"},{"ty":"fl","fillEnabled":true,"c":{"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":37.5,"s":[0.8,0.8,0.8,1],"e":[1,0.68,0,1]},{"t":43.5}]},"o":{"k":100},"nm":"Fill 1"},{"ty":"tr","p":{"k":[15.434,13.798],"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 9"}],"bounds":{"l":9,"t":7,"b":24,"r":22},"ip":25,"op":150,"st":-2,"bm":0,"sr":1}],"ip":0,"op":150,"fr":30,"w":40,"h":40} \ No newline at end of file
diff --git a/ssg/example/lottie1.json b/ssg/example/lottie1.json
index e69de29bb2..1e78be5115 100644
--- a/ssg/example/lottie1.json
+++ b/ssg/example/lottie1.json
@@ -0,0 +1,29 @@
1"ks":
2 {
3 "k":{
4 "i":[[0,0],[0,0]],
5 "o":[[0,0],[0,0]],
6 "v":[[12.38,24.417],[18.715,24.417]]
7 }
8 },
9
10"ks":{
11 "o":{"k":100},
12 "r":{"k":0},
13 "p":{"k":[15,15,0]},
14 "a":{"k":[15,15,0]},
15 "s":{"k":[100,100,100]}
16 }
17
18"c":{
19 "k":[
20 {
21 "i":{"x":[0.833],"y":[0.833]},
22 "o":{"x":[0.167],"y":[0.167]},
23 "n":["0p833_0p833_0p167_0p167"],
24 "t":37.5,
25 "s":[0.8,0.8,0.8,1],
26 "e":[1,0.68,0,1]
27 },
28 {"t":43.5}
29 ]}
diff --git a/ssg/example/main.cpp b/ssg/example/main.cpp
index 938f4dba0c..84f8db0815 100644
--- a/ssg/example/main.cpp
+++ b/ssg/example/main.cpp
@@ -11,19 +11,21 @@ using namespace std;
11#include<fstream> 11#include<fstream>
12#include<sstream> 12#include<sstream>
13 13
14
15int main() 14int main()
16{ 15{
16 std::string filepath = DEMO_DIR;
17 filepath += "LightBulb.json";
17 initialize(GuaranteedLogger(), "/tmp/", "ssglog", 1); 18 initialize(GuaranteedLogger(), "/tmp/", "ssglog", 1);
18 set_log_level(LogLevel::INFO); 19 set_log_level(LogLevel::INFO);
19 //SGJson json; 20 //SGJson json;
20 std::ifstream file; 21 std::ifstream file;
21 file.open("/home/subhransu/openSourceEfl/ssgrepo/ssg/build/example/LightBulb.json"); 22 file.open(filepath);
22 std::stringstream buffer; 23 std::stringstream buffer;
23 buffer << file.rdbuf(); 24 buffer << file.rdbuf();
24 //std::cout <<"file size = "<< buffer.str().size()<<std::endl; 25 //std::cout <<"file size = "<< buffer.str().size()<<std::endl;
25 //std::cout << buffer.str().data(); 26 //std::cout << buffer.str().data();
26 SGJson json(buffer.str().data()); 27 SGJson json(buffer.str().data());
27 file.close(); 28 file.close();
29 std::cout<<"sizeof float :"<<sizeof(float)<<" size of double :"<<sizeof(double)<<std::endl;
28 return 0; 30 return 0;
29} 31}
diff --git a/ssg/include/sgpoint.h b/ssg/include/sgpoint.h
index 90a9205dc3..3dec5677aa 100644
--- a/ssg/include/sgpoint.h
+++ b/ssg/include/sgpoint.h
@@ -7,16 +7,19 @@ class SG_EXPORT SGPointF
7{ 7{
8public: 8public:
9 constexpr inline SGPointF() noexcept :mx(0), my(0){} 9 constexpr inline SGPointF() noexcept :mx(0), my(0){}
10 constexpr inline SGPointF(double x, double y) noexcept :mx(x), my(y){} 10 constexpr inline SGPointF(float x, float y) noexcept :mx(x), my(y){}
11 constexpr inline double x() const noexcept {return mx;} 11 constexpr inline float x() const noexcept {return mx;}
12 constexpr inline double y() const noexcept {return my;} 12 constexpr inline float y() const noexcept {return my;}
13 inline void setX(float x) {mx = x;} 13 inline void setX(float x) {mx = x;}
14 inline void setY(float y) {my = y;} 14 inline void setY(float y) {my = y;}
15 inline SGPointF &operator+=(const SGPointF &p) noexcept; 15 inline SGPointF &operator+=(const SGPointF &p) noexcept;
16 inline SGPointF &operator-=(const SGPointF &p) noexcept; 16 inline SGPointF &operator-=(const SGPointF &p) noexcept;
17 friend const SGPointF operator+(const SGPointF & p1, const SGPointF & p2) {
18 return SGPointF(p1.mx + p2.mx , p1.my + p2.my);
19 }
17private: 20private:
18 double mx; 21 float mx;
19 double my; 22 float my;
20}; 23};
21 24
22inline SGPointF &SGPointF::operator+=(const SGPointF &p) noexcept 25inline SGPointF &SGPointF::operator+=(const SGPointF &p) noexcept
diff --git a/ssg/meson.build b/ssg/meson.build
index 0bf4923e52..1ec11197c9 100644
--- a/ssg/meson.build
+++ b/ssg/meson.build
@@ -3,7 +3,7 @@ project('scenegraph library',
3 license : 'MIT', 3 license : 'MIT',
4 default_options : ['libdir=lib/ssg', 'c_std=c11', 'cpp_std=c++11']) 4 default_options : ['libdir=lib/ssg', 'c_std=c11', 'cpp_std=c++11'])
5ssg_lib_version = '0.0.1' 5ssg_lib_version = '0.0.1'
6 6add_global_arguments('-DDEMO_DIR="@0@/example/"'.format(meson.current_source_dir()), language : 'cpp')
7compiler_flags = ['-Wall','-std=c++11'] 7compiler_flags = ['-Wall','-std=c++11']
8if (build_machine.system() == 'linux') 8if (build_machine.system() == 'linux')
9 compiler_flags += ['-pthread'] 9 compiler_flags += ['-pthread']
diff --git a/ssg/src/lottie/jsontest.cpp b/ssg/src/lottie/jsontest.cpp
deleted file mode 100644
index 02f2fb0e18..0000000000
--- a/ssg/src/lottie/jsontest.cpp
+++ /dev/null
@@ -1,707 +0,0 @@
1#include"sgjson.h"
2#include "rapidjson/document.h"
3#include <iostream>
4#include "lottiecomposition.h"
5#include "lottiemodel.h"
6
7RAPIDJSON_DIAG_PUSH
8#ifdef __GNUC__
9RAPIDJSON_DIAG_OFF(effc++)
10#endif
11
12// This example demonstrates JSON token-by-token parsing with an API that is
13// more direct; you don't need to design your logic around a handler object and
14// callbacks. Instead, you retrieve values from the JSON stream by calling
15// GetInt(), GetDouble(), GetString() and GetBool(), traverse into structures
16// by calling EnterObject() and EnterArray(), and skip over unwanted data by
17// calling SkipValue(). When you know your JSON's structure, this can be quite
18// convenient.
19//
20// If you aren't sure of what's next in the JSON data, you can use PeekType() and
21// PeekValue() to look ahead to the next object before reading it.
22//
23// If you call the wrong retrieval method--e.g. GetInt when the next JSON token is
24// not an int, EnterObject or EnterArray when there isn't actually an object or array
25// to read--the stream parsing will end immediately and no more data will be delivered.
26//
27// After calling EnterObject, you retrieve keys via NextObjectKey() and values via
28// the normal getters. When NextObjectKey() returns null, you have exited the
29// object, or you can call SkipObject() to skip to the end of the object
30// immediately. If you fetch the entire object (i.e. NextObjectKey() returned null),
31// you should not call SkipObject().
32//
33// After calling EnterArray(), you must alternate between calling NextArrayValue()
34// to see if the array has more data, and then retrieving values via the normal
35// getters. You can call SkipArray() to skip to the end of the array immediately.
36// If you fetch the entire array (i.e. NextArrayValue() returned null),
37// you should not call SkipArray().
38//
39// This parser uses in-situ strings, so the JSON buffer will be altered during the
40// parse.
41
42using namespace rapidjson;
43
44
45class LookaheadParserHandler {
46public:
47 bool Null() { st_ = kHasNull; v_.SetNull(); return true; }
48 bool Bool(bool b) { st_ = kHasBool; v_.SetBool(b); return true; }
49 bool Int(int i) { st_ = kHasNumber; v_.SetInt(i); return true; }
50 bool Uint(unsigned u) { st_ = kHasNumber; v_.SetUint(u); return true; }
51 bool Int64(int64_t i) { st_ = kHasNumber; v_.SetInt64(i); return true; }
52 bool Uint64(uint64_t u) { st_ = kHasNumber; v_.SetUint64(u); return true; }
53 bool Double(double d) { st_ = kHasNumber; v_.SetDouble(d); return true; }
54 bool RawNumber(const char*, SizeType, bool) { return false; }
55 bool String(const char* str, SizeType length, bool) { st_ = kHasString; v_.SetString(str, length); return true; }
56 bool StartObject() { st_ = kEnteringObject; return true; }
57 bool Key(const char* str, SizeType length, bool) { st_ = kHasKey; v_.SetString(str, length); return true; }
58 bool EndObject(SizeType) { st_ = kExitingObject; return true; }
59 bool StartArray() { st_ = kEnteringArray; return true; }
60 bool EndArray(SizeType) { st_ = kExitingArray; return true; }
61
62protected:
63 LookaheadParserHandler(char* str);
64 void ParseNext();
65
66protected:
67 enum LookaheadParsingState {
68 kInit,
69 kError,
70 kHasNull,
71 kHasBool,
72 kHasNumber,
73 kHasString,
74 kHasKey,
75 kEnteringObject,
76 kExitingObject,
77 kEnteringArray,
78 kExitingArray
79 };
80
81 Value v_;
82 LookaheadParsingState st_;
83 Reader r_;
84 InsituStringStream ss_;
85
86 static const int parseFlags = kParseDefaultFlags | kParseInsituFlag;
87};
88
89LookaheadParserHandler::LookaheadParserHandler(char* str) : v_(), st_(kInit), r_(), ss_(str) {
90 r_.IterativeParseInit();
91 ParseNext();
92}
93
94void LookaheadParserHandler::ParseNext() {
95 if (r_.HasParseError()) {
96 st_ = kError;
97 return;
98 }
99
100 if (!r_.IterativeParseNext<parseFlags>(ss_, *this)) {
101 sgCritical<<"Lottie file parsing error";
102 RAPIDJSON_ASSERT(0);
103 }
104}
105
106class LottieParser : protected LookaheadParserHandler {
107public:
108 LottieParser(char* str) : LookaheadParserHandler(str) {}
109
110 bool EnterObject();
111 bool EnterArray();
112 const char* NextObjectKey();
113 bool NextArrayValue();
114 int GetInt();
115 double GetDouble();
116 const char* GetString();
117 bool GetBool();
118 void GetNull();
119
120 void SkipObject();
121 void SkipArray();
122 void SkipValue();
123 Value* PeekValue();
124 int PeekType(); // returns a rapidjson::Type, or -1 for no value (at end of object/array)
125
126 bool IsValid() { return st_ != kError; }
127
128 void Skip(const char *key);
129 SGRect getRect();
130 LottieBlendMode getBlendMode();
131
132 void parseGroupItem(FINode *group);
133 void parseEllipse(FINode *parent);
134 LottieComposition *parseComposition();
135 void parseLayers(LottieComposition *comp);
136 LottieLayer *parseLayer();
137 void parseShapesAttr(LottieLayer *layer);
138 void parseObject(LottieGroupObj *parent);
139 LottieObject* parseObjectTypeAttr();
140 LottieObject *parseGroupObject();
141 LottieObject *parseRectObject();
142 LottieObject *parseEllipseObject();
143
144 void parseArrayValue(SGPointF &pt);
145 void parseArrayValue(float &val);
146 template<typename T>
147 void parseKeyFrame(LottieProperty<T,true> &obj);
148 template<typename T>
149 void parseProperty(LottiePropertyHelper<T> &obj);
150
151protected:
152 void SkipOut(int depth);
153};
154
155bool LottieParser::EnterObject() {
156 if (st_ != kEnteringObject) {
157 st_ = kError;
158 RAPIDJSON_ASSERT(false);
159 return false;
160 }
161
162 ParseNext();
163 return true;
164}
165
166bool LottieParser::EnterArray() {
167 if (st_ != kEnteringArray) {
168 st_ = kError;
169 RAPIDJSON_ASSERT(false);
170 return false;
171 }
172
173 ParseNext();
174 return true;
175}
176
177const char* LottieParser::NextObjectKey() {
178 if (st_ == kHasKey) {
179 const char* result = v_.GetString();
180 ParseNext();
181 return result;
182 }
183
184 /* SPECIAL CASE
185 * The parser works with a prdefined rule that it will be only
186 * while (NextObjectKey()) for each object but in case of our nested group
187 * object we can call multiple time NextObjectKey() while exiting the object
188 * so ignore those and don't put parser in the error state.
189 * */
190 if (st_ == kExitingArray || st_ == kEnteringObject ) {
191 sgDebug<<"O: Exiting nested loop";
192 return 0;
193 }
194
195 if (st_ != kExitingObject) {
196 RAPIDJSON_ASSERT(false);
197 st_ = kError;
198 return 0;
199 }
200
201 ParseNext();
202 return 0;
203}
204
205bool LottieParser::NextArrayValue() {
206 if (st_ == kExitingArray) {
207 ParseNext();
208 return false;
209 }
210
211 /* SPECIAL CASE
212 * same as NextObjectKey()
213 */
214 if (st_ == kExitingObject || st_ == kEnteringArray) {
215 sgDebug<<"A: Exiting nested loop";
216 return 0;
217 }
218
219 if (st_ == kError || st_ == kHasKey) {
220 RAPIDJSON_ASSERT(false);
221 st_ = kError;
222 return false;
223 }
224
225 return true;
226}
227
228int LottieParser::GetInt() {
229 if (st_ != kHasNumber || !v_.IsInt()) {
230 st_ = kError;
231 RAPIDJSON_ASSERT(false);
232 return 0;
233 }
234
235 int result = v_.GetInt();
236 ParseNext();
237 return result;
238}
239
240double LottieParser::GetDouble() {
241 if (st_ != kHasNumber) {
242 st_ = kError;
243 RAPIDJSON_ASSERT(false);
244 return 0.;
245 }
246
247 double result = v_.GetDouble();
248 ParseNext();
249 return result;
250}
251
252bool LottieParser::GetBool() {
253 if (st_ != kHasBool) {
254 st_ = kError;
255 RAPIDJSON_ASSERT(false);
256 return false;
257 }
258
259 bool result = v_.GetBool();
260 ParseNext();
261 return result;
262}
263
264void LottieParser::GetNull() {
265 if (st_ != kHasNull) {
266 st_ = kError;
267 return;
268 }
269
270 ParseNext();
271}
272
273const char* LottieParser::GetString() {
274 if (st_ != kHasString) {
275 st_ = kError;
276 RAPIDJSON_ASSERT(false);
277 return 0;
278 }
279
280 const char* result = v_.GetString();
281 ParseNext();
282 return result;
283}
284
285void LottieParser::SkipOut(int depth) {
286 do {
287 if (st_ == kEnteringArray || st_ == kEnteringObject) {
288 ++depth;
289 }
290 else if (st_ == kExitingArray || st_ == kExitingObject) {
291 --depth;
292 }
293 else if (st_ == kError) {
294 RAPIDJSON_ASSERT(false);
295 return;
296 }
297
298 ParseNext();
299 }
300 while (depth > 0);
301}
302
303void LottieParser::SkipValue() {
304 SkipOut(0);
305}
306
307void LottieParser::SkipArray() {
308 SkipOut(1);
309}
310
311void LottieParser::SkipObject() {
312 SkipOut(1);
313}
314
315Value* LottieParser::PeekValue() {
316 if (st_ >= kHasNull && st_ <= kHasKey) {
317 return &v_;
318 }
319
320 return 0;
321}
322
323int LottieParser::PeekType() {
324 if (st_ >= kHasNull && st_ <= kHasKey) {
325 return v_.GetType();
326 }
327
328 if (st_ == kEnteringArray) {
329 return kArrayType;
330 }
331
332 if (st_ == kEnteringObject) {
333 return kObjectType;
334 }
335
336 return -1;
337}
338
339void LottieParser::Skip(const char *key)
340{
341 if (PeekType() == kArrayType) {
342// if(key)
343// sgWarning<<"Lottie ARRAY attribute not supported : "<<key;
344 EnterArray();
345 SkipArray();
346 } else if (PeekType() == kObjectType) {
347// if(key)
348// sgWarning<<"Lottie OBJECT attribute not supported : "<<key;
349 EnterObject();
350 SkipObject();
351 } else {
352 SkipValue();
353// if(key)
354// sgWarning<<"Lottie VALUE attribute not supported : "<<key;
355 }
356}
357
358LottieBlendMode
359LottieParser::getBlendMode()
360{
361 RAPIDJSON_ASSERT(PeekType() == kNumberType);
362 LottieBlendMode mode = LottieBlendMode::Normal;
363
364 switch (GetInt()) {
365 case 1:
366 mode = LottieBlendMode::Multiply;
367 break;
368 case 2:
369 mode = LottieBlendMode::Screen;
370 break;
371 case 3:
372 mode = LottieBlendMode::OverLay;
373 break;
374 default:
375 break;
376 }
377 return mode;
378}
379SGRect LottieParser::getRect()
380{
381 SGRect r;
382 RAPIDJSON_ASSERT(PeekType() == kObjectType);
383 EnterObject();
384 while (const char* key = NextObjectKey()) {
385 if (0 == strcmp(key, "l")) {
386 RAPIDJSON_ASSERT(PeekType() == kNumberType);
387 r.setLeft(GetInt());
388 } else if (0 == strcmp(key, "r")) {
389 RAPIDJSON_ASSERT(PeekType() == kNumberType);
390 r.setRight(GetInt());
391 } else if (0 == strcmp(key, "t")) {
392 RAPIDJSON_ASSERT(PeekType() == kNumberType);
393 r.setTop(GetInt());
394 } else if (0 == strcmp(key, "b")) {
395 RAPIDJSON_ASSERT(PeekType() == kNumberType);
396 r.setBottom(GetInt());
397 } else {
398 RAPIDJSON_ASSERT(false);
399 }
400 }
401 return r;
402}
403
404LottieComposition *LottieParser::parseComposition()
405{
406 RAPIDJSON_ASSERT(PeekType() == kObjectType);
407 EnterObject();
408 LottieComposition *comp = new LottieComposition();
409 while (const char* key = NextObjectKey()) {
410 if (0 == strcmp(key, "w")) {
411 RAPIDJSON_ASSERT(PeekType() == kNumberType);
412 comp->mBound.setWidth(GetInt());
413 } else if (0 == strcmp(key, "h")) {
414 RAPIDJSON_ASSERT(PeekType() == kNumberType);
415 comp->mBound.setHeight(GetInt());
416 } else if (0 == strcmp(key, "ip")) {
417 RAPIDJSON_ASSERT(PeekType() == kNumberType);
418 comp->mStartFrame = GetDouble();
419 } else if (0 == strcmp(key, "op")) {
420 RAPIDJSON_ASSERT(PeekType() == kNumberType);
421 comp->mEndFrame = GetDouble();
422 } else if (0 == strcmp(key, "fr")) {
423 RAPIDJSON_ASSERT(PeekType() == kNumberType);
424 comp->mFrameRate = GetDouble();
425 } else if (0 == strcmp(key, "layers")) {
426 parseLayers(comp);
427 }
428 else {
429 sgWarning<<"Composition Attribute Skipped : "<<key;
430 Skip(key);
431 }
432 }
433 return comp;
434}
435
436void LottieParser::parseLayers(LottieComposition *composition)
437{
438 RAPIDJSON_ASSERT(PeekType() == kArrayType);
439 EnterArray();
440 while (NextArrayValue()) {
441 auto layer = parseLayer();
442 composition->mChildren.push_back(layer);
443 }
444}
445
446/*
447 * https://github.com/airbnb/lottie-web/blob/master/docs/json/layers/shape.json
448 *
449 */
450LottieLayer * LottieParser::parseLayer()
451{
452 RAPIDJSON_ASSERT(PeekType() == kObjectType);
453 sgDebug<<"parse LAYER: S";
454 LottieLayer *layer = new LottieLayer();
455 EnterObject();
456 while (const char* key = NextObjectKey()) {
457 if (0 == strcmp(key, "ty")) { /* Type of layer: Shape. Value 4.*/
458 RAPIDJSON_ASSERT(PeekType() == kNumberType);
459 layer->mGroupType = GetInt();
460 } else if (0 == strcmp(key, "ind")) { /*Layer index in AE. Used for parenting and expressions.*/
461 RAPIDJSON_ASSERT(PeekType() == kNumberType);
462 layer->mId = GetInt();
463 } else if (0 == strcmp(key, "parent")) { /*Layer Parent. Uses "ind" of parent.*/
464 RAPIDJSON_ASSERT(PeekType() == kNumberType);
465 layer->mParentId = GetInt();
466 }else if (0 == strcmp(key, "sr")) { // "Layer Time Stretching"
467 RAPIDJSON_ASSERT(PeekType() == kNumberType);
468 layer->mTimeStreatch = GetDouble();
469 } else if (0 == strcmp(key, "ip")) {
470 RAPIDJSON_ASSERT(PeekType() == kNumberType);
471 layer->mStartFrame = GetDouble();
472 } else if (0 == strcmp(key, "op")) {
473 RAPIDJSON_ASSERT(PeekType() == kNumberType);
474 layer->mEndFrame = GetDouble();
475 } else if (0 == strcmp(key, "st")) {
476 RAPIDJSON_ASSERT(PeekType() == kNumberType);
477 layer->mStartTime = GetDouble();
478 } else if (0 == strcmp(key, "bounds")) {
479 layer->mBound = getRect();
480 } else if (0 == strcmp(key, "bm")) {
481 layer->mBlendMode = getBlendMode();
482 } else if (0 == strcmp(key, "shapes")) { /* Shape list of items */
483 parseShapesAttr(layer);
484 } else {
485 sgWarning<<"Layer Attribute Skipped : "<<key;
486 Skip(key);
487 }
488 }
489 sgDebug<<"parse LAYER: E";
490 return layer;
491}
492
493void LottieParser::parseShapesAttr(LottieLayer *layer)
494{
495 sgDebug<<"ENTER SHAPE ATTR";
496 RAPIDJSON_ASSERT(PeekType() == kArrayType);
497 EnterArray();
498 while (NextArrayValue()) {
499 parseObject(layer);
500 }
501 sgDebug<<"EXIT SHAPE ATTR";
502}
503
504LottieObject*
505LottieParser::parseObjectTypeAttr()
506{
507 RAPIDJSON_ASSERT(PeekType() == kStringType);
508 const char *type = GetString();
509 if (0 == strcmp(type, "gr")) {
510 return parseGroupObject();
511 } else if (0 == strcmp(type, "rc")) {
512 return parseRectObject();
513 } else if (0 == strcmp(type, "el")) {
514 return parseEllipseObject();
515 } else {
516 sgDebug<<"The Object Type not yet handled = "<< type;
517 return nullptr;
518 }
519}
520
521void
522LottieParser::parseObject(LottieGroupObj *parent)
523{
524 RAPIDJSON_ASSERT(PeekType() == kObjectType);
525 EnterObject();
526 while (const char* key = NextObjectKey()) {
527 if (0 == strcmp(key, "ty")) {
528 auto child = parseObjectTypeAttr();
529 if (child)
530 parent->mChildren.push_back(child);
531 } else {
532 Skip(key);
533 }
534 }
535}
536
537LottieObject *
538LottieParser::parseGroupObject()
539{
540 LottieGroupObj *group = new LottieGroupObj();
541 sgDebug<<"ENTER GROUP item";
542 while (const char* key = NextObjectKey()) {
543 if (0 == strcmp(key, "it")) {
544 sgDebug<<"ENTER IT attribute";
545 RAPIDJSON_ASSERT(PeekType() == kArrayType);
546 EnterArray();
547 while (NextArrayValue()) {
548 RAPIDJSON_ASSERT(PeekType() == kObjectType);
549 parseObject(group);
550 }
551 sgDebug<<"EXIT IT attribute";
552 } else {
553 Skip(key);
554 }
555 }
556 sgDebug<<"EXIT GROUP item";
557 return group;
558}
559
560/*
561 * https://github.com/airbnb/lottie-web/blob/master/docs/json/shapes/rect.json
562 */
563LottieObject *
564LottieParser::parseRectObject()
565{
566 LottiePropertyHelper<float> roundness(0);
567 while (const char* key = NextObjectKey()) {
568 if (0 == strcmp(key, "p")) {
569 Skip(key);
570 } else if (0 == strcmp(key, "s")) {
571 Skip(key);
572 } else if (0 == strcmp(key, "r")) {
573 parseProperty(roundness);
574 } else if (0 == strcmp(key, "d")) {
575 Skip(key);
576 } else {
577 Skip(key);
578 }
579 }
580 return nullptr;
581}
582
583LottieObject *
584LottieParser::parseEllipseObject()
585{
586 sgDebug<<"parse EL item START:";
587 LottiePropertyHelper<SGPointF> pos = LottiePropertyHelper<SGPointF>(SGPointF());
588 LottiePropertyHelper<SGPointF> size = LottiePropertyHelper<SGPointF>(SGPointF());
589 while (const char* key = NextObjectKey()) {
590 if (0 == strcmp(key, "p")) {
591 parseProperty(pos);
592 } else if (0 == strcmp(key, "s")) {
593 parseProperty(size);
594 } else {
595 Skip(key);
596 }
597 }
598
599 if (!pos.mAnimation && !size.mAnimation) {
600 auto obj = new LottieEllipseObject<false, false>();
601 obj->mPos = std::move(pos.mProperty.mValue);
602 obj->mSize = std::move(size.mProperty.mValue);
603 return obj;
604 } else {
605 auto obj = new LottieEllipseObject<true, true>();
606 obj->mPos = std::move(pos.mProperty);
607 obj->mSize = std::move(size.mProperty);
608 return obj;
609 }
610 sgDebug<<"parse EL item END:";
611 return nullptr;
612}
613
614
615void LottieParser::parseArrayValue(SGPointF &pt)
616{
617 float val[10];
618 int i=0;
619 while (NextArrayValue()) {
620 val[i++] = GetDouble();
621 }
622 sgDebug<<"Value parsed as point / size"<<i;
623
624 pt.setX(val[0]);
625 pt.setY(val[1]);
626}
627
628void LottieParser::parseArrayValue(float &val)
629{
630 sgDebug<<"Value parsed as single val";
631 val = GetDouble();
632}
633
634template<typename T>
635void LottieParser::parseKeyFrame(LottieProperty<T,true> &obj)
636{
637 EnterObject();
638 LottieKeyFrame<T> keyframe;
639 while (const char* key = NextObjectKey()) {
640 if (0 == strcmp(key, "i")) {
641 sgDebug<<"i";
642 Skip(key);
643 } else if (0 == strcmp(key, "o")) {
644 sgDebug<<"o";
645 Skip(key);
646 } else if (0 == strcmp(key, "n")) {
647 sgDebug<<"n";
648 Skip(key);
649 } else if (0 == strcmp(key, "t")) {
650 keyframe.mStartFrame = GetDouble();
651 } else if (0 == strcmp(key, "s")) {
652 if (PeekType() == kArrayType)
653 EnterArray();
654 parseArrayValue(keyframe.mStartValue);
655 } else if (0 == strcmp(key, "e")) {
656 if (PeekType() == kArrayType)
657 EnterArray();
658 parseArrayValue(keyframe.mEndValue);
659 } else {
660 Skip(key);
661 }
662 }
663
664 if (!obj.mKeyFrames.empty()) {
665 // update the endFrame value of current keyframe
666 obj.mKeyFrames.back().mEndFrame = keyframe.mStartFrame;
667 }
668 obj.mKeyFrames.push_back(keyframe);
669}
670
671template<typename T>
672void LottieParser::parseProperty(LottiePropertyHelper<T> &obj)
673{
674 EnterObject();
675 while (const char* key = NextObjectKey()) {
676 if (0 == strcmp(key, "a")) {
677 obj.mAnimation = GetBool();
678 sgDebug<<"animation property :"<< obj.mAnimation;
679 } else if (0 == strcmp(key, "k")) {
680 RAPIDJSON_ASSERT(PeekType() == kArrayType);
681 EnterArray();
682 while (NextArrayValue()) {
683 // for key frame
684 if (PeekType() == kObjectType) {
685 obj.mAnimation = true;
686 parseKeyFrame(obj.mProperty);
687 } else if (PeekType() == kNumberType) {
688 parseArrayValue(obj.mProperty.mValue);
689 } else {
690 sgDebug<<"Something is really wrong here ++++++++";
691 Skip(nullptr);
692 }
693 }
694 } else {
695 sgDebug<<"Property ignored :";
696 Skip(key);
697 }
698 }
699}
700
701SGJson::SGJson(const char *data)
702{
703 LottieParser r(const_cast<char *>(data));
704 r.parseComposition();
705}
706
707RAPIDJSON_DIAG_POP
diff --git a/ssg/src/lottie/lottiecomposition.h b/ssg/src/lottie/lottiecomposition.h
index 8c6c2cc43c..f2568b9abc 100644
--- a/ssg/src/lottie/lottiecomposition.h
+++ b/ssg/src/lottie/lottiecomposition.h
@@ -7,223 +7,6 @@
7#include"sgdebug.h" 7#include"sgdebug.h"
8#include<vector> 8#include<vector>
9 9
10#include"lottiemodel.h"
11
12// Format Indipendent class to generate data after parsing svg file
13
14struct FIDefNode;
15struct FIGradinet;
16struct FIColor
17{
18 int red;
19 int green;
20 int blue;
21};
22
23struct FIPaint
24{
25 FIColor mColor;
26 bool mNone;
27 bool mCurColor;
28 FIGradinet *mGradient;
29 std::string mRef;
30};
31
32struct FIFill
33{
34 enum class Mode {
35 OddEven,
36 Winding
37 };
38 enum class Flag {
39 Paint = 0x1,
40 Opacity = 0x2,
41 Gradient = 0x4,
42 FillRule = 0x8
43 };
44 FIFill::Flag mFlag;
45 FIPaint mPaint;
46 int mOpacity;
47 FIFill::Mode mMode;
48};
49
50struct FIStroke
51{
52 struct Dash {
53
54 };
55 enum class Cap {
56 Butt,
57 Round,
58 Square
59 };
60 enum class Join {
61 Bevel,
62 Round,
63 Meter
64 };
65 enum class Flag {
66 Paint = 0x1,
67 Opacity = 0x2,
68 Gradient = 0x4,
69 Scale = 0x8,
70 Width = 0x10,
71 Cap = 0x20,
72 Join = 0x40,
73 Dash = 0x80
74 };
75 FIStroke::Flag mFlags;
76 FIPaint mPaint;
77 int mOpacity;
78 float mScale;
79 float mWidth;
80 float mCentered;
81 FIStroke::Cap mCap;
82 FIStroke::Join mJoin;
83 FIStroke::Dash *mDash;
84};
85
86class FINodeProperty
87{
88 FIFill mfill;
89 FIStroke mstroke;
90};
91struct FINode
92{
93 ~FINode(){}
94 FINode(FINode *parent = nullptr) {
95 if (parent) {
96 mParent = parent;
97 mParent->mChildren.push_back(this);
98 }
99 }
100 enum class Type {
101 Doc,
102 Group,
103 Def,
104 Ellipse,
105 Circle,
106 Rect,
107 Line,
108 Path
109 };
110 FINode::Type mType;
111 FINode *mParent;
112 std::string name;
113 FINodeProperty *property;
114 std::vector<FINode *> mChildren;
115
116};
117
118struct FIGroupNode : public FINode
119{
120 FIGroupNode(FINode *parent = nullptr):FINode(parent) { mType = FINode::Type::Group;}
121 int mParentId; // Lottie the id of the parent in the composition
122 int mId; // Lottie the group id used for parenting.
123 int mGroupType; //lottie layer type (solid/shape/precomp)
124};
125
126struct FIDocNode : public FINode
127{
128 FIDocNode(FINode *parent = nullptr):FINode(parent) { mType = FINode::Type::Doc;}
129 SGRect mBound;
130 FIDefNode *mDef;
131 bool mpreserveAspect = false;
132 bool mAnimation = false;
133 float mStartTime = 0;
134 float mEndTime = 0;
135 float mFrameRate;
136};
137
138struct FIEllipseNode : public FINode
139{
140 FIEllipseNode(FINode *parent = nullptr):FINode(parent) { mType = FINode::Type::Ellipse;}
141 SGPointF mCenter;
142 float mRadiusX;
143 float mRadiusY;
144};
145
146struct FICircleNode : public FINode
147{
148 FICircleNode(FINode *parent = nullptr):FINode(parent) { mType = FINode::Type::Circle;}
149 SGPointF mCenter;
150 float mRadius;
151};
152
153struct FIRectNode : public FINode
154{
155 FIRectNode(FINode *parent = nullptr):FINode(parent) { mType = FINode::Type::Rect;}
156 SGRect mRect;
157 float mRadiusX;
158 float mRadiusY;
159};
160
161template<bool sizeAnim>
162struct LNode : public FINode
163{
164 LottieIntProperty<sizeAnim> mSizeProperty;
165};
166
167struct FILineNode : public FINode
168{
169 FILineNode(FINode *parent = nullptr):FINode(parent) { mType = FINode::Type::Line;}
170 SGPointF mStartPt;
171 SGPointF mEndPt;
172};
173
174struct FIPathNode : public FINode
175{
176 FIPathNode(FINode *parent = nullptr):FINode(parent) { mType = FINode::Type::Path;}
177 std::vector<float> mPoints;
178};
179
180struct FIDefNode : public FINode
181{
182 FIDefNode(FINode *parent = nullptr):FINode(parent) { mType = FINode::Type::Def;}
183 std::vector<FIGradinet> mGradients;
184};
185
186struct FIGradinet
187{
188 enum class Type {
189 Linear,
190 Radial
191 };
192 enum class Spread {
193 Pad,
194 Repeat,
195 Reflect
196 };
197
198 FIGradinet::Type mType;
199 std::string mId;
200 bool mUserSpace;
201 FIGradinet::Spread mSpread;
202 std::vector<float> mStops;
203};
204
205struct FILinearGradinet : FIGradinet
206{
207 FILinearGradinet()
208 {
209 mType = FIGradinet::Type::Linear;
210 }
211 SGPointF mStartPt;
212 SGPointF mEndPt;
213};
214
215struct FIRadialGradinet : FIGradinet
216{
217 FIRadialGradinet()
218 {
219 mType = FIGradinet::Type::Radial;
220 }
221 SGPointF mCenter;
222 SGPointF mFocal;
223 float mRadius;
224};
225
226
227//using namespace rapidjson; 10//using namespace rapidjson;
228 11
229//class LottieCompositionData; 12//class LottieCompositionData;
diff --git a/ssg/src/lottie/lottiemodel.cpp b/ssg/src/lottie/lottiemodel.cpp
new file mode 100644
index 0000000000..7c98f3e124
--- /dev/null
+++ b/ssg/src/lottie/lottiemodel.cpp
@@ -0,0 +1,52 @@
1#include "lottiemodel.h"
2
3/*
4 * Convert the AE shape format to
5 * list of bazier curves
6 */
7void LottieShapeObject::process()
8{
9 if (mShape.isStatic()) {
10 mShape.mValue.process(mClosed);
11 } else {
12 for (auto &keyframe : mShape.mAnimInfo->mKeyFrames) {
13 keyframe.mStartValue.process(mClosed);
14 keyframe.mEndValue.process(mClosed);
15 }
16 }
17}
18
19/*
20 * Convert the AE shape format to
21 * list of bazier curves
22 * The final structure will be M+size*C
23 */
24void LottieShape::process(bool closed)
25{
26 if (mInPoint.size() != mOutPoint.size() ||
27 mInPoint.size() != mVertices.size()) {
28 sgCritical<<"The Shape data are corrupted";
29 mInPoint = std::vector<SGPointF>();
30 mOutPoint = std::vector<SGPointF>();
31 mVertices = std::vector<SGPointF>();
32 }
33 int size = mVertices.size();
34 mPoints.reserve(3*size + 4);
35 mPoints.push_back(mVertices[0]);
36 for (int i =1; i <size ; i++ ) {
37 mPoints.push_back(mVertices[i-1] + mOutPoint[i-1]); // CP1 = start + outTangent
38 mPoints.push_back(mVertices[i] + mInPoint[i]); // CP2 = end + inTangent
39 mPoints.push_back(mVertices[i]); //end point
40 }
41
42 if (closed) {
43 mPoints.push_back(mVertices[size-1] + mOutPoint[size-1]); // CP1 = start + outTangent
44 mPoints.push_back(mVertices[0] + mInPoint[0]); // CP2 = end + inTangent
45 mPoints.push_back(mVertices[0]); //end point
46 }
47
48 // below data no more needed
49 mInPoint = std::vector<SGPointF>();
50 mOutPoint = std::vector<SGPointF>();
51 mVertices = std::vector<SGPointF>();
52}
diff --git a/ssg/src/lottie/lottiemodel.h b/ssg/src/lottie/lottiemodel.h
index 0e8c396e15..a0d1fc99b5 100644
--- a/ssg/src/lottie/lottiemodel.h
+++ b/ssg/src/lottie/lottiemodel.h
@@ -2,60 +2,104 @@
2#define LOTTIEMODEL_H 2#define LOTTIEMODEL_H
3 3
4#include<vector> 4#include<vector>
5#include<unordered_map>
5#include"sgpoint.h" 6#include"sgpoint.h"
6#include"sgrect.h" 7#include"sgrect.h"
7 8
8template<typename T> 9
9class LottieKeyFrame 10class LottieComposition;
11class LottieLayer;
12class LottieTransform;
13class LottieShapeGroup;
14class LottieShapeObject;
15class LottieRectObject;
16class LottieEllipseObject;
17class LottieTrimObject;
18class LottieRepeaterObject;
19class LottieFillObject;
20class LottieStrokeObject;
21class LottieGroupObject;
22
23class LottieObjectVisitor
10{ 24{
11public: 25public:
12 T mStartValue; 26 virtual ~LottieObjectVisitor() {}
13 T mEndValue; 27 virtual void visit(LottieComposition *) = 0;
14 int mStartFrame; 28 virtual void visit(LottieLayer *) = 0;
15 int mEndFrame; 29 virtual void visit(LottieTransform *) = 0;
30 virtual void visit(LottieShapeGroup *) = 0;
31 virtual void visit(LottieShapeObject *) = 0;
32 virtual void visit(LottieRectObject *) = 0;
33 virtual void visit(LottieEllipseObject *) = 0;
34 virtual void visit(LottieTrimObject *) = 0;
35 virtual void visit(LottieRepeaterObject *) = 0;
36 virtual void visit(LottieFillObject *) = 0;
37 virtual void visit(LottieStrokeObject *) = 0;
38 virtual void visitChildren(LottieGroupObject *) = 0;
16}; 39};
17 40
18template<typename T, bool animation = true> 41class LottieColor
19class LottieProperty
20{ 42{
21public: 43public:
22 LottieProperty(){} 44 float r;
23 constexpr bool staticType() const {return false;} 45 float g;
24 constexpr bool isStatic() const {return mKeyFrames.empty();} 46 float b;
47};
48
49class LottieInterpolater
50{
25public: 51public:
26 T mValue; 52 SGPointF mInTangent;
27 std::vector<LottieKeyFrame<T>> mKeyFrames; 53 SGPointF mOutTangent;
28}; 54};
29 55
30template<typename T> 56template<typename T>
31class LottieProperty<T, false> 57class LottieKeyFrame
32{ 58{
33public: 59public:
34 LottieProperty(){} 60 LottieKeyFrame():mStartValue(),
35 LottieProperty<T,false>(T initialvalue): mValue(initialvalue){} 61 mEndValue(),
36 constexpr bool staticType() const {return true;} 62 mStartFrame(0),
37 constexpr bool isStatic() const {return true;} 63 mEndFrame(0),
64 mInterpolator(nullptr),
65 mInTangent(),
66 mOutTangent(),
67 mPathKeyFrame(false){}
38public: 68public:
39 T mValue; 69 T mStartValue;
40}; 70 T mEndValue;
41 71 int mStartFrame;
42// Template aliasing for easy of use. 72 int mEndFrame;
43template<bool animation> 73 LottieInterpolater *mInterpolator;
44using LottieIntProperty = LottieProperty<int, animation>;
45 74
46template<bool animation> 75 /* this is for interpolating position along a path
47using LottieFloatProperty = LottieProperty<float, animation>; 76 * Need to move to other place because its only applicable
77 * for positional property.
78 */
79 SGPointF mInTangent;
80 SGPointF mOutTangent;
81 bool mPathKeyFrame;
82};
48 83
49template <typename T> 84template<typename T>
50struct LottiePropertyHelper 85class LottieAnimInfo
51{ 86{
52 LottiePropertyHelper(const T &value):mAnimation(false){ mProperty.mValue = value;} 87public:
53 bool mAnimation; 88 std::vector<LottieKeyFrame<T>> mKeyFrames;
54 LottieProperty<T,true> mProperty;
55}; 89};
56 90
57template<bool animation> 91template<typename T>
58using LottiePointFProperty = LottieProperty<SGPointF, animation>; 92class LottieAnimatable
93{
94public:
95 LottieAnimatable():mValue(),mAnimInfo(nullptr){}
96 LottieAnimatable(const T &value): mValue(value), mAnimInfo(nullptr){}
97 constexpr bool isStatic() const {return ((mAnimInfo==nullptr) ? true : false);}
98public:
99 T mValue;
100 LottieAnimInfo<T> *mAnimInfo;
101 int mPropertyIndex; /* "ix" */
102};
59 103
60enum class LottieBlendMode 104enum class LottieBlendMode
61{ 105{
@@ -64,13 +108,16 @@ enum class LottieBlendMode
64 Screen = 2, 108 Screen = 2,
65 OverLay = 3 109 OverLay = 3
66}; 110};
111
112class LottieObjectVisitor;
67class LottieObject 113class LottieObject
68{ 114{
69public: 115public:
70 enum class Type { 116 enum class Type {
71 Composition, 117 Composition = 1,
72 Layer, 118 Layer,
73 Group, 119 ShapeGroup,
120 Transform,
74 Fill, 121 Fill,
75 Stroke, 122 Stroke,
76 GFill, 123 GFill,
@@ -78,36 +125,62 @@ public:
78 Rect, 125 Rect,
79 Ellipse, 126 Ellipse,
80 Shape, 127 Shape,
81 Star 128 Star,
129 Trim,
130 Repeater
82 }; 131 };
83 132 virtual void accept(LottieObjectVisitor *){}
133 virtual ~LottieObject(){}
134 LottieObject(LottieObject::Type type): mType(type){}
84 bool isStatic(); 135 bool isStatic();
85public: 136public:
86 LottieObject::Type mType; 137 LottieObject::Type mType;
87}; 138};
88 139
89class LottieGroupObj : public LottieObject 140class LottieGroupObject: public LottieObject
90{ 141{
91public: 142public:
143 LottieGroupObject(LottieObject::Type type):LottieObject(type){}
144public:
92 std::vector<LottieObject *> mChildren; 145 std::vector<LottieObject *> mChildren;
93 int mParentId; // Lottie the id of the parent in the composition
94 int mId; // Lottie the group id used for parenting.
95}; 146};
96 147
97class LottieComposition : public LottieGroupObj 148class LottieShapeGroup : public LottieGroupObject
98{ 149{
99public: 150public:
100 SGRect mBound; 151 void accept(LottieObjectVisitor *visitor) override
101 bool mpreserveAspect = false; 152 {visitor->visit(this); visitor->visitChildren(this);}
102 bool mAnimation = false; 153
103 long mStartFrame = 0; 154 LottieShapeGroup():LottieGroupObject(LottieObject::Type::ShapeGroup){}
104 long mEndFrame = 0; 155};
105 float mFrameRate; 156
157class LottieTransform;
158class LottieComposition : public LottieGroupObject
159{
160public:
161 void accept(LottieObjectVisitor *visitor) override
162 {visitor->visit(this); visitor->visitChildren(this);}
163 LottieComposition():LottieGroupObject(LottieObject::Type::Composition){}
164public:
165 SGRect mBound;
166 bool mAnimation = false;
167 long mStartFrame = 0;
168 long mEndFrame = 0;
169 float mFrameRate;
170 long mStartTime;
171 LottieBlendMode mBlendMode;
172 float mTimeStreatch;
173 std::unordered_map<std::string, LottieInterpolater*> mInterpolatorCache;
106}; 174};
107 175
108class LottieLayer : public LottieGroupObj 176class LottieLayer : public LottieGroupObject
109{ 177{
110public: 178public:
179 void accept(LottieObjectVisitor *visitor) override
180 {visitor->visit(this); visitor->visitChildren(this);}
181 LottieLayer():LottieGroupObject(LottieObject::Type::Layer),mParentId(-1),mId(-1){}
182
183public:
111 SGRect mBound; 184 SGRect mBound;
112 int mlayerType; //lottie layer type (solid/shape/precomp) 185 int mlayerType; //lottie layer type (solid/shape/precomp)
113 int mParentId; // Lottie the id of the parent in the composition 186 int mParentId; // Lottie the id of the parent in the composition
@@ -118,35 +191,145 @@ public:
118 long mStartTime; 191 long mStartTime;
119 LottieBlendMode mBlendMode; 192 LottieBlendMode mBlendMode;
120 float mTimeStreatch; 193 float mTimeStreatch;
194 LottieTransform *mTransform;
195};
196
197class LottieTransform : public LottieObject
198{
199public:
200 void accept(LottieObjectVisitor *visitor) final
201 {visitor->visit(this);}
202 LottieTransform():LottieObject(LottieObject::Type::Transform),
203 mRotation(0),
204 mScale(SGPointF(100, 100)),
205 mPosition(SGPointF(0, 0)),
206 mAnchor(SGPointF(0, 0)),
207 mOpacity(100),
208 mSkew(0),
209 mSkewAxis(0){}
210public:
211 LottieAnimatable<float> mRotation; /* "r" */
212 LottieAnimatable<SGPointF> mScale; /* "s" */
213 LottieAnimatable<SGPointF> mPosition; /* "p" */
214 LottieAnimatable<SGPointF> mAnchor; /* "a" */
215 LottieAnimatable<float> mOpacity; /* "o" */
216 LottieAnimatable<float> mSkew; /* "sk" */
217 LottieAnimatable<float> mSkewAxis; /* "sa" */
218};
219
220class LottieFillObject : public LottieObject
221{
222public:
223 void accept(LottieObjectVisitor *visitor) final
224 {visitor->visit(this);}
225 LottieFillObject():LottieObject(LottieObject::Type::Fill){}
226public:
227 LottieAnimatable<LottieColor> mColor; /* "c" */
228 LottieAnimatable<int> mOpacity; /* "o" */
229 bool mEnabled = true; /* "fillEnabled" */
121}; 230};
122 231
123class LottieMatrix : public LottieGroupObj 232class LottieStrokeObject : public LottieObject
124{ 233{
125public: 234public:
126 LottieFloatProperty<true> mRotation; /* "r" */ 235 void accept(LottieObjectVisitor *visitor) final
127 LottiePointFProperty<true> mScale; /* "s" */ 236 {visitor->visit(this);}
128 LottiePointFProperty<true> mPosition; /* "p" */ 237 LottieStrokeObject():LottieObject(LottieObject::Type::Stroke){}
129 LottiePointFProperty<true> mAnchor; /* "a" */ 238public:
130 LottieFloatProperty<true> mOpacity; /* "o" */ 239 LottieAnimatable<LottieColor> mColor; /* "c" */
131 LottiePointFProperty<true> mSkew; /* "sk" */ 240 LottieAnimatable<int> mOpacity; /* "o" */
132 LottieFloatProperty<true> mSkewAxis; /* "sa" */ 241 LottieAnimatable<float> mWidth; /* "w" */
242 CapStyle mCapStyle; /* "lc" */
243 JoinStyle mJoinStyle; /* "lj" */
244 float mMeterLimit; /* "ml" */
245 bool mEnabled = true; /* "fillEnabled" */
246};
247
248class LottieShape
249{
250public:
251 void process(bool closed =false);
252 std::vector<SGPointF> mInPoint;
253 std::vector<SGPointF> mOutPoint;
254 std::vector<SGPointF> mVertices;
255 std::vector<SGPointF> mPoints;
256 int mSegments;
257};
258
259class LottieShapeObject : public LottieObject
260{
261public:
262 void accept(LottieObjectVisitor *visitor) final
263 {visitor->visit(this);}
264 void process();
265 LottieShapeObject():LottieObject(LottieObject::Type::Shape){}
266public:
267 LottieAnimatable<LottieShape> mShape;
268 bool mClosed = false;
133}; 269};
134 270
135template<bool pos, bool size, bool roundness>
136class LottieRectObject : public LottieObject 271class LottieRectObject : public LottieObject
137{ 272{
138public: 273public:
139 LottiePointFProperty<pos> mPos; 274 void accept(LottieObjectVisitor *visitor) final
140 LottiePointFProperty<size> mSize; 275 {visitor->visit(this);}
141 LottieFloatProperty<roundness> mRound; 276 LottieRectObject():LottieObject(LottieObject::Type::Rect),
277 mPos(SGPointF(0,0)),
278 mSize(SGPointF(0,0)),
279 mRound(0){}
280public:
281 LottieAnimatable<SGPointF> mPos;
282 LottieAnimatable<SGPointF> mSize;
283 LottieAnimatable<float> mRound;
142}; 284};
143 285
144template<bool pos, bool size>
145class LottieEllipseObject : public LottieObject 286class LottieEllipseObject : public LottieObject
146{ 287{
147public: 288public:
148 LottiePointFProperty<pos> mPos; 289 void accept(LottieObjectVisitor *visitor) final
149 LottiePointFProperty<size> mSize; 290 {visitor->visit(this);}
291 LottieEllipseObject():LottieObject(LottieObject::Type::Ellipse),
292 mPos(SGPointF(0,0)),
293 mSize(SGPointF(0,0)){}
294public:
295 LottieAnimatable<SGPointF> mPos;
296 LottieAnimatable<SGPointF> mSize;
297};
298
299class LottieTrimObject : public LottieObject
300{
301public:
302 void accept(LottieObjectVisitor *visitor) final
303 {visitor->visit(this);}
304 enum class TrimType {
305 Simultaneously,
306 Individually
307 };
308 LottieTrimObject():LottieObject(LottieObject::Type::Trim),
309 mStart(0),
310 mEnd(0),
311 mOffset(0),
312 mTrimType(TrimType::Simultaneously){}
313public:
314 LottieAnimatable<float> mStart;
315 LottieAnimatable<float> mEnd;
316 LottieAnimatable<float> mOffset;
317 LottieTrimObject::TrimType mTrimType;
318};
319
320class LottieRepeaterObject : public LottieObject
321{
322public:
323 void accept(LottieObjectVisitor *visitor) final
324 {visitor->visit(this);}
325 LottieRepeaterObject():LottieObject(LottieObject::Type::Repeater),
326 mCopies(0),
327 mOffset(0),
328 mTransform(nullptr){}
329public:
330 LottieAnimatable<float> mCopies;
331 LottieAnimatable<float> mOffset;
332 LottieTransform *mTransform;
150}; 333};
151 334
152 335
diff --git a/ssg/src/lottie/lottieparser.cpp b/ssg/src/lottie/lottieparser.cpp
new file mode 100644
index 0000000000..87f105ab5d
--- /dev/null
+++ b/ssg/src/lottie/lottieparser.cpp
@@ -0,0 +1,1186 @@
1#include"sgjson.h"
2#include "rapidjson/document.h"
3#include <iostream>
4#include "lottiecomposition.h"
5#include "lottiemodel.h"
6
7RAPIDJSON_DIAG_PUSH
8#ifdef __GNUC__
9RAPIDJSON_DIAG_OFF(effc++)
10#endif
11
12// This example demonstrates JSON token-by-token parsing with an API that is
13// more direct; you don't need to design your logic around a handler object and
14// callbacks. Instead, you retrieve values from the JSON stream by calling
15// GetInt(), GetDouble(), GetString() and GetBool(), traverse into structures
16// by calling EnterObject() and EnterArray(), and skip over unwanted data by
17// calling SkipValue(). When you know your JSON's structure, this can be quite
18// convenient.
19//
20// If you aren't sure of what's next in the JSON data, you can use PeekType() and
21// PeekValue() to look ahead to the next object before reading it.
22//
23// If you call the wrong retrieval method--e.g. GetInt when the next JSON token is
24// not an int, EnterObject or EnterArray when there isn't actually an object or array
25// to read--the stream parsing will end immediately and no more data will be delivered.
26//
27// After calling EnterObject, you retrieve keys via NextObjectKey() and values via
28// the normal getters. When NextObjectKey() returns null, you have exited the
29// object, or you can call SkipObject() to skip to the end of the object
30// immediately. If you fetch the entire object (i.e. NextObjectKey() returned null),
31// you should not call SkipObject().
32//
33// After calling EnterArray(), you must alternate between calling NextArrayValue()
34// to see if the array has more data, and then retrieving values via the normal
35// getters. You can call SkipArray() to skip to the end of the array immediately.
36// If you fetch the entire array (i.e. NextArrayValue() returned null),
37// you should not call SkipArray().
38//
39// This parser uses in-situ strings, so the JSON buffer will be altered during the
40// parse.
41
42using namespace rapidjson;
43
44
45class LookaheadParserHandler {
46public:
47 bool Null() { st_ = kHasNull; v_.SetNull(); return true; }
48 bool Bool(bool b) { st_ = kHasBool; v_.SetBool(b); return true; }
49 bool Int(int i) { st_ = kHasNumber; v_.SetInt(i); return true; }
50 bool Uint(unsigned u) { st_ = kHasNumber; v_.SetUint(u); return true; }
51 bool Int64(int64_t i) { st_ = kHasNumber; v_.SetInt64(i); return true; }
52 bool Uint64(uint64_t u) { st_ = kHasNumber; v_.SetUint64(u); return true; }
53 bool Double(double d) { st_ = kHasNumber; v_.SetDouble(d); return true; }
54 bool RawNumber(const char*, SizeType, bool) { return false; }
55 bool String(const char* str, SizeType length, bool) { st_ = kHasString; v_.SetString(str, length); return true; }
56 bool StartObject() { st_ = kEnteringObject; return true; }
57 bool Key(const char* str, SizeType length, bool) { st_ = kHasKey; v_.SetString(str, length); return true; }
58 bool EndObject(SizeType) { st_ = kExitingObject; return true; }
59 bool StartArray() { st_ = kEnteringArray; return true; }
60 bool EndArray(SizeType) { st_ = kExitingArray; return true; }
61
62protected:
63 LookaheadParserHandler(char* str);
64 void ParseNext();
65
66protected:
67 enum LookaheadParsingState {
68 kInit,
69 kError,
70 kHasNull,
71 kHasBool,
72 kHasNumber,
73 kHasString,
74 kHasKey,
75 kEnteringObject,
76 kExitingObject,
77 kEnteringArray,
78 kExitingArray
79 };
80
81 Value v_;
82 LookaheadParsingState st_;
83 Reader r_;
84 InsituStringStream ss_;
85
86 static const int parseFlags = kParseDefaultFlags | kParseInsituFlag;
87};
88
89LookaheadParserHandler::LookaheadParserHandler(char* str) : v_(), st_(kInit), r_(), ss_(str) {
90 r_.IterativeParseInit();
91 ParseNext();
92}
93
94void LookaheadParserHandler::ParseNext() {
95 if (r_.HasParseError()) {
96 st_ = kError;
97 return;
98 }
99
100 if (!r_.IterativeParseNext<parseFlags>(ss_, *this)) {
101 sgCritical<<"Lottie file parsing error";
102 RAPIDJSON_ASSERT(0);
103 }
104}
105
106class LottieParser : protected LookaheadParserHandler {
107public:
108 LottieParser(char* str) : LookaheadParserHandler(str) {}
109
110 bool EnterObject();
111 bool EnterArray();
112 const char* NextObjectKey();
113 bool NextArrayValue();
114 int GetInt();
115 double GetDouble();
116 const char* GetString();
117 bool GetBool();
118 void GetNull();
119
120 void SkipObject();
121 void SkipArray();
122 void SkipValue();
123 Value* PeekValue();
124 int PeekType(); // returns a rapidjson::Type, or -1 for no value (at end of object/array)
125
126 bool IsValid() { return st_ != kError; }
127
128 void Skip(const char *key);
129 SGRect getRect();
130 LottieBlendMode getBlendMode();
131 CapStyle getLineCap();
132 JoinStyle getLineJoin();
133 LottieTrimObject::TrimType getTrimType();
134
135 LottieComposition *parseComposition();
136 void parseLayers(LottieComposition *comp);
137 LottieLayer *parseLayer();
138 void parseShapesAttr(LottieLayer *layer);
139 void parseObject(LottieGroupObject *parent);
140 LottieObject* parseObjectTypeAttr();
141 LottieObject *parseGroupObject();
142 LottieObject *parseRectObject();
143 LottieObject *parseEllipseObject();
144 LottieObject *parseShapeObject();
145
146 LottieTransform *parseTransformObject();
147 LottieObject *parseFillObject();
148 LottieObject *parseGradientFillObject();
149 LottieObject *parseStrokeObject();
150 LottieObject *parseGradientStrokeObject();
151 LottieObject *parseTrimObject();
152 LottieObject *parseReapeaterObject();
153
154 SGPointF parseInperpolatorPoint();
155 void parseArrayValue(SGPointF &pt);
156 void parseArrayValue(LottieColor &pt);
157 void parseArrayValue(float &val);
158 void parseArrayValue(int &val);
159 void getValue(SGPointF &val);
160 void getValue(float &val);
161 void getValue(LottieColor &val);
162 void getValue(int &val);
163 void getValue(LottieShape &shape);
164 template<typename T>
165 void parseKeyFrame(LottieAnimInfo<T> &obj);
166 template<typename T>
167 void parseProperty(LottieAnimatable<T> &obj);
168
169 void parseShapeKeyFrame(LottieAnimInfo<LottieShape> &obj);
170 void parseShapeProperty(LottieAnimatable<LottieShape> &obj);
171 void parseArrayValue(std::vector<SGPointF> &v);
172protected:
173 LottieComposition *compRef;
174 void SkipOut(int depth);
175};
176
177bool LottieParser::EnterObject() {
178 if (st_ != kEnteringObject) {
179 st_ = kError;
180 RAPIDJSON_ASSERT(false);
181 return false;
182 }
183
184 ParseNext();
185 return true;
186}
187
188bool LottieParser::EnterArray() {
189 if (st_ != kEnteringArray) {
190 st_ = kError;
191 RAPIDJSON_ASSERT(false);
192 return false;
193 }
194
195 ParseNext();
196 return true;
197}
198
199const char* LottieParser::NextObjectKey() {
200 if (st_ == kHasKey) {
201 const char* result = v_.GetString();
202 ParseNext();
203 return result;
204 }
205
206 /* SPECIAL CASE
207 * The parser works with a prdefined rule that it will be only
208 * while (NextObjectKey()) for each object but in case of our nested group
209 * object we can call multiple time NextObjectKey() while exiting the object
210 * so ignore those and don't put parser in the error state.
211 * */
212 if (st_ == kExitingArray || st_ == kEnteringObject ) {
213 sgDebug<<"O: Exiting nested loop";
214 return 0;
215 }
216
217 if (st_ != kExitingObject) {
218 RAPIDJSON_ASSERT(false);
219 st_ = kError;
220 return 0;
221 }
222
223 ParseNext();
224 return 0;
225}
226
227bool LottieParser::NextArrayValue() {
228 if (st_ == kExitingArray) {
229 ParseNext();
230 return false;
231 }
232
233 /* SPECIAL CASE
234 * same as NextObjectKey()
235 */
236 if (st_ == kExitingObject) {
237 sgDebug<<"A: Exiting nested loop";
238 return 0;
239 }
240
241
242 if (st_ == kError || st_ == kHasKey) {
243 RAPIDJSON_ASSERT(false);
244 st_ = kError;
245 return false;
246 }
247
248 return true;
249}
250
251int LottieParser::GetInt() {
252 if (st_ != kHasNumber || !v_.IsInt()) {
253 st_ = kError;
254 RAPIDJSON_ASSERT(false);
255 return 0;
256 }
257
258 int result = v_.GetInt();
259 ParseNext();
260 return result;
261}
262
263double LottieParser::GetDouble() {
264 if (st_ != kHasNumber) {
265 st_ = kError;
266 RAPIDJSON_ASSERT(false);
267 return 0.;
268 }
269
270 double result = v_.GetDouble();
271 ParseNext();
272 return result;
273}
274
275bool LottieParser::GetBool() {
276 if (st_ != kHasBool) {
277 st_ = kError;
278 RAPIDJSON_ASSERT(false);
279 return false;
280 }
281
282 bool result = v_.GetBool();
283 ParseNext();
284 return result;
285}
286
287void LottieParser::GetNull() {
288 if (st_ != kHasNull) {
289 st_ = kError;
290 return;
291 }
292
293 ParseNext();
294}
295
296const char* LottieParser::GetString() {
297 if (st_ != kHasString) {
298 st_ = kError;
299 RAPIDJSON_ASSERT(false);
300 return 0;
301 }
302
303 const char* result = v_.GetString();
304 ParseNext();
305 return result;
306}
307
308void LottieParser::SkipOut(int depth) {
309 do {
310 if (st_ == kEnteringArray || st_ == kEnteringObject) {
311 ++depth;
312 }
313 else if (st_ == kExitingArray || st_ == kExitingObject) {
314 --depth;
315 }
316 else if (st_ == kError) {
317 RAPIDJSON_ASSERT(false);
318 return;
319 }
320
321 ParseNext();
322 }
323 while (depth > 0);
324}
325
326void LottieParser::SkipValue() {
327 SkipOut(0);
328}
329
330void LottieParser::SkipArray() {
331 SkipOut(1);
332}
333
334void LottieParser::SkipObject() {
335 SkipOut(1);
336}
337
338Value* LottieParser::PeekValue() {
339 if (st_ >= kHasNull && st_ <= kHasKey) {
340 return &v_;
341 }
342
343 return 0;
344}
345
346int LottieParser::PeekType() {
347 if (st_ >= kHasNull && st_ <= kHasKey) {
348 return v_.GetType();
349 }
350
351 if (st_ == kEnteringArray) {
352 return kArrayType;
353 }
354
355 if (st_ == kEnteringObject) {
356 return kObjectType;
357 }
358
359 return -1;
360}
361
362void LottieParser::Skip(const char *key)
363{
364 if (PeekType() == kArrayType) {
365 EnterArray();
366 SkipArray();
367 } else if (PeekType() == kObjectType) {
368 EnterObject();
369 SkipObject();
370 } else {
371 SkipValue();
372 }
373}
374
375LottieBlendMode
376LottieParser::getBlendMode()
377{
378 RAPIDJSON_ASSERT(PeekType() == kNumberType);
379 LottieBlendMode mode = LottieBlendMode::Normal;
380
381 switch (GetInt()) {
382 case 1:
383 mode = LottieBlendMode::Multiply;
384 break;
385 case 2:
386 mode = LottieBlendMode::Screen;
387 break;
388 case 3:
389 mode = LottieBlendMode::OverLay;
390 break;
391 default:
392 break;
393 }
394 return mode;
395}
396SGRect LottieParser::getRect()
397{
398 SGRect r;
399 RAPIDJSON_ASSERT(PeekType() == kObjectType);
400 EnterObject();
401 while (const char* key = NextObjectKey()) {
402 if (0 == strcmp(key, "l")) {
403 RAPIDJSON_ASSERT(PeekType() == kNumberType);
404 r.setLeft(GetInt());
405 } else if (0 == strcmp(key, "r")) {
406 RAPIDJSON_ASSERT(PeekType() == kNumberType);
407 r.setRight(GetInt());
408 } else if (0 == strcmp(key, "t")) {
409 RAPIDJSON_ASSERT(PeekType() == kNumberType);
410 r.setTop(GetInt());
411 } else if (0 == strcmp(key, "b")) {
412 RAPIDJSON_ASSERT(PeekType() == kNumberType);
413 r.setBottom(GetInt());
414 } else {
415 RAPIDJSON_ASSERT(false);
416 }
417 }
418 return r;
419}
420
421LottieComposition *LottieParser::parseComposition()
422{
423 RAPIDJSON_ASSERT(PeekType() == kObjectType);
424 EnterObject();
425 LottieComposition *comp = new LottieComposition();
426 compRef = comp;
427 while (const char* key = NextObjectKey()) {
428 if (0 == strcmp(key, "w")) {
429 RAPIDJSON_ASSERT(PeekType() == kNumberType);
430 comp->mBound.setWidth(GetInt());
431 } else if (0 == strcmp(key, "h")) {
432 RAPIDJSON_ASSERT(PeekType() == kNumberType);
433 comp->mBound.setHeight(GetInt());
434 } else if (0 == strcmp(key, "ip")) {
435 RAPIDJSON_ASSERT(PeekType() == kNumberType);
436 comp->mStartFrame = GetDouble();
437 } else if (0 == strcmp(key, "op")) {
438 RAPIDJSON_ASSERT(PeekType() == kNumberType);
439 comp->mEndFrame = GetDouble();
440 } else if (0 == strcmp(key, "fr")) {
441 RAPIDJSON_ASSERT(PeekType() == kNumberType);
442 comp->mFrameRate = GetDouble();
443 } else if (0 == strcmp(key, "layers")) {
444 parseLayers(comp);
445 } else {
446 sgWarning<<"Composition Attribute Skipped : "<<key;
447 Skip(key);
448 }
449 }
450 return comp;
451}
452
453void LottieParser::parseLayers(LottieComposition *composition)
454{
455 RAPIDJSON_ASSERT(PeekType() == kArrayType);
456 EnterArray();
457 while (NextArrayValue()) {
458 auto layer = parseLayer();
459 composition->mChildren.push_back(layer);
460 }
461}
462
463/*
464 * https://github.com/airbnb/lottie-web/blob/master/docs/json/layers/shape.json
465 *
466 */
467LottieLayer * LottieParser::parseLayer()
468{
469 RAPIDJSON_ASSERT(PeekType() == kObjectType);
470 //sgDebug<<"parse LAYER: S";
471 LottieLayer *layer = new LottieLayer();
472 EnterObject();
473 while (const char* key = NextObjectKey()) {
474 if (0 == strcmp(key, "ty")) { /* Type of layer: Shape. Value 4.*/
475 RAPIDJSON_ASSERT(PeekType() == kNumberType);
476 layer->mGroupType = GetInt();
477 } else if (0 == strcmp(key, "ind")) { /*Layer index in AE. Used for parenting and expressions.*/
478 RAPIDJSON_ASSERT(PeekType() == kNumberType);
479 layer->mId = GetInt();
480 } else if (0 == strcmp(key, "parent")) { /*Layer Parent. Uses "ind" of parent.*/
481 RAPIDJSON_ASSERT(PeekType() == kNumberType);
482 layer->mParentId = GetInt();
483 }else if (0 == strcmp(key, "sr")) { // "Layer Time Stretching"
484 RAPIDJSON_ASSERT(PeekType() == kNumberType);
485 layer->mTimeStreatch = GetDouble();
486 } else if (0 == strcmp(key, "ip")) {
487 RAPIDJSON_ASSERT(PeekType() == kNumberType);
488 layer->mStartFrame = GetDouble();
489 } else if (0 == strcmp(key, "op")) {
490 RAPIDJSON_ASSERT(PeekType() == kNumberType);
491 layer->mEndFrame = GetDouble();
492 } else if (0 == strcmp(key, "st")) {
493 RAPIDJSON_ASSERT(PeekType() == kNumberType);
494 layer->mStartTime = GetDouble();
495 } else if (0 == strcmp(key, "bounds")) {
496 layer->mBound = getRect();
497 } else if (0 == strcmp(key, "bm")) {
498 layer->mBlendMode = getBlendMode();
499 } else if (0 == strcmp(key, "ks")) {
500 RAPIDJSON_ASSERT(PeekType() == kObjectType);
501 EnterObject();
502 layer->mTransform = parseTransformObject();
503 } else if (0 == strcmp(key, "shapes")) {
504 parseShapesAttr(layer);
505 } else {
506 sgWarning<<"Layer Attribute Skipped : "<<key;
507 Skip(key);
508 }
509 }
510 //sgDebug<<"parse LAYER: E";
511 return layer;
512}
513
514void LottieParser::parseShapesAttr(LottieLayer *layer)
515{
516 //sgDebug<<"ENTER SHAPE ATTR";
517 RAPIDJSON_ASSERT(PeekType() == kArrayType);
518 EnterArray();
519 while (NextArrayValue()) {
520 parseObject(layer);
521 }
522 //sgDebug<<"EXIT SHAPE ATTR";
523}
524
525LottieObject*
526LottieParser::parseObjectTypeAttr()
527{
528 RAPIDJSON_ASSERT(PeekType() == kStringType);
529 const char *type = GetString();
530 if (0 == strcmp(type, "gr")) {
531 return parseGroupObject();
532 } else if (0 == strcmp(type, "rc")) {
533 return parseRectObject();
534 } else if (0 == strcmp(type, "el")) {
535 return parseEllipseObject();
536 } else if (0 == strcmp(type, "tr")) {
537 return parseTransformObject();
538 } else if (0 == strcmp(type, "fl")) {
539 return parseFillObject();
540 } else if (0 == strcmp(type, "st")) {
541 return parseStrokeObject();
542 } else if (0 == strcmp(type, "sh")) {
543 return parseShapeObject();
544 } else if (0 == strcmp(type, "tm")) {
545 return parseTrimObject();
546 } else {
547 sgDebug<<"The Object Type not yet handled = "<< type;
548 return nullptr;
549 }
550}
551
552void
553LottieParser::parseObject(LottieGroupObject *parent)
554{
555 RAPIDJSON_ASSERT(PeekType() == kObjectType);
556 EnterObject();
557 while (const char* key = NextObjectKey()) {
558 if (0 == strcmp(key, "ty")) {
559 auto child = parseObjectTypeAttr();
560 if (child)
561 parent->mChildren.push_back(child);
562 } else {
563 Skip(key);
564 }
565 }
566}
567
568LottieObject *
569LottieParser::parseGroupObject()
570{
571 LottieShapeGroup *group = new LottieShapeGroup();
572 while (const char* key = NextObjectKey()) {
573 if (0 == strcmp(key, "it")) {
574 RAPIDJSON_ASSERT(PeekType() == kArrayType);
575 EnterArray();
576 while (NextArrayValue()) {
577 RAPIDJSON_ASSERT(PeekType() == kObjectType);
578 parseObject(group);
579 }
580 } else {
581 Skip(key);
582 }
583 }
584 return group;
585}
586
587/*
588 * https://github.com/airbnb/lottie-web/blob/master/docs/json/shapes/rect.json
589 */
590LottieObject *
591LottieParser::parseRectObject()
592{
593 LottieRectObject *obj = new LottieRectObject();
594 while (const char* key = NextObjectKey()) {
595 if (0 == strcmp(key, "p")) {
596 parseProperty(obj->mPos);
597 } else if (0 == strcmp(key, "s")) {
598 parseProperty(obj->mSize);
599 } else if (0 == strcmp(key, "r")) {
600 parseProperty(obj->mRound);
601 } else if (0 == strcmp(key, "d")) {
602 Skip(key);
603 } else {
604 Skip(key);
605 }
606 }
607 return nullptr;
608}
609
610/*
611 * https://github.com/airbnb/lottie-web/blob/master/docs/json/shapes/ellipse.json
612 */
613LottieObject *
614LottieParser::parseEllipseObject()
615{
616 LottieEllipseObject *obj = new LottieEllipseObject();
617 while (const char* key = NextObjectKey()) {
618 if (0 == strcmp(key, "p")) {
619 parseProperty(obj->mPos);
620 } else if (0 == strcmp(key, "s")) {
621 parseProperty(obj->mSize);
622 } else {
623 Skip(key);
624 }
625 }
626 return obj;
627}
628
629/*
630 * https://github.com/airbnb/lottie-web/blob/master/docs/json/properties/shape.json
631 */
632LottieObject *
633LottieParser::parseShapeObject()
634{
635 //sgDebug<<"parse Shape START:";
636 LottieShapeObject *obj = new LottieShapeObject();
637 while (const char* key = NextObjectKey()) {
638 if (0 == strcmp(key, "ks")) {
639 parseShapeProperty(obj->mShape);
640 } else if (0 == strcmp(key, "closed")) {
641 obj->mClosed = GetBool();
642 } else {
643 sgDebug<<"Shape property ignored :"<<key;
644 Skip(key);
645 }
646 }
647 //sgDebug<<"parse Shape item END:";
648 obj->process();
649 return obj;
650}
651
652LottieTrimObject::TrimType
653LottieParser::getTrimType()
654{
655 RAPIDJSON_ASSERT(PeekType() == kNumberType);
656 switch (GetInt()) {
657 case 1:
658 return LottieTrimObject::TrimType::Simultaneously;
659 break;
660 case 2:
661 return LottieTrimObject::TrimType::Individually;
662 break;
663 default:
664 RAPIDJSON_ASSERT(0);
665 break;
666 }
667}
668
669/*
670 * https://github.com/airbnb/lottie-web/blob/master/docs/json/shapes/trim.json
671 */
672LottieObject *
673LottieParser::parseTrimObject()
674{
675 LottieTrimObject *obj = new LottieTrimObject();
676 while (const char* key = NextObjectKey()) {
677 if (0 == strcmp(key, "s")) {
678 parseProperty(obj->mStart);
679 } else if (0 == strcmp(key, "e")) {
680 parseProperty(obj->mEnd);
681 } else if (0 == strcmp(key, "o")) {
682 parseProperty(obj->mOffset);
683 } else if (0 == strcmp(key, "m")) {
684 obj->mTrimType = getTrimType();
685 } else {
686 sgDebug<<"Trim property ignored :"<<key;
687 Skip(key);
688 }
689 }
690 return obj;
691}
692
693LottieObject *
694LottieParser::parseReapeaterObject()
695{
696 LottieRepeaterObject *obj = new LottieRepeaterObject();
697 while (const char* key = NextObjectKey()) {
698 if (0 == strcmp(key, "c")) {
699 parseProperty(obj->mCopies);
700 } else if (0 == strcmp(key, "o")) {
701 parseProperty(obj->mOffset);
702 } else if (0 == strcmp(key, "tr")) {
703 obj->mTransform = parseTransformObject();
704 } else {
705 sgDebug<<"Repeater property ignored :"<<key;
706 Skip(key);
707 }
708 }
709 return obj;
710}
711
712
713/*
714 * https://github.com/airbnb/lottie-web/blob/master/docs/json/shapes/transform.json
715 */
716LottieTransform *
717LottieParser::parseTransformObject()
718{
719 LottieTransform *obj = new LottieTransform();
720 while (const char* key = NextObjectKey()) {
721 if (0 == strcmp(key, "a")) {
722 parseProperty(obj->mAnchor);
723 } else if (0 == strcmp(key, "p")) {
724 parseProperty(obj->mPosition);
725 } else if (0 == strcmp(key, "r")) {
726 parseProperty(obj->mRotation);
727 } else if (0 == strcmp(key, "s")) {
728 parseProperty(obj->mScale);
729 } else if (0 == strcmp(key, "sk")) {
730 parseProperty(obj->mSkew);
731 } else if (0 == strcmp(key, "sa")) {
732 parseProperty(obj->mSkewAxis);
733 } else if (0 == strcmp(key, "o")) {
734 parseProperty(obj->mOpacity);
735 } else {
736 Skip(key);
737 }
738 }
739 return obj;
740}
741
742/*
743 * https://github.com/airbnb/lottie-web/blob/master/docs/json/shapes/fill.json
744 */
745LottieObject *
746LottieParser::parseFillObject()
747{
748 LottieFillObject *obj = new LottieFillObject();
749 while (const char* key = NextObjectKey()) {
750 if (0 == strcmp(key, "c")) {
751 parseProperty(obj->mColor);
752 } else if (0 == strcmp(key, "o")) {
753 parseProperty(obj->mOpacity);
754 } else if (0 == strcmp(key, "fillEnabled")) {
755 obj->mEnabled = GetBool();
756 } else {
757 sgWarning<<"Fill property skipped = "<<key;
758 Skip(key);
759 }
760 }
761 return obj;
762}
763
764/*
765 * https://github.com/airbnb/lottie-web/blob/master/docs/json/helpers/lineCap.json
766 */
767CapStyle LottieParser::getLineCap()
768{
769 RAPIDJSON_ASSERT(PeekType() == kNumberType);
770 switch (GetInt()) {
771 case 1:
772 return CapStyle::Flat;
773 break;
774 case 2:
775 return CapStyle::Round;
776 break;
777 default:
778 return CapStyle::Square;
779 break;
780 }
781}
782
783/*
784 * https://github.com/airbnb/lottie-web/blob/master/docs/json/helpers/lineJoin.json
785 */
786JoinStyle LottieParser::getLineJoin()
787{
788 RAPIDJSON_ASSERT(PeekType() == kNumberType);
789 switch (GetInt()) {
790 case 1:
791 return JoinStyle::Miter;
792 break;
793 case 2:
794 return JoinStyle::Round;
795 break;
796 default:
797 return JoinStyle::Bevel;
798 break;
799 }
800}
801
802/*
803 * https://github.com/airbnb/lottie-web/blob/master/docs/json/shapes/stroke.json
804 */
805LottieObject *
806LottieParser::parseStrokeObject()
807{
808 LottieStrokeObject *obj = new LottieStrokeObject();
809 while (const char* key = NextObjectKey()) {
810 if (0 == strcmp(key, "c")) {
811 parseProperty(obj->mColor);
812 } else if (0 == strcmp(key, "o")) {
813 parseProperty(obj->mOpacity);
814 } else if (0 == strcmp(key, "w")) {
815 parseProperty(obj->mWidth);
816 } else if (0 == strcmp(key, "fillEnabled")) {
817 obj->mEnabled = GetBool();
818 } else if (0 == strcmp(key, "lc")) {
819 obj->mCapStyle = getLineCap();
820 } else if (0 == strcmp(key, "lj")) {
821 obj->mJoinStyle = getLineJoin();
822 } else if (0 == strcmp(key, "ml")) {
823 RAPIDJSON_ASSERT(PeekType() == kNumberType);
824 obj->mMeterLimit = GetDouble();
825 } else {
826 sgWarning<<"Stroke property skipped = "<<key;
827 Skip(key);
828 }
829 }
830 return obj;
831}
832
833void LottieParser::parseArrayValue(LottieColor &color)
834{
835 float val[4];
836 int i=0;
837 while (NextArrayValue()) {
838 val[i++] = GetDouble();
839 }
840
841 color.r = val[0];
842 color.g = val[1];
843 color.b = val[2];
844}
845
846void LottieParser::parseArrayValue(SGPointF &pt)
847{
848 float val[4];
849 int i=0;
850 while (NextArrayValue()) {
851 val[i++] = GetDouble();
852 }
853// sgDebug<<"Value parsed as point / size"<<i;
854
855 pt.setX(val[0]);
856 pt.setY(val[1]);
857}
858
859void LottieParser::parseArrayValue(float &val)
860{
861 val = GetDouble();
862}
863
864void LottieParser::parseArrayValue(int &val)
865{
866 val = GetInt();
867}
868
869void LottieParser::parseArrayValue(std::vector<SGPointF> &v)
870{
871 RAPIDJSON_ASSERT(PeekType() == kArrayType);
872 EnterArray();
873 while (NextArrayValue()) {
874 RAPIDJSON_ASSERT(PeekType() == kArrayType);
875 EnterArray();
876 while (NextArrayValue()) {
877 v.push_back(SGPointF(GetDouble(), GetDouble()));
878 }
879 }
880}
881
882void LottieParser::getValue(SGPointF &pt)
883{
884 float val[4];
885 int i=0;
886 RAPIDJSON_ASSERT(PeekType() == kArrayType);
887 EnterArray();
888 while (NextArrayValue()) {
889 val[i++] = GetDouble();
890 }
891 pt.setX(val[0]);
892 pt.setY(val[1]);
893}
894
895void LottieParser::getValue(float &val)
896{
897 RAPIDJSON_ASSERT(PeekType() == kArrayType);
898 EnterArray();
899 while (NextArrayValue()) {
900 val = GetDouble();
901 }
902}
903
904void LottieParser::getValue(LottieColor &color)
905{
906 float val[4];
907 int i=0;
908 RAPIDJSON_ASSERT(PeekType() == kArrayType);
909 EnterArray();
910 while (NextArrayValue()) {
911 val[i++] = GetDouble();
912 }
913 color.r = val[0];
914 color.g = val[1];
915 color.b = val[2];
916}
917
918void LottieParser::getValue(int &val)
919{
920 RAPIDJSON_ASSERT(PeekType() == kNumberType);
921 val = GetInt();
922}
923
924void LottieParser::getValue(LottieShape &obj)
925{
926 /*
927 * The shape object could be wrapped by a array
928 * if its part of the keyframe object
929 */
930 bool arrayWrapper = (PeekType() == kArrayType);
931 if (arrayWrapper)
932 EnterArray();
933
934 RAPIDJSON_ASSERT(PeekType() == kObjectType);
935 EnterObject();
936 while (const char* key = NextObjectKey()) {
937 if (0 == strcmp(key, "i")) {
938 parseArrayValue(obj.mInPoint);
939 } else if (0 == strcmp(key, "o")) {
940 parseArrayValue(obj.mOutPoint);
941 } else if (0 == strcmp(key, "v")) {
942 parseArrayValue(obj.mVertices);
943 } else {
944 RAPIDJSON_ASSERT(0);
945 Skip(nullptr);
946 }
947 }
948 // exit properly from the array
949 if (arrayWrapper)
950 NextArrayValue();
951}
952
953SGPointF
954LottieParser::parseInperpolatorPoint()
955{
956 SGPointF cp;
957 RAPIDJSON_ASSERT(PeekType() == kObjectType);
958 EnterObject();
959 while(const char* key = NextObjectKey()) {
960 if (0 == strcmp(key, "x")) {
961 if (PeekType() == kNumberType) {
962 cp.setX(GetDouble());
963 } else {
964 RAPIDJSON_ASSERT(PeekType() == kArrayType);
965 EnterArray();
966 while (NextArrayValue()) {
967 cp.setX(GetDouble());
968 }
969 }
970 }
971 if (0 == strcmp(key, "y")) {
972 if (PeekType() == kNumberType) {
973 cp.setY(GetDouble());
974 } else {
975 RAPIDJSON_ASSERT(PeekType() == kArrayType);
976 EnterArray();
977 while (NextArrayValue()) {
978 cp.setY(GetDouble());
979 }
980 }
981 }
982 }
983 return cp;
984}
985
986/*
987 * https://github.com/airbnb/lottie-web/blob/master/docs/json/properties/multiDimensionalKeyframed.json
988 */
989template<typename T>
990void LottieParser::parseKeyFrame(LottieAnimInfo<T> &obj)
991{
992 EnterObject();
993 LottieKeyFrame<T> keyframe;
994 SGPointF inTangent;
995 SGPointF outTangent;
996 const char *interpolatorKey = nullptr;
997 while (const char* key = NextObjectKey()) {
998 if (0 == strcmp(key, "i")) {
999 inTangent = parseInperpolatorPoint();
1000 } else if (0 == strcmp(key, "o")) {
1001 outTangent = parseInperpolatorPoint();
1002 } else if (0 == strcmp(key, "n")) {
1003 if (PeekType() == kStringType) {
1004 interpolatorKey = GetString();
1005 } else {
1006 RAPIDJSON_ASSERT(PeekType() == kArrayType);
1007 EnterArray();
1008 while (NextArrayValue()) {
1009 RAPIDJSON_ASSERT(PeekType() == kStringType);
1010 interpolatorKey = GetString();
1011 }
1012 }
1013 continue;
1014 } else if (0 == strcmp(key, "t")) {
1015 keyframe.mStartFrame = GetDouble();
1016 } else if (0 == strcmp(key, "s")) {
1017 getValue(keyframe.mStartValue);
1018 continue;
1019 } else if (0 == strcmp(key, "e")) {
1020 getValue(keyframe.mEndValue);
1021 continue;
1022 } else if (0 == strcmp(key, "ti")) {
1023 keyframe.mPathKeyFrame = true;
1024 getValue(keyframe.mInTangent);
1025 continue;
1026 } else if (0 == strcmp(key, "to")) {
1027 keyframe.mPathKeyFrame = true;
1028 getValue(keyframe.mOutTangent);
1029 continue;
1030 } else {
1031 sgDebug<<"key frame property skipped = "<<key;
1032 Skip(key);
1033 }
1034 }
1035
1036 if (!obj.mKeyFrames.empty()) {
1037 // update the endFrame value of current keyframe
1038 obj.mKeyFrames.back().mEndFrame = keyframe.mStartFrame;
1039 }
1040
1041 // Try to find the interpolator from cache
1042 if (interpolatorKey) {
1043 auto search = compRef->mInterpolatorCache.find(interpolatorKey);
1044 if (search != compRef->mInterpolatorCache.end()) {
1045 keyframe.mInterpolator = search->second;
1046 } else {
1047 keyframe.mInterpolator = new LottieInterpolater();
1048 keyframe.mInterpolator->mInTangent = inTangent;
1049 keyframe.mInterpolator->mOutTangent = outTangent;
1050 compRef->mInterpolatorCache[interpolatorKey] = keyframe.mInterpolator;
1051 }
1052 } else {
1053 /* this is the last key frame just skip it */
1054 return;
1055 }
1056 obj.mKeyFrames.push_back(keyframe);
1057}
1058
1059/*
1060 * https://github.com/airbnb/lottie-web/blob/master/docs/json/properties/shapeKeyframed.json
1061 */
1062
1063/*
1064 * https://github.com/airbnb/lottie-web/blob/master/docs/json/properties/shape.json
1065 */
1066void
1067LottieParser::parseShapeProperty(LottieAnimatable<LottieShape> &obj)
1068{
1069 EnterObject();
1070 while (const char* key = NextObjectKey()) {
1071 if (0 == strcmp(key, "k")) {
1072 if (PeekType() == kArrayType) {
1073 EnterArray();
1074 while (NextArrayValue()) {
1075 RAPIDJSON_ASSERT(PeekType() == kObjectType);
1076 if (!obj.mAnimInfo)
1077 obj.mAnimInfo = new LottieAnimInfo<LottieShape>();
1078 parseKeyFrame(*obj.mAnimInfo);
1079 }
1080 } else {
1081 getValue(obj.mValue);
1082 }
1083 } else {
1084 sgDebug<<"shape property ignored = "<<key;
1085 Skip(nullptr);
1086 }
1087 }
1088}
1089
1090/*
1091 * https://github.com/airbnb/lottie-web/tree/master/docs/json/properties
1092 */
1093template<typename T>
1094void LottieParser::parseProperty(LottieAnimatable<T> &obj)
1095{
1096 EnterObject();
1097 while (const char* key = NextObjectKey()) {
1098 if (0 == strcmp(key, "k")) {
1099 if (PeekType() == kNumberType) {
1100 /*single value property with no animation*/
1101 parseArrayValue(obj.mValue);
1102 } else {
1103 RAPIDJSON_ASSERT(PeekType() == kArrayType);
1104 EnterArray();
1105 while (NextArrayValue()) {
1106 /* property with keyframe info*/
1107 if (PeekType() == kObjectType) {
1108 if (!obj.mAnimInfo)
1109 obj.mAnimInfo = new LottieAnimInfo<T>();
1110 parseKeyFrame(*obj.mAnimInfo);
1111 } else {
1112 /* Read before modifying.
1113 * as there is no way of knowing if the
1114 * value of the array is either array of numbers
1115 * or array of object without entering the array
1116 * thats why this hack is there
1117 */
1118 RAPIDJSON_ASSERT(PeekType() == kNumberType);
1119 /*multi value property with no animation*/
1120 parseArrayValue(obj.mValue);
1121 /*break here as we already reached end of array*/
1122 break;
1123 }
1124 }
1125 }
1126 } else if (0 == strcmp(key, "ix")){
1127 RAPIDJSON_ASSERT(PeekType() == kNumberType);
1128 obj.mPropertyIndex = GetInt();
1129 } else {
1130 Skip(key);
1131 }
1132 }
1133}
1134
1135class LottieObjectInspector : public LottieObjectVisitor
1136{
1137public:
1138 void visit(LottieComposition *obj) {
1139 sgDebug<<"[COMP: START]";
1140 }
1141 void visit(LottieLayer *obj) {
1142 sgDebug<<"[LAYER: "<<"type: "<<obj->mGroupType<<" id: "<<obj->mId<<" parent: "<<obj->mParentId;
1143 }
1144 void visit(LottieTransform *) {
1145 sgDebug<<"[TRANSFORM: ]";
1146 }
1147 void visit(LottieShapeGroup *obj) {
1148 sgDebug<<"[SHAPE GROP: START]";
1149 }
1150 void visit(LottieShapeObject *) {
1151 sgDebug<<"[SHAPE: ]";
1152 }
1153 void visit(LottieRectObject *) {
1154 sgDebug<<"[RECT: ]";
1155 }
1156 void visit(LottieEllipseObject *) {
1157 sgDebug<<"[ELLIPSE: ]";
1158 }
1159 void visit(LottieTrimObject *) {
1160 sgDebug<<"[TRIM: ]";
1161 }
1162 void visit(LottieRepeaterObject *) {
1163 sgDebug<<"[REPEATER: ]";
1164 }
1165 void visit(LottieFillObject *) {
1166 sgDebug<<"[FILL: ]";
1167 }
1168 void visit(LottieStrokeObject *) {
1169 sgDebug<<"[STROKE: ]";
1170 }
1171 void visitChildren(LottieGroupObject *obj) {
1172 for(auto child :obj->mChildren)
1173 child->accept(this);
1174 }
1175};
1176
1177SGJson::SGJson(const char *data)
1178{
1179 LottieParser r(const_cast<char *>(data));
1180
1181 LottieComposition *comp = r.parseComposition();
1182 LottieObjectInspector inspector;
1183 comp->accept(&inspector);
1184}
1185
1186RAPIDJSON_DIAG_POP
diff --git a/ssg/src/lottie/meson.build b/ssg/src/lottie/meson.build
index fa20573562..7307ed8908 100644
--- a/ssg/src/lottie/meson.build
+++ b/ssg/src/lottie/meson.build
@@ -1,7 +1,8 @@
1ssg_sources_lottie = [] 1ssg_sources_lottie = []
2 2
3ssg_lottie_file = ['lottiedrawable.cpp', 3ssg_lottie_file = ['lottiedrawable.cpp',
4 'jsontest.cpp' 4 'lottieparser.cpp',
5 'lottiemodel.cpp'
5 ] 6 ]
6 7
7foreach file: ssg_lottie_file 8foreach file: ssg_lottie_file