SwRender & tvgPaint: Implement ClipPath feature (#68)
authorJunsuChoi <jsuya.choi@samsung.com>
Wed, 7 Oct 2020 02:21:23 +0000 (11:21 +0900)
committerHermet Park <chuneon.park@samsung.com>
Wed, 7 Oct 2020 05:20:12 +0000 (14:20 +0900)
common sw_engine: Implement ClipPath feature

Paint object can composite by using composite API.
ClipPath composite is clipping by path unit of paint.
The following cases are supported.

Shape->composite(Shape);
Scene->composite(Shape);
Picture->composite(Shape);

Add enum
  enum CompMethod { None = 0, ClipPath };

Add APIs
  Result composite(std::unique_ptr<Paint> comp, CompMethod method) const noexcept;

* Example: Added testClipPath

Change-Id: I7151607744ef7f567b6dd2a4bdb4062500197e66

17 files changed:
inc/thorvg.h
src/examples/meson.build
src/examples/testClipPath.cpp [new file with mode: 0644]
src/lib/gl_engine/tvgGlRenderer.cpp
src/lib/gl_engine/tvgGlRenderer.h
src/lib/sw_engine/tvgSwCommon.h
src/lib/sw_engine/tvgSwRenderer.cpp
src/lib/sw_engine/tvgSwRenderer.h
src/lib/sw_engine/tvgSwRle.cpp
src/lib/sw_engine/tvgSwShape.cpp
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 71f6b498cc786c1d241023534985a24f19d47115..4d95715f13b4a3bd568825c11b72af27ff000b9a 100644 (file)
@@ -56,6 +56,7 @@ enum class TVG_EXPORT PathCommand { Close = 0, MoveTo, LineTo, CubicTo };
 enum class TVG_EXPORT StrokeCap { Square = 0, Round, Butt };
 enum class TVG_EXPORT StrokeJoin { Bevel = 0, Round, Miter };
 enum class TVG_EXPORT FillSpread { Pad = 0, Reflect, Repeat };
+enum class TVG_EXPORT CompMethod { None = 0, ClipPath };
 enum class TVG_EXPORT CanvasEngine { Sw = (1 << 1), Gl = (1 << 2)};
 
 
@@ -93,6 +94,8 @@ public:
     Result bounds(float* x, float* y, float* w, float* h) const noexcept;
     Paint* duplicate() const noexcept;
 
+    Result composite(std::unique_ptr<Paint> target, CompMethod method) const noexcept;
+
     _TVG_DECLARE_ACCESSOR();
     _TVG_DECLARE_PRIVATE(Paint);
 };
index d50e4f9522b1962e10349d40add3c360a040feb3..24f2cbd6305d036c21f429b1f2b862d9f39cd2a8 100644 (file)
@@ -26,6 +26,7 @@ source_file = [
     'testSvg.cpp',
     'testTransform.cpp',
     'testUpdate.cpp',
+    'testClipPath.cpp',
 ]
 
 foreach current_file : source_file
diff --git a/src/examples/testClipPath.cpp b/src/examples/testClipPath.cpp
new file mode 100644 (file)
index 0000000..667abba
--- /dev/null
@@ -0,0 +1,217 @@
+#include "testCommon.h"
+
+/************************************************************************/
+/* Drawing Commands                                                     */
+/************************************************************************/
+
+void tvgDrawStar(tvg::Shape* star)
+{
+    star->moveTo(199, 34);
+    star->lineTo(253, 143);
+    star->lineTo(374, 160);
+    star->lineTo(287, 244);
+    star->lineTo(307, 365);
+    star->lineTo(199, 309);
+    star->lineTo(97, 365);
+    star->lineTo(112, 245);
+    star->lineTo(26, 161);
+    star->lineTo(146, 143);
+    star->close();
+}
+
+void tvgDrawCmds(tvg::Canvas* canvas)
+{
+    if (!canvas) return;
+    //Background
+    auto shape = tvg::Shape::gen();
+    shape->appendRect(0, 0, WIDTH, HEIGHT, 0, 0);
+    shape->fill(255, 255, 255, 255);
+    if (canvas->push(move(shape)) != tvg::Result::Success) return;
+
+    //////////////////////////////////////////////
+    auto scene = tvg::Scene::gen();
+    scene->reserve(2);
+
+    auto star1 = tvg::Shape::gen();
+    tvgDrawStar(star1.get());
+    star1->fill(255, 255, 0, 255);
+    star1->stroke(255 ,0, 0, 128);
+    star1->stroke(10);
+
+    //Move Star1
+    star1->translate(-10, -10);
+
+    auto clipStar = tvg::Shape::gen();
+    clipStar->appendCircle(200, 230, 110, 110);
+    clipStar->fill(255, 255, 255, 255); // clip object must have alpha.
+    clipStar->translate(10, 10);
+
+    star1->composite(move(clipStar), tvg::CompMethod::ClipPath);
+
+    auto star2 = tvg::Shape::gen();
+    tvgDrawStar(star2.get());
+    star2->fill(0, 255, 255, 64);
+    star2->stroke(0 ,255, 0, 128);
+    star2->stroke(10);
+
+    //Move Star2
+    star2->translate(10, 40);
+
+    auto clip = tvg::Shape::gen();
+    clip->appendCircle(200, 230, 130, 130);
+    clip->fill(255, 255, 255, 255); // clip object must have alpha.
+    clip->translate(10, 10);
+
+    scene->push(move(star1));
+    scene->push(move(star2));
+
+    //Clipping scene to shape
+    scene->composite(move(clip), tvg::CompMethod::ClipPath);
+
+    canvas->push(move(scene));
+
+    //////////////////////////////////////////////
+    auto star3 = tvg::Shape::gen();
+    tvgDrawStar(star3.get());
+    star3->translate(400, 0);
+    star3->fill(255, 255, 0, 255);                    //r, g, b, a
+    star3->stroke(255 ,0, 0, 128);
+    star3->stroke(10);
+
+    auto clipRect = tvg::Shape::gen();
+    clipRect->appendRect(480, 110, 200, 200, 0, 0);          //x, y, w, h, rx, ry
+    clipRect->fill(255, 255, 255, 255); // clip object must have alpha.
+    clipRect->translate(20, 20);
+
+    //Clipping scene to rect(shape)
+    star3->composite(move(clipRect), tvg::CompMethod::ClipPath);
+
+    canvas->push(move(star3));
+
+    //////////////////////////////////////////////
+    auto picture = tvg::Picture::gen();
+    char buf[PATH_MAX];
+    sprintf(buf,"%s/cartman.svg", EXAMPLE_DIR);
+
+    if (picture->load(buf) != tvg::Result::Success) return;
+
+    picture->scale(3);
+    picture->translate(200, 400);
+
+    auto clipPath = tvg::Shape::gen();
+    clipPath->appendCircle(350, 510, 110, 110);          //x, y, w, h, rx, ry
+    clipPath->appendCircle(350, 650, 50, 50);          //x, y, w, h, rx, ry
+    clipPath->fill(255, 255, 255, 255); // clip object must have alpha.
+    clipPath->translate(20, 20);
+
+    //Clipping picture to path
+    picture->composite(move(clipPath), tvg::CompMethod::ClipPath);
+
+    canvas->push(move(picture));
+}
+
+
+/************************************************************************/
+/* Sw Engine Test Code                                                  */
+/************************************************************************/
+
+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 896a80be265bb88e7e91f728f00e3464c12e4c26..b7cbfc4ef75c7b584936c5a3f55a5452a772042c 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, RenderUpdateFlag flags)
+void* GlRenderer::prepare(const Shape& shape, void* data, TVG_UNUSED const RenderTransform* transform, vector<Composite>& compList, RenderUpdateFlag flags)
 {
     //prepare shape data
     GlShape* sdata = static_cast<GlShape*>(data);
index 7d63c5a7b34050de4fdf83ce6d4895b0cc47ade3..c545f82a948062b665441eb53d2eaa3190a022f9 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, RenderUpdateFlag flags) override;
+    void* prepare(const Shape& shape, void* data, const RenderTransform* transform, 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 f90f44bb122f30c9422bc2a01a9a934671542bc5..47a69154d24f09c1e676e1f1ea7853294a7c7e9d 100644 (file)
@@ -270,7 +270,7 @@ SwFixed mathMean(SwFixed angle1, SwFixed angle2);
 void shapeReset(SwShape* shape);
 bool shapeGenOutline(SwShape* shape, const Shape* sdata, const Matrix* transform);
 bool shapePrepare(SwShape* shape, const Shape* sdata, const SwSize& clip, const Matrix* transform);
-bool shapeGenRle(SwShape* shape, const Shape* sdata, const SwSize& clip, bool antiAlias);
+bool shapeGenRle(SwShape* shape, const Shape* sdata, const SwSize& clip, bool antiAlias, bool hasComposite);
 void shapeDelOutline(SwShape* shape);
 void shapeResetStroke(SwShape* shape, const Shape* sdata, const Matrix* transform);
 bool shapeGenStrokeRle(SwShape* shape, const Shape* sdata, const Matrix* transform, const SwSize& clip);
@@ -293,6 +293,9 @@ void fillFetchRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x,
 
 SwRleData* rleRender(const SwOutline* outline, const SwBBox& bbox, const SwSize& clip, bool antiAlias);
 void rleFree(SwRleData* rle);
+void rleClipPath(SwRleData *rle, const SwRleData *clip);
+void rleClipRect(SwRleData *rle, const SwBBox* clip);
+
 
 bool rasterCompositor(SwSurface* surface);
 bool rasterGradientShape(SwSurface* surface, SwShape* shape, unsigned id);
index 8c6ee95f992cb8a4e5f820f00689282f388227e8..aac65fd312e0aa3037eb506df42775ff29481e79 100644 (file)
@@ -36,6 +36,7 @@ struct SwTask : Task
     Matrix* transform = nullptr;
     SwSurface* surface = nullptr;
     RenderUpdateFlag flags = RenderUpdateFlag::None;
+    vector<Composite> compList;
 
     void run() override
     {
@@ -58,10 +59,11 @@ struct SwTask : Task
                 if (!shapePrepare(&shape, sdata, clip, transform)) return;
                 if (renderShape) {
                     auto antiAlias = (strokeAlpha > 0 && strokeWidth >= 2) ? false : true;
-                    if (!shapeGenRle(&shape, sdata, clip, antiAlias)) return;
+                    if (!shapeGenRle(&shape, sdata, clip, antiAlias, compList.size() > 0 ? true : false)) return;
                 }
             }
         }
