mirror of
https://github.com/thorvg/thorvg.git
synced 2025-06-08 05:33:36 +00:00
common: support Tint SceneEffect
The Tint effect in ThorVG is used to modify the overall color tone of a scene. It works by blending a specified tint color with the existing colors of the scene. This effect is useful for color grading, mood changes, or applying thematic filters to vector graphics and animations. Applied the equation is: Result = (1 - L) * Black + L * White, where the L is Luminance. issue: https://github.com/thorvg/thorvg/issues/2718
This commit is contained in:
parent
1806b32971
commit
b778f98206
7 changed files with 107 additions and 17 deletions
|
@ -231,7 +231,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]}
|
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]}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -571,6 +571,7 @@ void rasterXYFlip(uint32_t* src, uint32_t* dst, int32_t stride, int32_t w, int32
|
||||||
void rasterUnpremultiply(RenderSurface* surface);
|
void rasterUnpremultiply(RenderSurface* surface);
|
||||||
void rasterPremultiply(RenderSurface* surface);
|
void rasterPremultiply(RenderSurface* surface);
|
||||||
bool rasterConvertCS(RenderSurface* surface, ColorSpace to);
|
bool rasterConvertCS(RenderSurface* surface, ColorSpace to);
|
||||||
|
uint32_t rasterUnpremultiply(uint32_t data);
|
||||||
|
|
||||||
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);
|
||||||
|
@ -578,5 +579,8 @@ bool effectDropShadow(SwCompositor* cmp, SwSurface* surfaces[2], const RenderEff
|
||||||
bool effectDropShadowPrepare(RenderEffectDropShadow* effect);
|
bool effectDropShadowPrepare(RenderEffectDropShadow* effect);
|
||||||
bool effectFillPrepare(RenderEffectFill* effect);
|
bool effectFillPrepare(RenderEffectFill* effect);
|
||||||
bool effectFill(SwCompositor* cmp, const RenderEffectFill* params, bool direct);
|
bool effectFill(SwCompositor* cmp, const RenderEffectFill* params, bool direct);
|
||||||
|
bool effectTintPrepare(RenderEffectTint* effect);
|
||||||
|
bool effectTint(SwCompositor* cmp, const RenderEffectTint* params, bool direct);
|
||||||
|
|
||||||
|
|
||||||
#endif /* _TVG_SW_COMMON_H_ */
|
#endif /* _TVG_SW_COMMON_H_ */
|
||||||
|
|
|
@ -450,3 +450,58 @@ bool effectFill(SwCompositor* cmp, const RenderEffectFill* params, bool direct)
|
||||||
}
|
}
|
||||||
return true;
|
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, %f)", 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);
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
|
@ -1668,6 +1668,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)
|
void rasterUnpremultiply(RenderSurface* surface)
|
||||||
{
|
{
|
||||||
if (surface->channelSize != sizeof(uint32_t)) return;
|
if (surface->channelSize != sizeof(uint32_t)) return;
|
||||||
|
@ -1678,20 +1692,7 @@ void rasterUnpremultiply(RenderSurface* surface)
|
||||||
for (uint32_t y = 0; y < surface->h; y++) {
|
for (uint32_t y = 0; y < surface->h; y++) {
|
||||||
auto buffer = surface->buf32 + surface->stride * y;
|
auto buffer = surface->buf32 + surface->stride * y;
|
||||||
for (uint32_t x = 0; x < surface->w; ++x) {
|
for (uint32_t x = 0; x < surface->w; ++x) {
|
||||||
uint8_t a = buffer[x] >> 24;
|
buffer[x] = rasterUnpremultiply(buffer[x]);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
surface->premultiplied = false;
|
surface->premultiplied = false;
|
||||||
|
|
|
@ -655,6 +655,7 @@ bool SwRenderer::prepare(RenderEffect* effect)
|
||||||
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));
|
case SceneEffect::Fill: return effectFillPrepare(static_cast<RenderEffectFill*>(effect));
|
||||||
|
case SceneEffect::Tint: return effectTintPrepare(static_cast<RenderEffectTint*>(effect));
|
||||||
default: return false;
|
default: return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -687,6 +688,9 @@ bool SwRenderer::effect(RenderCompositor* cmp, const RenderEffect* effect, bool
|
||||||
case SceneEffect::Fill: {
|
case SceneEffect::Fill: {
|
||||||
return effectFill(p, static_cast<const RenderEffectFill*>(effect), direct);
|
return effectFill(p, static_cast<const RenderEffectFill*>(effect), direct);
|
||||||
}
|
}
|
||||||
|
case SceneEffect::Tint: {
|
||||||
|
return effectTint(p, static_cast<const RenderEffectTint*>(effect), direct);
|
||||||
|
}
|
||||||
default: return false;
|
default: return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -301,7 +301,7 @@ struct RenderEffectDropShadow : RenderEffect
|
||||||
inst->color[0] = va_arg(args, int);
|
inst->color[0] = va_arg(args, int);
|
||||||
inst->color[1] = va_arg(args, int);
|
inst->color[1] = va_arg(args, int);
|
||||||
inst->color[2] = 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->angle = (float) va_arg(args, double);
|
||||||
inst->distance = (float) va_arg(args, double);
|
inst->distance = (float) va_arg(args, double);
|
||||||
inst->sigma = std::max((float) va_arg(args, double), 0.0f);
|
inst->sigma = std::max((float) va_arg(args, double), 0.0f);
|
||||||
|
@ -321,12 +321,33 @@ struct RenderEffectFill : RenderEffect
|
||||||
inst->color[0] = va_arg(args, int);
|
inst->color[0] = va_arg(args, int);
|
||||||
inst->color[1] = va_arg(args, int);
|
inst->color[1] = va_arg(args, int);
|
||||||
inst->color[2] = 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->type = SceneEffect::Fill;
|
inst->type = SceneEffect::Fill;
|
||||||
return inst;
|
return inst;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct RenderEffectTint : RenderEffect
|
||||||
|
{
|
||||||
|
uint8_t black[3]; //rgb
|
||||||
|
uint8_t white[3]; //rgb
|
||||||
|
float intensity; //0 - 100
|
||||||
|
|
||||||
|
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 = std::min((float)va_arg(args, double), 100.0f) * 2.55f;
|
||||||
|
inst->type = SceneEffect::Tint;
|
||||||
|
return inst;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
class RenderMethod
|
class RenderMethod
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -326,6 +326,10 @@ struct Scene::Impl : Paint::Impl
|
||||||
re = RenderEffectFill::gen(args);
|
re = RenderEffectFill::gen(args);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case SceneEffect::Tint: {
|
||||||
|
re = RenderEffectTint::gen(args);
|
||||||
|
break;
|
||||||
|
}
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue