Coverage Report

Created: 2026-07-15 07:31

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