mirror of
https://github.com/thorvg/thorvg.git
synced 2025-06-07 21:23:32 +00:00
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:
parent
e34ba91ff1
commit
d6c80f538c
12 changed files with 108 additions and 30 deletions
|
@ -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]}
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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_ */
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
}
|
|
@ -634,12 +634,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 == 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<RenderEffectGaussianBlur*>(effect));
|
||||
case SceneEffect::DropShadow: return effectDropShadowPrepare(static_cast<RenderEffectDropShadow*>(effect));
|
||||
case SceneEffect::Fill: return effectFillPrepare(static_cast<RenderEffectFill*>(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, true), static_cast<const RenderEffectGaussianBlur*>(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<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);
|
||||
}
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Add table
Reference in a new issue