sw_engine: adding a gradient as a stroke feature
authorMira Grudzinska <67589014+mgrudzinska@users.noreply.github.com>
Fri, 19 Feb 2021 08:16:10 +0000 (09:16 +0100)
committerJunsuChoi <jsuya.choi@samsung.com>
Fri, 19 Feb 2021 08:35:25 +0000 (17:35 +0900)
* sw_engine: adding a gradient as a stroke feature

Similarly as a shape may have a gradient fill so can the stroke.

* Capi: adding APIs for a gradient stroke

Co-authored-by: Hermet Park <hermetpark@gmail.com>
inc/thorvg.h
inc/thorvg_capi.h
src/bindings/capi/tvgCapi.cpp
src/lib/sw_engine/tvgSwCommon.h
src/lib/sw_engine/tvgSwRaster.cpp
src/lib/sw_engine/tvgSwRenderer.cpp
src/lib/sw_engine/tvgSwShape.cpp
src/lib/sw_engine/tvgSwStroke.cpp
src/lib/tvgRender.h
src/lib/tvgShape.cpp
src/lib/tvgShapeImpl.h

index 04009df84de2243a411c20008ebe30a221c9ec6d..0c575296d5a7d943512637b9a2c22eed7a0f7c2e 100644 (file)
@@ -230,6 +230,7 @@ public:
     //Stroke
     Result stroke(float width) noexcept;
     Result stroke(uint8_t r, uint8_t g, uint8_t b, uint8_t a) noexcept;
+    Result stroke(std::unique_ptr<Fill> f) noexcept;
     Result stroke(const float* dashPattern, uint32_t cnt) noexcept;
     Result stroke(StrokeCap cap) noexcept;
     Result stroke(StrokeJoin join) noexcept;
@@ -248,6 +249,7 @@ public:
 
     float strokeWidth() const noexcept;
     Result strokeColor(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a) const noexcept;
+    const Fill* strokeFill() const noexcept;
     uint32_t strokeDash(const float** dashPattern) const noexcept;
     StrokeCap strokeCap() const noexcept;
     StrokeJoin strokeJoin() const noexcept;
