common shape: support scale/rotate transform 64/232364/4
authorHermet Park <chuneon.park@samsung.com>
Sat, 2 May 2020 13:30:48 +0000 (22:30 +0900)
committerHermet Park <chuneon.park@samsung.com>
Sat, 2 May 2020 14:01:16 +0000 (23:01 +0900)
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.

Change-Id: I41f260271cdefc977eea01a778d49632440c777f

inc/tizenvg.h
src/lib/tvgScene.cpp
src/lib/tvgShape.cpp
src/lib/tvgShapePath.h
test/testDirectUpdate.cpp
test/testUpdate.cpp

index b443d50..8325440 100644 (file)
@@ -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<Shape> gen() noexcept;
 
@@ -157,9 +167,13 @@ public:
     ~Scene();
 
     int update(RenderMethod* engine) noexcept override;
-
     int push(std::unique_ptr<Shape> 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<Scene> gen() noexcept;
 
     _TIZENVG_DECLARE_PRIVATE(Scene);
index 7572561..bf937a2 100644 (file)
@@ -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
index 5fbfce8..9c41c08 100644 (file)
@@ -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_
index 6b016f1..9c42829 100644 (file)
@@ -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_
index df5384d..1888de4 100644 (file)
@@ -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());
 
index f225624..d941e26 100644 (file)
@@ -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