From 538db6e881c6149ee579321fa88599be9086f17d Mon Sep 17 00:00:00 2001 From: Michal Szczecinski Date: Thu, 10 Sep 2020 12:55:08 +0200 Subject: [PATCH] shape: added duplicate api. Changes: 1. New shape->duplicate(Shape) api. 2. New example: testDuplicate 3. Added capi binding for duploicate api 4. Added capi duplication test in testCapi.c Description: Added implementation of duplicate api. For now it supports stroke properties and shape properties (fill color, path) duplication. TODO: Implement gradient properties duplication --- inc/thorvg.h | 5 ++ inc/thorvg_capi.h | 2 +- src/bindings/capi/tvgCapi.cpp | 8 ++ src/lib/tvgPaint.cpp | 2 +- src/lib/tvgPaint.h | 2 +- src/lib/tvgPicture.cpp | 9 ++- src/lib/tvgScene.cpp | 9 ++- src/lib/tvgShape.cpp | 10 ++- src/lib/tvgShapeImpl.h | 42 +++++++++- src/lib/tvgShapePath.h | 1 - test/makefile | 1 + test/testCapi.c | 11 +++ test/testDuplicate.cpp | 148 ++++++++++++++++++++++++++++++++++ 13 files changed, 242 insertions(+), 8 deletions(-) create mode 100644 test/testDuplicate.cpp diff --git a/inc/thorvg.h b/inc/thorvg.h index 16252fe9..c6fb0373 100644 --- a/inc/thorvg.h +++ b/inc/thorvg.h @@ -92,6 +92,8 @@ public: Result transform(const Matrix& m) noexcept; Result bounds(float* x, float* y, float* w, float* h) const noexcept; + virtual std::unique_ptr duplicate() const noexcept = 0; + _TVG_DECLARE_ACCESSOR(); _TVG_DECLARE_PRIVATE(Paint); }; @@ -248,6 +250,7 @@ public: StrokeJoin strokeJoin() const noexcept; static std::unique_ptr gen() noexcept; + std::unique_ptr duplicate() const noexcept override; _TVG_DECLARE_PRIVATE(Shape); }; @@ -271,6 +274,7 @@ public: Result viewbox(float* x, float* y, float* w, float* h) const noexcept; static std::unique_ptr gen() noexcept; + std::unique_ptr duplicate() const noexcept override; _TVG_DECLARE_PRIVATE(Picture); }; @@ -293,6 +297,7 @@ public: Result reserve(uint32_t size) noexcept; static std::unique_ptr gen() noexcept; + std::unique_ptr duplicate() const noexcept override; _TVG_DECLARE_PRIVATE(Scene); }; diff --git a/inc/thorvg_capi.h b/inc/thorvg_capi.h index 8ec3f99b..5231d26c 100644 --- a/inc/thorvg_capi.h +++ b/inc/thorvg_capi.h @@ -120,7 +120,7 @@ TVG_EXPORT Tvg_Result tvg_paint_scale(Tvg_Paint* paint, float factor); TVG_EXPORT Tvg_Result tvg_paint_rotate(Tvg_Paint* paint, float degree); TVG_EXPORT Tvg_Result tvg_paint_translate(Tvg_Paint* paint, float x, float y); TVG_EXPORT Tvg_Result tvg_paint_transform(Tvg_Paint* paint, const Tvg_Matrix* m); - +TVG_EXPORT Tvg_Paint* tvg_paint_duplicate(Tvg_Paint* paint); /************************************************************************/ /* Shape API */ diff --git a/src/bindings/capi/tvgCapi.cpp b/src/bindings/capi/tvgCapi.cpp index be2ec92e..7e3ab478 100644 --- a/src/bindings/capi/tvgCapi.cpp +++ b/src/bindings/capi/tvgCapi.cpp @@ -177,6 +177,14 @@ TVG_EXPORT Tvg_Result tvg_paint_transform(Tvg_Paint* paint, const Tvg_Matrix* m) } + +TVG_EXPORT Tvg_Paint* tvg_paint_duplicate(Tvg_Paint* paint) +{ + if (!paint) return NULL; + return (Tvg_Paint*) reinterpret_cast(paint)->duplicate().release(); +} + + /************************************************************************/ /* Shape API */ /************************************************************************/ diff --git a/src/lib/tvgPaint.cpp b/src/lib/tvgPaint.cpp index 3105e279..fbbad597 100644 --- a/src/lib/tvgPaint.cpp +++ b/src/lib/tvgPaint.cpp @@ -71,4 +71,4 @@ Result Paint::bounds(float* x, float* y, float* w, float* h) const noexcept { if (IMPL->bounds(x, y, w, h)) return Result::Success; return Result::InsufficientCondition; -} \ No newline at end of file +} diff --git a/src/lib/tvgPaint.h b/src/lib/tvgPaint.h index 24b6768c..709a99ac 100644 --- a/src/lib/tvgPaint.h +++ b/src/lib/tvgPaint.h @@ -177,4 +177,4 @@ namespace tvg }; } -#endif //_TVG_PAINT_H_ \ No newline at end of file +#endif //_TVG_PAINT_H_ diff --git a/src/lib/tvgPicture.cpp b/src/lib/tvgPicture.cpp index 7c20a372..a3676e07 100644 --- a/src/lib/tvgPicture.cpp +++ b/src/lib/tvgPicture.cpp @@ -62,4 +62,11 @@ Result Picture::viewbox(float* x, float* y, float* w, float* h) const noexcept { if (IMPL->viewbox(x, y, w, h)) return Result::Success; return Result::InsufficientCondition; -} \ No newline at end of file +} + + +std::unique_ptr Picture::duplicate() const noexcept +{ + //TODO: implement + return nullptr; +} diff --git a/src/lib/tvgScene.cpp b/src/lib/tvgScene.cpp index df7362fb..11ccebbe 100644 --- a/src/lib/tvgScene.cpp +++ b/src/lib/tvgScene.cpp @@ -57,4 +57,11 @@ Result Scene::reserve(uint32_t size) noexcept IMPL->paints.reserve(size); return Result::Success; -} \ No newline at end of file +} + + +std::unique_ptr Scene::duplicate() const noexcept +{ + //TODO: implement + return nullptr; +} diff --git a/src/lib/tvgShape.cpp b/src/lib/tvgShape.cpp index 8d0beb8c..e75af91b 100644 --- a/src/lib/tvgShape.cpp +++ b/src/lib/tvgShape.cpp @@ -49,6 +49,14 @@ unique_ptr Shape::gen() noexcept } +unique_ptr Shape::duplicate() const noexcept +{ + auto shape = Shape::gen(); + shape->pImpl->duplicate(IMPL); + return shape; +} + + Result Shape::reset() noexcept { IMPL->path->reset(); @@ -402,4 +410,4 @@ StrokeJoin Shape::strokeJoin() const noexcept if (!IMPL->stroke) return StrokeJoin::Bevel; return IMPL->stroke->join; -} \ No newline at end of file +} diff --git a/src/lib/tvgShapeImpl.h b/src/lib/tvgShapeImpl.h index 612bfe34..5cda05a8 100644 --- a/src/lib/tvgShapeImpl.h +++ b/src/lib/tvgShapeImpl.h @@ -162,6 +162,46 @@ struct Shape::Impl return true; } + + void duplicate(Shape::Impl *s) + { + if (memcmp(color, s->color, sizeof(color))) { + memcpy(color, s->color, sizeof(color)); + flag = RenderUpdateFlag::Color; + } + + if (s->path) { + path = new ShapePath(); + + path->cmdCnt = s->path->cmdCnt; + path->ptsCnt = s->path->ptsCnt; + path->reservedCmdCnt = s->path->reservedCmdCnt; + path->reservedPtsCnt = s->path->reservedPtsCnt; + + path->cmds = static_cast(malloc(sizeof(PathCommand) * path->reservedCmdCnt)); + path->pts = static_cast(malloc(sizeof(Point) * path->reservedPtsCnt)); + + memcpy(path->cmds, s->path->cmds, sizeof(PathCommand) * s->path->cmdCnt); + memcpy(path->pts, s->path->pts, sizeof(Point) * s->path->ptsCnt); + + flag = RenderUpdateFlag::Path; + } + + if (s->stroke) { + stroke = new ShapeStroke(); + strokeCap(s->stroke->cap); + strokeColor(s->stroke->color[0], s->stroke->color[1], + s->stroke->color[2], s->stroke->color[3]); + strokeJoin(s->stroke->join); + strokeWidth(s->stroke->width); + + if (s->stroke->dashPattern && s->stroke->dashCnt > 0) { + strokeDash(s->stroke->dashPattern, s->stroke->dashCnt); + } + } + + //TODO: add fill + } }; -#endif //_TVG_SHAPE_IMPL_H_ \ No newline at end of file +#endif //_TVG_SHAPE_IMPL_H_ diff --git a/src/lib/tvgShapePath.h b/src/lib/tvgShapePath.h index f3e0ae59..19bbe2f7 100644 --- a/src/lib/tvgShapePath.h +++ b/src/lib/tvgShapePath.h @@ -38,7 +38,6 @@ struct ShapePath uint32_t ptsCnt = 0; uint32_t reservedPtsCnt = 0; - ~ShapePath() { if (cmds) free(cmds); diff --git a/test/makefile b/test/makefile index 2bdd5499..b5028b14 100644 --- a/test/makefile +++ b/test/makefile @@ -21,4 +21,5 @@ all: gcc -o testAsync testAsync.cpp -g -lstdc++ `pkg-config --cflags --libs elementary thorvg` gcc -o testArc testArc.cpp -g -lstdc++ `pkg-config --cflags --libs elementary thorvg` gcc -o testMultiCanvas testMultiCanvas.cpp -g -lstdc++ `pkg-config --cflags --libs elementary thorvg` + gcc -o testDuplicate testDuplicate.cpp -g -lstdc++ `pkg-config --cflags --libs elementary thorvg` gcc -o testCapi testCapi.c -g `pkg-config --cflags --libs elementary thorvg` diff --git a/test/testCapi.c b/test/testCapi.c index ab405ffe..82df1f57 100644 --- a/test/testCapi.c +++ b/test/testCapi.c @@ -135,6 +135,17 @@ void testCapi() for(int i=0; i < ptsCnt; ++i) { printf("(%.2lf, %.2lf)\n", pts[i].x, pts[i].y); } + + //Origin paint for duplicated + Tvg_Paint* org = tvg_shape_new(); + tvg_shape_append_rect(org, 550, 10, 100, 100, 0, 0); + tvg_shape_set_stroke_width(org, 3); + tvg_shape_set_stroke_color(org, 255, 0, 0, 255); + tvg_shape_set_fill_color(org, 0, 255, 0, 255); + + //Duplicated paint test - should copy rectangle parameters from origin + Tvg_Paint* dup = tvg_paint_duplicate(org); + tvg_canvas_push(canvas, dup); tvg_canvas_draw(canvas); tvg_canvas_sync(canvas); diff --git a/test/testDuplicate.cpp b/test/testDuplicate.cpp new file mode 100644 index 00000000..47fed103 --- /dev/null +++ b/test/testDuplicate.cpp @@ -0,0 +1,148 @@ +#include "testCommon.h" + +/************************************************************************/ +/* Drawing Commands */ +/************************************************************************/ + +void tvgDrawCmds(tvg::Canvas* canvas) +{ + if (!canvas) return; + + //Prepare first shape, which will not be drawn + auto shape1 = tvg::Shape::gen(); + shape1->appendRect(10, 10, 200, 200, 0, 0); + shape1->appendRect(220, 10, 100, 100, 0, 0); + + shape1->stroke(3); + shape1->stroke(0, 255, 0, 255); + + float dashPattern[2] = {4, 4}; + shape1->stroke(dashPattern, 2); + shape1->fill(255, 0, 0, 255); + + //Create second shape and duplicate parameters + auto shape2 = shape1->duplicate(); + + //Create third shape, duplicate from first and add some new parameters + auto shape3 = shape1->duplicate(); + + //Get access to valid derived class type + tvg::Shape* shapePtr = reinterpret_cast(shape3.get()); + + //move shape3 to new postion to don't cover second shape + shape3->translate(0, 220); + + //append new paths + shapePtr->appendRect(340, 10, 100, 100, 0, 0); + shapePtr->fill(0, 0, 255, 255); + + //TODO: test gradient + canvas->push(move(shape2)); + canvas->push(move(shape3)); +} + + +/************************************************************************/ +/* 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; +}