Skip to content

Commit

Permalink
lottie: handle roundness in path
Browse files Browse the repository at this point in the history
Implemented rounding of corners between
bezier curves that are straight lines.

@issue: thorvg#2230
  • Loading branch information
mgrudzinska committed May 13, 2024
1 parent b2adb2b commit 111f992
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 14 deletions.
23 changes: 23 additions & 0 deletions src/common/tvgMath.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,12 @@ static inline bool mathZero(float a)
}


static inline bool mathZero(const Point& p)
{
return mathZero(p.x) && mathZero(p.y);
}


static inline bool mathEqual(float a, float b)
{
return (fabsf(a - b) < FLT_EPSILON);
Expand Down Expand Up @@ -116,6 +122,17 @@ static inline void mathTransform(Matrix* transform, Point* coord)
}


static inline Point mathTransform(Matrix* transform, const Point& coord)
{
if (!transform) return coord;

auto x = coord.x * transform->e11 + coord.y * transform->e12 + transform->e13;
auto y = coord.x * transform->e21 + coord.y * transform->e22 + transform->e23;

return {x, y};
}


static inline void mathScale(Matrix* m, float sx, float sy)
{
m->e11 *= sx;
Expand Down Expand Up @@ -169,6 +186,12 @@ static inline float mathLength(const Point* a, const Point* b)
}


static inline float mathLength(const Point& a)
{
return sqrtf(a.x * a.x + a.y * a.y);
}


