FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libswscale/ops_optimizer.c
Date: 2026-07-18 19:31:15
Exec Total Coverage
Lines: 595 650 91.5%
Functions: 19 19 100.0%
Branches: 428 497 86.1%

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 1001663 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 194906 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 150376 times.
✓ Branch 4 taken 55597 times.
✓ Branch 5 taken 461354 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
1001663 switch (next->op) {
45 139430 case SWS_OP_CONVERT:
46 139430 op->type = next->convert.to;
47 av_fallthrough;
48 334336 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 334336 ff_sws_apply_op_q(next, op->clear.value);
56 334336 return true;
57 case SWS_OP_FILTER_H:
58 case SWS_OP_FILTER_V:
59 op->type = next->filter.type;
60 return true;
61 150376 case SWS_OP_SWIZZLE:
62 150376 ff_sws_comp_mask_swizzle(&op->clear.mask, &next->swizzle);
63 150376 ff_sws_apply_op_q(next, op->clear.value);
64 150376 return true;
65 55597 case SWS_OP_SWAP_BYTES:
66
2/2
✓ Branch 0 taken 40358 times.
✓ Branch 1 taken 15239 times.
55597 switch (next->type) {
67 40358 case SWS_PIXEL_U16:
68 case SWS_PIXEL_U32:
69 40358 ff_sws_apply_op_q(next, op->clear.value); /* always representable */
70 40358 return true;
71 15239 default:
72 15239 return false;
73 }
74 461354 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 461354 return false;
81 case SWS_OP_TYPE_NB:
82 break;
83 }
84
85 av_unreachable("Invalid operation type!");
86 return false;
87 }
88
89 /**
90 * Try to commute a swizzle op with the next operation. Makes any adjustments
91 * to the operations as needed, but does not perform the actual commutation.
92 *
93 * Returns whether successful.
94 */
95 1054668 static bool op_commute_swizzle(SwsOp *op, SwsOp *next)
96 {
97 1054668 bool seen[4] = {0};
98
99 av_assert1(op->op == SWS_OP_SWIZZLE);
100
5/8
✓ Branch 0 taken 181830 times.
✓ Branch 1 taken 181740 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 84386 times.
✓ Branch 4 taken 68162 times.
✓ Branch 5 taken 538550 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
1054668 switch (next->op) {
101 181830 case SWS_OP_CONVERT:
102 181830 op->type = next->convert.to;
103 av_fallthrough;
104 363570 case SWS_OP_SWAP_BYTES:
105 case SWS_OP_LSHIFT:
106 case SWS_OP_RSHIFT:
107 case SWS_OP_SCALE:
108 363570 return true;
109 case SWS_OP_FILTER_H:
110 case SWS_OP_FILTER_V:
111 op->type = next->filter.type;
112 return true;
113
114 /**
115 * We can commute per-channel ops only if the per-channel constants are the
116 * same for all duplicated channels; e.g.:
117 * SWIZZLE {0, 0, 0, 3}
118 * NEXT {x, x, x, w}
119 * ->
120 * NEXT {x, _, _, w}
121 * SWIZZLE {0, 0, 0, 3}
122 */
123 84386 case SWS_OP_MIN:
124 case SWS_OP_MAX: {
125 84386 const SwsClampOp c = next->clamp;
126
2/2
✓ Branch 0 taken 337544 times.
✓ Branch 1 taken 84386 times.
421930 for (int i = 0; i < 4; i++) {
127
2/2
✓ Branch 0 taken 77343 times.
✓ Branch 1 taken 260201 times.
337544 if (!SWS_OP_NEEDED(op, i))
128 77343 continue;
129 260201 const int j = op->swizzle.in[i];
130
3/4
✓ Branch 0 taken 48270 times.
✓ Branch 1 taken 211931 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 48270 times.
260201 if (seen[j] && av_cmp_q64(next->clamp.limit[j], c.limit[i]))
131 return false;
132 260201 next->clamp.limit[j] = c.limit[i];
133 260201 seen[j] = true;
134 }
135 84386 return true;
136 }
137
138 68162 case SWS_OP_DITHER: {
139 68162 const SwsDitherOp d = next->dither;
140
2/2
✓ Branch 0 taken 256010 times.
✓ Branch 1 taken 59843 times.
315853 for (int i = 0; i < 4; i++) {
141
2/2
✓ Branch 0 taken 55815 times.
✓ Branch 1 taken 200195 times.
256010 if (!SWS_OP_NEEDED(op, i))
142 55815 continue;
143 200195 const int j = op->swizzle.in[i];
144
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])
145 8319 return false;
146 191876 next->dither.y_offset[j] = d.y_offset[i];
147 191876 seen[j] = true;
148 }
149 59843 return true;
150 }
151
152 538550 case SWS_OP_INVALID:
153 case SWS_OP_READ:
154 case SWS_OP_WRITE:
155 case SWS_OP_SWIZZLE:
156 case SWS_OP_CLEAR:
157 case SWS_OP_LINEAR:
158 case SWS_OP_PACK:
159 case SWS_OP_UNPACK:
160 538550 return false;
161 case SWS_OP_TYPE_NB:
162 break;
163 }
164
165 av_unreachable("Invalid operation type!");
166 return false;
167 }
168
169 /**
170 * Try to commute a filter op with the previous operation. Makes any
171 * adjustments to the operations as needed, but does not perform the actual
172 * commutation.
173 *
174 * Returns whether successful.
175 */
176 7277577 static bool op_commute_filter(SwsOp *op, SwsOp *prev)
177 {
178
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7277577 times.
7277577 av_assert0(!ff_sws_pixel_type_is_int(op->filter.type));
179
180
2/4
✓ Branch 0 taken 895049 times.
✓ Branch 1 taken 6382528 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
7277577 switch (prev->op) {
181 895049 case SWS_OP_SWIZZLE:
182 case SWS_OP_SCALE:
183 case SWS_OP_LINEAR:
184 case SWS_OP_DITHER:
185 895049 prev->type = op->filter.type;
186 895049 return true;
187 6382528 case SWS_OP_CONVERT:
188 case SWS_OP_INVALID:
189 case SWS_OP_READ:
190 case SWS_OP_WRITE:
191 case SWS_OP_SWAP_BYTES:
192 case SWS_OP_UNPACK:
193 case SWS_OP_PACK:
194 case SWS_OP_LSHIFT:
195 case SWS_OP_RSHIFT:
196 case SWS_OP_CLEAR:
197 case SWS_OP_MIN:
198 case SWS_OP_MAX:
199 case SWS_OP_FILTER_H:
200 case SWS_OP_FILTER_V:
201 6382528 return false;
202 case SWS_OP_TYPE_NB:
203 break;
204 }
205
206 av_unreachable("Invalid operation type!");
207 return false;
208 }
209
210 /* returns log2(x) only if x is a power of two, or 0 otherwise */
211 721692 static int exact_log2(const int x)
212 {
213 int p;
214
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 721692 times.
721692 if (x <= 0)
215 return 0;
216 721692 p = av_log2(x);
217
2/2
✓ Branch 0 taken 265201 times.
✓ Branch 1 taken 456491 times.
721692 return (1 << p) == x ? p : 0;
218 }
219
220 1407482 static int exact_log2_q64(const AVRational64 x)
221 {
222
2/2
✓ Branch 0 taken 384575 times.
✓ Branch 1 taken 1022907 times.
1407482 if (x.den == 1)
223 384575 return exact_log2(x.num);
224
2/2
✓ Branch 0 taken 337117 times.
✓ Branch 1 taken 685790 times.
1022907 else if (x.num == 1)
225 337117 return -exact_log2(x.den);
226 else
227 685790 return 0;
228 }
229
230 /**
231 * If a linear operation can be reduced to a scalar multiplication, returns
232 * the corresponding scaling factor, or 0 otherwise.
233 */
234 2028791 static bool extract_scalar(const SwsLinearOp *c,
235 const SwsComps *comps, const SwsComps *prev,
236 SwsScaleOp *out_scale)
237 {
238 2028791 SwsScaleOp scale = {0};
239
240 /* There are components not on the main diagonal */
241
2/2
✓ Branch 0 taken 1594083 times.
✓ Branch 1 taken 434708 times.
2028791 if (c->mask & ~SWS_MASK_DIAG4)
242 1594083 return false;
243
244
2/2
✓ Branch 0 taken 1284487 times.
✓ Branch 1 taken 148586 times.
1433073 for (int i = 0; i < 4; i++) {
245 1284487 const AVRational64 s = c->m[i][i];
246
1/2
✓ Branch 0 taken 1284487 times.
✗ Branch 1 not taken.
1284487 if ((prev->flags[i] & SWS_COMP_ZERO) ||
247
2/2
✓ Branch 0 taken 153995 times.
✓ Branch 1 taken 1130492 times.
1284487 (comps->flags[i] & SWS_COMP_GARBAGE))
248 153995 continue;
249
4/4
✓ Branch 0 taken 695784 times.
✓ Branch 1 taken 434708 times.
✓ Branch 3 taken 286122 times.
✓ Branch 4 taken 409662 times.
1130492 if (scale.factor.den && av_cmp_q64(s, scale.factor))
250 286122 return false;
251 844370 scale.factor = s;
252 }
253
254
1/2
✓ Branch 0 taken 148586 times.
✗ Branch 1 not taken.
148586 if (scale.factor.den)
255 148586 *out_scale = scale;
256 148586 return scale.factor.den;
257 }
258
259 /* Extracts an integer clear operation (subset) from the given linear op. */
260 2162965 static bool extract_constant_rows(SwsLinearOp *c, const SwsComps *prev,
261 SwsClearOp *out_clear)
262 {
263 2162965 SwsClearOp clear = {0};
264 2162965 bool ret = false;
265
266
2/2
✓ Branch 0 taken 8651860 times.
✓ Branch 1 taken 2162965 times.
10814825 for (int i = 0; i < 4; i++) {
267 8651860 bool const_row = c->m[i][4].den == 1; /* offset is integer */
268
2/2
✓ Branch 0 taken 34607440 times.
✓ Branch 1 taken 8651860 times.
43259300 for (int j = 0; j < 4; j++) {
269
2/2
✓ Branch 0 taken 14307526 times.
✓ Branch 1 taken 20299914 times.
48914966 const_row &= c->m[i][j].num == 0 || /* scalar is zero */
270
2/2
✓ Branch 0 taken 259475 times.
✓ Branch 1 taken 14048051 times.
14307526 (prev->flags[j] & SWS_COMP_ZERO); /* input is zero */
271 }
272
3/4
✓ Branch 0 taken 164085 times.
✓ Branch 1 taken 8487775 times.
✓ Branch 2 taken 164085 times.
✗ Branch 3 not taken.
8651860 if (const_row && (c->mask & SWS_MASK_ROW(i))) {
273 164085 clear.mask |= SWS_COMP(i);
274 164085 clear.value[i] = c->m[i][4];
275
2/2
✓ Branch 0 taken 820425 times.
✓ Branch 1 taken 164085 times.
984510 for (int j = 0; j < 5; j++)
276 820425 c->m[i][j] = Q(i == j);
277 164085 c->mask &= ~SWS_MASK_ROW(i);
278 164085 ret = true;
279 }
280 }
281
282
2/2
✓ Branch 0 taken 134174 times.
✓ Branch 1 taken 2028791 times.
2162965 if (ret)
283 134174 *out_clear = clear;
284 2162965 return ret;
285 }
286
287 /* Unswizzle a linear operation by aligning single-input rows with
288 * their corresponding diagonal */
289 1880205 static bool extract_swizzle(SwsLinearOp *op, const SwsComps *prev,
290 SwsSwizzleOp *out_swiz)
291 {
292 1880205 SwsSwizzleOp swiz = SWS_SWIZZLE(0, 1, 2, 3);
293 1880205 SwsLinearOp c = *op;
294
295 /* Find non-zero coefficients in the main 4x4 matrix */
296 1880205 uint32_t nonzero = 0;
297
2/2
✓ Branch 0 taken 7520820 times.
✓ Branch 1 taken 1880205 times.
9401025 for (int i = 0; i < 4; i++) {
298
2/2
✓ Branch 0 taken 30083280 times.
✓ Branch 1 taken 7520820 times.
37604100 for (int j = 0; j < 4; j++) {
299
4/4
✓ Branch 0 taken 12916664 times.
✓ Branch 1 taken 17166616 times.
✓ Branch 2 taken 75694 times.
✓ Branch 3 taken 12840970 times.
30083280 if (!c.m[i][j].num || (prev->flags[j] & SWS_COMP_ZERO))
300 17242310 continue;
301 12840970 nonzero |= SWS_MASK(i, j);
302 }
303 }
304
305 /* If a value is unique in its row and the target column is
306 * empty, move it there and update the input swizzle */
307
2/2
✓ Branch 0 taken 7520820 times.
✓ Branch 1 taken 1880205 times.
9401025 for (int i = 0; i < 4; i++) {
308
2/2
✓ Branch 0 taken 7445126 times.
✓ Branch 1 taken 75694 times.
7520820 if (nonzero & SWS_MASK_COL(i))
309 7445126 continue; /* target column is not empty */
310
1/2
✓ Branch 0 taken 75694 times.
✗ Branch 1 not taken.
75694 for (int j = 0; j < 4; j++) {
311
1/2
✓ Branch 0 taken 75694 times.
✗ Branch 1 not taken.
75694 if ((nonzero & SWS_MASK_ROW(i)) == SWS_MASK(i, j)) {
312 /* Move coefficient to the diagonal */
313 75694 c.m[i][i] = c.m[i][j];
314 75694 c.m[i][j] = Q(0);
315 75694 swiz.in[i] = j;
316 75694 break;
317 }
318 }
319 }
320
321
2/2
✓ Branch 0 taken 1842358 times.
✓ Branch 1 taken 37847 times.
1880205 if (swiz.mask == SWS_SWIZZLE(0, 1, 2, 3).mask)
322 1842358 return false; /* no swizzle was identified */
323
324 37847 c.mask = ff_sws_linear_mask(&c);
325 37847 *out_swiz = swiz;
326 37847 *op = c;
327 37847 return true;
328 }
329
330 24651 static int op_result_is_exact(const SwsOp *op)
331 {
332
2/2
✓ Branch 0 taken 46566 times.
✓ Branch 1 taken 6845 times.
53411 for (int i = 0; i < 4; i++) {
333
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))
334 17806 return false;
335 }
336
337 6845 return true;
338 }
339
340 1212147 int ff_sws_op_list_optimize(SwsOpList *ops)
341 {
342 int ret;
343
344 7927919 retry:
345 9140066 ff_sws_op_list_update_comps(ops);
346
347 /* Try to push filters towards the input; do this first to unblock
348 * in-place optimizations like linear op fusion */
349
2/2
✓ Branch 0 taken 78810931 times.
✓ Branch 1 taken 7936237 times.
86747168 for (int n = 1; n < ops->num_ops; n++) {
350 78810931 SwsOp *op = &ops->ops[n];
351 78810931 SwsOp *prev = &ops->ops[n - 1];
352
353
2/2
✓ Branch 0 taken 7277577 times.
✓ Branch 1 taken 71533354 times.
78810931 switch (op->op) {
354 7277577 case SWS_OP_FILTER_H:
355 case SWS_OP_FILTER_V:
356
2/2
✓ Branch 1 taken 895049 times.
✓ Branch 2 taken 6382528 times.
7277577 if (op_commute_filter(op, prev)) {
357 895049 FFSWAP(SwsOp, *op, *prev);
358 895049 goto retry;
359 }
360
361 /* Merge filter with prior conversion */
362
3/4
✓ Branch 0 taken 942029 times.
✓ Branch 1 taken 5440499 times.
✓ Branch 2 taken 942029 times.
✗ Branch 3 not taken.
6382528 if (prev->op == SWS_OP_CONVERT && !prev->convert.expand) {
363 942029 int size_from = ff_sws_pixel_type_size(prev->type);
364 942029 int size_to = ff_sws_pixel_type_size(op->type);
365 av_assert1(prev->convert.to == op->type);
366
2/2
✓ Branch 0 taken 308780 times.
✓ Branch 1 taken 633249 times.
942029 if (size_from < size_to) {
367 308780 op->type = prev->type;
368 308780 ff_sws_op_list_remove_at(ops, n - 1, 1);
369 308780 goto retry;
370 }
371 }
372 6073748 break;
373 }
374 }
375
376 /* Apply all in-place optimizations (that do not re-order the list) */
377
2/2
✓ Branch 0 taken 43278424 times.
✓ Branch 1 taken 2251861 times.
45530285 for (int n = 0; n < ops->num_ops; n++) {
378 43278424 SwsOp dummy = {0};
379 43278424 SwsOp *op = &ops->ops[n];
380
2/2
✓ Branch 0 taken 35342187 times.
✓ Branch 1 taken 7936237 times.
43278424 SwsOp *prev = n ? &ops->ops[n - 1] : &dummy;
381
2/2
✓ Branch 0 taken 41026563 times.
✓ Branch 1 taken 2251861 times.
43278424 SwsOp *next = n + 1 < ops->num_ops ? &ops->ops[n + 1] : &dummy;
382
383 /* common helper variable */
384 43278424 const SwsCompMask needed = ff_sws_comp_mask_needed(op);
385 43278424 bool noop = true;
386
387
4/4
✓ Branch 0 taken 2679155 times.
✓ Branch 1 taken 40599269 times.
✓ Branch 2 taken 427294 times.
✓ Branch 3 taken 2251861 times.
43278424 if (!needed && op->op != SWS_OP_WRITE) {
388 /* Remove any operation whose output is not needed */
389 427294 ff_sws_op_list_remove_at(ops, n, 1);
390 5684376 goto retry;
391 }
392
393
14/14
✓ Branch 0 taken 7386106 times.
✓ Branch 1 taken 3324803 times.
✓ Branch 2 taken 1247817 times.
✓ Branch 3 taken 656771 times.
✓ Branch 4 taken 2720098 times.
✓ Branch 5 taken 4819363 times.
✓ Branch 6 taken 4869056 times.
✓ Branch 7 taken 1869759 times.
✓ Branch 8 taken 1150074 times.
✓ Branch 9 taken 2452602 times.
✓ Branch 10 taken 3687638 times.
✓ Branch 11 taken 1407482 times.
✓ Branch 12 taken 4641510 times.
✓ Branch 13 taken 2618051 times.
42851130 switch (op->op) {
394 7386106 case SWS_OP_READ:
395 /* "Compress" planar reads where not all components are needed */
396
2/2
✓ Branch 0 taken 4393870 times.
✓ Branch 1 taken 2992236 times.
7386106 if (op->rw.mode == SWS_RW_PLANAR) {
397 4393870 SwsSwizzleOp swiz = SWS_SWIZZLE(0, 1, 2, 3);
398 4393870 int nb_planes = 0;
399
2/2
✓ Branch 0 taken 11065854 times.
✓ Branch 1 taken 4393870 times.
15459724 for (int i = 0; i < op->rw.elems; i++) {
400
2/2
✓ Branch 0 taken 70600 times.
✓ Branch 1 taken 10995254 times.
11065854 if (!SWS_OP_NEEDED(op, i)) {
401 70600 swiz.in[i] = 3 - (i - nb_planes); /* map to unused plane */
402 70600 continue;
403 }
404
405 10995254 const int idx = nb_planes++;
406 av_assert1(idx <= i);
407 10995254 ops->plane_src[idx] = ops->plane_src[i];
408 10995254 swiz.in[i] = idx;
409 }
410
411
2/2
✓ Branch 0 taken 57389 times.
✓ Branch 1 taken 4336481 times.
4393870 if (nb_planes < op->rw.elems) {
412 57389 op->rw.elems = nb_planes;
413
3/4
✓ Branch 0 taken 3944 times.
✓ Branch 1 taken 53445 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 57389 times.
57389 RET(ff_sws_op_list_insert_at(ops, n + 1, &(SwsOp) {
414 .op = SWS_OP_SWIZZLE,
415 .type = op->rw.filter.op ? op->rw.filter.type : op->type,
416 .swizzle = swiz,
417 }));
418 57389 goto retry;
419 }
420 }
421 7328717 break;
422
423 3324803 case SWS_OP_SWAP_BYTES:
424 /* Redundant (double) swap */
425
2/2
✓ Branch 0 taken 882 times.
✓ Branch 1 taken 3323921 times.
3324803 if (next->op == SWS_OP_SWAP_BYTES) {
426 882 ff_sws_op_list_remove_at(ops, n, 2);
427 882 goto retry;
428 }
429 3323921 break;
430
431 1247817 case SWS_OP_UNPACK:
432 /* Redundant unpack+pack */
433
3/4
✓ Branch 0 taken 220 times.
✓ Branch 1 taken 1247597 times.
✓ Branch 2 taken 220 times.
✗ Branch 3 not taken.
1247817 if (next->op == SWS_OP_PACK && next->type == op->type &&
434
1/2
✓ Branch 0 taken 220 times.
✗ Branch 1 not taken.
220 next->pack.pattern[0] == op->pack.pattern[0] &&
435
1/2
✓ Branch 0 taken 220 times.
✗ Branch 1 not taken.
220 next->pack.pattern[1] == op->pack.pattern[1] &&
436
1/2
✓ Branch 0 taken 220 times.
✗ Branch 1 not taken.
220 next->pack.pattern[2] == op->pack.pattern[2] &&
437
1/2
✓ Branch 0 taken 220 times.
✗ Branch 1 not taken.
220 next->pack.pattern[3] == op->pack.pattern[3])
438 {
439 220 ff_sws_op_list_remove_at(ops, n, 2);
440 220 goto retry;
441 }
442 1247597 break;
443
444 656771 case SWS_OP_LSHIFT:
445 case SWS_OP_RSHIFT:
446 /* Two shifts in the same direction */
447
2/2
✓ Branch 0 taken 772 times.
✓ Branch 1 taken 655999 times.
656771 if (next->op == op->op) {
448 772 op->shift.amount += next->shift.amount;
449 772 ff_sws_op_list_remove_at(ops, n + 1, 1);
450 772 goto retry;
451 }
452
453 /* No-op shift */
454
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 655999 times.
655999 if (!op->shift.amount) {
455 ff_sws_op_list_remove_at(ops, n, 1);
456 goto retry;
457 }
458 655999 break;
459
460 2720098 case SWS_OP_CLEAR:
461
2/2
✓ Branch 0 taken 10880392 times.
✓ Branch 1 taken 2720098 times.
13600490 for (int i = 0; i < 4; i++) {
462
2/2
✓ Branch 0 taken 7231640 times.
✓ Branch 1 taken 3648752 times.
10880392 if (!SWS_COMP_TEST(op->clear.mask, i))
463 7231640 continue;
464
465
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3648752 times.
3648752 if ((prev->comps.flags[i] & SWS_COMP_ZERO) &&
466 !(prev->comps.flags[i] & SWS_COMP_GARBAGE) &&
467 op->clear.value[i].num == 0)
468 {
469 /* Redundant clear-to-zero of zero component */
470 op->clear.mask ^= SWS_COMP(i);
471
2/2
✓ Branch 0 taken 586477 times.
✓ Branch 1 taken 3062275 times.
3648752 } else if (!SWS_OP_NEEDED(op, i)) {
472 /* Unnecessary clear of unused component */
473 586477 op->clear.mask ^= SWS_COMP(i);
474 } else {
475 3062275 noop = false;
476 }
477 }
478
479
2/2
✓ Branch 0 taken 519450 times.
✓ Branch 1 taken 2200648 times.
2720098 if (noop) {
480 519450 ff_sws_op_list_remove_at(ops, n, 1);
481 519450 goto retry;
482 }
483
484 /* Transitive clear */
485
2/2
✓ Branch 0 taken 2643 times.
✓ Branch 1 taken 2198005 times.
2200648 if (next->op == SWS_OP_CLEAR) {
486
2/2
✓ Branch 0 taken 10572 times.
✓ Branch 1 taken 2643 times.
13215 for (int i = 0; i < 4; i++) {
487
2/2
✓ Branch 0 taken 2643 times.
✓ Branch 1 taken 7929 times.
10572 if (SWS_COMP_TEST(next->clear.mask, i))
488 2643 op->clear.value[i] = next->clear.value[i];
489 }
490 2643 op->clear.mask |= next->clear.mask;
491 2643 ff_sws_op_list_remove_at(ops, n + 1, 1);
492 2643 goto retry;
493 }
494 2198005 break;
495
496 4819363 case SWS_OP_SWIZZLE:
497
2/2
✓ Branch 0 taken 19277452 times.
✓ Branch 1 taken 4819363 times.
24096815 for (int i = 0; i < 4; i++) {
498
2/2
✓ Branch 0 taken 4879794 times.
✓ Branch 1 taken 14397658 times.
19277452 if (!SWS_OP_NEEDED(op, i))
499 4879794 continue;
500
2/2
✓ Branch 0 taken 9807471 times.
✓ Branch 1 taken 4590187 times.
14397658 if (op->swizzle.in[i] != i)
501 9807471 noop = false;
502 }
503
504 /* Identity swizzle */
505
2/2
✓ Branch 0 taken 780176 times.
✓ Branch 1 taken 4039187 times.
4819363 if (noop) {
506 780176 ff_sws_op_list_remove_at(ops, n, 1);
507 780176 goto retry;
508 }
509
510 /* Transitive swizzle */
511
2/2
✓ Branch 0 taken 93474 times.
✓ Branch 1 taken 3945713 times.
4039187 if (next->op == SWS_OP_SWIZZLE) {
512 93474 const SwsSwizzleOp orig = op->swizzle;
513
2/2
✓ Branch 0 taken 373896 times.
✓ Branch 1 taken 93474 times.
467370 for (int i = 0; i < 4; i++)
514 373896 op->swizzle.in[i] = orig.in[next->swizzle.in[i]];
515 93474 ff_sws_op_list_remove_at(ops, n + 1, 1);
516 93474 goto retry;
517 }
518
519 /* Swizzle planes instead of components, if possible */
520
4/4
✓ Branch 0 taken 571939 times.
✓ Branch 1 taken 3373774 times.
✓ Branch 2 taken 253545 times.
✓ Branch 3 taken 318394 times.
3945713 if (prev->op == SWS_OP_READ && prev->rw.mode == SWS_RW_PLANAR) {
521
2/2
✓ Branch 0 taken 357092 times.
✓ Branch 1 taken 58244 times.
415336 for (int dst = 0; dst < prev->rw.elems; dst++) {
522 357092 const int src = op->swizzle.in[dst];
523
4/4
✓ Branch 0 taken 203588 times.
✓ Branch 1 taken 153504 times.
✓ Branch 2 taken 195301 times.
✓ Branch 3 taken 8287 times.
357092 if (src > dst && src < prev->rw.elems) {
524 195301 FFSWAP(int, ops->plane_src[dst], ops->plane_src[src]);
525
2/2
✓ Branch 0 taken 698948 times.
✓ Branch 1 taken 195301 times.
894249 for (int i = dst; i < 4; i++) {
526
2/2
✓ Branch 0 taken 204372 times.
✓ Branch 1 taken 494576 times.
698948 if (op->swizzle.in[i] == dst)
527 204372 op->swizzle.in[i] = src;
528
2/2
✓ Branch 0 taken 195301 times.
✓ Branch 1 taken 299275 times.
494576 else if (op->swizzle.in[i] == src)
529 195301 op->swizzle.in[i] = dst;
530 }
531 195301 goto retry;
532 }
533 }
534 }
535
536
4/4
✓ Branch 0 taken 604143 times.
✓ Branch 1 taken 3146269 times.
✓ Branch 2 taken 398207 times.
✓ Branch 3 taken 205936 times.
3750412 if (next->op == SWS_OP_WRITE && next->rw.mode == SWS_RW_PLANAR) {
537
2/2
✓ Branch 0 taken 562222 times.
✓ Branch 1 taken 130937 times.
693159 for (int dst = 0; dst < next->rw.elems; dst++) {
538 562222 const int src = op->swizzle.in[dst];
539
4/4
✓ Branch 0 taken 378132 times.
✓ Branch 1 taken 184090 times.
✓ Branch 2 taken 267270 times.
✓ Branch 3 taken 110862 times.
562222 if (src > dst && src < next->rw.elems) {
540 267270 FFSWAP(int, ops->plane_dst[dst], ops->plane_dst[src]);
541 267270 FFSWAP(int, op->swizzle.in[dst], op->swizzle.in[src]);
542 267270 goto retry;
543 }
544 }
545 }
546 3483142 break;
547
548 4869056 case SWS_OP_CONVERT:
549 /* No-op conversion */
550
2/2
✓ Branch 0 taken 128826 times.
✓ Branch 1 taken 4740230 times.
4869056 if (op->type == op->convert.to) {
551 128826 ff_sws_op_list_remove_at(ops, n, 1);
552 128826 goto retry;
553 }
554
555 /* Transitive conversion */
556
2/2
✓ Branch 0 taken 111139 times.
✓ Branch 1 taken 4629091 times.
4740230 if (next->op == SWS_OP_CONVERT &&
557
1/2
✓ Branch 0 taken 111139 times.
✗ Branch 1 not taken.
111139 op->convert.expand == next->convert.expand)
558 {
559 av_assert1(op->convert.to == next->type);
560 111139 op->convert.to = next->convert.to;
561 111139 ff_sws_op_list_remove_at(ops, n + 1, 1);
562 111139 goto retry;
563 }
564
565 /* Conversion followed by integer expansion */
566
3/4
✓ Branch 0 taken 272251 times.
✓ Branch 1 taken 4356840 times.
✓ Branch 2 taken 272251 times.
✗ Branch 3 not taken.
4629091 if (next->op == SWS_OP_SCALE && !op->convert.expand &&
567
2/2
✓ Branch 0 taken 271875 times.
✓ Branch 1 taken 376 times.
272251 ff_sws_pixel_type_is_int(op->type) &&
568
4/4
✓ Branch 0 taken 4321 times.
✓ Branch 1 taken 267554 times.
✓ Branch 2 taken 1332 times.
✓ Branch 3 taken 2989 times.
276196 ff_sws_pixel_type_is_int(op->convert.to) &&
569 4321 !av_cmp_q64(next->scale.factor,
570 ff_sws_pixel_expand(op->type, op->convert.to)))
571 {
572 1332 op->convert.expand = true;
573 1332 ff_sws_op_list_remove_at(ops, n + 1, 1);
574 1332 goto retry;
575 }
576 4627759 break;
577
578 1869759 case SWS_OP_MIN:
579
2/2
✓ Branch 0 taken 7479036 times.
✓ Branch 1 taken 1869759 times.
9348795 for (int i = 0; i < 4; i++) {
580
4/4
✓ Branch 0 taken 5454121 times.
✓ Branch 1 taken 2024915 times.
✓ Branch 2 taken 64859 times.
✓ Branch 3 taken 5389262 times.
7479036 if (!SWS_OP_NEEDED(op, i) || !op->clamp.limit[i].den)
581 2089774 continue;
582
2/2
✓ Branch 1 taken 4705139 times.
✓ Branch 2 taken 684123 times.
5389262 if (av_cmp_q64(op->clamp.limit[i], prev->comps.max[i]) < 0)
583 4705139 noop = false;
584 }
585
586
2/2
✓ Branch 0 taken 126017 times.
✓ Branch 1 taken 1743742 times.
1869759 if (noop) {
587 126017 ff_sws_op_list_remove_at(ops, n, 1);
588 126017 goto retry;
589 }
590 1743742 break;
591
592 1150074 case SWS_OP_MAX:
593
2/2
✓ Branch 0 taken 4600296 times.
✓ Branch 1 taken 1150074 times.
5750370 for (int i = 0; i < 4; i++) {
594
3/4
✓ Branch 0 taken 3324650 times.
✓ Branch 1 taken 1275646 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3324650 times.
4600296 if (!SWS_OP_NEEDED(op, i) || !op->clamp.limit[i].den)
595 1275646 continue;
596
2/2
✓ Branch 1 taken 2372346 times.
✓ Branch 2 taken 952304 times.
3324650 if (av_cmp_q64(prev->comps.min[i], op->clamp.limit[i]) < 0)
597 2372346 noop = false;
598 }
599
600
2/2
✓ Branch 0 taken 255549 times.
✓ Branch 1 taken 894525 times.
1150074 if (noop) {
601 255549 ff_sws_op_list_remove_at(ops, n, 1);
602 255549 goto retry;
603 }
604 894525 break;
605
606 2452602 case SWS_OP_DITHER:
607
2/2
✓ Branch 0 taken 9523827 times.
✓ Branch 1 taken 1996264 times.
11520091 for (int i = 0; i < 4; i++) {
608
2/2
✓ Branch 0 taken 2827584 times.
✓ Branch 1 taken 6696243 times.
9523827 if (op->dither.y_offset[i] < 0)
609 2827584 continue;
610
4/4
✓ Branch 0 taken 6378794 times.
✓ Branch 1 taken 317449 times.
✓ Branch 2 taken 138889 times.
✓ Branch 3 taken 6239905 times.
6696243 if (!SWS_OP_NEEDED(op, i) || (prev->comps.flags[i] & SWS_COMP_EXACT)) {
611 456338 op->dither.y_offset[i] = -1; /* unnecessary dither */
612 456338 goto retry;
613 } else {
614 6239905 noop = false;
615 }
616 }
617
618
2/2
✓ Branch 0 taken 9627 times.
✓ Branch 1 taken 1986637 times.
1996264 if (noop) {
619 9627 ff_sws_op_list_remove_at(ops, n, 1);
620 9627 goto retry;
621 }
622 1986637 break;
623
624 3687638 case SWS_OP_LINEAR: {
625 SwsSwizzleOp swizzle;
626 SwsClearOp clear;
627 SwsScaleOp scale;
628
629 /* No-op (identity) linear operation */
630
2/2
✓ Branch 0 taken 54863 times.
✓ Branch 1 taken 3632775 times.
3687638 if (!op->lin.mask) {
631 54863 ff_sws_op_list_remove_at(ops, n, 1);
632 1845280 goto retry;
633 }
634
635
2/2
✓ Branch 0 taken 864923 times.
✓ Branch 1 taken 2767852 times.
3632775 if (next->op == SWS_OP_LINEAR) {
636 /* 5x5 matrix multiplication after appending [ 0 0 0 0 1 ] */
637 864923 const SwsLinearOp m1 = op->lin;
638 864923 const SwsLinearOp m2 = next->lin;
639
2/2
✓ Branch 0 taken 3459692 times.
✓ Branch 1 taken 864923 times.
4324615 for (int i = 0; i < 4; i++) {
640
2/2
✓ Branch 0 taken 17298460 times.
✓ Branch 1 taken 3459692 times.
20758152 for (int j = 0; j < 5; j++) {
641 17298460 AVRational64 sum = Q(0);
642
2/2
✓ Branch 0 taken 69193840 times.
✓ Branch 1 taken 17298460 times.
86492300 for (int k = 0; k < 4; k++)
643 69193840 sum = av_add_q64(sum, av_mul_q64(m2.m[i][k], m1.m[k][j]));
644
2/2
✓ Branch 0 taken 3459692 times.
✓ Branch 1 taken 13838768 times.
17298460 if (j == 4) /* m1.m[4][j] == 1 */
645 3459692 sum = av_add_q64(sum, m2.m[i][4]);
646 17298460 op->lin.m[i][j] = sum;
647 }
648 }
649 864923 op->lin.mask = ff_sws_linear_mask(&op->lin);
650 864923 ff_sws_op_list_remove_at(ops, n + 1, 1);
651 864923 goto retry;
652 }
653
654 /* Optimize away zero columns */
655
2/2
✓ Branch 0 taken 10891159 times.
✓ Branch 1 taken 2561895 times.
13453054 for (int j = 0; j < 4; j++) {
656 10891159 const uint32_t col = SWS_MASK_COL(j);
657
4/4
✓ Branch 0 taken 636705 times.
✓ Branch 1 taken 10254454 times.
✓ Branch 2 taken 430748 times.
✓ Branch 3 taken 205957 times.
10891159 if (!(prev->comps.flags[j] & SWS_COMP_ZERO) || !(op->lin.mask & col))
658 10685202 continue;
659
2/2
✓ Branch 0 taken 823828 times.
✓ Branch 1 taken 205957 times.
1029785 for (int i = 0; i < 4; i++)
660 823828 op->lin.m[i][j] = Q(i == j);
661 205957 op->lin.mask &= ~col;
662 205957 goto retry;
663 }
664
665 /* Optimize away unused rows */
666
2/2
✓ Branch 0 taken 10069239 times.
✓ Branch 1 taken 2162965 times.
12232204 for (int i = 0; i < 4; i++) {
667 10069239 const uint32_t row = SWS_MASK_ROW(i);
668
4/4
✓ Branch 0 taken 3607871 times.
✓ Branch 1 taken 6461368 times.
✓ Branch 2 taken 3208941 times.
✓ Branch 3 taken 398930 times.
10069239 if (SWS_OP_NEEDED(op, i) || !(op->lin.mask & row))
669 9670309 continue;
670
2/2
✓ Branch 0 taken 1994650 times.
✓ Branch 1 taken 398930 times.
2393580 for (int j = 0; j < 5; j++)
671 1994650 op->lin.m[i][j] = Q(i == j);
672 398930 op->lin.mask &= ~row;
673 398930 goto retry;
674 }
675
676 /* Convert constant rows to explicit clear instruction */
677
2/2
✓ Branch 1 taken 134174 times.
✓ Branch 2 taken 2028791 times.
2162965 if (extract_constant_rows(&op->lin, &prev->comps, &clear)) {
678
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 134174 times.
134174 RET(ff_sws_op_list_insert_at(ops, n + 1, &(SwsOp) {
679 .op = SWS_OP_CLEAR,
680 .type = op->type,
681 .comps = op->comps,
682 .clear = clear,
683 }));
684 134174 goto retry;
685 }
686
687 /* Multiplication by scalar constant */
688
2/2
✓ Branch 1 taken 148586 times.
✓ Branch 2 taken 1880205 times.
2028791 if (extract_scalar(&op->lin, &op->comps, &prev->comps, &scale)) {
689 148586 op->op = SWS_OP_SCALE;
690 148586 op->scale = scale;
691 148586 goto retry;
692 }
693
694 /* Swizzle by fixed pattern */
695
2/2
✓ Branch 1 taken 37847 times.
✓ Branch 2 taken 1842358 times.
1880205 if (extract_swizzle(&op->lin, &prev->comps, &swizzle)) {
696
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 37847 times.
37847 RET(ff_sws_op_list_insert_at(ops, n, &(SwsOp) {
697 .op = SWS_OP_SWIZZLE,
698 .type = op->type,
699 .swizzle = swizzle,
700 }));
701 37847 goto retry;
702 }
703 1842358 break;
704 }
705
706 1407482 case SWS_OP_SCALE: {
707 1407482 const int factor2 = exact_log2_q64(op->scale.factor);
708
709 /* No-op scaling */
710
3/4
✓ Branch 0 taken 337117 times.
✓ Branch 1 taken 1070365 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 337117 times.
1407482 if (op->scale.factor.num == 1 && op->scale.factor.den == 1) {
711 ff_sws_op_list_remove_at(ops, n, 1);
712 goto retry;
713 }
714
715 /* Merge consecutive scaling operations */
716
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1407482 times.
1407482 if (next->op == SWS_OP_SCALE) {
717 op->scale.factor = av_mul_q64(op->scale.factor, next->scale.factor);
718 ff_sws_op_list_remove_at(ops, n + 1, 1);
719 goto retry;
720 }
721
722 /* Scaling by exact power of two */
723
4/4
✓ Branch 0 taken 265201 times.
✓ Branch 1 taken 1142281 times.
✓ Branch 2 taken 3667 times.
✓ Branch 3 taken 261534 times.
1407482 if (factor2 && ff_sws_pixel_type_is_int(op->type)) {
724
1/2
✓ Branch 0 taken 3667 times.
✗ Branch 1 not taken.
3667 op->op = factor2 > 0 ? SWS_OP_LSHIFT : SWS_OP_RSHIFT;
725 3667 op->shift.amount = FFABS(factor2);
726 3667 goto retry;
727 }
728 1403815 break;
729 }
730
731 4641510 case SWS_OP_FILTER_H:
732 case SWS_OP_FILTER_V:
733 /* Merge with prior simple planar read */
734
4/4
✓ Branch 0 taken 1401281 times.
✓ Branch 1 taken 3240229 times.
✓ Branch 2 taken 1057367 times.
✓ Branch 3 taken 343914 times.
4641510 if (prev->op == SWS_OP_READ && !prev->rw.filter.op &&
735
4/4
✓ Branch 0 taken 446095 times.
✓ Branch 1 taken 611272 times.
✓ Branch 2 taken 401730 times.
✓ Branch 3 taken 44365 times.
1057367 prev->rw.mode == SWS_RW_PLANAR && !prev->rw.frac) {
736 401730 prev->rw.filter.op = op->op;
737 401730 prev->rw.filter.kernel = av_refstruct_ref(op->filter.kernel);
738 401730 prev->rw.filter.type = op->filter.type;
739 401730 ff_sws_op_list_remove_at(ops, n, 1);
740 401730 goto retry;
741 }
742 4239780 break;
743 }
744 }
745
746 /* Push clears to the back to void any unused components */
747
2/2
✓ Branch 0 taken 14754657 times.
✓ Branch 1 taken 1726791 times.
16481448 for (int n = 0; n < ops->num_ops - 1; n++) {
748 14754657 SwsOp *op = &ops->ops[n];
749 14754657 SwsOp *next = &ops->ops[n + 1];
750
751
2/2
✓ Branch 0 taken 1001663 times.
✓ Branch 1 taken 13752994 times.
14754657 switch (op->op) {
752 1001663 case SWS_OP_CLEAR:
753
2/2
✓ Branch 1 taken 525070 times.
✓ Branch 2 taken 476593 times.
1001663 if (op_commute_clear(op, next)) {
754 525070 FFSWAP(SwsOp, *op, *next);
755 525070 goto retry;
756 }
757 476593 break;
758 }
759 }
760
761 /* Apply any remaining preferential re-ordering optimizations; do these
762 * last because they are more likely to block other optimizations if done
763 * too aggressively */
764
2/2
✓ Branch 0 taken 9474854 times.
✓ Branch 1 taken 1212147 times.
10687001 for (int n = 0; n < ops->num_ops - 1; n++) {
765 9474854 SwsOp *op = &ops->ops[n];
766 9474854 SwsOp *next = &ops->ops[n + 1];
767
768
3/3
✓ Branch 0 taken 1054668 times.
✓ Branch 1 taken 518064 times.
✓ Branch 2 taken 7902122 times.
9474854 switch (op->op) {
769 1054668 case SWS_OP_SWIZZLE: {
770 /* Try to push swizzles towards the output */
771
2/2
✓ Branch 1 taken 507799 times.
✓ Branch 2 taken 546869 times.
1054668 if (op_commute_swizzle(op, next)) {
772 507799 FFSWAP(SwsOp, *op, *next);
773 507799 goto retry;
774 }
775 546869 break;
776 }
777
778 518064 case SWS_OP_SCALE:
779 /* Exact integer multiplication */
780
4/4
✓ Branch 0 taken 144585 times.
✓ Branch 1 taken 373479 times.
✓ Branch 2 taken 24651 times.
✓ Branch 3 taken 119934 times.
518064 if (op->scale.factor.den == 1 && next->op == SWS_OP_CONVERT &&
781
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) &&
782 24651 op_result_is_exact(op))
783 {
784 6845 op->type = next->convert.to;
785 6845 FFSWAP(SwsOp, *op, *next);
786 6845 goto retry;
787 }
788 511219 break;
789 }
790 }
791
792 1212147 return 0;
793 }
794
795 106322 static int select_planes(SwsOpList *ops, SwsCompMask planes)
796 {
797 106322 SwsSwizzleOp swiz = SWS_SWIZZLE(0, 1, 2, 3);
798 106322 SwsOp *write = &ops->ops[ops->num_ops - 1];
799
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 106322 times.
106322 av_assert0(write->op == SWS_OP_WRITE);
800
801 106322 write->rw.elems = 0;
802
2/2
✓ Branch 0 taken 425288 times.
✓ Branch 1 taken 106322 times.
531610 for (int src = 0; src < 4; src++) {
803
2/2
✓ Branch 0 taken 220108 times.
✓ Branch 1 taken 205180 times.
425288 if (!SWS_COMP_TEST(planes, src))
804 220108 continue; /* plane not selected */
805 205180 const int dst = write->rw.elems++;
806 av_assert2(src >= dst);
807 205180 swiz.in[dst] = src;
808 205180 FFSWAP(int, ops->plane_dst[dst], ops->plane_dst[src]);
809 }
810
811 /* Insert swizzle to select desired planes */
812 106322 int ret = ff_sws_op_list_insert_at(ops, ops->num_ops - 1, &(SwsOp) {
813 .op = SWS_OP_SWIZZLE,
814 106322 .type = write->type,
815 .swizzle = swiz,
816 });
817
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 106322 times.
106322 if (ret < 0)
818 return ret;
819
820 /* The optimizer will take care of the rest */
821 106322 return ff_sws_op_list_optimize(ops);
822 }
823
824 1158690 int ff_sws_op_list_split_planes(SwsOpList *ops1, SwsOpList **out_ops2, SwsCompMask planes)
825 {
826 1158690 const SwsOp *write = ff_sws_op_list_output(ops1);
827
3/4
✓ Branch 0 taken 1158690 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 350771 times.
✓ Branch 3 taken 807919 times.
1158690 if (!write || write->rw.mode != SWS_RW_PLANAR) {
828 350771 *out_ops2 = NULL;
829 350771 return 0;
830 }
831
832 807919 const SwsCompMask full = SWS_COMP_ELEMS(write->rw.elems);
833 807919 const SwsCompMask mask1 = planes & full;
834 807919 const SwsCompMask mask2 = full ^ mask1;
835
4/4
✓ Branch 0 taken 106623 times.
✓ Branch 1 taken 701296 times.
✓ Branch 2 taken 53462 times.
✓ Branch 3 taken 53161 times.
807919 if (!mask1 || !mask2) {
836 /* Nothing to filter */
837 754758 *out_ops2 = NULL;
838 754758 return 0;
839 }
840
841 53161 SwsOpList *ops2 = ff_sws_op_list_duplicate(ops1);
842
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 53161 times.
53161 if (!ops2)
843 return AVERROR(ENOMEM);
844
845 int ret;
846
2/4
✓ Branch 1 taken 53161 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 53161 times.
106322 if ((ret = select_planes(ops1, mask1)) < 0 ||
847 53161 (ret = select_planes(ops2, mask2)) < 0)
848 {
849 ff_sws_op_list_free(&ops2);
850 return ret;
851 }
852
853 53161 *out_ops2 = ops2;
854 53161 return 0;
855 }
856
857 25106 int ff_sws_shuffle_mask(const SwsUOp *uop, int8_t shuffle[], int size)
858 {
859 25106 const SwsShuffleUOp *par = &uop->par.shuffle;
860 av_assert1(uop->uop == SWS_UOP_RW_SHUFFLE);
861 av_assert1(par->write_size <= sizeof(uop->data.shuffle.mask));
862 av_assert1(size <= INT8_MAX);
863
864 25106 const int num_groups = size / FFMAX(par->read_size, par->write_size);
865
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 25106 times.
25106 if (!num_groups)
866 return AVERROR(EINVAL);
867
868 25106 memset(shuffle, 0, size);
869
2/2
✓ Branch 0 taken 133185 times.
✓ Branch 1 taken 25106 times.
158291 for (int n = 0; n < num_groups; n++) {
870 133185 const int base_in = n * par->read_size;
871 133185 const int base_out = n * par->write_size;
872
2/2
✓ Branch 0 taken 322236 times.
✓ Branch 1 taken 133185 times.
455421 for (int i = 0; i < par->write_size; i++) {
873 322236 const int8_t idx = uop->data.shuffle.mask[i];
874 322236 shuffle[base_out + i] = idx + (idx >= 0) * base_in;
875 }
876 }
877
878 25106 return num_groups;
879 }
880
881 936 static bool pixel_is_repeating(SwsPixelType type, SwsPixel val)
882 {
883
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)) {
884 453 case 1: return true;
885 398 case 2: return val.u16 == val.u8 * 0x101ul;
886 85 case 4: return val.u32 == val.u8 * 0x1010101ul;
887 default: break;
888 }
889
890 av_unreachable("Invalid pixel type!");
891 return false;
892 }
893
894 624729 static int solve_shuffle(const SwsUOpList *const uops, SwsUOp *out)
895 {
896
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 624729 times.
624729 if (!uops->num_ops)
897 return AVERROR(EINVAL);
898 624729 const SwsUOp *read = &uops->ops[0];
899
3/3
✓ Branch 0 taken 93362 times.
✓ Branch 1 taken 163057 times.
✓ Branch 2 taken 368310 times.
624729 switch (read->uop) {
900 93362 case SWS_UOP_READ_PACKED:
901 93362 break;
902 163057 case SWS_UOP_READ_PLANAR:
903
2/2
✓ Branch 0 taken 79187 times.
✓ Branch 1 taken 83870 times.
163057 if (read->mask != SWS_COMP_ELEMS(1))
904 79187 return AVERROR(ENOTSUP);
905 83870 break;
906 368310 default:
907 368310 return AVERROR(ENOTSUP);
908 }
909
910 177232 const int read_size = ff_sws_pixel_type_size(read->type);
911 177232 uint32_t mask[4] = {0};
912 177232 int clear_val = -1;
913 177232 int read_elems = 0;
914
2/2
✓ Branch 0 taken 708928 times.
✓ Branch 1 taken 177232 times.
886160 for (int i = 0; i < 4; i++) {
915
2/2
✓ Branch 0 taken 409833 times.
✓ Branch 1 taken 299095 times.
708928 if (SWS_COMP_TEST(read->mask, i)) {
916 409833 mask[i] = 0x01010101 * i * read_size + 0x03020100;
917 409833 read_elems++;
918 }
919 }
920
921
1/2
✓ Branch 0 taken 263606 times.
✗ Branch 1 not taken.
263606 for (int opidx = 1; opidx < uops->num_ops; opidx++) {
922 263606 const SwsUOp *uop = &uops->ops[opidx];
923 263606 const SwsUOpParams *par = &uop->par;
924
7/7
✓ Branch 0 taken 13253 times.
✓ Branch 1 taken 71524 times.
✓ Branch 2 taken 932 times.
✓ Branch 3 taken 780 times.
✓ Branch 4 taken 78204 times.
✓ Branch 5 taken 1838 times.
✓ Branch 6 taken 97075 times.
263606 switch (uop->uop) {
925 13253 case SWS_UOP_COPY:
926 case SWS_UOP_PERMUTE: {
927 uint32_t tmp;
928
2/2
✓ Branch 0 taken 16854 times.
✓ Branch 1 taken 13253 times.
30107 for (int i = 0; i < par->move.num_moves; i++) {
929 16854 const int dst_idx = par->move.dst[i];
930 16854 const int src_idx = par->move.src[i];
931
2/2
✓ Branch 0 taken 790 times.
✓ Branch 1 taken 16064 times.
16854 uint32_t *src = src_idx < 0 ? &tmp : &mask[src_idx];
932
2/2
✓ Branch 0 taken 790 times.
✓ Branch 1 taken 16064 times.
16854 uint32_t *dst = dst_idx < 0 ? &tmp : &mask[dst_idx];
933 16854 *dst = *src;
934 }
935 13253 break;
936 }
937
938 71524 case SWS_UOP_SWAP_BYTES:
939
2/2
✓ Branch 0 taken 286096 times.
✓ Branch 1 taken 71524 times.
357620 for (int i = 0; i < 4; i++) {
940
2/3
✓ Branch 0 taken 199724 times.
✓ Branch 1 taken 86372 times.
✗ Branch 2 not taken.
286096 switch (ff_sws_pixel_type_size(uop->type)) {
941 199724 case 2: mask[i] = av_bswap16(mask[i]); break;
942 86372 case 4: mask[i] = av_bswap32(mask[i]); break;
943 }
944 }
945 71524 break;
946
947 932 case SWS_UOP_CLEAR:
948
2/2
✓ Branch 0 taken 3688 times.
✓ Branch 1 taken 817 times.
4505 for (int i = 0; i < 4; i++) {
949
2/2
✓ Branch 0 taken 2752 times.
✓ Branch 1 taken 936 times.
3688 if (!SWS_COMP_TEST(uop->mask, i))
950 2752 continue;
951 936 SwsPixel val = uop->data.vec4[i];
952
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) ||
953
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 (clear_val >= 0 && clear_val != val.u8))
954 115 return AVERROR(ENOTSUP); /* would require different bytes */
955 821 mask[i] = 0xFFFFFFFFul; /* (uint8_t[4]) { -1, -1, -1, -1 } */
956 821 clear_val = val.u8;
957 }
958 817 break;
959
960 780 case SWS_UOP_EXPAND_PAIR:
961 case SWS_UOP_EXPAND_QUAD:
962
2/2
✓ Branch 0 taken 3120 times.
✓ Branch 1 taken 780 times.
3900 for (int i = 0; i < 4; i++)
963 3120 mask[i] = 0x01010101 * (mask[i] & 0xFF);
964 780 break;
965
966 78204 case SWS_UOP_WRITE_PLANAR:
967
2/2
✓ Branch 0 taken 57501 times.
✓ Branch 1 taken 20703 times.
78204 if (uop->mask != SWS_COMP_ELEMS(1))
968 57501 return AVERROR(ENOTSUP);
969 av_fallthrough;
970 case SWS_UOP_WRITE_PACKED: {
971 22541 const int write_elems = av_popcount(uop->mask);
972 22541 const int write_size = ff_sws_pixel_type_size(uop->type);
973 22541 *out = (SwsUOp) {
974 .uop = SWS_UOP_RW_SHUFFLE,
975 .type = SWS_PIXEL_U8,
976 .mask = SWS_COMP_ELEMS(1), /* single plane for now */
977 };
978
979 22541 SwsShuffleUOp *par = &out->par.shuffle;
980 22541 SwsShuffleMask *data = &out->data.shuffle;
981 22541 *par = (SwsShuffleUOp) {
982 .read_size = read_elems * read_size,
983 .write_size = write_elems * write_size,
984 22541 .clear_value = clear_val >= 0 ? clear_val : 0,
985 };
986
987 /* Generate baseline shuffle for a single pixel */
988 22541 data->pixels = 1;
989
2/2
✓ Branch 0 taken 27235 times.
✓ Branch 1 taken 22541 times.
49776 for (int i = 0; i < write_elems; i++) {
990 27235 const int offset = i * write_size;
991
2/2
✓ Branch 0 taken 58744 times.
✓ Branch 1 taken 27235 times.
85979 for (int b = 0; b < write_size; b++)
992 58744 data->mask[offset + b] = mask[i] >> (b * 8);
993 }
994
995 /* Expand as many times as needed to round up to the size of the
996 * shuffle uop data mask */
997 int8_t tmp[FF_ARRAY_ELEMS(data->mask)];
998 22541 const int num_groups = ff_sws_shuffle_mask(out, tmp, sizeof(tmp));
999
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 22541 times.
22541 if (num_groups < 0)
1000 return num_groups;
1001 22541 memcpy(data->mask, tmp, sizeof(tmp));
1002 22541 par->read_size *= num_groups;
1003 22541 par->write_size *= num_groups;
1004 22541 data->pixels = num_groups;
1005 22541 return 0;
1006 }
1007
1008 97075 default:
1009 97075 return AVERROR(ENOTSUP);
1010 }
1011 }
1012
1013 return AVERROR(EINVAL);
1014 }
1015
1016 1313110 int ff_sws_uop_list_optimize(SwsContext *ctx, SwsUOpFlags flags, SwsUOpList *uops)
1017 {
1018 /* Try promoting the entire uop list to a packed shuffle operation */
1019
2/2
✓ Branch 0 taken 624729 times.
✓ Branch 1 taken 688381 times.
1313110 if (flags & SWS_UOP_FLAG_PSHUFB) {
1020 SwsUOp shuffle;
1021 624729 int ret = solve_shuffle(uops, &shuffle);
1022
2/2
✓ Branch 0 taken 22541 times.
✓ Branch 1 taken 602188 times.
624729 if (ret >= 0) {
1023 22541 ff_sws_uop_list_remove_at(uops, 0, uops->num_ops);
1024 22541 return ff_sws_uop_list_append(uops, &shuffle);
1025
2/4
✓ Branch 0 taken 602188 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 602188 times.
602188 } else if (ret < 0 && ret != AVERROR(ENOTSUP)) {
1026 return ret;
1027 }
1028 }
1029
1030 #if 0
1031 static const SwsUOp dummy = {0};
1032
1033 retry:
1034 for (int i = 0; i < uops->num_ops; i++) {
1035 const SwsUOp *next = i < uops->num_ops - 1 ? &uops->ops[i + 1] : &dummy;
1036 SwsUOp *op = &uops->ops[i];
1037
1038 switch (op->uop) {
1039 /* placeholder */
1040 }
1041 }
1042 #endif
1043
1044 1290569 return 0;
1045 }
1046
1047 /**
1048 * Determine a suitable intermediate buffer format for a given combination
1049 * of pixel types and number of planes. The exact interpretation of these
1050 * formats does not matter at all; since they will only ever be used as
1051 * temporary intermediate buffers. We still need to pick *some* format as
1052 * a consequence of ff_sws_graph_add_pass() taking an AVPixelFormat for the
1053 * output buffer.
1054 */
1055 319754 static enum AVPixelFormat get_planar_fmt(SwsPixelType type, int nb_planes)
1056 {
1057
3/4
✓ Branch 0 taken 79642 times.
✓ Branch 1 taken 118858 times.
✓ Branch 2 taken 121254 times.
✗ Branch 3 not taken.
319754 switch (ff_sws_pixel_type_size(type)) {
1058 79642 case 1:
1059
4/5
✓ Branch 0 taken 7017 times.
✓ Branch 1 taken 1030 times.
✓ Branch 2 taken 65646 times.
✓ Branch 3 taken 5949 times.
✗ Branch 4 not taken.
79642 switch (nb_planes) {
1060 7017 case 1: return AV_PIX_FMT_GRAY8;
1061 1030 case 2: return AV_PIX_FMT_YUV444P; // FIXME: no 2-plane planar fmt
1062 65646 case 3: return AV_PIX_FMT_YUV444P;
1063 5949 case 4: return AV_PIX_FMT_YUVA444P;
1064 }
1065 break;
1066 118858 case 2:
1067
4/5
✓ Branch 0 taken 20545 times.
✓ Branch 1 taken 2066 times.
✓ Branch 2 taken 85795 times.
✓ Branch 3 taken 10452 times.
✗ Branch 4 not taken.
118858 switch (nb_planes) {
1068 20545 case 1: return AV_PIX_FMT_GRAY16;
1069 2066 case 2: return AV_PIX_FMT_YUV444P16; // FIXME: no 2-plane planar fmt
1070 85795 case 3: return AV_PIX_FMT_YUV444P16;
1071 10452 case 4: return AV_PIX_FMT_YUVA444P16;
1072 }
1073 break;
1074 121254 case 4:
1075
4/5
✓ Branch 0 taken 22947 times.
✓ Branch 1 taken 3787 times.
✓ Branch 2 taken 82093 times.
✓ Branch 3 taken 12427 times.
✗ Branch 4 not taken.
121254 switch (nb_planes) {
1076 22947 case 1: return AV_PIX_FMT_GRAYF32;
1077 3787 case 2: return AV_PIX_FMT_GBRPF32; // FIXME: no 2-plane planar fmt
1078 82093 case 3: return AV_PIX_FMT_GBRPF32;
1079 12427 case 4: return AV_PIX_FMT_GBRAPF32;
1080 }
1081 break;
1082 }
1083
1084 av_unreachable("Invalid pixel type or number of planes?");
1085 return AV_PIX_FMT_NONE;
1086 }
1087
1088 319754 static void get_input_size(const SwsOpList *ops, SwsFormat *fmt)
1089 {
1090 319754 fmt->width = ops->src.width;
1091 319754 fmt->height = ops->src.height;
1092
1093 319754 const SwsOp *read = ff_sws_op_list_input(ops);
1094
2/4
✓ Branch 0 taken 319754 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 319754 times.
319754 if (read && read->rw.filter.op == SWS_OP_FILTER_V) {
1095 fmt->height = read->rw.filter.kernel->dst_size;
1096
3/4
✓ Branch 0 taken 319754 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 100445 times.
✓ Branch 3 taken 219309 times.
319754 } else if (read && read->rw.filter.op == SWS_OP_FILTER_H) {
1097 100445 fmt->width = read->rw.filter.kernel->dst_size;
1098 }
1099 319754 }
1100
1101 319754 int ff_sws_op_list_split_at(SwsOpList *ops1, SwsOpList **out_ops2, int index)
1102 {
1103 int ret;
1104
2/4
✓ Branch 0 taken 319754 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 319754 times.
319754 if (index <= 0 || index >= ops1->num_ops) {
1105 *out_ops2 = NULL;
1106 return 0;
1107 }
1108
1109 319754 const SwsOp *op = &ops1->ops[index];
1110 319754 const SwsOp *prev = &ops1->ops[index - 1];
1111
1112 319754 SwsOpList *ops2 = ff_sws_op_list_duplicate(ops1);
1113
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 319754 times.
319754 if (!ops2)
1114 return AVERROR(ENOMEM);
1115
1116 /**
1117 * Not all components may be needed; but we need the ones that *are*
1118 * used to be contiguous for the write/read operations. So, first
1119 * compress them into a linearly ascending list of components
1120 */
1121 319754 int nb_planes = 0;
1122 319754 SwsSwizzleOp swiz_wr = SWS_SWIZZLE(0, 1, 2, 3);
1123 319754 SwsSwizzleOp swiz_rd = SWS_SWIZZLE(0, 1, 2, 3);
1124
2/2
✓ Branch 0 taken 1279016 times.
✓ Branch 1 taken 319754 times.
1598770 for (int i = 0; i < 4; i++) {
1125
2/2
✓ Branch 0 taken 880189 times.
✓ Branch 1 taken 398827 times.
1279016 if (SWS_OP_NEEDED(prev, i)) {
1126 880189 const int o = nb_planes++;
1127 880189 swiz_wr.in[o] = i;
1128 880189 swiz_rd.in[i] = o;
1129 }
1130 }
1131
1132 /* Determine metadata for the intermediate format */
1133 319754 const SwsPixelType type = op->type;
1134 319754 ops2->src.format = get_planar_fmt(type, nb_planes);
1135 319754 ops2->src.desc = av_pix_fmt_desc_get(ops2->src.format);
1136 319754 get_input_size(ops1, &ops2->src);
1137 319754 ops1->dst = ops2->src;
1138
1139
2/2
✓ Branch 0 taken 880189 times.
✓ Branch 1 taken 319754 times.
1199943 for (int i = 0; i < nb_planes; i++) {
1140 880189 const int idx = swiz_wr.in[i];
1141 880189 ops1->plane_dst[i] = ops2->plane_src[i] = i;
1142 880189 ops2->comps_src.flags[i] = prev->comps.flags[idx];
1143 880189 ops2->comps_src.min[i] = prev->comps.min[idx];
1144 880189 ops2->comps_src.max[i] = prev->comps.max[idx];
1145 }
1146
1147 319754 ff_sws_op_list_remove_at(ops1, index, ops1->num_ops - index);
1148 319754 ff_sws_op_list_remove_at(ops2, 0, index);
1149 319754 op = NULL; /* the above command may invalidate op */
1150
1151
2/2
✓ Branch 0 taken 27778 times.
✓ Branch 1 taken 291976 times.
319754 if (swiz_wr.mask != SWS_SWIZZLE(0, 1, 2, 3).mask) {
1152 27778 ret = ff_sws_op_list_append(ops1, &(SwsOp) {
1153 .op = SWS_OP_SWIZZLE,
1154 .type = type,
1155 .swizzle = swiz_wr,
1156 });
1157
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 27778 times.
27778 if (ret < 0)
1158 goto fail;
1159 }
1160
1161 319754 ret = ff_sws_op_list_append(ops1, &(SwsOp) {
1162 .op = SWS_OP_WRITE,
1163 .type = type,
1164 .rw.elems = nb_planes,
1165 });
1166
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 319754 times.
319754 if (ret < 0)
1167 goto fail;
1168
1169 319754 ret = ff_sws_op_list_insert_at(ops2, 0, &(SwsOp) {
1170 .op = SWS_OP_READ,
1171 .type = type,
1172 .rw.elems = nb_planes,
1173 });
1174
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 319754 times.
319754 if (ret < 0)
1175 goto fail;
1176
1177
2/2
✓ Branch 0 taken 27778 times.
✓ Branch 1 taken 291976 times.
319754 if (swiz_rd.mask != SWS_SWIZZLE(0, 1, 2, 3).mask) {
1178 27778 ret = ff_sws_op_list_insert_at(ops2, 1, &(SwsOp) {
1179 .op = SWS_OP_SWIZZLE,
1180 .type = type,
1181 .swizzle = swiz_rd,
1182 });
1183
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 27778 times.
27778 if (ret < 0)
1184 goto fail;
1185 }
1186
1187 319754 ret = ff_sws_op_list_optimize(ops1);
1188
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 319754 times.
319754 if (ret < 0)
1189 goto fail;
1190
1191 319754 ret = ff_sws_op_list_optimize(ops2);
1192
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 319754 times.
319754 if (ret < 0)
1193 goto fail;
1194
1195 319754 *out_ops2 = ops2;
1196 319754 return 0;
1197
1198 fail:
1199 ff_sws_op_list_free(&ops2);
1200 return ret;
1201 }
1202