mirror of
https://github.com/thorvg/thorvg.git
synced 2025-06-10 06:34:01 +00:00
lottie: keep the code clean and neat.
- Separate the rounded corner logic from the updateStar since its logic has become too lengthy and complicated. - Revise the path generation to eliminate duplicates.
This commit is contained in:
parent
47e1d48b28
commit
bcc8dd4f09
2 changed files with 133 additions and 159 deletions
|
@ -191,13 +191,12 @@ static bool _updateTransform(LottieTransform* transform, float frameNo, bool aut
|
|||
}
|
||||
|
||||
auto skewAngle = transform->skewAngle(frameNo, exps);
|
||||
if (fabsf(skewAngle) > 0.01f) {
|
||||
if (skewAngle != 0.0f) {
|
||||
// For angles where tangent explodes, the shape degenerates into an infinitely thin line.
|
||||
// This is handled by zeroing out the matrix due to finite numerical precision.
|
||||
skewAngle = fmod(skewAngle, 180.0f);
|
||||
if (fabsf(skewAngle - 90.0f) < 0.01f || fabsf(skewAngle + 90.0f) < 0.01f) return false;
|
||||
auto skewAxis = transform->skewAxis(frameNo, exps);
|
||||
_skew(&matrix, skewAngle, skewAxis);
|
||||
_skew(&matrix, skewAngle, transform->skewAxis(frameNo, exps));
|
||||
}
|
||||
|
||||
auto scale = transform->scale(frameNo, exps);
|
||||
|
@ -498,7 +497,7 @@ static void _updateRect(LottieGroup* parent, LottieObject** child, float frameNo
|
|||
auto roundness = rect->radius(frameNo, exps);
|
||||
if (ctx->roundness > roundness) roundness = ctx->roundness;
|
||||
|
||||
if (roundness > 0.0f) {
|
||||
if (roundness > ROUNDNESS_EPSILON) {
|
||||
if (roundness > size.x * 0.5f) roundness = size.x * 0.5f;
|
||||
if (roundness > size.y * 0.5f) roundness = size.y * 0.5f;
|
||||
}
|
||||
|
@ -656,10 +655,72 @@ static void _updateText(LottieGroup* parent, LottieObject** child, float frameNo
|
|||
}
|
||||
|
||||
|
||||
static void _applyRoundedCorner(Shape* star, Shape* merging, float outerRoundness, float roundness, bool hasRoundness)
|
||||
{
|
||||
static constexpr auto ROUNDED_POLYSTAR_MAGIC_NUMBER = 0.47829f;
|
||||
|
||||
auto cmdCnt = star->pathCommands(nullptr);
|
||||
const Point *pts = nullptr;
|
||||
auto ptsCnt = star->pathCoords(&pts);
|
||||
|
||||
auto len = mathLength(pts[1] - pts[2]);
|
||||
auto r = len > 0.0f ? ROUNDED_POLYSTAR_MAGIC_NUMBER * mathMin(len * 0.5f, roundness) / len : 0.0f;
|
||||
|
||||
if (hasRoundness) {
|
||||
P(merging)->rs.path.cmds.grow((uint32_t)(1.5 * cmdCnt));
|
||||
P(merging)->rs.path.pts.grow((uint32_t)(4.5 * cmdCnt));
|
||||
|
||||
int start = 3 * mathZero(outerRoundness);
|
||||
merging->moveTo(pts[start].x, pts[start].y);
|
||||
|
||||
for (uint32_t i = 1 + start; i < ptsCnt; i += 6) {
|
||||
auto& prev = pts[i];
|
||||
auto& curr = pts[i + 2];
|
||||
auto& next = (i < ptsCnt - start) ? pts[i + 4] : pts[2];
|
||||
auto& nextCtrl = (i < ptsCnt - start) ? pts[i + 5] : pts[3];
|
||||
auto dNext = r * (curr - next);
|
||||
auto dPrev = r * (curr - prev);
|
||||
|
||||
auto p0 = curr - 2.0f * dPrev;
|
||||
auto p1 = curr - dPrev;
|
||||
auto p2 = curr - dNext;
|
||||
auto p3 = curr - 2.0f * dNext;
|
||||
|
||||
merging->cubicTo(prev.x, prev.y, p0.x, p0.y, p0.x, p0.y);
|
||||
merging->cubicTo(p1.x, p1.y, p2.x, p2.y, p3.x, p3.y);
|
||||
merging->cubicTo(p3.x, p3.y, next.x, next.y, nextCtrl.x, nextCtrl.y);
|
||||
}
|
||||
} else {
|
||||
P(merging)->rs.path.cmds.grow(2 * cmdCnt);
|
||||
P(merging)->rs.path.pts.grow(4 * cmdCnt);
|
||||
|
||||
auto dPrev = r * (pts[1] - pts[0]);
|
||||
auto p = pts[0] + 2.0f * dPrev;
|
||||
merging->moveTo(p.x, p.y);
|
||||
|
||||
for (uint32_t i = 1; i < ptsCnt; ++i) {
|
||||
auto& curr = pts[i];
|
||||
auto& next = (i == ptsCnt - 1) ? pts[1] : pts[i + 1];
|
||||
auto dNext = r * (curr - next);
|
||||
|
||||
auto p0 = curr - 2.0f * dPrev;
|
||||
auto p1 = curr - dPrev;
|
||||
auto p2 = curr - dNext;
|
||||
auto p3 = curr - 2.0f * dNext;
|
||||
|
||||
merging->lineTo(p0.x, p0.y);
|
||||
merging->cubicTo(p1.x, p1.y, p2.x, p2.y, p3.x, p3.y);
|
||||
|
||||
dPrev = -1.0f * dNext;
|
||||
}
|
||||
}
|
||||
merging->close();
|
||||
}
|
||||
|
||||
|
||||
static void _updateStar(LottieGroup* parent, LottiePolyStar* star, Matrix* transform, float roundness, float frameNo, Shape* merging, LottieExpressions* exps)
|
||||
{
|
||||
static constexpr auto POLYSTAR_MAGIC_NUMBER = 0.47829f / 0.28f;
|
||||
static constexpr auto ROUNDED_POLYSTAR_MAGIC_NUMBER = 0.47829f;
|
||||
|
||||
auto ptsCnt = star->ptsCnt(frameNo, exps);
|
||||
auto innerRadius = star->innerRadius(frameNo, exps);
|
||||
|
@ -676,8 +737,9 @@ static void _updateStar(LottieGroup* parent, LottiePolyStar* star, Matrix* trans
|
|||
auto numPoints = size_t(ceilf(ptsCnt) * 2);
|
||||
auto direction = (star->direction == 0) ? 1.0f : -1.0f;
|
||||
auto hasRoundness = false;
|
||||
bool applyExtRoundness = roundness > ROUNDNESS_EPSILON && (mathZero(innerRoundness) || mathZero(outerRoundness));
|
||||
auto shape = applyExtRoundness ? Shape::gen().release() : merging;
|
||||
bool roundedCorner = (roundness > ROUNDNESS_EPSILON) && (mathZero(innerRoundness) || mathZero(outerRoundness));
|
||||
//TODO: we can use PathCommand / PathCoord directly.
|
||||
auto shape = roundedCorner ? Shape::gen().release() : merging;
|
||||
|
||||
float x, y;
|
||||
|
||||
|
@ -766,63 +828,8 @@ static void _updateStar(LottieGroup* parent, LottiePolyStar* star, Matrix* trans
|
|||
}
|
||||
shape->close();
|
||||
|
||||
if (applyExtRoundness) {
|
||||
auto cmdCnt = shape->pathCommands(nullptr);
|
||||
const Point *pts = nullptr;
|
||||
auto ptsCnt = shape->pathCoords(&pts);
|
||||
|
||||
auto len = mathLength(pts[1] - pts[2]);
|
||||
auto r = len > 0.0f ? ROUNDED_POLYSTAR_MAGIC_NUMBER * mathMin(len * 0.5f, roundness) / len : 0.0f;
|
||||
|
||||
if (hasRoundness) {
|
||||
P(merging)->rs.path.cmds.grow((uint32_t)(1.5 * cmdCnt));
|
||||
P(merging)->rs.path.pts.grow((uint32_t)(4.5 * cmdCnt));
|
||||
|
||||
int start = 3 * mathZero(outerRoundness);
|
||||
merging->moveTo(pts[start].x, pts[start].y);
|
||||
|
||||
for (uint32_t i = 1 + start; i < ptsCnt; i += 6) {
|
||||
auto& prev = pts[i];
|
||||
auto& curr = pts[i + 2];
|
||||
auto& next = (i < ptsCnt - start) ? pts[i + 4] : pts[2];
|
||||
auto& nextCtrl = (i < ptsCnt - start) ? pts[i + 5] : pts[3];
|
||||
auto dNext = r * (curr - next);
|
||||
auto dPrev = r * (curr - prev);
|
||||
|
||||
auto p0 = curr - 2.0f * dPrev;
|
||||
auto p1 = curr - dPrev;
|
||||
auto p2 = curr - dNext;
|
||||
auto p3 = curr - 2.0f * dNext;
|
||||
|
||||
merging->cubicTo(prev.x, prev.y, p0.x, p0.y, p0.x, p0.y);
|
||||
merging->cubicTo(p1.x, p1.y, p2.x, p2.y, p3.x, p3.y);
|
||||
merging->cubicTo(p3.x, p3.y, next.x, next.y, nextCtrl.x, nextCtrl.y);
|
||||
}
|
||||
} else {
|
||||
P(merging)->rs.path.cmds.grow(2 * cmdCnt);
|
||||
P(merging)->rs.path.pts.grow(4 * cmdCnt);
|
||||
|
||||
auto dPrev = r * (pts[1] - pts[0]);
|
||||
auto p = pts[0] + 2.0f * dPrev;
|
||||
merging->moveTo(p.x, p.y);
|
||||
|
||||
for (uint32_t i = 1; i < ptsCnt; ++i) {
|
||||
auto& curr = pts[i];
|
||||
auto& next = (i == ptsCnt - 1) ? pts[1] : pts[i + 1];
|
||||
auto dNext = r * (curr - next);
|
||||
|
||||
auto p0 = curr - 2.0f * dPrev;
|
||||
auto p1 = curr - dPrev;
|
||||
auto p2 = curr - dNext;
|
||||
auto p3 = curr - 2.0f * dNext;
|
||||
|
||||
merging->lineTo(p0.x, p0.y);
|
||||
merging->cubicTo(p1.x, p1.y, p2.x, p2.y, p3.x, p3.y);
|
||||
|
||||
dPrev = -1.0f * dNext;
|
||||
}
|
||||
}
|
||||
merging->close();
|
||||
if (roundedCorner) {
|
||||
_applyRoundedCorner(shape, merging, outerRoundness, roundness, hasRoundness);
|
||||
delete(shape);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -210,30 +210,30 @@ struct LottieExpression
|
|||
};
|
||||
|
||||
|
||||
static void _copy(PathSet& pathset, Array<Point>& outPts, Matrix* transform)
|
||||
static void _copy(PathSet* pathset, Array<Point>& outPts, Matrix* transform)
|
||||
{
|
||||
Array<Point> inPts;
|
||||
|
||||
if (transform) {
|
||||
for (int i = 0; i < pathset.ptsCnt; ++i) {
|
||||
Point pt = pathset.pts[i];
|
||||
for (int i = 0; i < pathset->ptsCnt; ++i) {
|
||||
Point pt = pathset->pts[i];
|
||||
mathMultiply(&pt, transform);
|
||||
outPts.push(pt);
|
||||
}
|
||||
} else {
|
||||
inPts.data = pathset.pts;
|
||||
inPts.count = pathset.ptsCnt;
|
||||
inPts.data = pathset->pts;
|
||||
inPts.count = pathset->ptsCnt;
|
||||
outPts.push(inPts);
|
||||
inPts.data = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void _copy(PathSet& pathset, Array<PathCommand>& outCmds)
|
||||
static void _copy(PathSet* pathset, Array<PathCommand>& outCmds)
|
||||
{
|
||||
Array<PathCommand> inCmds;
|
||||
inCmds.data = pathset.cmds;
|
||||
inCmds.count = pathset.cmdsCnt;
|
||||
inCmds.data = pathset->cmds;
|
||||
inCmds.count = pathset->cmdsCnt;
|
||||
outCmds.push(inCmds);
|
||||
inCmds.data = nullptr;
|
||||
}
|
||||
|
@ -258,44 +258,44 @@ static void _roundCorner(Array<PathCommand>& cmds, Array<Point>& pts, const Poin
|
|||
}
|
||||
|
||||
|
||||
static void _handleCorners(const PathSet& path, Array<PathCommand>& cmds, Array<Point>& pts, Matrix* transform, float roundness)
|
||||
static bool _modifier(Point* inputPts, uint32_t inputPtsCnt, PathCommand* inputCmds, uint32_t inputCmdsCnt, Array<PathCommand>& cmds, Array<Point>& pts, Matrix* transform, float roundness)
|
||||
{
|
||||
cmds.reserve(path.cmdsCnt * 2);
|
||||
pts.reserve((uint16_t)(path.ptsCnt * 1.5));
|
||||
cmds.reserve(inputCmdsCnt * 2);
|
||||
pts.reserve((uint16_t)(inputPtsCnt * 1.5));
|
||||
auto ptsCnt = pts.count;
|
||||
|
||||
auto startIndex = 0;
|
||||
for (auto iCmds = 0, iPts = 0; iCmds < path.cmdsCnt; ++iCmds) {
|
||||
switch (path.cmds[iCmds]) {
|
||||
for (uint32_t iCmds = 0, iPts = 0; iCmds < inputCmdsCnt; ++iCmds) {
|
||||
switch (inputCmds[iCmds]) {
|
||||
case PathCommand::MoveTo: {
|
||||
startIndex = pts.count;
|
||||
cmds.push(PathCommand::MoveTo);
|
||||
pts.push(path.pts[iPts++]);
|
||||
pts.push(inputPts[iPts++]);
|
||||
break;
|
||||
}
|
||||
case PathCommand::CubicTo: {
|
||||
auto& prev = path.pts[iPts - 1];
|
||||
auto& curr = path.pts[iPts + 2];
|
||||
if (iCmds < path.cmdsCnt - 1 &&
|
||||
mathZero(path.pts[iPts - 1] - path.pts[iPts]) &&
|
||||
mathZero(path.pts[iPts + 1] - path.pts[iPts + 2])) {
|
||||
if (path.cmds[iCmds + 1] == PathCommand::CubicTo &&
|
||||
mathZero(path.pts[iPts + 2] - path.pts[iPts + 3]) &&
|
||||
mathZero(path.pts[iPts + 4] - path.pts[iPts + 5])) {
|
||||
_roundCorner(cmds, pts, prev, curr, path.pts[iPts + 5], roundness);
|
||||
auto& prev = inputPts[iPts - 1];
|
||||
auto& curr = inputPts[iPts + 2];
|
||||
if (iCmds < inputCmdsCnt - 1 &&
|
||||
mathZero(inputPts[iPts - 1] - inputPts[iPts]) &&
|
||||
mathZero(inputPts[iPts + 1] - inputPts[iPts + 2])) {
|
||||
if (inputCmds[iCmds + 1] == PathCommand::CubicTo &&
|
||||
mathZero(inputPts[iPts + 2] - inputPts[iPts + 3]) &&
|
||||
mathZero(inputPts[iPts + 4] - inputPts[iPts + 5])) {
|
||||
_roundCorner(cmds, pts, prev, curr, inputPts[iPts + 5], roundness);
|
||||
iPts += 3;
|
||||
break;
|
||||
} else if (path.cmds[iCmds + 1] == PathCommand::Close) {
|
||||
_roundCorner(cmds, pts, prev, curr, path.pts[2], roundness);
|
||||
} else if (inputCmds[iCmds + 1] == PathCommand::Close) {
|
||||
_roundCorner(cmds, pts, prev, curr, inputPts[2], roundness);
|
||||
pts[startIndex] = pts.last();
|
||||
iPts += 3;
|
||||
break;
|
||||
}
|
||||
}
|
||||
cmds.push(PathCommand::CubicTo);
|
||||
pts.push(path.pts[iPts++]);
|
||||
pts.push(path.pts[iPts++]);
|
||||
pts.push(path.pts[iPts++]);
|
||||
pts.push(inputPts[iPts++]);
|
||||
pts.push(inputPts[iPts++]);
|
||||
pts.push(inputPts[iPts++]);
|
||||
break;
|
||||
}
|
||||
case PathCommand::Close: {
|
||||
|
@ -309,6 +309,7 @@ static void _handleCorners(const PathSet& path, Array<PathCommand>& cmds, Array<
|
|||
for (auto i = ptsCnt; i < pts.count; ++i)
|
||||
mathTransform(transform, &pts[i]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
@ -442,8 +443,8 @@ struct LottieGenericProperty : LottieProperty
|
|||
|
||||
T operator()(float frameNo, LottieExpressions* exps)
|
||||
{
|
||||
T out{};
|
||||
if (exps && (exp && exp->enabled)) {
|
||||
T out{};
|
||||
if (exp->loop.mode != LottieExpression::LoopMode::None) frameNo = _loop(frames, frameNo, exp);
|
||||
if (exps->result<LottieGenericProperty<T>>(frameNo, out, exp)) return out;
|
||||
}
|
||||
|
@ -531,59 +532,29 @@ struct LottiePathSet : LottieProperty
|
|||
|
||||
bool operator()(float frameNo, Array<PathCommand>& cmds, Array<Point>& pts, Matrix* transform, float roundness)
|
||||
{
|
||||
if (!frames) {
|
||||
if (roundness > ROUNDNESS_EPSILON) {
|
||||
_handleCorners(value, cmds, pts, transform, roundness);
|
||||
return true;
|
||||
PathSet* path = nullptr;
|
||||
LottieScalarFrame<PathSet>* frame = nullptr;
|
||||
float t;
|
||||
bool interpolate = false;
|
||||
|
||||
if (!frames) path = &value;
|
||||
else if (frames->count == 1 || frameNo <= frames->first().no) path = &frames->first().value;
|
||||
else if (frameNo >= frames->last().no) path = &frames->last().value;
|
||||
else {
|
||||
frame = frames->data + _bsearch(frames, frameNo);
|
||||
if (mathEqual(frame->no, frameNo)) path = &frame->value;
|
||||
else {
|
||||
t = (frameNo - frame->no) / ((frame + 1)->no - frame->no);
|
||||
if (frame->interpolator) t = frame->interpolator->progress(t);
|
||||
if (frame->hold) path = &(frame + ((t < 1.0f) ? 0 : 1))->value;
|
||||
else interpolate = true;
|
||||
}
|
||||
_copy(value, cmds);
|
||||
_copy(value, pts, transform);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (frames->count == 1 || frameNo <= frames->first().no) {
|
||||
if (roundness > ROUNDNESS_EPSILON) {
|
||||
_handleCorners(frames->first().value, cmds, pts, transform, roundness);
|
||||
return true;
|
||||
}
|
||||
_copy(frames->first().value, cmds);
|
||||
_copy(frames->first().value, pts, transform);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (frameNo >= frames->last().no) {
|
||||
if (roundness > ROUNDNESS_EPSILON) {
|
||||
_handleCorners(frames->last().value, cmds, pts, transform, roundness);
|
||||
return true;
|
||||
}
|
||||
_copy(frames->last().value, cmds);
|
||||
_copy(frames->last().value, pts, transform);
|
||||
return true;
|
||||
}
|
||||
|
||||
auto frame = frames->data + _bsearch(frames, frameNo);
|
||||
|
||||
if (mathEqual(frame->no, frameNo)) {
|
||||
if (roundness > ROUNDNESS_EPSILON) {
|
||||
_handleCorners(frame->value, cmds, pts, transform, roundness);
|
||||
return true;
|
||||
}
|
||||
_copy(frame->value, cmds);
|
||||
_copy(frame->value, pts, transform);
|
||||
return true;
|
||||
}
|
||||
|
||||
//interpolate
|
||||
auto t = (frameNo - frame->no) / ((frame + 1)->no - frame->no);
|
||||
if (frame->interpolator) t = frame->interpolator->progress(t);
|
||||
|
||||
if (frame->hold) {
|
||||
if (roundness > ROUNDNESS_EPSILON) {
|
||||
_handleCorners((frame + ((t < 1.0f) ? 0 : 1))->value, cmds, pts, transform, roundness);
|
||||
return true;
|
||||
}
|
||||
_copy((frame + ((t < 1.0f) ? 0 : 1))->value, cmds);
|
||||
_copy((frame)->value, pts, transform);
|
||||
if (!interpolate) {
|
||||
if (roundness > ROUNDNESS_EPSILON) return _modifier(path->pts, path->ptsCnt, path->cmds, path->cmdsCnt, cmds, pts, transform, roundness);
|
||||
_copy(path, cmds);
|
||||
_copy(path, pts, transform);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -591,27 +562,23 @@ struct LottiePathSet : LottieProperty
|
|||
auto e = (frame + 1)->value.pts;
|
||||
|
||||
if (roundness > ROUNDNESS_EPSILON) {
|
||||
PathSet interpPathSet = frame->value;
|
||||
interpPathSet.pts = (Point*)malloc(frame->value.ptsCnt * sizeof(Point));
|
||||
|
||||
auto p = interpPathSet.pts;
|
||||
auto interpPts = (Point*)malloc(frame->value.ptsCnt * sizeof(Point));
|
||||
auto p = interpPts;
|
||||
for (auto i = 0; i < frame->value.ptsCnt; ++i, ++s, ++e, ++p) {
|
||||
*p = mathLerp(*s, *e, t);
|
||||
if (transform) mathMultiply(p, transform);
|
||||
}
|
||||
_modifier(interpPts, frame->value.ptsCnt, frame->value.cmds, frame->value.cmdsCnt, cmds, pts, nullptr, roundness);
|
||||
free(interpPts);
|
||||
return true;
|
||||
} else {
|
||||
for (auto i = 0; i < frame->value.ptsCnt; ++i, ++s, ++e) {
|
||||
auto pt = mathLerp(*s, *e, t);
|
||||
if (transform) mathMultiply(&pt, transform);
|
||||
*p = pt;
|
||||
pts.push(pt);
|
||||
}
|
||||
_handleCorners(interpPathSet, cmds, pts, nullptr, roundness);
|
||||
free(interpPathSet.pts);
|
||||
return true;
|
||||
_copy(&frame->value, cmds);
|
||||
}
|
||||
|
||||
for (auto i = 0; i < frame->value.ptsCnt; ++i, ++s, ++e) {
|
||||
auto pt = mathLerp(*s, *e, t);
|
||||
if (transform) mathMultiply(&pt, transform);
|
||||
pts.push(pt);
|
||||
}
|
||||
_copy(frame->value, cmds);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue