This is an automated email from the git hooks/post-receive script. Git pushed a commit to branch master in repository ffmpeg.
commit 96e0967ecac6cd7aaf4e25c5af72de82b9814c8a Author: Niklas Haas <[email protected]> AuthorDate: Mon Jul 27 13:51:10 2026 +0200 Commit: Niklas Haas <[email protected]> CommitDate: Mon Aug 3 09:32:30 2026 +0000 swscale/cms: fix IPT rounding error av_round16f() was incorrectly scaling to 65534, which would correspond to a neutral PT offset of 32767 = (1 << 15) - 1, but the code was assuming a value of (1 << 15). To fix it, and make the PT channel correctly symmetric around the intended neutral value, we have to map PT = +0.5 to 65536, an unrepresentable value. This is not an issue because the PT channel values are strictly inside some subset of [-0.5, 0.5] in practice, for real in-gamut color values - it's already an envelope that includes quite a bit of safety margin. Similarly, av_round16f() is also the wrong tool for the I/RGB channels, because it incorrectly scaled those to 65534, an off by one of the intended unorm16 full range peak of 65535. This silently resulted in e.g. RGBA64 true white (65535) not round-tripping through the 3DLUT. Fix both by splitting this helper into two separate helpers, each of them fixed to the correct, intended value range. Sponsored-by: Sovereign Tech Fund Signed-off-by: Niklas Haas <[email protected]> --- libswscale/cms.c | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/libswscale/cms.c b/libswscale/cms.c index 1c9c874a55..73448ecebc 100644 --- a/libswscale/cms.c +++ b/libswscale/cms.c @@ -562,9 +562,14 @@ static IPT saturation(const CmsCtx * ctx, IPT ipt) return rgb2ipt(rgb, ctx->dst.content2lms); } -static av_always_inline av_const uint16_t av_round16f(float x) +static av_always_inline av_const uint16_t round_unorm16(float x) { - return av_clip_uint16(x * (UINT16_MAX - 1) + 0.5f); + return av_clip_uint16(x * UINT16_MAX + 0.5f); +} + +static av_always_inline av_const uint16_t round_pt16(float x) +{ + return av_clip_uint16(x * (1 << 16) + 0.5f); } /* Call this whenever the hue changes inside the loop body */ @@ -602,7 +607,6 @@ static void generate_slice(void *priv, int jobnr, int threadnr, int nb_jobs, const float I_scale = 1.0f / (ctx.src.Imax - ctx.src.Imin); const float I_offset = -ctx.src.Imin * I_scale; - const float PT_offset = (float) (1 << 15) / (UINT16_MAX - 1); const float input_scale = 1.0f / (ctx.size_input - 1); const float output_scale_PT = 1.0f / (ctx.size_output_PT - 1); @@ -625,9 +629,9 @@ static void generate_slice(void *priv, int jobnr, int threadnr, int nb_jobs, if (output) { /* Save intermediate value to 3DLUT */ *input++ = (v3u16_t) { - av_round16f(I_scale * ipt.I + I_offset), - av_round16f(ipt.P + PT_offset), - av_round16f(ipt.T + PT_offset), + round_unorm16(I_scale * ipt.I + I_offset), + round_pt16(ipt.P + 0.5f), + round_pt16(ipt.T + 0.5f), }; } else { update_hue_peaks(&ctx, ipt.P, ipt.T); @@ -641,9 +645,9 @@ static void generate_slice(void *priv, int jobnr, int threadnr, int nb_jobs, c[2] = rgb.B; ctx.dst.eotf_inv(ctx.dst.Lw, ctx.dst.Lb, c); *input++ = (v3u16_t) { - av_round16f(c[0]), - av_round16f(c[1]), - av_round16f(c[2]), + round_unorm16(c[0]), + round_unorm16(c[1]), + round_unorm16(c[2]), }; } } @@ -655,9 +659,9 @@ static void generate_slice(void *priv, int jobnr, int threadnr, int nb_jobs, /* Generate split gamut mapping LUT */ for (int Tx = output_start; Tx < output_end; Tx++) { - const float T = output_scale_PT * Tx - PT_offset; + const float T = output_scale_PT * Tx - 0.5f; for (int Px = 0; Px < ctx.size_output_PT; Px++) { - const float P = output_scale_PT * Px - PT_offset; + const float P = output_scale_PT * Px - 0.5f; update_hue_peaks(&ctx, P, T); for (int Ix = 0; Ix < ctx.size_output_I; Ix++) { @@ -667,9 +671,9 @@ static void generate_slice(void *priv, int jobnr, int threadnr, int nb_jobs, double c[3] = { rgb.R, rgb.G, rgb.B }; ctx.dst.eotf_inv(ctx.dst.Lw, ctx.dst.Lb, c); *output++ = (v3u16_t) { - av_round16f(c[0]), - av_round16f(c[1]), - av_round16f(c[2]), + round_unorm16(c[0]), + round_unorm16(c[1]), + round_unorm16(c[2]), }; } } @@ -759,7 +763,7 @@ void ff_sws_tone_map_generate(v2u16_t *lut, int size, const SwsColorMap *map) const float I = src_scale * i + src_offset; IPT ipt = tone_map_apply(&ctx, (IPT) { I, 1.0f }); lut[i] = (v2u16_t) { - av_round16f(dst_scale * ipt.I + dst_offset), + round_unorm16(dst_scale * ipt.I + dst_offset), av_clip_uint16(ipt.P * (1 << 15) + 0.5f), }; } _______________________________________________ ffmpeg-cvslog mailing list -- [email protected] To unsubscribe send an email to [email protected]
