Selaa lähdekoodia

Merge pull request #91788 from akien-mga/thorvg-0.13.3

thorvg: Update to 0.13.3, add webp loader
Rémi Verschelde 1 vuosi sitten
vanhempi
commit
1cf283dd26
37 muutettua tiedostoa jossa 630 lisäystä ja 3037 poistoa
  1. 6 1
      modules/svg/SCsub
  2. 1 1
      thirdparty/README.md
  3. 5 3
      thirdparty/thorvg/AUTHORS
  4. 2 1
      thirdparty/thorvg/inc/config.h
  5. 56 12
      thirdparty/thorvg/inc/thorvg.h
  6. 1 0
      thirdparty/thorvg/src/common/tvgArray.h
  7. 2 2
      thirdparty/thorvg/src/common/tvgCompressor.cpp
  8. 22 3
      thirdparty/thorvg/src/common/tvgLines.cpp
  9. 13 3
      thirdparty/thorvg/src/common/tvgLines.h
  10. 12 0
      thirdparty/thorvg/src/common/tvgMath.h
  11. 3 0
      thirdparty/thorvg/src/common/tvgStr.cpp
  12. 41 46
      thirdparty/thorvg/src/loaders/external_webp/tvgWebpLoader.cpp
  13. 15 17
      thirdparty/thorvg/src/loaders/external_webp/tvgWebpLoader.h
  14. 0 2628
      thirdparty/thorvg/src/loaders/png/tvgLodePng.cpp
  15. 0 174
      thirdparty/thorvg/src/loaders/png/tvgLodePng.h
  16. 16 0
      thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.cpp
  17. 168 34
      thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp
  18. 1 1
      thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.h
  19. 4 3
      thirdparty/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h
  20. 11 5
      thirdparty/thorvg/src/loaders/svg/tvgSvgPath.cpp
  21. 4 4
      thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp
  22. 3 2
      thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.cpp
  23. 4 1
      thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp
  24. 6 1
      thirdparty/thorvg/src/renderer/sw_engine/tvgSwRaster.cpp
  25. 18 1
      thirdparty/thorvg/src/renderer/sw_engine/tvgSwRasterAvx.h
  26. 50 10
      thirdparty/thorvg/src/renderer/sw_engine/tvgSwRasterNeon.h
  27. 48 38
      thirdparty/thorvg/src/renderer/sw_engine/tvgSwShape.cpp
  28. 27 0
      thirdparty/thorvg/src/renderer/tvgAnimation.cpp
  29. 2 13
      thirdparty/thorvg/src/renderer/tvgCanvas.h
  30. 15 0
      thirdparty/thorvg/src/renderer/tvgFrameModule.h
  31. 2 2
      thirdparty/thorvg/src/renderer/tvgGlCanvas.cpp
  32. 34 16
      thirdparty/thorvg/src/renderer/tvgLoader.cpp
  33. 1 0
      thirdparty/thorvg/src/renderer/tvgRender.h
  34. 7 4
      thirdparty/thorvg/src/renderer/tvgSaver.cpp
  35. 6 4
      thirdparty/thorvg/src/renderer/tvgShape.cpp
  36. 17 3
      thirdparty/thorvg/src/renderer/tvgShape.h
  37. 7 4
      thirdparty/thorvg/update-thorvg.sh

+ 6 - 1
modules/svg/SCsub

@@ -12,8 +12,8 @@ thirdparty_obj = []
 thirdparty_dir = "#thirdparty/thorvg/"
 thirdparty_sources = [
     # common
-    "src/common/tvgBezier.cpp",
     "src/common/tvgCompressor.cpp",
+    "src/common/tvgLines.cpp",
     "src/common/tvgMath.cpp",
     "src/common/tvgStr.cpp",
     # SVG parser
@@ -24,7 +24,9 @@ thirdparty_sources = [
     "src/loaders/svg/tvgSvgUtil.cpp",
     "src/loaders/svg/tvgXmlParser.cpp",
     "src/loaders/raw/tvgRawLoader.cpp",
+    # image loaders
     "src/loaders/external_png/tvgPngLoader.cpp",
+    "src/loaders/external_webp/tvgWebpLoader.cpp",
     "src/loaders/jpg/tvgJpgd.cpp",
     "src/loaders/jpg/tvgJpgLoader.cpp",
     # renderer common
@@ -74,7 +76,10 @@ env_thirdparty.Prepend(
         thirdparty_dir + "src/renderer/sw_engine",
         thirdparty_dir + "src/loaders/raw",
         thirdparty_dir + "src/loaders/external_png",
+        thirdparty_dir + "src/loaders/external_webp",
         thirdparty_dir + "src/loaders/jpg",
+        "#thirdparty/libpng",
+        "#thirdparty/libwebp/src",
     ]
 )
 

+ 1 - 1
thirdparty/README.md

@@ -882,7 +882,7 @@ instead of `miniz.h` as an external dependency.
 ## thorvg
 
 - Upstream: https://github.com/thorvg/thorvg
-- Version: 0.12.9 (afa6d8499bd49141d99d5e40a4620bd9f6bc0467, 2024)
+- Version: 0.13.3 (6235068cad8cad176ccd0cbcf82f25e985fbc258, 2024)
 - License: MIT
 
 Files extracted from upstream source:

+ 5 - 3
thirdparty/thorvg/AUTHORS

@@ -1,10 +1,10 @@
-Hermet Park <[email protected]>
+Hermet Park <[email protected]>, <[email protected]>
 Prudhvi Raj Vasireddi <[email protected]>
 Junsu Choi <[email protected]>
 Pranay Samanta <[email protected]>
 Mateusz Palkowski <[email protected]>
 Subhransu Mohanty <[email protected]>
-Mira Grudzinska <[email protected]>
+Mira Grudzinska <[email protected]>, <[email protected]>
 Michal Szczecinski <[email protected]>
 Shinwoo Kim <[email protected]>
 Piotr Kalota <[email protected]>
@@ -18,10 +18,12 @@ Rémi Verschelde <[email protected]>
 Martin Liska <[email protected]>
 Vincenzo Pupillo <[email protected]>
 EunSik Jeong <[email protected]>
-Samsung Electronics Co., Ltd
 Rafał Mikrut <[email protected]>
 Martin Capitanio <[email protected]>
 RuiwenTang <[email protected]>
 YouJin Lee <[email protected]>
 SergeyLebedkin <[email protected]>
 Jinny You <[email protected]>
+Nattu Adnan <[email protected]>
+Gabor Kiss-Vamosi <[email protected]>
+Lorcán Mc Donagh <[email protected]>

+ 2 - 1
thirdparty/thorvg/inc/config.h

@@ -5,10 +5,11 @@
 #define THORVG_SVG_LOADER_SUPPORT
 #define THORVG_PNG_LOADER_SUPPORT
 #define THORVG_JPG_LOADER_SUPPORT
+#define THORVG_WEBP_LOADER_SUPPORT
 #define THORVG_THREAD_SUPPORT
 
 // For internal debugging:
 //#define THORVG_LOG_ENABLED
 
-#define THORVG_VERSION_STRING "0.12.10"
+#define THORVG_VERSION_STRING "0.13.3"
 #endif

+ 56 - 12
thirdparty/thorvg/inc/thorvg.h

@@ -1645,7 +1645,7 @@ public:
     };
 
     /**
-     * @brief Sets the target buffer for the rasterization.
+     * @brief Sets the drawing target for the rasterization.
      *
      * The buffer of a desirable size should be allocated and owned by the caller.
      *
@@ -1714,13 +1714,22 @@ public:
     ~GlCanvas();
 
     /**
-     * @brief Sets the target buffer for the rasterization.
+     * @brief Sets the drawing target for rasterization.
      *
-     * @warning Please do not use it, this API is not official one. It could be modified in the next version.
+     * This function specifies the drawing target where the rasterization will occur. It can target
+     * a specific framebuffer object (FBO) or the main surface.
+     *
+     * @param[in] id The GL target ID, usually indicating the FBO ID. A value of @c 0 specifies the main surface.
+     * @param[in] w The width (in pixels) of the raster image.
+     * @param[in] h The height (in pixels) of the raster image.
      *
+     * @warning This API is experimental and not officially supported. It may be modified or removed in future versions.
+     * @warning Drawing on the main surface is currently not permitted. If the identifier (@p id) is set to @c 0, the operation will be aborted.
+     *
+     * @note Currently, this only allows the GL_RGBA8 color space format.
      * @note Experimental API
-     */
-    Result target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h) noexcept;
+    */
+    Result target(int32_t id, uint32_t w, uint32_t h) noexcept;
 
     /**
      * @brief Creates a new GlCanvas object.
@@ -1828,7 +1837,7 @@ public:
  *
  * This class supports the display and control of animation frames.
  *
- * @note Experimental API
+ * @since 0.13
  */
 
 class TVG_API Animation
@@ -1845,9 +1854,11 @@ public:
      * @retval Result::InsufficientCondition if the given @p no is the same as the current frame value.
      * @retval Result::NonSupport The current Picture data does not support animations.
      *
+     * @note For efficiency, ThorVG ignores updates to the new frame value if the difference from the current frame value
+     *       is less than 0.001. In such cases, it returns @c Result::InsufficientCondition.
+     *
      * @see totalFrame()
      *
-     * @note Experimental API
      */
     Result frame(float no) noexcept;
 
@@ -1862,7 +1873,6 @@ public:
      *
      * @warning The picture instance is owned by Animation. It should not be deleted manually.
      *
-     * @note Experimental API
      */
     Picture* picture() const noexcept;
 
@@ -1876,7 +1886,6 @@ public:
      * @see Animation::frame(float no)
      * @see Animation::totalFrame()
      *
-     * @note Experimental API
      */
     float curFrame() const noexcept;
 
@@ -1888,7 +1897,6 @@ public:
      * @note Frame numbering starts from 0.
      * @note If the Picture is not properly configured, this function will return 0.
      *
-     * @note Experimental API
      */
     float totalFrame() const noexcept;
 
@@ -1899,16 +1907,52 @@ public:
      *
      * @note If the Picture is not properly configured, this function will return 0.
      *
-     * @% Experimental API
      */
     float duration() const noexcept;
 
+    /**
+     * @brief Specifies the playback segment of the animation.
+     *
+     * The set segment is designated as the play area of the animation.
+     * This is useful for playing a specific segment within the entire animation.
+     * After setting, the number of animation frames and the playback time are calculated
+     * by mapping the playback segment as the entire range.
+     *
+     * @param[in] begin segment start.
+     * @param[in] end segment end.
+     *
+     * @retval Result::Success When succeed.
+     * @retval Result::InsufficientCondition In case the animation is not loaded.
+     * @retval Result::InvalidArguments When the given parameter is invalid.
+     * @retval Result::NonSupport When it's not animatable.
+     *
+     * @note Range from 0.0~1.0
+     * @note If a marker has been specified, its range will be disregarded.
+     * @see LottieAnimation::segment(const char* marker)
+     * @note Experimental API
+     */
+    Result segment(float begin, float end) noexcept;
+
+    /**
+     * @brief Gets the current segment.
+     *
+     * @param[out] begin segment start.
+     * @param[out] end segment end.
+     *
+     * @retval Result::Success When succeed.
+     * @retval Result::InsufficientCondition In case the animation is not loaded.
+     * @retval Result::InvalidArguments When the given parameter is invalid.
+     * @retval Result::NonSupport When it's not animatable.
+     *
+     * @note Experimental API
+     */
+    Result segment(float* begin, float* end = nullptr) noexcept;
+
     /**
      * @brief Creates a new Animation object.
      *
      * @return A new Animation object.
      *
-     * @note Experimental API
      */
     static std::unique_ptr<Animation> gen() noexcept;
 

+ 1 - 0
thirdparty/thorvg/src/common/tvgArray.h

