sw_engine renderer: support scene opacity composition 13/249113/1
authorHermet Park <hermetpark@gmail.com>
Mon, 7 Dec 2020 06:45:44 +0000 (15:45 +0900)
committerJunsuChoi <jsuya.choi@samsung.com>
Mon, 7 Dec 2020 07:39:48 +0000 (16:39 +0900)
this is an additional enhancement of af8c278c5e2a47c21f44440cdb0a7aaf6f6d3baa

Now scene opacity composition is supported.

Also, this implementaion fixes an incorrect scene bounding box computation.

Plus, adding stroking feathering to shape bounding box size.

Change-Id: Ic74f2667ca1858b32b67135b3da0354fd2f6bfc5

src/examples/Opacity.cpp
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/tvgRender.h
src/lib/tvgSceneImpl.h
src/lib/tvgShapeImpl.h

index 53a2bab..9a78edd 100644 (file)
@@ -8,41 +8,59 @@ void tvgDrawCmds(tvg::Canvas* canvas)
 {
     if (!canvas) return;
 
+    //Create a Scene
+    auto scene = tvg::Scene::gen();
+    scene->opacity(175);              //Apply opacity to scene (0 - 255)
+    scene->reserve(2);
+
     //Prepare Circle
     auto shape1 = tvg::Shape::gen();
     shape1->appendCircle(400, 400, 250, 250);
     shape1->fill(255, 255, 0, 255);
-    canvas->push(move(shape1));
+    scene->push(move(shape1));
+
+    //Round rectangle
+    auto shape2 = tvg::Shape::gen();
+    shape2->appendRect(450, 100, 200, 200, 50, 50);
+    shape2->fill(0, 255, 0, 255);
+    shape2->stroke(10);
+    shape2->stroke(255, 255, 255, 255);
+    scene->push(move(shape2));
 
-    //Create a Scene
-    auto scene = tvg::Scene::gen();
-    scene->opacity(127);              //Apply opacity to scene (0 - 255)
-    scene->reserve(2);
+
+    //Draw the Scene onto the Canvas
+    canvas->push(move(scene));
+
+    //Create a Scene 2
+    auto scene2 = tvg::Scene::gen();
+    scene2->opacity(127);              //Apply opacity to scene (0 - 255)
+    scene2->scale(1.2);
+    scene2->reserve(2);
 
     //Star
-    auto shape2 = tvg::Shape::gen();
+    auto shape3 = 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);
-    shape2->stroke(10);
-    shape2->stroke(255, 255, 255, 255);
-    shape2->opacity(127);
+    shape3->moveTo(199, 34);
+    shape3->lineTo(253, 143);
+    shape3->lineTo(374, 160);
+    shape3->lineTo(287, 244);
+    shape3->lineTo(307, 365);
+    shape3->lineTo(199, 309);
+    shape3->lineTo(97, 365);
+    shape3->lineTo(112, 245);
+    shape3->lineTo(26, 161);
+    shape3->lineTo(146, 143);
+    shape3->close();
+    shape3->fill(0, 0, 255, 255);
+    shape3->stroke(10);
+    shape3->stroke(255, 255, 255, 255);
+    shape3->opacity(127);
 
-    scene->push(move(shape2));
+    scene2->push(move(shape3));
 
     //Circle
-    auto shape3 = tvg::Shape::gen();
+    auto shape4 = tvg::Shape::gen();
 
     auto cx = 550.0f;
     auto cy = 550.0f;
@@ -50,19 +68,19 @@ void tvgDrawCmds(tvg::Canvas* canvas)
     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);
-    shape3->stroke(10);
-    shape3->stroke(0, 0, 255, 255);
-    shape3->opacity(200);
-    scene->push(move(shape3));
+    shape4->moveTo(cx, cy - radius);
+    shape4->cubicTo(cx + halfRadius, cy - radius, cx + radius, cy - halfRadius, cx + radius, cy);
+    shape4->cubicTo(cx + radius, cy + halfRadius, cx + halfRadius, cy + radius, cx, cy+ radius);
+    shape4->cubicTo(cx - halfRadius, cy + radius, cx - radius, cy + halfRadius, cx - radius, cy);
+    shape4->cubicTo(cx - radius, cy - halfRadius, cx - halfRadius, cy - radius, cx, cy - radius);
+    shape4->fill(255, 0, 0, 255);
+    shape4->stroke(10);
+    shape4->stroke(0, 0, 255, 255);
+    shape4->opacity(200);
+    scene2->push(move(shape4));
 
     //Draw the Scene onto the Canvas
