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
This commit is contained in:
Hermet Park 2024-12-12 10:59:16 +09:00 committed by Hermet Park
parent e34ba91ff1
commit d6c80f538c
12 changed files with 108 additions and 30 deletions

View file

@ -226,7 +226,8 @@ 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]}
}; };

View file

@ -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); TVGLOG("GL_ENGINE", "SceneEffect(%d) is not supported", (int)effect->type);
return false; return false;

View file

@ -87,7 +87,7 @@ public:
bool endComposite(RenderCompositor* cmp) override; bool endComposite(RenderCompositor* cmp) override;
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 GlRenderer* gen(); static GlRenderer* gen();
static int init(TVG_UNUSED uint32_t threads); static int init(TVG_UNUSED uint32_t threads);

View file

@ -574,7 +574,9 @@ bool rasterConvertCS(RenderSurface* surface, ColorSpace to);
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);
#endif /* _TVG_SW_COMMON_H_ */ #endif /* _TVG_SW_COMMON_H_ */

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,54 @@ 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 = (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;
}

View file

@ -634,12 +634,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 == MaskMethod::None) { if (p->method == MaskMethod::None) {
Matrix m = {1, 0, 0, 0, 1, 0, 0, 0, 1}; Matrix m = {1, 0, 0, 0, 1, 0, 0, 0, 1};
@ -655,17 +658,23 @@ 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));
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, true), static_cast<const RenderEffectGaussianBlur*>(effect)); return effectGaussianBlur(p, request(surface->channelSize, true), static_cast<const RenderEffectGaussianBlur*>(effect));
@ -675,10 +684,13 @@ bool SwRenderer::effect(RenderCompositor* cmp, const RenderEffect* effect, uint8
cmp1->compositor->valid = false; cmp1->compositor->valid = false;
auto cmp2 = request(surface->channelSize, true); 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);
}
default: return false; default: return false;
} }
} }

View file

@ -61,7 +61,7 @@ public:
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);

View file

@ -260,7 +260,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()
{ {
@ -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 class RenderMethod
{ {
private: private:
@ -344,7 +360,7 @@ public:
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(MaskMethod method) static inline bool MASK_REGION_MERGING(MaskMethod method)

View file

@ -109,6 +109,10 @@ Result Scene::push(SceneEffect effect, ...) noexcept
re = RenderEffectDropShadow::gen(args); re = RenderEffectDropShadow::gen(args);
break; break;
} }
case SceneEffect::Fill: {
re = RenderEffectFill::gen(args);
break;
}
default: break; default: break;
} }

View file

@ -141,9 +141,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);
@ -176,7 +177,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

@ -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); TVGLOG("WG_ENGINE", "SceneEffect(%d) is not supported", (int)effect->type);
return false; return false;

View file

@ -52,7 +52,7 @@ public:
bool endComposite(RenderCompositor* cmp) override; bool endComposite(RenderCompositor* cmp) override;
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 WgRenderer* gen(); static WgRenderer* gen();
static bool init(uint32_t threads); static bool init(uint32_t threads);