FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libswscale/ops_optimizer.c
Date: 2026-08-15 14:54:27
Exec Total Coverage
Lines: 599 654 91.6%
Functions: 19 19 100.0%
Branches: 432 500 86.4%

Line Branch Exec Source
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/attributes.h"
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 */
41 1163471 static bool op_commute_clear(SwsOp *op, SwsOp *next)
42 {
43 av_assert1(op->op == SWS_OP_CLEAR);
44
5/8
✓ Branch 0 taken 139430 times.
✓ Branch 1 taken 219482 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 159624 times.
✓ Branch 4 taken 57725 times.
✓ Branch 5 taken 587210 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
1163471 switch (next->op) {
45 139430 case SWS_OP_CONVERT:
46 139430 op->type = next->convert.to;
47 av_fallthrough;
48 358912 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 358912 ff_sws_apply_op_q(next, op->clear.value);
56 358912 return true;
57 case SWS_OP_FILTER_H:
58 case SWS_OP_FILTER_V:
59 op->type = next->filter.type;
60 return true;
61 159624 case SWS_OP_SWIZZLE:
62 159624 ff_sws_comp_mask_swizzle(&op->clear.mask, &next->swizzle);
63 159624 ff_sws_apply_op_q(next, op->clear.value);
64 159624 return true;
65 57725 case SWS_OP_SWAP_BYTES:
66
2/2
✓ Branch 0 taken 42486 times.
✓ Branch 1 taken 15239 times.
57725 switch (next->type) {
67 42486 case SWS_PIXEL_U16:
68 case SWS_PIXEL_U32:
69 42486 ff_sws_apply_op_q(next, op->clear.value); /* always representable */
70 42486 return true;
71 15239 default:
72 15239 return false;
73 }
74 587210 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 587210 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 */
96 1413308 static bool op_commute_swizzle(SwsOp *op, SwsOp *next)
97 {
98 1413308 bool seen[4] = {0};
99
100 av_assert1(op->op == SWS_OP_SWIZZLE);
101
5/8
✓ Branch 0 taken 210478 times.
✓ Branch 1 taken 261772 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 94754 times.
✓ Branch 4 taken 68162 times.
✓ Branch 5 taken 778142 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
1413308 switch (next->op) {
102 210478 case SWS_OP_CONVERT:
103 210478 op->type = next->convert.to;
104 av_fallthrough;
105 472250 case SWS_OP_SWAP_BYTES:
106 case SWS_OP_LSHIFT:
107 case SWS_OP_RSHIFT:
108 case SWS_OP_SCALE:
109 472250 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 94754 case SWS_OP_MIN:
125 case SWS_OP_MAX: {
126 94754 const SwsClampOp c = next->clamp;
127
2/2
✓ Branch 0 taken 379016 times.
✓ Branch 1 taken 94754 times.
473770 for (int i = 0; i < 4; i++) {
128
2/2
✓ Branch 0 taken 87711 times.
✓ Branch 1 taken 291305 times.
379016 if (!SWS_OP_NEEDED(op, i))
129 87711 continue;
130 291305 const int j = op->swizzle.in[i];
131
3/4
✓ Branch 0 taken 62094 times.
✓ Branch 1 taken 229211 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 62094 times.
291305 if (seen[j] && av_cmp_q64(next->clamp.limit[j], c.limit[i]))
132 return false;
133 291305 next->clamp.limit[j] = c.limit[i];
134 291305 seen[j] = true;
135 }
136 94754 return true;
137 }
138
139 68162 case SWS_OP_DITHER: {
140 68162 const SwsDitherOp d = next->dither;
141
2/2
✓ Branch 0 taken 256010 times.
✓ Branch 1 taken 59843 times.
315853 for (int i = 0; i < 4; i++) {
142
2/2
✓ Branch 0 taken 55815 times.
✓ Branch 1 taken 200195 times.
256010 if (!SWS_OP_NEEDED(op, i))
143 55815 continue;
144 200195 const int j = op->swizzle.in[i];
145
4/4
✓ Branch 0 taken 36507 times.
✓ Branch 1 taken 163688 times.
✓ Branch 2 taken 8319 times.
✓ Branch 3 taken 28188 times.
200195 if (seen[j] && next->dither.y_offset[j] != d.y_offset[i])
146 8319 return false;
147 191876 next->dither.y_offset[j] = d.y_offset[i];
148 191876 seen[j] = true;
149 }
150 59843 return true;
151 }
152
153 778142 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 778142 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 9642025 static bool op_commute_filter(SwsOp *op, SwsOp *prev)
179 {
180
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9642025 times.
9642025 av_assert0(!ff_sws_pixel_type_is_int(op->filter.type));
181
182
2/4
✓ Branch 0 taken 1168553 times.
✓ Branch 1 taken 8473472 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
9642025 switch (prev->op) {
183 1168553 case SWS_OP_SWIZZLE:
184 case SWS_OP_SCALE:
185 case SWS_OP_LINEAR:
186 case SWS_OP_DITHER:
187 1168553 prev->type = op->filter.type;
188 1168553 return true;
189 8473472 case SWS_OP_CONVERT:
190 case SWS_OP_INVALID:
191 case SWS_OP_READ:
192 case SWS_OP_WRITE:
193 case SWS_OP_SWAP_BYTES:
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 8473472 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 */
214 929798 static int exact_log2(const int x)
215 {
216 int p;
217
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 929798 times.
929798 if (x <= 0)
218 return 0;
219 929798 p = av_log2(x);
220
2/2
✓ Branch 0 taken 377471 times.
✓ Branch 1 taken 552327 times.
929798 return (1 << p) == x ? p : 0;
221 }
222
223 2442860 static int exact_log2_q64(const AVRational64 x)
224 {
225
2/2
✓ Branch 0 taken 496845 times.
✓ Branch 1 taken 1946015 times.
2442860 if (x.den == 1)
226 496845 return exact_log2(x.num);
227
2/2
✓ Branch 0 taken 432953 times.
✓ Branch 1 taken 1513062 times.
1946015 else if (x.num == 1)
228 432953 return -exact_log2(x.den);
229 else
230 1513062 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 */
237 3325335 static bool extract_scalar(const SwsLinearOp *c,
238 const SwsComps *comps, const SwsComps *prev,
239 SwsScaleOp *out_scale)
240 {
241 3325335 SwsScaleOp scale = {0};
242
243 /* There are components not on the main diagonal */
244
2/2
✓ Branch 1 taken 2354737 times.
✓ Branch 2 taken 970598 times.
3325335 if (ff_sws_linear_mask(c) & ~SWS_MASK_DIAG4)
245 2354737 return false;
246
247
2/2
✓ Branch 0 taken 3119267 times.
✓ Branch 1 taken 258534 times.
3377801 for (int i = 0; i < 4; i++) {
248 3119267 const AVRational64 s = c->m[i][i];
249
1/2
✓ Branch 0 taken 3119267 times.
✗ Branch 1 not taken.
3119267 if ((prev->flags[i] & SWS_COMP_ZERO) ||
250
2/2
✓ Branch 0 taken 263943 times.
✓ Branch 1 taken 2855324 times.
3119267 (comps->flags[i] & SWS_COMP_GARBAGE))
251 263943 continue;
252
4/4
✓ Branch 0 taken 1884726 times.
✓ Branch 1 taken 970598 times.
✓ Branch 3 taken 712064 times.
✓ Branch 4 taken 1172662 times.
2855324 if (scale.factor.den && av_cmp_q64(s, scale.factor))
253 712064 return false;
254 2143260 scale.factor = s;
255 }
256
257
1/2
✓ Branch 0 taken 258534 times.
✗ Branch 1 not taken.
258534 if (scale.factor.den)
258 258534 *out_scale = scale;
259 258534 return scale.factor.den;
260 }
261
262 /* Extracts an integer clear operation (subset) from the given linear op. */
263 3494837 static bool extract_constant_rows(SwsLinearOp *c, const SwsComps *prev,
264 SwsClearOp *out_clear)
265 {
266 3494837 const uint32_t mask = ff_sws_linear_mask(c);
267 3494837 SwsClearOp clear = {0};
268 3494837 bool ret = false;
269
270
2/2
✓ Branch 0 taken 13979348 times.
✓ Branch 1 taken 3494837 times.
17474185 for (int i = 0; i < 4; i++) {
271 13979348 bool const_row = c->m[i][4].den == 1; /* offset is integer */
272
2/2
✓ Branch 0 taken 55917392 times.
✓ Branch 1 taken 13979348 times.
69896740 for (int j = 0; j < 4; j++) {
273
2/2
✓ Branch 0 taken 22904210 times.
✓ Branch 1 taken 33013182 times.
78821602 const_row &= c->m[i][j].num == 0 || /* scalar is zero */
274
2/2
✓ Branch 0 taken 348563 times.
✓ Branch 1 taken 22555647 times.
22904210 (prev->flags[j] & SWS_COMP_ZERO); /* input is zero */
275 }
276
3/4
✓ Branch 0 taken 199413 times.
✓ Branch 1 taken 13779935 times.
✓ Branch 2 taken 199413 times.
✗ Branch 3 not taken.
13979348 if (const_row && (mask & SWS_MASK_ROW(i))) {
277 199413 clear.mask |= SWS_COMP(i);
278 199413 clear.value[i] = c->m[i][4];
279
2/2
✓ Branch 0 taken 997065 times.
✓ Branch 1 taken 199413 times.
1196478 for (int j = 0; j < 5; j++)
280 997065 c->m[i][j] = Q(i == j);
281 199413 ret = true;
282 }
283 }
284
285
2/2
✓ Branch 0 taken 169502 times.
✓ Branch 1 taken 3325335 times.
3494837 if (ret)
286 169502 *out_clear = clear;
287 3494837 return ret;
288 }
289
290 /* Unswizzle a linear operation by aligning single-input rows with
291 * their corresponding diagonal */
292 3066801 static bool extract_swizzle(SwsLinearOp *op, const SwsComps *prev,
293 SwsSwizzleOp *out_swiz)
294 {
295 3066801 SwsSwizzleOp swiz = SWS_SWIZZLE(0, 1, 2, 3);
296 3066801 SwsLinearOp c = *op;
297
298 /* Find non-zero coefficients in the main 4x4 matrix */
299 3066801 uint32_t nonzero = 0;
300
2/2
✓ Branch 0 taken 12267204 times.
✓ Branch 1 taken 3066801 times.
15334005 for (int i = 0; i < 4; i++) {
301
2/2
✓ Branch 0 taken 49068816 times.
✓ Branch 1 taken 12267204 times.
61336020 for (int j = 0; j < 4; j++) {
302
4/4
✓ Branch 0 taken 20880788 times.
✓ Branch 1 taken 28188028 times.
✓ Branch 2 taken 117934 times.
✓ Branch 3 taken 20762854 times.
49068816 if (!c.m[i][j].num || (prev->flags[j] & SWS_COMP_ZERO))
303 28305962 continue;
304 20762854 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
2/2
✓ Branch 0 taken 12267204 times.
✓ Branch 1 taken 3066801 times.
15334005 for (int i = 0; i < 4; i++) {
311
2/2
✓ Branch 0 taken 12149270 times.
✓ Branch 1 taken 117934 times.
12267204 if (nonzero & SWS_MASK_COL(i))
312 12149270 continue; /* target column is not empty */
313
1/2
✓ Branch 0 taken 117934 times.
✗ Branch 1 not taken.
117934 for (int j = 0; j < 4; j++) {
314
1/2
✓ Branch 0 taken 117934 times.
✗ Branch 1 not taken.
117934 if ((nonzero & SWS_MASK_ROW(i)) == SWS_MASK(i, j)) {
315 /* Move coefficient to the diagonal */
316 117934 c.m[i][i] = c.m[i][j];
317 117934 c.m[i][j] = Q(0);
318 117934 swiz.in[i] = j;
319 117934 break;
320 }
321 }
322 }
323
324
2/2
✓ Branch 0 taken 3007834 times.
✓ Branch 1 taken 58967 times.
3066801 if (swiz.mask == SWS_SWIZZLE(0, 1, 2, 3).mask)
325 3007834 return false; /* no swizzle was identified */
326
327 58967 *out_swiz = swiz;
328 58967 *op = c;
329 58967 return true;
330 }
331
332 24651 static int op_result_is_exact(const SwsOp *op)
333 {
334
2/2
✓ Branch 0 taken 46566 times.
✓ Branch 1 taken 6845 times.
53411 for (int i = 0; i < 4; i++) {
335
4/4
✓ Branch 0 taken 37031 times.
✓ Branch 1 taken 9535 times.
✓ Branch 2 taken 17806 times.
✓ Branch 3 taken 19225 times.
46566 if (SWS_OP_NEEDED(op, i) && !(op->comps.flags[i] & SWS_COMP_EXACT))
336 17806 return false;
337 }
338
339 6845 return true;
340 }
341
342 1584335 int ff_sws_op_list_optimize(SwsOpList *ops)
343 {
344 int ret;
345
346 10381779 retry:
347 11966114 ff_sws_op_list_update_comps(ops);
348
349 /* Try to push filters towards the input; do this first to unblock
350 * in-place optimizations like linear op fusion */
351
2/2
✓ Branch 0 taken 112366553 times.
✓ Branch 1 taken 10392949 times.
122759502 for (int n = 1; n < ops->num_ops; n++) {
352 112366553 SwsOp *op = &ops->ops[n];
353 112366553 SwsOp *prev = &ops->ops[n - 1];
354
355
2/2
✓ Branch 0 taken 9642025 times.
✓ Branch 1 taken 102724528 times.
112366553 switch (op->op) {
356 9642025 case SWS_OP_FILTER_H:
357 case SWS_OP_FILTER_V:
358
2/2
✓ Branch 1 taken 1168553 times.
✓ Branch 2 taken 8473472 times.
9642025 if (op_commute_filter(op, prev)) {
359 1168553 FFSWAP(SwsOp, *op, *prev);
360 1168553 goto retry;
361 }
362
363 /* Merge filter with prior conversion */
364
3/4
✓ Branch 0 taken 1248041 times.
✓ Branch 1 taken 7225431 times.
✓ Branch 2 taken 1248041 times.
✗ Branch 3 not taken.
8473472 if (prev->op == SWS_OP_CONVERT && !prev->convert.expand) {
365 1248041 int size_from = ff_sws_pixel_type_size(prev->type);
366 1248041 int size_to = ff_sws_pixel_type_size(op->type);
367 av_assert1(prev->convert.to == op->type);
368
2/2
✓ Branch 0 taken 404612 times.
✓ Branch 1 taken 843429 times.
1248041 if (size_from < size_to) {
369 404612 op->type = prev->type;
370 404612 ff_sws_op_list_remove_at(ops, n - 1, 1);
371 404612 goto retry;
372 }
373 }
374 8068860 break;
375 }
376 }
377
378 /* Apply all in-place optimizations (that do not re-order the list) */
379
2/2
✓ Branch 0 taken 59275510 times.
✓ Branch 1 taken 2779049 times.
62054559 for (int n = 0; n < ops->num_ops; n++) {
380 59275510 SwsOp dummy = {0};
381 59275510 SwsOp *op = &ops->ops[n];
382
2/2
✓ Branch 0 taken 48882561 times.
✓ Branch 1 taken 10392949 times.
59275510 SwsOp *prev = n ? &ops->ops[n - 1] : &dummy;
383
2/2
✓ Branch 0 taken 56496461 times.
✓ Branch 1 taken 2779049 times.
59275510 SwsOp *next = n + 1 < ops->num_ops ? &ops->ops[n + 1] : &dummy;
384
385 /* common helper variable */
386 59275510 const SwsCompMask needed = ff_sws_comp_mask_needed(op);
387 59275510 bool noop = true;
388
389
4/4
✓ Branch 0 taken 3210957 times.
✓ Branch 1 taken 56064553 times.
✓ Branch 2 taken 431908 times.
✓ Branch 3 taken 2779049 times.
59275510 if (!needed && op->op != SWS_OP_WRITE) {
390 /* Remove any operation whose output is not needed */
391 431908 ff_sws_op_list_remove_at(ops, n, 1);
392 7613900 goto retry;
393 }
394
395
15/15
✓ Branch 0 taken 9833052 times.
✓ Branch 1 taken 4336147 times.
✓ Branch 2 taken 1649633 times.
✓ Branch 3 taken 849727 times.
✓ Branch 4 taken 3448890 times.
✓ Branch 5 taken 6391801 times.
✓ Branch 6 taken 6147348 times.
✓ Branch 7 taken 2726129 times.
✓ Branch 8 taken 1826634 times.
✓ Branch 9 taken 2947966 times.
✓ Branch 10 taken 5607206 times.
✓ Branch 11 taken 2442860 times.
✓ Branch 12 taken 6231070 times.
✓ Branch 13 taken 1173984 times.
✓ Branch 14 taken 3231155 times.
58843602 switch (op->op) {
396 9833052 case SWS_OP_READ:
397 /* "Compress" planar reads where not all components are needed */
398
2/2
✓ Branch 0 taken 5854888 times.
✓ Branch 1 taken 3978164 times.
9833052 if (op->rw.mode == SWS_RW_PLANAR) {
399 5854888 SwsSwizzleOp swiz = SWS_SWIZZLE(0, 1, 2, 3);
400 5854888 int nb_planes = 0;
401
2/2
✓ Branch 0 taken 14891880 times.
✓ Branch 1 taken 5854888 times.
20746768 for (int i = 0; i < op->rw.elems; i++) {
402
2/2
✓ Branch 0 taken 84160 times.
✓ Branch 1 taken 14807720 times.
14891880 if (!SWS_OP_NEEDED(op, i)) {
403 84160 swiz.in[i] = 3 - (i - nb_planes); /* map to unused plane */
404 84160 continue;
405 }
406
407 14807720 const int idx = nb_planes++;
408 av_assert1(idx <= i);
409 14807720 ops->plane_src[idx] = ops->plane_src[i];
410 14807720 swiz.in[i] = idx;
411 }
412
413
2/2
✓ Branch 0 taken 70889 times.
✓ Branch 1 taken 5783999 times.
5854888 if (nb_planes < op->rw.elems) {
414 70889 op->rw.elems = nb_planes;
415
3/4
✓ Branch 0 taken 3944 times.
✓ Branch 1 taken 66945 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 70889 times.
70889 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 70889 goto retry;
421 }
422 }
423 9762163 break;
424
425 4336147 case SWS_OP_SWAP_BYTES:
426 /* Redundant (double) swap */
427
2/2
✓ Branch 0 taken 882 times.
✓ Branch 1 taken 4335265 times.
4336147 if (next->op == SWS_OP_SWAP_BYTES) {
428 882 ff_sws_op_list_remove_at(ops, n, 2);
429 882 goto retry;
430 }
431 4335265 break;
432
433 1649633 case SWS_OP_UNPACK:
434 /* Redundant unpack+pack */
435
3/4
✓ Branch 0 taken 220 times.
✓ Branch 1 taken 1649413 times.
✓ Branch 2 taken 220 times.
✗ Branch 3 not taken.
1649633 if (next->op == SWS_OP_PACK && next->type == op->type &&
436
1/2
✓ Branch 0 taken 220 times.
✗ Branch 1 not taken.
220 next->pack.pattern[0] == op->pack.pattern[0] &&
437
1/2
✓ Branch 0 taken 220 times.
✗ Branch 1 not taken.
220 next->pack.pattern[1] == op->pack.pattern[1] &&
438
1/2
✓ Branch 0 taken 220 times.
✗ Branch 1 not taken.
220 next->pack.pattern[2] == op->pack.pattern[2] &&
439
1/2
✓ Branch 0 taken 220 times.
✗ Branch 1 not taken.
220 next->pack.pattern[3] == op->pack.pattern[3])
440 {
441 220 ff_sws_op_list_remove_at(ops, n, 2);
442 220 goto retry;
443 }
444 1649413 break;
445
446 849727 case SWS_OP_LSHIFT:
447 case SWS_OP_RSHIFT:
448 /* Two shifts in the same direction */
449
2/2
✓ Branch 0 taken 772 times.
✓ Branch 1 taken 848955 times.
849727 if (next->op == op->op) {
450 772 op->shift.amount += next->shift.amount;
451 772 ff_sws_op_list_remove_at(ops, n + 1, 1);
452 772 goto retry;
453 }
454
455 /* No-op shift */
456
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 848955 times.
848955 if (!op->shift.amount) {
457 ff_sws_op_list_remove_at(ops, n, 1);
458 goto retry;
459 }
460 848955 break;
461
462 3448890 case SWS_OP_CLEAR:
463
2/2
✓ Branch 0 taken 13795560 times.
✓ Branch 1 taken 3448890 times.
17244450 for (int i = 0; i < 4; i++) {
464
2/2
✓ Branch 0 taken 9261088 times.
✓ Branch 1 taken 4534472 times.
13795560 if (!SWS_COMP_TEST(op->clear.mask, i))
465 9261088 continue;
466
467
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4534472 times.
4534472 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
2/2
✓ Branch 0 taken 727341 times.
✓ Branch 1 taken 3807131 times.
4534472 } else if (!SWS_OP_NEEDED(op, i)) {
474 /* Unnecessary clear of unused component */
475 727341 op->clear.mask ^= SWS_COMP(i);
476 } else {
477 3807131 noop = false;
478 }
479 }
480
481
2/2
✓ Branch 0 taken 660634 times.
✓ Branch 1 taken 2788256 times.
3448890 if (noop) {
482 660634 ff_sws_op_list_remove_at(ops, n, 1);
483 660634 goto retry;
484 }
485
486 /* Transitive clear */
487
2/2
✓ Branch 0 taken 2643 times.
✓ Branch 1 taken 2785613 times.
2788256 if (next->op == SWS_OP_CLEAR) {
488
2/2
✓ Branch 0 taken 10572 times.
✓ Branch 1 taken 2643 times.
13215 for (int i = 0; i < 4; i++) {
489
2/2
✓ Branch 0 taken 2643 times.
✓ Branch 1 taken 7929 times.
10572 if (SWS_COMP_TEST(next->clear.mask, i))
490 2643 op->clear.value[i] = next->clear.value[i];
491 }
492 2643 op->clear.mask |= next->clear.mask;
493 2643 ff_sws_op_list_remove_at(ops, n + 1, 1);
494 2643 goto retry;
495 }
496 2785613 break;
497
498 6391801 case SWS_OP_SWIZZLE:
499
2/2
✓ Branch 0 taken 25567204 times.
✓ Branch 1 taken 6391801 times.
31959005 for (int i = 0; i < 4; i++) {
500
2/2
✓ Branch 0 taken 6324406 times.
✓ Branch 1 taken 19242798 times.
25567204 if (!SWS_OP_NEEDED(op, i))
501 6324406 continue;
502
2/2
✓ Branch 0 taken 13139669 times.
✓ Branch 1 taken 6103129 times.
19242798 if (op->swizzle.in[i] != i)
503 13139669 noop = false;
504 }
505
506 /* Identity swizzle */
507
2/2
✓ Branch 0 taken 992522 times.
✓ Branch 1 taken 5399279 times.
6391801 if (noop) {
508 992522 ff_sws_op_list_remove_at(ops, n, 1);
509 992522 goto retry;
510 }
511
512 /* Transitive swizzle */
513
2/2
✓ Branch 0 taken 104280 times.
✓ Branch 1 taken 5294999 times.
5399279 if (next->op == SWS_OP_SWIZZLE) {
514 104280 const SwsSwizzleOp orig = op->swizzle;
515
2/2
✓ Branch 0 taken 417120 times.
✓ Branch 1 taken 104280 times.
521400 for (int i = 0; i < 4; i++)
516 417120 op->swizzle.in[i] = orig.in[next->swizzle.in[i]];
517 104280 ff_sws_op_list_remove_at(ops, n + 1, 1);
518 104280 goto retry;
519 }
520
521 /* Swizzle planes instead of components, if possible */
522
4/4
✓ Branch 0 taken 772119 times.
✓ Branch 1 taken 4522880 times.
✓ Branch 2 taken 352485 times.
✓ Branch 3 taken 419634 times.
5294999 if (prev->op == SWS_OP_READ && prev->rw.mode == SWS_RW_PLANAR) {
523
2/2
✓ Branch 0 taken 490136 times.
✓ Branch 1 taken 100016 times.
590152 for (int dst = 0; dst < prev->rw.elems; dst++) {
524 490136 const int src = op->swizzle.in[dst];
525
4/4
✓ Branch 0 taken 261074 times.
✓ Branch 1 taken 229062 times.
✓ Branch 2 taken 252469 times.
✓ Branch 3 taken 8605 times.
490136 if (src > dst && src < prev->rw.elems) {
526 252469 FFSWAP(int, ops->plane_src[dst], ops->plane_src[src]);
527
2/2
✓ Branch 0 taken 902636 times.
✓ Branch 1 taken 252469 times.
1155105 for (int i = dst; i < 4; i++) {
528
2/2
✓ Branch 0 taken 264708 times.
✓ Branch 1 taken 637928 times.
902636 if (op->swizzle.in[i] == dst)
529 264708 op->swizzle.in[i] = src;
530
2/2
✓ Branch 0 taken 252469 times.
✓ Branch 1 taken 385459 times.
637928 else if (op->swizzle.in[i] == src)
531 252469 op->swizzle.in[i] = dst;
532 }
533 252469 goto retry;
534 }
535 }
536 }
537
538
4/4
✓ Branch 0 taken 747737 times.
✓ Branch 1 taken 4294793 times.
✓ Branch 2 taken 485095 times.
✓ Branch 3 taken 262642 times.
5042530 if (next->op == SWS_OP_WRITE && next->rw.mode == SWS_RW_PLANAR) {
539
2/2
✓ Branch 0 taken 666822 times.
✓ Branch 1 taken 140529 times.
807351 for (int dst = 0; dst < next->rw.elems; dst++) {
540 666822 const int src = op->swizzle.in[dst];
541
4/4
✓ Branch 0 taken 465020 times.
✓ Branch 1 taken 201802 times.
✓ Branch 2 taken 344566 times.
✓ Branch 3 taken 120454 times.
666822 if (src > dst && src < next->rw.elems) {
542 344566 FFSWAP(int, ops->plane_dst[dst], ops->plane_dst[src]);
543 344566 FFSWAP(int, op->swizzle.in[dst], op->swizzle.in[src]);
544 344566 goto retry;
545 }
546 }
547 }
548 4697964 break;
549
550 6147348 case SWS_OP_CONVERT:
551 /* No-op conversion */
552
2/2
✓ Branch 0 taken 163822 times.
✓ Branch 1 taken 5983526 times.
6147348 if (op->type == op->convert.to) {
553 163822 ff_sws_op_list_remove_at(ops, n, 1);
554 163822 goto retry;
555 }
556
557 /* Transitive conversion */
558
2/2
✓ Branch 0 taken 140919 times.
✓ Branch 1 taken 5842607 times.
5983526 if (next->op == SWS_OP_CONVERT &&
559
1/2
✓ Branch 0 taken 140919 times.
✗ Branch 1 not taken.
140919 op->convert.expand == next->convert.expand)
560 {
561 av_assert1(op->convert.to == next->type);
562 140919 op->convert.to = next->convert.to;
563 140919 ff_sws_op_list_remove_at(ops, n + 1, 1);
564 140919 goto retry;
565 }
566
567 /* Conversion followed by integer expansion */
568
3/4
✓ Branch 0 taken 400481 times.
✓ Branch 1 taken 5442126 times.
✓ Branch 2 taken 400481 times.
✗ Branch 3 not taken.
5842607 if (next->op == SWS_OP_SCALE && !op->convert.expand &&
569
2/2
✓ Branch 0 taken 400105 times.
✓ Branch 1 taken 376 times.
400481 ff_sws_pixel_type_is_int(op->type) &&
570
4/4
✓ Branch 0 taken 4321 times.
✓ Branch 1 taken 395784 times.
✓ Branch 2 taken 1332 times.
✓ Branch 3 taken 2989 times.
404426 ff_sws_pixel_type_is_int(op->convert.to) &&
571 4321 !av_cmp_q64(next->scale.factor,
572 ff_sws_pixel_expand(op->type, op->convert.to)))
573 {
574 1332 op->convert.expand = true;
575 1332 ff_sws_op_list_remove_at(ops, n + 1, 1);
576 1332 goto retry;
577 }
578 5841275 break;
579
580 2726129 case SWS_OP_MIN:
581
2/2
✓ Branch 0 taken 10904516 times.
✓ Branch 1 taken 2726129 times.
13630645 for (int i = 0; i < 4; i++) {
582
4/4
✓ Branch 0 taken 8183931 times.
✓ Branch 1 taken 2720585 times.
✓ Branch 2 taken 482050 times.
✓ Branch 3 taken 7701881 times.
10904516 if (!SWS_OP_NEEDED(op, i) || !op->clamp.limit[i].den)
583 3202635 continue;
584
2/2
✓ Branch 1 taken 979206 times.
✓ Branch 2 taken 6722675 times.
7701881 if (av_cmp_q64(op->clamp.limit[i], prev->comps.max[i]) >= 0)
585 979206 op->clamp.limit[i] = (AVRational64) {0}; /* no-op */
586 else
587 6722675 noop = false;
588 }
589
590
2/2
✓ Branch 0 taken 281511 times.
✓ Branch 1 taken 2444618 times.
2726129 if (noop) {
591 281511 ff_sws_op_list_remove_at(ops, n, 1);
592 281511 goto retry;
593 }
594 2444618 break;
595
596 1826634 case SWS_OP_MAX:
597
2/2
✓ Branch 0 taken 7306536 times.
✓ Branch 1 taken 1826634 times.
9133170 for (int i = 0; i < 4; i++) {
598
4/4
✓ Branch 0 taken 5492102 times.
✓ Branch 1 taken 1814434 times.
✓ Branch 2 taken 251503 times.
✓ Branch 3 taken 5240599 times.
7306536 if (!SWS_OP_NEEDED(op, i) || !op->clamp.limit[i].den)
599 2065937 continue;
600
2/2
✓ Branch 1 taken 1509727 times.
✓ Branch 2 taken 3730872 times.
5240599 if (av_cmp_q64(prev->comps.min[i], op->clamp.limit[i]) >= 0)
601 1509727 op->clamp.limit[i] = (AVRational64) {0};
602 else
603 3730872 noop = false;
604 }
605
606
2/2
✓ Branch 0 taken 466779 times.
✓ Branch 1 taken 1359855 times.
1826634 if (noop) {
607 466779 ff_sws_op_list_remove_at(ops, n, 1);
608 466779 goto retry;
609 }
610 1359855 break;
611
612 2947966 case SWS_OP_DITHER:
613
2/2
✓ Branch 0 taken 11479639 times.
✓ Branch 1 taken 2405814 times.
13885453 for (int i = 0; i < 4; i++) {
614
2/2
✓ Branch 0 taken 3303134 times.
✓ Branch 1 taken 8176505 times.
11479639 if (op->dither.y_offset[i] < 0)
615 3303134 continue;
616
4/4
✓ Branch 0 taken 7790906 times.
✓ Branch 1 taken 385599 times.
✓ Branch 2 taken 156553 times.
✓ Branch 3 taken 7634353 times.
8176505 if (!SWS_OP_NEEDED(op, i) || (prev->comps.flags[i] & SWS_COMP_EXACT)) {
617 542152 op->dither.y_offset[i] = -1; /* unnecessary dither */
618 542152 goto retry;
619 } else {
620 7634353 noop = false;
621 }
622 }
623
624
2/2
✓ Branch 0 taken 9645 times.
✓ Branch 1 taken 2396169 times.
2405814 if (noop) {
625 9645 ff_sws_op_list_remove_at(ops, n, 1);
626 9645 goto retry;
627 }
628 2396169 break;
629
630 5607206 case SWS_OP_LINEAR: {
631 5607206 const uint32_t mask = ff_sws_linear_mask(&op->lin);
632 SwsSwizzleOp swizzle;
633 SwsClearOp clear;
634 SwsScaleOp scale;
635
636 /* No-op (identity) linear operation */
637
2/2
✓ Branch 0 taken 68351 times.
✓ Branch 1 taken 5538855 times.
5607206 if (!mask) {
638 68351 ff_sws_op_list_remove_at(ops, n, 1);
639 2599372 goto retry;
640 }
641
642
2/2
✓ Branch 0 taken 1266683 times.
✓ Branch 1 taken 4272172 times.
5538855 if (next->op == SWS_OP_LINEAR) {
643 /* 5x5 matrix multiplication after appending [ 0 0 0 0 1 ] */
644 1266683 const SwsLinearOp m1 = op->lin;
645 1266683 const SwsLinearOp m2 = next->lin;
646
2/2
✓ Branch 0 taken 5066732 times.
✓ Branch 1 taken 1266683 times.
6333415 for (int i = 0; i < 4; i++) {
647
2/2
✓ Branch 0 taken 25333660 times.
✓ Branch 1 taken 5066732 times.
30400392 for (int j = 0; j < 5; j++) {
648 25333660 AVRational64 sum = Q(0);
649
2/2
✓ Branch 0 taken 101334640 times.
✓ Branch 1 taken 25333660 times.
126668300 for (int k = 0; k < 4; k++)
650 101334640 sum = av_add_q64(sum, av_mul_q64(m2.m[i][k], m1.m[k][j]));
651
2/2
✓ Branch 0 taken 5066732 times.
✓ Branch 1 taken 20266928 times.
25333660 if (j == 4) /* m1.m[4][j] == 1 */
652 5066732 sum = av_add_q64(sum, m2.m[i][4]);
653 25333660 op->lin.m[i][j] = sum;
654 }
655 }
656 1266683 ff_sws_op_list_remove_at(ops, n + 1, 1);
657 1266683 goto retry;
658 }
659
660 /* Optimize away zero columns */
661
2/2
✓ Branch 0 taken 16845079 times.
✓ Branch 1 taken 4023975 times.
20869054 for (int j = 0; j < 4; j++) {
662 16845079 const uint32_t col = SWS_MASK_COL(j);
663
4/4
✓ Branch 0 taken 813345 times.
✓ Branch 1 taken 16031734 times.
✓ Branch 2 taken 565148 times.
✓ Branch 3 taken 248197 times.
16845079 if (!(prev->comps.flags[j] & SWS_COMP_ZERO) || !(mask & col))
664 16596882 continue;
665
2/2
✓ Branch 0 taken 992788 times.
✓ Branch 1 taken 248197 times.
1240985 for (int i = 0; i < 4; i++)
666 992788 op->lin.m[i][j] = Q(i == j);
667 248197 goto retry;
668 }
669
670 /* Optimize away unused rows */
671
2/2
✓ Branch 0 taken 15849111 times.
✓ Branch 1 taken 3494837 times.
19343948 for (int i = 0; i < 4; i++) {
672 15849111 const uint32_t row = SWS_MASK_ROW(i);
673
4/4
✓ Branch 0 taken 4878065 times.
✓ Branch 1 taken 10971046 times.
✓ Branch 2 taken 4348927 times.
✓ Branch 3 taken 529138 times.
15849111 if (SWS_OP_NEEDED(op, i) || !(mask & row))
674 15319973 continue;
675
2/2
✓ Branch 0 taken 2645690 times.
✓ Branch 1 taken 529138 times.
3174828 for (int j = 0; j < 5; j++)
676 2645690 op->lin.m[i][j] = Q(i == j);
677 529138 goto retry;
678 }
679
680 /* Convert constant rows to explicit clear instruction */
681
2/2
✓ Branch 1 taken 169502 times.
✓ Branch 2 taken 3325335 times.
3494837 if (extract_constant_rows(&op->lin, &prev->comps, &clear)) {
682
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 169502 times.
169502 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 169502 goto retry;
689 }
690
691 /* Multiplication by scalar constant */
692
2/2
✓ Branch 1 taken 258534 times.
✓ Branch 2 taken 3066801 times.
3325335 if (extract_scalar(&op->lin, &op->comps, &prev->comps, &scale)) {
693 258534 op->op = SWS_OP_SCALE;
694 258534 op->scale = scale;
695 258534 goto retry;
696 }
697
698 /* Swizzle by fixed pattern */
699
2/2
✓ Branch 1 taken 58967 times.
✓ Branch 2 taken 3007834 times.
3066801 if (extract_swizzle(&op->lin, &prev->comps, &swizzle)) {
700
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 58967 times.
58967 RET(ff_sws_op_list_insert_at(ops, n, &(SwsOp) {
701 .op = SWS_OP_SWIZZLE,
702 .type = op->type,
703 .swizzle = swizzle,
704 }));
705 58967 goto retry;
706 }
707 3007834 break;
708 }
709
710 2442860 case SWS_OP_SCALE: {
711 2442860 const int factor2 = exact_log2_q64(op->scale.factor);
712
713 /* No-op scaling */
714
3/4
✓ Branch 0 taken 432953 times.
✓ Branch 1 taken 2009907 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 432953 times.
2442860 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
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2442860 times.
2442860 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
4/4
✓ Branch 0 taken 377471 times.
✓ Branch 1 taken 2065389 times.
✓ Branch 2 taken 3667 times.
✓ Branch 3 taken 373804 times.
2442860 if (factor2 && ff_sws_pixel_type_is_int(op->type)) {
728
1/2
✓ Branch 0 taken 3667 times.
✗ Branch 1 not taken.
3667 op->op = factor2 > 0 ? SWS_OP_LSHIFT : SWS_OP_RSHIFT;
729 3667 op->shift.amount = FFABS(factor2);
730 3667 goto retry;
731 }
732 2439193 break;
733 }
734
735 6231070 case SWS_OP_FILTER_H:
736 case SWS_OP_FILTER_V:
737 /* Merge with prior simple planar read */
738
4/4
✓ Branch 0 taken 1903873 times.
✓ Branch 1 taken 4327197 times.
✓ Branch 2 taken 1440191 times.
✓ Branch 3 taken 463682 times.
6231070 if (prev->op == SWS_OP_READ && !prev->rw.filter.op &&
739
4/4
✓ Branch 0 taken 602191 times.
✓ Branch 1 taken 838000 times.
✓ Branch 2 taken 542178 times.
✓ Branch 3 taken 60013 times.
1440191 prev->rw.mode == SWS_RW_PLANAR && !prev->rw.frac) {
740 542178 prev->rw.filter.op = op->op;
741 542178 prev->rw.filter.kernel = av_refstruct_ref(op->filter.kernel);
742 542178 prev->rw.filter.type = op->filter.type;
743 542178 ff_sws_op_list_remove_at(ops, n, 1);
744 542178 goto retry;
745 }
746 5688892 break;
747
748 1173984 case SWS_OP_LUT_3D:
749 /* Eliminate unnecessary 3DLUT */
750
2/2
✓ Branch 0 taken 738 times.
✓ Branch 1 taken 1173246 times.
1173984 if (!(needed & SWS_COMP_ELEMS(3))) {
751 738 ff_sws_op_list_remove_at(ops, n, 1);
752 738 goto retry;
753 }
754 1173246 break;
755 }
756 }
757
758 /* Push clears to the back to void any unused components */
759
2/2
✓ Branch 0 taken 18984579 times.
✓ Branch 1 taken 2218027 times.
21202606 for (int n = 0; n < ops->num_ops - 1; n++) {
760 18984579 SwsOp *op = &ops->ops[n];
761 18984579 SwsOp *next = &ops->ops[n + 1];
762
763
2/2
✓ Branch 0 taken 1163471 times.
✓ Branch 1 taken 17821108 times.
18984579 switch (op->op) {
764 1163471 case SWS_OP_CLEAR:
765
2/2
✓ Branch 1 taken 561022 times.
✓ Branch 2 taken 602449 times.
1163471 if (op_commute_clear(op, next)) {
766 561022 FFSWAP(SwsOp, *op, *next);
767 561022 goto retry;
768 }
769 602449 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
2/2
✓ Branch 0 taken 12808866 times.
✓ Branch 1 taken 1584335 times.
14393201 for (int n = 0; n < ops->num_ops - 1; n++) {
777 12808866 SwsOp *op = &ops->ops[n];
778 12808866 SwsOp *next = &ops->ops[n + 1];
779
780
3/3
✓ Branch 0 taken 1413308 times.
✓ Branch 1 taken 758910 times.
✓ Branch 2 taken 10636648 times.
12808866 switch (op->op) {
781 1413308 case SWS_OP_SWIZZLE: {
782 /* Try to push swizzles towards the output */
783
2/2
✓ Branch 1 taken 626847 times.
✓ Branch 2 taken 786461 times.
1413308 if (op_commute_swizzle(op, next)) {
784 626847 FFSWAP(SwsOp, *op, *next);
785 626847 goto retry;
786 }
787 786461 break;
788 }
789
790 758910 case SWS_OP_SCALE:
791 /* Exact integer multiplication */
792
4/4
✓ Branch 0 taken 178491 times.
✓ Branch 1 taken 580419 times.
✓ Branch 2 taken 24651 times.
✓ Branch 3 taken 153840 times.
758910 if (op->scale.factor.den == 1 && next->op == SWS_OP_CONVERT &&
793
3/4
✓ Branch 0 taken 24651 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6845 times.
✓ Branch 3 taken 17806 times.
49302 ff_sws_pixel_type_is_int(next->convert.to) &&
794 24651 op_result_is_exact(op))
795 {
796 6845 op->type = next->convert.to;
797 6845 FFSWAP(SwsOp, *op, *next);
798 6845 goto retry;
799 }
800 752065 break;
801 }
802 }
803
804 1584335 return 0;
805 }
806
807 107854 static int select_planes(SwsOpList *ops, SwsCompMask planes)
808 {
809 107854 SwsSwizzleOp swiz = SWS_SWIZZLE(0, 1, 2, 3);
810 107854 SwsOp *write = &ops->ops[ops->num_ops - 1];
811
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 107854 times.
107854 av_assert0(write->op == SWS_OP_WRITE);
812
813 107854 write->rw.elems = 0;
814
2/2
✓ Branch 0 taken 431416 times.
✓ Branch 1 taken 107854 times.
539270 for (int src = 0; src < 4; src++) {
815
2/2
✓ Branch 0 taken 223172 times.
✓ Branch 1 taken 208244 times.
431416 if (!SWS_COMP_TEST(planes, src))
816 223172 continue; /* plane not selected */
817 208244 const int dst = write->rw.elems++;
818 av_assert2(src >= dst);
819 208244 swiz.in[dst] = src;
820 208244 FFSWAP(int, ops->plane_dst[dst], ops->plane_dst[src]);
821 }
822
823 /* Insert swizzle to select desired planes */
824 107854 int ret = ff_sws_op_list_insert_at(ops, ops->num_ops - 1, &(SwsOp) {
825 .op = SWS_OP_SWIZZLE,
826 107854 .type = write->type,
827 .swizzle = swiz,
828 });
829
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 107854 times.
107854 if (ret < 0)
830 return ret;
831
832 /* The optimizer will take care of the rest */
833 107854 return ff_sws_op_list_optimize(ops);
834 }
835
836 1530878 int ff_sws_op_list_split_planes(SwsOpList *ops1, SwsOpList **out_ops2, SwsCompMask planes)
837 {
838 1530878 const SwsOp *write = ff_sws_op_list_output(ops1);
839
3/4
✓ Branch 0 taken 1530878 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 474675 times.
✓ Branch 3 taken 1056203 times.
1530878 if (!write || write->rw.mode != SWS_RW_PLANAR) {
840 474675 *out_ops2 = NULL;
841 474675 return 0;
842 }
843
844 1056203 const SwsCompMask full = SWS_COMP_ELEMS(write->rw.elems);
845 1056203 const SwsCompMask mask1 = planes & full;
846 1056203 const SwsCompMask mask2 = full ^ mask1;
847
4/4
✓ Branch 0 taken 108155 times.
✓ Branch 1 taken 948048 times.
✓ Branch 2 taken 54228 times.
✓ Branch 3 taken 53927 times.
1056203 if (!mask1 || !mask2) {
848 /* Nothing to filter */
849 1002276 *out_ops2 = NULL;
850 1002276 return 0;
851 }
852
853 53927 SwsOpList *ops2 = ff_sws_op_list_duplicate(ops1);
854
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 53927 times.
53927 if (!ops2)
855 return AVERROR(ENOMEM);
856
857 int ret;
858
2/4
✓ Branch 1 taken 53927 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 53927 times.
107854 if ((ret = select_planes(ops1, mask1)) < 0 ||
859 53927 (ret = select_planes(ops2, mask2)) < 0)
860 {
861 ff_sws_op_list_free(&ops2);
862 return ret;
863 }
864
865 53927 *out_ops2 = ops2;
866 53927 return 0;
867 }
868
869 32396 int ff_sws_shuffle_mask(const SwsUOp *uop, int8_t shuffle[], int size)
870 {
871 32396 const SwsShuffleUOp *par = &uop->par.shuffle;
872 av_assert1(uop->uop == SWS_UOP_RW_SHUFFLE);
873 av_assert1(par->write_size <= sizeof(uop->data.shuffle.mask));
874 av_assert1(size <= INT8_MAX);
875
876 32396 const int num_groups = size / FFMAX(par->read_size, par->write_size);
877
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 32396 times.
32396 if (!num_groups)
878 return AVERROR(EINVAL);
879
880 32396 memset(shuffle, 0, size);
881
2/2
✓ Branch 0 taken 178281 times.
✓ Branch 1 taken 32396 times.
210677 for (int n = 0; n < num_groups; n++) {
882 178281 const int base_in = n * par->read_size;
883 178281 const int base_out = n * par->write_size;
884
2/2
✓ Branch 0 taken 418716 times.
✓ Branch 1 taken 178281 times.
596997 for (int i = 0; i < par->write_size; i++) {
885 418716 const int8_t idx = uop->data.shuffle.mask[i];
886 418716 shuffle[base_out + i] = idx + (idx >= 0) * base_in;
887 }
888 }
889
890 32396 return num_groups;
891 }
892
893 936 static bool pixel_is_repeating(SwsPixelType type, SwsPixel val)
894 {
895
3/4
✓ Branch 0 taken 453 times.
✓ Branch 1 taken 398 times.
✓ Branch 2 taken 85 times.
✗ Branch 3 not taken.
936 switch (ff_sws_pixel_type_size(type)) {
896 453 case 1: return true;
897 398 case 2: return val.u16 == val.u8 * 0x101ul;
898 85 case 4: return val.u32 == val.u8 * 0x1010101ul;
899 default: break;
900 }
901
902 av_unreachable("Invalid pixel type!");
903 return false;
904 }
905
906 881035 static int solve_shuffle(const SwsUOpList *const uops, SwsUOp *out)
907 {
908
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 881035 times.
881035 if (!uops->num_ops)
909 return AVERROR(EINVAL);
910 881035 const SwsUOp *read = &uops->ops[0];
911
3/3
✓ Branch 0 taken 133490 times.
✓ Branch 1 taken 232771 times.
✓ Branch 2 taken 514774 times.
881035 switch (read->uop) {
912 133490 case SWS_UOP_READ_PACKED:
913 133490 break;
914 232771 case SWS_UOP_READ_PLANAR:
915
2/2
✓ Branch 0 taken 115355 times.
✓ Branch 1 taken 117416 times.
232771 if (read->mask != SWS_COMP_ELEMS(1))
916 115355 return AVERROR(ENOTSUP);
917 117416 break;
918 514774 default:
919 514774 return AVERROR(ENOTSUP);
920 }
921
922 250906 const int read_size = ff_sws_pixel_type_size(read->type);
923 250906 uint32_t mask[4] = {0};
924 250906 int clear_val = -1;
925 250906 int read_elems = 0;
926
2/2
✓ Branch 0 taken 1003624 times.
✓ Branch 1 taken 250906 times.
1254530 for (int i = 0; i < 4; i++) {
927
2/2
✓ Branch 0 taken 583827 times.
✓ Branch 1 taken 419797 times.
1003624 if (SWS_COMP_TEST(read->mask, i)) {
928 583827 mask[i] = 0x01010101 * i * read_size + 0x03020100;
929 583827 read_elems++;
930 }
931 }
932
933
1/2
✓ Branch 0 taken 370088 times.
✗ Branch 1 not taken.
370088 for (int opidx = 1; opidx < uops->num_ops; opidx++) {
934 370088 const SwsUOp *uop = &uops->ops[opidx];
935 370088 const SwsUOpParams *par = &uop->par;
936
7/7
✓ Branch 0 taken 17549 times.
✓ Branch 1 taken 100036 times.
✓ Branch 2 taken 932 times.
✓ Branch 3 taken 780 times.
✓ Branch 4 taken 111486 times.
✓ Branch 5 taken 1838 times.
✓ Branch 6 taken 137467 times.
370088 switch (uop->uop) {
937 17549 case SWS_UOP_COPY:
938 case SWS_UOP_PERMUTE: {
939 uint32_t tmp;
940
2/2
✓ Branch 0 taken 21534 times.
✓ Branch 1 taken 17549 times.
39083 for (int i = 0; i < par->move.num_moves; i++) {
941 21534 const int dst_idx = par->move.dst[i];
942 21534 const int src_idx = par->move.src[i];
943
2/2
✓ Branch 0 taken 790 times.
✓ Branch 1 taken 20744 times.
21534 uint32_t *src = src_idx < 0 ? &tmp : &mask[src_idx];
944
2/2
✓ Branch 0 taken 790 times.
✓ Branch 1 taken 20744 times.
21534 uint32_t *dst = dst_idx < 0 ? &tmp : &mask[dst_idx];
945 21534 *dst = *src;
946 }
947 17549 break;
948 }
949
950 100036 case SWS_UOP_SWAP_BYTES:
951
2/2
✓ Branch 0 taken 400144 times.
✓ Branch 1 taken 100036 times.
500180 for (int i = 0; i < 4; i++) {
952
2/3
✓ Branch 0 taken 279980 times.
✓ Branch 1 taken 120164 times.
✗ Branch 2 not taken.
400144 switch (ff_sws_pixel_type_size(uop->type)) {
953 279980 case 2: mask[i] = av_bswap16(mask[i]); break;
954 120164 case 4: mask[i] = av_bswap32(mask[i]); break;
955 }
956 }
957 100036 break;
958
959 932 case SWS_UOP_CLEAR:
960
2/2
✓ Branch 0 taken 3688 times.
✓ Branch 1 taken 817 times.
4505 for (int i = 0; i < 4; i++) {
961
2/2
✓ Branch 0 taken 2752 times.
✓ Branch 1 taken 936 times.
3688 if (!SWS_COMP_TEST(uop->mask, i))
962 2752 continue;
963 936 SwsPixel val = uop->data.vec4[i];
964
4/4
✓ Branch 1 taken 821 times.
✓ Branch 2 taken 115 times.
✓ Branch 3 taken 4 times.
✓ Branch 4 taken 817 times.
936 if (!pixel_is_repeating(uop->type, val) ||
965
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 (clear_val >= 0 && clear_val != val.u8))
966 115 return AVERROR(ENOTSUP); /* would require different bytes */
967 821 mask[i] = 0xFFFFFFFFul; /* (uint8_t[4]) { -1, -1, -1, -1 } */
968 821 clear_val = val.u8;
969 }
970 817 break;
971
972 780 case SWS_UOP_EXPAND_PAIR:
973 case SWS_UOP_EXPAND_QUAD:
974
2/2
✓ Branch 0 taken 3120 times.
✓ Branch 1 taken 780 times.
3900 for (int i = 0; i < 4; i++)
975 3120 mask[i] = 0x01010101 * (mask[i] & 0xFF);
976 780 break;
977
978 111486 case SWS_UOP_WRITE_PLANAR:
979
2/2
✓ Branch 0 taken 83493 times.
✓ Branch 1 taken 27993 times.
111486 if (uop->mask != SWS_COMP_ELEMS(1))
980 83493 return AVERROR(ENOTSUP);
981 av_fallthrough;
982 case SWS_UOP_WRITE_PACKED: {
983 29831 const int write_elems = av_popcount(uop->mask);
984 29831 const int write_size = ff_sws_pixel_type_size(uop->type);
985 29831 *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 29831 SwsShuffleUOp *par = &out->par.shuffle;
992 29831 SwsShuffleMask *data = &out->data.shuffle;
993 29831 *par = (SwsShuffleUOp) {
994 .read_size = read_elems * read_size,
995 .write_size = write_elems * write_size,
996 29831 .clear_value = clear_val >= 0 ? clear_val : 0,
997 };
998
999 /* Generate baseline shuffle for a single pixel */
1000 29831 data->pixels = 1;
1001
2/2
✓ Branch 0 taken 34525 times.
✓ Branch 1 taken 29831 times.
64356 for (int i = 0; i < write_elems; i++) {
1002 34525 const int offset = i * write_size;
1003
2/2
✓ Branch 0 taken 76420 times.
✓ Branch 1 taken 34525 times.
110945 for (int b = 0; b < write_size; b++)
1004 76420 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 29831 const int num_groups = ff_sws_shuffle_mask(out, tmp, sizeof(tmp));
1011
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 29831 times.
29831 if (num_groups < 0)
1012 return num_groups;
1013 29831 memcpy(data->mask, tmp, sizeof(tmp));
1014 29831 par->read_size *= num_groups;
1015 29831 par->write_size *= num_groups;
1016 29831 data->pixels = num_groups;
1017 29831 return 0;
1018 }
1019
1020 137467 default:
1021 137467 return AVERROR(ENOTSUP);
1022 }
1023 }
1024
1025 return AVERROR(EINVAL);
1026 }
1027
1028 1825726 int ff_sws_uop_list_optimize(SwsContext *ctx, SwsUOpFlags flags, SwsUOpList *uops)
1029 {
1030 /* Try promoting the entire uop list to a packed shuffle operation */
1031
2/2
✓ Branch 0 taken 881035 times.
✓ Branch 1 taken 944691 times.
1825726 if (flags & SWS_UOP_FLAG_PSHUFB) {
1032 SwsUOp shuffle;
1033 881035 int ret = solve_shuffle(uops, &shuffle);
1034
2/2
✓ Branch 0 taken 29831 times.
✓ Branch 1 taken 851204 times.
881035 if (ret >= 0) {
1035 29831 ff_sws_uop_list_remove_at(uops, 0, uops->num_ops);
1036 29831 return ff_sws_uop_list_append(uops, &shuffle);
1037
2/4
✓ Branch 0 taken 851204 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 851204 times.
851204 } else if (ret < 0 && ret != AVERROR(ENOTSUP)) {
1038 return ret;
1039 }
1040 }
1041
1042 #if 0
1043 static const SwsUOp dummy = {0};
1044
1045 retry:
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 1795895 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 */
1067 434858 static enum AVPixelFormat get_planar_fmt(SwsPixelType type, int nb_planes)
1068 {
1069
3/4
✓ Branch 0 taken 108946 times.
✓ Branch 1 taken 162418 times.
✓ Branch 2 taken 163494 times.
✗ Branch 3 not taken.
434858 switch (ff_sws_pixel_type_size(type)) {
1070 108946 case 1:
1071
4/5
✓ Branch 0 taken 9105 times.
✓ Branch 1 taken 1318 times.
✓ Branch 2 taken 90270 times.
✓ Branch 3 taken 8253 times.
✗ Branch 4 not taken.
108946 switch (nb_planes) {
1072 9105 case 1: return AV_PIX_FMT_GRAY8;
1073 1318 case 2: return AV_PIX_FMT_YUV444P; // FIXME: no 2-plane planar fmt
1074 90270 case 3: return AV_PIX_FMT_YUV444P;
1075 8253 case 4: return AV_PIX_FMT_YUVA444P;
1076 }
1077 break;
1078 162418 case 2:
1079
4/5
✓ Branch 0 taken 25513 times.
✓ Branch 1 taken 2642 times.
✓ Branch 2 taken 119779 times.
✓ Branch 3 taken 14484 times.
✗ Branch 4 not taken.
162418 switch (nb_planes) {
1080 25513 case 1: return AV_PIX_FMT_GRAY16;
1081 2642 case 2: return AV_PIX_FMT_YUV444P16; // FIXME: no 2-plane planar fmt
1082 119779 case 3: return AV_PIX_FMT_YUV444P16;
1083 14484 case 4: return AV_PIX_FMT_YUVA444P16;
1084 }
1085 break;
1086 163494 case 4:
1087
4/5
✓ Branch 0 taken 29547 times.
✓ Branch 1 taken 4843 times.
✓ Branch 2 taken 112357 times.
✓ Branch 3 taken 16747 times.
✗ Branch 4 not taken.
163494 switch (nb_planes) {
1088 29547 case 1: return AV_PIX_FMT_GRAYF32;
1089 4843 case 2: return AV_PIX_FMT_GBRPF32; // FIXME: no 2-plane planar fmt
1090 112357 case 3: return AV_PIX_FMT_GBRPF32;
1091 16747 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
1100 434858 static void get_input_size(const SwsOpList *ops, SwsFormat *fmt)
1101 {
1102 434858 fmt->width = ops->src.width;
1103 434858 fmt->height = ops->src.height;
1104
1105 434858 const SwsOp *read = ff_sws_op_list_input(ops);
1106
2/4
✓ Branch 0 taken 434858 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 434858 times.
434858 if (read && read->rw.filter.op == SWS_OP_FILTER_V) {
1107 fmt->height = read->rw.filter.kernel->dst_size;
1108
3/4
✓ Branch 0 taken 434858 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 135557 times.
✓ Branch 3 taken 299301 times.
434858 } else if (read && read->rw.filter.op == SWS_OP_FILTER_H) {
1109 135557 fmt->width = read->rw.filter.kernel->dst_size;
1110 }
1111 434858 }
1112
1113 434858 int ff_sws_op_list_split_at(SwsOpList *ops1, SwsOpList **out_ops2, int index)
1114 {
1115 int ret;
1116
2/4
✓ Branch 0 taken 434858 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 434858 times.
434858 if (index <= 0 || index >= ops1->num_ops) {
1117 *out_ops2 = NULL;
1118 return 0;
1119 }
1120
1121 434858 const SwsOp *op = &ops1->ops[index];
1122 434858 const SwsOp *prev = &ops1->ops[index - 1];
1123
1124 434858 SwsOpList *ops2 = ff_sws_op_list_duplicate(ops1);
1125
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 434858 times.
434858 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 434858 int nb_planes = 0;
1134 434858 SwsSwizzleOp swiz_wr = SWS_SWIZZLE(0, 1, 2, 3);
1135 434858 SwsSwizzleOp swiz_rd = SWS_SWIZZLE(0, 1, 2, 3);
1136
2/2
✓ Branch 0 taken 1739432 times.
✓ Branch 1 taken 434858 times.
2174290 for (int i = 0; i < 4; i++) {
1137
2/2
✓ Branch 0 taken 1206925 times.
✓ Branch 1 taken 532507 times.
1739432 if (SWS_OP_NEEDED(prev, i)) {
1138 1206925 const int o = nb_planes++;
1139 1206925 swiz_wr.in[o] = i;
1140 1206925 swiz_rd.in[i] = o;
1141 }
1142 }
1143
1144 /* Determine metadata for the intermediate format */
1145 434858 const SwsPixelType type = op->type;
1146 434858 ops2->src.format = get_planar_fmt(type, nb_planes);
1147 434858 ops2->src.desc = av_pix_fmt_desc_get(ops2->src.format);
1148 434858 get_input_size(ops1, &ops2->src);
1149 434858 ops1->dst = ops2->src;
1150
1151
2/2
✓ Branch 0 taken 1206925 times.
✓ Branch 1 taken 434858 times.
1641783 for (int i = 0; i < nb_planes; i++) {
1152 1206925 const int idx = swiz_wr.in[i];
1153 1206925 ops1->plane_dst[i] = ops2->plane_src[i] = i;
1154 1206925 ops2->comps_src.flags[i] = prev->comps.flags[idx];
1155 1206925 ops2->comps_src.min[i] = prev->comps.min[idx];
1156 1206925 ops2->comps_src.max[i] = prev->comps.max[idx];
1157 }
1158
1159 434858 ff_sws_op_list_remove_at(ops1, index, ops1->num_ops - index);
1160 434858 ff_sws_op_list_remove_at(ops2, 0, index);
1161 434858 op = NULL; /* the above command may invalidate op */
1162
1163
2/2
✓ Branch 0 taken 36634 times.
✓ Branch 1 taken 398224 times.
434858 if (swiz_wr.mask != SWS_SWIZZLE(0, 1, 2, 3).mask) {
1164 36634 ret = ff_sws_op_list_append(ops1, &(SwsOp) {
1165 .op = SWS_OP_SWIZZLE,
1166 .type = type,
1167 .swizzle = swiz_wr,
1168 });
1169
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 36634 times.
36634 if (ret < 0)
1170 goto fail;
1171 }
1172
1173 434858 ret = ff_sws_op_list_append(ops1, &(SwsOp) {
1174 .op = SWS_OP_WRITE,
1175 .type = type,
1176 .rw.elems = nb_planes,
1177 });
1178
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 434858 times.
434858 if (ret < 0)
1179 goto fail;
1180
1181 434858 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
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 434858 times.
434858 if (ret < 0)
1187 goto fail;
1188
1189
2/2
✓ Branch 0 taken 36634 times.
✓ Branch 1 taken 398224 times.
434858 if (swiz_rd.mask != SWS_SWIZZLE(0, 1, 2, 3).mask) {
1190 36634 ret = ff_sws_op_list_insert_at(ops2, 1, &(SwsOp) {
1191 .op = SWS_OP_SWIZZLE,
1192 .type = type,
1193 .swizzle = swiz_rd,
1194 });
1195
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 36634 times.
36634 if (ret < 0)
1196 goto fail;
1197 }
1198
1199 434858 ret = ff_sws_op_list_optimize(ops1);
1200
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 434858 times.
434858 if (ret < 0)
1201 goto fail;
1202
1203 434858 ret = ff_sws_op_list_optimize(ops2);
1204
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 434858 times.
434858 if (ret < 0)
1205 goto fail;
1206
1207 434858 *out_ops2 = ops2;
1208 434858 return 0;
1209
1210 fail:
1211 ff_sws_op_list_free(&ops2);
1212 return ret;
1213 }
1214