From c70d1b1e458dc2703a0a1c7bb2a99a32edd785bd Mon Sep 17 00:00:00 2001 From: JunsuChoi Date: Wed, 7 Oct 2020 11:21:23 +0900 Subject: [PATCH] SwRender & tvgPaint: Implement ClipPath feature (#68) 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 comp, CompMethod method) const noexcept; * Example: Added testClipPath --- inc/thorvg.h | 3 + src/examples/meson.build | 1 + src/examples/testClipPath.cpp | 217 ++++++++++++++++++++++++++++ src/lib/gl_engine/tvgGlRenderer.cpp | 2 +- src/lib/gl_engine/tvgGlRenderer.h | 2 +- src/lib/sw_engine/tvgSwCommon.h | 5 +- src/lib/sw_engine/tvgSwRenderer.cpp | 24 ++- src/lib/sw_engine/tvgSwRenderer.h | 2 +- src/lib/sw_engine/tvgSwRle.cpp | 135 ++++++++++++++++- src/lib/sw_engine/tvgSwShape.cpp | 6 +- src/lib/tvgCanvasImpl.h | 11 +- src/lib/tvgPaint.cpp | 10 +- src/lib/tvgPaint.h | 40 ++++- src/lib/tvgPictureImpl.h | 6 +- src/lib/tvgRender.h | 10 +- src/lib/tvgSceneImpl.h | 8 +- src/lib/tvgShapeImpl.h | 9 +- 17 files changed, 455 insertions(+), 36 deletions(-) create mode 100644 src/examples/testClipPath.cpp diff --git a/inc/thorvg.h b/inc/thorvg.h index 71f6b498..4d95715f 100644 --- a/inc/thorvg.h +++ b/inc/thorvg.h @@ -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 target, CompMethod method) const noexcept; + _TVG_DECLARE_ACCESSOR(); _TVG_DECLARE_PRIVATE(Paint); }; diff --git a/src/examples/meson.build b/src/examples/meson.build index d50e4f95..24f2cbd6 100644 --- a/src/examples/meson.build +++ b/src/examples/meson.build @@ -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 index 00000000..667abbab --- /dev/null +++ b/src/examples/testClipPath.cpp @@ -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 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/lib/gl_engine/tvgGlRenderer.cpp b/src/lib/gl_engine/tvgGlRenderer.cpp index 896a80be..b7cbfc4e 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, RenderUpdateFlag flags) +void* GlRenderer::prepare(const Shape& shape, void* data, TVG_UNUSED const RenderTransform* transform, 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 7d63c5a7..c545f82a 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, RenderUpdateFlag flags) override; + void* prepare(const Shape& shape, void* data, const RenderTransform* transform, 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/tvgSwCommon.h b/src/lib/sw_engine/tvgSwCommon.h index f90f44bb..47a69154 100644 --- a/src/lib/sw_engine/tvgSwCommon.h +++ b/src/lib/sw_engine/tvgSwCommon.h @@ -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); diff --git a/src/lib/sw_engine/tvgSwRenderer.cpp b/src/lib/sw_engine/tvgSwRenderer.cpp index 8c6ee95f..aac65fd3 100644 --- a/src/lib/sw_engine/tvgSwRenderer.cpp +++ b/src/lib/sw_engine/tvgSwRenderer.cpp @@ -36,6 +36,7 @@ struct SwTask : Task Matrix* transform = nullptr; SwSurface* surface = nullptr; RenderUpdateFlag flags = RenderUpdateFlag::None; + vector 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(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& compList, RenderUpdateFlag flags) { //prepare task auto task = static_cast(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(comp.edata)->get(); + task->compList.assign(compList.begin(), compList.end()); + } if (transform) { if (!task->transform) task->transform = static_cast(malloc(sizeof(Matrix))); diff --git a/src/lib/sw_engine/tvgSwRenderer.h b/src/lib/sw_engine/tvgSwRenderer.h index 5b399900..417d3a7e 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, RenderUpdateFlag flags) override; + void* prepare(const Shape& shape, void* data, const RenderTransform* transform, vector& compList, RenderUpdateFlag flags) override; bool dispose(const Shape& shape, void *data) override; bool preRender() override; bool postRender() override; diff --git a/src/lib/sw_engine/tvgSwRle.cpp b/src/lib/sw_engine/tvgSwRle.cpp index de20d617..38f62fd2 100644 --- a/src/lib/sw_engine/tvgSwRle.cpp +++ b/src/lib/sw_engine/tvgSwRle.cpp @@ -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(bbox->min.x); + auto miny = static_cast(bbox->min.y); + auto maxx = minx + static_cast(bbox->max.x - bbox->min.x) - 1; + auto maxy = miny + static_cast(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(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(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(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); +} diff --git a/src/lib/sw_engine/tvgSwShape.cpp b/src/lib/sw_engine/tvgSwShape.cpp index 0c6391c5..49166ecf 100644 --- a/src/lib/sw_engine/tvgSwShape.cpp +++ b/src/lib/sw_engine/tvgSwShape.cpp @@ -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 +} diff --git a/src/lib/tvgCanvasImpl.h b/src/lib/tvgCanvasImpl.h index e2de0333..835b7269 100644 --- a/src/lib/tvgCanvasImpl.h +++ b/src/lib/tvgCanvasImpl.h @@ -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 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_ */ diff --git a/src/lib/tvgPaint.cpp b/src/lib/tvgPaint.cpp index 7c490bab..f12f37fc 100644 --- a/src/lib/tvgPaint.cpp +++ b/src/lib/tvgPaint.cpp @@ -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 target, CompMethod method) const noexcept +{ + if (pImpl->composite(target.release(), method)) return Result::Success; + return Result::InsufficientCondition; +} + diff --git a/src/lib/tvgPaint.h b/src/lib/tvgPaint.h index 4b49520b..e46bf636 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 bool update(RenderMethod& renderer, const RenderTransform* transform, RenderUpdateFlag pFlag) = 0; + virtual bool update(RenderMethod& renderer, const RenderTransform* transform, vector 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& compList, void **edata, uint32_t pFlag) { if (flag & RenderUpdateFlag::Transform) { if (!rTransform) return false; @@ -135,14 +139,30 @@ namespace tvg auto newFlag = static_cast(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(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 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 diff --git a/src/lib/tvgPictureImpl.h b/src/lib/tvgPictureImpl.h index 0f07ed1c..6cea5852 100644 --- a/src/lib/tvgPictureImpl.h +++ b/src/lib/tvgPictureImpl.h @@ -57,13 +57,13 @@ struct Picture::Impl } - bool update(RenderMethod &renderer, const RenderTransform* transform, RenderUpdateFlag flag) + bool update(RenderMethod &renderer, const RenderTransform* transform, vector& 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_ diff --git a/src/lib/tvgRender.h b/src/lib/tvgRender.h index f710a0f6..72e7bea0 100644 --- a/src/lib/tvgRender.h +++ b/src/lib/tvgRender.h @@ -22,6 +22,7 @@ #ifndef _TVG_RENDER_H_ #define _TVG_RENDER_H_ +#include #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& 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_ diff --git a/src/lib/tvgSceneImpl.h b/src/lib/tvgSceneImpl.h index c384d8c7..4bc9bdaa 100644 --- a/src/lib/tvgSceneImpl.h +++ b/src/lib/tvgSceneImpl.h @@ -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& compList, void** edata, RenderUpdateFlag flag) { - for(auto paint: paints) { - if (!paint->pImpl->update(renderer, transform, static_cast(flag))) return false; + for (auto paint: paints) { + if (!paint->pImpl->update(renderer, transform, compList, edata, static_cast(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_ diff --git a/src/lib/tvgShapeImpl.h b/src/lib/tvgShapeImpl.h index 18e58e6d..ff2d2545 100644 --- a/src/lib/tvgShapeImpl.h +++ b/src/lib/tvgShapeImpl.h @@ -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& compList, void** edata, RenderUpdateFlag pFlag) { - edata = renderer.prepare(*shape, edata, transform, static_cast(pFlag | flag)); - + this->edata = renderer.prepare(*shape, this->edata, transform, compList, static_cast(pFlag | flag)); flag = RenderUpdateFlag::None; - - if (edata) return true; + if (edata) *edata = this->edata; + if (this->edata) return true; return false; }