From b778f98206efe37eef5f2cb895d707ca45105d82 Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Mon, 16 Dec 2024 12:42:32 +0900 Subject: [PATCH] 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 --- inc/thorvg.h | 3 +- src/renderer/sw_engine/tvgSwCommon.h | 4 ++ src/renderer/sw_engine/tvgSwPostEffect.cpp | 55 ++++++++++++++++++++++ src/renderer/sw_engine/tvgSwRaster.cpp | 29 ++++++------ src/renderer/sw_engine/tvgSwRenderer.cpp | 4 ++ src/renderer/tvgRender.h | 25 +++++++++- src/renderer/tvgScene.h | 4 ++ 7 files changed, 107 insertions(+), 17 deletions(-) diff --git a/inc/thorvg.h b/inc/thorvg.h index fed62b7a..ec1a6e0f 100644 --- a/inc/thorvg.h +++ b/inc/thorvg.h @@ -231,7 +231,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]} - 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]} }; diff --git a/src/renderer/sw_engine/tvgSwCommon.h b/src/renderer/sw_engine/tvgSwCommon.h index bad67c54..b1cfcddd 100644 --- a/src/renderer/sw_engine/tvgSwCommon.h +++ b/src/renderer/sw_engine/tvgSwCommon.h @@ -571,6 +571,7 @@ void rasterXYFlip(uint32_t* src, uint32_t* dst, int32_t stride, int32_t w, int32 void rasterUnpremultiply(RenderSurface* surface); void rasterPremultiply(RenderSurface* surface); bool rasterConvertCS(RenderSurface* surface, ColorSpace to); +uint32_t rasterUnpremultiply(uint32_t data); bool effectGaussianBlur(SwCompositor* cmp, SwSurface* surface, const RenderEffectGaussianBlur* params); bool effectGaussianBlurPrepare(RenderEffectGaussianBlur* effect); @@ -578,5 +579,8 @@ bool effectDropShadow(SwCompositor* cmp, SwSurface* surfaces[2], const RenderEff bool effectDropShadowPrepare(RenderEffectDropShadow* effect); bool effectFillPrepare(RenderEffectFill* effect); 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_ */ diff --git a/src/renderer/sw_engine/tvgSwPostEffect.cpp b/src/renderer/sw_engine/tvgSwPostEffect.cpp index ad106a91..eebf051f 100644 --- a/src/renderer/sw_engine/tvgSwPostEffect.cpp +++ b/src/renderer/sw_engine/tvgSwPostEffect.cpp @@ -448,5 +448,60 @@ bool effectFill(SwCompositor* cmp, const RenderEffectFill* params, bool direct) dbuffer += cmp->image.stride; } } + 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; } \ No newline at end of file diff --git a/src/renderer/sw_engine/tvgSwRaster.cpp b/src/renderer/sw_engine/tvgSwRaster.cpp index 2b678387..10c2825a 100644 --- a/src/renderer/sw_engine/tvgSwRaster.cpp +++ b/src/renderer/sw_engine/tvgSwRaster.cpp @@ -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) { if (surface->channelSize != sizeof(uint32_t)) return; @@ -1678,20 +1692,7 @@ void rasterUnpremultiply(RenderSurface* surface) for (uint32_t y = 0; y < surface->h; y++) { auto buffer = surface->buf32 + surface->stride * y; for (uint32_t x = 0; x < surface->w; ++x) { - uint8_t a = buffer[x] >> 24; - 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); - } + buffer[x] = rasterUnpremultiply(buffer[x]); } } surface->premultiplied = false; diff --git a/src/renderer/sw_engine/tvgSwRenderer.cpp b/src/renderer/sw_engine/tvgSwRenderer.cpp index 15043f6c..c89be2c8 100644 --- a/src/renderer/sw_engine/tvgSwRenderer.cpp +++ b/src/renderer/sw_engine/tvgSwRenderer.cpp @@ -655,6 +655,7 @@ bool SwRenderer::prepare(RenderEffect* effect) case SceneEffect::GaussianBlur: return effectGaussianBlurPrepare(static_cast(effect)); case SceneEffect::DropShadow: return effectDropShadowPrepare(static_cast(effect)); case SceneEffect::Fill: return effectFillPrepare(static_cast(effect)); + case SceneEffect::Tint: return effectTintPrepare(static_cast(effect)); default: return false; } } @@ -687,6 +688,9 @@ bool SwRenderer::effect(RenderCompositor* cmp, const RenderEffect* effect, bool case SceneEffect::Fill: { return effectFill(p, static_cast(effect), direct); } + case SceneEffect::Tint: { + return effectTint(p, static_cast(effect), direct); + } default: return false; } } diff --git a/src/renderer/tvgRender.h b/src/renderer/tvgRender.h index b8b454b4..0ce3b9bc 100644 --- a/src/renderer/tvgRender.h +++ b/src/renderer/tvgRender.h @@ -301,7 +301,7 @@ struct RenderEffectDropShadow : RenderEffect 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->color[3] = va_arg(args, int); inst->angle = (float) va_arg(args, double); inst->distance = (float) va_arg(args, double); 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[1] = 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; 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 { private: diff --git a/src/renderer/tvgScene.h b/src/renderer/tvgScene.h index 626bd755..3ee7b193 100644 --- a/src/renderer/tvgScene.h +++ b/src/renderer/tvgScene.h @@ -326,6 +326,10 @@ struct Scene::Impl : Paint::Impl re = RenderEffectFill::gen(args); break; } + case SceneEffect::Tint: { + re = RenderEffectTint::gen(args); + break; + } default: break; }