mirror of
https://github.com/thorvg/thorvg.git
synced 2025-06-14 12:04:29 +00:00
lottie: support the expression feature
The current development of the expression engine is experimental. It does not support multi-threading. Therefore, when tvg::Initializer::init() is configured with more than one thread, expressions will be automatically disabled. issue: https://github.com/thorvg/thorvg/issues/1640
This commit is contained in:
parent
4d02cda350
commit
324d0f8943
9 changed files with 1828 additions and 152 deletions
|
@ -3,15 +3,17 @@ if lottie_expressions
|
|||
endif
|
||||
|
||||
source_file = [
|
||||
'tvgLottieAnimation.cpp',
|
||||
'tvgLottieBuilder.h',
|
||||
'tvgLottieExpressions.h',
|
||||
'tvgLottieInterpolator.h',
|
||||
'tvgLottieLoader.h',
|
||||
'tvgLottieModel.h',
|
||||
'tvgLottieParser.h',
|
||||
'tvgLottieParserHandler.h',
|
||||
'tvgLottieProperty.h',
|
||||
'tvgLottieAnimation.cpp',
|
||||
'tvgLottieBuilder.cpp',
|
||||
'tvgLottieExpressions.cpp',
|
||||
'tvgLottieInterpolator.cpp',
|
||||
'tvgLottieLoader.cpp',
|
||||
'tvgLottieModel.cpp',
|
||||
|
|
|
@ -27,9 +27,10 @@
|
|||
#include "tvgPaint.h"
|
||||
#include "tvgShape.h"
|
||||
#include "tvgInlist.h"
|
||||
#include "tvgTaskScheduler.h"
|
||||
#include "tvgLottieModel.h"
|
||||
#include "tvgLottieBuilder.h"
|
||||
#include "tvgTaskScheduler.h"
|
||||
#include "tvgLottieExpressions.h"
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
|
@ -96,8 +97,8 @@ struct RenderContext
|
|||
};
|
||||
|
||||
|
||||
static void _updateChildren(LottieGroup* parent, float frameNo, Inlist<RenderContext>& contexts);
|
||||
static void _updateLayer(LottieLayer* root, LottieLayer* layer, float frameNo);
|
||||
static void _updateChildren(LottieGroup* parent, float frameNo, Inlist<RenderContext>& contexts, LottieExpressions* exps);
|
||||
static void _updateLayer(LottieLayer* root, LottieLayer* layer, float frameNo, LottieExpressions* exps);
|
||||
static bool _buildComposition(LottieComposition* comp, LottieGroup* parent);
|
||||
static Shape* _draw(LottieGroup* parent, RenderContext* ctx);
|
||||
|
||||
|
@ -128,7 +129,7 @@ static void _rotationZ(Matrix* m, float degree)
|
|||
}
|
||||
|
||||
|
||||
static bool _updateTransform(LottieTransform* transform, float frameNo, bool autoOrient, Matrix& matrix, uint8_t& opacity)
|
||||
static bool _updateTransform(LottieTransform* transform, float frameNo, bool autoOrient, Matrix& matrix, uint8_t& opacity, LottieExpressions* exps)
|
||||
{
|
||||
mathIdentity(&matrix);
|
||||
|
||||
|
@ -140,46 +141,46 @@ static bool _updateTransform(LottieTransform* transform, float frameNo, bool aut
|
|||
if (transform->coords) {
|
||||
mathTranslate(&matrix, transform->coords->x(frameNo), transform->coords->y(frameNo));
|
||||
} else {
|
||||
auto position = transform->position(frameNo);
|
||||
auto position = transform->position(frameNo, exps);
|
||||
mathTranslate(&matrix, position.x, position.y);
|
||||
}
|
||||
|
||||
auto angle = 0.0f;
|
||||
if (autoOrient) angle = transform->position.angle(frameNo);
|
||||
_rotationZ(&matrix, transform->rotation(frameNo) + angle);
|
||||
_rotationZ(&matrix, transform->rotation(frameNo, exps) + angle);
|
||||
|
||||
if (transform->rotationEx) {
|
||||
_rotateY(&matrix, transform->rotationEx->y(frameNo));
|
||||
_rotateX(&matrix, transform->rotationEx->x(frameNo));
|
||||
}
|
||||
|
||||
auto scale = transform->scale(frameNo);
|
||||
auto scale = transform->scale(frameNo, exps);
|
||||
mathScaleR(&matrix, scale.x * 0.01f, scale.y * 0.01f);
|
||||
|
||||
//Lottie specific anchor transform.
|
||||
auto anchor = transform->anchor(frameNo);
|
||||
auto anchor = transform->anchor(frameNo, exps);
|
||||
mathTranslateR(&matrix, -anchor.x, -anchor.y);
|
||||
|
||||
//invisible just in case.
|
||||
if (scale.x == 0.0f || scale.y == 0.0f) opacity = 0;
|
||||
else opacity = transform->opacity(frameNo);
|
||||
else opacity = transform->opacity(frameNo, exps);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static void _updateTransform(LottieLayer* layer, float frameNo)
|
||||
static void _updateTransform(LottieLayer* layer, float frameNo, LottieExpressions* exps)
|
||||
{
|
||||
if (!layer || mathEqual(layer->cache.frameNo, frameNo)) return;
|
||||
|
||||
auto transform = layer->transform;
|
||||
auto parent = layer->parent;
|
||||
|
||||
if (parent) _updateTransform(parent, frameNo);
|
||||
if (parent) _updateTransform(parent, frameNo, exps);
|
||||
|
||||
auto& matrix = layer->cache.matrix;
|
||||
|
||||
_updateTransform(transform, frameNo, layer->autoOrient, matrix, layer->cache.opacity);
|
||||
_updateTransform(transform, frameNo, layer->autoOrient, matrix, layer->cache.opacity, exps);
|
||||
|
||||
if (parent) {
|
||||
if (!mathIdentity((const Matrix*) &parent->cache.matrix)) {
|
||||
|
@ -191,7 +192,7 @@ static void _updateTransform(LottieLayer* layer, float frameNo)
|
|||
}
|
||||
|
||||
|
||||
static void _updateTransform(LottieGroup* parent, LottieObject** child, float frameNo, TVG_UNUSED Inlist<RenderContext>& contexts, RenderContext* ctx)
|
||||
static void _updateTransform(LottieGroup* parent, LottieObject** child, float frameNo, TVG_UNUSED Inlist<RenderContext>& contexts, RenderContext* ctx, LottieExpressions* exps)
|
||||
{
|
||||
auto transform = static_cast<LottieTransform*>(*child);
|
||||
if (!transform) return;
|
||||
|
@ -200,14 +201,14 @@ static void _updateTransform(LottieGroup* parent, LottieObject** child, float fr
|
|||
|
||||
if (parent->mergeable()) {
|
||||
if (!ctx->transform) ctx->transform = (Matrix*)malloc(sizeof(Matrix));
|
||||
_updateTransform(transform, frameNo, false, *ctx->transform, opacity);
|
||||
_updateTransform(transform, frameNo, false, *ctx->transform, opacity, exps);
|
||||
return;
|
||||
}
|
||||
|
||||
ctx->merging = nullptr;
|
||||
|
||||
Matrix matrix;
|
||||
if (!_updateTransform(transform, frameNo, false, matrix, opacity)) return;
|
||||
if (!_updateTransform(transform, frameNo, false, matrix, opacity, exps)) return;
|
||||
|
||||
auto pmatrix = PP(ctx->propagator)->transform();
|
||||
ctx->propagator->transform(pmatrix ? mathMultiply(pmatrix, &matrix) : matrix);
|
||||
|
@ -221,7 +222,7 @@ static void _updateTransform(LottieGroup* parent, LottieObject** child, float fr
|
|||
}
|
||||
|
||||
|
||||
static void _updateGroup(LottieGroup* parent, LottieObject** child, float frameNo, TVG_UNUSED Inlist<RenderContext>& pcontexts, RenderContext* ctx)
|
||||
static void _updateGroup(LottieGroup* parent, LottieObject** child, float frameNo, TVG_UNUSED Inlist<RenderContext>& pcontexts, RenderContext* ctx, LottieExpressions* exps)
|
||||
{
|
||||
auto group = static_cast<LottieGroup*>(*child);
|
||||
|
||||
|
@ -237,13 +238,13 @@ static void _updateGroup(LottieGroup* parent, LottieObject** child, float frameN
|
|||
Inlist<RenderContext> contexts;
|
||||
contexts.back(new RenderContext(*ctx, group->mergeable()));
|
||||
|
||||
_updateChildren(group, frameNo, contexts);
|
||||
_updateChildren(group, frameNo, contexts, exps);
|
||||
|
||||
contexts.free();
|
||||
}
|
||||
|
||||
|
||||
static void _updateStroke(LottieStroke* stroke, float frameNo, RenderContext* ctx)
|
||||
static void _updateStroke(LottieStroke* stroke, float frameNo, RenderContext* ctx, LottieExpressions* exps)
|
||||
{
|
||||
ctx->propagator->strokeWidth(stroke->width(frameNo));
|
||||
ctx->propagator->strokeCap(stroke->cap);
|
||||
|
@ -252,9 +253,9 @@ static void _updateStroke(LottieStroke* stroke, float frameNo, RenderContext* ct
|
|||
|
||||
if (stroke->dashattr) {
|
||||
float dashes[2];
|
||||
dashes[0] = stroke->dashSize(frameNo);
|
||||
dashes[1] = dashes[0] + stroke->dashGap(frameNo);
|
||||
ctx->propagator->strokeDash(dashes, 2, stroke->dashOffset(frameNo));
|
||||
dashes[0] = stroke->dashSize(frameNo, exps);
|
||||
dashes[1] = dashes[0] + stroke->dashGap(frameNo, exps);
|
||||
ctx->propagator->strokeDash(dashes, 2, stroke->dashOffset(frameNo, exps));
|
||||
} else {
|
||||
ctx->propagator->strokeDash(nullptr, 0);
|
||||
}
|
||||
|
@ -275,7 +276,7 @@ static bool _fragmented(LottieObject** child, Inlist<RenderContext>& contexts, R
|
|||
}
|
||||
|
||||
|
||||
static void _updateSolidStroke(TVG_UNUSED LottieGroup* parent, LottieObject** child, float frameNo, Inlist<RenderContext>& contexts, RenderContext* ctx)
|
||||
static void _updateSolidStroke(TVG_UNUSED LottieGroup* parent, LottieObject** child, float frameNo, Inlist<RenderContext>& contexts, RenderContext* ctx, LottieExpressions* exps)
|
||||
{
|
||||
if (_fragmented(child, contexts, ctx)) return;
|
||||
|
||||
|
@ -284,19 +285,19 @@ static void _updateSolidStroke(TVG_UNUSED LottieGroup* parent, LottieObject** ch
|
|||
ctx->merging = nullptr;
|
||||
auto color = stroke->color(frameNo);
|
||||
ctx->propagator->strokeFill(color.rgb[0], color.rgb[1], color.rgb[2], stroke->opacity(frameNo));
|
||||
_updateStroke(static_cast<LottieStroke*>(stroke), frameNo, ctx);
|
||||
_updateStroke(static_cast<LottieStroke*>(stroke), frameNo, ctx, exps);
|
||||
}
|
||||
|
||||
|
||||
static void _updateGradientStroke(TVG_UNUSED LottieGroup* parent, LottieObject** child, float frameNo, Inlist<RenderContext>& contexts, RenderContext* ctx)
|
||||
static void _updateGradientStroke(TVG_UNUSED LottieGroup* parent, LottieObject** child, float frameNo, Inlist<RenderContext>& contexts, RenderContext* ctx, LottieExpressions* exps)
|
||||
{
|
||||
if (_fragmented(child, contexts, ctx)) return;
|
||||
|
||||
auto stroke = static_cast<LottieGradientStroke*>(*child);
|
||||
|
||||
ctx->merging = nullptr;
|
||||
ctx->propagator->strokeFill(unique_ptr<Fill>(stroke->fill(frameNo)));
|
||||
_updateStroke(static_cast<LottieStroke*>(stroke), frameNo, ctx);
|
||||
ctx->propagator->strokeFill(unique_ptr<Fill>(stroke->fill(frameNo, exps)));
|
||||
_updateStroke(static_cast<LottieStroke*>(stroke), frameNo, ctx, exps);
|
||||
}
|
||||
|
||||
|
||||
|
@ -315,7 +316,7 @@ static void _updateSolidFill(TVG_UNUSED LottieGroup* parent, LottieObject** chil
|
|||
}
|
||||
|
||||
|
||||
static Shape* _updateGradientFill(TVG_UNUSED LottieGroup* parent, LottieObject** child, float frameNo, TVG_UNUSED Inlist<RenderContext>& contexts, RenderContext* ctx)
|
||||
static Shape* _updateGradientFill(TVG_UNUSED LottieGroup* parent, LottieObject** child, float frameNo, TVG_UNUSED Inlist<RenderContext>& contexts, RenderContext* ctx, LottieExpressions* exps)
|
||||
{
|
||||
if (_fragmented(child, contexts, ctx)) return nullptr;
|
||||
|
||||
|
@ -323,7 +324,7 @@ static Shape* _updateGradientFill(TVG_UNUSED LottieGroup* parent, LottieObject**
|
|||
|
||||
ctx->merging = nullptr;
|
||||
//TODO: reuse the fill instance?
|
||||
ctx->propagator->fill(unique_ptr<Fill>(fill->fill(frameNo)));
|
||||
ctx->propagator->fill(unique_ptr<Fill>(fill->fill(frameNo, exps)));
|
||||
ctx->propagator->fill(fill->rule);
|
||||
ctx->propagator->opacity(MULTIPLY(fill->opacity(frameNo), PP(ctx->propagator)->opacity));
|
||||
|
||||
|
@ -513,17 +514,17 @@ static void _updateEllipse(LottieGroup* parent, LottieObject** child, float fram
|
|||
}
|
||||
|
||||
|
||||
static void _updatePath(LottieGroup* parent, LottieObject** child, float frameNo, TVG_UNUSED Inlist<RenderContext>& contexts, RenderContext* ctx)
|
||||
static void _updatePath(LottieGroup* parent, LottieObject** child, float frameNo, TVG_UNUSED Inlist<RenderContext>& contexts, RenderContext* ctx, LottieExpressions* exps)
|
||||
{
|
||||
auto path = static_cast<LottiePath*>(*child);
|
||||
|
||||
if (ctx->repeater) {
|
||||
auto p = Shape::gen();
|
||||
path->pathset(frameNo, P(p)->rs.path.cmds, P(p)->rs.path.pts, ctx->transform);
|
||||
path->pathset(frameNo, P(p)->rs.path.cmds, P(p)->rs.path.pts, ctx->transform, exps);
|
||||
_repeat(parent, std::move(p), ctx);
|
||||
} else {
|
||||
auto merging = _draw(parent, ctx);
|
||||
if (path->pathset(frameNo, P(merging)->rs.path.cmds, P(merging)->rs.path.pts, ctx->transform)) {
|
||||
if (path->pathset(frameNo, P(merging)->rs.path.cmds, P(merging)->rs.path.pts, ctx->transform, exps)) {
|
||||
P(merging)->update(RenderUpdateFlag::Path);
|
||||
}
|
||||
if (ctx->roundness > 1.0f && P(merging)->rs.stroke) {
|
||||
|
@ -534,7 +535,7 @@ static void _updatePath(LottieGroup* parent, LottieObject** child, float frameNo
|
|||
}
|
||||
|
||||
|
||||
static void _updateText(LottieGroup* parent, LottieObject** child, float frameNo, TVG_UNUSED Inlist<RenderContext>& contexts, TVG_UNUSED RenderContext* ctx)
|
||||
static void _updateText(LottieGroup* parent, LottieObject** child, float frameNo, TVG_UNUSED Inlist<RenderContext>& contexts, TVG_UNUSED RenderContext* ctx, LottieExpressions* exps)
|
||||
{
|
||||
auto text = static_cast<LottieText*>(*child);
|
||||
auto& doc = text->doc(frameNo);
|
||||
|
@ -586,7 +587,7 @@ static void _updateText(LottieGroup* parent, LottieObject** child, float frameNo
|
|||
for (auto g = glyph->children.begin(); g < glyph->children.end(); ++g) {
|
||||
auto group = static_cast<LottieGroup*>(*g);
|
||||
for (auto p = group->children.begin(); p < group->children.end(); ++p) {
|
||||
if (static_cast<LottiePath*>(*p)->pathset(frameNo, P(shape)->rs.path.cmds, P(shape)->rs.path.pts)) {
|
||||
if (static_cast<LottiePath*>(*p)->pathset(frameNo, P(shape)->rs.path.cmds, P(shape)->rs.path.pts, nullptr, exps)) {
|
||||
P(shape)->update(RenderUpdateFlag::Path);
|
||||
}
|
||||
}
|
||||
|
@ -888,12 +889,12 @@ static void _updateRepeater(TVG_UNUSED LottieGroup* parent, LottieObject** child
|
|||
}
|
||||
|
||||
|
||||
static void _updateTrimpath(TVG_UNUSED LottieGroup* parent, LottieObject** child, float frameNo, TVG_UNUSED Inlist<RenderContext>& contexts, RenderContext* ctx)
|
||||
static void _updateTrimpath(TVG_UNUSED LottieGroup* parent, LottieObject** child, float frameNo, TVG_UNUSED Inlist<RenderContext>& contexts, RenderContext* ctx, LottieExpressions* exps)
|
||||
{
|
||||
auto trimpath= static_cast<LottieTrimpath*>(*child);
|
||||
|
||||
float begin, end;
|
||||
trimpath->segment(frameNo, begin, end);
|
||||
trimpath->segment(frameNo, begin, end, exps);
|
||||
|
||||
if (P(ctx->propagator)->rs.stroke) {
|
||||
auto pbegin = P(ctx->propagator)->rs.stroke->trim.begin;
|
||||
|
@ -907,7 +908,7 @@ static void _updateTrimpath(TVG_UNUSED LottieGroup* parent, LottieObject** child
|
|||
}
|
||||
|
||||
|
||||
static void _updateChildren(LottieGroup* parent, float frameNo, Inlist<RenderContext>& contexts)
|
||||
static void _updateChildren(LottieGroup* parent, float frameNo, Inlist<RenderContext>& contexts, LottieExpressions* exps)
|
||||
{
|
||||
contexts.head->begin = parent->children.end() - 1;
|
||||
|
||||
|
@ -917,11 +918,11 @@ static void _updateChildren(LottieGroup* parent, float frameNo, Inlist<RenderCon
|
|||
for (auto child = ctx->begin; child >= parent->children.data; --child) {
|
||||
switch ((*child)->type) {
|
||||
case LottieObject::Group: {
|
||||
_updateGroup(parent, child, frameNo, contexts, ctx);
|
||||
_updateGroup(parent, child, frameNo, contexts, ctx, exps);
|
||||
break;
|
||||
}
|
||||
case LottieObject::Transform: {
|
||||
_updateTransform(parent, child, frameNo, contexts, ctx);
|
||||
_updateTransform(parent, child, frameNo, contexts, ctx, exps);
|
||||
break;
|
||||
}
|
||||
case LottieObject::SolidFill: {
|
||||
|
@ -929,15 +930,15 @@ static void _updateChildren(LottieGroup* parent, float frameNo, Inlist<RenderCon
|
|||
break;
|
||||
}
|
||||
case LottieObject::SolidStroke: {
|
||||
_updateSolidStroke(parent, child, frameNo, contexts, ctx);
|
||||
_updateSolidStroke(parent, child, frameNo, contexts, ctx, exps);
|
||||
break;
|
||||
}
|
||||
case LottieObject::GradientFill: {
|
||||
_updateGradientFill(parent, child, frameNo, contexts, ctx);
|
||||
_updateGradientFill(parent, child, frameNo, contexts, ctx, exps);
|
||||
break;
|
||||
}
|
||||
case LottieObject::GradientStroke: {
|
||||
_updateGradientStroke(parent, child, frameNo, contexts, ctx);
|
||||
_updateGradientStroke(parent, child, frameNo, contexts, ctx, exps);
|
||||
break;
|
||||
}
|
||||
case LottieObject::Rect: {
|
||||
|
@ -949,7 +950,7 @@ static void _updateChildren(LottieGroup* parent, float frameNo, Inlist<RenderCon
|
|||
break;
|
||||
}
|
||||
case LottieObject::Path: {
|
||||
_updatePath(parent, child, frameNo, contexts, ctx);
|
||||
_updatePath(parent, child, frameNo, contexts, ctx, exps);
|
||||
break;
|
||||
}
|
||||
case LottieObject::Polystar: {
|
||||
|
@ -961,11 +962,11 @@ static void _updateChildren(LottieGroup* parent, float frameNo, Inlist<RenderCon
|
|||
break;
|
||||
}
|
||||
case LottieObject::Trimpath: {
|
||||
_updateTrimpath(parent, child, frameNo, contexts, ctx);
|
||||
_updateTrimpath(parent, child, frameNo, contexts, ctx, exps);
|
||||
break;
|
||||
}
|
||||
case LottieObject::Text: {
|
||||
_updateText(parent, child, frameNo, contexts, ctx);
|
||||
_updateText(parent, child, frameNo, contexts, ctx, exps);
|
||||
break;
|
||||
}
|
||||
case LottieObject::Repeater: {
|
||||
|
@ -984,14 +985,14 @@ static void _updateChildren(LottieGroup* parent, float frameNo, Inlist<RenderCon
|
|||
}
|
||||
|
||||
|
||||
static void _updatePrecomp(LottieLayer* precomp, float frameNo)
|
||||
static void _updatePrecomp(LottieLayer* precomp, float frameNo, LottieExpressions* exps)
|
||||
{
|
||||
if (precomp->children.empty()) return;
|
||||
|
||||
frameNo = precomp->remap(frameNo);
|
||||
frameNo = precomp->remap(frameNo, exps);
|
||||
|
||||
for (auto child = precomp->children.end() - 1; child >= precomp->children.begin(); --child) {
|
||||
_updateLayer(precomp, static_cast<LottieLayer*>(*child), frameNo);
|
||||
_updateLayer(precomp, static_cast<LottieLayer*>(*child), frameNo, exps);
|
||||
}
|
||||
|
||||
//clip the layer viewport
|
||||
|
@ -1018,7 +1019,7 @@ static void _updateSolid(LottieLayer* layer)
|
|||
}
|
||||
|
||||
|
||||
static void _updateMaskings(LottieLayer* layer, float frameNo)
|
||||
static void _updateMaskings(LottieLayer* layer, float frameNo, LottieExpressions* exps)
|
||||
{
|
||||
if (layer->masks.count == 0) return;
|
||||
|
||||
|
@ -1031,7 +1032,7 @@ static void _updateMaskings(LottieLayer* layer, float frameNo)
|
|||
auto shape = Shape::gen().release();
|
||||
shape->fill(255, 255, 255, mask->opacity(frameNo));
|
||||
shape->transform(layer->cache.matrix);
|
||||
if (mask->pathset(frameNo, P(shape)->rs.path.cmds, P(shape)->rs.path.pts)) {
|
||||
if (mask->pathset(frameNo, P(shape)->rs.path.cmds, P(shape)->rs.path.pts, nullptr, exps)) {
|
||||
P(shape)->update(RenderUpdateFlag::Path);
|
||||
}
|
||||
auto method = mask->method;
|
||||
|
@ -1056,12 +1057,12 @@ static void _updateMaskings(LottieLayer* layer, float frameNo)
|
|||
}
|
||||
|
||||
|
||||
static bool _updateMatte(LottieLayer* root, LottieLayer* layer, float frameNo)
|
||||
static bool _updateMatte(LottieLayer* root, LottieLayer* layer, float frameNo, LottieExpressions* exps)
|
||||
{
|
||||
auto target = layer->matte.target;
|
||||
if (!target) return true;
|
||||
|
||||
_updateLayer(root, target, frameNo);
|
||||
_updateLayer(root, target, frameNo, exps);
|
||||
|
||||
if (target->scene) {
|
||||
layer->scene->composite(cast(target->scene), layer->matte.type);
|
||||
|
@ -1075,14 +1076,14 @@ static bool _updateMatte(LottieLayer* root, LottieLayer* layer, float frameNo)
|
|||
}
|
||||
|
||||
|
||||
static void _updateLayer(LottieLayer* root, LottieLayer* layer, float frameNo)
|
||||
static void _updateLayer(LottieLayer* root, LottieLayer* layer, float frameNo, LottieExpressions* exps)
|
||||
{
|
||||
layer->scene = nullptr;
|
||||
|
||||
//visibility
|
||||
if (frameNo < layer->inFrame || frameNo >= layer->outFrame) return;
|
||||
|
||||
_updateTransform(layer, frameNo);
|
||||
_updateTransform(layer, frameNo, exps);
|
||||
|
||||
//full transparent scene. no need to perform
|
||||
if (layer->type != LottieLayer::Null && layer->cache.opacity == 0) return;
|
||||
|
@ -1097,13 +1098,13 @@ static void _updateLayer(LottieLayer* root, LottieLayer* layer, float frameNo)
|
|||
|
||||
if (layer->matte.target && layer->masks.count > 0) TVGERR("LOTTIE", "FIXME: Matte + Masking??");
|
||||
|
||||
if (!_updateMatte(root, layer, frameNo)) return;
|
||||
if (!_updateMatte(root, layer, frameNo, exps)) return;
|
||||
|
||||
_updateMaskings(layer, frameNo);
|
||||
_updateMaskings(layer, frameNo, exps);
|
||||
|
||||
switch (layer->type) {
|
||||
case LottieLayer::Precomp: {
|
||||
_updatePrecomp(layer, frameNo);
|
||||
_updatePrecomp(layer, frameNo, exps);
|
||||
break;
|
||||
}
|
||||
case LottieLayer::Solid: {
|
||||
|
@ -1114,7 +1115,7 @@ static void _updateLayer(LottieLayer* root, LottieLayer* layer, float frameNo)
|
|||
if (!layer->children.empty()) {
|
||||
Inlist<RenderContext> contexts;
|
||||
contexts.back(new RenderContext);
|
||||
_updateChildren(layer, frameNo, contexts);
|
||||
_updateChildren(layer, frameNo, contexts, exps);
|
||||
contexts.free();
|
||||
}
|
||||
break;
|
||||
|
@ -1231,9 +1232,12 @@ bool LottieBuilder::update(LottieComposition* comp, float frameNo)
|
|||
auto root = comp->root;
|
||||
root->scene->clear();
|
||||
|
||||
if (exps && comp->expressions) exps->update(frameNo, comp->timeAtFrame(frameNo));
|
||||
|
||||
for (auto child = root->children.end() - 1; child >= root->children.begin(); --child) {
|
||||
_updateLayer(root, static_cast<LottieLayer*>(*child), frameNo);
|
||||
_updateLayer(root, static_cast<LottieLayer*>(*child), frameNo, exps);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -24,11 +24,24 @@
|
|||
#define _TVG_LOTTIE_BUILDER_H_
|
||||
|
||||
#include "tvgCommon.h"
|
||||
#include "tvgLottieExpressions.h"
|
||||
|
||||
struct LottieComposition;
|
||||
|
||||
struct LottieBuilder
|
||||
{
|
||||
LottieExpressions* exps = nullptr;
|
||||
|
||||
LottieBuilder()
|
||||
{
|
||||
exps = LottieExpressions::instance();
|
||||
}
|
||||
|
||||
~LottieBuilder()
|
||||
{
|
||||
LottieExpressions::retrieve(exps);
|
||||
}
|
||||
|
||||
bool update(LottieComposition* comp, float progress);
|
||||
void build(LottieComposition* comp);
|
||||
};
|
||||
|
|
1261
src/loaders/lottie/tvgLottieExpressions.cpp
Normal file
1261
src/loaders/lottie/tvgLottieExpressions.cpp
Normal file
File diff suppressed because it is too large
Load diff
170
src/loaders/lottie/tvgLottieExpressions.h
Normal file
170
src/loaders/lottie/tvgLottieExpressions.h
Normal file
|
@ -0,0 +1,170 @@
|
|||
/*
|
||||
* Copyright (c) 2024 the ThorVG project. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _TVG_LOTTIE_EXPRESSIONS_H_
|
||||
#define _TVG_LOTTIE_EXPRESSIONS_H_
|
||||
|
||||
#include "tvgCommon.h"
|
||||
|
||||
struct LottieExpression;
|
||||
struct LottieComposition;
|
||||
struct RGB24;
|
||||
|
||||
#ifdef THORVG_LOTTIE_EXPRESSIONS_SUPPORT
|
||||
|
||||
#include "jerryscript.h"
|
||||
|
||||
|
||||
struct LottieExpressions
|
||||
{
|
||||
public:
|
||||
template<typename Property, typename NumType>
|
||||
bool result(float frameNo, NumType& out, LottieExpression* exp)
|
||||
{
|
||||
auto success = true;
|
||||
auto bm_rt = evaluate(frameNo, exp);
|
||||
|
||||
if (auto prop = static_cast<Property*>(jerry_object_get_native_ptr(bm_rt, nullptr))) {
|
||||
out = (*prop)(frameNo);
|
||||
} else if (jerry_value_is_number(bm_rt)) {
|
||||
out = (NumType) jerry_value_as_number(bm_rt);
|
||||
} else {
|
||||
TVGERR("LOTTIE", "Failed dispatching a Value!");
|
||||
success = false;
|
||||
}
|
||||
jerry_value_free(bm_rt);
|
||||
return success;
|
||||
}
|
||||
|
||||
template<typename Property>
|
||||
bool result(float frameNo, Point& out, LottieExpression* exp)
|
||||
{
|
||||
auto success = true;
|
||||
auto bm_rt = evaluate(frameNo, exp);
|
||||
|
||||
if (jerry_value_is_object(bm_rt)) {
|
||||
if (auto prop = static_cast<Property*>(jerry_object_get_native_ptr(bm_rt, nullptr))) {
|
||||
out = (*prop)(frameNo);
|
||||
} else {
|
||||
auto x = jerry_object_get_index(bm_rt, 0);
|
||||
auto y = jerry_object_get_index(bm_rt, 1);
|
||||
out.x = jerry_value_as_number(x);
|
||||
out.y = jerry_value_as_number(y);
|
||||
jerry_value_free(x);
|
||||
jerry_value_free(y);
|
||||
}
|
||||
} else {
|
||||
TVGERR("LOTTIE", "Failed dispatching Point!");
|
||||
success = false;
|
||||
}
|
||||
jerry_value_free(bm_rt);
|
||||
return success;
|
||||
}
|
||||
|
||||
template<typename Property>
|
||||
bool result(float frameNo, RGB24& out, LottieExpression* exp)
|
||||
{
|
||||
auto success = true;
|
||||
auto bm_rt = evaluate(frameNo, exp);
|
||||
|
||||
if (auto color = static_cast<Property*>(jerry_object_get_native_ptr(bm_rt, nullptr))) {
|
||||
out = (*color)(frameNo);
|
||||
} else {
|
||||
TVGERR("LOTTIE", "Failed dispatching Color!");
|
||||
success = false;
|
||||
}
|
||||
jerry_value_free(bm_rt);
|
||||
return success;
|
||||
}
|
||||
|
||||
template<typename Property>
|
||||
bool result(float frameNo, Fill* fill, LottieExpression* exp)
|
||||
{
|
||||
auto success = true;
|
||||
auto bm_rt = evaluate(frameNo, exp);
|
||||
|
||||
if (auto colorStop = static_cast<Property*>(jerry_object_get_native_ptr(bm_rt, nullptr))) {
|
||||
(*colorStop)(frameNo, fill, this);
|
||||
} else {
|
||||
TVGERR("LOTTIE", "Failed dispatching ColorStop!");
|
||||
success = false;
|
||||
}
|
||||
jerry_value_free(bm_rt);
|
||||
return success;
|
||||
}
|
||||
|
||||
template<typename Property>
|
||||
bool result(float frameNo, Array<PathCommand>& cmds, Array<Point>& pts, Matrix* transform, LottieExpression* exp)
|
||||
{
|
||||
auto success = true;
|
||||
auto bm_rt = evaluate(frameNo, exp);
|
||||
|
||||
if (auto pathset = static_cast<Property*>(jerry_object_get_native_ptr(bm_rt, nullptr))) {
|
||||
(*pathset)(frameNo, cmds, pts, transform);
|
||||
} else {
|
||||
TVGERR("LOTTIE", "Failed dispatching PathSet!");
|
||||
success = false;
|
||||
}
|
||||
jerry_value_free(bm_rt);
|
||||
return success;
|
||||
}
|
||||
|
||||
void update(float frameNo, float curTime);
|
||||
|
||||
//singleton (no thread safety)
|
||||
static LottieExpressions* instance();
|
||||
static void retrieve(LottieExpressions* instance);
|
||||
|
||||
private:
|
||||
LottieExpressions();
|
||||
~LottieExpressions();
|
||||
|
||||
jerry_value_t evaluate(float frameNo, LottieExpression* exp);
|
||||
jerry_value_t buildGlobal();
|
||||
void buildComp(LottieComposition* comp);
|
||||
|
||||
//global object, attributes, methods
|
||||
jerry_value_t global;
|
||||
jerry_value_t comp;
|
||||
jerry_value_t layer;
|
||||
jerry_value_t thisComp;
|
||||
jerry_value_t thisLayer;
|
||||
jerry_value_t thisProperty;
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
struct LottieExpressions
|
||||
{
|
||||
template<typename Property, typename NumType> bool result(TVG_UNUSED float, TVG_UNUSED NumType&, TVG_UNUSED LottieExpression*) { return false; }
|
||||
template<typename Property> bool result(TVG_UNUSED float, TVG_UNUSED Point&, LottieExpression*) { return false; }
|
||||
template<typename Property> bool result(TVG_UNUSED float, TVG_UNUSED RGB24&, TVG_UNUSED LottieExpression*) { return false; }
|
||||
template<typename Property> bool result(TVG_UNUSED float, TVG_UNUSED Fill*, TVG_UNUSED LottieExpression*) { return false; }
|
||||
template<typename Property> bool result(TVG_UNUSED float, TVG_UNUSED Array<PathCommand>&, TVG_UNUSED Array<Point>&, TVG_UNUSED Matrix* transform, TVG_UNUSED LottieExpression*) { return false; }
|
||||
void update(TVG_UNUSED float, TVG_UNUSED float) {}
|
||||
static LottieExpressions* instance() { return nullptr; }
|
||||
static void retrieve(TVG_UNUSED LottieExpressions* instance) {}
|
||||
};
|
||||
|
||||
#endif //THORVG_LOTTIE_EXPRESSIONS_SUPPORT
|
||||
|
||||
#endif //_TVG_LOTTIE_EXPRESSIONS_H_
|
|
@ -46,11 +46,11 @@ LottieImage::~LottieImage()
|
|||
}
|
||||
|
||||
|
||||
void LottieTrimpath::segment(float frameNo, float& start, float& end)
|
||||
void LottieTrimpath::segment(float frameNo, float& start, float& end, LottieExpressions* exps)
|
||||
{
|
||||
auto s = this->start(frameNo) * 0.01f;
|
||||
auto e = this->end(frameNo) * 0.01f;
|
||||
auto o = fmodf(this->offset(frameNo), 360.0f) / 360.0f; //0 ~ 1
|
||||
auto s = this->start(frameNo, exps) * 0.01f;
|
||||
auto e = this->end(frameNo, exps) * 0.01f;
|
||||
auto o = fmodf(this->offset(frameNo, exps), 360.0f) / 360.0f; //0 ~ 1
|
||||
|
||||
auto diff = fabs(s - e);
|
||||
if (mathZero(diff)) {
|
||||
|
@ -89,7 +89,7 @@ void LottieTrimpath::segment(float frameNo, float& start, float& end)
|
|||
}
|
||||
|
||||
|
||||
Fill* LottieGradient::fill(float frameNo)
|
||||
Fill* LottieGradient::fill(float frameNo, LottieExpressions* exps)
|
||||
{
|
||||
Fill* fill = nullptr;
|
||||
|
||||
|
@ -126,7 +126,7 @@ Fill* LottieGradient::fill(float frameNo)
|
|||
|
||||
if (!fill) return nullptr;
|
||||
|
||||
colorStops(frameNo, fill);
|
||||
colorStops(frameNo, fill, exps);
|
||||
|
||||
return fill;
|
||||
}
|
||||
|
@ -216,10 +216,10 @@ void LottieLayer::prepare()
|
|||
}
|
||||
|
||||
|
||||
float LottieLayer::remap(float frameNo)
|
||||
float LottieLayer::remap(float frameNo, LottieExpressions* exp)
|
||||
{
|
||||
if (timeRemap.frames || timeRemap.value) {
|
||||
frameNo = comp->frameAtTime(timeRemap(frameNo));
|
||||
frameNo = comp->frameAtTime(timeRemap(frameNo, exp));
|
||||
} else {
|
||||
frameNo -= startFrame;
|
||||
}
|
||||
|
|
|
@ -51,29 +51,23 @@ struct LottieStroke
|
|||
return dashattr->value[no];
|
||||
}
|
||||
|
||||
float dashOffset(float frameNo)
|
||||
float dashOffset(float frameNo, LottieExpressions* exps)
|
||||
{
|
||||
return dash(0)(frameNo);
|
||||
return dash(0)(frameNo, exps);
|
||||
}
|
||||
|
||||
float dashGap(float frameNo)
|
||||
float dashGap(float frameNo, LottieExpressions* exps)
|
||||
{
|
||||
return dash(2)(frameNo);
|
||||
return dash(2)(frameNo, exps);
|
||||
}
|
||||
|
||||
float dashSize(float frameNo)
|
||||
float dashSize(float frameNo, LottieExpressions* exps)
|
||||
{
|
||||
auto d = dash(1)(frameNo);
|
||||
auto d = dash(1)(frameNo, exps);
|
||||
if (d == 0.0f) return 0.1f;
|
||||
else return d;
|
||||
}
|
||||
|
||||
bool dynamic()
|
||||
{
|
||||
if (width.frames || dashattr) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
LottieFloat width = 0.0f;
|
||||
DashAttr* dashattr = nullptr;
|
||||
float miterLimit = 0;
|
||||
|
@ -221,7 +215,7 @@ struct LottieTrimpath : LottieObject
|
|||
return false;
|
||||
}
|
||||
|
||||
void segment(float frameNo, float& start, float& end);
|
||||
void segment(float frameNo, float& start, float& end, LottieExpressions* exps);
|
||||
|
||||
LottieFloat start = 0.0f;
|
||||
LottieFloat end = 100.0f;
|
||||
|
@ -489,7 +483,7 @@ struct LottieGradient : LottieObject
|
|||
return false;
|
||||
}
|
||||
|
||||
Fill* fill(float frameNo);
|
||||
Fill* fill(float frameNo, LottieExpressions* exps);
|
||||
|
||||
LottiePoint start = Point{0.0f, 0.0f};
|
||||
LottiePoint end = Point{0.0f, 0.0f};
|
||||
|
@ -626,7 +620,7 @@ struct LottieLayer : LottieGroup
|
|||
bool mergeable() override { return false; }
|
||||
|
||||
void prepare();
|
||||
float remap(float frameNo);
|
||||
float remap(float frameNo, LottieExpressions* exp);
|
||||
|
||||
struct {
|
||||
CompositeMethod type = CompositeMethod::None;
|
||||
|
@ -823,6 +817,7 @@ struct LottieComposition
|
|||
Array<LottieFont*> fonts;
|
||||
Array<LottieSlot*> slots;
|
||||
Array<LottieMarker*> markers;
|
||||
bool expressions = false;
|
||||
bool initiated = false;
|
||||
};
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include "tvgCompressor.h"
|
||||
#include "tvgLottieModel.h"
|
||||
#include "tvgLottieParser.h"
|
||||
#include "tvgLottieExpressions.h"
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
|
@ -33,6 +34,22 @@
|
|||
#define KEY_AS(name) !strcmp(key, name)
|
||||
|
||||
|
||||
static LottieExpression* _expression(char* code, LottieComposition* comp, LottieLayer* layer, LottieObject* object, LottieProperty* property, LottieProperty::Type type)
|
||||
{
|
||||
if (!comp->expressions) comp->expressions = true;
|
||||
|
||||
auto inst = new LottieExpression;
|
||||
inst->code = code;
|
||||
inst->comp = comp;
|
||||
inst->layer = layer;
|
||||
inst->object = object;
|
||||
inst->property = property;
|
||||
inst->type = type;
|
||||
|
||||
return inst;
|
||||
}
|
||||
|
||||
|
||||
static char* _int2str(int num)
|
||||
{
|
||||
char str[20];
|
||||
|
@ -486,7 +503,10 @@ void LottieParser::parseProperty(T& prop, LottieObject* obj)
|
|||
return;
|
||||
}
|
||||
comp->slots.push(new LottieSlot(sid, obj, type));
|
||||
} else skip(key);
|
||||
} else if (!strcmp(key, "x")) {
|
||||
prop.exp = _expression(getStringCopy(), comp, context.layer, context.parent, &prop, type);
|
||||
}
|
||||
else skip(key);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -552,6 +572,7 @@ LottieTransform* LottieParser::parseTransform(bool ddd)
|
|||
//check separateCoord to figure out whether "x(expression)" / "x(coord)"
|
||||
else if (transform->coords && KEY_AS("x")) parseProperty<LottieProperty::Type::Float>(transform->coords->x);
|
||||
else if (transform->coords && KEY_AS("y")) parseProperty<LottieProperty::Type::Float>(transform->coords->y);
|
||||
else if (KEY_AS("x")) transform->position.exp = _expression(getStringCopy(), comp, context.layer, context.parent, &transform->position, LottieProperty::Type::Position);
|
||||
else skip(key);
|
||||
}
|
||||
}
|
||||
|
@ -638,7 +659,7 @@ LottieSolidStroke* LottieParser::parseSolidStroke()
|
|||
}
|
||||
|
||||
|
||||
void LottieParser::getPathSet(LottiePathSet& path)
|
||||
void LottieParser::getPathSet(LottiePathSet& path)
|
||||
{
|
||||
enterObject();
|
||||
while (auto key = nextObjectKey()) {
|
||||
|
@ -649,6 +670,8 @@ LottieSolidStroke* LottieParser::parseSolidStroke()
|
|||
} else {
|
||||
getValue(path.value);
|
||||
}
|
||||
} else if (!strcmp(key, "x")) {
|
||||
path.exp = _expression(getStringCopy(), comp, context.layer, context.parent, &path, LottieProperty::Type::PathSet);
|
||||
} else skip(key);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,6 +28,13 @@
|
|||
#include "tvgMath.h"
|
||||
#include "tvgLines.h"
|
||||
#include "tvgLottieInterpolator.h"
|
||||
#include "tvgLottieExpressions.h"
|
||||
|
||||
|
||||
struct LottieFont;
|
||||
struct LottieLayer;
|
||||
struct LottieObject;
|
||||
|
||||
|
||||
struct PathSet
|
||||
{
|
||||
|
@ -51,8 +58,6 @@ struct ColorStop
|
|||
};
|
||||
|
||||
|
||||
struct LottieFont;
|
||||
|
||||
struct TextDocument
|
||||
{
|
||||
char* text = nullptr;
|
||||
|
@ -93,35 +98,6 @@ static inline RGB24 operator*(const RGB24& lhs, float rhs)
|
|||
}
|
||||
|
||||
|
||||
static void copy(PathSet& pathset, Array<Point>& outPts, Matrix* transform)
|
||||
{
|
||||
Array<Point> inPts;
|
||||
|
||||
if (transform) {
|
||||
for (int i = 0; i < pathset.ptsCnt; ++i) {
|
||||
Point pt = pathset.pts[i];
|
||||
mathMultiply(&pt, transform);
|
||||
outPts.push(pt);
|
||||
}
|
||||
} else {
|
||||
inPts.data = pathset.pts;
|
||||
inPts.count = pathset.ptsCnt;
|
||||
outPts.push(inPts);
|
||||
inPts.data = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void copy(PathSet& pathset, Array<PathCommand>& outCmds)
|
||||
{
|
||||
Array<PathCommand> inCmds;
|
||||
inCmds.data = pathset.cmds;
|
||||
inCmds.count = pathset.cmdsCnt;
|
||||
outCmds.push(inCmds);
|
||||
inCmds.data = nullptr;
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
struct LottieScalarFrame
|
||||
{
|
||||
|
@ -192,8 +168,76 @@ struct LottieVectorFrame
|
|||
};
|
||||
|
||||
|
||||
//Property would have an either keyframes or single value.
|
||||
struct LottieProperty
|
||||
{
|
||||
enum class Type : uint8_t { Point = 0, Float, Opacity, Color, PathSet, ColorStop, Position, TextDoc, Invalid };
|
||||
virtual ~LottieProperty() {}
|
||||
|
||||
LottieExpression* exp = nullptr;
|
||||
|
||||
//TODO: Apply common bodies?
|
||||
virtual uint32_t frameCnt() = 0;
|
||||
virtual uint32_t nearest(float time) = 0;
|
||||
virtual float frameNo(int32_t key) = 0;
|
||||
};
|
||||
|
||||
|
||||
struct LottieExpression
|
||||
{
|
||||
enum LoopMode : uint8_t { None = 0, InCycle = 1, InPingPong, InOffset, InContinue, OutCycle, OutPingPong, OutOffset, OutContinue };
|
||||
|
||||
char* code;
|
||||
LottieComposition* comp;
|
||||
LottieLayer* layer;
|
||||
LottieObject* object;
|
||||
LottieProperty* property;
|
||||
LottieProperty::Type type;
|
||||
|
||||
struct {
|
||||
uint32_t key = 0; //the keyframe number repeating to
|
||||
float in = FLT_MAX; //looping duration in frame number
|
||||
LoopMode mode = None;
|
||||
} loop;
|
||||
;
|
||||
~LottieExpression()
|
||||
{
|
||||
free(code);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
static void _copy(PathSet& pathset, Array<Point>& outPts, Matrix* transform)
|
||||
{
|
||||
Array<Point> inPts;
|
||||
|
||||
if (transform) {
|
||||
for (int i = 0; i < pathset.ptsCnt; ++i) {
|
||||
Point pt = pathset.pts[i];
|
||||
mathMultiply(&pt, transform);
|
||||
outPts.push(pt);
|
||||
}
|
||||
} else {
|
||||
inPts.data = pathset.pts;
|
||||
inPts.count = pathset.ptsCnt;
|
||||
outPts.push(inPts);
|
||||
inPts.data = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void _copy(PathSet& pathset, Array<PathCommand>& outCmds)
|
||||
{
|
||||
Array<PathCommand> inCmds;
|
||||
inCmds.data = pathset.cmds;
|
||||
inCmds.count = pathset.cmdsCnt;
|
||||
outCmds.push(inCmds);
|
||||
inCmds.data = nullptr;
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
uint32_t bsearch(T* frames, float frameNo)
|
||||
uint32_t _bsearch(T* frames, float frameNo)
|
||||
{
|
||||
int32_t low = 0;
|
||||
int32_t high = int32_t(frames->count) - 1;
|
||||
|
@ -206,16 +250,50 @@ uint32_t bsearch(T* frames, float frameNo)
|
|||
}
|
||||
if (high < low) low = high;
|
||||
if (low < 0) low = 0;
|
||||
|
||||
return low;
|
||||
}
|
||||
|
||||
|
||||
struct LottieProperty
|
||||
template<typename T>
|
||||
uint32_t _nearest(T* frames, float frameNo)
|
||||
{
|
||||
enum class Type : uint8_t { Point = 0, Float, Opacity, Color, PathSet, ColorStop, Position, TextDoc, Invalid };
|
||||
virtual ~LottieProperty() {}
|
||||
};
|
||||
if (frames) {
|
||||
auto key = _bsearch(frames, frameNo);
|
||||
if (key == frames->count - 1) return key;
|
||||
return (fabsf(frames->data[key].no - frameNo) < fabsf(frames->data[key + 1].no - frameNo)) ? key : (key + 1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
float _frameNo(T* frames, int32_t key)
|
||||
{
|
||||
if (!frames) return 0.0f;
|
||||
if (key < 0) key = 0;
|
||||
if (key >= (int32_t) frames->count) key = (int32_t)(frames->count - 1);
|
||||
return (*frames)[key].no;
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
float _loop(T* frames, float frameNo, LottieExpression* exp)
|
||||
{
|
||||
if (frameNo >= exp->loop.in || frameNo < frames->first().no ||frameNo < frames->last().no) return frameNo;
|
||||
|
||||
switch (exp->loop.mode) {
|
||||
case LottieExpression::LoopMode::InCycle: {
|
||||
frameNo -= frames->first().no;
|
||||
return fmodf(frameNo, frames->last().no - frames->first().no) + (*frames)[exp->loop.key].no;
|
||||
}
|
||||
case LottieExpression::LoopMode::OutCycle: {
|
||||
frameNo -= frames->first().no;
|
||||
return fmodf(frameNo, (*frames)[frames->count - 1 - exp->loop.key].no - frames->first().no) + frames->first().no;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
return frameNo;
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
|
@ -236,6 +314,26 @@ struct LottieGenericProperty : LottieProperty
|
|||
void release()
|
||||
{
|
||||
delete(frames);
|
||||
frames = nullptr;
|
||||
if (exp) {
|
||||
delete(exp);
|
||||
exp = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t nearest(float frameNo) override
|
||||
{
|
||||
return _nearest(frames, frameNo);
|
||||
}
|
||||
|
||||
uint32_t frameCnt() override
|
||||
{
|
||||
return frames ? frames->count : 1;
|
||||
}
|
||||
|
||||
float frameNo(int32_t key) override
|
||||
{
|
||||
return _frameNo(frames, key);
|
||||
}
|
||||
|
||||
LottieScalarFrame<T>& newFrame()
|
||||
|
@ -261,11 +359,21 @@ struct LottieGenericProperty : LottieProperty
|
|||
if (frames->count == 1 || frameNo <= frames->first().no) return frames->first().value;
|
||||
if (frameNo >= frames->last().no) return frames->last().value;
|
||||
|
||||
auto frame = frames->data + bsearch(frames, frameNo);
|
||||
auto frame = frames->data + _bsearch(frames, frameNo);
|
||||
if (frame->no == frameNo) return frame->value;
|
||||
return frame->interpolate(frame + 1, frameNo);
|
||||
}
|
||||
|
||||
T operator()(float frameNo, LottieExpressions* exps)
|
||||
{
|
||||
T out{};
|
||||
if (exps && exp) {
|
||||
if (exp->loop.mode != LottieExpression::LoopMode::None) frameNo = _loop(frames, frameNo, exp);
|
||||
if (exps->result<LottieGenericProperty<T>>(frameNo, out, exp)) return out;
|
||||
}
|
||||
return operator()(frameNo);
|
||||
}
|
||||
|
||||
T& operator=(const T& other)
|
||||
{
|
||||
//shallow copy, used for slot overriding
|
||||
|
@ -293,6 +401,11 @@ struct LottiePathSet : LottieProperty
|
|||
|
||||
void release()
|
||||
{
|
||||
if (exp) {
|
||||
delete(exp);
|
||||
exp = nullptr;
|
||||
}
|
||||
|
||||
free(value.cmds);
|
||||
free(value.pts);
|
||||
|
||||
|
@ -306,6 +419,21 @@ struct LottiePathSet : LottieProperty
|
|||
free(frames);
|
||||
}
|
||||
|
||||
uint32_t nearest(float frameNo) override
|
||||
{
|
||||
return _nearest(frames, frameNo);
|
||||
}
|
||||
|
||||
uint32_t frameCnt() override
|
||||
{
|
||||
return frames ? frames->count : 1;
|
||||
}
|
||||
|
||||
float frameNo(int32_t key) override
|
||||
{
|
||||
return _frameNo(frames, key);
|
||||
}
|
||||
|
||||
LottieScalarFrame<PathSet>& newFrame()
|
||||
{
|
||||
if (!frames) {
|
||||
|
@ -325,43 +453,43 @@ struct LottiePathSet : LottieProperty
|
|||
return (*frames)[frames->count];
|
||||
}
|
||||
|
||||
bool operator()(float frameNo, Array<PathCommand>& cmds, Array<Point>& pts, Matrix* transform = nullptr)
|
||||
bool operator()(float frameNo, Array<PathCommand>& cmds, Array<Point>& pts, Matrix* transform)
|
||||
{
|
||||
if (!frames) {
|
||||
copy(value, cmds);
|
||||
copy(value, pts, transform);
|
||||
_copy(value, cmds);
|
||||
_copy(value, pts, transform);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (frames->count == 1 || frameNo <= frames->first().no) {
|
||||
copy(frames->first().value, cmds);
|
||||
copy(frames->first().value, pts, transform);
|
||||
_copy(frames->first().value, cmds);
|
||||
_copy(frames->first().value, pts, transform);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (frameNo >= frames->last().no) {
|
||||
copy(frames->last().value, cmds);
|
||||
copy(frames->last().value, pts, transform);
|
||||
_copy(frames->last().value, cmds);
|
||||
_copy(frames->last().value, pts, transform);
|
||||
return true;
|
||||
}
|
||||
|
||||
auto frame = frames->data + bsearch(frames, frameNo);
|
||||
auto frame = frames->data + _bsearch(frames, frameNo);
|
||||
|
||||
if (frame->no == frameNo) {
|
||||
copy(frame->value, cmds);
|
||||
copy(frame->value, pts, transform);
|
||||
_copy(frame->value, cmds);
|
||||
_copy(frame->value, pts, transform);
|
||||
return true;
|
||||
}
|
||||
|
||||
//interpolate
|
||||
copy(frame->value, cmds);
|
||||
_copy(frame->value, cmds);
|
||||
|
||||
auto t = (frameNo - frame->no) / ((frame + 1)->no - frame->no);
|
||||
if (frame->interpolator) t = frame->interpolator->progress(t);
|
||||
|
||||
if (frame->hold) {
|
||||
if (t < 1.0f) copy(frame->value, pts, transform);
|
||||
else copy((frame + 1)->value, pts, transform);
|
||||
if (t < 1.0f) _copy(frame->value, pts, transform);
|
||||
else _copy((frame + 1)->value, pts, transform);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -376,6 +504,16 @@ struct LottiePathSet : LottieProperty
|
|||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool operator()(float frameNo, Array<PathCommand>& cmds, Array<Point>& pts, Matrix* transform, LottieExpressions* exps)
|
||||
{
|
||||
if (exps && exp) {
|
||||
if (exp->loop.mode != LottieExpression::LoopMode::None) frameNo = _loop(frames, frameNo, exp);
|
||||
if (exps->result<LottiePathSet>(frameNo, cmds, pts, transform, exp)) return true;
|
||||
}
|
||||
return operator()(frameNo, cmds, pts, transform);
|
||||
}
|
||||
|
||||
void prepare() {}
|
||||
};
|
||||
|
||||
|
@ -394,6 +532,11 @@ struct LottieColorStop : LottieProperty
|
|||
|
||||
void release()
|
||||
{
|
||||
if (exp) {
|
||||
delete(exp);
|
||||
exp = nullptr;
|
||||
}
|
||||
|
||||
if (value.data) {
|
||||
free(value.data);
|
||||
value.data = nullptr;
|
||||
|
@ -409,6 +552,21 @@ struct LottieColorStop : LottieProperty
|
|||
frames = nullptr;
|
||||
}
|
||||
|
||||
uint32_t nearest(float frameNo) override
|
||||
{
|
||||
return _nearest(frames, frameNo);
|
||||
}
|
||||
|
||||
uint32_t frameCnt() override
|
||||
{
|
||||
return frames ? frames->count : 1;
|
||||
}
|
||||
|
||||
float frameNo(int32_t key) override
|
||||
{
|
||||
return _frameNo(frames, key);
|
||||
}
|
||||
|
||||
LottieScalarFrame<ColorStop>& newFrame()
|
||||
{
|
||||
if (!frames) {
|
||||
|
@ -428,27 +586,26 @@ struct LottieColorStop : LottieProperty
|
|||
return (*frames)[frames->count];
|
||||
}
|
||||
|
||||
void operator()(float frameNo, Fill* fill)
|
||||
Result operator()(float frameNo, Fill* fill, LottieExpressions* exps)
|
||||
{
|
||||
if (!frames) {
|
||||
fill->colorStops(value.data, count);
|
||||
return;
|
||||
if (exps && exp) {
|
||||
if (exp->loop.mode != LottieExpression::LoopMode::None) frameNo = _loop(frames, frameNo, exp);
|
||||
if (exps->result<LottieColorStop>(frameNo, fill, exp)) return Result::Success;
|
||||
}
|
||||
|
||||
if (!frames) return fill->colorStops(value.data, count);
|
||||
|
||||
if (frames->count == 1 || frameNo <= frames->first().no) {
|
||||
fill->colorStops(frames->first().value.data, count);
|
||||
return;
|
||||
return fill->colorStops(frames->first().value.data, count);
|
||||
}
|
||||
|
||||
if (frameNo >= frames->last().no) {
|
||||
fill->colorStops(frames->last().value.data, count);
|
||||
return;
|
||||
return fill->colorStops(frames->last().value.data, count);
|
||||
}
|
||||
|
||||
auto frame = frames->data + bsearch(frames, frameNo);
|
||||
auto frame = frames->data + _bsearch(frames, frameNo);
|
||||
if (frame->no == frameNo) {
|
||||
fill->colorStops(frame->value.data, count);
|
||||
return;
|
||||
return fill->colorStops(frame->value.data, count);
|
||||
}
|
||||
|
||||
//interpolate
|
||||
|
@ -473,7 +630,7 @@ struct LottieColorStop : LottieProperty
|
|||
auto a = mathLerp(s->a, e->a, t);
|
||||
result.push({offset, r, g, b, a});
|
||||
}
|
||||
fill->colorStops(result.data, count);
|
||||
return fill->colorStops(result.data, count);
|
||||
}
|
||||
|
||||
LottieColorStop& operator=(const LottieColorStop& other)
|
||||
|
@ -513,6 +670,27 @@ struct LottiePosition : LottieProperty
|
|||
void release()
|
||||
{
|
||||
delete(frames);
|
||||
frames = nullptr;
|
||||
|
||||
if (exp) {
|
||||
delete(exp);
|
||||
exp = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t nearest(float frameNo) override
|
||||
{
|
||||
return _nearest(frames, frameNo);
|
||||
}
|
||||
|
||||
uint32_t frameCnt() override
|
||||
{
|
||||
return frames ? frames->count : 1;
|
||||
}
|
||||
|
||||
float frameNo(int32_t key) override
|
||||
{
|
||||
return _frameNo(frames, key);
|
||||
}
|
||||
|
||||
LottieVectorFrame<Point>& newFrame()
|
||||
|
@ -538,18 +716,28 @@ struct LottiePosition : LottieProperty
|
|||
if (frames->count == 1 || frameNo <= frames->first().no) return frames->first().value;
|
||||
if (frameNo >= frames->last().no) return frames->last().value;
|
||||
|
||||
auto frame = frames->data + bsearch(frames, frameNo);
|
||||
auto frame = frames->data + _bsearch(frames, frameNo);
|
||||
if (frame->no == frameNo) return frame->value;
|
||||
return frame->interpolate(frame + 1, frameNo);
|
||||
}
|
||||
|
||||
Point operator()(float frameNo, LottieExpressions* exps)
|
||||
{
|
||||
Point out{};
|
||||
if (exps && exp) {
|
||||
if (exp->loop.mode != LottieExpression::LoopMode::None) frameNo = _loop(frames, frameNo, exp);
|
||||
if (exps->result<LottiePosition>(frameNo, out, exp)) return out;
|
||||
}
|
||||
return operator()(frameNo);
|
||||
}
|
||||
|
||||
float angle(float frameNo)
|
||||
{
|
||||
if (!frames) return 0;
|
||||
if (frames->count == 1 || frameNo <= frames->first().no) return 0;
|
||||
if (frameNo >= frames->last().no) return 0;
|
||||
|
||||
auto frame = frames->data + bsearch(frames, frameNo);
|
||||
auto frame = frames->data + _bsearch(frames, frameNo);
|
||||
return frame->angle(frame + 1, frameNo);
|
||||
}
|
||||
|
||||
|
@ -575,6 +763,11 @@ struct LottieTextDoc : LottieProperty
|
|||
|
||||
void release()
|
||||
{
|
||||
if (exp) {
|
||||
delete(exp);
|
||||
exp = nullptr;
|
||||
}
|
||||
|
||||
if (value.text) {
|
||||
free(value.text);
|
||||
value.text = nullptr;
|
||||
|
@ -594,6 +787,21 @@ struct LottieTextDoc : LottieProperty
|
|||
frames = nullptr;
|
||||
}
|
||||
|
||||
uint32_t nearest(float frameNo) override
|
||||
{
|
||||
return _nearest(frames, frameNo);
|
||||
}
|
||||
|
||||
uint32_t frameCnt() override
|
||||
{
|
||||
return frames ? frames->count : 1;
|
||||
}
|
||||
|
||||
float frameNo(int32_t key) override
|
||||
{
|
||||
return _frameNo(frames, key);
|
||||
}
|
||||
|
||||
LottieScalarFrame<TextDocument>& newFrame()
|
||||
{
|
||||
if (!frames) frames = new Array<LottieScalarFrame<TextDocument>>;
|
||||
|
@ -617,7 +825,7 @@ struct LottieTextDoc : LottieProperty
|
|||
if (frames->count == 1 || frameNo <= frames->first().no) return frames->first().value;
|
||||
if (frameNo >= frames->last().no) return frames->last().value;
|
||||
|
||||
auto frame = frames->data + bsearch(frames, frameNo);
|
||||
auto frame = frames->data + _bsearch(frames, frameNo);
|
||||
return frame->value;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue