sw_engine: implement gradial gradient feature 91/236091/4
authorHermet Park <chuneon.park@samsung.com>
Sun, 14 Jun 2020 08:53:29 +0000 (17:53 +0900)
committerHermet Park <chuneon.park@samsung.com>
Mon, 15 Jun 2020 08:43:52 +0000 (17:43 +0900)
also added testRadialGradient

Change-Id: If4a278cb4667c38c7842ad30edf5aa2fdd56fff7

.gitignore
src/lib/sw_engine/tvgSwCommon.h
src/lib/sw_engine/tvgSwFill.cpp
src/lib/sw_engine/tvgSwRaster.cpp
src/lib/sw_engine/tvgSwRenderer.cpp
test/makefile
test/testRadialGradient.cpp [new file with mode: 0644]

index b9cdf24..f108357 100644 (file)
@@ -15,3 +15,4 @@ testSceneTransform
 testStroke
 testStrokeLine
 testLinearGradient
+testRadialGradient
index 7a0bd41..21a1705 100644 (file)
@@ -158,11 +158,24 @@ struct SwDashStroke
 
 struct SwFill
 {
+    struct SwLinear {
+        float dx, dy;
+        float len;
+        float offset;
+    };
+
+    struct SwRadial {
+        float cx, cy;
+        float a;
+        float inv2a;
+    };
+
+    union {
+        SwLinear linear;
+        SwRadial radial;
+    };
+
     uint32_t* ctable;
-    float x1, y1, x2, y2;
-    float dx, dy;
-    float len;
-    float offset;
     FillSpread spread;
     bool translucent;
 };
@@ -251,12 +264,13 @@ void strokeFree(SwStroke* stroke);
 bool fillGenColorTable(SwFill* fill, const Fill* fdata);
 void fillReset(SwFill* fill, const Fill* fdata);
 void fillFree(SwFill* fill);
-void fillFetch(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len);
+void fillFetchLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len);
+void fillFetchRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len);
 
 SwRleData* rleRender(const SwOutline* outline, const SwBBox& bbox, const SwSize& clip);
 void rleFree(SwRleData* rle);
 
-bool rasterGradientShape(Surface& surface, SwShape& shape);
+bool rasterGradientShape(Surface& surface, SwShape& shape, unsigned id);
 bool rasterSolidShape(Surface& surface, SwShape& shape, uint8_t r, uint8_t g, uint8_t b, uint8_t a);
 bool rasterStroke(Surface& surface, SwShape& shape, uint8_t r, uint8_t g, uint8_t b, uint8_t a);
 
index 9c4927b..1fcafa3 100644 (file)
@@ -29,9 +29,9 @@
 #define FIXPT_SIZE (1<<FIXPT_BITS)
 
 
