common: added blending factor to tritone post-effect
Some checks are pending
Android / build_x86_64 (push) Waiting to run
Android / build_aarch64 (push) Waiting to run
iOS / build_x86_64 (push) Waiting to run
iOS / build_arm64 (push) Waiting to run
macOS / build (push) Waiting to run
macOS / compact_test (push) Waiting to run
macOS / unit_test (push) Waiting to run
Ubuntu / build (push) Waiting to run
Ubuntu / compact_test (push) Waiting to run
Ubuntu / unit_test (push) Waiting to run
Windows / build (push) Waiting to run
Windows / compact_test (push) Waiting to run
Windows / unit_test (push) Waiting to run

- introduced a blending factor to control the mix between the original color and the tritone effect.
- improved Lottie compliance with this enhancement.
- implemented the spec by all engines

CAPI:
* Tvg_Result tvg_scene_push_effect_tritone(Tvg_Paint* scene, int shadow_r, int shadow_g, int shadow_b, int midtone_r, int midtone_g, int midtone_b, int highlight_r, int highlight_g, int highlight_b);
 -> TVG_API Tvg_Result tvg_scene_push_effect_tritone(Tvg_Paint* scene, int shadow_r, int shadow_g, int shadow_b, int midtone_r, int midtone_g, int midtone_b, int highlight_r, int highlight_g, int highlight_b, int blend);
This commit is contained in:
Hermet Park 2025-07-05 13:20:55 +09:00 committed by Hermet Park
parent 53680eae2f
commit 3fe7432ac5
14 changed files with 57 additions and 31 deletions

View file

@ -115,9 +115,9 @@ struct UserExample : tvgexam::Example
tint->push(tvg::SceneEffect::ClearAll);
tint->push(tvg::SceneEffect::Tint, 0, 0, 0, 0, (int)(progress * 255), 0, (double)(progress * 100.0f));
//Apply Trintone post effect (shadow:rgb, midtone:rgb, highlight:rgb)
//Apply Tritone post effect (shadow:rgb, midtone:rgb, highlight:rgb, blending with original)
trintone->push(tvg::SceneEffect::ClearAll);
trintone->push(tvg::SceneEffect::Tritone, 0, (int)(progress * 255), 0, 199, 110, 36, 255, 255, 255);
trintone->push(tvg::SceneEffect::Tritone, 0, (int)(progress * 255), 0, 199, 110, 36, 255, 255, 255, 0);
canvas->update();

View file

@ -233,9 +233,9 @@ 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(4) = {sigma(double)[> 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(double)[0 - 360], distance(double), blur_sigma(double)[> 0], quality(int)[0 - 100]}
Fill, ///< Override the scene content color with a given fill information (Experimental API). Param(4) = {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 parameters (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(double)[0 - 100]}
Tritone ///< Apply a tritone color effect to the scene using three color parameters for shadows, midtones, and highlights (Experimental API). Param(9) = {Shadow_R(int)[0 - 255], Shadow_G(int)[0 - 255], Shadow_B(int)[0 - 255], Midtone_R(int)[0 - 255], Midtone_G(int)[0 - 255], Midtone_B(int)[0 - 255], Highlight_R(int)[0 - 255], Highlight_G(int)[0 - 255], Highlight_B(int)[0 - 255]}
Fill, ///< Override the scene content color with a given fill information. Param(4) = {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 parameters. 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(double)[0 - 100]}
Tritone ///< Apply a tritone color effect to the scene using three color parameters for shadows, midtones, and highlights. A blending factor determines the mix between the original color and the tritone colors. Param(9) = {Shadow_R(int)[0 - 255], Shadow_G(int)[0 - 255], Shadow_B(int)[0 - 255], Midtone_R(int)[0 - 255], Midtone_G(int)[0 - 255], Midtone_B(int)[0 - 255], Highlight_R(int)[0 - 255], Highlight_G(int)[0 - 255], Highlight_B(int)[0 - 255], Blend(int)[0 - 255]}
};

View file

