common: strokeTrim api introduced

New api sets/gets the trim of the stroke
along the defined path segment, allowing
control over which part of the stroke is
visible.

@issue: https://github.com/thorvg/thorvg/issues/2190
This commit is contained in:
Mira Grudzinska 2024-05-30 21:10:37 +02:00 committed by Hermet Park
parent ca92d4a53b
commit 33f5ea34c0
7 changed files with 88 additions and 38 deletions

View file

@ -1021,7 +1021,6 @@ public:
*/
Result strokeJoin(StrokeJoin join) noexcept;
/**
* @brief Sets the stroke miterlimit.
*
@ -1033,6 +1032,22 @@ public:
*/
Result strokeMiterlimit(float miterlimit) noexcept;
/**
* @brief Sets the trim of the stroke along the defined path segment, allowing control over which part of the stroke is visible.
*
* The values of the arguments @p begin, @p end, and @p offset are in the range of 0.0 to 1.0, representing the beginning of the path and the end, respectively.
*
* @param[in] begin Specifies the start of the segment to display along the path.
* @param[in] end Specifies the end of the segment to display along the path.
* @param[in] simultaneous Determines how to trim multiple paths within a single shape. If set to @c true (default), trimming is applied simultaneously to all paths;
* Otherwise, all paths are treated as a single entity with a combined length equal to the sum of their individual lengths and are trimmed as such.
*
* @retval Result::Success when succeed.
*
* @note Experimental API
*/
Result strokeTrim(float begin, float end, bool simultaneous = true) noexcept;
/**
* @brief Sets the solid color for all of the figures from the path.
*
@ -1072,7 +1087,6 @@ public:
*/
Result fill(FillRule r) noexcept;
/**
* @brief Sets the rendering order of the stroke and the fill.
*
@ -1084,7 +1098,6 @@ public:
*/
Result order(bool strokeFirst) noexcept;
/**
* @brief Gets the commands data of the path.
*
@ -1190,6 +1203,18 @@ public:
*/
float strokeMiterlimit() const noexcept;
/**
* @brief Gets the trim of the stroke along the defined path segment.
*
* @param[out] begin The starting point of the segment to display along the path.
* @param[out] end Specifies the end of the segment to display along the path.
*
* @retval @c true if trimming is applied simultaneously to all paths of the shape, @c false otherwise.
*
* @note Experimental API
*/
bool strokeTrim(float* begin, float* end) const noexcept;
/**
* @brief Creates a new Shape object.
*

View file

@ -916,7 +916,7 @@ static void _updateTrimpath(TVG_UNUSED LottieGroup* parent, LottieObject** child
end = (length * end) + pbegin;
}
P(ctx->propagator)->strokeTrim(begin, end, trimpath->type == LottieTrimpath::Type::Individual ? true : false);
P(ctx->propagator)->strokeTrim(begin, end, trimpath->type == LottieTrimpath::Type::Simultaneous);
}

View file

@ -121,11 +121,11 @@ LottieImage::~LottieImage()
void LottieTrimpath::segment(float frameNo, float& start, float& end, LottieExpressions* exps)
{
auto s = this->start(frameNo, exps) * 0.01f;
auto e = this->end(frameNo, exps) * 0.01f;
start = this->start(frameNo, exps) * 0.01f;
end = this->end(frameNo, exps) * 0.01f;
auto o = fmodf(this->offset(frameNo, exps), 360.0f) / 360.0f; //0 ~ 1
auto diff = fabs(s - e);
auto diff = fabs(start - end);
if (mathZero(diff)) {
start = 0.0f;
end = 0.0f;
@ -137,28 +137,8 @@ void LottieTrimpath::segment(float frameNo, float& start, float& end, LottieExpr
return;
}
s += o;
e += o;
auto loop = true;
//no loop
if (s > 1.0f && e > 1.0f) loop = false;
if (s < 0.0f && e < 0.0f) loop = false;
if (s >= 0.0f && s <= 1.0f && e >= 0.0f && e <= 1.0f) loop = false;
if (s > 1.0f) s -= 1.0f;
if (s < 0.0f) s += 1.0f;
if (e > 1.0f) e -= 1.0f;
if (e < 0.0f) e += 1.0f;
if (loop) {
start = s > e ? s : e;
end = s < e ? s : e;
} else {
start = s < e ? s : e;
end = s > e ? s : e;
}
start += o;
end += o;
}

View file

@ -331,8 +331,9 @@ static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* trans
break;
}
case PathCommand::MoveTo: {
if (rshape->stroke->trim.individual) _dashMoveTo(dash, pts);
else _dashMoveTo(dash, offIdx, offset, pts);
if (rshape->stroke->trim.simultaneous) _dashMoveTo(dash, offIdx, offset, pts);
else _dashMoveTo(dash, pts);
++pts;
break;
}
@ -371,7 +372,7 @@ static float _outlineLength(const RenderShape* rshape)
const Point* close = nullptr;
auto length = 0.0f;
auto slength = -1.0f;
auto simultaneous = !rshape->stroke->trim.individual;
auto simultaneous = rshape->stroke->trim.simultaneous;
//Compute the whole length
while (cmdCnt-- > 0) {

View file

@ -140,7 +140,7 @@ struct RenderStroke
struct {
float begin = 0.0f;
float end = 1.0f;
bool individual = false;
bool simultaneous = true;
} trim;
~RenderStroke()

View file

@ -362,6 +362,7 @@ Result Shape::strokeJoin(StrokeJoin join) noexcept
return Result::Success;
}
Result Shape::strokeMiterlimit(float miterlimit) noexcept
{
// https://www.w3.org/TR/SVG2/painting.html#LineJoin
@ -385,12 +386,26 @@ StrokeJoin Shape::strokeJoin() const noexcept
return pImpl->rs.strokeJoin();
}
float Shape::strokeMiterlimit() const noexcept
{
return pImpl->rs.strokeMiterlimit();
}
Result Shape::strokeTrim(float begin, float end, bool simultaneous) noexcept
{
pImpl->strokeTrim(begin, end, simultaneous);
return Result::Success;
}
bool Shape::strokeTrim(float* begin, float* end) const noexcept
{
return pImpl->strokeTrim(begin, end);
}
Result Shape::fill(FillRule r) noexcept
{
pImpl->rs.rule = r;

View file

@ -216,21 +216,50 @@ struct Shape::Impl
return true;
}
bool strokeTrim(float begin, float end, bool individual)
void strokeTrim(float begin, float end, bool simultaneous)
{
if (!rs.stroke) {
if (begin == 0.0f && end == 1.0f) return true;
if (begin == 0.0f && end == 1.0f) return;
rs.stroke = new RenderStroke();
}
if (mathEqual(rs.stroke->trim.begin, begin) && mathEqual(rs.stroke->trim.end, end)) return true;
if (mathEqual(rs.stroke->trim.begin, begin) && mathEqual(rs.stroke->trim.end, end) &&
rs.stroke->trim.simultaneous == simultaneous) return;
auto loop = true;
if (begin > 1.0f && end > 1.0f) loop = false;
if (begin < 0.0f && end < 0.0f) loop = false;
if (begin >= 0.0f && begin <= 1.0f && end >= 0.0f && end <= 1.0f) loop = false;
if (begin > 1.0f) begin -= 1.0f;
if (begin < 0.0f) begin += 1.0f;
if (end > 1.0f) end -= 1.0f;
if (end < 0.0f) end += 1.0f;
if ((loop && begin < end) || (!loop && begin > end)) {
auto tmp = begin;
begin = end;
end = tmp;
}
rs.stroke->trim.begin = begin;
rs.stroke->trim.end = end;
rs.stroke->trim.individual = individual;
rs.stroke->trim.simultaneous = simultaneous;
flag |= RenderUpdateFlag::Stroke;
}
return true;
bool strokeTrim(float* begin, float* end)
{
if (rs.stroke) {
if (begin) *begin = rs.stroke->trim.begin;
if (end) *end = rs.stroke->trim.end;
return rs.stroke->trim.simultaneous;
} else {
if (begin) *begin = 0.0f;
if (end) *end = 1.0f;
return false;
}
}
bool strokeCap(StrokeCap cap)