Coverage Report

Created: 2026-08-17 07:50

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ffmpeg/libswscale/ops.c
Line
Count
Source
1
/**
2
 * Copyright (C) 2025 Niklas Haas
3
 *
4
 * This file is part of FFmpeg.
5
 *
6
 * FFmpeg is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU Lesser General Public
8
 * License as published by the Free Software Foundation; either
9
 * version 2.1 of the License, or (at your option) any later version.
10
 *
11
 * FFmpeg is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
 * Lesser General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU Lesser General Public
17
 * License along with FFmpeg; if not, write to the Free Software
18
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19
 */
20
21
#include "libavutil/attributes.h"
22
#include "libavutil/avassert.h"
23
#include "libavutil/avstring.h"
24
#include "libavutil/bprint.h"
25
#include "libavutil/bswap.h"
26
#include "libavutil/mem.h"
27
#include "libavutil/rational.h"
28
#include "libavutil/refstruct.h"
29
30
#include "format.h"
31
#include "ops.h"
32
#include "ops_internal.h"
33
34
extern const SwsOpBackend backend_c;
35
extern const SwsOpBackend backend_murder;
36
extern const SwsOpBackend backend_aarch64;
37
extern const SwsOpBackend backend_x86;
38
#if HAVE_SPIRV_HEADERS_SPIRV_H || HAVE_SPIRV_UNIFIED1_SPIRV_H
39
extern const SwsOpBackend backend_spirv;
40
#endif
41
42
const SwsOpBackend * const ff_sws_op_backends[] = {
43
    &backend_murder,
44
#if ARCH_AARCH64 && HAVE_NEON
45
    &backend_aarch64,
46
#elif ARCH_X86_64 && HAVE_X86ASM
47
    &backend_x86,
48
#endif
49
    &backend_c,
50
#if HAVE_SPIRV_HEADERS_SPIRV_H || HAVE_SPIRV_UNIFIED1_SPIRV_H
51
    &backend_spirv,
52
#endif
53
    NULL
54
};
55
56
const char *ff_sws_pixel_type_name(SwsPixelType type)
57
0
{
58
0
    switch (type) {
59
0
    case SWS_PIXEL_U8:   return "u8";
60
0
    case SWS_PIXEL_U16:  return "u16";
61
0
    case SWS_PIXEL_U32:  return "u32";
62
0
    case SWS_PIXEL_F32:  return "f32";
63
0
    case SWS_PIXEL_NONE: return "none";
64
0
    case SWS_PIXEL_TYPE_NB: break;
65
0
    }
66
67
0
    av_unreachable("Invalid pixel type!");
68
0
    return "ERR";
69
0
}
70
71
const char *ff_sws_op_type_name(SwsOpType op)
72
0
{
73
0
    switch (op) {
74
0
    case SWS_OP_READ:        return "SWS_OP_READ";
75
0
    case SWS_OP_WRITE:       return "SWS_OP_WRITE";
76
0
    case SWS_OP_SWAP_BYTES:  return "SWS_OP_SWAP_BYTES";
77
0
    case SWS_OP_SWIZZLE:     return "SWS_OP_SWIZZLE";
78
0
    case SWS_OP_UNPACK:      return "SWS_OP_UNPACK";
79
0
    case SWS_OP_PACK:        return "SWS_OP_PACK";
80
0
    case SWS_OP_LSHIFT:      return "SWS_OP_LSHIFT";
81
0
    case SWS_OP_RSHIFT:      return "SWS_OP_RSHIFT";
82
0
    case SWS_OP_CLEAR:       return "SWS_OP_CLEAR";
83
0
    case SWS_OP_CONVERT:     return "SWS_OP_CONVERT";
84
0
    case SWS_OP_MIN:         return "SWS_OP_MIN";
85
0
    case SWS_OP_MAX:         return "SWS_OP_MAX";
86
0
    case SWS_OP_SCALE:       return "SWS_OP_SCALE";
87
0
    case SWS_OP_LINEAR:      return "SWS_OP_LINEAR";
88
0
    case SWS_OP_DITHER:      return "SWS_OP_DITHER";
89
0
    case SWS_OP_FILTER_H:    return "SWS_OP_FILTER_H";
90
0
    case SWS_OP_FILTER_V:    return "SWS_OP_FILTER_V";
91
0
    case SWS_OP_LUT_3D:      return "SWS_OP_LUT_3D";
92
0
    case SWS_OP_INVALID:     return "SWS_OP_INVALID";
93
0
    case SWS_OP_TYPE_NB: break;
94
0
    }
95
96
0
    av_unreachable("Invalid operation type!");
97
0
    return "ERR";
98
0
}
99
100
SwsCompMask ff_sws_comp_mask_q4(const AVRational64 q[4])
101
0
{
102
0
    SwsCompMask mask = 0;
103
0
    for (int i = 0; i < 4; i++) {
104
0
        if (q[i].den)
105
0
            mask |= SWS_COMP(i);
106
0
    }
107
0
    return mask;
108
0
}
109
110
void ff_sws_comp_mask_swizzle(SwsCompMask *mask, const SwsSwizzleOp *swiz)
111
0
{
112
0
    const SwsCompMask orig = *mask;
113
0
    SwsCompMask res = 0;
114
0
    for (int i = 0; i < 4; i++) {
115
0
        const int src = swiz->in[i];
116
0
        if (SWS_COMP_TEST(orig, src))
117
0
            res |= SWS_COMP(i);
118
0
    }
119
120
0
    *mask = res;
121
0
}
122
123
SwsCompMask ff_sws_comp_mask_needed(const SwsOp *op)
124
0
{
125
0
    SwsCompMask mask = 0;
126
0
    for (int i = 0; i < 4; i++) {
127
0
        if (SWS_OP_NEEDED(op, i))
128
0
            mask |= SWS_COMP(i);
129
0
    }
130
0
    return mask;
131
0
}
132
133
int ff_sws_rw_op_planes(const SwsOp *op)
134
0
{
135
0
    av_assert2(op->op == SWS_OP_READ || op->op == SWS_OP_WRITE);
136
0
    switch (op->rw.mode) {
137
0
    case SWS_RW_PLANAR:  return op->rw.elems;
138
0
    case SWS_RW_PACKED:  return 1;
139
0
    case SWS_RW_PALETTE: return 2;
140
0
    }
141
142
0
    av_unreachable("Invalid read/write mode!");
143
0
    return 0;
144
0
}
145
146
/* biased towards `a` */
147
static AVRational64 av_min_q64(AVRational64 a, AVRational64 b)
148
0
{
149
0
    return av_cmp_q64(a, b) == 1 ? b : a;
150
0
}
151
152
static AVRational64 av_max_q64(AVRational64 a, AVRational64 b)
153
0
{
154
0
    return av_cmp_q64(a, b) == -1 ? b : a;
155
0
}
156
157
void ff_sws_apply_op_q(const SwsOp *op, AVRational64 x[4])
158
0
{
159
0
    uint64_t mask[4];
160
0
    int shift[4];
161
162
0
    switch (op->op) {
163
0
    case SWS_OP_READ:
164
0
    case SWS_OP_WRITE:
165
0
        return;
166
0
    case SWS_OP_UNPACK: {
167
0
        av_assert1(ff_sws_pixel_type_is_int(op->type));
168
0
        ff_sws_pack_op_decode(op, mask, shift);
169
0
        unsigned val = x[0].num;
170
0
        for (int i = 0; i < 4; i++)
171
0
            x[i] = Q((val >> shift[i]) & mask[i]);
172
0
        return;
173
0
    }
174
0
    case SWS_OP_PACK: {
175
0
        av_assert1(ff_sws_pixel_type_is_int(op->type));
176
0
        ff_sws_pack_op_decode(op, mask, shift);
177
0
        unsigned val = 0;
178
0
        for (int i = 0; i < 4; i++)
179
0
            val |= (x[i].num & mask[i]) << shift[i];
180
0
        x[0] = Q(val);
181
0
        return;
182
0
    }
183
0
    case SWS_OP_SWAP_BYTES:
184
0
        switch (op->type) {
185
0
        case SWS_PIXEL_U16:
186
0
            for (int i = 0; i < 4; i++) {
187
0
                av_assert2(x[i].num >= 0 && x[i].num <= UINT16_MAX);
188
0
                x[i].num = av_bswap16(x[i].num);
189
0
            }
190
0
            return;
191
0
        case SWS_PIXEL_U32:
192
0
            for (int i = 0; i < 4; i++) {
193
0
                av_assert2(x[i].num >= 0 && x[i].num <= UINT32_MAX);
194
0
                x[i].num = av_bswap32(x[i].num);
195
0
            }
196
0
            return;
197
0
        }
198
0
        av_unreachable("Invalid pixel type for SWS_OP_SWAP_BYTES!");
199
0
        return;
200
0
    case SWS_OP_CLEAR:
201
0
        for (int i = 0; i < 4; i++) {
202
0
            if (SWS_COMP_TEST(op->clear.mask, i))
203
0
                x[i] = op->clear.value[i];
204
0
        }
205
0
        return;
206
0
    case SWS_OP_LSHIFT: {
207
0
        av_assert1(ff_sws_pixel_type_is_int(op->type));
208
0
        AVRational64 mult = Q(1 << op->shift.amount);
209
0
        for (int i = 0; i < 4; i++)
210
0
            x[i] = x[i].den ? av_mul_q64(x[i], mult) : x[i];
211
0
        return;
212
0
    }
213
0
    case SWS_OP_RSHIFT: {
214
0
        av_assert1(ff_sws_pixel_type_is_int(op->type));
215
0
        for (int i = 0; i < 4; i++)
216
0
            x[i] = x[i].den ? Q((x[i].num / x[i].den) >> op->shift.amount) : x[i];
217
0
        return;
218
0
    }
219
0
    case SWS_OP_SWIZZLE: {
220
0
        const AVRational64 orig[4] = { x[0], x[1], x[2], x[3] };
221
0
        for (int i = 0; i < 4; i++)
222
0
            x[i] = orig[op->swizzle.in[i]];
223
0
        return;
224
0
    }
225
0
    case SWS_OP_CONVERT:
226
0
        if (ff_sws_pixel_type_is_int(op->convert.to)) {
227
0
            const AVRational64 scale = ff_sws_pixel_expand(op->type, op->convert.to);
228
0
            for (int i = 0; i < 4; i++) {
229
0
                x[i] = x[i].den ? Q(x[i].num / x[i].den) : x[i];
230
0
                if (op->convert.expand)
231
0
                    x[i] = av_mul_q64(x[i], scale);
232
0
            }
233
0
        }
234
0
        return;
235
0
    case SWS_OP_DITHER:
236
0
        av_assert1(!ff_sws_pixel_type_is_int(op->type));
237
0
        for (int i = 0; i < 4; i++) {
238
0
            if (op->dither.y_offset[i] >= 0 && x[i].den)
239
0
                x[i] = av_add_q64(x[i], av_make_q64(1, 2));
240
0
        }
241
0
        return;
242
0
    case SWS_OP_MIN:
243
0
        for (int i = 0; i < 4; i++)
244
0
            x[i] = av_min_q64(x[i], op->clamp.limit[i]);
245
0
        return;
246
0
    case SWS_OP_MAX:
247
0
        for (int i = 0; i < 4; i++)
248
0
            x[i] = av_max_q64(x[i], op->clamp.limit[i]);
249
0
        return;
250
0
    case SWS_OP_LINEAR: {
251
0
        av_assert1(!ff_sws_pixel_type_is_int(op->type));
252
0
        const AVRational64 orig[4] = { x[0], x[1], x[2], x[3] };
253
0
        for (int i = 0; i < 4; i++) {
254
0
            AVRational64 sum = op->lin.m[i][4];
255
0
            for (int j = 0; j < 4; j++)
256
0
                sum = av_add_q64(sum, av_mul_q64(orig[j], op->lin.m[i][j]));
257
0
            x[i] = sum;
258
0
        }
259
0
        return;
260
0
    }
261
0
    case SWS_OP_SCALE:
262
0
        for (int i = 0; i < 4; i++)
263
0
            x[i] = x[i].den ? av_mul_q64(x[i], op->scale.factor) : x[i];
264
0
        return;
265
0
    case SWS_OP_FILTER_H:
266
0
    case SWS_OP_FILTER_V:
267
        /* Filters have normalized energy by definition, so they don't
268
         * conceptually modify individual components */
269
0
        return;
270
0
    case SWS_OP_LUT_3D:
271
        /* 3D LUTs are treated as a black box, so set those values to NaN */
272
0
        for (int i = 0; i < 3; i++)
273
0
            x[i] = (AVRational64) {0};
274
0
        return;
275
0
    }
276
277
0
    av_unreachable("Invalid operation type!");
278
0
}
279
280
enum {
281
    SWS_COMP_IDENTITY = SWS_COMP_ZERO | SWS_COMP_EXACT |
282
                        SWS_COMP_COPY | SWS_COMP_CONST,
283
284
    SWS_COMP_DIRTY = ~(SWS_COMP_COPY | SWS_COMP_CONST),
285
};
286
287
/* merge_comp_flags() forms a monoid with SWS_COMP_IDENTITY as the null element */
288
static SwsCompFlags merge_comp_flags(SwsCompFlags a, SwsCompFlags b)
289
0
{
290
0
    const SwsCompFlags flags_or  = SWS_COMP_GARBAGE | SWS_COMP_SWAPPED;
291
0
    const SwsCompFlags flags_and = SWS_COMP_IDENTITY;
292
0
    return ((a & b) & flags_and) | ((a | b) & flags_or);
293
0
}
294
295
static void apply_filter_weights(SwsComps *comps, const SwsComps *prev,
296
                                 const SwsFilterWeights *weights)
