From ff6ea4b6c4564fd6c7c1d8bdde951dc0165e4034 Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Thu, 23 Nov 2023 12:04:20 +0900 Subject: [PATCH] Loaders: Introduced a loader cache. The loader cache is applied to conserve memory. If the input data is already present in loaders, the loader cache will promptly return the active loader. This results in a lot of memory savings for the duplicated resources. binary diff: -400 bytes --- src/common/tvgInlist.h | 16 ++- src/loaders/external_jpg/tvgJpgLoader.cpp | 18 +-- src/loaders/external_jpg/tvgJpgLoader.h | 1 - src/loaders/external_png/tvgPngLoader.cpp | 32 +++--- src/loaders/external_png/tvgPngLoader.h | 3 +- src/loaders/external_webp/tvgWebpLoader.cpp | 45 ++------ src/loaders/external_webp/tvgWebpLoader.h | 2 - src/loaders/jpg/tvgJpgLoader.cpp | 20 ++-- src/loaders/jpg/tvgJpgLoader.h | 1 + src/loaders/lottie/tvgLottieLoader.cpp | 42 ++----- src/loaders/lottie/tvgLottieLoader.h | 3 +- src/loaders/png/tvgPngLoader.cpp | 38 ++---- src/loaders/png/tvgPngLoader.h | 2 - src/loaders/raw/tvgRawLoader.cpp | 14 ++- src/loaders/raw/tvgRawLoader.h | 2 +- src/loaders/svg/tvgSvgLoader.cpp | 10 +- src/loaders/tvg/tvgTvgLoader.cpp | 60 +++++----- src/loaders/tvg/tvgTvgLoader.h | 2 +- src/renderer/tvgAnimation.cpp | 8 +- src/renderer/tvgFrameModule.h | 1 + src/renderer/tvgLoadModule.h | 29 ++++- src/renderer/tvgLoader.cpp | 121 ++++++++++++++++---- src/renderer/tvgLoader.h | 7 +- src/renderer/tvgPicture.cpp | 24 +++- src/renderer/tvgPicture.h | 33 +++--- 25 files changed, 293 insertions(+), 241 deletions(-) diff --git a/src/common/tvgInlist.h b/src/common/tvgInlist.h index 16eaffd4..d0880c4c 100644 --- a/src/common/tvgInlist.h +++ b/src/common/tvgInlist.h @@ -33,8 +33,8 @@ namespace tvg { template struct Inlist { - T *head = nullptr; - T *tail = nullptr; + T* head = nullptr; + T* tail = nullptr; void free() { @@ -52,6 +52,7 @@ struct Inlist tail->next = element; element->prev = tail; element->next = nullptr; + tail = element; } else { head = tail = element; element->prev = nullptr; @@ -62,9 +63,10 @@ struct Inlist void front(T* element) { if (head) { + head->prev = element; element->prev = nullptr; element->next = head; - head->prev = element; + head = element; } else { head = tail = element; element->prev = nullptr; @@ -90,6 +92,14 @@ struct Inlist return t; } + void remove(T* element) + { + if (element->prev) element->prev->next = element->next; + if (element->next) element->next->prev = element->prev; + if (element == head) head = element->next; + if (element == tail) tail = element->prev; + } + bool empty() { return head ? false : true; diff --git a/src/loaders/external_jpg/tvgJpgLoader.cpp b/src/loaders/external_jpg/tvgJpgLoader.cpp index 572207b4..995d974c 100644 --- a/src/loaders/external_jpg/tvgJpgLoader.cpp +++ b/src/loaders/external_jpg/tvgJpgLoader.cpp @@ -41,7 +41,7 @@ void JpgLoader::clear() /* External Class Implementation */ /************************************************************************/ -JpgLoader::JpgLoader() +JpgLoader::JpgLoader() : LoadModule(FileType::Jpg) { jpegDecompressor = tjInitDecompress(); } @@ -49,7 +49,7 @@ JpgLoader::JpgLoader() JpgLoader::~JpgLoader() { - if (freeData) free(data); + clear(); tjDestroy(jpegDecompressor); //This image is shared with raster engine. @@ -59,8 +59,6 @@ JpgLoader::~JpgLoader() bool JpgLoader::open(const string& path) { - clear(); - auto jpegFile = fopen(path.c_str(), "rb"); if (!jpegFile) return false; @@ -102,8 +100,6 @@ finalize: bool JpgLoader::open(const char* data, uint32_t size, TVG_UNUSED const string& rpath, bool copy) { - clear(); - int width, height, subSample, colorSpace; if (tjDecompressHeader3(jpegDecompressor, (unsigned char *) data, size, &width, &height, &subSample, &colorSpace) < 0) return false; @@ -128,6 +124,10 @@ bool JpgLoader::open(const char* data, uint32_t size, TVG_UNUSED const string& r bool JpgLoader::read() { + if (!LoadModule::read()) return true; + + if (w == 0 || h == 0) return false; + /* OPTIMIZE: We assume the desired colorspace is ColorSpace::ARGB How could we notice the renderer colorspace at this time? */ if (image) tjFree(image); @@ -142,12 +142,6 @@ bool JpgLoader::read() return false; } - return true; -} - - -bool JpgLoader::close() -{ clear(); return true; } diff --git a/src/loaders/external_jpg/tvgJpgLoader.h b/src/loaders/external_jpg/tvgJpgLoader.h index e7ada9b0..75b784c6 100644 --- a/src/loaders/external_jpg/tvgJpgLoader.h +++ b/src/loaders/external_jpg/tvgJpgLoader.h @@ -38,7 +38,6 @@ public: bool open(const string& path) override; bool open(const char* data, uint32_t size, const string& rpath, bool copy) override; bool read() override; - bool close() override; unique_ptr bitmap() override; diff --git a/src/loaders/external_png/tvgPngLoader.cpp b/src/loaders/external_png/tvgPngLoader.cpp index df9c279c..f602abf0 100644 --- a/src/loaders/external_png/tvgPngLoader.cpp +++ b/src/loaders/external_png/tvgPngLoader.cpp @@ -26,12 +26,19 @@ /* Internal Class Implementation */ /************************************************************************/ +void PngLoader::clear() +{ + png_image_free(image); + free(image); + image = nullptr; +} + /************************************************************************/ /* External Class Implementation */ /************************************************************************/ -PngLoader::PngLoader() +PngLoader::PngLoader() : LoadModule(FileType::Png) { image = static_cast(calloc(1, sizeof(png_image))); image->version = PNG_IMAGE_VERSION; @@ -40,13 +47,11 @@ PngLoader::PngLoader() PngLoader::~PngLoader() { - if (content) { - free((void*)content); - content = nullptr; - } - free(image); + clear(); + free((void*)content); } + bool PngLoader::open(const string& path) { image->opaque = NULL; @@ -60,6 +65,7 @@ bool PngLoader::open(const string& path) return true; } + bool PngLoader::open(const char* data, uint32_t size, TVG_UNUSED const string& rpath, bool copy) { image->opaque = NULL; @@ -76,6 +82,10 @@ bool PngLoader::open(const char* data, uint32_t size, TVG_UNUSED const string& r bool PngLoader::read() { + if (!LoadModule::read()) return true; + + if (w == 0 || h == 0) return false; + png_bytep buffer; image->format = PNG_FORMAT_BGRA; buffer = static_cast(malloc(PNG_IMAGE_SIZE((*image)))); @@ -90,14 +100,11 @@ bool PngLoader::read() } content = reinterpret_cast(buffer); + clear(); + return true; } -bool PngLoader::close() -{ - png_image_free(image); - return true; -} unique_ptr PngLoader::bitmap() { @@ -115,5 +122,4 @@ unique_ptr PngLoader::bitmap() surface->premultiplied = false; return unique_ptr(surface); -} - +} \ No newline at end of file diff --git a/src/loaders/external_png/tvgPngLoader.h b/src/loaders/external_png/tvgPngLoader.h index 65a319a5..8fb09c06 100644 --- a/src/loaders/external_png/tvgPngLoader.h +++ b/src/loaders/external_png/tvgPngLoader.h @@ -36,11 +36,12 @@ public: bool open(const string& path) override; bool open(const char* data, uint32_t size, const string& rpath, bool copy) override; bool read() override; - bool close() override; unique_ptr bitmap() override; private: + void clear(); + png_imagep image = nullptr; uint32_t* content = nullptr; }; diff --git a/src/loaders/external_webp/tvgWebpLoader.cpp b/src/loaders/external_webp/tvgWebpLoader.cpp index 11d64f62..86509c30 100644 --- a/src/loaders/external_webp/tvgWebpLoader.cpp +++ b/src/loaders/external_webp/tvgWebpLoader.cpp @@ -30,22 +30,9 @@ /* Internal Class Implementation */ /************************************************************************/ -void WebpLoader::clear() -{ - if (freeData) free(data); - data = nullptr; - size = 0; - freeData = false; -} - void WebpLoader::run(unsigned tid) { - if (image) { - WebPFree(image); - image = nullptr; - } - image = WebPDecodeBGRA(data, size, nullptr, nullptr); } @@ -54,22 +41,25 @@ void WebpLoader::run(unsigned tid) /* External Class Implementation */ /************************************************************************/ -WebpLoader::WebpLoader() +WebpLoader::WebpLoader() : LoadModule(FileType::Webp) { } WebpLoader::~WebpLoader() { + this->done(); + if (freeData) free(data); + data = nullptr; + size = 0; + freeData = false; WebPFree(image); } bool WebpLoader::open(const string& path) { - clear(); - auto webpFile = fopen(path.c_str(), "rb"); if (!webpFile) return false; @@ -85,10 +75,10 @@ bool WebpLoader::open(const string& path) freeData = true; - if (fread(data, size, 1, webpFile) < 1) goto failure; + if (fread(data, size, 1, webpFile) < 1) goto finalize; int width, height; - if (!WebPGetInfo(data, size, &width, &height)) goto failure; + if (!WebPGetInfo(data, size, &width, &height)) goto finalize; w = static_cast(width); h = static_cast(height); @@ -96,11 +86,6 @@ bool WebpLoader::open(const string& path) ret = true; - goto finalize; - -failure: - clear(); - finalize: fclose(webpFile); return ret; @@ -109,8 +94,6 @@ finalize: bool WebpLoader::open(const char* data, uint32_t size, TVG_UNUSED const string& rpath, bool copy) { - clear(); - if (copy) { this->data = (unsigned char *) malloc(size); if (!this->data) return false; @@ -134,21 +117,15 @@ bool WebpLoader::open(const char* data, uint32_t size, TVG_UNUSED const string& bool WebpLoader::read() { - if (!data || w <= 0 || h <= 0) return false; + if (!LoadModule::read()) return true; + + if (!data || w == 0 || h == 0) return false; TaskScheduler::request(this); return true; } -bool WebpLoader::close() -{ - this->done(); - clear(); - return true; -} - - unique_ptr WebpLoader::bitmap() { this->done(); diff --git a/src/loaders/external_webp/tvgWebpLoader.h b/src/loaders/external_webp/tvgWebpLoader.h index 85f3b067..1f9bfd76 100644 --- a/src/loaders/external_webp/tvgWebpLoader.h +++ b/src/loaders/external_webp/tvgWebpLoader.h @@ -36,12 +36,10 @@ public: bool open(const string& path) override; bool open(const char* data, uint32_t size, const string& rpath, bool copy) override; bool read() override; - bool close() override; unique_ptr bitmap() override; private: - void clear(); void run(unsigned tid) override; unsigned char* data = nullptr; diff --git a/src/loaders/jpg/tvgJpgLoader.cpp b/src/loaders/jpg/tvgJpgLoader.cpp index 9e296c81..72357e02 100644 --- a/src/loaders/jpg/tvgJpgLoader.cpp +++ b/src/loaders/jpg/tvgJpgLoader.cpp @@ -44,6 +44,8 @@ void JpgLoader::run(unsigned tid) image = nullptr; } image = jpgdDecompress(decoder); + + clear(); } @@ -51,19 +53,21 @@ void JpgLoader::run(unsigned tid) /* External Class Implementation */ /************************************************************************/ +JpgLoader::JpgLoader() : LoadModule(FileType::Jpg) +{ + +} + JpgLoader::~JpgLoader() { - jpgdDelete(decoder); - if (freeData) free(data); + clear(); free(image); } bool JpgLoader::open(const string& path) { - clear(); - int width, height; decoder = jpgdHeader(path.c_str(), &width, &height); if (!decoder) return false; @@ -78,8 +82,6 @@ bool JpgLoader::open(const string& path) bool JpgLoader::open(const char* data, uint32_t size, TVG_UNUSED const string& rpath, bool copy) { - clear(); - if (copy) { this->data = (char *) malloc(size); if (!this->data) return false; @@ -105,7 +107,9 @@ bool JpgLoader::open(const char* data, uint32_t size, TVG_UNUSED const string& r bool JpgLoader::read() { - if (!decoder || w <= 0 || h <= 0) return false; + if (!LoadModule::read()) return true; + + if (!decoder || w == 0 || h == 0) return false; TaskScheduler::request(this); @@ -115,8 +119,8 @@ bool JpgLoader::read() bool JpgLoader::close() { + if (!LoadModule::close()) return false; this->done(); - clear(); return true; } diff --git a/src/loaders/jpg/tvgJpgLoader.h b/src/loaders/jpg/tvgJpgLoader.h index 3a3da640..8a05a491 100644 --- a/src/loaders/jpg/tvgJpgLoader.h +++ b/src/loaders/jpg/tvgJpgLoader.h @@ -39,6 +39,7 @@ private: void run(unsigned tid) override; public: + JpgLoader(); ~JpgLoader(); using LoadModule::open; diff --git a/src/loaders/lottie/tvgLottieLoader.cpp b/src/loaders/lottie/tvgLottieLoader.cpp index 7b25cb1b..0a9cc71b 100644 --- a/src/loaders/lottie/tvgLottieLoader.cpp +++ b/src/loaders/lottie/tvgLottieLoader.cpp @@ -20,7 +20,6 @@ * SOFTWARE. */ -#include "tvgLoader.h" #include "tvgLottieLoader.h" #include "tvgLottieModel.h" #include "tvgLottieParser.h" @@ -48,17 +47,6 @@ static float _str2float(const char* str, int len) } -void LottieLoader::clear() -{ - if (copy) free((char*)content); - free(dirName); - dirName = nullptr; - size = 0; - content = nullptr; - copy = false; -} - - void LottieLoader::run(unsigned tid) { //update frame @@ -84,21 +72,21 @@ void LottieLoader::run(unsigned tid) /* External Class Implementation */ /************************************************************************/ -LottieLoader::LottieLoader() : builder(new LottieBuilder) +LottieLoader::LottieLoader() : FrameModule(FileType::Lottie), builder(new LottieBuilder) { + } LottieLoader::~LottieLoader() { - close(); + this->done(); + + if (copy) free((char*)content); + free(dirName); //TODO: correct position? - if (comp) { - delete(comp); - comp = nullptr; - } - + delete(comp); delete(builder); } @@ -208,8 +196,6 @@ bool LottieLoader::header() bool LottieLoader::open(const char* data, uint32_t size, const std::string& rpath, bool copy) { - clear(); - //If the format is dotLottie auto dotLottie = _checkDotLottie(data); if (dotLottie) { @@ -236,8 +222,6 @@ bool LottieLoader::open(const char* data, uint32_t size, const std::string& rpat bool LottieLoader::open(const string& path) { - clear(); - auto f = fopen(path.c_str(), "r"); if (!f) return false; @@ -297,6 +281,8 @@ bool LottieLoader::read() { if (!content || size == 0) return false; + if (!LoadModule::read()) return true; + //the loading has been already completed in header() if (comp) return true; @@ -306,16 +292,6 @@ bool LottieLoader::read() } -bool LottieLoader::close() -{ - this->done(); - - clear(); - - return true; -} - - unique_ptr LottieLoader::paint() { this->done(); diff --git a/src/loaders/lottie/tvgLottieLoader.h b/src/loaders/lottie/tvgLottieLoader.h index a087c06a..1bab6a35 100644 --- a/src/loaders/lottie/tvgLottieLoader.h +++ b/src/loaders/lottie/tvgLottieLoader.h @@ -39,7 +39,7 @@ public: float frameCnt = 0.0f; float frameDuration = 0.0f; - LottieBuilder* builder = nullptr; + LottieBuilder* builder; LottieComposition* comp = nullptr; char* dirName = nullptr; //base resource directory @@ -54,7 +54,6 @@ public: bool open(const char* data, uint32_t size, const std::string& rpath, bool copy) override; bool resize(Paint* paint, float w, float h) override; bool read() override; - bool close() override; unique_ptr paint() override; //Frame Controls diff --git a/src/loaders/png/tvgPngLoader.cpp b/src/loaders/png/tvgPngLoader.cpp index dbfc5c97..6f621763 100644 --- a/src/loaders/png/tvgPngLoader.cpp +++ b/src/loaders/png/tvgPngLoader.cpp @@ -29,16 +29,6 @@ /* Internal Class Implementation */ /************************************************************************/ -void PngLoader::clear() -{ - lodepng_state_cleanup(&state); - - if (freeData) free(data); - data = nullptr; - size = 0; - freeData = false; -} - void PngLoader::run(unsigned tid) { @@ -59,7 +49,7 @@ void PngLoader::run(unsigned tid) /* External Class Implementation */ /************************************************************************/ -PngLoader::PngLoader() +PngLoader::PngLoader() : LoadModule(FileType::Png) { lodepng_state_init(&state); } @@ -69,13 +59,12 @@ PngLoader::~PngLoader() { if (freeData) free(data); free(image); + lodepng_state_cleanup(&state); } bool PngLoader::open(const string& path) { - clear(); - auto pngFile = fopen(path.c_str(), "rb"); if (!pngFile) return false; @@ -91,12 +80,12 @@ bool PngLoader::open(const string& path) freeData = true; - if (fread(data, size, 1, pngFile) < 1) goto failure; + if (fread(data, size, 1, pngFile) < 1) goto finalize; lodepng_state_init(&state); unsigned int width, height; - if (lodepng_inspect(&width, &height, &state, data, size) > 0) goto failure; + if (lodepng_inspect(&width, &height, &state, data, size) > 0) goto finalize; w = static_cast(width); h = static_cast(height); @@ -108,9 +97,6 @@ bool PngLoader::open(const string& path) goto finalize; -failure: - clear(); - finalize: fclose(pngFile); return ret; @@ -119,10 +105,6 @@ finalize: bool PngLoader::open(const char* data, uint32_t size, TVG_UNUSED const string& rpath, bool copy) { - clear(); - - lodepng_state_init(&state); - unsigned int width, height; if (lodepng_inspect(&width, &height, &state, (unsigned char*)(data), size) > 0) return false; @@ -148,7 +130,9 @@ bool PngLoader::open(const char* data, uint32_t size, TVG_UNUSED const string& r bool PngLoader::read() { - if (!data || w <= 0 || h <= 0) return false; + if (!data || w == 0 || h == 0) return false; + + if (!LoadModule::read()) return true; TaskScheduler::request(this); @@ -156,14 +140,6 @@ bool PngLoader::read() } -bool PngLoader::close() -{ - this->done(); - clear(); - return true; -} - - unique_ptr PngLoader::bitmap() { this->done(); diff --git a/src/loaders/png/tvgPngLoader.h b/src/loaders/png/tvgPngLoader.h index a59d9d0d..a19aca6f 100644 --- a/src/loaders/png/tvgPngLoader.h +++ b/src/loaders/png/tvgPngLoader.h @@ -36,7 +36,6 @@ private: unsigned long size = 0; bool freeData = false; - void clear(); void run(unsigned tid) override; public: @@ -47,7 +46,6 @@ public: bool open(const string& path) override; bool open(const char* data, uint32_t size, const string& rpath, bool copy) override; bool read() override; - bool close() override; unique_ptr bitmap() override; }; diff --git a/src/loaders/raw/tvgRawLoader.cpp b/src/loaders/raw/tvgRawLoader.cpp index e7ddff85..ca624273 100644 --- a/src/loaders/raw/tvgRawLoader.cpp +++ b/src/loaders/raw/tvgRawLoader.cpp @@ -34,6 +34,11 @@ /* External Class Implementation */ /************************************************************************/ +RawLoader::RawLoader() : LoadModule(FileType::Raw) +{ +} + + RawLoader::~RawLoader() { if (copy && content) { @@ -45,6 +50,8 @@ RawLoader::~RawLoader() bool RawLoader::open(const uint32_t* data, uint32_t w, uint32_t h, bool premultiplied, bool copy) { + if (!LoadModule::read()) return true; + if (!data || w == 0 || h == 0) return false; this->w = (float)w; @@ -67,12 +74,7 @@ bool RawLoader::open(const uint32_t* data, uint32_t w, uint32_t h, bool premulti bool RawLoader::read() { - return true; -} - - -bool RawLoader::close() -{ + LoadModule::read(); return true; } diff --git a/src/loaders/raw/tvgRawLoader.h b/src/loaders/raw/tvgRawLoader.h index 47f17ac2..45589e83 100644 --- a/src/loaders/raw/tvgRawLoader.h +++ b/src/loaders/raw/tvgRawLoader.h @@ -30,12 +30,12 @@ public: bool copy = false; bool premultiplied = false; + RawLoader(); ~RawLoader(); using LoadModule::open; bool open(const uint32_t* data, uint32_t w, uint32_t h, bool premultiplied, bool copy); bool read() override; - bool close() override; unique_ptr bitmap() override; }; diff --git a/src/loaders/svg/tvgSvgLoader.cpp b/src/loaders/svg/tvgSvgLoader.cpp index 6a4eaf44..183b3421 100644 --- a/src/loaders/svg/tvgSvgLoader.cpp +++ b/src/loaders/svg/tvgSvgLoader.cpp @@ -3538,14 +3538,15 @@ void SvgLoader::clear(bool all) /* External Class Implementation */ /************************************************************************/ -SvgLoader::SvgLoader() +SvgLoader::SvgLoader() : LoadModule(FileType::Svg) { } SvgLoader::~SvgLoader() { - close(); + this->done(); + clear(); } @@ -3727,6 +3728,8 @@ bool SvgLoader::read() { if (!content || size == 0) return false; + if (!LoadModule::read()) return true; + //the loading has been already completed in header() if (root) return true; @@ -3738,10 +3741,9 @@ bool SvgLoader::read() bool SvgLoader::close() { + if (!LoadModule::close()) return false; this->done(); - clear(); - return true; } diff --git a/src/loaders/tvg/tvgTvgLoader.cpp b/src/loaders/tvg/tvgTvgLoader.cpp index a5a5a3b1..1c2d3e63 100644 --- a/src/loaders/tvg/tvgTvgLoader.cpp +++ b/src/loaders/tvg/tvgTvgLoader.cpp @@ -39,10 +39,8 @@ void TvgLoader::clear() size = 0; copy = false; - if (interpreter) { - delete(interpreter); - interpreter = nullptr; - } + delete(interpreter); + interpreter = nullptr; } @@ -103,13 +101,37 @@ bool TvgLoader::readHeader() } +void TvgLoader::run(unsigned tid) +{ + if (root) root.reset(); + + auto data = const_cast(ptr); + + if (compressed) { + data = (char*) lzwDecode((uint8_t*) data, compressedSize, compressedSizeBits, uncompressedSize); + root = interpreter->run(data, data + uncompressedSize); + free(data); + } else { + root = interpreter->run(data, this->data + size); + } + + clear(); +} + + /************************************************************************/ /* External Class Implementation */ /************************************************************************/ +TvgLoader::TvgLoader() : LoadModule(FileType::Tvg) +{ +} + + TvgLoader::~TvgLoader() { - close(); + this->done(); + clear(); } @@ -193,38 +215,14 @@ bool TvgLoader::read() { if (!ptr || size == 0) return false; + if (!LoadModule::read()) return true; + TaskScheduler::request(this); return true; } -bool TvgLoader::close() -{ - this->done(); - clear(); - return true; -} - - -void TvgLoader::run(unsigned tid) -{ - if (root) root.reset(); - - auto data = const_cast(ptr); - - if (compressed) { - data = (char*) lzwDecode((uint8_t*) data, compressedSize, compressedSizeBits, uncompressedSize); - root = interpreter->run(data, data + uncompressedSize); - free(data); - } else { - root = interpreter->run(data, this->data + size); - } - - if (!root) clear(); -} - - unique_ptr TvgLoader::paint() { this->done(); diff --git a/src/loaders/tvg/tvgTvgLoader.h b/src/loaders/tvg/tvgTvgLoader.h index ddd895ab..389425e7 100644 --- a/src/loaders/tvg/tvgTvgLoader.h +++ b/src/loaders/tvg/tvgTvgLoader.h @@ -42,13 +42,13 @@ public: bool copy = false; bool compressed = false; + TvgLoader(); ~TvgLoader(); using LoadModule::open; bool open(const string &path) override; bool open(const char *data, uint32_t size, const string& rpath, bool copy) override; bool read() override; - bool close() override; bool resize(Paint* paint, float w, float h) override; unique_ptr paint() override; diff --git a/src/renderer/tvgAnimation.cpp b/src/renderer/tvgAnimation.cpp index b894e00c..5c153b6e 100644 --- a/src/renderer/tvgAnimation.cpp +++ b/src/renderer/tvgAnimation.cpp @@ -64,7 +64,7 @@ Animation::Animation() : pImpl(new Impl) Result Animation::frame(float no) noexcept { - auto loader = pImpl->picture->pImpl->loader.get(); + auto loader = pImpl->picture->pImpl->loader; if (!loader) return Result::InsufficientCondition; if (!loader->animatable()) return Result::NonSupport; @@ -82,7 +82,7 @@ Picture* Animation::picture() const noexcept float Animation::curFrame() const noexcept { - auto loader = pImpl->picture->pImpl->loader.get(); + auto loader = pImpl->picture->pImpl->loader; if (!loader) return 0; if (!loader->animatable()) return 0; @@ -93,7 +93,7 @@ float Animation::curFrame() const noexcept float Animation::totalFrame() const noexcept { - auto loader = pImpl->picture->pImpl->loader.get(); + auto loader = pImpl->picture->pImpl->loader; if (!loader) return 0; if (!loader->animatable()) return 0; @@ -104,7 +104,7 @@ float Animation::totalFrame() const noexcept float Animation::duration() const noexcept { - auto loader = pImpl->picture->pImpl->loader.get(); + auto loader = pImpl->picture->pImpl->loader; if (!loader) return 0; if (!loader->animatable()) return 0; diff --git a/src/renderer/tvgFrameModule.h b/src/renderer/tvgFrameModule.h index 332cca30..728298dd 100644 --- a/src/renderer/tvgFrameModule.h +++ b/src/renderer/tvgFrameModule.h @@ -31,6 +31,7 @@ namespace tvg class FrameModule: public LoadModule { public: + FrameModule(FileType type) : LoadModule(type) {} virtual ~FrameModule() {} virtual bool frame(float no) = 0; //set the current frame number diff --git a/src/renderer/tvgLoadModule.h b/src/renderer/tvgLoadModule.h index de1d0437..9cda65ca 100644 --- a/src/renderer/tvgLoadModule.h +++ b/src/renderer/tvgLoadModule.h @@ -24,16 +24,26 @@ #define _TVG_LOAD_MODULE_H_ #include "tvgRender.h" +#include "tvgInlist.h" namespace tvg { -class LoadModule +struct LoadModule { -public: + INLIST_ITEM(LoadModule); + + //Use either hashkey(data) or hashpath(path) + uint64_t hashkey; + string hashpath; + float w = 0, h = 0; //default image size ColorSpace cs = ColorSpace::Unsupported; //must be clarified at open() + FileType type; //current loader file type + uint16_t sharing = 0; //reference count + bool readied = false; //read done already. + LoadModule(FileType type) : type(type) {} virtual ~LoadModule() {} virtual bool open(const string& path) { return false; } @@ -45,8 +55,19 @@ public: virtual bool animatable() { return false; } //true if this loader supports animation. virtual void sync() {}; //finish immediately if any async update jobs. - virtual bool read() = 0; - virtual bool close() = 0; + virtual bool read() + { + if (readied) return false; + readied = true; + return true; + } + + virtual bool close() + { + if (sharing == 0) return true; + --sharing; + return false; + } virtual unique_ptr bitmap() { return nullptr; } virtual unique_ptr paint() { return nullptr; } diff --git a/src/renderer/tvgLoader.cpp b/src/renderer/tvgLoader.cpp index 57f35be3..a386e2ac 100644 --- a/src/renderer/tvgLoader.cpp +++ b/src/renderer/tvgLoader.cpp @@ -20,6 +20,7 @@ * SOFTWARE. */ +#include "tvgInlist.h" #include "tvgLoader.h" #ifdef THORVG_SVG_LOADER_SUPPORT @@ -48,10 +49,19 @@ #include "tvgRawLoader.h" + +uint64_t HASH_KEY(const char* data, uint64_t size) +{ + return (((uint64_t) data) << 32) | size; +} + /************************************************************************/ /* Internal Class Implementation */ /************************************************************************/ +static Inlist _activeLoaders; + + static LoadModule* _find(FileType type) { switch(type) { @@ -156,10 +166,8 @@ static LoadModule* _findByPath(const string& path) } -static LoadModule* _findByType(const string& mimeType) +static FileType _convert(const string& mimeType) { - if (mimeType.empty()) return nullptr; - auto type = FileType::Unknown; if (mimeType == "tvg") type = FileType::Tvg; @@ -169,12 +177,50 @@ static LoadModule* _findByType(const string& mimeType) else if (mimeType == "png") type = FileType::Png; else if (mimeType == "jpg" || mimeType == "jpeg") type = FileType::Jpg; else if (mimeType == "webp") type = FileType::Webp; - else { - TVGLOG("RENDERER", "Given mimetype is unknown = \"%s\".", mimeType.c_str()); - return nullptr; - } + else TVGLOG("RENDERER", "Given mimetype is unknown = \"%s\".", mimeType.c_str()); - return _find(type); + return type; +} + + +static LoadModule* _findByType(const string& mimeType) +{ + return _find(_convert(mimeType)); +} + + +static LoadModule* _findFromCache(const string& path) +{ + auto loader = _activeLoaders.head; + + while (loader) { + if (loader->hashpath == path) { + ++loader->sharing; + return loader; + } + loader = loader->next; + } + return nullptr; +} + + +static LoadModule* _findFromCache(const char* data, uint32_t size, const string& mimeType) +{ + auto type = _convert(mimeType); + if (type == FileType::Unknown) return nullptr; + + auto loader = _activeLoaders.head; + + auto key = HASH_KEY(data, size); + + while (loader) { + if (loader->type == type && loader->hashkey == key) { + ++loader->sharing; + return loader; + } + loader = loader->next; + } + return nullptr; } @@ -185,40 +231,57 @@ static LoadModule* _findByType(const string& mimeType) bool LoaderMgr::init() { - //TODO: - return true; } bool LoaderMgr::term() { - //TODO: - return true; } -shared_ptr LoaderMgr::loader(const string& path, bool* invalid) +void LoaderMgr::retrieve(LoadModule* loader) +{ + if (!loader) return; + + if (loader->close()) { + _activeLoaders.remove(loader); + delete(loader); + } +} + + +LoadModule* LoaderMgr::loader(const string& path, bool* invalid) { *invalid = false; + if (auto loader = _findFromCache(path)) return loader; + if (auto loader = _findByPath(path)) { - if (loader->open(path)) return shared_ptr(loader); - else delete(loader); + if (loader->open(path)) { + loader->hashpath = path; + _activeLoaders.back(loader); + return loader; + } + delete(loader); *invalid = true; } return nullptr; } -shared_ptr LoaderMgr::loader(const char* data, uint32_t size, const string& mimeType, const string& rpath, bool copy) +LoadModule* LoaderMgr::loader(const char* data, uint32_t size, const string& mimeType, const string& rpath, bool copy) { + if (auto loader = _findFromCache(data, size, mimeType)) return loader; + //Try with the given MimeType if (!mimeType.empty()) { if (auto loader = _findByType(mimeType)) { if (loader->open(data, size, rpath, copy)) { - return shared_ptr(loader); + loader->hashkey = HASH_KEY(data, size); + _activeLoaders.back(loader); + return loader; } else { TVGLOG("LOADER", "Given mimetype \"%s\" seems incorrect or not supported.", mimeType.c_str()); delete(loader); @@ -229,8 +292,12 @@ shared_ptr LoaderMgr::loader(const char* data, uint32_t size, const for (int i = 0; i < static_cast(FileType::Unknown); i++) { auto loader = _find(static_cast(i)); if (loader) { - if (loader->open(data, size, rpath, copy)) return shared_ptr(loader); - else delete(loader); + if (loader->open(data, size, rpath, copy)) { + loader->hashkey = HASH_KEY(data, size); + _activeLoaders.back(loader); + return loader; + } + delete(loader); } } } @@ -238,12 +305,18 @@ shared_ptr LoaderMgr::loader(const char* data, uint32_t size, const } -shared_ptr LoaderMgr::loader(const uint32_t *data, uint32_t w, uint32_t h, bool premultiplied, bool copy) +LoadModule* LoaderMgr::loader(const uint32_t *data, uint32_t w, uint32_t h, bool premultiplied, bool copy) { + //TODO: should we check premultiplied?? + if (auto loader = _findFromCache((const char*)(data), w * h, "raw")) return loader; + //function is dedicated for raw images only auto loader = new RawLoader; - if (loader->open(data, w, h, premultiplied, copy)) return shared_ptr(loader); - else delete(loader); - + if (loader->open(data, w, h, premultiplied, copy)) { + loader->hashkey = HASH_KEY((const char*)data, w * h); + _activeLoaders.back(loader); + return loader; + } + delete(loader); return nullptr; -} +} \ No newline at end of file diff --git a/src/renderer/tvgLoader.h b/src/renderer/tvgLoader.h index a94dd501..29f19cf0 100644 --- a/src/renderer/tvgLoader.h +++ b/src/renderer/tvgLoader.h @@ -29,9 +29,10 @@ struct LoaderMgr { static bool init(); static bool term(); - static shared_ptr loader(const string& path, bool* invalid); - static shared_ptr loader(const char* data, uint32_t size, const string& mimeType, const string& rpath, bool copy); - static shared_ptr loader(const uint32_t* data, uint32_t w, uint32_t h, bool premultiplied, bool copy); + static LoadModule* loader(const string& path, bool* invalid); + static LoadModule* loader(const char* data, uint32_t size, const string& mimeType, const string& rpath, bool copy); + static LoadModule* loader(const uint32_t* data, uint32_t w, uint32_t h, bool premultiplied, bool copy); + static void retrieve(LoadModule* loader); }; #endif //_TVG_LOADER_H_ diff --git a/src/renderer/tvgPicture.cpp b/src/renderer/tvgPicture.cpp index 1465f09f..23c6d55b 100644 --- a/src/renderer/tvgPicture.cpp +++ b/src/renderer/tvgPicture.cpp @@ -32,7 +32,6 @@ RenderUpdateFlag Picture::Impl::load() if (!paint) { if (auto p = loader->paint()) { paint = p.release(); - loader->close(); if (w != loader->w || h != loader->h) { if (!resizing) { w = loader->w; @@ -47,7 +46,6 @@ RenderUpdateFlag Picture::Impl::load() if (!surface) { if ((surface = loader->bitmap().release())) { - loader->close(); return RenderUpdateFlag::Image; } } @@ -120,6 +118,28 @@ RenderTransform Picture::Impl::resizeTransform(const RenderTransform* pTransform } +Result Picture::Impl::load(LoadModule* loader) +{ + //Same resource has been loaded. + if (this->loader == loader) { + this->loader->sharing--; //make it sure the reference counting. + return Result::Success; + } else if (this->loader) { + LoaderMgr::retrieve(this->loader); + } + + this->loader = loader; + + if (!loader->read()) return Result::Unknown; + + this->w = loader->w; + this->h = loader->h; + + return Result::Success; +} + + + /************************************************************************/ /* External Class Implementation */ /************************************************************************/ diff --git a/src/renderer/tvgPicture.h b/src/renderer/tvgPicture.h index 94f61510..f4f5ee0b 100644 --- a/src/renderer/tvgPicture.h +++ b/src/renderer/tvgPicture.h @@ -57,7 +57,7 @@ struct PictureIterator : Iterator struct Picture::Impl { - shared_ptr loader = nullptr; + LoadModule* loader = nullptr; Paint* paint = nullptr; //vector picture uses Surface* surface = nullptr; //bitmap picture uses @@ -73,6 +73,7 @@ struct Picture::Impl bool render(RenderMethod &renderer); bool size(float w, float h); RenderRegion bounds(RenderMethod& renderer); + Result load(LoadModule* ploader); Impl(Picture* p) : picture(p) { @@ -80,6 +81,7 @@ struct Picture::Impl ~Impl() { + LoaderMgr::retrieve(loader); delete(paint); delete(surface); } @@ -149,40 +151,32 @@ struct Picture::Impl Result load(const string& path) { if (paint || surface) return Result::InsufficientCondition; - if (loader) loader->close(); + bool invalid; //Invalid Path - loader = LoaderMgr::loader(path, &invalid); + auto loader = LoaderMgr::loader(path, &invalid); if (!loader) { if (invalid) return Result::InvalidArguments; return Result::NonSupport; } - if (!loader->read()) return Result::Unknown; - w = loader->w; - h = loader->h; - return Result::Success; + return load(loader); } Result load(const char* data, uint32_t size, const string& mimeType, const string& rpath, bool copy) { if (paint || surface) return Result::InsufficientCondition; - if (loader) loader->close(); - loader = LoaderMgr::loader(data, size, mimeType, rpath, copy); + auto loader = LoaderMgr::loader(data, size, mimeType, rpath, copy); if (!loader) return Result::NonSupport; - if (!loader->read()) return Result::Unknown; - w = loader->w; - h = loader->h; - return Result::Success; + return load(loader); } Result load(uint32_t* data, uint32_t w, uint32_t h, bool premultiplied, bool copy) { if (paint || surface) return Result::InsufficientCondition; - if (loader) loader->close(); - loader = LoaderMgr::loader(data, w, h, premultiplied, copy); + + auto loader = LoaderMgr::loader(data, w, h, premultiplied, copy); if (!loader) return Result::FailedAllocation; - this->w = loader->w; - this->h = loader->h; - return Result::Success; + + return load(loader); } void mesh(const Polygon* triangles, const uint32_t triangleCnt) @@ -208,10 +202,11 @@ struct Picture::Impl if (paint) dup->paint = paint->duplicate(); dup->loader = loader; + ++dup->loader->sharing; + if (surface) { dup->surface = new Surface; *dup->surface = *surface; - //TODO: A dupilcation is not a proxy... it needs copy of the pixel data? dup->surface->owner = false; } dup->w = w;