common paint: introduce opacity() method.
authorHermet Park <chuneon.park@samsung.com>
Mon, 26 Oct 2020 09:57:11 +0000 (18:57 +0900)
committerHermet Park <chuneon.park@samsung.com>
Mon, 2 Nov 2020 04:09:35 +0000 (13:09 +0900)
We introduced separate opacity interface to adjust alpha value by paint.
This opacity will affect to whole paint image if paint is a group of paints.

Also, this opacity is to multipy with fill/stroke alpha values.
This means if the opacity is valid, the paint might deal with a composition step,
which is very expensive due to additional rendering step.

One tip is, if you want to toggle on/off for a certian paint,
you can set opacity to 255 or 0.

@API Additions:

Result Paint::opacity(uint8_t o) noexcept;
uint8_t Paint::opacity() const noexcept;

@Examples: examples/Opacity

@Issues: 94

Change-Id: Ie291890304230c34006fbe7f96a8707e01d9439c

14 files changed:
inc/thorvg.h
src/examples/Opacity.cpp [new file with mode: 0644]
src/examples/meson.build
src/lib/gl_engine/tvgGlRenderer.cpp
src/lib/gl_engine/tvgGlRenderer.h
src/lib/sw_engine/tvgSwRenderer.cpp
src/lib/sw_engine/tvgSwRenderer.h
src/lib/tvgCanvasImpl.h
src/lib/tvgPaint.cpp
src/lib/tvgPaint.h
src/lib/tvgPictureImpl.h
src/lib/tvgRender.h
src/lib/tvgSceneImpl.h
src/lib/tvgShapeImpl.h

index 8b101e1..84bbb30 100644 (file)
@@ -93,10 +93,13 @@ public:
     Result translate(float x, float y) noexcept;
     Result transform(const Matrix& m) noexcept;
     Result bounds(float* x, float* y, float* w, float* h) const noexcept;
+    Result opacity(uint8_t o) noexcept;
     Paint* duplicate() const noexcept;
 
     Result composite(std::unique_ptr<Paint> target, CompositeMethod method) const noexcept;
 
+    uint8_t opacity() const noexcept;
+
     _TVG_DECLARE_ACCESSOR();
     _TVG_DECLARE_PRIVATE(Paint);
 };
