mirror of
https://github.com/thorvg/thorvg.git
synced 2025-07-23 14:48:24 +00:00
common: share stroke dasher
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
This commit is contained in:
parent
10b68f4936
commit
235ed47d9d
5 changed files with 291 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:
|
||||
|
|
|
@ -590,4 +590,277 @@ bool RenderTrimPath::trim(const RenderPath& in, RenderPath& out) const
|
|||
}
|
||||
|
||||
return out.pts.count >= 2;
|
||||
}
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
/* RenderDashPath Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
class RenderDashPath
|
||||
{
|
||||
public:
|
||||
RenderDashPath(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 = {};
|
||||
};
|
||||
|
||||
|
||||
RenderDashPath::RenderDashPath(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 RenderDashPath::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 RenderDashPath::drawPoint(const Point& p)
|
||||
{
|
||||
if (mMove || mDashPattern[mCurrIdx] < FLOAT_EPSILON) {
|
||||
this->moveTo(p);
|
||||
mMove = false;
|
||||
}
|
||||
this->lineTo(p);
|
||||
}
|
||||
|
||||
|
||||
void RenderDashPath::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 RenderDashPath::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 RenderDashPath::moveTo(const Point& pt)
|
||||
{
|
||||
mPts->push(pt);
|
||||
mCmds->push(PathCommand::MoveTo);
|
||||
}
|
||||
|
||||
|
||||
void RenderDashPath::lineTo(const Point& pt)
|
||||
{
|
||||
mPts->push(pt);
|
||||
mCmds->push(PathCommand::LineTo);
|
||||
}
|
||||
|
||||
|
||||
void RenderDashPath::cubicTo(const Point& cnt1, const Point& cnt2, const Point& end)
|
||||
{
|
||||
mPts->push(cnt1);
|
||||
mPts->push(cnt2);
|
||||
mPts->push(end);
|
||||
mCmds->push(PathCommand::CubicTo);
|
||||
}
|
||||
|
||||
|
||||
bool RenderShape::strokeDash(RenderPath& out) const
|
||||
{
|
||||
if (!stroke) return false;
|
||||
if (!stroke->dash.pattern) return false;
|
||||
if (stroke->dash.count == 0) return false;
|
||||
if (stroke->dash.length < DASH_PATTERN_THRESHOLD) return false;
|
||||
|
||||
out.cmds.reserve(20 * path.cmds.count);
|
||||
out.pts.reserve(20 * path.pts.count);
|
||||
|
||||
RenderDashPath dash(&out.cmds, &out.pts, stroke->dash.pattern, stroke->dash.count, stroke->dash.offset, stroke->dash.length);
|
||||
if (trimpath()) {
|
||||
RenderPath trimmedPath;
|
||||
if (stroke->trim.trim(path, trimmedPath)) dash.doStroke(trimmedPath, stroke->cap != StrokeCap::Butt);
|
||||
else return false;
|
||||
} else dash.doStroke(path, stroke->cap != StrokeCap::Butt);
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -364,6 +364,8 @@ struct RenderShape
|
|||
return stroke->dash.count;
|
||||
}
|
||||
|
||||
bool strokeDash(RenderPath& out) const;
|
||||
|
||||
StrokeCap strokeCap() const
|
||||
{
|
||||
if (!stroke) return StrokeCap::Square;
|
||||
|
|
Loading…
Add table
Reference in a new issue