lottie/vector: refactor vpath class to use vcow_ptr 61/184761/2
authorsubhransu mohanty <sub.mohanty@samsung.com>
Mon, 23 Jul 2018 00:33:56 +0000 (09:33 +0900)
committerHermet Park <chuneon.park@samsung.com>
Mon, 23 Jul 2018 05:39:43 +0000 (05:39 +0000)
Change-Id: Ib16da52a8a97bbee87766ee362e1d6e5678ac305

src/vector/vpath.cpp
src/vector/vpath.h

index 697bd54..d8e2e13 100644 (file)
@@ -7,56 +7,34 @@
 
 V_BEGIN_NAMESPACE
 
-struct VPathData
-{
-    VPathData():ref(-1),
-                m_points(),
-                m_elements(),
-                m_segments(0),
-                mStartPoint(),
-                mNewSegment(true){}
-
-    void copy(VPathData *o);
-    void moveTo(const VPointF &pt);
-    void lineTo(const VPointF &pt);
-    void cubicTo(const VPointF &c1, const VPointF &c2, const VPointF &e);
-    void close();
-    void reset();
-    void checkNewSegment();
-    int  segments() const;
-    void transform(const VMatrix &m);
-    RefCount                      ref;
-    std::vector<VPointF>         m_points;
-    std::vector<VPath::Element>  m_elements;
-    int                           m_segments;
-    VPointF                      mStartPoint;
-    bool                          mNewSegment;
-};
-
-
-void VPathData::transform(const VMatrix &m)
+VPath::VPathData::VPathData():m_points(),
+                              m_elements(),
+                              m_segments(0),
+                              mStartPoint(),
+                              mNewSegment(true){}
+
+VPath::VPathData::VPathData(const VPathData &o):m_points(o.m_points),
+                                                m_elements(o.m_elements),
+                                                m_segments(o.m_segments),
+                                                mStartPoint(o.mStartPoint),
+                                                mNewSegment(o.mNewSegment){}
+
+void VPath::VPathData::transform(const VMatrix &m)
 {
     for(auto &i : m_points) {
         i = m.map(i);
     }
 }
 
-void VPathData::checkNewSegment()
+void VPath::VPathData::checkNewSegment()
 {
     if (mNewSegment) {
         moveTo(VPointF(0,0));
         mNewSegment = false;
     }
 }
-void VPathData::copy(VPathData *o)
-{
-    m_points = o->m_points;
-    m_elements = o->m_elements;
-    m_segments = o->m_segments;
-    mStartPoint = o->mStartPoint;
-}
 
-void VPathData::moveTo(const VPointF &p)
+void VPath::VPathData::moveTo(const VPointF &p)
 {
     mStartPoint = p;
     mNewSegment = false;
@@ -64,13 +42,13 @@ void VPathData::moveTo(const VPointF &p)
     m_points.push_back(p);
     m_segments++;
 }
-void VPathData::lineTo(const VPointF &p)
+void VPath::VPathData::lineTo(const VPointF &p)
 {
     checkNewSegment();
     m_elements.push_back(VPath::Element::LineTo);
     m_points.push_back(p);
 }
-void VPathData::cubicTo(const VPointF &c1, const VPointF &c2, const VPointF &e)
+void VPath::VPathData::cubicTo(const VPointF &c1, const VPointF &c2, const VPointF &e)
 {
     checkNewSegment();
     m_elements.push_back(VPath::Element::CubicTo);
@@ -79,8 +57,10 @@ void VPathData::cubicTo(const VPointF &c1, const VPointF &c2, const VPointF &e)
     m_points.push_back(e);
 }
 
