From d7a822b3e59cec45a1ac12657f87ddd3c61d3d48 Mon Sep 17 00:00:00 2001 From: X-Ryl669 Date: Tue, 15 Mar 2022 18:40:17 +0100 Subject: [PATCH] Add support for vector class that's not using exponential growth to limit lottie memory usage. --- CMakeLists.txt | 1 + cmake/config.h.in | 6 + example/lottieview.h | 1 + inc/rlottie.h | 6 +- meson.build | 4 + meson_options.txt | 5 +- src/lottie/JSON | 2 +- src/lottie/lottieanimation.cpp | 9 +- src/lottie/lottiefiltermodel.h | 4 +- src/lottie/lottieitem.cpp | 26 +- src/lottie/lottieitem.h | 42 +-- src/lottie/lottieitem_capi.cpp | 4 +- src/lottie/lottiekeypath.h | 4 +- src/lottie/lottiemodel.cpp | 9 +- src/lottie/lottiemodel.h | 28 +- src/lottie/lottieparser.cpp | 14 +- src/lottie/lottieroparser.cpp | 14 +- src/vector/varenaalloc.h | 2 +- src/vector/vbrush.h | 4 +- src/vector/vdasher.cpp | 4 +- src/vector/vdrawable.cpp | 2 +- src/vector/vdrawable.h | 5 +- src/vector/vpath.cpp | 7 +- src/vector/vpath.h | 18 +- src/vector/vraster.cpp | 8 +- src/vector/vrle.cpp | 6 +- src/vector/vrle.h | 4 +- src/vector/vvector.h | 457 +++++++++++++++++++++++++++++++++ 28 files changed, 583 insertions(+), 113 deletions(-) create mode 100644 src/vector/vvector.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 4108949e..2cbd91e6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -35,6 +35,7 @@ option(LOTTIE_TEST "Build LOTTIE AUTOTESTS" OFF) option(LOTTIE_CCACHE "Enable LOTTIE ccache SUPPORT" OFF) option(LOTTIE_ASAN "Compile with asan" OFF) option(LOTTIE_JSON "Use readonly JSON parser" OFF) +option(LOTTIE_MEMSHRINK "Minimize memory but increase parsing time" OFF) option(LOTTIE_EXAMPLE "Build examples" ON) set(LOTTIE_MODULE_PATH "${CMAKE_SHARED_LIBRARY_PREFIX}rlottie-image-loader${CMAKE_SHARED_LIBRARY_SUFFIX}" diff --git a/cmake/config.h.in b/cmake/config.h.in index 89ff483e..5f3f1a9d 100644 --- a/cmake/config.h.in +++ b/cmake/config.h.in @@ -23,3 +23,9 @@ #ifdef LOTTIE_JSON #define LOTTIE_JSON_SUPPORT #endif + +#cmakedefine LOTTIE_MEMSHRINK + +#ifdef LOTTIE_MEMSHRINK +#define LOTTIE_MEMSHRINK_SUPPORT +#endif \ No newline at end of file diff --git a/example/lottieview.h b/example/lottieview.h index ca8de7e1..4e48eebc 100644 --- a/example/lottieview.h +++ b/example/lottieview.h @@ -41,6 +41,7 @@ #include #include #include +#include class RenderStrategy { public: diff --git a/inc/rlottie.h b/inc/rlottie.h index 8e76e073..6d795c94 100644 --- a/inc/rlottie.h +++ b/inc/rlottie.h @@ -24,8 +24,8 @@ #define _RLOTTIE_H_ #include -#include #include +#include "../src/vector/vvector.h" #if defined _WIN32 || defined __CYGWIN__ #ifdef RLOTTIE_BUILD @@ -255,7 +255,7 @@ class RLOTTIE_API Surface { }mDrawArea; }; -using MarkerList = std::vector>; +using MarkerList = VVector>; /** * @brief https://helpx.adobe.com/after-effects/using/layer-markers-composition-markers.html * Markers exported form AE are used to describe a segmnet of an animation {comment/tag , startFrame, endFrame} @@ -263,7 +263,7 @@ using MarkerList = std::vector>; * start frame and duration of that segment. */ -using LayerInfoList = std::vector>; +using LayerInfoList = VVector>; using ColorFilter = std::function; diff --git a/meson.build b/meson.build index afbda37b..5f5daaad 100644 --- a/meson.build +++ b/meson.build @@ -58,6 +58,10 @@ if get_option('json') == true config_h.set10('LOTTIE_JSON_SUPPORT', true) endif +if get_option('memshrink') == true + config_h.set10('LOTTIE_MEMSHRINK_SUPPORT', true) +endif + configure_file( output: 'config.h', diff --git a/meson_options.txt b/meson_options.txt index d46fad45..d8e4cb12 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -47,4 +47,7 @@ option('json', value: false, description: 'Use readonly JSON parser instead of RapidJSON') - +option('memshrink', + type: 'boolean', + value: false, + description: 'Minimize memory usage but increase parsing time') diff --git a/src/lottie/JSON b/src/lottie/JSON index d9b64a9f..d9806adc 160000 --- a/src/lottie/JSON +++ b/src/lottie/JSON @@ -1 +1 @@ -Subproject commit d9b64a9f6dda68f7da5e7fb8dedf173bdf0a8440 +Subproject commit d9806adc73663c38e1d855d722621938f957e841 diff --git a/src/lottie/lottieanimation.cpp b/src/lottie/lottieanimation.cpp index 92f7a87e..2ec336d3 100644 --- a/src/lottie/lottieanimation.cpp +++ b/src/lottie/lottieanimation.cpp @@ -25,6 +25,7 @@ #include "rlottie.h" #include +#include "vvector.h" using namespace rlottie; using namespace rlottie::internal; @@ -185,10 +186,10 @@ void AnimationImpl::init(std::shared_ptr composition) * just waits for new task on its own queue. */ class RenderTaskScheduler { - const unsigned _count{std::thread::hardware_concurrency()}; - std::vector _threads; - std::vector> _q{_count}; - std::atomic _index{0}; + const unsigned _count{std::thread::hardware_concurrency()}; + VVector _threads; + VVector> _q{_count}; + std::atomic _index{0}; void run(unsigned i) { diff --git a/src/lottie/lottiefiltermodel.h b/src/lottie/lottiefiltermodel.h index 4f78c11a..a2ed8e97 100644 --- a/src/lottie/lottiefiltermodel.h +++ b/src/lottie/lottiefiltermodel.h @@ -307,7 +307,7 @@ class FilterData { return *result; } std::bitset<32> mBitset{0}; - std::vector mFilters; + VVector mFilters; }; template @@ -366,7 +366,7 @@ class Filter : public FilterBase { CapStyle capStyle() const { return this->model()->capStyle(); } JoinStyle joinStyle() const { return this->model()->joinStyle(); } bool hasDashInfo() const { return this->model()->hasDashInfo(); } - void getDashInfo(int frameNo, std::vector& result) const + void getDashInfo(int frameNo, VVector& result) const { return this->model()->getDashInfo(frameNo, result); } diff --git a/src/lottie/lottieitem.cpp b/src/lottie/lottieitem.cpp index 126c7fc4..ba2849f1 100644 --- a/src/lottie/lottieitem.cpp +++ b/src/lottie/lottieitem.cpp @@ -878,7 +878,7 @@ renderer::ShapeLayer::ShapeLayer(model::Layer *layerData, { mRoot->addChildren(layerData, allocator); - std::vector list; + VVector list; mRoot->processPaintItems(list); if (layerData->hasPathOperator()) { @@ -1044,14 +1044,14 @@ void renderer::Group::applyTrim() } } -void renderer::Group::renderList(std::vector &list) +void renderer::Group::renderList(VVector &list) { for (const auto &content : mContents) { content->renderList(list); } } -void renderer::Group::processPaintItems(std::vector &list) +void renderer::Group::processPaintItems(VVector &list) { size_t curOpCount = list.size(); for (auto i = mContents.rbegin(); i != mContents.rend(); ++i) { @@ -1078,7 +1078,7 @@ void renderer::Group::processPaintItems(std::vector &list) } } -void renderer::Group::processTrimItems(std::vector &list) +void renderer::Group::processTrimItems(VVector &list) { size_t curOpCount = list.size(); for (auto i = mContents.rbegin(); i != mContents.rend(); ++i) { @@ -1260,7 +1260,7 @@ void renderer::Paint::updateRenderNode() } } -void renderer::Paint::renderList(std::vector &list) +void renderer::Paint::renderList(VVector &list) { if (mRenderNodeUpdate) { updateRenderNode(); @@ -1278,11 +1278,11 @@ void renderer::Paint::renderList(std::vector &list) if (mContentToRender) list.push_back(&mDrawable); } -void renderer::Paint::addPathItems(std::vector &list, +void renderer::Paint::addPathItems(VVector &list, size_t startOffset) { - std::copy(list.begin() + startOffset, list.end(), - back_inserter(mPathItems)); + mPathItems.reserve(mPathItems.size() + list.end() - list.begin() - startOffset); + std::copy(list.begin() + startOffset, list.end(), std::back_inserter(mPathItems)); } renderer::Fill::Fill(model::Fill *data) @@ -1334,7 +1334,7 @@ renderer::Stroke::Stroke(model::Stroke *data) } } -static vthread_local std::vector Dash_Vector; +static vthread_local VVector Dash_Vector; bool renderer::Stroke::updateContent(int frameNo, const VMatrix &matrix, float alpha) @@ -1479,11 +1479,11 @@ void renderer::Trim::update() } } -void renderer::Trim::addPathItems(std::vector &list, +void renderer::Trim::addPathItems(VVector &list, size_t startOffset) { - std::copy(list.begin() + startOffset, list.end(), - back_inserter(mPathItems)); + mPathItems.reserve(mPathItems.size() + list.end() - list.begin() - startOffset); + std::copy(list.begin() + startOffset, list.end(), std::back_inserter(mPathItems)); } renderer::Repeater::Repeater(model::Repeater *data, VArenaAlloc *allocator) @@ -1537,7 +1537,7 @@ void renderer::Repeater::update(int frameNo, const VMatrix &parentMatrix, } } -void renderer::Repeater::renderList(std::vector &list) +void renderer::Repeater::renderList(VVector &list) { if (mHidden) return; return renderer::Group::renderList(list); diff --git a/src/lottie/lottieitem.h b/src/lottie/lottieitem.h index b9dc2c5b..ecc61149 100644 --- a/src/lottie/lottieitem.h +++ b/src/lottie/lottieitem.h @@ -108,7 +108,7 @@ class SurfaceCache { void release_surface(VBitmap &surface) { mCache.push_back(surface); } private: - std::vector mCache; + VVector mCache; }; class Drawable final : public VDrawable { @@ -128,9 +128,9 @@ class Drawable final : public VDrawable { struct CApiData { CApiData(); LOTLayerNode mLayer; - std::vector mMasks; - std::vector mLayers; - std::vector mCNodeList; + VVector mMasks; + VVector mLayers; + VVector mCNodeList; }; class Clipper { @@ -179,7 +179,7 @@ class LayerMask { void preprocess(const VRect &clip); public: - std::vector mMasks; + VVector mMasks; VRle mRle; bool mStatic{true}; bool mDirty{true}; @@ -238,9 +238,9 @@ class Layer { bool visible() const; virtual void buildLayerNode(); LOTLayerNode & clayer() { return mCApiData->mLayer; } - std::vector &clayers() { return mCApiData->mLayers; } - std::vector & cmasks() { return mCApiData->mMasks; } - std::vector & cnodes() { return mCApiData->mCNodeList; } + VVector &clayers() { return mCApiData->mLayers; } + VVector & cmasks() { return mCApiData->mMasks; } + VVector & cnodes() { return mCApiData->mCNodeList; } const char * name() const { return mLayerData->name(); } virtual bool resolveKeyPath(LOTKeyPath &keyPath, uint32_t depth, LOTVariant &value); @@ -293,7 +293,7 @@ class CompLayer final : public Layer { SurfaceCache &cache); private: - std::vector mLayers; + VVector mLayers; std::unique_ptr mClipper; }; @@ -326,7 +326,7 @@ class ShapeLayer final : public Layer { protected: void preprocessStage(const VRect &clip) final; void updateContent() final; - std::vector mDrawableList; + VVector mDrawableList; Group * mRoot{nullptr}; }; @@ -363,7 +363,7 @@ class Object { Object & operator=(Object &&) noexcept = delete; virtual void update(int frameNo, const VMatrix &parentMatrix, float parentAlpha, const DirtyFlag &flag) = 0; - virtual void renderList(std::vector &) {} + virtual void renderList(VVector &) {} virtual bool resolveKeyPath(LOTKeyPath &, uint32_t, LOTVariant &) { return false; @@ -380,9 +380,9 @@ class Group : public Object { void update(int frameNo, const VMatrix &parentMatrix, float parentAlpha, const DirtyFlag &flag) override; void applyTrim(); - void processTrimItems(std::vector &list); - void processPaintItems(std::vector &list); - void renderList(std::vector &list) override; + void processTrimItems(VVector &list); + void processPaintItems(VVector &list); + void renderList(VVector &list) override; Object::Type type() const final { return Object::Type::Group; } const VMatrix &matrix() const { return mMatrix; } const char * name() const @@ -394,7 +394,7 @@ class Group : public Object { LOTVariant &value) override; protected: - std::vector mContents; + VVector mContents; VMatrix mMatrix; private: @@ -506,10 +506,10 @@ class Polystar final : public Shape { class Paint : public Object { public: Paint(bool staticContent); - void addPathItems(std::vector &list, size_t startOffset); + void addPathItems(VVector &list, size_t startOffset); void update(int frameNo, const VMatrix &parentMatrix, float parentAlpha, const DirtyFlag &flag) override; - void renderList(std::vector &list) final; + void renderList(VVector &list) final; Object::Type type() const final { return Object::Type::Paint; } protected: @@ -520,7 +520,7 @@ class Paint : public Object { void updateRenderNode(); protected: - std::vector mPathItems; + VVector mPathItems; Drawable mDrawable; VPath mPath; DirtyFlag mFlag; @@ -586,7 +586,7 @@ class Trim final : public Object { const DirtyFlag &flag) final; Object::Type type() const final { return Object::Type::Trim; } void update(); - void addPathItems(std::vector &list, size_t startOffset); + void addPathItems(VVector &list, size_t startOffset); private: bool pathDirty() const @@ -601,7 +601,7 @@ class Trim final : public Object { model::Trim::Segment mSegment{}; }; Cache mCache; - std::vector mPathItems; + VVector mPathItems; model::Trim * mData{nullptr}; VPathMesure mPathMesure; bool mDirty{true}; @@ -612,7 +612,7 @@ class Repeater final : public Group { explicit Repeater(model::Repeater *data, VArenaAlloc *allocator); void update(int frameNo, const VMatrix &parentMatrix, float parentAlpha, const DirtyFlag &flag) final; - void renderList(std::vector &list) final; + void renderList(VVector &list) final; private: model::Repeater *mRepeaterData{nullptr}; diff --git a/src/lottie/lottieitem_capi.cpp b/src/lottie/lottieitem_capi.cpp index 31ca54a6..4049a642 100644 --- a/src/lottie/lottieitem_capi.cpp +++ b/src/lottie/lottieitem_capi.cpp @@ -237,8 +237,8 @@ void renderer::Drawable::sync() if (mFlag & DirtyState::Path) { applyDashOp(); - const std::vector &elm = mPath.elements(); - const std::vector & pts = mPath.points(); + const VVector &elm = mPath.elements(); + const VVector & pts = mPath.points(); const float *ptPtr = reinterpret_cast(pts.data()); const char * elmPtr = reinterpret_cast(elm.data()); mCNode->mPath.elmPtr = elmPtr; diff --git a/src/lottie/lottiekeypath.h b/src/lottie/lottiekeypath.h index 2c53287e..6b3fcd81 100644 --- a/src/lottie/lottiekeypath.h +++ b/src/lottie/lottiekeypath.h @@ -24,8 +24,8 @@ #define LOTTIEKEYPATH_H #include -#include #include "vglobal.h" +#include "vvector.h" class LOTKeyPath { public: @@ -47,7 +47,7 @@ class LOTKeyPath { size_t size() const { return mKeys.size() - 1; } private: - std::vector mKeys; + VVector mKeys; }; #endif // LOTTIEKEYPATH_H diff --git a/src/lottie/lottiemodel.cpp b/src/lottie/lottiemodel.cpp index 1bca99d2..e7a677e6 100644 --- a/src/lottie/lottiemodel.cpp +++ b/src/lottie/lottiemodel.cpp @@ -62,8 +62,7 @@ class LottieRepeaterProcesser { // object before the repeater ++i; // 2. move all the children till repater to the group - std::move(obj->mChildren.begin(), i.base(), - back_inserter(content->mChildren)); + std::move(obj->mChildren.begin(), i.base(), std::back_inserter(content->mChildren)); // 3. erase the objects from the original children list obj->mChildren.erase(obj->mChildren.begin(), i.base()); @@ -201,7 +200,7 @@ VMatrix model::Transform::Data::matrix(int frameNo, bool autoOrient) const return m; } -void model::Dash::getDashInfo(int frameNo, std::vector &result) const +void model::Dash::getDashInfo(int frameNo, VVector &result) const { result.clear(); @@ -348,11 +347,11 @@ void model::Asset::loadImagePath(std::string path) if (!path.empty()) mBitmap = VImageLoader::instance().load(path.c_str()); } -std::vector model::Composition::layerInfoList() const +VVector model::Composition::layerInfoList() const { if (!mRootLayer || mRootLayer->mChildren.empty()) return {}; - std::vector result; + VVector result; result.reserve(mRootLayer->mChildren.size()); diff --git a/src/lottie/lottiemodel.h b/src/lottie/lottiemodel.h index 6530af88..5aac8ade 100644 --- a/src/lottie/lottiemodel.h +++ b/src/lottie/lottiemodel.h @@ -29,7 +29,6 @@ #include #include #include -#include #include "varenaalloc.h" #include "vbezier.h" #include "vbrush.h" @@ -38,6 +37,7 @@ #include "vpath.h" #include "vpoint.h" #include "vrect.h" +#include "vvector.h" V_USE_NAMESPACE @@ -104,7 +104,7 @@ inline const Color operator*(float m, const Color &c) } struct PathData { - std::vector mPoints; + VVector mPoints; bool mClosed = false; /* "c" */ void reserve(size_t size) { mPoints.reserve(mPoints.size() + size); } static void lerp(const PathData &start, const PathData &end, float t, @@ -277,7 +277,7 @@ class KeyFrames { } public: - std::vector frames_; + VVector frames_; }; template @@ -400,7 +400,7 @@ class Property { class Path; struct PathData; struct Dash { - std::vector> mData; + VVector> mData; bool empty() const { return mData.empty(); } size_t size() const { return mData.size(); } bool isStatic() const @@ -409,7 +409,7 @@ struct Dash { if (!elm.isStatic()) return false; return true; } - void getDashInfo(int frameNo, std::vector &result) const; + void getDashInfo(int frameNo, VVector &result) const; }; class Mask { @@ -516,7 +516,7 @@ struct Asset { Type mAssetType{Type::Precomp}; bool mStatic{true}; std::string mRefId; // ref id - std::vector mLayers; + VVector mLayers; // image asset data int mWidth{0}; int mHeight{0}; @@ -528,8 +528,8 @@ class Layer; class Composition : public Object { public: Composition() : Object(Object::Type::Composition) {} - std::vector layerInfoList() const; - const std::vector &markers() const { return mMarkers; } + VVector layerInfoList() const; + const VVector &markers() const { return mMarkers; } double duration() const { return frameDuration() / frameRate(); // in second @@ -572,7 +572,7 @@ class Composition : public Object { Layer * mRootLayer{nullptr}; std::unordered_map mAssets; - std::vector mMarkers; + VVector mMarkers; VArenaAlloc mArenaAlloc{2048}; Stats mStats; }; @@ -666,7 +666,7 @@ class Group : public Object { explicit Group(Object::Type type) : Object(type) {} public: - std::vector mChildren; + VVector mChildren; Transform * mTransform{nullptr}; }; @@ -716,7 +716,7 @@ class Layer : public Group { Property mTimeRemap; /* "tm" */ Composition * mCompRef{nullptr}; Asset * mAsset{nullptr}; - std::vector mMasks; + VVector mMasks; }; Layer::Extra *extra() @@ -787,7 +787,7 @@ class Stroke : public Object { JoinStyle joinStyle() const { return mJoinStyle; } float miterLimit() const { return mMiterLimit; } bool hasDashInfo() const { return !mDash.empty(); } - void getDashInfo(int frameNo, std::vector &result) const + void getDashInfo(int frameNo, VVector &result) const { return mDash.getDashInfo(frameNo, result); } @@ -815,7 +815,7 @@ class Gradient : public Object { const Gradient::Data &g); public: - std::vector mGradient; + VVector mGradient; }; explicit Gradient(Object::Type type) : Object(type) {} inline float opacity(int frameNo) const @@ -848,7 +848,7 @@ class GradientStroke : public Gradient { JoinStyle joinStyle() const { return mJoinStyle; } float miterLimit() const { return mMiterLimit; } bool hasDashInfo() const { return !mDash.empty(); } - void getDashInfo(int frameNo, std::vector &result) const + void getDashInfo(int frameNo, VVector &result) const { return mDash.getDashInfo(frameNo, result); } diff --git a/src/lottie/lottieparser.cpp b/src/lottie/lottieparser.cpp index 83be17e3..72fc301f 100644 --- a/src/lottie/lottieparser.cpp +++ b/src/lottie/lottieparser.cpp @@ -258,7 +258,7 @@ class LottieParserImpl : public LookaheadParserHandler { void getValue(int &ival); void getValue(model::PathData &shape); void getValue(model::Gradient::Data &gradient); - void getValue(std::vector &v); + void getValue(VVector &v); void getValue(model::Repeater::Transform &); template @@ -290,10 +290,10 @@ class LottieParserImpl : public LookaheadParserHandler { private: model::ColorFilter mColorFilter; struct { - std::vector mInPoint; /* "i" */ - std::vector mOutPoint; /* "o" */ - std::vector mVertices; /* "v" */ - std::vector mResult; + VVector mInPoint; /* "i" */ + VVector mOutPoint; /* "o" */ + VVector mVertices; /* "v" */ + VVector mResult; bool mClosed{false}; void convert() @@ -367,7 +367,7 @@ class LottieParserImpl : public LookaheadParserHandler { std::shared_ptr mComposition; model::Composition * compRef{nullptr}; model::Layer * curLayerRef{nullptr}; - std::vector mLayersToUpdate; + VVector mLayersToUpdate; std::string mDirPath; void SkipOut(int depth); }; @@ -1824,7 +1824,7 @@ model::GradientStroke *LottieParserImpl::parseGStrokeObject() return obj; } -void LottieParserImpl::getValue(std::vector &v) +void LottieParserImpl::getValue(VVector &v) { EnterArray(); while (NextArrayValue()) { diff --git a/src/lottie/lottieroparser.cpp b/src/lottie/lottieroparser.cpp index 88731fdc..5e77fc8d 100644 --- a/src/lottie/lottieroparser.cpp +++ b/src/lottie/lottieroparser.cpp @@ -184,7 +184,7 @@ struct LottieParserImpl { void getValue(int &ival); void getValue(model::PathData &shape); void getValue(model::Gradient::Data &gradient); - void getValue(std::vector &v); + void getValue(VVector &v); void getValue(model::Repeater::Transform &); template @@ -216,10 +216,10 @@ struct LottieParserImpl { private: model::ColorFilter mColorFilter; struct { - std::vector mInPoint; /* "i" */ - std::vector mOutPoint; /* "o" */ - std::vector mVertices; /* "v" */ - std::vector mResult; + VVector mInPoint; /* "i" */ + VVector mOutPoint; /* "o" */ + VVector mVertices; /* "v" */ + VVector mResult; bool mClosed{false}; void convert() @@ -307,7 +307,7 @@ struct LottieParserImpl { std::shared_ptr mComposition; model::Composition * compRef{nullptr}; model::Layer * curLayerRef{nullptr}; - std::vector mLayersToUpdate; + VVector mLayersToUpdate; std::string mDirPath; void SkipOut(int depth); @@ -1691,7 +1691,7 @@ model::GradientStroke *LottieParserImpl::parseGStrokeObject() return obj; } -void LottieParserImpl::getValue(std::vector &v) +void LottieParserImpl::getValue(VVector &v) { EnterArray(); while (NextArrayValue()) { diff --git a/src/vector/varenaalloc.h b/src/vector/varenaalloc.h index ed03b53f..90272074 100644 --- a/src/vector/varenaalloc.h +++ b/src/vector/varenaalloc.h @@ -16,7 +16,7 @@ #include #include #include -#include +#include "vvector.h" // SkArenaAlloc allocates object and destroys the allocated objects when destroyed. It's designed // to minimize the number of underlying block allocations. SkArenaAlloc allocates first out of an diff --git a/src/vector/vbrush.h b/src/vector/vbrush.h index a1abbd34..08ba9f52 100644 --- a/src/vector/vbrush.h +++ b/src/vector/vbrush.h @@ -23,7 +23,7 @@ #ifndef VBRUSH_H #define VBRUSH_H -#include +#include "vvector.h" #include "vglobal.h" #include "vmatrix.h" #include "vpoint.h" @@ -32,7 +32,7 @@ V_BEGIN_NAMESPACE using VGradientStop = std::pair; -using VGradientStops = std::vector; +using VGradientStops = VVector; class VGradient { public: enum class Mode { Absolute, Relative }; diff --git a/src/vector/vdasher.cpp b/src/vector/vdasher.cpp index 759e30ac..b5ccd73f 100644 --- a/src/vector/vdasher.cpp +++ b/src/vector/vdasher.cpp @@ -194,8 +194,8 @@ void VDasher::dashHelper(const VPath &path, VPath &result) mResult = &result; mResult->reserve(path.points().size(), path.elements().size()); mIndex = 0; - const std::vector &elms = path.elements(); - const std::vector & pts = path.points(); + const VVector &elms = path.elements(); + const VVector & pts = path.points(); const VPointF * ptPtr = pts.data(); for (auto &i : elms) { diff --git a/src/vector/vdrawable.cpp b/src/vector/vdrawable.cpp index 55b1bc84..2fdab6d2 100644 --- a/src/vector/vdrawable.cpp +++ b/src/vector/vdrawable.cpp @@ -97,7 +97,7 @@ void VDrawable::setStrokeInfo(CapStyle cap, JoinStyle join, float miterLimit, mFlag |= DirtyState::Path; } -void VDrawable::setDashInfo(std::vector &dashInfo) +void VDrawable::setDashInfo(VVector &dashInfo) { assert(mStrokeInfo); assert(mType == VDrawable::Type::StrokeWithDash); diff --git a/src/vector/vdrawable.h b/src/vector/vdrawable.h index 357a69fd..b9302483 100644 --- a/src/vector/vdrawable.h +++ b/src/vector/vdrawable.h @@ -28,6 +28,7 @@ #include "vpath.h" #include "vrle.h" #include "vraster.h" +#include "vvector.h" class VDrawable { public: @@ -55,7 +56,7 @@ class VDrawable { void setBrush(const VBrush &brush) { mBrush = brush; } void setStrokeInfo(CapStyle cap, JoinStyle join, float miterLimit, float strokeWidth); - void setDashInfo(std::vector &dashInfo); + void setDashInfo(VVector &dashInfo); void preprocess(const VRect &clip); void applyDashOp(); VRle rle(); @@ -74,7 +75,7 @@ class VDrawable { }; struct StrokeWithDashInfo : public StrokeInfo{ - std::vector mDash; + VVector mDash; }; public: diff --git a/src/vector/vpath.cpp b/src/vector/vpath.cpp index d49cd009..7cf99202 100644 --- a/src/vector/vpath.cpp +++ b/src/vector/vpath.cpp @@ -22,7 +22,6 @@ #include "vpath.h" #include #include -#include #include "vbezier.h" #include "vdebug.h" #include "vline.h" @@ -695,12 +694,10 @@ void VPath::VPathData::addPath(const VPathData &path, const VMatrix *m) m_points.push_back(m->map(i)); } } else { - std::copy(path.m_points.begin(), path.m_points.end(), - back_inserter(m_points)); + std::copy(path.m_points.begin(), path.m_points.end(), std::back_inserter(m_points)); } - std::copy(path.m_elements.begin(), path.m_elements.end(), - back_inserter(m_elements)); + std::copy(path.m_elements.begin(), path.m_elements.end(), std::back_inserter(m_elements)); m_segments += segment; mLengthDirty = true; diff --git a/src/vector/vpath.h b/src/vector/vpath.h index 9273cb61..a427bd37 100644 --- a/src/vector/vpath.h +++ b/src/vector/vpath.h @@ -22,11 +22,11 @@ #ifndef VPATH_H #define VPATH_H -#include #include "vcowptr.h" #include "vmatrix.h" #include "vpoint.h" #include "vrect.h" +#include "vvector.h" V_BEGIN_NAMESPACE @@ -70,8 +70,8 @@ class VPath { void addPath(const VPath &path, const VMatrix &m); void transform(const VMatrix &m); float length() const; - const std::vector &elements() const; - const std::vector & points() const; + const VVector &elements() const; + const VVector & points() const; void clone(const VPath &srcPath); bool unique() const { return d.unique();} size_t refCount() const { return d.refCount();} @@ -105,13 +105,13 @@ class VPath { VPath::Direction dir = Direction::CW); void addPath(const VPathData &path, const VMatrix *m = nullptr); void clone(const VPath::VPathData &o) { *this = o;} - const std::vector &elements() const + const VVector &elements() const { return m_elements; } - const std::vector &points() const { return m_points; } - std::vector m_points; - std::vector m_elements; + const VVector &points() const { return m_points; } + VVector m_points; + VVector m_elements; size_t m_segments; VPointF mStartPoint; mutable float mLength{0}; @@ -265,12 +265,12 @@ inline void VPath::addPath(const VPath &path, const VMatrix &m) d.write().addPath(path.d.read(), &m); } -inline const std::vector &VPath::elements() const +inline const VVector &VPath::elements() const { return d->elements(); } -inline const std::vector &VPath::points() const +inline const VVector &VPath::points() const { return d->points(); } diff --git a/src/vector/vraster.cpp b/src/vector/vraster.cpp index fdf66fb0..ed0fdd80 100644 --- a/src/vector/vraster.cpp +++ b/src/vector/vraster.cpp @@ -104,8 +104,8 @@ void FTOutline::grow(size_t points, size_t segments) void FTOutline::convert(const VPath &path) { - const std::vector &elements = path.elements(); - const std::vector & points = path.points(); + const VVector &elements = path.elements(); + const VVector & points = path.points(); grow(points.size(), path.segments()); @@ -422,8 +422,8 @@ using VTask = std::shared_ptr; class RleTaskScheduler { const unsigned _count{std::thread::hardware_concurrency()}; - std::vector _threads; - std::vector> _q{_count}; + VVector _threads; + VVector> _q{_count}; std::atomic _index{0}; void run(unsigned i) diff --git a/src/vector/vrle.cpp b/src/vector/vrle.cpp index 753db1a1..641779c2 100644 --- a/src/vector/vrle.cpp +++ b/src/vector/vrle.cpp @@ -28,7 +28,7 @@ #include #include #include -#include +#include "vvector.h" #include "vdebug.h" #include "vglobal.h" @@ -47,11 +47,11 @@ static inline uint8_t divBy255(int x) } inline static void copy(const VRle::Span *span, size_t count, - std::vector &v) + VVector &v) { // make sure enough memory available if (v.capacity() < v.size() + count) v.reserve(v.size() + count); - std::copy(span, span + count, back_inserter(v)); + std::copy(span, span + count, std::back_inserter(v)); } void VRle::Data::addSpan(const VRle::Span *span, size_t count) diff --git a/src/vector/vrle.h b/src/vector/vrle.h index 1bbb8365..989a9ea7 100644 --- a/src/vector/vrle.h +++ b/src/vector/vrle.h @@ -23,7 +23,7 @@ #ifndef VRLE_H #define VRLE_H -#include +#include "vvector.h" #include "vcowptr.h" #include "vglobal.h" #include "vpoint.h" @@ -99,7 +99,7 @@ class VRle { void addRect(const VRect &rect); void clone(const VRle::Data &); - std::vector mSpans; + VVector mSpans; VPoint mOffset; mutable VRect mBbox; mutable bool mBboxDirty = true; diff --git a/src/vector/vvector.h b/src/vector/vvector.h new file mode 100644 index 00000000..fb29e975 --- /dev/null +++ b/src/vector/vvector.h @@ -0,0 +1,457 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All rights reserved. + + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef VVECTOR_H +#define VVECTOR_H + +#include +#include +#include +#include "config.h" + +namespace lottie +{ + +#ifndef CUSTOM_LOTTIE_ALLOCATOR + struct LottieAllocator + { + static void * alloc(size_t n) { return ::malloc(n); } + static void free(void * p) { return ::free(p); } + static void * realloc(void * p , size_t n) { return ::realloc(p, n); } + }; +#else + struct LottieAllocator + { + static void * alloc(size_t n); + static void free(void * p); + static void * realloc(void * p , size_t n); + }; +#endif + /** An memory efficient vector class that implement std::vector interface. + Unlike std::vector, this implementation does not grow exponentially, so appending to + the vector is a O(N) operation + + @param T The type stored by the vector + @param LottieAllocator An allocator that must implement: + - static void * alloc(size_t) + - static void free(void *) + - static void * realloc(void *, size_t) + */ + template + class vector + { + typedef vector this_type; + + public: + typedef T value_type; + typedef T* pointer; + typedef const T* const_pointer; + typedef T& reference; + typedef const T& const_reference; + typedef T* iterator; + typedef const T* const_iterator; + typedef std::reverse_iterator reverse_iterator; + typedef std::reverse_iterator const_reverse_iterator; + typedef size_t size_type; + typedef ptrdiff_t difference_type; + + const static bool trivial = std::is_trivially_copyable::value; + + public: + vector() noexcept : first(nullptr), last(nullptr), cap(0) { } + explicit vector(size_type n) { allocate(n); } + vector(size_type n, const value_type& value) { allocate(n, value); } + vector(const this_type& x) { allocate(x.size()); assign(x.first, x.last); } + + template + vector(InputIterator first, InputIterator last) { + allocate(last - first); assign(first, last); + } + + ~vector() { + deallocate(); + } + + this_type& operator=(const this_type& x) { + if (this != &x) { + deallocate(); + allocate(x.size()); + assign(x.first, x.last); + } + return *this; + } + + iterator begin() noexcept { return first; } + const_iterator begin() const noexcept { return first; } + const_iterator cbegin() const noexcept { return first; } + + iterator end() noexcept { return last; } + const_iterator end() const noexcept { return last; } + const_iterator cend() const noexcept { return last; } + + reverse_iterator rbegin() noexcept { return reverse_iterator(end()); } + const_reverse_iterator rbegin() const noexcept{ return const_reverse_iterator(end()); } + const_reverse_iterator crbegin() const noexcept{ return const_reverse_iterator(cend()); } + + reverse_iterator rend() noexcept { return reverse_iterator(begin()); } + const_reverse_iterator rend() const noexcept { return const_reverse_iterator(begin()); } + const_reverse_iterator crend() const noexcept { return const_reverse_iterator(crbegin()); } + + bool empty() const noexcept { return size() == 0; } + size_type size() const noexcept { return (size_t)(last - first); } + size_type capacity() const noexcept { return cap; } + + void resize(size_type n, const value_type& value) { resize_impl(n, value); } + void resize(size_type n) { resize_impl(n); } + void reserve(size_type n) { if (n > cap) reserve_impl(n); } + + pointer data() noexcept { return first; } + const_pointer data() const noexcept { return first; } + + reference operator[](size_type n) { return first[n]; } + const_reference operator[](size_type n) const { return first[n]; } + + reference front() { return *first; } + const_reference front() const { return *first; } + + reference back() { return *(last - 1); } + const_reference back() const { return *(last - 1); } + + void push_back(const value_type& value) { if (size() == cap) reserve(cap + 1); new(last++) value_type(value); } + reference push_back() { if (size() == cap) resize(cap + 1); return *(last - 1); } + void push_back(value_type&& value) { if (size() == cap) reserve(cap + 1); new(last++) value_type(std::move(value)); } + void pop_back() { free(last - 1, last != nullptr ? 1 : 0); } // Don't reduce the capacity here? + + template + reference emplace_back(Args&&... args) { + if (size() == cap) reserve(cap + 1); + new(last++) value_type(std::forward(args)...); + return back(); + } + + reference emplace_back() { + if (size() == cap) reserve(cap + 1); + new(last++) value_type(); + return back(); + } + + + // iterator erase(const_iterator position); + iterator erase(const_iterator begin, const_iterator end) { + if (begin == end) return first; + move(const_cast(end), const_cast(begin)); + free(begin, (end - begin)); + last -= (end - begin); + return first; + } + + // reverse_iterator erase(const_reverse_iterator position); + // reverse_iterator erase(const_reverse_iterator first, const_reverse_iterator last); + + void clear() noexcept { deallocate(); } + + private: + pointer first, last; + size_t cap {0}; + + void allocate(size_t nelem) { + if (!nelem) { + first = last = nullptr; + cap = 0; + return; + } + first = (value_type*)LottieAllocator::alloc(nelem * sizeof(value_type)); + last = &first[nelem]; + set(first, nelem); + cap = nelem; + } + + void allocate(size_t nelem, const_reference value) { + first = (value_type*)LottieAllocator::alloc(nelem * sizeof(value_type)); + last = &first[nelem]; + set(first, nelem, value); + cap = nelem; + } + + void assign(const_iterator b, const_iterator e) { + if ((size_t)(e - b) > size()) + e = b + size(); + copy(first, b, e - b); + } + + void deallocate() { + free(first, last - first); + LottieAllocator::free(first); + first = last = nullptr; + cap = 0; + } + + // Optimization functions for trivial types + private: + template typename std::enable_if::value>::type copy(U* dest, const U* src, size_t size) + { + memcpy(dest, src, sizeof(U) * size); + } + + template typename std::enable_if::value>::type copy(U* dest, const U* src, size_t size) + { + std::copy_n(src, size, dest); + } + + void move(pointer start, pointer into, const_pointer end = 0) { + if (!end) end = last; + for (pointer f = start; f != end; ++f, ++into) { + new(into) value_type(std::move(*f)); + f->~T(); + } + } + + template typename std::enable_if::value>::type reserve_impl(size_t size) + { + // Try to realloc first + pointer p = (pointer)LottieAllocator::realloc(first, size * sizeof(value_type)); + if (p == nullptr) return; // Should throw here + // No memcpy required, it's done in realloc + cap = size; + last = p + (last - first); + first = p; + } + + template typename std::enable_if::value>::type reserve_impl(size_t size) + { + // Can't realloc here, need the new size and copy the objects here + pointer p = (pointer)LottieAllocator::alloc(size * sizeof(value_type)); + if (p == nullptr) return; // Should throw here + move(first, p); + cap = size; + last = p + (last - first); + LottieAllocator::free(first); + first = p; + } + + template typename std::enable_if::value>::type resize_impl(size_t size) + { + size_t cur_size = (last - first); + if (cur_size == size) return; + // Try to realloc first + pointer p = (pointer)LottieAllocator::realloc(first, size * sizeof(value_type)); + if (p == nullptr) return; // Should throw here + // No memcpy required, it's done in realloc + cap = size; + if (cur_size < size) { + set(p + cur_size, size - cur_size); + } + last = p + size; + first = p; + } + + template typename std::enable_if::value>::type resize_impl(size_t size) + { + size_t cur_size = (last - first); + if (cur_size == size) return; + // Try to realloc first + pointer p = (pointer)LottieAllocator::alloc(size * sizeof(value_type)); + if (p == nullptr) return; // Should throw here + std::copy_n(std::make_move_iterator(first), std::min(cur_size, size), p); + free(first, cur_size); + cap = size; + if (cur_size < size) { + set(p + cur_size, size - cur_size); + } + last = p + size; + LottieAllocator::free(first); + first = p; + } + + template typename std::enable_if::value>::type resize_impl(size_t size, const U & value) + { + size_t cur_size = (last - first); + if (cur_size == size) return; + // Try to realloc first + pointer p = (pointer)LottieAllocator::realloc(first, size * sizeof(value_type)); + if (p == nullptr) return; // Should throw here + // No memcpy required, it's done in realloc + cap = size; + if (cur_size < size) { + set(p + cur_size, size - cur_size, value); + } + last = p + size; + first = p; + } + + template typename std::enable_if::value>::type resize_impl(size_t size, const U & value) + { + size_t cur_size = (last - first); + if (cur_size == size) return; + // Try to realloc first + pointer p = (pointer)LottieAllocator::alloc(size * sizeof(value_type)); + if (p == nullptr) return; // Should throw here + free(first + size, cur_size > size ? cur_size - size : 0); + std::copy_n(std::make_move_iterator(first), std::min(cur_size, size), p); + cap = size; + if (cur_size < size) { + set(p + cur_size, size - cur_size, value); + } + last = p + size; + LottieAllocator::free(first); + first = p; + } + + template typename std::enable_if::value>::type set(U* dest, size_t size, const U & value) + { + while (size--) + memcpy(dest++, &value, sizeof(U)); + } + + template typename std::enable_if::value>::type set(U* dest, size_t size, const U & value) + { + while (size--) + new(dest++) U(value); + } + template typename std::enable_if::value>::type set(U* dest, size_t size) + { + U u{}; + while (size--) + memcpy(dest++, &u, sizeof(U)); + } + + template typename std::enable_if::value>::type set(U* dest, size_t size) + { + while (size--) + new(dest++) U(); + } + + template typename std::enable_if::value>::type free(const U* dest, size_t size) + { + } + template typename std::enable_if::value>::type free(const U* dest, size_t size) + { + while (size--) + (dest++)->~U(); + } + + + + private: + /* Below is the missing interface from a std::vector that's not used in lottie, hence, not implemented */ + + // void set_capacity(size_type n = 0); // Revises the capacity to the user-specified value. Resizes the container to match the capacity if the requested capacity n is less than the current size. If n == npos then the capacity is reallocated (if necessary) such that capacity == size. + // void shrink_to_fit(); // C++11 function which is the same as set_capacity(). + // explicit vector(const allocator_type& allocator) noexcept; + // vector(const this_type& x, const allocator_type& allocator); + // vector(this_type&& x) noexcept; + // vector(this_type&& x, const allocator_type& allocator); + // vector(std::initializer_list ilist); + // this_type& operator=(std::initializer_list ilist); + // this_type& operator=(this_type&& x); // TODO(c++17): noexcept(allocator_traits::propagate_on_container_move_assignment::value || allocator_traits::is_always_equal::value) + + // void swap(this_type& x); // TODO(c++17): noexcept(allocator_traits::propagate_on_container_move_assignment::value || allocator_traits::is_always_equal::value) + + // void assign(size_type n, const value_type& value); + + // template + // void assign(InputIterator first, InputIterator last); + // template + // iterator emplace(const_iterator position, Args&&... args); + + // void assign(std::initializer_list ilist); + // iterator insert(const_iterator position, const value_type& value); + // iterator insert(const_iterator position, size_type n, const value_type& value); + // iterator insert(const_iterator position, value_type&& value); + // iterator insert(const_iterator position, std::initializer_list ilist); + + // template + // iterator insert(const_iterator position, InputIterator first, InputIterator last); + + // iterator erase_first(const T& value); + // iterator erase_first_unsorted(const T& value); // Same as erase, except it doesn't preserve order, but is faster because it simply copies the last item in the vector over the erased position. + // reverse_iterator erase_last(const T& value); + // reverse_iterator erase_last_unsorted(const T& value); // Same as erase, except it doesn't preserve order, but is faster because it simply copies the last item in the vector over the erased position. + // iterator erase_unsorted(const_iterator position); // Same as erase, except it doesn't preserve order, but is faster because it simply copies the last item in the vector over the erased position. + // reverse_iterator erase_unsorted(const_reverse_iterator position); + // void reset_lose_memory() noexcept; // This is a unilateral reset to an initially empty state. No destructors are called, no deallocation occurs. + + // bool validate() const noexcept; + // int validate_iterator(const_iterator i) const noexcept; + // reference at(size_type n); + // const_reference at(size_type n) const; + // void* push_back_uninitialized(); + }; + + template + inline bool operator==(const vector& a, const vector& b) + { + return ((a.size() == b.size()) && std::equal(a.begin(), a.end(), b.begin())); + } + + + template + inline bool operator!=(const vector& a, const vector& b) + { + return ((a.size() != b.size()) || !std::equal(a.begin(), a.end(), b.begin())); + } + + + template + inline bool operator<(const vector& a, const vector& b) + { + return std::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end()); + } + + + template + inline bool operator>(const vector& a, const vector& b) + { + return b < a; + } + + + template + inline bool operator<=(const vector& a, const vector& b) + { + return !(b < a); + } + + + template + inline bool operator>=(const vector& a, const vector& b) + { + return !(a < b); + } + + + template + inline void swap(vector& a, vector& b) + { + a.swap(b); + } +} + +#ifdef LOTTIE_MEMSHRINK_SUPPORT + template + using VVector = lottie::vector; +#else + #include + template + using VVector = std::vector; +#endif + +#endif