diff --git a/inc/tizenvg.h b/inc/tizenvg.h index b443d503..83254406 100644 --- a/inc/tizenvg.h +++ b/inc/tizenvg.h @@ -72,7 +72,13 @@ class TIZENVG_EXPORT Paint { public: virtual ~Paint() {} + virtual int update(RenderMethod*) = 0; + + virtual int rotate(float degree) = 0; + virtual int scale(float factor) = 0; + + virtual int bounds(float&x, float& y, float& w, float& h) const = 0; }; @@ -130,9 +136,13 @@ public: int fill(size_t r, size_t g, size_t b, size_t a) noexcept; + int rotate(float degree) noexcept override; + int scale(float factor) noexcept override; + int pathCommands(const PathCommand** cmds) const noexcept; int pathCoords(const Point** pts) const noexcept; int fill(size_t* r, size_t* g, size_t* b, size_t* a) const noexcept; + int bounds(float&x, float& y, float& w, float& h) const noexcept override; static std::unique_ptr gen() noexcept; @@ -157,9 +167,13 @@ public: ~Scene(); int update(RenderMethod* engine) noexcept override; - int push(std::unique_ptr shape) noexcept; + int rotate(float degree) noexcept override; + int scale(float factor) noexcept override; + + int bounds(float&x, float& y, float& w, float& h) const noexcept override; + static std::unique_ptr gen() noexcept; _TIZENVG_DECLARE_PRIVATE(Scene); diff --git a/src/lib/tvgScene.cpp b/src/lib/tvgScene.cpp index 75725617..bf937a24 100644 --- a/src/lib/tvgScene.cpp +++ b/src/lib/tvgScene.cpp @@ -64,4 +64,22 @@ int Scene::update(RenderMethod* engine) noexcept return 0; } -#endif /* _TVG_SCENE_CPP_ */ + +int Scene::scale(float scaleFacator) noexcept +{ + return 0; +} + + +int Scene::rotate(float degree) noexcept +{ + return 0; +} + + +int Scene::bounds(float& x, float& y, float& w, float& h) const noexcept +{ + return 0; +} + +#endif /* _TVG_SCENE_CPP_ */ \ No newline at end of file diff --git a/src/lib/tvgShape.cpp b/src/lib/tvgShape.cpp index 5fbfce84..9c41c082 100644 --- a/src/lib/tvgShape.cpp +++ b/src/lib/tvgShape.cpp @@ -35,19 +35,14 @@ struct ShapeStroke }; -struct ShapeTransform -{ - float e[4*4]; -}; - - struct Shape::Impl { - ShapeTransform *transform = nullptr; ShapeFill *fill = nullptr; ShapeStroke *stroke = nullptr; ShapePath *path = nullptr; uint8_t color[4] = {0, 0, 0, 0}; //r, g, b, a + float scale = 1; + float rotate = 0; void *edata = nullptr; //engine data Impl() : path(new ShapePath) @@ -59,7 +54,14 @@ struct Shape::Impl if (path) delete(path); if (stroke) delete(stroke); if (fill) delete(fill); - if (transform) delete(transform); + } + + bool update() + { + if (path->scale(scale)) scale = 1; + if (path->rotate(rotate)) rotate = 0; + + return true; } }; @@ -97,6 +99,7 @@ int Shape::update(RenderMethod* engine) noexcept auto impl = pImpl.get(); assert(impl); + if (!impl->update()) return -1; impl->edata = engine->prepare(*this, impl->edata, RenderMethod::UpdateFlag::All); if (impl->edata) return 0; return - 1; @@ -106,7 +109,7 @@ int Shape::update(RenderMethod* engine) noexcept int Shape::reset() noexcept { auto impl = pImpl.get(); - assert(impl); + assert(impl && impl->path); impl->path->reset(); @@ -117,7 +120,7 @@ int Shape::reset() noexcept int Shape::pathCommands(const PathCommand** cmds) const noexcept { auto impl = pImpl.get(); - assert(impl && cmds); + assert(impl && impl->path && cmds); *cmds = impl->path->cmds; @@ -128,7 +131,7 @@ int Shape::pathCommands(const PathCommand** cmds) const noexcept int Shape::pathCoords(const Point** pts) const noexcept { auto impl = pImpl.get(); - assert(impl && pts); + assert(impl && impl->path && pts); *pts = impl->path->pts; @@ -142,7 +145,7 @@ int Shape::appendPath(const PathCommand *cmds, size_t cmdCnt, const Point* pts, assert(cmds && pts); auto impl = pImpl.get(); - assert(impl); + assert(impl && impl->path); impl->path->grow(cmdCnt, ptsCnt); impl->path->append(cmds, cmdCnt, pts, ptsCnt); @@ -154,7 +157,7 @@ int Shape::appendPath(const PathCommand *cmds, size_t cmdCnt, const Point* pts, int Shape::moveTo(float x, float y) noexcept { auto impl = pImpl.get(); - assert(impl); + assert(impl && impl->path); impl->path->moveTo(x, y); @@ -165,7 +168,7 @@ int Shape::moveTo(float x, float y) noexcept int Shape::lineTo(float x, float y) noexcept { auto impl = pImpl.get(); - assert(impl); + assert(impl && impl->path); impl->path->lineTo(x, y); @@ -176,7 +179,7 @@ int Shape::lineTo(float x, float y) noexcept int Shape::cubicTo(float cx1, float cy1, float cx2, float cy2, float x, float y) noexcept { auto impl = pImpl.get(); - assert(impl); + assert(impl && impl->path); impl->path->cubicTo(cx1, cy1, cx2, cy2, x, y); @@ -187,7 +190,7 @@ int Shape::cubicTo(float cx1, float cy1, float cx2, float cy2, float x, float y) int Shape::close() noexcept { auto impl = pImpl.get(); - assert(impl); + assert(impl && impl->path); impl->path->close(); @@ -198,7 +201,7 @@ int Shape::close() noexcept int Shape::appendCircle(float cx, float cy, float radiusW, float radiusH) noexcept { auto impl = pImpl.get(); - assert(impl); + assert(impl && impl->path); auto halfKappaW = radiusW * PATH_KAPPA; auto halfKappaH = radiusH * PATH_KAPPA; @@ -218,7 +221,7 @@ int Shape::appendCircle(float cx, float cy, float radiusW, float radiusH) noexce int Shape::appendRect(float x, float y, float w, float h, float cornerRadius) noexcept { auto impl = pImpl.get(); - assert(impl); + assert(impl && impl->path); //clamping cornerRadius by minimum size auto min = (w < h ? w : h) * 0.5f; @@ -281,4 +284,41 @@ int Shape::fill(size_t* r, size_t* g, size_t* b, size_t* a) const noexcept return 0; } + +int Shape::scale(float factor) noexcept +{ + if (factor < FLT_EPSILON || fabsf(factor - 1) <= FLT_EPSILON) return -1; + + auto impl = pImpl.get(); + assert(impl); + + impl->scale *= factor; + + return 0; +} + + +int Shape::rotate(float degree) noexcept +{ + if (fabsf(degree) <= FLT_EPSILON) return -1; + + auto impl = pImpl.get(); + assert(impl); + + impl->rotate += degree; + + return 0; +} + + +int Shape::bounds(float& x, float& y, float& w, float& h) const noexcept +{ + auto impl = pImpl.get(); + assert(impl && impl->path); + + if (!impl->path->bounds(x, y, w, h)) return -1; + + return 0; +} + #endif //_TVG_SHAPE_CPP_ diff --git a/src/lib/tvgShapePath.h b/src/lib/tvgShapePath.h index 6b016f1f..9c42829c 100644 --- a/src/lib/tvgShapePath.h +++ b/src/lib/tvgShapePath.h @@ -112,6 +112,71 @@ struct ShapePath if (cmdCnt + 1 > reservedCmdCnt) reserveCmd((cmdCnt + 1) * 2); cmds[cmdCnt++] = PathCommand::Close; } + + bool bounds(float& x, float& y, float& w, float& h) + { + if (ptsCnt == 0) return false; + + 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 true; + } + + bool rotate(float degree) + { + constexpr auto PI = 3.141592f; + + if (fabsf(degree) <= FLT_EPSILON) return false; + + float x, y, w, h; + if (!bounds(x, y, w, h)) return false; + + auto radian = degree / 180.0f * PI; + auto cx = x + w * 0.5f; + auto cy = y + h * 0.5f; + auto cosVal = cosf(radian); + auto sinVal = sinf(radian); + + for(size_t i = 0; i < ptsCnt; ++i) { + auto dx = pts[i].x - cx; + auto dy = pts[i].y - cy; + pts[i].x = (cosVal * dx - sinVal * dy) + cx; + pts[i].y = (sinVal * dx + cosVal * dy) + cy; + } + + return true; + } + + bool scale(float factor) + { + if (fabsf(factor - 1) <= FLT_EPSILON) return false; + + float x, y, w, h; + if (!bounds(x, y, w, h)) return false; + + auto cx = x + w * 0.5f; + auto cy = y + h * 0.5f; + + for(size_t i = 0; i < ptsCnt; ++i) { + pts[i].x = (pts[i].x - cx) * factor + cx; + pts[i].y = (pts[i].y - cy) * factor + cy; + } + + return true; + } }; #endif //_TVG_SHAPE_PATH_CPP_ diff --git a/test/testDirectUpdate.cpp b/test/testDirectUpdate.cpp index df5384df..1888de44 100644 --- a/test/testDirectUpdate.cpp +++ b/test/testDirectUpdate.cpp @@ -25,6 +25,7 @@ void tvgtest() shape->appendRect(-100, -100, 200, 200, 0); shape->fill(127, 255, 255, 255); + shape->rotate(45); canvas->push(move(shape)); //Draw first frame @@ -42,6 +43,12 @@ void transit_cb(Elm_Transit_Effect *effect, Elm_Transit* transit, double progres pShape->appendRect(-100 + (800 * progress), -100 + (800 * progress), 200, 200, (100 * progress)); + /* rotate, scale won't be retained, when you call reset() for the shape, these values will be reset as well. + These are working in fire & forget method, it actually modify the path data for avoiding compuatation every frames. + Thus user needs to keep the last values to understand the final accumulated values. */ + pShape->rotate(45); + pShape->scale(1 - 0.75 * progress); + //Update shape for drawing (this may work asynchronously) pShape->update(canvas->engine()); diff --git a/test/testUpdate.cpp b/test/testUpdate.cpp index f2256245..d941e26d 100644 --- a/test/testUpdate.cpp +++ b/test/testUpdate.cpp @@ -35,6 +35,9 @@ void transit_cb(Elm_Transit_Effect *effect, Elm_Transit* transit, double progres auto shape = tvg::Shape::gen(); shape->appendRect(-100 + (800 * progress), -100 + (800 * progress), 200, 200, (100 * progress)); shape->fill(rand()%255, rand()%255, rand()%255, 255); + shape->scale(1 - 0.75 * progress); + shape->rotate(360 * progress); + canvas->push(move(shape)); //Draw Next frames