mirror of
https://github.com/thorvg/thorvg.git
synced 2025-07-23 14:48:24 +00:00
renderer: refactored to share gl stroke dasher among engines
Move stroke dasher to the common code space to create an abillity to use it on the cross API renderers (wg and gl). Stroke dasher is a path-to-path operation, same as path trim, so can be placed to the common space. Now RenderShape can generate dashed path by itself. Dashing mechanics fully hiden from the user but can be used in gl and wg renderers. issue: https://github.com/thorvg/thorvg/issues/3557 Co-Authored-By: Hermet Park <hermet@lottiefiles.com>
This commit is contained in:
parent
d8bbb0df31
commit
371f69b845
5 changed files with 249 additions and 278 deletions
|
@ -28,18 +28,16 @@
|
|||
|
||||
bool GlGeometry::tesselate(const RenderShape& rshape, RenderUpdateFlag flag)
|
||||
{
|
||||
const RenderPath* path = nullptr;
|
||||
RenderPath trimmedPath;
|
||||
if (rshape.trimpath()) {
|
||||
if (!rshape.stroke->trim.trim(rshape.path, trimmedPath)) return true;
|
||||
path = &trimmedPath;
|
||||
} else path = &rshape.path;
|
||||
|
||||
if (flag & (RenderUpdateFlag::Color | RenderUpdateFlag::Gradient | RenderUpdateFlag::Transform | RenderUpdateFlag::Path)) {
|
||||
fill.clear();
|
||||
|
||||
BWTessellator bwTess{&fill};
|
||||
bwTess.tessellate(*path, matrix);
|
||||
if (rshape.trimpath()) {
|
||||
RenderPath trimmedPath;
|
||||
if (rshape.stroke->trim.trim(rshape.path, trimmedPath)) bwTess.tessellate(trimmedPath, matrix);
|
||||
else return true;
|
||||
} else bwTess.tessellate(rshape.path, matrix);
|
||||
|
||||
fillRule = rshape.rule;
|
||||
bounds = bwTess.bounds();
|
||||
}
|
||||
|
@ -48,7 +46,7 @@ bool GlGeometry::tesselate(const RenderShape& rshape, RenderUpdateFlag flag)
|
|||
stroke.clear();
|
||||
|
||||
Stroker stroker{&stroke, matrix};
|
||||
stroker.stroke(&rshape, *path);
|
||||
stroker.stroke(&rshape);
|
||||
bounds = stroker.bounds();
|
||||
}
|
||||
|
||||
|
|
|
@ -39,7 +39,7 @@ Stroker::Stroker(GlGeometryBuffer* buffer, const Matrix& matrix) : mBuffer(buffe
|
|||
}
|
||||
|
||||
|
||||
void Stroker::stroke(const RenderShape *rshape, const RenderPath& path)
|
||||
void Stroker::stroke(const RenderShape *rshape)
|
||||
{
|
||||
mMiterLimit = rshape->strokeMiterlimit();
|
||||
mStrokeCap = rshape->strokeCap();
|
||||
|
@ -52,9 +52,12 @@ void Stroker::stroke(const RenderShape *rshape, const RenderPath& path)
|
|||
mStrokeWidth = strokeWidth / mMatrix.e11;
|
||||
}
|
||||
|
||||
auto& dash = rshape->stroke->dash;
|
||||
if (dash.length < DASH_PATTERN_THRESHOLD) doStroke(path);
|
||||
else doDashStroke(path, dash.pattern, dash.count, dash.offset, dash.length);
|
||||
RenderPath dashed;
|
||||
if (rshape->strokeDash(dashed)) doStroke(dashed);
|
||||
else if (rshape->trimpath()) {
|
||||
RenderPath trimmedPath;
|
||||
if (rshape->stroke->trim.trim(rshape->path, trimmedPath)) doStroke(trimmedPath);
|
||||
} else doStroke(rshape->path);
|
||||
}
|
||||
|
||||
|
||||
|
@ -109,19 +112,6 @@ void Stroker::doStroke(const RenderPath& path)
|
|||
}
|
||||
|
||||
|
||||
void Stroker::doDashStroke(const RenderPath& path, const float *patterns, uint32_t patternCnt, float offset, float length)
|
||||
{
|
||||
RenderPath dpath;
|
||||
|
||||
dpath.cmds.reserve(20 * path.cmds.count);
|
||||
dpath.pts.reserve(20 * path.pts.count);
|
||||
|
||||
DashStroke dash(&dpath.cmds, &dpath.pts, patterns, patternCnt, offset, length);
|
||||
dash.doStroke(path, mStrokeCap != StrokeCap::Butt);
|
||||
doStroke(dpath);
|
||||
}
|
||||
|
||||
|
||||
void Stroker::strokeCap()
|
||||
{
|
||||
if (mStrokeCap == StrokeCap::Butt) return;
|
||||
|
@ -442,227 +432,6 @@ void Stroker::strokeRound(const Point& p, const Point& outDir)
|
|||
}
|
||||
|
||||
|
||||
DashStroke::DashStroke(Array<PathCommand> *cmds, Array<Point> *pts, const float *patterns, uint32_t patternCnt, float offset, float length)
|
||||
: mCmds(cmds),
|
||||
mPts(pts),
|
||||
mDashPattern(patterns),
|
||||
mDashCount(patternCnt),
|
||||
mDashOffset(offset),
|
||||
mDashLength(length)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void DashStroke::doStroke(const RenderPath& path, bool validPoint)
|
||||
{
|
||||
//validPoint: zero length segment with non-butt cap still should be rendered as a point - only the caps are visible
|
||||
int32_t idx = 0;
|
||||
auto offset = mDashOffset;
|
||||
bool gap = false;
|
||||
if (!tvg::zero(mDashOffset)) {
|
||||
auto length = (mDashCount % 2) ? mDashLength * 2 : mDashLength;
|
||||
offset = fmodf(offset, length);
|
||||
if (offset < 0) offset += length;
|
||||
|
||||
for (uint32_t i = 0; i < mDashCount * (mDashCount % 2 + 1); ++i, ++idx) {
|
||||
auto curPattern = mDashPattern[i % mDashCount];
|
||||
if (offset < curPattern) break;
|
||||
offset -= curPattern;
|
||||
gap = !gap;
|
||||
}
|
||||
idx = idx % mDashCount;
|
||||
}
|
||||
|
||||
auto pts = path.pts.data;
|
||||
ARRAY_FOREACH(cmd, path.cmds) {
|
||||
switch (*cmd) {
|
||||
case PathCommand::Close: {
|
||||
this->dashLineTo(mPtStart, validPoint);
|
||||
break;
|
||||
}
|
||||
case PathCommand::MoveTo: {
|
||||
// reset the dash state
|
||||
mCurrIdx = idx;
|
||||
mCurrLen = mDashPattern[idx] - offset;
|
||||
mCurOpGap = gap;
|
||||
mMove = true;
|
||||
mPtStart = mPtCur = *pts;
|
||||
pts++;
|
||||
break;
|
||||
}
|
||||
case PathCommand::LineTo: {
|
||||
this->dashLineTo(*pts, validPoint);
|
||||
pts++;
|
||||
break;
|
||||
}
|
||||
case PathCommand::CubicTo: {
|
||||
this->dashCubicTo(pts[0], pts[1], pts[2], validPoint);
|
||||
pts += 3;
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DashStroke::drawPoint(const Point& p)
|
||||
{
|
||||
if (mMove || mDashPattern[mCurrIdx] < FLOAT_EPSILON) {
|
||||
this->moveTo(p);
|
||||
mMove = false;
|
||||
}
|
||||
this->lineTo(p);
|
||||
}
|
||||
|
||||
|
||||
void DashStroke::dashLineTo(const Point& to, bool validPoint)
|
||||
{
|
||||
auto len = length(mPtCur - to);
|
||||
|
||||
if (tvg::zero(len)) {
|
||||
this->moveTo(mPtCur);
|
||||
} else if (len <= mCurrLen) {
|
||||
mCurrLen -= len;
|
||||
if (!mCurOpGap) {
|
||||
if (mMove) {
|
||||
this->moveTo(mPtCur);
|
||||
mMove = false;
|
||||
}
|
||||
this->lineTo(to);
|
||||
}
|
||||
} else {
|
||||
Line curr = {mPtCur, to};
|
||||
|
||||
while (len - mCurrLen > DASH_PATTERN_THRESHOLD) {
|
||||
Line right;
|
||||
if (mCurrLen > 0.0f) {
|
||||
Line left;
|
||||
curr.split(mCurrLen, left, right);
|
||||
len -= mCurrLen;
|
||||
if (!mCurOpGap) {
|
||||
if (mMove || mDashPattern[mCurrIdx] - mCurrLen < FLOAT_EPSILON) {
|
||||
this->moveTo(left.pt1);
|
||||
mMove = false;
|
||||
}
|
||||
this->lineTo(left.pt2);
|
||||
}
|
||||
} else {
|
||||
if (validPoint && !mCurOpGap) drawPoint(curr.pt1);
|
||||
right = curr;
|
||||
}
|
||||
mCurrIdx = (mCurrIdx + 1) % mDashCount;
|
||||
mCurrLen = mDashPattern[mCurrIdx];
|
||||
mCurOpGap = !mCurOpGap;
|
||||
curr = right;
|
||||
mPtCur = curr.pt1;
|
||||
mMove = true;
|
||||
}
|
||||
mCurrLen -= len;
|
||||
if (!mCurOpGap) {
|
||||
if (mMove) {
|
||||
this->moveTo(curr.pt1);
|
||||
mMove = false;
|
||||
}
|
||||
this->lineTo(curr.pt2);
|
||||
}
|
||||
|
||||
if (mCurrLen < 0.1f) {
|
||||
mCurrIdx = (mCurrIdx + 1) % mDashCount;
|
||||
mCurrLen = mDashPattern[mCurrIdx];
|
||||
mCurOpGap = !mCurOpGap;
|
||||
}
|
||||
}
|
||||
|
||||
mPtCur = to;
|
||||
}
|
||||
|
||||
|
||||
void DashStroke::dashCubicTo(const Point& cnt1, const Point& cnt2, const Point& end, bool validPoint)
|
||||
{
|
||||
Bezier cur{ mPtCur, cnt1, cnt2, end };
|
||||
|
||||
auto len = cur.length();
|
||||
|
||||
if (tvg::zero(len)) {
|
||||
this->moveTo(mPtCur);
|
||||
} else if (len <= mCurrLen) {
|
||||
mCurrLen -= len;
|
||||
if (!mCurOpGap) {
|
||||
if (mMove) {
|
||||
this->moveTo(mPtCur);
|
||||
mMove = false;
|
||||
}
|
||||
this->cubicTo(cnt1, cnt2, end);
|
||||
}
|
||||
} else {
|
||||
while (len - mCurrLen > DASH_PATTERN_THRESHOLD) {
|
||||
Bezier right;
|
||||
if (mCurrLen > 0.0f) {
|
||||
Bezier left;
|
||||
cur.split(mCurrLen, left, right);
|
||||
len -= mCurrLen;
|
||||
if (!mCurOpGap) {
|
||||
if (mMove || mDashPattern[mCurrIdx] - mCurrLen < FLOAT_EPSILON) {
|
||||
this->moveTo(left.start);
|
||||
mMove = false;
|
||||
}
|
||||
this->cubicTo(left.ctrl1, left.ctrl2, left.end);
|
||||
}
|
||||
} else {
|
||||
if (validPoint && !mCurOpGap) drawPoint(cur.start);
|
||||
right = cur;
|
||||
}
|
||||
mCurrIdx = (mCurrIdx + 1) % mDashCount;
|
||||
mCurrLen = mDashPattern[mCurrIdx];
|
||||
mCurOpGap = !mCurOpGap;
|
||||
cur = right;
|
||||
mPtCur = cur.start;
|
||||
mMove = true;
|
||||
}
|
||||
|
||||
mCurrLen -= len;
|
||||
if (!mCurOpGap) {
|
||||
if (mMove) {
|
||||
this->moveTo(cur.start);
|
||||
mMove = false;
|
||||
}
|
||||
this->cubicTo(cur.ctrl1, cur.ctrl2, cur.end);
|
||||
}
|
||||
|
||||
if (mCurrLen < 0.1f) {
|
||||
mCurrIdx = (mCurrIdx + 1) % mDashCount;
|
||||
mCurrLen = mDashPattern[mCurrIdx];
|
||||
mCurOpGap = !mCurOpGap;
|
||||
}
|
||||
}
|
||||
mPtCur = end;
|
||||
}
|
||||
|
||||
|
||||
void DashStroke::moveTo(const Point& pt)
|
||||
{
|
||||
mPts->push(pt);
|
||||
mCmds->push(PathCommand::MoveTo);
|
||||
}
|
||||
|
||||
|
||||
void DashStroke::lineTo(const Point& pt)
|
||||
{
|
||||
mPts->push(pt);
|
||||
mCmds->push(PathCommand::LineTo);
|
||||
}
|
||||
|
||||
|
||||
void DashStroke::cubicTo(const Point& cnt1, const Point& cnt2, const Point& end)
|
||||
{
|
||||
mPts->push(cnt1);
|
||||
mPts->push(cnt2);
|
||||
mPts->push(end);
|
||||
mCmds->push(PathCommand::CubicTo);
|
||||
}
|
||||
|
||||
|
||||
BWTessellator::BWTessellator(GlGeometryBuffer* buffer): mBuffer(buffer)
|
||||
{
|
||||
}
|
||||
|
|
|
@ -39,12 +39,11 @@ class Stroker
|
|||
};
|
||||
public:
|
||||
Stroker(GlGeometryBuffer* buffer, const Matrix& matrix);
|
||||
void stroke(const RenderShape *rshape, const RenderPath& path);
|
||||
void stroke(const RenderShape *rshape);
|
||||
RenderRegion bounds() const;
|
||||
|
||||
private:
|
||||
void doStroke(const RenderPath& path);
|
||||
void doDashStroke(const RenderPath& path, const float* patterns, uint32_t patternCnt, float offset, float length);
|
||||
|
||||
float strokeRadius() const
|
||||
{
|
||||
|
@ -75,34 +74,6 @@ private:
|
|||
Point mRightBottom = {0.0f, 0.0f};
|
||||
};
|
||||
|
||||
class DashStroke
|
||||
{
|
||||
public:
|
||||
DashStroke(Array<PathCommand>* cmds, Array<Point>* pts, const float* patterns, uint32_t patternCnt, float offset, float length);
|
||||
void doStroke(const RenderPath& path, bool drawPoint);
|
||||
|
||||
private:
|
||||
void drawPoint(const Point& p);
|
||||
void dashLineTo(const Point& pt, bool drawPoint);
|
||||
void dashCubicTo(const Point& pt1, const Point& pt2, const Point& pt3, bool drawPoint);
|
||||
void moveTo(const Point& pt);
|
||||
void lineTo(const Point& pt);
|
||||
void cubicTo(const Point& pt1, const Point& pt2, const Point& pt3);
|
||||
|
||||
Array<PathCommand>* mCmds;
|
||||
Array<Point>* mPts;
|
||||
const float* mDashPattern;
|
||||
uint32_t mDashCount;
|
||||
float mDashOffset;
|
||||
float mDashLength;
|
||||
float mCurrLen = 0.0f;
|
||||
int32_t mCurrIdx = 0;
|
||||
bool mCurOpGap = false;
|
||||
bool mMove = true;
|
||||
Point mPtStart = {};
|
||||
Point mPtCur = {};
|
||||
};
|
||||
|
||||
class BWTessellator
|
||||
{
|
||||
public:
|
||||
|
|
|
@ -587,4 +587,210 @@ bool RenderTrimPath::trim(const RenderPath& in, RenderPath& out) const
|
|||
}
|
||||
|
||||
return out.pts.count >= 2;
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
/* StrokeDashPath Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
//TODO: use this common function from all engines
|
||||
#ifdef THORVG_GL_RASTER_SUPPORT
|
||||
|
||||
struct StrokeDashPath
|
||||
{
|
||||
public:
|
||||
StrokeDashPath(RenderStroke::Dash dash) : dash(dash) {}
|
||||
bool gen(const RenderPath& in, RenderPath& out, bool drawPoint);
|
||||
|
||||
private:
|
||||
void lineTo(RenderPath& out, const Point& pt, bool drawPoint);
|
||||
void cubicTo(RenderPath& out, const Point& pt1, const Point& pt2, const Point& pt3, bool drawPoint);
|
||||
void point(RenderPath& out, const Point& p);
|
||||
|
||||
template<typename Segment, typename LengthFn, typename SplitFn, typename DrawFn, typename PointFn>
|
||||
void segment(Segment seg, float len, RenderPath& out, bool allowDot, LengthFn lengthFn, SplitFn splitFn, DrawFn drawFn, PointFn getStartPt, const Point& endPos);
|
||||
|
||||
RenderStroke::Dash dash;
|
||||
float curLen = 0.0f;
|
||||
int32_t curIdx = 0;
|
||||
Point curPos{};
|
||||
bool opGap = false;
|
||||
bool move = true;
|
||||
};
|
||||
|
||||
|
||||
template<typename Segment, typename LengthFn, typename SplitFn, typename DrawFn, typename PointFn>
|
||||
void StrokeDashPath::segment(Segment seg, float len, RenderPath& out, bool allowDot, LengthFn lengthFn, SplitFn splitFn, DrawFn drawFn, PointFn getStartPt, const Point& end)
|
||||
{
|
||||
#define MIN_CURR_LEN_THRESHOLD 0.1f
|
||||
|
||||
if (tvg::zero(len)) {
|
||||
out.moveTo(curPos);
|
||||
} else if (len <= curLen) {
|
||||
curLen -= len;
|
||||
if (!opGap) {
|
||||
if (move) {
|
||||
out.moveTo(curPos);
|
||||
move = false;
|
||||
}
|
||||
drawFn(seg);
|
||||
}
|
||||
} else {
|
||||
Segment left, right;
|
||||
while (len - curLen > DASH_PATTERN_THRESHOLD) {
|
||||
if (curLen > 0.0f) {
|
||||
splitFn(seg, curLen, left, right);
|
||||
len -= curLen;
|
||||
if (!opGap) {
|
||||
if (move || dash.pattern[curIdx] - curLen < FLOAT_EPSILON) {
|
||||
out.moveTo(getStartPt(left));
|
||||
move = false;
|
||||
}
|
||||
drawFn(left);
|
||||
}
|
||||
} else {
|
||||
if (allowDot && !opGap) point(out, getStartPt(seg));
|
||||
right = seg;
|
||||
}
|
||||
|
||||
curIdx = (curIdx + 1) % dash.count;
|
||||
curLen = dash.pattern[curIdx];
|
||||
opGap = !opGap;
|
||||
seg = right;
|
||||
curPos = getStartPt(seg);
|
||||
move = true;
|
||||
}
|
||||
curLen -= len;
|
||||
if (!opGap) {
|
||||
if (move) {
|
||||
out.moveTo(getStartPt(seg));
|
||||
move = false;
|
||||
}
|
||||
drawFn(seg);
|
||||
}
|
||||
if (curLen < MIN_CURR_LEN_THRESHOLD) {
|
||||
curIdx = (curIdx + 1) % dash.count;
|
||||
curLen = dash.pattern[curIdx];
|
||||
opGap = !opGap;
|
||||
}
|
||||
}
|
||||
curPos = end;
|
||||
}
|
||||
|
||||
|
||||
//allowDot: zero length segment with non-butt cap still should be rendered as a point - only the caps are visible
|
||||
bool StrokeDashPath::gen(const RenderPath& in, RenderPath& out, bool allowDot)
|
||||
{
|
||||
int32_t idx = 0;
|
||||
auto offset = dash.offset;
|
||||
auto gap = false;
|
||||
if (!tvg::zero(dash.offset)) {
|
||||
auto length = (dash.count % 2) ? dash.length * 2 : dash.length;
|
||||
offset = fmodf(offset, length);
|
||||
if (offset < 0) offset += length;
|
||||
|
||||
for (uint32_t i = 0; i < dash.count * (dash.count % 2 + 1); ++i, ++idx) {
|
||||
auto curPattern = dash.pattern[i % dash.count];
|
||||
if (offset < curPattern) break;
|
||||
offset -= curPattern;
|
||||
gap = !gap;
|
||||
}
|
||||
idx = idx % dash.count;
|
||||
}
|
||||
|
||||
auto pts = in.pts.data;
|
||||
Point start{};
|
||||
|
||||
ARRAY_FOREACH(cmd, in.cmds) {
|
||||
switch (*cmd) {
|
||||
case PathCommand::Close: {
|
||||
lineTo(out, start, allowDot);
|
||||
break;
|
||||
}
|
||||
case PathCommand::MoveTo: {
|
||||
// reset the dash state
|
||||
curIdx = idx;
|
||||
curLen = dash.pattern[idx] - offset;
|
||||
opGap = gap;
|
||||
move = true;
|
||||
start = curPos = *pts;
|
||||
pts++;
|
||||
break;
|
||||
}
|
||||
case PathCommand::LineTo: {
|
||||
lineTo(out, *pts, allowDot);
|
||||
pts++;
|
||||
break;
|
||||
}
|
||||
case PathCommand::CubicTo: {
|
||||
cubicTo(out, pts[0], pts[1], pts[2], allowDot);
|
||||
pts += 3;
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void StrokeDashPath::point(RenderPath& out, const Point& p)
|
||||
{
|
||||
if (move || dash.pattern[curIdx] < FLOAT_EPSILON) {
|
||||
out.moveTo(p);
|
||||
move = false;
|
||||
}
|
||||
out.lineTo(p);
|
||||
}
|
||||
|
||||
|
||||
void StrokeDashPath::lineTo(RenderPath& out, const Point& to, bool allowDot)
|
||||
{
|
||||
Line line = {curPos, to};
|
||||
auto len = length(to - curPos);
|
||||
segment<Line>(line, len, out, allowDot,
|
||||
[](const Line& l) { return length(l.pt2 - l.pt1); },
|
||||
[](const Line& l, float len, Line& left, Line& right) { l.split(len, left, right); },
|
||||
[&](const Line& l) { out.lineTo(l.pt2); },
|
||||
[](const Line& l) { return l.pt1; },
|
||||
to
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
void StrokeDashPath::cubicTo(RenderPath& out, const Point& cnt1, const Point& cnt2, const Point& end, bool allowDot)
|
||||
{
|
||||
Bezier curve = {curPos, cnt1, cnt2, end};
|
||||
auto len = curve.length();
|
||||
segment<Bezier>(curve, len, out, allowDot,
|
||||
[](const Bezier& b) { return b.length(); },
|
||||
[](const Bezier& b, float len, Bezier& left, Bezier& right) { b.split(len, left, right); },
|
||||
[&](const Bezier& b) { out.cubicTo(b.ctrl1, b.ctrl2, b.end); },
|
||||
[](const Bezier& b) { return b.start; },
|
||||
end
|
||||
);
|
||||
}
|
||||
#endif
|
||||
|
||||
bool RenderShape::strokeDash(RenderPath& out) const
|
||||
{
|
||||
if (!stroke || stroke->dash.count == 0 || stroke->dash.length < DASH_PATTERN_THRESHOLD) return false;
|
||||
|
||||
//TODO: use this common function from all engines
|
||||
#ifdef THORVG_GL_RASTER_SUPPORT
|
||||
out.cmds.reserve(20 * path.cmds.count);
|
||||
out.pts.reserve(20 * path.pts.count);
|
||||
|
||||
StrokeDashPath dash(stroke->dash);
|
||||
auto allowDot = stroke->cap != StrokeCap::Butt;
|
||||
|
||||
if (trimpath()) {
|
||||
RenderPath tpath;
|
||||
if (stroke->trim.trim(path, tpath)) return dash.gen(tpath, out, allowDot);
|
||||
else return false;
|
||||
}
|
||||
return dash.gen(path, out, allowDot);
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
|
@ -233,6 +233,31 @@ struct RenderPath
|
|||
cmds.clear();
|
||||
}
|
||||
|
||||
void close()
|
||||
{
|
||||
cmds.push(PathCommand::Close);
|
||||
}
|
||||
|
||||
void moveTo(const Point& pt)
|
||||
{
|
||||
pts.push(pt);
|
||||
cmds.push(PathCommand::MoveTo);
|
||||
}
|
||||
|
||||
void lineTo(const Point& pt)
|
||||
{
|
||||
pts.push(pt);
|
||||
cmds.push(PathCommand::LineTo);
|
||||
}
|
||||
|
||||
void cubicTo(const Point& cnt1, const Point& cnt2, const Point& end)
|
||||
{
|
||||
pts.push(cnt1);
|
||||
pts.push(cnt2);
|
||||
pts.push(end);
|
||||
cmds.push(PathCommand::CubicTo);
|
||||
}
|
||||
|
||||
bool bounds(Matrix* m, float* x, float* y, float* w, float* h);
|
||||
};
|
||||
|
||||
|
@ -256,7 +281,7 @@ struct RenderStroke
|
|||
float width = 0.0f;
|
||||
RenderColor color{};
|
||||
Fill *fill = nullptr;
|
||||
struct {
|
||||
struct Dash {
|
||||
float* pattern = nullptr;
|
||||
uint32_t count = 0;
|
||||
float offset = 0.0f;
|
||||
|
@ -383,6 +408,8 @@ struct RenderShape
|
|||
if (!stroke) return 4.0f;
|
||||
return stroke->miterlimit;;
|
||||
}
|
||||
|
||||
bool strokeDash(RenderPath& out) const;
|
||||
};
|
||||
|
||||
struct RenderEffect
|
||||
|
|
Loading…
Add table
Reference in a new issue