@@ -61,6 +61,7 @@ struct Array
 
     void push(Array<T>& rhs)
     {
+        if (rhs.count == 0) return;
         grow(rhs.count);
         memcpy(data + count, rhs.data, rhs.count * sizeof(T));
         count += rhs.count;

+ 2 - 2
thirdparty/thorvg/src/common/tvgCompressor.cpp

@@ -458,11 +458,11 @@ size_t b64Decode(const char* encoded, const size_t len, char** decoded)
         auto value2 = B64_INDEX[(size_t)encoded[1]];
         output[idx++] = (value1 << 2) + ((value2 & 0x30) >> 4);
 
-        if (!encoded[2] || encoded[2] == '=' || encoded[2] == '.') break;
+        if (!encoded[2] || encoded[3] < 0 || encoded[2] == '=' || encoded[2] == '.') break;
         auto value3 = B64_INDEX[(size_t)encoded[2]];
         output[idx++] = ((value2 & 0x0f) << 4) + ((value3 & 0x3c) >> 2);
 
-        if (!encoded[3] || encoded[3] == '=' || encoded[3] == '.') break;
+        if (!encoded[3] || encoded[3] < 0 || encoded[3] == '=' || encoded[3] == '.') break;
         auto value4 = B64_INDEX[(size_t)encoded[3]];
         output[idx++] = ((value3 & 0x03) << 6) + value4;
         encoded += 4;

+ 22 - 3
thirdparty/thorvg/src/common/tvgBezier.cpp → thirdparty/thorvg/src/common/tvgLines.cpp

@@ -21,9 +21,9 @@
  */
 
 #include "tvgMath.h"
-#include "tvgBezier.h"
+#include "tvgLines.h"
 
-#define BEZIER_EPSILON 1e-4f
+#define BEZIER_EPSILON 1e-2f
 
 /************************************************************************/
 /* Internal Class Implementation                                        */
@@ -101,6 +101,25 @@ float _bezAt(const Bezier& bz, float at, float length, LengthFunc lineLengthFunc
 namespace tvg
 {
 
+float lineLength(const Point& pt1, const Point& pt2)
+{
+    return _lineLength(pt1, pt2);
+}
+
+
+void lineSplitAt(const Line& cur, float at, Line& left, Line& right)
+{
+    auto len = lineLength(cur.pt1, cur.pt2);
+    auto dx = ((cur.pt2.x - cur.pt1.x) / len) * at;
+    auto dy = ((cur.pt2.y - cur.pt1.y) / len) * at;
+    left.pt1 = cur.pt1;
+    left.pt2.x = left.pt1.x + dx;
+    left.pt2.y = left.pt1.y + dy;
+    right.pt1 = left.pt2;
+    right.pt2 = cur.pt2;
+}
+
+
 void bezSplit(const Bezier& cur, Bezier& left, Bezier& right)
 {
     auto c = (cur.ctrl1.x + cur.ctrl2.x) * 0.5f;
@@ -219,7 +238,7 @@ float bezAngleAt(const Bezier& bz, float t)
     pt.x *= 3;
     pt.y *= 3;
 
-    return atan2(pt.x, pt.y) * 180.0f / 3.141592f;
+    return mathRad2Deg(atan2(pt.x, pt.y));
 }
 
 

+ 13 - 3
thirdparty/thorvg/src/common/tvgBezier.h → thirdparty/thorvg/src/common/tvgLines.h

@@ -20,14 +20,24 @@
  * SOFTWARE.
  */
 
-#ifndef _TVG_BEZIER_H_
-#define _TVG_BEZIER_H_
+#ifndef _TVG_LINES_H_
+#define _TVG_LINES_H_
 
 #include "tvgCommon.h"
 
 namespace tvg
 {
 
+struct Line
+{
+    Point pt1;
+    Point pt2;
+};
+
+float lineLength(const Point& pt1, const Point& pt2);
+void lineSplitAt(const Line& cur, float at, Line& left, Line& right);
+
+
 struct Bezier
 {
     Point start;
@@ -48,4 +58,4 @@ float bezLengthApprox(const Bezier& cur);
 float bezAtApprox(const Bezier& bz, float at, float length);
 }
 
-#endif //_TVG_BEZIER_H_
+#endif //_TVG_LINES_H_

+ 12 - 0
thirdparty/thorvg/src/common/tvgMath.h

@@ -44,6 +44,18 @@ bool mathIdentity(const Matrix* m);
 void mathMultiply(Point* pt, const Matrix* transform);
 
 
+static inline float mathDeg2Rad(float degree)
+{
+     return degree * (MATH_PI / 180.0f);
+}
+
+
+static inline float mathRad2Deg(float radian)
+{
+    return radian * (180.0f / MATH_PI);
+}
+
+
 static inline bool mathZero(float a)
 {
     return (fabsf(a) < FLT_EPSILON) ? true : false;

+ 3 - 0
thirdparty/thorvg/src/common/tvgStr.cpp

@@ -21,6 +21,7 @@
  */
 
 #include "config.h"
+#include <cmath>
 #include <cstring>
 #include <memory.h>
 #include "tvgMath.h"
@@ -197,6 +198,8 @@ float strToFloat(const char *nPtr, char **endPtr)
 
 success:
     if (endPtr) *endPtr = (char *)(a);
+    if (!std::isfinite(val)) return 0.0f;
+
     return minus * val;
 
 error:

+ 41 - 46
thirdparty/thorvg/src/loaders/png/tvgPngLoader.cpp → thirdparty/thorvg/src/loaders/external_webp/tvgWebpLoader.cpp

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021 - 2024 the ThorVG project. All rights reserved.
+ * Copyright (c) 2023 - 2024 the ThorVG project. 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
@@ -21,8 +21,9 @@
  */
 
 #include <memory.h>
-#include "tvgLoader.h"
-#include "tvgPngLoader.h"
+#include <webp/decode.h>
+
+#include "tvgWebpLoader.h"
 
 
 /************************************************************************/
@@ -30,23 +31,17 @@
 /************************************************************************/
 
 
-void PngLoader::run(unsigned tid)
+void WebpLoader::run(unsigned tid)
 {
-    auto width = static_cast<unsigned>(w);
-    auto height = static_cast<unsigned>(h);
-
-    state.info_raw.colortype = LCT_RGBA;   //request this image format
-
-    if (lodepng_decode(&surface.buf8, &width, &height, &state, data, size)) {
-        TVGERR("PNG", "Failed to decode image");
-    }
-
-    //setup the surface
-    surface.stride = width;
-    surface.w = width;
-    surface.h = height;
-    surface.cs = ColorSpace::ABGR8888;
+    //TODO: acquire the current colorspace format & pre-multiplied alpha image.
+    surface.buf8 = WebPDecodeBGRA(data, size, nullptr, nullptr);
+    surface.stride = (uint32_t)w;
+    surface.w = (uint32_t)w;
+    surface.h = (uint32_t)h;
     surface.channelSize = sizeof(uint32_t);
+    surface.cs = ColorSpace::ARGB8888;
+    surface.premultiplied = false;
+
 }
 
 
@@ -54,62 +49,58 @@ void PngLoader::run(unsigned tid)
 /* External Class Implementation                                        */
 /************************************************************************/
 
-PngLoader::PngLoader() : ImageLoader(FileType::Png)
+WebpLoader::WebpLoader() : ImageLoader(FileType::Webp)
 {
-    lodepng_state_init(&state);
 }
 
 
-PngLoader::~PngLoader()
+WebpLoader::~WebpLoader()
 {
+    this->done();
+
     if (freeData) free(data);
-    free(surface.buf8);
-    lodepng_state_cleanup(&state);
+    data = nullptr;
+    size = 0;
+    freeData = false;
+    WebPFree(surface.buf8);
 }
 
 
-bool PngLoader::open(const string& path)
+bool WebpLoader::open(const string& path)
 {
-    auto pngFile = fopen(path.c_str(), "rb");
-    if (!pngFile) return false;
+    auto webpFile = fopen(path.c_str(), "rb");
+    if (!webpFile) return false;
 
     auto ret = false;
 
     //determine size
-    if (fseek(pngFile, 0, SEEK_END) < 0) goto finalize;
-    if (((size = ftell(pngFile)) < 1)) goto finalize;
-    if (fseek(pngFile, 0, SEEK_SET)) goto finalize;
+    if (fseek(webpFile, 0, SEEK_END) < 0) goto finalize;
+    if (((size = ftell(webpFile)) < 1)) goto finalize;
+    if (fseek(webpFile, 0, SEEK_SET)) goto finalize;
 
     data = (unsigned char *) malloc(size);
     if (!data) goto finalize;
 
     freeData = true;
 
-    if (fread(data, size, 1, pngFile) < 1) goto finalize;
-
-    lodepng_state_init(&state);
+    if (fread(data, size, 1, webpFile) < 1) goto finalize;
 
-    unsigned int width, height;
-    if (lodepng_inspect(&width, &height, &state, data, size) > 0) goto finalize;
+    int width, height;
+    if (!WebPGetInfo(data, size, &width, &height)) goto finalize;
 
     w = static_cast<float>(width);
     h = static_cast<float>(height);
 
     ret = true;
 
-    goto finalize;
-
 finalize:
-    fclose(pngFile);
+    fclose(webpFile);
     return ret;
 }
 
 
-bool PngLoader::open(const char* data, uint32_t size, bool copy)
+bool WebpLoader::open(const char* data, uint32_t size, bool copy)
 {
-    unsigned int width, height;
-    if (lodepng_inspect(&width, &height, &state, (unsigned char*)(data), size) > 0) return false;
-
     if (copy) {
         this->data = (unsigned char *) malloc(size);
         if (!this->data) return false;
@@ -120,28 +111,32 @@ bool PngLoader::open(const char* data, uint32_t size, bool copy)
         freeData = false;
     }
 
+    int width, height;
+    if (!WebPGetInfo(this->data, size, &width, &height)) return false;
+
     w = static_cast<float>(width);
     h = static_cast<float>(height);
+    surface.cs = ColorSpace::ARGB8888;
     this->size = size;
-
     return true;
 }
 
 
-bool PngLoader::read()
+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;
 }
 
 
-Surface* PngLoader::bitmap()
+Surface* WebpLoader::bitmap()
 {
     this->done();
+
     return ImageLoader::bitmap();
 }

+ 15 - 17
thirdparty/thorvg/src/loaders/png/tvgPngLoader.h → thirdparty/thorvg/src/loaders/external_webp/tvgWebpLoader.h

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021 - 2024 the ThorVG project. All rights reserved.
+ * Copyright (c) 2023 - 2024 the ThorVG project. 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
@@ -20,32 +20,30 @@
  * SOFTWARE.
  */
 
-#ifndef _TVG_PNG_LOADER_H_
-#define _TVG_PNG_LOADER_H_
+#ifndef _TVG_WEBP_LOADER_H_
+#define _TVG_WEBP_LOADER_H_
 
-#include "tvgLodePng.h"
+#include "tvgLoader.h"
 #include "tvgTaskScheduler.h"
 
-
-class PngLoader : public ImageLoader, public Task
+class WebpLoader : public ImageLoader, public Task
 {
-private:
-    LodePNGState state;
-    unsigned char* data = nullptr;
-    unsigned long size = 0;
-    bool freeData = false;
-
-    void run(unsigned tid) override;
-
 public:
-    PngLoader();
-    ~PngLoader();
+    WebpLoader();
+    ~WebpLoader();
 
     bool open(const string& path) override;
     bool open(const char* data, uint32_t size, bool copy) override;
     bool read() override;
 
     Surface* bitmap() override;
+
+private:
+    void run(unsigned tid) override;
+
+    unsigned char* data = nullptr;
+    unsigned long size = 0;
+    bool freeData = false;
 };
 
-#endif //_TVG_PNG_LOADER_H_
+#endif //_TVG_WEBP_LOADER_H_

+ 0 - 2628
thirdparty/thorvg/src/loaders/png/tvgLodePng.cpp

@@ -1,2628 +0,0 @@
-/*
- * Copyright (c) 2020 - 2024 the ThorVG project. 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.
- */
-
-/*
-  LodePNG version 20200306
-
-  Copyright (c) 2005-2020 Lode Vandevenne
-
-  This software is provided 'as-is', without any express or implied
-  warranty. In no event will the authors be held liable for any damages
-  arising from the use of this software.
-
-  Permission is granted to anyone to use this software for any purpose,
-  including commercial applications, and to alter it and redistribute it
-  freely, subject to the following restrictions:
-
-    1. The origin of this software must not be misrepresented; you must not
-    claim that you wrote the original software. If you use this software
-    in a product, an acknowledgment in the product documentation would be
-    appreciated but is not required.
-
-    2. Altered source versions must be plainly marked as such, and must not be
-    misrepresented as being the original software.
-
-    3. This notice may not be removed or altered from any sourcedistribution.
-*/
-
-#include <cstdlib>
-#include "tvgLodePng.h"
-
-
-/************************************************************************/
-/* Internal Class Implementation                                        */
-/************************************************************************/
-
-#if defined(_MSC_VER) && (_MSC_VER >= 1310) /*Visual Studio: A few warning types are not desired here.*/
-    #pragma warning( disable : 4244 ) /*implicit conversions: not warned by gcc -Wall -Wextra and requires too much casts*/
-    #pragma warning( disable : 4996 ) /*VS does not like fopen, but fopen_s is not standard C so unusable here*/
-#endif /*_MSC_VER */
-
-
-/* convince the compiler to inline a function, for use when this measurably improves performance */
-/* inline is not available in C90, but use it when supported by the compiler */
-#if (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || (defined(__cplusplus) && (__cplusplus >= 199711L))
-    #define LODEPNG_INLINE inline
-#else
-    #define LODEPNG_INLINE /* not available */
-#endif
-
-/* restrict is not available in C90, but use it when supported by the compiler */
-#if (defined(__GNUC__) && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))) ||\
-    (defined(_MSC_VER) && (_MSC_VER >= 1400)) || \
-    (defined(__WATCOMC__) && (__WATCOMC__ >= 1250) && !defined(__cplusplus))
-    #define LODEPNG_RESTRICT __restrict
-#else
-    #define LODEPNG_RESTRICT /* not available */
-#endif
-
-#define LODEPNG_MAX(a, b) (((a) > (b)) ? (a) : (b))
-#define LODEPNG_MIN(a, b) (((a) < (b)) ? (a) : (b))
-#define LODEPNG_ABS(x) ((x) < 0 ? -(x) : (x))
-
-
-/* Replacements for C library functions such as memcpy and strlen, to support platforms
-where a full C library is not available. The compiler can recognize them and compile
-to something as fast. */
-
-static void lodepng_memcpy(void* LODEPNG_RESTRICT dst, const void* LODEPNG_RESTRICT src, size_t size)
-{
-    size_t i;
-    for (i = 0; i < size; i++) ((char*)dst)[i] = ((const char*)src)[i];
-}
-
-
-static void lodepng_memset(void* LODEPNG_RESTRICT dst, int value, size_t num)
-{
-    size_t i;
-    for (i = 0; i < num; i++) ((char*)dst)[i] = (char)value;
-}
-
-
-/* does not check memory out of bounds, do not use on untrusted data */
-static size_t lodepng_strlen(const char* a)
-{
-    const char* orig = a;
-    /* avoid warning about unused function in case of disabled COMPILE... macros */
-    (void)(&lodepng_strlen);
-    while (*a) a++;
-    return (size_t)(a - orig);
-}
-
-
-/* Safely check if adding two integers will overflow (no undefined
-behavior, compiler removing the code, etc...) and output result. */
-static int lodepng_addofl(size_t a, size_t b, size_t* result)
-{
-    *result = a + b; /* Unsigned addition is well defined and safe in C90 */
-    return *result < a;
-}
-
-
-/* Safely check if multiplying two integers will overflow (no undefined
-behavior, compiler removing the code, etc...) and output result. */
-static int lodepng_mulofl(size_t a, size_t b, size_t* result)
-{
-    *result = a * b; /* Unsigned multiplication is well defined and safe in C90 */
-    return (a != 0 && *result / a != b);
-}
-
-
-/* Safely check if a + b > c, even if overflow could happen. */
-static int lodepng_gtofl(size_t a, size_t b, size_t c)
-{
-    size_t d;
-    if (lodepng_addofl(a, b, &d)) return 1;
-    return d > c;
-}
-
-
-/*
-    Often in case of an error a value is assigned to a variable and then it breaks
-    out of a loop (to go to the cleanup phase of a function). This macro does that.
-    It makes the error handling code shorter and more readable.
-
-    Example: if(!uivector_resize(&lz77_encoded, datasize)) ERROR_BREAK(83);
-*/
-#define CERROR_BREAK(errorvar, code){\
-  errorvar = code;\
-  break;\
-}
-
-/* version of CERROR_BREAK that assumes the common case where the error variable is named "error" */
-#define ERROR_BREAK(code) CERROR_BREAK(error, code)
-
-/* Set error var to the error code, and return it.*/
-#define CERROR_RETURN_ERROR(errorvar, code){\
-  errorvar = code;\
-  return code;\
-}
-
-/* Try the code, if it returns error, also return the error. */
-#define CERROR_TRY_RETURN(call){\
-  unsigned error = call;\
-  if(error) return error;\
-}
-
-/* Set error var to the error code, and return from the void function. */
-#define CERROR_RETURN(errorvar, code){\
-  errorvar = code;\
-  return;\
-}
-
-
-/* dynamic vector of unsigned chars */
-struct ucvector
-{
-    unsigned char* data;
-    size_t size; /*used size*/
-    size_t allocsize; /*allocated size*/
-};
-
-
-/* returns 1 if success, 0 if failure ==> nothing done */
-static unsigned ucvector_resize(ucvector* p, size_t size)
-{
-    if (size > p->allocsize) {
-        size_t newsize = size + (p->allocsize >> 1u);
-        void* data = realloc(p->data, newsize);
-        if(data) {
-            p->allocsize = newsize;
-            p->data = (unsigned char*)data;
-        }
-        else return 0; /*error: not enough memory*/
-    }
-    p->size = size;
-    return 1; /*success*/
-}
-
-
-static ucvector ucvector_init(unsigned char* buffer, size_t size)
-{
-    ucvector v;
-    v.data = buffer;
-    v.allocsize = v.size = size;
-    return v;
-}
-
-
-static unsigned lodepng_read32bitInt(const unsigned char* buffer)
-{
-    return (((unsigned)buffer[0] << 24u) | ((unsigned)buffer[1] << 16u) | ((unsigned)buffer[2] << 8u) | (unsigned)buffer[3]);
-}
-
-
-/* ////////////////////////////////////////////////////////////////////////// */
-/* ////////////////////////////////////////////////////////////////////////// */
-/* // End of common code and tools. Begin of Zlib related code.            // */
-/* ////////////////////////////////////////////////////////////////////////// */
-/* ////////////////////////////////////////////////////////////////////////// */
-
-struct LodePNGBitReader
-{
-    const unsigned char* data;
-    size_t size; /*size of data in bytes*/
-    size_t bitsize; /*size of data in bits, end of valid bp values, should be 8*size*/
-    size_t bp;
-    unsigned buffer; /*buffer for reading bits. NOTE: 'unsigned' must support at least 32 bits*/
-};
-
-
-/* data size argument is in bytes. Returns error if size too large causing overflow */
-static unsigned LodePNGBitReader_init(LodePNGBitReader* reader, const unsigned char* data, size_t size)
-{
-    size_t temp;
-    reader->data = data;
-    reader->size = size;
-    /* size in bits, return error if overflow (if size_t is 32 bit this supports up to 500MB)  */
-    if (lodepng_mulofl(size, 8u, &reader->bitsize)) return 105;
-    /*ensure incremented bp can be compared to bitsize without overflow even when it would be incremented 32 too much and
-    trying to ensure 32 more bits*/
-    if (lodepng_addofl(reader->bitsize, 64u, &temp)) return 105;
-    reader->bp = 0;
-    reader->buffer = 0;
-    return 0; /*ok*/
-  }
-
-/*
-  ensureBits functions:
-  Ensures the reader can at least read nbits bits in one or more readBits calls,
-  safely even if not enough bits are available.
-  Returns 1 if there are enough bits available, 0 if not.
-*/
-
-/*See ensureBits documentation above. This one ensures exactly 1 bit */
-/*static unsigned ensureBits1(LodePNGBitReader* reader) {
-  if(reader->bp >= reader->bitsize) return 0;
-  reader->buffer = (unsigned)reader->data[reader->bp >> 3u] >> (reader->bp & 7u);
-  return 1;
-}*/
-
-/*See ensureBits documentation above. This one ensures up to 9 bits */
-static unsigned ensureBits9(LodePNGBitReader* reader, size_t nbits)
-{
-    size_t start = reader->bp >> 3u;
-    size_t size = reader->size;
-    if (start + 1u < size) {
-        reader->buffer = (unsigned)reader->data[start + 0] | ((unsigned)reader->data[start + 1] << 8u);
-        reader->buffer >>= (reader->bp & 7u);
-        return 1;
-    } else {
-        reader->buffer = 0;
-        if (start + 0u < size) reader->buffer |= reader->data[start + 0];
-        reader->buffer >>= (reader->bp & 7u);
-        return reader->bp + nbits <= reader->bitsize;
-    }
-}
-
-
-/*See ensureBits documentation above. This one ensures up to 17 bits */
-static unsigned ensureBits17(LodePNGBitReader* reader, size_t nbits)
-{
-    size_t start = reader->bp >> 3u;
-    size_t size = reader->size;
-    if (start + 2u < size) {
-        reader->buffer = (unsigned)reader->data[start + 0] | ((unsigned)reader->data[start + 1] << 8u) | ((unsigned)reader->data[start + 2] << 16u);
-        reader->buffer >>= (reader->bp & 7u);
-        return 1;
-    } else {
-        reader->buffer = 0;
-        if (start + 0u < size) reader->buffer |= reader->data[start + 0];
-        if (start + 1u < size) reader->buffer |= ((unsigned)reader->data[start + 1] << 8u);
-        reader->buffer >>= (reader->bp & 7u);
-        return reader->bp + nbits <= reader->bitsize;
-    }
-}
-
-
-/*See ensureBits documentation above. This one ensures up to 25 bits */
-static LODEPNG_INLINE unsigned ensureBits25(LodePNGBitReader* reader, size_t nbits)
-{
-    size_t start = reader->bp >> 3u;
-    size_t size = reader->size;
-    if (start + 3u < size) {
-        reader->buffer = (unsigned)reader->data[start + 0] | ((unsigned)reader->data[start + 1] << 8u) |  ((unsigned)reader->data[start + 2] << 16u) | ((unsigned)reader->data[start + 3] << 24u);
-        reader->buffer >>= (reader->bp & 7u);
-        return 1;
-    } else {
-        reader->buffer = 0;
-        if (start + 0u < size) reader->buffer |= reader->data[start + 0];
-        if (start + 1u < size) reader->buffer |= ((unsigned)reader->data[start + 1] << 8u);
-        if (start + 2u < size) reader->buffer |= ((unsigned)reader->data[start + 2] << 16u);
-        reader->buffer >>= (reader->bp & 7u);
-        return reader->bp + nbits <= reader->bitsize;
-    }
-}
-
-
-/*See ensureBits documentation above. This one ensures up to 32 bits */
-static LODEPNG_INLINE unsigned ensureBits32(LodePNGBitReader* reader, size_t nbits)
-{
-    size_t start = reader->bp >> 3u;
-    size_t size = reader->size;
-    if(start + 4u < size) {
-        reader->buffer = (unsigned)reader->data[start + 0] | ((unsigned)reader->data[start + 1] << 8u) | ((unsigned)reader->data[start + 2] << 16u) | ((unsigned)reader->data[start + 3] << 24u);
-        reader->buffer >>= (reader->bp & 7u);
-        reader->buffer |= (((unsigned)reader->data[start + 4] << 24u) << (8u - (reader->bp & 7u)));
-        return 1;
-    } else {
-        reader->buffer = 0;
-        if (start + 0u < size) reader->buffer |= reader->data[start + 0];
-        if (start + 1u < size) reader->buffer |= ((unsigned)reader->data[start + 1] << 8u);
-        if (start + 2u < size) reader->buffer |= ((unsigned)reader->data[start + 2] << 16u);
-        if (start + 3u < size) reader->buffer |= ((unsigned)reader->data[start + 3] << 24u);
-        reader->buffer >>= (reader->bp & 7u);
-        return reader->bp + nbits <= reader->bitsize;
-    }
-}
-
-
-/* Get bits without advancing the bit pointer. Must have enough bits available with ensureBits. Max nbits is 31. */
-static unsigned peekBits(LodePNGBitReader* reader, size_t nbits)
-{
-    /* The shift allows nbits to be only up to 31. */
-    return reader->buffer & ((1u << nbits) - 1u);
-}
-
-
-/* Must have enough bits available with ensureBits */
-static void advanceBits(LodePNGBitReader* reader, size_t nbits)
-{
-    reader->buffer >>= nbits;
-    reader->bp += nbits;
-}
-
-
-/* Must have enough bits available with ensureBits */
-static unsigned readBits(LodePNGBitReader* reader, size_t nbits)
-{
-    unsigned result = peekBits(reader, nbits);
-    advanceBits(reader, nbits);
-    return result;
-}
-
-
-/* Public for testing only. steps and result must have numsteps values. */
-unsigned lode_png_test_bitreader(const unsigned char* data, size_t size, size_t numsteps, const size_t* steps, unsigned* result)
-{
-    size_t i;
-    LodePNGBitReader reader;
-    unsigned error = LodePNGBitReader_init(&reader, data, size);
-    if (error) return 0;
-    for (i = 0; i < numsteps; i++) {
-        size_t step = steps[i];
-        unsigned ok;
-        if (step > 25) ok = ensureBits32(&reader, step);
-        else if (step > 17) ok = ensureBits25(&reader, step);
-        else if (step > 9) ok = ensureBits17(&reader, step);
-        else ok = ensureBits9(&reader, step);
-        if (!ok) return 0;
-        result[i] = readBits(&reader, step);
-    }
-    return 1;
-}
-
-
-static unsigned reverseBits(unsigned bits, unsigned num)
-{
-    /*TODO: implement faster lookup table based version when needed*/
-    unsigned i, result = 0;
-    for (i = 0; i < num; i++) result |= ((bits >> (num - i - 1u)) & 1u) << i;
-    return result;
-}
-
-/* ////////////////////////////////////////////////////////////////////////// */
-/* / Deflate - Huffman                                                      / */
-/* ////////////////////////////////////////////////////////////////////////// */
-
-#define FIRST_LENGTH_CODE_INDEX 257
-#define LAST_LENGTH_CODE_INDEX 285
-/*256 literals, the end code, some length codes, and 2 unused codes*/
-#define NUM_DEFLATE_CODE_SYMBOLS 288
-/*the distance codes have their own symbols, 30 used, 2 unused*/
-#define NUM_DISTANCE_SYMBOLS 32
-/*the code length codes. 0-15: code lengths, 16: copy previous 3-6 times, 17: 3-10 zeros, 18: 11-138 zeros*/
-#define NUM_CODE_LENGTH_CODES 19
-
-/*the base lengths represented by codes 257-285*/
-static const unsigned LENGTHBASE[29]
-  = {3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59,
-     67, 83, 99, 115, 131, 163, 195, 227, 258};
-
-/*the extra bits used by codes 257-285 (added to base length)*/
-static const unsigned LENGTHEXTRA[29]
-  = {0, 0, 0, 0, 0, 0, 0,  0,  1,  1,  1,  1,  2,  2,  2,  2,  3,  3,  3,  3,
-      4,  4,  4,   4,   5,   5,   5,   5,   0};
-
-/*the base backwards distances (the bits of distance codes appear after length codes and use their own huffman tree)*/
-static const unsigned DISTANCEBASE[30]
-  = {1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513,
-     769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577};
-
-/*the extra bits of backwards distances (added to base)*/
-static const unsigned DISTANCEEXTRA[30]
-  = {0, 0, 0, 0, 1, 1, 2,  2,  3,  3,  4,  4,  5,  5,   6,   6,   7,   7,   8,
-       8,    9,    9,   10,   10,   11,   11,   12,    12,    13,    13};
-
-/*the order in which "code length alphabet code lengths" are stored as specified by deflate, out of this the huffman
-tree of the dynamic huffman tree lengths is generated*/
-static const unsigned CLCL_ORDER[NUM_CODE_LENGTH_CODES]
-  = {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15};
-
-/* ////////////////////////////////////////////////////////////////////////// */
-
-/*
-Huffman tree struct, containing multiple representations of the tree
-*/
-struct HuffmanTree
-{
-    unsigned* codes; /*the huffman codes (bit patterns representing the symbols)*/
-    unsigned* lengths; /*the lengths of the huffman codes*/
-    unsigned maxbitlen; /*maximum number of bits a single code can get*/
-    unsigned numcodes; /*number of symbols in the alphabet = number of codes*/
-    /* for reading only */
-    unsigned char* table_len; /*length of symbol from lookup table, or max length if secondary lookup needed*/
-    unsigned short* table_value; /*value of symbol from lookup table, or pointer to secondary table if needed*/
-};
-
-
-static void HuffmanTree_init(HuffmanTree* tree)
-{
-    tree->codes = 0;
-    tree->lengths = 0;
-    tree->table_len = 0;
-    tree->table_value = 0;
-}
-
-
-static void HuffmanTree_cleanup(HuffmanTree* tree)
-{
-    free(tree->codes);
-    free(tree->lengths);
-    free(tree->table_len);
-    free(tree->table_value);
-}
-
-
-/* amount of bits for first huffman table lookup (aka root bits), see HuffmanTree_makeTable and huffmanDecodeSymbol.*/
-/* values 8u and 9u work the fastest */
-#define FIRSTBITS 9u
-
-/* a symbol value too big to represent any valid symbol, to indicate reading disallowed huffman bits combination,
-which is possible in case of only 0 or 1 present symbols. */
-#define INVALIDSYMBOL 65535u
-
-/* make table for huffman decoding */
-static unsigned HuffmanTree_makeTable(HuffmanTree* tree)
-{
-    static const unsigned headsize = 1u << FIRSTBITS; /*size of the first table*/
-    static const unsigned mask = (1u << FIRSTBITS) /*headsize*/ - 1u;
-    size_t i, numpresent, pointer, size; /*total table size*/
-    unsigned* maxlens = (unsigned*)malloc(headsize * sizeof(unsigned));
-    if (!maxlens) return 83; /*alloc fail*/
-
-    /* compute maxlens: max total bit length of symbols sharing prefix in the first table*/
-    lodepng_memset(maxlens, 0, headsize * sizeof(*maxlens));
-    for (i = 0; i < tree->numcodes; i++) {
-        unsigned symbol = tree->codes[i];
-        unsigned l = tree->lengths[i];
-        unsigned index;
-        if(l <= FIRSTBITS) continue; /*symbols that fit in first table don't increase secondary table size*/
-        /*get the FIRSTBITS MSBs, the MSBs of the symbol are encoded first. See later comment about the reversing*/
-        index = reverseBits(symbol >> (l - FIRSTBITS), FIRSTBITS);
-        maxlens[index] = LODEPNG_MAX(maxlens[index], l);
-    }
-    /* compute total table size: size of first table plus all secondary tables for symbols longer than FIRSTBITS */
-    size = headsize;
-    for (i = 0; i < headsize; ++i) {
-        unsigned l = maxlens[i];
-        if (l > FIRSTBITS) size += (1u << (l - FIRSTBITS));
-    }
-    tree->table_len = (unsigned char*)malloc(size * sizeof(*tree->table_len));
-    tree->table_value = (unsigned short*)malloc(size * sizeof(*tree->table_value));
-    if (!tree->table_len || !tree->table_value) {
-        free(maxlens);
-        /* freeing tree->table values is done at a higher scope */
-        return 83; /*alloc fail*/
-    }
-    /*initialize with an invalid length to indicate unused entries*/
-    for (i = 0; i < size; ++i) tree->table_len[i] = 16;
-
-    /*fill in the first table for long symbols: max prefix size and pointer to secondary tables*/
-    pointer = headsize;
-    for (i = 0; i < headsize; ++i) {
-        unsigned l = maxlens[i];
-        if(l <= FIRSTBITS) continue;
-        tree->table_len[i] = l;
-        tree->table_value[i] = pointer;
-        pointer += (1u << (l - FIRSTBITS));
-    }
-    free(maxlens);
-
-    /*fill in the first table for short symbols, or secondary table for long symbols*/
-    numpresent = 0;
-    for (i = 0; i < tree->numcodes; ++i) {
-        unsigned l = tree->lengths[i];
-        unsigned symbol = tree->codes[i]; /*the huffman bit pattern. i itself is the value.*/
-        /*reverse bits, because the huffman bits are given in MSB first order but the bit reader reads LSB first*/
-        unsigned reverse = reverseBits(symbol, l);
-        if (l == 0) continue;
-        numpresent++;
-
-        if (l <= FIRSTBITS) {
-            /*short symbol, fully in first table, replicated num times if l < FIRSTBITS*/
-            unsigned num = 1u << (FIRSTBITS - l);
-            unsigned j;
-            for (j = 0; j < num; ++j) {
-                /*bit reader will read the l bits of symbol first, the remaining FIRSTBITS - l bits go to the MSB's*/
-                unsigned index = reverse | (j << l);
-                if(tree->table_len[index] != 16) return 55; /*invalid tree: long symbol shares prefix with short symbol*/
-                tree->table_len[index] = l;
-                tree->table_value[index] = i;
-            }
-        } else {
-            /*long symbol, shares prefix with other long symbols in first lookup table, needs second lookup*/
-            /*the FIRSTBITS MSBs of the symbol are the first table index*/
-            unsigned index = reverse & mask;
-            unsigned maxlen = tree->table_len[index];
-            /*log2 of secondary table length, should be >= l - FIRSTBITS*/
-            unsigned tablelen = maxlen - FIRSTBITS;
-            unsigned start = tree->table_value[index]; /*starting index in secondary table*/
-            unsigned num = 1u << (tablelen - (l - FIRSTBITS)); /*amount of entries of this symbol in secondary table*/
-            unsigned j;
-            if (maxlen < l) return 55; /*invalid tree: long symbol shares prefix with short symbol*/
-            for (j = 0; j < num; ++j) {
-                unsigned reverse2 = reverse >> FIRSTBITS; /* l - FIRSTBITS bits */
-                unsigned index2 = start + (reverse2 | (j << (l - FIRSTBITS)));
-                tree->table_len[index2] = l;
-                tree->table_value[index2] = i;
-            }
-        }
-    }
-
-    if (numpresent < 2) {
-        /* In case of exactly 1 symbol, in theory the huffman symbol needs 0 bits,
-        but deflate uses 1 bit instead. In case of 0 symbols, no symbols can
-        appear at all, but such huffman tree could still exist (e.g. if distance
-        codes are never used). In both cases, not all symbols of the table will be
-        filled in. Fill them in with an invalid symbol value so returning them from
-        huffmanDecodeSymbol will cause error. */
-        for (i = 0; i < size; ++i) {
-            if (tree->table_len[i] == 16) {
-                /* As length, use a value smaller than FIRSTBITS for the head table,
-                and a value larger than FIRSTBITS for the secondary table, to ensure
-                valid behavior for advanceBits when reading this symbol. */
-                tree->table_len[i] = (i < headsize) ? 1 : (FIRSTBITS + 1);
-                tree->table_value[i] = INVALIDSYMBOL;
-            }
-        }
-    } else {
-        /* A good huffman tree has N * 2 - 1 nodes, of which N - 1 are internal nodes.
-        If that is not the case (due to too long length codes), the table will not
-        have been fully used, and this is an error (not all bit combinations can be
-        decoded): an oversubscribed huffman tree, indicated by error 55. */
-        for (i = 0; i < size; ++i) {
-            if (tree->table_len[i] == 16) return 55;
-        }
-    }
-    return 0;
-}
-
-
-/*
-  Second step for the ...makeFromLengths and ...makeFromFrequencies functions.
-  numcodes, lengths and maxbitlen must already be filled in correctly. return
-  value is error.
-*/
-static unsigned HuffmanTree_makeFromLengths2(HuffmanTree* tree)
-{
-    unsigned* blcount;
-    unsigned* nextcode;
-    unsigned error = 0;
-    unsigned bits, n;
-
-    tree->codes = (unsigned*)malloc(tree->numcodes * sizeof(unsigned));
-    blcount = (unsigned*)malloc((tree->maxbitlen + 1) * sizeof(unsigned));
-    nextcode = (unsigned*)malloc((tree->maxbitlen + 1) * sizeof(unsigned));
-    if (!tree->codes || !blcount || !nextcode) error = 83; /*alloc fail*/
-
-    if (!error) {
-        for (n = 0; n != tree->maxbitlen + 1; n++) blcount[n] = nextcode[n] = 0;
-        /*step 1: count number of instances of each code length*/
-        for (bits = 0; bits != tree->numcodes; ++bits) ++blcount[tree->lengths[bits]];
-        /*step 2: generate the nextcode values*/
-        for(bits = 1; bits <= tree->maxbitlen; ++bits) {
-            nextcode[bits] = (nextcode[bits - 1] + blcount[bits - 1]) << 1u;
-        }
-        /*step 3: generate all the codes*/
-        for (n = 0; n != tree->numcodes; ++n) {
-            if (tree->lengths[n] != 0) {
-                tree->codes[n] = nextcode[tree->lengths[n]]++;
-                /*remove superfluous bits from the code*/
-                tree->codes[n] &= ((1u << tree->lengths[n]) - 1u);
-            }
-        }
-    }
-
-    free(blcount);
-    free(nextcode);
-
-    if (!error) error = HuffmanTree_makeTable(tree);
-    return error;
-}
-
-
-/*
-  given the code lengths (as stored in the PNG file), generate the tree as defined
-  by Deflate. maxbitlen is the maximum bits that a code in the tree can have.
-  return value is error.
-*/
-static unsigned HuffmanTree_makeFromLengths(HuffmanTree* tree, const unsigned* bitlen, size_t numcodes, unsigned maxbitlen)
-{
-    unsigned i;
-    tree->lengths = (unsigned*)malloc(numcodes * sizeof(unsigned));
-    if (!tree->lengths) return 83; /*alloc fail*/
-    for (i = 0; i != numcodes; ++i) tree->lengths[i] = bitlen[i];
-    tree->numcodes = (unsigned)numcodes; /*number of symbols*/
-    tree->maxbitlen = maxbitlen;
-    return HuffmanTree_makeFromLengths2(tree);
-}
-
-
-/*get the literal and length code tree of a deflated block with fixed tree, as per the deflate specification*/
-static unsigned generateFixedLitLenTree(HuffmanTree* tree)
-{
-    unsigned i, error = 0;
-    unsigned* bitlen = (unsigned*)malloc(NUM_DEFLATE_CODE_SYMBOLS * sizeof(unsigned));
-    if (!bitlen) return 83; /*alloc fail*/
-
-    /*288 possible codes: 0-255=literals, 256=endcode, 257-285=lengthcodes, 286-287=unused*/
-    for (i =   0; i <= 143; ++i) bitlen[i] = 8;
-    for (i = 144; i <= 255; ++i) bitlen[i] = 9;
-    for (i = 256; i <= 279; ++i) bitlen[i] = 7;
-    for (i = 280; i <= 287; ++i) bitlen[i] = 8;
-
-    error = HuffmanTree_makeFromLengths(tree, bitlen, NUM_DEFLATE_CODE_SYMBOLS, 15);
-
-    free(bitlen);
-    return error;
-}
-
-
-/*get the distance code tree of a deflated block with fixed tree, as specified in the deflate specification*/
-static unsigned generateFixedDistanceTree(HuffmanTree* tree)
-{
-    unsigned i, error = 0;
-    unsigned* bitlen = (unsigned*)malloc(NUM_DISTANCE_SYMBOLS * sizeof(unsigned));
-    if (!bitlen) return 83; /*alloc fail*/
-
-    /*there are 32 distance codes, but 30-31 are unused*/
-    for (i = 0; i != NUM_DISTANCE_SYMBOLS; ++i) bitlen[i] = 5;
-    error = HuffmanTree_makeFromLengths(tree, bitlen, NUM_DISTANCE_SYMBOLS, 15);
-
-    free(bitlen);
-    return error;
-}
-
-
-/*
-  returns the code. The bit reader must already have been ensured at least 15 bits
-*/
-static unsigned huffmanDecodeSymbol(LodePNGBitReader* reader, const HuffmanTree* codetree)
-{
-    unsigned short code = peekBits(reader, FIRSTBITS);
-    unsigned short l = codetree->table_len[code];
-    unsigned short value = codetree->table_value[code];
-    if (l <= FIRSTBITS) {
-        advanceBits(reader, l);
-        return value;
-    } else {
-        unsigned index2;
-        advanceBits(reader, FIRSTBITS);
-        index2 = value + peekBits(reader, l - FIRSTBITS);
-        advanceBits(reader, codetree->table_len[index2] - FIRSTBITS);
-        return codetree->table_value[index2];
-    }
-}
-
-
-/* ////////////////////////////////////////////////////////////////////////// */
-/* / Inflator (Decompressor)                                                / */
-/* ////////////////////////////////////////////////////////////////////////// */
-
-/*get the tree of a deflated block with fixed tree, as specified in the deflate specification
-Returns error code.*/
-static unsigned getTreeInflateFixed(HuffmanTree* tree_ll, HuffmanTree* tree_d)
-{
-    unsigned error = generateFixedLitLenTree(tree_ll);
-    if (error) return error;
-    return generateFixedDistanceTree(tree_d);
-}
-
-
-/*get the tree of a deflated block with dynamic tree, the tree itself is also Huffman compressed with a known tree*/
-static unsigned getTreeInflateDynamic(HuffmanTree* tree_ll, HuffmanTree* tree_d, LodePNGBitReader* reader)
-{
-    /*make sure that length values that aren't filled in will be 0, or a wrong tree will be generated*/
-    unsigned error = 0;
-    unsigned n, HLIT, HDIST, HCLEN, i;
-
-    /*see comments in deflateDynamic for explanation of the context and these variables, it is analogous*/
-    unsigned* bitlen_ll = 0; /*lit,len code lengths*/
-    unsigned* bitlen_d = 0; /*dist code lengths*/
-    /*code length code lengths ("clcl"), the bit lengths of the huffman tree used to compress bitlen_ll and bitlen_d*/
-    unsigned* bitlen_cl = 0;
-    HuffmanTree tree_cl; /*the code tree for code length codes (the huffman tree for compressed huffman trees)*/
-
-    if (!ensureBits17(reader, 14)) return 49; /*error: the bit pointer is or will go past the memory*/
-
-    /*number of literal/length codes + 257. Unlike the spec, the value 257 is added to it here already*/
-    HLIT =  readBits(reader, 5) + 257;
-    /*number of distance codes. Unlike the spec, the value 1 is added to it here already*/
-    HDIST = readBits(reader, 5) + 1;
-    /*number of code length codes. Unlike the spec, the value 4 is added to it here already*/
-    HCLEN = readBits(reader, 4) + 4;
-
-    bitlen_cl = (unsigned*)malloc(NUM_CODE_LENGTH_CODES * sizeof(unsigned));
-    if(!bitlen_cl) return 83 /*alloc fail*/;
-
-    HuffmanTree_init(&tree_cl);
-
-    while (!error) {
-        /*read the code length codes out of 3 * (amount of code length codes) bits*/
-        if (lodepng_gtofl(reader->bp, HCLEN * 3, reader->bitsize)) {
-            ERROR_BREAK(50); /*error: the bit pointer is or will go past the memory*/
-        }
-        for (i = 0; i != HCLEN; ++i) {
-            ensureBits9(reader, 3); /*out of bounds already checked above */
-            bitlen_cl[CLCL_ORDER[i]] = readBits(reader, 3);
-        }
-        for (i = HCLEN; i != NUM_CODE_LENGTH_CODES; ++i) {
-            bitlen_cl[CLCL_ORDER[i]] = 0;
-        }
-
-        error = HuffmanTree_makeFromLengths(&tree_cl, bitlen_cl, NUM_CODE_LENGTH_CODES, 7);
-        if(error) break;
-
-        /*now we can use this tree to read the lengths for the tree that this function will return*/
-        bitlen_ll = (unsigned*)malloc(NUM_DEFLATE_CODE_SYMBOLS * sizeof(unsigned));
-        bitlen_d = (unsigned*)malloc(NUM_DISTANCE_SYMBOLS * sizeof(unsigned));
-        if (!bitlen_ll || !bitlen_d) ERROR_BREAK(83 /*alloc fail*/);
-        lodepng_memset(bitlen_ll, 0, NUM_DEFLATE_CODE_SYMBOLS * sizeof(*bitlen_ll));
-        lodepng_memset(bitlen_d, 0, NUM_DISTANCE_SYMBOLS * sizeof(*bitlen_d));
-
-        /*i is the current symbol we're reading in the part that contains the code lengths of lit/len and dist codes*/
-        i = 0;
-        while (i < HLIT + HDIST) {
-            unsigned code;
-            ensureBits25(reader, 22); /* up to 15 bits for huffman code, up to 7 extra bits below*/
-            code = huffmanDecodeSymbol(reader, &tree_cl);
-            if (code <= 15) /*a length code*/ {
-                if (i < HLIT) bitlen_ll[i] = code;
-                else bitlen_d[i - HLIT] = code;
-                ++i;
-            } else if (code == 16) /*repeat previous*/ {
-                unsigned replength = 3; /*read in the 2 bits that indicate repeat length (3-6)*/
-                unsigned value; /*set value to the previous code*/
-
-                if (i == 0) ERROR_BREAK(54); /*can't repeat previous if i is 0*/
-
-                replength += readBits(reader, 2);
-
-                if (i < HLIT + 1) value = bitlen_ll[i - 1];
-                else value = bitlen_d[i - HLIT - 1];
-                /*repeat this value in the next lengths*/
-                for (n = 0; n < replength; ++n) {
-                    if (i >= HLIT + HDIST) ERROR_BREAK(13); /*error: i is larger than the amount of codes*/
-                    if (i < HLIT) bitlen_ll[i] = value;
-                    else bitlen_d[i - HLIT] = value;
-                    ++i;
-                }
-            } else if(code == 17) /*repeat "0" 3-10 times*/ {
-                unsigned replength = 3; /*read in the bits that indicate repeat length*/
-                replength += readBits(reader, 3);
-
-                /*repeat this value in the next lengths*/
-                for (n = 0; n < replength; ++n) {
-                    if (i >= HLIT + HDIST) ERROR_BREAK(14); /*error: i is larger than the amount of codes*/
-
-                    if (i < HLIT) bitlen_ll[i] = 0;
-                    else bitlen_d[i - HLIT] = 0;
-                    ++i;
-                }
-            } else if(code == 18) /*repeat "0" 11-138 times*/ {
-                unsigned replength = 11; /*read in the bits that indicate repeat length*/
-                replength += readBits(reader, 7);
-
-                /*repeat this value in the next lengths*/
-                for (n = 0; n < replength; ++n) {
-                    if(i >= HLIT + HDIST) ERROR_BREAK(15); /*error: i is larger than the amount of codes*/
-
-                    if(i < HLIT) bitlen_ll[i] = 0;
-                    else bitlen_d[i - HLIT] = 0;
-                    ++i;
-                }
-            } else /*if(code == INVALIDSYMBOL)*/ {
-                ERROR_BREAK(16); /*error: tried to read disallowed huffman symbol*/
-            }
-            /*check if any of the ensureBits above went out of bounds*/
-            if (reader->bp > reader->bitsize) {
-                /*return error code 10 or 11 depending on the situation that happened in huffmanDecodeSymbol
-                (10=no endcode, 11=wrong jump outside of tree)*/
-                /* TODO: revise error codes 10,11,50: the above comment is no longer valid */
-                ERROR_BREAK(50); /*error, bit pointer jumps past memory*/
-            }
-        }
-        if (error) break;
-
-        if (bitlen_ll[256] == 0) ERROR_BREAK(64); /*the length of the end code 256 must be larger than 0*/
-
-        /*now we've finally got HLIT and HDIST, so generate the code trees, and the function is done*/
-        error = HuffmanTree_makeFromLengths(tree_ll, bitlen_ll, NUM_DEFLATE_CODE_SYMBOLS, 15);
-        if (error) break;
-        error = HuffmanTree_makeFromLengths(tree_d, bitlen_d, NUM_DISTANCE_SYMBOLS, 15);
-
-        break; /*end of error-while*/
-    }
-
-    free(bitlen_cl);
-    free(bitlen_ll);
-    free(bitlen_d);
-    HuffmanTree_cleanup(&tree_cl);
-
-    return error;
-}
-
-
-/*inflate a block with dynamic of fixed Huffman tree. btype must be 1 or 2.*/
-static unsigned inflateHuffmanBlock(ucvector* out, LodePNGBitReader* reader, unsigned btype)
-{
-    unsigned error = 0;
-    HuffmanTree tree_ll; /*the huffman tree for literal and length codes*/
-    HuffmanTree tree_d; /*the huffman tree for distance codes*/
-
-    HuffmanTree_init(&tree_ll);
-    HuffmanTree_init(&tree_d);
-
-    if (btype == 1) error = getTreeInflateFixed(&tree_ll, &tree_d);
-    else /*if(btype == 2)*/ error = getTreeInflateDynamic(&tree_ll, &tree_d, reader);
-
-    while (!error) /*decode all symbols until end reached, breaks at end code*/ {
-        /*code_ll is literal, length or end code*/
-        unsigned code_ll;
-        ensureBits25(reader, 20); /* up to 15 for the huffman symbol, up to 5 for the length extra bits */
-        code_ll = huffmanDecodeSymbol(reader, &tree_ll);
-        if (code_ll <= 255) /*literal symbol*/ {
-            if (!ucvector_resize(out, out->size + 1)) ERROR_BREAK(83 /*alloc fail*/);
-            out->data[out->size - 1] = (unsigned char)code_ll;
-        } else if (code_ll >= FIRST_LENGTH_CODE_INDEX && code_ll <= LAST_LENGTH_CODE_INDEX) /*length code*/ {
-            unsigned code_d, distance;
-            unsigned numextrabits_l, numextrabits_d; /*extra bits for length and distance*/
-            size_t start, backward, length;
-
-            /*part 1: get length base*/
-            length = LENGTHBASE[code_ll - FIRST_LENGTH_CODE_INDEX];
-
-            /*part 2: get extra bits and add the value of that to length*/
-            numextrabits_l = LENGTHEXTRA[code_ll - FIRST_LENGTH_CODE_INDEX];
-            if (numextrabits_l != 0) {
-                /* bits already ensured above */
-                length += readBits(reader, numextrabits_l);
-            }
-
-            /*part 3: get distance code*/
-            ensureBits32(reader, 28); /* up to 15 for the huffman symbol, up to 13 for the extra bits */
-            code_d = huffmanDecodeSymbol(reader, &tree_d);
-            if (code_d > 29) {
-                if (code_d <= 31) {
-                    ERROR_BREAK(18); /*error: invalid distance code (30-31 are never used)*/
-                } else /* if(code_d == INVALIDSYMBOL) */{
-                    ERROR_BREAK(16); /*error: tried to read disallowed huffman symbol*/
-                }
-            }
-            distance = DISTANCEBASE[code_d];
-
-            /*part 4: get extra bits from distance*/
-            numextrabits_d = DISTANCEEXTRA[code_d];
-            if (numextrabits_d != 0) {
-                /* bits already ensured above */
-                distance += readBits(reader, numextrabits_d);
-            }
-
-            /*part 5: fill in all the out[n] values based on the length and dist*/
-            start = out->size;
-            if (distance > start) ERROR_BREAK(52); /*too long backward distance*/
-            backward = start - distance;
-
-            if (!ucvector_resize(out, out->size + length)) ERROR_BREAK(83 /*alloc fail*/);
-            if (distance < length) {
-                size_t forward;
-                lodepng_memcpy(out->data + start, out->data + backward, distance);
-                start += distance;
-                for (forward = distance; forward < length; ++forward) {
-                  out->data[start++] = out->data[backward++];
-                }
-            } else {
-                lodepng_memcpy(out->data + start, out->data + backward, length);
-            }
-        } else if (code_ll == 256) {
-            break; /*end code, break the loop*/
-        } else /*if(code_ll == INVALIDSYMBOL)*/ {
-            ERROR_BREAK(16); /*error: tried to read disallowed huffman symbol*/
-        }
-        /*check if any of the ensureBits above went out of bounds*/
-        if (reader->bp > reader->bitsize) {
-          /*return error code 10 or 11 depending on the situation that happened in huffmanDecodeSymbol
-          (10=no endcode, 11=wrong jump outside of tree)*/
-          /* TODO: revise error codes 10,11,50: the above comment is no longer valid */
-          ERROR_BREAK(51); /*error, bit pointer jumps past memory*/
-        }
-    }
-
-    HuffmanTree_cleanup(&tree_ll);
-    HuffmanTree_cleanup(&tree_d);
-
-    return error;
-}
-
-
-static unsigned inflateNoCompression(ucvector* out, LodePNGBitReader* reader, const LodePNGDecompressSettings* settings)
-{
-    size_t bytepos;
-    size_t size = reader->size;
-    unsigned LEN, NLEN, error = 0;
-
-    /*go to first boundary of byte*/
-    bytepos = (reader->bp + 7u) >> 3u;
-
-    /*read LEN (2 bytes) and NLEN (2 bytes)*/
-    if (bytepos + 4 >= size) return 52; /*error, bit pointer will jump past memory*/
-    LEN = (unsigned)reader->data[bytepos] + ((unsigned)reader->data[bytepos + 1] << 8u); bytepos += 2;
-    NLEN = (unsigned)reader->data[bytepos] + ((unsigned)reader->data[bytepos + 1] << 8u); bytepos += 2;
-
-    /*check if 16-bit NLEN is really the one's complement of LEN*/
-    if (!settings->ignore_nlen && LEN + NLEN != 65535) {
-        return 21; /*error: NLEN is not one's complement of LEN*/
-    }
-
-    if (!ucvector_resize(out, out->size + LEN)) return 83; /*alloc fail*/
-
-    /*read the literal data: LEN bytes are now stored in the out buffer*/
-    if (bytepos + LEN > size) return 23; /*error: reading outside of in buffer*/
-
-    lodepng_memcpy(out->data + out->size - LEN, reader->data + bytepos, LEN);
-    bytepos += LEN;
-
-    reader->bp = bytepos << 3u;
-
-    return error;
-}
-
-
-static unsigned lodepng_inflatev(ucvector* out, const unsigned char* in, size_t insize, const LodePNGDecompressSettings* settings)
-{
-    unsigned BFINAL = 0;
-    LodePNGBitReader reader;
-    unsigned error = LodePNGBitReader_init(&reader, in, insize);
-
-    if (error) return error;
-
-    while (!BFINAL) {
-        unsigned BTYPE;
-        if (!ensureBits9(&reader, 3)) return 52; /*error, bit pointer will jump past memory*/
-        BFINAL = readBits(&reader, 1);
-        BTYPE = readBits(&reader, 2);
-
-        if (BTYPE == 3) return 20; /*error: invalid BTYPE*/
-        else if (BTYPE == 0) error = inflateNoCompression(out, &reader, settings); /*no compression*/
-        else error = inflateHuffmanBlock(out, &reader, BTYPE); /*compression, BTYPE 01 or 10*/
-
-        if (error) return error;
-    }
-
-    return error;
-}
-
-
-static unsigned inflatev(ucvector* out, const unsigned char* in, size_t insize, const LodePNGDecompressSettings* settings)
-{
-    if (settings->custom_inflate) {
-        unsigned error = settings->custom_inflate(&out->data, &out->size, in, insize, settings);
-        out->allocsize = out->size;
-        return error;
-    } else {
-        return lodepng_inflatev(out, in, insize, settings);
-    }
-}
-
-
-/* ////////////////////////////////////////////////////////////////////////// */
-/* / Adler32                                                                / */
-/* ////////////////////////////////////////////////////////////////////////// */
-
-static unsigned update_adler32(unsigned adler, const unsigned char* data, unsigned len)
-{
-    unsigned s1 = adler & 0xffffu;
-    unsigned s2 = (adler >> 16u) & 0xffffu;
-
-    while (len != 0u) {
-        unsigned i;
-        /*at least 5552 sums can be done before the sums overflow, saving a lot of module divisions*/
-        unsigned amount = len > 5552u ? 5552u : len;
-        len -= amount;
-        for (i = 0; i != amount; ++i) {
-            s1 += (*data++);
-            s2 += s1;
-        }
-        s1 %= 65521u;
-        s2 %= 65521u;
-    }
-
-    return (s2 << 16u) | s1;
-}
-
-/*Return the adler32 of the bytes data[0..len-1]*/
-static unsigned adler32(const unsigned char* data, unsigned len)
-{
-    return update_adler32(1u, data, len);
-}
-
-/* ////////////////////////////////////////////////////////////////////////// */
-/* / Zlib                                                                   / */
-/* ////////////////////////////////////////////////////////////////////////// */
-
-static unsigned lodepng_zlib_decompressv(ucvector* out, const unsigned char* in, size_t insize, const LodePNGDecompressSettings* settings)
-{
-    unsigned error = 0;
-    unsigned CM, CINFO, FDICT;
-
-    if (insize < 2) return 53; /*error, size of zlib data too small*/
-    /*read information from zlib header*/
-    if ((in[0] * 256 + in[1]) % 31 != 0) {
-        /*error: 256 * in[0] + in[1] must be a multiple of 31, the FCHECK value is supposed to be made that way*/
-        return 24;
-    }
-
-    CM = in[0] & 15;
-    CINFO = (in[0] >> 4) & 15;
-    /*FCHECK = in[1] & 31;*/ /*FCHECK is already tested above*/
-    FDICT = (in[1] >> 5) & 1;
-    /*FLEVEL = (in[1] >> 6) & 3;*/ /*FLEVEL is not used here*/
-
-    if (CM != 8 || CINFO > 7) {
-        /*error: only compression method 8: inflate with sliding window of 32k is supported by the PNG spec*/
-        return 25;
-    }
-    if (FDICT != 0) {
-        /*error: the specification of PNG says about the zlib stream:
-          "The additional flags shall not specify a preset dictionary."*/
-        return 26;
-    }
-
-    error = inflatev(out, in + 2, insize - 2, settings);
-    if (error) return error;
-
-    if (!settings->ignore_adler32) {
-        unsigned ADLER32 = lodepng_read32bitInt(&in[insize - 4]);
-        unsigned checksum = adler32(out->data, (unsigned)(out->size));
-        if(checksum != ADLER32) return 58; /*error, adler checksum not correct, data must be corrupted*/
-    }
-
-    return 0; /*no error*/
-}
-
-
-/*expected_size is expected output size, to avoid intermediate allocations. Set to 0 if not known. */
-static unsigned zlib_decompress(unsigned char** out, size_t* outsize, size_t expected_size, const unsigned char* in, size_t insize, const LodePNGDecompressSettings* settings)
-{
-    if(settings->custom_zlib) {
-        return settings->custom_zlib(out, outsize, in, insize, settings);
-    } else {
-        unsigned error;
-        ucvector v = ucvector_init(*out, *outsize);
-        if (expected_size) {
-            /*reserve the memory to avoid intermediate reallocations*/
-            ucvector_resize(&v, *outsize + expected_size);
-            v.size = *outsize;
-        }
-        error = lodepng_zlib_decompressv(&v, in, insize, settings);
-        *out = v.data;
-        *outsize = v.size;
-        return error;
-    }
-}
-
-
-static void lodepng_decompress_settings_init(LodePNGDecompressSettings* settings)
-{
-    settings->ignore_adler32 = 0;
-    settings->ignore_nlen = 0;
-    settings->custom_zlib = 0;
-    settings->custom_inflate = 0;
-    settings->custom_context = 0;
-}
-
-
-/* ////////////////////////////////////////////////////////////////////////// */
-/* ////////////////////////////////////////////////////////////////////////// */
-/* // End of Zlib related code. Begin of PNG related code.                 // */
-/* ////////////////////////////////////////////////////////////////////////// */
-/* ////////////////////////////////////////////////////////////////////////// */
-
-
-#if 0 //thorvg don't use crc
-/* CRC polynomial: 0xedb88320 */
-static unsigned lodepng_crc32_table[256] = {
-            0u, 1996959894u, 3993919788u, 2567524794u,  124634137u, 1886057615u, 3915621685u, 2657392035u,
-    249268274u, 2044508324u, 3772115230u, 2547177864u,  162941995u, 2125561021u, 3887607047u, 2428444049u,
-    498536548u, 1789927666u, 4089016648u, 2227061214u,  450548861u, 1843258603u, 4107580753u, 2211677639u,
-    325883990u, 1684777152u, 4251122042u, 2321926636u,  335633487u, 1661365465u, 4195302755u, 2366115317u,
-    997073096u, 1281953886u, 3579855332u, 2724688242u, 1006888145u, 1258607687u, 3524101629u, 2768942443u,
-    901097722u, 1119000684u, 3686517206u, 2898065728u,  853044451u, 1172266101u, 3705015759u, 2882616665u,
-    651767980u, 1373503546u, 3369554304u, 3218104598u,  565507253u, 1454621731u, 3485111705u, 3099436303u,
-    671266974u, 1594198024u, 3322730930u, 2970347812u,  795835527u, 1483230225u, 3244367275u, 3060149565u,
-    1994146192u,   31158534u, 2563907772u, 4023717930u, 1907459465u,  112637215u, 2680153253u, 3904427059u,
-    2013776290u,  251722036u, 2517215374u, 3775830040u, 2137656763u,  141376813u, 2439277719u, 3865271297u,
-    1802195444u,  476864866u, 2238001368u, 4066508878u, 1812370925u,  453092731u, 2181625025u, 4111451223u,
-    1706088902u,  314042704u, 2344532202u, 4240017532u, 1658658271u,  366619977u, 2362670323u, 4224994405u,
-    1303535960u,  984961486u, 2747007092u, 3569037538u, 1256170817u, 1037604311u, 2765210733u, 3554079995u,
-    1131014506u,  879679996u, 2909243462u, 3663771856u, 1141124467u,  855842277u, 2852801631u, 3708648649u,
-    1342533948u,  654459306u, 3188396048u, 3373015174u, 1466479909u,  544179635u, 3110523913u, 3462522015u,
-    1591671054u,  702138776u, 2966460450u, 3352799412u, 1504918807u,  783551873u, 3082640443u, 3233442989u,
-    3988292384u, 2596254646u,   62317068u, 1957810842u, 3939845945u, 2647816111u,   81470997u, 1943803523u,
-    3814918930u, 2489596804u,  225274430u, 2053790376u, 3826175755u, 2466906013u,  167816743u, 2097651377u,
-    4027552580u, 2265490386u,  503444072u, 1762050814u, 4150417245u, 2154129355u,  426522225u, 1852507879u,
-    4275313526u, 2312317920u,  282753626u, 1742555852u, 4189708143u, 2394877945u,  397917763u, 1622183637u,
-    3604390888u, 2714866558u,  953729732u, 1340076626u, 3518719985u, 2797360999u, 1068828381u, 1219638859u,
-    3624741850u, 2936675148u,  906185462u, 1090812512u, 3747672003u, 2825379669u,  829329135u, 1181335161u,
-    3412177804u, 3160834842u,  628085408u, 1382605366u, 3423369109u, 3138078467u,  570562233u, 1426400815u,
-    3317316542u, 2998733608u,  733239954u, 1555261956u, 3268935591u, 3050360625u,  752459403u, 1541320221u,
-    2607071920u, 3965973030u, 1969922972u,   40735498u, 2617837225u, 3943577151u, 1913087877u,   83908371u,
-    2512341634u, 3803740692u, 2075208622u,  213261112u, 2463272603u, 3855990285u, 2094854071u,  198958881u,
-    2262029012u, 4057260610u, 1759359992u,  534414190u, 2176718541u, 4139329115u, 1873836001u,  414664567u,
-    2282248934u, 4279200368u, 1711684554u,  285281116u, 2405801727u, 4167216745u, 1634467795u,  376229701u,
-    2685067896u, 3608007406u, 1308918612u,  956543938u, 2808555105u, 3495958263u, 1231636301u, 1047427035u,
-    2932959818u, 3654703836u, 1088359270u,  936918000u, 2847714899u, 3736837829u, 1202900863u,  817233897u,
-    3183342108u, 3401237130u, 1404277552u,  615818150u, 3134207493u, 3453421203u, 1423857449u,  601450431u,
-    3009837614u, 3294710456u, 1567103746u,  711928724u, 3020668471u, 3272380065u, 1510334235u,  755167117u
-};
-
-
-/* Calculate CRC32 of buffer
-   Return the CRC of the bytes buf[0..len-1]. */
-static unsigned lodepng_crc32(const unsigned char* data, size_t length)
-{
-    unsigned r = 0xffffffffu;
-    size_t i;
-    for (i = 0; i < length; ++i) {
-        r = lodepng_crc32_table[(r ^ data[i]) & 0xffu] ^ (r >> 8u);
-    }
-    return r ^ 0xffffffffu;
-}
-#endif
-
-/* ////////////////////////////////////////////////////////////////////////// */
-/* / Reading and writing PNG color channel bits                             / */
-/* ////////////////////////////////////////////////////////////////////////// */
-
-/* The color channel bits of less-than-8-bit pixels are read with the MSB of bytes first,
-so LodePNGBitWriter and LodePNGBitReader can't be used for those. */
-
-static unsigned char readBitFromReversedStream(size_t* bitpointer, const unsigned char* bitstream)
-{
-    unsigned char result = (unsigned char)((bitstream[(*bitpointer) >> 3] >> (7 - ((*bitpointer) & 0x7))) & 1);
-    ++(*bitpointer);
-    return result;
-}
-
-
-/* TODO: make this faster */
-static unsigned readBitsFromReversedStream(size_t* bitpointer, const unsigned char* bitstream, size_t nbits)
-{
-    unsigned result = 0;
-    size_t i;
-    for (i = 0 ; i < nbits; ++i) {
-        result <<= 1u;
-        result |= (unsigned)readBitFromReversedStream(bitpointer, bitstream);
-    }
-    return result;
-}
-
-
-static void setBitOfReversedStream(size_t* bitpointer, unsigned char* bitstream, unsigned char bit)
-{
-    /*the current bit in bitstream may be 0 or 1 for this to work*/
-    if (bit == 0) bitstream[(*bitpointer) >> 3u] &=  (unsigned char)(~(1u << (7u - ((*bitpointer) & 7u))));
-    else bitstream[(*bitpointer) >> 3u] |=  (1u << (7u - ((*bitpointer) & 7u)));
-    ++(*bitpointer);
-}
-
-/* ////////////////////////////////////////////////////////////////////////// */
-/* / PNG chunks                                                             / */
-/* ////////////////////////////////////////////////////////////////////////// */
-
-/*
-  The lodepng_chunk functions are normally not needed, except to traverse the
-  unknown chunks stored in the LodePNGInfo struct, or add new ones to it.
-  It also allows traversing the chunks of an encoded PNG file yourself.
-
-  The chunk pointer always points to the beginning of the chunk itself, that is
-  the first byte of the 4 length bytes.
-
-  In the PNG file format, chunks have the following format:
-  -4 bytes length: length of the data of the chunk in bytes (chunk itself is 12 bytes longer)
-  -4 bytes chunk type (ASCII a-z,A-Z only, see below)
-  -length bytes of data (may be 0 bytes if length was 0)
-  -4 bytes of CRC, computed on chunk name + data
-
-  The first chunk starts at the 8th byte of the PNG file, the entire rest of the file
-  exists out of concatenated chunks with the above format.
-
-  PNG standard chunk ASCII naming conventions:
-  -First byte: uppercase = critical, lowercase = ancillary
-  -Second byte: uppercase = public, lowercase = private
-  -Third byte: must be uppercase
-  -Fourth byte: uppercase = unsafe to copy, lowercase = safe to copy
-*/
-
-
-/*
-  Gets the length of the data of the chunk. Total chunk length has 12 bytes more.
-  There must be at least 4 bytes to read from. If the result value is too large,
-  it may be corrupt data.
-*/
-static unsigned lodepng_chunk_length(const unsigned char* chunk)
-{
-    return lodepng_read32bitInt(&chunk[0]);
-}
-
-
-/* check if the type is the given type */
-static unsigned char lodepng_chunk_type_equals(const unsigned char* chunk, const char* type)
-{
-    if (lodepng_strlen(type) != 4) return 0;
-    return (chunk[4] == type[0] && chunk[5] == type[1] && chunk[6] == type[2] && chunk[7] == type[3]);
-}
-
-
-/* 0: it's one of the critical chunk types, 1: it's an ancillary chunk (see PNG standard) */
-static unsigned char lodepng_chunk_ancillary(const unsigned char* chunk)
-{
-    return ((chunk[4] & 32) != 0);
-}
-
-
-static const unsigned char* lodepng_chunk_data_const(const unsigned char* chunk)
-{
-    return &chunk[8];
-}
-
-#if 0 //thorvg don't use crc
-/* returns 0 if the crc is correct, 1 if it's incorrect (0 for OK as usual!) */
-static unsigned lodepng_chunk_check_crc(const unsigned char* chunk)
-{
-    unsigned length = lodepng_chunk_length(chunk);
-    unsigned CRC = lodepng_read32bitInt(&chunk[length + 8]);
-    /*the CRC is taken of the data and the 4 chunk type letters, not the length*/
-    unsigned checksum = lodepng_crc32(&chunk[4], length + 4);
-    if (CRC != checksum) return 1;
-    else return 0;
-}
-#endif
-
-static const unsigned char* lodepng_chunk_next_const(const unsigned char* chunk, const unsigned char* end)
-{
-    if (chunk >= end || end - chunk < 12) return end; /*too small to contain a chunk*/
-    if (chunk[0] == 0x89 && chunk[1] == 0x50 && chunk[2] == 0x4e && chunk[3] == 0x47
-        && chunk[4] == 0x0d && chunk[5] == 0x0a && chunk[6] == 0x1a && chunk[7] == 0x0a) {
-        /* Is PNG magic header at start of PNG file. Jump to first actual chunk. */
-        return chunk + 8;
-    } else {
-        size_t total_chunk_length;
-        const unsigned char* result;
-        if (lodepng_addofl(lodepng_chunk_length(chunk), 12, &total_chunk_length)) return end;
-        result = chunk + total_chunk_length;
-        if (result < chunk) return end; /*pointer overflow*/
-        return result;
-    }
-}
-
-
-/* ////////////////////////////////////////////////////////////////////////// */
-/* / Color types, channels, bits                                            / */
-/* ////////////////////////////////////////////////////////////////////////// */
-
-/*checks if the colortype is valid and the bitdepth bd is allowed for this colortype.
-Return value is a LodePNG error code.*/
-static unsigned checkColorValidity(LodePNGColorType colortype, unsigned bd)
-{
-    switch(colortype) {
-        case LCT_GREY:       if(!(bd == 1 || bd == 2 || bd == 4 || bd == 8 || bd == 16)) return 37; break;
-        case LCT_RGB:        if(!(                                 bd == 8 || bd == 16)) return 37; break;
-        case LCT_PALETTE:    if(!(bd == 1 || bd == 2 || bd == 4 || bd == 8            )) return 37; break;
-        case LCT_GREY_ALPHA: if(!(                                 bd == 8 || bd == 16)) return 37; break;
-        case LCT_RGBA:       if(!(                                 bd == 8 || bd == 16)) return 37; break;
-        case LCT_MAX_OCTET_VALUE: return 31; /* invalid color type */
-        default: return 31; /* invalid color type */
-    }
-    return 0; /*allowed color type / bits combination*/
-}
-
-
-static unsigned getNumColorChannels(LodePNGColorType colortype)
-{
-    switch(colortype) {
-        case LCT_GREY: return 1;
-        case LCT_RGB: return 3;
-        case LCT_PALETTE: return 1;
-        case LCT_GREY_ALPHA: return 2;
-        case LCT_RGBA: return 4;
-        case LCT_MAX_OCTET_VALUE: return 0; /* invalid color type */
-        default: return 0; /*invalid color type*/
-    }
-}
-
-
-static unsigned lodepng_get_bpp_lct(LodePNGColorType colortype, unsigned bitdepth)
-{
-    /*bits per pixel is amount of channels * bits per channel*/
-    return getNumColorChannels(colortype) * bitdepth;
-}
-
-
-static void lodepng_color_mode_init(LodePNGColorMode* info)
-{
-    info->key_defined = 0;
-    info->key_r = info->key_g = info->key_b = 0;
-    info->colortype = LCT_RGBA;
-    info->bitdepth = 8;
-    info->palette = 0;
-    info->palettesize = 0;
-}
-
-
-/*allocates palette memory if needed, and initializes all colors to black*/
-static void lodepng_color_mode_alloc_palette(LodePNGColorMode* info)
-{
-    size_t i;
-    /*if the palette is already allocated, it will have size 1024 so no reallocation needed in that case*/
-    /*the palette must have room for up to 256 colors with 4 bytes each.*/
-    if (!info->palette) info->palette = (unsigned char*)malloc(1024);
-    if (!info->palette) return; /*alloc fail*/
-    for (i = 0; i != 256; ++i) {
-        /*Initialize all unused colors with black, the value used for invalid palette indices.
-        This is an error according to the PNG spec, but common PNG decoders make it black instead.
-        That makes color conversion slightly faster due to no error handling needed.*/
-        info->palette[i * 4 + 0] = 0;
-        info->palette[i * 4 + 1] = 0;
-        info->palette[i * 4 + 2] = 0;
-        info->palette[i * 4 + 3] = 255;
-    }
-}
-
-static void lodepng_palette_clear(LodePNGColorMode* info)
-{
-    if (info->palette) free(info->palette);
-    info->palette = 0;
-    info->palettesize = 0;
-}
-
-
-static void lodepng_color_mode_cleanup(LodePNGColorMode* info)
-{
-    lodepng_palette_clear(info);
-}
-
-
-/*return value is error code (0 means no error)*/
-static unsigned lodepng_color_mode_copy(LodePNGColorMode* dest, const LodePNGColorMode* source)
-{
-    lodepng_color_mode_cleanup(dest);
-    lodepng_memcpy(dest, source, sizeof(LodePNGColorMode));
-    if (source->palette) {
-        dest->palette = (unsigned char*)malloc(1024);
-        if (!dest->palette && source->palettesize) return 83; /*alloc fail*/
-        lodepng_memcpy(dest->palette, source->palette, source->palettesize * 4);
-    }
-    return 0;
-}
-
-
-static int lodepng_color_mode_equal(const LodePNGColorMode* a, const LodePNGColorMode* b)
-{
-    size_t i;
-    if (a->colortype != b->colortype) return 0;
-    if (a->bitdepth != b->bitdepth) return 0;
-    if (a->key_defined != b->key_defined) return 0;
-    if (a->key_defined) {
-        if(a->key_r != b->key_r) return 0;
-        if(a->key_g != b->key_g) return 0;
-        if(a->key_b != b->key_b) return 0;
-    }
-    if (a->palettesize != b->palettesize) return 0;
-    for (i = 0; i != a->palettesize * 4; ++i) {
-        if (a->palette[i] != b->palette[i]) return 0;
-    }
-    return 1;
-}
-
-
-static size_t lodepng_get_raw_size_lct(unsigned w, unsigned h, LodePNGColorType colortype, unsigned bitdepth)
-{
-    size_t bpp = lodepng_get_bpp_lct(colortype, bitdepth);
-    size_t n = (size_t)w * (size_t)h;
-    return ((n / 8u) * bpp) + ((n & 7u) * bpp + 7u) / 8u;
-}
-
-
-/* Returns the byte size of a raw image buffer with given width, height and color mode */
-static size_t lodepng_get_raw_size(unsigned w, unsigned h, const LodePNGColorMode* color)
-{
-    return lodepng_get_raw_size_lct(w, h, color->colortype, color->bitdepth);
-}
-
-
-/*in an idat chunk, each scanline is a multiple of 8 bits, unlike the lodepng output buffer,
-and in addition has one extra byte per line: the filter byte. So this gives a larger
-result than lodepng_get_raw_size. Set h to 1 to get the size of 1 row including filter byte. */
-static size_t lodepng_get_raw_size_idat(unsigned w, unsigned h, unsigned bpp)
-{
-    /* + 1 for the filter byte, and possibly plus padding bits per line. */
-    /* Ignoring casts, the expression is equal to (w * bpp + 7) / 8 + 1, but avoids overflow of w * bpp */
-    size_t line = ((size_t)(w / 8u) * bpp) + 1u + ((w & 7u) * bpp + 7u) / 8u;
-    return (size_t)h * line;
-}
-
-
-/* Safely checks whether size_t overflow can be caused due to amount of pixels.
-   This check is overcautious rather than precise. If this check indicates no overflow,
-   you can safely compute in a size_t (but not an unsigned):
-   -(size_t)w * (size_t)h * 8
-   -amount of bytes in IDAT (including filter, padding and Adam7 bytes)
-   -amount of bytes in raw color model
-   Returns 1 if overflow possible, 0 if not. */
-static int lodepng_pixel_overflow(unsigned w, unsigned h, const LodePNGColorMode* pngcolor, const LodePNGColorMode* rawcolor)
-{
-    size_t bpp = LODEPNG_MAX(lodepng_get_bpp_lct(pngcolor->colortype, pngcolor->bitdepth), lodepng_get_bpp_lct(rawcolor->colortype, rawcolor->bitdepth));
-    size_t numpixels, total;
-    size_t line; /* bytes per line in worst case */
-
-    if (lodepng_mulofl((size_t)w, (size_t)h, &numpixels)) return 1;
-    if (lodepng_mulofl(numpixels, 8, &total)) return 1; /* bit pointer with 8-bit color, or 8 bytes per channel color */
-
-    /* Bytes per scanline with the expression "(w / 8u) * bpp) + ((w & 7u) * bpp + 7u) / 8u" */
-    if (lodepng_mulofl((size_t)(w / 8u), bpp, &line)) return 1;
-    if (lodepng_addofl(line, ((w & 7u) * bpp + 7u) / 8u, &line)) return 1;
-
-    if (lodepng_addofl(line, 5, &line)) return 1; /* 5 bytes overhead per line: 1 filterbyte, 4 for Adam7 worst case */
-    if (lodepng_mulofl(line, h, &total)) return 1; /* Total bytes in worst case */
-
-    return 0; /* no overflow */
-}
-
-
-static void lodepng_info_init(LodePNGInfo* info)
-{
-    lodepng_color_mode_init(&info->color);
-    info->interlace_method = 0;
-    info->compression_method = 0;
-    info->filter_method = 0;
-}
-
-
-static void lodepng_info_cleanup(LodePNGInfo* info)
-{
-    lodepng_color_mode_cleanup(&info->color);
-}
-
-
-/* index: bitgroup index, bits: bitgroup size(1, 2 or 4), in: bitgroup value, out: octet array to add bits to */
-static void addColorBits(unsigned char* out, size_t index, unsigned bits, unsigned in)
-{
-    unsigned m = bits == 1 ? 7 : bits == 2 ? 3 : 1; /*8 / bits - 1*/
-    /*p = the partial index in the byte, e.g. with 4 palettebits it is 0 for first half or 1 for second half*/
-    unsigned p = index & m;
-    in &= (1u << bits) - 1u; /*filter out any other bits of the input value*/
-    in = in << (bits * (m - p));
-    if(p == 0) out[index * bits / 8u] = in;
-    else out[index * bits / 8u] |= in;
-}
-
-/*
-    One node of a color tree
-    This is the data structure used to count the number of unique colors and to get a palette
-    index for a color. It's like an octree, but because the alpha channel is used too, each
-    node has 16 instead of 8 children.
-*/
-struct ColorTree
-{
-    ColorTree* children[16]; /* up to 16 pointers to ColorTree of next level */
-    int index; /* the payload. Only has a meaningful value if this is in the last level */
-};
-
-static void color_tree_init(ColorTree* tree)
-{
-    lodepng_memset(tree->children, 0, 16 * sizeof(*tree->children));
-    tree->index = -1;
-}
-
-static void color_tree_cleanup(ColorTree* tree)
-{
-    int i;
-    for (i = 0; i != 16; ++i) {
-        if(tree->children[i]) {
-            color_tree_cleanup(tree->children[i]);
-            free(tree->children[i]);
-        }
-    }
-}
-
-
-/* returns -1 if color not present, its index otherwise */
-static int color_tree_get(ColorTree* tree, unsigned char r, unsigned char g, unsigned char b, unsigned char a)
-{
-    int bit = 0;
-    for (bit = 0; bit < 8; ++bit) {
-        int i = 8 * ((r >> bit) & 1) + 4 * ((g >> bit) & 1) + 2 * ((b >> bit) & 1) + 1 * ((a >> bit) & 1);
-        if (!tree->children[i]) return -1;
-        else tree = tree->children[i];
-    }
-    return tree ? tree->index : -1;
-}
-
-
-/* color is not allowed to already exist.
-   Index should be >= 0 (it's signed to be compatible with using -1 for "doesn't exist")
-   Returns error code, or 0 if ok */
-static unsigned color_tree_add(ColorTree* tree, unsigned char r, unsigned char g, unsigned char b, unsigned char a, unsigned index)
-{
-    int bit;
-    for (bit = 0; bit < 8; ++bit) {
-        int i = 8 * ((r >> bit) & 1) + 4 * ((g >> bit) & 1) + 2 * ((b >> bit) & 1) + 1 * ((a >> bit) & 1);
-        if (!tree->children[i]) {
-            tree->children[i] = (ColorTree*)malloc(sizeof(ColorTree));
-            if (!tree->children[i]) return 83; /*alloc fail*/
-            color_tree_init(tree->children[i]);
-        }
-        tree = tree->children[i];
-    }
-    tree->index = (int)index;
-    return 0;
-}
-
-/* put a pixel, given its RGBA color, into image of any color type */
-static unsigned rgba8ToPixel(unsigned char* out, size_t i, const LodePNGColorMode* mode, ColorTree* tree /*for palette*/, unsigned char r, unsigned char g, unsigned char b, unsigned char a)
-{
-    if (mode->colortype == LCT_GREY) {
-        unsigned char gray = r; /*((unsigned short)r + g + b) / 3u;*/
-        if (mode->bitdepth == 8) out[i] = gray;
-        else if (mode->bitdepth == 16) out[i * 2 + 0] = out[i * 2 + 1] = gray;
-        else {
-            /*take the most significant bits of gray*/
-            gray = ((unsigned)gray >> (8u - mode->bitdepth)) & ((1u << mode->bitdepth) - 1u);
-            addColorBits(out, i, mode->bitdepth, gray);
-        }
-    } else if (mode->colortype == LCT_RGB) {
-        if (mode->bitdepth == 8) {
-            out[i * 3 + 0] = r;
-            out[i * 3 + 1] = g;
-            out[i * 3 + 2] = b;
-        } else {
-            out[i * 6 + 0] = out[i * 6 + 1] = r;
-            out[i * 6 + 2] = out[i * 6 + 3] = g;
-            out[i * 6 + 4] = out[i * 6 + 5] = b;
-        }
-    } else if(mode->colortype == LCT_PALETTE) {
-        int index = color_tree_get(tree, r, g, b, a);
-        if (index < 0) return 82; /*color not in palette*/
-        if (mode->bitdepth == 8) out[i] = index;
-        else addColorBits(out, i, mode->bitdepth, (unsigned)index);
-    } else if (mode->colortype == LCT_GREY_ALPHA) {
-        unsigned char gray = r; /*((unsigned short)r + g + b) / 3u;*/
-        if (mode->bitdepth == 8) {
-            out[i * 2 + 0] = gray;
-            out[i * 2 + 1] = a;
-        } else if (mode->bitdepth == 16) {
-            out[i * 4 + 0] = out[i * 4 + 1] = gray;
-            out[i * 4 + 2] = out[i * 4 + 3] = a;
-        }
-    } else if (mode->colortype == LCT_RGBA) {
-        if (mode->bitdepth == 8) {
-            out[i * 4 + 0] = r;
-            out[i * 4 + 1] = g;
-            out[i * 4 + 2] = b;
-            out[i * 4 + 3] = a;
-        } else {
-            out[i * 8 + 0] = out[i * 8 + 1] = r;
-            out[i * 8 + 2] = out[i * 8 + 3] = g;
-            out[i * 8 + 4] = out[i * 8 + 5] = b;
-            out[i * 8 + 6] = out[i * 8 + 7] = a;
-        }
-    }
-    return 0; /*no error*/
-}
-
-
-/* put a pixel, given its RGBA16 color, into image of any color 16-bitdepth type */
-static void rgba16ToPixel(unsigned char* out, size_t i, const LodePNGColorMode* mode, unsigned short r, unsigned short g, unsigned short b, unsigned short a)
-{
-    if (mode->colortype == LCT_GREY) {
-        unsigned short gray = r; /*((unsigned)r + g + b) / 3u;*/
-        out[i * 2 + 0] = (gray >> 8) & 255;
-        out[i * 2 + 1] = gray & 255;
-    } else if (mode->colortype == LCT_RGB) {
-        out[i * 6 + 0] = (r >> 8) & 255;
-        out[i * 6 + 1] = r & 255;
-        out[i * 6 + 2] = (g >> 8) & 255;
-        out[i * 6 + 3] = g & 255;
-        out[i * 6 + 4] = (b >> 8) & 255;
-        out[i * 6 + 5] = b & 255;
-    } else if (mode->colortype == LCT_GREY_ALPHA) {
-        unsigned short gray = r; /*((unsigned)r + g + b) / 3u;*/
-        out[i * 4 + 0] = (gray >> 8) & 255;
-        out[i * 4 + 1] = gray & 255;
-        out[i * 4 + 2] = (a >> 8) & 255;
-        out[i * 4 + 3] = a & 255;
-    } else if (mode->colortype == LCT_RGBA) {
-        out[i * 8 + 0] = (r >> 8) & 255;
-        out[i * 8 + 1] = r & 255;
-        out[i * 8 + 2] = (g >> 8) & 255;
-        out[i * 8 + 3] = g & 255;
-        out[i * 8 + 4] = (b >> 8) & 255;
-        out[i * 8 + 5] = b & 255;
-        out[i * 8 + 6] = (a >> 8) & 255;
-        out[i * 8 + 7] = a & 255;
-    }
-}
-
-
-/* Get RGBA8 color of pixel with index i (y * width + x) from the raw image with given color type. */
-static void getPixelColorRGBA8(unsigned char* r, unsigned char* g, unsigned char* b, unsigned char* a, const unsigned char* in, size_t i, const LodePNGColorMode* mode)
-{
-    if (mode->colortype == LCT_GREY) {
-        if (mode->bitdepth == 8) {
-            *r = *g = *b = in[i];
-            if (mode->key_defined && *r == mode->key_r) *a = 0;
-            else *a = 255;
-        } else if (mode->bitdepth == 16) {
-            *r = *g = *b = in[i * 2 + 0];
-            if (mode->key_defined && 256U * in[i * 2 + 0] + in[i * 2 + 1] == mode->key_r) *a = 0;
-            else *a = 255;
-        } else {
-            unsigned highest = ((1U << mode->bitdepth) - 1U); /* highest possible value for this bit depth */
-            size_t j = i * mode->bitdepth;
-            unsigned value = readBitsFromReversedStream(&j, in, mode->bitdepth);
-            *r = *g = *b = (value * 255) / highest;
-            if (mode->key_defined && value == mode->key_r) *a = 0;
-            else *a = 255;
-        }
-    } else if (mode->colortype == LCT_RGB) {
-        if (mode->bitdepth == 8) {
-            *r = in[i * 3 + 0]; *g = in[i * 3 + 1]; *b = in[i * 3 + 2];
-            if (mode->key_defined && *r == mode->key_r && *g == mode->key_g && *b == mode->key_b) *a = 0;
-            else *a = 255;
-        } else {
-            *r = in[i * 6 + 0];
-            *g = in[i * 6 + 2];
-            *b = in[i * 6 + 4];
-            if (mode->key_defined && 256U * in[i * 6 + 0] + in[i * 6 + 1] == mode->key_r
-              && 256U * in[i * 6 + 2] + in[i * 6 + 3] == mode->key_g
-              && 256U * in[i * 6 + 4] + in[i * 6 + 5] == mode->key_b) *a = 0;
-            else *a = 255;
-        }
-    } else if (mode->colortype == LCT_PALETTE) {
-        unsigned index;
-        if (mode->bitdepth == 8) index = in[i];
-        else {
-            size_t j = i * mode->bitdepth;
-            index = readBitsFromReversedStream(&j, in, mode->bitdepth);
-        }
-        /* out of bounds of palette not checked: see lodepng_color_mode_alloc_palette. */
-        *r = mode->palette[index * 4 + 0];
-        *g = mode->palette[index * 4 + 1];
-        *b = mode->palette[index * 4 + 2];
-        *a = mode->palette[index * 4 + 3];
-    } else if (mode->colortype == LCT_GREY_ALPHA) {
-        if (mode->bitdepth == 8) {
-            *r = *g = *b = in[i * 2 + 0];
-            *a = in[i * 2 + 1];
-        } else {
-            *r = *g = *b = in[i * 4 + 0];
-            *a = in[i * 4 + 2];
-        }
-    } else if (mode->colortype == LCT_RGBA) {
-        if (mode->bitdepth == 8) {
-            *r = in[i * 4 + 0];
-            *g = in[i * 4 + 1];
-            *b = in[i * 4 + 2];
-            *a = in[i * 4 + 3];
-        } else {
-            *r = in[i * 8 + 0];
-            *g = in[i * 8 + 2];
-            *b = in[i * 8 + 4];
-            *a = in[i * 8 + 6];
-        }
-    }
-}
-
-
-/* Similar to getPixelColorRGBA8, but with all the for loops inside of the color
-   mode test cases, optimized to convert the colors much faster, when converting
-   to the common case of RGBA with 8 bit per channel. buffer must be RGBA with
-   enough memory.*/
-static void getPixelColorsRGBA8(unsigned char* LODEPNG_RESTRICT buffer, size_t numpixels, const unsigned char* LODEPNG_RESTRICT in, const LodePNGColorMode* mode)
-{
-    unsigned num_channels = 4;
-    size_t i;
-    if (mode->colortype == LCT_GREY) {
-        if (mode->bitdepth == 8) {
-            for (i = 0; i != numpixels; ++i, buffer += num_channels) {
-                buffer[0] = buffer[1] = buffer[2] = in[i];
-                buffer[3] = 255;
-            }
-            if (mode->key_defined) {
-                buffer -= numpixels * num_channels;
-                for (i = 0; i != numpixels; ++i, buffer += num_channels) {
-                    if(buffer[0] == mode->key_r) buffer[3] = 0;
-                }
-            }
-        } else if (mode->bitdepth == 16) {
-            for (i = 0; i != numpixels; ++i, buffer += num_channels) {
-                buffer[0] = buffer[1] = buffer[2] = in[i * 2];
-                buffer[3] = mode->key_defined && 256U * in[i * 2 + 0] + in[i * 2 + 1] == mode->key_r ? 0 : 255;
-            }
-        } else {
-            unsigned highest = ((1U << mode->bitdepth) - 1U); /* highest possible value for this bit depth */
-            size_t j = 0;
-            for (i = 0; i != numpixels; ++i, buffer += num_channels) {
-                unsigned value = readBitsFromReversedStream(&j, in, mode->bitdepth);
-                buffer[0] = buffer[1] = buffer[2] = (value * 255) / highest;
-                buffer[3] = mode->key_defined && value == mode->key_r ? 0 : 255;
-            }
-        }
-    } else if (mode->colortype == LCT_RGB) {
-        if (mode->bitdepth == 8) {
-            for (i = 0; i != numpixels; ++i, buffer += num_channels) {
-                lodepng_memcpy(buffer, &in[i * 3], 3);
-                buffer[3] = 255;
-            }
-            if (mode->key_defined) {
-                buffer -= numpixels * num_channels;
-                for (i = 0; i != numpixels; ++i, buffer += num_channels) {
-                    if (buffer[0] == mode->key_r && buffer[1]== mode->key_g && buffer[2] == mode->key_b) buffer[3] = 0;
-                }
-            }
-        } else {
-            for (i = 0; i != numpixels; ++i, buffer += num_channels) {
-                buffer[0] = in[i * 6 + 0];
-                buffer[1] = in[i * 6 + 2];
-                buffer[2] = in[i * 6 + 4];
-                buffer[3] = mode->key_defined
-                  && 256U * in[i * 6 + 0] + in[i * 6 + 1] == mode->key_r
-                  && 256U * in[i * 6 + 2] + in[i * 6 + 3] == mode->key_g
-                  && 256U * in[i * 6 + 4] + in[i * 6 + 5] == mode->key_b ? 0 : 255;
-            }
-        }
-    } else if (mode->colortype == LCT_PALETTE) {
-        if (mode->bitdepth == 8) {
-            for (i = 0; i != numpixels; ++i, buffer += num_channels) {
-              unsigned index = in[i];
-              /* out of bounds of palette not checked: see lodepng_color_mode_alloc_palette. */
-              lodepng_memcpy(buffer, &mode->palette[index * 4], 4);
-            }
-        } else {
-            size_t j = 0;
-            for (i = 0; i != numpixels; ++i, buffer += num_channels) {
-              unsigned index = readBitsFromReversedStream(&j, in, mode->bitdepth);
-              /* out of bounds of palette not checked: see lodepng_color_mode_alloc_palette. */
-              lodepng_memcpy(buffer, &mode->palette[index * 4], 4);
-            }
-        }
-    } else if (mode->colortype == LCT_GREY_ALPHA) {
-        if (mode->bitdepth == 8) {
-            for (i = 0; i != numpixels; ++i, buffer += num_channels) {
-              buffer[0] = buffer[1] = buffer[2] = in[i * 2 + 0];
-              buffer[3] = in[i * 2 + 1];
-            }
-        } else {
-            for (i = 0; i != numpixels; ++i, buffer += num_channels) {
-              buffer[0] = buffer[1] = buffer[2] = in[i * 4 + 0];
-              buffer[3] = in[i * 4 + 2];
-            }
-        }
-    } else if (mode->colortype == LCT_RGBA) {
-        if (mode->bitdepth == 8) {
-            lodepng_memcpy(buffer, in, numpixels * 4);
-        } else {
-            for (i = 0; i != numpixels; ++i, buffer += num_channels) {
-                buffer[0] = in[i * 8 + 0];
-                buffer[1] = in[i * 8 + 2];
-                buffer[2] = in[i * 8 + 4];
-                buffer[3] = in[i * 8 + 6];
-            }
-        }
-    }
-}
-
-
-/* Similar to getPixelColorsRGBA8, but with 3-channel RGB output. */
-static void getPixelColorsRGB8(unsigned char* LODEPNG_RESTRICT buffer, size_t numpixels, const unsigned char* LODEPNG_RESTRICT in, const LodePNGColorMode* mode)
-{
-    const unsigned num_channels = 3;
-    size_t i;
-    if (mode->colortype == LCT_GREY) {
-        if (mode->bitdepth == 8) {
-            for (i = 0; i != numpixels; ++i, buffer += num_channels) {
-               buffer[0] = buffer[1] = buffer[2] = in[i];
-            }
-        } else if (mode->bitdepth == 16) {
-            for (i = 0; i != numpixels; ++i, buffer += num_channels) {
-                buffer[0] = buffer[1] = buffer[2] = in[i * 2];
-            }
-        } else {
-            unsigned highest = ((1U << mode->bitdepth) - 1U); /* highest possible value for this bit depth */
-            size_t j = 0;
-            for (i = 0; i != numpixels; ++i, buffer += num_channels) {
-                unsigned value = readBitsFromReversedStream(&j, in, mode->bitdepth);
-                buffer[0] = buffer[1] = buffer[2] = (value * 255) / highest;
-            }
-        }
-    } else if (mode->colortype == LCT_RGB) {
-        if (mode->bitdepth == 8) {
-           lodepng_memcpy(buffer, in, numpixels * 3);
-        } else {
-            for(i = 0; i != numpixels; ++i, buffer += num_channels) {
-                buffer[0] = in[i * 6 + 0];
-                buffer[1] = in[i * 6 + 2];
-                buffer[2] = in[i * 6 + 4];
-            }
-        }
-    } else if (mode->colortype == LCT_PALETTE) {
-        if (mode->bitdepth == 8) {
-            for (i = 0; i != numpixels; ++i, buffer += num_channels) {
-                unsigned index = in[i];
-                /* out of bounds of palette not checked: see lodepng_color_mode_alloc_palette. */
-                lodepng_memcpy(buffer, &mode->palette[index * 4], 3);
-            }
-        } else {
-            size_t j = 0;
-            for (i = 0; i != numpixels; ++i, buffer += num_channels) {
-                unsigned index = readBitsFromReversedStream(&j, in, mode->bitdepth);
-                /* out of bounds of palette not checked: see lodepng_color_mode_alloc_palette. */
-                lodepng_memcpy(buffer, &mode->palette[index * 4], 3);
-            }
-        }
-    } else if (mode->colortype == LCT_GREY_ALPHA) {
-        if (mode->bitdepth == 8) {
-            for (i = 0; i != numpixels; ++i, buffer += num_channels) {
-                buffer[0] = buffer[1] = buffer[2] = in[i * 2 + 0];
-            }
-        } else {
-            for (i = 0; i != numpixels; ++i, buffer += num_channels) {
-                buffer[0] = buffer[1] = buffer[2] = in[i * 4 + 0];
-            }
-        }
-    } else if (mode->colortype == LCT_RGBA) {
-        if (mode->bitdepth == 8) {
-            for(i = 0; i != numpixels; ++i, buffer += num_channels) {
-                lodepng_memcpy(buffer, &in[i * 4], 3);
-            }
-        } else {
-            for (i = 0; i != numpixels; ++i, buffer += num_channels) {
-                buffer[0] = in[i * 8 + 0];
-                buffer[1] = in[i * 8 + 2];
-                buffer[2] = in[i * 8 + 4];
-            }
-        }
-    }
-}
-
-
-/* Get RGBA16 color of pixel with index i (y * width + x) from the raw image with
-   given color type, but the given color type must be 16-bit itself. */
-static void getPixelColorRGBA16(unsigned short* r, unsigned short* g, unsigned short* b, unsigned short* a, const unsigned char* in, size_t i, const LodePNGColorMode* mode)
-{
-    if (mode->colortype == LCT_GREY) {
-        *r = *g = *b = 256 * in[i * 2 + 0] + in[i * 2 + 1];
-        if (mode->key_defined && 256U * in[i * 2 + 0] + in[i * 2 + 1] == mode->key_r) *a = 0;
-        else *a = 65535;
-    } else if (mode->colortype == LCT_RGB) {
-        *r = 256u * in[i * 6 + 0] + in[i * 6 + 1];
-        *g = 256u * in[i * 6 + 2] + in[i * 6 + 3];
-        *b = 256u * in[i * 6 + 4] + in[i * 6 + 5];
-        if (mode->key_defined
-          && 256u * in[i * 6 + 0] + in[i * 6 + 1] == mode->key_r
-          && 256u * in[i * 6 + 2] + in[i * 6 + 3] == mode->key_g
-          && 256u * in[i * 6 + 4] + in[i * 6 + 5] == mode->key_b) *a = 0;
-        else *a = 65535;
-    } else if (mode->colortype == LCT_GREY_ALPHA) {
-        *r = *g = *b = 256u * in[i * 4 + 0] + in[i * 4 + 1];
-        *a = 256u * in[i * 4 + 2] + in[i * 4 + 3];
-    } else if (mode->colortype == LCT_RGBA) {
-        *r = 256u * in[i * 8 + 0] + in[i * 8 + 1];
-        *g = 256u * in[i * 8 + 2] + in[i * 8 + 3];
-        *b = 256u * in[i * 8 + 4] + in[i * 8 + 5];
-        *a = 256u * in[i * 8 + 6] + in[i * 8 + 7];
-    }
-}
-
-/*
-  Converts raw buffer from one color type to another color type, based on
-  LodePNGColorMode structs to describe the input and output color type.
-  See the reference manual at the end of this header file to see which color conversions are supported.
-  return value = LodePNG error code (0 if all went ok, an error if the conversion isn't supported)
-  The out buffer must have size (w * h * bpp + 7) / 8, where bpp is the bits per pixel
-  of the output color type (lodepng_get_bpp).
-  For < 8 bpp images, there should not be padding bits at the end of scanlines.
-  For 16-bit per channel colors, uses big endian format like PNG does.
-  Return value is LodePNG error code
-*/
-static unsigned lodepng_convert(unsigned char* out, const unsigned char* in, const LodePNGColorMode* mode_out, const LodePNGColorMode* mode_in, unsigned w, unsigned h)
-{
-    size_t i;
-    ColorTree tree;
-    size_t numpixels = (size_t)w * (size_t)h;
-    unsigned error = 0;
-
-    if (mode_in->colortype == LCT_PALETTE && !mode_in->palette) {
-        return 107; /* error: must provide palette if input mode is palette */
-    }
-
-    if (lodepng_color_mode_equal(mode_out, mode_in)) {
-        size_t numbytes = lodepng_get_raw_size(w, h, mode_in);
-        lodepng_memcpy(out, in, numbytes);
-        return 0;
-    }
-
-    if (mode_out->colortype == LCT_PALETTE) {
-        size_t palettesize = mode_out->palettesize;
-        const unsigned char* palette = mode_out->palette;
-        size_t palsize = (size_t)1u << mode_out->bitdepth;
-        /* if the user specified output palette but did not give the values, assume
-           they want the values of the input color type (assuming that one is palette).
-           Note that we never create a new palette ourselves.*/
-        if (palettesize == 0) {
-            palettesize = mode_in->palettesize;
-            palette = mode_in->palette;
-            /* if the input was also palette with same bitdepth, then the color types are also
-               equal, so copy literally. This to preserve the exact indices that were in the PNG
-               even in case there are duplicate colors in the palette.*/
-            if (mode_in->colortype == LCT_PALETTE && mode_in->bitdepth == mode_out->bitdepth) {
-                size_t numbytes = lodepng_get_raw_size(w, h, mode_in);
-                lodepng_memcpy(out, in, numbytes);
-                return 0;
-            }
-        }
-        if (palettesize < palsize) palsize = palettesize;
-        color_tree_init(&tree);
-        for (i = 0; i != palsize; ++i) {
-            const unsigned char* p = &palette[i * 4];
-            error = color_tree_add(&tree, p[0], p[1], p[2], p[3], (unsigned)i);
-            if (error) break;
-        }
-    }
-
-    if (!error) {
-        if (mode_in->bitdepth == 16 && mode_out->bitdepth == 16) {
-            for (i = 0; i != numpixels; ++i) {
-                unsigned short r = 0, g = 0, b = 0, a = 0;
-                getPixelColorRGBA16(&r, &g, &b, &a, in, i, mode_in);
-                rgba16ToPixel(out, i, mode_out, r, g, b, a);
-            }
-        } else if (mode_out->bitdepth == 8 && mode_out->colortype == LCT_RGBA) {
-            getPixelColorsRGBA8(out, numpixels, in, mode_in);
-        } else if(mode_out->bitdepth == 8 && mode_out->colortype == LCT_RGB) {
-            getPixelColorsRGB8(out, numpixels, in, mode_in);
-        } else {
-            unsigned char r = 0, g = 0, b = 0, a = 0;
-            for (i = 0; i != numpixels; ++i) {
-                getPixelColorRGBA8(&r, &g, &b, &a, in, i, mode_in);
-                error = rgba8ToPixel(out, i, mode_out, &tree, r, g, b, a);
-                if (error) break;
-            }
-        }
-    }
-
-    if (mode_out->colortype == LCT_PALETTE) {
-        color_tree_cleanup(&tree);
-    }
-
-    return error;
-}
-
-
-/* Paeth predictor, used by PNG filter type 4
-   The parameters are of type short, but should come from unsigned chars, the shorts
-   are only needed to make the paeth calculation correct.
-*/
-static unsigned char paethPredictor(short a, short b, short c)
-{
-    short pa = LODEPNG_ABS(b - c);
-    short pb = LODEPNG_ABS(a - c);
-    short pc = LODEPNG_ABS(a + b - c - c);
-    /* return input value associated with smallest of pa, pb, pc (with certain priority if equal) */
-    if (pb < pa) { a = b; pa = pb; }
-    return (pc < pa) ? c : a;
-}
-
-
-/*shared values used by multiple Adam7 related functions*/
-static const unsigned ADAM7_IX[7] = { 0, 4, 0, 2, 0, 1, 0 }; /*x start values*/
-static const unsigned ADAM7_IY[7] = { 0, 0, 4, 0, 2, 0, 1 }; /*y start values*/
-static const unsigned ADAM7_DX[7] = { 8, 8, 4, 4, 2, 2, 1 }; /*x delta values*/
-static const unsigned ADAM7_DY[7] = { 8, 8, 8, 4, 4, 2, 2 }; /*y delta values*/
-
-/* Outputs various dimensions and positions in the image related to the Adam7 reduced images.
-   passw: output containing the width of the 7 passes
-   passh: output containing the height of the 7 passes
-   filter_passstart: output containing the index of the start and end of each
-   reduced image with filter bytes
-   padded_passstart output containing the index of the start and end of each
-   reduced image when without filter bytes but with padded scanlines
-   passstart: output containing the index of the start and end of each reduced
-   image without padding between scanlines, but still padding between the images
-   w, h: width and height of non-interlaced image
-   bpp: bits per pixel
-   "padded" is only relevant if bpp is less than 8 and a scanline or image does not
-   end at a full byte */
-static void Adam7_getpassvalues(unsigned passw[7], unsigned passh[7], size_t filter_passstart[8], size_t padded_passstart[8], size_t passstart[8], unsigned w, unsigned h, unsigned bpp)
-{
-    /* the passstart values have 8 values: the 8th one indicates the byte after the end of the 7th (= last) pass */
-    unsigned i;
-
-    /* calculate width and height in pixels of each pass */
-    for (i = 0; i != 7; ++i) {
-        passw[i] = (w + ADAM7_DX[i] - ADAM7_IX[i] - 1) / ADAM7_DX[i];
-        passh[i] = (h + ADAM7_DY[i] - ADAM7_IY[i] - 1) / ADAM7_DY[i];
-        if(passw[i] == 0) passh[i] = 0;
-        if(passh[i] == 0) passw[i] = 0;
-    }
-
-    filter_passstart[0] = padded_passstart[0] = passstart[0] = 0;
-    for (i = 0; i != 7; ++i) {
-        /* if passw[i] is 0, it's 0 bytes, not 1 (no filtertype-byte) */
-        filter_passstart[i + 1] = filter_passstart[i]
-                                + ((passw[i] && passh[i]) ? passh[i] * (1u + (passw[i] * bpp + 7u) / 8u) : 0);
-        /* bits padded if needed to fill full byte at end of each scanline */
-        padded_passstart[i + 1] = padded_passstart[i] + passh[i] * ((passw[i] * bpp + 7u) / 8u);
-        /* only padded at end of reduced image */
-        passstart[i + 1] = passstart[i] + (passh[i] * passw[i] * bpp + 7u) / 8u;
-    }
-}
-
-
-/* ////////////////////////////////////////////////////////////////////////// */
-/* / PNG Decoder                                                            / */
-/* ////////////////////////////////////////////////////////////////////////// */
-
-static unsigned unfilterScanline(unsigned char* recon, const unsigned char* scanline, const unsigned char* precon, size_t bytewidth, unsigned char filterType, size_t length)
-{
-    /* For PNG filter method 0
-       unfilter a PNG image scanline by scanline. when the pixels are smaller than 1 byte,
-       the filter works byte per byte (bytewidth = 1)
-       precon is the previous unfiltered scanline, recon the result, scanline the current one
-       the incoming scanlines do NOT include the filtertype byte, that one is given in the parameter filterType instead
-       recon and scanline MAY be the same memory address! precon must be disjoint. */
-
-    size_t i;
-    switch (filterType) {
-        case 0:
-            for (i = 0; i != length; ++i) recon[i] = scanline[i];
-            break;
-        case 1:
-            for (i = 0; i != bytewidth; ++i) recon[i] = scanline[i];
-            for (i = bytewidth; i < length; ++i) recon[i] = scanline[i] + recon[i - bytewidth];
-            break;
-        case 2:
-            if (precon) {
-                for(i = 0; i != length; ++i) recon[i] = scanline[i] + precon[i];
-            } else {
-                for(i = 0; i != length; ++i) recon[i] = scanline[i];
-            }
-            break;
-        case 3:
-          if (precon) {
-              for (i = 0; i != bytewidth; ++i) recon[i] = scanline[i] + (precon[i] >> 1u);
-              for (i = bytewidth; i < length; ++i) recon[i] = scanline[i] + ((recon[i - bytewidth] + precon[i]) >> 1u);
-          } else {
-              for (i = 0; i != bytewidth; ++i) recon[i] = scanline[i];
-              for (i = bytewidth; i < length; ++i) recon[i] = scanline[i] + (recon[i - bytewidth] >> 1u);
-          }
-          break;
-        case 4:
-            if (precon) {
-                for (i = 0; i != bytewidth; ++i) {
-                    recon[i] = (scanline[i] + precon[i]); /*paethPredictor(0, precon[i], 0) is always precon[i]*/
-                }
-
-                /* Unroll independent paths of the paeth predictor. A 6x and 8x version would also be possible but that
-                   adds too much code. Whether this actually speeds anything up at all depends on compiler and settings. */
-                if (bytewidth >= 4) {
-                    for (; i + 3 < length; i += 4) {
-                        size_t j = i - bytewidth;
-                        unsigned char s0 = scanline[i + 0], s1 = scanline[i + 1], s2 = scanline[i + 2], s3 = scanline[i + 3];
-                        unsigned char r0 = recon[j + 0], r1 = recon[j + 1], r2 = recon[j + 2], r3 = recon[j + 3];
-                        unsigned char p0 = precon[i + 0], p1 = precon[i + 1], p2 = precon[i + 2], p3 = precon[i + 3];
-                        unsigned char q0 = precon[j + 0], q1 = precon[j + 1], q2 = precon[j + 2], q3 = precon[j + 3];
-                        recon[i + 0] = s0 + paethPredictor(r0, p0, q0);
-                        recon[i + 1] = s1 + paethPredictor(r1, p1, q1);
-                        recon[i + 2] = s2 + paethPredictor(r2, p2, q2);
-                        recon[i + 3] = s3 + paethPredictor(r3, p3, q3);
-                    }
-                } else if (bytewidth >= 3) {
-                    for (; i + 2 < length; i += 3) {
-                        size_t j = i - bytewidth;
-                        unsigned char s0 = scanline[i + 0], s1 = scanline[i + 1], s2 = scanline[i + 2];
-                        unsigned char r0 = recon[j + 0], r1 = recon[j + 1], r2 = recon[j + 2];
-                        unsigned char p0 = precon[i + 0], p1 = precon[i + 1], p2 = precon[i + 2];
-                        unsigned char q0 = precon[j + 0], q1 = precon[j + 1], q2 = precon[j + 2];
-                        recon[i + 0] = s0 + paethPredictor(r0, p0, q0);
-                        recon[i + 1] = s1 + paethPredictor(r1, p1, q1);
-                        recon[i + 2] = s2 + paethPredictor(r2, p2, q2);
-                    }
-                } else if (bytewidth >= 2) {
-                    for (; i + 1 < length; i += 2) {
-                        size_t j = i - bytewidth;
-                        unsigned char s0 = scanline[i + 0], s1 = scanline[i + 1];
-                        unsigned char r0 = recon[j + 0], r1 = recon[j + 1];
-                        unsigned char p0 = precon[i + 0], p1 = precon[i + 1];
-                        unsigned char q0 = precon[j + 0], q1 = precon[j + 1];
-                        recon[i + 0] = s0 + paethPredictor(r0, p0, q0);
-                        recon[i + 1] = s1 + paethPredictor(r1, p1, q1);
-                    }
-                }
-
-                for (; i != length; ++i) {
-                    recon[i] = (scanline[i] + paethPredictor(recon[i - bytewidth], precon[i], precon[i - bytewidth]));
-                }
-            } else {
-                for (i = 0; i != bytewidth; ++i) {
-                    recon[i] = scanline[i];
-                }
-                for (i = bytewidth; i < length; ++i) {
-                    /* paethPredictor(recon[i - bytewidth], 0, 0) is always recon[i - bytewidth] */
-                    recon[i] = (scanline[i] + recon[i - bytewidth]);
-                }
-            }
-            break;
-        default: return 36; /* error: invalid filter type given */
-    }
-    return 0;
-}
-
-
-static unsigned unfilter(unsigned char* out, const unsigned char* in, unsigned w, unsigned h, unsigned bpp)
-{
-    /* For PNG filter method 0
-       this function unfilters a single image (e.g. without interlacing this is called once, with Adam7 seven times)
-       out must have enough bytes allocated already, in must have the scanlines + 1 filtertype byte per scanline
-       w and h are image dimensions or dimensions of reduced image, bpp is bits per pixel
-       in and out are allowed to be the same memory address (but aren't the same size since in has the extra filter bytes) */
-
-    unsigned y;
-    unsigned char* prevline = 0;
-
-    /* bytewidth is used for filtering, is 1 when bpp < 8, number of bytes per pixel otherwise */
-    size_t bytewidth = (bpp + 7u) / 8u;
-    /* the width of a scanline in bytes, not including the filter type */
-    size_t linebytes = lodepng_get_raw_size_idat(w, 1, bpp) - 1u;
-
-    for (y = 0; y < h; ++y) {
-        size_t outindex = linebytes * y;
-        size_t inindex = (1 + linebytes) * y; /* the extra filterbyte added to each row */
-        unsigned char filterType = in[inindex];
-        CERROR_TRY_RETURN(unfilterScanline(&out[outindex], &in[inindex + 1], prevline, bytewidth, filterType, linebytes));
-        prevline = &out[outindex];
-    }
-
-    return 0;
-}
-
-/* in: Adam7 interlaced image, with no padding bits between scanlines, but between
-   reduced images so that each reduced image starts at a byte.
-   out: the same pixels, but re-ordered so that they're now a non-interlaced image with size w*h
-   bpp: bits per pixel
-   out has the following size in bits: w * h * bpp.
-   in is possibly bigger due to padding bits between reduced images.
-   out must be big enough AND must be 0 everywhere if bpp < 8 in the current implementation
-   (because that's likely a little bit faster)
-   NOTE: comments about padding bits are only relevant if bpp < 8 */
-static void Adam7_deinterlace(unsigned char* out, const unsigned char* in, unsigned w, unsigned h, unsigned bpp)
-{
-    unsigned passw[7], passh[7];
-    size_t filter_passstart[8], padded_passstart[8], passstart[8];
-    unsigned i;
-
-    Adam7_getpassvalues(passw, passh, filter_passstart, padded_passstart, passstart, w, h, bpp);
-
-    if (bpp >= 8) {
-        for(i = 0; i != 7; ++i) {
-            unsigned x, y, b;
-            size_t bytewidth = bpp / 8u;
-            for (y = 0; y < passh[i]; ++y)
-            for (x = 0; x < passw[i]; ++x) {
-                size_t pixelinstart = passstart[i] + (y * passw[i] + x) * bytewidth;
-                size_t pixeloutstart = ((ADAM7_IY[i] + (size_t)y * ADAM7_DY[i]) * (size_t)w + ADAM7_IX[i] + (size_t)x * ADAM7_DX[i]) * bytewidth;
-                for (b = 0; b < bytewidth; ++b) {
-                    out[pixeloutstart + b] = in[pixelinstart + b];
-                }
-            }
-        }
-    } else /* bpp < 8: Adam7 with pixels < 8 bit is a bit trickier: with bit pointers */ {
-        for (i = 0; i != 7; ++i) {
-            unsigned x, y, b;
-            unsigned ilinebits = bpp * passw[i];
-            unsigned olinebits = bpp * w;
-            size_t obp, ibp; /* bit pointers (for out and in buffer) */
-            for (y = 0; y < passh[i]; ++y)
-            for (x = 0; x < passw[i]; ++x) {
-                ibp = (8 * passstart[i]) + (y * ilinebits + x * bpp);
-                obp = (ADAM7_IY[i] + (size_t)y * ADAM7_DY[i]) * olinebits + (ADAM7_IX[i] + (size_t)x * ADAM7_DX[i]) * bpp;
-                for (b = 0; b < bpp; ++b) {
-                    unsigned char bit = readBitFromReversedStream(&ibp, in);
-                    setBitOfReversedStream(&obp, out, bit);
-                }
-            }
-        }
-    }
-}
-
-
-static void removePaddingBits(unsigned char* out, const unsigned char* in, size_t olinebits, size_t ilinebits, unsigned h)
-{
-    /* After filtering there are still padding bits if scanlines have non multiple of 8 bit amounts. They need
-       to be removed (except at last scanline of (Adam7-reduced) image) before working with pure image buffers
-       for the Adam7 code, the color convert code and the output to the user.
-       in and out are allowed to be the same buffer, in may also be higher but still overlapping; in must
-       have >= ilinebits*h bits, out must have >= olinebits*h bits, olinebits must be <= ilinebits
-       also used to move bits after earlier such operations happened, e.g. in a sequence of reduced images from Adam7
-       only useful if (ilinebits - olinebits) is a value in the range 1..7 */
-    unsigned y;
-    size_t diff = ilinebits - olinebits;
-    size_t ibp = 0, obp = 0; /*input and output bit pointers*/
-    for (y = 0; y < h; ++y) {
-        size_t x;
-        for (x = 0; x < olinebits; ++x) {
-            unsigned char bit = readBitFromReversedStream(&ibp, in);
-            setBitOfReversedStream(&obp, out, bit);
-        }
-        ibp += diff;
-    }
-}
-
-
-/* out must be buffer big enough to contain full image, and in must contain the full decompressed data from
-   the IDAT chunks (with filter index bytes and possible padding bits)
-   return value is error */
-static unsigned postProcessScanlines(unsigned char* out, unsigned char* in, unsigned w, unsigned h, const LodePNGInfo* info_png)
-{
-    /* This function converts the filtered-padded-interlaced data into pure 2D image buffer with the PNG's colortype.
-       Steps:
-       *) if no Adam7: 1) unfilter 2) remove padding bits (= possible extra bits per scanline if bpp < 8)
-       *) if adam7: 1) 7x unfilter 2) 7x remove padding bits 3) Adam7_deinterlace
-       NOTE: the in buffer will be overwritten with intermediate data! */
-    unsigned bpp = lodepng_get_bpp_lct(info_png->color.colortype, info_png->color.bitdepth);
-    if (bpp == 0) return 31; /* error: invalid colortype */
-
-    if (info_png->interlace_method == 0) {
-        if (bpp < 8 && w * bpp != ((w * bpp + 7u) / 8u) * 8u) {
-            CERROR_TRY_RETURN(unfilter(in, in, w, h, bpp));
-            removePaddingBits(out, in, w * bpp, ((w * bpp + 7u) / 8u) * 8u, h);
-        }
-        /* we can immediately filter into the out buffer, no other steps needed */
-        else CERROR_TRY_RETURN(unfilter(out, in, w, h, bpp));
-    } else /* interlace_method is 1 (Adam7) */ {
-        unsigned passw[7], passh[7]; size_t filter_passstart[8], padded_passstart[8], passstart[8];
-        unsigned i;
-
-        Adam7_getpassvalues(passw, passh, filter_passstart, padded_passstart, passstart, w, h, bpp);
-
-        for (i = 0; i != 7; ++i) {
-            CERROR_TRY_RETURN(unfilter(&in[padded_passstart[i]], &in[filter_passstart[i]], passw[i], passh[i], bpp));
-            /* TODO: possible efficiency improvement: if in this reduced image the bits fit nicely in 1 scanline,
-               move bytes instead of bits or move not at all */
-            if (bpp < 8) {
-              /* remove padding bits in scanlines; after this there still may be padding
-                 bits between the different reduced images: each reduced image still starts nicely at a byte */
-              removePaddingBits(&in[passstart[i]], &in[padded_passstart[i]], passw[i] * bpp, ((passw[i] * bpp + 7u) / 8u) * 8u, passh[i]);
-            }
-        }
-        Adam7_deinterlace(out, in, w, h, bpp);
-    }
-    return 0;
-}
-
-
-static unsigned readChunk_PLTE(LodePNGColorMode* color, const unsigned char* data, size_t chunkLength)
-{
-    unsigned pos = 0, i;
-    color->palettesize = chunkLength / 3u;
-    if (color->palettesize == 0 || color->palettesize > 256) return 38; /* error: palette too small or big */
-    lodepng_color_mode_alloc_palette(color);
-    if (!color->palette && color->palettesize) {
-        color->palettesize = 0;
-        return 83; /* alloc fail */
-    }
-
-    for (i = 0; i != color->palettesize; ++i) {
-        color->palette[4 * i + 0] = data[pos++]; /*R*/
-        color->palette[4 * i + 1] = data[pos++]; /*G*/
-        color->palette[4 * i + 2] = data[pos++]; /*B*/
-        color->palette[4 * i + 3] = 255; /*alpha*/
-    }
-
-    return 0; /* OK */
-}
-
-
-static unsigned readChunk_tRNS(LodePNGColorMode* color, const unsigned char* data, size_t chunkLength)
-{
-    unsigned i;
-    if (color->colortype == LCT_PALETTE) {
-        /* error: more alpha values given than there are palette entries */
-        if (chunkLength > color->palettesize) return 39;
-
-        for (i = 0; i != chunkLength; ++i) color->palette[4 * i + 3] = data[i];
-    } else if (color->colortype == LCT_GREY) {
-        /* error: this chunk must be 2 bytes for grayscale image */
-        if (chunkLength != 2) return 30;
-
-        color->key_defined = 1;
-        color->key_r = color->key_g = color->key_b = 256u * data[0] + data[1];
-    } else if (color->colortype == LCT_RGB) {
-        /* error: this chunk must be 6 bytes for RGB image */
-        if (chunkLength != 6) return 41;
-
-        color->key_defined = 1;
-        color->key_r = 256u * data[0] + data[1];
-        color->key_g = 256u * data[2] + data[3];
-        color->key_b = 256u * data[4] + data[5];
-    }
-    else return 42; /* error: tRNS chunk not allowed for other color models */
-
-    return 0; /* OK */
-}
-
-
-/* read a PNG, the result will be in the same color type as the PNG (hence "generic") */
-static void decodeGeneric(unsigned char** out, unsigned* w, unsigned* h, LodePNGState* state, const unsigned char* in, size_t insize)
-{
-    unsigned char IEND = 0;
-    const unsigned char* chunk;
-    unsigned char* idat; /*the data from idat chunks, zlib compressed*/
-    size_t idatsize = 0;
-    unsigned char* scanlines = 0;
-    size_t scanlines_size = 0, expected_size = 0;
-    size_t outsize = 0;
-
-    /* safe output values in case error happens */
-    *out = 0;
-    *w = *h = 0;
-
-    state->error = lodepng_inspect(w, h, state, in, insize); /*reads header and resets other parameters in state->info_png*/
-    if (state->error) return;
-
-    if (lodepng_pixel_overflow(*w, *h, &state->info_png.color, &state->info_raw)) {
-        CERROR_RETURN(state->error, 92); /*overflow possible due to amount of pixels*/
-    }
-
-    /*the input filesize is a safe upper bound for the sum of idat chunks size*/
-    idat = (unsigned char*)malloc(insize);
-    if (!idat) CERROR_RETURN(state->error, 83); /*alloc fail*/
-
-    chunk = &in[33]; /*first byte of the first chunk after the header*/
-
-    /*loop through the chunks, ignoring unknown chunks and stopping at IEND chunk.
-    IDAT data is put at the start of the in buffer*/
-    while (!IEND && !state->error) {
-        unsigned chunkLength;
-        const unsigned char* data; /*the data in the chunk*/
-
-        /*error: size of the in buffer too small to contain next chunk*/
-        if ((size_t)((chunk - in) + 12) > insize || chunk < in) {
-            if (state->decoder.ignore_end) break; /*other errors may still happen though*/
-            CERROR_BREAK(state->error, 30);
-        }
-
-        /*length of the data of the chunk, excluding the length bytes, chunk type and CRC bytes*/
-        chunkLength = lodepng_chunk_length(chunk);
-        /*error: chunk length larger than the max PNG chunk size*/
-        if (chunkLength > 2147483647) {
-            if (state->decoder.ignore_end) break; /*other errors may still happen though*/
-            CERROR_BREAK(state->error, 63);
-        }
-
-        if ((size_t)((chunk - in) + chunkLength + 12) > insize || (chunk + chunkLength + 12) < in) {
-            CERROR_BREAK(state->error, 64); /*error: size of the in buffer too small to contain next chunk*/
-        }
-
-        data = lodepng_chunk_data_const(chunk);
-
-        /*for unknown chunk order*/
-        //unsigned unknown = 0;
-
-        /*IDAT chunk, containing compressed image data*/
-        if (lodepng_chunk_type_equals(chunk, "IDAT")) {
-            size_t newsize;
-            if (lodepng_addofl(idatsize, chunkLength, &newsize)) CERROR_BREAK(state->error, 95);
-            if (newsize > insize) CERROR_BREAK(state->error, 95);
-            lodepng_memcpy(idat + idatsize, data, chunkLength);
-            idatsize += chunkLength;
-        } else if (lodepng_chunk_type_equals(chunk, "IEND")) {
-            /*IEND chunk*/
-            IEND = 1;
-        } else if (lodepng_chunk_type_equals(chunk, "PLTE")) {
-            /*palette chunk (PLTE)*/
-            state->error = readChunk_PLTE(&state->info_png.color, data, chunkLength);
-            if (state->error) break;
-        } else if (lodepng_chunk_type_equals(chunk, "tRNS")) {
-            /*palette transparency chunk (tRNS). Even though this one is an ancillary chunk , it is still compiled
-            in without 'LODEPNG_COMPILE_ANCILLARY_CHUNKS' because it contains essential color information that
-            affects the alpha channel of pixels. */
-            state->error = readChunk_tRNS(&state->info_png.color, data, chunkLength);
-            if (state->error) break;
-        } else /*it's not an implemented chunk type, so ignore it: skip over the data*/ {
-            /*error: unknown critical chunk (5th bit of first byte of chunk type is 0)*/
-            if (!state->decoder.ignore_critical && !lodepng_chunk_ancillary(chunk)) {
-                CERROR_BREAK(state->error, 69);
-            }
-            //unknown = 1;
-        }
-
-#if 0 //We don't use CRC
-        if (!state->decoder.ignore_crc && !unknown) /*check CRC if wanted, only on known chunk types*/ {
-            if (lodepng_chunk_check_crc(chunk)) CERROR_BREAK(state->error, 57); /*invalid CRC*/
-        }
-#endif
-        if (!IEND) chunk = lodepng_chunk_next_const(chunk, in + insize);
-    }
-
-    if (state->info_png.color.colortype == LCT_PALETTE && !state->info_png.color.palette) {
-        state->error = 106; /* error: PNG file must have PLTE chunk if color type is palette */
-    }
-
-    if (!state->error) {
-        /*predict output size, to allocate exact size for output buffer to avoid more dynamic allocation.
-        If the decompressed size does not match the prediction, the image must be corrupt.*/
-        if (state->info_png.interlace_method == 0) {
-            size_t bpp = lodepng_get_bpp_lct(state->info_png.color.colortype, state->info_png.color.bitdepth);
-            expected_size = lodepng_get_raw_size_idat(*w, *h, bpp);
-        } else {
-            size_t bpp = lodepng_get_bpp_lct(state->info_png.color.colortype, state->info_png.color.bitdepth);
-            /*Adam-7 interlaced: expected size is the sum of the 7 sub-images sizes*/
-            expected_size = 0;
-            expected_size += lodepng_get_raw_size_idat((*w + 7) >> 3, (*h + 7) >> 3, bpp);
-            if (*w > 4) expected_size += lodepng_get_raw_size_idat((*w + 3) >> 3, (*h + 7) >> 3, bpp);
-            expected_size += lodepng_get_raw_size_idat((*w + 3) >> 2, (*h + 3) >> 3, bpp);
-            if (*w > 2) expected_size += lodepng_get_raw_size_idat((*w + 1) >> 2, (*h + 3) >> 2, bpp);
-            expected_size += lodepng_get_raw_size_idat((*w + 1) >> 1, (*h + 1) >> 2, bpp);
-            if (*w > 1) expected_size += lodepng_get_raw_size_idat((*w + 0) >> 1, (*h + 1) >> 1, bpp);
-            expected_size += lodepng_get_raw_size_idat((*w + 0), (*h + 0) >> 1, bpp);
-        }
-        state->error = zlib_decompress(&scanlines, &scanlines_size, expected_size, idat, idatsize, &state->decoder.zlibsettings);
-    }
-
-    if (!state->error && scanlines_size != expected_size) state->error = 91; /*decompressed size doesn't match prediction*/
-    free(idat);
-
-    if (!state->error) {
-        outsize = lodepng_get_raw_size(*w, *h, &state->info_png.color);
-        *out = (unsigned char*)malloc(outsize);
-        if (!*out) state->error = 83; /*alloc fail*/
-    }
-    if (!state->error) {
-        lodepng_memset(*out, 0, outsize);
-        state->error = postProcessScanlines(*out, scanlines, *w, *h, &state->info_png);
-    }
-    free(scanlines);
-}
-
-
-static void lodepng_decoder_settings_init(LodePNGDecoderSettings* settings)
-{
-    settings->color_convert = 1;
-    settings->ignore_crc = 0;
-    settings->ignore_critical = 0;
-    settings->ignore_end = 0;
-    lodepng_decompress_settings_init(&settings->zlibsettings);
-}
-
-
-/************************************************************************/
-/* External Class Implementation                                        */
-/************************************************************************/
-
-/*read the information from the header and store it in the LodePNGInfo. return value is error*/
-unsigned lodepng_inspect(unsigned* w, unsigned* h, LodePNGState* state, const unsigned char* in, size_t insize)
-{
-    unsigned width, height;
-    LodePNGInfo* info = &state->info_png;
-    if (insize == 0 || in == 0) {
-        CERROR_RETURN_ERROR(state->error, 48); /*error: the given data is empty*/
-    }
-    if (insize < 33) {
-        CERROR_RETURN_ERROR(state->error, 27); /*error: the data length is smaller than the length of a PNG header*/
-    }
-
-    /* when decoding a new PNG image, make sure all parameters created after previous decoding are reset */
-    /* TODO: remove this. One should use a new LodePNGState for new sessions */
-    lodepng_info_cleanup(info);
-    lodepng_info_init(info);
-
-    if (in[0] != 137 || in[1] != 80 || in[2] != 78 || in[3] != 71 || in[4] != 13 || in[5] != 10 || in[6] != 26 || in[7] != 10) {
-        CERROR_RETURN_ERROR(state->error, 28); /*error: the first 8 bytes are not the correct PNG signature*/
-    }
-    if (lodepng_chunk_length(in + 8) != 13) {
-        CERROR_RETURN_ERROR(state->error, 94); /*error: header size must be 13 bytes*/
-    }
-    if (!lodepng_chunk_type_equals(in + 8, "IHDR")) {
-        CERROR_RETURN_ERROR(state->error, 29); /*error: it doesn't start with a IHDR chunk!*/
-    }
-
-    /*read the values given in the header*/
-    width = lodepng_read32bitInt(&in[16]);
-    height = lodepng_read32bitInt(&in[20]);
-    /*TODO: remove the undocumented feature that allows to give null pointers to width or height*/
-    if (w) *w = width;
-    if (h) *h = height;
-    info->color.bitdepth = in[24];
-    info->color.colortype = (LodePNGColorType)in[25];
-    info->compression_method = in[26];
-    info->filter_method = in[27];
-    info->interlace_method = in[28];
-
-    /*errors returned only after the parsing so other values are still output*/
-
-    /*error: invalid image size*/
-    if (width == 0 || height == 0) CERROR_RETURN_ERROR(state->error, 93);
-    /*error: invalid colortype or bitdepth combination*/
-    state->error = checkColorValidity(info->color.colortype, info->color.bitdepth);
-    if (state->error) return state->error;
-    /*error: only compression method 0 is allowed in the specification*/
-    if (info->compression_method != 0) CERROR_RETURN_ERROR(state->error, 32);
-    /*error: only filter method 0 is allowed in the specification*/
-    if (info->filter_method != 0) CERROR_RETURN_ERROR(state->error, 33);
-    /*error: only interlace methods 0 and 1 exist in the specification*/
-    if (info->interlace_method > 1) CERROR_RETURN_ERROR(state->error, 34);
-
-#if 0 //thorvg don't use crc
-    if (!state->decoder.ignore_crc) {
-        unsigned CRC = lodepng_read32bitInt(&in[29]);
-        unsigned checksum = lodepng_crc32(&in[12], 17);
-        if (CRC != checksum) {
-          CERROR_RETURN_ERROR(state->error, 57); /*invalid CRC*/
-        }
-    }
-#endif
-    return state->error;
-}
-
-
-unsigned lodepng_decode(unsigned char** out, unsigned* w, unsigned* h, LodePNGState* state, const unsigned char* in, size_t insize)
-{
-    *out = 0;
-    decodeGeneric(out, w, h, state, in, insize);
-    if (state->error) return state->error;
-    if (!state->decoder.color_convert || lodepng_color_mode_equal(&state->info_raw, &state->info_png.color)) {
-        /*same color type, no copying or converting of data needed*/
-        /*store the info_png color settings on the info_raw so that the info_raw still reflects what colortype
-        the raw image has to the end user*/
-        if (!state->decoder.color_convert) {
-            state->error = lodepng_color_mode_copy(&state->info_raw, &state->info_png.color);
-            if (state->error) return state->error;
-        }
-    } else { /*color conversion needed*/
-        unsigned char* data = *out;
-        size_t outsize;
-
-        /*TODO: check if this works according to the statement in the documentation: "The converter can convert
-        from grayscale input color type, to 8-bit grayscale or grayscale with alpha"*/
-        if (!(state->info_raw.colortype == LCT_RGB || state->info_raw.colortype == LCT_RGBA) && !(state->info_raw.bitdepth == 8)) {
-            return 56; /*unsupported color mode conversion*/
-        }
-
-        outsize = lodepng_get_raw_size(*w, *h, &state->info_raw);
-        *out = (unsigned char*)malloc(outsize);
-        if (!(*out)) {
-            state->error = 83; /*alloc fail*/
-        }
-        else state->error = lodepng_convert(*out, data, &state->info_raw, &state->info_png.color, *w, *h);
-        free(data);
-    }
-    return state->error;
-}
-
-
-void lodepng_state_init(LodePNGState* state)
-{
-    lodepng_decoder_settings_init(&state->decoder);
-    lodepng_color_mode_init(&state->info_raw);
-    lodepng_info_init(&state->info_png);
-    state->error = 1;
-}
-
-
-void lodepng_state_cleanup(LodePNGState* state)
-{
-    lodepng_color_mode_cleanup(&state->info_raw);
-    lodepng_info_cleanup(&state->info_png);
-}

