mirror of
https://github.com/thorvg/thorvg.git
synced 2025-06-14 12:04:29 +00:00
API, CAPI, sw_engine: add suport for stroke-miterlimit.
This commit is contained in:
parent
07dc68f655
commit
44a750ee5d
13 changed files with 154 additions and 4 deletions
21
inc/thorvg.h
21
inc/thorvg.h
|
@ -990,6 +990,18 @@ public:
|
|||
*/
|
||||
Result stroke(StrokeJoin join) noexcept;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Sets the stroke miterlimit.
|
||||
*
|
||||
* @param[in] miterlimit The miterlimit imposes a limit on the extent of the stroke join, when the @c StrokeJoin::Miter join style is set. The default value is 4.
|
||||
*
|
||||
* @return Result::Success when succeed, Result::NonSupport unsupported value, Result::FailedAllocation otherwise.
|
||||
*
|
||||
* @BETA_API
|
||||
*/
|
||||
Result strokeMiterlimit(float miterlimit) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Sets the solid color for all of the figures from the path.
|
||||
*
|
||||
|
@ -1135,6 +1147,15 @@ public:
|
|||
*/
|
||||
StrokeJoin strokeJoin() const noexcept;
|
||||
|
||||
/**
|
||||
* @brief Gets the stroke miterlimit.
|
||||
*
|
||||
* @return The stroke miterlimit value when succeed, 4 if no stroke was set.
|
||||
*
|
||||
* @BETA_API
|
||||
*/
|
||||
float strokeMiterlimit() const noexcept;
|
||||
|
||||
/**
|
||||
* @brief Creates a new Shape object.
|
||||
*
|
||||
|
|
|
@ -1411,6 +1411,34 @@ TVG_API Tvg_Result tvg_shape_set_stroke_join(Tvg_Paint* paint, Tvg_Stroke_Join j
|
|||
TVG_API Tvg_Result tvg_shape_get_stroke_join(const Tvg_Paint* paint, Tvg_Stroke_Join* join);
|
||||
|
||||
|
||||
/*!
|
||||
* \brief Sets the stroke miterlimit. (BETA_API)
|
||||
*
|
||||
* \param[in] paint A Tvg_Paint pointer to the shape object.
|
||||
* \param[in] miterlimit The miterlimit imposes a limit on the extent of the stroke join when the @c TVG_STROKE_JOIN_MITER join style is set. The default value is 4.
|
||||
*
|
||||
* \return Tvg_Result enumeration.
|
||||
* \retval TVG_RESULT_SUCCESS Succeed.
|
||||
* \retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Paint pointer.
|
||||
* \retval TVG_RESULT_NOT_SUPPORTED Unsupported value.
|
||||
* \retval TVG_RESULT_FAILED_ALLOCATION An internal error with a memory allocation.
|
||||
*/
|
||||
TVG_API Tvg_Result tvg_shape_set_stroke_miterlimit(Tvg_Paint* paint, float miterlimit);
|
||||
|
||||
|
||||
/*!
|
||||
* \brief The function gets the stroke miterlimit. (BETA_API)
|
||||
*
|
||||
* \param[in] paint A Tvg_Paint pointer to the shape object.
|
||||
* \param[out] miterlimit The stroke miterlimit.
|
||||
*
|
||||
* \return Tvg_Result enumeration.
|
||||
* \retval TVG_RESULT_SUCCESS Succeed.
|
||||
* \retval TVG_RESULT_INVALID_ARGUMENT An invalid pointer passed as an argument.
|
||||
*/
|
||||
TVG_API Tvg_Result tvg_shape_get_stroke_miterlimit(const Tvg_Paint* paint, float* miterlimit);
|
||||
|
||||
|
||||
/*!
|
||||
* \brief Sets the shape's solid color.
|
||||
*
|
||||
|
|
|
@ -412,6 +412,22 @@ TVG_API Tvg_Result tvg_shape_get_stroke_join(const Tvg_Paint* paint, Tvg_Stroke_
|
|||
}
|
||||
|
||||
|
||||
TVG_API Tvg_Result tvg_shape_set_stroke_miterlimit(Tvg_Paint* paint, float ml)
|
||||
{
|
||||
if (ml < 0.0f) return TVG_RESULT_NOT_SUPPORTED;
|
||||
if (!paint) return TVG_RESULT_INVALID_ARGUMENT;
|
||||
return (Tvg_Result) reinterpret_cast<Shape*>(paint)->strokeMiterlimit(ml);
|
||||
}
|
||||
|
||||
|
||||
TVG_API Tvg_Result tvg_shape_get_stroke_miterlimit(const Tvg_Paint* paint, float* ml)
|
||||
{
|
||||
if (!paint || !ml) return TVG_RESULT_INVALID_ARGUMENT;
|
||||
*ml = reinterpret_cast<const Shape*>(paint)->strokeMiterlimit();
|
||||
return TVG_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
TVG_API Tvg_Result tvg_shape_set_fill_color(Tvg_Paint* paint, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
|
||||
{
|
||||
if (!paint) return TVG_RESULT_INVALID_ARGUMENT;
|
||||
|
|
|
@ -180,6 +180,7 @@ struct SwStroke
|
|||
SwPoint ptStartSubPath;
|
||||
SwFixed subPathLineLength;
|
||||
SwFixed width;
|
||||
float miterlimit;
|
||||
|
||||
StrokeCap cap;
|
||||
StrokeJoin join;
|
||||
|
|
|
@ -233,8 +233,6 @@ static void _arcTo(SwStroke& stroke, int32_t side)
|
|||
|
||||
static void _outside(SwStroke& stroke, int32_t side, SwFixed lineLength)
|
||||
{
|
||||
constexpr SwFixed MITER_LIMIT = 4 * (1 << 16);
|
||||
|
||||
auto border = stroke.borders + side;
|
||||
|
||||
if (stroke.join == StrokeJoin::Round) {
|
||||
|
@ -257,7 +255,7 @@ static void _outside(SwStroke& stroke, int32_t side, SwFixed lineLength)
|
|||
}
|
||||
|
||||
thcos = mathCos(theta);
|
||||
auto sigma = mathMultiply(MITER_LIMIT, thcos);
|
||||
auto sigma = mathMultiply(stroke.miterlimit, thcos);
|
||||
|
||||
//is miter limit exceeded?
|
||||
if (sigma < 0x10000L) bevel = true;
|
||||
|
@ -844,6 +842,7 @@ void strokeReset(SwStroke* stroke, const RenderShape* rshape, const Matrix* tran
|
|||
|
||||
stroke->width = HALF_STROKE(rshape->strokeWidth());
|
||||
stroke->cap = rshape->strokeCap();
|
||||
stroke->miterlimit = rshape->strokeMiterlimit() * (1 << 16);
|
||||
|
||||
//Save line join: it can be temporarily changed when stroking curves...
|
||||
stroke->joinSaved = stroke->join = rshape->strokeJoin();
|
||||
|
|
|
@ -139,6 +139,7 @@ struct RenderStroke
|
|||
uint32_t dashCnt = 0;
|
||||
StrokeCap cap = StrokeCap::Square;
|
||||
StrokeJoin join = StrokeJoin::Bevel;
|
||||
float miterlimit = 4.0f;
|
||||
bool strokeFirst = false;
|
||||
|
||||
~RenderStroke()
|
||||
|
@ -225,6 +226,13 @@ struct RenderShape
|
|||
if (!stroke) return StrokeJoin::Bevel;
|
||||
return stroke->join;
|
||||
}
|
||||
|
||||
float strokeMiterlimit() const
|
||||
{
|
||||
if (!stroke) return 4.0f;
|
||||
|
||||
return stroke->miterlimit;;
|
||||
}
|
||||
};
|
||||
|
||||
class RenderMethod
|
||||
|
|
|
@ -373,6 +373,17 @@ Result Shape::stroke(StrokeJoin join) noexcept
|
|||
return Result::Success;
|
||||
}
|
||||
|
||||
Result Shape::strokeMiterlimit(float miterlimit) noexcept
|
||||
{
|
||||
// https://www.w3.org/TR/SVG2/painting.html#LineJoin
|
||||
// - A negative value for stroke-miterlimit must be treated as an illegal value.
|
||||
if (miterlimit < 0.0f) return Result::NonSupport;
|
||||
// TODO Find out a reasonable max value.
|
||||
if (!pImpl->strokeMiterlimit(miterlimit)) return Result::FailedAllocation;
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
StrokeCap Shape::strokeCap() const noexcept
|
||||
{
|
||||
|
@ -385,6 +396,11 @@ StrokeJoin Shape::strokeJoin() const noexcept
|
|||
return pImpl->rs.strokeJoin();
|
||||
}
|
||||
|
||||
float Shape::strokeMiterlimit() const noexcept
|
||||
{
|
||||
return pImpl->rs.strokeMiterlimit();
|
||||
}
|
||||
|
||||
|
||||
Result Shape::fill(FillRule r) noexcept
|
||||
{
|
||||
|
|
|
@ -245,6 +245,15 @@ struct Shape::Impl
|
|||
return true;
|
||||
}
|
||||
|
||||
bool strokeMiterlimit(float miterlimit)
|
||||
{
|
||||
if (!rs.stroke) rs.stroke = new RenderStroke();
|
||||
rs.stroke->miterlimit = miterlimit;
|
||||
flag |= RenderUpdateFlag::Stroke;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool strokeColor(uint8_t r, uint8_t g, uint8_t b, uint8_t a)
|
||||
{
|
||||
if (!rs.stroke) rs.stroke = new RenderStroke();
|
||||
|
|
|
@ -983,6 +983,22 @@ static void _handleStrokeLineJoinAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode*
|
|||
node->style->stroke.join = _toLineJoin(value);
|
||||
}
|
||||
|
||||
static void _handleStrokeMiterlimitAttr(SvgLoaderData* loader, SvgNode* node, const char* value)
|
||||
{
|
||||
char* end = nullptr;
|
||||
const float miterlimit = svgUtilStrtof(value, &end);
|
||||
|
||||
// https://www.w3.org/TR/SVG2/painting.html#LineJoin
|
||||
// - A negative value for stroke-miterlimit must be treated as an illegal value.
|
||||
if (miterlimit < 0.0f) {
|
||||
TVGERR("SVG", "A stroke-miterlimit change (%f <- %f) with a negative value is omitted.",
|
||||
node->style->stroke.miterlimit, miterlimit);
|
||||
return;
|
||||
}
|
||||
|
||||
node->style->stroke.flags = (node->style->stroke.flags | SvgStrokeFlags::Miterlimit);
|
||||
node->style->stroke.miterlimit = miterlimit;
|
||||
}
|
||||
|
||||
static void _handleFillRuleAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
|
||||
{
|
||||
|
@ -1099,6 +1115,7 @@ static constexpr struct
|
|||
STYLE_DEF(stroke, Stroke, SvgStyleFlags::Stroke),
|
||||
STYLE_DEF(stroke-width, StrokeWidth, SvgStyleFlags::StrokeWidth),
|
||||
STYLE_DEF(stroke-linejoin, StrokeLineJoin, SvgStyleFlags::StrokeLineJoin),
|
||||
STYLE_DEF(stroke-miterlimit, StrokeMiterlimit, SvgStyleFlags::StrokeMiterlimit),
|
||||
STYLE_DEF(stroke-linecap, StrokeLineCap, SvgStyleFlags::StrokeLineCap),
|
||||
STYLE_DEF(stroke-opacity, StrokeOpacity, SvgStyleFlags::StrokeOpacity),
|
||||
STYLE_DEF(stroke-dasharray, StrokeDashArray, SvgStyleFlags::StrokeDashArray),
|
||||
|
@ -1307,6 +1324,7 @@ static SvgNode* _createNode(SvgNode* parent, SvgNodeType type)
|
|||
node->style->stroke.cap = StrokeCap::Butt;
|
||||
//Default line join is miter
|
||||
node->style->stroke.join = StrokeJoin::Miter;
|
||||
node->style->stroke.miterlimit = 4.0f;
|
||||
node->style->stroke.scale = 1.0;
|
||||
|
||||
node->style->paintOrder = _toPaintOrder("fill stroke");
|
||||
|
@ -2785,6 +2803,9 @@ static void _styleInherit(SvgStyleProperty* child, const SvgStyleProperty* paren
|
|||
if (!(child->stroke.flags & SvgStrokeFlags::Join)) {
|
||||
child->stroke.join = parent->stroke.join;
|
||||
}
|
||||
if (!(child->stroke.flags & SvgStrokeFlags::Miterlimit)) {
|
||||
child->stroke.miterlimit = parent->stroke.miterlimit;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -2848,6 +2869,10 @@ static void _styleCopy(SvgStyleProperty* to, const SvgStyleProperty* from)
|
|||
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,6 +100,7 @@ enum class SvgStrokeFlags
|
|||
Cap = 0x20,
|
||||
Join = 0x40,
|
||||
Dash = 0x80,
|
||||
Miterlimit = 0x100
|
||||
};
|
||||
|
||||
constexpr bool operator &(SvgStrokeFlags a, SvgStrokeFlags b)
|
||||
|
@ -137,7 +138,8 @@ enum class SvgStyleFlags
|
|||
Mask = 0x2000,
|
||||
MaskType = 0x4000,
|
||||
Display = 0x8000,
|
||||
PaintOrder = 0x10000
|
||||
PaintOrder = 0x10000,
|
||||
StrokeMiterlimit = 0x20000
|
||||
};
|
||||
|
||||
constexpr bool operator &(SvgStyleFlags a, SvgStyleFlags b)
|
||||
|
@ -466,6 +468,7 @@ struct SvgStyleStroke
|
|||
float centered;
|
||||
StrokeCap cap;
|
||||
StrokeJoin join;
|
||||
float miterlimit;
|
||||
SvgDash dash;
|
||||
int dashCount;
|
||||
};
|
||||
|
|
|
@ -342,6 +342,7 @@ static void _applyProperty(SvgNode* node, Shape* vg, const Box& vBox, const stri
|
|||
vg->stroke(style->stroke.width);
|
||||
vg->stroke(style->stroke.cap);
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -224,6 +224,7 @@ TEST_CASE("Stroke join", "[capiStrokeJoin]")
|
|||
REQUIRE(paint);
|
||||
|
||||
Tvg_Stroke_Join join;
|
||||
float ml = -1.0f;
|
||||
|
||||
REQUIRE(tvg_shape_set_stroke_join(paint, TVG_STROKE_JOIN_BEVEL) == TVG_RESULT_SUCCESS);
|
||||
REQUIRE(tvg_shape_get_stroke_join(paint, &join) == TVG_RESULT_SUCCESS);
|
||||
|
@ -233,6 +234,20 @@ TEST_CASE("Stroke join", "[capiStrokeJoin]")
|
|||
REQUIRE(tvg_shape_get_stroke_join(paint, &join) == TVG_RESULT_SUCCESS);
|
||||
REQUIRE(join == TVG_STROKE_JOIN_MITER);
|
||||
|
||||
|
||||
REQUIRE(tvg_shape_get_stroke_miterlimit(paint, &ml) == TVG_RESULT_SUCCESS);
|
||||
REQUIRE(ml == 4.0f);
|
||||
|
||||
REQUIRE(tvg_shape_set_stroke_miterlimit(paint, 1000.0f) == TVG_RESULT_SUCCESS);
|
||||
REQUIRE(tvg_shape_get_stroke_miterlimit(paint, &ml) == TVG_RESULT_SUCCESS);
|
||||
REQUIRE(ml == 1000.0f);
|
||||
|
||||
REQUIRE(tvg_shape_set_stroke_miterlimit(paint, -0.001f) == TVG_RESULT_NOT_SUPPORTED);
|
||||
REQUIRE(tvg_shape_get_stroke_miterlimit(paint, &ml) == TVG_RESULT_SUCCESS);
|
||||
REQUIRE(ml == 1000.0f);
|
||||
|
||||
|
||||
|
||||
REQUIRE(tvg_paint_del(paint) == TVG_RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
|
|
|
@ -191,6 +191,14 @@ TEST_CASE("Stroking", "[tvgShape]")
|
|||
REQUIRE(shape->stroke(StrokeJoin::Round) == Result::Success);
|
||||
REQUIRE(shape->strokeJoin() == StrokeJoin::Round);
|
||||
|
||||
//Stroke Miterlimit
|
||||
REQUIRE(shape->strokeMiterlimit() == 4.0f);
|
||||
REQUIRE(shape->strokeMiterlimit(0.00001f) == Result::Success);
|
||||
REQUIRE(shape->strokeMiterlimit() == 0.00001f);
|
||||
REQUIRE(shape->strokeMiterlimit(1000.0f) == Result::Success);
|
||||
REQUIRE(shape->strokeMiterlimit() == 1000.0f);
|
||||
REQUIRE(shape->strokeMiterlimit(-0.001f) == Result::NonSupport);
|
||||
|
||||
//Stroke Order After Stroke Setting
|
||||
REQUIRE(shape->order(true) == Result::Success);
|
||||
REQUIRE(shape->order(false) == Result::Success);
|
||||
|
|
Loading…
Add table
Reference in a new issue