diff --git a/src/examples/Opacity.cpp b/src/examples/Opacity.cpp
new file mode 100644 (file)
index 0000000..3b9dd44
--- /dev/null
@@ -0,0 +1,166 @@
+#include "Common.h"
+
+/************************************************************************/
+/* Drawing Commands                                                     */
+/************************************************************************/
+
+void tvgDrawCmds(tvg::Canvas* canvas)
+{
+    if (!canvas) return;
+
+    //Prepare Circle
+    auto shape1 = tvg::Shape::gen();
+    shape1->appendCircle(400, 400, 250, 250);
+    shape1->fill(255, 255, 0, 255);
+    canvas->push(move(shape1));
+
+    //Create a Scene
+    auto scene = tvg::Scene::gen();
+    scene->opacity(127);              //Apply opacity to scene (0 - 255)
+    scene->reserve(2);
+
+    //Star
+    auto shape2 = tvg::Shape::gen();
+
+    //Appends Paths
+    shape2->moveTo(199, 34);
+    shape2->lineTo(253, 143);
+    shape2->lineTo(374, 160);
+    shape2->lineTo(287, 244);
+    shape2->lineTo(307, 365);
+    shape2->lineTo(199, 309);
+    shape2->lineTo(97, 365);
+    shape2->lineTo(112, 245);
+    shape2->lineTo(26, 161);
+    shape2->lineTo(146, 143);
+    shape2->close();
+    shape2->fill(0, 0, 255, 255);
+    scene->push(move(shape2));
+
+    //Circle
+    auto shape3 = tvg::Shape::gen();
+
+    auto cx = 550.0f;
+    auto cy = 550.0f;
+    auto radius = 125.0f;
+    auto halfRadius = radius * 0.552284f;
+
+    //Append Paths
+    shape3->moveTo(cx, cy - radius);
+    shape3->cubicTo(cx + halfRadius, cy - radius, cx + radius, cy - halfRadius, cx + radius, cy);
+    shape3->cubicTo(cx + radius, cy + halfRadius, cx + halfRadius, cy + radius, cx, cy+ radius);
+    shape3->cubicTo(cx - halfRadius, cy + radius, cx - radius, cy + halfRadius, cx - radius, cy);
+    shape3->cubicTo(cx - radius, cy - halfRadius, cx - halfRadius, cy - radius, cx, cy - radius);
+    shape3->fill(255, 0, 0, 255);
+    scene->push(move(shape3));
+
+    //Draw the Scene onto the Canvas
+    canvas->push(move(scene));
+}
+
+
+/************************************************************************/
+/* Sw Engine Test Code                                                  */
+/************************************************************************/
+
+static unique_ptr<tvg::SwCanvas> swCanvas;
+
+void tvgSwTest(uint32_t* buffer)
+{
+    //Create a Canvas
+    swCanvas = tvg::SwCanvas::gen();
+    swCanvas->target(buffer, WIDTH, WIDTH, HEIGHT, tvg::SwCanvas::ARGB8888);
+
+    /* Push the shape into the Canvas drawing list
+       When this shape is into the canvas list, the shape could update & prepare
+       internal data asynchronously for coming rendering.
+       Canvas keeps this shape node unless user call canvas->clear() */
+    tvgDrawCmds(swCanvas.get());
+}
+
+void drawSwView(void* data, Eo* obj)
+{
+    if (swCanvas->draw() == tvg::Result::Success) {
+        swCanvas->sync();
+    }
+}
+
+
+/************************************************************************/
+/* GL Engine Test Code                                                  */
+/************************************************************************/
+
+static unique_ptr<tvg::GlCanvas> glCanvas;
+
+void initGLview(Evas_Object *obj)
+{
+    static constexpr auto BPP = 4;
+
+    //Create a Canvas
+    glCanvas = tvg::GlCanvas::gen();
+    glCanvas->target(nullptr, WIDTH * BPP, WIDTH, HEIGHT);
+
+    /* Push the shape into the Canvas drawing list
+       When this shape is into the canvas list, the shape could update & prepare
+       internal data asynchronously for coming rendering.
+       Canvas keeps this shape node unless user call canvas->clear() */
+    tvgDrawCmds(glCanvas.get());
+}
+
+void drawGLview(Evas_Object *obj)
+{
+    auto gl = elm_glview_gl_api_get(obj);
+    gl->glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
+    gl->glClear(GL_COLOR_BUFFER_BIT);
+
+    if (glCanvas->draw() == tvg::Result::Success) {
+        glCanvas->sync();
+    }
+}
+
+
+/************************************************************************/
+/* Main Code                                                            */
+/************************************************************************/
+
+int main(int argc, char **argv)
+{
+    tvg::CanvasEngine tvgEngine = tvg::CanvasEngine::Sw;
+
+    if (argc > 1) {
+        if (!strcmp(argv[1], "gl")) tvgEngine = tvg::CanvasEngine::Gl;
+    }
+
+    //Initialize ThorVG Engine
+    if (tvgEngine == tvg::CanvasEngine::Sw) {
+        cout << "tvg engine: software" << endl;
+    } else {
+        cout << "tvg engine: opengl" << endl;
+    }
+
+    //Threads Count
+    auto threads = std::thread::hardware_concurrency();
+
+    //Initialize ThorVG Engine
+    if (tvg::Initializer::init(tvgEngine, threads) == tvg::Result::Success) {
+
+
+        elm_init(argc, argv);
+
+        if (tvgEngine == tvg::CanvasEngine::Sw) {
+            createSwView();
+        } else {
+            createGlView();
+        }
+
+        elm_run();
+        elm_shutdown();
+
+        //Terminate ThorVG Engine
+        tvg::Initializer::term(tvgEngine);
+
+    } else {
+        cout << "engine is not supported" << endl;
+    }
+    return 0;
+}
index 43ad4fa..bb9d576 100644 (file)
@@ -14,6 +14,7 @@ source_file = [
     'LinearGradient.cpp',
     'MultiCanvas.cpp',
     'MultiShapes.cpp',
+    'Opacity.cpp',
     'PathCopy.cpp',
     'Path.cpp',
     'RadialGradient.cpp',
index b7cbfc4..54df5d5 100644 (file)
@@ -140,7 +140,7 @@ bool GlRenderer::dispose(TVG_UNUSED const Shape& shape, void *data)
 }
 
 
