diff --git a/.gitignore b/.gitignore index f1083577..bc115012 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,4 @@ testStroke testStrokeLine testLinearGradient testRadialGradient +testGradientTransform diff --git a/src/lib/sw_engine/tvgSwCommon.h b/src/lib/sw_engine/tvgSwCommon.h index 21a17059..eedaf9d9 100644 --- a/src/lib/sw_engine/tvgSwCommon.h +++ b/src/lib/sw_engine/tvgSwCommon.h @@ -252,7 +252,7 @@ void shapeResetStroke(SwShape& shape, const Shape& sdata); bool shapeGenStrokeRle(SwShape& shape, const Shape& sdata, const SwSize& clip); void shapeFree(SwShape* shape); void shapeDelStroke(SwShape& shape); -bool shapeGenFillColors(SwShape& shape, const Fill* fill); +bool shapeGenFillColors(SwShape& shape, const Fill* fill, const RenderTransform* transform, bool ctable); void shapeResetFill(SwShape& shape, const Fill* fill); void shapeDelFill(SwShape& shape); @@ -261,7 +261,7 @@ bool strokeParseOutline(SwStroke& stroke, const SwOutline& outline); SwOutline* strokeExportOutline(SwStroke& stroke); void strokeFree(SwStroke* stroke); -bool fillGenColorTable(SwFill* fill, const Fill* fdata); +bool fillGenColorTable(SwFill* fill, const Fill* fdata, const RenderTransform* transform, bool ctable); void fillReset(SwFill* fill, const Fill* fdata); void fillFree(SwFill* fill); void fillFetchLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len); diff --git a/src/lib/sw_engine/tvgSwFill.cpp b/src/lib/sw_engine/tvgSwFill.cpp index 1fcafa38..f6091837 100644 --- a/src/lib/sw_engine/tvgSwFill.cpp +++ b/src/lib/sw_engine/tvgSwFill.cpp @@ -88,13 +88,26 @@ static bool _updateColorTable(SwFill* fill, const Fill* fdata) } -bool _prepareLinear(SwFill* fill, const LinearGradient* linear) +bool _prepareLinear(SwFill* fill, const LinearGradient* linear, const RenderTransform* transform) { assert(fill && linear); float x1, x2, y1, y2; if (linear->linear(&x1, &y1, &x2, &y2) != Result::Success) return false; + if (transform) { + auto cx = (x2 - x1) * 0.5f + x1; + auto cy = (y2 - y1) * 0.5f + y1; + auto dx = x1 - cx; + auto dy = y1 - cy; + x1 = dx * transform->e11 + dy * transform->e12 + transform->e31; + y1 = dx * transform->e21 + dy * transform->e22 + transform->e32; + dx = x2 - cx; + dy = y2 - cy; + x2 = dx * transform->e11 + dy * transform->e12 + transform->e31; + y2 = dx * transform->e21 + dy * transform->e22 + transform->e32; + } + 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; @@ -105,11 +118,11 @@ bool _prepareLinear(SwFill* fill, const LinearGradient* linear) fill->linear.dy /= fill->linear.len; fill->linear.offset = -fill->linear.dx * x1 - fill->linear.dy * y1; - return _updateColorTable(fill, linear); + return true; } -bool _prepareRadial(SwFill* fill, const RadialGradient* radial) +bool _prepareRadial(SwFill* fill, const RadialGradient* radial, const RenderTransform* transform) { assert(fill && radial); @@ -117,10 +130,18 @@ bool _prepareRadial(SwFill* fill, const RadialGradient* radial) if (radial->radial(&fill->radial.cx, &fill->radial.cy, &radius) != Result::Success) return false; if (radius < FLT_EPSILON) return true; + if (transform) { + auto tx = fill->radial.cx * transform->e11 + fill->radial.cy * transform->e12 + transform->e31; + auto ty = fill->radial.cx * transform->e21 + fill->radial.cy * transform->e22 + transform->e32; + fill->radial.cx = tx; + fill->radial.cy = ty; + radius *= transform->e33; + } + fill->radial.a = radius * radius; fill->radial.inv2a = pow(1 / (2 * fill->radial.a), 2); - return _updateColorTable(fill, radial); + return true; } @@ -251,7 +272,7 @@ void fillFetchLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, } -bool fillGenColorTable(SwFill* fill, const Fill* fdata) +bool fillGenColorTable(SwFill* fill, const Fill* fdata, const RenderTransform* transform, bool ctable) { if (!fill) return false; @@ -259,10 +280,14 @@ bool fillGenColorTable(SwFill* fill, const Fill* fdata) fill->spread = fdata->spread(); + if (ctable) { + if (!_updateColorTable(fill, fdata)) return false; + } + if (fdata->id() == FILL_ID_LINEAR) { - return _prepareLinear(fill, static_cast(fdata)); + return _prepareLinear(fill, static_cast(fdata), transform); } else if (fdata->id() == FILL_ID_RADIAL) { - return _prepareRadial(fill, static_cast(fdata)); + return _prepareRadial(fill, static_cast(fdata), transform); } cout << "What type of gradient?!" << endl; diff --git a/src/lib/sw_engine/tvgSwRenderer.cpp b/src/lib/sw_engine/tvgSwRenderer.cpp index 35a3122e..1246eabf 100644 --- a/src/lib/sw_engine/tvgSwRenderer.cpp +++ b/src/lib/sw_engine/tvgSwRenderer.cpp @@ -113,11 +113,12 @@ void* SwRenderer::prepare(const Shape& sdata, void* data, const RenderTransform* } //Fill - if (flags & (RenderUpdateFlag::Gradient)) { + if (flags & (RenderUpdateFlag::Gradient | RenderUpdateFlag::Transform)) { auto fill = sdata.fill(); if (fill) { - shapeResetFill(*shape, fill); - if (!shapeGenFillColors(*shape, fill)) return shape; + auto ctable = (flags & RenderUpdateFlag::Gradient) ? true : false; + if (ctable) shapeResetFill(*shape, fill); + if (!shapeGenFillColors(*shape, fill, transform, ctable)) return shape; } else { shapeDelFill(*shape); } diff --git a/src/lib/sw_engine/tvgSwShape.cpp b/src/lib/sw_engine/tvgSwShape.cpp index 4dcf2109..4d3c44ec 100644 --- a/src/lib/sw_engine/tvgSwShape.cpp +++ b/src/lib/sw_engine/tvgSwShape.cpp @@ -347,9 +347,9 @@ static void _transformOutline(SwOutline* outline, const RenderTransform* transfo for(uint32_t i = 0; i < outline->ptsCnt; ++i) { auto dx = static_cast(outline->pts[i].x >> 6); auto dy = static_cast(outline->pts[i].y >> 6); - auto tx = dx * transform->e11 + dy * transform->e12 + transform->e13; - auto ty = dx * transform->e21 + dy * transform->e22 + transform->e23; - auto pt = Point{tx + transform->e31, ty + transform->e32}; + auto tx = dx * transform->e11 + dy * transform->e12 + transform->e31; + auto ty = dx * transform->e21 + dy * transform->e22 + transform->e32; + auto pt = Point{tx, ty}; outline->pts[i] = TO_SWPOINT(&pt); } } @@ -740,11 +740,11 @@ bool shapeGenStrokeRle(SwShape& shape, const Shape& sdata, const SwSize& clip) } -bool shapeGenFillColors(SwShape& shape, const Fill* fill) +bool shapeGenFillColors(SwShape& shape, const Fill* fill, const RenderTransform* transform, bool ctable) { assert(fill); - fillGenColorTable(shape.fill, fill); + fillGenColorTable(shape.fill, fill, transform, ctable); return true; } diff --git a/test/makefile b/test/makefile index b041e6de..431fa665 100644 --- a/test/makefile +++ b/test/makefile @@ -14,3 +14,4 @@ all: 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` + gcc -o testGradientTransform testGradientTransform.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` diff --git a/test/testGradientTransform.cpp b/test/testGradientTransform.cpp new file mode 100644 index 00000000..0b4b677c --- /dev/null +++ b/test/testGradientTransform.cpp @@ -0,0 +1,170 @@ +#include +#include + +using namespace std; + +#define WIDTH 800 +#define HEIGHT 800 + +static uint32_t buffer[WIDTH * HEIGHT]; +unique_ptr canvas = nullptr; +tvg::Shape* pShape = nullptr; +tvg::Shape* pShape2 = nullptr; +tvg::Shape* pShape3 = nullptr; + +void tvgtest() +{ + //Create a Canvas + canvas = tvg::SwCanvas::gen(); + canvas->target(buffer, WIDTH, WIDTH, HEIGHT); + + //Shape1 + auto shape = tvg::Shape::gen(); + + /* Acquire shape pointer to access it again. + instead, you should consider not to interrupt this pointer life-cycle. */ + pShape = shape.get(); + + shape->appendRect(-285, -300, 200, 200, 0); + shape->appendRect(-185, -200, 300, 300, 100); + shape->appendCircle(115, 100, 100, 100); + shape->appendCircle(115, 200, 170, 100); + + //LinearGradient + auto fill = tvg::LinearGradient::gen(); + fill->linear(-285, -300, 285, 300); + + //Gradient Color Stops + tvg::Fill::ColorStop colorStops[3]; + colorStops[0] = {0, 255, 0, 0, 255}; + colorStops[1] = {0.5, 255, 255, 0, 255}; + colorStops[2] = {1, 255, 255, 255, 255}; + + fill->colorStops(colorStops, 3); + shape->fill(move(fill)); + shape->translate(385, 400); + canvas->push(move(shape)); + + //Shape2 + auto shape2 = tvg::Shape::gen(); + pShape2 = shape2.get(); + shape2->appendRect(-50, -50, 100, 100, 0); + shape2->translate(400, 400); + + //LinearGradient + auto fill2 = tvg::LinearGradient::gen(); + fill2->linear(-50, -50, 50, 50); + + //Gradient Color Stops + tvg::Fill::ColorStop colorStops2[2]; + colorStops2[0] = {0, 0, 0, 0, 255}; + colorStops2[1] = {1, 255, 255, 255, 255}; + + fill2->colorStops(colorStops2, 2); + shape2->fill(move(fill2)); + canvas->push(move(shape2)); + + //Shape3 + auto shape3 = tvg::Shape::gen(); + pShape3 = shape3.get(); + + /* Look, how shape3's origin is different with shape2 + The center of the shape is the anchor point for transformation. */ + shape3->appendRect(100, 100, 150, 100, 20); + + //RadialGradient + auto fill3 = tvg::RadialGradient::gen(); + fill3->radial(175, 150, 75); + + //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)); + shape3->translate(400, 400); + canvas->push(move(shape3)); + + //Draw first frame + canvas->draw(); + canvas->sync(); +} + +void transit_cb(Elm_Transit_Effect *effect, Elm_Transit* transit, double progress) +{ + /* Update shape directly. + You can update only necessary properties of this shape, + while retaining other properties. */ + + //Update Shape1 + pShape->scale(1 - 0.75 * progress); + pShape->rotate(360 * progress); + + //Update shape for drawing (this may work asynchronously) + canvas->update(pShape); + + //Update Shape2 + pShape2->rotate(360 * progress); + pShape2->translate(400 + progress * 300, 400); + canvas->update(pShape2); + + //Update Shape3 + pShape3->rotate(-360 * progress); + pShape3->scale(0.5 + progress); + canvas->update(pShape3); + + //Draw Next frames + canvas->draw(); + canvas->sync(); + + //Update Efl Canvas + Eo* img = (Eo*) effect; + evas_object_image_data_update_add(img, 0, 0, WIDTH, HEIGHT); +} + +void +win_del(void *data, Evas_Object *o, void *ev) +{ + elm_exit(); +} + +int main(int argc, char **argv) +{ + //Initialize TizenVG Engine + tvg::Engine::init(); + + 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_Transit *transit = elm_transit_add(); + elm_transit_effect_add(transit, transit_cb, img, nullptr); + elm_transit_duration_set(transit, 2); + elm_transit_repeat_times_set(transit, -1); + elm_transit_auto_reverse_set(transit, EINA_TRUE); + elm_transit_go(transit); + + elm_run(); + elm_shutdown(); + + //Terminate TizenVG Engine + tvg::Engine::term(); +}