+ 0 - 174
thirdparty/thorvg/src/loaders/png/tvgLodePng.h

@@ -1,174 +0,0 @@
-/*
- * Copyright (c) 2020 - 2024 the ThorVG project. 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.
- */
-
-/*
-  LodePNG version 20200306
-
-  Copyright (c) 2005-2020 Lode Vandevenne
-
-  This software is provided 'as-is', without any express or implied
-  warranty. In no event will the authors be held liable for any damages
-  arising from the use of this software.
-
-  Permission is granted to anyone to use this software for any purpose,
-  including commercial applications, and to alter it and redistribute it
-  freely, subject to the following restrictions:
-
-      1. The origin of this software must not be misrepresented; you must not
-      claim that you wrote the original software. If you use this software
-      in a product, an acknowledgment in the product documentation would be
-      appreciated but is not required.
-
-      2. Altered source versions must be plainly marked as such, and must not be
-      misrepresented as being the original software.
-
-      3. This notice may not be removed or altered from any source
-      distribution.
-*/
-
-#ifndef _TVG_LODEPNG_H_
-#define _TVG_LODEPNG_H_
-
-#include <stddef.h>
-
-/*The PNG color types (also used for raw image).*/
-enum LodePNGColorType
-{
-    LCT_GREY = 0, /*grayscale: 1,2,4,8,16 bit*/
-    LCT_RGB = 2, /*RGB: 8,16 bit*/
-    LCT_PALETTE = 3, /*palette: 1,2,4,8 bit*/
-    LCT_GREY_ALPHA = 4, /*grayscale with alpha: 8,16 bit*/
-    LCT_RGBA = 6, /*RGB with alpha: 8,16 bit*/
-    /*LCT_MAX_OCTET_VALUE lets the compiler allow this enum to represent any invalid
-    byte value from 0 to 255 that could be present in an invalid PNG file header. Do
-    not use, compare with or set the name LCT_MAX_OCTET_VALUE, instead either use
-    the valid color type names above, or numeric values like 1 or 7 when checking for
-    particular disallowed color type byte values, or cast to integer to print it.*/
-    LCT_MAX_OCTET_VALUE = 255
-};
-
-/*Settings for zlib decompression*/
-struct LodePNGDecompressSettings
-{
-    /* Check LodePNGDecoderSettings for more ignorable errors such as ignore_crc */
-    unsigned ignore_adler32; /*if 1, continue and don't give an error message if the Adler32 checksum is corrupted*/
-    unsigned ignore_nlen; /*ignore complement of len checksum in uncompressed blocks*/
-
-    /*use custom zlib decoder instead of built in one (default: null)*/
-    unsigned (*custom_zlib)(unsigned char**, size_t*, const unsigned char*, size_t, const LodePNGDecompressSettings*);
-    /*use custom deflate decoder instead of built in one (default: null) if custom_zlib is not null, custom_inflate is ignored (the zlib format uses deflate)*/
-    unsigned (*custom_inflate)(unsigned char**, size_t*, const unsigned char*, size_t, const LodePNGDecompressSettings*);
-
-    const void* custom_context; /*optional custom settings for custom functions*/
-};
-
-/*
-  Color mode of an image. Contains all information required to decode the pixel
-  bits to RGBA colors. This information is the same as used in the PNG file
-  format, and is used both for PNG and raw image data in LodePNG.
-*/
-struct LodePNGColorMode
-{
-    /*header (IHDR)*/
-    LodePNGColorType colortype; /*color type, see PNG standard or documentation further in this header file*/
-    unsigned bitdepth;  /*bits per sample, see PNG standard or documentation further in this header file*/
-
-    /*
-      palette (PLTE and tRNS)
-
-      Dynamically allocated with the colors of the palette, including alpha.
-      This field may not be allocated directly, use lodepng_color_mode_init first,
-      then lodepng_palette_add per color to correctly initialize it (to ensure size
-      of exactly 1024 bytes).
-
-      The alpha channels must be set as well, set them to 255 for opaque images.
-
-      When decoding, by default you can ignore this palette, since LodePNG already
-      fills the palette colors in the pixels of the raw RGBA output.
-
-      The palette is only supported for color type 3.
-    */
-    unsigned char* palette; /*palette in RGBARGBA... order. Must be either 0, or when allocated must have 1024 bytes*/
-    size_t palettesize; /*palette size in number of colors (amount of used bytes is 4 * palettesize)*/
-
-    /*
-      transparent color key (tRNS)
-
-      This color uses the same bit depth as the bitdepth value in this struct, which can be 1-bit to 16-bit.
-      For grayscale PNGs, r, g and b will all 3 be set to the same.
-
-      When decoding, by default you can ignore this information, since LodePNG sets
-      pixels with this key to transparent already in the raw RGBA output.
-
-      The color key is only supported for color types 0 and 2.
-    */
-    unsigned key_defined; /*is a transparent color key given? 0 = false, 1 = true*/
-    unsigned key_r;       /*red/grayscale component of color key*/
-    unsigned key_g;       /*green component of color key*/
-    unsigned key_b;       /*blue component of color key*/
-};
-
-/*Information about the PNG image, except pixels, width and height.*/
-struct LodePNGInfo
-{
-    /*header (IHDR), palette (PLTE) and transparency (tRNS) chunks*/
-    unsigned compression_method;/*compression method of the original file. Always 0.*/
-    unsigned filter_method;     /*filter method of the original file*/
-    unsigned interlace_method;  /*interlace method of the original file: 0=none, 1=Adam7*/
-    LodePNGColorMode color;     /*color type and bits, palette and transparency of the PNG file*/
-};
-
-/*
-  Settings for the decoder. This contains settings for the PNG and the Zlib
-  decoder, but not the Info settings from the Info structs.
-*/
-struct LodePNGDecoderSettings
-{
-    LodePNGDecompressSettings zlibsettings; /*in here is the setting to ignore Adler32 checksums*/
-
-    /* Check LodePNGDecompressSettings for more ignorable errors such as ignore_adler32 */
-    unsigned ignore_crc; /*ignore CRC checksums*/
-    unsigned ignore_critical; /*ignore unknown critical chunks*/
-    unsigned ignore_end; /*ignore issues at end of file if possible (missing IEND chunk, too large chunk, ...)*/
-    /* TODO: make a system involving warnings with levels and a strict mode instead. Other potentially recoverable
-       errors: srgb rendering intent value, size of content of ancillary chunks, more than 79 characters for some
-       strings, placement/combination rules for ancillary chunks, crc of unknown chunks, allowed characters
-       in string keys, etc... */
-
-    unsigned color_convert; /*whether to convert the PNG to the color type you want. Default: yes*/
-};
-
-/*The settings, state and information for extended encoding and decoding.*/
-struct LodePNGState
-{
-    LodePNGDecoderSettings decoder; /*the decoding settings*/
-    LodePNGColorMode info_raw; /*specifies the format in which you would like to get the raw pixel buffer*/
-    LodePNGInfo info_png; /*info of the PNG image obtained after decoding*/
-    unsigned error;
-};
-
-void lodepng_state_init(LodePNGState* state);
-void lodepng_state_cleanup(LodePNGState* state);
-unsigned lodepng_decode(unsigned char** out, unsigned* w, unsigned* h, LodePNGState* state, const unsigned char* in, size_t insize);
-unsigned lodepng_inspect(unsigned* w, unsigned* h, LodePNGState* state, const unsigned char* in, size_t insize);
-
-#endif //_TVG_LODEPNG_H_

