Coverage Report

Created: 2026-10-28 07:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ffmpeg/libswscale/uops_tmpl.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 <libavutil/bswap.h>
22
23
#include "uops_tmpl.h"
24
25
#ifndef BIT_DEPTH
26
#  define BIT_DEPTH 8
27
#endif
28
29
#if IS_FLOAT && BIT_DEPTH == 32
30
#  define PIXEL_TYPE SWS_PIXEL_F32
31
0
#  define pixel_t    float
32
0
#  define inter_t    float
33
#  define uinter_t   float
34
0
#  define vec3_t     v3f32_t
35
#  define PX         F32
36
0
#  define px         f32
37
#elif BIT_DEPTH == 32
38
0
#  define PIXEL_MAX  0xFFFFFFFFu
39
0
#  define PIXEL_SWAP av_bswap32
40
0
#  define pixel_t    uint32_t
41
#  define inter_t    int64_t
42
#  define uinter_t   uint64_t
43
#  define PX         U32
44
0
#  define px         u32
45
#elif BIT_DEPTH == 16
46
0
#  define PIXEL_MAX  0xFFFFu
47
0
#  define PIXEL_SWAP av_bswap16
48
0
#  define pixel_t    uint16_t
49
0
#  define inter_t    int64_t
50
#  define uinter_t   uint64_t
51
#  define PX         U16
52
0
#  define px         u16
53
#elif BIT_DEPTH == 8
54
0
#  define PIXEL_MAX  0xFFu
55
0
#  define pixel_t    uint8_t
56
0
#  define inter_t    int32_t
57
#  define uinter_t   uint32_t
58
#  define PX         U8
59
0
#  define px         u8
60
#else
61
#  error Invalid BIT_DEPTH
62
#endif
63
64
/*********************************
65
 * Generic read/write operations *
66
 *********************************/
67
68
DECL_READ(read_planar, const SwsCompMask mask)
69
0
{
70
0
    SWS_LOOP
71
0
    for (int i = 0; i < SWS_BLOCK_SIZE; i++) {
72
0
        if (X) x[i] = in0[i];
73
0
        if (Y) y[i] = in1[i];
74
0
        if (Z) z[i] = in2[i];
75
0
        if (W) w[i] = in3[i];
76
0
    }
77
78
0
    if (X) iter->in[0] += SIZEOF_BLOCK;
79
0
    if (Y) iter->in[1] += SIZEOF_BLOCK;
80
0
    if (Z) iter->in[2] += SIZEOF_BLOCK;
81
0
    if (W) iter->in[3] += SIZEOF_BLOCK;
82
83
0
    CONTINUE(x, y, z, w);
84
0
}
Unexecuted instantiation: uops_backend.c:read_planar_U8
Unexecuted instantiation: uops_backend.c:read_planar_U16
Unexecuted instantiation: uops_backend.c:read_planar_U32
Unexecuted instantiation: uops_backend.c:read_planar_F32
85
86
DECL_READ(read_packed, const SwsCompMask mask)
87
0
{
88
0
    const int elems = W ? 4 : Z ? 3 : Y ? 2 : 1;
89
90
0
    SWS_LOOP
91
0
    for (int i = 0; i < SWS_BLOCK_SIZE; i++) {
92
0
        if (X) x[i] = in0[elems * i + 0];
93
0
        if (Y) y[i] = in0[elems * i + 1];
94
0
        if (Z) z[i] = in0[elems * i + 2];
95
0
        if (W) w[i] = in0[elems * i + 3];
96
0
    }
97
98
0
    iter->in[0] += SIZEOF_BLOCK * elems;
99
0
    CONTINUE(x, y, z, w);
100
0
}
Unexecuted instantiation: uops_backend.c:read_packed_U8
Unexecuted instantiation: uops_backend.c:read_packed_U16
Unexecuted instantiation: uops_backend.c:read_packed_U32
Unexecuted instantiation: uops_backend.c:read_packed_F32
101
102
DECL_WRITE(write_planar, const SwsCompMask mask)
103
0
{
104
0
    SWS_LOOP
105
0
    for (int i = 0; i < SWS_BLOCK_SIZE; i++) {
106
0
        if (X) out0[i] = x[i];
107
0
        if (Y) out1[i] = y[i];
108
0
        if (Z) out2[i] = z[i];
109
0
        if (W) out3[i] = w[i];
110
0
    }
111
112
0
    if (X) iter->out[0] += SIZEOF_BLOCK;
113
0
    if (Y) iter->out[1] += SIZEOF_BLOCK;
114
0
    if (Z) iter->out[2] += SIZEOF_BLOCK;
115
0
    if (W) iter->out[3] += SIZEOF_BLOCK;
116
0
}
Unexecuted instantiation: uops_backend.c:write_planar_U8
Unexecuted instantiation: uops_backend.c:write_planar_U16
Unexecuted instantiation: uops_backend.c:write_planar_U32
Unexecuted instantiation: uops_backend.c:write_planar_F32
117
118
DECL_WRITE(write_packed, const SwsCompMask mask)
119
0
{
120
0
    const int elems = W ? 4 : Z ? 3 : Y ? 2 : 1;
121
122
0
    SWS_LOOP
123
0
    for (int i = 0; i < SWS_BLOCK_SIZE; i++) {
124
0
        if (X) out0[elems * i + 0] = x[i];
125
0
        if (Y) out0[elems * i + 1] = y[i];
126
0
        if (Z) out0[elems * i + 2] = z[i];
127
0
        if (W) out0[elems * i + 3] = w[i];
128
0
    }
129
130
0
    iter->out[0] += SIZEOF_BLOCK * elems;
131
0
}
Unexecuted instantiation: uops_backend.c:write_packed_U8
Unexecuted instantiation: uops_backend.c:write_packed_U16
Unexecuted instantiation: uops_backend.c:write_packed_U32
Unexecuted instantiation: uops_backend.c:write_packed_F32
132
133
#if BIT_DEPTH == 8
134
135
DECL_READ(read_bit, const SwsCompMask mask)
136
0
{
137
0
    av_assert2(mask == SWS_COMP_ELEMS(1));
138
139
0
    SWS_LOOP
140
0
    for (int i = 0; i < SWS_BLOCK_SIZE; i += 8) {
141
0
        const pixel_t val = ((const pixel_t *) in0)[i >> 3];
142
0
        x[i + 0] = (val >> 7) & 1;
143
0
        x[i + 1] = (val >> 6) & 1;
144
0
        x[i + 2] = (val >> 5) & 1;
145
0
        x[i + 3] = (val >> 4) & 1;
146
0
        x[i + 4] = (val >> 3) & 1;
147
0
        x[i + 5] = (val >> 2) & 1;
148
0
        x[i + 6] = (val >> 1) & 1;
149
0
        x[i + 7] = (val >> 0) & 1;
150
0
    }
151
152
0
    iter->in[0] += SIZEOF_BLOCK >> 3;
153
0
    CONTINUE(x, y, z, w);
154
0
}
155
156
DECL_READ(read_nibble, const SwsCompMask mask)
157
0
{
158
0
    av_assert2(mask == SWS_COMP_ELEMS(1));
159
160
0
    SWS_LOOP
161
0
    for (int i = 0; i < SWS_BLOCK_SIZE; i += 2) {
162
0
        const pixel_t val = in0[i >> 1];
163
0
        x[i + 0] = val >> 4;  /* high nibble */
164
0
        x[i + 1] = val & 0xF; /* low nibble */
165
0
    }
166
167
0
    iter->in[0] += SIZEOF_BLOCK >> 1;
168
0
    CONTINUE(x, y, z, w);
169
0
}
170
171
DECL_READ(read_palette, const SwsCompMask mask)
172
0
{
173
0
    av_assert2(mask == SWS_COMP_ELEMS(4));
174
175
0
    SWS_LOOP
176
0
    for (int i = 0; i < SWS_BLOCK_SIZE; i++) {
177
0
        const pixel_t index = in0[i];
178
0
        const pixel_t *value = &in1[index * 4];
179
0
        x[i] = value[0];
180
0
        y[i] = value[1];
181
0
        z[i] = value[2];
182
0
        w[i] = value[3];
183
0
    }
184
185
0
    iter->in[0] += SIZEOF_BLOCK;
186
0
    CONTINUE(x, y, z, w);
187
0
}
188
189
DECL_WRITE(write_bit, const SwsCompMask mask)
190
0
{
191
0
    av_assert2(mask == SWS_COMP_ELEMS(1));
192
193
0
    SWS_LOOP
194
0
    for (int i = 0; i < SWS_BLOCK_SIZE; i += 8) {
195
0
        out0[i >> 3] = x[i + 0] << 7 |
196
0
                       x[i + 1] << 6 |
197
0
                       x[i + 2] << 5 |
198
0
                       x[i + 3] << 4 |
199
0
                       x[i + 4] << 3 |
200
0
                       x[i + 5] << 2 |
201
0
                       x[i + 6] << 1 |
202
0
                       x[i + 7];
203
0
    }
204
205
0
    iter->out[0] += SIZEOF_BLOCK >> 3;
206
0
}
207
208
DECL_WRITE(write_nibble, const SwsCompMask mask)
209
0
{
210
0
    av_assert2(mask == SWS_COMP_ELEMS(1));
211
212
0
    SWS_LOOP
213
0
    for (int i = 0; i < SWS_BLOCK_SIZE; i += 2)
214
0
        out0[i >> 1] = x[i] << 4 | x[i + 1];
215
216
0
    iter->out[0] += SIZEOF_BLOCK >> 1;
217
0
}
218
219
#endif /* BIT_DEPTH == 8 */
220
221
SWS_FOR(PX, READ_PLANAR,    DECL_IMPL_READ,     read_planar)
222
SWS_FOR(PX, READ_PACKED,    DECL_IMPL_READ,     read_packed)
223
SWS_FOR(PX, READ_NIBBLE,    DECL_IMPL_READ,     read_nibble)
224
SWS_FOR(PX, READ_BIT,       DECL_IMPL_READ,     read_bit)
225
SWS_FOR(PX, READ_PALETTE,   DECL_IMPL_READ,     read_palette)
226
SWS_FOR(PX, WRITE_PLANAR,   DECL_IMPL_WRITE,    write_planar)
227
SWS_FOR(PX, WRITE_PACKED,   DECL_IMPL_WRITE,    write_packed)
228
SWS_FOR(PX, WRITE_NIBBLE,   DECL_IMPL_WRITE,    write_nibble)
229
SWS_FOR(PX, WRITE_BIT,      DECL_IMPL_WRITE,    write_bit)
230
231
SWS_FOR_STRUCT(PX, READ_PLANAR,     DECL_ENTRY)
232
SWS_FOR_STRUCT(PX, READ_PACKED,     DECL_ENTRY)
233
SWS_FOR_STRUCT(PX, READ_NIBBLE,     DECL_ENTRY)
234
SWS_FOR_STRUCT(PX, READ_BIT,        DECL_ENTRY)
235
SWS_FOR_STRUCT(PX, READ_PALETTE,    DECL_ENTRY)
236
SWS_FOR_STRUCT(PX, WRITE_PLANAR,    DECL_ENTRY)
237
SWS_FOR_STRUCT(PX, WRITE_PACKED,    DECL_ENTRY)
238
SWS_FOR_STRUCT(PX, WRITE_NIBBLE,    DECL_ENTRY)
239
SWS_FOR_STRUCT(PX, WRITE_BIT,       DECL_ENTRY)
240
241
/*****************************
242
 * Scaling / filtering reads *
243
 *****************************/
244
245
DECL_SETUP(setup_filter_v, params, out)
246
0
{
247
0
    if (params->uop->par.filter.type != SWS_PIXEL_F32)
248
0
        return AVERROR(ENOTSUP);
249
250
0
    const SwsFilterWeights *filter = params->uop->data.kernel;
251
0
    static_assert(sizeof(out->priv.ptr) <= sizeof(int32_t[2]),
252
0
                  ">8 byte pointers not supported");
253
254
    /* Pre-convert weights to float */
255
0
    float *weights = av_calloc(filter->num_weights, sizeof(float));
256
0
    if (!weights)
257
0
        return AVERROR(ENOMEM);
258
259
0
    for (int i = 0; i < filter->num_weights; i++)
260
0
        weights[i] = (float) filter->weights[i] / SWS_FILTER_SCALE;
261
262
0
    out->priv.ptr = weights;
263
0
    out->priv.i32[2] = filter->filter_size;
264
0
    out->free = ff_op_priv_free;
265
0
    return 0;
266
0
}
Unexecuted instantiation: uops_backend.c:setup_filter_v_U8
Unexecuted instantiation: uops_backend.c:setup_filter_v_U16
Unexecuted instantiation: uops_backend.c:setup_filter_v_F32
Unexecuted instantiation: uops_backend.c:setup_filter_v_U32
267
268
/* Fully general vertical planar filter case */
269
DECL_READ(read_planar_fv, const SwsCompMask mask, const SwsPixelType type)
270
0
{
271
0
    av_assert2(type == SWS_PIXEL_F32);
272
0
    const SwsOpExec *exec = iter->exec;
273
0
    const float *restrict weights = impl->priv.ptr;
274
0
    const int filter_size = impl->priv.i32[2];
275
0
    weights += filter_size * iter->y;
276
277
0
    block_t xs, ys, zs, ws;
278
0
    if (X) memset(&xs.f32, 0, sizeof(xs.f32));
279
0
    if (Y) memset(&ys.f32, 0, sizeof(ys.f32));
280
0
    if (Z) memset(&zs.f32, 0, sizeof(zs.f32));
281
0
    if (W) memset(&ws.f32, 0, sizeof(ws.f32));
282
283
0
    for (int j = 0; j < filter_size; j++) {
284
0
        const float weight = weights[j];
285
286
0
        SWS_LOOP
287
0
        for (int i = 0; i < SWS_BLOCK_SIZE; i++) {
288
0
            if (X) xs.f32[i] += weight * in0[i];
289
0
            if (Y) ys.f32[i] += weight * in1[i];
290
0
            if (Z) zs.f32[i] += weight * in2[i];
291
0
            if (W) ws.f32[i] += weight * in3[i];
292
0
        }
293
294
0
        if (X) in0 = bump_ptr(in0, exec->in_stride[0]);
295
0
        if (Y) in1 = bump_ptr(in1, exec->in_stride[1]);
296
0
        if (Z) in2 = bump_ptr(in2, exec->in_stride[2]);
297
0
        if (W) in3 = bump_ptr(in3, exec->in_stride[3]);
298
0
    }
299
300
0
    if (X) iter->in[0] += SIZEOF_BLOCK;
301
0
    if (Y) iter->in[1] += SIZEOF_BLOCK;
302
0
    if (Z) iter->in[2] += SIZEOF_BLOCK;
303
0
    if (W) iter->in[3] += SIZEOF_BLOCK;
304
305
0
    CONTINUE(&xs, &ys, &zs, &ws);
306
0
}
Unexecuted instantiation: uops_backend.c:read_planar_fv_U8
Unexecuted instantiation: uops_backend.c:read_planar_fv_U16
Unexecuted instantiation: uops_backend.c:read_planar_fv_F32
Unexecuted instantiation: uops_backend.c:read_planar_fv_U32
307
308
DECL_SETUP(setup_filter_h, params, out)
309
0
{
310
0
    if (params->uop->par.filter.type != SWS_PIXEL_F32)
311
0
        return AVERROR(ENOTSUP);
312
313
0
    SwsFilterWeights *filter = params->uop->data.kernel;
314
0
    out->priv.i32[2] = filter->filter_size;
315
316
    /* The horizontal filter uop reads weights for a full SWS_BLOCK_SIZE
317
     * outputs per block (see read_planar_fh), so the weights array must be
318
     * padded up to the block-aligned output count. Pad the tail with zero
319
     * weights, which contribute nothing to the accumulated sums. */
320
0
    const size_t padded_w = FFALIGN(filter->dst_size, SWS_BLOCK_SIZE);
321
0
    if (padded_w == filter->dst_size) {
322
0
        out->priv.ptr = av_refstruct_ref(filter->weights);
323
0
        out->free = ff_op_priv_unref;
324
0
        return 0;
325
0
    }
326
327
0
    int *weights = av_calloc(padded_w * filter->filter_size, sizeof(*weights));
328
0
    if (!weights)
329
0
        return AVERROR(ENOMEM);
330
0
    memcpy(weights, filter->weights, filter->num_weights * sizeof(*weights));
331
0
    out->priv.ptr = weights;
332
0
    out->free = ff_op_priv_free;
333
0
    return 0;
334
0
}
Unexecuted instantiation: uops_backend.c:setup_filter_h_U8
Unexecuted instantiation: uops_backend.c:setup_filter_h_U16
Unexecuted instantiation: uops_backend.c:setup_filter_h_F32
Unexecuted instantiation: uops_backend.c:setup_filter_h_U32
335
336
/* Fully general horizontal planar filter case */
337
DECL_READ(read_planar_fh, const SwsCompMask mask, const SwsPixelType type)
338
0
{
339
0
    av_assert2(type == SWS_PIXEL_F32);
340
0
    const SwsOpExec *exec = iter->exec;
341
0
    const int *restrict weights = impl->priv.ptr;
342
0
    const int filter_size = impl->priv.i32[2];
343
0
    const float scale = 1.0f / SWS_FILTER_SCALE;
344
0
    const int xpos = iter->x;
345
0
    weights += filter_size * iter->x;
346
347
0
    block_t xs, ys, zs, ws;
348
0
    for (int i = 0; i < SWS_BLOCK_SIZE; i++) {
349
0
        const int offset = exec->in_offset_x[xpos + i];
350
0
        pixel_t *start0 = bump_ptr(in0, offset);
351
0
        pixel_t *start1 = bump_ptr(in1, offset);
352
0
        pixel_t *start2 = bump_ptr(in2, offset);
353
0
        pixel_t *start3 = bump_ptr(in3, offset);
354
355
0
        inter_t sx = 0, sy = 0, sz = 0, sw = 0;
356
0
        for (int j = 0; j < filter_size; j++) {
357
0
            const int weight = weights[j];
358
0
            if (X) sx += weight * start0[j];
359
0
            if (Y) sy += weight * start1[j];
360
0
            if (Z) sz += weight * start2[j];
361
0
            if (W) sw += weight * start3[j];
362
0
        }
363
364
0
        if (X) xs.f32[i] = (float) sx * scale;
365
0
        if (Y) ys.f32[i] = (float) sy * scale;
366
0
        if (Z) zs.f32[i] = (float) sz * scale;
367
0
        if (W) ws.f32[i] = (float) sw * scale;
368
369
0
        weights += filter_size;
370
0
    }
371
372
0
    CONTINUE(&xs, &ys, &zs, &ws);
373
0
}
Unexecuted instantiation: uops_backend.c:read_planar_fh_U8
Unexecuted instantiation: uops_backend.c:read_planar_fh_U16
Unexecuted instantiation: uops_backend.c:read_planar_fh_F32
Unexecuted instantiation: uops_backend.c:read_planar_fh_U32
374
375
SWS_FOR(PX, READ_PLANAR_FV, DECL_IMPL_READ, read_planar_fv)
376
SWS_FOR(PX, READ_PLANAR_FH, DECL_IMPL_READ, read_planar_fh)
377
SWS_FOR_STRUCT(PX, READ_PLANAR_FV, DECL_ENTRY, .setup = fn(setup_filter_v) )
378
SWS_FOR_STRUCT(PX, READ_PLANAR_FH, DECL_ENTRY, .setup = fn(setup_filter_h) )
379
380
/***************************
381
 * Permutation and copying *
382
 ***************************/
