1 /*
2  * Copyright (c) 2024 the ThorVG project. All rights reserved.
3 
4  * Permission is hereby granted, free of charge, to any person obtaining a copy
5  * of this software and associated documentation files (the "Software"), to deal
6  * in the Software without restriction, including without limitation the rights
7  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8  * copies of the Software, and to permit persons to whom the Software is
9  * furnished to do so, subject to the following conditions:
10 
11  * The above copyright notice and this permission notice shall be included in all
12  * copies or substantial portions of the Software.
13 
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20  * SOFTWARE.
21  */
22 
23 #include "../../lv_conf_internal.h"
24 #if LV_USE_THORVG_INTERNAL
25 
26 
27 #include "tvgMath.h"
28 #include "tvgCompressor.h"
29 #include "tvgLottieModel.h"
30 #include "tvgLottieExpressions.h"
31 
32 #ifdef THORVG_LOTTIE_EXPRESSIONS_SUPPORT
33 
34 /************************************************************************/
35 /* Internal Class Implementation                                        */
36 /************************************************************************/
37 
38 struct ExpContent
39 {
40     LottieExpression* exp;
41     LottieObject* obj;
42     float frameNo;
43 };
44 
45 static jerry_value_t _content(const jerry_call_info_t* info, const jerry_value_t args[], const jerry_length_t argsCnt);
46 
47 //reserved expressions specifiers
48 static const char* EXP_NAME = "name";
49 static const char* EXP_CONTENT = "content";
50 static const char* EXP_WIDTH = "width";
51 static const char* EXP_HEIGHT = "height";
52 static const char* EXP_CYCLE = "cycle";
53 static const char* EXP_PINGPONG = "pingpong";
54 static const char* EXP_OFFSET = "offset";
55 static const char* EXP_CONTINUE = "continue";
56 static const char* EXP_TIME = "time";
57 static const char* EXP_VALUE = "value";
58 static const char* EXP_INDEX = "index";
59 static const char* EXP_EFFECT= "effect";
60 
61 static LottieExpressions* exps = nullptr;   //singleton instance engine
62 
63 
_expcontent(LottieExpression * exp,float frameNo,LottieObject * obj)64 static ExpContent* _expcontent(LottieExpression* exp, float frameNo, LottieObject* obj)
65 {
66     auto data = (ExpContent*)malloc(sizeof(ExpContent));
67     data->exp = exp;
68     data->frameNo = frameNo;
69     data->obj = obj;
70     return data;
71 }
72 
73 
contentFree(void * native_p,struct jerry_object_native_info_t * info_p)74 static void contentFree(void *native_p, struct jerry_object_native_info_t *info_p)
75 {
76     free(native_p);
77 }
78 
79 static jerry_object_native_info_t freeCb {contentFree, 0, 0};
80 static uint32_t engineRefCnt = 0;  //Expressions Engine reference count
81 
82 
_name(jerry_value_t args)83 static char* _name(jerry_value_t args)
84 {
85     auto arg0 = jerry_value_to_string(args);
86     auto len = jerry_string_length(arg0);
87     auto name = (jerry_char_t*)malloc(len * sizeof(jerry_char_t) + 1);
88     jerry_string_to_buffer(arg0, JERRY_ENCODING_UTF8, name, len);
89     name[len] = '\0';
90     jerry_value_free(arg0);
91     return (char*) name;
92 }
93 
94 
_idByName(jerry_value_t args)95 static unsigned long _idByName(jerry_value_t args)
96 {
97     auto name = _name(args);
98     auto id = djb2Encode(name);
99     free(name);
100     return id;
101 }
102 
103 
_toComp(const jerry_call_info_t * info,const jerry_value_t args[],const jerry_length_t argsCnt)104 static jerry_value_t _toComp(const jerry_call_info_t* info, const jerry_value_t args[], const jerry_length_t argsCnt)
105 {
106     TVGLOG("LOTTIE", "toComp is not supported in expressions!");
107 
108     return jerry_undefined();
109 }
110 
111 
_value(float frameNo,LottieProperty * property)112 static jerry_value_t _value(float frameNo, LottieProperty* property)
113 {
114     switch (property->type) {
115         case LottieProperty::Type::Point: {
116             auto value = jerry_object();
117             auto pos = (*static_cast<LottiePoint*>(property))(frameNo);
118             auto val1 = jerry_number(pos.x);
119             auto val2 = jerry_number(pos.y);
120             jerry_object_set_index(value, 0, val1);
121             jerry_object_set_index(value, 1, val2);
122             jerry_value_free(val1);
123             jerry_value_free(val2);
124             return value;
125         }
126         case LottieProperty::Type::Float: {
127             return jerry_number((*static_cast<LottieFloat*>(property))(frameNo));
128         }
129         case LottieProperty::Type::Opacity: {
130             return jerry_number((*static_cast<LottieOpacity*>(property))(frameNo));
131         }
132         case LottieProperty::Type::PathSet: {
133             auto value = jerry_object();
134             jerry_object_set_native_ptr(value, nullptr, property);
135             return value;
136         }
137         case LottieProperty::Type::Position: {
138             auto value = jerry_object();
139             auto pos = (*static_cast<LottiePosition*>(property))(frameNo);
140             auto val1 = jerry_number(pos.x);
141             auto val2 = jerry_number(pos.y);
142             jerry_object_set_index(value, 0, val1);
143             jerry_object_set_index(value, 1, val2);
144             jerry_value_free(val1);
145             jerry_value_free(val2);
146             return value;
147         }
148         default: {
149             TVGERR("LOTTIE", "Non supported type for value? = %d", (int) property->type);
150         }
151     }
152     return jerry_undefined();
153 }
154 
155 
_buildTransform(jerry_value_t context,float frameNo,LottieTransform * transform)156 static void _buildTransform(jerry_value_t context, float frameNo, LottieTransform* transform)
157 {
158     if (!transform) return;
159 
160     auto obj = jerry_object();
161     jerry_object_set_sz(context, "transform", obj);
162 
163     auto anchorPoint = _value(frameNo, &transform->anchor);
164     jerry_object_set_sz(obj, "anchorPoint", anchorPoint);
165     jerry_value_free(anchorPoint);
166 
167     auto position = _value(frameNo, &transform->position);
168     jerry_object_set_sz(obj, "position", position);
169     jerry_value_free(position);
170 
171     auto scale = _value(frameNo, &transform->scale);
172     jerry_object_set_sz(obj, "scale", scale);
173     jerry_value_free(scale);
174 
175     auto rotation = _value(frameNo, &transform->rotation);
176     jerry_object_set_sz(obj, "rotation", rotation);
177     jerry_value_free(rotation);
178 
179     auto opacity = _value(frameNo, &transform->opacity);
180     jerry_object_set_sz(obj, "opacity", opacity);
181     jerry_value_free(opacity);
182 
183     jerry_value_free(obj);
184 }
185 
186 
_buildGroup(LottieGroup * group,float frameNo)187 static jerry_value_t _buildGroup(LottieGroup* group, float frameNo)
188 {
189     auto obj = jerry_function_external(_content);
190 
191     //attach a transform
192     for (auto c = group->children.begin(); c < group->children.end(); ++c) {
193         if ((*c)->type == LottieObject::Type::Transform) {
194             _buildTransform(obj, frameNo, static_cast<LottieTransform*>(*c));
195             break;
196         }
197     }
198     jerry_object_set_native_ptr(obj, &freeCb, _expcontent(nullptr, frameNo, group));
199     jerry_object_set_sz(obj, EXP_CONTENT, obj);
200     return obj;
201 }
202 
203 
_buildPolystar(LottiePolyStar * polystar,float frameNo)204 static jerry_value_t _buildPolystar(LottiePolyStar* polystar, float frameNo)
205 {
206     auto obj = jerry_object();
207     auto position = jerry_object();
208     jerry_object_set_native_ptr(position, nullptr, &polystar->position);
209     jerry_object_set_sz(obj, "position", position);
210     jerry_value_free(position);
211     auto innerRadius = jerry_number(polystar->innerRadius(frameNo));
212     jerry_object_set_sz(obj, "innerRadius", innerRadius);
213     jerry_value_free(innerRadius);
214     auto outerRadius = jerry_number(polystar->outerRadius(frameNo));
215     jerry_object_set_sz(obj, "outerRadius", outerRadius);
216     jerry_value_free(outerRadius);
217     auto innerRoundness = jerry_number(polystar->innerRoundness(frameNo));
218     jerry_object_set_sz(obj, "innerRoundness", innerRoundness);
219     jerry_value_free(innerRoundness);
220     auto outerRoundness = jerry_number(polystar->outerRoundness(frameNo));
221     jerry_object_set_sz(obj, "outerRoundness", outerRoundness);
222     jerry_value_free(outerRoundness);
223     auto rotation = jerry_number(polystar->rotation(frameNo));
224     jerry_object_set_sz(obj, "rotation", rotation);
225     jerry_value_free(rotation);
226     auto ptsCnt = jerry_number(polystar->ptsCnt(frameNo));
227     jerry_object_set_sz(obj, "points", ptsCnt);
228     jerry_value_free(ptsCnt);
229 
230     return obj;
231 }
232 
233 
_buildTrimpath(LottieTrimpath * trimpath,float frameNo)234 static jerry_value_t _buildTrimpath(LottieTrimpath* trimpath, float frameNo)
235 {
236     jerry_value_t obj = jerry_object();
237     auto start = jerry_number(trimpath->start(frameNo));
238     jerry_object_set_sz(obj, "start", start);
239     jerry_value_free(start);
240     auto end = jerry_number(trimpath->end(frameNo));
241     jerry_object_set_sz(obj, "end", end);
242     jerry_value_free(end);
243     auto offset = jerry_number(trimpath->offset(frameNo));
244     jerry_object_set_sz(obj, "offset", end);
245     jerry_value_free(offset);
246 
247     return obj;
248 }
249 
250 
_buildLayer(jerry_value_t context,float frameNo,LottieLayer * layer,LottieLayer * comp,LottieExpression * exp)251 static void _buildLayer(jerry_value_t context, float frameNo, LottieLayer* layer, LottieLayer* comp, LottieExpression* exp)
252 {
253     auto width = jerry_number(layer->w);
254     jerry_object_set_sz(context, EXP_WIDTH, width);
255     jerry_value_free(width);
256 
257     auto height = jerry_number(layer->h);
258     jerry_object_set_sz(context, EXP_HEIGHT, height);
259     jerry_value_free(height);
260 
261     auto index = jerry_number(layer->idx);
262     jerry_object_set_sz(context, EXP_INDEX, index);
263     jerry_value_free(index);
264 
265     auto parent = jerry_object();
266     jerry_object_set_native_ptr(parent, nullptr, layer->parent);
267     jerry_object_set_sz(context, "parent", parent);
268     jerry_value_free(parent);
269 
270     auto hasParent = jerry_boolean(layer->parent ? true : false);
271     jerry_object_set_sz(context, "hasParent", hasParent);
272     jerry_value_free(hasParent);
273 
274     auto inPoint = jerry_number(layer->inFrame);
275     jerry_object_set_sz(context, "inPoint", inPoint);
276     jerry_value_free(inPoint);
277 
278     auto outPoint = jerry_number(layer->outFrame);
279     jerry_object_set_sz(context, "outPoint", outPoint);
280     jerry_value_free(outPoint);
281 
282     //TODO: Confirm exp->layer->comp->timeAtFrame() ?
283     auto startTime = jerry_number(exp->comp->timeAtFrame(layer->startFrame));
284     jerry_object_set_sz(context, "startTime", startTime);
285     jerry_value_free(startTime);
286 
287     auto hasVideo = jerry_boolean(false);
288     jerry_object_set_sz(context, "hasVideo", hasVideo);
289     jerry_value_free(hasVideo);
290 
291     auto hasAudio = jerry_boolean(false);
292     jerry_object_set_sz(context, "hasAudio", hasAudio);
293     jerry_value_free(hasAudio);
294 
295     //active, #current in the animation range?
296 
297     auto enabled = jerry_boolean(!layer->hidden);
298     jerry_object_set_sz(context, "enabled", enabled);
299     jerry_value_free(enabled);
300 
301     auto audioActive = jerry_boolean(false);
302     jerry_object_set_sz(context, "audioActive", audioActive);
303     jerry_value_free(audioActive);
304 
305     //sampleImage(point, radius = [.5, .5], postEffect=true, t=time)
306 
307     _buildTransform(context, frameNo, layer->transform);
308 
309     //audioLevels, #the value of the Audio Levels property of the layer in decibels
310 
311     auto timeRemap = jerry_object();
312     jerry_object_set_native_ptr(timeRemap, nullptr, &layer->timeRemap);
313     jerry_object_set_sz(context, "timeRemap", timeRemap);
314     jerry_value_free(timeRemap);
315 
316     //marker.key(index)
317     //marker.key(name)
318     //marker.nearestKey(t)
319     //marker.numKeys
320 
321     auto name = jerry_string_sz(layer->name);
322     jerry_object_set_sz(context, EXP_NAME, name);
323     jerry_value_free(name);
324 
325     auto toComp = jerry_function_external(_toComp);
326     jerry_object_set_sz(context, "toComp", toComp);
327     jerry_object_set_native_ptr(toComp, nullptr, comp);
328     jerry_value_free(toComp);
329 
330     //content("name"), #look for the named property from a layer
331     auto content = jerry_function_external(_content);
332     jerry_object_set_sz(context, EXP_CONTENT, content);
333     jerry_object_set_native_ptr(content, &freeCb, _expcontent(exp, frameNo, layer));
334     jerry_value_free(content);
335 }
336 
337 
_addsub(const jerry_value_t args[],float addsub)338 static jerry_value_t _addsub(const jerry_value_t args[], float addsub)
339 {
340     auto n1 = jerry_value_is_number(args[0]);
341     auto n2 = jerry_value_is_number(args[1]);
342 
343     //1d + 1d
344     if (n1 && n2) return jerry_number(jerry_value_as_number(args[0]) + addsub * jerry_value_as_number(args[1]));
345 
346     auto val1 = jerry_object_get_index(args[n1 ? 1 : 0], 0);
347     auto val2 = jerry_object_get_index(args[n1 ? 1 : 0], 1);
348     auto x = jerry_value_as_number(val1);
349     auto y = jerry_value_as_number(val2);
350     jerry_value_free(val1);
351     jerry_value_free(val2);
352 
353     //2d + 1d
354     if (n1 || n2) {
355         auto secondary = n1 ? 0 : 1;
356         auto val3 = jerry_value_as_number(args[secondary]);
357         if (secondary == 0) x = (x * addsub) + val3;
358         else x += (addsub * val3);
359     //2d + 2d
360     } else {
361         auto val3 = jerry_object_get_index(args[1], 0);
362         auto val4 = jerry_object_get_index(args[1], 1);
363         x += (addsub * jerry_value_as_number(val3));
364         y += (addsub * jerry_value_as_number(val4));
365         jerry_value_free(val3);
366         jerry_value_free(val4);
367     }
368 
369     auto obj = jerry_object();
370     val1 = jerry_number(x);
371     val2 = jerry_number(y);
372     jerry_object_set_index(obj, 0, val1);
373     jerry_object_set_index(obj, 1, val2);
374     jerry_value_free(val1);
375     jerry_value_free(val2);
376 
377     return obj;
378 }
379 
380 
_muldiv(const jerry_value_t arg1,float arg2)381 static jerry_value_t _muldiv(const jerry_value_t arg1, float arg2)
382 {
383     //1d
384     if (jerry_value_is_number(arg1)) return jerry_number(jerry_value_as_number(arg1) * arg2);
385 
386     //2d
387     auto val1 = jerry_object_get_index(arg1, 0);
388     auto val2 = jerry_object_get_index(arg1, 1);
389     auto x = jerry_value_as_number(val1) * arg2;
390     auto y = jerry_value_as_number(val2) * arg2;
391 
392     jerry_value_free(val1);
393     jerry_value_free(val2);
394 
395     auto obj = jerry_object();
396     val1 = jerry_number(x);
397     val2 = jerry_number(y);
398     jerry_object_set_index(obj, 0, val1);
399     jerry_object_set_index(obj, 1, val2);
400     jerry_value_free(val1);
401     jerry_value_free(val2);
402 
403     return obj;
404 }
405 
406 
_add(const jerry_call_info_t * info,const jerry_value_t args[],const jerry_length_t argsCnt)407 static jerry_value_t _add(const jerry_call_info_t* info, const jerry_value_t args[], const jerry_length_t argsCnt)
408 {
409     return _addsub(args, 1.0f);
410 }
411 
412 
_sub(const jerry_call_info_t * info,const jerry_value_t args[],const jerry_length_t argsCnt)413 static jerry_value_t _sub(const jerry_call_info_t* info, const jerry_value_t args[], const jerry_length_t argsCnt)
414 {
415     return _addsub(args, -1.0f);
416 }
417 
418 
_mul(const jerry_call_info_t * info,const jerry_value_t args[],const jerry_length_t argsCnt)419 static jerry_value_t _mul(const jerry_call_info_t* info, const jerry_value_t args[], const jerry_length_t argsCnt)
420 {
421     return _muldiv(args[0], jerry_value_as_number(args[1]));
422 }
423 
424 
_div(const jerry_call_info_t * info,const jerry_value_t args[],const jerry_length_t argsCnt)425 static jerry_value_t _div(const jerry_call_info_t* info, const jerry_value_t args[], const jerry_length_t argsCnt)
426 {
427     return _muldiv(args[0], 1.0f / jerry_value_as_number(args[1]));
428 }
429 
430 
_interp(float t,const jerry_value_t args[],int argsCnt)431 static jerry_value_t _interp(float t, const jerry_value_t args[], int argsCnt)
432 {
433     auto tMin = 0.0f;
434     auto tMax = 1.0f;
435     int idx = 0;
436 
437     tMin = jerry_value_as_number(args[1]);
438     tMax = jerry_value_as_number(args[2]);
439     idx += 2;
440 
441     t = (t - tMin) / (tMax - tMin);
442     if (t < 0) t = 0.0f;
443     else if (t > 1) t = 1.0f;
444 
445     //2d
446     if (jerry_value_is_object(args[idx + 1]) && jerry_value_is_object(args[idx + 2])) {
447         auto val1 = jerry_object_get_index(args[idx + 1], 0);
448         auto val2 = jerry_object_get_index(args[idx + 1], 1);
449         auto val3 = jerry_object_get_index(args[idx + 2], 0);
450         auto val4 = jerry_object_get_index(args[idx + 2], 1);
451 
452         Point pt1 = {(float)jerry_value_as_number(val1),  (float)jerry_value_as_number(val2)};
453         Point pt2 = {(float)jerry_value_as_number(val3),  (float)jerry_value_as_number(val4)};
454         Point ret;
455         ret = lerp(pt1, pt2, t);
456 
457         jerry_value_free(val1);
458         jerry_value_free(val2);
459         jerry_value_free(val3);
460         jerry_value_free(val4);
461 
462         auto obj = jerry_object();
463         val1 = jerry_number(ret.x);
464         val2 = jerry_number(ret.y);
465         jerry_object_set_index(obj, 0, val1);
466         jerry_object_set_index(obj, 1, val2);
467         jerry_value_free(val1);
468         jerry_value_free(val2);
469 
470         return obj;
471     }
472 
473     //1d
474     auto val1 = (float) jerry_value_as_number(args[idx + 1]);
475     auto val2 = (float) jerry_value_as_number(args[idx + 2]);
476     return jerry_number(lerp(val1, val2, t));
477 }
478 
479 
_linear(const jerry_call_info_t * info,const jerry_value_t args[],const jerry_length_t argsCnt)480 static jerry_value_t _linear(const jerry_call_info_t* info, const jerry_value_t args[], const jerry_length_t argsCnt)
481 {
482     auto t = (float) jerry_value_as_number(args[0]);
483     return _interp(t, args, jerry_value_as_uint32(argsCnt));
484 }
485 
486 
_ease(const jerry_call_info_t * info,const jerry_value_t args[],const jerry_length_t argsCnt)487 static jerry_value_t _ease(const jerry_call_info_t* info, const jerry_value_t args[], const jerry_length_t argsCnt)
488 {
489     auto t = (float) jerry_value_as_number(args[0]);
490     t = (t < 0.5) ? (4 * t * t * t) : (1.0f - pow(-2.0f * t + 2.0f, 3) * 0.5f);
491     return _interp(t, args, jerry_value_as_uint32(argsCnt));
492 }
493 
494 
495 
_easeIn(const jerry_call_info_t * info,const jerry_value_t args[],const jerry_length_t argsCnt)496 static jerry_value_t _easeIn(const jerry_call_info_t* info, const jerry_value_t args[], const jerry_length_t argsCnt)
497 {
498     auto t = (float) jerry_value_as_number(args[0]);
499     t = t * t * t;
500     return _interp(t, args, jerry_value_as_uint32(argsCnt));
501 }
502 
503 
_easeOut(const jerry_call_info_t * info,const jerry_value_t args[],const jerry_length_t argsCnt)504 static jerry_value_t _easeOut(const jerry_call_info_t* info, const jerry_value_t args[], const jerry_length_t argsCnt)
505 {
506     auto t = (float) jerry_value_as_number(args[0]);
507     t = 1.0f - pow(1.0f - t, 3);
508     return _interp(t, args, jerry_value_as_uint32(argsCnt));
509 }
510 
511 
_clamp(const jerry_call_info_t * info,const jerry_value_t args[],const jerry_length_t argsCnt)512 static jerry_value_t _clamp(const jerry_call_info_t* info, const jerry_value_t args[], const jerry_length_t argsCnt)
513 {
514     auto num = jerry_value_as_number(args[0]);
515     auto limit1 = jerry_value_as_number(args[1]);
516     auto limit2 = jerry_value_as_number(args[2]);
517 
518     //clamping
519     if (num < limit1) num = limit1;
520     if (num > limit2) num = limit2;
521 
522     return jerry_number(num);
523 }
524 
525 
_dot(const jerry_call_info_t * info,const jerry_value_t args[],const jerry_length_t argsCnt)526 static jerry_value_t _dot(const jerry_call_info_t* info, const jerry_value_t args[], const jerry_length_t argsCnt)
527 {
528     auto val1 = jerry_object_get_index(args[0], 0);
529     auto val2 = jerry_object_get_index(args[0], 1);
530     auto val3 = jerry_object_get_index(args[1], 0);
531     auto val4 = jerry_object_get_index(args[1], 1);
532 
533     auto x = jerry_value_as_number(val1) * jerry_value_as_number(val3);
534     auto y = jerry_value_as_number(val2) * jerry_value_as_number(val4);
535 
536     jerry_value_free(val1);
537     jerry_value_free(val2);
538     jerry_value_free(val3);
539     jerry_value_free(val4);
540 
541     return jerry_number(x + y);
542 }
543 
544 
_cross(const jerry_call_info_t * info,const jerry_value_t args[],const jerry_length_t argsCnt)545 static jerry_value_t _cross(const jerry_call_info_t* info, const jerry_value_t args[], const jerry_length_t argsCnt)
546 {
547     auto val1 = jerry_object_get_index(args[0], 0);
548     auto val2 = jerry_object_get_index(args[0], 1);
549     auto val3 = jerry_object_get_index(args[1], 0);
550     auto val4 = jerry_object_get_index(args[1], 1);
551 
552     auto x = jerry_value_as_number(val1) * jerry_value_as_number(val4);
553     auto y = jerry_value_as_number(val2) * jerry_value_as_number(val3);
554 
555     jerry_value_free(val1);
556     jerry_value_free(val2);
557     jerry_value_free(val3);
558     jerry_value_free(val4);
559 
560     return jerry_number(x - y);
561 }
562 
563 
_normalize(const jerry_call_info_t * info,const jerry_value_t args[],const jerry_length_t argsCnt)564 static jerry_value_t _normalize(const jerry_call_info_t* info, const jerry_value_t args[], const jerry_length_t argsCnt)
565 {
566     auto val1 = jerry_object_get_index(args[0], 0);
567     auto val2 = jerry_object_get_index(args[0], 1);
568     auto x = jerry_value_as_number(val1);
569     auto y = jerry_value_as_number(val2);
570 
571     jerry_value_free(val1);
572     jerry_value_free(val2);
573 
574     auto length = sqrtf(x * x + y * y);
575 
576     x /= length;
577     y /= length;
578 
579     auto obj = jerry_object();
580     val1 = jerry_number(x);
581     val2 = jerry_number(y);
582     jerry_object_set_index(obj, 0, val1);
583     jerry_object_set_index(obj, 0, val2);
584     jerry_value_free(val1);
585     jerry_value_free(val2);
586 
587     return obj;
588 }
589 
590 
_length(const jerry_call_info_t * info,const jerry_value_t args[],const jerry_length_t argsCnt)591 static jerry_value_t _length(const jerry_call_info_t* info, const jerry_value_t args[], const jerry_length_t argsCnt)
592 {
593     auto val1 = jerry_object_get_index(args[0], 0);
594     auto val2 = jerry_object_get_index(args[0], 1);
595     auto x = jerry_value_as_number(val1);
596     auto y = jerry_value_as_number(val2);
597 
598     jerry_value_free(val1);
599     jerry_value_free(val2);
600 
601     return jerry_number(sqrtf(x * x + y * y));
602 }
603 
604 
_random(const jerry_call_info_t * info,const jerry_value_t args[],const jerry_length_t argsCnt)605 static jerry_value_t _random(const jerry_call_info_t* info, const jerry_value_t args[], const jerry_length_t argsCnt)
606 {
607     auto val = (float)(rand() % 10000001);
608     return jerry_number(val * 0.0000001f);
609 }
610 
611 
_deg2rad(const jerry_call_info_t * info,const jerry_value_t args[],const jerry_length_t argsCnt)612 static jerry_value_t _deg2rad(const jerry_call_info_t* info, const jerry_value_t args[], const jerry_length_t argsCnt)
613 {
614     return jerry_number(deg2rad((float)jerry_value_as_number(args[0])));
615 }
616 
617 
_rad2deg(const jerry_call_info_t * info,const jerry_value_t args[],const jerry_length_t argsCnt)618 static jerry_value_t _rad2deg(const jerry_call_info_t* info, const jerry_value_t args[], const jerry_length_t argsCnt)
619 {
620     return jerry_number(rad2deg((float)jerry_value_as_number(args[0])));
621 }
622 
623 
_effect(const jerry_call_info_t * info,const jerry_value_t args[],const jerry_length_t argsCnt)624 static jerry_value_t _effect(const jerry_call_info_t* info, const jerry_value_t args[], const jerry_length_t argsCnt)
625 {
626     TVGLOG("LOTTIE", "effect is not supported in expressions!");
627 
628     return jerry_undefined();
629 }
630 
631 
_fromCompToSurface(const jerry_call_info_t * info,const jerry_value_t args[],const jerry_length_t argsCnt)632 static jerry_value_t _fromCompToSurface(const jerry_call_info_t* info, const jerry_value_t args[], const jerry_length_t argsCnt)
633 {
634     TVGLOG("LOTTIE", "fromCompToSurface is not supported in expressions!");
635 
636     return jerry_undefined();
637 }
638 
639 
_content(const jerry_call_info_t * info,const jerry_value_t args[],const jerry_length_t argsCnt)640 static jerry_value_t _content(const jerry_call_info_t* info, const jerry_value_t args[], const jerry_length_t argsCnt)
641 {
642     auto data = static_cast<ExpContent*>(jerry_object_get_native_ptr(info->function, &freeCb));
643     auto group = static_cast<LottieGroup*>(data->obj);
644     auto target = group->content(_idByName(args[0]));
645     if (!target) return jerry_undefined();
646 
647     //find the a path property(sh) in the group layer?
648     switch (target->type) {
649         case LottieObject::Group: return _buildGroup(static_cast<LottieGroup*>(target), data->frameNo);
650         case LottieObject::Path: {
651             jerry_value_t obj = jerry_object();
652             jerry_object_set_native_ptr(obj, nullptr, &static_cast<LottiePath*>(target)->pathset);
653             jerry_object_set_sz(obj, "path", obj);
654             return obj;
655         }
656         case LottieObject::Polystar: return _buildPolystar(static_cast<LottiePolyStar*>(target), data->frameNo);
657         case LottieObject::Trimpath: return _buildTrimpath(static_cast<LottieTrimpath*>(target), data->frameNo);
658         default: break;
659     }
660     return jerry_undefined();
661 }
662 
663 
_layer(const jerry_call_info_t * info,const jerry_value_t args[],const jerry_length_t argsCnt)664 static jerry_value_t _layer(const jerry_call_info_t* info, const jerry_value_t args[], const jerry_length_t argsCnt)
665 {
666     auto data = static_cast<ExpContent*>(jerry_object_get_native_ptr(info->function, &freeCb));
667     auto comp = static_cast<LottieLayer*>(data->obj);
668     LottieLayer* layer;
669 
670     //layer index
671     if (jerry_value_is_number(args[0])) {
672         auto idx = (uint16_t)jerry_value_as_int32(args[0]);
673         layer = comp->layerByIdx(idx);
674         jerry_value_free(idx);
675     //layer name
676     } else {
677         layer = comp->layerById(_idByName(args[0]));
678     }
679 
680     if (!layer) return jerry_undefined();
681 
682     auto obj = jerry_object();
683     jerry_object_set_native_ptr(obj, nullptr, layer);
684     _buildLayer(obj, data->frameNo, layer, comp, data->exp);
685 
686     return obj;
687 }
688 
689 
_nearestKey(const jerry_call_info_t * info,const jerry_value_t args[],const jerry_length_t argsCnt)690 static jerry_value_t _nearestKey(const jerry_call_info_t* info, const jerry_value_t args[], const jerry_length_t argsCnt)
691 {
692     auto exp = static_cast<LottieExpression*>(jerry_object_get_native_ptr(info->function, nullptr));
693     auto time = jerry_value_as_number(args[0]);
694     auto frameNo = exp->comp->frameAtTime(time);
695     auto index = jerry_number(exp->property->nearest(frameNo));
696 
697     auto obj = jerry_object();
698     jerry_object_set_sz(obj, EXP_INDEX, index);
699     jerry_value_free(index);
700 
701     return obj;
702 }
703 
_property(const jerry_call_info_t * info,const jerry_value_t args[],const jerry_length_t argsCnt)704 static jerry_value_t _property(const jerry_call_info_t* info, const jerry_value_t args[], const jerry_length_t argsCnt)
705 {
706     auto data = static_cast<ExpContent*>(jerry_object_get_native_ptr(info->function, &freeCb));
707     auto property = data->obj->property(jerry_value_as_int32(args[0]));
708     if (!property) return jerry_undefined();
709     return _value(data->frameNo, property);
710 }
711 
712 
_propertyGroup(const jerry_call_info_t * info,const jerry_value_t args[],const jerry_length_t argsCnt)713 static jerry_value_t _propertyGroup(const jerry_call_info_t* info, const jerry_value_t args[], const jerry_length_t argsCnt)
714 {
715     auto data = static_cast<ExpContent*>(jerry_object_get_native_ptr(info->function, &freeCb));
716     auto level = jerry_value_as_int32(args[0]);
717 
718     //intermediate group
719     if (level == 1) {
720         auto group = jerry_function_external(_property);
721         jerry_object_set_native_ptr(group, &freeCb, _expcontent(data->exp, data->frameNo, data->obj));
722         jerry_object_set_sz(group, "", group);
723         return group;
724     }
725 
726     TVGLOG("LOTTIE", "propertyGroup(%d)?", level);
727 
728     return jerry_undefined();
729 }
730 
731 
_valueAtTime(const jerry_call_info_t * info,const jerry_value_t args[],const jerry_length_t argsCnt)732 static jerry_value_t _valueAtTime(const jerry_call_info_t* info, const jerry_value_t args[], const jerry_length_t argsCnt)
733 {
734     auto exp = static_cast<LottieExpression*>(jerry_object_get_native_ptr(info->function, nullptr));
735     auto time = jerry_value_as_number(args[0]);
736     auto frameNo = exp->comp->frameAtTime(time);
737     return _value(frameNo, exp->property);
738 }
739 
740 
_velocity(float px,float cx,float py,float cy,float elapsed)741 static jerry_value_t _velocity(float px, float cx, float py, float cy, float elapsed)
742 {
743     float velocity[] = {(cx - px) / elapsed, (cy - py) / elapsed};
744     auto obj = jerry_object();
745     auto val1 = jerry_number(velocity[0]);
746     auto val2 = jerry_number(velocity[1]);
747     jerry_object_set_index(obj, 0, val1);
748     jerry_object_set_index(obj, 1, val2);
749     jerry_value_free(val1);
750     jerry_value_free(val2);
751     return obj;
752 }
753 
754 
_velocityAtTime(const jerry_call_info_t * info,const jerry_value_t args[],const jerry_length_t argsCnt)755 static jerry_value_t _velocityAtTime(const jerry_call_info_t* info, const jerry_value_t args[], const jerry_length_t argsCnt)
756 {
757     auto exp = static_cast<LottieExpression*>(jerry_object_get_native_ptr(info->function, nullptr));
758     auto time = jerry_value_as_number(args[0]);
759     auto frameNo = exp->comp->frameAtTime(time);
760     auto key = exp->property->nearest(frameNo);
761     auto pframe = exp->property->frameNo(key - 1);
762     auto cframe = exp->property->frameNo(key);
763     auto elapsed = (cframe - pframe) / (exp->comp->frameRate);
764 
765     //compute the velocity
766     switch (exp->property->type) {
767         case LottieProperty::Type::Point: {
768             auto prv = (*static_cast<LottiePoint*>(exp->property))(pframe);
769             auto cur = (*static_cast<LottiePoint*>(exp->property))(cframe);
770             return _velocity(prv.x, cur.x, prv.y, cur.y, elapsed);
771         }
772         case LottieProperty::Type::Position: {
773             auto prv = (*static_cast<LottiePosition*>(exp->property))(pframe);
774             auto cur = (*static_cast<LottiePosition*>(exp->property))(cframe);
775             return _velocity(prv.x, cur.x, prv.y, cur.y, elapsed);
776         }
777         case LottieProperty::Type::Float: {
778             auto prv = (*static_cast<LottieFloat*>(exp->property))(pframe);
779             auto cur = (*static_cast<LottieFloat*>(exp->property))(cframe);
780             auto velocity = (cur - prv) / elapsed;
781             return jerry_number(velocity);
782         }
783         default: TVGLOG("LOTTIE", "Non supported type for velocityAtTime?");
784     }
785     return jerry_undefined();
786 }
787 
788 
_speedAtTime(const jerry_call_info_t * info,const jerry_value_t args[],const jerry_length_t argsCnt)789 static jerry_value_t _speedAtTime(const jerry_call_info_t* info, const jerry_value_t args[], const jerry_length_t argsCnt)
790 {
791     auto exp = static_cast<LottieExpression*>(jerry_object_get_native_ptr(info->function, nullptr));
792     auto time = jerry_value_as_number(args[0]);
793     auto frameNo = exp->comp->frameAtTime(time);
794     auto key = exp->property->nearest(frameNo);
795     auto pframe = exp->property->frameNo(key - 1);
796     auto cframe = exp->property->frameNo(key);
797     auto elapsed = (cframe - pframe) / (exp->comp->frameRate);
798 
799     Point cur, prv;
800 
801     //compute the velocity
802     switch (exp->property->type) {
803         case LottieProperty::Type::Point: {
804             prv = (*static_cast<LottiePoint*>(exp->property))(pframe);
805             cur = (*static_cast<LottiePoint*>(exp->property))(cframe);
806             break;
807         }
808         case LottieProperty::Type::Position: {
809             prv = (*static_cast<LottiePosition*>(exp->property))(pframe);
810             cur = (*static_cast<LottiePosition*>(exp->property))(cframe);
811             break;
812         }
813         default: {
814             TVGLOG("LOTTIE", "Non supported type for speedAtTime?");
815             return jerry_undefined();
816         }
817     }
818 
819     auto speed = sqrtf(pow(cur.x - prv.x, 2) + pow(cur.y - prv.y, 2)) / elapsed;
820     auto obj = jerry_number(speed);
821     return obj;
822 }
823 
824 
_loopOutCommon(LottieExpression * exp,const jerry_value_t args[],const jerry_length_t argsCnt)825 static bool _loopOutCommon(LottieExpression* exp, const jerry_value_t args[], const jerry_length_t argsCnt)
826 {
827     exp->loop.mode = LottieExpression::LoopMode::OutCycle;
828 
829     if (argsCnt > 0) {
830         auto name = _name(args[0]);
831         if (!strcmp(name, EXP_CYCLE)) exp->loop.mode = LottieExpression::LoopMode::OutCycle;
832         else if (!strcmp(name, EXP_PINGPONG)) exp->loop.mode = LottieExpression::LoopMode::OutPingPong;
833         else if (!strcmp(name, EXP_OFFSET)) exp->loop.mode = LottieExpression::LoopMode::OutOffset;
834         else if (!strcmp(name, EXP_CONTINUE)) exp->loop.mode = LottieExpression::LoopMode::OutContinue;
835         free(name);
836     }
837 
838     if (exp->loop.mode != LottieExpression::LoopMode::OutCycle && exp->loop.mode != LottieExpression::LoopMode::OutPingPong) {
839         TVGLOG("LOTTIE", "Not supported loopOut type = %d", exp->loop.mode);
840         return false;
841     }
842 
843     return true;
844 }
845 
846 
_loopOut(const jerry_call_info_t * info,const jerry_value_t args[],const jerry_length_t argsCnt)847 static jerry_value_t _loopOut(const jerry_call_info_t* info, const jerry_value_t args[], const jerry_length_t argsCnt)
848 {
849     auto exp = static_cast<LottieExpression*>(jerry_object_get_native_ptr(info->function, nullptr));
850 
851     if (!_loopOutCommon(exp, args, argsCnt)) return jerry_undefined();
852 
853     if (argsCnt > 1) exp->loop.key = jerry_value_as_int32(args[1]);
854 
855     auto obj = jerry_object();
856     jerry_object_set_native_ptr(obj, nullptr, exp->property);
857     return obj;
858 }
859 
860 
_loopOutDuration(const jerry_call_info_t * info,const jerry_value_t args[],const jerry_length_t argsCnt)861 static jerry_value_t _loopOutDuration(const jerry_call_info_t* info, const jerry_value_t args[], const jerry_length_t argsCnt)
862 {
863     auto exp = static_cast<LottieExpression*>(jerry_object_get_native_ptr(info->function, nullptr));
864 
865     if (!_loopOutCommon(exp, args, argsCnt)) return jerry_undefined();
866 
867     if (argsCnt > 1) {
868         exp->loop.in = exp->comp->frameAtTime((float)jerry_value_as_int32(args[1]));
869     }
870 
871     auto obj = jerry_object();
872     jerry_object_set_native_ptr(obj, nullptr, exp->property);
873     return obj;
874 }
875 
876 
_loopInCommon(LottieExpression * exp,const jerry_value_t args[],const jerry_length_t argsCnt)877 static bool _loopInCommon(LottieExpression* exp, const jerry_value_t args[], const jerry_length_t argsCnt)
878 {
879     exp->loop.mode = LottieExpression::LoopMode::InCycle;
880 
881     if (argsCnt > 0) {
882         auto name = _name(args[0]);
883         if (!strcmp(name, EXP_CYCLE)) exp->loop.mode = LottieExpression::LoopMode::InCycle;
884         else if (!strcmp(name, EXP_PINGPONG)) exp->loop.mode = LottieExpression::LoopMode::InPingPong;
885         else if (!strcmp(name, EXP_OFFSET)) exp->loop.mode = LottieExpression::LoopMode::InOffset;
886         else if (!strcmp(name, EXP_CONTINUE)) exp->loop.mode = LottieExpression::LoopMode::InContinue;
887         free(name);
888     }
889 
890     if (exp->loop.mode != LottieExpression::LoopMode::InCycle && exp->loop.mode != LottieExpression::LoopMode::InPingPong) {
891         TVGLOG("LOTTIE", "Not supported loopIn type = %d", exp->loop.mode);
892         return false;
893     }
894 
895     return true;
896 }
897 
_loopIn(const jerry_call_info_t * info,const jerry_value_t args[],const jerry_length_t argsCnt)898 static jerry_value_t _loopIn(const jerry_call_info_t* info, const jerry_value_t args[], const jerry_length_t argsCnt)
899 {
900     auto exp = static_cast<LottieExpression*>(jerry_object_get_native_ptr(info->function, nullptr));
901 
902     if (!_loopInCommon(exp, args, argsCnt)) return jerry_undefined();
903 
904     if (argsCnt > 1) exp->loop.key = jerry_value_as_int32(args[1]);
905 
906     auto obj = jerry_object();
907     jerry_object_set_native_ptr(obj, nullptr, exp->property);
908     return obj;
909 }
910 
911 
_loopInDuration(const jerry_call_info_t * info,const jerry_value_t args[],const jerry_length_t argsCnt)912 static jerry_value_t _loopInDuration(const jerry_call_info_t* info, const jerry_value_t args[], const jerry_length_t argsCnt)
913 {
914     auto exp = static_cast<LottieExpression*>(jerry_object_get_native_ptr(info->function, nullptr));
915 
916     if (argsCnt > 1) {
917         exp->loop.in = exp->comp->frameAtTime((float)jerry_value_as_int32(args[1]));
918     }
919 
920     if (!_loopInCommon(exp, args, argsCnt)) return jerry_undefined();
921 
922     auto obj = jerry_object();
923     jerry_object_set_native_ptr(obj, nullptr, exp->property);
924     return obj;
925 }
926 
927 
_key(const jerry_call_info_t * info,const jerry_value_t args[],const jerry_length_t argsCnt)928 static jerry_value_t _key(const jerry_call_info_t* info, const jerry_value_t args[], const jerry_length_t argsCnt)
929 {
930     auto exp = static_cast<LottieExpression*>(jerry_object_get_native_ptr(info->function, nullptr));
931     auto key = jerry_value_as_int32(args[0]);
932     auto frameNo = exp->property->frameNo(key);
933     auto time = jerry_number(exp->comp->timeAtFrame(frameNo));
934     auto value = _value(frameNo, exp->property);
935 
936     auto obj = jerry_object();
937     jerry_object_set_sz(obj, EXP_TIME, time);
938     jerry_object_set_sz(obj, EXP_INDEX, args[0]);
939     jerry_object_set_sz(obj, EXP_VALUE, value);
940 
941     //direct access, key[0], key[1]
942     if (exp->property->type == LottieProperty::Type::Float) {
943         jerry_object_set_index(obj, 0, value);
944     } else if (exp->property->type == LottieProperty::Type::Point || exp->property->type == LottieProperty::Type::Position) {
945         jerry_object_set_index(obj, 0, jerry_object_get_index(value, 0));
946         jerry_object_set_index(obj, 1, jerry_object_get_index(value, 1));
947     }
948 
949     jerry_value_free(time);
950     jerry_value_free(value);
951 
952     return obj;
953 }
954 
955 
_createPath(const jerry_call_info_t * info,const jerry_value_t args[],const jerry_length_t argsCnt)956 static jerry_value_t _createPath(const jerry_call_info_t* info, const jerry_value_t args[], const jerry_length_t argsCnt)
957 {
958     //TODO: arg1: points, arg2: inTangents, arg3: outTangents, arg4: isClosed
959     auto arg1 = jerry_value_to_object(args[0]);
960     auto pathset = jerry_object_get_native_ptr(arg1, nullptr);
961     if (!pathset) {
962         TVGERR("LOTTIE", "failed createPath()");
963         return jerry_undefined();
964     }
965 
966     jerry_value_free(arg1);
967 
968     auto obj = jerry_object();
969     jerry_object_set_native_ptr(obj, nullptr, pathset);
970     return obj;
971 }
972 
973 
_uniformPath(const jerry_call_info_t * info,const jerry_value_t args[],const jerry_length_t argsCnt)974 static jerry_value_t _uniformPath(const jerry_call_info_t* info, const jerry_value_t args[], const jerry_length_t argsCnt)
975 {
976     auto pathset = static_cast<LottiePathSet*>(jerry_object_get_native_ptr(info->function, nullptr));
977 
978     /* TODO: ThorVG prebuilds the path data for performance.
979        It actually need to constructs the Array<Point> for points, inTangents, outTangents and then return here... */
980     auto obj = jerry_object();
981     jerry_object_set_native_ptr(obj, nullptr, pathset);
982     return obj;
983 }
984 
985 
_isClosed(const jerry_call_info_t * info,const jerry_value_t args[],const jerry_length_t argsCnt)986 static jerry_value_t _isClosed(const jerry_call_info_t* info, const jerry_value_t args[], const jerry_length_t argsCnt)
987 {
988     //TODO: Not used
989     return jerry_boolean(true);
990 }
991 
992 
_buildPath(jerry_value_t context,LottieExpression * exp)993 static void _buildPath(jerry_value_t context, LottieExpression* exp)
994 {
995     //Trick for fast building path.
996     auto points = jerry_function_external(_uniformPath);
997     jerry_object_set_native_ptr(points, nullptr, exp->property);
998     jerry_object_set_sz(context, "points", points);
999     jerry_value_free(points);
1000 
1001     auto inTangents = jerry_function_external(_uniformPath);
1002     jerry_object_set_native_ptr(inTangents, nullptr, exp->property);
1003     jerry_object_set_sz(context, "inTangents", inTangents);
1004     jerry_value_free(inTangents);
1005 
1006     auto outTangents = jerry_function_external(_uniformPath);
1007     jerry_object_set_native_ptr(outTangents, nullptr, exp->property);
1008     jerry_object_set_sz(context, "outTangents", outTangents);
1009     jerry_value_free(outTangents);
1010 
1011     auto isClosed = jerry_function_external(_isClosed);
1012     jerry_object_set_native_ptr(isClosed, nullptr, exp->property);
1013     jerry_object_set_sz(context, "isClosed", isClosed);
1014     jerry_value_free(isClosed);
1015 
1016 }
1017 
1018 
_buildProperty(float frameNo,jerry_value_t context,LottieExpression * exp)1019 static void _buildProperty(float frameNo, jerry_value_t context, LottieExpression* exp)
1020 {
1021     auto value = _value(frameNo, exp->property);
1022     jerry_object_set_sz(context, EXP_VALUE, value);
1023     jerry_value_free(value);
1024 
1025     auto valueAtTime = jerry_function_external(_valueAtTime);
1026     jerry_object_set_sz(context, "valueAtTime", valueAtTime);
1027     jerry_object_set_native_ptr(valueAtTime, nullptr, exp);
1028     jerry_value_free(valueAtTime);
1029 
1030     auto velocity = jerry_number(0.0f);
1031     jerry_object_set_sz(context, "velocity", velocity);
1032     jerry_value_free(velocity);
1033 
1034     auto velocityAtTime = jerry_function_external(_velocityAtTime);
1035     jerry_object_set_sz(context, "velocityAtTime", velocityAtTime);
1036     jerry_object_set_native_ptr(velocityAtTime, nullptr, exp);
1037     jerry_value_free(velocityAtTime);
1038 
1039     auto speed = jerry_number(0.0f);
1040     jerry_object_set_sz(context, "speed", speed);
1041     jerry_value_free(speed);
1042 
1043     auto speedAtTime = jerry_function_external(_speedAtTime);
1044     jerry_object_set_sz(context, "speedAtTime", speedAtTime);
1045     jerry_object_set_native_ptr(speedAtTime, nullptr, exp);
1046     jerry_value_free(speedAtTime);
1047 
1048     //wiggle(freq, amp, octaves=1, amp_mult=.5, t=time)
1049     //temporalWiggle(freq, amp, octaves=1, amp_mult=.5, t=time)
1050     //smooth(width=.2, samples=5, t=time)
1051 
1052     auto loopIn = jerry_function_external(_loopIn);
1053     jerry_object_set_sz(context, "loopIn", loopIn);
1054     jerry_object_set_native_ptr(loopIn, nullptr, exp);
1055     jerry_value_free(loopIn);
1056 
1057     auto loopOut = jerry_function_external(_loopOut);
1058     jerry_object_set_sz(context, "loopOut", loopOut);
1059     jerry_object_set_native_ptr(loopOut, nullptr, exp);
1060     jerry_value_free(loopOut);
1061 
1062     auto loopInDuration = jerry_function_external(_loopInDuration);
1063     jerry_object_set_sz(context, "loopInDuration", loopInDuration);
1064     jerry_object_set_native_ptr(loopInDuration, nullptr, exp);
1065     jerry_value_free(loopInDuration);
1066 
1067     auto loopOutDuration = jerry_function_external(_loopOutDuration);
1068     jerry_object_set_sz(context, "loopOutDuration", loopOutDuration);
1069     jerry_object_set_native_ptr(loopOutDuration, nullptr, exp);
1070     jerry_value_free(loopOutDuration);
1071 
1072     auto key = jerry_function_external(_key);
1073     jerry_object_set_sz(context, "key", key);
1074     jerry_object_set_native_ptr(key, nullptr, exp);
1075     jerry_value_free(key);
1076 
1077     //key(markerName)
1078 
1079     auto nearestKey = jerry_function_external(_nearestKey);
1080     jerry_object_set_native_ptr(nearestKey, nullptr, exp);
1081     jerry_object_set_sz(context, "nearestKey", nearestKey);
1082     jerry_value_free(nearestKey);
1083 
1084     auto numKeys = jerry_number(exp->property->frameCnt());
1085     jerry_object_set_sz(context, "numKeys", numKeys);
1086     jerry_value_free(numKeys);
1087 
1088     auto propertyGroup = jerry_function_external(_propertyGroup);
1089     jerry_object_set_native_ptr(propertyGroup, &freeCb, _expcontent(exp, frameNo, exp->object));
1090     jerry_object_set_sz(context, "propertyGroup", propertyGroup);
1091     jerry_value_free(propertyGroup);
1092 
1093     //propertyIndex
1094 
1095     //name
1096 
1097     //content("name"), #look for the named property from a layer
1098     auto content = jerry_function_external(_content);
1099     jerry_object_set_sz(context, EXP_CONTENT, content);
1100     jerry_object_set_native_ptr(content, &freeCb, _expcontent(exp, frameNo, exp->layer));
1101     jerry_value_free(content);
1102 
1103     //expansions per types
1104     if (exp->property->type == LottieProperty::Type::PathSet) _buildPath(context, exp);
1105 }
1106 
1107 
_comp(const jerry_call_info_t * info,const jerry_value_t args[],const jerry_length_t argsCnt)1108 static jerry_value_t _comp(const jerry_call_info_t* info, const jerry_value_t args[], const jerry_length_t argsCnt)
1109 {
1110     auto data = static_cast<ExpContent*>(jerry_object_get_native_ptr(info->function, &freeCb));
1111     auto comp = static_cast<LottieLayer*>(data->obj);
1112     auto layer = comp->layerById(_idByName(args[0]));
1113 
1114     if (!layer) return jerry_undefined();
1115 
1116     auto obj = jerry_object();
1117     jerry_object_set_native_ptr(obj, nullptr, layer);
1118     _buildLayer(obj, data->frameNo, layer, comp, data->exp);
1119 
1120     return obj;
1121 }
1122 
1123 
_buildMath(jerry_value_t context)1124 static void _buildMath(jerry_value_t context)
1125 {
1126     auto bm_mul = jerry_function_external(_mul);
1127     jerry_object_set_sz(context, "$bm_mul", bm_mul);
1128     jerry_value_free(bm_mul);
1129 
1130     auto bm_sum = jerry_function_external(_add);
1131     jerry_object_set_sz(context, "$bm_sum", bm_sum);
1132     jerry_value_free(bm_sum);
1133 
1134     auto bm_add = jerry_function_external(_add);
1135     jerry_object_set_sz(context, "$bm_add", bm_add);
1136     jerry_value_free(bm_add);
1137 
1138     auto bm_sub = jerry_function_external(_sub);
1139     jerry_object_set_sz(context, "$bm_sub", bm_sub);
1140     jerry_value_free(bm_sub);
1141 
1142     auto bm_div = jerry_function_external(_div);
1143     jerry_object_set_sz(context, "$bm_div", bm_div);
1144     jerry_value_free(bm_div);
1145 
1146     auto mul = jerry_function_external(_mul);
1147     jerry_object_set_sz(context, "mul", mul);
1148     jerry_value_free(mul);
1149 
1150     auto sum = jerry_function_external(_add);
1151     jerry_object_set_sz(context, "sum", sum);
1152     jerry_value_free(sum);
1153 
1154     auto add = jerry_function_external(_add);
1155     jerry_object_set_sz(context, "add", add);
1156     jerry_value_free(add);
1157 
1158     auto sub = jerry_function_external(_sub);
1159     jerry_object_set_sz(context, "sub", sub);
1160     jerry_value_free(sub);
1161 
1162     auto div = jerry_function_external(_div);
1163     jerry_object_set_sz(context, "div", div);
1164     jerry_value_free(div);
1165 
1166     auto clamp = jerry_function_external(_clamp);
1167     jerry_object_set_sz(context, "clamp", clamp);
1168     jerry_value_free(clamp);
1169 
1170     auto dot = jerry_function_external(_dot);
1171     jerry_object_set_sz(context, "dot", dot);
1172     jerry_value_free(dot);
1173 
1174     auto cross = jerry_function_external(_cross);
1175     jerry_object_set_sz(context, "cross", cross);
1176     jerry_value_free(cross);
1177 
1178     auto normalize = jerry_function_external(_normalize);
1179     jerry_object_set_sz(context, "normalize", normalize);
1180     jerry_value_free(normalize);
1181 
1182     auto length = jerry_function_external(_length);
1183     jerry_object_set_sz(context, "length", length);
1184     jerry_value_free(length);
1185 
1186     auto random = jerry_function_external(_random);
1187     jerry_object_set_sz(context, "random", random);
1188     jerry_value_free(random);
1189 
1190     auto deg2rad = jerry_function_external(_deg2rad);
1191     jerry_object_set_sz(context, "degreesToRadians", deg2rad);
1192     jerry_value_free(deg2rad);
1193 
1194     auto rad2deg = jerry_function_external(_rad2deg);
1195     jerry_object_set_sz(context, "radiansToDegrees", rad2deg);
1196     jerry_value_free(rad2deg);
1197 
1198     auto linear = jerry_function_external(_linear);
1199     jerry_object_set_sz(context, "linear", linear);
1200     jerry_value_free(linear);
1201 
1202     auto ease = jerry_function_external(_ease);
1203     jerry_object_set_sz(context, "ease", ease);
1204     jerry_value_free(ease);
1205 
1206     auto easeIn = jerry_function_external(_easeIn);
1207     jerry_object_set_sz(context, "easeIn", easeIn);
1208     jerry_value_free(easeIn);
1209 
1210     auto easeOut = jerry_function_external(_easeOut);
1211     jerry_object_set_sz(context, "easeOut", easeOut);
1212     jerry_value_free(easeOut);
1213 
1214     //lookAt
1215 }
1216 
1217 
buildGlobal(LottieExpression * exp)1218 void LottieExpressions::buildGlobal(LottieExpression* exp)
1219 {
1220     auto index = jerry_number(exp->layer->idx);
1221     jerry_object_set_sz(global, EXP_INDEX, index);
1222     jerry_value_free(index);
1223 }
1224 
1225 
buildComp(jerry_value_t context,float frameNo,LottieLayer * comp,LottieExpression * exp)1226 void LottieExpressions::buildComp(jerry_value_t context, float frameNo, LottieLayer* comp, LottieExpression* exp)
1227 {
1228     auto data = static_cast<ExpContent*>(jerry_object_get_native_ptr(context, &freeCb));
1229     data->exp = exp;
1230     data->frameNo = frameNo;
1231     data->obj = comp;
1232 
1233     //layer(index) / layer(name) / layer(otherLayer, reIndex)
1234     auto layer = jerry_function_external(_layer);
1235     jerry_object_set_sz(context, "layer", layer);
1236 
1237     jerry_object_set_native_ptr(layer, &freeCb, _expcontent(exp, frameNo, comp));
1238     jerry_value_free(layer);
1239 
1240     auto numLayers = jerry_number(comp->children.count);
1241     jerry_object_set_sz(context, "numLayers", numLayers);
1242     jerry_value_free(numLayers);
1243 }
1244 
1245 
buildComp(LottieComposition * comp,float frameNo,LottieExpression * exp)1246 void LottieExpressions::buildComp(LottieComposition* comp, float frameNo, LottieExpression* exp)
1247 {
1248     buildComp(this->comp, frameNo, comp->root, exp);
1249 
1250     //marker
1251     //marker.key(index)
1252     //marker.key(name)
1253     //marker.nearestKey(t)
1254     //marker.numKeys
1255 
1256     //activeCamera
1257 
1258     auto width = jerry_number(comp->w);
1259     jerry_object_set_sz(thisComp, EXP_WIDTH, width);
1260     jerry_value_free(width);
1261 
1262     auto height = jerry_number(comp->h);
1263     jerry_object_set_sz(thisComp, EXP_HEIGHT, height);
1264     jerry_value_free(height);
1265 
1266     auto duration = jerry_number(comp->duration());
1267     jerry_object_set_sz(thisComp, "duration", duration);
1268     jerry_value_free(duration);
1269 
1270     //ntscDropFrame
1271     //displayStartTime
1272 
1273     auto frameDuration = jerry_number(1.0f / comp->frameRate);
1274     jerry_object_set_sz(thisComp, "frameDuration", frameDuration);
1275     jerry_value_free(frameDuration);
1276 
1277     //shutterAngle
1278     //shutterPhase
1279     //bgColor
1280     //pixelAspect
1281 
1282     auto name = jerry_string((jerry_char_t*)comp->name, strlen(comp->name), JERRY_ENCODING_UTF8);
1283     jerry_object_set_sz(thisComp, EXP_NAME, name);
1284     jerry_value_free(name);
1285 }
1286 
1287 
buildGlobal()1288 jerry_value_t LottieExpressions::buildGlobal()
1289 {
1290     global = jerry_current_realm();
1291 
1292     //comp(name)
1293     comp = jerry_function_external(_comp);
1294     jerry_object_set_native_ptr(comp, &freeCb, _expcontent(nullptr, 0.0f, nullptr));
1295     jerry_object_set_sz(global, "comp", comp);
1296 
1297     //footage(name)
1298 
1299     thisComp = jerry_object();
1300     jerry_object_set_native_ptr(thisComp, &freeCb, _expcontent(nullptr, 0.0f, nullptr));
1301     jerry_object_set_sz(global, "thisComp", thisComp);
1302 
1303     thisLayer = jerry_object();
1304     jerry_object_set_sz(global, "thisLayer", thisLayer);
1305 
1306     thisProperty = jerry_object();
1307     jerry_object_set_sz(global, "thisProperty", thisProperty);
1308 
1309     auto effect = jerry_function_external(_effect);
1310     jerry_object_set_sz(global, EXP_EFFECT, effect);
1311     jerry_value_free(effect);
1312 
1313     auto fromCompToSurface = jerry_function_external(_fromCompToSurface);
1314     jerry_object_set_sz(global, "fromCompToSurface", fromCompToSurface);
1315     jerry_value_free(fromCompToSurface);
1316 
1317     auto createPath = jerry_function_external(_createPath);
1318     jerry_object_set_sz(global, "createPath", createPath);
1319     jerry_value_free(createPath);
1320 
1321     //posterizeTime(framesPerSecond)
1322     //value
1323 
1324     return global;
1325 }
1326 
1327 
evaluate(float frameNo,LottieExpression * exp)1328 jerry_value_t LottieExpressions::evaluate(float frameNo, LottieExpression* exp)
1329 {
1330     if (exp->disabled) return jerry_undefined();
1331 
1332     buildGlobal(exp);
1333 
1334     //main composition
1335     buildComp(exp->comp, frameNo, exp);
1336 
1337     //this composition
1338     buildComp(thisComp, frameNo, exp->layer->comp, exp);
1339 
1340     //update global context values
1341     _buildProperty(frameNo, global, exp);
1342 
1343     //this layer
1344     jerry_object_set_native_ptr(thisLayer, nullptr, exp->layer);
1345     _buildLayer(thisLayer, frameNo, exp->layer, exp->comp->root, exp);
1346 
1347     //this property
1348     jerry_object_set_native_ptr(thisProperty, nullptr, exp->property);
1349     _buildProperty(frameNo, thisProperty, exp);
1350 
1351     //expansions per object type
1352     if (exp->object->type == LottieObject::Transform) _buildTransform(global, frameNo, static_cast<LottieTransform*>(exp->object));
1353 
1354     //evaluate the code
1355     auto eval = jerry_eval((jerry_char_t *) exp->code, strlen(exp->code), JERRY_PARSE_NO_OPTS);
1356 
1357     if (jerry_value_is_exception(eval) || jerry_value_is_undefined(eval)) {
1358         TVGERR("LOTTIE", "Failed to dispatch the expressions!");
1359         exp->disabled = true;
1360         return jerry_undefined();
1361     }
1362 
1363     jerry_value_free(eval);
1364 
1365     return jerry_object_get_sz(global, "$bm_rt");
1366 }
1367 
1368 
1369 /************************************************************************/
1370 /* External Class Implementation                                        */
1371 /************************************************************************/
1372 
~LottieExpressions()1373 LottieExpressions::~LottieExpressions()
1374 {
1375     jerry_value_free(thisProperty);
1376     jerry_value_free(thisLayer);
1377     jerry_value_free(thisComp);
1378     jerry_value_free(comp);
1379     jerry_value_free(global);
1380     jerry_cleanup();
1381 }
1382 
1383 
LottieExpressions()1384 LottieExpressions::LottieExpressions()
1385 {
1386     jerry_init(JERRY_INIT_EMPTY);
1387     _buildMath(buildGlobal());
1388 }
1389 
1390 
update(float curTime)1391 void LottieExpressions::update(float curTime)
1392 {
1393     //time, #current time in seconds
1394     auto time = jerry_number(curTime);
1395     jerry_object_set_sz(global, EXP_TIME, time);
1396     jerry_value_free(time);
1397 }
1398 
1399 
1400 //FIXME: Threads support
1401 #include "tvgTaskScheduler.h"
1402 
instance()1403 LottieExpressions* LottieExpressions::instance()
1404 {
1405     //FIXME: Threads support
1406     if (TaskScheduler::threads() > 1) {
1407         TVGLOG("LOTTIE", "Lottie Expressions are not supported with tvg threads");
1408         return nullptr;
1409     }
1410 
1411     if (!exps) exps = new LottieExpressions;
1412     ++engineRefCnt;
1413     return exps;
1414 }
1415 
1416 
retrieve(LottieExpressions * instance)1417 void LottieExpressions::retrieve(LottieExpressions* instance)
1418 {
1419     if (--engineRefCnt == 0) {
1420         delete(instance);
1421         exps = nullptr;
1422     }
1423 }
1424 
1425 
1426 #endif //THORVG_LOTTIE_EXPRESSIONS_SUPPORT
1427 
1428 #endif /* LV_USE_THORVG_INTERNAL */
1429 
1430