Merge pull request #91788 from akien-mga/thorvg-0.13.3

thorvg: Update to 0.13.3, add webp loader
This commit is contained in:
Rémi Verschelde 2024-05-11 12:38:21 +02:00
commit 1cf283dd26
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_sources = [
# common
"src/common/tvgBezier.cpp",
"src/common/tvgCompressor.cpp",
"src/common/tvgLines.cpp",
"src/common/tvgMath.cpp",
"src/common/tvgStr.cpp",
# SVG parser
@ -24,7 +24,9 @@ thirdparty_sources = [
"src/loaders/svg/tvgSvgUtil.cpp",
"src/loaders/svg/tvgXmlParser.cpp",
"src/loaders/raw/tvgRawLoader.cpp",
# image loaders
"src/loaders/external_png/tvgPngLoader.cpp",
"src/loaders/external_webp/tvgWebpLoader.cpp",
"src/loaders/jpg/tvgJpgd.cpp",
"src/loaders/jpg/tvgJpgLoader.cpp",
# renderer common
@ -74,7 +76,10 @@ env_thirdparty.Prepend(
thirdparty_dir + "src/renderer/sw_engine",
thirdparty_dir + "src/loaders/raw",
thirdparty_dir + "src/loaders/external_png",
thirdparty_dir + "src/loaders/external_webp",
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
- Upstream: https://github.com/thorvg/thorvg
- Version: 0.12.9 (afa6d8499bd49141d99d5e40a4620bd9f6bc0467, 2024)
- Version: 0.13.3 (6235068cad8cad176ccd0cbcf82f25e985fbc258, 2024)
- License: MIT
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>
Junsu Choi <jsuya.choi@samsung.com>
Pranay Samanta <pranay.ks@samsung.com>
Mateusz Palkowski <m.palkowski@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>
Shinwoo Kim <cinoo.kim@samsung.com>
Piotr Kalota <p.kalota@samsung.com>
@ -18,10 +18,12 @@ Rémi Verschelde <rverschelde@gmail.com>
Martin Liska <mliksa@suse.cz>
Vincenzo Pupillo <vincenzo.pupillo@unimi.it>
EunSik Jeong <rinechran@outlook.jp>
Samsung Electronics Co., Ltd
Rafał Mikrut <mikrutrafal@protonmail.com>
Martin Capitanio <capnm@capitanio.org>
RuiwenTang <tangruiwen1989@gmail.com>
YouJin Lee <ol-of@naver.com>
SergeyLebedkin <sergii@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_PNG_LOADER_SUPPORT
#define THORVG_JPG_LOADER_SUPPORT
#define THORVG_WEBP_LOADER_SUPPORT
#define THORVG_THREAD_SUPPORT
// For internal debugging:
//#define THORVG_LOG_ENABLED
#define THORVG_VERSION_STRING "0.12.10"
#define THORVG_VERSION_STRING "0.13.3"
#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.
*
@ -1714,13 +1714,22 @@ public:
~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
*/
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.
@ -1828,7 +1837,7 @@ public:
*
* This class supports the display and control of animation frames.
*
* @note Experimental API
* @since 0.13
*/
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::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()
*
* @note Experimental API
*/
Result frame(float no) noexcept;
@ -1862,7 +1873,6 @@ public:
*
* @warning The picture instance is owned by Animation. It should not be deleted manually.
*
* @note Experimental API
*/
Picture* picture() const noexcept;
@ -1876,7 +1886,6 @@ public:
* @see Animation::frame(float no)
* @see Animation::totalFrame()
*
* @note Experimental API
*/
float curFrame() const noexcept;
@ -1888,7 +1897,6 @@ public:
* @note Frame numbering starts from 0.
* @note If the Picture is not properly configured, this function will return 0.
*
* @note Experimental API
*/
float totalFrame() const noexcept;
@ -1899,16 +1907,52 @@ public:
*
* @note If the Picture is not properly configured, this function will return 0.
*
* @% Experimental API
*/
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.
*
* @return A new Animation object.
*
* @note Experimental API
*/
static std::unique_ptr<Animation> gen() noexcept;

View File

@ -61,6 +61,7 @@ struct Array
void push(Array<T>& rhs)
{
if (rhs.count == 0) return;
grow(rhs.count);
memcpy(data + count, rhs.data, rhs.count * sizeof(T));
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]];
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]];
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]];
output[idx++] = ((value3 & 0x03) << 6) + value4;
encoded += 4;