297
0
{
298
0
    const AVRational64 posw = { weights->sum_positive, SWS_FILTER_SCALE };
299
0
    const AVRational64 negw = { weights->sum_negative, SWS_FILTER_SCALE };
300
0
    for (int i = 0; i < 4; i++) {
301
0
        comps->flags[i]  = prev->flags[i] & SWS_COMP_DIRTY;
302
0
        comps->dep_in[i] = prev->dep_in[i];
303
        /* Only point sampling preserves exactness */
304
0
        if (weights->filter_size != 1)
305
0
            comps->flags[i] &= ~SWS_COMP_EXACT;
306
        /* Update min/max assuming extremes */
307
0
        comps->min[i] = av_add_q64(av_mul_q64(prev->min[i], posw),
308
0
                                   av_mul_q64(prev->max[i], negw));
309
0
        comps->max[i] = av_add_q64(av_mul_q64(prev->min[i], negw),
310
0
                                   av_mul_q64(prev->max[i], posw));
311
0
    }
312
0
}
313
314
/* Infer + propagate known information about components */
315
void ff_sws_op_list_update_comps(SwsOpList *ops)
316
0
{
317
0
    SwsComps prev = { .flags = {
318
0
        SWS_COMP_GARBAGE, SWS_COMP_GARBAGE, SWS_COMP_GARBAGE, SWS_COMP_GARBAGE,
319
0
    }};
320
321
    /* Forwards pass, propagates knowledge about the incoming pixel values */
322
0
    for (int n = 0; n < ops->num_ops; n++) {
323
0
        SwsOp *op = &ops->ops[n];
324
325
0
        switch (op->op) {
326
0
        case SWS_OP_LINEAR:
327
0
        case SWS_OP_DITHER:
328
0
        case SWS_OP_SWAP_BYTES:
329
0
        case SWS_OP_UNPACK:
330
0
        case SWS_OP_FILTER_H:
331
0
        case SWS_OP_FILTER_V:
332
0
        case SWS_OP_LUT_3D:
333
0
            break; /* special cases, handled below */
334
0
        default:
335
0
            memcpy(op->comps.min, prev.min, sizeof(prev.min));
336
0
            memcpy(op->comps.max, prev.max, sizeof(prev.max));
337
0
            ff_sws_apply_op_q(op, op->comps.min);
338
0
            ff_sws_apply_op_q(op, op->comps.max);
339
0
            break;
340
0
        }
341
342
0
        for (int i = 0; i < 4; i++) {
343
0
            op->comps.flags[i]  = SWS_COMP_IDENTITY;
344
0
            op->comps.dep_in[i] = SWS_COMP_NONE;
345
0
        }
346
347
0
        #define FORWARD(I, J, EXPR) do {                                        \
348
0
            SwsCompFlags flags = prev.flags[J];                                 \
349
0
            op->comps.flags[I] = merge_comp_flags(op->comps.flags[I], (EXPR));  \
350
0
            op->comps.dep_in[I] |= prev.dep_in[J];                              \
351
0
        } while (0)
352
353
0
        #define RESET(I) do {                                                   \
354
0
            op->comps.flags[I] = SWS_COMP_GARBAGE;                              \
355
0
            op->comps.min[I] = op->comps.max[I] = (AVRational64) {0};           \
356
0
            op->comps.dep_in[I] = SWS_COMP_NONE;                                \
357
0
        } while (0)
