From 74d2f275e74c4aa4bf3b9b68f297c2d0f95d8c80 Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Fri, 1 May 2020 13:59:02 +0900 Subject: [PATCH] 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