383
384
DECL_FUNC(permute, const SwsCompMask mask, int num_moves,
385
          int8_t d0, int8_t d1, int8_t d2, int8_t d3, int8_t d4, int8_t d5,
386
          int8_t s0, int8_t s1, int8_t s2, int8_t s3, int8_t s4, int8_t s5)
387
0
{
388
0
    const int8_t dst[SWS_UOP_MOVE_MAX] = { d0, d1, d2, d3, d4, d5 };
389
0
    const int8_t src[SWS_UOP_MOVE_MAX] = { s0, s1, s2, s3, s4, s5 };
390
391
0
    pixel_t *ptr[5] = { NULL, x, y, z, w };
392
0
    for (int n = 0; n < num_moves; n++)
393
0
        ptr[dst[n] + 1] = ptr[src[n] + 1];
394
395
    /* The unneeded registers may still alias the used ones, so point them
396
     * back at the stack to avoid collisions */
397
0
    block_t xx, yy, zz, ww;
398
0
    CONTINUE(X ? ptr[1] : xx.px,
399
0
             Y ? ptr[2] : yy.px,
400
0
             Z ? ptr[3] : zz.px,
401
0
             W ? ptr[4] : ww.px);
402
0
}
Unexecuted instantiation: uops_backend.c:permute_U8
Unexecuted instantiation: uops_backend.c:permute_U16
Unexecuted instantiation: uops_backend.c:permute_U32
Unexecuted instantiation: uops_backend.c:permute_F32
403
404
DECL_FUNC(copy, const SwsCompMask mask, int num_moves,
405
          int8_t d0, int8_t d1, int8_t d2, int8_t d3, int8_t d4, int8_t d5,
406
          int8_t s0, int8_t s1, int8_t s2, int8_t s3, int8_t s4, int8_t s5)
