Merge pull request #101347 from akien-mga/thorvg-0.15.8

thorvg: Update to 0.15.8
This commit is contained in:
Thaddeus Crews 2025-01-16 17:17:44 -06:00
commit df7572f8bc
No known key found for this signature in database
GPG Key ID: 62181B86FE9E5D84
31 changed files with 545 additions and 192 deletions

View File

@ -935,7 +935,7 @@ instead of `miniz.h` as an external dependency.
## thorvg
- Upstream: https://github.com/thorvg/thorvg
- Version: 0.15.5 (89ab573acb253567975b2494069c7ee9abc9267c, 2024)
- Version: 0.15.8 (bd8c2fca7663a22fba7a339937cb60f2f6247a2e, 2025)
- License: MIT
Files extracted from upstream source:

View File

@ -22,7 +22,7 @@ Rafał Mikrut <mikrutrafal@protonmail.com>
Martin Capitanio <capnm@capitanio.org>
RuiwenTang <tangruiwen1989@gmail.com>
YouJin Lee <ol-of@naver.com>
SergeyLebedkin <sergii@lottiefiles.com>
Sergii Liebodkin <sergii@lottiefiles.com>
Jinny You <jinny@lottiefiles.com>
Nattu Adnan <nattu@reallynattu.com>
Gabor Kiss-Vamosi <kisvegabor@gmail.com>
@ -35,3 +35,6 @@ Thaddeus Crews <repiteo@outlook.com>
Josh Soref <jsoref@gmail.com>
Elliott Sales de Andrade <quantum.analyst@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:
//#define THORVG_LOG_ENABLED
#define THORVG_VERSION_STRING "0.15.5"
#define THORVG_VERSION_STRING "0.15.8"
#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.
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.
*
* @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] data Data passed to the @p func as its argument.
*
@ -2118,7 +2121,7 @@ public:
*
* @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.

View File

@ -67,6 +67,7 @@ bool PngLoader::open(const string& path)
bool PngLoader::open(const char* data, uint32_t size, bool copy)
{
#ifdef THORVG_FILE_IO_SUPPORT
image->opaque = NULL;
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;
return true;
#else
return false;
#endif
}

View File

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

View File

@ -70,6 +70,7 @@ JpgLoader::~JpgLoader()
bool JpgLoader::open(const string& path)
{
#ifdef THORVG_FILE_IO_SUPPORT
int width, height;
decoder = jpgdHeader(path.c_str(), &width, &height);
if (!decoder) return false;
@ -78,6 +79,9 @@ bool JpgLoader::open(const string& path)
h = static_cast<float>(height);
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)
{
#ifdef THORVG_FILE_IO_SUPPORT
clear();
ifstream f;
@ -3990,6 +3991,9 @@ bool SvgLoader::open(const string& path)
size = filePath.size();
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};
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);
}

View File

@ -547,8 +547,8 @@ SwRle* rleRender(const SwBBox* bbox);
void rleFree(SwRle* rle);
void rleReset(SwRle* rle);
void rleMerge(SwRle* rle, SwRle* clip1, SwRle* clip2);
void rleClip(SwRle* rle, const SwRle* clip);
void rleClip(SwRle* rle, const SwBBox* clip);
bool rleClip(SwRle* rle, const SwRle* clip);
bool rleClip(SwRle* rle, const SwBBox* clip);
SwMpool* mpoolInit(uint32_t threads);
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 rasterPremultiply(RenderSurface* surface);
bool rasterConvertCS(RenderSurface* surface, ColorSpace to);
uint32_t rasterUnpremultiply(uint32_t data);
bool effectGaussianBlur(SwCompositor* cmp, SwSurface* surface, const RenderEffectGaussianBlur* params);
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 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_ */

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 tmp = maskOp(src, *cmp, 0);
*dst = tmp + MULTIPLY(*dst, ~tmp);
det += deltaDet;
deltaDet += deltaDeltaDet;
b += deltaB;
}

View File

@ -150,7 +150,6 @@ bool effectGaussianBlurPrepare(RenderEffectGaussianBlur* params)
//invalid
if (extends == 0) {
params->invalid = true;
free(rd);
return false;
}
@ -158,6 +157,7 @@ bool effectGaussianBlurPrepare(RenderEffectGaussianBlur* params)
_gaussianExtendRegion(params->extend, extends, params->direction);
params->rd = rd;
params->valid = true;
return true;
}
@ -165,11 +165,6 @@ bool effectGaussianBlurPrepare(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 data = static_cast<SwGaussianBlur*>(params->rd);
auto& bbox = cmp->bbox;
@ -310,7 +305,6 @@ bool effectDropShadowPrepare(RenderEffectDropShadow* params)
//invalid
if (extends == 0 || params->color[3] == 0) {
params->invalid = true;
free(rd);
return false;
}
@ -327,6 +321,7 @@ bool effectDropShadowPrepare(RenderEffectDropShadow* params)
_dropShadowExtendRegion(params->extend, extends, rd->offset);
params->rd = rd;
params->valid = true;
return true;
}
@ -335,13 +330,8 @@ bool effectDropShadowPrepare(RenderEffectDropShadow* params)
//A quite same integration with effectGaussianBlur(). See it for detailed comments.
//surface[0]: the original image, to overlay it into 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.
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 front = cmp->image.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);
@ -408,3 +399,181 @@ bool effectDropShadow(SwCompositor* cmp, SwSurface* surface[2], const RenderEffe
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)
{
if (surface->channelSize != sizeof(uint32_t)) return;
@ -1677,20 +1691,7 @@ void rasterUnpremultiply(RenderSurface* surface)
for (uint32_t y = 0; y < surface->h; y++) {
auto buffer = surface->buf32 + surface->stride * y;
for (uint32_t x = 0; x < surface->w; ++x) {
uint8_t a = buffer[x] >> 24;
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);
}
buffer[x] = rasterUnpremultiply(buffer[x]);
}
}
surface->premultiplied = false;

View File

@ -103,11 +103,9 @@ struct SwShapeTask : SwTask
bool clip(SwRle* target) override
{
if (shape.fastTrack) rleClip(target, &bbox);
else if (shape.rle) rleClip(target, shape.rle);
else return false;
return true;
if (shape.fastTrack) return rleClip(target, &bbox);
else if (shape.rle) return rleClip(target, shape.rle);
return false;
}
void run(unsigned tid) override
@ -177,10 +175,8 @@ struct SwShapeTask : SwTask
//Clip Path
for (auto clip = clips.begin(); clip < clips.end(); ++clip) {
auto clipper = static_cast<SwTask*>(*clip);
//Clip shape rle
if (shape.rle && !clipper->clip(shape.rle)) goto err;
//Clip stroke rle
if (shape.strokeRle && !clipper->clip(shape.strokeRle)) goto err;
if (shape.rle && !clipper->clip(shape.rle)) goto err; //Clip shape rle
if (shape.strokeRle && !clipper->clip(shape.strokeRle)) goto err; //Clip stroke rle
}
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;
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
for (auto p = compositors.begin(); p < compositors.end(); ++p) {
if ((*p)->compositor->valid && (*p)->compositor->image.channelSize == channelSize) {
cmp = *p;
break;
auto cur = *p;
if (cur->compositor->valid && cur->compositor->image.channelSize == channelSize) {
if (w == cur->w && h == cur->h) {
cmp = *p;
break;
}
}
}
@ -563,15 +571,13 @@ SwSurface* SwRenderer::request(int channelSize)
//Inherits attributes from main surface
cmp = new SwSurface(surface);
cmp->compositor = new SwCompositor;
cmp->compositor->image.data = (pixel_t*)malloc(channelSize * surface->stride * surface->h);
cmp->compositor->image.w = surface->w;
cmp->compositor->image.h = surface->h;
cmp->compositor->image.stride = surface->stride;
cmp->compositor->image.data = (pixel_t*)malloc(channelSize * w * h);
cmp->w = cmp->compositor->image.w = w;
cmp->h = cmp->compositor->image.h = h;
cmp->compositor->image.stride = w;
cmp->compositor->image.direct = true;
cmp->compositor->valid = true;
cmp->channelSize = cmp->compositor->image.channelSize = channelSize;
cmp->w = cmp->compositor->image.w;
cmp->h = cmp->compositor->image.h;
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 y = region.y;
@ -595,7 +601,7 @@ RenderCompositor* SwRenderer::target(const RenderRegion& region, ColorSpace cs)
//Out of boundary
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
if (x < 0) x = 0;
@ -630,12 +636,15 @@ bool SwRenderer::endComposite(RenderCompositor* cmp)
if (!cmp) return false;
auto p = static_cast<SwCompositor*>(cmp);
p->valid = true;
//Recover Context
surface = p->recoverSfc;
surface->compositor = p->recoverCmp;
//only invalid (currently used) surface can be composited
if (p->valid) return true;
p->valid = true;
//Default is alpha blending
if (p->method == CompositeMethod::None) {
Matrix m = {1, 0, 0, 0, 1, 0, 0, 0, 1};
@ -651,30 +660,47 @@ bool SwRenderer::prepare(RenderEffect* effect)
switch (effect->type) {
case SceneEffect::GaussianBlur: return effectGaussianBlurPrepare(static_cast<RenderEffectGaussianBlur*>(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;
}
}
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);
if (p->image.channelSize != sizeof(uint32_t)) {
TVGERR("SW_ENGINE", "Not supported grayscale Gaussian Blur!");
return false;
}
switch (effect->type) {
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: {
auto cmp1 = request(surface->channelSize);
auto cmp1 = request(surface->channelSize, true);
cmp1->compositor->valid = false;
auto cmp2 = request(surface->channelSize);
auto cmp2 = request(surface->channelSize, true);
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;
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;
}
}

View File

@ -55,13 +55,13 @@ public:
bool target(pixel_t* data, uint32_t stride, uint32_t w, uint32_t h, ColorSpace cs);
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 endComposite(RenderCompositor* cmp) override;
void clearCompositors();
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 bool init(uint32_t threads);
@ -79,7 +79,7 @@ private:
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);
};

View File

@ -188,7 +188,6 @@
* http://www.freetype.org
*/
#include <setjmp.h>
#include <limits.h>
#include <memory.h>
#include "tvgSwCommon.h"
@ -243,8 +242,6 @@ struct RleWorker
int bandSize;
int bandShoot;
jmp_buf jmpBuf;
void* buffer;
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)));
}
}
//Clip x range
SwCoord xOver = 0;
if (x + aCount >= rw.cellMax.x) xOver -= (x + aCount - rw.cellMax.x);
@ -418,7 +415,7 @@ static Cell* _findCell(RleWorker& rw)
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++;
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) {
auto cell = _findCell(rw);
if (cell == nullptr) return false;
cell->area += rw.area;
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' */
/* 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.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?
if (pos != rw.cellPos) {
//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);
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.cellMin.x) pos.x = rw.cellMin.x;
@ -483,23 +489,27 @@ static void _startCell(RleWorker& rw, SwPoint pos)
rw.cellPos = pos - rw.cellMin;
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 */
if (!rw.invalid) _recordCell(rw);
if (!rw.invalid) {
if (!_recordCell(rw)) return false;
}
//start to a new position
_startCell(rw, TRUNC(to));
if (!_startCell(rw, TRUNC(to))) return false;
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) \
static_cast<SwCoord>(((unsigned long)(a) * (unsigned long)(b)) >> \
@ -511,7 +521,7 @@ static void _lineTo(RleWorker& rw, const SwPoint& to)
//vertical clipping
if ((e1.y >= rw.cellMax.y && e2.y >= rw.cellMax.y) || (e1.y < rw.cellMin.y && e2.y < rw.cellMin.y)) {
rw.pos = to;
return;
return true;
}
auto line = rw.lineStack;
@ -539,7 +549,7 @@ static void _lineTo(RleWorker& rw, const SwPoint& to)
//any horizontal line
} else if (diff.y == 0) {
e1.x = e2.x;
_setCell(rw, e1);
if (!_setCell(rw, e1)) return false;
} else if (diff.x == 0) {
//vertical line up
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;
f1.y = 0;
++e1.y;
_setCell(rw, e1);
if (!_setCell(rw, e1)) return false;
} while(e1.y != e2.y);
//vertical line down
} else {
@ -559,7 +569,7 @@ static void _lineTo(RleWorker& rw, const SwPoint& to)
rw.area += (f2.y - f1.y) * f1.x * 2;
f1.y = ONE_PIXEL;
--e1.y;
_setCell(rw, e1);
if (!_setCell(rw, e1)) return false;
} while(e1.y != e2.y);
}
//any other line
@ -612,7 +622,7 @@ static void _lineTo(RleWorker& rw, const SwPoint& to)
--e1.y;
}
_setCell(rw, e1);
if (!_setCell(rw, e1)) return false;
} 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.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;
arc[0] = to;
@ -691,14 +701,14 @@ static void _cubicTo(RleWorker& rw, const SwPoint& ctrl1, const SwPoint& ctrl2,
continue;
draw:
_lineTo(rw, arc[0]);
if (arc == rw.bezStack) return;
if (!_lineTo(rw, arc[0])) return false;
if (arc == rw.bezStack) return true;
arc -= 3;
}
}
static void _decomposeOutline(RleWorker& rw)
static bool _decomposeOutline(RleWorker& rw)
{
auto outline = rw.outline;
auto first = 0; //index of first point in contour
@ -711,38 +721,43 @@ static void _decomposeOutline(RleWorker& rw)
auto types = outline->types.data + first;
++types;
_moveTo(rw, UPSCALE(outline->pts[first]));
if (!_moveTo(rw, UPSCALE(outline->pts[first]))) return false;
while (pt < limit) {
//emit a single line_to
if (types[0] == SW_CURVE_TYPE_POINT) {
++pt;
++types;
_lineTo(rw, UPSCALE(*pt));
if (!_lineTo(rw, UPSCALE(*pt))) return false;
//types cubic
} else {
pt += 3;
types += 3;
if (pt <= limit) _cubicTo(rw, UPSCALE(pt[-2]), UPSCALE(pt[-1]), UPSCALE(pt[0]));
else if (pt - 1 == limit) _cubicTo(rw, UPSCALE(pt[-2]), UPSCALE(pt[-1]), start);
if (pt <= limit) {
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;
}
}
close:
_lineTo(rw, start);
if (!_lineTo(rw, start)) return false;
first = last + 1;
}
return true;
}
static int _genRle(RleWorker& rw)
{
if (setjmp(rw.jmpBuf) == 0) {
_decomposeOutline(rw);
if (!rw.invalid) _recordCell(rw);
return 0;
if (!_decomposeOutline(rw)) return -1;
if (!rw.invalid) {
if (!_recordCell(rw)) return -1;
}
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 spans = static_cast<SwSpan*>(malloc(sizeof(SwSpan) * (spanCnt)));
auto spansEnd = _intersectSpansRegion(clip, rle, spans, spanCnt);
@ -1015,16 +1030,21 @@ void rleClip(SwRle *rle, const SwRle *clip)
_replaceClipSpan(rle, spans, spansEnd - spans);
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 spansEnd = _intersectSpansRect(clip, rle, spans, rle->size);
_replaceClipSpan(rle, spans, spansEnd - spans);
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;
//a zero-length lineto is a no-op; avoid creating a spurious corner
if (delta.zero()) return;
//a zero-length lineto is a no-op
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 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
if (valid < 0 && arc == bezStack) {
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;
}

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
if (!func(picture, data)) return Result::Success;
if (!func(paint, data)) return Result::Success;
//Children
if (auto it = IteratorAccessor::iterator(picture)) {
if (auto it = IteratorAccessor::iterator(paint)) {
accessChildren(it, func, data);
delete(it);
}

View File

@ -172,6 +172,7 @@ static LoadModule* _find(FileType type)
}
#ifdef THORVG_FILE_IO_SUPPORT
static LoadModule* _findByPath(const string& path)
{
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);
return nullptr;
}
#endif
static FileType _convert(const string& mimeType)
@ -292,6 +294,7 @@ bool LoaderMgr::retrieve(LoadModule* loader)
LoadModule* LoaderMgr::loader(const string& path, bool* invalid)
{
#ifdef THORVG_FILE_IO_SUPPORT
*invalid = false;
//TODO: svg & lottie is not sharable.
@ -335,6 +338,7 @@ LoadModule* LoaderMgr::loader(const string& path, bool* invalid)
}
}
*invalid = true;
#endif
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 (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)) {
compData->target->pImpl->render(renderer);
}
@ -374,7 +374,7 @@ void Paint::Impl::reset()
blendMethod = BlendMethod::Normal;
renderFlag = RenderUpdateFlag::None;
ctxFlag = ContextFlag::Invalid;
ctxFlag = ContextFlag::Default;
opacity = 255;
paint->id = 0;
}