@ -2091,10 +2091,11 @@ TVG_API Tvg_Result tvg_scene_push_effect_tint(Tvg_Paint* scene, int black_r, int
* @param[in] highlight_r Red component of the highlight color [0 - 255].
* @param[in] highlight_g Green component of the highlight color [0 - 255].
* @param[in] highlight_b Blue component of the highlight color [0 - 255].
* @param[in] blend A blending factor that determines the mix between the original color and the tritone colors [0 - 255].
*
* @since 1.0
*/
TVG_API Tvg_Result tvg_scene_push_effect_tritone(Tvg_Paint* scene, int shadow_r, int shadow_g, int shadow_b, int midtone_r, int midtone_g, int midtone_b, int highlight_r, int highlight_g, int highlight_b);
TVG_API Tvg_Result tvg_scene_push_effect_tritone(Tvg_Paint* scene, int shadow_r, int shadow_g, int shadow_b, int midtone_r, int midtone_g, int midtone_b, int highlight_r, int highlight_g, int highlight_b, int blend);
/** \} */ // end defgroup ThorVGCapi_Scene

View file

@ -813,9 +813,9 @@ TVG_API Tvg_Result tvg_scene_push_effect_tint(Tvg_Paint* scene, int black_r, int
}
TVG_API Tvg_Result tvg_scene_push_effect_tritone(Tvg_Paint* scene, int shadow_r, int shadow_g, int shadow_b, int midtone_r, int midtone_g, int midtone_b, int highlight_r, int highlight_g, int highlight_b)
TVG_API Tvg_Result tvg_scene_push_effect_tritone(Tvg_Paint* scene, int shadow_r, int shadow_g, int shadow_b, int midtone_r, int midtone_g, int midtone_b, int highlight_r, int highlight_g, int highlight_b, int blend)
{
if (scene) return (Tvg_Result) reinterpret_cast<Scene*>(scene)->push(SceneEffect::Tritone, shadow_r, shadow_g, shadow_b, midtone_r, midtone_g, midtone_b, highlight_r, highlight_g, highlight_b);
if (scene) return (Tvg_Result) reinterpret_cast<Scene*>(scene)->push(SceneEffect::Tritone, shadow_r, shadow_g, shadow_b, midtone_r, midtone_g, midtone_b, highlight_r, highlight_g, highlight_b, blend);
return TVG_RESULT_INVALID_ARGUMENT;
}

View file

@ -1343,7 +1343,7 @@ void LottieBuilder::updateEffect(LottieLayer* layer, float frameNo)
auto dark = effect->dark(frameNo);
auto midtone = effect->midtone(frameNo);
auto bright = effect->bright(frameNo);
layer->scene->push(SceneEffect::Tritone, dark.rgb[0], dark.rgb[1], dark.rgb[2], midtone.rgb[0], midtone.rgb[1], midtone.rgb[2], bright.rgb[0], bright.rgb[1], bright.rgb[2]);
layer->scene->push(SceneEffect::Tritone, dark.rgb[0], dark.rgb[1], dark.rgb[2], midtone.rgb[0], midtone.rgb[1], midtone.rgb[2], bright.rgb[0], bright.rgb[1], bright.rgb[2], (int)effect->blend(frameNo));
break;
}
case LottieEffect::DropShadow: {

View file

@ -204,6 +204,7 @@ struct LottieFxTritone : LottieEffect
LottieColor bright;
LottieColor midtone;
LottieColor dark;
LottieOpacity blend;
LottieFxTritone()
{

View file

@ -1338,6 +1338,7 @@ void LottieParser::parseTritone(LottieEffect* effect, int idx)
if (idx == 0) parsePropertyInternal(tritone->bright);
else if (idx == 1) parsePropertyInternal(tritone->midtone);
else if (idx == 2) parsePropertyInternal(tritone->dark);
else if (idx == 3) parsePropertyInternal(tritone->blend);
else skip();
}

View file

@ -177,7 +177,7 @@ struct GlDropShadow: GlGaussianBlur {
struct GlEffectParams {
// fill: [0..3]: color
// tint: [0..2]: black, [4..6]: white, [8]: intensity
// tritone: [0..2]: shadow, [4..6]: midtone, [8..10]: highlight
// tritone: [0..2]: shadow, [4..6]: midtone, [8..10]: highlight [11]: blender
float params[4+4+4];
};

View file

@ -1001,6 +1001,9 @@ void GlRenderer::effectFillUpdate(RenderEffectFill* effect, const Matrix& transf
void GlRenderer::effectTintUpdate(RenderEffectTint* effect, const Matrix& transform)
{
effect->valid = (effect->intensity > 0);
if (!effect->valid) return;
auto params = (GlEffectParams*)effect->rd;
if (!params) params = tvg::malloc<GlEffectParams*>(sizeof(GlEffectParams));
params->params[0] = effect->black[0] / 255.0f;
@ -1013,12 +1016,14 @@ void GlRenderer::effectTintUpdate(RenderEffectTint* effect, const Matrix& transf
params->params[7] = 0.0f;
params->params[8] = effect->intensity / 255.0f;
effect->rd = params;
effect->valid = (effect->intensity > 0);
}
void GlRenderer::effectTritoneUpdate(RenderEffectTritone* effect, const Matrix& transform)
{
effect->valid = (effect->blender < 255);
if (!effect->valid) return;
auto params = (GlEffectParams*)effect->rd;
if (!params) params = tvg::malloc<GlEffectParams*>(sizeof(GlEffectParams));
params->params[0] = effect->shadow[0] / 255.0f;
@ -1032,9 +1037,8 @@ void GlRenderer::effectTritoneUpdate(RenderEffectTritone* effect, const Matrix&
params->params[8] = effect->highlight[0] / 255.0f;
params->params[9] = effect->highlight[1] / 255.0f;
params->params[10] = effect->highlight[2] / 255.0f;
params->params[11] = 0.0f;
params->params[11] = effect->blender / 255.0f;
effect->rd = params;
effect->valid = true;
}

View file

@ -889,11 +889,15 @@ void main()
vec4 midtone = uParams.params[1];
vec4 highlight = uParams.params[2];
FragColor = vec4(
vec4 tmp = vec4(
luma >= 0.5f ? mix(midtone.r, highlight.r, (luma - 0.5f)*2.0f) : mix(shadow.r, midtone.r, luma * 2.0f),
luma >= 0.5f ? mix(midtone.g, highlight.g, (luma - 0.5f)*2.0f) : mix(shadow.g, midtone.g, luma * 2.0f),
luma >= 0.5f ? mix(midtone.b, highlight.b, (luma - 0.5f)*2.0f) : mix(shadow.b, midtone.b, luma * 2.0f),
luma >= 0.5f ? mix(midtone.a, highlight.a, (luma - 0.5f)*2.0f) : mix(shadow.a, midtone.a, luma * 2.0f)
) * orig.a;
);
//blender
if (highlight.a > 0.0f) tmp = mix(tmp, orig, highlight.a);
FragColor = tmp * orig.a;
}
)";

View file

@ -540,7 +540,7 @@ static uint32_t _trintone(uint32_t s, uint32_t m, uint32_t h, int l)
void effectTritoneUpdate(RenderEffectTritone* params)
{
params->valid = true;
params->valid = (params->blender < 255);
}
@ -555,7 +555,7 @@ bool effectTritone(SwCompositor* cmp, const RenderEffectTritone* params, bool di
auto opacity = cmp->opacity;
auto luma = cmp->recoverSfc->alphas[2]; //luma function
TVGLOG("SW_ENGINE", "Tritone region(%d, %d, %d, %d), param(%d %d %d, %d %d %d, %d %d %d)", bbox.min.x, bbox.min.y, bbox.max.x, bbox.max.y, params->shadow[0], params->shadow[1], params->shadow[2], params->midtone[0], params->midtone[1], params->midtone[2], params->highlight[0], params->highlight[1], params->highlight[2]);
TVGLOG("SW_ENGINE", "Tritone region(%d, %d, %d, %d), param(%d %d %d, %d %d %d, %d %d %d, %d)", bbox.min.x, bbox.min.y, bbox.max.x, bbox.max.y, params->shadow[0], params->shadow[1], params->shadow[2], params->midtone[0], params->midtone[1], params->midtone[2], params->highlight[0], params->highlight[1], params->highlight[2], params->blender);
if (direct) {
auto dbuffer = cmp->recoverSfc->buf32 + (bbox.min.y * cmp->recoverSfc->stride + bbox.min.x);
@ -563,9 +563,14 @@ bool effectTritone(SwCompositor* cmp, const RenderEffectTritone* params, bool di
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);
*dst = INTERPOLATE(_trintone(shadow, midtone, highlight, luma((uint8_t*)&tmp)), *dst, MULTIPLY(opacity, A(tmp)));
if (params->blender == 0) {
for (size_t x = 0; x < w; ++x, ++dst, ++src) {
*dst = INTERPOLATE(_trintone(shadow, midtone, highlight, luma((uint8_t*)src)), *dst, MULTIPLY(opacity, A(*src)));
}
} else {
for (size_t x = 0; x < w; ++x, ++dst, ++src) {
*dst = INTERPOLATE(INTERPOLATE(*src, _trintone(shadow, midtone, highlight, luma((uint8_t*)src)), params->blender), *dst, MULTIPLY(opacity, A(*src)));
}
}
dbuffer += cmp->image.stride;
sbuffer += cmp->recoverSfc->stride;
@ -575,9 +580,14 @@ bool effectTritone(SwCompositor* cmp, const RenderEffectTritone* params, bool di
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);
*dst = ALPHA_BLEND(_trintone(shadow, midtone, highlight, luma((uint8_t*)&tmp)), A(tmp));
if (params->blender == 0) {
for (size_t x = 0; x < w; ++x, ++dst) {
*dst = ALPHA_BLEND(_trintone(shadow, midtone, highlight, luma((uint8_t*)dst)), MULTIPLY(A(*dst), opacity));
}
} else {
for (size_t x = 0; x < w; ++x, ++dst) {
*dst = ALPHA_BLEND(INTERPOLATE(*dst, _trintone(shadow, midtone, highlight, luma((uint8_t*)dst)), params->blender), MULTIPLY(A(*dst), opacity));
}
}
dbuffer += cmp->image.stride;
}

View file

@ -479,6 +479,7 @@ struct RenderEffectTritone : RenderEffect
uint8_t shadow[3]; //rgb
uint8_t midtone[3]; //rgb
uint8_t highlight[3]; //rgb
uint8_t blender = 0; //0 ~ 255
static RenderEffectTritone* gen(va_list& args)
{
@ -492,6 +493,7 @@ struct RenderEffectTritone : RenderEffect
inst->highlight[0] = va_arg(args, int);
inst->highlight[1] = va_arg(args, int);
inst->highlight[2] = va_arg(args, int);
inst->blender = va_arg(args, int);
inst->type = SceneEffect::Tritone;
return inst;
}

View file

@ -874,11 +874,14 @@ fn cs_main_tritone(@builtin(global_invocation_id) gid: vec3u) {
let shadow = settings[0];
let midtone = settings[1];
let highlight = settings[2];
let color = select(
mix(shadow, midtone, luma * 2.0f),
mix(midtone, highlight, (luma - 0.5f)*2.0f),
luma >= 0.5f
) * orig.a;
textureStore(imageTrg, uid.xy, color);
var color = select(mix(shadow, midtone, luma * 2.0f), mix(midtone, highlight, (luma - 0.5f)*2.0f), luma >= 0.5f);
// blending
if (highlight.a > 0.0f) {
color = mix(color, orig, highlight.a);
}
textureStore(imageTrg, uid.xy, color * orig.a);
}
)";

View file

@ -294,8 +294,8 @@ bool WgShaderTypeEffectParams::update(RenderEffectTritone* tritone)
params[8] = tritone->highlight[0] / 255.0f;
params[9] = tritone->highlight[1] / 255.0f;
params[10] = tritone->highlight[2] / 255.0f;
params[11] = 0.0f;
params[11] = tritone->blender / 255.0f;
tritone->valid = true;
tritone->valid = tritone->blender < 255;
return true;
}