Experimental path factory pattern
diff --git a/include/rive/refcnt.hpp b/include/rive/refcnt.hpp
new file mode 100644
index 0000000..cf5792c
--- /dev/null
+++ b/include/rive/refcnt.hpp
@@ -0,0 +1,175 @@
+/*
+ * Copyright 2021 Rive
+ */
+
+#ifndef _RIVE_REFCNT_HPP_
+#define _RIVE_REFCNT_HPP_
+
+#include <assert.h>
+
+#include <atomic>
+#include <cstddef>
+#include <type_traits>
+#include <utility>
+
+/*
+ * RefCnt : Threadsafe shared pointer baseclass.
+ *
+ * The reference count is set to one in the constructor, and goes up on every call to ref(),
+ * and down on every call to unref(). When a call to unref() brings the counter to 0,
+ * delete is called on the object.
+ *
+ * rcp : template wrapper for subclasses of RefCnt, to manage assignment and parameter passing
+ * to safely keep track of shared ownership.
+ *
+ * Both of these inspired by Skia's SkRefCnt and sk_sp
+ */
+
+namespace rive {
+
+class RefCnt {
+public:
+ RefCnt() : m_refcnt(1) {}
+
+ virtual ~RefCnt() {
+ assert(this->debugging_refcnt() == 1);
+ }
+
+ void ref() const {
+ (void)m_refcnt.fetch_add(+1, std::memory_order_relaxed);
+ }
+
+ void unref() const {
+ if (1 == m_refcnt.fetch_add(-1, std::memory_order_acq_rel)) {
+#ifndef NDEBUG
+ // we restore the "1" in debug builds just to make our destructor happy
+ (void)m_refcnt.fetch_add(+1, std::memory_order_relaxed);
+#endif
+ delete this;
+ }
+ }
+
+ // not reliable in actual threaded scenarios, but useful (perhaps) for debugging
+ int32_t debugging_refcnt() const {
+ return m_refcnt.load(std::memory_order_relaxed);
+ }
+
+private:
+ mutable std::atomic<int32_t> m_refcnt;
+
+ RefCnt(RefCnt&&) = delete;
+ RefCnt(const RefCnt&) = delete;
+ RefCnt& operator=(RefCnt&&) = delete;
+ RefCnt& operator=(const RefCnt&) = delete;
+};
+
+template <typename T> static inline T* safe_ref(T* obj) {
+ if (obj) {
+ obj->ref();
+ }
+ return obj;
+}
+
+template <typename T> static inline void safe_unref(T* obj) {
+ if (obj) {
+ obj->unref();
+ }
+}
+
+// rcp : smart point template for holding subclasses of RefCnt
+
+template <typename T> class rcp {
+public:
+ constexpr rcp() : m_ptr(nullptr) {}
+ constexpr rcp(std::nullptr_t) : m_ptr(nullptr) {}
+ explicit rcp(T* ptr) : m_ptr(ptr) {}
+
+ rcp(const rcp<T>& other) : m_ptr(safe_ref(other.get())) {}
+ rcp(rcp<T>&& other) : m_ptr(other.release()) {}
+
+ /**
+ * Calls unref() on the underlying object pointer.
+ */
+ ~rcp() {
+ safe_unref(m_ptr);
+ }
+
+ rcp<T>& operator=(std::nullptr_t) { this->reset(); return *this; }
+
+ rcp<T>& operator=(const rcp<T>& other) {
+ if (this != &other) {
+ this->reset(safe_ref(other.get()));
+ }
+ return *this;
+ }
+
+ rcp<T>& operator=(rcp<T>&& other) {
+ this->reset(other.release());
+ return *this;
+ }
+
+ T& operator*() const {
+ assert(this->get() != nullptr);
+ return *this->get();
+ }
+
+ explicit operator bool() const { return this->get() != nullptr; }
+
+ T* get() const { return m_ptr; }
+ T* operator->() const { return m_ptr; }
+
+ void reset(T* ptr = nullptr) {
+ // Calling m_ptr->unref() may call this->~() or this->reset(T*).
+ // http://wg21.cmeerw.net/lwg/issue998
+ // http://wg21.cmeerw.net/lwg/issue2262
+ T* oldPtr = m_ptr;
+ m_ptr = ptr;
+ safe_unref(oldPtr);
+ }
+
+ T* release() {
+ T* ptr = m_ptr;
+ m_ptr = nullptr;
+ return ptr;
+ }
+
+ void swap(rcp<T>& other) {
+ std::swap(m_ptr, other.m_ptr);
+ }
+
+private:
+ T* m_ptr;
+};
+
+template <typename T> inline void swap(rcp<T>& a, rcp<T>& b) {
+ a.swap(b);
+}
+
+// == variants
+
+template <typename T> inline bool operator==(const rcp<T>& a, std::nullptr_t) {
+ return !a;
+}
+template <typename T> inline bool operator==(std::nullptr_t, const rcp<T>& b) {
+ return !b;
+}
+template <typename T, typename U> inline bool operator==(const rcp<T>& a, const rcp<U>& b) {
+ return a.get() == b.get();
+}
+
+// != variants
+
+template <typename T> inline bool operator!=(const rcp<T>& a, std::nullptr_t) {
+ return static_cast<bool>(a);
+}
+template <typename T> inline bool operator!=(std::nullptr_t, const rcp<T>& b) {
+ return static_cast<bool>(b);
+}
+template <typename T, typename U> inline bool operator!=(const rcp<T>& a, const rcp<U>& b) {
+ return a.get() != b.get();
+}
+
+} // namespace rive
+
+#endif
+
diff --git a/include/rive/renderer.hpp b/include/rive/renderer.hpp
index 3b4bce5..cc4584b 100644
--- a/include/rive/renderer.hpp
+++ b/include/rive/renderer.hpp
@@ -3,6 +3,7 @@
#include "rive/command_path.hpp"
#include "rive/layout.hpp"
+#include "rive/refcnt.hpp"
#include "rive/math/aabb.hpp"
#include "rive/math/mat2d.hpp"
#include "rive/shapes/paint/blend_mode.hpp"
@@ -52,18 +53,29 @@
int height() const { return m_Height; }
};
- class RenderPath : public CommandPath
- {
- public:
- RenderPath* renderPath() override { return this; }
- void addPath(CommandPath* path, const Mat2D& transform) override
- {
- addRenderPath(path->renderPath(), transform);
- }
+ class RenderPath : public RefCnt {};
- virtual void addRenderPath(RenderPath* path,
- const Mat2D& transform) = 0;
- };
+ enum class FillRule {
+ nonZero,
+ evenOdd
+ };
+
+ enum class PathVerb {
+ move,
+ line,
+ quad,
+ cubic,
+ close,
+ };
+
+ /* Returns an immutable RenderPath.
+ *
+ * ptCount is the logical number of points. Since "pts" is passed as
+ * an array of floats, there will be 2*ptCount number of floats in the array.
+ */
+ extern rcp<RenderPath> makeRenderPath(const float pts[], int ptCount,
+ const uint8_t verbs[], int verbCount,
+ FillRule);
class Renderer
{
@@ -72,8 +84,8 @@
virtual void save() = 0;
virtual void restore() = 0;
virtual void transform(const Mat2D& transform) = 0;
- virtual void drawPath(RenderPath* path, RenderPaint* paint) = 0;
- virtual void clipPath(RenderPath* path) = 0;
+ virtual void drawPath(rcp<RenderPath> path, RenderPaint* paint) = 0;
+ virtual void clipPath(rcp<RenderPath> path) = 0;
virtual void
drawImage(RenderImage* image, BlendMode value, float opacity) = 0;
@@ -168,8 +180,7 @@
}
};
- extern RenderPath* makeRenderPath();
extern RenderPaint* makeRenderPaint();
extern RenderImage* makeRenderImage();
} // namespace rive
-#endif
\ No newline at end of file
+#endif