mirror of
https://github.com/thorvg/thorvg.git
synced 2025-06-07 21:23:32 +00:00
common: stroke dash offset support added
The feature is supported also in the svg loader. @Issue: https://github.com/thorvg/thorvg/issues/1591
This commit is contained in:
parent
c1e4e0808a
commit
478e45f9f3
9 changed files with 57 additions and 19 deletions
|
@ -1002,6 +1002,7 @@ public:
|
|||
*
|
||||
* @param[in] dashPattern The array of consecutive pair values of the dash length and the gap length.
|
||||
* @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.
|
||||
*
|
||||
* @retval Result::Success When succeed.
|
||||
* @retval Result::FailedAllocation An internal error with a memory allocation for an object to be dashed.
|
||||
|
@ -1010,7 +1011,7 @@ public:
|
|||
* @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.
|
||||
*/
|
||||
Result stroke(const float* dashPattern, uint32_t cnt) noexcept;
|
||||
Result stroke(const float* dashPattern, uint32_t cnt, float offset = 0.0f) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Sets the cap style of the stroke in the open sub-paths.
|
||||
|
@ -1170,10 +1171,11 @@ public:
|
|||
* @brief Gets the dash pattern of the stroke.
|
||||
*
|
||||
* @param[out] dashPattern The pointer to the memory, where the dash pattern array is stored.
|
||||
* @param[out] offset The shift of the starting point within the repeating dash pattern.
|
||||
*
|
||||
* @return The length of the @p dashPattern array.
|
||||
*/
|
||||
uint32_t strokeDash(const float** dashPattern) const noexcept;
|
||||
uint32_t strokeDash(const float** dashPattern, float* offset = nullptr) const noexcept;
|
||||
|
||||
/**
|
||||
* @brief Gets the cap style used for stroking the path.
|
||||
|
|
|
@ -132,7 +132,7 @@ struct SwShapeTask : SwTask
|
|||
shape outline below stroke could be full covered by stroke drawing.
|
||||
Thus it turns off antialising in that condition.
|
||||
Also, it shouldn't be dash style. */
|
||||
auto antiAlias = strokeAlpha < 255 || rshape->strokeWidth() <= 2 || rshape->strokeDash(nullptr) > 0 || (rshape->stroke && rshape->stroke->strokeFirst);
|
||||
auto antiAlias = strokeAlpha < 255 || rshape->strokeWidth() <= 2 || rshape->strokeDash(nullptr, nullptr) > 0 || (rshape->stroke && rshape->stroke->strokeFirst);
|
||||
|
||||
if (!shapeGenRle(&shape, rshape, antiAlias)) goto err;
|
||||
}
|
||||
|
|
|
@ -222,9 +222,27 @@ static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* trans
|
|||
dash.curOpGap = false;
|
||||
|
||||
const float* pattern;
|
||||
dash.cnt = rshape->strokeDash(&pattern);
|
||||
float offset;
|
||||
dash.cnt = rshape->strokeDash(&pattern, &offset);
|
||||
if (dash.cnt == 0) return nullptr;
|
||||
|
||||
auto patternLength = 0.0f;
|
||||
uint32_t offIdx = 0;
|
||||
if (fabsf(offset) > FLT_EPSILON) {
|
||||
for (auto i = 0; i < dash.cnt; ++i) patternLength += pattern[i];
|
||||
bool isOdd = dash.cnt % 2;
|
||||
if (isOdd) patternLength *= 2;
|
||||
|
||||
if (offset < 0) offset = patternLength + fmod(offset, patternLength);
|
||||
else offset = fmod(offset, patternLength);
|
||||
|
||||
for (auto i = 0; i < dash.cnt * (1 + isOdd); ++i, ++offIdx) {
|
||||
auto curPattern = pattern[i % dash.cnt];
|
||||
if (offset < curPattern) break;
|
||||
offset -= curPattern;
|
||||
}
|
||||
}
|
||||
|
||||
//OPTMIZE ME: Use mempool???
|
||||
dash.pattern = const_cast<float*>(pattern);
|
||||
dash.outline = static_cast<SwOutline*>(calloc(1, sizeof(SwOutline)));
|
||||
|
@ -271,9 +289,9 @@ static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* trans
|
|||
}
|
||||
case PathCommand::MoveTo: {
|
||||
//reset the dash
|
||||
dash.curIdx = 0;
|
||||
dash.curLen = *dash.pattern;
|
||||
dash.curOpGap = false;
|
||||
dash.curIdx = offIdx % dash.cnt;
|
||||
dash.curLen = dash.pattern[dash.curIdx] - offset;
|
||||
dash.curOpGap = offIdx % 2;
|
||||
dash.ptStart = dash.ptCur = *pts;
|
||||
++pts;
|
||||
break;
|
||||
|
@ -515,7 +533,7 @@ bool shapeGenStrokeRle(SwShape* shape, const RenderShape* rshape, const Matrix*
|
|||
bool ret = true;
|
||||
|
||||
//Dash Style Stroke
|
||||
if (rshape->strokeDash(nullptr) > 0) {
|
||||
if (rshape->strokeDash(nullptr, nullptr) > 0) {
|
||||
shapeOutline = _genDashOutline(rshape, transform);
|
||||
if (!shapeOutline) return false;
|
||||
freeOutline = true;
|
||||
|
|
|
@ -137,6 +137,7 @@ struct RenderStroke
|
|||
Fill *fill = nullptr;
|
||||
float* dashPattern = nullptr;
|
||||
uint32_t dashCnt = 0;
|
||||
float dashOffset = 0.0f;
|
||||
StrokeCap cap = StrokeCap::Square;
|
||||
StrokeJoin join = StrokeJoin::Bevel;
|
||||
float miterlimit = 4.0f;
|
||||
|
@ -200,10 +201,11 @@ struct RenderShape
|
|||
return stroke->fill;
|
||||
}
|
||||
|
||||
uint32_t strokeDash(const float** dashPattern) const
|
||||
uint32_t strokeDash(const float** dashPattern, float* offset) const
|
||||
{
|
||||
if (!stroke) return 0;
|
||||
if (dashPattern) *dashPattern = stroke->dashPattern;
|
||||
if (offset) *offset = stroke->dashOffset;
|
||||
return stroke->dashCnt;
|
||||
}
|
||||
|
||||
|
|
|
@ -340,7 +340,7 @@ const Fill* Shape::strokeFill() const noexcept
|
|||
}
|
||||
|
||||
|
||||
Result Shape::stroke(const float* dashPattern, uint32_t cnt) noexcept
|
||||
Result Shape::stroke(const float* dashPattern, uint32_t cnt, float offset) noexcept
|
||||
{
|
||||
if ((cnt == 1) || (!dashPattern && cnt > 0) || (dashPattern && cnt == 0)) {
|
||||
return Result::InvalidArguments;
|
||||
|
@ -349,15 +349,15 @@ Result Shape::stroke(const float* dashPattern, uint32_t cnt) noexcept
|
|||
for (uint32_t i = 0; i < cnt; i++)
|
||||
if (dashPattern[i] < FLT_EPSILON) return Result::InvalidArguments;
|
||||
|
||||
if (!pImpl->strokeDash(dashPattern, cnt)) return Result::FailedAllocation;
|
||||
if (!pImpl->strokeDash(dashPattern, cnt, offset)) return Result::FailedAllocation;
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
uint32_t Shape::strokeDash(const float** dashPattern) const noexcept
|
||||
uint32_t Shape::strokeDash(const float** dashPattern, float* offset) const noexcept
|
||||
{
|
||||
return pImpl->rs.strokeDash(dashPattern);
|
||||
return pImpl->rs.strokeDash(dashPattern, offset);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -269,7 +269,7 @@ struct Shape::Impl
|
|||
return Result::Success;
|
||||
}
|
||||
|
||||
bool strokeDash(const float* pattern, uint32_t cnt)
|
||||
bool strokeDash(const float* pattern, uint32_t cnt, float offset)
|
||||
{
|
||||
//Reset dash
|
||||
if (!pattern && cnt == 0) {
|
||||
|
@ -290,6 +290,7 @@ struct Shape::Impl
|
|||
}
|
||||
}
|
||||
rs.stroke->dashCnt = cnt;
|
||||
rs.stroke->dashOffset = offset;
|
||||
flag |= RenderUpdateFlag::Stroke;
|
||||
|
||||
return true;
|
||||
|
@ -338,6 +339,7 @@ struct Shape::Impl
|
|||
dup->rs.stroke = new RenderStroke();
|
||||
dup->rs.stroke->width = rs.stroke->width;
|
||||
dup->rs.stroke->dashCnt = rs.stroke->dashCnt;
|
||||
dup->rs.stroke->dashOffset = rs.stroke->dashOffset;
|
||||
dup->rs.stroke->cap = rs.stroke->cap;
|
||||
dup->rs.stroke->join = rs.stroke->join;
|
||||
dup->rs.stroke->strokeFirst = rs.stroke->strokeFirst;
|
||||
|
|
|
@ -952,6 +952,12 @@ static void _handleStrokeDashArrayAttr(SvgLoaderData* loader, SvgNode* node, con
|
|||
_parseDashArray(loader, value, &node->style->stroke.dash);
|
||||
}
|
||||
|
||||
static void _handleStrokeDashOffsetAttr(SvgLoaderData* loader, SvgNode* node, const char* value)
|
||||
{
|
||||
node->style->stroke.flags = (node->style->stroke.flags | SvgStrokeFlags::DashOffset);
|
||||
node->style->stroke.dash.offset = _toFloat(loader->svgParse, value, SvgParserLengthType::Horizontal);
|
||||
}
|
||||
|
||||
static void _handleStrokeWidthAttr(SvgLoaderData* loader, SvgNode* node, const char* value)
|
||||
{
|
||||
node->style->stroke.flags = (node->style->stroke.flags | SvgStrokeFlags::Width);
|
||||
|
@ -1108,6 +1114,7 @@ static constexpr struct
|
|||
STYLE_DEF(stroke-linecap, StrokeLineCap, SvgStyleFlags::StrokeLineCap),
|
||||
STYLE_DEF(stroke-opacity, StrokeOpacity, SvgStyleFlags::StrokeOpacity),
|
||||
STYLE_DEF(stroke-dasharray, StrokeDashArray, SvgStyleFlags::StrokeDashArray),
|
||||
STYLE_DEF(stroke-dashoffset, StrokeDashOffset, SvgStyleFlags::StrokeDashOffset),
|
||||
STYLE_DEF(transform, Transform, SvgStyleFlags::Transform),
|
||||
STYLE_DEF(clip-path, ClipPath, SvgStyleFlags::ClipPath),
|
||||
STYLE_DEF(mask, Mask, SvgStyleFlags::Mask),
|
||||
|
@ -2773,6 +2780,9 @@ static void _styleInherit(SvgStyleProperty* child, const SvgStyleProperty* paren
|
|||
}
|
||||
}
|
||||
}
|
||||
if (!(child->stroke.flags & SvgStrokeFlags::DashOffset)) {
|
||||
child->stroke.dash.offset = parent->stroke.dash.offset;
|
||||
}
|
||||
if (!(child->stroke.flags & SvgStrokeFlags::Cap)) {
|
||||
child->stroke.cap = parent->stroke.cap;
|
||||
}
|
||||
|
@ -2839,13 +2849,15 @@ static void _styleCopy(SvgStyleProperty* to, const SvgStyleProperty* from)
|
|||
}
|
||||
}
|
||||
}
|
||||
if (from->stroke.flags & SvgStrokeFlags::DashOffset) {
|
||||
to->stroke.dash.offset = from->stroke.dash.offset;
|
||||
}
|
||||
if (from->stroke.flags & SvgStrokeFlags::Cap) {
|
||||
to->stroke.cap = from->stroke.cap;
|
||||
}
|
||||
if (from->stroke.flags & SvgStrokeFlags::Join) {
|
||||
to->stroke.join = from->stroke.join;
|
||||
}
|
||||
|
||||
if (from->stroke.flags & SvgStrokeFlags::Miterlimit) {
|
||||
to->stroke.miterlimit = from->stroke.miterlimit;
|
||||
}
|
||||
|
|
|
@ -100,7 +100,8 @@ enum class SvgStrokeFlags
|
|||
Cap = 0x20,
|
||||
Join = 0x40,
|
||||
Dash = 0x80,
|
||||
Miterlimit = 0x100
|
||||
Miterlimit = 0x100,
|
||||
DashOffset = 0x200
|
||||
};
|
||||
|
||||
constexpr bool operator &(SvgStrokeFlags a, SvgStrokeFlags b)
|
||||
|
@ -139,7 +140,8 @@ enum class SvgStyleFlags
|
|||
MaskType = 0x4000,
|
||||
Display = 0x8000,
|
||||
PaintOrder = 0x10000,
|
||||
StrokeMiterlimit = 0x20000
|
||||
StrokeMiterlimit = 0x20000,
|
||||
StrokeDashOffset = 0x40000,
|
||||
};
|
||||
|
||||
constexpr bool operator &(SvgStyleFlags a, SvgStyleFlags b)
|
||||
|
@ -423,6 +425,7 @@ struct SvgPaint
|
|||
struct SvgDash
|
||||
{
|
||||
Array<float> array;
|
||||
float offset;
|
||||
};
|
||||
|
||||
struct SvgStyleGradient
|
||||
|
@ -469,7 +472,6 @@ struct SvgStyleStroke
|
|||
StrokeJoin join;
|
||||
float miterlimit;
|
||||
SvgDash dash;
|
||||
int dashCount;
|
||||
};
|
||||
|
||||
struct SvgStyleProperty
|
||||
|
|
|
@ -349,7 +349,7 @@ static void _applyProperty(SvgLoaderData& loaderData, SvgNode* node, Shape* vg,
|
|||
vg->stroke(style->stroke.join);
|
||||
vg->strokeMiterlimit(style->stroke.miterlimit);
|
||||
if (style->stroke.dash.array.count > 0) {
|
||||
vg->stroke(style->stroke.dash.array.data, style->stroke.dash.array.count);
|
||||
vg->stroke(style->stroke.dash.array.data, style->stroke.dash.array.count, style->stroke.dash.offset);
|
||||
}
|
||||
|
||||
//If stroke property is nullptr then do nothing
|
||||
|
|
Loading…
Add table
Reference in a new issue