358
359
0
        switch (op->op) {
360
0
        case SWS_OP_READ:
361
            /* Active components are taken from the user-provided values,
362
             * other components are explicitly stripped */
363
0
            for (int i = 0; i < op->rw.elems; i++) {
364
0
                int idx = 0;
365
0
                switch (op->rw.mode) {
366
0
                case SWS_RW_PALETTE: idx = i; break;
367
0
                case SWS_RW_PACKED:  idx = i; break;
368
0
                case SWS_RW_PLANAR:  idx = ops->plane_src[i]; break;
369
0
                }
370
371
0
                av_assert0(!(ops->comps_src.flags[idx] & SWS_COMP_GARBAGE));
372
0
                op->comps.flags[i] = ops->comps_src.flags[idx] & SWS_COMP_DIRTY;
373
0
                op->comps.min[i]   = ops->comps_src.min[idx];
374
0
                op->comps.max[i]   = ops->comps_src.max[idx];
375
0
                op->comps.dep_in[i] = SWS_COMP(i);
376
377
                /**
378
                 * Don't mark packed or fractional reads as a copy, because the
379
                 * read operation implicitly unpacks the data into separate
380
                 * components. The only case in which op lists involving such
381
                 * reads can be refcopies is in the case of a true noop, which
382
                 * is already covered by the no-op check.
383
                 */
384
0
                if (op->rw.mode == SWS_RW_PLANAR && !op->rw.frac)
385
0
                    op->comps.flags[i] |= SWS_COMP_COPY;
386
0
            }
387
388
0
            if (op->rw.filter.op) {
389
0
                const SwsComps prev = op->comps;
390
0
                apply_filter_weights(&op->comps, &prev, op->rw.filter.kernel);
391
0
            }
392
0
            break;
393
0
        case SWS_OP_SWAP_BYTES:
394
0
            for (int i = 0; i < 4; i++) {
395
0
                FORWARD(i, i, (flags ^ SWS_COMP_SWAPPED) & SWS_COMP_DIRTY);
396
0
                op->comps.min[i] = prev.min[i];
397
0
                op->comps.max[i] = prev.max[i];
398
0
            }
399
0
            break;
400
0
        case SWS_OP_WRITE:
401
0
            for (int i = 0; i < op->rw.elems; i++)
402
0
                av_assert1(!(prev.flags[i] & SWS_COMP_GARBAGE));
403
0
            for (int i = 0; i < 4; i++)
404
0
                FORWARD(i, i, flags);
405
0
            break;
406
0
        case SWS_OP_LSHIFT:
407
0
        case SWS_OP_RSHIFT:
408
0
            for (int i = 0; i < 4; i++)
409
0
                FORWARD(i, i, flags & SWS_COMP_DIRTY);
410
0
            break;
411
0
        case SWS_OP_MIN:
412
0
        case SWS_OP_MAX: {
413
0
            AVRational64 *bound = op->op == SWS_OP_MIN ? op->comps.max : op->comps.min;
414
0
            for (int i = 0; i < 4; i++) {
415
0
                FORWARD(i, i, flags);
416
0
                if (op->clamp.limit[i].den)
417
0
                    op->comps.flags[i] &= SWS_COMP_DIRTY;
418
0
                if (!bound[i].den) /* reset undefined bounds to known range */
419
0
                    bound[i] = op->clamp.limit[i];
420
0
            }
421
0
            break;
422
0
        }
423
0
        case SWS_OP_DITHER:
424
0
            for (int i = 0; i < 4; i++) {
425
0
                FORWARD(i, i, flags);
426
0
                op->comps.min[i] = prev.min[i];
427
0
                op->comps.max[i] = prev.max[i];
428
0
                if (op->dither.y_offset[i] < 0)
429
0
                    continue;
430
                /* Strip zero flag because of the nonzero dithering offset */
431
0
                op->comps.flags[i] &= ~SWS_COMP_ZERO & SWS_COMP_DIRTY;
432
0
                op->comps.min[i] = av_add_q64(op->comps.min[i], op->dither.min);
433
0
                op->comps.max[i] = av_add_q64(op->comps.max[i], op->dither.max);
434
0
            }
435
0
            break;
436
0
        case SWS_OP_UNPACK:
437
0
            for (int i = 0; i < 4; i++) {
438
0
                const int pattern = op->pack.pattern[i];
439
0
                if (pattern) {
440
0
                    av_assert1(pattern < 32);
441
0
                    FORWARD(i, 0, flags & SWS_COMP_DIRTY);
442
0
                    op->comps.min[i] = Q(0);
443
0
                    op->comps.max[i] = Q((1ULL << pattern) - 1);
444
0
                } else
445
0
                    RESET(i);
446
0
            }
447
0
            break;
448
0
        case SWS_OP_PACK:
449
0
            for (int i = 0; i < 4; i++) {
450
0
                if (op->pack.pattern[i])
451
0
                    FORWARD(0, i, flags & SWS_COMP_DIRTY);
452
0
                if (i > 0) /* clear remaining comps for sanity */
453
0
                    RESET(i);
454
0
            }
455
0
            break;
456
0
        case SWS_OP_CLEAR:
457
0
            for (int i = 0; i < 4; i++) {
458
0
                if (SWS_COMP_TEST(op->clear.mask, i)) {
459
0
                    op->comps.flags[i] = SWS_COMP_CONST;
460
0
                    if (op->clear.value[i].num == 0)
461
0
                        op->comps.flags[i] |= SWS_COMP_ZERO;
462
0
                    if (op->clear.value[i].den == 1)
463
0
                        op->comps.flags[i] |= SWS_COMP_EXACT;
464
0
                } else {
465
0
                    FORWARD(i, i, flags);
466
0
                }
467
0
            }
468
0
            break;
469
0
        case SWS_OP_SWIZZLE:
470
0
            for (int i = 0; i < 4; i++)
471
0
                FORWARD(i, op->swizzle.in[i], flags);
472
0
            break;
473
0
        case SWS_OP_CONVERT:
474
0
            for (int i = 0; i < 4; i++) {
475
0
                FORWARD(i, i, flags);
476
0
                if (!(prev.flags[i] & SWS_COMP_EXACT) || op->convert.expand)
477
0
                    op->comps.flags[i] &= SWS_COMP_DIRTY;
478
0
                if (ff_sws_pixel_type_is_int(op->convert.to))
479
0
                    op->comps.flags[i] |= SWS_COMP_EXACT;
480
0
            }
481
0
            break;
482
0
        case SWS_OP_LINEAR:
483
0
            for (int i = 0; i < 4; i++) {
484
0
                AVRational64 min = Q(0), max = Q(0);
485
0
                bool first = true;
486
0
                for (int j = 0; j < 4; j++) {
487
0
                    const AVRational64 k = op->lin.m[i][j];
488
0
                    AVRational64 mink = av_mul_q64(prev.min[j], k);
489
0
                    AVRational64 maxk = av_mul_q64(prev.max[j], k);
490
0
                    if (k.num) {
491
0
                        FORWARD(i, j, flags);
492
0
                        if (k.den != 1) /* fractional coefficient */
493
0
                            op->comps.flags[i] &= ~SWS_COMP_EXACT;
494
0
                        if (k.num < 0)
495
0
                            FFSWAP(AVRational64, mink, maxk);
496
0
                        min = av_add_q64(min, mink);
497
0
                        max = av_add_q64(max, maxk);
498
0
                        if (!first || av_cmp_q64(k, Q(1)))
499
0
                            op->comps.flags[i] &= SWS_COMP_DIRTY;
500
0
                        first = false;
501
0
                    }
502
0
                }
503
0
                if (op->lin.m[i][4].num) { /* nonzero offset */
504
0
                    op->comps.flags[i] &= ~SWS_COMP_ZERO & SWS_COMP_DIRTY;
505
0
                    if (op->lin.m[i][4].den != 1) /* fractional offset */
506
0
                        op->comps.flags[i] &= ~SWS_COMP_EXACT;
507
0
                    min = av_add_q64(min, op->lin.m[i][4]);
508
0
                    max = av_add_q64(max, op->lin.m[i][4]);
509
0
                }
510
0
                op->comps.min[i] = min;
511
0
                op->comps.max[i] = max;
512
0
            }
513
0
            break;
514
0
        case SWS_OP_SCALE:
515
0
            for (int i = 0; i < 4; i++) {
516
0
                FORWARD(i, i, flags & SWS_COMP_DIRTY);
517
0
                if (op->scale.factor.den != 1) /* fractional scale */
518
0
                    op->comps.flags[i] &= ~SWS_COMP_EXACT;
519
0
                if (op->scale.factor.num < 0)
520
0
                    FFSWAP(AVRational64, op->comps.min[i], op->comps.max[i]);
521
0
            }
522
0
            break;
523
0
        case SWS_OP_FILTER_H:
524
0
        case SWS_OP_FILTER_V: {
525
0
            apply_filter_weights(&op->comps, &prev, op->filter.kernel);
526
0
            break;
527
0
        }
528
0
        case SWS_OP_LUT_3D:
529
0
            for (int i = 0; i < 3; i++) {
530
                /* 3x3 dependency matrix; strip all information except
531
                 * SWS_COMP_GARBAGE (for correctness validation) */
532
0
                for (int j = 0; j < 3; j++)
533
0
                    FORWARD(i, j, flags & SWS_COMP_GARBAGE);
534
                /* LUT output domain is always scaled to full 16-bit range */
535
0
                op->comps.min[i] = Q(0);
536
0
                op->comps.max[i] = Q(UINT16_MAX);
537
0
            }
538
            /* Pass through alpha channel untouched */
539
0
            FORWARD(3, 3, flags);
540
0
            op->comps.min[3] = prev.min[3];
541
0
            op->comps.max[3] = prev.max[3];
542
0
            break;
543
0
        case SWS_OP_INVALID:
544
0
        case SWS_OP_TYPE_NB:
545
0
            av_unreachable("Invalid operation type!");
546
0
        }
