diff --git a/inc/thorvg.h b/inc/thorvg.h index 1ee898ca..209ac64c 100644 --- a/inc/thorvg.h +++ b/inc/thorvg.h @@ -217,7 +217,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 5e6459ce..da7e1ff2 100644 --- a/src/renderer/gl_engine/tvgGlRenderer.cpp +++ b/src/renderer/gl_engine/tvgGlRenderer.cpp @@ -1079,7 +1079,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 dc5d564b..d88b4c9c 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 9371ae6c..5b63e3a6 100644 --- a/src/renderer/sw_engine/tvgSwCommon.h +++ b/src/renderer/sw_engine/tvgSwCommon.h @@ -578,7 +578,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 5c760fe6..fd4cf871 100644 --- a/src/renderer/sw_engine/tvgSwRenderer.cpp +++ b/src/renderer/sw_engine/tvgSwRenderer.cpp @@ -640,12 +640,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 == CompositeMethod::None) { Matrix m = {1, 0, 0, 0, 1, 0, 0, 0, 1}; @@ -661,17 +664,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)); @@ -681,10 +690,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 1c19f063..02fbe3b6 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 a7ee3290..09aadca9 100644 --- a/src/renderer/tvgRender.h +++ b/src/renderer/tvgRender.h @@ -269,7 +269,7 @@ struct RenderEffect RenderData rd = nullptr; RenderRegion extend = {0, 0, 0, 0}; SceneEffect type; - bool invalid = false; + bool valid = false; virtual ~RenderEffect() { @@ -320,6 +320,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: @@ -353,7 +369,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(CompositeMethod method) diff --git a/src/renderer/tvgScene.cpp b/src/renderer/tvgScene.cpp index ce169d33..63e06d37 100644 --- a/src/renderer/tvgScene.cpp +++ b/src/renderer/tvgScene.cpp @@ -128,6 +128,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 18d11189..bc789584 100644 --- a/src/renderer/tvgScene.h +++ b/src/renderer/tvgScene.h @@ -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); diff --git a/src/renderer/wg_engine/tvgWgRenderer.cpp b/src/renderer/wg_engine/tvgWgRenderer.cpp index 80218ff6..38aaadd9 100755 --- a/src/renderer/wg_engine/tvgWgRenderer.cpp +++ b/src/renderer/wg_engine/tvgWgRenderer.cpp @@ -431,7 +431,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 20847996..4c90b6fc 100755 --- a/src/renderer/wg_engine/tvgWgRenderer.h +++ b/src/renderer/wg_engine/tvgWgRenderer.h @@ -53,7 +53,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);