+
         //Fill
         if (flags & (RenderUpdateFlag::Gradient | RenderUpdateFlag::Transform)) {
             auto fill = sdata->fill();
@@ -82,6 +84,20 @@ struct SwTask : Task
                 shapeDelStroke(&shape);
             }
         }
+
+        //Composite clip-path
+        for (auto comp : compList) {
+             SwShape *compShape = &static_cast<SwTask*>(comp.edata)->shape;
+             if (comp.method == CompMethod::ClipPath) {
+                  //Clip to fill(path) rle
+                  if (shape.rle && compShape->rect) rleClipRect(shape.rle, &compShape->bbox);
+                  else if (shape.rle && compShape->rle) rleClipPath(shape.rle, compShape->rle);
+
+                  //Clip to stroke rle
+                  if (shape.strokeRle && compShape->rect) rleClipRect(shape.strokeRle, &compShape->bbox);
+                  else if (shape.strokeRle && compShape->rle) rleClipPath(shape.strokeRle, compShape->rle);
+             }
+        }
         shapeDelOutline(&shape);
     }
 };
@@ -184,7 +200,7 @@ bool SwRenderer::dispose(TVG_UNUSED const Shape& sdata, void *data)
 }
 
 
-void* SwRenderer::prepare(const Shape& sdata, void* data, const RenderTransform* transform, RenderUpdateFlag flags)
+void* SwRenderer::prepare(const Shape& sdata, void* data, const RenderTransform* transform, vector<Composite>& compList, RenderUpdateFlag flags)
 {
     //prepare task
     auto task = static_cast<SwTask*>(data);
@@ -196,6 +212,10 @@ void* SwRenderer::prepare(const Shape& sdata, void* data, const RenderTransform*
     if (flags == RenderUpdateFlag::None || task->valid()) return task;
 
     task->sdata = &sdata;
+    if (compList.size() > 0) {
+        for (auto comp : compList)  static_cast<SwTask*>(comp.edata)->get();
+        task->compList.assign(compList.begin(), compList.end());
+    }
 
     if (transform) {
         if (!task->transform) task->transform = static_cast<Matrix*>(malloc(sizeof(Matrix)));
index 5b399900b1444fed50b0dacbc371235be161d330..417d3a7e46ed272674531e3813c6e34100a5c55f 100644 (file)
@@ -34,7 +34,7 @@ namespace tvg
 class SwRenderer : public RenderMethod
 {
 public:
-    void* prepare(const Shape& shape, void* data, const RenderTransform* transform, RenderUpdateFlag flags) override;
+    void* prepare(const Shape& shape, void* data, const RenderTransform* transform, vector<Composite>& compList, RenderUpdateFlag flags) override;
     bool dispose(const Shape& shape, void *data) override;
     bool preRender() override;
     bool postRender() override;
index de20d61728ddac0d620e8072efb72cf5f33ea6ea..38f62fd289cf7f26afc2ccfb17e19ff451924c66 100644 (file)
@@ -613,6 +613,95 @@ static int _genRle(RleWorker& rw)
 }
 
 
+SwSpan* _intersectSpansRegion(const SwRleData *clip, const SwRleData *targetRle, SwSpan *outSpans, uint32_t spanCnt)
+{
+    auto out = outSpans;
+    auto spans = targetRle->spans;
+    auto end = targetRle->spans + targetRle->size;
+    auto clipSpans = clip->spans;
+    auto clipEnd = clip->spans + clip->size;
+
+    while (spanCnt && spans < end ) {
+        if (clipSpans > clipEnd) {
+            spans = end;
+            break;
+        }
+        if (clipSpans->y > spans->y) {
+            ++spans;
+            continue;
+        }
+        if (spans->y != clipSpans->y) {
+            ++clipSpans;
+            continue;
+        }
+        auto sx1 = spans->x;
+        auto sx2 = sx1 + spans->len;
+        auto cx1 = clipSpans->x;
+        auto cx2 = cx1 + clipSpans->len;
+
+        if (cx1 < sx1 && cx2 < sx1) {
+            ++clipSpans;
+            continue;
+        }
+        else if (sx1 < cx1 && sx2 < cx1) {
+            ++spans;
+            continue;
+        }
+        auto x = sx1 > cx1 ? sx1 : cx1;
+        auto len = (sx2 < cx2 ? sx2 : cx2) - x;
+        if (len) {
+            auto spansCorverage = spans->coverage;
+            auto clipSpansCoverage = clipSpans->coverage;
+            out->x = sx1 > cx1 ? sx1 : cx1;
+            out->len = (sx2 < cx2 ? sx2 : cx2) - out->x;
+            out->y = spans->y;
+            out->coverage = (uint8_t)((spansCorverage * clipSpansCoverage) >> 8);
+            ++out;
+            --spanCnt;
+        }
+        if (sx2 < cx2) ++spans;
+        else ++clipSpans;
+    }
+    return out;
+}
+
+SwSpan* _intersectSpansRect(const SwBBox *bbox, const SwRleData *targetRle, SwSpan *outSpans, uint32_t spanCnt)
+{
+    auto out = outSpans;
+    auto spans = targetRle->spans;
+    auto end = targetRle->spans + targetRle->size;
+    auto minx = static_cast<int16_t>(bbox->min.x);
+    auto miny = static_cast<int16_t>(bbox->min.y);
+    auto maxx = minx + static_cast<int16_t>(bbox->max.x - bbox->min.x) - 1;
+    auto maxy = miny + static_cast<int16_t>(bbox->max.y - bbox->min.y) - 1;
+
+    while (spanCnt && spans < end ) {
+        if (spans->y > maxy) {
+            spans = end;
+            break;
+        }
+        if (spans->y < miny || spans->x > maxx || spans->x + spans->len <= minx) {
+            ++spans;
+            continue;
+        }
+        if (spans->x < minx) {
+            out->len = (spans->len - (minx - spans->x)) < (maxx - minx + 1) ? (spans->len - (minx - spans->x)) : (maxx - minx + 1);
+            out->x = minx;
+        }
+        else {
+            out->x = spans->x;
+            out->len = spans->len < (maxx - spans->x + 1) ? spans->len : (maxx - spans->x + 1);
+        }
+        if (out->len != 0) {
+            out->y = spans->y;
+            out->coverage = spans->coverage;
+            ++out;
+        }
+        ++spans;
+        --spanCnt;
+    }
+    return out;
+}
 
 /************************************************************************/
 /* External Class Implementation                                        */
@@ -746,4 +835,48 @@ void rleFree(SwRleData* rle)
     if (!rle) return;
     if (rle->spans) free(rle->spans);
     free(rle);
-}
\ No newline at end of file
+}
+
+void updateRleSpans(SwRleData *rle, SwSpan* curSpans, uint32_t size)
+{
+    if (!rle->spans || !curSpans || size == 0) return;
+    rle->size = size;
+    rle->spans = static_cast<SwSpan*>(realloc(rle->spans, rle->size * sizeof(SwSpan)));
+
+    if (!rle->spans) return;
+
+    for (int i = 0; i < (int)rle->size ; i++)
+    {
+       rle->spans[i].x = curSpans[i].x;
+       rle->spans[i].y = curSpans[i].y;
+       rle->spans[i].len = curSpans[i].len;
+       rle->spans[i].coverage = curSpans[i].coverage;
+    }
+}
+
+void rleClipPath(SwRleData *rle, const SwRleData *clip)
+{
+    if (rle->size == 0 || clip->size == 0) return;
+    auto spanCnt = rle->size > clip->size ? rle->size : clip->size;
+    auto spans = static_cast<SwSpan*>(malloc(sizeof(SwSpan) * (spanCnt)));
+    if (!spans) return;
+    auto spansEnd = _intersectSpansRegion(clip, rle, spans, spanCnt);
+
+    //Update Spans
+    updateRleSpans(rle, spans, spansEnd - spans);
+
+    if (spans) free(spans);
+}
+
+void rleClipRect(SwRleData *rle, const SwBBox* clip)
+{
+    if (rle->size == 0) return;
+    auto spans = static_cast<SwSpan*>(malloc(sizeof(SwSpan) * (rle->size)));
+    if (!spans) return;
+    auto spansEnd = _intersectSpansRect(clip, rle, spans, rle->size);
+
+    //Update Spans
+    updateRleSpans(rle, spans, spansEnd - spans);
+
+    if (spans) free(spans);
+}
index 0c6391c55a726e957f33446bbac27ed74b725654..49166ecf51a97f6fcb70f1da5e7a2f81096a0188 100644 (file)
@@ -459,14 +459,14 @@ bool shapePrepare(SwShape* shape, const Shape* sdata, const SwSize& clip, const
 }
 
 
-bool shapeGenRle(SwShape* shape, TVG_UNUSED const Shape* sdata, const SwSize& clip, bool antiAlias)
+bool shapeGenRle(SwShape* shape, TVG_UNUSED const Shape* sdata, const SwSize& clip, bool antiAlias, bool hasComposite)
 {
     //FIXME: Should we draw it?
     //Case: Stroke Line
     //if (shape.outline->opened) return true;
 
     //Case A: Fast Track Rectangle Drawing
-    if ((shape->rect = _fastTrack(shape->outline))) return true;
+    if (!hasComposite && (shape->rect = _fastTrack(shape->outline))) return true;
     //Case B: Normale Shape RLE Drawing
     if ((shape->rle = rleRender(shape->outline, shape->bbox, clip, antiAlias))) return true;
 
@@ -687,4 +687,4 @@ void shapeDelFill(SwShape* shape)
     if (!shape->fill) return;
     fillFree(shape->fill);
     shape->fill = nullptr;
-}
\ No newline at end of file
+}
index e2de033363a031f6b55a74e1b8895d3a7a25d261..835b72692a703ca92b4f443401e5ac439a08cdd8 100644 (file)
@@ -64,6 +64,7 @@ struct Canvas::Impl
             paint->pImpl->dispose(*renderer);
             delete(paint);
         }
