From 7366e8949bbae8a4cb201ef2dbc2df2b84e232dc Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Sun, 14 Jun 2020 17:53:29 +0900 Subject: [PATCH] sw_engine: implement gradial gradient feature also added testRadialGradient Change-Id: If4a278cb4667c38c7842ad30edf5aa2fdd56fff7 --- .gitignore | 1 + src/lib/sw_engine/tvgSwCommon.h | 26 ++++++-- src/lib/sw_engine/tvgSwFill.cpp | 65 ++++++++++++++------ src/lib/sw_engine/tvgSwRaster.cpp | 63 +++++++++++++++++--- src/lib/sw_engine/tvgSwRenderer.cpp | 4 +- test/makefile | 1 + test/testRadialGradient.cpp | 115 ++++++++++++++++++++++++++++++++++++ 7 files changed, 242 insertions(+), 33 deletions(-) create mode 100644 test/testRadialGradient.cpp diff --git a/.gitignore b/.gitignore index b9cdf24..f108357 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ testSceneTransform testStroke testStrokeLine testLinearGradient +testRadialGradient diff --git a/src/lib/sw_engine/tvgSwCommon.h b/src/lib/sw_engine/tvgSwCommon.h index 7a0bd41..21a1705 100644 --- a/src/lib/sw_engine/tvgSwCommon.h +++ b/src/lib/sw_engine/tvgSwCommon.h @@ -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); diff --git a/src/lib/sw_engine/tvgSwFill.cpp b/src/lib/sw_engine/tvgSwFill.cpp index 9c4927b..1fcafa3 100644 --- a/src/lib/sw_engine/tvgSwFill.cpp +++ b/src/lib/sw_engine/tvgSwFill.cpp @@ -29,9 +29,9 @@ #define FIXPT_SIZE (1<ctable) { fill->ctable = static_cast(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(t * FIXPT_SIZE)); diff --git a/src/lib/sw_engine/tvgSwRaster.cpp b/src/lib/sw_engine/tvgSwRaster.cpp index 581f8b7..05f23e6 100644 --- a/src/lib/sw_engine/tvgSwRaster.cpp +++ b/src/lib/sw_engine/tvgSwRaster.cpp @@ -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(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); } diff --git a/src/lib/sw_engine/tvgSwRenderer.cpp b/src/lib/sw_engine/tvgSwRenderer.cpp index e99bf38..35a3122 100644 --- a/src/lib/sw_engine/tvgSwRenderer.cpp +++ b/src/lib/sw_engine/tvgSwRenderer.cpp @@ -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); diff --git a/test/makefile b/test/makefile index 353d7b8..b041e6d 100644 --- a/test/makefile +++ b/test/makefile @@ -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 index 0000000..e3e7e6e --- /dev/null +++ b/test/testRadialGradient.cpp @@ -0,0 +1,115 @@ +#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(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(); +} -- 2.7.4