+ 16 - 0
thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.cpp

@@ -49,6 +49,22 @@ static void _copyStyle(SvgStyleProperty* to, const SvgStyleProperty* from)
             to->flagsImportance = (to->flagsImportance | SvgStyleFlags::Color);
         }
     }
+    if (((from->flags & SvgStyleFlags::PaintOrder) && !(to->flags & SvgStyleFlags::PaintOrder)) ||
+        _isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::PaintOrder)) {
+        to->paintOrder = from->paintOrder;
+        to->flags = (to->flags | SvgStyleFlags::PaintOrder);
+        if (from->flagsImportance & SvgStyleFlags::PaintOrder) {
+            to->flagsImportance = (to->flagsImportance | SvgStyleFlags::PaintOrder);
+        }
+    }
+    if (((from->flags & SvgStyleFlags::Display) && !(to->flags & SvgStyleFlags::Display)) ||
+        _isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::Display)) {
+        to->display = from->display;
+        to->flags = (to->flags | SvgStyleFlags::Display);
+        if (from->flagsImportance & SvgStyleFlags::Display) {
+            to->flagsImportance = (to->flagsImportance | SvgStyleFlags::Display);
+        }
+    }
     //Fill
     if (((from->fill.flags & SvgFillFlags::Paint) && !(to->flags & SvgStyleFlags::Fill)) ||
         _isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::Fill)) {

+ 168 - 34
thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp

@@ -103,15 +103,19 @@ static const char* _skipComma(const char* content)
 }
 
 
