Coverage Report

Created: 2026-10-28 07:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ffmpeg/libswscale/uops.c
Line
Count
Source
1
/**
2
 * Copyright (C) 2026 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 <stdbool.h>
22
23
#include "libavutil/avassert.h"
24
#include "libavutil/mem.h"
25
#include "libavutil/refstruct.h"
26
27
#include "ops.h"
28
#include "uops.h"
29
#include "uops_list.h"
30
31
int ff_sws_uop_cmp(const SwsUOp *a, const SwsUOp *b)
32
0
{
33
0
    if (a->type != b->type)
34
0
        return (int) a->type - b->type;
35
0
    if (a->uop != b->uop)
36
0
        return (int) a->uop - b->uop;
37
0
    if (a->mask != b->mask)
38
0
        return (int) a->mask - b->mask;
39
0
    return memcmp(&a->par, &b->par, sizeof(a->par));
40
0
}
41
42
static const struct {
43
    char abbr[32];
44
} uop_names[SWS_UOP_TYPE_NB] = {
45
#define UOP_NAME(OP, ABBR) [OP] = { ABBR },
46
    UOPS_LIST(UOP_NAME)
47
#undef UOP_NAME
48
};
49
50
void ff_sws_uop_name(const SwsUOp *op, char buf[SWS_UOP_NAME_MAX])
51
0
{
52
0
    AVBPrint bp;
53
0
    av_bprint_init_for_buffer(&bp, buf, SWS_UOP_NAME_MAX);
54
55
0
    if (op->type != SWS_PIXEL_NONE)
56
0
        av_bprintf(&bp, "%s_", ff_sws_pixel_type_name(op->type));
57
0
    av_bprintf(&bp, "%s", uop_names[op->uop].abbr);
58
59
0
    if (op->mask)
60
0
        av_bprintf(&bp, "_%s", ff_sws_comp_mask_str(op->mask));
61
62
0
    const SwsUOpParams *par = &op->par;
63
0
    switch (op->uop) {
64
0
    case SWS_UOP_READ_PLANAR_FH:
65
0
    case SWS_UOP_READ_PLANAR_FV:
66
0
    case SWS_UOP_READ_PLANAR_FV_FMA:
67
0
        av_bprintf(&bp, "_%s", ff_sws_pixel_type_name(par->filter.type));
68
0
        break;
69
0
    case SWS_UOP_RW_SHUFFLE:
70
0
        av_bprintf(&bp, "_%x_%u_%u", par->shuffle.clear_value,
71
0
                   par->shuffle.read_size, par->shuffle.write_size);
72
0
        break;
73
0
    case SWS_UOP_LSHIFT:
74
0
    case SWS_UOP_RSHIFT:
75
0
        av_bprintf(&bp, "_%u", par->shift.amount);
76
0
        break;
77
0
    case SWS_UOP_PERMUTE:
78
0
    case SWS_UOP_COPY:
79
0
        av_bprint_chars(&bp, '_', 1);
80
0
        for (int i = 0; i < par->move.num_moves; i++)
81
0
            av_bprint_chars(&bp, "txyzw"[par->move.dst[i] + 1], 1);
82
0
        av_bprint_chars(&bp, '_', 1);
83
0
        for (int i = 0; i < par->move.num_moves; i++)
84
0
            av_bprint_chars(&bp, "txyzw"[par->move.src[i] + 1], 1);
85
0
        break;
86
0
    case SWS_UOP_PACK:
87
0
    case SWS_UOP_UNPACK:
88
0
        av_bprint_chars(&bp, '_', 1);
89
0
        for (int i = 0; i < 4 && par->pack.pattern[i]; i++)
90
0
            av_bprintf(&bp, "%x", par->pack.pattern[i]);
91
0
        break;
92
0
    case SWS_UOP_CLEAR:
93
0
        av_bprint_chars(&bp, '_', 1);
94
0
        for (int i = 0; i < 4; i++) {
95
0
            if (!SWS_COMP_TEST(op->mask, i))
96
0
                continue;
97
0
            else if (SWS_COMP_TEST(par->clear.one, i))
98
0
                av_bprint_chars(&bp, '1', 1);
99
0
            else if (SWS_COMP_TEST(par->clear.zero, i))
100
0
                av_bprint_chars(&bp, '0', 1);
101
0
            else
102
0
                av_bprint_chars(&bp, 'x', 1);
103
0
        }
104
0
        break;
105
0
    case SWS_UOP_LINEAR:
106
0
    case SWS_UOP_LINEAR_FMA:
107
0
        for (int i = 0; i < 4; i++) {
108
0
            if (!SWS_COMP_TEST(op->mask, i))
109
0
                continue;
110
0
            av_bprint_chars(&bp, '_', 1);
111
0
            for (int j = 0; j < 5; j++) {
112
0
                if (par->lin.one & SWS_MASK(i, j))
113
0
                    av_bprint_chars(&bp, '1', 1);
114
0
                else if (par->lin.zero & SWS_MASK(i, j))
115
0
                    av_bprint_chars(&bp, '0', 1);
116
0
                else if (par->lin.exact & SWS_MASK(i, j))
117
0
                    av_bprint_chars(&bp, 'X', 1);
118
0
                else
119
0
                    av_bprint_chars(&bp, 'x', 1);
120
0
            }
121
0
        }
122
0
        break;
123
0
    case SWS_UOP_DITHER:
124
0
        for (int i = 0; i < 4; i++) {
125
0
            if (SWS_COMP_TEST(op->mask, i))
126
0
                av_bprintf(&bp, "_%d", par->dither.y_offset[i]);
127
0
        }
128
0
        const unsigned size = 1u << par->dither.size_log2;
129
0
        av_bprintf(&bp, "_%ux%u", size, size);
130
0
        break;
131
0
    case SWS_UOP_LUT_3D:
132
0
        av_bprintf(&bp, "_%s", par->lut3d.dynamic ? "dynamic" : "static");
133
0
        break;
134
0
    }
135
136
0
    av_assert0(av_bprint_is_complete(&bp));
137
0
}
138
139
static void uop_uninit(SwsUOp *uop)
140
0
{
141
0
    switch (uop->uop) {
142
0
    case SWS_UOP_DITHER:
143
0
        av_refstruct_unref(&uop->data.ptr);
144
0
        break;
145
0
    case SWS_UOP_READ_PLANAR_FH:
146
0
    case SWS_UOP_READ_PLANAR_FV:
147
0
    case SWS_UOP_READ_PLANAR_FV_FMA:
148
0
        av_refstruct_unref(&uop->data.kernel);
149
0
        break;
150
0
    case SWS_UOP_LUT_3D:
151
0
        av_refstruct_unref(&uop->data.lut3d);
152
0
        break;
153
0
    }
154
155
0
    *uop = (SwsUOp) {0};
156
0
}
157
158
void ff_sws_uop_list_free(SwsUOpList **p_ops)
159
0
{
160
0
    SwsUOpList *ops = *p_ops;
161
0
    if (!ops)
162
0
        return;
163
164
0
    for (int i = 0; i < ops->num_ops; i++)
165
0
        uop_uninit(&ops->ops[i]);
166
167
0
    av_freep(&ops->ops);
168
0
    av_free(ops);
169
0
    *p_ops = NULL;
170
0
}
171
172
SwsUOpList *ff_sws_uop_list_alloc(void)
173
0
{
174
0
    return av_mallocz(sizeof(SwsUOpList));
175
0
}
176
177
int ff_sws_uop_list_append(SwsUOpList *uops, SwsUOp *uop)
178
0
{
179
0
    if (!av_dynarray2_add((void **) &uops->ops, &uops->num_ops,
180
0
                          sizeof(*uop), (uint8_t *) uop))
181
0
    {
182
0
        uop_uninit(uop);
183
0
        return AVERROR(ENOMEM);
184
0
    }
185
186
0
    *uop = (SwsUOp) {0};
187
0
    return 0;
188
0
}
189
190
void ff_sws_uop_list_remove_at(SwsUOpList *uops, int index, int count)
191
0
{
192
0
    const int end = uops->num_ops - count;
193
0
    av_assert2(index >= 0 && count >= 0 && index + count <= uops->num_ops);
194
0
    for (int i = 0; i < count; i++)
195
0
        uop_uninit(&uops->ops[index + i]);
196
0
    for (int i = index; i < end; i++)
197
0
        uops->ops[i] = uops->ops[i + count];
198
0
    uops->num_ops = end;
199
0
}
200
201
int ff_sws_dither_height(const SwsDitherUOp *dither)
202
0
{
203
0
    int max_offset = 0;
204
0
    for (int i = 0; i < 4; i++)
205
0
        max_offset = FFMAX(max_offset, dither->y_offset[i]);
206
0
    return (1 << dither->size_log2) + max_offset;
207
0
}
208
209
/* Conditionally promotes a pixel type to integer only if allowed by the flags */
210
static SwsPixelType pixel_type_to_int(SwsUOpFlags flags, const SwsPixelType type)
211
0
{
212
0
    if (!(flags & SWS_UOP_FLAG_PUNNING))
213
0
        return type;
214
215
0
    switch (ff_sws_pixel_type_size(type)) {
216
0
    case 1: return SWS_PIXEL_U8;
217
0
    case 2: return SWS_PIXEL_U16;
218
0
    case 4: return SWS_PIXEL_U32;
219
0
    default: break;
220
0
    }
221
222
0
    av_unreachable("Invalid pixel type!");
223
0
    return SWS_PIXEL_NONE;
224
0
}
225
226
static bool exact_product_f32(float a, float b)
227
0
{
228
0
    volatile float prod   = a * b;
229
0
    volatile float result = b ? prod / b : 0.0f;
230
0
    return !b || result == a;
231
0
}
232
233
static bool exact_prod(SwsPixel coef, const SwsOpPad *input)
234
0
{
235
0
    if (ff_sws_pixel_type_is_int(input->type))
236
0
        return true;
237
0
    else if (!input->min.den || !input->max.den)
238
0
        return false; /* unknown bounds */
239
240
0
    const SwsPixel min = ff_sws_pixel_from_q64(input->type, input->min);
241
0
    const SwsPixel max = ff_sws_pixel_from_q64(input->type, input->max);
242
0
    switch (input->type) {
243
0
    case SWS_PIXEL_F32:
244
0
        return exact_product_f32(coef.f32, min.f32) &&
245
0
               exact_product_f32(coef.f32, max.f32);
246
0
    }
247
248
0
    av_unreachable("Invalid pixel type!");
249
0
    return false;
250
0
}
251
252
static bool check_filter_fma(SwsContext *ctx, SwsUOpFlags flags, const SwsOp *op)
253
0
{
254
0
    if (!(flags & SWS_UOP_FLAG_FMA))
255
0
        return false;
256
0
    if (!(ctx->flags & SWS_BITEXACT))
257
0
        return true;
258
0
    if (!ff_sws_pixel_type_is_int(op->rw.type))
259
0
        return false;
260
261
0
    const int bits = ff_sws_pixel_type_size(op->rw.type) * 8;
262
0
    const uint64_t max_val = UINT64_MAX >> (64 - bits);
263
264
    /* Maximum value representable losslessly as float. Note that this is
265
     * currently true only for U8, but that may change if we ever update the
266
     * value of SWS_FILTER_SCALE. */
267
0
    return max_val * SWS_FILTER_SCALE <= (1 << 22);
268
0
}
269
270
static int translate_rw_op(SwsContext *ctx, SwsUOpList *ops, SwsUOpFlags flags,
271
                           const SwsOp *op)
