common: move&fix trimming wrapping logic

The logic of interpretation of trimming start and
end values has been moved to the sw_engine,
so the values provided by the user are not modified.
No logical changes in the interpretation alg.

For pairs of trim's start/end values where the distance
between begin and end is >= 1, the entire stroke should
be drawn, but instead, nothing or only part is drawn. Fixed.

Stroke trim docs fixed.
This commit is contained in:
Mira Grudzinska 2024-07-25 19:48:33 +02:00 committed by Hermet Park
parent c9ad7289a6
commit d29a3d754b
5 changed files with 37 additions and 25 deletions

View file

@ -1040,7 +1040,7 @@ public:
/** /**
* @brief Sets the trim of the stroke along the defined path segment, allowing control over which part of the stroke is visible. * @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. * If the values of the arguments @p begin and @p end exceed the 0-1 range, they are wrapped around in a manner similar to angle wrapping, effectively treating the range as circular.
* *
* @param[in] begin Specifies the start of the segment to display along the path. * @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] end Specifies the end of the segment to display along the path.

View file

@ -1511,7 +1511,7 @@ TVG_API Tvg_Result tvg_shape_get_stroke_miterlimit(const Tvg_Paint* paint, float
/*! /*!
* \brief Sets the trim of the stroke along the defined path segment, allowing control over which part of the stroke is visible. * \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. * If the values of the arguments @p begin and @p end exceed the 0-1 range, they are wrapped around in a manner similar to angle wrapping, effectively treating the range as circular.
* *
* \param[in] paint A Tvg_Paint pointer to the shape object. * \param[in] paint A Tvg_Paint pointer to the shape object.
* \param[in] begin Specifies the start of the segment to display along the path. * \param[in] begin Specifies the start of the segment to display along the path.

View file

@ -244,10 +244,10 @@ static void _dashMoveTo(SwDashStroke& dash, uint32_t offIdx, float offset, const
} }
static void _trimPattern(SwDashStroke* dash, const RenderShape* rshape, float length) static void _trimPattern(SwDashStroke* dash, const RenderShape* rshape, float length, float trimBegin, float trimEnd)
{ {
auto begin = length * rshape->stroke->trim.begin; auto begin = length * trimBegin;
auto end = length * rshape->stroke->trim.end; auto end = length * trimEnd;
//default //default
if (end > begin) { if (end > begin) {
@ -340,6 +340,8 @@ static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix& trans
auto offset = 0.0f; auto offset = 0.0f;
dash.cnt = rshape->strokeDash((const float**)&dash.pattern, &offset); dash.cnt = rshape->strokeDash((const float**)&dash.pattern, &offset);
auto simultaneous = rshape->stroke->trim.simultaneous; auto simultaneous = rshape->stroke->trim.simultaneous;
float trimBegin = 0.0f, trimEnd = 1.0f;
if (trimmed) rshape->stroke->strokeTrim(trimBegin, trimEnd);
if (dash.cnt == 0) { if (dash.cnt == 0) {
if (trimmed) dash.pattern = (float*)malloc(sizeof(float) * 4); if (trimmed) dash.pattern = (float*)malloc(sizeof(float) * 4);
@ -371,7 +373,7 @@ static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix& trans
//must begin with moveTo //must begin with moveTo
if (cmds[0] == PathCommand::MoveTo) { if (cmds[0] == PathCommand::MoveTo) {
if (trimmed) _trimPattern(&dash, rshape, _outlineLength(rshape, 0, 0, simultaneous)); if (trimmed) _trimPattern(&dash, rshape, _outlineLength(rshape, 0, 0, simultaneous), trimBegin, trimEnd);
_dashMoveTo(dash, offIdx, offset, pts); _dashMoveTo(dash, offIdx, offset, pts);
cmds++; cmds++;
pts++; pts++;
@ -386,7 +388,7 @@ static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix& trans
case PathCommand::MoveTo: { case PathCommand::MoveTo: {
if (trimmed) { if (trimmed) {
if (simultaneous) { if (simultaneous) {
_trimPattern(&dash, rshape, _outlineLength(rshape, pts - startPts, cmds - startCmds, true)); _trimPattern(&dash, rshape, _outlineLength(rshape, pts - startPts, cmds - startCmds, true), trimBegin, trimEnd);
_dashMoveTo(dash, offIdx, offset, pts); _dashMoveTo(dash, offIdx, offset, pts);
} else _dashMoveTo(dash, pts); } else _dashMoveTo(dash, pts);
} else _dashMoveTo(dash, offIdx, offset, pts); } else _dashMoveTo(dash, offIdx, offset, pts);

View file

@ -23,6 +23,7 @@
#ifndef _TVG_RENDER_H_ #ifndef _TVG_RENDER_H_
#define _TVG_RENDER_H_ #define _TVG_RENDER_H_
#include <math.h>
#include "tvgCommon.h" #include "tvgCommon.h"
#include "tvgArray.h" #include "tvgArray.h"
#include "tvgLock.h" #include "tvgLock.h"
@ -155,6 +156,32 @@ struct RenderStroke
trim = rhs.trim; trim = rhs.trim;
} }
bool strokeTrim(float& begin, float& end) const
{
begin = trim.begin;
end = trim.end;
if (fabsf(end - begin) > 1.0f) {
begin = 0.0f;
end = 1.0f;
return false;
}
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)) std::swap(begin, end);
return true;
}
~RenderStroke() ~RenderStroke()
{ {
free(dashPattern); free(dashPattern);
@ -199,7 +226,7 @@ struct RenderShape
{ {
if (!stroke) return false; if (!stroke) return false;
if (stroke->trim.begin == 0.0f && stroke->trim.end == 1.0f) return false; if (stroke->trim.begin == 0.0f && stroke->trim.end == 1.0f) return false;
if (stroke->trim.begin == 1.0f && stroke->trim.end == 0.0f) return false; if (fabsf(stroke->trim.end - stroke->trim.begin) > 1.0f) return false;
return true; return true;
} }

View file

@ -217,23 +217,6 @@ struct Shape::Impl
if (tvg::equal(rs.stroke->trim.begin, begin) && tvg::equal(rs.stroke->trim.end, end) && if (tvg::equal(rs.stroke->trim.begin, begin) && tvg::equal(rs.stroke->trim.end, end) &&
rs.stroke->trim.simultaneous == simultaneous) return; 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.begin = begin;
rs.stroke->trim.end = end; rs.stroke->trim.end = end;
rs.stroke->trim.simultaneous = simultaneous; rs.stroke->trim.simultaneous = simultaneous;