FFmpeg
Loading...
Searching...
No Matches
ops_optimizer.c
Go to the documentation of this file.
1/**
2 * Copyright (C) 2025 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
22#include "libavutil/avassert.h"
23#include "libavutil/bswap.h"
24#include "libavutil/rational.h"
25
26#include "ops.h"
27#include "ops_internal.h"
28
29#define RET(x) \
30 do { \
31 if ((ret = (x)) < 0) \
32 return ret; \
33 } while (0)
34
35/**
36 * Try to commute a clear op with the next operation. Makes any adjustments
37 * to the operations as needed, but does not perform the actual commutation.
38 *
39 * Returns whether successful.
40 */
41static bool op_commute_clear(SwsOp *op, SwsOp *next)
42{
44 switch (next->op) {
45 case SWS_OP_CONVERT:
46 op->type = next->convert.to;
48 case SWS_OP_LSHIFT:
49 case SWS_OP_RSHIFT:
50 case SWS_OP_DITHER:
51 case SWS_OP_MIN:
52 case SWS_OP_MAX:
53 case SWS_OP_SCALE:
54 case SWS_OP_READ:
55 ff_sws_apply_op_q(next, op->clear.value);
56 return true;
57 case SWS_OP_FILTER_H:
58 case SWS_OP_FILTER_V:
59 op->type = next->filter.type;
60 return true;
61 case SWS_OP_SWIZZLE:
62 ff_sws_comp_mask_swizzle(&op->clear.mask, &next->swizzle);
63 ff_sws_apply_op_q(next, op->clear.value);
64 return true;
66 switch (next->type) {
67 case SWS_PIXEL_U16:
68 case SWS_PIXEL_U32:
69 ff_sws_apply_op_q(next, op->clear.value); /* always representable */
70 return true;
71 default:
72 return false;
73 }
74 case SWS_OP_INVALID:
75 case SWS_OP_WRITE:
76 case SWS_OP_LINEAR:
77 case SWS_OP_PACK:
78 case SWS_OP_UNPACK:
79 case SWS_OP_CLEAR:
80 case SWS_OP_LUT_3D:
81 return false;
82 case SWS_OP_TYPE_NB:
83 break;
84 }
85
86 av_unreachable("Invalid operation type!");
87 return false;
88}
89
90 /**
91 * Try to commute a swizzle op with the next operation. Makes any adjustments
92 * to the operations as needed, but does not perform the actual commutation.
93 *
94 * Returns whether successful.
95 */
96static bool op_commute_swizzle(SwsOp *op, SwsOp *next)
97{
98 bool seen[4] = {0};
99
101 switch (next->op) {
102 case SWS_OP_CONVERT:
103 op->type = next->convert.to;
106 case SWS_OP_LSHIFT:
107 case SWS_OP_RSHIFT:
108 case SWS_OP_SCALE:
109 return true;
110 case SWS_OP_FILTER_H:
111 case SWS_OP_FILTER_V:
112 op->type = next->filter.type;
113 return true;
114
115 /**
116 * We can commute per-channel ops only if the per-channel constants are the
117 * same for all duplicated channels; e.g.:
118 * SWIZZLE {0, 0, 0, 3}
119 * NEXT {x, x, x, w}
120 * ->
121 * NEXT {x, _, _, w}
122 * SWIZZLE {0, 0, 0, 3}
123 */
124 case SWS_OP_MIN:
125 case SWS_OP_MAX: {
126 const SwsClampOp c = next->clamp;
127 for (int i = 0; i < 4; i++) {
128 if (!SWS_OP_NEEDED(op, i))
129 continue;
130 const int j = op->swizzle.in[i];
131 if (seen[j] && av_cmp_q64(next->clamp.limit[j], c.limit[i]))
132 return false;
133 next->clamp.limit[j] = c.limit[i];
134 seen[j] = true;
135 }
136 return true;
137 }
138
139 case SWS_OP_DITHER: {
140 const SwsDitherOp d = next->dither;
141 for (int i = 0; i < 4; i++) {
142 if (!SWS_OP_NEEDED(op, i))
143 continue;
144 const int j = op->swizzle.in[i];
145 if (seen[j] && next->dither.y_offset[j] != d.y_offset[i])
146 return false;
147 next->dither.y_offset[j] = d.y_offset[i];
148 seen[j] = true;
149 }
150 return true;
151 }
152
153 case SWS_OP_INVALID:
154 case SWS_OP_READ:
155 case SWS_OP_WRITE:
156 case SWS_OP_SWIZZLE:
157 case SWS_OP_CLEAR:
158 case SWS_OP_LINEAR:
159 case SWS_OP_PACK:
160 case SWS_OP_UNPACK:
161 case SWS_OP_LUT_3D:
162 return false;
163 case SWS_OP_TYPE_NB:
164 break;
165 }
166
167 av_unreachable("Invalid operation type!");
168 return false;
169}
170
171/**
172 * Try to commute a filter op with the previous operation. Makes any
173 * adjustments to the operations as needed, but does not perform the actual
174 * commutation.
175 *
176 * Returns whether successful.
177 */
178static bool op_commute_filter(SwsOp *op, SwsOp *prev)
179{
180 av_assert0(!ff_sws_pixel_type_is_int(op->filter.type));
181
182 switch (prev->op) {
183 case SWS_OP_SWIZZLE:
184 case SWS_OP_SCALE:
185 case SWS_OP_LINEAR:
186 case SWS_OP_DITHER:
187 prev->type = op->filter.type;
188 return true;
189 case SWS_OP_CONVERT:
190 case SWS_OP_INVALID:
191 case SWS_OP_READ:
192 case SWS_OP_WRITE:
194 case SWS_OP_UNPACK:
195 case SWS_OP_PACK:
196 case SWS_OP_LSHIFT:
197 case SWS_OP_RSHIFT:
198 case SWS_OP_CLEAR:
199 case SWS_OP_MIN:
200 case SWS_OP_MAX:
201 case SWS_OP_FILTER_H:
202 case SWS_OP_FILTER_V:
203 case SWS_OP_LUT_3D:
204 return false;
205 case SWS_OP_TYPE_NB:
206 break;
207 }
208
209 av_unreachable("Invalid operation type!");
210 return false;
211}
212
213/* returns log2(x) only if x is a power of two, or 0 otherwise */
214static int exact_log2(const int x)
215{
216 int p;
217 if (x <= 0)
218 return 0;
219 p = av_log2(x);
220 return (1 << p) == x ? p : 0;
221}
222
223static int exact_log2_q64(const AVRational64 x)
224{
225 if (x.den == 1)
226 return exact_log2(x.num);
227 else if (x.num == 1)
228 return -exact_log2(x.den);
229 else
230 return 0;
231}
232
233/**
234 * If a linear operation can be reduced to a scalar multiplication, returns
235 * the corresponding scaling factor, or 0 otherwise.
236 */
237static bool extract_scalar(const SwsLinearOp *c,
238 const SwsComps *comps, const SwsComps *prev,
239 SwsScaleOp *out_scale)
240{
241 SwsScaleOp scale = {0};
242
243 /* There are components not on the main diagonal */
245 return false;
246
247 for (int i = 0; i < 4; i++) {
248 const AVRational64 s = c->m[i][i];
249 if ((prev->flags[i] & SWS_COMP_ZERO) ||
250 (comps->flags[i] & SWS_COMP_GARBAGE))
251 continue;
252 if (scale.factor.den && av_cmp_q64(s, scale.factor))
253 return false;
254 scale.factor = s;
255 }
256
257 if (scale.factor.den)
258 *out_scale = scale;
259 return scale.factor.den;
260}
261
262/* Extracts an integer clear operation (subset) from the given linear op. */
263static bool extract_constant_rows(SwsLinearOp *c, const SwsComps *prev,
264 SwsClearOp *out_clear)
265{
266 const uint32_t mask = ff_sws_linear_mask(c);
267 SwsClearOp clear = {0};
268 bool ret = false;
269
270 for (int i = 0; i < 4; i++) {
271 bool const_row = c->m[i][4].den == 1; /* offset is integer */
272 for (int j = 0; j < 4; j++) {
273 const_row &= c->m[i][j].num == 0 || /* scalar is zero */
274 (prev->flags[j] & SWS_COMP_ZERO); /* input is zero */
275 }
276 if (const_row && (mask & SWS_MASK_ROW(i))) {
277 clear.mask |= SWS_COMP(i);
278 clear.value[i] = c->m[i][4];
279 for (int j = 0; j < 5; j++)
280 c->m[i][j] = Q(i == j);
281 ret = true;
282 }
283 }
284
285 if (ret)
286 *out_clear = clear;
287 return ret;
288}
289
290/* Unswizzle a linear operation by aligning single-input rows with
291 * their corresponding diagonal */
292static bool extract_swizzle(SwsLinearOp *op, const SwsComps *prev,
293 SwsSwizzleOp *out_swiz)
294{
295 SwsSwizzleOp swiz = SWS_SWIZZLE(0, 1, 2, 3);
296 SwsLinearOp c = *op;
297
298 /* Find non-zero coefficients in the main 4x4 matrix */
299 uint32_t nonzero = 0;
300 for (int i = 0; i < 4; i++) {
301 for (int j = 0; j < 4; j++) {
302 if (!c.m[i][j].num || (prev->flags[j] & SWS_COMP_ZERO))
303 continue;
304 nonzero |= SWS_MASK(i, j);
305 }
306 }
307
308 /* If a value is unique in its row and the target column is
309 * empty, move it there and update the input swizzle */
310 for (int i = 0; i < 4; i++) {
311 if (nonzero & SWS_MASK_COL(i))
312 continue; /* target column is not empty */
313 for (int j = 0; j < 4; j++) {
314 if ((nonzero & SWS_MASK_ROW(i)) == SWS_MASK(i, j)) {
315 /* Move coefficient to the diagonal */
316 c.m[i][i] = c.m[i][j];
317 c.m[i][j] = Q(0);
318 swiz.in[i] = j;
319 break;
320 }
321 }
322 }
323
324 if (swiz.mask == SWS_SWIZZLE(0, 1, 2, 3).mask)
325 return false; /* no swizzle was identified */
326
327 *out_swiz = swiz;
328 *op = c;
329 return true;
330}
331
332static int op_result_is_exact(const SwsOp *op)
333{
334 for (int i = 0; i < 4; i++) {
335 if (SWS_OP_NEEDED(op, i) && !(op->comps.flags[i] & SWS_COMP_EXACT))
336 return false;
337 }
338
339 return true;
340}
341
343{
344 int ret;
345
346retry:
348
349 /* Try to push filters towards the input; do this first to unblock
350 * in-place optimizations like linear op fusion */
351 for (int n = 1; n < ops->num_ops; n++) {
352 SwsOp *op = &ops->ops[n];
353 SwsOp *prev = &ops->ops[n - 1];
354
355 switch (op->op) {
356 case SWS_OP_FILTER_H:
357 case SWS_OP_FILTER_V:
358 if (op_commute_filter(op, prev)) {
359 FFSWAP(SwsOp, *op, *prev);
360 goto retry;
361 }
362
363 /* Merge filter with prior conversion */
364 if (prev->op == SWS_OP_CONVERT && !prev->convert.expand) {
365 int size_from = ff_sws_pixel_type_size(prev->type);
366 int size_to = ff_sws_pixel_type_size(op->type);
367 av_assert1(prev->convert.to == op->type);
368 if (size_from < size_to) {
369 op->type = prev->type;
370 ff_sws_op_list_remove_at(ops, n - 1, 1);
371 goto retry;
372 }
373 }
374 break;
375 }
376 }
377
378 /* Apply all in-place optimizations (that do not re-order the list) */
379 for (int n = 0; n < ops->num_ops; n++) {
380 SwsOp dummy = {0};
381 SwsOp *op = &ops->ops[n];
382 SwsOp *prev = n ? &ops->ops[n - 1] : &dummy;
383 SwsOp *next = n + 1 < ops->num_ops ? &ops->ops[n + 1] : &dummy;
384
385 /* common helper variable */
386 const SwsCompMask needed = ff_sws_comp_mask_needed(op);
387 bool noop = true;
388
389 if (!needed && op->op != SWS_OP_WRITE) {
390 /* Remove any operation whose output is not needed */
391 ff_sws_op_list_remove_at(ops, n, 1);
392 goto retry;
393 }
394
395 switch (op->op) {
396 case SWS_OP_READ:
397 /* "Compress" planar reads where not all components are needed */
398 if (op->rw.mode == SWS_RW_PLANAR) {
399 SwsSwizzleOp swiz = SWS_SWIZZLE(0, 1, 2, 3);
400 int nb_planes = 0;
401 for (int i = 0; i < op->rw.elems; i++) {
402 if (!SWS_OP_NEEDED(op, i)) {
403 swiz.in[i] = 3 - (i - nb_planes); /* map to unused plane */
404 continue;
405 }
406
407 const int idx = nb_planes++;
408 av_assert1(idx <= i);
409 ops->plane_src[idx] = ops->plane_src[i];
410 swiz.in[i] = idx;
411 }
412
413 if (nb_planes < op->rw.elems) {
414 op->rw.elems = nb_planes;
415 RET(ff_sws_op_list_insert_at(ops, n + 1, &(SwsOp) {
416 .op = SWS_OP_SWIZZLE,
417 .type = op->rw.filter.op ? op->rw.filter.type : op->type,
418 .swizzle = swiz,
419 }));
420 goto retry;
421 }
422 }
423 break;
424
426 /* Redundant (double) swap */
427 if (next->op == SWS_OP_SWAP_BYTES) {
428 ff_sws_op_list_remove_at(ops, n, 2);
429 goto retry;
430 }
431 break;
432
433 case SWS_OP_UNPACK:
434 /* Redundant unpack+pack */
435 if (next->op == SWS_OP_PACK && next->type == op->type &&
436 next->pack.pattern[0] == op->pack.pattern[0] &&
437 next->pack.pattern[1] == op->pack.pattern[1] &&
438 next->pack.pattern[2] == op->pack.pattern[2] &&
439 next->pack.pattern[3] == op->pack.pattern[3])
440 {
441 ff_sws_op_list_remove_at(ops, n, 2);
442 goto retry;
443 }
444 break;
445
446 case SWS_OP_LSHIFT:
447 case SWS_OP_RSHIFT:
448 /* Two shifts in the same direction */
449 if (next->op == op->op) {
450 op->shift.amount += next->shift.amount;
451 ff_sws_op_list_remove_at(ops, n + 1, 1);
452 goto retry;
453 }
454
455 /* No-op shift */
456 if (!op->shift.amount) {
457 ff_sws_op_list_remove_at(ops, n, 1);
458 goto retry;
459 }
460 break;
461
462 case SWS_OP_CLEAR:
463 for (int i = 0; i < 4; i++) {
464 if (!SWS_COMP_TEST(op->clear.mask, i))
465 continue;
466
467 if ((prev->comps.flags[i] & SWS_COMP_ZERO) &&
468 !(prev->comps.flags[i] & SWS_COMP_GARBAGE) &&
469 op->clear.value[i].num == 0)
470 {
471 /* Redundant clear-to-zero of zero component */
472 op->clear.mask ^= SWS_COMP(i);
473 } else if (!SWS_OP_NEEDED(op, i)) {
474 /* Unnecessary clear of unused component */
475 op->clear.mask ^= SWS_COMP(i);
476 } else {
477 noop = false;
478 }
479 }
480
481 if (noop) {
482 ff_sws_op_list_remove_at(ops, n, 1);
483 goto retry;
484 }
485
486 /* Transitive clear */
487 if (next->op == SWS_OP_CLEAR) {
488 for (int i = 0; i < 4; i++) {
489 if (SWS_COMP_TEST(next->clear.mask, i))
490 op->clear.value[i] = next->clear.value[i];
491 }
492 op->clear.mask |= next->clear.mask;
493 ff_sws_op_list_remove_at(ops, n + 1, 1);
494 goto retry;
495 }
496 break;
497
498 case SWS_OP_SWIZZLE:
499 for (int i = 0; i < 4; i++) {
500 if (!SWS_OP_NEEDED(op, i))
501 continue;
502 if (op->swizzle.in[i] != i)
503 noop = false;
504 }
505
506 /* Identity swizzle */
507 if (noop) {
508 ff_sws_op_list_remove_at(ops, n, 1);
509 goto retry;
510 }
511
512 /* Transitive swizzle */
513 if (next->op == SWS_OP_SWIZZLE) {
514 const SwsSwizzleOp orig = op->swizzle;
515 for (int i = 0; i < 4; i++)
516 op->swizzle.in[i] = orig.in[next->swizzle.in[i]];
517 ff_sws_op_list_remove_at(ops, n + 1, 1);
518 goto retry;
519 }
520
521 /* Swizzle planes instead of components, if possible */
522 if (prev->op == SWS_OP_READ && prev->rw.mode == SWS_RW_PLANAR) {
523 for (int dst = 0; dst < prev->rw.elems; dst++) {
524 const int src = op->swizzle.in[dst];
525 if (src > dst && src < prev->rw.elems) {
526 FFSWAP(int, ops->plane_src[dst], ops->plane_src[src]);
527 for (int i = dst; i < 4; i++) {
528 if (op->swizzle.in[i] == dst)
529 op->swizzle.in[i] = src;
530 else if (op->swizzle.in[i] == src)
531 op->swizzle.in[i] = dst;
532 }
533 goto retry;
534 }
535 }
536 }
537
538 if (next->op == SWS_OP_WRITE && next->rw.mode == SWS_RW_PLANAR) {
539 for (int dst = 0; dst < next->rw.elems; dst++) {
540 const int src = op->swizzle.in[dst];
541 if (src > dst && src < next->rw.elems) {
542 FFSWAP(int, ops->plane_dst[dst], ops->plane_dst[src]);
543 FFSWAP(int, op->swizzle.in[dst], op->swizzle.in[src]);
544 goto retry;
545 }
546 }
547 }
548 break;
549
550 case SWS_OP_CONVERT:
551 /* No-op conversion */
552 if (op->type == op->convert.to) {
553 ff_sws_op_list_remove_at(ops, n, 1);
554 goto retry;
555 }
556
557 /* Transitive conversion */
558 if (next->op == SWS_OP_CONVERT &&
559 op->convert.expand == next->convert.expand)
560 {
561 av_assert1(op->convert.to == next->type);
562 op->convert.to = next->convert.to;
563 ff_sws_op_list_remove_at(ops, n + 1, 1);
564 goto retry;
565 }
566
567 /* Conversion followed by integer expansion */
568 if (next->op == SWS_OP_SCALE && !op->convert.expand &&
570 ff_sws_pixel_type_is_int(op->convert.to) &&
571 !av_cmp_q64(next->scale.factor,
572 ff_sws_pixel_expand(op->type, op->convert.to)))
573 {
574 op->convert.expand = true;
575 ff_sws_op_list_remove_at(ops, n + 1, 1);
576 goto retry;
577 }
578 break;
579
580 case SWS_OP_MIN:
581 for (int i = 0; i < 4; i++) {
582 if (!SWS_OP_NEEDED(op, i) || !op->clamp.limit[i].den)
583 continue;
584 if (av_cmp_q64(op->clamp.limit[i], prev->comps.max[i]) >= 0)
585 op->clamp.limit[i] = (AVRational64) {0}; /* no-op */
586 else
587 noop = false;
588 }
589
590 if (noop) {
591 ff_sws_op_list_remove_at(ops, n, 1);
592 goto retry;
593 }
594 break;
595
596 case SWS_OP_MAX:
597 for (int i = 0; i < 4; i++) {
598 if (!SWS_OP_NEEDED(op, i) || !op->clamp.limit[i].den)
599 continue;
600 if (av_cmp_q64(prev->comps.min[i], op->clamp.limit[i]) >= 0)
601 op->clamp.limit[i] = (AVRational64) {0};
602 else
603 noop = false;
604 }
605
606 if (noop) {
607 ff_sws_op_list_remove_at(ops, n, 1);
608 goto retry;
609 }
610 break;
611
612 case SWS_OP_DITHER:
613 for (int i = 0; i < 4; i++) {
614 if (op->dither.y_offset[i] < 0)
615 continue;
616 if (!SWS_OP_NEEDED(op, i) || (prev->comps.flags[i] & SWS_COMP_EXACT)) {
617 op->dither.y_offset[i] = -1; /* unnecessary dither */
618 goto retry;
619 } else {
620 noop = false;
621 }
622 }
623
624 if (noop) {
625 ff_sws_op_list_remove_at(ops, n, 1);
626 goto retry;
627 }
628 break;
629
630 case SWS_OP_LINEAR: {
631 const uint32_t mask = ff_sws_linear_mask(&op->lin);
632 SwsSwizzleOp swizzle;
633 SwsClearOp clear;
635
636 /* No-op (identity) linear operation */
637 if (!mask) {
638 ff_sws_op_list_remove_at(ops, n, 1);
639 goto retry;
640 }
641
642 if (next->op == SWS_OP_LINEAR) {
643 /* 5x5 matrix multiplication after appending [ 0 0 0 0 1 ] */
644 const SwsLinearOp m1 = op->lin;
645 const SwsLinearOp m2 = next->lin;
646 for (int i = 0; i < 4; i++) {
647 for (int j = 0; j < 5; j++) {
648 AVRational64 sum = Q(0);
649 for (int k = 0; k < 4; k++)
650 sum = av_add_q64(sum, av_mul_q64(m2.m[i][k], m1.m[k][j]));
651 if (j == 4) /* m1.m[4][j] == 1 */
652 sum = av_add_q64(sum, m2.m[i][4]);
653 op->lin.m[i][j] = sum;
654 }
655 }
656 ff_sws_op_list_remove_at(ops, n + 1, 1);
657 goto retry;
658 }
659
660 /* Optimize away zero columns */
661 for (int j = 0; j < 4; j++) {
662 const uint32_t col = SWS_MASK_COL(j);
663 if (!(prev->comps.flags[j] & SWS_COMP_ZERO) || !(mask & col))
664 continue;
665 for (int i = 0; i < 4; i++)
666 op->lin.m[i][j] = Q(i == j);
667 goto retry;
668 }
669
670 /* Optimize away unused rows */
671 for (int i = 0; i < 4; i++) {
672 const uint32_t row = SWS_MASK_ROW(i);
673 if (SWS_OP_NEEDED(op, i) || !(mask & row))
674 continue;
675 for (int j = 0; j < 5; j++)
676 op->lin.m[i][j] = Q(i == j);
677 goto retry;
678 }
679
680 /* Convert constant rows to explicit clear instruction */
681 if (extract_constant_rows(&op->lin, &prev->comps, &clear)) {
682 RET(ff_sws_op_list_insert_at(ops, n + 1, &(SwsOp) {
683 .op = SWS_OP_CLEAR,
684 .type = op->type,
685 .comps = op->comps,
686 .clear = clear,
687 }));
688 goto retry;
689 }
690
691 /* Multiplication by scalar constant */
692 if (extract_scalar(&op->lin, &op->comps, &prev->comps, &scale)) {
693 op->op = SWS_OP_SCALE;
694 op->scale = scale;
695 goto retry;
696 }
697
698 /* Swizzle by fixed pattern */
699 if (extract_swizzle(&op->lin, &prev->comps, &swizzle)) {
701 .op = SWS_OP_SWIZZLE,
702 .type = op->type,
703 .swizzle = swizzle,
704 }));
705 goto retry;
706 }
707 break;
708 }
709
710 case SWS_OP_SCALE: {
711 const int factor2 = exact_log2_q64(op->scale.factor);
712
713 /* No-op scaling */
714 if (op->scale.factor.num == 1 && op->scale.factor.den == 1) {
715 ff_sws_op_list_remove_at(ops, n, 1);
716 goto retry;
717 }
718
719 /* Merge consecutive scaling operations */
720 if (next->op == SWS_OP_SCALE) {
721 op->scale.factor = av_mul_q64(op->scale.factor, next->scale.factor);
722 ff_sws_op_list_remove_at(ops, n + 1, 1);
723 goto retry;
724 }
725
726 /* Scaling by exact power of two */
727 if (factor2 && ff_sws_pixel_type_is_int(op->type)) {
728 op->op = factor2 > 0 ? SWS_OP_LSHIFT : SWS_OP_RSHIFT;
729 op->shift.amount = FFABS(factor2);
730 goto retry;
731 }
732 break;
733 }
734
735 case SWS_OP_FILTER_H:
736 case SWS_OP_FILTER_V:
737 /* Merge with prior simple planar read */
738 if (prev->op == SWS_OP_READ && !prev->rw.filter.op &&
739 prev->rw.mode == SWS_RW_PLANAR && !prev->rw.frac) {
740 prev->rw.filter.op = op->op;
741 prev->rw.filter.kernel = av_refstruct_ref(op->filter.kernel);
742 prev->rw.filter.type = op->filter.type;
743 ff_sws_op_list_remove_at(ops, n, 1);
744 goto retry;
745 }
746 break;
747
748 case SWS_OP_LUT_3D:
749 /* Eliminate unnecessary 3DLUT */
750 if (!(needed & SWS_COMP_ELEMS(3))) {
751 ff_sws_op_list_remove_at(ops, n, 1);
752 goto retry;
753 }
754 break;
755 }
756 }
757
758 /* Push clears to the back to void any unused components */
759 for (int n = 0; n < ops->num_ops - 1; n++) {
760 SwsOp *op = &ops->ops[n];
761 SwsOp *next = &ops->ops[n + 1];
762
763 switch (op->op) {
764 case SWS_OP_CLEAR:
765 if (op_commute_clear(op, next)) {
766 FFSWAP(SwsOp, *op, *next);
767 goto retry;
768 }
769 break;
770 }
771 }
772
773 /* Apply any remaining preferential re-ordering optimizations; do these
774 * last because they are more likely to block other optimizations if done
775 * too aggressively */
776 for (int n = 0; n < ops->num_ops - 1; n++) {
777 SwsOp *op = &ops->ops[n];
778 SwsOp *next = &ops->ops[n + 1];
779
780 switch (op->op) {
781 case SWS_OP_SWIZZLE: {
782 /* Try to push swizzles towards the output */
783 if (op_commute_swizzle(op, next)) {
784 FFSWAP(SwsOp, *op, *next);
785 goto retry;
786 }
787 break;
788 }
789
790 case SWS_OP_SCALE:
791 /* Exact integer multiplication */
792 if (op->scale.factor.den == 1 && next->op == SWS_OP_CONVERT &&
795 {
796 op->type = next->convert.to;
797 FFSWAP(SwsOp, *op, *next);
798 goto retry;
799 }
800 break;
801 }
802 }
803
804 return 0;
805}
806
808{
809 SwsSwizzleOp swiz = SWS_SWIZZLE(0, 1, 2, 3);
810 SwsOp *write = &ops->ops[ops->num_ops - 1];
811 av_assert0(write->op == SWS_OP_WRITE);
812
813 write->rw.elems = 0;
814 for (int src = 0; src < 4; src++) {
815 if (!SWS_COMP_TEST(planes, src))
816 continue; /* plane not selected */
817 const int dst = write->rw.elems++;
818 av_assert2(src >= dst);
819 swiz.in[dst] = src;
820 FFSWAP(int, ops->plane_dst[dst], ops->plane_dst[src]);
821 }
822
823 /* Insert swizzle to select desired planes */
824 int ret = ff_sws_op_list_insert_at(ops, ops->num_ops - 1, &(SwsOp) {
825 .op = SWS_OP_SWIZZLE,
826 .type = write->type,
827 .swizzle = swiz,
828 });
829 if (ret < 0)
830 return ret;
831
832 /* The optimizer will take care of the rest */
833 return ff_sws_op_list_optimize(ops);
834}
835
837{
838 const SwsOp *write = ff_sws_op_list_output(ops1);
839 if (!write || write->rw.mode != SWS_RW_PLANAR) {
840 *out_ops2 = NULL;
841 return 0;
842 }
843
844 const SwsCompMask full = SWS_COMP_ELEMS(write->rw.elems);
845 const SwsCompMask mask1 = planes & full;
846 const SwsCompMask mask2 = full ^ mask1;
847 if (!mask1 || !mask2) {
848 /* Nothing to filter */
849 *out_ops2 = NULL;
850 return 0;
851 }
852
854 if (!ops2)
855 return AVERROR(ENOMEM);
856
857 int ret;
858 if ((ret = select_planes(ops1, mask1)) < 0 ||
859 (ret = select_planes(ops2, mask2)) < 0)
860 {
861 ff_sws_op_list_free(&ops2);
862 return ret;
863 }
864
865 *out_ops2 = ops2;
866 return 0;
867}
868
869int ff_sws_shuffle_mask(const SwsUOp *uop, int8_t shuffle[], int size)
870{
871 const SwsShuffleUOp *par = &uop->par.shuffle;
873 av_assert1(par->write_size <= sizeof(uop->data.shuffle.mask));
874 av_assert1(size <= INT8_MAX);
875
876 const int num_groups = size / FFMAX(par->read_size, par->write_size);
877 if (!num_groups)
878 return AVERROR(EINVAL);
879
880 memset(shuffle, 0, size);
881 for (int n = 0; n < num_groups; n++) {
882 const int base_in = n * par->read_size;
883 const int base_out = n * par->write_size;
884 for (int i = 0; i < par->write_size; i++) {
885 const int8_t idx = uop->data.shuffle.mask[i];
886 shuffle[base_out + i] = idx + (idx >= 0) * base_in;
887 }
888 }
889
890 return num_groups;
891}
892
894{
895 switch (ff_sws_pixel_type_size(type)) {
896 case 1: return true;
897 case 2: return val.u16 == val.u8 * 0x101ul;
898 case 4: return val.u32 == val.u8 * 0x1010101ul;
899 default: break;
900 }
901
902 av_unreachable("Invalid pixel type!");
903 return false;
904}
905
906static int solve_shuffle(const SwsUOpList *const uops, SwsUOp *out)
907{
908 if (!uops->num_ops)
909 return AVERROR(EINVAL);
910 const SwsUOp *read = &uops->ops[0];
911 switch (read->uop) {
913 break;
915 if (read->mask != SWS_COMP_ELEMS(1))
916 return AVERROR(ENOTSUP);
917 break;
918 default:
919 return AVERROR(ENOTSUP);
920 }
921
922 const int read_size = ff_sws_pixel_type_size(read->type);
923 uint32_t mask[4] = {0};
924 int clear_val = -1;
925 int read_elems = 0;
926 for (int i = 0; i < 4; i++) {
927 if (SWS_COMP_TEST(read->mask, i)) {
928 mask[i] = 0x01010101 * i * read_size + 0x03020100;
929 read_elems++;
930 }
931 }
932
933 for (int opidx = 1; opidx < uops->num_ops; opidx++) {
934 const SwsUOp *uop = &uops->ops[opidx];
935 const SwsUOpParams *par = &uop->par;
936 switch (uop->uop) {
937 case SWS_UOP_COPY:
938 case SWS_UOP_PERMUTE: {
939 uint32_t tmp;
940 for (int i = 0; i < par->move.num_moves; i++) {
941 const int dst_idx = par->move.dst[i];
942 const int src_idx = par->move.src[i];
943 uint32_t *src = src_idx < 0 ? &tmp : &mask[src_idx];
944 uint32_t *dst = dst_idx < 0 ? &tmp : &mask[dst_idx];
945 *dst = *src;
946 }
947 break;
948 }
949
951 for (int i = 0; i < 4; i++) {
952 switch (ff_sws_pixel_type_size(uop->type)) {
953 case 2: mask[i] = av_bswap16(mask[i]); break;
954 case 4: mask[i] = av_bswap32(mask[i]); break;
955 }
956 }
957 break;
958
959 case SWS_UOP_CLEAR:
960 for (int i = 0; i < 4; i++) {
961 if (!SWS_COMP_TEST(uop->mask, i))
962 continue;
963 SwsPixel val = uop->data.vec4[i];
964 if (!pixel_is_repeating(uop->type, val) ||
965 (clear_val >= 0 && clear_val != val.u8))
966 return AVERROR(ENOTSUP); /* would require different bytes */
967 mask[i] = 0xFFFFFFFFul; /* (uint8_t[4]) { -1, -1, -1, -1 } */
968 clear_val = val.u8;
969 }
970 break;
971
974 for (int i = 0; i < 4; i++)
975 mask[i] = 0x01010101 * (mask[i] & 0xFF);
976 break;
977
979 if (uop->mask != SWS_COMP_ELEMS(1))
980 return AVERROR(ENOTSUP);
983 const int write_elems = av_popcount(uop->mask);
984 const int write_size = ff_sws_pixel_type_size(uop->type);
985 *out = (SwsUOp) {
986 .uop = SWS_UOP_RW_SHUFFLE,
987 .type = SWS_PIXEL_U8,
988 .mask = SWS_COMP_ELEMS(1), /* single plane for now */
989 };
990
991 SwsShuffleUOp *par = &out->par.shuffle;
992 SwsShuffleMask *data = &out->data.shuffle;
993 *par = (SwsShuffleUOp) {
994 .read_size = read_elems * read_size,
995 .write_size = write_elems * write_size,
996 .clear_value = clear_val >= 0 ? clear_val : 0,
997 };
998
999 /* Generate baseline shuffle for a single pixel */
1000 data->pixels = 1;
1001 for (int i = 0; i < write_elems; i++) {
1002 const int offset = i * write_size;
1003 for (int b = 0; b < write_size; b++)
1004 data->mask[offset + b] = mask[i] >> (b * 8);
1005 }
1006
1007 /* Expand as many times as needed to round up to the size of the
1008 * shuffle uop data mask */
1009 int8_t tmp[FF_ARRAY_ELEMS(data->mask)];
1010 const int num_groups = ff_sws_shuffle_mask(out, tmp, sizeof(tmp));
1011 if (num_groups < 0)
1012 return num_groups;
1013 memcpy(data->mask, tmp, sizeof(tmp));
1014 par->read_size *= num_groups;
1015 par->write_size *= num_groups;
1016 data->pixels = num_groups;
1017 return 0;
1018 }
1019
1020 default:
1021 return AVERROR(ENOTSUP);
1022 }
1023 }
1024
1025 return AVERROR(EINVAL);
1026}
1027
1029{
1030 /* Try promoting the entire uop list to a packed shuffle operation */
1031 if (flags & SWS_UOP_FLAG_PSHUFB) {
1033 int ret = solve_shuffle(uops, &shuffle);
1034 if (ret >= 0) {
1035 ff_sws_uop_list_remove_at(uops, 0, uops->num_ops);
1036 return ff_sws_uop_list_append(uops, &shuffle);
1037 } else if (ret < 0 && ret != AVERROR(ENOTSUP)) {
1038 return ret;
1039 }
1040 }
1041
1042#if 0
1043 static const SwsUOp dummy = {0};
1044
1045retry:
1046 for (int i = 0; i < uops->num_ops; i++) {
1047 const SwsUOp *next = i < uops->num_ops - 1 ? &uops->ops[i + 1] : &dummy;
1048 SwsUOp *op = &uops->ops[i];
1049
1050 switch (op->uop) {
1051 /* placeholder */
1052 }
1053 }
1054#endif
1055
1056 return 0;
1057}
1058
1059/**
1060 * Determine a suitable intermediate buffer format for a given combination
1061 * of pixel types and number of planes. The exact interpretation of these
1062 * formats does not matter at all; since they will only ever be used as
1063 * temporary intermediate buffers. We still need to pick *some* format as
1064 * a consequence of ff_sws_graph_add_pass() taking an AVPixelFormat for the
1065 * output buffer.
1066 */
1068{
1069 switch (ff_sws_pixel_type_size(type)) {
1070 case 1:
1071 switch (nb_planes) {
1072 case 1: return AV_PIX_FMT_GRAY8;
1073 case 2: return AV_PIX_FMT_YUV444P; // FIXME: no 2-plane planar fmt
1074 case 3: return AV_PIX_FMT_YUV444P;
1075 case 4: return AV_PIX_FMT_YUVA444P;
1076 }
1077 break;
1078 case 2:
1079 switch (nb_planes) {
1080 case 1: return AV_PIX_FMT_GRAY16;
1081 case 2: return AV_PIX_FMT_YUV444P16; // FIXME: no 2-plane planar fmt
1082 case 3: return AV_PIX_FMT_YUV444P16;
1083 case 4: return AV_PIX_FMT_YUVA444P16;
1084 }
1085 break;
1086 case 4:
1087 switch (nb_planes) {
1088 case 1: return AV_PIX_FMT_GRAYF32;
1089 case 2: return AV_PIX_FMT_GBRPF32; // FIXME: no 2-plane planar fmt
1090 case 3: return AV_PIX_FMT_GBRPF32;
1091 case 4: return AV_PIX_FMT_GBRAPF32;
1092 }
1093 break;
1094 }
1095
1096 av_unreachable("Invalid pixel type or number of planes?");
1097 return AV_PIX_FMT_NONE;
1098}
1099
1100static void get_input_size(const SwsOpList *ops, SwsFormat *fmt)
1101{
1102 fmt->width = ops->src.width;
1103 fmt->height = ops->src.height;
1104
1105 const SwsOp *read = ff_sws_op_list_input(ops);
1106 if (read && read->rw.filter.op == SWS_OP_FILTER_V) {
1107 fmt->height = read->rw.filter.kernel->dst_size;
1108 } else if (read && read->rw.filter.op == SWS_OP_FILTER_H) {
1109 fmt->width = read->rw.filter.kernel->dst_size;
1110 }
1111}
1112
1114{
1115 int ret;
1116 if (index <= 0 || index >= ops1->num_ops) {
1117 *out_ops2 = NULL;
1118 return 0;
1119 }
1120
1121 const SwsOp *op = &ops1->ops[index];
1122 const SwsOp *prev = &ops1->ops[index - 1];
1123
1124 SwsOpList *ops2 = ff_sws_op_list_duplicate(ops1);
1125 if (!ops2)
1126 return AVERROR(ENOMEM);
1127
1128 /**
1129 * Not all components may be needed; but we need the ones that *are*
1130 * used to be contiguous for the write/read operations. So, first
1131 * compress them into a linearly ascending list of components
1132 */
1133 int nb_planes = 0;
1134 SwsSwizzleOp swiz_wr = SWS_SWIZZLE(0, 1, 2, 3);
1135 SwsSwizzleOp swiz_rd = SWS_SWIZZLE(0, 1, 2, 3);
1136 for (int i = 0; i < 4; i++) {
1137 if (SWS_OP_NEEDED(prev, i)) {
1138 const int o = nb_planes++;
1139 swiz_wr.in[o] = i;
1140 swiz_rd.in[i] = o;
1141 }
1142 }
1143
1144 /* Determine metadata for the intermediate format */
1145 const SwsPixelType type = op->type;
1146 ops2->src.format = get_planar_fmt(type, nb_planes);
1147 ops2->src.desc = av_pix_fmt_desc_get(ops2->src.format);
1148 get_input_size(ops1, &ops2->src);
1149 ops1->dst = ops2->src;
1150
1151 for (int i = 0; i < nb_planes; i++) {
1152 const int idx = swiz_wr.in[i];
1153 ops1->plane_dst[i] = ops2->plane_src[i] = i;
1154 ops2->comps_src.flags[i] = prev->comps.flags[idx];
1155 ops2->comps_src.min[i] = prev->comps.min[idx];
1156 ops2->comps_src.max[i] = prev->comps.max[idx];
1157 }
1158
1161 op = NULL; /* the above command may invalidate op */
1162
1163 if (swiz_wr.mask != SWS_SWIZZLE(0, 1, 2, 3).mask) {
1164 ret = ff_sws_op_list_append(ops1, &(SwsOp) {
1165 .op = SWS_OP_SWIZZLE,
1166 .type = type,
1167 .swizzle = swiz_wr,
1168 });
1169 if (ret < 0)
1170 goto fail;
1171 }
1172
1173 ret = ff_sws_op_list_append(ops1, &(SwsOp) {
1174 .op = SWS_OP_WRITE,
1175 .type = type,
1176 .rw.elems = nb_planes,
1177 });
1178 if (ret < 0)
1179 goto fail;
1180
1181 ret = ff_sws_op_list_insert_at(ops2, 0, &(SwsOp) {
1182 .op = SWS_OP_READ,
1183 .type = type,
1184 .rw.elems = nb_planes,
1185 });
1186 if (ret < 0)
1187 goto fail;
1188
1189 if (swiz_rd.mask != SWS_SWIZZLE(0, 1, 2, 3).mask) {
1190 ret = ff_sws_op_list_insert_at(ops2, 1, &(SwsOp) {
1191 .op = SWS_OP_SWIZZLE,
1192 .type = type,
1193 .swizzle = swiz_rd,
1194 });
1195 if (ret < 0)
1196 goto fail;
1197 }
1198
1199 ret = ff_sws_op_list_optimize(ops1);
1200 if (ret < 0)
1201 goto fail;
1202
1203 ret = ff_sws_op_list_optimize(ops2);
1204 if (ret < 0)
1205 goto fail;
1206
1207 *out_ops2 = ops2;
1208 return 0;
1209
1210fail:
1211 ff_sws_op_list_free(&ops2);
1212 return ret;
1213}
uint8_t ptrdiff_t const uint8_t ptrdiff_t int intptr_t intptr_t int int16_t * dst
Definition dsp.h:87
static double val(void *priv, double ch)
Definition aeval.c:77
static FILE * out
static AVFormatContext * ctx
simple assert() macros that are a bit more flexible than ISO C assert().
#define av_assert2(cond)
assert() equivalent, that does lie in speed critical code.
Definition avassert.h:68
#define av_assert1(cond)
assert() equivalent, that does not lie in speed critical code.
Definition avassert.h:58
#define av_unreachable(msg)
Asserts that are used as compiler optimization hints depending upon ASSERT_LEVEL and NBDEBUG.
Definition avassert.h:109
#define av_assert0(cond)
assert() equivalent, that is always enabled.
Definition avassert.h:42
static uint32_t BS_FUNC read(BSCTX *bc, unsigned int n)
Return n bits from the buffer, n has to be in the 0-32 range.
byte swapping routines
#define flags(name, subs,...)
Definition cbs_h264.c:74
#define i(width, name, range_min, range_max)
Definition cbs_h264.c:63
#define s(width, name)
Definition cbs_vp9.c:198
#define av_popcount
Definition common.h:154
#define FFABS(a)
Absolute value, Note, INT_MIN / INT64_MIN result in undefined behavior as they are not representable ...
Definition common.h:74
#define NULL
Definition coverity.c:32
static uint64_t shuffle(uint64_t in, const uint8_t *shuffle, int shuffle_len)
Definition des.c:179
static int dummy
Definition ffplay.c:3754
AVRational64 av_mul_q64(AVRational64 b, AVRational64 c)
Multiply two 64-bit rationals.
Definition rational64.c:124
int av_cmp_q64(AVRational64 a, AVRational64 b)
Compare two 64-bit rationals.
Definition rational64.c:108
AVRational64 av_add_q64(AVRational64 b, AVRational64 c)
Add two 64-bit rationals.
Definition rational64.c:135
#define fail
Definition test.h:479
#define AVERROR(e)
Definition error.h:45
int index
Definition gxfenc.c:90
#define Q(q)
cl_device_type type
#define b
Definition input.c:43
#define av_log2
Definition intmath.h:84
static void scale(int *out, const int *in, const int w, const int h, const int shift)
Definition intra.c:278
unsigned offset
Definition libaomenc.c:763
static int op(uint8_t **dst, const uint8_t *dst_end, GetByteContext *gb, int pixel, int count, int *x, int width, int linesize)
Perform decode operation.
Definition anm.c:76
Macro definitions for various function/variable attributes.
#define av_fallthrough
Definition attributes.h:67
static const struct @257111027162314367033347246032313251342043035002 planes[]
static const uint16_t mask[17]
Definition lzw.c:38
#define FFSWAP(type, a, b)
Definition macros.h:52
#define FFMAX(a, b)
Definition macros.h:47
const char data[16]
Definition mxf.c:149
void ff_sws_op_list_update_comps(SwsOpList *ops)
Infer + propagate known information about components.
Definition ops.c:315
uint32_t ff_sws_linear_mask(const SwsLinearOp *c)
Definition ops.c:802
int ff_sws_op_list_insert_at(SwsOpList *ops, int index, SwsOp *op)
Definition ops.c:742
const SwsOp * ff_sws_op_list_input(const SwsOpList *ops)
Returns the input operation for a given op list, or NULL if there is none (e.g.
Definition ops.c:713
void ff_sws_op_list_free(SwsOpList **p_ops)
Definition ops.c:659
int ff_sws_op_list_append(SwsOpList *ops, SwsOp *op)
These will take over ownership of op and set it to {0}, even on failure.
Definition ops.c:756
void ff_sws_comp_mask_swizzle(SwsCompMask *mask, const SwsSwizzleOp *swiz)
Definition ops.c:110
SwsCompMask ff_sws_comp_mask_needed(const SwsOp *op)
Definition ops.c:123
const SwsOp * ff_sws_op_list_output(const SwsOpList *ops)
Returns the output operation for a given op list, or NULL if there is none.
Definition ops.c:722
void ff_sws_apply_op_q(const SwsOp *op, AVRational64 x[4])
Apply an operation to an AVRational64.
Definition ops.c:157
void ff_sws_op_list_remove_at(SwsOpList *ops, int index, int count)
Definition ops.c:731
SwsOpList * ff_sws_op_list_duplicate(const SwsOpList *ops)
Returns a duplicate of ops, or NULL on OOM.
Definition ops.c:673
@ SWS_OP_TYPE_NB
Definition ops.h:69
@ SWS_OP_INVALID
Definition ops.h:37
@ SWS_OP_RSHIFT
Definition ops.h:49
@ SWS_OP_SWIZZLE
Definition ops.h:43
@ SWS_OP_LSHIFT
Definition ops.h:48
@ SWS_OP_FILTER_V
Definition ops.h:64
@ SWS_OP_SCALE
Definition ops.h:56
@ SWS_OP_FILTER_H
Definition ops.h:63
@ SWS_OP_WRITE
Definition ops.h:41
@ SWS_OP_READ
Definition ops.h:40
@ SWS_OP_CLEAR
Definition ops.h:52
@ SWS_OP_SWAP_BYTES
Definition ops.h:42
@ SWS_OP_MIN
Definition ops.h:54
@ SWS_OP_UNPACK
Definition ops.h:46
@ SWS_OP_LINEAR
Definition ops.h:59
@ SWS_OP_PACK
Definition ops.h:47
@ SWS_OP_DITHER
Definition ops.h:60
@ SWS_OP_MAX
Definition ops.h:55
@ SWS_OP_LUT_3D
Definition ops.h:67
@ SWS_OP_CONVERT
Definition ops.h:53
@ SWS_RW_PLANAR
Note: 1-component reads are either SWS_RW_PLANAR or SWS_RW_PACKED, depending on the underlying interp...
Definition ops.h:107
@ SWS_COMP_ZERO
Definition ops.h:80
@ SWS_COMP_EXACT
Definition ops.h:79
@ SWS_COMP_GARBAGE
Definition ops.h:78
#define SWS_SWIZZLE(X, Y, Z, W)
Definition ops.h:160
#define SWS_OP_NEEDED(op, idx)
Definition ops.h:265
static AVRational64 ff_sws_pixel_expand(SwsPixelType from, SwsPixelType to)
static bool pixel_is_repeating(SwsPixelType type, SwsPixel val)
int ff_sws_op_list_split_planes(SwsOpList *ops1, SwsOpList **out_ops2, SwsCompMask planes)
Reduce an op list into a reduced subset that operates only on a given subset of planes.
static int exact_log2(const int x)
static enum AVPixelFormat get_planar_fmt(SwsPixelType type, int nb_planes)
Determine a suitable intermediate buffer format for a given combination of pixel types and number of ...
static int solve_shuffle(const SwsUOpList *const uops, SwsUOp *out)
int ff_sws_op_list_optimize(SwsOpList *ops)
Fuse compatible and eliminate redundant operations, as well as replacing some operations with more ef...
#define RET(x)
Copyright (C) 2025 Niklas Haas.
static bool op_commute_filter(SwsOp *op, SwsOp *prev)
Try to commute a filter op with the previous operation.
static bool op_commute_clear(SwsOp *op, SwsOp *next)
Try to commute a clear op with the next operation.
int ff_sws_uop_list_optimize(SwsContext *ctx, SwsUOpFlags flags, SwsUOpList *uops)
Called internally by ff_sws_ops_translate().
static void get_input_size(const SwsOpList *ops, SwsFormat *fmt)
static int exact_log2_q64(const AVRational64 x)
int ff_sws_op_list_split_at(SwsOpList *ops1, SwsOpList **out_ops2, int index)
Split an op list into two at the given index.
static int select_planes(SwsOpList *ops, SwsCompMask planes)
static bool extract_swizzle(SwsLinearOp *op, const SwsComps *prev, SwsSwizzleOp *out_swiz)
static int op_result_is_exact(const SwsOp *op)
static bool op_commute_swizzle(SwsOp *op, SwsOp *next)
Try to commute a swizzle op with the next operation.
int ff_sws_shuffle_mask(const SwsUOp *uop, int8_t shuffle[], int size)
Compute a shuffle mask for pshufb-style ASM functions, by repeating the shuffle pattern for as many g...
static bool extract_scalar(const SwsLinearOp *c, const SwsComps *comps, const SwsComps *prev, SwsScaleOp *out_scale)
If a linear operation can be reduced to a scalar multiplication, returns the corresponding scaling fa...
static bool extract_constant_rows(SwsLinearOp *c, const SwsComps *prev, SwsClearOp *out_clear)
const AVPixFmtDescriptor * av_pix_fmt_desc_get(enum AVPixelFormat pix_fmt)
Definition pixdesc.c:3460
#define AV_PIX_FMT_GBRPF32
Definition pixfmt.h:584
#define AV_PIX_FMT_GRAYF32
Definition pixfmt.h:588
AVPixelFormat
Pixel format.
Definition pixfmt.h:71
@ AV_PIX_FMT_NONE
Definition pixfmt.h:72
@ AV_PIX_FMT_GRAY8
Y , 8bpp.
Definition pixfmt.h:81
@ AV_PIX_FMT_YUV444P
planar YUV 4:4:4, 24bpp, (1 Cr & Cb sample per 1x1 Y samples)
Definition pixfmt.h:78
@ AV_PIX_FMT_YUVA444P
planar YUV 4:4:4 32bpp, (1 Cr & Cb sample per 1x1 Y & A samples)
Definition pixfmt.h:174
#define AV_PIX_FMT_GRAY16
Definition pixfmt.h:528
#define AV_PIX_FMT_YUVA444P16
Definition pixfmt.h:603
#define AV_PIX_FMT_GBRAPF32
Definition pixfmt.h:585
#define AV_PIX_FMT_YUV444P16
Definition pixfmt.h:558
#define noop(a)
Utilities for rational number calculation.
void * av_refstruct_ref(void *obj)
Create a new reference to an object managed via this API, i.e.
Definition refstruct.c:140
#define av_bswap32
Definition bswap.h:47
#define av_bswap16
Definition bswap.h:28
#define FF_ARRAY_ELEMS(a)
64-bit Rational number (pair of numerator and denominator).
Definition rational64.h:52
int64_t num
Numerator.
Definition rational64.h:53
int64_t den
Denominator.
Definition rational64.h:54
AVRational64 limit[4]
Definition ops.h:178
SwsCompMask mask
Definition ops.h:168
AVRational64 value[4]
Definition ops.h:169
Definition ops.h:86
SwsCompFlags flags[4]
Definition ops.h:87
AVRational64 min[4]
Definition ops.h:91
AVRational64 max[4]
Definition ops.h:91
Main external API structure.
Definition swscale.h:227
bool expand
Definition ops.h:174
SwsPixelType to
Definition ops.h:173
int8_t y_offset[4]
Definition ops.h:189
SwsPixelType type
Definition ops.h:208
enum AVPixelFormat format
Definition format.h:81
const AVPixFmtDescriptor * desc
Definition format.h:86
int width
Definition format.h:78
int height
Definition format.h:78
AVRational64 m[4][5]
Generalized 5x5 affine transformation: [ Out.x ] = [ A B C D E ] [ Out.y ] = [ F G H I J ] * [ x y z ...
Definition ops.h:200
int8_t src[SWS_UOP_MOVE_MAX]
Definition uops.h:211
int num_moves
Definition uops.h:207
int8_t dst[SWS_UOP_MOVE_MAX]
Definition uops.h:210
Helper struct for representing a list of operations.
Definition ops.h:293
SwsFormat dst
Definition ops.h:298
SwsComps comps_src
Source component metadata associated with pixel values from each corresponding component (in plane/me...
Definition ops.h:312
uint8_t plane_src[4]
Definition ops.h:301
uint8_t plane_dst[4]
Definition ops.h:301
SwsOp * ops
Definition ops.h:294
int num_ops
Definition ops.h:295
SwsFormat src
Definition ops.h:298
Definition ops.h:237
SwsClampOp clamp
Definition ops.h:248
SwsScaleOp scale
Definition ops.h:249
SwsSwizzleOp swizzle
Definition ops.h:244
SwsComps comps
Metadata about the operation's input/output components.
Definition ops.h:262
SwsPixelType type
Definition ops.h:239
SwsOpType op
Definition ops.h:238
SwsConvertOp convert
Definition ops.h:247
SwsPackOp pack
Definition ops.h:243
SwsClearOp clear
Definition ops.h:246
SwsLinearOp lin
Definition ops.h:241
SwsReadWriteOp rw
Definition ops.h:242
SwsFilterOp filter
Definition ops.h:251
SwsShiftOp shift
Definition ops.h:245
SwsDitherOp dither
Definition ops.h:250
uint8_t pattern[4]
Packed bits are assumed to be LSB-aligned within the underlying integer type; i.e.
Definition ops.h:145
SwsFilterWeights * kernel
Definition ops.h:135
SwsReadWriteMode mode
Examples: rgba = 4x u8 packed yuv444p = 3x u8 rgb565 = 1x u16 <- use SWS_OP_UNPACK to unpack monow = ...
Definition ops.h:122
SwsPixelType type
Definition ops.h:136
SwsOpType op
Definition ops.h:134
uint8_t frac
Definition ops.h:124
uint8_t elems
Definition ops.h:123
struct SwsReadWriteOp::@312356277033155231251141106170064356360153154105 filter
Filter kernel to apply to each plane while sampling.
AVRational64 factor
Definition ops.h:182
uint8_t amount
Definition ops.h:164
int8_t mask[16]
Definition uops.h:192
uint8_t write_size
Definition uops.h:188
uint8_t read_size
Definition uops.h:187
uint8_t in[4]
Definition ops.h:155
uint32_t mask
Definition ops.h:154
SwsUOp * ops
Definition uops.h:301
int num_ops
Definition uops.h:302
Definition uops.h:264
SwsCompMask mask
Definition uops.h:268
SwsUOpType uop
Definition uops.h:267
SwsUOpParams par
Definition uops.h:269
union SwsUOp::@242237116251216327057105100216205033300341206345 data
SwsPixelType type
Definition uops.h:266
SwsShuffleMask shuffle
Definition uops.h:278
SwsPixel vec4[4]
Definition uops.h:276
static uint8_t tmp[40]
Definition aes_ctr.c:52
#define src
Definition vp8dsp.c:248
int size
SwsMoveUOp move
Definition uops.h:256
SwsShuffleUOp shuffle
Definition uops.h:253
int ff_sws_uop_list_append(SwsUOpList *uops, SwsUOp *uop)
Definition uops.c:208
void ff_sws_uop_list_remove_at(SwsUOpList *uops, int index, int count)
Definition uops.c:221
uint32_t SwsUOpFlags
Definition uops.h:122
SwsPixelType
Definition uops.h:39
@ SWS_PIXEL_U32
Definition uops.h:43
@ SWS_PIXEL_U16
Definition uops.h:42
@ SWS_PIXEL_U8
Definition uops.h:41
#define SWS_COMP(X)
Definition uops.h:97
#define SWS_MASK_ROW(I)
Definition uops.h:233
#define SWS_MASK_DIAG4
Definition uops.h:235
#define SWS_COMP_TEST(mask, X)
Definition uops.h:98
#define SWS_COMP_ELEMS(N)
Definition uops.h:100
#define SWS_MASK(I, J)
Definition uops.h:231
#define SWS_MASK_COL(J)
Definition uops.h:234
@ SWS_UOP_PERMUTE
Definition uops.h:151
@ SWS_UOP_EXPAND_QUAD
Definition uops.h:158
@ SWS_UOP_READ_PLANAR
Definition uops.h:133
@ SWS_UOP_WRITE_PLANAR
Definition uops.h:142
@ SWS_UOP_SWAP_BYTES
Definition uops.h:155
@ SWS_UOP_COPY
Definition uops.h:152
@ SWS_UOP_WRITE_PACKED
Definition uops.h:143
@ SWS_UOP_READ_PACKED
Definition uops.h:137
@ SWS_UOP_EXPAND_PAIR
Definition uops.h:157
@ SWS_UOP_CLEAR
Definition uops.h:175
@ SWS_UOP_RW_SHUFFLE
Definition uops.h:148
@ SWS_UOP_FLAG_PSHUFB
Definition uops.h:126
static av_const bool ff_sws_pixel_type_is_int(SwsPixelType type)
Definition uops.h:63
uint8_t SwsCompMask
Bit-mask of components.
Definition uops.h:93
static av_const int ff_sws_pixel_type_size(SwsPixelType type)
Definition uops.h:50
char full[32]
static double c[64]