static inline Point operator-(const Point& lhs, const Point& rhs)
{
return {lhs.x - rhs.x, lhs.y - rhs.y};
Expand Down
11 changes: 5 additions & 6 deletions src/loaders/lottie/tvgLottieBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -520,15 +520,14 @@ static void _updatePath(LottieGroup* parent, LottieObject** child, float frameNo

if (ctx->repeater) {
auto p = Shape::gen();
path->pathset(frameNo, P(p)->rs.path.cmds, P(p)->rs.path.pts, ctx->transform, exps);
path->pathset(frameNo, P(p)->rs.path.cmds, P(p)->rs.path.pts, ctx->transform, ctx->roundness, exps);
_repeat(parent, std::move(p), ctx);
} else {
auto merging = _draw(parent, ctx);
if (path->pathset(frameNo, P(merging)->rs.path.cmds, P(merging)->rs.path.pts, ctx->transform, exps)) {
if (path->pathset(frameNo, P(merging)->rs.path.cmds, P(merging)->rs.path.pts, ctx->transform, ctx->roundness, exps)) {
P(merging)->update(RenderUpdateFlag::Path);
}
if (ctx->roundness > 1.0f && P(merging)->rs.stroke) {
TVGERR("LOTTIE", "FIXME: Path roundesss should be applied properly!");
if (ctx->roundness > ROUNDNESS_EPSILON && P(merging)->rs.stroke) {
P(merging)->rs.stroke->join = StrokeJoin::Round;
}
}
Expand Down Expand Up @@ -587,7 +586,7 @@ static void _updateText(LottieGroup* parent, LottieObject** child, float frameNo
for (auto g = glyph->children.begin(); g < glyph->children.end(); ++g) {
auto group = static_cast<LottieGroup*>(*g);
for (auto p = group->children.begin(); p < group->children.end(); ++p) {
if (static_cast<LottiePath*>(*p)->pathset(frameNo, P(shape)->rs.path.cmds, P(shape)->rs.path.pts, nullptr, exps)) {
if (static_cast<LottiePath*>(*p)->pathset(frameNo, P(shape)->rs.path.cmds, P(shape)->rs.path.pts, nullptr, 0.0f, exps)) {
P(shape)->update(RenderUpdateFlag::Path);
}
}
Expand Down Expand Up @@ -1032,7 +1031,7 @@ static void _updateMaskings(LottieLayer* layer, float frameNo, LottieExpressions
auto shape = Shape::gen().release();
shape->fill(255, 255, 255, mask->opacity(frameNo));
shape->transform(layer->cache.matrix);
if (mask->pathset(frameNo, P(shape)->rs.path.cmds, P(shape)->rs.path.pts, nullptr, exps)) {
if (mask->pathset(frameNo, P(shape)->rs.path.cmds, P(shape)->rs.path.pts, nullptr, 0.0f, exps)) {
P(shape)->update(RenderUpdateFlag::Path);
}
auto method = mask->method;
Expand Down
4 changes: 2 additions & 2 deletions src/loaders/lottie/tvgLottieExpressions.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,12 +109,12 @@ struct LottieExpressions
}

template<typename Property>
bool result(float frameNo, Array<PathCommand>& cmds, Array<Point>& pts, Matrix* transform, LottieExpression* exp)
bool result(float frameNo, Array<PathCommand>& cmds, Array<Point>& pts, Matrix* transform, float roundness, LottieExpression* exp)
{
auto bm_rt = evaluate(frameNo, exp);

if (auto pathset = static_cast<Property*>(jerry_object_get_native_ptr(bm_rt, nullptr))) {
(*pathset)(frameNo, cmds, pts, transform);
(*pathset)(frameNo, cmds, pts, transform, roundness);
} else {
TVGERR("LOTTIE", "Failed dispatching PathSet!");
return false;
Expand Down
88 changes: 82 additions & 6 deletions src/loaders/lottie/tvgLottieProperty.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include "tvgLottieInterpolator.h"
#include "tvgLottieExpressions.h"

#define ROUNDNESS_EPSILON 1.0f

struct LottieFont;
struct LottieLayer;
Expand Down Expand Up @@ -455,11 +456,14 @@ struct LottiePathSet : LottieProperty
return (*frames)[frames->count];
}

bool operator()(float frameNo, Array<PathCommand>& cmds, Array<Point>& pts, Matrix* transform)
bool operator()(float frameNo, Array<PathCommand>& cmds, Array<Point>& pts, Matrix* transform, float roundness)
{
if (!frames) {
_copy(value, cmds);
_copy(value, pts, transform);
if (roundness > ROUNDNESS_EPSILON) _handleCorners(cmds, pts, transform, roundness);
else {
_copy(value, cmds);
_copy(value, pts, transform);
}
return true;
}

Expand Down Expand Up @@ -507,16 +511,88 @@ struct LottiePathSet : LottieProperty
}


bool operator()(float frameNo, Array<PathCommand>& cmds, Array<Point>& pts, Matrix* transform, LottieExpressions* exps)
bool operator()(float frameNo, Array<PathCommand>& cmds, Array<Point>& pts, Matrix* transform, float roundness, LottieExpressions* exps)
{
if (exps && (exp && exp->enabled)) {
if (exp->loop.mode != LottieExpression::LoopMode::None) frameNo = _loop(frames, frameNo, exp);
if (exps->result<LottiePathSet>(frameNo, cmds, pts, transform, exp)) return true;
if (exps->result<LottiePathSet>(frameNo, cmds, pts, transform, roundness, exp)) return true;
}
return operator()(frameNo, cmds, pts, transform);
return operator()(frameNo, cmds, pts, transform, roundness);
}

void prepare() {}

private:
void _roundCorner(Array<PathCommand>& cmds, Array<Point>& pts, const Point& prev, const Point& curr, const Point& next, float roundness)
{
auto lenPrev = mathLength(prev - curr);
auto rPrev = lenPrev > 0.0f ? 0.5f * mathMin(lenPrev * 0.5f, roundness) / lenPrev : 0.0f;
auto lenNext = mathLength(next - curr);
auto rNext = lenNext > 0.0f ? 0.5f * mathMin(lenNext * 0.5f, roundness) / lenNext : 0.0f;

auto dPrev = rPrev * (curr - prev);
auto dNext = rNext * (curr - next);

pts.push(curr - 2.0f * dPrev);
pts.push(curr - dPrev);
pts.push(curr - dNext);
pts.push(curr - 2.0f * dNext);
cmds.push(PathCommand::LineTo);
cmds.push(PathCommand::CubicTo);
}

void _handleCorners(Array<PathCommand>& cmds, Array<Point>& pts, Matrix* transform, float roundness)
{
cmds.reserve(value.cmdsCnt * 2);
pts.reserve((uint16_t)(value.ptsCnt * 1.5));
auto ptsCnt = pts.count;

for (auto iCmds = 0, iPts = 0; iCmds < value.cmdsCnt; ++iCmds) {
auto startIndex = 0;
switch (value.cmds[iCmds]) {
case PathCommand::MoveTo: {
startIndex = pts.count;
cmds.push(PathCommand::MoveTo);
pts.push(value.pts[iPts++]);
break;
}
case PathCommand::CubicTo: {
auto& prev = value.pts[iPts - 1];
auto& curr = value.pts[iPts + 2];
if (iCmds < value.cmdsCnt - 1 &&
(mathZero(value.pts[iPts - 1] - value.pts[iPts]) ||
mathZero(value.pts[iPts + 1] - value.pts[iPts + 2]))) {
if (value.cmds[iCmds + 1] == PathCommand::CubicTo &&
(mathZero(value.pts[iPts + 2] - value.pts[iPts + 3]) ||
mathZero(value.pts[iPts + 4] - value.pts[iPts + 5]))) {
_roundCorner(cmds, pts, prev, curr, value.pts[iPts + 5], roundness);
iPts += 3;
break;
} else if (value.cmds[iCmds + 1] == PathCommand::Close) {
_roundCorner(cmds, pts, prev, curr, value.pts[2], roundness);
pts[startIndex] = pts.last();
iPts += 3;
break;
}
}
cmds.push(PathCommand::CubicTo);
pts.push(value.pts[iPts++]);
pts.push(value.pts[iPts++]);
pts.push(value.pts[iPts++]);
break;
}
case PathCommand::Close: {
cmds.push(PathCommand::Close);
break;
}
default: break;
}
}
if (transform) {
for (auto i = ptsCnt; i < pts.count; ++i)
mathTransform(transform, &pts[i]);
}
}
};


Expand Down

0 comments on commit 111f992

Please sign in to comment.