From 765ee0a60a03896d3ab57af3de79dc85c8c629aa Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Mon, 26 Oct 2020 18:57:11 +0900 Subject: [PATCH] common paint: introduce opacity() method. 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 --- inc/thorvg.h | 3 + src/examples/Opacity.cpp | 166 ++++++++++++++++++++++++++++ src/examples/meson.build | 1 + src/lib/gl_engine/tvgGlRenderer.cpp | 2 +- src/lib/gl_engine/tvgGlRenderer.h | 2 +- src/lib/sw_engine/tvgSwRenderer.cpp | 12 +- src/lib/sw_engine/tvgSwRenderer.h | 2 +- src/lib/tvgCanvasImpl.h | 4 +- src/lib/tvgPaint.cpp | 19 ++++ src/lib/tvgPaint.h | 19 ++-- src/lib/tvgPictureImpl.h | 4 +- src/lib/tvgRender.h | 2 +- src/lib/tvgSceneImpl.h | 4 +- src/lib/tvgShapeImpl.h | 4 +- 14 files changed, 223 insertions(+), 21 deletions(-) create mode 100644 src/examples/Opacity.cpp diff --git a/inc/thorvg.h b/inc/thorvg.h index 8b101e15..84bbb308 100644 --- a/inc/thorvg.h +++ b/inc/thorvg.h @@ -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 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 index 00000000..3b9dd44e --- /dev/null +++ b/src/examples/Opacity.cpp @@ -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 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 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; +} diff --git a/src/examples/meson.build b/src/examples/meson.build index 43ad4fa1..bb9d576d 100644 --- a/src/examples/meson.build +++ b/src/examples/meson.build @@ -14,6 +14,7 @@ source_file = [ 'LinearGradient.cpp', 'MultiCanvas.cpp', 'MultiShapes.cpp', + 'Opacity.cpp', 'PathCopy.cpp', 'Path.cpp', 'RadialGradient.cpp', diff --git a/src/lib/gl_engine/tvgGlRenderer.cpp b/src/lib/gl_engine/tvgGlRenderer.cpp index b7cbfc4e..54df5d51 100644 --- a/src/lib/gl_engine/tvgGlRenderer.cpp +++ b/src/lib/gl_engine/tvgGlRenderer.cpp @@ -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& compList, RenderUpdateFlag flags) +void* GlRenderer::prepare(const Shape& shape, void* data, TVG_UNUSED const RenderTransform* transform, TVG_UNUSED uint32_t opacity, vector& compList, RenderUpdateFlag flags) { //prepare shape data GlShape* sdata = static_cast(data); diff --git a/src/lib/gl_engine/tvgGlRenderer.h b/src/lib/gl_engine/tvgGlRenderer.h index c545f82a..d7d80f32 100644 --- a/src/lib/gl_engine/tvgGlRenderer.h +++ b/src/lib/gl_engine/tvgGlRenderer.h @@ -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& compList, RenderUpdateFlag flags) override; + void* prepare(const Shape& shape, void* data, const RenderTransform* transform, uint32_t opacity, vector& compList, RenderUpdateFlag flags) override; bool dispose(const Shape& shape, void *data) override; bool preRender() override; bool render(const Shape& shape, void *data) override; diff --git a/src/lib/sw_engine/tvgSwRenderer.cpp b/src/lib/sw_engine/tvgSwRenderer.cpp index 8f6a1df9..efeb0f33 100644 --- a/src/lib/sw_engine/tvgSwRenderer.cpp +++ b/src/lib/sw_engine/tvgSwRenderer.cpp @@ -37,9 +37,12 @@ struct SwTask : Task SwSurface* surface = nullptr; RenderUpdateFlag flags = RenderUpdateFlag::None; vector 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(static_cast(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((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((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& compList, RenderUpdateFlag flags) +void* SwRenderer::prepare(const Shape& sdata, void* data, const RenderTransform* transform, uint32_t opacity, vector& compList, RenderUpdateFlag flags) { //prepare task auto task = static_cast(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; diff --git a/src/lib/sw_engine/tvgSwRenderer.h b/src/lib/sw_engine/tvgSwRenderer.h index 417d3a7e..9453ec31 100644 --- a/src/lib/sw_engine/tvgSwRenderer.h +++ b/src/lib/sw_engine/tvgSwRenderer.h @@ -34,7 +34,7 @@ namespace tvg class SwRenderer : public RenderMethod { public: - void* prepare(const Shape& shape, void* data, const RenderTransform* transform, vector& compList, RenderUpdateFlag flags) override; + void* prepare(const Shape& shape, void* data, const RenderTransform* transform, uint32_t opacity, vector& compList, RenderUpdateFlag flags) override; bool dispose(const Shape& shape, void *data) override; bool preRender() override; bool postRender() override; diff --git a/src/lib/tvgCanvasImpl.h b/src/lib/tvgCanvasImpl.h index 06a54af2..755dabdd 100644 --- a/src/lib/tvgCanvasImpl.h +++ b/src/lib/tvgCanvasImpl.h @@ -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; diff --git a/src/lib/tvgPaint.cpp b/src/lib/tvgPaint.cpp index cdf0c254..0d47641c 100644 --- a/src/lib/tvgPaint.cpp +++ b/src/lib/tvgPaint.cpp @@ -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 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; +} diff --git a/src/lib/tvgPaint.h b/src/lib/tvgPaint.h index 2c4a63d1..11bfad71 100644 --- a/src/lib/tvgPaint.h +++ b/src/lib/tvgPaint.h @@ -33,7 +33,7 @@ namespace tvg virtual ~StrategyMethod(){} virtual bool dispose(RenderMethod& renderer) = 0; - virtual void* update(RenderMethod& renderer, const RenderTransform* transform, vector compList, RenderUpdateFlag pFlag) = 0; //Return engine data if it has. + virtual void* update(RenderMethod& renderer, const RenderTransform* transform, uint32_t opacity, vector 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& compList, uint32_t pFlag) + void* update(RenderMethod& renderer, const RenderTransform* pTransform, uint32_t opacity, vector& 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(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 compList, RenderUpdateFlag flag) override + void* update(RenderMethod& renderer, const RenderTransform* transform, uint32_t opacity, vector compList, RenderUpdateFlag flag) override { - return inst->update(renderer, transform, compList, flag); + return inst->update(renderer, transform, opacity, compList, flag); } bool render(RenderMethod& renderer) override diff --git a/src/lib/tvgPictureImpl.h b/src/lib/tvgPictureImpl.h index 1ac191ad..7ae736c5 100644 --- a/src/lib/tvgPictureImpl.h +++ b/src/lib/tvgPictureImpl.h @@ -58,13 +58,13 @@ struct Picture::Impl } - void* update(RenderMethod &renderer, const RenderTransform* transform, vector& compList, RenderUpdateFlag flag) + void* update(RenderMethod &renderer, const RenderTransform* transform, uint32_t opacity, vector& 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) diff --git a/src/lib/tvgRender.h b/src/lib/tvgRender.h index 4575263c..666463ba 100644 --- a/src/lib/tvgRender.h +++ b/src/lib/tvgRender.h @@ -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& 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& 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; } diff --git a/src/lib/tvgSceneImpl.h b/src/lib/tvgSceneImpl.h index b6e83d52..68fcff85 100644 --- a/src/lib/tvgSceneImpl.h +++ b/src/lib/tvgSceneImpl.h @@ -44,14 +44,14 @@ struct Scene::Impl return true; } - void* update(RenderMethod &renderer, const RenderTransform* transform, vector& compList, RenderUpdateFlag flag) + void* update(RenderMethod &renderer, const RenderTransform* transform, uint32_t opacity, vector& 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(flag)); + edata = paint->pImpl->update(renderer, transform, opacity, compList, static_cast(flag)); } return edata; } diff --git a/src/lib/tvgShapeImpl.h b/src/lib/tvgShapeImpl.h index 982b62a0..c69b8447 100644 --- a/src/lib/tvgShapeImpl.h +++ b/src/lib/tvgShapeImpl.h @@ -222,9 +222,9 @@ struct Shape::Impl return renderer.render(*shape, edata); } - void* update(RenderMethod& renderer, const RenderTransform* transform, vector& compList, RenderUpdateFlag pFlag) + void* update(RenderMethod& renderer, const RenderTransform* transform, uint32_t opacity, vector& compList, RenderUpdateFlag pFlag) { - this->edata = renderer.prepare(*shape, this->edata, transform, compList, static_cast(pFlag | flag)); + this->edata = renderer.prepare(*shape, this->edata, transform, opacity, compList, static_cast(pFlag | flag)); flag = RenderUpdateFlag::None; return this->edata; } -- 2.34.1