547
548
0
        prev = op->comps;
549
0
    }
550
551
    /* Backwards pass, solves for output component dependencies */
552
0
    SwsCompMask need_out[4] = {0};
553
554
0
    for (int n = ops->num_ops - 1; n >= 0; n--) {
555
0
        SwsOp *op = &ops->ops[n];
556
0
        SwsCompMask need_in[4] = {0};
557
558
0
        for (int i = 0; i < 4; i++) {
559
0
            op->comps.dep_out[i] = need_out[i];
560
0
            if (!need_out[i])
561
0
                RESET(i);
562
0
        }
563
564
0
        switch (op->op) {
565
0
        case SWS_OP_READ:
566
0
        case SWS_OP_WRITE:
567
0
            for (int i = 0; i < op->rw.elems; i++)
568
0
                need_in[i] = (op->op == SWS_OP_WRITE) ? SWS_COMP(i) : 0;
569
0
            for (int i = op->rw.elems; i < 4; i++)
570
0
                need_in[i] = need_out[i];
571
0
            break;
572
0
        case SWS_OP_SWAP_BYTES:
573
0
        case SWS_OP_LSHIFT:
574
0
        case SWS_OP_RSHIFT:
575
0
        case SWS_OP_CONVERT:
576
0
        case SWS_OP_DITHER:
577
0
        case SWS_OP_MIN:
578
0
        case SWS_OP_MAX:
579
0
        case SWS_OP_SCALE:
580
0
        case SWS_OP_FILTER_H:
581
0
        case SWS_OP_FILTER_V:
582
0
            for (int i = 0; i < 4; i++)
583
0
                need_in[i] = need_out[i];
584
0
            break;
585
0
        case SWS_OP_UNPACK:
586
0
            for (int i = 0; i < 4 && op->pack.pattern[i]; i++)
587
0
                need_in[0] |= need_out[i];
588
0
            break;
589
0
        case SWS_OP_PACK:
590
0
            for (int i = 0; i < 4 && op->pack.pattern[i]; i++)
591
0
                need_in[i] = need_out[0];
592
0
            break;
593
0
        case SWS_OP_CLEAR:
594
0
            for (int i = 0; i < 4; i++) {
595
0
                if (!SWS_COMP_TEST(op->clear.mask, i))
596
0
                    need_in[i] = need_out[i];
597
0
            }
598
0
            break;
599
0
        case SWS_OP_SWIZZLE:
600
0
            for (int i = 0; i < 4; i++)
601
0
                need_in[op->swizzle.in[i]] |= need_out[i];
602
0
            break;
603
0
        case SWS_OP_LINEAR:
604
0
            for (int i = 0; i < 4; i++) {
605
0
                for (int j = 0; j < 4; j++) {
606
0
                    if (op->lin.m[i][j].num)
607
0
                        need_in[j] |= need_out[i];
608
0
                }
609
0
            }
610
0
            break;
611
0
        case SWS_OP_LUT_3D:
612
0
            for (int i = 0; i < 3; i++)
613
0
                need_in[i] = need_out[0] | need_out[1] | need_out[2];
614
0
            need_in[3] = need_out[3];
615
0
            break;
616
0
        }
617
618
0
        memcpy(need_out, need_in, sizeof(need_in));
619
0
    }
