thorvg: Update to 0.10.7

Fixes #81618.
This commit is contained in:
Rémi Verschelde 2023-09-18 14:49:04 +02:00
parent b905959f43
commit 81949c2cd2
No known key found for this signature in database
GPG key ID: C3336907360768E1
52 changed files with 2006 additions and 1636 deletions

View file

@ -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/

View file

@ -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

View file

@ -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"])

View file

@ -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(

View file

@ -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"])

View file

@ -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(

View file

@ -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:

View file

@ -5,5 +5,5 @@
#define THORVG_SVG_LOADER_SUPPORT
#define THORVG_VERSION_STRING "0.10.0"
#define THORVG_VERSION_STRING "0.10.7"
#endif

View file

@ -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;

View file

@ -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);

View file

@ -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

View file

@ -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

View file

@ -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;
}

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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];

View file

@ -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;

View file

@ -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();

View file

@ -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_

View file

@ -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;
}

View file

@ -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_

View file

@ -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;
}

View file

@ -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;

View file

@ -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;
}

View file

@ -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

View file

@ -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;

View file

@ -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;
}

View file

@ -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;
}
}

View file

@ -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();

View file

@ -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);
}

View file

@ -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

View file

@ -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;
}

View file

@ -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

View file

@ -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)) {

View file

@ -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))) {

View file

@ -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

View file

@ -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;

View file

@ -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) {

View file

@ -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);
}

View file

@ -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_

View file

@ -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);
}

View file

@ -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];

View file

@ -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;

View file

@ -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
}

View file

@ -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_

View file

@ -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};

239
thirdparty/thorvg/src/utils/tvgStr.cpp vendored Normal file
View file

@ -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);
}
}

37
thirdparty/thorvg/src/utils/tvgStr.h vendored Normal file
View file

@ -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_

View file

@ -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