mirror of
https://github.com/thorvg/thorvg.git
synced 2025-06-14 12:04:29 +00:00
loader/lottie: support the repeater property
This commit is contained in:
parent
2fab9fb7d9
commit
a49ac81543
11 changed files with 353 additions and 169 deletions
1
src/examples/images/5317-fireworkds.json
Normal file
1
src/examples/images/5317-fireworkds.json
Normal file
File diff suppressed because one or more lines are too long
1
src/examples/images/abstract_circle.json
Normal file
1
src/examples/images/abstract_circle.json
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{"v":"5.1.16","fr":15,"ip":0,"op":51,"w":500,"h":500,"nm":"El 28","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Circle Abstract","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":49,"s":[100],"e":[0]},{"t":51}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[250.316,250.684,0],"ix":2},"a":{"a":0,"k":[280,0,0],"ix":1},"s":{"a":0,"k":[225,225,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[145,145],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.190695878571,0.889764404297,0.972549019608,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.583]},"o":{"x":[0.01],"y":[0.006]},"n":["0p833_0p583_0p01_0p006"],"t":22.2,"s":[3],"e":[0]},{"t":49.2001953125}],"ix":5},"lc":1,"lj":1,"ml":4,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[236,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.99,0.99],"y":[0.997,0.997]},"o":{"x":[1,1],"y":[1,1]},"n":["0p99_0p997_1_1","0p99_0p997_1_1"],"t":0,"s":[0,0],"e":[50,50]},{"i":{"x":[0.833,0.833],"y":[0.676,0.676]},"o":{"x":[0.01,0.01],"y":[0.043,0.043]},"n":["0p833_0p676_0p01_0p043","0p833_0p676_0p01_0p043"],"t":6,"s":[50,50],"e":[75,75]},{"t":49.2001953125}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"rp","c":{"a":1,"k":[{"i":{"x":[0.99],"y":[0.985]},"o":{"x":[1],"y":[2.692]},"n":["0p99_0p985_1_2p692"],"t":0,"s":[0],"e":[18]},{"i":{"x":[0.833],"y":[0.288]},"o":{"x":[0.01],"y":[-0.02]},"n":["0p833_0p288_0p01_-0p02"],"t":21,"s":[18],"e":[0]},{"t":49.2001953125}],"ix":1},"o":{"a":0,"k":5,"ix":2},"m":1,"ix":2,"tr":{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[280,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":20,"ix":4},"so":{"a":0,"k":100,"ix":5},"eo":{"a":0,"k":100,"ix":6},"nm":"Transform"},"nm":"Repeater 1","mn":"ADBE Vector Filter - Repeater","hd":false}],"ip":0,"op":51,"st":0,"bm":0}],"markers":[]}
|
1
src/examples/images/browser_click.json
Normal file
1
src/examples/images/browser_click.json
Normal file
File diff suppressed because one or more lines are too long
1
src/examples/images/focal_test.json
Normal file
1
src/examples/images/focal_test.json
Normal file
File diff suppressed because one or more lines are too long
1
src/examples/images/game_finished.json
Normal file
1
src/examples/images/game_finished.json
Normal file
File diff suppressed because one or more lines are too long
1
src/examples/images/message.json
Normal file
1
src/examples/images/message.json
Normal file
File diff suppressed because one or more lines are too long
1
src/examples/images/morphing_anim.json
Normal file
1
src/examples/images/morphing_anim.json
Normal file
File diff suppressed because one or more lines are too long
|
@ -31,7 +31,35 @@
|
||||||
/* Internal Class Implementation */
|
/* Internal Class Implementation */
|
||||||
/************************************************************************/
|
/************************************************************************/
|
||||||
|
|
||||||
static void _updateChildren(LottieGroup* parent, int32_t frameNo, Shape* baseShape, float roundedCorner);
|
struct LottieModifier
|
||||||
|
{
|
||||||
|
Shape* merging = nullptr; //merging shapes if possible (if shapes have same properties)
|
||||||
|
float roundness = 0.0f;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
int cnt;
|
||||||
|
float offset;
|
||||||
|
Point position;
|
||||||
|
Point anchor;
|
||||||
|
Point scale;
|
||||||
|
float rotation;
|
||||||
|
uint8_t startOpacity;
|
||||||
|
uint8_t endOpacity;
|
||||||
|
bool interpOpacity;
|
||||||
|
bool inorder;
|
||||||
|
bool valid = false;
|
||||||
|
} repeater;
|
||||||
|
|
||||||
|
LottieModifier() = default;
|
||||||
|
LottieModifier(const LottieModifier& rhs)
|
||||||
|
{
|
||||||
|
repeater = rhs.repeater;
|
||||||
|
roundness = rhs.roundness;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static void _updateChildren(LottieGroup* parent, int32_t frameNo, Shape* propagtor, LottieModifier& modifier);
|
||||||
static void _updateLayer(LottieLayer* root, LottieLayer* layer, int32_t frameNo);
|
static void _updateLayer(LottieLayer* root, LottieLayer* layer, int32_t frameNo);
|
||||||
static bool _buildPrecomp(LottieComposition* comp, LottieGroup* parent);
|
static bool _buildPrecomp(LottieComposition* comp, LottieGroup* parent);
|
||||||
|
|
||||||
|
@ -92,29 +120,29 @@ static void _updateTransform(LottieLayer* layer, int32_t frameNo)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static Shape* _updateTransform(Paint* paint, LottieTransform* transform, int32_t frameNo)
|
static void _updateTransform(LottieTransform* transform, int32_t frameNo, Paint* propagtor, LottieModifier& modifier)
|
||||||
{
|
{
|
||||||
if (!transform) return nullptr;
|
if (!transform) return;
|
||||||
|
|
||||||
|
modifier.merging = nullptr;
|
||||||
|
|
||||||
Matrix matrix;
|
Matrix matrix;
|
||||||
uint8_t opacity;
|
uint8_t opacity;
|
||||||
if (!_updateTransform(transform, frameNo, false, matrix, opacity)) return nullptr;
|
if (!_updateTransform(transform, frameNo, false, matrix, opacity)) return;
|
||||||
|
|
||||||
auto pmatrix = P(paint)->transform();
|
auto pmatrix = P(propagtor)->transform();
|
||||||
paint->transform(pmatrix ? mathMultiply(pmatrix, &matrix) : matrix);
|
propagtor->transform(pmatrix ? mathMultiply(pmatrix, &matrix) : matrix);
|
||||||
paint->opacity(opacity);
|
propagtor->opacity(opacity);
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static Shape* _updateGroup(LottieGroup* parent, LottieGroup* group, int32_t frameNo, Shape* baseShape, float roundedCorner)
|
static void _updateGroup(LottieGroup* parent, LottieGroup* group, int32_t frameNo, Shape* propagtor, LottieModifier& modifier)
|
||||||
{
|
{
|
||||||
//Prepare render data
|
//Prepare render data
|
||||||
group->scene = parent->scene;
|
group->scene = parent->scene;
|
||||||
|
|
||||||
auto opacity = group->opacity(frameNo);
|
auto opacity = group->opacity(frameNo);
|
||||||
if (opacity == 0) return nullptr;
|
if (opacity == 0) return;
|
||||||
|
|
||||||
if (group->transform) {
|
if (group->transform) {
|
||||||
TVGERR("LOTTIE", "group transform is not working!");
|
TVGERR("LOTTIE", "group transform is not working!");
|
||||||
|
@ -127,121 +155,191 @@ static Shape* _updateGroup(LottieGroup* parent, LottieGroup* group, int32_t fram
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
_updateChildren(group, frameNo, baseShape, roundedCorner);
|
auto modifier2 = modifier;
|
||||||
return nullptr;
|
_updateChildren(group, frameNo, propagtor, modifier2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static Shape* _updateFill(LottieSolidFill* fill, int32_t frameNo, Shape* baseShape)
|
static void _updateFill(LottieSolidFill* fill, int32_t frameNo, Shape* propagtor, LottieModifier& modifier)
|
||||||
{
|
{
|
||||||
|
//TODO: Skip if property has not been modified actually.
|
||||||
|
modifier.merging = nullptr;
|
||||||
|
|
||||||
auto color = fill->color(frameNo);
|
auto color = fill->color(frameNo);
|
||||||
baseShape->fill(color.rgb[0], color.rgb[1], color.rgb[2], fill->opacity(frameNo));
|
propagtor->fill(color.rgb[0], color.rgb[1], color.rgb[2], fill->opacity(frameNo));
|
||||||
baseShape->fill(fill->rule);
|
propagtor->fill(fill->rule);
|
||||||
return nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static Shape* _updateStroke(LottieStroke* stroke, int32_t frameNo, Shape* baseShape)
|
static void _updateStroke(LottieStroke* stroke, int32_t frameNo, Shape* propagtor)
|
||||||
{
|
{
|
||||||
baseShape->stroke(stroke->width(frameNo));
|
propagtor->stroke(stroke->width(frameNo));
|
||||||
baseShape->stroke(stroke->cap);
|
propagtor->stroke(stroke->cap);
|
||||||
baseShape->stroke(stroke->join);
|
propagtor->stroke(stroke->join);
|
||||||
baseShape->strokeMiterlimit(stroke->miterLimit);
|
propagtor->strokeMiterlimit(stroke->miterLimit);
|
||||||
|
|
||||||
if (stroke->dashattr) {
|
if (stroke->dashattr) {
|
||||||
float dashes[2];
|
float dashes[2];
|
||||||
dashes[0] = stroke->dashSize(frameNo);
|
dashes[0] = stroke->dashSize(frameNo);
|
||||||
dashes[1] = dashes[0] + stroke->dashGap(frameNo);
|
dashes[1] = dashes[0] + stroke->dashGap(frameNo);
|
||||||
P(baseShape)->strokeDash(dashes, 2, stroke->dashOffset(frameNo));
|
P(propagtor)->strokeDash(dashes, 2, stroke->dashOffset(frameNo));
|
||||||
} else {
|
} else {
|
||||||
baseShape->stroke(nullptr, 0);
|
propagtor->stroke(nullptr, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void _updateStroke(LottieSolidStroke* stroke, int32_t frameNo, Shape* propagtor, LottieModifier& modifier)
|
||||||
|
{
|
||||||
|
modifier.merging = nullptr;
|
||||||
|
|
||||||
|
auto color = stroke->color(frameNo);
|
||||||
|
propagtor->stroke(color.rgb[0], color.rgb[1], color.rgb[2], stroke->opacity(frameNo));
|
||||||
|
_updateStroke(static_cast<LottieStroke*>(stroke), frameNo, propagtor);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void _updateStroke(LottieGradientStroke* stroke, int32_t frameNo, Shape* propagtor, LottieModifier& modifier)
|
||||||
|
{
|
||||||
|
modifier.merging = nullptr;
|
||||||
|
|
||||||
|
propagtor->stroke(unique_ptr<Fill>(stroke->fill(frameNo)));
|
||||||
|
_updateStroke(static_cast<LottieStroke*>(stroke), frameNo, propagtor);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static Shape* _updateFill(LottieGradientFill* fill, int32_t frameNo, Shape* propagtor, LottieModifier& modifier)
|
||||||
|
{
|
||||||
|
modifier.merging = nullptr;
|
||||||
|
|
||||||
|
//TODO: reuse the fill instance?
|
||||||
|
propagtor->fill(unique_ptr<Fill>(fill->fill(frameNo)));
|
||||||
|
propagtor->fill(fill->rule);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static Shape* _draw(LottieGroup* parent, int32_t frameNo, Shape* propagtor, LottieModifier& modifier)
|
||||||
|
{
|
||||||
|
if (modifier.merging) return modifier.merging;
|
||||||
|
|
||||||
|
auto shape = cast<Shape>(propagtor->duplicate());
|
||||||
|
modifier.merging = shape.get();
|
||||||
|
parent->scene->push(std::move(shape));
|
||||||
|
|
||||||
|
return modifier.merging;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//OPTIMIZE: path?
|
||||||
|
static void _repeat(LottieGroup* parent, int32_t frameNo, Shape* propagtor, unique_ptr<Shape> path, LottieModifier& modifier)
|
||||||
|
{
|
||||||
|
auto& repeater = modifier.repeater;
|
||||||
|
|
||||||
|
Array<Shape*> shapes;
|
||||||
|
shapes.reserve(repeater.cnt);
|
||||||
|
|
||||||
|
for (int i = 0; i < repeater.cnt; ++i) {
|
||||||
|
auto multiplier = repeater.offset + static_cast<float>(i);
|
||||||
|
|
||||||
|
auto shape = static_cast<Shape*>(propagtor->duplicate());
|
||||||
|
P(shape)->rs.path = P(path.get())->rs.path;
|
||||||
|
|
||||||
|
auto opacity = repeater.interpOpacity ? mathLerp<uint8_t>(repeater.startOpacity, repeater.endOpacity, static_cast<float>(i + 1) / repeater.cnt) : repeater.startOpacity;
|
||||||
|
shape->opacity(opacity);
|
||||||
|
|
||||||
|
Matrix m;
|
||||||
|
mathIdentity(&m);
|
||||||
|
mathTranslate(&m, repeater.position.x * multiplier + repeater.anchor.x, repeater.position.y * multiplier + repeater.anchor.y);
|
||||||
|
mathScale(&m, powf(repeater.scale.x * 0.01f, multiplier), powf(repeater.scale.y * 0.01f, multiplier));
|
||||||
|
mathRotate(&m, repeater.rotation * multiplier);
|
||||||
|
mathTranslateR(&m, -repeater.anchor.x, -repeater.anchor.y);
|
||||||
|
|
||||||
|
auto pm = PP(shape)->transform();
|
||||||
|
shape->transform(pm ? mathMultiply(&m, pm) : m);
|
||||||
|
|
||||||
|
if (modifier.roundness > 1.0f && P(shape)->rs.stroke) {
|
||||||
|
TVGERR("LOTTIE", "FIXME: Path roundesss should be applied properly!");
|
||||||
|
P(shape)->rs.stroke->join = StrokeJoin::Round;
|
||||||
|
}
|
||||||
|
|
||||||
|
shapes.push(shape);
|
||||||
}
|
}
|
||||||
|
|
||||||
return nullptr;
|
//push repeat shapes in order.
|
||||||
|
if (repeater.inorder) {
|
||||||
|
for (auto shape = shapes.data; shape < shapes.end(); ++shape) {
|
||||||
|
parent->scene->push(cast<Shape>(*shape));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (auto shape = shapes.end() - 1; shape >= shapes.data; --shape) {
|
||||||
|
parent->scene->push(cast<Shape>(*shape));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static Shape* _updateStroke(LottieSolidStroke* stroke, int32_t frameNo, Shape* baseShape)
|
static void _updateRect(LottieGroup* parent, LottieRect* rect, int32_t frameNo, Shape* propagtor, LottieModifier& modifier)
|
||||||
{
|
|
||||||
auto color = stroke->color(frameNo);
|
|
||||||
baseShape->stroke(color.rgb[0], color.rgb[1], color.rgb[2], stroke->opacity(frameNo));
|
|
||||||
|
|
||||||
return _updateStroke(static_cast<LottieStroke*>(stroke), frameNo, baseShape);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static Shape* _updateStroke(LottieGradientStroke* stroke, int32_t frameNo, Shape* baseShape)
|
|
||||||
{
|
|
||||||
baseShape->stroke(unique_ptr<Fill>(stroke->fill(frameNo)));
|
|
||||||
return _updateStroke(static_cast<LottieStroke*>(stroke), frameNo, baseShape);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static Shape* _updateFill(LottieGradientFill* fill, int32_t frameNo, Shape* baseShape)
|
|
||||||
{
|
|
||||||
//TODO: reuse the fill instance?
|
|
||||||
baseShape->fill(unique_ptr<Fill>(fill->fill(frameNo)));
|
|
||||||
baseShape->fill(fill->rule);
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static Shape* _updateRect(LottieGroup* parent, LottieRect* rect, int32_t frameNo, Shape* baseShape, Shape* mergingShape, float roundedCorner)
|
|
||||||
{
|
{
|
||||||
auto position = rect->position(frameNo);
|
auto position = rect->position(frameNo);
|
||||||
auto size = rect->size(frameNo);
|
auto size = rect->size(frameNo);
|
||||||
auto round = rect->radius(frameNo);
|
auto roundness = rect->radius(frameNo);
|
||||||
if (roundedCorner > round) round = roundedCorner;
|
if (modifier.roundness > roundness) roundness = modifier.roundness;
|
||||||
|
|
||||||
if (round > 0.0f) {
|
if (roundness > 0.0f) {
|
||||||
if (round > size.x * 0.5f) round = size.x * 0.5f;
|
if (roundness > size.x * 0.5f) roundness = size.x * 0.5f;
|
||||||
if (round > size.y * 0.5f) round = size.y * 0.5f;
|
if (roundness > size.y * 0.5f) roundness = size.y * 0.5f;
|
||||||
}
|
}
|
||||||
if (!mergingShape) {
|
|
||||||
auto newShape = cast<Shape>(baseShape->duplicate());
|
if (modifier.repeater.valid) {
|
||||||
mergingShape = newShape.get();
|
auto path = Shape::gen();
|
||||||
parent->scene->push(std::move(newShape));
|
path->appendRect(position.x - size.x * 0.5f, position.y - size.y * 0.5f, size.x, size.y, roundness, roundness);
|
||||||
|
_repeat(parent, frameNo, propagtor, std::move(path), modifier);
|
||||||
|
} else {
|
||||||
|
auto merging = _draw(parent, frameNo, propagtor, modifier);
|
||||||
|
merging->appendRect(position.x - size.x * 0.5f, position.y - size.y * 0.5f, size.x, size.y, roundness, roundness);
|
||||||
}
|
}
|
||||||
mergingShape->appendRect(position.x - size.x * 0.5f, position.y - size.y * 0.5f, size.x, size.y, round, round);
|
|
||||||
return mergingShape;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static Shape* _updateEllipse(LottieGroup* parent, LottieEllipse* ellipse, int32_t frameNo, Shape* baseShape, Shape* mergingShape)
|
static void _updateEllipse(LottieGroup* parent, LottieEllipse* ellipse, int32_t frameNo, Shape* propagtor, LottieModifier& modifier)
|
||||||
{
|
{
|
||||||
auto position = ellipse->position(frameNo);
|
auto position = ellipse->position(frameNo);
|
||||||
auto size = ellipse->size(frameNo);
|
auto size = ellipse->size(frameNo);
|
||||||
if (!mergingShape) {
|
|
||||||
auto newShape = cast<Shape>(baseShape->duplicate());
|
if (modifier.repeater.valid) {
|
||||||
mergingShape = newShape.get();
|
auto path = Shape::gen();
|
||||||
parent->scene->push(std::move(newShape));
|
path->appendCircle(position.x, position.y, size.x * 0.5f, size.y * 0.5f);
|
||||||
|
_repeat(parent, frameNo, propagtor, std::move(path), modifier);
|
||||||
|
} else {
|
||||||
|
auto merging = _draw(parent, frameNo, propagtor, modifier);
|
||||||
|
merging->appendCircle(position.x, position.y, size.x * 0.5f, size.y * 0.5f);
|
||||||
}
|
}
|
||||||
mergingShape->appendCircle(position.x, position.y, size.x * 0.5f, size.y * 0.5f);
|
|
||||||
return mergingShape;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static Shape* _updatePath(LottieGroup* parent, LottiePath* path, int32_t frameNo, Shape* baseShape, Shape* mergingShape, float roundedCorner)
|
static void _updatePath(LottieGroup* parent, LottiePath* path, int32_t frameNo, Shape* propagtor, LottieModifier& modifier)
|
||||||
{
|
{
|
||||||
if (!mergingShape) {
|
if (modifier.repeater.valid) {
|
||||||
auto newShape = cast<Shape>(baseShape->duplicate());
|
auto p = Shape::gen();
|
||||||
mergingShape = newShape.get();
|
path->pathset(frameNo, P(p)->rs.path.cmds, P(p)->rs.path.pts);
|
||||||
parent->scene->push(std::move(newShape));
|
_repeat(parent, frameNo, propagtor, std::move(p), modifier);
|
||||||
}
|
} else {
|
||||||
if (path->pathset(frameNo, P(mergingShape)->rs.path.cmds, P(mergingShape)->rs.path.pts)) {
|
auto merging = _draw(parent, frameNo, propagtor, modifier);
|
||||||
P(mergingShape)->update(RenderUpdateFlag::Path);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (roundedCorner > 1.0f && P(mergingShape)->rs.stroke) {
|
if (path->pathset(frameNo, P(merging)->rs.path.cmds, P(merging)->rs.path.pts)) {
|
||||||
TVGERR("LOTTIE", "FIXME: Path roundesss should be applied properly!");
|
P(merging)->update(RenderUpdateFlag::Path);
|
||||||
P(mergingShape)->rs.stroke->join = StrokeJoin::Round;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return mergingShape;
|
if (modifier.roundness > 1.0f && P(merging)->rs.stroke) {
|
||||||
|
TVGERR("LOTTIE", "FIXME: Path roundesss should be applied properly!");
|
||||||
|
P(merging)->rs.stroke->join = StrokeJoin::Round;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void _updateStar(LottieGroup* parent, LottiePolyStar* star, Matrix* transform, int32_t frameNo, Shape* mergingShape)
|
static void _updateStar(LottieGroup* parent, LottiePolyStar* star, Matrix* transform, int32_t frameNo, Shape* merging)
|
||||||
{
|
{
|
||||||
static constexpr auto POLYSTAR_MAGIC_NUMBER = 0.47829f / 0.28f;
|
static constexpr auto POLYSTAR_MAGIC_NUMBER = 0.47829f / 0.28f;
|
||||||
|
|
||||||
|
@ -279,17 +377,17 @@ static void _updateStar(LottieGroup* parent, LottiePolyStar* star, Matrix* trans
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mathZero(innerRoundness) && mathZero(outerRoundness)) {
|
if (mathZero(innerRoundness) && mathZero(outerRoundness)) {
|
||||||
P(mergingShape)->rs.path.pts.reserve(numPoints + 2);
|
P(merging)->rs.path.pts.reserve(numPoints + 2);
|
||||||
P(mergingShape)->rs.path.cmds.reserve(numPoints + 3);
|
P(merging)->rs.path.cmds.reserve(numPoints + 3);
|
||||||
} else {
|
} else {
|
||||||
P(mergingShape)->rs.path.pts.reserve(numPoints * 3 + 2);
|
P(merging)->rs.path.pts.reserve(numPoints * 3 + 2);
|
||||||
P(mergingShape)->rs.path.cmds.reserve(numPoints + 3);
|
P(merging)->rs.path.cmds.reserve(numPoints + 3);
|
||||||
hasRoundness = true;
|
hasRoundness = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Point in = {x, y};
|
Point in = {x, y};
|
||||||
if (transform) mathTransform(transform, &in);
|
if (transform) mathTransform(transform, &in);
|
||||||
mergingShape->moveTo(in.x, in.y);
|
merging->moveTo(in.x, in.y);
|
||||||
|
|
||||||
for (size_t i = 0; i < numPoints; i++) {
|
for (size_t i = 0; i < numPoints; i++) {
|
||||||
auto radius = longSegment ? outerRadius : innerRadius;
|
auto radius = longSegment ? outerRadius : innerRadius;
|
||||||
|
@ -337,20 +435,20 @@ static void _updateStar(LottieGroup* parent, LottiePolyStar* star, Matrix* trans
|
||||||
mathTransform(transform, &in3);
|
mathTransform(transform, &in3);
|
||||||
mathTransform(transform, &in4);
|
mathTransform(transform, &in4);
|
||||||
}
|
}
|
||||||
mergingShape->cubicTo(in2.x, in2.y, in3.x, in3.y, in4.x, in4.y);
|
merging->cubicTo(in2.x, in2.y, in3.x, in3.y, in4.x, in4.y);
|
||||||
} else {
|
} else {
|
||||||
Point in = {x, y};
|
Point in = {x, y};
|
||||||
if (transform) mathTransform(transform, &in);
|
if (transform) mathTransform(transform, &in);
|
||||||
mergingShape->lineTo(in.x, in.y);
|
merging->lineTo(in.x, in.y);
|
||||||
}
|
}
|
||||||
angle += dTheta * direction;
|
angle += dTheta * direction;
|
||||||
longSegment = !longSegment;
|
longSegment = !longSegment;
|
||||||
}
|
}
|
||||||
mergingShape->close();
|
merging->close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void _updatePolygon(LottieGroup* parent, LottiePolyStar* star, Matrix* transform, int32_t frameNo, Shape* mergingShape)
|
static void _updatePolygon(LottieGroup* parent, LottiePolyStar* star, Matrix* transform, int32_t frameNo, Shape* merging)
|
||||||
{
|
{
|
||||||
static constexpr auto POLYGON_MAGIC_NUMBER = 0.25f;
|
static constexpr auto POLYGON_MAGIC_NUMBER = 0.25f;
|
||||||
|
|
||||||
|
@ -368,17 +466,17 @@ static void _updatePolygon(LottieGroup* parent, LottiePolyStar* star, Matrix* tr
|
||||||
angle += anglePerPoint * direction;
|
angle += anglePerPoint * direction;
|
||||||
|
|
||||||
if (mathZero(roundness)) {
|
if (mathZero(roundness)) {
|
||||||
P(mergingShape)->rs.path.pts.reserve(ptsCnt + 2);
|
P(merging)->rs.path.pts.reserve(ptsCnt + 2);
|
||||||
P(mergingShape)->rs.path.cmds.reserve(ptsCnt + 3);
|
P(merging)->rs.path.cmds.reserve(ptsCnt + 3);
|
||||||
} else {
|
} else {
|
||||||
P(mergingShape)->rs.path.pts.reserve(ptsCnt * 3 + 2);
|
P(merging)->rs.path.pts.reserve(ptsCnt * 3 + 2);
|
||||||
P(mergingShape)->rs.path.cmds.reserve(ptsCnt + 3);
|
P(merging)->rs.path.cmds.reserve(ptsCnt + 3);
|
||||||
hasRoundness = true;
|
hasRoundness = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Point in = {x, y};
|
Point in = {x, y};
|
||||||
if (transform) mathTransform(transform, &in);
|
if (transform) mathTransform(transform, &in);
|
||||||
mergingShape->moveTo(in.x, in.y);
|
merging->moveTo(in.x, in.y);
|
||||||
|
|
||||||
for (size_t i = 0; i < ptsCnt; i++) {
|
for (size_t i = 0; i < ptsCnt; i++) {
|
||||||
auto previousX = x;
|
auto previousX = x;
|
||||||
|
@ -407,26 +505,20 @@ static void _updatePolygon(LottieGroup* parent, LottiePolyStar* star, Matrix* tr
|
||||||
mathTransform(transform, &in3);
|
mathTransform(transform, &in3);
|
||||||
mathTransform(transform, &in4);
|
mathTransform(transform, &in4);
|
||||||
}
|
}
|
||||||
mergingShape->cubicTo(in2.x, in2.y, in3.x, in3.y, in4.x, in4.y);
|
merging->cubicTo(in2.x, in2.y, in3.x, in3.y, in4.x, in4.y);
|
||||||
} else {
|
} else {
|
||||||
Point in = {x, y};
|
Point in = {x, y};
|
||||||
if (transform) mathTransform(transform, &in);
|
if (transform) mathTransform(transform, &in);
|
||||||
mergingShape->lineTo(in.x, in.y);
|
merging->lineTo(in.x, in.y);
|
||||||
}
|
}
|
||||||
angle += anglePerPoint * direction;
|
angle += anglePerPoint * direction;
|
||||||
}
|
}
|
||||||
mergingShape->close();
|
merging->close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static Shape* _updatePolystar(LottieGroup* parent, LottiePolyStar* star, int32_t frameNo, Shape* baseShape, Shape* mergingShape)
|
static void _updatePolystar(LottieGroup* parent, LottiePolyStar* star, int32_t frameNo, Shape* propagtor, LottieModifier& modifier)
|
||||||
{
|
{
|
||||||
if (!mergingShape) {
|
|
||||||
auto newShape = cast<Shape>(baseShape->duplicate());
|
|
||||||
mergingShape = newShape.get();
|
|
||||||
parent->scene->push(std::move(newShape));
|
|
||||||
}
|
|
||||||
|
|
||||||
//Optimize: Can we skip the individual coords transform?
|
//Optimize: Can we skip the individual coords transform?
|
||||||
Matrix matrix;
|
Matrix matrix;
|
||||||
mathIdentity(&matrix);
|
mathIdentity(&matrix);
|
||||||
|
@ -436,16 +528,22 @@ static Shape* _updatePolystar(LottieGroup* parent, LottiePolyStar* star, int32_t
|
||||||
|
|
||||||
auto identity = mathIdentity((const Matrix*)&matrix);
|
auto identity = mathIdentity((const Matrix*)&matrix);
|
||||||
|
|
||||||
if (star->type == LottiePolyStar::Star) _updateStar(parent, star, identity ? nullptr : &matrix, frameNo, mergingShape);
|
|
||||||
else _updatePolygon(parent, star, identity ? nullptr : &matrix, frameNo, mergingShape);
|
|
||||||
|
|
||||||
P(mergingShape)->update(RenderUpdateFlag::Path);
|
if (modifier.repeater.valid) {
|
||||||
|
auto p = Shape::gen();
|
||||||
return mergingShape;
|
if (star->type == LottiePolyStar::Star) _updateStar(parent, star, identity ? nullptr : &matrix, frameNo, p.get());
|
||||||
|
else _updatePolygon(parent, star, identity ? nullptr : &matrix, frameNo, p.get());
|
||||||
|
_repeat(parent, frameNo, propagtor, std::move(p), modifier);
|
||||||
|
} else {
|
||||||
|
auto merging = _draw(parent, frameNo, propagtor, modifier);
|
||||||
|
if (star->type == LottiePolyStar::Star) _updateStar(parent, star, identity ? nullptr : &matrix, frameNo, merging);
|
||||||
|
else _updatePolygon(parent, star, identity ? nullptr : &matrix, frameNo, merging);
|
||||||
|
P(merging)->update(RenderUpdateFlag::Path);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void _updateImage(LottieGroup* parent, LottieImage* image, int32_t frameNo, Paint* baseShape)
|
static void _updateImage(LottieGroup* parent, LottieImage* image, int32_t frameNo, Paint* propagtor)
|
||||||
{
|
{
|
||||||
auto picture = image->picture;
|
auto picture = image->picture;
|
||||||
|
|
||||||
|
@ -458,11 +556,11 @@ static void _updateImage(LottieGroup* parent, LottieImage* image, int32_t frameN
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (baseShape) {
|
if (propagtor) {
|
||||||
if (auto matrix = P(baseShape)->transform()) {
|
if (auto matrix = P(propagtor)->transform()) {
|
||||||
picture->transform(*matrix);
|
picture->transform(*matrix);
|
||||||
}
|
}
|
||||||
picture->opacity(baseShape->opacity());
|
picture->opacity(propagtor->opacity());
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: remove duplicate.
|
//TODO: remove duplicate.
|
||||||
|
@ -472,106 +570,123 @@ static void _updateImage(LottieGroup* parent, LottieImage* image, int32_t frameN
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static float _updateRoundedCorner(LottieRoundedCorner* roundedCorner, int32_t frameNo, float baseValue)
|
static void _updateRoundedCorner(LottieRoundedCorner* roundedCorner, int32_t frameNo, LottieModifier& modifier)
|
||||||
{
|
{
|
||||||
auto round = roundedCorner->radius(frameNo);
|
auto roundness = roundedCorner->radius(frameNo);
|
||||||
if (baseValue > round) round = baseValue;
|
if (modifier.roundness < roundness) modifier.roundness = roundness;
|
||||||
return round;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static Shape* _updateTrimpath(LottieGroup* parent, LottieTrimpath* trimpath, int32_t frameNo, Shape* baseShape)
|
static void _updateRepeater(LottieRepeater* repeater, int32_t frameNo, LottieModifier& modifier)
|
||||||
|
{
|
||||||
|
modifier.repeater.cnt = repeater->copies(frameNo);
|
||||||
|
modifier.repeater.offset = repeater->offset(frameNo);
|
||||||
|
modifier.repeater.position = repeater->position(frameNo);
|
||||||
|
modifier.repeater.anchor = repeater->anchor(frameNo);
|
||||||
|
modifier.repeater.scale = repeater->scale(frameNo);
|
||||||
|
modifier.repeater.rotation = repeater->rotation(frameNo);
|
||||||
|
modifier.repeater.startOpacity = repeater->startOpacity(frameNo);
|
||||||
|
modifier.repeater.endOpacity = repeater->endOpacity(frameNo);
|
||||||
|
modifier.repeater.inorder = repeater->inorder;
|
||||||
|
modifier.repeater.interpOpacity = (modifier.repeater.startOpacity == modifier.repeater.endOpacity) ? false : true;
|
||||||
|
modifier.repeater.valid = true;
|
||||||
|
|
||||||
|
modifier.merging = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void _updateTrimpath(LottieTrimpath* trimpath, int32_t frameNo, Shape* propagtor)
|
||||||
{
|
{
|
||||||
float begin, end;
|
float begin, end;
|
||||||
trimpath->segment(frameNo, begin, end);
|
trimpath->segment(frameNo, begin, end);
|
||||||
|
|
||||||
if (trimpath->type == LottieTrimpath::Simultaneous) {
|
if (P(propagtor)->rs.stroke) {
|
||||||
if (P(baseShape)->rs.stroke) {
|
auto pbegin = P(propagtor)->rs.stroke->trim.begin;
|
||||||
auto pbegin = P(baseShape)->rs.stroke->trim.begin;
|
auto pend = P(propagtor)->rs.stroke->trim.end;
|
||||||
auto pend = P(baseShape)->rs.stroke->trim.end;
|
auto length = fabsf(pend - pbegin);
|
||||||
auto length = fabsf(pend - pbegin);
|
begin = (length * begin) + pbegin;
|
||||||
begin = (length * begin) + pbegin;
|
end = (length * end) + pbegin;
|
||||||
end = (length * end) + pbegin;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
P(baseShape)->strokeTrim(begin, end);
|
if (trimpath->type == LottieTrimpath::Individual) {
|
||||||
|
TVGERR("LOTTIE", "TODO: Individual Trimpath");
|
||||||
|
}
|
||||||
|
|
||||||
return nullptr;
|
P(propagtor)->strokeTrim(begin, end);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void _updateChildren(LottieGroup* parent, int32_t frameNo, Shape* baseShape, float roundedCorner)
|
static void _updateChildren(LottieGroup* parent, int32_t frameNo, Shape* propagtor, LottieModifier& modifier)
|
||||||
{
|
{
|
||||||
if (parent->children.empty()) return;
|
if (parent->children.empty()) return;
|
||||||
|
|
||||||
//inherits the parent's shape properties.
|
//inherits the parent's shape properties.
|
||||||
baseShape = baseShape ? static_cast<Shape*>(baseShape->duplicate()) : Shape::gen().release();
|
propagtor = propagtor ? static_cast<Shape*>(propagtor->duplicate()) : Shape::gen().release();
|
||||||
|
|
||||||
//merge shapes if they are continuously appended?
|
|
||||||
Shape* mergingShape = nullptr;
|
|
||||||
|
|
||||||
//Draw the parent shapes first
|
//Draw the parent shapes first
|
||||||
for (auto child = parent->children.end() - 1; child >= parent->children.data; --child) {
|
for (auto child = parent->children.end() - 1; child >= parent->children.data; --child) {
|
||||||
|
//TODO: Polymorphsim?
|
||||||
switch ((*child)->type) {
|
switch ((*child)->type) {
|
||||||
case LottieObject::Group: {
|
case LottieObject::Group: {
|
||||||
mergingShape = _updateGroup(parent, static_cast<LottieGroup*>(*child), frameNo, baseShape, roundedCorner);
|
_updateGroup(parent, static_cast<LottieGroup*>(*child), frameNo, propagtor, modifier);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case LottieObject::Transform: {
|
case LottieObject::Transform: {
|
||||||
mergingShape = _updateTransform(baseShape, static_cast<LottieTransform*>(*child), frameNo);
|
_updateTransform(static_cast<LottieTransform*>(*child), frameNo, propagtor, modifier);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case LottieObject::SolidFill: {
|
case LottieObject::SolidFill: {
|
||||||
mergingShape = _updateFill(static_cast<LottieSolidFill*>(*child), frameNo, baseShape);
|
_updateFill(static_cast<LottieSolidFill*>(*child), frameNo, propagtor, modifier);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case LottieObject::SolidStroke: {
|
case LottieObject::SolidStroke: {
|
||||||
mergingShape = _updateStroke(static_cast<LottieSolidStroke*>(*child), frameNo, baseShape);
|
_updateStroke(static_cast<LottieSolidStroke*>(*child), frameNo, propagtor, modifier);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case LottieObject::GradientFill: {
|
case LottieObject::GradientFill: {
|
||||||
mergingShape = _updateFill(static_cast<LottieGradientFill*>(*child), frameNo, baseShape);
|
_updateFill(static_cast<LottieGradientFill*>(*child), frameNo, propagtor, modifier);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case LottieObject::GradientStroke: {
|
case LottieObject::GradientStroke: {
|
||||||
mergingShape = _updateStroke(static_cast<LottieGradientStroke*>(*child), frameNo, baseShape);
|
_updateStroke(static_cast<LottieGradientStroke*>(*child), frameNo, propagtor, modifier);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case LottieObject::Rect: {
|
case LottieObject::Rect: {
|
||||||
mergingShape = _updateRect(parent, static_cast<LottieRect*>(*child), frameNo, baseShape, mergingShape, roundedCorner);
|
_updateRect(parent, static_cast<LottieRect*>(*child), frameNo, propagtor, modifier);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case LottieObject::Ellipse: {
|
case LottieObject::Ellipse: {
|
||||||
mergingShape = _updateEllipse(parent, static_cast<LottieEllipse*>(*child), frameNo, baseShape, mergingShape);
|
_updateEllipse(parent, static_cast<LottieEllipse*>(*child), frameNo, propagtor, modifier);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case LottieObject::Path: {
|
case LottieObject::Path: {
|
||||||
mergingShape = _updatePath(parent, static_cast<LottiePath*>(*child), frameNo, baseShape, mergingShape, roundedCorner);
|
_updatePath(parent, static_cast<LottiePath*>(*child), frameNo, propagtor, modifier);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case LottieObject::Polystar: {
|
case LottieObject::Polystar: {
|
||||||
mergingShape = _updatePolystar(parent, static_cast<LottiePolyStar*>(*child), frameNo, baseShape, mergingShape);
|
_updatePolystar(parent, static_cast<LottiePolyStar*>(*child), frameNo, propagtor, modifier);
|
||||||
break;
|
|
||||||
}
|
|
||||||
case LottieObject::RoundedCorner: {
|
|
||||||
roundedCorner = _updateRoundedCorner(static_cast<LottieRoundedCorner*>(*child), frameNo, roundedCorner);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case LottieObject::Trimpath: {
|
|
||||||
mergingShape = _updateTrimpath(parent, static_cast<LottieTrimpath*>(*child), frameNo, baseShape);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case LottieObject::Image: {
|
case LottieObject::Image: {
|
||||||
_updateImage(parent, static_cast<LottieImage*>(*child), frameNo, baseShape);
|
_updateImage(parent, static_cast<LottieImage*>(*child), frameNo, propagtor);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default: {
|
case LottieObject::Trimpath: {
|
||||||
|
_updateTrimpath(static_cast<LottieTrimpath*>(*child), frameNo, propagtor);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case LottieObject::Repeater: {
|
||||||
|
_updateRepeater(static_cast<LottieRepeater*>(*child), frameNo, modifier);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LottieObject::RoundedCorner: {
|
||||||
|
_updateRoundedCorner(static_cast<LottieRoundedCorner*>(*child), frameNo, modifier);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
delete(baseShape);
|
delete (propagtor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -599,9 +714,8 @@ static void _updateMaskings(LottieLayer* layer, int32_t frameNo)
|
||||||
{
|
{
|
||||||
if (layer->masks.count == 0) return;
|
if (layer->masks.count == 0) return;
|
||||||
|
|
||||||
//maskings+clipping
|
//maskings
|
||||||
Shape* mergingMask = nullptr;
|
Shape* pmask = nullptr;
|
||||||
|
|
||||||
auto pmethod = CompositeMethod::AlphaMask;
|
auto pmethod = CompositeMethod::AlphaMask;
|
||||||
|
|
||||||
for (auto m = layer->masks.data; m < layer->masks.end(); ++m) {
|
for (auto m = layer->masks.data; m < layer->masks.end(); ++m) {
|
||||||
|
@ -613,14 +727,14 @@ static void _updateMaskings(LottieLayer* layer, int32_t frameNo)
|
||||||
P(shape)->update(RenderUpdateFlag::Path);
|
P(shape)->update(RenderUpdateFlag::Path);
|
||||||
}
|
}
|
||||||
auto method = mask->method;
|
auto method = mask->method;
|
||||||
if (mergingMask) {
|
if (pmask) {
|
||||||
//false of false is true. invert.
|
//false of false is true. invert.
|
||||||
if (method == CompositeMethod::SubtractMask && pmethod == method) {
|
if (method == CompositeMethod::SubtractMask && pmethod == method) {
|
||||||
method = CompositeMethod::AddMask;
|
method = CompositeMethod::AddMask;
|
||||||
} else if (pmethod == CompositeMethod::DifferenceMask && pmethod == method) {
|
} else if (pmethod == CompositeMethod::DifferenceMask && pmethod == method) {
|
||||||
method = CompositeMethod::IntersectMask;
|
method = CompositeMethod::IntersectMask;
|
||||||
}
|
}
|
||||||
mergingMask->composite(cast<Shape>(shape), method);
|
pmask->composite(cast<Shape>(shape), method);
|
||||||
} else {
|
} else {
|
||||||
if (method == CompositeMethod::SubtractMask) method = CompositeMethod::InvAlphaMask;
|
if (method == CompositeMethod::SubtractMask) method = CompositeMethod::InvAlphaMask;
|
||||||
else if (method == CompositeMethod::AddMask) method = CompositeMethod::AlphaMask;
|
else if (method == CompositeMethod::AddMask) method = CompositeMethod::AlphaMask;
|
||||||
|
@ -629,7 +743,7 @@ static void _updateMaskings(LottieLayer* layer, int32_t frameNo)
|
||||||
layer->scene->composite(cast<Shape>(shape), method);
|
layer->scene->composite(cast<Shape>(shape), method);
|
||||||
}
|
}
|
||||||
pmethod = mask->method;
|
pmethod = mask->method;
|
||||||
mergingMask = shape;
|
pmask = shape;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -664,7 +778,8 @@ static void _updateLayer(LottieLayer* root, LottieLayer* layer, int32_t frameNo)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
_updateChildren(layer, rFrameNo, nullptr, 0);
|
LottieModifier modifier;
|
||||||
|
_updateChildren(layer, rFrameNo, nullptr, modifier);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -130,10 +130,10 @@ struct LottieObject
|
||||||
Ellipse,
|
Ellipse,
|
||||||
Path,
|
Path,
|
||||||
Polystar,
|
Polystar,
|
||||||
|
Image,
|
||||||
Trimpath,
|
Trimpath,
|
||||||
Repeater,
|
Repeater,
|
||||||
RoundedCorner,
|
RoundedCorner,
|
||||||
Image
|
|
||||||
};
|
};
|
||||||
|
|
||||||
virtual ~LottieObject()
|
virtual ~LottieObject()
|
||||||
|
@ -350,6 +350,28 @@ struct LottieImage : LottieObject
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct LottieRepeater : LottieObject
|
||||||
|
{
|
||||||
|
void prepare()
|
||||||
|
{
|
||||||
|
LottieObject::type = LottieObject::Repeater;
|
||||||
|
if (copies.frames || offset.frames || position.frames || rotation.frames || scale.frames || anchor.frames || startOpacity.frames || endOpacity.frames) statical = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
LottieFloat copies = 0.0f;
|
||||||
|
LottieFloat offset = 0.0f;
|
||||||
|
|
||||||
|
//Transform
|
||||||
|
LottiePosition position = Point{0.0f, 0.0f};
|
||||||
|
LottieFloat rotation = 0.0f;
|
||||||
|
LottiePoint scale = Point{100.0f, 100.0f};
|
||||||
|
LottiePoint anchor = Point{0.0f, 0.0f};
|
||||||
|
LottieOpacity startOpacity = 255;
|
||||||
|
LottieOpacity endOpacity = 255;
|
||||||
|
bool inorder = true; //true: higher, false: lower
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
struct LottieGroup : LottieObject
|
struct LottieGroup : LottieObject
|
||||||
{
|
{
|
||||||
virtual ~LottieGroup()
|
virtual ~LottieGroup()
|
||||||
|
|
|
@ -755,13 +755,44 @@ LottieTrimpath* LottieParser::parseTrimpath()
|
||||||
else if (!strcmp(key, "hd")) trim->hidden = getBool();
|
else if (!strcmp(key, "hd")) trim->hidden = getBool();
|
||||||
else skip(key);
|
else skip(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
trim->prepare();
|
trim->prepare();
|
||||||
|
|
||||||
return trim;
|
return trim;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
LottieRepeater* LottieParser::parseRepeater()
|
||||||
|
{
|
||||||
|
auto repeater = new LottieRepeater;
|
||||||
|
if (!repeater) return nullptr;
|
||||||
|
|
||||||
|
while (auto key = nextObjectKey()) {
|
||||||
|
if (!strcmp(key, "nm")) repeater->name = getStringCopy();
|
||||||
|
else if (!strcmp(key, "c")) parseProperty(repeater->copies);
|
||||||
|
else if (!strcmp(key, "o")) parseProperty(repeater->offset);
|
||||||
|
else if (!strcmp(key, "m")) repeater->inorder = getInt();
|
||||||
|
else if (!strcmp(key, "tr"))
|
||||||
|
{
|
||||||
|
enterObject();
|
||||||
|
while (auto key2 = nextObjectKey()) {
|
||||||
|
if (!strcmp(key2, "a")) parseProperty(repeater->anchor);
|
||||||
|
else if (!strcmp(key2, "p")) parseProperty(repeater->position);
|
||||||
|
else if (!strcmp(key2, "r")) parseProperty(repeater->rotation);
|
||||||
|
else if (!strcmp(key2, "s")) parseProperty(repeater->scale);
|
||||||
|
else if (!strcmp(key2, "so")) parseProperty(repeater->startOpacity);
|
||||||
|
else if (!strcmp(key2, "eo")) parseProperty(repeater->endOpacity);
|
||||||
|
else skip(key2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (!strcmp(key, "hd")) repeater->hidden = getBool();
|
||||||
|
else skip(key);
|
||||||
|
}
|
||||||
|
repeater->prepare();
|
||||||
|
|
||||||
|
return repeater;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
LottieObject* LottieParser::parseObject()
|
LottieObject* LottieParser::parseObject()
|
||||||
{
|
{
|
||||||
auto type = getString();
|
auto type = getString();
|
||||||
|
@ -779,9 +810,12 @@ LottieObject* LottieParser::parseObject()
|
||||||
else if (!strcmp(type, "gf")) return parseGradientFill();
|
else if (!strcmp(type, "gf")) return parseGradientFill();
|
||||||
else if (!strcmp(type, "gs")) return parseGradientStroke();
|
else if (!strcmp(type, "gs")) return parseGradientStroke();
|
||||||
else if (!strcmp(type, "tm")) return parseTrimpath();
|
else if (!strcmp(type, "tm")) return parseTrimpath();
|
||||||
else if (!strcmp(type, "rp")) TVGERR("LOTTIE", "Repeater(rp) is not supported yet");
|
else if (!strcmp(type, "rp")) return parseRepeater();
|
||||||
else if (!strcmp(type, "mm")) TVGERR("LOTTIE", "MergePath(mm) is not supported yet");
|
else if (!strcmp(type, "mm")) TVGERR("LOTTIE", "MergePath(mm) is not supported yet");
|
||||||
else TVGERR("LOTTIE", "Unkown object type(%s) is given", type);
|
else if (!strcmp(type, "pb")) TVGERR("LOTTIE", "Puker/Bloat(pb) is not supported yet");
|
||||||
|
else if (!strcmp(type, "tw")) TVGERR("LOTTIE", "Twist(tw) is not supported yet");
|
||||||
|
else if (!strcmp(type, "op")) TVGERR("LOTTIE", "Offset Path(op) is not supported yet");
|
||||||
|
else if (!strcmp(type, "zz")) TVGERR("LOTTIE", "Zig Zag(zz) is not supported yet");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1002,6 +1036,11 @@ LottieLayer* LottieParser::parseLayer()
|
||||||
else if (!strcmp(key, "hd")) layer->hidden = getBool();
|
else if (!strcmp(key, "hd")) layer->hidden = getBool();
|
||||||
else if (!strcmp(key, "refId")) layer->refId = getStringCopy();
|
else if (!strcmp(key, "refId")) layer->refId = getStringCopy();
|
||||||
else if (!strcmp(key, "td")) layer->matteSrc = getInt(); //used for matte layer
|
else if (!strcmp(key, "td")) layer->matteSrc = getInt(); //used for matte layer
|
||||||
|
else if (!strcmp(key, "ef"))
|
||||||
|
{
|
||||||
|
TVGERR("LOTTIE", "layer effect(ef) is not supported!");
|
||||||
|
skip(key);
|
||||||
|
}
|
||||||
else skip(key);
|
else skip(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -84,6 +84,7 @@ private:
|
||||||
LottieLayer* parseLayers();
|
LottieLayer* parseLayers();
|
||||||
LottieMask* parseMask();
|
LottieMask* parseMask();
|
||||||
LottieTrimpath* parseTrimpath();
|
LottieTrimpath* parseTrimpath();
|
||||||
|
LottieRepeater* parseRepeater();
|
||||||
|
|
||||||
void parseObject(LottieGroup* parent);
|
void parseObject(LottieGroup* parent);
|
||||||
void parseShapes(LottieLayer* layer);
|
void parseShapes(LottieLayer* layer);
|
||||||
|
|
Loading…
Add table
Reference in a new issue