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