thorvg: Update to 0.15.8

This commit is contained in:
Rémi Verschelde
2025-01-09 15:03:57 +01:00
parent 0e3a5eda86
commit 6be45d2a71
31 changed files with 545 additions and 192 deletions

View File

@ -934,7 +934,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.15.5 (89ab573acb253567975b2494069c7ee9abc9267c, 2024) - Version: 0.15.8 (bd8c2fca7663a22fba7a339937cb60f2f6247a2e, 2025)
- License: MIT - License: MIT
Files extracted from upstream source: Files extracted from upstream source:

View File

@ -22,7 +22,7 @@ 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> Sergii Liebodkin <sergii@lottiefiles.com>
Jinny You <jinny@lottiefiles.com> Jinny You <jinny@lottiefiles.com>
Nattu Adnan <nattu@reallynattu.com> Nattu Adnan <nattu@reallynattu.com>
Gabor Kiss-Vamosi <kisvegabor@gmail.com> Gabor Kiss-Vamosi <kisvegabor@gmail.com>
@ -35,3 +35,6 @@ Thaddeus Crews <repiteo@outlook.com>
Josh Soref <jsoref@gmail.com> Josh Soref <jsoref@gmail.com>
Elliott Sales de Andrade <quantum.analyst@gmail.com> Elliott Sales de Andrade <quantum.analyst@gmail.com>
Łukasz Pomietło <oficjalnyadreslukasza@gmail.com> Łukasz Pomietło <oficjalnyadreslukasza@gmail.com>
Kelly Loh <kelly@lottiefiles.com>
Dragoș Tiselice <dragos@lottiefiles.com>
Marcin Baszczewski <marcin@baszczewski.pl>

View File

@ -15,5 +15,5 @@
// For internal debugging: // For internal debugging:
//#define THORVG_LOG_ENABLED //#define THORVG_LOG_ENABLED
#define THORVG_VERSION_STRING "0.15.5" #define THORVG_VERSION_STRING "0.15.8"
#endif #endif

View File

@ -217,7 +217,10 @@ enum class SceneEffect : uint8_t
{ {
ClearAll = 0, ///< Reset all previously applied scene effects, restoring the scene to its original state. ClearAll = 0, ///< Reset all previously applied scene effects, restoring the scene to its original state.
GaussianBlur, ///< Apply a blur effect with a Gaussian filter. Param(3) = {sigma(float)[> 0], direction(int)[both: 0 / horizontal: 1 / vertical: 2], border(int)[duplicate: 0 / wrap: 1], quality(int)[0 - 100]} GaussianBlur, ///< Apply a blur effect with a Gaussian filter. Param(3) = {sigma(float)[> 0], direction(int)[both: 0 / horizontal: 1 / vertical: 2], border(int)[duplicate: 0 / wrap: 1], quality(int)[0 - 100]}
DropShadow ///< Apply a drop shadow effect with a Gaussian Blur filter. Param(8) = {color_R(int)[0 - 255], color_G(int)[0 - 255], color_B(int)[0 - 255], opacity(int)[0 - 255], angle(float)[0 - 360], distance(float), blur_sigma(float)[> 0], quality(int)[0 - 100]} DropShadow, ///< Apply a drop shadow effect with a Gaussian Blur filter. Param(8) = {color_R(int)[0 - 255], color_G(int)[0 - 255], color_B(int)[0 - 255], opacity(int)[0 - 255], angle(float)[0 - 360], distance(float), blur_sigma(float)[> 0], quality(int)[0 - 100]}
Fill, ///< Override the scene content color with a given fill information (Experimental API). Param(5) = {color_R(int)[0 - 255], color_G(int)[0 - 255], color_B(int)[0 - 255], opacity(int)[0 - 255]}
Tint, ///< Tinting the current scene color with a given black, white color paramters (Experimental API). Param(7) = {black_R(int)[0 - 255], black_G(int)[0 - 255], black_B(int)[0 - 255], white_R(int)[0 - 255], white_G(int)[0 - 255], white_B(int)[0 - 255], intensity(float)[0 - 100]}
Tritone ///< Apply a tritone color effect to the scene using three color parameters for shadows, midtones, and highlights (Experimental API). Param(9) = {Shadow_R(int)[0 - 255], Shadow_G(int)[0 - 255], Shadow_B(int)[0 - 255], Midtone_R(int)[0 - 255], Midtone_G(int)[0 - 255], Midtone_B(int)[0 - 255], Highlight_R(int)[0 - 255], Highlight_G(int)[0 - 255], Highlight_B(int)[0 - 255]}
}; };
@ -2110,7 +2113,7 @@ public:
/** /**
* @brief Set the access function for traversing the Picture scene tree nodes. * @brief Set the access function for traversing the Picture scene tree nodes.
* *
* @param[in] picture The picture node to traverse the internal scene-tree. * @param[in] paint The paint node to traverse the internal scene-tree.
* @param[in] func The callback function calling for every paint nodes of the Picture. * @param[in] func The callback function calling for every paint nodes of the Picture.
* @param[in] data Data passed to the @p func as its argument. * @param[in] data Data passed to the @p func as its argument.
* *
@ -2118,7 +2121,7 @@ public:
* *
* @note Experimental API * @note Experimental API
*/ */
Result set(const Picture* picture, std::function<bool(const Paint* paint, void* data)> func, void* data) noexcept; Result set(Paint* paint, std::function<bool(const Paint* paint, void* data)> func, void* data) noexcept;
/** /**
* @brief Generate a unique ID (hash key) from a given name. * @brief Generate a unique ID (hash key) from a given name.

View File

@ -67,6 +67,7 @@ bool PngLoader::open(const string& path)
bool PngLoader::open(const char* data, uint32_t size, bool copy) bool PngLoader::open(const char* data, uint32_t size, bool copy)
{ {
#ifdef THORVG_FILE_IO_SUPPORT
image->opaque = NULL; image->opaque = NULL;
if (!png_image_begin_read_from_memory(image, data, size)) return false; if (!png_image_begin_read_from_memory(image, data, size)) return false;
@ -75,6 +76,9 @@ bool PngLoader::open(const char* data, uint32_t size, bool copy)
h = (float)image->height; h = (float)image->height;
return true; return true;
#else
return false;
#endif
} }

View File

@ -68,6 +68,7 @@ WebpLoader::~WebpLoader()
bool WebpLoader::open(const string& path) bool WebpLoader::open(const string& path)
{ {
#ifdef THORVG_FILE_IO_SUPPORT
auto webpFile = fopen(path.c_str(), "rb"); auto webpFile = fopen(path.c_str(), "rb");
if (!webpFile) return false; if (!webpFile) return false;
@ -96,6 +97,9 @@ bool WebpLoader::open(const string& path)
finalize: finalize:
fclose(webpFile); fclose(webpFile);
return ret; return ret;
#else
return false;
#endif
} }

View File

@ -70,6 +70,7 @@ JpgLoader::~JpgLoader()
bool JpgLoader::open(const string& path) bool JpgLoader::open(const string& path)
{ {
#ifdef THORVG_FILE_IO_SUPPORT
int width, height; int width, height;
decoder = jpgdHeader(path.c_str(), &width, &height); decoder = jpgdHeader(path.c_str(), &width, &height);
if (!decoder) return false; if (!decoder) return false;
@ -78,6 +79,9 @@ bool JpgLoader::open(const string& path)
h = static_cast<float>(height); h = static_cast<float>(height);
return true; return true;
#else
return false;
#endif
} }

View File

@ -3973,6 +3973,7 @@ bool SvgLoader::open(const char* data, uint32_t size, bool copy)
bool SvgLoader::open(const string& path) bool SvgLoader::open(const string& path)
{ {
#ifdef THORVG_FILE_IO_SUPPORT
clear(); clear();
ifstream f; ifstream f;
@ -3990,6 +3991,9 @@ bool SvgLoader::open(const string& path)
size = filePath.size(); size = filePath.size();
return header(); return header();
#else
return false;
#endif
} }

View File

@ -213,7 +213,7 @@ static bool _appendClipUseNode(SvgLoaderData& loaderData, SvgNode* node, Shape*
Matrix m = {1, 0, node->node.use.x, 0, 1, node->node.use.y, 0, 0, 1}; Matrix m = {1, 0, node->node.use.x, 0, 1, node->node.use.y, 0, 0, 1};
finalTransform *= m; finalTransform *= m;
} }
if (child->transform) finalTransform = *child->transform * finalTransform; if (child->transform) finalTransform *= *child->transform;
return _appendClipShape(loaderData, child, shape, vBox, svgPath, identity((const Matrix*)(&finalTransform)) ? nullptr : &finalTransform); return _appendClipShape(loaderData, child, shape, vBox, svgPath, identity((const Matrix*)(&finalTransform)) ? nullptr : &finalTransform);
} }

View File

@ -547,8 +547,8 @@ SwRle* rleRender(const SwBBox* bbox);
void rleFree(SwRle* rle); void rleFree(SwRle* rle);
void rleReset(SwRle* rle); void rleReset(SwRle* rle);
void rleMerge(SwRle* rle, SwRle* clip1, SwRle* clip2); void rleMerge(SwRle* rle, SwRle* clip1, SwRle* clip2);
void rleClip(SwRle* rle, const SwRle* clip); bool rleClip(SwRle* rle, const SwRle* clip);
void rleClip(SwRle* rle, const SwBBox* clip); bool rleClip(SwRle* rle, const SwBBox* clip);
SwMpool* mpoolInit(uint32_t threads); SwMpool* mpoolInit(uint32_t threads);
bool mpoolTerm(SwMpool* mpool); bool mpoolTerm(SwMpool* mpool);
@ -575,10 +575,17 @@ void rasterXYFlip(uint32_t* src, uint32_t* dst, int32_t stride, int32_t w, int32
void rasterUnpremultiply(RenderSurface* surface); void rasterUnpremultiply(RenderSurface* surface);
void rasterPremultiply(RenderSurface* surface); void rasterPremultiply(RenderSurface* surface);
bool rasterConvertCS(RenderSurface* surface, ColorSpace to); bool rasterConvertCS(RenderSurface* surface, ColorSpace to);
uint32_t rasterUnpremultiply(uint32_t data);
bool effectGaussianBlur(SwCompositor* cmp, SwSurface* surface, const RenderEffectGaussianBlur* params); bool effectGaussianBlur(SwCompositor* cmp, SwSurface* surface, const RenderEffectGaussianBlur* params);
bool effectGaussianBlurPrepare(RenderEffectGaussianBlur* effect); bool effectGaussianBlurPrepare(RenderEffectGaussianBlur* effect);
bool effectDropShadow(SwCompositor* cmp, SwSurface* surfaces[2], const RenderEffectDropShadow* params, uint8_t opacity, bool direct); bool effectDropShadow(SwCompositor* cmp, SwSurface* surfaces[2], const RenderEffectDropShadow* params, bool direct);
bool effectDropShadowPrepare(RenderEffectDropShadow* effect); bool effectDropShadowPrepare(RenderEffectDropShadow* effect);
bool effectFillPrepare(RenderEffectFill* effect);
bool effectFill(SwCompositor* cmp, const RenderEffectFill* params, bool direct);
bool effectTintPrepare(RenderEffectTint* effect);
bool effectTint(SwCompositor* cmp, const RenderEffectTint* params, bool direct);
bool effectTritonePrepare(RenderEffectTritone* effect);
bool effectTritone(SwCompositor* cmp, const RenderEffectTritone* params, bool direct);
#endif /* _TVG_SW_COMMON_H_ */ #endif /* _TVG_SW_COMMON_H_ */