View File

@ -21,9 +21,9 @@
*/
#include "tvgMath.h"
#include "tvgBezier.h"
#include "tvgLines.h"
#define BEZIER_EPSILON 1e-4f
#define BEZIER_EPSILON 1e-2f
/************************************************************************/
/* Internal Class Implementation */
@ -101,6 +101,25 @@ float _bezAt(const Bezier& bz, float at, float length, LengthFunc lineLengthFunc
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)
{
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.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.
*/
#ifndef _TVG_BEZIER_H_
#define _TVG_BEZIER_H_
#ifndef _TVG_LINES_H_
#define _TVG_LINES_H_
#include "tvgCommon.h"
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
{
Point start;
@ -48,4 +58,4 @@ float bezLengthApprox(const Bezier& cur);
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);
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)
{
return (fabsf(a) < FLT_EPSILON) ? true : false;

View File

@ -21,6 +21,7 @@
*/
#include "config.h"
#include <cmath>
#include <cstring>
#include <memory.h>
#include "tvgMath.h"
@ -197,6 +198,8 @@ float strToFloat(const char *nPtr, char **endPtr)
success:
if (endPtr) *endPtr = (char *)(a);
if (!std::isfinite(val)) return 0.0f;
return minus * val;
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
* of this software and associated documentation files (the "Software"), to deal
@ -21,8 +21,9 @@
*/
#include <memory.h>
#include "tvgLoader.h"
#include "tvgPngLoader.h"
#include <webp/decode.h>
#include "tvgWebpLoader.h"
/************************************************************************/
@ -30,23 +31,17 @@
/************************************************************************/
void PngLoader::run(unsigned tid)
void WebpLoader::run(unsigned tid)
{
auto width = static_cast<unsigned>(w);
auto height = static_cast<unsigned>(h);
state.info_raw.colortype = LCT_RGBA; //request this image format
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;
//TODO: acquire the current colorspace format & pre-multiplied alpha image.
surface.buf8 = WebPDecodeBGRA(data, size, nullptr, nullptr);
surface.stride = (uint32_t)w;
surface.w = (uint32_t)w;
surface.h = (uint32_t)h;
surface.channelSize = sizeof(uint32_t);
surface.cs = ColorSpace::ARGB8888;
surface.premultiplied = false;
}
@ -54,62 +49,58 @@ void PngLoader::run(unsigned tid)
/* 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);
free(surface.buf8);
lodepng_state_cleanup(&state);
data = nullptr;
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");
if (!pngFile) return false;
auto webpFile = fopen(path.c_str(), "rb");
if (!webpFile) return false;
auto ret = false;
//determine size
if (fseek(pngFile, 0, SEEK_END) < 0) goto finalize;
if (((size = ftell(pngFile)) < 1)) goto finalize;
if (fseek(pngFile, 0, SEEK_SET)) goto finalize;
if (fseek(webpFile, 0, SEEK_END) < 0) goto finalize;
if (((size = ftell(webpFile)) < 1)) goto finalize;
if (fseek(webpFile, 0, SEEK_SET)) goto finalize;
data = (unsigned char *) malloc(size);
if (!data) goto finalize;
freeData = true;
if (fread(data, size, 1, pngFile) < 1) goto finalize;
if (fread(data, size, 1, webpFile) < 1) goto finalize;
lodepng_state_init(&state);
unsigned int width, height;
if (lodepng_inspect(&width, &height, &state, data, size) > 0) goto finalize;
int width, height;
if (!WebPGetInfo(data, size, &width, &height)) goto finalize;
w = static_cast<float>(width);
h = static_cast<float>(height);
ret = true;
goto finalize;
finalize:
fclose(pngFile);
fclose(webpFile);
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) {
this->data = (unsigned char *) malloc(size);
if (!this->data) return false;
@ -120,28 +111,32 @@ bool PngLoader::open(const char* data, uint32_t size, bool copy)
freeData = false;
}
int width, height;
if (!WebPGetInfo(this->data, size, &width, &height)) return false;
w = static_cast<float>(width);
h = static_cast<float>(height);
surface.cs = ColorSpace::ARGB8888;
this->size = size;
return true;
}
bool PngLoader::read()
bool WebpLoader::read()
{
if (!data || w == 0 || h == 0) return false;
if (!LoadModule::read()) return true;
if (!data || w == 0 || h == 0) return false;
TaskScheduler::request(this);
return true;
}
Surface* PngLoader::bitmap()
Surface* WebpLoader::bitmap()
{
this->done();
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
* of this software and associated documentation files (the "Software"), to deal
@ -20,32 +20,30 @@
* SOFTWARE.
*/
#ifndef _TVG_PNG_LOADER_H_
#define _TVG_PNG_LOADER_H_
#ifndef _TVG_WEBP_LOADER_H_
#define _TVG_WEBP_LOADER_H_
#include "tvgLodePng.h"
#include "tvgLoader.h"
#include "tvgTaskScheduler.h"
class PngLoader : public ImageLoader, public Task
class WebpLoader : public ImageLoader, public Task
{
private:
LodePNGState state;
unsigned char* data = nullptr;
unsigned long size = 0;
bool freeData = false;
void run(unsigned tid) override;
public:
PngLoader();
~PngLoader();
WebpLoader();
~WebpLoader();
bool open(const string& path) override;
bool open(const char* data, uint32_t size, bool copy) override;
bool read() 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);
}
}
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
if (((from->fill.flags & SvgFillFlags::Paint) && !(to->flags & 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 ((*content) == end) return false;
if ((*content) == _end) {
if (end) *end = _end;
return false;
}
//Skip comma if any
*content = _skipComma(end);
*content = _skipComma(_end);
if (end) *end = _end;
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);
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];
*b = strtol(tmp, nullptr, 16);
}
return true;
} 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])) {
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];
*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] == ')') {
tr = _parseColor(str + 4, &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)) {
if (*ref) free(*ref);
*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 {
//Handle named color
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]);
*g = (((uint8_t*)(&(colors[i].value)))[1]);
*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.
points[0] = fmodf(points[0], 360.0f);
if (points[0] < 0) points[0] += 360.0f;
auto c = cosf(points[0] * (MATH_PI / 180.0f));
auto s = sinf(points[0] * (MATH_PI / 180.0f));
auto c = cosf(mathDeg2Rad(points[0]));
auto s = sinf(mathDeg2Rad(points[0]));
if (ptCount == 1) {
Matrix tmp = { c, -s, 0, s, c, 0, 0, 0, 1 };
*matrix = mathMultiply(matrix, &tmp);
@ -769,12 +877,12 @@ static Matrix* _parseTransformationMatrix(const char* value)
*matrix = mathMultiply(matrix, &tmp);
} else if (state == MatrixState::SkewX) {
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 = mathMultiply(matrix, &tmp);
} else if (state == MatrixState::SkewY) {
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 = mathMultiply(matrix, &tmp);
}
@ -854,10 +962,10 @@ static bool _attrParseSvgNode(void* data, const char* key, const char* value)
doc->viewFlag = (doc->viewFlag | SvgViewFlag::Height);
}
} else if (!strcmp(key, "viewBox")) {
if (_parseNumber(&value, &doc->vx)) {
if (_parseNumber(&value, &doc->vy)) {
if (_parseNumber(&value, &doc->vw)) {
if (_parseNumber(&value, &doc->vh)) {
if (_parseNumber(&value, nullptr, &doc->vx)) {
if (_parseNumber(&value, nullptr, &doc->vy)) {
if (_parseNumber(&value, nullptr, &doc->vw)) {
if (_parseNumber(&value, nullptr, &doc->vh)) {
doc->viewFlag = (doc->viewFlag | SvgViewFlag::Viewbox);
loader->svgParse->global.h = doc->vh;
}
@ -898,20 +1006,21 @@ static void _handlePaintAttr(SvgPaint* paint, const char* value)
paint->none = true;
return;
}
paint->none = false;
if (!strcmp(value, "currentColor")) {
paint->curColor = true;
paint->none = false;
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)
{
SvgStyleProperty* style = node->style;
style->curColorSet = true;
_toColor(value, &style->color.r, &style->color.g, &style->color.b, nullptr);
if (_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)
{
node->style->flags = (node->style->flags | SvgStyleFlags::Opacity);
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.
// Depending on the type of node, additional functionality may be required.
// refer to https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/display
if (!strcmp(value, "none")) node->display = false;
else node->display = true;
node->style->flags = (node->style->flags | SvgStyleFlags::Display);
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);
if (!strcmp(key, "viewBox")) {
if (!_parseNumber(&value, &symbol->vx) || !_parseNumber(&value, &symbol->vy)) return false;
if (!_parseNumber(&value, &symbol->vw) || !_parseNumber(&value, &symbol->vh)) return false;
if (!_parseNumber(&value, nullptr, &symbol->vx) || !_parseNumber(&value, nullptr, &symbol->vy)) return false;
if (!_parseNumber(&value, nullptr, &symbol->vw) || !_parseNumber(&value, nullptr, &symbol->vh)) return false;
symbol->hasViewBox = true;
} else if (!strcmp(key, "width")) {
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");
//Default display is true("inline").
node->display = true;
node->style->display = true;
node->parent = parent;
node->type = type;
@ -1408,7 +1519,7 @@ static SvgNode* _createClipPathNode(SvgLoaderData* loader, SvgNode* parent, cons
loader->svgParse->node = _createNode(parent, SvgNodeType::ClipPath);
if (!loader->svgParse->node) return nullptr;
loader->svgParse->node->display = false;
loader->svgParse->node->style->display = false;
loader->svgParse->node->node.clip.userSpace = true;
func(buf, bufLength, _attrParseClipPathNode, loader);
@ -1433,7 +1544,6 @@ static SvgNode* _createSymbolNode(SvgLoaderData* loader, SvgNode* parent, const
loader->svgParse->node = _createNode(parent, SvgNodeType::Symbol);
if (!loader->svgParse->node) return nullptr;
loader->svgParse->node->display = false;
loader->svgParse->node->node.symbol.align = AspectRatioAlign::XMidYMid;
loader->svgParse->node->node.symbol.meetOrSlice = AspectRatioMeetOrSlice::Meet;
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)
{
float num;
while (_parseNumber(&str, &num)) polygon->pts.push(num);
float num_x, num_y;
while (_parseNumber(&str, nullptr, &num_x) && _parseNumber(&str, nullptr, &num_y)) {
polygon->pts.push(num_x);
polygon->pts.push(num_y);
}
return true;
}
@ -2378,8 +2491,9 @@ static bool _attrParseStopsStyle(void* data, const char* key, const char* value)
stop->a = _toOpacity(value);
loader->svgParse->flags = (loader->svgParse->flags | SvgStopStyleFlags::StopOpacity);
} else if (!strcmp(key, "stop-color")) {
_toColor(value, &stop->r, &stop->g, &stop->b, nullptr);
loader->svgParse->flags = (loader->svgParse->flags | SvgStopStyleFlags::StopColor);
if (_toColor(value, &stop->r, &stop->g, &stop->b, nullptr)) {
loader->svgParse->flags = (loader->svgParse->flags | SvgStopStyleFlags::StopColor);
}
} else {
return false;
}
@ -2849,9 +2963,15 @@ static void _styleCopy(SvgStyleProperty* to, const SvgStyleProperty* from)
to->color = from->color;
to->curColorSet = true;
}
if (from->flags & SvgStyleFlags::Opacity) {
to->opacity = from->opacity;
}
if (from->flags & SvgStyleFlags::PaintOrder) {
to->paintOrder = from->paintOrder;
}
if (from->flags & SvgStyleFlags::Display) {
to->display = from->display;
}
//Fill
to->fill.flags = (to->fill.flags | from->fill.flags);
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--;
}
@ -3146,6 +3274,11 @@ static void _svgLoaderParserXmlOpen(SvgLoaderData* loader, const char* content,
if (loader->stack.count > 0) parent = loader->stack.last();
else parent = loader->doc;
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))) {
SvgStyleGradient* gradient;
gradient = gradientMethod(loader, attrs, attrsLength);
@ -3257,7 +3390,7 @@ static void _inefficientNodeCheck(TVG_UNUSED SvgNode* node)
#ifdef THORVG_LOG_ENABLED
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->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();
if (copy) {
content = (char*)malloc(size);
content = (char*)malloc(size + 1);
if (!content) return false;
memcpy((char*)content, data, size);
} else content = data;
content[size] = '\0';
} else content = (char*)data;
this->size = size;
this->copy = copy;
@ -3726,7 +3860,7 @@ bool SvgLoader::open(const string& path)
if (filePath.empty()) return false;
content = filePath.c_str();
content = (char*)filePath.c_str();
size = filePath.size();
return header();

View File

@ -31,7 +31,7 @@ class SvgLoader : public ImageLoader, public Task
public:
string filePath;
string svgPath = "";
const char* content = nullptr;
char* content = nullptr;
uint32_t size = 0;
SvgLoaderData loaderData;

View File

@ -485,11 +485,12 @@ struct SvgStyleProperty
SvgComposite mask;
int opacity;
SvgColor color;
bool curColorSet;
char* cssClass;
bool paintOrder; //true if default (fill, stroke), false otherwise
SvgStyleFlags flags;
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
@ -518,7 +519,6 @@ struct SvgNode
SvgCssStyleNode cssStyle;
SvgSymbolNode symbol;
} node;
bool display;
~SvgNode();
};
@ -560,6 +560,7 @@ struct SvgLoaderData
int level = 0;
bool result = false;
bool style = false;
SvgNode* currentGraphicsNode = nullptr;
};
struct Box

View File

@ -126,7 +126,7 @@ void _pathAppendArcTo(Array<PathCommand>* cmds, Array<Point>* pts, Point* cur, P
rx = fabsf(rx);
ry = fabsf(ry);
angle = angle * MATH_PI / 180.0f;
angle = mathDeg2Rad(angle);
cosPhi = cosf(angle);
sinPhi = sinf(angle);
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) {
case 'm':
@ -464,6 +464,7 @@ static bool _processCommand(Array<PathCommand>* cmds, Array<Point>* pts, char cm
case 'Z': {
cmds->push(PathCommand::Close);
*cur = *startPoint;
*closed = true;
break;
}
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;
@ -500,6 +501,9 @@ static char* _nextCommand(char* path, char* cmd, float* arr, int* count)
} else {
if (*cmd == 'm') *cmd = 'l';
else if (*cmd == 'M') *cmd = 'L';
else {
if (*closed) return nullptr;
}
}
if (*count == 7) {
//Special case for arc command
@ -548,6 +552,7 @@ bool svgPathToShape(const char* svgPath, Shape* shape)
Point startPoint = { 0, 0 };
char cmd = 0;
bool isQuadratic = false;
bool closed = false;
char* path = (char*)svgPath;
auto& pts = P(shape)->rs.path.pts;
@ -555,9 +560,10 @@ bool svgPathToShape(const char* svgPath, Shape* shape)
auto lastCmds = cmds.count;
while ((path[0] != '\0')) {
path = _nextCommand(path, &cmd, numberArray, &numberCount);
path = _nextCommand(path, &cmd, numberArray, &numberCount, &closed);
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;

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
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 (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)
{
if (!node->node.image.href) return nullptr;
if (!node->node.image.href || !strlen(node->node.image.href)) return nullptr;
auto picture = Picture::gen();
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()
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;
for (uint32_t i = 0; i < node->child.count; ++i, ++child) {
if (_isGroupType((*child)->type)) {
if ((*child)->type == SvgNodeType::Use)
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));
} else if ((*child)->type == SvgNodeType::Image) {
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;
char* decoded = (char*)malloc(sizeof(char) * length + 1);
decoded[length] = '\0';
char a, b;
int idx =0;
@ -64,7 +63,9 @@ size_t svgUtilURLDecode(const char *src, char** dst)
decoded[idx++] = *src++;
}
}
decoded[idx] = '\0';
*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 == 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;
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)
{
//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);
#endif
}

View File

@ -1,4 +1,4 @@
/*
/*
* Copyright (c) 2021 - 2024 the ThorVG project. All rights reserved.
* 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)
{
//1. calculate how many iterations we need to cover the length

View File

@ -24,6 +24,15 @@
#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)
{
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)
{
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 neonFilled = iterations * 4;
dst += offset;
uint32x4_t vectorVal = {val, val, val, val};
for (uint32_t i = 0; i < iterations; ++i) {
vst1q_u32(dst, vectorVal);
dst += 4;
}
#endif
int32_t leftovers = len - neonFilled;
while (leftovers--) *dst++ = val;
}
@ -56,7 +96,7 @@ static bool neonRasterTranslucentRle(SwSurface* surface, const SwRleData* rle, u
return false;
}
auto color = surface->blender.join(r, g, b, a);
auto color = surface->join(r, g, b, a);
auto span = rle->spans;
uint32_t src;
uint8x8_t *vDst = nullptr;
@ -67,9 +107,9 @@ static bool neonRasterTranslucentRle(SwSurface* surface, const SwRleData* rle, u
else src = color;
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
*dst = src + ALPHA_BLEND(*dst, ialpha);
vDst = (uint8x8_t*)(dst + 1);
@ -101,7 +141,7 @@ static bool neonRasterTranslucentRect(SwSurface* surface, const SwBBox& region,
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 h = static_cast<uint32_t>(region.max.y - region.min.y);
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) {
auto dst = &buffer[y * surface->stride];
if ((((uint32_t) dst) & 0x7) != 0) {
if ((((uintptr_t) dst) & 0x7) != 0) {
//fill not aligned byte
*dst = color + ALPHA_BLEND(*dst, ialpha);
vDst = (uint8x8_t*) (dst + 1);

View File

@ -22,43 +22,26 @@
#include "tvgSwCommon.h"
#include "tvgMath.h"
#include "tvgBezier.h"
#include "tvgLines.h"
/************************************************************************/
/* Internal Class Implementation */
/************************************************************************/
struct Line
static bool _outlineBegin(SwOutline& outline)
{
Point pt1;
Point pt2;
};
static float _lineLength(const Point& pt1, const Point& pt2)
{
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;
//Make a contour if lineTo/curveTo without calling close or moveTo beforehand.
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()]);
outline.types.push(SW_CURVE_TYPE_POINT);
return false;
}
static bool _outlineEnd(SwOutline& outline)
{
//Make a contour if lineTo/curveTo without calling close/moveTo beforehand.
if (outline.pts.empty()) return false;
outline.cntrs.push(outline.pts.count - 1);
outline.closed.push(false);
@ -119,10 +102,11 @@ static bool _outlineClose(SwOutline& outline)
static void _dashLineTo(SwDashStroke& dash, const Point* to, const Matrix* transform)
{
Line cur = {dash.ptCur, *to};
auto len = _lineLength(cur.pt1, cur.pt2);
auto len = lineLength(cur.pt1, cur.pt2);
if (mathZero(len)) {
_outlineMoveTo(*dash.outline, &dash.ptCur, transform);
//draw the current line fully
} else if (len < dash.curLen) {
dash.curLen -= len;
if (!dash.curOpGap) {
@ -132,12 +116,13 @@ static void _dashLineTo(SwDashStroke& dash, const Point* to, const Matrix* trans
}
_outlineLineTo(*dash.outline, to, transform);
}
//draw the current line partially
} else {
while (len - dash.curLen > 0.0001f) {
Line left, right;
if (dash.curLen > 0) {
len -= dash.curLen;
_lineSplitAt(cur, dash.curLen, left, right);
lineSplitAt(cur, dash.curLen, left, right);
if (!dash.curOpGap) {
if (dash.move || dash.pattern[dash.curIdx] - dash.curLen < FLT_EPSILON) {
_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};
auto len = bezLength(cur);
//draw the current line fully
if (mathZero(len)) {
_outlineMoveTo(*dash.outline, &dash.ptCur, transform);
} 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);
}
//draw the current line partially
} else {
while ((len - dash.curLen) > 0.0001f) {
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.curLen = dash.pattern[dash.curIdx] - offset;
@ -275,7 +270,7 @@ static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* trans
//default
if (end > begin) {
if (begin > 0) dash.cnt += 4;
if (begin > 0.0f) dash.cnt += 4;
else dash.cnt += 2;
//looping
} 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[1] = begin;
dash.pattern[2] = end - begin;
dash.pattern[3] = length - (end - begin);
dash.pattern[3] = length - end;
}
trimmed = true;
@ -322,14 +317,22 @@ static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* trans
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) {
case PathCommand::Close: {
_dashClose(dash, transform);
break;
}
case PathCommand::MoveTo: {
_dashMoveTo(dash, offIdx, offset, pts, transform);
if (rshape->stroke->trim.individual) _dashMoveTo(dash, pts);
else _dashMoveTo(dash, offIdx, offset, pts);
++pts;
break;
}
@ -367,13 +370,19 @@ static float _outlineLength(const RenderShape* rshape)
const Point* close = nullptr;
auto length = 0.0f;
auto slength = -1.0f;
auto simultaneous = !rshape->stroke->trim.individual;
//Compute the whole length
while (cmdCnt-- > 0) {
switch (*cmds) {
case PathCommand::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;
}
case PathCommand::MoveTo: {
@ -394,7 +403,8 @@ static float _outlineLength(const RenderShape* rshape)
}
++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);
auto outline = shape->outline;
bool closed = false;
auto closed = false;
//Generate Outlines
while (cmdCnt-- > 0) {
@ -444,13 +454,13 @@ static bool _genOutline(SwShape* shape, const RenderShape* rshape, const Matrix*
break;
}
case PathCommand::LineTo: {
if (closed) closed = _outlineEnd(*outline);
if (closed) closed = _outlineBegin(*outline);
_outlineLineTo(*outline, pts, transform);
++pts;
break;
}
case PathCommand::CubicTo: {
if (closed) closed = _outlineEnd(*outline);
if (closed) closed = _outlineBegin(*outline);
_outlineCubicTo(*outline, pts, pts + 1, pts + 2, transform);
pts += 3;
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
{
return unique_ptr<Animation>(new Animation);

View File

@ -98,25 +98,14 @@ struct Canvas::Impl
auto flag = RenderUpdateFlag::None;
if (refresh || force) flag = RenderUpdateFlag::All;
//Update single paint node
if (paint) {
//Optimize Me: Can we skip the searching?
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
paint->pImpl->update(renderer, nullptr, clips, 255, flag);
} else {
for (auto paint : paints) {
paint->pImpl->update(renderer, nullptr, clips, 255, flag);
}
refresh = false;
}
refresh = false;
return Result::Success;
}

View File

@ -31,6 +31,9 @@ namespace tvg
class FrameModule: public ImageLoader
{
public:
float segmentBegin = 0.0f;
float segmentEnd = 1.0f;
FrameModule(FileType type) : ImageLoader(type) {}
virtual ~FrameModule() {}
@ -39,6 +42,18 @@ public:
virtual float curFrame() = 0; //return the current frame number
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; }
};

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
//We know renderer type, avoid dynamic_cast for performance.
auto renderer = static_cast<GlRenderer*>(Canvas::pImpl->renderer);
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.
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("svg")) return _find(FileType::Svg);
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("jpg")) return _find(FileType::Jpg);
if (!ext.compare("webp")) return _find(FileType::Webp);
@ -271,7 +270,7 @@ bool LoaderMgr::term()
auto tmp = loader;
loader = loader->next;
_activeLoaders.remove(tmp);
if (ret) delete(loader);
if (ret) delete(tmp);
}
return true;
}
@ -295,15 +294,24 @@ LoadModule* LoaderMgr::loader(const string& path, bool* invalid)
{
*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 (loader->open(path)) {
loader->hashpath = strdup(path.c_str());
loader->pathcache = true;
{
ScopedLock lock(key);
_activeLoaders.back(loader);
if (allowCache) {
loader->hashpath = strdup(path.c_str());
loader->pathcache = true;
{
ScopedLock lock(key);
_activeLoaders.back(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++) {
if (auto loader = _find(static_cast<FileType>(i))) {
if (loader->open(path)) {
loader->hashpath = strdup(path.c_str());
loader->pathcache = true;
{
ScopedLock lock(key);
_activeLoaders.back(loader);
if (allowCache) {
loader->hashpath = strdup(path.c_str());
loader->pathcache = true;
{
ScopedLock lock(key);
_activeLoaders.back(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.
//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;
}
@ -362,7 +380,7 @@ LoadModule* LoaderMgr::loader(const char* data, uint32_t size, const string& mim
if (!mimeType.empty()) {
if (auto loader = _findByType(mimeType)) {
if (loader->open(data, size, copy)) {
if (!copy) {
if (allowCache) {
loader->hashkey = HASH_KEY(data);
ScopedLock lock(key);
_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));
if (loader) {
if (loader->open(data, size, copy)) {
if (!copy) {
if (allowCache) {
loader->hashkey = HASH_KEY(data);
ScopedLock lock(key);
_activeLoaders.back(loader);

View File

@ -163,6 +163,7 @@ struct RenderStroke
struct {
float begin = 0.0f;
float end = 1.0f;
bool individual = false;
} trim;
~RenderStroke()

View File

@ -157,14 +157,17 @@ Result Saver::save(unique_ptr<Animation> animation, const string& path, uint32_t
auto a = animation.release();
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())) {
delete(a);
if (remove) delete(a);
return Result::InsufficientCondition;
}
//Already on saving an other resource.
if (pImpl->saveModule) {
delete(a);
if (remove) delete(a);
return Result::InsufficientCondition;
}
@ -173,12 +176,12 @@ Result Saver::save(unique_ptr<Animation> animation, const string& path, uint32_t
pImpl->saveModule = saveModule;
return Result::Success;
} else {
delete(a);
if (remove) delete(a);
delete(saveModule);
return Result::Unknown;
}
}
delete(a);
if (remove) delete(a);
return Result::NonSupport;
}

View File

@ -145,13 +145,15 @@ Result Shape::appendArc(float cx, float cy, float radius, float startAngle, floa
//just circle
if (sweep >= 360.0f || sweep <= -360.0f) return appendCircle(cx, cy, radius, radius);
startAngle = (startAngle * MATH_PI) / 180.0f;
sweep = sweep * MATH_PI / 180.0f;
const float arcPrecision = 1e-5f;
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 fract = fmodf(sweep, MATH_PI2);
fract = (mathZero(fract)) ? MATH_PI2 * sweepSign : fract;
fract = (fabsf(fract) < arcPrecision) ? MATH_PI2 * sweepSign : fract;
//Start from here
Point start = {radius * cosf(startAngle), radius * sinf(startAngle)};

View File

@ -76,8 +76,21 @@ struct Shape::Impl
//Composition test
const Paint* target;
auto method = shape->composite(&target);
if (!target || method == tvg::CompositeMethod::ClipPath) return false;
if (target->pImpl->opacity == 255 || target->pImpl->opacity == 0) return false;
if (!target || method == CompositeMethod::ClipPath) 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;
}
@ -203,7 +216,7 @@ struct Shape::Impl
return true;
}
bool strokeTrim(float begin, float end)
bool strokeTrim(float begin, float end, bool individual)
{
if (!rs.stroke) {
if (begin == 0.0f && end == 1.0f) return true;
@ -214,6 +227,7 @@ struct Shape::Impl
rs.stroke->trim.begin = begin;
rs.stroke->trim.end = end;
rs.stroke->trim.individual = individual;
flag |= RenderUpdateFlag::Stroke;
return true;

View File

@ -1,6 +1,6 @@
#!/bin/bash -e
VERSION=0.12.10
VERSION=0.13.3
cd thirdparty/thorvg/ || true
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_PNG_LOADER_SUPPORT
#define THORVG_JPG_LOADER_SUPPORT
#define THORVG_WEBP_LOADER_SUPPORT
#define THORVG_THREAD_SUPPORT
// For internal debugging:
@ -55,11 +56,13 @@ cp -rv src/renderer ../src/
rm -rfv ../src/renderer/gl_engine
rm -rfv ../src/renderer/wg_engine
# Enabled embedded loaders: raw, JPEG, PNG.
# Enabled embedded loaders: raw, JPEG, PNG, WebP.
mkdir ../src/loaders
cp -rv src/loaders/svg src/loaders/raw ../src/loaders/
cp -rv src/loaders/jpg ../src/loaders/
cp -rv src/loaders/png src/loaders/external_png ../src/loaders/
cp -rv 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
rm -rf tmp