-void* GlRenderer::prepare(const Shape& shape, void* data, TVG_UNUSED const RenderTransform* transform, vector<Composite>& compList, RenderUpdateFlag flags)
+void* GlRenderer::prepare(const Shape& shape, void* data, TVG_UNUSED const RenderTransform* transform, TVG_UNUSED uint32_t opacity, vector<Composite>& compList, RenderUpdateFlag flags)
 {
     //prepare shape data
     GlShape* sdata = static_cast<GlShape*>(data);
index c545f82..d7d80f3 100644 (file)
@@ -30,7 +30,7 @@ class GlRenderer : public RenderMethod
 public:
     Surface surface = {nullptr, 0, 0, 0};
 
-    void* prepare(const Shape& shape, void* data, const RenderTransform* transform, vector<Composite>& compList, RenderUpdateFlag flags) override;
+    void* prepare(const Shape& shape, void* data, const RenderTransform* transform, uint32_t opacity, vector<Composite>& compList, RenderUpdateFlag flags) override;
     bool dispose(const Shape& shape, void *data) override;
     bool preRender() override;
     bool render(const Shape& shape, void *data) override;
index 8f6a1df..efeb0f3 100644 (file)
@@ -37,9 +37,12 @@ struct SwTask : Task
     SwSurface* surface = nullptr;
     RenderUpdateFlag flags = RenderUpdateFlag::None;
     vector<Composite> compList;
+    uint32_t opacity;
 
     void run() override
     {
+        if (opacity == 0) return;  //Invisible
+
         //Valid Stroking?
         uint8_t strokeAlpha = 0;
         auto strokeWidth = sdata->strokeWidth();
@@ -51,12 +54,13 @@ struct SwTask : Task
 
         //Invisiable shape turned to visible by alpha.
         auto prepareShape = false;
-        if (!shapePrepared(&shape) && (flags & RenderUpdateFlag::Color)) prepareShape = true;
+        if (!shapePrepared(&shape) && ((flags & RenderUpdateFlag::Color) || (opacity > 0))) prepareShape = true;
 
         //Shape
         if (flags & (RenderUpdateFlag::Path | RenderUpdateFlag::Transform) || prepareShape) {
             uint8_t alpha = 0;
             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) {
                 shapeReset(&shape);
@@ -181,12 +185,15 @@ bool SwRenderer::render(const Shape& shape, void *data)
 
     uint8_t r, g, b, a;
     if (auto fill = task->sdata->fill()) {
+        //FIXME: pass opacity to apply gradient fill?
         rasterGradientShape(surface, &task->shape, fill->id());
     } else{
         task->sdata->fillColor(&r, &g, &b, &a);
+        a = static_cast<uint8_t>((task->opacity * (uint32_t) a) / 255);
         if (a > 0) rasterSolidShape(surface, &task->shape, r, g, b, a);
     }
     task->sdata->strokeColor(&r, &g, &b, &a);
+    a = static_cast<uint8_t>((task->opacity * (uint32_t) a) / 255);
     if (a > 0) rasterStroke(surface, &task->shape, r, g, b, a);
 
     return true;
@@ -207,7 +214,7 @@ bool SwRenderer::dispose(TVG_UNUSED const Shape& sdata, void *data)
 }
 
 
-void* SwRenderer::prepare(const Shape& sdata, void* data, const RenderTransform* transform, vector<Composite>& compList, RenderUpdateFlag flags)
+void* SwRenderer::prepare(const Shape& sdata, void* data, const RenderTransform* transform, uint32_t opacity, vector<Composite>& compList, RenderUpdateFlag flags)
 {
     //prepare task
     auto task = static_cast<SwTask*>(data);
@@ -237,6 +244,7 @@ void* SwRenderer::prepare(const Shape& sdata, void* data, const RenderTransform*
         task->transform = nullptr;
     }
 
+    task->opacity = opacity;
     task->surface = surface;
     task->flags = flags;
 
index 417d3a7..9453ec3 100644 (file)
@@ -34,7 +34,7 @@ namespace tvg
 class SwRenderer : public RenderMethod
 {
 public:
-    void* prepare(const Shape& shape, void* data, const RenderTransform* transform, vector<Composite>& compList, RenderUpdateFlag flags) override;
+    void* prepare(const Shape& shape, void* data, const RenderTransform* transform, uint32_t opacity, vector<Composite>& compList, RenderUpdateFlag flags) override;
     bool dispose(const Shape& shape, void *data) override;
     bool preRender() override;
     bool postRender() override;
index 06a54af..755dabd 100644 (file)
@@ -81,11 +81,11 @@ struct Canvas::Impl
 
         //Update single paint node
         if (paint) {
-            paint->pImpl->update(*renderer, nullptr, compList, RenderUpdateFlag::None);
+            paint->pImpl->update(*renderer, nullptr, 255, compList, RenderUpdateFlag::None);
         //Update all retained paint nodes
         } else {
             for (auto paint: paints) {
-                paint->pImpl->update(*renderer, nullptr, compList, RenderUpdateFlag::None);
+                paint->pImpl->update(*renderer, nullptr, 255, compList, RenderUpdateFlag::None);
             }
         }
         return Result::Success;
index cdf0c25..0d47641 100644 (file)
@@ -67,20 +67,39 @@ Result Paint::transform(const Matrix& m) noexcept
     return Result::FailedAllocation;
 }
 
+
 Result Paint::bounds(float* x, float* y, float* w, float* h) const noexcept
 {
     if (pImpl->bounds(x, y, w, h)) return Result::Success;
     return Result::InsufficientCondition;
 }
 
+
 Paint* Paint::duplicate() const noexcept
 {
     return pImpl->duplicate();
 }
 
+
 Result Paint::composite(std::unique_ptr<Paint> target, CompositeMethod method) const noexcept
 {
     if (pImpl->composite(target.release(), method)) return Result::Success;
     return Result::InsufficientCondition;
 }
 
+
+Result Paint::opacity(uint8_t o) noexcept
+{
+    if (pImpl->opacity == o) return Result::Success;
+
+    pImpl->opacity = o;
+    pImpl->flag |= RenderUpdateFlag::Color;
+
+    return Result::Success;
+}
+
+
+uint8_t Paint::opacity() const noexcept
+{
+    return pImpl->opacity;
+}
index 2c4a63d..11bfad7 100644 (file)
@@ -33,7 +33,7 @@ namespace tvg
         virtual ~StrategyMethod(){}
 
         virtual bool dispose(RenderMethod& renderer) = 0;
-        virtual void* update(RenderMethod& renderer, const RenderTransform* transform, vector<Composite> compList, RenderUpdateFlag pFlag) = 0;   //Return engine data if it has.
+        virtual void* update(RenderMethod& renderer, const RenderTransform* transform, uint32_t opacity, vector<Composite> compList, RenderUpdateFlag pFlag) = 0;   //Return engine data if it has.
         virtual bool render(RenderMethod& renderer) = 0;
         virtual bool bounds(float* x, float* y, float* w, float* h) const = 0;
         virtual Paint* duplicate() = 0;
@@ -48,6 +48,8 @@ namespace tvg
         Paint* compTarget = nullptr;
         CompositeMethod compMethod = CompositeMethod::None;
 
+        uint8_t opacity = 255;
+
         ~Impl() {
             if (smethod) delete(smethod);
             if (rTransform) delete(rTransform);
@@ -127,7 +129,7 @@ namespace tvg
             return smethod->dispose(renderer);
         }
 
-        void* update(RenderMethod& renderer, const RenderTransform* pTransform, vector<Composite>& compList, uint32_t pFlag)
+        void* update(RenderMethod& renderer, const RenderTransform* pTransform, uint32_t opacity, vector<Composite>& compList, uint32_t pFlag)
         {
             if (flag & RenderUpdateFlag::Transform) {
                 if (!rTransform) return nullptr;
@@ -140,20 +142,21 @@ namespace tvg
             void *compdata = nullptr;
 
             if (compTarget && compMethod == CompositeMethod::ClipPath) {
-                compdata = compTarget->pImpl->update(renderer, pTransform, compList, pFlag);
+                compdata = compTarget->pImpl->update(renderer, pTransform, opacity, compList, pFlag);
                 if (compdata) compList.push_back({compdata, compMethod});
             }
 
             void *edata = nullptr;
             auto newFlag = static_cast<RenderUpdateFlag>(pFlag | flag);
             flag = RenderUpdateFlag::None;
+            opacity = (opacity * this->opacity) / 255;
 
             if (rTransform && pTransform) {
                 RenderTransform outTransform(pTransform, rTransform);
-                edata = smethod->update(renderer, &outTransform, compList, newFlag);
+                edata = smethod->update(renderer, &outTransform, opacity, compList, newFlag);
             } else {
                 auto outTransform = pTransform ? pTransform : rTransform;
-                edata = smethod->update(renderer, outTransform, compList, newFlag);
+                edata = smethod->update(renderer, outTransform, opacity, compList, newFlag);
             }
 
             if (compdata) compList.pop_back();
@@ -179,6 +182,8 @@ namespace tvg
                 }
             }
 
+            ret->pImpl->opacity = opacity;
+
             return ret;
         }
 
@@ -210,9 +215,9 @@ namespace tvg
             return inst->dispose(renderer);
         }
 
-        void* update(RenderMethod& renderer, const RenderTransform* transform, vector<Composite> compList, RenderUpdateFlag flag) override
+        void* update(RenderMethod& renderer, const RenderTransform* transform, uint32_t opacity, vector<Composite> compList, RenderUpdateFlag flag) override
         {
-            return inst->update(renderer, transform, compList, flag);
+            return inst->update(renderer, transform, opacity, compList, flag);
         }
 
         bool render(RenderMethod& renderer) override
index 1ac191a..7ae736c 100644 (file)
@@ -58,13 +58,13 @@ struct Picture::Impl
     }
 
 