+
         paints.clear();
 
         return Result::Success;
@@ -73,15 +74,17 @@ struct Canvas::Impl
     {
         if (!renderer) return Result::InsufficientCondition;
 
+        vector<Composite> compList;
+
         //Update single paint node
         if (paint) {
-            if (!paint->pImpl->update(*renderer, nullptr, RenderUpdateFlag::None)) {
+            if (!paint->pImpl->update(*renderer, nullptr, compList, nullptr, RenderUpdateFlag::None)) {
                 return Result::InsufficientCondition;
             }
         //Update retained all paint nodes
         } else {
-            for(auto paint: paints) {
-                if (!paint->pImpl->update(*renderer, nullptr, RenderUpdateFlag::None)) {
+            for (auto paint: paints) {
+                if (!paint->pImpl->update(*renderer, nullptr, compList, nullptr, RenderUpdateFlag::None)) {
                     return Result::InsufficientCondition;
                 }
             }
@@ -105,4 +108,4 @@ struct Canvas::Impl
     }
 };
 
-#endif /* _TVG_CANVAS_IMPL_H_ */
\ No newline at end of file
+#endif /* _TVG_CANVAS_IMPL_H_ */
index 7c490babb6517c28bf8c9cedbe68ef21604a9474..f12f37fcbdeea0254a041de6b318aebe25a39e9f 100644 (file)
@@ -67,7 +67,6 @@ 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;
@@ -77,4 +76,11 @@ Result Paint::bounds(float* x, float* y, float* w, float* h) const noexcept
 Paint* Paint::duplicate() const noexcept
 {
     return pImpl->duplicate();
-}
\ No newline at end of file
+}
+
+Result Paint::composite(std::unique_ptr<Paint> target, CompMethod method) const noexcept
+{
+    if (pImpl->composite(target.release(), method)) return Result::Success;
+    return Result::InsufficientCondition;
+}
+
index 4b49520b40cfe2c9459c7bffaf8cbccc647f3f8d..e46bf636cc0fdac2e5a4bfc4c3e2dcbbc06ae101 100644 (file)
@@ -33,7 +33,7 @@ namespace tvg
         virtual ~StrategyMethod(){}
 
         virtual bool dispose(RenderMethod& renderer) = 0;
-        virtual bool update(RenderMethod& renderer, const RenderTransform* transform, RenderUpdateFlag pFlag) = 0;
+        virtual bool update(RenderMethod& renderer, const RenderTransform* transform, vector<Composite> compList, void** edata, RenderUpdateFlag pFlag) = 0;
         virtual bool render(RenderMethod& renderer) = 0;
         virtual bool bounds(float* x, float* y, float* w, float* h) const = 0;
         virtual Paint* duplicate() = 0;
@@ -45,6 +45,9 @@ namespace tvg
         RenderTransform *rTransform = nullptr;
         uint32_t flag = RenderUpdateFlag::None;
 
+        Paint* compTarget = nullptr;
+        CompMethod compMethod = CompMethod::None;
+
         ~Impl() {
             if (smethod) delete(smethod);
             if (rTransform) delete(rTransform);
@@ -120,10 +123,11 @@ namespace tvg
 
         bool dispose(RenderMethod& renderer)
         {
+            if (this->compTarget) this->compTarget->pImpl->dispose(renderer);
             return smethod->dispose(renderer);
         }
 
-        bool update(RenderMethod& renderer, const RenderTransform* pTransform, uint32_t pFlag)
+        bool update(RenderMethod& renderer, const RenderTransform* pTransform, vector<Composite>& compList, void **edata, uint32_t pFlag)
         {
             if (flag & RenderUpdateFlag::Transform) {
                 if (!rTransform) return false;
@@ -135,14 +139,30 @@ namespace tvg
 
             auto newFlag = static_cast<RenderUpdateFlag>(pFlag | flag);
             flag = RenderUpdateFlag::None;
+            bool updated = false;
+            void *compEngineData = nullptr; //composite target paint's engine data.
+
+            if (this->compTarget && compMethod == CompMethod::ClipPath) {
+                if (this->compTarget->pImpl->update(renderer, pTransform, compList, &compEngineData, static_cast<RenderUpdateFlag>(pFlag | flag))) {
+                    Composite comp;
+                    comp.edata = compEngineData;
+                    comp.method = this->compMethod;
+                    compList.push_back(comp);
+                }
+            }
 
             if (rTransform && pTransform) {
                 RenderTransform outTransform(pTransform, rTransform);
-                return smethod->update(renderer, &outTransform, newFlag);
+                updated = smethod->update(renderer, &outTransform, compList, edata, newFlag);
             } else {
                 auto outTransform = pTransform ? pTransform : rTransform;
-                return smethod->update(renderer, outTransform, newFlag);
+                updated = smethod->update(renderer, outTransform, compList, edata, newFlag);
+            }
+
+            if (compEngineData) {
+                compList.pop_back();
             }
+            return updated;
         }
 
         bool render(RenderMethod& renderer)
@@ -165,6 +185,14 @@ namespace tvg
 
             return ret;
         }
+
+        bool composite(Paint* target, CompMethod compMethod)
+        {
+            this->compTarget = target;
+            this->compMethod = compMethod;
+            if (this->compTarget) return true;
+            return false;
+        }
     };
 
 
@@ -186,9 +214,9 @@ namespace tvg
             return inst->dispose(renderer);
         }
 
-        bool update(RenderMethod& renderer, const RenderTransform* transform, RenderUpdateFlag flag) override
+        bool update(RenderMethod& renderer, const RenderTransform* transform, vector<Composite> compList, void** edata, RenderUpdateFlag flag) override
         {
-            return inst->update(renderer, transform, flag);
+            return inst->update(renderer, transform, compList, edata, flag);
         }
 
         bool render(RenderMethod& renderer) override
index 0f07ed1c3fd9e1d74307fb3d6426b07cea8c0f90..6cea58521804e4373395e0a91a8645c1b5de1e51 100644 (file)
@@ -57,13 +57,13 @@ struct Picture::Impl
     }
 
 
