From 69a958335405f594bc54effca2c36ccc1897e291 Mon Sep 17 00:00:00 2001 From: Mira Grudzinska Date: Wed, 5 Apr 2023 02:25:14 +0200 Subject: [PATCH] common: order(bool strokeFirst) api introduced The new api is introduced to handle the rendering order of a stroke and a fill. By default fill is rendered as the first one, stroke as the second one. This order can be reversed by calling order(true). @Issue: https://github.com/thorvg/thorvg/issues/1340 --- inc/thorvg.h | 12 +++++++ src/lib/sw_engine/tvgSwRenderer.cpp | 50 ++++++++++++++++++----------- src/lib/tvgRender.h | 1 + src/lib/tvgShape.cpp | 10 +++++- src/lib/tvgShapeImpl.h | 10 ++++++ 5 files changed, 64 insertions(+), 19 deletions(-) diff --git a/inc/thorvg.h b/inc/thorvg.h index 1fd0f903..9315f19f 100644 --- a/inc/thorvg.h +++ b/inc/thorvg.h @@ -977,6 +977,18 @@ public: */ Result fill(FillRule r) noexcept; + + /** + * @brief Sets the rendering order of the stroke and the fill. + * + * @param[in] strokeFirst If @c true the stroke is rendered before the fill, otherwise the stroke is rendered as the second one (the default option). + * + * @return Result::Success when succeed, Result::FailedAllocation otherwise. + * @BETA_API + */ + Result order(bool strokeFirst) noexcept; + + /** * @brief Gets the commands data of the path. * diff --git a/src/lib/sw_engine/tvgSwRenderer.cpp b/src/lib/sw_engine/tvgSwRenderer.cpp index 412cbe0b..fddcd31a 100644 --- a/src/lib/sw_engine/tvgSwRenderer.cpp +++ b/src/lib/sw_engine/tvgSwRenderer.cpp @@ -96,7 +96,7 @@ struct SwShapeTask : SwTask } void run(unsigned tid) override - { + { if (opacity == 0 && !clipper) return; //Invisible uint8_t strokeAlpha = 0; @@ -136,7 +136,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) ? false : true; + auto antiAlias = strokeAlpha < 255 || rshape->strokeWidth() <= 2 || rshape->strokeDash(nullptr) > 0 || (rshape->stroke && rshape->stroke->strokeFirst); if (!shapeGenRle(&shape, rshape, antiAlias)) goto err; } @@ -322,6 +322,31 @@ static void _termEngine() } +static void _renderFill(SwShapeTask* task, SwSurface* surface, uint32_t opacity) +{ + uint8_t r, g, b, a; + if (auto fill = task->rshape->fill) { + rasterGradientShape(surface, &task->shape, fill->identifier()); + } else { + task->rshape->fillColor(&r, &g, &b, &a); + a = static_cast((opacity * (uint32_t) a) / 255); + if (a > 0) rasterShape(surface, &task->shape, r, g, b, a); + } +} + +static void _renderStroke(SwShapeTask* task, SwSurface* surface, uint32_t opacity) +{ + uint8_t r, g, b, a; + if (auto strokeFill = task->rshape->strokeFill()) { + rasterGradientStroke(surface, &task->shape, strokeFill->identifier()); + } else { + if (task->rshape->strokeColor(&r, &g, &b, &a)) { + a = static_cast((opacity * (uint32_t) a) / 255); + if (a > 0) rasterStroke(surface, &task->shape, r, g, b, a); + } + } +} + /************************************************************************/ /* External Class Implementation */ /************************************************************************/ @@ -482,23 +507,12 @@ bool SwRenderer::renderShape(RenderData data) } //Main raster stage - uint8_t r, g, b, a; - - if (auto fill = task->rshape->fill) { - rasterGradientShape(surface, &task->shape, fill->identifier()); + if (task->rshape->stroke && task->rshape->stroke->strokeFirst) { + _renderStroke(task, surface, opacity); + _renderFill(task, surface, opacity); } else { - task->rshape->fillColor(&r, &g, &b, &a); - a = static_cast((opacity * (uint32_t) a) / 255); - if (a > 0) rasterShape(surface, &task->shape, r, g, b, a); - } - - if (auto strokeFill = task->rshape->strokeFill()) { - rasterGradientStroke(surface, &task->shape, strokeFill->identifier()); - } else { - if (task->rshape->strokeColor(&r, &g, &b, &a)) { - a = static_cast((opacity * (uint32_t) a) / 255); - if (a > 0) rasterStroke(surface, &task->shape, r, g, b, a); - } + _renderFill(task, surface, opacity); + _renderStroke(task, surface, opacity); } if (task->cmpStroking) endComposite(cmp); diff --git a/src/lib/tvgRender.h b/src/lib/tvgRender.h index a52f203c..56a97a85 100644 --- a/src/lib/tvgRender.h +++ b/src/lib/tvgRender.h @@ -94,6 +94,7 @@ struct RenderStroke uint32_t dashCnt = 0; StrokeCap cap = StrokeCap::Square; StrokeJoin join = StrokeJoin::Bevel; + bool strokeFirst = false; ~RenderStroke() { diff --git a/src/lib/tvgShape.cpp b/src/lib/tvgShape.cpp index ab07ed65..114ff61e 100644 --- a/src/lib/tvgShape.cpp +++ b/src/lib/tvgShape.cpp @@ -288,6 +288,14 @@ const Fill* Shape::fill() const noexcept } +Result Shape::order(bool strokeFirst) noexcept +{ + if (!pImpl->strokeFirst(strokeFirst)) return Result::FailedAllocation; + + return Result::Success; +} + + Result Shape::stroke(float width) noexcept { if (!pImpl->strokeWidth(width)) return Result::FailedAllocation; @@ -390,4 +398,4 @@ Result Shape::fill(FillRule r) noexcept FillRule Shape::fillRule() const noexcept { return pImpl->rs.rule; -} +} \ No newline at end of file diff --git a/src/lib/tvgShapeImpl.h b/src/lib/tvgShapeImpl.h index 824142a8..da288756 100644 --- a/src/lib/tvgShapeImpl.h +++ b/src/lib/tvgShapeImpl.h @@ -262,6 +262,15 @@ struct Shape::Impl return true; } + bool strokeFirst(bool strokeFirst) + { + if (!rs.stroke) rs.stroke = new RenderStroke(); + rs.stroke->strokeFirst = strokeFirst; + flag |= RenderUpdateFlag::Stroke; + + return true; + } + Paint* duplicate() { auto ret = Shape::gen(); @@ -295,6 +304,7 @@ struct Shape::Impl dup->rs.stroke->dashCnt = rs.stroke->dashCnt; dup->rs.stroke->cap = rs.stroke->cap; dup->rs.stroke->join = rs.stroke->join; + dup->rs.stroke->strokeFirst = rs.stroke->strokeFirst; memcpy(dup->rs.stroke->color, rs.stroke->color, sizeof(rs.stroke->color)); if (rs.stroke->dashCnt > 0) {