272
0
{
273
0
    SwsUOp uop = {
274
0
        .type = op->rw.type,
275
0
        .mask = SWS_COMP_MASK(op->rw.elems > 0, op->rw.elems > 1,
276
0
                              op->rw.elems > 2, op->rw.elems > 3),
277
0
    };
278
279
    /* Non-filtered reads don't care about the exact pixel contents */
280
0
    if (!op->rw.filter.op)
281
0
        uop.type = pixel_type_to_int(flags, op->rw.type);
282
283
0
    const bool is_read = op->op == SWS_OP_READ;
284
0
    if (op->rw.filter.op) {
285
0
        if (op->op == SWS_OP_WRITE || op->rw.frac || op->rw.mode != SWS_RW_PLANAR)
286
0
            return AVERROR(ENOTSUP);
287
0
        uop.par.filter.type = op->rw.filter.type;
288
0
        uop.data.kernel = av_refstruct_ref(op->rw.filter.kernel);
289
0
        if (op->rw.filter.op == SWS_OP_FILTER_H) {
290
0
            uop.uop = SWS_UOP_READ_PLANAR_FH;
291
0
        } else if (check_filter_fma(ctx, flags, op)) {
292
0
            uop.uop = SWS_UOP_READ_PLANAR_FV_FMA;
293
0
        } else {
294
0
            uop.uop = SWS_UOP_READ_PLANAR_FV;
295
0
        }
296
0
    } else if (op->rw.mode == SWS_RW_PACKED && op->rw.elems > 1) {
297
0
        if (op->rw.frac)
298
0
            return AVERROR(ENOTSUP);
299
0
        uop.uop = is_read ? SWS_UOP_READ_PACKED : SWS_UOP_WRITE_PACKED;
300
0
    } else if (op->rw.mode == SWS_RW_PALETTE) {
301
0
        if (!(flags & SWS_UOP_FLAG_READ_PALETTE) || op->rw.frac || !is_read)
302
0
            return AVERROR(ENOTSUP);
303
0
        uop.uop = SWS_UOP_READ_PALETTE;
304
0
    } else if (op->rw.frac == 3) {
305
0
        uop.uop = is_read ? SWS_UOP_READ_BIT : SWS_UOP_WRITE_BIT;
306
0
    } else if (op->rw.frac == 1) {
307
0
        uop.uop = is_read ? SWS_UOP_READ_NIBBLE : SWS_UOP_WRITE_NIBBLE;
308
0
    } else {
309
0
        av_assert0(!op->rw.frac);
310
0
        uop.uop = is_read ? SWS_UOP_READ_PLANAR : SWS_UOP_WRITE_PLANAR;
311
0
    }
312
313
0
    const int planes = ff_sws_rw_op_planes(op);
314
0
    if (op->op == SWS_OP_READ) {
315
0
        ops->planes_in  |= SWS_COMP_ELEMS(planes);
316
0
    } else {
317
0
        ops->planes_out |= SWS_COMP_ELEMS(planes);
318
0
    }
319
320
0
    return ff_sws_uop_list_append(ops, &uop);
321
0
}
322
323
static int count_idx(const int *arr, size_t size, int val)
324
0
{
325
0
    int num = 0;
326
0
    for (size_t i = 0; i < size; i++) {
327
0
        if (arr[i] == val)
328
0
            num++;
329
0
    }
330
331
0
    return num;
332
0
}
333
334
static int translate_swizzle(SwsUOpList *ops, SwsUOpFlags flags, const SwsOp *op)
335
0
{
336
0
    SwsUOp uop = {
337
0
        .uop  = SWS_UOP_PERMUTE,
338
0
        .type = pixel_type_to_int(flags, ff_sws_op_input_type(op)),
339
0
        .mask = ff_sws_comp_mask_needed(op),
340
0
    };
341
0
    SwsMoveUOp *par = &uop.par.move;
342
343
    /* Mask of components that are not yet satisfied */
344
0
    SwsCompMask todo = uop.mask;
345
0
    for (int i = 0; i < 4; i++) {
346
0
        if (op->swizzle.in[i] == i)
347
0
            todo &= ~SWS_COMP(i);
348
0
    }
349
350
    /* Mask of components whose value is required for the final output */
351
0
    SwsCompMask needed = 0;
352
0
    for (int i = 0; i < 4; i++) {
353
0
        if (SWS_OP_NEEDED(op, i))
354
0
            needed |= SWS_COMP(op->swizzle.in[i]);
355
0
    }
356
357
    /* Current mapping of registers to components */
358
0
    int idx[4 + 1] = { 0, 1, 2, 3, -1 }; /* +1 for tmp */
359
360
    /* Decompose the swizzle mask into a series of register-register moves */
361
0
    while (todo) {
362
0
        int dst = -1, src = -1;
363
364
        /* Find next unsatisfied dst <- src move that doesn't clobber a value */
365
0
        for (dst = 0; dst < 4; dst++) {
366
0
            if (!SWS_COMP_TEST(todo, dst))
367
0
                continue; /* already satisfied */
368
0
            const int cur = idx[dst];
369
0
            if (count_idx(idx, FF_ARRAY_ELEMS(idx), cur) == 1 && SWS_COMP_TEST(needed, cur))
370
0
                continue; /* clobbers last remaining, still-needed value */
371
0
            for (src = 0; src < FF_ARRAY_ELEMS(idx); src++) {
372
0
                if (idx[src] == op->swizzle.in[dst]) {
373
                    /* Prevent read-after-write dependency. */
374
0
                    if (par->num_moves > 0 && src == par->dst[par->num_moves - 1])
375
0
                        src = par->src[par->num_moves - 1];
376
0
                    break;
377
0
                }
378
0
            }
379
0
            av_assert1(src < FF_ARRAY_ELEMS(idx));
380
0
            todo &= ~SWS_COMP(dst);
381
0
            break;
382
0
        }
383
384
0
        if (dst == 4) {
385
            /* Stuck in a cycle, break it by saving to the scratch register */
386
0
            dst = 4;
387
0
            for (src = 0; src < 4; src++) {
388
0
                if (SWS_COMP_TEST(todo, src)) {
389
0
                    needed &= ~SWS_COMP(idx[src]);
390
0
                    break;
391
0
                }
392
0
            }
393
0
            av_assert1(src < 4);
394
0
        }
395
396
0
        av_assert0(par->num_moves < SWS_UOP_MOVE_MAX);
397
0
        par->dst[par->num_moves] = dst > 3 ? -1 : dst;
398
0
        par->src[par->num_moves] = src > 3 ? -1 : src;
399
0
        par->num_moves++;
400
0
        idx[dst] = idx[src];
401
0
    }
402
403
    /* Check for duplicates in the final register map */
404
0
    SwsCompMask seen = 0;
405
0
    for (int i = 0; i < 4; i++) {
406
0
        if (!SWS_COMP_TEST(uop.mask, i))
407
0
            continue;
408
0
        av_assert2(idx[i] >= 0); /* should be no tmp register */
409
0
        const SwsCompMask bit = SWS_COMP(idx[i]);
410
0
        if (seen & bit) {
411
0
            uop.uop = SWS_UOP_COPY;
412
0
            break;
413
0
        }
414
0
        seen |= bit;
415
0
    }
416
417
    /* Add any extra unused components to the mask, to prevent generating
418
     * duplicate uops like permute_xyz_txy_xyt and permute_xyzw_txy_xyt */
419
0
    for (int i = 0; i < 4; i++) {
420
0
        const SwsCompMask bit = SWS_COMP(i);
421
0
        if (!(seen & bit) && idx[i] == i)
422
0
            uop.mask |= bit;
423
0
    }
424
425
0
    return ff_sws_uop_list_append(ops, &uop);
426
0
}
427
428
static int translate_dither_op(SwsUOpList *ops, SwsUOpFlags flags, const SwsOp *op)
429
0
{
430
0
    SwsUOp uop = {
431
0
        .type = ff_sws_op_input_type(op),
432
0
        .uop  = SWS_UOP_DITHER,
433
0
        .par.dither.size_log2 = op->dither.size_log2,
434
0
    };
435
436
0
    if ((flags & SWS_UOP_FLAG_ADD) && op->dither.size_log2 == 0) {
437
        /* Constant offset */
438
0
        const SwsPixel val = ff_sws_pixel_from_q64(uop.type, op->dither.matrix[0]);
439
0
        uop.uop = SWS_UOP_ADD;
440
0
        for (int i = 0; i < 4; i++) {
441
0
            if (!SWS_OP_NEEDED(op, i) || op->dither.y_offset[i] < 0)
442
0
                continue;
443
0
            uop.mask |= SWS_COMP(i);
444
0
            uop.data.vec4[i] = val;
445
0
        }
446
447
0
        return ff_sws_uop_list_append(ops, &uop);
448
0
    }
449
450
0
    const int size = 1 << op->dither.size_log2;
451
0
    for (int i = 0; i < 4; i++) {
452
0
        if (!SWS_OP_NEEDED(op, i) || op->dither.y_offset[i] < 0)
453
0
            continue;
454
0
        const uint8_t off = op->dither.y_offset[i] & (size - 1);
455
0
        uop.mask |= SWS_COMP(i);
456
0
        uop.par.dither.y_offset[i] = off;
457
0
    }
458
459
    /* Allocate extra rows to allow over-reading for row offsets. Note that
460
     * y_offset is currently never larger than 5, so the extra space needed
461
     * for this over-allocation is bounded by 5 * size * sizeof(float),
462
     * typically 320 bytes for a 16x16 dither matrix. */
463
0
    const int stride   = size * sizeof(SwsPixel);
464
0
    const int num_rows = ff_sws_dither_height(&uop.par.dither);
465
0
    SwsPixel *matrix = uop.data.ptr = av_refstruct_allocz(num_rows * stride);
466
0
    if (!matrix)
467
0
        return AVERROR(ENOMEM);
468
469
0
    for (int i = 0; i < size * size; i++)
470
0
        matrix[i] = ff_sws_pixel_from_q64(uop.type, op->dither.matrix[i]);
471
0
    memcpy(&matrix[size * size], matrix, (num_rows - size) * stride);
472
473
0
    return ff_sws_uop_list_append(ops, &uop);
474
0
}
475
476
static int translate_linear_op(SwsContext *ctx, SwsUOpList *ops,
477
                               SwsUOpFlags flags, const SwsOp *op,
478
                               const SwsOpPad inputs[4])