View File

@ -487,6 +487,7 @@ void fillRadial(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32
auto src = MULTIPLY(A(_pixel(fill, sqrtf(det))), a); auto src = MULTIPLY(A(_pixel(fill, sqrtf(det))), a);
auto tmp = maskOp(src, *cmp, 0); auto tmp = maskOp(src, *cmp, 0);
*dst = tmp + MULTIPLY(*dst, ~tmp); *dst = tmp + MULTIPLY(*dst, ~tmp);
det += deltaDet;
deltaDet += deltaDeltaDet; deltaDet += deltaDeltaDet;
b += deltaB; b += deltaB;
} }

View File

@ -150,7 +150,6 @@ bool effectGaussianBlurPrepare(RenderEffectGaussianBlur* params)
//invalid //invalid
if (extends == 0) { if (extends == 0) {
params->invalid = true;
free(rd); free(rd);
return false; return false;
} }
@ -158,6 +157,7 @@ bool effectGaussianBlurPrepare(RenderEffectGaussianBlur* params)
_gaussianExtendRegion(params->extend, extends, params->direction); _gaussianExtendRegion(params->extend, extends, params->direction);
params->rd = rd; params->rd = rd;
params->valid = true;
return true; return true;
} }
@ -165,11 +165,6 @@ bool effectGaussianBlurPrepare(RenderEffectGaussianBlur* params)
bool effectGaussianBlur(SwCompositor* cmp, SwSurface* surface, const RenderEffectGaussianBlur* params) bool effectGaussianBlur(SwCompositor* cmp, SwSurface* surface, const RenderEffectGaussianBlur* params)
{ {
if (cmp->image.channelSize != sizeof(uint32_t)) {
TVGERR("SW_ENGINE", "Not supported grayscale Gaussian Blur!");
return false;
}
auto& buffer = surface->compositor->image; auto& buffer = surface->compositor->image;
auto data = static_cast<SwGaussianBlur*>(params->rd); auto data = static_cast<SwGaussianBlur*>(params->rd);
auto& bbox = cmp->bbox; auto& bbox = cmp->bbox;
@ -310,7 +305,6 @@ bool effectDropShadowPrepare(RenderEffectDropShadow* params)
//invalid //invalid
if (extends == 0 || params->color[3] == 0) { if (extends == 0 || params->color[3] == 0) {
params->invalid = true;
free(rd); free(rd);
return false; return false;
} }
@ -327,6 +321,7 @@ bool effectDropShadowPrepare(RenderEffectDropShadow* params)
_dropShadowExtendRegion(params->extend, extends, rd->offset); _dropShadowExtendRegion(params->extend, extends, rd->offset);
params->rd = rd; params->rd = rd;
params->valid = true;
return true; return true;
} }
@ -335,13 +330,8 @@ bool effectDropShadowPrepare(RenderEffectDropShadow* params)
//A quite same integration with effectGaussianBlur(). See it for detailed comments. //A quite same integration with effectGaussianBlur(). See it for detailed comments.
//surface[0]: the original image, to overlay it into the filtered image. //surface[0]: the original image, to overlay it into the filtered image.
//surface[1]: temporary buffer for generating the filtered image. //surface[1]: temporary buffer for generating the filtered image.
bool effectDropShadow(SwCompositor* cmp, SwSurface* surface[2], const RenderEffectDropShadow* params, uint8_t opacity, bool direct) bool effectDropShadow(SwCompositor* cmp, SwSurface* surface[2], const RenderEffectDropShadow* params, bool direct)
{ {
if (cmp->image.channelSize != sizeof(uint32_t)) {
TVGERR("SW_ENGINE", "Not supported grayscale Drop Shadow!");
return false;
}
//FIXME: if the body is partially visible due to clipping, the shadow also becomes partially visible. //FIXME: if the body is partially visible due to clipping, the shadow also becomes partially visible.
auto data = static_cast<SwDropShadow*>(params->rd); auto data = static_cast<SwDropShadow*>(params->rd);
@ -357,7 +347,8 @@ bool effectDropShadow(SwCompositor* cmp, SwSurface* surface[2], const RenderEffe
auto stride = cmp->image.stride; auto stride = cmp->image.stride;
auto front = cmp->image.buf32; auto front = cmp->image.buf32;
auto back = buffer[1]->buf32; auto back = buffer[1]->buf32;
opacity = MULTIPLY(params->color[3], opacity);
auto opacity = direct ? MULTIPLY(params->color[3], cmp->opacity) : params->color[3];
TVGLOG("SW_ENGINE", "DropShadow region(%ld, %ld, %ld, %ld) params(%f %f %f), level(%d)", bbox.min.x, bbox.min.y, bbox.max.x, bbox.max.y, params->angle, params->distance, params->sigma, data->level); TVGLOG("SW_ENGINE", "DropShadow region(%ld, %ld, %ld, %ld) params(%f %f %f), level(%d)", bbox.min.x, bbox.min.y, bbox.max.x, bbox.max.y, params->angle, params->distance, params->sigma, data->level);
@ -408,3 +399,181 @@ bool effectDropShadow(SwCompositor* cmp, SwSurface* surface[2], const RenderEffe
return true; return true;
} }
/************************************************************************/
/* Fill Implementation */
/************************************************************************/
bool effectFillPrepare(RenderEffectFill* params)
{
params->valid = true;
return true;
}
bool effectFill(SwCompositor* cmp, const RenderEffectFill* params, bool direct)
{
auto opacity = direct ? MULTIPLY(params->color[3], cmp->opacity) : params->color[3];
auto& bbox = cmp->bbox;
auto w = size_t(bbox.max.x - bbox.min.x);
auto h = size_t(bbox.max.y - bbox.min.y);
auto color = cmp->recoverSfc->join(params->color[0], params->color[1], params->color[2], 255);
TVGLOG("SW_ENGINE", "Fill region(%ld, %ld, %ld, %ld), param(%d %d %d %d)", bbox.min.x, bbox.min.y, bbox.max.x, bbox.max.y, params->color[0], params->color[1], params->color[2], params->color[3]);
if (direct) {
auto dbuffer = cmp->recoverSfc->buf32 + (bbox.min.y * cmp->recoverSfc->stride + bbox.min.x);
auto sbuffer = cmp->image.buf32 + (bbox.min.y * cmp->image.stride + bbox.min.x);
for (size_t y = 0; y < h; ++y) {
auto dst = dbuffer;
auto src = sbuffer;
for (size_t x = 0; x < w; ++x, ++dst, ++src) {
auto a = MULTIPLY(opacity, A(*src));
auto tmp = ALPHA_BLEND(color, a);
*dst = tmp + ALPHA_BLEND(*dst, 255 - a);
}
dbuffer += cmp->image.stride;
sbuffer += cmp->recoverSfc->stride;
}
cmp->valid = true; //no need the subsequent composition
} else {
auto dbuffer = cmp->image.buf32 + (bbox.min.y * cmp->image.stride + bbox.min.x);
for (size_t y = 0; y < h; ++y) {
auto dst = dbuffer;
for (size_t x = 0; x < w; ++x, ++dst) {
*dst = ALPHA_BLEND(color, MULTIPLY(opacity, A(*dst)));
}
dbuffer += cmp->image.stride;
}
}
return true;
}
/************************************************************************/
/* Tint Implementation */
/************************************************************************/
bool effectTintPrepare(RenderEffectTint* params)
{
params->valid = true;
return true;
}
bool effectTint(SwCompositor* cmp, const RenderEffectTint* params, bool direct)
{
auto& bbox = cmp->bbox;
auto w = size_t(bbox.max.x - bbox.min.x);
auto h = size_t(bbox.max.y - bbox.min.y);
auto black = cmp->recoverSfc->join(params->black[0], params->black[1], params->black[2], 255);
auto white = cmp->recoverSfc->join(params->white[0], params->white[1], params->white[2], 255);
auto opacity = cmp->opacity;
auto luma = cmp->recoverSfc->alphas[2]; //luma function
TVGLOG("SW_ENGINE", "Tint region(%ld, %ld, %ld, %ld), param(%d %d %d, %d %d %d, %d)", bbox.min.x, bbox.min.y, bbox.max.x, bbox.max.y, params->black[0], params->black[1], params->black[2], params->white[0], params->white[1], params->white[2], params->intensity);
/* Tint Formula: (1 - L) * Black + L * White, where the L is Luminance. */
if (direct) {
auto dbuffer = cmp->recoverSfc->buf32 + (bbox.min.y * cmp->recoverSfc->stride + bbox.min.x);
auto sbuffer = cmp->image.buf32 + (bbox.min.y * cmp->image.stride + bbox.min.x);
for (size_t y = 0; y < h; ++y) {
auto dst = dbuffer;
auto src = sbuffer;
for (size_t x = 0; x < w; ++x, ++dst, ++src) {
auto tmp = rasterUnpremultiply(*src);
auto val = INTERPOLATE(INTERPOLATE(black, white, luma((uint8_t*)&tmp)), tmp, params->intensity);
*dst = INTERPOLATE(val, *dst, MULTIPLY(opacity, A(tmp)));
}
dbuffer += cmp->image.stride;
sbuffer += cmp->recoverSfc->stride;
}
cmp->valid = true; //no need the subsequent composition
} else {
auto dbuffer = cmp->image.buf32 + (bbox.min.y * cmp->image.stride + bbox.min.x);
for (size_t y = 0; y < h; ++y) {
auto dst = dbuffer;
for (size_t x = 0; x < w; ++x, ++dst) {
auto tmp = rasterUnpremultiply(*dst);
auto val = INTERPOLATE(INTERPOLATE(black, white, luma((uint8_t*)&tmp)), tmp, params->intensity);
*dst = ALPHA_BLEND(val, A(tmp));
}
dbuffer += cmp->image.stride;
}
}
return true;
}
/************************************************************************/
/* Tritone Implementation */
/************************************************************************/
static uint32_t _trintone(uint32_t s, uint32_t m, uint32_t h, int l)
{
/* Tritone Formula:
if (L < 0.5) { (1 - 2L) * Shadow + 2L * Midtone }
else { (1 - 2(L - 0.5)) * Midtone + (2(L - 0.5)) * Highlight }
Where the L is Luminance. */
if (l < 128) {
auto a = std::min(l * 2, 255);
return ALPHA_BLEND(s, 255 - a) + ALPHA_BLEND(m, a);
} else {
auto a = 2 * std::max(0, l - 128);
return ALPHA_BLEND(m, 255 - a) + ALPHA_BLEND(h, a);
}
}
bool effectTritonePrepare(RenderEffectTritone* params)
{
params->valid = true;
return true;
}
bool effectTritone(SwCompositor* cmp, const RenderEffectTritone* params, bool direct)
{
auto& bbox = cmp->bbox;
auto w = size_t(bbox.max.x - bbox.min.x);
auto h = size_t(bbox.max.y - bbox.min.y);
auto shadow = cmp->recoverSfc->join(params->shadow[0], params->shadow[1], params->shadow[2], 255);
auto midtone = cmp->recoverSfc->join(params->midtone[0], params->midtone[1], params->midtone[2], 255);
auto highlight = cmp->recoverSfc->join(params->highlight[0], params->highlight[1], params->highlight[2], 255);
auto opacity = cmp->opacity;
auto luma = cmp->recoverSfc->alphas[2]; //luma function
TVGLOG("SW_ENGINE", "Tritone region(%ld, %ld, %ld, %ld), param(%d %d %d, %d %d %d, %d %d %d)", bbox.min.x, bbox.min.y, bbox.max.x, bbox.max.y, params->shadow[0], params->shadow[1], params->shadow[2], params->midtone[0], params->midtone[1], params->midtone[2], params->highlight[0], params->highlight[1], params->highlight[2]);
if (direct) {
auto dbuffer = cmp->recoverSfc->buf32 + (bbox.min.y * cmp->recoverSfc->stride + bbox.min.x);
auto sbuffer = cmp->image.buf32 + (bbox.min.y * cmp->image.stride + bbox.min.x);
for (size_t y = 0; y < h; ++y) {
auto dst = dbuffer;
auto src = sbuffer;
for (size_t x = 0; x < w; ++x, ++dst, ++src) {
auto tmp = rasterUnpremultiply(*src);
*dst = INTERPOLATE(_trintone(shadow, midtone, highlight, luma((uint8_t*)&tmp)), *dst, MULTIPLY(opacity, A(tmp)));
}
dbuffer += cmp->image.stride;
sbuffer += cmp->recoverSfc->stride;
}
cmp->valid = true; //no need the subsequent composition
} else {
auto dbuffer = cmp->image.buf32 + (bbox.min.y * cmp->image.stride + bbox.min.x);
for (size_t y = 0; y < h; ++y) {
auto dst = dbuffer;
for (size_t x = 0; x < w; ++x, ++dst) {
auto tmp = rasterUnpremultiply(*dst);
*dst = ALPHA_BLEND(_trintone(shadow, midtone, highlight, luma((uint8_t*)&tmp)), A(tmp));
}
dbuffer += cmp->image.stride;
}
}
return true;
}

View File

@ -1667,6 +1667,20 @@ bool rasterClear(SwSurface* surface, uint32_t x, uint32_t y, uint32_t w, uint32_
} }
uint32_t rasterUnpremultiply(uint32_t data)
{
uint8_t a = data >> 24;
if (a == 255 || a == 0) return data;
uint16_t r = ((data >> 8) & 0xff00) / a;
uint16_t g = ((data) & 0xff00) / a;
uint16_t b = ((data << 8) & 0xff00) / a;
if (r > 0xff) r = 0xff;
if (g > 0xff) g = 0xff;
if (b > 0xff) b = 0xff;
return (a << 24) | (r << 16) | (g << 8) | (b);
}
void rasterUnpremultiply(RenderSurface* surface) void rasterUnpremultiply(RenderSurface* surface)
{ {
if (surface->channelSize != sizeof(uint32_t)) return; if (surface->channelSize != sizeof(uint32_t)) return;
@ -1677,20 +1691,7 @@ void rasterUnpremultiply(RenderSurface* surface)
for (uint32_t y = 0; y < surface->h; y++) { for (uint32_t y = 0; y < surface->h; y++) {
auto buffer = surface->buf32 + surface->stride * y; auto buffer = surface->buf32 + surface->stride * y;
for (uint32_t x = 0; x < surface->w; ++x) { for (uint32_t x = 0; x < surface->w; ++x) {
uint8_t a = buffer[x] >> 24; buffer[x] = rasterUnpremultiply(buffer[x]);
if (a == 255) {
continue;
} else if (a == 0) {
buffer[x] = 0x00ffffff;
} else {
uint16_t r = ((buffer[x] >> 8) & 0xff00) / a;
uint16_t g = ((buffer[x]) & 0xff00) / a;
uint16_t b = ((buffer[x] << 8) & 0xff00) / a;
if (r > 0xff) r = 0xff;
if (g > 0xff) g = 0xff;
if (b > 0xff) b = 0xff;
buffer[x] = (a << 24) | (r << 16) | (g << 8) | (b);
}
} }
} }
surface->premultiplied = false; surface->premultiplied = false;

View File

@ -103,11 +103,9 @@ struct SwShapeTask : SwTask
bool clip(SwRle* target) override bool clip(SwRle* target) override
{ {
if (shape.fastTrack) rleClip(target, &bbox); if (shape.fastTrack) return rleClip(target, &bbox);
else if (shape.rle) rleClip(target, shape.rle); else if (shape.rle) return rleClip(target, shape.rle);
else return false; return false;
return true;
} }
void run(unsigned tid) override void run(unsigned tid) override
@ -177,10 +175,8 @@ struct SwShapeTask : SwTask
//Clip Path //Clip Path
for (auto clip = clips.begin(); clip < clips.end(); ++clip) { for (auto clip = clips.begin(); clip < clips.end(); ++clip) {
auto clipper = static_cast<SwTask*>(*clip); auto clipper = static_cast<SwTask*>(*clip);
//Clip shape rle if (shape.rle && !clipper->clip(shape.rle)) goto err; //Clip shape rle
if (shape.rle && !clipper->clip(shape.rle)) goto err; if (shape.strokeRle && !clipper->clip(shape.strokeRle)) goto err; //Clip stroke rle
//Clip stroke rle
if (shape.strokeRle && !clipper->clip(shape.strokeRle)) goto err;
} }
bbox = renderRegion; //sync bbox = renderRegion; //sync
@ -546,15 +542,27 @@ const RenderSurface* SwRenderer::mainSurface()
} }
SwSurface* SwRenderer::request(int channelSize) SwSurface* SwRenderer::request(int channelSize, bool square)
{ {
SwSurface* cmp = nullptr; SwSurface* cmp = nullptr;
uint32_t w, h;
if (square) {
//Same Dimensional Size is demanded for the Post Processing Fast Flipping
w = h = std::max(surface->w, surface->h);
} else {
w = surface->w;
h = surface->h;
}
//Use cached data //Use cached data
for (auto p = compositors.begin(); p < compositors.end(); ++p) { for (auto p = compositors.begin(); p < compositors.end(); ++p) {
if ((*p)->compositor->valid && (*p)->compositor->image.channelSize == channelSize) { auto cur = *p;
cmp = *p; if (cur->compositor->valid && cur->compositor->image.channelSize == channelSize) {
break; if (w == cur->w && h == cur->h) {
cmp = *p;
break;
}
} }
} }
@ -563,15 +571,13 @@ SwSurface* SwRenderer::request(int channelSize)
//Inherits attributes from main surface //Inherits attributes from main surface
cmp = new SwSurface(surface); cmp = new SwSurface(surface);
cmp->compositor = new SwCompositor; cmp->compositor = new SwCompositor;
cmp->compositor->image.data = (pixel_t*)malloc(channelSize * surface->stride * surface->h); cmp->compositor->image.data = (pixel_t*)malloc(channelSize * w * h);
cmp->compositor->image.w = surface->w; cmp->w = cmp->compositor->image.w = w;
cmp->compositor->image.h = surface->h; cmp->h = cmp->compositor->image.h = h;
cmp->compositor->image.stride = surface->stride; cmp->compositor->image.stride = w;
cmp->compositor->image.direct = true; cmp->compositor->image.direct = true;
cmp->compositor->valid = true; cmp->compositor->valid = true;
cmp->channelSize = cmp->compositor->image.channelSize = channelSize; cmp->channelSize = cmp->compositor->image.channelSize = channelSize;
cmp->w = cmp->compositor->image.w;
cmp->h = cmp->compositor->image.h;
compositors.push(cmp); compositors.push(cmp);
} }
@ -583,7 +589,7 @@ SwSurface* SwRenderer::request(int channelSize)
} }
RenderCompositor* SwRenderer::target(const RenderRegion& region, ColorSpace cs) RenderCompositor* SwRenderer::target(const RenderRegion& region, ColorSpace cs, CompositionFlag flags)
{ {
auto x = region.x; auto x = region.x;
auto y = region.y; auto y = region.y;
@ -595,7 +601,7 @@ RenderCompositor* SwRenderer::target(const RenderRegion& region, ColorSpace cs)
//Out of boundary //Out of boundary
if (x >= sw || y >= sh || x + w < 0 || y + h < 0) return nullptr; if (x >= sw || y >= sh || x + w < 0 || y + h < 0) return nullptr;
auto cmp = request(CHANNEL_SIZE(cs)); auto cmp = request(CHANNEL_SIZE(cs), (flags & CompositionFlag::PostProcessing));
//Boundary Check //Boundary Check
if (x < 0) x = 0; if (x < 0) x = 0;
@ -630,12 +636,15 @@ bool SwRenderer::endComposite(RenderCompositor* cmp)
if (!cmp) return false; if (!cmp) return false;
auto p = static_cast<SwCompositor*>(cmp); auto p = static_cast<SwCompositor*>(cmp);
p->valid = true;
//Recover Context //Recover Context
surface = p->recoverSfc; surface = p->recoverSfc;
surface->compositor = p->recoverCmp; surface->compositor = p->recoverCmp;
//only invalid (currently used) surface can be composited
if (p->valid) return true;
p->valid = true;
//Default is alpha blending //Default is alpha blending
if (p->method == CompositeMethod::None) { if (p->method == CompositeMethod::None) {
Matrix m = {1, 0, 0, 0, 1, 0, 0, 0, 1}; Matrix m = {1, 0, 0, 0, 1, 0, 0, 0, 1};
@ -651,30 +660,47 @@ bool SwRenderer::prepare(RenderEffect* effect)
switch (effect->type) { switch (effect->type) {
case SceneEffect::GaussianBlur: return effectGaussianBlurPrepare(static_cast<RenderEffectGaussianBlur*>(effect)); case SceneEffect::GaussianBlur: return effectGaussianBlurPrepare(static_cast<RenderEffectGaussianBlur*>(effect));
case SceneEffect::DropShadow: return effectDropShadowPrepare(static_cast<RenderEffectDropShadow*>(effect)); case SceneEffect::DropShadow: return effectDropShadowPrepare(static_cast<RenderEffectDropShadow*>(effect));
case SceneEffect::Fill: return effectFillPrepare(static_cast<RenderEffectFill*>(effect));
case SceneEffect::Tint: return effectTintPrepare(static_cast<RenderEffectTint*>(effect));
case SceneEffect::Tritone: return effectTritonePrepare(static_cast<RenderEffectTritone*>(effect));
default: return false; default: return false;
} }
} }
bool SwRenderer::effect(RenderCompositor* cmp, const RenderEffect* effect, uint8_t opacity, bool direct) bool SwRenderer::effect(RenderCompositor* cmp, const RenderEffect* effect, bool direct)
{ {
if (effect->invalid) return false; if (!effect->valid) return false;
auto p = static_cast<SwCompositor*>(cmp); auto p = static_cast<SwCompositor*>(cmp);
if (p->image.channelSize != sizeof(uint32_t)) {
TVGERR("SW_ENGINE", "Not supported grayscale Gaussian Blur!");
return false;
}
switch (effect->type) { switch (effect->type) {
case SceneEffect::GaussianBlur: { case SceneEffect::GaussianBlur: {
return effectGaussianBlur(p, request(surface->channelSize), static_cast<const RenderEffectGaussianBlur*>(effect)); return effectGaussianBlur(p, request(surface->channelSize, true), static_cast<const RenderEffectGaussianBlur*>(effect));
} }
case SceneEffect::DropShadow: { case SceneEffect::DropShadow: {
auto cmp1 = request(surface->channelSize); auto cmp1 = request(surface->channelSize, true);
cmp1->compositor->valid = false; cmp1->compositor->valid = false;
auto cmp2 = request(surface->channelSize); auto cmp2 = request(surface->channelSize, true);
SwSurface* surfaces[] = {cmp1, cmp2}; SwSurface* surfaces[] = {cmp1, cmp2};
auto ret = effectDropShadow(p, surfaces, static_cast<const RenderEffectDropShadow*>(effect), opacity, direct); auto ret = effectDropShadow(p, surfaces, static_cast<const RenderEffectDropShadow*>(effect), direct);
cmp1->compositor->valid = true; cmp1->compositor->valid = true;
return ret; return ret;
} }
case SceneEffect::Fill: {
return effectFill(p, static_cast<const RenderEffectFill*>(effect), direct);
}
case SceneEffect::Tint: {
return effectTint(p, static_cast<const RenderEffectTint*>(effect), direct);
}
case SceneEffect::Tritone: {
return effectTritone(p, static_cast<const RenderEffectTritone*>(effect), direct);
}
default: return false; default: return false;
} }
} }

View File

@ -55,13 +55,13 @@ public:
bool target(pixel_t* data, uint32_t stride, uint32_t w, uint32_t h, ColorSpace cs); bool target(pixel_t* data, uint32_t stride, uint32_t w, uint32_t h, ColorSpace cs);
bool mempool(bool shared); bool mempool(bool shared);
RenderCompositor* target(const RenderRegion& region, ColorSpace cs) override; RenderCompositor* target(const RenderRegion& region, ColorSpace cs, CompositionFlag flags) override;
bool beginComposite(RenderCompositor* cmp, CompositeMethod method, uint8_t opacity) override; bool beginComposite(RenderCompositor* cmp, CompositeMethod method, uint8_t opacity) override;
bool endComposite(RenderCompositor* cmp) override; bool endComposite(RenderCompositor* cmp) override;
void clearCompositors(); void clearCompositors();
bool prepare(RenderEffect* effect) override; bool prepare(RenderEffect* effect) override;
bool effect(RenderCompositor* cmp, const RenderEffect* effect, uint8_t opacity, bool direct) override; bool effect(RenderCompositor* cmp, const RenderEffect* effect, bool direct) override;
static SwRenderer* gen(); static SwRenderer* gen();
static bool init(uint32_t threads); static bool init(uint32_t threads);
@ -79,7 +79,7 @@ private:
SwRenderer(); SwRenderer();
~SwRenderer(); ~SwRenderer();
SwSurface* request(int channelSize); SwSurface* request(int channelSize, bool square);
RenderData prepareCommon(SwTask* task, const Matrix& transform, const Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags); RenderData prepareCommon(SwTask* task, const Matrix& transform, const Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags);
}; };

