swscale/ops: split SwsConst up into op-specific structs

It was a bit clunky, lacked semantic contextual information, and made it
harder to reason about the effects of extending this struct. There should be
zero runtime overhead as a result of the fact that this is already a big
union.

I made the changes in this commit by hand, but due to the length and noise
level of the commit, I used Opus 4.6 to verify that I did not accidentally
introduce any bugs or typos.

Signed-off-by: Niklas Haas <git@haasn.dev>
This commit is contained in:
Niklas Haas
2026-03-28 18:45:26 +01:00
committed by Niklas Haas
parent 75b7e8904b
commit 85bef2c2bc
12 changed files with 210 additions and 191 deletions
+5 -5
View File
@@ -851,7 +851,7 @@ static void asmgen_op_pack(SwsAArch64Context *s, const SwsAArch64OpImplParams *p
}
/*********************************************************************/
/* logical left shift of raw pixel values by (u8) */
/* logical left shift of raw pixel values */
/* AARCH64_SWS_OP_LSHIFT */
static void asmgen_op_lshift(SwsAArch64Context *s, const SwsAArch64OpImplParams *p)
@@ -865,7 +865,7 @@ static void asmgen_op_lshift(SwsAArch64Context *s, const SwsAArch64OpImplParams
}
/*********************************************************************/
/* right shift of raw pixel values by (u8) */
/* right shift of raw pixel values */
/* AARCH64_SWS_OP_RSHIFT */
static void asmgen_op_rshift(SwsAArch64Context *s, const SwsAArch64OpImplParams *p)
@@ -1012,7 +1012,7 @@ static void asmgen_op_expand(SwsAArch64Context *s, const SwsAArch64OpImplParams
}
/*********************************************************************/
/* numeric minimum (q4) */
/* numeric minimum */
/* AARCH64_SWS_OP_MIN */
static void asmgen_op_min(SwsAArch64Context *s, const SwsAArch64OpImplParams *p)
@@ -1036,7 +1036,7 @@ static void asmgen_op_min(SwsAArch64Context *s, const SwsAArch64OpImplParams *p)
}
/*********************************************************************/
/* numeric maximum (q4) */
/* numeric maximum */
/* AARCH64_SWS_OP_MAX */
static void asmgen_op_max(SwsAArch64Context *s, const SwsAArch64OpImplParams *p)
@@ -1060,7 +1060,7 @@ static void asmgen_op_max(SwsAArch64Context *s, const SwsAArch64OpImplParams *p)
}
/*********************************************************************/
/* multiplication by scalar (q) */
/* multiplication by scalar */
/* AARCH64_SWS_OP_SCALE */
static void asmgen_op_scale(SwsAArch64Context *s, const SwsAArch64OpImplParams *p)
+5 -5
View File
@@ -181,14 +181,14 @@ static int convert_to_aarch64_impl(SwsContext *ctx, const SwsOpList *ops, int n,
break;
case AARCH64_SWS_OP_LSHIFT:
case AARCH64_SWS_OP_RSHIFT:
out->shift = op->c.u;
out->shift = op->shift.amount;
break;
case AARCH64_SWS_OP_CLEAR:
out->mask = 0;
MASK_SET(out->mask, 0, !!op->c.q4[0].den);
MASK_SET(out->mask, 1, !!op->c.q4[1].den);
MASK_SET(out->mask, 2, !!op->c.q4[2].den);
MASK_SET(out->mask, 3, !!op->c.q4[3].den);
MASK_SET(out->mask, 0, !!op->clear.value[0].den);
MASK_SET(out->mask, 1, !!op->clear.value[1].den);
MASK_SET(out->mask, 2, !!op->clear.value[2].den);
MASK_SET(out->mask, 3, !!op->clear.value[3].den);
break;
case AARCH64_SWS_OP_EXPAND:
case AARCH64_SWS_OP_CONVERT:
+33 -32
View File
@@ -827,7 +827,7 @@ static int cmp_comp(const void *a, const void *b) {
}
static int fmt_analyze_regular(const AVPixFmtDescriptor *desc, SwsReadWriteOp *rw_op,
SwsSwizzleOp *swizzle, int *shift)
SwsSwizzleOp *swizzle, SwsShiftOp *shift)
{
if (desc->nb_components == 2) {
/* YA formats */
@@ -848,7 +848,7 @@ static int fmt_analyze_regular(const AVPixFmtDescriptor *desc, SwsReadWriteOp *r
*swizzle = swiz;
}
*shift = desc->comp[0].shift;
*shift = (SwsShiftOp) { desc->comp[0].shift };
*rw_op = (SwsReadWriteOp) {
.elems = desc->nb_components,
.packed = desc->nb_components > 1 && !(desc->flags & AV_PIX_FMT_FLAG_PLANAR),
@@ -857,8 +857,9 @@ static int fmt_analyze_regular(const AVPixFmtDescriptor *desc, SwsReadWriteOp *r
}
static int fmt_analyze(enum AVPixelFormat fmt, SwsReadWriteOp *rw_op,
SwsPackOp *pack_op, SwsSwizzleOp *swizzle, int *shift,
SwsPixelType *pixel_type, SwsPixelType *raw_type)
SwsPackOp *pack_op, SwsSwizzleOp *swizzle,
SwsShiftOp *shift, SwsPixelType *pixel_type,
SwsPixelType *raw_type)
{
const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(fmt);
if (!desc)
@@ -889,7 +890,7 @@ static int fmt_analyze(enum AVPixelFormat fmt, SwsReadWriteOp *rw_op,
*rw_op = info.rw;
*pack_op = info.pack;
*swizzle = info.swizzle;
*shift = info.shift;
*shift = (SwsShiftOp) { info.shift };
if (info.pack.pattern[0]) {
const int sum = info.pack.pattern[0] + info.pack.pattern[1] +
@@ -923,17 +924,17 @@ static SwsSwizzleOp swizzle_inv(SwsSwizzleOp swiz) {
* it will end up getting pushed towards the output or optimized away entirely
* by the optimization pass.
*/
static SwsConst fmt_clear(enum AVPixelFormat fmt)
static SwsClearOp fmt_clear(enum AVPixelFormat fmt)
{
const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(fmt);
const bool has_chroma = desc->nb_components >= 3;
const bool has_alpha = desc->flags & AV_PIX_FMT_FLAG_ALPHA;
SwsConst c = {0};
SwsClearOp c = {0};
if (!has_chroma)
c.q4[1] = c.q4[2] = Q0;
c.value[1] = c.value[2] = Q0;
if (!has_alpha)
c.q4[3] = Q0;
c.value[3] = Q0;
return c;
}
@@ -952,7 +953,7 @@ int ff_sws_decode_pixfmt(SwsOpList *ops, enum AVPixelFormat fmt)
SwsSwizzleOp swizzle;
SwsPackOp unpack;
SwsComps *comps = &ops->comps_src;
int shift;
SwsShiftOp shift;
RET(fmt_analyze(fmt, &rw_op, &unpack, &swizzle, &shift,
&pixel_type, &raw_type));
@@ -974,7 +975,7 @@ int ff_sws_decode_pixfmt(SwsOpList *ops, enum AVPixelFormat fmt)
* canonical order {Y, U, V, A} */
const int is_ya = desc->nb_components == 2;
for (int c = 0; c < desc->nb_components; c++) {
const int bits = desc->comp[c].depth + shift;
const int bits = desc->comp[c].depth + shift.amount;
const int idx = swizzle.in[is_ya ? 3 * c : c];
comps->min[idx] = Q0;
if (bits < 32) /* FIXME: AVRational is limited to INT_MAX */
@@ -1016,18 +1017,18 @@ int ff_sws_decode_pixfmt(SwsOpList *ops, enum AVPixelFormat fmt)
.swizzle = swizzle,
}));
if (shift) {
if (shift.amount) {
RET(ff_sws_op_list_append(ops, &(SwsOp) {
.op = SWS_OP_RSHIFT,
.type = pixel_type,
.c.u = shift,
.op = SWS_OP_RSHIFT,
.type = pixel_type,
.shift = shift,
}));
}
RET(ff_sws_op_list_append(ops, &(SwsOp) {
.op = SWS_OP_CLEAR,
.type = pixel_type,
.c = fmt_clear(fmt),
.op = SWS_OP_CLEAR,
.type = pixel_type,
.clear = fmt_clear(fmt),
}));
return 0;
@@ -1040,16 +1041,16 @@ int ff_sws_encode_pixfmt(SwsOpList *ops, enum AVPixelFormat fmt)
SwsReadWriteOp rw_op;
SwsSwizzleOp swizzle;
SwsPackOp pack;
int shift;
SwsShiftOp shift;
RET(fmt_analyze(fmt, &rw_op, &pack, &swizzle, &shift,
&pixel_type, &raw_type));
if (shift) {
if (shift.amount) {
RET(ff_sws_op_list_append(ops, &(SwsOp) {
.op = SWS_OP_LSHIFT,
.type = pixel_type,
.c.u = shift,
.op = SWS_OP_LSHIFT,
.type = pixel_type,
.shift = shift,
}));
}
@@ -1059,7 +1060,7 @@ int ff_sws_encode_pixfmt(SwsOpList *ops, enum AVPixelFormat fmt)
RET(ff_sws_op_list_append(ops, &(SwsOp) {
.op = SWS_OP_CLEAR,
.type = pixel_type,
.c.q4[3] = Q0,
.clear.value[3] = Q0,
}));
}
@@ -1513,26 +1514,26 @@ int ff_sws_encode_colors(SwsContext *ctx, SwsPixelType type,
}));
if (!(dst->desc->flags & AV_PIX_FMT_FLAG_FLOAT)) {
SwsConst range = {0};
SwsClampOp range = {0};
const bool is_ya = dst->desc->nb_components == 2;
for (int i = 0; i < dst->desc->nb_components; i++) {
/* Clamp to legal pixel range */
const int idx = i * (is_ya ? 3 : 1);
range.q4[idx] = Q((1 << dst->desc->comp[i].depth) - 1);
range.limit[idx] = Q((1 << dst->desc->comp[i].depth) - 1);
}
RET(fmt_dither(ctx, ops, type, src, dst));
RET(ff_sws_op_list_append(ops, &(SwsOp) {
.op = SWS_OP_MAX,
.type = type,
.c.q4 = { Q0, Q0, Q0, Q0 },
.op = SWS_OP_MAX,
.type = type,
.clamp = {{ Q0, Q0, Q0, Q0 }},
}));
RET(ff_sws_op_list_append(ops, &(SwsOp) {
.op = SWS_OP_MIN,
.type = type,
.c = range,
.op = SWS_OP_MIN,
.type = type,
.clamp = range,
}));
}
+23 -23
View File
@@ -177,13 +177,13 @@ void ff_sws_apply_op_q(const SwsOp *op, AVRational x[4])
return;
case SWS_OP_CLEAR:
for (int i = 0; i < 4; i++) {
if (op->c.q4[i].den)
x[i] = op->c.q4[i];
if (op->clear.value[i].den)
x[i] = op->clear.value[i];
}
return;
case SWS_OP_LSHIFT: {
av_assert1(ff_sws_pixel_type_is_int(op->type));
AVRational mult = Q(1 << op->c.u);
AVRational mult = Q(1 << op->shift.amount);
for (int i = 0; i < 4; i++)
x[i] = x[i].den ? av_mul_q(x[i], mult) : x[i];
return;
@@ -191,7 +191,7 @@ void ff_sws_apply_op_q(const SwsOp *op, AVRational x[4])
case SWS_OP_RSHIFT: {
av_assert1(ff_sws_pixel_type_is_int(op->type));
for (int i = 0; i < 4; i++)
x[i] = x[i].den ? Q((x[i].num / x[i].den) >> op->c.u) : x[i];
x[i] = x[i].den ? Q((x[i].num / x[i].den) >> op->shift.amount) : x[i];
return;
}
case SWS_OP_SWIZZLE: {
@@ -219,11 +219,11 @@ void ff_sws_apply_op_q(const SwsOp *op, AVRational x[4])
return;
case SWS_OP_MIN:
for (int i = 0; i < 4; i++)
x[i] = av_min_q(x[i], op->c.q4[i]);
x[i] = av_min_q(x[i], op->clamp.limit[i]);
return;
case SWS_OP_MAX:
for (int i = 0; i < 4; i++)
x[i] = av_max_q(x[i], op->c.q4[i]);
x[i] = av_max_q(x[i], op->clamp.limit[i]);
return;
case SWS_OP_LINEAR: {
av_assert1(!ff_sws_pixel_type_is_int(op->type));
@@ -238,7 +238,7 @@ void ff_sws_apply_op_q(const SwsOp *op, AVRational x[4])
}
case SWS_OP_SCALE:
for (int i = 0; i < 4; i++)
x[i] = x[i].den ? av_mul_q(x[i], op->c.q) : x[i];
x[i] = x[i].den ? av_mul_q(x[i], op->scale.factor) : x[i];
return;
case SWS_OP_FILTER_H:
case SWS_OP_FILTER_V:
@@ -362,11 +362,11 @@ void ff_sws_op_list_update_comps(SwsOpList *ops)
break;
case SWS_OP_MIN:
propagate_flags(op, &prev);
clear_undefined_values(op->comps.max, op->c.q4);
clear_undefined_values(op->comps.max, op->clamp.limit);
break;
case SWS_OP_MAX:
propagate_flags(op, &prev);
clear_undefined_values(op->comps.min, op->c.q4);
clear_undefined_values(op->comps.min, op->clamp.limit);
break;
case SWS_OP_DITHER:
for (int i = 0; i < 4; i++) {
@@ -405,11 +405,11 @@ void ff_sws_op_list_update_comps(SwsOpList *ops)
}
case SWS_OP_CLEAR:
for (int i = 0; i < 4; i++) {
if (op->c.q4[i].den) {
if (op->clear.value[i].den) {
op->comps.flags[i] = 0;
if (op->c.q4[i].num == 0)
if (op->clear.value[i].num == 0)
op->comps.flags[i] |= SWS_COMP_ZERO;
if (op->c.q4[i].den == 1)
if (op->clear.value[i].den == 1)
op->comps.flags[i] |= SWS_COMP_EXACT;
} else {
op->comps.flags[i] = prev.flags[i];
@@ -460,9 +460,9 @@ void ff_sws_op_list_update_comps(SwsOpList *ops)
case SWS_OP_SCALE:
for (int i = 0; i < 4; i++) {
op->comps.flags[i] = prev.flags[i];
if (op->c.q.den != 1) /* fractional scale */
if (op->scale.factor.den != 1) /* fractional scale */
op->comps.flags[i] &= ~SWS_COMP_EXACT;
if (op->c.q.num < 0)
if (op->scale.factor.num < 0)
FFSWAP(AVRational, op->comps.min[i], op->comps.max[i]);
}
break;
@@ -524,7 +524,7 @@ void ff_sws_op_list_update_comps(SwsOpList *ops)
break;
case SWS_OP_CLEAR:
for (int i = 0; i < 4; i++) {
if (!op->c.q4[i].den)
if (!op->clear.value[i].den)
need_in[i] = need_out[i];
}
break;
@@ -839,10 +839,10 @@ void ff_sws_op_desc(AVBPrint *bp, const SwsOp *op)
op->rw.filter == SWS_OP_FILTER_H ? 'H' : 'V');
break;
case SWS_OP_LSHIFT:
av_bprintf(bp, "%-20s: << %u", name, op->c.u);
av_bprintf(bp, "%-20s: << %u", name, op->shift.amount);
break;
case SWS_OP_RSHIFT:
av_bprintf(bp, "%-20s: >> %u", name, op->c.u);
av_bprintf(bp, "%-20s: >> %u", name, op->shift.amount);
break;
case SWS_OP_PACK:
case SWS_OP_UNPACK:
@@ -852,7 +852,7 @@ void ff_sws_op_desc(AVBPrint *bp, const SwsOp *op)
break;
case SWS_OP_CLEAR:
av_bprintf(bp, "%-20s: ", name);
print_q4(bp, op->c.q4, true, op->comps.flags);
print_q4(bp, op->clear.value, true, op->comps.flags);
break;
case SWS_OP_SWIZZLE:
av_bprintf(bp, "%-20s: %d%d%d%d", name,
@@ -872,11 +872,11 @@ void ff_sws_op_desc(AVBPrint *bp, const SwsOp *op)
break;
case SWS_OP_MIN:
av_bprintf(bp, "%-20s: x <= ", name);
print_q4(bp, op->c.q4, true, op->comps.flags);
print_q4(bp, op->clamp.limit, true, op->comps.flags);
break;
case SWS_OP_MAX:
av_bprintf(bp, "%-20s: ", name);
print_q4(bp, op->c.q4, true, op->comps.flags);
print_q4(bp, op->clamp.limit, true, op->comps.flags);
av_bprintf(bp, " <= x");
break;
case SWS_OP_LINEAR:
@@ -892,9 +892,9 @@ void ff_sws_op_desc(AVBPrint *bp, const SwsOp *op)
av_bprintf(bp, "]");
break;
case SWS_OP_SCALE:
av_bprintf(bp, "%-20s: * %d", name, op->c.q.num);
if (op->c.q.den != 1)
av_bprintf(bp, "/%d", op->c.q.den);
av_bprintf(bp, "%-20s: * %d", name, op->scale.factor.num);
if (op->scale.factor.den != 1)
av_bprintf(bp, "/%d", op->scale.factor.den);
break;
case SWS_OP_FILTER_H:
case SWS_OP_FILTER_V: {
+25 -16
View File
@@ -55,15 +55,15 @@ typedef enum SwsOpType {
/* Bit manipulation operations. Defined for integers only. */
SWS_OP_UNPACK, /* split tightly packed data into components */
SWS_OP_PACK, /* compress components into tightly packed data */
SWS_OP_LSHIFT, /* logical left shift of raw pixel values by (u8) */
SWS_OP_RSHIFT, /* right shift of raw pixel values by (u8) */
SWS_OP_LSHIFT, /* logical left shift of raw pixel values */
SWS_OP_RSHIFT, /* right shift of raw pixel values */
/* Generic arithmetic. Defined and implemented for all types */
SWS_OP_CLEAR, /* clear pixel values */
SWS_OP_CONVERT, /* convert (cast) between formats */
SWS_OP_MIN, /* numeric minimum (q4) */
SWS_OP_MAX, /* numeric maximum (q4) */
SWS_OP_SCALE, /* multiplication by scalar (q) */
SWS_OP_MIN, /* numeric minimum */
SWS_OP_MAX, /* numeric maximum */
SWS_OP_SCALE, /* multiplication by scalar */
/* Floating-point only arithmetic operations. */
SWS_OP_LINEAR, /* generalized linear affine transform */
@@ -87,16 +87,6 @@ typedef enum SwsCompFlags {
#define SWS_OP_NEEDED(op, idx) (!((op)->comps.flags[idx] & SWS_COMP_GARBAGE))
typedef union SwsConst {
/* Generic constant value */
AVRational q4[4];
AVRational q;
unsigned u;
} SwsConst;
static_assert(sizeof(SwsConst) == sizeof(AVRational) * 4,
"First field of SwsConst should span the entire union");
typedef struct SwsComps {
SwsCompFlags flags[4]; /* knowledge about (output) component contents */
bool unused[4]; /* which input components are definitely unused */
@@ -152,11 +142,27 @@ typedef struct SwsSwizzleOp {
#define SWS_SWIZZLE(X,Y,Z,W) ((SwsSwizzleOp) { .in = {X, Y, Z, W} })
typedef struct SwsShiftOp {
uint8_t amount; /* number of bits to shift */
} SwsShiftOp;
typedef struct SwsClearOp {
AVRational value[4]; /* value to set, or {0, 0} for no-op */
} SwsClearOp;
typedef struct SwsConvertOp {
SwsPixelType to; /* type of pixel to convert to */
bool expand; /* if true, integers are expanded to the full range */
} SwsConvertOp;
typedef struct SwsClampOp {
AVRational limit[4]; /* per-component min/max value */
} SwsClampOp;
typedef struct SwsScaleOp {
AVRational factor; /* scalar multiplication factor */
} SwsScaleOp;
typedef struct SwsDitherOp {
AVRational *matrix; /* tightly packed dither matrix (refstruct) */
AVRational min, max; /* minimum/maximum value in `matrix` */
@@ -217,10 +223,13 @@ typedef struct SwsOp {
SwsReadWriteOp rw;
SwsPackOp pack;
SwsSwizzleOp swizzle;
SwsShiftOp shift;
SwsClearOp clear;
SwsConvertOp convert;
SwsClampOp clamp;
SwsScaleOp scale;
SwsDitherOp dither;
SwsFilterOp filter;
SwsConst c;
};
/**
+10 -8
View File
@@ -115,7 +115,9 @@ static int op_match(const SwsOp *op, const SwsOpEntry *entry)
if (op->op == SWS_OP_CLEAR) {
/* Clear pattern must match exactly, regardless of `entry->flexible` */
for (int i = 0; i < 4; i++) {
if (SWS_OP_NEEDED(op, i) && entry->unused[i] != !!op->c.q4[i].den)
if (!SWS_OP_NEEDED(op, i))
continue;
if (entry->unused[i] != !!op->clear.value[i].den)
return 0;
}
}
@@ -147,9 +149,9 @@ static int op_match(const SwsOp *op, const SwsOpEntry *entry)
return score;
case SWS_OP_CLEAR:
for (int i = 0; i < 4; i++) {
if (!op->c.q4[i].den || !SWS_OP_NEEDED(op, i))
if (!op->clear.value[i].den || !SWS_OP_NEEDED(op, i))
continue;
if (av_cmp_q(op->c.q4[i], Q(entry->clear_value)))
if (av_cmp_q(op->clear.value[i], Q(entry->clear_value)))
return 0;
}
return score;
@@ -188,7 +190,7 @@ static int op_match(const SwsOp *op, const SwsOpEntry *entry)
score += av_popcount(SWS_MASK_ALL ^ entry->linear_mask);
return score;
case SWS_OP_SCALE:
return av_cmp_q(op->c.q, entry->scale) ? 0 : score;
return av_cmp_q(op->scale.factor, entry->scale) ? 0 : score;
case SWS_OP_FILTER_H:
case SWS_OP_FILTER_V:
return score;
@@ -265,14 +267,14 @@ int ff_sws_op_compile_tables(SwsContext *ctx, const SwsOpTable *const tables[],
int ff_sws_setup_shift(const SwsImplParams *params, SwsImplResult *out)
{
out->priv.u8[0] = params->op->c.u;
out->priv.u8[0] = params->op->shift.amount;
return 0;
}
int ff_sws_setup_scale(const SwsImplParams *params, SwsImplResult *out)
{
const SwsOp *op = params->op;
const AVRational factor = op->c.q;
const AVRational factor = op->scale.factor;
switch (op->type) {
case SWS_PIXEL_U8: out->priv.u8[0] = q2pixel(uint8_t, factor); break;
case SWS_PIXEL_U16: out->priv.u16[0] = q2pixel(uint16_t, factor); break;
@@ -288,7 +290,7 @@ int ff_sws_setup_clamp(const SwsImplParams *params, SwsImplResult *out)
{
const SwsOp *op = params->op;
for (int i = 0; i < 4; i++) {
const AVRational limit = op->c.q4[i];
const AVRational limit = op->clamp.limit[i];
switch (op->type) {
case SWS_PIXEL_U8: out->priv.u8[i] = q2pixel(uint8_t, limit); break;
case SWS_PIXEL_U16: out->priv.u16[i] = q2pixel(uint16_t, limit); break;
@@ -305,7 +307,7 @@ int ff_sws_setup_clear(const SwsImplParams *params, SwsImplResult *out)
{
const SwsOp *op = params->op;
for (int i = 0; i < 4; i++) {
const AVRational value = op->c.q4[i];
const AVRational value = op->clear.value[i];
if (!value.den)
continue;
switch (op->type) {
+4 -4
View File
@@ -86,20 +86,20 @@ static int compile(SwsContext *ctx, SwsOpList *ops, SwsCompiledOp *out)
case SWS_OP_CLEAR:
for (int i = 0; i < 4; i++) {
if (!op->c.q4[i].den)
if (!op->clear.value[i].den)
continue;
if (op->c.q4[i].den != 1)
if (op->clear.value[i].den != 1)
return AVERROR(ENOTSUP);
/* Ensure all bytes to be cleared are the same, because we
* can't memset on multi-byte sequences */
uint8_t val = op->c.q4[i].num & 0xFF;
uint8_t val = op->clear.value[i].num & 0xFF;
uint32_t ref = val;
switch (ff_sws_pixel_type_size(op->type)) {
case 2: ref *= 0x101; break;
case 4: ref *= 0x1010101; break;
}
if (ref != op->c.q4[i].num)
if (ref != op->clear.value[i].num)
return AVERROR(ENOTSUP);
p.clear_value[i] = val;
p.index[i] = -1;
+49 -47
View File
@@ -39,7 +39,7 @@
*/
static bool op_commute_clear(SwsOp *op, SwsOp *next)
{
SwsOp tmp;
SwsClearOp tmp;
av_assert1(op->op == SWS_OP_CLEAR);
switch (next->op) {
@@ -56,21 +56,21 @@ static bool op_commute_clear(SwsOp *op, SwsOp *next)
case SWS_OP_SWIZZLE:
case SWS_OP_FILTER_H:
case SWS_OP_FILTER_V:
ff_sws_apply_op_q(next, op->c.q4);
ff_sws_apply_op_q(next, op->clear.value);
return true;
case SWS_OP_SWAP_BYTES:
switch (next->type) {
case SWS_PIXEL_U16:
ff_sws_apply_op_q(next, op->c.q4); /* always works */
ff_sws_apply_op_q(next, op->clear.value); /* always works */
return true;
case SWS_PIXEL_U32:
for (int i = 0; i < 4; i++) {
uint32_t v = av_bswap32(op->c.q4[i].num);
uint32_t v = av_bswap32(op->clear.value[i].num);
if (v > INT_MAX)
return false; /* can't represent as AVRational anymore */
tmp.c.q4[i] = Q(v);
tmp.value[i] = Q(v);
}
op->c = tmp.c;
op->clear = tmp;
return true;
default:
return false;
@@ -124,14 +124,14 @@ static bool op_commute_swizzle(SwsOp *op, SwsOp *next)
*/
case SWS_OP_MIN:
case SWS_OP_MAX: {
const SwsConst c = next->c;
const SwsClampOp c = next->clamp;
for (int i = 0; i < 4; i++) {
if (!SWS_OP_NEEDED(op, i))
continue;
const int j = op->swizzle.in[i];
if (seen[j] && av_cmp_q(next->c.q4[j], c.q4[i]))
if (seen[j] && av_cmp_q(next->clamp.limit[j], c.limit[i]))
return false;
next->c.q4[j] = c.q4[i];
next->clamp.limit[j] = c.limit[i];
seen[j] = true;
}
return true;
@@ -238,9 +238,9 @@ static int exact_log2_q(const AVRational x)
* the corresponding scaling factor, or 0 otherwise.
*/
static bool extract_scalar(const SwsLinearOp *c, SwsComps comps, SwsComps prev,
SwsConst *out_scale)
SwsScaleOp *out_scale)
{
SwsConst scale = {0};
SwsScaleOp scale = {0};
/* There are components not on the main diagonal */
if (c->mask & ~SWS_MASK_DIAG4)
@@ -251,21 +251,21 @@ static bool extract_scalar(const SwsLinearOp *c, SwsComps comps, SwsComps prev,
if ((prev.flags[i] & SWS_COMP_ZERO) ||
(comps.flags[i] & SWS_COMP_GARBAGE))
continue;
if (scale.q.den && av_cmp_q(s, scale.q))
if (scale.factor.den && av_cmp_q(s, scale.factor))
return false;
scale.q = s;
scale.factor = s;
}
if (scale.q.den)
if (scale.factor.den)
*out_scale = scale;
return scale.q.den;
return scale.factor.den;
}
/* Extracts an integer clear operation (subset) from the given linear op. */
static bool extract_constant_rows(SwsLinearOp *c, SwsComps prev,
SwsConst *out_clear)
SwsClearOp *out_clear)
{
SwsConst clear = {0};
SwsClearOp clear = {0};
bool ret = false;
for (int i = 0; i < 4; i++) {
@@ -275,7 +275,7 @@ static bool extract_constant_rows(SwsLinearOp *c, SwsComps prev,
(prev.flags[j] & SWS_COMP_ZERO); /* input is zero */
}
if (const_row && (c->mask & SWS_MASK_ROW(i))) {
clear.q4[i] = c->m[i][4];
clear.value[i] = c->m[i][4];
for (int j = 0; j < 5; j++)
c->m[i][j] = Q(i == j);
c->mask &= ~SWS_MASK_ROW(i);
@@ -428,13 +428,13 @@ retry:
case SWS_OP_RSHIFT:
/* Two shifts in the same direction */
if (next->op == op->op) {
op->c.u += next->c.u;
op->shift.amount += next->shift.amount;
ff_sws_op_list_remove_at(ops, n + 1, 1);
goto retry;
}
/* No-op shift */
if (!op->c.u) {
if (!op->shift.amount) {
ff_sws_op_list_remove_at(ops, n, 1);
goto retry;
}
@@ -442,19 +442,19 @@ retry:
case SWS_OP_CLEAR:
for (int i = 0; i < 4; i++) {
if (!op->c.q4[i].den)
if (!op->clear.value[i].den)
continue;
if ((prev->comps.flags[i] & SWS_COMP_ZERO) &&
!(prev->comps.flags[i] & SWS_COMP_GARBAGE) &&
op->c.q4[i].num == 0)
op->clear.value[i].num == 0)
{
/* Redundant clear-to-zero of zero component */
op->c.q4[i].den = 0;
op->clear.value[i].den = 0;
} else if (!SWS_OP_NEEDED(op, i)) {
/* Unnecessary clear of unused component */
op->c.q4[i] = (AVRational) {0, 0};
} else if (op->c.q4[i].den) {
op->clear.value[i] = (AVRational) {0, 0};
} else if (op->clear.value[i].den) {
noop = false;
}
}
@@ -467,8 +467,8 @@ retry:
/* Transitive clear */
if (next->op == SWS_OP_CLEAR) {
for (int i = 0; i < 4; i++) {
if (next->c.q4[i].den)
op->c.q4[i] = next->c.q4[i];
if (next->clear.value[i].den)
op->clear.value[i] = next->clear.value[i];
}
ff_sws_op_list_remove_at(ops, n + 1, 1);
goto retry;
@@ -548,7 +548,8 @@ retry:
if (next->op == SWS_OP_SCALE && !op->convert.expand &&
ff_sws_pixel_type_is_int(op->type) &&
ff_sws_pixel_type_is_int(op->convert.to) &&
!av_cmp_q(next->c.q, ff_sws_pixel_expand(op->type, op->convert.to)))
!av_cmp_q(next->scale.factor,
ff_sws_pixel_expand(op->type, op->convert.to)))
{
op->convert.expand = true;
ff_sws_op_list_remove_at(ops, n + 1, 1);
@@ -558,9 +559,9 @@ retry:
case SWS_OP_MIN:
for (int i = 0; i < 4; i++) {
if (!SWS_OP_NEEDED(op, i) || !op->c.q4[i].den)
if (!SWS_OP_NEEDED(op, i) || !op->clamp.limit[i].den)
continue;
if (av_cmp_q(op->c.q4[i], prev->comps.max[i]) < 0)
if (av_cmp_q(op->clamp.limit[i], prev->comps.max[i]) < 0)
noop = false;
}
@@ -572,9 +573,9 @@ retry:
case SWS_OP_MAX:
for (int i = 0; i < 4; i++) {
if (!SWS_OP_NEEDED(op, i) || !op->c.q4[i].den)
if (!SWS_OP_NEEDED(op, i) || !op->clamp.limit[i].den)
continue;
if (av_cmp_q(prev->comps.min[i], op->c.q4[i]) < 0)
if (av_cmp_q(prev->comps.min[i], op->clamp.limit[i]) < 0)
noop = false;
}
@@ -604,7 +605,8 @@ retry:
case SWS_OP_LINEAR: {
SwsSwizzleOp swizzle;
SwsConst c;
SwsClearOp clear;
SwsScaleOp scale;
/* No-op (identity) linear operation */
if (!op->lin.mask) {
@@ -654,20 +656,20 @@ retry:
}
/* Convert constant rows to explicit clear instruction */
if (extract_constant_rows(&op->lin, prev->comps, &c)) {
if (extract_constant_rows(&op->lin, prev->comps, &clear)) {
RET(ff_sws_op_list_insert_at(ops, n + 1, &(SwsOp) {
.op = SWS_OP_CLEAR,
.type = op->type,
.comps = op->comps,
.c = c,
.clear = clear,
}));
goto retry;
}
/* Multiplication by scalar constant */
if (extract_scalar(&op->lin, op->comps, prev->comps, &c)) {
op->op = SWS_OP_SCALE;
op->c = c;
if (extract_scalar(&op->lin, op->comps, prev->comps, &scale)) {
op->op = SWS_OP_SCALE;
op->scale = scale;
goto retry;
}
@@ -684,20 +686,20 @@ retry:
}
case SWS_OP_SCALE: {
const int factor2 = exact_log2_q(op->c.q);
const int factor2 = exact_log2_q(op->scale.factor);
/* No-op scaling */
if (op->c.q.num == 1 && op->c.q.den == 1) {
if (op->scale.factor.num == 1 && op->scale.factor.den == 1) {
ff_sws_op_list_remove_at(ops, n, 1);
goto retry;
}
/* Merge consecutive scaling operations (that don't overflow) */
if (next->op == SWS_OP_SCALE) {
int64_t p = op->c.q.num * (int64_t) next->c.q.num;
int64_t q = op->c.q.den * (int64_t) next->c.q.den;
int64_t p = op->scale.factor.num * (int64_t) next->scale.factor.num;
int64_t q = op->scale.factor.den * (int64_t) next->scale.factor.den;
if (FFABS(p) <= INT_MAX && FFABS(q) <= INT_MAX) {
av_reduce(&op->c.q.num, &op->c.q.den, p, q, INT_MAX);
av_reduce(&op->scale.factor.num, &op->scale.factor.den, p, q, INT_MAX);
ff_sws_op_list_remove_at(ops, n + 1, 1);
goto retry;
}
@@ -706,7 +708,7 @@ retry:
/* Scaling by exact power of two */
if (factor2 && ff_sws_pixel_type_is_int(op->type)) {
op->op = factor2 > 0 ? SWS_OP_LSHIFT : SWS_OP_RSHIFT;
op->c.u = FFABS(factor2);
op->shift.amount = FFABS(factor2);
goto retry;
}
break;
@@ -760,7 +762,7 @@ retry:
case SWS_OP_SCALE:
/* Scaling by integer before conversion to int */
if (op->c.q.den == 1 && next->op == SWS_OP_CONVERT &&
if (op->scale.factor.den == 1 && next->op == SWS_OP_CONVERT &&
ff_sws_pixel_type_is_int(next->convert.to))
{
op->type = next->convert.to;
@@ -812,9 +814,9 @@ int ff_sws_solve_shuffle(const SwsOpList *const ops, uint8_t shuffle[],
case SWS_OP_CLEAR:
for (int i = 0; i < 4; i++) {
if (!op->c.q4[i].den)
if (!op->clear.value[i].den)
continue;
if (op->c.q4[i].num != 0 || !clear_val)
if (op->clear.value[i].num != 0 || !clear_val)
return AVERROR(ENOTSUP);
mask[i] = 0x1010101ul * clear_val;
}
+6 -1
View File
@@ -65,11 +65,16 @@ static int register_op(SwsContext *ctx, void *opaque, SwsOp *op)
}
break;
case SWS_OP_SCALE:
op->scale.factor = (AVRational) { 0, 1 };
break;
case SWS_OP_MIN:
case SWS_OP_MAX:
for (int i = 0; i < 4; i++)
op->clamp.limit[i] = (AVRational) { 0, 1 };
break;
case SWS_OP_CLEAR:
for (int i = 0; i < 4; i++)
op->c.q4[i] = (AVRational) { 0, !!op->c.q4[i].den };
op->clear.value[i] = (AVRational) { 0, !!op->clear.value[i].den };
break;
case SWS_OP_DITHER:
/* Strip arbitrary offset */
+6 -6
View File
@@ -274,32 +274,32 @@ static int add_ops_glsl(VulkanPriv *p, FFVulkanOpsCtx *s,
}
case SWS_OP_CLEAR: {
for (int i = 0; i < 4; i++) {
if (!op->c.q4[i].den)
if (!op->clear.value[i].den)
continue;
av_bprintf(&shd->src, " %s.%c = %s"QSTR";\n", type_name,
"xyzw"[i], type_s, QTYPE(op->c.q4[i]));
"xyzw"[i], type_s, QTYPE(op->clear.value[i]));
}
break;
}
case SWS_OP_SCALE:
av_bprintf(&shd->src, " %s = %s * "QSTR";\n",
type_name, type_name, QTYPE(op->c.q));
type_name, type_name, QTYPE(op->scale.factor));
break;
case SWS_OP_MIN:
case SWS_OP_MAX:
for (int i = 0; i < 4; i++) {
if (!op->c.q4[i].den)
if (!op->clamp.limit[i].den)
continue;
av_bprintf(&shd->src, " %s.%c = %s(%s.%c, "QSTR");\n",
type_name, "xyzw"[i],
op->op == SWS_OP_MIN ? "min" : "max",
type_name, "xyzw"[i], QTYPE(op->c.q4[i]));
type_name, "xyzw"[i], QTYPE(op->clamp.limit[i]));
}
break;
case SWS_OP_LSHIFT:
case SWS_OP_RSHIFT:
av_bprintf(&shd->src, " %s %s= %i;\n", type_name,
op->op == SWS_OP_LSHIFT ? "<<" : ">>", op->c.u);
op->op == SWS_OP_LSHIFT ? "<<" : ">>", op->shift.amount);
break;
case SWS_OP_CONVERT:
if (ff_sws_pixel_type_is_int(cur_type) && op->convert.expand) {
+5 -5
View File
@@ -133,7 +133,7 @@ static int setup_clear(const SwsImplParams *params, SwsImplResult *out)
{
const SwsOp *op = params->op;
for (int i = 0; i < 4; i++)
out->priv.u32[i] = (uint32_t) op->c.q4[i].num;
out->priv.u32[i] = (uint32_t) op->clear.value[i].num;
return 0;
}
@@ -165,7 +165,7 @@ static int setup_clear(const SwsImplParams *params, SwsImplResult *out)
static int setup_shift(const SwsImplParams *params, SwsImplResult *out)
{
out->priv.u16[0] = params->op->c.u;
out->priv.u16[0] = params->op->shift.amount;
return 0;
}
@@ -944,7 +944,7 @@ static void normalize_clear(SwsOp *op)
ff_sws_setup_clear(&(const SwsImplParams) { .op = op }, &res);
for (int i = 0; i < 4; i++) {
if (!op->c.q4[i].den)
if (!op->clear.value[i].den)
continue;
switch (ff_sws_pixel_type_size(op->type)) {
case 1: c.u32 = 0x1010101U * res.priv.u8[i]; break;
@@ -952,8 +952,8 @@ static void normalize_clear(SwsOp *op)
case 4: c.u32 = res.priv.u32[i]; break;
}
op->c.q4[i].num = c.i;
op->c.q4[i].den = 1;
op->clear.value[i].num = c.i;
op->clear.value[i].den = 1;
}
}
+39 -39
View File
@@ -498,45 +498,45 @@ static void check_clear(void)
const AVRational zero = (AVRational) { 0, 1};
const AVRational none = {0};
const SwsConst patterns[] = {
const SwsClearOp patterns[] = {
/* Zero only */
{.q4 = { none, none, none, zero }},
{.q4 = { zero, none, none, none }},
{{ none, none, none, zero }},
{{ zero, none, none, none }},
/* Alpha only */
{.q4 = { none, none, none, alpha }},
{.q4 = { alpha, none, none, none }},
{{ none, none, none, alpha }},
{{ alpha, none, none, none }},
/* Chroma only */
{.q4 = { chroma, chroma, none, none }},
{.q4 = { none, chroma, chroma, none }},
{.q4 = { none, none, chroma, chroma }},
{.q4 = { chroma, none, chroma, none }},
{.q4 = { none, chroma, none, chroma }},
{{ chroma, chroma, none, none }},
{{ none, chroma, chroma, none }},
{{ none, none, chroma, chroma }},
{{ chroma, none, chroma, none }},
{{ none, chroma, none, chroma }},
/* Alpha+chroma */
{.q4 = { chroma, chroma, none, alpha }},
{.q4 = { none, chroma, chroma, alpha }},
{.q4 = { alpha, none, chroma, chroma }},
{.q4 = { chroma, none, chroma, alpha }},
{.q4 = { alpha, chroma, none, chroma }},
{{ chroma, chroma, none, alpha }},
{{ none, chroma, chroma, alpha }},
{{ alpha, none, chroma, chroma }},
{{ chroma, none, chroma, alpha }},
{{ alpha, chroma, none, chroma }},
/* Random values */
{.q4 = { none, rndq(t), rndq(t), rndq(t) }},
{.q4 = { none, rndq(t), rndq(t), rndq(t) }},
{.q4 = { none, rndq(t), rndq(t), rndq(t) }},
{.q4 = { none, rndq(t), rndq(t), rndq(t) }},
{{ none, rndq(t), rndq(t), rndq(t) }},
{{ none, rndq(t), rndq(t), rndq(t) }},
{{ none, rndq(t), rndq(t), rndq(t) }},
{{ none, rndq(t), rndq(t), rndq(t) }},
};
for (int i = 0; i < FF_ARRAY_ELEMS(patterns); i++) {
CHECK(FMT("clear_pattern_%s[%d]", type, i), 4, 4, t, t, {
.op = SWS_OP_CLEAR,
.type = t,
.c = patterns[i],
.op = SWS_OP_CLEAR,
.type = t,
.clear = patterns[i],
});
}
} else if (!ff_sws_pixel_type_is_int(t)) {
/* Floating point YUV doesn't exist, only alpha needs to be cleared */
CHECK(FMT("clear_alpha_%s", type), 4, 4, t, t, {
.op = SWS_OP_CLEAR,
.type = t,
.c.q4[3] = { 0, 1 },
.op = SWS_OP_CLEAR,
.type = t,
.clear.value[3] = { 0, 1 },
});
}
}
@@ -551,15 +551,15 @@ static void check_shift(void)
for (int shift = 1; shift <= 8; shift++) {
CHECK_COMMON(FMT("lshift%d_%s", shift, type), t, t, {
.op = SWS_OP_LSHIFT,
.type = t,
.c.u = shift,
.op = SWS_OP_LSHIFT,
.type = t,
.shift = { shift },
});
CHECK_COMMON(FMT("rshift%d_%s", shift, type), t, t, {
.op = SWS_OP_RSHIFT,
.type = t,
.c.u = shift,
.op = SWS_OP_RSHIFT,
.type = t,
.shift = { shift },
});
}
}
@@ -697,13 +697,13 @@ static void check_min_max(void)
CHECK_COMMON(FMT("min_%s", type), t, t, {
.op = SWS_OP_MIN,
.type = t,
.c.q4 = { rndq(t), rndq(t), rndq(t), rndq(t) },
.clamp = {{ rndq(t), rndq(t), rndq(t), rndq(t) }},
});
CHECK_COMMON(FMT("max_%s", type), t, t, {
.op = SWS_OP_MAX,
.type = t,
.c.q4 = { rndq(t), rndq(t), rndq(t), rndq(t) },
.clamp = {{ rndq(t), rndq(t), rndq(t), rndq(t) }},
});
}
}
@@ -776,15 +776,15 @@ static void check_scale(void)
const unsigned scale = rnd() & max;
const unsigned range = max / (scale ? scale : 1);
CHECK_COMMON_RANGE(FMT("scale_%s", type), range, t, t, {
.op = SWS_OP_SCALE,
.type = t,
.c.q = { scale, 1 },
.op = SWS_OP_SCALE,
.type = t,
.scale = {{ scale, 1 }},
});
} else {
CHECK_COMMON(FMT("scale_%s", type), t, t, {
.op = SWS_OP_SCALE,
.type = t,
.c.q = rndq(t),
.op = SWS_OP_SCALE,
.type = t,
.scale = { rndq(t) },
});
}
}