-static bool _parseNumber(const char** content, float* number)
+static bool _parseNumber(const char** content, const char** end, float* number)
 {
-    char* end = nullptr;
+    const char* _end = end ? *end : nullptr;
 
-    *number = strToFloat(*content, &end);
+    *number = strToFloat(*content, (char**)&_end);
     //If the start of string is not number
-    if ((*content) == end) return false;
+    if ((*content) == _end) {
+        if (end) *end = _end;
+        return false;
+    }
     //Skip comma if any
-    *content = _skipComma(end);
+    *content = _skipComma(_end);
+    if (end) *end = _end;
 
     return true;
 }
@@ -576,7 +580,82 @@ static constexpr struct
 };
 
 
-static void _toColor(const char* str, uint8_t* r, uint8_t* g, uint8_t* b, char** ref)
+static bool _hslToRgb(float hue, float satuation, float brightness, uint8_t* red, uint8_t* green, uint8_t* blue)
+{
+    if (!red || !green || !blue) return false;
+
+    float sv, vsf, f, p, q, t, v;
+    float _red = 0, _green = 0, _blue = 0;
+    uint32_t i = 0;
+
+    if (mathZero(satuation))  _red = _green = _blue = brightness;
+    else {
+        if (mathEqual(hue, 360.0)) hue = 0.0f;
+        hue /= 60.0f;
+
+        v = (brightness <= 0.5f) ? (brightness * (1.0f + satuation)) : (brightness + satuation - (brightness * satuation));
+        p = brightness + brightness - v;
+
+        if (!mathZero(v)) sv = (v - p) / v;
+        else sv = 0;
+
+        i = static_cast<uint8_t>(hue);
+        f = hue - i;
+
+        vsf = v * sv * f;
+
+        t = p + vsf;
+        q = v - vsf;
+
+        switch (i) {
+            case 0: {
+                _red = v;
+                _green = t;
+                _blue = p;
+                break;
+            }
+            case 1: {
+                _red = q;
+                _green = v;
+                _blue = p;
+                break;
+            }
+            case 2: {
+                _red = p;
+                _green = v;
+                _blue = t;
+                break;
+            }
+            case 3: {
+                _red = p;
+                _green = q;
+                _blue = v;
+                break;
+            }
+            case 4: {
+                _red = t;
+                _green = p;
+                _blue = v;
+                break;
+            }
+            case 5: {
+                _red = v;
+                _green = p;
+                _blue = q;
+                break;
+            }
+        }
+    }
+
+    *red = static_cast<uint8_t>(roundf(_red * 255.0f));
+    *green = static_cast<uint8_t>(roundf(_green * 255.0f));
+    *blue = static_cast<uint8_t>(roundf(_blue * 255.0f));
+
+    return true;
+}
+
+
+static bool _toColor(const char* str, uint8_t* r, uint8_t* g, uint8_t* b, char** ref)
 {
     unsigned int len = strlen(str);
     char *red, *green, *blue;
@@ -596,6 +675,7 @@ static void _toColor(const char* str, uint8_t* r, uint8_t* g, uint8_t* b, char**
             tmp[1] = str[3];
             *b = strtol(tmp, nullptr, 16);
         }
+        return true;
     } else if (len == 7 && str[0] == '#') {
         if (isxdigit(str[1]) && isxdigit(str[2]) && isxdigit(str[3]) && isxdigit(str[4]) && isxdigit(str[5]) && isxdigit(str[6])) {
             char tmp[3] = { '\0', '\0', '\0' };
@@ -609,6 +689,7 @@ static void _toColor(const char* str, uint8_t* r, uint8_t* g, uint8_t* b, char**
             tmp[1] = str[6];
             *b = strtol(tmp, nullptr, 16);
         }
+        return true;
     } else if (len >= 10 && (str[0] == 'r' || str[0] == 'R') && (str[1] == 'g' || str[1] == 'G') && (str[2] == 'b' || str[2] == 'B') && str[3] == '(' && str[len - 1] == ')') {
         tr = _parseColor(str + 4, &red);
         if (red && *red == ',') {
@@ -622,9 +703,35 @@ static void _toColor(const char* str, uint8_t* r, uint8_t* g, uint8_t* b, char**
                 }
             }
         }
+        return true;
     } else if (ref && len >= 3 && !strncmp(str, "url", 3)) {
         if (*ref) free(*ref);
         *ref = _idFromUrl((const char*)(str + 3));
+        return true;
+    } else if (len >= 10 && (str[0] == 'h' || str[0] == 'H') && (str[1] == 's' || str[1] == 'S') && (str[2] == 'l' || str[2] == 'L') && str[3] == '(' && str[len - 1] == ')') {
+        float_t th, ts, tb;
+        const char *content, *hue, *satuation, *brightness;
+        content = str + 4;
+        content = _skipSpace(content, nullptr);
+        if (_parseNumber(&content, &hue, &th) && hue) {
+            th = float(uint32_t(th) % 360);
+            hue = _skipSpace(hue, nullptr);
+            hue = (char*)_skipComma(hue);
+            hue = _skipSpace(hue, nullptr);
+            if (_parseNumber(&hue, &satuation, &ts) && satuation && *satuation == '%') {
+                ts /= 100.0f;
+                satuation = _skipSpace(satuation + 1, nullptr);
+                satuation = (char*)_skipComma(satuation);
+                satuation = _skipSpace(satuation, nullptr);
+                if (_parseNumber(&satuation, &brightness, &tb) && brightness && *brightness == '%') {
+                    tb /= 100.0f;
+                    brightness = _skipSpace(brightness + 1, nullptr);
+                    if (brightness && brightness[0] == ')' && brightness[1] == '\0') {
+                       return _hslToRgb(th, ts, tb, r, g, b);
+                    }
+                }
+            }
+        }
     } else {
         //Handle named color
         for (unsigned int i = 0; i < (sizeof(colors) / sizeof(colors[0])); i++) {
@@ -632,10 +739,11 @@ static void _toColor(const char* str, uint8_t* r, uint8_t* g, uint8_t* b, char**
                 *r = (((uint8_t*)(&(colors[i].value)))[2]);
                 *g = (((uint8_t*)(&(colors[i].value)))[1]);
                 *b = (((uint8_t*)(&(colors[i].value)))[0]);
-                return;
+                return true;
             }
         }
     }
