loader/lottie: enhanced the coverage of the rounded corner feature.

Rounded corners should now be applied not only to rectangles
but also to path strokes. This enhancement changes the stroke join style
to round when a rounded corner is present.

Indeed, rounded corners ensure the accurate calculation of roundness based on the join style.
However, this patch has not yet been designed to handle it.
This commit is contained in:
Hermet Park 2023-08-31 10:42:11 +09:00 committed by Hermet Park
parent 1112444bd2
commit 7c669f622f
3 changed files with 42 additions and 51 deletions

View file

@ -31,7 +31,7 @@
/* Internal Class Implementation */
/************************************************************************/
static void _updateChildren(LottieGroup* parent, int32_t frameNo, Shape* baseShape);
static void _updateChildren(LottieGroup* parent, int32_t frameNo, Shape* baseShape, float roundedCorner);
static void _updateLayer(LottieLayer* root, LottieLayer* layer, int32_t frameNo);
static bool _buildPrecomp(LottieComposition* comp, LottieGroup* parent);
@ -108,7 +108,7 @@ static Shape* _updateTransform(Paint* paint, LottieTransform* transform, int32_t
}
static Shape* _updateGroup(LottieGroup* parent, LottieGroup* group, int32_t frameNo, Shape* baseShape)
static Shape* _updateGroup(LottieGroup* parent, LottieGroup* group, int32_t frameNo, Shape* baseShape, float roundedCorner)
{
//Prepare render data
group->scene = parent->scene;
@ -127,7 +127,7 @@ static Shape* _updateGroup(LottieGroup* parent, LottieGroup* group, int32_t fram
#endif
}
_updateChildren(group, frameNo, baseShape);
_updateChildren(group, frameNo, baseShape, roundedCorner);
return nullptr;
}
@ -185,21 +185,23 @@ static Shape* _updateStroke(LottieGradientStroke* stroke, int32_t frameNo, Shape
}
static Shape* _updateRect(LottieGroup* parent, LottieRect* rect, int32_t frameNo, Shape* baseShape, Shape* mergingShape)
static Shape* _updateRect(LottieGroup* parent, LottieRect* rect, int32_t frameNo, Shape* baseShape, Shape* mergingShape, float roundedCorner)
{
auto position = rect->position(frameNo);
auto size = rect->size(frameNo);
auto roundness = rect->roundness(frameNo);
if (roundness != 0) {
if (roundness > size.x * 0.5f) roundness = size.x * 0.5f;
if (roundness > size.y * 0.5f) roundness = size.y * 0.5f;
auto round = rect->radius(frameNo);
if (roundedCorner > round) round = roundedCorner;
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 (!mergingShape) {
auto newShape = cast<Shape>(baseShape->duplicate());
mergingShape = newShape.get();
parent->scene->push(std::move(newShape));
}
mergingShape->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;
}
@ -218,7 +220,7 @@ static Shape* _updateEllipse(LottieGroup* parent, LottieEllipse* ellipse, int32_
}
static Shape* _updatePath(LottieGroup* parent, LottiePath* path, int32_t frameNo, Shape* baseShape, Shape* mergingShape)
static Shape* _updatePath(LottieGroup* parent, LottiePath* path, int32_t frameNo, Shape* baseShape, Shape* mergingShape, float roundedCorner)
{
if (!mergingShape) {
auto newShape = cast<Shape>(baseShape->duplicate());
@ -228,6 +230,12 @@ static Shape* _updatePath(LottieGroup* parent, LottiePath* path, int32_t frameNo
if (path->pathset(frameNo, P(mergingShape)->rs.path.cmds, P(mergingShape)->rs.path.pts)) {
P(mergingShape)->update(RenderUpdateFlag::Path);
}
if (roundedCorner > 1.0f && P(mergingShape)->rs.stroke) {
TVGERR("LOTTIE", "FIXME: Path roundesss should be applied properly!");
P(mergingShape)->rs.stroke->join = StrokeJoin::Round;
}
return mergingShape;
}
@ -465,7 +473,15 @@ static void _updateImage(LottieGroup* parent, LottieImage* image, int32_t frameN
}
static void _updateChildren(LottieGroup* parent, int32_t frameNo, Shape* baseShape)
static float _updateRoundedCorner(LottieRoundedCorner* roundedCorner, int32_t frameNo, float baseValue)
{
auto round = roundedCorner->radius(frameNo);
if (baseValue > round) round = baseValue;
return round;
}
static void _updateChildren(LottieGroup* parent, int32_t frameNo, Shape* baseShape, float roundedCorner)
{
if (parent->children.empty()) return;
@ -479,7 +495,7 @@ static void _updateChildren(LottieGroup* parent, int32_t frameNo, Shape* baseSha
for (auto child = parent->children.end() - 1; child >= parent->children.data; --child) {
switch ((*child)->type) {
case LottieObject::Group: {
mergingShape = _updateGroup(parent, static_cast<LottieGroup*>(*child), frameNo, baseShape);
mergingShape = _updateGroup(parent, static_cast<LottieGroup*>(*child), frameNo, baseShape, roundedCorner);
break;
}
case LottieObject::Transform: {
@ -503,7 +519,7 @@ static void _updateChildren(LottieGroup* parent, int32_t frameNo, Shape* baseSha
break;
}
case LottieObject::Rect: {
mergingShape = _updateRect(parent, static_cast<LottieRect*>(*child), frameNo, baseShape, mergingShape);
mergingShape = _updateRect(parent, static_cast<LottieRect*>(*child), frameNo, baseShape, mergingShape, roundedCorner);
break;
}
case LottieObject::Ellipse: {
@ -511,13 +527,17 @@ static void _updateChildren(LottieGroup* parent, int32_t frameNo, Shape* baseSha
break;
}
case LottieObject::Path: {
mergingShape = _updatePath(parent, static_cast<LottiePath*>(*child), frameNo, baseShape, mergingShape);
mergingShape = _updatePath(parent, static_cast<LottiePath*>(*child), frameNo, baseShape, mergingShape, roundedCorner);
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::Image: {
_updateImage(parent, static_cast<LottieImage*>(*child), frameNo, baseShape);
break;
@ -615,7 +635,7 @@ static void _updateLayer(LottieLayer* root, LottieLayer* layer, int32_t frameNo)
break;
}
default: {
_updateChildren(layer, rFrameNo, nullptr);
_updateChildren(layer, rFrameNo, nullptr, 0);
break;
}
}

View file

@ -162,7 +162,6 @@ struct LottieRoundedCorner : LottieObject
LottieObject::type = LottieObject::RoundedCorner;
if (radius.frames) statical = false;
}
LottieFloat radius = 0.0f;
};
@ -184,18 +183,12 @@ struct LottieRect : LottieShape
void prepare()
{
LottieObject::type = LottieObject::Rect;
if (position.frames || size.frames || round.frames) statical = false;
if (position.frames || size.frames || radius.frames) statical = false;
}
float roundness(int32_t frameNo)
{
return roundedCorner ? roundedCorner->radius(frameNo) : round(frameNo);
}
LottieRoundedCorner* roundedCorner = nullptr;
LottiePosition position = Point{0.0f, 0.0f};
LottiePoint size = Point{0.0f, 0.0f};
LottieFloat round = 0.0f;
LottieFloat radius = 0.0f; //rounded corner radius
};

View file

@ -40,24 +40,6 @@ static char* _int2str(int num)
}
static void _updateRoundedCorner(LottieGroup* parent, LottieRoundedCorner* roundedCorner)
{
for (auto child = parent->children.data; child < parent->children.end(); ++child) {
auto obj = *child;
if (obj->type == LottieObject::Rect) {
auto rect = static_cast<LottieRect*>(obj);
rect->roundedCorner = roundedCorner;
rect->statical &= roundedCorner->statical;
parent->statical &= roundedCorner->statical;
continue;
}
if (obj->type == LottieObject::Group || obj->type == LottieObject::Layer) {
_updateRoundedCorner(static_cast<LottieGroup*>(obj), roundedCorner);
}
}
}
CompositeMethod LottieParser::getMaskMethod(bool inversed)
{
switch (getString()[0]) {
@ -484,7 +466,7 @@ LottieRect* LottieParser::parseRect()
if (!strcmp(key, "d")) rect->direction = getInt();
else if (!strcmp(key, "s")) parseProperty(rect->size);
else if (!strcmp(key, "p")) parseProperty(rect->position);
else if (!strcmp(key, "r")) parseProperty(rect->round);
else if (!strcmp(key, "r")) parseProperty(rect->radius);
else if (!strcmp(key, "nm")) rect->name = getStringCopy();
else if (!strcmp(key, "hd")) rect->hidden = getBool();
else skip(key);
@ -787,14 +769,10 @@ void LottieParser::parseObject(LottieGroup* parent)
enterObject();
while (auto key = nextObjectKey()) {
if (!strcmp(key, "ty")) {
auto child = parseObject();
if (child && !child->hidden) {
//propagate the rounded corner properties.
if (child->type == LottieObject::RoundedCorner) {
_updateRoundedCorner(parent, static_cast<LottieRoundedCorner*>(child));
}
parent->children.push(child);
} else delete(child);
if (auto child = parseObject()) {
if (child->hidden) delete(child);
else parent->children.push(child);
}
} else skip(key);
}
}