View File

@ -188,7 +188,6 @@
* http://www.freetype.org * http://www.freetype.org
*/ */
#include <setjmp.h>
#include <limits.h> #include <limits.h>
#include <memory.h> #include <memory.h>
#include "tvgSwCommon.h" #include "tvgSwCommon.h"
@ -243,8 +242,6 @@ struct RleWorker
int bandSize; int bandSize;
int bandShoot; int bandShoot;
jmp_buf jmpBuf;
void* buffer; void* buffer;
long bufferSize; long bufferSize;
@ -359,7 +356,7 @@ static void _horizLine(RleWorker& rw, SwCoord x, SwCoord y, SwCoord area, SwCoor
rle->spans = static_cast<SwSpan*>(realloc(rle->spans, rle->alloc * sizeof(SwSpan))); rle->spans = static_cast<SwSpan*>(realloc(rle->spans, rle->alloc * sizeof(SwSpan)));
} }
} }
//Clip x range //Clip x range
SwCoord xOver = 0; SwCoord xOver = 0;
if (x + aCount >= rw.cellMax.x) xOver -= (x + aCount - rw.cellMax.x); if (x + aCount >= rw.cellMax.x) xOver -= (x + aCount - rw.cellMax.x);
@ -418,7 +415,7 @@ static Cell* _findCell(RleWorker& rw)
pcell = &cell->next; pcell = &cell->next;
} }
if (rw.cellsCnt >= rw.maxCells) longjmp(rw.jmpBuf, 1); if (rw.cellsCnt >= rw.maxCells) return nullptr;
auto cell = rw.cells + rw.cellsCnt++; auto cell = rw.cells + rw.cellsCnt++;
cell->x = x; cell->x = x;
@ -431,17 +428,22 @@ static Cell* _findCell(RleWorker& rw)
} }
static void _recordCell(RleWorker& rw) static bool _recordCell(RleWorker& rw)
{ {
if (rw.area | rw.cover) { if (rw.area | rw.cover) {
auto cell = _findCell(rw); auto cell = _findCell(rw);
if (cell == nullptr) return false;
cell->area += rw.area; cell->area += rw.area;
cell->cover += rw.cover; cell->cover += rw.cover;
} }
return true;
} }
static void _setCell(RleWorker& rw, SwPoint pos) static bool _setCell(RleWorker& rw, SwPoint pos)
{ {
/* Move the cell pointer to a new position. We set the `invalid' */ /* Move the cell pointer to a new position. We set the `invalid' */
/* flag to indicate that the cell isn't part of those we're interested */ /* flag to indicate that the cell isn't part of those we're interested */
@ -458,22 +460,26 @@ static void _setCell(RleWorker& rw, SwPoint pos)
pos.x -= rw.cellMin.x; pos.x -= rw.cellMin.x;
pos.y -= rw.cellMin.y; pos.y -= rw.cellMin.y;
if (pos.x > rw.cellMax.x) pos.x = rw.cellMax.x; //exceptions
if (pos.x < 0) pos.x = -1;
else if (pos.x > rw.cellMax.x) pos.x = rw.cellMax.x;
//Are we moving to a different cell? //Are we moving to a different cell?
if (pos != rw.cellPos) { if (pos != rw.cellPos) {
//Record the current one if it is valid //Record the current one if it is valid
if (!rw.invalid) _recordCell(rw); if (!rw.invalid) {
if (!_recordCell(rw)) return false;
}
rw.area = rw.cover = 0;
rw.cellPos = pos;
} }
rw.area = 0;
rw.cover = 0;
rw.cellPos = pos;
rw.invalid = ((unsigned)pos.y >= (unsigned)rw.cellYCnt || pos.x >= rw.cellXCnt); rw.invalid = ((unsigned)pos.y >= (unsigned)rw.cellYCnt || pos.x >= rw.cellXCnt);
return true;
} }
static void _startCell(RleWorker& rw, SwPoint pos) static bool _startCell(RleWorker& rw, SwPoint pos)
{ {
if (pos.x > rw.cellMax.x) pos.x = rw.cellMax.x; if (pos.x > rw.cellMax.x) pos.x = rw.cellMax.x;
if (pos.x < rw.cellMin.x) pos.x = rw.cellMin.x; if (pos.x < rw.cellMin.x) pos.x = rw.cellMin.x;
@ -483,23 +489,27 @@ static void _startCell(RleWorker& rw, SwPoint pos)
rw.cellPos = pos - rw.cellMin; rw.cellPos = pos - rw.cellMin;
rw.invalid = false; rw.invalid = false;
_setCell(rw, pos); return _setCell(rw, pos);
} }
static void _moveTo(RleWorker& rw, const SwPoint& to) static bool _moveTo(RleWorker& rw, const SwPoint& to)
{ {
//record current cell, if any */ //record current cell, if any */
if (!rw.invalid) _recordCell(rw); if (!rw.invalid) {
if (!_recordCell(rw)) return false;
}
//start to a new position //start to a new position
_startCell(rw, TRUNC(to)); if (!_startCell(rw, TRUNC(to))) return false;
rw.pos = to; rw.pos = to;
return true;
} }
static void _lineTo(RleWorker& rw, const SwPoint& to) static bool _lineTo(RleWorker& rw, const SwPoint& to)
{ {
#define SW_UDIV(a, b) \ #define SW_UDIV(a, b) \
static_cast<SwCoord>(((unsigned long)(a) * (unsigned long)(b)) >> \ static_cast<SwCoord>(((unsigned long)(a) * (unsigned long)(b)) >> \
@ -511,7 +521,7 @@ static void _lineTo(RleWorker& rw, const SwPoint& to)
//vertical clipping //vertical clipping
if ((e1.y >= rw.cellMax.y && e2.y >= rw.cellMax.y) || (e1.y < rw.cellMin.y && e2.y < rw.cellMin.y)) { if ((e1.y >= rw.cellMax.y && e2.y >= rw.cellMax.y) || (e1.y < rw.cellMin.y && e2.y < rw.cellMin.y)) {
rw.pos = to; rw.pos = to;
return; return true;
} }
auto line = rw.lineStack; auto line = rw.lineStack;
@ -539,7 +549,7 @@ static void _lineTo(RleWorker& rw, const SwPoint& to)
//any horizontal line //any horizontal line
} else if (diff.y == 0) { } else if (diff.y == 0) {
e1.x = e2.x; e1.x = e2.x;
_setCell(rw, e1); if (!_setCell(rw, e1)) return false;
} else if (diff.x == 0) { } else if (diff.x == 0) {
//vertical line up //vertical line up
if (diff.y > 0) { if (diff.y > 0) {
@ -549,7 +559,7 @@ static void _lineTo(RleWorker& rw, const SwPoint& to)
rw.area += (f2.y - f1.y) * f1.x * 2; rw.area += (f2.y - f1.y) * f1.x * 2;
f1.y = 0; f1.y = 0;
++e1.y; ++e1.y;
_setCell(rw, e1); if (!_setCell(rw, e1)) return false;
} while(e1.y != e2.y); } while(e1.y != e2.y);
//vertical line down //vertical line down
} else { } else {
@ -559,7 +569,7 @@ static void _lineTo(RleWorker& rw, const SwPoint& to)
rw.area += (f2.y - f1.y) * f1.x * 2; rw.area += (f2.y - f1.y) * f1.x * 2;
f1.y = ONE_PIXEL; f1.y = ONE_PIXEL;
--e1.y; --e1.y;
_setCell(rw, e1); if (!_setCell(rw, e1)) return false;
} while(e1.y != e2.y); } while(e1.y != e2.y);
} }
//any other line //any other line
@ -612,7 +622,7 @@ static void _lineTo(RleWorker& rw, const SwPoint& to)
--e1.y; --e1.y;
} }
_setCell(rw, e1); if (!_setCell(rw, e1)) return false;
} while(e1 != e2); } while(e1 != e2);
} }
@ -622,12 +632,12 @@ static void _lineTo(RleWorker& rw, const SwPoint& to)
rw.area += (f2.y - f1.y) * (f1.x + f2.x); rw.area += (f2.y - f1.y) * (f1.x + f2.x);
rw.pos = line[0]; rw.pos = line[0];
if (line-- == rw.lineStack) return; if (line-- == rw.lineStack) return true;
} }
} }
static void _cubicTo(RleWorker& rw, const SwPoint& ctrl1, const SwPoint& ctrl2, const SwPoint& to) static bool _cubicTo(RleWorker& rw, const SwPoint& ctrl1, const SwPoint& ctrl2, const SwPoint& to)
{ {
auto arc = rw.bezStack; auto arc = rw.bezStack;
arc[0] = to; arc[0] = to;
@ -691,14 +701,14 @@ static void _cubicTo(RleWorker& rw, const SwPoint& ctrl1, const SwPoint& ctrl2,
continue; continue;
draw: draw:
_lineTo(rw, arc[0]); if (!_lineTo(rw, arc[0])) return false;
if (arc == rw.bezStack) return; if (arc == rw.bezStack) return true;
arc -= 3; arc -= 3;
} }
} }
static void _decomposeOutline(RleWorker& rw) static bool _decomposeOutline(RleWorker& rw)
{ {
auto outline = rw.outline; auto outline = rw.outline;
auto first = 0; //index of first point in contour auto first = 0; //index of first point in contour
@ -711,38 +721,43 @@ static void _decomposeOutline(RleWorker& rw)
auto types = outline->types.data + first; auto types = outline->types.data + first;
++types; ++types;
_moveTo(rw, UPSCALE(outline->pts[first])); if (!_moveTo(rw, UPSCALE(outline->pts[first]))) return false;
while (pt < limit) { while (pt < limit) {
//emit a single line_to //emit a single line_to
if (types[0] == SW_CURVE_TYPE_POINT) { if (types[0] == SW_CURVE_TYPE_POINT) {
++pt; ++pt;
++types; ++types;
_lineTo(rw, UPSCALE(*pt)); if (!_lineTo(rw, UPSCALE(*pt))) return false;
//types cubic //types cubic
} else { } else {
pt += 3; pt += 3;
types += 3; types += 3;
if (pt <= limit) _cubicTo(rw, UPSCALE(pt[-2]), UPSCALE(pt[-1]), UPSCALE(pt[0])); if (pt <= limit) {
else if (pt - 1 == limit) _cubicTo(rw, UPSCALE(pt[-2]), UPSCALE(pt[-1]), start); if (!_cubicTo(rw, UPSCALE(pt[-2]), UPSCALE(pt[-1]), UPSCALE(pt[0]))) return false;
}
else if (pt - 1 == limit) {
if (!_cubicTo(rw, UPSCALE(pt[-2]), UPSCALE(pt[-1]), start)) return false;
}
else goto close; else goto close;
} }
} }
close: close:
_lineTo(rw, start); if (!_lineTo(rw, start)) return false;
first = last + 1; first = last + 1;
} }
return true;
} }
static int _genRle(RleWorker& rw) static int _genRle(RleWorker& rw)
{ {
if (setjmp(rw.jmpBuf) == 0) { if (!_decomposeOutline(rw)) return -1;
_decomposeOutline(rw); if (!rw.invalid) {
if (!rw.invalid) _recordCell(rw); if (!_recordCell(rw)) return -1;
return 0;
} }
return -1; //lack of cell memory return 0;
} }
@ -1005,9 +1020,9 @@ void rleFree(SwRle* rle)
} }
void rleClip(SwRle *rle, const SwRle *clip) bool rleClip(SwRle *rle, const SwRle *clip)
{ {
if (rle->size == 0 || clip->size == 0) return; if (rle->size == 0 || clip->size == 0) return false;
auto spanCnt = rle->size > clip->size ? rle->size : clip->size; auto spanCnt = rle->size > clip->size ? rle->size : clip->size;
auto spans = static_cast<SwSpan*>(malloc(sizeof(SwSpan) * (spanCnt))); auto spans = static_cast<SwSpan*>(malloc(sizeof(SwSpan) * (spanCnt)));
auto spansEnd = _intersectSpansRegion(clip, rle, spans, spanCnt); auto spansEnd = _intersectSpansRegion(clip, rle, spans, spanCnt);
@ -1015,16 +1030,21 @@ void rleClip(SwRle *rle, const SwRle *clip)
_replaceClipSpan(rle, spans, spansEnd - spans); _replaceClipSpan(rle, spans, spansEnd - spans);
TVGLOG("SW_ENGINE", "Using Path Clipping!"); TVGLOG("SW_ENGINE", "Using Path Clipping!");
return true;
} }
void rleClip(SwRle *rle, const SwBBox* clip) bool rleClip(SwRle *rle, const SwBBox* clip)
{ {
if (rle->size == 0) return; if (rle->size == 0) return false;
auto spans = static_cast<SwSpan*>(malloc(sizeof(SwSpan) * (rle->size))); auto spans = static_cast<SwSpan*>(malloc(sizeof(SwSpan) * (rle->size)));
auto spansEnd = _intersectSpansRect(clip, rle, spans, rle->size); auto spansEnd = _intersectSpansRect(clip, rle, spans, rle->size);
_replaceClipSpan(rle, spans, spansEnd - spans); _replaceClipSpan(rle, spans, spansEnd - spans);
TVGLOG("SW_ENGINE", "Using Box Clipping!"); TVGLOG("SW_ENGINE", "Using Box Clipping!");
return true;
} }

