diff --git a/COPYRIGHT.txt b/COPYRIGHT.txt index e79817a2e76f..8288e7960292 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 c4d7671fb30b..55b8c4f4a050 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 360741363a23..1acff681358d 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 38fd5f640367..bf29ad3016f0 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 0da2a54bc255..8705bc430d7a 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 20e1afa2e555..40bb2dc1b916 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 9da4905943d1..15d9ad4e8e5a 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 87125418fb08..b2efe3def346 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 897296fa9d61..a5efc5ec17f3 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 3d68b56fb88e..4cee0b18e280 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 + #if 0 #include 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 1c6eb4e42897..cede9e6eb7a9 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<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; - float invR = 1.0f / radius; - fill->radial.shiftX = -cx; - fill->radial.shiftY = -cy; - fill->radial.a = radius; + 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; + } + + 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,28 +279,48 @@ 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; - // 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; + 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 { - 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); + + 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; + } } } } @@ -262,48 +328,132 @@ void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint3 void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, uint8_t a) { - auto rx = (x + 0.5f) * fill->radial.a11 + (y + 0.5f) * fill->radial.a12 + fill->radial.shiftX; - auto ry = (x + 0.5f) * fill->radial.a21 + (y + 0.5f) * fill->radial.a22 + fill->radial.shiftY; + 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 { + float b, deltaB, det, deltaDet, deltaDeltaDet; + _calculateCoefficients(fill, x, y, b, deltaB, det, deltaDet, deltaDeltaDet); - // 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) - b), *dst, a); + det += deltaDet; + deltaDet += deltaDeltaDet; + b += deltaB; + } + } +} - for (uint32_t i = 0 ; i < len ; ++i, ++dst) { - *dst = op(_pixel(fill, sqrtf(det)), *dst, a); - det += detFirstDerivative; - detFirstDerivative += detSecondDerivative; + +void fillRadial(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32_t len, SwMask maskOp, 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); + 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, uint8_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwMask maskOp, 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, ++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; + } } } void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, SwBlender op2, uint8_t a) { - auto rx = (x + 0.5f) * fill->radial.a11 + (y + 0.5f) * fill->radial.a12 + fill->radial.shiftX; - auto ry = (x + 0.5f) * fill->radial.a21 + (y + 0.5f) * fill->radial.a22 + fill->radial.shiftY; + 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; - // 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; + 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(t * FIXPT_SIZE)))); + for (uint32_t i = 0; i < len; ++i, ++dst) { + *dst = maskOp(src, *dst, ~src); + } + return; + } + + auto vMax = static_cast(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(t * FIXPT_SIZE); + auto inc2 = static_cast(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(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(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(t * FIXPT_SIZE); + auto inc2 = static_cast(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 4829a8c81d52..fb8581b412ac 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 bd32bf0b237c..8fd54c2a4f21 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 uint8_t _opMaskAdd(uint8_t s, uint8_t d, uint8_t a) +{ + return s + MULTIPLY(d, a); } -static inline uint32_t _opMaskAdd(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 s + ALPHA_BLEND(d, a); + return MULTIPLY(s, 255 - d); } -static inline uint32_t _opMaskSubtract(TVG_UNUSED 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(d, a); + return MULTIPLY(s, d); } -static inline uint32_t _opMaskDifference(uint32_t s, uint32_t d, uint8_t a) +static inline uint8_t _opMaskDifference(uint8_t s, uint8_t d, uint8_t a) { - return ALPHA_BLEND(s, IA(d)) + ALPHA_BLEND(d, a); + return MULTIPLY(s, 255 - d) + MULTIPLY(d, a); } -static inline uint32_t _opAMaskAdd(uint32_t s, uint32_t d, uint8_t a) +static inline bool _direct(CompositeMethod method) { - return INTERPOLATE(s, d, a); + //subtract & Intersect allows the direct composition + if (method == CompositeMethod::SubtractMask || method == CompositeMethod::IntersectMask) return true; + return false; } -static inline uint32_t _opAMaskSubtract(TVG_UNUSED uint32_t s, uint32_t d, uint8_t a) -{ - 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)); -} - - -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(region.max.x - region.min.x); auto h = static_cast(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(region.max.x - region.min.x); auto h = static_cast(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; + //8bit masking channels composition + if (surface->channelSize != sizeof(uint8_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); + 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); - 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; - } - - //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; + uint8_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; - } + 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; + //8bit masking channels composition + if (surface->channelSize != sizeof(uint8_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; - } - - //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; @@ -668,29 +656,12 @@ 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(span->x); x < static_cast(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(span->x); x < static_cast(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(span->x); x < static_cast(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(span->x); x < static_cast(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); @@ -1088,41 +1058,18 @@ 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, ®ion, 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(region.max.y - region.min.y); - auto w = static_cast(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); + 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) { - 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; + 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(region.max.y - region.min.y); + auto w = static_cast(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); //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, ~*src); + } + } else { + for (uint32_t x = 0; x < w; ++x, ++src, ++cmp) { + 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 bool _rasterDirectDirectMaskedImage(SwSurface* surface, const SwImage* image, const SwBBox& region, SwMask maskOp, uint8_t opacity) { auto h = static_cast(region.max.y - region.min.y); auto w = static_cast(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 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) { - *cmp = maskOp(*src, *cmp, IA(*src)); + 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); } } else { - for (uint32_t x = 0; x < w; ++x, ++src, ++cmp) { - *cmp = amaskOp(*src, *cmp, opacity); + 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; } - - -static void _rasterDirectMaskedImageInt(SwSurface* surface, const SwImage* image, const SwBBox& region, uint8_t opacity) -{ - auto h = static_cast(region.max.y - region.min.y); - auto w = static_cast(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; - } - y += (h - 1); - } else { - rasterPixel32(cbuffer, 0x00000000, 0, surface->compositor->bbox.max.x - surface->compositor->bbox.min.x); - } - cbuffer += cstride; - } -} - +#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(region.max.y - region.min.y); auto w = static_cast(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)); - } - } 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)); + 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)); + } } + 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, ®ion, opacity); } } @@ -1564,52 +1517,36 @@ static bool _rasterImage(SwSurface* surface, SwImage* image, const Matrix* trans /************************************************************************/ template -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(region.max.y - region.min.y); auto w = static_cast(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 -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(region.max.y - region.min.y); auto w = static_cast(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(surface, region, fill, opMaskPreAdd); - else if (method == CompositeMethod::SubtractMask) _rasterGradientMaskedRectDup(surface, region, fill, opMaskPreSubtract); - else if (method == CompositeMethod::DifferenceMask) _rasterGradientMaskedRectDup(surface, region, fill, opMaskPreDifference); - else if (method == CompositeMethod::IntersectMask) _rasterGradientMaskedRectInt(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(surface, region, fill, maskOp); + else return _rasterCompositeGradientMaskedRect(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(surface, region, fill); else return _rasterGradientMaskedRect(surface, region, fill); @@ -1734,64 +1667,54 @@ static bool _rasterRadialGradientRect(SwSurface* surface, const SwBBox& region, } - /************************************************************************/ /* Rle Gradient */ /************************************************************************/ template -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 -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; + auto cbuffer = surface->compositor->image.buf8; + auto dbuffer = surface->buf8; - 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; - } - } + 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 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(surface, rle, fill, opMaskAdd); - else if (method == CompositeMethod::SubtractMask) _rasterGradientMaskedRleDup(surface, rle, fill, opMaskSubtract); - else if (method == CompositeMethod::DifferenceMask) _rasterGradientMaskedRleDup(surface, rle, fill, opMaskDifference); - else if (method == CompositeMethod::IntersectMask) _rasterGradientMaskedRleInt(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(surface, rle, fill, maskOp); + else return _rasterCompositeGradientMaskedRle(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(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(surface->w) || bbox.min.y >= static_cast(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 4b30c52ea398..698ab37da297 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) { + return false; + +#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; - int32_t sw = static_cast(image->stride); - int32_t sh = image->h; - int32_t x1, x2, ar, ab, iru, irv, px, ay; - int32_t vv = 0, uu = 0; - int32_t minx = INT32_MAX, maxx = INT32_MIN; - float dx, u, v, iptr; - auto cbuffer = surface->compositor->image.buf32; - SwSpan* span = nullptr; //used only when rle based. - - if (!_arrange(image, region, yStart, yEnd)) return; - - //Clear out of the Polygon vertical ranges - auto size = surface->compositor->bbox.max.x - surface->compositor->bbox.min.x; - if (dirFlag == 1) { //left top case. - for(int y = surface->compositor->bbox.min.y; y < yStart; ++y) { - rasterPixel32(surface->compositor->image.buf32 + y * surface->compositor->image.stride, 0, surface->compositor->bbox.min.x, size); - } - } - if (dirFlag == 4) { //right bottom case. - for(int y = yEnd; y < surface->compositor->bbox.max.y; ++y) { - rasterPixel32(surface->compositor->image.buf32 + y * surface->compositor->image.stride, 0, surface->compositor->bbox.min.x, size); - } - } - - //Loop through all lines in the segment - uint32_t spanIdx = 0; - - if (region) { - minx = region->min.x; - maxx = region->max.x; - } else { - span = image->rle->spans; - while (span->y < yStart) { - ++span; - ++spanIdx; - } - } - - for (int32_t y = yStart; y < yEnd; ++y) { - auto cmp = &cbuffer[y * surface->compositor->image.stride]; - x1 = (int32_t)_xa; - x2 = (int32_t)_xb; - - if (!region) { - minx = INT32_MAX; - maxx = INT32_MIN; - //one single row, could be consisted of multiple spans. - while (span->y == y && spanIdx < image->rle->size) { - if (minx > span->x) minx = span->x; - if (maxx < span->x + span->len) maxx = span->x + span->len; - ++span; - ++spanIdx; - } - } - - if (x1 < minx) x1 = minx; - if (x2 > maxx) x2 = maxx; - - //Anti-Aliasing frames - //FIXME: this aa must be applied before masking op - ay = y - aaSpans->yStart; - if (aaSpans->lines[ay].x[0] > x1) aaSpans->lines[ay].x[0] = x1; - if (aaSpans->lines[ay].x[1] < x2) aaSpans->lines[ay].x[1] = x2; - - //Range allowed - if ((x2 - x1) >= 1 && (x1 < maxx) && (x2 > minx)) { - for (int32_t x = surface->compositor->bbox.min.x; x < surface->compositor->bbox.max.x; ++x) { - //Range allowed - if (x >= x1 && x < x2) { - //Perform subtexel pre-stepping on UV - dx = 1 - (_xa - x1); - u = _ua + dx * _dudx; - v = _va + dx * _dvdx; - if ((uint32_t)v >= image->h) { - cmp[x] = 0; - } else { - if (opacity == 255) { - uu = (int) u; - if (uu >= sw) continue; - vv = (int) v; - if (vv >= sh) continue; - - ar = (int)(255 * (1 - modff(u, &iptr))); - ab = (int)(255 * (1 - modff(v, &iptr))); - iru = uu + 1; - irv = vv + 1; - - px = *(sbuf + (vv * sw) + uu); - - /* horizontal interpolate */ - if (iru < sw) { - /* right pixel */ - int px2 = *(sbuf + (vv * sw) + iru); - px = INTERPOLATE(px, px2, ar); - } - /* vertical interpolate */ - if (irv < sh) { - /* bottom pixel */ - int px2 = *(sbuf + (irv * sw) + uu); - - /* horizontal interpolate */ - if (iru < sw) { - /* bottom right pixel */ - int px3 = *(sbuf + (irv * sw) + iru); - px2 = INTERPOLATE(px2, px3, ar); - } - px = INTERPOLATE(px, px2, ab); - } - cmp[x] = ALPHA_BLEND(cmp[x], A(px)); - - //Step UV horizontally - u += _dudx; - v += _dvdx; - } else { - uu = (int) u; - if (uu >= sw) continue; - vv = (int) v; - if (vv >= sh) continue; - - ar = (int)(255 * (1 - modff(u, &iptr))); - ab = (int)(255 * (1 - modff(v, &iptr))); - iru = uu + 1; - irv = vv + 1; - - px = *(sbuf + (vv * sw) + uu); - - /* horizontal interpolate */ - if (iru < sw) { - /* right pixel */ - int px2 = *(sbuf + (vv * sw) + iru); - px = INTERPOLATE(px, px2, ar); - } - /* vertical interpolate */ - if (irv < sh) { - /* bottom pixel */ - int px2 = *(sbuf + (irv * sw) + uu); - - /* horizontal interpolate */ - if (iru < sw) { - /* bottom right pixel */ - int px3 = *(sbuf + (irv * sw) + iru); - px2 = INTERPOLATE(px2, px3, ar); - } - px = INTERPOLATE(px, px2, ab); - } - cmp[x] = ALPHA_BLEND(cmp[x], MULTIPLY(A(px), opacity)); - - //Step UV horizontally - u += _dudx; - v += _dvdx; - } - } - } else { - //Clear out of polygon horizontal range - if (x < x1 && (dirFlag == 1 || dirFlag == 2)) cmp[x] = 0; - else if (x >= x2 && (dirFlag == 3 || dirFlag == 4)) cmp[x] = 0; - } - } - } - //Step along both edges - _xa += _dxdya; - _xb += _dxdyb; - _ua += _dudya; - _va += _dvdya; - } - xa = _xa; - xb = _xb; - ua = _ua; - va = _va; -} - - -static void _rasterMaskedPolygonImageSegmentDup(SwSurface* surface, const SwImage* image, const SwBBox* region, SwBlender maskOp, SwBlender amaskOp, int yStart, int yEnd, AASpans* aaSpans, uint8_t opacity) -{ - 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(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 1115a41d6498..091b72fa9701 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 fe84983a767a..a4a7fabdee1f 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 14dd68b906f6..651eaee45242 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 -#include /************************************************************************/ /* 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,53 +223,83 @@ 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; - const float* pattern; - dash.cnt = rshape->strokeDash(&pattern); - if (dash.cnt == 0) return nullptr; + dash.cnt = rshape->strokeDash((const float**)&dash.pattern, &offset); - //OPTMIZE ME: Use mempool??? - dash.pattern = const_cast(pattern); - dash.outline = static_cast(calloc(1, sizeof(SwOutline))); + //dash by trimming. + if (length > 0.0f && dash.cnt == 0) { + auto begin = length * rshape->stroke->trim.begin; + auto end = length * rshape->stroke->trim.end; - //smart reservation - auto outlinePtsCnt = 0; - auto outlineCntrsCnt = 0; + //TODO: mix trimming + dash style - for (uint32_t i = 0; i < cmdCnt; ++i) { - switch (*(cmds + i)) { - case PathCommand::Close: { - ++outlinePtsCnt; - break; - } - case PathCommand::MoveTo: { - ++outlineCntrsCnt; - ++outlinePtsCnt; - break; - } - case PathCommand::LineTo: { - ++outlinePtsCnt; - break; - } - case PathCommand::CubicTo: { - outlinePtsCnt += 3; - break; - } + //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; + } + + //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; } } - ++outlinePtsCnt; //for close - ++outlineCntrsCnt; //for end + //OPTMIZE ME: Use mempool??? + dash.outline = static_cast(calloc(1, sizeof(SwOutline))); + + //smart reservation + 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; + } //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); + //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) { @@ -271,9 +309,9 @@ static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* trans } case PathCommand::MoveTo: { //reset the dash - dash.curIdx = 0; - dash.curLen = *dash.pattern; - dash.curOpGap = false; + dash.curIdx = offIdx % dash.cnt; + dash.curLen = dash.pattern[dash.curIdx] - offset; + dash.curOpGap = offIdx % 2; dash.ptStart = dash.ptCur = *pts; ++pts; break; @@ -294,10 +332,55 @@ static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* trans _outlineEnd(*dash.outline); + 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: { + length += mathLength(pts - 1, close); + ++pts; + break; + } + case PathCommand::MoveTo: { + close = pts; + ++pts; + break; + } + case PathCommand::LineTo: { + length += mathLength(pts - 1, pts); + ++pts; + break; + } + case PathCommand::CubicTo: { + length += bezLength({*(pts - 1), *pts, *(pts + 1), *(pts + 2)}); + pts += 3; + break; + } + } + ++cmds; + } + return length; +} + + static bool _axisAlignedRect(const SwOutline* outline) { //Fast Track: axis-aligned rectangle? @@ -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(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 38626cb42895..f46a4a5a1d4f 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 f671daa727cb..4e8c8568d4c0 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(picture)->pImpl->ref(); + } + + ~Impl() + { + if (static_cast(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 9a3018e7a61e..fe934ec3521b 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 23011ffed577..f36b4b9b305b 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 cc7e1ccaed76..9215882c8d92 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(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::gen() noexcept +{ + return unique_ptr(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(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::gen() noexcept +{ + return unique_ptr(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 20603b733364..ff3dd48c4927 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 3e040c08f112..000000000000 --- 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 -#include -#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(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::gen() noexcept -{ - return unique_ptr(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 16b604c89eba..8ed0d5752e4d 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 LoaderMgr::loader(const string& path, bool* invalid) shared_ptr 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(loader); - } else { - TVGLOG("LOADER", "Given mimetype \"%s\" seems incorrect or not supported. Will try again with other types.", mimeType.c_str()); - delete(loader); + //Try with the given MimeType + if (!mimeType.empty()) { + if (auto loader = _findByType(mimeType)) { + if (loader->open(data, size, copy)) { + return shared_ptr(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(FileType::Unknown); i++) { - auto loader = _find(static_cast(i)); - if (loader) { - if (loader->open(data, size, copy)) return shared_ptr(loader); - else delete(loader); + //Unkown MimeType. Try with the candidates in the order + } else { + for (int i = 0; i < static_cast(FileType::Unknown); i++) { + auto loader = _find(static_cast(i)); + if (loader) { + if (loader->open(data, size, copy)) return shared_ptr(loader); + else delete(loader); + } } } return nullptr; diff --git a/thirdparty/thorvg/src/lib/tvgPaint.cpp b/thirdparty/thorvg/src/lib/tvgPaint.cpp index 57da269cd791..bac5e434a501 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(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(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 c00238a070eb..c020a7dffda2 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& 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& 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 d445c72c10e3..f29b8a1ec3ad 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 a85f60e5d0c5..000000000000 --- 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 -#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(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::gen() noexcept -{ - return unique_ptr(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 9d7bafb613b2..1231089ff526 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 8fa38bcc8574..90b1775610d3 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 f32feb213374..336ac71d813b 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 b05f85bf1cf4..bb266866d0e2 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(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(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 019468083d49..e3887c60fc38 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 7ada963b776b..2dad80f5d040 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 6fdb64120422..c3c477a263cf 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& 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 998c98e83fe2..9825fd8cc4b4 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& dst, const Array& 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* 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 (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 961438ff42f1..1809c7749c43 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 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 e044931b5183..79a9c0771dd6 100644 --- a/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.cpp +++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.cpp @@ -55,7 +55,7 @@ #include #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* cmds, Array* 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* cmds, Array* 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 e5beef8093d1..71712442e8e0 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 #include +#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 _sceneBuildHelper(const SvgNode* node, const Box& vBox, const string& svgPath, bool mask, int depth, bool* isMaskWhite = nullptr); @@ -138,7 +144,7 @@ static unique_ptr _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 _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 _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 _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 _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; - - 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; - } + 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); - return valid; + 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); +} + + +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 _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 cmds; - Array pts; - switch (node->type) { case SvgNodeType::Path: { if (node->node.path.path) { + Array cmds; + Array 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(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 _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 _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 _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 _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 2aaeb2b25dfd..763a357f99e9 100644 --- a/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.cpp +++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.cpp @@ -21,19 +21,12 @@ */ #include -#include -#include #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(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(*iter - '0'); - pow10 *= 10ULL; - } - } - } else if (isspace(*iter)) { //skip if there is a space after the dot. - a = iter; - goto success; - } - - val += static_cast(decimalPart) / static_cast(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(*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(exponentPart)) <= -308)) { - if ((_floatExact(val, 1.175494351f)) && ((minus_e * static_cast(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 48be4649bc33..e914eadc6588 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 faacfcd223fc..dbc3b17b70ed 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 similarity index 88% rename from thirdparty/thorvg/src/lib/tvgArray.h rename to thirdparty/thorvg/src/utils/tvgArray.h index 0e8aef30714e..919da7e108f0 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 similarity index 99% rename from thirdparty/thorvg/src/lib/tvgBezier.cpp rename to thirdparty/thorvg/src/utils/tvgBezier.cpp index 87511a06c7df..f9daf07b84c9 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 similarity index 100% rename from thirdparty/thorvg/src/lib/tvgBezier.h rename to thirdparty/thorvg/src/utils/tvgBezier.h diff --git a/thirdparty/thorvg/src/lib/tvgLzw.cpp b/thirdparty/thorvg/src/utils/tvgCompressor.cpp similarity index 88% rename from thirdparty/thorvg/src/lib/tvgLzw.cpp rename to thirdparty/thorvg/src/utils/tvgCompressor.cpp index 52f4ed6716d1..e38940f3d002 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 #include -#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(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 similarity index 90% rename from thirdparty/thorvg/src/lib/tvgLzw.h rename to thirdparty/thorvg/src/utils/tvgCompressor.h index bd4e783fbf4a..05d23f429323 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 @@ -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 similarity index 83% rename from thirdparty/thorvg/src/lib/tvgMath.h rename to thirdparty/thorvg/src/utils/tvgMath.h index afe1849825d9..897ff464274f 100644 --- a/thirdparty/thorvg/src/lib/tvgMath.h +++ b/thirdparty/thorvg/src/utils/tvgMath.h @@ -29,6 +29,8 @@ #include #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 000000000000..eeed4fc404bb --- /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 +#include +#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(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(*iter - '0'); + pow10 *= 10ULL; + } + } + } else if (isspace(*iter)) { //skip if there is a space after the dot. + a = iter; + goto success; + } + + val += static_cast(decimalPart) / static_cast(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(*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(exponentPart)) <= -308)) { + if ((_floatExact(val, 1.175494351f)) && ((minus_e * static_cast(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 000000000000..448cc69336f2 --- /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 + +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 f57824872b19..804d3b76db1a 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