index 4c98d946add39359a0a764bec64f678871afdded..25ccbdb2d8968363dc7fec25658afe1bead07d60 100644 (file)
@@ -829,6 +829,10 @@ TVG_EXPORT Tvg_Result tvg_shape_set_stroke_color(Tvg_Paint* paint, uint8_t r, ui
 */
 TVG_EXPORT Tvg_Result tvg_shape_get_stroke_color(const Tvg_Paint* paint, uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a);
 
+TVG_EXPORT Tvg_Result tvg_shape_set_stroke_linear_gradient(Tvg_Paint* paint, Tvg_Gradient* grad);
+TVG_EXPORT Tvg_Result tvg_shape_set_stroke_radial_gradient(Tvg_Paint* paint, Tvg_Gradient* grad);
+TVG_EXPORT Tvg_Result tvg_shape_get_stroke_gradient(const Tvg_Paint* paint, Tvg_Gradient** grad);
+
 
 /*!
 * \fn TVG_EXPORT Tvg_Result tvg_shape_set_stroke_dash(Tvg_Paint* paint, const float* dashPattern, uint32_t cnt)
index ff80aedbaad3e669e006c57cc7781afbf17bc55e..410a08f2303263f102ec3bf7727a49c09359b42d 100644 (file)
@@ -313,6 +313,28 @@ TVG_EXPORT Tvg_Result tvg_shape_get_stroke_color(const Tvg_Paint* paint, uint8_t
 }
 
 
+TVG_EXPORT Tvg_Result tvg_shape_set_stroke_linear_gradient(Tvg_Paint* paint, Tvg_Gradient* gradient)
+{
+    if (!paint) return TVG_RESULT_INVALID_ARGUMENT;
+    return (Tvg_Result) reinterpret_cast<Shape*>(paint)->stroke(unique_ptr<LinearGradient>((LinearGradient*)(gradient)));
+}
+
+
+TVG_EXPORT Tvg_Result tvg_shape_set_stroke_radial_gradient(Tvg_Paint* paint, Tvg_Gradient* gradient)
+{
+    if (!paint) return TVG_RESULT_INVALID_ARGUMENT;
+    return (Tvg_Result) reinterpret_cast<Shape*>(paint)->stroke(unique_ptr<RadialGradient>((RadialGradient*)(gradient)));
+}
+
+
+TVG_EXPORT Tvg_Result tvg_shape_stroke_get_gradient(const Tvg_Paint* paint, Tvg_Gradient** gradient)
+{
+   if (!paint || !gradient) return TVG_RESULT_INVALID_ARGUMENT;
+   *gradient = (Tvg_Gradient*)(reinterpret_cast<Shape*>(CCP(paint))->strokeFill());
+   return TVG_RESULT_SUCCESS;
+}
+
+
 TVG_EXPORT Tvg_Result tvg_shape_set_stroke_dash(Tvg_Paint* paint, const float* dashPattern, uint32_t cnt)
 {
     if (!paint) return TVG_RESULT_INVALID_ARGUMENT;
index 24450db819a2265bc7eeb7748af1e6524a566a11..7ecb402136f5d708d10f8fb0205c0fdc7ca0264e 100644 (file)
@@ -127,6 +127,32 @@ struct SwBBox
     SwPoint min, max;
 };
 
+struct SwFill
+{
+    struct SwLinear {
+        float dx, dy;
+        float len;
+        float offset;
+    };
+
+    struct SwRadial {
+        float cx, cy;
+        float a;
+        float inv2a;
+    };
+
+    union {
+        SwLinear linear;
+        SwRadial radial;
+    };
+
+    uint32_t* ctable;
+    FillSpread spread;
+    float sx, sy;
+
+    bool translucent;
+};
+
 struct SwStrokeBorder
 {
     uint32_t ptsCnt;
@@ -152,6 +178,7 @@ struct SwStroke
     StrokeCap cap;
     StrokeJoin join;
     StrokeJoin joinSaved;
+    SwFill* fill = nullptr;
 
     SwStrokeBorder borders[2];
 
@@ -174,32 +201,6 @@ struct SwDashStroke
     bool curOpGap;
 };
 
-struct SwFill
-{
-    struct SwLinear {
-        float dx, dy;
-        float len;
-        float offset;
-    };
-
-    struct SwRadial {
-        float cx, cy;
-        float a;
-        float inv2a;
-    };
-
-    union {
-        SwLinear linear;
-        SwRadial radial;
-    };
-
-    uint32_t* ctable;
-    FillSpread spread;
-    float sx, sy;
-
-    bool translucent;
-};
-
 struct SwShape
 {
     SwOutline*   outline = nullptr;
@@ -297,8 +298,11 @@ bool shapeGenStrokeRle(SwShape* shape, const Shape* sdata, unsigned tid, const M
 void shapeFree(SwShape* shape);
 void shapeDelStroke(SwShape* shape);
 bool shapeGenFillColors(SwShape* shape, const Fill* fill, const Matrix* transform, SwSurface* surface, uint32_t opacity, bool ctable);
+bool shapeGenStrokeFillColors(SwShape* shape, const Fill* fill, const Matrix* transform, SwSurface* surface, uint32_t opacity, bool ctable);
 void shapeResetFill(SwShape* shape);
+void shapeResetStrokeFill(SwShape* shape);
 void shapeDelFill(SwShape* shape);
+void shapeDelStrokeFill(SwShape* shape);
 
 void strokeReset(SwStroke* stroke, const Shape* shape, const Matrix* transform);
 bool strokeParseOutline(SwStroke* stroke, const SwOutline& outline);
@@ -339,6 +343,7 @@ bool rasterGradientShape(SwSurface* surface, SwShape* shape, unsigned id);
 bool rasterSolidShape(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint8_t b, uint8_t a);
 bool rasterImage(SwSurface* surface, SwImage* image, const Matrix* transform, const SwBBox& bbox, uint32_t opacity);
 bool rasterStroke(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint8_t b, uint8_t a);
+bool rasterGradientStroke(SwSurface* surface, SwShape* shape, unsigned id);
 bool rasterClear(SwSurface* surface);
 
 static inline void rasterRGBA32(uint32_t *dst, uint32_t val, uint32_t offset, int32_t len)
index e9ed58122740da7adf703495793d0d815869b364..2262a7879a0e1a841a1d3154a26eae2337ed6599 100644 (file)
@@ -817,6 +817,15 @@ bool rasterStroke(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint
 }
 
 
+bool rasterGradientStroke(SwSurface* surface, SwShape* shape, unsigned id)
+{
+    if (id == FILL_ID_LINEAR) return _rasterLinearGradientRle(surface, shape->strokeRle, shape->stroke->fill);
+    return _rasterRadialGradientRle(surface, shape->strokeRle, shape->stroke->fill);
+
+    return false;
+}
+
+
 bool rasterClear(SwSurface* surface)
 {
     if (!surface || !surface->buffer || surface->stride <= 0 || surface->w <= 0 || surface->h <= 0) return false;
index 470f1a4d48abee2d639e93e2d6060bffe97c7dd5..94e8bc3e1bae4cb0593d547fe986ee8b8b8c0a67 100644 (file)
@@ -76,6 +76,7 @@ struct SwShapeTask : SwTask
         if (HALF_STROKE(strokeWidth) > 0) {
             sdata->strokeColor(nullptr, nullptr, nullptr, &strokeAlpha);
         }
+        bool validStroke = (strokeAlpha > 0) || sdata->strokeFill();
 
         SwSize clip = {static_cast<SwCoord>(surface->w), static_cast<SwCoord>(surface->h)};
 
@@ -89,7 +90,7 @@ struct SwShapeTask : SwTask
             sdata->fillColor(nullptr, nullptr, nullptr, &alpha);
             alpha = static_cast<uint8_t>(static_cast<uint32_t>(alpha) * opacity / 255);
             bool renderShape = (alpha > 0 || sdata->fill());
-            if (renderShape || strokeAlpha) {
+            if (renderShape || validStroke) {
                 shapeReset(&shape);
                 if (!shapePrepare(&shape, sdata, tid, clip, transform, bbox)) goto err;
                 if (renderShape) {
@@ -118,10 +119,18 @@ struct SwShapeTask : SwTask
 
         //Stroke
         if (flags & (RenderUpdateFlag::Stroke | RenderUpdateFlag::Transform)) {
-            if (strokeAlpha > 0) {
+            if (validStroke) {
                 shapeResetStroke(&shape, sdata, transform);
                 if (!shapeGenStrokeRle(&shape, sdata, tid, transform, clip, bbox)) goto err;
                 ++addStroking;
+
+                if (auto fill = sdata->strokeFill()) {
+                    auto ctable = (flags & RenderUpdateFlag::GradientStroke) ? true : false;
+                    if (ctable) shapeResetStrokeFill(&shape);
+                    if (!shapeGenStrokeFillColors(&shape, fill, transform, surface, opacity, ctable)) goto err;
+                } else {
+                    shapeDelStrokeFill(&shape);
+                }
             } else {
                 shapeDelStroke(&shape);
             }
@@ -324,6 +333,9 @@ bool SwRenderer::renderShape(RenderData data)
         if (a > 0) rasterSolidShape(surface, &task->shape, r, g, b, a);
     }
 
+    if (auto strokeFill = task->sdata->strokeFill()) {
+        rasterGradientStroke(surface, &task->shape, strokeFill->id());
+    } else {
     if (task->sdata->strokeColor(&r, &g, &b, &a) == Result::Success) {
         a = static_cast<uint8_t>((opacity * (uint32_t) a) / 255);
         if (a > 0) rasterStroke(surface, &task->shape, r, g, b, a);
index 6a2b4f551437d764ddd45aa88fc39d13a6d7fc87..02284ff10333dcdb626f94a98ab645aa8785122b 100644 (file)
@@ -650,6 +650,12 @@ bool shapeGenFillColors(SwShape* shape, const Fill* fill, const Matrix* transfor
 }
 
 
+bool shapeGenStrokeFillColors(SwShape* shape, const Fill* fill, const Matrix* transform, SwSurface* surface, uint32_t opacity, bool ctable)
+{
+    return fillGenColorTable(shape->stroke->fill, fill, transform, surface, opacity, ctable);
+}
+
+
 void shapeResetFill(SwShape* shape)
 {
     if (!shape->fill) {
@@ -660,9 +666,28 @@ void shapeResetFill(SwShape* shape)
 }
 
 
+void shapeResetStrokeFill(SwShape* shape)
+{
+    if (!shape->stroke->fill) {
+        shape->stroke->fill = static_cast<SwFill*>(calloc(1, sizeof(SwFill)));
+        if (!shape->stroke->fill) return;
+    }
+    fillReset(shape->stroke->fill);
+}
+
+
 void shapeDelFill(SwShape* shape)
 {
     if (!shape->fill) return;
     fillFree(shape->fill);
     shape->fill = nullptr;
 }
+
+
+void shapeDelStrokeFill(SwShape* shape)
+{
+    if (!shape->stroke->fill) return;
+    fillFree(shape->stroke->fill);
+    shape->stroke->fill = nullptr;
+}
+
index 4f4618547d2c0e0d76a51dea6ca08c4dfd2208cc..a93ebb64e004e0e61d515d181c59c8830443e848 100644 (file)
@@ -23,7 +23,6 @@
 #include <math.h>
 #include "tvgSwCommon.h"
 
-
 /************************************************************************/
 /* Internal Class Implementation                                        */
 /************************************************************************/
@@ -821,6 +820,9 @@ void strokeFree(SwStroke* stroke)
     if (stroke->borders[1].pts) free(stroke->borders[1].pts);
     if (stroke->borders[1].tags) free(stroke->borders[1].tags);
 
+    fillFree(stroke->fill);
+    stroke->fill = nullptr;
+
     free(stroke);
 }
 