620
621
0
    #undef FORWARD
622
0
    #undef RESET
623
0
}
624
625
static void op_uninit(SwsOp *op)
626
0
{
627
0
    switch (op->op) {
628
0
    case SWS_OP_READ:
629
0
        av_refstruct_unref(&op->rw.filter.kernel);
630
0
        break;
631
0
    case SWS_OP_DITHER:
632
0
        av_refstruct_unref(&op->dither.matrix);
633
0
        break;
634
0
    case SWS_OP_FILTER_H:
635
0
    case SWS_OP_FILTER_V:
636
0
        av_refstruct_unref(&op->filter.kernel);
637
0
        break;
638
0
    case SWS_OP_LUT_3D:
639
0
        av_refstruct_unref(&op->lut3d.lut);
640
0
        break;
641
0
    }
642
643
0
    *op = (SwsOp) {0};
644
0
}
645
646
SwsOpList *ff_sws_op_list_alloc(void)
647
0
{
648
0
    SwsOpList *ops = av_mallocz(sizeof(SwsOpList));
649
0
    if (!ops)
650
0
        return NULL;
651
652
0
    for (int i = 0; i < 4; i++)
653
0
        ops->plane_src[i] = ops->plane_dst[i] = i;
654
0
    ff_fmt_clear(&ops->src);
655
0
    ff_fmt_clear(&ops->dst);
656
0
    return ops;
657
0
}
658
659
void ff_sws_op_list_free(SwsOpList **p_ops)
660
0
{
661
0
    SwsOpList *ops = *p_ops;
662
0
    if (!ops)
663
0
        return;
664
665
0
    for (int i = 0; i < ops->num_ops; i++)
666
0
        op_uninit(&ops->ops[i]);
667
668
0
    av_freep(&ops->ops);
669
0
    av_free(ops);
670
0
    *p_ops = NULL;
671
0
}
672
673
SwsOpList *ff_sws_op_list_duplicate(const SwsOpList *ops)
674
0
{
675
0
    SwsOpList *copy = av_malloc(sizeof(*copy));
676
0
    if (!copy)
677
0
        return NULL;
678
679
0
    int num = ops->num_ops;
680
0
    if (num)
681
0
        num = 1 << av_ceil_log2(num);
682
683
0
    *copy = *ops;
684
0
    copy->ops = av_memdup(ops->ops, num * sizeof(ops->ops[0]));
685
0
    if (!copy->ops) {
686
0
        av_free(copy);
687
0
        return NULL;
688
0
    }
689
690
0
    for (int i = 0; i < copy->num_ops; i++) {
691
0
        const SwsOp *op = &copy->ops[i];
692
0
        switch (op->op) {
693
0
        case SWS_OP_READ:
694
0
            if (op->rw.filter.kernel)
695
0
                av_refstruct_ref(op->rw.filter.kernel);
696
0
            break;
697
0
        case SWS_OP_DITHER:
698
0
            av_refstruct_ref(op->dither.matrix);
699
0
            break;
700
0
        case SWS_OP_FILTER_H:
701
0
        case SWS_OP_FILTER_V:
702
0
            av_refstruct_ref(op->filter.kernel);
703
0
            break;
704
0
        case SWS_OP_LUT_3D:
705
0
            av_refstruct_ref_c(op->lut3d.lut);
706
0
            break;
707
0
        }
708
0
    }
709
710
0
    return copy;
711
0
}
712
713
const SwsOp *ff_sws_op_list_input(const SwsOpList *ops)
714
0
{
715
0
    if (!ops->num_ops)
716
0
        return NULL;
717
718
0
    const SwsOp *read = &ops->ops[0];
719
0
    return read->op == SWS_OP_READ ? read : NULL;
720
0
}
721
722
const SwsOp *ff_sws_op_list_output(const SwsOpList *ops)
723
0
{
724
0
    if (!ops->num_ops)
725
0
        return NULL;
726
727
0
    const SwsOp *write = &ops->ops[ops->num_ops - 1];
728
0
    return write->op == SWS_OP_WRITE ? write : NULL;
729
0
}
730
731
void ff_sws_op_list_remove_at(SwsOpList *ops, int index, int count)
732
0
{
733
0
    const int end = ops->num_ops - count;
734
0
    av_assert2(index >= 0 && count >= 0 && index + count <= ops->num_ops);
735
0
    for (int i = 0; i < count; i++)
736
0
        op_uninit(&ops->ops[index + i]);
737
0
    for (int i = index; i < end; i++)
738
0
        ops->ops[i] = ops->ops[i + count];
739
0
    ops->num_ops = end;
740
0
}
741
742
int ff_sws_op_list_insert_at(SwsOpList *ops, int index, SwsOp *op)
743
0
{
744
0
    void *ret = av_dynarray2_add((void **) &ops->ops, &ops->num_ops, sizeof(*op), NULL);
745
0
    if (!ret) {
746
0
        op_uninit(op);
747
0
        return AVERROR(ENOMEM);
748
0
    }
749
750
0
    for (int i = ops->num_ops - 1; i > index; i--)
751
0
        ops->ops[i] = ops->ops[i - 1];
752
0
    ops->ops[index] = *op;
753
0
    return 0;
754
0
}
755
756
int ff_sws_op_list_append(SwsOpList *ops, SwsOp *op)
757
0
{
758
0
    return ff_sws_op_list_insert_at(ops, ops->num_ops, op);
759
0
}
760
761
bool ff_sws_op_list_is_noop(const SwsOpList *ops)
762
0
{
763
0
    if (!ops->num_ops)
764
0
        return true;
765
766
0
    const SwsOp *read  = ff_sws_op_list_input(ops);
767
0
    const SwsOp *write = ff_sws_op_list_output(ops);
768
0
    if (!read || !write || ops->num_ops > 2 ||
769
0
        read->type != write->type ||
770
0
        read->rw.mode != write->rw.mode ||
771
0
        read->rw.elems != write->rw.elems ||
772
0
        read->rw.frac != write->rw.frac ||
773
0
        read->rw.filter.op || write->rw.filter.op)
774
0
        return false;
775
776
    /**
777
     * Note that this check is unlikely to ever be hit in practice, since it
778
     * would imply the existence of planar formats with different plane orders
779
     * between them, e.g. rgbap <-> gbrap, which doesn't currently exist.
780
     * However, the check is cheap and lets me sleep at night.
781
     */
782
0
    const int num_planes = ff_sws_rw_op_planes(read);
783
0
    for (int i = 0; i < num_planes; i++) {
784
0
        if (ops->plane_src[i] != ops->plane_dst[i])
785
0
            return false;
786
0
    }
787
788
0
    return true;
789
0
}
790
791
int ff_sws_op_list_max_size(const SwsOpList *ops)
792
0
{
793
0
    int max_size = 0;
794
0
    for (int i = 0; i < ops->num_ops; i++) {
795
0
        const int size = ff_sws_pixel_type_size(ops->ops[i].type);
796
0
        max_size = FFMAX(max_size, size);
797
0
    }
798
799
0
    return max_size;
800
0
}
801
802
uint32_t ff_sws_linear_mask(const SwsLinearOp *c)
803
0
{
804
0
    uint32_t mask = 0;
805
0
    for (int i = 0; i < 4; i++) {
806
0
        for (int j = 0; j < 5; j++) {
807
0
            if (av_cmp_q64(c->m[i][j], Q(i == j)))
808
0
                mask |= SWS_MASK(i, j);
809
0
        }
810
0
    }
811
0
    return mask;
812
0
}
813
814
static char describe_comp_flags(SwsCompFlags flags)
815
0
{
816
0
    if (flags & SWS_COMP_GARBAGE)
817
0
        return 'X';
818
0
    else if (flags & SWS_COMP_ZERO)
819
0
        return '0';
820
0
    else if (flags & SWS_COMP_SWAPPED)
821
0
        return 'z';
822
0
    else if (flags & SWS_COMP_CONST)
823
0
        return '$';
824
0
    else if (flags & SWS_COMP_COPY)
825
0
        return '=';
826
0
    else if (flags & SWS_COMP_EXACT)
827
0
        return '+';
828
0
    else
829
0
        return '.';
830
0
}
831
832
static void print_deps(AVBPrint *bp, const SwsCompMask *deps)
833
0
{
834
0
    av_bprintf(bp, "{");
835
0
    for (int i = 0; i < 4; i++) {
836
0
        if (i)
837
0
            av_bprintf(bp, " ");
838
0
        av_bprintf(bp, "%s", deps[i] ? ff_sws_comp_mask_str(deps[i]) : "_");
839
0
    }
840
0
    av_bprintf(bp, "}");
841
0
}
842
843
static void print_q(AVBPrint *bp, const AVRational64 q)
844
0
{
845
0
    if (!q.den) {
846
0
        av_bprintf(bp, "%s", q.num > 0 ? "inf" : q.num < 0 ? "-inf" : "nan");
847
0
    } else if (q.den == 1) {
848
0
        av_bprintf(bp, "%"PRId64, q.num);
849
0
    } else if (q.num > 1000 || q.num < -1000 || q.den > 1000 || q.den < -1000) {
850
0
        av_bprintf(bp, "%f", av_q2d_64(q));
851
0
    } else {
852
0
        av_bprintf(bp, "%"PRId64"/%"PRId64, q.num, q.den);
853
0
    }
854
0
}
855
856
static void print_q4(AVBPrint *bp, const AVRational64 q4[4], SwsCompMask mask)
857
0
{
858
0
    av_bprintf(bp, "{");
859
0
    for (int i = 0; i < 4; i++) {
860
0
        if (i)
861
0
            av_bprintf(bp, " ");
862
0
        if (!SWS_COMP_TEST(mask, i)) {
863
0
            av_bprintf(bp, "_");
864
0
        } else {
865
0
            print_q(bp, q4[i]);
866
0
        }
867
0
    }
868
0
    av_bprintf(bp, "}");
869
0
}
870
871
static const char *const rw_mode_names[] = {
872
    [SWS_RW_PLANAR]     = "planar",
873
    [SWS_RW_PACKED]     = "packed",
874
    [SWS_RW_PALETTE]    = "palette"
875
};
876
877
void ff_sws_op_desc(AVBPrint *bp, const SwsOp *op)
878
0
{
879
0
    const char *name  = ff_sws_op_type_name(op->op);
880
0
    const SwsCompMask mask = ff_sws_comp_mask_needed(op);
881
882
0
    switch (op->op) {
883
0
    case SWS_OP_INVALID:
884
0
    case SWS_OP_SWAP_BYTES:
885
0
        av_bprintf(bp, "%s", name);
886
0
        break;
887
0
    case SWS_OP_READ:
888
0
    case SWS_OP_WRITE:
889
0
        av_bprintf(bp, "%-20s: %d elem(s) %s >> %d", name,
890
0
                   op->rw.elems, rw_mode_names[op->rw.mode],
891
0
                   op->rw.frac);
892
0
        if (!op->rw.filter.op)
893
0
            break;
894
0
        const SwsFilterWeights *kernel = op->rw.filter.kernel;
895
0
        av_bprintf(bp, " + %d tap %s filter (%c)",
896
0
                   kernel->filter_size, kernel->name,
897
0
                   op->rw.filter.op == SWS_OP_FILTER_H ? 'H' : 'V');
898
0
        break;
899
0
    case SWS_OP_LSHIFT:
900
0
        av_bprintf(bp, "%-20s: << %u", name, op->shift.amount);
901
0
        break;
902
0
    case SWS_OP_RSHIFT:
903
0
        av_bprintf(bp, "%-20s: >> %u", name, op->shift.amount);
904
0
        break;
905
0
    case SWS_OP_PACK:
906
0
    case SWS_OP_UNPACK:
907
0
        av_bprintf(bp, "%-20s: {%d %d %d %d}", name,
908
0
                   op->pack.pattern[0], op->pack.pattern[1],
909
0
                   op->pack.pattern[2], op->pack.pattern[3]);
910
0
        break;
911
0
    case SWS_OP_CLEAR:
912
0
        av_bprintf(bp, "%-20s: ", name);
913
0
        print_q4(bp, op->clear.value, mask & op->clear.mask);
914
0
        break;
915
0
    case SWS_OP_SWIZZLE:
916
0
        av_bprintf(bp, "%-20s: %d%d%d%d", name,
917
0
                   op->swizzle.x, op->swizzle.y, op->swizzle.z, op->swizzle.w);
918
0
        break;
919
0
    case SWS_OP_CONVERT:
920
0
        av_bprintf(bp, "%-20s: %s -> %s%s", name,
921
0
                   ff_sws_pixel_type_name(op->type),
922
0
                   ff_sws_pixel_type_name(op->convert.to),
923
0
                   op->convert.expand ? " (expand)" : "");
924
0
        break;
925
0
    case SWS_OP_DITHER:
926
0
        av_bprintf(bp, "%-20s: %dx%d matrix + {%d %d %d %d}", name,
927
0
                   1 << op->dither.size_log2, 1 << op->dither.size_log2,
928
0
                   op->dither.y_offset[0], op->dither.y_offset[1],
929
0
                   op->dither.y_offset[2], op->dither.y_offset[3]);
930
0
        break;
931
0
    case SWS_OP_MIN:
932
0
        av_bprintf(bp, "%-20s: x <= ", name);
933
0
        print_q4(bp, op->clamp.limit, mask & ff_sws_comp_mask_q4(op->clamp.limit));
934
0
        break;
935
0
    case SWS_OP_MAX:
936
0
        av_bprintf(bp, "%-20s: ", name);
937
0
        print_q4(bp, op->clamp.limit, mask & ff_sws_comp_mask_q4(op->clamp.limit));
938
0
        av_bprintf(bp, " <= x");
939
0
        break;
940
0
    case SWS_OP_LINEAR:
941
0
        av_bprintf(bp, "%-20s: [", name);
942
0
        for (int i = 0; i < 4; i++) {
943
0
            av_bprintf(bp, "%s[", i ? " " : "");
944
0
            for (int j = 0; j < 5; j++) {
945
0
                av_bprintf(bp, j ? " " : "");
946
0
                print_q(bp, op->lin.m[i][j]);
947
0
            }
948
0
            av_bprintf(bp, "]");
949
0
        }
950
0
        av_bprintf(bp, "]");
951
0
        break;
952
0
    case SWS_OP_SCALE:
953
0
        av_bprintf(bp, "%-20s: * %"PRId64, name, op->scale.factor.num);
954
0
        if (op->scale.factor.den != 1)
955
0
            av_bprintf(bp, "/%"PRId64, op->scale.factor.den);
956
0
        break;
957
0
    case SWS_OP_FILTER_H:
958
0
    case SWS_OP_FILTER_V: {
959
0
        const SwsFilterWeights *kernel = op->filter.kernel;
960
0
        av_bprintf(bp, "%-20s: %d -> %d %s (%d taps)", name,
961
0
                   kernel->src_size, kernel->dst_size,
962
0
                   kernel->name, kernel->filter_size);
963
0
        break;
964
0
    }
965
0
    case SWS_OP_LUT_3D:
966
0
        av_bprintf(bp, "%-20s: %s", name, op->lut3d.dynamic ? "dynamic" : "static");
967
0
        break;
968
0
    case SWS_OP_TYPE_NB:
969
0
        break;
970
0
    }
971
0
}
972
973
static void desc_plane_order(AVBPrint *bp, int nb_planes, const uint8_t *order)
974
0
{
975
0
    bool inorder = true;
976
0
    for (int i = 0; i < nb_planes; i++)
977
0
        inorder &= order[i] == i;
978
0
    if (inorder)
979
0
        return;
980
981
0
    av_bprintf(bp, ", via {");
982
0
    for (int i = 0; i < nb_planes; i++)
983
0
        av_bprintf(bp, "%s%d", i ? ", " : "", order[i]);
984
0
    av_bprintf(bp, "}");
985
0
}
986
987
void ff_sws_op_list_print(void *log, int lev, int lev_extra,
988
                          const SwsOpList *ops)