407
0
{
408
0
    const size_t block_size = SWS_BLOCK_SIZE * sizeof(pixel_t);
409
0
    const int8_t dst[SWS_UOP_MOVE_MAX] = { d0, d1, d2, d3, d4, d5 };
410
0
    const int8_t src[SWS_UOP_MOVE_MAX] = { s0, s1, s2, s3, s4, s5 };
411
412
0
    block_t data[5];
413
0
    memcpy(&data[1].px, x, block_size);
414
0
    memcpy(&data[2].px, y, block_size);
415
0
    memcpy(&data[3].px, z, block_size);
416
0
    memcpy(&data[4].px, w, block_size);
417
418
0
    for (int n = 0; n < num_moves; n++)
419
0
        data[dst[n] + 1] = data[src[n] + 1];
420
421
0
    memcpy(x, &data[1].px, block_size);
422
0
    memcpy(y, &data[2].px, block_size);
423
0
    memcpy(z, &data[3].px, block_size);
424
0
    memcpy(w, &data[4].px, block_size);
425
426
0
    CONTINUE(x, y, z, w);
427
0
}
Unexecuted instantiation: uops_backend.c:copy_U8
Unexecuted instantiation: uops_backend.c:copy_U16
Unexecuted instantiation: uops_backend.c:copy_U32
Unexecuted instantiation: uops_backend.c:copy_F32
428
429
SWS_FOR(PX, PERMUTE, DECL_IMPL, permute)
430
SWS_FOR(PX, COPY,    DECL_IMPL, copy)
431
SWS_FOR_STRUCT(PX, PERMUTE, DECL_ENTRY)
432
SWS_FOR_STRUCT(PX, COPY,    DECL_ENTRY)
433
434
/*********************
435
 * Format conversion *
436
 *********************/