index 009e56a0dc0cbeabbd43349d625c6538353c9a1c..7815e2161ddbb546536d588f206588889811a867 100644 (file)
@@ -44,7 +44,7 @@ struct Compositor {
     uint32_t        opacity;
 };
 
-enum RenderUpdateFlag {None = 0, Path = 1, Color = 2, Gradient = 4, Stroke = 8, Transform = 16, Image = 32, All = 63};
+enum RenderUpdateFlag {None = 0, Path = 1, Color = 2, Gradient = 4, Stroke = 8, Transform = 16, Image = 32, GradientStroke = 64, All = 127};
 
 struct RenderTransform
 {
index 2ee50ea0f6ecad4ca956f5dd29752a094a5cedd1..d5d6c835943c99e94459befc1ad6237ecdf7261f 100644 (file)
@@ -335,6 +335,20 @@ Result Shape::strokeColor(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a) const
 }
 
 
+Result Shape::stroke(unique_ptr<Fill> f) noexcept
+{
+    return pImpl->strokeFill(move(f));
+}
+
+
+const Fill* Shape::strokeFill() const noexcept
+{
+    if (!pImpl->stroke) return nullptr;
+
+    return pImpl->stroke->fill;
+}
+
+
 Result Shape::stroke(const float* dashPattern, uint32_t cnt) noexcept
 {
     if (cnt < 2 || !dashPattern) return Result::InvalidArguments;
index 67aba9c5d8b9b26f1282bb7be0ff9ecfdecc846e..2281ea0b7fb9d4626405ff7a0bcaf83383918f47 100644 (file)
@@ -33,6 +33,7 @@ struct ShapeStroke
 {
     float width = 0;
     uint8_t color[4] = {0, 0, 0, 0};
+    Fill *fill = nullptr;
     float* dashPattern = nullptr;
     uint32_t dashCnt = 0;
     StrokeCap cap = StrokeCap::Square;
@@ -49,11 +50,14 @@ struct ShapeStroke
         memcpy(color, src->color, sizeof(color));
         dashPattern = static_cast<float*>(malloc(sizeof(float) * dashCnt));
         memcpy(dashPattern, src->dashPattern, sizeof(float) * dashCnt);
+        if (src->fill)
+            fill = src->fill->duplicate();
     }
 
     ~ShapeStroke()
     {
         if (dashPattern) free(dashPattern);
+        if (fill) delete(fill);
     }
 };
 