View File

@ -374,8 +374,12 @@ static void _lineTo(SwStroke& stroke, const SwPoint& to)
{ {
auto delta = to - stroke.center; auto delta = to - stroke.center;
//a zero-length lineto is a no-op; avoid creating a spurious corner //a zero-length lineto is a no-op
if (delta.zero()) return; if (delta.zero()) {
//round and square caps are expected to be drawn as a dot even for zero-length lines
if (stroke.firstPt && stroke.cap != StrokeCap::Butt) _firstSubPath(stroke, 0, 0);
return;
}
/* The lineLength is used to determine the intersection of strokes outlines. /* The lineLength is used to determine the intersection of strokes outlines.
The scale needs to be reverted since the stroke width has not been scaled. The scale needs to be reverted since the stroke width has not been scaled.
@ -454,6 +458,9 @@ static void _cubicTo(SwStroke& stroke, const SwPoint& ctrl1, const SwPoint& ctrl
//ignoreable size //ignoreable size
if (valid < 0 && arc == bezStack) { if (valid < 0 && arc == bezStack) {
stroke.center = to; stroke.center = to;
//round and square caps are expected to be drawn as a dot even for zero-length lines
if (stroke.firstPt && stroke.cap != StrokeCap::Butt) _firstSubPath(stroke, 0, 0);
return; return;
} }

View File

@ -64,17 +64,17 @@ TVG_DEPRECATED unique_ptr<Picture> Accessor::set(unique_ptr<Picture> picture, fu
} }
Result Accessor::set(const Picture* picture, function<bool(const Paint* paint, void* data)> func, void* data) noexcept Result Accessor::set(Paint* paint, function<bool(const Paint* paint, void* data)> func, void* data) noexcept
{ {
if (!picture || !func) return Result::InvalidArguments; if (!paint || !func) return Result::InvalidArguments;
//Use the Preorder Tree-Search //Use the Preorder Tree-Searc
//Root //Root
if (!func(picture, data)) return Result::Success; if (!func(paint, data)) return Result::Success;
//Children //Children
if (auto it = IteratorAccessor::iterator(picture)) { if (auto it = IteratorAccessor::iterator(paint)) {
accessChildren(it, func, data); accessChildren(it, func, data);
delete(it); delete(it);
} }

View File

@ -172,6 +172,7 @@ static LoadModule* _find(FileType type)
} }
#ifdef THORVG_FILE_IO_SUPPORT
static LoadModule* _findByPath(const string& path) static LoadModule* _findByPath(const string& path)
{ {
auto ext = path.substr(path.find_last_of(".") + 1); auto ext = path.substr(path.find_last_of(".") + 1);
@ -185,6 +186,7 @@ static LoadModule* _findByPath(const string& path)
if (!ext.compare("otf") || !ext.compare("otc")) return _find(FileType::Ttf); if (!ext.compare("otf") || !ext.compare("otc")) return _find(FileType::Ttf);
return nullptr; return nullptr;
} }
#endif
static FileType _convert(const string& mimeType) static FileType _convert(const string& mimeType)
@ -292,6 +294,7 @@ bool LoaderMgr::retrieve(LoadModule* loader)
LoadModule* LoaderMgr::loader(const string& path, bool* invalid) LoadModule* LoaderMgr::loader(const string& path, bool* invalid)
{ {
#ifdef THORVG_FILE_IO_SUPPORT
*invalid = false; *invalid = false;
//TODO: svg & lottie is not sharable. //TODO: svg & lottie is not sharable.
@ -335,6 +338,7 @@ LoadModule* LoaderMgr::loader(const string& path, bool* invalid)
} }
} }
*invalid = true; *invalid = true;
#endif
return nullptr; return nullptr;
} }

View File

@ -216,7 +216,7 @@ bool Paint::Impl::render(RenderMethod* renderer)
if (MASK_REGION_MERGING(compData->method)) region.add(P(compData->target)->bounds(renderer)); if (MASK_REGION_MERGING(compData->method)) region.add(P(compData->target)->bounds(renderer));
if (region.w == 0 || region.h == 0) return true; if (region.w == 0 || region.h == 0) return true;
cmp = renderer->target(region, COMPOSITE_TO_COLORSPACE(renderer, compData->method)); cmp = renderer->target(region, COMPOSITE_TO_COLORSPACE(renderer, compData->method), CompositionFlag::Masking);
if (renderer->beginComposite(cmp, CompositeMethod::None, 255)) { if (renderer->beginComposite(cmp, CompositeMethod::None, 255)) {
compData->target->pImpl->render(renderer); compData->target->pImpl->render(renderer);
} }
@ -374,7 +374,7 @@ void Paint::Impl::reset()
blendMethod = BlendMethod::Normal; blendMethod = BlendMethod::Normal;
renderFlag = RenderUpdateFlag::None; renderFlag = RenderUpdateFlag::None;
ctxFlag = ContextFlag::Invalid; ctxFlag = ContextFlag::Default;
opacity = 255; opacity = 255;
paint->id = 0; paint->id = 0;
} }

View File

@ -28,7 +28,7 @@
namespace tvg namespace tvg
{ {
enum ContextFlag : uint8_t {Invalid = 0, FastTrack = 1}; enum ContextFlag : uint8_t {Default = 0, FastTrack = 1};
struct Iterator struct Iterator
{ {

View File

@ -56,18 +56,20 @@ RenderUpdateFlag Picture::Impl::load()
} }
bool Picture::Impl::needComposition(uint8_t opacity) void Picture::Impl::queryComposition(uint8_t opacity)
{ {
cFlag = CompositionFlag::Invalid;
//In this case, paint(scene) would try composition itself. //In this case, paint(scene) would try composition itself.
if (opacity < 255) return false; if (opacity < 255) return;
//Composition test //Composition test
const Paint* target; const Paint* target;
auto method = picture->composite(&target); auto method = picture->composite(&target);
if (!target || method == tvg::CompositeMethod::ClipPath) return false; if (!target || method == tvg::CompositeMethod::ClipPath) return;
if (target->pImpl->opacity == 255 || target->pImpl->opacity == 0) return false; if (target->pImpl->opacity == 255 || target->pImpl->opacity == 0) return;
return true; cFlag = CompositionFlag::Opacity;
} }
@ -79,8 +81,8 @@ bool Picture::Impl::render(RenderMethod* renderer)
if (surface) return renderer->renderImage(rd); if (surface) return renderer->renderImage(rd);
else if (paint) { else if (paint) {
RenderCompositor* cmp = nullptr; RenderCompositor* cmp = nullptr;
if (needComp) { if (cFlag) {
cmp = renderer->target(bounds(renderer), renderer->colorSpace()); cmp = renderer->target(bounds(renderer), renderer->colorSpace(), static_cast<CompositionFlag>(cFlag));
renderer->beginComposite(cmp, CompositeMethod::None, 255); renderer->beginComposite(cmp, CompositeMethod::None, 255);
} }
ret = paint->pImpl->render(renderer); ret = paint->pImpl->render(renderer);
@ -164,9 +166,14 @@ Type Picture::type() const noexcept
Result Picture::load(const std::string& path) noexcept Result Picture::load(const std::string& path) noexcept
{ {
#ifdef THORVG_FILE_IO_SUPPORT
if (path.empty()) return Result::InvalidArguments; if (path.empty()) return Result::InvalidArguments;
return pImpl->load(path); return pImpl->load(path);
#else
TVGLOG("RENDERER", "FILE IO is disabled!");
return Result::NonSupport;
#endif
} }

View File

@ -64,10 +64,10 @@ struct Picture::Impl
RenderData rd = nullptr; //engine data RenderData rd = nullptr; //engine data
float w = 0, h = 0; float w = 0, h = 0;
Picture* picture = nullptr; Picture* picture = nullptr;
uint8_t cFlag = CompositionFlag::Invalid;
bool resizing = false; bool resizing = false;
bool needComp = false; //need composition
bool needComposition(uint8_t opacity); void queryComposition(uint8_t opacity);
bool render(RenderMethod* renderer); bool render(RenderMethod* renderer);
bool size(float w, float h); bool size(float w, float h);
RenderRegion bounds(RenderMethod* renderer); RenderRegion bounds(RenderMethod* renderer);
@ -107,7 +107,7 @@ struct Picture::Impl
loader->resize(paint, w, h); loader->resize(paint, w, h);
resizing = false; resizing = false;
} }
needComp = needComposition(opacity) ? true : false; queryComposition(opacity);
rd = paint->pImpl->update(renderer, transform, clips, opacity, flag, false); rd = paint->pImpl->update(renderer, transform, clips, opacity, flag, false);
} }
return rd; return rd;

View File

@ -36,6 +36,7 @@ using RenderData = void*;
using pixel_t = uint32_t; using pixel_t = uint32_t;
enum RenderUpdateFlag : uint8_t {None = 0, Path = 1, Color = 2, Gradient = 4, Stroke = 8, Transform = 16, Image = 32, GradientStroke = 64, Blend = 128, All = 255}; enum RenderUpdateFlag : uint8_t {None = 0, Path = 1, Color = 2, Gradient = 4, Stroke = 8, Transform = 16, Image = 32, GradientStroke = 64, Blend = 128, All = 255};
enum CompositionFlag : uint8_t {Invalid = 0, Opacity = 1, Blending = 2, Masking = 4, PostProcessing = 8}; //Composition Purpose
//TODO: Move this in public header unifying with SwCanvas::Colorspace //TODO: Move this in public header unifying with SwCanvas::Colorspace
enum ColorSpace : uint8_t enum ColorSpace : uint8_t
@ -137,6 +138,7 @@ struct RenderStroke
dashPattern = nullptr; dashPattern = nullptr;
} }
dashCnt = rhs.dashCnt; dashCnt = rhs.dashCnt;
dashOffset = rhs.dashOffset;
miterlimit = rhs.miterlimit; miterlimit = rhs.miterlimit;
cap = rhs.cap; cap = rhs.cap;
join = rhs.join; join = rhs.join;
@ -268,7 +270,7 @@ struct RenderEffect
RenderData rd = nullptr; RenderData rd = nullptr;
RenderRegion extend = {0, 0, 0, 0}; RenderRegion extend = {0, 0, 0, 0};
SceneEffect type; SceneEffect type;
bool invalid = false; bool valid = false;
virtual ~RenderEffect() virtual ~RenderEffect()
{ {
@ -309,7 +311,7 @@ struct RenderEffectDropShadow : RenderEffect
inst->color[0] = va_arg(args, int); inst->color[0] = va_arg(args, int);
inst->color[1] = va_arg(args, int); inst->color[1] = va_arg(args, int);
inst->color[2] = va_arg(args, int); inst->color[2] = va_arg(args, int);
inst->color[3] = std::min(va_arg(args, int), 255); inst->color[3] = va_arg(args, int);
inst->angle = (float) va_arg(args, double); inst->angle = (float) va_arg(args, double);
inst->distance = (float) va_arg(args, double); inst->distance = (float) va_arg(args, double);
inst->sigma = std::max((float) va_arg(args, double), 0.0f); inst->sigma = std::max((float) va_arg(args, double), 0.0f);
@ -319,6 +321,66 @@ struct RenderEffectDropShadow : RenderEffect
} }
}; };
struct RenderEffectFill : RenderEffect
{
uint8_t color[4]; //rgba
static RenderEffectFill* gen(va_list& args)
{
auto inst = new RenderEffectFill;
inst->color[0] = va_arg(args, int);
inst->color[1] = va_arg(args, int);
inst->color[2] = va_arg(args, int);
inst->color[3] = va_arg(args, int);
inst->type = SceneEffect::Fill;
return inst;
}
};
struct RenderEffectTint : RenderEffect
{
uint8_t black[3]; //rgb
uint8_t white[3]; //rgb
uint8_t intensity; //0 - 255
static RenderEffectTint* gen(va_list& args)
{
auto inst = new RenderEffectTint;
inst->black[0] = va_arg(args, int);
inst->black[1] = va_arg(args, int);
inst->black[2] = va_arg(args, int);
inst->white[0] = va_arg(args, int);
inst->white[1] = va_arg(args, int);
inst->white[2] = va_arg(args, int);
inst->intensity = (uint8_t)(va_arg(args, double) * 2.55f);
inst->type = SceneEffect::Tint;
return inst;
}
};
struct RenderEffectTritone : RenderEffect
{
uint8_t shadow[3]; //rgb
uint8_t midtone[3]; //rgb
uint8_t highlight[3]; //rgb
static RenderEffectTritone* gen(va_list& args)
{
auto inst = new RenderEffectTritone;
inst->shadow[0] = va_arg(args, int);
inst->shadow[1] = va_arg(args, int);
inst->shadow[2] = va_arg(args, int);
inst->midtone[0] = va_arg(args, int);
inst->midtone[1] = va_arg(args, int);
inst->midtone[2] = va_arg(args, int);
inst->highlight[0] = va_arg(args, int);
inst->highlight[1] = va_arg(args, int);
inst->highlight[2] = va_arg(args, int);
inst->type = SceneEffect::Tritone;
return inst;
}
};
class RenderMethod class RenderMethod
{ {
private: private:
@ -347,12 +409,12 @@ public:
virtual bool clear() = 0; virtual bool clear() = 0;
virtual bool sync() = 0; virtual bool sync() = 0;
virtual RenderCompositor* target(const RenderRegion& region, ColorSpace cs) = 0; virtual RenderCompositor* target(const RenderRegion& region, ColorSpace cs, CompositionFlag flags) = 0;
virtual bool beginComposite(RenderCompositor* cmp, CompositeMethod method, uint8_t opacity) = 0; virtual bool beginComposite(RenderCompositor* cmp, CompositeMethod method, uint8_t opacity) = 0;
virtual bool endComposite(RenderCompositor* cmp) = 0; virtual bool endComposite(RenderCompositor* cmp) = 0;
virtual bool prepare(RenderEffect* effect) = 0; virtual bool prepare(RenderEffect* effect) = 0;
virtual bool effect(RenderCompositor* cmp, const RenderEffect* effect, uint8_t opacity, bool direct) = 0; virtual bool effect(RenderCompositor* cmp, const RenderEffect* effect, bool direct) = 0;
}; };
static inline bool MASK_REGION_MERGING(CompositeMethod method) static inline bool MASK_REGION_MERGING(CompositeMethod method)

View File

@ -128,6 +128,18 @@ Result Scene::push(SceneEffect effect, ...) noexcept
re = RenderEffectDropShadow::gen(args); re = RenderEffectDropShadow::gen(args);
break; break;
} }
case SceneEffect::Fill: {
re = RenderEffectFill::gen(args);
break;
}
case SceneEffect::Tint: {
re = RenderEffectTint::gen(args);
break;
}
case SceneEffect::Tritone: {
re = RenderEffectTritone::gen(args);
break;
}
default: break; default: break;
} }

View File

@ -62,8 +62,8 @@ struct Scene::Impl
Scene* scene = nullptr; Scene* scene = nullptr;
RenderRegion vport = {0, 0, INT32_MAX, INT32_MAX}; RenderRegion vport = {0, 0, INT32_MAX, INT32_MAX};
Array<RenderEffect*>* effects = nullptr; Array<RenderEffect*>* effects = nullptr;
uint8_t compFlag = CompositionFlag::Invalid;
uint8_t opacity; //for composition uint8_t opacity; //for composition
bool needComp = false; //composite or not
Impl(Scene* s) : scene(s) Impl(Scene* s) : scene(s)
{ {
@ -82,36 +82,36 @@ struct Scene::Impl
} }
} }
bool needComposition(uint8_t opacity) uint8_t needComposition(uint8_t opacity)
{ {
if (opacity == 0 || paints.empty()) return false; compFlag = CompositionFlag::Invalid;
//post effects requires composition if (opacity == 0 || paints.empty()) return 0;
if (effects) return true;
//Masking may require composition (even if opacity == 255) //post effects, masking, blending may require composition
if (effects) compFlag |= CompositionFlag::PostProcessing;
auto compMethod = scene->composite(nullptr); auto compMethod = scene->composite(nullptr);
if (compMethod != CompositeMethod::None && compMethod != CompositeMethod::ClipPath) return true; if (compMethod != CompositeMethod::None && compMethod != CompositeMethod::ClipPath) compFlag |= CompositionFlag::Masking;
if (PP(scene)->blendMethod != BlendMethod::Normal) compFlag |= CompositionFlag::Blending;
//Blending may require composition (even if opacity == 255)
if (PP(scene)->blendMethod != BlendMethod::Normal) return true;
//Half translucent requires intermediate composition. //Half translucent requires intermediate composition.
if (opacity == 255) return false; if (opacity == 255) return compFlag;
//If scene has several children or only scene, it may require composition. //If scene has several children or only scene, it may require composition.
//OPTIMIZE: the bitmap type of the picture would not need the composition. //OPTIMIZE: the bitmap type of the picture would not need the composition.
//OPTIMIZE: a single paint of a scene would not need the composition. //OPTIMIZE: a single paint of a scene would not need the composition.
if (paints.size() == 1 && paints.front()->type() == Type::Shape) return false; if (paints.size() == 1 && paints.front()->type() == Type::Shape) return compFlag;
return true; compFlag |= CompositionFlag::Opacity;
return 1;
} }
RenderData update(RenderMethod* renderer, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flag, TVG_UNUSED bool clipper) RenderData update(RenderMethod* renderer, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flag, TVG_UNUSED bool clipper)
{ {
this->vport = renderer->viewport(); this->vport = renderer->viewport();
if ((needComp = needComposition(opacity))) { if (needComposition(opacity)) {
/* Overriding opacity value. If this scene is half-translucent, /* Overriding opacity value. If this scene is half-translucent,
It must do intermediate composition with that opacity value. */ It must do intermediate composition with that opacity value. */
this->opacity = opacity; this->opacity = opacity;
@ -131,8 +131,8 @@ struct Scene::Impl
renderer->blend(PP(scene)->blendMethod); renderer->blend(PP(scene)->blendMethod);
if (needComp) { if (compFlag) {
cmp = renderer->target(bounds(renderer), renderer->colorSpace()); cmp = renderer->target(bounds(renderer), renderer->colorSpace(), static_cast<CompositionFlag>(compFlag));
renderer->beginComposite(cmp, CompositeMethod::None, opacity); renderer->beginComposite(cmp, CompositeMethod::None, opacity);
} }
@ -143,9 +143,10 @@ struct Scene::Impl
if (cmp) { if (cmp) {
//Apply post effects if any. //Apply post effects if any.
if (effects) { if (effects) {
auto direct = effects->count == 1 ? true : false; //Notify the possiblity of the direct composition of the effect result to the origin surface.
auto direct = (effects->count == 1) & (compFlag == CompositionFlag::PostProcessing);
for (auto e = effects->begin(); e < effects->end(); ++e) { for (auto e = effects->begin(); e < effects->end(); ++e) {
renderer->effect(cmp, *e, opacity, direct); renderer->effect(cmp, *e, direct);
} }
} }
renderer->endComposite(cmp); renderer->endComposite(cmp);
@ -178,7 +179,7 @@ struct Scene::Impl
if (effects) { if (effects) {
for (auto e = effects->begin(); e < effects->end(); ++e) { for (auto e = effects->begin(); e < effects->end(); ++e) {
auto effect = *e; auto effect = *e;
if (effect->rd || renderer->prepare(effect)) { if (effect->valid || renderer->prepare(effect)) {
ex = std::min(ex, effect->extend.x); ex = std::min(ex, effect->extend.x);
ey = std::min(ey, effect->extend.y); ey = std::min(ey, effect->extend.y);
ew = std::max(ew, effect->extend.w); ew = std::max(ew, effect->extend.w);

View File

@ -66,7 +66,7 @@ Result Shape::reset() noexcept
pImpl->rs.path.cmds.clear(); pImpl->rs.path.cmds.clear();
pImpl->rs.path.pts.clear(); pImpl->rs.path.pts.clear();
pImpl->flag |= RenderUpdateFlag::Path; pImpl->rFlag |= RenderUpdateFlag::Path;
return Result::Success; return Result::Success;
} }
@ -93,7 +93,7 @@ Result Shape::appendPath(const PathCommand *cmds, uint32_t cmdCnt, const Point*
pImpl->grow(cmdCnt, ptsCnt); pImpl->grow(cmdCnt, ptsCnt);
pImpl->append(cmds, cmdCnt, pts, ptsCnt); pImpl->append(cmds, cmdCnt, pts, ptsCnt);
pImpl->flag |= RenderUpdateFlag::Path; pImpl->rFlag |= RenderUpdateFlag::Path;
return Result::Success; return Result::Success;
} }
@ -111,7 +111,7 @@ Result Shape::lineTo(float x, float y) noexcept
{ {
pImpl->lineTo(x, y); pImpl->lineTo(x, y);
pImpl->flag |= RenderUpdateFlag::Path; pImpl->rFlag |= RenderUpdateFlag::Path;
return Result::Success; return Result::Success;
} }
@ -121,7 +121,7 @@ Result Shape::cubicTo(float cx1, float cy1, float cx2, float cy2, float x, float
{ {
pImpl->cubicTo(cx1, cy1, cx2, cy2, x, y); pImpl->cubicTo(cx1, cy1, cx2, cy2, x, y);
pImpl->flag |= RenderUpdateFlag::Path; pImpl->rFlag |= RenderUpdateFlag::Path;
return Result::Success; return Result::Success;
} }
@ -131,7 +131,7 @@ Result Shape::close() noexcept
{ {
pImpl->close(); pImpl->close();
pImpl->flag |= RenderUpdateFlag::Path; pImpl->rFlag |= RenderUpdateFlag::Path;
return Result::Success; return Result::Success;
} }
@ -150,7 +150,7 @@ Result Shape::appendCircle(float cx, float cy, float rx, float ry) noexcept
pImpl->cubicTo(cx + rxKappa, cy - ry, cx + rx, cy - ryKappa, cx + rx, cy); pImpl->cubicTo(cx + rxKappa, cy - ry, cx + rx, cy - ryKappa, cx + rx, cy);
pImpl->close(); pImpl->close();
pImpl->flag |= RenderUpdateFlag::Path; pImpl->rFlag |= RenderUpdateFlag::Path;
return Result::Success; return Result::Success;
} }
@ -212,7 +212,7 @@ TVG_DEPRECATED Result Shape::appendArc(float cx, float cy, float radius, float s
if (pie) pImpl->close(); if (pie) pImpl->close();
pImpl->flag |= RenderUpdateFlag::Path; pImpl->rFlag |= RenderUpdateFlag::Path;
return Result::Success; return Result::Success;
} }
@ -252,7 +252,7 @@ Result Shape::appendRect(float x, float y, float w, float h, float rx, float ry)
pImpl->close(); pImpl->close();
} }
pImpl->flag |= RenderUpdateFlag::Path; pImpl->rFlag |= RenderUpdateFlag::Path;
return Result::Success; return Result::Success;
} }
@ -263,7 +263,7 @@ Result Shape::fill(uint8_t r, uint8_t g, uint8_t b, uint8_t a) noexcept
if (pImpl->rs.fill) { if (pImpl->rs.fill) {
delete(pImpl->rs.fill); delete(pImpl->rs.fill);
pImpl->rs.fill = nullptr; pImpl->rs.fill = nullptr;
pImpl->flag |= RenderUpdateFlag::Gradient; pImpl->rFlag |= RenderUpdateFlag::Gradient;
} }
if (r == pImpl->rs.color[0] && g == pImpl->rs.color[1] && b == pImpl->rs.color[2] && a == pImpl->rs.color[3]) return Result::Success; if (r == pImpl->rs.color[0] && g == pImpl->rs.color[1] && b == pImpl->rs.color[2] && a == pImpl->rs.color[3]) return Result::Success;
@ -272,7 +272,7 @@ Result Shape::fill(uint8_t r, uint8_t g, uint8_t b, uint8_t a) noexcept
pImpl->rs.color[1] = g; pImpl->rs.color[1] = g;
pImpl->rs.color[2] = b; pImpl->rs.color[2] = b;
pImpl->rs.color[3] = a; pImpl->rs.color[3] = a;
pImpl->flag |= RenderUpdateFlag::Color; pImpl->rFlag |= RenderUpdateFlag::Color;
return Result::Success; return Result::Success;
} }
@ -285,7 +285,7 @@ Result Shape::fill(unique_ptr<Fill> f) noexcept
if (pImpl->rs.fill && pImpl->rs.fill != p) delete(pImpl->rs.fill); if (pImpl->rs.fill && pImpl->rs.fill != p) delete(pImpl->rs.fill);
pImpl->rs.fill = p; pImpl->rs.fill = p;
pImpl->flag |= RenderUpdateFlag::Gradient; pImpl->rFlag |= RenderUpdateFlag::Gradient;
return Result::Success; return Result::Success;
} }

