From a79f9788c187f072c21a358d95e1d0487f5fa706 Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Fri, 18 Jul 2025 17:24:05 +0900 Subject: [PATCH] common: code cleanup++ - Use RenderPath common interfaces instead of direct array manipulations. - Replace multiple scalar operations with Point utility operations where applicable. --- src/common/tvgMath.h | 8 +- src/loaders/lottie/tvgLottieModifier.cpp | 72 ++--- src/loaders/lottie/tvgLottieParser.cpp | 42 ++- src/loaders/svg/tvgSvgPath.cpp | 340 ++++++++--------------- src/loaders/svg/tvgSvgPath.h | 4 +- src/loaders/svg/tvgSvgSceneBuilder.cpp | 3 +- src/loaders/ttf/tvgTtfReader.cpp | 30 +- src/renderer/tvgRender.h | 2 + src/renderer/tvgShape.cpp | 11 +- src/renderer/tvgShape.h | 31 --- 10 files changed, 181 insertions(+), 362 deletions(-) diff --git a/src/common/tvgMath.h b/src/common/tvgMath.h index a6f4f763..27a603c6 100644 --- a/src/common/tvgMath.h +++ b/src/common/tvgMath.h @@ -189,7 +189,6 @@ Point operator*(const Point& pt, const Matrix& m); Point normal(const Point& p1, const Point& p2); void normalize(Point& pt); - static inline constexpr const Point operator*=(Point& pt, const Matrix* m) { if (m) pt *= *m; @@ -313,6 +312,13 @@ static inline Point operator*(const Point& lhs, const float rhs) } +static inline void operator*=(Point& lhs, const float rhs) +{ + lhs.x *= rhs; + lhs.y *= rhs; +} + + static inline Point operator*(const float& lhs, const Point& rhs) { return {lhs * rhs.x, lhs * rhs.y}; diff --git a/src/loaders/lottie/tvgLottieModifier.cpp b/src/loaders/lottie/tvgLottieModifier.cpp index de72fc98..2f68909b 100644 --- a/src/loaders/lottie/tvgLottieModifier.cpp +++ b/src/loaders/lottie/tvgLottieModifier.cpp @@ -33,22 +33,17 @@ static bool _colinear(const Point* p) } -static void _roundCorner(Array& cmds, Array& pts, Point& prev, Point& curr, Point& next, float r) +static void _roundCorner(RenderPath& out, Point& prev, Point& curr, Point& next, float r) { auto lenPrev = length(prev - curr); auto rPrev = lenPrev > 0.0f ? 0.5f * std::min(lenPrev * 0.5f, r) / lenPrev : 0.0f; auto lenNext = length(next - curr); auto rNext = lenNext > 0.0f ? 0.5f * std::min(lenNext * 0.5f, r) / 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); + out.lineTo(curr - 2.0f * dPrev); + out.cubicTo(curr - dPrev, curr - dNext, curr - 2.0f * dNext); } @@ -106,24 +101,14 @@ void LottieOffsetModifier::corner(RenderPath& out, Line& line, Line& nextLine, u } else { out.pts.push(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); + out.cubicTo((line.pt2 + intersect) * 0.5f, (nextLine.pt1 + intersect) * 0.5f, 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); - } else { - out.cmds.push(PathCommand::LineTo); - out.pts.push(nextLine.pt1); - } + if (1.0f <= miterLimit * fabsf(miterDirection.x * norm.x + miterDirection.y * norm.y)) out.lineTo(intersect); + out.lineTo(nextLine.pt1); + } else out.lineTo(nextLine.pt1); } } else out.pts.push(line.pt2); } @@ -139,9 +124,8 @@ void LottieOffsetModifier::line(RenderPath& out, PathCommand* inCmds, uint32_t i if (inCmds[curCmd - 1] != PathCommand::LineTo) state.line = _offset(inPts[curPt - 1], inPts[curPt], offset); if (state.moveto) { - out.cmds.push(PathCommand::MoveTo); + out.moveTo(state.line.pt1); state.movetoOutIndex = out.pts.count; - out.pts.push(state.line.pt1); state.firstLine = state.line; state.moveto = false; } @@ -179,7 +163,6 @@ bool LottieRoundnessModifier::modifyPath(PathCommand* inCmds, uint32_t inCmdsCnt buffer->clear(); auto& path = (next) ? *buffer : out; - path.cmds.reserve(inCmdsCnt * 2); path.pts.reserve((uint32_t)(inPtsCnt * 1.5)); auto pivot = path.pts.count; @@ -189,8 +172,7 @@ bool LottieRoundnessModifier::modifyPath(PathCommand* inCmds, uint32_t inCmdsCnt switch (inCmds[iCmds]) { case PathCommand::MoveTo: { startIndex = path.pts.count; - path.cmds.push(PathCommand::MoveTo); - path.pts.push(inPts[iPts++]); + path.moveTo(inPts[iPts++]); break; } case PathCommand::CubicTo: { @@ -198,24 +180,22 @@ bool LottieRoundnessModifier::modifyPath(PathCommand* inCmds, uint32_t inCmdsCnt auto& prev = inPts[iPts - 1]; auto& curr = inPts[iPts + 2]; if (inCmds[iCmds + 1] == PathCommand::CubicTo && _colinear(inPts + iPts + 2)) { - _roundCorner(path.cmds, path.pts, prev, curr, inPts[iPts + 5], r); + _roundCorner(path, prev, curr, inPts[iPts + 5], r); iPts += 3; break; } else if (inCmds[iCmds + 1] == PathCommand::Close) { - _roundCorner(path.cmds, path.pts, prev, curr, inPts[2], r); + _roundCorner(path, prev, curr, inPts[2], r); 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++]); + path.cubicTo(inPts[iPts], inPts[iPts + 1], inPts[iPts + 2]); + iPts += 3; break; } case PathCommand::Close: { - path.cmds.push(PathCommand::Close); + path.close(); break; } default: break; @@ -249,8 +229,7 @@ bool LottieRoundnessModifier::modifyPolystar(RenderPath& in, RenderPath& out, fl path.pts.grow((uint32_t)(4.5 * in.cmds.count)); int start = 3 * tvg::zero(outerRoundness); - path.cmds.push(PathCommand::MoveTo); - path.pts.push(in.pts[start]); + path.moveTo(in.pts[start]); for (uint32_t i = 1 + start; i < in.pts.count; i += 6) { auto& prev = in.pts[i]; @@ -265,12 +244,9 @@ 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); + path.cubicTo(prev, p0, p0); + path.cubicTo(p1, p2, p3); + path.cubicTo(p3, next, nextCtrl); } } else { path.cmds.grow(2 * in.cmds.count); @@ -278,8 +254,7 @@ bool LottieRoundnessModifier::modifyPolystar(RenderPath& in, RenderPath& out, fl auto dPrev = r * (in.pts[1] - in.pts[0]); auto p = in.pts[0] + 2.0f * dPrev; - path.cmds.push(PathCommand::MoveTo); - path.pts.push(p); + path.moveTo(p); for (uint32_t i = 1; i < in.pts.count; ++i) { auto& curr = in.pts[i]; @@ -291,10 +266,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); + path.lineTo(p0); + path.cubicTo(p1, p2, p3); dPrev = -1.0f * dNext; } @@ -359,9 +332,8 @@ bool LottieOffsetModifier::modifyPath(PathCommand* inCmds, uint32_t inCmdsCnt, P auto line3 = _offset(bezier.ctrl2, bezier.end, offset); if (state.moveto) { - out.cmds.push(PathCommand::MoveTo); + out.moveTo(line1.pt1); state.movetoOutIndex = out.pts.count; - out.pts.push(line1.pt1); state.firstLine = line1; state.moveto = false; } diff --git a/src/loaders/lottie/tvgLottieParser.cpp b/src/loaders/lottie/tvgLottieParser.cpp index 486a1e04..5b3812ae 100644 --- a/src/loaders/lottie/tvgLottieParser.cpp +++ b/src/loaders/lottie/tvgLottieParser.cpp @@ -181,44 +181,36 @@ bool LottieParser::getValue(PathSet& path) auto pt = pts.begin(); //Store manipulated results - Array outPts; - Array outCmds; + RenderPath temp; //Reuse the buffers - outPts.data = path.pts; - outPts.reserved = path.ptsCnt; - outCmds.data = path.cmds; - outCmds.reserved = path.cmdsCnt; + temp.pts.data = path.pts; + temp.pts.reserved = path.ptsCnt; + temp.cmds.data = path.cmds; + temp.cmds.reserved = path.cmdsCnt; size_t extra = closed ? 3 : 0; - outPts.reserve(pts.count * 3 + 1 + extra); - outCmds.reserve(pts.count + 2); + temp.pts.reserve(pts.count * 3 + 1 + extra); + temp.cmds.reserve(pts.count + 2); - outCmds.push(PathCommand::MoveTo); - outPts.push(*pt); + temp.moveTo(*pt); for (++pt, ++out, ++in; pt < pts.end(); ++pt, ++out, ++in) { - outCmds.push(PathCommand::CubicTo); - outPts.push(*(pt - 1) + *(out - 1)); - outPts.push(*pt + *in); - outPts.push(*pt); + temp.cubicTo(*(pt - 1) + *(out - 1), *pt + *in, *pt); } if (closed) { - outPts.push(pts.last() + outs.last()); - outPts.push(pts.first() + ins.first()); - outPts.push(pts.first()); - outCmds.push(PathCommand::CubicTo); - outCmds.push(PathCommand::Close); + temp.cubicTo(pts.last() + outs.last(), pts.first() + ins.first(), pts.first()); + temp.close(); } - path.pts = outPts.data; - path.cmds = outCmds.data; - path.ptsCnt = outPts.count; - path.cmdsCnt = outCmds.count; + path.pts = temp.pts.data; + path.cmds = temp.cmds.data; + path.ptsCnt = temp.pts.count; + path.cmdsCnt = temp.cmds.count; - outPts.data = nullptr; - outCmds.data = nullptr; + temp.pts.data = nullptr; + temp.cmds.data = nullptr; return false; } diff --git a/src/loaders/svg/tvgSvgPath.cpp b/src/loaders/svg/tvgSvgPath.cpp index d68460b5..9b36b61e 100644 --- a/src/loaders/svg/tvgSvgPath.cpp +++ b/src/loaders/svg/tvgSvgPath.cpp @@ -25,7 +25,6 @@ #include #include #include "tvgMath.h" -#include "tvgShape.h" #include "tvgSvgLoaderCommon.h" #include "tvgSvgPath.h" #include "tvgStr.h" @@ -69,108 +68,64 @@ static bool _parseFlag(char** content, int* number) } -void _pathAppendArcTo(Array* cmds, Array* pts, Point* cur, Point* curCtl, float x, float y, float rx, float ry, float angle, bool largeArc, bool sweep) +//Some helpful stuff is available here: +//http://www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes +void _pathAppendArcTo(RenderPath& out, Point& cur, Point& curCtl, const Point& next, Point radius, float angle, bool largeArc, bool sweep) { - float cxp, cyp, cx, cy; - float sx, sy; - float cosPhi, sinPhi; - float dx2, dy2; - float x1p, y1p; - float x1p2, y1p2; - float rx2, ry2; - float lambda; - float c; - float at; - float theta1, deltaTheta; - float nat; - float delta, bcp; - float cosPhiRx, cosPhiRy; - float sinPhiRx, sinPhiRy; - float cosTheta1, sinTheta1; - int segments; - - //Some helpful stuff is available here: - //http://www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes - sx = cur->x; - sy = cur->y; - - //Correction of out-of-range radii, see F6.6.1 (step 2) - rx = fabsf(rx); - ry = fabsf(ry); - - angle = deg2rad(angle); - cosPhi = cosf(angle); - sinPhi = sinf(angle); - dx2 = (sx - x) / 2.0f; - dy2 = (sy - y) / 2.0f; - x1p = cosPhi * dx2 + sinPhi * dy2; - y1p = cosPhi * dy2 - sinPhi * dx2; - x1p2 = x1p * x1p; - y1p2 = y1p * y1p; - rx2 = rx * rx; - ry2 = ry * ry; - lambda = (x1p2 / rx2) + (y1p2 / ry2); + auto start = cur; + auto cosPhi = cosf(angle); + auto sinPhi = sinf(angle); + auto d2 = (start - next) * 0.5f; + auto x1p = cosPhi * d2.x + sinPhi * d2.y; + auto y1p = cosPhi * d2.y - sinPhi * d2.x; + auto x1p2 = x1p * x1p; + auto y1p2 = y1p * y1p; + auto radius2 = Point{radius.x * radius.x, radius.y * radius.y}; + auto lambda = (x1p2 / radius2.x) + (y1p2 / radius2.y); //Correction of out-of-range radii, see F6.6.2 (step 4) if (lambda > 1.0f) { //See F6.6.3 - float lambdaRoot = sqrtf(lambda); - - rx *= lambdaRoot; - ry *= lambdaRoot; - //Update rx2 and ry2 - rx2 = rx * rx; - ry2 = ry * ry; + radius *= sqrtf(lambda); + radius2 = {radius.x * radius.x, radius.y * radius.y}; } - c = (rx2 * ry2) - (rx2 * y1p2) - (ry2 * x1p2); + Point cp, center; + auto c = (radius2.x * radius2.y) - (radius2.x * y1p2) - (radius2.y * x1p2); //Check if there is no possible solution //(i.e. we can't do a square root of a negative value) if (c < 0.0f) { //Scale uniformly until we have a single solution //(see F6.2) i.e. when c == 0.0 - float scale = sqrtf(1.0f - c / (rx2 * ry2)); - rx *= scale; - ry *= scale; - //Update rx2 and ry2 - rx2 = rx * rx; - ry2 = ry * ry; - + radius *= sqrtf(1.0f - c / (radius2.x * radius2.y)); + radius2 = {radius.x * radius.x, radius.y * radius.y}; //Step 2 (F6.5.2) - simplified since c == 0.0 - cxp = 0.0f; - cyp = 0.0f; + cp = {0.0f, 0.0f}; //Step 3 (F6.5.3 first part) - simplified since cxp and cyp == 0.0 - cx = 0.0f; - cy = 0.0f; + center = {0.0f, 0.0f}; } else { //Complete c calculation - c = sqrtf(c / ((rx2 * y1p2) + (ry2 * x1p2))); + c = sqrtf(c / ((radius2.x * y1p2) + (radius2.y * x1p2))); //Inverse sign if Fa == Fs if (largeArc == sweep) c = -c; - //Step 2 (F6.5.2) - cxp = c * (rx * y1p / ry); - cyp = c * (-ry * x1p / rx); - + cp = c * Point{(radius.x * y1p / radius.y), (-radius.y * x1p / radius.x)}; //Step 3 (F6.5.3 first part) - cx = cosPhi * cxp - sinPhi * cyp; - cy = sinPhi * cxp + cosPhi * cyp; + center = {cosPhi * cp.x - sinPhi * cp.y, sinPhi * cp.x + cosPhi * cp.y}; } //Step 3 (F6.5.3 second part) we now have the center point of the ellipse - cx += (sx + x) / 2.0f; - cy += (sy + y) / 2.0f; + center += (start + next) * 0.5f; //Step 4 (F6.5.4) //We dont' use arccos (as per w3c doc), see //http://www.euclideanspace.com/maths/algebra/vectors/angleBetween/index.htm //Note: atan2 (0.0, 1.0) == 0.0 - at = tvg::atan2(((y1p - cyp) / ry), ((x1p - cxp) / rx)); - theta1 = (at < 0.0f) ? 2.0f * MATH_PI + at : at; - - nat = tvg::atan2(((-y1p - cyp) / ry), ((-x1p - cxp) / rx)); - deltaTheta = (nat < at) ? 2.0f * MATH_PI - at + nat : nat - at; + auto at = tvg::atan2(((y1p - cp.y) / radius.y), ((x1p - cp.x) / radius.x)); + auto theta1 = (at < 0.0f) ? 2.0f * MATH_PI + at : at; + auto nat = tvg::atan2(((-y1p - cp.y) / radius.y), ((-x1p - cp.x) / radius.x)); + auto deltaTheta = (nat < at) ? 2.0f * MATH_PI - at + nat : nat - at; if (sweep) { //Ensure delta theta < 0 or else add 360 degrees @@ -184,52 +139,35 @@ void _pathAppendArcTo(Array* cmds, Array* pts, Point* cur, P //(smaller than 90 degrees) //We add one extra segment because we want something //Smaller than 90deg (i.e. not 90 itself) - segments = static_cast(fabsf(deltaTheta / MATH_PI2) + 1.0f); - delta = deltaTheta / segments; + auto segments = int(fabsf(deltaTheta / MATH_PI2) + 1.0f); + auto delta = deltaTheta / segments; //http://www.stillhq.com/ctpfaq/2001/comp.text.pdf-faq-2001-04.txt (section 2.13) - bcp = 4.0f / 3.0f * (1.0f - cosf(delta / 2.0f)) / sinf(delta / 2.0f); - - cosPhiRx = cosPhi * rx; - cosPhiRy = cosPhi * ry; - sinPhiRx = sinPhi * rx; - sinPhiRy = sinPhi * ry; - - cosTheta1 = cosf(theta1); - sinTheta1 = sinf(theta1); + auto bcp = 4.0f / 3.0f * (1.0f - cosf(delta / 2.0f)) / sinf(delta / 2.0f); + auto cosPhiR = Point{cosPhi * radius.x, cosPhi * radius.y}; + auto sinPhiR = Point{sinPhi * radius.x, sinPhi * radius.y}; + auto cosTheta1 = cosf(theta1); + auto sinTheta1 = sinf(theta1); for (int i = 0; i < segments; ++i) { //End angle (for this segment) = current + delta - float c1x, c1y, ex, ey, c2x, c2y; - float theta2 = theta1 + delta; - float cosTheta2 = cosf(theta2); - float sinTheta2 = sinf(theta2); - Point p[3]; + auto theta2 = theta1 + delta; + auto cosTheta2 = cosf(theta2); + auto sinTheta2 = sinf(theta2); //First control point (based on start point sx,sy) - c1x = sx - bcp * (cosPhiRx * sinTheta1 + sinPhiRy * cosTheta1); - c1y = sy + bcp * (cosPhiRy * cosTheta1 - sinPhiRx * sinTheta1); + auto c1 = start + Point{-bcp * (cosPhiR.x * sinTheta1 + sinPhiR.y * cosTheta1), bcp * (cosPhiR.y * cosTheta1 - sinPhiR.x * sinTheta1)}; //End point (for this segment) - ex = cx + (cosPhiRx * cosTheta2 - sinPhiRy * sinTheta2); - ey = cy + (sinPhiRx * cosTheta2 + cosPhiRy * sinTheta2); + auto e = center + Point{cosPhiR.x * cosTheta2 - sinPhiR.y * sinTheta2, sinPhiR.x * cosTheta2 + cosPhiR.y * sinTheta2}; //Second control point (based on end point ex,ey) - c2x = ex + bcp * (cosPhiRx * sinTheta2 + sinPhiRy * cosTheta2); - c2y = ey + bcp * (sinPhiRx * sinTheta2 - cosPhiRy * cosTheta2); - cmds->push(PathCommand::CubicTo); - p[0] = {c1x, c1y}; - p[1] = {c2x, c2y}; - p[2] = {ex, ey}; - pts->push(p[0]); - pts->push(p[1]); - pts->push(p[2]); - *curCtl = p[1]; - *cur = p[2]; + curCtl = e + Point{bcp * (cosPhiR.x * sinTheta2 + sinPhiR.y * cosTheta2), bcp * (sinPhiR.x * sinTheta2 - cosPhiR.y * cosTheta2)}; + cur = e; + out.cubicTo(c1, curCtl, cur); //Next start point is the current end point (same for angle) - sx = ex; - sy = ey; + start = e; theta1 = theta2; //Avoid recomputations cosTheta1 = cosTheta2; @@ -237,6 +175,7 @@ void _pathAppendArcTo(Array* cmds, Array* pts, Point* cur, P } } + static int _numberCount(char cmd) { int count = 0; @@ -276,14 +215,13 @@ static int _numberCount(char cmd) count = 7; break; } - default: - break; + default: break; } return count; } -static bool _processCommand(Array* cmds, Array* pts, char cmd, float* arr, int count, Point* cur, Point* curCtl, Point* startPoint, bool *isQuadratic, bool* closed) +static bool _processCommand(RenderPath& out, char cmd, float* arr, int count, Point& cur, Point& curCtl, Point& start, bool& quadratic, bool& closed) { switch (cmd) { case 'm': @@ -293,169 +231,120 @@ static bool _processCommand(Array* cmds, Array* pts, char cm case 'q': case 't': { for (int i = 0; i < count - 1; i += 2) { - arr[i] = arr[i] + cur->x; - arr[i + 1] = arr[i + 1] + cur->y; + arr[i] = arr[i] + cur.x; + arr[i + 1] = arr[i + 1] + cur.y; } break; } case 'h': { - arr[0] = arr[0] + cur->x; + arr[0] = arr[0] + cur.x; break; } case 'v': { - arr[0] = arr[0] + cur->y; + arr[0] = arr[0] + cur.y; break; } case 'a': { - arr[5] = arr[5] + cur->x; - arr[6] = arr[6] + cur->y; - break; - } - default: { + arr[5] = arr[5] + cur.x; + arr[6] = arr[6] + cur.y; break; } + default: break; } switch (cmd) { case 'm': case 'M': { - Point p = {arr[0], arr[1]}; - cmds->push(PathCommand::MoveTo); - pts->push(p); - *cur = {arr[0], arr[1]}; - *startPoint = {arr[0], arr[1]}; + start = cur = {arr[0], arr[1]}; + out.moveTo(cur); break; } case 'l': case 'L': { - Point p = {arr[0], arr[1]}; - cmds->push(PathCommand::LineTo); - pts->push(p); - *cur = {arr[0], arr[1]}; + cur = {arr[0], arr[1]}; + out.lineTo(cur); break; } case 'c': case 'C': { - Point p[3]; - cmds->push(PathCommand::CubicTo); - p[0] = {arr[0], arr[1]}; - p[1] = {arr[2], arr[3]}; - p[2] = {arr[4], arr[5]}; - pts->push(p[0]); - pts->push(p[1]); - pts->push(p[2]); - *curCtl = p[1]; - *cur = p[2]; - *isQuadratic = false; + curCtl = {arr[2], arr[3]}; + cur = {arr[4], arr[5]}; + out.cubicTo({arr[0], arr[1]}, curCtl, cur); + quadratic = false; break; } case 's': case 'S': { - Point p[3], ctrl; - if ((cmds->count > 1) && (cmds->last() == PathCommand::CubicTo) && - !(*isQuadratic)) { - ctrl.x = 2 * cur->x - curCtl->x; - ctrl.y = 2 * cur->y - curCtl->y; + Point ctrl; + if ((out.cmds.count > 1) && (out.cmds.last() == PathCommand::CubicTo) && !quadratic) { + ctrl = 2 * cur - curCtl; } else { - ctrl = *cur; + ctrl = cur; } - cmds->push(PathCommand::CubicTo); - p[0] = ctrl; - p[1] = {arr[0], arr[1]}; - p[2] = {arr[2], arr[3]}; - pts->push(p[0]); - pts->push(p[1]); - pts->push(p[2]); - *curCtl = p[1]; - *cur = p[2]; - *isQuadratic = false; + curCtl = {arr[0], arr[1]}; + cur = {arr[2], arr[3]}; + out.cubicTo(ctrl, curCtl, cur); + quadratic = false; break; } case 'q': case 'Q': { - Point p[3]; - float ctrl_x0 = (cur->x + 2 * arr[0]) * (1.0f / 3.0f); - float ctrl_y0 = (cur->y + 2 * arr[1]) * (1.0f / 3.0f); - float ctrl_x1 = (arr[2] + 2 * arr[0]) * (1.0f / 3.0f); - float ctrl_y1 = (arr[3] + 2 * arr[1]) * (1.0f / 3.0f); - cmds->push(PathCommand::CubicTo); - p[0] = {ctrl_x0, ctrl_y0}; - p[1] = {ctrl_x1, ctrl_y1}; - p[2] = {arr[2], arr[3]}; - pts->push(p[0]); - pts->push(p[1]); - pts->push(p[2]); - *curCtl = {arr[0], arr[1]}; - *cur = p[2]; - *isQuadratic = true; + auto ctrl1 = (cur + 2 * Point{arr[0], arr[1]}) * (1.0f / 3.0f); + auto ctrl2 = (Point{arr[2], arr[3]} + 2 * Point{arr[0], arr[1]}) * (1.0f / 3.0f); + curCtl = {arr[0], arr[1]}; + cur = {arr[2], arr[3]}; + out.cubicTo(ctrl1, ctrl2, cur); + quadratic = true; break; } case 't': case 'T': { - Point p[3], ctrl; - if ((cmds->count > 1) && (cmds->last() == PathCommand::CubicTo) && - *isQuadratic) { - ctrl.x = 2 * cur->x - curCtl->x; - ctrl.y = 2 * cur->y - curCtl->y; + Point ctrl; + if ((out.cmds.count > 1) && (out.cmds.last() == PathCommand::CubicTo) && quadratic) { + ctrl = 2 * cur - curCtl; } else { - ctrl = *cur; + ctrl = cur; } - float ctrl_x0 = (cur->x + 2 * ctrl.x) * (1.0f / 3.0f); - float ctrl_y0 = (cur->y + 2 * ctrl.y) * (1.0f / 3.0f); - float ctrl_x1 = (arr[0] + 2 * ctrl.x) * (1.0f / 3.0f); - float ctrl_y1 = (arr[1] + 2 * ctrl.y) * (1.0f / 3.0f); - cmds->push(PathCommand::CubicTo); - p[0] = {ctrl_x0, ctrl_y0}; - p[1] = {ctrl_x1, ctrl_y1}; - p[2] = {arr[0], arr[1]}; - pts->push(p[0]); - pts->push(p[1]); - pts->push(p[2]); - *curCtl = {ctrl.x, ctrl.y}; - *cur = p[2]; - *isQuadratic = true; + auto ctrl1 = (cur + 2 * ctrl) * (1.0f / 3.0f); + auto ctrl2 = (Point{arr[0], arr[1]} + 2 * ctrl) * (1.0f / 3.0f); + curCtl = {ctrl.x, ctrl.y}; + cur = {arr[0], arr[1]}; + out.cubicTo(ctrl1, ctrl2, cur); + quadratic = true; break; } case 'h': case 'H': { - Point p = {arr[0], cur->y}; - cmds->push(PathCommand::LineTo); - pts->push(p); - cur->x = arr[0]; + out.lineTo({arr[0], cur.y}); + cur.x = arr[0]; break; } case 'v': case 'V': { - Point p = {cur->x, arr[0]}; - cmds->push(PathCommand::LineTo); - pts->push(p); - cur->y = arr[0]; + out.lineTo({cur.x, arr[0]}); + cur.y = arr[0]; break; } case 'z': case 'Z': { - cmds->push(PathCommand::Close); - *cur = *startPoint; - *closed = true; + out.close(); + cur = start; + closed = true; break; } case 'a': case 'A': { if (tvg::zero(arr[0]) || tvg::zero(arr[1])) { - Point p = {arr[5], arr[6]}; - cmds->push(PathCommand::LineTo); - pts->push(p); - *cur = {arr[5], arr[6]}; - } else if (!tvg::equal(cur->x, arr[5]) || !tvg::equal(cur->y, arr[6])) { - _pathAppendArcTo(cmds, pts, cur, curCtl, arr[5], arr[6], fabsf(arr[0]), fabsf(arr[1]), arr[2], arr[3], arr[4]); - *cur = *curCtl = {arr[5], arr[6]}; - *isQuadratic = false; + cur = {arr[5], arr[6]}; + out.lineTo(cur); + } else if (!tvg::equal(cur.x, arr[5]) || !tvg::equal(cur.y, arr[6])) { + _pathAppendArcTo(out, cur, curCtl, {arr[5], arr[6]}, {fabsf(arr[0]), fabsf(arr[1])}, deg2rad(arr[2]), arr[3], arr[4]); + cur = curCtl = {arr[5], arr[6]}; + quadratic = false; } break; } - default: { - return false; - } + default: return false; } return true; } @@ -497,12 +386,12 @@ static char* _nextCommand(char* path, char* cmd, float* arr, int* count, bool* c } } *count = 0; - return NULL; + return nullptr; } for (int i = 0; i < *count; i++) { if (!_parseNumber(&path, &arr[i])) { *count = 0; - return NULL; + return nullptr; } path = _skipComma(path); } @@ -515,29 +404,26 @@ static char* _nextCommand(char* path, char* cmd, float* arr, int* count, bool* c /************************************************************************/ -bool svgPathToShape(const char* svgPath, Shape* shape) +bool svgPathToShape(const char* svgPath, RenderPath& out) { float numberArray[7]; int numberCount = 0; - Point cur = { 0, 0 }; - Point curCtl = { 0, 0 }; - Point startPoint = { 0, 0 }; + Point cur = {0, 0}; + Point curCtl = {0, 0}; + Point start = {0, 0}; char cmd = 0; - bool isQuadratic = false; - bool closed = false; - char* path = (char*)svgPath; - - auto& pts = SHAPE(shape)->rs.path.pts; - auto& cmds = SHAPE(shape)->rs.path.cmds; - auto lastCmds = cmds.count; + auto path = (char*)svgPath; + auto lastCmds = out.cmds.count; + auto isQuadratic = false; + auto closed = false; while ((path[0] != '\0')) { path = _nextCommand(path, &cmd, numberArray, &numberCount, &closed); if (!path) break; closed = false; - if (!_processCommand(&cmds, &pts, cmd, numberArray, numberCount, &cur, &curCtl, &startPoint, &isQuadratic, &closed)) break; + if (!_processCommand(out, cmd, numberArray, numberCount, cur, curCtl, start, isQuadratic, closed)) break; } - if (cmds.count > lastCmds && cmds[lastCmds] != PathCommand::MoveTo) return false; + if (out.cmds.count > lastCmds && out.cmds[lastCmds] != PathCommand::MoveTo) return false; return true; } diff --git a/src/loaders/svg/tvgSvgPath.h b/src/loaders/svg/tvgSvgPath.h index 63e0bc8e..a8a0bae7 100644 --- a/src/loaders/svg/tvgSvgPath.h +++ b/src/loaders/svg/tvgSvgPath.h @@ -23,8 +23,8 @@ #ifndef _TVG_SVG_PATH_H_ #define _TVG_SVG_PATH_H_ -#include +#include "tvgRender.h" -bool svgPathToShape(const char* svgPath, Shape* shape); +bool svgPathToShape(const char* svgPath, RenderPath& out); #endif //_TVG_SVG_PATH_H_ diff --git a/src/loaders/svg/tvgSvgSceneBuilder.cpp b/src/loaders/svg/tvgSvgSceneBuilder.cpp index 40c4a4db..87efa46e 100644 --- a/src/loaders/svg/tvgSvgSceneBuilder.cpp +++ b/src/loaders/svg/tvgSvgSceneBuilder.cpp @@ -25,6 +25,7 @@ #include "tvgCompressor.h" #include "tvgFill.h" #include "tvgStr.h" +#include "tvgShape.h" #include "tvgSvgLoaderCommon.h" #include "tvgSvgSceneBuilder.h" #include "tvgSvgPath.h" @@ -454,7 +455,7 @@ static bool _recognizeShape(SvgNode* node, Shape* shape) switch (node->type) { case SvgNodeType::Path: { if (node->node.path.path) { - if (!svgPathToShape(node->node.path.path, shape)) { + if (!svgPathToShape(node->node.path.path, SHAPE(shape)->rs.path)) { TVGERR("SVG", "Invalid path information."); return false; } diff --git a/src/loaders/ttf/tvgTtfReader.cpp b/src/loaders/ttf/tvgTtfReader.cpp index 97c43150..3d75a477 100644 --- a/src/loaders/ttf/tvgTtfReader.cpp +++ b/src/loaders/ttf/tvgTtfReader.cpp @@ -474,10 +474,9 @@ bool TtfReader::convert(Shape* shape, TtfGlyphMetrics& gmetrics, const Point& of if (!this->points(outline, flags, pts, ptsCnt, offset + kerning)) return false; //generate tvg paths. - auto& pathCmds = SHAPE(shape)->rs.path.cmds; - auto& pathPts = SHAPE(shape)->rs.path.pts; - pathCmds.reserve(ptsCnt); - pathPts.reserve(ptsCnt); + auto& path = SHAPE(shape)->rs.path; + path.cmds.reserve(ptsCnt); + path.pts.reserve(ptsCnt); uint32_t begin = 0; @@ -485,42 +484,31 @@ bool TtfReader::convert(Shape* shape, TtfGlyphMetrics& gmetrics, const Point& of //contour must start with move to bool offCurve = !(flags[begin] & ON_CURVE); Point ptsBegin = offCurve ? (pts[begin] + pts[endPts[i]]) * 0.5f : pts[begin]; - pathCmds.push(PathCommand::MoveTo); - pathPts.push(ptsBegin); + path.moveTo(ptsBegin); auto cnt = endPts[i] - begin + 1; for (uint32_t x = 1; x < cnt; ++x) { if (flags[begin + x] & ON_CURVE) { if (offCurve) { - pathCmds.push(PathCommand::CubicTo); - pathPts.push(pathPts.last() + (2.0f/3.0f) * (pts[begin + x - 1] - pathPts.last())); - pathPts.push(pts[begin + x] + (2.0f/3.0f) * (pts[begin + x - 1] - pts[begin + x])); - pathPts.push(pts[begin + x]); + path.cubicTo(path.pts.last() + (2.0f/3.0f) * (pts[begin + x - 1] - path.pts.last()), pts[begin + x] + (2.0f/3.0f) * (pts[begin + x - 1] - pts[begin + x]), pts[begin + x]); offCurve = false; } else { - pathCmds.push(PathCommand::LineTo); - pathPts.push(pts[begin + x]); + path.lineTo(pts[begin + x]); } } else { if (offCurve) { - pathCmds.push(PathCommand::CubicTo); auto end = (pts[begin + x] + pts[begin + x - 1]) * 0.5f; - pathPts.push(pathPts.last() + (2.0f/3.0f) * (pts[begin + x - 1] - pathPts.last())); - pathPts.push(end + (2.0f/3.0f) * (pts[begin + x - 1] - end)); - pathPts.push(end); + path.cubicTo(path.pts.last() + (2.0f/3.0f) * (pts[begin + x - 1] - path.pts.last()), end + (2.0f/3.0f) * (pts[begin + x - 1] - end), end); } else { offCurve = true; } } } if (offCurve) { - pathCmds.push(PathCommand::CubicTo); - pathPts.push(pathPts.last() + (2.0f/3.0f) * (pts[begin + cnt - 1] - pathPts.last())); - pathPts.push(ptsBegin + (2.0f/3.0f) * (pts[begin + cnt - 1] - ptsBegin)); - pathPts.push(ptsBegin); + path.cubicTo(path.pts.last() + (2.0f/3.0f) * (pts[begin + cnt - 1] - path.pts.last()), ptsBegin + (2.0f/3.0f) * (pts[begin + cnt - 1] - ptsBegin), ptsBegin); } //contour must end with close - pathCmds.push(PathCommand::Close); + path.close(); begin = endPts[i] + 1; } return true; diff --git a/src/renderer/tvgRender.h b/src/renderer/tvgRender.h index ee9c1cd8..737dda07 100644 --- a/src/renderer/tvgRender.h +++ b/src/renderer/tvgRender.h @@ -235,6 +235,8 @@ struct RenderPath void close() { + //Don't close multiple times. + if (cmds.count > 0 && cmds.last() == PathCommand::Close) return; cmds.push(PathCommand::Close); } diff --git a/src/renderer/tvgShape.cpp b/src/renderer/tvgShape.cpp index aacebd27..8f01d641 100644 --- a/src/renderer/tvgShape.cpp +++ b/src/renderer/tvgShape.cpp @@ -66,28 +66,31 @@ Result Shape::appendPath(const PathCommand *cmds, uint32_t cmdCnt, const Point* Result Shape::moveTo(float x, float y) noexcept { - SHAPE(this)->moveTo(x, y); + SHAPE(this)->rs.path.moveTo({x, y}); return Result::Success; } Result Shape::lineTo(float x, float y) noexcept { - SHAPE(this)->lineTo(x, y); + SHAPE(this)->rs.path.lineTo({x, y}); + SHAPE(this)->impl.mark(RenderUpdateFlag::Path); return Result::Success; } Result Shape::cubicTo(float cx1, float cy1, float cx2, float cy2, float x, float y) noexcept { - SHAPE(this)->cubicTo(cx1, cy1, cx2, cy2, x, y); + SHAPE(this)->rs.path.cubicTo({cx1, cy1}, {cx2, cy2}, {x, y}); + SHAPE(this)->impl.mark(RenderUpdateFlag::Path); return Result::Success; } Result Shape::close() noexcept { - SHAPE(this)->close(); + SHAPE(this)->rs.path.close(); + SHAPE(this)->impl.mark(RenderUpdateFlag::Path); return Result::Success; } diff --git a/src/renderer/tvgShape.h b/src/renderer/tvgShape.h index ceb17851..58ee4d01 100644 --- a/src/renderer/tvgShape.h +++ b/src/renderer/tvgShape.h @@ -170,37 +170,6 @@ struct ShapeImpl : Shape rs.path.pts.count += ptsCnt; } - void moveTo(float x, float y) - { - rs.path.cmds.push(PathCommand::MoveTo); - rs.path.pts.push({x, y}); - } - - void lineTo(float x, float y) - { - rs.path.cmds.push(PathCommand::LineTo); - rs.path.pts.push({x, y}); - impl.mark(RenderUpdateFlag::Path); - } - - void cubicTo(float cx1, float cy1, float cx2, float cy2, float x, float y) - { - rs.path.cmds.push(PathCommand::CubicTo); - rs.path.pts.push({cx1, cy1}); - rs.path.pts.push({cx2, cy2}); - rs.path.pts.push({x, y}); - - impl.mark(RenderUpdateFlag::Path); - } - - void close() - { - //Don't close multiple times. - if (rs.path.cmds.count > 0 && rs.path.cmds.last() == PathCommand::Close) return; - rs.path.cmds.push(PathCommand::Close); - impl.mark(RenderUpdateFlag::Path); - } - void strokeWidth(float width) { if (!rs.stroke) rs.stroke = new RenderStroke();