parent
b905959f43
commit
81949c2cd2
|
@ -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/
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"])
|
||||
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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"])
|
||||
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -5,5 +5,5 @@
|
|||
|
||||
#define THORVG_SVG_LOADER_SUPPORT
|
||||
|
||||
#define THORVG_VERSION_STRING "0.10.0"
|
||||
#define THORVG_VERSION_STRING "0.10.7"
|
||||
#endif
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -26,6 +26,8 @@
|
|||
#include "tvgCommon.h"
|
||||
#include "tvgRender.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#if 0
|
||||
#include <sys/time.h>
|
||||
static double timeStamp()
|
||||
|
@ -139,10 +141,11 @@ struct SwFill
|
|||
};
|
||||
|
||||
struct SwRadial {
|
||||
float a11, a12, shiftX;
|
||||
float a21, a22, shiftY;
|
||||
float detSecDeriv;
|
||||
float a;
|
||||
float a11, a12, a13;
|
||||
float a21, a22, a23;
|
||||
float fx, fy, fr;
|
||||
float dx, dy, dr;
|
||||
float invA, a;
|
||||
};
|
||||
|
||||
union {
|
||||
|
@ -194,14 +197,14 @@ struct SwStroke
|
|||
|
||||
struct SwDashStroke
|
||||
{
|
||||
SwOutline* outline;
|
||||
float curLen;
|
||||
int32_t curIdx;
|
||||
Point ptStart;
|
||||
Point ptCur;
|
||||
float* pattern;
|
||||
uint32_t cnt;
|
||||
bool curOpGap;
|
||||
SwOutline* outline = nullptr;
|
||||
float curLen = 0;
|
||||
int32_t curIdx = 0;
|
||||
Point ptStart = {0, 0};
|
||||
Point ptCur = {0, 0};
|
||||
float* pattern = nullptr;
|
||||
uint32_t cnt = 0;
|
||||
bool curOpGap = false;
|
||||
};
|
||||
|
||||
struct SwShape
|
||||
|
@ -235,6 +238,7 @@ struct SwImage
|
|||
bool scaled = false; //draw scaled image
|
||||
};
|
||||
|
||||
typedef uint8_t(*SwMask)(uint8_t s, uint8_t d, uint8_t a); //src, dst, alpha
|
||||
typedef uint32_t(*SwBlender)(uint32_t s, uint32_t d, uint8_t a); //src, dst, alpha
|
||||
typedef uint32_t(*SwJoin)(uint8_t r, uint8_t g, uint8_t b, uint8_t a); //color channel join
|
||||
typedef uint8_t(*SwAlpha)(uint8_t*); //blending alpha
|
||||
|
@ -295,7 +299,7 @@ static inline uint32_t INTERPOLATE(uint32_t s, uint32_t d, uint8_t a)
|
|||
|
||||
static inline uint8_t INTERPOLATE8(uint8_t s, uint8_t d, uint8_t a)
|
||||
{
|
||||
return ((s * a + 0xff) >> 8) + ((d * ~a + 0xff) >> 8);
|
||||
return (((s) * (a) + 0xff) >> 8) + (((d) * ~(a) + 0xff) >> 8);
|
||||
}
|
||||
|
||||
static inline SwCoord HALF_STROKE(float width)
|
||||
|
@ -363,18 +367,18 @@ static inline uint32_t opBlendDifference(uint32_t s, uint32_t d, TVG_UNUSED uint
|
|||
static inline uint32_t opBlendExclusion(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a)
|
||||
{
|
||||
//A + B - 2AB
|
||||
auto c1 = min(255, C1(s) + C1(d) - min(255, (C1(s) * C1(d)) << 1));
|
||||
auto c2 = min(255, C2(s) + C2(d) - min(255, (C2(s) * C2(d)) << 1));
|
||||
auto c3 = min(255, C3(s) + C3(d) - min(255, (C3(s) * C3(d)) << 1));
|
||||
auto c1 = std::min(255, C1(s) + C1(d) - std::min(255, (C1(s) * C1(d)) << 1));
|
||||
auto c2 = std::min(255, C2(s) + C2(d) - std::min(255, (C2(s) * C2(d)) << 1));
|
||||
auto c3 = std::min(255, C3(s) + C3(d) - std::min(255, (C3(s) * C3(d)) << 1));
|
||||
return JOIN(255, c1, c2, c3);
|
||||
}
|
||||
|
||||
static inline uint32_t opBlendAdd(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a)
|
||||
{
|
||||
// s + d
|
||||
auto c1 = min(C1(s) + C1(d), 255);
|
||||
auto c2 = min(C2(s) + C2(d), 255);
|
||||
auto c3 = min(C3(s) + C3(d), 255);
|
||||
auto c1 = std::min(C1(s) + C1(d), 255);
|
||||
auto c2 = std::min(C2(s) + C2(d), 255);
|
||||
auto c3 = std::min(C3(s) + C3(d), 255);
|
||||
return JOIN(255, c1, c2, c3);
|
||||
}
|
||||
|
||||
|
@ -402,27 +406,27 @@ static inline uint32_t opBlendOverlay(uint32_t s, uint32_t d, TVG_UNUSED uint8_t
|
|||
{
|
||||
// if (2 * d < da) => 2 * s * d,
|
||||
// else => 1 - 2 * (1 - s) * (1 - d)
|
||||
auto c1 = (C1(d) < 128) ? min(255, 2 * MULTIPLY(C1(s), C1(d))) : (255 - min(255, 2 * MULTIPLY(255 - C1(s), 255 - C1(d))));
|
||||
auto c2 = (C2(d) < 128) ? min(255, 2 * MULTIPLY(C2(s), C2(d))) : (255 - min(255, 2 * MULTIPLY(255 - C2(s), 255 - C2(d))));
|
||||
auto c3 = (C3(d) < 128) ? min(255, 2 * MULTIPLY(C3(s), C3(d))) : (255 - min(255, 2 * MULTIPLY(255 - C3(s), 255 - C3(d))));
|
||||
auto c1 = (C1(d) < 128) ? std::min(255, 2 * MULTIPLY(C1(s), C1(d))) : (255 - std::min(255, 2 * MULTIPLY(255 - C1(s), 255 - C1(d))));
|
||||
auto c2 = (C2(d) < 128) ? std::min(255, 2 * MULTIPLY(C2(s), C2(d))) : (255 - std::min(255, 2 * MULTIPLY(255 - C2(s), 255 - C2(d))));
|
||||
auto c3 = (C3(d) < 128) ? std::min(255, 2 * MULTIPLY(C3(s), C3(d))) : (255 - std::min(255, 2 * MULTIPLY(255 - C3(s), 255 - C3(d))));
|
||||
return JOIN(255, c1, c2, c3);
|
||||
}
|
||||
|
||||
static inline uint32_t opBlendDarken(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a)
|
||||
{
|
||||
// min(s, d)
|
||||
auto c1 = min(C1(s), C1(d));
|
||||
auto c2 = min(C2(s), C2(d));
|
||||
auto c3 = min(C3(s), C3(d));
|
||||
auto c1 = std::min(C1(s), C1(d));
|
||||
auto c2 = std::min(C2(s), C2(d));
|
||||
auto c3 = std::min(C3(s), C3(d));
|
||||
return JOIN(255, c1, c2, c3);
|
||||
}
|
||||
|
||||
static inline uint32_t opBlendLighten(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a)
|
||||
{
|
||||
// max(s, d)
|
||||
auto c1 = max(C1(s), C1(d));
|
||||
auto c2 = max(C2(s), C2(d));
|
||||
auto c3 = max(C3(s), C3(d));
|
||||
auto c1 = std::max(C1(s), C1(d));
|
||||
auto c2 = std::max(C2(s), C2(d));
|
||||
auto c3 = std::max(C3(s), C3(d));
|
||||
return JOIN(255, c1, c2, c3);
|
||||
}
|
||||
|
||||
|
@ -448,61 +452,21 @@ static inline uint32_t opBlendColorBurn(uint32_t s, uint32_t d, TVG_UNUSED uint8
|
|||
|
||||
static inline uint32_t opBlendHardLight(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a)
|
||||
{
|
||||
auto c1 = (C1(s) < 128) ? min(255, 2 * MULTIPLY(C1(s), C1(d))) : (255 - min(255, 2 * MULTIPLY(255 - C1(s), 255 - C1(d))));
|
||||
auto c2 = (C2(s) < 128) ? min(255, 2 * MULTIPLY(C2(s), C2(d))) : (255 - min(255, 2 * MULTIPLY(255 - C2(s), 255 - C2(d))));
|
||||
auto c3 = (C3(s) < 128) ? min(255, 2 * MULTIPLY(C3(s), C3(d))) : (255 - min(255, 2 * MULTIPLY(255 - C3(s), 255 - C3(d))));
|
||||
auto c1 = (C1(s) < 128) ? std::min(255, 2 * MULTIPLY(C1(s), C1(d))) : (255 - std::min(255, 2 * MULTIPLY(255 - C1(s), 255 - C1(d))));
|
||||
auto c2 = (C2(s) < 128) ? std::min(255, 2 * MULTIPLY(C2(s), C2(d))) : (255 - std::min(255, 2 * MULTIPLY(255 - C2(s), 255 - C2(d))));
|
||||
auto c3 = (C3(s) < 128) ? std::min(255, 2 * MULTIPLY(C3(s), C3(d))) : (255 - std::min(255, 2 * MULTIPLY(255 - C3(s), 255 - C3(d))));
|
||||
return JOIN(255, c1, c2, c3);
|
||||
}
|
||||
|
||||
static inline uint32_t opBlendSoftLight(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a)
|
||||
{
|
||||
//(255 - 2 * s) * (d * d) + (2 * s * b)
|
||||
auto c1 = min(255, MULTIPLY(255 - min(255, 2 * C1(s)), MULTIPLY(C1(d), C1(d))) + 2 * MULTIPLY(C1(s), C1(d)));
|
||||
auto c2 = min(255, MULTIPLY(255 - min(255, 2 * C2(s)), MULTIPLY(C2(d), C2(d))) + 2 * MULTIPLY(C2(s), C2(d)));
|
||||
auto c3 = min(255, MULTIPLY(255 - min(255, 2 * C3(s)), MULTIPLY(C3(d), C3(d))) + 2 * MULTIPLY(C3(s), C3(d)));
|
||||
auto c1 = std::min(255, MULTIPLY(255 - std::min(255, 2 * C1(s)), MULTIPLY(C1(d), C1(d))) + 2 * MULTIPLY(C1(s), C1(d)));
|
||||
auto c2 = std::min(255, MULTIPLY(255 - std::min(255, 2 * C2(s)), MULTIPLY(C2(d), C2(d))) + 2 * MULTIPLY(C2(s), C2(d)));
|
||||
auto c3 = std::min(255, MULTIPLY(255 - std::min(255, 2 * C3(s)), MULTIPLY(C3(d), C3(d))) + 2 * MULTIPLY(C3(s), C3(d)));
|
||||
return JOIN(255, c1, c2, c3);
|
||||
}
|
||||
|
||||
static inline uint32_t opMaskAdd(uint32_t s, uint32_t d, uint8_t a)
|
||||
{
|
||||
return opBlendNormal(s, d, a);
|
||||
}
|
||||
|
||||
static inline uint32_t opMaskSubtract(uint32_t s, uint32_t d, uint8_t a)
|
||||
{
|
||||
return ALPHA_BLEND(d, MULTIPLY(IA(s), a));
|
||||
}
|
||||
|
||||
static inline uint32_t opMaskDifference(uint32_t s, uint32_t d, uint8_t a)
|
||||
{
|
||||
auto t = ALPHA_BLEND(s, a);
|
||||
return ALPHA_BLEND(t, IA(d)) + ALPHA_BLEND(d, IA(t));
|
||||
}
|
||||
|
||||
static inline uint32_t opMaskIntersect(uint32_t s, uint32_t d, uint8_t a)
|
||||
{
|
||||
return ALPHA_BLEND(d, MULTIPLY(IA(s), a));
|
||||
}
|
||||
|
||||
static inline uint32_t opMaskPreAdd(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a)
|
||||
{
|
||||
return opBlendPreNormal(s, d, a);
|
||||
}
|
||||
|
||||
static inline uint32_t opMaskPreSubtract(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a)
|
||||
{
|
||||
return ALPHA_BLEND(d, IA(s));
|
||||
}
|
||||
|
||||
static inline uint32_t opMaskPreDifference(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a)
|
||||
{
|
||||
return ALPHA_BLEND(s, IA(d)) + ALPHA_BLEND(d, IA(s));
|
||||
}
|
||||
|
||||
static inline uint32_t opMaskPreIntersect(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a)
|
||||
{
|
||||
return ALPHA_BLEND(d, MULTIPLY(a, IA(s)));
|
||||
}
|
||||
|
||||
int64_t mathMultiply(int64_t a, int64_t b);
|
||||
int64_t mathDivide(int64_t a, int64_t b);
|
||||
|
@ -551,13 +515,19 @@ void imageFree(SwImage* image);
|
|||
bool fillGenColorTable(SwFill* fill, const Fill* fdata, const Matrix* transform, SwSurface* surface, uint8_t opacity, bool ctable);
|
||||
void fillReset(SwFill* fill);
|
||||
void fillFree(SwFill* fill);
|
||||
|
||||
//OPTIMIZE_ME: Skip the function pointer access
|
||||
void fillLinear(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32_t len, SwMask maskOp, uint8_t opacity); //composite masking ver.
|
||||
void fillLinear(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwMask maskOp, uint8_t opacity); //direct masking ver.
|
||||
void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, uint8_t a); //blending ver.
|
||||
void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, SwBlender op2, uint8_t a); //blending + BlendingMethod(op2) ver.
|
||||
void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwAlpha alpha, uint8_t csize, uint8_t opacity); //masking ver.
|
||||
void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwAlpha alpha, uint8_t csize, uint8_t opacity); //matting ver.
|
||||
|
||||
void fillRadial(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32_t len, SwMask op, uint8_t a); //composite masking ver.
|
||||
void fillRadial(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwMask op, uint8_t a) ; //direct masking ver.
|
||||
void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, uint8_t a); //blending ver.
|
||||
void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, SwBlender op2, uint8_t a); //blending + BlendingMethod(op2) ver.
|
||||
void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwAlpha alpha, uint8_t csize, uint8_t opacity); //masking ver.
|
||||
void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwAlpha alpha, uint8_t csize, uint8_t opacity); //matting ver.
|
||||
|
||||
SwRleData* rleRender(SwRleData* rle, const SwOutline* outline, const SwBBox& renderRegion, bool antiAlias);
|
||||
SwRleData* rleRender(const SwBBox* bbox);
|
||||
|
|
|
@ -22,16 +22,46 @@
|
|||
|
||||
#include "tvgMath.h"
|
||||
#include "tvgSwCommon.h"
|
||||
|
||||
#include "tvgFill.h"
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
#define RADIAL_A_THRESHOLD 0.0005f
|
||||
#define GRADIENT_STOP_SIZE 1024
|
||||
#define FIXPT_BITS 8
|
||||
#define FIXPT_SIZE (1<<FIXPT_BITS)
|
||||
|
||||
/*
|
||||
* quadratic equation with the following coefficients (rx and ry defined in the _calculateCoefficients()):
|
||||
* A = a // fill->radial.a
|
||||
* B = 2 * (dr * fr + rx * dx + ry * dy)
|
||||
* C = fr^2 - rx^2 - ry^2
|
||||
* Derivatives are computed with respect to dx.
|
||||
* This procedure aims to optimize and eliminate the need to calculate all values from the beginning
|
||||
* for consecutive x values with a constant y. The Taylor series expansions are computed as long as
|
||||
* its terms are non-zero.
|
||||
*/
|
||||
static void _calculateCoefficients(const SwFill* fill, uint32_t x, uint32_t y, float& b, float& deltaB, float& det, float& deltaDet, float& deltaDeltaDet)
|
||||
{
|
||||
auto radial = &fill->radial;
|
||||
|
||||
auto rx = (x + 0.5f) * radial->a11 + (y + 0.5f) * radial->a12 + radial->a13 - radial->fx;
|
||||
auto ry = (x + 0.5f) * radial->a21 + (y + 0.5f) * radial->a22 + radial->a23 - radial->fy;
|
||||
|
||||
b = (radial->dr * radial->fr + rx * radial->dx + ry * radial->dy) * radial->invA;
|
||||
deltaB = (radial->a11 * radial->dx + radial->a21 * radial->dy) * radial->invA;
|
||||
|
||||
auto rr = rx * rx + ry * ry;
|
||||
auto deltaRr = 2.0f * (rx * radial->a11 + ry * radial->a21) * radial->invA;
|
||||
auto deltaDeltaRr = 2.0f * (radial->a11 * radial->a11 + radial->a21 * radial->a21) * radial->invA;
|
||||
|
||||
det = b * b + (rr - radial->fr * radial->fr) * radial->invA;
|
||||
deltaDet = 2.0f * b * deltaB + deltaB * deltaB + deltaRr + deltaDeltaRr;
|
||||
deltaDeltaDet = 2.0f * deltaB * deltaB + deltaDeltaRr;
|
||||
}
|
||||
|
||||
|
||||
static bool _updateColorTable(SwFill* fill, const Fill* fdata, const SwSurface* surface, uint8_t opacity)
|
||||
{
|
||||
|
@ -146,46 +176,62 @@ bool _prepareLinear(SwFill* fill, const LinearGradient* linear, const Matrix* tr
|
|||
|
||||
bool _prepareRadial(SwFill* fill, const RadialGradient* radial, const Matrix* transform)
|
||||
{
|
||||
float radius, cx, cy;
|
||||
if (radial->radial(&cx, &cy, &radius) != Result::Success) return false;
|
||||
if (radius < FLT_EPSILON) return true;
|
||||
auto cx = P(radial)->cx;
|
||||
auto cy = P(radial)->cy;
|
||||
auto r = P(radial)->r;
|
||||
auto fx = P(radial)->fx;
|
||||
auto fy = P(radial)->fy;
|
||||
auto fr = P(radial)->fr;
|
||||
|
||||
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<int32_t>(t * FIXPT_SIZE))));
|
||||
for (uint32_t i = 0; i < len; ++i, ++dst) {
|
||||
*dst = maskOp(src, *dst, ~src);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
auto vMax = static_cast<float>(INT32_MAX >> (FIXPT_BITS + 1));
|
||||
auto vMin = -vMax;
|
||||
auto v = t + (inc * len);
|
||||
|
||||
//we can use fixed point math
|
||||
if (v < vMax && v > vMin) {
|
||||
auto t2 = static_cast<int32_t>(t * FIXPT_SIZE);
|
||||
auto inc2 = static_cast<int32_t>(inc * FIXPT_SIZE);
|
||||
for (uint32_t j = 0; j < len; ++j, ++dst) {
|
||||
auto src = MULTIPLY(_fixedPixel(fill, t2), a);
|
||||
*dst = maskOp(src, *dst, ~src);
|
||||
t2 += inc2;
|
||||
}
|
||||
//we have to fallback to float math
|
||||
} else {
|
||||
uint32_t counter = 0;
|
||||
while (counter++ < len) {
|
||||
auto src = MULTIPLY(_pixel(fill, t / GRADIENT_STOP_SIZE), a);
|
||||
*dst = maskOp(src, *dst, ~src);
|
||||
++dst;
|
||||
t += inc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void fillLinear(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwMask maskOp, uint8_t a)
|
||||
{
|
||||
//Rotation
|
||||
float rx = x + 0.5f;
|
||||
float ry = y + 0.5f;
|
||||
float t = (fill->linear.dx * rx + fill->linear.dy * ry + fill->linear.offset) * (GRADIENT_STOP_SIZE - 1);
|
||||
float inc = (fill->linear.dx) * (GRADIENT_STOP_SIZE - 1);
|
||||
|
||||
if (mathZero(inc)) {
|
||||
auto src = A(_fixedPixel(fill, static_cast<int32_t>(t * FIXPT_SIZE)));
|
||||
src = MULTIPLY(src, a);
|
||||
for (uint32_t i = 0; i < len; ++i, ++dst, ++cmp) {
|
||||
auto tmp = maskOp(src, *cmp, 0);
|
||||
*dst = tmp + MULTIPLY(*dst, ~tmp);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
auto vMax = static_cast<float>(INT32_MAX >> (FIXPT_BITS + 1));
|
||||
auto vMin = -vMax;
|
||||
auto v = t + (inc * len);
|
||||
|
||||
//we can use fixed point math
|
||||
if (v < vMax && v > vMin) {
|
||||
auto t2 = static_cast<int32_t>(t * FIXPT_SIZE);
|
||||
auto inc2 = static_cast<int32_t>(inc * FIXPT_SIZE);
|
||||
for (uint32_t j = 0; j < len; ++j, ++dst, ++cmp) {
|
||||
auto src = MULTIPLY(a, A(_fixedPixel(fill, t2)));
|
||||
auto tmp = maskOp(src, *cmp, 0);
|
||||
*dst = tmp + MULTIPLY(*dst, ~tmp);
|
||||
t2 += inc2;
|
||||
}
|
||||
//we have to fallback to float math
|
||||
} else {
|
||||
uint32_t counter = 0;
|
||||
while (counter++ < len) {
|
||||
auto src = MULTIPLY(A(_pixel(fill, t / GRADIENT_STOP_SIZE)), a);
|
||||
auto tmp = maskOp(src, *cmp, 0);
|
||||
*dst = tmp + MULTIPLY(*dst, ~tmp);
|
||||
++dst;
|
||||
++cmp;
|
||||
t += inc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, uint8_t a)
|
||||
{
|
||||
//Rotation
|
||||
|
|
|
@ -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);
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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<int32_t>(image->stride);
|
||||
int32_t sh = image->h;
|
||||
int32_t x1, x2, ar, ab, iru, irv, px, ay;
|
||||
int32_t vv = 0, uu = 0;
|
||||
int32_t minx = INT32_MAX, maxx = INT32_MIN;
|
||||
float dx, u, v, iptr;
|
||||
auto cbuffer = surface->compositor->image.buf32;
|
||||
SwSpan* span = nullptr; //used only when rle based.
|
||||
|
||||
if (!_arrange(image, region, yStart, yEnd)) return;
|
||||
|
||||
//Clear out of the Polygon vertical ranges
|
||||
auto size = surface->compositor->bbox.max.x - surface->compositor->bbox.min.x;
|
||||
if (dirFlag == 1) { //left top case.
|
||||
for(int y = surface->compositor->bbox.min.y; y < yStart; ++y) {
|
||||
rasterPixel32(surface->compositor->image.buf32 + y * surface->compositor->image.stride, 0, surface->compositor->bbox.min.x, size);
|
||||
}
|
||||
}
|
||||
if (dirFlag == 4) { //right bottom case.
|
||||
for(int y = yEnd; y < surface->compositor->bbox.max.y; ++y) {
|
||||
rasterPixel32(surface->compositor->image.buf32 + y * surface->compositor->image.stride, 0, surface->compositor->bbox.min.x, size);
|
||||
}
|
||||
}
|
||||
|
||||
//Loop through all lines in the segment
|
||||
uint32_t spanIdx = 0;
|
||||
|
||||
if (region) {
|
||||
minx = region->min.x;
|
||||
maxx = region->max.x;
|
||||
} else {
|
||||
span = image->rle->spans;
|
||||
while (span->y < yStart) {
|
||||
++span;
|
||||
++spanIdx;
|
||||
}
|
||||
}
|
||||
|
||||
for (int32_t y = yStart; y < yEnd; ++y) {
|
||||
auto cmp = &cbuffer[y * surface->compositor->image.stride];
|
||||
x1 = (int32_t)_xa;
|
||||
x2 = (int32_t)_xb;
|
||||
|
||||
if (!region) {
|
||||
minx = INT32_MAX;
|
||||
maxx = INT32_MIN;
|
||||
//one single row, could be consisted of multiple spans.
|
||||
while (span->y == y && spanIdx < image->rle->size) {
|
||||
if (minx > span->x) minx = span->x;
|
||||
if (maxx < span->x + span->len) maxx = span->x + span->len;
|
||||
++span;
|
||||
++spanIdx;
|
||||
}
|
||||
}
|
||||
|
||||
if (x1 < minx) x1 = minx;
|
||||
if (x2 > maxx) x2 = maxx;
|
||||
|
||||
//Anti-Aliasing frames
|
||||
//FIXME: this aa must be applied before masking op
|
||||
ay = y - aaSpans->yStart;
|
||||
if (aaSpans->lines[ay].x[0] > x1) aaSpans->lines[ay].x[0] = x1;
|
||||
if (aaSpans->lines[ay].x[1] < x2) aaSpans->lines[ay].x[1] = x2;
|
||||
|
||||
//Range allowed
|
||||
if ((x2 - x1) >= 1 && (x1 < maxx) && (x2 > minx)) {
|
||||
for (int32_t x = surface->compositor->bbox.min.x; x < surface->compositor->bbox.max.x; ++x) {
|
||||
//Range allowed
|
||||
if (x >= x1 && x < x2) {
|
||||
//Perform subtexel pre-stepping on UV
|
||||
dx = 1 - (_xa - x1);
|
||||
u = _ua + dx * _dudx;
|
||||
v = _va + dx * _dvdx;
|
||||
if ((uint32_t)v >= image->h) {
|
||||
cmp[x] = 0;
|
||||
} else {
|
||||
if (opacity == 255) {
|
||||
uu = (int) u;
|
||||
if (uu >= sw) continue;
|
||||
vv = (int) v;
|
||||
if (vv >= sh) continue;
|
||||
|
||||
ar = (int)(255 * (1 - modff(u, &iptr)));
|
||||
ab = (int)(255 * (1 - modff(v, &iptr)));
|
||||
iru = uu + 1;
|
||||
irv = vv + 1;
|
||||
|
||||
px = *(sbuf + (vv * sw) + uu);
|
||||
|
||||
/* horizontal interpolate */
|
||||
if (iru < sw) {
|
||||
/* right pixel */
|
||||
int px2 = *(sbuf + (vv * sw) + iru);
|
||||
px = INTERPOLATE(px, px2, ar);
|
||||
}
|
||||
/* vertical interpolate */
|
||||
if (irv < sh) {
|
||||
/* bottom pixel */
|
||||
int px2 = *(sbuf + (irv * sw) + uu);
|
||||
|
||||
/* horizontal interpolate */
|
||||
if (iru < sw) {
|
||||
/* bottom right pixel */
|
||||
int px3 = *(sbuf + (irv * sw) + iru);
|
||||
px2 = INTERPOLATE(px2, px3, ar);
|
||||
}
|
||||
px = INTERPOLATE(px, px2, ab);
|
||||
}
|
||||
cmp[x] = ALPHA_BLEND(cmp[x], A(px));
|
||||
|
||||
//Step UV horizontally
|
||||
u += _dudx;
|
||||
v += _dvdx;
|
||||
} else {
|
||||
uu = (int) u;
|
||||
if (uu >= sw) continue;
|
||||
vv = (int) v;
|
||||
if (vv >= sh) continue;
|
||||
|
||||
ar = (int)(255 * (1 - modff(u, &iptr)));
|
||||
ab = (int)(255 * (1 - modff(v, &iptr)));
|
||||
iru = uu + 1;
|
||||
irv = vv + 1;
|
||||
|
||||
px = *(sbuf + (vv * sw) + uu);
|
||||
|
||||
/* horizontal interpolate */
|
||||
if (iru < sw) {
|
||||
/* right pixel */
|
||||
int px2 = *(sbuf + (vv * sw) + iru);
|
||||
px = INTERPOLATE(px, px2, ar);
|
||||
}
|
||||
/* vertical interpolate */
|
||||
if (irv < sh) {
|
||||
/* bottom pixel */
|
||||
int px2 = *(sbuf + (irv * sw) + uu);
|
||||
|
||||
/* horizontal interpolate */
|
||||
if (iru < sw) {
|
||||
/* bottom right pixel */
|
||||
int px3 = *(sbuf + (irv * sw) + iru);
|
||||
px2 = INTERPOLATE(px2, px3, ar);
|
||||
}
|
||||
px = INTERPOLATE(px, px2, ab);
|
||||
}
|
||||
cmp[x] = ALPHA_BLEND(cmp[x], MULTIPLY(A(px), opacity));
|
||||
|
||||
//Step UV horizontally
|
||||
u += _dudx;
|
||||
v += _dvdx;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//Clear out of polygon horizontal range
|
||||
if (x < x1 && (dirFlag == 1 || dirFlag == 2)) cmp[x] = 0;
|
||||
else if (x >= x2 && (dirFlag == 3 || dirFlag == 4)) cmp[x] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
//Step along both edges
|
||||
_xa += _dxdya;
|
||||
_xb += _dxdyb;
|
||||
_ua += _dudya;
|
||||
_va += _dvdya;
|
||||
}
|
||||
xa = _xa;
|
||||
xb = _xb;
|
||||
ua = _ua;
|
||||
va = _va;
|
||||
}
|
||||
|
||||
|
||||
static void _rasterMaskedPolygonImageSegmentDup(SwSurface* surface, const SwImage* image, const SwBBox* region, SwBlender maskOp, SwBlender amaskOp, int yStart, int yEnd, AASpans* aaSpans, uint8_t opacity)
|
||||
{
|
||||
float _dudx = dudx, _dvdx = dvdx;
|
||||
float _dxdya = dxdya, _dxdyb = dxdyb, _dudya = dudya, _dvdya = dvdya;
|
||||
float _xa = xa, _xb = xb, _ua = ua, _va = va;
|
||||
auto sbuf = image->buf32;
|
||||
auto sbuf = image->buf8;
|
||||
int32_t sw = static_cast<int32_t>(image->stride);
|
||||
int32_t sh = image->h;
|
||||
int32_t x1, x2, x, y, ar, ab, iru, irv, px, ay;
|
||||
|
@ -262,7 +89,7 @@ static void _rasterMaskedPolygonImageSegmentDup(SwSurface* surface, const SwImag
|
|||
float dx, u, v, iptr;
|
||||
SwSpan* span = nullptr; //used only when rle based.
|
||||
|
||||
if (!_arrange(image, region, yStart, yEnd)) return;
|
||||
if (!_arrange(image, region, yStart, yEnd)) return false;
|
||||
|
||||
//Loop through all lines in the segment
|
||||
uint32_t spanIdx = 0;
|
||||
|
@ -313,7 +140,8 @@ static void _rasterMaskedPolygonImageSegmentDup(SwSurface* surface, const SwImag
|
|||
|
||||
x = x1;
|
||||
|
||||
auto cmp = &surface->compositor->image.buf32[y * surface->compositor->image.stride + x1];
|
||||
auto cmp = &surface->compositor->image.buf8[y * surface->compositor->image.stride + x1];
|
||||
auto dst = &surface->buf8[y * surface->stride + x1];
|
||||
|
||||
if (opacity == 255) {
|
||||
//Draw horizontal line
|
||||
|
@ -349,7 +177,13 @@ static void _rasterMaskedPolygonImageSegmentDup(SwSurface* surface, const SwImag
|
|||
}
|
||||
px = INTERPOLATE(px, px2, ab);
|
||||
}
|
||||
*cmp = maskOp(px, *cmp, IA(px));
|
||||
if (direct) {
|
||||
auto tmp = maskOp(px, *cmp, 0); //not use alpha
|
||||
*dst = tmp + MULTIPLY(*dst, ~tmp);
|
||||
++dst;
|
||||
} else {
|
||||
*cmp = maskOp(px, *cmp, ~px);
|
||||
}
|
||||
++cmp;
|
||||
|
||||
//Step UV horizontally
|
||||
|
@ -392,7 +226,15 @@ static void _rasterMaskedPolygonImageSegmentDup(SwSurface* surface, const SwImag
|
|||
}
|
||||
px = INTERPOLATE(px, px2, ab);
|
||||
}
|
||||
*cmp = amaskOp(px, *cmp, opacity);
|
||||
|
||||
if (direct) {
|
||||
auto tmp = maskOp(MULTIPLY(px, opacity), *cmp, 0);
|
||||
*dst = tmp + MULTIPLY(*dst, ~tmp);
|
||||
++dst;
|
||||
} else {
|
||||
auto tmp = MULTIPLY(px, opacity);
|
||||
*cmp = maskOp(tmp, *cmp, ~px);
|
||||
}
|
||||
++cmp;
|
||||
|
||||
//Step UV horizontally
|
||||
|
@ -418,17 +260,9 @@ static void _rasterMaskedPolygonImageSegmentDup(SwSurface* surface, const SwImag
|
|||
xb = _xb;
|
||||
ua = _ua;
|
||||
va = _va;
|
||||
}
|
||||
|
||||
|
||||
static void _rasterMaskedPolygonImageSegment(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, AASpans* aaSpans, uint8_t opacity, uint8_t dirFlag = 0)
|
||||
{
|
||||
if (surface->compositor->method == CompositeMethod::IntersectMask) {
|
||||
_rasterMaskedPolygonImageSegmentInt(surface, image, region, yStart, yEnd, aaSpans, opacity, dirFlag);
|
||||
} else if (auto opMask = _getMaskOp(surface->compositor->method)) {
|
||||
//Other Masking operations: Add, Subtract, Difference ...
|
||||
_rasterMaskedPolygonImageSegmentDup(surface, image, region, opMask, _getAMaskOp(surface->compositor->method), yStart, yEnd, aaSpans, opacity);
|
||||
}
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
@ -1256,6 +1090,11 @@ static bool _apply(SwSurface* surface, AASpans* aaSpans)
|
|||
*/
|
||||
static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const Matrix* transform, const SwBBox* region, uint8_t opacity)
|
||||
{
|
||||
if (surface->channelSize == sizeof(uint8_t)) {
|
||||
TVGERR("SW_ENGINE", "Not supported grayscale Textmap polygon!");
|
||||
return false;
|
||||
}
|
||||
|
||||
//Exceptions: No dedicated drawing area?
|
||||
if ((!image->rle && !region) || (image->rle && image->rle->size == 0)) return false;
|
||||
|
||||
|
@ -1294,6 +1133,11 @@ static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const
|
|||
|
||||
_rasterPolygonImage(surface, image, region, polygon, aaSpans, opacity);
|
||||
|
||||
#if 0
|
||||
if (_compositing(surface) && _masking(surface) && !_direct(surface->compositor->method)) {
|
||||
_compositeMaskImage(surface, &surface->compositor->image, surface->compositor->bbox);
|
||||
}
|
||||
#endif
|
||||
return _apply(surface, aaSpans);
|
||||
}
|
||||
|
||||
|
@ -1313,6 +1157,11 @@ static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const
|
|||
*/
|
||||
static bool _rasterTexmapPolygonMesh(SwSurface* surface, const SwImage* image, const RenderMesh* mesh, const Matrix* transform, const SwBBox* region, uint8_t opacity)
|
||||
{
|
||||
if (surface->channelSize == sizeof(uint8_t)) {
|
||||
TVGERR("SW_ENGINE", "Not supported grayscale Textmap polygon mesh!");
|
||||
return false;
|
||||
}
|
||||
|
||||
//Exceptions: No dedicated drawing area?
|
||||
if ((!image->rle && !region) || (image->rle && image->rle->size == 0)) return false;
|
||||
|
||||
|
@ -1342,15 +1191,17 @@ static bool _rasterTexmapPolygonMesh(SwSurface* surface, const SwImage* image, c
|
|||
}
|
||||
|
||||
// Get AA spans and step polygons again to draw
|
||||
auto aaSpans = _AASpans(ys, ye, image, region);
|
||||
if (aaSpans) {
|
||||
if (auto aaSpans = _AASpans(ys, ye, image, region)) {
|
||||
for (uint32_t i = 0; i < mesh->triangleCnt; i++) {
|
||||
_rasterPolygonImage(surface, image, region, transformedTris[i], aaSpans, opacity);
|
||||
}
|
||||
// Apply to surface (note: frees the AA spans)
|
||||
#if 0
|
||||
if (_compositing(surface) && _masking(surface) && !_direct(surface->compositor->method)) {
|
||||
_compositeMaskImage(surface, &surface->compositor->image, surface->compositor->bbox);
|
||||
}
|
||||
#endif
|
||||
_apply(surface, aaSpans);
|
||||
}
|
||||
free(transformedTris);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -21,9 +21,8 @@
|
|||
*/
|
||||
|
||||
#include "tvgSwCommon.h"
|
||||
#include "tvgMath.h"
|
||||
#include "tvgBezier.h"
|
||||
#include <float.h>
|
||||
#include <math.h>
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
|
@ -108,7 +107,7 @@ static void _outlineClose(SwOutline& outline)
|
|||
if (outline.pts.count == i) return;
|
||||
|
||||
//Close the path
|
||||
outline.pts.push(outline.pts.data[i]);
|
||||
outline.pts.push(outline.pts[i]);
|
||||
outline.types.push(SW_CURVE_TYPE_POINT);
|
||||
outline.closed.push(true);
|
||||
}
|
||||
|
@ -127,14 +126,18 @@ static void _dashLineTo(SwDashStroke& dash, const Point* to, const Matrix* trans
|
|||
}
|
||||
} else {
|
||||
while (len > dash.curLen) {
|
||||
len -= dash.curLen;
|
||||
Line left, right;
|
||||
_lineSplitAt(cur, dash.curLen, left, right);;
|
||||
dash.curIdx = (dash.curIdx + 1) % dash.cnt;
|
||||
if (!dash.curOpGap) {
|
||||
_outlineMoveTo(*dash.outline, &left.pt1, transform);
|
||||
_outlineLineTo(*dash.outline, &left.pt2, transform);
|
||||
if (dash.curLen > 0) {
|
||||
len -= dash.curLen;
|
||||
_lineSplitAt(cur, dash.curLen, left, right);
|
||||
if (!dash.curOpGap) {
|
||||
_outlineMoveTo(*dash.outline, &left.pt1, transform);
|
||||
_outlineLineTo(*dash.outline, &left.pt2, transform);
|
||||
}
|
||||
} else {
|
||||
right = cur;
|
||||
}
|
||||
dash.curIdx = (dash.curIdx + 1) % dash.cnt;
|
||||
dash.curLen = dash.pattern[dash.curIdx];
|
||||
dash.curOpGap = !dash.curOpGap;
|
||||
cur = right;
|
||||
|
@ -169,16 +172,22 @@ static void _dashCubicTo(SwDashStroke& dash, const Point* ctrl1, const Point* ct
|
|||
_outlineCubicTo(*dash.outline, ctrl1, ctrl2, to, transform);
|
||||
}
|
||||
} else {
|
||||
bool begin = true; //starting with move_to
|
||||
while (len > dash.curLen) {
|
||||
Bezier left, right;
|
||||
len -= dash.curLen;
|
||||
bezSplitAt(cur, dash.curLen, left, right);
|
||||
if (!dash.curOpGap) {
|
||||
// leftovers from a previous command don't require moveTo
|
||||
if (dash.pattern[dash.curIdx] - dash.curLen < FLT_EPSILON) {
|
||||
_outlineMoveTo(*dash.outline, &left.start, transform);
|
||||
if (dash.curLen > 0) {
|
||||
len -= dash.curLen;
|
||||
bezSplitAt(cur, dash.curLen, left, right);
|
||||
if (!dash.curOpGap) {
|
||||
// leftovers from a previous command don't require moveTo
|
||||
if (begin || dash.pattern[dash.curIdx] - dash.curLen < FLT_EPSILON) {
|
||||
_outlineMoveTo(*dash.outline, &left.start, transform);
|
||||
begin = false;
|
||||
}
|
||||
_outlineCubicTo(*dash.outline, &left.ctrl1, &left.ctrl2, &left.end, transform);
|
||||
}
|
||||
_outlineCubicTo(*dash.outline, &left.ctrl1, &left.ctrl2, &left.end, transform);
|
||||
} else {
|
||||
right = cur;
|
||||
}
|
||||
dash.curIdx = (dash.curIdx + 1) % dash.cnt;
|
||||
dash.curLen = dash.pattern[dash.curIdx];
|
||||
|
@ -203,11 +212,10 @@ static void _dashCubicTo(SwDashStroke& dash, const Point* ctrl1, const Point* ct
|
|||
}
|
||||
|
||||
|
||||
static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* transform)
|
||||
static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* transform, float length)
|
||||
{
|
||||
const PathCommand* cmds = rshape->path.cmds.data;
|
||||
auto cmdCnt = rshape->path.cmds.count;
|
||||
|
||||
const Point* pts = rshape->path.pts.data;
|
||||
auto ptsCnt = rshape->path.pts.count;
|
||||
|
||||
|
@ -215,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<float*>(pattern);
|
||||
dash.outline = static_cast<SwOutline*>(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<SwOutline*>(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<uint32_t>(outlinePtsCnt - closeCnt) > ptsCnt) {
|
||||
TVGERR("SW_ENGINE", "Wrong a pair of the commands & points - required(%d), current(%d)", outlinePtsCnt - closeCnt, ptsCnt);
|
||||
return false;
|
||||
}
|
||||
|
||||
++outlinePtsCnt; //for close
|
||||
++outlineCntrsCnt; //for end
|
||||
|
||||
shape->outline = mpoolReqOutline(mpool, tid);
|
||||
auto outline = shape->outline;
|
||||
|
||||
outline->pts.grow(outlinePtsCnt);
|
||||
outline->types.grow(outlinePtsCnt);
|
||||
outline->cntrs.grow(outlineCntrsCnt);
|
||||
//OPTIMIZE: we can directly copy the path points when the close is occupied with a point.
|
||||
outline->pts.grow(ptsCnt + closeCnt + 1);
|
||||
outline->types.grow(ptsCnt + closeCnt + 1);
|
||||
outline->cntrs.grow(moveCnt + 1);
|
||||
|
||||
//Dash outlines are always opened.
|
||||
//Only normal outlines use this information, it sholud be same to their contour counts.
|
||||
|
@ -514,12 +570,14 @@ bool shapeGenStrokeRle(SwShape* shape, const RenderShape* rshape, const Matrix*
|
|||
bool freeOutline = false;
|
||||
bool ret = true;
|
||||
|
||||
//Dash Style Stroke
|
||||
if (rshape->strokeDash(nullptr) > 0) {
|
||||
shapeOutline = _genDashOutline(rshape, transform);
|
||||
auto length = rshape->strokeTrim() ? _outlineLength(rshape) : 0.0f;
|
||||
|
||||
//Dash style (+trimming)
|
||||
if (rshape->stroke->dashCnt > 0 || length > 0) {
|
||||
shapeOutline = _genDashOutline(rshape, transform, length);
|
||||
if (!shapeOutline) return false;
|
||||
freeOutline = true;
|
||||
//Normal Style stroke
|
||||
//Normal style
|
||||
} else {
|
||||
if (!shape->outline) {
|
||||
if (!_genOutline(shape, rshape, transform, mpool, tid, false)) return false;
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
//#include "tvgAnimationImpl.h"
|
||||
#include "tvgCommon.h"
|
||||
#include "tvgFrameModule.h"
|
||||
#include "tvgPaint.h"
|
||||
#include "tvgPictureImpl.h"
|
||||
|
||||
/************************************************************************/
|
||||
|
@ -31,8 +32,20 @@
|
|||
|
||||
struct Animation::Impl
|
||||
{
|
||||
//TODO: Memory Safety
|
||||
Picture picture;
|
||||
Picture* picture = nullptr;
|
||||
|
||||
Impl()
|
||||
{
|
||||
picture = Picture::gen().release();
|
||||
static_cast<Paint*>(picture)->pImpl->ref();
|
||||
}
|
||||
|
||||
~Impl()
|
||||
{
|
||||
if (static_cast<Paint*>(picture)->pImpl->unref() == 0) {
|
||||
delete(picture);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/************************************************************************/
|
||||
|
@ -41,19 +54,18 @@ struct Animation::Impl
|
|||
|
||||
Animation::~Animation()
|
||||
{
|
||||
|
||||
delete(pImpl);
|
||||
}
|
||||
|
||||
|
||||
Animation::Animation() : pImpl(new Impl)
|
||||
{
|
||||
pImpl->picture.pImpl->animated = true;
|
||||
}
|
||||
|
||||
|
||||
Result Animation::frame(uint32_t no) noexcept
|
||||
{
|
||||
auto loader = pImpl->picture.pImpl->loader.get();
|
||||
auto loader = pImpl->picture->pImpl->loader.get();
|
||||
|
||||
if (!loader) return Result::InsufficientCondition;
|
||||
if (!loader->animatable()) return Result::NonSupport;
|
||||
|
@ -65,13 +77,13 @@ Result Animation::frame(uint32_t no) noexcept
|
|||
|
||||
Picture* Animation::picture() const noexcept
|
||||
{
|
||||
return &pImpl->picture;
|
||||
return pImpl->picture;
|
||||
}
|
||||
|
||||
|
||||
uint32_t Animation::curFrame() const noexcept
|
||||
{
|
||||
auto loader = pImpl->picture.pImpl->loader.get();
|
||||
auto loader = pImpl->picture->pImpl->loader.get();
|
||||
|
||||
if (!loader) return 0;
|
||||
if (!loader->animatable()) return 0;
|
||||
|
@ -82,7 +94,7 @@ uint32_t Animation::curFrame() const noexcept
|
|||
|
||||
uint32_t Animation::totalFrame() const noexcept
|
||||
{
|
||||
auto loader = pImpl->picture.pImpl->loader.get();
|
||||
auto loader = pImpl->picture->pImpl->loader.get();
|
||||
|
||||
if (!loader) return 0;
|
||||
if (!loader->animatable()) return 0;
|
||||
|
@ -93,7 +105,7 @@ uint32_t Animation::totalFrame() const noexcept
|
|||
|
||||
float Animation::duration() const noexcept
|
||||
{
|
||||
auto loader = pImpl->picture.pImpl->loader.get();
|
||||
auto loader = pImpl->picture->pImpl->loader.get();
|
||||
|
||||
if (!loader) return 0;
|
||||
if (!loader->animatable()) return 0;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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_
|
||||
|
|
|
@ -26,6 +26,50 @@
|
|||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
Fill* RadialGradient::Impl::duplicate()
|
||||
{
|
||||
auto ret = RadialGradient::gen();
|
||||
if (!ret) return nullptr;
|
||||
|
||||
ret->pImpl->cx = cx;
|
||||
ret->pImpl->cy = cy;
|
||||
ret->pImpl->r = r;
|
||||
ret->pImpl->fx = fx;
|
||||
ret->pImpl->fy = fy;
|
||||
ret->pImpl->fr = fr;
|
||||
|
||||
return ret.release();
|
||||
}
|
||||
|
||||
|
||||
Result RadialGradient::Impl::radial(float cx, float cy, float r, float fx, float fy, float fr)
|
||||
{
|
||||
if (r < 0 || fr < 0) return Result::InvalidArguments;
|
||||
|
||||
this->cx = cx;
|
||||
this->cy = cy;
|
||||
this->r = r;
|
||||
this->fx = fx;
|
||||
this->fy = fy;
|
||||
this->fr = fr;
|
||||
|
||||
return Result::Success;
|
||||
};
|
||||
|
||||
|
||||
Fill* LinearGradient::Impl::duplicate()
|
||||
{
|
||||
auto ret = LinearGradient::gen();
|
||||
if (!ret) return nullptr;
|
||||
|
||||
ret->pImpl->x1 = x1;
|
||||
ret->pImpl->y1 = y1;
|
||||
ret->pImpl->x2 = x2;
|
||||
ret->pImpl->y2 = y2;
|
||||
|
||||
return ret.release();
|
||||
};
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
|
@ -110,7 +154,97 @@ Fill* Fill::duplicate() const noexcept
|
|||
return pImpl->duplicate();
|
||||
}
|
||||
|
||||
|
||||
uint32_t Fill::identifier() const noexcept
|
||||
{
|
||||
return pImpl->id;
|
||||
}
|
||||
|
||||
|
||||
RadialGradient::RadialGradient():pImpl(new Impl())
|
||||
{
|
||||
Fill::pImpl->id = TVG_CLASS_ID_RADIAL;
|
||||
Fill::pImpl->method(new FillDup<RadialGradient::Impl>(pImpl));
|
||||
}
|
||||
|
||||
|
||||
RadialGradient::~RadialGradient()
|
||||
{
|
||||
delete(pImpl);
|
||||
}
|
||||
|
||||
|
||||
Result RadialGradient::radial(float cx, float cy, float r) noexcept
|
||||
{
|
||||
return pImpl->radial(cx, cy, r, cx, cy, 0.0f);
|
||||
}
|
||||
|
||||
|
||||
Result RadialGradient::radial(float* cx, float* cy, float* r) const noexcept
|
||||
{
|
||||
if (cx) *cx = pImpl->cx;
|
||||
if (cy) *cy = pImpl->cy;
|
||||
if (r) *r = pImpl->r;
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
unique_ptr<RadialGradient> RadialGradient::gen() noexcept
|
||||
{
|
||||
return unique_ptr<RadialGradient>(new RadialGradient);
|
||||
}
|
||||
|
||||
|
||||
uint32_t RadialGradient::identifier() noexcept
|
||||
{
|
||||
return TVG_CLASS_ID_RADIAL;
|
||||
}
|
||||
|
||||
|
||||
LinearGradient::LinearGradient():pImpl(new Impl())
|
||||
{
|
||||
Fill::pImpl->id = TVG_CLASS_ID_LINEAR;
|
||||
Fill::pImpl->method(new FillDup<LinearGradient::Impl>(pImpl));
|
||||
}
|
||||
|
||||
|
||||
LinearGradient::~LinearGradient()
|
||||
{
|
||||
delete(pImpl);
|
||||
}
|
||||
|
||||
|
||||
Result LinearGradient::linear(float x1, float y1, float x2, float y2) noexcept
|
||||
{
|
||||
pImpl->x1 = x1;
|
||||
pImpl->y1 = y1;
|
||||
pImpl->x2 = x2;
|
||||
pImpl->y2 = y2;
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
Result LinearGradient::linear(float* x1, float* y1, float* x2, float* y2) const noexcept
|
||||
{
|
||||
if (x1) *x1 = pImpl->x1;
|
||||
if (x2) *x2 = pImpl->x2;
|
||||
if (y1) *y1 = pImpl->y1;
|
||||
if (y2) *y2 = pImpl->y2;
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
unique_ptr<LinearGradient> LinearGradient::gen() noexcept
|
||||
{
|
||||
return unique_ptr<LinearGradient>(new LinearGradient);
|
||||
}
|
||||
|
||||
|
||||
uint32_t LinearGradient::identifier() noexcept
|
||||
{
|
||||
return TVG_CLASS_ID_LINEAR;
|
||||
}
|
||||
|
||||
|
|
|
@ -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_
|
||||
|
|
|
@ -1,100 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <float.h>
|
||||
#include <math.h>
|
||||
#include "tvgFill.h"
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
struct LinearGradient::Impl
|
||||
{
|
||||
float x1 = 0;
|
||||
float y1 = 0;
|
||||
float x2 = 0;
|
||||
float y2 = 0;
|
||||
|
||||
Fill* duplicate()
|
||||
{
|
||||
auto ret = LinearGradient::gen();
|
||||
if (!ret) return nullptr;
|
||||
|
||||
ret->pImpl->x1 = x1;
|
||||
ret->pImpl->y1 = y1;
|
||||
ret->pImpl->x2 = x2;
|
||||
ret->pImpl->y2 = y2;
|
||||
|
||||
return ret.release();
|
||||
}
|
||||
};
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
LinearGradient::LinearGradient():pImpl(new Impl())
|
||||
{
|
||||
Fill::pImpl->id = TVG_CLASS_ID_LINEAR;
|
||||
Fill::pImpl->method(new FillDup<LinearGradient::Impl>(pImpl));
|
||||
}
|
||||
|
||||
|
||||
LinearGradient::~LinearGradient()
|
||||
{
|
||||
delete(pImpl);
|
||||
}
|
||||
|
||||
|
||||
Result LinearGradient::linear(float x1, float y1, float x2, float y2) noexcept
|
||||
{
|
||||
pImpl->x1 = x1;
|
||||
pImpl->y1 = y1;
|
||||
pImpl->x2 = x2;
|
||||
pImpl->y2 = y2;
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
Result LinearGradient::linear(float* x1, float* y1, float* x2, float* y2) const noexcept
|
||||
{
|
||||
if (x1) *x1 = pImpl->x1;
|
||||
if (x2) *x2 = pImpl->x2;
|
||||
if (y1) *y1 = pImpl->y1;
|
||||
if (y2) *y2 = pImpl->y2;
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
unique_ptr<LinearGradient> LinearGradient::gen() noexcept
|
||||
{
|
||||
return unique_ptr<LinearGradient>(new LinearGradient);
|
||||
}
|
||||
|
||||
|
||||
uint32_t LinearGradient::identifier() noexcept
|
||||
{
|
||||
return TVG_CLASS_ID_LINEAR;
|
||||
}
|
|
@ -164,7 +164,7 @@ static LoadModule* _findByType(const string& mimeType)
|
|||
|
||||
if (mimeType == "tvg") type = FileType::Tvg;
|
||||
else if (mimeType == "svg" || mimeType == "svg+xml") type = FileType::Svg;
|
||||
else if (mimeType == "lottie" || mimeType == "json") type = FileType::Lottie;
|
||||
else if (mimeType == "lottie") type = FileType::Lottie;
|
||||
else if (mimeType == "raw") type = FileType::Raw;
|
||||
else if (mimeType == "png") type = FileType::Png;
|
||||
else if (mimeType == "jpg" || mimeType == "jpeg") type = FileType::Jpg;
|
||||
|
@ -214,22 +214,24 @@ shared_ptr<LoadModule> LoaderMgr::loader(const string& path, bool* invalid)
|
|||
|
||||
shared_ptr<LoadModule> LoaderMgr::loader(const char* data, uint32_t size, const string& mimeType, bool copy)
|
||||
{
|
||||
//Try first with the given MimeType
|
||||
if (auto loader = _findByType(mimeType)) {
|
||||
if (loader->open(data, size, copy)) {
|
||||
return shared_ptr<LoadModule>(loader);
|
||||
} else {
|
||||
TVGLOG("LOADER", "Given mimetype \"%s\" seems incorrect or not supported. Will try again with other types.", mimeType.c_str());
|
||||
delete(loader);
|
||||
//Try with the given MimeType
|
||||
if (!mimeType.empty()) {
|
||||
if (auto loader = _findByType(mimeType)) {
|
||||
if (loader->open(data, size, copy)) {
|
||||
return shared_ptr<LoadModule>(loader);
|
||||
} else {
|
||||
TVGLOG("LOADER", "Given mimetype \"%s\" seems incorrect or not supported. Will try again with other types.", mimeType.c_str());
|
||||
delete(loader);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Abnormal MimeType. Try with the candidates in the order
|
||||
for (int i = 0; i < static_cast<int>(FileType::Unknown); i++) {
|
||||
auto loader = _find(static_cast<FileType>(i));
|
||||
if (loader) {
|
||||
if (loader->open(data, size, copy)) return shared_ptr<LoadModule>(loader);
|
||||
else delete(loader);
|
||||
//Unkown MimeType. Try with the candidates in the order
|
||||
} else {
|
||||
for (int i = 0; i < static_cast<int>(FileType::Unknown); i++) {
|
||||
auto loader = _find(static_cast<FileType>(i));
|
||||
if (loader) {
|
||||
if (loader->open(data, size, copy)) return shared_ptr<LoadModule>(loader);
|
||||
else delete(loader);
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
|
|
|
@ -166,7 +166,7 @@ bool Paint::Impl::render(RenderMethod& renderer)
|
|||
Create a composition image. */
|
||||
if (compData && compData->method != CompositeMethod::ClipPath && !(compData->target->pImpl->ctxFlag & ContextFlag::FastTrack)) {
|
||||
auto region = smethod->bounds(renderer);
|
||||
if (MASK_OPERATION(compData->method)) region.add(compData->target->pImpl->smethod->bounds(renderer));
|
||||
if (MASK_REGION_MERGING(compData->method)) region.add(compData->target->pImpl->smethod->bounds(renderer));
|
||||
if (region.w == 0 || region.h == 0) return true;
|
||||
cmp = renderer.target(region, COMPOSITE_TO_COLORSPACE(renderer, compData->method));
|
||||
if (renderer.beginComposite(cmp, CompositeMethod::None, 255)) {
|
||||
|
@ -206,23 +206,20 @@ RenderData Paint::Impl::update(RenderMethod& renderer, const RenderTransform* pT
|
|||
auto method = compData->method;
|
||||
target->pImpl->ctxFlag &= ~ContextFlag::FastTrack; //reset
|
||||
|
||||
/* If transform has no rotation factors && ClipPath / AlphaMasking is a simple rectangle,
|
||||
we can avoid regular ClipPath / AlphaMasking sequence but use viewport for performance */
|
||||
/* If the transformation has no rotational factors and the ClipPath/Alpha(InvAlpha)Masking involves a simple rectangle,
|
||||
we can optimize by using the viewport instead of the regular ClipPath/AlphaMasking sequence for improved performance. */
|
||||
auto tryFastTrack = false;
|
||||
if (target->identifier() == TVG_CLASS_ID_SHAPE) {
|
||||
if (method == CompositeMethod::ClipPath) tryFastTrack = true;
|
||||
//OPTIMIZE HERE: Actually, this condition AlphaMask is useless. We can skip it?
|
||||
else if (method == CompositeMethod::AlphaMask) {
|
||||
else {
|
||||
auto shape = static_cast<Shape*>(target);
|
||||
uint8_t a;
|
||||
shape->fillColor(nullptr, nullptr, nullptr, &a);
|
||||
if (a == 255 && shape->opacity() == 255 && !shape->fill()) tryFastTrack = true;
|
||||
//OPTIMIZE HERE: Actually, this condition InvAlphaMask is useless. We can skip it?
|
||||
} else if (method == CompositeMethod::InvAlphaMask) {
|
||||
auto shape = static_cast<Shape*>(target);
|
||||
uint8_t a;
|
||||
shape->fillColor(nullptr, nullptr, nullptr, &a);
|
||||
if ((a == 0 || shape->opacity() == 0) && !shape->fill()) tryFastTrack = true;
|
||||
//no gradient fill & no compositions of the composition target.
|
||||
if (!shape->fill() && !(PP(shape)->compData)) {
|
||||
if (method == CompositeMethod::AlphaMask && a == 255 && PP(shape)->opacity == 255) tryFastTrack = true;
|
||||
else if (method == CompositeMethod::InvAlphaMask && (a == 0 || PP(shape)->opacity == 0)) tryFastTrack = true;
|
||||
}
|
||||
}
|
||||
if (tryFastTrack) {
|
||||
RenderRegion viewport2;
|
||||
|
@ -263,12 +260,12 @@ RenderData Paint::Impl::update(RenderMethod& renderer, const RenderTransform* pT
|
|||
}
|
||||
|
||||
|
||||
bool Paint::Impl::bounds(float* x, float* y, float* w, float* h, bool transformed)
|
||||
bool Paint::Impl::bounds(float* x, float* y, float* w, float* h, bool transformed, bool stroking)
|
||||
{
|
||||
Matrix* m = nullptr;
|
||||
|
||||
//Case: No transformed, quick return!
|
||||
if (!transformed || !(m = this->transform())) return smethod->bounds(x, y, w, h);
|
||||
if (!transformed || !(m = this->transform())) return smethod->bounds(x, y, w, h, stroking);
|
||||
|
||||
//Case: Transformed
|
||||
auto tx = 0.0f;
|
||||
|
@ -276,7 +273,7 @@ bool Paint::Impl::bounds(float* x, float* y, float* w, float* h, bool transforme
|
|||
auto tw = 0.0f;
|
||||
auto th = 0.0f;
|
||||
|
||||
auto ret = smethod->bounds(&tx, &ty, &tw, &th);
|
||||
auto ret = smethod->bounds(&tx, &ty, &tw, &th, stroking);
|
||||
|
||||
//Get vertices
|
||||
Point pt[4] = {{tx, ty}, {tx + tw, ty}, {tx + tw, ty + th}, {tx, ty + th}};
|
||||
|
@ -365,7 +362,7 @@ TVG_DEPRECATED Result Paint::bounds(float* x, float* y, float* w, float* h) cons
|
|||
|
||||
Result Paint::bounds(float* x, float* y, float* w, float* h, bool transform) const noexcept
|
||||
{
|
||||
if (pImpl->bounds(x, y, w, h, transform)) return Result::Success;
|
||||
if (pImpl->bounds(x, y, w, h, transform, true)) return Result::Success;
|
||||
return Result::InsufficientCondition;
|
||||
}
|
||||
|
||||
|
|
|
@ -42,10 +42,10 @@ namespace tvg
|
|||
{
|
||||
virtual ~StrategyMethod() {}
|
||||
|
||||
virtual bool dispose(RenderMethod& renderer) = 0;
|
||||
virtual bool dispose(RenderMethod& renderer) = 0; //return true if the deletion is allowed.
|
||||
virtual void* update(RenderMethod& renderer, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper) = 0; //Return engine data if it has.
|
||||
virtual bool render(RenderMethod& renderer) = 0;
|
||||
virtual bool bounds(float* x, float* y, float* w, float* h) = 0;
|
||||
virtual bool bounds(float* x, float* y, float* w, float* h, bool stroking) = 0;
|
||||
virtual RenderRegion bounds(RenderMethod& renderer) const = 0;
|
||||
virtual Paint* duplicate() = 0;
|
||||
virtual Iterator* iterator() = 0;
|
||||
|
@ -68,6 +68,7 @@ namespace tvg
|
|||
uint8_t ctxFlag = ContextFlag::Invalid;
|
||||
uint8_t id;
|
||||
uint8_t opacity = 255;
|
||||
uint8_t refCnt = 1;
|
||||
|
||||
~Impl()
|
||||
{
|
||||
|
@ -79,6 +80,18 @@ namespace tvg
|
|||
delete(rTransform);
|
||||
}
|
||||
|
||||
uint8_t ref()
|
||||
{
|
||||
if (refCnt == 255) TVGERR("RENDERER", "Corrupted reference count!");
|
||||
return (++refCnt);
|
||||
}
|
||||
|
||||
uint8_t unref()
|
||||
{
|
||||
if (refCnt == 0) TVGERR("RENDERER", "Corrupted reference count!");
|
||||
return (--refCnt);
|
||||
}
|
||||
|
||||
void method(StrategyMethod* method)
|
||||
{
|
||||
smethod = method;
|
||||
|
@ -147,7 +160,7 @@ namespace tvg
|
|||
bool rotate(float degree);
|
||||
bool scale(float factor);
|
||||
bool translate(float x, float y);
|
||||
bool bounds(float* x, float* y, float* w, float* h, bool transformed);
|
||||
bool bounds(float* x, float* y, float* w, float* h, bool transformed, bool stroking);
|
||||
RenderData update(RenderMethod& renderer, const RenderTransform* pTransform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper = false);
|
||||
bool render(RenderMethod& renderer);
|
||||
Paint* duplicate();
|
||||
|
@ -162,9 +175,9 @@ namespace tvg
|
|||
PaintMethod(T* _inst) : inst(_inst) {}
|
||||
~PaintMethod() {}
|
||||
|
||||
bool bounds(float* x, float* y, float* w, float* h) override
|
||||
bool bounds(float* x, float* y, float* w, float* h, bool stroking) override
|
||||
{
|
||||
return inst->bounds(x, y, w, h);
|
||||
return inst->bounds(x, y, w, h, stroking);
|
||||
}
|
||||
|
||||
RenderRegion bounds(RenderMethod& renderer) const override
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -1,98 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <float.h>
|
||||
#include "tvgFill.h"
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
struct RadialGradient::Impl
|
||||
{
|
||||
float cx = 0;
|
||||
float cy = 0;
|
||||
float radius = 0;
|
||||
|
||||
Fill* duplicate()
|
||||
{
|
||||
auto ret = RadialGradient::gen();
|
||||
if (!ret) return nullptr;
|
||||
|
||||
ret->pImpl->cx = cx;
|
||||
ret->pImpl->cy = cy;
|
||||
ret->pImpl->radius = radius;
|
||||
|
||||
return ret.release();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
RadialGradient::RadialGradient():pImpl(new Impl())
|
||||
{
|
||||
Fill::pImpl->id = TVG_CLASS_ID_RADIAL;
|
||||
Fill::pImpl->method(new FillDup<RadialGradient::Impl>(pImpl));
|
||||
}
|
||||
|
||||
|
||||
RadialGradient::~RadialGradient()
|
||||
{
|
||||
delete(pImpl);
|
||||
}
|
||||
|
||||
|
||||
Result RadialGradient::radial(float cx, float cy, float radius) noexcept
|
||||
{
|
||||
if (radius < 0) return Result::InvalidArguments;
|
||||
|
||||
pImpl->cx = cx;
|
||||
pImpl->cy = cy;
|
||||
pImpl->radius = radius;
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
Result RadialGradient::radial(float* cx, float* cy, float* radius) const noexcept
|
||||
{
|
||||
if (cx) *cx = pImpl->cx;
|
||||
if (cy) *cy = pImpl->cy;
|
||||
if (radius) *radius = pImpl->radius;
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
unique_ptr<RadialGradient> RadialGradient::gen() noexcept
|
||||
{
|
||||
return unique_ptr<RadialGradient>(new RadialGradient);
|
||||
}
|
||||
|
||||
|
||||
uint32_t RadialGradient::identifier() noexcept
|
||||
{
|
||||
return TVG_CLASS_ID_RADIAL;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -46,9 +46,9 @@ struct Shape::Impl
|
|||
|
||||
bool dispose(RenderMethod& renderer)
|
||||
{
|
||||
auto ret = renderer.dispose(rd);
|
||||
renderer.dispose(rd);
|
||||
rd = nullptr;
|
||||
return ret;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool render(RenderMethod& renderer)
|
||||
|
@ -70,7 +70,7 @@ struct Shape::Impl
|
|||
if (opacity == 0) return false;
|
||||
|
||||
//Shape composition is only necessary when stroking & fill are valid.
|
||||
if (!rs.stroke || rs.stroke->width < FLT_EPSILON || rs.stroke->color[3] == 0) return false;
|
||||
if (!rs.stroke || rs.stroke->width < FLT_EPSILON || (!rs.stroke->fill && rs.stroke->color[3] == 0)) return false;
|
||||
if (!rs.fill && rs.color[3] == 0) return false;
|
||||
|
||||
//translucent fill & stroke
|
||||
|
@ -104,7 +104,7 @@ struct Shape::Impl
|
|||
return renderer.region(rd);
|
||||
}
|
||||
|
||||
bool bounds(float* x, float* y, float* w, float* h)
|
||||
bool bounds(float* x, float* y, float* w, float* h, bool stroking)
|
||||
{
|
||||
//Path bounding size
|
||||
if (rs.path.pts.count > 0 ) {
|
||||
|
@ -126,7 +126,7 @@ struct Shape::Impl
|
|||
}
|
||||
|
||||
//Stroke feathering
|
||||
if (rs.stroke) {
|
||||
if (stroking && rs.stroke) {
|
||||
if (x) *x -= rs.stroke->width * 0.5f;
|
||||
if (y) *y -= rs.stroke->width * 0.5f;
|
||||
if (w) *w += rs.stroke->width;
|
||||
|
@ -199,8 +199,6 @@ struct Shape::Impl
|
|||
|
||||
bool strokeWidth(float width)
|
||||
{
|
||||
//TODO: Size Exception?
|
||||
|
||||
if (!rs.stroke) rs.stroke = new RenderStroke();
|
||||
rs.stroke->width = width;
|
||||
flag |= RenderUpdateFlag::Stroke;
|
||||
|
@ -208,6 +206,22 @@ struct Shape::Impl
|
|||
return true;
|
||||
}
|
||||
|
||||
bool strokeTrim(float begin, float end)
|
||||
{
|
||||
if (!rs.stroke) {
|
||||
if (begin == 0.0f && end == 1.0f) return true;
|
||||
rs.stroke = new RenderStroke();
|
||||
}
|
||||
|
||||
if (mathEqual(rs.stroke->trim.begin, begin) && mathEqual(rs.stroke->trim.end, end)) return true;
|
||||
|
||||
rs.stroke->trim.begin = begin;
|
||||
rs.stroke->trim.end = end;
|
||||
flag |= RenderUpdateFlag::Stroke;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool strokeCap(StrokeCap cap)
|
||||
{
|
||||
if (!rs.stroke) rs.stroke = new RenderStroke();
|
||||
|
@ -269,8 +283,16 @@ struct Shape::Impl
|
|||
return Result::Success;
|
||||
}
|
||||
|
||||
bool strokeDash(const float* pattern, uint32_t cnt)
|
||||
Result strokeDash(const float* pattern, uint32_t cnt, float offset)
|
||||
{
|
||||
if ((cnt == 1) || (!pattern && cnt > 0) || (pattern && cnt == 0)) {
|
||||
return Result::InvalidArguments;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < cnt; i++) {
|
||||
if (pattern[i] < FLT_EPSILON) return Result::InvalidArguments;
|
||||
}
|
||||
|
||||
//Reset dash
|
||||
if (!pattern && cnt == 0) {
|
||||
free(rs.stroke->dashPattern);
|
||||
|
@ -283,16 +305,17 @@ struct Shape::Impl
|
|||
}
|
||||
if (!rs.stroke->dashPattern) {
|
||||
rs.stroke->dashPattern = static_cast<float*>(malloc(sizeof(float) * cnt));
|
||||
if (!rs.stroke->dashPattern) return false;
|
||||
if (!rs.stroke->dashPattern) return Result::FailedAllocation;
|
||||
}
|
||||
for (uint32_t i = 0; i < cnt; ++i) {
|
||||
rs.stroke->dashPattern[i] = pattern[i];
|
||||
}
|
||||
}
|
||||
rs.stroke->dashCnt = cnt;
|
||||
rs.stroke->dashOffset = offset;
|
||||
flag |= RenderUpdateFlag::Stroke;
|
||||
|
||||
return true;
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
bool strokeFirst()
|
||||
|
@ -336,24 +359,17 @@ struct Shape::Impl
|
|||
//Stroke
|
||||
if (rs.stroke) {
|
||||
dup->rs.stroke = new RenderStroke();
|
||||
dup->rs.stroke->width = rs.stroke->width;
|
||||
dup->rs.stroke->dashCnt = rs.stroke->dashCnt;
|
||||
dup->rs.stroke->cap = rs.stroke->cap;
|
||||
dup->rs.stroke->join = rs.stroke->join;
|
||||
dup->rs.stroke->strokeFirst = rs.stroke->strokeFirst;
|
||||
*dup->rs.stroke = *rs.stroke;
|
||||
memcpy(dup->rs.stroke->color, rs.stroke->color, sizeof(rs.stroke->color));
|
||||
|
||||
if (rs.stroke->dashCnt > 0) {
|
||||
dup->rs.stroke->dashPattern = static_cast<float*>(malloc(sizeof(float) * rs.stroke->dashCnt));
|
||||
memcpy(dup->rs.stroke->dashPattern, rs.stroke->dashPattern, sizeof(float) * rs.stroke->dashCnt);
|
||||
}
|
||||
|
||||
dup->flag |= RenderUpdateFlag::Stroke;
|
||||
|
||||
if (rs.stroke->fill) {
|
||||
dup->rs.stroke->fill = rs.stroke->fill->duplicate();
|
||||
dup->flag |= RenderUpdateFlag::GradientStroke;
|
||||
}
|
||||
dup->flag |= RenderUpdateFlag::Stroke;
|
||||
}
|
||||
|
||||
//Fill
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -123,7 +123,7 @@ static void _copyStyle(SvgStyleProperty* to, const SvgStyleProperty* from)
|
|||
to->stroke.dash.array.clear();
|
||||
to->stroke.dash.array.reserve(from->stroke.dash.array.count);
|
||||
for (uint32_t i = 0; i < from->stroke.dash.array.count; ++i) {
|
||||
to->stroke.dash.array.push(from->stroke.dash.array.data[i]);
|
||||
to->stroke.dash.array.push(from->stroke.dash.array[i]);
|
||||
}
|
||||
to->stroke.flags = (to->stroke.flags | SvgStrokeFlags::Dash);
|
||||
to->flags = (to->flags | SvgStyleFlags::StrokeDashArray);
|
||||
|
@ -236,7 +236,7 @@ void cssUpdateStyle(SvgNode* doc, SvgNode* style)
|
|||
void cssApplyStyleToPostponeds(Array<SvgNodeIdPair>& postponeds, SvgNode* style)
|
||||
{
|
||||
for (uint32_t i = 0; i < postponeds.count; ++i) {
|
||||
auto nodeIdPair = postponeds.data[i];
|
||||
auto nodeIdPair = postponeds[i];
|
||||
|
||||
//css styling: tag.name has higher priority than .name
|
||||
if (auto cssNode = cssFindStyleNode(style, nodeIdPair.id, nodeIdPair.node->type)) {
|
||||
|
|
|
@ -59,7 +59,7 @@
|
|||
#include "tvgXmlParser.h"
|
||||
#include "tvgSvgLoader.h"
|
||||
#include "tvgSvgSceneBuilder.h"
|
||||
#include "tvgSvgUtil.h"
|
||||
#include "tvgStr.h"
|
||||
#include "tvgSvgCssStyle.h"
|
||||
#include "tvgMath.h"
|
||||
|
||||
|
@ -110,7 +110,7 @@ static bool _parseNumber(const char** content, float* number)
|
|||
{
|
||||
char* end = nullptr;
|
||||
|
||||
*number = svgUtilStrtof(*content, &end);
|
||||
*number = strToFloat(*content, &end);
|
||||
//If the start of string is not number
|
||||
if ((*content) == end) return false;
|
||||
//Skip comma if any
|
||||
|
@ -166,7 +166,7 @@ static void _parseAspectRatio(const char** content, AspectRatioAlign* align, Asp
|
|||
*/
|
||||
static float _toFloat(const SvgParser* svgParse, const char* str, SvgParserLengthType type)
|
||||
{
|
||||
float parsedValue = svgUtilStrtof(str, nullptr);
|
||||
float parsedValue = strToFloat(str, nullptr);
|
||||
|
||||
if (strstr(str, "cm")) parsedValue *= PX_PER_CM;
|
||||
else if (strstr(str, "mm")) parsedValue *= PX_PER_MM;
|
||||
|
@ -194,7 +194,7 @@ static float _gradientToFloat(const SvgParser* svgParse, const char* str, bool&
|
|||
{
|
||||
char* end = nullptr;
|
||||
|
||||
float parsedValue = svgUtilStrtof(str, &end);
|
||||
float parsedValue = strToFloat(str, &end);
|
||||
isPercentage = false;
|
||||
|
||||
if (strstr(str, "%")) {
|
||||
|
@ -217,7 +217,7 @@ static float _toOffset(const char* str)
|
|||
char* end = nullptr;
|
||||
auto strEnd = str + strlen(str);
|
||||
|
||||
float parsedValue = svgUtilStrtof(str, &end);
|
||||
float parsedValue = strToFloat(str, &end);
|
||||
|
||||
end = _skipSpace(end, nullptr);
|
||||
auto ptr = strstr(str, "%");
|
||||
|
@ -234,7 +234,7 @@ static float _toOffset(const char* str)
|
|||
static int _toOpacity(const char* str)
|
||||
{
|
||||
char* end = nullptr;
|
||||
float opacity = svgUtilStrtof(str, &end);
|
||||
float opacity = strToFloat(str, &end);
|
||||
|
||||
if (end) {
|
||||
if (end[0] == '%' && end[1] == '\0') return lrint(opacity * 2.55f);
|
||||
|
@ -362,7 +362,7 @@ static void _parseDashArray(SvgLoaderData* loader, const char *str, SvgDash* das
|
|||
|
||||
while (*str) {
|
||||
str = _skipComma(str);
|
||||
float parsedValue = svgUtilStrtof(str, &end);
|
||||
float parsedValue = strToFloat(str, &end);
|
||||
if (str == end) break;
|
||||
if (parsedValue <= 0.0f) break;
|
||||
if (*end == '%') {
|
||||
|
@ -375,7 +375,7 @@ static void _parseDashArray(SvgLoaderData* loader, const char *str, SvgDash* das
|
|||
str = end;
|
||||
}
|
||||
//If dash array size is 1, it means that dash and gap size are the same.
|
||||
if ((*dash).array.count == 1) (*dash).array.push((*dash).array.data[0]);
|
||||
if ((*dash).array.count == 1) (*dash).array.push((*dash).array[0]);
|
||||
}
|
||||
|
||||
|
||||
|
@ -393,7 +393,7 @@ static char* _idFromUrl(const char* url)
|
|||
int i = 0;
|
||||
while (url[i] > ' ' && url[i] != ')' && url[i] != '\'') ++i;
|
||||
|
||||
return svgUtilStrndup(url, i);
|
||||
return strDuplicate(url, i);
|
||||
}
|
||||
|
||||
|
||||
|
@ -401,7 +401,7 @@ static unsigned char _parseColor(const char* value, char** end)
|
|||
{
|
||||
float r;
|
||||
|
||||
r = svgUtilStrtof(value, end);
|
||||
r = strToFloat(value, end);
|
||||
*end = _skipSpace(*end, nullptr);
|
||||
if (**end == '%') {
|
||||
r = 255 * r / 100;
|
||||
|
@ -643,7 +643,7 @@ static char* _parseNumbersArray(char* str, float* points, int* ptCount, int len)
|
|||
|
||||
str = _skipSpace(str, nullptr);
|
||||
while ((count < len) && (isdigit(*str) || *str == '-' || *str == '+' || *str == '.')) {
|
||||
points[count++] = svgUtilStrtof(str, &end);
|
||||
points[count++] = strToFloat(str, &end);
|
||||
str = end;
|
||||
str = _skipSpace(str, nullptr);
|
||||
if (*str == ',') ++str;
|
||||
|
@ -893,7 +893,7 @@ static bool _attrParseSvgNode(void* data, const char* key, const char* value)
|
|||
} else if (!strcmp(key, "style")) {
|
||||
return simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader);
|
||||
#ifdef THORVG_LOG_ENABLED
|
||||
} else if ((!strcmp(key, "x") || !strcmp(key, "y")) && fabsf(svgUtilStrtof(value, nullptr)) > FLT_EPSILON) {
|
||||
} else if ((!strcmp(key, "x") || !strcmp(key, "y")) && fabsf(strToFloat(value, nullptr)) > FLT_EPSILON) {
|
||||
TVGLOG("SVG", "Unsupported attributes used [Elements type: Svg][Attribute: %s][Value: %s]", key, value);
|
||||
#endif
|
||||
} else {
|
||||
|
@ -956,6 +956,12 @@ static void _handleStrokeDashArrayAttr(SvgLoaderData* loader, SvgNode* node, con
|
|||
_parseDashArray(loader, value, &node->style->stroke.dash);
|
||||
}
|
||||
|
||||
static void _handleStrokeDashOffsetAttr(SvgLoaderData* loader, SvgNode* node, const char* value)
|
||||
{
|
||||
node->style->stroke.flags = (node->style->stroke.flags | SvgStrokeFlags::DashOffset);
|
||||
node->style->stroke.dash.offset = _toFloat(loader->svgParse, value, SvgParserLengthType::Horizontal);
|
||||
}
|
||||
|
||||
static void _handleStrokeWidthAttr(SvgLoaderData* loader, SvgNode* node, const char* value)
|
||||
{
|
||||
node->style->stroke.flags = (node->style->stroke.flags | SvgStrokeFlags::Width);
|
||||
|
@ -979,7 +985,7 @@ static void _handleStrokeLineJoinAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode*
|
|||
static void _handleStrokeMiterlimitAttr(SvgLoaderData* loader, SvgNode* node, const char* value)
|
||||
{
|
||||
char* end = nullptr;
|
||||
const float miterlimit = svgUtilStrtof(value, &end);
|
||||
const float miterlimit = strToFloat(value, &end);
|
||||
|
||||
// https://www.w3.org/TR/SVG2/painting.html#LineJoin
|
||||
// - A negative value for stroke-miterlimit must be treated as an illegal value.
|
||||
|
@ -1112,6 +1118,7 @@ static constexpr struct
|
|||
STYLE_DEF(stroke-linecap, StrokeLineCap, SvgStyleFlags::StrokeLineCap),
|
||||
STYLE_DEF(stroke-opacity, StrokeOpacity, SvgStyleFlags::StrokeOpacity),
|
||||
STYLE_DEF(stroke-dasharray, StrokeDashArray, SvgStyleFlags::StrokeDashArray),
|
||||
STYLE_DEF(stroke-dashoffset, StrokeDashOffset, SvgStyleFlags::StrokeDashOffset),
|
||||
STYLE_DEF(transform, Transform, SvgStyleFlags::Transform),
|
||||
STYLE_DEF(clip-path, ClipPath, SvgStyleFlags::ClipPath),
|
||||
STYLE_DEF(mask, Mask, SvgStyleFlags::Mask),
|
||||
|
@ -1141,7 +1148,7 @@ static bool _parseStyleAttr(void* data, const char* key, const char* value, bool
|
|||
while (size > 0 && isspace(value[size - 1])) {
|
||||
size--;
|
||||
}
|
||||
value = svgUtilStrndup(value, size);
|
||||
value = strDuplicate(value, size);
|
||||
importance = true;
|
||||
}
|
||||
if (style) {
|
||||
|
@ -2097,6 +2104,12 @@ static void _handleRadialFyAttr(SvgLoaderData* loader, SvgRadialGradient* radial
|
|||
}
|
||||
|
||||
|
||||
static void _handleRadialFrAttr(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value)
|
||||
{
|
||||
radial->fr = _gradientToFloat(loader->svgParse, value, radial->isFrPercentage);
|
||||
}
|
||||
|
||||
|
||||
static void _handleRadialRAttr(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value)
|
||||
{
|
||||
radial->r = _gradientToFloat(loader->svgParse, value, radial->isRPercentage);
|
||||
|
@ -2127,6 +2140,13 @@ static void _recalcRadialFyAttr(SvgLoaderData* loader, SvgRadialGradient* radial
|
|||
}
|
||||
|
||||
|
||||
static void _recalcRadialFrAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
|
||||
{
|
||||
// scaling factor based on the Units paragraph from : https://www.w3.org/TR/2015/WD-SVG2-20150915/coords.html
|
||||
if (userSpace && !radial->isFrPercentage) radial->fr = radial->fr / (sqrtf(powf(loader->svgParse->global.h, 2) + powf(loader->svgParse->global.w, 2)) / sqrtf(2.0));
|
||||
}
|
||||
|
||||
|
||||
static void _recalcRadialRAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
|
||||
{
|
||||
// scaling factor based on the Units paragraph from : https://www.w3.org/TR/2015/WD-SVG2-20150915/coords.html
|
||||
|
@ -2170,6 +2190,15 @@ static void _recalcInheritedRadialFyAttr(SvgLoaderData* loader, SvgRadialGradien
|
|||
}
|
||||
|
||||
|
||||
static void _recalcInheritedRadialFrAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
|
||||
{
|
||||
if (!radial->isFrPercentage) {
|
||||
if (userSpace) radial->fr /= sqrtf(powf(loader->svgParse->global.h, 2) + powf(loader->svgParse->global.w, 2)) / sqrtf(2.0);
|
||||
else radial->fr *= sqrtf(powf(loader->svgParse->global.h, 2) + powf(loader->svgParse->global.w, 2)) / sqrtf(2.0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void _recalcInheritedRadialRAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
|
||||
{
|
||||
if (!radial->isRPercentage) {
|
||||
|
@ -2211,6 +2240,14 @@ static void _inheritRadialFyAttr(SvgStyleGradient* to, SvgStyleGradient* from)
|
|||
}
|
||||
|
||||
|
||||
static void _inheritRadialFrAttr(SvgStyleGradient* to, SvgStyleGradient* from)
|
||||
{
|
||||
to->radial->fr = from->radial->fr;
|
||||
to->radial->isFrPercentage = from->radial->isFrPercentage;
|
||||
to->flags = (to->flags | SvgGradientFlags::Fr);
|
||||
}
|
||||
|
||||
|
||||
static void _inheritRadialRAttr(SvgStyleGradient* to, SvgStyleGradient* from)
|
||||
{
|
||||
to->radial->r = from->radial->r;
|
||||
|
@ -2244,7 +2281,8 @@ static constexpr struct
|
|||
RADIAL_DEF(cy, Cy, SvgGradientFlags::Cy),
|
||||
RADIAL_DEF(fx, Fx, SvgGradientFlags::Fx),
|
||||
RADIAL_DEF(fy, Fy, SvgGradientFlags::Fy),
|
||||
RADIAL_DEF(r, R, SvgGradientFlags::R)
|
||||
RADIAL_DEF(r, R, SvgGradientFlags::R),
|
||||
RADIAL_DEF(fr, Fr, SvgGradientFlags::Fr)
|
||||
};
|
||||
|
||||
|
||||
|
@ -2312,6 +2350,7 @@ static SvgStyleGradient* _createRadialGradient(SvgLoaderData* loader, const char
|
|||
grad->radial->isFxPercentage = true;
|
||||
grad->radial->isFyPercentage = true;
|
||||
grad->radial->isRPercentage = true;
|
||||
grad->radial->isFrPercentage = true;
|
||||
|
||||
loader->svgParse->gradient.parsedFx = false;
|
||||
loader->svgParse->gradient.parsedFy = false;
|
||||
|
@ -2619,7 +2658,7 @@ static GradientFactoryMethod _findGradientFactory(const char* name)
|
|||
static void _cloneGradStops(Array<Fill::ColorStop>& dst, const Array<Fill::ColorStop>& src)
|
||||
{
|
||||
for (uint32_t i = 0; i < src.count; ++i) {
|
||||
dst.push(src.data[i]);
|
||||
dst.push(src[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2773,10 +2812,13 @@ static void _styleInherit(SvgStyleProperty* child, const SvgStyleProperty* paren
|
|||
child->stroke.dash.array.clear();
|
||||
child->stroke.dash.array.reserve(parent->stroke.dash.array.count);
|
||||
for (uint32_t i = 0; i < parent->stroke.dash.array.count; ++i) {
|
||||
child->stroke.dash.array.push(parent->stroke.dash.array.data[i]);
|
||||
child->stroke.dash.array.push(parent->stroke.dash.array[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!(child->stroke.flags & SvgStrokeFlags::DashOffset)) {
|
||||
child->stroke.dash.offset = parent->stroke.dash.offset;
|
||||
}
|
||||
if (!(child->stroke.flags & SvgStrokeFlags::Cap)) {
|
||||
child->stroke.cap = parent->stroke.cap;
|
||||
}
|
||||
|
@ -2839,17 +2881,19 @@ static void _styleCopy(SvgStyleProperty* to, const SvgStyleProperty* from)
|
|||
to->stroke.dash.array.clear();
|
||||
to->stroke.dash.array.reserve(from->stroke.dash.array.count);
|
||||
for (uint32_t i = 0; i < from->stroke.dash.array.count; ++i) {
|
||||
to->stroke.dash.array.push(from->stroke.dash.array.data[i]);
|
||||
to->stroke.dash.array.push(from->stroke.dash.array[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (from->stroke.flags & SvgStrokeFlags::DashOffset) {
|
||||
to->stroke.dash.offset = from->stroke.dash.offset;
|
||||
}
|
||||
if (from->stroke.flags & SvgStrokeFlags::Cap) {
|
||||
to->stroke.cap = from->stroke.cap;
|
||||
}
|
||||
if (from->stroke.flags & SvgStrokeFlags::Join) {
|
||||
to->stroke.join = from->stroke.join;
|
||||
}
|
||||
|
||||
if (from->stroke.flags & SvgStrokeFlags::Miterlimit) {
|
||||
to->stroke.miterlimit = from->stroke.miterlimit;
|
||||
}
|
||||
|
@ -2983,7 +3027,7 @@ static void _cloneNode(SvgNode* from, SvgNode* parent, int depth)
|
|||
static void _clonePostponedNodes(Array<SvgNodeIdPair>* cloneNodes, SvgNode* doc)
|
||||
{
|
||||
for (uint32_t i = 0; i < cloneNodes->count; ++i) {
|
||||
auto nodeIdPair = cloneNodes->data[i];
|
||||
auto nodeIdPair = (*cloneNodes)[i];
|
||||
auto defs = _getDefsNode(nodeIdPair.node);
|
||||
auto nodeFrom = _findNodeById(defs, nodeIdPair.id);
|
||||
if (!nodeFrom) nodeFrom = _findNodeById(doc, nodeIdPair.id);
|
||||
|
@ -3064,7 +3108,7 @@ static void _svgLoaderParserXmlOpen(SvgLoaderData* loader, const char* content,
|
|||
loader->doc = node;
|
||||
} else {
|
||||
if (!strcmp(tagName, "svg")) return; //Already loaded <svg>(SvgNodeType::Doc) tag
|
||||
if (loader->stack.count > 0) parent = loader->stack.data[loader->stack.count - 1];
|
||||
if (loader->stack.count > 0) parent = loader->stack.last();
|
||||
else parent = loader->doc;
|
||||
if (!strcmp(tagName, "style")) {
|
||||
// TODO: For now only the first style node is saved. After the css id selector
|
||||
|
@ -3085,7 +3129,7 @@ static void _svgLoaderParserXmlOpen(SvgLoaderData* loader, const char* content,
|
|||
loader->stack.push(node);
|
||||
}
|
||||
} else if ((method = _findGraphicsFactory(tagName))) {
|
||||
if (loader->stack.count > 0) parent = loader->stack.data[loader->stack.count - 1];
|
||||
if (loader->stack.count > 0) parent = loader->stack.last();
|
||||
else parent = loader->doc;
|
||||
node = method(loader, parent, attrs, attrsLength, simpleXmlParseAttributes);
|
||||
} else if ((gradientMethod = _findGradientFactory(tagName))) {
|
||||
|
|
|
@ -100,7 +100,8 @@ enum class SvgStrokeFlags
|
|||
Cap = 0x20,
|
||||
Join = 0x40,
|
||||
Dash = 0x80,
|
||||
Miterlimit = 0x100
|
||||
Miterlimit = 0x100,
|
||||
DashOffset = 0x200
|
||||
};
|
||||
|
||||
constexpr bool operator &(SvgStrokeFlags a, SvgStrokeFlags b)
|
||||
|
@ -139,7 +140,8 @@ enum class SvgStyleFlags
|
|||
MaskType = 0x4000,
|
||||
Display = 0x8000,
|
||||
PaintOrder = 0x10000,
|
||||
StrokeMiterlimit = 0x20000
|
||||
StrokeMiterlimit = 0x20000,
|
||||
StrokeDashOffset = 0x40000,
|
||||
};
|
||||
|
||||
constexpr bool operator &(SvgStyleFlags a, SvgStyleFlags b)
|
||||
|
@ -182,7 +184,8 @@ enum class SvgGradientFlags
|
|||
Cy = 0x80,
|
||||
R = 0x100,
|
||||
Fx = 0x200,
|
||||
Fy = 0x400
|
||||
Fy = 0x400,
|
||||
Fr = 0x800
|
||||
};
|
||||
|
||||
constexpr bool operator &(SvgGradientFlags a, SvgGradientFlags b)
|
||||
|
@ -390,11 +393,13 @@ struct SvgRadialGradient
|
|||
float fx;
|
||||
float fy;
|
||||
float r;
|
||||
float fr;
|
||||
bool isCxPercentage;
|
||||
bool isCyPercentage;
|
||||
bool isFxPercentage;
|
||||
bool isFyPercentage;
|
||||
bool isRPercentage;
|
||||
bool isFrPercentage;
|
||||
};
|
||||
|
||||
struct SvgComposite
|
||||
|
@ -423,6 +428,7 @@ struct SvgPaint
|
|||
struct SvgDash
|
||||
{
|
||||
Array<float> array;
|
||||
float offset;
|
||||
};
|
||||
|
||||
struct SvgStyleGradient
|
||||
|
@ -469,7 +475,6 @@ struct SvgStyleStroke
|
|||
StrokeJoin join;
|
||||
float miterlimit;
|
||||
SvgDash dash;
|
||||
int dashCount;
|
||||
};
|
||||
|
||||
struct SvgStyleProperty
|
||||
|
@ -561,18 +566,4 @@ struct Box
|
|||
float x, y, w, h;
|
||||
};
|
||||
|
||||
/*
|
||||
* https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/strtof-strtod-l-wcstod-wcstod-l?view=vs-2017
|
||||
*
|
||||
* src should be one of the following form :
|
||||
*
|
||||
* [whitespace] [sign] {digits [radix digits] | radix digits} [{e | E} [sign] digits]
|
||||
* [whitespace] [sign] {INF | INFINITY}
|
||||
* [whitespace] [sign] NAN [sequence]
|
||||
*
|
||||
* No hexadecimal form supported
|
||||
* no sequence supported after NAN
|
||||
*/
|
||||
float customStrtof(const char *nptr, char **endptr);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -55,7 +55,7 @@
|
|||
#include <ctype.h>
|
||||
#include "tvgSvgLoaderCommon.h"
|
||||
#include "tvgSvgPath.h"
|
||||
#include "tvgSvgUtil.h"
|
||||
#include "tvgStr.h"
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
|
@ -74,7 +74,7 @@ static char* _skipComma(const char* content)
|
|||
static bool _parseNumber(char** content, float* number)
|
||||
{
|
||||
char* end = NULL;
|
||||
*number = svgUtilStrtof(*content, &end);
|
||||
*number = strToFloat(*content, &end);
|
||||
//If the start of string is not number
|
||||
if ((*content) == end) return false;
|
||||
//Skip comma if any
|
||||
|
@ -382,7 +382,7 @@ static bool _processCommand(Array<PathCommand>* cmds, Array<Point>* pts, char cm
|
|||
case 's':
|
||||
case 'S': {
|
||||
Point p[3], ctrl;
|
||||
if ((cmds->count > 1) && (cmds->data[cmds->count - 1] == PathCommand::CubicTo) &&
|
||||
if ((cmds->count > 1) && (cmds->last() == PathCommand::CubicTo) &&
|
||||
!(*isQuadratic)) {
|
||||
ctrl.x = 2 * cur->x - curCtl->x;
|
||||
ctrl.y = 2 * cur->y - curCtl->y;
|
||||
|
@ -423,7 +423,7 @@ static bool _processCommand(Array<PathCommand>* cmds, Array<Point>* pts, char cm
|
|||
case 't':
|
||||
case 'T': {
|
||||
Point p[3], ctrl;
|
||||
if ((cmds->count > 1) && (cmds->data[cmds->count - 1] == PathCommand::CubicTo) &&
|
||||
if ((cmds->count > 1) && (cmds->last() == PathCommand::CubicTo) &&
|
||||
*isQuadratic) {
|
||||
ctrl.x = 2 * cur->x - curCtl->x;
|
||||
ctrl.y = 2 * cur->y - curCtl->y;
|
||||
|
|
|
@ -52,6 +52,11 @@
|
|||
#include "tvgMath.h" /* to include math.h before cstring */
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include "tvgShapeImpl.h"
|
||||
#include "tvgCompressor.h"
|
||||
#include "tvgPaint.h"
|
||||
#include "tvgFill.h"
|
||||
#include "tvgStr.h"
|
||||
#include "tvgSvgLoaderCommon.h"
|
||||
#include "tvgSvgSceneBuilder.h"
|
||||
#include "tvgSvgPath.h"
|
||||
|
@ -62,6 +67,7 @@
|
|||
/************************************************************************/
|
||||
|
||||
static bool _appendShape(SvgNode* node, Shape* shape, const Box& vBox, const string& svgPath);
|
||||
static bool _appendClipShape(SvgNode* node, Shape* shape, const Box& vBox, const string& svgPath, const Matrix* transform);
|
||||
static unique_ptr<Scene> _sceneBuildHelper(const SvgNode* node, const Box& vBox, const string& svgPath, bool mask, int depth, bool* isMaskWhite = nullptr);
|
||||
|
||||
|
||||
|
@ -138,7 +144,7 @@ static unique_ptr<LinearGradient> _applyLinearGradientProperty(SvgStyleGradient*
|
|||
if (!stops) return fillGrad;
|
||||
auto prevOffset = 0.0f;
|
||||
for (uint32_t i = 0; i < g->stops.count; ++i) {
|
||||
auto colorStop = &g->stops.data[i];
|
||||
auto colorStop = &g->stops[i];
|
||||
//Use premultiplied color
|
||||
stops[i].r = colorStop->r;
|
||||
stops[i].g = colorStop->g;
|
||||
|
@ -175,6 +181,7 @@ static unique_ptr<RadialGradient> _applyRadialGradientProperty(SvgStyleGradient*
|
|||
g->radial->r = g->radial->r * sqrtf(powf(vBox.w, 2.0f) + powf(vBox.h, 2.0f)) / sqrtf(2.0f);
|
||||
g->radial->fx = g->radial->fx * vBox.w;
|
||||
g->radial->fy = g->radial->fy * vBox.h;
|
||||
g->radial->fr = g->radial->fr * sqrtf(powf(vBox.w, 2.0f) + powf(vBox.h, 2.0f)) / sqrtf(2.0f);
|
||||
} else {
|
||||
Matrix m = {vBox.w, 0, vBox.x, 0, vBox.h, vBox.y, 0, 0, 1};
|
||||
if (isTransform) _transformMultiply(&m, &finalTransform);
|
||||
|
@ -186,11 +193,7 @@ static unique_ptr<RadialGradient> _applyRadialGradientProperty(SvgStyleGradient*
|
|||
|
||||
if (isTransform) fillGrad->transform(finalTransform);
|
||||
|
||||
//TODO: Tvg is not support to focal
|
||||
//if (g->radial->fx != 0 && g->radial->fy != 0) {
|
||||
// fillGrad->radial(g->radial->fx, g->radial->fy, g->radial->r);
|
||||
//}
|
||||
fillGrad->radial(g->radial->cx, g->radial->cy, g->radial->r);
|
||||
P(fillGrad)->radial(g->radial->cx, g->radial->cy, g->radial->r, g->radial->fx, g->radial->fy, g->radial->fr);
|
||||
fillGrad->spread(g->spread);
|
||||
|
||||
//Update the stops
|
||||
|
@ -200,7 +203,7 @@ static unique_ptr<RadialGradient> _applyRadialGradientProperty(SvgStyleGradient*
|
|||
if (!stops) return fillGrad;
|
||||
auto prevOffset = 0.0f;
|
||||
for (uint32_t i = 0; i < g->stops.count; ++i) {
|
||||
auto colorStop = &g->stops.data[i];
|
||||
auto colorStop = &g->stops[i];
|
||||
//Use premultiplied color
|
||||
stops[i].r = colorStop->r;
|
||||
stops[i].g = colorStop->g;
|
||||
|
@ -219,20 +222,50 @@ static unique_ptr<RadialGradient> _applyRadialGradientProperty(SvgStyleGradient*
|
|||
}
|
||||
|
||||
|
||||
static bool _appendChildShape(SvgNode* node, Shape* shape, const Box& vBox, const string& svgPath)
|
||||
//The SVG standard allows only for 'use' nodes that point directly to a basic shape.
|
||||
static bool _appendClipUseNode(SvgNode* node, Shape* shape, const Box& vBox, const string& svgPath)
|
||||
{
|
||||
auto valid = false;
|
||||
if (node->child.count != 1) return false;
|
||||
auto child = *(node->child.data);
|
||||
|
||||
if (_appendShape(node, shape, vBox, svgPath)) valid = true;
|
||||
|
||||
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<Shape> _shapeBuildHelper(SvgNode* node, const Box& vBox, const
|
|||
}
|
||||
|
||||
|
||||
static bool _appendShape(SvgNode* node, Shape* shape, const Box& vBox, const string& svgPath)
|
||||
static bool _recognizeShape(SvgNode* node, Shape* shape)
|
||||
{
|
||||
Array<PathCommand> cmds;
|
||||
Array<Point> pts;
|
||||
|
||||
switch (node->type) {
|
||||
case SvgNodeType::Path: {
|
||||
if (node->node.path.path) {
|
||||
Array<PathCommand> cmds;
|
||||
Array<Point> pts;
|
||||
if (svgPathToTvgPath(node->node.path.path, cmds, pts)) {
|
||||
shape->appendPath(cmds.data, cmds.count, pts.data, pts.count);
|
||||
}
|
||||
|
@ -437,8 +469,41 @@ static bool _appendShape(SvgNode* node, Shape* shape, const Box& vBox, const str
|
|||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
_applyProperty(node, shape, vBox, svgPath);
|
||||
|
||||
static bool _appendShape(SvgNode* node, Shape* shape, const Box& vBox, const string& svgPath)
|
||||
{
|
||||
if (!_recognizeShape(node, shape)) return false;
|
||||
|
||||
_applyProperty(node, shape, vBox, svgPath, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static bool _appendClipShape(SvgNode* node, Shape* shape, const Box& vBox, const string& svgPath, const Matrix* transform)
|
||||
{
|
||||
//The 'transform' matrix has higher priority than the node->transform, since it already contains it
|
||||
auto m = transform ? transform : (node->transform ? node->transform : nullptr);
|
||||
|
||||
uint32_t currentPtsCnt = 0;
|
||||
if (m) {
|
||||
const Point *tmp = nullptr;
|
||||
currentPtsCnt = shape->pathCoords(&tmp);
|
||||
}
|
||||
|
||||
if (!_recognizeShape(node, shape)) return false;
|
||||
|
||||
if (m) {
|
||||
const Point *pts = nullptr;
|
||||
auto ptsCnt = shape->pathCoords(&pts);
|
||||
|
||||
auto p = const_cast<Point*>(pts) + currentPtsCnt;
|
||||
while (currentPtsCnt++ < ptsCnt) mathMultiply(p++, m);
|
||||
}
|
||||
|
||||
_applyProperty(node, shape, vBox, svgPath, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -514,12 +579,15 @@ static bool _isValidImageMimeTypeAndEncoding(const char** href, const char** mim
|
|||
return false;
|
||||
}
|
||||
|
||||
#include "tvgTaskScheduler.h"
|
||||
|
||||
static unique_ptr<Picture> _imageBuildHelper(SvgNode* node, const Box& vBox, const string& svgPath)
|
||||
{
|
||||
if (!node->node.image.href) return nullptr;
|
||||
auto picture = Picture::gen();
|
||||
|
||||
TaskScheduler::async(false); //force to load a picture on the same thread
|
||||
|
||||
const char* href = node->node.image.href;
|
||||
if (!strncmp(href, "data:", sizeof("data:") - 1)) {
|
||||
href += sizeof("data:") - 1;
|
||||
|
@ -527,11 +595,22 @@ static unique_ptr<Picture> _imageBuildHelper(SvgNode* node, const Box& vBox, con
|
|||
imageMimeTypeEncoding encoding;
|
||||
if (!_isValidImageMimeTypeAndEncoding(&href, &mimetype, &encoding)) return nullptr; //not allowed mime type or encoding
|
||||
if (encoding == imageMimeTypeEncoding::base64) {
|
||||
string decoded = svgUtilBase64Decode(href);
|
||||
if (picture->load(decoded.c_str(), decoded.size(), mimetype, true) != Result::Success) return nullptr;
|
||||
char* decoded = nullptr;
|
||||
auto size = b64Decode(href, strlen(href), &decoded);
|
||||
//OPTIMIZE: Skip data copy.
|
||||
if (picture->load(decoded, size, mimetype, true) != Result::Success) {
|
||||
free(decoded);
|
||||
TaskScheduler::async(true);
|
||||
return nullptr;
|
||||
}
|
||||
free(decoded);
|
||||
} else {
|
||||
string decoded = svgUtilURLDecode(href);
|
||||
if (picture->load(decoded.c_str(), decoded.size(), mimetype, true) != Result::Success) return nullptr;
|
||||
//OPTIMIZE: Skip data copy.
|
||||
if (picture->load(decoded.c_str(), decoded.size(), mimetype, true) != Result::Success) {
|
||||
TaskScheduler::async(true);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!strncmp(href, "file://", sizeof("file://") - 1)) href += sizeof("file://") - 1;
|
||||
|
@ -540,6 +619,7 @@ static unique_ptr<Picture> _imageBuildHelper(SvgNode* node, const Box& vBox, con
|
|||
const char *dot = strrchr(href, '.');
|
||||
if (dot && !strcmp(dot, ".svg")) {
|
||||
TVGLOG("SVG", "Embedded svg file is disabled.");
|
||||
TaskScheduler::async(true);
|
||||
return nullptr;
|
||||
}
|
||||
string imagePath = href;
|
||||
|
@ -547,9 +627,14 @@ static unique_ptr<Picture> _imageBuildHelper(SvgNode* node, const Box& vBox, con
|
|||
auto last = svgPath.find_last_of("/");
|
||||
imagePath = svgPath.substr(0, (last == string::npos ? 0 : last + 1)) + imagePath;
|
||||
}
|
||||
if (picture->load(imagePath) != Result::Success) return nullptr;
|
||||
if (picture->load(imagePath) != Result::Success) {
|
||||
TaskScheduler::async(true);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
TaskScheduler::async(true);
|
||||
|
||||
float w, h;
|
||||
Matrix m = {1, 0, 0, 0, 1, 0, 0, 0, 1};
|
||||
if (picture->size(&w, &h) == Result::Success && w > 0 && h > 0) {
|
||||
|
|
|
@ -21,19 +21,12 @@
|
|||
*/
|
||||
|
||||
#include <cstring>
|
||||
#include <math.h>
|
||||
#include <memory.h>
|
||||
#include "tvgSvgUtil.h"
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
static inline bool _floatExact(float a, float b)
|
||||
{
|
||||
return memcmp(&a, &b, sizeof(float)) == 0;
|
||||
}
|
||||
|
||||
static uint8_t _hexCharToDec(const char c)
|
||||
{
|
||||
if (c >= 'a') return c - 'a' + 10;
|
||||
|
@ -41,181 +34,11 @@ static uint8_t _hexCharToDec(const char c)
|
|||
else return c - '0';
|
||||
}
|
||||
|
||||
static uint8_t _base64Value(const char chr)
|
||||
{
|
||||
if (chr >= 'A' && chr <= 'Z') return chr - 'A';
|
||||
else if (chr >= 'a' && chr <= 'z') return chr - 'a' + ('Z' - 'A') + 1;
|
||||
else if (chr >= '0' && chr <= '9') return chr - '0' + ('Z' - 'A') + ('z' - 'a') + 2;
|
||||
else if (chr == '+' || chr == '-') return 62;
|
||||
else return 63;
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
|
||||
/*
|
||||
* https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/strtof-strtof-l-wcstof-wcstof-l?view=msvc-160
|
||||
*
|
||||
* src should be one of the following form :
|
||||
*
|
||||
* [whitespace] [sign] {digits [radix digits] | radix digits} [{e | E} [sign] digits]
|
||||
* [whitespace] [sign] {INF | INFINITY}
|
||||
* [whitespace] [sign] NAN [sequence]
|
||||
*
|
||||
* No hexadecimal form supported
|
||||
* no sequence supported after NAN
|
||||
*/
|
||||
float svgUtilStrtof(const char *nPtr, char **endPtr)
|
||||
{
|
||||
if (endPtr) *endPtr = (char*)(nPtr);
|
||||
if (!nPtr) return 0.0f;
|
||||
|
||||
auto a = nPtr;
|
||||
auto iter = nPtr;
|
||||
auto val = 0.0f;
|
||||
unsigned long long integerPart = 0;
|
||||
int minus = 1;
|
||||
|
||||
//ignore leading whitespaces
|
||||
while (isspace(*iter)) iter++;
|
||||
|
||||
//signed or not
|
||||
if (*iter == '-') {
|
||||
minus = -1;
|
||||
iter++;
|
||||
} else if (*iter == '+') {
|
||||
iter++;
|
||||
}
|
||||
|
||||
if (tolower(*iter) == 'i') {
|
||||
if ((tolower(*(iter + 1)) == 'n') && (tolower(*(iter + 2)) == 'f')) iter += 3;
|
||||
else goto error;
|
||||
|
||||
if (tolower(*(iter)) == 'i') {
|
||||
if ((tolower(*(iter + 1)) == 'n') && (tolower(*(iter + 2)) == 'i') && (tolower(*(iter + 3)) == 't') && (tolower(*(iter + 4)) == 'y')) iter += 5;
|
||||
else goto error;
|
||||
}
|
||||
if (endPtr) *endPtr = (char *)(iter);
|
||||
return (minus == -1) ? -INFINITY : INFINITY;
|
||||
}
|
||||
|
||||
if (tolower(*iter) == 'n') {
|
||||
if ((tolower(*(iter + 1)) == 'a') && (tolower(*(iter + 2)) == 'n')) iter += 3;
|
||||
else goto error;
|
||||
|
||||
if (endPtr) *endPtr = (char *)(iter);
|
||||
return (minus == -1) ? -NAN : NAN;
|
||||
}
|
||||
|
||||
//Optional: integer part before dot
|
||||
if (isdigit(*iter)) {
|
||||
for (; isdigit(*iter); iter++) {
|
||||
integerPart = integerPart * 10ULL + (unsigned long long)(*iter - '0');
|
||||
}
|
||||
a = iter;
|
||||
} else if (*iter != '.') {
|
||||
goto success;
|
||||
}
|
||||
|
||||
val = static_cast<float>(integerPart);
|
||||
|
||||
//Optional: decimal part after dot
|
||||
if (*iter == '.') {
|
||||
unsigned long long decimalPart = 0;
|
||||
unsigned long long pow10 = 1;
|
||||
int count = 0;
|
||||
|
||||
iter++;
|
||||
|
||||
if (isdigit(*iter)) {
|
||||
for (; isdigit(*iter); iter++, count++) {
|
||||
if (count < 19) {
|
||||
decimalPart = decimalPart * 10ULL + + static_cast<unsigned long long>(*iter - '0');
|
||||
pow10 *= 10ULL;
|
||||
}
|
||||
}
|
||||
} else if (isspace(*iter)) { //skip if there is a space after the dot.
|
||||
a = iter;
|
||||
goto success;
|
||||
}
|
||||
|
||||
val += static_cast<float>(decimalPart) / static_cast<float>(pow10);
|
||||
a = iter;
|
||||
}
|
||||
|
||||
//Optional: exponent
|
||||
if (*iter == 'e' || *iter == 'E') {
|
||||
++iter;
|
||||
|
||||
//Exception: svg may have 'em' unit for fonts. ex) 5em, 10.5em
|
||||
if ((*iter == 'm') || (*iter == 'M')) {
|
||||
//TODO: We don't support font em unit now, but has to multiply val * font size later...
|
||||
a = iter + 1;
|
||||
goto success;
|
||||
}
|
||||
|
||||
//signed or not
|
||||
int minus_e = 1;
|
||||
|
||||
if (*iter == '-') {
|
||||
minus_e = -1;
|
||||
++iter;
|
||||
} else if (*iter == '+') {
|
||||
iter++;
|
||||
}
|
||||
|
||||
unsigned int exponentPart = 0;
|
||||
|
||||
if (isdigit(*iter)) {
|
||||
while (*iter == '0') iter++;
|
||||
for (; isdigit(*iter); iter++) {
|
||||
exponentPart = exponentPart * 10U + static_cast<unsigned int>(*iter - '0');
|
||||
}
|
||||
} else if (!isdigit(*(a - 1))) {
|
||||
a = nPtr;
|
||||
goto success;
|
||||
} else if (*iter == 0) {
|
||||
goto success;
|
||||
}
|
||||
|
||||
//if ((_floatExact(val, 2.2250738585072011f)) && ((minus_e * static_cast<int>(exponentPart)) <= -308)) {
|
||||
if ((_floatExact(val, 1.175494351f)) && ((minus_e * static_cast<int>(exponentPart)) <= -38)) {
|
||||
//val *= 1.0e-308f;
|
||||
val *= 1.0e-38f;
|
||||
a = iter;
|
||||
goto success;
|
||||
}
|
||||
|
||||
a = iter;
|
||||
auto scale = 1.0f;
|
||||
|
||||
while (exponentPart >= 8U) {
|
||||
scale *= 1E8;
|
||||
exponentPart -= 8U;
|
||||
}
|
||||
while (exponentPart > 0U) {
|
||||
scale *= 10.0f;
|
||||
exponentPart--;
|
||||
}
|
||||
val = (minus_e == -1) ? (val / scale) : (val * scale);
|
||||
} else if ((iter > nPtr) && !isdigit(*(iter - 1))) {
|
||||
a = nPtr;
|
||||
goto success;
|
||||
}
|
||||
|
||||
success:
|
||||
if (endPtr) *endPtr = (char*)(a);
|
||||
return minus * val;
|
||||
|
||||
error:
|
||||
if (endPtr) *endPtr = (char*)(nPtr);
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
|
||||
string svgUtilURLDecode(const char *src)
|
||||
{
|
||||
if (!src) return nullptr;
|
||||
|
@ -242,49 +65,3 @@ string svgUtilURLDecode(const char *src)
|
|||
}
|
||||
return decoded;
|
||||
}
|
||||
|
||||
|
||||
string svgUtilBase64Decode(const char *src)
|
||||
{
|
||||
if (!src) return nullptr;
|
||||
|
||||
auto length = strlen(src);
|
||||
if (length == 0) return nullptr;
|
||||
|
||||
string decoded;
|
||||
decoded.reserve(3*(1+(length >> 2)));
|
||||
|
||||
while (*src && *(src+1)) {
|
||||
if (*src <= 0x20) {
|
||||
++src;
|
||||
continue;
|
||||
}
|
||||
|
||||
auto value1 = _base64Value(src[0]);
|
||||
auto value2 = _base64Value(src[1]);
|
||||
decoded += (value1 << 2) + ((value2 & 0x30) >> 4);
|
||||
|
||||
if (!src[2] || src[2] == '=' || src[2] == '.') break;
|
||||
auto value3 = _base64Value(src[2]);
|
||||
decoded += ((value2 & 0x0f) << 4) + ((value3 & 0x3c) >> 2);
|
||||
|
||||
if (!src[3] || src[3] == '=' || src[3] == '.') break;
|
||||
auto value4 = _base64Value(src[3]);
|
||||
decoded += ((value3 & 0x03) << 6) + value4;
|
||||
src += 4;
|
||||
}
|
||||
return decoded;
|
||||
}
|
||||
|
||||
|
||||
char* svgUtilStrndup(const char* str, size_t n)
|
||||
{
|
||||
auto len = strlen(str);
|
||||
if (len < n) n = len;
|
||||
|
||||
auto ret = (char*)malloc(n + 1);
|
||||
if (!ret) return nullptr;
|
||||
ret[n] = '\0';
|
||||
|
||||
return (char*)memcpy(ret, str, n);
|
||||
}
|
||||
|
|
|
@ -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_
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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];
|
|
@ -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;
|
|
@ -55,17 +55,20 @@
|
|||
*/
|
||||
#include "config.h"
|
||||
|
||||
#if defined(THORVG_TVG_SAVER_SUPPORT) || defined(THORVG_TVG_LOADER_SUPPORT)
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
#include <string>
|
||||
#include <memory.h>
|
||||
#include "tvgLzw.h"
|
||||
#include "tvgCompressor.h"
|
||||
|
||||
namespace tvg {
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* LZW Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
|
||||
namespace {
|
||||
//LZW Dictionary helper:
|
||||
constexpr int Nil = -1;
|
||||
constexpr int MaxDictBits = 12;
|
||||
|
@ -334,15 +337,8 @@ static bool outputSequence(const Dictionary& dict, int code, uint8_t*& output, i
|
|||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
namespace tvg {
|
||||
|
||||
uint8_t* lzwDecode(const uint8_t* compressed, uint32_t compressedSizeBytes, uint32_t compressedSizeBits, uint32_t uncompressedSizeBytes)
|
||||
{
|
||||
int code = Nil;
|
||||
|
@ -423,6 +419,57 @@ uint8_t* lzwEncode(const uint8_t* uncompressed, uint32_t uncompressedSizeBytes,
|
|||
return bitStream.release();
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* B64 Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
|
||||
size_t b64Decode(const char* encoded, const size_t len, char** decoded)
|
||||
{
|
||||
static constexpr const char B64_INDEX[256] =
|
||||
{
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 62, 63, 62, 62, 63, 52, 53, 54, 55, 56, 57,
|
||||
58, 59, 60, 61, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6,
|
||||
7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
|
||||
25, 0, 0, 0, 0, 63, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
|
||||
37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
|
||||
};
|
||||
|
||||
|
||||
if (!decoded || !encoded || len == 0) return 0;
|
||||
|
||||
auto reserved = 3 * (1 + (len >> 2)) + 1;
|
||||
auto output = static_cast<char*>(malloc(reserved * sizeof(char)));
|
||||
if (!output) return 0;
|
||||
output[reserved - 1] = '\0';
|
||||
|
||||
size_t idx = 0;
|
||||
|
||||
while (*encoded && *(encoded + 1)) {
|
||||
if (*encoded <= 0x20) {
|
||||
++encoded;
|
||||
continue;
|
||||
}
|
||||
|
||||
auto value1 = B64_INDEX[(size_t)encoded[0]];
|
||||
auto value2 = B64_INDEX[(size_t)encoded[1]];
|
||||
output[idx++] = (value1 << 2) + ((value2 & 0x30) >> 4);
|
||||
|
||||
if (!encoded[2] || encoded[2] == '=' || encoded[2] == '.') break;
|
||||
auto value3 = B64_INDEX[(size_t)encoded[2]];
|
||||
output[idx++] = ((value2 & 0x0f) << 4) + ((value3 & 0x3c) >> 2);
|
||||
|
||||
if (!encoded[3] || encoded[3] == '=' || encoded[3] == '.') break;
|
||||
auto value4 = B64_INDEX[(size_t)encoded[3]];
|
||||
output[idx++] = ((value3 & 0x03) << 6) + value4;
|
||||
encoded += 4;
|
||||
}
|
||||
*decoded = output;
|
||||
return reserved;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
}
|
|
@ -20,8 +20,8 @@
|
|||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _TVG_LZW_H_
|
||||
#define _TVG_LZW_H_
|
||||
#ifndef _TVG_COMPRESSOR_H_
|
||||
#define _TVG_COMPRESSOR_H_
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
|
@ -29,6 +29,7 @@ namespace tvg
|
|||
{
|
||||
uint8_t* lzwEncode(const uint8_t* uncompressed, uint32_t uncompressedSizeBytes, uint32_t* compressedSizeBytes, uint32_t* compressedSizeBits);
|
||||
uint8_t* lzwDecode(const uint8_t* compressed, uint32_t compressedSizeBytes, uint32_t compressedSizeBits, uint32_t uncompressedSizeBytes);
|
||||
size_t b64Decode(const char* encoded, const size_t len, char** decoded);
|
||||
}
|
||||
|
||||
#endif //_TVG_LZW_H
|
||||
#endif //_TVG_COMPRESSOR_H_
|
|
@ -29,6 +29,8 @@
|
|||
#include <math.h>
|
||||
#include "tvgCommon.h"
|
||||
|
||||
#define MATH_PI 3.14159265358979323846f
|
||||
#define MATH_PI2 1.57079632679489661923f
|
||||
|
||||
#define mathMin(x, y) (((x) < (y)) ? (x) : (y))
|
||||
#define mathMax(x, y) (((x) > (y)) ? (x) : (y))
|
||||
|
@ -45,6 +47,7 @@ static inline bool mathEqual(float a, float b)
|
|||
return (fabsf(a - b) < FLT_EPSILON);
|
||||
}
|
||||
|
||||
|
||||
static inline bool mathEqual(const Matrix& a, const Matrix& b)
|
||||
{
|
||||
if (!mathEqual(a.e11, b.e11) || !mathEqual(a.e12, b.e12) || !mathEqual(a.e13, b.e13) ||
|
||||
|
@ -55,6 +58,7 @@ static inline bool mathEqual(const Matrix& a, const Matrix& b)
|
|||
return true;
|
||||
}
|
||||
|
||||
|
||||
static inline bool mathRightAngle(const Matrix* m)
|
||||
{
|
||||
auto radian = fabsf(atan2f(m->e21, m->e11));
|
||||
|
@ -118,6 +122,15 @@ static inline void mathIdentity(Matrix* m)
|
|||
}
|
||||
|
||||
|
||||
static inline void mathTransform(Matrix* transform, Point* coord)
|
||||
{
|
||||
auto x = coord->x;
|
||||
auto y = coord->y;
|
||||
coord->x = x * transform->e11 + y * transform->e12 + transform->e13;
|
||||
coord->y = x * transform->e21 + y * transform->e22 + transform->e23;
|
||||
}
|
||||
|
||||
|
||||
static inline void mathScale(Matrix* m, float sx, float sy)
|
||||
{
|
||||
m->e11 *= sx;
|
||||
|
@ -125,6 +138,19 @@ static inline void mathScale(Matrix* m, float sx, float sy)
|
|||
}
|
||||
|
||||
|
||||
static inline void mathScaleR(Matrix* m, float x, float y)
|
||||
{
|
||||
if (x != 1.0f) {
|
||||
m->e11 *= x;
|
||||
m->e21 *= x;
|
||||
}
|
||||
if (y != 1.0f) {
|
||||
m->e22 *= y;
|
||||
m->e12 *= y;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static inline void mathTranslate(Matrix* m, float x, float y)
|
||||
{
|
||||
m->e13 += x;
|
||||
|
@ -174,6 +200,32 @@ static inline Matrix mathMultiply(const Matrix* lhs, const Matrix* rhs)
|
|||
}
|
||||
|
||||
|
||||
static inline void mathTranslateR(Matrix* m, float x, float y)
|
||||
{
|
||||
if (x == 0.0f && y == 0.0f) return;
|
||||
m->e13 += (x * m->e11 + y * m->e12);
|
||||
m->e23 += (x * m->e21 + y * m->e22);
|
||||
}
|
||||
|
||||
|
||||
static inline void mathLog(Matrix* m)
|
||||
{
|
||||
TVGLOG("MATH", "Matrix: [%f %f %f] [%f %f %f] [%f %f %f]", m->e11, m->e12, m->e13, m->e21, m->e22, m->e23, m->e31, m->e32, m->e33);
|
||||
}
|
||||
|
||||
|
||||
static inline float mathLength(const Point* a, const Point* b)
|
||||
{
|
||||
auto x = b->x - a->x;
|
||||
auto y = b->y - a->y;
|
||||
|
||||
if (x < 0) x = -x;
|
||||
if (y < 0) y = -y;
|
||||
|
||||
return (x > y) ? (x + 0.375f * y) : (y + 0.375f * x);
|
||||
}
|
||||
|
||||
|
||||
static inline Point operator-(const Point& lhs, const Point& rhs)
|
||||
{
|
||||
return {lhs.x - rhs.x, lhs.y - rhs.y};
|
|
@ -0,0 +1,239 @@
|
|||
/*
|
||||
* Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include <cstring>
|
||||
#include <memory.h>
|
||||
#include "tvgMath.h"
|
||||
#include "tvgStr.h"
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
static inline bool _floatExact(float a, float b)
|
||||
{
|
||||
return memcmp(&a, &b, sizeof(float)) == 0;
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
namespace tvg {
|
||||
|
||||
/*
|
||||
* https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/strtof-strtof-l-wcstof-wcstof-l?view=msvc-160
|
||||
*
|
||||
* src should be one of the following form :
|
||||
*
|
||||
* [whitespace] [sign] {digits [radix digits] | radix digits} [{e | E} [sign] digits]
|
||||
* [whitespace] [sign] {INF | INFINITY}
|
||||
* [whitespace] [sign] NAN [sequence]
|
||||
*
|
||||
* No hexadecimal form supported
|
||||
* no sequence supported after NAN
|
||||
*/
|
||||
float strToFloat(const char *nPtr, char **endPtr)
|
||||
{
|
||||
if (endPtr) *endPtr = (char *) (nPtr);
|
||||
if (!nPtr) return 0.0f;
|
||||
|
||||
auto a = nPtr;
|
||||
auto iter = nPtr;
|
||||
auto val = 0.0f;
|
||||
unsigned long long integerPart = 0;
|
||||
int minus = 1;
|
||||
|
||||
//ignore leading whitespaces
|
||||
while (isspace(*iter)) iter++;
|
||||
|
||||
//signed or not
|
||||
if (*iter == '-') {
|
||||
minus = -1;
|
||||
iter++;
|
||||
} else if (*iter == '+') {
|
||||
iter++;
|
||||
}
|
||||
|
||||
if (tolower(*iter) == 'i') {
|
||||
if ((tolower(*(iter + 1)) == 'n') && (tolower(*(iter + 2)) == 'f')) iter += 3;
|
||||
else goto error;
|
||||
|
||||
if (tolower(*(iter)) == 'i') {
|
||||
if ((tolower(*(iter + 1)) == 'n') && (tolower(*(iter + 2)) == 'i') && (tolower(*(iter + 3)) == 't') &&
|
||||
(tolower(*(iter + 4)) == 'y'))
|
||||
iter += 5;
|
||||
else goto error;
|
||||
}
|
||||
if (endPtr) *endPtr = (char *) (iter);
|
||||
return (minus == -1) ? -INFINITY : INFINITY;
|
||||
}
|
||||
|
||||
if (tolower(*iter) == 'n') {
|
||||
if ((tolower(*(iter + 1)) == 'a') && (tolower(*(iter + 2)) == 'n')) iter += 3;
|
||||
else goto error;
|
||||
|
||||
if (endPtr) *endPtr = (char *) (iter);
|
||||
return (minus == -1) ? -NAN : NAN;
|
||||
}
|
||||
|
||||
//Optional: integer part before dot
|
||||
if (isdigit(*iter)) {
|
||||
for (; isdigit(*iter); iter++) {
|
||||
integerPart = integerPart * 10ULL + (unsigned long long) (*iter - '0');
|
||||
}
|
||||
a = iter;
|
||||
} else if (*iter != '.') {
|
||||
goto success;
|
||||
}
|
||||
|
||||
val = static_cast<float>(integerPart);
|
||||
|
||||
//Optional: decimal part after dot
|
||||
if (*iter == '.') {
|
||||
unsigned long long decimalPart = 0;
|
||||
unsigned long long pow10 = 1;
|
||||
int count = 0;
|
||||
|
||||
iter++;
|
||||
|
||||
if (isdigit(*iter)) {
|
||||
for (; isdigit(*iter); iter++, count++) {
|
||||
if (count < 19) {
|
||||
decimalPart = decimalPart * 10ULL + +static_cast<unsigned long long>(*iter - '0');
|
||||
pow10 *= 10ULL;
|
||||
}
|
||||
}
|
||||
} else if (isspace(*iter)) { //skip if there is a space after the dot.
|
||||
a = iter;
|
||||
goto success;
|
||||
}
|
||||
|
||||
val += static_cast<float>(decimalPart) / static_cast<float>(pow10);
|
||||
a = iter;
|
||||
}
|
||||
|
||||
//Optional: exponent
|
||||
if (*iter == 'e' || *iter == 'E') {
|
||||
++iter;
|
||||
|
||||
//Exception: svg may have 'em' unit for fonts. ex) 5em, 10.5em
|
||||
if ((*iter == 'm') || (*iter == 'M')) {
|
||||
//TODO: We don't support font em unit now, but has to multiply val * font size later...
|
||||
a = iter + 1;
|
||||
goto success;
|
||||
}
|
||||
|
||||
//signed or not
|
||||
int minus_e = 1;
|
||||
|
||||
if (*iter == '-') {
|
||||
minus_e = -1;
|
||||
++iter;
|
||||
} else if (*iter == '+') {
|
||||
iter++;
|
||||
}
|
||||
|
||||
unsigned int exponentPart = 0;
|
||||
|
||||
if (isdigit(*iter)) {
|
||||
while (*iter == '0') iter++;
|
||||
for (; isdigit(*iter); iter++) {
|
||||
exponentPart = exponentPart * 10U + static_cast<unsigned int>(*iter - '0');
|
||||
}
|
||||
} else if (!isdigit(*(a - 1))) {
|
||||
a = nPtr;
|
||||
goto success;
|
||||
} else if (*iter == 0) {
|
||||
goto success;
|
||||
}
|
||||
|
||||
//if ((_floatExact(val, 2.2250738585072011f)) && ((minus_e * static_cast<int>(exponentPart)) <= -308)) {
|
||||
if ((_floatExact(val, 1.175494351f)) && ((minus_e * static_cast<int>(exponentPart)) <= -38)) {
|
||||
//val *= 1.0e-308f;
|
||||
val *= 1.0e-38f;
|
||||
a = iter;
|
||||
goto success;
|
||||
}
|
||||
|
||||
a = iter;
|
||||
auto scale = 1.0f;
|
||||
|
||||
while (exponentPart >= 8U) {
|
||||
scale *= 1E8;
|
||||
exponentPart -= 8U;
|
||||
}
|
||||
while (exponentPart > 0U) {
|
||||
scale *= 10.0f;
|
||||
exponentPart--;
|
||||
}
|
||||
val = (minus_e == -1) ? (val / scale) : (val * scale);
|
||||
} else if ((iter > nPtr) && !isdigit(*(iter - 1))) {
|
||||
a = nPtr;
|
||||
goto success;
|
||||
}
|
||||
|
||||
success:
|
||||
if (endPtr) *endPtr = (char *)(a);
|
||||
return minus * val;
|
||||
|
||||
error:
|
||||
if (endPtr) *endPtr = (char *)(nPtr);
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
|
||||
int str2int(const char* str, size_t n)
|
||||
{
|
||||
int ret = 0;
|
||||
for(size_t i = 0; i < n; ++i) {
|
||||
ret = ret * 10 + (str[i] - '0');
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
char* strDuplicate(const char *str, size_t n)
|
||||
{
|
||||
auto len = strlen(str);
|
||||
if (len < n) n = len;
|
||||
|
||||
auto ret = (char *) malloc(n + 1);
|
||||
if (!ret) return nullptr;
|
||||
ret[n] = '\0';
|
||||
|
||||
return (char *) memcpy(ret, str, n);
|
||||
}
|
||||
|
||||
char* strDirname(const char* path)
|
||||
{
|
||||
const char *ptr = strrchr(path, '/');
|
||||
#ifdef _WIN32
|
||||
if (ptr) ptr = strrchr(ptr + 1, '\\');
|
||||
#endif
|
||||
int len = int(ptr + 1 - path); // +1 to include '/'
|
||||
return strDuplicate(path, len);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _TVG_STR_H_
|
||||
#define _TVG_STR_H_
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
namespace tvg
|
||||
{
|
||||
|
||||
float strToFloat(const char *nPtr, char **endPtr); //convert to float
|
||||
int str2int(const char* str, size_t n); //convert to integer
|
||||
char* strDuplicate(const char *str, size_t n); //copy the string
|
||||
char* strDirname(const char* path); //return the full directory name
|
||||
|
||||
}
|
||||
#endif //_TVG_STR_H_
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Reference in New Issue