View File

@ -33,10 +33,9 @@ struct Shape::Impl
RenderShape rs; //shape data RenderShape rs; //shape data
RenderData rd = nullptr; //engine data RenderData rd = nullptr; //engine data
Shape* shape; Shape* shape;
uint8_t flag = RenderUpdateFlag::None; uint8_t rFlag = RenderUpdateFlag::None;
uint8_t cFlag = CompositionFlag::Invalid;
uint8_t opacity; //for composition uint8_t opacity; //for composition
bool needComp = false; //composite or not
Impl(Shape* s) : shape(s) Impl(Shape* s) : shape(s)
{ {
@ -57,8 +56,8 @@ struct Shape::Impl
renderer->blend(PP(shape)->blendMethod); renderer->blend(PP(shape)->blendMethod);
if (needComp) { if (cFlag) {
cmp = renderer->target(bounds(renderer), renderer->colorSpace()); cmp = renderer->target(bounds(renderer), renderer->colorSpace(), static_cast<CompositionFlag>(cFlag));
renderer->beginComposite(cmp, CompositeMethod::None, opacity); renderer->beginComposite(cmp, CompositeMethod::None, opacity);
} }
@ -69,6 +68,8 @@ struct Shape::Impl
bool needComposition(uint8_t opacity) bool needComposition(uint8_t opacity)
{ {
cFlag = CompositionFlag::Invalid;
if (opacity == 0) return false; if (opacity == 0) return false;
//Shape composition is only necessary when stroking & fill are valid. //Shape composition is only necessary when stroking & fill are valid.
@ -76,7 +77,10 @@ struct Shape::Impl
if (!rs.fill && rs.color[3] == 0) return false; if (!rs.fill && rs.color[3] == 0) return false;
//translucent fill & stroke //translucent fill & stroke
if (opacity < 255) return true; if (opacity < 255) {
cFlag = CompositionFlag::Opacity;
return true;
}
//Composition test //Composition test
const Paint* target; const Paint* target;
@ -97,22 +101,23 @@ struct Shape::Impl
} }
} }
cFlag = CompositionFlag::Masking;
return true; return true;
} }
RenderData update(RenderMethod* renderer, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper) RenderData update(RenderMethod* renderer, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper)
{ {
if (static_cast<RenderUpdateFlag>(pFlag | flag) == RenderUpdateFlag::None) return rd; if (static_cast<RenderUpdateFlag>(pFlag | rFlag) == RenderUpdateFlag::None) return rd;
if ((needComp = needComposition(opacity))) { if (needComposition(opacity)) {
/* Overriding opacity value. If this scene is half-translucent, /* Overriding opacity value. If this scene is half-translucent,
It must do intermediate composition with that opacity value. */ It must do intermediate composition with that opacity value. */
this->opacity = opacity; this->opacity = opacity;
opacity = 255; opacity = 255;
} }
rd = renderer->prepare(rs, rd, transform, clips, opacity, static_cast<RenderUpdateFlag>(pFlag | flag), clipper); rd = renderer->prepare(rs, rd, transform, clips, opacity, static_cast<RenderUpdateFlag>(pFlag | rFlag), clipper);
flag = RenderUpdateFlag::None; rFlag = RenderUpdateFlag::None;
return rd; return rd;
} }
@ -209,7 +214,7 @@ struct Shape::Impl
{ {
if (!rs.stroke) rs.stroke = new RenderStroke(); if (!rs.stroke) rs.stroke = new RenderStroke();
rs.stroke->width = width; rs.stroke->width = width;
flag |= RenderUpdateFlag::Stroke; rFlag |= RenderUpdateFlag::Stroke;
} }
void strokeTrim(float begin, float end, bool simultaneous) void strokeTrim(float begin, float end, bool simultaneous)
@ -225,7 +230,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.simultaneous = simultaneous; rs.stroke->trim.simultaneous = simultaneous;
flag |= RenderUpdateFlag::Stroke; rFlag |= RenderUpdateFlag::Stroke;
} }
bool strokeTrim(float* begin, float* end) bool strokeTrim(float* begin, float* end)
@ -245,21 +250,21 @@ struct Shape::Impl
{ {
if (!rs.stroke) rs.stroke = new RenderStroke(); if (!rs.stroke) rs.stroke = new RenderStroke();
rs.stroke->cap = cap; rs.stroke->cap = cap;
flag |= RenderUpdateFlag::Stroke; rFlag |= RenderUpdateFlag::Stroke;
} }
void strokeJoin(StrokeJoin join) void strokeJoin(StrokeJoin join)
{ {
if (!rs.stroke) rs.stroke = new RenderStroke(); if (!rs.stroke) rs.stroke = new RenderStroke();
rs.stroke->join = join; rs.stroke->join = join;
flag |= RenderUpdateFlag::Stroke; rFlag |= RenderUpdateFlag::Stroke;
} }
void strokeMiterlimit(float miterlimit) void strokeMiterlimit(float miterlimit)
{ {
if (!rs.stroke) rs.stroke = new RenderStroke(); if (!rs.stroke) rs.stroke = new RenderStroke();
rs.stroke->miterlimit = miterlimit; rs.stroke->miterlimit = miterlimit;
flag |= RenderUpdateFlag::Stroke; rFlag |= RenderUpdateFlag::Stroke;
} }
void strokeColor(uint8_t r, uint8_t g, uint8_t b, uint8_t a) void strokeColor(uint8_t r, uint8_t g, uint8_t b, uint8_t a)
@ -268,7 +273,7 @@ struct Shape::Impl
if (rs.stroke->fill) { if (rs.stroke->fill) {
delete(rs.stroke->fill); delete(rs.stroke->fill);
rs.stroke->fill = nullptr; rs.stroke->fill = nullptr;
flag |= RenderUpdateFlag::GradientStroke; rFlag |= RenderUpdateFlag::GradientStroke;
} }
rs.stroke->color[0] = r; rs.stroke->color[0] = r;
@ -276,7 +281,7 @@ struct Shape::Impl
rs.stroke->color[2] = b; rs.stroke->color[2] = b;
rs.stroke->color[3] = a; rs.stroke->color[3] = a;
flag |= RenderUpdateFlag::Stroke; rFlag |= RenderUpdateFlag::Stroke;
} }
Result strokeFill(unique_ptr<Fill> f) Result strokeFill(unique_ptr<Fill> f)
@ -289,8 +294,8 @@ struct Shape::Impl
rs.stroke->fill = p; rs.stroke->fill = p;
rs.stroke->color[3] = 0; rs.stroke->color[3] = 0;
flag |= RenderUpdateFlag::Stroke; rFlag |= RenderUpdateFlag::Stroke;
flag |= RenderUpdateFlag::GradientStroke; rFlag |= RenderUpdateFlag::GradientStroke;
return Result::Success; return Result::Success;
} }
@ -325,7 +330,7 @@ struct Shape::Impl
} }
rs.stroke->dashCnt = cnt; rs.stroke->dashCnt = cnt;
rs.stroke->dashOffset = offset; rs.stroke->dashOffset = offset;
flag |= RenderUpdateFlag::Stroke; rFlag |= RenderUpdateFlag::Stroke;
return Result::Success; return Result::Success;
} }
@ -340,12 +345,12 @@ struct Shape::Impl
{ {
if (!rs.stroke) rs.stroke = new RenderStroke(); if (!rs.stroke) rs.stroke = new RenderStroke();
rs.stroke->strokeFirst = strokeFirst; rs.stroke->strokeFirst = strokeFirst;
flag |= RenderUpdateFlag::Stroke; rFlag |= RenderUpdateFlag::Stroke;
} }
void update(RenderUpdateFlag flag) void update(RenderUpdateFlag flag)
{ {
this->flag |= flag; rFlag |= flag;
} }
Paint* duplicate(Paint* ret) Paint* duplicate(Paint* ret)
@ -358,7 +363,7 @@ struct Shape::Impl
delete(dup->rs.fill); delete(dup->rs.fill);
//Default Properties //Default Properties
dup->flag = RenderUpdateFlag::All; dup->rFlag = RenderUpdateFlag::All;
dup->rs.rule = rs.rule; dup->rs.rule = rs.rule;
//Color //Color

