FFmpeg
Loading...
Searching...
No Matches
uops_tmpl.c
Go to the documentation of this file.
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# define pixel_t float
32# define inter_t float
33# define vec3_t v3f32_t
34# define PX F32
35# define px f32
36#elif BIT_DEPTH == 32
37# define PIXEL_MAX 0xFFFFFFFFu
38# define PIXEL_SWAP av_bswap32
39# define pixel_t uint32_t
40# define inter_t int64_t
41# define PX U32
42# define px u32
43#elif BIT_DEPTH == 16
44# define PIXEL_MAX 0xFFFFu
45# define PIXEL_SWAP av_bswap16
46# define pixel_t uint16_t
47# define inter_t int64_t
48# define PX U16
49# define px u16
50#elif BIT_DEPTH == 8
51# define PIXEL_MAX 0xFFu
52# define pixel_t uint8_t
53# define inter_t int32_t
54# define PX U8
55# define px u8
56#else
57# error Invalid BIT_DEPTH
58#endif
59
60/*********************************
61 * Generic read/write operations *
62 *********************************/
63
64DECL_READ(read_planar, const SwsCompMask mask)
65{
67 for (int i = 0; i < SWS_BLOCK_SIZE; i++) {
68 if (X) x[i] = in0[i];
69 if (Y) y[i] = in1[i];
70 if (Z) z[i] = in2[i];
71 if (W) w[i] = in3[i];
72 }
73
74 if (X) iter->in[0] += SIZEOF_BLOCK;
75 if (Y) iter->in[1] += SIZEOF_BLOCK;
76 if (Z) iter->in[2] += SIZEOF_BLOCK;
77 if (W) iter->in[3] += SIZEOF_BLOCK;
78
79 CONTINUE(x, y, z, w);
80}
81
82DECL_READ(read_packed, const SwsCompMask mask)
83{
84 const int elems = W ? 4 : Z ? 3 : Y ? 2 : 1;
85
87 for (int i = 0; i < SWS_BLOCK_SIZE; i++) {
88 if (X) x[i] = in0[elems * i + 0];
89 if (Y) y[i] = in0[elems * i + 1];
90 if (Z) z[i] = in0[elems * i + 2];
91 if (W) w[i] = in0[elems * i + 3];
92 }
93
94 iter->in[0] += SIZEOF_BLOCK * elems;
95 CONTINUE(x, y, z, w);
96}
97
98DECL_WRITE(write_planar, const SwsCompMask mask)
99{
101 for (int i = 0; i < SWS_BLOCK_SIZE; i++) {
102 if (X) out0[i] = x[i];
103 if (Y) out1[i] = y[i];
104 if (Z) out2[i] = z[i];
105 if (W) out3[i] = w[i];
106 }
107
108 if (X) iter->out[0] += SIZEOF_BLOCK;
109 if (Y) iter->out[1] += SIZEOF_BLOCK;
110 if (Z) iter->out[2] += SIZEOF_BLOCK;
111 if (W) iter->out[3] += SIZEOF_BLOCK;
112}
113
114DECL_WRITE(write_packed, const SwsCompMask mask)
115{
116 const int elems = W ? 4 : Z ? 3 : Y ? 2 : 1;
117
119 for (int i = 0; i < SWS_BLOCK_SIZE; i++) {
120 if (X) out0[elems * i + 0] = x[i];
121 if (Y) out0[elems * i + 1] = y[i];
122 if (Z) out0[elems * i + 2] = z[i];
123 if (W) out0[elems * i + 3] = w[i];
124 }
125
126 iter->out[0] += SIZEOF_BLOCK * elems;
127}
128
129#if BIT_DEPTH == 8
130
132{
134
136 for (int i = 0; i < SWS_BLOCK_SIZE; i += 8) {
137 const pixel_t val = ((const pixel_t *) in0)[i >> 3];
138 x[i + 0] = (val >> 7) & 1;
139 x[i + 1] = (val >> 6) & 1;
140 x[i + 2] = (val >> 5) & 1;
141 x[i + 3] = (val >> 4) & 1;
142 x[i + 4] = (val >> 3) & 1;
143 x[i + 5] = (val >> 2) & 1;
144 x[i + 6] = (val >> 1) & 1;
145 x[i + 7] = (val >> 0) & 1;
146 }
147
148 iter->in[0] += SIZEOF_BLOCK >> 3;
149 CONTINUE(x, y, z, w);
150}
151
152DECL_READ(read_nibble, const SwsCompMask mask)
153{
155
157 for (int i = 0; i < SWS_BLOCK_SIZE; i += 2) {
158 const pixel_t val = in0[i >> 1];
159 x[i + 0] = val >> 4; /* high nibble */
160 x[i + 1] = val & 0xF; /* low nibble */
161 }
162
163 iter->in[0] += SIZEOF_BLOCK >> 1;
164 CONTINUE(x, y, z, w);
165}
166
167DECL_READ(read_palette, const SwsCompMask mask)
168{
170
172 for (int i = 0; i < SWS_BLOCK_SIZE; i++) {
173 const pixel_t index = in0[i];
174 const pixel_t *value = &in1[index * 4];
175 x[i] = value[0];
176 y[i] = value[1];
177 z[i] = value[2];
178 w[i] = value[3];
179 }
180
181 iter->in[0] += SIZEOF_BLOCK;
182 CONTINUE(x, y, z, w);
183}
184
185DECL_WRITE(write_bit, const SwsCompMask mask)
186{
188
190 for (int i = 0; i < SWS_BLOCK_SIZE; i += 8) {
191 out0[i >> 3] = x[i + 0] << 7 |
192 x[i + 1] << 6 |
193 x[i + 2] << 5 |
194 x[i + 3] << 4 |
195 x[i + 4] << 3 |
196 x[i + 5] << 2 |
197 x[i + 6] << 1 |
198 x[i + 7];
199 }
200
201 iter->out[0] += SIZEOF_BLOCK >> 3;
202}
203
204DECL_WRITE(write_nibble, const SwsCompMask mask)
205{
207
209 for (int i = 0; i < SWS_BLOCK_SIZE; i += 2)
210 out0[i >> 1] = x[i] << 4 | x[i + 1];
211
212 iter->out[0] += SIZEOF_BLOCK >> 1;
213}
214
215#endif /* BIT_DEPTH == 8 */
216
217SWS_FOR(PX, READ_PLANAR, DECL_IMPL_READ, read_planar)
218SWS_FOR(PX, READ_PACKED, DECL_IMPL_READ, read_packed)
219SWS_FOR(PX, READ_NIBBLE, DECL_IMPL_READ, read_nibble)
220SWS_FOR(PX, READ_BIT, DECL_IMPL_READ, read_bit)
221SWS_FOR(PX, READ_PALETTE, DECL_IMPL_READ, read_palette)
222SWS_FOR(PX, WRITE_PLANAR, DECL_IMPL_WRITE, write_planar)
223SWS_FOR(PX, WRITE_PACKED, DECL_IMPL_WRITE, write_packed)
224SWS_FOR(PX, WRITE_NIBBLE, DECL_IMPL_WRITE, write_nibble)
225SWS_FOR(PX, WRITE_BIT, DECL_IMPL_WRITE, write_bit)
226
227SWS_FOR_STRUCT(PX, READ_PLANAR, DECL_ENTRY)
228SWS_FOR_STRUCT(PX, READ_PACKED, DECL_ENTRY)
229SWS_FOR_STRUCT(PX, READ_NIBBLE, DECL_ENTRY)
230SWS_FOR_STRUCT(PX, READ_BIT, DECL_ENTRY)
231SWS_FOR_STRUCT(PX, READ_PALETTE, DECL_ENTRY)
232SWS_FOR_STRUCT(PX, WRITE_PLANAR, DECL_ENTRY)
233SWS_FOR_STRUCT(PX, WRITE_PACKED, DECL_ENTRY)
234SWS_FOR_STRUCT(PX, WRITE_NIBBLE, DECL_ENTRY)
235SWS_FOR_STRUCT(PX, WRITE_BIT, DECL_ENTRY)
236
237/*****************************
238 * Scaling / filtering reads *
239 *****************************/
240
242{
243 if (params->uop->par.filter.type != SWS_PIXEL_F32)
244 return AVERROR(ENOTSUP);
245
246 const SwsFilterWeights *filter = params->uop->data.kernel;
247 static_assert(sizeof(out->priv.ptr) <= sizeof(int32_t[2]),
248 ">8 byte pointers not supported");
249
250 /* Pre-convert weights to float */
251 float *weights = av_calloc(filter->num_weights, sizeof(float));
252 if (!weights)
253 return AVERROR(ENOMEM);
254
255 for (int i = 0; i < filter->num_weights; i++)
256 weights[i] = (float) filter->weights[i] / SWS_FILTER_SCALE;
257
258 out->priv.ptr = weights;
259 out->priv.i32[2] = filter->filter_size;
260 out->free = ff_op_priv_free;
261 return 0;
262}
263
264/* Fully general vertical planar filter case */
265DECL_READ(read_planar_fv, const SwsCompMask mask, const SwsPixelType type)
266{
268 const SwsOpExec *exec = iter->exec;
269 const float *restrict weights = impl->priv.ptr;
270 const int filter_size = impl->priv.i32[2];
271 weights += filter_size * iter->y;
272
273 block_t xs, ys, zs, ws;
274 if (X) memset(&xs.f32, 0, sizeof(xs.f32));
275 if (Y) memset(&ys.f32, 0, sizeof(ys.f32));
276 if (Z) memset(&zs.f32, 0, sizeof(zs.f32));
277 if (W) memset(&ws.f32, 0, sizeof(ws.f32));
278
279 for (int j = 0; j < filter_size; j++) {
280 const float weight = weights[j];
281
283 for (int i = 0; i < SWS_BLOCK_SIZE; i++) {
284 if (X) xs.f32[i] += weight * in0[i];
285 if (Y) ys.f32[i] += weight * in1[i];
286 if (Z) zs.f32[i] += weight * in2[i];
287 if (W) ws.f32[i] += weight * in3[i];
288 }
289
290 if (X) in0 = bump_ptr(in0, exec->in_stride[0]);
291 if (Y) in1 = bump_ptr(in1, exec->in_stride[1]);
292 if (Z) in2 = bump_ptr(in2, exec->in_stride[2]);
293 if (W) in3 = bump_ptr(in3, exec->in_stride[3]);
294 }
295
296 if (X) iter->in[0] += SIZEOF_BLOCK;
297 if (Y) iter->in[1] += SIZEOF_BLOCK;
298 if (Z) iter->in[2] += SIZEOF_BLOCK;
299 if (W) iter->in[3] += SIZEOF_BLOCK;
300
301 CONTINUE(&xs, &ys, &zs, &ws);
302}
303
305{
306 if (params->uop->par.filter.type != SWS_PIXEL_F32)
307 return AVERROR(ENOTSUP);
308
309 SwsFilterWeights *filter = params->uop->data.kernel;
310 out->priv.ptr = av_refstruct_ref(filter->weights);
311 out->priv.i32[2] = filter->filter_size;
312 out->free = ff_op_priv_unref;
313 return 0;
314}
315
316/* Fully general horizontal planar filter case */
317DECL_READ(read_planar_fh, const SwsCompMask mask, const SwsPixelType type)
318{
320 const SwsOpExec *exec = iter->exec;
321 const int *restrict weights = impl->priv.ptr;
322 const int filter_size = impl->priv.i32[2];
323 const float scale = 1.0f / SWS_FILTER_SCALE;
324 const int xpos = iter->x;
325 weights += filter_size * iter->x;
326
327 block_t xs, ys, zs, ws;
328 for (int i = 0; i < SWS_BLOCK_SIZE; i++) {
329 const int offset = exec->in_offset_x[xpos + i];
330 pixel_t *start0 = bump_ptr(in0, offset);
331 pixel_t *start1 = bump_ptr(in1, offset);
332 pixel_t *start2 = bump_ptr(in2, offset);
333 pixel_t *start3 = bump_ptr(in3, offset);
334
335 inter_t sx = 0, sy = 0, sz = 0, sw = 0;
336 for (int j = 0; j < filter_size; j++) {
337 const int weight = weights[j];
338 if (X) sx += weight * start0[j];
339 if (Y) sy += weight * start1[j];
340 if (Z) sz += weight * start2[j];
341 if (W) sw += weight * start3[j];
342 }
343
344 if (X) xs.f32[i] = (float) sx * scale;
345 if (Y) ys.f32[i] = (float) sy * scale;
346 if (Z) zs.f32[i] = (float) sz * scale;
347 if (W) ws.f32[i] = (float) sw * scale;
348
349 weights += filter_size;
350 }
351
352 CONTINUE(&xs, &ys, &zs, &ws);
353}
354
355SWS_FOR(PX, READ_PLANAR_FV, DECL_IMPL_READ, read_planar_fv)
356SWS_FOR(PX, READ_PLANAR_FH, DECL_IMPL_READ, read_planar_fh)
357SWS_FOR_STRUCT(PX, READ_PLANAR_FV, DECL_ENTRY, .setup = fn(setup_filter_v) )
358SWS_FOR_STRUCT(PX, READ_PLANAR_FH, DECL_ENTRY, .setup = fn(setup_filter_h) )
359
360/***************************
361 * Permutation and copying *
362 ***************************/
363
364DECL_FUNC(permute, const SwsCompMask mask, int num_moves,
365 int8_t d0, int8_t d1, int8_t d2, int8_t d3, int8_t d4, int8_t d5,
366 int8_t s0, int8_t s1, int8_t s2, int8_t s3, int8_t s4, int8_t s5)
367{
368 const int8_t dst[SWS_UOP_MOVE_MAX] = { d0, d1, d2, d3, d4, d5 };
369 const int8_t src[SWS_UOP_MOVE_MAX] = { s0, s1, s2, s3, s4, s5 };
370
371 pixel_t *ptr[5] = { NULL, x, y, z, w };
372 for (int n = 0; n < num_moves; n++)
373 ptr[dst[n] + 1] = ptr[src[n] + 1];
374
375 /* The unneeded registers may still alias the used ones, so point them
376 * back at the stack to avoid collisions */
377 block_t xx, yy, zz, ww;
378 CONTINUE(X ? ptr[1] : xx.px,
379 Y ? ptr[2] : yy.px,
380 Z ? ptr[3] : zz.px,
381 W ? ptr[4] : ww.px);
382}
383
384DECL_FUNC(copy, 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{
388 const size_t block_size = SWS_BLOCK_SIZE * sizeof(pixel_t);
389 const int8_t dst[SWS_UOP_MOVE_MAX] = { d0, d1, d2, d3, d4, d5 };
390 const int8_t src[SWS_UOP_MOVE_MAX] = { s0, s1, s2, s3, s4, s5 };
391
392 block_t data[5];
393 memcpy(&data[1].px, x, block_size);
394 memcpy(&data[2].px, y, block_size);
395 memcpy(&data[3].px, z, block_size);
396 memcpy(&data[4].px, w, block_size);
397
398 for (int n = 0; n < num_moves; n++)
399 data[dst[n] + 1] = data[src[n] + 1];
400
401 memcpy(x, &data[1].px, block_size);
402 memcpy(y, &data[2].px, block_size);
403 memcpy(z, &data[3].px, block_size);
404 memcpy(w, &data[4].px, block_size);
405
406 CONTINUE(x, y, z, w);
407}
408
409SWS_FOR(PX, PERMUTE, DECL_IMPL, permute)
411SWS_FOR_STRUCT(PX, PERMUTE, DECL_ENTRY)
413
414/*********************
415 * Format conversion *
416 *********************/
417
418#define DECL_CAST(DST, dst) \
419 DECL_FUNC(to_##dst, const SwsCompMask mask) \
420 { \
421 block_t xx, yy, zz, ww; \
422 \
423 SWS_LOOP \
424 for (int i = 0; i < SWS_BLOCK_SIZE; i++) { \
425 if (X) xx.dst[i] = x[i]; \
426 if (Y) yy.dst[i] = y[i]; \
427 if (Z) zz.dst[i] = z[i]; \
428 if (W) ww.dst[i] = w[i]; \
429 } \
430 \
431 CONTINUE(&xx, &yy, &zz, &ww); \
432 } \
433 \
434 SWS_FOR(PX, TO_##DST, DECL_IMPL, to_##dst) \
435 SWS_FOR_STRUCT(PX, TO_##DST, DECL_ENTRY)
436
437DECL_CAST(U8, u8)
438DECL_CAST(U16, u16)
439DECL_CAST(U32, u32)
440DECL_CAST(F32, f32)
441
442/********************
443 * Bit manipulation *
444 ********************/
445
446#if !IS_FLOAT
447DECL_FUNC(lshift, const SwsCompMask mask, const uint8_t amount)
448{
450 for (int i = 0; i < SWS_BLOCK_SIZE; i++) {
451 if (X) x[i] <<= amount;
452 if (Y) y[i] <<= amount;
453 if (Z) z[i] <<= amount;
454 if (W) w[i] <<= amount;
455 }
456
457 CONTINUE(x, y, z, w);
458}
459
460DECL_FUNC(rshift, const SwsCompMask mask, const uint8_t amount)
461{
463 for (int i = 0; i < SWS_BLOCK_SIZE; i++) {
464 if (X) x[i] >>= amount;
465 if (Y) y[i] >>= amount;
466 if (Z) z[i] >>= amount;
467 if (W) w[i] >>= amount;
468 }
469
470 CONTINUE(x, y, z, w);
471}
472#endif
473
474SWS_FOR(PX, LSHIFT, DECL_IMPL, lshift)
475SWS_FOR(PX, RSHIFT, DECL_IMPL, rshift)
476
477SWS_FOR_STRUCT(PX, LSHIFT, DECL_ENTRY)
479
480#ifdef PIXEL_SWAP
481DECL_FUNC(swap_bytes, const SwsCompMask mask)
482{
484 for (int i = 0; i < SWS_BLOCK_SIZE; i++) {
485 if (X) x[i] = PIXEL_SWAP(x[i]);
486 if (Y) y[i] = PIXEL_SWAP(y[i]);
487 if (Z) z[i] = PIXEL_SWAP(z[i]);
488 if (W) w[i] = PIXEL_SWAP(w[i]);
489 }
490
491 CONTINUE(x, y, z, w);
492}
493#endif /* PIXEL_SWAP */
494
495SWS_FOR(PX, SWAP_BYTES, DECL_IMPL, swap_bytes)
496SWS_FOR_STRUCT(PX, SWAP_BYTES, DECL_ENTRY)
497
498#ifdef PIXEL_MAX
499DECL_FUNC(expand_bit, const SwsCompMask mask)
500{
502 for (int i = 0; i < SWS_BLOCK_SIZE; i++) {
503 if (X) x[i] = x[i] ? PIXEL_MAX : 0;
504 if (Y) y[i] = y[i] ? PIXEL_MAX : 0;
505 if (Z) z[i] = z[i] ? PIXEL_MAX : 0;
506 if (W) w[i] = w[i] ? PIXEL_MAX : 0;
507 }
508
509 CONTINUE(x, y, z, w);
510}
511#endif
512
513#if BIT_DEPTH == 8
514DECL_FUNC(expand_pair, const SwsCompMask mask)
515{
516 block_t x16, y16, z16, w16;
517
519 for (int i = 0; i < SWS_BLOCK_SIZE; i++) {
520 if (X) x16.u16[i] = x[i] << 8 | x[i];
521 if (Y) y16.u16[i] = y[i] << 8 | y[i];
522 if (Z) z16.u16[i] = z[i] << 8 | z[i];
523 if (W) w16.u16[i] = w[i] << 8 | w[i];
524 }
525
526 CONTINUE(&x16, &y16, &z16, &w16);
527}
528
529DECL_FUNC(expand_quad, const SwsCompMask mask)
530{
531 block_t x32, y32, z32, w32;
532
534 for (int i = 0; i < SWS_BLOCK_SIZE; i++) {
535 if (X) x32.u32[i] = (uint32_t) x[i] << 24 | x[i] << 16 | x[i] << 8 | x[i];
536 if (Y) y32.u32[i] = (uint32_t) y[i] << 24 | y[i] << 16 | y[i] << 8 | y[i];
537 if (Z) z32.u32[i] = (uint32_t) z[i] << 24 | z[i] << 16 | z[i] << 8 | z[i];
538 if (W) w32.u32[i] = (uint32_t) w[i] << 24 | w[i] << 16 | w[i] << 8 | w[i];
539 }
540
541 CONTINUE(&x32, &y32, &z32, &w32);
542}
543#endif /* BIT_DEPTH == 8 */
544
545SWS_FOR(PX, EXPAND_BIT, DECL_IMPL, expand_bit)
546SWS_FOR(PX, EXPAND_PAIR, DECL_IMPL, expand_pair)
547SWS_FOR(PX, EXPAND_QUAD, DECL_IMPL, expand_quad)
548SWS_FOR_STRUCT(PX, EXPAND_BIT, DECL_ENTRY)
549SWS_FOR_STRUCT(PX, EXPAND_PAIR, DECL_ENTRY)
550SWS_FOR_STRUCT(PX, EXPAND_QUAD, DECL_ENTRY)
551
552/*************************
553 * Packing and unpacking *
554 ************************/
555
556#if !IS_FLOAT
558 const uint8_t bx, const uint8_t by,
559 const uint8_t bz, const uint8_t bw)
560{
561 const uint8_t sx = bw + bz + by;
562 const uint8_t sy = bw + bz;
563 const uint8_t sz = bw;
564 const uint8_t sw = 0;
565
566 const pixel_t mx = (1 << bx) - 1;
567 const pixel_t my = (1 << by) - 1;
568 const pixel_t mz = (1 << bz) - 1;
569 const pixel_t mw = (1 << bw) - 1;
570
572 for (int i = 0; i < SWS_BLOCK_SIZE; i++) {
573 const pixel_t val = x[i];
574 if (X) x[i] = (val >> sx) & mx;
575 if (Y) y[i] = (val >> sy) & my;
576 if (Z) z[i] = (val >> sz) & mz;
577 if (W) w[i] = (val >> sw) & mw;
578 }
579
580 CONTINUE(x, y, z, w);
581}
582
584 const uint8_t bx, const uint8_t by,
585 const uint8_t bz, const uint8_t bw)
586{
587 const uint8_t sx = bw + bz + by;
588 const uint8_t sy = bw + bz;
589 const uint8_t sz = bw;
590 const uint8_t sw = 0;
591
593 for (int i = 0; i < SWS_BLOCK_SIZE; i++) {
594 pixel_t val = 0;
595 if (X) val |= x[i] << sx;
596 if (Y) val |= y[i] << sy;
597 if (Z) val |= z[i] << sz;
598 if (W) val |= w[i] << sw;
599 x[i] = val;
600 }
601
602 CONTINUE(x, y, z, w);
603}
604#endif /* !IS_FLOAT */
605
606SWS_FOR(PX, UNPACK, DECL_IMPL, unpack)
607SWS_FOR(PX, PACK, DECL_IMPL, pack)
608SWS_FOR_STRUCT(PX, UNPACK, DECL_ENTRY)
609SWS_FOR_STRUCT(PX, PACK, DECL_ENTRY)
610
611/***********************
612 * Pixel data clearing *
613 ***********************/
614
615#ifdef PIXEL_MAX
616DECL_FUNC(clear, const SwsCompMask mask, const SwsCompMask one,
617 const SwsCompMask zero)
618{
619 #define ONE(N) SWS_COMP_TEST(one, N)
620 #define ZERO(N) SWS_COMP_TEST(zero, N)
621 const pixel_t cx = ONE(0) ? PIXEL_MAX : ZERO(0) ? 0 : impl->priv.px[0];
622 const pixel_t cy = ONE(1) ? PIXEL_MAX : ZERO(1) ? 0 : impl->priv.px[1];
623 const pixel_t cz = ONE(2) ? PIXEL_MAX : ZERO(2) ? 0 : impl->priv.px[2];
624 const pixel_t cw = ONE(3) ? PIXEL_MAX : ZERO(3) ? 0 : impl->priv.px[3];
625
627 for (int i = 0; i < SWS_BLOCK_SIZE; i++) {
628 if (X) x[i] = cx;
629 if (Y) y[i] = cy;
630 if (Z) z[i] = cz;
631 if (W) w[i] = cw;
632 }
633
634 CONTINUE(x, y, z, w);
635}
636#endif
637
638SWS_FOR(PX, CLEAR, DECL_IMPL, clear)
640
641/*************************
642 * Arithmetic operations *
643 *************************/
644
646{
647 const pixel_t scale = impl->priv.px[0];
648
650 for (int i = 0; i < SWS_BLOCK_SIZE; i++) {
651 if (X) x[i] *= scale;
652 if (Y) y[i] *= scale;
653 if (Z) z[i] *= scale;
654 if (W) w[i] *= scale;
655 }
656
657 CONTINUE(x, y, z, w);
658}
659
661{
663 for (int i = 0; i < SWS_BLOCK_SIZE; i++) {
664 if (X) x[i] += impl->priv.px[0];
665 if (Y) y[i] += impl->priv.px[1];
666 if (Z) z[i] += impl->priv.px[2];
667 if (W) w[i] += impl->priv.px[3];
668 }
669
670 CONTINUE(x, y, z, w);
671}
672
674{
676 for (int i = 0; i < SWS_BLOCK_SIZE; i++) {
677 if (X) x[i] = FFMIN(x[i], impl->priv.px[0]);
678 if (Y) y[i] = FFMIN(y[i], impl->priv.px[1]);
679 if (Z) z[i] = FFMIN(z[i], impl->priv.px[2]);
680 if (W) w[i] = FFMIN(w[i], impl->priv.px[3]);
681 }
682
683 CONTINUE(x, y, z, w);
684}
685
687{
689 for (int i = 0; i < SWS_BLOCK_SIZE; i++) {
690 if (X) x[i] = FFMAX(x[i], impl->priv.px[0]);
691 if (Y) y[i] = FFMAX(y[i], impl->priv.px[1]);
692 if (Z) z[i] = FFMAX(z[i], impl->priv.px[2]);
693 if (W) w[i] = FFMAX(w[i], impl->priv.px[3]);
694 }
695
696 CONTINUE(x, y, z, w);
697}
698
700SWS_FOR(PX, ADD, DECL_IMPL, add)
707
708/*************
709 * Dithering *
710 *************/
711
713{
714 const SwsUOp *uop = params->uop;
715 const SwsDitherUOp *dither = &uop->par.dither;
716 const int size = 1 << dither->size_log2;
717 if (size >= SWS_BLOCK_SIZE) {
718 /* No extra padding needed */
719 out->priv.ptr = av_refstruct_ref(uop->data.ptr);
720 out->free = ff_op_priv_unref;
721 return 0;
722 }
723
724 const int stride = FFMAX(size, SWS_BLOCK_SIZE);
726 pixel_t *matrix = av_malloc(sizeof(pixel_t) * height * stride);
727 if (!matrix)
728 return AVERROR(ENOMEM);
729 out->priv.ptr = matrix;
730 out->free = ff_op_priv_free;
731
732 /* Pad to multiple of block size. We don't need extra padding for the
733 * height because ff_sws_dither_height() already includes any padding
734 * necessary for the y_offset */
735 for (int y = 0; y < height; y++) {
736 pixel_t *row = &matrix[y * stride];
737 for (int x = 0; x < size; x++)
738 row[x] = uop->data.ptr[y * size + x].px;
739 for (int x = size; x < stride; x++)
740 row[x] = row[x % size];
741 }
742
743 return 0;
744}
745
747 const uint8_t off0, const uint8_t off1,
748 const uint8_t off2, const uint8_t off3,
749 const uint8_t size_log2)
750{
751 const int size = 1 << size_log2;
752 const int stride = FFMAX(size, SWS_BLOCK_SIZE);
753
754 const pixel_t *matrix = impl->priv.ptr;
755 matrix += (iter->y & (size - 1)) * stride;
756 matrix += (iter->x & (size - 1)) & ~(SWS_BLOCK_SIZE - 1);
757
758 const pixel_t *const row0 = &matrix[off0 * stride];
759 const pixel_t *const row1 = &matrix[off1 * stride];
760 const pixel_t *const row2 = &matrix[off2 * stride];
761 const pixel_t *const row3 = &matrix[off3 * stride];
762
764 for (int i = 0; i < SWS_BLOCK_SIZE; i++) {
765 if (X) x[i] += row0[i];
766 if (Y) y[i] += row1[i];
767 if (Z) z[i] += row2[i];
768 if (W) w[i] += row3[i];
769 }
770
771 CONTINUE(x, y, z, w);
772}
773
774SWS_FOR(PX, DITHER, DECL_IMPL, dither)
775SWS_FOR_STRUCT(PX, DITHER, DECL_ENTRY, .setup = fn(setup_dither) )
776
777/*********************
778 * Linear operations *
779 *********************/
780
781typedef struct {
782 /* Stored in split form for convenience */
783 pixel_t m[4][4];
784 pixel_t k[4];
785} fn(LinCoeffs);
786
788{
789 const SwsUOp *uop = params->uop;
790 fn(LinCoeffs) c;
791
792 for (int i = 0; i < 4; i++) {
793 for (int j = 0; j < 4; j++)
794 c.m[i][j] = uop->data.mat4[i][j].px;
795 c.k[i] = uop->data.mat4[i][4].px;
796 }
797
798 out->priv.ptr = av_memdup(&c, sizeof(c));
799 out->free = ff_op_priv_free;
800 return out->priv.ptr ? 0 : AVERROR(ENOMEM);
801}
802
803/**
804 * Fully general case for a 5x5 linear affine transformation. Should never be
805 * called without constant `mask`. This function will compile down to the
806 * appropriately optimized version for the required subset of operations when
807 * called with a constant mask.
808 */
809DECL_FUNC(linear, const SwsCompMask mask, const uint32_t one, const uint32_t zero)
810{
811 const fn(LinCoeffs) c = *(const fn(LinCoeffs) *) impl->priv.ptr;
812
814 for (int i = 0; i < SWS_BLOCK_SIZE; i++) {
815 const pixel_t xx = x[i];
816 const pixel_t yy = y[i];
817 const pixel_t zz = z[i];
818 const pixel_t ww = w[i];
819
820#define LIN_VAL(I, J, val) \
821 ((one & SWS_MASK(I, J)) ? (val) : c.m[I][J] * (val))
822
823#define LIN_ROW(I, var) do { \
824 var[i] = (zero & SWS_MASK(I, 4)) ? 0 : c.k[I]; \
825 if (!(zero & SWS_MASK(I, 0))) var[i] += LIN_VAL(I, 0, xx); \
826 if (!(zero & SWS_MASK(I, 1))) var[i] += LIN_VAL(I, 1, yy); \
827 if (!(zero & SWS_MASK(I, 2))) var[i] += LIN_VAL(I, 2, zz); \
828 if (!(zero & SWS_MASK(I, 3))) var[i] += LIN_VAL(I, 3, ww); \
829} while (0)
830
831 if (X) LIN_ROW(0, x);
832 if (Y) LIN_ROW(1, y);
833 if (Z) LIN_ROW(2, z);
834 if (W) LIN_ROW(3, w);
835 }
836
837 CONTINUE(x, y, z, w);
838}
839
842
843/******************
844 * Look-up tables *
845 ******************/
846
847DECL_SETUP(setup_lut3d, params, out)
848{
849 const SwsLut3D *lut = params->uop->data.lut3d;
850 out->priv.ptr = (void *) av_refstruct_ref_c(lut);
851 out->free = ff_op_priv_unref;
852 return 0;
853}
854
855#if IS_FLOAT
856av_always_inline static vec3_t fn(vec3)(v3u16_t v)
857{
858 return (vec3_t) { v.x, v.y, v.z };
859}
860
861#define lerp(a, b, w) ((a) + (w) * ((pixel_t) (b) - (a)))
862
863av_always_inline static
864vec3_t fn(lerp3)(vec3_t a, vec3_t b, pixel_t w)
865{
866 return (vec3_t) {
867 lerp(a.x, b.x, w),
868 lerp(a.y, b.y, w),
869 lerp(a.z, b.z, w),
870 };
871}
872
873av_always_inline static
874vec3_t fn(lut3d_static)(const SwsLut3D *restrict lut3d, vec3_t rgb)
875{
876 const int r_base = (int) rgb.x;
877 const int g_base = (int) rgb.y;
878 const int b_base = (int) rgb.z;
879
880 int off0 = (r_base < INPUT_LUT_SIZE - 1);
881 int off1 = (g_base < INPUT_LUT_SIZE - 1) * INPUT_LUT_SIZE;
882 int off2 = (b_base < INPUT_LUT_SIZE - 1) * INPUT_LUT_SIZE * INPUT_LUT_SIZE;
883 pixel_t f0 = rgb.x - r_base;
884 pixel_t f1 = rgb.y - g_base;
885 pixel_t f2 = rgb.z - b_base;
886
887 /* Sort offsets descending by relative weight */
888 if (f0 < f1) {
889 FFSWAP(pixel_t, f0, f1);
890 FFSWAP(int, off0, off1);
891 }
892 if (f0 < f2) {
893 FFSWAP(pixel_t, f0, f2);
894 FFSWAP(int, off0, off2);
895 }
896 if (f1 < f2) {
897 FFSWAP(pixel_t, f1, f2);
898 FFSWAP(int, off1, off2);
899 }
900
901 /* Tetrahedral interpolation */
902 const pixel_t w0 = 1 - f0;
903 const pixel_t w1 = f0 - f1;
904 const pixel_t w2 = f1 - f2;
905 const pixel_t w3 = f2;
906
907 const v3u16_t *restrict base = &lut3d->input[b_base][g_base][r_base];
908 const vec3_t v0 = fn(vec3)(base[0]);
909 const vec3_t v1 = fn(vec3)(base[off0]);
910 const vec3_t v2 = fn(vec3)(base[off0 + off1]);
911 const vec3_t v3 = fn(vec3)(base[off0 + off1 + off2]);
912
913 return (vec3_t) {
914 w0 * v0.x + w1 * v1.x + w2 * v2.x + w3 * v3.x,
915 w0 * v0.y + w1 * v1.y + w2 * v2.y + w3 * v3.y,
916 w0 * v0.z + w1 * v1.z + w2 * v2.z + w3 * v3.z,
917 };
918}
919
920av_always_inline static
921vec3_t fn(lut3d_dynamic)(const SwsLut3D *restrict lut3d, vec3_t rgb)
922{
923 rgb.x *= (TONE_LUT_SIZE - 1) / (pixel_t) UINT16_MAX;
924
925 /* Linear interpolation */
926 const int Ix = (int) rgb.x;
927 const pixel_t If = rgb.x - Ix;
928
929 const v2u16_t a = lut3d->tone_map[Ix];
930 const v2u16_t b = lut3d->tone_map[Ix + 1];
931
932 const pixel_t k = lerp(a.y, b.y, If);
933 const pixel_t bias = (1 << 15) - k;
934 const pixel_t scale = k / (pixel_t) (1 << 15);
935
936 rgb.x = lerp(a.x, b.x, If);
937 rgb.y = bias + scale * rgb.y;
938 rgb.z = bias + scale * rgb.z;
939
940 /* Re-scale to output LUT size */
941 rgb.x *= (OUTPUT_LUT_SIZE_I - 1) / (pixel_t) UINT16_MAX;
942 rgb.y *= (OUTPUT_LUT_SIZE_PT - 1) / (pixel_t) UINT16_MAX;
943 rgb.z *= (OUTPUT_LUT_SIZE_PT - 1) / (pixel_t) UINT16_MAX;
944
945 /* Trilinear interpolation */
946 const int lo0 = (int) rgb.x;
947 const int lo1 = (int) rgb.y;
948 const int lo2 = (int) rgb.z;
949
950 const int hi0 = FFMIN(lo0 + 1, OUTPUT_LUT_SIZE_I - 1);
951 const int hi1 = FFMIN(lo1 + 1, OUTPUT_LUT_SIZE_PT - 1);
952 const int hi2 = FFMIN(lo2 + 1, OUTPUT_LUT_SIZE_PT - 1);
953
954 const pixel_t w0 = rgb.x - lo0;
955 const vec3_t c000 = fn(vec3)(lut3d->output[lo2][lo1][lo0]);
956 const vec3_t c001 = fn(vec3)(lut3d->output[lo2][lo1][hi0]);
957 const vec3_t c00 = fn(lerp3)(c000, c001, w0);
958 const vec3_t c010 = fn(vec3)(lut3d->output[lo2][hi1][lo0]);
959 const vec3_t c011 = fn(vec3)(lut3d->output[lo2][hi1][hi0]);
960 const vec3_t c01 = fn(lerp3)(c010, c011, w0);
961 const vec3_t c100 = fn(vec3)(lut3d->output[hi2][lo1][lo0]);
962 const vec3_t c101 = fn(vec3)(lut3d->output[hi2][lo1][hi0]);
963 const vec3_t c10 = fn(lerp3)(c100, c101, w0);
964 const vec3_t c110 = fn(vec3)(lut3d->output[hi2][hi1][lo0]);
965 const vec3_t c111 = fn(vec3)(lut3d->output[hi2][hi1][hi0]);
966 const vec3_t c11 = fn(lerp3)(c110, c111, w0);
967
968 const pixel_t w1 = rgb.y - lo1;
969 const vec3_t c0 = fn(lerp3)(c00, c01, w1);
970 const vec3_t c1 = fn(lerp3)(c10, c11, w1);
971
972 const pixel_t w2 = rgb.z - lo2;
973 return fn(lerp3)(c0, c1, w2);
974}
975
976DECL_FUNC(lut3d, const SwsCompMask mask, const int dynamic)
977{
978 const SwsLut3D *restrict lut3d = impl->priv.ptr;
979
981 for (int i = 0; i < SWS_BLOCK_SIZE; i++) {
982 vec3_t c = { x[i], y[i], z[i] };
983 c = fn(lut3d_static)(lut3d, c);
984 if (dynamic)
985 c = fn(lut3d_dynamic)(lut3d, c);
986
987 x[i] = c.x;
988 y[i] = c.y;
989 z[i] = c.z;
990 }
991
992 CONTINUE(x, y, z, w);
993}
994#endif /* IS_FLOAT */
995
996SWS_FOR(PX, LUT_3D, DECL_IMPL, lut3d)
997SWS_FOR_STRUCT(PX, LUT_3D, DECL_ENTRY, .setup = fn(setup_lut3d) )
998
999#undef PIXEL_MAX
1000#undef PIXEL_SWAP
1001#undef pixel_t
1002#undef inter_t
1003#undef vec3_t
1004#undef PX
1005#undef px
#define fn(a)
#define ZERO
uint8_t ptrdiff_t const uint8_t ptrdiff_t int intptr_t intptr_t my
Definition dsp.h:57
uint8_t ptrdiff_t const uint8_t ptrdiff_t int intptr_t mx
Definition dsp.h:57
uint8_t ptrdiff_t const uint8_t ptrdiff_t int intptr_t intptr_t int int16_t * dst
Definition dsp.h:87
SwsAArch64OpImplParams params
Definition ops.c:51
static double val(void *priv, double ch)
Definition aeval.c:77
static double mz(int i, double w0, double r, double alpha)
Definition af_atilt.c:55
int32_t
#define av_assert2(cond)
assert() equivalent, that does lie in speed critical code.
Definition avassert.h:68
#define COPY(src, name)
static unsigned int BS_FUNC read_bit(BSCTX *bc)
Return one bit from the buffer.
#define MAX
Definition blend_modes.c:46
#define Y
Definition boxblur.h:37
byte swapping routines
#define i(width, name, range_min, range_max)
Definition cbs_h264.c:63
#define xs(width, name, var, subs,...)
Definition cbs_vp9.c:222
#define RSHIFT(a, b)
Definition common.h:56
#define NULL
Definition coverity.c:32
#define min(a, b)
#define max(a, b)
#define SCALE(c)
Definition dcadata.c:7338
#define ADD(a, b)
static void permute(int16_t dst[64], const int16_t src[64], enum idct_permutation_type perm_type)
Definition dct.c:158
static int unpack(const uint8_t *src, const uint8_t *src_end, uint8_t *dst, int width, int height)
Unpack buffer.
Definition eatgv.c:73
double value
Definition eval.c:102
#define X
Definition f_ebur128.c:157
#define AVERROR(e)
Definition error.h:45
void * av_memdup(const void *p, size_t size)
Duplicate a buffer with av_malloc().
Definition mem.c:302
int index
Definition gxfenc.c:90
int a
static const int weights[]
Definition hevc_pel.c:32
cl_device_type type
#define b
Definition input.c:43
static int linear(InterplayACMContext *s, unsigned ind, unsigned col)
static int zero(InterplayACMContext *s, unsigned ind, unsigned col)
static void scale(int *out, const int *in, const int w, const int h, const int shift)
Definition intra.c:278
#define W(a, i, v)
Definition jpegls.h:119
#define ONE
Definition jrevdct.c:137
unsigned offset
Definition libaomenc.c:763
#define av_always_inline
Definition attributes.h:72
@ SWS_FILTER_SCALE
14-bit coefficients are picked to fit comfortably within int16_t for efficient SIMD processing (e....
Definition filters.h:40
@ TONE_LUT_SIZE
Definition lut3d.h:40
@ OUTPUT_LUT_SIZE_I
Definition lut3d.h:46
@ INPUT_LUT_SIZE
Definition lut3d.h:36
@ OUTPUT_LUT_SIZE_PT
Definition lut3d.h:47
uint8_t w
Definition llvidencdsp.c:39
static const uint16_t mask[17]
Definition lzw.c:38
#define FFSWAP(type, a, b)
Definition macros.h:52
#define FFMIN(a, b)
Definition macros.h:49
#define FFMAX(a, b)
Definition macros.h:47
void * av_calloc(size_t nmemb, size_t size)
Definition mem.c:264
static const uint64_t c1
Definition murmur3.c:52
const char data[16]
Definition mxf.c:149
int ff_sws_setup_vec4(const SwsImplParams *params, SwsImplResult *out)
Definition ops_chain.c:200
int ff_sws_setup_scalar(const SwsImplParams *params, SwsImplResult *out)
Definition ops_chain.c:185
static void ff_op_priv_unref(SwsOpPriv *priv)
Definition ops_chain.h:149
static void ff_op_priv_free(SwsOpPriv *priv)
Definition ops_chain.h:144
#define av_malloc(s)
Definition ops_static.c:52
static double lerp(double a, double b, double x)
Definition perlin.c:75
#define MIN(a, b)
const void * av_refstruct_ref_c(const void *obj)
Analog of av_refstruct_ref(), but for constant objects.
Definition refstruct.c:149
void * av_refstruct_ref(void *obj)
Create a new reference to an object managed via this API, i.e.
Definition refstruct.c:140
const h264_weight_func weight
Represents a computed filter kernel.
Definition filters.h:85
Append a set of operations for applying a gamut/tone mapping 3D LUT to the pixels.
Definition lut3d.h:50
Copyright (C) 2026 Niklas Haas.
int32_t * in_offset_x
Pixel offset map; for horizontal scaling, in bytes.
ptrdiff_t in_stride[4]
Definition uops.h:264
SwsUOpParams par
Definition uops.h:269
SwsPixel mat4[4][5]
Definition uops.h:277
SwsPixel * ptr
Definition uops.h:274
union SwsUOp::@242237116251216327057105100216205033300341206345 data
pixel_t m[4][4]
Definition uops_tmpl.c:783
pixel_t k[4]
Definition uops_tmpl.c:784
Definition rpzaenc.c:60
#define stride
void(* filter)(uint8_t *src, ptrdiff_t stride, int qscale)
Definition h263dsp.c:29
#define src
Definition vp8dsp.c:248
static FILE * out
Definition movenc.c:55
#define height
Definition dsp.h:89
#define PIXEL_MAX
Definition tiny_ssim.c:40
int size
SwsDitherUOp dither
Definition uops.h:260
uint32_t u32[SWS_BLOCK_SIZE]
Definition uops_tmpl.h:46
float f32[SWS_BLOCK_SIZE]
Definition uops_tmpl.h:47
uint16_t u16[SWS_BLOCK_SIZE]
Definition uops_tmpl.h:45
int ff_sws_dither_height(const SwsDitherUOp *dither)
Computes (1 << size_log2) + MAX(y_offset).
Definition uops.c:232
SwsPixelType
Definition uops.h:39
@ SWS_PIXEL_F32
Definition uops.h:44
#define SWS_UOP_MOVE_MAX
Definition uops.h:206
#define SWS_COMP_ELEMS(N)
Definition uops.h:100
uint8_t SwsCompMask
Bit-mask of components.
Definition uops.h:93
#define SWS_FOR(TYPE, UOP, MACRO,...)
Definition uops_macros.h:17
#define SWS_FOR_STRUCT(TYPE, UOP, MACRO,...)
Definition uops_macros.h:19
#define DECL_CAST(DST, dst)
Definition uops_tmpl.c:418
#define LIN_ROW(I, var)
#define DECL_IMPL_WRITE(...)
Definition uops_tmpl.h:133
#define DECL_WRITE(NAME,...)
Definition uops_tmpl.h:99
#define DECL_IMPL_READ(...)
Definition uops_tmpl.h:128
#define CONTINUE(...)
Definition uops_tmpl.h:107
#define Z
Definition uops_tmpl.h:83
#define DECL_FUNC(NAME,...)
Definition uops_tmpl.h:87
#define bump_ptr(ptr, bump)
Definition uops_tmpl.h:78
#define DECL_IMPL(FUNC, NAME, TYPE, UOP,...)
Definition uops_tmpl.h:119
#define SIZEOF_BLOCK
Definition uops_tmpl.h:50
#define SWS_BLOCK_SIZE
Copyright (C) 2026 Niklas Haas.
Definition uops_tmpl.h:40
#define DECL_READ(NAME,...)
Definition uops_tmpl.h:94
#define SWS_LOOP
Definition uops_tmpl.h:68
#define DECL_ENTRY(SETUP, NAME,...)
Definition uops_tmpl.h:139
#define DECL_SETUP(NAME, PARAMS, OUT)
Definition uops_tmpl.h:114
static const uint16_t dither[8][8]
Definition vf_gradfun.c:46
#define LINEAR
static void copy(const float *p1, float *p2, const int length)
uint8_t base
Definition vp3data.h:128
static int bias(int x, int c)
Definition vqcdec.c:115
static double c[64]
#define CLEAR(destin)
Definition wavpackenc.c:50
static int setup_linear(const SwsImplParams *params, SwsImplResult *out)
Definition ops.c:280
static int setup_filter_v(const SwsImplParams *params, SwsImplResult *out)
Definition ops.c:46
static int setup_dither(const SwsImplParams *params, SwsImplResult *out)
Definition ops.c:273
static int setup_filter_h(const SwsImplParams *params, SwsImplResult *out)
Definition ops.c:76