loader/lottie: support the repeater property

This commit is contained in:
Hermet Park 2023-09-05 15:00:18 +09:00 committed by Hermet Park
parent 2fab9fb7d9
commit a49ac81543
11 changed files with 353 additions and 169 deletions

File diff suppressed because one or more lines are too long

View 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":[]}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -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;
} }
} }

View file

@ -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()

View file

@ -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);
} }

View file

@ -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);