989
0
{
990
0
    AVBPrint bp;
991
0
    if (!ops->num_ops) {
992
0
        av_log(log, lev, "  (empty)\n");
993
0
        return;
994
0
    }
995
996
0
    av_bprint_init(&bp, 0, AV_BPRINT_SIZE_AUTOMATIC);
997
998
0
    for (int i = 0; i < ops->num_ops; i++) {
999
0
        const SwsOp *op = &ops->ops[i];
1000
0
        const SwsCompMask mask = ff_sws_comp_mask_needed(op);
1001
0
        av_bprint_clear(&bp);
1002
0
        av_bprintf(&bp, "  [%3s %c%c%c%c] ",
1003
0
                   ff_sws_pixel_type_name(op->type),
1004
0
                   describe_comp_flags(op->comps.flags[0]),
1005
0
                   describe_comp_flags(op->comps.flags[1]),
1006
0
                   describe_comp_flags(op->comps.flags[2]),
1007
0
                   describe_comp_flags(op->comps.flags[3]));
1008
1009
0
        ff_sws_op_desc(&bp, op);
1010
1011
0
        if (op->op == SWS_OP_READ || op->op == SWS_OP_WRITE) {
1012
0
            const int planes = ff_sws_rw_op_planes(op);
1013
0
            desc_plane_order(&bp, planes,
1014
0
                op->op == SWS_OP_READ ? ops->plane_src : ops->plane_dst);
1015
0
        }
1016
1017
0
        av_assert0(av_bprint_is_complete(&bp));
1018
0
        av_log(log, lev, "%s\n", bp.str);
1019
1020
        /* Only print value ranges if any are relevant */
1021
0
        SwsCompMask range_mask = ff_sws_comp_mask_q4(op->comps.min) |
1022
0
                                 ff_sws_comp_mask_q4(op->comps.max);
1023
0
        if (range_mask & mask) {
1024
0
            av_bprint_clear(&bp);
1025
0
            av_bprintf(&bp, "    min: ");
1026
0
            print_q4(&bp, op->comps.min, mask);
1027
0
            av_bprintf(&bp, ", max: ");
1028
0
            print_q4(&bp, op->comps.max, mask);
1029
0
            av_assert0(av_bprint_is_complete(&bp));
1030
0
            av_log(log, lev_extra, "%s\n", bp.str);
1031
0
        }
1032
1033
0
        bool has_deps = false;
1034
0
        for (int i = 0; i < 4; i++)
1035
0
            has_deps |= op->comps.dep_in[i] || op->comps.dep_out[i];
1036
0
        if (has_deps) {
1037
0
            av_bprint_clear(&bp);
1038
0
            av_bprintf(&bp, "    inputs: ");
1039
0
            print_deps(&bp, op->comps.dep_in);
1040
0
            av_bprintf(&bp, ", outputs: ");
1041
0
            print_deps(&bp, op->comps.dep_out);
1042
0
            av_assert0(av_bprint_is_complete(&bp));
1043
0
            av_log(log, lev_extra, "%s\n", bp.str);
1044
0
        }
1045
1046
0
    }
1047
1048
0
    av_log(log, lev, "    ('X' unused, 'z' byteswapped, '=' copied, '$' const, '+' integer, '0' zero)\n");
1049
0
}