thorvg: Update to 0.13.3, add webp loader
Remove embedded png loader, we use the external (libpng) one.
This commit is contained in:
parent
c4279fe3e0
commit
1cf9f37589
|
@ -12,8 +12,8 @@ thirdparty_obj = []
|
||||||
thirdparty_dir = "#thirdparty/thorvg/"
|
thirdparty_dir = "#thirdparty/thorvg/"
|
||||||
thirdparty_sources = [
|
thirdparty_sources = [
|
||||||
# common
|
# common
|
||||||
"src/common/tvgBezier.cpp",
|
|
||||||
"src/common/tvgCompressor.cpp",
|
"src/common/tvgCompressor.cpp",
|
||||||
|
"src/common/tvgLines.cpp",
|
||||||
"src/common/tvgMath.cpp",
|
"src/common/tvgMath.cpp",
|
||||||
"src/common/tvgStr.cpp",
|
"src/common/tvgStr.cpp",
|
||||||
# SVG parser
|
# SVG parser
|
||||||
|
@ -24,7 +24,9 @@ thirdparty_sources = [
|
||||||
"src/loaders/svg/tvgSvgUtil.cpp",
|
"src/loaders/svg/tvgSvgUtil.cpp",
|
||||||
"src/loaders/svg/tvgXmlParser.cpp",
|
"src/loaders/svg/tvgXmlParser.cpp",
|
||||||
"src/loaders/raw/tvgRawLoader.cpp",
|
"src/loaders/raw/tvgRawLoader.cpp",
|
||||||
|
# image loaders
|
||||||
"src/loaders/external_png/tvgPngLoader.cpp",
|
"src/loaders/external_png/tvgPngLoader.cpp",
|
||||||
|
"src/loaders/external_webp/tvgWebpLoader.cpp",
|
||||||
"src/loaders/jpg/tvgJpgd.cpp",
|
"src/loaders/jpg/tvgJpgd.cpp",
|
||||||
"src/loaders/jpg/tvgJpgLoader.cpp",
|
"src/loaders/jpg/tvgJpgLoader.cpp",
|
||||||
# renderer common
|
# renderer common
|
||||||
|
@ -74,7 +76,10 @@ env_thirdparty.Prepend(
|
||||||
thirdparty_dir + "src/renderer/sw_engine",
|
thirdparty_dir + "src/renderer/sw_engine",
|
||||||
thirdparty_dir + "src/loaders/raw",
|
thirdparty_dir + "src/loaders/raw",
|
||||||
thirdparty_dir + "src/loaders/external_png",
|
thirdparty_dir + "src/loaders/external_png",
|
||||||
|
thirdparty_dir + "src/loaders/external_webp",
|
||||||
thirdparty_dir + "src/loaders/jpg",
|
thirdparty_dir + "src/loaders/jpg",
|
||||||
|
"#thirdparty/libpng",
|
||||||
|
"#thirdparty/libwebp/src",
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -882,7 +882,7 @@ instead of `miniz.h` as an external dependency.
|
||||||
## thorvg
|
## thorvg
|
||||||
|
|
||||||
- Upstream: https://github.com/thorvg/thorvg
|
- Upstream: https://github.com/thorvg/thorvg
|
||||||
- Version: 0.12.9 (afa6d8499bd49141d99d5e40a4620bd9f6bc0467, 2024)
|
- Version: 0.13.3 (6235068cad8cad176ccd0cbcf82f25e985fbc258, 2024)
|
||||||
- License: MIT
|
- License: MIT
|
||||||
|
|
||||||
Files extracted from upstream source:
|
Files extracted from upstream source:
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
Hermet Park <hermet@lottiefiles.com>
|
Hermet Park <hermet@lottiefiles.com>, <chuneon.park@samsung.com>
|
||||||
Prudhvi Raj Vasireddi <prudhvi.raj@samsung.com>
|
Prudhvi Raj Vasireddi <prudhvi.raj@samsung.com>
|
||||||
Junsu Choi <jsuya.choi@samsung.com>
|
Junsu Choi <jsuya.choi@samsung.com>
|
||||||
Pranay Samanta <pranay.ks@samsung.com>
|
Pranay Samanta <pranay.ks@samsung.com>
|
||||||
Mateusz Palkowski <m.palkowski@samsung.com>
|
Mateusz Palkowski <m.palkowski@samsung.com>
|
||||||
Subhransu Mohanty <sub.mohanty@samsung.com>
|
Subhransu Mohanty <sub.mohanty@samsung.com>
|
||||||
Mira Grudzinska <veleveta@gmail.com>
|
Mira Grudzinska <veleveta@gmail.com>, <m.grudzinska@samsung.com>
|
||||||
Michal Szczecinski <m.szczecinsk@partner.samsung.com>
|
Michal Szczecinski <m.szczecinsk@partner.samsung.com>
|
||||||
Shinwoo Kim <cinoo.kim@samsung.com>
|
Shinwoo Kim <cinoo.kim@samsung.com>
|
||||||
Piotr Kalota <p.kalota@samsung.com>
|
Piotr Kalota <p.kalota@samsung.com>
|
||||||
|
@ -18,10 +18,12 @@ Rémi Verschelde <rverschelde@gmail.com>
|
||||||
Martin Liska <mliksa@suse.cz>
|
Martin Liska <mliksa@suse.cz>
|
||||||
Vincenzo Pupillo <vincenzo.pupillo@unimi.it>
|
Vincenzo Pupillo <vincenzo.pupillo@unimi.it>
|
||||||
EunSik Jeong <rinechran@outlook.jp>
|
EunSik Jeong <rinechran@outlook.jp>
|
||||||
Samsung Electronics Co., Ltd
|
|
||||||
Rafał Mikrut <mikrutrafal@protonmail.com>
|
Rafał Mikrut <mikrutrafal@protonmail.com>
|
||||||
Martin Capitanio <capnm@capitanio.org>
|
Martin Capitanio <capnm@capitanio.org>
|
||||||
RuiwenTang <tangruiwen1989@gmail.com>
|
RuiwenTang <tangruiwen1989@gmail.com>
|
||||||
YouJin Lee <ol-of@naver.com>
|
YouJin Lee <ol-of@naver.com>
|
||||||
SergeyLebedkin <sergii@lottiefiles.com>
|
SergeyLebedkin <sergii@lottiefiles.com>
|
||||||
Jinny You <jinny@lottiefiles.com>
|
Jinny You <jinny@lottiefiles.com>
|
||||||
|
Nattu Adnan <nattu@reallynattu.com>
|
||||||
|
Gabor Kiss-Vamosi <kisvegabor@gmail.com>
|
||||||
|
Lorcán Mc Donagh <lorcan@lmdsp.com>
|
||||||
|
|
|
@ -5,10 +5,11 @@
|
||||||
#define THORVG_SVG_LOADER_SUPPORT
|
#define THORVG_SVG_LOADER_SUPPORT
|
||||||
#define THORVG_PNG_LOADER_SUPPORT
|
#define THORVG_PNG_LOADER_SUPPORT
|
||||||
#define THORVG_JPG_LOADER_SUPPORT
|
#define THORVG_JPG_LOADER_SUPPORT
|
||||||
|
#define THORVG_WEBP_LOADER_SUPPORT
|
||||||
#define THORVG_THREAD_SUPPORT
|
#define THORVG_THREAD_SUPPORT
|
||||||
|
|
||||||
// For internal debugging:
|
// For internal debugging:
|
||||||
//#define THORVG_LOG_ENABLED
|
//#define THORVG_LOG_ENABLED
|
||||||
|
|
||||||
#define THORVG_VERSION_STRING "0.12.10"
|
#define THORVG_VERSION_STRING "0.13.3"
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1645,7 +1645,7 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Sets the target buffer for the rasterization.
|
* @brief Sets the drawing target for the rasterization.
|
||||||
*
|
*
|
||||||
* The buffer of a desirable size should be allocated and owned by the caller.
|
* The buffer of a desirable size should be allocated and owned by the caller.
|
||||||
*
|
*
|
||||||
|
@ -1714,13 +1714,22 @@ public:
|
||||||
~GlCanvas();
|
~GlCanvas();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Sets the target buffer for the rasterization.
|
* @brief Sets the drawing target for rasterization.
|
||||||
*
|
*
|
||||||
* @warning Please do not use it, this API is not official one. It could be modified in the next version.
|
* This function specifies the drawing target where the rasterization will occur. It can target
|
||||||
|
* a specific framebuffer object (FBO) or the main surface.
|
||||||
*
|
*
|
||||||
|
* @param[in] id The GL target ID, usually indicating the FBO ID. A value of @c 0 specifies the main surface.
|
||||||
|
* @param[in] w The width (in pixels) of the raster image.
|
||||||
|
* @param[in] h The height (in pixels) of the raster image.
|
||||||
|
*
|
||||||
|
* @warning This API is experimental and not officially supported. It may be modified or removed in future versions.
|
||||||
|
* @warning Drawing on the main surface is currently not permitted. If the identifier (@p id) is set to @c 0, the operation will be aborted.
|
||||||
|
*
|
||||||
|
* @note Currently, this only allows the GL_RGBA8 color space format.
|
||||||
* @note Experimental API
|
* @note Experimental API
|
||||||
*/
|
*/
|
||||||
Result target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h) noexcept;
|
Result target(int32_t id, uint32_t w, uint32_t h) noexcept;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Creates a new GlCanvas object.
|
* @brief Creates a new GlCanvas object.
|
||||||
|
@ -1828,7 +1837,7 @@ public:
|
||||||
*
|
*
|
||||||
* This class supports the display and control of animation frames.
|
* This class supports the display and control of animation frames.
|
||||||
*
|
*
|
||||||
* @note Experimental API
|
* @since 0.13
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class TVG_API Animation
|
class TVG_API Animation
|
||||||
|
@ -1845,9 +1854,11 @@ public:
|
||||||
* @retval Result::InsufficientCondition if the given @p no is the same as the current frame value.
|
* @retval Result::InsufficientCondition if the given @p no is the same as the current frame value.
|
||||||
* @retval Result::NonSupport The current Picture data does not support animations.
|
* @retval Result::NonSupport The current Picture data does not support animations.
|
||||||
*
|
*
|
||||||
|
* @note For efficiency, ThorVG ignores updates to the new frame value if the difference from the current frame value
|
||||||
|
* is less than 0.001. In such cases, it returns @c Result::InsufficientCondition.
|
||||||
|
*
|
||||||
* @see totalFrame()
|
* @see totalFrame()
|
||||||
*
|
*
|
||||||
* @note Experimental API
|
|
||||||
*/
|
*/
|
||||||
Result frame(float no) noexcept;
|
Result frame(float no) noexcept;
|
||||||
|
|
||||||
|
@ -1862,7 +1873,6 @@ public:
|
||||||
*
|
*
|
||||||
* @warning The picture instance is owned by Animation. It should not be deleted manually.
|
* @warning The picture instance is owned by Animation. It should not be deleted manually.
|
||||||
*
|
*
|
||||||
* @note Experimental API
|
|
||||||
*/
|
*/
|
||||||
Picture* picture() const noexcept;
|
Picture* picture() const noexcept;
|
||||||
|
|
||||||
|
@ -1876,7 +1886,6 @@ public:
|
||||||
* @see Animation::frame(float no)
|
* @see Animation::frame(float no)
|
||||||
* @see Animation::totalFrame()
|
* @see Animation::totalFrame()
|
||||||
*
|
*
|
||||||
* @note Experimental API
|
|
||||||
*/
|
*/
|
||||||
float curFrame() const noexcept;
|
float curFrame() const noexcept;
|
||||||
|
|
||||||
|
@ -1888,7 +1897,6 @@ public:
|
||||||
* @note Frame numbering starts from 0.
|
* @note Frame numbering starts from 0.
|
||||||
* @note If the Picture is not properly configured, this function will return 0.
|
* @note If the Picture is not properly configured, this function will return 0.
|
||||||
*
|
*
|
||||||
* @note Experimental API
|
|
||||||
*/
|
*/
|
||||||
float totalFrame() const noexcept;
|
float totalFrame() const noexcept;
|
||||||
|
|
||||||
|
@ -1899,16 +1907,52 @@ public:
|
||||||
*
|
*
|
||||||
* @note If the Picture is not properly configured, this function will return 0.
|
* @note If the Picture is not properly configured, this function will return 0.
|
||||||
*
|
*
|
||||||
* @% Experimental API
|
|
||||||
*/
|
*/
|
||||||
float duration() const noexcept;
|
float duration() const noexcept;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Specifies the playback segment of the animation.
|
||||||
|
*
|
||||||
|
* The set segment is designated as the play area of the animation.
|
||||||
|
* This is useful for playing a specific segment within the entire animation.
|
||||||
|
* After setting, the number of animation frames and the playback time are calculated
|
||||||
|
* by mapping the playback segment as the entire range.
|
||||||
|
*
|
||||||
|
* @param[in] begin segment start.
|
||||||
|
* @param[in] end segment end.
|
||||||
|
*
|
||||||
|
* @retval Result::Success When succeed.
|
||||||
|
* @retval Result::InsufficientCondition In case the animation is not loaded.
|
||||||
|
* @retval Result::InvalidArguments When the given parameter is invalid.
|
||||||
|
* @retval Result::NonSupport When it's not animatable.
|
||||||
|
*
|
||||||
|
* @note Range from 0.0~1.0
|
||||||
|
* @note If a marker has been specified, its range will be disregarded.
|
||||||
|
* @see LottieAnimation::segment(const char* marker)
|
||||||
|
* @note Experimental API
|
||||||
|
*/
|
||||||
|
Result segment(float begin, float end) noexcept;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Gets the current segment.
|
||||||
|
*
|
||||||
|
* @param[out] begin segment start.
|
||||||
|
* @param[out] end segment end.
|
||||||
|
*
|
||||||
|
* @retval Result::Success When succeed.
|
||||||
|
* @retval Result::InsufficientCondition In case the animation is not loaded.
|
||||||
|
* @retval Result::InvalidArguments When the given parameter is invalid.
|
||||||
|
* @retval Result::NonSupport When it's not animatable.
|
||||||
|
*
|
||||||
|
* @note Experimental API
|
||||||
|
*/
|
||||||
|
Result segment(float* begin, float* end = nullptr) noexcept;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Creates a new Animation object.
|
* @brief Creates a new Animation object.
|
||||||
*
|
*
|
||||||
* @return A new Animation object.
|
* @return A new Animation object.
|
||||||
*
|
*
|
||||||
* @note Experimental API
|
|
||||||
*/
|
*/
|
||||||
static std::unique_ptr<Animation> gen() noexcept;
|
static std::unique_ptr<Animation> gen() noexcept;
|
||||||
|
|
||||||
|
|
|
@ -61,6 +61,7 @@ struct Array
|
||||||
|
|
||||||
void push(Array<T>& rhs)
|
void push(Array<T>& rhs)
|
||||||
{
|
{
|
||||||
|
if (rhs.count == 0) return;
|
||||||
grow(rhs.count);
|
grow(rhs.count);
|
||||||
memcpy(data + count, rhs.data, rhs.count * sizeof(T));
|
memcpy(data + count, rhs.data, rhs.count * sizeof(T));
|
||||||
count += rhs.count;
|
count += rhs.count;
|
||||||
|
|
|
@ -458,11 +458,11 @@ size_t b64Decode(const char* encoded, const size_t len, char** decoded)
|
||||||
auto value2 = B64_INDEX[(size_t)encoded[1]];
|
auto value2 = B64_INDEX[(size_t)encoded[1]];
|
||||||
output[idx++] = (value1 << 2) + ((value2 & 0x30) >> 4);
|
output[idx++] = (value1 << 2) + ((value2 & 0x30) >> 4);
|
||||||
|
|
||||||
if (!encoded[2] || encoded[2] == '=' || encoded[2] == '.') break;
|
if (!encoded[2] || encoded[3] < 0 || encoded[2] == '=' || encoded[2] == '.') break;
|
||||||
auto value3 = B64_INDEX[(size_t)encoded[2]];
|
auto value3 = B64_INDEX[(size_t)encoded[2]];
|
||||||
output[idx++] = ((value2 & 0x0f) << 4) + ((value3 & 0x3c) >> 2);
|
output[idx++] = ((value2 & 0x0f) << 4) + ((value3 & 0x3c) >> 2);
|
||||||
|
|
||||||
if (!encoded[3] || encoded[3] == '=' || encoded[3] == '.') break;
|
if (!encoded[3] || encoded[3] < 0 || encoded[3] == '=' || encoded[3] == '.') break;
|
||||||
auto value4 = B64_INDEX[(size_t)encoded[3]];
|
auto value4 = B64_INDEX[(size_t)encoded[3]];
|
||||||
output[idx++] = ((value3 & 0x03) << 6) + value4;
|
output[idx++] = ((value3 & 0x03) << 6) + value4;
|
||||||
encoded += 4;
|
encoded += 4;
|
||||||
|
|
|
@ -21,9 +21,9 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "tvgMath.h"
|
#include "tvgMath.h"
|
||||||
#include "tvgBezier.h"
|
#include "tvgLines.h"
|
||||||
|
|
||||||
#define BEZIER_EPSILON 1e-4f
|
#define BEZIER_EPSILON 1e-2f
|
||||||
|
|
||||||
/************************************************************************/
|
/************************************************************************/
|
||||||
/* Internal Class Implementation */
|
/* Internal Class Implementation */
|
||||||
|
@ -101,6 +101,25 @@ float _bezAt(const Bezier& bz, float at, float length, LengthFunc lineLengthFunc
|
||||||
namespace tvg
|
namespace tvg
|
||||||
{
|
{
|
||||||
|
|
||||||
|
float lineLength(const Point& pt1, const Point& pt2)
|
||||||
|
{
|
||||||
|
return _lineLength(pt1, pt2);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void lineSplitAt(const Line& cur, float at, Line& left, Line& right)
|
||||||
|
{
|
||||||
|
auto len = lineLength(cur.pt1, cur.pt2);
|
||||||
|
auto dx = ((cur.pt2.x - cur.pt1.x) / len) * at;
|
||||||
|
auto dy = ((cur.pt2.y - cur.pt1.y) / len) * at;
|
||||||
|
left.pt1 = cur.pt1;
|
||||||
|
left.pt2.x = left.pt1.x + dx;
|
||||||
|
left.pt2.y = left.pt1.y + dy;
|
||||||
|
right.pt1 = left.pt2;
|
||||||
|
right.pt2 = cur.pt2;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void bezSplit(const Bezier& cur, Bezier& left, Bezier& right)
|
void bezSplit(const Bezier& cur, Bezier& left, Bezier& right)
|
||||||
{
|
{
|
||||||
auto c = (cur.ctrl1.x + cur.ctrl2.x) * 0.5f;
|
auto c = (cur.ctrl1.x + cur.ctrl2.x) * 0.5f;
|
||||||
|
@ -219,7 +238,7 @@ float bezAngleAt(const Bezier& bz, float t)
|
||||||
pt.x *= 3;
|
pt.x *= 3;
|
||||||
pt.y *= 3;
|
pt.y *= 3;
|
||||||
|
|
||||||
return atan2(pt.x, pt.y) * 180.0f / 3.141592f;
|
return mathRad2Deg(atan2(pt.x, pt.y));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,14 +20,24 @@
|
||||||
* SOFTWARE.
|
* SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _TVG_BEZIER_H_
|
#ifndef _TVG_LINES_H_
|
||||||
#define _TVG_BEZIER_H_
|
#define _TVG_LINES_H_
|
||||||
|
|
||||||
#include "tvgCommon.h"
|
#include "tvgCommon.h"
|
||||||
|
|
||||||
namespace tvg
|
namespace tvg
|
||||||
{
|
{
|
||||||
|
|
||||||
|
struct Line
|
||||||
|
{
|
||||||
|
Point pt1;
|
||||||
|
Point pt2;
|
||||||
|
};
|
||||||
|
|
||||||
|
float lineLength(const Point& pt1, const Point& pt2);
|
||||||
|
void lineSplitAt(const Line& cur, float at, Line& left, Line& right);
|
||||||
|
|
||||||
|
|
||||||
struct Bezier
|
struct Bezier
|
||||||
{
|
{
|
||||||
Point start;
|
Point start;
|
||||||
|
@ -48,4 +58,4 @@ float bezLengthApprox(const Bezier& cur);
|
||||||
float bezAtApprox(const Bezier& bz, float at, float length);
|
float bezAtApprox(const Bezier& bz, float at, float length);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif //_TVG_BEZIER_H_
|
#endif //_TVG_LINES_H_
|
|
@ -44,6 +44,18 @@ bool mathIdentity(const Matrix* m);
|
||||||
void mathMultiply(Point* pt, const Matrix* transform);
|
void mathMultiply(Point* pt, const Matrix* transform);
|
||||||
|
|
||||||
|
|
||||||
|
static inline float mathDeg2Rad(float degree)
|
||||||
|
{
|
||||||
|
return degree * (MATH_PI / 180.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline float mathRad2Deg(float radian)
|
||||||
|
{
|
||||||
|
return radian * (180.0f / MATH_PI);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static inline bool mathZero(float a)
|
static inline bool mathZero(float a)
|
||||||
{
|
{
|
||||||
return (fabsf(a) < FLT_EPSILON) ? true : false;
|
return (fabsf(a) < FLT_EPSILON) ? true : false;
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
#include <cmath>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <memory.h>
|
#include <memory.h>
|
||||||
#include "tvgMath.h"
|
#include "tvgMath.h"
|
||||||
|
@ -197,6 +198,8 @@ float strToFloat(const char *nPtr, char **endPtr)
|
||||||
|
|
||||||
success:
|
success:
|
||||||
if (endPtr) *endPtr = (char *)(a);
|
if (endPtr) *endPtr = (char *)(a);
|
||||||
|
if (!std::isfinite(val)) return 0.0f;
|
||||||
|
|
||||||
return minus * val;
|
return minus * val;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2021 - 2024 the ThorVG project. All rights reserved.
|
* Copyright (c) 2023 - 2024 the ThorVG project. All rights reserved.
|
||||||
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
@ -21,8 +21,9 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <memory.h>
|
#include <memory.h>
|
||||||
#include "tvgLoader.h"
|
#include <webp/decode.h>
|
||||||
#include "tvgPngLoader.h"
|
|
||||||
|
#include "tvgWebpLoader.h"
|
||||||
|
|
||||||
|
|
||||||
/************************************************************************/
|
/************************************************************************/
|
||||||
|
@ -30,23 +31,17 @@
|
||||||
/************************************************************************/
|
/************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
void PngLoader::run(unsigned tid)
|
void WebpLoader::run(unsigned tid)
|
||||||
{
|
{
|
||||||
auto width = static_cast<unsigned>(w);
|
//TODO: acquire the current colorspace format & pre-multiplied alpha image.
|
||||||
auto height = static_cast<unsigned>(h);
|
surface.buf8 = WebPDecodeBGRA(data, size, nullptr, nullptr);
|
||||||
|
surface.stride = (uint32_t)w;
|
||||||
state.info_raw.colortype = LCT_RGBA; //request this image format
|
surface.w = (uint32_t)w;
|
||||||
|
surface.h = (uint32_t)h;
|
||||||
if (lodepng_decode(&surface.buf8, &width, &height, &state, data, size)) {
|
|
||||||
TVGERR("PNG", "Failed to decode image");
|
|
||||||
}
|
|
||||||
|
|
||||||
//setup the surface
|
|
||||||
surface.stride = width;
|
|
||||||
surface.w = width;
|
|
||||||
surface.h = height;
|
|
||||||
surface.cs = ColorSpace::ABGR8888;
|
|
||||||
surface.channelSize = sizeof(uint32_t);
|
surface.channelSize = sizeof(uint32_t);
|
||||||
|
surface.cs = ColorSpace::ARGB8888;
|
||||||
|
surface.premultiplied = false;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -54,62 +49,58 @@ void PngLoader::run(unsigned tid)
|
||||||
/* External Class Implementation */
|
/* External Class Implementation */
|
||||||
/************************************************************************/
|
/************************************************************************/
|
||||||
|
|
||||||
PngLoader::PngLoader() : ImageLoader(FileType::Png)
|
WebpLoader::WebpLoader() : ImageLoader(FileType::Webp)
|
||||||
{
|
{
|
||||||
lodepng_state_init(&state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
PngLoader::~PngLoader()
|
WebpLoader::~WebpLoader()
|
||||||
{
|
{
|
||||||
|
this->done();
|
||||||
|
|
||||||
if (freeData) free(data);
|
if (freeData) free(data);
|
||||||
free(surface.buf8);
|
data = nullptr;
|
||||||
lodepng_state_cleanup(&state);
|
size = 0;
|
||||||
|
freeData = false;
|
||||||
|
WebPFree(surface.buf8);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool PngLoader::open(const string& path)
|
bool WebpLoader::open(const string& path)
|
||||||
{
|
{
|
||||||
auto pngFile = fopen(path.c_str(), "rb");
|
auto webpFile = fopen(path.c_str(), "rb");
|
||||||
if (!pngFile) return false;
|
if (!webpFile) return false;
|
||||||
|
|
||||||
auto ret = false;
|
auto ret = false;
|
||||||
|
|
||||||
//determine size
|
//determine size
|
||||||
if (fseek(pngFile, 0, SEEK_END) < 0) goto finalize;
|
if (fseek(webpFile, 0, SEEK_END) < 0) goto finalize;
|
||||||
if (((size = ftell(pngFile)) < 1)) goto finalize;
|
if (((size = ftell(webpFile)) < 1)) goto finalize;
|
||||||
if (fseek(pngFile, 0, SEEK_SET)) goto finalize;
|
if (fseek(webpFile, 0, SEEK_SET)) goto finalize;
|
||||||
|
|
||||||
data = (unsigned char *) malloc(size);
|
data = (unsigned char *) malloc(size);
|
||||||
if (!data) goto finalize;
|
if (!data) goto finalize;
|
||||||
|
|
||||||
freeData = true;
|
freeData = true;
|
||||||
|
|
||||||
if (fread(data, size, 1, pngFile) < 1) goto finalize;
|
if (fread(data, size, 1, webpFile) < 1) goto finalize;
|
||||||
|
|
||||||
lodepng_state_init(&state);
|
int width, height;
|
||||||
|
if (!WebPGetInfo(data, size, &width, &height)) goto finalize;
|
||||||
unsigned int width, height;
|
|
||||||
if (lodepng_inspect(&width, &height, &state, data, size) > 0) goto finalize;
|
|
||||||
|
|
||||||
w = static_cast<float>(width);
|
w = static_cast<float>(width);
|
||||||
h = static_cast<float>(height);
|
h = static_cast<float>(height);
|
||||||
|
|
||||||
ret = true;
|
ret = true;
|
||||||
|
|
||||||
goto finalize;
|
|
||||||
|
|
||||||
finalize:
|
finalize:
|
||||||
fclose(pngFile);
|
fclose(webpFile);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool PngLoader::open(const char* data, uint32_t size, bool copy)
|
bool WebpLoader::open(const char* data, uint32_t size, bool copy)
|
||||||
{
|
{
|
||||||
unsigned int width, height;
|
|
||||||
if (lodepng_inspect(&width, &height, &state, (unsigned char*)(data), size) > 0) return false;
|
|
||||||
|
|
||||||
if (copy) {
|
if (copy) {
|
||||||
this->data = (unsigned char *) malloc(size);
|
this->data = (unsigned char *) malloc(size);
|
||||||
if (!this->data) return false;
|
if (!this->data) return false;
|
||||||
|
@ -120,28 +111,32 @@ bool PngLoader::open(const char* data, uint32_t size, bool copy)
|
||||||
freeData = false;
|
freeData = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int width, height;
|
||||||
|
if (!WebPGetInfo(this->data, size, &width, &height)) return false;
|
||||||
|
|
||||||
w = static_cast<float>(width);
|
w = static_cast<float>(width);
|
||||||
h = static_cast<float>(height);
|
h = static_cast<float>(height);
|
||||||
|
surface.cs = ColorSpace::ARGB8888;
|
||||||
this->size = size;
|
this->size = size;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool PngLoader::read()
|
bool WebpLoader::read()
|
||||||
{
|
{
|
||||||
if (!data || w == 0 || h == 0) return false;
|
|
||||||
|
|
||||||
if (!LoadModule::read()) return true;
|
if (!LoadModule::read()) return true;
|
||||||
|
|
||||||
|
if (!data || w == 0 || h == 0) return false;
|
||||||
|
|
||||||
TaskScheduler::request(this);
|
TaskScheduler::request(this);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Surface* PngLoader::bitmap()
|
Surface* WebpLoader::bitmap()
|
||||||
{
|
{
|
||||||
this->done();
|
this->done();
|
||||||
|
|
||||||
return ImageLoader::bitmap();
|
return ImageLoader::bitmap();
|
||||||
}
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2021 - 2024 the ThorVG project. All rights reserved.
|
* Copyright (c) 2023 - 2024 the ThorVG project. All rights reserved.
|
||||||
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
@ -20,32 +20,30 @@
|
||||||
* SOFTWARE.
|
* SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _TVG_PNG_LOADER_H_
|
#ifndef _TVG_WEBP_LOADER_H_
|
||||||
#define _TVG_PNG_LOADER_H_
|
#define _TVG_WEBP_LOADER_H_
|
||||||
|
|
||||||
#include "tvgLodePng.h"
|
#include "tvgLoader.h"
|
||||||
#include "tvgTaskScheduler.h"
|
#include "tvgTaskScheduler.h"
|
||||||
|
|
||||||
|
class WebpLoader : public ImageLoader, public Task
|
||||||
class PngLoader : public ImageLoader, public Task
|
|
||||||
{
|
{
|
||||||
private:
|
|
||||||
LodePNGState state;
|
|
||||||
unsigned char* data = nullptr;
|
|
||||||
unsigned long size = 0;
|
|
||||||
bool freeData = false;
|
|
||||||
|
|
||||||
void run(unsigned tid) override;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
PngLoader();
|
WebpLoader();
|
||||||
~PngLoader();
|
~WebpLoader();
|
||||||
|
|
||||||
bool open(const string& path) override;
|
bool open(const string& path) override;
|
||||||
bool open(const char* data, uint32_t size, bool copy) override;
|
bool open(const char* data, uint32_t size, bool copy) override;
|
||||||
bool read() override;
|
bool read() override;
|
||||||
|
|
||||||
Surface* bitmap() override;
|
Surface* bitmap() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void run(unsigned tid) override;
|
||||||
|
|
||||||
|
unsigned char* data = nullptr;
|
||||||
|
unsigned long size = 0;
|
||||||
|
bool freeData = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif //_TVG_PNG_LOADER_H_
|
#endif //_TVG_WEBP_LOADER_H_
|
File diff suppressed because it is too large
Load Diff
|
@ -1,174 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2020 - 2024 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
LodePNG version 20200306
|
|
||||||
|
|
||||||
Copyright (c) 2005-2020 Lode Vandevenne
|
|
||||||
|
|
||||||
This software is provided 'as-is', without any express or implied
|
|
||||||
warranty. In no event will the authors be held liable for any damages
|
|
||||||
arising from the use of this software.
|
|
||||||
|
|
||||||
Permission is granted to anyone to use this software for any purpose,
|
|
||||||
including commercial applications, and to alter it and redistribute it
|
|
||||||
freely, subject to the following restrictions:
|
|
||||||
|
|
||||||
1. The origin of this software must not be misrepresented; you must not
|
|
||||||
claim that you wrote the original software. If you use this software
|
|
||||||
in a product, an acknowledgment in the product documentation would be
|
|
||||||
appreciated but is not required.
|
|
||||||
|
|
||||||
2. Altered source versions must be plainly marked as such, and must not be
|
|
||||||
misrepresented as being the original software.
|
|
||||||
|
|
||||||
3. This notice may not be removed or altered from any source
|
|
||||||
distribution.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _TVG_LODEPNG_H_
|
|
||||||
#define _TVG_LODEPNG_H_
|
|
||||||
|
|
||||||
#include <stddef.h>
|
|
||||||
|
|
||||||
/*The PNG color types (also used for raw image).*/
|
|
||||||
enum LodePNGColorType
|
|
||||||
{
|
|
||||||
LCT_GREY = 0, /*grayscale: 1,2,4,8,16 bit*/
|
|
||||||
LCT_RGB = 2, /*RGB: 8,16 bit*/
|
|
||||||
LCT_PALETTE = 3, /*palette: 1,2,4,8 bit*/
|
|
||||||
LCT_GREY_ALPHA = 4, /*grayscale with alpha: 8,16 bit*/
|
|
||||||
LCT_RGBA = 6, /*RGB with alpha: 8,16 bit*/
|
|
||||||
/*LCT_MAX_OCTET_VALUE lets the compiler allow this enum to represent any invalid
|
|
||||||
byte value from 0 to 255 that could be present in an invalid PNG file header. Do
|
|
||||||
not use, compare with or set the name LCT_MAX_OCTET_VALUE, instead either use
|
|
||||||
the valid color type names above, or numeric values like 1 or 7 when checking for
|
|
||||||
particular disallowed color type byte values, or cast to integer to print it.*/
|
|
||||||
LCT_MAX_OCTET_VALUE = 255
|
|
||||||
};
|
|
||||||
|
|
||||||
/*Settings for zlib decompression*/
|
|
||||||
struct LodePNGDecompressSettings
|
|
||||||
{
|
|
||||||
/* Check LodePNGDecoderSettings for more ignorable errors such as ignore_crc */
|
|
||||||
unsigned ignore_adler32; /*if 1, continue and don't give an error message if the Adler32 checksum is corrupted*/
|
|
||||||
unsigned ignore_nlen; /*ignore complement of len checksum in uncompressed blocks*/
|
|
||||||
|
|
||||||
/*use custom zlib decoder instead of built in one (default: null)*/
|
|
||||||
unsigned (*custom_zlib)(unsigned char**, size_t*, const unsigned char*, size_t, const LodePNGDecompressSettings*);
|
|
||||||
/*use custom deflate decoder instead of built in one (default: null) if custom_zlib is not null, custom_inflate is ignored (the zlib format uses deflate)*/
|
|
||||||
unsigned (*custom_inflate)(unsigned char**, size_t*, const unsigned char*, size_t, const LodePNGDecompressSettings*);
|
|
||||||
|
|
||||||
const void* custom_context; /*optional custom settings for custom functions*/
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
Color mode of an image. Contains all information required to decode the pixel
|
|
||||||
bits to RGBA colors. This information is the same as used in the PNG file
|
|
||||||
format, and is used both for PNG and raw image data in LodePNG.
|
|
||||||
*/
|
|
||||||
struct LodePNGColorMode
|
|
||||||
{
|
|
||||||
/*header (IHDR)*/
|
|
||||||
LodePNGColorType colortype; /*color type, see PNG standard or documentation further in this header file*/
|
|
||||||
unsigned bitdepth; /*bits per sample, see PNG standard or documentation further in this header file*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
palette (PLTE and tRNS)
|
|
||||||
|
|
||||||
Dynamically allocated with the colors of the palette, including alpha.
|
|
||||||
This field may not be allocated directly, use lodepng_color_mode_init first,
|
|
||||||
then lodepng_palette_add per color to correctly initialize it (to ensure size
|
|
||||||
of exactly 1024 bytes).
|
|
||||||
|
|
||||||
The alpha channels must be set as well, set them to 255 for opaque images.
|
|
||||||
|
|
||||||
When decoding, by default you can ignore this palette, since LodePNG already
|
|
||||||
fills the palette colors in the pixels of the raw RGBA output.
|
|
||||||
|
|
||||||
The palette is only supported for color type 3.
|
|
||||||
*/
|
|
||||||
unsigned char* palette; /*palette in RGBARGBA... order. Must be either 0, or when allocated must have 1024 bytes*/
|
|
||||||
size_t palettesize; /*palette size in number of colors (amount of used bytes is 4 * palettesize)*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
transparent color key (tRNS)
|
|
||||||
|
|
||||||
This color uses the same bit depth as the bitdepth value in this struct, which can be 1-bit to 16-bit.
|
|
||||||
For grayscale PNGs, r, g and b will all 3 be set to the same.
|
|
||||||
|
|
||||||
When decoding, by default you can ignore this information, since LodePNG sets
|
|
||||||
pixels with this key to transparent already in the raw RGBA output.
|
|
||||||
|
|
||||||
The color key is only supported for color types 0 and 2.
|
|
||||||
*/
|
|
||||||
unsigned key_defined; /*is a transparent color key given? 0 = false, 1 = true*/
|
|
||||||
unsigned key_r; /*red/grayscale component of color key*/
|
|
||||||
unsigned key_g; /*green component of color key*/
|
|
||||||
unsigned key_b; /*blue component of color key*/
|
|
||||||
};
|
|
||||||
|
|
||||||
/*Information about the PNG image, except pixels, width and height.*/
|
|
||||||
struct LodePNGInfo
|
|
||||||
{
|
|
||||||
/*header (IHDR), palette (PLTE) and transparency (tRNS) chunks*/
|
|
||||||
unsigned compression_method;/*compression method of the original file. Always 0.*/
|
|
||||||
unsigned filter_method; /*filter method of the original file*/
|
|
||||||
unsigned interlace_method; /*interlace method of the original file: 0=none, 1=Adam7*/
|
|
||||||
LodePNGColorMode color; /*color type and bits, palette and transparency of the PNG file*/
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
Settings for the decoder. This contains settings for the PNG and the Zlib
|
|
||||||
decoder, but not the Info settings from the Info structs.
|
|
||||||
*/
|
|
||||||
struct LodePNGDecoderSettings
|
|
||||||
{
|
|
||||||
LodePNGDecompressSettings zlibsettings; /*in here is the setting to ignore Adler32 checksums*/
|
|
||||||
|
|
||||||
/* Check LodePNGDecompressSettings for more ignorable errors such as ignore_adler32 */
|
|
||||||
unsigned ignore_crc; /*ignore CRC checksums*/
|
|
||||||
unsigned ignore_critical; /*ignore unknown critical chunks*/
|
|
||||||
unsigned ignore_end; /*ignore issues at end of file if possible (missing IEND chunk, too large chunk, ...)*/
|
|
||||||
/* TODO: make a system involving warnings with levels and a strict mode instead. Other potentially recoverable
|
|
||||||
errors: srgb rendering intent value, size of content of ancillary chunks, more than 79 characters for some
|
|
||||||
strings, placement/combination rules for ancillary chunks, crc of unknown chunks, allowed characters
|
|
||||||
in string keys, etc... */
|
|
||||||
|
|
||||||
unsigned color_convert; /*whether to convert the PNG to the color type you want. Default: yes*/
|
|
||||||
};
|
|
||||||
|
|
||||||
/*The settings, state and information for extended encoding and decoding.*/
|
|
||||||
struct LodePNGState
|
|
||||||
{
|
|
||||||
LodePNGDecoderSettings decoder; /*the decoding settings*/
|
|
||||||
LodePNGColorMode info_raw; /*specifies the format in which you would like to get the raw pixel buffer*/
|
|
||||||
LodePNGInfo info_png; /*info of the PNG image obtained after decoding*/
|
|
||||||
unsigned error;
|
|
||||||
};
|
|
||||||
|
|
||||||
void lodepng_state_init(LodePNGState* state);
|
|
||||||
void lodepng_state_cleanup(LodePNGState* state);
|
|
||||||
unsigned lodepng_decode(unsigned char** out, unsigned* w, unsigned* h, LodePNGState* state, const unsigned char* in, size_t insize);
|
|
||||||
unsigned lodepng_inspect(unsigned* w, unsigned* h, LodePNGState* state, const unsigned char* in, size_t insize);
|
|
||||||
|
|
||||||
#endif //_TVG_LODEPNG_H_
|
|
|
@ -49,6 +49,22 @@ static void _copyStyle(SvgStyleProperty* to, const SvgStyleProperty* from)
|
||||||
to->flagsImportance = (to->flagsImportance | SvgStyleFlags::Color);
|
to->flagsImportance = (to->flagsImportance | SvgStyleFlags::Color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (((from->flags & SvgStyleFlags::PaintOrder) && !(to->flags & SvgStyleFlags::PaintOrder)) ||
|
||||||
|
_isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::PaintOrder)) {
|
||||||
|
to->paintOrder = from->paintOrder;
|
||||||
|
to->flags = (to->flags | SvgStyleFlags::PaintOrder);
|
||||||
|
if (from->flagsImportance & SvgStyleFlags::PaintOrder) {
|
||||||
|
to->flagsImportance = (to->flagsImportance | SvgStyleFlags::PaintOrder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (((from->flags & SvgStyleFlags::Display) && !(to->flags & SvgStyleFlags::Display)) ||
|
||||||
|
_isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::Display)) {
|
||||||
|
to->display = from->display;
|
||||||
|
to->flags = (to->flags | SvgStyleFlags::Display);
|
||||||
|
if (from->flagsImportance & SvgStyleFlags::Display) {
|
||||||
|
to->flagsImportance = (to->flagsImportance | SvgStyleFlags::Display);
|
||||||
|
}
|
||||||
|
}
|
||||||
//Fill
|
//Fill
|
||||||
if (((from->fill.flags & SvgFillFlags::Paint) && !(to->flags & SvgStyleFlags::Fill)) ||
|
if (((from->fill.flags & SvgFillFlags::Paint) && !(to->flags & SvgStyleFlags::Fill)) ||
|
||||||
_isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::Fill)) {
|
_isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::Fill)) {
|
||||||
|
|
|
@ -103,15 +103,19 @@ static const char* _skipComma(const char* content)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static bool _parseNumber(const char** content, float* number)
|
static bool _parseNumber(const char** content, const char** end, float* number)
|
||||||
{
|
{
|
||||||
char* end = nullptr;
|
const char* _end = end ? *end : nullptr;
|
||||||
|
|
||||||
*number = strToFloat(*content, &end);
|
*number = strToFloat(*content, (char**)&_end);
|
||||||
//If the start of string is not number
|
//If the start of string is not number
|
||||||
if ((*content) == end) return false;
|
if ((*content) == _end) {
|
||||||
|
if (end) *end = _end;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
//Skip comma if any
|
//Skip comma if any
|
||||||
*content = _skipComma(end);
|
*content = _skipComma(_end);
|
||||||
|
if (end) *end = _end;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -576,7 +580,82 @@ static constexpr struct
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
static void _toColor(const char* str, uint8_t* r, uint8_t* g, uint8_t* b, char** ref)
|
static bool _hslToRgb(float hue, float satuation, float brightness, uint8_t* red, uint8_t* green, uint8_t* blue)
|
||||||
|
{
|
||||||
|
if (!red || !green || !blue) return false;
|
||||||
|
|
||||||
|
float sv, vsf, f, p, q, t, v;
|
||||||
|
float _red = 0, _green = 0, _blue = 0;
|
||||||
|
uint32_t i = 0;
|
||||||
|
|
||||||
|
if (mathZero(satuation)) _red = _green = _blue = brightness;
|
||||||
|
else {
|
||||||
|
if (mathEqual(hue, 360.0)) hue = 0.0f;
|
||||||
|
hue /= 60.0f;
|
||||||
|
|
||||||
|
v = (brightness <= 0.5f) ? (brightness * (1.0f + satuation)) : (brightness + satuation - (brightness * satuation));
|
||||||
|
p = brightness + brightness - v;
|
||||||
|
|
||||||
|
if (!mathZero(v)) sv = (v - p) / v;
|
||||||
|
else sv = 0;
|
||||||
|
|
||||||
|
i = static_cast<uint8_t>(hue);
|
||||||
|
f = hue - i;
|
||||||
|
|
||||||
|
vsf = v * sv * f;
|
||||||
|
|
||||||
|
t = p + vsf;
|
||||||
|
q = v - vsf;
|
||||||
|
|
||||||
|
switch (i) {
|
||||||
|
case 0: {
|
||||||
|
_red = v;
|
||||||
|
_green = t;
|
||||||
|
_blue = p;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 1: {
|
||||||
|
_red = q;
|
||||||
|
_green = v;
|
||||||
|
_blue = p;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 2: {
|
||||||
|
_red = p;
|
||||||
|
_green = v;
|
||||||
|
_blue = t;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 3: {
|
||||||
|
_red = p;
|
||||||
|
_green = q;
|
||||||
|
_blue = v;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 4: {
|
||||||
|
_red = t;
|
||||||
|
_green = p;
|
||||||
|
_blue = v;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 5: {
|
||||||
|
_red = v;
|
||||||
|
_green = p;
|
||||||
|
_blue = q;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*red = static_cast<uint8_t>(roundf(_red * 255.0f));
|
||||||
|
*green = static_cast<uint8_t>(roundf(_green * 255.0f));
|
||||||
|
*blue = static_cast<uint8_t>(roundf(_blue * 255.0f));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static bool _toColor(const char* str, uint8_t* r, uint8_t* g, uint8_t* b, char** ref)
|
||||||
{
|
{
|
||||||
unsigned int len = strlen(str);
|
unsigned int len = strlen(str);
|
||||||
char *red, *green, *blue;
|
char *red, *green, *blue;
|
||||||
|
@ -596,6 +675,7 @@ static void _toColor(const char* str, uint8_t* r, uint8_t* g, uint8_t* b, char**
|
||||||
tmp[1] = str[3];
|
tmp[1] = str[3];
|
||||||
*b = strtol(tmp, nullptr, 16);
|
*b = strtol(tmp, nullptr, 16);
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
} else if (len == 7 && str[0] == '#') {
|
} else if (len == 7 && str[0] == '#') {
|
||||||
if (isxdigit(str[1]) && isxdigit(str[2]) && isxdigit(str[3]) && isxdigit(str[4]) && isxdigit(str[5]) && isxdigit(str[6])) {
|
if (isxdigit(str[1]) && isxdigit(str[2]) && isxdigit(str[3]) && isxdigit(str[4]) && isxdigit(str[5]) && isxdigit(str[6])) {
|
||||||
char tmp[3] = { '\0', '\0', '\0' };
|
char tmp[3] = { '\0', '\0', '\0' };
|
||||||
|
@ -609,6 +689,7 @@ static void _toColor(const char* str, uint8_t* r, uint8_t* g, uint8_t* b, char**
|
||||||
tmp[1] = str[6];
|
tmp[1] = str[6];
|
||||||
*b = strtol(tmp, nullptr, 16);
|
*b = strtol(tmp, nullptr, 16);
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
} else if (len >= 10 && (str[0] == 'r' || str[0] == 'R') && (str[1] == 'g' || str[1] == 'G') && (str[2] == 'b' || str[2] == 'B') && str[3] == '(' && str[len - 1] == ')') {
|
} else if (len >= 10 && (str[0] == 'r' || str[0] == 'R') && (str[1] == 'g' || str[1] == 'G') && (str[2] == 'b' || str[2] == 'B') && str[3] == '(' && str[len - 1] == ')') {
|
||||||
tr = _parseColor(str + 4, &red);
|
tr = _parseColor(str + 4, &red);
|
||||||
if (red && *red == ',') {
|
if (red && *red == ',') {
|
||||||
|
@ -622,9 +703,35 @@ static void _toColor(const char* str, uint8_t* r, uint8_t* g, uint8_t* b, char**
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
} else if (ref && len >= 3 && !strncmp(str, "url", 3)) {
|
} else if (ref && len >= 3 && !strncmp(str, "url", 3)) {
|
||||||
if (*ref) free(*ref);
|
if (*ref) free(*ref);
|
||||||
*ref = _idFromUrl((const char*)(str + 3));
|
*ref = _idFromUrl((const char*)(str + 3));
|
||||||
|
return true;
|
||||||
|
} else if (len >= 10 && (str[0] == 'h' || str[0] == 'H') && (str[1] == 's' || str[1] == 'S') && (str[2] == 'l' || str[2] == 'L') && str[3] == '(' && str[len - 1] == ')') {
|
||||||
|
float_t th, ts, tb;
|
||||||
|
const char *content, *hue, *satuation, *brightness;
|
||||||
|
content = str + 4;
|
||||||
|
content = _skipSpace(content, nullptr);
|
||||||
|
if (_parseNumber(&content, &hue, &th) && hue) {
|
||||||
|
th = float(uint32_t(th) % 360);
|
||||||
|
hue = _skipSpace(hue, nullptr);
|
||||||
|
hue = (char*)_skipComma(hue);
|
||||||
|
hue = _skipSpace(hue, nullptr);
|
||||||
|
if (_parseNumber(&hue, &satuation, &ts) && satuation && *satuation == '%') {
|
||||||
|
ts /= 100.0f;
|
||||||
|
satuation = _skipSpace(satuation + 1, nullptr);
|
||||||
|
satuation = (char*)_skipComma(satuation);
|
||||||
|
satuation = _skipSpace(satuation, nullptr);
|
||||||
|
if (_parseNumber(&satuation, &brightness, &tb) && brightness && *brightness == '%') {
|
||||||
|
tb /= 100.0f;
|
||||||
|
brightness = _skipSpace(brightness + 1, nullptr);
|
||||||
|
if (brightness && brightness[0] == ')' && brightness[1] == '\0') {
|
||||||
|
return _hslToRgb(th, ts, tb, r, g, b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
//Handle named color
|
//Handle named color
|
||||||
for (unsigned int i = 0; i < (sizeof(colors) / sizeof(colors[0])); i++) {
|
for (unsigned int i = 0; i < (sizeof(colors) / sizeof(colors[0])); i++) {
|
||||||
|
@ -632,10 +739,11 @@ static void _toColor(const char* str, uint8_t* r, uint8_t* g, uint8_t* b, char**
|
||||||
*r = (((uint8_t*)(&(colors[i].value)))[2]);
|
*r = (((uint8_t*)(&(colors[i].value)))[2]);
|
||||||
*g = (((uint8_t*)(&(colors[i].value)))[1]);
|
*g = (((uint8_t*)(&(colors[i].value)))[1]);
|
||||||
*b = (((uint8_t*)(&(colors[i].value)))[0]);
|
*b = (((uint8_t*)(&(colors[i].value)))[0]);
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -745,8 +853,8 @@ static Matrix* _parseTransformationMatrix(const char* value)
|
||||||
//Transform to signed.
|
//Transform to signed.
|
||||||
points[0] = fmodf(points[0], 360.0f);
|
points[0] = fmodf(points[0], 360.0f);
|
||||||
if (points[0] < 0) points[0] += 360.0f;
|
if (points[0] < 0) points[0] += 360.0f;
|
||||||
auto c = cosf(points[0] * (MATH_PI / 180.0f));
|
auto c = cosf(mathDeg2Rad(points[0]));
|
||||||
auto s = sinf(points[0] * (MATH_PI / 180.0f));
|
auto s = sinf(mathDeg2Rad(points[0]));
|
||||||
if (ptCount == 1) {
|
if (ptCount == 1) {
|
||||||
Matrix tmp = { c, -s, 0, s, c, 0, 0, 0, 1 };
|
Matrix tmp = { c, -s, 0, s, c, 0, 0, 0, 1 };
|
||||||
*matrix = mathMultiply(matrix, &tmp);
|
*matrix = mathMultiply(matrix, &tmp);
|
||||||
|
@ -769,12 +877,12 @@ static Matrix* _parseTransformationMatrix(const char* value)
|
||||||
*matrix = mathMultiply(matrix, &tmp);
|
*matrix = mathMultiply(matrix, &tmp);
|
||||||
} else if (state == MatrixState::SkewX) {
|
} else if (state == MatrixState::SkewX) {
|
||||||
if (ptCount != 1) goto error;
|
if (ptCount != 1) goto error;
|
||||||
auto deg = tanf(points[0] * (MATH_PI / 180.0f));
|
auto deg = tanf(mathDeg2Rad(points[0]));
|
||||||
Matrix tmp = { 1, deg, 0, 0, 1, 0, 0, 0, 1 };
|
Matrix tmp = { 1, deg, 0, 0, 1, 0, 0, 0, 1 };
|
||||||
*matrix = mathMultiply(matrix, &tmp);
|
*matrix = mathMultiply(matrix, &tmp);
|
||||||
} else if (state == MatrixState::SkewY) {
|
} else if (state == MatrixState::SkewY) {
|
||||||
if (ptCount != 1) goto error;
|
if (ptCount != 1) goto error;
|
||||||
auto deg = tanf(points[0] * (MATH_PI / 180.0f));
|
auto deg = tanf(mathDeg2Rad(points[0]));
|
||||||
Matrix tmp = { 1, 0, 0, deg, 1, 0, 0, 0, 1 };
|
Matrix tmp = { 1, 0, 0, deg, 1, 0, 0, 0, 1 };
|
||||||
*matrix = mathMultiply(matrix, &tmp);
|
*matrix = mathMultiply(matrix, &tmp);
|
||||||
}
|
}
|
||||||
|
@ -854,10 +962,10 @@ static bool _attrParseSvgNode(void* data, const char* key, const char* value)
|
||||||
doc->viewFlag = (doc->viewFlag | SvgViewFlag::Height);
|
doc->viewFlag = (doc->viewFlag | SvgViewFlag::Height);
|
||||||
}
|
}
|
||||||
} else if (!strcmp(key, "viewBox")) {
|
} else if (!strcmp(key, "viewBox")) {
|
||||||
if (_parseNumber(&value, &doc->vx)) {
|
if (_parseNumber(&value, nullptr, &doc->vx)) {
|
||||||
if (_parseNumber(&value, &doc->vy)) {
|
if (_parseNumber(&value, nullptr, &doc->vy)) {
|
||||||
if (_parseNumber(&value, &doc->vw)) {
|
if (_parseNumber(&value, nullptr, &doc->vw)) {
|
||||||
if (_parseNumber(&value, &doc->vh)) {
|
if (_parseNumber(&value, nullptr, &doc->vh)) {
|
||||||
doc->viewFlag = (doc->viewFlag | SvgViewFlag::Viewbox);
|
doc->viewFlag = (doc->viewFlag | SvgViewFlag::Viewbox);
|
||||||
loader->svgParse->global.h = doc->vh;
|
loader->svgParse->global.h = doc->vh;
|
||||||
}
|
}
|
||||||
|
@ -898,20 +1006,21 @@ static void _handlePaintAttr(SvgPaint* paint, const char* value)
|
||||||
paint->none = true;
|
paint->none = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
paint->none = false;
|
|
||||||
if (!strcmp(value, "currentColor")) {
|
if (!strcmp(value, "currentColor")) {
|
||||||
paint->curColor = true;
|
paint->curColor = true;
|
||||||
|
paint->none = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_toColor(value, &paint->color.r, &paint->color.g, &paint->color.b, &paint->url);
|
if (_toColor(value, &paint->color.r, &paint->color.g, &paint->color.b, &paint->url)) paint->none = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void _handleColorAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
|
static void _handleColorAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
|
||||||
{
|
{
|
||||||
SvgStyleProperty* style = node->style;
|
SvgStyleProperty* style = node->style;
|
||||||
style->curColorSet = true;
|
if (_toColor(value, &style->color.r, &style->color.g, &style->color.b, nullptr)) {
|
||||||
_toColor(value, &style->color.r, &style->color.g, &style->color.b, nullptr);
|
style->curColorSet = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -995,6 +1104,7 @@ static void _handleFillRuleAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node,
|
||||||
|
|
||||||
static void _handleOpacityAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
|
static void _handleOpacityAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
|
||||||
{
|
{
|
||||||
|
node->style->flags = (node->style->flags | SvgStyleFlags::Opacity);
|
||||||
node->style->opacity = _toOpacity(value);
|
node->style->opacity = _toOpacity(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1046,8 +1156,9 @@ static void _handleDisplayAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node,
|
||||||
// The default is "inline" which means visible and "none" means invisible.
|
// The default is "inline" which means visible and "none" means invisible.
|
||||||
// Depending on the type of node, additional functionality may be required.
|
// Depending on the type of node, additional functionality may be required.
|
||||||
// refer to https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/display
|
// refer to https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/display
|
||||||
if (!strcmp(value, "none")) node->display = false;
|
node->style->flags = (node->style->flags | SvgStyleFlags::Display);
|
||||||
else node->display = true;
|
if (!strcmp(value, "none")) node->style->display = false;
|
||||||
|
else node->style->display = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1267,8 +1378,8 @@ static bool _attrParseSymbolNode(void* data, const char* key, const char* value)
|
||||||
SvgSymbolNode* symbol = &(node->node.symbol);
|
SvgSymbolNode* symbol = &(node->node.symbol);
|
||||||
|
|
||||||
if (!strcmp(key, "viewBox")) {
|
if (!strcmp(key, "viewBox")) {
|
||||||
if (!_parseNumber(&value, &symbol->vx) || !_parseNumber(&value, &symbol->vy)) return false;
|
if (!_parseNumber(&value, nullptr, &symbol->vx) || !_parseNumber(&value, nullptr, &symbol->vy)) return false;
|
||||||
if (!_parseNumber(&value, &symbol->vw) || !_parseNumber(&value, &symbol->vh)) return false;
|
if (!_parseNumber(&value, nullptr, &symbol->vw) || !_parseNumber(&value, nullptr, &symbol->vh)) return false;
|
||||||
symbol->hasViewBox = true;
|
symbol->hasViewBox = true;
|
||||||
} else if (!strcmp(key, "width")) {
|
} else if (!strcmp(key, "width")) {
|
||||||
symbol->w = _toFloat(loader->svgParse, value, SvgParserLengthType::Horizontal);
|
symbol->w = _toFloat(loader->svgParse, value, SvgParserLengthType::Horizontal);
|
||||||
|
@ -1332,7 +1443,7 @@ static SvgNode* _createNode(SvgNode* parent, SvgNodeType type)
|
||||||
node->style->paintOrder = _toPaintOrder("fill stroke");
|
node->style->paintOrder = _toPaintOrder("fill stroke");
|
||||||
|
|
||||||
//Default display is true("inline").
|
//Default display is true("inline").
|
||||||
node->display = true;
|
node->style->display = true;
|
||||||
|
|
||||||
node->parent = parent;
|
node->parent = parent;
|
||||||
node->type = type;
|
node->type = type;
|
||||||
|
@ -1408,7 +1519,7 @@ static SvgNode* _createClipPathNode(SvgLoaderData* loader, SvgNode* parent, cons
|
||||||
loader->svgParse->node = _createNode(parent, SvgNodeType::ClipPath);
|
loader->svgParse->node = _createNode(parent, SvgNodeType::ClipPath);
|
||||||
if (!loader->svgParse->node) return nullptr;
|
if (!loader->svgParse->node) return nullptr;
|
||||||
|
|
||||||
loader->svgParse->node->display = false;
|
loader->svgParse->node->style->display = false;
|
||||||
loader->svgParse->node->node.clip.userSpace = true;
|
loader->svgParse->node->node.clip.userSpace = true;
|
||||||
|
|
||||||
func(buf, bufLength, _attrParseClipPathNode, loader);
|
func(buf, bufLength, _attrParseClipPathNode, loader);
|
||||||
|
@ -1433,7 +1544,6 @@ static SvgNode* _createSymbolNode(SvgLoaderData* loader, SvgNode* parent, const
|
||||||
loader->svgParse->node = _createNode(parent, SvgNodeType::Symbol);
|
loader->svgParse->node = _createNode(parent, SvgNodeType::Symbol);
|
||||||
if (!loader->svgParse->node) return nullptr;
|
if (!loader->svgParse->node) return nullptr;
|
||||||
|
|
||||||
loader->svgParse->node->display = false;
|
|
||||||
loader->svgParse->node->node.symbol.align = AspectRatioAlign::XMidYMid;
|
loader->svgParse->node->node.symbol.align = AspectRatioAlign::XMidYMid;
|
||||||
loader->svgParse->node->node.symbol.meetOrSlice = AspectRatioMeetOrSlice::Meet;
|
loader->svgParse->node->node.symbol.meetOrSlice = AspectRatioMeetOrSlice::Meet;
|
||||||
loader->svgParse->node->node.symbol.overflowVisible = false;
|
loader->svgParse->node->node.symbol.overflowVisible = false;
|
||||||
|
@ -1615,8 +1725,11 @@ static SvgNode* _createEllipseNode(SvgLoaderData* loader, SvgNode* parent, const
|
||||||
|
|
||||||
static bool _attrParsePolygonPoints(const char* str, SvgPolygonNode* polygon)
|
static bool _attrParsePolygonPoints(const char* str, SvgPolygonNode* polygon)
|
||||||
{
|
{
|
||||||
float num;
|
float num_x, num_y;
|
||||||
while (_parseNumber(&str, &num)) polygon->pts.push(num);
|
while (_parseNumber(&str, nullptr, &num_x) && _parseNumber(&str, nullptr, &num_y)) {
|
||||||
|
polygon->pts.push(num_x);
|
||||||
|
polygon->pts.push(num_y);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2378,8 +2491,9 @@ static bool _attrParseStopsStyle(void* data, const char* key, const char* value)
|
||||||
stop->a = _toOpacity(value);
|
stop->a = _toOpacity(value);
|
||||||
loader->svgParse->flags = (loader->svgParse->flags | SvgStopStyleFlags::StopOpacity);
|
loader->svgParse->flags = (loader->svgParse->flags | SvgStopStyleFlags::StopOpacity);
|
||||||
} else if (!strcmp(key, "stop-color")) {
|
} else if (!strcmp(key, "stop-color")) {
|
||||||
_toColor(value, &stop->r, &stop->g, &stop->b, nullptr);
|
if (_toColor(value, &stop->r, &stop->g, &stop->b, nullptr)) {
|
||||||
loader->svgParse->flags = (loader->svgParse->flags | SvgStopStyleFlags::StopColor);
|
loader->svgParse->flags = (loader->svgParse->flags | SvgStopStyleFlags::StopColor);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -2849,9 +2963,15 @@ static void _styleCopy(SvgStyleProperty* to, const SvgStyleProperty* from)
|
||||||
to->color = from->color;
|
to->color = from->color;
|
||||||
to->curColorSet = true;
|
to->curColorSet = true;
|
||||||
}
|
}
|
||||||
|
if (from->flags & SvgStyleFlags::Opacity) {
|
||||||
|
to->opacity = from->opacity;
|
||||||
|
}
|
||||||
if (from->flags & SvgStyleFlags::PaintOrder) {
|
if (from->flags & SvgStyleFlags::PaintOrder) {
|
||||||
to->paintOrder = from->paintOrder;
|
to->paintOrder = from->paintOrder;
|
||||||
}
|
}
|
||||||
|
if (from->flags & SvgStyleFlags::Display) {
|
||||||
|
to->display = from->display;
|
||||||
|
}
|
||||||
//Fill
|
//Fill
|
||||||
to->fill.flags = (to->fill.flags | from->fill.flags);
|
to->fill.flags = (to->fill.flags | from->fill.flags);
|
||||||
if (from->fill.flags & SvgFillFlags::Paint) {
|
if (from->fill.flags & SvgFillFlags::Paint) {
|
||||||
|
@ -3080,6 +3200,14 @@ static void _svgLoaderParserXmlClose(SvgLoaderData* loader, const char* content)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < sizeof(graphicsTags) / sizeof(graphicsTags[0]); i++) {
|
||||||
|
if (!strncmp(content, graphicsTags[i].tag, graphicsTags[i].sz - 1)) {
|
||||||
|
loader->currentGraphicsNode = nullptr;
|
||||||
|
loader->stack.pop();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
loader->level--;
|
loader->level--;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3146,6 +3274,11 @@ static void _svgLoaderParserXmlOpen(SvgLoaderData* loader, const char* content,
|
||||||
if (loader->stack.count > 0) parent = loader->stack.last();
|
if (loader->stack.count > 0) parent = loader->stack.last();
|
||||||
else parent = loader->doc;
|
else parent = loader->doc;
|
||||||
node = method(loader, parent, attrs, attrsLength, simpleXmlParseAttributes);
|
node = method(loader, parent, attrs, attrsLength, simpleXmlParseAttributes);
|
||||||
|
if (node && !empty) {
|
||||||
|
auto defs = _createDefsNode(loader, nullptr, nullptr, 0, nullptr);
|
||||||
|
loader->stack.push(defs);
|
||||||
|
loader->currentGraphicsNode = node;
|
||||||
|
}
|
||||||
} else if ((gradientMethod = _findGradientFactory(tagName))) {
|
} else if ((gradientMethod = _findGradientFactory(tagName))) {
|
||||||
SvgStyleGradient* gradient;
|
SvgStyleGradient* gradient;
|
||||||
gradient = gradientMethod(loader, attrs, attrsLength);
|
gradient = gradientMethod(loader, attrs, attrsLength);
|
||||||
|
@ -3257,7 +3390,7 @@ static void _inefficientNodeCheck(TVG_UNUSED SvgNode* node)
|
||||||
#ifdef THORVG_LOG_ENABLED
|
#ifdef THORVG_LOG_ENABLED
|
||||||
auto type = simpleXmlNodeTypeToString(node->type);
|
auto type = simpleXmlNodeTypeToString(node->type);
|
||||||
|
|
||||||
if (!node->display && node->type != SvgNodeType::ClipPath && node->type != SvgNodeType::Symbol) TVGLOG("SVG", "Inefficient elements used [Display is none][Node Type : %s]", type);
|
if (!node->style->display && node->type != SvgNodeType::ClipPath) TVGLOG("SVG", "Inefficient elements used [Display is none][Node Type : %s]", type);
|
||||||
if (node->style->opacity == 0) TVGLOG("SVG", "Inefficient elements used [Opacity is zero][Node Type : %s]", type);
|
if (node->style->opacity == 0) TVGLOG("SVG", "Inefficient elements used [Opacity is zero][Node Type : %s]", type);
|
||||||
if (node->style->fill.opacity == 0 && node->style->stroke.opacity == 0) TVGLOG("SVG", "Inefficient elements used [Fill opacity and stroke opacity are zero][Node Type : %s]", type);
|
if (node->style->fill.opacity == 0 && node->style->stroke.opacity == 0) TVGLOG("SVG", "Inefficient elements used [Fill opacity and stroke opacity are zero][Node Type : %s]", type);
|
||||||
|
|
||||||
|
@ -3699,10 +3832,11 @@ bool SvgLoader::open(const char* data, uint32_t size, bool copy)
|
||||||
clear();
|
clear();
|
||||||
|
|
||||||
if (copy) {
|
if (copy) {
|
||||||
content = (char*)malloc(size);
|
content = (char*)malloc(size + 1);
|
||||||
if (!content) return false;
|
if (!content) return false;
|
||||||
memcpy((char*)content, data, size);
|
memcpy((char*)content, data, size);
|
||||||
} else content = data;
|
content[size] = '\0';
|
||||||
|
} else content = (char*)data;
|
||||||
|
|
||||||
this->size = size;
|
this->size = size;
|
||||||
this->copy = copy;
|
this->copy = copy;
|
||||||
|
@ -3726,7 +3860,7 @@ bool SvgLoader::open(const string& path)
|
||||||
|
|
||||||
if (filePath.empty()) return false;
|
if (filePath.empty()) return false;
|
||||||
|
|
||||||
content = filePath.c_str();
|
content = (char*)filePath.c_str();
|
||||||
size = filePath.size();
|
size = filePath.size();
|
||||||
|
|
||||||
return header();
|
return header();
|
||||||
|
|
|
@ -31,7 +31,7 @@ class SvgLoader : public ImageLoader, public Task
|
||||||
public:
|
public:
|
||||||
string filePath;
|
string filePath;
|
||||||
string svgPath = "";
|
string svgPath = "";
|
||||||
const char* content = nullptr;
|
char* content = nullptr;
|
||||||
uint32_t size = 0;
|
uint32_t size = 0;
|
||||||
|
|
||||||
SvgLoaderData loaderData;
|
SvgLoaderData loaderData;
|
||||||
|
|
|
@ -485,11 +485,12 @@ struct SvgStyleProperty
|
||||||
SvgComposite mask;
|
SvgComposite mask;
|
||||||
int opacity;
|
int opacity;
|
||||||
SvgColor color;
|
SvgColor color;
|
||||||
bool curColorSet;
|
|
||||||
char* cssClass;
|
char* cssClass;
|
||||||
bool paintOrder; //true if default (fill, stroke), false otherwise
|
|
||||||
SvgStyleFlags flags;
|
SvgStyleFlags flags;
|
||||||
SvgStyleFlags flagsImportance; //indicates the importance of the flag - if set, higher priority is applied (https://drafts.csswg.org/css-cascade-4/#importance)
|
SvgStyleFlags flagsImportance; //indicates the importance of the flag - if set, higher priority is applied (https://drafts.csswg.org/css-cascade-4/#importance)
|
||||||
|
bool curColorSet;
|
||||||
|
bool paintOrder; //true if default (fill, stroke), false otherwise
|
||||||
|
bool display;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SvgNode
|
struct SvgNode
|
||||||
|
@ -518,7 +519,6 @@ struct SvgNode
|
||||||
SvgCssStyleNode cssStyle;
|
SvgCssStyleNode cssStyle;
|
||||||
SvgSymbolNode symbol;
|
SvgSymbolNode symbol;
|
||||||
} node;
|
} node;
|
||||||
bool display;
|
|
||||||
~SvgNode();
|
~SvgNode();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -560,6 +560,7 @@ struct SvgLoaderData
|
||||||
int level = 0;
|
int level = 0;
|
||||||
bool result = false;
|
bool result = false;
|
||||||
bool style = false;
|
bool style = false;
|
||||||
|
SvgNode* currentGraphicsNode = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Box
|
struct Box
|
||||||
|
|
|
@ -126,7 +126,7 @@ void _pathAppendArcTo(Array<PathCommand>* cmds, Array<Point>* pts, Point* cur, P
|
||||||
rx = fabsf(rx);
|
rx = fabsf(rx);
|
||||||
ry = fabsf(ry);
|
ry = fabsf(ry);
|
||||||
|
|
||||||
angle = angle * MATH_PI / 180.0f;
|
angle = mathDeg2Rad(angle);
|
||||||
cosPhi = cosf(angle);
|
cosPhi = cosf(angle);
|
||||||
sinPhi = sinf(angle);
|
sinPhi = sinf(angle);
|
||||||
dx2 = (sx - x) / 2.0f;
|
dx2 = (sx - x) / 2.0f;
|
||||||
|
@ -311,7 +311,7 @@ static int _numberCount(char cmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static bool _processCommand(Array<PathCommand>* cmds, Array<Point>* pts, char cmd, float* arr, int count, Point* cur, Point* curCtl, Point* startPoint, bool *isQuadratic)
|
static bool _processCommand(Array<PathCommand>* cmds, Array<Point>* pts, char cmd, float* arr, int count, Point* cur, Point* curCtl, Point* startPoint, bool *isQuadratic, bool* closed)
|
||||||
{
|
{
|
||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
case 'm':
|
case 'm':
|
||||||
|
@ -464,6 +464,7 @@ static bool _processCommand(Array<PathCommand>* cmds, Array<Point>* pts, char cm
|
||||||
case 'Z': {
|
case 'Z': {
|
||||||
cmds->push(PathCommand::Close);
|
cmds->push(PathCommand::Close);
|
||||||
*cur = *startPoint;
|
*cur = *startPoint;
|
||||||
|
*closed = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'a':
|
case 'a':
|
||||||
|
@ -488,7 +489,7 @@ static bool _processCommand(Array<PathCommand>* cmds, Array<Point>* pts, char cm
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static char* _nextCommand(char* path, char* cmd, float* arr, int* count)
|
static char* _nextCommand(char* path, char* cmd, float* arr, int* count, bool* closed)
|
||||||
{
|
{
|
||||||
int large, sweep;
|
int large, sweep;
|
||||||
|
|
||||||
|
@ -500,6 +501,9 @@ static char* _nextCommand(char* path, char* cmd, float* arr, int* count)
|
||||||
} else {
|
} else {
|
||||||
if (*cmd == 'm') *cmd = 'l';
|
if (*cmd == 'm') *cmd = 'l';
|
||||||
else if (*cmd == 'M') *cmd = 'L';
|
else if (*cmd == 'M') *cmd = 'L';
|
||||||
|
else {
|
||||||
|
if (*closed) return nullptr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (*count == 7) {
|
if (*count == 7) {
|
||||||
//Special case for arc command
|
//Special case for arc command
|
||||||
|
@ -548,6 +552,7 @@ bool svgPathToShape(const char* svgPath, Shape* shape)
|
||||||
Point startPoint = { 0, 0 };
|
Point startPoint = { 0, 0 };
|
||||||
char cmd = 0;
|
char cmd = 0;
|
||||||
bool isQuadratic = false;
|
bool isQuadratic = false;
|
||||||
|
bool closed = false;
|
||||||
char* path = (char*)svgPath;
|
char* path = (char*)svgPath;
|
||||||
|
|
||||||
auto& pts = P(shape)->rs.path.pts;
|
auto& pts = P(shape)->rs.path.pts;
|
||||||
|
@ -555,9 +560,10 @@ bool svgPathToShape(const char* svgPath, Shape* shape)
|
||||||
auto lastCmds = cmds.count;
|
auto lastCmds = cmds.count;
|
||||||
|
|
||||||
while ((path[0] != '\0')) {
|
while ((path[0] != '\0')) {
|
||||||
path = _nextCommand(path, &cmd, numberArray, &numberCount);
|
path = _nextCommand(path, &cmd, numberArray, &numberCount, &closed);
|
||||||
if (!path) break;
|
if (!path) break;
|
||||||
if (!_processCommand(&cmds, &pts, cmd, numberArray, numberCount, &cur, &curCtl, &startPoint, &isQuadratic)) break;
|
closed = false;
|
||||||
|
if (!_processCommand(&cmds, &pts, cmd, numberArray, numberCount, &cur, &curCtl, &startPoint, &isQuadratic, &closed)) break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cmds.count > lastCmds && cmds[lastCmds] != PathCommand::MoveTo) return false;
|
if (cmds.count > lastCmds && cmds[lastCmds] != PathCommand::MoveTo) return false;
|
||||||
|
|
|
@ -310,7 +310,7 @@ static void _applyProperty(SvgLoaderData& loaderData, SvgNode* node, Shape* vg,
|
||||||
|
|
||||||
//Clip transformation is applied directly to the path in the _appendClipShape function
|
//Clip transformation is applied directly to the path in the _appendClipShape function
|
||||||
if (node->transform && !clip) vg->transform(*node->transform);
|
if (node->transform && !clip) vg->transform(*node->transform);
|
||||||
if (node->type == SvgNodeType::Doc || !node->display) return;
|
if (node->type == SvgNodeType::Doc || !node->style->display) return;
|
||||||
|
|
||||||
//If fill property is nullptr then do nothing
|
//If fill property is nullptr then do nothing
|
||||||
if (style->fill.paint.none) {
|
if (style->fill.paint.none) {
|
||||||
|
@ -557,7 +557,7 @@ static bool _isValidImageMimeTypeAndEncoding(const char** href, const char** mim
|
||||||
|
|
||||||
static unique_ptr<Picture> _imageBuildHelper(SvgLoaderData& loaderData, SvgNode* node, const Box& vBox, const string& svgPath)
|
static unique_ptr<Picture> _imageBuildHelper(SvgLoaderData& loaderData, SvgNode* node, const Box& vBox, const string& svgPath)
|
||||||
{
|
{
|
||||||
if (!node->node.image.href) return nullptr;
|
if (!node->node.image.href || !strlen(node->node.image.href)) return nullptr;
|
||||||
auto picture = Picture::gen();
|
auto picture = Picture::gen();
|
||||||
|
|
||||||
TaskScheduler::async(false); //force to load a picture on the same thread
|
TaskScheduler::async(false); //force to load a picture on the same thread
|
||||||
|
@ -782,13 +782,13 @@ static unique_ptr<Scene> _sceneBuildHelper(SvgLoaderData& loaderData, const SvgN
|
||||||
// For a Symbol node, the viewBox transformation has to be applied first - see _useBuildHelper()
|
// For a Symbol node, the viewBox transformation has to be applied first - see _useBuildHelper()
|
||||||
if (!mask && node->transform && node->type != SvgNodeType::Symbol) scene->transform(*node->transform);
|
if (!mask && node->transform && node->type != SvgNodeType::Symbol) scene->transform(*node->transform);
|
||||||
|
|
||||||
if (node->display && node->style->opacity != 0) {
|
if (node->style->display && node->style->opacity != 0) {
|
||||||
auto child = node->child.data;
|
auto child = node->child.data;
|
||||||
for (uint32_t i = 0; i < node->child.count; ++i, ++child) {
|
for (uint32_t i = 0; i < node->child.count; ++i, ++child) {
|
||||||
if (_isGroupType((*child)->type)) {
|
if (_isGroupType((*child)->type)) {
|
||||||
if ((*child)->type == SvgNodeType::Use)
|
if ((*child)->type == SvgNodeType::Use)
|
||||||
scene->push(_useBuildHelper(loaderData, *child, vBox, svgPath, depth + 1, isMaskWhite));
|
scene->push(_useBuildHelper(loaderData, *child, vBox, svgPath, depth + 1, isMaskWhite));
|
||||||
else
|
else if (!((*child)->type == SvgNodeType::Symbol && node->type != SvgNodeType::Use))
|
||||||
scene->push(_sceneBuildHelper(loaderData, *child, vBox, svgPath, false, depth + 1, isMaskWhite));
|
scene->push(_sceneBuildHelper(loaderData, *child, vBox, svgPath, false, depth + 1, isMaskWhite));
|
||||||
} else if ((*child)->type == SvgNodeType::Image) {
|
} else if ((*child)->type == SvgNodeType::Image) {
|
||||||
auto image = _imageBuildHelper(loaderData, *child, vBox, svgPath);
|
auto image = _imageBuildHelper(loaderData, *child, vBox, svgPath);
|
||||||
|
|
|
@ -47,7 +47,6 @@ size_t svgUtilURLDecode(const char *src, char** dst)
|
||||||
if (length == 0) return 0;
|
if (length == 0) return 0;
|
||||||
|
|
||||||
char* decoded = (char*)malloc(sizeof(char) * length + 1);
|
char* decoded = (char*)malloc(sizeof(char) * length + 1);
|
||||||
decoded[length] = '\0';
|
|
||||||
|
|
||||||
char a, b;
|
char a, b;
|
||||||
int idx =0;
|
int idx =0;
|
||||||
|
@ -64,7 +63,9 @@ size_t svgUtilURLDecode(const char *src, char** dst)
|
||||||
decoded[idx++] = *src++;
|
decoded[idx++] = *src++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
decoded[idx] = '\0';
|
||||||
|
|
||||||
*dst = decoded;
|
*dst = decoded;
|
||||||
return length + 1;
|
return idx + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -314,7 +314,10 @@ bool simpleXmlParseAttributes(const char* buf, unsigned bufLength, simpleXMLAttr
|
||||||
if ((*keyEnd == '=') || (isspace((unsigned char)*keyEnd))) break;
|
if ((*keyEnd == '=') || (isspace((unsigned char)*keyEnd))) break;
|
||||||
}
|
}
|
||||||
if (keyEnd == itrEnd) goto error;
|
if (keyEnd == itrEnd) goto error;
|
||||||
if (keyEnd == key) continue;
|
if (keyEnd == key) { // There is no key. This case is invalid, but explores the following syntax.
|
||||||
|
itr = keyEnd + 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (*keyEnd == '=') value = keyEnd + 1;
|
if (*keyEnd == '=') value = keyEnd + 1;
|
||||||
else {
|
else {
|
||||||
|
|
|
@ -1755,8 +1755,13 @@ static bool _rasterRadialGradientRle(SwSurface* surface, const SwRleData* rle, c
|
||||||
|
|
||||||
void rasterGrayscale8(uint8_t *dst, uint8_t val, uint32_t offset, int32_t len)
|
void rasterGrayscale8(uint8_t *dst, uint8_t val, uint32_t offset, int32_t len)
|
||||||
{
|
{
|
||||||
//OPTIMIZE_ME: Support SIMD
|
#if defined(THORVG_AVX_VECTOR_SUPPORT)
|
||||||
|
avxRasterGrayscale8(dst, val, offset, len);
|
||||||
|
#elif defined(THORVG_NEON_VECTOR_SUPPORT)
|
||||||
|
neonRasterGrayscale8(dst, val, offset, len);
|
||||||
|
#else
|
||||||
cRasterPixels(dst, val, offset, len);
|
cRasterPixels(dst, val, offset, len);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2021 - 2024 the ThorVG project. All rights reserved.
|
* Copyright (c) 2021 - 2024 the ThorVG project. All rights reserved.
|
||||||
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
@ -62,6 +62,23 @@ static inline __m128i ALPHA_BLEND(__m128i c, __m128i a)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void avxRasterGrayscale8(uint8_t* dst, uint8_t val, uint32_t offset, int32_t len)
|
||||||
|
{
|
||||||
|
dst += offset;
|
||||||
|
|
||||||
|
__m256i vecVal = _mm256_set1_epi8(val);
|
||||||
|
|
||||||
|
int32_t i = 0;
|
||||||
|
for (; i <= len - 32; i += 32) {
|
||||||
|
_mm256_storeu_si256((__m256i*)(dst + i), vecVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (; i < len; ++i) {
|
||||||
|
dst[i] = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static void avxRasterPixel32(uint32_t *dst, uint32_t val, uint32_t offset, int32_t len)
|
static void avxRasterPixel32(uint32_t *dst, uint32_t val, uint32_t offset, int32_t len)
|
||||||
{
|
{
|
||||||
//1. calculate how many iterations we need to cover the length
|
//1. calculate how many iterations we need to cover the length
|
||||||
|
|
|
@ -24,6 +24,15 @@
|
||||||
|
|
||||||
#include <arm_neon.h>
|
#include <arm_neon.h>
|
||||||
|
|
||||||
|
//TODO : need to support windows ARM
|
||||||
|
|
||||||
|
#if defined(__ARM_64BIT_STATE) || defined(_M_ARM64)
|
||||||
|
#define TVG_AARCH64 1
|
||||||
|
#else
|
||||||
|
#define TVG_AARCH64 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
static inline uint8x8_t ALPHA_BLEND(uint8x8_t c, uint8x8_t a)
|
static inline uint8x8_t ALPHA_BLEND(uint8x8_t c, uint8x8_t a)
|
||||||
{
|
{
|
||||||
uint16x8_t t = vmull_u8(c, a);
|
uint16x8_t t = vmull_u8(c, a);
|
||||||
|
@ -31,19 +40,50 @@ static inline uint8x8_t ALPHA_BLEND(uint8x8_t c, uint8x8_t a)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void neonRasterGrayscale8(uint8_t* dst, uint8_t val, uint32_t offset, int32_t len)
|
||||||
|
{
|
||||||
|
dst += offset;
|
||||||
|
|
||||||
|
int32_t i = 0;
|
||||||
|
const uint8x16_t valVec = vdupq_n_u8(val);
|
||||||
|
#if TVG_AARCH64
|
||||||
|
uint8x16x4_t valQuad = {valVec, valVec, valVec, valVec};
|
||||||
|
for (; i <= len - 16 * 4; i += 16 * 4) {
|
||||||
|
vst1q_u8_x4(dst + i, valQuad);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
for (; i <= len - 16; i += 16) {
|
||||||
|
vst1q_u8(dst + i, valVec);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
for (; i < len; i++) {
|
||||||
|
dst[i] = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static void neonRasterPixel32(uint32_t *dst, uint32_t val, uint32_t offset, int32_t len)
|
static void neonRasterPixel32(uint32_t *dst, uint32_t val, uint32_t offset, int32_t len)
|
||||||
{
|
{
|
||||||
|
dst += offset;
|
||||||
|
|
||||||
|
uint32x4_t vectorVal = vdupq_n_u32(val);
|
||||||
|
|
||||||
|
#if TVG_AARCH64
|
||||||
|
uint32_t iterations = len / 16;
|
||||||
|
uint32_t neonFilled = iterations * 16;
|
||||||
|
uint32x4x4_t valQuad = {vectorVal, vectorVal, vectorVal, vectorVal};
|
||||||
|
for (uint32_t i = 0; i < iterations; ++i) {
|
||||||
|
vst4q_u32(dst, valQuad);
|
||||||
|
dst += 16;
|
||||||
|
}
|
||||||
|
#else
|
||||||
uint32_t iterations = len / 4;
|
uint32_t iterations = len / 4;
|
||||||
uint32_t neonFilled = iterations * 4;
|
uint32_t neonFilled = iterations * 4;
|
||||||
|
|
||||||
dst += offset;
|
|
||||||
uint32x4_t vectorVal = {val, val, val, val};
|
|
||||||
|
|
||||||
for (uint32_t i = 0; i < iterations; ++i) {
|
for (uint32_t i = 0; i < iterations; ++i) {
|
||||||
vst1q_u32(dst, vectorVal);
|
vst1q_u32(dst, vectorVal);
|
||||||
dst += 4;
|
dst += 4;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
int32_t leftovers = len - neonFilled;
|
int32_t leftovers = len - neonFilled;
|
||||||
while (leftovers--) *dst++ = val;
|
while (leftovers--) *dst++ = val;
|
||||||
}
|
}
|
||||||
|
@ -56,7 +96,7 @@ static bool neonRasterTranslucentRle(SwSurface* surface, const SwRleData* rle, u
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto color = surface->blender.join(r, g, b, a);
|
auto color = surface->join(r, g, b, a);
|
||||||
auto span = rle->spans;
|
auto span = rle->spans;
|
||||||
uint32_t src;
|
uint32_t src;
|
||||||
uint8x8_t *vDst = nullptr;
|
uint8x8_t *vDst = nullptr;
|
||||||
|
@ -67,9 +107,9 @@ static bool neonRasterTranslucentRle(SwSurface* surface, const SwRleData* rle, u
|
||||||
else src = color;
|
else src = color;
|
||||||
|
|
||||||
auto dst = &surface->buf32[span->y * surface->stride + span->x];
|
auto dst = &surface->buf32[span->y * surface->stride + span->x];
|
||||||
auto ialpha = IALPHA(src);
|
auto ialpha = IA(src);
|
||||||
|
|
||||||
if ((((uint32_t) dst) & 0x7) != 0) {
|
if ((((uintptr_t) dst) & 0x7) != 0) {
|
||||||
//fill not aligned byte
|
//fill not aligned byte
|
||||||
*dst = src + ALPHA_BLEND(*dst, ialpha);
|
*dst = src + ALPHA_BLEND(*dst, ialpha);
|
||||||
vDst = (uint8x8_t*)(dst + 1);
|
vDst = (uint8x8_t*)(dst + 1);
|
||||||
|
@ -101,7 +141,7 @@ static bool neonRasterTranslucentRect(SwSurface* surface, const SwBBox& region,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto color = surface->blender.join(r, g, b, a);
|
auto color = surface->join(r, g, b, a);
|
||||||
auto buffer = surface->buf32 + (region.min.y * surface->stride) + region.min.x;
|
auto buffer = surface->buf32 + (region.min.y * surface->stride) + region.min.x;
|
||||||
auto h = static_cast<uint32_t>(region.max.y - region.min.y);
|
auto h = static_cast<uint32_t>(region.max.y - region.min.y);
|
||||||
auto w = static_cast<uint32_t>(region.max.x - region.min.x);
|
auto w = static_cast<uint32_t>(region.max.x - region.min.x);
|
||||||
|
@ -116,7 +156,7 @@ static bool neonRasterTranslucentRect(SwSurface* surface, const SwBBox& region,
|
||||||
for (uint32_t y = 0; y < h; ++y) {
|
for (uint32_t y = 0; y < h; ++y) {
|
||||||
auto dst = &buffer[y * surface->stride];
|
auto dst = &buffer[y * surface->stride];
|
||||||
|
|
||||||
if ((((uint32_t) dst) & 0x7) != 0) {
|
if ((((uintptr_t) dst) & 0x7) != 0) {
|
||||||
//fill not aligned byte
|
//fill not aligned byte
|
||||||
*dst = color + ALPHA_BLEND(*dst, ialpha);
|
*dst = color + ALPHA_BLEND(*dst, ialpha);
|
||||||
vDst = (uint8x8_t*) (dst + 1);
|
vDst = (uint8x8_t*) (dst + 1);
|
||||||
|
|
|
@ -22,43 +22,26 @@
|
||||||
|
|
||||||
#include "tvgSwCommon.h"
|
#include "tvgSwCommon.h"
|
||||||
#include "tvgMath.h"
|
#include "tvgMath.h"
|
||||||
#include "tvgBezier.h"
|
#include "tvgLines.h"
|
||||||
|
|
||||||
/************************************************************************/
|
/************************************************************************/
|
||||||
/* Internal Class Implementation */
|
/* Internal Class Implementation */
|
||||||
/************************************************************************/
|
/************************************************************************/
|
||||||
|
|
||||||
|
static bool _outlineBegin(SwOutline& outline)
|
||||||
struct Line
|
|
||||||
{
|
{
|
||||||
Point pt1;
|
//Make a contour if lineTo/curveTo without calling close or moveTo beforehand.
|
||||||
Point pt2;
|
if (outline.pts.empty()) return false;
|
||||||
};
|
outline.cntrs.push(outline.pts.count - 1);
|
||||||
|
outline.closed.push(false);
|
||||||
|
outline.pts.push(outline.pts[outline.cntrs.last()]);
|
||||||
static float _lineLength(const Point& pt1, const Point& pt2)
|
outline.types.push(SW_CURVE_TYPE_POINT);
|
||||||
{
|
return false;
|
||||||
Point diff = {pt2.x - pt1.x, pt2.y - pt1.y};
|
|
||||||
return sqrtf(diff.x * diff.x + diff.y * diff.y);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void _lineSplitAt(const Line& cur, float at, Line& left, Line& right)
|
|
||||||
{
|
|
||||||
auto len = _lineLength(cur.pt1, cur.pt2);
|
|
||||||
auto dx = ((cur.pt2.x - cur.pt1.x) / len) * at;
|
|
||||||
auto dy = ((cur.pt2.y - cur.pt1.y) / len) * at;
|
|
||||||
left.pt1 = cur.pt1;
|
|
||||||
left.pt2.x = left.pt1.x + dx;
|
|
||||||
left.pt2.y = left.pt1.y + dy;
|
|
||||||
right.pt1 = left.pt2;
|
|
||||||
right.pt2 = cur.pt2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static bool _outlineEnd(SwOutline& outline)
|
static bool _outlineEnd(SwOutline& outline)
|
||||||
{
|
{
|
||||||
//Make a contour if lineTo/curveTo without calling close/moveTo beforehand.
|
|
||||||
if (outline.pts.empty()) return false;
|
if (outline.pts.empty()) return false;
|
||||||
outline.cntrs.push(outline.pts.count - 1);
|
outline.cntrs.push(outline.pts.count - 1);
|
||||||
outline.closed.push(false);
|
outline.closed.push(false);
|
||||||
|
@ -119,10 +102,11 @@ static bool _outlineClose(SwOutline& outline)
|
||||||
static void _dashLineTo(SwDashStroke& dash, const Point* to, const Matrix* transform)
|
static void _dashLineTo(SwDashStroke& dash, const Point* to, const Matrix* transform)
|
||||||
{
|
{
|
||||||
Line cur = {dash.ptCur, *to};
|
Line cur = {dash.ptCur, *to};
|
||||||
auto len = _lineLength(cur.pt1, cur.pt2);
|
auto len = lineLength(cur.pt1, cur.pt2);
|
||||||
|
|
||||||
if (mathZero(len)) {
|
if (mathZero(len)) {
|
||||||
_outlineMoveTo(*dash.outline, &dash.ptCur, transform);
|
_outlineMoveTo(*dash.outline, &dash.ptCur, transform);
|
||||||
|
//draw the current line fully
|
||||||
} else if (len < dash.curLen) {
|
} else if (len < dash.curLen) {
|
||||||
dash.curLen -= len;
|
dash.curLen -= len;
|
||||||
if (!dash.curOpGap) {
|
if (!dash.curOpGap) {
|
||||||
|
@ -132,12 +116,13 @@ static void _dashLineTo(SwDashStroke& dash, const Point* to, const Matrix* trans
|
||||||
}
|
}
|
||||||
_outlineLineTo(*dash.outline, to, transform);
|
_outlineLineTo(*dash.outline, to, transform);
|
||||||
}
|
}
|
||||||
|
//draw the current line partially
|
||||||
} else {
|
} else {
|
||||||
while (len - dash.curLen > 0.0001f) {
|
while (len - dash.curLen > 0.0001f) {
|
||||||
Line left, right;
|
Line left, right;
|
||||||
if (dash.curLen > 0) {
|
if (dash.curLen > 0) {
|
||||||
len -= dash.curLen;
|
len -= dash.curLen;
|
||||||
_lineSplitAt(cur, dash.curLen, left, right);
|
lineSplitAt(cur, dash.curLen, left, right);
|
||||||
if (!dash.curOpGap) {
|
if (!dash.curOpGap) {
|
||||||
if (dash.move || dash.pattern[dash.curIdx] - dash.curLen < FLT_EPSILON) {
|
if (dash.move || dash.pattern[dash.curIdx] - dash.curLen < FLT_EPSILON) {
|
||||||
_outlineMoveTo(*dash.outline, &left.pt1, transform);
|
_outlineMoveTo(*dash.outline, &left.pt1, transform);
|
||||||
|
@ -180,6 +165,7 @@ static void _dashCubicTo(SwDashStroke& dash, const Point* ctrl1, const Point* ct
|
||||||
Bezier cur = {dash.ptCur, *ctrl1, *ctrl2, *to};
|
Bezier cur = {dash.ptCur, *ctrl1, *ctrl2, *to};
|
||||||
auto len = bezLength(cur);
|
auto len = bezLength(cur);
|
||||||
|
|
||||||
|
//draw the current line fully
|
||||||
if (mathZero(len)) {
|
if (mathZero(len)) {
|
||||||
_outlineMoveTo(*dash.outline, &dash.ptCur, transform);
|
_outlineMoveTo(*dash.outline, &dash.ptCur, transform);
|
||||||
} else if (len < dash.curLen) {
|
} else if (len < dash.curLen) {
|
||||||
|
@ -191,6 +177,7 @@ static void _dashCubicTo(SwDashStroke& dash, const Point* ctrl1, const Point* ct
|
||||||
}
|
}
|
||||||
_outlineCubicTo(*dash.outline, ctrl1, ctrl2, to, transform);
|
_outlineCubicTo(*dash.outline, ctrl1, ctrl2, to, transform);
|
||||||
}
|
}
|
||||||
|
//draw the current line partially
|
||||||
} else {
|
} else {
|
||||||
while ((len - dash.curLen) > 0.0001f) {
|
while ((len - dash.curLen) > 0.0001f) {
|
||||||
Bezier left, right;
|
Bezier left, right;
|
||||||
|
@ -240,7 +227,15 @@ static void _dashClose(SwDashStroke& dash, const Matrix* transform)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void _dashMoveTo(SwDashStroke& dash, uint32_t offIdx, float offset, const Point* pts, const Matrix* transform)
|
static void _dashMoveTo(SwDashStroke& dash, const Point* pts)
|
||||||
|
{
|
||||||
|
dash.ptCur = *pts;
|
||||||
|
dash.ptStart = *pts;
|
||||||
|
dash.move = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void _dashMoveTo(SwDashStroke& dash, uint32_t offIdx, float offset, const Point* pts)
|
||||||
{
|
{
|
||||||
dash.curIdx = offIdx % dash.cnt;
|
dash.curIdx = offIdx % dash.cnt;
|
||||||
dash.curLen = dash.pattern[dash.curIdx] - offset;
|
dash.curLen = dash.pattern[dash.curIdx] - offset;
|
||||||
|
@ -275,7 +270,7 @@ static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* trans
|
||||||
|
|
||||||
//default
|
//default
|
||||||
if (end > begin) {
|
if (end > begin) {
|
||||||
if (begin > 0) dash.cnt += 4;
|
if (begin > 0.0f) dash.cnt += 4;
|
||||||
else dash.cnt += 2;
|
else dash.cnt += 2;
|
||||||
//looping
|
//looping
|
||||||
} else dash.cnt += 3;
|
} else dash.cnt += 3;
|
||||||
|
@ -293,7 +288,7 @@ static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* trans
|
||||||
dash.pattern[0] = 0; //zero dash to start with a space.
|
dash.pattern[0] = 0; //zero dash to start with a space.
|
||||||
dash.pattern[1] = begin;
|
dash.pattern[1] = begin;
|
||||||
dash.pattern[2] = end - begin;
|
dash.pattern[2] = end - begin;
|
||||||
dash.pattern[3] = length - (end - begin);
|
dash.pattern[3] = length - end;
|
||||||
}
|
}
|
||||||
|
|
||||||
trimmed = true;
|
trimmed = true;
|
||||||
|
@ -322,14 +317,22 @@ static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* trans
|
||||||
|
|
||||||
dash.outline = mpoolReqDashOutline(mpool, tid);
|
dash.outline = mpoolReqDashOutline(mpool, tid);
|
||||||
|
|
||||||
while (cmdCnt-- > 0) {
|
//must begin with moveTo
|
||||||
|
if (cmds[0] == PathCommand::MoveTo) {
|
||||||
|
_dashMoveTo(dash, offIdx, offset, pts);
|
||||||
|
cmds++;
|
||||||
|
pts++;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (--cmdCnt > 0) {
|
||||||
switch (*cmds) {
|
switch (*cmds) {
|
||||||
case PathCommand::Close: {
|
case PathCommand::Close: {
|
||||||
_dashClose(dash, transform);
|
_dashClose(dash, transform);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case PathCommand::MoveTo: {
|
case PathCommand::MoveTo: {
|
||||||
_dashMoveTo(dash, offIdx, offset, pts, transform);
|
if (rshape->stroke->trim.individual) _dashMoveTo(dash, pts);
|
||||||
|
else _dashMoveTo(dash, offIdx, offset, pts);
|
||||||
++pts;
|
++pts;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -367,13 +370,19 @@ static float _outlineLength(const RenderShape* rshape)
|
||||||
|
|
||||||
const Point* close = nullptr;
|
const Point* close = nullptr;
|
||||||
auto length = 0.0f;
|
auto length = 0.0f;
|
||||||
|
auto slength = -1.0f;
|
||||||
|
auto simultaneous = !rshape->stroke->trim.individual;
|
||||||
|
|
||||||
//Compute the whole length
|
//Compute the whole length
|
||||||
while (cmdCnt-- > 0) {
|
while (cmdCnt-- > 0) {
|
||||||
switch (*cmds) {
|
switch (*cmds) {
|
||||||
case PathCommand::Close: {
|
case PathCommand::Close: {
|
||||||
length += mathLength(pts - 1, close);
|
length += mathLength(pts - 1, close);
|
||||||
++pts;
|
//retrieve the max length of the shape if the simultaneous mode.
|
||||||
|
if (simultaneous) {
|
||||||
|
if (slength < length) slength = length;
|
||||||
|
length = 0.0f;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case PathCommand::MoveTo: {
|
case PathCommand::MoveTo: {
|
||||||
|
@ -394,7 +403,8 @@ static float _outlineLength(const RenderShape* rshape)
|
||||||
}
|
}
|
||||||
++cmds;
|
++cmds;
|
||||||
}
|
}
|
||||||
return length;
|
if (simultaneous && slength > length) return slength;
|
||||||
|
else return length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -429,7 +439,7 @@ static bool _genOutline(SwShape* shape, const RenderShape* rshape, const Matrix*
|
||||||
|
|
||||||
shape->outline = mpoolReqOutline(mpool, tid);
|
shape->outline = mpoolReqOutline(mpool, tid);
|
||||||
auto outline = shape->outline;
|
auto outline = shape->outline;
|
||||||
bool closed = false;
|
auto closed = false;
|
||||||
|
|
||||||
//Generate Outlines
|
//Generate Outlines
|
||||||
while (cmdCnt-- > 0) {
|
while (cmdCnt-- > 0) {
|
||||||
|
@ -444,13 +454,13 @@ static bool _genOutline(SwShape* shape, const RenderShape* rshape, const Matrix*
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case PathCommand::LineTo: {
|
case PathCommand::LineTo: {
|
||||||
if (closed) closed = _outlineEnd(*outline);
|
if (closed) closed = _outlineBegin(*outline);
|
||||||
_outlineLineTo(*outline, pts, transform);
|
_outlineLineTo(*outline, pts, transform);
|
||||||
++pts;
|
++pts;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case PathCommand::CubicTo: {
|
case PathCommand::CubicTo: {
|
||||||
if (closed) closed = _outlineEnd(*outline);
|
if (closed) closed = _outlineBegin(*outline);
|
||||||
_outlineCubicTo(*outline, pts, pts + 1, pts + 2, transform);
|
_outlineCubicTo(*outline, pts, pts + 1, pts + 2, transform);
|
||||||
pts += 3;
|
pts += 3;
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -93,6 +93,33 @@ float Animation::duration() const noexcept
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Result Animation::segment(float begin, float end) noexcept
|
||||||
|
{
|
||||||
|
if (begin < 0.0 || end > 1.0 || begin >= end) return Result::InvalidArguments;
|
||||||
|
|
||||||
|
auto loader = pImpl->picture->pImpl->loader;
|
||||||
|
if (!loader) return Result::InsufficientCondition;
|
||||||
|
if (!loader->animatable()) return Result::NonSupport;
|
||||||
|
|
||||||
|
static_cast<FrameModule*>(loader)->segment(begin, end);
|
||||||
|
|
||||||
|
return Result::Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Result Animation::segment(float *begin, float *end) noexcept
|
||||||
|
{
|
||||||
|
auto loader = pImpl->picture->pImpl->loader;
|
||||||
|
if (!loader) return Result::InsufficientCondition;
|
||||||
|
if (!loader->animatable()) return Result::NonSupport;
|
||||||
|
if (!begin && !end) return Result::InvalidArguments;
|
||||||
|
|
||||||
|
static_cast<FrameModule*>(loader)->segment(begin, end);
|
||||||
|
|
||||||
|
return Result::Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
unique_ptr<Animation> Animation::gen() noexcept
|
unique_ptr<Animation> Animation::gen() noexcept
|
||||||
{
|
{
|
||||||
return unique_ptr<Animation>(new Animation);
|
return unique_ptr<Animation>(new Animation);
|
||||||
|
|
|
@ -98,25 +98,14 @@ struct Canvas::Impl
|
||||||
auto flag = RenderUpdateFlag::None;
|
auto flag = RenderUpdateFlag::None;
|
||||||
if (refresh || force) flag = RenderUpdateFlag::All;
|
if (refresh || force) flag = RenderUpdateFlag::All;
|
||||||
|
|
||||||
//Update single paint node
|
|
||||||
if (paint) {
|
if (paint) {
|
||||||
//Optimize Me: Can we skip the searching?
|
paint->pImpl->update(renderer, nullptr, clips, 255, flag);
|
||||||
for (auto paint2 : paints) {
|
|
||||||
if (paint2 == paint) {
|
|
||||||
paint->pImpl->update(renderer, nullptr, clips, 255, flag);
|
|
||||||
return Result::Success;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Result::InvalidArguments;
|
|
||||||
//Update all retained paint nodes
|
|
||||||
} else {
|
} else {
|
||||||
for (auto paint : paints) {
|
for (auto paint : paints) {
|
||||||
paint->pImpl->update(renderer, nullptr, clips, 255, flag);
|
paint->pImpl->update(renderer, nullptr, clips, 255, flag);
|
||||||
}
|
}
|
||||||
|
refresh = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
refresh = false;
|
|
||||||
|
|
||||||
return Result::Success;
|
return Result::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,9 @@ namespace tvg
|
||||||
class FrameModule: public ImageLoader
|
class FrameModule: public ImageLoader
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
float segmentBegin = 0.0f;
|
||||||
|
float segmentEnd = 1.0f;
|
||||||
|
|
||||||
FrameModule(FileType type) : ImageLoader(type) {}
|
FrameModule(FileType type) : ImageLoader(type) {}
|
||||||
virtual ~FrameModule() {}
|
virtual ~FrameModule() {}
|
||||||
|
|
||||||
|
@ -39,6 +42,18 @@ public:
|
||||||
virtual float curFrame() = 0; //return the current frame number
|
virtual float curFrame() = 0; //return the current frame number
|
||||||
virtual float duration() = 0; //return the animation duration in seconds
|
virtual float duration() = 0; //return the animation duration in seconds
|
||||||
|
|
||||||
|
void segment(float* begin, float* end)
|
||||||
|
{
|
||||||
|
if (begin) *begin = segmentBegin;
|
||||||
|
if (end) *end = segmentEnd;
|
||||||
|
}
|
||||||
|
|
||||||
|
void segment(float begin, float end)
|
||||||
|
{
|
||||||
|
segmentBegin = begin;
|
||||||
|
segmentEnd = end;
|
||||||
|
}
|
||||||
|
|
||||||
virtual bool animatable() override { return true; }
|
virtual bool animatable() override { return true; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -60,14 +60,14 @@ GlCanvas::~GlCanvas()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Result GlCanvas::target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h) noexcept
|
Result GlCanvas::target(int32_t id, uint32_t w, uint32_t h) noexcept
|
||||||
{
|
{
|
||||||
#ifdef THORVG_GL_RASTER_SUPPORT
|
#ifdef THORVG_GL_RASTER_SUPPORT
|
||||||
//We know renderer type, avoid dynamic_cast for performance.
|
//We know renderer type, avoid dynamic_cast for performance.
|
||||||
auto renderer = static_cast<GlRenderer*>(Canvas::pImpl->renderer);
|
auto renderer = static_cast<GlRenderer*>(Canvas::pImpl->renderer);
|
||||||
if (!renderer) return Result::MemoryCorruption;
|
if (!renderer) return Result::MemoryCorruption;
|
||||||
|
|
||||||
if (!renderer->target(buffer, stride, w, h)) return Result::Unknown;
|
if (!renderer->target(id, w, h)) return Result::Unknown;
|
||||||
|
|
||||||
//Paints must be updated again with this new target.
|
//Paints must be updated again with this new target.
|
||||||
Canvas::pImpl->needRefresh();
|
Canvas::pImpl->needRefresh();
|
||||||
|
|
|
@ -178,7 +178,6 @@ static LoadModule* _findByPath(const string& path)
|
||||||
if (!ext.compare("tvg")) return _find(FileType::Tvg);
|
if (!ext.compare("tvg")) return _find(FileType::Tvg);
|
||||||
if (!ext.compare("svg")) return _find(FileType::Svg);
|
if (!ext.compare("svg")) return _find(FileType::Svg);
|
||||||
if (!ext.compare("json")) return _find(FileType::Lottie);
|
if (!ext.compare("json")) return _find(FileType::Lottie);
|
||||||
if (!ext.compare("lottie")) return _find(FileType::Lottie);
|
|
||||||
if (!ext.compare("png")) return _find(FileType::Png);
|
if (!ext.compare("png")) return _find(FileType::Png);
|
||||||
if (!ext.compare("jpg")) return _find(FileType::Jpg);
|
if (!ext.compare("jpg")) return _find(FileType::Jpg);
|
||||||
if (!ext.compare("webp")) return _find(FileType::Webp);
|
if (!ext.compare("webp")) return _find(FileType::Webp);
|
||||||
|
@ -271,7 +270,7 @@ bool LoaderMgr::term()
|
||||||
auto tmp = loader;
|
auto tmp = loader;
|
||||||
loader = loader->next;
|
loader = loader->next;
|
||||||
_activeLoaders.remove(tmp);
|
_activeLoaders.remove(tmp);
|
||||||
if (ret) delete(loader);
|
if (ret) delete(tmp);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -295,15 +294,24 @@ LoadModule* LoaderMgr::loader(const string& path, bool* invalid)
|
||||||
{
|
{
|
||||||
*invalid = false;
|
*invalid = false;
|
||||||
|
|
||||||
if (auto loader = _findFromCache(path)) return loader;
|
//TODO: lottie is not sharable.
|
||||||
|
auto allowCache = true;
|
||||||
|
auto ext = path.substr(path.find_last_of(".") + 1);
|
||||||
|
if (!ext.compare("json")) allowCache = false;
|
||||||
|
|
||||||
|
if (allowCache) {
|
||||||
|
if (auto loader = _findFromCache(path)) return loader;
|
||||||
|
}
|
||||||
|
|
||||||
if (auto loader = _findByPath(path)) {
|
if (auto loader = _findByPath(path)) {
|
||||||
if (loader->open(path)) {
|
if (loader->open(path)) {
|
||||||
loader->hashpath = strdup(path.c_str());
|
if (allowCache) {
|
||||||
loader->pathcache = true;
|
loader->hashpath = strdup(path.c_str());
|
||||||
{
|
loader->pathcache = true;
|
||||||
ScopedLock lock(key);
|
{
|
||||||
_activeLoaders.back(loader);
|
ScopedLock lock(key);
|
||||||
|
_activeLoaders.back(loader);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return loader;
|
return loader;
|
||||||
}
|
}
|
||||||
|
@ -313,11 +321,13 @@ LoadModule* LoaderMgr::loader(const string& path, bool* invalid)
|
||||||
for (int i = 0; i < static_cast<int>(FileType::Raw); i++) {
|
for (int i = 0; i < static_cast<int>(FileType::Raw); i++) {
|
||||||
if (auto loader = _find(static_cast<FileType>(i))) {
|
if (auto loader = _find(static_cast<FileType>(i))) {
|
||||||
if (loader->open(path)) {
|
if (loader->open(path)) {
|
||||||
loader->hashpath = strdup(path.c_str());
|
if (allowCache) {
|
||||||
loader->pathcache = true;
|
loader->hashpath = strdup(path.c_str());
|
||||||
{
|
loader->pathcache = true;
|
||||||
ScopedLock lock(key);
|
{
|
||||||
_activeLoaders.back(loader);
|
ScopedLock lock(key);
|
||||||
|
_activeLoaders.back(loader);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return loader;
|
return loader;
|
||||||
}
|
}
|
||||||
|
@ -354,7 +364,15 @@ LoadModule* LoaderMgr::loader(const char* data, uint32_t size, const string& mim
|
||||||
{
|
{
|
||||||
//Note that users could use the same data pointer with the different content.
|
//Note that users could use the same data pointer with the different content.
|
||||||
//Thus caching is only valid for shareable.
|
//Thus caching is only valid for shareable.
|
||||||
if (!copy) {
|
auto allowCache = !copy;
|
||||||
|
|
||||||
|
//TODO: lottie is not sharable.
|
||||||
|
if (allowCache) {
|
||||||
|
auto type = _convert(mimeType);
|
||||||
|
if (type == FileType::Lottie) allowCache = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (allowCache) {
|
||||||
if (auto loader = _findFromCache(data, size, mimeType)) return loader;
|
if (auto loader = _findFromCache(data, size, mimeType)) return loader;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -362,7 +380,7 @@ LoadModule* LoaderMgr::loader(const char* data, uint32_t size, const string& mim
|
||||||
if (!mimeType.empty()) {
|
if (!mimeType.empty()) {
|
||||||
if (auto loader = _findByType(mimeType)) {
|
if (auto loader = _findByType(mimeType)) {
|
||||||
if (loader->open(data, size, copy)) {
|
if (loader->open(data, size, copy)) {
|
||||||
if (!copy) {
|
if (allowCache) {
|
||||||
loader->hashkey = HASH_KEY(data);
|
loader->hashkey = HASH_KEY(data);
|
||||||
ScopedLock lock(key);
|
ScopedLock lock(key);
|
||||||
_activeLoaders.back(loader);
|
_activeLoaders.back(loader);
|
||||||
|
@ -379,7 +397,7 @@ LoadModule* LoaderMgr::loader(const char* data, uint32_t size, const string& mim
|
||||||
auto loader = _find(static_cast<FileType>(i));
|
auto loader = _find(static_cast<FileType>(i));
|
||||||
if (loader) {
|
if (loader) {
|
||||||
if (loader->open(data, size, copy)) {
|
if (loader->open(data, size, copy)) {
|
||||||
if (!copy) {
|
if (allowCache) {
|
||||||
loader->hashkey = HASH_KEY(data);
|
loader->hashkey = HASH_KEY(data);
|
||||||
ScopedLock lock(key);
|
ScopedLock lock(key);
|
||||||
_activeLoaders.back(loader);
|
_activeLoaders.back(loader);
|
||||||
|
|
|
@ -163,6 +163,7 @@ struct RenderStroke
|
||||||
struct {
|
struct {
|
||||||
float begin = 0.0f;
|
float begin = 0.0f;
|
||||||
float end = 1.0f;
|
float end = 1.0f;
|
||||||
|
bool individual = false;
|
||||||
} trim;
|
} trim;
|
||||||
|
|
||||||
~RenderStroke()
|
~RenderStroke()
|
||||||
|
|
|
@ -157,14 +157,17 @@ Result Saver::save(unique_ptr<Animation> animation, const string& path, uint32_t
|
||||||
auto a = animation.release();
|
auto a = animation.release();
|
||||||
if (!a) return Result::MemoryCorruption;
|
if (!a) return Result::MemoryCorruption;
|
||||||
|
|
||||||
|
//animation holds the picture, it must be 1 at the bottom.
|
||||||
|
auto remove = PP(a->picture())->refCnt <= 1 ? true : false;
|
||||||
|
|
||||||
if (mathZero(a->totalFrame())) {
|
if (mathZero(a->totalFrame())) {
|
||||||
delete(a);
|
if (remove) delete(a);
|
||||||
return Result::InsufficientCondition;
|
return Result::InsufficientCondition;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Already on saving an other resource.
|
//Already on saving an other resource.
|
||||||
if (pImpl->saveModule) {
|
if (pImpl->saveModule) {
|
||||||
delete(a);
|
if (remove) delete(a);
|
||||||
return Result::InsufficientCondition;
|
return Result::InsufficientCondition;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -173,12 +176,12 @@ Result Saver::save(unique_ptr<Animation> animation, const string& path, uint32_t
|
||||||
pImpl->saveModule = saveModule;
|
pImpl->saveModule = saveModule;
|
||||||
return Result::Success;
|
return Result::Success;
|
||||||
} else {
|
} else {
|
||||||
delete(a);
|
if (remove) delete(a);
|
||||||
delete(saveModule);
|
delete(saveModule);
|
||||||
return Result::Unknown;
|
return Result::Unknown;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
delete(a);
|
if (remove) delete(a);
|
||||||
return Result::NonSupport;
|
return Result::NonSupport;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -145,13 +145,15 @@ Result Shape::appendArc(float cx, float cy, float radius, float startAngle, floa
|
||||||
//just circle
|
//just circle
|
||||||
if (sweep >= 360.0f || sweep <= -360.0f) return appendCircle(cx, cy, radius, radius);
|
if (sweep >= 360.0f || sweep <= -360.0f) return appendCircle(cx, cy, radius, radius);
|
||||||
|
|
||||||
startAngle = (startAngle * MATH_PI) / 180.0f;
|
const float arcPrecision = 1e-5f;
|
||||||
sweep = sweep * MATH_PI / 180.0f;
|
startAngle = mathDeg2Rad(startAngle);
|
||||||
|
sweep = mathDeg2Rad(sweep);
|
||||||
|
|
||||||
auto nCurves = ceil(fabsf(sweep / MATH_PI2));
|
auto nCurves = static_cast<int>(fabsf(sweep / MATH_PI2));
|
||||||
|
if (fabsf(sweep / MATH_PI2) - nCurves > arcPrecision) ++nCurves;
|
||||||
auto sweepSign = (sweep < 0 ? -1 : 1);
|
auto sweepSign = (sweep < 0 ? -1 : 1);
|
||||||
auto fract = fmodf(sweep, MATH_PI2);
|
auto fract = fmodf(sweep, MATH_PI2);
|
||||||
fract = (mathZero(fract)) ? MATH_PI2 * sweepSign : fract;
|
fract = (fabsf(fract) < arcPrecision) ? MATH_PI2 * sweepSign : fract;
|
||||||
|
|
||||||
//Start from here
|
//Start from here
|
||||||
Point start = {radius * cosf(startAngle), radius * sinf(startAngle)};
|
Point start = {radius * cosf(startAngle), radius * sinf(startAngle)};
|
||||||
|
|
|
@ -76,8 +76,21 @@ struct Shape::Impl
|
||||||
//Composition test
|
//Composition test
|
||||||
const Paint* target;
|
const Paint* target;
|
||||||
auto method = shape->composite(&target);
|
auto method = shape->composite(&target);
|
||||||
if (!target || method == tvg::CompositeMethod::ClipPath) return false;
|
if (!target || method == CompositeMethod::ClipPath) return false;
|
||||||
if (target->pImpl->opacity == 255 || target->pImpl->opacity == 0) return false;
|
if (target->pImpl->opacity == 255 || target->pImpl->opacity == 0) {
|
||||||
|
if (target->identifier() == TVG_CLASS_ID_SHAPE) {
|
||||||
|
auto shape = static_cast<const Shape*>(target);
|
||||||
|
if (!shape->fill()) {
|
||||||
|
uint8_t r, g, b, a;
|
||||||
|
shape->fillColor(&r, &g, &b, &a);
|
||||||
|
if (a == 0 || a == 255) {
|
||||||
|
if (method == CompositeMethod::LumaMask || method == CompositeMethod::InvLumaMask) {
|
||||||
|
if ((r == 255 && g == 255 && b == 255) || (r == 0 && g == 0 && b == 0)) return false;
|
||||||
|
} else return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -203,7 +216,7 @@ struct Shape::Impl
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool strokeTrim(float begin, float end)
|
bool strokeTrim(float begin, float end, bool individual)
|
||||||
{
|
{
|
||||||
if (!rs.stroke) {
|
if (!rs.stroke) {
|
||||||
if (begin == 0.0f && end == 1.0f) return true;
|
if (begin == 0.0f && end == 1.0f) return true;
|
||||||
|
@ -214,6 +227,7 @@ struct Shape::Impl
|
||||||
|
|
||||||
rs.stroke->trim.begin = begin;
|
rs.stroke->trim.begin = begin;
|
||||||
rs.stroke->trim.end = end;
|
rs.stroke->trim.end = end;
|
||||||
|
rs.stroke->trim.individual = individual;
|
||||||
flag |= RenderUpdateFlag::Stroke;
|
flag |= RenderUpdateFlag::Stroke;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#!/bin/bash -e
|
#!/bin/bash -e
|
||||||
|
|
||||||
VERSION=0.12.10
|
VERSION=0.13.3
|
||||||
|
|
||||||
cd thirdparty/thorvg/ || true
|
cd thirdparty/thorvg/ || true
|
||||||
rm -rf AUTHORS LICENSE inc/ src/ *.zip *.tar.gz tmp/
|
rm -rf AUTHORS LICENSE inc/ src/ *.zip *.tar.gz tmp/
|
||||||
|
@ -38,6 +38,7 @@ cat << EOF > ../inc/config.h
|
||||||
#define THORVG_SVG_LOADER_SUPPORT
|
#define THORVG_SVG_LOADER_SUPPORT
|
||||||
#define THORVG_PNG_LOADER_SUPPORT
|
#define THORVG_PNG_LOADER_SUPPORT
|
||||||
#define THORVG_JPG_LOADER_SUPPORT
|
#define THORVG_JPG_LOADER_SUPPORT
|
||||||
|
#define THORVG_WEBP_LOADER_SUPPORT
|
||||||
#define THORVG_THREAD_SUPPORT
|
#define THORVG_THREAD_SUPPORT
|
||||||
|
|
||||||
// For internal debugging:
|
// For internal debugging:
|
||||||
|
@ -55,11 +56,13 @@ cp -rv src/renderer ../src/
|
||||||
rm -rfv ../src/renderer/gl_engine
|
rm -rfv ../src/renderer/gl_engine
|
||||||
rm -rfv ../src/renderer/wg_engine
|
rm -rfv ../src/renderer/wg_engine
|
||||||
|
|
||||||
# Enabled embedded loaders: raw, JPEG, PNG.
|
# Enabled embedded loaders: raw, JPEG, PNG, WebP.
|
||||||
mkdir ../src/loaders
|
mkdir ../src/loaders
|
||||||
cp -rv src/loaders/svg src/loaders/raw ../src/loaders/
|
cp -rv src/loaders/svg src/loaders/raw ../src/loaders/
|
||||||
cp -rv src/loaders/jpg ../src/loaders/
|
cp -rv src/loaders/external_png ../src/loaders/
|
||||||
cp -rv src/loaders/png src/loaders/external_png ../src/loaders/
|
cp -rv src/loaders/external_webp ../src/loaders/
|
||||||
|
# Not using external jpg as it's turbojpeg, which we don't have.
|
||||||
|
cp -rv src/loaders/jpg ../src/loaders/
|
||||||
|
|
||||||
popd
|
popd
|
||||||
rm -rf tmp
|
rm -rf tmp
|
||||||
|
|
Loading…
Reference in New Issue