mirror of
https://github.com/thorvg/thorvg.git
synced 2025-07-23 14:48:24 +00:00
lottie: use cubics instead of lines
This commit is contained in:
parent
bd3d92f599
commit
065408b47a
3 changed files with 115 additions and 74 deletions
|
@ -391,12 +391,71 @@ static void _repeat(LottieGroup* parent, Shape* path, RenderContext* ctx)
|
|||
}
|
||||
|
||||
|
||||
static void _appendRect(RenderPath& path, float x, float y, float w, float h, float r, bool cw)
|
||||
{
|
||||
//sharp rect
|
||||
if (tvg::zero(r)) {
|
||||
path.cmds.grow(6);
|
||||
path.cmds.push(PathCommand::MoveTo);
|
||||
for (auto i = 0; i < 4; ++i) path.cmds.push(PathCommand::CubicTo);
|
||||
path.cmds.push(PathCommand::Close);
|
||||
|
||||
path.pts.grow(13);
|
||||
path.pts.push({x + w, y});
|
||||
if (cw) {
|
||||
path.pts.push({x + w, y}); path.pts.push({x + w, y + h}); path.pts.push({x + w, y + h});
|
||||
path.pts.push({x + w, y + h}); path.pts.push({x, y + h}); path.pts.push({x, y + h});
|
||||
path.pts.push({x, y + h}); path.pts.push({x, y}); path.pts.push({x, y});
|
||||
path.pts.push({x, y}); path.pts.push({x + w, y}); path.pts.push({x + w, y});
|
||||
} else {
|
||||
path.pts.push({x + w, y}); path.pts.push({x, y}); path.pts.push({x, y});
|
||||
path.pts.push({x, y}); path.pts.push({x, y + h}); path.pts.push({x, y + h});
|
||||
path.pts.push({x, y + h}); path.pts.push({x + w, y + h}); path.pts.push({x + w, y + h});
|
||||
path.pts.push({x + w, y + h}); path.pts.push({x + w, y}); path.pts.push({x + w, y});
|
||||
}
|
||||
//round rect
|
||||
} else {
|
||||
auto hsize = Point{w * 0.5f, h * 0.5f};
|
||||
auto rx = (r > hsize.x) ? hsize.x : r;
|
||||
auto ry = (r > hsize.y) ? hsize.y : r;
|
||||
auto hr = Point{rx * PATH_KAPPA, ry * PATH_KAPPA};
|
||||
|
||||
path.cmds.grow(10);
|
||||
path.cmds.push(PathCommand::MoveTo);
|
||||
for (auto i = 0; i < 8; ++i) path.cmds.push(PathCommand::CubicTo);
|
||||
path.cmds.push(PathCommand::Close);
|
||||
|
||||
path.pts.grow(25);
|
||||
path.pts.push({x + w, y + ry});
|
||||
if (cw) {
|
||||
path.pts.push({x + w, y + ry}); path.pts.push({x + w, y + h - ry}); path.pts.push({x + w, y + h - ry});
|
||||
path.pts.push({x + w, y + h - ry + hr.y}); path.pts.push({x + w - rx + hr.x, y + h}); path.pts.push({x + w - rx, y + h});
|
||||
path.pts.push({x + w - rx, y + h}); path.pts.push({x + rx, y + h}); path.pts.push({x + rx, y + h});
|
||||
path.pts.push({x + rx - hr.x, y + h}); path.pts.push({x, y + h - ry + hr.y}); path.pts.push({x, y + h - ry});
|
||||
path.pts.push({x, y + h - ry}); path.pts.push({x, y + ry}); path.pts.push({x, y + ry});
|
||||
path.pts.push({x, y + ry - hr.y}); path.pts.push({x + rx - hr.x, y}); path.pts.push({x + rx, y});
|
||||
path.pts.push({x + rx, y}); path.pts.push({x + w - rx, y}); path.pts.push({x + w - rx, y});
|
||||
path.pts.push({x + w - rx + hr.x, y}); path.pts.push({x + w, y + ry - hr.y}); path.pts.push({x + w, y + ry});
|
||||
} else {
|
||||
path.pts.push({x + w, y + ry - hr.y}); path.pts.push({x + w - rx + hr.x, y}); path.pts.push({x + w - rx, y});
|
||||
path.pts.push({x + w - rx, y}); path.pts.push({x + rx, y}); path.pts.push({x + rx, y});
|
||||
path.pts.push({x + rx, y}); path.pts.push({x, y + ry - hr.y}); path.pts.push({x, y + ry});
|
||||
path.pts.push({x, y + ry}); path.pts.push({x, y + h - ry}); path.pts.push({x, y + h - ry});
|
||||
path.pts.push({x, y + h - ry + hr.y}); path.pts.push({x + rx - hr.x, y + h}); path.pts.push({x + rx, y + h});
|
||||
path.pts.push({x + rx, y + h}); path.pts.push({x + w - rx, y + h}); path.pts.push({x + w - rx, y + h});
|
||||
path.pts.push({x + w - rx + hr.x, y + h}); path.pts.push({x + w, y + h - ry + hr.y}); path.pts.push({x + w, y + h - ry});
|
||||
path.pts.push({x + w, y + h - ry}); path.pts.push({x + w, y + ry}); path.pts.push({x + w, y + ry});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void LottieBuilder::appendRect(Shape* shape, Point& pos, Point& size, float r, bool clockwise, RenderContext* ctx)
|
||||
{
|
||||
auto temp = (ctx->offset) ? Shape::gen() : shape;
|
||||
auto cnt = SHAPE(temp)->rs.path.pts.count;
|
||||
|
||||
temp->appendRect(pos.x, pos.y, size.x, size.y, r, r, clockwise);
|
||||
_appendRect(SHAPE(temp)->rs.path, pos.x, pos.y, size.x, size.y, r, clockwise);
|
||||
|
||||
if (ctx->transform) {
|
||||
for (auto i = cnt; i < SHAPE(temp)->rs.path.pts.count; ++i) {
|
||||
|
@ -506,7 +565,7 @@ void LottieBuilder::updateStar(LottiePolyStar* star, float frameNo, Matrix* tran
|
|||
auto longSegment = false;
|
||||
auto numPoints = size_t(ceilf(ptsCnt) * 2);
|
||||
auto direction = star->clockwise ? 1.0f : -1.0f;
|
||||
auto hasRoundness = false;
|
||||
auto hasRoundness = !tvg::zero(innerRoundness) || !tvg::zero(outerRoundness);
|
||||
bool roundedCorner = ctx->roundness && (tvg::zero(innerRoundness) || tvg::zero(outerRoundness));
|
||||
|
||||
Shape* shape;
|
||||
|
@ -534,14 +593,8 @@ void LottieBuilder::updateStar(LottiePolyStar* star, float frameNo, Matrix* tran
|
|||
angle += halfAnglePerPoint * direction;
|
||||
}
|
||||
|
||||
if (tvg::zero(innerRoundness) && tvg::zero(outerRoundness)) {
|
||||
SHAPE(shape)->rs.path.pts.reserve(numPoints + 2);
|
||||
SHAPE(shape)->rs.path.cmds.reserve(numPoints + 3);
|
||||
} else {
|
||||
SHAPE(shape)->rs.path.pts.reserve(numPoints * 3 + 2);
|
||||
SHAPE(shape)->rs.path.cmds.reserve(numPoints + 3);
|
||||
hasRoundness = true;
|
||||
}
|
||||
SHAPE(shape)->rs.path.pts.reserve(numPoints * 3 + 2);
|
||||
SHAPE(shape)->rs.path.cmds.reserve(numPoints + 3);
|
||||
|
||||
auto in = Point{x, y} * transform;
|
||||
shape->moveTo(in.x, in.y);
|
||||
|
@ -590,7 +643,7 @@ void LottieBuilder::updateStar(LottiePolyStar* star, float frameNo, Matrix* tran
|
|||
shape->cubicTo(in2.x, in2.y, in3.x, in3.y, in4.x, in4.y);
|
||||
} else {
|
||||
auto in = Point{x, y} * transform;
|
||||
shape->lineTo(in.x, in.y);
|
||||
shape->cubicTo(SHAPE(shape)->rs.path.pts.last().x, SHAPE(shape)->rs.path.pts.last().y, in.x, in.y, in.x, in.y);
|
||||
}
|
||||
angle += dTheta * direction;
|
||||
longSegment = !longSegment;
|
||||
|
@ -659,7 +712,7 @@ void LottieBuilder::updatePolygon(LottieGroup* parent, LottiePolyStar* star, flo
|
|||
} else {
|
||||
Point in = {x, y};
|
||||
if (transform) in *= *transform;
|
||||
shape->lineTo(in.x, in.y);
|
||||
shape->cubicTo(SHAPE(shape)->rs.path.pts.last().x, SHAPE(shape)->rs.path.pts.last().y, in.x, in.y, in.x, in.y);
|
||||
}
|
||||
angle += anglePerPoint * direction;
|
||||
}
|
||||
|
|
|
@ -33,6 +33,15 @@ static bool _colinear(const Point* p)
|
|||
}
|
||||
|
||||
|
||||
static void _cubic(Array<PathCommand>& cmds, Array<Point>& pts, const Point& p1, const Point& p2, const Point& p3)
|
||||
{
|
||||
cmds.push(PathCommand::CubicTo);
|
||||
pts.push(p1);
|
||||
pts.push(p2);
|
||||
pts.push(p3);
|
||||
}
|
||||
|
||||
|
||||
static void _roundCorner(Array<PathCommand>& cmds, Array<Point>& pts, Point& prev, Point& curr, Point& next, float r)
|
||||
{
|
||||
auto lenPrev = length(prev - curr);
|
||||
|
@ -43,12 +52,8 @@ static void _roundCorner(Array<PathCommand>& cmds, Array<Point>& pts, Point& pre
|
|||
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);
|
||||
_cubic(cmds, pts, pts.last(), curr - 2.0f * dPrev, curr - 2.0f * dPrev);
|
||||
_cubic(cmds, pts, curr - dPrev, curr - dNext, curr - 2.0f * dNext);
|
||||
}
|
||||
|
||||
|
||||
|
@ -108,42 +113,39 @@ void LottieOffsetModifier::corner(RenderPath& out, Line& line, Line& nextLine, u
|
|||
Point intersect{};
|
||||
if (_intersect(line, nextLine, intersect, inside)) {
|
||||
if (inside) {
|
||||
if (nextClose) out.pts[movetoOutIndex] = intersect;
|
||||
out.pts.push(intersect);
|
||||
if (nextClose) {
|
||||
if (movetoOutIndex + 1 < out.pts.count && tvg::zero(out.pts[movetoOutIndex] - out.pts[movetoOutIndex + 1])) out.pts[movetoOutIndex + 1] = intersect;
|
||||
out.pts[movetoOutIndex] = intersect;
|
||||
}
|
||||
_cubic(out.cmds, out.pts, out.pts.last(), intersect, intersect);
|
||||
} else {
|
||||
out.pts.push(line.pt2);
|
||||
_cubic(out.cmds, out.pts, out.pts.last(), line.pt2, line.pt2);
|
||||
if (join == StrokeJoin::Round) {
|
||||
out.cmds.push(PathCommand::CubicTo);
|
||||
out.pts.push((line.pt2 + intersect) * 0.5f);
|
||||
out.pts.push((nextLine.pt1 + intersect) * 0.5f);
|
||||
out.pts.push(nextLine.pt1);
|
||||
} else if (join == StrokeJoin::Miter) {
|
||||
auto norm = normal(line.pt1, line.pt2);
|
||||
auto nextNorm = normal(nextLine.pt1, nextLine.pt2);
|
||||
auto miterDirection = (norm + nextNorm) / length(norm + nextNorm);
|
||||
if (1.0f <= miterLimit * fabsf(miterDirection.x * norm.x + miterDirection.y * norm.y)) {
|
||||
out.cmds.push(PathCommand::LineTo);
|
||||
out.pts.push(intersect);
|
||||
}
|
||||
out.cmds.push(PathCommand::LineTo);
|
||||
out.pts.push(nextLine.pt1);
|
||||
_cubic(out.cmds, out.pts, (line.pt2 + intersect) * 0.5f, (nextLine.pt1 + intersect) * 0.5f, nextLine.pt1);
|
||||
} else {
|
||||
out.cmds.push(PathCommand::LineTo);
|
||||
out.pts.push(nextLine.pt1);
|
||||
if (join == StrokeJoin::Miter) {
|
||||
auto norm = normal(line.pt1, line.pt2);
|
||||
auto nextNorm = normal(nextLine.pt1, nextLine.pt2);
|
||||
auto miterDirection = (norm + nextNorm) / length(norm + nextNorm);
|
||||
if (1.0f <= miterLimit * fabsf(miterDirection.x * norm.x + miterDirection.y * norm.y)) {
|
||||
_cubic(out.cmds, out.pts, out.pts.last(), intersect, intersect);
|
||||
}
|
||||
}
|
||||
_cubic(out.cmds, out.pts, out.pts.last(), nextLine.pt1, nextLine.pt1);
|
||||
}
|
||||
}
|
||||
} else out.pts.push(line.pt2);
|
||||
} else _cubic(out.cmds, out.pts, out.pts.last(), line.pt2, line.pt2);
|
||||
}
|
||||
|
||||
|
||||
void LottieOffsetModifier::line(RenderPath& out, PathCommand* inCmds, uint32_t inCmdsCnt, Point* inPts, uint32_t& curPt, uint32_t curCmd, State& state, float offset, bool degenerated)
|
||||
void LottieOffsetModifier::line(RenderPath& out, PathCommand* inCmds, uint32_t inCmdsCnt, Point* inPts, uint32_t& curPt, uint32_t curCmd, State& state, float offset)
|
||||
{
|
||||
if (tvg::zero(inPts[curPt - 1] - inPts[curPt])) {
|
||||
++curPt;
|
||||
return;
|
||||
}
|
||||
|
||||
if (inCmds[curCmd - 1] != PathCommand::LineTo) state.line = _offset(inPts[curPt - 1], inPts[curPt], offset);
|
||||
state.line = _offset(inPts[curPt - 1], inPts[curPt], offset);
|
||||
|
||||
if (state.moveto) {
|
||||
out.cmds.push(PathCommand::MoveTo);
|
||||
|
@ -157,19 +159,16 @@ void LottieOffsetModifier::line(RenderPath& out, PathCommand* inCmds, uint32_t i
|
|||
return inCmds[cmd] == PathCommand::CubicTo && !tvg::zero(inPts[pt] - inPts[pt + 1]) && !tvg::zero(inPts[pt + 2] - inPts[pt + 3]);
|
||||
};
|
||||
|
||||
out.cmds.push(PathCommand::LineTo);
|
||||
|
||||
if (curCmd + 1 == inCmdsCnt || inCmds[curCmd + 1] == PathCommand::MoveTo || nonDegeneratedCubic(curCmd + 1, curPt + degenerated)) {
|
||||
out.pts.push(state.line.pt2);
|
||||
if (curCmd + 1 == inCmdsCnt || inCmds[curCmd + 1] == PathCommand::MoveTo || nonDegeneratedCubic(curCmd + 1, curPt + 1)) {
|
||||
_cubic(out.cmds, out.pts, out.pts.last(), state.line.pt2, state.line.pt2);
|
||||
++curPt;
|
||||
return;
|
||||
}
|
||||
|
||||
Line nextLine = state.firstLine;
|
||||
if (inCmds[curCmd + 1] == PathCommand::LineTo) nextLine = _offset(inPts[curPt + degenerated], inPts[curPt + 1 + degenerated], offset);
|
||||
else if (inCmds[curCmd + 1] == PathCommand::CubicTo) nextLine = _offset(inPts[curPt + 1 + degenerated], inPts[curPt + 2 + degenerated], offset);
|
||||
else if (inCmds[curCmd + 1] == PathCommand::Close && !_zero(inPts[curPt + degenerated], inPts[state.movetoInIndex + degenerated]))
|
||||
nextLine = _offset(inPts[curPt + degenerated], inPts[state.movetoInIndex + degenerated], offset);
|
||||
if (inCmds[curCmd + 1] == PathCommand::CubicTo) nextLine = _offset(inPts[curPt + 2], inPts[curPt + 3], offset);
|
||||
else if (inCmds[curCmd + 1] == PathCommand::Close && !_zero(inPts[curPt + 1], inPts[state.movetoInIndex + 1]))
|
||||
nextLine = _offset(inPts[curPt + 1], inPts[state.movetoInIndex + 1], offset);
|
||||
|
||||
corner(out, state.line, nextLine, state.movetoOutIndex, inCmds[curCmd + 1] == PathCommand::Close);
|
||||
|
||||
|
@ -210,15 +209,14 @@ bool LottieRoundnessModifier::modifyPath(PathCommand* inCmds, uint32_t inCmdsCnt
|
|||
break;
|
||||
} else if (inCmds[iCmds + 1] == PathCommand::Close) {
|
||||
_roundCorner(path.cmds, path.pts, prev, curr, inPts[2], r);
|
||||
if (tvg::zero(path.pts[startIndex] - path.pts[startIndex + 1])) path.pts[startIndex + 1] = path.pts.last();
|
||||
path.pts[startIndex] = path.pts.last();
|
||||
iPts += 3;
|
||||
break;
|
||||
}
|
||||
}
|
||||
path.cmds.push(PathCommand::CubicTo);
|
||||
path.pts.push(inPts[iPts++]);
|
||||
path.pts.push(inPts[iPts++]);
|
||||
path.pts.push(inPts[iPts++]);
|
||||
_cubic(path.cmds, path.pts, inPts[iPts], inPts[iPts + 1], inPts[iPts + 2]);
|
||||
iPts += 3;
|
||||
break;
|
||||
}
|
||||
case PathCommand::Close: {
|
||||
|
@ -272,14 +270,12 @@ bool LottieRoundnessModifier::modifyPolystar(RenderPath& in, RenderPath& out, fl
|
|||
auto p2 = curr - dNext;
|
||||
auto p3 = curr - 2.0f * dNext;
|
||||
|
||||
path.cmds.push(PathCommand::CubicTo);
|
||||
path.pts.push(prev); path.pts.push(p0); path.pts.push(p0);
|
||||
path.cmds.push(PathCommand::CubicTo);
|
||||
path.pts.push(p1); path.pts.push(p2); path.pts.push(p3);
|
||||
path.cmds.push(PathCommand::CubicTo);
|
||||
path.pts.push(p3); path.pts.push(next); path.pts.push(nextCtrl);
|
||||
_cubic(path.cmds, path.pts, prev, p0, p0);
|
||||
_cubic(path.cmds, path.pts, p1, p2, p3);
|
||||
_cubic(path.cmds, path.pts, p3, next, nextCtrl);
|
||||
}
|
||||
} else {
|
||||
//TODO - change for cubic instead of lines
|
||||
path.cmds.grow(2 * in.cmds.count);
|
||||
path.pts.grow(4 * in.cmds.count);
|
||||
|
||||
|
@ -298,10 +294,8 @@ bool LottieRoundnessModifier::modifyPolystar(RenderPath& in, RenderPath& out, fl
|
|||
auto p2 = curr - dNext;
|
||||
auto p3 = curr - 2.0f * dNext;
|
||||
|
||||
path.cmds.push(PathCommand::LineTo);
|
||||
path.pts.push(p0);
|
||||
path.cmds.push(PathCommand::CubicTo);
|
||||
path.pts.push(p1); path.pts.push(p2); path.pts.push(p3);
|
||||
_cubic(path.cmds, path.pts, path.pts.last(), p0, p0);
|
||||
_cubic(path.cmds, path.pts, p1, p2, p3);
|
||||
|
||||
dPrev = -1.0f * dNext;
|
||||
}
|
||||
|
@ -337,13 +331,11 @@ bool LottieOffsetModifier::modifyPath(PathCommand* inCmds, uint32_t inCmdsCnt, P
|
|||
if (inCmds[iCmd] == PathCommand::MoveTo) {
|
||||
state.moveto = true;
|
||||
state.movetoInIndex = iPt++;
|
||||
} else if (inCmds[iCmd] == PathCommand::LineTo) {
|
||||
line(out, inCmds, inCmdsCnt, inPts, iPt, iCmd, state, offset, false);
|
||||
} else if (inCmds[iCmd] == PathCommand::CubicTo) {
|
||||
//cubic degenerated to a line
|
||||
if (tvg::zero(inPts[iPt - 1] - inPts[iPt]) || tvg::zero(inPts[iPt + 1] - inPts[iPt + 2])) {
|
||||
++iPt;
|
||||
line(out, inCmds, inCmdsCnt, inPts, iPt, iCmd, state, offset, true);
|
||||
line(out, inCmds, inCmdsCnt, inPts, iPt, iCmd, state, offset);
|
||||
++iPt;
|
||||
continue;
|
||||
}
|
||||
|
@ -374,20 +366,16 @@ bool LottieOffsetModifier::modifyPath(PathCommand* inCmds, uint32_t inCmdsCnt, P
|
|||
}
|
||||
|
||||
bool inside{};
|
||||
Point intersect{};
|
||||
_intersect(line1, line2, intersect, inside);
|
||||
out.pts.push(intersect);
|
||||
_intersect(line2, line3, intersect, inside);
|
||||
out.pts.push(intersect);
|
||||
out.pts.push(line3.pt2);
|
||||
out.cmds.push(PathCommand::CubicTo);
|
||||
Point intersect1{}, intersect2{};
|
||||
_intersect(line1, line2, intersect1, inside);
|
||||
_intersect(line2, line3, intersect2, inside);
|
||||
_cubic(out.cmds, out.pts, intersect1, intersect2, line3.pt2);
|
||||
}
|
||||
|
||||
iPt += 3;
|
||||
}
|
||||
else {
|
||||
if (!_zero(inPts[iPt - 1], inPts[state.movetoInIndex])) {
|
||||
out.cmds.push(PathCommand::LineTo);
|
||||
corner(out, state.line, state.firstLine, state.movetoOutIndex, true);
|
||||
}
|
||||
out.cmds.push(PathCommand::Close);
|
||||
|
|
|
@ -102,7 +102,7 @@ private:
|
|||
uint32_t movetoInIndex = 0;
|
||||
};
|
||||
|
||||
void line(RenderPath& out, PathCommand* inCmds, uint32_t inCmdsCnt, Point* inPts, uint32_t& curPt, uint32_t curCmd, State& state, float offset, bool degenerated);
|
||||
void line(RenderPath& out, PathCommand* inCmds, uint32_t inCmdsCnt, Point* inPts, uint32_t& curPt, uint32_t curCmd, State& state, float offset);
|
||||
void corner(RenderPath& out, Line& line, Line& nextLine, uint32_t movetoIndex, bool nextClose);
|
||||
};
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue