From d6c80f538c813a169f7dd5113a3f74bb220e9982 Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Thu, 12 Dec 2024 10:59:16 +0900 Subject: [PATCH] renderer: add a new scene effect type - Fill Fill is used for overriding the scene content color with a given fill information. It's yet an Experimental API. issue: https://github.com/thorvg/thorvg/issues/2718 --- inc/thorvg.h | 3 +- src/renderer/gl_engine/tvgGlRenderer.cpp | 2 +- src/renderer/gl_engine/tvgGlRenderer.h | 2 +- src/renderer/sw_engine/tvgSwCommon.h | 4 +- src/renderer/sw_engine/tvgSwPostEffect.cpp | 70 +++++++++++++++++----- src/renderer/sw_engine/tvgSwRenderer.cpp | 20 +++++-- src/renderer/sw_engine/tvgSwRenderer.h | 2 +- src/renderer/tvgRender.h | 20 ++++++- src/renderer/tvgScene.cpp | 4 ++ src/renderer/tvgScene.h | 7 ++- src/renderer/wg_engine/tvgWgRenderer.cpp | 2 +- src/renderer/wg_engine/tvgWgRenderer.h | 2 +- 12 files changed, 108 insertions(+), 30 deletions(-) diff --git a/inc/thorvg.h b/inc/thorvg.h index 6156f886..aeb8a1b2 100644 --- a/inc/thorvg.h +++ b/inc/thorvg.h @@ -226,7 +226,8 @@ 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]} }; diff --git a/src/renderer/gl_engine/tvgGlRenderer.cpp b/src/renderer/gl_engine/tvgGlRenderer.cpp index 7e4a6d19..e66c9e3f 100644 --- a/src/renderer/gl_engine/tvgGlRenderer.cpp +++ b/src/renderer/gl_engine/tvgGlRenderer.cpp @@ -982,7 +982,7 @@ bool GlRenderer::prepare(TVG_UNUSED RenderEffect* effect) } -bool GlRenderer::effect(TVG_UNUSED RenderCompositor* cmp, TVG_UNUSED const RenderEffect* effect, TVG_UNUSED uint8_t opacity, TVG_UNUSED bool direct) +bool GlRenderer::effect(TVG_UNUSED RenderCompositor* cmp, TVG_UNUSED const RenderEffect* effect, TVG_UNUSED bool direct) { TVGLOG("GL_ENGINE", "SceneEffect(%d) is not supported", (int)effect->type); return false; diff --git a/src/renderer/gl_engine/tvgGlRenderer.h b/src/renderer/gl_engine/tvgGlRenderer.h index b157b2e0..b9ae57fb 100644 --- a/src/renderer/gl_engine/tvgGlRenderer.h +++ b/src/renderer/gl_engine/tvgGlRenderer.h @@ -87,7 +87,7 @@ public: bool endComposite(RenderCompositor* cmp) 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 GlRenderer* gen(); static int init(TVG_UNUSED uint32_t threads); diff --git a/src/renderer/sw_engine/tvgSwCommon.h b/src/renderer/sw_engine/tvgSwCommon.h index 4ced7d5e..ad70be84 100644 --- a/src/renderer/sw_engine/tvgSwCommon.h +++ b/src/renderer/sw_engine/tvgSwCommon.h @@ -574,7 +574,9 @@ bool rasterConvertCS(RenderSurface* surface, ColorSpace to); 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); #endif /* _TVG_SW_COMMON_H_ */ diff --git a/src/renderer/sw_engine/tvgSwPostEffect.cpp b/src/renderer/sw_engine/tvgSwPostEffect.cpp index 4168917b..a553d9d0 100644 --- a/src/renderer/sw_engine/tvgSwPostEffect.cpp +++ b/src/renderer/sw_engine/tvgSwPostEffect.cpp @@ -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(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(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); @@ -406,5 +397,56 @@ bool effectDropShadow(SwCompositor* cmp, SwSurface* surface[2], const RenderEffe d += cmp->image.stride; } + 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 = (bbox.max.x - bbox.min.x); + auto h = (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 (uint32_t y = 0; y < h; ++y) { + auto dst = dbuffer; + auto src = sbuffer; + for (uint32_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 (uint32_t y = 0; y < h; ++y) { + auto dst = dbuffer; + for (uint32_t x = 0; x < w; ++x, ++dst) { + *dst = ALPHA_BLEND(color, MULTIPLY(opacity, A(*dst))); + } + dbuffer += cmp->image.stride; + } + } return true; } \ No newline at end of file diff --git a/src/renderer/sw_engine/tvgSwRenderer.cpp b/src/renderer/sw_engine/tvgSwRenderer.cpp index 707978ea..ea582246 100644 --- a/src/renderer/sw_engine/tvgSwRenderer.cpp +++ b/src/renderer/sw_engine/tvgSwRenderer.cpp @@ -634,12 +634,15 @@ bool SwRenderer::endComposite(RenderCompositor* cmp) if (!cmp) return false; auto p = static_cast(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 == MaskMethod::None) { Matrix m = {1, 0, 0, 0, 1, 0, 0, 0, 1}; @@ -655,17 +658,23 @@ bool SwRenderer::prepare(RenderEffect* effect) switch (effect->type) { case SceneEffect::GaussianBlur: return effectGaussianBlurPrepare(static_cast(effect)); case SceneEffect::DropShadow: return effectDropShadowPrepare(static_cast(effect)); + case SceneEffect::Fill: return effectFillPrepare(static_cast(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(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, true), static_cast(effect)); @@ -675,10 +684,13 @@ bool SwRenderer::effect(RenderCompositor* cmp, const RenderEffect* effect, uint8 cmp1->compositor->valid = false; auto cmp2 = request(surface->channelSize, true); SwSurface* surfaces[] = {cmp1, cmp2}; - auto ret = effectDropShadow(p, surfaces, static_cast(effect), opacity, direct); + auto ret = effectDropShadow(p, surfaces, static_cast(effect), direct); cmp1->compositor->valid = true; return ret; } + case SceneEffect::Fill: { + return effectFill(p, static_cast(effect), direct); + } default: return false; } } diff --git a/src/renderer/sw_engine/tvgSwRenderer.h b/src/renderer/sw_engine/tvgSwRenderer.h index ca96da88..a15fdb57 100644 --- a/src/renderer/sw_engine/tvgSwRenderer.h +++ b/src/renderer/sw_engine/tvgSwRenderer.h @@ -61,7 +61,7 @@ public: 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); diff --git a/src/renderer/tvgRender.h b/src/renderer/tvgRender.h index ba735bcb..b8b454b4 100644 --- a/src/renderer/tvgRender.h +++ b/src/renderer/tvgRender.h @@ -260,7 +260,7 @@ struct RenderEffect RenderData rd = nullptr; RenderRegion extend = {0, 0, 0, 0}; SceneEffect type; - bool invalid = false; + bool valid = false; virtual ~RenderEffect() { @@ -311,6 +311,22 @@ 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] = std::min(va_arg(args, int), 255); + inst->type = SceneEffect::Fill; + return inst; + } +}; + class RenderMethod { private: @@ -344,7 +360,7 @@ public: 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(MaskMethod method) diff --git a/src/renderer/tvgScene.cpp b/src/renderer/tvgScene.cpp index b846c75e..4072cbaa 100644 --- a/src/renderer/tvgScene.cpp +++ b/src/renderer/tvgScene.cpp @@ -109,6 +109,10 @@ Result Scene::push(SceneEffect effect, ...) noexcept re = RenderEffectDropShadow::gen(args); break; } + case SceneEffect::Fill: { + re = RenderEffectFill::gen(args); + break; + } default: break; } diff --git a/src/renderer/tvgScene.h b/src/renderer/tvgScene.h index c320978d..279801e2 100644 --- a/src/renderer/tvgScene.h +++ b/src/renderer/tvgScene.h @@ -141,9 +141,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); @@ -176,7 +177,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); diff --git a/src/renderer/wg_engine/tvgWgRenderer.cpp b/src/renderer/wg_engine/tvgWgRenderer.cpp index fc8f2987..dadb666b 100755 --- a/src/renderer/wg_engine/tvgWgRenderer.cpp +++ b/src/renderer/wg_engine/tvgWgRenderer.cpp @@ -473,7 +473,7 @@ bool WgRenderer::prepare(TVG_UNUSED RenderEffect* effect) } -bool WgRenderer::effect(TVG_UNUSED RenderCompositor* cmp, TVG_UNUSED const RenderEffect* effect, TVG_UNUSED uint8_t opacity, TVG_UNUSED bool direct) +bool WgRenderer::effect(TVG_UNUSED RenderCompositor* cmp, TVG_UNUSED const RenderEffect* effect, TVG_UNUSED bool direct) { TVGLOG("WG_ENGINE", "SceneEffect(%d) is not supported", (int)effect->type); return false; diff --git a/src/renderer/wg_engine/tvgWgRenderer.h b/src/renderer/wg_engine/tvgWgRenderer.h index f6f10788..85aea67e 100755 --- a/src/renderer/wg_engine/tvgWgRenderer.h +++ b/src/renderer/wg_engine/tvgWgRenderer.h @@ -52,7 +52,7 @@ public: bool endComposite(RenderCompositor* cmp) 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 WgRenderer* gen(); static bool init(uint32_t threads);