View File

@ -28,7 +28,7 @@
namespace tvg
{
enum ContextFlag : uint8_t {Invalid = 0, FastTrack = 1};
enum ContextFlag : uint8_t {Default = 0, FastTrack = 1};
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.
if (opacity < 255) return false;
if (opacity < 255) return;
//Composition test
const Paint* target;
auto method = picture->composite(&target);
if (!target || method == tvg::CompositeMethod::ClipPath) return false;
if (target->pImpl->opacity == 255 || target->pImpl->opacity == 0) return false;
if (!target || method == tvg::CompositeMethod::ClipPath) return;
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);
else if (paint) {
RenderCompositor* cmp = nullptr;
if (needComp) {
cmp = renderer->target(bounds(renderer), renderer->colorSpace());
if (cFlag) {
cmp = renderer->target(bounds(renderer), renderer->colorSpace(), static_cast<CompositionFlag>(cFlag));
renderer->beginComposite(cmp, CompositeMethod::None, 255);
}
ret = paint->pImpl->render(renderer);
@ -164,9 +166,14 @@ Type Picture::type() const noexcept
Result Picture::load(const std::string& path) noexcept
{
#ifdef THORVG_FILE_IO_SUPPORT
if (path.empty()) return Result::InvalidArguments;
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
float w = 0, h = 0;
Picture* picture = nullptr;
uint8_t cFlag = CompositionFlag::Invalid;
bool resizing = false;
bool needComp = false; //need composition
bool needComposition(uint8_t opacity);
void queryComposition(uint8_t opacity);
bool render(RenderMethod* renderer);
bool size(float w, float h);
RenderRegion bounds(RenderMethod* renderer);
@ -107,7 +107,7 @@ struct Picture::Impl
loader->resize(paint, w, h);
resizing = false;
}
needComp = needComposition(opacity) ? true : false;
queryComposition(opacity);
rd = paint->pImpl->update(renderer, transform, clips, opacity, flag, false);
}
return rd;

View File

@ -36,6 +36,7 @@ using RenderData = void*;
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 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
enum ColorSpace : uint8_t
@ -137,6 +138,7 @@ struct RenderStroke
dashPattern = nullptr;
}
dashCnt = rhs.dashCnt;
dashOffset = rhs.dashOffset;
miterlimit = rhs.miterlimit;
cap = rhs.cap;
join = rhs.join;
@ -268,7 +270,7 @@ struct RenderEffect
RenderData rd = nullptr;
RenderRegion extend = {0, 0, 0, 0};
SceneEffect type;
bool invalid = false;
bool valid = false;
virtual ~RenderEffect()
{
@ -309,7 +311,7 @@ struct RenderEffectDropShadow : RenderEffect
inst->color[0] = va_arg(args, int);
inst->color[1] = 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->distance = (float) va_arg(args, double);
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
{
private:
@ -347,12 +409,12 @@ public:
virtual bool clear() = 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 endComposite(RenderCompositor* cmp) = 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)

View File

@ -128,6 +128,18 @@ Result Scene::push(SceneEffect effect, ...) noexcept
re = RenderEffectDropShadow::gen(args);
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;
}

View File

@ -62,8 +62,8 @@ struct Scene::Impl
Scene* scene = nullptr;
RenderRegion vport = {0, 0, INT32_MAX, INT32_MAX};
Array<RenderEffect*>* effects = nullptr;
uint8_t compFlag = CompositionFlag::Invalid;
uint8_t opacity; //for composition
bool needComp = false; //composite or not
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 (effects) return true;
if (opacity == 0 || paints.empty()) return 0;
//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);
if (compMethod != CompositeMethod::None && compMethod != CompositeMethod::ClipPath) return true;
//Blending may require composition (even if opacity == 255)
if (PP(scene)->blendMethod != BlendMethod::Normal) return true;
if (compMethod != CompositeMethod::None && compMethod != CompositeMethod::ClipPath) compFlag |= CompositionFlag::Masking;
if (PP(scene)->blendMethod != BlendMethod::Normal) compFlag |= CompositionFlag::Blending;
//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.
//OPTIMIZE: the bitmap type of the picture 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)
{
this->vport = renderer->viewport();
if ((needComp = needComposition(opacity))) {
if (needComposition(opacity)) {
/* Overriding opacity value. If this scene is half-translucent,
It must do intermediate composition with that opacity value. */
this->opacity = opacity;
@ -131,8 +131,8 @@ struct Scene::Impl
renderer->blend(PP(scene)->blendMethod);
if (needComp) {
cmp = renderer->target(bounds(renderer), renderer->colorSpace());
if (compFlag) {
cmp = renderer->target(bounds(renderer), renderer->colorSpace(), static_cast<CompositionFlag>(compFlag));
renderer->beginComposite(cmp, CompositeMethod::None, opacity);
}
@ -143,9 +143,10 @@ struct Scene::Impl
if (cmp) {
//Apply post effects if any.
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) {
renderer->effect(cmp, *e, opacity, direct);
renderer->effect(cmp, *e, direct);
}
}
renderer->endComposite(cmp);
@ -178,7 +179,7 @@ struct Scene::Impl
if (effects) {
for (auto e = effects->begin(); e < effects->end(); ++e) {
auto effect = *e;
if (effect->rd || renderer->prepare(effect)) {
if (effect->valid || renderer->prepare(effect)) {
ex = std::min(ex, effect->extend.x);
ey = std::min(ey, effect->extend.y);
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.pts.clear();
pImpl->flag |= RenderUpdateFlag::Path;
pImpl->rFlag |= RenderUpdateFlag::Path;
return Result::Success;
}
@ -93,7 +93,7 @@ Result Shape::appendPath(const PathCommand *cmds, uint32_t cmdCnt, const Point*
pImpl->grow(cmdCnt, ptsCnt);
pImpl->append(cmds, cmdCnt, pts, ptsCnt);
pImpl->flag |= RenderUpdateFlag::Path;
pImpl->rFlag |= RenderUpdateFlag::Path;
return Result::Success;
}
@ -111,7 +111,7 @@ Result Shape::lineTo(float x, float y) noexcept
{
pImpl->lineTo(x, y);
pImpl->flag |= RenderUpdateFlag::Path;
pImpl->rFlag |= RenderUpdateFlag::Path;
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->flag |= RenderUpdateFlag::Path;
pImpl->rFlag |= RenderUpdateFlag::Path;
return Result::Success;
}
@ -131,7 +131,7 @@ Result Shape::close() noexcept
{
pImpl->close();
pImpl->flag |= RenderUpdateFlag::Path;
pImpl->rFlag |= RenderUpdateFlag::Path;
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->close();
pImpl->flag |= RenderUpdateFlag::Path;
pImpl->rFlag |= RenderUpdateFlag::Path;
return Result::Success;
}
@ -212,7 +212,7 @@ TVG_DEPRECATED Result Shape::appendArc(float cx, float cy, float radius, float s
if (pie) pImpl->close();
pImpl->flag |= RenderUpdateFlag::Path;
pImpl->rFlag |= RenderUpdateFlag::Path;
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->flag |= RenderUpdateFlag::Path;
pImpl->rFlag |= RenderUpdateFlag::Path;
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) {
delete(pImpl->rs.fill);
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;
@ -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[2] = b;
pImpl->rs.color[3] = a;
pImpl->flag |= RenderUpdateFlag::Color;
pImpl->rFlag |= RenderUpdateFlag::Color;
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);
pImpl->rs.fill = p;
pImpl->flag |= RenderUpdateFlag::Gradient;
pImpl->rFlag |= RenderUpdateFlag::Gradient;
return Result::Success;
}

View File

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

View File

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