Browse Source

Merge pull request #80095 from capnm/update_thorvg_0.10.0

Update ThorVG to v0.10.0
Rémi Verschelde 2 năm trước cách đây
mục cha
commit
08690d6af5
70 tập tin đã thay đổi với 3278 bổ sung6417 xóa
  1. 3 10
      modules/svg/SCsub
  2. 1 1
      modules/svg/image_loader_svg.cpp
  3. 1 1
      modules/text_server_adv/thorvg_svg_in_ot.cpp
  4. 1 1
      modules/text_server_fb/thorvg_svg_in_ot.cpp
  5. 1 1
      thirdparty/README.md
  6. 1 0
      thirdparty/thorvg/AUTHORS
  7. 3 11
      thirdparty/thorvg/inc/config.h
  8. 291 73
      thirdparty/thorvg/inc/thorvg.h
  9. 245 24
      thirdparty/thorvg/src/lib/sw_engine/tvgSwCommon.h
  10. 223 17
      thirdparty/thorvg/src/lib/sw_engine/tvgSwFill.cpp
  11. 10 21
      thirdparty/thorvg/src/lib/sw_engine/tvgSwImage.cpp
  12. 3 5
      thirdparty/thorvg/src/lib/sw_engine/tvgSwMath.cpp
  13. 16 35
      thirdparty/thorvg/src/lib/sw_engine/tvgSwMemPool.cpp
  14. 567 258
      thirdparty/thorvg/src/lib/sw_engine/tvgSwRaster.cpp
  15. 5 5
      thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterAvx.h
  16. 64 15
      thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterC.h
  17. 3 3
      thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterNeon.h
  18. 755 46
      thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterTexmap.h
  19. 0 168
      thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterTexmapInternal.h
  20. 89 54
      thirdparty/thorvg/src/lib/sw_engine/tvgSwRenderer.cpp
  21. 6 6
      thirdparty/thorvg/src/lib/sw_engine/tvgSwRenderer.h
  22. 10 23
      thirdparty/thorvg/src/lib/sw_engine/tvgSwRle.cpp
  23. 49 111
      thirdparty/thorvg/src/lib/sw_engine/tvgSwShape.cpp
  24. 23 47
      thirdparty/thorvg/src/lib/sw_engine/tvgSwStroke.cpp
  25. 108 0
      thirdparty/thorvg/src/lib/tvgAnimation.cpp
  26. 36 18
      thirdparty/thorvg/src/lib/tvgArray.h
  27. 54 12
      thirdparty/thorvg/src/lib/tvgBezier.cpp
  28. 3 3
      thirdparty/thorvg/src/lib/tvgBezier.h
  29. 8 7
      thirdparty/thorvg/src/lib/tvgBinaryDesc.h
  30. 9 4
      thirdparty/thorvg/src/lib/tvgCanvas.cpp
  31. 14 14
      thirdparty/thorvg/src/lib/tvgCanvasImpl.h
  32. 3 1
      thirdparty/thorvg/src/lib/tvgCommon.h
  33. 2 2
      thirdparty/thorvg/src/lib/tvgFill.h
  34. 17 17
      thirdparty/thorvg/src/lib/tvgFrameModule.h
  35. 4 5
      thirdparty/thorvg/src/lib/tvgLoadModule.h
  36. 33 0
      thirdparty/thorvg/src/lib/tvgLoader.cpp
  37. 39 5
      thirdparty/thorvg/src/lib/tvgMath.h
  38. 29 6
      thirdparty/thorvg/src/lib/tvgPaint.cpp
  39. 11 10
      thirdparty/thorvg/src/lib/tvgPaint.h
  40. 1 8
      thirdparty/thorvg/src/lib/tvgPicture.cpp
  41. 40 18
      thirdparty/thorvg/src/lib/tvgPictureImpl.h
  42. 1 1
      thirdparty/thorvg/src/lib/tvgRender.cpp
  43. 64 19
      thirdparty/thorvg/src/lib/tvgRender.h
  44. 1 1
      thirdparty/thorvg/src/lib/tvgSaver.cpp
  45. 9 5
      thirdparty/thorvg/src/lib/tvgScene.cpp
  46. 54 45
      thirdparty/thorvg/src/lib/tvgSceneImpl.h
  47. 34 16
      thirdparty/thorvg/src/lib/tvgShape.cpp
  48. 102 66
      thirdparty/thorvg/src/lib/tvgShapeImpl.h
  49. 1 1
      thirdparty/thorvg/src/lib/tvgSwCanvas.cpp
  50. 1 0
      thirdparty/thorvg/src/lib/tvgTaskScheduler.h
  51. 0 120
      thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.cpp
  52. 0 143
      thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.cpp
  53. 0 52
      thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.h
  54. 0 3029
      thirdparty/thorvg/src/loaders/jpg/tvgJpgd.cpp
  55. 0 35
      thirdparty/thorvg/src/loaders/jpg/tvgJpgd.h
  56. 63 11
      thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.cpp
  57. 68 54
      thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp
  58. 5 0
      thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.h
  59. 7 4
      thirdparty/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h
  60. 32 29
      thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp
  61. 13 0
      thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.cpp
  62. 2 0
      thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.h
  63. 3 10
      thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp
  64. 0 471
      thirdparty/thorvg/src/loaders/tvg/tvgTvgBinInterpreter.cpp
  65. 0 54
      thirdparty/thorvg/src/loaders/tvg/tvgTvgCommon.h
  66. 0 233
      thirdparty/thorvg/src/loaders/tvg/tvgTvgLoader.cpp
  67. 0 61
      thirdparty/thorvg/src/loaders/tvg/tvgTvgLoader.h
  68. 0 792
      thirdparty/thorvg/src/savers/tvg/tvgTvgSaver.cpp
  69. 0 78
      thirdparty/thorvg/src/savers/tvg/tvgTvgSaver.h
  70. 37 21
      thirdparty/thorvg/update-thorvg.sh

+ 3 - 10
modules/svg/SCsub

@@ -38,9 +38,6 @@ thirdparty_sources = [
     "src/lib/tvgShape.cpp",
     "src/lib/tvgSwCanvas.cpp",
     "src/lib/tvgTaskScheduler.cpp",
-    "src/loaders/external_png/tvgPngLoader.cpp",
-    "src/loaders/jpg/tvgJpgd.cpp",
-    "src/loaders/jpg/tvgJpgLoader.cpp",
     "src/loaders/raw/tvgRawLoader.cpp",
     "src/loaders/svg/tvgSvgCssStyle.cpp",
     "src/loaders/svg/tvgSvgLoader.cpp",
@@ -48,27 +45,23 @@ thirdparty_sources = [
     "src/loaders/svg/tvgSvgSceneBuilder.cpp",
     "src/loaders/svg/tvgSvgUtil.cpp",
     "src/loaders/svg/tvgXmlParser.cpp",
-    "src/loaders/tvg/tvgTvgBinInterpreter.cpp",
-    "src/loaders/tvg/tvgTvgLoader.cpp",
-    "src/savers/tvg/tvgTvgSaver.cpp",
 ]
 
 thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
 
 env_svg.Prepend(CPPPATH=[thirdparty_dir + "inc"])
 
+# Enable ThorVG static object linking.
+env_svg.Append(CPPDEFINES=["TVG_STATIC"])
+
 env_thirdparty = env_svg.Clone()
 env_thirdparty.disable_warnings()
 env_thirdparty.Prepend(
     CPPPATH=[
         thirdparty_dir + "src/lib",
         thirdparty_dir + "src/lib/sw_engine",
-        thirdparty_dir + "src/loaders/external_png",
-        thirdparty_dir + "src/loaders/jpg",
         thirdparty_dir + "src/loaders/raw",
         thirdparty_dir + "src/loaders/svg",
-        thirdparty_dir + "src/loaders/tvg",
-        thirdparty_dir + "src/savers/tvg",
     ]
 )
 # Also requires libpng headers

+ 1 - 1
modules/svg/image_loader_svg.cpp

@@ -107,7 +107,7 @@ Error ImageLoaderSVG::create_image_from_utf8_buffer(Ref<Image> p_image, const ui
 	// Note: memalloc here, be sure to memfree before any return.
 	uint32_t *buffer = (uint32_t *)memalloc(sizeof(uint32_t) * width * height);
 
-	tvg::Result res = sw_canvas->target(buffer, width, width, height, tvg::SwCanvas::ARGB8888_STRAIGHT);
+	tvg::Result res = sw_canvas->target(buffer, width, width, height, tvg::SwCanvas::ARGB8888S);
 	if (res != tvg::Result::Success) {
 		memfree(buffer);
 		ERR_FAIL_V_MSG(FAILED, "ImageLoaderSVG: Couldn't set target on ThorVG canvas.");

+ 1 - 1
modules/text_server_adv/thorvg_svg_in_ot.cpp

@@ -256,7 +256,7 @@ FT_Error tvg_svg_in_ot_render(FT_GlyphSlot p_slot, FT_Pointer *p_state) {
 	}
 
 	std::unique_ptr<tvg::SwCanvas> sw_canvas = tvg::SwCanvas::gen();
-	res = sw_canvas->target((uint32_t *)p_slot->bitmap.buffer, (int)p_slot->bitmap.width, (int)p_slot->bitmap.width, (int)p_slot->bitmap.rows, tvg::SwCanvas::ARGB8888_STRAIGHT);
+	res = sw_canvas->target((uint32_t *)p_slot->bitmap.buffer, (int)p_slot->bitmap.width, (int)p_slot->bitmap.width, (int)p_slot->bitmap.rows, tvg::SwCanvas::ARGB8888S);
 	if (res != tvg::Result::Success) {
 		ERR_FAIL_V_MSG(FT_Err_Invalid_Outline, "Failed to create SVG canvas.");
 	}

+ 1 - 1
modules/text_server_fb/thorvg_svg_in_ot.cpp

@@ -256,7 +256,7 @@ FT_Error tvg_svg_in_ot_render(FT_GlyphSlot p_slot, FT_Pointer *p_state) {
 	}
 
 	std::unique_ptr<tvg::SwCanvas> sw_canvas = tvg::SwCanvas::gen();
-	res = sw_canvas->target((uint32_t *)p_slot->bitmap.buffer, (int)p_slot->bitmap.width, (int)p_slot->bitmap.width, (int)p_slot->bitmap.rows, tvg::SwCanvas::ARGB8888_STRAIGHT);
+	res = sw_canvas->target((uint32_t *)p_slot->bitmap.buffer, (int)p_slot->bitmap.width, (int)p_slot->bitmap.width, (int)p_slot->bitmap.rows, tvg::SwCanvas::ARGB8888S);
 	if (res != tvg::Result::Success) {
 		ERR_FAIL_V_MSG(FT_Err_Invalid_Outline, "Failed to create SVG canvas.");
 	}

+ 1 - 1
thirdparty/README.md

@@ -713,7 +713,7 @@ instead of `miniz.h` as an external dependency.
 ## thorvg
 
 - Upstream: https://github.com/thorvg/thorvg
-- Version: 0.9.0 (a744006aa1edb918bacf0a415d0a57ca058e25f4, 2023)
+- Version: 0.10.0 (b8c605583fd7de73209a93a1238e1ba72cce2e8f, 2023)
 - License: MIT
 
 Files extracted from upstream source:

+ 1 - 0
thirdparty/thorvg/AUTHORS

@@ -20,3 +20,4 @@ Vincenzo Pupillo <[email protected]>
 EunSik Jeong <[email protected]>
 Samsung Electronics Co., Ltd
 Rafał Mikrut <[email protected]>
+Martin Capitanio <[email protected]>

+ 3 - 11
thirdparty/thorvg/inc/config.h

@@ -1,17 +1,9 @@
 #ifndef THORVG_CONFIG_H
 #define THORVG_CONFIG_H
 
-#define THORVG_SW_RASTER_SUPPORT 1
+#define THORVG_SW_RASTER_SUPPORT
 
-#define THORVG_SVG_LOADER_SUPPORT 1
+#define THORVG_SVG_LOADER_SUPPORT
 
-#define THORVG_PNG_LOADER_SUPPORT 1
-
-#define THORVG_TVG_LOADER_SUPPORT 1
-
-#define THORVG_TVG_SAVER_SUPPORT 1
-
-#define THORVG_JPG_LOADER_SUPPORT 1
-
-#define THORVG_VERSION_STRING "0.9.0"
+#define THORVG_VERSION_STRING "0.10.0"
 #endif

+ 291 - 73
thirdparty/thorvg/inc/thorvg.h

@@ -18,43 +18,48 @@
 #include <functional>
 #include <memory>
 #include <string>
+#include <list>
 
 #ifdef TVG_API
     #undef TVG_API
 #endif
 
-#if defined(_WIN32) && !defined(__clang__)
-    #if TVG_BUILD
-        #if TVG_EXPORT
+#ifndef TVG_STATIC
+    #ifdef _WIN32
+        #if TVG_BUILD
             #define TVG_API __declspec(dllexport)
         #else
-            #define TVG_API
+            #define TVG_API __declspec(dllimport)
         #endif
+    #elif (defined(__SUNPRO_C)  || defined(__SUNPRO_CC))
+        #define TVG_API __global
     #else
-        #define TVG_API
-    #endif
-    #define TVG_DEPRECATED __declspec(deprecated)
-#else
-    #if TVG_BUILD
-        #if TVG_EXPORT
-            #define TVG_API __attribute__ ((visibility ("default")))
+        #if (defined(__GNUC__) && __GNUC__ >= 4) || defined(__INTEL_COMPILER)
+            #define TVG_API __attribute__ ((visibility("default")))
         #else
             #define TVG_API
         #endif
-    #else
-        #define TVG_API
     #endif
-    #define TVG_DEPRECATED __attribute__ ((__deprecated__))
+#else
+    #define TVG_API
+#endif
+
+#ifdef TVG_DEPRECATED
+    #undef TVG_DEPRECATED
 #endif
 
-#ifdef __cplusplus
-extern "C" {
+#ifdef _WIN32
+    #define TVG_DEPRECATED __declspec(deprecated)
+#elif __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)
+    #define TVG_DEPRECATED __attribute__ ((__deprecated__))
+#else
+    #define TVG_DEPRECATED
 #endif
 
 #define _TVG_DECLARE_PRIVATE(A) \
-protected: \
     struct Impl; \
     Impl* pImpl; \
+protected: \
     A(const A&) = delete; \
     const A& operator=(const A&) = delete; \
     A()
@@ -63,23 +68,14 @@ protected: \
     A() = delete; \
     ~A() = delete
 
-#define _TVG_DECLARE_ACCESSOR() \
-    friend Canvas; \
-    friend Scene; \
-    friend Picture; \
-    friend Accessor; \
-    friend IteratorAccessor
-
+#define _TVG_DECLARE_ACCESSOR(A) \
+    friend A
 
 namespace tvg
 {
 
 class RenderMethod;
-class IteratorAccessor;
-class Scene;
-class Picture;
-class Canvas;
-class Accessor;
+class Animation;
 
 /**
  * @defgroup ThorVG ThorVG
@@ -102,6 +98,7 @@ enum class Result
     Unknown                ///< The value returned in all other cases.
 };
 
+
 /**
  * @brief Enumeration specifying the values of the path commands accepted by TVG.
  *
@@ -116,6 +113,7 @@ enum class PathCommand
     CubicTo    ///< Draws a cubic Bezier curve from the current point to the given point using two given control points and sets a new value of the current point. This command expects 3 points: the 1st control-point, the 2nd control-point, the end-point of the curve.
 };
 
+
 /**
  * @brief Enumeration determining the ending type of a stroke in the open sub-paths.
  */
@@ -126,6 +124,7 @@ enum class StrokeCap
     Butt        ///< The stroke ends exactly at each of the two end-points of a sub-path. For zero length sub-paths no stroke is rendered.
 };
 
+
 /**
  * @brief Enumeration determining the style used at the corners of joined stroked path segments.
  */
@@ -136,6 +135,7 @@ enum class StrokeJoin
     Miter      ///< The outer corner of the joined path segments is spiked. The spike is created by extension beyond the join point of the outer edges of the stroke until they intersect. In case the extension goes beyond the limit, the join style is converted to the Bevel style.
 };
 
+
 /**
  * @brief Enumeration specifying how to fill the area outside the gradient bounds.
  */
@@ -146,6 +146,7 @@ enum class FillSpread
     Repeat   ///< The gradient pattern is repeated continuously beyond the gradient area until the expected region is filled.
 };
 
+
 /**
  * @brief Enumeration specifying the algorithm used to establish which parts of the shape are treated as the inside of the shape.
  */
@@ -155,18 +156,57 @@ enum class FillRule
     EvenOdd      ///< A line from the point to a location outside the shape is drawn and its intersections with the path segments of the shape are counted. If the number of intersections is an odd number, the point is inside the shape.
 };
 
+
 /**
  * @brief Enumeration indicating the method used in the composition of two objects - the target and the source.
+ *
+ * Notation: S(Source), T(Target), SA(Source Alpha), TA(Target Alpha)
+ *
+ * @see Paint::composite()
  */
 enum class CompositeMethod
 {
-    None = 0,     ///< No composition is applied.
-    ClipPath,     ///< The intersection of the source and the target is determined and only the resulting pixels from the source are rendered.
-    AlphaMask,    ///< The pixels of the source and the target are alpha blended. As a result, only the part of the source, which alpha intersects with the target is visible.
-    InvAlphaMask, ///< The pixels of the source and the complement to the target's pixels are alpha blended. As a result, only the part of the source which alpha is not covered by the target is visible.
-    LumaMask      ///< The source pixels are converted to the grayscale (luma value) and alpha blended with the target. As a result, only the part of the source, which intersects with the target is visible. @since 0.9
+    None = 0,           ///< No composition is applied.
+    ClipPath,           ///< The intersection of the source and the target is determined and only the resulting pixels from the source are rendered.
+    AlphaMask,          ///< Alpha Masking using the compositing target's pixels as an alpha value.
+    InvAlphaMask,       ///< Alpha Masking using the complement to the compositing target's pixels as an alpha value.
+    LumaMask,           ///< Alpha Masking using the grayscale (0.2125R + 0.7154G + 0.0721*B) of the compositing target's pixels. @since 0.9
+    InvLumaMask,        ///< Alpha Masking using the grayscale (0.2125R + 0.7154G + 0.0721*B) of the complement to the compositing target's pixels. @BETA_API
+    AddMask,            ///< Combines the target and source objects pixels using target alpha. (T * TA) + (S * (255 - TA)) @BETA_API
+    SubtractMask,       ///< Subtracts the source color from the target color while considering their respective target alpha. (T * TA) - (S * (255 - TA)) @BETA_API
+    IntersectMask,      ///< Computes the result by taking the minimum value between the target alpha and the source alpha and multiplies it with the target color. (T * min(TA, SA)) @BETA_API
+    DifferenceMask      ///< Calculates the absolute difference between the target color and the source color multiplied by the complement of the target alpha. abs(T - S * (255 - TA)) @BETA_API
 };
 
+
+/**
+ * @brief Enumeration indicates the method used for blending paint. Please refer to the respective formulas for each method.
+ *
+ * Notation: S(source paint as the top layer), D(destination as the bottom layer), Sa(source paint alpha), Da(destination alpha)
+ *
+ * @see Paint::blend()
+ *
+ * @BETA_API
+ */
+enum class BlendMethod : uint8_t
+{
+    Normal = 0,        ///< Perform the alpha blending(default). S if (Sa == 255), otherwise (Sa * S) + (255 - Sa) * D
+    Add,               ///< Simply adds pixel values of one layer with the other. (S + D)
+    Screen,            ///< The values of the pixels in the two layers are inverted, multiplied, and then inverted again. (S + D) - (S * D)
+    Multiply,          ///< Takes the RGB channel values from 0 to 255 of each pixel in the top layer and multiples them with the values for the corresponding pixel from the bottom layer. (S * D)
+    Overlay,           ///< Combines Multiply and Screen blend modes. (2 * S * D) if (2 * D < Da), otherwise (Sa * Da) - 2 * (Da - S) * (Sa - D)
+    Difference,        ///< Subtracts the bottom layer from the top layer or the other way around, to always get a non-negative value. (S - D) if (S > D), otherwise (D - S)
+    Exclusion,         ///< The result is twice the product of the top and bottom layers, subtracted from their sum. s + d - (2 * s * d)
+    SrcOver,           ///< Replace the bottom layer with the top layer.
+    Darken,            ///< Creates a pixel that retains the smallest components of the top and bottom layer pixels. min(S, D)
+    Lighten,           ///< Only has the opposite action of Darken Only. max(S, D)
+    ColorDodge,        ///< Divides the bottom layer by the inverted top layer. D / (255 - S)
+    ColorBurn,         ///< Divides the inverted bottom layer by the top layer, and then inverts the result. 255 - (255 - D) / S
+    HardLight,         ///< The same as Overlay but with the color roles reversed. (2 * S * D) if (S < Sa), otherwise (Sa * Da) - 2 * (Da - S) * (Sa - D)
+    SoftLight          ///< The same as Overlay but with applying pure black or white does not result in pure black or white. (1 - 2 * S) * (D ^ 2) + (2 * S * D)
+};
+
+
 /**
  * @brief Enumeration specifying the engine type used for the graphics backend. For multiple backends bitwise operation is allowed.
  */
@@ -293,7 +333,7 @@ public:
      * The values of the matrix can be set by the transform() API, as well by the translate(),
      * scale() and rotate(). In case no transformation was applied, the identity matrix is returned.
      *
-     * @retval The augmented transformation matrix.
+     * @return The augmented transformation matrix.
      *
      * @since 0.4
      */
@@ -321,6 +361,21 @@ public:
      */
     Result composite(std::unique_ptr<Paint> target, CompositeMethod method) noexcept;
 
+    /**
+     * @brief Sets the blending method for the paint object.
+     *
+     * The blending feature allows you to combine colors to create visually appealing effects, including transparency, lighting, shading, and color mixing, among others.
+     * its process involves the combination of colors or images from the source paint object with the destination (the lower layer image) using blending operations.
+     * The blending operation is determined by the chosen @p BlendMethod, which specifies how the colors or images are combined.
+     *
+     * @param[in] method The blending method to be set.
+     *
+     * @return Result::Success when the blending method is successfully set.
+     *
+     * @BETA_API
+     */
+    Result blend(BlendMethod method) const noexcept;
+
     /**
      * @brief Gets the bounding box of the paint object before any transformation.
      *
@@ -333,6 +388,7 @@ public:
      *
      * @note The bounding box doesn't indicate the final rendered region. It's the smallest rectangle that encloses the object.
      * @see Paint::bounds(float* x, float* y, float* w, float* h, bool transformed);
+     * @deprecated Use bounds(float* x, float* y, float* w, float* h, bool transformed) instead
      */
     TVG_DEPRECATED Result bounds(float* x, float* y, float* w, float* h) const noexcept;
 
@@ -380,6 +436,15 @@ public:
      */
     CompositeMethod composite(const Paint** target) const noexcept;
 
+    /**
+     * @brief Gets the blending method of the object.
+     *
+     * @return The blending method
+     *
+     * @BETA_API
+     */
+    BlendMethod blend() const noexcept;
+
     /**
      * @brief Return the unique id value of the paint instance.
      *
@@ -389,7 +454,6 @@ public:
      */
     uint32_t identifier() const noexcept;
 
-    _TVG_DECLARE_ACCESSOR();
     _TVG_DECLARE_PRIVATE(Paint);
 };
 
@@ -525,14 +589,25 @@ public:
      *
      * @return Result::Success when succeed.
      */
-    Result reserve(uint32_t n) noexcept;
+    TVG_DEPRECATED Result reserve(uint32_t n) noexcept;
+
+    /**
+     * @brief Returns the list of the paints that currently held by the Canvas.
+     *
+     * This function provides the list of paint nodes, allowing users a direct opportunity to modify the scene tree.
+     *
+     * @warning  Please avoid accessing the paints during Canvas update/draw. You can access them after calling sync().
+     * @see Canvas::sync()
+     *
+     * @BETA_API
+     */
+    std::list<Paint*>& paints() noexcept;
 
     /**
      * @brief Passes drawing elements to the Canvas using Paint objects.
      *
      * Only pushed paints in the canvas will be drawing targets.
      * They are retained by the canvas until you call Canvas::clear().
-     * If you know the number of the pushed objects in advance, please call Canvas::reserve().
      *
      * @param[in] paint A Paint object to be drawn.
      *
@@ -541,7 +616,7 @@ public:
      * @retval Result::InsufficientCondition An internal error.
      *
      * @note The rendering order of the paints is the same as the order as they were pushed into the canvas. Consider sorting the paints before pushing them if you intend to use layering.
-     * @see Canvas::reserve()
+     * @see Canvas::paints()
      * @see Canvas::clear()
      */
     virtual Result push(std::unique_ptr<Paint> paint) noexcept;
@@ -555,6 +630,8 @@ public:
      * @return Result::Success when succeed, Result::InsufficientCondition otherwise.
      *
      * @warning If you don't free the paints they become dangled. They are supposed to be reused, otherwise you are responsible for their lives. Thus please use the @p free argument only when you know how it works, otherwise it's not recommended.
+     * @see Canvas::push()
+     * @see Canvas::paints()
      */
     virtual Result clear(bool free = true) noexcept;
 
@@ -829,7 +906,7 @@ public:
      *
      * @note For @p rx and @p ry greater than or equal to the half of @p w and the half of @p h, respectively, the shape become an ellipse.
      */
-    Result appendRect(float x, float y, float w, float h, float rx, float ry) noexcept;
+    Result appendRect(float x, float y, float w, float h, float rx = 0, float ry = 0) noexcept;
 
     /**
      * @brief Appends an ellipse to the path.
@@ -905,7 +982,7 @@ public:
      *
      * @return Result::Success when succeed, Result::FailedAllocation otherwise.
      */
-    Result stroke(uint8_t r, uint8_t g, uint8_t b, uint8_t a) noexcept;
+    Result stroke(uint8_t r, uint8_t g, uint8_t b, uint8_t a = 255) noexcept;
 
     /**
      * @brief Sets the gradient fill of the stroke for all of the figures from the path.
@@ -953,6 +1030,18 @@ public:
      */
     Result stroke(StrokeJoin join) noexcept;
 
+
+    /**
+     * @brief Sets the stroke miterlimit.
+     *
+     * @param[in] miterlimit The miterlimit imposes a limit on the extent of the stroke join, when the @c StrokeJoin::Miter join style is set. The default value is 4.
+     *
+     * @return Result::Success when succeed, Result::NonSupport unsupported value, Result::FailedAllocation otherwise.
+     * 
+     * @BETA_API
+     */
+    Result strokeMiterlimit(float miterlimit) noexcept;
+
     /**
      * @brief Sets the solid color for all of the figures from the path.
      *
@@ -968,7 +1057,7 @@ public:
      * @note Either a solid color or a gradient fill is applied, depending on what was set as last.
      * @note ClipPath won't use the fill values. (see: enum class CompositeMethod::ClipPath)
      */
-    Result fill(uint8_t r, uint8_t g, uint8_t b, uint8_t a) noexcept;
+    Result fill(uint8_t r, uint8_t g, uint8_t b, uint8_t a = 255) noexcept;
 
     /**
      * @brief Sets the gradient fill for all of the figures from the path.
@@ -999,7 +1088,8 @@ public:
      * @param[in] strokeFirst If @c true the stroke is rendered before the fill, otherwise the stroke is rendered as the second one (the default option).
      *
      * @return Result::Success when succeed, Result::FailedAllocation otherwise.
-     * @BETA_API
+     *
+     * @since 0.10
      */
     Result order(bool strokeFirst) noexcept;
 
@@ -1039,7 +1129,7 @@ public:
      *
      * @return Result::Success when succeed.
      */
-    Result fillColor(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a) const noexcept;
+    Result fillColor(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a = nullptr) const noexcept;
 
     /**
      * @brief Gets the fill rule value.
@@ -1065,7 +1155,7 @@ public:
      *
      * @return Result::Success when succeed, Result::InsufficientCondition otherwise.
      */
-    Result strokeColor(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a) const noexcept;
+    Result strokeColor(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a = nullptr) const noexcept;
 
     /**
      * @brief Gets the pointer to the gradient fill of the stroke.
@@ -1097,6 +1187,15 @@ public:
      */
     StrokeJoin strokeJoin() const noexcept;
 
+    /**
+     * @brief Gets the stroke miterlimit.
+     *
+     * @return The stroke miterlimit value when succeed, 4 if no stroke was set.
+     *
+     * @BETA_API
+     */
+    float strokeMiterlimit() const noexcept;
+
     /**
      * @brief Creates a new Shape object.
      *
@@ -1120,10 +1219,11 @@ public:
 /**
  * @class Picture
  *
- * @brief A class representing an image read in one of the supported formats: raw, svg, png, jpg and etc.
+ * @brief A class representing an image read in one of the supported formats: raw, svg, png, jpg, lottie(json) and etc.
  * Besides the methods inherited from the Paint, it provides methods to load & draw images on the canvas.
  *
  * @note Supported formats are depended on the available TVG loaders.
+ * @note See Animation class if the picture data is animatable.
  */
 class TVG_API Picture final : public Paint
 {
@@ -1240,8 +1340,8 @@ public:
      * @param[in] triangles An array of Polygons(triangles) that make up the mesh, or null to remove the mesh.
      * @param[in] triangleCnt The number of Polygons(triangles) provided, or 0 to remove the mesh.
      *
-     * @return Result::Success When succeed.
-     * @return Result::Unknown If fails
+     * @retval Result::Success When succeed.
+     * @retval Result::Unknown If fails
      *
      * @note The Polygons are copied internally, so modifying them after calling Mesh::mesh has no affect.
      * @warning Please do not use it, this API is not official one. It could be modified in the next version.
@@ -1264,15 +1364,6 @@ public:
      */
     uint32_t mesh(const Polygon** triangles) const noexcept;
 
-    /**
-     * @brief Gets the position and the size of the loaded SVG picture.
-     *
-     * @warning Please do not use it, this API is not official one. It could be modified in the next version.
-     *
-     * @BETA_API
-     */
-    Result viewbox(float* x, float* y, float* w, float* h) const noexcept;
-
     /**
      * @brief Creates a new Picture object.
      *
@@ -1289,6 +1380,7 @@ public:
      */
     static uint32_t identifier() noexcept;
 
+    _TVG_DECLARE_ACCESSOR(Animation);
     _TVG_DECLARE_PRIVATE(Picture);
 };
 
@@ -1314,14 +1406,14 @@ public:
      *
      * Only the paints pushed into the scene will be the drawn targets.
      * The paints are retained by the scene until Scene::clear() is called.
-     * If you know the number of the pushed objects in advance, please call Scene::reserve().
      *
      * @param[in] paint A Paint object to be drawn.
      *
      * @return Result::Success when succeed, Result::MemoryCorruption otherwise.
      *
      * @note The rendering order of the paints is the same as the order as they were pushed. Consider sorting the paints before pushing them if you intend to use layering.
-     * @see Scene::reserve()
+     * @see Scene::paints()
+     * @see Scene::clear()
      */
     Result push(std::unique_ptr<Paint> paint) noexcept;
 
@@ -1335,7 +1427,21 @@ public:
      *
      * @return Result::Success when succeed, Result::FailedAllocation otherwise.
      */
-    Result reserve(uint32_t size) noexcept;
+    TVG_DEPRECATED Result reserve(uint32_t size) noexcept;
+
+    /**
+     * @brief Returns the list of the paints that currently held by the Scene.
+     *
+     * This function provides the list of paint nodes, allowing users a direct opportunity to modify the scene tree.
+     *
+     * @warning  Please avoid accessing the paints during Scene update/draw. You can access them after calling Canvas::sync().
+     * @see Canvas::sync()
+     * @see Scene::push()
+     * @see Scene::clear()
+     *
+     * @BETA_API
+     */
+    std::list<Paint*>& paints() noexcept;
 
     /**
      * @brief Sets the total number of the paints pushed into the scene to be zero.
@@ -1386,10 +1492,10 @@ public:
      */
     enum Colorspace
     {
-        ABGR8888 = 0,      ///< The channels are joined in the order: alpha, blue, green, red. Colors are alpha-premultiplied.
-        ARGB8888,          ///< The channels are joined in the order: alpha, red, green, blue. Colors are alpha-premultiplied.
-        ABGR8888_STRAIGHT, ///< @BETA_API The channels are joined in the order: alpha, blue, green, red. Colors are un-alpha-premultiplied.
-        ARGB8888_STRAIGHT, ///< @BETA_API The channels are joined in the order: alpha, red, green, blue. Colors are un-alpha-premultiplied.
+        ABGR8888 = 0,      ///< The channels are joined in the order: alpha, blue, green, red. Colors are alpha-premultiplied. (a << 24 | b << 16 | g << 8 | r)
+        ARGB8888,          ///< The channels are joined in the order: alpha, red, green, blue. Colors are alpha-premultiplied. (a << 24 | r << 16 | g << 8 | b)
+        ABGR8888S,         ///< @BETA_API The channels are joined in the order: alpha, blue, green, red. Colors are un-alpha-premultiplied.
+        ARGB8888S,         ///< @BETA_API The channels are joined in the order: alpha, red, green, blue. Colors are un-alpha-premultiplied.
     };
 
     /**
@@ -1544,6 +1650,101 @@ public:
 };
 
 
+/**
+ * @class Animation
+ *
+ * @brief The Animation class enables manipulation of animatable images.
+ *
+ * This class supports the display and control of animation frames.
+ *
+ * @BETA_API
+ */
+
+class TVG_API Animation
+{
+public:
+    ~Animation();
+
+    /**
+     * @brief Specifies the current frame in the animation.
+     *
+     * @param[in] no The index of the animation frame to be displayed. The index should be less than the totalFrame().
+     *
+     * @retval Result::Success Successfully set the frame.
+     * @retval Result::InsufficientCondition No animatable data loaded from the Picture.
+     * @retval Result::NonSupport The Picture data does not support animations.
+     *
+     * @see totalFrame()
+     *
+     * @BETA_API
+     */
+    Result frame(uint32_t no) noexcept;
+
+    /**
+     * @brief Retrieves a picture instance associated with this animation instance.
+     *
+     * This function provides access to the picture instance that can be used to load animation formats, such as Lottie(json).
+     * After setting up the picture, it can be pushed to the designated canvas, enabling control over animation frames
+     * with this Animation instance.
+     *
+     * @return A picture instance that is tied to this animation.
+     *
+     * @warning The picture instance is owned by Animation. It should not be deleted manually.
+     *
+     * @BETA_API
+     */
+    Picture* picture() const noexcept;
+
+    /**
+     * @brief Retrieves the current frame number of the animation.
+     *
+     * @return The current frame number of the animation, between 0 and totalFrame() - 1.
+     *
+     * @note If the Picture is not properly configured, this function will return 0.
+     *
+     * @see Animation::frame(uint32_t no)
+     * @see Animation::totalFrame()
+     *
+     * @BETA_API
+     */
+    uint32_t curFrame() const noexcept;
+
+    /**
+     * @brief Retrieves the total number of frames in the animation.
+     *
+     * @return The total number of frames in the animation.
+     *
+     * @note Frame numbering starts from 0.
+     * @note If the Picture is not properly configured, this function will return 0.
+     *
+     * @BETA_API
+     */
+    uint32_t totalFrame() const noexcept;
+
+    /**
+     * @brief Retrieves the duration of the animation in seconds.
+     *
+     * @return The duration of the animation in seconds.
+     *
+     * @note If the Picture is not properly configured, this function will return 0.
+     *
+     * @BETA_API
+     */
+    float duration() const noexcept;
+
+    /**
+     * @brief Creates a new Animation object.
+     *
+     * @return A new Animation object.
+     *
+     * @BETA_API
+     */
+    static std::unique_ptr<Animation> gen() noexcept;
+
+    _TVG_DECLARE_PRIVATE(Animation);
+};
+
+
 /**
  * @class Saver
  *
@@ -1629,7 +1830,7 @@ public:
  *
  * @warning We strongly warn you not to change the paints of a scene unless you really know the design-structure.
  *
- * @BETA_API
+ * @since 0.10
  */
 class TVG_API Accessor final
 {
@@ -1645,8 +1846,6 @@ public:
      * @return Return the given @p picture instance.
      *
      * @note The bitmap based picture might not have the scene-tree.
-     *
-     * @BETA_API
      */
     std::unique_ptr<Picture> set(std::unique_ptr<Picture> picture, std::function<bool(const Paint* paint)> func) noexcept;
 
@@ -1654,20 +1853,39 @@ public:
      * @brief Creates a new Accessor object.
      *
      * @return A new Accessor object.
-     *
-     * @BETA_API
      */
     static std::unique_ptr<Accessor> gen() noexcept;
 
     _TVG_DECLARE_PRIVATE(Accessor);
 };
 
-/** @}*/
 
-} //namespace
+/**
+ * @brief The cast() function is a utility function used to cast a 'Paint' to type 'T'.
+ *
+ * @BETA_API
+ */
+template<typename T>
+std::unique_ptr<T> cast(Paint* paint)
+{
+    return std::unique_ptr<T>(static_cast<T*>(paint));
+}
 
-#ifdef __cplusplus
+
+/**
+ * @brief The cast() function is a utility function used to cast a 'Fill' to type 'T'.
+ *
+ * @BETA_API
+ */
+template<typename T>
+std::unique_ptr<T> cast(Fill* fill)
+{
+    return std::unique_ptr<T>(static_cast<T*>(fill));
 }
-#endif
+
+
+/** @}*/
+
+} //namespace
 
 #endif //_THORVG_H_

+ 245 - 24
thirdparty/thorvg/src/lib/sw_engine/tvgSwCommon.h

@@ -99,15 +99,11 @@ struct SwSize
 
 struct SwOutline
 {
-    SwPoint*      pts;              //the outline's points
-    uint32_t      ptsCnt;           //number of points in the glyph
-    uint32_t      reservedPtsCnt;
-    uint32_t*     cntrs;            //the contour end points
-    uint16_t      cntrsCnt;         //number of contours in glyph
-    uint16_t      reservedCntrsCnt;
-    uint8_t*      types;            //curve type
-    bool*         closed;           //opened or closed path?
-    FillRule      fillRule;
+    Array<SwPoint> pts;             //the outline's points
+    Array<uint32_t> cntrs;          //the contour end points
+    Array<uint8_t> types;           //curve type
+    Array<bool> closed;             //opened or closed path?
+    FillRule fillRule;
 };
 
 struct SwSpan
@@ -180,6 +176,7 @@ struct SwStroke
     SwPoint ptStartSubPath;
     SwFixed subPathLineLength;
     SwFixed width;
+    SwFixed miterlimit;
 
     StrokeCap cap;
     StrokeJoin join;
@@ -238,18 +235,25 @@ struct SwImage
     bool         scaled = false;  //draw scaled image
 };
 
-struct SwBlender
-{
-    uint32_t (*join)(uint8_t r, uint8_t g, uint8_t b, uint8_t a);
-    uint8_t (*luma)(uint8_t* c);
-};
+typedef uint32_t(*SwBlender)(uint32_t s, uint32_t d, uint8_t a);            //src, dst, alpha
+typedef uint32_t(*SwJoin)(uint8_t r, uint8_t g, uint8_t b, uint8_t a);      //color channel join
+typedef uint8_t(*SwAlpha)(uint8_t*);                                        //blending alpha
 
 struct SwCompositor;
 
 struct SwSurface : Surface
 {
-    SwBlender blender;                    //mandatory
+    SwJoin  join;
+    SwAlpha alphas[4];                    //Alpha:2, InvAlpha:3, Luma:4, InvLuma:5
+    SwBlender blender = nullptr;          //blender (optional)
     SwCompositor* compositor = nullptr;   //compositor (optional)
+    BlendMethod          blendMethod;     //blending method (uint8_t)
+
+    SwAlpha alpha(CompositeMethod method)
+    {
+        auto idx = (int)(method) - 2;       //0: None, 1: ClipPath
+        return alphas[idx > 3 ? 0 : idx];   //CompositeMethod has only four Matting methods.
+    }
 };
 
 struct SwCompositor : Compositor
@@ -273,15 +277,25 @@ static inline SwCoord TO_SWCOORD(float val)
     return SwCoord(val * 64.0f);
 }
 
+static inline uint32_t JOIN(uint8_t c0, uint8_t c1, uint8_t c2, uint8_t c3)
+{
+    return (c0 << 24 | c1 << 16 | c2 << 8 | c3);
+}
+
 static inline uint32_t ALPHA_BLEND(uint32_t c, uint32_t a)
 {
     return (((((c >> 8) & 0x00ff00ff) * a + 0x00ff00ff) & 0xff00ff00) +
             ((((c & 0x00ff00ff) * a + 0x00ff00ff) >> 8) & 0x00ff00ff));
 }
 
-static inline uint32_t INTERPOLATE(uint32_t a, uint32_t c0, uint32_t c1)
+static inline uint32_t INTERPOLATE(uint32_t s, uint32_t d, uint8_t a)
+{
+    return (((((((s >> 8) & 0xff00ff) - ((d >> 8) & 0xff00ff)) * a) + (d & 0xff00ff00)) & 0xff00ff00) + ((((((s & 0xff00ff) - (d & 0xff00ff)) * a) >> 8) + (d & 0xff00ff)) & 0xff00ff));
+}
+
+static inline uint8_t INTERPOLATE8(uint8_t s, uint8_t d, uint8_t a)
 {
-    return (((((((c0 >> 8) & 0xff00ff) - ((c1 >> 8) & 0xff00ff)) * a) + (c1 & 0xff00ff00)) & 0xff00ff00) + ((((((c0 & 0xff00ff) - (c1 & 0xff00ff)) * a) >> 8) + (c1 & 0xff00ff)) & 0xff00ff));
+    return ((s * a + 0xff) >> 8) + ((d * ~a + 0xff) >> 8);
 }
 
 static inline SwCoord HALF_STROKE(float width)
@@ -289,6 +303,207 @@ static inline SwCoord HALF_STROKE(float width)
     return TO_SWCOORD(width * 0.5f);
 }
 
+static inline uint8_t A(uint32_t c)
+{
+    return ((c) >> 24);
+}
+
+static inline uint8_t IA(uint32_t c)
+{
+    return (~(c) >> 24);
+}
+
+static inline uint8_t C1(uint32_t c)
+{
+    return ((c) >> 16);
+}
+
+static inline uint8_t C2(uint32_t c)
+{
+    return ((c) >> 8);
+}
+
+static inline uint8_t C3(uint32_t c)
+{
+    return (c);
+}
+
+static inline uint32_t opBlendInterp(uint32_t s, uint32_t d, uint8_t a)
+{
+    return INTERPOLATE(s, d, a);
+}
+
+static inline uint32_t opBlendNormal(uint32_t s, uint32_t d, uint8_t a)
+{
+    auto t = ALPHA_BLEND(s, a);
+    return t + ALPHA_BLEND(d, IA(t));
+}
+
+static inline uint32_t opBlendPreNormal(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a)
+{
+    return s + ALPHA_BLEND(d, IA(s));
+}
+
+static inline uint32_t opBlendSrcOver(uint32_t s, TVG_UNUSED uint32_t d, TVG_UNUSED uint8_t a)
+{
+    return s;
+}
+
+//TODO: BlendMethod could remove the alpha parameter.
+static inline uint32_t opBlendDifference(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a)
+{
+    //if (s > d) => s - d
+    //else => d - s
+    auto c1 = (C1(s) > C1(d)) ? (C1(s) - C1(d)) : (C1(d) - C1(s));
+    auto c2 = (C2(s) > C2(d)) ? (C2(s) - C2(d)) : (C2(d) - C2(s));
+    auto c3 = (C3(s) > C3(d)) ? (C3(s) - C3(d)) : (C3(d) - C3(s));
+    return JOIN(255, c1, c2, c3);
+}
+
+static inline uint32_t opBlendExclusion(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a)
+{
+    //A + B - 2AB
+    auto c1 = min(255, C1(s) + C1(d) - min(255, (C1(s) * C1(d)) << 1));
+    auto c2 = min(255, C2(s) + C2(d) - min(255, (C2(s) * C2(d)) << 1));
+    auto c3 = min(255, C3(s) + C3(d) - min(255, (C3(s) * C3(d)) << 1));
+    return JOIN(255, c1, c2, c3);
+}
+
+static inline uint32_t opBlendAdd(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a)
+{
+    // s + d
+    auto c1 = min(C1(s) + C1(d), 255);
+    auto c2 = min(C2(s) + C2(d), 255);
+    auto c3 = min(C3(s) + C3(d), 255);
+    return JOIN(255, c1, c2, c3);
+}
+
+static inline uint32_t opBlendScreen(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a)
+{
+    // s + d - s * d
+    auto c1 = C1(s) + C1(d) - MULTIPLY(C1(s), C1(d));
+    auto c2 = C2(s) + C2(d) - MULTIPLY(C2(s), C2(d));
+    auto c3 = C3(s) + C3(d) - MULTIPLY(C3(s), C3(d));
+    return JOIN(255, c1, c2, c3);
+}
+
+
+static inline uint32_t opBlendMultiply(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a)
+{
+    // s * d
+    auto c1 = MULTIPLY(C1(s), C1(d));
+    auto c2 = MULTIPLY(C2(s), C2(d));
+    auto c3 = MULTIPLY(C3(s), C3(d));
+    return JOIN(255, c1, c2, c3);
+}
+
+
+static inline uint32_t opBlendOverlay(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a)
+{
+    // if (2 * d < da) => 2 * s * d,
+    // else => 1 - 2 * (1 - s) * (1 - d)
+    auto c1 = (C1(d) < 128) ? min(255, 2 * MULTIPLY(C1(s), C1(d))) : (255 - min(255, 2 * MULTIPLY(255 - C1(s), 255 - C1(d))));
+    auto c2 = (C2(d) < 128) ? min(255, 2 * MULTIPLY(C2(s), C2(d))) : (255 - min(255, 2 * MULTIPLY(255 - C2(s), 255 - C2(d))));
+    auto c3 = (C3(d) < 128) ? min(255, 2 * MULTIPLY(C3(s), C3(d))) : (255 - min(255, 2 * MULTIPLY(255 - C3(s), 255 - C3(d))));
+    return JOIN(255, c1, c2, c3);
+}
+
+static inline uint32_t opBlendDarken(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a)
+{
+    // min(s, d)
+    auto c1 = min(C1(s), C1(d));
+    auto c2 = min(C2(s), C2(d));
+    auto c3 = min(C3(s), C3(d));
+    return JOIN(255, c1, c2, c3);
+}
+
+static inline uint32_t opBlendLighten(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a)
+{
+    // max(s, d)
+    auto c1 = max(C1(s), C1(d));
+    auto c2 = max(C2(s), C2(d));
+    auto c3 = max(C3(s), C3(d));
+    return JOIN(255, c1, c2, c3);
+}
+
+static inline uint32_t opBlendColorDodge(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a)
+{
+    // d / (1 - s)
+    auto is = 0xffffffff - s;
+    auto c1 = (C1(is) > 0) ? (C1(d) / C1(is)) : C1(d);
+    auto c2 = (C2(is) > 0) ? (C2(d) / C2(is)) : C2(d);
+    auto c3 = (C3(is) > 0) ? (C3(d) / C3(is)) : C3(d);
+    return JOIN(255, c1, c2, c3);
+}
+
+static inline uint32_t opBlendColorBurn(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a)
+{
+    // 1 - (1 - d) / s
+    auto id = 0xffffffff - d;
+    auto c1 = 255 - ((C1(s) > 0) ? (C1(id) / C1(s)) : C1(id));
+    auto c2 = 255 - ((C2(s) > 0) ? (C2(id) / C2(s)) : C2(id));
+    auto c3 = 255 - ((C3(s) > 0) ? (C3(id) / C3(s)) : C3(id));
+    return JOIN(255, c1, c2, c3);
+}
+
+static inline uint32_t opBlendHardLight(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a)
+{
+    auto c1 = (C1(s) < 128) ? min(255, 2 * MULTIPLY(C1(s), C1(d))) : (255 - min(255, 2 * MULTIPLY(255 - C1(s), 255 - C1(d))));
+    auto c2 = (C2(s) < 128) ? min(255, 2 * MULTIPLY(C2(s), C2(d))) : (255 - min(255, 2 * MULTIPLY(255 - C2(s), 255 - C2(d))));
+    auto c3 = (C3(s) < 128) ? min(255, 2 * MULTIPLY(C3(s), C3(d))) : (255 - min(255, 2 * MULTIPLY(255 - C3(s), 255 - C3(d))));
+    return JOIN(255, c1, c2, c3);
+}
+
+static inline uint32_t opBlendSoftLight(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a)
+{
+    //(255 - 2 * s) * (d * d) + (2 * s * b)
+    auto c1 = min(255, MULTIPLY(255 - min(255, 2 * C1(s)), MULTIPLY(C1(d), C1(d))) + 2 * MULTIPLY(C1(s), C1(d)));
+    auto c2 = min(255, MULTIPLY(255 - min(255, 2 * C2(s)), MULTIPLY(C2(d), C2(d))) + 2 * MULTIPLY(C2(s), C2(d)));
+    auto c3 = min(255, MULTIPLY(255 - min(255, 2 * C3(s)), MULTIPLY(C3(d), C3(d))) + 2 * MULTIPLY(C3(s), C3(d)));
+    return JOIN(255, c1, c2, c3);
+}
+
+static inline uint32_t opMaskAdd(uint32_t s, uint32_t d, uint8_t a)
+{
+    return opBlendNormal(s, d, a);
+}
+
+static inline uint32_t opMaskSubtract(uint32_t s, uint32_t d, uint8_t a)
+{
+    return ALPHA_BLEND(d, MULTIPLY(IA(s), a));
+}
+
+static inline uint32_t opMaskDifference(uint32_t s, uint32_t d, uint8_t a)
+{
+    auto t = ALPHA_BLEND(s, a);
+    return ALPHA_BLEND(t, IA(d)) + ALPHA_BLEND(d, IA(t));
+}
+
+static inline uint32_t opMaskIntersect(uint32_t s, uint32_t d, uint8_t a)
+{
+   return ALPHA_BLEND(d, MULTIPLY(IA(s), a));
+}
+
+static inline uint32_t opMaskPreAdd(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a)
+{
+    return opBlendPreNormal(s, d, a);
+}
+
+static inline uint32_t opMaskPreSubtract(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a)
+{
+    return ALPHA_BLEND(d, IA(s));
+}
+
+static inline uint32_t opMaskPreDifference(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a)
+{
+   return ALPHA_BLEND(s, IA(d)) + ALPHA_BLEND(d, IA(s));
+}
+
+static inline uint32_t opMaskPreIntersect(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a)
+{
+   return ALPHA_BLEND(d, MULTIPLY(a, IA(s)));
+}
+
 int64_t mathMultiply(int64_t a, int64_t b);
 int64_t mathDivide(int64_t a, int64_t b);
 int64_t mathMulDiv(int64_t a, int64_t b, int64_t c);
@@ -315,8 +530,8 @@ void shapeResetStroke(SwShape* shape, const RenderShape* rshape, const Matrix* t
 bool shapeGenStrokeRle(SwShape* shape, const RenderShape* rshape, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid);
 void shapeFree(SwShape* shape);
 void shapeDelStroke(SwShape* shape);
-bool shapeGenFillColors(SwShape* shape, const Fill* fill, const Matrix* transform, SwSurface* surface, uint32_t opacity, bool ctable);
-bool shapeGenStrokeFillColors(SwShape* shape, const Fill* fill, const Matrix* transform, SwSurface* surface, uint32_t opacity, bool ctable);
+bool shapeGenFillColors(SwShape* shape, const Fill* fill, const Matrix* transform, SwSurface* surface, uint8_t opacity, bool ctable);
+bool shapeGenStrokeFillColors(SwShape* shape, const Fill* fill, const Matrix* transform, SwSurface* surface, uint8_t opacity, bool ctable);
 void shapeResetFill(SwShape* shape);
 void shapeResetStrokeFill(SwShape* shape);
 void shapeDelFill(SwShape* shape);
@@ -333,11 +548,16 @@ void imageDelOutline(SwImage* image, SwMpool* mpool, uint32_t tid);
 void imageReset(SwImage* image);
 void imageFree(SwImage* image);
 
-bool fillGenColorTable(SwFill* fill, const Fill* fdata, const Matrix* transform, SwSurface* surface, uint32_t opacity, bool ctable);
+bool fillGenColorTable(SwFill* fill, const Fill* fdata, const Matrix* transform, SwSurface* surface, uint8_t opacity, bool ctable);
 void fillReset(SwFill* fill);
 void fillFree(SwFill* fill);
-void fillFetchLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len);
-void fillFetchRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len);
+//OPTIMIZE_ME: Skip the function pointer access
+void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, uint8_t a);                                         //blending ver.
+void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, SwBlender op2, uint8_t a);                          //blending + BlendingMethod(op2) ver.
+void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwAlpha alpha, uint8_t csize, uint8_t opacity);     //masking ver.
+void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, uint8_t a);                                         //blending ver.
+void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, SwBlender op2, uint8_t a);                          //blending + BlendingMethod(op2) ver.
+void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwAlpha alpha, uint8_t csize, uint8_t opacity);     //masking ver.
 
 SwRleData* rleRender(SwRleData* rle, const SwOutline* outline, const SwBBox& renderRegion, bool antiAlias);
 SwRleData* rleRender(const SwBBox* bbox);
@@ -358,11 +578,12 @@ void mpoolRetStrokeOutline(SwMpool* mpool, unsigned idx);
 bool rasterCompositor(SwSurface* surface);
 bool rasterGradientShape(SwSurface* surface, SwShape* shape, unsigned id);
 bool rasterShape(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint8_t b, uint8_t a);
-bool rasterImage(SwSurface* surface, SwImage* image, const RenderMesh* mesh, const Matrix* transform, const SwBBox& bbox, uint32_t opacity);
+bool rasterImage(SwSurface* surface, SwImage* image, const RenderMesh* mesh, const Matrix* transform, const SwBBox& bbox, uint8_t opacity);
 bool rasterStroke(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint8_t b, uint8_t a);
 bool rasterGradientStroke(SwSurface* surface, SwShape* shape, unsigned id);
 bool rasterClear(SwSurface* surface, uint32_t x, uint32_t y, uint32_t w, uint32_t h);
-void rasterRGBA32(uint32_t *dst, uint32_t val, uint32_t offset, int32_t len);
+void rasterPixel32(uint32_t *dst, uint32_t val, uint32_t offset, int32_t len);
+void rasterGrayscale8(uint8_t *dst, uint8_t val, uint32_t offset, int32_t len);
 void rasterUnpremultiply(Surface* surface);
 void rasterPremultiply(Surface* surface);
 bool rasterConvertCS(Surface* surface, ColorSpace to);

+ 223 - 17
thirdparty/thorvg/src/lib/sw_engine/tvgSwFill.cpp

@@ -33,7 +33,7 @@
 #define FIXPT_SIZE (1<<FIXPT_BITS)
 
 
-static bool _updateColorTable(SwFill* fill, const Fill* fdata, const SwSurface* surface, uint32_t opacity)
+static bool _updateColorTable(SwFill* fill, const Fill* fdata, const SwSurface* surface, uint8_t opacity)
 {
     if (!fill->ctable) {
         fill->ctable = static_cast<uint32_t*>(malloc(GRADIENT_STOP_SIZE * sizeof(uint32_t)));
@@ -46,13 +46,13 @@ static bool _updateColorTable(SwFill* fill, const Fill* fdata, const SwSurface*
 
     auto pColors = colors;
 
-    auto a = (pColors->a * opacity) / 255;
+    auto a = MULTIPLY(pColors->a, opacity);
     if (a < 255) fill->translucent = true;
 
     auto r = pColors->r;
     auto g = pColors->g;
     auto b = pColors->b;
-    auto rgba = surface->blender.join(r, g, b, a);
+    auto rgba = surface->join(r, g, b, a);
 
     auto inc = 1.0f / static_cast<float>(GRADIENT_STOP_SIZE);
     auto pos = 1.5f * inc;
@@ -70,17 +70,17 @@ static bool _updateColorTable(SwFill* fill, const Fill* fdata, const SwSurface*
         auto curr = colors + j;
         auto next = curr + 1;
         auto delta = 1.0f / (next->offset - curr->offset);
-        auto a2 = (next->a * opacity) / 255;
+        auto a2 = MULTIPLY(next->a, opacity);
         if (!fill->translucent && a2 < 255) fill->translucent = true;
 
-        auto rgba2 = surface->blender.join(next->r, next->g, next->b, a2);
+        auto rgba2 = surface->join(next->r, next->g, next->b, a2);
 
         while (pos < next->offset && i < GRADIENT_STOP_SIZE) {
             auto t = (pos - curr->offset) * delta;
             auto dist = static_cast<int32_t>(255 * t);
             auto dist2 = 255 - dist;
 
-            auto color = INTERPOLATE(dist2, rgba, rgba2);
+            auto color = INTERPOLATE(rgba, rgba2, dist2);
             fill->ctable[i] = ALPHA_BLEND((color | 0xff000000), (color >> 24));
 
             ++i;
@@ -233,7 +233,7 @@ static inline uint32_t _pixel(const SwFill* fill, float pos)
 /* External Class Implementation                                        */
 /************************************************************************/
 
-void fillFetchRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len)
+void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwAlpha alpha, uint8_t csize, uint8_t opacity)
 {
     auto rx = (x + 0.5f) * fill->radial.a11 + (y + 0.5f) * fill->radial.a12 + fill->radial.shiftX;
     auto ry = (x + 0.5f) * fill->radial.a21 + (y + 0.5f) * fill->radial.a22 + fill->radial.shiftY;
@@ -244,16 +244,146 @@ void fillFetchRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x,
     auto detFirstDerivative = 2.0f * (fill->radial.a11 * rx + fill->radial.a21 * ry) + 0.5f * detSecondDerivative;
     auto det = rx * rx + ry * ry;
 
-    for (uint32_t i = 0 ; i < len ; ++i) {
-        *dst = _pixel(fill, sqrtf(det));
-        ++dst;
+    if (opacity == 255) {
+        for (uint32_t i = 0 ; i < len ; ++i, ++dst, cmp += csize) {
+            *dst = opBlendNormal(_pixel(fill, sqrtf(det)), *dst, alpha(cmp));
+            det += detFirstDerivative;
+            detFirstDerivative += detSecondDerivative;
+        }
+    } else {
+        for (uint32_t i = 0 ; i < len ; ++i, ++dst, cmp += csize) {
+            *dst = opBlendNormal(_pixel(fill, sqrtf(det)), *dst, MULTIPLY(opacity, alpha(cmp)));
+            det += detFirstDerivative;
+            detFirstDerivative += detSecondDerivative;
+        }
+    }
+}
+
+
+void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, uint8_t a)
+{
+    auto rx = (x + 0.5f) * fill->radial.a11 + (y + 0.5f) * fill->radial.a12 + fill->radial.shiftX;
+    auto ry = (x + 0.5f) * fill->radial.a21 + (y + 0.5f) * fill->radial.a22 + fill->radial.shiftY;
+
+    // detSecondDerivative = d(detFirstDerivative)/dx = d( d(det)/dx )/dx
+    auto detSecondDerivative = fill->radial.detSecDeriv;
+    // detFirstDerivative = d(det)/dx
+    auto detFirstDerivative = 2.0f * (fill->radial.a11 * rx + fill->radial.a21 * ry) + 0.5f * detSecondDerivative;
+    auto det = rx * rx + ry * ry;
+
+    for (uint32_t i = 0 ; i < len ; ++i, ++dst) {
+        *dst = op(_pixel(fill, sqrtf(det)), *dst, a);
         det += detFirstDerivative;
         detFirstDerivative += detSecondDerivative;
     }
 }
 
 
-void fillFetchLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len)
+void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, SwBlender op2, uint8_t a)
+{
+    auto rx = (x + 0.5f) * fill->radial.a11 + (y + 0.5f) * fill->radial.a12 + fill->radial.shiftX;
+    auto ry = (x + 0.5f) * fill->radial.a21 + (y + 0.5f) * fill->radial.a22 + fill->radial.shiftY;
+
+    // detSecondDerivative = d(detFirstDerivative)/dx = d( d(det)/dx )/dx
+    auto detSecondDerivative = fill->radial.detSecDeriv;
+    // detFirstDerivative = d(det)/dx
+    auto detFirstDerivative = 2.0f * (fill->radial.a11 * rx + fill->radial.a21 * ry) + 0.5f * detSecondDerivative;
+    auto det = rx * rx + ry * ry;
+
+    if (a == 255) {
+        for (uint32_t i = 0 ; i < len ; ++i, ++dst) {
+            auto tmp = op(_pixel(fill, sqrtf(det)), *dst, 255);
+            *dst = op2(tmp, *dst, 255);
+            det += detFirstDerivative;
+            detFirstDerivative += detSecondDerivative;
+        }
+    } else {
+        for (uint32_t i = 0 ; i < len ; ++i, ++dst) {
+            auto tmp = op(_pixel(fill, sqrtf(det)), *dst, 255);
+            auto tmp2 = op2(tmp, *dst, 255);
+            *dst = INTERPOLATE(tmp2, *dst, a);
+            det += detFirstDerivative;
+            detFirstDerivative += detSecondDerivative;
+        }
+    }
+}
+
+
+void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwAlpha alpha, uint8_t csize, uint8_t opacity)
+{
+    //Rotation
+    float rx = x + 0.5f;
+    float ry = y + 0.5f;
+    float t = (fill->linear.dx * rx + fill->linear.dy * ry + fill->linear.offset) * (GRADIENT_STOP_SIZE - 1);
+    float inc = (fill->linear.dx) * (GRADIENT_STOP_SIZE - 1);
+
+    if (opacity == 255) {
+        if (mathZero(inc)) {
+            auto color = _fixedPixel(fill, static_cast<int32_t>(t * FIXPT_SIZE));
+            for (uint32_t i = 0; i < len; ++i, ++dst, cmp += csize) {
+                *dst = opBlendNormal(color, *dst, alpha(cmp));
+            }
+            return;
+        }
+
+        auto vMax = static_cast<float>(INT32_MAX >> (FIXPT_BITS + 1));
+        auto vMin = -vMax;
+        auto v = t + (inc * len);
+
+        //we can use fixed point math
+        if (v < vMax && v > vMin) {
+            auto t2 = static_cast<int32_t>(t * FIXPT_SIZE);
+            auto inc2 = static_cast<int32_t>(inc * FIXPT_SIZE);
+            for (uint32_t j = 0; j < len; ++j, ++dst, cmp += csize) {
+                *dst = opBlendNormal(_fixedPixel(fill, t2), *dst, alpha(cmp));
+                t2 += inc2;
+            }
+        //we have to fallback to float math
+        } else {
+            uint32_t counter = 0;
+            while (counter++ < len) {
+                *dst = opBlendNormal(_pixel(fill, t / GRADIENT_STOP_SIZE), *dst, alpha(cmp));
+                ++dst;
+                t += inc;
+                cmp += csize;
+            }
+        }
+    } else {
+        if (mathZero(inc)) {
+            auto color = _fixedPixel(fill, static_cast<int32_t>(t * FIXPT_SIZE));
+            for (uint32_t i = 0; i < len; ++i, ++dst, cmp += csize) {
+                *dst = opBlendNormal(color, *dst, MULTIPLY(alpha(cmp), opacity));
+            }
+            return;
+        }
+
+        auto vMax = static_cast<float>(INT32_MAX >> (FIXPT_BITS + 1));
+        auto vMin = -vMax;
+        auto v = t + (inc * len);
+
+        //we can use fixed point math
+        if (v < vMax && v > vMin) {
+            auto t2 = static_cast<int32_t>(t * FIXPT_SIZE);
+            auto inc2 = static_cast<int32_t>(inc * FIXPT_SIZE);
+            for (uint32_t j = 0; j < len; ++j, ++dst, cmp += csize) {
+                *dst = opBlendNormal(_fixedPixel(fill, t2), *dst, MULTIPLY(alpha(cmp), opacity));
+                t2 += inc2;
+            }
+        //we have to fallback to float math
+        } else {
+            uint32_t counter = 0;
+            while (counter++ < len) {
+                *dst = opBlendNormal(_pixel(fill, t / GRADIENT_STOP_SIZE), *dst, MULTIPLY(opacity, alpha(cmp)));
+                ++dst;
+                t += inc;
+                cmp += csize;
+            }
+        }
+    }
+}
+
+
+void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, uint8_t a)
 {
     //Rotation
     float rx = x + 0.5f;
@@ -263,7 +393,9 @@ void fillFetchLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x,
 
     if (mathZero(inc)) {
         auto color = _fixedPixel(fill, static_cast<int32_t>(t * FIXPT_SIZE));
-        rasterRGBA32(dst, color, 0, len);
+        for (uint32_t i = 0; i < len; ++i, ++dst) {
+            *dst = op(color, *dst, a);
+        }
         return;
     }
 
@@ -275,16 +407,15 @@ void fillFetchLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x,
     if (v < vMax && v > vMin) {
         auto t2 = static_cast<int32_t>(t * FIXPT_SIZE);
         auto inc2 = static_cast<int32_t>(inc * FIXPT_SIZE);
-        for (uint32_t j = 0; j < len; ++j) {
-            *dst = _fixedPixel(fill, t2);
-            ++dst;
+        for (uint32_t j = 0; j < len; ++j, ++dst) {
+            *dst = op(_fixedPixel(fill, t2), *dst, a);
             t2 += inc2;
         }
     //we have to fallback to float math
     } else {
         uint32_t counter = 0;
         while (counter++ < len) {
-            *dst = _pixel(fill, t / GRADIENT_STOP_SIZE);
+            *dst = op(_pixel(fill, t / GRADIENT_STOP_SIZE), *dst, a);
             ++dst;
             t += inc;
         }
@@ -292,7 +423,82 @@ void fillFetchLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x,
 }
 
 
-bool fillGenColorTable(SwFill* fill, const Fill* fdata, const Matrix* transform, SwSurface* surface, uint32_t opacity, bool ctable)
+void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, SwBlender op2, uint8_t a)
+{
+    //Rotation
+    float rx = x + 0.5f;
+    float ry = y + 0.5f;
+    float t = (fill->linear.dx * rx + fill->linear.dy * ry + fill->linear.offset) * (GRADIENT_STOP_SIZE - 1);
+    float inc = (fill->linear.dx) * (GRADIENT_STOP_SIZE - 1);
+
+    if (mathZero(inc)) {
+        auto color = _fixedPixel(fill, static_cast<int32_t>(t * FIXPT_SIZE));
+        if (a == 255) {
+            for (uint32_t i = 0; i < len; ++i, ++dst) {
+                auto tmp = op(color, *dst, a);
+                *dst = op2(tmp, *dst, 255);
+            }
+        } else {
+            for (uint32_t i = 0; i < len; ++i, ++dst) {
+                auto tmp = op(color, *dst, a);
+                auto tmp2 = op2(tmp, *dst, 255);
+                *dst = INTERPOLATE(tmp2, *dst, a);
+            }
+        }
+        return;
+    }
+
+    auto vMax = static_cast<float>(INT32_MAX >> (FIXPT_BITS + 1));
+    auto vMin = -vMax;
+    auto v = t + (inc * len);
+
+    if (a == 255) {
+        //we can use fixed point math
+        if (v < vMax && v > vMin) {
+            auto t2 = static_cast<int32_t>(t * FIXPT_SIZE);
+            auto inc2 = static_cast<int32_t>(inc * FIXPT_SIZE);
+            for (uint32_t j = 0; j < len; ++j, ++dst) {
+                auto tmp = op(_fixedPixel(fill, t2), *dst, 255);
+                *dst = op2(tmp, *dst, 255);
+                t2 += inc2;
+            }
+        //we have to fallback to float math
+        } else {
+            uint32_t counter = 0;
+            while (counter++ < len) {
+                auto tmp = op(_pixel(fill, t / GRADIENT_STOP_SIZE), *dst, 255);
+                *dst = op2(tmp, *dst, 255);
+                ++dst;
+                t += inc;
+            }
+        }
+    } else {
+        //we can use fixed point math
+        if (v < vMax && v > vMin) {
+            auto t2 = static_cast<int32_t>(t * FIXPT_SIZE);
+            auto inc2 = static_cast<int32_t>(inc * FIXPT_SIZE);
+            for (uint32_t j = 0; j < len; ++j, ++dst) {
+                auto tmp = op(_fixedPixel(fill, t2), *dst, 255);
+                auto tmp2 = op2(tmp, *dst, 255);
+                *dst = INTERPOLATE(tmp2, *dst, a);
+                t2 += inc2;
+            }
+        //we have to fallback to float math
+        } else {
+            uint32_t counter = 0;
+            while (counter++ < len) {
+                auto tmp = op(_pixel(fill, t / GRADIENT_STOP_SIZE), *dst, 255);
+                auto tmp2 = op2(tmp, *dst, 255);
+                *dst = INTERPOLATE(tmp2, *dst, a);
+                ++dst;
+                t += inc;
+            }
+        }
+    }
+}
+
+
+bool fillGenColorTable(SwFill* fill, const Fill* fdata, const Matrix* transform, SwSurface* surface, uint8_t opacity, bool ctable)
 {
     if (!fill) return false;
 

+ 10 - 21
thirdparty/thorvg/src/lib/sw_engine/tvgSwImage.cpp

@@ -39,18 +39,10 @@ static bool _genOutline(SwImage* image, const RenderMesh* mesh, const Matrix* tr
     image->outline = mpoolReqOutline(mpool, tid);
     auto outline = image->outline;
 
-     if (outline->reservedPtsCnt < 5) {
-        outline->reservedPtsCnt = 5;
-        outline->pts = static_cast<SwPoint*>(realloc(outline->pts, outline->reservedPtsCnt * sizeof(SwPoint)));
-        outline->types = static_cast<uint8_t*>(realloc(outline->types, outline->reservedPtsCnt * sizeof(uint8_t)));
-     }
-
-    if (outline->reservedCntrsCnt < 1) {
-        outline->reservedCntrsCnt = 1;
-        outline->cntrs = static_cast<uint32_t*>(realloc(outline->cntrs, outline->reservedCntrsCnt * sizeof(uint32_t)));
-        outline->closed = static_cast<bool*>(realloc(outline->closed, outline->reservedCntrsCnt * sizeof(bool)));
-        outline->closed[0] = true;
-    }
+    outline->pts.reserve(5);
+    outline->types.reserve(5);
+    outline->cntrs.reserve(1);
+    outline->closed.reserve(1);
 
     Point to[4];
     if (mesh->triangleCnt > 0) {
@@ -97,17 +89,14 @@ static bool _genOutline(SwImage* image, const RenderMesh* mesh, const Matrix* tr
     }
 
     for (int i = 0; i < 4; i++) {
-        outline->pts[outline->ptsCnt] = mathTransform(&to[i], transform);
-        outline->types[outline->ptsCnt] = SW_CURVE_TYPE_POINT;
-        ++outline->ptsCnt;
+        outline->pts.push(mathTransform(&to[i], transform));
+        outline->types.push(SW_CURVE_TYPE_POINT);
     }
 
-    outline->pts[outline->ptsCnt] = outline->pts[0];
-    outline->types[outline->ptsCnt] = SW_CURVE_TYPE_POINT;
-    ++outline->ptsCnt;
-
-    outline->cntrs[outline->cntrsCnt] = outline->ptsCnt - 1;
-    ++outline->cntrsCnt;
+    outline->pts.push(outline->pts.data[0]);
+    outline->types.push(SW_CURVE_TYPE_POINT);
+    outline->cntrs.push(outline->pts.count - 1);
+    outline->closed.push(true);
 
     image->outline = outline;
 

+ 3 - 5
thirdparty/thorvg/src/lib/sw_engine/tvgSwMath.cpp

@@ -465,9 +465,9 @@ bool mathUpdateOutlineBBox(const SwOutline* outline, const SwBBox& clipRegion, S
 {
     if (!outline) return false;
 
-    auto pt = outline->pts;
+    auto pt = outline->pts.data;
 
-    if (outline->ptsCnt == 0 || outline->cntrsCnt <= 0) {
+    if (outline->pts.empty() || outline->cntrs.empty()) {
         renderRegion.reset();
         return false;
     }
@@ -477,9 +477,7 @@ bool mathUpdateOutlineBBox(const SwOutline* outline, const SwBBox& clipRegion, S
     auto yMin = pt->y;
     auto yMax = pt->y;
 
-    ++pt;
-
-    for (uint32_t i = 1; i < outline->ptsCnt; ++i, ++pt) {
+    for (++pt; pt < outline->pts.end(); ++pt) {
         if (xMin > pt->x) xMin = pt->x;
         if (xMax < pt->x) xMax = pt->x;
         if (yMin > pt->y) yMin = pt->y;

+ 16 - 35
thirdparty/thorvg/src/lib/sw_engine/tvgSwMemPool.cpp

@@ -40,8 +40,10 @@ SwOutline* mpoolReqOutline(SwMpool* mpool, unsigned idx)
 
 void mpoolRetOutline(SwMpool* mpool, unsigned idx)
 {
-    mpool->outline[idx].cntrsCnt = 0;
-    mpool->outline[idx].ptsCnt = 0;
+    mpool->outline[idx].pts.clear();
+    mpool->outline[idx].cntrs.clear();
+    mpool->outline[idx].types.clear();
+    mpool->outline[idx].closed.clear();
 }
 
 
@@ -53,8 +55,10 @@ SwOutline* mpoolReqStrokeOutline(SwMpool* mpool, unsigned idx)
 
 void mpoolRetStrokeOutline(SwMpool* mpool, unsigned idx)
 {
-    mpool->strokeOutline[idx].cntrsCnt = 0;
-    mpool->strokeOutline[idx].ptsCnt = 0;
+    mpool->strokeOutline[idx].pts.clear();
+    mpool->strokeOutline[idx].cntrs.clear();
+    mpool->strokeOutline[idx].types.clear();
+    mpool->strokeOutline[idx].closed.clear();
 }
 
 
@@ -93,42 +97,19 @@ bool mpoolClear(SwMpool* mpool)
     SwOutline* p;
 
     for (unsigned i = 0; i < mpool->allocSize; ++i) {
-
         //Outline
         p = &mpool->outline[i];
-
-        free(p->cntrs);
-        p->cntrs = nullptr;
-
-        free(p->pts);
-        p->pts = nullptr;
-
-        free(p->types);
-        p->types = nullptr;
-
-        free(p->closed);
-        p->closed = nullptr;
-
-        p->cntrsCnt = p->reservedCntrsCnt = 0;
-        p->ptsCnt = p->reservedPtsCnt = 0;
+        p->pts.reset();
+        p->cntrs.reset();
+        p->types.reset();
+        p->closed.reset();
 
         //StrokeOutline
         p = &mpool->strokeOutline[i];
-
-        free(p->cntrs);
-        p->cntrs = nullptr;
-
-        free(p->pts);
-        p->pts = nullptr;
-
-        free(p->types);
-        p->types = nullptr;
-
-        free(p->closed);
-        p->closed = nullptr;
-
-        p->cntrsCnt = p->reservedCntrsCnt = 0;
-        p->ptsCnt = p->reservedPtsCnt = 0;
+        p->pts.reset();
+        p->cntrs.reset();
+        p->types.reset();
+        p->closed.reset();
     }
 
     return true;

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 567 - 258
thirdparty/thorvg/src/lib/sw_engine/tvgSwRaster.cpp


+ 5 - 5
thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterAvx.h

@@ -62,7 +62,7 @@ static inline __m128i ALPHA_BLEND(__m128i c, __m128i a)
 }
 
 
-static void avxRasterRGBA32(uint32_t *dst, uint32_t val, uint32_t offset, int32_t len)
+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
     uint32_t iterations = len / N_32BITS_IN_256REG;
@@ -89,12 +89,12 @@ static bool avxRasterTranslucentRect(SwSurface* surface, const SwBBox& region, u
         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);
 
-    auto ialpha = 255 - static_cast<uint8_t>(_alpha(color));
+    uint32_t ialpha = 255 - a;
 
     auto avxColor = _mm_set1_epi32(color);
     auto avxIalpha = _mm_set1_epi8(ialpha);
@@ -138,7 +138,7 @@ static bool avxRasterTranslucentRle(SwSurface* surface, const SwRleData* rle, ui
         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;
 
@@ -148,7 +148,7 @@ static bool avxRasterTranslucentRle(SwSurface* surface, const SwRleData* rle, ui
         if (span->coverage < 255) src = ALPHA_BLEND(color, span->coverage);
         else src = color;
 
-        auto ialpha = 255 - static_cast<uint8_t>(_alpha(src));
+	auto ialpha = IA(src);
 
         //1. fill the not aligned memory (for 128-bit registers a 16-bytes alignment is required)
         auto notAligned = ((uintptr_t)dst & 0xf) / 4;

+ 64 - 15
thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterC.h

@@ -21,9 +21,41 @@
  */
 
 template<typename PIXEL_T>
-static void inline cRasterPixels(PIXEL_T* dst, uint32_t val, uint32_t offset, int32_t len)
+static void inline cRasterPixels(PIXEL_T* dst, PIXEL_T val, uint32_t offset, int32_t len)
 {
     dst += offset;
+
+    //fix the misaligned memory
+    auto alignOffset = (long long) dst % 8;
+    if (alignOffset > 0) {
+        if (sizeof(PIXEL_T) == 4) alignOffset /= 4;
+        else if (sizeof(PIXEL_T) == 1) alignOffset = 8 - alignOffset;
+        while (alignOffset > 0 && len > 0) {
+            *dst++ = val;
+            --len;
+            --alignOffset;
+        }
+    }
+
+    //64bits faster clear
+    if ((sizeof(PIXEL_T) == 4)) {
+        auto val64 = (uint64_t(val) << 32) | uint64_t(val);
+        while (len > 1) {
+            *reinterpret_cast<uint64_t*>(dst) = val64;
+            len -= 2;
+            dst += 2;
+        }
+    } else if (sizeof(PIXEL_T) == 1) {
+        auto val32 = (uint32_t(val) << 24) | (uint32_t(val) << 16) | (uint32_t(val) << 8) | uint32_t(val);
+        auto val64 = (uint64_t(val32) << 32) | val32;
+        while (len > 7) {
+            *reinterpret_cast<uint64_t*>(dst) = val64;
+            len -= 8;
+            dst += 8;
+        }
+    }
+
+    //leftovers
     while (len--) *dst++ = val;
 }
 
@@ -34,14 +66,15 @@ static bool inline cRasterTranslucentRle(SwSurface* surface, const SwRleData* rl
 
     //32bit channels
     if (surface->channelSize == sizeof(uint32_t)) {
-        auto color = surface->blender.join(r, g, b, a);
+        auto color = surface->join(r, g, b, a);
         uint32_t src;
         for (uint32_t i = 0; i < rle->size; ++i, ++span) {
             auto dst = &surface->buf32[span->y * surface->stride + span->x];
             if (span->coverage < 255) src = ALPHA_BLEND(color, span->coverage);
             else src = color;
+            auto ialpha = IA(src);
             for (uint32_t x = 0; x < span->len; ++x, ++dst) {
-                *dst = src + ALPHA_BLEND(*dst, _ialpha(src));
+                *dst = src + ALPHA_BLEND(*dst, ialpha);
             }
         }
     //8bit grayscale
@@ -49,10 +82,11 @@ static bool inline cRasterTranslucentRle(SwSurface* surface, const SwRleData* rl
         uint8_t src;
         for (uint32_t i = 0; i < rle->size; ++i, ++span) {
             auto dst = &surface->buf8[span->y * surface->stride + span->x];
-            if (span->coverage < 255) src = _multiply<uint8_t>(span->coverage, a);
+            if (span->coverage < 255) src = MULTIPLY(span->coverage, a);
             else src = a;
+            auto ialpha = ~a;
             for (uint32_t x = 0; x < span->len; ++x, ++dst) {
-                *dst = src + _multiply<uint8_t>(*dst, ~src);
+                *dst = src + MULTIPLY(*dst, ialpha);
             }
         }
     }
@@ -67,9 +101,9 @@ static bool inline cRasterTranslucentRect(SwSurface* surface, const SwBBox& regi
 
     //32bits channels
     if (surface->channelSize == sizeof(uint32_t)) {
-        auto color = surface->blender.join(r, g, b, a);
+        auto color = surface->join(r, g, b, 255);
         auto buffer = surface->buf32 + (region.min.y * surface->stride) + region.min.x;
-        auto ialpha = _ialpha(color);
+        auto ialpha = 255 - a;
         for (uint32_t y = 0; y < h; ++y) {
             auto dst = &buffer[y * surface->stride];
             for (uint32_t x = 0; x < w; ++x, ++dst) {
@@ -79,10 +113,11 @@ static bool inline cRasterTranslucentRect(SwSurface* surface, const SwBBox& regi
     //8bit grayscale
     } else if (surface->channelSize == sizeof(uint8_t)) {
         auto buffer = surface->buf8 + (region.min.y * surface->stride) + region.min.x;
+        auto ialpha = ~a;
         for (uint32_t y = 0; y < h; ++y) {
             auto dst = &buffer[y * surface->stride];
             for (uint32_t x = 0; x < w; ++x, ++dst) {
-                *dst = a + _multiply<uint8_t>(*dst, ~a);
+                *dst = a + MULTIPLY(*dst, ialpha);
             }
         }
     }
@@ -94,13 +129,27 @@ static bool inline cRasterABGRtoARGB(Surface* surface)
 {
     TVGLOG("SW_ENGINE", "Convert ColorSpace ABGR - ARGB [Size: %d x %d]", surface->w, surface->h);
 
-    auto buffer = surface->buf32;
-    for (uint32_t y = 0; y < surface->h; ++y, buffer += surface->stride) {
-        auto dst = buffer;
-        for (uint32_t x = 0; x < surface->w; ++x, ++dst) {
-            auto c = *dst;
-            //flip Blue, Red channels
-            *dst = (c & 0xff000000) + ((c & 0x00ff0000) >> 16) + (c & 0x0000ff00) + ((c & 0x000000ff) << 16);
+    //64bits faster converting
+    if (surface->w % 2 == 0) {
+        auto buffer = reinterpret_cast<uint64_t*>(surface->buf32);
+        for (uint32_t y = 0; y < surface->h; ++y, buffer += surface->stride / 2) {
+            auto dst = buffer;
+            for (uint32_t x = 0; x < surface->w / 2; ++x, ++dst) {
+                auto c = *dst;
+                //flip Blue, Red channels
+                *dst = (c & 0xff000000ff000000) + ((c & 0x00ff000000ff0000) >> 16) + (c & 0x0000ff000000ff00) + ((c & 0x000000ff000000ff) << 16);
+            }
+        }
+    //default converting
+    } else {
+        auto buffer = surface->buf32;
+        for (uint32_t y = 0; y < surface->h; ++y, buffer += surface->stride) {
+            auto dst = buffer;
+            for (uint32_t x = 0; x < surface->w; ++x, ++dst) {
+                auto c = *dst;
+                //flip Blue, Red channels
+                *dst = (c & 0xff000000) + ((c & 0x00ff0000) >> 16) + (c & 0x0000ff00) + ((c & 0x000000ff) << 16);
+            }
         }
     }
     return true;

+ 3 - 3
thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterNeon.h

@@ -31,7 +31,7 @@ static inline uint8x8_t ALPHA_BLEND(uint8x8_t c, uint8x8_t a)
 }
 
 
-static void neonRasterRGBA32(uint32_t *dst, uint32_t val, uint32_t offset, int32_t len)
+static void neonRasterPixel32(uint32_t *dst, uint32_t val, uint32_t offset, int32_t len)
 {
     uint32_t iterations = len / 4;
     uint32_t neonFilled = iterations * 4;
@@ -67,7 +67,7 @@ static bool neonRasterTranslucentRle(SwSurface* surface, const SwRleData* rle, u
         else src = color;
 
         auto dst = &surface->buf32[span->y * surface->stride + span->x];
-        auto ialpha = 255 - _alpha(src);
+        auto ialpha = IALPHA(src);
 
         if ((((uint32_t) dst) & 0x7) != 0) {
             //fill not aligned byte
@@ -105,7 +105,7 @@ static bool neonRasterTranslucentRect(SwSurface* surface, const SwBBox& region,
     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);
-    auto ialpha = 255 - _alpha(color);
+    auto ialpha = 255 - a;
 
     auto vColor = vdup_n_u32(color);
     auto vIalpha = vdup_n_u8((uint8_t) ialpha);

+ 755 - 46
thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterTexmap.h

@@ -41,6 +41,7 @@ static inline void _swap(float& a, float& b, float& tmp)
     b = tmp;
 }
 
+
 //Careful! Shared resource, No support threading
 static float dudx, dvdx;
 static float dxdya, dxdyb, dudya, dvdya;
@@ -69,40 +70,744 @@ static bool _arrange(const SwImage* image, const SwBBox* region, int& yStart, in
 }
 
 
-static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, uint32_t opacity, uint8_t(*blender)(uint8_t*), AASpans* aaSpans)
+static void _rasterMaskedPolygonImageSegmentInt(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, AASpans* aaSpans, uint8_t opacity, uint8_t dirFlag)
+{
+    float _dudx = dudx, _dvdx = dvdx;
+    float _dxdya = dxdya, _dxdyb = dxdyb, _dudya = dudya, _dvdya = dvdya;
+    float _xa = xa, _xb = xb, _ua = ua, _va = va;
+    auto sbuf = image->buf32;
+    int32_t sw = static_cast<int32_t>(image->stride);
+    int32_t sh = image->h;
+    int32_t x1, x2, ar, ab, iru, irv, px, ay;
+    int32_t vv = 0, uu = 0;
+    int32_t minx = INT32_MAX, maxx = INT32_MIN;
+    float dx, u, v, iptr;
+    auto cbuffer = surface->compositor->image.buf32;
+    SwSpan* span = nullptr;         //used only when rle based.
+
+    if (!_arrange(image, region, yStart, yEnd)) return;
+
+    //Clear out of the Polygon vertical ranges
+    auto size = surface->compositor->bbox.max.x - surface->compositor->bbox.min.x;
+    if (dirFlag == 1) {     //left top case.
+        for(int y = surface->compositor->bbox.min.y; y < yStart; ++y) {
+            rasterPixel32(surface->compositor->image.buf32 + y * surface->compositor->image.stride, 0, surface->compositor->bbox.min.x, size);
+        }
+    }
+    if (dirFlag == 4) {     //right bottom case.
+        for(int y = yEnd; y < surface->compositor->bbox.max.y; ++y) {
+            rasterPixel32(surface->compositor->image.buf32 + y * surface->compositor->image.stride, 0, surface->compositor->bbox.min.x, size);
+        }
+    }
+
+    //Loop through all lines in the segment
+    uint32_t spanIdx = 0;
+
+    if (region) {
+        minx = region->min.x;
+        maxx = region->max.x;
+    } else {
+        span = image->rle->spans;
+        while (span->y < yStart) {
+            ++span;
+            ++spanIdx;
+        }
+    }
+
+    for (int32_t y = yStart; y < yEnd; ++y) {
+        auto cmp = &cbuffer[y * surface->compositor->image.stride];
+        x1 = (int32_t)_xa;
+        x2 = (int32_t)_xb;
+
+        if (!region) {
+            minx = INT32_MAX;
+            maxx = INT32_MIN;
+            //one single row, could be consisted of multiple spans.
+            while (span->y == y && spanIdx < image->rle->size) {
+                if (minx > span->x) minx = span->x;
+                if (maxx < span->x + span->len) maxx = span->x + span->len;
+                ++span;
+                ++spanIdx;
+            }
+        }
+
+        if (x1 < minx) x1 = minx;
+        if (x2 > maxx) x2 = maxx;
+
+        //Anti-Aliasing frames
+        //FIXME: this aa must be applied before masking op
+        ay = y - aaSpans->yStart;
+        if (aaSpans->lines[ay].x[0] > x1) aaSpans->lines[ay].x[0] = x1;
+        if (aaSpans->lines[ay].x[1] < x2) aaSpans->lines[ay].x[1] = x2;
+
+        //Range allowed
+        if ((x2 - x1) >= 1 && (x1 < maxx) && (x2 > minx)) {
+            for (int32_t x = surface->compositor->bbox.min.x; x < surface->compositor->bbox.max.x; ++x) {
+                //Range allowed
+                if (x >= x1 && x < x2) {
+                    //Perform subtexel pre-stepping on UV
+                    dx = 1 - (_xa - x1);
+                    u = _ua + dx * _dudx;
+                    v = _va + dx * _dvdx;
+                    if ((uint32_t)v >= image->h) {
+                        cmp[x] = 0;
+                    } else {
+                        if (opacity == 255) {
+                            uu = (int) u;
+                            if (uu >= sw) continue;
+                            vv = (int) v;
+                            if (vv >= sh) continue;
+
+                            ar = (int)(255 * (1 - modff(u, &iptr)));
+                            ab = (int)(255 * (1 - modff(v, &iptr)));
+                            iru = uu + 1;
+                            irv = vv + 1;
+
+                            px = *(sbuf + (vv * sw) + uu);
+
+                            /* horizontal interpolate */
+                            if (iru < sw) {
+                                /* right pixel */
+                                int px2 = *(sbuf + (vv * sw) + iru);
+                                px = INTERPOLATE(px, px2, ar);
+                            }
+                            /* vertical interpolate */
+                            if (irv < sh) {
+                                /* bottom pixel */
+                                int px2 = *(sbuf + (irv * sw) + uu);
+
+                                /* horizontal interpolate */
+                                if (iru < sw) {
+                                    /* bottom right pixel */
+                                    int px3 = *(sbuf + (irv * sw) + iru);
+                                    px2 = INTERPOLATE(px2, px3, ar);
+                                }
+                                px = INTERPOLATE(px, px2, ab);
+                            }
+                            cmp[x] = ALPHA_BLEND(cmp[x], A(px));
+
+                            //Step UV horizontally
+                            u += _dudx;
+                            v += _dvdx;
+                        } else {
+                            uu = (int) u;
+                            if (uu >= sw) continue;
+                            vv = (int) v;
+                            if (vv >= sh) continue;
+
+                            ar = (int)(255 * (1 - modff(u, &iptr)));
+                            ab = (int)(255 * (1 - modff(v, &iptr)));
+                            iru = uu + 1;
+                            irv = vv + 1;
+
+                            px = *(sbuf + (vv * sw) + uu);
+
+                            /* horizontal interpolate */
+                            if (iru < sw) {
+                                /* right pixel */
+                                int px2 = *(sbuf + (vv * sw) + iru);
+                                px = INTERPOLATE(px, px2, ar);
+                            }
+                            /* vertical interpolate */
+                            if (irv < sh) {
+                                /* bottom pixel */
+                                int px2 = *(sbuf + (irv * sw) + uu);
+
+                                /* horizontal interpolate */
+                                if (iru < sw) {
+                                    /* bottom right pixel */
+                                    int px3 = *(sbuf + (irv * sw) + iru);
+                                    px2 = INTERPOLATE(px2, px3, ar);
+                                }
+                                px = INTERPOLATE(px, px2, ab);
+                            }
+                            cmp[x] = ALPHA_BLEND(cmp[x], MULTIPLY(A(px), opacity));
+
+                            //Step UV horizontally
+                            u += _dudx;
+                            v += _dvdx;
+                        }
+                    }
+                } else {
+                    //Clear out of polygon horizontal range
+                    if (x < x1 && (dirFlag == 1 || dirFlag == 2)) cmp[x] = 0;
+                    else if (x >= x2 && (dirFlag == 3 || dirFlag == 4)) cmp[x] = 0;
+                }
+            }
+        }
+        //Step along both edges
+        _xa += _dxdya;
+        _xb += _dxdyb;
+        _ua += _dudya;
+        _va += _dvdya;
+    }
+    xa = _xa;
+    xb = _xb;
+    ua = _ua;
+    va = _va;
+}
+
+
+static void _rasterMaskedPolygonImageSegmentDup(SwSurface* surface, const SwImage* image, const SwBBox* region, SwBlender maskOp, SwBlender amaskOp, int yStart, int yEnd, AASpans* aaSpans, uint8_t opacity)
 {
-#define TEXMAP_TRANSLUCENT
-#define TEXMAP_MASKING
-      #include "tvgSwRasterTexmapInternal.h"
-#undef TEXMAP_MASKING
-#undef TEXMAP_TRANSLUCENT
+    float _dudx = dudx, _dvdx = dvdx;
+    float _dxdya = dxdya, _dxdyb = dxdyb, _dudya = dudya, _dvdya = dvdya;
+    float _xa = xa, _xb = xb, _ua = ua, _va = va;
+    auto sbuf = image->buf32;
+    int32_t sw = static_cast<int32_t>(image->stride);
+    int32_t sh = image->h;
+    int32_t x1, x2, x, y, ar, ab, iru, irv, px, ay;
+    int32_t vv = 0, uu = 0;
+    int32_t minx = INT32_MAX, maxx = INT32_MIN;
+    float dx, u, v, iptr;
+    SwSpan* span = nullptr;         //used only when rle based.
+
+    if (!_arrange(image, region, yStart, yEnd)) return;
+
+    //Loop through all lines in the segment
+    uint32_t spanIdx = 0;
+
+    if (region) {
+        minx = region->min.x;
+        maxx = region->max.x;
+    } else {
+        span = image->rle->spans;
+        while (span->y < yStart) {
+            ++span;
+            ++spanIdx;
+        }
+    }
+
+    y = yStart;
+
+    while (y < yEnd) {
+        x1 = (int32_t)_xa;
+        x2 = (int32_t)_xb;
+
+        if (!region) {
+            minx = INT32_MAX;
+            maxx = INT32_MIN;
+            //one single row, could be consisted of multiple spans.
+            while (span->y == y && spanIdx < image->rle->size) {
+                if (minx > span->x) minx = span->x;
+                if (maxx < span->x + span->len) maxx = span->x + span->len;
+                ++span;
+                ++spanIdx;
+            }
+        }
+        if (x1 < minx) x1 = minx;
+        if (x2 > maxx) x2 = maxx;
+
+        //Anti-Aliasing frames
+        ay = y - aaSpans->yStart;
+        if (aaSpans->lines[ay].x[0] > x1) aaSpans->lines[ay].x[0] = x1;
+        if (aaSpans->lines[ay].x[1] < x2) aaSpans->lines[ay].x[1] = x2;
+
+        //Range allowed
+        if ((x2 - x1) >= 1 && (x1 < maxx) && (x2 > minx)) {
+
+            //Perform subtexel pre-stepping on UV
+            dx = 1 - (_xa - x1);
+            u = _ua + dx * _dudx;
+            v = _va + dx * _dvdx;
+
+            x = x1;
+
+            auto cmp = &surface->compositor->image.buf32[y * surface->compositor->image.stride + x1];
+
+            if (opacity == 255) {
+                //Draw horizontal line
+                while (x++ < x2) {
+                    uu = (int) u;
+                    if (uu >= sw) continue;
+                    vv = (int) v;
+                    if (vv >= sh) continue;
+
+                    ar = (int)(255 * (1 - modff(u, &iptr)));
+                    ab = (int)(255 * (1 - modff(v, &iptr)));
+                    iru = uu + 1;
+                    irv = vv + 1;
+
+                    px = *(sbuf + (vv * sw) + uu);
+
+                    /* horizontal interpolate */
+                    if (iru < sw) {
+                        /* right pixel */
+                        int px2 = *(sbuf + (vv * sw) + iru);
+                        px = INTERPOLATE(px, px2, ar);
+                    }
+                    /* vertical interpolate */
+                    if (irv < sh) {
+                        /* bottom pixel */
+                        int px2 = *(sbuf + (irv * sw) + uu);
+
+                        /* horizontal interpolate */
+                        if (iru < sw) {
+                            /* bottom right pixel */
+                            int px3 = *(sbuf + (irv * sw) + iru);
+                            px2 = INTERPOLATE(px2, px3, ar);
+                        }
+                        px = INTERPOLATE(px, px2, ab);
+                    }
+                    *cmp = maskOp(px, *cmp, IA(px));
+                    ++cmp;
+
+                    //Step UV horizontally
+                    u += _dudx;
+                    v += _dvdx;
+                    //range over?
+                    if ((uint32_t)v >= image->h) break;
+                }
+            } else {
+                //Draw horizontal line
+                while (x++ < x2) {
+                    uu = (int) u;
+                    if (uu >= sw) continue;
+                    vv = (int) v;
+                    if (vv >= sh) continue;
+
+                    ar = (int)(255 * (1 - modff(u, &iptr)));
+                    ab = (int)(255 * (1 - modff(v, &iptr)));
+                    iru = uu + 1;
+                    irv = vv + 1;
+
+                    px = *(sbuf + (vv * sw) + uu);
+
+                    /* horizontal interpolate */
+                    if (iru < sw) {
+                        /* right pixel */
+                        int px2 = *(sbuf + (vv * sw) + iru);
+                        px = INTERPOLATE(px, px2, ar);
+                    }
+                    /* vertical interpolate */
+                    if (irv < sh) {
+                        /* bottom pixel */
+                        int px2 = *(sbuf + (irv * sw) + uu);
+
+                        /* horizontal interpolate */
+                        if (iru < sw) {
+                            /* bottom right pixel */
+                            int px3 = *(sbuf + (irv * sw) + iru);
+                            px2 = INTERPOLATE(px2, px3, ar);
+                        }
+                        px = INTERPOLATE(px, px2, ab);
+                    }
+                    *cmp = amaskOp(px, *cmp, opacity);
+                    ++cmp;
+
+                    //Step UV horizontally
+                    u += _dudx;
+                    v += _dvdx;
+                    //range over?
+                    if ((uint32_t)v >= image->h) break;
+                }
+            }
+        }
+
+        //Step along both edges
+        _xa += _dxdya;
+        _xb += _dxdyb;
+        _ua += _dudya;
+        _va += _dvdya;
+
+        if (!region && spanIdx >= image->rle->size) break;
+
+        ++y;
+    }
+    xa = _xa;
+    xb = _xb;
+    ua = _ua;
+    va = _va;
 }
 
 
-static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, uint8_t(*blender)(uint8_t*), AASpans* aaSpans)
+static void _rasterMaskedPolygonImageSegment(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, AASpans* aaSpans, uint8_t opacity, uint8_t dirFlag = 0)
 {
-#define TEXMAP_MASKING
-    #include "tvgSwRasterTexmapInternal.h"
-#undef TEXMAP_MASKING
+    if (surface->compositor->method == CompositeMethod::IntersectMask) {
+        _rasterMaskedPolygonImageSegmentInt(surface, image, region, yStart, yEnd, aaSpans, opacity, dirFlag);
+    } else if (auto opMask = _getMaskOp(surface->compositor->method)) {
+        //Other Masking operations: Add, Subtract, Difference ...
+        _rasterMaskedPolygonImageSegmentDup(surface, image, region, opMask, _getAMaskOp(surface->compositor->method), yStart, yEnd, aaSpans, opacity);
+    }
 }
 
 
-static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, uint32_t opacity, AASpans* aaSpans)
+static void _rasterBlendingPolygonImageSegment(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, AASpans* aaSpans, uint8_t opacity)
 {
-#define TEXMAP_TRANSLUCENT
-     #include "tvgSwRasterTexmapInternal.h"
-#undef TEXMAP_TRANSLUCENT
+    float _dudx = dudx, _dvdx = dvdx;
+    float _dxdya = dxdya, _dxdyb = dxdyb, _dudya = dudya, _dvdya = dvdya;
+    float _xa = xa, _xb = xb, _ua = ua, _va = va;
+    auto sbuf = image->buf32;
+    auto dbuf = surface->buf32;
+    int32_t sw = static_cast<int32_t>(image->stride);
+    int32_t sh = image->h;
+    int32_t dw = surface->stride;
+    int32_t x1, x2, x, y, ar, ab, iru, irv, px, ay;
+    int32_t vv = 0, uu = 0;
+    int32_t minx = INT32_MAX, maxx = INT32_MIN;
+    float dx, u, v, iptr;
+    uint32_t* buf;
+    SwSpan* span = nullptr;         //used only when rle based.
+
+    if (!_arrange(image, region, yStart, yEnd)) return;
+
+    //Loop through all lines in the segment
+    uint32_t spanIdx = 0;
+
+    if (region) {
+        minx = region->min.x;
+        maxx = region->max.x;
+    } else {
+        span = image->rle->spans;
+        while (span->y < yStart) {
+            ++span;
+            ++spanIdx;
+        }
+    }
+
+    y = yStart;
+
+    while (y < yEnd) {
+        x1 = (int32_t)_xa;
+        x2 = (int32_t)_xb;
+
+        if (!region) {
+            minx = INT32_MAX;
+            maxx = INT32_MIN;
+            //one single row, could be consisted of multiple spans.
+            while (span->y == y && spanIdx < image->rle->size) {
+                if (minx > span->x) minx = span->x;
+                if (maxx < span->x + span->len) maxx = span->x + span->len;
+                ++span;
+                ++spanIdx;
+            }
+        }
+        if (x1 < minx) x1 = minx;
+        if (x2 > maxx) x2 = maxx;
+
+        //Anti-Aliasing frames
+        ay = y - aaSpans->yStart;
+        if (aaSpans->lines[ay].x[0] > x1) aaSpans->lines[ay].x[0] = x1;
+        if (aaSpans->lines[ay].x[1] < x2) aaSpans->lines[ay].x[1] = x2;
+
+        //Range allowed
+        if ((x2 - x1) >= 1 && (x1 < maxx) && (x2 > minx)) {
+
+            //Perform subtexel pre-stepping on UV
+            dx = 1 - (_xa - x1);
+            u = _ua + dx * _dudx;
+            v = _va + dx * _dvdx;
+
+            buf = dbuf + ((y * dw) + x1);
+
+            x = x1;
+
+            if (opacity == 255) {
+                //Draw horizontal line
+                while (x++ < x2) {
+                    uu = (int) u;
+                    if (uu >= sw) continue;
+                    vv = (int) v;
+                    if (vv >= sh) continue;
+
+                    ar = (int)(255 * (1 - modff(u, &iptr)));
+                    ab = (int)(255 * (1 - modff(v, &iptr)));
+                    iru = uu + 1;
+                    irv = vv + 1;
+
+                    px = *(sbuf + (vv * sw) + uu);
+
+                    /* horizontal interpolate */
+                    if (iru < sw) {
+                        /* right pixel */
+                        int px2 = *(sbuf + (vv * sw) + iru);
+                        px = INTERPOLATE(px, px2, ar);
+                    }
+                    /* vertical interpolate */
+                    if (irv < sh) {
+                        /* bottom pixel */
+                        int px2 = *(sbuf + (irv * sw) + uu);
+
+                        /* horizontal interpolate */
+                        if (iru < sw) {
+                            /* bottom right pixel */
+                            int px3 = *(sbuf + (irv * sw) + iru);
+                            px2 = INTERPOLATE(px2, px3, ar);
+                        }
+                        px = INTERPOLATE(px, px2, ab);
+                    }
+                    *buf = surface->blender(px, *buf, IA(px));
+                    ++buf;
+
+                    //Step UV horizontally
+                    u += _dudx;
+                    v += _dvdx;
+                    //range over?
+                    if ((uint32_t)v >= image->h) break;
+                }
+            } else {
+                //Draw horizontal line
+                while (x++ < x2) {
+                    uu = (int) u;
+                    if (uu >= sw) continue;
+                    vv = (int) v;
+                    if (vv >= sh) continue;
+
+                    ar = (int)(255 * (1 - modff(u, &iptr)));
+                    ab = (int)(255 * (1 - modff(v, &iptr)));
+                    iru = uu + 1;
+                    irv = vv + 1;
+
+                    px = *(sbuf + (vv * sw) + uu);
+
+                    /* horizontal interpolate */
+                    if (iru < sw) {
+                        /* right pixel */
+                        int px2 = *(sbuf + (vv * sw) + iru);
+                        px = INTERPOLATE(px, px2, ar);
+                    }
+                    /* vertical interpolate */
+                    if (irv < sh) {
+                        /* bottom pixel */
+                        int px2 = *(sbuf + (irv * sw) + uu);
+
+                        /* horizontal interpolate */
+                        if (iru < sw) {
+                            /* bottom right pixel */
+                            int px3 = *(sbuf + (irv * sw) + iru);
+                            px2 = INTERPOLATE(px2, px3, ar);
+                        }
+                        px = INTERPOLATE(px, px2, ab);
+                    }
+                    auto src = ALPHA_BLEND(px, opacity);
+                    *buf = surface->blender(src, *buf, IA(src));
+                    ++buf;
+
+                    //Step UV horizontally
+                    u += _dudx;
+                    v += _dvdx;
+                    //range over?
+                    if ((uint32_t)v >= image->h) break;
+                }
+            }
+        }
+
+        //Step along both edges
+        _xa += _dxdya;
+        _xb += _dxdyb;
+        _ua += _dudya;
+        _va += _dvdya;
+
+        if (!region && spanIdx >= image->rle->size) break;
+
+        ++y;
+    }
+    xa = _xa;
+    xb = _xb;
+    ua = _ua;
+    va = _va;
 }
 
 
-static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, AASpans* aaSpans)
+static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, AASpans* aaSpans, uint8_t opacity, bool matting)
 {
-    #include "tvgSwRasterTexmapInternal.h"
+    float _dudx = dudx, _dvdx = dvdx;
+    float _dxdya = dxdya, _dxdyb = dxdyb, _dudya = dudya, _dvdya = dvdya;
+    float _xa = xa, _xb = xb, _ua = ua, _va = va;
+    auto sbuf = image->buf32;
+    auto dbuf = surface->buf32;
+    int32_t sw = static_cast<int32_t>(image->stride);
+    int32_t sh = image->h;
+    int32_t dw = surface->stride;
+    int32_t x1, x2, x, y, ar, ab, iru, irv, px, ay;
+    int32_t vv = 0, uu = 0;
+    int32_t minx = INT32_MAX, maxx = INT32_MIN;
+    float dx, u, v, iptr;
+    uint32_t* buf;
+    SwSpan* span = nullptr;         //used only when rle based.
+
+    //for matting(composition)
+    auto csize = matting ? surface->compositor->image.channelSize: 0;
+    auto alpha = matting ? surface->alpha(surface->compositor->method) : nullptr;
+    uint8_t* cmp = nullptr;
+
+    if (!_arrange(image, region, yStart, yEnd)) return;
+
+    //Loop through all lines in the segment
+    uint32_t spanIdx = 0;
+
+    if (region) {
+        minx = region->min.x;
+        maxx = region->max.x;
+    } else {
+        span = image->rle->spans;
+        while (span->y < yStart) {
+            ++span;
+            ++spanIdx;
+        }
+    }
+
+    y = yStart;
+
+    while (y < yEnd) {
+        x1 = (int32_t)_xa;
+        x2 = (int32_t)_xb;
+
+        if (!region) {
+            minx = INT32_MAX;
+            maxx = INT32_MIN;
+            //one single row, could be consisted of multiple spans.
+            while (span->y == y && spanIdx < image->rle->size) {
+                if (minx > span->x) minx = span->x;
+                if (maxx < span->x + span->len) maxx = span->x + span->len;
+                ++span;
+                ++spanIdx;
+            }
+        }
+        if (x1 < minx) x1 = minx;
+        if (x2 > maxx) x2 = maxx;
+
+        //Anti-Aliasing frames
+        ay = y - aaSpans->yStart;
+        if (aaSpans->lines[ay].x[0] > x1) aaSpans->lines[ay].x[0] = x1;
+        if (aaSpans->lines[ay].x[1] < x2) aaSpans->lines[ay].x[1] = x2;
+
+        //Range allowed
+        if ((x2 - x1) >= 1 && (x1 < maxx) && (x2 > minx)) {
+
+            //Perform subtexel pre-stepping on UV
+            dx = 1 - (_xa - x1);
+            u = _ua + dx * _dudx;
+            v = _va + dx * _dvdx;
+
+            buf = dbuf + ((y * dw) + x1);
+
+            x = x1;
+
+            if (matting) cmp = &surface->compositor->image.buf8[(y * surface->compositor->image.stride + x1) * csize];
+
+            if (opacity == 255) {
+                //Draw horizontal line
+                while (x++ < x2) {
+                    uu = (int) u;
+                    if (uu >= sw) continue;
+                    vv = (int) v;
+                    if (vv >= sh) continue;
+
+                    ar = (int)(255 * (1 - modff(u, &iptr)));
+                    ab = (int)(255 * (1 - modff(v, &iptr)));
+                    iru = uu + 1;
+                    irv = vv + 1;
+
+                    px = *(sbuf + (vv * sw) + uu);
+
+                    /* horizontal interpolate */
+                    if (iru < sw) {
+                        /* right pixel */
+                        int px2 = *(sbuf + (vv * sw) + iru);
+                        px = INTERPOLATE(px, px2, ar);
+                    }
+                    /* vertical interpolate */
+                    if (irv < sh) {
+                        /* bottom pixel */
+                        int px2 = *(sbuf + (irv * sw) + uu);
+
+                        /* horizontal interpolate */
+                        if (iru < sw) {
+                            /* bottom right pixel */
+                            int px3 = *(sbuf + (irv * sw) + iru);
+                            px2 = INTERPOLATE(px2, px3, ar);
+                        }
+                        px = INTERPOLATE(px, px2, ab);
+                    }
+                    uint32_t src;
+                    if (matting) {
+                        src = ALPHA_BLEND(px, alpha(cmp));
+                        cmp += csize;
+                    } else {
+                        src = px;
+                    }
+                    *buf = src + ALPHA_BLEND(*buf, IA(src));
+                    ++buf;
+
+                    //Step UV horizontally
+                    u += _dudx;
+                    v += _dvdx;
+                    //range over?
+                    if ((uint32_t)v >= image->h) break;
+                }
+            } else {
+                //Draw horizontal line
+                while (x++ < x2) {
+                    uu = (int) u;
+                    vv = (int) v;
+
+                    ar = (int)(255 * (1 - modff(u, &iptr)));
+                    ab = (int)(255 * (1 - modff(v, &iptr)));
+                    iru = uu + 1;
+                    irv = vv + 1;
+
+                    if (vv >= sh) continue;
+
+                    px = *(sbuf + (vv * sw) + uu);
+
+                    /* horizontal interpolate */
+                    if (iru < sw) {
+                        /* right pixel */
+                        int px2 = *(sbuf + (vv * sw) + iru);
+                        px = INTERPOLATE(px, px2, ar);
+                    }
+                    /* vertical interpolate */
+                    if (irv < sh) {
+                        /* bottom pixel */
+                        int px2 = *(sbuf + (irv * sw) + uu);
+
+                        /* horizontal interpolate */
+                        if (iru < sw) {
+                            /* bottom right pixel */
+                            int px3 = *(sbuf + (irv * sw) + iru);
+                            px2 = INTERPOLATE(px2, px3, ar);
+                        }
+                        px = INTERPOLATE(px, px2, ab);
+                    }
+                    uint32_t src;
+                    if (matting) {
+                        src = ALPHA_BLEND(px, MULTIPLY(opacity, alpha(cmp)));
+                        cmp += csize;
+                    } else {
+                        src = ALPHA_BLEND(px, opacity);
+                    }
+                    *buf = src + ALPHA_BLEND(*buf, IA(src));
+                    ++buf;
+
+                    //Step UV horizontally
+                    u += _dudx;
+                    v += _dvdx;
+                    //range over?
+                    if ((uint32_t)v >= image->h) break;
+                }
+            }
+        }
+
+        //Step along both edges
+        _xa += _dxdya;
+        _xb += _dxdyb;
+        _ua += _dudya;
+        _va += _dvdya;
+
+        if (!region && spanIdx >= image->rle->size) break;
+
+        ++y;
+    }
+    xa = _xa;
+    xb = _xb;
+    ua = _ua;
+    va = _va;
 }
 
 
 /* This mapping algorithm is based on Mikael Kalms's. */
-static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const SwBBox* region, uint32_t opacity, Polygon& polygon, uint8_t(*blender)(uint8_t*), AASpans* aaSpans)
+static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const SwBBox* region, Polygon& polygon, AASpans* aaSpans, uint8_t opacity)
 {
     float x[3] = {polygon.vertex[0].pt.x, polygon.vertex[1].pt.x, polygon.vertex[2].pt.x};
     float y[3] = {polygon.vertex[0].pt.y, polygon.vertex[1].pt.y, polygon.vertex[2].pt.y};
@@ -165,6 +870,8 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const
     if (mathEqual(y[1], y[2])) side = x[2] > x[1];
 
     auto regionTop = region ? region->min.y : image->rle->spans->y;  //Normal Image or Rle Image?
+    auto compositing = _compositing(surface);   //Composition required
+    auto blending = _blending(surface);         //Blending required
 
     //Longer edge is on the left side
     if (!side) {
@@ -190,14 +897,14 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const
             dxdyb = dxdy[0];
             xb = x[0] + dy * dxdyb + (off_y * dxdyb);
 
-            if (blender) {
-                if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], blender, aaSpans);
-                else _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], opacity, blender, aaSpans);
+            if (compositing) {
+                if (_matting(surface)) _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity, true);
+                else _rasterMaskedPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity, 1);
+            } else if (blending) {
+                _rasterBlendingPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity);
             } else {
-                if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans);
-                else _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], opacity, aaSpans);
+                _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity, false);
             }
-
             upper = true;
         }
         //Draw lower segment if possibly visible
@@ -211,12 +918,13 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const
             // Set right edge X-slope and perform subpixel pre-stepping
             dxdyb = dxdy[2];
             xb = x[1] + (1 - (y[1] - yi[1])) * dxdyb + (off_y * dxdyb);
-            if (blender) {
-                if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], blender, aaSpans);
-                else _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], opacity, blender, aaSpans);
+            if (compositing) {
+                if (_matting(surface)) _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity, true);
+                else _rasterMaskedPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity, 2);
+            } else if (blending) {
+                 _rasterBlendingPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity);
             } else {
-                if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans);
-                else _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], opacity, aaSpans);
+                _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity, false);
             }
         }
     //Longer edge is on the right side
@@ -240,14 +948,14 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const
             ua = u[0] + dy * dudya + (off_y * dudya);
             va = v[0] + dy * dvdya + (off_y * dvdya);
 
-            if (blender) {
-                if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], blender, aaSpans);
-                else _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], opacity, blender, aaSpans);
+            if (compositing) {
+                if (_matting(surface)) _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity, true);
+                else _rasterMaskedPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity, 3);
+            } else if (blending) {
+                _rasterBlendingPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity);
             } else {
-                if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans);
-                else _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], opacity, aaSpans);
+                _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity, false);
             }
-
             upper = true;
         }
         //Draw lower segment if possibly visible
@@ -264,12 +972,13 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const
             ua = u[1] + dy * dudya + (off_y * dudya);
             va = v[1] + dy * dvdya + (off_y * dvdya);
 
-            if (blender) {
-                if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], blender, aaSpans);
-                else _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], opacity, blender, aaSpans);
+            if (compositing) {
+                if (_matting(surface)) _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity, true);
+                else _rasterMaskedPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity, 4);
+            } else if (blending) {
+                _rasterBlendingPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity);
             } else {
-                if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans);
-                else _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], opacity, aaSpans);
+                _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity, false);
             }
         }
     }
@@ -508,7 +1217,7 @@ static bool _apply(SwSurface* surface, AASpans* aaSpans)
 
             pos = 1;
             while (pos <= line->length[0]) {
-                *dst = INTERPOLATE((line->coverage[0] * pos), *dst, pixel);
+                *dst = INTERPOLATE(*dst, pixel, line->coverage[0] * pos);
                 ++dst;
                 ++pos;
             }
@@ -520,7 +1229,7 @@ static bool _apply(SwSurface* surface, AASpans* aaSpans)
 
             pos = width;
             while ((int32_t)(width - line->length[1]) < pos) {
-                *dst = INTERPOLATE(255 - (line->coverage[1] * (line->length[1] - (width - pos))), *dst, pixel);
+                *dst = INTERPOLATE(*dst, pixel, 255 - (line->coverage[1] * (line->length[1] - (width - pos))));
                 --dst;
                 --pos;
             }
@@ -545,7 +1254,7 @@ static bool _apply(SwSurface* surface, AASpans* aaSpans)
     | /  |
     3 -- 2
 */
-static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const Matrix* transform, const SwBBox* region, uint32_t opacity, uint8_t(*blender)(uint8_t*))
+static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const Matrix* transform, const SwBBox* region, uint8_t opacity)
 {
     //Exceptions: No dedicated drawing area?
     if ((!image->rle && !region) || (image->rle && image->rle->size == 0)) return false;
@@ -576,14 +1285,14 @@ static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const
     polygon.vertex[1] = vertices[1];
     polygon.vertex[2] = vertices[3];
 
-    _rasterPolygonImage(surface, image, region, opacity, polygon, blender, aaSpans);
+    _rasterPolygonImage(surface, image, region, polygon, aaSpans, opacity);
 
     //Draw the second polygon
     polygon.vertex[0] = vertices[1];
     polygon.vertex[1] = vertices[2];
     polygon.vertex[2] = vertices[3];
 
-    _rasterPolygonImage(surface, image, region, opacity, polygon, blender, aaSpans);
+    _rasterPolygonImage(surface, image, region, polygon, aaSpans, opacity);
 
     return _apply(surface, aaSpans);
 }
@@ -602,7 +1311,7 @@ static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const
       Should provide two Polygons, one for each triangle.
       // TODO: region?
 */
-static bool _rasterTexmapPolygonMesh(SwSurface* surface, const SwImage* image, const RenderMesh* mesh, const Matrix* transform, const SwBBox* region, uint32_t opacity, uint8_t(*blender)(uint8_t*))
+static bool _rasterTexmapPolygonMesh(SwSurface* surface, const SwImage* image, const RenderMesh* mesh, const Matrix* transform, const SwBBox* region, uint8_t opacity)
 {
     //Exceptions: No dedicated drawing area?
     if ((!image->rle && !region) || (image->rle && image->rle->size == 0)) return false;
@@ -636,7 +1345,7 @@ static bool _rasterTexmapPolygonMesh(SwSurface* surface, const SwImage* image, c
     auto aaSpans = _AASpans(ys, ye, image, region);
     if (aaSpans) {
         for (uint32_t i = 0; i < mesh->triangleCnt; i++) {
-            _rasterPolygonImage(surface, image, region, opacity, transformedTris[i], blender, aaSpans);
+            _rasterPolygonImage(surface, image, region, transformedTris[i], aaSpans, opacity);
         }
         // Apply to surface (note: frees the AA spans)
         _apply(surface, aaSpans);

+ 0 - 168
thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterTexmapInternal.h

@@ -1,168 +0,0 @@
-/*
- * Copyright (c) 2021 - 2023 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.
- */
-
-{
-    float _dudx = dudx, _dvdx = dvdx;
-    float _dxdya = dxdya, _dxdyb = dxdyb, _dudya = dudya, _dvdya = dvdya;
-    float _xa = xa, _xb = xb, _ua = ua, _va = va;
-    auto sbuf = image->buf32;
-    auto dbuf = surface->buf32;
-    int32_t sw = static_cast<int32_t>(image->stride);
-    int32_t sh = image->h;
-    int32_t dw = surface->stride;
-    int32_t x1, x2, x, y, ar, ab, iru, irv, px, ay;
-    int32_t vv = 0, uu = 0;
-    int32_t minx = INT32_MAX, maxx = INT32_MIN;
-    float dx, u, v, iptr;
-    uint32_t* buf;
-    SwSpan* span = nullptr;         //used only when rle based.
-
-#ifdef TEXMAP_MASKING
-    uint8_t* cmp;
-    auto csize = surface->compositor->image.channelSize;
-#endif
-
-    if (!_arrange(image, region, yStart, yEnd)) return;
-
-    //Loop through all lines in the segment
-    uint32_t spanIdx = 0;
-
-    if (region) {
-        minx = region->min.x;
-        maxx = region->max.x;
-    } else {
-        span = image->rle->spans;
-        while (span->y < yStart) {
-            ++span;
-            ++spanIdx;
-        }
-    }
-
-    y = yStart;
-
-    while (y < yEnd) {
-        x1 = (int32_t)_xa;
-        x2 = (int32_t)_xb;
-
-        if (!region) {
-            minx = INT32_MAX;
-            maxx = INT32_MIN;
-            //one single row, could be consisted of multiple spans.
-            while (span->y == y && spanIdx < image->rle->size) {
-                if (minx > span->x) minx = span->x;
-                if (maxx < span->x + span->len) maxx = span->x + span->len;
-                ++span;
-                ++spanIdx;
-            }
-        }
-        if (x1 < minx) x1 = minx;
-        if (x2 > maxx) x2 = maxx;
-
-        //Anti-Aliasing frames
-        ay = y - aaSpans->yStart;
-        if (aaSpans->lines[ay].x[0] > x1) aaSpans->lines[ay].x[0] = x1;
-        if (aaSpans->lines[ay].x[1] < x2) aaSpans->lines[ay].x[1] = x2;
-
-        //Range exception
-        if ((x2 - x1) < 1 || (x1 >= maxx) || (x2 <= minx)) goto next;
-
-        //Perform subtexel pre-stepping on UV
-        dx = 1 - (_xa - x1);
-        u = _ua + dx * _dudx;
-        v = _va + dx * _dvdx;
-
-        buf = dbuf + ((y * dw) + x1);
-
-        x = x1;
-
-#ifdef TEXMAP_MASKING
-        cmp = &surface->compositor->image.buf8[(y * surface->compositor->image.stride + x1) * csize];
-#endif
-        //Draw horizontal line
-        while (x++ < x2) {
-            uu = (int) u;
-            vv = (int) v;
-
-            ar = (int)(255 * (1 - modff(u, &iptr)));
-            ab = (int)(255 * (1 - modff(v, &iptr)));
-            iru = uu + 1;
-            irv = vv + 1;
-
-            if (vv >= sh) continue;
-
-            px = *(sbuf + (vv * sw) + uu);
-
-            /* horizontal interpolate */
-            if (iru < sw) {
-                /* right pixel */
-                int px2 = *(sbuf + (vv * sw) + iru);
-                px = INTERPOLATE(ar, px, px2);
-            }
-            /* vertical interpolate */
-            if (irv < sh) {
-                /* bottom pixel */
-                int px2 = *(sbuf + (irv * sw) + uu);
-
-                /* horizontal interpolate */
-                if (iru < sw) {
-                    /* bottom right pixel */
-                    int px3 = *(sbuf + (irv * sw) + iru);
-                    px2 = INTERPOLATE(ar, px2, px3);
-                }
-                px = INTERPOLATE(ab, px, px2);
-            }
-#if defined(TEXMAP_MASKING) && defined(TEXMAP_TRANSLUCENT)
-            auto src = ALPHA_BLEND(px, _multiply<uint32_t>(opacity, blender(cmp)));
-#elif defined(TEXMAP_MASKING)
-            auto src = ALPHA_BLEND(px, blender(cmp));
-#elif defined(TEXMAP_TRANSLUCENT)
-            auto src = ALPHA_BLEND(px, opacity);
-#else
-            auto src = px;
-#endif
-            *buf = src + ALPHA_BLEND(*buf, _ialpha(src));
-            ++buf;
-#ifdef TEXMAP_MASKING
-            cmp += csize;
-#endif
-            //Step UV horizontally
-            u += _dudx;
-            v += _dvdx;
-            //range over?
-            if ((uint32_t)v >= image->h) break;
-        }
-next:
-        //Step along both edges
-        _xa += _dxdya;
-        _xb += _dxdyb;
-        _ua += _dudya;
-        _va += _dvdya;
-
-        if (!region && spanIdx >= image->rle->size) break;
-
-        ++y;
-    }
-    xa = _xa;
-    xb = _xb;
-    ua = _ua;
-    va = _va;
-}

+ 89 - 54
thirdparty/thorvg/src/lib/sw_engine/tvgSwRenderer.cpp

@@ -35,13 +35,13 @@ static uint32_t threadsCnt = 0;
 
 struct SwTask : Task
 {
-    Matrix* transform = nullptr;
     SwSurface* surface = nullptr;
     SwMpool* mpool = nullptr;
-    RenderUpdateFlag flags = RenderUpdateFlag::None;
-    Array<RenderData> clips;
-    uint32_t opacity;
     SwBBox bbox = {{0, 0}, {0, 0}};       //Whole Rendering Region
+    Matrix* transform = nullptr;
+    Array<RenderData> clips;
+    RenderUpdateFlag flags = RenderUpdateFlag::None;
+    uint8_t opacity;
     bool pushed = false;                  //Pushed into task list?
     bool disposed = false;                //Disposed task?
 
@@ -106,7 +106,7 @@ struct SwShapeTask : SwTask
 
         if (HALF_STROKE(rshape->strokeWidth()) > 0) {
             rshape->strokeColor(nullptr, nullptr, nullptr, &strokeAlpha);
-            visibleStroke = rshape->strokeFill() || (static_cast<uint32_t>(strokeAlpha * opacity / 255) > 0);
+            visibleStroke = rshape->strokeFill() || (MULTIPLY(strokeAlpha, opacity) > 0);
         }
 
         //This checks also for the case, if the invisible shape turned to visible by alpha.
@@ -117,7 +117,7 @@ struct SwShapeTask : SwTask
         if (flags & (RenderUpdateFlag::Path | RenderUpdateFlag::Transform) || prepareShape) {
             uint8_t alpha = 0;
             rshape->fillColor(nullptr, nullptr, nullptr, &alpha);
-            alpha = static_cast<uint8_t>(static_cast<uint32_t>(alpha) * opacity / 255);
+            alpha = MULTIPLY(alpha, opacity);
             visibleFill = (alpha > 0 || rshape->fill);
             if (visibleFill || visibleStroke || clipper) {
                 shapeReset(&shape);
@@ -125,10 +125,6 @@ struct SwShapeTask : SwTask
             }
         }
 
-        //Decide Stroking Composition
-        if (visibleStroke && visibleFill && opacity < 255) cmpStroking = true;
-        else cmpStroking = false;
-
         //Fill
         if (flags & (RenderUpdateFlag::Gradient | RenderUpdateFlag::Transform | RenderUpdateFlag::Color)) {
             if (visibleFill || clipper) {
@@ -143,7 +139,7 @@ struct SwShapeTask : SwTask
             if (auto fill = rshape->fill) {
                 auto ctable = (flags & RenderUpdateFlag::Gradient) ? true : false;
                 if (ctable) shapeResetFill(&shape);
-                if (!shapeGenFillColors(&shape, fill, transform, surface, cmpStroking ? 255 : opacity, ctable)) goto err;
+                if (!shapeGenFillColors(&shape, fill, transform, surface, opacity, ctable)) goto err;
             } else {
                 shapeDelFill(&shape);
             }
@@ -158,7 +154,7 @@ struct SwShapeTask : SwTask
                 if (auto fill = rshape->strokeFill()) {
                     auto ctable = (flags & RenderUpdateFlag::GradientStroke) ? true : false;
                     if (ctable) shapeResetStrokeFill(&shape);
-                    if (!shapeGenStrokeFillColors(&shape, fill, transform, surface, cmpStroking ? 255 : opacity, ctable)) goto err;
+                    if (!shapeGenStrokeFillColors(&shape, fill, transform, surface, opacity, ctable)) goto err;
                 } else {
                     shapeDelStrokeFill(&shape);
                 }
@@ -171,7 +167,7 @@ struct SwShapeTask : SwTask
         shapeDelOutline(&shape, mpool, tid);
 
         //Clip Path
-        for (auto clip = clips.data; clip < (clips.data + clips.count); ++clip) {
+        for (auto clip = clips.data; clip < clips.end(); ++clip) {
             auto clipper = static_cast<SwTask*>(*clip);
             //Clip shape rle
             if (shape.rle && !clipper->clip(shape.rle)) goto err;
@@ -232,7 +228,7 @@ struct SwSceneTask : SwTask
             rleMerge(sceneRle, clipper1->rle(), clipper2->rle());
 
             //Unify the remained clippers
-            for (auto rd = scene.data + 2; rd < (scene.data + scene.count); ++rd) {
+            for (auto rd = scene.data + 2; rd < scene.end(); ++rd) {
                 auto clipper = static_cast<SwTask*>(*rd);
                 rleMerge(sceneRle, sceneRle, clipper->rle());
             }
@@ -294,7 +290,7 @@ struct SwImageTask : SwTask
                 if (image.rle) {
                     //Clear current task memorypool here if the clippers would use the same memory pool
                     imageDelOutline(&image, mpool, tid);
-                    for (auto clip = clips.data; clip < (clips.data + clips.count); ++clip) {
+                    for (auto clip = clips.data; clip < clips.end(); ++clip) {
                         auto clipper = static_cast<SwTask*>(*clip);
                         if (!clipper->clip(image.rle)) goto err;
                     }
@@ -326,26 +322,26 @@ static void _termEngine()
 }
 
 
-static void _renderFill(SwShapeTask* task, SwSurface* surface, uint32_t opacity)
+static void _renderFill(SwShapeTask* task, SwSurface* surface, uint8_t opacity)
 {
     uint8_t r, g, b, a;
     if (auto fill = task->rshape->fill) {
         rasterGradientShape(surface, &task->shape, fill->identifier());
     } else {
         task->rshape->fillColor(&r, &g, &b, &a);
-        a = static_cast<uint8_t>((opacity * (uint32_t) a) / 255);
+        a = MULTIPLY(opacity, a);
         if (a > 0) rasterShape(surface, &task->shape, r, g, b, a);
     }
 }
 
-static void _renderStroke(SwShapeTask* task, SwSurface* surface, uint32_t opacity)
+static void _renderStroke(SwShapeTask* task, SwSurface* surface, uint8_t opacity)
 {
     uint8_t r, g, b, a;
     if (auto strokeFill = task->rshape->strokeFill()) {
         rasterGradientStroke(surface, &task->shape, strokeFill->identifier());
     } else {
         if (task->rshape->strokeColor(&r, &g, &b, &a)) {
-            a = static_cast<uint8_t>((opacity * (uint32_t) a) / 255);
+            a = MULTIPLY(opacity, a);
             if (a > 0) rasterStroke(surface, &task->shape, r, g, b, a);
         }
     }
@@ -359,7 +355,7 @@ SwRenderer::~SwRenderer()
 {
     clearCompositors();
 
-    if (surface) delete(surface);
+    delete(surface);
 
     if (!sharedMpool) mpoolTerm(mpool);
 
@@ -371,7 +367,7 @@ SwRenderer::~SwRenderer()
 
 bool SwRenderer::clear()
 {
-    for (auto task = tasks.data; task < (tasks.data + tasks.count); ++task) {
+    for (auto task = tasks.data; task < tasks.end(); ++task) {
         if ((*task)->disposed) {
             delete(*task);
         } else {
@@ -444,7 +440,7 @@ bool SwRenderer::preRender()
 void SwRenderer::clearCompositors()
 {
     //Free Composite Caches
-    for (auto comp = compositors.data; comp < (compositors.data + compositors.count); ++comp) {
+    for (auto comp = compositors.data; comp < compositors.end(); ++comp) {
         free((*comp)->compositor->image.data);
         delete((*comp)->compositor);
         delete(*comp);
@@ -460,8 +456,9 @@ bool SwRenderer::postRender()
         rasterUnpremultiply(surface);
     }
 
-    for (auto task = tasks.data; task < (tasks.data + tasks.count); ++task) {
-        (*task)->pushed = false;
+    for (auto task = tasks.data; task < tasks.end(); ++task) {
+        if ((*task)->disposed) delete(*task);
+        else (*task)->pushed = false;
     }
     tasks.clear();
 
@@ -490,41 +487,79 @@ bool SwRenderer::renderShape(RenderData data)
 
     if (task->opacity == 0) return true;
 
-    uint32_t opacity;
-    Compositor* cmp = nullptr;
-
-    //Do Stroking Composition
-    if (task->cmpStroking) {
-        opacity = 255;
-        cmp = target(task->bounds(), colorSpace());
-        beginComposite(cmp, CompositeMethod::None, task->opacity);
-    //No Stroking Composition
-    } else {
-        opacity = task->opacity;
-    }
-
     //Main raster stage
     if (task->rshape->stroke && task->rshape->stroke->strokeFirst) {
-        _renderStroke(task, surface, opacity);
-        _renderFill(task, surface, opacity);
+        _renderStroke(task, surface, task->opacity);
+        _renderFill(task, surface, task->opacity);
     } else {
-        _renderFill(task, surface, opacity);
-        _renderStroke(task, surface, opacity);
+        _renderFill(task, surface, task->opacity);
+        _renderStroke(task, surface, task->opacity);
     }
 
-    if (task->cmpStroking) endComposite(cmp);
-
     return true;
 }
 
 
+bool SwRenderer::blend(BlendMethod method)
+{
+    if (surface->blendMethod == method) return true;
+    surface->blendMethod = method;
+
+    switch (method) {
+        case BlendMethod::Add:
+            surface->blender = opBlendAdd;
+            break;
+        case BlendMethod::Screen:
+            surface->blender = opBlendScreen;
+            break;
+        case BlendMethod::Multiply:
+            surface->blender = opBlendMultiply;
+            break;
+        case BlendMethod::Overlay:
+            surface->blender = opBlendOverlay;
+            break;
+        case BlendMethod::Difference:
+            surface->blender = opBlendDifference;
+            break;
+        case BlendMethod::Exclusion:
+            surface->blender = opBlendExclusion;
+            break;
+        case BlendMethod::SrcOver:
+            surface->blender = opBlendSrcOver;
+            break;
+        case BlendMethod::Darken:
+            surface->blender = opBlendDarken;
+            break;
+        case BlendMethod::Lighten:
+            surface->blender = opBlendLighten;
+            break;
+        case BlendMethod::ColorDodge:
+            surface->blender = opBlendColorDodge;
+            break;
+        case BlendMethod::ColorBurn:
+            surface->blender = opBlendColorBurn;
+            break;
+        case BlendMethod::HardLight:
+            surface->blender = opBlendHardLight;
+            break;
+        case BlendMethod::SoftLight:
+            surface->blender = opBlendSoftLight;
+            break;
+        default:
+            surface->blender = nullptr;
+            break;
+    }
+    return false;
+}
+
+
 RenderRegion SwRenderer::region(RenderData data)
 {
     return static_cast<SwTask*>(data)->bounds();
 }
 
 
-bool SwRenderer::beginComposite(Compositor* cmp, CompositeMethod method, uint32_t opacity)
+bool SwRenderer::beginComposite(Compositor* cmp, CompositeMethod method, uint8_t opacity)
 {
     if (!cmp) return false;
     auto p = static_cast<SwCompositor*>(cmp);
@@ -579,7 +614,7 @@ Compositor* SwRenderer::target(const RenderRegion& region, ColorSpace cs)
     auto reqChannelSize = CHANNEL_SIZE(cs);
 
     //Use cached data
-    for (auto p = compositors.data; p < (compositors.data + compositors.count); ++p) {
+    for (auto p = compositors.data; p < compositors.end(); ++p) {
         if ((*p)->compositor->valid && (*p)->compositor->image.channelSize == reqChannelSize) {
             cmp = *p;
             break;
@@ -674,7 +709,7 @@ bool SwRenderer::dispose(RenderData data)
 }
 
 
-void* SwRenderer::prepareCommon(SwTask* task, const RenderTransform* transform, uint32_t opacity, const Array<RenderData>& clips, RenderUpdateFlag flags)
+void* SwRenderer::prepareCommon(SwTask* task, const RenderTransform* transform, const Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags)
 {
     if (!surface) return task;
     if (flags == RenderUpdateFlag::None) return task;
@@ -685,7 +720,7 @@ void* SwRenderer::prepareCommon(SwTask* task, const RenderTransform* transform,
     //TODO: Failed threading them. It would be better if it's possible.
     //See: https://github.com/thorvg/thorvg/issues/1409
     //Guarantee composition targets get ready.
-    for (auto clip = clips.data; clip < (clips.data + clips.count); ++clip) {
+    for (auto clip = clips.data; clip < clips.end(); ++clip) {
         static_cast<SwTask*>(*clip)->done();
     }
 
@@ -719,7 +754,7 @@ void* SwRenderer::prepareCommon(SwTask* task, const RenderTransform* transform,
 }
 
 
-RenderData SwRenderer::prepare(Surface* surface, const RenderMesh* mesh, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags)
+RenderData SwRenderer::prepare(Surface* surface, const RenderMesh* mesh, RenderData data, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags)
 {
     //prepare task
     auto task = static_cast<SwImageTask*>(data);
@@ -728,11 +763,11 @@ RenderData SwRenderer::prepare(Surface* surface, const RenderMesh* mesh, RenderD
         task->source = surface;
         task->mesh = mesh;
     }
-    return prepareCommon(task, transform, opacity, clips, flags);
+    return prepareCommon(task, transform, clips, opacity, flags);
 }
 
 
-RenderData SwRenderer::prepare(const Array<RenderData>& scene, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags)
+RenderData SwRenderer::prepare(const Array<RenderData>& scene, RenderData data, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags)
 {
     //prepare task
     auto task = static_cast<SwSceneTask*>(data);
@@ -742,14 +777,14 @@ RenderData SwRenderer::prepare(const Array<RenderData>& scene, RenderData data,
     //TODO: Failed threading them. It would be better if it's possible.
     //See: https://github.com/thorvg/thorvg/issues/1409
     //Guarantee composition targets get ready.
-    for (auto task = scene.data; task < (scene.data + scene.count); ++task) {
+    for (auto task = scene.data; task < scene.end(); ++task) {
         static_cast<SwTask*>(*task)->done();
     }
-    return prepareCommon(task, transform, opacity, clips, flags);
+    return prepareCommon(task, transform, clips, opacity, flags);
 }
 
 
-RenderData SwRenderer::prepare(const RenderShape& rshape, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags, bool clipper)
+RenderData SwRenderer::prepare(const RenderShape& rshape, RenderData data, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags, bool clipper)
 {
     //prepare task
     auto task = static_cast<SwShapeTask*>(data);
@@ -759,7 +794,7 @@ RenderData SwRenderer::prepare(const RenderShape& rshape, RenderData data, const
     }
     task->clipper = clipper;
 
-    return prepareCommon(task, transform, opacity, clips, flags);
+    return prepareCommon(task, transform, clips, opacity, flags);
 }
 
 

+ 6 - 6
thirdparty/thorvg/src/lib/sw_engine/tvgSwRenderer.h

@@ -36,9 +36,9 @@ namespace tvg
 class SwRenderer : public RenderMethod
 {
 public:
-    RenderData prepare(const RenderShape& rshape, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags, bool clipper) override;
-    RenderData prepare(const Array<RenderData>& scene, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags) override;
-    RenderData prepare(Surface* surface, const RenderMesh* mesh, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags) override;
+    RenderData prepare(const RenderShape& rshape, RenderData data, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags, bool clipper) override;
+    RenderData prepare(const Array<RenderData>& scene, RenderData data, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags) override;
+    RenderData prepare(Surface* surface, const RenderMesh* mesh, RenderData data, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags) override;
     bool preRender() override;
     bool renderShape(RenderData data) override;
     bool renderImage(RenderData data) override;
@@ -47,6 +47,7 @@ public:
     RenderRegion region(RenderData data) override;
     RenderRegion viewport() override;
     bool viewport(const RenderRegion& vp) override;
+    bool blend(BlendMethod method) override;
     ColorSpace colorSpace() override;
 
     bool clear() override;
@@ -55,7 +56,7 @@ public:
     bool mempool(bool shared);
 
     Compositor* target(const RenderRegion& region, ColorSpace cs) override;
-    bool beginComposite(Compositor* cmp, CompositeMethod method, uint32_t opacity) override;
+    bool beginComposite(Compositor* cmp, CompositeMethod method, uint8_t opacity) override;
     bool endComposite(Compositor* cmp) override;
     void clearCompositors();
 
@@ -70,13 +71,12 @@ private:
     Array<SwSurface*>    compositors;                 //render targets cache list
     SwMpool*             mpool;                       //private memory pool
     RenderRegion         vport;                       //viewport
-
     bool                 sharedMpool = true;          //memory-pool behavior policy
 
     SwRenderer();
     ~SwRenderer();
 
-    RenderData prepareCommon(SwTask* task, const RenderTransform* transform, uint32_t opacity, const Array<RenderData>& clips, RenderUpdateFlag flags);
+    RenderData prepareCommon(SwTask* task, const RenderTransform* transform, const Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags);
 };
 
 }

+ 10 - 23
thirdparty/thorvg/src/lib/sw_engine/tvgSwRle.cpp

@@ -708,22 +708,19 @@ static void _cubicTo(RleWorker& rw, const SwPoint& ctrl1, const SwPoint& ctrl2,
 }
 
 
-static bool _decomposeOutline(RleWorker& rw)
+static void _decomposeOutline(RleWorker& rw)
 {
     auto outline = rw.outline;
     auto first = 0;  //index of first point in contour
 
-    for (uint32_t n = 0; n < outline->cntrsCnt; ++n) {
-        auto last = outline->cntrs[n];
-        auto limit = outline->pts + last;
-        auto start = UPSCALE(outline->pts[first]);
-        auto pt = outline->pts + first;
-        auto types = outline->types + first;
+    for (auto cntr = outline->cntrs.data; cntr < outline->cntrs.end(); ++cntr) {
+        auto last = *cntr;
+        auto limit = outline->pts.data + last;
+        auto start = UPSCALE(outline->pts.data[first]);
+        auto pt = outline->pts.data + first;
+        auto types = outline->types.data + first;
 
-        /* A contour cannot start with a cubic control point! */
-        if (types[0] == SW_CURVE_TYPE_CUBIC) goto invalid_outline;
-
-        _moveTo(rw, UPSCALE(outline->pts[first]));
+        _moveTo(rw, UPSCALE(outline->pts.data[first]));
 
         while (pt < limit) {
             ++pt;
@@ -734,9 +731,6 @@ static bool _decomposeOutline(RleWorker& rw)
                 _lineTo(rw, UPSCALE(*pt));
             //types cubic
             } else {
-                if (pt + 1 > limit || types[1] != SW_CURVE_TYPE_CUBIC)
-                    goto invalid_outline;
-
                 pt += 2;
                 types += 2;
 
@@ -752,22 +746,15 @@ static bool _decomposeOutline(RleWorker& rw)
     close:
        first = last + 1;
     }
-
-    return true;
-
-invalid_outline:
-    TVGERR("SW_ENGINE", "Invalid Outline!");
-    return false;
 }
 
 
 static int _genRle(RleWorker& rw)
 {
     if (setjmp(rw.jmpBuf) == 0) {
-        auto ret = _decomposeOutline(rw);
+        _decomposeOutline(rw);
         if (!rw.invalid) _recordCell(rw);
-        if (ret) return 0;  //success
-        else return 1;      //fail
+        return 0;
     }
     return -1;              //lack of cell memory
 }

+ 49 - 111
thirdparty/thorvg/src/lib/sw_engine/tvgSwShape.cpp

@@ -61,91 +61,39 @@ static void _lineSplitAt(const Line& cur, float at, Line& left, Line& right)
 }
 
 
-static bool _growOutlineContour(SwOutline& outline, uint32_t n)
-{
-    if (outline.reservedCntrsCnt >= outline.cntrsCnt + n) return false;
-    outline.reservedCntrsCnt = outline.cntrsCnt + n;
-    outline.cntrs = static_cast<uint32_t*>(realloc(outline.cntrs, outline.reservedCntrsCnt * sizeof(uint32_t)));
-    return true;
-}
-
-
-static void _reserveOutlineClose(SwOutline& outline)
-{
-    //Dash outlines are always opened.
-    //Only normal outlines use this information, it sholud be same to their contour counts.
-    if (outline.closed) free(outline.closed);
-    outline.closed = static_cast<bool*>(calloc(outline.reservedCntrsCnt, sizeof(bool)));
-}
-
-
-static void _resetOutlineClose(SwOutline& outline)
-{
-    memset(outline.closed, 0x0, outline.reservedCntrsCnt * sizeof(bool));
-}
-
-
-static void _growOutlinePoint(SwOutline& outline, uint32_t n)
-{
-    if (outline.reservedPtsCnt >= outline.ptsCnt + n) return;
-    outline.reservedPtsCnt = outline.ptsCnt + n;
-    outline.pts = static_cast<SwPoint*>(realloc(outline.pts, outline.reservedPtsCnt * sizeof(SwPoint)));
-    outline.types = static_cast<uint8_t*>(realloc(outline.types, outline.reservedPtsCnt * sizeof(uint8_t)));
-}
-
-
 static void _outlineEnd(SwOutline& outline)
 {
-    if (outline.ptsCnt == 0) return;
-
-    _growOutlineContour(outline, 1);
-    outline.cntrs[outline.cntrsCnt] = outline.ptsCnt - 1;
-    ++outline.cntrsCnt;
+    if (outline.pts.empty()) return;
+    outline.cntrs.push(outline.pts.count - 1);
 }
 
 
 static void _outlineMoveTo(SwOutline& outline, const Point* to, const Matrix* transform)
 {
-    _growOutlinePoint(outline, 1);
-
-    outline.pts[outline.ptsCnt] = mathTransform(to, transform);
-    outline.types[outline.ptsCnt] = SW_CURVE_TYPE_POINT;
-
-    if (outline.ptsCnt > 0) {
-        _growOutlineContour(outline, 1);
-        outline.cntrs[outline.cntrsCnt] = outline.ptsCnt - 1;
-        ++outline.cntrsCnt;
-    }
+    if (outline.pts.count > 0) outline.cntrs.push(outline.pts.count - 1);
 
-    ++outline.ptsCnt;
+    outline.pts.push(mathTransform(to, transform));
+    outline.types.push(SW_CURVE_TYPE_POINT);
 }
 
 
 static void _outlineLineTo(SwOutline& outline, const Point* to, const Matrix* transform)
 {
-    _growOutlinePoint(outline, 1);
-
-    outline.pts[outline.ptsCnt] = mathTransform(to, transform);
-    outline.types[outline.ptsCnt] = SW_CURVE_TYPE_POINT;
-    ++outline.ptsCnt;
+    outline.pts.push(mathTransform(to, transform));
+    outline.types.push(SW_CURVE_TYPE_POINT);
 }
 
 
 static void _outlineCubicTo(SwOutline& outline, const Point* ctrl1, const Point* ctrl2, const Point* to, const Matrix* transform)
 {
-    _growOutlinePoint(outline, 3);
-
-    outline.pts[outline.ptsCnt] = mathTransform(ctrl1, transform);
-    outline.types[outline.ptsCnt] = SW_CURVE_TYPE_CUBIC;
-    ++outline.ptsCnt;
+    outline.pts.push(mathTransform(ctrl1, transform));
+    outline.types.push(SW_CURVE_TYPE_CUBIC);
 
-    outline.pts[outline.ptsCnt] = mathTransform(ctrl2, transform);
-    outline.types[outline.ptsCnt] = SW_CURVE_TYPE_CUBIC;
-    ++outline.ptsCnt;
+    outline.pts.push(mathTransform(ctrl2, transform));
+    outline.types.push(SW_CURVE_TYPE_CUBIC);    
 
-    outline.pts[outline.ptsCnt] = mathTransform(to, transform);
-    outline.types[outline.ptsCnt] = SW_CURVE_TYPE_POINT;
-    ++outline.ptsCnt;
+    outline.pts.push(mathTransform(to, transform));
+    outline.types.push(SW_CURVE_TYPE_POINT);
 }
 
 
@@ -153,30 +101,21 @@ static void _outlineClose(SwOutline& outline)
 {
     uint32_t i = 0;
 
-    if (outline.cntrsCnt > 0) {
-        i = outline.cntrs[outline.cntrsCnt - 1] + 1;
-    } else {
-        i = 0;   //First Path
-    }
+    if (outline.cntrs.count > 0) i = outline.cntrs.last() + 1;
+    else i = 0;   //First Path
 
     //Make sure there is at least one point in the current path
-    if (outline.ptsCnt == i) return;
+    if (outline.pts.count == i) return;
 
     //Close the path
-    _growOutlinePoint(outline, 1);
-
-    outline.pts[outline.ptsCnt] = outline.pts[i];
-    outline.types[outline.ptsCnt] = SW_CURVE_TYPE_POINT;
-    ++outline.ptsCnt;
-    outline.closed[outline.cntrsCnt] = true;
+    outline.pts.push(outline.pts.data[i]);
+    outline.types.push(SW_CURVE_TYPE_POINT);
+    outline.closed.push(true);
 }
 
 
 static void _dashLineTo(SwDashStroke& dash, const Point* to, const Matrix* transform)
 {
-    _growOutlinePoint(*dash.outline, dash.outline->ptsCnt >> 1);
-    _growOutlineContour(*dash.outline, dash.outline->cntrsCnt >> 1);
-
     Line cur = {dash.ptCur, *to};
     auto len = _lineLength(cur.pt1, cur.pt2);
 
@@ -220,9 +159,6 @@ static void _dashLineTo(SwDashStroke& dash, const Point* to, const Matrix* trans
 
 static void _dashCubicTo(SwDashStroke& dash, const Point* ctrl1, const Point* ctrl2, const Point* to, const Matrix* transform)
 {
-    _growOutlinePoint(*dash.outline, dash.outline->ptsCnt >> 1);
-    _growOutlineContour(*dash.outline, dash.outline->cntrsCnt >> 1);
-
     Bezier cur = {dash.ptCur, *ctrl1, *ctrl2, *to};
     auto len = bezLength(cur);
 
@@ -269,11 +205,11 @@ static void _dashCubicTo(SwDashStroke& dash, const Point* ctrl1, const Point* ct
 
 static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* transform)
 {
-    const PathCommand* cmds = rshape->path.cmds;
-    auto cmdCnt = rshape->path.cmdCnt;
+    const PathCommand* cmds = rshape->path.cmds.data;
+    auto cmdCnt = rshape->path.cmds.count;
 
-    const Point* pts = rshape->path.pts;
-    auto ptsCnt = rshape->path.ptsCnt;
+    const Point* pts = rshape->path.pts.data;
+    auto ptsCnt = rshape->path.pts.count;
 
     //No actual shape data
     if (cmdCnt == 0 || ptsCnt == 0) return nullptr;
@@ -323,8 +259,9 @@ static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* trans
     ++outlineCntrsCnt;  //for end
 
     //No idea exact count.... Reserve Approximitely 20x...
-    _growOutlinePoint(*dash.outline, outlinePtsCnt * 20);
-    _growOutlineContour(*dash.outline, outlineCntrsCnt * 20);
+    dash.outline->pts.grow(20 * outlinePtsCnt);
+    dash.outline->types.grow(20 * outlinePtsCnt);
+    dash.outline->cntrs.grow(20 * outlineCntrsCnt);
 
     while (cmdCnt-- > 0) {
         switch (*cmds) {
@@ -364,12 +301,12 @@ static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* trans
 static bool _axisAlignedRect(const SwOutline* outline)
 {
     //Fast Track: axis-aligned rectangle?
-    if (outline->ptsCnt != 5) return false;
+    if (outline->pts.count != 5) return false;
 
-    auto pt1 = outline->pts + 0;
-    auto pt2 = outline->pts + 1;
-    auto pt3 = outline->pts + 2;
-    auto pt4 = outline->pts + 3;
+    auto pt1 = outline->pts.data + 0;
+    auto pt2 = outline->pts.data + 1;
+    auto pt3 = outline->pts.data + 2;
+    auto pt4 = outline->pts.data + 3;
 
     auto a = SwPoint{pt1->x, pt3->y};
     auto b = SwPoint{pt3->x, pt1->y};
@@ -380,14 +317,13 @@ static bool _axisAlignedRect(const SwOutline* outline)
 }
 
 
-
 static bool _genOutline(SwShape* shape, const RenderShape* rshape, const Matrix* transform, SwMpool* mpool, unsigned tid, bool hasComposite)
 {
-    const PathCommand* cmds = rshape->path.cmds;
-    auto cmdCnt = rshape->path.cmdCnt;
+    const PathCommand* cmds = rshape->path.cmds.data;
+    auto cmdCnt = rshape->path.cmds.count;
 
-    const Point* pts = rshape->path.pts;
-    auto ptsCnt = rshape->path.ptsCnt;
+    const Point* pts = rshape->path.pts.data;
+    auto ptsCnt = rshape->path.pts.count;
 
     //No actual shape data
     if (cmdCnt == 0 || ptsCnt == 0) return false;
@@ -431,13 +367,15 @@ static bool _genOutline(SwShape* shape, const RenderShape* rshape, const Matrix*
     shape->outline = mpoolReqOutline(mpool, tid);
     auto outline = shape->outline;
 
-    _growOutlinePoint(*outline, outlinePtsCnt);
+    outline->pts.grow(outlinePtsCnt);
+    outline->types.grow(outlinePtsCnt);
+    outline->cntrs.grow(outlineCntrsCnt);
 
-     if (_growOutlineContour(*outline, outlineCntrsCnt)) {
-        _reserveOutlineClose(*outline);
-    } else {
-        _resetOutlineClose(*outline);
-    }
+    //Dash outlines are always opened.
+    //Only normal outlines use this information, it sholud be same to their contour counts.
+    outline->closed.reserve(outline->cntrs.reserved);
+
+    memset(outline->closed.data, 0x0, sizeof(bool) * outline->closed.reserved);
 
     //Generate Outlines
     while (cmdCnt-- > 0) {
@@ -605,10 +543,10 @@ bool shapeGenStrokeRle(SwShape* shape, const RenderShape* rshape, const Matrix*
 
 fail:
     if (freeOutline) {
-        if (shapeOutline->cntrs) free(shapeOutline->cntrs);
-        if (shapeOutline->pts) free(shapeOutline->pts);
-        if (shapeOutline->types) free(shapeOutline->types);
-        if (shapeOutline->closed) free(shapeOutline->closed);
+        free(shapeOutline->cntrs.data);
+        free(shapeOutline->pts.data);
+        free(shapeOutline->types.data);
+        free(shapeOutline->closed.data);
         free(shapeOutline);
     }
     mpoolRetStrokeOutline(mpool, tid);
@@ -617,13 +555,13 @@ fail:
 }
 
 
-bool shapeGenFillColors(SwShape* shape, const Fill* fill, const Matrix* transform, SwSurface* surface, uint32_t opacity, bool ctable)
+bool shapeGenFillColors(SwShape* shape, const Fill* fill, const Matrix* transform, SwSurface* surface, uint8_t opacity, bool ctable)
 {
     return fillGenColorTable(shape->fill, fill, transform, surface, opacity, ctable);
 }
 
 
-bool shapeGenStrokeFillColors(SwShape* shape, const Fill* fill, const Matrix* transform, SwSurface* surface, uint32_t opacity, bool ctable)
+bool shapeGenStrokeFillColors(SwShape* shape, const Fill* fill, const Matrix* transform, SwSurface* surface, uint8_t opacity, bool ctable)
 {
     return fillGenColorTable(shape->stroke->fill, fill, transform, surface, opacity, ctable);
 }

+ 23 - 47
thirdparty/thorvg/src/lib/sw_engine/tvgSwStroke.cpp

@@ -129,7 +129,6 @@ static void _borderCubicTo(SwStrokeBorder* border, const SwPoint& ctrl1, const S
     tag[2] = SW_STROKE_TAG_POINT;
 
     border->ptsCnt += 3;
-
     border->movable = false;
 }
 
@@ -193,7 +192,6 @@ static void _borderLineTo(SwStrokeBorder* border, const SwPoint& to, bool movabl
         //move last point
         border->pts[border->ptsCnt - 1] = to;
     } else {
-
         //don't add zero-length line_to
         if (border->ptsCnt > 0 && (border->pts[border->ptsCnt - 1] - to).small()) return;
 
@@ -233,8 +231,6 @@ static void _arcTo(SwStroke& stroke, int32_t side)
 
 static void _outside(SwStroke& stroke, int32_t side, SwFixed lineLength)
 {
-    constexpr SwFixed MITER_LIMIT = 4 * (1 << 16);
-
     auto border = stroke.borders + side;
 
     if (stroke.join == StrokeJoin::Round) {
@@ -257,7 +253,7 @@ static void _outside(SwStroke& stroke, int32_t side, SwFixed lineLength)
             }
 
             thcos = mathCos(theta);
-            auto sigma = mathMultiply(MITER_LIMIT, thcos);
+            auto sigma = mathMultiply(stroke.miterlimit, thcos);
 
             //is miter limit exceeded?
             if (sigma < 0x10000L) bevel = true;
@@ -432,8 +428,7 @@ static void _lineTo(SwStroke& stroke, const SwPoint& to)
 
 static void _cubicTo(SwStroke& stroke, const SwPoint& ctrl1, const SwPoint& ctrl2, const SwPoint& to)
 {
-    /* if all control points are coincident, this is a no-op;
-       avoid creating a spurious corner */
+    //if all control points are coincident, this is a no-op; avoid creating a spurious corner
     if ((stroke.center - ctrl1).small() && (ctrl1 - ctrl2).small() && (ctrl2 - to).small()) {
         stroke.center = to;
         return;
@@ -499,8 +494,7 @@ static void _cubicTo(SwStroke& stroke, const SwPoint& ctrl1, const SwPoint& ctrl
         auto border = stroke.borders;
         int32_t side = 0;
 
-        while (side <= 1)
-        {
+        while (side < 2) {
             auto rotate = SIDE_TO_ROTATE(side);
 
             //compute control points
@@ -521,7 +515,6 @@ static void _cubicTo(SwStroke& stroke, const SwPoint& ctrl1, const SwPoint& ctrl
             _end += arc[0];
 
             if (stroke.handleWideStrokes) {
-
                 /* determine whether the border radius is greater than the radius of
                    curvature of the original arc */
                 auto _start = border->pts[border->ptsCnt - 1];
@@ -556,8 +549,6 @@ static void _cubicTo(SwStroke& stroke, const SwPoint& ctrl1, const SwPoint& ctrl
                     ++border;
                     continue;
                 }
-
-            //else fall through
             }
             _borderCubicTo(border, _ctrl1, _ctrl2, _end);
             ++side;
@@ -653,7 +644,6 @@ static void _addReverseLeft(SwStroke& stroke, bool opened)
             if (ttag == SW_STROKE_TAG_BEGIN || ttag == SW_STROKE_TAG_END)
               dstTag[0] ^= (SW_STROKE_TAG_BEGIN | SW_STROKE_TAG_END);
         }
-
         --srcPt;
         --srcTag;
         ++dstPt;
@@ -779,36 +769,27 @@ fail:
 static void _exportBorderOutline(const SwStroke& stroke, SwOutline* outline, uint32_t side)
 {
     auto border = stroke.borders + side;
+    if (border->ptsCnt == 0) return;
 
-    if (border->ptsCnt == 0) return;  //invalid border
-
-    memcpy(outline->pts + outline->ptsCnt, border->pts, border->ptsCnt * sizeof(SwPoint));
+    memcpy(outline->pts.data + outline->pts.count, border->pts, border->ptsCnt * sizeof(SwPoint));
 
     auto cnt = border->ptsCnt;
     auto src = border->tags;
-    auto tags = outline->types + outline->ptsCnt;
-    auto cntrs = outline->cntrs + outline->cntrsCnt;
-    auto idx = outline->ptsCnt;
+    auto tags = outline->types.data + outline->types.count;
+    auto idx = outline->pts.count;
 
     while (cnt > 0) {
-
         if (*src & SW_STROKE_TAG_POINT) *tags = SW_CURVE_TYPE_POINT;
         else if (*src & SW_STROKE_TAG_CUBIC) *tags = SW_CURVE_TYPE_CUBIC;
-        else {
-            //LOG: What type of stroke outline??
-        }
-
-        if (*src & SW_STROKE_TAG_END) {
-            *cntrs = idx;
-            ++cntrs;
-            ++outline->cntrsCnt;
-        }
+        else TVGERR("SW_ENGINE", "Invalid stroke tag was given! = %d", *src);
+        if (*src & SW_STROKE_TAG_END) outline->cntrs.push(idx);
         ++src;
         ++tags;
         ++idx;
         --cnt;
     }
-    outline->ptsCnt = outline->ptsCnt + border->ptsCnt;
+    outline->pts.count += border->ptsCnt;
+    outline->types.count += border->ptsCnt;
 }
 
 
@@ -844,6 +825,7 @@ void strokeReset(SwStroke* stroke, const RenderShape* rshape, const Matrix* tran
 
     stroke->width = HALF_STROKE(rshape->strokeWidth());
     stroke->cap = rshape->strokeCap();
+    stroke->miterlimit = static_cast<SwFixed>(rshape->strokeMiterlimit()) << 16;
 
     //Save line join: it can be temporarily changed when stroking curves...
     stroke->joinSaved = stroke->join = rshape->strokeJoin();
@@ -858,10 +840,11 @@ void strokeReset(SwStroke* stroke, const RenderShape* rshape, const Matrix* tran
 bool strokeParseOutline(SwStroke* stroke, const SwOutline& outline)
 {
     uint32_t first = 0;
+    uint32_t i = 0;
 
-    for (uint32_t i = 0; i < outline.cntrsCnt; ++i) {
-        auto last = outline.cntrs[i];  //index of last point in contour
-        auto limit = outline.pts + last;
+    for (auto cntr = outline.cntrs.data; cntr < outline.cntrs.end(); ++cntr, ++i) {
+        auto last = *cntr;           //index of last point in contour
+        auto limit = outline.pts.data + last;
 
         //Skip empty points
         if (last <= first) {
@@ -869,15 +852,15 @@ bool strokeParseOutline(SwStroke* stroke, const SwOutline& outline)
             continue;
         }
 
-        auto start = outline.pts[first];
-        auto pt = outline.pts + first;
-        auto types = outline.types + first;
+        auto start = outline.pts.data[first];
+        auto pt = outline.pts.data + first;
+        auto types = outline.types.data + first;
         auto type = types[0];
 
         //A contour cannot start with a cubic control point
         if (type == SW_CURVE_TYPE_CUBIC) return false;
 
-        auto closed =  outline.closed ? outline.closed[i]: false;
+        auto closed =  outline.closed.data ? outline.closed.data[i]: false;
 
         _beginSubPath(*stroke, start, closed);
 
@@ -903,7 +886,6 @@ bool strokeParseOutline(SwStroke* stroke, const SwOutline& outline)
                 goto close;
             }
         }
-
     close:
         if (!stroke->firstPt) _endSubPath(*stroke);
         first = last + 1;
@@ -923,15 +905,9 @@ SwOutline* strokeExportOutline(SwStroke* stroke, SwMpool* mpool, unsigned tid)
     auto cntrsCnt = count2 + count4;
 
     auto outline = mpoolReqStrokeOutline(mpool, tid);
-    if (outline->reservedPtsCnt < ptsCnt) {
-        outline->pts = static_cast<SwPoint*>(realloc(outline->pts, sizeof(SwPoint) * ptsCnt));
-        outline->types = static_cast<uint8_t*>(realloc(outline->types, sizeof(uint8_t) * ptsCnt));
-        outline->reservedPtsCnt = ptsCnt;
-    }
-    if (outline->reservedCntrsCnt < cntrsCnt) {
-        outline->cntrs = static_cast<uint32_t*>(realloc(outline->cntrs, sizeof(uint32_t) * cntrsCnt));
-        outline->reservedCntrsCnt = cntrsCnt;
-    }
+    outline->pts.reserve(ptsCnt);
+    outline->types.reserve(ptsCnt);
+    outline->cntrs.reserve(cntrsCnt);
 
     _exportBorderOutline(*stroke, outline, 0);  //left
     _exportBorderOutline(*stroke, outline, 1);  //right

+ 108 - 0
thirdparty/thorvg/src/lib/tvgAnimation.cpp

@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2023 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.
+ */
+
+//#include "tvgAnimationImpl.h"
+#include "tvgCommon.h"
+#include "tvgFrameModule.h"
+#include "tvgPictureImpl.h"
+
+/************************************************************************/
+/* Internal Class Implementation                                        */
+/************************************************************************/
+
+struct Animation::Impl
+{
+    //TODO: Memory Safety
+    Picture picture;
+};
+
+/************************************************************************/
+/* External Class Implementation                                        */
+/************************************************************************/
+
+Animation::~Animation()
+{
+
+}
+
+
+Animation::Animation() : pImpl(new Impl)
+{
+    pImpl->picture.pImpl->animated = true;
+}
+
+
+Result Animation::frame(uint32_t no) noexcept
+{
+    auto loader = pImpl->picture.pImpl->loader.get();
+
+    if (!loader) return Result::InsufficientCondition;
+    if (!loader->animatable()) return Result::NonSupport;
+
+    if (static_cast<FrameModule*>(loader)->frame(no)) return Result::Success;
+    return Result::InsufficientCondition;
+}
+
+
+Picture* Animation::picture() const noexcept
+{
+    return &pImpl->picture;
+}
+
+
+uint32_t Animation::curFrame() const noexcept
+{
+    auto loader = pImpl->picture.pImpl->loader.get();
+
+    if (!loader) return 0;
+    if (!loader->animatable()) return 0;
+
+    return static_cast<FrameModule*>(loader)->curFrame();
+}
+
+
+uint32_t Animation::totalFrame() const noexcept
+{
+    auto loader = pImpl->picture.pImpl->loader.get();
+
+    if (!loader) return 0;
+    if (!loader->animatable()) return 0;
+
+    return static_cast<FrameModule*>(loader)->totalFrame();
+}
+
+
+float Animation::duration() const noexcept
+{
+    auto loader = pImpl->picture.pImpl->loader.get();
+
+    if (!loader) return 0;
+    if (!loader->animatable()) return 0;
+
+    return static_cast<FrameModule*>(loader)->duration();
+}
+
+
+unique_ptr<Animation> Animation::gen() noexcept
+{
+    return unique_ptr<Animation>(new Animation);
+}

+ 36 - 18
thirdparty/thorvg/src/lib/tvgArray.h

@@ -35,30 +35,35 @@ struct Array
     uint32_t count = 0;
     uint32_t reserved = 0;
 
+    Array(){}
+
+    Array(const Array& rhs)
+    {
+        reset();
+        *this = rhs;
+    }
+
     void push(T element)
     {
         if (count + 1 > reserved) {
-            reserved = (count + 1) * 2;
-            auto p  = data;
+            reserved = count + (count + 2) / 2;
             data = static_cast<T*>(realloc(data, sizeof(T) * reserved));
-            if (!data) {
-                data = p;
-                return;
-            }
         }
         data[count++] = element;
     }
 
+    void push(Array<T>& rhs)
+    {
+        grow(rhs.count);
+        memcpy(data + count, rhs.data, rhs.count * sizeof(T));
+        count += rhs.count;
+    }
+
     bool reserve(uint32_t size)
     {
         if (size > reserved) {
             reserved = size;
-            auto p = data;
             data = static_cast<T*>(realloc(data, sizeof(T) * reserved));
-            if (!data) {
-                data = p;
-                return false;
-            }
         }
         return true;
     }
@@ -68,11 +73,21 @@ struct Array
         return reserve(count + size);
     }
 
-    T* ptr()
+    T* end() const
     {
         return data + count;
     }
 
+    T& last()
+    {
+        return data[count - 1];
+    }
+
+    T& first()
+    {
+        return data[0];
+    }
+
     void pop()
     {
         if (count > 0) --count;
@@ -80,10 +95,8 @@ struct Array
 
     void reset()
     {
-        if (data) {
-            free(data);
-            data = nullptr;
-        }
+        free(data);
+        data = nullptr;
         count = reserved = 0;
     }
 
@@ -92,16 +105,21 @@ struct Array
         count = 0;
     }
 
+    bool empty() const
+    {
+        return count == 0;
+    }
+
     void operator=(const Array& rhs)
     {
         reserve(rhs.count);
-        if (rhs.count > 0) memcpy(data, rhs.data, sizeof(T) * reserved);
+        if (rhs.count > 0) memcpy(data, rhs.data, sizeof(T) * rhs.count);
         count = rhs.count;
     }
 
     ~Array()
     {
-        if (data) free(data);
+        free(data);
     }
 };
 

+ 54 - 12
thirdparty/thorvg/src/lib/tvgBezier.cpp

@@ -20,10 +20,11 @@
  * SOFTWARE.
  */
 
-#include <float.h>
-#include <math.h>
+#include "tvgMath.h"
 #include "tvgBezier.h"
 
+#define BEZIER_EPSILON 1e-4f
+
 /************************************************************************/
 /* Internal Class Implementation                                        */
 /************************************************************************/
@@ -107,29 +108,25 @@ void bezSplitLeft(Bezier& cur, float at, Bezier& left)
 }
 
 
-float bezAt(const Bezier& bz, float at)
+float bezAt(const Bezier& bz, float at, float length)
 {
-    auto len = bezLength(bz);
     auto biggest = 1.0f;
     auto smallest = 0.0f;
     auto t = 0.5f;
 
     //just in case to prevent an infinite loop
     if (at <= 0) return 0.0f;
-
-    if (at >= len) return 1.0f;
+    if (at >= length) return length;
 
     while (true) {
         auto right = bz;
         Bezier left;
         bezSplitLeft(right, t, left);
-        len = bezLength(left);
-
-        if (fabsf(len - at) < BEZIER_EPSILON || fabsf(smallest - biggest) < BEZIER_EPSILON) {
+        length = bezLength(left);
+        if (fabsf(length - at) < BEZIER_EPSILON || fabsf(smallest - biggest) < BEZIER_EPSILON) {
             break;
         }
-
-        if (len < at) {
+        if (length < at) {
             smallest = t;
             t = (t + biggest) * 0.5f;
         } else {
@@ -144,8 +141,53 @@ float bezAt(const Bezier& bz, float at)
 void bezSplitAt(const Bezier& cur, float at, Bezier& left, Bezier& right)
 {
     right = cur;
-    auto t = bezAt(right, at);
+    auto t = bezAt(right, at, bezLength(right));
     bezSplitLeft(right, t, left);
 }
 
+
+Point bezPointAt(const Bezier& bz, float t)
+{
+    Point cur;
+    auto it = 1.0f - t;
+
+    auto ax = bz.start.x * it + bz.ctrl1.x * t;
+    auto bx = bz.ctrl1.x * it + bz.ctrl2.x * t;
+    auto cx = bz.ctrl2.x * it + bz.end.x * t;
+    ax = ax * it + bx * t;
+    bx = bx * it + cx * t;
+    cur.x = ax * it + bx * t;
+
+    float ay = bz.start.y * it + bz.ctrl1.y * t;
+    float by = bz.ctrl1.y * it + bz.ctrl2.y * t;
+    float cy = bz.ctrl2.y * it + bz.end.y * t;
+    ay = ay * it + by * t;
+    by = by * it + cy * t;
+    cur.y = ay * it + by * t;
+
+    return cur;
+}
+
+
+float bezAngleAt(const Bezier& bz, float t)
+{
+    if (t < 0 || t > 1) return 0;
+
+    //derivate
+    // p'(t) = 3 * (-(1-2t+t^2) * p0 + (1 - 4 * t + 3 * t^2) * p1 + (2 * t - 3 *
+    // t^2) * p2 + t^2 * p3)
+    float mt = 1.0f - t;
+    float d = t * t;
+    float a = -mt * mt;
+    float b = 1 - 4 * t + 3 * d;
+    float c = 2 * t - 3 * d;
+
+    Point pt ={a * bz.start.x + b * bz.ctrl1.x + c * bz.ctrl2.x + d * bz.end.x, a * bz.start.y + b * bz.ctrl1.y + c * bz.ctrl2.y + d * bz.end.y};
+    pt.x *= 3;
+    pt.y *= 3;
+
+    return atan2(pt.x, pt.y) * 180.0f / 3.141592f;
+}
+
+
 }

+ 3 - 3
thirdparty/thorvg/src/lib/tvgBezier.h

@@ -28,8 +28,6 @@
 namespace tvg
 {
 
-#define BEZIER_EPSILON 1e-4f
-
 struct Bezier
 {
     Point start;
@@ -41,8 +39,10 @@ struct Bezier
 void bezSplit(const Bezier&cur, Bezier& left, Bezier& right);
 float bezLength(const Bezier& cur);
 void bezSplitLeft(Bezier& cur, float at, Bezier& left);
-float bezAt(const Bezier& bz, float at);
+float bezAt(const Bezier& bz, float at, float length);
 void bezSplitAt(const Bezier& cur, float at, Bezier& left, Bezier& right);
+Point bezPointAt(const Bezier& bz, float t);
+float bezAngleAt(const Bezier& bz, float t);
 
 }
 

+ 8 - 7
thirdparty/thorvg/src/lib/tvgBinaryDesc.h

@@ -26,9 +26,6 @@
 /* TODO: Need to consider whether uin8_t is enough size for extension...
    Rather than optimal data, we can use enough size and data compress? */
 
-/* Data types, do not change data types once Tvg Format is officially released,
-   That would occur the abi break. */
-
 using TvgBinByte = uint8_t;
 using TvgBinCounter = uint32_t;
 using TvgBinTag = TvgBinByte;
@@ -39,7 +36,7 @@ using TvgBinFlag = TvgBinByte;
 #define TVG_HEADER_SIZE 33                //TVG_HEADER_SIGNATURE_LENGTH + TVG_HEADER_VERSION_LENGTH + 2*SIZE(float) + TVG_HEADER_RESERVED_LENGTH + TVG_HEADER_COMPRESS_SIZE
 #define TVG_HEADER_SIGNATURE "ThorVG"
 #define TVG_HEADER_SIGNATURE_LENGTH 6
-#define TVG_HEADER_VERSION "000400"       //Major 00, Minor 04, Micro 00
+#define TVG_HEADER_VERSION "001000"       //Major 00, Minor 10, Micro 00
 #define TVG_HEADER_VERSION_LENGTH 6
 #define TVG_HEADER_RESERVED_LENGTH 1      //Storing flags for extensions
 #define TVG_HEADER_COMPRESS_SIZE 12       //TVG_HEADER_UNCOMPRESSED_SIZE + TVG_HEADER_COMPRESSED_SIZE + TVG_HEADER_COMPRESSED_SIZE_BITS
@@ -63,8 +60,9 @@ using TvgBinFlag = TvgBinByte;
 #define TVG_TAG_PAINT_CMP_METHOD                    (TvgBinTag)0x20
 
 
+//TODO: Keep this for the compatibility, Remove in TVG 1.0 release
 //Scene
-#define TVG_TAG_SCENE_RESERVEDCNT                   (TvgBinTag)0x30
+ #define TVG_TAG_SCENE_RESERVEDCNT                   (TvgBinTag)0x30
 
 
 //Shape
@@ -73,14 +71,17 @@ using TvgBinFlag = TvgBinByte;
 #define TVG_TAG_SHAPE_FILL                          (TvgBinTag)0x42
 #define TVG_TAG_SHAPE_COLOR                         (TvgBinTag)0x43
 #define TVG_TAG_SHAPE_FILLRULE                      (TvgBinTag)0x44
-#define TVG_TAG_SHAPE_STROKE_CAP                    (TvgBinTag)0x50
-#define TVG_TAG_SHAPE_STROKE_JOIN                   (TvgBinTag)0x51
+
 
 //Stroke
+#define TVG_TAG_SHAPE_STROKE_CAP                    (TvgBinTag)0x50
+#define TVG_TAG_SHAPE_STROKE_JOIN                   (TvgBinTag)0x51
 #define TVG_TAG_SHAPE_STROKE_WIDTH                  (TvgBinTag)0x52
 #define TVG_TAG_SHAPE_STROKE_COLOR                  (TvgBinTag)0x53
 #define TVG_TAG_SHAPE_STROKE_FILL                   (TvgBinTag)0x54
 #define TVG_TAG_SHAPE_STROKE_DASHPTRN               (TvgBinTag)0x55
+#define TVG_TAG_SHAPE_STROKE_MITERLIMIT             (TvgBinTag)0x56
+#define TVG_TAG_SHAPE_STROKE_ORDER                  (TvgBinTag)0x57
 
 
 //Fill

+ 9 - 4
thirdparty/thorvg/src/lib/tvgCanvas.cpp

@@ -37,16 +37,21 @@ Canvas::~Canvas()
 }
 
 
-Result Canvas::reserve(uint32_t n) noexcept
+Result Canvas::reserve(TVG_UNUSED uint32_t n) noexcept
 {
-    if (!pImpl->paints.reserve(n)) return Result::FailedAllocation;
-    return Result::Success;
+    return Result::NonSupport;
+}
+
+
+list<Paint*>& Canvas::paints() noexcept
+{
+    return pImpl->paints;
 }
 
 
 Result Canvas::push(unique_ptr<Paint> paint) noexcept
 {
-    return pImpl->push(move(paint));
+    return pImpl->push(std::move(paint));
 }
 
 

+ 14 - 14
thirdparty/thorvg/src/lib/tvgCanvasImpl.h

@@ -31,7 +31,7 @@
 
 struct Canvas::Impl
 {
-    Array<Paint*> paints;
+    list<Paint*> paints;
     RenderMethod* renderer;
     bool refresh = false;   //if all paints should be updated by force.
     bool drawing = false;   //on drawing condition?
@@ -53,7 +53,7 @@ struct Canvas::Impl
 
         auto p = paint.release();
         if (!p) return Result::MemoryCorruption;
-        paints.push(p);
+        paints.push_back(p);
 
         return update(p, true);
     }
@@ -64,9 +64,9 @@ struct Canvas::Impl
         if (!renderer || !renderer->clear()) return Result::InsufficientCondition;
 
         //Free paints
-        for (auto paint = paints.data; paint < (paints.data + paints.count); ++paint) {
-            (*paint)->pImpl->dispose(*renderer);
-            if (free) delete(*paint);
+        for (auto paint : paints) {
+            paint->pImpl->dispose(*renderer);
+            if (free) delete(paint);
         }
 
         paints.clear();
@@ -83,7 +83,7 @@ struct Canvas::Impl
 
     Result update(Paint* paint, bool force)
     {
-        if (paints.count == 0 || drawing || !renderer) return Result::InsufficientCondition;
+        if (paints.empty() || drawing || !renderer) return Result::InsufficientCondition;
 
         Array<RenderData> clips;
         auto flag = RenderUpdateFlag::None;
@@ -92,17 +92,17 @@ struct Canvas::Impl
         //Update single paint node
         if (paint) {
             //Optimize Me: Can we skip the searching?
-            for (auto paint2 = paints.data; paint2 < (paints.data + paints.count); ++paint2) {
-                if ((*paint2) == paint) {
-                    paint->pImpl->update(*renderer, nullptr, 255, clips, flag);
+            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
         } else {
-            for (auto paint = paints.data; paint < (paints.data + paints.count); ++paint) {
-                (*paint)->pImpl->update(*renderer, nullptr, 255, clips, flag);
+            for (auto paint : paints) {
+                paint->pImpl->update(*renderer, nullptr, clips, 255, flag);
             }
         }
 
@@ -113,11 +113,11 @@ struct Canvas::Impl
 
     Result draw()
     {
-        if (drawing || paints.count == 0 || !renderer || !renderer->preRender()) return Result::InsufficientCondition;
+        if (drawing || paints.empty() || !renderer || !renderer->preRender()) return Result::InsufficientCondition;
 
         bool rendered = false;
-        for (auto paint = paints.data; paint < (paints.data + paints.count); ++paint) {
-            if ((*paint)->pImpl->render(*renderer)) rendered = true;
+        for (auto paint : paints) {
+            if (paint->pImpl->render(*renderer)) rendered = true;
         }
 
         if (!rendered || !renderer->postRender()) return Result::InsufficientCondition;

+ 3 - 1
thirdparty/thorvg/src/lib/tvgCommon.h

@@ -62,7 +62,9 @@ using namespace tvg;
 #define TVG_CLASS_ID_LINEAR    4
 #define TVG_CLASS_ID_RADIAL    5
 
-enum class FileType { Tvg = 0, Svg, Raw, Png, Jpg, Unknown };
+enum class FileType { Tvg = 0, Svg, Lottie, Raw, Png, Jpg, Webp, Unknown };
+
+using Size = Point;
 
 #ifdef THORVG_LOG_ENABLED
     constexpr auto ErrorColor = "\033[31m";  //red

+ 2 - 2
thirdparty/thorvg/src/lib/tvgFill.h

@@ -55,11 +55,11 @@ struct Fill::Impl
     uint32_t cnt = 0;
     FillSpread spread;
     DuplicateMethod<Fill>* dup = nullptr;
-    uint32_t id;
+    uint8_t id;
 
     ~Impl()
     {
-        if (dup) delete(dup);
+        delete(dup);
         free(colorStops);
         free(transform);
     }

+ 17 - 17
thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.h → thirdparty/thorvg/src/lib/tvgFrameModule.h

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved.
+ * Copyright (c) 2023 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,28 +20,28 @@
  * SOFTWARE.
  */
 
-#ifndef _TVG_PNG_LOADER_H_
-#define _TVG_PNG_LOADER_H_
+#ifndef _TVG_FRAME_MODULE_H_
+#define _TVG_FRAME_MODULE_H_
 
-#include <png.h>
+#include "tvgLoadModule.h"
 
-class PngLoader : public LoadModule
+namespace tvg
+{
+
+class FrameModule: public LoadModule
 {
 public:
-    PngLoader();
-    ~PngLoader();
+    virtual ~FrameModule() {}
 
-    using LoadModule::open;
-    bool open(const string& path) override;
-    bool open(const char* data, uint32_t size, bool copy) override;
-    bool read() override;
-    bool close() override;
+    virtual bool frame(uint32_t frameNo) = 0;   //set the current frame number
 
-    unique_ptr<Surface> bitmap() override;
+    virtual uint32_t totalFrame() = 0;      //return the total frame count
+    virtual uint32_t curFrame() = 0;        //return the current frame number
+    virtual float duration() = 0;           //return the animation duration in seconds
 
-private:
-    png_imagep image = nullptr;
-    uint32_t* content = nullptr;
+    virtual bool animatable() override { return true; }
 };
 
-#endif //_TVG_PNG_LOADER_H_
+}
+
+#endif //_TVG_FRAME_MODULE_H_

+ 4 - 5
thirdparty/thorvg/src/lib/tvgLoadModule.h

@@ -31,11 +31,6 @@ namespace tvg
 class LoadModule
 {
 public:
-    //default view box, if any.
-    float vx = 0;
-    float vy = 0;
-    float vw = 0;
-    float vh = 0;
     float w = 0, h = 0;                             //default image size
     ColorSpace cs = ColorSpace::Unsupported;        //must be clarified at open()
 
@@ -48,8 +43,12 @@ public:
     //Override this if the vector-format has own resizing policy.
     virtual bool resize(Paint* paint, float w, float h) { return false; }
 
+    virtual bool animatable() { return false; }  //true if this loader supports animation.
+    virtual void sync() {};  //finish immediately if any async update jobs.
+
     virtual bool read() = 0;
     virtual bool close() = 0;
+
     virtual unique_ptr<Surface> bitmap() { return nullptr; }
     virtual unique_ptr<Paint> paint() { return nullptr; }
 };

+ 33 - 0
thirdparty/thorvg/src/lib/tvgLoader.cpp

@@ -38,6 +38,14 @@
     #include "tvgJpgLoader.h"
 #endif
 
+#ifdef THORVG_WEBP_LOADER_SUPPORT
+    #include "tvgWebpLoader.h"
+#endif
+
+#ifdef THORVG_LOTTIE_LOADER_SUPPORT
+    #include "tvgLottieLoader.h"
+#endif
+
 #include "tvgRawLoader.h"
 
 /************************************************************************/
@@ -56,6 +64,12 @@ static LoadModule* _find(FileType type)
         case FileType::Svg: {
 #ifdef THORVG_SVG_LOADER_SUPPORT
             return new SvgLoader;
+#endif
+            break;
+        }
+        case FileType::Lottie: {
+#ifdef THORVG_LOTTIE_LOADER_SUPPORT
+            return new LottieLoader;
 #endif
             break;
         }
@@ -72,6 +86,12 @@ static LoadModule* _find(FileType type)
         case FileType::Jpg: {
 #ifdef THORVG_JPG_LOADER_SUPPORT
             return new JpgLoader;
+#endif
+            break;
+        }
+        case FileType::Webp: {
+#ifdef THORVG_WEBP_LOADER_SUPPORT
+            return new WebpLoader;
 #endif
             break;
         }
@@ -91,6 +111,10 @@ static LoadModule* _find(FileType type)
             format = "SVG";
             break;
         }
+        case FileType::Lottie: {
+            format = "lottie(json)";
+            break;
+        }
         case FileType::Raw: {
             format = "RAW";
             break;
@@ -103,6 +127,10 @@ static LoadModule* _find(FileType type)
             format = "JPG";
             break;
         }
+        case FileType::Webp: {
+            format = "WEBP";
+            break;
+        }
         default: {
             format = "???";
             break;
@@ -119,8 +147,11 @@ static LoadModule* _findByPath(const string& path)
     auto ext = path.substr(path.find_last_of(".") + 1);
     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);
     return nullptr;
 }
 
@@ -133,9 +164,11 @@ static LoadModule* _findByType(const string& mimeType)
 
     if (mimeType == "tvg") type = FileType::Tvg;
     else if (mimeType == "svg" || mimeType == "svg+xml") type = FileType::Svg;
+    else if (mimeType == "lottie" || mimeType == "json") type = FileType::Lottie;
     else if (mimeType == "raw") type = FileType::Raw;
     else if (mimeType == "png") type = FileType::Png;
     else if (mimeType == "jpg" || mimeType == "jpeg") type = FileType::Jpg;
+    else if (mimeType == "webp") type = FileType::Webp;
     else {
         TVGLOG("LOADER", "Given mimetype is unknown = \"%s\".", mimeType.c_str());
         return nullptr;

+ 39 - 5
thirdparty/thorvg/src/lib/tvgMath.h

@@ -45,6 +45,15 @@ static inline bool mathEqual(float a, float b)
     return (fabsf(a - b) < FLT_EPSILON);
 }
 
+static inline bool mathEqual(const Matrix& a, const Matrix& b)
+{
+    if (!mathEqual(a.e11, b.e11) || !mathEqual(a.e12, b.e12) || !mathEqual(a.e13, b.e13) ||
+        !mathEqual(a.e21, b.e21) || !mathEqual(a.e22, b.e22) || !mathEqual(a.e23, b.e23) ||
+        !mathEqual(a.e31, b.e31) || !mathEqual(a.e32, b.e32) || !mathEqual(a.e33, b.e33)) {
+       return false;
+    }
+    return true;
+}
 
 static inline bool mathRightAngle(const Matrix* m)
 {
@@ -109,17 +118,17 @@ static inline void mathIdentity(Matrix* m)
 }
 
 
-static inline void mathScale(Matrix* m, float scale)
+static inline void mathScale(Matrix* m, float sx, float sy)
 {
-    m->e11 = scale;
-    m->e22 = scale;
+    m->e11 *= sx;
+    m->e22 *= sy;
 }
 
 
 static inline void mathTranslate(Matrix* m, float x, float y)
 {
-    m->e13 = x;
-    m->e23 = y;
+    m->e13 += x;
+    m->e23 += y;
 }
 
 
@@ -165,4 +174,29 @@ static inline Matrix mathMultiply(const Matrix* lhs, const Matrix* rhs)
 }
 
 
+static inline Point operator-(const Point& lhs, const Point& rhs)
+{
+    return {lhs.x - rhs.x, lhs.y - rhs.y};
+}
+
+
+static inline Point operator+(const Point& lhs, const Point& rhs)
+{
+    return {lhs.x + rhs.x, lhs.y + rhs.y};
+}
+
+
+static inline Point operator*(const Point& lhs, float rhs)
+{
+    return {lhs.x * rhs, lhs.y * rhs};
+}
+
+
+template <typename T>
+static inline T mathLerp(const T &start, const T &end, float t)
+{
+    return static_cast<T>(start + (end - start) * t);
+}
+
+
 #endif //_TVG_MATH_H_

+ 29 - 6
thirdparty/thorvg/src/lib/tvgPaint.cpp

@@ -166,6 +166,7 @@ bool Paint::Impl::render(RenderMethod& renderer)
         Create a composition image. */
     if (compData && compData->method != CompositeMethod::ClipPath && !(compData->target->pImpl->ctxFlag & ContextFlag::FastTrack)) {
         auto region = smethod->bounds(renderer);
+        if (MASK_OPERATION(compData->method)) region.add(compData->target->pImpl->smethod->bounds(renderer));
         if (region.w == 0 || region.h == 0) return true;
         cmp = renderer.target(region, COMPOSITE_TO_COLORSPACE(renderer, compData->method));
         if (renderer.beginComposite(cmp, CompositeMethod::None, 255)) {
@@ -175,6 +176,7 @@ bool Paint::Impl::render(RenderMethod& renderer)
 
     if (cmp) renderer.beginComposite(cmp, compData->method, compData->target->pImpl->opacity);
 
+    renderer.blend(blendMethod);
     auto ret = smethod->render(renderer);
 
     if (cmp) renderer.endComposite(cmp);
@@ -183,7 +185,7 @@ bool Paint::Impl::render(RenderMethod& renderer)
 }
 
 
-RenderData Paint::Impl::update(RenderMethod& renderer, const RenderTransform* pTransform, uint32_t opacity, Array<RenderData>& clips, uint32_t pFlag, bool clipper)
+RenderData Paint::Impl::update(RenderMethod& renderer, const RenderTransform* pTransform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper)
 {
     if (renderFlag & RenderUpdateFlag::Transform) {
         if (!rTransform) return nullptr;
@@ -209,11 +211,18 @@ RenderData Paint::Impl::update(RenderMethod& renderer, const RenderTransform* pT
         auto tryFastTrack = false;
         if (target->identifier() == TVG_CLASS_ID_SHAPE) {
             if (method == CompositeMethod::ClipPath) tryFastTrack = true;
+            //OPTIMIZE HERE: Actually, this condition AlphaMask is useless. We can skip it?
             else if (method == CompositeMethod::AlphaMask) {
                 auto shape = static_cast<Shape*>(target);
                 uint8_t a;
                 shape->fillColor(nullptr, nullptr, nullptr, &a);
                 if (a == 255 && shape->opacity() == 255 && !shape->fill()) tryFastTrack = true;
+            //OPTIMIZE HERE: Actually, this condition InvAlphaMask is useless. We can skip it?
+            } else if (method == CompositeMethod::InvAlphaMask) {
+                auto shape = static_cast<Shape*>(target);
+                uint8_t a;
+                shape->fillColor(nullptr, nullptr, nullptr, &a);
+                if ((a == 0 || shape->opacity() == 0) && !shape->fill()) tryFastTrack = true;
             }
             if (tryFastTrack) {
                 RenderRegion viewport2;
@@ -227,7 +236,7 @@ RenderData Paint::Impl::update(RenderMethod& renderer, const RenderTransform* pT
         }
         if (!compFastTrack) {
             childClipper = compData->method == CompositeMethod::ClipPath ? true : false;
-            trd = target->pImpl->update(renderer, pTransform, 255, clips, pFlag, childClipper);
+            trd = target->pImpl->update(renderer, pTransform, clips, 255, pFlag, childClipper);
             if (childClipper) clips.push(trd);
         }
     }
@@ -236,14 +245,14 @@ RenderData Paint::Impl::update(RenderMethod& renderer, const RenderTransform* pT
     RenderData rd = nullptr;
     auto newFlag = static_cast<RenderUpdateFlag>(pFlag | renderFlag);
     renderFlag = RenderUpdateFlag::None;
-    opacity = (opacity * this->opacity) / 255;
+    opacity = MULTIPLY(opacity, this->opacity);
 
     if (rTransform && pTransform) {
         RenderTransform outTransform(pTransform, rTransform);
-        rd = smethod->update(renderer, &outTransform, opacity, clips, newFlag, clipper);
+        rd = smethod->update(renderer, &outTransform, clips, opacity, newFlag, clipper);
     } else {
         auto outTransform = pTransform ? pTransform : rTransform;
-        rd = smethod->update(renderer, outTransform, opacity, clips, newFlag, clipper);
+        rd = smethod->update(renderer, outTransform, clips, opacity, newFlag, clipper);
     }
 
     /* 3. Composition Post Processing */
@@ -371,7 +380,7 @@ Result Paint::composite(std::unique_ptr<Paint> target, CompositeMethod method) n
 {
     auto p = target.release();
     if (pImpl->composite(this, p, method)) return Result::Success;
-    if (p) delete(p);
+    delete(p);
     return Result::InvalidArguments;
 }
 
@@ -409,3 +418,17 @@ uint32_t Paint::identifier() const noexcept
 {
     return pImpl->id;
 }
+
+
+Result Paint::blend(BlendMethod method) const noexcept
+{
+    pImpl->blendMethod = method;
+
+    return Result::Success;
+}
+
+
+BlendMethod Paint::blend() const noexcept
+{
+    return pImpl->blendMethod;
+}

+ 11 - 10
thirdparty/thorvg/src/lib/tvgPaint.h

@@ -28,7 +28,7 @@
 
 namespace tvg
 {
-    enum ContextFlag {Invalid = 0, FastTrack = 1};
+    enum ContextFlag : uint8_t {Invalid = 0, FastTrack = 1};
 
     struct Iterator
     {
@@ -43,7 +43,7 @@ namespace tvg
         virtual ~StrategyMethod() {}
 
         virtual bool dispose(RenderMethod& renderer) = 0;
-        virtual void* update(RenderMethod& renderer, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag pFlag, bool clipper) = 0;   //Return engine data if it has.
+        virtual void* update(RenderMethod& renderer, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper) = 0;   //Return engine data if it has.
         virtual bool render(RenderMethod& renderer) = 0;
         virtual bool bounds(float* x, float* y, float* w, float* h) = 0;
         virtual RenderRegion bounds(RenderMethod& renderer) const = 0;
@@ -63,9 +63,10 @@ namespace tvg
         StrategyMethod* smethod = nullptr;
         RenderTransform* rTransform = nullptr;
         Composite* compData = nullptr;
-        uint32_t renderFlag = RenderUpdateFlag::None;
-        uint32_t ctxFlag = ContextFlag::Invalid;
-        uint32_t id;
+        BlendMethod blendMethod = BlendMethod::Normal;              //uint8_t
+        uint8_t renderFlag = RenderUpdateFlag::None;
+        uint8_t ctxFlag = ContextFlag::Invalid;
+        uint8_t id;
         uint8_t opacity = 255;
 
         ~Impl()
@@ -74,8 +75,8 @@ namespace tvg
                 delete(compData->target);
                 free(compData);
             }
-            if (smethod) delete(smethod);
-            if (rTransform) delete(rTransform);
+            delete(smethod);
+            delete(rTransform);
         }
 
         void method(StrategyMethod* method)
@@ -147,7 +148,7 @@ namespace tvg
         bool scale(float factor);
         bool translate(float x, float y);
         bool bounds(float* x, float* y, float* w, float* h, bool transformed);
-        RenderData update(RenderMethod& renderer, const RenderTransform* pTransform, uint32_t opacity, Array<RenderData>& clips, uint32_t pFlag, bool clipper = false);
+        RenderData update(RenderMethod& renderer, const RenderTransform* pTransform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper = false);
         bool render(RenderMethod& renderer);
         Paint* duplicate();
     };
@@ -176,9 +177,9 @@ namespace tvg
             return inst->dispose(renderer);
         }
 
-        RenderData update(RenderMethod& renderer, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag renderFlag, bool clipper) override
+        RenderData update(RenderMethod& renderer, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag renderFlag, bool clipper) override
         {
-            return inst->update(renderer, transform, opacity, clips, renderFlag, clipper);
+            return inst->update(renderer, transform, clips, opacity, renderFlag, clipper);
         }
 
         bool render(RenderMethod& renderer) override

+ 1 - 8
thirdparty/thorvg/src/lib/tvgPicture.cpp

@@ -26,7 +26,7 @@
 /* External Class Implementation                                        */
 /************************************************************************/
 
-Picture::Picture() : pImpl(new Impl)
+Picture::Picture() : pImpl(new Impl(this))
 {
     Paint::pImpl->id = TVG_CLASS_ID_PICTURE;
     Paint::pImpl->method(new PaintMethod<Picture::Impl>(pImpl));
@@ -81,13 +81,6 @@ Result Picture::load(uint32_t* data, uint32_t w, uint32_t h, bool copy) noexcept
 }
 
 
-Result Picture::viewbox(float* x, float* y, float* w, float* h) const noexcept
-{
-    if (pImpl->viewbox(x, y, w, h)) return Result::Success;
-    return Result::InsufficientCondition;
-}
-
-
 Result Picture::size(float w, float h) noexcept
 {
     if (pImpl->size(w, h)) return Result::Success;

+ 40 - 18
thirdparty/thorvg/src/lib/tvgPictureImpl.h

@@ -67,11 +67,18 @@ struct Picture::Impl
     RenderData rd = nullptr;          //engine data
     float w = 0, h = 0;
     RenderMesh rm;                    //mesh data
+    Picture* picture = nullptr;
     bool resizing = false;
+    bool needComp = false;            //need composition
+    bool animated = false;            //picture is belonged to Animation
+
+    Impl(Picture* p) : picture(p)
+    {
+    }
 
     ~Impl()
     {
-        if (paint) delete(paint);
+        delete(paint);
         delete(surface);
     }
 
@@ -85,7 +92,7 @@ struct Picture::Impl
         return ret;
     }
 
-    uint32_t load()
+    RenderUpdateFlag load()
     {
         if (loader) {
             if (!paint) {
@@ -102,7 +109,8 @@ struct Picture::Impl
                     }
                     if (paint) return RenderUpdateFlag::None;
                 }
-            }
+            } else loader->sync();
+
             if (!surface) {
                 if ((surface = loader->bitmap().release())) {
                     loader->close();
@@ -127,38 +135,52 @@ struct Picture::Impl
         else return RenderTransform(pTransform, &tmp);
     }
 
-    RenderData update(RenderMethod &renderer, const RenderTransform* pTransform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag pFlag, bool clipper)
+    bool needComposition(uint8_t opacity)
+    {
+        //In this case, paint(scene) would try composition itself.
+        if (opacity < 255) return false;
+
+        //Composition test
+        const Paint* target;
+        auto method = picture->composite(&target);
+        if (!target || method == tvg::CompositeMethod::ClipPath) return false;
+        if (target->pImpl->opacity == 255 || target->pImpl->opacity == 0) return false;
+
+        return true;
+    }
+
+    RenderData update(RenderMethod &renderer, const RenderTransform* pTransform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper)
     {
         auto flag = load();
 
         if (surface) {
             auto transform = resizeTransform(pTransform);
-            rd = renderer.prepare(surface, &rm, rd, &transform, opacity, clips, static_cast<RenderUpdateFlag>(pFlag | flag));
+            rd = renderer.prepare(surface, &rm, rd, &transform, clips, opacity, static_cast<RenderUpdateFlag>(pFlag | flag));
         } else if (paint) {
             if (resizing) {
                 loader->resize(paint, w, h);
                 resizing = false;
             }
-            rd = paint->pImpl->update(renderer, pTransform, opacity, clips, static_cast<RenderUpdateFlag>(pFlag | flag), clipper);
+            needComp = needComposition(opacity) ? true : false;
+            rd = paint->pImpl->update(renderer, pTransform, clips, opacity, static_cast<RenderUpdateFlag>(pFlag | flag), clipper);
         }
         return rd;
     }
 
     bool render(RenderMethod &renderer)
     {
+        bool ret = false;
         if (surface) return renderer.renderImage(rd);
-        else if (paint) return paint->pImpl->render(renderer);
-        return false;
-    }
-
-    bool viewbox(float* x, float* y, float* w, float* h)
-    {
-        if (!loader) return false;
-        if (x) *x = loader->vx;
-        if (y) *y = loader->vy;
-        if (w) *w = loader->vw;
-        if (h) *h = loader->vh;
-        return true;
+        else if (paint) {
+            Compositor* cmp = nullptr;
+            if (needComp) {
+                cmp = renderer.target(bounds(renderer), renderer.colorSpace());
+                renderer.beginComposite(cmp, CompositeMethod::None, 255);
+            }
+            ret = paint->pImpl->render(renderer);
+            if (cmp) renderer.endComposite(cmp);
+        }
+        return ret;
     }
 
     bool size(float w, float h)

+ 1 - 1
thirdparty/thorvg/src/lib/tvgRender.cpp

@@ -53,7 +53,7 @@ bool RenderTransform::update()
 
     mathIdentity(&m);
 
-    mathScale(&m, scale);
+    mathScale(&m, scale, scale);
 
     if (!mathZero(degree)) mathRotate(&m, degree);
 

+ 64 - 19
thirdparty/thorvg/src/lib/tvgRender.h

@@ -32,7 +32,7 @@ namespace tvg
 using RenderData = void*;
 using pixel_t = uint32_t;
 
-enum RenderUpdateFlag {None = 0, Path = 1, Color = 2, Gradient = 4, Stroke = 8, Transform = 16, Image = 32, GradientStroke = 64, All = 255};
+enum RenderUpdateFlag : uint8_t {None = 0, Path = 1, Color = 2, Gradient = 4, Stroke = 8, Transform = 16, Image = 32, GradientStroke = 64, All = 255};
 
 struct Surface;
 
@@ -65,7 +65,7 @@ struct Surface
 struct Compositor
 {
     CompositeMethod method;
-    uint32_t        opacity;
+    uint8_t        opacity;
 };
 
 struct RenderMesh
@@ -98,6 +98,20 @@ struct RenderRegion
         if (w < 0) w = 0;
         if (h < 0) h = 0;
     }
+
+    void add(const RenderRegion& rhs)
+    {
+        if (rhs.x < x) {
+            w += (x - rhs.x);
+            x = rhs.x;
+        }
+        if (rhs.y < y) {
+            h += (y - rhs.y);
+            y = rhs.y;
+        }
+        if (rhs.x + rhs.w > x + w) w = (rhs.x + rhs.w) - x;
+        if (rhs.y + rhs.h > y + h) h = (rhs.y + rhs.h) - y;
+    }
 };
 
 struct RenderTransform
@@ -125,12 +139,13 @@ struct RenderStroke
     uint32_t dashCnt = 0;
     StrokeCap cap = StrokeCap::Square;
     StrokeJoin join = StrokeJoin::Bevel;
+    float miterlimit = 4.0f;
     bool strokeFirst = false;
 
     ~RenderStroke()
     {
         free(dashPattern);
-        if (fill) delete(fill);
+        delete(fill);
     }
 };
 
@@ -138,13 +153,8 @@ struct RenderShape
 {
     struct
     {
-        PathCommand* cmds = nullptr;
-        uint32_t cmdCnt = 0;
-        uint32_t reservedCmdCnt = 0;
-
-        Point *pts = nullptr;
-        uint32_t ptsCnt = 0;
-        uint32_t reservedPtsCnt = 0;
+        Array<PathCommand> cmds;
+        Array<Point> pts;
     } path;
 
     Fill *fill = nullptr;
@@ -154,11 +164,8 @@ struct RenderShape
 
     ~RenderShape()
     {
-        free(path.cmds);
-        free(path.pts);
-
-        if (fill) delete(fill);
-        if (stroke) delete(stroke);
+        delete(fill);
+        delete(stroke);
     }
 
     void fillColor(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a) const
@@ -211,15 +218,22 @@ struct RenderShape
         if (!stroke) return StrokeJoin::Bevel;
         return stroke->join;
     }
+
+    float strokeMiterlimit() const
+    {
+        if (!stroke) return 4.0f;
+
+        return stroke->miterlimit;;
+    }
 };
 
 class RenderMethod
 {
 public:
     virtual ~RenderMethod() {}
-    virtual RenderData prepare(const RenderShape& rshape, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags, bool clipper) = 0;
-    virtual RenderData prepare(const Array<RenderData>& scene, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags) = 0;
-    virtual RenderData prepare(Surface* surface, const RenderMesh* mesh, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags) = 0;
+    virtual RenderData prepare(const RenderShape& rshape, RenderData data, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags, bool clipper) = 0;
+    virtual RenderData prepare(const Array<RenderData>& scene, RenderData data, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags) = 0;
+    virtual RenderData prepare(Surface* surface, const RenderMesh* mesh, RenderData data, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags) = 0;
     virtual bool preRender() = 0;
     virtual bool renderShape(RenderData data) = 0;
     virtual bool renderImage(RenderData data) = 0;
@@ -228,16 +242,36 @@ public:
     virtual RenderRegion region(RenderData data) = 0;
     virtual RenderRegion viewport() = 0;
     virtual bool viewport(const RenderRegion& vp) = 0;
+    virtual bool blend(BlendMethod method) = 0;
     virtual ColorSpace colorSpace() = 0;
 
     virtual bool clear() = 0;
     virtual bool sync() = 0;
 
     virtual Compositor* target(const RenderRegion& region, ColorSpace cs) = 0;
-    virtual bool beginComposite(Compositor* cmp, CompositeMethod method, uint32_t opacity) = 0;
+    virtual bool beginComposite(Compositor* cmp, CompositeMethod method, uint8_t opacity) = 0;
     virtual bool endComposite(Compositor* cmp) = 0;
 };
 
+static inline bool MASK_OPERATION(CompositeMethod method)
+{
+    switch(method) {
+        case CompositeMethod::AlphaMask:
+        case CompositeMethod::InvAlphaMask:
+        case CompositeMethod::LumaMask:
+        case CompositeMethod::InvLumaMask:
+            return false;
+        case CompositeMethod::AddMask:
+        case CompositeMethod::SubtractMask:
+        case CompositeMethod::IntersectMask:
+        case CompositeMethod::DifferenceMask:
+            return true;
+        default:
+            TVGERR("COMMON", "Unsupported Composite Size! = %d", (int)method);
+            return false;
+    }
+}
+
 static inline uint8_t CHANNEL_SIZE(ColorSpace cs)
 {
     switch(cs) {
@@ -262,6 +296,11 @@ static inline ColorSpace COMPOSITE_TO_COLORSPACE(RenderMethod& renderer, Composi
         case CompositeMethod::InvAlphaMask:
             return ColorSpace::Grayscale8;
         case CompositeMethod::LumaMask:
+        case CompositeMethod::InvLumaMask:
+        case CompositeMethod::AddMask:
+        case CompositeMethod::SubtractMask:
+        case CompositeMethod::IntersectMask:
+        case CompositeMethod::DifferenceMask:
             return renderer.colorSpace();
         default:
             TVGERR("COMMON", "Unsupported Composite Size! = %d", (int)method);
@@ -269,6 +308,12 @@ static inline ColorSpace COMPOSITE_TO_COLORSPACE(RenderMethod& renderer, Composi
     }
 }
 
+static inline uint8_t MULTIPLY(uint8_t c, uint8_t a)
+{
+    return (((c) * (a) + 0xff) >> 8);
+}
+
+
 }
 
 #endif //_TVG_RENDER_H_

+ 1 - 1
thirdparty/thorvg/src/lib/tvgSaver.cpp

@@ -36,7 +36,7 @@ struct Saver::Impl
     SaveModule* saveModule = nullptr;
     ~Impl()
     {
-        if (saveModule) delete(saveModule);
+        delete(saveModule);
     }
 };
 

+ 9 - 5
thirdparty/thorvg/src/lib/tvgScene.cpp

@@ -55,17 +55,15 @@ Result Scene::push(unique_ptr<Paint> paint) noexcept
 {
     auto p = paint.release();
     if (!p) return Result::MemoryCorruption;
-    pImpl->paints.push(p);
+    pImpl->paints.push_back(p);
 
     return Result::Success;
 }
 
 
-Result Scene::reserve(uint32_t size) noexcept
+Result Scene::reserve(TVG_UNUSED uint32_t size) noexcept
 {
-    if (!pImpl->paints.reserve(size)) return Result::FailedAllocation;
-
-    return Result::Success;
+    return Result::NonSupport;
 }
 
 
@@ -75,3 +73,9 @@ Result Scene::clear(bool free) noexcept
 
     return Result::Success;
 }
+
+
+list<Paint*>& Scene::paints() noexcept
+{
+    return pImpl->paints;
+}

+ 54 - 45
thirdparty/thorvg/src/lib/tvgSceneImpl.h

@@ -32,37 +32,41 @@
 
 struct SceneIterator : Iterator
 {
-    Array<Paint*>* paints;
-    uint32_t idx = 0;
+    list<Paint*>* paints;
+    list<Paint*>::iterator itr;
 
-    SceneIterator(Array<Paint*>* p) : paints(p)
+    SceneIterator(list<Paint*>* p) : paints(p)
     {
+        begin();
     }
 
     const Paint* next() override
     {
-        if (idx >= paints->count) return nullptr;
-        return paints->data[idx++];
+        if (itr == paints->end()) return nullptr;
+        auto paint = *itr;
+        ++itr;
+        return paint;
     }
 
     uint32_t count() override
     {
-        return paints->count;
+       return paints->size();
     }
 
     void begin() override
     {
-        idx = 0;
+        itr = paints->begin();
     }
 };
 
 struct Scene::Impl
 {
-    Array<Paint*> paints;
-    uint8_t opacity;                     //for composition
+    list<Paint*> paints;
     RenderMethod* renderer = nullptr;    //keep it for explicit clear
     RenderData rd = nullptr;
     Scene* scene = nullptr;
+    uint8_t opacity;                     //for composition
+    bool needComp;                       //composite or not
 
     Impl(Scene* s) : scene(s)
     {
@@ -70,15 +74,15 @@ struct Scene::Impl
 
     ~Impl()
     {
-        for (auto paint = paints.data; paint < (paints.data + paints.count); ++paint) {
-            delete(*paint);
+        for (auto paint : paints) {
+            delete(paint);
         }
     }
 
     bool dispose(RenderMethod& renderer)
     {
-        for (auto paint = paints.data; paint < (paints.data + paints.count); ++paint) {
-            (*paint)->pImpl->dispose(renderer);
+        for (auto paint : paints) {
+            paint->pImpl->dispose(renderer);
         }
 
         auto ret = renderer.dispose(rd);
@@ -88,43 +92,50 @@ struct Scene::Impl
         return ret;
     }
 
-    bool needComposition(uint32_t opacity)
+    bool needComposition(uint8_t opacity)
     {
-        if (opacity == 0 || paints.count == 0) return false;
+        if (opacity == 0 || paints.empty()) return false;
 
         //Masking may require composition (even if opacity == 255)
         auto compMethod = scene->composite(nullptr);
         if (compMethod != CompositeMethod::None && compMethod != CompositeMethod::ClipPath) return true;
 
+        //Blending may require composition (even if opacity == 255)
+        if (scene->blend() != BlendMethod::Normal) return true;
+
         //Half translucent requires intermediate composition.
         if (opacity == 255) return false;
 
         //If scene has several children or only scene, it may require composition.
-        if (paints.count > 1) return true;
-        if (paints.count == 1 && (*paints.data)->identifier() == TVG_CLASS_ID_SCENE) return true;
-        return false;
+        //OPTIMIZE: the bitmap type of the picture would not need the composition.
+        //OPTIMIZE: a single paint of a scene would not need the composition.
+        if (paints.size() == 1 && paints.front()->identifier() == TVG_CLASS_ID_SHAPE) return false;
+
+        return true;
     }
 
-    RenderData update(RenderMethod &renderer, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flag, bool clipper)
+    RenderData update(RenderMethod &renderer, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flag, bool clipper)
     {
-        /* Overriding opacity value. If this scene is half-translucent,
-           It must do intermeidate composition with that opacity value. */
-        this->opacity = static_cast<uint8_t>(opacity);
-        if (needComposition(opacity)) opacity = 255;
+        if ((needComp = needComposition(opacity))) {
+            /* Overriding opacity value. If this scene is half-translucent,
+               It must do intermeidate composition with that opacity value. */
+            this->opacity = opacity;
+            opacity = 255;
+        }
 
         this->renderer = &renderer;
 
         if (clipper) {
             Array<RenderData> rds;
-            rds.reserve(paints.count);
-            for (auto paint = paints.data; paint < (paints.data + paints.count); ++paint) {
-                rds.push((*paint)->pImpl->update(renderer, transform, opacity, clips, flag, true));
+            rds.reserve(paints.size());
+            for (auto paint : paints) {
+                rds.push(paint->pImpl->update(renderer, transform, clips, opacity, flag, true));
             }
-            rd = renderer.prepare(rds, rd, transform, opacity, clips, flag);
+            rd = renderer.prepare(rds, rd, transform, clips, opacity, flag);
             return rd;
         } else {
-            for (auto paint = paints.data; paint < (paints.data + paints.count); ++paint) {
-                (*paint)->pImpl->update(renderer, transform, opacity, clips, flag, false);
+            for (auto paint : paints) {
+                paint->pImpl->update(renderer, transform, clips, opacity, flag, false);
             }
             return nullptr;
         }
@@ -134,13 +145,13 @@ struct Scene::Impl
     {
         Compositor* cmp = nullptr;
 
-        if (needComposition(opacity)) {
+        if (needComp) {
             cmp = renderer.target(bounds(renderer), renderer.colorSpace());
             renderer.beginComposite(cmp, CompositeMethod::None, opacity);
         }
 
-        for (auto paint = paints.data; paint < (paints.data + paints.count); ++paint) {
-            if (!(*paint)->pImpl->render(renderer)) return false;
+        for (auto paint : paints) {
+            if (!paint->pImpl->render(renderer)) return false;
         }
 
         if (cmp) renderer.endComposite(cmp);
@@ -150,15 +161,15 @@ struct Scene::Impl
 
     RenderRegion bounds(RenderMethod& renderer) const
     {
-        if (paints.count == 0) return {0, 0, 0, 0};
+        if (paints.empty()) return {0, 0, 0, 0};
 
         int32_t x1 = INT32_MAX;
         int32_t y1 = INT32_MAX;
         int32_t x2 = 0;
         int32_t y2 = 0;
 
-        for (auto paint = paints.data; paint < (paints.data + paints.count); ++paint) {
-            auto region = (*paint)->pImpl->bounds(renderer);
+        for (auto paint : paints) {
+            auto region = paint->pImpl->bounds(renderer);
 
             //Merge regions
             if (region.x < x1) x1 = region.x;
@@ -172,20 +183,20 @@ struct Scene::Impl
 
     bool bounds(float* px, float* py, float* pw, float* ph)
     {
-        if (paints.count == 0) return false;
+        if (paints.empty()) return false;
 
         auto x1 = FLT_MAX;
         auto y1 = FLT_MAX;
         auto x2 = -FLT_MAX;
         auto y2 = -FLT_MAX;
 
-        for (auto paint = paints.data; paint < (paints.data + paints.count); ++paint) {
+        for (auto paint : paints) {
             auto x = FLT_MAX;
             auto y = FLT_MAX;
             auto w = 0.0f;
             auto h = 0.0f;
 
-            if ((*paint)->bounds(&x, &y, &w, &h, true) != tvg::Result::Success) continue;
+            if (paint->bounds(&x, &y, &w, &h, true) != tvg::Result::Success) continue;
 
             //Merge regions
             if (x < x1) x1 = x;
@@ -208,10 +219,8 @@ struct Scene::Impl
 
         auto dup = ret.get()->pImpl;
 
-        dup->paints.reserve(paints.count);
-
-        for (auto paint = paints.data; paint < (paints.data + paints.count); ++paint) {
-            dup->paints.push((*paint)->duplicate());
+        for (auto paint : paints) {
+            dup->paints.push_back(paint->duplicate());
         }
 
         return ret.release();
@@ -221,9 +230,9 @@ struct Scene::Impl
     {
         auto dispose = renderer ? true : false;
 
-        for (auto paint = paints.data; paint < (paints.data + paints.count); ++paint) {
-            if (dispose) (*paint)->pImpl->dispose(*renderer);
-            if (free) delete(*paint);
+        for (auto paint : paints) {
+            if (dispose) paint->pImpl->dispose(*renderer);
+            if (free) delete(paint);
         }
         paints.clear();
         renderer = nullptr;

+ 34 - 16
thirdparty/thorvg/src/lib/tvgShape.cpp

@@ -32,7 +32,7 @@ constexpr auto PATH_KAPPA = 0.552284f;
 /* External Class Implementation                                        */
 /************************************************************************/
 
-Shape :: Shape() : pImpl(new Impl())
+Shape :: Shape() : pImpl(new Impl(this))
 {
     Paint::pImpl->id = TVG_CLASS_ID_SHAPE;
     Paint::pImpl->method(new PaintMethod<Shape::Impl>(pImpl));
@@ -59,7 +59,10 @@ uint32_t Shape::identifier() noexcept
 
 Result Shape::reset() noexcept
 {
-    pImpl->reset();
+    pImpl->rs.path.cmds.clear();
+    pImpl->rs.path.pts.clear();
+
+    pImpl->flag = RenderUpdateFlag::Path;
 
     return Result::Success;
 }
@@ -69,9 +72,8 @@ uint32_t Shape::pathCommands(const PathCommand** cmds) const noexcept
 {
     if (!cmds) return 0;
 
-    *cmds = pImpl->rs.path.cmds;
-
-    return pImpl->rs.path.cmdCnt;
+    *cmds = pImpl->rs.path.cmds.data;
+    return pImpl->rs.path.cmds.count;
 }
 
 
@@ -79,9 +81,8 @@ uint32_t Shape::pathCoords(const Point** pts) const noexcept
 {
     if (!pts) return 0;
 
-    *pts = pImpl->rs.path.pts;
-
-    return pImpl->rs.path.ptsCnt;
+    *pts = pImpl->rs.path.pts.data;
+    return pImpl->rs.path.pts.count;
 }
 
 
@@ -242,21 +243,22 @@ Result Shape::appendRect(float x, float y, float w, float h, float rx, float ry)
 }
 
 
-//TODO: kill alpha at TVG 1.0, because we also have opacity
 Result Shape::fill(uint8_t r, uint8_t g, uint8_t b, uint8_t a) noexcept
 {
-    pImpl->rs.color[0] = r;
-    pImpl->rs.color[1] = g;
-    pImpl->rs.color[2] = b;
-    pImpl->rs.color[3] = a;
-    pImpl->flag |= RenderUpdateFlag::Color;
-
     if (pImpl->rs.fill) {
         delete(pImpl->rs.fill);
         pImpl->rs.fill = nullptr;
         pImpl->flag |= RenderUpdateFlag::Gradient;
     }
 
+    if (r == pImpl->rs.color[0] && g == pImpl->rs.color[1] && b == pImpl->rs.color[2] && a == pImpl->rs.color[3]) return Result::Success;
+
+    pImpl->rs.color[0] = r;
+    pImpl->rs.color[1] = g;
+    pImpl->rs.color[2] = b;
+    pImpl->rs.color[3] = a;
+    pImpl->flag |= RenderUpdateFlag::Color;
+
     return Result::Success;
 }
 
@@ -328,7 +330,7 @@ Result Shape::strokeColor(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a) const
 
 Result Shape::stroke(unique_ptr<Fill> f) noexcept
 {
-    return pImpl->strokeFill(move(f));
+    return pImpl->strokeFill(std::move(f));
 }
 
 
@@ -374,6 +376,17 @@ Result Shape::stroke(StrokeJoin join) noexcept
     return Result::Success;
 }
 
+Result Shape::strokeMiterlimit(float miterlimit) noexcept
+{
+    // https://www.w3.org/TR/SVG2/painting.html#LineJoin
+    // - A negative value for stroke-miterlimit must be treated as an illegal value.
+    if (miterlimit < 0.0f) return Result::NonSupport;
+    // TODO Find out a reasonable max value.
+    if (!pImpl->strokeMiterlimit(miterlimit)) return Result::FailedAllocation;
+
+    return Result::Success;
+}
+
 
 StrokeCap Shape::strokeCap() const noexcept
 {
@@ -386,6 +399,11 @@ StrokeJoin Shape::strokeJoin() const noexcept
     return pImpl->rs.strokeJoin();
 }
 
+float Shape::strokeMiterlimit() const noexcept
+{
+    return pImpl->rs.strokeMiterlimit();
+}
+
 
 Result Shape::fill(FillRule r) noexcept
 {

+ 102 - 66
thirdparty/thorvg/src/lib/tvgShapeImpl.h

@@ -24,6 +24,7 @@
 #define _TVG_SHAPE_IMPL_H_
 
 #include <memory.h>
+#include "tvgMath.h"
 #include "tvgPaint.h"
 
 /************************************************************************/
@@ -34,7 +35,14 @@ struct Shape::Impl
 {
     RenderShape rs;                     //shape data
     RenderData rd = nullptr;            //engine data
-    uint32_t flag = RenderUpdateFlag::None;
+    Shape* shape;
+    uint8_t flag = RenderUpdateFlag::None;
+    uint8_t opacity;                    //for composition
+    bool needComp;                      //composite or not
+
+    Impl(Shape* s) : shape(s)
+    {
+    }
 
     bool dispose(RenderMethod& renderer)
     {
@@ -45,12 +53,48 @@ struct Shape::Impl
 
     bool render(RenderMethod& renderer)
     {
-        return renderer.renderShape(rd);
+        Compositor* cmp = nullptr;
+        bool ret;
+
+        if (needComp) {
+            cmp = renderer.target(bounds(renderer), renderer.colorSpace());
+            renderer.beginComposite(cmp, CompositeMethod::None, opacity);
+        }
+        ret = renderer.renderShape(rd);
+        if (cmp) renderer.endComposite(cmp);
+        return ret;
     }
 
-    RenderData update(RenderMethod& renderer, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag pFlag, bool clipper)
+    bool needComposition(uint8_t opacity)
     {
-        rd = renderer.prepare(rs, rd, transform, opacity, clips, static_cast<RenderUpdateFlag>(pFlag | flag), clipper);
+        if (opacity == 0) return false;
+
+        //Shape composition is only necessary when stroking & fill are valid.
+        if (!rs.stroke || rs.stroke->width < FLT_EPSILON || rs.stroke->color[3] == 0) return false;
+        if (!rs.fill && rs.color[3] == 0) return false;
+
+        //translucent fill & stroke
+        if (opacity < 255) return true;
+
+        //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;
+
+        return true;
+    }
+
+    RenderData update(RenderMethod& renderer, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper)
+    {     
+        if ((needComp = needComposition(opacity))) {
+            /* Overriding opacity value. If this scene is half-translucent,
+               It must do intermeidate composition with that opacity value. */ 
+            this->opacity = opacity;
+            opacity = 255;
+        }
+
+        rd = renderer.prepare(rs, rd, transform, clips, opacity, static_cast<RenderUpdateFlag>(pFlag | flag), clipper);
         flag = RenderUpdateFlag::None;
         return rd;
     }
@@ -63,15 +107,16 @@ struct Shape::Impl
     bool bounds(float* x, float* y, float* w, float* h)
     {
         //Path bounding size
-        if (rs.path.ptsCnt > 0 ) {
-            Point min = { rs.path.pts[0].x, rs.path.pts[0].y };
-            Point max = { rs.path.pts[0].x, rs.path.pts[0].y };
-
-            for (uint32_t i = 1; i < rs.path.ptsCnt; ++i) {
-                if (rs.path.pts[i].x < min.x) min.x = rs.path.pts[i].x;
-                if (rs.path.pts[i].y < min.y) min.y = rs.path.pts[i].y;
-                if (rs.path.pts[i].x > max.x) max.x = rs.path.pts[i].x;
-                if (rs.path.pts[i].y > max.y) max.y = rs.path.pts[i].y;
+        if (rs.path.pts.count > 0 ) {
+            auto pts = rs.path.pts.data;
+            Point min = { pts->x, pts->y };
+            Point max = { pts->x, pts->y };
+
+            for (auto pts2 = pts + 1; pts2 < rs.path.pts.end(); ++pts2) {
+                if (pts2->x < min.x) min.x = pts2->x;
+                if (pts2->y < min.y) min.y = pts2->y;
+                if (pts2->x > max.x) max.x = pts2->x;
+                if (pts2->y > max.y) max.y = pts2->y;
             }
 
             if (x) *x = min.x;
@@ -87,88 +132,67 @@ struct Shape::Impl
             if (w) *w += rs.stroke->width;
             if (h) *h += rs.stroke->width;
         }
-        return rs.path.ptsCnt > 0 ? true : false;
+        return rs.path.pts.count > 0 ? true : false;
     }
 
     void reserveCmd(uint32_t cmdCnt)
     {
-        if (cmdCnt <= rs.path.reservedCmdCnt) return;
-        rs.path.reservedCmdCnt = cmdCnt;
-        rs.path.cmds = static_cast<PathCommand*>(realloc(rs.path.cmds, sizeof(PathCommand) * rs.path.reservedCmdCnt));
+        rs.path.cmds.reserve(cmdCnt);
     }
 
     void reservePts(uint32_t ptsCnt)
     {
-        if (ptsCnt <= rs.path.reservedPtsCnt) return;
-        rs.path.reservedPtsCnt = ptsCnt;
-        rs.path.pts = static_cast<Point*>(realloc(rs.path.pts, sizeof(Point) * rs.path.reservedPtsCnt));
+        rs.path.pts.reserve(ptsCnt);
     }
 
     void grow(uint32_t cmdCnt, uint32_t ptsCnt)
     {
-        reserveCmd(rs.path.cmdCnt + cmdCnt);
-        reservePts(rs.path.ptsCnt + ptsCnt);
-    }
-
-    void reset()
-    {
-        rs.path.cmdCnt = 0;
-        rs.path.ptsCnt = 0;
-
-        flag = RenderUpdateFlag::Path;
+        rs.path.cmds.grow(cmdCnt);
+        rs.path.pts.grow(ptsCnt);
     }
 
     void append(const PathCommand* cmds, uint32_t cmdCnt, const Point* pts, uint32_t ptsCnt)
     {
-        memcpy(rs.path.cmds + rs.path.cmdCnt, cmds, sizeof(PathCommand) * cmdCnt);
-        memcpy(rs.path.pts + rs.path.ptsCnt, pts, sizeof(Point) * ptsCnt);
-        rs.path.cmdCnt += cmdCnt;
-        rs.path.ptsCnt += ptsCnt;
+        memcpy(rs.path.cmds.end(), cmds, sizeof(PathCommand) * cmdCnt);
+        memcpy(rs.path.pts.end(), pts, sizeof(Point) * ptsCnt);
+        rs.path.cmds.count += cmdCnt;
+        rs.path.pts.count += ptsCnt;
 
         flag |= RenderUpdateFlag::Path;
     }
 
     void moveTo(float x, float y)
     {
-        if (rs.path.cmdCnt + 1 > rs.path.reservedCmdCnt) reserveCmd((rs.path.cmdCnt + 1) * 2);
-        if (rs.path.ptsCnt + 2 > rs.path.reservedPtsCnt) reservePts((rs.path.ptsCnt + 2) * 2);
-
-        rs.path.cmds[rs.path.cmdCnt++] = PathCommand::MoveTo;
-        rs.path.pts[rs.path.ptsCnt++] = {x, y};
+        rs.path.cmds.push(PathCommand::MoveTo);
+        rs.path.pts.push({x, y});
 
         flag |= RenderUpdateFlag::Path;
     }
 
     void lineTo(float x, float y)
     {
-        if (rs.path.cmdCnt + 1 > rs.path.reservedCmdCnt) reserveCmd((rs.path.cmdCnt + 1) * 2);
-        if (rs.path.ptsCnt + 2 > rs.path.reservedPtsCnt) reservePts((rs.path.ptsCnt + 2) * 2);
-
-        rs.path.cmds[rs.path.cmdCnt++] = PathCommand::LineTo;
-        rs.path.pts[rs.path.ptsCnt++] = {x, y};
+        rs.path.cmds.push(PathCommand::LineTo);
+        rs.path.pts.push({x, y});
 
         flag |= RenderUpdateFlag::Path;
     }
 
     void cubicTo(float cx1, float cy1, float cx2, float cy2, float x, float y)
     {
-        if (rs.path.cmdCnt + 1 > rs.path.reservedCmdCnt) reserveCmd((rs.path.cmdCnt + 1) * 2);
-        if (rs.path.ptsCnt + 3 > rs.path.reservedPtsCnt) reservePts((rs.path.ptsCnt + 3) * 2);
-
-        rs.path.cmds[rs.path.cmdCnt++] = PathCommand::CubicTo;
-        rs.path.pts[rs.path.ptsCnt++] = {cx1, cy1};
-        rs.path.pts[rs.path.ptsCnt++] = {cx2, cy2};
-        rs.path.pts[rs.path.ptsCnt++] = {x, y};
+        rs.path.cmds.push(PathCommand::CubicTo);
+        rs.path.pts.push({cx1, cy1});
+        rs.path.pts.push({cx2, cy2});
+        rs.path.pts.push({x, y});
 
         flag |= RenderUpdateFlag::Path;
     }
 
     void close()
     {
-        if (rs.path.cmdCnt > 0 && rs.path.cmds[rs.path.cmdCnt - 1] == PathCommand::Close) return;
+        //Don't close multiple times.
+        if (rs.path.cmds.count > 0 && rs.path.cmds.last() == PathCommand::Close) return;
 
-        if (rs.path.cmdCnt + 1 > rs.path.reservedCmdCnt) reserveCmd((rs.path.cmdCnt + 1) * 2);
-        rs.path.cmds[rs.path.cmdCnt++] = PathCommand::Close;
+        rs.path.cmds.push(PathCommand::Close);
 
         flag |= RenderUpdateFlag::Path;
     }
@@ -202,6 +226,15 @@ struct Shape::Impl
         return true;
     }
 
+    bool strokeMiterlimit(float miterlimit)
+    {
+        if (!rs.stroke) rs.stroke = new RenderStroke();
+        rs.stroke->miterlimit = miterlimit;
+        flag |= RenderUpdateFlag::Stroke;
+
+        return true;
+    }
+
     bool strokeColor(uint8_t r, uint8_t g, uint8_t b, uint8_t a)
     {
         if (!rs.stroke) rs.stroke = new RenderStroke();
@@ -262,6 +295,12 @@ struct Shape::Impl
         return true;
     }
 
+    bool strokeFirst()
+    {
+        if (!rs.stroke) return true;
+        return rs.stroke->strokeFirst;
+    }
+
     bool strokeFirst(bool strokeFirst)
     {
         if (!rs.stroke) rs.stroke = new RenderStroke();
@@ -271,6 +310,11 @@ struct Shape::Impl
         return true;
     }
 
+    void update(RenderUpdateFlag flag)
+    {
+        this->flag |= flag;
+    }
+
     Paint* duplicate()
     {
         auto ret = Shape::gen();
@@ -283,19 +327,11 @@ struct Shape::Impl
         dup->flag = RenderUpdateFlag::Color;
 
         //Path
-        if (rs.path.cmdCnt > 0 && rs.path.ptsCnt > 0) {
-            dup->rs.path.cmdCnt = rs.path.cmdCnt;
-            dup->rs.path.reservedCmdCnt = rs.path.reservedCmdCnt;
-            dup->rs.path.ptsCnt = rs.path.ptsCnt;
-            dup->rs.path.reservedPtsCnt = rs.path.reservedPtsCnt;
-
-            dup->rs.path.cmds = static_cast<PathCommand*>(malloc(sizeof(PathCommand) * dup->rs.path.reservedCmdCnt));
-            if (dup->rs.path.cmds) memcpy(dup->rs.path.cmds, rs.path.cmds, sizeof(PathCommand) * dup->rs.path.cmdCnt);
-
-            dup->rs.path.pts = static_cast<Point*>(malloc(sizeof(Point) * dup->rs.path.reservedPtsCnt));
-            if (dup->rs.path.pts) memcpy(dup->rs.path.pts, rs.path.pts, sizeof(Point) * dup->rs.path.ptsCnt);
+        if (rs.path.cmds.count > 0 && rs.path.pts.count > 0) {
+            dup->rs.path.cmds = rs.path.cmds;
+            dup->rs.path.pts = rs.path.pts;
+            dup->flag |= RenderUpdateFlag::Path;
         }
-        dup->flag |= RenderUpdateFlag::Path;
 
         //Stroke
         if (rs.stroke) {

+ 1 - 1
thirdparty/thorvg/src/lib/tvgSwCanvas.cpp

@@ -67,7 +67,7 @@ Result SwCanvas::mempool(MempoolPolicy policy) noexcept
     if (!renderer) return Result::MemoryCorruption;
 
     //It can't change the policy during the running.
-    if (Canvas::pImpl->paints.count > 0) return Result::InsufficientCondition;
+    if (!Canvas::pImpl->paints.empty()) return Result::InsufficientCondition;
 
     if (policy == MempoolPolicy::Individual) renderer->mempool(false);
     else renderer->mempool(true);

+ 1 - 0
thirdparty/thorvg/src/lib/tvgTaskScheduler.h

@@ -85,3 +85,4 @@ private:
 }
 
 #endif //_TVG_TASK_SCHEDULER_H_
+ 

+ 0 - 120
thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.cpp

@@ -1,120 +0,0 @@
-/*
- * Copyright (c) 2020 - 2023 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.
- */
-
-#include "tvgLoader.h"
-#include "tvgPngLoader.h"
-
-/************************************************************************/
-/* Internal Class Implementation                                        */
-/************************************************************************/
-
-
-/************************************************************************/
-/* External Class Implementation                                        */
-/************************************************************************/
-
-PngLoader::PngLoader()
-{
-    image = static_cast<png_imagep>(calloc(1, sizeof(png_image)));
-    image->version = PNG_IMAGE_VERSION;
-    image->opaque = NULL;
-}
-
-PngLoader::~PngLoader()
-{
-    if (content) {
-        free((void*)content);
-        content = nullptr;
-    }
-    free(image);
-}
-
-bool PngLoader::open(const string& path)
-{
-    image->opaque = NULL;
-
-    if (!png_image_begin_read_from_file(image, path.c_str())) return false;
-
-    w = (float)image->width;
-    h = (float)image->height;
-    cs = ColorSpace::ARGB8888;
-
-    return true;
-}
-
-bool PngLoader::open(const char* data, uint32_t size, bool copy)
-{
-    image->opaque = NULL;
-
-    if (!png_image_begin_read_from_memory(image, data, size)) return false;
-
-    w = (float)image->width;
-    h = (float)image->height;
-    cs = ColorSpace::ARGB8888;
-
-    return true;
-}
-
-
-bool PngLoader::read()
-{
-    png_bytep buffer;
-    image->format = PNG_FORMAT_BGRA;
-    buffer = static_cast<png_bytep>(malloc(PNG_IMAGE_SIZE((*image))));
-    if (!buffer) {
-        //out of memory, only time when libpng doesnt free its data
-        png_image_free(image);
-        return false;
-    }
-    if (!png_image_finish_read(image, NULL, buffer, 0, NULL)) {
-        free(buffer);
-        return false;
-    }
-    content = reinterpret_cast<uint32_t*>(buffer);
-
-    return true;
-}
-
-bool PngLoader::close()
-{
-    png_image_free(image);
-    return true;
-}
-
-unique_ptr<Surface> PngLoader::bitmap()
-{
-    if (!content) return nullptr;
-
-    //TODO: It's better to keep this surface instance in the loader side
-    auto surface = new Surface;
-    surface->buf32 = content;
-    surface->stride = w;
-    surface->w = w;
-    surface->h = h;
-    surface->cs = cs;
-    surface->channelSize = sizeof(uint32_t);
-    surface->owner = true;
-    surface->premultiplied = false;
-
-    return unique_ptr<Surface>(surface);
-}
-

+ 0 - 143
thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.cpp

@@ -1,143 +0,0 @@
-/*
- * Copyright (c) 2021 - 2023 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.
- */
-
-#include <memory.h>
-#include "tvgLoader.h"
-#include "tvgJpgLoader.h"
-
-/************************************************************************/
-/* Internal Class Implementation                                        */
-/************************************************************************/
-
-void JpgLoader::clear()
-{
-    jpgdDelete(decoder);
-    if (freeData) free(data);
-    decoder = nullptr;
-    data = nullptr;
-    freeData = false;
-}
-
-
-/************************************************************************/
-/* External Class Implementation                                        */
-/************************************************************************/
-
-
-JpgLoader::~JpgLoader()
-{
-    jpgdDelete(decoder);
-    if (freeData) free(data);
-    free(image);
-}
-
-
-bool JpgLoader::open(const string& path)
-{
-    clear();
-
-    int width, height;
-    decoder = jpgdHeader(path.c_str(), &width, &height);
-    if (!decoder) return false;
-
-    w = static_cast<float>(width);
-    h = static_cast<float>(height);
-    cs = ColorSpace::ARGB8888;
-
-    return true;
-}
-
-
-bool JpgLoader::open(const char* data, uint32_t size, bool copy)
-{
-    clear();
-
-    if (copy) {
-        this->data = (char *) malloc(size);
-        if (!this->data) return false;
-        memcpy((char *)this->data, data, size);
-        freeData = true;
-    } else {
-        this->data = (char *) data;
-        freeData = false;
-    }
-
-    int width, height;
-    decoder = jpgdHeader(this->data, size, &width, &height);
-    if (!decoder) return false;
-
-    w = static_cast<float>(width);
-    h = static_cast<float>(height);
-    cs = ColorSpace::ARGB8888;
-
-    return true;
-}
-
-
-
-bool JpgLoader::read()
-{
-    if (!decoder || w <= 0 || h <= 0) return false;
-
-    TaskScheduler::request(this);
-
-    return true;
-}
-
-
-bool JpgLoader::close()
-{
-    this->done();
-    clear();
-    return true;
-}
-
-
-unique_ptr<Surface> JpgLoader::bitmap()
-{
-    this->done();
-
-    if (!image) return nullptr;
-
-    //TODO: It's better to keep this surface instance in the loader side
-    auto surface = new Surface;
-    surface->buf8 = image;
-    surface->stride = static_cast<uint32_t>(w);
-    surface->w = static_cast<uint32_t>(w);
-    surface->h = static_cast<uint32_t>(h);
-    surface->cs = cs;
-    surface->channelSize = sizeof(uint32_t);
-    surface->premultiplied = true;
-    surface->owner = true;
-
-    return unique_ptr<Surface>(surface);
-}
-
-
-void JpgLoader::run(unsigned tid)
-{
-    if (image) {
-        free(image);
-        image = nullptr;
-    }
-    image = jpgdDecompress(decoder);
-}

+ 0 - 52
thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.h

@@ -1,52 +0,0 @@
-/*
- * Copyright (c) 2021 - 2023 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.
- */
-
-#ifndef _TVG_JPG_LOADER_H_
-#define _TVG_JPG_LOADER_H_
-
-#include "tvgTaskScheduler.h"
-#include "tvgJpgd.h"
-
-class JpgLoader : public LoadModule, public Task
-{
-private:
-    jpeg_decoder* decoder = nullptr;
-    char* data = nullptr;
-    unsigned char *image = nullptr;
-    bool freeData = false;
-
-    void clear();
-
-public:
-    ~JpgLoader();
-
-    using LoadModule::open;
-    bool open(const string& path) override;
-    bool open(const char* data, uint32_t size, bool copy) override;
-    bool read() override;
-    bool close() override;
-
-    unique_ptr<Surface> bitmap() override;
-    void run(unsigned tid) override;
-};
-
-#endif //_TVG_JPG_LOADER_H_

+ 0 - 3029
thirdparty/thorvg/src/loaders/jpg/tvgJpgd.cpp

@@ -1,3029 +0,0 @@
-/*
- * Copyright (c) 2021 - 2023 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.
- */
-
-// jpgd.cpp - C++ class for JPEG decompression.
-// Public domain, Rich Geldreich <[email protected]>
-// Alex Evans: Linear memory allocator (taken from jpge.h).
-// v1.04, May. 19, 2012: Code tweaks to fix VS2008 static code analysis warnings (all looked harmless)
-//
-// Supports progressive and baseline sequential JPEG image files, and the most common chroma subsampling factors: Y, H1V1, H2V1, H1V2, and H2V2.
-//
-// Chroma upsampling quality: H2V2 is upsampled in the frequency domain, H2V1 and H1V2 are upsampled using point sampling.
-// Chroma upsampling reference: "Fast Scheme for Image Size Change in the Compressed Domain"
-// http://vision.ai.uiuc.edu/~dugad/research/dct/index.html
-
-#include <memory.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <setjmp.h>
-#include <stdint.h>
-#include "tvgJpgd.h"
-
-#ifdef _MSC_VER
-  #pragma warning (disable : 4611) // warning C4611: interaction between '_setjmp' and C++ object destruction is non-portable
-  #define JPGD_NORETURN __declspec(noreturn)
-#elif defined(__GNUC__)
-  #define JPGD_NORETURN __attribute__ ((noreturn))
-#else
-  #define JPGD_NORETURN
-#endif
-
-/************************************************************************/
-/* Internal Class Implementation                                        */
-/************************************************************************/
-
-
-// Set to 1 to enable freq. domain chroma upsampling on images using H2V2 subsampling (0=faster nearest neighbor sampling).
-// This is slower, but results in higher quality on images with highly saturated colors.
-#define JPGD_SUPPORT_FREQ_DOMAIN_UPSAMPLING 1
-
-#define JPGD_ASSERT(x)
-#define JPGD_MAX(a,b) (((a)>(b)) ? (a) : (b))
-#define JPGD_MIN(a,b) (((a)<(b)) ? (a) : (b))
-
-typedef int16_t jpgd_quant_t;
-typedef int16_t jpgd_block_t;
-
-// Success/failure error codes.
-enum jpgd_status
-{
-    JPGD_SUCCESS = 0, JPGD_FAILED = -1, JPGD_DONE = 1,
-    JPGD_BAD_DHT_COUNTS = -256, JPGD_BAD_DHT_INDEX, JPGD_BAD_DHT_MARKER, JPGD_BAD_DQT_MARKER, JPGD_BAD_DQT_TABLE,
-    JPGD_BAD_PRECISION, JPGD_BAD_HEIGHT, JPGD_BAD_WIDTH, JPGD_TOO_MANY_COMPONENTS,
-    JPGD_BAD_SOF_LENGTH, JPGD_BAD_VARIABLE_MARKER, JPGD_BAD_DRI_LENGTH, JPGD_BAD_SOS_LENGTH,
-    JPGD_BAD_SOS_COMP_ID, JPGD_W_EXTRA_BYTES_BEFORE_MARKER, JPGD_NO_ARITHMITIC_SUPPORT, JPGD_UNEXPECTED_MARKER,
-    JPGD_NOT_JPEG, JPGD_UNSUPPORTED_MARKER, JPGD_BAD_DQT_LENGTH, JPGD_TOO_MANY_BLOCKS,
-    JPGD_UNDEFINED_QUANT_TABLE, JPGD_UNDEFINED_HUFF_TABLE, JPGD_NOT_SINGLE_SCAN, JPGD_UNSUPPORTED_COLORSPACE,
-    JPGD_UNSUPPORTED_SAMP_FACTORS, JPGD_DECODE_ERROR, JPGD_BAD_RESTART_MARKER, JPGD_ASSERTION_ERROR,
-    JPGD_BAD_SOS_SPECTRAL, JPGD_BAD_SOS_SUCCESSIVE, JPGD_STREAM_READ, JPGD_NOTENOUGHMEM
-};
-
-enum
-{
-    JPGD_IN_BUF_SIZE = 8192, JPGD_MAX_BLOCKS_PER_MCU = 10, JPGD_MAX_HUFF_TABLES = 8, JPGD_MAX_QUANT_TABLES = 4,
-    JPGD_MAX_COMPONENTS = 4, JPGD_MAX_COMPS_IN_SCAN = 4, JPGD_MAX_BLOCKS_PER_ROW = 8192, JPGD_MAX_HEIGHT = 16384, JPGD_MAX_WIDTH = 16384
-};
-
-// Input stream interface.
-// Derive from this class to read input data from sources other than files or memory. Set m_eof_flag to true when no more data is available.
-// The decoder is rather greedy: it will keep on calling this method until its internal input buffer is full, or until the EOF flag is set.
-// It the input stream contains data after the JPEG stream's EOI (end of image) marker it will probably be pulled into the internal buffer.
-// Call the get_total_bytes_read() method to determine the actual size of the JPEG stream after successful decoding.
-struct jpeg_decoder_stream
-{
-    jpeg_decoder_stream() { }
-    virtual ~jpeg_decoder_stream() { }
-
-    // The read() method is called when the internal input buffer is empty.
-    // Parameters:
-    // pBuf - input buffer
-    // max_bytes_to_read - maximum bytes that can be written to pBuf
-    // pEOF_flag - set this to true if at end of stream (no more bytes remaining)
-    // Returns -1 on error, otherwise return the number of bytes actually written to the buffer (which may be 0).
-    // Notes: This method will be called in a loop until you set *pEOF_flag to true or the internal buffer is full.
-    virtual int read(uint8_t *pBuf, int max_bytes_to_read, bool *pEOF_flag) = 0;
-};
-
-
-// stdio FILE stream class.
-class jpeg_decoder_file_stream : public jpeg_decoder_stream
-{
-    jpeg_decoder_file_stream(const jpeg_decoder_file_stream &);
-    jpeg_decoder_file_stream &operator =(const jpeg_decoder_file_stream &);
-
-    FILE *m_pFile = nullptr;
-    bool m_eof_flag = false;
-    bool m_error_flag = false;
-
-public:
-    jpeg_decoder_file_stream() {}
-    virtual ~jpeg_decoder_file_stream();
-    bool open(const char *Pfilename);
-    void close();
-    virtual int read(uint8_t *pBuf, int max_bytes_to_read, bool *pEOF_flag);
-  };
-
-
-// Memory stream class.
-class jpeg_decoder_mem_stream : public jpeg_decoder_stream
-{
-    const uint8_t *m_pSrc_data;
-    uint32_t m_ofs, m_size;
-
-public:
-    jpeg_decoder_mem_stream() : m_pSrc_data(nullptr), m_ofs(0), m_size(0) {}
-    jpeg_decoder_mem_stream(const uint8_t *pSrc_data, uint32_t size) : m_pSrc_data(pSrc_data), m_ofs(0), m_size(size) {}
-    virtual ~jpeg_decoder_mem_stream() {}
-    bool open(const uint8_t *pSrc_data, uint32_t size);
-    void close() { m_pSrc_data = nullptr; m_ofs = 0; m_size = 0; }
-    virtual int read(uint8_t *pBuf, int max_bytes_to_read, bool *pEOF_flag);
-};
-
-
-class jpeg_decoder
-{
-public:
-    // Call get_error_code() after constructing to determine if the stream is valid or not. You may call the get_width(), get_height(), etc.
-    // methods after the constructor is called. You may then either destruct the object, or begin decoding the image by calling begin_decoding(), then decode() on each scanline.
-    jpeg_decoder(jpeg_decoder_stream *pStream);
-    ~jpeg_decoder();
-
-    // Call this method after constructing the object to begin decompression.
-    // If JPGD_SUCCESS is returned you may then call decode() on each scanline.
-    int begin_decoding();
-    // Returns the next scan line.
-    // For grayscale images, pScan_line will point to a buffer containing 8-bit pixels (get_bytes_per_pixel() will return 1).
-    // Otherwise, it will always point to a buffer containing 32-bit RGBA pixels (A will always be 255, and get_bytes_per_pixel() will return 4).
-    // Returns JPGD_SUCCESS if a scan line has been returned.
-    // Returns JPGD_DONE if all scan lines have been returned.
-    // Returns JPGD_FAILED if an error occurred. Call get_error_code() for a more info.
-    int decode(const void** pScan_line, uint32_t* pScan_line_len);
-    inline jpgd_status get_error_code() const { return m_error_code; }
-    inline int get_width() const { return m_image_x_size; }
-    inline int get_height() const { return m_image_y_size; }
-    inline int get_num_components() const { return m_comps_in_frame; }
-    inline int get_bytes_per_pixel() const { return m_dest_bytes_per_pixel; }
-    inline int get_bytes_per_scan_line() const { return m_image_x_size * get_bytes_per_pixel(); }
-    // Returns the total number of bytes actually consumed by the decoder (which should equal the actual size of the JPEG file).
-    inline int get_total_bytes_read() const { return m_total_bytes_read; }
-
-private:
-    jpeg_decoder(const jpeg_decoder &);
-    jpeg_decoder &operator =(const jpeg_decoder &);
-
-    typedef void (*pDecode_block_func)(jpeg_decoder *, int, int, int);
-
-    struct huff_tables
-    {
-      bool ac_table;
-      uint32_t  look_up[256];
-      uint32_t  look_up2[256];
-      uint8_t code_size[256];
-      uint32_t  tree[512];
-    };
-
-    struct coeff_buf
-    {
-      uint8_t *pData;
-      int block_num_x, block_num_y;
-      int block_len_x, block_len_y;
-      int block_size;
-    };
-
-    struct mem_block
-    {
-      mem_block *m_pNext;
-      size_t m_used_count;
-      size_t m_size;
-      char m_data[1];
-    };
-
-    jmp_buf m_jmp_state;
-    mem_block *m_pMem_blocks;
-    int m_image_x_size;
-    int m_image_y_size;
-    jpeg_decoder_stream *m_pStream;
-    int m_progressive_flag;
-    uint8_t m_huff_ac[JPGD_MAX_HUFF_TABLES];
-    uint8_t* m_huff_num[JPGD_MAX_HUFF_TABLES];      // pointer to number of Huffman codes per bit size
-    uint8_t* m_huff_val[JPGD_MAX_HUFF_TABLES];      // pointer to Huffman codes per bit size
-    jpgd_quant_t* m_quant[JPGD_MAX_QUANT_TABLES]; // pointer to quantization tables
-    int m_scan_type;                              // Gray, Yh1v1, Yh1v2, Yh2v1, Yh2v2 (CMYK111, CMYK4114 no longer supported)
-    int m_comps_in_frame;                         // # of components in frame
-    int m_comp_h_samp[JPGD_MAX_COMPONENTS];       // component's horizontal sampling factor
-    int m_comp_v_samp[JPGD_MAX_COMPONENTS];       // component's vertical sampling factor
-    int m_comp_quant[JPGD_MAX_COMPONENTS];        // component's quantization table selector
-    int m_comp_ident[JPGD_MAX_COMPONENTS];        // component's ID
-    int m_comp_h_blocks[JPGD_MAX_COMPONENTS];
-    int m_comp_v_blocks[JPGD_MAX_COMPONENTS];
-    int m_comps_in_scan;                          // # of components in scan
-    int m_comp_list[JPGD_MAX_COMPS_IN_SCAN];      // components in this scan
-    int m_comp_dc_tab[JPGD_MAX_COMPONENTS];       // component's DC Huffman coding table selector
-    int m_comp_ac_tab[JPGD_MAX_COMPONENTS];       // component's AC Huffman coding table selector
-    int m_spectral_start;                         // spectral selection start
-    int m_spectral_end;                           // spectral selection end
-    int m_successive_low;                         // successive approximation low
-    int m_successive_high;                        // successive approximation high
-    int m_max_mcu_x_size;                         // MCU's max. X size in pixels
-    int m_max_mcu_y_size;                         // MCU's max. Y size in pixels
-    int m_blocks_per_mcu;
-    int m_max_blocks_per_row;
-    int m_mcus_per_row, m_mcus_per_col;
-    int m_mcu_org[JPGD_MAX_BLOCKS_PER_MCU];
-    int m_total_lines_left;                       // total # lines left in image
-    int m_mcu_lines_left;                         // total # lines left in this MCU
-    int m_real_dest_bytes_per_scan_line;
-    int m_dest_bytes_per_scan_line;               // rounded up
-    int m_dest_bytes_per_pixel;                   // 4 (RGB) or 1 (Y)
-    huff_tables* m_pHuff_tabs[JPGD_MAX_HUFF_TABLES];
-    coeff_buf* m_dc_coeffs[JPGD_MAX_COMPONENTS];
-    coeff_buf* m_ac_coeffs[JPGD_MAX_COMPONENTS];
-    int m_eob_run;
-    int m_block_y_mcu[JPGD_MAX_COMPONENTS];
-    uint8_t* m_pIn_buf_ofs;
-    int m_in_buf_left;
-    int m_tem_flag;
-    bool m_eof_flag;
-    uint8_t m_in_buf_pad_start[128];
-    uint8_t m_in_buf[JPGD_IN_BUF_SIZE + 128];
-    uint8_t m_in_buf_pad_end[128];
-    int m_bits_left;
-    uint32_t m_bit_buf;
-    int m_restart_interval;
-    int m_restarts_left;
-    int m_next_restart_num;
-    int m_max_mcus_per_row;
-    int m_max_blocks_per_mcu;
-    int m_expanded_blocks_per_mcu;
-    int m_expanded_blocks_per_row;
-    int m_expanded_blocks_per_component;
-    bool  m_freq_domain_chroma_upsample;
-    int m_max_mcus_per_col;
-    uint32_t m_last_dc_val[JPGD_MAX_COMPONENTS];
-    jpgd_block_t* m_pMCU_coefficients;
-    int m_mcu_block_max_zag[JPGD_MAX_BLOCKS_PER_MCU];
-    uint8_t* m_pSample_buf;
-    int m_crr[256];
-    int m_cbb[256];
-    int m_crg[256];
-    int m_cbg[256];
-    uint8_t* m_pScan_line_0;
-    uint8_t* m_pScan_line_1;
-    jpgd_status m_error_code;
-    bool m_ready_flag;
-    int m_total_bytes_read;
-
-    void free_all_blocks();
-    JPGD_NORETURN void stop_decoding(jpgd_status status);
-    void *alloc(size_t n, bool zero = false);
-    void word_clear(void *p, uint16_t c, uint32_t n);
-    void prep_in_buffer();
-    void read_dht_marker();
-    void read_dqt_marker();
-    void read_sof_marker();
-    void skip_variable_marker();
-    void read_dri_marker();
-    void read_sos_marker();
-    int next_marker();
-    int process_markers();
-    void locate_soi_marker();
-    void locate_sof_marker();
-    int locate_sos_marker();
-    void init(jpeg_decoder_stream * pStream);
-    void create_look_ups();
-    void fix_in_buffer();
-    void transform_mcu(int mcu_row);
-    void transform_mcu_expand(int mcu_row);
-    coeff_buf* coeff_buf_open(int block_num_x, int block_num_y, int block_len_x, int block_len_y);
-    inline jpgd_block_t *coeff_buf_getp(coeff_buf *cb, int block_x, int block_y);
-    void load_next_row();
-    void decode_next_row();
-    void make_huff_table(int index, huff_tables *pH);
-    void check_quant_tables();
-    void check_huff_tables();
-    void calc_mcu_block_order();
-    int init_scan();
-    void init_frame();
-    void process_restart();
-    void decode_scan(pDecode_block_func decode_block_func);
-    void init_progressive();
-    void init_sequential();
-    void decode_start();
-    void decode_init(jpeg_decoder_stream * pStream);
-    void H2V2Convert();
-    void H2V1Convert();
-    void H1V2Convert();
-    void H1V1Convert();
-    void gray_convert();
-    void expanded_convert();
-    void find_eoi();
-    inline uint32_t get_char();
-    inline uint32_t get_char(bool *pPadding_flag);
-    inline void stuff_char(uint8_t q);
-    inline uint8_t get_octet();
-    inline uint32_t get_bits(int num_bits);
-    inline uint32_t get_bits_no_markers(int numbits);
-    inline int huff_decode(huff_tables *pH);
-    inline int huff_decode(huff_tables *pH, int& extrabits);
-    static inline uint8_t clamp(int i);
-    static void decode_block_dc_first(jpeg_decoder *pD, int component_id, int block_x, int block_y);
-    static void decode_block_dc_refine(jpeg_decoder *pD, int component_id, int block_x, int block_y);
-    static void decode_block_ac_first(jpeg_decoder *pD, int component_id, int block_x, int block_y);
-    static void decode_block_ac_refine(jpeg_decoder *pD, int component_id, int block_x, int block_y);
-};
-
-
-// DCT coefficients are stored in this sequence.
-static int g_ZAG[64] = {  0,1,8,16,9,2,3,10,17,24,32,25,18,11,4,5,12,19,26,33,40,48,41,34,27,20,13,6,7,14,21,28,35,42,49,56,57,50,43,36,29,22,15,23,30,37,44,51,58,59,52,45,38,31,39,46,53,60,61,54,47,55,62,63 };
-
-enum JPEG_MARKER
-{
-  M_SOF0  = 0xC0, M_SOF1  = 0xC1, M_SOF2  = 0xC2, M_SOF3  = 0xC3, M_SOF5  = 0xC5, M_SOF6  = 0xC6, M_SOF7  = 0xC7, M_JPG   = 0xC8,
-  M_SOF9  = 0xC9, M_SOF10 = 0xCA, M_SOF11 = 0xCB, M_SOF13 = 0xCD, M_SOF14 = 0xCE, M_SOF15 = 0xCF, M_DHT   = 0xC4, M_DAC   = 0xCC,
-  M_RST0  = 0xD0, M_RST1  = 0xD1, M_RST2  = 0xD2, M_RST3  = 0xD3, M_RST4  = 0xD4, M_RST5  = 0xD5, M_RST6  = 0xD6, M_RST7  = 0xD7,
-  M_SOI   = 0xD8, M_EOI   = 0xD9, M_SOS   = 0xDA, M_DQT   = 0xDB, M_DNL   = 0xDC, M_DRI   = 0xDD, M_DHP   = 0xDE, M_EXP   = 0xDF,
-  M_APP0  = 0xE0, M_APP15 = 0xEF, M_JPG0  = 0xF0, M_JPG13 = 0xFD, M_COM   = 0xFE, M_TEM   = 0x01, M_ERROR = 0x100, RST0   = 0xD0
-};
-
-enum JPEG_SUBSAMPLING { JPGD_GRAYSCALE = 0, JPGD_YH1V1, JPGD_YH2V1, JPGD_YH1V2, JPGD_YH2V2 };
-
-#define CONST_BITS  13
-#define PASS1_BITS  2
-#define SCALEDONE ((int32_t)1)
-#define DESCALE(x,n)  (((x) + (SCALEDONE << ((n)-1))) >> (n))
-#define DESCALE_ZEROSHIFT(x,n)  (((x) + (128 << (n)) + (SCALEDONE << ((n)-1))) >> (n))
-#define MULTIPLY(var, cnst)  ((var) * (cnst))
-#define CLAMP(i) ((static_cast<uint32_t>(i) > 255) ? (((~i) >> 31) & 0xFF) : (i))
-
-#define FIX_0_298631336  ((int32_t)2446)        /* FIX(0.298631336) */
-#define FIX_0_390180644  ((int32_t)3196)        /* FIX(0.390180644) */
-#define FIX_0_541196100  ((int32_t)4433)        /* FIX(0.541196100) */
-#define FIX_0_765366865  ((int32_t)6270)        /* FIX(0.765366865) */
-#define FIX_0_899976223  ((int32_t)7373)        /* FIX(0.899976223) */
-#define FIX_1_175875602  ((int32_t)9633)        /* FIX(1.175875602) */
-#define FIX_1_501321110  ((int32_t)12299)       /* FIX(1.501321110) */
-#define FIX_1_847759065  ((int32_t)15137)       /* FIX(1.847759065) */
-#define FIX_1_961570560  ((int32_t)16069)       /* FIX(1.961570560) */
-#define FIX_2_053119869  ((int32_t)16819)       /* FIX(2.053119869) */
-#define FIX_2_562915447  ((int32_t)20995)       /* FIX(2.562915447) */
-#define FIX_3_072711026  ((int32_t)25172)       /* FIX(3.072711026) */
-
-
-// Compiler creates a fast path 1D IDCT for X non-zero columns
-template <int NONZERO_COLS>
-struct Row
-{
-    static void idct(int* pTemp, const jpgd_block_t* pSrc)
-    {
-        // ACCESS_COL() will be optimized at compile time to either an array access, or 0.
-        #define ACCESS_COL(x) (((x) < NONZERO_COLS) ? (int)pSrc[x] : 0)
-
-        const int z2 = ACCESS_COL(2), z3 = ACCESS_COL(6);
-        const int z1 = MULTIPLY(z2 + z3, FIX_0_541196100);
-        const int tmp2 = z1 + MULTIPLY(z3, - FIX_1_847759065);
-        const int tmp3 = z1 + MULTIPLY(z2, FIX_0_765366865);
-
-        const int tmp0 = static_cast<unsigned int>(ACCESS_COL(0) + ACCESS_COL(4)) << CONST_BITS;
-        const int tmp1 = static_cast<unsigned int>(ACCESS_COL(0) - ACCESS_COL(4)) << CONST_BITS;
-
-        const int tmp10 = tmp0 + tmp3, tmp13 = tmp0 - tmp3, tmp11 = tmp1 + tmp2, tmp12 = tmp1 - tmp2;
-
-        const int atmp0 = ACCESS_COL(7), atmp1 = ACCESS_COL(5), atmp2 = ACCESS_COL(3), atmp3 = ACCESS_COL(1);
-
-        const int bz1 = atmp0 + atmp3, bz2 = atmp1 + atmp2, bz3 = atmp0 + atmp2, bz4 = atmp1 + atmp3;
-        const int bz5 = MULTIPLY(bz3 + bz4, FIX_1_175875602);
-
-        const int az1 = MULTIPLY(bz1, - FIX_0_899976223);
-        const int az2 = MULTIPLY(bz2, - FIX_2_562915447);
-        const int az3 = MULTIPLY(bz3, - FIX_1_961570560) + bz5;
-        const int az4 = MULTIPLY(bz4, - FIX_0_390180644) + bz5;
-
-        const int btmp0 = MULTIPLY(atmp0, FIX_0_298631336) + az1 + az3;
-        const int btmp1 = MULTIPLY(atmp1, FIX_2_053119869) + az2 + az4;
-        const int btmp2 = MULTIPLY(atmp2, FIX_3_072711026) + az2 + az3;
-        const int btmp3 = MULTIPLY(atmp3, FIX_1_501321110) + az1 + az4;
-
-        pTemp[0] = DESCALE(tmp10 + btmp3, CONST_BITS-PASS1_BITS);
-        pTemp[7] = DESCALE(tmp10 - btmp3, CONST_BITS-PASS1_BITS);
-        pTemp[1] = DESCALE(tmp11 + btmp2, CONST_BITS-PASS1_BITS);
-        pTemp[6] = DESCALE(tmp11 - btmp2, CONST_BITS-PASS1_BITS);
-        pTemp[2] = DESCALE(tmp12 + btmp1, CONST_BITS-PASS1_BITS);
-        pTemp[5] = DESCALE(tmp12 - btmp1, CONST_BITS-PASS1_BITS);
-        pTemp[3] = DESCALE(tmp13 + btmp0, CONST_BITS-PASS1_BITS);
-        pTemp[4] = DESCALE(tmp13 - btmp0, CONST_BITS-PASS1_BITS);
-    }
-};
-
-
-template <>
-struct Row<0>
-{
-    static void idct(int* pTemp, const jpgd_block_t* pSrc)
-    {
-#ifdef _MSC_VER
-      pTemp; pSrc;
-#endif
-    }
-};
-
-
-template <>
-struct Row<1>
-{
-    static void idct(int* pTemp, const jpgd_block_t* pSrc)
-    {
-        const int dcval = (pSrc[0] << PASS1_BITS);
-
-        pTemp[0] = dcval;
-        pTemp[1] = dcval;
-        pTemp[2] = dcval;
-        pTemp[3] = dcval;
-        pTemp[4] = dcval;
-        pTemp[5] = dcval;
-        pTemp[6] = dcval;
-        pTemp[7] = dcval;
-    }
-};
-
-
-// Compiler creates a fast path 1D IDCT for X non-zero rows
-template <int NONZERO_ROWS>
-struct Col
-{
-    static void idct(uint8_t* pDst_ptr, const int* pTemp)
-    {
-        // ACCESS_ROW() will be optimized at compile time to either an array access, or 0.
-        #define ACCESS_ROW(x) (((x) < NONZERO_ROWS) ? pTemp[x * 8] : 0)
-
-        const int z2 = ACCESS_ROW(2);
-        const int z3 = ACCESS_ROW(6);
-
-        const int z1 = MULTIPLY(z2 + z3, FIX_0_541196100);
-        const int tmp2 = z1 + MULTIPLY(z3, - FIX_1_847759065);
-        const int tmp3 = z1 + MULTIPLY(z2, FIX_0_765366865);
-
-        const int tmp0 = static_cast<unsigned int>(ACCESS_ROW(0) + ACCESS_ROW(4)) << CONST_BITS;
-        const int tmp1 = static_cast<unsigned int>(ACCESS_ROW(0) - ACCESS_ROW(4)) << CONST_BITS;
-
-        const int tmp10 = tmp0 + tmp3, tmp13 = tmp0 - tmp3, tmp11 = tmp1 + tmp2, tmp12 = tmp1 - tmp2;
-
-        const int atmp0 = ACCESS_ROW(7), atmp1 = ACCESS_ROW(5), atmp2 = ACCESS_ROW(3), atmp3 = ACCESS_ROW(1);
-
-        const int bz1 = atmp0 + atmp3, bz2 = atmp1 + atmp2, bz3 = atmp0 + atmp2, bz4 = atmp1 + atmp3;
-        const int bz5 = MULTIPLY(bz3 + bz4, FIX_1_175875602);
-
-        const int az1 = MULTIPLY(bz1, - FIX_0_899976223);
-        const int az2 = MULTIPLY(bz2, - FIX_2_562915447);
-        const int az3 = MULTIPLY(bz3, - FIX_1_961570560) + bz5;
-        const int az4 = MULTIPLY(bz4, - FIX_0_390180644) + bz5;
-
-        const int btmp0 = MULTIPLY(atmp0, FIX_0_298631336) + az1 + az3;
-        const int btmp1 = MULTIPLY(atmp1, FIX_2_053119869) + az2 + az4;
-        const int btmp2 = MULTIPLY(atmp2, FIX_3_072711026) + az2 + az3;
-        const int btmp3 = MULTIPLY(atmp3, FIX_1_501321110) + az1 + az4;
-
-        int i = DESCALE_ZEROSHIFT(tmp10 + btmp3, CONST_BITS+PASS1_BITS+3);
-        pDst_ptr[8*0] = (uint8_t)CLAMP(i);
-
-        i = DESCALE_ZEROSHIFT(tmp10 - btmp3, CONST_BITS+PASS1_BITS+3);
-        pDst_ptr[8*7] = (uint8_t)CLAMP(i);
-
-        i = DESCALE_ZEROSHIFT(tmp11 + btmp2, CONST_BITS+PASS1_BITS+3);
-        pDst_ptr[8*1] = (uint8_t)CLAMP(i);
-
-        i = DESCALE_ZEROSHIFT(tmp11 - btmp2, CONST_BITS+PASS1_BITS+3);
-        pDst_ptr[8*6] = (uint8_t)CLAMP(i);
-
-        i = DESCALE_ZEROSHIFT(tmp12 + btmp1, CONST_BITS+PASS1_BITS+3);
-        pDst_ptr[8*2] = (uint8_t)CLAMP(i);
-
-        i = DESCALE_ZEROSHIFT(tmp12 - btmp1, CONST_BITS+PASS1_BITS+3);
-        pDst_ptr[8*5] = (uint8_t)CLAMP(i);
-
-        i = DESCALE_ZEROSHIFT(tmp13 + btmp0, CONST_BITS+PASS1_BITS+3);
-        pDst_ptr[8*3] = (uint8_t)CLAMP(i);
-
-        i = DESCALE_ZEROSHIFT(tmp13 - btmp0, CONST_BITS+PASS1_BITS+3);
-        pDst_ptr[8*4] = (uint8_t)CLAMP(i);
-    }
-};
-
-
-template <>
-struct Col<1>
-{
-    static void idct(uint8_t* pDst_ptr, const int* pTemp)
-    {
-        int dcval = DESCALE_ZEROSHIFT(pTemp[0], PASS1_BITS+3);
-        const uint8_t dcval_clamped = (uint8_t)CLAMP(dcval);
-        pDst_ptr[0*8] = dcval_clamped;
-        pDst_ptr[1*8] = dcval_clamped;
-        pDst_ptr[2*8] = dcval_clamped;
-        pDst_ptr[3*8] = dcval_clamped;
-        pDst_ptr[4*8] = dcval_clamped;
-        pDst_ptr[5*8] = dcval_clamped;
-        pDst_ptr[6*8] = dcval_clamped;
-        pDst_ptr[7*8] = dcval_clamped;
-    }
-};
-
-
-static const uint8_t s_idct_row_table[] = {
-    1,0,0,0,0,0,0,0, 2,0,0,0,0,0,0,0, 2,1,0,0,0,0,0,0, 2,1,1,0,0,0,0,0, 2,2,1,0,0,0,0,0, 3,2,1,0,0,0,0,0, 4,2,1,0,0,0,0,0, 4,3,1,0,0,0,0,0,
-    4,3,2,0,0,0,0,0, 4,3,2,1,0,0,0,0, 4,3,2,1,1,0,0,0, 4,3,2,2,1,0,0,0, 4,3,3,2,1,0,0,0, 4,4,3,2,1,0,0,0, 5,4,3,2,1,0,0,0, 6,4,3,2,1,0,0,0,
-    6,5,3,2,1,0,0,0, 6,5,4,2,1,0,0,0, 6,5,4,3,1,0,0,0, 6,5,4,3,2,0,0,0, 6,5,4,3,2,1,0,0, 6,5,4,3,2,1,1,0, 6,5,4,3,2,2,1,0, 6,5,4,3,3,2,1,0,
-    6,5,4,4,3,2,1,0, 6,5,5,4,3,2,1,0, 6,6,5,4,3,2,1,0, 7,6,5,4,3,2,1,0, 8,6,5,4,3,2,1,0, 8,7,5,4,3,2,1,0, 8,7,6,4,3,2,1,0, 8,7,6,5,3,2,1,0,
-    8,7,6,5,4,2,1,0, 8,7,6,5,4,3,1,0, 8,7,6,5,4,3,2,0, 8,7,6,5,4,3,2,1, 8,7,6,5,4,3,2,2, 8,7,6,5,4,3,3,2, 8,7,6,5,4,4,3,2, 8,7,6,5,5,4,3,2,
-    8,7,6,6,5,4,3,2, 8,7,7,6,5,4,3,2, 8,8,7,6,5,4,3,2, 8,8,8,6,5,4,3,2, 8,8,8,7,5,4,3,2, 8,8,8,7,6,4,3,2, 8,8,8,7,6,5,3,2, 8,8,8,7,6,5,4,2,
-    8,8,8,7,6,5,4,3, 8,8,8,7,6,5,4,4, 8,8,8,7,6,5,5,4, 8,8,8,7,6,6,5,4, 8,8,8,7,7,6,5,4, 8,8,8,8,7,6,5,4, 8,8,8,8,8,6,5,4, 8,8,8,8,8,7,5,4,
-    8,8,8,8,8,7,6,4, 8,8,8,8,8,7,6,5, 8,8,8,8,8,7,6,6, 8,8,8,8,8,7,7,6, 8,8,8,8,8,8,7,6, 8,8,8,8,8,8,8,6, 8,8,8,8,8,8,8,7, 8,8,8,8,8,8,8,8,
-};
-
-
-static const uint8_t s_idct_col_table[] = { 1, 1, 2, 3, 3, 3, 3, 3, 3, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8 };
-
-
-void idct(const jpgd_block_t* pSrc_ptr, uint8_t* pDst_ptr, int block_max_zag)
-{
-    JPGD_ASSERT(block_max_zag >= 1);
-    JPGD_ASSERT(block_max_zag <= 64);
-
-    if (block_max_zag <= 1) {
-        int k = ((pSrc_ptr[0] + 4) >> 3) + 128;
-        k = CLAMP(k);
-        k = k | (k<<8);
-        k = k | (k<<16);
-        for (int i = 8; i > 0; i--) {
-            *(int*)&pDst_ptr[0] = k;
-            *(int*)&pDst_ptr[4] = k;
-            pDst_ptr += 8;
-        }
-      return;
-    }
-
-    int temp[64];
-    const jpgd_block_t* pSrc = pSrc_ptr;
-    int* pTemp = temp;
-    const uint8_t* pRow_tab = &s_idct_row_table[(block_max_zag - 1) * 8];
-    int i;
-    for (i = 8; i > 0; i--, pRow_tab++) {
-        switch (*pRow_tab) {
-            case 0: Row<0>::idct(pTemp, pSrc); break;
-            case 1: Row<1>::idct(pTemp, pSrc); break;
-            case 2: Row<2>::idct(pTemp, pSrc); break;
-            case 3: Row<3>::idct(pTemp, pSrc); break;
-            case 4: Row<4>::idct(pTemp, pSrc); break;
-            case 5: Row<5>::idct(pTemp, pSrc); break;
-            case 6: Row<6>::idct(pTemp, pSrc); break;
-            case 7: Row<7>::idct(pTemp, pSrc); break;
-            case 8: Row<8>::idct(pTemp, pSrc); break;
-        }
-        pSrc += 8;
-        pTemp += 8;
-    }
-
-    pTemp = temp;
-
-    const int nonzero_rows = s_idct_col_table[block_max_zag - 1];
-    for (i = 8; i > 0; i--) {
-        switch (nonzero_rows) {
-            case 1: Col<1>::idct(pDst_ptr, pTemp); break;
-            case 2: Col<2>::idct(pDst_ptr, pTemp); break;
-            case 3: Col<3>::idct(pDst_ptr, pTemp); break;
-            case 4: Col<4>::idct(pDst_ptr, pTemp); break;
-            case 5: Col<5>::idct(pDst_ptr, pTemp); break;
-            case 6: Col<6>::idct(pDst_ptr, pTemp); break;
-            case 7: Col<7>::idct(pDst_ptr, pTemp); break;
-            case 8: Col<8>::idct(pDst_ptr, pTemp); break;
-        }
-        pTemp++;
-        pDst_ptr++;
-    }
-}
-
-
-void idct_4x4(const jpgd_block_t* pSrc_ptr, uint8_t* pDst_ptr)
-{
-    int temp[64];
-    int* pTemp = temp;
-    const jpgd_block_t* pSrc = pSrc_ptr;
-
-    for (int i = 4; i > 0; i--) {
-        Row<4>::idct(pTemp, pSrc);
-        pSrc += 8;
-        pTemp += 8;
-    }
-
-    pTemp = temp;
-
-    for (int i = 8; i > 0; i--) {
-        Col<4>::idct(pDst_ptr, pTemp);
-        pTemp++;
-        pDst_ptr++;
-    }
-}
-
-
-// Retrieve one character from the input stream.
-inline uint32_t jpeg_decoder::get_char()
-{
-    // Any bytes remaining in buffer?
-    if (!m_in_buf_left) {
-        // Try to get more bytes.
-        prep_in_buffer();
-        // Still nothing to get?
-        if (!m_in_buf_left) {
-            // Pad the end of the stream with 0xFF 0xD9 (EOI marker)
-            int t = m_tem_flag;
-            m_tem_flag ^= 1;
-            if (t) return 0xD9;
-            else return 0xFF;
-        }
-    }
-    uint32_t c = *m_pIn_buf_ofs++;
-    m_in_buf_left--;
-    return c;
-}
-
-
-// Same as previous method, except can indicate if the character is a pad character or not.
-inline uint32_t jpeg_decoder::get_char(bool *pPadding_flag)
-{
-    if (!m_in_buf_left) {
-        prep_in_buffer();
-        if (!m_in_buf_left) {
-            *pPadding_flag = true;
-            int t = m_tem_flag;
-            m_tem_flag ^= 1;
-            if (t) return 0xD9;
-            else return 0xFF;
-        }
-    }
-    *pPadding_flag = false;
-    uint32_t c = *m_pIn_buf_ofs++;
-    m_in_buf_left--;
-
-    return c;
-}
-
-
-// Inserts a previously retrieved character back into the input buffer.
-inline void jpeg_decoder::stuff_char(uint8_t q)
-{
-    *(--m_pIn_buf_ofs) = q;
-    m_in_buf_left++;
-}
-
-
-// Retrieves one character from the input stream, but does not read past markers. Will continue to return 0xFF when a marker is encountered.
-inline uint8_t jpeg_decoder::get_octet()
-{
-    bool padding_flag;
-    int c = get_char(&padding_flag);
-
-    if (c == 0xFF) {
-        if (padding_flag) return 0xFF;
-
-        c = get_char(&padding_flag);
-        if (padding_flag) {
-            stuff_char(0xFF);
-            return 0xFF;
-        }
-        if (c == 0x00) return 0xFF;
-        else {
-            stuff_char(static_cast<uint8_t>(c));
-            stuff_char(0xFF);
-            return 0xFF;
-        }
-    }
-    return static_cast<uint8_t>(c);
-}
-
-
-// Retrieves a variable number of bits from the input stream. Does not recognize markers.
-inline uint32_t jpeg_decoder::get_bits(int num_bits)
-{
-    if (!num_bits) return 0;
-
-    uint32_t i = m_bit_buf >> (32 - num_bits);
-
-    if ((m_bits_left -= num_bits) <= 0) {
-        m_bit_buf <<= (num_bits += m_bits_left);
-        uint32_t c1 = get_char();
-        uint32_t c2 = get_char();
-        m_bit_buf = (m_bit_buf & 0xFFFF0000) | (c1 << 8) | c2;
-        m_bit_buf <<= -m_bits_left;
-        m_bits_left += 16;
-        JPGD_ASSERT(m_bits_left >= 0);
-    }
-    else m_bit_buf <<= num_bits;
-
-    return i;
-}
-
-
-// Retrieves a variable number of bits from the input stream. Markers will not be read into the input bit buffer. Instead, an infinite number of all 1's will be returned when a marker is encountered.
-inline uint32_t jpeg_decoder::get_bits_no_markers(int num_bits)
-{
-    if (!num_bits)return 0;
-
-    uint32_t i = m_bit_buf >> (32 - num_bits);
-
-    if ((m_bits_left -= num_bits) <= 0) {
-        m_bit_buf <<= (num_bits += m_bits_left);
-        if ((m_in_buf_left < 2) || (m_pIn_buf_ofs[0] == 0xFF) || (m_pIn_buf_ofs[1] == 0xFF)) {
-            uint32_t c1 = get_octet();
-            uint32_t c2 = get_octet();
-            m_bit_buf |= (c1 << 8) | c2;
-        } else {
-            m_bit_buf |= ((uint32_t)m_pIn_buf_ofs[0] << 8) | m_pIn_buf_ofs[1];
-            m_in_buf_left -= 2;
-            m_pIn_buf_ofs += 2;
-        }
-        m_bit_buf <<= -m_bits_left;
-        m_bits_left += 16;
-        JPGD_ASSERT(m_bits_left >= 0);
-    } else m_bit_buf <<= num_bits;
-
-    return i;
-}
-
-
-// Decodes a Huffman encoded symbol.
-inline int jpeg_decoder::huff_decode(huff_tables *pH)
-{
-    int symbol;
-
-    // Check first 8-bits: do we have a complete symbol?
-    if ((symbol = pH->look_up[m_bit_buf >> 24]) < 0) {
-        // Decode more bits, use a tree traversal to find symbol.
-        int ofs = 23;
-        do {
-            symbol = pH->tree[-(int)(symbol + ((m_bit_buf >> ofs) & 1))];
-            ofs--;
-        } while (symbol < 0);
-        get_bits_no_markers(8 + (23 - ofs));
-    } else get_bits_no_markers(pH->code_size[symbol]);
-
-  return symbol;
-}
-
-
-// Decodes a Huffman encoded symbol.
-inline int jpeg_decoder::huff_decode(huff_tables *pH, int& extra_bits)
-{
-    int symbol;
-
-    // Check first 8-bits: do we have a complete symbol?
-    if ((symbol = pH->look_up2[m_bit_buf >> 24]) < 0) {
-        // Use a tree traversal to find symbol.
-        int ofs = 23;
-        do {
-            symbol = pH->tree[-(int)(symbol + ((m_bit_buf >> ofs) & 1))];
-            ofs--;
-        } while (symbol < 0);
-
-        get_bits_no_markers(8 + (23 - ofs));
-        extra_bits = get_bits_no_markers(symbol & 0xF);
-    } else {
-        JPGD_ASSERT(((symbol >> 8) & 31) == pH->code_size[symbol & 255] + ((symbol & 0x8000) ? (symbol & 15) : 0));
-
-        if (symbol & 0x8000) {
-            get_bits_no_markers((symbol >> 8) & 31);
-            extra_bits = symbol >> 16;
-        } else  {
-            int code_size = (symbol >> 8) & 31;
-            int num_extra_bits = symbol & 0xF;
-            int bits = code_size + num_extra_bits;
-            if (bits <= (m_bits_left + 16)) extra_bits = get_bits_no_markers(bits) & ((1 << num_extra_bits) - 1);
-            else {
-                get_bits_no_markers(code_size);
-                extra_bits = get_bits_no_markers(num_extra_bits);
-            }
-        }
-        symbol &= 0xFF;
-    }
-    return symbol;
-}
-
-
-// Tables and macro used to fully decode the DPCM differences.
-static const int s_extend_test[16] = { 0, 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080, 0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000 };
-static const unsigned int s_extend_offset[16] = { 0, ((~0u)<<1) + 1, ((~0u)<<2) + 1, ((~0u)<<3) + 1, ((~0u)<<4) + 1, ((~0u)<<5) + 1, ((~0u)<<6) + 1, ((~0u)<<7) + 1, ((~0u)<<8) + 1, ((~0u)<<9) + 1, ((~0u)<<10) + 1, ((~0u)<<11) + 1, ((~0u)<<12) + 1, ((~0u)<<13) + 1, ((~0u)<<14) + 1, ((~0u)<<15) + 1 };
-
-// The logical AND's in this macro are to shut up static code analysis (aren't really necessary - couldn't find another way to do this)
-#define JPGD_HUFF_EXTEND(x, s) (((x) < s_extend_test[s & 15]) ? ((x) + s_extend_offset[s & 15]) : (x))
-
-
-// Clamps a value between 0-255.
-inline uint8_t jpeg_decoder::clamp(int i)
-{
-    if (static_cast<uint32_t>(i) > 255) i = (((~i) >> 31) & 0xFF);
-    return static_cast<uint8_t>(i);
-}
-
-
-namespace DCT_Upsample
-{
-    struct Matrix44
-    {
-        typedef int Element_Type;
-        enum { NUM_ROWS = 4, NUM_COLS = 4 };
-
-        Element_Type v[NUM_ROWS][NUM_COLS];
-
-        inline int rows() const { return NUM_ROWS; }
-        inline int cols() const { return NUM_COLS; }
-        inline const Element_Type & at(int r, int c) const { return v[r][c]; }
-        inline       Element_Type & at(int r, int c)       { return v[r][c]; }
-
-        inline Matrix44() {}
-
-        inline Matrix44& operator += (const Matrix44& a)
-        {
-            for (int r = 0; r < NUM_ROWS; r++) {
-                at(r, 0) += a.at(r, 0);
-                at(r, 1) += a.at(r, 1);
-                at(r, 2) += a.at(r, 2);
-                at(r, 3) += a.at(r, 3);
-            }
-            return *this;
-        }
-
-        inline Matrix44& operator -= (const Matrix44& a)
-        {
-            for (int r = 0; r < NUM_ROWS; r++) {
-                at(r, 0) -= a.at(r, 0);
-                at(r, 1) -= a.at(r, 1);
-                at(r, 2) -= a.at(r, 2);
-                at(r, 3) -= a.at(r, 3);
-            }
-            return *this;
-        }
-
-        friend inline Matrix44 operator + (const Matrix44& a, const Matrix44& b)
-        {
-            Matrix44 ret;
-            for (int r = 0; r < NUM_ROWS; r++) {
-                ret.at(r, 0) = a.at(r, 0) + b.at(r, 0);
-                ret.at(r, 1) = a.at(r, 1) + b.at(r, 1);
-                ret.at(r, 2) = a.at(r, 2) + b.at(r, 2);
-                ret.at(r, 3) = a.at(r, 3) + b.at(r, 3);
-            }
-            return ret;
-        }
-
-        friend inline Matrix44 operator - (const Matrix44& a, const Matrix44& b)
-        {
-            Matrix44 ret;
-            for (int r = 0; r < NUM_ROWS; r++) {
-                ret.at(r, 0) = a.at(r, 0) - b.at(r, 0);
-                ret.at(r, 1) = a.at(r, 1) - b.at(r, 1);
-                ret.at(r, 2) = a.at(r, 2) - b.at(r, 2);
-                ret.at(r, 3) = a.at(r, 3) - b.at(r, 3);
-            }
-            return ret;
-        }
-
-        static inline void add_and_store(jpgd_block_t* pDst, const Matrix44& a, const Matrix44& b)
-        {
-            for (int r = 0; r < 4; r++) {
-                pDst[0*8 + r] = static_cast<jpgd_block_t>(a.at(r, 0) + b.at(r, 0));
-                pDst[1*8 + r] = static_cast<jpgd_block_t>(a.at(r, 1) + b.at(r, 1));
-                pDst[2*8 + r] = static_cast<jpgd_block_t>(a.at(r, 2) + b.at(r, 2));
-                pDst[3*8 + r] = static_cast<jpgd_block_t>(a.at(r, 3) + b.at(r, 3));
-            }
-        }
-
-        static inline void sub_and_store(jpgd_block_t* pDst, const Matrix44& a, const Matrix44& b)
-        {
-            for (int r = 0; r < 4; r++) {
-                pDst[0*8 + r] = static_cast<jpgd_block_t>(a.at(r, 0) - b.at(r, 0));
-                pDst[1*8 + r] = static_cast<jpgd_block_t>(a.at(r, 1) - b.at(r, 1));
-                pDst[2*8 + r] = static_cast<jpgd_block_t>(a.at(r, 2) - b.at(r, 2));
-                pDst[3*8 + r] = static_cast<jpgd_block_t>(a.at(r, 3) - b.at(r, 3));
-            }
-        }
-    };
-
-    const int FRACT_BITS = 10;
-    const int SCALE = 1 << FRACT_BITS;
-
-    typedef int Temp_Type;
-    #define D(i) (((i) + (SCALE >> 1)) >> FRACT_BITS)
-    #define F(i) ((int)((i) * SCALE + .5f))
-
-    // Any decent C++ compiler will optimize this at compile time to a 0, or an array access.
-    #define AT(c, r) ((((c)>=NUM_COLS)||((r)>=NUM_ROWS)) ? 0 : pSrc[(c)+(r)*8])
-
-    // NUM_ROWS/NUM_COLS = # of non-zero rows/cols in input matrix
-    template<int NUM_ROWS, int NUM_COLS>
-    struct P_Q
-    {
-        static void calc(Matrix44& P, Matrix44& Q, const jpgd_block_t* pSrc)
-        {
-            // 4x8 = 4x8 times 8x8, matrix 0 is constant
-            const Temp_Type X000 = AT(0, 0);
-            const Temp_Type X001 = AT(0, 1);
-            const Temp_Type X002 = AT(0, 2);
-            const Temp_Type X003 = AT(0, 3);
-            const Temp_Type X004 = AT(0, 4);
-            const Temp_Type X005 = AT(0, 5);
-            const Temp_Type X006 = AT(0, 6);
-            const Temp_Type X007 = AT(0, 7);
-            const Temp_Type X010 = D(F(0.415735f) * AT(1, 0) + F(0.791065f) * AT(3, 0) + F(-0.352443f) * AT(5, 0) + F(0.277785f) * AT(7, 0));
-            const Temp_Type X011 = D(F(0.415735f) * AT(1, 1) + F(0.791065f) * AT(3, 1) + F(-0.352443f) * AT(5, 1) + F(0.277785f) * AT(7, 1));
-            const Temp_Type X012 = D(F(0.415735f) * AT(1, 2) + F(0.791065f) * AT(3, 2) + F(-0.352443f) * AT(5, 2) + F(0.277785f) * AT(7, 2));
-            const Temp_Type X013 = D(F(0.415735f) * AT(1, 3) + F(0.791065f) * AT(3, 3) + F(-0.352443f) * AT(5, 3) + F(0.277785f) * AT(7, 3));
-            const Temp_Type X014 = D(F(0.415735f) * AT(1, 4) + F(0.791065f) * AT(3, 4) + F(-0.352443f) * AT(5, 4) + F(0.277785f) * AT(7, 4));
-            const Temp_Type X015 = D(F(0.415735f) * AT(1, 5) + F(0.791065f) * AT(3, 5) + F(-0.352443f) * AT(5, 5) + F(0.277785f) * AT(7, 5));
-            const Temp_Type X016 = D(F(0.415735f) * AT(1, 6) + F(0.791065f) * AT(3, 6) + F(-0.352443f) * AT(5, 6) + F(0.277785f) * AT(7, 6));
-            const Temp_Type X017 = D(F(0.415735f) * AT(1, 7) + F(0.791065f) * AT(3, 7) + F(-0.352443f) * AT(5, 7) + F(0.277785f) * AT(7, 7));
-            const Temp_Type X020 = AT(4, 0);
-            const Temp_Type X021 = AT(4, 1);
-            const Temp_Type X022 = AT(4, 2);
-            const Temp_Type X023 = AT(4, 3);
-            const Temp_Type X024 = AT(4, 4);
-            const Temp_Type X025 = AT(4, 5);
-            const Temp_Type X026 = AT(4, 6);
-            const Temp_Type X027 = AT(4, 7);
-            const Temp_Type X030 = D(F(0.022887f) * AT(1, 0) + F(-0.097545f) * AT(3, 0) + F(0.490393f) * AT(5, 0) + F(0.865723f) * AT(7, 0));
-            const Temp_Type X031 = D(F(0.022887f) * AT(1, 1) + F(-0.097545f) * AT(3, 1) + F(0.490393f) * AT(5, 1) + F(0.865723f) * AT(7, 1));
-            const Temp_Type X032 = D(F(0.022887f) * AT(1, 2) + F(-0.097545f) * AT(3, 2) + F(0.490393f) * AT(5, 2) + F(0.865723f) * AT(7, 2));
-            const Temp_Type X033 = D(F(0.022887f) * AT(1, 3) + F(-0.097545f) * AT(3, 3) + F(0.490393f) * AT(5, 3) + F(0.865723f) * AT(7, 3));
-            const Temp_Type X034 = D(F(0.022887f) * AT(1, 4) + F(-0.097545f) * AT(3, 4) + F(0.490393f) * AT(5, 4) + F(0.865723f) * AT(7, 4));
-            const Temp_Type X035 = D(F(0.022887f) * AT(1, 5) + F(-0.097545f) * AT(3, 5) + F(0.490393f) * AT(5, 5) + F(0.865723f) * AT(7, 5));
-            const Temp_Type X036 = D(F(0.022887f) * AT(1, 6) + F(-0.097545f) * AT(3, 6) + F(0.490393f) * AT(5, 6) + F(0.865723f) * AT(7, 6));
-            const Temp_Type X037 = D(F(0.022887f) * AT(1, 7) + F(-0.097545f) * AT(3, 7) + F(0.490393f) * AT(5, 7) + F(0.865723f) * AT(7, 7));
-
-            // 4x4 = 4x8 times 8x4, matrix 1 is constant
-            P.at(0, 0) = X000;
-            P.at(0, 1) = D(X001 * F(0.415735f) + X003 * F(0.791065f) + X005 * F(-0.352443f) + X007 * F(0.277785f));
-            P.at(0, 2) = X004;
-            P.at(0, 3) = D(X001 * F(0.022887f) + X003 * F(-0.097545f) + X005 * F(0.490393f) + X007 * F(0.865723f));
-            P.at(1, 0) = X010;
-            P.at(1, 1) = D(X011 * F(0.415735f) + X013 * F(0.791065f) + X015 * F(-0.352443f) + X017 * F(0.277785f));
-            P.at(1, 2) = X014;
-            P.at(1, 3) = D(X011 * F(0.022887f) + X013 * F(-0.097545f) + X015 * F(0.490393f) + X017 * F(0.865723f));
-            P.at(2, 0) = X020;
-            P.at(2, 1) = D(X021 * F(0.415735f) + X023 * F(0.791065f) + X025 * F(-0.352443f) + X027 * F(0.277785f));
-            P.at(2, 2) = X024;
-            P.at(2, 3) = D(X021 * F(0.022887f) + X023 * F(-0.097545f) + X025 * F(0.490393f) + X027 * F(0.865723f));
-            P.at(3, 0) = X030;
-            P.at(3, 1) = D(X031 * F(0.415735f) + X033 * F(0.791065f) + X035 * F(-0.352443f) + X037 * F(0.277785f));
-            P.at(3, 2) = X034;
-            P.at(3, 3) = D(X031 * F(0.022887f) + X033 * F(-0.097545f) + X035 * F(0.490393f) + X037 * F(0.865723f));
-            // 40 muls 24 adds
-
-            // 4x4 = 4x8 times 8x4, matrix 1 is constant
-            Q.at(0, 0) = D(X001 * F(0.906127f) + X003 * F(-0.318190f) + X005 * F(0.212608f) + X007 * F(-0.180240f));
-            Q.at(0, 1) = X002;
-            Q.at(0, 2) = D(X001 * F(-0.074658f) + X003 * F(0.513280f) + X005 * F(0.768178f) + X007 * F(-0.375330f));
-            Q.at(0, 3) = X006;
-            Q.at(1, 0) = D(X011 * F(0.906127f) + X013 * F(-0.318190f) + X015 * F(0.212608f) + X017 * F(-0.180240f));
-            Q.at(1, 1) = X012;
-            Q.at(1, 2) = D(X011 * F(-0.074658f) + X013 * F(0.513280f) + X015 * F(0.768178f) + X017 * F(-0.375330f));
-            Q.at(1, 3) = X016;
-            Q.at(2, 0) = D(X021 * F(0.906127f) + X023 * F(-0.318190f) + X025 * F(0.212608f) + X027 * F(-0.180240f));
-            Q.at(2, 1) = X022;
-            Q.at(2, 2) = D(X021 * F(-0.074658f) + X023 * F(0.513280f) + X025 * F(0.768178f) + X027 * F(-0.375330f));
-            Q.at(2, 3) = X026;
-            Q.at(3, 0) = D(X031 * F(0.906127f) + X033 * F(-0.318190f) + X035 * F(0.212608f) + X037 * F(-0.180240f));
-            Q.at(3, 1) = X032;
-            Q.at(3, 2) = D(X031 * F(-0.074658f) + X033 * F(0.513280f) + X035 * F(0.768178f) + X037 * F(-0.375330f));
-            Q.at(3, 3) = X036;
-            // 40 muls 24 adds
-        }
-    };
-
-
-    template<int NUM_ROWS, int NUM_COLS>
-    struct R_S
-    {
-        static void calc(Matrix44& R, Matrix44& S, const jpgd_block_t* pSrc)
-        {
-            // 4x8 = 4x8 times 8x8, matrix 0 is constant
-            const Temp_Type X100 = D(F(0.906127f) * AT(1, 0) + F(-0.318190f) * AT(3, 0) + F(0.212608f) * AT(5, 0) + F(-0.180240f) * AT(7, 0));
-            const Temp_Type X101 = D(F(0.906127f) * AT(1, 1) + F(-0.318190f) * AT(3, 1) + F(0.212608f) * AT(5, 1) + F(-0.180240f) * AT(7, 1));
-            const Temp_Type X102 = D(F(0.906127f) * AT(1, 2) + F(-0.318190f) * AT(3, 2) + F(0.212608f) * AT(5, 2) + F(-0.180240f) * AT(7, 2));
-            const Temp_Type X103 = D(F(0.906127f) * AT(1, 3) + F(-0.318190f) * AT(3, 3) + F(0.212608f) * AT(5, 3) + F(-0.180240f) * AT(7, 3));
-            const Temp_Type X104 = D(F(0.906127f) * AT(1, 4) + F(-0.318190f) * AT(3, 4) + F(0.212608f) * AT(5, 4) + F(-0.180240f) * AT(7, 4));
-            const Temp_Type X105 = D(F(0.906127f) * AT(1, 5) + F(-0.318190f) * AT(3, 5) + F(0.212608f) * AT(5, 5) + F(-0.180240f) * AT(7, 5));
-            const Temp_Type X106 = D(F(0.906127f) * AT(1, 6) + F(-0.318190f) * AT(3, 6) + F(0.212608f) * AT(5, 6) + F(-0.180240f) * AT(7, 6));
-            const Temp_Type X107 = D(F(0.906127f) * AT(1, 7) + F(-0.318190f) * AT(3, 7) + F(0.212608f) * AT(5, 7) + F(-0.180240f) * AT(7, 7));
-            const Temp_Type X110 = AT(2, 0);
-            const Temp_Type X111 = AT(2, 1);
-            const Temp_Type X112 = AT(2, 2);
-            const Temp_Type X113 = AT(2, 3);
-            const Temp_Type X114 = AT(2, 4);
-            const Temp_Type X115 = AT(2, 5);
-            const Temp_Type X116 = AT(2, 6);
-            const Temp_Type X117 = AT(2, 7);
-            const Temp_Type X120 = D(F(-0.074658f) * AT(1, 0) + F(0.513280f) * AT(3, 0) + F(0.768178f) * AT(5, 0) + F(-0.375330f) * AT(7, 0));
-            const Temp_Type X121 = D(F(-0.074658f) * AT(1, 1) + F(0.513280f) * AT(3, 1) + F(0.768178f) * AT(5, 1) + F(-0.375330f) * AT(7, 1));
-            const Temp_Type X122 = D(F(-0.074658f) * AT(1, 2) + F(0.513280f) * AT(3, 2) + F(0.768178f) * AT(5, 2) + F(-0.375330f) * AT(7, 2));
-            const Temp_Type X123 = D(F(-0.074658f) * AT(1, 3) + F(0.513280f) * AT(3, 3) + F(0.768178f) * AT(5, 3) + F(-0.375330f) * AT(7, 3));
-            const Temp_Type X124 = D(F(-0.074658f) * AT(1, 4) + F(0.513280f) * AT(3, 4) + F(0.768178f) * AT(5, 4) + F(-0.375330f) * AT(7, 4));
-            const Temp_Type X125 = D(F(-0.074658f) * AT(1, 5) + F(0.513280f) * AT(3, 5) + F(0.768178f) * AT(5, 5) + F(-0.375330f) * AT(7, 5));
-            const Temp_Type X126 = D(F(-0.074658f) * AT(1, 6) + F(0.513280f) * AT(3, 6) + F(0.768178f) * AT(5, 6) + F(-0.375330f) * AT(7, 6));
-            const Temp_Type X127 = D(F(-0.074658f) * AT(1, 7) + F(0.513280f) * AT(3, 7) + F(0.768178f) * AT(5, 7) + F(-0.375330f) * AT(7, 7));
-            const Temp_Type X130 = AT(6, 0);
-            const Temp_Type X131 = AT(6, 1);
-            const Temp_Type X132 = AT(6, 2);
-            const Temp_Type X133 = AT(6, 3);
-            const Temp_Type X134 = AT(6, 4);
-            const Temp_Type X135 = AT(6, 5);
-            const Temp_Type X136 = AT(6, 6);
-            const Temp_Type X137 = AT(6, 7);
-            // 80 muls 48 adds
-
-            // 4x4 = 4x8 times 8x4, matrix 1 is constant
-            R.at(0, 0) = X100;
-            R.at(0, 1) = D(X101 * F(0.415735f) + X103 * F(0.791065f) + X105 * F(-0.352443f) + X107 * F(0.277785f));
-            R.at(0, 2) = X104;
-            R.at(0, 3) = D(X101 * F(0.022887f) + X103 * F(-0.097545f) + X105 * F(0.490393f) + X107 * F(0.865723f));
-            R.at(1, 0) = X110;
-            R.at(1, 1) = D(X111 * F(0.415735f) + X113 * F(0.791065f) + X115 * F(-0.352443f) + X117 * F(0.277785f));
-            R.at(1, 2) = X114;
-            R.at(1, 3) = D(X111 * F(0.022887f) + X113 * F(-0.097545f) + X115 * F(0.490393f) + X117 * F(0.865723f));
-            R.at(2, 0) = X120;
-            R.at(2, 1) = D(X121 * F(0.415735f) + X123 * F(0.791065f) + X125 * F(-0.352443f) + X127 * F(0.277785f));
-            R.at(2, 2) = X124;
-            R.at(2, 3) = D(X121 * F(0.022887f) + X123 * F(-0.097545f) + X125 * F(0.490393f) + X127 * F(0.865723f));
-            R.at(3, 0) = X130;
-            R.at(3, 1) = D(X131 * F(0.415735f) + X133 * F(0.791065f) + X135 * F(-0.352443f) + X137 * F(0.277785f));
-            R.at(3, 2) = X134;
-            R.at(3, 3) = D(X131 * F(0.022887f) + X133 * F(-0.097545f) + X135 * F(0.490393f) + X137 * F(0.865723f));
-            // 40 muls 24 adds
-            // 4x4 = 4x8 times 8x4, matrix 1 is constant
-            S.at(0, 0) = D(X101 * F(0.906127f) + X103 * F(-0.318190f) + X105 * F(0.212608f) + X107 * F(-0.180240f));
-            S.at(0, 1) = X102;
-            S.at(0, 2) = D(X101 * F(-0.074658f) + X103 * F(0.513280f) + X105 * F(0.768178f) + X107 * F(-0.375330f));
-            S.at(0, 3) = X106;
-            S.at(1, 0) = D(X111 * F(0.906127f) + X113 * F(-0.318190f) + X115 * F(0.212608f) + X117 * F(-0.180240f));
-            S.at(1, 1) = X112;
-            S.at(1, 2) = D(X111 * F(-0.074658f) + X113 * F(0.513280f) + X115 * F(0.768178f) + X117 * F(-0.375330f));
-            S.at(1, 3) = X116;
-            S.at(2, 0) = D(X121 * F(0.906127f) + X123 * F(-0.318190f) + X125 * F(0.212608f) + X127 * F(-0.180240f));
-            S.at(2, 1) = X122;
-            S.at(2, 2) = D(X121 * F(-0.074658f) + X123 * F(0.513280f) + X125 * F(0.768178f) + X127 * F(-0.375330f));
-            S.at(2, 3) = X126;
-            S.at(3, 0) = D(X131 * F(0.906127f) + X133 * F(-0.318190f) + X135 * F(0.212608f) + X137 * F(-0.180240f));
-            S.at(3, 1) = X132;
-            S.at(3, 2) = D(X131 * F(-0.074658f) + X133 * F(0.513280f) + X135 * F(0.768178f) + X137 * F(-0.375330f));
-            S.at(3, 3) = X136;
-            // 40 muls 24 adds
-        }
-    };
-} // end namespace DCT_Upsample
-
-
-// Unconditionally frees all allocated m_blocks.
-void jpeg_decoder::free_all_blocks()
-{
-    delete(m_pStream);
-    m_pStream = nullptr;
-
-    for (mem_block *b = m_pMem_blocks; b; ) {
-        mem_block *n = b->m_pNext;
-        free(b);
-        b = n;
-    }
-    m_pMem_blocks = nullptr;
-}
-
-
-// This method handles all errors. It will never return.
-// It could easily be changed to use C++ exceptions.
-JPGD_NORETURN void jpeg_decoder::stop_decoding(jpgd_status status)
-{
-    m_error_code = status;
-    free_all_blocks();
-    longjmp(m_jmp_state, status);
-}
-
-
-void *jpeg_decoder::alloc(size_t nSize, bool zero)
-{
-    nSize = (JPGD_MAX(nSize, 1) + 3) & ~3;
-    char *rv = nullptr;
-    for (mem_block *b = m_pMem_blocks; b; b = b->m_pNext) {
-        if ((b->m_used_count + nSize) <= b->m_size) {
-            rv = b->m_data + b->m_used_count;
-            b->m_used_count += nSize;
-            break;
-        }
-    }
-    if (!rv) {
-        int capacity = JPGD_MAX(32768 - 256, (nSize + 2047) & ~2047);
-        mem_block *b = (mem_block*)malloc(sizeof(mem_block) + capacity);
-        if (!b) stop_decoding(JPGD_NOTENOUGHMEM);
-        b->m_pNext = m_pMem_blocks; m_pMem_blocks = b;
-        b->m_used_count = nSize;
-        b->m_size = capacity;
-        rv = b->m_data;
-    }
-    if (zero) memset(rv, 0, nSize);
-    return rv;
-}
-
-
-void jpeg_decoder::word_clear(void *p, uint16_t c, uint32_t n)
-{
-    uint8_t *pD = (uint8_t*)p;
-    const uint8_t l = c & 0xFF, h = (c >> 8) & 0xFF;
-    while (n) {
-        pD[0] = l; pD[1] = h; pD += 2;
-        n--;
-    }
-}
-
-
-// Refill the input buffer.
-// This method will sit in a loop until (A) the buffer is full or (B)
-// the stream's read() method reports and end of file condition.
-void jpeg_decoder::prep_in_buffer()
-{
-    m_in_buf_left = 0;
-    m_pIn_buf_ofs = m_in_buf;
-
-    if (m_eof_flag) return;
-
-    do {
-        int bytes_read = m_pStream->read(m_in_buf + m_in_buf_left, JPGD_IN_BUF_SIZE - m_in_buf_left, &m_eof_flag);
-        if (bytes_read == -1) stop_decoding(JPGD_STREAM_READ);
-        m_in_buf_left += bytes_read;
-    } while ((m_in_buf_left < JPGD_IN_BUF_SIZE) && (!m_eof_flag));
-
-    m_total_bytes_read += m_in_buf_left;
-
-    // Pad the end of the block with M_EOI (prevents the decompressor from going off the rails if the stream is invalid).
-    // (This dates way back to when this decompressor was written in C/asm, and the all-asm Huffman decoder did some fancy things to increase perf.)
-    word_clear(m_pIn_buf_ofs + m_in_buf_left, 0xD9FF, 64);
-}
-
-
-// Read a Huffman code table.
-void jpeg_decoder::read_dht_marker()
-{
-    int i, index, count;
-    uint8_t huff_num[17];
-    uint8_t huff_val[256];
-    uint32_t num_left = get_bits(16);
-
-    if (num_left < 2) stop_decoding(JPGD_BAD_DHT_MARKER);
-    num_left -= 2;
-
-    while (num_left) {
-        index = get_bits(8);
-        huff_num[0] = 0;
-        count = 0;
-
-        for (i = 1; i <= 16; i++) {
-            huff_num[i] = static_cast<uint8_t>(get_bits(8));
-            count += huff_num[i];
-        }
-
-        if (count > 255) stop_decoding(JPGD_BAD_DHT_COUNTS);
-
-        for (i = 0; i < count; i++)
-            huff_val[i] = static_cast<uint8_t>(get_bits(8));
-
-        i = 1 + 16 + count;
-
-        if (num_left < (uint32_t)i) stop_decoding(JPGD_BAD_DHT_MARKER);
-        num_left -= i;
-
-        if ((index & 0x10) > 0x10) stop_decoding(JPGD_BAD_DHT_INDEX);
-        index = (index & 0x0F) + ((index & 0x10) >> 4) * (JPGD_MAX_HUFF_TABLES >> 1);
-        if (index >= JPGD_MAX_HUFF_TABLES) stop_decoding(JPGD_BAD_DHT_INDEX);
-
-        if (!m_huff_num[index]) m_huff_num[index] = (uint8_t *)alloc(17);
-        if (!m_huff_val[index]) m_huff_val[index] = (uint8_t *)alloc(256);
-
-        m_huff_ac[index] = (index & 0x10) != 0;
-        memcpy(m_huff_num[index], huff_num, 17);
-        memcpy(m_huff_val[index], huff_val, 256);
-    }
-}
-
-
-// Read a quantization table.
-void jpeg_decoder::read_dqt_marker()
-{
-    int n, i, prec;
-    uint32_t temp;
-    uint32_t num_left = get_bits(16);
-    if (num_left < 2) stop_decoding(JPGD_BAD_DQT_MARKER);
-    num_left -= 2;
-
-    while (num_left) {
-        n = get_bits(8);
-        prec = n >> 4;
-        n &= 0x0F;
-
-        if (n >= JPGD_MAX_QUANT_TABLES) stop_decoding(JPGD_BAD_DQT_TABLE);
-
-        if (!m_quant[n]) m_quant[n] = (jpgd_quant_t *)alloc(64 * sizeof(jpgd_quant_t));
-
-        // read quantization entries, in zag order
-        for (i = 0; i < 64; i++) {
-            temp = get_bits(8);
-            if (prec) temp = (temp << 8) + get_bits(8);
-            m_quant[n][i] = static_cast<jpgd_quant_t>(temp);
-        }
-        i = 64 + 1;
-        if (prec) i += 64;
-        if (num_left < (uint32_t)i) stop_decoding(JPGD_BAD_DQT_LENGTH);
-        num_left -= i;
-    }
-}
-
-
-// Read the start of frame (SOF) marker.
-void jpeg_decoder::read_sof_marker()
-{
-    int i;
-    uint32_t num_left = get_bits(16);
-
-    if (get_bits(8) != 8) stop_decoding(JPGD_BAD_PRECISION);   /* precision: sorry, only 8-bit precision is supported right now */
-
-    m_image_y_size = get_bits(16);
-    if ((m_image_y_size < 1) || (m_image_y_size > JPGD_MAX_HEIGHT)) stop_decoding(JPGD_BAD_HEIGHT);
-
-    m_image_x_size = get_bits(16);
-    if ((m_image_x_size < 1) || (m_image_x_size > JPGD_MAX_WIDTH)) stop_decoding(JPGD_BAD_WIDTH);
-
-    m_comps_in_frame = get_bits(8);
-    if (m_comps_in_frame > JPGD_MAX_COMPONENTS) stop_decoding(JPGD_TOO_MANY_COMPONENTS);
-
-    if (num_left != (uint32_t)(m_comps_in_frame * 3 + 8)) stop_decoding(JPGD_BAD_SOF_LENGTH);
-
-    for (i = 0; i < m_comps_in_frame; i++) {
-        m_comp_ident[i]  = get_bits(8);
-        m_comp_h_samp[i] = get_bits(4);
-        m_comp_v_samp[i] = get_bits(4);
-        m_comp_quant[i]  = get_bits(8);
-    }
-}
-
-
-// Used to skip unrecognized markers.
-void jpeg_decoder::skip_variable_marker()
-{
-    uint32_t num_left = get_bits(16);
-    if (num_left < 2) stop_decoding(JPGD_BAD_VARIABLE_MARKER);
-    num_left -= 2;
-
-    while (num_left) {
-        get_bits(8);
-        num_left--;
-    }
-}
-
-
-// Read a define restart interval (DRI) marker.
-void jpeg_decoder::read_dri_marker()
-{
-    if (get_bits(16) != 4) stop_decoding(JPGD_BAD_DRI_LENGTH);
-    m_restart_interval = get_bits(16);
-}
-
-
-// Read a start of scan (SOS) marker.
-void jpeg_decoder::read_sos_marker()
-{
-    int i, ci, c, cc;
-    uint32_t num_left = get_bits(16);
-    int n = get_bits(8);
-
-    m_comps_in_scan = n;
-    num_left -= 3;
-
-    if ( (num_left != (uint32_t)(n * 2 + 3)) || (n < 1) || (n > JPGD_MAX_COMPS_IN_SCAN) ) stop_decoding(JPGD_BAD_SOS_LENGTH);
-
-    for (i = 0; i < n; i++) {
-        cc = get_bits(8);
-        c = get_bits(8);
-        num_left -= 2;
-
-        for (ci = 0; ci < m_comps_in_frame; ci++)
-          if (cc == m_comp_ident[ci]) break;
-
-        if (ci >= m_comps_in_frame) stop_decoding(JPGD_BAD_SOS_COMP_ID);
-
-        m_comp_list[i]    = ci;
-        m_comp_dc_tab[ci] = (c >> 4) & 15;
-        m_comp_ac_tab[ci] = (c & 15) + (JPGD_MAX_HUFF_TABLES >> 1);
-    }
-    m_spectral_start  = get_bits(8);
-    m_spectral_end    = get_bits(8);
-    m_successive_high = get_bits(4);
-    m_successive_low  = get_bits(4);
-
-    if (!m_progressive_flag) {
-        m_spectral_start = 0;
-        m_spectral_end = 63;
-    }
-    num_left -= 3;
-
-    while (num_left) {    /* read past whatever is num_left */
-        get_bits(8);
-        num_left--;
-    }
-}
-
-
-// Finds the next marker.
-int jpeg_decoder::next_marker()
-{
-    uint32_t c, bytes = 0;
-
-    do {
-        do {
-            bytes++;
-            c = get_bits(8);
-        } while (c != 0xFF);
-
-        do {
-            c = get_bits(8);
-        } while (c == 0xFF);
-    } while (c == 0);
-
-    // If bytes > 0 here, there where extra bytes before the marker (not good).
-    return c;
-}
-
-
-// Process markers. Returns when an SOFx, SOI, EOI, or SOS marker is
-// encountered.
-int jpeg_decoder::process_markers()
-{
-    int c;
-
-    for ( ; ; ) {
-        c = next_marker();
-        switch (c) {
-            case M_SOF0:
-            case M_SOF1:
-            case M_SOF2:
-            case M_SOF3:
-            case M_SOF5:
-            case M_SOF6:
-            case M_SOF7:
-      //      case M_JPG:
-            case M_SOF9:
-            case M_SOF10:
-            case M_SOF11:
-            case M_SOF13:
-            case M_SOF14:
-            case M_SOF15:
-            case M_SOI:
-            case M_EOI:
-            case M_SOS: return c;
-            case M_DHT: {
-                read_dht_marker();
-                break;
-            }
-            // No arithmitic support - dumb patents!
-            case M_DAC: {
-                stop_decoding(JPGD_NO_ARITHMITIC_SUPPORT);
-                break;
-            }
-            case M_DQT: {
-                read_dqt_marker();
-                break;
-            }
-            case M_DRI: {
-                read_dri_marker();
-                break;
-            }
-            //case M_APP0:  /* no need to read the JFIF marker */
-            case M_JPG:
-            case M_RST0:    /* no parameters */
-            case M_RST1:
-            case M_RST2:
-            case M_RST3:
-            case M_RST4:
-            case M_RST5:
-            case M_RST6:
-            case M_RST7:
-            case M_TEM: {
-                stop_decoding(JPGD_UNEXPECTED_MARKER);
-                break;
-            }
-            default: {   /* must be DNL, DHP, EXP, APPn, JPGn, COM, or RESn or APP0 */
-                skip_variable_marker();
-                break;
-            }
-        }
-    }
-}
-
-
-// Finds the start of image (SOI) marker.
-// This code is rather defensive: it only checks the first 512 bytes to avoid
-// false positives.
-void jpeg_decoder::locate_soi_marker()
-{
-    uint32_t lastchar = get_bits(8);
-    uint32_t thischar = get_bits(8);
-
-    /* ok if it's a normal JPEG file without a special header */
-    if ((lastchar == 0xFF) && (thischar == M_SOI)) return;
-
-    uint32_t bytesleft = 4096; //512;
-
-    while (true) {
-        if (--bytesleft == 0) stop_decoding(JPGD_NOT_JPEG);
-
-        lastchar = thischar;
-        thischar = get_bits(8);
-
-        if (lastchar == 0xFF) {
-          if (thischar == M_SOI) break;
-          else if (thischar == M_EOI) stop_decoding(JPGD_NOT_JPEG); // get_bits will keep returning M_EOI if we read past the end
-        }
-    }
-
-    // Check the next character after marker: if it's not 0xFF, it can't be the start of the next marker, so the file is bad.
-    thischar = (m_bit_buf >> 24) & 0xFF;
-    if (thischar != 0xFF) stop_decoding(JPGD_NOT_JPEG);
-}
-
-
-// Find a start of frame (SOF) marker.
-void jpeg_decoder::locate_sof_marker()
-{
-    locate_soi_marker();
-    int c = process_markers();
-
-    switch (c) {
-        case M_SOF2: m_progressive_flag = true;
-        case M_SOF0:  /* baseline DCT */
-        case M_SOF1: { /* extended sequential DCT */
-          read_sof_marker();
-          break;
-        }
-        case M_SOF9: {  /* Arithmitic coding */
-          stop_decoding(JPGD_NO_ARITHMITIC_SUPPORT);
-          break;
-        }
-        default: {
-          stop_decoding(JPGD_UNSUPPORTED_MARKER);
-          break;
-        }
-    }
-}
-
-
-// Find a start of scan (SOS) marker.
-int jpeg_decoder::locate_sos_marker()
-{
-    int c = process_markers();
-    if (c == M_EOI) return false;
-    else if (c != M_SOS) stop_decoding(JPGD_UNEXPECTED_MARKER);
-    read_sos_marker();
-    return true;
-}
-
-
-// Reset everything to default/uninitialized state.
-void jpeg_decoder::init(jpeg_decoder_stream *pStream)
-{
-    m_pMem_blocks = nullptr;
-    m_error_code = JPGD_SUCCESS;
-    m_ready_flag = false;
-    m_image_x_size = m_image_y_size = 0;
-    m_pStream = pStream;
-    m_progressive_flag = false;
-
-    memset(m_huff_ac, 0, sizeof(m_huff_ac));
-    memset(m_huff_num, 0, sizeof(m_huff_num));
-    memset(m_huff_val, 0, sizeof(m_huff_val));
-    memset(m_quant, 0, sizeof(m_quant));
-
-    m_scan_type = 0;
-    m_comps_in_frame = 0;
-
-    memset(m_comp_h_samp, 0, sizeof(m_comp_h_samp));
-    memset(m_comp_v_samp, 0, sizeof(m_comp_v_samp));
-    memset(m_comp_quant, 0, sizeof(m_comp_quant));
-    memset(m_comp_ident, 0, sizeof(m_comp_ident));
-    memset(m_comp_h_blocks, 0, sizeof(m_comp_h_blocks));
-    memset(m_comp_v_blocks, 0, sizeof(m_comp_v_blocks));
-
-    m_comps_in_scan = 0;
-    memset(m_comp_list, 0, sizeof(m_comp_list));
-    memset(m_comp_dc_tab, 0, sizeof(m_comp_dc_tab));
-    memset(m_comp_ac_tab, 0, sizeof(m_comp_ac_tab));
-
-    m_spectral_start = 0;
-    m_spectral_end = 0;
-    m_successive_low = 0;
-    m_successive_high = 0;
-    m_max_mcu_x_size = 0;
-    m_max_mcu_y_size = 0;
-    m_blocks_per_mcu = 0;
-    m_max_blocks_per_row = 0;
-    m_mcus_per_row = 0;
-    m_mcus_per_col = 0;
-    m_expanded_blocks_per_component = 0;
-    m_expanded_blocks_per_mcu = 0;
-    m_expanded_blocks_per_row = 0;
-    m_freq_domain_chroma_upsample = false;
-
-    memset(m_mcu_org, 0, sizeof(m_mcu_org));
-
-    m_total_lines_left = 0;
-    m_mcu_lines_left = 0;
-    m_real_dest_bytes_per_scan_line = 0;
-    m_dest_bytes_per_scan_line = 0;
-    m_dest_bytes_per_pixel = 0;
-
-    memset(m_pHuff_tabs, 0, sizeof(m_pHuff_tabs));
-
-    memset(m_dc_coeffs, 0, sizeof(m_dc_coeffs));
-    memset(m_ac_coeffs, 0, sizeof(m_ac_coeffs));
-    memset(m_block_y_mcu, 0, sizeof(m_block_y_mcu));
-
-    m_eob_run = 0;
-
-    memset(m_block_y_mcu, 0, sizeof(m_block_y_mcu));
-
-    m_pIn_buf_ofs = m_in_buf;
-    m_in_buf_left = 0;
-    m_eof_flag = false;
-    m_tem_flag = 0;
-
-    memset(m_in_buf_pad_start, 0, sizeof(m_in_buf_pad_start));
-    memset(m_in_buf, 0, sizeof(m_in_buf));
-    memset(m_in_buf_pad_end, 0, sizeof(m_in_buf_pad_end));
-
-    m_restart_interval = 0;
-    m_restarts_left    = 0;
-    m_next_restart_num = 0;
-
-    m_max_mcus_per_row = 0;
-    m_max_blocks_per_mcu = 0;
-    m_max_mcus_per_col = 0;
-
-    memset(m_last_dc_val, 0, sizeof(m_last_dc_val));
-    m_pMCU_coefficients = nullptr;
-    m_pSample_buf = nullptr;
-
-    m_total_bytes_read = 0;
-
-    m_pScan_line_0 = nullptr;
-    m_pScan_line_1 = nullptr;
-
-    // Ready the input buffer.
-    prep_in_buffer();
-
-    // Prime the bit buffer.
-    m_bits_left = 16;
-    m_bit_buf = 0;
-
-    get_bits(16);
-    get_bits(16);
-
-    for (int i = 0; i < JPGD_MAX_BLOCKS_PER_MCU; i++) {
-        m_mcu_block_max_zag[i] = 64;
-    }
-}
-
-#define SCALEBITS 16
-#define ONE_HALF  ((int) 1 << (SCALEBITS-1))
-#define FIX(x)    ((int) ((x) * (1L<<SCALEBITS) + 0.5f))
-
-
-// Create a few tables that allow us to quickly convert YCbCr to RGB.
-void jpeg_decoder::create_look_ups()
-{
-  for (int i = 0; i <= 255; i++) {
-      int k = i - 128;
-      m_crr[i] = ( FIX(1.40200f)  * k + ONE_HALF) >> SCALEBITS;
-      m_cbb[i] = ( FIX(1.77200f)  * k + ONE_HALF) >> SCALEBITS;
-      m_crg[i] = (-FIX(0.71414f)) * k;
-      m_cbg[i] = (-FIX(0.34414f)) * k + ONE_HALF;
-  }
-}
-
-
-// This method throws back into the stream any bytes that where read
-// into the bit buffer during initial marker scanning.
-void jpeg_decoder::fix_in_buffer()
-{
-    // In case any 0xFF's where pulled into the buffer during marker scanning.
-    JPGD_ASSERT((m_bits_left & 7) == 0);
-
-    if (m_bits_left == 16) stuff_char( (uint8_t)(m_bit_buf & 0xFF));
-    if (m_bits_left >= 8) stuff_char( (uint8_t)((m_bit_buf >> 8) & 0xFF));
-
-    stuff_char((uint8_t)((m_bit_buf >> 16) & 0xFF));
-    stuff_char((uint8_t)((m_bit_buf >> 24) & 0xFF));
-
-    m_bits_left = 16;
-    get_bits_no_markers(16);
-    get_bits_no_markers(16);
-}
-
-
-void jpeg_decoder::transform_mcu(int mcu_row)
-{
-    jpgd_block_t* pSrc_ptr = m_pMCU_coefficients;
-    uint8_t* pDst_ptr = m_pSample_buf + mcu_row * m_blocks_per_mcu * 64;
-
-    for (int mcu_block = 0; mcu_block < m_blocks_per_mcu; mcu_block++) {
-        idct(pSrc_ptr, pDst_ptr, m_mcu_block_max_zag[mcu_block]);
-        pSrc_ptr += 64;
-        pDst_ptr += 64;
-    }
-}
-
-
-static const uint8_t s_max_rc[64] =
-{
-    17, 18, 34, 50, 50, 51, 52, 52, 52, 68, 84, 84, 84, 84, 85, 86, 86, 86, 86, 86,
-    102, 118, 118, 118, 118, 118, 118, 119, 120, 120, 120, 120, 120, 120, 120, 136,
-    136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136,
-    136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136
-};
-
-
-void jpeg_decoder::transform_mcu_expand(int mcu_row)
-{
-    jpgd_block_t* pSrc_ptr = m_pMCU_coefficients;
-    uint8_t* pDst_ptr = m_pSample_buf + mcu_row * m_expanded_blocks_per_mcu * 64;
-
-    // Y IDCT
-    int mcu_block;
-    for (mcu_block = 0; mcu_block < m_expanded_blocks_per_component; mcu_block++) {
-        idct(pSrc_ptr, pDst_ptr, m_mcu_block_max_zag[mcu_block]);
-        pSrc_ptr += 64;
-        pDst_ptr += 64;
-    }
-
-    // Chroma IDCT, with upsampling
-    jpgd_block_t temp_block[64];
-
-    for (int i = 0; i < 2; i++) {
-        DCT_Upsample::Matrix44 P, Q, R, S;
-        JPGD_ASSERT(m_mcu_block_max_zag[mcu_block] >= 1);
-        JPGD_ASSERT(m_mcu_block_max_zag[mcu_block] <= 64);
-
-        int max_zag = m_mcu_block_max_zag[mcu_block++] - 1;
-        if (max_zag <= 0) max_zag = 0; // should never happen, only here to shut up static analysis
-
-        switch (s_max_rc[max_zag]) {
-            case 1*16+1:
-                DCT_Upsample::P_Q<1, 1>::calc(P, Q, pSrc_ptr);
-                DCT_Upsample::R_S<1, 1>::calc(R, S, pSrc_ptr);
-                break;
-            case 1*16+2:
-                DCT_Upsample::P_Q<1, 2>::calc(P, Q, pSrc_ptr);
-                DCT_Upsample::R_S<1, 2>::calc(R, S, pSrc_ptr);
-                break;
-            case 2*16+2:
-                DCT_Upsample::P_Q<2, 2>::calc(P, Q, pSrc_ptr);
-                DCT_Upsample::R_S<2, 2>::calc(R, S, pSrc_ptr);
-                break;
-            case 3*16+2:
-                DCT_Upsample::P_Q<3, 2>::calc(P, Q, pSrc_ptr);
-                DCT_Upsample::R_S<3, 2>::calc(R, S, pSrc_ptr);
-                break;
-            case 3*16+3:
-                DCT_Upsample::P_Q<3, 3>::calc(P, Q, pSrc_ptr);
-                DCT_Upsample::R_S<3, 3>::calc(R, S, pSrc_ptr);
-                break;
-            case 3*16+4:
-                DCT_Upsample::P_Q<3, 4>::calc(P, Q, pSrc_ptr);
-                DCT_Upsample::R_S<3, 4>::calc(R, S, pSrc_ptr);
-                break;
-            case 4*16+4:
-                DCT_Upsample::P_Q<4, 4>::calc(P, Q, pSrc_ptr);
-                DCT_Upsample::R_S<4, 4>::calc(R, S, pSrc_ptr);
-                break;
-            case 5*16+4:
-                DCT_Upsample::P_Q<5, 4>::calc(P, Q, pSrc_ptr);
-                DCT_Upsample::R_S<5, 4>::calc(R, S, pSrc_ptr);
-                break;
-            case 5*16+5:
-                DCT_Upsample::P_Q<5, 5>::calc(P, Q, pSrc_ptr);
-                DCT_Upsample::R_S<5, 5>::calc(R, S, pSrc_ptr);
-                break;
-            case 5*16+6:
-                DCT_Upsample::P_Q<5, 6>::calc(P, Q, pSrc_ptr);
-                DCT_Upsample::R_S<5, 6>::calc(R, S, pSrc_ptr);
-                break;
-            case 6*16+6:
-                DCT_Upsample::P_Q<6, 6>::calc(P, Q, pSrc_ptr);
-                DCT_Upsample::R_S<6, 6>::calc(R, S, pSrc_ptr);
-                break;
-            case 7*16+6:
-                DCT_Upsample::P_Q<7, 6>::calc(P, Q, pSrc_ptr);
-                DCT_Upsample::R_S<7, 6>::calc(R, S, pSrc_ptr);
-                break;
-            case 7*16+7:
-                DCT_Upsample::P_Q<7, 7>::calc(P, Q, pSrc_ptr);
-                DCT_Upsample::R_S<7, 7>::calc(R, S, pSrc_ptr);
-                break;
-            case 7*16+8:
-                DCT_Upsample::P_Q<7, 8>::calc(P, Q, pSrc_ptr);
-                DCT_Upsample::R_S<7, 8>::calc(R, S, pSrc_ptr);
-                break;
-            case 8*16+8:
-                DCT_Upsample::P_Q<8, 8>::calc(P, Q, pSrc_ptr);
-                DCT_Upsample::R_S<8, 8>::calc(R, S, pSrc_ptr);
-                break;
-            default:
-                JPGD_ASSERT(false);
-        }
-        DCT_Upsample::Matrix44 a(P + Q); P -= Q;
-        DCT_Upsample::Matrix44& b = P;
-        DCT_Upsample::Matrix44 c(R + S); R -= S;
-        DCT_Upsample::Matrix44& d = R;
-
-        DCT_Upsample::Matrix44::add_and_store(temp_block, a, c);
-        idct_4x4(temp_block, pDst_ptr);
-        pDst_ptr += 64;
-
-        DCT_Upsample::Matrix44::sub_and_store(temp_block, a, c);
-        idct_4x4(temp_block, pDst_ptr);
-        pDst_ptr += 64;
-
-        DCT_Upsample::Matrix44::add_and_store(temp_block, b, d);
-        idct_4x4(temp_block, pDst_ptr);
-        pDst_ptr += 64;
-
-        DCT_Upsample::Matrix44::sub_and_store(temp_block, b, d);
-        idct_4x4(temp_block, pDst_ptr);
-        pDst_ptr += 64;
-        pSrc_ptr += 64;
-    }
-}
-
-
-// Loads and dequantizes the next row of (already decoded) coefficients.
-// Progressive images only.
-void jpeg_decoder::load_next_row()
-{
-    int i;
-    jpgd_block_t *p;
-    jpgd_quant_t *q;
-    int mcu_row, mcu_block, row_block = 0;
-    int component_num, component_id;
-    int block_x_mcu[JPGD_MAX_COMPONENTS];
-
-    memset(block_x_mcu, 0, JPGD_MAX_COMPONENTS * sizeof(int));
-
-    for (mcu_row = 0; mcu_row < m_mcus_per_row; mcu_row++) {
-        int block_x_mcu_ofs = 0, block_y_mcu_ofs = 0;
-
-        for (mcu_block = 0; mcu_block < m_blocks_per_mcu; mcu_block++) {
-            component_id = m_mcu_org[mcu_block];
-            q = m_quant[m_comp_quant[component_id]];
-            p = m_pMCU_coefficients + 64 * mcu_block;
-
-            jpgd_block_t* pAC = coeff_buf_getp(m_ac_coeffs[component_id], block_x_mcu[component_id] + block_x_mcu_ofs, m_block_y_mcu[component_id] + block_y_mcu_ofs);
-            jpgd_block_t* pDC = coeff_buf_getp(m_dc_coeffs[component_id], block_x_mcu[component_id] + block_x_mcu_ofs, m_block_y_mcu[component_id] + block_y_mcu_ofs);
-            p[0] = pDC[0];
-            memcpy(&p[1], &pAC[1], 63 * sizeof(jpgd_block_t));
-
-            for (i = 63; i > 0; i--) {
-                if (p[g_ZAG[i]]) break;
-            }
-
-            m_mcu_block_max_zag[mcu_block] = i + 1;
-
-            for ( ; i >= 0; i--) {
-                if (p[g_ZAG[i]]) {
-                    p[g_ZAG[i]] = static_cast<jpgd_block_t>(p[g_ZAG[i]] * q[i]);
-                }
-            }
-
-            row_block++;
-
-            if (m_comps_in_scan == 1) block_x_mcu[component_id]++;
-            else {
-                if (++block_x_mcu_ofs == m_comp_h_samp[component_id]) block_x_mcu_ofs = 0;
-                if (++block_y_mcu_ofs == m_comp_v_samp[component_id]) {
-                    block_y_mcu_ofs = 0;
-                    block_x_mcu[component_id] += m_comp_h_samp[component_id];
-                }
-            }
-        }
-        if (m_freq_domain_chroma_upsample) transform_mcu_expand(mcu_row);
-        else transform_mcu(mcu_row);
-    }
-    if (m_comps_in_scan == 1) m_block_y_mcu[m_comp_list[0]]++;
-    else {
-        for (component_num = 0; component_num < m_comps_in_scan; component_num++) {
-            component_id = m_comp_list[component_num];
-            m_block_y_mcu[component_id] += m_comp_v_samp[component_id];
-        }
-    }
-}
-
-
-// Restart interval processing.
-void jpeg_decoder::process_restart()
-{
-    int i;
-    int c = 0;
-
-    // Align to a byte boundry
-    // FIXME: Is this really necessary? get_bits_no_markers() never reads in markers!
-    //get_bits_no_markers(m_bits_left & 7);
-
-    // Let's scan a little bit to find the marker, but not _too_ far.
-    // 1536 is a "fudge factor" that determines how much to scan.
-    for (i = 1536; i > 0; i--) {
-        if (get_char() == 0xFF) break;
-    }
-    if (i == 0) stop_decoding(JPGD_BAD_RESTART_MARKER);
-
-    for ( ; i > 0; i--) {
-        if ((c = get_char()) != 0xFF) break;
-    }
-    if (i == 0) stop_decoding(JPGD_BAD_RESTART_MARKER);
-
-    // Is it the expected marker? If not, something bad happened.
-    if (c != (m_next_restart_num + M_RST0)) stop_decoding(JPGD_BAD_RESTART_MARKER);
-
-    // Reset each component's DC prediction values.
-    memset(&m_last_dc_val, 0, m_comps_in_frame * sizeof(uint32_t));
-
-    m_eob_run = 0;
-    m_restarts_left = m_restart_interval;
-    m_next_restart_num = (m_next_restart_num + 1) & 7;
-
-    // Get the bit buffer going again...
-    m_bits_left = 16;
-    get_bits_no_markers(16);
-    get_bits_no_markers(16);
-}
-
-
-static inline int dequantize_ac(int c, int q)
-{
-    c *= q;
-    return c;
-}
-
-// Decodes and dequantizes the next row of coefficients.
-void jpeg_decoder::decode_next_row()
-{
-    int row_block = 0;
-
-    for (int mcu_row = 0; mcu_row < m_mcus_per_row; mcu_row++) {
-        if ((m_restart_interval) && (m_restarts_left == 0)) process_restart();
-
-        jpgd_block_t* p = m_pMCU_coefficients;
-
-        for (int mcu_block = 0; mcu_block < m_blocks_per_mcu; mcu_block++, p += 64) {
-            int component_id = m_mcu_org[mcu_block];
-            jpgd_quant_t* q = m_quant[m_comp_quant[component_id]];
-
-            int r, s;
-            s = huff_decode(m_pHuff_tabs[m_comp_dc_tab[component_id]], r);
-            s = JPGD_HUFF_EXTEND(r, s);
-
-            m_last_dc_val[component_id] = (s += m_last_dc_val[component_id]);
-
-            p[0] = static_cast<jpgd_block_t>(s * q[0]);
-
-            int prev_num_set = m_mcu_block_max_zag[mcu_block];
-            huff_tables *pH = m_pHuff_tabs[m_comp_ac_tab[component_id]];
-            int k;
-            for (k = 1; k < 64; k++) {
-                int extra_bits;
-                s = huff_decode(pH, extra_bits);
-                r = s >> 4;
-                s &= 15;
-
-                if (s) {
-                    if (r) {
-                        if ((k + r) > 63) stop_decoding(JPGD_DECODE_ERROR);
-                        if (k < prev_num_set) {
-                            int n = JPGD_MIN(r, prev_num_set - k);
-                            int kt = k;
-                            while (n--) p[g_ZAG[kt++]] = 0;
-                        }
-                        k += r;
-                    }
-                    s = JPGD_HUFF_EXTEND(extra_bits, s);
-                    JPGD_ASSERT(k < 64);
-                    p[g_ZAG[k]] = static_cast<jpgd_block_t>(dequantize_ac(s, q[k])); //s * q[k];
-                } else {
-                    if (r == 15) {
-                        if ((k + 16) > 64) stop_decoding(JPGD_DECODE_ERROR);
-                        if (k < prev_num_set) {
-                            int n = JPGD_MIN(16, prev_num_set - k);
-                            int kt = k;
-                            while (n--) {
-                                JPGD_ASSERT(kt <= 63);
-                                p[g_ZAG[kt++]] = 0;
-                            }
-                        }
-                        k += 16 - 1; // - 1 because the loop counter is k
-                        JPGD_ASSERT(p[g_ZAG[k]] == 0);
-                    } else  break;
-                }
-            }
-
-            if (k < prev_num_set) {
-                int kt = k;
-                while (kt < prev_num_set) p[g_ZAG[kt++]] = 0;
-            }
-
-            m_mcu_block_max_zag[mcu_block] = k;
-            row_block++;
-        }
-        if (m_freq_domain_chroma_upsample) transform_mcu_expand(mcu_row);
-        else transform_mcu(mcu_row);
-        m_restarts_left--;
-    }
-}
-
-
-// YCbCr H1V1 (1x1:1:1, 3 m_blocks per MCU) to RGB
-void jpeg_decoder::H1V1Convert()
-{
-    int row = m_max_mcu_y_size - m_mcu_lines_left;
-    uint8_t *d = m_pScan_line_0;
-    uint8_t *s = m_pSample_buf + row * 8;
-
-    for (int i = m_max_mcus_per_row; i > 0; i--) {
-        for (int j = 0; j < 8; j++) {
-            int y = s[j];
-            int cb = s[64+j];
-            int cr = s[128+j];
-
-            d[0] = clamp(y + m_crr[cr]);
-            d[1] = clamp(y + ((m_crg[cr] + m_cbg[cb]) >> 16));
-            d[2] = clamp(y + m_cbb[cb]);
-            d[3] = 255;
-            d += 4;
-        }
-        s += 64*3;
-    }
-}
-
-
-// YCbCr H2V1 (2x1:1:1, 4 m_blocks per MCU) to RGB
-void jpeg_decoder::H2V1Convert()
-{
-    int row = m_max_mcu_y_size - m_mcu_lines_left;
-    uint8_t *d0 = m_pScan_line_0;
-    uint8_t *y = m_pSample_buf + row * 8;
-    uint8_t *c = m_pSample_buf + 2*64 + row * 8;
-
-    for (int i = m_max_mcus_per_row; i > 0; i--) {
-        for (int l = 0; l < 2; l++) {
-            for (int j = 0; j < 4; j++) {
-                int cb = c[0];
-                int cr = c[64];
-
-                int rc = m_crr[cr];
-                int gc = ((m_crg[cr] + m_cbg[cb]) >> 16);
-                int bc = m_cbb[cb];
-
-                int yy = y[j<<1];
-                d0[0] = clamp(yy+rc);
-                d0[1] = clamp(yy+gc);
-                d0[2] = clamp(yy+bc);
-                d0[3] = 255;
-
-                yy = y[(j<<1)+1];
-                d0[4] = clamp(yy+rc);
-                d0[5] = clamp(yy+gc);
-                d0[6] = clamp(yy+bc);
-                d0[7] = 255;
-                d0 += 8;
-                c++;
-            }
-            y += 64;
-        }
-        y += 64*4 - 64*2;
-        c += 64*4 - 8;
-    }
-}
-
-
-// YCbCr H2V1 (1x2:1:1, 4 m_blocks per MCU) to RGB
-void jpeg_decoder::H1V2Convert()
-{
-    int row = m_max_mcu_y_size - m_mcu_lines_left;
-    uint8_t *d0 = m_pScan_line_0;
-    uint8_t *d1 = m_pScan_line_1;
-    uint8_t *y;
-    uint8_t *c;
-
-    if (row < 8) y = m_pSample_buf + row * 8;
-    else y = m_pSample_buf + 64*1 + (row & 7) * 8;
-
-    c = m_pSample_buf + 64*2 + (row >> 1) * 8;
-
-    for (int i = m_max_mcus_per_row; i > 0; i--) {
-        for (int j = 0; j < 8; j++) {
-            int cb = c[0+j];
-            int cr = c[64+j];
-
-            int rc = m_crr[cr];
-            int gc = ((m_crg[cr] + m_cbg[cb]) >> 16);
-            int bc = m_cbb[cb];
-
-            int yy = y[j];
-            d0[0] = clamp(yy+rc);
-            d0[1] = clamp(yy+gc);
-            d0[2] = clamp(yy+bc);
-            d0[3] = 255;
-
-            yy = y[8+j];
-            d1[0] = clamp(yy+rc);
-            d1[1] = clamp(yy+gc);
-            d1[2] = clamp(yy+bc);
-            d1[3] = 255;
-
-            d0 += 4;
-            d1 += 4;
-        }
-        y += 64*4;
-        c += 64*4;
-    }
-}
-
-
-// YCbCr H2V2 (2x2:1:1, 6 m_blocks per MCU) to RGB
-void jpeg_decoder::H2V2Convert()
-{
-    int row = m_max_mcu_y_size - m_mcu_lines_left;
-    uint8_t *d0 = m_pScan_line_0;
-    uint8_t *d1 = m_pScan_line_1;
-    uint8_t *y;
-    uint8_t *c;
-
-    if (row < 8) y = m_pSample_buf + row * 8;
-    else y = m_pSample_buf + 64*2 + (row & 7) * 8;
-
-    c = m_pSample_buf + 64*4 + (row >> 1) * 8;
-
-    for (int i = m_max_mcus_per_row; i > 0; i--) {
-        for (int l = 0; l < 2; l++) {
-            for (int j = 0; j < 8; j += 2) {
-                int cb = c[0];
-                int cr = c[64];
-
-                int rc = m_crr[cr];
-                int gc = ((m_crg[cr] + m_cbg[cb]) >> 16);
-                int bc = m_cbb[cb];
-
-                int yy = y[j];
-                d0[0] = clamp(yy+rc);
-                d0[1] = clamp(yy+gc);
-                d0[2] = clamp(yy+bc);
-                d0[3] = 255;
-
-                yy = y[j+1];
-                d0[4] = clamp(yy+rc);
-                d0[5] = clamp(yy+gc);
-                d0[6] = clamp(yy+bc);
-                d0[7] = 255;
-
-                yy = y[j+8];
-                d1[0] = clamp(yy+rc);
-                d1[1] = clamp(yy+gc);
-                d1[2] = clamp(yy+bc);
-                d1[3] = 255;
-
-                yy = y[j+8+1];
-                d1[4] = clamp(yy+rc);
-                d1[5] = clamp(yy+gc);
-                d1[6] = clamp(yy+bc);
-                d1[7] = 255;
-
-                d0 += 8;
-                d1 += 8;
-
-                c++;
-            }
-            y += 64;
-        }
-        y += 64*6 - 64*2;
-        c += 64*6 - 8;
-    }
-}
-
-
-// Y (1 block per MCU) to 8-bit grayscale
-void jpeg_decoder::gray_convert()
-{
-    int row = m_max_mcu_y_size - m_mcu_lines_left;
-    uint8_t *d = m_pScan_line_0;
-    uint8_t *s = m_pSample_buf + row * 8;
-
-    for (int i = m_max_mcus_per_row; i > 0; i--) {
-        *(uint32_t *)d = *(uint32_t *)s;
-        *(uint32_t *)(&d[4]) = *(uint32_t *)(&s[4]);
-        s += 64;
-        d += 8;
-    }
-}
-
-
-void jpeg_decoder::expanded_convert()
-{
-    int row = m_max_mcu_y_size - m_mcu_lines_left;
-    uint8_t* Py = m_pSample_buf + (row / 8) * 64 * m_comp_h_samp[0] + (row & 7) * 8;
-    uint8_t* d = m_pScan_line_0;
-
-    for (int i = m_max_mcus_per_row; i > 0; i--) {
-        for (int k = 0; k < m_max_mcu_x_size; k += 8) {
-            const int Y_ofs = k * 8;
-            const int Cb_ofs = Y_ofs + 64 * m_expanded_blocks_per_component;
-            const int Cr_ofs = Y_ofs + 64 * m_expanded_blocks_per_component * 2;
-            for (int j = 0; j < 8; j++) {
-                int y = Py[Y_ofs + j];
-                int cb = Py[Cb_ofs + j];
-                int cr = Py[Cr_ofs + j];
-
-                d[0] = clamp(y + m_crr[cr]);
-                d[1] = clamp(y + ((m_crg[cr] + m_cbg[cb]) >> 16));
-                d[2] = clamp(y + m_cbb[cb]);
-                d[3] = 255;
-
-                d += 4;
-            }
-        }
-        Py += 64 * m_expanded_blocks_per_mcu;
-    }
-}
-
-
-// Find end of image (EOI) marker, so we can return to the user the exact size of the input stream.
-void jpeg_decoder::find_eoi()
-{
-    if (!m_progressive_flag) {
-        // Attempt to read the EOI marker.
-        //get_bits_no_markers(m_bits_left & 7);
-
-        // Prime the bit buffer
-        m_bits_left = 16;
-        get_bits(16);
-        get_bits(16);
-
-        // The next marker _should_ be EOI
-        process_markers();
-    }
-    m_total_bytes_read -= m_in_buf_left;
-}
-
-
-int jpeg_decoder::decode(const void** pScan_line, uint32_t* pScan_line_len)
-{
-    if ((m_error_code) || (!m_ready_flag)) return JPGD_FAILED;
-    if (m_total_lines_left == 0) return JPGD_DONE;
-    if (m_mcu_lines_left == 0) {
-        if (setjmp(m_jmp_state)) return JPGD_FAILED;
-        if (m_progressive_flag) load_next_row();
-        else decode_next_row();
-        // Find the EOI marker if that was the last row.
-        if (m_total_lines_left <= m_max_mcu_y_size) find_eoi();
-        m_mcu_lines_left = m_max_mcu_y_size;
-    }
-
-    if (m_freq_domain_chroma_upsample) {
-        expanded_convert();
-        *pScan_line = m_pScan_line_0;
-    } else {
-        switch (m_scan_type) {
-            case JPGD_YH2V2: {
-                if ((m_mcu_lines_left & 1) == 0) {
-                    H2V2Convert();
-                    *pScan_line = m_pScan_line_0;
-                }
-              else *pScan_line = m_pScan_line_1;
-              break;
-            }
-            case JPGD_YH2V1: {
-                H2V1Convert();
-                *pScan_line = m_pScan_line_0;
-                break;
-            }
-            case JPGD_YH1V2: {
-                if ((m_mcu_lines_left & 1) == 0) {
-                    H1V2Convert();
-                    *pScan_line = m_pScan_line_0;
-                } else *pScan_line = m_pScan_line_1;
-                break;
-            }
-            case JPGD_YH1V1: {
-                H1V1Convert();
-                *pScan_line = m_pScan_line_0;
-                break;
-            }
-            case JPGD_GRAYSCALE: {
-                gray_convert();
-                *pScan_line = m_pScan_line_0;
-                break;
-            }
-        }
-    }
-
-    *pScan_line_len = m_real_dest_bytes_per_scan_line;
-    m_mcu_lines_left--;
-    m_total_lines_left--;
-
-    return JPGD_SUCCESS;
-}
-
-
-// Creates the tables needed for efficient Huffman decoding.
-void jpeg_decoder::make_huff_table(int index, huff_tables *pH)
-{
-    int p, i, l, si;
-    uint8_t huffsize[257];
-    uint32_t huffcode[257];
-    uint32_t code;
-    uint32_t subtree;
-    int code_size;
-    int lastp;
-    int nextfreeentry;
-    int currententry;
-
-    pH->ac_table = m_huff_ac[index] != 0;
-    p = 0;
-
-    for (l = 1; l <= 16; l++)  {
-        for (i = 1; i <= m_huff_num[index][l]; i++) {
-            huffsize[p++] = static_cast<uint8_t>(l);
-        }
-    }
-
-    huffsize[p] = 0;
-    lastp = p;
-    code = 0;
-    si = huffsize[0];
-    p = 0;
-
-    while (huffsize[p]) {
-        while (huffsize[p] == si) {
-            huffcode[p++] = code;
-            code++;
-        }
-        code <<= 1;
-        si++;
-    }
-
-    memset(pH->look_up, 0, sizeof(pH->look_up));
-    memset(pH->look_up2, 0, sizeof(pH->look_up2));
-    memset(pH->tree, 0, sizeof(pH->tree));
-    memset(pH->code_size, 0, sizeof(pH->code_size));
-
-    nextfreeentry = -1;
-    p = 0;
-
-    while (p < lastp) {
-        i = m_huff_val[index][p];
-        code = huffcode[p];
-        code_size = huffsize[p];
-        pH->code_size[i] = static_cast<uint8_t>(code_size);
-
-        if (code_size <= 8) {
-            code <<= (8 - code_size);
-            for (l = 1 << (8 - code_size); l > 0; l--) {
-                JPGD_ASSERT(i < 256);
-                pH->look_up[code] = i;
-                bool has_extrabits = false;
-                int extra_bits = 0;
-                int num_extra_bits = i & 15;
-                int bits_to_fetch = code_size;
-
-                if (num_extra_bits) {
-                    int total_codesize = code_size + num_extra_bits;
-                    if (total_codesize <= 8) {
-                        has_extrabits = true;
-                        extra_bits = ((1 << num_extra_bits) - 1) & (code >> (8 - total_codesize));
-                        JPGD_ASSERT(extra_bits <= 0x7FFF);
-                        bits_to_fetch += num_extra_bits;
-                    }
-                }
-                if (!has_extrabits) pH->look_up2[code] = i | (bits_to_fetch << 8);
-                else pH->look_up2[code] = i | 0x8000 | (extra_bits << 16) | (bits_to_fetch << 8);
-                code++;
-            }
-        } else {
-            subtree = (code >> (code_size - 8)) & 0xFF;
-            currententry = pH->look_up[subtree];
-
-            if (currententry == 0) {
-                pH->look_up[subtree] = currententry = nextfreeentry;
-                pH->look_up2[subtree] = currententry = nextfreeentry;
-                nextfreeentry -= 2;
-            }
-
-            code <<= (16 - (code_size - 8));
-
-            for (l = code_size; l > 9; l--) {
-                if ((code & 0x8000) == 0) currententry--;
-                if (pH->tree[-currententry - 1] == 0) {
-                    pH->tree[-currententry - 1] = nextfreeentry;
-                    currententry = nextfreeentry;
-                    nextfreeentry -= 2;
-                } else currententry = pH->tree[-currententry - 1];
-                code <<= 1;
-            }
-            if ((code & 0x8000) == 0) currententry--;
-            pH->tree[-currententry - 1] = i;
-        }
-        p++;
-    }
-}
-
-
-// Verifies the quantization tables needed for this scan are available.
-void jpeg_decoder::check_quant_tables()
-{
-    for (int i = 0; i < m_comps_in_scan; i++) {
-        if (m_quant[m_comp_quant[m_comp_list[i]]] == nullptr) stop_decoding(JPGD_UNDEFINED_QUANT_TABLE);
-    }
-}
-
-
-// Verifies that all the Huffman tables needed for this scan are available.
-void jpeg_decoder::check_huff_tables()
-{
-    for (int i = 0; i < m_comps_in_scan; i++) {
-      if ((m_spectral_start == 0) && (m_huff_num[m_comp_dc_tab[m_comp_list[i]]] == nullptr)) stop_decoding(JPGD_UNDEFINED_HUFF_TABLE);
-      if ((m_spectral_end > 0) && (m_huff_num[m_comp_ac_tab[m_comp_list[i]]] == nullptr)) stop_decoding(JPGD_UNDEFINED_HUFF_TABLE);
-    }
-
-    for (int i = 0; i < JPGD_MAX_HUFF_TABLES; i++) {
-        if (m_huff_num[i]) {
-            if (!m_pHuff_tabs[i]) m_pHuff_tabs[i] = (huff_tables *)alloc(sizeof(huff_tables));
-            make_huff_table(i, m_pHuff_tabs[i]);
-        }
-    }
-}
-
-
-// Determines the component order inside each MCU.
-// Also calcs how many MCU's are on each row, etc.
-void jpeg_decoder::calc_mcu_block_order()
-{
-    int component_num, component_id;
-    int max_h_samp = 0, max_v_samp = 0;
-
-    for (component_id = 0; component_id < m_comps_in_frame; component_id++) {
-        if (m_comp_h_samp[component_id] > max_h_samp) {
-          max_h_samp = m_comp_h_samp[component_id];
-        }
-        if (m_comp_v_samp[component_id] > max_v_samp) {
-          max_v_samp = m_comp_v_samp[component_id];
-        }
-    }
-
-    for (component_id = 0; component_id < m_comps_in_frame; component_id++) {
-        m_comp_h_blocks[component_id] = ((((m_image_x_size * m_comp_h_samp[component_id]) + (max_h_samp - 1)) / max_h_samp) + 7) / 8;
-        m_comp_v_blocks[component_id] = ((((m_image_y_size * m_comp_v_samp[component_id]) + (max_v_samp - 1)) / max_v_samp) + 7) / 8;
-    }
-
-    if (m_comps_in_scan == 1) {
-        m_mcus_per_row = m_comp_h_blocks[m_comp_list[0]];
-        m_mcus_per_col = m_comp_v_blocks[m_comp_list[0]];
-    } else {
-        m_mcus_per_row = (((m_image_x_size + 7) / 8) + (max_h_samp - 1)) / max_h_samp;
-        m_mcus_per_col = (((m_image_y_size + 7) / 8) + (max_v_samp - 1)) / max_v_samp;
-    }
-
-    if (m_comps_in_scan == 1) {
-        m_mcu_org[0] = m_comp_list[0];
-        m_blocks_per_mcu = 1;
-    } else {
-        m_blocks_per_mcu = 0;
-
-        for (component_num = 0; component_num < m_comps_in_scan; component_num++) {
-            int num_blocks;
-            component_id = m_comp_list[component_num];
-            num_blocks = m_comp_h_samp[component_id] * m_comp_v_samp[component_id];
-            while (num_blocks--) m_mcu_org[m_blocks_per_mcu++] = component_id;
-        }
-    }
-}
-
-
-// Starts a new scan.
-int jpeg_decoder::init_scan()
-{
-    if (!locate_sos_marker()) return false;
-
-    calc_mcu_block_order();
-    check_huff_tables();
-    check_quant_tables();
-
-    memset(m_last_dc_val, 0, m_comps_in_frame * sizeof(uint32_t));
-
-    m_eob_run = 0;
-
-    if (m_restart_interval) {
-        m_restarts_left = m_restart_interval;
-        m_next_restart_num = 0;
-    }
-    fix_in_buffer();
-    return true;
-}
-
-
-// Starts a frame. Determines if the number of components or sampling factors
-// are supported.
-void jpeg_decoder::init_frame()
-{
-    int i;
-
-    if (m_comps_in_frame == 1) {
-        if ((m_comp_h_samp[0] != 1) || (m_comp_v_samp[0] != 1)) stop_decoding(JPGD_UNSUPPORTED_SAMP_FACTORS);
-        m_scan_type = JPGD_GRAYSCALE;
-        m_max_blocks_per_mcu = 1;
-        m_max_mcu_x_size = 8;
-        m_max_mcu_y_size = 8;
-    } else if (m_comps_in_frame == 3) {
-        if (((m_comp_h_samp[1] != 1) || (m_comp_v_samp[1] != 1)) || ((m_comp_h_samp[2] != 1) || (m_comp_v_samp[2] != 1)))
-            stop_decoding(JPGD_UNSUPPORTED_SAMP_FACTORS);
-
-        if ((m_comp_h_samp[0] == 1) && (m_comp_v_samp[0] == 1)) {
-            m_scan_type = JPGD_YH1V1;
-            m_max_blocks_per_mcu = 3;
-            m_max_mcu_x_size = 8;
-            m_max_mcu_y_size = 8;
-        } else if ((m_comp_h_samp[0] == 2) && (m_comp_v_samp[0] == 1)) {
-            m_scan_type = JPGD_YH2V1;
-            m_max_blocks_per_mcu = 4;
-            m_max_mcu_x_size = 16;
-            m_max_mcu_y_size = 8;
-        } else if ((m_comp_h_samp[0] == 1) && (m_comp_v_samp[0] == 2)) {
-            m_scan_type = JPGD_YH1V2;
-            m_max_blocks_per_mcu = 4;
-            m_max_mcu_x_size = 8;
-            m_max_mcu_y_size = 16;
-        } else if ((m_comp_h_samp[0] == 2) && (m_comp_v_samp[0] == 2)) {
-            m_scan_type = JPGD_YH2V2;
-            m_max_blocks_per_mcu = 6;
-            m_max_mcu_x_size = 16;
-            m_max_mcu_y_size = 16;
-        } else stop_decoding(JPGD_UNSUPPORTED_SAMP_FACTORS);
-    } else stop_decoding(JPGD_UNSUPPORTED_COLORSPACE);
-
-    m_max_mcus_per_row = (m_image_x_size + (m_max_mcu_x_size - 1)) / m_max_mcu_x_size;
-    m_max_mcus_per_col = (m_image_y_size + (m_max_mcu_y_size - 1)) / m_max_mcu_y_size;
-
-    // These values are for the *destination* pixels: after conversion.
-    if (m_scan_type == JPGD_GRAYSCALE) m_dest_bytes_per_pixel = 1;
-    else m_dest_bytes_per_pixel = 4;
-
-    m_dest_bytes_per_scan_line = ((m_image_x_size + 15) & 0xFFF0) * m_dest_bytes_per_pixel;
-    m_real_dest_bytes_per_scan_line = (m_image_x_size * m_dest_bytes_per_pixel);
-
-    // Initialize two scan line buffers.
-    m_pScan_line_0 = (uint8_t *)alloc(m_dest_bytes_per_scan_line, true);
-    if ((m_scan_type == JPGD_YH1V2) || (m_scan_type == JPGD_YH2V2)) {
-        m_pScan_line_1 = (uint8_t *)alloc(m_dest_bytes_per_scan_line, true);
-    }
-
-    m_max_blocks_per_row = m_max_mcus_per_row * m_max_blocks_per_mcu;
-
-    // Should never happen
-    if (m_max_blocks_per_row > JPGD_MAX_BLOCKS_PER_ROW) stop_decoding(JPGD_ASSERTION_ERROR);
-
-    // Allocate the coefficient buffer, enough for one MCU
-    m_pMCU_coefficients = (jpgd_block_t*)alloc(m_max_blocks_per_mcu * 64 * sizeof(jpgd_block_t));
-
-    for (i = 0; i < m_max_blocks_per_mcu; i++) {
-        m_mcu_block_max_zag[i] = 64;
-    }
-
-    m_expanded_blocks_per_component = m_comp_h_samp[0] * m_comp_v_samp[0];
-    m_expanded_blocks_per_mcu = m_expanded_blocks_per_component * m_comps_in_frame;
-    m_expanded_blocks_per_row = m_max_mcus_per_row * m_expanded_blocks_per_mcu;
-    // Freq. domain chroma upsampling is only supported for H2V2 subsampling factor (the most common one I've seen).
-    m_freq_domain_chroma_upsample = false;
-#if JPGD_SUPPORT_FREQ_DOMAIN_UPSAMPLING
-    m_freq_domain_chroma_upsample = (m_expanded_blocks_per_mcu == 4*3);
-#endif
-
-    if (m_freq_domain_chroma_upsample)
-        m_pSample_buf = (uint8_t *)alloc(m_expanded_blocks_per_row * 64);
-    else
-        m_pSample_buf = (uint8_t *)alloc(m_max_blocks_per_row * 64);
-
-    m_total_lines_left = m_image_y_size;
-    m_mcu_lines_left = 0;
-    create_look_ups();
-}
-
-
-// The coeff_buf series of methods originally stored the coefficients
-// into a "virtual" file which was located in EMS, XMS, or a disk file. A cache
-// was used to make this process more efficient. Now, we can store the entire
-// thing in RAM.
-jpeg_decoder::coeff_buf* jpeg_decoder::coeff_buf_open(int block_num_x, int block_num_y, int block_len_x, int block_len_y)
-{
-    coeff_buf* cb = (coeff_buf*)alloc(sizeof(coeff_buf));
-    cb->block_num_x = block_num_x;
-    cb->block_num_y = block_num_y;
-    cb->block_len_x = block_len_x;
-    cb->block_len_y = block_len_y;
-    cb->block_size = (block_len_x * block_len_y) * sizeof(jpgd_block_t);
-    cb->pData = (uint8_t *)alloc(cb->block_size * block_num_x * block_num_y, true);
-    return cb;
-}
-
-
-inline jpgd_block_t *jpeg_decoder::coeff_buf_getp(coeff_buf *cb, int block_x, int block_y)
-{
-    JPGD_ASSERT((block_x < cb->block_num_x) && (block_y < cb->block_num_y));
-    return (jpgd_block_t *)(cb->pData + block_x * cb->block_size + block_y * (cb->block_size * cb->block_num_x));
-}
-
-
-// The following methods decode the various types of m_blocks encountered
-// in progressively encoded images.
-void jpeg_decoder::decode_block_dc_first(jpeg_decoder *pD, int component_id, int block_x, int block_y)
-{
-    int s, r;
-    jpgd_block_t *p = pD->coeff_buf_getp(pD->m_dc_coeffs[component_id], block_x, block_y);
-
-    if ((s = pD->huff_decode(pD->m_pHuff_tabs[pD->m_comp_dc_tab[component_id]])) != 0) {
-        r = pD->get_bits_no_markers(s);
-        s = JPGD_HUFF_EXTEND(r, s);
-    }
-    pD->m_last_dc_val[component_id] = (s += pD->m_last_dc_val[component_id]);
-    p[0] = static_cast<jpgd_block_t>(static_cast<unsigned int>(s) << pD->m_successive_low);
-}
-
-
-void jpeg_decoder::decode_block_dc_refine(jpeg_decoder *pD, int component_id, int block_x, int block_y)
-{
-    if (pD->get_bits_no_markers(1)) {
-        jpgd_block_t *p = pD->coeff_buf_getp(pD->m_dc_coeffs[component_id], block_x, block_y);
-        p[0] |= (1 << pD->m_successive_low);
-    }
-}
-
-
-void jpeg_decoder::decode_block_ac_first(jpeg_decoder *pD, int component_id, int block_x, int block_y)
-{
-    int k, s, r;
-
-    if (pD->m_eob_run) {
-        pD->m_eob_run--;
-        return;
-    }
-    jpgd_block_t *p = pD->coeff_buf_getp(pD->m_ac_coeffs[component_id], block_x, block_y);
-
-    for (k = pD->m_spectral_start; k <= pD->m_spectral_end; k++) {
-        s = pD->huff_decode(pD->m_pHuff_tabs[pD->m_comp_ac_tab[component_id]]);
-        r = s >> 4;
-        s &= 15;
-        if (s) {
-            if ((k += r) > 63) pD->stop_decoding(JPGD_DECODE_ERROR);
-            r = pD->get_bits_no_markers(s);
-            s = JPGD_HUFF_EXTEND(r, s);
-            p[g_ZAG[k]] = static_cast<jpgd_block_t>(static_cast<unsigned int>(s) << pD->m_successive_low);
-        } else {
-            if (r == 15) {
-                if ((k += 15) > 63) pD->stop_decoding(JPGD_DECODE_ERROR);
-            } else {
-                pD->m_eob_run = 1 << r;
-                if (r) pD->m_eob_run += pD->get_bits_no_markers(r);
-                pD->m_eob_run--;
-                break;
-            }
-        }
-    }
-}
-
-
-void jpeg_decoder::decode_block_ac_refine(jpeg_decoder *pD, int component_id, int block_x, int block_y)
-{
-    int s, k, r;
-    int p1 = 1 << pD->m_successive_low;
-    int m1 = static_cast<unsigned int>(-1) << pD->m_successive_low;
-    jpgd_block_t *p = pD->coeff_buf_getp(pD->m_ac_coeffs[component_id], block_x, block_y);
-
-    JPGD_ASSERT(pD->m_spectral_end <= 63);
-
-    k = pD->m_spectral_start;
-
-    if (pD->m_eob_run == 0) {
-        for ( ; k <= pD->m_spectral_end; k++) {
-            s = pD->huff_decode(pD->m_pHuff_tabs[pD->m_comp_ac_tab[component_id]]);
-            r = s >> 4;
-            s &= 15;
-            if (s) {
-                if (s != 1) pD->stop_decoding(JPGD_DECODE_ERROR);
-                if (pD->get_bits_no_markers(1)) s = p1;
-                else s = m1;
-            } else {
-                if (r != 15) {
-                    pD->m_eob_run = 1 << r;
-                    if (r) pD->m_eob_run += pD->get_bits_no_markers(r);
-                    break;
-                }
-            }
-
-            do {
-                jpgd_block_t *this_coef = p + g_ZAG[k & 63];
-
-                if (*this_coef != 0) {
-                    if (pD->get_bits_no_markers(1)) {
-                        if ((*this_coef & p1) == 0) {
-                            if (*this_coef >= 0) *this_coef = static_cast<jpgd_block_t>(*this_coef + p1);
-                            else *this_coef = static_cast<jpgd_block_t>(*this_coef + m1);
-                        }
-                    }
-                } else {
-                    if (--r < 0) break;
-                }
-                k++;
-            } while (k <= pD->m_spectral_end);
-
-            if ((s) && (k < 64)) {
-              p[g_ZAG[k]] = static_cast<jpgd_block_t>(s);
-            }
-        }
-    }
-
-    if (pD->m_eob_run > 0) {
-        for ( ; k <= pD->m_spectral_end; k++) {
-            jpgd_block_t *this_coef = p + g_ZAG[k & 63]; // logical AND to shut up static code analysis
-
-            if (*this_coef != 0) {
-                if (pD->get_bits_no_markers(1)) {
-                    if ((*this_coef & p1) == 0) {
-                        if (*this_coef >= 0) *this_coef = static_cast<jpgd_block_t>(*this_coef + p1);
-                        else *this_coef = static_cast<jpgd_block_t>(*this_coef + m1);
-                    }
-                }
-            }
-        }
-        pD->m_eob_run--;
-    }
-}
-
-
-// Decode a scan in a progressively encoded image.
-void jpeg_decoder::decode_scan(pDecode_block_func decode_block_func)
-{
-    int mcu_row, mcu_col, mcu_block;
-    int block_x_mcu[JPGD_MAX_COMPONENTS], m_block_y_mcu[JPGD_MAX_COMPONENTS];
-
-    memset(m_block_y_mcu, 0, sizeof(m_block_y_mcu));
-
-    for (mcu_col = 0; mcu_col < m_mcus_per_col; mcu_col++) {
-        int component_num, component_id;
-        memset(block_x_mcu, 0, sizeof(block_x_mcu));
-
-        for (mcu_row = 0; mcu_row < m_mcus_per_row; mcu_row++) {
-            int block_x_mcu_ofs = 0, block_y_mcu_ofs = 0;
-
-            if ((m_restart_interval) && (m_restarts_left == 0)) process_restart();
-
-            for (mcu_block = 0; mcu_block < m_blocks_per_mcu; mcu_block++) {
-                component_id = m_mcu_org[mcu_block];
-                decode_block_func(this, component_id, block_x_mcu[component_id] + block_x_mcu_ofs, m_block_y_mcu[component_id] + block_y_mcu_ofs);
-
-                if (m_comps_in_scan == 1) block_x_mcu[component_id]++;
-                else {
-                    if (++block_x_mcu_ofs == m_comp_h_samp[component_id]) {
-                        block_x_mcu_ofs = 0;
-
-                        if (++block_y_mcu_ofs == m_comp_v_samp[component_id]) {
-                            block_y_mcu_ofs = 0;
-                            block_x_mcu[component_id] += m_comp_h_samp[component_id];
-                        }
-                    }
-                }
-            }
-            m_restarts_left--;
-        }
-
-        if (m_comps_in_scan == 1) m_block_y_mcu[m_comp_list[0]]++;
-        else {
-            for (component_num = 0; component_num < m_comps_in_scan; component_num++) {
-                component_id = m_comp_list[component_num];
-                m_block_y_mcu[component_id] += m_comp_v_samp[component_id];
-            }
-        }
-    }
-}
-
-
-// Decode a progressively encoded image.
-void jpeg_decoder::init_progressive()
-{
-    int i;
-
-    if (m_comps_in_frame == 4) stop_decoding(JPGD_UNSUPPORTED_COLORSPACE);
-
-    // Allocate the coefficient buffers.
-    for (i = 0; i < m_comps_in_frame; i++) {
-        m_dc_coeffs[i] = coeff_buf_open(m_max_mcus_per_row * m_comp_h_samp[i], m_max_mcus_per_col * m_comp_v_samp[i], 1, 1);
-        m_ac_coeffs[i] = coeff_buf_open(m_max_mcus_per_row * m_comp_h_samp[i], m_max_mcus_per_col * m_comp_v_samp[i], 8, 8);
-    }
-
-    while (true) {
-        int dc_only_scan, refinement_scan;
-        pDecode_block_func decode_block_func;
-
-        if (!init_scan()) break;
-
-        dc_only_scan = (m_spectral_start == 0);
-        refinement_scan = (m_successive_high != 0);
-
-        if ((m_spectral_start > m_spectral_end) || (m_spectral_end > 63)) stop_decoding(JPGD_BAD_SOS_SPECTRAL);
-
-        if (dc_only_scan) {
-            if (m_spectral_end) stop_decoding(JPGD_BAD_SOS_SPECTRAL);
-        } else if (m_comps_in_scan != 1) {  /* AC scans can only contain one component */
-            stop_decoding(JPGD_BAD_SOS_SPECTRAL);
-        }
-
-        if ((refinement_scan) && (m_successive_low != m_successive_high - 1)) stop_decoding(JPGD_BAD_SOS_SUCCESSIVE);
-
-        if (dc_only_scan) {
-            if (refinement_scan) decode_block_func = decode_block_dc_refine;
-            else decode_block_func = decode_block_dc_first;
-        } else {
-            if (refinement_scan) decode_block_func = decode_block_ac_refine;
-            else decode_block_func = decode_block_ac_first;
-        }
-        decode_scan(decode_block_func);
-        m_bits_left = 16;
-        get_bits(16);
-        get_bits(16);
-    }
-
-    m_comps_in_scan = m_comps_in_frame;
-
-    for (i = 0; i < m_comps_in_frame; i++) {
-        m_comp_list[i] = i;
-    }
-
-    calc_mcu_block_order();
-}
-
-
-void jpeg_decoder::init_sequential()
-{
-    if (!init_scan()) stop_decoding(JPGD_UNEXPECTED_MARKER);
-}
-
-
-void jpeg_decoder::decode_start()
-{
-    init_frame();
-    if (m_progressive_flag) init_progressive();
-    else init_sequential();
-}
-
-
-void jpeg_decoder::decode_init(jpeg_decoder_stream *pStream)
-{
-    init(pStream);
-    locate_sof_marker();
-}
-
-
-jpeg_decoder::jpeg_decoder(jpeg_decoder_stream *pStream)
-{
-    if (setjmp(m_jmp_state)) return;
-    decode_init(pStream);
-}
-
-
-int jpeg_decoder::begin_decoding()
-{
-    if (m_ready_flag) return JPGD_SUCCESS;
-    if (m_error_code) return JPGD_FAILED;
-    if (setjmp(m_jmp_state)) return JPGD_FAILED;
-
-    decode_start();
-    m_ready_flag = true;
-
-    return JPGD_SUCCESS;
-}
-
-
-jpeg_decoder::~jpeg_decoder()
-{
-    free_all_blocks();
-}
-
-
-void jpeg_decoder_file_stream::close()
-{
-    if (m_pFile) {
-        fclose(m_pFile);
-        m_pFile = nullptr;
-    }
-    m_eof_flag = false;
-    m_error_flag = false;
-}
-
-
-jpeg_decoder_file_stream::~jpeg_decoder_file_stream()
-{
-    close();
-}
-
-
-bool jpeg_decoder_file_stream::open(const char *Pfilename)
-{
-    close();
-
-    m_eof_flag = false;
-    m_error_flag = false;
-
-#if defined(_MSC_VER)
-    m_pFile = nullptr;
-    fopen_s(&m_pFile, Pfilename, "rb");
-#else
-    m_pFile = fopen(Pfilename, "rb");
-#endif
-    return m_pFile != nullptr;
-}
-
-
-int jpeg_decoder_file_stream::read(uint8_t *pBuf, int max_bytes_to_read, bool *pEOF_flag)
-{
-    if (!m_pFile) return -1;
-
-    if (m_eof_flag) {
-        *pEOF_flag = true;
-        return 0;
-    }
-
-    if (m_error_flag) return -1;
-
-    int bytes_read = static_cast<int>(fread(pBuf, 1, max_bytes_to_read, m_pFile));
-    if (bytes_read < max_bytes_to_read) {
-        if (ferror(m_pFile)) {
-            m_error_flag = true;
-            return -1;
-        }
-        m_eof_flag = true;
-        *pEOF_flag = true;
-    }
-    return bytes_read;
-}
-
-
-bool jpeg_decoder_mem_stream::open(const uint8_t *pSrc_data, uint32_t size)
-{
-    close();
-    m_pSrc_data = pSrc_data;
-    m_ofs = 0;
-    m_size = size;
-    return true;
-}
-
-
-int jpeg_decoder_mem_stream::read(uint8_t *pBuf, int max_bytes_to_read, bool *pEOF_flag)
-{
-    *pEOF_flag = false;
-    if (!m_pSrc_data) return -1;
-
-    uint32_t bytes_remaining = m_size - m_ofs;
-    if ((uint32_t)max_bytes_to_read > bytes_remaining) {
-        max_bytes_to_read = bytes_remaining;
-        *pEOF_flag = true;
-    }
-    memcpy(pBuf, m_pSrc_data + m_ofs, max_bytes_to_read);
-    m_ofs += max_bytes_to_read;
-
-    return max_bytes_to_read;
-}
-
-
-/************************************************************************/
-/* External Class Implementation                                        */
-/************************************************************************/
-
-
-jpeg_decoder* jpgdHeader(const char* data, int size, int* width, int* height)
-{
-    auto decoder = new jpeg_decoder(new jpeg_decoder_mem_stream((const uint8_t*)data, size));
-    if (decoder->get_error_code() != JPGD_SUCCESS) {
-        delete(decoder);
-        return nullptr;
-    }
-
-    if (width) *width = decoder->get_width();
-    if (height) *height = decoder->get_height();
-
-    return decoder;
-}
-
-
-jpeg_decoder* jpgdHeader(const char* filename, int* width, int* height)
-{
-    auto fileStream = new jpeg_decoder_file_stream();
-    if (!fileStream->open(filename)) return nullptr;
-
-    auto decoder = new jpeg_decoder(fileStream);
-    if (decoder->get_error_code() != JPGD_SUCCESS) {
-        delete(decoder);
-        return nullptr;
-    }
-
-    if (width) *width = decoder->get_width();
-    if (height) *height = decoder->get_height();
-
-    return decoder;
-}
-
-
-void jpgdDelete(jpeg_decoder* decoder)
-{
-    delete(decoder);
-}
-
-
-unsigned char* jpgdDecompress(jpeg_decoder* decoder)
-{
-    if (!decoder) return nullptr;
-
-    int req_comps = 4;  //TODO: fixed 4 channel components now?
-    if ((req_comps != 1) && (req_comps != 3) && (req_comps != 4)) return nullptr;
-
-    auto image_width = decoder->get_width();
-    auto image_height = decoder->get_height();
-    //auto actual_comps = decoder->get_num_components();
-
-    if (decoder->begin_decoding() != JPGD_SUCCESS) return nullptr;
-
-    const int dst_bpl = image_width * req_comps;
-    uint8_t *pImage_data = (uint8_t*)malloc(dst_bpl * image_height);
-    if (!pImage_data) return nullptr;
-
-    for (int y = 0; y < image_height; y++) {
-        const uint8_t* pScan_line;
-        uint32_t scan_line_len;
-        if (decoder->decode((const void**)&pScan_line, &scan_line_len) != JPGD_SUCCESS) {
-            free(pImage_data);
-            return nullptr;
-        }
-
-        uint8_t *pDst = pImage_data + y * dst_bpl;
-
-        //Return as BGRA
-        if ((req_comps == 4) && (decoder->get_num_components() == 3)) {
-            for (int x = 0; x < image_width; x++) {
-                pDst[0] = pScan_line[x*4+2];
-                pDst[1] = pScan_line[x*4+1];
-                pDst[2] = pScan_line[x*4+0];
-                pDst[3] = 255;
-                pDst += 4;
-            }
-        } else if (((req_comps == 1) && (decoder->get_num_components() == 1)) || ((req_comps == 4) && (decoder->get_num_components() == 3))) {
-            memcpy(pDst, pScan_line, dst_bpl);
-        } else if (decoder->get_num_components() == 1) {
-            if (req_comps == 3) {
-                for (int x = 0; x < image_width; x++) {
-                    uint8_t luma = pScan_line[x];
-                    pDst[0] = luma;
-                    pDst[1] = luma;
-                    pDst[2] = luma;
-                    pDst += 3;
-                }
-            } else {
-                for (int x = 0; x < image_width; x++) {
-                    uint8_t luma = pScan_line[x];
-                    pDst[0] = luma;
-                    pDst[1] = luma;
-                    pDst[2] = luma;
-                    pDst[3] = 255;
-                    pDst += 4;
-                }
-            }
-        } else if (decoder->get_num_components() == 3) {
-            if (req_comps == 1) {
-                const int YR = 19595, YG = 38470, YB = 7471;
-                for (int x = 0; x < image_width; x++) {
-                    int r = pScan_line[x*4+0];
-                    int g = pScan_line[x*4+1];
-                    int b = pScan_line[x*4+2];
-                    *pDst++ = static_cast<uint8_t>((r * YR + g * YG + b * YB + 32768) >> 16);
-                }
-            } else {
-                for (int x = 0; x < image_width; x++) {
-                    pDst[0] = pScan_line[x*4+0];
-                    pDst[1] = pScan_line[x*4+1];
-                    pDst[2] = pScan_line[x*4+2];
-                    pDst += 3;
-                }
-            }
-        }
-    }
-    return pImage_data;
-}

+ 0 - 35
thirdparty/thorvg/src/loaders/jpg/tvgJpgd.h

@@ -1,35 +0,0 @@
-/*
- * Copyright (c) 2021 - 2023 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.
- */
-
-// jpgd.h - C++ class for JPEG decompression.
-// Public domain, Rich Geldreich <[email protected]>
-#ifndef _TVG_JPGD_H_
-#define _TVG_JPGD_H_
-
-class jpeg_decoder;
-
-jpeg_decoder* jpgdHeader(const char* data, int size, int* width, int* height);
-jpeg_decoder* jpgdHeader(const char* filename, int* width, int* height);
-unsigned char* jpgdDecompress(jpeg_decoder* decoder);
-void jpgdDelete(jpeg_decoder* decoder);
-
-#endif //_TVG_JPGD_H_

+ 63 - 11
thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.cpp

@@ -28,17 +28,30 @@
 /* Internal Class Implementation                                        */
 /************************************************************************/
 
+static bool _isImportanceApplicable(SvgStyleFlags &toFlagsImportance, SvgStyleFlags fromFlagsImportance, SvgStyleFlags flag)
+{
+    if (!(toFlagsImportance & flag) && (fromFlagsImportance & flag)) {
+        return true;
+    }
+    return false;
+}
+
 static void _copyStyle(SvgStyleProperty* to, const SvgStyleProperty* from)
 {
     if (from == nullptr) return;
     //Copy the properties of 'from' only if they were explicitly set (not the default ones).
-    if (from->curColorSet && !(to->flags & SvgStyleFlags::Color)) {
+    if ((from->curColorSet && !(to->flags & SvgStyleFlags::Color)) ||
+        _isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::Color)) {
         to->color = from->color;
         to->curColorSet = true;
         to->flags = (to->flags | SvgStyleFlags::Color);
+        if (from->flagsImportance & SvgStyleFlags::Color) {
+            to->flagsImportance = (to->flagsImportance | SvgStyleFlags::Color);
+        }
     }
     //Fill
-    if ((from->fill.flags & SvgFillFlags::Paint) && !(to->flags & SvgStyleFlags::Fill)) {
+    if (((from->fill.flags & SvgFillFlags::Paint) && !(to->flags & SvgStyleFlags::Fill)) ||
+        _isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::Fill)) {
         to->fill.paint.color = from->fill.paint.color;
         to->fill.paint.none = from->fill.paint.none;
         to->fill.paint.curColor = from->fill.paint.curColor;
@@ -48,19 +61,31 @@ static void _copyStyle(SvgStyleProperty* to, const SvgStyleProperty* from)
         }
         to->fill.flags = (to->fill.flags | SvgFillFlags::Paint);
         to->flags = (to->flags | SvgStyleFlags::Fill);
+        if (from->flagsImportance & SvgStyleFlags::Fill) {
+            to->flagsImportance = (to->flagsImportance | SvgStyleFlags::Fill);
+        }
     }
-    if ((from->fill.flags & SvgFillFlags::Opacity) && !(to->flags & SvgStyleFlags::FillOpacity)) {
+    if (((from->fill.flags & SvgFillFlags::Opacity) && !(to->flags & SvgStyleFlags::FillOpacity)) ||
+        _isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::FillOpacity)) {
         to->fill.opacity = from->fill.opacity;
         to->fill.flags = (to->fill.flags | SvgFillFlags::Opacity);
         to->flags = (to->flags | SvgStyleFlags::FillOpacity);
+        if (from->flagsImportance & SvgStyleFlags::FillOpacity) {
+            to->flagsImportance = (to->flagsImportance | SvgStyleFlags::FillOpacity);
+        }
     }
-    if ((from->fill.flags & SvgFillFlags::FillRule) && !(to->flags & SvgStyleFlags::FillRule)) {
+    if (((from->fill.flags & SvgFillFlags::FillRule) && !(to->flags & SvgStyleFlags::FillRule)) ||
+        _isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::FillRule)) {
         to->fill.fillRule = from->fill.fillRule;
         to->fill.flags = (to->fill.flags | SvgFillFlags::FillRule);
         to->flags = (to->flags | SvgStyleFlags::FillRule);
+        if (from->flagsImportance & SvgStyleFlags::FillRule) {
+            to->flagsImportance = (to->flagsImportance | SvgStyleFlags::FillRule);
+        }
     }
     //Stroke
-    if ((from->stroke.flags & SvgStrokeFlags::Paint) && !(to->flags & SvgStyleFlags::Stroke)) {
+    if (((from->stroke.flags & SvgStrokeFlags::Paint) && !(to->flags & SvgStyleFlags::Stroke)) ||
+        _isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::Stroke)) {
         to->stroke.paint.color = from->stroke.paint.color;
         to->stroke.paint.none = from->stroke.paint.none;
         to->stroke.paint.curColor = from->stroke.paint.curColor;
@@ -70,18 +95,30 @@ static void _copyStyle(SvgStyleProperty* to, const SvgStyleProperty* from)
         }
         to->stroke.flags = (to->stroke.flags | SvgStrokeFlags::Paint);
         to->flags = (to->flags | SvgStyleFlags::Stroke);
+        if (from->flagsImportance & SvgStyleFlags::Stroke) {
+            to->flagsImportance = (to->flagsImportance | SvgStyleFlags::Stroke);
+        }
     }
-    if ((from->stroke.flags & SvgStrokeFlags::Opacity) && !(to->flags & SvgStyleFlags::StrokeOpacity)) {
+    if (((from->stroke.flags & SvgStrokeFlags::Opacity) && !(to->flags & SvgStyleFlags::StrokeOpacity)) ||
+        _isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::StrokeOpacity)) {
         to->stroke.opacity = from->stroke.opacity;
         to->stroke.flags = (to->stroke.flags | SvgStrokeFlags::Opacity);
         to->flags = (to->flags | SvgStyleFlags::StrokeOpacity);
+        if (from->flagsImportance & SvgStyleFlags::StrokeOpacity) {
+            to->flagsImportance = (to->flagsImportance | SvgStyleFlags::StrokeOpacity);
+        }
     }
-    if ((from->stroke.flags & SvgStrokeFlags::Width) && !(to->flags & SvgStyleFlags::StrokeWidth)) {
+    if (((from->stroke.flags & SvgStrokeFlags::Width) && !(to->flags & SvgStyleFlags::StrokeWidth)) ||
+        _isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::StrokeWidth)) {
         to->stroke.width = from->stroke.width;
         to->stroke.flags = (to->stroke.flags | SvgStrokeFlags::Width);
         to->flags = (to->flags | SvgStyleFlags::StrokeWidth);
+        if (from->flagsImportance & SvgStyleFlags::StrokeWidth) {
+            to->flagsImportance = (to->flagsImportance | SvgStyleFlags::StrokeWidth);
+        }
     }
-    if ((from->stroke.flags & SvgStrokeFlags::Dash) && !(to->flags & SvgStyleFlags::StrokeDashArray)) {
+    if (((from->stroke.flags & SvgStrokeFlags::Dash) && !(to->flags & SvgStyleFlags::StrokeDashArray)) ||
+        _isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::StrokeDashArray)) {
         if (from->stroke.dash.array.count > 0) {
             to->stroke.dash.array.clear();
             to->stroke.dash.array.reserve(from->stroke.dash.array.count);
@@ -90,23 +127,38 @@ static void _copyStyle(SvgStyleProperty* to, const SvgStyleProperty* from)
             }
             to->stroke.flags = (to->stroke.flags | SvgStrokeFlags::Dash);
             to->flags = (to->flags | SvgStyleFlags::StrokeDashArray);
+            if (from->flagsImportance & SvgStyleFlags::StrokeDashArray) {
+                to->flagsImportance = (to->flagsImportance | SvgStyleFlags::StrokeDashArray);
+            }
         }
     }
-    if ((from->stroke.flags & SvgStrokeFlags::Cap) && !(to->flags & SvgStyleFlags::StrokeLineCap)) {
+    if (((from->stroke.flags & SvgStrokeFlags::Cap) && !(to->flags & SvgStyleFlags::StrokeLineCap)) ||
+        _isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::StrokeLineCap)) {
         to->stroke.cap = from->stroke.cap;
         to->stroke.flags = (to->stroke.flags | SvgStrokeFlags::Cap);
         to->flags = (to->flags | SvgStyleFlags::StrokeLineCap);
+        if (from->flagsImportance & SvgStyleFlags::StrokeLineCap) {
+            to->flagsImportance = (to->flagsImportance | SvgStyleFlags::StrokeLineCap);
+        }
     }
-    if ((from->stroke.flags & SvgStrokeFlags::Join) && !(to->flags & SvgStyleFlags::StrokeLineJoin)) {
+    if (((from->stroke.flags & SvgStrokeFlags::Join) && !(to->flags & SvgStyleFlags::StrokeLineJoin)) ||
+        _isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::StrokeLineJoin)) {
         to->stroke.join = from->stroke.join;
         to->stroke.flags = (to->stroke.flags | SvgStrokeFlags::Join);
         to->flags = (to->flags | SvgStyleFlags::StrokeLineJoin);
+        if (from->flagsImportance & SvgStyleFlags::StrokeLineJoin) {
+            to->flagsImportance = (to->flagsImportance | SvgStyleFlags::StrokeLineJoin);
+        }
     }
     //Opacity
     //TODO: it can be set to be 255 and shouldn't be changed by attribute 'opacity'
-    if (from->opacity < 255 && !(to->flags & SvgStyleFlags::Opacity)) {
+    if ((from->opacity < 255 && !(to->flags & SvgStyleFlags::Opacity)) ||
+        _isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::Opacity)) {
         to->opacity = from->opacity;
         to->flags = (to->flags | SvgStyleFlags::Opacity);
+        if (from->flagsImportance & SvgStyleFlags::Opacity) {
+            to->flagsImportance = (to->flagsImportance | SvgStyleFlags::Opacity);
+        }
     }
 }
 

+ 68 - 54
thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp

@@ -392,15 +392,8 @@ static char* _idFromUrl(const char* url)
 
     int i = 0;
     while (url[i] > ' ' && url[i] != ')' && url[i] != '\'') ++i;
-
-    //custom strndup() for portability
-    int len = strlen(url);
-    if (i < len) len = i;
-
-    auto ret = (char*) malloc(len + 1);
-    if (!ret) return 0;
-    ret[len] = '\0';
-    return (char*) memcpy(ret, url, len);
+    
+    return svgUtilStrndup(url, i);
 }
 
 
@@ -983,6 +976,22 @@ static void _handleStrokeLineJoinAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode*
     node->style->stroke.join = _toLineJoin(value);
 }
 
+static void _handleStrokeMiterlimitAttr(SvgLoaderData* loader, SvgNode* node, const char* value)
+{
+    char* end = nullptr;
+    const float miterlimit = svgUtilStrtof(value, &end);
+
+    // https://www.w3.org/TR/SVG2/painting.html#LineJoin
+    // - A negative value for stroke-miterlimit must be treated as an illegal value.
+    if (miterlimit < 0.0f) {
+        TVGERR("SVG", "A stroke-miterlimit change (%f <- %f) with a negative value is omitted.",
+            node->style->stroke.miterlimit, miterlimit);
+        return;
+    }
+
+    node->style->stroke.flags = (node->style->stroke.flags | SvgStrokeFlags::Miterlimit);
+    node->style->stroke.miterlimit = miterlimit;
+}
 
 static void _handleFillRuleAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
 {
@@ -1099,6 +1108,7 @@ static constexpr struct
     STYLE_DEF(stroke, Stroke, SvgStyleFlags::Stroke),
     STYLE_DEF(stroke-width, StrokeWidth, SvgStyleFlags::StrokeWidth),
     STYLE_DEF(stroke-linejoin, StrokeLineJoin, SvgStyleFlags::StrokeLineJoin),
+    STYLE_DEF(stroke-miterlimit, StrokeMiterlimit, SvgStyleFlags::StrokeMiterlimit),
     STYLE_DEF(stroke-linecap, StrokeLineCap, SvgStyleFlags::StrokeLineCap),
     STYLE_DEF(stroke-opacity, StrokeOpacity, SvgStyleFlags::StrokeOpacity),
     STYLE_DEF(stroke-dasharray, StrokeDashArray, SvgStyleFlags::StrokeDashArray),
@@ -1125,12 +1135,27 @@ static bool _parseStyleAttr(void* data, const char* key, const char* value, bool
     sz = strlen(key);
     for (unsigned int i = 0; i < sizeof(styleTags) / sizeof(styleTags[0]); i++) {
         if (styleTags[i].sz - 1 == sz && !strncmp(styleTags[i].tag, key, sz)) {
+            bool importance = false;
+            if (auto ptr = strstr(value, "!important")) {
+                size_t size = ptr - value;
+                while (size > 0 && isspace(value[size - 1])) {
+                    size--;
+                }
+                value = svgUtilStrndup(value, size);
+                importance = true;
+            }
             if (style) {
-                styleTags[i].tagHandler(loader, node, value);
-                node->style->flags = (node->style->flags | styleTags[i].flag);
+                if (importance || !(node->style->flagsImportance & styleTags[i].flag)) {
+                    styleTags[i].tagHandler(loader, node, value);
+                    node->style->flags = (node->style->flags | styleTags[i].flag);
+                }
             } else if (!(node->style->flags & styleTags[i].flag)) {
                 styleTags[i].tagHandler(loader, node, value);
             }
+            if (importance) {
+                node->style->flagsImportance = (node->style->flags | styleTags[i].flag);
+                free(const_cast<char*>(value));
+            }
             return true;
         }
     }
@@ -1307,6 +1332,7 @@ static SvgNode* _createNode(SvgNode* parent, SvgNodeType type)
     node->style->stroke.cap = StrokeCap::Butt;
     //Default line join is miter
     node->style->stroke.join = StrokeJoin::Miter;
+    node->style->stroke.miterlimit = 4.0f;
     node->style->stroke.scale = 1.0;
 
     node->style->paintOrder = _toPaintOrder("fill stroke");
@@ -1593,39 +1619,11 @@ static SvgNode* _createEllipseNode(SvgLoaderData* loader, SvgNode* parent, const
 }
 
 
-static bool _attrParsePolygonPoints(const char* str, float** points, int* ptCount)
+static bool _attrParsePolygonPoints(const char* str, SvgPolygonNode* polygon)
 {
-    float tmp[50];
-    int tmpCount = 0;
-    int count = 0;
     float num;
-    float *pointArray = nullptr, *tmpArray;
-
-    while (_parseNumber(&str, &num)) {
-        tmp[tmpCount++] = num;
-        if (tmpCount == 50) {
-            tmpArray = (float*)realloc(pointArray, (count + tmpCount) * sizeof(float));
-            if (!tmpArray) goto error_alloc;
-            pointArray = tmpArray;
-            memcpy(&pointArray[count], tmp, tmpCount * sizeof(float));
-            count += tmpCount;
-            tmpCount = 0;
-        }
-    }
-
-    if (tmpCount > 0) {
-        tmpArray = (float*)realloc(pointArray, (count + tmpCount) * sizeof(float));
-        if (!tmpArray) goto error_alloc;
-        pointArray = tmpArray;
-        memcpy(&pointArray[count], tmp, tmpCount * sizeof(float));
-        count += tmpCount;
-    }
-    *ptCount = count;
-    *points = pointArray;
+    while (_parseNumber(&str, &num)) polygon->pts.push(num);
     return true;
-
-error_alloc:
-    return false;
 }
 
 
@@ -1642,7 +1640,7 @@ static bool _attrParsePolygonNode(void* data, const char* key, const char* value
     else polygon = &(node->node.polyline);
 
     if (!strcmp(key, "points")) {
-        return _attrParsePolygonPoints(value, &polygon->points, &polygon->pointsCount);
+        return _attrParsePolygonPoints(value, polygon);
     } else if (!strcmp(key, "style")) {
         return simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader);
     } else if (!strcmp(key, "clip-path")) {
@@ -1903,6 +1901,7 @@ static SvgNode* _getDefsNode(SvgNode* node)
     }
 
     if (node->type == SvgNodeType::Doc) return node->node.doc.defs;
+    if (node->type == SvgNodeType::Defs) return node;
 
     return nullptr;
 }
@@ -2680,7 +2679,7 @@ static void _inheritGradient(SvgLoaderData* loader, SvgStyleGradient* to, SvgSty
         }
     }
 
-    if (to->stops.count == 0) _cloneGradStops(to->stops, from->stops);
+    if (to->stops.empty()) _cloneGradStops(to->stops, from->stops);
 }
 
 
@@ -2784,6 +2783,9 @@ static void _styleInherit(SvgStyleProperty* child, const SvgStyleProperty* paren
     if (!(child->stroke.flags & SvgStrokeFlags::Join)) {
         child->stroke.join = parent->stroke.join;
     }
+    if (!(child->stroke.flags & SvgStrokeFlags::Miterlimit)) {
+        child->stroke.miterlimit = parent->stroke.miterlimit;
+    }
 }
 
 
@@ -2847,6 +2849,10 @@ static void _styleCopy(SvgStyleProperty* to, const SvgStyleProperty* from)
     if (from->stroke.flags & SvgStrokeFlags::Join) {
         to->stroke.join = from->stroke.join;
     }
+
+    if (from->stroke.flags & SvgStrokeFlags::Miterlimit) {
+        to->stroke.miterlimit = from->stroke.miterlimit;
+    }
 }
 
 
@@ -2910,16 +2916,14 @@ static void _copyAttr(SvgNode* to, const SvgNode* from)
             break;
         }
         case SvgNodeType::Polygon: {
-            if ((to->node.polygon.pointsCount = from->node.polygon.pointsCount)) {
-                to->node.polygon.points = (float*)malloc(to->node.polygon.pointsCount * sizeof(float));
-                memcpy(to->node.polygon.points, from->node.polygon.points, to->node.polygon.pointsCount * sizeof(float));
+            if ((to->node.polygon.pts.count = from->node.polygon.pts.count)) {
+                to->node.polygon.pts = from->node.polygon.pts;
             }
             break;
         }
         case SvgNodeType::Polyline: {
-            if ((to->node.polyline.pointsCount = from->node.polyline.pointsCount)) {
-                to->node.polyline.points = (float*)malloc(to->node.polyline.pointsCount * sizeof(float));
-                memcpy(to->node.polyline.points, from->node.polyline.points, to->node.polyline.pointsCount * sizeof(float));
+            if ((to->node.polyline.pts.count = from->node.polyline.pts.count)) {
+                to->node.polyline.pts = from->node.polyline.pts;
             }
             break;
         }
@@ -2934,6 +2938,16 @@ static void _copyAttr(SvgNode* to, const SvgNode* from)
             }
             break;
         }
+        case SvgNodeType::Use: {
+            to->node.use.x = from->node.use.x;
+            to->node.use.y = from->node.use.y;
+            to->node.use.w = from->node.use.w;
+            to->node.use.h = from->node.use.h;
+            to->node.use.isWidthSet = from->node.use.isWidthSet;
+            to->node.use.isHeightSet = from->node.use.isHeightSet;
+            to->node.use.symbol = from->node.use.symbol;
+            break;
+        }
         default: {
             break;
         }
@@ -3200,7 +3214,7 @@ static void _inefficientNodeCheck(TVG_UNUSED SvgNode* node)
         }
         case SvgNodeType::Polygon:
         case SvgNodeType::Polyline: {
-            if (node->node.polygon.pointsCount < 2) TVGLOG("SVG", "Inefficient elements used [Invalid Polygon][Node Type : %s]", type);
+            if (node->node.polygon.pts.count < 2) TVGLOG("SVG", "Inefficient elements used [Invalid Polygon][Node Type : %s]", type);
             break;
         }
         case SvgNodeType::Circle: {
@@ -3356,11 +3370,11 @@ static void _freeNode(SvgNode* node)
              break;
          }
          case SvgNodeType::Polygon: {
-             free(node->node.polygon.points);
+             free(node->node.polygon.pts.data);
              break;
          }
          case SvgNodeType::Polyline: {
-             free(node->node.polyline.points);
+             free(node->node.polyline.pts.data);
              break;
          }
          case SvgNodeType::Doc: {
@@ -3497,6 +3511,7 @@ void SvgLoader::run(unsigned tid)
         if (defs) _updateComposite(loaderData.doc, defs);
 
         _updateStyle(loaderData.doc, nullptr);
+        if (defs) _updateStyle(defs, nullptr);
 
         if (loaderData.gradients.count > 0) _updateGradient(&loaderData, loaderData.doc, &loaderData.gradients);
         if (defs) _updateGradient(&loaderData, loaderData.doc, &defs->node.defs.gradients);
@@ -3683,6 +3698,5 @@ bool SvgLoader::close()
 unique_ptr<Paint> SvgLoader::paint()
 {
     this->done();
-    if (root) return move(root);
-    else return nullptr;
+    return std::move(root);
 }

+ 5 - 0
thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.h

@@ -48,12 +48,17 @@ public:
     bool resize(Paint* paint, float w, float h) override;
     bool read() override;
     bool close() override;
+
     unique_ptr<Paint> paint() override;
 
 private:
     SvgViewFlag viewFlag = SvgViewFlag::None;
     AspectRatioAlign align = AspectRatioAlign::XMidYMid;
     AspectRatioMeetOrSlice meetOrSlice = AspectRatioMeetOrSlice::Meet;
+    float vx = 0;
+    float vy = 0;
+    float vw = 0;
+    float vh = 0;
 
     bool header();
     void clear();

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

@@ -100,6 +100,7 @@ enum class SvgStrokeFlags
     Cap = 0x20,
     Join = 0x40,
     Dash = 0x80,
+    Miterlimit = 0x100
 };
 
 constexpr bool operator &(SvgStrokeFlags a, SvgStrokeFlags b)
@@ -137,7 +138,8 @@ enum class SvgStyleFlags
     Mask = 0x2000,
     MaskType = 0x4000,
     Display = 0x8000,
-    PaintOrder = 0x10000
+    PaintOrder = 0x10000,
+    StrokeMiterlimit = 0x20000
 };
 
 constexpr bool operator &(SvgStyleFlags a, SvgStyleFlags b)
@@ -351,8 +353,7 @@ struct SvgPathNode
 
 struct SvgPolygonNode
 {
-    int pointsCount;
-    float* points;
+    Array<float> pts;
 };
 
 struct SvgClipNode
@@ -466,6 +467,7 @@ struct SvgStyleStroke
     float centered;
     StrokeCap cap;
     StrokeJoin join;
+    float miterlimit;
     SvgDash dash;
     int dashCount;
 };
@@ -482,6 +484,7 @@ struct SvgStyleProperty
     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)
 };
 
 struct SvgNode
@@ -539,7 +542,7 @@ struct SvgNodeIdPair
 
 struct SvgLoaderData
 {
-    Array<SvgNode*> stack = {nullptr, 0, 0};
+    Array<SvgNode*> stack;
     SvgNode* doc = nullptr;
     SvgNode* def = nullptr;
     SvgNode* cssStyle = nullptr;

+ 32 - 29
thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp

@@ -263,7 +263,7 @@ static void _applyComposition(Paint* paint, const SvgNode* node, const Box& vBox
                 comp->transform(m);
             }
 
-            if (valid) paint->composite(move(comp), CompositeMethod::ClipPath);
+            if (valid) paint->composite(std::move(comp), CompositeMethod::ClipPath);
 
             node->style->clipPath.applying = false;
         }
@@ -285,9 +285,9 @@ static void _applyComposition(Paint* paint, const SvgNode* node, const Box& vBox
                 if (node->transform) comp->transform(*node->transform);
 
                 if (compNode->node.mask.type == SvgMaskType::Luminance && !isMaskWhite) {
-                    paint->composite(move(comp), CompositeMethod::LumaMask);
+                    paint->composite(std::move(comp), CompositeMethod::LumaMask);
                 } else {
-                    paint->composite(move(comp), CompositeMethod::AlphaMask);
+                    paint->composite(std::move(comp), CompositeMethod::AlphaMask);
                 }
             }
 
@@ -313,10 +313,10 @@ static void _applyProperty(SvgNode* node, Shape* vg, const Box& vBox, const stri
 
         if (style->fill.paint.gradient->type == SvgGradientType::Linear) {
              auto linear = _applyLinearGradientProperty(style->fill.paint.gradient, vg, bBox, style->fill.opacity);
-             vg->fill(move(linear));
+             vg->fill(std::move(linear));
         } else if (style->fill.paint.gradient->type == SvgGradientType::Radial) {
              auto radial = _applyRadialGradientProperty(style->fill.paint.gradient, vg, bBox, style->fill.opacity);
-             vg->fill(move(radial));
+             vg->fill(std::move(radial));
         }
     } else if (style->fill.paint.url) {
         //TODO: Apply the color pointed by url
@@ -342,6 +342,7 @@ static void _applyProperty(SvgNode* node, Shape* vg, const Box& vBox, const stri
     vg->stroke(style->stroke.width);
     vg->stroke(style->stroke.cap);
     vg->stroke(style->stroke.join);
+    vg->strokeMiterlimit(style->stroke.miterlimit);
     if (style->stroke.dash.array.count > 0) {
         vg->stroke(style->stroke.dash.array.data, style->stroke.dash.array.count);
     }
@@ -355,10 +356,10 @@ static void _applyProperty(SvgNode* node, Shape* vg, const Box& vBox, const stri
 
         if (style->stroke.paint.gradient->type == SvgGradientType::Linear) {
              auto linear = _applyLinearGradientProperty(style->stroke.paint.gradient, vg, bBox, style->stroke.opacity);
-             vg->stroke(move(linear));
+             vg->stroke(std::move(linear));
         } else if (style->stroke.paint.gradient->type == SvgGradientType::Radial) {
              auto radial = _applyRadialGradientProperty(style->stroke.paint.gradient, vg, bBox, style->stroke.opacity);
-             vg->stroke(move(radial));
+             vg->stroke(std::move(radial));
         }
     } else if (style->stroke.paint.url) {
         //TODO: Apply the color pointed by url
@@ -401,19 +402,21 @@ static bool _appendShape(SvgNode* node, Shape* shape, const Box& vBox, const str
             break;
         }
         case SvgNodeType::Polygon: {
-            if (node->node.polygon.pointsCount < 2) break;
-            shape->moveTo(node->node.polygon.points[0], node->node.polygon.points[1]);
-            for (int i = 2; i < node->node.polygon.pointsCount - 1; i += 2) {
-                shape->lineTo(node->node.polygon.points[i], node->node.polygon.points[i + 1]);
+            if (node->node.polygon.pts.count < 2) break;
+            auto pts = node->node.polygon.pts.data;
+            shape->moveTo(pts[0], pts[1]);
+            for (pts += 2; pts < node->node.polygon.pts.end(); pts += 2) {
+                shape->lineTo(pts[0], pts[1]);
             }
             shape->close();
             break;
         }
         case SvgNodeType::Polyline: {
-            if (node->node.polygon.pointsCount < 2) break;
-            shape->moveTo(node->node.polygon.points[0], node->node.polygon.points[1]);
-            for (int i = 2; i < node->node.polygon.pointsCount - 1; i += 2) {
-                shape->lineTo(node->node.polygon.points[i], node->node.polygon.points[i + 1]);
+            if (node->node.polyline.pts.count < 2) break;
+            auto pts = node->node.polyline.pts.data;
+            shape->moveTo(pts[0], pts[1]);
+            for (pts += 2; pts < node->node.polyline.pts.end(); pts += 2) {
+                shape->lineTo(pts[0], pts[1]);
             }
             break;
         }
@@ -676,7 +679,7 @@ static unique_ptr<Scene> _useBuildHelper(const SvgNode* node, const Box& vBox, c
         scene->transform(mSceneTransform);
 
         if (node->node.use.symbol->node.symbol.overflowVisible) {
-            finalScene = move(scene);
+            finalScene = std::move(scene);
         } else {
             auto viewBoxClip = Shape::gen();
             viewBoxClip->appendRect(0, 0, width, height, 0, 0);
@@ -689,17 +692,17 @@ static unique_ptr<Scene> _useBuildHelper(const SvgNode* node, const Box& vBox, c
             viewBoxClip->transform(mClipTransform);
 
             auto compositeLayer = Scene::gen();
-            compositeLayer->composite(move(viewBoxClip), CompositeMethod::ClipPath);
-            compositeLayer->push(move(scene));
+            compositeLayer->composite(std::move(viewBoxClip), CompositeMethod::ClipPath);
+            compositeLayer->push(std::move(scene));
 
             auto root = Scene::gen();
-            root->push(move(compositeLayer));
+            root->push(std::move(compositeLayer));
 
-            finalScene = move(root);
+            finalScene = std::move(root);
         }
     } else {
         if (!mathIdentity((const Matrix*)(&mUseTransform))) scene->transform(mUseTransform);
-        finalScene = move(scene);
+        finalScene = std::move(scene);
     }
 
     return finalScene;
@@ -731,7 +734,7 @@ static unique_ptr<Scene> _sceneBuildHelper(const SvgNode* node, const Box& vBox,
                 } else if ((*child)->type == SvgNodeType::Image) {
                     auto image = _imageBuildHelper(*child, vBox, svgPath);
                     if (image) {
-                        scene->push(move(image));
+                        scene->push(std::move(image));
                         if (isMaskWhite) *isMaskWhite = false;
                     }
                 } else if ((*child)->type != SvgNodeType::Mask) {
@@ -739,13 +742,13 @@ static unique_ptr<Scene> _sceneBuildHelper(const SvgNode* node, const Box& vBox,
                     if (shape) {
                         if (isMaskWhite) {
                             uint8_t r, g, b;
-                            shape->fillColor(&r, &g, &b, nullptr);
+                            shape->fillColor(&r, &g, &b);
                             if (shape->fill() || r < 255 || g < 255 || b < 255 || shape->strokeFill() ||
-                                (shape->strokeColor(&r, &g, &b, nullptr) == Result::Success && (r < 255 || g < 255 || b < 255))) {
+                                (shape->strokeColor(&r, &g, &b) == Result::Success && (r < 255 || g < 255 || b < 255))) {
                                 *isMaskWhite = false;
                             }
                         }
-                        scene->push(move(shape));
+                        scene->push(std::move(shape));
                     }
                 }
             }
@@ -801,14 +804,14 @@ unique_ptr<Scene> svgSceneBuild(SvgLoaderData& loaderData, Box vBox, float w, fl
 
     auto viewBoxClip = Shape::gen();
     viewBoxClip->appendRect(0, 0, w, h, 0, 0);
-    viewBoxClip->fill(0, 0, 0, 255);
+    viewBoxClip->fill(0, 0, 0);
 
     auto compositeLayer = Scene::gen();
-    compositeLayer->composite(move(viewBoxClip), CompositeMethod::ClipPath);
-    compositeLayer->push(move(docNode));
+    compositeLayer->composite(std::move(viewBoxClip), CompositeMethod::ClipPath);
+    compositeLayer->push(std::move(docNode));
 
     auto root = Scene::gen();
-    root->push(move(compositeLayer));
+    root->push(std::move(compositeLayer));
 
     loaderData.doc->node.doc.vx = vBox.x;
     loaderData.doc->node.doc.vy = vBox.y;

+ 13 - 0
thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.cpp

@@ -275,3 +275,16 @@ string svgUtilBase64Decode(const char *src)
     }
     return decoded;
 }
+
+
+char* svgUtilStrndup(const char* str, size_t n)
+{
+    auto len = strlen(str);
+    if (len < n) n = len;
+
+    auto ret = (char*)malloc(n + 1);
+    if (!ret) return nullptr;
+    ret[n] = '\0';
+
+    return (char*)memcpy(ret, str, n);
+}

+ 2 - 0
thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.h

@@ -30,4 +30,6 @@ float svgUtilStrtof(const char *nPtr, char **endPtr);
 string svgUtilURLDecode(const char *src);
 string svgUtilBase64Decode(const char *src);
 
+char* svgUtilStrndup(const char* str, size_t n);
+
 #endif //_TVG_SVG_UTIL_H_

+ 3 - 10
thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp

@@ -33,6 +33,7 @@
 #endif
 
 #include "tvgXmlParser.h"
+#include "tvgSvgUtil.h"
 
 /************************************************************************/
 /* Internal Class Implementation                                        */
@@ -238,14 +239,6 @@ static SimpleXMLType _getXMLType(const char* itr, const char* itrEnd, size_t &to
 }
 
 
-static char* _strndup(const char* src, unsigned len)
-{
-    auto ret = (char*)malloc(len + 1);
-    if (!ret) return nullptr;
-    ret[len] = '\0';
-    return (char*)memcpy(ret, src, len);
-}
-
 /************************************************************************/
 /* External Class Implementation                                        */
 /************************************************************************/
@@ -564,10 +557,10 @@ const char* simpleXmlParseCSSAttribute(const char* buf, unsigned bufLength, char
     }
 
     if (p == itr) *tag = strdup("all");
-    else *tag = _strndup(itr, p - itr);
+    else *tag = svgUtilStrndup(itr, p - itr);
 
     if (p == itrEnd) *name = nullptr;
-    else *name = _strndup(p + 1, itrEnd - p - 1);
+    else *name = svgUtilStrndup(p + 1, itrEnd - p - 1);
 
     return (nextElement ? nextElement + 1 : nullptr);
 }

+ 0 - 471
thirdparty/thorvg/src/loaders/tvg/tvgTvgBinInterpreter.cpp

@@ -1,471 +0,0 @@
-/*
- * Copyright (c) 2021 - 2023 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.
- */
-
-#include <memory.h>
-
-#ifdef _WIN32
-    #include <malloc.h>
-#elif defined(__linux__)
-    #include <alloca.h>
-#else
-    #include <stdlib.h>
-#endif
-
-#include "tvgTvgCommon.h"
-
-
-/************************************************************************/
-/* Internal Class Implementation                                        */
-/************************************************************************/
-
-struct TvgBinBlock
-{
-    TvgBinTag type;
-    TvgBinCounter length;
-    const char* data;
-    const char* end;
-};
-
-static Paint* _parsePaint(TvgBinBlock baseBlock);
-
-
-static TvgBinBlock _readBlock(const char *ptr)
-{
-    TvgBinBlock block;
-    block.type = *ptr;
-    READ_UI32(&block.length, ptr + SIZE(TvgBinTag));
-    block.data = ptr + SIZE(TvgBinTag) + SIZE(TvgBinCounter);
-    block.end = block.data + block.length;
-    return block;
-}
-
-
-static bool _parseCmpTarget(const char *ptr, const char *end, Paint *paint)
-{
-    auto block = _readBlock(ptr);
-    if (block.end > end) return false;
-
-    if (block.type != TVG_TAG_PAINT_CMP_METHOD) return false;
-    if (block.length != SIZE(TvgBinFlag)) return false;
-
-    auto cmpMethod = static_cast<CompositeMethod>(*block.data);
-
-    ptr = block.end;
-
-    auto cmpBlock = _readBlock(ptr);
-    if (cmpBlock.end > end) return false;
-
-    paint->composite(unique_ptr<Paint>(_parsePaint(cmpBlock)), cmpMethod);
-
-    return true;
-}
-
-
-static bool _parsePaintProperty(TvgBinBlock block, Paint *paint)
-{
-    switch (block.type) {
-        case TVG_TAG_PAINT_OPACITY: {
-            if (block.length != SIZE(uint8_t)) return false;
-            paint->opacity(*block.data);
-            return true;
-        }
-        case TVG_TAG_PAINT_TRANSFORM: {
-            if (block.length != SIZE(Matrix)) return false;
-            Matrix matrix;
-            memcpy(&matrix, block.data, SIZE(Matrix));
-            paint->transform(matrix);
-            return true;
-        }
-        case TVG_TAG_PAINT_CMP_TARGET: {
-            if (block.length < SIZE(TvgBinTag) + SIZE(TvgBinCounter)) return false;
-            return _parseCmpTarget(block.data, block.end, paint);
-        }
-    }
-    return false;
-}
-
-
-static bool _parseScene(TvgBinBlock block, Paint *paint)
-{
-    auto scene = static_cast<Scene*>(paint);
-
-    //Case1: scene reserve count
-    if (block.type == TVG_TAG_SCENE_RESERVEDCNT) {
-        if (block.length != SIZE(uint32_t)) return false;
-        uint32_t reservedCnt;
-        READ_UI32(&reservedCnt, block.data);
-        scene->reserve(reservedCnt);
-        return true;
-    }
-
-    //Case2: Base Paint Properties
-    if (_parsePaintProperty(block, scene)) return true;
-
-    //Case3: A Child paint
-    if (auto paint = _parsePaint(block)) {
-        scene->push(unique_ptr<Paint>(paint));
-        return true;
-    }
-
-    return false;
-}
-
-
-static bool _parseShapePath(const char *ptr, const char *end, Shape *shape)
-{
-    uint32_t cmdCnt, ptsCnt;
-
-    READ_UI32(&cmdCnt, ptr);
-    ptr += SIZE(cmdCnt);
-
-    READ_UI32(&ptsCnt, ptr);
-    ptr += SIZE(ptsCnt);
-
-    auto cmds = (TvgBinFlag*) ptr;
-    ptr += SIZE(TvgBinFlag) * cmdCnt;
-
-    auto pts = (Point*) ptr;
-    ptr += SIZE(Point) * ptsCnt;
-
-    if (ptr > end) return false;
-
-    /* Recover to PathCommand(4 bytes) from TvgBinFlag(1 byte) */
-    PathCommand* inCmds = (PathCommand*)alloca(sizeof(PathCommand) * cmdCnt);
-    for (uint32_t i = 0; i < cmdCnt; ++i) {
-        inCmds[i] = static_cast<PathCommand>(cmds[i]);
-    }
-
-    shape->appendPath(inCmds, cmdCnt, pts, ptsCnt);
-
-    return true;
-}
-
-
-static unique_ptr<Fill> _parseShapeFill(const char *ptr, const char *end)
-{
-    unique_ptr<Fill> fillGrad;
-
-    while (ptr < end) {
-        auto block = _readBlock(ptr);
-        if (block.end > end) return nullptr;
-
-        switch (block.type) {
-            case TVG_TAG_FILL_RADIAL_GRADIENT: {
-                if (block.length != 3 * SIZE(float)) return nullptr;
-
-                auto ptr = block.data;
-                float x, y, radius;
-
-                READ_FLOAT(&x, ptr);
-                ptr += SIZE(float);
-                READ_FLOAT(&y, ptr);
-                ptr += SIZE(float);
-                READ_FLOAT(&radius, ptr);
-
-                auto fillGradRadial = RadialGradient::gen();
-                fillGradRadial->radial(x, y, radius);
-                fillGrad = move(fillGradRadial);
-                break;
-            }
-            case TVG_TAG_FILL_LINEAR_GRADIENT: {
-                if (block.length != 4 * SIZE(float)) return nullptr;
-
-                auto ptr = block.data;
-                float x1, y1, x2, y2;
-
-                READ_FLOAT(&x1, ptr);
-                ptr += SIZE(float);
-                READ_FLOAT(&y1, ptr);
-                ptr += SIZE(float);
-                READ_FLOAT(&x2, ptr);
-                ptr += SIZE(float);
-                READ_FLOAT(&y2, ptr);
-
-                auto fillGradLinear = LinearGradient::gen();
-                fillGradLinear->linear(x1, y1, x2, y2);
-                fillGrad = move(fillGradLinear);
-                break;
-            }
-            case TVG_TAG_FILL_FILLSPREAD: {
-                if (!fillGrad) return nullptr;
-                if (block.length != SIZE(TvgBinFlag)) return nullptr;
-                fillGrad->spread((FillSpread) *block.data);
-                break;
-            }
-            case TVG_TAG_FILL_COLORSTOPS: {
-                if (!fillGrad) return nullptr;
-                if (block.length == 0 || block.length & 0x07) return nullptr;
-                uint32_t stopsCnt = block.length >> 3; // 8 bytes per ColorStop
-                if (stopsCnt > 1023) return nullptr;
-                Fill::ColorStop* stops = (Fill::ColorStop*)alloca(sizeof(Fill::ColorStop) * stopsCnt);
-                auto p = block.data;
-                for (uint32_t i = 0; i < stopsCnt; i++, p += 8) {
-                    READ_FLOAT(&stops[i].offset, p);
-                    stops[i].r = p[4];
-                    stops[i].g = p[5];
-                    stops[i].b = p[6];
-                    stops[i].a = p[7];
-                }
-                fillGrad->colorStops(stops, stopsCnt);
-                break;
-            }
-            case TVG_TAG_FILL_TRANSFORM: {
-                if (!fillGrad || block.length != SIZE(Matrix)) return nullptr;
-                Matrix gradTransform;
-                memcpy(&gradTransform, block.data, SIZE(Matrix));
-                fillGrad->transform(gradTransform);
-                break;
-            }
-            default: {
-                TVGLOG("TVG", "Unsupported tag %d (0x%x) used as one of the fill properties, %d bytes skipped", block.type, block.type, block.length);
-                break;
-            }
-        }
-        ptr = block.end;
-    }
-    return fillGrad;
-}
-
-
-static bool _parseShapeStrokeDashPattern(const char *ptr, const char *end, Shape *shape)
-{
-    uint32_t dashPatternCnt;
-    READ_UI32(&dashPatternCnt, ptr);
-    ptr += SIZE(uint32_t);
-    if (dashPatternCnt > 0) {
-        float* dashPattern = static_cast<float*>(malloc(sizeof(float) * dashPatternCnt));
-        if (!dashPattern) return false;
-        memcpy(dashPattern, ptr, sizeof(float) * dashPatternCnt);
-        ptr += SIZE(float) * dashPatternCnt;
-
-        if (ptr > end) {
-            free(dashPattern);
-            return false;
-        }
-
-        shape->stroke(dashPattern, dashPatternCnt);
-        free(dashPattern);
-    }
-    return true;
-}
-
-
-static bool _parseShapeStroke(const char *ptr, const char *end, Shape *shape)
-{
-    while (ptr < end) {
-        auto block = _readBlock(ptr);
-        if (block.end > end) return false;
-
-        switch (block.type) {
-            case TVG_TAG_SHAPE_STROKE_CAP: {
-                if (block.length != SIZE(TvgBinFlag)) return false;
-                shape->stroke((StrokeCap) *block.data);
-                break;
-            }
-            case TVG_TAG_SHAPE_STROKE_JOIN: {
-                if (block.length != SIZE(TvgBinFlag)) return false;
-                shape->stroke((StrokeJoin) *block.data);
-                break;
-            }
-            case TVG_TAG_SHAPE_STROKE_WIDTH: {
-                if (block.length != SIZE(float)) return false;
-                float width;
-                READ_FLOAT(&width, block.data);
-                shape->stroke(width);
-                break;
-            }
-            case TVG_TAG_SHAPE_STROKE_COLOR: {
-                if (block.length != 4) return false;
-                shape->stroke(block.data[0], block.data[1], block.data[2], block.data[3]);
-                break;
-            }
-            case TVG_TAG_SHAPE_STROKE_FILL: {
-                auto fill = _parseShapeFill(block.data, block.end);
-                if (!fill) return false;
-                shape->stroke(move(move(fill)));
-                break;
-            }
-            case TVG_TAG_SHAPE_STROKE_DASHPTRN: {
-                if (!_parseShapeStrokeDashPattern(block.data, block.end, shape)) return false;
-                break;
-            }
-            default: {
-                TVGLOG("TVG", "Unsupported tag %d (0x%x) used as one of stroke properties, %d bytes skipped", block.type, block.type, block.length);
-                break;
-            }
-        }
-        ptr = block.end;
-    }
-    return true;
-}
-
-
-static bool _parseShape(TvgBinBlock block, Paint* paint)
-{
-    auto shape = static_cast<Shape*>(paint);
-
-    //Case1: Shape specific properties
-    switch (block.type) {
-        case TVG_TAG_SHAPE_PATH: {
-            return _parseShapePath(block.data, block.end, shape);
-        }
-        case TVG_TAG_SHAPE_STROKE: {
-            return _parseShapeStroke(block.data, block.end, shape);
-        }
-        case TVG_TAG_SHAPE_FILL: {
-            auto fill = _parseShapeFill(block.data, block.end);
-            if (!fill) return false;
-            shape->fill(move(fill));
-            return true;
-        }
-        case TVG_TAG_SHAPE_COLOR: {
-            if (block.length != 4) return false;
-            shape->fill(block.data[0], block.data[1], block.data[2], block.data[3]);
-            return true;
-        }
-        case TVG_TAG_SHAPE_FILLRULE: {
-            if (block.length != SIZE(TvgBinFlag)) return false;
-            shape->fill((FillRule)*block.data);
-            return true;
-        }
-    }
-
-    //Case2: Base Paint Properties
-    return _parsePaintProperty(block, shape);
-}
-
-
-static bool _parsePicture(TvgBinBlock block, Paint* paint)
-{
-    auto picture = static_cast<Picture*>(paint);
-
-    switch (block.type) {
-        case TVG_TAG_PICTURE_RAW_IMAGE: {
-            if (block.length < 2 * SIZE(uint32_t)) return false;
-
-            auto ptr = block.data;
-            uint32_t w, h;
-
-            READ_UI32(&w, ptr);
-            ptr += SIZE(uint32_t);
-            READ_UI32(&h, ptr);
-            ptr += SIZE(uint32_t);
-
-            auto size = w * h * SIZE(uint32_t);
-            if (block.length != 2 * SIZE(uint32_t) + size) return false;
-
-            picture->load((uint32_t*) ptr, w, h, true);
-
-            return true;
-        }
-        case TVG_TAG_PICTURE_MESH: {
-            if (block.length < 1 * SIZE(uint32_t)) return false;
-
-            auto ptr = block.data;
-            uint32_t meshCnt;
-            READ_UI32(&meshCnt, ptr);
-            ptr += SIZE(uint32_t);
-
-            auto size = meshCnt * SIZE(Polygon);
-            if (block.length != SIZE(uint32_t) + size) return false;
-
-            picture->mesh((Polygon*) ptr, meshCnt);
-
-            return true;
-        }
-        //Base Paint Properties
-        default: {
-            if (_parsePaintProperty(block, picture)) return true;
-        }
-    }
-
-    //Vector Picture won't be requested since Saver replaces it with the Scene
-    return false;
-}
-
-
-static Paint* _parsePaint(TvgBinBlock baseBlock)
-{
-    bool (*parser)(TvgBinBlock, Paint*);
-    Paint *paint;
-
-    //1. Decide the type of paint.
-    switch (baseBlock.type) {
-        case TVG_TAG_CLASS_SCENE: {
-            paint = Scene::gen().release();
-            parser = _parseScene;
-            break;
-        }
-        case TVG_TAG_CLASS_SHAPE: {
-            paint = Shape::gen().release();
-            parser = _parseShape;
-            break;
-        }
-        case TVG_TAG_CLASS_PICTURE: {
-            paint = Picture::gen().release();
-            parser = _parsePicture;
-            break;
-        }
-        default: {
-            TVGERR("TVG", "Invalid Paint Type %d (0x%x)", baseBlock.type, baseBlock.type);
-            return nullptr;
-        }
-    }
-
-    auto ptr = baseBlock.data;
-
-    //2. Read Subsequent properties of the current paint.
-    while (ptr < baseBlock.end) {
-        auto block = _readBlock(ptr);
-        if (block.end > baseBlock.end) return paint;
-        if (!parser(block, paint)) {
-            TVGERR("TVG", "Encountered the wrong paint properties... Paint Class %d (0x%x)", baseBlock.type, baseBlock.type);
-            return paint;
-        }
-        ptr = block.end;
-    }
-    return paint;
-}
-
-
-
-/************************************************************************/
-/* External Class Implementation                                        */
-/************************************************************************/
-
-unique_ptr<Scene> TvgBinInterpreter::run(const char *ptr, const char* end)
-{
-    auto scene = Scene::gen();
-    if (!scene) return nullptr;
-
-    while (ptr < end) {
-        auto block = _readBlock(ptr);
-        if (block.end > end) {
-            TVGERR("TVG", "Corrupted tvg file.");
-            return nullptr;
-        }
-        scene->push(unique_ptr<Paint>(_parsePaint(block)));
-        ptr = block.end;
-    }
-
-    return scene;
-}

+ 0 - 54
thirdparty/thorvg/src/loaders/tvg/tvgTvgCommon.h

@@ -1,54 +0,0 @@
-/*
- * Copyright (c) 2021 - 2023 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.
- */
-
-#ifndef _TVG_TVG_COMMON_H_
-#define _TVG_TVG_COMMON_H_
-
-#include "tvgCommon.h"
-#include "tvgBinaryDesc.h"
-
-#define SIZE(A) sizeof(A)
-#define READ_UI32(dst, src) memcpy(dst, (src), sizeof(uint32_t))
-#define READ_FLOAT(dst, src) memcpy(dst, (src), sizeof(float))
-
-
-/* Interface for Tvg Binary Interpreter */
-class TvgBinInterpreterBase
-{
-public:
-    virtual ~TvgBinInterpreterBase() {}
-
-    /* ptr: points the tvg binary body (after header)
-       end: end of the tvg binary data */
-    virtual unique_ptr<Scene> run(const char* ptr, const char* end) = 0;
-};
-
-
-/* Version 0 */
-class TvgBinInterpreter : public TvgBinInterpreterBase
-{
-public:
-    unique_ptr<Scene> run(const char* ptr, const char* end) override;
-};
-
-
-#endif  //_TVG_TVG_COMMON_H_

+ 0 - 233
thirdparty/thorvg/src/loaders/tvg/tvgTvgLoader.cpp

@@ -1,233 +0,0 @@
-/*
- * Copyright (c) 2021 - 2023 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.
- */
-
-#include <memory.h>
-#include <fstream>
-#include "tvgLoader.h"
-#include "tvgTvgLoader.h"
-#include "tvgLzw.h"
-
-
-/************************************************************************/
-/* Internal Class Implementation                                        */
-/************************************************************************/
-
-
-void TvgLoader::clear()
-{
-    if (copy) free((char*)data);
-    ptr = data = nullptr;
-    size = 0;
-    copy = false;
-
-    if (interpreter) {
-        delete(interpreter);
-        interpreter = nullptr;
-    }
-}
-
-
-/* WARNING: Header format shall not change! */
-bool TvgLoader::readHeader()
-{
-    if (!ptr) return false;
-
-    //Make sure the size is large enough to hold the header
-    if (size < TVG_HEADER_SIZE) return false;
-
-    //1. Signature
-    if (memcmp(ptr, TVG_HEADER_SIGNATURE, TVG_HEADER_SIGNATURE_LENGTH)) return false;
-    ptr += TVG_HEADER_SIGNATURE_LENGTH;
-
-    //2. Version
-    char version[TVG_HEADER_VERSION_LENGTH + 1];
-    memcpy(version, ptr, TVG_HEADER_VERSION_LENGTH);
-    version[TVG_HEADER_VERSION_LENGTH - 1] = '\0';
-    ptr += TVG_HEADER_VERSION_LENGTH;
-    this->version = atoi(version);
-    if (this->version > THORVG_VERSION_NUMBER()) {
-        TVGLOG("TVG", "This TVG file expects a higher version(%d) of ThorVG symbol(%d)", this->version, THORVG_VERSION_NUMBER());
-    }
-
-    //3. View Size
-    READ_FLOAT(&w, ptr);
-    ptr += SIZE(float);
-    READ_FLOAT(&h, ptr);
-    ptr += SIZE(float);
-
-    //4. Reserved
-    if (*ptr & TVG_HEAD_FLAG_COMPRESSED) compressed = true;
-    ptr += TVG_HEADER_RESERVED_LENGTH;
-
-    //5. Compressed Size if any
-    if (compressed) {
-        auto p = ptr;
-
-        //TVG_HEADER_UNCOMPRESSED_SIZE
-        memcpy(&uncompressedSize, p, sizeof(uint32_t));
-        p += SIZE(uint32_t);
-
-        //TVG_HEADER_COMPRESSED_SIZE
-        memcpy(&compressedSize, p, sizeof(uint32_t));
-        p += SIZE(uint32_t);
-
-        //TVG_HEADER_COMPRESSED_SIZE_BITS
-        memcpy(&compressedSizeBits, p, sizeof(uint32_t));
-    }
-
-    ptr += TVG_HEADER_COMPRESS_SIZE;
-
-    //Decide the proper Tvg Binary Interpreter based on the current file version
-    if (this->version >= 0) interpreter = new TvgBinInterpreter;
-
-    return true;
-}
-
-
-/************************************************************************/
-/* External Class Implementation                                        */
-/************************************************************************/
-
-TvgLoader::~TvgLoader()
-{
-    close();
-}
-
-
-bool TvgLoader::open(const string &path)
-{
-    clear();
-
-    ifstream f;
-    f.open(path, ifstream::in | ifstream::binary | ifstream::ate);
-
-    if (!f.is_open()) return false;
-
-    size = f.tellg();
-    f.seekg(0, ifstream::beg);
-
-    copy = true;
-    data = (char*)malloc(size);
-    if (!data) {
-        clear();
-        f.close();
-        return false;
-    }
-
-    if (!f.read((char*)data, size))
-    {
-        clear();
-        f.close();
-        return false;
-    }
-
-    f.close();
-
-    ptr = data;
-
-    return readHeader();
-}
-
-
-bool TvgLoader::open(const char *data, uint32_t size, bool copy)
-{
-    clear();
-
-    if (copy) {
-        this->data = (char*)malloc(size);
-        if (!this->data) return false;
-        memcpy((char*)this->data, data, size);
-    } else this->data = data;
-
-    this->ptr = this->data;
-    this->size = size;
-    this->copy = copy;
-
-    return readHeader();
-}
-
-
-bool TvgLoader::resize(Paint* paint, float w, float h)
-{
-    if (!paint) return false;
-
-    auto sx = w / this->w;
-    auto sy = h / this->h;
-
-    //Scale
-    auto scale = sx < sy ? sx : sy;
-    paint->scale(scale);
-
-    //Align
-    float tx = 0, ty = 0;
-    auto sw = this->w * scale;
-    auto sh = this->h * scale;
-    if (sw > sh) ty -= (h - sh) * 0.5f;
-    else tx -= (w - sw) * 0.5f;
-    paint->translate(-tx, -ty);
-
-    return true;
-}
-
-
-bool TvgLoader::read()
-{
-    if (!ptr || size == 0) return false;
-
-    TaskScheduler::request(this);
-
-    return true;
-}
-
-
-bool TvgLoader::close()
-{
-    this->done();
-    clear();
-    return true;
-}
-
-
-void TvgLoader::run(unsigned tid)
-{
-    if (root) root.reset();
-
-    auto data = const_cast<char*>(ptr);
-
-    if (compressed) {
-        data = (char*) lzwDecode((uint8_t*) data, compressedSize, compressedSizeBits, uncompressedSize);
-        root = interpreter->run(data, data + uncompressedSize);
-        free(data);
-    } else {
-        root = interpreter->run(data, this->data + size);
-    }
-
-    if (!root) clear();
-}
-
-
-unique_ptr<Paint> TvgLoader::paint()
-{
-    this->done();
-    if (root) return move(root);
-    return nullptr;
-}

+ 0 - 61
thirdparty/thorvg/src/loaders/tvg/tvgTvgLoader.h

@@ -1,61 +0,0 @@
-/*
- * Copyright (c) 2021 - 2023 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.
- */
-
-#ifndef _TVG_TVG_LOADER_H_
-#define _TVG_TVG_LOADER_H_
-
-#include "tvgTaskScheduler.h"
-#include "tvgTvgCommon.h"
-
-
-class TvgLoader : public LoadModule, public Task
-{
-public:
-    const char* data = nullptr;
-    const char* ptr = nullptr;
-    uint32_t size = 0;
-    uint16_t version = 0;
-    unique_ptr<Scene> root = nullptr;
-    TvgBinInterpreterBase* interpreter = nullptr;
-    uint32_t uncompressedSize = 0;
-    uint32_t compressedSize = 0;
-    uint32_t compressedSizeBits = 0;
-    bool copy = false;
-    bool compressed = false;
-
-    ~TvgLoader();
-
-    using LoadModule::open;
-    bool open(const string &path) override;
-    bool open(const char *data, uint32_t size, bool copy) override;
-    bool read() override;
-    bool close() override;
-    bool resize(Paint* paint, float w, float h) override;
-    unique_ptr<Paint> paint() override;
-
-private:
-    bool readHeader();
-    void run(unsigned tid) override;
-    void clear();
-};
-
-#endif //_TVG_TVG_LOADER_H_

+ 0 - 792
thirdparty/thorvg/src/savers/tvg/tvgTvgSaver.cpp

@@ -1,792 +0,0 @@
-/*
- * Copyright (c) 2021 - 2023 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.
- */
-
-#include "tvgMath.h"
-#include "tvgSaveModule.h"
-#include "tvgTvgSaver.h"
-#include "tvgLzw.h"
-
-#include <cstring>
-
-#ifdef _WIN32
-    #include <malloc.h>
-#elif defined(__linux__)
-    #include <alloca.h>
-#else
-    #include <stdlib.h>
-#endif
-
-static FILE* _fopen(const char* filename, const char* mode)
-{
-#if defined(_MSC_VER) && defined(__clang__)
-    FILE *fp;
-    auto err = fopen_s(&fp, filename, mode);
-    if (err != 0) return nullptr;
-    return fp;
-#else
-    auto fp = fopen(filename, mode);
-    if (!fp) return nullptr;
-    return fp;
-#endif
-}
-
-#define SIZE(A) sizeof(A)
-
-/************************************************************************/
-/* Internal Class Implementation                                        */
-/************************************************************************/
-
-static inline TvgBinCounter SERIAL_DONE(TvgBinCounter cnt)
-{
-    return SIZE(TvgBinTag) + SIZE(TvgBinCounter) + cnt;
-}
-
-
-/* if the properties are identical, we can merge the shapes. */
-static bool _merge(Shape* from, Shape* to)
-{
-    uint8_t r, g, b, a;
-    uint8_t r2, g2, b2, a2;
-
-    //fill
-    if (from->fill() || to->fill()) return false;
-
-    r = g = b = a = r2 = g2 = b2 = a2 = 0;
-
-    from->fillColor(&r, &g, &b, &a);
-    to->fillColor(&r2, &g2, &b2, &a2);
-
-    if (r != r2 || g != g2 || b != b2 || a != a2) return false;
-
-    //composition
-    if (from->composite(nullptr) != CompositeMethod::None) return false;
-    if (to->composite(nullptr) != CompositeMethod::None) return false;
-
-    //opacity
-    if (from->opacity() != to->opacity()) return false;
-
-    //transform
-    auto t1 = from->transform();
-    auto t2 = to->transform();
-
-    if (!mathEqual(t1.e11, t2.e11) || !mathEqual(t1.e12, t2.e12) || !mathEqual(t1.e13, t2.e13) ||
-        !mathEqual(t1.e21, t2.e21) || !mathEqual(t1.e22, t2.e22) || !mathEqual(t1.e23, t2.e23) ||
-        !mathEqual(t1.e31, t2.e31) || !mathEqual(t1.e32, t2.e32) || !mathEqual(t1.e33, t2.e33)) {
-       return false;
-    }
-
-    //stroke
-    r = g = b = a = r2 = g2 = b2 = a2 = 0;
-
-    from->strokeColor(&r, &g, &b, &a);
-    to->strokeColor(&r2, &g2, &b2, &a2);
-
-    if (r != r2 || g != g2 || b != b2 || a != a2) return false;
-
-    if (fabs(from->strokeWidth() - to->strokeWidth()) > FLT_EPSILON) return false;
-
-    //OPTIMIZE: Yet we can't merge outlining shapes unless we can support merging shapes feature.
-    if (from->strokeWidth() > 0 || to->strokeWidth() > 0) return false;
-
-    if (from->strokeCap() != to->strokeCap()) return false;
-    if (from->strokeJoin() != to->strokeJoin()) return false;
-    if (from->strokeDash(nullptr) > 0 || to->strokeDash(nullptr) > 0) return false;
-    if (from->strokeFill() || to->strokeFill()) return false;
-
-    //fill rule
-    if (from->fillRule() != to->fillRule()) return false;
-
-    //Good, identical shapes, we can merge them.
-    const PathCommand* cmds = nullptr;
-    auto cmdCnt = from->pathCommands(&cmds);
-
-    const Point* pts = nullptr;
-    auto ptsCnt = from->pathCoords(&pts);
-
-    to->appendPath(cmds, cmdCnt, pts, ptsCnt);
-
-    return true;
-}
-
-
-bool TvgSaver::saveEncoding(const std::string& path)
-{
-    if (!compress) return flushTo(path);
-
-    //Try encoding
-    auto uncompressed = buffer.data + headerSize;
-    auto uncompressedSize = buffer.count - headerSize;
-
-    uint32_t compressedSize, compressedSizeBits;
-
-    auto compressed = lzwEncode(uncompressed, uncompressedSize, &compressedSize, &compressedSizeBits);
-
-    //Failed compression.
-    if (!compressed) return flushTo(path);
-
-    //Optimization is ineffective.
-    if (compressedSize >= uncompressedSize) {
-        free(compressed);
-        return flushTo(path);
-    }
-
-    TVGLOG("TVG_SAVER", "%s, compressed: %d -> %d, saved rate: %3.2f%%", path.c_str(), uncompressedSize, compressedSize, (1 - ((float) compressedSize / (float) uncompressedSize)) * 100);
-
-    //Update compress size in the header.
-    uncompressed -= (TVG_HEADER_COMPRESS_SIZE + TVG_HEADER_RESERVED_LENGTH);
-
-    //Compression Flag
-    *uncompressed |= TVG_HEAD_FLAG_COMPRESSED;
-    uncompressed += TVG_HEADER_RESERVED_LENGTH;
-
-    //Uncompressed Size
-    memcpy(uncompressed, &uncompressedSize, TVG_HEADER_UNCOMPRESSED_SIZE);
-    uncompressed += TVG_HEADER_UNCOMPRESSED_SIZE;
-
-    //Comprssed Size
-    memcpy(uncompressed, &compressedSize, TVG_HEADER_COMPRESSED_SIZE);
-    uncompressed += TVG_HEADER_COMPRESSED_SIZE;
-
-    //Compressed Size Bits
-    memcpy(uncompressed, &compressedSizeBits, TVG_HEADER_COMPRESSED_SIZE_BITS);
-
-    //Good optimization, flush to file.
-    auto fp = _fopen(path.c_str(), "wb+");
-    if (!fp) goto fail;
-
-    //write header
-    if (fwrite(buffer.data, SIZE(uint8_t), headerSize, fp) == 0) goto fail;
-
-    //write compressed data
-    if (fwrite(compressed, SIZE(uint8_t), compressedSize, fp) == 0) goto fail;
-
-    fclose(fp);
-    free(compressed);
-
-    return true;
-
-fail:
-    if (fp) fclose(fp);
-    if (compressed) free(compressed);
-    return false;
-}
-
-
-bool TvgSaver::flushTo(const std::string& path)
-{
-    auto fp = _fopen(path.c_str(), "wb+");
-    if (!fp) return false;
-
-    if (fwrite(buffer.data, SIZE(uint8_t), buffer.count, fp) == 0) {
-        fclose(fp);
-        return false;
-    }
-    fclose(fp);
-
-    return true;
-}
-
-
-/* WARNING: Header format shall not changed! */
-bool TvgSaver::writeHeader()
-{
-    headerSize = TVG_HEADER_SIGNATURE_LENGTH + TVG_HEADER_VERSION_LENGTH + SIZE(vsize) + TVG_HEADER_RESERVED_LENGTH + TVG_HEADER_COMPRESS_SIZE;
-
-    buffer.grow(headerSize);
-
-    //1. Signature
-    auto ptr = buffer.ptr();
-    memcpy(ptr, TVG_HEADER_SIGNATURE, TVG_HEADER_SIGNATURE_LENGTH);
-    ptr += TVG_HEADER_SIGNATURE_LENGTH;
-
-    //2. Version
-    memcpy(ptr, TVG_HEADER_VERSION, TVG_HEADER_VERSION_LENGTH);
-    ptr += TVG_HEADER_VERSION_LENGTH;
-
-    buffer.count += (TVG_HEADER_SIGNATURE_LENGTH + TVG_HEADER_VERSION_LENGTH);
-
-    //3. View Size
-    writeData(vsize, SIZE(vsize));
-    ptr += SIZE(vsize);
-
-    //4. Reserved data + Compress size
-    memset(ptr, 0x00, TVG_HEADER_RESERVED_LENGTH + TVG_HEADER_COMPRESS_SIZE);
-    buffer.count += (TVG_HEADER_RESERVED_LENGTH + TVG_HEADER_COMPRESS_SIZE);
-
-    return true;
-}
-
-
-void TvgSaver::writeTag(TvgBinTag tag)
-{
-    buffer.grow(SIZE(TvgBinTag));
-    memcpy(buffer.ptr(), &tag, SIZE(TvgBinTag));
-    buffer.count += SIZE(TvgBinTag);
-}
-
-
-void TvgSaver::writeCount(TvgBinCounter cnt)
-{
-    buffer.grow(SIZE(TvgBinCounter));
-    memcpy(buffer.ptr(), &cnt, SIZE(TvgBinCounter));
-    buffer.count += SIZE(TvgBinCounter);
-}
-
-
-void TvgSaver::writeReservedCount(TvgBinCounter cnt)
-{
-    memcpy(buffer.ptr() - cnt - SIZE(TvgBinCounter), &cnt, SIZE(TvgBinCounter));
-}
-
-
-void TvgSaver::reserveCount()
-{
-    buffer.grow(SIZE(TvgBinCounter));
-    buffer.count += SIZE(TvgBinCounter);
-}
-
-
-TvgBinCounter TvgSaver::writeData(const void* data, TvgBinCounter cnt)
-{
-    buffer.grow(cnt);
-    memcpy(buffer.ptr(), data, cnt);
-    buffer.count += cnt;
-
-    return cnt;
-}
-
-
-TvgBinCounter TvgSaver::writeTagProperty(TvgBinTag tag, TvgBinCounter cnt, const void* data)
-{
-    auto growCnt = SERIAL_DONE(cnt);
-
-    buffer.grow(growCnt);
-
-    auto ptr = buffer.ptr();
-
-    *ptr = tag;
-    ++ptr;
-
-    memcpy(ptr, &cnt, SIZE(TvgBinCounter));
-    ptr += SIZE(TvgBinCounter);
-
-    memcpy(ptr, data, cnt);
-    ptr += cnt;
-
-    buffer.count += growCnt;
-
-    return growCnt;
-}
-
-
-TvgBinCounter TvgSaver::writeTransform(const Matrix* transform, TvgBinTag tag)
-{
-    if (!mathIdentity(transform)) return writeTagProperty(tag, SIZE(Matrix), transform);
-    return 0;
-}
-
-
-TvgBinCounter TvgSaver::serializePaint(const Paint* paint, const Matrix* pTransform)
-{
-    TvgBinCounter cnt = 0;
-
-    //opacity
-    auto opacity = paint->opacity();
-    if (opacity < 255) {
-        cnt += writeTagProperty(TVG_TAG_PAINT_OPACITY, SIZE(opacity), &opacity);
-    }
-
-    //composite
-    const Paint* cmpTarget = nullptr;
-    auto cmpMethod = paint->composite(&cmpTarget);
-    if (cmpMethod != CompositeMethod::None && cmpTarget) {
-        cnt += serializeComposite(cmpTarget, cmpMethod, pTransform);
-    }
-
-    return cnt;
-}
-
-
-/* Propagate parents properties to the child so that we can skip saving the parent. */
-TvgBinCounter TvgSaver::serializeChild(const Paint* parent, const Paint* child, const Matrix* transform)
-{
-    const Paint* compTarget = nullptr;
-    auto compMethod = parent->composite(&compTarget);
-
-    /* If the parent & the only child have composition, we can't skip the parent...
-       Or if the parent has the transform and composition, we can't skip the parent... */
-    if (compMethod != CompositeMethod::None) {
-        if (transform || child->composite(nullptr) != CompositeMethod::None) return 0;
-    }
-
-    //propagate opacity
-    uint32_t opacity = parent->opacity();
-
-    if (opacity < 255) {
-        uint32_t tmp = (child->opacity() * opacity);
-        if (tmp > 0) tmp /= 255;
-        const_cast<Paint*>(child)->opacity(tmp);
-    }
-
-    //propagate composition
-    if (compTarget) const_cast<Paint*>(child)->composite(unique_ptr<Paint>(compTarget->duplicate()), compMethod);
-
-    return serialize(child, transform);
-}
-
-
-TvgBinCounter TvgSaver::serializeScene(const Scene* scene, const Matrix* pTransform, const Matrix* cTransform)
-{
-    auto it = IteratorAccessor::iterator(scene);
-    if (it->count() == 0) {
-        delete(it);
-        return 0;
-    }
-
-    //Case - Only Child: Skip saving this scene.
-    if (it->count() == 1) {
-        auto cnt = serializeChild(scene, it->next(), cTransform);
-        if (cnt > 0) {
-            delete(it);
-            return cnt;
-        }
-    }
-
-    it->begin();
-
-    //Case - Delegator Scene: This scene is just a delegator, we can skip this:
-    if (scene->composite(nullptr) == CompositeMethod::None && scene->opacity() == 255) {
-        auto ret = serializeChildren(it, cTransform, false);
-        delete(it);
-        return ret;
-    }
-
-    //Case - Serialize Scene & its children
-    writeTag(TVG_TAG_CLASS_SCENE);
-    reserveCount();
-
-    auto cnt = serializeChildren(it, cTransform, true) + serializePaint(scene, pTransform);
-
-    delete(it);
-
-    writeReservedCount(cnt);
-
-    return SERIAL_DONE(cnt);
-}
-
-
-TvgBinCounter TvgSaver::serializeFill(const Fill* fill, TvgBinTag tag, const Matrix* pTransform)
-{
-    const Fill::ColorStop* stops = nullptr;
-    auto stopsCnt = fill->colorStops(&stops);
-    if (!stops || stopsCnt == 0) return 0;
-
-    writeTag(tag);
-    reserveCount();
-
-    TvgBinCounter cnt = 0;
-
-    //radial fill
-    if (fill->identifier() == TVG_CLASS_ID_RADIAL) {
-        float args[3];
-        static_cast<const RadialGradient*>(fill)->radial(args, args + 1, args + 2);
-        cnt += writeTagProperty(TVG_TAG_FILL_RADIAL_GRADIENT, SIZE(args), args);
-    //linear fill
-    } else {
-        float args[4];
-        static_cast<const LinearGradient*>(fill)->linear(args, args + 1, args + 2, args + 3);
-        cnt += writeTagProperty(TVG_TAG_FILL_LINEAR_GRADIENT, SIZE(args), args);
-    }
-
-    if (auto flag = static_cast<TvgBinFlag>(fill->spread()))
-        cnt += writeTagProperty(TVG_TAG_FILL_FILLSPREAD, SIZE(TvgBinFlag), &flag);
-    cnt += writeTagProperty(TVG_TAG_FILL_COLORSTOPS, stopsCnt * SIZE(Fill::ColorStop), stops);
-
-    auto gTransform = fill->transform();
-    if (pTransform) gTransform = mathMultiply(pTransform, &gTransform);
-
-    cnt += writeTransform(&gTransform, TVG_TAG_FILL_TRANSFORM);
-
-    writeReservedCount(cnt);
-
-    return SERIAL_DONE(cnt);
-}
-
-
-TvgBinCounter TvgSaver::serializeStroke(const Shape* shape, const Matrix* pTransform, bool preTransform)
-{
-    writeTag(TVG_TAG_SHAPE_STROKE);
-    reserveCount();
-
-    //width
-    auto width = shape->strokeWidth();
-    if (preTransform) width *= sqrtf(powf(pTransform->e11, 2.0f) + powf(pTransform->e21, 2.0f));  //we know x/y scaling factors are same.
-    auto cnt = writeTagProperty(TVG_TAG_SHAPE_STROKE_WIDTH, SIZE(width), &width);
-
-    //cap
-    if (auto flag = static_cast<TvgBinFlag>(shape->strokeCap()))
-        cnt += writeTagProperty(TVG_TAG_SHAPE_STROKE_CAP, SIZE(TvgBinFlag), &flag);
-
-    //join
-    if (auto flag = static_cast<TvgBinFlag>(shape->strokeJoin()))
-        cnt += writeTagProperty(TVG_TAG_SHAPE_STROKE_JOIN, SIZE(TvgBinFlag), &flag);
-
-    //fill
-    if (auto fill = shape->strokeFill()) {
-        cnt += serializeFill(fill, TVG_TAG_SHAPE_STROKE_FILL, (preTransform ? pTransform : nullptr));
-    } else {
-        uint8_t color[4] = {0, 0, 0, 0};
-        shape->strokeColor(color, color + 1, color + 2, color + 3);
-        cnt += writeTagProperty(TVG_TAG_SHAPE_STROKE_COLOR, SIZE(color), &color);
-    }
-
-    //dash
-    const float* dashPattern = nullptr;
-    auto dashCnt = shape->strokeDash(&dashPattern);
-    if (dashPattern && dashCnt > 0) {
-        TvgBinCounter dashCntSize = SIZE(dashCnt);
-        TvgBinCounter dashPtrnSize = dashCnt * SIZE(dashPattern[0]);
-
-        writeTag(TVG_TAG_SHAPE_STROKE_DASHPTRN);
-        writeCount(dashCntSize + dashPtrnSize);
-        cnt += writeData(&dashCnt, dashCntSize);
-        cnt += writeData(dashPattern, dashPtrnSize);
-        cnt += SIZE(TvgBinTag) + SIZE(TvgBinCounter);
-    }
-
-    writeReservedCount(cnt);
-
-    return SERIAL_DONE(cnt);
-}
-
-
-TvgBinCounter TvgSaver::serializePath(const Shape* shape, const Matrix* transform, bool preTransform)
-{
-    const PathCommand* cmds = nullptr;
-    auto cmdCnt = shape->pathCommands(&cmds);
-    const Point* pts = nullptr;
-    auto ptsCnt = shape->pathCoords(&pts);
-
-    if (!cmds || !pts || cmdCnt == 0 || ptsCnt == 0) return 0;
-
-    writeTag(TVG_TAG_SHAPE_PATH);
-    reserveCount();
-
-    /* Reduce the binary size.
-       Convert PathCommand(4 bytes) to TvgBinFlag(1 byte) */
-    TvgBinFlag* outCmds = (TvgBinFlag*)alloca(SIZE(TvgBinFlag) * cmdCnt);
-    for (uint32_t i = 0; i < cmdCnt; ++i) {
-        outCmds[i] = static_cast<TvgBinFlag>(cmds[i]);
-    }
-
-    auto cnt = writeData(&cmdCnt, SIZE(cmdCnt));
-    cnt += writeData(&ptsCnt, SIZE(ptsCnt));
-    cnt += writeData(outCmds, SIZE(TvgBinFlag) * cmdCnt);
-
-    //transform?
-    if (preTransform) {
-        if (!mathEqual(transform->e11, 1.0f) || !mathZero(transform->e12) || !mathZero(transform->e13) ||
-            !mathZero(transform->e21) || !mathEqual(transform->e22, 1.0f) || !mathZero(transform->e23) ||
-            !mathZero(transform->e31) || !mathZero(transform->e32) || !mathEqual(transform->e33, 1.0f)) {
-            auto p = const_cast<Point*>(pts);
-            for (uint32_t i = 0; i < ptsCnt; ++i) mathMultiply(p++, transform);
-        }
-    }
-
-    cnt += writeData(pts, ptsCnt * SIZE(pts[0]));
-
-    writeReservedCount(cnt);
-
-    return SERIAL_DONE(cnt);
-}
-
-
-TvgBinCounter TvgSaver::serializeShape(const Shape* shape, const Matrix* pTransform, const Matrix* cTransform)
-{
-    writeTag(TVG_TAG_CLASS_SHAPE);
-    reserveCount();
-    TvgBinCounter cnt = 0;
-
-    //fill rule
-    if (auto flag = static_cast<TvgBinFlag>(shape->fillRule())) {
-        cnt = writeTagProperty(TVG_TAG_SHAPE_FILLRULE, SIZE(TvgBinFlag), &flag);
-    }
-
-    //the pre-transformation can't be applied in the case when the stroke is dashed or irregulary scaled
-    bool preTransform = true;
-
-    //stroke
-    if (shape->strokeWidth() > 0) {
-        uint8_t color[4] = {0, 0, 0, 0};
-        shape->strokeColor(color, color + 1, color + 2, color + 3);
-        auto fill = shape->strokeFill();
-        if (fill || color[3] > 0) {
-            if (!mathEqual(cTransform->e11, cTransform->e22) || (mathZero(cTransform->e11) && !mathEqual(cTransform->e12, cTransform->e21)) || shape->strokeDash(nullptr) > 0) preTransform = false;
-            cnt += serializeStroke(shape, cTransform, preTransform);
-        }
-    }
-
-    //fill
-    if (auto fill = shape->fill()) {
-        cnt += serializeFill(fill, TVG_TAG_SHAPE_FILL, (preTransform ? cTransform : nullptr));
-    } else {
-        uint8_t color[4] = {0, 0, 0, 0};
-        shape->fillColor(color, color + 1, color + 2, color + 3);
-        if (color[3] > 0) cnt += writeTagProperty(TVG_TAG_SHAPE_COLOR, SIZE(color), color);
-    }
-
-    cnt += serializePath(shape, cTransform, preTransform);
-
-    if (!preTransform) cnt += writeTransform(cTransform, TVG_TAG_PAINT_TRANSFORM);
-    cnt += serializePaint(shape, pTransform);
-
-    writeReservedCount(cnt);
-
-    return SERIAL_DONE(cnt);
-}
-
-
-/* Picture has either a vector scene or a bitmap. */
-TvgBinCounter TvgSaver::serializePicture(const Picture* picture, const Matrix* pTransform, const Matrix* cTransform)
-{
-    auto it = IteratorAccessor::iterator(picture);
-
-    //Case - Vector Scene:
-    if (it->count() == 1) {
-        auto cnt = serializeChild(picture, it->next(), cTransform);
-        //Only child, Skip to save Picture...
-        if (cnt > 0) {
-            delete(it);
-            return cnt;
-        /* Unfortunately, we can't skip the Picture because it might have a compositor,
-           Serialize Scene(instead of the Picture) & its scene. */
-        } else {
-            writeTag(TVG_TAG_CLASS_SCENE);
-            reserveCount();
-            auto cnt = serializeChildren(it, cTransform, true) + serializePaint(picture, pTransform);
-            writeReservedCount(cnt);
-            delete(it);
-            return SERIAL_DONE(cnt);
-        }
-    }
-    delete(it);
-
-    //Case - Bitmap Image:
-    uint32_t w, h;
-    auto pixels = picture->data(&w, &h);
-    if (!pixels) return 0;
-
-    writeTag(TVG_TAG_CLASS_PICTURE);
-    reserveCount();
-
-    TvgBinCounter cnt = 0;
-    TvgBinCounter sizeCnt = SIZE(w);
-    TvgBinCounter imgSize = w * h * SIZE(pixels[0]);
-
-    writeTag(TVG_TAG_PICTURE_RAW_IMAGE);
-    writeCount(2 * sizeCnt + imgSize);
-
-    cnt += writeData(&w, sizeCnt);
-    cnt += writeData(&h, sizeCnt);
-    cnt += writeData(pixels, imgSize);
-    cnt += SIZE(TvgBinTag) + SIZE(TvgBinCounter);
-
-    //mesh: currently only available in bitmap image.
-    const Polygon* triangles = nullptr;
-    auto triangleCnt = picture->mesh(&triangles);
-    if (triangles && triangleCnt > 0) {
-        TvgBinCounter triangleCntSize = SIZE(triangleCnt);
-        TvgBinCounter trianglesSize = triangleCnt * SIZE(triangles[0]);
-
-        writeTag(TVG_TAG_PICTURE_MESH);
-        writeCount(triangleCntSize + trianglesSize);
-        cnt += writeData(&triangleCnt, triangleCntSize);
-        cnt += writeData(triangles, trianglesSize);
-        cnt += SIZE(TvgBinTag) + SIZE(TvgBinCounter);
-    }
-
-    //Bitmap picture needs the transform info.
-    cnt += writeTransform(cTransform, TVG_TAG_PAINT_TRANSFORM);
-
-    cnt += serializePaint(picture, pTransform);
-
-    writeReservedCount(cnt);
-
-    return SERIAL_DONE(cnt);
-}
-
-
-TvgBinCounter TvgSaver::serializeComposite(const Paint* cmpTarget, CompositeMethod cmpMethod, const Matrix* pTransform)
-{
-    writeTag(TVG_TAG_PAINT_CMP_TARGET);
-    reserveCount();
-
-    auto flag = static_cast<TvgBinFlag>(cmpMethod);
-    auto cnt = writeTagProperty(TVG_TAG_PAINT_CMP_METHOD, SIZE(TvgBinFlag), &flag);
-
-    cnt += serialize(cmpTarget, pTransform, true);
-
-    writeReservedCount(cnt);
-
-    return SERIAL_DONE(cnt);
-}
-
-
-TvgBinCounter TvgSaver::serializeChildren(Iterator* it, const Matrix* pTransform, bool reserved)
-{
-    TvgBinCounter cnt = 0;
-
-    //Merging shapes. the result is written in the children.
-    Array<const Paint*> children;
-    children.reserve(it->count());
-    children.push(it->next());
-
-    while (auto child = it->next()) {
-        if (child->identifier() == TVG_CLASS_ID_SHAPE) {
-            //only dosable if the previous child is a shape.
-            auto target = children.ptr() - 1;
-            if ((*target)->identifier() == TVG_CLASS_ID_SHAPE) {
-                if (_merge((Shape*)child, (Shape*)*target)) {
-                    continue;
-                }
-            }
-        }
-        children.push(child);
-    }
-
-    //The children of a reserved scene
-    if (reserved && children.count > 1) {
-        cnt += writeTagProperty(TVG_TAG_SCENE_RESERVEDCNT, SIZE(children.count), &children.count);
-    }
-
-    //Serialize merged children.
-    auto child = children.data;
-    for (uint32_t i = 0; i < children.count; ++i, ++child) {
-        cnt += serialize(*child, pTransform);
-    }
-
-    return cnt;
-}
-
-
-TvgBinCounter TvgSaver::serialize(const Paint* paint, const Matrix* pTransform, bool compTarget)
-{
-    if (!paint) return 0;
-
-    //Invisible paint, no point to save it if the paint is not the composition target...
-    if (!compTarget && paint->opacity() == 0) return 0;
-
-    auto transform = const_cast<Paint*>(paint)->transform();
-    if (pTransform) transform = mathMultiply(pTransform, &transform);
-
-    switch (paint->identifier()) {
-        case TVG_CLASS_ID_SHAPE: return serializeShape(static_cast<const Shape*>(paint), pTransform, &transform);
-        case TVG_CLASS_ID_SCENE: return serializeScene(static_cast<const Scene*>(paint), pTransform, &transform);
-        case TVG_CLASS_ID_PICTURE: return serializePicture(static_cast<const Picture*>(paint), pTransform, &transform);
-    }
-
-    return 0;
-}
-
-
-void TvgSaver::run(unsigned tid)
-{
-    if (!writeHeader()) return;
-
-    //Serialize Root Paint, without its transform.
-    Matrix transform = {1, 0, 0, 0, 1, 0, 0, 0, 1};
-
-    if (paint->opacity() > 0) {
-        switch (paint->identifier()) {
-            case TVG_CLASS_ID_SHAPE: {
-                serializeShape(static_cast<const Shape*>(paint), nullptr, &transform);
-                break;
-            }
-            case TVG_CLASS_ID_SCENE: {
-                serializeScene(static_cast<const Scene*>(paint), nullptr, &transform);
-                break;
-            }
-            case TVG_CLASS_ID_PICTURE: {
-                serializePicture(static_cast<const Picture*>(paint), nullptr, &transform);
-                break;
-            }
-        }
-    }
-
-    if (!saveEncoding(path)) return;
-}
-
-
-/************************************************************************/
-/* External Class Implementation                                        */
-/************************************************************************/
-
-TvgSaver::~TvgSaver()
-{
-    close();
-}
-
-
-bool TvgSaver::close()
-{
-    this->done();
-
-    if (paint) {
-        delete(paint);
-        paint = nullptr;
-    }
-    if (path) {
-        free(path);
-        path = nullptr;
-    }
-    buffer.reset();
-    return true;
-}
-
-
-bool TvgSaver::save(Paint* paint, const string& path, bool compress)
-{
-    close();
-
-    float x, y;
-    x = y = 0;
-    paint->bounds(&x, &y, &vsize[0], &vsize[1], false);
-
-    //cut off the negative space
-    if (x < 0) vsize[0] += x;
-    if (y < 0) vsize[1] += y;
-
-    if (vsize[0] < FLT_EPSILON || vsize[1] < FLT_EPSILON) {
-        TVGLOG("TVG_SAVER", "Saving paint(%p) has zero view size.", paint);
-        return false;
-    }
-
-    this->path = strdup(path.c_str());
-    if (!this->path) return false;
-
-    this->paint = paint;
-    this->compress = compress;
-
-    TaskScheduler::request(this);
-
-    return true;
-}

+ 0 - 78
thirdparty/thorvg/src/savers/tvg/tvgTvgSaver.h

@@ -1,78 +0,0 @@
-/*
- * Copyright (c) 2021 - 2023 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.
- */
-
-#ifndef _TVG_TVGSAVER_H_
-#define _TVG_TVGSAVER_H_
-
-#include "tvgArray.h"
-#include "tvgBinaryDesc.h"
-#include "tvgTaskScheduler.h"
-
-namespace tvg
-{
-
-class TvgSaver : public SaveModule, public Task
-{
-private:
-    Array<TvgBinByte> buffer;
-    Paint* paint = nullptr;
-    char *path = nullptr;
-    uint32_t headerSize;
-    float vsize[2] = {0.0f, 0.0f};
-    bool compress;
-
-    bool flushTo(const std::string& path);
-    bool saveEncoding(const std::string& path);
-    void reserveCount();
-
-    bool writeHeader();
-    bool writeViewSize();
-    void writeTag(TvgBinTag tag);
-    void writeCount(TvgBinCounter cnt);
-    void writeReservedCount(TvgBinCounter cnt);
-    TvgBinCounter writeData(const void* data, TvgBinCounter cnt);
-    TvgBinCounter writeTagProperty(TvgBinTag tag, TvgBinCounter cnt, const void* data);
-    TvgBinCounter writeTransform(const Matrix* transform, TvgBinTag tag);
-
-    TvgBinCounter serialize(const Paint* paint, const Matrix* pTransform, bool compTarget = false);
-    TvgBinCounter serializeScene(const Scene* scene, const Matrix* pTransform, const Matrix* cTransform);
-    TvgBinCounter serializeShape(const Shape* shape, const Matrix* pTransform, const Matrix* cTransform);
-    TvgBinCounter serializePicture(const Picture* picture, const Matrix* pTransform, const Matrix* cTransform);
-    TvgBinCounter serializePaint(const Paint* paint, const Matrix* pTransform);
-    TvgBinCounter serializeFill(const Fill* fill, TvgBinTag tag, const Matrix* pTransform);
-    TvgBinCounter serializeStroke(const Shape* shape, const Matrix* pTransform, bool preTransform);
-    TvgBinCounter serializePath(const Shape* shape, const Matrix* transform, bool preTransform);
-    TvgBinCounter serializeComposite(const Paint* cmpTarget, CompositeMethod cmpMethod, const Matrix* pTransform);
-    TvgBinCounter serializeChildren(Iterator* it, const Matrix* transform, bool reserved);
-    TvgBinCounter serializeChild(const Paint* parent, const Paint* child, const Matrix* pTransform);
-
-public:
-    ~TvgSaver();
-
-    bool save(Paint* paint, const string& path, bool compress) override;
-    bool close() override;
-    void run(unsigned tid) override;
-};
-
-}
-
-#endif  //_TVG_SAVE_MODULE_H_

+ 37 - 21
thirdparty/thorvg/update-thorvg.sh

@@ -1,31 +1,47 @@
-VERSION=0.9.0
-rm -rf AUTHORS inc LICENSE src *.zip
-curl -L -O https://github.com/thorvg/thorvg/archive/v$VERSION.zip
-bsdtar --strip-components=1 -xvf *.zip
-rm *.zip
-rm -rf .github docs pc res test tools tvgcompat .git* *.md *.txt wasm_build.sh CODEOWNERS
-find . -type f -name 'meson.build' -delete
-rm -rf src/bin src/bindings src/examples src/wasm
-rm -rf src/lib/gl_engine src/loaders/external_jpg src/loaders/png
-cat << EOF > inc/config.h
-#ifndef THORVG_CONFIG_H
-#define THORVG_CONFIG_H
+#!/bin/bash -e
 
-#define THORVG_SW_RASTER_SUPPORT 1
+VERSION=0.10.0
 
-#define THORVG_SVG_LOADER_SUPPORT 1
+rm -rf AUTHORS LICENSE inc/ src/ *.zip *.tar.gz tmp/
 
-#define THORVG_PNG_LOADER_SUPPORT 1
+mkdir tmp/ && pushd tmp/
+
+curl -L -O https://github.com/thorvg/thorvg/archive/v$VERSION.tar.gz
+tar --strip-components=1 -xvf *.tar.gz
+rm *.tar.gz
+find . -type f -name 'meson.build' -delete
 
-#define THORVG_TVG_LOADER_SUPPORT 1
+# Fix newline at end of file.
+for source in $(find ./ -type f \( -iname \*.h -o -iname \*.cpp \)); do
+    sed -i -e '$a\' $source
+done
 
-#define THORVG_TVG_SAVER_SUPPORT 1
+cp -v AUTHORS LICENSE ..
+cp -rv inc ../
 
-#define THORVG_JPG_LOADER_SUPPORT 1
+cat << EOF > ../inc/config.h
+#ifndef THORVG_CONFIG_H
+#define THORVG_CONFIG_H
+
+#define THORVG_SW_RASTER_SUPPORT
+
+#define THORVG_SVG_LOADER_SUPPORT
 
 #define THORVG_VERSION_STRING "$VERSION"
 #endif
 EOF
-for source in $(find ./ -type f \( -iname \*.h -o -iname \*.cpp \)); do
-    sed -i -e '$a\' $source
-done
+
+mkdir ../src
+cp -rv src/lib ../src/
+# Only sw_engine is enabled.
+rm -rfv ../src/lib/gl_engine
+
+# Only svg loader is enabled.
+mkdir ../src/loaders
+cp -rv src/loaders/svg src/loaders/raw  ../src/loaders/
+
+# Future versions
+# cp -rv src/utils ../src
+
+popd
+rm -rf tmp/

Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác