summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRémi Verschelde <rverschelde@gmail.com>2023-09-27 14:03:38 +0200
committerRémi Verschelde <rverschelde@gmail.com>2023-09-27 14:03:38 +0200
commit214c978f4be618fd5618c86ef67b94c609fee495 (patch)
treeed074c8b335294743b90a3250a2078bcbea26d48
parent737c308dcc34ae9cf488e292e45180581c4e9a5a (diff)
parent81949c2cd23e07db9a078b2206b3e43847e051b9 (diff)
downloadredot-engine-214c978f4be618fd5618c86ef67b94c609fee495.tar.gz
Merge pull request #81862 from akien-mga/thorvg-0.10.6
thorvg: Update to 0.10.7
-rw-r--r--COPYRIGHT.txt2
-rw-r--r--modules/svg/SCsub8
-rw-r--r--modules/text_server_adv/SCsub4
-rw-r--r--modules/text_server_adv/gdextension_build/SConstruct16
-rw-r--r--modules/text_server_fb/SCsub4
-rw-r--r--modules/text_server_fb/gdextension_build/SConstruct16
-rw-r--r--thirdparty/README.md2
-rw-r--r--thirdparty/thorvg/inc/config.h2
-rw-r--r--thirdparty/thorvg/inc/thorvg.h3
-rw-r--r--thirdparty/thorvg/src/lib/sw_engine/tvgSwCommon.h122
-rw-r--r--thirdparty/thorvg/src/lib/sw_engine/tvgSwFill.cpp397
-rw-r--r--thirdparty/thorvg/src/lib/sw_engine/tvgSwImage.cpp2
-rw-r--r--thirdparty/thorvg/src/lib/sw_engine/tvgSwRaster.cpp869
-rw-r--r--thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterTexmap.h245
-rw-r--r--thirdparty/thorvg/src/lib/sw_engine/tvgSwRenderer.cpp48
-rw-r--r--thirdparty/thorvg/src/lib/sw_engine/tvgSwRle.cpp4
-rw-r--r--thirdparty/thorvg/src/lib/sw_engine/tvgSwShape.cpp244
-rw-r--r--thirdparty/thorvg/src/lib/sw_engine/tvgSwStroke.cpp12
-rw-r--r--thirdparty/thorvg/src/lib/tvgAnimation.cpp30
-rw-r--r--thirdparty/thorvg/src/lib/tvgCanvasImpl.h5
-rw-r--r--thirdparty/thorvg/src/lib/tvgCommon.h8
-rw-r--r--thirdparty/thorvg/src/lib/tvgFill.cpp134
-rw-r--r--thirdparty/thorvg/src/lib/tvgFill.h23
-rw-r--r--thirdparty/thorvg/src/lib/tvgLinearGradient.cpp100
-rw-r--r--thirdparty/thorvg/src/lib/tvgLoader.cpp36
-rw-r--r--thirdparty/thorvg/src/lib/tvgPaint.cpp29
-rw-r--r--thirdparty/thorvg/src/lib/tvgPaint.h23
-rw-r--r--thirdparty/thorvg/src/lib/tvgPictureImpl.h11
-rw-r--r--thirdparty/thorvg/src/lib/tvgRadialGradient.cpp98
-rw-r--r--thirdparty/thorvg/src/lib/tvgRender.h39
-rw-r--r--thirdparty/thorvg/src/lib/tvgSceneImpl.h12
-rw-r--r--thirdparty/thorvg/src/lib/tvgShape.cpp23
-rw-r--r--thirdparty/thorvg/src/lib/tvgShapeImpl.h54
-rw-r--r--thirdparty/thorvg/src/lib/tvgTaskScheduler.cpp13
-rw-r--r--thirdparty/thorvg/src/lib/tvgTaskScheduler.h1
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.cpp4
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp88
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h27
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgPath.cpp8
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp163
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.cpp223
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.h5
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp6
-rw-r--r--thirdparty/thorvg/src/utils/tvgArray.h (renamed from thirdparty/thorvg/src/lib/tvgArray.h)27
-rw-r--r--thirdparty/thorvg/src/utils/tvgBezier.cpp (renamed from thirdparty/thorvg/src/lib/tvgBezier.cpp)2
-rw-r--r--thirdparty/thorvg/src/utils/tvgBezier.h (renamed from thirdparty/thorvg/src/lib/tvgBezier.h)0
-rw-r--r--thirdparty/thorvg/src/utils/tvgCompressor.cpp (renamed from thirdparty/thorvg/src/lib/tvgLzw.cpp)75
-rw-r--r--thirdparty/thorvg/src/utils/tvgCompressor.h (renamed from thirdparty/thorvg/src/lib/tvgLzw.h)7
-rw-r--r--thirdparty/thorvg/src/utils/tvgMath.h (renamed from thirdparty/thorvg/src/lib/tvgMath.h)52
-rw-r--r--thirdparty/thorvg/src/utils/tvgStr.cpp239
-rw-r--r--thirdparty/thorvg/src/utils/tvgStr.h37
-rwxr-xr-xthirdparty/thorvg/update-thorvg.sh4
52 files changed, 1988 insertions, 1618 deletions
diff --git a/COPYRIGHT.txt b/COPYRIGHT.txt
index e79817a2e7..8288e79602 100644
--- a/COPYRIGHT.txt
+++ b/COPYRIGHT.txt
@@ -466,7 +466,7 @@ License: Expat
Files: ./thirdparty/thorvg/
Comment: ThorVG
-Copyright: 2020-2022, Samsung Electronics Co., Ltd.
+Copyright: 2020-2023, The ThorVG Project
License: Expat
Files: ./thirdparty/tinyexr/
diff --git a/modules/svg/SCsub b/modules/svg/SCsub
index c4d7671fb3..55b8c4f4a0 100644
--- a/modules/svg/SCsub
+++ b/modules/svg/SCsub
@@ -21,23 +21,22 @@ thirdparty_sources = [
"src/lib/sw_engine/tvgSwShape.cpp",
"src/lib/sw_engine/tvgSwStroke.cpp",
"src/lib/tvgAccessor.cpp",
- "src/lib/tvgBezier.cpp",
"src/lib/tvgCanvas.cpp",
"src/lib/tvgFill.cpp",
"src/lib/tvgGlCanvas.cpp",
"src/lib/tvgInitializer.cpp",
- "src/lib/tvgLinearGradient.cpp",
"src/lib/tvgLoader.cpp",
- "src/lib/tvgLzw.cpp",
"src/lib/tvgPaint.cpp",
"src/lib/tvgPicture.cpp",
- "src/lib/tvgRadialGradient.cpp",
"src/lib/tvgRender.cpp",
"src/lib/tvgSaver.cpp",
"src/lib/tvgScene.cpp",
"src/lib/tvgShape.cpp",
"src/lib/tvgSwCanvas.cpp",
"src/lib/tvgTaskScheduler.cpp",
+ "src/utils/tvgBezier.cpp",
+ "src/utils/tvgCompressor.cpp",
+ "src/utils/tvgStr.cpp",
"src/loaders/raw/tvgRawLoader.cpp",
"src/loaders/svg/tvgSvgCssStyle.cpp",
"src/loaders/svg/tvgSvgLoader.cpp",
@@ -62,6 +61,7 @@ env_thirdparty.Prepend(
thirdparty_dir + "src/lib/sw_engine",
thirdparty_dir + "src/loaders/raw",
thirdparty_dir + "src/loaders/svg",
+ thirdparty_dir + "src/utils",
]
)
# Also requires libpng headers
diff --git a/modules/text_server_adv/SCsub b/modules/text_server_adv/SCsub
index 360741363a..1acff68135 100644
--- a/modules/text_server_adv/SCsub
+++ b/modules/text_server_adv/SCsub
@@ -39,7 +39,9 @@ freetype_enabled = "freetype" in env.module_list
msdfgen_enabled = "msdfgen" in env.module_list
if "svg" in env.module_list:
- env_text_server_adv.Prepend(CPPPATH=["#thirdparty/thorvg/inc", "#thirdparty/thorvg/src/lib"])
+ env_text_server_adv.Prepend(
+ CPPPATH=["#thirdparty/thorvg/inc", "#thirdparty/thorvg/src/lib", "#thirdparty/thorvg/src/utils"]
+ )
# Enable ThorVG static object linking.
env_text_server_adv.Append(CPPDEFINES=["TVG_STATIC"])
diff --git a/modules/text_server_adv/gdextension_build/SConstruct b/modules/text_server_adv/gdextension_build/SConstruct
index 38fd5f6403..bf29ad3016 100644
--- a/modules/text_server_adv/gdextension_build/SConstruct
+++ b/modules/text_server_adv/gdextension_build/SConstruct
@@ -52,23 +52,22 @@ if env["thorvg_enabled"] and env["freetype_enabled"]:
"src/lib/sw_engine/tvgSwShape.cpp",
"src/lib/sw_engine/tvgSwStroke.cpp",
"src/lib/tvgAccessor.cpp",
- "src/lib/tvgBezier.cpp",
"src/lib/tvgCanvas.cpp",
"src/lib/tvgFill.cpp",
"src/lib/tvgGlCanvas.cpp",
"src/lib/tvgInitializer.cpp",
- "src/lib/tvgLinearGradient.cpp",
"src/lib/tvgLoader.cpp",
- "src/lib/tvgLzw.cpp",
"src/lib/tvgPaint.cpp",
"src/lib/tvgPicture.cpp",
- "src/lib/tvgRadialGradient.cpp",
"src/lib/tvgRender.cpp",
"src/lib/tvgSaver.cpp",
"src/lib/tvgScene.cpp",
"src/lib/tvgShape.cpp",
"src/lib/tvgSwCanvas.cpp",
"src/lib/tvgTaskScheduler.cpp",
+ "src/utils/tvgBezier.cpp",
+ "src/utils/tvgCompressor.cpp",
+ "src/utils/tvgStr.cpp",
"src/loaders/raw/tvgRawLoader.cpp",
"src/loaders/svg/tvgSvgCssStyle.cpp",
"src/loaders/svg/tvgSvgLoader.cpp",
@@ -86,6 +85,7 @@ if env["thorvg_enabled"] and env["freetype_enabled"]:
"../../../thirdparty/thorvg/src/lib/sw_engine",
"../../../thirdparty/thorvg/src/loaders/raw",
"../../../thirdparty/thorvg/src/loaders/svg",
+ "../../../thirdparty/thorvg/src/utils",
"../../../thirdparty/libpng",
]
)
@@ -93,7 +93,13 @@ if env["thorvg_enabled"] and env["freetype_enabled"]:
# Enable ThorVG static object linking.
env_tvg.Append(CPPDEFINES=["TVG_STATIC"])
- env.Append(CPPPATH=["../../../thirdparty/thorvg/inc", "../../../thirdparty/thorvg/src/lib"])
+ env.Append(
+ CPPPATH=[
+ "../../../thirdparty/thorvg/inc",
+ "../../../thirdparty/thorvg/src/lib",
+ "../../../thirdparty/thorvg/src/utils",
+ ]
+ )
env.Append(CPPDEFINES=["MODULE_SVG_ENABLED"])
lib = env_tvg.Library(
diff --git a/modules/text_server_fb/SCsub b/modules/text_server_fb/SCsub
index 0da2a54bc2..8705bc430d 100644
--- a/modules/text_server_fb/SCsub
+++ b/modules/text_server_fb/SCsub
@@ -9,7 +9,9 @@ msdfgen_enabled = "msdfgen" in env.module_list
env_text_server_fb = env_modules.Clone()
if "svg" in env.module_list:
- env_text_server_fb.Prepend(CPPPATH=["#thirdparty/thorvg/inc", "#thirdparty/thorvg/src/lib"])
+ env_text_server_fb.Prepend(
+ CPPPATH=["#thirdparty/thorvg/inc", "#thirdparty/thorvg/src/lib", "#thirdparty/thorvg/src/utils"]
+ )
# Enable ThorVG static object linking.
env_text_server_fb.Append(CPPDEFINES=["TVG_STATIC"])
diff --git a/modules/text_server_fb/gdextension_build/SConstruct b/modules/text_server_fb/gdextension_build/SConstruct
index 20e1afa2e5..40bb2dc1b9 100644
--- a/modules/text_server_fb/gdextension_build/SConstruct
+++ b/modules/text_server_fb/gdextension_build/SConstruct
@@ -47,23 +47,22 @@ if env["thorvg_enabled"] and env["freetype_enabled"]:
"src/lib/sw_engine/tvgSwShape.cpp",
"src/lib/sw_engine/tvgSwStroke.cpp",
"src/lib/tvgAccessor.cpp",
- "src/lib/tvgBezier.cpp",
"src/lib/tvgCanvas.cpp",
"src/lib/tvgFill.cpp",
"src/lib/tvgGlCanvas.cpp",
"src/lib/tvgInitializer.cpp",
- "src/lib/tvgLinearGradient.cpp",
"src/lib/tvgLoader.cpp",
- "src/lib/tvgLzw.cpp",
"src/lib/tvgPaint.cpp",
"src/lib/tvgPicture.cpp",
- "src/lib/tvgRadialGradient.cpp",
"src/lib/tvgRender.cpp",
"src/lib/tvgSaver.cpp",
"src/lib/tvgScene.cpp",
"src/lib/tvgShape.cpp",
"src/lib/tvgSwCanvas.cpp",
"src/lib/tvgTaskScheduler.cpp",
+ "src/utils/tvgBezier.cpp",
+ "src/utils/tvgCompressor.cpp",
+ "src/utils/tvgStr.cpp",
"src/loaders/raw/tvgRawLoader.cpp",
"src/loaders/svg/tvgSvgCssStyle.cpp",
"src/loaders/svg/tvgSvgLoader.cpp",
@@ -81,6 +80,7 @@ if env["thorvg_enabled"] and env["freetype_enabled"]:
"../../../thirdparty/thorvg/src/lib/sw_engine",
"../../../thirdparty/thorvg/src/loaders/raw",
"../../../thirdparty/thorvg/src/loaders/svg",
+ "../../../thirdparty/thorvg/src/utils",
"../../../thirdparty/libpng",
]
)
@@ -88,7 +88,13 @@ if env["thorvg_enabled"] and env["freetype_enabled"]:
# Enable ThorVG static object linking.
env_tvg.Append(CPPDEFINES=["TVG_STATIC"])
- env.Append(CPPPATH=["../../../thirdparty/thorvg/inc", "../../../thirdparty/thorvg/src/lib"])
+ env.Append(
+ CPPPATH=[
+ "../../../thirdparty/thorvg/inc",
+ "../../../thirdparty/thorvg/src/lib",
+ "../../../thirdparty/thorvg/src/utils",
+ ]
+ )
env.Append(CPPDEFINES=["MODULE_SVG_ENABLED"])
lib = env_tvg.Library(
diff --git a/thirdparty/README.md b/thirdparty/README.md
index 9da4905943..15d9ad4e8e 100644
--- a/thirdparty/README.md
+++ b/thirdparty/README.md
@@ -815,7 +815,7 @@ instead of `miniz.h` as an external dependency.
## thorvg
- Upstream: https://github.com/thorvg/thorvg
-- Version: 0.10.0 (b8c605583fd7de73209a93a1238e1ba72cce2e8f, 2023)
+- Version: 0.10.7 (026ff4ce7eda10dd0cf80eeaef56fe3a5ed89f93, 2023)
- License: MIT
Files extracted from upstream source:
diff --git a/thirdparty/thorvg/inc/config.h b/thirdparty/thorvg/inc/config.h
index 87125418fb..b2efe3def3 100644
--- a/thirdparty/thorvg/inc/config.h
+++ b/thirdparty/thorvg/inc/config.h
@@ -5,5 +5,5 @@
#define THORVG_SVG_LOADER_SUPPORT
-#define THORVG_VERSION_STRING "0.10.0"
+#define THORVG_VERSION_STRING "0.10.7"
#endif
diff --git a/thirdparty/thorvg/inc/thorvg.h b/thirdparty/thorvg/inc/thorvg.h
index 897296fa9d..a5efc5ec17 100644
--- a/thirdparty/thorvg/inc/thorvg.h
+++ b/thirdparty/thorvg/inc/thorvg.h
@@ -1268,7 +1268,7 @@ public:
*
* @param[in] data A pointer to a memory location where the content of the picture file is stored.
* @param[in] size The size in bytes of the memory occupied by the @p data.
- * @param[in] mimeType Mimetype or extension of data such as "jpg", "jpeg", "svg", "svg+xml", "png", etc. In case an empty string or an unknown type is provided, the loaders will be tried one by one.
+ * @param[in] mimeType Mimetype or extension of data such as "jpg", "jpeg", "lottie", "svg", "svg+xml", "png", etc. In case an empty string or an unknown type is provided, the loaders will be tried one by one.
* @param[in] copy If @c true the data are copied into the engine local buffer, otherwise they are not.
*
* @retval Result::Success When succeed.
@@ -1278,6 +1278,7 @@ public:
*
* @warning: It's the user responsibility to release the @p data memory if the @p copy is @c true.
*
+ * @note If you are unsure about the MIME type, you can provide an empty value like @c "", and thorvg will attempt to figure it out.
* @since 0.5
*/
Result load(const char* data, uint32_t size, const std::string& mimeType, bool copy = false) noexcept;
diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwCommon.h b/thirdparty/thorvg/src/lib/sw_engine/tvgSwCommon.h
index 3d68b56fb8..4cee0b18e2 100644
--- a/thirdparty/thorvg/src/lib/sw_engine/tvgSwCommon.h
+++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwCommon.h
@@ -26,6 +26,8 @@
#include "tvgCommon.h"
#include "tvgRender.h"
+#include <algorithm>
+
#if 0
#include <sys/time.h>
static double timeStamp()
@@ -139,10 +141,11 @@ struct SwFill
};
struct SwRadial {
- float a11, a12, shiftX;
- float a21, a22, shiftY;
- float detSecDeriv;
- float a;
+ float a11, a12, a13;
+ float a21, a22, a23;
+ float fx, fy, fr;
+ float dx, dy, dr;
+ float invA, a;
};
union {
@@ -194,14 +197,14 @@ struct SwStroke
struct SwDashStroke
{
- SwOutline* outline;
- float curLen;
- int32_t curIdx;
- Point ptStart;
- Point ptCur;
- float* pattern;
- uint32_t cnt;
- bool curOpGap;
+ SwOutline* outline = nullptr;
+ float curLen = 0;
+ int32_t curIdx = 0;
+ Point ptStart = {0, 0};
+ Point ptCur = {0, 0};
+ float* pattern = nullptr;
+ uint32_t cnt = 0;
+ bool curOpGap = false;
};
struct SwShape
@@ -235,6 +238,7 @@ struct SwImage
bool scaled = false; //draw scaled image
};
+typedef uint8_t(*SwMask)(uint8_t s, uint8_t d, uint8_t a); //src, dst, alpha
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
@@ -295,7 +299,7 @@ static inline uint32_t INTERPOLATE(uint32_t s, uint32_t d, uint8_t a)
static inline uint8_t INTERPOLATE8(uint8_t s, uint8_t d, uint8_t a)
{
- return ((s * a + 0xff) >> 8) + ((d * ~a + 0xff) >> 8);
+ return (((s) * (a) + 0xff) >> 8) + (((d) * ~(a) + 0xff) >> 8);
}
static inline SwCoord HALF_STROKE(float width)
@@ -363,18 +367,18 @@ static inline uint32_t opBlendDifference(uint32_t s, uint32_t d, TVG_UNUSED uint
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));
+ auto c1 = std::min(255, C1(s) + C1(d) - std::min(255, (C1(s) * C1(d)) << 1));
+ auto c2 = std::min(255, C2(s) + C2(d) - std::min(255, (C2(s) * C2(d)) << 1));
+ auto c3 = std::min(255, C3(s) + C3(d) - std::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);
+ auto c1 = std::min(C1(s) + C1(d), 255);
+ auto c2 = std::min(C2(s) + C2(d), 255);
+ auto c3 = std::min(C3(s) + C3(d), 255);
return JOIN(255, c1, c2, c3);
}
@@ -402,27 +406,27 @@ static inline uint32_t opBlendOverlay(uint32_t s, uint32_t d, TVG_UNUSED uint8_t
{
// 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))));
+ auto c1 = (C1(d) < 128) ? std::min(255, 2 * MULTIPLY(C1(s), C1(d))) : (255 - std::min(255, 2 * MULTIPLY(255 - C1(s), 255 - C1(d))));
+ auto c2 = (C2(d) < 128) ? std::min(255, 2 * MULTIPLY(C2(s), C2(d))) : (255 - std::min(255, 2 * MULTIPLY(255 - C2(s), 255 - C2(d))));
+ auto c3 = (C3(d) < 128) ? std::min(255, 2 * MULTIPLY(C3(s), C3(d))) : (255 - std::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));
+ auto c1 = std::min(C1(s), C1(d));
+ auto c2 = std::min(C2(s), C2(d));
+ auto c3 = std::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));
+ auto c1 = std::max(C1(s), C1(d));
+ auto c2 = std::max(C2(s), C2(d));
+ auto c3 = std::max(C3(s), C3(d));
return JOIN(255, c1, c2, c3);
}
@@ -448,61 +452,21 @@ static inline uint32_t opBlendColorBurn(uint32_t s, uint32_t d, TVG_UNUSED uint8
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))));
+ auto c1 = (C1(s) < 128) ? std::min(255, 2 * MULTIPLY(C1(s), C1(d))) : (255 - std::min(255, 2 * MULTIPLY(255 - C1(s), 255 - C1(d))));
+ auto c2 = (C2(s) < 128) ? std::min(255, 2 * MULTIPLY(C2(s), C2(d))) : (255 - std::min(255, 2 * MULTIPLY(255 - C2(s), 255 - C2(d))));
+ auto c3 = (C3(s) < 128) ? std::min(255, 2 * MULTIPLY(C3(s), C3(d))) : (255 - std::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)));
+ auto c1 = std::min(255, MULTIPLY(255 - std::min(255, 2 * C1(s)), MULTIPLY(C1(d), C1(d))) + 2 * MULTIPLY(C1(s), C1(d)));
+ auto c2 = std::min(255, MULTIPLY(255 - std::min(255, 2 * C2(s)), MULTIPLY(C2(d), C2(d))) + 2 * MULTIPLY(C2(s), C2(d)));
+ auto c3 = std::min(255, MULTIPLY(255 - std::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);
@@ -551,13 +515,19 @@ void imageFree(SwImage* image);
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);
+
//OPTIMIZE_ME: Skip the function pointer access
+void fillLinear(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32_t len, SwMask maskOp, uint8_t opacity); //composite masking ver.
+void fillLinear(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwMask maskOp, uint8_t opacity); //direct masking ver.
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 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); //matting ver.
+
+void fillRadial(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32_t len, SwMask op, uint8_t a); //composite masking ver.
+void fillRadial(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwMask op, uint8_t a) ; //direct 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.
+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); //matting ver.
SwRleData* rleRender(SwRleData* rle, const SwOutline* outline, const SwBBox& renderRegion, bool antiAlias);
SwRleData* rleRender(const SwBBox* bbox);
diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwFill.cpp b/thirdparty/thorvg/src/lib/sw_engine/tvgSwFill.cpp
index 1c6eb4e428..cede9e6eb7 100644
--- a/thirdparty/thorvg/src/lib/sw_engine/tvgSwFill.cpp
+++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwFill.cpp
@@ -22,16 +22,46 @@
#include "tvgMath.h"
#include "tvgSwCommon.h"
-
+#include "tvgFill.h"
/************************************************************************/
/* Internal Class Implementation */
/************************************************************************/
+#define RADIAL_A_THRESHOLD 0.0005f
#define GRADIENT_STOP_SIZE 1024
#define FIXPT_BITS 8
#define FIXPT_SIZE (1<<FIXPT_BITS)
+/*
+ * quadratic equation with the following coefficients (rx and ry defined in the _calculateCoefficients()):
+ * A = a // fill->radial.a
+ * B = 2 * (dr * fr + rx * dx + ry * dy)
+ * C = fr^2 - rx^2 - ry^2
+ * Derivatives are computed with respect to dx.
+ * This procedure aims to optimize and eliminate the need to calculate all values from the beginning
+ * for consecutive x values with a constant y. The Taylor series expansions are computed as long as
+ * its terms are non-zero.
+ */
+static void _calculateCoefficients(const SwFill* fill, uint32_t x, uint32_t y, float& b, float& deltaB, float& det, float& deltaDet, float& deltaDeltaDet)
+{
+ auto radial = &fill->radial;
+
+ auto rx = (x + 0.5f) * radial->a11 + (y + 0.5f) * radial->a12 + radial->a13 - radial->fx;
+ auto ry = (x + 0.5f) * radial->a21 + (y + 0.5f) * radial->a22 + radial->a23 - radial->fy;
+
+ b = (radial->dr * radial->fr + rx * radial->dx + ry * radial->dy) * radial->invA;
+ deltaB = (radial->a11 * radial->dx + radial->a21 * radial->dy) * radial->invA;
+
+ auto rr = rx * rx + ry * ry;
+ auto deltaRr = 2.0f * (rx * radial->a11 + ry * radial->a21) * radial->invA;
+ auto deltaDeltaRr = 2.0f * (radial->a11 * radial->a11 + radial->a21 * radial->a21) * radial->invA;
+
+ det = b * b + (rr - radial->fr * radial->fr) * radial->invA;
+ deltaDet = 2.0f * b * deltaB + deltaB * deltaB + deltaRr + deltaDeltaRr;
+ deltaDeltaDet = 2.0f * deltaB * deltaB + deltaDeltaRr;
+}
+
static bool _updateColorTable(SwFill* fill, const Fill* fdata, const SwSurface* surface, uint8_t opacity)
{
@@ -146,46 +176,62 @@ bool _prepareLinear(SwFill* fill, const LinearGradient* linear, const Matrix* tr
bool _prepareRadial(SwFill* fill, const RadialGradient* radial, const Matrix* transform)
{
- float radius, cx, cy;
- if (radial->radial(&cx, &cy, &radius) != Result::Success) return false;
- if (radius < FLT_EPSILON) return true;
+ auto cx = P(radial)->cx;
+ auto cy = P(radial)->cy;
+ auto r = P(radial)->r;
+ auto fx = P(radial)->fx;
+ auto fy = P(radial)->fy;
+ auto fr = P(radial)->fr;
+
+ if (r < FLT_EPSILON) return true;
+
+ fill->radial.dr = r - fr;
+ fill->radial.dx = cx - fx;
+ fill->radial.dy = cy - fy;
+ fill->radial.fr = fr;
+ fill->radial.fx = fx;
+ fill->radial.fy = fy;
+ fill->radial.a = fill->radial.dr * fill->radial.dr - fill->radial.dx * fill->radial.dx - fill->radial.dy * fill->radial.dy;
+
+ //This condition fulfills the SVG 1.1 std:
+ //the focal point, if outside the end circle, is moved to be on the end circle
+ //See: the SVG 2 std requirements: https://www.w3.org/TR/SVG2/pservers.html#RadialGradientNotes
+ if (fill->radial.a < 0) {
+ auto dist = sqrtf(fill->radial.dx * fill->radial.dx + fill->radial.dy * fill->radial.dy);
+ fill->radial.fx = cx + r * (fx - cx) / dist;
+ fill->radial.fy = cy + r * (fy - cy) / dist;
+ fill->radial.dx = cx - fill->radial.fx;
+ fill->radial.dy = cy - fill->radial.fy;
+ fill->radial.a = fill->radial.dr * fill->radial.dr - fill->radial.dx * fill->radial.dx - fill->radial.dy * fill->radial.dy;
+ }
- float invR = 1.0f / radius;
- fill->radial.shiftX = -cx;
- fill->radial.shiftY = -cy;
- fill->radial.a = radius;
+ if (fill->radial.a > 0) fill->radial.invA = 1.0f / fill->radial.a;
auto gradTransform = radial->transform();
bool isTransformation = !mathIdentity((const Matrix*)(&gradTransform));
- if (isTransformation) {
- if (transform) gradTransform = mathMultiply(transform, &gradTransform);
- } else if (transform) {
- gradTransform = *transform;
- isTransformation = true;
+ if (transform) {
+ if (isTransformation) gradTransform = mathMultiply(transform, &gradTransform);
+ else {
+ gradTransform = *transform;
+ isTransformation = true;
+ }
}
if (isTransformation) {
Matrix invTransform;
if (!mathInverse(&gradTransform, &invTransform)) return false;
-
- fill->radial.a11 = invTransform.e11 * invR;
- fill->radial.a12 = invTransform.e12 * invR;
- fill->radial.shiftX += invTransform.e13;
- fill->radial.a21 = invTransform.e21 * invR;
- fill->radial.a22 = invTransform.e22 * invR;
- fill->radial.shiftY += invTransform.e23;
- fill->radial.detSecDeriv = 2.0f * fill->radial.a11 * fill->radial.a11 + 2 * fill->radial.a21 * fill->radial.a21;
-
- fill->radial.a *= sqrt(pow(invTransform.e11, 2) + pow(invTransform.e21, 2));
+ fill->radial.a11 = invTransform.e11;
+ fill->radial.a12 = invTransform.e12;
+ fill->radial.a13 = invTransform.e13;
+ fill->radial.a21 = invTransform.e21;
+ fill->radial.a22 = invTransform.e22;
+ fill->radial.a23 = invTransform.e23;
} else {
- fill->radial.a11 = fill->radial.a22 = invR;
- fill->radial.a12 = fill->radial.a21 = 0.0f;
- fill->radial.detSecDeriv = 2.0f * invR * invR;
+ fill->radial.a11 = fill->radial.a22 = 1.0f;
+ fill->radial.a12 = fill->radial.a13 = 0.0f;
+ fill->radial.a21 = fill->radial.a23 = 0.0f;
}
- fill->radial.shiftX *= invR;
- fill->radial.shiftY *= invR;
-
return true;
}
@@ -233,77 +279,181 @@ static inline uint32_t _pixel(const SwFill* fill, float pos)
/* External Class Implementation */
/************************************************************************/
+
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;
+ //edge case
+ if (fill->radial.a < RADIAL_A_THRESHOLD) {
+ auto radial = &fill->radial;
+ auto rx = (x + 0.5f) * radial->a11 + (y + 0.5f) * radial->a12 + radial->a13 - radial->fx;
+ auto ry = (x + 0.5f) * radial->a21 + (y + 0.5f) * radial->a22 + radial->a23 - radial->fy;
+
+ if (opacity == 255) {
+ for (uint32_t i = 0 ; i < len ; ++i, ++dst, cmp += csize) {
+ auto x0 = 0.5f * (rx * rx + ry * ry - radial->fr * radial->fr) / (radial->dr * radial->fr + rx * radial->dx + ry * radial->dy);
+ *dst = opBlendNormal(_pixel(fill, x0), *dst, alpha(cmp));
+ rx += radial->a11;
+ ry += radial->a21;
+ }
+ } else {
+ for (uint32_t i = 0 ; i < len ; ++i, ++dst, cmp += csize) {
+ auto x0 = 0.5f * (rx * rx + ry * ry - radial->fr * radial->fr) / (radial->dr * radial->fr + rx * radial->dx + ry * radial->dy);
+ *dst = opBlendNormal(_pixel(fill, x0), *dst, MULTIPLY(opacity, alpha(cmp)));
+ rx += radial->a11;
+ ry += radial->a21;
+ }
+ }
+ } else {
+ float b, deltaB, det, deltaDet, deltaDeltaDet;
+ _calculateCoefficients(fill, x, y, b, deltaB, det, deltaDet, deltaDeltaDet);
+
+ if (opacity == 255) {
+ for (uint32_t i = 0 ; i < len ; ++i, ++dst, cmp += csize) {
+ *dst = opBlendNormal(_pixel(fill, sqrtf(det) - b), *dst, alpha(cmp));
+ det += deltaDet;
+ deltaDet += deltaDeltaDet;
+ b += deltaB;
+ }
+ } else {
+ for (uint32_t i = 0 ; i < len ; ++i, ++dst, cmp += csize) {
+ *dst = opBlendNormal(_pixel(fill, sqrtf(det) - b), *dst, MULTIPLY(opacity, alpha(cmp)));
+ det += deltaDet;
+ deltaDet += deltaDeltaDet;
+ b += deltaB;
+ }
+ }
+ }
+}
- // 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 (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;
+void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, uint8_t a)
+{
+ if (fill->radial.a < RADIAL_A_THRESHOLD) {
+ auto radial = &fill->radial;
+ auto rx = (x + 0.5f) * radial->a11 + (y + 0.5f) * radial->a12 + radial->a13 - radial->fx;
+ auto ry = (x + 0.5f) * radial->a21 + (y + 0.5f) * radial->a22 + radial->a23 - radial->fy;
+ for (uint32_t i = 0; i < len; ++i, ++dst) {
+ auto x0 = 0.5f * (rx * rx + ry * ry - radial->fr * radial->fr) / (radial->dr * radial->fr + rx * radial->dx + ry * radial->dy);
+ *dst = op(_pixel(fill, x0), *dst, a);
+ rx += radial->a11;
+ ry += radial->a21;
}
} 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;
+ float b, deltaB, det, deltaDet, deltaDeltaDet;
+ _calculateCoefficients(fill, x, y, b, deltaB, det, deltaDet, deltaDeltaDet);
+
+ for (uint32_t i = 0; i < len; ++i, ++dst) {
+ *dst = op(_pixel(fill, sqrtf(det) - b), *dst, a);
+ det += deltaDet;
+ deltaDet += deltaDeltaDet;
+ b += deltaB;
}
}
}
-void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, uint8_t a)
+void fillRadial(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32_t len, SwMask maskOp, 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;
+ if (fill->radial.a < RADIAL_A_THRESHOLD) {
+ auto radial = &fill->radial;
+ auto rx = (x + 0.5f) * radial->a11 + (y + 0.5f) * radial->a12 + radial->a13 - radial->fx;
+ auto ry = (x + 0.5f) * radial->a21 + (y + 0.5f) * radial->a22 + radial->a23 - radial->fy;
+ for (uint32_t i = 0 ; i < len ; ++i, ++dst) {
+ auto x0 = 0.5f * (rx * rx + ry * ry - radial->fr * radial->fr) / (radial->dr * radial->fr + rx * radial->dx + ry * radial->dy);
+ auto src = MULTIPLY(a, A(_pixel(fill, x0)));
+ *dst = maskOp(src, *dst, ~src);
+ rx += radial->a11;
+ ry += radial->a21;
+ }
+ } else {
+ float b, deltaB, det, deltaDet, deltaDeltaDet;
+ _calculateCoefficients(fill, x, y, b, deltaB, det, deltaDet, deltaDeltaDet);
+
+ for (uint32_t i = 0 ; i < len ; ++i, ++dst) {
+ auto src = MULTIPLY(a, A(_pixel(fill, sqrtf(det) - b)));
+ *dst = maskOp(src, *dst, ~src);
+ det += deltaDet;
+ deltaDet += deltaDeltaDet;
+ b += deltaB;
+ }
}
}
-void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, SwBlender op2, uint8_t a)
+void fillRadial(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwMask maskOp, 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;
+ if (fill->radial.a < RADIAL_A_THRESHOLD) {
+ auto radial = &fill->radial;
+ auto rx = (x + 0.5f) * radial->a11 + (y + 0.5f) * radial->a12 + radial->a13 - radial->fx;
+ auto ry = (x + 0.5f) * radial->a21 + (y + 0.5f) * radial->a22 + radial->a23 - radial->fy;
+ for (uint32_t i = 0 ; i < len ; ++i, ++dst, ++cmp) {
+ auto x0 = 0.5f * (rx * rx + ry * ry - radial->fr * radial->fr) / (radial->dr * radial->fr + rx * radial->dx + ry * radial->dy);
+ auto src = MULTIPLY(A(A(_pixel(fill, x0))), a);
+ auto tmp = maskOp(src, *cmp, 0);
+ *dst = tmp + MULTIPLY(*dst, ~tmp);
+ rx += radial->a11;
+ ry += radial->a21;
+ }
+ } else {
+ float b, deltaB, det, deltaDet, deltaDeltaDet;
+ _calculateCoefficients(fill, x, y, b, deltaB, det, deltaDet, deltaDeltaDet);
+
+ for (uint32_t i = 0 ; i < len ; ++i, ++dst, ++cmp) {
+ auto src = MULTIPLY(A(_pixel(fill, sqrtf(det))), a);
+ auto tmp = maskOp(src, *cmp, 0);
+ *dst = tmp + MULTIPLY(*dst, ~tmp);
+ deltaDet += deltaDeltaDet;
+ b += deltaB;
+ }
+ }
+}
- // 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;
+void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, SwBlender op2, uint8_t a)
+{
+ if (fill->radial.a < RADIAL_A_THRESHOLD) {
+ auto radial = &fill->radial;
+ auto rx = (x + 0.5f) * radial->a11 + (y + 0.5f) * radial->a12 + radial->a13 - radial->fx;
+ auto ry = (x + 0.5f) * radial->a21 + (y + 0.5f) * radial->a22 + radial->a23 - radial->fy;
+
+ if (a == 255) {
+ for (uint32_t i = 0; i < len; ++i, ++dst) {
+ auto x0 = 0.5f * (rx * rx + ry * ry - radial->fr * radial->fr) / (radial->dr * radial->fr + rx * radial->dx + ry * radial->dy);
+ auto tmp = op(_pixel(fill, x0), *dst, 255);
+ *dst = op2(tmp, *dst, 255);
+ rx += radial->a11;
+ ry += radial->a21;
+ }
+ } else {
+ for (uint32_t i = 0; i < len; ++i, ++dst) {
+ auto x0 = 0.5f * (rx * rx + ry * ry - radial->fr * radial->fr) / (radial->dr * radial->fr + rx * radial->dx + ry * radial->dy);
+ auto tmp = op(_pixel(fill, x0), *dst, 255);
+ auto tmp2 = op2(tmp, *dst, 255);
+ *dst = INTERPOLATE(tmp2, *dst, a);
+ rx += radial->a11;
+ ry += radial->a21;
+ }
}
} 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;
+ float b, deltaB, det, deltaDet, deltaDeltaDet;
+ _calculateCoefficients(fill, x, y, b, deltaB, det, deltaDet, deltaDeltaDet);
+ if (a == 255) {
+ for (uint32_t i = 0 ; i < len ; ++i, ++dst) {
+ auto tmp = op(_pixel(fill, sqrtf(det) - b), *dst, 255);
+ *dst = op2(tmp, *dst, 255);
+ det += deltaDet;
+ deltaDet += deltaDeltaDet;
+ b += deltaB;
+ }
+ } else {
+ for (uint32_t i = 0 ; i < len ; ++i, ++dst) {
+ auto tmp = op(_pixel(fill, sqrtf(det) - b), *dst, 255);
+ auto tmp2 = op2(tmp, *dst, 255);
+ *dst = INTERPOLATE(tmp2, *dst, a);
+ det += deltaDet;
+ deltaDet += deltaDeltaDet;
+ b += deltaB;
+ }
}
}
}
@@ -383,6 +533,95 @@ void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint3
}
+void fillLinear(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32_t len, SwMask maskOp, 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 src = MULTIPLY(a, A(_fixedPixel(fill, static_cast<int32_t>(t * FIXPT_SIZE))));
+ for (uint32_t i = 0; i < len; ++i, ++dst) {
+ *dst = maskOp(src, *dst, ~src);
+ }
+ 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) {
+ auto src = MULTIPLY(_fixedPixel(fill, t2), a);
+ *dst = maskOp(src, *dst, ~src);
+ t2 += inc2;
+ }
+ //we have to fallback to float math
+ } else {
+ uint32_t counter = 0;
+ while (counter++ < len) {
+ auto src = MULTIPLY(_pixel(fill, t / GRADIENT_STOP_SIZE), a);
+ *dst = maskOp(src, *dst, ~src);
+ ++dst;
+ t += inc;
+ }
+ }
+}
+
+
+void fillLinear(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwMask maskOp, 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 src = A(_fixedPixel(fill, static_cast<int32_t>(t * FIXPT_SIZE)));
+ src = MULTIPLY(src, a);
+ for (uint32_t i = 0; i < len; ++i, ++dst, ++cmp) {
+ auto tmp = maskOp(src, *cmp, 0);
+ *dst = tmp + MULTIPLY(*dst, ~tmp);
+ }
+ 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) {
+ auto src = MULTIPLY(a, A(_fixedPixel(fill, t2)));
+ auto tmp = maskOp(src, *cmp, 0);
+ *dst = tmp + MULTIPLY(*dst, ~tmp);
+ t2 += inc2;
+ }
+ //we have to fallback to float math
+ } else {
+ uint32_t counter = 0;
+ while (counter++ < len) {
+ auto src = MULTIPLY(A(_pixel(fill, t / GRADIENT_STOP_SIZE)), a);
+ auto tmp = maskOp(src, *cmp, 0);
+ *dst = tmp + MULTIPLY(*dst, ~tmp);
+ ++dst;
+ ++cmp;
+ t += inc;
+ }
+ }
+}
+
+
void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, uint8_t a)
{
//Rotation
diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwImage.cpp b/thirdparty/thorvg/src/lib/sw_engine/tvgSwImage.cpp
index 4829a8c81d..fb8581b412 100644
--- a/thirdparty/thorvg/src/lib/sw_engine/tvgSwImage.cpp
+++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwImage.cpp
@@ -93,7 +93,7 @@ static bool _genOutline(SwImage* image, const RenderMesh* mesh, const Matrix* tr
outline->types.push(SW_CURVE_TYPE_POINT);
}
- outline->pts.push(outline->pts.data[0]);
+ outline->pts.push(outline->pts[0]);
outline->types.push(SW_CURVE_TYPE_POINT);
outline->cntrs.push(outline->pts.count - 1);
outline->closed.push(true);
diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRaster.cpp b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRaster.cpp
index bd32bf0b23..8fd54c2a4f 100644
--- a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRaster.cpp
+++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRaster.cpp
@@ -39,6 +39,16 @@ constexpr auto DOWN_SCALE_TOLERANCE = 0.5f;
struct FillLinear
{
+ void operator()(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32_t len, SwMask op, uint8_t a)
+ {
+ fillLinear(fill, dst, y, x, len, op, a);
+ }
+
+ void operator()(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwMask op, uint8_t a)
+ {
+ fillLinear(fill, dst, y, x, len, cmp, op, a);
+ }
+
void operator()(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, uint8_t a)
{
fillLinear(fill, dst, y, x, len, op, a);
@@ -58,6 +68,16 @@ struct FillLinear
struct FillRadial
{
+ void operator()(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32_t len, SwMask op, uint8_t a)
+ {
+ fillRadial(fill, dst, y, x, len, op, a);
+ }
+
+ void operator()(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwMask op, uint8_t a)
+ {
+ fillRadial(fill, dst, y, x, len, cmp, op, a);
+ }
+
void operator()(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, uint8_t a)
{
fillRadial(fill, dst, y, x, len, op, a);
@@ -75,9 +95,6 @@ struct FillRadial
};
-static bool _rasterDirectImage(SwSurface* surface, const SwImage* image, const SwBBox& region, uint8_t opacity = 255);
-
-
static inline uint8_t _alpha(uint8_t* a)
{
return *a;
@@ -148,70 +165,70 @@ static inline bool _matting(const SwSurface* surface)
else return false;
}
-
-static inline bool _masking(const SwSurface* surface)
+static inline uint8_t _opMaskNone(uint8_t s, TVG_UNUSED uint8_t d, TVG_UNUSED uint8_t a)
{
- if ((int)surface->compositor->method >= (int)CompositeMethod::AddMask) return true;
- else return false;
+ return s;
}
-
-static inline uint32_t _opMaskAdd(uint32_t s, uint32_t d, uint8_t a)
+static inline uint8_t _opMaskAdd(uint8_t s, uint8_t d, uint8_t a)
{
- return s + ALPHA_BLEND(d, a);
+ return s + MULTIPLY(d, a);
}
-static inline uint32_t _opMaskSubtract(TVG_UNUSED uint32_t s, uint32_t d, uint8_t a)
+static inline uint8_t _opMaskSubtract(uint8_t s, uint8_t d, TVG_UNUSED uint8_t a)
{
- return ALPHA_BLEND(d, a);
+ return MULTIPLY(s, 255 - d);
}
-static inline uint32_t _opMaskDifference(uint32_t s, uint32_t d, uint8_t a)
+static inline uint8_t _opMaskIntersect(uint8_t s, uint8_t d, TVG_UNUSED uint8_t a)
{
- return ALPHA_BLEND(s, IA(d)) + ALPHA_BLEND(d, a);
+ return MULTIPLY(s, d);
}
-static inline uint32_t _opAMaskAdd(uint32_t s, uint32_t d, uint8_t a)
+static inline uint8_t _opMaskDifference(uint8_t s, uint8_t d, uint8_t a)
{
- return INTERPOLATE(s, d, a);
+ return MULTIPLY(s, 255 - d) + MULTIPLY(d, a);
}
-static inline uint32_t _opAMaskSubtract(TVG_UNUSED uint32_t s, uint32_t d, uint8_t a)
+static inline bool _direct(CompositeMethod method)
{
- return ALPHA_BLEND(d, IA(ALPHA_BLEND(s, a)));
-}
-
-
-static inline uint32_t _opAMaskDifference(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));
+ //subtract & Intersect allows the direct composition
+ if (method == CompositeMethod::SubtractMask || method == CompositeMethod::IntersectMask) return true;
+ return false;
}
-static inline SwBlender _getMaskOp(CompositeMethod method)
+static inline SwMask _getMaskOp(CompositeMethod method)
{
switch (method) {
case CompositeMethod::AddMask: return _opMaskAdd;
case CompositeMethod::SubtractMask: return _opMaskSubtract;
case CompositeMethod::DifferenceMask: return _opMaskDifference;
+ case CompositeMethod::IntersectMask: return _opMaskIntersect;
default: return nullptr;
}
}
-static inline SwBlender _getAMaskOp(CompositeMethod method)
+static bool _compositeMaskImage(SwSurface* surface, const SwImage* image, const SwBBox& region)
{
- switch (method) {
- case CompositeMethod::AddMask: return _opAMaskAdd;
- case CompositeMethod::SubtractMask: return _opAMaskSubtract;
- case CompositeMethod::DifferenceMask: return _opAMaskDifference;
- default: return nullptr;
+ auto dbuffer = &surface->buf8[region.min.y * surface->stride + region.min.x];
+ auto sbuffer = image->buf8 + (region.min.y + image->oy) * image->stride + (region.min.x + image->ox);
+
+ for (auto y = region.min.y; y < region.max.y; ++y) {
+ auto dst = dbuffer;
+ auto src = sbuffer;
+ for (auto x = region.min.x; x < region.max.x; x++, dst++, src++) {
+ *dst = *src + MULTIPLY(*dst, ~*src);
+ }
+ dbuffer += surface->stride;
+ sbuffer += image->stride;
}
+ return true;
}
@@ -284,78 +301,57 @@ static uint32_t _interpDownScaler(const uint32_t *img, uint32_t stride, uint32_t
/* Rect */
/************************************************************************/
-static void _rasterMaskedRectDup(SwSurface* surface, const SwBBox& region, SwBlender opMask, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
+static bool _rasterCompositeMaskedRect(SwSurface* surface, const SwBBox& region, SwMask maskOp, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
{
auto w = static_cast<uint32_t>(region.max.x - region.min.x);
auto h = static_cast<uint32_t>(region.max.y - region.min.y);
- auto cbuffer = surface->compositor->image.buf32 + (region.min.y * surface->compositor->image.stride + region.min.x); //compositor buffer
auto cstride = surface->compositor->image.stride;
- auto color = surface->join(r, g, b, a);
+ auto cbuffer = surface->compositor->image.buf8 + (region.min.y * cstride + region.min.x); //compositor buffer
auto ialpha = 255 - a;
for (uint32_t y = 0; y < h; ++y) {
auto cmp = cbuffer;
for (uint32_t x = 0; x < w; ++x, ++cmp) {
- *cmp = opMask(color, *cmp, ialpha);
+ *cmp = maskOp(a, *cmp, ialpha);
}
cbuffer += cstride;
}
+ return _compositeMaskImage(surface, &surface->compositor->image, surface->compositor->bbox);
}
-static void _rasterMaskedRectInt(SwSurface* surface, const SwBBox& region, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
+static bool _rasterDirectMaskedRect(SwSurface* surface, const SwBBox& region, SwMask maskOp, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
{
auto w = static_cast<uint32_t>(region.max.x - region.min.x);
auto h = static_cast<uint32_t>(region.max.y - region.min.y);
- auto cstride = surface->compositor->image.stride;
+ auto cbuffer = surface->compositor->image.buf8 + (region.min.y * surface->compositor->image.stride + region.min.x); //compositor buffer
+ auto dbuffer = surface->buf8 + (region.min.y * surface->stride + region.min.x); //destination buffer
- for (auto y = surface->compositor->bbox.min.y; y < surface->compositor->bbox.max.y; ++y) {
- auto cmp = surface->compositor->image.buf32 + (y * cstride + surface->compositor->bbox.min.x);
- if (y == region.min.y) {
- for (auto y2 = y; y2 < region.max.y; ++y2) {
- auto tmp = cmp;
- auto x = surface->compositor->bbox.min.x;
- while (x < surface->compositor->bbox.max.x) {
- if (x == region.min.x) {
- for (uint32_t i = 0; i < w; ++i, ++tmp) {
- *tmp = ALPHA_BLEND(*tmp, a);
- }
- x += w;
- } else {
- *tmp = 0;
- ++tmp;
- ++x;
- }
- }
- cmp += cstride;
- }
- y += (h - 1);
- } else {
- rasterPixel32(cmp, 0x00000000, 0, w);
- cmp += cstride;
+ for (uint32_t y = 0; y < h; ++y) {
+ auto cmp = cbuffer;
+ auto dst = dbuffer;
+ for (uint32_t x = 0; x < w; ++x, ++cmp, ++dst) {
+ auto tmp = maskOp(a, *cmp, 0); //not use alpha.
+ *dst = tmp + MULTIPLY(*dst, ~tmp);
}
+ cbuffer += surface->compositor->image.stride;
+ dbuffer += surface->stride;
}
+ return true;
}
static bool _rasterMaskedRect(SwSurface* surface, const SwBBox& region, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
{
- //32bit channels composition
- if (surface->channelSize != sizeof(uint32_t)) return false;
-
- TVGLOG("SW_ENGINE", "Masked(%d) Rect [Region: %lu %lu %lu %lu]", (int)surface->compositor->method, region.min.x, region.min.y, region.max.x - region.max.y, region.min.y);
+ //8bit masking channels composition
+ if (surface->channelSize != sizeof(uint8_t)) return false;
- if (surface->compositor->method == CompositeMethod::IntersectMask) {
- _rasterMaskedRectInt(surface, region, r, g, b, a);
- } else if (auto opMask = _getMaskOp(surface->compositor->method)) {
- //Other Masking operations: Add, Subtract, Difference ...
- _rasterMaskedRectDup(surface, region, opMask, r, g, b, a);
- } else {
- return false;
- }
+ TVGLOG("SW_ENGINE", "Masked(%d) Rect [Region: %lu %lu %lu %lu]", (int)surface->compositor->method, region.min.x, region.min.y, region.max.x - region.min.x, region.max.y - region.min.y);
- //Masking Composition
- return _rasterDirectImage(surface, &surface->compositor->image, surface->compositor->bbox);
+ auto maskOp = _getMaskOp(surface->compositor->method);
+ if (_direct(surface->compositor->method)) return _rasterDirectMaskedRect(surface, region, maskOp, r, g, b, a);
+ else return _rasterCompositeMaskedRect(surface, region, maskOp, r, g, b, a);
+ return false;
}
@@ -444,7 +440,7 @@ static bool _rasterSolidRect(SwSurface* surface, const SwBBox& region, uint8_t r
//8bits grayscale
if (surface->channelSize == sizeof(uint8_t)) {
for (uint32_t y = 0; y < h; ++y) {
- rasterGrayscale8(surface->buf8, 255, region.min.y * surface->stride + region.min.x, w);
+ rasterGrayscale8(surface->buf8, 255, (y + region.min.y) * surface->stride + region.min.x, w);
}
return true;
}
@@ -471,53 +467,44 @@ static bool _rasterRect(SwSurface* surface, const SwBBox& region, uint8_t r, uin
/* Rle */
/************************************************************************/
-static void _rasterMaskedRleDup(SwSurface* surface, SwRleData* rle, SwBlender maskOp, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
+static bool _rasterCompositeMaskedRle(SwSurface* surface, SwRleData* rle, SwMask maskOp, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
{
auto span = rle->spans;
- auto cbuffer = surface->compositor->image.buf32;
+ auto cbuffer = surface->compositor->image.buf8;
auto cstride = surface->compositor->image.stride;
- auto color = surface->join(r, g, b, a);
- uint32_t src;
+ uint8_t src;
for (uint32_t i = 0; i < rle->size; ++i, ++span) {
auto cmp = &cbuffer[span->y * cstride + span->x];
- if (span->coverage == 255) src = color;
- else src = ALPHA_BLEND(color, span->coverage);
- auto ialpha = IA(src);
+ if (span->coverage == 255) src = a;
+ else src = MULTIPLY(a, span->coverage);
+ auto ialpha = 255 - src;
for (auto x = 0; x < span->len; ++x, ++cmp) {
*cmp = maskOp(src, *cmp, ialpha);
}
}
+ return _compositeMaskImage(surface, &surface->compositor->image, surface->compositor->bbox);
}
-static void _rasterMaskedRleInt(SwSurface* surface, SwRleData* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
+static bool _rasterDirectMaskedRle(SwSurface* surface, SwRleData* rle, SwMask maskOp, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
{
auto span = rle->spans;
- auto cbuffer = surface->compositor->image.buf32;
+ auto cbuffer = surface->compositor->image.buf8;
auto cstride = surface->compositor->image.stride;
- auto color = surface->join(r, g, b, a);
- uint32_t src;
-
- for (auto y = surface->compositor->bbox.min.y; y < surface->compositor->bbox.max.y; ++y) {
- auto cmp = &cbuffer[y * cstride];
- auto x = surface->compositor->bbox.min.x;
- while (x < surface->compositor->bbox.max.x) {
- if (y == span->y && x == span->x && x + span->len <= surface->compositor->bbox.max.x) {
- if (span->coverage == 255) src = color;
- else src = ALPHA_BLEND(color, span->coverage);
- auto alpha = A(src);
- for (uint32_t i = 0; i < span->len; ++i) {
- cmp[x + i] = ALPHA_BLEND(cmp[x + i], alpha);
- }
- x += span->len;
- ++span;
- } else {
- cmp[x] = 0;
- ++x;
- }
+ uint8_t src;
+
+ for (uint32_t i = 0; i < rle->size; ++i, ++span) {
+ auto cmp = &cbuffer[span->y * cstride + span->x];
+ auto dst = &surface->buf8[span->y * surface->stride + span->x];
+ if (span->coverage == 255) src = a;
+ else src = MULTIPLY(a, span->coverage);
+ for (auto x = 0; x < span->len; ++x, ++cmp, ++dst) {
+ auto tmp = maskOp(src, *cmp, 0); //not use alpha
+ *dst = tmp + MULTIPLY(*dst, ~tmp);
}
}
+ return true;
}
@@ -525,20 +512,13 @@ static bool _rasterMaskedRle(SwSurface* surface, SwRleData* rle, uint8_t r, uint
{
TVGLOG("SW_ENGINE", "Masked(%d) Rle", (int)surface->compositor->method);
- //32bit channels composition
- if (surface->channelSize != sizeof(uint32_t)) return false;
-
- if (surface->compositor->method == CompositeMethod::IntersectMask) {
- _rasterMaskedRleInt(surface, rle, r, g, b, a);
- } else if (auto opMask = _getMaskOp(surface->compositor->method)) {
- //Other Masking operations: Add, Subtract, Difference ...
- _rasterMaskedRleDup(surface, rle, opMask, r, g, b, a);
- } else {
- return false;
- }
+ //8bit masking channels composition
+ if (surface->channelSize != sizeof(uint8_t)) return false;
- //Masking Composition
- return _rasterDirectImage(surface, &surface->compositor->image, surface->compositor->bbox);
+ auto maskOp = _getMaskOp(surface->compositor->method);
+ if (_direct(surface->compositor->method)) return _rasterDirectMaskedRle(surface, rle, maskOp, r, g, b, a);
+ else return _rasterCompositeMaskedRle(surface, rle, maskOp, r, g, b, a);
+ return false;
}
@@ -644,7 +624,15 @@ static bool _rasterSolidRle(SwSurface* surface, const SwRleData* rle, uint8_t r,
//8bit grayscale
} else if (surface->channelSize == sizeof(uint8_t)) {
for (uint32_t i = 0; i < rle->size; ++i, ++span) {
- rasterGrayscale8(surface->buf8, span->coverage, span->y * surface->stride + span->x, span->len);
+ if (span->coverage == 255) {
+ rasterGrayscale8(surface->buf8, span->coverage, span->y * surface->stride + span->x, span->len);
+ } else {
+ auto dst = &surface->buf8[span->y * surface->stride + span->x];
+ auto ialpha = 255 - span->coverage;
+ for (uint32_t x = 0; x < span->len; ++x, ++dst) {
+ *dst = span->coverage + MULTIPLY(*dst, ialpha);
+ }
+ }
}
}
return true;
@@ -669,28 +657,11 @@ static bool _rasterRle(SwSurface* surface, SwRleData* rle, uint8_t r, uint8_t g,
/************************************************************************/
-/* RLE Transformed Image */
-/************************************************************************/
-
-static bool _transformedRleImage(SwSurface* surface, const SwImage* image, const Matrix* transform, uint8_t opacity)
-{
- auto ret = _rasterTexmapPolygon(surface, image, transform, nullptr, opacity);
-
- //Masking Composition
- if (_compositing(surface) && _masking(surface)) {
- return _rasterDirectImage(surface, &surface->compositor->image, surface->compositor->bbox);
- }
-
- return ret;
-
-}
-
-
-/************************************************************************/
/* RLE Scaled Image */
/************************************************************************/
-static void _rasterScaledMaskedRleImageDup(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, SwBlender maskOp, SwBlender amaskOp, uint8_t opacity)
+#if 0 //Enable it when GRAYSCALE image is supported
+static bool _rasterCompositeScaledMaskedRleImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, SwMask maskOp, uint8_t opacity)
{
auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler;
auto sampleSize = _sampleSize(image->scale);
@@ -700,82 +671,77 @@ static void _rasterScaledMaskedRleImageDup(SwSurface* surface, const SwImage* im
for (uint32_t i = 0; i < image->rle->size; ++i, ++span) {
auto sy = span->y * itransform->e22 + itransform->e23;
if ((uint32_t)sy >= image->h) continue;
- auto cmp = &surface->compositor->image.buf32[span->y * surface->compositor->image.stride + span->x];
+ auto cmp = &surface->compositor->image.buf8[span->y * surface->compositor->image.stride + span->x];
auto a = MULTIPLY(span->coverage, opacity);
if (a == 255) {
for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++cmp) {
auto sx = x * itransform->e11 + itransform->e13;
if ((uint32_t)sx >= image->w) continue;
- auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2);
- *cmp = maskOp(src, *cmp, 255);
+ auto src = scaleMethod(image->buf8, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2);
+ *cmp = maskOp(src, *cmp, ~src);
}
} else {
for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++cmp) {
auto sx = x * itransform->e11 + itransform->e13;
if ((uint32_t)sx >= image->w) continue;
- auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2);
- *cmp = amaskOp(src, *cmp, a);
+ auto src = scaleMethod(image->buf8, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2);
+ auto tmp = MULTIPLY(src, a);
+ *cmp = maskOp(tmp, *cmp, ~tmp);
}
}
}
+ return true;
}
-static void _rasterScaledMaskedRleImageInt(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint8_t opacity)
+static bool _rasterDirectScaledMaskedRleImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, SwMask maskOp, uint8_t opacity)
{
auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler;
auto sampleSize = _sampleSize(image->scale);
auto sampleSize2 = sampleSize * sampleSize;
auto span = image->rle->spans;
- auto cbuffer = surface->compositor->image.buf32;
- auto cstride = surface->compositor->image.stride;
- for (auto y = surface->compositor->bbox.min.y; y < surface->compositor->bbox.max.y; ++y) {
- auto cmp = &cbuffer[y * cstride];
- for (auto x = surface->compositor->bbox.min.x; x < surface->compositor->bbox.max.x; ++x) {
- if (y == span->y && x == span->x && x + span->len <= surface->compositor->bbox.max.x) {
- auto sy = span->y * itransform->e22 + itransform->e23;
- if ((uint32_t)sy >= image->h) continue;
- auto alpha = MULTIPLY(span->coverage, opacity);
- if (alpha == 255) {
- for (uint32_t i = 0; i < span->len; ++i) {
- auto sx = (x + i) * itransform->e11 + itransform->e13;
- if ((uint32_t)sx >= image->w) continue;
- auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2);
- cmp[x + i] = ALPHA_BLEND(cmp[x + i], A(src));
- }
- } else {
- for (uint32_t i = 0; i < span->len; ++i) {
- auto sx = (x + i) * itransform->e11 + itransform->e13;
- if ((uint32_t)sx >= image->w) continue;
- auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2);
- cmp[x + i] = ALPHA_BLEND(cmp[x + i], A(ALPHA_BLEND(src, alpha)));
- }
- }
- x += span->len - 1;
- ++span;
- } else {
- cmp[x] = 0;
+ for (uint32_t i = 0; i < image->rle->size; ++i, ++span) {
+ auto sy = span->y * itransform->e22 + itransform->e23;
+ if ((uint32_t)sy >= image->h) continue;
+ auto cmp = &surface->compositor->image.buf8[span->y * surface->compositor->image.stride + span->x];
+ auto dst = &surface->buf8[span->y * surface->stride + span->x];
+ auto a = MULTIPLY(span->coverage, opacity);
+ if (a == 255) {
+ for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++cmp, ++dst) {
+ auto sx = x * itransform->e11 + itransform->e13;
+ if ((uint32_t)sx >= image->w) continue;
+ auto src = scaleMethod(image->buf8, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2);
+ auto tmp = maskOp(src, *cmp, 0); //not use alpha
+ *dst = tmp + MULTIPLY(*dst, ~tmp);
+ }
+ } else {
+ for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++cmp, ++dst) {
+ auto sx = x * itransform->e11 + itransform->e13;
+ if ((uint32_t)sx >= image->w) continue;
+ auto src = scaleMethod(image->buf8, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2);
+ auto tmp = maskOp(MULTIPLY(src, a), *cmp, 0); //not use alpha
+ *dst = tmp + MULTIPLY(*dst, ~tmp);
}
}
}
+ return _compositeMaskImage(surface, &surface->compositor->image, surface->compositor->bbox);
}
-
+#endif
static bool _rasterScaledMaskedRleImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint8_t opacity)
{
+#if 0 //Enable it when GRAYSCALE image is supported
TVGLOG("SW_ENGINE", "Scaled Masked(%d) Rle Image", (int)surface->compositor->method);
- if (surface->compositor->method == CompositeMethod::IntersectMask) {
- _rasterScaledMaskedRleImageInt(surface, image, itransform, region, opacity);
- } else if (auto opMask = _getMaskOp(surface->compositor->method)) {
- //Other Masking operations: Add, Subtract, Difference ...
- _rasterScaledMaskedRleImageDup(surface, image, itransform, region, opMask, _getAMaskOp(surface->compositor->method), opacity);
- } else {
- return false;
- }
- //Masking Composition
- return _rasterDirectImage(surface, &surface->compositor->image, surface->compositor->bbox);
+ //8bit masking channels composition
+ if (surface->channelSize != sizeof(uint8_t)) return false;
+
+ auto maskOp = _getMaskOp(surface->compositor->method);
+ if (_direct(surface->compositor->method)) return _rasterDirectScaledMaskedRleImage(surface, image, itransform, region, maskOp, opacity);
+ else return _rasterCompositeScaledMaskedRleImage(surface, image, itransform, region, maskOp, opacity);
+#endif
+ return false;
}
@@ -895,6 +861,11 @@ static bool _rasterScaledRleImage(SwSurface* surface, const SwImage* image, cons
static bool _scaledRleImage(SwSurface* surface, const SwImage* image, const Matrix* transform, const SwBBox& region, uint8_t opacity)
{
+ if (surface->channelSize == sizeof(uint8_t)) {
+ TVGERR("SW_ENGINE", "Not supported scaled rle image!");
+ return false;
+ }
+
Matrix itransform;
if (transform) {
@@ -917,78 +888,72 @@ static bool _scaledRleImage(SwSurface* surface, const SwImage* image, const Matr
/* RLE Direct Image */
/************************************************************************/
-static void _rasterDirectMaskedRleImageDup(SwSurface* surface, const SwImage* image, SwBlender maskOp, SwBlender amaskOp, uint8_t opacity)
+#if 0 //Enable it when GRAYSCALE image is supported
+static bool _rasterCompositeDirectMaskedRleImage(SwSurface* surface, const SwImage* image, SwMask maskOp, uint8_t opacity)
{
auto span = image->rle->spans;
- auto cbuffer = surface->compositor->image.buf32;
+ auto cbuffer = surface->compositor->image.buf8;
auto ctride = surface->compositor->image.stride;
for (uint32_t i = 0; i < image->rle->size; ++i, ++span) {
- auto src = image->buf32 + (span->y + image->oy) * image->stride + (span->x + image->ox);
+ auto src = image->buf8 + (span->y + image->oy) * image->stride + (span->x + image->ox);
auto cmp = &cbuffer[span->y * ctride + span->x];
auto alpha = MULTIPLY(span->coverage, opacity);
if (alpha == 255) {
for (uint32_t x = 0; x < span->len; ++x, ++src, ++cmp) {
- *cmp = maskOp(*src, *cmp, IA(*src));
+ *cmp = maskOp(*src, *cmp, ~*src);
}
} else {
for (uint32_t x = 0; x < span->len; ++x, ++src, ++cmp) {
- *cmp = amaskOp(*src, *cmp, alpha);
+ auto tmp = MULTIPLY(*src, alpha);
+ *cmp = maskOp(*src, *cmp, ~tmp);
}
}
}
+ return _compositeMaskImage(surface, &surface->compositor->image, surface->compositor->bbox);
}
-static void _rasterDirectMaskedRleImageInt(SwSurface* surface, const SwImage* image, uint8_t opacity)
+static bool _rasterDirectDirectMaskedRleImage(SwSurface* surface, const SwImage* image, SwMask maskOp, uint8_t opacity)
{
auto span = image->rle->spans;
- auto cbuffer = surface->compositor->image.buf32;
+ auto cbuffer = surface->compositor->image.buf8;
auto ctride = surface->compositor->image.stride;
- for (auto y = surface->compositor->bbox.min.y; y < surface->compositor->bbox.max.y; ++y) {
- auto cmp = &cbuffer[y * ctride];
- auto x = surface->compositor->bbox.min.x;
- while (x < surface->compositor->bbox.max.x) {
- if (y == span->y && x == span->x && x + span->len <= surface->compositor->bbox.max.x) {
- auto alpha = MULTIPLY(span->coverage, opacity);
- auto src = image->buf32 + (span->y + image->oy) * image->stride + (span->x + image->ox);
- if (alpha == 255) {
- for (uint32_t i = 0; i < span->len; ++i, ++src) {
- cmp[x + i] = ALPHA_BLEND(cmp[x + i], A(*src));
- }
- } else {
- for (uint32_t i = 0; i < span->len; ++i, ++src) {
- auto t = ALPHA_BLEND(*src, alpha);
- cmp[x + i] = ALPHA_BLEND(cmp[x + i], A(t));
- }
- }
- x += span->len;
- ++span;
- } else {
- cmp[x] = 0;
- ++x;
+ for (uint32_t i = 0; i < image->rle->size; ++i, ++span) {
+ auto src = image->buf8 + (span->y + image->oy) * image->stride + (span->x + image->ox);
+ auto cmp = &cbuffer[span->y * ctride + span->x];
+ auto dst = &surface->buf8[span->y * surface->stride + span->x];
+ auto alpha = MULTIPLY(span->coverage, opacity);
+ if (alpha == 255) {
+ for (uint32_t x = 0; x < span->len; ++x, ++src, ++cmp, ++dst) {
+ auto tmp = maskOp(*src, *cmp, 0); //not use alpha
+ *dst = INTERPOLATE8(tmp, *dst, (255 - tmp));
+ }
+ } else {
+ for (uint32_t x = 0; x < span->len; ++x, ++src, ++cmp, ++dst) {
+ auto tmp = maskOp(MULTIPLY(*src, alpha), *cmp, 0); //not use alpha
+ *dst = INTERPOLATE8(tmp, *dst, (255 - tmp));
}
}
}
+ return true;
}
-
+#endif
static bool _rasterDirectMaskedRleImage(SwSurface* surface, const SwImage* image, uint8_t opacity)
{
+#if 0 //Enable it when GRAYSCALE image is supported
TVGLOG("SW_ENGINE", "Direct Masked(%d) Rle Image", (int)surface->compositor->method);
- if (surface->compositor->method == CompositeMethod::IntersectMask) {
- _rasterDirectMaskedRleImageInt(surface, image, opacity);
- } else if (auto opMask = _getMaskOp(surface->compositor->method)) {
- //Other Masking operations: Add, Subtract, Difference ...
- _rasterDirectMaskedRleImageDup(surface, image, opMask, _getAMaskOp(surface->compositor->method), opacity);
- } else {
- return false;
- }
+ //8bit masking channels composition
+ if (surface->channelSize != sizeof(uint8_t)) return false;
- //Masking Composition
- return _rasterDirectImage(surface, &surface->compositor->image, surface->compositor->bbox);
+ auto maskOp = _getMaskOp(surface->compositor->method);
+ if (_direct(surface->compositor->method)) _rasterDirectDirectMaskedRleImage(surface, image, maskOp, opacity);
+ else return _rasterCompositeDirectMaskedRleImage(surface, image, maskOp, opacity);
+#endif
+ return false;
}
@@ -1076,6 +1041,11 @@ static bool _rasterDirectRleImage(SwSurface* surface, const SwImage* image, uint
static bool _directRleImage(SwSurface* surface, const SwImage* image, uint8_t opacity)
{
+ if (surface->channelSize == sizeof(uint8_t)) {
+ TVGERR("SW_ENGINE", "Not supported grayscale rle image!");
+ return false;
+ }
+
if (_compositing(surface)) {
if (_matting(surface)) return _rasterDirectMattedRleImage(surface, image, opacity);
else return _rasterDirectMaskedRleImage(surface, image, opacity);
@@ -1089,40 +1059,17 @@ static bool _directRleImage(SwSurface* surface, const SwImage* image, uint8_t op
/************************************************************************/
-/* Transformed Image */
-/************************************************************************/
-
-static bool _transformedImage(SwSurface* surface, const SwImage* image, const Matrix* transform, const SwBBox& region, uint8_t opacity)
-{
- auto ret = _rasterTexmapPolygon(surface, image, transform, &region, opacity);
-
- //Masking Composition
- if (_compositing(surface) && _masking(surface)) {
- return _rasterDirectImage(surface, &surface->compositor->image, surface->compositor->bbox);
- }
-
- return ret;
-}
-
-
-static bool _transformedImageMesh(SwSurface* surface, const SwImage* image, const RenderMesh* mesh, const Matrix* transform, const SwBBox* region, uint8_t opacity)
-{
- //TODO: Not completed for all cases.
- return _rasterTexmapPolygonMesh(surface, image, mesh, transform, region, opacity);
-}
-
-
-/************************************************************************/
/*Scaled Image */
/************************************************************************/
-static void _rasterScaledMaskedImageDup(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, SwBlender maskOp, SwBlender amaskOp, uint8_t opacity)
+#if 0 //Enable it when GRAYSCALE image is supported
+static bool _rasterCompositeScaledMaskedImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, SwMask maskOp, uint8_t opacity)
{
auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler;
auto sampleSize = _sampleSize(image->scale);
auto sampleSize2 = sampleSize * sampleSize;
auto cstride = surface->compositor->image.stride;
- auto cbuffer = surface->compositor->image.buf32 + (region.min.y * cstride + region.min.x);
+ auto cbuffer = surface->compositor->image.buf8 + (region.min.y * cstride + region.min.x);
for (auto y = region.min.y; y < region.max.y; ++y) {
auto sy = y * itransform->e22 + itransform->e23;
@@ -1132,92 +1079,73 @@ static void _rasterScaledMaskedImageDup(SwSurface* surface, const SwImage* image
for (auto x = region.min.x; x < region.max.x; ++x, ++cmp) {
auto sx = x * itransform->e11 + itransform->e13;
if ((uint32_t)sx >= image->w) continue;
- auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2);
- *cmp = maskOp(src, *cmp, IA(src));
+ auto src = scaleMethod(image->buf8, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2);
+ *cmp = maskOp(src, *cmp, ~src);
}
} else {
for (auto x = region.min.x; x < region.max.x; ++x, ++cmp) {
auto sx = x * itransform->e11 + itransform->e13;
if ((uint32_t)sx >= image->w) continue;
- auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2);
- *cmp = amaskOp(src, *cmp, opacity);
+ auto src = scaleMethod(image->buf8, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2);
+ auto tmp = MULTIPLY(src, opacity);
+ *cmp = maskOp(tmp, *cmp, ~tmp);
}
}
cbuffer += cstride;
}
+ return _compositeMaskImage(surface, &surface->compositor->image, surface->compositor->bbox);
}
-static void _rasterScaledMaskedImageInt(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint8_t opacity)
+
+static bool _rasterDirectScaledMaskedImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, SwMask maskOp, uint8_t opacity)
{
auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler;
auto sampleSize = _sampleSize(image->scale);
auto sampleSize2 = sampleSize * sampleSize;
- 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 cstride = surface->compositor->image.stride;
- auto cbuffer = surface->compositor->image.buf32 + (surface->compositor->bbox.min.y * cstride + surface->compositor->bbox.min.x);
-
- for (auto y = surface->compositor->bbox.min.y; y < surface->compositor->bbox.max.y; ++y) {
- if (y == region.min.y) {
- auto cbuffer2 = cbuffer;
- for (auto y2 = y; y2 < region.max.y; ++y2) {
- auto sy = y2 * itransform->e22 + itransform->e23;
- if ((uint32_t)sy >= image->h) continue;
- auto tmp = cbuffer2;
- auto x = surface->compositor->bbox.min.x;
- while (x < surface->compositor->bbox.max.x) {
- if (x == region.min.x) {
- if (opacity == 255) {
- for (uint32_t i = 0; i < w; ++i, ++tmp) {
- auto sx = (x + i) * itransform->e11 + itransform->e13;
- if ((uint32_t)sx >= image->w) continue;
- auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2);
- *tmp = ALPHA_BLEND(*tmp, A(src));
- }
- } else {
- for (uint32_t i = 0; i < w; ++i, ++tmp) {
- auto sx = (x + i) * itransform->e11 + itransform->e13;
- if ((uint32_t)sx >= image->w) continue;
- auto src = ALPHA_BLEND(scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2), opacity);
- *tmp = ALPHA_BLEND(*tmp, A(src));
- }
- }
- x += w;
- } else {
- *tmp = 0;
- ++tmp;
- ++x;
- }
- }
- cbuffer2 += cstride;
+ auto cbuffer = surface->compositor->image.buf8 + (region.min.y * cstride + region.min.x);
+ auto dbuffer = surface->buf8 + (region.min.y * surface->stride + region.min.x);
+
+ for (auto y = region.min.y; y < region.max.y; ++y) {
+ auto sy = y * itransform->e22 + itransform->e23;
+ if ((uint32_t)sy >= image->h) continue;
+ auto cmp = cbuffer;
+ auto dst = dbuffer;
+ if (opacity == 255) {
+ for (auto x = region.min.x; x < region.max.x; ++x, ++cmp, ++dst) {
+ auto sx = x * itransform->e11 + itransform->e13;
+ if ((uint32_t)sx >= image->w) continue;
+ auto src = scaleMethod(image->buf8, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2);
+ auto tmp = maskOp(src, *cmp, 0); //not use alpha
+ *dst = tmp + MULTIPLY(*dst, ~tmp);
}
- y += (h - 1);
} else {
- auto tmp = cbuffer;
- for (auto x = surface->compositor->bbox.min.x; x < surface->compositor->bbox.max.x; ++x, ++tmp) {
- *tmp = 0;
+ for (auto x = region.min.x; x < region.max.x; ++x, ++cmp, ++dst) {
+ auto sx = x * itransform->e11 + itransform->e13;
+ if ((uint32_t)sx >= image->w) continue;
+ auto src = scaleMethod(image->buf8, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2);
+ auto tmp = MULTIPLY(src, opacity);
+ auto tmp2 = maskOp(tmp, *cmp, 0); //not use alpha
+ *dst = tmp2 + MULTIPLY(*dst, ~tmp2);
}
}
cbuffer += cstride;
+ dbuffer += surface->stride;
}
+ return true;
}
-
+#endif
static bool _rasterScaledMaskedImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint8_t opacity)
{
+#if 0 //Enable it when GRAYSCALE image is supported
TVGLOG("SW_ENGINE", "Scaled Masked(%d) Image [Region: %lu %lu %lu %lu]", (int)surface->compositor->method, region.min.x, region.min.y, region.max.x - region.min.x, region.max.y - region.min.y);
- if (surface->compositor->method == CompositeMethod::IntersectMask) {
- _rasterScaledMaskedImageInt(surface, image, itransform, region, opacity);
- } else if (auto opMask = _getMaskOp(surface->compositor->method)) {
- //Other Masking operations: Add, Subtract, Difference ...
- _rasterScaledMaskedImageDup(surface, image, itransform, region, opMask, _getAMaskOp(surface->compositor->method), opacity);
- } else {
- return false;
- }
-
- //Masking Composition
- return _rasterDirectImage(surface, &surface->compositor->image, surface->compositor->bbox);
+ auto maskOp = _getMaskOp(surface->compositor->method);
+ if (_direct(surface->compositor->method)) return _rasterDirectScaledMaskedImage(surface, image, itransform, region, maskOp, opacity);
+ else return _rasterCompositeScaledMaskedImage(surface, image, itransform, region, maskOp, opacity);
+#endif
+ return false;
}
@@ -1329,6 +1257,11 @@ static bool _rasterScaledImage(SwSurface* surface, const SwImage* image, const M
static bool _scaledImage(SwSurface* surface, const SwImage* image, const Matrix* transform, const SwBBox& region, uint8_t opacity)
{
+ if (surface->channelSize == sizeof(uint8_t)) {
+ TVGERR("SW_ENGINE", "Not supported grayscale Textmap polygon mesh!");
+ return false;
+ }
+
Matrix itransform;
if (transform) {
@@ -1351,125 +1284,135 @@ static bool _scaledImage(SwSurface* surface, const SwImage* image, const Matrix*
/* Direct Image */
/************************************************************************/
-static void _rasterDirectMaskedImageDup(SwSurface* surface, const SwImage* image, const SwBBox& region, SwBlender maskOp, SwBlender amaskOp, uint8_t opacity)
+#if 0 //Enable it when GRAYSCALE image is supported
+static bool _rasterCompositeDirectMaskedImage(SwSurface* surface, const SwImage* image, const SwBBox& region, SwMask maskOp, uint8_t opacity)
{
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 cstride = surface->compositor->image.stride;
- auto cbuffer = surface->compositor->image.buf32 + (region.min.y * cstride + region.min.x); //compositor buffer
- auto sbuffer = image->buf32 + (region.min.y + image->oy) * image->stride + (region.min.x + image->ox);
+ auto cbuffer = surface->compositor->image.buf8 + (region.min.y * cstride + region.min.x); //compositor buffer
+ auto sbuffer = image->buf8 + (region.min.y + image->oy) * image->stride + (region.min.x + image->ox);
for (uint32_t y = 0; y < h; ++y) {
auto cmp = cbuffer;
auto src = sbuffer;
if (opacity == 255) {
for (uint32_t x = 0; x < w; ++x, ++src, ++cmp) {
- *cmp = maskOp(*src, *cmp, IA(*src));
+ *cmp = maskOp(*src, *cmp, ~*src);
}
} else {
for (uint32_t x = 0; x < w; ++x, ++src, ++cmp) {
- *cmp = amaskOp(*src, *cmp, opacity);
+ auto tmp = MULTIPLY(*src, opacity);
+ *cmp = maskOp(tmp, *cmp, ~tmp);
}
}
cbuffer += cstride;
sbuffer += image->stride;
}
+ return _compositeMaskImage(surface, &surface->compositor->image, surface->compositor->bbox);
}
-static void _rasterDirectMaskedImageInt(SwSurface* surface, const SwImage* image, const SwBBox& region, uint8_t opacity)
+static bool _rasterDirectDirectMaskedImage(SwSurface* surface, const SwImage* image, const SwBBox& region, SwMask maskOp, uint8_t opacity)
{
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 cstride = surface->compositor->image.stride;
- auto cbuffer = surface->compositor->image.buf32 + (surface->compositor->bbox.min.y * cstride + surface->compositor->bbox.min.x);
-
- for (auto y = surface->compositor->bbox.min.y; y < surface->compositor->bbox.max.y; ++y) {
- if (y == region.min.y) {
- auto cbuffer2 = cbuffer;
- for (auto y2 = y; y2 < region.max.y; ++y2) {
- auto tmp = cbuffer2;
- auto x = surface->compositor->bbox.min.x;
- while (x < surface->compositor->bbox.max.x) {
- if (x == region.min.x) {
- auto src = &image->buf32[(y2 + image->oy) * image->stride + (x + image->ox)];
- if (opacity == 255) {
- for (uint32_t i = 0; i < w; ++i, ++tmp, ++src) {
- *tmp = ALPHA_BLEND(*tmp, A(*src));
- }
- } else {
- for (uint32_t i = 0; i < w; ++i, ++tmp, ++src) {
- auto t = ALPHA_BLEND(*src, opacity);
- *tmp = ALPHA_BLEND(*tmp, A(t));
- }
- }
- x += w;
- } else {
- *tmp = 0;
- ++tmp;
- ++x;
- }
- }
- cbuffer2 += cstride;
+
+ auto cbuffer = surface->compositor->image.buf32 + (region.min.y * cstride + region.min.x); //compositor buffer
+ auto dbuffer = surface->buf8 + (region.min.y * surface->stride + region.min.x); //destination buffer
+ auto sbuffer = image->buf8 + (region.min.y + image->oy) * image->stride + (region.min.x + image->ox);
+
+ for (uint32_t y = 0; y < h; ++y) {
+ auto cmp = cbuffer;
+ auto dst = dbuffer;
+ auto src = sbuffer;
+ if (opacity == 255) {
+ for (uint32_t x = 0; x < w; ++x, ++src, ++cmp, ++dst) {
+ auto tmp = maskOp(*src, *cmp, 0); //not use alpha
+ *dst = tmp + MULTIPLY(*dst, ~tmp);
}
- y += (h - 1);
} else {
- rasterPixel32(cbuffer, 0x00000000, 0, surface->compositor->bbox.max.x - surface->compositor->bbox.min.x);
+ for (uint32_t x = 0; x < w; ++x, ++src, ++cmp, ++dst) {
+ auto tmp = maskOp(MULTIPLY(*src, opacity), *cmp, 0); //not use alpha
+ *dst = tmp + MULTIPLY(*dst, ~tmp);
+ }
}
cbuffer += cstride;
+ dbuffer += surface->stride;
+ sbuffer += image->stride;
}
+ return true;
}
-
+#endif
static bool _rasterDirectMaskedImage(SwSurface* surface, const SwImage* image, const SwBBox& region, uint8_t opacity)
{
- TVGLOG("SW_ENGINE", "Direct Masked(%d) Image [Region: %lu %lu %lu %lu]", (int)surface->compositor->method, region.min.x, region.min.y, region.max.x - region.min.x, region.max.y - region.min.y);
+ TVGERR("SW_ENGINE", "Not Supported: Direct Masked(%d) Image [Region: %lu %lu %lu %lu]", (int)surface->compositor->method, region.min.x, region.min.y, region.max.x - region.min.x, region.max.y - region.min.y);
- if (surface->compositor->method == CompositeMethod::IntersectMask) {
- _rasterDirectMaskedImageInt(surface, image, region, opacity);
- } else if (auto opMask = _getMaskOp(surface->compositor->method)) {
- //Other Masking operations: Add, Subtract, Difference ...
- _rasterDirectMaskedImageDup(surface, image, region, opMask, _getAMaskOp(surface->compositor->method), opacity);
- } else {
- return false;
- }
- //Masking Composition
- return _rasterDirectImage(surface, &surface->compositor->image, surface->compositor->bbox);
+#if 0 //Enable it when GRAYSCALE image is supported
+ auto maskOp = _getMaskOp(surface->compositor->method);
+ if (_direct(surface->compositor->method)) return _rasterDirectDirectMaskedImage(surface, image, region, maskOp, opacity);
+ else return _rasterCompositeDirectMaskedImage(surface, image, region, maskOp, opacity);
+#endif
+ return false;
}
static bool _rasterDirectMattedImage(SwSurface* surface, const SwImage* image, const SwBBox& region, uint8_t opacity)
{
- 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 csize = surface->compositor->image.channelSize;
auto alpha = surface->alpha(surface->compositor->method);
-
- TVGLOG("SW_ENGINE", "Direct Matted(%d) Image [Region: %lu %lu %u %u]", (int)surface->compositor->method, region.min.x, region.min.y, w, h);
-
auto sbuffer = image->buf32 + (region.min.y + image->oy) * image->stride + (region.min.x + image->ox);
auto cbuffer = surface->compositor->image.buf8 + (region.min.y * surface->compositor->image.stride + region.min.x) * csize; //compositor buffer
- for (uint32_t y = 0; y < h; ++y) {
- auto dst = buffer;
- auto cmp = cbuffer;
- auto src = sbuffer;
- if (opacity == 255) {
- for (uint32_t x = 0; x < w; ++x, ++dst, ++src, cmp += csize) {
- auto tmp = ALPHA_BLEND(*src, alpha(cmp));
- *dst = tmp + ALPHA_BLEND(*dst, IA(tmp));
+ TVGLOG("SW_ENGINE", "Direct Matted(%d) Image [Region: %lu %lu %u %u]", (int)surface->compositor->method, region.min.x, region.min.y, w, h);
+
+ //32 bits
+ if (surface->channelSize == sizeof(uint32_t)) {
+ auto buffer = surface->buf32 + (region.min.y * surface->stride) + region.min.x;
+ for (uint32_t y = 0; y < h; ++y) {
+ auto dst = buffer;
+ auto cmp = cbuffer;
+ auto src = sbuffer;
+ if (opacity == 255) {
+ for (uint32_t x = 0; x < w; ++x, ++dst, ++src, cmp += csize) {
+ auto tmp = ALPHA_BLEND(*src, alpha(cmp));
+ *dst = tmp + ALPHA_BLEND(*dst, IA(tmp));
+ }
+ } else {
+ for (uint32_t x = 0; x < w; ++x, ++dst, ++src, cmp += csize) {
+ auto tmp = ALPHA_BLEND(*src, MULTIPLY(opacity, alpha(cmp)));
+ *dst = tmp + ALPHA_BLEND(*dst, IA(tmp));
+ }
}
- } else {
- for (uint32_t x = 0; x < w; ++x, ++dst, ++src, cmp += csize) {
- auto tmp = ALPHA_BLEND(*src, MULTIPLY(opacity, alpha(cmp)));
- *dst = tmp + ALPHA_BLEND(*dst, IA(tmp));
+ buffer += surface->stride;
+ cbuffer += surface->compositor->image.stride * csize;
+ sbuffer += image->stride;
+ }
+ //8 bits
+ } else if (surface->channelSize == sizeof(uint8_t)) {
+ auto buffer = surface->buf8 + (region.min.y * surface->stride) + region.min.x;
+ for (uint32_t y = 0; y < h; ++y) {
+ auto dst = buffer;
+ auto cmp = cbuffer;
+ auto src = sbuffer;
+ if (opacity == 255) {
+ for (uint32_t x = 0; x < w; ++x, ++dst, ++src, cmp += csize) {
+ *dst = MULTIPLY(A(*src), alpha(cmp));
+ }
+ } else {
+ for (uint32_t x = 0; x < w; ++x, ++dst, ++src, cmp += csize) {
+ *dst = MULTIPLY(A(*src), MULTIPLY(opacity, alpha(cmp)));
+ }
}
+ buffer += surface->stride;
+ cbuffer += surface->compositor->image.stride * csize;
+ sbuffer += image->stride;
}
- buffer += surface->stride;
- cbuffer += surface->compositor->image.stride * csize;
- sbuffer += image->stride;
}
return true;
}
@@ -1477,6 +1420,11 @@ static bool _rasterDirectMattedImage(SwSurface* surface, const SwImage* image, c
static bool _rasterDirectBlendingImage(SwSurface* surface, const SwImage* image, const SwBBox& region, uint8_t opacity)
{
+ if (surface->channelSize == sizeof(uint8_t)) {
+ TVGERR("SW_ENGINE", "Not supported grayscale image!");
+ return false;
+ }
+
auto dbuffer = &surface->buf32[region.min.y * surface->stride + region.min.x];
auto sbuffer = image->buf32 + (region.min.y + image->oy) * image->stride + (region.min.x + image->ox);
@@ -1504,6 +1452,11 @@ static bool _rasterDirectBlendingImage(SwSurface* surface, const SwImage* image,
static bool _rasterDirectImage(SwSurface* surface, const SwImage* image, const SwBBox& region, uint8_t opacity)
{
+ if (surface->channelSize == sizeof(uint8_t)) {
+ TVGERR("SW_ENGINE", "Not supported grayscale image!");
+ return false;
+ }
+
auto dbuffer = &surface->buf32[region.min.y * surface->stride + region.min.x];
auto sbuffer = image->buf32 + (region.min.y + image->oy) * image->stride + (region.min.x + image->ox);
@@ -1549,12 +1502,12 @@ static bool _rasterImage(SwSurface* surface, SwImage* image, const Matrix* trans
if (image->rle) {
if (image->direct) return _directRleImage(surface, image, opacity);
else if (image->scaled) return _scaledRleImage(surface, image, transform, region, opacity);
- else return _transformedRleImage(surface, image, transform, opacity);
+ else return _rasterTexmapPolygon(surface, image, transform, nullptr, opacity);
//Whole Image
} else {
if (image->direct) return _directImage(surface, image, region, opacity);
else if (image->scaled) return _scaledImage(surface, image, transform, region, opacity);
- else return _transformedImage(surface, image, transform, region, opacity);
+ else return _rasterTexmapPolygon(surface, image, transform, &region, opacity);
}
}
@@ -1564,52 +1517,36 @@ static bool _rasterImage(SwSurface* surface, SwImage* image, const Matrix* trans
/************************************************************************/
template<typename fillMethod>
-static void _rasterGradientMaskedRectDup(SwSurface* surface, const SwBBox& region, const SwFill* fill, SwBlender maskOp)
+static bool _rasterCompositeGradientMaskedRect(SwSurface* surface, const SwBBox& region, const SwFill* fill, SwMask maskOp)
{
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 cstride = surface->compositor->image.stride;
- auto cbuffer = surface->compositor->image.buf32 + (region.min.y * cstride + region.min.x);
+ auto cbuffer = surface->compositor->image.buf8 + (region.min.y * cstride + region.min.x);
for (uint32_t y = 0; y < h; ++y) {
fillMethod()(fill, cbuffer, region.min.y + y, region.min.x, w, maskOp, 255);
cbuffer += surface->stride;
}
+ return _compositeMaskImage(surface, &surface->compositor->image, surface->compositor->bbox);
}
template<typename fillMethod>
-static void _rasterGradientMaskedRectInt(SwSurface* surface, const SwBBox& region, const SwFill* fill)
+static bool _rasterDirectGradientMaskedRect(SwSurface* surface, const SwBBox& region, const SwFill* fill, SwMask maskOp)
{
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 cstride = surface->compositor->image.stride;
+ auto cbuffer = surface->compositor->image.buf8 + (region.min.y * cstride + region.min.x);
+ auto dbuffer = surface->buf8 + (region.min.y * surface->stride + region.min.x);
- for (auto y = surface->compositor->bbox.min.y; y < surface->compositor->bbox.max.y; ++y) {
- auto cmp = surface->compositor->image.buf32 + (y * cstride + surface->compositor->bbox.min.x);
- if (y == region.min.y) {
- for (auto y2 = y; y2 < region.max.y; ++y2) {
- auto tmp = cmp;
- auto x = surface->compositor->bbox.min.x;
- while (x < surface->compositor->bbox.max.x) {
- if (x == region.min.x) {
- fillMethod()(fill, tmp, y2, x, w, opMaskPreIntersect, 255);
- x += w;
- tmp += w;
- } else {
- *tmp = 0;
- ++tmp;
- ++x;
- }
- }
- cmp += cstride;
- }
- y += (h - 1);
- } else {
- rasterPixel32(cmp, 0x00000000, 0, surface->compositor->bbox.max.x -surface->compositor->bbox.min.x);
- cmp += cstride;
- }
+ for (uint32_t y = 0; y < h; ++y) {
+ fillMethod()(fill, dbuffer, region.min.y + y, region.min.x, w, cbuffer, maskOp, 255);
+ cbuffer += cstride;
+ dbuffer += surface->stride;
}
+ return true;
}
@@ -1618,16 +1555,14 @@ static bool _rasterGradientMaskedRect(SwSurface* surface, const SwBBox& region,
{
auto method = surface->compositor->method;
- TVGLOG("SW_ENGINE", "Masked(%d) Gradient [Region: %lu %lu %lu %lu]", (int)surface->compositor->method, region.min.x, region.min.y, region.max.x - region.min.x, region.max.y - region.min.y);
+ TVGLOG("SW_ENGINE", "Masked(%d) Gradient [Region: %lu %lu %lu %lu]", (int)method, region.min.x, region.min.y, region.max.x - region.min.x, region.max.y - region.min.y);
- if (method == CompositeMethod::AddMask) _rasterGradientMaskedRectDup<fillMethod>(surface, region, fill, opMaskPreAdd);
- else if (method == CompositeMethod::SubtractMask) _rasterGradientMaskedRectDup<fillMethod>(surface, region, fill, opMaskPreSubtract);
- else if (method == CompositeMethod::DifferenceMask) _rasterGradientMaskedRectDup<fillMethod>(surface, region, fill, opMaskPreDifference);
- else if (method == CompositeMethod::IntersectMask) _rasterGradientMaskedRectInt<fillMethod>(surface, region, fill);
- else return false;
+ auto maskOp = _getMaskOp(method);
- //Masking Composition
- return _rasterDirectImage(surface, &surface->compositor->image, surface->compositor->bbox, 255);
+ if (_direct(method)) return _rasterDirectGradientMaskedRect<fillMethod>(surface, region, fill, maskOp);
+ else return _rasterCompositeGradientMaskedRect<fillMethod>(surface, region, fill, maskOp);
+
+ return false;
}
@@ -1719,8 +1654,6 @@ static bool _rasterLinearGradientRect(SwSurface* surface, const SwBBox& region,
static bool _rasterRadialGradientRect(SwSurface* surface, const SwBBox& region, const SwFill* fill)
{
- if (fill->radial.a < FLT_EPSILON) return false;
-
if (_compositing(surface)) {
if (_matting(surface)) return _rasterGradientMattedRect<FillRadial>(surface, region, fill);
else return _rasterGradientMaskedRect<FillRadial>(surface, region, fill);
@@ -1734,64 +1667,54 @@ static bool _rasterRadialGradientRect(SwSurface* surface, const SwBBox& region,
}
-
/************************************************************************/
/* Rle Gradient */
/************************************************************************/
template<typename fillMethod>
-static void _rasterGradientMaskedRleDup(SwSurface* surface, const SwRleData* rle, const SwFill* fill, SwBlender maskOp)
+static bool _rasterCompositeGradientMaskedRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill, SwMask maskOp)
{
auto span = rle->spans;
auto cstride = surface->compositor->image.stride;
- auto cbuffer = surface->compositor->image.buf32;
+ auto cbuffer = surface->compositor->image.buf8;
for (uint32_t i = 0; i < rle->size; ++i, ++span) {
auto cmp = &cbuffer[span->y * cstride + span->x];
fillMethod()(fill, cmp, span->y, span->x, span->len, maskOp, span->coverage);
}
+ return _compositeMaskImage(surface, &surface->compositor->image, surface->compositor->bbox);
}
template<typename fillMethod>
-static void _rasterGradientMaskedRleInt(SwSurface* surface, const SwRleData* rle, const SwFill* fill)
+static bool _rasterDirectGradientMaskedRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill, SwMask maskOp)
{
auto span = rle->spans;
auto cstride = surface->compositor->image.stride;
- auto cbuffer = surface->compositor->image.buf32;
-
- for (auto y = surface->compositor->bbox.min.y; y < surface->compositor->bbox.max.y; ++y) {
- auto cmp = &cbuffer[y * cstride];
- auto x = surface->compositor->bbox.min.x;
- while (x < surface->compositor->bbox.max.x) {
- if (y == span->y && x == span->x && x + span->len <= surface->compositor->bbox.max.x) {
- fillMethod()(fill, cmp, span->y, span->x, span->len, opMaskIntersect, span->coverage);
- x += span->len;
- ++span;
- } else {
- cmp[x] = 0;
- ++x;
- }
- }
+ auto cbuffer = surface->compositor->image.buf8;
+ auto dbuffer = surface->buf8;
+
+ for (uint32_t i = 0; i < rle->size; ++i, ++span) {
+ auto cmp = &cbuffer[span->y * cstride + span->x];
+ auto dst = &dbuffer[span->y * surface->stride + span->x];
+ fillMethod()(fill, dst, span->y, span->x, span->len, cmp, maskOp, span->coverage);
}
+ return true;
}
template<typename fillMethod>
static bool _rasterGradientMaskedRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill)
{
- TVGLOG("SW_ENGINE", "Masked(%d) Rle Linear Gradient", (int)surface->compositor->method);
-
auto method = surface->compositor->method;
- if (method == CompositeMethod::AddMask) _rasterGradientMaskedRleDup<fillMethod>(surface, rle, fill, opMaskAdd);
- else if (method == CompositeMethod::SubtractMask) _rasterGradientMaskedRleDup<fillMethod>(surface, rle, fill, opMaskSubtract);
- else if (method == CompositeMethod::DifferenceMask) _rasterGradientMaskedRleDup<fillMethod>(surface, rle, fill, opMaskDifference);
- else if (method == CompositeMethod::IntersectMask) _rasterGradientMaskedRleInt<fillMethod>(surface, rle, fill);
- else return false;
+ TVGLOG("SW_ENGINE", "Masked(%d) Rle Linear Gradient", (int)method);
- //Masking Composition
- return _rasterDirectImage(surface, &surface->compositor->image, surface->compositor->bbox, 255);
+ auto maskOp = _getMaskOp(method);
+
+ if (_direct(method)) return _rasterDirectGradientMaskedRle<fillMethod>(surface, rle, fill, maskOp);
+ else return _rasterCompositeGradientMaskedRle<fillMethod>(surface, rle, fill, maskOp);
+ return false;
}
@@ -1832,10 +1755,19 @@ static bool _rasterTranslucentGradientRle(SwSurface* surface, const SwRleData* r
{
auto span = rle->spans;
- for (uint32_t i = 0; i < rle->size; ++i, ++span) {
- auto dst = &surface->buf32[span->y * surface->stride + span->x];
- if (span->coverage == 255) fillMethod()(fill, dst, span->y, span->x, span->len, opBlendPreNormal, 255);
- else fillMethod()(fill, dst, span->y, span->x, span->len, opBlendNormal, span->coverage);
+ //32 bits
+ if (surface->channelSize == sizeof(uint32_t)) {
+ for (uint32_t i = 0; i < rle->size; ++i, ++span) {
+ auto dst = &surface->buf32[span->y * surface->stride + span->x];
+ if (span->coverage == 255) fillMethod()(fill, dst, span->y, span->x, span->len, opBlendPreNormal, 255);
+ else fillMethod()(fill, dst, span->y, span->x, span->len, opBlendNormal, span->coverage);
+ }
+ //8 bits
+ } else if (surface->channelSize == sizeof(uint8_t)) {
+ for (uint32_t i = 0; i < rle->size; ++i, ++span) {
+ auto dst = &surface->buf8[span->y * surface->stride + span->x];
+ fillMethod()(fill, dst, span->y, span->x, span->len, _opMaskAdd, 255);
+ }
}
return true;
}
@@ -1846,11 +1778,22 @@ static bool _rasterSolidGradientRle(SwSurface* surface, const SwRleData* rle, co
{
auto span = rle->spans;
- for (uint32_t i = 0; i < rle->size; ++i, ++span) {
- auto dst = &surface->buf32[span->y * surface->stride + span->x];
- if (span->coverage == 255) fillMethod()(fill, dst, span->y, span->x, span->len, opBlendSrcOver, 255);
- else fillMethod()(fill, dst, span->y, span->x, span->len, opBlendInterp, span->coverage);
+ //32 bits
+ if (surface->channelSize == sizeof(uint32_t)) {
+ for (uint32_t i = 0; i < rle->size; ++i, ++span) {
+ auto dst = &surface->buf32[span->y * surface->stride + span->x];
+ if (span->coverage == 255) fillMethod()(fill, dst, span->y, span->x, span->len, opBlendSrcOver, 255);
+ else fillMethod()(fill, dst, span->y, span->x, span->len, opBlendInterp, span->coverage);
+ }
+ //8 bits
+ } else if (surface->channelSize == sizeof(uint8_t)) {
+ for (uint32_t i = 0; i < rle->size; ++i, ++span) {
+ auto dst = &surface->buf8[span->y * surface->stride + span->x];
+ if (span->coverage == 255) fillMethod()(fill, dst, span->y, span->x, span->len, _opMaskNone, 255);
+ else fillMethod()(fill, dst, span->y, span->x, span->len, _opMaskAdd, span->coverage);
+ }
}
+
return true;
}
@@ -1874,7 +1817,7 @@ static bool _rasterLinearGradientRle(SwSurface* surface, const SwRleData* rle, c
static bool _rasterRadialGradientRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill)
{
- if (!rle || fill->radial.a < FLT_EPSILON) return false;
+ if (!rle) return false;
if (_compositing(surface)) {
if (_matting(surface)) return _rasterGradientMattedRle<FillRadial>(surface, rle, fill);
@@ -2018,11 +1961,6 @@ void rasterPremultiply(Surface* surface)
bool rasterGradientShape(SwSurface* surface, SwShape* shape, unsigned id)
{
- if (surface->channelSize == sizeof(uint8_t)) {
- TVGERR("SW_ENGINE", "Not supported grayscale gradient!");
- return false;
- }
-
if (!shape->fill) return false;
if (shape->fastTrack) {
@@ -2038,11 +1976,6 @@ bool rasterGradientShape(SwSurface* surface, SwShape* shape, unsigned id)
bool rasterGradientStroke(SwSurface* surface, SwShape* shape, unsigned id)
{
- if (surface->channelSize == sizeof(uint8_t)) {
- TVGERR("SW_ENGINE", "Not supported grayscale gradient!");
- return false;
- }
-
if (!shape->stroke || !shape->stroke->fill || !shape->strokeRle) return false;
if (id == TVG_CLASS_ID_LINEAR) return _rasterLinearGradientRle(surface, shape->strokeRle, shape->stroke->fill);
@@ -2059,7 +1992,6 @@ bool rasterShape(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint8
g = MULTIPLY(g, a);
b = MULTIPLY(b, a);
}
-
if (shape->fastTrack) return _rasterRect(surface, shape->bbox, r, g, b, a);
else return _rasterRle(surface, shape->rle, r, g, b, a);
}
@@ -2079,19 +2011,10 @@ bool rasterStroke(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint
bool rasterImage(SwSurface* surface, SwImage* image, const RenderMesh* mesh, const Matrix* transform, const SwBBox& bbox, uint8_t opacity)
{
- if (surface->channelSize == sizeof(uint8_t)) {
- TVGERR("SW_ENGINE", "Not supported grayscale image!");
- return false;
- }
-
//Verify Boundary
if (bbox.max.x < 0 || bbox.max.y < 0 || bbox.min.x >= static_cast<SwCoord>(surface->w) || bbox.min.y >= static_cast<SwCoord>(surface->h)) return false;
- //TOOD: switch (image->format)
- //TODO: case: _rasterRGBImageMesh()
- //TODO: case: _rasterGrayscaleImageMesh()
- //TODO: case: _rasterAlphaImageMesh()
- if (mesh && mesh->triangleCnt > 0) return _transformedImageMesh(surface, image, mesh, transform, &bbox, opacity);
+ if (mesh && mesh->triangleCnt > 0) return _rasterTexmapPolygonMesh(surface, image, mesh, transform, &bbox, opacity);
else return _rasterImage(surface, image, transform, bbox, opacity);
}
diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterTexmap.h b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterTexmap.h
index 4b30c52ea3..698ab37da2 100644
--- a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterTexmap.h
+++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterTexmap.h
@@ -70,190 +70,17 @@ static bool _arrange(const SwImage* image, const SwBBox* region, int& yStart, in
}
-static void _rasterMaskedPolygonImageSegmentInt(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, AASpans* aaSpans, uint8_t opacity, uint8_t dirFlag)
+static bool _rasterMaskedPolygonImageSegment(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, AASpans* aaSpans, uint8_t opacity, uint8_t dirFlag = 0)
{
- 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;
-}
-
+ return false;
-static void _rasterMaskedPolygonImageSegmentDup(SwSurface* surface, const SwImage* image, const SwBBox* region, SwBlender maskOp, SwBlender amaskOp, int yStart, int yEnd, AASpans* aaSpans, uint8_t opacity)
-{
+#if 0 //Enable it when GRAYSCALE image is supported
+ auto maskOp = _getMaskOp(surface->compositor->method);
+ auto direct = _direct(surface->compositor->method);
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 sbuf = image->buf8;
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;
@@ -262,7 +89,7 @@ static void _rasterMaskedPolygonImageSegmentDup(SwSurface* surface, const SwImag
float dx, u, v, iptr;
SwSpan* span = nullptr; //used only when rle based.
- if (!_arrange(image, region, yStart, yEnd)) return;
+ if (!_arrange(image, region, yStart, yEnd)) return false;
//Loop through all lines in the segment
uint32_t spanIdx = 0;
@@ -313,7 +140,8 @@ static void _rasterMaskedPolygonImageSegmentDup(SwSurface* surface, const SwImag
x = x1;
- auto cmp = &surface->compositor->image.buf32[y * surface->compositor->image.stride + x1];
+ auto cmp = &surface->compositor->image.buf8[y * surface->compositor->image.stride + x1];
+ auto dst = &surface->buf8[y * surface->stride + x1];
if (opacity == 255) {
//Draw horizontal line
@@ -349,7 +177,13 @@ static void _rasterMaskedPolygonImageSegmentDup(SwSurface* surface, const SwImag
}
px = INTERPOLATE(px, px2, ab);
}
- *cmp = maskOp(px, *cmp, IA(px));
+ if (direct) {
+ auto tmp = maskOp(px, *cmp, 0); //not use alpha
+ *dst = tmp + MULTIPLY(*dst, ~tmp);
+ ++dst;
+ } else {
+ *cmp = maskOp(px, *cmp, ~px);
+ }
++cmp;
//Step UV horizontally
@@ -392,7 +226,15 @@ static void _rasterMaskedPolygonImageSegmentDup(SwSurface* surface, const SwImag
}
px = INTERPOLATE(px, px2, ab);
}
- *cmp = amaskOp(px, *cmp, opacity);
+
+ if (direct) {
+ auto tmp = maskOp(MULTIPLY(px, opacity), *cmp, 0);
+ *dst = tmp + MULTIPLY(*dst, ~tmp);
+ ++dst;
+ } else {
+ auto tmp = MULTIPLY(px, opacity);
+ *cmp = maskOp(tmp, *cmp, ~px);
+ }
++cmp;
//Step UV horizontally
@@ -418,17 +260,9 @@ static void _rasterMaskedPolygonImageSegmentDup(SwSurface* surface, const SwImag
xb = _xb;
ua = _ua;
va = _va;
-}
-
-static void _rasterMaskedPolygonImageSegment(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, AASpans* aaSpans, uint8_t opacity, uint8_t dirFlag = 0)
-{
- 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);
- }
+ return true;
+#endif
}
@@ -1256,6 +1090,11 @@ static bool _apply(SwSurface* surface, AASpans* aaSpans)
*/
static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const Matrix* transform, const SwBBox* region, uint8_t opacity)
{
+ if (surface->channelSize == sizeof(uint8_t)) {
+ TVGERR("SW_ENGINE", "Not supported grayscale Textmap polygon!");
+ return false;
+ }
+
//Exceptions: No dedicated drawing area?
if ((!image->rle && !region) || (image->rle && image->rle->size == 0)) return false;
@@ -1294,6 +1133,11 @@ static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const
_rasterPolygonImage(surface, image, region, polygon, aaSpans, opacity);
+#if 0
+ if (_compositing(surface) && _masking(surface) && !_direct(surface->compositor->method)) {
+ _compositeMaskImage(surface, &surface->compositor->image, surface->compositor->bbox);
+ }
+#endif
return _apply(surface, aaSpans);
}
@@ -1313,6 +1157,11 @@ static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const
*/
static bool _rasterTexmapPolygonMesh(SwSurface* surface, const SwImage* image, const RenderMesh* mesh, const Matrix* transform, const SwBBox* region, uint8_t opacity)
{
+ if (surface->channelSize == sizeof(uint8_t)) {
+ TVGERR("SW_ENGINE", "Not supported grayscale Textmap polygon mesh!");
+ return false;
+ }
+
//Exceptions: No dedicated drawing area?
if ((!image->rle && !region) || (image->rle && image->rle->size == 0)) return false;
@@ -1342,15 +1191,17 @@ static bool _rasterTexmapPolygonMesh(SwSurface* surface, const SwImage* image, c
}
// Get AA spans and step polygons again to draw
- auto aaSpans = _AASpans(ys, ye, image, region);
- if (aaSpans) {
+ if (auto aaSpans = _AASpans(ys, ye, image, region)) {
for (uint32_t i = 0; i < mesh->triangleCnt; i++) {
_rasterPolygonImage(surface, image, region, transformedTris[i], aaSpans, opacity);
}
- // Apply to surface (note: frees the AA spans)
+#if 0
+ if (_compositing(surface) && _masking(surface) && !_direct(surface->compositor->method)) {
+ _compositeMaskImage(surface, &surface->compositor->image, surface->compositor->bbox);
+ }
+#endif
_apply(surface, aaSpans);
}
free(transformedTris);
-
return true;
}
diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRenderer.cpp b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRenderer.cpp
index 1115a41d64..091b72fa97 100644
--- a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRenderer.cpp
+++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRenderer.cpp
@@ -78,6 +78,30 @@ struct SwShapeTask : SwTask
bool cmpStroking = false;
bool clipper = false;
+ /* We assume that if the stroke width is greater than 2,
+ the shape's outline beneath the stroke could be adequately covered by the stroke drawing.
+ Therefore, antialiasing is disabled under this condition.
+ Additionally, the stroke style should not be dashed. */
+ bool antialiasing(float strokeWidth)
+ {
+ return strokeWidth < 2.0f || rshape->stroke->dashCnt > 0 || rshape->stroke->strokeFirst;
+ }
+
+ float validStrokeWidth()
+ {
+ if (!rshape->stroke) return 0.0f;
+
+ auto width = rshape->stroke->width;
+ if (mathZero(width)) return 0.0f;
+
+ if (!rshape->stroke->fill && (MULTIPLY(rshape->stroke->color[3], opacity) == 0)) return 0.0f;
+ if (mathZero(rshape->stroke->trim.begin - rshape->stroke->trim.end)) return 0.0f;
+
+ if (transform) return (width * sqrt(transform->e11 * transform->e11 + transform->e12 * transform->e12));
+ else return width;
+ }
+
+
bool clip(SwRleData* target) override
{
if (shape.fastTrack) rleClipRect(target, &bbox);
@@ -99,16 +123,10 @@ struct SwShapeTask : SwTask
{
if (opacity == 0 && !clipper) return; //Invisible
- uint8_t strokeAlpha = 0;
- auto visibleStroke = false;
+ auto strokeWidth = validStrokeWidth();
bool visibleFill = false;
auto clipRegion = bbox;
- if (HALF_STROKE(rshape->strokeWidth()) > 0) {
- rshape->strokeColor(nullptr, nullptr, nullptr, &strokeAlpha);
- visibleStroke = rshape->strokeFill() || (MULTIPLY(strokeAlpha, opacity) > 0);
- }
-
//This checks also for the case, if the invisible shape turned to visible by alpha.
auto prepareShape = false;
if (!shapePrepared(&shape) && (flags & RenderUpdateFlag::Color)) prepareShape = true;
@@ -119,22 +137,15 @@ struct SwShapeTask : SwTask
rshape->fillColor(nullptr, nullptr, nullptr, &alpha);
alpha = MULTIPLY(alpha, opacity);
visibleFill = (alpha > 0 || rshape->fill);
- if (visibleFill || visibleStroke || clipper) {
+ if (visibleFill || clipper) {
shapeReset(&shape);
if (!shapePrepare(&shape, rshape, transform, clipRegion, bbox, mpool, tid, clips.count > 0 ? true : false)) goto err;
}
}
-
//Fill
if (flags & (RenderUpdateFlag::Gradient | RenderUpdateFlag::Transform | RenderUpdateFlag::Color)) {
if (visibleFill || clipper) {
- /* We assume that if stroke width is bigger than 2,
- shape outline below stroke could be full covered by stroke drawing.
- Thus it turns off antialising in that condition.
- Also, it shouldn't be dash style. */
- auto antiAlias = strokeAlpha < 255 || rshape->strokeWidth() <= 2 || rshape->strokeDash(nullptr) > 0 || (rshape->stroke && rshape->stroke->strokeFirst);
-
- if (!shapeGenRle(&shape, rshape, antiAlias)) goto err;
+ if (!shapeGenRle(&shape, rshape, antialiasing(strokeWidth))) goto err;
}
if (auto fill = rshape->fill) {
auto ctable = (flags & RenderUpdateFlag::Gradient) ? true : false;
@@ -144,10 +155,9 @@ struct SwShapeTask : SwTask
shapeDelFill(&shape);
}
}
-
//Stroke
if (flags & (RenderUpdateFlag::Stroke | RenderUpdateFlag::Transform)) {
- if (visibleStroke) {
+ if (strokeWidth > 0.0f) {
shapeResetStroke(&shape, rshape, transform);
if (!shapeGenStrokeRle(&shape, rshape, transform, clipRegion, bbox, mpool, tid)) goto err;
@@ -641,8 +651,6 @@ Compositor* SwRenderer::target(const RenderRegion& region, ColorSpace cs)
if (x + w > sw) w = (sw - x);
if (y + h > sh) h = (sh - y);
- TVGLOG("SW_ENGINE", "Using intermediate composition [Region: %d %d %d %d]", x, y, w, h);
-
cmp->compositor->recoverSfc = surface;
cmp->compositor->recoverCmp = surface->compositor;
cmp->compositor->valid = false;
diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRle.cpp b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRle.cpp
index fe84983a76..a4a7fabdee 100644
--- a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRle.cpp
+++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRle.cpp
@@ -716,11 +716,11 @@ static void _decomposeOutline(RleWorker& rw)
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 start = UPSCALE(outline->pts[first]);
auto pt = outline->pts.data + first;
auto types = outline->types.data + first;
- _moveTo(rw, UPSCALE(outline->pts.data[first]));
+ _moveTo(rw, UPSCALE(outline->pts[first]));
while (pt < limit) {
++pt;
diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwShape.cpp b/thirdparty/thorvg/src/lib/sw_engine/tvgSwShape.cpp
index 14dd68b906..651eaee452 100644
--- a/thirdparty/thorvg/src/lib/sw_engine/tvgSwShape.cpp
+++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwShape.cpp
@@ -21,9 +21,8 @@
*/
#include "tvgSwCommon.h"
+#include "tvgMath.h"
#include "tvgBezier.h"
-#include <float.h>
-#include <math.h>
/************************************************************************/
/* Internal Class Implementation */
@@ -108,7 +107,7 @@ static void _outlineClose(SwOutline& outline)
if (outline.pts.count == i) return;
//Close the path
- outline.pts.push(outline.pts.data[i]);
+ outline.pts.push(outline.pts[i]);
outline.types.push(SW_CURVE_TYPE_POINT);
outline.closed.push(true);
}
@@ -127,14 +126,18 @@ static void _dashLineTo(SwDashStroke& dash, const Point* to, const Matrix* trans
}
} else {
while (len > dash.curLen) {
- len -= dash.curLen;
Line left, right;
- _lineSplitAt(cur, dash.curLen, left, right);;
- dash.curIdx = (dash.curIdx + 1) % dash.cnt;
- if (!dash.curOpGap) {
- _outlineMoveTo(*dash.outline, &left.pt1, transform);
- _outlineLineTo(*dash.outline, &left.pt2, transform);
+ if (dash.curLen > 0) {
+ len -= dash.curLen;
+ _lineSplitAt(cur, dash.curLen, left, right);
+ if (!dash.curOpGap) {
+ _outlineMoveTo(*dash.outline, &left.pt1, transform);
+ _outlineLineTo(*dash.outline, &left.pt2, transform);
+ }
+ } else {
+ right = cur;
}
+ dash.curIdx = (dash.curIdx + 1) % dash.cnt;
dash.curLen = dash.pattern[dash.curIdx];
dash.curOpGap = !dash.curOpGap;
cur = right;
@@ -169,16 +172,22 @@ static void _dashCubicTo(SwDashStroke& dash, const Point* ctrl1, const Point* ct
_outlineCubicTo(*dash.outline, ctrl1, ctrl2, to, transform);
}
} else {
+ bool begin = true; //starting with move_to
while (len > dash.curLen) {
Bezier left, right;
- len -= dash.curLen;
- bezSplitAt(cur, dash.curLen, left, right);
- if (!dash.curOpGap) {
- // leftovers from a previous command don't require moveTo
- if (dash.pattern[dash.curIdx] - dash.curLen < FLT_EPSILON) {
- _outlineMoveTo(*dash.outline, &left.start, transform);
+ if (dash.curLen > 0) {
+ len -= dash.curLen;
+ bezSplitAt(cur, dash.curLen, left, right);
+ if (!dash.curOpGap) {
+ // leftovers from a previous command don't require moveTo
+ if (begin || dash.pattern[dash.curIdx] - dash.curLen < FLT_EPSILON) {
+ _outlineMoveTo(*dash.outline, &left.start, transform);
+ begin = false;
+ }
+ _outlineCubicTo(*dash.outline, &left.ctrl1, &left.ctrl2, &left.end, transform);
}
- _outlineCubicTo(*dash.outline, &left.ctrl1, &left.ctrl2, &left.end, transform);
+ } else {
+ right = cur;
}
dash.curIdx = (dash.curIdx + 1) % dash.cnt;
dash.curLen = dash.pattern[dash.curIdx];
@@ -203,11 +212,10 @@ static void _dashCubicTo(SwDashStroke& dash, const Point* ctrl1, const Point* ct
}
-static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* transform)
+static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* transform, float length)
{
const PathCommand* cmds = rshape->path.cmds.data;
auto cmdCnt = rshape->path.cmds.count;
-
const Point* pts = rshape->path.pts.data;
auto ptsCnt = rshape->path.pts.count;
@@ -215,86 +223,161 @@ static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* trans
if (cmdCnt == 0 || ptsCnt == 0) return nullptr;
SwDashStroke dash;
- dash.curIdx = 0;
- dash.curLen = 0;
- dash.ptStart = {0, 0};
- dash.ptCur = {0, 0};
- dash.curOpGap = false;
+ auto offset = 0.0f;
+ auto trimmed = false;
+
+ dash.cnt = rshape->strokeDash((const float**)&dash.pattern, &offset);
+
+ //dash by trimming.
+ if (length > 0.0f && dash.cnt == 0) {
+ auto begin = length * rshape->stroke->trim.begin;
+ auto end = length * rshape->stroke->trim.end;
+
+ //TODO: mix trimming + dash style
+
+ //default
+ if (end > begin) {
+ if (begin > 0) dash.cnt += 4;
+ else dash.cnt += 2;
+ //looping
+ } else dash.cnt += 3;
+
+ dash.pattern = (float*)malloc(sizeof(float) * dash.cnt);
+
+ if (dash.cnt == 2) {
+ dash.pattern[0] = end - begin;
+ dash.pattern[1] = length - (end - begin);
+ } else if (dash.cnt == 3) {
+ dash.pattern[0] = end;
+ dash.pattern[1] = (begin - end);
+ dash.pattern[2] = length - begin;
+ } else {
+ dash.pattern[0] = 0; //zero dash to start with a space.
+ dash.pattern[1] = begin;
+ dash.pattern[2] = end - begin;
+ dash.pattern[3] = length - (end - begin);
+ }
+
+ trimmed = true;
+ //just a dasy style.
+ } else {
+
+ if (dash.cnt == 0) return nullptr;
+ }
- const float* pattern;
- dash.cnt = rshape->strokeDash(&pattern);
- if (dash.cnt == 0) return nullptr;
+ //offset?
+ auto patternLength = 0.0f;
+ uint32_t offIdx = 0;
+ if (!mathZero(offset)) {
+ for (size_t i = 0; i < dash.cnt; ++i) patternLength += dash.pattern[i];
+ bool isOdd = dash.cnt % 2;
+ if (isOdd) patternLength *= 2;
+
+ offset = fmod(offset, patternLength);
+ if (offset < 0) offset += patternLength;
+
+ for (size_t i = 0; i < dash.cnt * (1 + (size_t)isOdd); ++i, ++offIdx) {
+ auto curPattern = dash.pattern[i % dash.cnt];
+ if (offset < curPattern) break;
+ offset -= curPattern;
+ }
+ }
//OPTMIZE ME: Use mempool???
- dash.pattern = const_cast<float*>(pattern);
dash.outline = static_cast<SwOutline*>(calloc(1, sizeof(SwOutline)));
//smart reservation
- auto outlinePtsCnt = 0;
- auto outlineCntrsCnt = 0;
+ auto closeCnt = 0;
+ auto moveCnt = 0;
+
+ for (auto cmd = rshape->path.cmds.data; cmd < rshape->path.cmds.end(); ++cmd) {
+ if (*cmd == PathCommand::Close) ++closeCnt;
+ else if (*cmd == PathCommand::MoveTo) ++moveCnt;
+ }
- for (uint32_t i = 0; i < cmdCnt; ++i) {
- switch (*(cmds + i)) {
+ //No idea exact count.... Reserve Approximitely 20x...
+ //OPTIMIZE: we can directly copy the path points when the close is occupied with a point.
+ dash.outline->pts.grow(20 * (closeCnt + ptsCnt + 1));
+ dash.outline->types.grow(20 * (closeCnt + ptsCnt + 1));
+ dash.outline->cntrs.grow(20 * (moveCnt + 1));
+
+ while (cmdCnt-- > 0) {
+ switch (*cmds) {
case PathCommand::Close: {
- ++outlinePtsCnt;
+ _dashLineTo(dash, &dash.ptStart, transform);
break;
}
case PathCommand::MoveTo: {
- ++outlineCntrsCnt;
- ++outlinePtsCnt;
+ //reset the dash
+ dash.curIdx = offIdx % dash.cnt;
+ dash.curLen = dash.pattern[dash.curIdx] - offset;
+ dash.curOpGap = offIdx % 2;
+ dash.ptStart = dash.ptCur = *pts;
+ ++pts;
break;
}
case PathCommand::LineTo: {
- ++outlinePtsCnt;
+ _dashLineTo(dash, pts, transform);
+ ++pts;
break;
}
case PathCommand::CubicTo: {
- outlinePtsCnt += 3;
+ _dashCubicTo(dash, pts, pts + 1, pts + 2, transform);
+ pts += 3;
break;
}
}
+ ++cmds;
}
- ++outlinePtsCnt; //for close
- ++outlineCntrsCnt; //for end
+ _outlineEnd(*dash.outline);
- //No idea exact count.... Reserve Approximitely 20x...
- dash.outline->pts.grow(20 * outlinePtsCnt);
- dash.outline->types.grow(20 * outlinePtsCnt);
- dash.outline->cntrs.grow(20 * outlineCntrsCnt);
+ if (trimmed) free(dash.pattern);
+
+ return dash.outline;
+}
+
+
+static float _outlineLength(const RenderShape* rshape)
+{
+ const PathCommand* cmds = rshape->path.cmds.data;
+ auto cmdCnt = rshape->path.cmds.count;
+ const Point* pts = rshape->path.pts.data;
+ auto ptsCnt = rshape->path.pts.count;
+ //No actual shape data
+ if (cmdCnt == 0 || ptsCnt == 0) return 0.0f;
+
+ const Point* close = nullptr;
+ auto length = 0.0f;
+
+ //Compute the whole length
while (cmdCnt-- > 0) {
switch (*cmds) {
case PathCommand::Close: {
- _dashLineTo(dash, &dash.ptStart, transform);
+ length += mathLength(pts - 1, close);
+ ++pts;
break;
}
case PathCommand::MoveTo: {
- //reset the dash
- dash.curIdx = 0;
- dash.curLen = *dash.pattern;
- dash.curOpGap = false;
- dash.ptStart = dash.ptCur = *pts;
+ close = pts;
++pts;
break;
}
case PathCommand::LineTo: {
- _dashLineTo(dash, pts, transform);
+ length += mathLength(pts - 1, pts);
++pts;
break;
}
case PathCommand::CubicTo: {
- _dashCubicTo(dash, pts, pts + 1, pts + 2, transform);
+ length += bezLength({*(pts - 1), *pts, *(pts + 1), *(pts + 2)});
pts += 3;
break;
}
}
++cmds;
}
-
- _outlineEnd(*dash.outline);
-
- return dash.outline;
+ return length;
}
@@ -321,7 +404,6 @@ static bool _genOutline(SwShape* shape, const RenderShape* rshape, const Matrix*
{
const PathCommand* cmds = rshape->path.cmds.data;
auto cmdCnt = rshape->path.cmds.count;
-
const Point* pts = rshape->path.pts.data;
auto ptsCnt = rshape->path.pts.count;
@@ -329,47 +411,21 @@ static bool _genOutline(SwShape* shape, const RenderShape* rshape, const Matrix*
if (cmdCnt == 0 || ptsCnt == 0) return false;
//smart reservation
- auto outlinePtsCnt = 0;
- auto outlineCntrsCnt = 0;
+ auto moveCnt = 0;
auto closeCnt = 0;
- for (uint32_t i = 0; i < cmdCnt; ++i) {
- switch (*(cmds + i)) {
- case PathCommand::Close: {
- ++outlinePtsCnt;
- ++closeCnt;
- break;
- }
- case PathCommand::MoveTo: {
- ++outlineCntrsCnt;
- ++outlinePtsCnt;
- break;
- }
- case PathCommand::LineTo: {
- ++outlinePtsCnt;
- break;
- }
- case PathCommand::CubicTo: {
- outlinePtsCnt += 3;
- break;
- }
- }
+ for (auto cmd = rshape->path.cmds.data; cmd < rshape->path.cmds.end(); ++cmd) {
+ if (*cmd == PathCommand::Close) ++closeCnt;
+ else if (*cmd == PathCommand::MoveTo) ++moveCnt;
}
- if (static_cast<uint32_t>(outlinePtsCnt - closeCnt) > ptsCnt) {
- TVGERR("SW_ENGINE", "Wrong a pair of the commands & points - required(%d), current(%d)", outlinePtsCnt - closeCnt, ptsCnt);
- return false;
- }
-
- ++outlinePtsCnt; //for close
- ++outlineCntrsCnt; //for end
-
shape->outline = mpoolReqOutline(mpool, tid);
auto outline = shape->outline;
- outline->pts.grow(outlinePtsCnt);
- outline->types.grow(outlinePtsCnt);
- outline->cntrs.grow(outlineCntrsCnt);
+ //OPTIMIZE: we can directly copy the path points when the close is occupied with a point.
+ outline->pts.grow(ptsCnt + closeCnt + 1);
+ outline->types.grow(ptsCnt + closeCnt + 1);
+ outline->cntrs.grow(moveCnt + 1);
//Dash outlines are always opened.
//Only normal outlines use this information, it sholud be same to their contour counts.
@@ -514,12 +570,14 @@ bool shapeGenStrokeRle(SwShape* shape, const RenderShape* rshape, const Matrix*
bool freeOutline = false;
bool ret = true;
- //Dash Style Stroke
- if (rshape->strokeDash(nullptr) > 0) {
- shapeOutline = _genDashOutline(rshape, transform);
+ auto length = rshape->strokeTrim() ? _outlineLength(rshape) : 0.0f;
+
+ //Dash style (+trimming)
+ if (rshape->stroke->dashCnt > 0 || length > 0) {
+ shapeOutline = _genDashOutline(rshape, transform, length);
if (!shapeOutline) return false;
freeOutline = true;
- //Normal Style stroke
+ //Normal style
} else {
if (!shape->outline) {
if (!_genOutline(shape, rshape, transform, mpool, tid, false)) return false;
diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwStroke.cpp b/thirdparty/thorvg/src/lib/sw_engine/tvgSwStroke.cpp
index 38626cb428..f46a4a5a1d 100644
--- a/thirdparty/thorvg/src/lib/sw_engine/tvgSwStroke.cpp
+++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwStroke.cpp
@@ -373,10 +373,6 @@ void _firstSubPath(SwStroke& stroke, SwFixed startAngle, SwFixed lineLength)
static void _lineTo(SwStroke& stroke, const SwPoint& to)
{
auto delta = to - stroke.center;
-
- //a zero-length lineto is a no-op; avoid creating a spurious corner
- if (delta.zero()) return;
-
//compute length of line
auto angle = mathAtan(delta);
@@ -428,12 +424,6 @@ 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 ((stroke.center - ctrl1).small() && (ctrl1 - ctrl2).small() && (ctrl2 - to).small()) {
- stroke.center = to;
- return;
- }
-
SwPoint bezStack[37]; //TODO: static?
auto limit = bezStack + 32;
auto arc = bezStack;
@@ -852,7 +842,7 @@ bool strokeParseOutline(SwStroke* stroke, const SwOutline& outline)
continue;
}
- auto start = outline.pts.data[first];
+ auto start = outline.pts[first];
auto pt = outline.pts.data + first;
auto types = outline.types.data + first;
auto type = types[0];
diff --git a/thirdparty/thorvg/src/lib/tvgAnimation.cpp b/thirdparty/thorvg/src/lib/tvgAnimation.cpp
index f671daa727..4e8c8568d4 100644
--- a/thirdparty/thorvg/src/lib/tvgAnimation.cpp
+++ b/thirdparty/thorvg/src/lib/tvgAnimation.cpp
@@ -23,6 +23,7 @@
//#include "tvgAnimationImpl.h"
#include "tvgCommon.h"
#include "tvgFrameModule.h"
+#include "tvgPaint.h"
#include "tvgPictureImpl.h"
/************************************************************************/
@@ -31,8 +32,20 @@
struct Animation::Impl
{
- //TODO: Memory Safety
- Picture picture;
+ Picture* picture = nullptr;
+
+ Impl()
+ {
+ picture = Picture::gen().release();
+ static_cast<Paint*>(picture)->pImpl->ref();
+ }
+
+ ~Impl()
+ {
+ if (static_cast<Paint*>(picture)->pImpl->unref() == 0) {
+ delete(picture);
+ }
+ }
};
/************************************************************************/
@@ -41,19 +54,18 @@ struct Animation::Impl
Animation::~Animation()
{
-
+ delete(pImpl);
}
Animation::Animation() : pImpl(new Impl)
{
- pImpl->picture.pImpl->animated = true;
}
Result Animation::frame(uint32_t no) noexcept
{
- auto loader = pImpl->picture.pImpl->loader.get();
+ auto loader = pImpl->picture->pImpl->loader.get();
if (!loader) return Result::InsufficientCondition;
if (!loader->animatable()) return Result::NonSupport;
@@ -65,13 +77,13 @@ Result Animation::frame(uint32_t no) noexcept
Picture* Animation::picture() const noexcept
{
- return &pImpl->picture;
+ return pImpl->picture;
}
uint32_t Animation::curFrame() const noexcept
{
- auto loader = pImpl->picture.pImpl->loader.get();
+ auto loader = pImpl->picture->pImpl->loader.get();
if (!loader) return 0;
if (!loader->animatable()) return 0;
@@ -82,7 +94,7 @@ uint32_t Animation::curFrame() const noexcept
uint32_t Animation::totalFrame() const noexcept
{
- auto loader = pImpl->picture.pImpl->loader.get();
+ auto loader = pImpl->picture->pImpl->loader.get();
if (!loader) return 0;
if (!loader->animatable()) return 0;
@@ -93,7 +105,7 @@ uint32_t Animation::totalFrame() const noexcept
float Animation::duration() const noexcept
{
- auto loader = pImpl->picture.pImpl->loader.get();
+ auto loader = pImpl->picture->pImpl->loader.get();
if (!loader) return 0;
if (!loader->animatable()) return 0;
diff --git a/thirdparty/thorvg/src/lib/tvgCanvasImpl.h b/thirdparty/thorvg/src/lib/tvgCanvasImpl.h
index 9a3018e7a6..fe934ec352 100644
--- a/thirdparty/thorvg/src/lib/tvgCanvasImpl.h
+++ b/thirdparty/thorvg/src/lib/tvgCanvasImpl.h
@@ -65,8 +65,9 @@ struct Canvas::Impl
//Free paints
for (auto paint : paints) {
- paint->pImpl->dispose(*renderer);
- if (free) delete(paint);
+ if (paint->pImpl->dispose(*renderer)) {
+ if (free && paint->pImpl->unref() == 0) delete(paint);
+ }
}
paints.clear();
diff --git a/thirdparty/thorvg/src/lib/tvgCommon.h b/thirdparty/thorvg/src/lib/tvgCommon.h
index 23011ffed5..f36b4b9b30 100644
--- a/thirdparty/thorvg/src/lib/tvgCommon.h
+++ b/thirdparty/thorvg/src/lib/tvgCommon.h
@@ -76,10 +76,14 @@ using Size = Point;
#define TVGERR(tag, fmt, ...) fprintf(stderr, "%s[E]%s %s" tag "%s (%s %d): %s" fmt "\n", ErrorBgColor, ResetColors, ErrorColor, GreyColor, __FILE__, __LINE__, ResetColors, ##__VA_ARGS__)
#define TVGLOG(tag, fmt, ...) fprintf(stdout, "%s[L]%s %s" tag "%s (%s %d): %s" fmt "\n", LogBgColor, ResetColors, LogColor, GreyColor, __FILE__, __LINE__, ResetColors, ##__VA_ARGS__)
#else
- #define TVGERR(...)
- #define TVGLOG(...)
+ #define TVGERR(...) do {} while(0)
+ #define TVGLOG(...) do {} while(0)
#endif
uint16_t THORVG_VERSION_NUMBER();
+
+#define P(A) ((A)->pImpl) //Access to pimpl.
+#define PP(A) (((Paint*)(A))->pImpl) //Access to pimpl.
+
#endif //_TVG_COMMON_H_
diff --git a/thirdparty/thorvg/src/lib/tvgFill.cpp b/thirdparty/thorvg/src/lib/tvgFill.cpp
index cc7e1ccaed..9215882c8d 100644
--- a/thirdparty/thorvg/src/lib/tvgFill.cpp
+++ b/thirdparty/thorvg/src/lib/tvgFill.cpp
@@ -26,6 +26,50 @@
/* Internal Class Implementation */
/************************************************************************/
+Fill* RadialGradient::Impl::duplicate()
+{
+ auto ret = RadialGradient::gen();
+ if (!ret) return nullptr;
+
+ ret->pImpl->cx = cx;
+ ret->pImpl->cy = cy;
+ ret->pImpl->r = r;
+ ret->pImpl->fx = fx;
+ ret->pImpl->fy = fy;
+ ret->pImpl->fr = fr;
+
+ return ret.release();
+}
+
+
+Result RadialGradient::Impl::radial(float cx, float cy, float r, float fx, float fy, float fr)
+{
+ if (r < 0 || fr < 0) return Result::InvalidArguments;
+
+ this->cx = cx;
+ this->cy = cy;
+ this->r = r;
+ this->fx = fx;
+ this->fy = fy;
+ this->fr = fr;
+
+ return Result::Success;
+};
+
+
+Fill* LinearGradient::Impl::duplicate()
+{
+ auto ret = LinearGradient::gen();
+ if (!ret) return nullptr;
+
+ ret->pImpl->x1 = x1;
+ ret->pImpl->y1 = y1;
+ ret->pImpl->x2 = x2;
+ ret->pImpl->y2 = y2;
+
+ return ret.release();
+};
+
/************************************************************************/
/* External Class Implementation */
@@ -110,7 +154,97 @@ Fill* Fill::duplicate() const noexcept
return pImpl->duplicate();
}
+
uint32_t Fill::identifier() const noexcept
{
return pImpl->id;
}
+
+
+RadialGradient::RadialGradient():pImpl(new Impl())
+{
+ Fill::pImpl->id = TVG_CLASS_ID_RADIAL;
+ Fill::pImpl->method(new FillDup<RadialGradient::Impl>(pImpl));
+}
+
+
+RadialGradient::~RadialGradient()
+{
+ delete(pImpl);
+}
+
+
+Result RadialGradient::radial(float cx, float cy, float r) noexcept
+{
+ return pImpl->radial(cx, cy, r, cx, cy, 0.0f);
+}
+
+
+Result RadialGradient::radial(float* cx, float* cy, float* r) const noexcept
+{
+ if (cx) *cx = pImpl->cx;
+ if (cy) *cy = pImpl->cy;
+ if (r) *r = pImpl->r;
+
+ return Result::Success;
+}
+
+
+unique_ptr<RadialGradient> RadialGradient::gen() noexcept
+{
+ return unique_ptr<RadialGradient>(new RadialGradient);
+}
+
+
+uint32_t RadialGradient::identifier() noexcept
+{
+ return TVG_CLASS_ID_RADIAL;
+}
+
+
+LinearGradient::LinearGradient():pImpl(new Impl())
+{
+ Fill::pImpl->id = TVG_CLASS_ID_LINEAR;
+ Fill::pImpl->method(new FillDup<LinearGradient::Impl>(pImpl));
+}
+
+
+LinearGradient::~LinearGradient()
+{
+ delete(pImpl);
+}
+
+
+Result LinearGradient::linear(float x1, float y1, float x2, float y2) noexcept
+{
+ pImpl->x1 = x1;
+ pImpl->y1 = y1;
+ pImpl->x2 = x2;
+ pImpl->y2 = y2;
+
+ return Result::Success;
+}
+
+
+Result LinearGradient::linear(float* x1, float* y1, float* x2, float* y2) const noexcept
+{
+ if (x1) *x1 = pImpl->x1;
+ if (x2) *x2 = pImpl->x2;
+ if (y1) *y1 = pImpl->y1;
+ if (y2) *y2 = pImpl->y2;
+
+ return Result::Success;
+}
+
+
+unique_ptr<LinearGradient> LinearGradient::gen() noexcept
+{
+ return unique_ptr<LinearGradient>(new LinearGradient);
+}
+
+
+uint32_t LinearGradient::identifier() noexcept
+{
+ return TVG_CLASS_ID_LINEAR;
+}
+
diff --git a/thirdparty/thorvg/src/lib/tvgFill.h b/thirdparty/thorvg/src/lib/tvgFill.h
index 20603b7333..ff3dd48c49 100644
--- a/thirdparty/thorvg/src/lib/tvgFill.h
+++ b/thirdparty/thorvg/src/lib/tvgFill.h
@@ -86,4 +86,27 @@ struct Fill::Impl
}
};
+
+struct RadialGradient::Impl
+{
+ float cx = 0.0f, cy = 0.0f;
+ float fx = 0.0f, fy = 0.0f;
+ float r = 0.0f, fr = 0.0f;
+
+ Fill* duplicate();
+ Result radial(float cx, float cy, float r, float fx, float fy, float fr);
+};
+
+
+struct LinearGradient::Impl
+{
+ float x1 = 0.0f;
+ float y1 = 0.0f;
+ float x2 = 0.0f;
+ float y2 = 0.0f;
+
+ Fill* duplicate();
+};
+
+
#endif //_TVG_FILL_H_
diff --git a/thirdparty/thorvg/src/lib/tvgLinearGradient.cpp b/thirdparty/thorvg/src/lib/tvgLinearGradient.cpp
deleted file mode 100644
index 3e040c08f1..0000000000
--- a/thirdparty/thorvg/src/lib/tvgLinearGradient.cpp
+++ /dev/null
@@ -1,100 +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 <float.h>
-#include <math.h>
-#include "tvgFill.h"
-
-/************************************************************************/
-/* Internal Class Implementation */
-/************************************************************************/
-
-struct LinearGradient::Impl
-{
- float x1 = 0;
- float y1 = 0;
- float x2 = 0;
- float y2 = 0;
-
- Fill* duplicate()
- {
- auto ret = LinearGradient::gen();
- if (!ret) return nullptr;
-
- ret->pImpl->x1 = x1;
- ret->pImpl->y1 = y1;
- ret->pImpl->x2 = x2;
- ret->pImpl->y2 = y2;
-
- return ret.release();
- }
-};
-
-/************************************************************************/
-/* External Class Implementation */
-/************************************************************************/
-
-LinearGradient::LinearGradient():pImpl(new Impl())
-{
- Fill::pImpl->id = TVG_CLASS_ID_LINEAR;
- Fill::pImpl->method(new FillDup<LinearGradient::Impl>(pImpl));
-}
-
-
-LinearGradient::~LinearGradient()
-{
- delete(pImpl);
-}
-
-
-Result LinearGradient::linear(float x1, float y1, float x2, float y2) noexcept
-{
- pImpl->x1 = x1;
- pImpl->y1 = y1;
- pImpl->x2 = x2;
- pImpl->y2 = y2;
-
- return Result::Success;
-}
-
-
-Result LinearGradient::linear(float* x1, float* y1, float* x2, float* y2) const noexcept
-{
- if (x1) *x1 = pImpl->x1;
- if (x2) *x2 = pImpl->x2;
- if (y1) *y1 = pImpl->y1;
- if (y2) *y2 = pImpl->y2;
-
- return Result::Success;
-}
-
-
-unique_ptr<LinearGradient> LinearGradient::gen() noexcept
-{
- return unique_ptr<LinearGradient>(new LinearGradient);
-}
-
-
-uint32_t LinearGradient::identifier() noexcept
-{
- return TVG_CLASS_ID_LINEAR;
-}
diff --git a/thirdparty/thorvg/src/lib/tvgLoader.cpp b/thirdparty/thorvg/src/lib/tvgLoader.cpp
index 16b604c89e..8ed0d5752e 100644
--- a/thirdparty/thorvg/src/lib/tvgLoader.cpp
+++ b/thirdparty/thorvg/src/lib/tvgLoader.cpp
@@ -164,7 +164,7 @@ 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 == "lottie") 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;
@@ -214,22 +214,24 @@ shared_ptr<LoadModule> LoaderMgr::loader(const string& path, bool* invalid)
shared_ptr<LoadModule> LoaderMgr::loader(const char* data, uint32_t size, const string& mimeType, bool copy)
{
- //Try first with the given MimeType
- if (auto loader = _findByType(mimeType)) {
- if (loader->open(data, size, copy)) {
- return shared_ptr<LoadModule>(loader);
- } else {
- TVGLOG("LOADER", "Given mimetype \"%s\" seems incorrect or not supported. Will try again with other types.", mimeType.c_str());
- delete(loader);
- }
- }
-
- //Abnormal MimeType. Try with the candidates in the order
- for (int i = 0; i < static_cast<int>(FileType::Unknown); i++) {
- auto loader = _find(static_cast<FileType>(i));
- if (loader) {
- if (loader->open(data, size, copy)) return shared_ptr<LoadModule>(loader);
- else delete(loader);
+ //Try with the given MimeType
+ if (!mimeType.empty()) {
+ if (auto loader = _findByType(mimeType)) {
+ if (loader->open(data, size, copy)) {
+ return shared_ptr<LoadModule>(loader);
+ } else {
+ TVGLOG("LOADER", "Given mimetype \"%s\" seems incorrect or not supported. Will try again with other types.", mimeType.c_str());
+ delete(loader);
+ }
+ }
+ //Unkown MimeType. Try with the candidates in the order
+ } else {
+ for (int i = 0; i < static_cast<int>(FileType::Unknown); i++) {
+ auto loader = _find(static_cast<FileType>(i));
+ if (loader) {
+ if (loader->open(data, size, copy)) return shared_ptr<LoadModule>(loader);
+ else delete(loader);
+ }
}
}
return nullptr;
diff --git a/thirdparty/thorvg/src/lib/tvgPaint.cpp b/thirdparty/thorvg/src/lib/tvgPaint.cpp
index 57da269cd7..bac5e434a5 100644
--- a/thirdparty/thorvg/src/lib/tvgPaint.cpp
+++ b/thirdparty/thorvg/src/lib/tvgPaint.cpp
@@ -166,7 +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 (MASK_REGION_MERGING(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)) {
@@ -206,23 +206,20 @@ RenderData Paint::Impl::update(RenderMethod& renderer, const RenderTransform* pT
auto method = compData->method;
target->pImpl->ctxFlag &= ~ContextFlag::FastTrack; //reset
- /* If transform has no rotation factors && ClipPath / AlphaMasking is a simple rectangle,
- we can avoid regular ClipPath / AlphaMasking sequence but use viewport for performance */
+ /* If the transformation has no rotational factors and the ClipPath/Alpha(InvAlpha)Masking involves a simple rectangle,
+ we can optimize by using the viewport instead of the regular ClipPath/AlphaMasking sequence for improved performance. */
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) {
+ else {
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;
+ //no gradient fill & no compositions of the composition target.
+ if (!shape->fill() && !(PP(shape)->compData)) {
+ if (method == CompositeMethod::AlphaMask && a == 255 && PP(shape)->opacity == 255) tryFastTrack = true;
+ else if (method == CompositeMethod::InvAlphaMask && (a == 0 || PP(shape)->opacity == 0)) tryFastTrack = true;
+ }
}
if (tryFastTrack) {
RenderRegion viewport2;
@@ -263,12 +260,12 @@ RenderData Paint::Impl::update(RenderMethod& renderer, const RenderTransform* pT
}
-bool Paint::Impl::bounds(float* x, float* y, float* w, float* h, bool transformed)
+bool Paint::Impl::bounds(float* x, float* y, float* w, float* h, bool transformed, bool stroking)
{
Matrix* m = nullptr;
//Case: No transformed, quick return!
- if (!transformed || !(m = this->transform())) return smethod->bounds(x, y, w, h);
+ if (!transformed || !(m = this->transform())) return smethod->bounds(x, y, w, h, stroking);
//Case: Transformed
auto tx = 0.0f;
@@ -276,7 +273,7 @@ bool Paint::Impl::bounds(float* x, float* y, float* w, float* h, bool transforme
auto tw = 0.0f;
auto th = 0.0f;
- auto ret = smethod->bounds(&tx, &ty, &tw, &th);
+ auto ret = smethod->bounds(&tx, &ty, &tw, &th, stroking);
//Get vertices
Point pt[4] = {{tx, ty}, {tx + tw, ty}, {tx + tw, ty + th}, {tx, ty + th}};
@@ -365,7 +362,7 @@ TVG_DEPRECATED Result Paint::bounds(float* x, float* y, float* w, float* h) cons
Result Paint::bounds(float* x, float* y, float* w, float* h, bool transform) const noexcept
{
- if (pImpl->bounds(x, y, w, h, transform)) return Result::Success;
+ if (pImpl->bounds(x, y, w, h, transform, true)) return Result::Success;
return Result::InsufficientCondition;
}
diff --git a/thirdparty/thorvg/src/lib/tvgPaint.h b/thirdparty/thorvg/src/lib/tvgPaint.h
index c00238a070..c020a7dffd 100644
--- a/thirdparty/thorvg/src/lib/tvgPaint.h
+++ b/thirdparty/thorvg/src/lib/tvgPaint.h
@@ -42,10 +42,10 @@ namespace tvg
{
virtual ~StrategyMethod() {}
- virtual bool dispose(RenderMethod& renderer) = 0;
+ virtual bool dispose(RenderMethod& renderer) = 0; //return true if the deletion is allowed.
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 bool bounds(float* x, float* y, float* w, float* h, bool stroking) = 0;
virtual RenderRegion bounds(RenderMethod& renderer) const = 0;
virtual Paint* duplicate() = 0;
virtual Iterator* iterator() = 0;
@@ -68,6 +68,7 @@ namespace tvg
uint8_t ctxFlag = ContextFlag::Invalid;
uint8_t id;
uint8_t opacity = 255;
+ uint8_t refCnt = 1;
~Impl()
{
@@ -79,6 +80,18 @@ namespace tvg
delete(rTransform);
}
+ uint8_t ref()
+ {
+ if (refCnt == 255) TVGERR("RENDERER", "Corrupted reference count!");
+ return (++refCnt);
+ }
+
+ uint8_t unref()
+ {
+ if (refCnt == 0) TVGERR("RENDERER", "Corrupted reference count!");
+ return (--refCnt);
+ }
+
void method(StrategyMethod* method)
{
smethod = method;
@@ -147,7 +160,7 @@ namespace tvg
bool rotate(float degree);
bool scale(float factor);
bool translate(float x, float y);
- bool bounds(float* x, float* y, float* w, float* h, bool transformed);
+ bool bounds(float* x, float* y, float* w, float* h, bool transformed, bool stroking);
RenderData update(RenderMethod& renderer, const RenderTransform* pTransform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper = false);
bool render(RenderMethod& renderer);
Paint* duplicate();
@@ -162,9 +175,9 @@ namespace tvg
PaintMethod(T* _inst) : inst(_inst) {}
~PaintMethod() {}
- bool bounds(float* x, float* y, float* w, float* h) override
+ bool bounds(float* x, float* y, float* w, float* h, bool stroking) override
{
- return inst->bounds(x, y, w, h);
+ return inst->bounds(x, y, w, h, stroking);
}
RenderRegion bounds(RenderMethod& renderer) const override
diff --git a/thirdparty/thorvg/src/lib/tvgPictureImpl.h b/thirdparty/thorvg/src/lib/tvgPictureImpl.h
index d445c72c10..f29b8a1ec3 100644
--- a/thirdparty/thorvg/src/lib/tvgPictureImpl.h
+++ b/thirdparty/thorvg/src/lib/tvgPictureImpl.h
@@ -70,7 +70,6 @@ struct Picture::Impl
Picture* picture = nullptr;
bool resizing = false;
bool needComp = false; //need composition
- bool animated = false; //picture is belonged to Animation
Impl(Picture* p) : picture(p)
{
@@ -84,12 +83,10 @@ struct Picture::Impl
bool dispose(RenderMethod& renderer)
{
- bool ret = true;
- if (paint) ret = paint->pImpl->dispose(renderer);
- else if (surface) ret = renderer.dispose(rd);
+ if (paint) paint->pImpl->dispose(renderer);
+ else if (surface) renderer.dispose(rd);
rd = nullptr;
-
- return ret;
+ return true;
}
RenderUpdateFlag load()
@@ -191,7 +188,7 @@ struct Picture::Impl
return true;
}
- bool bounds(float* x, float* y, float* w, float* h)
+ bool bounds(float* x, float* y, float* w, float* h, bool stroking)
{
if (rm.triangleCnt > 0) {
auto triangles = rm.triangles;
diff --git a/thirdparty/thorvg/src/lib/tvgRadialGradient.cpp b/thirdparty/thorvg/src/lib/tvgRadialGradient.cpp
deleted file mode 100644
index a85f60e5d0..0000000000
--- a/thirdparty/thorvg/src/lib/tvgRadialGradient.cpp
+++ /dev/null
@@ -1,98 +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 <float.h>
-#include "tvgFill.h"
-
-/************************************************************************/
-/* Internal Class Implementation */
-/************************************************************************/
-
-struct RadialGradient::Impl
-{
- float cx = 0;
- float cy = 0;
- float radius = 0;
-
- Fill* duplicate()
- {
- auto ret = RadialGradient::gen();
- if (!ret) return nullptr;
-
- ret->pImpl->cx = cx;
- ret->pImpl->cy = cy;
- ret->pImpl->radius = radius;
-
- return ret.release();
- }
-};
-
-
-/************************************************************************/
-/* External Class Implementation */
-/************************************************************************/
-
-RadialGradient::RadialGradient():pImpl(new Impl())
-{
- Fill::pImpl->id = TVG_CLASS_ID_RADIAL;
- Fill::pImpl->method(new FillDup<RadialGradient::Impl>(pImpl));
-}
-
-
-RadialGradient::~RadialGradient()
-{
- delete(pImpl);
-}
-
-
-Result RadialGradient::radial(float cx, float cy, float radius) noexcept
-{
- if (radius < 0) return Result::InvalidArguments;
-
- pImpl->cx = cx;
- pImpl->cy = cy;
- pImpl->radius = radius;
-
- return Result::Success;
-}
-
-
-Result RadialGradient::radial(float* cx, float* cy, float* radius) const noexcept
-{
- if (cx) *cx = pImpl->cx;
- if (cy) *cy = pImpl->cy;
- if (radius) *radius = pImpl->radius;
-
- return Result::Success;
-}
-
-
-unique_ptr<RadialGradient> RadialGradient::gen() noexcept
-{
- return unique_ptr<RadialGradient>(new RadialGradient);
-}
-
-
-uint32_t RadialGradient::identifier() noexcept
-{
- return TVG_CLASS_ID_RADIAL;
-}
diff --git a/thirdparty/thorvg/src/lib/tvgRender.h b/thirdparty/thorvg/src/lib/tvgRender.h
index 9d7bafb613..1231089ff5 100644
--- a/thirdparty/thorvg/src/lib/tvgRender.h
+++ b/thirdparty/thorvg/src/lib/tvgRender.h
@@ -137,11 +137,17 @@ struct RenderStroke
Fill *fill = nullptr;
float* dashPattern = nullptr;
uint32_t dashCnt = 0;
+ float dashOffset = 0.0f;
StrokeCap cap = StrokeCap::Square;
StrokeJoin join = StrokeJoin::Bevel;
float miterlimit = 4.0f;
bool strokeFirst = false;
+ struct {
+ float begin = 0.0f;
+ float end = 1.0f;
+ } trim;
+
~RenderStroke()
{
free(dashPattern);
@@ -182,6 +188,14 @@ struct RenderShape
return stroke->width;
}
+ bool strokeTrim() const
+ {
+ if (!stroke) return false;
+ if (stroke->trim.begin == 0.0f && stroke->trim.end == 1.0f) return false;
+ if (stroke->trim.begin == 1.0f && stroke->trim.end == 0.0f) return false;
+ return true;
+ }
+
bool strokeColor(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a) const
{
if (!stroke) return false;
@@ -200,10 +214,11 @@ struct RenderShape
return stroke->fill;
}
- uint32_t strokeDash(const float** dashPattern) const
+ uint32_t strokeDash(const float** dashPattern, float* offset) const
{
if (!stroke) return 0;
if (dashPattern) *dashPattern = stroke->dashPattern;
+ if (offset) *offset = stroke->dashOffset;
return stroke->dashCnt;
}
@@ -253,21 +268,22 @@ public:
virtual bool endComposite(Compositor* cmp) = 0;
};
-static inline bool MASK_OPERATION(CompositeMethod method)
+static inline bool MASK_REGION_MERGING(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:
+ return false;
+ //these might expand the rendering region
+ case CompositeMethod::AddMask:
case CompositeMethod::DifferenceMask:
return true;
default:
- TVGERR("COMMON", "Unsupported Composite Size! = %d", (int)method);
+ TVGERR("RENDERER", "Unsupported Composite Method! = %d", (int)method);
return false;
}
}
@@ -284,7 +300,7 @@ static inline uint8_t CHANNEL_SIZE(ColorSpace cs)
return sizeof(uint8_t);
case ColorSpace::Unsupported:
default:
- TVGERR("SW_ENGINE", "Unsupported Channel Size! = %d", (int)cs);
+ TVGERR("RENDERER", "Unsupported Channel Size! = %d", (int)cs);
return 0;
}
}
@@ -294,16 +310,17 @@ static inline ColorSpace COMPOSITE_TO_COLORSPACE(RenderMethod& renderer, Composi
switch(method) {
case CompositeMethod::AlphaMask:
case CompositeMethod::InvAlphaMask:
- return ColorSpace::Grayscale8;
- case CompositeMethod::LumaMask:
- case CompositeMethod::InvLumaMask:
case CompositeMethod::AddMask:
+ case CompositeMethod::DifferenceMask:
case CompositeMethod::SubtractMask:
case CompositeMethod::IntersectMask:
- case CompositeMethod::DifferenceMask:
+ return ColorSpace::Grayscale8;
+ //TODO: Optimize Luma/InvLuma colorspace to Grayscale8
+ case CompositeMethod::LumaMask:
+ case CompositeMethod::InvLumaMask:
return renderer.colorSpace();
default:
- TVGERR("COMMON", "Unsupported Composite Size! = %d", (int)method);
+ TVGERR("RENDERER", "Unsupported Composite Size! = %d", (int)method);
return ColorSpace::Unsupported;
}
}
diff --git a/thirdparty/thorvg/src/lib/tvgSceneImpl.h b/thirdparty/thorvg/src/lib/tvgSceneImpl.h
index 8fa38bcc85..90b1775610 100644
--- a/thirdparty/thorvg/src/lib/tvgSceneImpl.h
+++ b/thirdparty/thorvg/src/lib/tvgSceneImpl.h
@@ -75,7 +75,7 @@ struct Scene::Impl
~Impl()
{
for (auto paint : paints) {
- delete(paint);
+ if (paint->pImpl->unref() == 0) delete(paint);
}
}
@@ -85,11 +85,11 @@ struct Scene::Impl
paint->pImpl->dispose(renderer);
}
- auto ret = renderer.dispose(rd);
+ renderer.dispose(rd);
this->renderer = nullptr;
this->rd = nullptr;
- return ret;
+ return true;
}
bool needComposition(uint8_t opacity)
@@ -181,7 +181,7 @@ struct Scene::Impl
return {x1, y1, (x2 - x1), (y2 - y1)};
}
- bool bounds(float* px, float* py, float* pw, float* ph)
+ bool bounds(float* px, float* py, float* pw, float* ph, bool stroking)
{
if (paints.empty()) return false;
@@ -196,7 +196,7 @@ struct Scene::Impl
auto w = 0.0f;
auto h = 0.0f;
- if (paint->bounds(&x, &y, &w, &h, true) != tvg::Result::Success) continue;
+ if (!P(paint)->bounds(&x, &y, &w, &h, true, stroking)) continue;
//Merge regions
if (x < x1) x1 = x;
@@ -231,7 +231,7 @@ struct Scene::Impl
auto dispose = renderer ? true : false;
for (auto paint : paints) {
- if (dispose) paint->pImpl->dispose(*renderer);
+ if (dispose) free &= paint->pImpl->dispose(*renderer);
if (free) delete(paint);
}
paints.clear();
diff --git a/thirdparty/thorvg/src/lib/tvgShape.cpp b/thirdparty/thorvg/src/lib/tvgShape.cpp
index f32feb2133..336ac71d81 100644
--- a/thirdparty/thorvg/src/lib/tvgShape.cpp
+++ b/thirdparty/thorvg/src/lib/tvgShape.cpp
@@ -150,13 +150,13 @@ Result Shape::appendArc(float cx, float cy, float radius, float startAngle, floa
//just circle
if (sweep >= 360.0f || sweep <= -360.0f) return appendCircle(cx, cy, radius, radius);
- startAngle = (startAngle * M_PI) / 180.0f;
- sweep = sweep * M_PI / 180.0f;
+ startAngle = (startAngle * MATH_PI) / 180.0f;
+ sweep = sweep * MATH_PI / 180.0f;
- auto nCurves = ceil(fabsf(sweep / float(M_PI_2)));
+ auto nCurves = ceil(fabsf(sweep / MATH_PI2));
auto sweepSign = (sweep < 0 ? -1 : 1);
- auto fract = fmodf(sweep, float(M_PI_2));
- fract = (mathZero(fract)) ? float(M_PI_2) * sweepSign : fract;
+ auto fract = fmodf(sweep, MATH_PI2);
+ fract = (mathZero(fract)) ? MATH_PI2 * sweepSign : fract;
//Start from here
Point start = {radius * cosf(startAngle), radius * sinf(startAngle)};
@@ -342,22 +342,13 @@ const Fill* Shape::strokeFill() const noexcept
Result Shape::stroke(const float* dashPattern, uint32_t cnt) noexcept
{
- if ((cnt == 1) || (!dashPattern && cnt > 0) || (dashPattern && cnt == 0)) {
- return Result::InvalidArguments;
- }
-
- for (uint32_t i = 0; i < cnt; i++)
- if (dashPattern[i] < FLT_EPSILON) return Result::InvalidArguments;
-
- if (!pImpl->strokeDash(dashPattern, cnt)) return Result::FailedAllocation;
-
- return Result::Success;
+ return pImpl->strokeDash(dashPattern, cnt, 0);
}
uint32_t Shape::strokeDash(const float** dashPattern) const noexcept
{
- return pImpl->rs.strokeDash(dashPattern);
+ return pImpl->rs.strokeDash(dashPattern, nullptr);
}
diff --git a/thirdparty/thorvg/src/lib/tvgShapeImpl.h b/thirdparty/thorvg/src/lib/tvgShapeImpl.h
index b05f85bf1c..bb266866d0 100644
--- a/thirdparty/thorvg/src/lib/tvgShapeImpl.h
+++ b/thirdparty/thorvg/src/lib/tvgShapeImpl.h
@@ -46,9 +46,9 @@ struct Shape::Impl
bool dispose(RenderMethod& renderer)
{
- auto ret = renderer.dispose(rd);
+ renderer.dispose(rd);
rd = nullptr;
- return ret;
+ return true;
}
bool render(RenderMethod& renderer)
@@ -70,7 +70,7 @@ struct Shape::Impl
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.stroke || rs.stroke->width < FLT_EPSILON || (!rs.stroke->fill && rs.stroke->color[3] == 0)) return false;
if (!rs.fill && rs.color[3] == 0) return false;
//translucent fill & stroke
@@ -104,7 +104,7 @@ struct Shape::Impl
return renderer.region(rd);
}
- bool bounds(float* x, float* y, float* w, float* h)
+ bool bounds(float* x, float* y, float* w, float* h, bool stroking)
{
//Path bounding size
if (rs.path.pts.count > 0 ) {
@@ -126,7 +126,7 @@ struct Shape::Impl
}
//Stroke feathering
- if (rs.stroke) {
+ if (stroking && rs.stroke) {
if (x) *x -= rs.stroke->width * 0.5f;
if (y) *y -= rs.stroke->width * 0.5f;
if (w) *w += rs.stroke->width;
@@ -199,8 +199,6 @@ struct Shape::Impl
bool strokeWidth(float width)
{
- //TODO: Size Exception?
-
if (!rs.stroke) rs.stroke = new RenderStroke();
rs.stroke->width = width;
flag |= RenderUpdateFlag::Stroke;
@@ -208,6 +206,22 @@ struct Shape::Impl
return true;
}
+ bool strokeTrim(float begin, float end)
+ {
+ if (!rs.stroke) {
+ if (begin == 0.0f && end == 1.0f) return true;
+ rs.stroke = new RenderStroke();
+ }
+
+ if (mathEqual(rs.stroke->trim.begin, begin) && mathEqual(rs.stroke->trim.end, end)) return true;
+
+ rs.stroke->trim.begin = begin;
+ rs.stroke->trim.end = end;
+ flag |= RenderUpdateFlag::Stroke;
+
+ return true;
+ }
+
bool strokeCap(StrokeCap cap)
{
if (!rs.stroke) rs.stroke = new RenderStroke();
@@ -269,8 +283,16 @@ struct Shape::Impl
return Result::Success;
}
- bool strokeDash(const float* pattern, uint32_t cnt)
+ Result strokeDash(const float* pattern, uint32_t cnt, float offset)
{
+ if ((cnt == 1) || (!pattern && cnt > 0) || (pattern && cnt == 0)) {
+ return Result::InvalidArguments;
+ }
+
+ for (uint32_t i = 0; i < cnt; i++) {
+ if (pattern[i] < FLT_EPSILON) return Result::InvalidArguments;
+ }
+
//Reset dash
if (!pattern && cnt == 0) {
free(rs.stroke->dashPattern);
@@ -283,16 +305,17 @@ struct Shape::Impl
}
if (!rs.stroke->dashPattern) {
rs.stroke->dashPattern = static_cast<float*>(malloc(sizeof(float) * cnt));
- if (!rs.stroke->dashPattern) return false;
+ if (!rs.stroke->dashPattern) return Result::FailedAllocation;
}
for (uint32_t i = 0; i < cnt; ++i) {
rs.stroke->dashPattern[i] = pattern[i];
}
}
rs.stroke->dashCnt = cnt;
+ rs.stroke->dashOffset = offset;
flag |= RenderUpdateFlag::Stroke;
- return true;
+ return Result::Success;
}
bool strokeFirst()
@@ -336,24 +359,17 @@ struct Shape::Impl
//Stroke
if (rs.stroke) {
dup->rs.stroke = new RenderStroke();
- dup->rs.stroke->width = rs.stroke->width;
- dup->rs.stroke->dashCnt = rs.stroke->dashCnt;
- dup->rs.stroke->cap = rs.stroke->cap;
- dup->rs.stroke->join = rs.stroke->join;
- dup->rs.stroke->strokeFirst = rs.stroke->strokeFirst;
+ *dup->rs.stroke = *rs.stroke;
memcpy(dup->rs.stroke->color, rs.stroke->color, sizeof(rs.stroke->color));
-
if (rs.stroke->dashCnt > 0) {
dup->rs.stroke->dashPattern = static_cast<float*>(malloc(sizeof(float) * rs.stroke->dashCnt));
memcpy(dup->rs.stroke->dashPattern, rs.stroke->dashPattern, sizeof(float) * rs.stroke->dashCnt);
}
-
- dup->flag |= RenderUpdateFlag::Stroke;
-
if (rs.stroke->fill) {
dup->rs.stroke->fill = rs.stroke->fill->duplicate();
dup->flag |= RenderUpdateFlag::GradientStroke;
}
+ dup->flag |= RenderUpdateFlag::Stroke;
}
//Fill
diff --git a/thirdparty/thorvg/src/lib/tvgTaskScheduler.cpp b/thirdparty/thorvg/src/lib/tvgTaskScheduler.cpp
index 019468083d..e3887c60fc 100644
--- a/thirdparty/thorvg/src/lib/tvgTaskScheduler.cpp
+++ b/thirdparty/thorvg/src/lib/tvgTaskScheduler.cpp
@@ -100,6 +100,9 @@ struct TaskQueue {
};
+static thread_local bool _async = true; //toggle async tasking for each thread on/off
+
+
struct TaskSchedulerImpl
{
uint32_t threadCnt;
@@ -109,6 +112,8 @@ struct TaskSchedulerImpl
TaskSchedulerImpl(unsigned threadCnt) : threadCnt(threadCnt), taskQueues(threadCnt)
{
+ threads.reserve(threadCnt);
+
for (unsigned i = 0; i < threadCnt; ++i) {
threads.emplace_back([&, i] { run(i); });
}
@@ -142,7 +147,7 @@ struct TaskSchedulerImpl
void request(Task* task)
{
//Async
- if (threadCnt > 0) {
+ if (threadCnt > 0 && _async) {
task->prepare();
auto i = idx++;
for (unsigned n = 0; n < threadCnt; ++n) {
@@ -190,3 +195,9 @@ unsigned TaskScheduler::threads()
if (inst) return inst->threadCnt;
return 0;
}
+
+
+void TaskScheduler::async(bool on)
+{
+ _async = on;
+}
diff --git a/thirdparty/thorvg/src/lib/tvgTaskScheduler.h b/thirdparty/thorvg/src/lib/tvgTaskScheduler.h
index 7ada963b77..2dad80f5d0 100644
--- a/thirdparty/thorvg/src/lib/tvgTaskScheduler.h
+++ b/thirdparty/thorvg/src/lib/tvgTaskScheduler.h
@@ -38,6 +38,7 @@ struct TaskScheduler
static void init(unsigned threads);
static void term();
static void request(Task* task);
+ static void async(bool on);
};
struct Task
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.cpp b/thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.cpp
index 6fdb641204..c3c477a263 100644
--- a/thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.cpp
+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.cpp
@@ -123,7 +123,7 @@ static void _copyStyle(SvgStyleProperty* to, const SvgStyleProperty* from)
to->stroke.dash.array.clear();
to->stroke.dash.array.reserve(from->stroke.dash.array.count);
for (uint32_t i = 0; i < from->stroke.dash.array.count; ++i) {
- to->stroke.dash.array.push(from->stroke.dash.array.data[i]);
+ to->stroke.dash.array.push(from->stroke.dash.array[i]);
}
to->stroke.flags = (to->stroke.flags | SvgStrokeFlags::Dash);
to->flags = (to->flags | SvgStyleFlags::StrokeDashArray);
@@ -236,7 +236,7 @@ void cssUpdateStyle(SvgNode* doc, SvgNode* style)
void cssApplyStyleToPostponeds(Array<SvgNodeIdPair>& postponeds, SvgNode* style)
{
for (uint32_t i = 0; i < postponeds.count; ++i) {
- auto nodeIdPair = postponeds.data[i];
+ auto nodeIdPair = postponeds[i];
//css styling: tag.name has higher priority than .name
if (auto cssNode = cssFindStyleNode(style, nodeIdPair.id, nodeIdPair.node->type)) {
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp
index 998c98e83f..9825fd8cc4 100644
--- a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp
+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp
@@ -59,7 +59,7 @@
#include "tvgXmlParser.h"
#include "tvgSvgLoader.h"
#include "tvgSvgSceneBuilder.h"
-#include "tvgSvgUtil.h"
+#include "tvgStr.h"
#include "tvgSvgCssStyle.h"
#include "tvgMath.h"
@@ -110,7 +110,7 @@ static bool _parseNumber(const char** content, float* number)
{
char* end = nullptr;
- *number = svgUtilStrtof(*content, &end);
+ *number = strToFloat(*content, &end);
//If the start of string is not number
if ((*content) == end) return false;
//Skip comma if any
@@ -166,7 +166,7 @@ static void _parseAspectRatio(const char** content, AspectRatioAlign* align, Asp
*/
static float _toFloat(const SvgParser* svgParse, const char* str, SvgParserLengthType type)
{
- float parsedValue = svgUtilStrtof(str, nullptr);
+ float parsedValue = strToFloat(str, nullptr);
if (strstr(str, "cm")) parsedValue *= PX_PER_CM;
else if (strstr(str, "mm")) parsedValue *= PX_PER_MM;
@@ -194,7 +194,7 @@ static float _gradientToFloat(const SvgParser* svgParse, const char* str, bool&
{
char* end = nullptr;
- float parsedValue = svgUtilStrtof(str, &end);
+ float parsedValue = strToFloat(str, &end);
isPercentage = false;
if (strstr(str, "%")) {
@@ -217,7 +217,7 @@ static float _toOffset(const char* str)
char* end = nullptr;
auto strEnd = str + strlen(str);
- float parsedValue = svgUtilStrtof(str, &end);
+ float parsedValue = strToFloat(str, &end);
end = _skipSpace(end, nullptr);
auto ptr = strstr(str, "%");
@@ -234,7 +234,7 @@ static float _toOffset(const char* str)
static int _toOpacity(const char* str)
{
char* end = nullptr;
- float opacity = svgUtilStrtof(str, &end);
+ float opacity = strToFloat(str, &end);
if (end) {
if (end[0] == '%' && end[1] == '\0') return lrint(opacity * 2.55f);
@@ -362,7 +362,7 @@ static void _parseDashArray(SvgLoaderData* loader, const char *str, SvgDash* das
while (*str) {
str = _skipComma(str);
- float parsedValue = svgUtilStrtof(str, &end);
+ float parsedValue = strToFloat(str, &end);
if (str == end) break;
if (parsedValue <= 0.0f) break;
if (*end == '%') {
@@ -375,7 +375,7 @@ static void _parseDashArray(SvgLoaderData* loader, const char *str, SvgDash* das
str = end;
}
//If dash array size is 1, it means that dash and gap size are the same.
- if ((*dash).array.count == 1) (*dash).array.push((*dash).array.data[0]);
+ if ((*dash).array.count == 1) (*dash).array.push((*dash).array[0]);
}
@@ -393,7 +393,7 @@ static char* _idFromUrl(const char* url)
int i = 0;
while (url[i] > ' ' && url[i] != ')' && url[i] != '\'') ++i;
- return svgUtilStrndup(url, i);
+ return strDuplicate(url, i);
}
@@ -401,7 +401,7 @@ static unsigned char _parseColor(const char* value, char** end)
{
float r;
- r = svgUtilStrtof(value, end);
+ r = strToFloat(value, end);
*end = _skipSpace(*end, nullptr);
if (**end == '%') {
r = 255 * r / 100;
@@ -643,7 +643,7 @@ static char* _parseNumbersArray(char* str, float* points, int* ptCount, int len)
str = _skipSpace(str, nullptr);
while ((count < len) && (isdigit(*str) || *str == '-' || *str == '+' || *str == '.')) {
- points[count++] = svgUtilStrtof(str, &end);
+ points[count++] = strToFloat(str, &end);
str = end;
str = _skipSpace(str, nullptr);
if (*str == ',') ++str;
@@ -893,7 +893,7 @@ static bool _attrParseSvgNode(void* data, const char* key, const char* value)
} else if (!strcmp(key, "style")) {
return simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader);
#ifdef THORVG_LOG_ENABLED
- } else if ((!strcmp(key, "x") || !strcmp(key, "y")) && fabsf(svgUtilStrtof(value, nullptr)) > FLT_EPSILON) {
+ } else if ((!strcmp(key, "x") || !strcmp(key, "y")) && fabsf(strToFloat(value, nullptr)) > FLT_EPSILON) {
TVGLOG("SVG", "Unsupported attributes used [Elements type: Svg][Attribute: %s][Value: %s]", key, value);
#endif
} else {
@@ -956,6 +956,12 @@ static void _handleStrokeDashArrayAttr(SvgLoaderData* loader, SvgNode* node, con
_parseDashArray(loader, value, &node->style->stroke.dash);
}
+static void _handleStrokeDashOffsetAttr(SvgLoaderData* loader, SvgNode* node, const char* value)
+{
+ node->style->stroke.flags = (node->style->stroke.flags | SvgStrokeFlags::DashOffset);
+ node->style->stroke.dash.offset = _toFloat(loader->svgParse, value, SvgParserLengthType::Horizontal);
+}
+
static void _handleStrokeWidthAttr(SvgLoaderData* loader, SvgNode* node, const char* value)
{
node->style->stroke.flags = (node->style->stroke.flags | SvgStrokeFlags::Width);
@@ -979,7 +985,7 @@ static void _handleStrokeLineJoinAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode*
static void _handleStrokeMiterlimitAttr(SvgLoaderData* loader, SvgNode* node, const char* value)
{
char* end = nullptr;
- const float miterlimit = svgUtilStrtof(value, &end);
+ const float miterlimit = strToFloat(value, &end);
// https://www.w3.org/TR/SVG2/painting.html#LineJoin
// - A negative value for stroke-miterlimit must be treated as an illegal value.
@@ -1112,6 +1118,7 @@ static constexpr struct
STYLE_DEF(stroke-linecap, StrokeLineCap, SvgStyleFlags::StrokeLineCap),
STYLE_DEF(stroke-opacity, StrokeOpacity, SvgStyleFlags::StrokeOpacity),
STYLE_DEF(stroke-dasharray, StrokeDashArray, SvgStyleFlags::StrokeDashArray),
+ STYLE_DEF(stroke-dashoffset, StrokeDashOffset, SvgStyleFlags::StrokeDashOffset),
STYLE_DEF(transform, Transform, SvgStyleFlags::Transform),
STYLE_DEF(clip-path, ClipPath, SvgStyleFlags::ClipPath),
STYLE_DEF(mask, Mask, SvgStyleFlags::Mask),
@@ -1141,7 +1148,7 @@ static bool _parseStyleAttr(void* data, const char* key, const char* value, bool
while (size > 0 && isspace(value[size - 1])) {
size--;
}
- value = svgUtilStrndup(value, size);
+ value = strDuplicate(value, size);
importance = true;
}
if (style) {
@@ -2097,6 +2104,12 @@ static void _handleRadialFyAttr(SvgLoaderData* loader, SvgRadialGradient* radial
}
+static void _handleRadialFrAttr(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value)
+{
+ radial->fr = _gradientToFloat(loader->svgParse, value, radial->isFrPercentage);
+}
+
+
static void _handleRadialRAttr(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value)
{
radial->r = _gradientToFloat(loader->svgParse, value, radial->isRPercentage);
@@ -2127,6 +2140,13 @@ static void _recalcRadialFyAttr(SvgLoaderData* loader, SvgRadialGradient* radial
}
+static void _recalcRadialFrAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
+{
+ // scaling factor based on the Units paragraph from : https://www.w3.org/TR/2015/WD-SVG2-20150915/coords.html
+ if (userSpace && !radial->isFrPercentage) radial->fr = radial->fr / (sqrtf(powf(loader->svgParse->global.h, 2) + powf(loader->svgParse->global.w, 2)) / sqrtf(2.0));
+}
+
+
static void _recalcRadialRAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
{
// scaling factor based on the Units paragraph from : https://www.w3.org/TR/2015/WD-SVG2-20150915/coords.html
@@ -2170,6 +2190,15 @@ static void _recalcInheritedRadialFyAttr(SvgLoaderData* loader, SvgRadialGradien
}
+static void _recalcInheritedRadialFrAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
+{
+ if (!radial->isFrPercentage) {
+ if (userSpace) radial->fr /= sqrtf(powf(loader->svgParse->global.h, 2) + powf(loader->svgParse->global.w, 2)) / sqrtf(2.0);
+ else radial->fr *= sqrtf(powf(loader->svgParse->global.h, 2) + powf(loader->svgParse->global.w, 2)) / sqrtf(2.0);
+ }
+}
+
+
static void _recalcInheritedRadialRAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
{
if (!radial->isRPercentage) {
@@ -2211,6 +2240,14 @@ static void _inheritRadialFyAttr(SvgStyleGradient* to, SvgStyleGradient* from)
}
+static void _inheritRadialFrAttr(SvgStyleGradient* to, SvgStyleGradient* from)
+{
+ to->radial->fr = from->radial->fr;
+ to->radial->isFrPercentage = from->radial->isFrPercentage;
+ to->flags = (to->flags | SvgGradientFlags::Fr);
+}
+
+
static void _inheritRadialRAttr(SvgStyleGradient* to, SvgStyleGradient* from)
{
to->radial->r = from->radial->r;
@@ -2244,7 +2281,8 @@ static constexpr struct
RADIAL_DEF(cy, Cy, SvgGradientFlags::Cy),
RADIAL_DEF(fx, Fx, SvgGradientFlags::Fx),
RADIAL_DEF(fy, Fy, SvgGradientFlags::Fy),
- RADIAL_DEF(r, R, SvgGradientFlags::R)
+ RADIAL_DEF(r, R, SvgGradientFlags::R),
+ RADIAL_DEF(fr, Fr, SvgGradientFlags::Fr)
};
@@ -2312,6 +2350,7 @@ static SvgStyleGradient* _createRadialGradient(SvgLoaderData* loader, const char
grad->radial->isFxPercentage = true;
grad->radial->isFyPercentage = true;
grad->radial->isRPercentage = true;
+ grad->radial->isFrPercentage = true;
loader->svgParse->gradient.parsedFx = false;
loader->svgParse->gradient.parsedFy = false;
@@ -2619,7 +2658,7 @@ static GradientFactoryMethod _findGradientFactory(const char* name)
static void _cloneGradStops(Array<Fill::ColorStop>& dst, const Array<Fill::ColorStop>& src)
{
for (uint32_t i = 0; i < src.count; ++i) {
- dst.push(src.data[i]);
+ dst.push(src[i]);
}
}
@@ -2773,10 +2812,13 @@ static void _styleInherit(SvgStyleProperty* child, const SvgStyleProperty* paren
child->stroke.dash.array.clear();
child->stroke.dash.array.reserve(parent->stroke.dash.array.count);
for (uint32_t i = 0; i < parent->stroke.dash.array.count; ++i) {
- child->stroke.dash.array.push(parent->stroke.dash.array.data[i]);
+ child->stroke.dash.array.push(parent->stroke.dash.array[i]);
}
}
}
+ if (!(child->stroke.flags & SvgStrokeFlags::DashOffset)) {
+ child->stroke.dash.offset = parent->stroke.dash.offset;
+ }
if (!(child->stroke.flags & SvgStrokeFlags::Cap)) {
child->stroke.cap = parent->stroke.cap;
}
@@ -2839,17 +2881,19 @@ static void _styleCopy(SvgStyleProperty* to, const SvgStyleProperty* from)
to->stroke.dash.array.clear();
to->stroke.dash.array.reserve(from->stroke.dash.array.count);
for (uint32_t i = 0; i < from->stroke.dash.array.count; ++i) {
- to->stroke.dash.array.push(from->stroke.dash.array.data[i]);
+ to->stroke.dash.array.push(from->stroke.dash.array[i]);
}
}
}
+ if (from->stroke.flags & SvgStrokeFlags::DashOffset) {
+ to->stroke.dash.offset = from->stroke.dash.offset;
+ }
if (from->stroke.flags & SvgStrokeFlags::Cap) {
to->stroke.cap = from->stroke.cap;
}
if (from->stroke.flags & SvgStrokeFlags::Join) {
to->stroke.join = from->stroke.join;
}
-
if (from->stroke.flags & SvgStrokeFlags::Miterlimit) {
to->stroke.miterlimit = from->stroke.miterlimit;
}
@@ -2983,7 +3027,7 @@ static void _cloneNode(SvgNode* from, SvgNode* parent, int depth)
static void _clonePostponedNodes(Array<SvgNodeIdPair>* cloneNodes, SvgNode* doc)
{
for (uint32_t i = 0; i < cloneNodes->count; ++i) {
- auto nodeIdPair = cloneNodes->data[i];
+ auto nodeIdPair = (*cloneNodes)[i];
auto defs = _getDefsNode(nodeIdPair.node);
auto nodeFrom = _findNodeById(defs, nodeIdPair.id);
if (!nodeFrom) nodeFrom = _findNodeById(doc, nodeIdPair.id);
@@ -3064,7 +3108,7 @@ static void _svgLoaderParserXmlOpen(SvgLoaderData* loader, const char* content,
loader->doc = node;
} else {
if (!strcmp(tagName, "svg")) return; //Already loaded <svg>(SvgNodeType::Doc) tag
- if (loader->stack.count > 0) parent = loader->stack.data[loader->stack.count - 1];
+ if (loader->stack.count > 0) parent = loader->stack.last();
else parent = loader->doc;
if (!strcmp(tagName, "style")) {
// TODO: For now only the first style node is saved. After the css id selector
@@ -3085,7 +3129,7 @@ static void _svgLoaderParserXmlOpen(SvgLoaderData* loader, const char* content,
loader->stack.push(node);
}
} else if ((method = _findGraphicsFactory(tagName))) {
- if (loader->stack.count > 0) parent = loader->stack.data[loader->stack.count - 1];
+ if (loader->stack.count > 0) parent = loader->stack.last();
else parent = loader->doc;
node = method(loader, parent, attrs, attrsLength, simpleXmlParseAttributes);
} else if ((gradientMethod = _findGradientFactory(tagName))) {
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h
index 961438ff42..1809c7749c 100644
--- a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h
+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h
@@ -100,7 +100,8 @@ enum class SvgStrokeFlags
Cap = 0x20,
Join = 0x40,
Dash = 0x80,
- Miterlimit = 0x100
+ Miterlimit = 0x100,
+ DashOffset = 0x200
};
constexpr bool operator &(SvgStrokeFlags a, SvgStrokeFlags b)
@@ -139,7 +140,8 @@ enum class SvgStyleFlags
MaskType = 0x4000,
Display = 0x8000,
PaintOrder = 0x10000,
- StrokeMiterlimit = 0x20000
+ StrokeMiterlimit = 0x20000,
+ StrokeDashOffset = 0x40000,
};
constexpr bool operator &(SvgStyleFlags a, SvgStyleFlags b)
@@ -182,7 +184,8 @@ enum class SvgGradientFlags
Cy = 0x80,
R = 0x100,
Fx = 0x200,
- Fy = 0x400
+ Fy = 0x400,
+ Fr = 0x800
};
constexpr bool operator &(SvgGradientFlags a, SvgGradientFlags b)
@@ -390,11 +393,13 @@ struct SvgRadialGradient
float fx;
float fy;
float r;
+ float fr;
bool isCxPercentage;
bool isCyPercentage;
bool isFxPercentage;
bool isFyPercentage;
bool isRPercentage;
+ bool isFrPercentage;
};
struct SvgComposite
@@ -423,6 +428,7 @@ struct SvgPaint
struct SvgDash
{
Array<float> array;
+ float offset;
};
struct SvgStyleGradient
@@ -469,7 +475,6 @@ struct SvgStyleStroke
StrokeJoin join;
float miterlimit;
SvgDash dash;
- int dashCount;
};
struct SvgStyleProperty
@@ -561,18 +566,4 @@ struct Box
float x, y, w, h;
};
-/*
- * https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/strtof-strtod-l-wcstod-wcstod-l?view=vs-2017
- *
- * src should be one of the following form :
- *
- * [whitespace] [sign] {digits [radix digits] | radix digits} [{e | E} [sign] digits]
- * [whitespace] [sign] {INF | INFINITY}
- * [whitespace] [sign] NAN [sequence]
- *
- * No hexadecimal form supported
- * no sequence supported after NAN
- */
-float customStrtof(const char *nptr, char **endptr);
-
#endif
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.cpp b/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.cpp
index e044931b51..79a9c0771d 100644
--- a/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.cpp
+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.cpp
@@ -55,7 +55,7 @@
#include <ctype.h>
#include "tvgSvgLoaderCommon.h"
#include "tvgSvgPath.h"
-#include "tvgSvgUtil.h"
+#include "tvgStr.h"
/************************************************************************/
/* Internal Class Implementation */
@@ -74,7 +74,7 @@ static char* _skipComma(const char* content)
static bool _parseNumber(char** content, float* number)
{
char* end = NULL;
- *number = svgUtilStrtof(*content, &end);
+ *number = strToFloat(*content, &end);
//If the start of string is not number
if ((*content) == end) return false;
//Skip comma if any
@@ -382,7 +382,7 @@ static bool _processCommand(Array<PathCommand>* cmds, Array<Point>* pts, char cm
case 's':
case 'S': {
Point p[3], ctrl;
- if ((cmds->count > 1) && (cmds->data[cmds->count - 1] == PathCommand::CubicTo) &&
+ if ((cmds->count > 1) && (cmds->last() == PathCommand::CubicTo) &&
!(*isQuadratic)) {
ctrl.x = 2 * cur->x - curCtl->x;
ctrl.y = 2 * cur->y - curCtl->y;
@@ -423,7 +423,7 @@ static bool _processCommand(Array<PathCommand>* cmds, Array<Point>* pts, char cm
case 't':
case 'T': {
Point p[3], ctrl;
- if ((cmds->count > 1) && (cmds->data[cmds->count - 1] == PathCommand::CubicTo) &&
+ if ((cmds->count > 1) && (cmds->last() == PathCommand::CubicTo) &&
*isQuadratic) {
ctrl.x = 2 * cur->x - curCtl->x;
ctrl.y = 2 * cur->y - curCtl->y;
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp b/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp
index e5beef8093..71712442e8 100644
--- a/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp
+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp
@@ -52,6 +52,11 @@
#include "tvgMath.h" /* to include math.h before cstring */
#include <cstring>
#include <string>
+#include "tvgShapeImpl.h"
+#include "tvgCompressor.h"
+#include "tvgPaint.h"
+#include "tvgFill.h"
+#include "tvgStr.h"
#include "tvgSvgLoaderCommon.h"
#include "tvgSvgSceneBuilder.h"
#include "tvgSvgPath.h"
@@ -62,6 +67,7 @@
/************************************************************************/
static bool _appendShape(SvgNode* node, Shape* shape, const Box& vBox, const string& svgPath);
+static bool _appendClipShape(SvgNode* node, Shape* shape, const Box& vBox, const string& svgPath, const Matrix* transform);
static unique_ptr<Scene> _sceneBuildHelper(const SvgNode* node, const Box& vBox, const string& svgPath, bool mask, int depth, bool* isMaskWhite = nullptr);
@@ -138,7 +144,7 @@ static unique_ptr<LinearGradient> _applyLinearGradientProperty(SvgStyleGradient*
if (!stops) return fillGrad;
auto prevOffset = 0.0f;
for (uint32_t i = 0; i < g->stops.count; ++i) {
- auto colorStop = &g->stops.data[i];
+ auto colorStop = &g->stops[i];
//Use premultiplied color
stops[i].r = colorStop->r;
stops[i].g = colorStop->g;
@@ -175,6 +181,7 @@ static unique_ptr<RadialGradient> _applyRadialGradientProperty(SvgStyleGradient*
g->radial->r = g->radial->r * sqrtf(powf(vBox.w, 2.0f) + powf(vBox.h, 2.0f)) / sqrtf(2.0f);
g->radial->fx = g->radial->fx * vBox.w;
g->radial->fy = g->radial->fy * vBox.h;
+ g->radial->fr = g->radial->fr * sqrtf(powf(vBox.w, 2.0f) + powf(vBox.h, 2.0f)) / sqrtf(2.0f);
} else {
Matrix m = {vBox.w, 0, vBox.x, 0, vBox.h, vBox.y, 0, 0, 1};
if (isTransform) _transformMultiply(&m, &finalTransform);
@@ -186,11 +193,7 @@ static unique_ptr<RadialGradient> _applyRadialGradientProperty(SvgStyleGradient*
if (isTransform) fillGrad->transform(finalTransform);
- //TODO: Tvg is not support to focal
- //if (g->radial->fx != 0 && g->radial->fy != 0) {
- // fillGrad->radial(g->radial->fx, g->radial->fy, g->radial->r);
- //}
- fillGrad->radial(g->radial->cx, g->radial->cy, g->radial->r);
+ P(fillGrad)->radial(g->radial->cx, g->radial->cy, g->radial->r, g->radial->fx, g->radial->fy, g->radial->fr);
fillGrad->spread(g->spread);
//Update the stops
@@ -200,7 +203,7 @@ static unique_ptr<RadialGradient> _applyRadialGradientProperty(SvgStyleGradient*
if (!stops) return fillGrad;
auto prevOffset = 0.0f;
for (uint32_t i = 0; i < g->stops.count; ++i) {
- auto colorStop = &g->stops.data[i];
+ auto colorStop = &g->stops[i];
//Use premultiplied color
stops[i].r = colorStop->r;
stops[i].g = colorStop->g;
@@ -219,20 +222,50 @@ static unique_ptr<RadialGradient> _applyRadialGradientProperty(SvgStyleGradient*
}
-static bool _appendChildShape(SvgNode* node, Shape* shape, const Box& vBox, const string& svgPath)
+//The SVG standard allows only for 'use' nodes that point directly to a basic shape.
+static bool _appendClipUseNode(SvgNode* node, Shape* shape, const Box& vBox, const string& svgPath)
{
- auto valid = false;
+ if (node->child.count != 1) return false;
+ auto child = *(node->child.data);
- if (_appendShape(node, shape, vBox, svgPath)) valid = true;
+ Matrix finalTransform = {1, 0, 0, 0, 1, 0, 0, 0, 1};
+ if (node->transform) finalTransform = *node->transform;
+ if (node->node.use.x != 0.0f || node->node.use.y != 0.0f) {
+ Matrix m = {1, 0, node->node.use.x, 0, 1, node->node.use.y, 0, 0, 1};
+ finalTransform = mathMultiply(&finalTransform, &m);
+ }
+ if (child->transform) finalTransform = mathMultiply(child->transform, &finalTransform);
- if (node->child.count > 0) {
- auto child = node->child.data;
- for (uint32_t i = 0; i < node->child.count; ++i, ++child) {
- if (_appendChildShape(*child, shape, vBox, svgPath)) valid = true;
- }
+ return _appendClipShape(child, shape, vBox, svgPath, mathIdentity((const Matrix*)(&finalTransform)) ? nullptr : &finalTransform);
+}
+
+
+static bool _appendClipChild(SvgNode* node, Shape* shape, const Box& vBox, const string& svgPath, bool clip)
+{
+ if (node->type == SvgNodeType::Use) {
+ return _appendClipUseNode(node, shape, vBox, svgPath);
}
+ return _appendClipShape(node, shape, vBox, svgPath, nullptr);
+}
- return valid;
+
+static Matrix _compositionTransform(Paint* paint, const SvgNode* node, const SvgNode* compNode, SvgNodeType type)
+{
+ Matrix m = {1, 0, 0, 0, 1, 0, 0, 0, 1};
+ //The initial mask transformation ignored according to the SVG standard.
+ if (node->transform && type != SvgNodeType::Mask) {
+ m = *node->transform;
+ }
+ if (compNode->transform) {
+ m = mathMultiply(&m, compNode->transform);
+ }
+ if (!compNode->node.clip.userSpace) {
+ float x, y, w, h;
+ P(paint)->bounds(&x, &y, &w, &h, false, false);
+ Matrix mBBox = {w, 0, x, 0, h, y, 0, 0, 1};
+ m = mathMultiply(&m, &mBBox);
+ }
+ return m;
}
@@ -251,19 +284,18 @@ static void _applyComposition(Paint* paint, const SvgNode* node, const Box& vBox
auto comp = Shape::gen();
auto child = compNode->child.data;
- auto valid = false; //Composite only when valid shapes are existed
+ auto valid = false; //Composite only when valid shapes exist
for (uint32_t i = 0; i < compNode->child.count; ++i, ++child) {
- if (_appendChildShape(*child, comp.get(), vBox, svgPath)) valid = true;
+ if (_appendClipChild(*child, comp.get(), vBox, svgPath, compNode->child.count > 1)) valid = true;
}
- if (node->transform) {
- auto m = comp->transform();
- m = mathMultiply(node->transform, &m);
- comp->transform(m);
- }
+ if (valid) {
+ Matrix finalTransform = _compositionTransform(paint, node, compNode, SvgNodeType::ClipPath);
+ comp->transform(finalTransform);
- if (valid) paint->composite(std::move(comp), CompositeMethod::ClipPath);
+ paint->composite(std::move(comp), CompositeMethod::ClipPath);
+ }
node->style->clipPath.applying = false;
}
@@ -280,9 +312,9 @@ static void _applyComposition(Paint* paint, const SvgNode* node, const Box& vBox
node->style->mask.applying = true;
bool isMaskWhite = true;
- auto comp = _sceneBuildHelper(compNode, vBox, svgPath, true, 0, &isMaskWhite);
- if (comp) {
- if (node->transform) comp->transform(*node->transform);
+ if (auto comp = _sceneBuildHelper(compNode, vBox, svgPath, true, 0, &isMaskWhite)) {
+ Matrix finalTransform = _compositionTransform(paint, node, compNode, SvgNodeType::Mask);
+ comp->transform(finalTransform);
if (compNode->node.mask.type == SvgMaskType::Luminance && !isMaskWhite) {
paint->composite(std::move(comp), CompositeMethod::LumaMask);
@@ -297,11 +329,12 @@ static void _applyComposition(Paint* paint, const SvgNode* node, const Box& vBox
}
-static void _applyProperty(SvgNode* node, Shape* vg, const Box& vBox, const string& svgPath)
+static void _applyProperty(SvgNode* node, Shape* vg, const Box& vBox, const string& svgPath, bool clip)
{
SvgStyleProperty* style = node->style;
- if (node->transform) vg->transform(*node->transform);
+ //Clip transformation is applied directly to the path in the _appendClipShape function
+ if (node->transform && !clip) vg->transform(*node->transform);
if (node->type == SvgNodeType::Doc || !node->display) return;
//If fill property is nullptr then do nothing
@@ -344,7 +377,7 @@ static void _applyProperty(SvgNode* node, Shape* vg, const Box& vBox, const stri
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);
+ P(vg)->strokeDash(style->stroke.dash.array.data, style->stroke.dash.array.count, style->stroke.dash.offset);
}
//If stroke property is nullptr then do nothing
@@ -383,14 +416,13 @@ static unique_ptr<Shape> _shapeBuildHelper(SvgNode* node, const Box& vBox, const
}
-static bool _appendShape(SvgNode* node, Shape* shape, const Box& vBox, const string& svgPath)
+static bool _recognizeShape(SvgNode* node, Shape* shape)
{
- Array<PathCommand> cmds;
- Array<Point> pts;
-
switch (node->type) {
case SvgNodeType::Path: {
if (node->node.path.path) {
+ Array<PathCommand> cmds;
+ Array<Point> pts;
if (svgPathToTvgPath(node->node.path.path, cmds, pts)) {
shape->appendPath(cmds.data, cmds.count, pts.data, pts.count);
}
@@ -437,8 +469,41 @@ static bool _appendShape(SvgNode* node, Shape* shape, const Box& vBox, const str
return false;
}
}
+ return true;
+}
+
- _applyProperty(node, shape, vBox, svgPath);
+static bool _appendShape(SvgNode* node, Shape* shape, const Box& vBox, const string& svgPath)
+{
+ if (!_recognizeShape(node, shape)) return false;
+
+ _applyProperty(node, shape, vBox, svgPath, false);
+ return true;
+}
+
+
+static bool _appendClipShape(SvgNode* node, Shape* shape, const Box& vBox, const string& svgPath, const Matrix* transform)
+{
+ //The 'transform' matrix has higher priority than the node->transform, since it already contains it
+ auto m = transform ? transform : (node->transform ? node->transform : nullptr);
+
+ uint32_t currentPtsCnt = 0;
+ if (m) {
+ const Point *tmp = nullptr;
+ currentPtsCnt = shape->pathCoords(&tmp);
+ }
+
+ if (!_recognizeShape(node, shape)) return false;
+
+ if (m) {
+ const Point *pts = nullptr;
+ auto ptsCnt = shape->pathCoords(&pts);
+
+ auto p = const_cast<Point*>(pts) + currentPtsCnt;
+ while (currentPtsCnt++ < ptsCnt) mathMultiply(p++, m);
+ }
+
+ _applyProperty(node, shape, vBox, svgPath, true);
return true;
}
@@ -514,12 +579,15 @@ static bool _isValidImageMimeTypeAndEncoding(const char** href, const char** mim
return false;
}
+#include "tvgTaskScheduler.h"
static unique_ptr<Picture> _imageBuildHelper(SvgNode* node, const Box& vBox, const string& svgPath)
{
if (!node->node.image.href) return nullptr;
auto picture = Picture::gen();
+ TaskScheduler::async(false); //force to load a picture on the same thread
+
const char* href = node->node.image.href;
if (!strncmp(href, "data:", sizeof("data:") - 1)) {
href += sizeof("data:") - 1;
@@ -527,11 +595,22 @@ static unique_ptr<Picture> _imageBuildHelper(SvgNode* node, const Box& vBox, con
imageMimeTypeEncoding encoding;
if (!_isValidImageMimeTypeAndEncoding(&href, &mimetype, &encoding)) return nullptr; //not allowed mime type or encoding
if (encoding == imageMimeTypeEncoding::base64) {
- string decoded = svgUtilBase64Decode(href);
- if (picture->load(decoded.c_str(), decoded.size(), mimetype, true) != Result::Success) return nullptr;
+ char* decoded = nullptr;
+ auto size = b64Decode(href, strlen(href), &decoded);
+ //OPTIMIZE: Skip data copy.
+ if (picture->load(decoded, size, mimetype, true) != Result::Success) {
+ free(decoded);
+ TaskScheduler::async(true);
+ return nullptr;
+ }
+ free(decoded);
} else {
string decoded = svgUtilURLDecode(href);
- if (picture->load(decoded.c_str(), decoded.size(), mimetype, true) != Result::Success) return nullptr;
+ //OPTIMIZE: Skip data copy.
+ if (picture->load(decoded.c_str(), decoded.size(), mimetype, true) != Result::Success) {
+ TaskScheduler::async(true);
+ return nullptr;
+ }
}
} else {
if (!strncmp(href, "file://", sizeof("file://") - 1)) href += sizeof("file://") - 1;
@@ -540,6 +619,7 @@ static unique_ptr<Picture> _imageBuildHelper(SvgNode* node, const Box& vBox, con
const char *dot = strrchr(href, '.');
if (dot && !strcmp(dot, ".svg")) {
TVGLOG("SVG", "Embedded svg file is disabled.");
+ TaskScheduler::async(true);
return nullptr;
}
string imagePath = href;
@@ -547,9 +627,14 @@ static unique_ptr<Picture> _imageBuildHelper(SvgNode* node, const Box& vBox, con
auto last = svgPath.find_last_of("/");
imagePath = svgPath.substr(0, (last == string::npos ? 0 : last + 1)) + imagePath;
}
- if (picture->load(imagePath) != Result::Success) return nullptr;
+ if (picture->load(imagePath) != Result::Success) {
+ TaskScheduler::async(true);
+ return nullptr;
+ }
}
+ TaskScheduler::async(true);
+
float w, h;
Matrix m = {1, 0, 0, 0, 1, 0, 0, 0, 1};
if (picture->size(&w, &h) == Result::Success && w > 0 && h > 0) {
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.cpp b/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.cpp
index 2aaeb2b25d..763a357f99 100644
--- a/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.cpp
+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.cpp
@@ -21,19 +21,12 @@
*/
#include <cstring>
-#include <math.h>
-#include <memory.h>
#include "tvgSvgUtil.h"
/************************************************************************/
/* Internal Class Implementation */
/************************************************************************/
-static inline bool _floatExact(float a, float b)
-{
- return memcmp(&a, &b, sizeof(float)) == 0;
-}
-
static uint8_t _hexCharToDec(const char c)
{
if (c >= 'a') return c - 'a' + 10;
@@ -41,181 +34,11 @@ static uint8_t _hexCharToDec(const char c)
else return c - '0';
}
-static uint8_t _base64Value(const char chr)
-{
- if (chr >= 'A' && chr <= 'Z') return chr - 'A';
- else if (chr >= 'a' && chr <= 'z') return chr - 'a' + ('Z' - 'A') + 1;
- else if (chr >= '0' && chr <= '9') return chr - '0' + ('Z' - 'A') + ('z' - 'a') + 2;
- else if (chr == '+' || chr == '-') return 62;
- else return 63;
-}
-
/************************************************************************/
/* External Class Implementation */
/************************************************************************/
-
-/*
- * https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/strtof-strtof-l-wcstof-wcstof-l?view=msvc-160
- *
- * src should be one of the following form :
- *
- * [whitespace] [sign] {digits [radix digits] | radix digits} [{e | E} [sign] digits]
- * [whitespace] [sign] {INF | INFINITY}
- * [whitespace] [sign] NAN [sequence]
- *
- * No hexadecimal form supported
- * no sequence supported after NAN
- */
-float svgUtilStrtof(const char *nPtr, char **endPtr)
-{
- if (endPtr) *endPtr = (char*)(nPtr);
- if (!nPtr) return 0.0f;
-
- auto a = nPtr;
- auto iter = nPtr;
- auto val = 0.0f;
- unsigned long long integerPart = 0;
- int minus = 1;
-
- //ignore leading whitespaces
- while (isspace(*iter)) iter++;
-
- //signed or not
- if (*iter == '-') {
- minus = -1;
- iter++;
- } else if (*iter == '+') {
- iter++;
- }
-
- if (tolower(*iter) == 'i') {
- if ((tolower(*(iter + 1)) == 'n') && (tolower(*(iter + 2)) == 'f')) iter += 3;
- else goto error;
-
- if (tolower(*(iter)) == 'i') {
- if ((tolower(*(iter + 1)) == 'n') && (tolower(*(iter + 2)) == 'i') && (tolower(*(iter + 3)) == 't') && (tolower(*(iter + 4)) == 'y')) iter += 5;
- else goto error;
- }
- if (endPtr) *endPtr = (char *)(iter);
- return (minus == -1) ? -INFINITY : INFINITY;
- }
-
- if (tolower(*iter) == 'n') {
- if ((tolower(*(iter + 1)) == 'a') && (tolower(*(iter + 2)) == 'n')) iter += 3;
- else goto error;
-
- if (endPtr) *endPtr = (char *)(iter);
- return (minus == -1) ? -NAN : NAN;
- }
-
- //Optional: integer part before dot
- if (isdigit(*iter)) {
- for (; isdigit(*iter); iter++) {
- integerPart = integerPart * 10ULL + (unsigned long long)(*iter - '0');
- }
- a = iter;
- } else if (*iter != '.') {
- goto success;
- }
-
- val = static_cast<float>(integerPart);
-
- //Optional: decimal part after dot
- if (*iter == '.') {
- unsigned long long decimalPart = 0;
- unsigned long long pow10 = 1;
- int count = 0;
-
- iter++;
-
- if (isdigit(*iter)) {
- for (; isdigit(*iter); iter++, count++) {
- if (count < 19) {
- decimalPart = decimalPart * 10ULL + + static_cast<unsigned long long>(*iter - '0');
- pow10 *= 10ULL;
- }
- }
- } else if (isspace(*iter)) { //skip if there is a space after the dot.
- a = iter;
- goto success;
- }
-
- val += static_cast<float>(decimalPart) / static_cast<float>(pow10);
- a = iter;
- }
-
- //Optional: exponent
- if (*iter == 'e' || *iter == 'E') {
- ++iter;
-
- //Exception: svg may have 'em' unit for fonts. ex) 5em, 10.5em
- if ((*iter == 'm') || (*iter == 'M')) {
- //TODO: We don't support font em unit now, but has to multiply val * font size later...
- a = iter + 1;
- goto success;
- }
-
- //signed or not
- int minus_e = 1;
-
- if (*iter == '-') {
- minus_e = -1;
- ++iter;
- } else if (*iter == '+') {
- iter++;
- }
-
- unsigned int exponentPart = 0;
-
- if (isdigit(*iter)) {
- while (*iter == '0') iter++;
- for (; isdigit(*iter); iter++) {
- exponentPart = exponentPart * 10U + static_cast<unsigned int>(*iter - '0');
- }
- } else if (!isdigit(*(a - 1))) {
- a = nPtr;
- goto success;
- } else if (*iter == 0) {
- goto success;
- }
-
- //if ((_floatExact(val, 2.2250738585072011f)) && ((minus_e * static_cast<int>(exponentPart)) <= -308)) {
- if ((_floatExact(val, 1.175494351f)) && ((minus_e * static_cast<int>(exponentPart)) <= -38)) {
- //val *= 1.0e-308f;
- val *= 1.0e-38f;
- a = iter;
- goto success;
- }
-
- a = iter;
- auto scale = 1.0f;
-
- while (exponentPart >= 8U) {
- scale *= 1E8;
- exponentPart -= 8U;
- }
- while (exponentPart > 0U) {
- scale *= 10.0f;
- exponentPart--;
- }
- val = (minus_e == -1) ? (val / scale) : (val * scale);
- } else if ((iter > nPtr) && !isdigit(*(iter - 1))) {
- a = nPtr;
- goto success;
- }
-
-success:
- if (endPtr) *endPtr = (char*)(a);
- return minus * val;
-
-error:
- if (endPtr) *endPtr = (char*)(nPtr);
- return 0.0f;
-}
-
-
string svgUtilURLDecode(const char *src)
{
if (!src) return nullptr;
@@ -242,49 +65,3 @@ string svgUtilURLDecode(const char *src)
}
return decoded;
}
-
-
-string svgUtilBase64Decode(const char *src)
-{
- if (!src) return nullptr;
-
- auto length = strlen(src);
- if (length == 0) return nullptr;
-
- string decoded;
- decoded.reserve(3*(1+(length >> 2)));
-
- while (*src && *(src+1)) {
- if (*src <= 0x20) {
- ++src;
- continue;
- }
-
- auto value1 = _base64Value(src[0]);
- auto value2 = _base64Value(src[1]);
- decoded += (value1 << 2) + ((value2 & 0x30) >> 4);
-
- if (!src[2] || src[2] == '=' || src[2] == '.') break;
- auto value3 = _base64Value(src[2]);
- decoded += ((value2 & 0x0f) << 4) + ((value3 & 0x3c) >> 2);
-
- if (!src[3] || src[3] == '=' || src[3] == '.') break;
- auto value4 = _base64Value(src[3]);
- decoded += ((value3 & 0x03) << 6) + value4;
- src += 4;
- }
- 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);
-}
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.h b/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.h
index 48be4649bc..e914eadc65 100644
--- a/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.h
+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.h
@@ -25,11 +25,6 @@
#include "tvgCommon.h"
-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_
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp b/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp
index faacfcd223..dbc3b17b70 100644
--- a/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp
+++ b/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp
@@ -33,7 +33,7 @@
#endif
#include "tvgXmlParser.h"
-#include "tvgSvgUtil.h"
+#include "tvgStr.h"
/************************************************************************/
/* Internal Class Implementation */
@@ -557,10 +557,10 @@ const char* simpleXmlParseCSSAttribute(const char* buf, unsigned bufLength, char
}
if (p == itr) *tag = strdup("all");
- else *tag = svgUtilStrndup(itr, p - itr);
+ else *tag = strDuplicate(itr, p - itr);
if (p == itrEnd) *name = nullptr;
- else *name = svgUtilStrndup(p + 1, itrEnd - p - 1);
+ else *name = strDuplicate(p + 1, itrEnd - p - 1);
return (nextElement ? nextElement + 1 : nullptr);
}
diff --git a/thirdparty/thorvg/src/lib/tvgArray.h b/thirdparty/thorvg/src/utils/tvgArray.h
index 0e8aef3071..919da7e108 100644
--- a/thirdparty/thorvg/src/lib/tvgArray.h
+++ b/thirdparty/thorvg/src/utils/tvgArray.h
@@ -73,11 +73,36 @@ struct Array
return reserve(count + size);
}
- T* end() const
+ const T& operator[](size_t idx) const
+ {
+ return data[idx];
+ }
+
+ T& operator[](size_t idx)
+ {
+ return data[idx];
+ }
+
+ T* end()
+ {
+ return data + count;
+ }
+
+ const T* end() const
{
return data + count;
}
+ const T& last() const
+ {
+ return data[count - 1];
+ }
+
+ const T& first() const
+ {
+ return data[0];
+ }
+
T& last()
{
return data[count - 1];
diff --git a/thirdparty/thorvg/src/lib/tvgBezier.cpp b/thirdparty/thorvg/src/utils/tvgBezier.cpp
index 87511a06c7..f9daf07b84 100644
--- a/thirdparty/thorvg/src/lib/tvgBezier.cpp
+++ b/thirdparty/thorvg/src/utils/tvgBezier.cpp
@@ -116,7 +116,7 @@ float bezAt(const Bezier& bz, float at, float length)
//just in case to prevent an infinite loop
if (at <= 0) return 0.0f;
- if (at >= length) return length;
+ if (at >= length) return 1.0f;
while (true) {
auto right = bz;
diff --git a/thirdparty/thorvg/src/lib/tvgBezier.h b/thirdparty/thorvg/src/utils/tvgBezier.h
index 539a63bdd3..539a63bdd3 100644
--- a/thirdparty/thorvg/src/lib/tvgBezier.h
+++ b/thirdparty/thorvg/src/utils/tvgBezier.h
diff --git a/thirdparty/thorvg/src/lib/tvgLzw.cpp b/thirdparty/thorvg/src/utils/tvgCompressor.cpp
index 52f4ed6716..e38940f3d0 100644
--- a/thirdparty/thorvg/src/lib/tvgLzw.cpp
+++ b/thirdparty/thorvg/src/utils/tvgCompressor.cpp
@@ -55,17 +55,20 @@
*/
#include "config.h"
-#if defined(THORVG_TVG_SAVER_SUPPORT) || defined(THORVG_TVG_LOADER_SUPPORT)
-/************************************************************************/
-/* Internal Class Implementation */
-/************************************************************************/
#include <string>
#include <memory.h>
-#include "tvgLzw.h"
+#include "tvgCompressor.h"
+
+namespace tvg {
+
+
+/************************************************************************/
+/* LZW Implementation */
+/************************************************************************/
+
-namespace {
//LZW Dictionary helper:
constexpr int Nil = -1;
constexpr int MaxDictBits = 12;
@@ -334,15 +337,8 @@ static bool outputSequence(const Dictionary& dict, int code, uint8_t*& output, i
}
return true;
}
-}
-/************************************************************************/
-/* External Class Implementation */
-/************************************************************************/
-
-namespace tvg {
-
uint8_t* lzwDecode(const uint8_t* compressed, uint32_t compressedSizeBytes, uint32_t compressedSizeBits, uint32_t uncompressedSizeBytes)
{
int code = Nil;
@@ -423,6 +419,57 @@ uint8_t* lzwEncode(const uint8_t* uncompressed, uint32_t uncompressedSizeBytes,
return bitStream.release();
}
+
+/************************************************************************/
+/* B64 Implementation */
+/************************************************************************/
+
+
+size_t b64Decode(const char* encoded, const size_t len, char** decoded)
+{
+ static constexpr const char B64_INDEX[256] =
+ {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 62, 63, 62, 62, 63, 52, 53, 54, 55, 56, 57,
+ 58, 59, 60, 61, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6,
+ 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, 0, 0, 0, 0, 63, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
+ 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
+ };
+
+
+ if (!decoded || !encoded || len == 0) return 0;
+
+ auto reserved = 3 * (1 + (len >> 2)) + 1;
+ auto output = static_cast<char*>(malloc(reserved * sizeof(char)));
+ if (!output) return 0;
+ output[reserved - 1] = '\0';
+
+ size_t idx = 0;
+
+ while (*encoded && *(encoded + 1)) {
+ if (*encoded <= 0x20) {
+ ++encoded;
+ continue;
+ }
+
+ auto value1 = B64_INDEX[(size_t)encoded[0]];
+ auto value2 = B64_INDEX[(size_t)encoded[1]];
+ output[idx++] = (value1 << 2) + ((value2 & 0x30) >> 4);
+
+ if (!encoded[2] || encoded[2] == '=' || encoded[2] == '.') break;
+ auto value3 = B64_INDEX[(size_t)encoded[2]];
+ output[idx++] = ((value2 & 0x0f) << 4) + ((value3 & 0x3c) >> 2);
+
+ if (!encoded[3] || encoded[3] == '=' || encoded[3] == '.') break;
+ auto value4 = B64_INDEX[(size_t)encoded[3]];
+ output[idx++] = ((value3 & 0x03) << 6) + value4;
+ encoded += 4;
+ }
+ *decoded = output;
+ return reserved;
}
-#endif
+
+}
diff --git a/thirdparty/thorvg/src/lib/tvgLzw.h b/thirdparty/thorvg/src/utils/tvgCompressor.h
index bd4e783fbf..05d23f4293 100644
--- a/thirdparty/thorvg/src/lib/tvgLzw.h
+++ b/thirdparty/thorvg/src/utils/tvgCompressor.h
@@ -20,8 +20,8 @@
* SOFTWARE.
*/
-#ifndef _TVG_LZW_H_
-#define _TVG_LZW_H_
+#ifndef _TVG_COMPRESSOR_H_
+#define _TVG_COMPRESSOR_H_
#include <cstdint>
@@ -29,6 +29,7 @@ namespace tvg
{
uint8_t* lzwEncode(const uint8_t* uncompressed, uint32_t uncompressedSizeBytes, uint32_t* compressedSizeBytes, uint32_t* compressedSizeBits);
uint8_t* lzwDecode(const uint8_t* compressed, uint32_t compressedSizeBytes, uint32_t compressedSizeBits, uint32_t uncompressedSizeBytes);
+ size_t b64Decode(const char* encoded, const size_t len, char** decoded);
}
-#endif //_TVG_LZW_H
+#endif //_TVG_COMPRESSOR_H_
diff --git a/thirdparty/thorvg/src/lib/tvgMath.h b/thirdparty/thorvg/src/utils/tvgMath.h
index afe1849825..897ff46427 100644
--- a/thirdparty/thorvg/src/lib/tvgMath.h
+++ b/thirdparty/thorvg/src/utils/tvgMath.h
@@ -29,6 +29,8 @@
#include <math.h>
#include "tvgCommon.h"
+#define MATH_PI 3.14159265358979323846f
+#define MATH_PI2 1.57079632679489661923f
#define mathMin(x, y) (((x) < (y)) ? (x) : (y))
#define mathMax(x, y) (((x) > (y)) ? (x) : (y))
@@ -45,6 +47,7 @@ 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) ||
@@ -55,6 +58,7 @@ static inline bool mathEqual(const Matrix& a, const Matrix& b)
return true;
}
+
static inline bool mathRightAngle(const Matrix* m)
{
auto radian = fabsf(atan2f(m->e21, m->e11));
@@ -118,6 +122,15 @@ static inline void mathIdentity(Matrix* m)
}
+static inline void mathTransform(Matrix* transform, Point* coord)
+{
+ auto x = coord->x;
+ auto y = coord->y;
+ coord->x = x * transform->e11 + y * transform->e12 + transform->e13;
+ coord->y = x * transform->e21 + y * transform->e22 + transform->e23;
+}
+
+
static inline void mathScale(Matrix* m, float sx, float sy)
{
m->e11 *= sx;
@@ -125,6 +138,19 @@ static inline void mathScale(Matrix* m, float sx, float sy)
}
+static inline void mathScaleR(Matrix* m, float x, float y)
+{
+ if (x != 1.0f) {
+ m->e11 *= x;
+ m->e21 *= x;
+ }
+ if (y != 1.0f) {
+ m->e22 *= y;
+ m->e12 *= y;
+ }
+}
+
+
static inline void mathTranslate(Matrix* m, float x, float y)
{
m->e13 += x;
@@ -174,6 +200,32 @@ static inline Matrix mathMultiply(const Matrix* lhs, const Matrix* rhs)
}
+static inline void mathTranslateR(Matrix* m, float x, float y)
+{
+ if (x == 0.0f && y == 0.0f) return;
+ m->e13 += (x * m->e11 + y * m->e12);
+ m->e23 += (x * m->e21 + y * m->e22);
+}
+
+
+static inline void mathLog(Matrix* m)
+{
+ TVGLOG("MATH", "Matrix: [%f %f %f] [%f %f %f] [%f %f %f]", m->e11, m->e12, m->e13, m->e21, m->e22, m->e23, m->e31, m->e32, m->e33);
+}
+
+
+static inline float mathLength(const Point* a, const Point* b)
+{
+ auto x = b->x - a->x;
+ auto y = b->y - a->y;
+
+ if (x < 0) x = -x;
+ if (y < 0) y = -y;
+
+ return (x > y) ? (x + 0.375f * y) : (y + 0.375f * x);
+}
+
+
static inline Point operator-(const Point& lhs, const Point& rhs)
{
return {lhs.x - rhs.x, lhs.y - rhs.y};
diff --git a/thirdparty/thorvg/src/utils/tvgStr.cpp b/thirdparty/thorvg/src/utils/tvgStr.cpp
new file mode 100644
index 0000000000..eeed4fc404
--- /dev/null
+++ b/thirdparty/thorvg/src/utils/tvgStr.cpp
@@ -0,0 +1,239 @@
+/*
+ * 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 "config.h"
+#include <cstring>
+#include <memory.h>
+#include "tvgMath.h"
+#include "tvgStr.h"
+
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+
+static inline bool _floatExact(float a, float b)
+{
+ return memcmp(&a, &b, sizeof(float)) == 0;
+}
+
+
+/************************************************************************/
+/* External Class Implementation */
+/************************************************************************/
+
+namespace tvg {
+
+/*
+ * https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/strtof-strtof-l-wcstof-wcstof-l?view=msvc-160
+ *
+ * src should be one of the following form :
+ *
+ * [whitespace] [sign] {digits [radix digits] | radix digits} [{e | E} [sign] digits]
+ * [whitespace] [sign] {INF | INFINITY}
+ * [whitespace] [sign] NAN [sequence]
+ *
+ * No hexadecimal form supported
+ * no sequence supported after NAN
+ */
+float strToFloat(const char *nPtr, char **endPtr)
+{
+ if (endPtr) *endPtr = (char *) (nPtr);
+ if (!nPtr) return 0.0f;
+
+ auto a = nPtr;
+ auto iter = nPtr;
+ auto val = 0.0f;
+ unsigned long long integerPart = 0;
+ int minus = 1;
+
+ //ignore leading whitespaces
+ while (isspace(*iter)) iter++;
+
+ //signed or not
+ if (*iter == '-') {
+ minus = -1;
+ iter++;
+ } else if (*iter == '+') {
+ iter++;
+ }
+
+ if (tolower(*iter) == 'i') {
+ if ((tolower(*(iter + 1)) == 'n') && (tolower(*(iter + 2)) == 'f')) iter += 3;
+ else goto error;
+
+ if (tolower(*(iter)) == 'i') {
+ if ((tolower(*(iter + 1)) == 'n') && (tolower(*(iter + 2)) == 'i') && (tolower(*(iter + 3)) == 't') &&
+ (tolower(*(iter + 4)) == 'y'))
+ iter += 5;
+ else goto error;
+ }
+ if (endPtr) *endPtr = (char *) (iter);
+ return (minus == -1) ? -INFINITY : INFINITY;
+ }
+
+ if (tolower(*iter) == 'n') {
+ if ((tolower(*(iter + 1)) == 'a') && (tolower(*(iter + 2)) == 'n')) iter += 3;
+ else goto error;
+
+ if (endPtr) *endPtr = (char *) (iter);
+ return (minus == -1) ? -NAN : NAN;
+ }
+
+ //Optional: integer part before dot
+ if (isdigit(*iter)) {
+ for (; isdigit(*iter); iter++) {
+ integerPart = integerPart * 10ULL + (unsigned long long) (*iter - '0');
+ }
+ a = iter;
+ } else if (*iter != '.') {
+ goto success;
+ }
+
+ val = static_cast<float>(integerPart);
+
+ //Optional: decimal part after dot
+ if (*iter == '.') {
+ unsigned long long decimalPart = 0;
+ unsigned long long pow10 = 1;
+ int count = 0;
+
+ iter++;
+
+ if (isdigit(*iter)) {
+ for (; isdigit(*iter); iter++, count++) {
+ if (count < 19) {
+ decimalPart = decimalPart * 10ULL + +static_cast<unsigned long long>(*iter - '0');
+ pow10 *= 10ULL;
+ }
+ }
+ } else if (isspace(*iter)) { //skip if there is a space after the dot.
+ a = iter;
+ goto success;
+ }
+
+ val += static_cast<float>(decimalPart) / static_cast<float>(pow10);
+ a = iter;
+ }
+
+ //Optional: exponent
+ if (*iter == 'e' || *iter == 'E') {
+ ++iter;
+
+ //Exception: svg may have 'em' unit for fonts. ex) 5em, 10.5em
+ if ((*iter == 'm') || (*iter == 'M')) {
+ //TODO: We don't support font em unit now, but has to multiply val * font size later...
+ a = iter + 1;
+ goto success;
+ }
+
+ //signed or not
+ int minus_e = 1;
+
+ if (*iter == '-') {
+ minus_e = -1;
+ ++iter;
+ } else if (*iter == '+') {
+ iter++;
+ }
+
+ unsigned int exponentPart = 0;
+
+ if (isdigit(*iter)) {
+ while (*iter == '0') iter++;
+ for (; isdigit(*iter); iter++) {
+ exponentPart = exponentPart * 10U + static_cast<unsigned int>(*iter - '0');
+ }
+ } else if (!isdigit(*(a - 1))) {
+ a = nPtr;
+ goto success;
+ } else if (*iter == 0) {
+ goto success;
+ }
+
+ //if ((_floatExact(val, 2.2250738585072011f)) && ((minus_e * static_cast<int>(exponentPart)) <= -308)) {
+ if ((_floatExact(val, 1.175494351f)) && ((minus_e * static_cast<int>(exponentPart)) <= -38)) {
+ //val *= 1.0e-308f;
+ val *= 1.0e-38f;
+ a = iter;
+ goto success;
+ }
+
+ a = iter;
+ auto scale = 1.0f;
+
+ while (exponentPart >= 8U) {
+ scale *= 1E8;
+ exponentPart -= 8U;
+ }
+ while (exponentPart > 0U) {
+ scale *= 10.0f;
+ exponentPart--;
+ }
+ val = (minus_e == -1) ? (val / scale) : (val * scale);
+ } else if ((iter > nPtr) && !isdigit(*(iter - 1))) {
+ a = nPtr;
+ goto success;
+ }
+
+success:
+ if (endPtr) *endPtr = (char *)(a);
+ return minus * val;
+
+error:
+ if (endPtr) *endPtr = (char *)(nPtr);
+ return 0.0f;
+}
+
+
+int str2int(const char* str, size_t n)
+{
+ int ret = 0;
+ for(size_t i = 0; i < n; ++i) {
+ ret = ret * 10 + (str[i] - '0');
+ }
+ return ret;
+}
+
+char* strDuplicate(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);
+}
+
+char* strDirname(const char* path)
+{
+ const char *ptr = strrchr(path, '/');
+#ifdef _WIN32
+ if (ptr) ptr = strrchr(ptr + 1, '\\');
+#endif
+ int len = int(ptr + 1 - path); // +1 to include '/'
+ return strDuplicate(path, len);
+}
+
+}
diff --git a/thirdparty/thorvg/src/utils/tvgStr.h b/thirdparty/thorvg/src/utils/tvgStr.h
new file mode 100644
index 0000000000..448cc69336
--- /dev/null
+++ b/thirdparty/thorvg/src/utils/tvgStr.h
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+#ifndef _TVG_STR_H_
+#define _TVG_STR_H_
+
+#include <cstddef>
+
+namespace tvg
+{
+
+float strToFloat(const char *nPtr, char **endPtr); //convert to float
+int str2int(const char* str, size_t n); //convert to integer
+char* strDuplicate(const char *str, size_t n); //copy the string
+char* strDirname(const char* path); //return the full directory name
+
+}
+#endif //_TVG_STR_H_
diff --git a/thirdparty/thorvg/update-thorvg.sh b/thirdparty/thorvg/update-thorvg.sh
index f57824872b..804d3b76db 100755
--- a/thirdparty/thorvg/update-thorvg.sh
+++ b/thirdparty/thorvg/update-thorvg.sh
@@ -1,6 +1,6 @@
#!/bin/bash -e
-VERSION=0.10.0
+VERSION=0.10.7
rm -rf AUTHORS LICENSE inc/ src/ *.zip *.tar.gz tmp/
@@ -32,7 +32,7 @@ cat << EOF > ../inc/config.h
EOF
mkdir ../src
-cp -rv src/lib ../src/
+cp -rv src/lib src/utils ../src/
# Only sw_engine is enabled.
rm -rfv ../src/lib/gl_engine