thorvg: Update to 0.13.3, add webp loader

Remove embedded png loader, we use the external (libpng) one.
This commit is contained in:
Rémi Verschelde 2024-05-10 09:30:57 +02:00
parent c4279fe3e0
commit 1cf9f37589
No known key found for this signature in database
GPG Key ID: C3336907360768E1
37 changed files with 630 additions and 3037 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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