From e655471e099702dd18b4bdbcfc07e314ee9a4229 Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Sun, 26 Apr 2020 15:00:29 +0900 Subject: [PATCH 01/16] common shape: support rounded rectangle. Also remove arcTo implementation since curveTo could covers it. Change-Id: Icc63eca55e51622fc80b57672f308f25f2301f85 --- src/lib/tvgShapeNode.cpp | 24 ++++- src/lib/tvgShapePath.h | 243 -------------------------------------------- test/testMultipleShapes.cpp | 2 +- test/testShape.cpp | 2 +- test/testStroke.cpp | 2 +- 5 files changed, 24 insertions(+), 249 deletions(-) diff --git a/src/lib/tvgShapeNode.cpp b/src/lib/tvgShapeNode.cpp index 7c4ab2e..6ee0b7a 100644 --- a/src/lib/tvgShapeNode.cpp +++ b/src/lib/tvgShapeNode.cpp @@ -23,6 +23,7 @@ /************************************************************************/ /* Internal Class Implementation */ /************************************************************************/ +constexpr auto PATH_KAPPA = 0.552284f; struct ShapeFill { @@ -139,8 +140,14 @@ int ShapeNode::appendCircle(float cx, float cy, float radius) noexcept auto impl = pImpl.get(); assert(impl); - impl->path->reserve(5, 13); //decide size experimentally (move + curve * 4) - impl->path->arcTo(cx - radius, cy - radius, 2 * radius, 2 * radius, 0, 360); + auto halfKappa = radius * PATH_KAPPA; + + impl->path->reserve(6, 13); + impl->path->moveTo(cx, cy - radius); + impl->path->cubicTo(cx + halfKappa, cy - radius, cx + radius, cy - halfKappa, cx + radius, cy); + impl->path->cubicTo(cx + radius, cy + halfKappa, cx + halfKappa, cy + radius, cx, cy + radius); + impl->path->cubicTo(cx - halfKappa, cy + radius, cx - radius, cy + halfKappa, cx - radius, cy); + impl->path->cubicTo(cx - radius, cy - halfKappa, cx - halfKappa, cy - radius, cx, cy - radius); impl->path->close(); return 0; @@ -168,7 +175,18 @@ int ShapeNode::appendRect(float x, float y, float w, float h, float cornerRadius } else if (w == h && cornerRadius * 2 == w) { return appendCircle(x + (w * 0.5f), y + (h * 0.5f), cornerRadius); } else { - //... + auto halfKappa = cornerRadius * 0.5; + impl->path->reserve(10, 17); + impl->path->moveTo(x + cornerRadius, y); + impl->path->lineTo(x + w - cornerRadius, y); + impl->path->cubicTo(x + w - cornerRadius + halfKappa, y, x + w, y + cornerRadius - halfKappa, x + w, y + cornerRadius); + impl->path->lineTo(x + w, y + h - cornerRadius); + impl->path->cubicTo(x + w, y + h - cornerRadius + halfKappa, x + w - cornerRadius + halfKappa, y + h, x + w - cornerRadius, y + h); + impl->path->lineTo(x + cornerRadius, y + h); + impl->path->cubicTo(x + cornerRadius - halfKappa, y + h, x, y + h - cornerRadius + halfKappa, x, y + h - cornerRadius); + impl->path->lineTo(x, y + cornerRadius); + impl->path->cubicTo(x, y + cornerRadius - halfKappa, x + cornerRadius - halfKappa, y, x + cornerRadius, y); + impl->path->close(); } return 0; diff --git a/src/lib/tvgShapePath.h b/src/lib/tvgShapePath.h index 8c35bbe..6fda0f0 100644 --- a/src/lib/tvgShapePath.h +++ b/src/lib/tvgShapePath.h @@ -23,14 +23,6 @@ /* Internal Class Implementation */ /************************************************************************/ -constexpr auto PATH_KAPPA = 0.552284f; - -struct ShapePath; - -static float _arcAngle(float angle); -static int _arcToCubic(ShapePath& path, const Point* pts, size_t ptsCnt); -static void _findEllipseCoords(float x, float y, float w, float h, float startAngle, float sweepAngle, Point& ptStart, Point& ptEnd); - struct ShapePath { PathCommand* cmds = nullptr; @@ -120,144 +112,6 @@ struct ShapePath } - int arcTo(float x, float y, float w, float h, float startAngle, float sweepAngle) - { - if ((fabsf(w) < FLT_EPSILON) || (fabsf(h) < FLT_EPSILON)) return -1; - if (fabsf(sweepAngle) < FLT_EPSILON) return -1; - - if (sweepAngle > 360) sweepAngle = 360; - else if (sweepAngle < -360) sweepAngle = -360; - - auto half_w = w * 0.5f; - auto half_h = h * 0.5f; - auto half_w_kappa = half_w * PATH_KAPPA; - auto half_h_kappa = half_h * PATH_KAPPA; - - //Curves for arc - Point pts[13] { - //start point: 0 degree - {x + w, y + half_h}, - - //0 -> 90 degree - {x + w, y + half_h + half_h_kappa}, - {x + half_w + half_w_kappa, y + h}, - {x + half_w, y + h}, - - //90 -> 180 degree - {x + half_w - half_w_kappa, y + h}, - {x, y + half_h + half_h_kappa}, - {x, y + half_h}, - - //180 -> 270 degree - {x, y + half_h - half_h_kappa}, - {x + half_w - half_w_kappa, y}, - {x + half_w, y}, - - //270 -> 0 degree - {x + half_w + half_w_kappa, y}, - {x + w, y + half_h - half_h_kappa}, - {x + w, y + half_w} - }; - - auto ptsCnt = 1; //one is reserved for the start point - Point curves[13]; - - //perfect circle: special case fast paths - if (fabsf(startAngle) <= FLT_EPSILON) { - if (fabsf(sweepAngle - 360) <= FLT_EPSILON) { - for (int i = 11; i >= 0; --i) { - curves[ptsCnt++] = pts[i]; - } - curves[0] = pts[12]; - return _arcToCubic(*this, curves, ptsCnt); - } else if (fabsf(sweepAngle + 360) <= FLT_EPSILON) { - for (int i = 1; i <= 12; ++i) { - curves[ptsCnt++] = pts[i]; - } - curves[0] = pts[0]; - return _arcToCubic(*this, curves, ptsCnt); - } - } - - auto startSegment = static_cast(floor(startAngle / 90)); - auto endSegment = static_cast(floor((startAngle + sweepAngle) / 90)); - auto startDelta = (startAngle - (startSegment * 90)) / 90; - auto endDelta = ((startAngle + sweepAngle) - (endSegment * 90)) / 90; - auto delta = sweepAngle > 0 ? 1 : -1; - - if (delta < 0) { - startDelta = 1 - startDelta; - endDelta = 1 - endDelta; - } - - //avoid empty start segment - if (fabsf(startDelta - 1) < FLT_EPSILON) { - startDelta = 0; - startSegment += delta; - } - - //avoid empty end segment - if (fabsf(endDelta) < FLT_EPSILON) { - endDelta = 1; - endSegment -= delta; - } - - startDelta = _arcAngle(startDelta * 90); - endDelta = _arcAngle(endDelta * 90); - - auto splitAtStart = (fabsf(startDelta) >= FLT_EPSILON) ? true : false; - auto splitAtEnd = (fabsf(endDelta - 1.0f) >= FLT_EPSILON) ? true : false; - auto end = endSegment + delta; - - //empty arc? - if (startSegment == end) { - auto quadrant = 3 - ((startSegment % 4) + 4) % 4; - auto i = 3 * quadrant; - curves[0] = (delta > 0) ? pts[i + 3] : pts[i]; - return _arcToCubic(*this, curves, ptsCnt); - } - - Point ptStart, ptEnd; - _findEllipseCoords(x, y, w, h, startAngle, sweepAngle, ptStart, ptEnd); - - for (auto i = startSegment; i != end; i += delta) { - //auto quadrant = 3 - ((i % 4) + 4) % 4; - //auto j = 3 * quadrant; - - if (delta > 0) { - //TODO: bezier - } else { - //TODO: bezier - } - - //empty arc? - if (startSegment == endSegment && (fabsf(startDelta - endDelta) < FLT_EPSILON)) { - curves[0] = ptStart; - return _arcToCubic(*this, curves, ptsCnt); - } - - if (i == startSegment) { - if (i == endSegment && splitAtEnd) { - //TODO: bezier - } else if (splitAtStart) { - //TODO: bezier - } - } else if (i == endSegment && splitAtEnd) { - //TODO: bezier - } - - //push control points - //curves[ptsCnt++] = ctrlPt1; - //curves[ptsCnt++] = ctrlPt2; - //curves[ptsCnt++] = endPt; - cout << "ArcTo: Not Implemented!" << endl; - } - - curves[ptsCnt - 1] = ptEnd; - - return _arcToCubic(*this, curves, ptsCnt); - } - int close() { if (cmdCnt + 1 > reservedCmdCnt) reserveCmd((cmdCnt + 1) * 2); @@ -289,101 +143,4 @@ struct ShapePath } }; -static float _arcAngle(float angle) - { - if (angle < FLT_EPSILON) return 0; - if (fabsf(angle - 90) < FLT_EPSILON) return 1; - - auto radian = (angle / 180) * M_PI; - auto cosAngle = cos(radian); - auto sinAngle = sin(radian); - - //initial guess - auto tc = angle / 90; - - /* do some iterations of newton's method to approximate cosAngle - finds the zero of the function b.pointAt(tc).x() - cosAngle */ - tc -= ((((2 - 3 * PATH_KAPPA) * tc + 3 * (PATH_KAPPA - 1)) * tc) * tc + 1 - cosAngle) // value - / (((6 - 9 * PATH_KAPPA) * tc + 6 * (PATH_KAPPA - 1)) * tc); // derivative - tc -= ((((2 - 3 * PATH_KAPPA) * tc + 3 * (PATH_KAPPA - 1)) * tc) * tc + 1 - cosAngle) // value - / (((6 - 9 * PATH_KAPPA) * tc + 6 * (PATH_KAPPA - 1)) * tc); // derivative - - // initial guess - auto ts = tc; - - /* do some iterations of newton's method to approximate sin_angle - finds the zero of the function b.pointAt(tc).y() - sinAngle */ - ts -= ((((3 * PATH_KAPPA - 2) * ts - 6 * PATH_KAPPA + 3) * ts + 3 * PATH_KAPPA) * ts - sinAngle) - / (((9 * PATH_KAPPA - 6) * ts + 12 * PATH_KAPPA - 6) * ts + 3 * PATH_KAPPA); - ts -= ((((3 * PATH_KAPPA - 2) * ts - 6 * PATH_KAPPA + 3) * ts + 3 * PATH_KAPPA) * ts - sinAngle) - / (((9 * PATH_KAPPA - 6) * ts + 12 * PATH_KAPPA - 6) * ts + 3 * PATH_KAPPA); - - //use the average of the t that best approximates cos_angle and the t that best approximates sin_angle - return (0.5 * (tc + ts)); -} - - -static int _arcToCubic(ShapePath& path, const Point* pts, size_t ptsCnt) -{ - assert(pts); - - if (path.cmdCnt > 0 && path.cmds[path.cmdCnt] != PathCommand::Close) { - if (path.lineTo(pts[0].x, pts[0].y)) return -1; - } else { - if (path.moveTo(pts[0].x, pts[0].y)) return -1; - } - - for (size_t i = 1; i < ptsCnt; i += 3) { - if (path.cubicTo(pts[i].x, pts[i].y, pts[i+1].x, pts[i+1].y, pts[i+2].x, pts[i+2].y)) { - return -1; - } - } - - return 0; -} - - -static void _findEllipseCoords(float x, float y, float w, float h, float startAngle, float sweepAngle, Point& ptStart, Point& ptEnd) -{ - float angles[2] = {startAngle, startAngle + sweepAngle}; - float half_w = w * 0.5f; - float half_h = h * 0.5f; - float cx = x + half_w; - float cy = y + half_h; - Point* pts[2] = {&ptStart, &ptEnd}; - - for (auto i = 0; i < 2; ++i) { - auto theta = angles[i] - 360 * floor(angles[i] / 360); - auto t = theta / 90; - auto quadrant = static_cast(t); //truncate - t -= quadrant; - t = _arcAngle(90 * t); - - //swap x and y? - if (quadrant & 1) t = (1 - t); - - //bezier coefficients - auto m = 1 - t; - auto b = m * m; - auto c = t * t; - auto d = c * t; - auto a = b * m; - b *= 3 * t; - c *= 3 * m; - - auto px = a + b + c * PATH_KAPPA; - auto py = d + c + b * PATH_KAPPA; - - //left quadrants - if (quadrant == 1 || quadrant == 2) px = -px; - - //top quadrants - if (quadrant == 0 || quadrant == 1) py = -py; - - pts[i]->x = cx + half_w * px; - pts[i]->y = cy + half_h * py; - } -} - - #endif //_TVG_SHAPE_PATH_CPP_ diff --git a/test/testMultipleShapes.cpp b/test/testMultipleShapes.cpp index 3826f8f..f1bdfe8 100644 --- a/test/testMultipleShapes.cpp +++ b/test/testMultipleShapes.cpp @@ -19,7 +19,7 @@ void tvgtest() //Prepare Rectangle auto shape1 = tvg::ShapeNode::gen(); - shape1->appendRect(0, 0, 400, 400, 0); //x, y, w, h, corner_radius + shape1->appendRect(0, 0, 400, 400, 50); //x, y, w, h, cornerRadius shape1->fill(0, 255, 0, 255); //r, g, b, a canvas->push(move(shape1)); diff --git a/test/testShape.cpp b/test/testShape.cpp index e42cba8..7d0aa88 100644 --- a/test/testShape.cpp +++ b/test/testShape.cpp @@ -18,7 +18,7 @@ void tvgtest() //Prepare a Shape (Rectangle) auto shape1 = tvg::ShapeNode::gen(); - shape1->appendRect(0, 0, 400, 400, 0); //x, y, w, h, corner_radius + shape1->appendRect(0, 0, 400, 400, 0); //x, y, w, h, cornerRadius shape1->fill(255, 0, 0, 255); //r, g, b, a /* Push the shape into the Canvas drawing list diff --git a/test/testStroke.cpp b/test/testStroke.cpp index 6a51412..de84c32 100644 --- a/test/testStroke.cpp +++ b/test/testStroke.cpp @@ -17,7 +17,7 @@ int main(int argc, char **argv) //Prepare a Shape auto shape1 = tvg::ShapeNode::gen(); - shape1->rect(0, 0, 400, 400, 0.1); //x, y, w, h, corner_radius + shape1->rect(0, 0, 400, 400, 0.1); //x, y, w, h, cornerRadius shape1->fill(0, 255, 0, 255); //Stroke Style -- 2.7.4 From 1e96cc2b1061259555118f7baaab68b7b432fc7f Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Sun, 26 Apr 2020 18:18:52 +0900 Subject: [PATCH 02/16] common shape: support ellipse Now, you can pass separate width radius and height radius in circle. Change-Id: Ie1e2b58fbb399d8ef74d55d83ec48d8f4323f21e --- inc/tizenvg.h | 2 +- src/lib/tvgShapeNode.cpp | 17 +++++++++-------- test/testMultipleShapes.cpp | 10 ++++++++-- 3 files changed, 18 insertions(+), 11 deletions(-) diff --git a/inc/tizenvg.h b/inc/tizenvg.h index 64c8ee4..e897439 100644 --- a/inc/tizenvg.h +++ b/inc/tizenvg.h @@ -93,7 +93,7 @@ public: int clear() noexcept; int appendRect(float x, float y, float w, float h, float cornerRadius) noexcept; - int appendCircle(float cx, float cy, float radius) noexcept; + int appendCircle(float cx, float cy, float radiusW, float radiusH) noexcept; int fill(size_t r, size_t g, size_t b, size_t a) noexcept; diff --git a/src/lib/tvgShapeNode.cpp b/src/lib/tvgShapeNode.cpp index 6ee0b7a..a6e4511 100644 --- a/src/lib/tvgShapeNode.cpp +++ b/src/lib/tvgShapeNode.cpp @@ -135,19 +135,20 @@ int ShapeNode::pathCoords(const Point** pts) const noexcept } -int ShapeNode::appendCircle(float cx, float cy, float radius) noexcept +int ShapeNode::appendCircle(float cx, float cy, float radiusW, float radiusH) noexcept { auto impl = pImpl.get(); assert(impl); - auto halfKappa = radius * PATH_KAPPA; + auto halfKappaW = radiusW * PATH_KAPPA; + auto halfKappaH = radiusH * PATH_KAPPA; impl->path->reserve(6, 13); - impl->path->moveTo(cx, cy - radius); - impl->path->cubicTo(cx + halfKappa, cy - radius, cx + radius, cy - halfKappa, cx + radius, cy); - impl->path->cubicTo(cx + radius, cy + halfKappa, cx + halfKappa, cy + radius, cx, cy + radius); - impl->path->cubicTo(cx - halfKappa, cy + radius, cx - radius, cy + halfKappa, cx - radius, cy); - impl->path->cubicTo(cx - radius, cy - halfKappa, cx - halfKappa, cy - radius, cx, cy - radius); + impl->path->moveTo(cx, cy - radiusH); + impl->path->cubicTo(cx + halfKappaW, cy - radiusH, cx + radiusW, cy - halfKappaH, cx + radiusW, cy); + impl->path->cubicTo(cx + radiusW, cy + halfKappaH, cx + halfKappaW, cy + radiusH, cx, cy + radiusH); + impl->path->cubicTo(cx - halfKappaW, cy + radiusH, cx - radiusW, cy + halfKappaH, cx - radiusW, cy); + impl->path->cubicTo(cx - radiusW, cy - halfKappaH, cx - halfKappaW, cy - radiusH, cx, cy - radiusH); impl->path->close(); return 0; @@ -173,7 +174,7 @@ int ShapeNode::appendRect(float x, float y, float w, float h, float cornerRadius impl->path->close(); //circle } else if (w == h && cornerRadius * 2 == w) { - return appendCircle(x + (w * 0.5f), y + (h * 0.5f), cornerRadius); + return appendCircle(x + (w * 0.5f), y + (h * 0.5f), cornerRadius, cornerRadius); } else { auto halfKappa = cornerRadius * 0.5; impl->path->reserve(10, 17); diff --git a/test/testMultipleShapes.cpp b/test/testMultipleShapes.cpp index f1bdfe8..a73d2b6 100644 --- a/test/testMultipleShapes.cpp +++ b/test/testMultipleShapes.cpp @@ -17,7 +17,7 @@ void tvgtest() auto canvas = tvg::SwCanvas::gen(buffer, WIDTH, HEIGHT); //canvas->reserve(2); //reserve 2 shape nodes (optional) - //Prepare Rectangle + //Prepare Round Rectangle auto shape1 = tvg::ShapeNode::gen(); shape1->appendRect(0, 0, 400, 400, 50); //x, y, w, h, cornerRadius shape1->fill(0, 255, 0, 255); //r, g, b, a @@ -25,10 +25,16 @@ void tvgtest() //Prepare Circle auto shape2 = tvg::ShapeNode::gen(); - shape2->appendCircle(400, 400, 200); //cx, cy, radius + shape2->appendCircle(400, 400, 200, 200); //cx, cy, radiusW, radiusH shape2->fill(255, 255, 0, 255); //r, g, b, a canvas->push(move(shape2)); + //Prepare Ellipse + auto shape3 = tvg::ShapeNode::gen(); + shape3->appendCircle(600, 600, 150, 100); //cx, cy, radiusW, radiusH + shape3->fill(0, 255, 255, 255); //r, g, b, a + canvas->push(move(shape3)); + //Draw the Shapes onto the Canvas canvas->draw(); canvas->sync(); -- 2.7.4 From dc87a59d5399422192054817547a461ec2ceba0e Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Sun, 26 Apr 2020 18:41:27 +0900 Subject: [PATCH 03/16] test: add merge shapes Change-Id: I2dc91d3514aed1cbb1bd3490ea5d91debeab662a --- test/makefile | 3 +- test/testMergeShapes.cpp | 63 ++++++++++++++++++++++ ...{testMultipleShapes.cpp => testMultiShapes.cpp} | 0 3 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 test/testMergeShapes.cpp rename test/{testMultipleShapes.cpp => testMultiShapes.cpp} (100%) diff --git a/test/makefile b/test/makefile index 00b7d7f..bb80729 100644 --- a/test/makefile +++ b/test/makefile @@ -1,3 +1,4 @@ all: gcc -o testShape testShape.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` - gcc -o testMultipleShapes testMultipleShapes.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` + gcc -o testMultiShapes testMultiShapes.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` + gcc -o testMergeShapes testMergeShapes.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` diff --git a/test/testMergeShapes.cpp b/test/testMergeShapes.cpp new file mode 100644 index 0000000..3242df7 --- /dev/null +++ b/test/testMergeShapes.cpp @@ -0,0 +1,63 @@ +#include +#include + +using namespace std; + +#define WIDTH 800 +#define HEIGHT 800 + +static uint32_t buffer[WIDTH * HEIGHT]; + +void tvgtest() +{ + //Initialize TizenVG Engine + tvg::Engine::init(); + + //Create a Canvas + auto canvas = tvg::SwCanvas::gen(buffer, WIDTH, HEIGHT); + + //Prepare a Shape (Rectangle + Rectangle + Circle) + auto shape1 = tvg::ShapeNode::gen(); + shape1->appendRect(0, 0, 200, 200, 0); //x, y, w, h, cornerRadius + shape1->appendRect(100, 100, 300, 300, 100); //x, y, w, h, cornerRadius + shape1->appendCircle(500, 500, 100, 100); //cx, cy, radiusW, radiusH + //FIXME: eeek! crash! + // shape1->appendCircle(400, 600, 170, 100); //cx, cy, radiusW, radiusH + shape1->fill(255, 0, 0, 255); //r, g, b, a + + /* 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() */ + canvas->push(move(shape1)); + + canvas->draw(); + canvas->sync(); + + //Terminate TizenVG Engine + tvg::Engine::term(); +} + + +int main(int argc, char **argv) +{ + tvgtest(); + + //Show the result using EFL... + elm_init(argc, argv); + + Eo* win = elm_win_util_standard_add(NULL, "TizenVG Test"); + + Eo* img = evas_object_image_filled_add(evas_object_evas_get(win)); + evas_object_image_size_set(img, WIDTH, HEIGHT); + evas_object_image_data_set(img, buffer); + evas_object_size_hint_weight_set(img, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_show(img); + + elm_win_resize_object_add(win, img); + evas_object_geometry_set(win, 0, 0, WIDTH, HEIGHT); + evas_object_show(win); + + elm_run(); + elm_shutdown(); +} diff --git a/test/testMultipleShapes.cpp b/test/testMultiShapes.cpp similarity index 100% rename from test/testMultipleShapes.cpp rename to test/testMultiShapes.cpp -- 2.7.4 From 6be53b07794d9dbaaf73fb2d5fc5d3014f868eb4 Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Sun, 26 Apr 2020 18:47:34 +0900 Subject: [PATCH 04/16] test: close the window at exit button Change-Id: Idf46d7132ae97356637816e54dc6c267be7122e6 --- test/testMergeShapes.cpp | 7 +++++++ test/testMultiShapes.cpp | 7 +++++++ test/testShape.cpp | 6 ++++++ 3 files changed, 20 insertions(+) diff --git a/test/testMergeShapes.cpp b/test/testMergeShapes.cpp index 3242df7..549ea9e 100644 --- a/test/testMergeShapes.cpp +++ b/test/testMergeShapes.cpp @@ -38,6 +38,12 @@ void tvgtest() tvg::Engine::term(); } +void +win_del(void *data, Evas_Object *o, void *ev) +{ + elm_exit(); +} + int main(int argc, char **argv) { @@ -47,6 +53,7 @@ int main(int argc, char **argv) elm_init(argc, argv); Eo* win = elm_win_util_standard_add(NULL, "TizenVG Test"); + evas_object_smart_callback_add(win, "delete,request", win_del, 0); Eo* img = evas_object_image_filled_add(evas_object_evas_get(win)); evas_object_image_size_set(img, WIDTH, HEIGHT); diff --git a/test/testMultiShapes.cpp b/test/testMultiShapes.cpp index a73d2b6..94d3c31 100644 --- a/test/testMultiShapes.cpp +++ b/test/testMultiShapes.cpp @@ -43,6 +43,12 @@ void tvgtest() tvg::Engine::term(); } +void +win_del(void *data, Evas_Object *o, void *ev) +{ + elm_exit(); +} + int main(int argc, char **argv) { tvgtest(); @@ -51,6 +57,7 @@ int main(int argc, char **argv) elm_init(argc, argv); Eo* win = elm_win_util_standard_add(NULL, "TizenVG Test"); + evas_object_smart_callback_add(win, "delete,request", win_del, 0); Eo* img = evas_object_image_filled_add(evas_object_evas_get(win)); evas_object_image_size_set(img, WIDTH, HEIGHT); diff --git a/test/testShape.cpp b/test/testShape.cpp index 7d0aa88..c806d99 100644 --- a/test/testShape.cpp +++ b/test/testShape.cpp @@ -34,6 +34,11 @@ void tvgtest() tvg::Engine::term(); } +void +win_del(void *data, Evas_Object *o, void *ev) +{ + elm_exit(); +} int main(int argc, char **argv) { @@ -43,6 +48,7 @@ int main(int argc, char **argv) elm_init(argc, argv); Eo* win = elm_win_util_standard_add(NULL, "TizenVG Test"); + evas_object_smart_callback_add(win, "delete,request", win_del, 0); Eo* img = evas_object_image_filled_add(evas_object_evas_get(win)); evas_object_image_size_set(img, WIDTH, HEIGHT); -- 2.7.4 From 0e6faa927610d8b2a67249f248a1dd4252837f0c Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Mon, 27 Apr 2020 01:45:52 +0900 Subject: [PATCH 05/16] common: code refactoring. introduce Canvas class to replace the CanvasBase. now, SwCanvas, GlCanvas inherits this Canvas for polymorphism and remove duplicated interfaces. Change-Id: I65a87e3aa2289d04997930a54aeccd14f57dd73a --- inc/tizenvg.h | 50 +++++++++------- src/lib/meson.build | 2 +- src/lib/{tvgCanvasBase.h => tvgCanvas.cpp} | 91 ++++++++++++++++++++++++------ src/lib/tvgGlCanvas.cpp | 51 +++-------------- src/lib/tvgSwCanvas.cpp | 64 ++++----------------- 5 files changed, 127 insertions(+), 131 deletions(-) rename src/lib/{tvgCanvasBase.h => tvgCanvas.cpp} (61%) diff --git a/inc/tizenvg.h b/inc/tizenvg.h index e897439..babca5d 100644 --- a/inc/tizenvg.h +++ b/inc/tizenvg.h @@ -72,7 +72,33 @@ class TIZENVG_EXPORT PaintNode { public: virtual ~PaintNode() {} - virtual int update(RenderMethod* engine) = 0; + virtual int update(RenderMethod*) = 0; +}; + + +/** + * @class Canvas + * + * @ingroup TizenVG + * + * @brief description... + * + */ +class TIZENVG_EXPORT Canvas +{ +public: + Canvas(RenderMethod*); + virtual ~Canvas(); + + virtual int push(std::unique_ptr paint) noexcept; + virtual int clear() noexcept; + virtual int update() noexcept; + virtual int draw(bool async = true) noexcept; + virtual int sync() = 0; + + RenderMethod* engine() noexcept; + + _TIZENVG_DECLARE_PRIVATE(Canvas); }; @@ -141,21 +167,13 @@ public: @brief description... * */ -class TIZENVG_EXPORT SwCanvas final +class TIZENVG_EXPORT SwCanvas final : public Canvas { public: ~SwCanvas(); - int push(std::unique_ptr paint) noexcept; - int clear() noexcept; - - int update() noexcept; - int draw(bool async = true) noexcept; - int sync() noexcept; - RenderMethod* engine() noexcept; - int target(uint32_t* buffer, size_t stride, size_t height) noexcept; - + int sync() noexcept override; static std::unique_ptr gen(uint32_t* buffer = nullptr, size_t stride = 0, size_t height = 0) noexcept; _TIZENVG_DECLARE_PRIVATE(SwCanvas); @@ -170,20 +188,14 @@ public: * @brief description... * */ -class TIZENVG_EXPORT GlCanvas final +class TIZENVG_EXPORT GlCanvas final : public Canvas { public: ~GlCanvas(); - int push(std::unique_ptr paint) noexcept; - int clear() noexcept; - //TODO: Gl Specific methods. Need gl backend configuration methods as well. - int update() noexcept; - int draw(bool async = true) noexcept; - int sync() noexcept { return 0; } - RenderMethod* engine() noexcept; + int sync() noexcept override; static std::unique_ptr gen() noexcept; _TIZENVG_DECLARE_PRIVATE(GlCanvas); diff --git a/src/lib/meson.build b/src/lib/meson.build index ffecb2a..976aa5e 100644 --- a/src/lib/meson.build +++ b/src/lib/meson.build @@ -5,8 +5,8 @@ source_file = [ 'tvgCommon.h', 'tvgRenderCommon.h', 'tvgEngine.cpp', - 'tvgCanvasBase.h', 'tvgShapePath.h', + 'tvgCanvas.cpp', 'tvgSwCanvas.cpp', 'tvgGlCanvas.cpp', 'tvgSceneNode.cpp', diff --git a/src/lib/tvgCanvasBase.h b/src/lib/tvgCanvas.cpp similarity index 61% rename from src/lib/tvgCanvasBase.h rename to src/lib/tvgCanvas.cpp index a2ed34b..3bde24f 100644 --- a/src/lib/tvgCanvasBase.h +++ b/src/lib/tvgCanvas.cpp @@ -14,26 +14,30 @@ * limitations under the License. * */ -#ifndef _TVG_CANVAS_BASE_CPP_ -#define _TVG_CANVAS_BASE_CPP_ +#ifndef _TVG_CANVAS_CPP_ +#define _TVG_CANVAS_CPP_ #include "tvgCommon.h" -struct CanvasBase +/************************************************************************/ +/* Internal Class Implementation */ +/************************************************************************/ + +struct Canvas::Impl { vector nodes; RenderMethod* renderer; - CanvasBase(RenderMethod *pRenderer):renderer(pRenderer) + Impl(RenderMethod *pRenderer):renderer(pRenderer) { renderer->ref(); } - ~CanvasBase() + ~Impl() { - clear(); - renderer->unref(); + clear(); + renderer->unref(); } int reserve(size_t n) @@ -43,6 +47,14 @@ struct CanvasBase return 0; } + int push(unique_ptr paint) + { + PaintNode *node = paint.release(); + assert(node); + nodes.push_back(node); + return node->update(renderer); + } + int clear() { assert(renderer); @@ -75,14 +87,6 @@ struct CanvasBase return ret; } - int push(unique_ptr paint) - { - PaintNode *node = paint.release(); - assert(node); - nodes.push_back(node); - return node->update(renderer); - } - int draw() { assert(renderer); @@ -101,4 +105,59 @@ struct CanvasBase }; -#endif /* _TVG_CANVAS_BASE_CPP_ */ + +/************************************************************************/ +/* External Class Implementation */ +/************************************************************************/ + +Canvas::Canvas(RenderMethod *pRenderer):pImpl(make_unique(pRenderer)) +{ +} + + +Canvas::~Canvas() +{ +} + + +int Canvas::push(unique_ptr paint) noexcept +{ + auto impl = pImpl.get(); + assert(impl); + + return impl->push(move(paint)); +} + + +int Canvas::clear() noexcept +{ + auto impl = pImpl.get(); + assert(impl); + return impl->clear(); +} + + +int Canvas::draw(bool async) noexcept +{ + auto impl = pImpl.get(); + assert(impl); + return impl->draw(); +} + + +int Canvas::update() noexcept +{ + auto impl = pImpl.get(); + assert(impl); + return impl->update(); +} + + +RenderMethod* Canvas::engine() noexcept +{ + auto impl = pImpl.get(); + assert(impl); + return impl->renderer; +} + +#endif /* _TVG_CANVAS_CPP_ */ diff --git a/src/lib/tvgGlCanvas.cpp b/src/lib/tvgGlCanvas.cpp index 386778d..a7719d3 100644 --- a/src/lib/tvgGlCanvas.cpp +++ b/src/lib/tvgGlCanvas.cpp @@ -18,16 +18,15 @@ #define _TVG_GLCANVAS_CPP_ #include "tvgCommon.h" -#include "tvgCanvasBase.h" #include "tvgGlRenderer.h" /************************************************************************/ /* Internal Class Implementation */ /************************************************************************/ -struct GlCanvas::Impl : CanvasBase +struct GlCanvas::Impl { - Impl() : CanvasBase(GlRenderer::inst()) {} + Impl() {} }; @@ -35,7 +34,7 @@ struct GlCanvas::Impl : CanvasBase /* External Class Implementation */ /************************************************************************/ -GlCanvas::GlCanvas() : pImpl(make_unique()) +GlCanvas::GlCanvas() : Canvas(GlRenderer::inst()), pImpl(make_unique()) { } @@ -45,51 +44,19 @@ GlCanvas::~GlCanvas() } -unique_ptr GlCanvas::gen() noexcept -{ - auto canvas = unique_ptr(new GlCanvas); - assert(canvas); - - return canvas; -} - - -int GlCanvas::push(unique_ptr paint) noexcept -{ - auto impl = pImpl.get(); - assert(impl); - return impl->push(move(paint)); -} - -int GlCanvas::clear() noexcept +int GlCanvas::sync() noexcept { - auto impl = pImpl.get(); - assert(impl); - return impl->clear(); + return 0; } -int GlCanvas::update() noexcept +unique_ptr GlCanvas::gen() noexcept { - auto impl = pImpl.get(); - assert(impl); - return impl->update(); -} - + auto canvas = unique_ptr(new GlCanvas); + assert(canvas); -RenderMethod* GlCanvas::engine() noexcept -{ - auto impl = pImpl.get(); - assert(impl); - return impl->renderer; + return canvas; } -int GlCanvas::draw(bool async) noexcept -{ - auto impl = pImpl.get(); - assert(impl); - return impl->draw(); -} - #endif /* _TVG_GLCANVAS_CPP_ */ diff --git a/src/lib/tvgSwCanvas.cpp b/src/lib/tvgSwCanvas.cpp index 442b0d7..04902fc 100644 --- a/src/lib/tvgSwCanvas.cpp +++ b/src/lib/tvgSwCanvas.cpp @@ -18,7 +18,6 @@ #define _TVG_SWCANVAS_CPP_ #include "tvgCommon.h" -#include "tvgCanvasBase.h" #include "tvgSwRenderer.h" @@ -26,9 +25,9 @@ /* Internal Class Implementation */ /************************************************************************/ -struct SwCanvas::Impl : CanvasBase +struct SwCanvas::Impl { - Impl() : CanvasBase(SwRenderer::inst()) {} + Impl() {} }; @@ -36,55 +35,30 @@ struct SwCanvas::Impl : CanvasBase /* External Class Implementation */ /************************************************************************/ -int SwCanvas::target(uint32_t* buffer, size_t stride, size_t height) noexcept -{ - auto impl = pImpl.get(); - assert(impl); - - dynamic_cast(impl->renderer)->target(buffer, stride, height); - - return 0; -} - - -int SwCanvas::draw(bool async) noexcept +SwCanvas::SwCanvas() : Canvas(SwRenderer::inst()), pImpl(make_unique()) { - auto impl = pImpl.get(); - assert(impl); - return impl->draw(); } -int SwCanvas::sync() noexcept -{ - return 0; -} - - -int SwCanvas::push(unique_ptr paint) noexcept +SwCanvas::~SwCanvas() { - auto impl = pImpl.get(); - assert(impl); - - return impl->push(move(paint)); } -int SwCanvas::clear() noexcept +int SwCanvas::target(uint32_t* buffer, size_t stride, size_t height) noexcept { - auto impl = pImpl.get(); - assert(impl); - return impl->clear(); -} + auto renderer = dynamic_cast(engine()); + assert(renderer); + if (!renderer->target(buffer, stride, height)) return -1; -SwCanvas::SwCanvas() : pImpl(make_unique()) -{ + return 0; } -SwCanvas::~SwCanvas() +int SwCanvas::sync() noexcept { + return 0; } @@ -99,20 +73,4 @@ unique_ptr SwCanvas::gen(uint32_t* buffer, size_t stride, size_t heigh return canvas; } - -int SwCanvas::update() noexcept -{ - auto impl = pImpl.get(); - assert(impl); - return impl->update(); -} - - -RenderMethod* SwCanvas::engine() noexcept -{ - auto impl = pImpl.get(); - assert(impl); - return impl->renderer; -} - #endif /* _TVG_SWCANVAS_CPP_ */ -- 2.7.4 From 809dfd8644c4b41a0958a212f24d2daa180bd51c Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Mon, 27 Apr 2020 01:52:44 +0900 Subject: [PATCH 06/16] canvas: add reserve() method. This allocates nodes slots in advance to avoid memory grow & copy. it will be benefit if you know how many shapes will be used in your canvas. Change-Id: I7d93d166c9c054078bd86593d3471a2ade3671ee --- inc/tizenvg.h | 1 + src/lib/tvgCanvas.cpp | 8 ++++++++ test/testMultiShapes.cpp | 2 +- test/testShape.cpp | 2 +- 4 files changed, 11 insertions(+), 2 deletions(-) diff --git a/inc/tizenvg.h b/inc/tizenvg.h index babca5d..1aee6bb 100644 --- a/inc/tizenvg.h +++ b/inc/tizenvg.h @@ -90,6 +90,7 @@ public: Canvas(RenderMethod*); virtual ~Canvas(); + int reserve(size_t n); virtual int push(std::unique_ptr paint) noexcept; virtual int clear() noexcept; virtual int update() noexcept; diff --git a/src/lib/tvgCanvas.cpp b/src/lib/tvgCanvas.cpp index 3bde24f..96b58e0 100644 --- a/src/lib/tvgCanvas.cpp +++ b/src/lib/tvgCanvas.cpp @@ -120,6 +120,14 @@ Canvas::~Canvas() } +int Canvas::reserve(size_t n) +{ + auto impl = pImpl.get(); + assert(impl); + return impl->reserve(n); +} + + int Canvas::push(unique_ptr paint) noexcept { auto impl = pImpl.get(); diff --git a/test/testMultiShapes.cpp b/test/testMultiShapes.cpp index 94d3c31..6fc0469 100644 --- a/test/testMultiShapes.cpp +++ b/test/testMultiShapes.cpp @@ -15,7 +15,7 @@ void tvgtest() //Create a Canvas auto canvas = tvg::SwCanvas::gen(buffer, WIDTH, HEIGHT); - //canvas->reserve(2); //reserve 2 shape nodes (optional) + canvas->reserve(3); //reserve 3 shape nodes (optional) //Prepare Round Rectangle auto shape1 = tvg::ShapeNode::gen(); diff --git a/test/testShape.cpp b/test/testShape.cpp index c806d99..b7fca2f 100644 --- a/test/testShape.cpp +++ b/test/testShape.cpp @@ -18,7 +18,7 @@ void tvgtest() //Prepare a Shape (Rectangle) auto shape1 = tvg::ShapeNode::gen(); - shape1->appendRect(0, 0, 400, 400, 0); //x, y, w, h, cornerRadius + shape1->appendRect(100, 100, 400, 400, 0); //x, y, w, h, cornerRadius shape1->fill(255, 0, 0, 255); //r, g, b, a /* Push the shape into the Canvas drawing list -- 2.7.4 From c813de45caa5f9c9e342be1dfea82ec450d09eda Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Mon, 27 Apr 2020 01:59:49 +0900 Subject: [PATCH 07/16] update gitignore file Change-Id: I0b245f1177e3df04ddc6f345213bbb1207d3e950 --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 45329ec..a6a008a 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,5 @@ build .vscode *.swp testShape +testMultiShapes +testMergeShapes -- 2.7.4 From 5120a7ae125db724c7ea32a77b0ed4819b57886a Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Mon, 27 Apr 2020 02:00:38 +0900 Subject: [PATCH 08/16] canvas: correct convention add missing noexcept option. Change-Id: I14eacb0fdb2c9bd40e365bf81bb5cc0b73239ffe --- inc/tizenvg.h | 2 +- src/lib/tvgCanvas.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/inc/tizenvg.h b/inc/tizenvg.h index 1aee6bb..c53a5dc 100644 --- a/inc/tizenvg.h +++ b/inc/tizenvg.h @@ -90,7 +90,7 @@ public: Canvas(RenderMethod*); virtual ~Canvas(); - int reserve(size_t n); + int reserve(size_t n) noexcept; virtual int push(std::unique_ptr paint) noexcept; virtual int clear() noexcept; virtual int update() noexcept; diff --git a/src/lib/tvgCanvas.cpp b/src/lib/tvgCanvas.cpp index 96b58e0..3723083 100644 --- a/src/lib/tvgCanvas.cpp +++ b/src/lib/tvgCanvas.cpp @@ -120,7 +120,7 @@ Canvas::~Canvas() } -int Canvas::reserve(size_t n) +int Canvas::reserve(size_t n) noexcept { auto impl = pImpl.get(); assert(impl); -- 2.7.4 From 46ba3352a8313b602c04da9ddcf8dea955c485b6 Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Thu, 30 Apr 2020 00:54:41 +0900 Subject: [PATCH 09/16] sw_engine: case cover out of surface boundary. Also added surface boundary test code. Change-Id: Ib4c327d12ce52d506f1b8a566ffa48e5b5b8c03e --- .gitignore | 1 + src/lib/sw_engine/tvgSwCommon.h | 12 ++++-- src/lib/sw_engine/tvgSwRaster.cpp | 3 +- src/lib/sw_engine/tvgSwRenderer.cpp | 5 ++- src/lib/sw_engine/tvgSwRle.cpp | 42 ++++++++++++------ src/lib/sw_engine/tvgSwShape.cpp | 12 +++++- test/makefile | 1 + test/testBoundary.cpp | 86 +++++++++++++++++++++++++++++++++++++ 8 files changed, 141 insertions(+), 21 deletions(-) create mode 100644 test/testBoundary.cpp diff --git a/.gitignore b/.gitignore index a6a008a..bbd3f8e 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ build testShape testMultiShapes testMergeShapes +testBoundary diff --git a/src/lib/sw_engine/tvgSwCommon.h b/src/lib/sw_engine/tvgSwCommon.h index 4b6f820..012f968 100644 --- a/src/lib/sw_engine/tvgSwCommon.h +++ b/src/lib/sw_engine/tvgSwCommon.h @@ -49,6 +49,11 @@ struct SwPoint } }; +struct SwSize +{ + SwCoord w, h; +}; + struct SwOutline { size_t* cntrs; //the contour end points @@ -63,7 +68,7 @@ struct SwOutline struct SwSpan { - uint16_t x, y; + int16_t x, y; uint16_t len; uint8_t coverage; }; @@ -90,10 +95,9 @@ struct SwShape void shapeReset(SwShape& sdata); bool shapeGenOutline(const ShapeNode& shape, SwShape& sdata); void shapeDelOutline(const ShapeNode& shape, SwShape& sdata); -bool shapeGenRle(const ShapeNode& shape, SwShape& sdata); +bool shapeGenRle(const ShapeNode& shape, SwShape& sdata, const SwSize& clip); bool shapeTransformOutline(const ShapeNode& shape, SwShape& sdata); - -SwRleData* rleRender(const SwShape& sdata); +SwRleData* rleRender(const SwShape& sdata, const SwSize& clip); bool rasterShape(Surface& surface, SwShape& sdata, size_t color); diff --git a/src/lib/sw_engine/tvgSwRaster.cpp b/src/lib/sw_engine/tvgSwRaster.cpp index 2e43a44..a71c443 100644 --- a/src/lib/sw_engine/tvgSwRaster.cpp +++ b/src/lib/sw_engine/tvgSwRaster.cpp @@ -23,13 +23,14 @@ bool rasterShape(Surface& surface, SwShape& sdata, size_t color) { SwRleData* rle = sdata.rle; - assert(rle); + if (!rle) return false; auto stride = surface.stride; auto span = rle->spans; for (size_t i = 0; i < rle->size; ++i) { assert(span); +// printf("raster y(%d) x(%d) len(%d)\n", span->y, span->x, span->len); for (auto j = 0; j < span->len; ++j) { surface.buffer[span->y * stride + span->x + j] = color; } diff --git a/src/lib/sw_engine/tvgSwRenderer.cpp b/src/lib/sw_engine/tvgSwRenderer.cpp index 425afab..ae43735 100644 --- a/src/lib/sw_engine/tvgSwRenderer.cpp +++ b/src/lib/sw_engine/tvgSwRenderer.cpp @@ -55,7 +55,6 @@ bool SwRenderer::render(const ShapeNode& shape, void *data) //invisible? size_t r, g, b, a; shape.fill(&r, &g, &b, &a); - if (a == 0) return true; //TODO: Threading return rasterShape(surface, *sdata, COLOR(r, g, b, a)); @@ -92,7 +91,9 @@ void* SwRenderer::prepare(const ShapeNode& shape, void* data, UpdateFlag flags) shapeReset(*sdata); if (!shapeGenOutline(shape, *sdata)) return sdata; if (!shapeTransformOutline(shape, *sdata)) return sdata; - if (!shapeGenRle(shape, *sdata)) return sdata; + + SwSize clip = {static_cast(surface.stride), static_cast(surface.height)}; + if (!shapeGenRle(shape, *sdata, clip)) return sdata; } return sdata; diff --git a/src/lib/sw_engine/tvgSwRle.cpp b/src/lib/sw_engine/tvgSwRle.cpp index f17c684..3991fff 100644 --- a/src/lib/sw_engine/tvgSwRle.cpp +++ b/src/lib/sw_engine/tvgSwRle.cpp @@ -84,6 +84,8 @@ struct RleWorker Cell** yCells; SwCoord yCnt; + SwSize clip; + bool invalid; }; @@ -161,6 +163,13 @@ static void _genSpan(SwRleData* rle, SwSpan* spans, size_t count) static void _horizLine(RleWorker& rw, SwCoord x, SwCoord y, SwCoord area, SwCoord acount) { + x += rw.cellMin.x; + y += rw.cellMin.y; + + //Clip Y range + if (y < 0) return; + if (y >= rw.clip.h) return; + /* compute the coverage line's coverage, depending on the outline fill rule */ /* the coverage percentage is area/(PIXEL_BITS*PIXEL_BITS*2) */ auto coverage = static_cast(area >> (PIXEL_BITS * 2 + 1 - 8)); //range 0 - 256 @@ -176,9 +185,6 @@ static void _horizLine(RleWorker& rw, SwCoord x, SwCoord y, SwCoord area, SwCoor if (coverage >= 256) coverage = 255; } - x += rw.cellMin.x; - y += rw.cellMin.y; - //span has ushort coordinates. check limit overflow if (x >= SHRT_MAX) { cout << "x(" << x << ") coordinate overflow!" << endl; @@ -197,7 +203,13 @@ static void _horizLine(RleWorker& rw, SwCoord x, SwCoord y, SwCoord area, SwCoor //see whether we can add this span to the current list if ((count > 0) && (rw.ySpan == y) && (span->x + span->len == x) && (span->coverage == coverage)) { - span->len = span->len + acount; + + //Clip x range + SwCoord xOver = 0; + if (x + acount >= rw.clip.w) xOver -= (x + acount - rw.clip.w); + if (x < 0) xOver += x; + + span->len += (acount + xOver) - 1; return; } @@ -211,10 +223,21 @@ static void _horizLine(RleWorker& rw, SwCoord x, SwCoord y, SwCoord area, SwCoor assert(span); } + //Clip x range + SwCoord xOver = 0; + if (x + acount >= rw.clip.w) xOver -= (x + acount - rw.clip.w); + if (x < 0) { + xOver += x; + x = 0; + } + + //Nothing to draw + if (acount + xOver <= 0) return; + //add a span to the current list span->x = x; span->y = y; - span->len = acount; + span->len = (acount + xOver); span->coverage = coverage; ++rw.spansCnt; } @@ -646,16 +669,13 @@ static bool _genRle(RleWorker& rw) /* External Class Implementation */ /************************************************************************/ -SwRleData* rleRender(const SwShape& sdata) +SwRleData* rleRender(const SwShape& sdata, const SwSize& clip) { constexpr auto RENDER_POOL_SIZE = 16384L; constexpr auto BAND_SIZE = 40; auto outline = sdata.outline; assert(outline); - - if (outline->ptsCnt == 0 || outline->cntrsCnt <= 0) return nullptr; - assert(outline->cntrs && outline->pts); assert(outline->ptsCnt == outline->cntrs[outline->cntrsCnt - 1] + 1); @@ -680,11 +700,10 @@ SwRleData* rleRender(const SwShape& sdata) rw.outline = outline; rw.bandSize = rw.bufferSize / (sizeof(Cell) * 8); //bandSize: 64 rw.bandShoot = 0; + rw.clip = clip; rw.rle = reinterpret_cast(calloc(1, sizeof(SwRleData))); assert(rw.rle); - //printf("bufferSize = %d, bbox(%d %d %d %d), exCnt(%f), eyCnt(%f), bandSize(%d)\n", rw.bufferSize, rw.cellMin.x, rw.cellMin.y, rw.cellMax.x, rw.cellMax.y, rw.cellXCnt, rw.cellYCnt, rw.bandSize); - //Generate RLE Band bands[BAND_SIZE]; Band* band; @@ -717,7 +736,6 @@ SwRleData* rleRender(const SwShape& sdata) auto cellEnd = rw.bufferSize; cellEnd -= cellEnd % sizeof(Cell); -//printf("n:%d, cellStart(%d), cellEnd(%d) cellMod(%d)\n", n, cellStart, cellEnd, cellMod); auto cellsMax = reinterpret_cast((char*)rw.buffer + cellEnd); rw.cells = reinterpret_cast((char*)rw.buffer + cellStart); diff --git a/src/lib/sw_engine/tvgSwShape.cpp b/src/lib/sw_engine/tvgSwShape.cpp index e725e30..1f24a63 100644 --- a/src/lib/sw_engine/tvgSwShape.cpp +++ b/src/lib/sw_engine/tvgSwShape.cpp @@ -235,11 +235,19 @@ bool shapeTransformOutline(const ShapeNode& shape, SwShape& sdata) } -bool shapeGenRle(const ShapeNode& shape, SwShape& sdata) +bool shapeGenRle(const ShapeNode& shape, SwShape& sdata, const SwSize& clip) { + if (sdata.outline->ptsCnt == 0 || sdata.outline->cntrsCnt <= 0) goto end; if (!_updateBBox(sdata)) goto end; - sdata.rle = rleRender(sdata); + + //Check boundary + if ((sdata.bbox.min.x > clip.w || sdata.bbox.min.y > clip.h) || + (sdata.bbox.min.x + sdata.bbox.max.x < 0) || + (sdata.bbox.min.y + sdata.bbox.max.y < 0)) goto end; + + sdata.rle = rleRender(sdata, clip); _deleteOutline(sdata); + end: if (sdata.rle) return true; return false; diff --git a/test/makefile b/test/makefile index bb80729..1bab747 100644 --- a/test/makefile +++ b/test/makefile @@ -2,3 +2,4 @@ all: gcc -o testShape testShape.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` gcc -o testMultiShapes testMultiShapes.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` gcc -o testMergeShapes testMergeShapes.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` + gcc -o testBoundary testBoundary.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` diff --git a/test/testBoundary.cpp b/test/testBoundary.cpp new file mode 100644 index 0000000..473fd59 --- /dev/null +++ b/test/testBoundary.cpp @@ -0,0 +1,86 @@ +#include +#include + +using namespace std; + +#define WIDTH 800 +#define HEIGHT 800 + +static uint32_t buffer[WIDTH * HEIGHT]; + +void tvgtest() +{ + //Initialize TizenVG Engine + tvg::Engine::init(); + + //Create a Canvas + auto canvas = tvg::SwCanvas::gen(buffer, WIDTH, HEIGHT); + canvas->reserve(5); //reserve 5 shape nodes (optional) + + //Prepare Shape1 + auto shape1 = tvg::ShapeNode::gen(); + shape1->appendRect(-100, -100, 1000, 1000, 50); + shape1->fill(255, 255, 255, 255); + canvas->push(move(shape1)); + + //Prepare Shape2 + auto shape2 = tvg::ShapeNode::gen(); + shape2->appendRect(-100, -100, 250, 250, 50); + shape2->fill(0, 0, 255, 255); + canvas->push(move(shape2)); + + //Prepare Shape3 + auto shape3 = tvg::ShapeNode::gen(); + shape3->appendRect(500, 500, 550, 550, 0); + shape3->fill(0, 255, 255, 255); + canvas->push(move(shape3)); + + //Prepare Shape4 + auto shape4 = tvg::ShapeNode::gen(); + shape4->appendCircle(800, 100, 200, 200); + shape4->fill(255, 255, 0, 255); + canvas->push(move(shape4)); + + //Prepare Shape5 + auto shape5 = tvg::ShapeNode::gen(); + shape5->appendCircle(200, 650, 250, 200); + shape5->fill(0, 0, 0, 255); + canvas->push(move(shape5)); + + //Draw the Shapes onto the Canvas + canvas->draw(); + canvas->sync(); + + //Terminate TizenVG Engine + tvg::Engine::term(); +} + +void +win_del(void *data, Evas_Object *o, void *ev) +{ + elm_exit(); +} + +int main(int argc, char **argv) +{ + tvgtest(); + + //Show the result using EFL... + elm_init(argc, argv); + + Eo* win = elm_win_util_standard_add(NULL, "TizenVG Test"); + evas_object_smart_callback_add(win, "delete,request", win_del, 0); + + Eo* img = evas_object_image_filled_add(evas_object_evas_get(win)); + evas_object_image_size_set(img, WIDTH, HEIGHT); + evas_object_image_data_set(img, buffer); + evas_object_size_hint_weight_set(img, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_show(img); + + elm_win_resize_object_add(win, img); + evas_object_geometry_set(win, 0, 0, WIDTH, HEIGHT); + evas_object_show(win); + + elm_run(); + elm_shutdown(); +} -- 2.7.4 From e0ecd680a02ccf9e3ef46c4c82b2336d9509426f Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Thu, 30 Apr 2020 15:41:16 +0900 Subject: [PATCH 10/16] sw_engine: fix crash at merge shape test increased spare cell memory for rasterizing. Change-Id: I391dfbfae0ef028d213c55fe1ceca023e272e676 --- src/lib/sw_engine/tvgSwRle.cpp | 3 ++- test/testMergeShapes.cpp | 7 +++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/lib/sw_engine/tvgSwRle.cpp b/src/lib/sw_engine/tvgSwRle.cpp index 3991fff..02870c3 100644 --- a/src/lib/sw_engine/tvgSwRle.cpp +++ b/src/lib/sw_engine/tvgSwRle.cpp @@ -671,7 +671,8 @@ static bool _genRle(RleWorker& rw) SwRleData* rleRender(const SwShape& sdata, const SwSize& clip) { - constexpr auto RENDER_POOL_SIZE = 16384L; + //Please adjust when you out of cell memory (default: 16384L) + constexpr auto RENDER_POOL_SIZE = 166641L; constexpr auto BAND_SIZE = 40; auto outline = sdata.outline; diff --git a/test/testMergeShapes.cpp b/test/testMergeShapes.cpp index 549ea9e..b37440b 100644 --- a/test/testMergeShapes.cpp +++ b/test/testMergeShapes.cpp @@ -20,10 +20,9 @@ void tvgtest() auto shape1 = tvg::ShapeNode::gen(); shape1->appendRect(0, 0, 200, 200, 0); //x, y, w, h, cornerRadius shape1->appendRect(100, 100, 300, 300, 100); //x, y, w, h, cornerRadius - shape1->appendCircle(500, 500, 100, 100); //cx, cy, radiusW, radiusH - //FIXME: eeek! crash! - // shape1->appendCircle(400, 600, 170, 100); //cx, cy, radiusW, radiusH - shape1->fill(255, 0, 0, 255); //r, g, b, a + shape1->appendCircle(400, 400, 100, 100); //cx, cy, radiusW, radiusH + shape1->appendCircle(400, 500, 170, 100); //cx, cy, radiusW, radiusH + shape1->fill(255, 255, 0, 255); //r, g, b, a /* Push the shape into the Canvas drawing list When this shape is into the canvas list, the shape could update & prepare -- 2.7.4 From 9b8ab42e545cac7514caa7893312d2beb03f1e02 Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Thu, 30 Apr 2020 18:31:51 +0900 Subject: [PATCH 11/16] common shape: support path appending it's usage may require a little handy but The path data is very low manipulated, its usage may require a little handy, but appedingPath() is designed for performance. Also added testPath. Change-Id: Ifd929d48506926e3f529198c0b40ee8f922835d4 --- .gitignore | 1 + inc/tizenvg.h | 1 + src/lib/tvgCommon.h | 1 + src/lib/tvgShapeNode.cpp | 24 ++++++++-- src/lib/tvgShapePath.h | 82 ++++++++++++--------------------- test/makefile | 1 + test/testPath.cpp | 116 +++++++++++++++++++++++++++++++++++++++++------ 7 files changed, 156 insertions(+), 70 deletions(-) diff --git a/.gitignore b/.gitignore index bbd3f8e..ddc6523 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ testShape testMultiShapes testMergeShapes testBoundary +testPath diff --git a/inc/tizenvg.h b/inc/tizenvg.h index c53a5dc..89db3f5 100644 --- a/inc/tizenvg.h +++ b/inc/tizenvg.h @@ -121,6 +121,7 @@ public: int appendRect(float x, float y, float w, float h, float cornerRadius) noexcept; int appendCircle(float cx, float cy, float radiusW, float radiusH) noexcept; + int appendPath(const PathCommand* cmds, size_t cmdCnt, const Point* pts, size_t ptsCnt) noexcept; int fill(size_t r, size_t g, size_t b, size_t a) noexcept; diff --git a/src/lib/tvgCommon.h b/src/lib/tvgCommon.h index 2653d3a..c3f0c56 100644 --- a/src/lib/tvgCommon.h +++ b/src/lib/tvgCommon.h @@ -22,6 +22,7 @@ #include #include #include +#include #include "tizenvg.h" #include "tvgRenderCommon.h" diff --git a/src/lib/tvgShapeNode.cpp b/src/lib/tvgShapeNode.cpp index a6e4511..0c01c0f 100644 --- a/src/lib/tvgShapeNode.cpp +++ b/src/lib/tvgShapeNode.cpp @@ -109,7 +109,9 @@ int ShapeNode::clear() noexcept auto impl = pImpl.get(); assert(impl); - return impl->path->clear(); + impl->path->clear(); + + return 0; } @@ -135,6 +137,20 @@ int ShapeNode::pathCoords(const Point** pts) const noexcept } +int ShapeNode::appendPath(const PathCommand *cmds, size_t cmdCnt, const Point* pts, size_t ptsCnt) noexcept +{ + if (cmdCnt < 0 || ptsCnt < 0) return -1; + assert(cmds && pts); + + auto impl = pImpl.get(); + assert(impl); + + impl->path->append(cmds, cmdCnt, pts, ptsCnt); + + return 0; +} + + int ShapeNode::appendCircle(float cx, float cy, float radiusW, float radiusH) noexcept { auto impl = pImpl.get(); @@ -143,7 +159,7 @@ int ShapeNode::appendCircle(float cx, float cy, float radiusW, float radiusH) no auto halfKappaW = radiusW * PATH_KAPPA; auto halfKappaH = radiusH * PATH_KAPPA; - impl->path->reserve(6, 13); + impl->path->grow(6, 13); impl->path->moveTo(cx, cy - radiusH); impl->path->cubicTo(cx + halfKappaW, cy - radiusH, cx + radiusW, cy - halfKappaH, cx + radiusW, cy); impl->path->cubicTo(cx + radiusW, cy + halfKappaH, cx + halfKappaW, cy + radiusH, cx, cy + radiusH); @@ -166,7 +182,7 @@ int ShapeNode::appendRect(float x, float y, float w, float h, float cornerRadius //rectangle if (cornerRadius == 0) { - impl->path->reserve(5, 4); + impl->path->grow(5, 4); impl->path->moveTo(x, y); impl->path->lineTo(x + w, y); impl->path->lineTo(x + w, y + h); @@ -177,7 +193,7 @@ int ShapeNode::appendRect(float x, float y, float w, float h, float cornerRadius return appendCircle(x + (w * 0.5f), y + (h * 0.5f), cornerRadius, cornerRadius); } else { auto halfKappa = cornerRadius * 0.5; - impl->path->reserve(10, 17); + impl->path->grow(10, 17); impl->path->moveTo(x + cornerRadius, y); impl->path->lineTo(x + w - cornerRadius, y); impl->path->cubicTo(x + w - cornerRadius + halfKappa, y, x + w, y + cornerRadius - halfKappa, x + w, y + cornerRadius); diff --git a/src/lib/tvgShapePath.h b/src/lib/tvgShapePath.h index 6fda0f0..f4fee18 100644 --- a/src/lib/tvgShapePath.h +++ b/src/lib/tvgShapePath.h @@ -40,65 +40,68 @@ struct ShapePath if (pts) delete(pts); } - int reserveCmd(size_t cmdCnt) + void reserveCmd(size_t cmdCnt) { - if (cmdCnt > reservedCmdCnt) { - reservedCmdCnt = cmdCnt; - cmds = static_cast(realloc(cmds, sizeof(PathCommand) * reservedCmdCnt)); - assert(cmds); - } - return 0; + if (cmdCnt <= reservedCmdCnt) return; + reservedCmdCnt = cmdCnt; + cmds = static_cast(realloc(cmds, sizeof(PathCommand) * reservedCmdCnt)); + assert(cmds); } - int reservePts(size_t ptsCnt) + void reservePts(size_t ptsCnt) { - if (ptsCnt > reservedPtsCnt) { - reservedPtsCnt = ptsCnt; - pts = static_cast(realloc(pts, sizeof(Point) * reservedPtsCnt)); - assert(pts); - } - return 0; + if (ptsCnt <= reservedPtsCnt) return; + reservedPtsCnt = ptsCnt; + pts = static_cast(realloc(pts, sizeof(Point) * reservedPtsCnt)); + assert(pts); } - int reserve(size_t cmdCnt, size_t ptsCnt) + void reserve(size_t cmdCnt, size_t ptsCnt) { reserveCmd(cmdCnt); reservePts(ptsCnt); + } - return 0; + void grow(size_t cmdCnt, size_t ptsCnt) + { + reserveCmd(this->cmdCnt + cmdCnt); + reservePts(this->ptsCnt + ptsCnt); } - int clear() + void clear() { cmdCnt = 0; ptsCnt = 0; + } - return 0; + void append(const PathCommand* cmds, size_t cmdCnt, const Point* pts, size_t ptsCnt) + { + grow(cmdCnt, ptsCnt); + memcpy(this->cmds + this->cmdCnt, cmds, sizeof(PathCommand) * cmdCnt); + memcpy(this->pts + this->ptsCnt, pts, sizeof(Point) * ptsCnt); + this->cmdCnt += cmdCnt; + this->ptsCnt += ptsCnt; } - int moveTo(float x, float y) + void moveTo(float x, float y) { if (cmdCnt + 1 > reservedCmdCnt) reserveCmd((cmdCnt + 1) * 2); if (ptsCnt + 2 > reservedPtsCnt) reservePts((ptsCnt + 2) * 2); cmds[cmdCnt++] = PathCommand::MoveTo; pts[ptsCnt++] = {x, y}; - - return 0; } - int lineTo(float x, float y) + void lineTo(float x, float y) { if (cmdCnt + 1 > reservedCmdCnt) reserveCmd((cmdCnt + 1) * 2); if (ptsCnt + 2 > reservedPtsCnt) reservePts((ptsCnt + 2) * 2); cmds[cmdCnt++] = PathCommand::LineTo; pts[ptsCnt++] = {x, y}; - - return 0; } - int cubicTo(float cx1, float cy1, float cx2, float cy2, float x, float y) + void cubicTo(float cx1, float cy1, float cx2, float cy2, float x, float y) { if (cmdCnt + 1 > reservedCmdCnt) reserveCmd((cmdCnt + 1) * 2); if (ptsCnt + 3 > reservedPtsCnt) reservePts((ptsCnt + 3) * 2); @@ -107,39 +110,12 @@ struct ShapePath pts[ptsCnt++] = {cx1, cy1}; pts[ptsCnt++] = {cx2, cy2}; pts[ptsCnt++] = {x, y}; - - return 0; } - - int close() + void close() { if (cmdCnt + 1 > reservedCmdCnt) reserveCmd((cmdCnt + 1) * 2); cmds[cmdCnt++] = PathCommand::Close; - - return 0; - } - - int bounds(float& x, float& y, float& w, float& h) - { - if (ptsCnt == 0) return -1; - - Point min = { pts[0].x, pts[0].y }; - Point max = { pts[0].x, pts[0].y }; - - for(size_t i = 1; i <= ptsCnt; ++i) { - if (pts[i].x < min.x) min.x = pts[i].x; - if (pts[i].y < min.y) min.y = pts[i].y; - if (pts[i].x > max.x) max.x = pts[i].x; - if (pts[i].y > max.y) max.y = pts[i].y; - } - - x = min.x; - y = min.y; - w = max.x - min.x; - h = max.y - min.y; - - return 0; } }; diff --git a/test/makefile b/test/makefile index 1bab747..e213907 100644 --- a/test/makefile +++ b/test/makefile @@ -3,3 +3,4 @@ all: gcc -o testMultiShapes testMultiShapes.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` gcc -o testMergeShapes testMergeShapes.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` gcc -o testBoundary testBoundary.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` + gcc -o testPath testPath.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` diff --git a/test/testPath.cpp b/test/testPath.cpp index e260682..a75c7f7 100644 --- a/test/testPath.cpp +++ b/test/testPath.cpp @@ -1,4 +1,5 @@ #include +#include using namespace std; @@ -7,7 +8,7 @@ using namespace std; static uint32_t buffer[WIDTH * HEIGHT]; -int main(int argc, char **argv) +void tvgtest() { //Initialize TizenVG Engine tvg::Engine::init(); @@ -15,25 +16,114 @@ int main(int argc, char **argv) //Create a Canvas auto canvas = tvg::SwCanvas::gen(buffer, WIDTH, HEIGHT); - //Prepare Path - auto path = tvg::Path::gen(); - path.reserve(cmdCnt, ptCnt); //command count, points count - path.moveTo(...); - path.lineTo(...); - path.cubicTo(...); - path.close(); + /* Star */ + + //Prepare Path Commands + tvg::PathCommand cmds[11]; + cmds[0] = tvg::PathCommand::MoveTo; + cmds[1] = tvg::PathCommand::LineTo; + cmds[2] = tvg::PathCommand::LineTo; + cmds[3] = tvg::PathCommand::LineTo; + cmds[4] = tvg::PathCommand::LineTo; + cmds[5] = tvg::PathCommand::LineTo; + cmds[6] = tvg::PathCommand::LineTo; + cmds[7] = tvg::PathCommand::LineTo; + cmds[8] = tvg::PathCommand::LineTo; + cmds[9] = tvg::PathCommand::LineTo; + cmds[10] = tvg::PathCommand::Close; + + //Prepare Path Points + tvg::Point pts[10]; + pts[0] = {199, 34}; //MoveTo + pts[1] = {253, 143}; //LineTo + pts[2] = {374, 160}; //LineTo + pts[3] = {287, 244}; //LineTo + pts[4] = {307, 365}; //LineTo + pts[5] = {199, 309}; //LineTo + pts[6] = {97, 365}; //LineTo + pts[7] = {112, 245}; //LineTo + pts[8] = {26, 161}; //LineTo + pts[9] = {146, 143}; //LineTo - //Prepare a Shape auto shape1 = tvg::ShapeNode::gen(); - shape1->path(move(path)); //migrate owner otherwise, - shape1->path(path.get()); //copy raw data directly for performance + shape1->appendPath(cmds, 11, pts, 10); //copy path data shape1->fill(0, 255, 0, 255); - - //Draw the Shape onto the Canvas canvas->push(move(shape1)); + + + /* Circle */ + auto cx = 550.0f; + auto cy = 550.0f; + auto radius = 125.0f; + auto halfRadius = radius * 0.552284f; + + //Prepare Path Commands + tvg::PathCommand cmds2[6]; + cmds2[0] = tvg::PathCommand::MoveTo; + cmds2[1] = tvg::PathCommand::CubicTo; + cmds2[2] = tvg::PathCommand::CubicTo; + cmds2[3] = tvg::PathCommand::CubicTo; + cmds2[4] = tvg::PathCommand::CubicTo; + cmds2[5] = tvg::PathCommand::Close; + + //Prepare Path Points + tvg::Point pts2[13]; + pts2[0] = {cx, cy - radius}; //MoveTo + //CubicTo 1 + pts2[1] = {cx + halfRadius, cy - radius}; //Ctrl1 + pts2[2] = {cx + radius, cy - halfRadius}; //Ctrl2 + pts2[3] = {cx + radius, cy}; //To + //CubicTo 2 + pts2[4] = {cx + radius, cy + halfRadius}; //Ctrl1 + pts2[5] = {cx + halfRadius, cy + radius}; //Ctrl2 + pts2[6] = {cx, cy+ radius}; //To + //CubicTo 3 + pts2[7] = {cx - halfRadius, cy + radius}; //Ctrl1 + pts2[8] = {cx - radius, cy + halfRadius}; //Ctrl2 + pts2[9] = {cx - radius, cy}; //To + //CubicTo 4 + pts2[10] = {cx - radius, cy - halfRadius}; //Ctrl1 + pts2[11] = {cx - halfRadius, cy - radius}; //Ctrl2 + pts2[12] = {cx, cy - radius}; //To + + auto shape2 = tvg::ShapeNode::gen(); + shape2->appendPath(cmds2, 6, pts2, 13); //copy path data + shape2->fill(255, 255, 0, 255); + canvas->push(move(shape2)); + canvas->draw(); canvas->sync(); //Terminate TizenVG Engine tvg::Engine::term(); } + +void +win_del(void *data, Evas_Object *o, void *ev) +{ + elm_exit(); +} + +int main(int argc, char **argv) +{ + tvgtest(); + + //Show the result using EFL... + elm_init(argc, argv); + + Eo* win = elm_win_util_standard_add(NULL, "TizenVG Test"); + evas_object_smart_callback_add(win, "delete,request", win_del, 0); + + Eo* img = evas_object_image_filled_add(evas_object_evas_get(win)); + evas_object_image_size_set(img, WIDTH, HEIGHT); + evas_object_image_data_set(img, buffer); + evas_object_size_hint_weight_set(img, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_show(img); + + elm_win_resize_object_add(win, img); + evas_object_geometry_set(win, 0, 0, WIDTH, HEIGHT); + evas_object_show(win); + + elm_run(); + elm_shutdown(); +} -- 2.7.4 From 308456ebade78905cd2ad6e9e3f8a5770475d523 Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Thu, 30 Apr 2020 18:38:34 +0900 Subject: [PATCH 12/16] common shape: remove unnecessary log Change-Id: Ie85153afdc01270ed282d9ff87607ee1a882be31 --- src/lib/tvgShapeNode.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lib/tvgShapeNode.cpp b/src/lib/tvgShapeNode.cpp index 0c01c0f..12ce6de 100644 --- a/src/lib/tvgShapeNode.cpp +++ b/src/lib/tvgShapeNode.cpp @@ -75,7 +75,6 @@ ShapeNode :: ShapeNode() : pImpl(make_unique()) ShapeNode :: ~ShapeNode() { - cout << "ShapeNode(" << this << ") destroyed!" << endl; } -- 2.7.4 From 700a44c623f9ff663d7b6488dbe831fe672ca7be Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Thu, 30 Apr 2020 19:11:04 +0900 Subject: [PATCH 13/16] common shape: added path commands interfaces for user convenient. +lineTo() +moveTo() +cubicTo() +close() These interfaces might not perfect optmizied, but you can build path commands easier than manual data set. Change-Id: Icb934ca256d3107ca0c938d28015d767fb93355e --- .gitignore | 1 + inc/tizenvg.h | 5 +++ src/lib/sw_engine/tvgSwRle.cpp | 2 +- src/lib/tvgShapeNode.cpp | 45 +++++++++++++++++++++ src/lib/tvgShapePath.h | 9 +---- test/makefile | 1 + test/testPath2.cpp | 89 ++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 144 insertions(+), 8 deletions(-) create mode 100644 test/testPath2.cpp diff --git a/.gitignore b/.gitignore index ddc6523..8e92d13 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ testMultiShapes testMergeShapes testBoundary testPath +testPath2 diff --git a/inc/tizenvg.h b/inc/tizenvg.h index 89db3f5..83e5f1d 100644 --- a/inc/tizenvg.h +++ b/inc/tizenvg.h @@ -119,6 +119,11 @@ public: int update(RenderMethod* engine) noexcept override; int clear() noexcept; + int moveTo(float x, float y) noexcept; + int lineTo(float x, float y) noexcept; + int cubicTo(float cx1, float cy1, float cx2, float cy2, float x, float y) noexcept; + int close() noexcept; + int appendRect(float x, float y, float w, float h, float cornerRadius) noexcept; int appendCircle(float cx, float cy, float radiusW, float radiusH) noexcept; int appendPath(const PathCommand* cmds, size_t cmdCnt, const Point* pts, size_t ptsCnt) noexcept; diff --git a/src/lib/sw_engine/tvgSwRle.cpp b/src/lib/sw_engine/tvgSwRle.cpp index 02870c3..5166de8 100644 --- a/src/lib/sw_engine/tvgSwRle.cpp +++ b/src/lib/sw_engine/tvgSwRle.cpp @@ -637,7 +637,7 @@ static bool _decomposeOutline(RleWorker& rw) } } - //Close the contour with a line segment? + //FIXME: Close the contour with a line segment? //_lineTo(rw, UPSCALE(outline->pts[first])); close: first = last + 1; diff --git a/src/lib/tvgShapeNode.cpp b/src/lib/tvgShapeNode.cpp index 12ce6de..b8397df 100644 --- a/src/lib/tvgShapeNode.cpp +++ b/src/lib/tvgShapeNode.cpp @@ -144,12 +144,57 @@ int ShapeNode::appendPath(const PathCommand *cmds, size_t cmdCnt, const Point* p auto impl = pImpl.get(); assert(impl); + impl->path->grow(cmdCnt, ptsCnt); impl->path->append(cmds, cmdCnt, pts, ptsCnt); return 0; } +int ShapeNode::moveTo(float x, float y) noexcept +{ + auto impl = pImpl.get(); + assert(impl); + + impl->path->moveTo(x, y); + + return 0; +} + + +int ShapeNode::lineTo(float x, float y) noexcept +{ + auto impl = pImpl.get(); + assert(impl); + + impl->path->lineTo(x, y); + + return 0; +} + + +int ShapeNode::cubicTo(float cx1, float cy1, float cx2, float cy2, float x, float y) noexcept +{ + auto impl = pImpl.get(); + assert(impl); + + impl->path->cubicTo(cx1, cy1, cx2, cy2, x, y); + + return 0; +} + + +int ShapeNode::close() noexcept +{ + auto impl = pImpl.get(); + assert(impl); + + impl->path->close(); + + return 0; +} + + int ShapeNode::appendCircle(float cx, float cy, float radiusW, float radiusH) noexcept { auto impl = pImpl.get(); diff --git a/src/lib/tvgShapePath.h b/src/lib/tvgShapePath.h index f4fee18..2849099 100644 --- a/src/lib/tvgShapePath.h +++ b/src/lib/tvgShapePath.h @@ -56,12 +56,6 @@ struct ShapePath assert(pts); } - void reserve(size_t cmdCnt, size_t ptsCnt) - { - reserveCmd(cmdCnt); - reservePts(ptsCnt); - } - void grow(size_t cmdCnt, size_t ptsCnt) { reserveCmd(this->cmdCnt + cmdCnt); @@ -76,7 +70,6 @@ struct ShapePath void append(const PathCommand* cmds, size_t cmdCnt, const Point* pts, size_t ptsCnt) { - grow(cmdCnt, ptsCnt); memcpy(this->cmds + this->cmdCnt, cmds, sizeof(PathCommand) * cmdCnt); memcpy(this->pts + this->ptsCnt, pts, sizeof(Point) * ptsCnt); this->cmdCnt += cmdCnt; @@ -114,6 +107,8 @@ struct ShapePath void close() { + if (cmdCnt > 0 && cmds[cmdCnt - 1] == PathCommand::Close) return; + if (cmdCnt + 1 > reservedCmdCnt) reserveCmd((cmdCnt + 1) * 2); cmds[cmdCnt++] = PathCommand::Close; } diff --git a/test/makefile b/test/makefile index e213907..1471ecd 100644 --- a/test/makefile +++ b/test/makefile @@ -4,3 +4,4 @@ all: gcc -o testMergeShapes testMergeShapes.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` gcc -o testBoundary testBoundary.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` gcc -o testPath testPath.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` + gcc -o testPath2 testPath2.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` diff --git a/test/testPath2.cpp b/test/testPath2.cpp new file mode 100644 index 0000000..3c6738a --- /dev/null +++ b/test/testPath2.cpp @@ -0,0 +1,89 @@ +#include +#include + +using namespace std; + +#define WIDTH 800 +#define HEIGHT 800 + +static uint32_t buffer[WIDTH * HEIGHT]; + +void tvgtest() +{ + //Initialize TizenVG Engine + tvg::Engine::init(); + + //Create a Canvas + auto canvas = tvg::SwCanvas::gen(buffer, WIDTH, HEIGHT); + + //Star + auto shape1 = tvg::ShapeNode::gen(); + + //Appends Paths + shape1->moveTo(199, 34); + shape1->lineTo(253, 143); + shape1->lineTo(374, 160); + shape1->lineTo(287, 244); + shape1->lineTo(307, 365); + shape1->lineTo(199, 309); + shape1->lineTo(97, 365); + shape1->lineTo(112, 245); + shape1->lineTo(26, 161); + shape1->lineTo(146, 143); + shape1->close(); + shape1->fill(0, 0, 255, 255); + canvas->push(move(shape1)); + + //Circle + auto shape2 = tvg::ShapeNode::gen(); + + auto cx = 550.0f; + auto cy = 550.0f; + auto radius = 125.0f; + auto halfRadius = radius * 0.552284f; + + //Append Paths + shape2->moveTo(cx, cy - radius); + shape2->cubicTo(cx + halfRadius, cy - radius, cx + radius, cy - halfRadius, cx + radius, cy); + shape2->cubicTo(cx + radius, cy + halfRadius, cx + halfRadius, cy + radius, cx, cy+ radius); + shape2->cubicTo(cx - halfRadius, cy + radius, cx - radius, cy + halfRadius, cx - radius, cy); + shape2->cubicTo(cx - radius, cy - halfRadius, cx - halfRadius, cy - radius, cx, cy - radius); + shape2->fill(255, 0, 0, 255); + canvas->push(move(shape2)); + + canvas->draw(); + canvas->sync(); + + //Terminate TizenVG Engine + tvg::Engine::term(); +} + +void +win_del(void *data, Evas_Object *o, void *ev) +{ + elm_exit(); +} + +int main(int argc, char **argv) +{ + tvgtest(); + + //Show the result using EFL... + elm_init(argc, argv); + + Eo* win = elm_win_util_standard_add(NULL, "TizenVG Test"); + evas_object_smart_callback_add(win, "delete,request", win_del, 0); + + Eo* img = evas_object_image_filled_add(evas_object_evas_get(win)); + evas_object_image_size_set(img, WIDTH, HEIGHT); + evas_object_image_data_set(img, buffer); + evas_object_size_hint_weight_set(img, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_show(img); + + elm_win_resize_object_add(win, img); + evas_object_geometry_set(win, 0, 0, WIDTH, HEIGHT); + evas_object_show(win); + + elm_run(); + elm_shutdown(); +} -- 2.7.4 From 14efeb4f0b84858a3a8c1f21c42868ed6bd7cee3 Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Thu, 30 Apr 2020 23:40:35 +0900 Subject: [PATCH 14/16] test: updated comments Change-Id: I92fccb5a9de088f837a768a2f1d60ac94eac9410 --- test/testMergeShapes.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/testMergeShapes.cpp b/test/testMergeShapes.cpp index b37440b..268e958 100644 --- a/test/testMergeShapes.cpp +++ b/test/testMergeShapes.cpp @@ -16,7 +16,7 @@ void tvgtest() //Create a Canvas auto canvas = tvg::SwCanvas::gen(buffer, WIDTH, HEIGHT); - //Prepare a Shape (Rectangle + Rectangle + Circle) + //Prepare a Shape (Rectangle + Rectangle + Circle + Circle) auto shape1 = tvg::ShapeNode::gen(); shape1->appendRect(0, 0, 200, 200, 0); //x, y, w, h, cornerRadius shape1->appendRect(100, 100, 300, 300, 100); //x, y, w, h, cornerRadius -- 2.7.4 From 2f174560ebe96ed6bbf779361d9301412cb920b7 Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Fri, 1 May 2020 12:46:54 +0900 Subject: [PATCH 15/16] test: rename testPath to testPathCopy Change-Id: I8cf2a26437a804d2cd061d61d3a99b3c6f3aa0e6 --- .gitignore | 2 +- test/makefile | 2 +- test/testPath.cpp | 88 ++++++++++------------------------ test/testPath2.cpp | 89 ---------------------------------- test/testPathCopy.cpp | 129 ++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 155 insertions(+), 155 deletions(-) delete mode 100644 test/testPath2.cpp create mode 100644 test/testPathCopy.cpp diff --git a/.gitignore b/.gitignore index 8e92d13..2c0e514 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,4 @@ testMultiShapes testMergeShapes testBoundary testPath -testPath2 +testPathCopy diff --git a/test/makefile b/test/makefile index 1471ecd..e28d76b 100644 --- a/test/makefile +++ b/test/makefile @@ -4,4 +4,4 @@ all: gcc -o testMergeShapes testMergeShapes.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` gcc -o testBoundary testBoundary.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` gcc -o testPath testPath.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` - gcc -o testPath2 testPath2.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` + gcc -o testPathCopy testPathCopy.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` diff --git a/test/testPath.cpp b/test/testPath.cpp index a75c7f7..3c6738a 100644 --- a/test/testPath.cpp +++ b/test/testPath.cpp @@ -16,79 +16,39 @@ void tvgtest() //Create a Canvas auto canvas = tvg::SwCanvas::gen(buffer, WIDTH, HEIGHT); - /* Star */ - - //Prepare Path Commands - tvg::PathCommand cmds[11]; - cmds[0] = tvg::PathCommand::MoveTo; - cmds[1] = tvg::PathCommand::LineTo; - cmds[2] = tvg::PathCommand::LineTo; - cmds[3] = tvg::PathCommand::LineTo; - cmds[4] = tvg::PathCommand::LineTo; - cmds[5] = tvg::PathCommand::LineTo; - cmds[6] = tvg::PathCommand::LineTo; - cmds[7] = tvg::PathCommand::LineTo; - cmds[8] = tvg::PathCommand::LineTo; - cmds[9] = tvg::PathCommand::LineTo; - cmds[10] = tvg::PathCommand::Close; - - //Prepare Path Points - tvg::Point pts[10]; - pts[0] = {199, 34}; //MoveTo - pts[1] = {253, 143}; //LineTo - pts[2] = {374, 160}; //LineTo - pts[3] = {287, 244}; //LineTo - pts[4] = {307, 365}; //LineTo - pts[5] = {199, 309}; //LineTo - pts[6] = {97, 365}; //LineTo - pts[7] = {112, 245}; //LineTo - pts[8] = {26, 161}; //LineTo - pts[9] = {146, 143}; //LineTo - + //Star auto shape1 = tvg::ShapeNode::gen(); - shape1->appendPath(cmds, 11, pts, 10); //copy path data - shape1->fill(0, 255, 0, 255); + + //Appends Paths + shape1->moveTo(199, 34); + shape1->lineTo(253, 143); + shape1->lineTo(374, 160); + shape1->lineTo(287, 244); + shape1->lineTo(307, 365); + shape1->lineTo(199, 309); + shape1->lineTo(97, 365); + shape1->lineTo(112, 245); + shape1->lineTo(26, 161); + shape1->lineTo(146, 143); + shape1->close(); + shape1->fill(0, 0, 255, 255); canvas->push(move(shape1)); + //Circle + auto shape2 = tvg::ShapeNode::gen(); - /* Circle */ auto cx = 550.0f; auto cy = 550.0f; auto radius = 125.0f; auto halfRadius = radius * 0.552284f; - //Prepare Path Commands - tvg::PathCommand cmds2[6]; - cmds2[0] = tvg::PathCommand::MoveTo; - cmds2[1] = tvg::PathCommand::CubicTo; - cmds2[2] = tvg::PathCommand::CubicTo; - cmds2[3] = tvg::PathCommand::CubicTo; - cmds2[4] = tvg::PathCommand::CubicTo; - cmds2[5] = tvg::PathCommand::Close; - - //Prepare Path Points - tvg::Point pts2[13]; - pts2[0] = {cx, cy - radius}; //MoveTo - //CubicTo 1 - pts2[1] = {cx + halfRadius, cy - radius}; //Ctrl1 - pts2[2] = {cx + radius, cy - halfRadius}; //Ctrl2 - pts2[3] = {cx + radius, cy}; //To - //CubicTo 2 - pts2[4] = {cx + radius, cy + halfRadius}; //Ctrl1 - pts2[5] = {cx + halfRadius, cy + radius}; //Ctrl2 - pts2[6] = {cx, cy+ radius}; //To - //CubicTo 3 - pts2[7] = {cx - halfRadius, cy + radius}; //Ctrl1 - pts2[8] = {cx - radius, cy + halfRadius}; //Ctrl2 - pts2[9] = {cx - radius, cy}; //To - //CubicTo 4 - pts2[10] = {cx - radius, cy - halfRadius}; //Ctrl1 - pts2[11] = {cx - halfRadius, cy - radius}; //Ctrl2 - pts2[12] = {cx, cy - radius}; //To - - auto shape2 = tvg::ShapeNode::gen(); - shape2->appendPath(cmds2, 6, pts2, 13); //copy path data - shape2->fill(255, 255, 0, 255); + //Append Paths + shape2->moveTo(cx, cy - radius); + shape2->cubicTo(cx + halfRadius, cy - radius, cx + radius, cy - halfRadius, cx + radius, cy); + shape2->cubicTo(cx + radius, cy + halfRadius, cx + halfRadius, cy + radius, cx, cy+ radius); + shape2->cubicTo(cx - halfRadius, cy + radius, cx - radius, cy + halfRadius, cx - radius, cy); + shape2->cubicTo(cx - radius, cy - halfRadius, cx - halfRadius, cy - radius, cx, cy - radius); + shape2->fill(255, 0, 0, 255); canvas->push(move(shape2)); canvas->draw(); diff --git a/test/testPath2.cpp b/test/testPath2.cpp deleted file mode 100644 index 3c6738a..0000000 --- a/test/testPath2.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include -#include - -using namespace std; - -#define WIDTH 800 -#define HEIGHT 800 - -static uint32_t buffer[WIDTH * HEIGHT]; - -void tvgtest() -{ - //Initialize TizenVG Engine - tvg::Engine::init(); - - //Create a Canvas - auto canvas = tvg::SwCanvas::gen(buffer, WIDTH, HEIGHT); - - //Star - auto shape1 = tvg::ShapeNode::gen(); - - //Appends Paths - shape1->moveTo(199, 34); - shape1->lineTo(253, 143); - shape1->lineTo(374, 160); - shape1->lineTo(287, 244); - shape1->lineTo(307, 365); - shape1->lineTo(199, 309); - shape1->lineTo(97, 365); - shape1->lineTo(112, 245); - shape1->lineTo(26, 161); - shape1->lineTo(146, 143); - shape1->close(); - shape1->fill(0, 0, 255, 255); - canvas->push(move(shape1)); - - //Circle - auto shape2 = tvg::ShapeNode::gen(); - - auto cx = 550.0f; - auto cy = 550.0f; - auto radius = 125.0f; - auto halfRadius = radius * 0.552284f; - - //Append Paths - shape2->moveTo(cx, cy - radius); - shape2->cubicTo(cx + halfRadius, cy - radius, cx + radius, cy - halfRadius, cx + radius, cy); - shape2->cubicTo(cx + radius, cy + halfRadius, cx + halfRadius, cy + radius, cx, cy+ radius); - shape2->cubicTo(cx - halfRadius, cy + radius, cx - radius, cy + halfRadius, cx - radius, cy); - shape2->cubicTo(cx - radius, cy - halfRadius, cx - halfRadius, cy - radius, cx, cy - radius); - shape2->fill(255, 0, 0, 255); - canvas->push(move(shape2)); - - canvas->draw(); - canvas->sync(); - - //Terminate TizenVG Engine - tvg::Engine::term(); -} - -void -win_del(void *data, Evas_Object *o, void *ev) -{ - elm_exit(); -} - -int main(int argc, char **argv) -{ - tvgtest(); - - //Show the result using EFL... - elm_init(argc, argv); - - Eo* win = elm_win_util_standard_add(NULL, "TizenVG Test"); - evas_object_smart_callback_add(win, "delete,request", win_del, 0); - - Eo* img = evas_object_image_filled_add(evas_object_evas_get(win)); - evas_object_image_size_set(img, WIDTH, HEIGHT); - evas_object_image_data_set(img, buffer); - evas_object_size_hint_weight_set(img, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); - evas_object_show(img); - - elm_win_resize_object_add(win, img); - evas_object_geometry_set(win, 0, 0, WIDTH, HEIGHT); - evas_object_show(win); - - elm_run(); - elm_shutdown(); -} diff --git a/test/testPathCopy.cpp b/test/testPathCopy.cpp new file mode 100644 index 0000000..a75c7f7 --- /dev/null +++ b/test/testPathCopy.cpp @@ -0,0 +1,129 @@ +#include +#include + +using namespace std; + +#define WIDTH 800 +#define HEIGHT 800 + +static uint32_t buffer[WIDTH * HEIGHT]; + +void tvgtest() +{ + //Initialize TizenVG Engine + tvg::Engine::init(); + + //Create a Canvas + auto canvas = tvg::SwCanvas::gen(buffer, WIDTH, HEIGHT); + + /* Star */ + + //Prepare Path Commands + tvg::PathCommand cmds[11]; + cmds[0] = tvg::PathCommand::MoveTo; + cmds[1] = tvg::PathCommand::LineTo; + cmds[2] = tvg::PathCommand::LineTo; + cmds[3] = tvg::PathCommand::LineTo; + cmds[4] = tvg::PathCommand::LineTo; + cmds[5] = tvg::PathCommand::LineTo; + cmds[6] = tvg::PathCommand::LineTo; + cmds[7] = tvg::PathCommand::LineTo; + cmds[8] = tvg::PathCommand::LineTo; + cmds[9] = tvg::PathCommand::LineTo; + cmds[10] = tvg::PathCommand::Close; + + //Prepare Path Points + tvg::Point pts[10]; + pts[0] = {199, 34}; //MoveTo + pts[1] = {253, 143}; //LineTo + pts[2] = {374, 160}; //LineTo + pts[3] = {287, 244}; //LineTo + pts[4] = {307, 365}; //LineTo + pts[5] = {199, 309}; //LineTo + pts[6] = {97, 365}; //LineTo + pts[7] = {112, 245}; //LineTo + pts[8] = {26, 161}; //LineTo + pts[9] = {146, 143}; //LineTo + + auto shape1 = tvg::ShapeNode::gen(); + shape1->appendPath(cmds, 11, pts, 10); //copy path data + shape1->fill(0, 255, 0, 255); + canvas->push(move(shape1)); + + + /* Circle */ + auto cx = 550.0f; + auto cy = 550.0f; + auto radius = 125.0f; + auto halfRadius = radius * 0.552284f; + + //Prepare Path Commands + tvg::PathCommand cmds2[6]; + cmds2[0] = tvg::PathCommand::MoveTo; + cmds2[1] = tvg::PathCommand::CubicTo; + cmds2[2] = tvg::PathCommand::CubicTo; + cmds2[3] = tvg::PathCommand::CubicTo; + cmds2[4] = tvg::PathCommand::CubicTo; + cmds2[5] = tvg::PathCommand::Close; + + //Prepare Path Points + tvg::Point pts2[13]; + pts2[0] = {cx, cy - radius}; //MoveTo + //CubicTo 1 + pts2[1] = {cx + halfRadius, cy - radius}; //Ctrl1 + pts2[2] = {cx + radius, cy - halfRadius}; //Ctrl2 + pts2[3] = {cx + radius, cy}; //To + //CubicTo 2 + pts2[4] = {cx + radius, cy + halfRadius}; //Ctrl1 + pts2[5] = {cx + halfRadius, cy + radius}; //Ctrl2 + pts2[6] = {cx, cy+ radius}; //To + //CubicTo 3 + pts2[7] = {cx - halfRadius, cy + radius}; //Ctrl1 + pts2[8] = {cx - radius, cy + halfRadius}; //Ctrl2 + pts2[9] = {cx - radius, cy}; //To + //CubicTo 4 + pts2[10] = {cx - radius, cy - halfRadius}; //Ctrl1 + pts2[11] = {cx - halfRadius, cy - radius}; //Ctrl2 + pts2[12] = {cx, cy - radius}; //To + + auto shape2 = tvg::ShapeNode::gen(); + shape2->appendPath(cmds2, 6, pts2, 13); //copy path data + shape2->fill(255, 255, 0, 255); + canvas->push(move(shape2)); + + canvas->draw(); + canvas->sync(); + + //Terminate TizenVG Engine + tvg::Engine::term(); +} + +void +win_del(void *data, Evas_Object *o, void *ev) +{ + elm_exit(); +} + +int main(int argc, char **argv) +{ + tvgtest(); + + //Show the result using EFL... + elm_init(argc, argv); + + Eo* win = elm_win_util_standard_add(NULL, "TizenVG Test"); + evas_object_smart_callback_add(win, "delete,request", win_del, 0); + + Eo* img = evas_object_image_filled_add(evas_object_evas_get(win)); + evas_object_image_size_set(img, WIDTH, HEIGHT); + evas_object_image_data_set(img, buffer); + evas_object_size_hint_weight_set(img, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_show(img); + + elm_win_resize_object_add(win, img); + evas_object_geometry_set(win, 0, 0, WIDTH, HEIGHT); + evas_object_show(win); + + elm_run(); + elm_shutdown(); +} -- 2.7.4 From 74d2f275e74c4aa4bf3b9b68f297c2d0f95d8c80 Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Fri, 1 May 2020 13:59:02 +0900 Subject: [PATCH 16/16] sw_engine: support color blending this contains testBlending as well Change-Id: Ia0aadea804a973cfe8ec981ed1b21c1b44512ef2 --- .gitignore | 1 + inc/tizenvg.h | 4 +- src/lib/sw_engine/tvgSwCommon.h | 2 +- src/lib/sw_engine/tvgSwRaster.cpp | 58 +++++++++++++++++++--- src/lib/sw_engine/tvgSwRenderer.cpp | 31 ++++++++---- src/lib/sw_engine/tvgSwRenderer.h | 3 +- src/lib/tvgRenderCommon.h | 2 +- src/lib/tvgSwCanvas.cpp | 10 ++-- test/makefile | 1 + test/testBlending.cpp | 97 +++++++++++++++++++++++++++++++++++++ test/testBoundary.cpp | 3 +- test/testMergeShapes.cpp | 3 +- test/testMultiShapes.cpp | 3 +- test/testPath.cpp | 3 +- test/testPathCopy.cpp | 3 +- test/testShape.cpp | 3 +- 16 files changed, 194 insertions(+), 33 deletions(-) create mode 100644 test/testBlending.cpp diff --git a/.gitignore b/.gitignore index 2c0e514..c26c55d 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ testMergeShapes testBoundary testPath testPathCopy +testBlending diff --git a/inc/tizenvg.h b/inc/tizenvg.h index 83e5f1d..8708695 100644 --- a/inc/tizenvg.h +++ b/inc/tizenvg.h @@ -179,9 +179,9 @@ class TIZENVG_EXPORT SwCanvas final : public Canvas public: ~SwCanvas(); - int target(uint32_t* buffer, size_t stride, size_t height) noexcept; + int target(uint32_t* buffer, size_t stride, size_t w, size_t h) noexcept; int sync() noexcept override; - static std::unique_ptr gen(uint32_t* buffer = nullptr, size_t stride = 0, size_t height = 0) noexcept; + static std::unique_ptr gen() noexcept; _TIZENVG_DECLARE_PRIVATE(SwCanvas); }; diff --git a/src/lib/sw_engine/tvgSwCommon.h b/src/lib/sw_engine/tvgSwCommon.h index 012f968..4b31bcd 100644 --- a/src/lib/sw_engine/tvgSwCommon.h +++ b/src/lib/sw_engine/tvgSwCommon.h @@ -99,6 +99,6 @@ bool shapeGenRle(const ShapeNode& shape, SwShape& sdata, const SwSize& clip); bool shapeTransformOutline(const ShapeNode& shape, SwShape& sdata); SwRleData* rleRender(const SwShape& sdata, const SwSize& clip); -bool rasterShape(Surface& surface, SwShape& sdata, size_t color); +bool rasterShape(Surface& surface, SwShape& sdata, uint8_t r, uint8_t g, uint8_t b, uint8_t a); #endif /* _TVG_SW_COMMON_H_ */ diff --git a/src/lib/sw_engine/tvgSwRaster.cpp b/src/lib/sw_engine/tvgSwRaster.cpp index a71c443..29350db 100644 --- a/src/lib/sw_engine/tvgSwRaster.cpp +++ b/src/lib/sw_engine/tvgSwRaster.cpp @@ -20,20 +20,66 @@ #include "tvgSwCommon.h" -bool rasterShape(Surface& surface, SwShape& sdata, size_t color) +/************************************************************************/ +/* Internal Class Implementation */ +/************************************************************************/ + +static inline size_t COLOR_ALPHA_BLEND(size_t color, size_t alpha) +{ + return (((((color >> 8) & 0x00ff00ff) * alpha) & 0xff00ff00) + + ((((color & 0x00ff00ff) * alpha) >> 8) & 0x00ff00ff)); +} + + +static inline size_t COLOR_ARGB_JOIN(uint8_t r, uint8_t g, uint8_t b, uint8_t a) +{ + return (a << 24 | r << 16 | g << 8 | b); +} + + +static void +_drawTranslucentSpan(uint32_t* dst, size_t len, size_t color, size_t alpha) +{ + //OPTIMIZE ME: SIMD + auto ialpha = 255 - alpha; + for (size_t i = 0; i < len; ++i) { + dst[i] = color + COLOR_ALPHA_BLEND(dst[i], ialpha); + } +} + + +static void +_drawSolidSpan(uint32_t* dst, size_t len, size_t color) +{ + //OPTIMIZE ME: SIMD + for (size_t i = 0; i < len; ++i) { + dst[i] = color; + } +} + + +/************************************************************************/ +/* External Class Implementation */ +/************************************************************************/ + +bool rasterShape(Surface& surface, SwShape& sdata, uint8_t r, uint8_t g, uint8_t b, uint8_t a) { SwRleData* rle = sdata.rle; if (!rle) return false; - auto stride = surface.stride; auto span = rle->spans; + auto stride = surface.stride; + auto color = COLOR_ARGB_JOIN(r, g, b, a); for (size_t i = 0; i < rle->size; ++i) { assert(span); -// printf("raster y(%d) x(%d) len(%d)\n", span->y, span->x, span->len); - for (auto j = 0; j < span->len; ++j) { - surface.buffer[span->y * stride + span->x + j] = color; - } + + auto dst = &surface.buffer[span->y * stride + span->x]; + assert(dst); + + if (a == 255) _drawSolidSpan(dst, span->len, color); + else _drawTranslucentSpan(dst, span->len, color, a); + ++span; } diff --git a/src/lib/sw_engine/tvgSwRenderer.cpp b/src/lib/sw_engine/tvgSwRenderer.cpp index ae43735..983a1ca 100644 --- a/src/lib/sw_engine/tvgSwRenderer.cpp +++ b/src/lib/sw_engine/tvgSwRenderer.cpp @@ -26,22 +26,33 @@ static RenderInitializer renderInit; -static inline size_t COLOR(uint8_t r, uint8_t g, uint8_t b, uint8_t a) -{ - return (a << 24 | r << 16 | g << 8 | b); -} - /************************************************************************/ /* External Class Implementation */ /************************************************************************/ -bool SwRenderer::target(uint32_t* buffer, size_t stride, size_t height) +bool SwRenderer::clear() +{ + if (!surface.buffer) return false; + + assert(surface.stride > 0 && surface.w > 0 && surface.h > 0); + + //OPTIMIZE ME: SIMD! + for (size_t i = 0; i < surface.h; i++) { + for (size_t j = 0; j < surface.w; j++) + surface.buffer[surface.stride * i + j] = 0xff000000; //Solid Black + } + + return true; +} + +bool SwRenderer::target(uint32_t* buffer, size_t stride, size_t w, size_t h) { - assert(buffer && stride > 0 && height > 0); + assert(buffer && stride > 0 && w > 0 && h > 0); surface.buffer = buffer; surface.stride = stride; - surface.height = height; + surface.w = w; + surface.h = h; return true; } @@ -57,7 +68,7 @@ bool SwRenderer::render(const ShapeNode& shape, void *data) shape.fill(&r, &g, &b, &a); //TODO: Threading - return rasterShape(surface, *sdata, COLOR(r, g, b, a)); + return rasterShape(surface, *sdata, r, g, b, a); } @@ -92,7 +103,7 @@ void* SwRenderer::prepare(const ShapeNode& shape, void* data, UpdateFlag flags) if (!shapeGenOutline(shape, *sdata)) return sdata; if (!shapeTransformOutline(shape, *sdata)) return sdata; - SwSize clip = {static_cast(surface.stride), static_cast(surface.height)}; + SwSize clip = {static_cast(surface.w), static_cast(surface.h)}; if (!shapeGenRle(shape, *sdata, clip)) return sdata; } diff --git a/src/lib/sw_engine/tvgSwRenderer.h b/src/lib/sw_engine/tvgSwRenderer.h index f8ce1d4..5de6c12 100644 --- a/src/lib/sw_engine/tvgSwRenderer.h +++ b/src/lib/sw_engine/tvgSwRenderer.h @@ -25,7 +25,8 @@ public: void* prepare(const ShapeNode& shape, void* data, UpdateFlag flags) override; bool dispose(const ShapeNode& shape, void *data) override; bool render(const ShapeNode& shape, void *data) override; - bool target(uint32_t* buffer, size_t stride, size_t height); + bool target(uint32_t* buffer, size_t stride, size_t w, size_t h); + bool clear(); size_t ref() override; size_t unref() override; diff --git a/src/lib/tvgRenderCommon.h b/src/lib/tvgRenderCommon.h index acdfc2f..ccbc415 100644 --- a/src/lib/tvgRenderCommon.h +++ b/src/lib/tvgRenderCommon.h @@ -25,7 +25,7 @@ struct Surface //TODO: Union for multiple types uint32_t* buffer; size_t stride; - size_t height; + size_t w, h; }; class RenderMethod diff --git a/src/lib/tvgSwCanvas.cpp b/src/lib/tvgSwCanvas.cpp index 04902fc..668c377 100644 --- a/src/lib/tvgSwCanvas.cpp +++ b/src/lib/tvgSwCanvas.cpp @@ -45,12 +45,13 @@ SwCanvas::~SwCanvas() } -int SwCanvas::target(uint32_t* buffer, size_t stride, size_t height) noexcept +int SwCanvas::target(uint32_t* buffer, size_t stride, size_t w, size_t h) noexcept { auto renderer = dynamic_cast(engine()); assert(renderer); - if (!renderer->target(buffer, stride, height)) return -1; + if (!renderer->target(buffer, stride, w, h)) return -1; + if (!renderer->clear()) return -1; return 0; } @@ -62,14 +63,11 @@ int SwCanvas::sync() noexcept } -unique_ptr SwCanvas::gen(uint32_t* buffer, size_t stride, size_t height) noexcept +unique_ptr SwCanvas::gen() noexcept { auto canvas = unique_ptr(new SwCanvas); assert(canvas); - int ret = canvas.get()->target(buffer, stride, height); - if (ret > 0) return nullptr; - return canvas; } diff --git a/test/makefile b/test/makefile index e28d76b..f455654 100644 --- a/test/makefile +++ b/test/makefile @@ -5,3 +5,4 @@ all: gcc -o testBoundary testBoundary.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` gcc -o testPath testPath.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` gcc -o testPathCopy testPathCopy.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` + gcc -o testBlending testBlending.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` diff --git a/test/testBlending.cpp b/test/testBlending.cpp new file mode 100644 index 0000000..ef142aa --- /dev/null +++ b/test/testBlending.cpp @@ -0,0 +1,97 @@ +#include +#include + +using namespace std; + +#define WIDTH 800 +#define HEIGHT 800 + +static uint32_t buffer[WIDTH * HEIGHT]; + +void tvgtest() +{ + //Initialize TizenVG Engine + tvg::Engine::init(); + + //Create a Canvas + auto canvas = tvg::SwCanvas::gen(); + canvas->target(buffer, WIDTH, WIDTH, HEIGHT); + canvas->reserve(5); + + //Prepare Round Rectangle + auto shape1 = tvg::ShapeNode::gen(); + shape1->appendRect(0, 0, 400, 400, 50); //x, y, w, h, cornerRadius + shape1->fill(0, 255, 0, 255); //r, g, b, a + canvas->push(move(shape1)); + + //Prepare Circle + auto shape2 = tvg::ShapeNode::gen(); + shape2->appendCircle(400, 400, 200, 200); //cx, cy, radiusW, radiusH + shape2->fill(170, 170, 0, 170); //r, g, b, a + canvas->push(move(shape2)); + + //Prepare Ellipse + auto shape3 = tvg::ShapeNode::gen(); + shape3->appendCircle(400, 400, 250, 100); //cx, cy, radiusW, radiusH + shape3->fill(100, 100, 100, 100); //r, g, b, a + canvas->push(move(shape3)); + + //Prepare Star + auto shape4 = tvg::ShapeNode::gen(); + shape4->moveTo(199, 234); + shape4->lineTo(253, 343); + shape4->lineTo(374, 360); + shape4->lineTo(287, 444); + shape4->lineTo(307, 565); + shape4->lineTo(199, 509); + shape4->lineTo(97, 565); + shape4->lineTo(112, 445); + shape4->lineTo(26, 361); + shape4->lineTo(146, 343); + shape4->close(); + shape4->fill(200, 0, 200, 200); + canvas->push(move(shape4)); + + //Prepare Opaque Ellipse + auto shape5 = tvg::ShapeNode::gen(); + shape5->appendCircle(600, 650, 200, 150); + shape5->fill(0, 0, 255, 255); + canvas->push(move(shape5)); + + //Draw the Shapes onto the Canvas + canvas->draw(); + canvas->sync(); + + //Terminate TizenVG Engine + tvg::Engine::term(); +} + +void +win_del(void *data, Evas_Object *o, void *ev) +{ + elm_exit(); +} + +int main(int argc, char **argv) +{ + tvgtest(); + + //Show the result using EFL... + elm_init(argc, argv); + + Eo* win = elm_win_util_standard_add(NULL, "TizenVG Test"); + evas_object_smart_callback_add(win, "delete,request", win_del, 0); + + Eo* img = evas_object_image_filled_add(evas_object_evas_get(win)); + evas_object_image_size_set(img, WIDTH, HEIGHT); + evas_object_image_data_set(img, buffer); + evas_object_size_hint_weight_set(img, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_show(img); + + elm_win_resize_object_add(win, img); + evas_object_geometry_set(win, 0, 0, WIDTH, HEIGHT); + evas_object_show(win); + + elm_run(); + elm_shutdown(); +} diff --git a/test/testBoundary.cpp b/test/testBoundary.cpp index 473fd59..7d305be 100644 --- a/test/testBoundary.cpp +++ b/test/testBoundary.cpp @@ -14,7 +14,8 @@ void tvgtest() tvg::Engine::init(); //Create a Canvas - auto canvas = tvg::SwCanvas::gen(buffer, WIDTH, HEIGHT); + auto canvas = tvg::SwCanvas::gen(); + canvas->target(buffer, WIDTH, WIDTH, HEIGHT); canvas->reserve(5); //reserve 5 shape nodes (optional) //Prepare Shape1 diff --git a/test/testMergeShapes.cpp b/test/testMergeShapes.cpp index 268e958..27ef0c5 100644 --- a/test/testMergeShapes.cpp +++ b/test/testMergeShapes.cpp @@ -14,7 +14,8 @@ void tvgtest() tvg::Engine::init(); //Create a Canvas - auto canvas = tvg::SwCanvas::gen(buffer, WIDTH, HEIGHT); + auto canvas = tvg::SwCanvas::gen(); + canvas->target(buffer, WIDTH, WIDTH, HEIGHT); //Prepare a Shape (Rectangle + Rectangle + Circle + Circle) auto shape1 = tvg::ShapeNode::gen(); diff --git a/test/testMultiShapes.cpp b/test/testMultiShapes.cpp index 6fc0469..d53e9fc 100644 --- a/test/testMultiShapes.cpp +++ b/test/testMultiShapes.cpp @@ -14,7 +14,8 @@ void tvgtest() tvg::Engine::init(); //Create a Canvas - auto canvas = tvg::SwCanvas::gen(buffer, WIDTH, HEIGHT); + auto canvas = tvg::SwCanvas::gen(); + canvas->target(buffer, WIDTH, WIDTH, HEIGHT); canvas->reserve(3); //reserve 3 shape nodes (optional) //Prepare Round Rectangle diff --git a/test/testPath.cpp b/test/testPath.cpp index 3c6738a..1ae897d 100644 --- a/test/testPath.cpp +++ b/test/testPath.cpp @@ -14,7 +14,8 @@ void tvgtest() tvg::Engine::init(); //Create a Canvas - auto canvas = tvg::SwCanvas::gen(buffer, WIDTH, HEIGHT); + auto canvas = tvg::SwCanvas::gen(); + canvas->target(buffer, WIDTH, WIDTH, HEIGHT); //Star auto shape1 = tvg::ShapeNode::gen(); diff --git a/test/testPathCopy.cpp b/test/testPathCopy.cpp index a75c7f7..e555226 100644 --- a/test/testPathCopy.cpp +++ b/test/testPathCopy.cpp @@ -14,7 +14,8 @@ void tvgtest() tvg::Engine::init(); //Create a Canvas - auto canvas = tvg::SwCanvas::gen(buffer, WIDTH, HEIGHT); + auto canvas = tvg::SwCanvas::gen(); + canvas->target(buffer, WIDTH, WIDTH, HEIGHT); /* Star */ diff --git a/test/testShape.cpp b/test/testShape.cpp index b7fca2f..4386637 100644 --- a/test/testShape.cpp +++ b/test/testShape.cpp @@ -14,7 +14,8 @@ void tvgtest() tvg::Engine::init(); //Create a Canvas - auto canvas = tvg::SwCanvas::gen(buffer, WIDTH, HEIGHT); + auto canvas = tvg::SwCanvas::gen(); + canvas->target(buffer, WIDTH, WIDTH, HEIGHT); //Prepare a Shape (Rectangle) auto shape1 = tvg::ShapeNode::gen(); -- 2.7.4