FFmpeg
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 
21 #include "libavutil/avassert.h"
22 #include "libavutil/bswap.h"
23 #include "libavutil/rational.h"
24 
25 #include "ops.h"
26 #include "ops_internal.h"
27 
28 #define RET(x) \
29  do { \
30  if ((ret = (x)) < 0) \
31  return ret; \
32  } while (0)
33 
34 /**
35  * Try to commute a clear op with the next operation. Makes any adjustments
36  * to the operations as needed, but does not perform the actual commutation.
37  *
38  * Returns whether successful.
39  */
40 static bool op_commute_clear(SwsOp *op, SwsOp *next)
41 {
43 
44  av_assert1(op->op == SWS_OP_CLEAR);
45  switch (next->op) {
46  case SWS_OP_CONVERT:
47  op->type = next->convert.to;
48  /* fall through */
49  case SWS_OP_LSHIFT:
50  case SWS_OP_RSHIFT:
51  case SWS_OP_DITHER:
52  case SWS_OP_MIN:
53  case SWS_OP_MAX:
54  case SWS_OP_SCALE:
55  case SWS_OP_READ:
56  case SWS_OP_SWIZZLE:
57  case SWS_OP_FILTER_H:
58  case SWS_OP_FILTER_V:
59  ff_sws_apply_op_q(next, op->clear.value);
60  return true;
61  case SWS_OP_SWAP_BYTES:
62  switch (next->type) {
63  case SWS_PIXEL_U16:
64  ff_sws_apply_op_q(next, op->clear.value); /* always works */
65  return true;
66  case SWS_PIXEL_U32:
67  for (int i = 0; i < 4; i++) {
68  uint32_t v = av_bswap32(op->clear.value[i].num);
69  if (v > INT_MAX)
70  return false; /* can't represent as AVRational anymore */
71  tmp.value[i] = Q(v);
72  }
73  op->clear = tmp;
74  return true;
75  default:
76  return false;
77  }
78  case SWS_OP_INVALID:
79  case SWS_OP_WRITE:
80  case SWS_OP_LINEAR:
81  case SWS_OP_PACK:
82  case SWS_OP_UNPACK:
83  case SWS_OP_CLEAR:
84  return false;
85  case SWS_OP_TYPE_NB:
86  break;
87  }
88 
89  av_unreachable("Invalid operation type!");
90  return false;
91 }
92 
93  /**
94  * Try to commute a swizzle op with the next operation. Makes any adjustments
95  * to the operations as needed, but does not perform the actual commutation.
96  *
97  * Returns whether successful.
98  */
99 static bool op_commute_swizzle(SwsOp *op, SwsOp *next)
100 {
101  bool seen[4] = {0};
102 
103  av_assert1(op->op == SWS_OP_SWIZZLE);
104  switch (next->op) {
105  case SWS_OP_CONVERT:
106  op->type = next->convert.to;
107  /* fall through */
108  case SWS_OP_SWAP_BYTES:
109  case SWS_OP_LSHIFT:
110  case SWS_OP_RSHIFT:
111  case SWS_OP_SCALE:
112  case SWS_OP_FILTER_H:
113  case SWS_OP_FILTER_V:
114  return true;
115 
116  /**
117  * We can commute per-channel ops only if the per-channel constants are the
118  * same for all duplicated channels; e.g.:
119  * SWIZZLE {0, 0, 0, 3}
120  * NEXT {x, x, x, w}
121  * ->
122  * NEXT {x, _, _, w}
123  * SWIZZLE {0, 0, 0, 3}
124  */
125  case SWS_OP_MIN:
126  case SWS_OP_MAX: {
127  const SwsClampOp c = next->clamp;
128  for (int i = 0; i < 4; i++) {
129  if (!SWS_OP_NEEDED(op, i))
130  continue;
131  const int j = op->swizzle.in[i];
132  if (seen[j] && av_cmp_q(next->clamp.limit[j], c.limit[i]))
133  return false;
134  next->clamp.limit[j] = c.limit[i];
135  seen[j] = true;
136  }
137  return true;
138  }
139 
140  case SWS_OP_DITHER: {
141  const SwsDitherOp d = next->dither;
142  for (int i = 0; i < 4; i++) {
143  if (!SWS_OP_NEEDED(op, i))
144  continue;
145  const int j = op->swizzle.in[i];
146  if (seen[j] && next->dither.y_offset[j] != d.y_offset[i])
147  return false;
148  next->dither.y_offset[j] = d.y_offset[i];
149  seen[j] = true;
150  }
151  return true;
152  }
153 
154  case SWS_OP_INVALID:
155  case SWS_OP_READ:
156  case SWS_OP_WRITE:
157  case SWS_OP_SWIZZLE:
158  case SWS_OP_CLEAR:
159  case SWS_OP_LINEAR:
160  case SWS_OP_PACK:
161  case SWS_OP_UNPACK:
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  */
178 static bool op_commute_filter(SwsOp *op, SwsOp *prev)
179 {
180  switch (prev->op) {
181  case SWS_OP_SWIZZLE:
182  case SWS_OP_SCALE:
183  case SWS_OP_LINEAR:
184  case SWS_OP_DITHER:
185  prev->type = SWS_PIXEL_F32;
186  return true;
187  case SWS_OP_CONVERT:
188  if (prev->convert.to == SWS_PIXEL_F32) {
189  av_assert0(!prev->convert.expand);
190  FFSWAP(SwsPixelType, op->type, prev->type);
191  return true;
192  }
193  return false;
194  case SWS_OP_INVALID:
195  case SWS_OP_READ:
196  case SWS_OP_WRITE:
197  case SWS_OP_SWAP_BYTES:
198  case SWS_OP_UNPACK:
199  case SWS_OP_PACK:
200  case SWS_OP_LSHIFT:
201  case SWS_OP_RSHIFT:
202  case SWS_OP_CLEAR:
203  case SWS_OP_MIN:
204  case SWS_OP_MAX:
205  case SWS_OP_FILTER_H:
206  case SWS_OP_FILTER_V:
207  return false;
208  case SWS_OP_TYPE_NB:
209  break;
210  }
211 
212  av_unreachable("Invalid operation type!");
213  return false;
214 }
215 
216 /* returns log2(x) only if x is a power of two, or 0 otherwise */
217 static int exact_log2(const int x)
218 {
219  int p;
220  if (x <= 0)
221  return 0;
222  p = av_log2(x);
223  return (1 << p) == x ? p : 0;
224 }
225 
226 static int exact_log2_q(const AVRational x)
227 {
228  if (x.den == 1)
229  return exact_log2(x.num);
230  else if (x.num == 1)
231  return -exact_log2(x.den);
232  else
233  return 0;
234 }
235 
236 /**
237  * If a linear operation can be reduced to a scalar multiplication, returns
238  * the corresponding scaling factor, or 0 otherwise.
239  */
240 static bool extract_scalar(const SwsLinearOp *c, SwsComps comps, SwsComps prev,
241  SwsScaleOp *out_scale)
242 {
243  SwsScaleOp scale = {0};
244 
245  /* There are components not on the main diagonal */
246  if (c->mask & ~SWS_MASK_DIAG4)
247  return false;
248 
249  for (int i = 0; i < 4; i++) {
250  const AVRational s = c->m[i][i];
251  if ((prev.flags[i] & SWS_COMP_ZERO) ||
252  (comps.flags[i] & SWS_COMP_GARBAGE))
253  continue;
254  if (scale.factor.den && av_cmp_q(s, scale.factor))
255  return false;
256  scale.factor = s;
257  }
258 
259  if (scale.factor.den)
260  *out_scale = scale;
261  return scale.factor.den;
262 }
263 
264 /* Extracts an integer clear operation (subset) from the given linear op. */
266  SwsClearOp *out_clear)
267 {
268  SwsClearOp clear = {0};
269  bool ret = false;
270 
271  for (int i = 0; i < 4; i++) {
272  bool const_row = c->m[i][4].den == 1; /* offset is integer */
273  for (int j = 0; j < 4; j++) {
274  const_row &= c->m[i][j].num == 0 || /* scalar is zero */
275  (prev.flags[j] & SWS_COMP_ZERO); /* input is zero */
276  }
277  if (const_row && (c->mask & SWS_MASK_ROW(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  c->mask &= ~SWS_MASK_ROW(i);
282  ret = true;
283  }
284  }
285 
286  if (ret)
287  *out_clear = clear;
288  return ret;
289 }
290 
291 /* Unswizzle a linear operation by aligning single-input rows with
292  * their corresponding diagonal */
293 static bool extract_swizzle(SwsLinearOp *op, SwsComps prev, 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  c.mask = ff_sws_linear_mask(c);
328  *out_swiz = swiz;
329  *op = c;
330  return true;
331 }
332 
334 {
335  int ret;
336 
337 retry:
339 
340  /* Try to push filters towards the input; do this first to unblock
341  * in-place optimizations like linear op fusion */
342  for (int n = 1; n < ops->num_ops; n++) {
343  SwsOp *op = &ops->ops[n];
344  SwsOp *prev = &ops->ops[n - 1];
345 
346  switch (op->op) {
347  case SWS_OP_FILTER_H:
348  case SWS_OP_FILTER_V:
349  if (op_commute_filter(op, prev)) {
350  FFSWAP(SwsOp, *op, *prev);
351  goto retry;
352  }
353  break;
354  }
355  }
356 
357  /* Apply all in-place optimizations (that do not re-order the list) */
358  for (int n = 0; n < ops->num_ops; n++) {
359  SwsOp dummy = {0};
360  SwsOp *op = &ops->ops[n];
361  SwsOp *prev = n ? &ops->ops[n - 1] : &dummy;
362  SwsOp *next = n + 1 < ops->num_ops ? &ops->ops[n + 1] : &dummy;
363 
364  /* common helper variable */
365  bool noop = true;
366 
367  if (!SWS_OP_NEEDED(op, 0) && !SWS_OP_NEEDED(op, 1) &&
368  !SWS_OP_NEEDED(op, 2) && !SWS_OP_NEEDED(op, 3) &&
369  op->op != SWS_OP_WRITE)
370  {
371  /* Remove any operation whose output is not needed */
372  ff_sws_op_list_remove_at(ops, n, 1);
373  goto retry;
374  }
375 
376  switch (op->op) {
377  case SWS_OP_READ:
378  /* "Compress" planar reads where not all components are needed */
379  if (!op->rw.packed) {
380  SwsSwizzleOp swiz = SWS_SWIZZLE(0, 1, 2, 3);
381  int nb_planes = 0;
382  for (int i = 0; i < op->rw.elems; i++) {
383  if (!SWS_OP_NEEDED(op, i)) {
384  swiz.in[i] = 3 - (i - nb_planes); /* map to unused plane */
385  continue;
386  }
387 
388  const int idx = nb_planes++;
389  av_assert1(idx <= i);
390  ops->plane_src[idx] = ops->plane_src[i];
391  swiz.in[i] = idx;
392  }
393 
394  if (nb_planes < op->rw.elems) {
395  op->rw.elems = nb_planes;
396  RET(ff_sws_op_list_insert_at(ops, n + 1, &(SwsOp) {
397  .op = SWS_OP_SWIZZLE,
398  .type = op->rw.filter ? SWS_PIXEL_F32 : op->type,
399  .swizzle = swiz,
400  }));
401  goto retry;
402  }
403  }
404  break;
405 
406  case SWS_OP_SWAP_BYTES:
407  /* Redundant (double) swap */
408  if (next->op == SWS_OP_SWAP_BYTES) {
409  ff_sws_op_list_remove_at(ops, n, 2);
410  goto retry;
411  }
412  break;
413 
414  case SWS_OP_UNPACK:
415  /* Redundant unpack+pack */
416  if (next->op == SWS_OP_PACK && next->type == op->type &&
417  next->pack.pattern[0] == op->pack.pattern[0] &&
418  next->pack.pattern[1] == op->pack.pattern[1] &&
419  next->pack.pattern[2] == op->pack.pattern[2] &&
420  next->pack.pattern[3] == op->pack.pattern[3])
421  {
422  ff_sws_op_list_remove_at(ops, n, 2);
423  goto retry;
424  }
425  break;
426 
427  case SWS_OP_LSHIFT:
428  case SWS_OP_RSHIFT:
429  /* Two shifts in the same direction */
430  if (next->op == op->op) {
431  op->shift.amount += next->shift.amount;
432  ff_sws_op_list_remove_at(ops, n + 1, 1);
433  goto retry;
434  }
435 
436  /* No-op shift */
437  if (!op->shift.amount) {
438  ff_sws_op_list_remove_at(ops, n, 1);
439  goto retry;
440  }
441  break;
442 
443  case SWS_OP_CLEAR:
444  for (int i = 0; i < 4; i++) {
445  if (!op->clear.value[i].den)
446  continue;
447 
448  if ((prev->comps.flags[i] & SWS_COMP_ZERO) &&
449  !(prev->comps.flags[i] & SWS_COMP_GARBAGE) &&
450  op->clear.value[i].num == 0)
451  {
452  /* Redundant clear-to-zero of zero component */
453  op->clear.value[i].den = 0;
454  } else if (!SWS_OP_NEEDED(op, i)) {
455  /* Unnecessary clear of unused component */
456  op->clear.value[i] = (AVRational) {0, 0};
457  } else if (op->clear.value[i].den) {
458  noop = false;
459  }
460  }
461 
462  if (noop) {
463  ff_sws_op_list_remove_at(ops, n, 1);
464  goto retry;
465  }
466 
467  /* Transitive clear */
468  if (next->op == SWS_OP_CLEAR) {
469  for (int i = 0; i < 4; i++) {
470  if (next->clear.value[i].den)
471  op->clear.value[i] = next->clear.value[i];
472  }
473  ff_sws_op_list_remove_at(ops, n + 1, 1);
474  goto retry;
475  }
476  break;
477 
478  case SWS_OP_SWIZZLE:
479  for (int i = 0; i < 4; i++) {
480  if (!SWS_OP_NEEDED(op, i))
481  continue;
482  if (op->swizzle.in[i] != i)
483  noop = false;
484  }
485 
486  /* Identity swizzle */
487  if (noop) {
488  ff_sws_op_list_remove_at(ops, n, 1);
489  goto retry;
490  }
491 
492  /* Transitive swizzle */
493  if (next->op == SWS_OP_SWIZZLE) {
494  const SwsSwizzleOp orig = op->swizzle;
495  for (int i = 0; i < 4; i++)
496  op->swizzle.in[i] = orig.in[next->swizzle.in[i]];
497  ff_sws_op_list_remove_at(ops, n + 1, 1);
498  goto retry;
499  }
500 
501  /* Swizzle planes instead of components, if possible */
502  if (prev->op == SWS_OP_READ && !prev->rw.packed) {
503  for (int dst = 0; dst < prev->rw.elems; dst++) {
504  const int src = op->swizzle.in[dst];
505  if (src > dst && src < prev->rw.elems) {
506  FFSWAP(int, ops->plane_src[dst], ops->plane_src[src]);
507  for (int i = dst; i < 4; i++) {
508  if (op->swizzle.in[i] == dst)
509  op->swizzle.in[i] = src;
510  else if (op->swizzle.in[i] == src)
511  op->swizzle.in[i] = dst;
512  }
513  goto retry;
514  }
515  }
516  }
517 
518  if (next->op == SWS_OP_WRITE && !next->rw.packed) {
519  for (int dst = 0; dst < next->rw.elems; dst++) {
520  const int src = op->swizzle.in[dst];
521  if (src > dst && src < next->rw.elems) {
522  FFSWAP(int, ops->plane_dst[dst], ops->plane_dst[src]);
523  FFSWAP(int, op->swizzle.in[dst], op->swizzle.in[src]);
524  goto retry;
525  }
526  }
527  }
528  break;
529 
530  case SWS_OP_CONVERT:
531  /* No-op conversion */
532  if (op->type == op->convert.to) {
533  ff_sws_op_list_remove_at(ops, n, 1);
534  goto retry;
535  }
536 
537  /* Transitive conversion */
538  if (next->op == SWS_OP_CONVERT &&
539  op->convert.expand == next->convert.expand)
540  {
541  av_assert1(op->convert.to == next->type);
542  op->convert.to = next->convert.to;
543  ff_sws_op_list_remove_at(ops, n + 1, 1);
544  goto retry;
545  }
546 
547  /* Conversion followed by integer expansion */
548  if (next->op == SWS_OP_SCALE && !op->convert.expand &&
549  ff_sws_pixel_type_is_int(op->type) &&
550  ff_sws_pixel_type_is_int(op->convert.to) &&
551  !av_cmp_q(next->scale.factor,
552  ff_sws_pixel_expand(op->type, op->convert.to)))
553  {
554  op->convert.expand = true;
555  ff_sws_op_list_remove_at(ops, n + 1, 1);
556  goto retry;
557  }
558  break;
559 
560  case SWS_OP_MIN:
561  for (int i = 0; i < 4; i++) {
562  if (!SWS_OP_NEEDED(op, i) || !op->clamp.limit[i].den)
563  continue;
564  if (av_cmp_q(op->clamp.limit[i], prev->comps.max[i]) < 0)
565  noop = false;
566  }
567 
568  if (noop) {
569  ff_sws_op_list_remove_at(ops, n, 1);
570  goto retry;
571  }
572  break;
573 
574  case SWS_OP_MAX:
575  for (int i = 0; i < 4; i++) {
576  if (!SWS_OP_NEEDED(op, i) || !op->clamp.limit[i].den)
577  continue;
578  if (av_cmp_q(prev->comps.min[i], op->clamp.limit[i]) < 0)
579  noop = false;
580  }
581 
582  if (noop) {
583  ff_sws_op_list_remove_at(ops, n, 1);
584  goto retry;
585  }
586  break;
587 
588  case SWS_OP_DITHER:
589  for (int i = 0; i < 4; i++) {
590  if (op->dither.y_offset[i] < 0)
591  continue;
592  if (!SWS_OP_NEEDED(op, i) || (prev->comps.flags[i] & SWS_COMP_EXACT)) {
593  op->dither.y_offset[i] = -1; /* unnecessary dither */
594  goto retry;
595  } else {
596  noop = false;
597  }
598  }
599 
600  if (noop) {
601  ff_sws_op_list_remove_at(ops, n, 1);
602  goto retry;
603  }
604  break;
605 
606  case SWS_OP_LINEAR: {
607  SwsSwizzleOp swizzle;
608  SwsClearOp clear;
610 
611  /* No-op (identity) linear operation */
612  if (!op->lin.mask) {
613  ff_sws_op_list_remove_at(ops, n, 1);
614  goto retry;
615  }
616 
617  if (next->op == SWS_OP_LINEAR) {
618  /* 5x5 matrix multiplication after appending [ 0 0 0 0 1 ] */
619  const SwsLinearOp m1 = op->lin;
620  const SwsLinearOp m2 = next->lin;
621  for (int i = 0; i < 4; i++) {
622  for (int j = 0; j < 5; j++) {
623  AVRational sum = Q(0);
624  for (int k = 0; k < 4; k++)
625  sum = av_add_q(sum, av_mul_q(m2.m[i][k], m1.m[k][j]));
626  if (j == 4) /* m1.m[4][j] == 1 */
627  sum = av_add_q(sum, m2.m[i][4]);
628  op->lin.m[i][j] = sum;
629  }
630  }
631  op->lin.mask = ff_sws_linear_mask(op->lin);
632  ff_sws_op_list_remove_at(ops, n + 1, 1);
633  goto retry;
634  }
635 
636  /* Optimize away zero columns */
637  for (int j = 0; j < 4; j++) {
638  const uint32_t col = SWS_MASK_COL(j);
639  if (!(prev->comps.flags[j] & SWS_COMP_ZERO) || !(op->lin.mask & col))
640  continue;
641  for (int i = 0; i < 4; i++)
642  op->lin.m[i][j] = Q(i == j);
643  op->lin.mask &= ~col;
644  goto retry;
645  }
646 
647  /* Optimize away unused rows */
648  for (int i = 0; i < 4; i++) {
649  const uint32_t row = SWS_MASK_ROW(i);
650  if (SWS_OP_NEEDED(op, i) || !(op->lin.mask & row))
651  continue;
652  for (int j = 0; j < 5; j++)
653  op->lin.m[i][j] = Q(i == j);
654  op->lin.mask &= ~row;
655  goto retry;
656  }
657 
658  /* Convert constant rows to explicit clear instruction */
659  if (extract_constant_rows(&op->lin, prev->comps, &clear)) {
660  RET(ff_sws_op_list_insert_at(ops, n + 1, &(SwsOp) {
661  .op = SWS_OP_CLEAR,
662  .type = op->type,
663  .comps = op->comps,
664  .clear = clear,
665  }));
666  goto retry;
667  }
668 
669  /* Multiplication by scalar constant */
670  if (extract_scalar(&op->lin, op->comps, prev->comps, &scale)) {
671  op->op = SWS_OP_SCALE;
672  op->scale = scale;
673  goto retry;
674  }
675 
676  /* Swizzle by fixed pattern */
677  if (extract_swizzle(&op->lin, prev->comps, &swizzle)) {
678  RET(ff_sws_op_list_insert_at(ops, n, &(SwsOp) {
679  .op = SWS_OP_SWIZZLE,
680  .type = op->type,
681  .swizzle = swizzle,
682  }));
683  goto retry;
684  }
685  break;
686  }
687 
688  case SWS_OP_SCALE: {
689  const int factor2 = exact_log2_q(op->scale.factor);
690 
691  /* No-op scaling */
692  if (op->scale.factor.num == 1 && op->scale.factor.den == 1) {
693  ff_sws_op_list_remove_at(ops, n, 1);
694  goto retry;
695  }
696 
697  /* Merge consecutive scaling operations (that don't overflow) */
698  if (next->op == SWS_OP_SCALE) {
699  int64_t p = op->scale.factor.num * (int64_t) next->scale.factor.num;
700  int64_t q = op->scale.factor.den * (int64_t) next->scale.factor.den;
701  if (FFABS(p) <= INT_MAX && FFABS(q) <= INT_MAX) {
702  av_reduce(&op->scale.factor.num, &op->scale.factor.den, p, q, INT_MAX);
703  ff_sws_op_list_remove_at(ops, n + 1, 1);
704  goto retry;
705  }
706  }
707 
708  /* Scaling by exact power of two */
709  if (factor2 && ff_sws_pixel_type_is_int(op->type)) {
710  op->op = factor2 > 0 ? SWS_OP_LSHIFT : SWS_OP_RSHIFT;
711  op->shift.amount = FFABS(factor2);
712  goto retry;
713  }
714  break;
715  }
716 
717  case SWS_OP_FILTER_H:
718  case SWS_OP_FILTER_V:
719  /* Merge with prior simple planar read */
720  if (prev->op == SWS_OP_READ && !prev->rw.filter &&
721  !prev->rw.packed && !prev->rw.frac) {
722  prev->rw.filter = op->op;
723  prev->rw.kernel = av_refstruct_ref(op->filter.kernel);
724  ff_sws_op_list_remove_at(ops, n, 1);
725  goto retry;
726  }
727  break;
728  }
729  }
730 
731  /* Push clears to the back to void any unused components */
732  for (int n = 0; n < ops->num_ops - 1; n++) {
733  SwsOp *op = &ops->ops[n];
734  SwsOp *next = &ops->ops[n + 1];
735 
736  switch (op->op) {
737  case SWS_OP_CLEAR:
738  if (op_commute_clear(op, next)) {
739  FFSWAP(SwsOp, *op, *next);
740  goto retry;
741  }
742  break;
743  }
744  }
745 
746  /* Apply any remaining preferential re-ordering optimizations; do these
747  * last because they are more likely to block other optimizations if done
748  * too aggressively */
749  for (int n = 0; n < ops->num_ops - 1; n++) {
750  SwsOp *op = &ops->ops[n];
751  SwsOp *next = &ops->ops[n + 1];
752 
753  switch (op->op) {
754  case SWS_OP_SWIZZLE: {
755  /* Try to push swizzles towards the output */
756  if (op_commute_swizzle(op, next)) {
757  FFSWAP(SwsOp, *op, *next);
758  goto retry;
759  }
760  break;
761  }
762 
763  case SWS_OP_SCALE:
764  /* Scaling by integer before conversion to int */
765  if (op->scale.factor.den == 1 && next->op == SWS_OP_CONVERT &&
767  {
768  op->type = next->convert.to;
769  FFSWAP(SwsOp, *op, *next);
770  goto retry;
771  }
772  break;
773  }
774  }
775 
776  return 0;
777 }
778 
779 int ff_sws_solve_shuffle(const SwsOpList *const ops, uint8_t shuffle[],
780  int size, uint8_t clear_val,
781  int *read_bytes, int *write_bytes)
782 {
783  if (!ops->num_ops)
784  return AVERROR(EINVAL);
785 
786  const SwsOp *read = ff_sws_op_list_input(ops);
787  if (!read || read->rw.frac || read->rw.filter ||
788  (!read->rw.packed && read->rw.elems > 1))
789  return AVERROR(ENOTSUP);
790 
791  const int read_size = ff_sws_pixel_type_size(read->type);
792  uint32_t mask[4] = {0};
793  for (int i = 0; i < read->rw.elems; i++)
794  mask[i] = 0x01010101 * i * read_size + 0x03020100;
795 
796  for (int opidx = 1; opidx < ops->num_ops; opidx++) {
797  const SwsOp *op = &ops->ops[opidx];
798  switch (op->op) {
799  case SWS_OP_SWIZZLE: {
800  uint32_t orig[4] = { mask[0], mask[1], mask[2], mask[3] };
801  for (int i = 0; i < 4; i++)
802  mask[i] = orig[op->swizzle.in[i]];
803  break;
804  }
805 
806  case SWS_OP_SWAP_BYTES:
807  for (int i = 0; i < 4; i++) {
808  switch (ff_sws_pixel_type_size(op->type)) {
809  case 2: mask[i] = av_bswap16(mask[i]); break;
810  case 4: mask[i] = av_bswap32(mask[i]); break;
811  }
812  }
813  break;
814 
815  case SWS_OP_CLEAR:
816  for (int i = 0; i < 4; i++) {
817  if (!op->clear.value[i].den)
818  continue;
819  if (op->clear.value[i].num != 0 || !clear_val)
820  return AVERROR(ENOTSUP);
821  mask[i] = 0x1010101ul * clear_val;
822  }
823  break;
824 
825  case SWS_OP_CONVERT: {
826  if (!op->convert.expand)
827  return AVERROR(ENOTSUP);
828  for (int i = 0; i < 4; i++) {
829  switch (ff_sws_pixel_type_size(op->type)) {
830  case 1: mask[i] = 0x01010101 * (mask[i] & 0xFF); break;
831  case 2: mask[i] = 0x00010001 * (mask[i] & 0xFFFF); break;
832  }
833  }
834  break;
835  }
836 
837  case SWS_OP_WRITE: {
838  if (op->rw.frac || op->rw.filter ||
839  (!op->rw.packed && op->rw.elems > 1))
840  return AVERROR(ENOTSUP);
841 
842  /* Initialize to no-op */
843  memset(shuffle, clear_val, size);
844 
845  const int write_size = ff_sws_pixel_type_size(op->type);
846  const int read_chunk = read->rw.elems * read_size;
847  const int write_chunk = op->rw.elems * write_size;
848  const int num_groups = size / FFMAX(read_chunk, write_chunk);
849  for (int n = 0; n < num_groups; n++) {
850  const int base_in = n * read_chunk;
851  const int base_out = n * write_chunk;
852  for (int i = 0; i < op->rw.elems; i++) {
853  const int offset = base_out + i * write_size;
854  for (int b = 0; b < write_size; b++) {
855  const uint8_t idx = mask[i] >> (b * 8);
856  if (idx != clear_val)
857  shuffle[offset + b] = base_in + idx;
858  }
859  }
860  }
861 
862  *read_bytes = num_groups * read_chunk;
863  *write_bytes = num_groups * write_chunk;
864  return num_groups;
865  }
866 
867  default:
868  return AVERROR(ENOTSUP);
869  }
870  }
871 
872  return AVERROR(EINVAL);
873 }
874 
875 /**
876  * Determine a suitable intermediate buffer format for a given combination
877  * of pixel types and number of planes. The exact interpretation of these
878  * formats does not matter at all; since they will only ever be used as
879  * temporary intermediate buffers. We still need to pick *some* format as
880  * a consequence of ff_sws_graph_add_pass() taking an AVPixelFormat for the
881  * output buffer.
882  */
883 static enum AVPixelFormat get_planar_fmt(SwsPixelType type, int nb_planes)
884 {
885  switch (ff_sws_pixel_type_size(type)) {
886  case 1:
887  switch (nb_planes) {
888  case 1: return AV_PIX_FMT_GRAY8;
889  case 2: return AV_PIX_FMT_YUV444P; // FIXME: no 2-plane planar fmt
890  case 3: return AV_PIX_FMT_YUV444P;
891  case 4: return AV_PIX_FMT_YUVA444P;
892  }
893  break;
894  case 2:
895  switch (nb_planes) {
896  case 1: return AV_PIX_FMT_GRAY16;
897  case 2: return AV_PIX_FMT_YUV444P16; // FIXME: no 2-plane planar fmt
898  case 3: return AV_PIX_FMT_YUV444P16;
899  case 4: return AV_PIX_FMT_YUVA444P16;
900  }
901  break;
902  case 4:
903  switch (nb_planes) {
904  case 1: return AV_PIX_FMT_GRAYF32;
905  case 2: return AV_PIX_FMT_GBRPF32; // FIXME: no 2-plane planar fmt
906  case 3: return AV_PIX_FMT_GBRPF32;
907  case 4: return AV_PIX_FMT_GBRAPF32;
908  }
909  break;
910  }
911 
912  av_unreachable("Invalid pixel type or number of planes?");
913  return AV_PIX_FMT_NONE;
914 }
915 
916 static void get_input_size(const SwsOpList *ops, SwsFormat *fmt)
917 {
918  fmt->width = ops->src.width;
919  fmt->height = ops->src.height;
920 
921  const SwsOp *read = ff_sws_op_list_input(ops);
922  if (read && read->rw.filter == SWS_OP_FILTER_V) {
923  fmt->height = read->rw.kernel->dst_size;
924  } else if (read && read->rw.filter == SWS_OP_FILTER_H) {
925  fmt->width = read->rw.kernel->dst_size;
926  }
927 }
928 
930 {
931  const SwsOp *op;
932  int ret, idx;
933 
934  for (idx = 0; idx < ops1->num_ops; idx++) {
935  op = &ops1->ops[idx];
936  if (op->op == SWS_OP_FILTER_H || op->op == SWS_OP_FILTER_V)
937  break;
938  }
939 
940  if (idx == ops1->num_ops) {
941  *out_rest = NULL;
942  return 0;
943  }
944 
945  av_assert0(idx > 0);
946  const SwsOp *prev = &ops1->ops[idx - 1];
947 
948  SwsOpList *ops2 = ff_sws_op_list_duplicate(ops1);
949  if (!ops2)
950  return AVERROR(ENOMEM);
951 
952  /**
953  * Not all components may be needed; but we need the ones that *are*
954  * used to be contiguous for the write/read operations. So, first
955  * compress them into a linearly ascending list of components
956  */
957  int nb_planes = 0;
958  SwsSwizzleOp swiz_wr = SWS_SWIZZLE(0, 1, 2, 3);
959  SwsSwizzleOp swiz_rd = SWS_SWIZZLE(0, 1, 2, 3);
960  for (int i = 0; i < 4; i++) {
961  if (SWS_OP_NEEDED(prev, i)) {
962  const int o = nb_planes++;
963  swiz_wr.in[o] = i;
964  swiz_rd.in[i] = o;
965  }
966  }
967 
968  /* Determine metadata for the intermediate format */
969  const SwsPixelType type = op->type;
970  ops2->comps_src = prev->comps;
971  ops2->src.format = get_planar_fmt(type, nb_planes);
972  ops2->src.desc = av_pix_fmt_desc_get(ops2->src.format);
973  get_input_size(ops1, &ops2->src);
974  ops1->dst = ops2->src;
975 
976  for (int i = 0; i < nb_planes; i++)
977  ops1->plane_dst[i] = ops2->plane_src[i] = i;
978 
979  ff_sws_op_list_remove_at(ops1, idx, ops1->num_ops - idx);
980  ff_sws_op_list_remove_at(ops2, 0, idx);
981  op = NULL; /* the above command may invalidate op */
982 
983  if (swiz_wr.mask != SWS_SWIZZLE(0, 1, 2, 3).mask) {
984  ret = ff_sws_op_list_append(ops1, &(SwsOp) {
985  .op = SWS_OP_SWIZZLE,
986  .type = type,
987  .swizzle = swiz_wr,
988  });
989  if (ret < 0)
990  goto fail;
991  }
992 
993  ret = ff_sws_op_list_append(ops1, &(SwsOp) {
994  .op = SWS_OP_WRITE,
995  .type = type,
996  .rw.elems = nb_planes,
997  });
998  if (ret < 0)
999  goto fail;
1000 
1001  ret = ff_sws_op_list_insert_at(ops2, 0, &(SwsOp) {
1002  .op = SWS_OP_READ,
1003  .type = type,
1004  .rw.elems = nb_planes,
1005  });
1006  if (ret < 0)
1007  goto fail;
1008 
1009  if (swiz_rd.mask != SWS_SWIZZLE(0, 1, 2, 3).mask) {
1010  ret = ff_sws_op_list_insert_at(ops2, 1, &(SwsOp) {
1011  .op = SWS_OP_SWIZZLE,
1012  .type = type,
1013  .swizzle = swiz_rd,
1014  });
1015  if (ret < 0)
1016  goto fail;
1017  }
1018 
1019  ret = ff_sws_op_list_optimize(ops1);
1020  if (ret < 0)
1021  goto fail;
1022 
1023  ret = ff_sws_op_list_optimize(ops2);
1024  if (ret < 0)
1025  goto fail;
1026 
1027  *out_rest = ops2;
1028  return 0;
1029 
1030 fail:
1031  ff_sws_op_list_free(&ops2);
1032  return ret;
1033 }
SWS_OP_READ
@ SWS_OP_READ
Definition: ops.h:50
SWS_PIXEL_U16
@ SWS_PIXEL_U16
Definition: ops.h:36
ff_sws_op_list_free
void ff_sws_op_list_free(SwsOpList **p_ops)
Definition: ops.c:591
AVPixelFormat
AVPixelFormat
Pixel format.
Definition: pixfmt.h:71
SWS_OP_SWIZZLE
@ SWS_OP_SWIZZLE
Definition: ops.h:53
AVERROR
Filter the word “frame” indicates either a video frame or a group of audio as stored in an AVFrame structure Format for each input and each output the list of supported formats For video that means pixel format For audio that means channel sample they are references to shared objects When the negotiation mechanism computes the intersection of the formats supported at each end of a all references to both lists are replaced with a reference to the intersection And when a single format is eventually chosen for a link amongst the remaining all references to the list are updated That means that if a filter requires that its input and output have the same format amongst a supported all it has to do is use a reference to the same list of formats query_formats can leave some formats unset and return AVERROR(EAGAIN) to cause the negotiation mechanism toagain later. That can be used by filters with complex requirements to use the format negotiated on one link to set the formats supported on another. Frame references ownership and permissions
SwsClearOp::value
AVRational value[4]
Definition: ops.h:150
SWS_OP_LSHIFT
@ SWS_OP_LSHIFT
Definition: ops.h:58
SWS_OP_UNPACK
@ SWS_OP_UNPACK
Definition: ops.h:56
ff_sws_op_list_duplicate
SwsOpList * ff_sws_op_list_duplicate(const SwsOpList *ops)
Returns a duplicate of ops, or NULL on OOM.
Definition: ops.c:605
SwsClearOp
Definition: ops.h:149
SwsSwizzleOp::mask
uint32_t mask
Definition: ops.h:137
SwsOpList::comps_src
SwsComps comps_src
Source component metadata associated with pixel values from each corresponding component (in plane/me...
Definition: ops.h:282
ff_sws_op_list_input
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:642
av_pix_fmt_desc_get
const AVPixFmtDescriptor * av_pix_fmt_desc_get(enum AVPixelFormat pix_fmt)
Definition: pixdesc.c:3456
SWS_COMP_ZERO
@ SWS_COMP_ZERO
Definition: ops.h:84
SWS_OP_CLEAR
@ SWS_OP_CLEAR
Definition: ops.h:62
ff_sws_linear_mask
uint32_t ff_sws_linear_mask(const SwsLinearOp c)
Definition: ops.c:730
SwsOp::swizzle
SwsSwizzleOp swizzle
Definition: ops.h:225
SwsLinearOp::m
AVRational 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:186
SwsOp::convert
SwsConvertOp convert
Definition: ops.h:228
rational.h
int64_t
long long int64_t
Definition: coverity.c:34
ff_sws_op_list_append
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:685
mask
int mask
Definition: mediacodecdec_common.c:154
SwsOp::rw
SwsReadWriteOp rw
Definition: ops.h:223
ops.h
SWS_OP_DITHER
@ SWS_OP_DITHER
Definition: ops.h:70
read_bytes
static void read_bytes(const uint8_t *src, float *dst, int src_stride, int dst_stride, int width, int height, float scale)
Definition: vf_nnedi.c:442
b
#define b
Definition: input.c:42
get_input_size
static void get_input_size(const SwsOpList *ops, SwsFormat *fmt)
Definition: ops_optimizer.c:916
ff_sws_op_list_optimize
int ff_sws_op_list_optimize(SwsOpList *ops)
Fuse compatible and eliminate redundant operations, as well as replacing some operations with more ef...
Definition: ops_optimizer.c:333
SwsClampOp::limit
AVRational limit[4]
Definition: ops.h:159
extract_constant_rows
static bool extract_constant_rows(SwsLinearOp *c, SwsComps prev, SwsClearOp *out_clear)
Definition: ops_optimizer.c:265
SWS_PIXEL_U32
@ SWS_PIXEL_U32
Definition: ops.h:37
SWS_OP_TYPE_NB
@ SWS_OP_TYPE_NB
Definition: ops.h:76
FFMAX
#define FFMAX(a, b)
Definition: macros.h:47
ff_sws_pixel_type_size
int ff_sws_pixel_type_size(SwsPixelType type)
Definition: ops.c:76
dummy
static int dummy
Definition: ffplay.c:3751
SWS_MASK_ROW
#define SWS_MASK_ROW(I)
Definition: ops.h:192
SwsPixelType
SwsPixelType
Copyright (C) 2025 Niklas Haas.
Definition: ops.h:33
SwsComps::max
AVRational max[4]
Definition: ops.h:96
SwsOpList::plane_dst
uint8_t plane_dst[4]
Definition: ops.h:271
SWS_PIXEL_F32
@ SWS_PIXEL_F32
Definition: ops.h:38
fail
#define fail()
Definition: checkasm.h:223
SwsOpList::num_ops
int num_ops
Definition: ops.h:265
SWS_MASK_COL
#define SWS_MASK_COL(J)
Definition: ops.h:193
SwsDitherOp
Definition: ops.h:166
AV_PIX_FMT_YUVA444P16
#define AV_PIX_FMT_YUVA444P16
Definition: pixfmt.h:597
SwsSwizzleOp
Definition: ops.h:131
ff_sws_pixel_type_is_int
bool ff_sws_pixel_type_is_int(SwsPixelType type)
Definition: ops.c:91
type
it s the only field you need to keep assuming you have a context There is some magic you don t need to care about around this just let it vf type
Definition: writing_filters.txt:86
AV_PIX_FMT_GRAY16
#define AV_PIX_FMT_GRAY16
Definition: pixfmt.h:522
av_reduce
int av_reduce(int *dst_num, int *dst_den, int64_t num, int64_t den, int64_t max)
Reduce a fraction.
Definition: rational.c:35
AVRational::num
int num
Numerator.
Definition: rational.h:59
SwsOp::op
SwsOpType op
Definition: ops.h:219
Q
#define Q(q)
SWS_OP_SCALE
@ SWS_OP_SCALE
Definition: ops.h:66
avassert.h
SwsOp::clear
SwsClearOp clear
Definition: ops.h:227
SwsFormat::height
int height
Definition: format.h:78
SWS_OP_NEEDED
#define SWS_OP_NEEDED(op, idx)
Definition: ops.h:88
SwsScaleOp::factor
AVRational factor
Definition: ops.h:163
SWS_MASK_DIAG4
@ SWS_MASK_DIAG4
Definition: ops.h:206
s
#define s(width, name)
Definition: cbs_vp9.c:198
AV_PIX_FMT_YUV444P16
#define AV_PIX_FMT_YUV444P16
Definition: pixfmt.h:552
SWS_SWIZZLE
#define SWS_SWIZZLE(X, Y, Z, W)
Definition: ops.h:143
SwsComps::min
AVRational min[4]
Definition: ops.h:96
read_chunk
static int read_chunk(AVFormatContext *s)
Definition: dhav.c:173
op
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
av_assert0
#define av_assert0(cond)
assert() equivalent, that is always enabled.
Definition: avassert.h:42
SWS_OP_MIN
@ SWS_OP_MIN
Definition: ops.h:64
exact_log2_q
static int exact_log2_q(const AVRational x)
Definition: ops_optimizer.c:226
extract_scalar
static bool extract_scalar(const SwsLinearOp *c, SwsComps comps, SwsComps prev, SwsScaleOp *out_scale)
If a linear operation can be reduced to a scalar multiplication, returns the corresponding scaling fa...
Definition: ops_optimizer.c:240
ff_sws_pixel_expand
static AVRational ff_sws_pixel_expand(SwsPixelType from, SwsPixelType to)
Definition: ops_internal.h:31
SWS_OP_LINEAR
@ SWS_OP_LINEAR
Definition: ops.h:69
op_commute_filter
static bool op_commute_filter(SwsOp *op, SwsOp *prev)
Try to commute a filter op with the previous operation.
Definition: ops_optimizer.c:178
SWS_OP_FILTER_H
@ SWS_OP_FILTER_H
Definition: ops.h:73
AV_PIX_FMT_GRAYF32
#define AV_PIX_FMT_GRAYF32
Definition: pixfmt.h:582
tmp
static uint8_t tmp[40]
Definition: aes_ctr.c:52
FFABS
#define FFABS(a)
Absolute value, Note, INT_MIN / INT64_MIN result in undefined behavior as they are not representable ...
Definition: common.h:74
SWS_OP_PACK
@ SWS_OP_PACK
Definition: ops.h:57
SwsOp::dither
SwsDitherOp dither
Definition: ops.h:231
SwsReadWriteOp::kernel
SwsFilterWeights * kernel
Definition: ops.h:120
NULL
#define NULL
Definition: coverity.c:32
AVRational
Rational number (pair of numerator and denominator).
Definition: rational.h:58
av_unreachable
#define av_unreachable(msg)
Asserts that are used as compiler optimization hints depending upon ASSERT_LEVEL and NBDEBUG.
Definition: avassert.h:116
SwsReadWriteOp::frac
uint8_t frac
Definition: ops.h:109
SWS_COMP_GARBAGE
@ SWS_COMP_GARBAGE
Definition: ops.h:82
SwsConvertOp::to
SwsPixelType to
Definition: ops.h:154
ff_sws_op_list_subpass
int ff_sws_op_list_subpass(SwsOpList *ops1, SwsOpList **out_rest)
Eliminate SWS_OP_FILTER_* operations by merging them with prior SWS_OP_READ operations.
Definition: ops_optimizer.c:929
SWS_OP_FILTER_V
@ SWS_OP_FILTER_V
Definition: ops.h:74
SwsOp::clamp
SwsClampOp clamp
Definition: ops.h:229
ff_sws_op_list_remove_at
void ff_sws_op_list_remove_at(SwsOpList *ops, int index, int count)
Definition: ops.c:660
RET
#define RET(x)
Copyright (C) 2025 Niklas Haas.
Definition: ops_optimizer.c:28
AV_PIX_FMT_GRAY8
@ AV_PIX_FMT_GRAY8
Y , 8bpp.
Definition: pixfmt.h:81
SWS_MASK
#define SWS_MASK(I, J)
Definition: ops.h:190
c
Undefined Behavior In the C some operations are like signed integer dereferencing freed accessing outside allocated Undefined Behavior must not occur in a C it is not safe even if the output of undefined operations is unused The unsafety may seem nit picking but Optimizing compilers have in fact optimized code on the assumption that no undefined Behavior occurs Optimizing code based on wrong assumptions can and has in some cases lead to effects beyond the output of computations The signed integer overflow problem in speed critical code Code which is highly optimized and works with signed integers sometimes has the problem that often the output of the computation does not c
Definition: undefined.txt:32
ff_sws_apply_op_q
void ff_sws_apply_op_q(const SwsOp *op, AVRational x[4])
Apply an operation to an AVRational.
Definition: ops.c:147
SwsConvertOp::expand
bool expand
Definition: ops.h:155
SwsPackOp::pattern
uint8_t pattern[4]
Packed bits are assumed to be LSB-aligned within the underlying integer type; i.e.
Definition: ops.h:128
dst
uint8_t ptrdiff_t const uint8_t ptrdiff_t int intptr_t intptr_t int int16_t * dst
Definition: dsp.h:87
SwsClampOp
Definition: ops.h:158
av_bswap32
#define av_bswap32
Definition: bswap.h:47
i
#define i(width, name, range_min, range_max)
Definition: cbs_h264.c:63
SwsOp::type
SwsPixelType type
Definition: ops.h:220
AV_PIX_FMT_GBRPF32
#define AV_PIX_FMT_GBRPF32
Definition: pixfmt.h:578
ff_sws_op_list_insert_at
int ff_sws_op_list_insert_at(SwsOpList *ops, int index, SwsOp *op)
Definition: ops.c:671
size
int size
Definition: twinvq_data.h:10344
SWS_OP_RSHIFT
@ SWS_OP_RSHIFT
Definition: ops.h:59
SwsOp::lin
SwsLinearOp lin
Definition: ops.h:222
SwsOpList::src
SwsFormat src
Definition: ops.h:268
SWS_OP_INVALID
@ SWS_OP_INVALID
Definition: ops.h:47
ff_sws_op_list_update_comps
void ff_sws_op_list_update_comps(SwsOpList *ops)
Infer + propagate known information about components.
Definition: ops.c:308
SwsFormat
Definition: format.h:77
SwsShiftOp::amount
uint8_t amount
Definition: ops.h:146
SWS_OP_WRITE
@ SWS_OP_WRITE
Definition: ops.h:51
av_refstruct_ref
void * av_refstruct_ref(void *obj)
Create a new reference to an object managed via this API, i.e.
Definition: refstruct.c:140
AV_PIX_FMT_YUVA444P
@ AV_PIX_FMT_YUVA444P
planar YUV 4:4:4 32bpp, (1 Cr & Cb sample per 1x1 Y & A samples)
Definition: pixfmt.h:174
offset
it s the only field you need to keep assuming you have a context There is some magic you don t need to care about around this just let it vf offset
Definition: writing_filters.txt:86
SwsOp::comps
SwsComps comps
Metadata about the operation's input/output components.
Definition: ops.h:242
SwsScaleOp
Definition: ops.h:162
SwsLinearOp
Definition: ops.h:173
get_planar_fmt
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 ...
Definition: ops_optimizer.c:883
noop
#define noop(a)
Definition: h264chroma_template.c:71
SwsFormat::format
enum AVPixelFormat format
Definition: format.h:80
extract_swizzle
static bool extract_swizzle(SwsLinearOp *op, SwsComps prev, SwsSwizzleOp *out_swiz)
Definition: ops_optimizer.c:293
SwsOpList::ops
SwsOp * ops
Definition: ops.h:264
SwsFormat::desc
const AVPixFmtDescriptor * desc
Definition: format.h:85
av_assert1
#define av_assert1(cond)
assert() equivalent, that does not lie in speed critical code.
Definition: avassert.h:58
ops_internal.h
SwsFormat::width
int width
Definition: format.h:78
SwsOp
Definition: ops.h:218
write_bytes
static void write_bytes(const float *src, uint8_t *dst, int src_stride, int dst_stride, int width, int height, int depth, float scale)
Definition: vf_nnedi.c:484
av_cmp_q
static int av_cmp_q(AVRational a, AVRational b)
Compare two rationals.
Definition: rational.h:89
SwsComps::flags
SwsCompFlags flags[4]
Definition: ops.h:91
ret
ret
Definition: filter_design.txt:187
bswap.h
FFSWAP
#define FFSWAP(type, a, b)
Definition: macros.h:52
SwsOpList::dst
SwsFormat dst
Definition: ops.h:268
SWS_OP_MAX
@ SWS_OP_MAX
Definition: ops.h:65
op_commute_swizzle
static bool op_commute_swizzle(SwsOp *op, SwsOp *next)
Try to commute a swizzle op with the next operation.
Definition: ops_optimizer.c:99
SwsReadWriteOp::filter
SwsOpType filter
Filter kernel to apply to each plane while sampling.
Definition: ops.h:119
SwsComps
Definition: ops.h:90
AVRational::den
int den
Denominator.
Definition: rational.h:60
SwsReadWriteOp::packed
bool packed
Definition: ops.h:110
AV_PIX_FMT_NONE
@ AV_PIX_FMT_NONE
Definition: pixfmt.h:72
AV_PIX_FMT_GBRAPF32
#define AV_PIX_FMT_GBRAPF32
Definition: pixfmt.h:579
SWS_OP_SWAP_BYTES
@ SWS_OP_SWAP_BYTES
Definition: ops.h:52
SwsOp::shift
SwsShiftOp shift
Definition: ops.h:226
ff_sws_solve_shuffle
int ff_sws_solve_shuffle(const SwsOpList *const ops, uint8_t shuffle[], int size, uint8_t clear_val, int *read_bytes, int *write_bytes)
"Solve" an op list into a fixed shuffle mask, with an optional ability to also directly clear the out...
Definition: ops_optimizer.c:779
Windows::Graphics::DirectX::Direct3D11::p
IDirect3DDxgiInterfaceAccess _COM_Outptr_ void ** p
Definition: vsrc_gfxcapture_winrt.hpp:53
av_mul_q
AVRational av_mul_q(AVRational b, AVRational c)
Multiply two rationals.
Definition: rational.c:80
AV_PIX_FMT_YUV444P
@ AV_PIX_FMT_YUV444P
planar YUV 4:4:4, 24bpp, (1 Cr & Cb sample per 1x1 Y samples)
Definition: pixfmt.h:78
SWS_COMP_EXACT
@ SWS_COMP_EXACT
Definition: ops.h:83
SwsReadWriteOp::elems
uint8_t elems
Examples: rgba = 4x u8 packed yuv444p = 3x u8 rgb565 = 1x u16 <- use SWS_OP_UNPACK to unpack monow = ...
Definition: ops.h:108
SwsDitherOp::y_offset
int8_t y_offset[4]
Definition: ops.h:170
scale
static void scale(int *out, const int *in, const int w, const int h, const int shift)
Definition: intra.c:278
av_add_q
AVRational av_add_q(AVRational b, AVRational c)
Add two rationals.
Definition: rational.c:93
SwsSwizzleOp::in
uint8_t in[4]
Definition: ops.h:138
SWS_OP_CONVERT
@ SWS_OP_CONVERT
Definition: ops.h:63
SwsOp::scale
SwsScaleOp scale
Definition: ops.h:230
op_commute_clear
static bool op_commute_clear(SwsOp *op, SwsOp *next)
Try to commute a clear op with the next operation.
Definition: ops_optimizer.c:40
SwsOpList::plane_src
uint8_t plane_src[4]
Definition: ops.h:271
SwsOpList
Helper struct for representing a list of operations.
Definition: ops.h:263
av_bswap16
#define av_bswap16
Definition: bswap.h:28
SwsOp::pack
SwsPackOp pack
Definition: ops.h:224
shuffle
static uint64_t shuffle(uint64_t in, const uint8_t *shuffle, int shuffle_len)
Definition: des.c:179
av_log2
int av_log2(unsigned v)
Definition: intmath.c:26
src
#define src
Definition: vp8dsp.c:248
read
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.
Definition: bitstream_template.h:239
exact_log2
static int exact_log2(const int x)
Definition: ops_optimizer.c:217