-void VPathData::close()
+void VPath::VPathData::close()
 {
+    if (isEmpty()) return;
+
     const VPointF &lastPt = m_points.back();
     if (!fuzzyCompare(mStartPoint, lastPt)) {
        lineTo(mStartPoint);
@@ -89,144 +69,149 @@ void VPathData::close()
     mNewSegment = true;
 }
 
-void VPathData::reset()
+void VPath::VPathData::reset()
 {
+    if (isEmpty()) return;
+
     m_elements.clear();
     m_points.clear();
     m_segments = 0;
 }
 
-int  VPathData::segments() const
+int VPath::VPathData::segments() const
 {
-    return m_segments;
+    return m_segments + 1;
 }
 
-
-static const struct VPathData shared_empty;
-
-inline void VPath::cleanUp(VPathData *d)
+void VPath::VPathData::reserve(int num_elm)
 {
-    delete d;
+    m_elements.reserve(num_elm);
+    m_points.reserve(2 * num_elm);
 }
 
-void VPath::detach()
-{
-    if (d->ref.isShared())
-        *this = copy();
-}
+static VPointF curvesForArc(const VRectF &, float, float, VPointF *, int *);
+static constexpr float PATH_KAPPA = 0.5522847498;
 
-VPath VPath::copy() const
+void VPath::VPathData::arcTo(const VRectF &rect, float startAngle, float sweepLength, bool forceMoveTo)
 {
-    VPath other;
-
-    other.d = new VPathData;
-    other.d->m_points = d->m_points;
-    other.d->m_elements = d->m_elements;
-    other.d->m_segments = d->m_segments;
-    other.d->ref.setOwned();
-    return other;
-}
-
-VPath::~VPath()
-{
-    if (!d->ref.deref())
-        cleanUp(d);
-}
+    int point_count = 0;
+    VPointF pts[15];
+    VPointF curve_start = curvesForArc(rect, startAngle, sweepLength, pts, &point_count);
 
-VPath::VPath()
-    : d(const_cast<VPathData*>(&shared_empty))
-{
+    if (isEmpty() || forceMoveTo) {
+        moveTo(curve_start);
+    } else {
+        lineTo(curve_start);
+    }
+    for (int i=0; i<point_count; i+=3) {
+        cubicTo(pts[i], pts[i+1], pts[i+2]);
+    }
 }
 
-VPath::VPath(const VPath &other)
+void VPath::VPathData::addCircle(float cx, float cy, float radius, VPath::Direction dir)
 {
-    d = other.d;
-    d->ref.ref();
+    addOval(VRectF(cx-radius, cy-radius, 2*radius, 2*radius) , dir);
 }
 
-VPath::VPath(VPath &&other): d(other.d)
+void VPath::VPathData::addOval(const VRectF &rect, VPath::Direction dir)
 {
-    other.d = const_cast<VPathData*>(&shared_empty);
-}
+    if (rect.isNull()) return;
 
-VPath &VPath::operator=(const VPath &other)
-{
-    other.d->ref.ref();
-    if (!d->ref.deref())
-        cleanUp(d);
+    float x = rect.x();
+    float y = rect.y();
 
-    d = other.d;
-    return *this;
-}
+    float w = rect.width();
+    float w2 = rect.width() / 2;
+    float w2k = w2 * PATH_KAPPA;
 
-inline VPath &VPath::operator=(VPath &&other)
-{
-    if (!d->ref.deref())
-        cleanUp(d);
-    d = other.d;
-    other.d = const_cast<VPathData*>(&shared_empty);
-    return *this;
-}
+    float h = rect.height();
+    float h2 = rect.height() / 2;
+    float h2k = h2 * PATH_KAPPA;
 
-bool VPath::isEmpty()const
-{
-    return d->m_elements.empty();
-}
 
-void VPath::close()
-{
-    if (isEmpty()) return;
-    detach();
-    d->close();
+    if (dir == VPath::Direction::CW) {
+       // moveto 12 o'clock.
+       moveTo(VPointF(x+w2, y));
+       // 12 -> 3 o'clock
+       cubicTo(VPointF(x + w2 + w2k, y), VPointF(x + w, y + h2 - h2k), VPointF(x + w, y + h2));
+       // 3 -> 6 o'clock
+       cubicTo(VPointF(x + w, y + h2 + h2k), VPointF(x + w2 + w2k, y + h), VPointF(x + w2, y + h));
+       // 6 -> 9 o'clock
+       cubicTo(VPointF(x + w2 - w2k, y + h), VPointF(x, y + h2 + h2k), VPointF(x , y + h2));
+       // 9 -> 12 o'clock
+       cubicTo(VPointF(x, y + h2 - h2k), VPointF(x + w2 - w2k, y), VPointF(x + w2, y));
+    } else {
+       // moveto 12 o'clock.
+       moveTo(VPointF(x+w2, y));
+       // 12 -> 9 o'clock
+       cubicTo(VPointF(x + w2 - w2k, y), VPointF(x, y + h2 - h2k), VPointF(x , y + h2));
+       // 9 -> 6 o'clock
+       cubicTo(VPointF(x, y + h2 + h2k), VPointF(x + w2 - w2k, y + h), VPointF(x + w2, y + h));
+       // 6 -> 3 o'clock
+       cubicTo(VPointF(x + w2 + w2k, y + h), VPointF(x + w, y + h2 + h2k), VPointF(x + w, y + h2));
+       // 3 -> 12 o'clock
+       cubicTo(VPointF(x + w, y + h2 - h2k), VPointF(x + w2 + w2k, y), VPointF(x+w2, y));
+    }
 }
 
-void VPath::reset()
+void VPath::VPathData::addRect(const VRectF &rect, VPath::Direction dir)
 {
-    if (isEmpty()) return;
-    detach();
-    d->reset();
-}
+    if (rect.isNull()) return;
 
-void VPath::moveTo(const VPointF &p)
-{
-    detach();
-    d->moveTo(p);
-}
+    float x = rect.x();
+    float y = rect.y();
+    float w = rect.width();
+    float h = rect.height();
 
-void VPath::lineTo(const VPointF &p)
-{
-    detach();
-    d->lineTo(p);
+    if (dir == VPath::Direction::CW) {
+      moveTo(VPointF(x + w, y));
+      lineTo(VPointF(x + w, y + h));
+      lineTo(VPointF(x , y + h));
+      lineTo(VPointF(x , y));
+      close();
+    } else {
+      moveTo(VPointF(x + w, y));
+      lineTo(VPointF(x , y));
+      lineTo(VPointF(x , y + h));
+      lineTo(VPointF(x + w, y + h));
+      close();
+    }
 }
 
-void VPath::cubicTo(const VPointF &c1, const VPointF &c2, const VPointF &e)
+void VPath::VPathData::addRoundRect(const VRectF &rect, float rx, float ry, VPath::Direction dir)
 {
-    detach();
-    d->cubicTo(c1, c2, e);
-}
+    if (vCompare(rx, 0.f) || vCompare(ry, 0.f)) {
+        addRect(rect, dir);
+        return;
+    }
 
-void VPath::reserve(int num_elm)
-{
-    detach();
-    d->m_elements.reserve(num_elm);
-    d->m_points.reserve(num_elm);
-}
+    float x = rect.x();
+    float y = rect.y();
+    float w = rect.width();
+    float h = rect.height();
+    // clamp the rx and ry radius value.
+    rx = 2*rx;
+    ry = 2*ry;
+    if (rx > w) rx = w;
+    if (ry > h) ry = h;
 
-const std::vector<VPath::Element> &VPath::elements() const
-{
-    return d->m_elements;
-}
-const std::vector<VPointF> &VPath::points() const
-{
-    return d->m_points;
-}
-int VPath::segments() const
-{
-    return d->m_segments + 1;
+     if (dir == VPath::Direction::CW) {
+         moveTo(VPointF(x + w, y + ry/2.f));
+         arcTo(VRectF(x + w - rx, y + h - ry, rx, ry), 0 , -90, false);
+         arcTo(VRectF(x, y + h - ry, rx, ry), -90 , -90, false);
+         arcTo(VRectF(x, y, rx, ry), -180 , -90, false);
+         arcTo(VRectF(x + w - rx, y, rx, ry), -270 , -90, false);
+         close();
+     } else {
+         moveTo(VPointF(x + w, y + ry/2.f));
+         arcTo(VRectF(x + w - rx, y , rx, ry), 0 , 90, false);
+         arcTo(VRectF(x, y, rx, ry), 90 , 90, false);
+         arcTo(VRectF(x, y + h - ry, rx, ry), 180 , 90, false);
+         arcTo(VRectF(x + w - rx, y + h - ry, rx, ry), 270 , 90, false);
+         close();
+     }
 }
 
-#define PATH_KAPPA 0.5522847498
-
 static float tForArcAngle(float angle);
 void findEllipseCoords(const VRectF &r, float angle, float length,
                        VPointF* startPoint, VPointF *endPoint)
@@ -454,132 +439,6 @@ curvesForArc(const VRectF &rect, float startAngle, float sweepLength,
     return startPoint;
 }
 
-void VPath::arcTo(const VRectF &rect, float startAngle, float sweepLength, bool forceMoveTo)
-{
-    detach();
-
-    int point_count = 0;
-    VPointF pts[15];
-    VPointF curve_start = curvesForArc(rect, startAngle, sweepLength, pts, &point_count);
-
-    if (isEmpty() || forceMoveTo) {
-        d->moveTo(curve_start);
-    } else {
-        d->lineTo(curve_start);
-    }
-    for (int i=0; i<point_count; i+=3) {
-        d->cubicTo(pts[i], pts[i+1], pts[i+2]);
-    }
-}
-
-void VPath::addCircle(float cx, float cy, float radius, VPath::Direction dir)
-{
-    addOval(VRectF(cx-radius, cy-radius, 2*radius, 2*radius) , dir);
-}
-
-void VPath::addOval(const VRectF &rect, VPath::Direction dir)
-{
-    if (rect.isNull()) return;
-
-    detach();
-
-    float x = rect.x();
-    float y = rect.y();
-
-    float w = rect.width();
-    float w2 = rect.width() / 2;
-    float w2k = w2 * PATH_KAPPA;
-
-    float h = rect.height();
-    float h2 = rect.height() / 2;
-    float h2k = h2 * PATH_KAPPA;
-
-
-    if (dir == VPath::Direction::CW) {
-       // moveto 12 o'clock.
-       d->moveTo(VPointF(x+w2, y));
-       // 12 -> 3 o'clock
-       d->cubicTo(VPointF(x + w2 + w2k, y), VPointF(x + w, y + h2 - h2k), VPointF(x + w, y + h2));
-       // 3 -> 6 o'clock
-       d->cubicTo(VPointF(x + w, y + h2 + h2k), VPointF(x + w2 + w2k, y + h), VPointF(x + w2, y + h));
-       // 6 -> 9 o'clock
-       d->cubicTo(VPointF(x + w2 - w2k, y + h), VPointF(x, y + h2 + h2k), VPointF(x , y + h2));
-       // 9 -> 12 o'clock
-       d->cubicTo(VPointF(x, y + h2 - h2k), VPointF(x + w2 - w2k, y), VPointF(x + w2, y));
-    } else {
-       // moveto 12 o'clock.
-       d->moveTo(VPointF(x+w2, y));
-       // 12 -> 9 o'clock
-       d->cubicTo(VPointF(x + w2 - w2k, y), VPointF(x, y + h2 - h2k), VPointF(x , y + h2));
-       // 9 -> 6 o'clock
-       d->cubicTo(VPointF(x, y + h2 + h2k), VPointF(x + w2 - w2k, y + h), VPointF(x + w2, y + h));
-       // 6 -> 3 o'clock
-       d->cubicTo(VPointF(x + w2 + w2k, y + h), VPointF(x + w, y + h2 + h2k), VPointF(x + w, y + h2));
-       // 3 -> 12 o'clock
-       d->cubicTo(VPointF(x + w, y + h2 - h2k), VPointF(x + w2 + w2k, y), VPointF(x+w2, y));
-    }
-}
-
-void VPath::addRect(const VRectF &rect, VPath::Direction dir)
-{
-    if (rect.isNull()) return;
-
-    detach();
-
-    float x = rect.x();
-    float y = rect.y();
-    float w = rect.width();
-    float h = rect.height();
-
-    if (dir == VPath::Direction::CW) {
-      moveTo(VPointF(x + w, y));
-      lineTo(VPointF(x + w, y + h));
-      lineTo(VPointF(x , y + h));
-      lineTo(VPointF(x , y));
-      close();
-    } else {
-       moveTo(VPointF(x + w, y));
-       lineTo(VPointF(x , y));
-       lineTo(VPointF(x , y + h));
-       lineTo(VPointF(x + w, y + h));
-       close();
-    }
-}
-
-void VPath::addRoundRect(const VRectF &rect, float rx, float ry, VPath::Direction dir)
-{
-    if (vCompare(rx, 0.f) || vCompare(ry, 0.f)) {
-        addRect(rect, dir);
-        return;
-    }
-
-    float x = rect.x();
-    float y = rect.y();
-    float w = rect.width();
-    float h = rect.height();
-    // clamp the rx and ry radius value.
-    rx = 2*rx;
-    ry = 2*ry;
-    if (rx > w) rx = w;
-    if (ry > h) ry = h;
-
-     if (dir == VPath::Direction::CW) {
-         moveTo(VPointF(x + w, y + ry/2.f));
-         arcTo(VRectF(x + w - rx, y + h - ry, rx, ry), 0 , -90, false);
-         arcTo(VRectF(x, y + h - ry, rx, ry), -90 , -90, false);
-         arcTo(VRectF(x, y, rx, ry), -180 , -90, false);
-         arcTo(VRectF(x + w - rx, y, rx, ry), -270 , -90, false);
-         close();
-     } else {
-         moveTo(VPointF(x + w, y + ry/2.f));
-         arcTo(VRectF(x + w - rx, y , rx, ry), 0 , 90, false);
-         arcTo(VRectF(x, y, rx, ry), 90 , 90, false);
-         arcTo(VRectF(x, y + h - ry, rx, ry), 180 , 90, false);
-         arcTo(VRectF(x + w - rx, y + h - ry, rx, ry), 270 , 90, false);
-         close();
-     }
-}
-
 void VPath::addPolystarStar(float startAngle, float cx, float cy, float points,
                              float innerRadius, float outerRadius,
                              float innerRoundness, float outerRoundness,
@@ -617,7 +476,9 @@ void VPath::addPolystarStar(float startAngle, float cx, float cy, float points,
         currentAngle += halfAnglePerPoint * angleDir;
    }
 
-   moveTo(VPointF(x + cx, y + cy));
+   VPathData &ref = d.write();
+
+   ref.moveTo(VPointF(x + cx, y + cy));
 
    for (int i = 0; i < numPoints; i++) {
         float radius = longSegment ? outerRadius : innerRadius;
@@ -634,7 +495,7 @@ void VPath::addPolystarStar(float startAngle, float cx, float cy, float points,
         y = (float) (radius * sin(currentAngle));
 
         if (innerRoundness == 0 && outerRoundness == 0) {
-             lineTo(VPointF(x + cx, y + cy));
+             ref.lineTo(VPointF(x + cx, y + cy));
         } else {
              float cp1Theta = (float) (atan2(previousY, previousX) - M_PI / 2.0 * angleDir);
              float cp1Dx = (float) cos(cp1Theta);
@@ -661,9 +522,9 @@ void VPath::addPolystarStar(float startAngle, float cx, float cy, float points,
                   cp2y *= partialPointAmount;
              }
 
-             cubicTo(VPointF(previousX - cp1x + cx, previousY - cp1y + cy),
-                     VPointF(x + cp2x + cx, y + cp2y + cy),
-                     VPointF(x + cx, y + cy));
+             ref.cubicTo(VPointF(previousX - cp1x + cx, previousY - cp1y + cy),
+                         VPointF(x + cp2x + cx, y + cp2y + cy),
+                         VPointF(x + cx, y + cy));
         }
 
         currentAngle += dTheta * angleDir;
@@ -729,11 +590,4 @@ void VPath::addPolystarPolygon(float startAngle, float cx, float cy, float point
    close();
 }
 
-void VPath::transform(const VMatrix &m)
-{
-    if (isEmpty()) return;
-    detach();
-    d->transform(m);
-}
-
 V_END_NAMESPACE
index 2fe0b9d..68acc52 100644 (file)
@@ -4,6 +4,7 @@
 #include "vrect.h"
 #include "vmatrix.h"
 #include<vector>
+#include<vcowptr.h>
 
 V_BEGIN_NAMESPACE
 
@@ -22,24 +23,18 @@ public:
         CubicTo,
         Close
     };
-    ~VPath();
-    VPath();
-    VPath(const VPath &path);
-    VPath(VPath &&other);
-    VPath &operator=(const VPath &);
-    VPath &operator=(VPath &&other);
     bool isEmpty()const;
     void moveTo(const VPointF &p);
-    inline void moveTo(float x, float y);
+    void moveTo(float x, float y);
     void lineTo(const VPointF &p);
-    inline void lineTo(float x, float y);
+    void lineTo(float x, float y);
     void cubicTo(const VPointF &c1, const VPointF &c2, const VPointF &e);
-    inline void cubicTo(float c1x, float c1y, float c2x, float c2y, float ex, float ey);
+    void cubicTo(float c1x, float c1y, float c2x, float c2y, float ex, float ey);
     void arcTo(const VRectF &rect, float startAngle, float sweepLength, bool forceMoveTo);
     void close();
     void reset();
     void reserve(int num_elm);
-
+    int segments() const;
     void addCircle(float cx, float cy, float radius, VPath::Direction dir = Direction::CW);
     void addOval(const VRectF &rect, VPath::Direction dir = Direction::CW);
     void addRoundRect(const VRectF &rect, float rx, float ry, VPath::Direction dir = Direction::CW);
@@ -51,19 +46,80 @@ public:
     void addPolystarPolygon(float startAngle, float cx, float cy, float points,
                             float radius, float roundness,
                             VPath::Direction dir = Direction::CW);
-
     void transform(const VMatrix &m);
     const std::vector<VPath::Element> &elements() const;
     const std::vector<VPointF> &points() const;
 private:
-    friend class VRaster;
-    int segments() const;
-    VPath copy() const;
-    void detach();
-    void cleanUp(VPathData *x);
-    VPathData *d;
+    struct VPathData {
+        VPathData();
+        VPathData(const VPathData &o);
+        bool isEmpty() const { return m_elements.empty();}
+        void moveTo(const VPointF &pt);
+        void lineTo(const VPointF &pt);
+        void cubicTo(const VPointF &c1, const VPointF &c2, const VPointF &e);
+        void close();
+        void reset();
+        void reserve(int num_elm);
+        void checkNewSegment();
+        int  segments() const;
+        void transform(const VMatrix &m);
+        void addRoundRect(const VRectF &, float, float, VPath::Direction);
+        void addRect(const VRectF &, VPath::Direction);
+        void arcTo(const VRectF&, float, float, bool);
+        void addCircle(float, float, float, VPath::Direction);
+        void addOval(const VRectF &, VPath::Direction);
+        const std::vector<VPath::Element> &elements() const { return m_elements;}
+        const std::vector<VPointF> &points() const {return m_points;}
+        std::vector<VPointF>         m_points;
+        std::vector<VPath::Element>  m_elements;
+        int                          m_segments;
+        VPointF                      mStartPoint;
+        bool                         mNewSegment;
+    };
+
+    vcow_ptr<VPathData> d;
 };
 
+inline bool VPath::isEmpty()const
+{
+    return d->isEmpty();
+}
+
+inline void VPath::moveTo(const VPointF &p)
+{
+    d.write().moveTo(p);
+}
+
+inline void VPath::lineTo(const VPointF &p)
+{
+    d.write().lineTo(p);
+}
+
+inline void VPath::close()
+{
+    d.write().close();
+}
+
+inline void VPath::reset()
+{
+    d.write().reset();
+}
+
+inline void VPath::reserve(int num_elm)
+{
+    d.write().reserve(num_elm);
+}
+
+inline int VPath::segments() const
+{
+    return d->segments();
+}
+
+inline void VPath::cubicTo(const VPointF &c1, const VPointF &c2, const VPointF &e)
+{
+    d.write().cubicTo(c1, c2, e);
+}
+
 inline void VPath::lineTo(float x, float y)
 {
     lineTo(VPointF(x,y));
@@ -76,7 +132,47 @@ inline void VPath::moveTo(float x, float y)
 
 inline void VPath::cubicTo(float c1x, float c1y, float c2x, float c2y, float ex, float ey)
 {
-      cubicTo(VPointF(c1x, c1y), VPointF(c2x, c2y), VPointF(ex, ey));
+    cubicTo(VPointF(c1x, c1y), VPointF(c2x, c2y), VPointF(ex, ey));
+}
+
+inline void VPath::transform(const VMatrix &m)
+{
+    d.write().transform(m);
+}
+
+inline void VPath::arcTo(const VRectF &rect, float startAngle, float sweepLength, bool forceMoveTo)
+{
+    d.write().arcTo(rect, startAngle, sweepLength, forceMoveTo);
+}
+
+inline void VPath::addRect(const VRectF &rect, VPath::Direction dir)
+{
+    d.write().addRect(rect, dir);
+}
+
+inline void VPath::addRoundRect(const VRectF &rect, float rx, float ry, VPath::Direction dir)
+{
+   d.write().addRoundRect(rect, rx, ry, dir);
+}
+
+inline void VPath::addCircle(float cx, float cy, float radius, VPath::Direction dir)
+{
+    d.write().addCircle(cx, cy, radius, dir);
+}
+
+inline void VPath::addOval(const VRectF &rect, VPath::Direction dir)
+{
+    d.write().addOval(rect, dir);
+}
+
+inline const std::vector<VPath::Element> &VPath::elements() const
+{
+    return d->elements();
+}
+
+inline const std::vector<VPointF> &VPath::points() const
+{
+    return d->points();
 }
 
 V_END_NAMESPACE