Coverage Report

Created: 2026-03-12 07:14

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/avassert.h"
22
#include "libavutil/avstring.h"
23
#include "libavutil/bswap.h"
24
#include "libavutil/mem.h"
25
#include "libavutil/rational.h"
26
#include "libavutil/refstruct.h"
27
28
#include "ops.h"
29
#include "ops_internal.h"
30
31
extern const SwsOpBackend backend_c;
32
extern const SwsOpBackend backend_murder;
33
extern const SwsOpBackend backend_x86;
34
extern const SwsOpBackend backend_vulkan;
35
36
const SwsOpBackend * const ff_sws_op_backends[] = {
37
    &backend_murder,
38
#if ARCH_X86_64 && HAVE_X86ASM
39
    &backend_x86,
40
#endif
41
    &backend_c,
42
#if CONFIG_VULKAN
43
    &backend_vulkan,
44
#endif
45
    NULL
46
};
47
48
const char *ff_sws_pixel_type_name(SwsPixelType type)
49
0
{
50
0
    switch (type) {
51
0
    case SWS_PIXEL_U8:   return "u8";
52
0
    case SWS_PIXEL_U16:  return "u16";
53
0
    case SWS_PIXEL_U32:  return "u32";
54
0
    case SWS_PIXEL_F32:  return "f32";
55
0
    case SWS_PIXEL_NONE: return "none";
56
0
    case SWS_PIXEL_TYPE_NB: break;
57
0
    }
58
59
0
    av_unreachable("Invalid pixel type!");
60
0
    return "ERR";
61
0
}
62
63
int ff_sws_pixel_type_size(SwsPixelType type)
64
0
{
65
0
    switch (type) {
66
0
    case SWS_PIXEL_U8:  return sizeof(uint8_t);
67
0
    case SWS_PIXEL_U16: return sizeof(uint16_t);
68
0
    case SWS_PIXEL_U32: return sizeof(uint32_t);
69
0
    case SWS_PIXEL_F32: return sizeof(float);
70
0
    case SWS_PIXEL_NONE: break;
71
0
    case SWS_PIXEL_TYPE_NB: break;
72
0
    }
73
74
0
    av_unreachable("Invalid pixel type!");
75
0
    return 0;
76
0
}
77
78
bool ff_sws_pixel_type_is_int(SwsPixelType type)
79
0
{
80
0
    switch (type) {
81
0
    case SWS_PIXEL_U8:
82
0
    case SWS_PIXEL_U16:
83
0
    case SWS_PIXEL_U32:
84
0
        return true;
85
0
    case SWS_PIXEL_F32:
86
0
        return false;
87
0
    case SWS_PIXEL_NONE:
88
0
    case SWS_PIXEL_TYPE_NB: break;
89
0
    }
90
91
0
    av_unreachable("Invalid pixel type!");
92
0
    return false;
93
0
}
94
95
const char *ff_sws_op_type_name(SwsOpType op)
96
0
{
97
0
    switch (op) {
98
0
    case SWS_OP_READ:        return "SWS_OP_READ";
99
0
    case SWS_OP_WRITE:       return "SWS_OP_WRITE";
100
0
    case SWS_OP_SWAP_BYTES:  return "SWS_OP_SWAP_BYTES";
101
0
    case SWS_OP_SWIZZLE:     return "SWS_OP_SWIZZLE";
102
0
    case SWS_OP_UNPACK:      return "SWS_OP_UNPACK";
103
0
    case SWS_OP_PACK:        return "SWS_OP_PACK";
104
0
    case SWS_OP_LSHIFT:      return "SWS_OP_LSHIFT";
105
0
    case SWS_OP_RSHIFT:      return "SWS_OP_RSHIFT";
106
0
    case SWS_OP_CLEAR:       return "SWS_OP_CLEAR";
107
0
    case SWS_OP_CONVERT:     return "SWS_OP_CONVERT";
108
0
    case SWS_OP_MIN:         return "SWS_OP_MIN";
109
0
    case SWS_OP_MAX:         return "SWS_OP_MAX";
110
0
    case SWS_OP_SCALE:       return "SWS_OP_SCALE";
111
0
    case SWS_OP_LINEAR:      return "SWS_OP_LINEAR";
112
0
    case SWS_OP_DITHER:      return "SWS_OP_DITHER";
113
0
    case SWS_OP_INVALID:     return "SWS_OP_INVALID";
114
0
    case SWS_OP_TYPE_NB: break;
115
0
    }
116
117
0
    av_unreachable("Invalid operation type!");
118
0
    return "ERR";
119
0
}
120
121
/* biased towards `a` */
122
static AVRational av_min_q(AVRational a, AVRational b)
123
0
{
124
0
    return av_cmp_q(a, b) == 1 ? b : a;
125
0
}
126
127
static AVRational av_max_q(AVRational a, AVRational b)
128
0
{
129
0
    return av_cmp_q(a, b) == -1 ? b : a;
130
0
}
131
132
void ff_sws_apply_op_q(const SwsOp *op, AVRational x[4])
133
0
{
134
0
    uint64_t mask[4];
135
0
    int shift[4];
136
137
0
    switch (op->op) {
138
0
    case SWS_OP_READ:
139
0
    case SWS_OP_WRITE:
140
0
        return;
141
0
    case SWS_OP_UNPACK: {
142
0
        av_assert1(ff_sws_pixel_type_is_int(op->type));
143
0
        ff_sws_pack_op_decode(op, mask, shift);
144
0
        unsigned val = x[0].num;
145
0
        for (int i = 0; i < 4; i++)
146
0
            x[i] = Q((val >> shift[i]) & mask[i]);
147
0
        return;
148
0
    }
149
0
    case SWS_OP_PACK: {
150
0
        av_assert1(ff_sws_pixel_type_is_int(op->type));
151
0
        ff_sws_pack_op_decode(op, mask, shift);
152
0
        unsigned val = 0;
153
0
        for (int i = 0; i < 4; i++)
154
0
            val |= (x[i].num & mask[i]) << shift[i];
155
0
        x[0] = Q(val);
156
0
        return;
157
0
    }
158
0
    case SWS_OP_SWAP_BYTES:
159
0
        av_assert1(ff_sws_pixel_type_is_int(op->type));
160
0
        switch (ff_sws_pixel_type_size(op->type)) {
161
0
        case 2:
162
0
            for (int i = 0; i < 4; i++)
163
0
                x[i].num = av_bswap16(x[i].num);
164
0
            break;
165
0
        case 4:
166
0
            for (int i = 0; i < 4; i++)
167
0
                x[i].num = av_bswap32(x[i].num);
168
0
            break;
169
0
        }
170
0
        return;
171
0
    case SWS_OP_CLEAR:
172
0
        for (int i = 0; i < 4; i++) {
173
0
            if (op->c.q4[i].den)
174
0
                x[i] = op->c.q4[i];
175
0
        }
176
0
        return;
177
0
    case SWS_OP_LSHIFT: {
178
0
        av_assert1(ff_sws_pixel_type_is_int(op->type));
179
0
        AVRational mult = Q(1 << op->c.u);
180
0
        for (int i = 0; i < 4; i++)
181
0
            x[i] = x[i].den ? av_mul_q(x[i], mult) : x[i];
182
0
        return;
183
0
    }
184
0
    case SWS_OP_RSHIFT: {
185
0
        av_assert1(ff_sws_pixel_type_is_int(op->type));
186
0
        for (int i = 0; i < 4; i++)
187
0
            x[i] = x[i].den ? Q((x[i].num / x[i].den) >> op->c.u) : x[i];
188
0
        return;
189
0
    }
190
0
    case SWS_OP_SWIZZLE: {
191
0
        const AVRational orig[4] = { x[0], x[1], x[2], x[3] };
192
0
        for (int i = 0; i < 4; i++)
193
0
            x[i] = orig[op->swizzle.in[i]];
194
0
        return;
195
0
    }
196
0
    case SWS_OP_CONVERT:
197
0
        if (ff_sws_pixel_type_is_int(op->convert.to)) {
198
0
            const AVRational scale = ff_sws_pixel_expand(op->type, op->convert.to);
199
0
            for (int i = 0; i < 4; i++) {
200
0
                x[i] = x[i].den ? Q(x[i].num / x[i].den) : x[i];
201
0
                if (op->convert.expand)
202
0
                    x[i] = av_mul_q(x[i], scale);
203
0
            }
204
0
        }
205
0
        return;
206
0
    case SWS_OP_DITHER:
207
0
        av_assert1(!ff_sws_pixel_type_is_int(op->type));
208
0
        for (int i = 0; i < 4; i++) {
209
0
            if (op->dither.y_offset[i] >= 0 && x[i].den)
210
0
                x[i] = av_add_q(x[i], av_make_q(1, 2));
211
0
        }
212
0
        return;
213
0
    case SWS_OP_MIN:
214
0
        for (int i = 0; i < 4; i++)
215
0
            x[i] = av_min_q(x[i], op->c.q4[i]);
216
0
        return;
217
0
    case SWS_OP_MAX:
218
0
        for (int i = 0; i < 4; i++)
219
0
            x[i] = av_max_q(x[i], op->c.q4[i]);
220
0
        return;
221
0
    case SWS_OP_LINEAR: {
222
0
        av_assert1(!ff_sws_pixel_type_is_int(op->type));
223
0
        const AVRational orig[4] = { x[0], x[1], x[2], x[3] };
224
0
        for (int i = 0; i < 4; i++) {
225
0
            AVRational sum = op->lin.m[i][4];
226
0
            for (int j = 0; j < 4; j++)
227
0
                sum = av_add_q(sum, av_mul_q(orig[j], op->lin.m[i][j]));
228
0
            x[i] = sum;
229
0
        }
230
0
        return;
231
0
    }
232
0
    case SWS_OP_SCALE:
233
0
        for (int i = 0; i < 4; i++)
234
0
            x[i] = x[i].den ? av_mul_q(x[i], op->c.q) : x[i];
235
0
        return;
236
0
    }
237
238
0
    av_unreachable("Invalid operation type!");
239
0
}
240
241
/* merge_comp_flags() forms a monoid with flags_identity as the null element */
242
static const SwsCompFlags flags_identity = SWS_COMP_ZERO | SWS_COMP_EXACT;
243
static SwsCompFlags merge_comp_flags(SwsCompFlags a, SwsCompFlags b)
244
0
{
245
0
    const SwsCompFlags flags_or  = SWS_COMP_GARBAGE;
246
0
    const SwsCompFlags flags_and = SWS_COMP_ZERO | SWS_COMP_EXACT;
247
0
    return ((a & b) & flags_and) | ((a | b) & flags_or);
248
0
}
249
250
/* Linearly propagate flags per component */
251
static void propagate_flags(SwsOp *op, const SwsComps *prev)
252
0
{
253
0
    for (int i = 0; i < 4; i++)
254
0
        op->comps.flags[i] = prev->flags[i];
255
0
}
256
257
/* Clear undefined values in dst with src */
258
static void clear_undefined_values(AVRational dst[4], const AVRational src[4])
259
0
{
260
0
    for (int i = 0; i < 4; i++) {
261
0
        if (dst[i].den == 0)
262
0
            dst[i] = src[i];
263
0
    }
264
0
}
265
266
/* Infer + propagate known information about components */
267
void ff_sws_op_list_update_comps(SwsOpList *ops)
268
0
{
269
0
    SwsComps next = { .unused = {true, true, true, true} };
270
0
    SwsComps prev = { .flags = {
271
0
        SWS_COMP_GARBAGE, SWS_COMP_GARBAGE, SWS_COMP_GARBAGE, SWS_COMP_GARBAGE,
272
0
    }};
273
274
    /* Forwards pass, propagates knowledge about the incoming pixel values */
275
0
    for (int n = 0; n < ops->num_ops; n++) {
276
0
        SwsOp *op = &ops->ops[n];
277
278
0
        switch (op->op) {
279
0
        case SWS_OP_READ:
280
0
        case SWS_OP_LINEAR:
281
0
        case SWS_OP_SWAP_BYTES:
282
0
        case SWS_OP_UNPACK:
283
0
            break; /* special cases, handled below */
284
0
        default:
285
0
            memcpy(op->comps.min, prev.min, sizeof(prev.min));
286
0
            memcpy(op->comps.max, prev.max, sizeof(prev.max));
287
0
            ff_sws_apply_op_q(op, op->comps.min);
288
0
            ff_sws_apply_op_q(op, op->comps.max);
289
0
            break;
290
0
        }
291
292
0
        switch (op->op) {
293
0
        case SWS_OP_READ:
294
            /* Active components are taken from the user-provided values,
295
             * other components are explicitly stripped */
296
0
            for (int i = 0; i < op->rw.elems; i++) {
297
0
                const int idx = op->rw.packed ? i : ops->order_src.in[i];
298
0
                op->comps.flags[i] = ops->comps_src.flags[idx];
299
0
                op->comps.min[i]   = ops->comps_src.min[idx];
300
0
                op->comps.max[i]   = ops->comps_src.max[idx];
301
0
            }
302
0
            for (int i = op->rw.elems; i < 4; i++) {
303
0
                op->comps.flags[i] = prev.flags[i];
304
0
                op->comps.min[i]   = prev.min[i];
305
0
                op->comps.max[i]   = prev.max[i];
306
0
            }
307
0
            break;
308
0
        case SWS_OP_SWAP_BYTES:
309
0
            for (int i = 0; i < 4; i++) {
310
0
                op->comps.flags[i] = prev.flags[i] ^ SWS_COMP_SWAPPED;
311
0
                op->comps.min[i]   = prev.min[i];
312
0
                op->comps.max[i]   = prev.max[i];
313
0
            }
314
0
            break;
315
0
        case SWS_OP_WRITE:
316
0
            for (int i = 0; i < op->rw.elems; i++)
317
0
                av_assert1(!(prev.flags[i] & SWS_COMP_GARBAGE));
318
            /* fall through */
319
0
        case SWS_OP_LSHIFT:
320
0
        case SWS_OP_RSHIFT:
321
0
            propagate_flags(op, &prev);
322
0
            break;
323
0
        case SWS_OP_MIN:
324
0
            propagate_flags(op, &prev);
325
0
            clear_undefined_values(op->comps.max, op->c.q4);
326
0
            break;
327
0
        case SWS_OP_MAX:
328
0
            propagate_flags(op, &prev);
329
0
            clear_undefined_values(op->comps.min, op->c.q4);
330
0
            break;
331
0
        case SWS_OP_DITHER:
332
            /* Strip zero flag because of the nonzero dithering offset */
333
0
            for (int i = 0; i < 4; i++)
334
0
                op->comps.flags[i] = prev.flags[i] & ~SWS_COMP_ZERO;
335
0
            break;
336
0
        case SWS_OP_UNPACK:
337
0
            for (int i = 0; i < 4; i++) {
338
0
                const int pattern = op->pack.pattern[i];
339
0
                if (pattern) {
340
0
                    av_assert1(pattern < 32);
341
0
                    op->comps.flags[i] = prev.flags[0];
342
0
                    op->comps.min[i]   = Q(0);
343
0
                    op->comps.max[i]   = Q((1ULL << pattern) - 1);
344
0
                } else
345
0
                    op->comps.flags[i] = SWS_COMP_GARBAGE;
346
0
            }
347
0
            break;
348
0
        case SWS_OP_PACK: {
349
0
            SwsCompFlags flags = flags_identity;
350
0
            for (int i = 0; i < 4; i++) {
351
0
                if (op->pack.pattern[i])
352
0
                    flags = merge_comp_flags(flags, prev.flags[i]);
353
0
                if (i > 0) /* clear remaining comps for sanity */
354
0
                    op->comps.flags[i] = SWS_COMP_GARBAGE;
355
0
            }
356
0
            op->comps.flags[0] = flags;
357
0
            break;
358
0
        }
359
0
        case SWS_OP_CLEAR:
360
0
            for (int i = 0; i < 4; i++) {
361
0
                if (op->c.q4[i].den) {
362
0
                    op->comps.flags[i] = 0;
363
0
                    if (op->c.q4[i].num == 0)
364
0
                        op->comps.flags[i] |= SWS_COMP_ZERO;
365
0
                    if (op->c.q4[i].den == 1)
366
0
                        op->comps.flags[i] |= SWS_COMP_EXACT;
367
0
                } else {
368
0
                    op->comps.flags[i] = prev.flags[i];
369
0
                }
370
0
            }
371
0
            break;
372
0
        case SWS_OP_SWIZZLE:
373
0
            for (int i = 0; i < 4; i++)
374
0
                op->comps.flags[i] = prev.flags[op->swizzle.in[i]];
375
0
            break;
376
0
        case SWS_OP_CONVERT:
377
0
            for (int i = 0; i < 4; i++) {
378
0
                op->comps.flags[i] = prev.flags[i];
379
0
                if (ff_sws_pixel_type_is_int(op->convert.to))
380
0
                    op->comps.flags[i] |= SWS_COMP_EXACT;
381
0
            }
382
0
            break;
383
0
        case SWS_OP_LINEAR:
384
0
            for (int i = 0; i < 4; i++) {
385
0
                SwsCompFlags flags = flags_identity;
386
0
                AVRational min = Q(0), max = Q(0);
387
0
                for (int j = 0; j < 4; j++) {
388
0
                    const AVRational k = op->lin.m[i][j];
389
0
                    AVRational mink = av_mul_q(prev.min[j], k);
390
0
                    AVRational maxk = av_mul_q(prev.max[j], k);
391
0
                    if (k.num) {
392
0
                        flags = merge_comp_flags(flags, prev.flags[j]);
393
0
                        if (k.den != 1) /* fractional coefficient */
394
0
                            flags &= ~SWS_COMP_EXACT;
395
0
                        if (k.num < 0)
396
0
                            FFSWAP(AVRational, mink, maxk);
397
0
                        min = av_add_q(min, mink);
398
0
                        max = av_add_q(max, maxk);
399
0
                    }
400
0
                }
401
0
                if (op->lin.m[i][4].num) { /* nonzero offset */
402
0
                    flags &= ~SWS_COMP_ZERO;
403
0
                    if (op->lin.m[i][4].den != 1) /* fractional offset */
404
0
                        flags &= ~SWS_COMP_EXACT;
405
0
                    min = av_add_q(min, op->lin.m[i][4]);
406
0
                    max = av_add_q(max, op->lin.m[i][4]);
407
0
                }
408
0
                op->comps.flags[i] = flags;
409
0
                op->comps.min[i] = min;
410
0
                op->comps.max[i] = max;
411
0
            }
412
0
            break;
413
0
        case SWS_OP_SCALE:
414
0
            for (int i = 0; i < 4; i++) {
415
0
                op->comps.flags[i] = prev.flags[i];
416
0
                if (op->c.q.den != 1) /* fractional scale */
417
0
                    op->comps.flags[i] &= ~SWS_COMP_EXACT;
418
0
                if (op->c.q.num < 0)
419
0
                    FFSWAP(AVRational, op->comps.min[i], op->comps.max[i]);
420
0
            }
421
0
            break;
422
423
0
        case SWS_OP_INVALID:
424
0
        case SWS_OP_TYPE_NB:
425
0
            av_unreachable("Invalid operation type!");
426
0
        }
427
428
0
        prev = op->comps;
429
0
    }
430
431
    /* Backwards pass, solves for component dependencies */
432
0
    for (int n = ops->num_ops - 1; n >= 0; n--) {
433
0
        SwsOp *op = &ops->ops[n];
434
435
0
        switch (op->op) {
436
0
        case SWS_OP_READ:
437
0
        case SWS_OP_WRITE:
438
0
            for (int i = 0; i < op->rw.elems; i++)
439
0
                op->comps.unused[i] = op->op == SWS_OP_READ;
440
0
            for (int i = op->rw.elems; i < 4; i++)
441
0
                op->comps.unused[i] = next.unused[i];
442
0
            break;
443
0
        case SWS_OP_SWAP_BYTES:
444
0
        case SWS_OP_LSHIFT:
445
0
        case SWS_OP_RSHIFT:
446
0
        case SWS_OP_CONVERT:
447
0
        case SWS_OP_DITHER:
448
0
        case SWS_OP_MIN:
449
0
        case SWS_OP_MAX:
450
0
        case SWS_OP_SCALE:
451
0
            for (int i = 0; i < 4; i++)
452
0
                op->comps.unused[i] = next.unused[i];
453
0
            break;
454
0
        case SWS_OP_UNPACK: {
455
0
            bool unused = true;
456
0
            for (int i = 0; i < 4; i++) {
457
0
                if (op->pack.pattern[i])
458
0
                    unused &= next.unused[i];
459
0
                op->comps.unused[i] = i > 0;
460
0
            }
461
0
            op->comps.unused[0] = unused;
462
0
            break;
463
0
        }
464
0
        case SWS_OP_PACK:
465
0
            for (int i = 0; i < 4; i++) {
466
0
                if (op->pack.pattern[i])
467
0
                    op->comps.unused[i] = next.unused[0];
468
0
                else
469
0
                    op->comps.unused[i] = true;
470
0
            }
471
0
            break;
472
0
        case SWS_OP_CLEAR:
473
0
            for (int i = 0; i < 4; i++) {
474
0
                if (op->c.q4[i].den)
475
0
                    op->comps.unused[i] = true;
476
0
                else
477
0
                    op->comps.unused[i] = next.unused[i];
478
0
            }
479
0
            break;
480
0
        case SWS_OP_SWIZZLE: {
481
0
            bool unused[4] = { true, true, true, true };
482
0
            for (int i = 0; i < 4; i++)
483
0
                unused[op->swizzle.in[i]] &= next.unused[i];
484
0
            for (int i = 0; i < 4; i++)
485
0
                op->comps.unused[i] = unused[i];
486
0
            break;
487
0
        }
488
0
        case SWS_OP_LINEAR:
489
0
            for (int j = 0; j < 4; j++) {
490
0
                bool unused = true;
491
0
                for (int i = 0; i < 4; i++) {
492
0
                    if (op->lin.m[i][j].num)
493
0
                        unused &= next.unused[i];
494
0
                }
495
0
                op->comps.unused[j] = unused;
496
0
            }
497
0
            break;
498
0
        }
499
500
0
        next = op->comps;
501
0
    }
502
0
}
503
504
static void op_uninit(SwsOp *op)
505
0
{
506
0
    switch (op->op) {
507
0
    case SWS_OP_DITHER:
508
0
        av_refstruct_unref(&op->dither.matrix);
509
0
        break;
510
0
    }
511
512
0
    *op = (SwsOp) {0};
513
0
}
514
515
SwsOpList *ff_sws_op_list_alloc(void)
516
0
{
517
0
    SwsOpList *ops = av_mallocz(sizeof(SwsOpList));
518
0
    if (!ops)
519
0
        return NULL;
520
521
0
    ops->order_src = ops->order_dst = SWS_SWIZZLE(0, 1, 2, 3);
522
0
    ff_fmt_clear(&ops->src);
523
0
    ff_fmt_clear(&ops->dst);
524
0
    return ops;
525
0
}
526
527
void ff_sws_op_list_free(SwsOpList **p_ops)
528
0
{
529
0
    SwsOpList *ops = *p_ops;
530
0
    if (!ops)
531
0
        return;
532
533
0
    for (int i = 0; i < ops->num_ops; i++)
534
0
        op_uninit(&ops->ops[i]);
535
536
0
    av_freep(&ops->ops);
537
0
    av_free(ops);
538
0
    *p_ops = NULL;
539
0
}
540
541
SwsOpList *ff_sws_op_list_duplicate(const SwsOpList *ops)
542
0
{
543
0
    SwsOpList *copy = av_malloc(sizeof(*copy));
544
0
    if (!copy)
545
0
        return NULL;
546
547
0
    int num = ops->num_ops;
548
0
    if (num)
549
0
        num = 1 << av_ceil_log2(num);
550
551
0
    *copy = *ops;
552
0
    copy->ops = av_memdup(ops->ops, num * sizeof(ops->ops[0]));
553
0
    if (!copy->ops) {
554
0
        av_free(copy);
555
0
        return NULL;
556
0
    }
557
558
0
    for (int i = 0; i < ops->num_ops; i++) {
559
0
        const SwsOp *op = &ops->ops[i];
560
0
        switch (op->op) {
561
0
        case SWS_OP_DITHER:
562
0
            av_refstruct_ref(copy->ops[i].dither.matrix);
563
0
            break;
564
0
        }
565
0
    }
566
567
0
    return copy;
568
0
}
569
570
const SwsOp *ff_sws_op_list_input(const SwsOpList *ops)
571
0
{
572
0
    if (!ops->num_ops)
573
0
        return NULL;
574
575
0
    const SwsOp *read = &ops->ops[0];
576
0
    return read->op == SWS_OP_READ ? read : NULL;
577
0
}
578
579
const SwsOp *ff_sws_op_list_output(const SwsOpList *ops)
580
0
{
581
0
    if (!ops->num_ops)
582
0
        return NULL;
583
584
0
    const SwsOp *write = &ops->ops[ops->num_ops - 1];
585
0
    return write->op == SWS_OP_WRITE ? write : NULL;
586
0
}
587
588
void ff_sws_op_list_remove_at(SwsOpList *ops, int index, int count)
589
0
{
590
0
    const int end = ops->num_ops - count;
591
0
    av_assert2(index >= 0 && count >= 0 && index + count <= ops->num_ops);
592
0
    op_uninit(&ops->ops[index]);
593
0
    for (int i = index; i < end; i++)
594
0
        ops->ops[i] = ops->ops[i + count];
595
0
    ops->num_ops = end;
596
0
}
597
598
int ff_sws_op_list_insert_at(SwsOpList *ops, int index, SwsOp *op)
599
0
{
600
0
    void *ret = av_dynarray2_add((void **) &ops->ops, &ops->num_ops, sizeof(*op), NULL);
601
0
    if (!ret) {
602
0
        op_uninit(op);
603
0
        return AVERROR(ENOMEM);
604
0
    }
605
606
0
    for (int i = ops->num_ops - 1; i > index; i--)
607
0
        ops->ops[i] = ops->ops[i - 1];
608
0
    ops->ops[index] = *op;
609
0
    return 0;
610
0
}
611
612
int ff_sws_op_list_append(SwsOpList *ops, SwsOp *op)
613
0
{
614
0
    return ff_sws_op_list_insert_at(ops, ops->num_ops, op);
615
0
}
616
617
bool ff_sws_op_list_is_noop(const SwsOpList *ops)
618
0
{
619
0
    if (!ops->num_ops)
620
0
        return true;
621
622
0
    const SwsOp *read  = ff_sws_op_list_input(ops);
623
0
    const SwsOp *write = ff_sws_op_list_output(ops);
624
0
    if (!read || !write || ops->num_ops > 2 ||
625
0
        read->type != write->type ||
626
0
        read->rw.packed != write->rw.packed ||
627
0
        read->rw.elems != write->rw.elems ||
628
0
        read->rw.frac != write->rw.frac)
629
0
        return false;
630
631
    /**
632
     * Note that this check is unlikely to ever be hit in practice, since it
633
     * would imply the existence of planar formats with different plane orders
634
     * between them, e.g. rgbap <-> gbrap, which doesn't currently exist.
635
     * However, the check is cheap and lets me sleep at night.
636
     */
637
0
    const int num_planes = read->rw.packed ? 1 : read->rw.elems;
638
0
    for (int i = 0; i < num_planes; i++) {
639
0
        if (ops->order_src.in[i] != ops->order_dst.in[i])
640
0
            return false;
641
0
    }
642
643
0
    return true;
644
0
}
645
646
int ff_sws_op_list_max_size(const SwsOpList *ops)
647
0
{
648
0
    int max_size = 0;
649
0
    for (int i = 0; i < ops->num_ops; i++) {
650
0
        const int size = ff_sws_pixel_type_size(ops->ops[i].type);
651
0
        max_size = FFMAX(max_size, size);
652
0
    }
653
654
0
    return max_size;
655
0
}
656
657
uint32_t ff_sws_linear_mask(const SwsLinearOp c)
658
0
{
659
0
    uint32_t mask = 0;
660
0
    for (int i = 0; i < 4; i++) {
661
0
        for (int j = 0; j < 5; j++) {
662
0
            if (av_cmp_q(c.m[i][j], Q(i == j)))
663
0
                mask |= SWS_MASK(i, j);
664
0
        }
665
0
    }
666
0
    return mask;
667
0
}
668
669
static const char *describe_lin_mask(uint32_t mask)
670
0
{
671
    /* Try to be fairly descriptive without assuming too much */
672
0
    static const struct {
673
0
        char name[24];
674
0
        uint32_t mask;
675
0
    } patterns[] = {
676
0
        { "noop",               0 },
677
0
        { "luma",               SWS_MASK_LUMA },
678
0
        { "alpha",              SWS_MASK_ALPHA },
679
0
        { "luma+alpha",         SWS_MASK_LUMA | SWS_MASK_ALPHA },
680
0
        { "dot3",               0x7 },
681
0
        { "dot4",               0xF },
682
0
        { "row0",               SWS_MASK_ROW(0) },
683
0
        { "row0+alpha",         SWS_MASK_ROW(0) | SWS_MASK_ALPHA },
684
0
        { "col0",               SWS_MASK_COL(0) },
685
0
        { "col0+off3",          SWS_MASK_COL(0) | SWS_MASK_OFF3 },
686
0
        { "off3",               SWS_MASK_OFF3 },
687
0
        { "off3+alpha",         SWS_MASK_OFF3 | SWS_MASK_ALPHA },
688
0
        { "diag3",              SWS_MASK_DIAG3 },
689
0
        { "diag4",              SWS_MASK_DIAG4 },
690
0
        { "diag3+alpha",        SWS_MASK_DIAG3 | SWS_MASK_ALPHA },
691
0
        { "diag3+off3",         SWS_MASK_DIAG3 | SWS_MASK_OFF3 },
692
0
        { "diag3+off3+alpha",   SWS_MASK_DIAG3 | SWS_MASK_OFF3 | SWS_MASK_ALPHA },
693
0
        { "diag4+off4",         SWS_MASK_DIAG4 | SWS_MASK_OFF4 },
694
0
        { "matrix3",            SWS_MASK_MAT3 },
695
0
        { "matrix3+off3",       SWS_MASK_MAT3 | SWS_MASK_OFF3 },
696
0
        { "matrix3+off3+alpha", SWS_MASK_MAT3 | SWS_MASK_OFF3 | SWS_MASK_ALPHA },
697
0
        { "matrix4",            SWS_MASK_MAT4 },
698
0
        { "matrix4+off4",       SWS_MASK_MAT4 | SWS_MASK_OFF4 },
699
0
    };
700
701
0
    for (int i = 0; i < FF_ARRAY_ELEMS(patterns); i++) {
702
0
        if (!(mask & ~patterns[i].mask))
703
0
            return patterns[i].name;
704
0
    }
705
706
0
    av_unreachable("Invalid linear mask!");
707
0
    return "ERR";
708
0
}
709
710
static char describe_comp_flags(SwsCompFlags flags)
711
0
{
712
0
    if (flags & SWS_COMP_GARBAGE)
713
0
        return 'X';
714
0
    else if (flags & SWS_COMP_ZERO)
715
0
        return '0';
716
0
    else if (flags & SWS_COMP_SWAPPED)
717
0
        return 'z';
718
0
    else if (flags & SWS_COMP_EXACT)
719
0
        return '+';
720
0
    else
721
0
        return '.';
722
0
}
723
724
static const char *describe_order(SwsSwizzleOp order, int planes, char buf[32])
725
0
{
726
0
    if (order.mask == SWS_SWIZZLE(0, 1, 2, 3).mask)
727
0
        return "";
728
729
0
    av_strlcpy(buf, ", via {", 32);
730
0
    for (int i = 0; i < planes; i++)
731
0
        av_strlcatf(buf, 32, "%s%d", i ? ", " : "", order.in[i]);
732
0
    av_strlcat(buf, "}", 32);
733
0
    return buf;
734
0
}
735
736
static const char *print_q(const AVRational q, char buf[], int buf_len)
737
0
{
738
0
    if (!q.den) {
739
0
        return q.num > 0 ? "inf" : q.num < 0 ? "-inf" : "nan";
740
0
    } else if (q.den == 1) {
741
0
        snprintf(buf, buf_len, "%d", q.num);
742
0
        return buf;
743
0
    } else if (abs(q.num) > 1000 || abs(q.den) > 1000) {
744
0
        snprintf(buf, buf_len, "%f", av_q2d(q));
745
0
        return buf;
746
0
    } else {
747
0
        snprintf(buf, buf_len, "%d/%d", q.num, q.den);
748
0
        return buf;
749
0
    }
750
0
}
751
752
0
#define PRINTQ(q) print_q(q, (char[32]){0}, sizeof(char[32]))
753
754
void ff_sws_op_list_print(void *log, int lev, int lev_extra,
755
                          const SwsOpList *ops)