479
0
{
480
0
    SwsUOp uop = {
481
0
        .type = ff_sws_op_input_type(op),
482
0
        .uop  = SWS_UOP_LINEAR,
483
0
    };
484
485
0
    const SwsPixel one = ff_sws_pixel_from_q64(uop.type, (AVRational64) {1, 1});
486
0
    const uint32_t mask = ff_sws_linear_mask(&op->lin);
487
0
    const bool bitexact = ctx->flags & SWS_BITEXACT;
488
0
    uint32_t exact = 0;
489
490
0
    for (int i = 0; i < 4; i++) {
491
0
        if (!SWS_OP_NEEDED(op, i) || !(mask & SWS_MASK_ROW(i))) {
492
0
            uop.par.lin.zero |= SWS_MASK_ROW(i) ^ SWS_MASK(i, i);
493
0
            uop.par.lin.one |= SWS_MASK(i, i);
494
0
            uop.data.mat4x5[i][i] = one;
495
0
            continue;
496
0
        }
497
0
        uop.mask |= SWS_COMP(i);
498
0
        bool nonzero = (op->lin.m[i][4].num != 0);
499
0
        for (int j = 0; j < 5; j++) {
500
0
            const AVRational64 k = op->lin.m[i][j];
501
0
            const SwsPixel px = ff_sws_pixel_from_q64(uop.type, k);
502
0
            uop.data.mat4x5[i][j] = px;
503
0
            if (k.num == 0)
504
0
                uop.par.lin.zero |= SWS_MASK(i, j);
505
0
            else if (j < 4 && k.num == k.den)
506
0
                uop.par.lin.one |= SWS_MASK(i, j);
507
0
            else if (j < 4 && nonzero && (!bitexact || exact_prod(px, &inputs[j])))
508
0
                exact |= SWS_MASK(i, j);
509
0
            if (k.num != 0)
510
0
                nonzero = true;
511
0
        }
512
0
    }
513
514
0
    if ((flags & SWS_UOP_FLAG_FMA) && !ff_sws_pixel_type_is_int(uop.type)) {
515
        /* multiplication by 1 and 0 are always exact by definition */
516
0
        uop.uop = SWS_UOP_LINEAR_FMA;
517
0
        uop.par.lin.exact = exact | uop.par.lin.zero | uop.par.lin.one;
518
0
    }
519
520
0
    return ff_sws_uop_list_append(ops, &uop);
521
0
}
522
523
static int translate_op(SwsContext *ctx, SwsUOpList *uops, SwsUOpFlags flags,
524
                        const SwsOp *op, const SwsOpPad inputs[4])
