Commit 153a0ae702 for ffmpeg
commit 153a0ae7020edd9a81eb508ce275516ab6b89258
Author: Niklas Haas <git@haasn.dev>
Date: Fri Oct 2 20:53:14 2026 +0200
swscale/ops: add type[4] to SwsLinearOp
Again, op->type is being replaced by more tightly scoped sources of truth.
Generally, that means the output type of an op is implicit from its
input type. However, SwsLinearOp represents a major challenge, because
being an affine operation, it can express a constant offset with no
corresponding input weight. As such, the *type* of such a constant output
row is ambiguous.
While in theory, we could hyper-specialize the type[] to only affect
inputless constant rows, I think in practice it loses us little and buys
us a lot of sanity to just pin the output type of each row explicitly.
Signed-off-by: Niklas Haas <git@haasn.dev>
diff --git a/libswscale/format.c b/libswscale/format.c
index cf37a5633d..a8988d00ba 100644
--- a/libswscale/format.c
+++ b/libswscale/format.c
@@ -1247,17 +1247,23 @@ static inline AVRational64 av_neg_q64(AVRational64 x)
return (AVRational64) { -x.num, x.den };
}
-static SwsLinearOp fmt_encode_range(const SwsFormat *fmt, bool *incomplete)
+#define LINEAR_OP_DATA(TYPE, ...) ((SwsLinearOp) { \
+ .type = { TYPE, TYPE, TYPE, TYPE }, \
+ .m = { __VA_ARGS__ }, \
+})
+
+static SwsLinearOp fmt_encode_range(const SwsFormat *fmt, SwsPixelType type,
+ bool *incomplete)
{
const AVRational64 q0 = Q(0);
const AVRational64 q1 = Q(1);
- SwsLinearOp c = { .m = {
+ SwsLinearOp c = LINEAR_OP_DATA(type,
{ q1, q0, q0, q0, q0 },
{ q0, q1, q0, q0, q0 },
{ q0, q0, q1, q0, q0 },
{ q0, q0, q0, q1, q0 },
- }};
+ );
const AVPixFmtDescriptor *desc = fmt_desc_decoded(fmt->format);
const int depth0 = desc->comp[0].depth;
@@ -1311,9 +1317,9 @@ static SwsLinearOp fmt_encode_range(const SwsFormat *fmt, bool *incomplete)
return c;
}
-static SwsLinearOp fmt_decode_range(const SwsFormat *fmt, bool *incomplete)
+static SwsLinearOp fmt_decode_range(const SwsFormat *fmt, SwsPixelType type, bool *incomplete)
{
- SwsLinearOp c = fmt_encode_range(fmt, incomplete);
+ SwsLinearOp c = fmt_encode_range(fmt, type, incomplete);
/* Invert main diagonal + offset: x = s * y + k ==> y = (x - k) / s */
for (int i = 0; i < 4; i++) {
@@ -1483,16 +1489,17 @@ static int fmt_dither(SwsContext *ctx, SwsOpList *ops,
#define Q64(x) ff_make_q64((x).num, (x).den)
static inline SwsLinearOp
-linear_mat3(const AVRational m00, const AVRational m01, const AVRational m02,
+linear_mat3(const SwsPixelType type,
+ const AVRational m00, const AVRational m01, const AVRational m02,
const AVRational m10, const AVRational m11, const AVRational m12,
const AVRational m20, const AVRational m21, const AVRational m22)
{
- return (SwsLinearOp) {{
+ return LINEAR_OP_DATA(type,
{ Q64(m00), Q64(m01), Q64(m02), Q(0), Q(0) },
{ Q64(m10), Q64(m11), Q64(m12), Q(0), Q(0) },
{ Q64(m20), Q64(m21), Q64(m22), Q(0), Q(0) },
{ Q(0), Q(0), Q(0), Q(1), Q(0) },
- }};
+ );
}
/* Transform decoded pixel values to normalized RGB */
@@ -1518,7 +1525,7 @@ static int decode_colors(SwsContext *ctx, SwsPixelType type, SwsOpList *ops,
RET(ff_sws_op_list_append(ops, &(SwsOp) {
.type = type,
.op = SWS_OP_LINEAR,
- .lin = fmt_decode_range(fmt, incomplete),
+ .lin = fmt_decode_range(fmt, type, incomplete),
}));
if (fmt->desc->flags & AV_PIX_FMT_FLAG_XYZ)
@@ -1548,7 +1555,7 @@ static int decode_colors(SwsContext *ctx, SwsPixelType type, SwsOpList *ops,
return ff_sws_op_list_append(ops, &(SwsOp) {
.type = type,
.op = SWS_OP_LINEAR,
- .lin = linear_mat3(
+ .lin = linear_mat3(type,
q1, q0, m02,
q1, m11, m12,
q1, m21, q0
@@ -1560,7 +1567,7 @@ static int decode_colors(SwsContext *ctx, SwsPixelType type, SwsOpList *ops,
return ff_sws_op_list_append(ops, &(SwsOp) {
.type = type,
.op = SWS_OP_LINEAR,
- .lin = linear_mat3(
+ .lin = linear_mat3(type,
q1, av_make_q(-1, 1), av_make_q( 1, 1),
q1, av_make_q( 1, 1), av_make_q( 0, 1),
q1, av_make_q(-1, 1), av_make_q(-1, 1)
@@ -1623,7 +1630,7 @@ static int encode_colors(SwsContext *ctx, SwsPixelType type, SwsOpList *ops,
RET(ff_sws_op_list_append(ops, &(SwsOp) {
.type = type,
.op = SWS_OP_LINEAR,
- .lin = linear_mat3(
+ .lin = linear_mat3(type,
c->cr, c->cg, c->cb,
m10, m11, m20,
m20, m21, m22
@@ -1636,7 +1643,7 @@ static int encode_colors(SwsContext *ctx, SwsPixelType type, SwsOpList *ops,
RET(ff_sws_op_list_append(ops, &(SwsOp) {
.type = type,
.op = SWS_OP_LINEAR,
- .lin = linear_mat3(
+ .lin = linear_mat3(type,
av_make_q( 1, 4), av_make_q(1, 2), av_make_q( 1, 4),
av_make_q( 1, 2), av_make_q(0, 1), av_make_q(-1, 2),
av_make_q(-1, 4), av_make_q(1, 2), av_make_q(-1, 4)
@@ -1662,7 +1669,7 @@ static int encode_colors(SwsContext *ctx, SwsPixelType type, SwsOpList *ops,
RET(ff_sws_op_list_append(ops, &(SwsOp) {
.type = type,
.op = SWS_OP_LINEAR,
- .lin = fmt_encode_range(dst, incomplete),
+ .lin = fmt_encode_range(dst, type, incomplete),
}));
if (!(dst->desc->flags & AV_PIX_FMT_FLAG_FLOAT)) {
@@ -1786,12 +1793,12 @@ static int apply_lut3d(SwsContext *ctx, SwsPixelType type, SwsOpList *ops,
RET(ff_sws_op_list_append(ops, &(SwsOp) {
.type = type,
.op = SWS_OP_LINEAR,
- .lin = {{
+ .lin = LINEAR_OP_DATA(type,
{ domain, Q(0), Q(0), Q(0), Q(0) },
{ Q(0), domain, Q(0), Q(0), Q(0) },
{ Q(0), Q(0), domain, Q(0), Q(0) },
{ Q(0), Q(0), Q(0), Q(1), Q(0) },
- }},
+ ),
}));
RET(ff_sws_op_list_append(ops, &(SwsOp) {
@@ -1819,12 +1826,12 @@ static int apply_lut3d(SwsContext *ctx, SwsPixelType type, SwsOpList *ops,
RET(ff_sws_op_list_append(ops, &(SwsOp) {
.type = type,
.op = SWS_OP_LINEAR,
- .lin = {{
+ .lin = LINEAR_OP_DATA(type,
{ inv, Q(0), Q(0), Q(0), Q(0) },
{ Q(0), inv, Q(0), Q(0), Q(0) },
{ Q(0), Q(0), inv, Q(0), Q(0) },
{ Q(0), Q(0), Q(0), Q(1), Q(0) },
- }},
+ ),
}));
return 0;
diff --git a/libswscale/ops.h b/libswscale/ops.h
index addfce7583..ec866f4914 100644
--- a/libswscale/ops.h
+++ b/libswscale/ops.h
@@ -170,6 +170,12 @@ typedef struct SwsDitherOp {
} SwsDitherOp;
typedef struct SwsLinearOp {
+ /**
+ * Pixel type per output component. This must match the type of incoming
+ * values, as determined by the weights matrix.
+ */
+ SwsPixelType type[4];
+
/**
* Generalized 5x5 affine transformation:
* [ Out.x ] = [ A B C D E ]
diff --git a/libswscale/ops_optimizer.c b/libswscale/ops_optimizer.c
index a7ef19e086..0d7f2438df 100644
--- a/libswscale/ops_optimizer.c
+++ b/libswscale/ops_optimizer.c
@@ -182,10 +182,14 @@ static bool op_commute_filter(SwsOp *op, SwsOp *prev)
switch (prev->op) {
case SWS_OP_SWIZZLE:
case SWS_OP_SCALE:
- case SWS_OP_LINEAR:
case SWS_OP_DITHER:
prev->type = op->filter.type;
return true;
+ case SWS_OP_LINEAR:
+ for (int i = 0; i < 4; i++)
+ prev->lin.type[i] = op->filter.type;
+ prev->type = op->filter.type;
+ return true;
case SWS_OP_CONVERT:
case SWS_OP_INVALID:
case SWS_OP_READ:
@@ -502,6 +506,9 @@ retry:
const AVRational64 x = op->clear.value[j];
for (int i = 0; i < 4; i++) {
+ if (!next->lin.m[i][j].num)
+ continue;
+ av_assert0(next->lin.type[i] == op->type);
const AVRational64 kx = ff_mul_q64(next->lin.m[i][j], x);
next->lin.m[i][4] = ff_add_q64(next->lin.m[i][4], kx);
next->lin.m[i][j] = Q(0);
@@ -648,6 +655,7 @@ retry:
const SwsLinearOp m1 = op->lin;
const SwsLinearOp m2 = next->lin;
for (int i = 0; i < 4; i++) {
+ op->lin.type[i] = m2.type[i];
for (int j = 0; j < 5; j++) {
AVRational64 sum = Q(0);
for (int k = 0; k < 4; k++)
@@ -808,6 +816,8 @@ retry:
ff_sws_pixel_type_is_int(next->convert.to) &&
op_result_is_exact(op))
{
+ for (int i = 0; i < 4; i++)
+ op->lin.type[i] = next->convert.to;
op->type = next->convert.to;
FFSWAP(SwsOp, *op, *next);
goto retry;