-    canvas->push(move(scene));
+    canvas->push(move(scene2));
 }
 
 
index 79ba1fa..133f20e 100644 (file)
@@ -96,6 +96,28 @@ bool GlRenderer::postRender()
 }
 
 
+void* GlRenderer::beginComposite(uint32_t x, uint32_t y, uint32_t w, uint32_t h)
+{
+    //TODO: Prepare frameBuffer & Setup render target for composition
+    return nullptr;
+}
+
+
+bool GlRenderer::endComposite(void* ctx, uint32_t opacity)
+{
+    //TODO: Composite Framebuffer to main surface
+    return false;
+}
+
+
+bool GlRenderer::render(const Picture& picture, void *data)
+{
+    //TODO Draw Bitmap Image
+
+    return true;
+}
+
+
 bool GlRenderer::render(const Shape& shape, void* data)
 {
     GlShape* sdata = static_cast<GlShape*>(data);
@@ -140,6 +162,13 @@ bool GlRenderer::dispose(void *data)
 }
 
 
+void* GlRenderer::prepare(TVG_UNUSED const Picture& picture, TVG_UNUSED void* data, TVG_UNUSED uint32_t *buffer, TVG_UNUSED const RenderTransform* transform, TVG_UNUSED uint32_t opacity, TVG_UNUSED vector<Composite>& compList, TVG_UNUSED RenderUpdateFlag flags)
+{
+    //TODO:
+    return nullptr;
+}
+
+
 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
index 50d66de..aa291a8 100644 (file)
@@ -31,9 +31,13 @@ public:
     Surface surface = {nullptr, 0, 0, 0};
 
     void* prepare(const Shape& shape, void* data, const RenderTransform* transform, uint32_t opacity, vector<Composite>& compList, RenderUpdateFlag flags) override;
+    void* prepare(const Picture& picture, void* data, uint32_t *buffer, const RenderTransform* transform, uint32_t opacity, vector<Composite>& compList, RenderUpdateFlag flags) override;
     bool dispose(void *data) override;
+    void* beginComposite(uint32_t x, uint32_t y, uint32_t w, uint32_t h) override;
+    bool endComposite(void* ctx, uint32_t opacity) override;
     bool preRender() override;
     bool render(const Shape& shape, void *data) override;
+    bool render(const Picture& picture, void *data) override;
     bool postRender() override;
     bool target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h);
     bool sync() override;
index 47b215f..42dedf0 100644 (file)
 static bool initEngine = false;
 static uint32_t rendererCnt = 0;
 
+struct CompositeCtx
+{
+    SwSurface surface;
+    SwSurface* recover;
+    SwImage image;
+};
+
+
 struct SwTask : Task
 {
     Matrix* transform = nullptr;
@@ -219,6 +227,12 @@ bool SwRenderer::clear()
 }
 
 
+bool SwRenderer::sync()
+{
+    return true;
+}
+
+
 bool SwRenderer::target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h, uint32_t cs)
 {
     if (!buffer || stride == 0 || w == 0 || h == 0) return false;
@@ -268,6 +282,80 @@ bool SwRenderer::render(TVG_UNUSED const Picture& picture, void *data)
 }
 
 