-    void* update(RenderMethod &renderer, const RenderTransform* transform, vector<Composite>& compList, RenderUpdateFlag flag)
+    void* update(RenderMethod &renderer, const RenderTransform* transform, uint32_t opacity, vector<Composite>& compList, RenderUpdateFlag flag)
     {
         reload();
 
         if (!paint) return nullptr;
 
-        return paint->pImpl->update(renderer, transform, compList, flag);
+        return paint->pImpl->update(renderer, transform, opacity, compList, flag);
     }
 
     bool render(RenderMethod &renderer)
index 4575263..666463b 100644 (file)
@@ -65,7 +65,7 @@ class RenderMethod
 {
 public:
     virtual ~RenderMethod() {}
-    virtual void* prepare(TVG_UNUSED const Shape& shape, TVG_UNUSED void* data, TVG_UNUSED const RenderTransform* transform, TVG_UNUSED vector<Composite>& compList, TVG_UNUSED RenderUpdateFlag flags) { return nullptr; }
+    virtual void* prepare(TVG_UNUSED const Shape& shape, TVG_UNUSED void* data, TVG_UNUSED const RenderTransform* transform, uint32_t opacity, TVG_UNUSED vector<Composite>& compList, TVG_UNUSED RenderUpdateFlag flags) { return nullptr; }
     virtual bool dispose(TVG_UNUSED const Shape& shape, TVG_UNUSED void *data) { return true; }
     virtual bool preRender() { return true; }
     virtual bool render(TVG_UNUSED const Shape& shape, TVG_UNUSED void *data) { return true; }
index b6e83d5..68fcff8 100644 (file)
@@ -44,14 +44,14 @@ struct Scene::Impl
         return true;
     }
 
