mirror of
https://github.com/thorvg/thorvg.git
synced 2025-06-07 21:23:32 +00:00
api: handling values <= 0 in strokeDash() api
The API allows now values <= 0 for dashes and gaps. Negative values are treated as zero. The exception is when all provided values are <= 0, in which case the dash is ignored. This fixes the issue when dash = 0 was provided for strokes with round or butt caps - the dot was not drawn, even though it should have been. docs: the strokeDash API behavior's clarification for odd numbers of values in dashPattern and refinement of the accepted values.
This commit is contained in:
parent
55847bdcb3
commit
d71e11495d
11 changed files with 123 additions and 66 deletions
11
inc/thorvg.h
11
inc/thorvg.h
|
@ -1083,14 +1083,17 @@ public:
|
|||
/**
|
||||
* @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] 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.
|
||||
* @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
|
||||
*/
|
||||
|
|
|
@ -1336,14 +1336,18 @@ TVG_API Tvg_Result tvg_shape_get_stroke_gradient(const Tvg_Paint* paint, Tvg_Gra
|
|||
* @brief Sets the shape's stroke dash pattern.
|
||||
*
|
||||
* @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] 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.
|
||||
* @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 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
|
||||
*/
|
||||
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);
|
||||
|
||||
if (stroke->dashattr) {
|
||||
auto size = stroke->dashattr->size == 1 ? 2 : stroke->dashattr->size;
|
||||
auto dashes = (float*)alloca(size * sizeof(float));
|
||||
for (uint8_t i = 0; i < stroke->dashattr->size; ++i) {
|
||||
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));
|
||||
auto dashes = (float*)alloca(stroke->dashattr->size * sizeof(float));
|
||||
for (uint8_t i = 0; i < stroke->dashattr->size; ++i) dashes[i] = stroke->dashattr->values[i](frameNo, tween, exps);
|
||||
ctx->propagator->strokeDash(dashes, stroke->dashattr->size, stroke->dashattr->offset(frameNo, tween, exps));
|
||||
} else {
|
||||
ctx->propagator->strokeDash(nullptr, 0);
|
||||
}
|
||||
|
|
|
@ -338,18 +338,19 @@ static void _parseDashArray(SvgLoaderData* loader, const char *str, SvgDash* das
|
|||
str = _skipComma(str);
|
||||
auto parsedValue = toFloat(str, &end);
|
||||
if (str == end) break;
|
||||
if (parsedValue <= 0.0f) break;
|
||||
if (parsedValue < 0.0f) {
|
||||
dash->array.reset();
|
||||
return;
|
||||
}
|
||||
if (*end == '%') {
|
||||
++end;
|
||||
//Refers to the diagonal length of the viewport.
|
||||
//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);
|
||||
}
|
||||
(*dash).array.push(parsedValue);
|
||||
dash->array.push(parsedValue);
|
||||
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;
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -1578,7 +1578,7 @@ void Stroker::doDashStroke(const RenderPath& path, const float *patterns, uint32
|
|||
dpath.pts.reserve(20 * path.pts.count);
|
||||
|
||||
DashStroke dash(&dpath.cmds, &dpath.pts, patterns, patternCnt, offset, length);
|
||||
dash.doStroke(path);
|
||||
dash.doStroke(path, mStrokeCap != StrokeCap::Butt);
|
||||
doStroke(dpath);
|
||||
}
|
||||
|
||||
|
@ -1924,7 +1924,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;
|
||||
auto offset = mDashOffset;
|
||||
|
@ -1947,7 +1947,7 @@ void DashStroke::doStroke(const RenderPath& path)
|
|||
ARRAY_FOREACH(cmd, path.cmds) {
|
||||
switch (*cmd) {
|
||||
case PathCommand::Close: {
|
||||
this->dashLineTo(mPtStart);
|
||||
this->dashLineTo(mPtStart, drawPoint);
|
||||
break;
|
||||
}
|
||||
case PathCommand::MoveTo: {
|
||||
|
@ -1961,12 +1961,12 @@ void DashStroke::doStroke(const RenderPath& path)
|
|||
break;
|
||||
}
|
||||
case PathCommand::LineTo: {
|
||||
this->dashLineTo(*pts);
|
||||
this->dashLineTo(*pts, drawPoint);
|
||||
pts++;
|
||||
break;
|
||||
}
|
||||
case PathCommand::CubicTo: {
|
||||
this->dashCubicTo(pts[0], pts[1], pts[2]);
|
||||
this->dashCubicTo(pts[0], pts[1], pts[2], drawPoint);
|
||||
pts += 3;
|
||||
break;
|
||||
}
|
||||
|
@ -1976,7 +1976,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);
|
||||
|
||||
|
@ -2007,7 +2007,18 @@ void DashStroke::dashLineTo(const Point& to)
|
|||
}
|
||||
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;
|
||||
mCurrLen = mDashPattern[mCurrIdx];
|
||||
mCurOpGap = !mCurOpGap;
|
||||
|
@ -2035,7 +2046,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;
|
||||
cur.start = {mPtCur.x, mPtCur.y};
|
||||
|
@ -2070,7 +2081,16 @@ void DashStroke::dashCubicTo(const Point& cnt1, const Point& cnt2, const Point&
|
|||
}
|
||||
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;
|
||||
mCurrLen = mDashPattern[mCurrIdx];
|
||||
mCurOpGap = !mCurOpGap;
|
||||
|
|
|
@ -120,11 +120,11 @@ 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);
|
||||
void doStroke(const RenderPath& path, bool drawPoint);
|
||||
|
||||
private:
|
||||
void dashLineTo(const Point& pt);
|
||||
void dashCubicTo(const Point& pt1, const Point& pt2, const Point& pt3);
|
||||
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);
|
||||
|
|
|
@ -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};
|
||||
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);
|
||||
}
|
||||
} 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;
|
||||
}
|
||||
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};
|
||||
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);
|
||||
}
|
||||
} 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;
|
||||
}
|
||||
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++;
|
||||
}
|
||||
|
||||
auto drawPoint = rshape->stroke->cap != StrokeCap::Butt;
|
||||
while (--cmdCnt > 0) {
|
||||
switch (*cmds) {
|
||||
case PathCommand::Close: {
|
||||
_dashClose(dash, transform);
|
||||
_dashClose(dash, transform, drawPoint);
|
||||
break;
|
||||
}
|
||||
case PathCommand::MoveTo: {
|
||||
|
@ -303,12 +318,12 @@ static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix& trans
|
|||
break;
|
||||
}
|
||||
case PathCommand::LineTo: {
|
||||
_dashLineTo(dash, pts, transform);
|
||||
_dashLineTo(dash, pts, transform, drawPoint);
|
||||
++pts;
|
||||
break;
|
||||
}
|
||||
case PathCommand::CubicTo: {
|
||||
_dashCubicTo(dash, pts, pts + 1, pts + 2, transform);
|
||||
_dashCubicTo(dash, pts, pts + 1, pts + 2, transform, drawPoint);
|
||||
pts += 3;
|
||||
break;
|
||||
}
|
||||
|
@ -508,7 +523,17 @@ bool shapeGenStrokeRle(SwShape* shape, const RenderShape* rshape, const Matrix&
|
|||
auto ret = true;
|
||||
|
||||
//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());
|
||||
if (!shapeOutline) return false;
|
||||
dashStroking = true;
|
||||
|
|
|
@ -290,7 +290,7 @@ struct ShapeImpl : Shape
|
|||
|
||||
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;
|
||||
//Reset dash
|
||||
auto& dash = rs.stroke->dash;
|
||||
|
@ -302,11 +302,7 @@ struct ShapeImpl : Shape
|
|||
if (!dash.pattern) dash.pattern = tvg::malloc<float*>(sizeof(float) * cnt);
|
||||
dash.length = 0.0f;
|
||||
for (uint32_t i = 0; i < cnt; ++i) {
|
||||
if (pattern[i] < DASH_PATTERN_THRESHOLD) {
|
||||
dash.count = 0;
|
||||
return Result::InvalidArguments;
|
||||
}
|
||||
dash.pattern[i] = pattern[i];
|
||||
dash.pattern[i] = pattern[i] < 0.0f ? 0.0f : pattern[i];
|
||||
dash.length += dash.pattern[i];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -359,7 +359,7 @@ struct WgIndexedVertexBuffer
|
|||
dashed->reset(scale);
|
||||
auto& dash = rstroke->dash;
|
||||
|
||||
if (buff.count < 2 || tvg::zero(dash.length)) return;
|
||||
if (buff.count < 2) return;
|
||||
|
||||
uint32_t index = 0;
|
||||
auto total = dash.pattern[index];
|
||||
|
@ -372,12 +372,14 @@ struct WgIndexedVertexBuffer
|
|||
auto gap = false;
|
||||
|
||||
// scip dashes by offset
|
||||
while(total <= dashOffset) {
|
||||
index = (index + 1) % dash.count;
|
||||
total += dash.pattern[index];
|
||||
gap = !gap;
|
||||
if (dashOffset > 0.0f) {
|
||||
while(total <= dashOffset) {
|
||||
index = (index + 1) % dash.count;
|
||||
total += dash.pattern[index];
|
||||
gap = !gap;
|
||||
}
|
||||
total -= dashOffset;
|
||||
}
|
||||
total -= dashOffset;
|
||||
|
||||
// iterate by polyline points
|
||||
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)
|
||||
{
|
||||
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 offset = sub / dist * halfWidth;
|
||||
Point nrm = {+offset.y, -offset.x};
|
||||
|
|
|
@ -394,7 +394,7 @@ void WgRenderDataShape::proceedStrokes(WgContext& context, const RenderStroke* r
|
|||
{
|
||||
assert(rstroke);
|
||||
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);
|
||||
|
||||
appendStroke(context, *strokesGenerator);
|
||||
|
|
|
@ -157,21 +157,30 @@ TEST_CASE("Stroking", "[tvgShape]")
|
|||
REQUIRE(shape->strokeFill(nullptr, nullptr, nullptr, nullptr) == Result::Success);
|
||||
|
||||
//Stroke Dash
|
||||
float dashPattern[3] = {0, 1.5f, 2.22f};
|
||||
REQUIRE(shape->strokeDash(dashPattern, 3) == Result::InvalidArguments);
|
||||
REQUIRE(shape->strokeDash(nullptr, 3) == Result::InvalidArguments);
|
||||
|
||||
float dashPattern2[3] = {1.0f, 1.5f, 2.22f};
|
||||
REQUIRE(shape->strokeDash(dashPattern2, 3) == Result::Success);
|
||||
REQUIRE(shape->strokeDash(dashPattern2, 3, 4.5) == Result::Success);
|
||||
float dashPattern0[3] = {-10.0f, 1.5f, 2.22f};
|
||||
REQUIRE(shape->strokeDash(dashPattern0, 0) == Result::InvalidArguments);
|
||||
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;
|
||||
REQUIRE(shape->strokeDash(nullptr) == 3);
|
||||
REQUIRE(shape->strokeDash(&dashPattern3) == 3);
|
||||
REQUIRE(shape->strokeDash(&dashPattern3, &offset) == 3);
|
||||
REQUIRE(dashPattern3[0] == 1.0f);
|
||||
REQUIRE(dashPattern3[1] == 1.5f);
|
||||
REQUIRE(dashPattern3[2] == 2.22f);
|
||||
REQUIRE(shape->strokeDash(&dashPattern4) == 3);
|
||||
REQUIRE(shape->strokeDash(&dashPattern4, &offset) == 3);
|
||||
REQUIRE(dashPattern4[0] == 1.0f);
|
||||
REQUIRE(dashPattern4[1] == 1.5f);
|
||||
REQUIRE(dashPattern4[2] == 2.22f);
|
||||
REQUIRE(offset == 4.5f);
|
||||
|
||||
REQUIRE(shape->strokeDash(nullptr, 0) == Result::Success);
|
||||
|
|
Loading…
Add table
Reference in a new issue