437
438
#define DECL_CAST(DST, dst)                                                     \
439
    DECL_FUNC(to_##dst, const SwsCompMask mask)                                 \
440
0
    {                                                                           \
441
0
        block_t xx, yy, zz, ww;                                                 \
442
0
                                                                                \
443
0
        SWS_LOOP                                                                \
444
0
        for (int i = 0; i < SWS_BLOCK_SIZE; i++) {                              \
445
0
            if (X) xx.dst[i] = x[i];                                            \
446
0
            if (Y) yy.dst[i] = y[i];                                            \
447
0
            if (Z) zz.dst[i] = z[i];                                            \
448
0
            if (W) ww.dst[i] = w[i];                                            \
449
0
        }                                                                       \
450
0
                                                                                \
451
0
        CONTINUE(&xx, &yy, &zz, &ww);                                           \
452
0
    }                                                                           \
Unexecuted instantiation: uops_backend.c:to_u16_U8
Unexecuted instantiation: uops_backend.c:to_u32_U8
Unexecuted instantiation: uops_backend.c:to_f32_U8
Unexecuted instantiation: uops_backend.c:to_u8_U16
Unexecuted instantiation: uops_backend.c:to_u32_U16
Unexecuted instantiation: uops_backend.c:to_f32_U16
Unexecuted instantiation: uops_backend.c:to_u16_U32
Unexecuted instantiation: uops_backend.c:to_f32_U32
Unexecuted instantiation: uops_backend.c:to_u8_F32
Unexecuted instantiation: uops_backend.c:to_u16_F32
Unexecuted instantiation: uops_backend.c:to_u32_F32
Unexecuted instantiation: uops_backend.c:to_u8_U8
Unexecuted instantiation: uops_backend.c:to_u16_U16
Unexecuted instantiation: uops_backend.c:to_u8_U32
Unexecuted instantiation: uops_backend.c:to_u32_U32
Unexecuted instantiation: uops_backend.c:to_f32_F32
453
                                                                                \
454
    SWS_FOR(PX, TO_##DST, DECL_IMPL, to_##dst)                                  \
455
    SWS_FOR_STRUCT(PX, TO_##DST, DECL_ENTRY)
456
457
DECL_CAST(U8,  u8)
458
DECL_CAST(U16, u16)
459
DECL_CAST(U32, u32)
460
DECL_CAST(F32, f32)
461
462
/********************
463
 * Bit manipulation *
464
 ********************/
465
466
#if !IS_FLOAT
467
DECL_FUNC(lshift, const SwsCompMask mask, const uint8_t amount)
468
0
{
469
0
    SWS_LOOP
470
0
    for (int i = 0; i < SWS_BLOCK_SIZE; i++) {
471
0
        if (X) x[i] <<= amount;
472
0
        if (Y) y[i] <<= amount;
473
0
        if (Z) z[i] <<= amount;
474
0
        if (W) w[i] <<= amount;
475
0
    }
476
477
0
    CONTINUE(x, y, z, w);
478
0
}
Unexecuted instantiation: uops_backend.c:lshift_U16
Unexecuted instantiation: uops_backend.c:lshift_U32
Unexecuted instantiation: uops_backend.c:lshift_U8
479
480
DECL_FUNC(rshift, const SwsCompMask mask, const uint8_t amount)
481
0
{
482
0
    SWS_LOOP
483
0
    for (int i = 0; i < SWS_BLOCK_SIZE; i++) {
484
0
        if (X) x[i] >>= amount;
485
0
        if (Y) y[i] >>= amount;
486
0
        if (Z) z[i] >>= amount;
487
0
        if (W) w[i] >>= amount;
488
0
    }
489
490
0
    CONTINUE(x, y, z, w);
491
0
}
Unexecuted instantiation: uops_backend.c:rshift_U16
Unexecuted instantiation: uops_backend.c:rshift_U8
Unexecuted instantiation: uops_backend.c:rshift_U32
492
#endif
493
494
SWS_FOR(PX, LSHIFT, DECL_IMPL, lshift)
495
SWS_FOR(PX, RSHIFT, DECL_IMPL, rshift)
496
497
SWS_FOR_STRUCT(PX, LSHIFT, DECL_ENTRY)
498
SWS_FOR_STRUCT(PX, RSHIFT, DECL_ENTRY)
499
500
#ifdef PIXEL_SWAP
501
DECL_FUNC(swap_bytes, const SwsCompMask mask)
502
0
{
503
0
    SWS_LOOP
504
0
    for (int i = 0; i < SWS_BLOCK_SIZE; i++) {
505
0
        if (X) x[i] = PIXEL_SWAP(x[i]);
506
0
        if (Y) y[i] = PIXEL_SWAP(y[i]);
507
0
        if (Z) z[i] = PIXEL_SWAP(z[i]);
508
0
        if (W) w[i] = PIXEL_SWAP(w[i]);
509
0
    }
510
511
0
    CONTINUE(x, y, z, w);
512
0
}
Unexecuted instantiation: uops_backend.c:swap_bytes_U16
Unexecuted instantiation: uops_backend.c:swap_bytes_U32
513
#endif /* PIXEL_SWAP */
514
515
SWS_FOR(PX, SWAP_BYTES, DECL_IMPL, swap_bytes)
516
SWS_FOR_STRUCT(PX, SWAP_BYTES, DECL_ENTRY)
517
518
#ifdef PIXEL_MAX
519
DECL_FUNC(expand_bit, const SwsCompMask mask)
520
0
{
521
0
    SWS_LOOP
522
0
    for (int i = 0; i < SWS_BLOCK_SIZE; i++) {
523
0
        if (X) x[i] = x[i] ? PIXEL_MAX : 0;
524
0
        if (Y) y[i] = y[i] ? PIXEL_MAX : 0;
525
0
        if (Z) z[i] = z[i] ? PIXEL_MAX : 0;
526
0
        if (W) w[i] = w[i] ? PIXEL_MAX : 0;
527
0
    }
528
529
0
    CONTINUE(x, y, z, w);
530
0
}
Unexecuted instantiation: uops_backend.c:expand_bit_U8
Unexecuted instantiation: uops_backend.c:expand_bit_U16
Unexecuted instantiation: uops_backend.c:expand_bit_U32
531
#endif
532
533
#if BIT_DEPTH == 8
534
DECL_FUNC(expand_pair, const SwsCompMask mask)
535
0
{
536
0
    block_t x16, y16, z16, w16;
537
538
0
    SWS_LOOP
539
0
    for (int i = 0; i < SWS_BLOCK_SIZE; i++) {
540
0
        if (X) x16.u16[i] = x[i] << 8 | x[i];
541
0
        if (Y) y16.u16[i] = y[i] << 8 | y[i];
542
0
        if (Z) z16.u16[i] = z[i] << 8 | z[i];
543
0
        if (W) w16.u16[i] = w[i] << 8 | w[i];
544
0
    }
545
546
0
    CONTINUE(&x16, &y16, &z16, &w16);
547
0
}
548
549
DECL_FUNC(expand_quad, const SwsCompMask mask)
550
0
{
551
0
    block_t x32, y32, z32, w32;
552
0
553
0
    SWS_LOOP
554
0
    for (int i = 0; i < SWS_BLOCK_SIZE; i++) {
555
0
        if (X) x32.u32[i] = (uint32_t) x[i] << 24 | x[i] << 16 | x[i] << 8 | x[i];
556
0
        if (Y) y32.u32[i] = (uint32_t) y[i] << 24 | y[i] << 16 | y[i] << 8 | y[i];
557
0
        if (Z) z32.u32[i] = (uint32_t) z[i] << 24 | z[i] << 16 | z[i] << 8 | z[i];
558
0
        if (W) w32.u32[i] = (uint32_t) w[i] << 24 | w[i] << 16 | w[i] << 8 | w[i];
559
0
    }
560
0
561
0
    CONTINUE(&x32, &y32, &z32, &w32);
562
0
}
563
#endif /* BIT_DEPTH == 8 */
564
565
SWS_FOR(PX, EXPAND_BIT,  DECL_IMPL, expand_bit)
566
SWS_FOR(PX, EXPAND_PAIR, DECL_IMPL, expand_pair)
567
SWS_FOR(PX, EXPAND_QUAD, DECL_IMPL, expand_quad)
568
SWS_FOR_STRUCT(PX, EXPAND_BIT,  DECL_ENTRY)
569
SWS_FOR_STRUCT(PX, EXPAND_PAIR, DECL_ENTRY)
570
SWS_FOR_STRUCT(PX, EXPAND_QUAD, DECL_ENTRY)
571
572
/*************************
573
 * Packing and unpacking *
574
 ************************/
575
576
#if !IS_FLOAT
577
DECL_FUNC(unpack, const SwsCompMask mask,
578
                  const uint8_t bx, const uint8_t by,
579
                  const uint8_t bz, const uint8_t bw)
580
0
{
581
0
    const uint8_t sx = bw + bz + by;
582
0
    const uint8_t sy = bw + bz;
583
0
    const uint8_t sz = bw;
584
0
    const uint8_t sw = 0;
585
586
0
    const pixel_t mx = (1 << bx) - 1;
587
0
    const pixel_t my = (1 << by) - 1;
588
0
    const pixel_t mz = (1 << bz) - 1;
589
0
    const pixel_t mw = (1 << bw) - 1;
590
591
0
    SWS_LOOP
592
0
    for (int i = 0; i < SWS_BLOCK_SIZE; i++) {
593
0
        const pixel_t val = x[i];
594
0
        if (X) x[i] = (val >> sx) & mx;
595
0
        if (Y) y[i] = (val >> sy) & my;
596
0
        if (Z) z[i] = (val >> sz) & mz;
597
0
        if (W) w[i] = (val >> sw) & mw;
598
0
    }
599
600
0
    CONTINUE(x, y, z, w);
601
0
}
Unexecuted instantiation: uops_backend.c:unpack_U8
Unexecuted instantiation: uops_backend.c:unpack_U16
Unexecuted instantiation: uops_backend.c:unpack_U32
602
603
DECL_FUNC(pack, const SwsCompMask mask,
604
                const uint8_t bx, const uint8_t by,
605
                const uint8_t bz, const uint8_t bw)
606
0
{
607
0
    const uint8_t sx = bw + bz + by;
608
0
    const uint8_t sy = bw + bz;
609
0
    const uint8_t sz = bw;
610
0
    const uint8_t sw = 0;
611
612
0
    SWS_LOOP
613
0
    for (int i = 0; i < SWS_BLOCK_SIZE; i++) {
614
0
        pixel_t val = 0;
615
0
        if (X) val |= x[i] << sx;
616
0
        if (Y) val |= y[i] << sy;
617
0
        if (Z) val |= z[i] << sz;
618
0
        if (W) val |= w[i] << sw;
619
0
        x[i] = val;
620
0
    }
621
622
0
    CONTINUE(x, y, z, w);
623
0
}
Unexecuted instantiation: uops_backend.c:pack_U8
Unexecuted instantiation: uops_backend.c:pack_U16
Unexecuted instantiation: uops_backend.c:pack_U32
624
#endif /* !IS_FLOAT */
625
626
SWS_FOR(PX, UNPACK, DECL_IMPL, unpack)
627
SWS_FOR(PX, PACK,   DECL_IMPL, pack)
628
SWS_FOR_STRUCT(PX, UNPACK,  DECL_ENTRY)
629
SWS_FOR_STRUCT(PX, PACK,    DECL_ENTRY)
630
631
/***********************
632
 * Pixel data clearing *
633
 ***********************/
634
635
#ifdef PIXEL_MAX
636
DECL_FUNC(clear, const SwsCompMask mask, const SwsCompMask one,
637
                 const SwsCompMask zero)
638
0
{
639
0
    #define ONE(N)  SWS_COMP_TEST(one, N)
640
0
    #define ZERO(N) SWS_COMP_TEST(zero, N)
641
0
    const pixel_t cx = ONE(0) ? PIXEL_MAX : ZERO(0) ? 0 : impl->priv.px[0];
642
0
    const pixel_t cy = ONE(1) ? PIXEL_MAX : ZERO(1) ? 0 : impl->priv.px[1];
643
0
    const pixel_t cz = ONE(2) ? PIXEL_MAX : ZERO(2) ? 0 : impl->priv.px[2];
644
0
    const pixel_t cw = ONE(3) ? PIXEL_MAX : ZERO(3) ? 0 : impl->priv.px[3];
645
646
0
    SWS_LOOP
647
0
    for (int i = 0; i < SWS_BLOCK_SIZE; i++) {
648
0
        if (X) x[i] = cx;
649
0
        if (Y) y[i] = cy;
650
0
        if (Z) z[i] = cz;
651
0
        if (W) w[i] = cw;
652
0
    }
653
654
0
    CONTINUE(x, y, z, w);
655
0
}
Unexecuted instantiation: uops_backend.c:clear_U8
Unexecuted instantiation: uops_backend.c:clear_U16
Unexecuted instantiation: uops_backend.c:clear_U32
656
#endif
657
658
SWS_FOR(PX, CLEAR, DECL_IMPL, clear)
659
SWS_FOR_STRUCT(PX, CLEAR, DECL_ENTRY, .setup = ff_sws_setup_vec4)
660
661
/*************************
662
 * Arithmetic operations *
663
 *************************/
664
665
DECL_FUNC(scale, const SwsCompMask mask)
666
0
{
667
0
    const pixel_t scale = impl->priv.px[0];
668
669
0
    SWS_LOOP
670
0
    for (int i = 0; i < SWS_BLOCK_SIZE; i++) {
671
0
        if (X) x[i] *= scale;
672
0
        if (Y) y[i] *= scale;
673
0
        if (Z) z[i] *= scale;
674
0
        if (W) w[i] *= scale;
675
0
    }
676
677
0
    CONTINUE(x, y, z, w);
678
0
}
Unexecuted instantiation: uops_backend.c:scale_U8
Unexecuted instantiation: uops_backend.c:scale_U16
Unexecuted instantiation: uops_backend.c:scale_U32
Unexecuted instantiation: uops_backend.c:scale_F32
679
680
DECL_FUNC(add, const SwsCompMask mask)
681
0
{
682
0
    SWS_LOOP
683
0
    for (int i = 0; i < SWS_BLOCK_SIZE; i++) {
684
0
        if (X) x[i] += impl->priv.px[0];
685
0
        if (Y) y[i] += impl->priv.px[1];
686
0
        if (Z) z[i] += impl->priv.px[2];
687
0
        if (W) w[i] += impl->priv.px[3];
688
0
    }
689
690
0
    CONTINUE(x, y, z, w);
691
0
}
Unexecuted instantiation: uops_backend.c:add_F32
Unexecuted instantiation: uops_backend.c:add_U8
Unexecuted instantiation: uops_backend.c:add_U16
Unexecuted instantiation: uops_backend.c:add_U32
692
693
DECL_FUNC(min, const SwsCompMask mask)
694
0
{
695
0
    SWS_LOOP
696
0
    for (int i = 0; i < SWS_BLOCK_SIZE; i++) {
697
0
        if (X) x[i] = FFMIN(x[i], impl->priv.px[0]);
698
0
        if (Y) y[i] = FFMIN(y[i], impl->priv.px[1]);
699
0
        if (Z) z[i] = FFMIN(z[i], impl->priv.px[2]);
700
0
        if (W) w[i] = FFMIN(w[i], impl->priv.px[3]);
701
0
    }
702
703
0
    CONTINUE(x, y, z, w);
704
0
}
Unexecuted instantiation: uops_backend.c:min_F32
Unexecuted instantiation: uops_backend.c:min_U8
Unexecuted instantiation: uops_backend.c:min_U16
Unexecuted instantiation: uops_backend.c:min_U32
705
706
DECL_FUNC(max, const SwsCompMask mask)
707
0
{
708
0
    SWS_LOOP
709
0
    for (int i = 0; i < SWS_BLOCK_SIZE; i++) {
710
0
        if (X) x[i] = FFMAX(x[i], impl->priv.px[0]);
711
0
        if (Y) y[i] = FFMAX(y[i], impl->priv.px[1]);
712
0
        if (Z) z[i] = FFMAX(z[i], impl->priv.px[2]);
713
0
        if (W) w[i] = FFMAX(w[i], impl->priv.px[3]);
714
0
    }
715
716
0
    CONTINUE(x, y, z, w);
717
0
}
Unexecuted instantiation: uops_backend.c:max_F32
Unexecuted instantiation: uops_backend.c:max_U8
Unexecuted instantiation: uops_backend.c:max_U16
Unexecuted instantiation: uops_backend.c:max_U32
718
719
SWS_FOR(PX, SCALE, DECL_IMPL, scale)
720
SWS_FOR(PX, ADD,   DECL_IMPL, add)
721
SWS_FOR(PX, MIN,   DECL_IMPL, min)
722
SWS_FOR(PX, MAX,   DECL_IMPL, max)
723
SWS_FOR_STRUCT(PX, SCALE, DECL_ENTRY, .setup = ff_sws_setup_scalar )
724
SWS_FOR_STRUCT(PX, ADD,   DECL_ENTRY, .setup = ff_sws_setup_vec4 )
725
SWS_FOR_STRUCT(PX, MIN,   DECL_ENTRY, .setup = ff_sws_setup_vec4 )
726
SWS_FOR_STRUCT(PX, MAX,   DECL_ENTRY, .setup = ff_sws_setup_vec4 )
727
728
/*************
729
 * Dithering *
730
 *************/
731
732
DECL_SETUP(setup_dither, params, out)
733
0
{
734
0
    const SwsUOp *uop = params->uop;
735
0
    const SwsDitherUOp *dither = &uop->par.dither;
736
0
    const int size = 1 << dither->size_log2;
737
0
    if (size >= SWS_BLOCK_SIZE) {
738
        /* No extra padding needed */
739
0
        out->priv.ptr = av_refstruct_ref(uop->data.ptr);
740
0
        out->free = ff_op_priv_unref;
741
0
        return 0;
742
0
    }
743
744
0
    const int stride = FFMAX(size, SWS_BLOCK_SIZE);
745
0
    const int height = ff_sws_dither_height(dither);
746
0
    pixel_t *matrix = av_malloc(sizeof(pixel_t) * height * stride);
747
0
    if (!matrix)
748
0
        return AVERROR(ENOMEM);
749
0
    out->priv.ptr = matrix;
750
0
    out->free = ff_op_priv_free;
751
752
    /* Pad to multiple of block size. We don't need extra padding for the
753
     * height because ff_sws_dither_height() already includes any padding
754
     * necessary for the y_offset */
755
0
    for (int y = 0; y < height; y++) {
756
0
        pixel_t *row = &matrix[y * stride];
757
0
        for (int x = 0; x < size; x++)
758
0
            row[x] = uop->data.ptr[y * size + x].px;
759
0
        for (int x = size; x < stride; x++)
760
0
            row[x] = row[x % size];
761
0
    }
762
763
0
    return 0;
764
0
}
Unexecuted instantiation: uops_backend.c:setup_dither_F32
Unexecuted instantiation: uops_backend.c:setup_dither_U8
Unexecuted instantiation: uops_backend.c:setup_dither_U16
Unexecuted instantiation: uops_backend.c:setup_dither_U32
765
766
DECL_FUNC(dither, const SwsCompMask mask,
767
                  const uint8_t off0, const uint8_t off1,
768
                  const uint8_t off2, const uint8_t off3,
769
                  const uint8_t size_log2)
770
0
{
771
0
    const int size   = 1 << size_log2;
772
0
    const int stride = FFMAX(size, SWS_BLOCK_SIZE);
773
774
0
    const pixel_t *matrix = impl->priv.ptr;
775
0
    matrix += (iter->y & (size - 1)) * stride;
776
0
    matrix += (iter->x & (size - 1)) & ~(SWS_BLOCK_SIZE - 1);
777
778
0
    const pixel_t *const row0 = &matrix[off0 * stride];
779
0
    const pixel_t *const row1 = &matrix[off1 * stride];
780
0
    const pixel_t *const row2 = &matrix[off2 * stride];
781
0
    const pixel_t *const row3 = &matrix[off3 * stride];
782
783
0
    SWS_LOOP
784
0
    for (int i = 0; i < SWS_BLOCK_SIZE; i++) {
785
0
        if (X) x[i] += row0[i];
786
0
        if (Y) y[i] += row1[i];
787
0
        if (Z) z[i] += row2[i];
788
0
        if (W) w[i] += row3[i];
789
0
    }
790
791
0
    CONTINUE(x, y, z, w);
792
0
}
Unexecuted instantiation: uops_backend.c:dither_F32
Unexecuted instantiation: uops_backend.c:dither_U8
Unexecuted instantiation: uops_backend.c:dither_U16
Unexecuted instantiation: uops_backend.c:dither_U32
793
794
SWS_FOR(PX, DITHER, DECL_IMPL, dither)
795
SWS_FOR_STRUCT(PX, DITHER, DECL_ENTRY, .setup = fn(setup_dither) )
796
797
/*********************
798
 * Linear operations *
799
 *********************/
800
801
typedef struct {
802
    /* Stored in split form for convenience */
803
    pixel_t m[4][4];
804
    pixel_t k[4];
805
} fn(LinCoeffs);
806
807
DECL_SETUP(setup_linear, params, out)
808
0
{
809
0
    const SwsUOp *uop = params->uop;
810
0
    fn(LinCoeffs) c;
811
812
0
    for (int i = 0; i < 4; i++) {
813
0
        for (int j = 0; j < 4; j++)
814
0
            c.m[i][j] = uop->data.mat4x5[i][j].px;
815
0
        c.k[i] = uop->data.mat4x5[i][4].px;
816
0
    }
817
818
0
    out->priv.ptr = av_memdup(&c, sizeof(c));
819
0
    out->free = ff_op_priv_free;
820
0
    return out->priv.ptr ? 0 : AVERROR(ENOMEM);
821
0
}
Unexecuted instantiation: uops_backend.c:setup_linear_U8
Unexecuted instantiation: uops_backend.c:setup_linear_U16
Unexecuted instantiation: uops_backend.c:setup_linear_U32
Unexecuted instantiation: uops_backend.c:setup_linear_F32
822
823
/**
824
 * Fully general case for a 5x5 linear affine transformation. Should never be
825
 * called without constant `mask`. This function will compile down to the
826
 * appropriately optimized version for the required subset of operations when
827
 * called with a constant mask.
828
 */
829
DECL_FUNC(linear, const SwsCompMask mask, const uint32_t one, const uint32_t zero)
830
0
{
831
0
    const fn(LinCoeffs) c = *(const fn(LinCoeffs) *) impl->priv.ptr;
832
833
0
    SWS_LOOP
834
0
    for (int i = 0; i < SWS_BLOCK_SIZE; i++) {
835
0
        const pixel_t xx = x[i];
836
0
        const pixel_t yy = y[i];
837
0
        const pixel_t zz = z[i];
838
0
        const pixel_t ww = w[i];
839
840
0
#define LIN_VAL(I, J, val) \
841
0
    ((one & SWS_MASK(I, J)) ? (val) : (uinter_t) c.m[I][J] * (val))
842
843
0
#define LIN_ROW(I, var) do {                                    \
844
0
    pixel_t tmp = (zero & SWS_MASK(I, 4)) ? 0 : c.k[I];         \
845
0
    if (!(zero & SWS_MASK(I, 0))) tmp += LIN_VAL(I, 0, xx);     \
846
0
    if (!(zero & SWS_MASK(I, 1))) tmp += LIN_VAL(I, 1, yy);     \
847
0
    if (!(zero & SWS_MASK(I, 2))) tmp += LIN_VAL(I, 2, zz);     \
848
0
    if (!(zero & SWS_MASK(I, 3))) tmp += LIN_VAL(I, 3, ww);     \
849
0
    var[i] = tmp;                                               \
850
0
} while (0)
851
852
0
        if (X) LIN_ROW(0, x);
853
0
        if (Y) LIN_ROW(1, y);
854
0
        if (Z) LIN_ROW(2, z);
855
0
        if (W) LIN_ROW(3, w);
856
0
    }
857
858
0
    CONTINUE(x, y, z, w);
859
0
}
Unexecuted instantiation: uops_backend.c:linear_U8
Unexecuted instantiation: uops_backend.c:linear_U16
Unexecuted instantiation: uops_backend.c:linear_U32
Unexecuted instantiation: uops_backend.c:linear_F32
860
861
SWS_FOR(PX, LINEAR, DECL_IMPL, linear)
862
SWS_FOR_STRUCT(PX, LINEAR, DECL_ENTRY, .setup = fn(setup_linear) )
863
864
/******************
865
 * Look-up tables *
866
 ******************/
867
868
DECL_SETUP(setup_lut3d, params, out)
869
0
{
870
0
    const SwsLut3D *lut = params->uop->data.lut3d;
871
0
    out->priv.ptr = (void *) av_refstruct_ref_c(lut);
872
0
    out->free = ff_op_priv_unref;
873
0
    return 0;
874
0
}
Unexecuted instantiation: uops_backend.c:setup_lut3d_F32
Unexecuted instantiation: uops_backend.c:setup_lut3d_U8
Unexecuted instantiation: uops_backend.c:setup_lut3d_U16
Unexecuted instantiation: uops_backend.c:setup_lut3d_U32
875
876
#if IS_FLOAT
877
av_always_inline static vec3_t fn(vec3)(v3u16_t v)
878
0
{
879
0
    return (vec3_t) { v.x, v.y, v.z };
880
0
}
881
882
0
#define lerp(a, b, w) ((a) + (w) * ((pixel_t) (b) - (a)))
883
884
av_always_inline static
885
vec3_t fn(lerp3)(vec3_t a, vec3_t b, pixel_t w)
886
0
{
887
0
    return (vec3_t) {
888
0
        lerp(a.x, b.x, w),
889
0
        lerp(a.y, b.y, w),
890
0
        lerp(a.z, b.z, w),
891
0
    };
892
0
}
893
894
av_always_inline static
895
vec3_t fn(lut3d_static)(const SwsLut3D *restrict lut3d, vec3_t rgb)
896
0
{
897
0
    const int r_base = (int) rgb.x;
898
0
    const int g_base = (int) rgb.y;
899
0
    const int b_base = (int) rgb.z;
900
901
0
    int off0 = (r_base < INPUT_LUT_SIZE - 1);
902
0
    int off1 = (g_base < INPUT_LUT_SIZE - 1) * INPUT_LUT_SIZE;
903
0
    int off2 = (b_base < INPUT_LUT_SIZE - 1) * INPUT_LUT_SIZE * INPUT_LUT_SIZE;
904
0
    pixel_t f0 = rgb.x - r_base;
905
0
    pixel_t f1 = rgb.y - g_base;
906
0
    pixel_t f2 = rgb.z - b_base;
907
908
    /* Sort offsets descending by relative weight */
909
0
    if (f0 < f1) {
910
0
        FFSWAP(pixel_t, f0, f1);
911
0
        FFSWAP(int, off0, off1);
912
0
    }
913
0
    if (f0 < f2) {
914
0
        FFSWAP(pixel_t, f0, f2);
915
0
        FFSWAP(int, off0, off2);
916
0
    }
917
0
    if (f1 < f2) {
918
0
        FFSWAP(pixel_t, f1, f2);
919
0
        FFSWAP(int, off1, off2);
920
0
    }
921
922
    /* Tetrahedral interpolation */
923
0
    const pixel_t w0 = 1 - f0;
924
0
    const pixel_t w1 = f0 - f1;
925
0
    const pixel_t w2 = f1 - f2;
926
0
    const pixel_t w3 = f2;
927
928
0
    const v3u16_t *restrict base = &lut3d->input[b_base][g_base][r_base];
929
0
    const vec3_t v0 = fn(vec3)(base[0]);
930
0
    const vec3_t v1 = fn(vec3)(base[off0]);
931
0
    const vec3_t v2 = fn(vec3)(base[off0 + off1]);
932
0
    const vec3_t v3 = fn(vec3)(base[off0 + off1 + off2]);
933
934
0
    return (vec3_t) {
935
0
        w0 * v0.x + w1 * v1.x + w2 * v2.x + w3 * v3.x,
936
0
        w0 * v0.y + w1 * v1.y + w2 * v2.y + w3 * v3.y,
937
0
        w0 * v0.z + w1 * v1.z + w2 * v2.z + w3 * v3.z,
938
0
    };
939
0
}
940
941
av_always_inline static
942
vec3_t fn(lut3d_dynamic)(const SwsLut3D *restrict lut3d, vec3_t rgb)
943
0
{
944
0
    rgb.x *= (TONE_LUT_SIZE - 1) / (pixel_t) UINT16_MAX;
945
946
    /* Linear interpolation */
947
0
    const int     Ix = (int) rgb.x;
948
0
    const pixel_t If = rgb.x - Ix;
949
950
0
    const v2u16_t a = lut3d->tone_map[Ix];
951
0
    const v2u16_t b = lut3d->tone_map[Ix + 1];
952
953
0
    const pixel_t k     = lerp(a.y, b.y, If);
954
0
    const pixel_t bias  = (1 << 15) - k;
955
0
    const pixel_t scale = k / (pixel_t) (1 << 15);
956
957
0
    rgb.x = lerp(a.x, b.x, If);
958
0
    rgb.y = bias + scale * rgb.y;
959
0
    rgb.z = bias + scale * rgb.z;
960
961
    /* Re-scale to output LUT size */
962
0
    rgb.x *= (OUTPUT_LUT_SIZE_I  - 1) / (pixel_t) UINT16_MAX;
963
0
    rgb.y *= (OUTPUT_LUT_SIZE_PT - 1) / (pixel_t) UINT16_MAX;
964
0
    rgb.z *= (OUTPUT_LUT_SIZE_PT - 1) / (pixel_t) UINT16_MAX;
965
966
    /* Trilinear interpolation */
967
0
    const int lo0 = (int) rgb.x;
968
0
    const int lo1 = (int) rgb.y;
969
0
    const int lo2 = (int) rgb.z;
970
971
0
    const int hi0 = FFMIN(lo0 + 1, OUTPUT_LUT_SIZE_I  - 1);
972
0
    const int hi1 = FFMIN(lo1 + 1, OUTPUT_LUT_SIZE_PT - 1);
973
0
    const int hi2 = FFMIN(lo2 + 1, OUTPUT_LUT_SIZE_PT - 1);
974
975
0
    const pixel_t w0  = rgb.x - lo0;
976
0
    const vec3_t c000 = fn(vec3)(lut3d->output[lo2][lo1][lo0]);
977
0
    const vec3_t c001 = fn(vec3)(lut3d->output[lo2][lo1][hi0]);
978
0
    const vec3_t c00  = fn(lerp3)(c000, c001, w0);
979
0
    const vec3_t c010 = fn(vec3)(lut3d->output[lo2][hi1][lo0]);
980
0
    const vec3_t c011 = fn(vec3)(lut3d->output[lo2][hi1][hi0]);
981
0
    const vec3_t c01  = fn(lerp3)(c010, c011, w0);
982
0
    const vec3_t c100 = fn(vec3)(lut3d->output[hi2][lo1][lo0]);
983
0
    const vec3_t c101 = fn(vec3)(lut3d->output[hi2][lo1][hi0]);
984
0
    const vec3_t c10  = fn(lerp3)(c100, c101, w0);
985
0
    const vec3_t c110 = fn(vec3)(lut3d->output[hi2][hi1][lo0]);
986
0
    const vec3_t c111 = fn(vec3)(lut3d->output[hi2][hi1][hi0]);
987
0
    const vec3_t c11  = fn(lerp3)(c110, c111, w0);
988
989
0
    const pixel_t w1 = rgb.y - lo1;
990
0
    const vec3_t c0  = fn(lerp3)(c00, c01, w1);
991
0
    const vec3_t c1  = fn(lerp3)(c10, c11, w1);
992
993
0
    const pixel_t w2 = rgb.z - lo2;
994
0
    return fn(lerp3)(c0, c1, w2);
995
0
}
996
997
DECL_FUNC(lut3d, const SwsCompMask mask, const int dynamic)
998
0
{
999
0
    const SwsLut3D *restrict lut3d = impl->priv.ptr;
1000
1001
0
    SWS_LOOP
1002
0
    for (int i = 0; i < SWS_BLOCK_SIZE; i++) {
1003
0
        vec3_t c = { x[i], y[i], z[i] };
1004
0
        c = fn(lut3d_static)(lut3d, c);
1005
0
        if (dynamic)
1006
0
            c = fn(lut3d_dynamic)(lut3d, c);
1007
1008
0
        x[i] = c.x;
1009
0
        y[i] = c.y;
1010
0
        z[i] = c.z;
1011
0
    }
1012
1013
0
    CONTINUE(x, y, z, w);
1014
0
}
1015
#endif /* IS_FLOAT */
1016
1017
SWS_FOR(PX, LUT_3D, DECL_IMPL, lut3d)
1018
SWS_FOR_STRUCT(PX, LUT_3D, DECL_ENTRY, .setup = fn(setup_lut3d) )
1019
1020
#undef PIXEL_MAX
1021
#undef PIXEL_SWAP
1022
#undef pixel_t
1023
#undef inter_t
1024
#undef uinter_t
1025
#undef vec3_t
1026
#undef PX
1027
#undef px