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 */
/************************************************************************/
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 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;
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();
paint->transform(pmatrix ? mathMultiply(pmatrix, &matrix) : matrix);
paint->opacity(opacity);
return nullptr;
auto pmatrix = P(propagtor)->transform();
propagtor->transform(pmatrix ? mathMultiply(pmatrix, &matrix) : matrix);
propagtor->opacity(opacity);
}
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
group->scene = parent->scene;
auto opacity = group->opacity(frameNo);
if (opacity == 0) return nullptr;
if (opacity == 0) return;
if (group->transform) {
TVGERR("LOTTIE", "group transform is not working!");
@ -127,121 +155,191 @@ static Shape* _updateGroup(LottieGroup* parent, LottieGroup* group, int32_t fram
#endif
}
_updateChildren(group, frameNo, baseShape, roundedCorner);
return nullptr;
auto modifier2 = modifier;
_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);
baseShape->fill(color.rgb[0], color.rgb[1], color.rgb[2], fill->opacity(frameNo));
baseShape->fill(fill->rule);
return nullptr;
propagtor->fill(color.rgb[0], color.rgb[1], color.rgb[2], fill->opacity(frameNo));
propagtor->fill(fill->rule);
}
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));
baseShape->stroke(stroke->cap);
baseShape->stroke(stroke->join);
baseShape->strokeMiterlimit(stroke->miterLimit);
propagtor->stroke(stroke->width(frameNo));
propagtor->stroke(stroke->cap);
propagtor->stroke(stroke->join);
propagtor->strokeMiterlimit(stroke->miterLimit);
if (stroke->dashattr) {
float dashes[2];
dashes[0] = stroke->dashSize(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 {
baseShape->stroke(nullptr, 0);
propagtor->stroke(nullptr, 0);
}
return nullptr;
}
static Shape* _updateStroke(LottieSolidStroke* stroke, int32_t frameNo, Shape* baseShape)
static void _updateStroke(LottieSolidStroke* stroke, int32_t frameNo, Shape* propagtor, LottieModifier& modifier)
{
modifier.merging = nullptr;
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);
propagtor->stroke(color.rgb[0], color.rgb[1], color.rgb[2], stroke->opacity(frameNo));
_updateStroke(static_cast<LottieStroke*>(stroke), frameNo, propagtor);
}
static Shape* _updateStroke(LottieGradientStroke* stroke, int32_t frameNo, Shape* baseShape)
static void _updateStroke(LottieGradientStroke* stroke, int32_t frameNo, Shape* propagtor, LottieModifier& modifier)
{
baseShape->stroke(unique_ptr<Fill>(stroke->fill(frameNo)));
return _updateStroke(static_cast<LottieStroke*>(stroke), frameNo, baseShape);
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* baseShape)
static Shape* _updateFill(LottieGradientFill* fill, int32_t frameNo, Shape* propagtor, LottieModifier& modifier)
{
modifier.merging = nullptr;
//TODO: reuse the fill instance?
baseShape->fill(unique_ptr<Fill>(fill->fill(frameNo)));
baseShape->fill(fill->rule);
propagtor->fill(unique_ptr<Fill>(fill->fill(frameNo)));
propagtor->fill(fill->rule);
return nullptr;
}
static Shape* _updateRect(LottieGroup* parent, LottieRect* rect, int32_t frameNo, Shape* baseShape, Shape* mergingShape, float roundedCorner)
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);
}
//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 void _updateRect(LottieGroup* parent, LottieRect* rect, int32_t frameNo, Shape* propagtor, LottieModifier& modifier)
{
auto position = rect->position(frameNo);
auto size = rect->size(frameNo);
auto round = rect->radius(frameNo);
if (roundedCorner > round) round = roundedCorner;
auto roundness = rect->radius(frameNo);
if (modifier.roundness > roundness) roundness = modifier.roundness;
if (round > 0.0f) {
if (round > size.x * 0.5f) round = size.x * 0.5f;
if (round > size.y * 0.5f) round = size.y * 0.5f;
if (roundness > 0.0f) {
if (roundness > size.x * 0.5f) roundness = size.x * 0.5f;
if (roundness > size.y * 0.5f) roundness = size.y * 0.5f;
}
if (!mergingShape) {
auto newShape = cast<Shape>(baseShape->duplicate());
mergingShape = newShape.get();
parent->scene->push(std::move(newShape));
if (modifier.repeater.valid) {
auto path = Shape::gen();
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 size = ellipse->size(frameNo);
if (!mergingShape) {
auto newShape = cast<Shape>(baseShape->duplicate());
mergingShape = newShape.get();
parent->scene->push(std::move(newShape));
if (modifier.repeater.valid) {
auto path = Shape::gen();
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) {
auto newShape = cast<Shape>(baseShape->duplicate());
mergingShape = newShape.get();
parent->scene->push(std::move(newShape));
}
if (path->pathset(frameNo, P(mergingShape)->rs.path.cmds, P(mergingShape)->rs.path.pts)) {
P(mergingShape)->update(RenderUpdateFlag::Path);
if (modifier.repeater.valid) {
auto p = Shape::gen();
path->pathset(frameNo, P(p)->rs.path.cmds, P(p)->rs.path.pts);
_repeat(parent, frameNo, propagtor, std::move(p), modifier);
} else {
auto merging = _draw(parent, frameNo, propagtor, modifier);
if (path->pathset(frameNo, P(merging)->rs.path.cmds, P(merging)->rs.path.pts)) {
P(merging)->update(RenderUpdateFlag::Path);
}
if (roundedCorner > 1.0f && P(mergingShape)->rs.stroke) {
if (modifier.roundness > 1.0f && P(merging)->rs.stroke) {
TVGERR("LOTTIE", "FIXME: Path roundesss should be applied properly!");
P(mergingShape)->rs.stroke->join = StrokeJoin::Round;
P(merging)->rs.stroke->join = StrokeJoin::Round;
}
}
return mergingShape;
}
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;
@ -279,17 +377,17 @@ static void _updateStar(LottieGroup* parent, LottiePolyStar* star, Matrix* trans
}
if (mathZero(innerRoundness) && mathZero(outerRoundness)) {
P(mergingShape)->rs.path.pts.reserve(numPoints + 2);
P(mergingShape)->rs.path.cmds.reserve(numPoints + 3);
P(merging)->rs.path.pts.reserve(numPoints + 2);
P(merging)->rs.path.cmds.reserve(numPoints + 3);
} else {
P(mergingShape)->rs.path.pts.reserve(numPoints * 3 + 2);
P(mergingShape)->rs.path.cmds.reserve(numPoints + 3);
P(merging)->rs.path.pts.reserve(numPoints * 3 + 2);
P(merging)->rs.path.cmds.reserve(numPoints + 3);
hasRoundness = true;
}
Point in = {x, y};
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++) {
auto radius = longSegment ? outerRadius : innerRadius;
@ -337,20 +435,20 @@ static void _updateStar(LottieGroup* parent, LottiePolyStar* star, Matrix* trans
mathTransform(transform, &in3);
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 {
Point in = {x, y};
if (transform) mathTransform(transform, &in);
mergingShape->lineTo(in.x, in.y);
merging->lineTo(in.x, in.y);
}
angle += dTheta * direction;
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;
@ -368,17 +466,17 @@ static void _updatePolygon(LottieGroup* parent, LottiePolyStar* star, Matrix* tr
angle += anglePerPoint * direction;
if (mathZero(roundness)) {
P(mergingShape)->rs.path.pts.reserve(ptsCnt + 2);
P(mergingShape)->rs.path.cmds.reserve(ptsCnt + 3);
P(merging)->rs.path.pts.reserve(ptsCnt + 2);
P(merging)->rs.path.cmds.reserve(ptsCnt + 3);
} else {
P(mergingShape)->rs.path.pts.reserve(ptsCnt * 3 + 2);
P(mergingShape)->rs.path.cmds.reserve(ptsCnt + 3);
P(merging)->rs.path.pts.reserve(ptsCnt * 3 + 2);
P(merging)->rs.path.cmds.reserve(ptsCnt + 3);
hasRoundness = true;
}
Point in = {x, y};
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++) {
auto previousX = x;
@ -407,26 +505,20 @@ static void _updatePolygon(LottieGroup* parent, LottiePolyStar* star, Matrix* tr
mathTransform(transform, &in3);
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 {
Point in = {x, y};
if (transform) mathTransform(transform, &in);
mergingShape->lineTo(in.x, in.y);
merging->lineTo(in.x, in.y);
}
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?
Matrix matrix;
mathIdentity(&matrix);
@ -436,16 +528,22 @@ static Shape* _updatePolystar(LottieGroup* parent, LottiePolyStar* star, int32_t
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);
return mergingShape;
if (modifier.repeater.valid) {
auto p = Shape::gen();
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;
@ -458,11 +556,11 @@ static void _updateImage(LottieGroup* parent, LottieImage* image, int32_t frameN
}
}
if (baseShape) {
if (auto matrix = P(baseShape)->transform()) {
if (propagtor) {
if (auto matrix = P(propagtor)->transform()) {
picture->transform(*matrix);
}
picture->opacity(baseShape->opacity());
picture->opacity(propagtor->opacity());
}
//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);
if (baseValue > round) round = baseValue;
return round;
auto roundness = roundedCorner->radius(frameNo);
if (modifier.roundness < roundness) modifier.roundness = roundness;
}
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;
trimpath->segment(frameNo, begin, end);
if (trimpath->type == LottieTrimpath::Simultaneous) {
if (P(baseShape)->rs.stroke) {
auto pbegin = P(baseShape)->rs.stroke->trim.begin;
auto pend = P(baseShape)->rs.stroke->trim.end;
if (P(propagtor)->rs.stroke) {
auto pbegin = P(propagtor)->rs.stroke->trim.begin;
auto pend = P(propagtor)->rs.stroke->trim.end;
auto length = fabsf(pend - pbegin);
begin = (length * begin) + pbegin;
end = (length * end) + pbegin;
}
if (trimpath->type == LottieTrimpath::Individual) {
TVGERR("LOTTIE", "TODO: Individual Trimpath");
}
P(baseShape)->strokeTrim(begin, end);
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;
//inherits the parent's shape properties.
baseShape = baseShape ? static_cast<Shape*>(baseShape->duplicate()) : Shape::gen().release();
//merge shapes if they are continuously appended?
Shape* mergingShape = nullptr;
propagtor = propagtor ? static_cast<Shape*>(propagtor->duplicate()) : Shape::gen().release();
//Draw the parent shapes first
for (auto child = parent->children.end() - 1; child >= parent->children.data; --child) {
//TODO: Polymorphsim?
switch ((*child)->type) {
case LottieObject::Group: {
mergingShape = _updateGroup(parent, static_cast<LottieGroup*>(*child), frameNo, baseShape, roundedCorner);
_updateGroup(parent, static_cast<LottieGroup*>(*child), frameNo, propagtor, modifier);
break;
}
case LottieObject::Transform: {
mergingShape = _updateTransform(baseShape, static_cast<LottieTransform*>(*child), frameNo);
_updateTransform(static_cast<LottieTransform*>(*child), frameNo, propagtor, modifier);
break;
}
case LottieObject::SolidFill: {
mergingShape = _updateFill(static_cast<LottieSolidFill*>(*child), frameNo, baseShape);
_updateFill(static_cast<LottieSolidFill*>(*child), frameNo, propagtor, modifier);
break;
}
case LottieObject::SolidStroke: {
mergingShape = _updateStroke(static_cast<LottieSolidStroke*>(*child), frameNo, baseShape);
_updateStroke(static_cast<LottieSolidStroke*>(*child), frameNo, propagtor, modifier);
break;
}
case LottieObject::GradientFill: {
mergingShape = _updateFill(static_cast<LottieGradientFill*>(*child), frameNo, baseShape);
_updateFill(static_cast<LottieGradientFill*>(*child), frameNo, propagtor, modifier);
break;
}
case LottieObject::GradientStroke: {
mergingShape = _updateStroke(static_cast<LottieGradientStroke*>(*child), frameNo, baseShape);
_updateStroke(static_cast<LottieGradientStroke*>(*child), frameNo, propagtor, modifier);
break;
}
case LottieObject::Rect: {
mergingShape = _updateRect(parent, static_cast<LottieRect*>(*child), frameNo, baseShape, mergingShape, roundedCorner);
_updateRect(parent, static_cast<LottieRect*>(*child), frameNo, propagtor, modifier);
break;
}
case LottieObject::Ellipse: {
mergingShape = _updateEllipse(parent, static_cast<LottieEllipse*>(*child), frameNo, baseShape, mergingShape);
_updateEllipse(parent, static_cast<LottieEllipse*>(*child), frameNo, propagtor, modifier);
break;
}
case LottieObject::Path: {
mergingShape = _updatePath(parent, static_cast<LottiePath*>(*child), frameNo, baseShape, mergingShape, roundedCorner);
_updatePath(parent, static_cast<LottiePath*>(*child), frameNo, propagtor, modifier);
break;
}
case LottieObject::Polystar: {
mergingShape = _updatePolystar(parent, static_cast<LottiePolyStar*>(*child), frameNo, baseShape, mergingShape);
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);
_updatePolystar(parent, static_cast<LottiePolyStar*>(*child), frameNo, propagtor, modifier);
break;
}
case LottieObject::Image: {
_updateImage(parent, static_cast<LottieImage*>(*child), frameNo, baseShape);
_updateImage(parent, static_cast<LottieImage*>(*child), frameNo, propagtor);
break;
}
default: {
case LottieObject::Trimpath: {
_updateTrimpath(static_cast<LottieTrimpath*>(*child), frameNo, propagtor);
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;
//maskings+clipping
Shape* mergingMask = nullptr;
//maskings
Shape* pmask = nullptr;
auto pmethod = CompositeMethod::AlphaMask;
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);
}
auto method = mask->method;
if (mergingMask) {
if (pmask) {
//false of false is true. invert.
if (method == CompositeMethod::SubtractMask && pmethod == method) {
method = CompositeMethod::AddMask;
} else if (pmethod == CompositeMethod::DifferenceMask && pmethod == method) {
method = CompositeMethod::IntersectMask;
}
mergingMask->composite(cast<Shape>(shape), method);
pmask->composite(cast<Shape>(shape), method);
} else {
if (method == CompositeMethod::SubtractMask) method = CompositeMethod::InvAlphaMask;
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);
}
pmethod = mask->method;
mergingMask = shape;
pmask = shape;
}
}
@ -664,7 +778,8 @@ static void _updateLayer(LottieLayer* root, LottieLayer* layer, int32_t frameNo)
break;
}
default: {
_updateChildren(layer, rFrameNo, nullptr, 0);
LottieModifier modifier;
_updateChildren(layer, rFrameNo, nullptr, modifier);
break;
}
}

View file

@ -130,10 +130,10 @@ struct LottieObject
Ellipse,
Path,
Polystar,
Image,
Trimpath,
Repeater,
RoundedCorner,
Image
};
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
{
virtual ~LottieGroup()

View file

@ -755,13 +755,44 @@ LottieTrimpath* LottieParser::parseTrimpath()
else if (!strcmp(key, "hd")) trim->hidden = getBool();
else skip(key);
}
trim->prepare();
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()
{
auto type = getString();
@ -779,9 +810,12 @@ LottieObject* LottieParser::parseObject()
else if (!strcmp(type, "gf")) return parseGradientFill();
else if (!strcmp(type, "gs")) return parseGradientStroke();
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 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;
}
@ -1002,6 +1036,11 @@ LottieLayer* LottieParser::parseLayer()
else if (!strcmp(key, "hd")) layer->hidden = getBool();
else if (!strcmp(key, "refId")) layer->refId = getStringCopy();
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);
}

View file

@ -84,6 +84,7 @@ private:
LottieLayer* parseLayers();
LottieMask* parseMask();
LottieTrimpath* parseTrimpath();
LottieRepeater* parseRepeater();
void parseObject(LottieGroup* parent);
void parseShapes(LottieLayer* layer);