-    bool update(RenderMethod &renderer, const RenderTransform* transform, RenderUpdateFlag flag)
+    bool update(RenderMethod &renderer, const RenderTransform* transform, vector<Composite>& compList, void** edata, RenderUpdateFlag flag)
     {
         reload();
 
         if (!paint) return false;
 
-        return paint->pImpl->update(renderer, transform, flag);
+        return paint->pImpl->update(renderer, transform, compList, edata, flag);
     }
 
     bool render(RenderMethod &renderer)
@@ -126,4 +126,4 @@ struct Picture::Impl
     }
 };
 
-#endif //_TVG_PICTURE_IMPL_H_
\ No newline at end of file
+#endif //_TVG_PICTURE_IMPL_H_
index f710a0f6e2ea6d4b157890d504eaed6ed1e37eb9..72e7bea04f1348adcdd1f3c4c64b194de231d19b 100644 (file)
@@ -22,6 +22,7 @@
 #ifndef _TVG_RENDER_H_
 #define _TVG_RENDER_H_
 
+#include <vector>
 #include "tvgCommon.h"
 
 namespace tvg
@@ -36,6 +37,11 @@ struct Surface
     uint32_t cs;
 };
 
+struct Composite {
+    void* edata;
+    CompMethod method;
+};
+
 enum RenderUpdateFlag {None = 0, Path = 1, Color = 2, Gradient = 4, Stroke = 8, Transform = 16, All = 32};
 
 struct RenderTransform