+void* SwRenderer::beginComposite(uint32_t x, uint32_t y, uint32_t w, uint32_t h)
+{
+    auto ctx = new CompositeCtx;
+    if (!ctx) return nullptr;
+
+    //SwImage, Optimize Me: Surface size from MainSurface(WxH) to Parameter W x H
+    ctx->image.data = (uint32_t*) malloc(sizeof(uint32_t) * mainSurface->w * mainSurface->h);
+    if (!ctx->image.data) {
+        delete(ctx);
+        return nullptr;
+    }
+
+    //Boundary Check
+    if (x < 0) x = 0;
+    if (y < 0) y = 0;
+    if (x + w > mainSurface->w) w = (mainSurface->w - x);
+    if (y + h > mainSurface->h) h = (mainSurface->h - y);
+
+    //FIXME: Should be removed if xywh is proper.
+    x = 0;
+    y = 0;
+    w = mainSurface->w;
+    h = mainSurface->h;
+
+    ctx->image.bbox.min.x = x;
+    ctx->image.bbox.min.y = y;
+    ctx->image.bbox.max.x = x + w;
+    ctx->image.bbox.max.y = y + h;
+    ctx->image.w = mainSurface->w;
+    ctx->image.h = mainSurface->h;
+
+    //Inherits attributes from main surface
+    ctx->surface.comp = mainSurface->comp;
+    ctx->surface.stride = mainSurface->w;
+    ctx->surface.cs = mainSurface->cs;
+
+    //We know partial clear region
+    ctx->surface.buffer = ctx->image.data + (ctx->surface.stride * y + x);
+    ctx->surface.w = w;
+    ctx->surface.h = h;
+
+    rasterClear(&ctx->surface);
+
+    //Recover context
+    ctx->surface.buffer = ctx->image.data;
+    ctx->surface.w = ctx->image.w;
+    ctx->surface.h = ctx->image.h;
+
+    //Switch render target
+    ctx->recover = mainSurface;
+    mainSurface = &ctx->surface;
+
+    return ctx;
+}
+
+
+bool SwRenderer::endComposite(void* p, uint32_t opacity)
+{
+    if (!p) return false;
+    auto ctx = static_cast<CompositeCtx*>(p);
+
+    //Recover render target
+    mainSurface = ctx->recover;
+
+    auto ret = rasterImage(mainSurface, &ctx->image, nullptr, opacity);
+
+    //Free resources
+    free(ctx->image.data);
+    delete(ctx);
+
+    return ret;
+}
+
+
 bool SwRenderer::prepareComposite(const SwShapeTask* task, SwImage* image)
 {
     if (!compSurface) {
index 43ae5cf..19546a0 100644 (file)
@@ -38,13 +38,16 @@ class SwRenderer : public RenderMethod
 public:
     void* prepare(const Shape& shape, void* data, const RenderTransform* transform, uint32_t opacity, vector<Composite>& compList, RenderUpdateFlag flags) override;
     void* prepare(const Picture& picture, void* data, uint32_t *buffer, const RenderTransform* transform, uint32_t opacity, vector<Composite>& compList, RenderUpdateFlag flags) override;
+    void* beginComposite(uint32_t x, uint32_t y, uint32_t w, uint32_t h) override;
+    bool endComposite(void* ctx, uint32_t opacity) override;
     bool dispose(void *data) override;
     bool preRender() override;
     bool postRender() override;
-    bool target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h, uint32_t cs);
     bool clear() override;
     bool render(const Shape& shape, void *data) override;
     bool render(const Picture& picture, void *data) override;
+    bool sync() override;
+    bool target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h, uint32_t cs);
 
     static SwRenderer* gen();
     static bool init(uint32_t threads);
index 9f8ebcc..c5e625b 100644 (file)
@@ -65,15 +65,17 @@ class RenderMethod
 {
 public:
     virtual ~RenderMethod() {}
-    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 void* prepare(TVG_UNUSED const Picture& picture, TVG_UNUSED void* data, TVG_UNUSED uint32_t *buffer, TVG_UNUSED const RenderTransform* transform, TVG_UNUSED uint32_t opacity, TVG_UNUSED vector<Composite>& compList, TVG_UNUSED RenderUpdateFlag flags) { return nullptr; }
-    virtual bool dispose(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; }
-    virtual bool render(TVG_UNUSED const Picture& picture, TVG_UNUSED void *data) { return true; }
-    virtual bool postRender() { return true; }
-    virtual bool clear() { return true; }
-    virtual bool sync() { return true; }
+    virtual void* prepare(const Shape& shape, void* data, const RenderTransform* transform, uint32_t opacity, vector<Composite>& compList, RenderUpdateFlag flags) = 0;
+    virtual void* prepare(const Picture& picture, void* data, uint32_t *buffer, const RenderTransform* transform, uint32_t opacity, vector<Composite>& compList, RenderUpdateFlag flags) = 0;
+    virtual void* beginComposite(uint32_t x, uint32_t y, uint32_t w, uint32_t h) = 0;
+    virtual bool endComposite(void* ctx, uint32_t opacity) = 0;
+    virtual bool dispose(void *data) = 0;
+    virtual bool preRender() = 0;
+    virtual bool render(const Shape& shape, void *data) = 0;
+    virtual bool render(const Picture& picture, void *data) = 0;
+    virtual bool postRender() = 0;
+    virtual bool clear() = 0;
+    virtual bool sync() = 0;
 };
 
 }