View File

@ -60,13 +60,17 @@ Result Text::font(const char* name, float size, const char* style) noexcept
Result Text::load(const std::string& path) noexcept Result Text::load(const std::string& path) noexcept
{ {
#ifdef THORVG_FILE_IO_SUPPORT
bool invalid; //invalid path bool invalid; //invalid path
if (!LoaderMgr::loader(path, &invalid)) { if (!LoaderMgr::loader(path, &invalid)) {
if (invalid) return Result::InvalidArguments; if (invalid) return Result::InvalidArguments;
else return Result::NonSupport; else return Result::NonSupport;
} }
return Result::Success; return Result::Success;
#else
TVGLOG("RENDERER", "FILE IO is disabled!");
return Result::NonSupport;
#endif
} }
@ -87,8 +91,13 @@ Result Text::load(const char* name, const char* data, uint32_t size, const strin
Result Text::unload(const std::string& path) noexcept Result Text::unload(const std::string& path) noexcept
{ {
#ifdef THORVG_FILE_IO_SUPPORT
if (LoaderMgr::retrieve(path)) return Result::Success; if (LoaderMgr::retrieve(path)) return Result::Success;
return Result::InsufficientCondition; return Result::InsufficientCondition;
#else
TVGLOG("RENDERER", "FILE IO is disabled!");
return Result::NonSupport;
#endif
} }

View File

@ -113,7 +113,7 @@ struct Text::Impl
//transform the gradient coordinates based on the final scaled font. //transform the gradient coordinates based on the final scaled font.
auto fill = P(shape)->rs.fill; auto fill = P(shape)->rs.fill;
if (fill && P(shape)->flag & RenderUpdateFlag::Gradient) { if (fill && P(shape)->rFlag & RenderUpdateFlag::Gradient) {
auto scale = 1.0f / loader->scale; auto scale = 1.0f / loader->scale;
if (fill->type() == Type::LinearGradient) { if (fill->type() == Type::LinearGradient) {
P(static_cast<LinearGradient*>(fill))->x1 *= scale; P(static_cast<LinearGradient*>(fill))->x1 *= scale;

View File

@ -1,6 +1,6 @@
#!/bin/bash -e #!/bin/bash -e
VERSION=0.15.5 VERSION=0.15.8
# Uncomment and set a git hash to use specific commit instead of tag. # Uncomment and set a git hash to use specific commit instead of tag.
#GIT_COMMIT= #GIT_COMMIT=