-static bool _updateColorTable(SwFill* fill, const LinearGradient* linear)
+static bool _updateColorTable(SwFill* fill, const Fill* fdata)
 {
-    assert(fill && linear);
+    assert(fill && fdata);
 
     if (!fill->ctable) {
         fill->ctable = static_cast<uint32_t*>(malloc(GRADIENT_STOP_SIZE * sizeof(uint32_t)));
@@ -39,7 +39,7 @@ static bool _updateColorTable(SwFill* fill, const LinearGradient* linear)
     }
 
     const Fill::ColorStop* colors;
-    auto cnt = linear->colorStops(&colors);
+    auto cnt = fdata->colorStops(&colors);
     if (cnt == 0 || !colors) return false;
 
     auto pColors = colors;
@@ -92,18 +92,18 @@ bool _prepareLinear(SwFill* fill, const LinearGradient* linear)
 {
     assert(fill && linear);
 
-    if (linear->linear(&fill->x1, &fill->y1, &fill->x2, &fill->y2) != Result::Success) return false;
+    float x1, x2, y1, y2;
+    if (linear->linear(&x1, &y1, &x2, &y2) != Result::Success) return false;
 
-    fill->dx = fill->x2 - fill->x1;
-    fill->dy = fill->y2 - fill->y1;
-    fill->len = fill->dx * fill->dx + fill->dy * fill->dy;
-    fill->offset = 0;
+    fill->linear.dx = x2 - x1;
+    fill->linear.dy = y2 - y1;
+    fill->linear.len = fill->linear.dx * fill->linear.dx + fill->linear.dy * fill->linear.dy;
 
-    if (fill->len < FLT_EPSILON) return true;
+    if (fill->linear.len < FLT_EPSILON) return true;
 
-    fill->dx /= fill->len;
-    fill->dy /= fill->len;
-    fill->offset = -fill->dx * fill->x1 - fill->dy * fill->y1;
+    fill->linear.dx /= fill->linear.len;
+    fill->linear.dy /= fill->linear.len;
+    fill->linear.offset = -fill->linear.dx * x1 - fill->linear.dy * y1;
 
     return _updateColorTable(fill, linear);
 }
@@ -113,7 +113,14 @@ bool _prepareRadial(SwFill* fill, const RadialGradient* radial)
 {
     assert(fill && radial);
 
-    return true;
+    float radius;
+    if (radial->radial(&fill->radial.cx, &fill->radial.cy, &radius) != Result::Success) return false;
+    if (radius < FLT_EPSILON) return true;
+
+    fill->radial.a = radius * radius;
+    fill->radial.inv2a = pow(1 / (2 * fill->radial.a), 2);
+
+    return _updateColorTable(fill, radial);
 }
 
 
@@ -180,15 +187,39 @@ static inline void _write(uint32_t *dst, uint32_t val, uint32_t len)
 /* External Class Implementation                                        */
 /************************************************************************/
 
-void fillFetch(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len)
+void fillFetchRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len)
+{
+    if (fill->radial.a < FLT_EPSILON) return;
+
+    //TODO: Rotation???
+    auto rx = x + 0.5f - fill->radial.cx;
+    auto ry = y + 0.5f - fill->radial.cy;
+    auto inv2a = fill->radial.inv2a;
+    auto rxy = rx * rx + ry * ry;
+    auto rxryPlus = 2 * rx;
+    auto det = (-4 * fill->radial.a * -rxy) * inv2a;
+    auto detDelta = (4 * fill->radial.a * (rxryPlus + 1.0f)) * inv2a;
+    auto detDelta2 = (4 * fill->radial.a * 2.0f) * inv2a;
+
+   for (uint32_t i = 0 ; i < len ; ++i)
+     {
+        *dst = _pixel(fill, sqrt(det));
+        ++dst;
+        det += detDelta;
+        detDelta += detDelta2;
+     }
+}
+
+
+void fillFetchLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len)
 {
-    assert(fill->len > 0);
+    if (fill->linear.len < FLT_EPSILON) return;
 
     //TODO: Rotation???
     auto rx = x + 0.5f;
     auto ry = y + 0.5f;
-    auto t = (fill->dx * rx + fill->dy * ry + fill->offset) * (GRADIENT_STOP_SIZE - 1);
-    auto inc = (fill->dx) * (GRADIENT_STOP_SIZE - 1);
+    auto t = (fill->linear.dx * rx + fill->linear.dy * ry + fill->linear.offset) * (GRADIENT_STOP_SIZE - 1);
+    auto inc = (fill->linear.dx) * (GRADIENT_STOP_SIZE - 1);
 
     if (fabsf(inc) < FLT_EPSILON) {
         auto color = _fixedPixel(fill, static_cast<uint32_t>(t * FIXPT_SIZE));
index 581f8b7..05f23e6 100644 (file)
@@ -69,7 +69,7 @@ static bool _rasterSolidRle(Surface& surface, SwRleData* rle, uint32_t color)
 }
 
 
-static bool _rasterGradientRle(Surface& surface, SwRleData* rle, const SwFill* fill)
+static bool _rasterLinearGradientRle(Surface& surface, SwRleData* rle, const SwFill* fill)
 {
     if (!rle || !fill) return false;
 
@@ -81,17 +81,16 @@ static bool _rasterGradientRle(Surface& surface, SwRleData* rle, const SwFill* f
 
     //Translucent Gradient
     if (fill->translucent) {
-        uint32_t tmp;
         for (uint32_t i = 0; i < rle->size; ++i) {
             auto dst = &surface.buffer[span->y * stride + span->x];
-            fillFetch(fill, buf, span->y, span->x, span->len);
+            fillFetchLinear(fill, buf, span->y, span->x, span->len);
             if (span->coverage == 255) {
                 for (uint32_t i = 0; i < span->len; ++i) {
                     dst[i] = buf[i] + COLOR_ALPHA_BLEND(dst[i], 255 - COLOR_ALPHA(buf[i]));
                 }
             } else {
                 for (uint32_t i = 0; i < span->len; ++i) {
-                    tmp = COLOR_ALPHA_BLEND(buf[i], span->coverage);
+                    auto tmp = COLOR_ALPHA_BLEND(buf[i], span->coverage);
                     dst[i] = tmp + COLOR_ALPHA_BLEND(dst[i], 255 - COLOR_ALPHA(tmp));
                 }
             }
@@ -102,9 +101,56 @@ static bool _rasterGradientRle(Surface& surface, SwRleData* rle, const SwFill* f
         for (uint32_t i = 0; i < rle->size; ++i) {
             auto dst = &surface.buffer[span->y * stride + span->x];
             if (span->coverage == 255) {
-                fillFetch(fill, dst, span->y, span->x, span->len);
+                fillFetchLinear(fill, dst, span->y, span->x, span->len);
             } else {
-                fillFetch(fill, buf, span->y, span->x, span->len);
+                fillFetchLinear(fill, buf, span->y, span->x, span->len);
+                auto ialpha = 255 - span->coverage;
+                for (uint32_t i = 0; i < span->len; ++i) {
+                    dst[i] = COLOR_ALPHA_BLEND(buf[i], span->coverage) + COLOR_ALPHA_BLEND(dst[i], ialpha);
+                }
+            }
+            ++span;
+        }
+    }
+    return true;
+}
+
+
+static bool _rasterRadialGradientRle(Surface& surface, SwRleData* rle, const SwFill* fill)
+{
+    if (!rle || !fill) return false;
+
+    auto buf = static_cast<uint32_t*>(alloca(surface.w * sizeof(uint32_t)));
+    if (!buf) return false;
+
+    auto span = rle->spans;
+    auto stride = surface.stride;
+
+    //Translucent Gradient
+    if (fill->translucent) {
+        for (uint32_t i = 0; i < rle->size; ++i) {
+            auto dst = &surface.buffer[span->y * stride + span->x];
+            fillFetchRadial(fill, buf, span->y, span->x, span->len);
+            if (span->coverage == 255) {
+                for (uint32_t i = 0; i < span->len; ++i) {
+                    dst[i] = buf[i] + COLOR_ALPHA_BLEND(dst[i], 255 - COLOR_ALPHA(buf[i]));
+                }
+            } else {
+                for (uint32_t i = 0; i < span->len; ++i) {
+                    auto tmp = COLOR_ALPHA_BLEND(buf[i], span->coverage);
+                    dst[i] = tmp + COLOR_ALPHA_BLEND(dst[i], 255 - COLOR_ALPHA(tmp));
+                }
+            }
+            ++span;
+        }
+    //Opaque Gradient
+    } else {
+        for (uint32_t i = 0; i < rle->size; ++i) {
+            auto dst = &surface.buffer[span->y * stride + span->x];
+            if (span->coverage == 255) {
+                fillFetchRadial(fill, dst, span->y, span->x, span->len);
+            } else {
+                fillFetchRadial(fill, buf, span->y, span->x, span->len);
                 auto ialpha = 255 - span->coverage;
                 for (uint32_t i = 0; i < span->len; ++i) {
                     dst[i] = COLOR_ALPHA_BLEND(buf[i], span->coverage) + COLOR_ALPHA_BLEND(dst[i], ialpha);
@@ -121,9 +167,10 @@ static bool _rasterGradientRle(Surface& surface, SwRleData* rle, const SwFill* f
 /* External Class Implementation                                        */
 /************************************************************************/
 
-bool rasterGradientShape(Surface& surface, SwShape& shape)
+bool rasterGradientShape(Surface& surface, SwShape& shape, unsigned id)
 {
-    return _rasterGradientRle(surface, shape.rle, shape.fill);
+    if (id == FILL_ID_LINEAR) return _rasterLinearGradientRle(surface, shape.rle, shape.fill);
+    return _rasterRadialGradientRle(surface, shape.rle, shape.fill);
 }
 
 
index e99bf38..35a3122 100644 (file)
@@ -64,8 +64,8 @@ bool SwRenderer::render(const Shape& sdata, void *data)
 
     uint8_t r, g, b, a;
 
-    if (sdata.fill()) {
-        rasterGradientShape(surface, *shape);
+    if (auto fill = sdata.fill()) {
+        rasterGradientShape(surface, *shape, fill->id());
     } else {
         sdata.fill(&r, &g, &b, &a);
         if (a > 0) rasterSolidShape(surface, *shape, r, g, b, a);
index 353d7b8..b041e6d 100644 (file)
@@ -13,3 +13,4 @@ all:
        gcc -o testStroke testStroke.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg`
        gcc -o testStrokeLine testStrokeLine.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg`
        gcc -o testLinearGradient testLinearGradient.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg`
+       gcc -o testRadialGradient testRadialGradient.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg`
diff --git a/test/testRadialGradient.cpp b/test/testRadialGradient.cpp
new file mode 100644 (file)
index 0000000..e3e7e6e
--- /dev/null
@@ -0,0 +1,115 @@
+#include <tizenvg.h>
+#include <Elementary.h>
+
+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(3);                          //reserve 3 shape nodes (optional)
+
+    //Prepare Round Rectangle
+    auto shape1 = tvg::Shape::gen();
+    shape1->appendRect(0, 0, 400, 400, 0);      //x, y, w, h, cornerRadius
+
+    //RadialGradient
+    auto fill = tvg::RadialGradient::gen();
+    fill->radial(200, 200, 200);
+
+    //Linear Gradient Color Stops
+    tvg::Fill::ColorStop colorStops[2];
+    colorStops[0] = {0, 255, 255, 255, 255};
+    colorStops[1] = {1, 0, 0, 0, 255};
+
+    fill->colorStops(colorStops, 2);
+
+    shape1->fill(move(fill));
+    canvas->push(move(shape1));
+
+    //Prepare Circle
+    auto shape2 = tvg::Shape::gen();
+    shape2->appendCircle(400, 400, 200, 200);    //cx, cy, radiusW, radiusH
+
+    //RadialGradient
+    auto fill2 = tvg::RadialGradient::gen();
+    fill2->radial(400, 400, 200);
+
+    //Linear Gradient Color Stops
+    tvg::Fill::ColorStop colorStops2[3];
+    colorStops2[0] = {0, 255, 0, 0, 255};
+    colorStops2[1] = {0.5, 255, 255, 0, 255};
+    colorStops2[2] = {1, 255, 255, 255, 255};
+
+    fill2->colorStops(colorStops2, 3);
+
+    shape2->fill(move(fill2));
+    canvas->push(move(shape2));
+
+
+    //Prepare Ellipse
+    auto shape3 = tvg::Shape::gen();
+    shape3->appendCircle(600, 600, 150, 100);    //cx, cy, radiusW, radiusH
+
+    //RadialGradient
+    auto fill3 = tvg::RadialGradient::gen();
+    fill3->radial(600, 600, 150);
+
+    //Linear Gradient Color Stops
+    tvg::Fill::ColorStop colorStops3[4];
+    colorStops3[0] = {0, 0, 127, 0, 127};
+    colorStops3[1] = {0.25, 0, 170, 170, 170};
+    colorStops3[2] = {0.5, 200, 0, 200, 200};
+    colorStops3[3] = {1, 255, 255, 255, 255};
+
+    fill3->colorStops(colorStops3, 4);
+
+    shape3->fill(move(fill3));
+    canvas->push(move(shape3));
+
+    //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();
+}