index 4b0eb77..21fa75d 100644 (file)
@@ -32,6 +32,7 @@
 struct Scene::Impl
 {
     vector<Paint*> paints;
+    uint32_t opacity;
 
     bool dispose(RenderMethod& renderer)
     {
@@ -46,6 +47,12 @@ struct Scene::Impl
 
     void* update(RenderMethod &renderer, const RenderTransform* transform, uint32_t opacity, vector<Composite>& compList, RenderUpdateFlag flag)
     {
+        this->opacity = opacity;
+
+        /* Overriding opacity value. If this scene is half-translucent,
+           It must do intermeidate composition with that opacity value. */
+        if (opacity < 255 && opacity > 0) opacity = 255;
+
         /* FXIME: it requires to return list of children engine data
            This is necessary for scene composition */
         void* edata = nullptr;
@@ -59,45 +66,54 @@ struct Scene::Impl
 
     bool render(RenderMethod &renderer)
     {
-        //TODO: composition begin
-        //auto data = renderer.beginComp();
+        void* ctx = nullptr;
+
+        //Half translucent. This requires intermediate composition.
+        if (opacity < 255 && opacity > 0) {
+            //FIXME: Get Render Boundary of Shapes.
+            //float x, y, w, h;
+            //if (!bounds(&x, &y, &w, &h)) return false;
+            //ctx = renderer.beginComposite(roundf(x), roundf(y), roundf(w), roundf(h));
+            ctx = renderer.beginComposite(0, 0, 0, 0);
+        }
 
         for (auto paint : paints) {
             if (!paint->pImpl->render(renderer)) return false;
-        }     
+        }
 
-        //TODO: composition end
-        //renderer.endComp(edata);
+        if (ctx) return renderer.endComposite(ctx, opacity);
 
         return true;
     }
 
     bool bounds(float* px, float* py, float* pw, float* ph)
     {
-        auto x = FLT_MAX;
-        auto y = FLT_MAX;
-        auto w = 0.0f;
-        auto h = 0.0f;
+        if (paints.size() == 0) return false;
+
+        auto x1 = FLT_MAX;
+        auto y1 = FLT_MAX;
+        auto x2 = 0.0f;
+        auto y2 = 0.0f;
 
         for (auto paint : paints) {
-            auto x2 = FLT_MAX;
-            auto y2 = FLT_MAX;
-            auto w2 = 0.0f;
-            auto h2 = 0.0f;
+            auto x = FLT_MAX;
+            auto y = FLT_MAX;
+            auto w = 0.0f;
+            auto h = 0.0f;
 
-            if (!paint->pImpl->bounds(&x2, &y2, &w2, &h2)) continue;
+            if (!paint->pImpl->bounds(&x, &y, &w, &h)) continue;
 
             //Merge regions
-            if (x2 < x) x = x2;
-            if (x + w < x2 + w2) w = (x2 + w2) - x;
-            if (y2 < y) y = y2;
-            if (y + h < y2 + h2) h = (y2 + h2) - y;
+            if (x < x1) x1 = x;
+            if (x2 < x + w) x2 = (x + w);
+            if (y < y1) y1 = y;
+            if (y2 < y + h) y2 = (y + h);
         }
 
-        if (px) *px = x;
-        if (py) *py = y;
-        if (pw) *pw = w;
-        if (ph) *ph = h;
+        if (px) *px = x1;
+        if (py) *py = y1;
+        if (pw) *pw = (x2 - x1);
+        if (ph) *ph = (y2 - y1);
 
         return true;
     }
index 66a549a..bd13ff7 100644 (file)
@@ -232,7 +232,16 @@ struct Shape::Impl
 
     bool bounds(float* x, float* y, float* w, float* h)
     {
-        return path.bounds(x, y, w, h);
+        auto ret = path.bounds(x, y, w, h);
+
+        //Stroke feathering
+        if (stroke) {
+            if (x) *x -= stroke->width * 0.5f;
+            if (y) *y -= stroke->width * 0.5f;
+            if (w) *w += stroke->width;
+            if (h) *h += stroke->width;
+        }
+        return ret;
     }
 
     bool strokeWidth(float width)