Coverage Report

Created: 2026-08-17 07:50

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