Commit 283197ce11 for ffmpeg
commit 283197ce1107afe182be91cff0fb4b4e00bfb106
Author: Joss Gray <joss@jossgray.net>
Date: Sun Jul 12 14:49:49 2026 -0400
avcodec/notchlc: fix alpha block decoding
Alpha ramp blocks read their endpoint pair and selector word once per
16x16 macroblock and then consumed only three selector bits per 4x4 block,
so every pixel of a block shared one selector and fifteen of the sixteen
blocks decoded from the wrong data. Read the 64-bit alpha word inside the
ramp case, once per 4x4 block, and advance the selector after every
sample.
The ramp mapping also dropped its divisor: alpha0 + (alpha1 - alpha0) *
selector scales the endpoint difference by up to seven instead of
interpolating between the endpoints. Divide by seven, rounding up, as the
chroma ramps already do.
Write the interpolation as the non-negative weighted sum
(alpha0 * (7 - selector) + alpha1 * selector + 6) / 7 rather than the
equivalent difference form. The difference form has a negative dividend
when alpha1 < alpha0, and C truncation toward zero then inverts the
round-up convention, so selector 7 reconstructs alpha1 + 1 instead of
alpha1. The weighted sum rounds up regardless of endpoint ordering and
reconstructs both endpoints exactly. Both forms are bit-identical for
alpha0 <= alpha1; the NotchLC Adobe CC plugin was not observed to emit
descending pairs, so this only affects streams with descending endpoints.
diff --git a/libavcodec/notchlc.c b/libavcodec/notchlc.c
index 396089254e..3d23c4e743 100644
--- a/libavcodec/notchlc.c
+++ b/libavcodec/notchlc.c
@@ -298,7 +298,7 @@ static int decode_blocks(AVCodecContext *avctx, AVFrame *p,
for (int x = 0; x < avctx->width; x += 16) {
unsigned m = bytestream2_get_le32(gb);
unsigned offset = bytestream2_get_le32(gb);
- unsigned alpha0, alpha1;
+ int alpha0, alpha1;
uint64_t control;
if (offset >= UINT_MAX / 4)
@@ -308,10 +308,6 @@ static int decode_blocks(AVCodecContext *avctx, AVFrame *p,
return AVERROR_INVALIDDATA;
bytestream2_seek(&dgb, offset, SEEK_SET);
- control = bytestream2_get_le64(&dgb);
- alpha0 = control & 0xFF;
- alpha1 = (control >> 8) & 0xFF;
- control = control >> 16;
for (int by = 0; by < 4; by++) {
for (int bx = 0; bx < 4; bx++) {
@@ -331,9 +327,16 @@ static int decode_blocks(AVCodecContext *avctx, AVFrame *p,
}
break;
case 2:
+ control = bytestream2_get_le64(&dgb);
+ alpha0 = control & 0xFF;
+ alpha1 = (control >> 8) & 0xFF;
+ control >>= 16;
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
- dsta[x + (i + by * 4) * alinesize + bx * 4 + j] = (alpha0 + (alpha1 - alpha0) * (control & 7)) << 4;
+ const int selector = control & 7;
+
+ dsta[x + (i + by * 4) * alinesize + bx * 4 + j] = ((alpha0 * (7 - selector) + alpha1 * selector + 6) / 7) << 4;
+ control >>= 3;
}
}
break;
@@ -341,7 +344,6 @@ static int decode_blocks(AVCodecContext *avctx, AVFrame *p,
return AVERROR_INVALIDDATA;
}
- control >>= 3;
m >>= 2;
}
}