525
0
{
526
0
    switch (op->op) {
527
0
    case SWS_OP_FILTER_H:
528
0
    case SWS_OP_FILTER_V:
529
0
        return AVERROR(ENOTSUP); /* always handled by subpass splitting */
530
0
    case SWS_OP_READ:
531
0
    case SWS_OP_WRITE:
532
0
        return translate_rw_op(ctx, uops, flags, op);
533
0
    case SWS_OP_SWIZZLE:
534
0
        return translate_swizzle(uops, flags, op);
535
0
    case SWS_OP_DITHER:
536
0
        return translate_dither_op(uops, flags, op);
537
0
    case SWS_OP_LINEAR:
538
0
        return translate_linear_op(ctx, uops, flags, op, inputs);
539
0
    default:
540
0
        break;
541
0
    }
542
543
    /* Default handling for "simple" ops */
544
0
    SwsUOp uop = {
545
0
        .uop  = SWS_UOP_INVALID,
546
0
        .type = ff_sws_op_input_type(op),
547
0
        .mask = ff_sws_comp_mask_needed(op),
548
0
    };
549
550
0
    switch (op->op) {
551
0
    case SWS_OP_CONVERT:
552
0
        switch (op->convert.to) {
553
0
        case SWS_PIXEL_U8:  uop.uop = SWS_UOP_TO_U8;  break;
554
0
        case SWS_PIXEL_U16: uop.uop = SWS_UOP_TO_U16; break;
555
0
        case SWS_PIXEL_U32: uop.uop = SWS_UOP_TO_U32; break;
556
0
        case SWS_PIXEL_F32: uop.uop = SWS_UOP_TO_F32; break;
557
0
        }
558
0
        break;
559
0
    case SWS_OP_UNPACK:
560
0
    case SWS_OP_PACK:
561
0
        uop.uop = op->op == SWS_OP_PACK ? SWS_UOP_PACK : SWS_UOP_UNPACK;
562
0
        uop.mask = 0;
563
0
        for (int i = 0; i < 4 && op->pack.pattern[i]; i++) {
564
0
            uop.par.pack.pattern[i] = op->pack.pattern[i];
565
0
            if (op->op == SWS_OP_PACK || SWS_OP_NEEDED(op, i))
566
0
                uop.mask |= SWS_COMP(i);
567
0
        }
568
0
        break;
569
0
    case SWS_OP_LSHIFT:
570
0
    case SWS_OP_RSHIFT:
571
0
        uop.uop = op->op == SWS_OP_LSHIFT ? SWS_UOP_LSHIFT : SWS_UOP_RSHIFT;
572
0
        uop.par.shift.amount = op->shift.amount;
573
0
        break;
574
0
    case SWS_OP_CLEAR:
575
0
        uop.uop = SWS_UOP_CLEAR;
576
0
        uop.mask &= op->clear.mask;
577
0
        for (int i = 0; i < 4; i++) {
578
0
            if (!SWS_COMP_TEST(op->clear.mask, i))
579
0
                continue;
580
0
            const AVRational64 v = op->clear.value[i];
581
0
            const SwsPixelType type = op->clear.type[i];
582
0
            const SwsPixel px = ff_sws_pixel_from_q64(type, v);
583
0
            uop.type = pixel_type_to_int(flags, type);
584
0
            uop.data.vec4[i] = px;
585
0
            if (v.num == 0)
586
0
                uop.par.clear.zero |= SWS_COMP(i);
587
0
            else if (ff_sws_pixel_is_1s(type, px))
588
0
                uop.par.clear.one |= SWS_COMP(i);
589
0
        }
590
0
        break;
591
0
    case SWS_OP_SCALE:
592
0
        uop.uop = SWS_UOP_SCALE;
593
0
        uop.data.scalar = ff_sws_pixel_from_q64(uop.type, op->scale.factor);
594
0
        break;
595
0
    case SWS_OP_MIN:
596
0
    case SWS_OP_MAX:
597
0
        uop.uop = op->op == SWS_OP_MIN ? SWS_UOP_MIN : SWS_UOP_MAX;
598
0
        uop.mask &= ff_sws_comp_mask_q4(op->clamp.limit);
599
0
        for (int i = 0; i < 4; i++) {
600
0
            if (SWS_COMP_TEST(uop.mask, i))
601
0
                uop.data.vec4[i] = ff_sws_pixel_from_q64(uop.type, op->clamp.limit[i]);
602
0
        }
603
0
        break;
604
0
    case SWS_OP_SWAP_BYTES:
605
0
        uop.uop = SWS_UOP_SWAP_BYTES;
606
0
        uop.type = pixel_type_to_int(flags, uop.type);
607
0
        break;
608
0
    case SWS_OP_LUT_3D:
609
0
        uop.uop = SWS_UOP_LUT_3D;
610
0
        uop.par.lut3d.dynamic = op->lut3d.dynamic;
611
0
        uop.data.lut3d = av_refstruct_ref_c(op->lut3d.lut);
612
0
        break;
613
0
    default:
614
0
        return AVERROR(ENOTSUP);
615
0
    }
616
617
0
    av_assert0(uop.uop != SWS_UOP_INVALID);
618
0
    return ff_sws_uop_list_append(uops, &uop);
619
0
}
620
621
int ff_sws_ops_translate(SwsContext *ctx, const SwsOpList *ops,
622
                         SwsUOpFlags flags, SwsUOpList *uops)
623
0
{
624
0
    const SwsOpPad *inputs = ops->inputs;
625
0
    for (int n = 0; n < ops->num_ops; n++) {
626
0
        const SwsOp *op = &ops->ops[n];
627
0
        for (int i = 0; i < 4; i++) {
628
0
            if (!SWS_OP_NEEDED(op, i))
629
0
                continue;
630
0
            const int pixel_size = ff_sws_pixel_type_size(op->outputs[i].type);
631
0
            if (pixel_size > uops->pixel_size_max)
632
0
                uops->pixel_size_max = pixel_size;
633
0
        }
634
635
0
        int ret = translate_op(ctx, uops, flags, op, inputs);
636
0
        if (ret < 0)
637
0
            return ret;
638
0
        inputs = op->outputs;
639
0
    }
640
641
0
    return ff_sws_uop_list_optimize(ctx, flags, uops);
642
0
}