-    void* update(RenderMethod &renderer, const RenderTransform* transform, vector<Composite>& compList, RenderUpdateFlag flag)
+    void* update(RenderMethod &renderer, const RenderTransform* transform, uint32_t opacity, vector<Composite>& compList, RenderUpdateFlag flag)
     {
         /* FXIME: it requires to return list of childr engine data
            This is necessary for scene composition */
         void* edata = nullptr;
 
         for (auto paint: paints) {
-            edata = paint->pImpl->update(renderer, transform, compList, static_cast<uint32_t>(flag));
+            edata = paint->pImpl->update(renderer, transform, opacity, compList, static_cast<uint32_t>(flag));
         }
         return edata;
     }
index 982b62a..c69b844 100644 (file)
@@ -222,9 +222,9 @@ struct Shape::Impl
         return renderer.render(*shape, edata);
     }
 
-    void* update(RenderMethod& renderer, const RenderTransform* transform, vector<Composite>& compList, RenderUpdateFlag pFlag)
+    void* update(RenderMethod& renderer, const RenderTransform* transform, uint32_t opacity, vector<Composite>& compList, RenderUpdateFlag pFlag)
     {
-        this->edata = renderer.prepare(*shape, this->edata, transform, compList, static_cast<RenderUpdateFlag>(pFlag | flag));
+        this->edata = renderer.prepare(*shape, this->edata, transform, opacity, compList, static_cast<RenderUpdateFlag>(pFlag | flag));
         flag = RenderUpdateFlag::None;
         return this->edata;
     }