756
0
{
757
0
    if (!ops->num_ops) {
758
0
        av_log(log, lev, "  (empty)\n");
759
0
        return;
760
0
    }
761
762
0
    for (int i = 0; i < ops->num_ops; i++) {
763
0
        const SwsOp *op   = &ops->ops[i];
764
0
        const SwsOp *next = i + 1 < ops->num_ops ? &ops->ops[i + 1] : op;
765
0
        const char *name  = ff_sws_op_type_name(op->op);
766
0
        char buf[32];
767
768
0
        av_log(log, lev, "  [%3s %c%c%c%c -> %c%c%c%c] ",
769
0
               ff_sws_pixel_type_name(op->type),
770
0
               op->comps.unused[0] ? 'X' : '.',
771
0
               op->comps.unused[1] ? 'X' : '.',
772
0
               op->comps.unused[2] ? 'X' : '.',
773
0
               op->comps.unused[3] ? 'X' : '.',
774
0
               next->comps.unused[0] ? 'X' : describe_comp_flags(op->comps.flags[0]),
775
0
               next->comps.unused[1] ? 'X' : describe_comp_flags(op->comps.flags[1]),
776
0
               next->comps.unused[2] ? 'X' : describe_comp_flags(op->comps.flags[2]),
777
0
               next->comps.unused[3] ? 'X' : describe_comp_flags(op->comps.flags[3]));
778
779
0
        switch (op->op) {
780
0
        case SWS_OP_INVALID:
781
0
        case SWS_OP_SWAP_BYTES:
782
0
            av_log(log, lev, "%s\n", name);
783
0
            break;
784
0
        case SWS_OP_READ:
785
0
        case SWS_OP_WRITE:
786
0
            av_log(log, lev, "%-20s: %d elem(s) %s >> %d%s\n", name,
787
0
                   op->rw.elems,  op->rw.packed ? "packed" : "planar",
788
0
                   op->rw.frac,
789
0
                   describe_order(op->op == SWS_OP_READ ? ops->order_src
790
0
                                                        : ops->order_dst,
791
0
                                  op->rw.packed ? 1 : op->rw.elems, buf));
792
0
            break;
793
0
        case SWS_OP_LSHIFT:
794
0
            av_log(log, lev, "%-20s: << %u\n", name, op->c.u);
795
0
            break;
796
0
        case SWS_OP_RSHIFT:
797
0
            av_log(log, lev, "%-20s: >> %u\n", name, op->c.u);
798
0
            break;
799
0
        case SWS_OP_PACK:
800
0
        case SWS_OP_UNPACK:
801
0
            av_log(log, lev, "%-20s: {%d %d %d %d}\n", name,
802
0
                   op->pack.pattern[0], op->pack.pattern[1],
803
0
                   op->pack.pattern[2], op->pack.pattern[3]);
804
0
            break;
805
0
        case SWS_OP_CLEAR:
806
0
            av_log(log, lev, "%-20s: {%s %s %s %s}\n", name,
807
0
                   op->c.q4[0].den ? PRINTQ(op->c.q4[0]) : "_",
808
0
                   op->c.q4[1].den ? PRINTQ(op->c.q4[1]) : "_",
809
0
                   op->c.q4[2].den ? PRINTQ(op->c.q4[2]) : "_",
810
0
                   op->c.q4[3].den ? PRINTQ(op->c.q4[3]) : "_");
811
0
            break;
812
0
        case SWS_OP_SWIZZLE:
813
0
            av_log(log, lev, "%-20s: %d%d%d%d\n", name,
814
0
                   op->swizzle.x, op->swizzle.y, op->swizzle.z, op->swizzle.w);
815
0
            break;
816
0
        case SWS_OP_CONVERT:
817
0
            av_log(log, lev, "%-20s: %s -> %s%s\n", name,
818
0
                   ff_sws_pixel_type_name(op->type),
819
0
                   ff_sws_pixel_type_name(op->convert.to),
820
0
                   op->convert.expand ? " (expand)" : "");
821
0
            break;
822
0
        case SWS_OP_DITHER:
823
0
            av_log(log, lev, "%-20s: %dx%d matrix + {%d %d %d %d}\n", name,
824
0
                    1 << op->dither.size_log2, 1 << op->dither.size_log2,
825
0
                    op->dither.y_offset[0], op->dither.y_offset[1],
826
0
                    op->dither.y_offset[2], op->dither.y_offset[3]);
827
0
            break;
828
0
        case SWS_OP_MIN:
829
0
            av_log(log, lev, "%-20s: x <= {%s %s %s %s}\n", name,
830
0
                    op->c.q4[0].den ? PRINTQ(op->c.q4[0]) : "_",
831
0
                    op->c.q4[1].den ? PRINTQ(op->c.q4[1]) : "_",
832
0
                    op->c.q4[2].den ? PRINTQ(op->c.q4[2]) : "_",
833
0
                    op->c.q4[3].den ? PRINTQ(op->c.q4[3]) : "_");
834
0
            break;
835
0
        case SWS_OP_MAX:
836
0
            av_log(log, lev, "%-20s: {%s %s %s %s} <= x\n", name,
837
0
                    op->c.q4[0].den ? PRINTQ(op->c.q4[0]) : "_",
838
0
                    op->c.q4[1].den ? PRINTQ(op->c.q4[1]) : "_",
839
0
                    op->c.q4[2].den ? PRINTQ(op->c.q4[2]) : "_",
840
0
                    op->c.q4[3].den ? PRINTQ(op->c.q4[3]) : "_");
841
0
            break;
842
0
        case SWS_OP_LINEAR:
843
0
            av_log(log, lev, "%-20s: %s [[%s %s %s %s %s] "
844
0
                                        "[%s %s %s %s %s] "
845
0
                                        "[%s %s %s %s %s] "
846
0
                                        "[%s %s %s %s %s]]\n",
847
0
                   name, describe_lin_mask(op->lin.mask),
848
0
                   PRINTQ(op->lin.m[0][0]), PRINTQ(op->lin.m[0][1]), PRINTQ(op->lin.m[0][2]), PRINTQ(op->lin.m[0][3]), PRINTQ(op->lin.m[0][4]),
849
0
                   PRINTQ(op->lin.m[1][0]), PRINTQ(op->lin.m[1][1]), PRINTQ(op->lin.m[1][2]), PRINTQ(op->lin.m[1][3]), PRINTQ(op->lin.m[1][4]),
850
0
                   PRINTQ(op->lin.m[2][0]), PRINTQ(op->lin.m[2][1]), PRINTQ(op->lin.m[2][2]), PRINTQ(op->lin.m[2][3]), PRINTQ(op->lin.m[2][4]),
851
0
                   PRINTQ(op->lin.m[3][0]), PRINTQ(op->lin.m[3][1]), PRINTQ(op->lin.m[3][2]), PRINTQ(op->lin.m[3][3]), PRINTQ(op->lin.m[3][4]));
852
0
            break;
853
0
        case SWS_OP_SCALE:
854
0
            av_log(log, lev, "%-20s: * %s\n", name, PRINTQ(op->c.q));
855
0
            break;
856
0
        case SWS_OP_TYPE_NB:
857
0
            break;
858
0
        }
859
860
0
        if (op->comps.min[0].den || op->comps.min[1].den ||
861
0
            op->comps.min[2].den || op->comps.min[3].den ||
862
0
            op->comps.max[0].den || op->comps.max[1].den ||
863
0
            op->comps.max[2].den || op->comps.max[3].den)
864
0
        {
865
0
            av_log(log, lev_extra, "    min: {%s, %s, %s, %s}, max: {%s, %s, %s, %s}\n",
866
0
                   next->comps.unused[0] ? "_" : PRINTQ(op->comps.min[0]),
867
0
                   next->comps.unused[1] ? "_" : PRINTQ(op->comps.min[1]),
868
0
                   next->comps.unused[2] ? "_" : PRINTQ(op->comps.min[2]),
869
0
                   next->comps.unused[3] ? "_" : PRINTQ(op->comps.min[3]),
870
0
                   next->comps.unused[0] ? "_" : PRINTQ(op->comps.max[0]),
871
0
                   next->comps.unused[1] ? "_" : PRINTQ(op->comps.max[1]),
872
0
                   next->comps.unused[2] ? "_" : PRINTQ(op->comps.max[2]),
873
0
                   next->comps.unused[3] ? "_" : PRINTQ(op->comps.max[3]));
874
0
        }
875
876
0
    }
877
878
0
    av_log(log, lev, "    (X = unused, z = byteswapped, + = exact, 0 = zero)\n");
879
0
}