+    return false;
 }
 
 
@@ -745,8 +853,8 @@ static Matrix* _parseTransformationMatrix(const char* value)
             //Transform to signed.
             points[0] = fmodf(points[0], 360.0f);
             if (points[0] < 0) points[0] += 360.0f;
-            auto c = cosf(points[0] * (MATH_PI / 180.0f));
-            auto s = sinf(points[0] * (MATH_PI / 180.0f));
+            auto c = cosf(mathDeg2Rad(points[0]));
+            auto s = sinf(mathDeg2Rad(points[0]));
             if (ptCount == 1) {
                 Matrix tmp = { c, -s, 0, s, c, 0, 0, 0, 1 };
                 *matrix = mathMultiply(matrix, &tmp);
@@ -769,12 +877,12 @@ static Matrix* _parseTransformationMatrix(const char* value)
             *matrix = mathMultiply(matrix, &tmp);
         } else if (state == MatrixState::SkewX) {
             if (ptCount != 1) goto error;
-            auto deg = tanf(points[0] * (MATH_PI / 180.0f));
+            auto deg = tanf(mathDeg2Rad(points[0]));
             Matrix tmp = { 1, deg, 0, 0, 1, 0, 0, 0, 1 };
             *matrix = mathMultiply(matrix, &tmp);
         } else if (state == MatrixState::SkewY) {
             if (ptCount != 1) goto error;
-            auto deg = tanf(points[0] * (MATH_PI / 180.0f));
+            auto deg = tanf(mathDeg2Rad(points[0]));
             Matrix tmp = { 1, 0, 0, deg, 1, 0, 0, 0, 1 };
             *matrix = mathMultiply(matrix, &tmp);
         }