@@ -59,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 RenderUpdateFlag flags) { return nullptr; }
+    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 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; }
@@ -70,4 +76,4 @@ public:
 
 }
 
-#endif //_TVG_RENDER_H_
\ No newline at end of file
+#endif //_TVG_RENDER_H_
index c384d8c736969b5e360b32ed88dea5f026891a1e..4bc9bdaa9bad7a67bd7f4297b0903571453981ac 100644 (file)
@@ -44,10 +44,10 @@ struct Scene::Impl
         return true;
     }
 
-    bool update(RenderMethod &renderer, const RenderTransform* transform, RenderUpdateFlag flag)
+    bool update(RenderMethod &renderer, const RenderTransform* transform, vector<Composite>& compList, void** edata, RenderUpdateFlag flag)
     {
-        for(auto paint: paints) {
-            if (!paint->pImpl->update(renderer, transform, static_cast<uint32_t>(flag))) return false;
+        for (auto paint: paints) {
+            if (!paint->pImpl->update(renderer, transform, compList, edata, static_cast<uint32_t>(flag))) return false;
         }
         return true;
     }
@@ -106,4 +106,4 @@ struct Scene::Impl
     }
 };
 
-#endif //_TVG_SCENE_IMPL_H_
\ No newline at end of file
+#endif //_TVG_SCENE_IMPL_H_
index 18e58e6df5500dc92d0ef075aa46dc3e5468dc2a..ff2d254557fc3905235c6bdcf0b44956783bfc07 100644 (file)
@@ -221,13 +221,12 @@ struct Shape::Impl
         return renderer.render(*shape, edata);
     }
 
-    bool update(RenderMethod& renderer, const RenderTransform* transform, RenderUpdateFlag pFlag)
+    bool update(RenderMethod& renderer, const RenderTransform* transform, vector<Composite>& compList, void** edata, RenderUpdateFlag pFlag)
     {
-        edata = renderer.prepare(*shape, edata, transform, static_cast<RenderUpdateFlag>(pFlag | flag));
-
+        this->edata = renderer.prepare(*shape, this->edata, transform, compList, static_cast<RenderUpdateFlag>(pFlag | flag));
         flag = RenderUpdateFlag::None;
-
-        if (edata) return true;
+        if (edata) *edata = this->edata;
+        if (this->edata) return true;
         return false;
     }