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