mirror of
https://github.com/thorvg/thorvg.git
synced 2025-06-08 05:33:36 +00:00
Merge d71e11495d
into 0fa5d41c8d
This commit is contained in:
commit
d0ab17e1eb
11 changed files with 123 additions and 66 deletions
11
inc/thorvg.h
11
inc/thorvg.h
|
@ -1097,14 +1097,17 @@ public:
|
||||||
/**
|
/**
|
||||||
* @brief Sets the dash pattern of the stroke.
|
* @brief Sets the dash pattern of the stroke.
|
||||||
*
|
*
|
||||||
* @param[in] dashPattern The array of consecutive pair values of the dash length and the gap length.
|
* @param[in] dashPattern An array of alternating dash and gap lengths.
|
||||||
* @param[in] cnt The length of the @p dashPattern array.
|
* @param[in] cnt The length of the @p dashPattern array.
|
||||||
* @param[in] offset The shift of the starting point within the repeating dash pattern from which the path's dashing begins.
|
* @param[in] offset The shift of the starting point within the repeating dash pattern, from which the pattern begins to be applied.
|
||||||
*
|
*
|
||||||
* @retval Result::InvalidArguments In case @p dashPattern is @c nullptr and @p cnt > 0, @p cnt is zero, any of the dash pattern values is zero or less.
|
* @retval Result::InvalidArguments In case @p dashPattern is @c nullptr and @p cnt > 0 or @p dashPattern is not @c nullptr and @p cnt is zero.
|
||||||
*
|
*
|
||||||
* @note To reset the stroke dash pattern, pass @c nullptr to @p dashPattern and zero to @p cnt.
|
* @note To reset the stroke dash pattern, pass @c nullptr to @p dashPattern and zero to @p cnt.
|
||||||
* @warning @p cnt must be greater than 1 if the dash pattern is valid.
|
* @note Values of @p dashPattern less than zero are treated as zero.
|
||||||
|
* @note If all values in the @p dashPattern are equal to or less than 0, the dash is ignored.
|
||||||
|
* @note If the @p dashPattern contains an odd number of elements, the sequence is repeated in the same
|
||||||
|
* order to form an even-length pattern, preserving the alternation of dashes and gaps.
|
||||||
*
|
*
|
||||||
* @since 1.0
|
* @since 1.0
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1350,14 +1350,18 @@ TVG_API Tvg_Result tvg_shape_get_stroke_gradient(const Tvg_Paint* paint, Tvg_Gra
|
||||||
* @brief Sets the shape's stroke dash pattern.
|
* @brief Sets the shape's stroke dash pattern.
|
||||||
*
|
*
|
||||||
* @param[in] paint A Tvg_Paint pointer to the shape object.
|
* @param[in] paint A Tvg_Paint pointer to the shape object.
|
||||||
* @param[in] dashPattern The array of consecutive pair values of the dash length and the gap length.
|
* @param[in] dashPattern An array of alternating dash and gap lengths.
|
||||||
* @param[in] cnt The size of the @p dashPattern array.
|
* @param[in] cnt The size of the @p dashPattern array.
|
||||||
* @param[in] offset The shift of the starting point within the repeating dash pattern from which the path's dashing begins.
|
* @param[in] offset The shift of the starting point within the repeating dash pattern, from which the pattern begins to be applied.
|
||||||
*
|
*
|
||||||
* @return Tvg_Result enumeration.
|
* @return Tvg_Result enumeration.
|
||||||
* @retval TVG_RESULT_INVALID_ARGUMENT An invalid pointer passed as an argument and @p cnt > 0, the given length of the array is less than two or any of the @p dashPattern values is zero or less.
|
* @retval TVG_RESULT_INVALID_ARGUMENT In case @p dashPattern is @c nullptr and @p cnt > 0 or @p dashPattern is not @c nullptr and @p cnt is zero.
|
||||||
*
|
*
|
||||||
* @note To reset the stroke dash pattern, pass @c nullptr to @p dashPattern and zero to @p cnt.
|
* @note To reset the stroke dash pattern, pass @c nullptr to @p dashPattern and zero to @p cnt.
|
||||||
|
* @note Values of @p dashPattern less than zero are treated as zero.
|
||||||
|
* @note If all values in the @p dashPattern are equal to or less than 0, the dash is ignored.
|
||||||
|
* @note If the @p dashPattern contains an odd number of elements, the sequence is repeated in the same
|
||||||
|
* order to form an even-length pattern, preserving the alternation of dashes and gaps.
|
||||||
* @since 1.0
|
* @since 1.0
|
||||||
*/
|
*/
|
||||||
TVG_API Tvg_Result tvg_shape_set_stroke_dash(Tvg_Paint* paint, const float* dashPattern, uint32_t cnt, float offset);
|
TVG_API Tvg_Result tvg_shape_set_stroke_dash(Tvg_Paint* paint, const float* dashPattern, uint32_t cnt, float offset);
|
||||||
|
|
|
@ -219,15 +219,9 @@ static void _updateStroke(LottieStroke* stroke, float frameNo, RenderContext* ct
|
||||||
ctx->propagator->strokeMiterlimit(stroke->miterLimit);
|
ctx->propagator->strokeMiterlimit(stroke->miterLimit);
|
||||||
|
|
||||||
if (stroke->dashattr) {
|
if (stroke->dashattr) {
|
||||||
auto size = stroke->dashattr->size == 1 ? 2 : stroke->dashattr->size;
|
auto dashes = (float*)alloca(stroke->dashattr->size * sizeof(float));
|
||||||
auto dashes = (float*)alloca(size * sizeof(float));
|
for (uint8_t i = 0; i < stroke->dashattr->size; ++i) dashes[i] = stroke->dashattr->values[i](frameNo, tween, exps);
|
||||||
for (uint8_t i = 0; i < stroke->dashattr->size; ++i) {
|
ctx->propagator->strokeDash(dashes, stroke->dashattr->size, stroke->dashattr->offset(frameNo, tween, exps));
|
||||||
auto value = stroke->dashattr->values[i](frameNo, tween, exps);
|
|
||||||
//FIXME: allow the zero value in the engine level.
|
|
||||||
dashes[i] = value < FLT_EPSILON ? 0.01f : value;
|
|
||||||
}
|
|
||||||
if (stroke->dashattr->size == 1) dashes[1] = dashes[0];
|
|
||||||
ctx->propagator->strokeDash(dashes, size, stroke->dashattr->offset(frameNo, tween, exps));
|
|
||||||
} else {
|
} else {
|
||||||
ctx->propagator->strokeDash(nullptr, 0);
|
ctx->propagator->strokeDash(nullptr, 0);
|
||||||
}
|
}
|
||||||
|
|
|
@ -338,18 +338,19 @@ static void _parseDashArray(SvgLoaderData* loader, const char *str, SvgDash* das
|
||||||
str = _skipComma(str);
|
str = _skipComma(str);
|
||||||
auto parsedValue = toFloat(str, &end);
|
auto parsedValue = toFloat(str, &end);
|
||||||
if (str == end) break;
|
if (str == end) break;
|
||||||
if (parsedValue <= 0.0f) break;
|
if (parsedValue < 0.0f) {
|
||||||
|
dash->array.reset();
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (*end == '%') {
|
if (*end == '%') {
|
||||||
++end;
|
++end;
|
||||||
//Refers to the diagonal length of the viewport.
|
//Refers to the diagonal length of the viewport.
|
||||||
//https://www.w3.org/TR/SVG2/coords.html#Units
|
//https://www.w3.org/TR/SVG2/coords.html#Units
|
||||||
parsedValue = (sqrtf(powf(loader->svgParse->global.w, 2) + powf(loader->svgParse->global.h, 2)) / sqrtf(2.0f)) * (parsedValue / 100.0f);
|
parsedValue = (sqrtf(powf(loader->svgParse->global.w, 2) + powf(loader->svgParse->global.h, 2)) / sqrtf(2.0f)) * (parsedValue / 100.0f);
|
||||||
}
|
}
|
||||||
(*dash).array.push(parsedValue);
|
dash->array.push(parsedValue);
|
||||||
str = end;
|
str = end;
|
||||||
}
|
}
|
||||||
//If dash array size is 1, it means that dash and gap size are the same.
|
|
||||||
if ((*dash).array.count == 1) (*dash).array.push((*dash).array[0]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1509,7 +1509,7 @@ void Stroker::stroke(const RenderShape *rshape, const RenderPath& path)
|
||||||
}
|
}
|
||||||
|
|
||||||
auto& dash = rshape->stroke->dash;
|
auto& dash = rshape->stroke->dash;
|
||||||
if (dash.count == 0) doStroke(path);
|
if (dash.count == 0 || dash.length < DASH_PATTERN_THRESHOLD) doStroke(path);
|
||||||
else doDashStroke(path, dash.pattern, dash.count, dash.offset, dash.length);
|
else doDashStroke(path, dash.pattern, dash.count, dash.offset, dash.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1573,7 +1573,7 @@ void Stroker::doDashStroke(const RenderPath& path, const float *patterns, uint32
|
||||||
dpath.pts.reserve(20 * path.pts.count);
|
dpath.pts.reserve(20 * path.pts.count);
|
||||||
|
|
||||||
DashStroke dash(&dpath.cmds, &dpath.pts, patterns, patternCnt, offset, length);
|
DashStroke dash(&dpath.cmds, &dpath.pts, patterns, patternCnt, offset, length);
|
||||||
dash.doStroke(path);
|
dash.doStroke(path, mStrokeCap != StrokeCap::Butt);
|
||||||
doStroke(dpath);
|
doStroke(dpath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1919,7 +1919,7 @@ DashStroke::DashStroke(Array<PathCommand> *cmds, Array<Point> *pts, const float
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void DashStroke::doStroke(const RenderPath& path)
|
void DashStroke::doStroke(const RenderPath& path, bool drawPoint)
|
||||||
{
|
{
|
||||||
int32_t idx = 0;
|
int32_t idx = 0;
|
||||||
auto offset = mDashOffset;
|
auto offset = mDashOffset;
|
||||||
|
@ -1942,7 +1942,7 @@ void DashStroke::doStroke(const RenderPath& path)
|
||||||
ARRAY_FOREACH(cmd, path.cmds) {
|
ARRAY_FOREACH(cmd, path.cmds) {
|
||||||
switch (*cmd) {
|
switch (*cmd) {
|
||||||
case PathCommand::Close: {
|
case PathCommand::Close: {
|
||||||
this->dashLineTo(mPtStart);
|
this->dashLineTo(mPtStart, drawPoint);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case PathCommand::MoveTo: {
|
case PathCommand::MoveTo: {
|
||||||
|
@ -1956,12 +1956,12 @@ void DashStroke::doStroke(const RenderPath& path)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case PathCommand::LineTo: {
|
case PathCommand::LineTo: {
|
||||||
this->dashLineTo(*pts);
|
this->dashLineTo(*pts, drawPoint);
|
||||||
pts++;
|
pts++;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case PathCommand::CubicTo: {
|
case PathCommand::CubicTo: {
|
||||||
this->dashCubicTo(pts[0], pts[1], pts[2]);
|
this->dashCubicTo(pts[0], pts[1], pts[2], drawPoint);
|
||||||
pts += 3;
|
pts += 3;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1971,7 +1971,7 @@ void DashStroke::doStroke(const RenderPath& path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void DashStroke::dashLineTo(const Point& to)
|
void DashStroke::dashLineTo(const Point& to, bool drawPoint)
|
||||||
{
|
{
|
||||||
auto len = length(mPtCur - to);
|
auto len = length(mPtCur - to);
|
||||||
|
|
||||||
|
@ -2002,7 +2002,18 @@ void DashStroke::dashLineTo(const Point& to)
|
||||||
}
|
}
|
||||||
this->lineTo(left.pt2);
|
this->lineTo(left.pt2);
|
||||||
}
|
}
|
||||||
} else right = curr;
|
} else {
|
||||||
|
if (drawPoint && !mCurOpGap) {
|
||||||
|
if (drawPoint && !mCurOpGap) {
|
||||||
|
if (mMove || mDashPattern[mCurrIdx] < FLOAT_EPSILON) {
|
||||||
|
this->moveTo(curr.pt1);
|
||||||
|
mMove = false;
|
||||||
|
}
|
||||||
|
this->lineTo(curr.pt1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
right = curr;
|
||||||
|
}
|
||||||
mCurrIdx = (mCurrIdx + 1) % mDashCount;
|
mCurrIdx = (mCurrIdx + 1) % mDashCount;
|
||||||
mCurrLen = mDashPattern[mCurrIdx];
|
mCurrLen = mDashPattern[mCurrIdx];
|
||||||
mCurOpGap = !mCurOpGap;
|
mCurOpGap = !mCurOpGap;
|
||||||
|
@ -2030,7 +2041,7 @@ void DashStroke::dashLineTo(const Point& to)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void DashStroke::dashCubicTo(const Point& cnt1, const Point& cnt2, const Point& end)
|
void DashStroke::dashCubicTo(const Point& cnt1, const Point& cnt2, const Point& end, bool drawPoint)
|
||||||
{
|
{
|
||||||
Bezier cur;
|
Bezier cur;
|
||||||
cur.start = {mPtCur.x, mPtCur.y};
|
cur.start = {mPtCur.x, mPtCur.y};
|
||||||
|
@ -2065,7 +2076,16 @@ void DashStroke::dashCubicTo(const Point& cnt1, const Point& cnt2, const Point&
|
||||||
}
|
}
|
||||||
this->cubicTo(left.ctrl1, left.ctrl2, left.end);
|
this->cubicTo(left.ctrl1, left.ctrl2, left.end);
|
||||||
}
|
}
|
||||||
} else right = cur;
|
} else {
|
||||||
|
if (drawPoint && !mCurOpGap) {
|
||||||
|
if (mMove || mDashPattern[mCurrIdx] < FLOAT_EPSILON) {
|
||||||
|
this->moveTo(cur.start);
|
||||||
|
mMove = false;
|
||||||
|
}
|
||||||
|
this->lineTo(cur.start);
|
||||||
|
}
|
||||||
|
right = cur;
|
||||||
|
}
|
||||||
mCurrIdx = (mCurrIdx + 1) % mDashCount;
|
mCurrIdx = (mCurrIdx + 1) % mDashCount;
|
||||||
mCurrLen = mDashPattern[mCurrIdx];
|
mCurrLen = mDashPattern[mCurrIdx];
|
||||||
mCurOpGap = !mCurOpGap;
|
mCurOpGap = !mCurOpGap;
|
||||||
|
|
|
@ -120,11 +120,11 @@ class DashStroke
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
DashStroke(Array<PathCommand>* cmds, Array<Point>* pts, const float* patterns, uint32_t patternCnt, float offset, float length);
|
DashStroke(Array<PathCommand>* cmds, Array<Point>* pts, const float* patterns, uint32_t patternCnt, float offset, float length);
|
||||||
void doStroke(const RenderPath& path);
|
void doStroke(const RenderPath& path, bool drawPoint);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void dashLineTo(const Point& pt);
|
void dashLineTo(const Point& pt, bool drawPoint);
|
||||||
void dashCubicTo(const Point& pt1, const Point& pt2, const Point& pt3);
|
void dashCubicTo(const Point& pt1, const Point& pt2, const Point& pt3, bool drawPoint);
|
||||||
void moveTo(const Point& pt);
|
void moveTo(const Point& pt);
|
||||||
void lineTo(const Point& pt);
|
void lineTo(const Point& pt);
|
||||||
void cubicTo(const Point& pt1, const Point& pt2, const Point& pt3);
|
void cubicTo(const Point& pt1, const Point& pt2, const Point& pt3);
|
||||||
|
|
|
@ -97,7 +97,7 @@ static bool _outlineClose(SwOutline& outline)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void _dashLineTo(SwDashStroke& dash, const Point* to, const Matrix& transform)
|
static void _dashLineTo(SwDashStroke& dash, const Point* to, const Matrix& transform, bool drawPoint)
|
||||||
{
|
{
|
||||||
Line cur = {dash.ptCur, *to};
|
Line cur = {dash.ptCur, *to};
|
||||||
auto len = cur.length();
|
auto len = cur.length();
|
||||||
|
@ -128,6 +128,13 @@ static void _dashLineTo(SwDashStroke& dash, const Point* to, const Matrix& trans
|
||||||
_outlineLineTo(*dash.outline, &left.pt2, transform);
|
_outlineLineTo(*dash.outline, &left.pt2, transform);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
if (drawPoint && !dash.curOpGap) {
|
||||||
|
if (dash.move || dash.pattern[dash.curIdx] < FLOAT_EPSILON) {
|
||||||
|
_outlineMoveTo(*dash.outline, &cur.pt1, transform);
|
||||||
|
dash.move = false;
|
||||||
|
}
|
||||||
|
_outlineLineTo(*dash.outline, &cur.pt1, transform);
|
||||||
|
}
|
||||||
right = cur;
|
right = cur;
|
||||||
}
|
}
|
||||||
dash.curIdx = (dash.curIdx + 1) % dash.cnt;
|
dash.curIdx = (dash.curIdx + 1) % dash.cnt;
|
||||||
|
@ -157,7 +164,7 @@ static void _dashLineTo(SwDashStroke& dash, const Point* to, const Matrix& trans
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void _dashCubicTo(SwDashStroke& dash, const Point* ctrl1, const Point* ctrl2, const Point* to, const Matrix& transform)
|
static void _dashCubicTo(SwDashStroke& dash, const Point* ctrl1, const Point* ctrl2, const Point* to, const Matrix& transform, bool drawPoint)
|
||||||
{
|
{
|
||||||
Bezier cur = {dash.ptCur, *ctrl1, *ctrl2, *to};
|
Bezier cur = {dash.ptCur, *ctrl1, *ctrl2, *to};
|
||||||
auto len = cur.length();
|
auto len = cur.length();
|
||||||
|
@ -189,6 +196,13 @@ static void _dashCubicTo(SwDashStroke& dash, const Point* ctrl1, const Point* ct
|
||||||
_outlineCubicTo(*dash.outline, &left.ctrl1, &left.ctrl2, &left.end, transform);
|
_outlineCubicTo(*dash.outline, &left.ctrl1, &left.ctrl2, &left.end, transform);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
if (drawPoint && !dash.curOpGap) {
|
||||||
|
if (dash.move || dash.pattern[dash.curIdx] < FLOAT_EPSILON) {
|
||||||
|
_outlineMoveTo(*dash.outline, &cur.start, transform);
|
||||||
|
dash.move = false;
|
||||||
|
}
|
||||||
|
_outlineLineTo(*dash.outline, &cur.start, transform);
|
||||||
|
}
|
||||||
right = cur;
|
right = cur;
|
||||||
}
|
}
|
||||||
dash.curIdx = (dash.curIdx + 1) % dash.cnt;
|
dash.curIdx = (dash.curIdx + 1) % dash.cnt;
|
||||||
|
@ -218,9 +232,9 @@ static void _dashCubicTo(SwDashStroke& dash, const Point* ctrl1, const Point* ct
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void _dashClose(SwDashStroke& dash, const Matrix& transform)
|
static void _dashClose(SwDashStroke& dash, const Matrix& transform, bool drawPoint)
|
||||||
{
|
{
|
||||||
_dashLineTo(dash, &dash.ptStart, transform);
|
_dashLineTo(dash, &dash.ptStart, transform, drawPoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -291,10 +305,11 @@ static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix& trans
|
||||||
pts++;
|
pts++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto drawPoint = rshape->stroke->cap != StrokeCap::Butt;
|
||||||
while (--cmdCnt > 0) {
|
while (--cmdCnt > 0) {
|
||||||
switch (*cmds) {
|
switch (*cmds) {
|
||||||
case PathCommand::Close: {
|
case PathCommand::Close: {
|
||||||
_dashClose(dash, transform);
|
_dashClose(dash, transform, drawPoint);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case PathCommand::MoveTo: {
|
case PathCommand::MoveTo: {
|
||||||
|
@ -303,12 +318,12 @@ static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix& trans
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case PathCommand::LineTo: {
|
case PathCommand::LineTo: {
|
||||||
_dashLineTo(dash, pts, transform);
|
_dashLineTo(dash, pts, transform, drawPoint);
|
||||||
++pts;
|
++pts;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case PathCommand::CubicTo: {
|
case PathCommand::CubicTo: {
|
||||||
_dashCubicTo(dash, pts, pts + 1, pts + 2, transform);
|
_dashCubicTo(dash, pts, pts + 1, pts + 2, transform, drawPoint);
|
||||||
pts += 3;
|
pts += 3;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -508,7 +523,17 @@ bool shapeGenStrokeRle(SwShape* shape, const RenderShape* rshape, const Matrix&
|
||||||
auto ret = true;
|
auto ret = true;
|
||||||
|
|
||||||
//Dash style with/without trimming
|
//Dash style with/without trimming
|
||||||
if (rshape->stroke->dash.count > 0) {
|
if (rshape->stroke->dash.count > 0 && rshape->stroke->dash.length > DASH_PATTERN_THRESHOLD) {
|
||||||
|
//check if the length of the drawn dashes is negligibly small => if yes, then it's a valid shape without a stroke
|
||||||
|
if (rshape->stroke->cap == StrokeCap::Butt) {
|
||||||
|
auto len = 0.0f;
|
||||||
|
for (uint32_t i = 0; i < rshape->stroke->dash.count; i += 2) len += rshape->stroke->dash.pattern[i];
|
||||||
|
if (len < DASH_PATTERN_THRESHOLD) {
|
||||||
|
ret = true;
|
||||||
|
goto clear;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
shapeOutline = _genDashOutline(rshape, transform, mpool, tid, rshape->trimpath());
|
shapeOutline = _genDashOutline(rshape, transform, mpool, tid, rshape->trimpath());
|
||||||
if (!shapeOutline) return false;
|
if (!shapeOutline) return false;
|
||||||
dashStroking = true;
|
dashStroking = true;
|
||||||
|
|
|
@ -290,7 +290,7 @@ struct ShapeImpl : Shape
|
||||||
|
|
||||||
Result strokeDash(const float* pattern, uint32_t cnt, float offset)
|
Result strokeDash(const float* pattern, uint32_t cnt, float offset)
|
||||||
{
|
{
|
||||||
if ((cnt == 1) || (!pattern && cnt > 0) || (pattern && cnt == 0)) return Result::InvalidArguments;
|
if ((!pattern && cnt > 0) || (pattern && cnt == 0)) return Result::InvalidArguments;
|
||||||
if (!rs.stroke) rs.stroke = new RenderStroke;
|
if (!rs.stroke) rs.stroke = new RenderStroke;
|
||||||
//Reset dash
|
//Reset dash
|
||||||
auto& dash = rs.stroke->dash;
|
auto& dash = rs.stroke->dash;
|
||||||
|
@ -302,11 +302,7 @@ struct ShapeImpl : Shape
|
||||||
if (!dash.pattern) dash.pattern = tvg::malloc<float*>(sizeof(float) * cnt);
|
if (!dash.pattern) dash.pattern = tvg::malloc<float*>(sizeof(float) * cnt);
|
||||||
dash.length = 0.0f;
|
dash.length = 0.0f;
|
||||||
for (uint32_t i = 0; i < cnt; ++i) {
|
for (uint32_t i = 0; i < cnt; ++i) {
|
||||||
if (pattern[i] < DASH_PATTERN_THRESHOLD) {
|
dash.pattern[i] = pattern[i] < 0.0f ? 0.0f : pattern[i];
|
||||||
dash.count = 0;
|
|
||||||
return Result::InvalidArguments;
|
|
||||||
}
|
|
||||||
dash.pattern[i] = pattern[i];
|
|
||||||
dash.length += dash.pattern[i];
|
dash.length += dash.pattern[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -359,7 +359,7 @@ struct WgIndexedVertexBuffer
|
||||||
dashed->reset(scale);
|
dashed->reset(scale);
|
||||||
auto& dash = rstroke->dash;
|
auto& dash = rstroke->dash;
|
||||||
|
|
||||||
if (buff.count < 2 || tvg::zero(dash.length)) return;
|
if (buff.count < 2) return;
|
||||||
|
|
||||||
uint32_t index = 0;
|
uint32_t index = 0;
|
||||||
auto total = dash.pattern[index];
|
auto total = dash.pattern[index];
|
||||||
|
@ -372,12 +372,14 @@ struct WgIndexedVertexBuffer
|
||||||
auto gap = false;
|
auto gap = false;
|
||||||
|
|
||||||
// scip dashes by offset
|
// scip dashes by offset
|
||||||
while(total <= dashOffset) {
|
if (dashOffset > 0.0f) {
|
||||||
index = (index + 1) % dash.count;
|
while(total <= dashOffset) {
|
||||||
total += dash.pattern[index];
|
index = (index + 1) % dash.count;
|
||||||
gap = !gap;
|
total += dash.pattern[index];
|
||||||
|
gap = !gap;
|
||||||
|
}
|
||||||
|
total -= dashOffset;
|
||||||
}
|
}
|
||||||
total -= dashOffset;
|
|
||||||
|
|
||||||
// iterate by polyline points
|
// iterate by polyline points
|
||||||
for (uint32_t i = 0; i < buff.count - 1; i++) {
|
for (uint32_t i = 0; i < buff.count - 1; i++) {
|
||||||
|
@ -466,7 +468,10 @@ struct WgIndexedVertexBuffer
|
||||||
|
|
||||||
void appendSquare(Point v0, Point v1, float dist, float halfWidth)
|
void appendSquare(Point v0, Point v1, float dist, float halfWidth)
|
||||||
{
|
{
|
||||||
if(tvg::zero(dist)) return;
|
if(tvg::zero(dist)) {
|
||||||
|
appendQuad(v1 + Point{-halfWidth, -halfWidth}, v1 + Point{-halfWidth, halfWidth}, v1 + Point{halfWidth, -halfWidth}, v1 + Point{halfWidth, halfWidth});
|
||||||
|
return;
|
||||||
|
}
|
||||||
Point sub = v1 - v0;
|
Point sub = v1 - v0;
|
||||||
Point offset = sub / dist * halfWidth;
|
Point offset = sub / dist * halfWidth;
|
||||||
Point nrm = {+offset.y, -offset.x};
|
Point nrm = {+offset.y, -offset.x};
|
||||||
|
|
|
@ -387,7 +387,7 @@ void WgRenderDataShape::proceedStrokes(WgContext& context, const RenderStroke* r
|
||||||
{
|
{
|
||||||
assert(rstroke);
|
assert(rstroke);
|
||||||
auto strokesGenerator = pool->reqIndexedVertexBuffer(buff.scale);
|
auto strokesGenerator = pool->reqIndexedVertexBuffer(buff.scale);
|
||||||
if (rstroke->dash.count == 0) strokesGenerator->appendStrokes(buff, rstroke);
|
if (rstroke->dash.count == 0 || rstroke->dash.length < DASH_PATTERN_THRESHOLD) strokesGenerator->appendStrokes(buff, rstroke);
|
||||||
else strokesGenerator->appendStrokesDashed(buff, rstroke);
|
else strokesGenerator->appendStrokesDashed(buff, rstroke);
|
||||||
|
|
||||||
appendStroke(context, *strokesGenerator);
|
appendStroke(context, *strokesGenerator);
|
||||||
|
|
|
@ -157,21 +157,30 @@ TEST_CASE("Stroking", "[tvgShape]")
|
||||||
REQUIRE(shape->strokeFill(nullptr, nullptr, nullptr, nullptr) == Result::Success);
|
REQUIRE(shape->strokeFill(nullptr, nullptr, nullptr, nullptr) == Result::Success);
|
||||||
|
|
||||||
//Stroke Dash
|
//Stroke Dash
|
||||||
float dashPattern[3] = {0, 1.5f, 2.22f};
|
REQUIRE(shape->strokeDash(nullptr, 3) == Result::InvalidArguments);
|
||||||
REQUIRE(shape->strokeDash(dashPattern, 3) == Result::InvalidArguments);
|
|
||||||
|
|
||||||
float dashPattern2[3] = {1.0f, 1.5f, 2.22f};
|
float dashPattern0[3] = {-10.0f, 1.5f, 2.22f};
|
||||||
REQUIRE(shape->strokeDash(dashPattern2, 3) == Result::Success);
|
REQUIRE(shape->strokeDash(dashPattern0, 0) == Result::InvalidArguments);
|
||||||
REQUIRE(shape->strokeDash(dashPattern2, 3, 4.5) == Result::Success);
|
REQUIRE(shape->strokeDash(dashPattern0, 3) == Result::Success);
|
||||||
|
|
||||||
const float* dashPattern3;
|
float dashPattern1[2] = {0.0f, 0.0f};
|
||||||
|
REQUIRE(shape->strokeDash(dashPattern1, 2) == Result::Success);
|
||||||
|
|
||||||
|
float dashPattern2[1] = {10.0f};
|
||||||
|
REQUIRE(shape->strokeDash(dashPattern2, 1) == Result::Success);
|
||||||
|
|
||||||
|
float dashPattern3[3] = {1.0f, 1.5f, 2.22f};
|
||||||
|
REQUIRE(shape->strokeDash(dashPattern3, 3) == Result::Success);
|
||||||
|
REQUIRE(shape->strokeDash(dashPattern3, 3, 4.5) == Result::Success);
|
||||||
|
|
||||||
|
const float* dashPattern4;
|
||||||
float offset;
|
float offset;
|
||||||
REQUIRE(shape->strokeDash(nullptr) == 3);
|
REQUIRE(shape->strokeDash(nullptr) == 3);
|
||||||
REQUIRE(shape->strokeDash(&dashPattern3) == 3);
|
REQUIRE(shape->strokeDash(&dashPattern4) == 3);
|
||||||
REQUIRE(shape->strokeDash(&dashPattern3, &offset) == 3);
|
REQUIRE(shape->strokeDash(&dashPattern4, &offset) == 3);
|
||||||
REQUIRE(dashPattern3[0] == 1.0f);
|
REQUIRE(dashPattern4[0] == 1.0f);
|
||||||
REQUIRE(dashPattern3[1] == 1.5f);
|
REQUIRE(dashPattern4[1] == 1.5f);
|
||||||
REQUIRE(dashPattern3[2] == 2.22f);
|
REQUIRE(dashPattern4[2] == 2.22f);
|
||||||
REQUIRE(offset == 4.5f);
|
REQUIRE(offset == 4.5f);
|
||||||
|
|
||||||
REQUIRE(shape->strokeDash(nullptr, 0) == Result::Success);
|
REQUIRE(shape->strokeDash(nullptr, 0) == Result::Success);
|
||||||
|
|
Loading…
Add table
Reference in a new issue