Commit 01fc714fc4 for ffmpeg
commit 01fc714fc4e65e31f7def59bc8af11b0d4a93f9b
Author: Joss Gray <joss@jossgray.net>
Date: Fri Jul 24 16:18:09 2026 -0400
avcodec/notchlc: fix alpha ramp range expansion
Ramp blocks interpolated the 8-bit endpoints and expanded the result with a
plain << 4, so full opacity decoded to 4080 while case 1 blocks emit 4095.
Fully opaque regions therefore showed a seam wherever the encoder switched
block type.
Expand the endpoints with (a << 4) | (a >> 4) before interpolating and drop the
trailing shift. Full opacity now reconstructs as 4095, and interpolating at 12
bits instead of quantising to 256 levels first also lowers the error against
the source.
Repeating the high nibble is the conventional 8->12 bit expansion and is what
proresdec.c uses for alpha. The chroma ramps in this decoder repeat the low
nibble instead. This differs from a * 4095 / 255, rounded to the nearest
integer, by up to 14 in either direction. No public NotchLC bitstream
reference documents the mapping, but
measuring against the source frames of encodes made with the NotchLC Adobe CC
plugin, mean alpha error over 1.2M ramp pixels is 1.451 with the high nibble
and 4.616 with the low nibble, and the low nibble form leaves a residual that
tracks the source value's low nibble. The chroma path measures the same way and
is left for a separate patch.
diff --git a/libavcodec/notchlc.c b/libavcodec/notchlc.c
index 016d39d129..b520f9a9ea 100644
--- a/libavcodec/notchlc.c
+++ b/libavcodec/notchlc.c
@@ -330,12 +330,14 @@ static int decode_blocks(AVCodecContext *avctx, AVFrame *p,
control = bytestream2_get_le64(&dgb);
alpha0 = control & 0xFF;
alpha1 = (control >> 8) & 0xFF;
+ alpha0 = (alpha0 << 4) | (alpha0 >> 4);
+ alpha1 = (alpha1 << 4) | (alpha1 >> 4);
control >>= 16;
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
const int selector = control & 7;
- dsta[x + (i + by * 4) * alinesize + bx * 4 + j] = ((alpha0 * (7 - selector) + alpha1 * selector + 6) / 7) << 4;
+ dsta[x + (i + by * 4) * alinesize + bx * 4 + j] = (alpha0 * (7 - selector) + alpha1 * selector + 6) / 7;
control >>= 3;
}
}