@@ -291,6 +295,12 @@ struct Shape::Impl
         if (!stroke) stroke = new ShapeStroke();
         if (!stroke) return false;
 
+        if (stroke->fill) {
+            delete(stroke->fill);
+            stroke->fill = nullptr;
+            flag |= RenderUpdateFlag::GradientStroke;
+        }
+
         stroke->color[0] = r;
         stroke->color[1] = g;
         stroke->color[2] = b;
@@ -301,6 +311,23 @@ struct Shape::Impl
         return true;
     }
 
+    Result strokeFill(unique_ptr<Fill> f)
+    {
+        auto p = f.release();
+        if (!p) return Result::MemoryCorruption;
+
+        if (!stroke) stroke = new ShapeStroke();
+        if (!stroke) return Result::FailedAllocation;
+
+        if (stroke->fill && stroke->fill != p) delete(stroke->fill);
+        stroke->fill = p;
+
+        flag |= RenderUpdateFlag::Stroke;
+        flag |= RenderUpdateFlag::GradientStroke;
+
+        return Result::Success;
+    }
+
     bool strokeDash(const float* pattern, uint32_t cnt)
     {
        if (!stroke) stroke = new ShapeStroke();
@@ -363,6 +390,9 @@ struct Shape::Impl
         if (stroke) {
             dup->stroke = new ShapeStroke(stroke);
             dup->flag |= RenderUpdateFlag::Stroke;
+
+            if (stroke->fill)
+                dup->flag |= RenderUpdateFlag::GradientStroke;
         }
 
         //Fill