@@ -854,10 +962,10 @@ static bool _attrParseSvgNode(void* data, const char* key, const char* value)
             doc->viewFlag = (doc->viewFlag | SvgViewFlag::Height);
         }
     } else if (!strcmp(key, "viewBox")) {
-        if (_parseNumber(&value, &doc->vx)) {
-            if (_parseNumber(&value, &doc->vy)) {
-                if (_parseNumber(&value, &doc->vw)) {
-                    if (_parseNumber(&value, &doc->vh)) {
+        if (_parseNumber(&value, nullptr, &doc->vx)) {
+            if (_parseNumber(&value, nullptr, &doc->vy)) {
+                if (_parseNumber(&value, nullptr, &doc->vw)) {
+                    if (_parseNumber(&value, nullptr, &doc->vh)) {
                         doc->viewFlag = (doc->viewFlag | SvgViewFlag::Viewbox);
                         loader->svgParse->global.h = doc->vh;
                     }
@@ -898,20 +1006,21 @@ static void _handlePaintAttr(SvgPaint* paint, const char* value)
         paint->none = true;
         return;
     }
-    paint->none = false;
     if (!strcmp(value, "currentColor")) {
         paint->curColor = true;
+        paint->none = false;
         return;
     }
-    _toColor(value, &paint->color.r, &paint->color.g, &paint->color.b, &paint->url);
+    if (_toColor(value, &paint->color.r, &paint->color.g, &paint->color.b, &paint->url)) paint->none = false;
 }
 
 
 static void _handleColorAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
 {
     SvgStyleProperty* style = node->style;
-    style->curColorSet = true;
-    _toColor(value, &style->color.r, &style->color.g, &style->color.b, nullptr);
+    if (_toColor(value, &style->color.r, &style->color.g, &style->color.b, nullptr)) {
+        style->curColorSet = true;
+    }
 }
 
 
@@ -995,6 +1104,7 @@ static void _handleFillRuleAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node,
 
 static void _handleOpacityAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
 {
+    node->style->flags = (node->style->flags | SvgStyleFlags::Opacity);
     node->style->opacity = _toOpacity(value);
 }
 
@@ -1046,8 +1156,9 @@ static void _handleDisplayAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node,
     //       The default is "inline" which means visible and "none" means invisible.
     //       Depending on the type of node, additional functionality may be required.
     //       refer to https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/display
-    if (!strcmp(value, "none")) node->display = false;
-    else node->display = true;
+    node->style->flags = (node->style->flags | SvgStyleFlags::Display);
+    if (!strcmp(value, "none")) node->style->display = false;
+    else node->style->display = true;
 }
 
 
@@ -1267,8 +1378,8 @@ static bool _attrParseSymbolNode(void* data, const char* key, const char* value)
     SvgSymbolNode* symbol = &(node->node.symbol);
 
     if (!strcmp(key, "viewBox")) {
-        if (!_parseNumber(&value, &symbol->vx) || !_parseNumber(&value, &symbol->vy)) return false;
-        if (!_parseNumber(&value, &symbol->vw) || !_parseNumber(&value, &symbol->vh)) return false;
+        if (!_parseNumber(&value, nullptr, &symbol->vx) || !_parseNumber(&value, nullptr, &symbol->vy)) return false;
+        if (!_parseNumber(&value, nullptr, &symbol->vw) || !_parseNumber(&value, nullptr, &symbol->vh)) return false;
         symbol->hasViewBox = true;
     } else if (!strcmp(key, "width")) {
         symbol->w = _toFloat(loader->svgParse, value, SvgParserLengthType::Horizontal);
@@ -1332,7 +1443,7 @@ static SvgNode* _createNode(SvgNode* parent, SvgNodeType type)
     node->style->paintOrder = _toPaintOrder("fill stroke");
 
     //Default display is true("inline").
-    node->display = true;
+    node->style->display = true;
 
     node->parent = parent;
     node->type = type;
@@ -1408,7 +1519,7 @@ static SvgNode* _createClipPathNode(SvgLoaderData* loader, SvgNode* parent, cons
     loader->svgParse->node = _createNode(parent, SvgNodeType::ClipPath);
     if (!loader->svgParse->node) return nullptr;
 
-    loader->svgParse->node->display = false;
+    loader->svgParse->node->style->display = false;
     loader->svgParse->node->node.clip.userSpace = true;
 
     func(buf, bufLength, _attrParseClipPathNode, loader);
@@ -1433,7 +1544,6 @@ static SvgNode* _createSymbolNode(SvgLoaderData* loader, SvgNode* parent, const
     loader->svgParse->node = _createNode(parent, SvgNodeType::Symbol);
     if (!loader->svgParse->node) return nullptr;
 
-    loader->svgParse->node->display = false;
     loader->svgParse->node->node.symbol.align = AspectRatioAlign::XMidYMid;
     loader->svgParse->node->node.symbol.meetOrSlice = AspectRatioMeetOrSlice::Meet;
     loader->svgParse->node->node.symbol.overflowVisible = false;
@@ -1615,8 +1725,11 @@ static SvgNode* _createEllipseNode(SvgLoaderData* loader, SvgNode* parent, const
 
 static bool _attrParsePolygonPoints(const char* str, SvgPolygonNode* polygon)
 {
-    float num;
-    while (_parseNumber(&str, &num)) polygon->pts.push(num);
+    float num_x, num_y;
+    while (_parseNumber(&str, nullptr, &num_x) && _parseNumber(&str, nullptr, &num_y)) {
+        polygon->pts.push(num_x);
+        polygon->pts.push(num_y);
+    }
     return true;
 }
 
@@ -2378,8 +2491,9 @@ static bool _attrParseStopsStyle(void* data, const char* key, const char* value)
         stop->a = _toOpacity(value);
         loader->svgParse->flags = (loader->svgParse->flags | SvgStopStyleFlags::StopOpacity);
     } else if (!strcmp(key, "stop-color")) {
-        _toColor(value, &stop->r, &stop->g, &stop->b, nullptr);
-        loader->svgParse->flags = (loader->svgParse->flags | SvgStopStyleFlags::StopColor);
+        if (_toColor(value, &stop->r, &stop->g, &stop->b, nullptr)) {
+            loader->svgParse->flags = (loader->svgParse->flags | SvgStopStyleFlags::StopColor);
+        }
     } else {
         return false;
     }
@@ -2849,9 +2963,15 @@ static void _styleCopy(SvgStyleProperty* to, const SvgStyleProperty* from)
         to->color = from->color;
         to->curColorSet = true;
     }
+    if (from->flags & SvgStyleFlags::Opacity) {
+        to->opacity = from->opacity;
+    }
     if (from->flags & SvgStyleFlags::PaintOrder) {
         to->paintOrder = from->paintOrder;
     }
+    if (from->flags & SvgStyleFlags::Display) {
+        to->display = from->display;
+    }
     //Fill
     to->fill.flags = (to->fill.flags | from->fill.flags);
     if (from->fill.flags & SvgFillFlags::Paint) {
@@ -3080,6 +3200,14 @@ static void _svgLoaderParserXmlClose(SvgLoaderData* loader, const char* content)
         }
     }
 
+    for (unsigned int i = 0; i < sizeof(graphicsTags) / sizeof(graphicsTags[0]); i++) {
+        if (!strncmp(content, graphicsTags[i].tag, graphicsTags[i].sz - 1)) {
+            loader->currentGraphicsNode = nullptr;
+            loader->stack.pop();
+            break;
+        }
+    }
+
     loader->level--;
 }
 
@@ -3146,6 +3274,11 @@ static void _svgLoaderParserXmlOpen(SvgLoaderData* loader, const char* content,
         if (loader->stack.count > 0) parent = loader->stack.last();
         else parent = loader->doc;
         node = method(loader, parent, attrs, attrsLength, simpleXmlParseAttributes);
+        if (node && !empty) {
+            auto defs = _createDefsNode(loader, nullptr, nullptr, 0, nullptr);
+            loader->stack.push(defs);
+            loader->currentGraphicsNode = node;
+        }
     } else if ((gradientMethod = _findGradientFactory(tagName))) {
         SvgStyleGradient* gradient;
         gradient = gradientMethod(loader, attrs, attrsLength);
@@ -3257,7 +3390,7 @@ static void _inefficientNodeCheck(TVG_UNUSED SvgNode* node)
 #ifdef THORVG_LOG_ENABLED
     auto type = simpleXmlNodeTypeToString(node->type);
 
-    if (!node->display && node->type != SvgNodeType::ClipPath && node->type != SvgNodeType::Symbol) TVGLOG("SVG", "Inefficient elements used [Display is none][Node Type : %s]", type);
+    if (!node->style->display && node->type != SvgNodeType::ClipPath) TVGLOG("SVG", "Inefficient elements used [Display is none][Node Type : %s]", type);
     if (node->style->opacity == 0) TVGLOG("SVG", "Inefficient elements used [Opacity is zero][Node Type : %s]", type);
     if (node->style->fill.opacity == 0 && node->style->stroke.opacity == 0) TVGLOG("SVG", "Inefficient elements used [Fill opacity and stroke opacity are zero][Node Type : %s]", type);
 
@@ -3699,10 +3832,11 @@ bool SvgLoader::open(const char* data, uint32_t size, bool copy)
     clear();
 
     if (copy) {
-        content = (char*)malloc(size);
+        content = (char*)malloc(size + 1);
         if (!content) return false;
         memcpy((char*)content, data, size);
-    } else content = data;
+        content[size] = '\0';
+    } else content = (char*)data;
 
     this->size = size;
     this->copy = copy;
@@ -3726,7 +3860,7 @@ bool SvgLoader::open(const string& path)
 
     if (filePath.empty()) return false;
 
-    content = filePath.c_str();
+    content = (char*)filePath.c_str();
     size = filePath.size();
 
     return header();

+ 1 - 1
thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.h

@@ -31,7 +31,7 @@ class SvgLoader : public ImageLoader, public Task
 public:
     string filePath;
     string svgPath = "";
-    const char* content = nullptr;
+    char* content = nullptr;
     uint32_t size = 0;
 
     SvgLoaderData loaderData;

+ 4 - 3
thirdparty/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h

@@ -485,11 +485,12 @@ struct SvgStyleProperty
     SvgComposite mask;
     int opacity;
     SvgColor color;
-    bool curColorSet;
     char* cssClass;
-    bool paintOrder; //true if default (fill, stroke), false otherwise
     SvgStyleFlags flags;
     SvgStyleFlags flagsImportance; //indicates the importance of the flag - if set, higher priority is applied (https://drafts.csswg.org/css-cascade-4/#importance)
+    bool curColorSet;
+    bool paintOrder; //true if default (fill, stroke), false otherwise
+    bool display;
 };
 
 struct SvgNode
@@ -518,7 +519,6 @@ struct SvgNode
         SvgCssStyleNode cssStyle;
         SvgSymbolNode symbol;
     } node;
-    bool display;
     ~SvgNode();
 };
 
@@ -560,6 +560,7 @@ struct SvgLoaderData
     int level = 0;
     bool result = false;
     bool style = false;
+    SvgNode* currentGraphicsNode = nullptr;
 };
 
 struct Box

+ 11 - 5
thirdparty/thorvg/src/loaders/svg/tvgSvgPath.cpp

@@ -126,7 +126,7 @@ void _pathAppendArcTo(Array<PathCommand>* cmds, Array<Point>* pts, Point* cur, P
     rx = fabsf(rx);
     ry = fabsf(ry);
 
-    angle = angle * MATH_PI / 180.0f;
+    angle = mathDeg2Rad(angle);
     cosPhi = cosf(angle);
     sinPhi = sinf(angle);
     dx2 = (sx - x) / 2.0f;
@@ -311,7 +311,7 @@ static int _numberCount(char cmd)
 }
 
 
-static bool _processCommand(Array<PathCommand>* cmds, Array<Point>* pts, char cmd, float* arr, int count, Point* cur, Point* curCtl, Point* startPoint, bool *isQuadratic)
+static bool _processCommand(Array<PathCommand>* cmds, Array<Point>* pts, char cmd, float* arr, int count, Point* cur, Point* curCtl, Point* startPoint, bool *isQuadratic, bool* closed)
 {
     switch (cmd) {
         case 'm':
@@ -464,6 +464,7 @@ static bool _processCommand(Array<PathCommand>* cmds, Array<Point>* pts, char cm
         case 'Z': {
             cmds->push(PathCommand::Close);
             *cur = *startPoint;
+            *closed = true;
             break;
         }
         case 'a':
@@ -488,7 +489,7 @@ static bool _processCommand(Array<PathCommand>* cmds, Array<Point>* pts, char cm
 }
 
 
-static char* _nextCommand(char* path, char* cmd, float* arr, int* count)
+static char* _nextCommand(char* path, char* cmd, float* arr, int* count, bool* closed)
 {
     int large, sweep;
 
@@ -500,6 +501,9 @@ static char* _nextCommand(char* path, char* cmd, float* arr, int* count)
     } else {
         if (*cmd == 'm') *cmd = 'l';
         else if (*cmd == 'M') *cmd = 'L';
+        else {
+          if (*closed) return nullptr;
+        }
     }
     if (*count == 7) {
         //Special case for arc command
@@ -548,6 +552,7 @@ bool svgPathToShape(const char* svgPath, Shape* shape)
     Point startPoint = { 0, 0 };
     char cmd = 0;
     bool isQuadratic = false;
+    bool closed = false;
     char* path = (char*)svgPath;
 
     auto& pts = P(shape)->rs.path.pts;
@@ -555,9 +560,10 @@ bool svgPathToShape(const char* svgPath, Shape* shape)
     auto lastCmds = cmds.count;
 
     while ((path[0] != '\0')) {
-        path = _nextCommand(path, &cmd, numberArray, &numberCount);
+        path = _nextCommand(path, &cmd, numberArray, &numberCount, &closed);
         if (!path) break;
-        if (!_processCommand(&cmds, &pts, cmd, numberArray, numberCount, &cur, &curCtl, &startPoint, &isQuadratic)) break;
+        closed = false;
+        if (!_processCommand(&cmds, &pts, cmd, numberArray, numberCount, &cur, &curCtl, &startPoint, &isQuadratic, &closed)) break;
     }
 
     if (cmds.count > lastCmds && cmds[lastCmds] != PathCommand::MoveTo) return false;

+ 4 - 4
thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp

@@ -310,7 +310,7 @@ static void _applyProperty(SvgLoaderData& loaderData, SvgNode* node, Shape* vg,
 
     //Clip transformation is applied directly to the path in the _appendClipShape function
     if (node->transform && !clip) vg->transform(*node->transform);
-    if (node->type == SvgNodeType::Doc || !node->display) return;
+    if (node->type == SvgNodeType::Doc || !node->style->display) return;
 
     //If fill property is nullptr then do nothing
     if (style->fill.paint.none) {
@@ -557,7 +557,7 @@ static bool _isValidImageMimeTypeAndEncoding(const char** href, const char** mim
 
 static unique_ptr<Picture> _imageBuildHelper(SvgLoaderData& loaderData, SvgNode* node, const Box& vBox, const string& svgPath)
 {
-    if (!node->node.image.href) return nullptr;
+    if (!node->node.image.href || !strlen(node->node.image.href)) return nullptr;
     auto picture = Picture::gen();
 
     TaskScheduler::async(false);    //force to load a picture on the same thread
@@ -782,13 +782,13 @@ static unique_ptr<Scene> _sceneBuildHelper(SvgLoaderData& loaderData, const SvgN
         // For a Symbol node, the viewBox transformation has to be applied first - see _useBuildHelper()
         if (!mask && node->transform && node->type != SvgNodeType::Symbol) scene->transform(*node->transform);
 
-        if (node->display && node->style->opacity != 0) {
+        if (node->style->display && node->style->opacity != 0) {
             auto child = node->child.data;
             for (uint32_t i = 0; i < node->child.count; ++i, ++child) {
                 if (_isGroupType((*child)->type)) {
                     if ((*child)->type == SvgNodeType::Use)
                         scene->push(_useBuildHelper(loaderData, *child, vBox, svgPath, depth + 1, isMaskWhite));
-                    else
+                    else if (!((*child)->type == SvgNodeType::Symbol && node->type != SvgNodeType::Use))
                         scene->push(_sceneBuildHelper(loaderData, *child, vBox, svgPath, false, depth + 1, isMaskWhite));
                 } else if ((*child)->type == SvgNodeType::Image) {
                     auto image = _imageBuildHelper(loaderData, *child, vBox, svgPath);

+ 3 - 2
thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.cpp

@@ -47,7 +47,6 @@ size_t svgUtilURLDecode(const char *src, char** dst)
     if (length == 0) return 0;
 
     char* decoded = (char*)malloc(sizeof(char) * length + 1);
-    decoded[length] = '\0';
 
     char a, b;
     int idx =0;
@@ -64,7 +63,9 @@ size_t svgUtilURLDecode(const char *src, char** dst)
             decoded[idx++] = *src++;
         }
     }
+    decoded[idx] = '\0';
 
     *dst = decoded;
-    return length + 1;
+    return idx + 1;
 }
+

+ 4 - 1
thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp

@@ -314,7 +314,10 @@ bool simpleXmlParseAttributes(const char* buf, unsigned bufLength, simpleXMLAttr
             if ((*keyEnd == '=') || (isspace((unsigned char)*keyEnd))) break;
         }
         if (keyEnd == itrEnd) goto error;
-        if (keyEnd == key) continue;
+        if (keyEnd == key) {  // There is no key. This case is invalid, but explores the following syntax.
+            itr = keyEnd + 1;
+            continue;
+        }
 
         if (*keyEnd == '=') value = keyEnd + 1;
         else {

+ 6 - 1
thirdparty/thorvg/src/renderer/sw_engine/tvgSwRaster.cpp

@@ -1755,8 +1755,13 @@ static bool _rasterRadialGradientRle(SwSurface* surface, const SwRleData* rle, c
 
 void rasterGrayscale8(uint8_t *dst, uint8_t val, uint32_t offset, int32_t len)
 {
-    //OPTIMIZE_ME: Support SIMD
+#if defined(THORVG_AVX_VECTOR_SUPPORT)
+    avxRasterGrayscale8(dst, val, offset, len);
+#elif defined(THORVG_NEON_VECTOR_SUPPORT)
+    neonRasterGrayscale8(dst, val, offset, len);
+#else
     cRasterPixels(dst, val, offset, len);
+#endif
 }
 
 

+ 18 - 1
thirdparty/thorvg/src/renderer/sw_engine/tvgSwRasterAvx.h

@@ -1,4 +1,4 @@
-/*
+/*
  * Copyright (c) 2021 - 2024 the ThorVG project. All rights reserved.
 
  * Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -62,6 +62,23 @@ static inline __m128i ALPHA_BLEND(__m128i c, __m128i a)
 }
 
 
+static void avxRasterGrayscale8(uint8_t* dst, uint8_t val, uint32_t offset, int32_t len) 
+{
+    dst += offset; 
+
+    __m256i vecVal = _mm256_set1_epi8(val);
+
+    int32_t i = 0;
+    for (; i <= len - 32; i += 32) {
+        _mm256_storeu_si256((__m256i*)(dst + i), vecVal);
+    }
+
+    for (; i < len; ++i) {
+        dst[i] = val;
+    }
+}
+
+
 static void avxRasterPixel32(uint32_t *dst, uint32_t val, uint32_t offset, int32_t len)
 {
     //1. calculate how many iterations we need to cover the length

+ 50 - 10
thirdparty/thorvg/src/renderer/sw_engine/tvgSwRasterNeon.h

@@ -24,6 +24,15 @@
 
 #include <arm_neon.h>
 
+//TODO : need to support windows ARM
+ 
+#if defined(__ARM_64BIT_STATE) || defined(_M_ARM64)
+#define TVG_AARCH64 1
+#else
+#define TVG_AARCH64 0
+#endif
+
+
 static inline uint8x8_t ALPHA_BLEND(uint8x8_t c, uint8x8_t a)
 {
     uint16x8_t t = vmull_u8(c, a);
@@ -31,19 +40,50 @@ static inline uint8x8_t ALPHA_BLEND(uint8x8_t c, uint8x8_t a)
 }
 
 
-static void neonRasterPixel32(uint32_t *dst, uint32_t val, uint32_t offset, int32_t len)
+static void neonRasterGrayscale8(uint8_t* dst, uint8_t val, uint32_t offset, int32_t len)
 {
-    uint32_t iterations = len / 4;
-    uint32_t neonFilled = iterations * 4;
+    dst += offset;
 
+    int32_t i = 0;
+    const uint8x16_t valVec = vdupq_n_u8(val);
+#if TVG_AARCH64
+    uint8x16x4_t valQuad = {valVec, valVec, valVec, valVec};
+    for (; i <= len - 16 * 4; i += 16 * 4) {
+        vst1q_u8_x4(dst + i, valQuad);
+    }
+#else
+    for (; i <= len - 16; i += 16) {
+        vst1q_u8(dst + i, valVec);
+    }
+#endif
+    for (; i < len; i++) {
+        dst[i] = val;
+    }
+}
+
+
+static void neonRasterPixel32(uint32_t *dst, uint32_t val, uint32_t offset, int32_t len)
+{
     dst += offset;
-    uint32x4_t vectorVal = {val, val, val, val};
 
+    uint32x4_t vectorVal = vdupq_n_u32(val);
+
+#if TVG_AARCH64
+    uint32_t iterations = len / 16;
+    uint32_t neonFilled = iterations * 16;
+    uint32x4x4_t valQuad = {vectorVal, vectorVal, vectorVal, vectorVal};
+    for (uint32_t i = 0; i < iterations; ++i) {
+        vst4q_u32(dst, valQuad);
+        dst += 16;
+    }
+#else
+    uint32_t iterations = len / 4;
+    uint32_t neonFilled = iterations * 4;
     for (uint32_t i = 0; i < iterations; ++i) {
         vst1q_u32(dst, vectorVal);
         dst += 4;
     }
-
+#endif
     int32_t leftovers = len - neonFilled;
     while (leftovers--) *dst++ = val;
 }
@@ -56,7 +96,7 @@ static bool neonRasterTranslucentRle(SwSurface* surface, const SwRleData* rle, u
         return false;
     }
 
-    auto color = surface->blender.join(r, g, b, a);
+    auto color = surface->join(r, g, b, a);
     auto span = rle->spans;
     uint32_t src;
     uint8x8_t *vDst = nullptr;
@@ -67,9 +107,9 @@ static bool neonRasterTranslucentRle(SwSurface* surface, const SwRleData* rle, u
         else src = color;
 
         auto dst = &surface->buf32[span->y * surface->stride + span->x];
-        auto ialpha = IALPHA(src);
+        auto ialpha = IA(src);
 
-        if ((((uint32_t) dst) & 0x7) != 0) {
+        if ((((uintptr_t) dst) & 0x7) != 0) {
             //fill not aligned byte
             *dst = src + ALPHA_BLEND(*dst, ialpha);
             vDst = (uint8x8_t*)(dst + 1);
@@ -101,7 +141,7 @@ static bool neonRasterTranslucentRect(SwSurface* surface, const SwBBox& region,
         return false;
     }
 
-    auto color = surface->blender.join(r, g, b, a);
+    auto color = surface->join(r, g, b, a);
     auto buffer = surface->buf32 + (region.min.y * surface->stride) + region.min.x;
     auto h = static_cast<uint32_t>(region.max.y - region.min.y);
     auto w = static_cast<uint32_t>(region.max.x - region.min.x);
@@ -116,7 +156,7 @@ static bool neonRasterTranslucentRect(SwSurface* surface, const SwBBox& region,
     for (uint32_t y = 0; y < h; ++y) {
         auto dst = &buffer[y * surface->stride];
 
-        if ((((uint32_t) dst) & 0x7) != 0) {
+        if ((((uintptr_t) dst) & 0x7) != 0) {
             //fill not aligned byte
             *dst = color + ALPHA_BLEND(*dst, ialpha);
             vDst = (uint8x8_t*) (dst + 1);

+ 48 - 38
thirdparty/thorvg/src/renderer/sw_engine/tvgSwShape.cpp

@@ -22,43 +22,26 @@
 
 #include "tvgSwCommon.h"
 #include "tvgMath.h"
-#include "tvgBezier.h"
+#include "tvgLines.h"
 
 /************************************************************************/
 /* Internal Class Implementation                                        */
 /************************************************************************/
 
-
-struct Line
-{
-    Point pt1;
-    Point pt2;
-};
-
-
-static float _lineLength(const Point& pt1, const Point& pt2)
-{
-    Point diff = {pt2.x - pt1.x, pt2.y - pt1.y};
-    return sqrtf(diff.x * diff.x + diff.y * diff.y);
-}
-
-
-static void _lineSplitAt(const Line& cur, float at, Line& left, Line& right)
+static bool _outlineBegin(SwOutline& outline)
 {
-    auto len = _lineLength(cur.pt1, cur.pt2);
-    auto dx = ((cur.pt2.x - cur.pt1.x) / len) * at;
-    auto dy = ((cur.pt2.y - cur.pt1.y) / len) * at;
-    left.pt1 = cur.pt1;
-    left.pt2.x = left.pt1.x + dx;
-    left.pt2.y = left.pt1.y + dy;
-    right.pt1 = left.pt2;
-    right.pt2 = cur.pt2;
+    //Make a contour if lineTo/curveTo without calling close or moveTo beforehand.
+    if (outline.pts.empty()) return false;
+    outline.cntrs.push(outline.pts.count - 1);
+    outline.closed.push(false);
+    outline.pts.push(outline.pts[outline.cntrs.last()]);
+    outline.types.push(SW_CURVE_TYPE_POINT);
+    return false;
 }
 
 
 static bool _outlineEnd(SwOutline& outline)
 {
-    //Make a contour if lineTo/curveTo without calling close/moveTo beforehand.
     if (outline.pts.empty()) return false;
     outline.cntrs.push(outline.pts.count - 1);
     outline.closed.push(false);
@@ -119,10 +102,11 @@ static bool _outlineClose(SwOutline& outline)
 static void _dashLineTo(SwDashStroke& dash, const Point* to, const Matrix* transform)
 {
     Line cur = {dash.ptCur, *to};
-    auto len = _lineLength(cur.pt1, cur.pt2);
+    auto len = lineLength(cur.pt1, cur.pt2);
 
     if (mathZero(len)) {
         _outlineMoveTo(*dash.outline, &dash.ptCur, transform);
+    //draw the current line fully
     } else if (len < dash.curLen) {
         dash.curLen -= len;
         if (!dash.curOpGap) {
@@ -132,12 +116,13 @@ static void _dashLineTo(SwDashStroke& dash, const Point* to, const Matrix* trans
             }
             _outlineLineTo(*dash.outline, to, transform);
         }
+    //draw the current line partially
     } else {
         while (len - dash.curLen > 0.0001f) {
             Line left, right;
             if (dash.curLen > 0) {
                 len -= dash.curLen;
-                _lineSplitAt(cur, dash.curLen, left, right);
+                lineSplitAt(cur, dash.curLen, left, right);
                 if (!dash.curOpGap) {
                     if (dash.move || dash.pattern[dash.curIdx] - dash.curLen < FLT_EPSILON) {
                         _outlineMoveTo(*dash.outline, &left.pt1, transform);
@@ -180,6 +165,7 @@ static void _dashCubicTo(SwDashStroke& dash, const Point* ctrl1, const Point* ct
     Bezier cur = {dash.ptCur, *ctrl1, *ctrl2, *to};
     auto len = bezLength(cur);
 
+    //draw the current line fully
     if (mathZero(len)) {
         _outlineMoveTo(*dash.outline, &dash.ptCur, transform);
     } else if (len < dash.curLen) {
@@ -191,6 +177,7 @@ static void _dashCubicTo(SwDashStroke& dash, const Point* ctrl1, const Point* ct
             }
             _outlineCubicTo(*dash.outline, ctrl1, ctrl2, to, transform);
         }
+    //draw the current line partially
     } else {
         while ((len - dash.curLen) > 0.0001f) {
             Bezier left, right;
@@ -240,7 +227,15 @@ static void _dashClose(SwDashStroke& dash, const Matrix* transform)
 }
 
 
-static void _dashMoveTo(SwDashStroke& dash, uint32_t offIdx, float offset, const Point* pts, const Matrix* transform)
+static void _dashMoveTo(SwDashStroke& dash, const Point* pts)
+{
+    dash.ptCur = *pts;
+    dash.ptStart = *pts;
+    dash.move = true;
+}
+
+
+static void _dashMoveTo(SwDashStroke& dash, uint32_t offIdx, float offset, const Point* pts)
 {
     dash.curIdx = offIdx % dash.cnt;
     dash.curLen = dash.pattern[dash.curIdx] - offset;
@@ -275,7 +270,7 @@ static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* trans
 
         //default
         if (end > begin) {
-            if (begin > 0) dash.cnt += 4;
+            if (begin > 0.0f) dash.cnt += 4;
             else dash.cnt += 2;
         //looping
         } else dash.cnt += 3;
@@ -293,7 +288,7 @@ static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* trans
             dash.pattern[0] = 0;     //zero dash to start with a space.
             dash.pattern[1] = begin;
             dash.pattern[2] = end - begin;
-            dash.pattern[3] = length - (end - begin);
+            dash.pattern[3] = length - end;
         }
 
         trimmed = true;
@@ -322,14 +317,22 @@ static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* trans
 
     dash.outline = mpoolReqDashOutline(mpool, tid);
 
-    while (cmdCnt-- > 0) {
+    //must begin with moveTo
+    if (cmds[0] == PathCommand::MoveTo) {
+        _dashMoveTo(dash, offIdx, offset, pts);
+        cmds++;
+        pts++;
+    }
+
+    while (--cmdCnt > 0) {
         switch (*cmds) {
             case PathCommand::Close: {
                 _dashClose(dash, transform);
                 break;
             }
             case PathCommand::MoveTo: {
-                _dashMoveTo(dash, offIdx, offset, pts, transform);
+                if (rshape->stroke->trim.individual) _dashMoveTo(dash, pts);
+                else _dashMoveTo(dash, offIdx, offset, pts);
                 ++pts;
                 break;
             }
@@ -367,13 +370,19 @@ static float _outlineLength(const RenderShape* rshape)
 
     const Point* close = nullptr;
     auto length = 0.0f;
+    auto slength = -1.0f;
+    auto simultaneous = !rshape->stroke->trim.individual;
 
     //Compute the whole length
     while (cmdCnt-- > 0) {
         switch (*cmds) {
             case PathCommand::Close: {
                 length += mathLength(pts - 1, close);
-                ++pts;
+                //retrieve the max length of the shape if the simultaneous mode.
+                if (simultaneous) {
+                    if (slength < length) slength = length;
+                    length = 0.0f;
+                }
                 break;
             }
             case PathCommand::MoveTo: {
@@ -394,7 +403,8 @@ static float _outlineLength(const RenderShape* rshape)
         }
         ++cmds;
     }
-    return length;
+    if (simultaneous && slength > length) return slength;
+    else return length;
 }
 
 
@@ -429,7 +439,7 @@ static bool _genOutline(SwShape* shape, const RenderShape* rshape, const Matrix*
 
     shape->outline = mpoolReqOutline(mpool, tid);
     auto outline = shape->outline;
-    bool closed = false;
+    auto closed = false;
 
     //Generate Outlines
     while (cmdCnt-- > 0) {
@@ -444,13 +454,13 @@ static bool _genOutline(SwShape* shape, const RenderShape* rshape, const Matrix*
                 break;
             }
             case PathCommand::LineTo: {
-                if (closed) closed = _outlineEnd(*outline);
+                if (closed) closed = _outlineBegin(*outline);
                 _outlineLineTo(*outline, pts, transform);
                 ++pts;
                 break;
             }
             case PathCommand::CubicTo: {
-                if (closed) closed = _outlineEnd(*outline);
+                if (closed) closed = _outlineBegin(*outline);
                 _outlineCubicTo(*outline, pts, pts + 1, pts + 2, transform);
                 pts += 3;
                 break;

+ 27 - 0
thirdparty/thorvg/src/renderer/tvgAnimation.cpp

@@ -93,6 +93,33 @@ float Animation::duration() const noexcept
 }
 
 
+Result Animation::segment(float begin, float end) noexcept
+{
+    if (begin < 0.0 || end > 1.0 || begin >= end) return Result::InvalidArguments;
+
+    auto loader = pImpl->picture->pImpl->loader;
+    if (!loader) return Result::InsufficientCondition;
+    if (!loader->animatable()) return Result::NonSupport;
+
+    static_cast<FrameModule*>(loader)->segment(begin, end);
+
+    return Result::Success;
+}
+
+
+Result Animation::segment(float *begin, float *end) noexcept
+{
+    auto loader = pImpl->picture->pImpl->loader;
+    if (!loader) return Result::InsufficientCondition;
+    if (!loader->animatable()) return Result::NonSupport;
+    if (!begin && !end) return Result::InvalidArguments;
+
+    static_cast<FrameModule*>(loader)->segment(begin, end);
+
+    return Result::Success;
+}
+
+
 unique_ptr<Animation> Animation::gen() noexcept
 {
     return unique_ptr<Animation>(new Animation);

+ 2 - 13
thirdparty/thorvg/src/renderer/tvgCanvas.h

@@ -98,25 +98,14 @@ struct Canvas::Impl
         auto flag = RenderUpdateFlag::None;
         if (refresh || force) flag = RenderUpdateFlag::All;
 
-        //Update single paint node
         if (paint) {
-            //Optimize Me: Can we skip the searching?
-            for (auto paint2 : paints) {
-                if (paint2 == paint) {
-                    paint->pImpl->update(renderer, nullptr, clips, 255, flag);
-                    return Result::Success;
-                }
-            }
-            return Result::InvalidArguments;
-        //Update all retained paint nodes
+            paint->pImpl->update(renderer, nullptr, clips, 255, flag);
         } else {
             for (auto paint : paints) {
                 paint->pImpl->update(renderer, nullptr, clips, 255, flag);
             }
+            refresh = false;
         }
-
-        refresh = false;
-
         return Result::Success;
     }
 

+ 15 - 0
thirdparty/thorvg/src/renderer/tvgFrameModule.h

@@ -31,6 +31,9 @@ namespace tvg
 class FrameModule: public ImageLoader
 {
 public:
+    float segmentBegin = 0.0f;
+    float segmentEnd = 1.0f;
+
     FrameModule(FileType type) : ImageLoader(type) {}
     virtual ~FrameModule() {}
 
@@ -39,6 +42,18 @@ public:
     virtual float curFrame() = 0;           //return the current frame number
     virtual float duration() = 0;           //return the animation duration in seconds
 
+    void segment(float* begin, float* end)
+    {
+        if (begin) *begin = segmentBegin;
+        if (end) *end = segmentEnd;
+    }
+
+    void segment(float begin, float end)
+    {
+        segmentBegin = begin;
+        segmentEnd = end;
+    }
+
     virtual bool animatable() override { return true; }
 };
 

+ 2 - 2
thirdparty/thorvg/src/renderer/tvgGlCanvas.cpp

@@ -60,14 +60,14 @@ GlCanvas::~GlCanvas()
 }
 
 
-Result GlCanvas::target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h) noexcept
+Result GlCanvas::target(int32_t id, uint32_t w, uint32_t h) noexcept
 {
 #ifdef THORVG_GL_RASTER_SUPPORT
     //We know renderer type, avoid dynamic_cast for performance.
     auto renderer = static_cast<GlRenderer*>(Canvas::pImpl->renderer);
     if (!renderer) return Result::MemoryCorruption;
 
-    if (!renderer->target(buffer, stride, w, h)) return Result::Unknown;
+    if (!renderer->target(id, w, h)) return Result::Unknown;
 
     //Paints must be updated again with this new target.
     Canvas::pImpl->needRefresh();

+ 34 - 16
thirdparty/thorvg/src/renderer/tvgLoader.cpp

@@ -178,7 +178,6 @@ static LoadModule* _findByPath(const string& path)
     if (!ext.compare("tvg")) return _find(FileType::Tvg);
     if (!ext.compare("svg")) return _find(FileType::Svg);
     if (!ext.compare("json")) return _find(FileType::Lottie);
-    if (!ext.compare("lottie")) return _find(FileType::Lottie);
     if (!ext.compare("png")) return _find(FileType::Png);
     if (!ext.compare("jpg")) return _find(FileType::Jpg);
     if (!ext.compare("webp")) return _find(FileType::Webp);
@@ -271,7 +270,7 @@ bool LoaderMgr::term()
         auto tmp = loader;
         loader = loader->next;
         _activeLoaders.remove(tmp);
-        if (ret) delete(loader);
+        if (ret) delete(tmp);
     }
     return true;
 }
@@ -295,15 +294,24 @@ LoadModule* LoaderMgr::loader(const string& path, bool* invalid)
 {
     *invalid = false;
 
-    if (auto loader = _findFromCache(path)) return loader;
+    //TODO: lottie is not sharable.
+    auto allowCache = true;
+    auto ext = path.substr(path.find_last_of(".") + 1);
+    if (!ext.compare("json")) allowCache = false;
+
+    if (allowCache) {
+        if (auto loader = _findFromCache(path)) return loader;
+    }
 
     if (auto loader = _findByPath(path)) {
         if (loader->open(path)) {
-            loader->hashpath = strdup(path.c_str());
-            loader->pathcache = true;
-            {
-                ScopedLock lock(key);
-                _activeLoaders.back(loader);
+            if (allowCache) {
+                loader->hashpath = strdup(path.c_str());
+                loader->pathcache = true;
+                {
+                    ScopedLock lock(key);
+                    _activeLoaders.back(loader);
+                }
             }
             return loader;
         }
@@ -313,11 +321,13 @@ LoadModule* LoaderMgr::loader(const string& path, bool* invalid)
     for (int i = 0; i < static_cast<int>(FileType::Raw); i++) {
         if (auto loader = _find(static_cast<FileType>(i))) {
             if (loader->open(path)) {
-                loader->hashpath = strdup(path.c_str());
-                loader->pathcache = true;
-                {
-                    ScopedLock lock(key);
-                    _activeLoaders.back(loader);
+                if (allowCache) {
+                    loader->hashpath = strdup(path.c_str());
+                    loader->pathcache = true;
+                    {
+                        ScopedLock lock(key);
+                        _activeLoaders.back(loader);
+                    }
                 }
                 return loader;
             }
@@ -354,7 +364,15 @@ LoadModule* LoaderMgr::loader(const char* data, uint32_t size, const string& mim
 {
     //Note that users could use the same data pointer with the different content.
     //Thus caching is only valid for shareable.
-    if (!copy) {
+    auto allowCache = !copy;
+
+    //TODO: lottie is not sharable.
+    if (allowCache) {
+        auto type = _convert(mimeType);
+        if (type == FileType::Lottie) allowCache = false;
+    }
+
+    if (allowCache) {
         if (auto loader = _findFromCache(data, size, mimeType)) return loader;
     }
 
@@ -362,7 +380,7 @@ LoadModule* LoaderMgr::loader(const char* data, uint32_t size, const string& mim
     if (!mimeType.empty()) {
         if (auto loader = _findByType(mimeType)) {
             if (loader->open(data, size, copy)) {
-                if (!copy) {
+                if (allowCache) {
                     loader->hashkey = HASH_KEY(data);
                     ScopedLock lock(key);
                     _activeLoaders.back(loader);
@@ -379,7 +397,7 @@ LoadModule* LoaderMgr::loader(const char* data, uint32_t size, const string& mim
         auto loader = _find(static_cast<FileType>(i));
         if (loader) {
             if (loader->open(data, size, copy)) {
-                if (!copy) {
+                if (allowCache) {
                     loader->hashkey = HASH_KEY(data);
                     ScopedLock lock(key);
                     _activeLoaders.back(loader);

+ 1 - 0
thirdparty/thorvg/src/renderer/tvgRender.h

@@ -163,6 +163,7 @@ struct RenderStroke
     struct {
         float begin = 0.0f;
         float end = 1.0f;
+        bool individual = false;
     } trim;
 
     ~RenderStroke()

+ 7 - 4
thirdparty/thorvg/src/renderer/tvgSaver.cpp

@@ -157,14 +157,17 @@ Result Saver::save(unique_ptr<Animation> animation, const string& path, uint32_t
     auto a = animation.release();
     if (!a) return Result::MemoryCorruption;
 
+    //animation holds the picture, it must be 1 at the bottom.
+    auto remove = PP(a->picture())->refCnt <= 1 ? true : false;
+
     if (mathZero(a->totalFrame())) {
-        delete(a);
+        if (remove) delete(a);
         return Result::InsufficientCondition;
     }
 
     //Already on saving an other resource.
     if (pImpl->saveModule) {
-        delete(a);
+        if (remove) delete(a);
         return Result::InsufficientCondition;
     }
 
@@ -173,12 +176,12 @@ Result Saver::save(unique_ptr<Animation> animation, const string& path, uint32_t
             pImpl->saveModule = saveModule;
             return Result::Success;
         } else {
-            delete(a);
+            if (remove) delete(a);
             delete(saveModule);
             return Result::Unknown;
         }
     }
-    delete(a);
+    if (remove) delete(a);
     return Result::NonSupport;
 }
 

+ 6 - 4
thirdparty/thorvg/src/renderer/tvgShape.cpp

@@ -145,13 +145,15 @@ Result Shape::appendArc(float cx, float cy, float radius, float startAngle, floa
     //just circle
     if (sweep >= 360.0f || sweep <= -360.0f) return appendCircle(cx, cy, radius, radius);
 
-    startAngle = (startAngle * MATH_PI) / 180.0f;
-    sweep = sweep * MATH_PI / 180.0f;
+    const float arcPrecision = 1e-5f;
+    startAngle = mathDeg2Rad(startAngle);
+    sweep = mathDeg2Rad(sweep);
 
-    auto nCurves = ceil(fabsf(sweep / MATH_PI2));
+    auto nCurves = static_cast<int>(fabsf(sweep / MATH_PI2));
+    if (fabsf(sweep / MATH_PI2) - nCurves > arcPrecision) ++nCurves;
     auto sweepSign = (sweep < 0 ? -1 : 1);
     auto fract = fmodf(sweep, MATH_PI2);
-    fract = (mathZero(fract)) ? MATH_PI2 * sweepSign : fract;
+    fract = (fabsf(fract) < arcPrecision) ? MATH_PI2 * sweepSign : fract;
 
     //Start from here
     Point start = {radius * cosf(startAngle), radius * sinf(startAngle)};

+ 17 - 3
thirdparty/thorvg/src/renderer/tvgShape.h

@@ -76,8 +76,21 @@ struct Shape::Impl
         //Composition test
         const Paint* target;
         auto method = shape->composite(&target);
-        if (!target || method == tvg::CompositeMethod::ClipPath) return false;
-        if (target->pImpl->opacity == 255 || target->pImpl->opacity == 0) return false;
+        if (!target || method == CompositeMethod::ClipPath) return false;
+        if (target->pImpl->opacity == 255 || target->pImpl->opacity == 0) {
+            if (target->identifier() == TVG_CLASS_ID_SHAPE) {
+                auto shape = static_cast<const Shape*>(target);
+                if (!shape->fill()) {
+                    uint8_t r, g, b, a;
+                    shape->fillColor(&r, &g, &b, &a);
+                    if (a == 0 || a == 255) {
+                        if (method == CompositeMethod::LumaMask || method == CompositeMethod::InvLumaMask) {
+                            if ((r == 255 && g == 255 && b == 255) || (r == 0 && g == 0 && b == 0)) return false;
+                        } else return false;
+                    }
+                }
+            }
+        }
 
         return true;
     }
@@ -203,7 +216,7 @@ struct Shape::Impl
         return true;
     }
 
-    bool strokeTrim(float begin, float end)
+    bool strokeTrim(float begin, float end, bool individual)
     {
         if (!rs.stroke) {
             if (begin == 0.0f && end == 1.0f) return true;
@@ -214,6 +227,7 @@ struct Shape::Impl
 
         rs.stroke->trim.begin = begin;
         rs.stroke->trim.end = end;
+        rs.stroke->trim.individual = individual;
         flag |= RenderUpdateFlag::Stroke;
 
         return true;

+ 7 - 4
thirdparty/thorvg/update-thorvg.sh

@@ -1,6 +1,6 @@
 #!/bin/bash -e
 
-VERSION=0.12.10
+VERSION=0.13.3
 
 cd thirdparty/thorvg/ || true
 rm -rf AUTHORS LICENSE inc/ src/ *.zip *.tar.gz tmp/
@@ -38,6 +38,7 @@ cat << EOF > ../inc/config.h
 #define THORVG_SVG_LOADER_SUPPORT
 #define THORVG_PNG_LOADER_SUPPORT
 #define THORVG_JPG_LOADER_SUPPORT
+#define THORVG_WEBP_LOADER_SUPPORT
 #define THORVG_THREAD_SUPPORT
 
 // For internal debugging:
@@ -55,11 +56,13 @@ cp -rv src/renderer ../src/
 rm -rfv ../src/renderer/gl_engine
 rm -rfv ../src/renderer/wg_engine
 
-# Enabled embedded loaders: raw, JPEG, PNG.
+# Enabled embedded loaders: raw, JPEG, PNG, WebP.
 mkdir ../src/loaders
 cp -rv src/loaders/svg src/loaders/raw  ../src/loaders/
-cp -rv src/loaders/jpg  ../src/loaders/
-cp -rv src/loaders/png src/loaders/external_png  ../src/loaders/
+cp -rv src/loaders/external_png ../src/loaders/
+cp -rv src/loaders/external_webp ../src/loaders/
+# Not using external jpg as it's turbojpeg, which we don't have.
+cp -rv src/loaders/jpg ../src/loaders/
 
 popd
 rm -rf tmp