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