FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libswscale/ops_optimizer.c
Date: 2026-05-26 21:41:24
Exec Total Coverage
Lines: 523 576 90.8%
Functions: 14 14 100.0%
Branches: 412 488 84.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 157349 static bool op_commute_clear(SwsOp *op, SwsOp *next)
42 {
43 157349 SwsClearOp tmp = {0};
44
45 av_assert1(op->op == SWS_OP_CLEAR);
46
5/7
✓ Branch 0 taken 24570 times.
✓ Branch 1 taken 25635 times.
✓ Branch 2 taken 17623 times.
✓ Branch 3 taken 10135 times.
✓ Branch 4 taken 79386 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
157349 switch (next->op) {
47 24570 case SWS_OP_CONVERT:
48 24570 op->type = next->convert.to;
49 av_fallthrough;
50 50205 case SWS_OP_LSHIFT:
51 case SWS_OP_RSHIFT:
52 case SWS_OP_DITHER:
53 case SWS_OP_MIN:
54 case SWS_OP_MAX:
55 case SWS_OP_SCALE:
56 case SWS_OP_READ:
57 case SWS_OP_FILTER_H:
58 case SWS_OP_FILTER_V:
59 50205 ff_sws_apply_op_q(next, op->clear.value);
60 50205 return true;
61 17623 case SWS_OP_SWIZZLE:
62 17623 op->clear.mask = ff_sws_comp_mask_swizzle(op->clear.mask, next->swizzle);
63 17623 ff_sws_apply_op_q(next, op->clear.value);
64 17623 return true;
65 10135 case SWS_OP_SWAP_BYTES:
66
2/3
✓ Branch 0 taken 7519 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2616 times.
10135 switch (next->type) {
67 7519 case SWS_PIXEL_U16:
68 7519 ff_sws_apply_op_q(next, op->clear.value); /* always works */
69 7519 return true;
70 case SWS_PIXEL_U32:
71 for (int i = 0; i < 4; i++) {
72 if (!SWS_COMP_TEST(op->clear.mask, i))
73 continue;
74 uint32_t v = av_bswap32(op->clear.value[i].num);
75 if (v > INT_MAX)
76 return false; /* can't represent as AVRational anymore */
77 tmp.value[i] = Q(v);
78 }
79 op->clear = tmp;
80 return true;
81 2616 default:
82 2616 return false;
83 }
84 79386 case SWS_OP_INVALID:
85 case SWS_OP_WRITE:
86 case SWS_OP_LINEAR:
87 case SWS_OP_PACK:
88 case SWS_OP_UNPACK:
89 case SWS_OP_CLEAR:
90 79386 return false;
91 case SWS_OP_TYPE_NB:
92 break;
93 }
94
95 av_unreachable("Invalid operation type!");
96 return false;
97 }
98
99 /**
100 * Try to commute a swizzle op with the next operation. Makes any adjustments
101 * to the operations as needed, but does not perform the actual commutation.
102 *
103 * Returns whether successful.
104 */
105 183770 static bool op_commute_swizzle(SwsOp *op, SwsOp *next)
106 {
107 183770 bool seen[4] = {0};
108
109 av_assert1(op->op == SWS_OP_SWIZZLE);
110
5/7
✓ Branch 0 taken 35775 times.
✓ Branch 1 taken 33094 times.
✓ Branch 2 taken 10483 times.
✓ Branch 3 taken 9977 times.
✓ Branch 4 taken 94441 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
183770 switch (next->op) {
111 35775 case SWS_OP_CONVERT:
112 35775 op->type = next->convert.to;
113 av_fallthrough;
114 68869 case SWS_OP_SWAP_BYTES:
115 case SWS_OP_LSHIFT:
116 case SWS_OP_RSHIFT:
117 case SWS_OP_SCALE:
118 case SWS_OP_FILTER_H:
119 case SWS_OP_FILTER_V:
120 68869 return true;
121
122 /**
123 * We can commute per-channel ops only if the per-channel constants are the
124 * same for all duplicated channels; e.g.:
125 * SWIZZLE {0, 0, 0, 3}
126 * NEXT {x, x, x, w}
127 * ->
128 * NEXT {x, _, _, w}
129 * SWIZZLE {0, 0, 0, 3}
130 */
131 10483 case SWS_OP_MIN:
132 case SWS_OP_MAX: {
133 10483 const SwsClampOp c = next->clamp;
134
2/2
✓ Branch 0 taken 41932 times.
✓ Branch 1 taken 10483 times.
52415 for (int i = 0; i < 4; i++) {
135
2/2
✓ Branch 0 taken 9690 times.
✓ Branch 1 taken 32242 times.
41932 if (!SWS_OP_NEEDED(op, i))
136 9690 continue;
137 32242 const int j = op->swizzle.in[i];
138
3/4
✓ Branch 0 taken 6324 times.
✓ Branch 1 taken 25918 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 6324 times.
32242 if (seen[j] && av_cmp_q(next->clamp.limit[j], c.limit[i]))
139 return false;
140 32242 next->clamp.limit[j] = c.limit[i];
141 32242 seen[j] = true;
142 }
143 10483 return true;
144 }
145
146 9977 case SWS_OP_DITHER: {
147 9977 const SwsDitherOp d = next->dither;
148
2/2
✓ Branch 0 taken 36844 times.
✓ Branch 1 taken 8445 times.
45289 for (int i = 0; i < 4; i++) {
149
2/2
✓ Branch 0 taken 7963 times.
✓ Branch 1 taken 28881 times.
36844 if (!SWS_OP_NEEDED(op, i))
150 7963 continue;
151 28881 const int j = op->swizzle.in[i];
152
4/4
✓ Branch 0 taken 5480 times.
✓ Branch 1 taken 23401 times.
✓ Branch 2 taken 1532 times.
✓ Branch 3 taken 3948 times.
28881 if (seen[j] && next->dither.y_offset[j] != d.y_offset[i])
153 1532 return false;
154 27349 next->dither.y_offset[j] = d.y_offset[i];
155 27349 seen[j] = true;
156 }
157 8445 return true;
158 }
159
160 94441 case SWS_OP_INVALID:
161 case SWS_OP_READ:
162 case SWS_OP_WRITE:
163 case SWS_OP_SWIZZLE:
164 case SWS_OP_CLEAR:
165 case SWS_OP_LINEAR:
166 case SWS_OP_PACK:
167 case SWS_OP_UNPACK:
168 94441 return false;
169 case SWS_OP_TYPE_NB:
170 break;
171 }
172
173 av_unreachable("Invalid operation type!");
174 return false;
175 }
176
177 /**
178 * Try to commute a filter op with the previous operation. Makes any
179 * adjustments to the operations as needed, but does not perform the actual
180 * commutation.
181 *
182 * Returns whether successful.
183 */
184 1165682 static bool op_commute_filter(SwsOp *op, SwsOp *prev)
185 {
186
3/5
✓ Branch 0 taken 136781 times.
✓ Branch 1 taken 146376 times.
✓ Branch 2 taken 882525 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
1165682 switch (prev->op) {
187 136781 case SWS_OP_SWIZZLE:
188 case SWS_OP_SCALE:
189 case SWS_OP_LINEAR:
190 case SWS_OP_DITHER:
191 136781 prev->type = SWS_PIXEL_F32;
192 136781 return true;
193 146376 case SWS_OP_CONVERT:
194
2/2
✓ Branch 0 taken 69696 times.
✓ Branch 1 taken 76680 times.
146376 if (prev->convert.to == SWS_PIXEL_F32) {
195
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 69696 times.
69696 av_assert0(!prev->convert.expand);
196 69696 FFSWAP(SwsPixelType, op->type, prev->type);
197 69696 return true;
198 }
199 76680 return false;
200 882525 case SWS_OP_INVALID:
201 case SWS_OP_READ:
202 case SWS_OP_WRITE:
203 case SWS_OP_SWAP_BYTES:
204 case SWS_OP_UNPACK:
205 case SWS_OP_PACK:
206 case SWS_OP_LSHIFT:
207 case SWS_OP_RSHIFT:
208 case SWS_OP_CLEAR:
209 case SWS_OP_MIN:
210 case SWS_OP_MAX:
211 case SWS_OP_FILTER_H:
212 case SWS_OP_FILTER_V:
213 882525 return false;
214 case SWS_OP_TYPE_NB:
215 break;
216 }
217
218 av_unreachable("Invalid operation type!");
219 return false;
220 }
221
222 /* returns log2(x) only if x is a power of two, or 0 otherwise */
223 119827 static int exact_log2(const int x)
224 {
225 int p;
226
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 119827 times.
119827 if (x <= 0)
227 return 0;
228 119827 p = av_log2(x);
229
2/2
✓ Branch 0 taken 42723 times.
✓ Branch 1 taken 77104 times.
119827 return (1 << p) == x ? p : 0;
230 }
231
232 236999 static int exact_log2_q(const AVRational x)
233 {
234
2/2
✓ Branch 0 taken 63395 times.
✓ Branch 1 taken 173604 times.
236999 if (x.den == 1)
235 63395 return exact_log2(x.num);
236
2/2
✓ Branch 0 taken 56432 times.
✓ Branch 1 taken 117172 times.
173604 else if (x.num == 1)
237 56432 return -exact_log2(x.den);
238 else
239 117172 return 0;
240 }
241
242 /**
243 * If a linear operation can be reduced to a scalar multiplication, returns
244 * the corresponding scaling factor, or 0 otherwise.
245 */
246 340764 static bool extract_scalar(const SwsLinearOp *c, SwsComps comps, SwsComps prev,
247 SwsScaleOp *out_scale)
248 {
249 340764 SwsScaleOp scale = {0};
250
251 /* There are components not on the main diagonal */
252
2/2
✓ Branch 0 taken 262946 times.
✓ Branch 1 taken 77818 times.
340764 if (c->mask & ~SWS_MASK_DIAG4)
253 262946 return false;
254
255
2/2
✓ Branch 0 taken 231543 times.
✓ Branch 1 taken 27026 times.
258569 for (int i = 0; i < 4; i++) {
256 231543 const AVRational s = c->m[i][i];
257
1/2
✓ Branch 0 taken 231543 times.
✗ Branch 1 not taken.
231543 if ((prev.flags[i] & SWS_COMP_ZERO) ||
258
2/2
✓ Branch 0 taken 28126 times.
✓ Branch 1 taken 203417 times.
231543 (comps.flags[i] & SWS_COMP_GARBAGE))
259 28126 continue;
260
4/4
✓ Branch 0 taken 125599 times.
✓ Branch 1 taken 77818 times.
✓ Branch 3 taken 50792 times.
✓ Branch 4 taken 74807 times.
203417 if (scale.factor.den && av_cmp_q(s, scale.factor))
261 50792 return false;
262 152625 scale.factor = s;
263 }
264
265
1/2
✓ Branch 0 taken 27026 times.
✗ Branch 1 not taken.
27026 if (scale.factor.den)
266 27026 *out_scale = scale;
267 27026 return scale.factor.den;
268 }
269
270 /* Extracts an integer clear operation (subset) from the given linear op. */
271 364348 static bool extract_constant_rows(SwsLinearOp *c, SwsComps prev,
272 SwsClearOp *out_clear)
273 {
274 364348 SwsClearOp clear = {0};
275 364348 bool ret = false;
276
277
2/2
✓ Branch 0 taken 1457392 times.
✓ Branch 1 taken 364348 times.
1821740 for (int i = 0; i < 4; i++) {
278 1457392 bool const_row = c->m[i][4].den == 1; /* offset is integer */
279
2/2
✓ Branch 0 taken 5829568 times.
✓ Branch 1 taken 1457392 times.
7286960 for (int j = 0; j < 4; j++) {
280
2/2
✓ Branch 0 taken 2402096 times.
✓ Branch 1 taken 3427472 times.
8231664 const_row &= c->m[i][j].num == 0 || /* scalar is zero */
281
2/2
✓ Branch 0 taken 46242 times.
✓ Branch 1 taken 2355854 times.
2402096 (prev.flags[j] & SWS_COMP_ZERO); /* input is zero */
282 }
283
3/4
✓ Branch 0 taken 28788 times.
✓ Branch 1 taken 1428604 times.
✓ Branch 2 taken 28788 times.
✗ Branch 3 not taken.
1457392 if (const_row && (c->mask & SWS_MASK_ROW(i))) {
284 28788 clear.mask |= SWS_COMP(i);
285 28788 clear.value[i] = c->m[i][4];
286
2/2
✓ Branch 0 taken 143940 times.
✓ Branch 1 taken 28788 times.
172728 for (int j = 0; j < 5; j++)
287 143940 c->m[i][j] = Q(i == j);
288 28788 c->mask &= ~SWS_MASK_ROW(i);
289 28788 ret = true;
290 }
291 }
292
293
2/2
✓ Branch 0 taken 23584 times.
✓ Branch 1 taken 340764 times.
364348 if (ret)
294 23584 *out_clear = clear;
295 364348 return ret;
296 }
297
298 /* Unswizzle a linear operation by aligning single-input rows with
299 * their corresponding diagonal */
300 313738 static bool extract_swizzle(SwsLinearOp *op, SwsComps prev, SwsSwizzleOp *out_swiz)
301 {
302 313738 SwsSwizzleOp swiz = SWS_SWIZZLE(0, 1, 2, 3);
303 313738 SwsLinearOp c = *op;
304
305 /* Find non-zero coefficients in the main 4x4 matrix */
306 313738 uint32_t nonzero = 0;
307
2/2
✓ Branch 0 taken 1254952 times.
✓ Branch 1 taken 313738 times.
1568690 for (int i = 0; i < 4; i++) {
308
2/2
✓ Branch 0 taken 5019808 times.
✓ Branch 1 taken 1254952 times.
6274760 for (int j = 0; j < 4; j++) {
309
4/4
✓ Branch 0 taken 2154948 times.
✓ Branch 1 taken 2864860 times.
✓ Branch 2 taken 13824 times.
✓ Branch 3 taken 2141124 times.
5019808 if (!c.m[i][j].num || (prev.flags[j] & SWS_COMP_ZERO))
310 2878684 continue;
311 2141124 nonzero |= SWS_MASK(i, j);
312 }
313 }
314
315 /* If a value is unique in its row and the target column is
316 * empty, move it there and update the input swizzle */
317
2/2
✓ Branch 0 taken 1254952 times.
✓ Branch 1 taken 313738 times.
1568690 for (int i = 0; i < 4; i++) {
318
2/2
✓ Branch 0 taken 1241128 times.
✓ Branch 1 taken 13824 times.
1254952 if (nonzero & SWS_MASK_COL(i))
319 1241128 continue; /* target column is not empty */
320
1/2
✓ Branch 0 taken 13824 times.
✗ Branch 1 not taken.
13824 for (int j = 0; j < 4; j++) {
321
1/2
✓ Branch 0 taken 13824 times.
✗ Branch 1 not taken.
13824 if ((nonzero & SWS_MASK_ROW(i)) == SWS_MASK(i, j)) {
322 /* Move coefficient to the diagonal */
323 13824 c.m[i][i] = c.m[i][j];
324 13824 c.m[i][j] = Q(0);
325 13824 swiz.in[i] = j;
326 13824 break;
327 }
328 }
329 }
330
331
2/2
✓ Branch 0 taken 306826 times.
✓ Branch 1 taken 6912 times.
313738 if (swiz.mask == SWS_SWIZZLE(0, 1, 2, 3).mask)
332 306826 return false; /* no swizzle was identified */
333
334 6912 c.mask = ff_sws_linear_mask(c);
335 6912 *out_swiz = swiz;
336 6912 *op = c;
337 6912 return true;
338 }
339
340 7228 static int op_result_is_exact(const SwsOp *op)
341 {
342
2/2
✓ Branch 0 taken 13114 times.
✓ Branch 1 taken 1824 times.
14938 for (int i = 0; i < 4; i++) {
343
4/4
✓ Branch 0 taken 10508 times.
✓ Branch 1 taken 2606 times.
✓ Branch 2 taken 5404 times.
✓ Branch 3 taken 5104 times.
13114 if (SWS_OP_NEEDED(op, i) && !(op->comps.flags[i] & SWS_COMP_EXACT))
344 5404 return false;
345 }
346
347 1824 return true;
348 }
349
350 198024 int ff_sws_op_list_optimize(SwsOpList *ops)
351 {
352 int ret;
353
354 1343727 retry:
355 1541751 ff_sws_op_list_update_comps(ops);
356
357 /* Try to push filters towards the input; do this first to unblock
358 * in-place optimizations like linear op fusion */
359
2/2
✓ Branch 0 taken 13766828 times.
✓ Branch 1 taken 1335274 times.
15102102 for (int n = 1; n < ops->num_ops; n++) {
360 13766828 SwsOp *op = &ops->ops[n];
361 13766828 SwsOp *prev = &ops->ops[n - 1];
362
363
2/2
✓ Branch 0 taken 1165682 times.
✓ Branch 1 taken 12601146 times.
13766828 switch (op->op) {
364 1165682 case SWS_OP_FILTER_H:
365 case SWS_OP_FILTER_V:
366
2/2
✓ Branch 1 taken 206477 times.
✓ Branch 2 taken 959205 times.
1165682 if (op_commute_filter(op, prev)) {
367 206477 FFSWAP(SwsOp, *op, *prev);
368 206477 goto retry;
369 }
370 959205 break;
371 }
372 }
373
374 /* Apply all in-place optimizations (that do not re-order the list) */
375
2/2
✓ Branch 0 taken 7336734 times.
✓ Branch 1 taken 362992 times.
7699726 for (int n = 0; n < ops->num_ops; n++) {
376 7336734 SwsOp dummy = {0};
377 7336734 SwsOp *op = &ops->ops[n];
378
2/2
✓ Branch 0 taken 6001460 times.
✓ Branch 1 taken 1335274 times.
7336734 SwsOp *prev = n ? &ops->ops[n - 1] : &dummy;
379
2/2
✓ Branch 0 taken 6973742 times.
✓ Branch 1 taken 362992 times.
7336734 SwsOp *next = n + 1 < ops->num_ops ? &ops->ops[n + 1] : &dummy;
380
381 /* common helper variable */
382 7336734 bool noop = true;
383
384
4/4
✓ Branch 0 taken 694347 times.
✓ Branch 1 taken 6642387 times.
✓ Branch 2 taken 371789 times.
✓ Branch 3 taken 322558 times.
7336734 if (!SWS_OP_NEEDED(op, 0) && !SWS_OP_NEEDED(op, 1) &&
385
3/4
✓ Branch 0 taken 362992 times.
✓ Branch 1 taken 8797 times.
✓ Branch 2 taken 362992 times.
✗ Branch 3 not taken.
371789 !SWS_OP_NEEDED(op, 2) && !SWS_OP_NEEDED(op, 3) &&
386
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 362992 times.
362992 op->op != SWS_OP_WRITE)
387 {
388 /* Remove any operation whose output is not needed */
389 ff_sws_op_list_remove_at(ops, n, 1);
390 972282 goto retry;
391 }
392
393
14/14
✓ Branch 0 taken 1335274 times.
✓ Branch 1 taken 575696 times.
✓ Branch 2 taken 220068 times.
✓ Branch 3 taken 120452 times.
✓ Branch 4 taken 468050 times.
✓ Branch 5 taken 837806 times.
✓ Branch 6 taken 952294 times.
✓ Branch 7 taken 274750 times.
✓ Branch 8 taken 161375 times.
✓ Branch 9 taken 342244 times.
✓ Branch 10 taken 640368 times.
✓ Branch 11 taken 236999 times.
✓ Branch 12 taken 744304 times.
✓ Branch 13 taken 427054 times.
7336734 switch (op->op) {
394 1335274 case SWS_OP_READ:
395 /* "Compress" planar reads where not all components are needed */
396
2/2
✓ Branch 0 taken 805330 times.
✓ Branch 1 taken 529944 times.
1335274 if (!op->rw.packed) {
397 805330 SwsSwizzleOp swiz = SWS_SWIZZLE(0, 1, 2, 3);
398 805330 int nb_planes = 0;
399
2/2
✓ Branch 0 taken 2042314 times.
✓ Branch 1 taken 805330 times.
2847644 for (int i = 0; i < op->rw.elems; i++) {
400
2/2
✓ Branch 0 taken 13220 times.
✓ Branch 1 taken 2029094 times.
2042314 if (!SWS_OP_NEEDED(op, i)) {
401 13220 swiz.in[i] = 3 - (i - nb_planes); /* map to unused plane */
402 13220 continue;
403 }
404
405 2029094 const int idx = nb_planes++;
406 av_assert1(idx <= i);
407 2029094 ops->plane_src[idx] = ops->plane_src[i];
408 2029094 swiz.in[i] = idx;
409 }
410
411
2/2
✓ Branch 0 taken 10770 times.
✓ Branch 1 taken 794560 times.
805330 if (nb_planes < op->rw.elems) {
412 10770 op->rw.elems = nb_planes;
413
3/4
✓ Branch 0 taken 10155 times.
✓ Branch 1 taken 615 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 10770 times.
10770 RET(ff_sws_op_list_insert_at(ops, n + 1, &(SwsOp) {
414 .op = SWS_OP_SWIZZLE,
415 .type = op->rw.filter ? SWS_PIXEL_F32 : op->type,
416 .swizzle = swiz,
417 }));
418 10770 goto retry;
419 }
420 }
421 1324504 break;
422
423 575696 case SWS_OP_SWAP_BYTES:
424 /* Redundant (double) swap */
425
2/2
✓ Branch 0 taken 209 times.
✓ Branch 1 taken 575487 times.
575696 if (next->op == SWS_OP_SWAP_BYTES) {
426 209 ff_sws_op_list_remove_at(ops, n, 2);
427 209 goto retry;
428 }
429 575487 break;
430
431 220068 case SWS_OP_UNPACK:
432 /* Redundant unpack+pack */
433
3/4
✓ Branch 0 taken 46 times.
✓ Branch 1 taken 220022 times.
✓ Branch 2 taken 46 times.
✗ Branch 3 not taken.
220068 if (next->op == SWS_OP_PACK && next->type == op->type &&
434
1/2
✓ Branch 0 taken 46 times.
✗ Branch 1 not taken.
46 next->pack.pattern[0] == op->pack.pattern[0] &&
435
1/2
✓ Branch 0 taken 46 times.
✗ Branch 1 not taken.
46 next->pack.pattern[1] == op->pack.pattern[1] &&
436
1/2
✓ Branch 0 taken 46 times.
✗ Branch 1 not taken.
46 next->pack.pattern[2] == op->pack.pattern[2] &&
437
1/2
✓ Branch 0 taken 46 times.
✗ Branch 1 not taken.
46 next->pack.pattern[3] == op->pack.pattern[3])
438 {
439 46 ff_sws_op_list_remove_at(ops, n, 2);
440 46 goto retry;
441 }
442 220022 break;
443
444 120452 case SWS_OP_LSHIFT:
445 case SWS_OP_RSHIFT:
446 /* Two shifts in the same direction */
447
2/2
✓ Branch 0 taken 204 times.
✓ Branch 1 taken 120248 times.
120452 if (next->op == op->op) {
448 204 op->shift.amount += next->shift.amount;
449 204 ff_sws_op_list_remove_at(ops, n + 1, 1);
450 204 goto retry;
451 }
452
453 /* No-op shift */
454
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 120248 times.
120248 if (!op->shift.amount) {
455 ff_sws_op_list_remove_at(ops, n, 1);
456 goto retry;
457 }
458 120248 break;
459
460 468050 case SWS_OP_CLEAR:
461
2/2
✓ Branch 0 taken 1872200 times.
✓ Branch 1 taken 468050 times.
2340250 for (int i = 0; i < 4; i++) {
462
2/2
✓ Branch 0 taken 1254035 times.
✓ Branch 1 taken 618165 times.
1872200 if (!SWS_COMP_TEST(op->clear.mask, i))
463 1254035 continue;
464
465
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 618165 times.
618165 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 83976 times.
✓ Branch 1 taken 534189 times.
618165 } else if (!SWS_OP_NEEDED(op, i)) {
472 /* Unnecessary clear of unused component */
473 83976 op->clear.mask ^= SWS_COMP(i);
474 } else {
475 534189 noop = false;
476 }
477 }
478
479
2/2
✓ Branch 0 taken 83976 times.
✓ Branch 1 taken 384074 times.
468050 if (noop) {
480 83976 ff_sws_op_list_remove_at(ops, n, 1);
481 83976 goto retry;
482 }
483
484 /* Transitive clear */
485
2/2
✓ Branch 0 taken 490 times.
✓ Branch 1 taken 383584 times.
384074 if (next->op == SWS_OP_CLEAR) {
486
2/2
✓ Branch 0 taken 1960 times.
✓ Branch 1 taken 490 times.
2450 for (int i = 0; i < 4; i++) {
487
2/2
✓ Branch 0 taken 490 times.
✓ Branch 1 taken 1470 times.
1960 if (SWS_COMP_TEST(next->clear.mask, i))
488 490 op->clear.value[i] = next->clear.value[i];
489 }
490 490 op->clear.mask |= next->clear.mask;
491 490 ff_sws_op_list_remove_at(ops, n + 1, 1);
492 490 goto retry;
493 }
494 383584 break;
495
496 837806 case SWS_OP_SWIZZLE:
497
2/2
✓ Branch 0 taken 3351224 times.
✓ Branch 1 taken 837806 times.
4189030 for (int i = 0; i < 4; i++) {
498
2/2
✓ Branch 0 taken 827000 times.
✓ Branch 1 taken 2524224 times.
3351224 if (!SWS_OP_NEEDED(op, i))
499 827000 continue;
500
2/2
✓ Branch 0 taken 1732195 times.
✓ Branch 1 taken 792029 times.
2524224 if (op->swizzle.in[i] != i)
501 1732195 noop = false;
502 }
503
504 /* Identity swizzle */
505
2/2
✓ Branch 0 taken 132724 times.
✓ Branch 1 taken 705082 times.
837806 if (noop) {
506 132724 ff_sws_op_list_remove_at(ops, n, 1);
507 132724 goto retry;
508 }
509
510 /* Transitive swizzle */
511
2/2
✓ Branch 0 taken 14158 times.
✓ Branch 1 taken 690924 times.
705082 if (next->op == SWS_OP_SWIZZLE) {
512 14158 const SwsSwizzleOp orig = op->swizzle;
513
2/2
✓ Branch 0 taken 56632 times.
✓ Branch 1 taken 14158 times.
70790 for (int i = 0; i < 4; i++)
514 56632 op->swizzle.in[i] = orig.in[next->swizzle.in[i]];
515 14158 ff_sws_op_list_remove_at(ops, n + 1, 1);
516 14158 goto retry;
517 }
518
519 /* Swizzle planes instead of components, if possible */
520
4/4
✓ Branch 0 taken 112973 times.
✓ Branch 1 taken 577951 times.
✓ Branch 2 taken 45581 times.
✓ Branch 3 taken 67392 times.
690924 if (prev->op == SWS_OP_READ && !prev->rw.packed) {
521
2/2
✓ Branch 0 taken 64538 times.
✓ Branch 1 taken 9884 times.
74422 for (int dst = 0; dst < prev->rw.elems; dst++) {
522 64538 const int src = op->swizzle.in[dst];
523
4/4
✓ Branch 0 taken 37055 times.
✓ Branch 1 taken 27483 times.
✓ Branch 2 taken 35697 times.
✓ Branch 3 taken 1358 times.
64538 if (src > dst && src < prev->rw.elems) {
524 35697 FFSWAP(int, ops->plane_src[dst], ops->plane_src[src]);
525
2/2
✓ Branch 0 taken 127653 times.
✓ Branch 1 taken 35697 times.
163350 for (int i = dst; i < 4; i++) {
526
2/2
✓ Branch 0 taken 37344 times.
✓ Branch 1 taken 90309 times.
127653 if (op->swizzle.in[i] == dst)
527 37344 op->swizzle.in[i] = src;
528
2/2
✓ Branch 0 taken 35697 times.
✓ Branch 1 taken 54612 times.
90309 else if (op->swizzle.in[i] == src)
529 35697 op->swizzle.in[i] = dst;
530 }
531 35697 goto retry;
532 }
533 }
534 }
535
536
4/4
✓ Branch 0 taken 88721 times.
✓ Branch 1 taken 566506 times.
✓ Branch 2 taken 52947 times.
✓ Branch 3 taken 35774 times.
655227 if (next->op == SWS_OP_WRITE && !next->rw.packed) {
537
2/2
✓ Branch 0 taken 74342 times.
✓ Branch 1 taken 11204 times.
85546 for (int dst = 0; dst < next->rw.elems; dst++) {
538 74342 const int src = op->swizzle.in[dst];
539
4/4
✓ Branch 0 taken 50119 times.
✓ Branch 1 taken 24223 times.
✓ Branch 2 taken 41743 times.
✓ Branch 3 taken 8376 times.
74342 if (src > dst && src < next->rw.elems) {
540 41743 FFSWAP(int, ops->plane_dst[dst], ops->plane_dst[src]);
541 41743 FFSWAP(int, op->swizzle.in[dst], op->swizzle.in[src]);
542 41743 goto retry;
543 }
544 }
545 }
546 613484 break;
547
548 952294 case SWS_OP_CONVERT:
549 /* No-op conversion */
550
2/2
✓ Branch 0 taken 69676 times.
✓ Branch 1 taken 882618 times.
952294 if (op->type == op->convert.to) {
551 69676 ff_sws_op_list_remove_at(ops, n, 1);
552 69676 goto retry;
553 }
554
555 /* Transitive conversion */
556
2/2
✓ Branch 0 taken 22049 times.
✓ Branch 1 taken 860569 times.
882618 if (next->op == SWS_OP_CONVERT &&
557
1/2
✓ Branch 0 taken 22049 times.
✗ Branch 1 not taken.
22049 op->convert.expand == next->convert.expand)
558 {
559 av_assert1(op->convert.to == next->type);
560 22049 op->convert.to = next->convert.to;
561 22049 ff_sws_op_list_remove_at(ops, n + 1, 1);
562 22049 goto retry;
563 }
564
565 /* Conversion followed by integer expansion */
566
3/4
✓ Branch 0 taken 64481 times.
✓ Branch 1 taken 796088 times.
✓ Branch 2 taken 64481 times.
✗ Branch 3 not taken.
860569 if (next->op == SWS_OP_SCALE && !op->convert.expand &&
567
1/2
✓ Branch 0 taken 64481 times.
✗ Branch 1 not taken.
64481 ff_sws_pixel_type_is_int(op->type) &&
568
4/4
✓ Branch 0 taken 1078 times.
✓ Branch 1 taken 63403 times.
✓ Branch 2 taken 360 times.
✓ Branch 3 taken 718 times.
65559 ff_sws_pixel_type_is_int(op->convert.to) &&
569 1078 !av_cmp_q(next->scale.factor,
570 ff_sws_pixel_expand(op->type, op->convert.to)))
571 {
572 360 op->convert.expand = true;
573 360 ff_sws_op_list_remove_at(ops, n + 1, 1);
574 360 goto retry;
575 }
576 860209 break;
577
578 274750 case SWS_OP_MIN:
579
2/2
✓ Branch 0 taken 1099000 times.
✓ Branch 1 taken 274750 times.
1373750 for (int i = 0; i < 4; i++) {
580
4/4
✓ Branch 0 taken 804692 times.
✓ Branch 1 taken 294308 times.
✓ Branch 2 taken 10164 times.
✓ Branch 3 taken 794528 times.
1099000 if (!SWS_OP_NEEDED(op, i) || !op->clamp.limit[i].den)
581 304472 continue;
582
2/2
✓ Branch 1 taken 657154 times.
✓ Branch 2 taken 137374 times.
794528 if (av_cmp_q(op->clamp.limit[i], prev->comps.max[i]) < 0)
583 657154 noop = false;
584 }
585
586
2/2
✓ Branch 0 taken 31653 times.
✓ Branch 1 taken 243097 times.
274750 if (noop) {
587 31653 ff_sws_op_list_remove_at(ops, n, 1);
588 31653 goto retry;
589 }
590 243097 break;
591
592 161375 case SWS_OP_MAX:
593
2/2
✓ Branch 0 taken 645500 times.
✓ Branch 1 taken 161375 times.
806875 for (int i = 0; i < 4; i++) {
594
3/4
✓ Branch 0 taken 470464 times.
✓ Branch 1 taken 175036 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 470464 times.
645500 if (!SWS_OP_NEEDED(op, i) || !op->clamp.limit[i].den)
595 175036 continue;
596
2/2
✓ Branch 1 taken 280940 times.
✓ Branch 2 taken 189524 times.
470464 if (av_cmp_q(prev->comps.min[i], op->clamp.limit[i]) < 0)
597 280940 noop = false;
598 }
599
600
2/2
✓ Branch 0 taken 54681 times.
✓ Branch 1 taken 106694 times.
161375 if (noop) {
601 54681 ff_sws_op_list_remove_at(ops, n, 1);
602 54681 goto retry;
603 }
604 106694 break;
605
606 342244 case SWS_OP_DITHER:
607
2/2
✓ Branch 0 taken 1326382 times.
✓ Branch 1 taken 274326 times.
1600708 for (int i = 0; i < 4; i++) {
608
2/2
✓ Branch 0 taken 386191 times.
✓ Branch 1 taken 940191 times.
1326382 if (op->dither.y_offset[i] < 0)
609 386191 continue;
610
4/4
✓ Branch 0 taken 892901 times.
✓ Branch 1 taken 47290 times.
✓ Branch 2 taken 20628 times.
✓ Branch 3 taken 872273 times.
940191 if (!SWS_OP_NEEDED(op, i) || (prev->comps.flags[i] & SWS_COMP_EXACT)) {
611 67918 op->dither.y_offset[i] = -1; /* unnecessary dither */
612 67918 goto retry;
613 } else {
614 872273 noop = false;
615 }
616 }
617
618
2/2
✓ Branch 0 taken 1712 times.
✓ Branch 1 taken 272614 times.
274326 if (noop) {
619 1712 ff_sws_op_list_remove_at(ops, n, 1);
620 1712 goto retry;
621 }
622 272614 break;
623
624 640368 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 8994 times.
✓ Branch 1 taken 631374 times.
640368 if (!op->lin.mask) {
631 8994 ff_sws_op_list_remove_at(ops, n, 1);
632 333542 goto retry;
633 }
634
635
2/2
✓ Branch 0 taken 156778 times.
✓ Branch 1 taken 474596 times.
631374 if (next->op == SWS_OP_LINEAR) {
636 /* 5x5 matrix multiplication after appending [ 0 0 0 0 1 ] */
637 156778 const SwsLinearOp m1 = op->lin;
638 156778 const SwsLinearOp m2 = next->lin;
639
2/2
✓ Branch 0 taken 627112 times.
✓ Branch 1 taken 156778 times.
783890 for (int i = 0; i < 4; i++) {
640
2/2
✓ Branch 0 taken 3135560 times.
✓ Branch 1 taken 627112 times.
3762672 for (int j = 0; j < 5; j++) {
641 3135560 AVRational sum = Q(0);
642
2/2
✓ Branch 0 taken 12542240 times.
✓ Branch 1 taken 3135560 times.
15677800 for (int k = 0; k < 4; k++)
643 12542240 sum = av_add_q(sum, av_mul_q(m2.m[i][k], m1.m[k][j]));
644
2/2
✓ Branch 0 taken 627112 times.
✓ Branch 1 taken 2508448 times.
3135560 if (j == 4) /* m1.m[4][j] == 1 */
645 627112 sum = av_add_q(sum, m2.m[i][4]);
646 3135560 op->lin.m[i][j] = sum;
647 }
648 }
649 156778 op->lin.mask = ff_sws_linear_mask(op->lin);
650 156778 ff_sws_op_list_remove_at(ops, n + 1, 1);
651 156778 goto retry;
652 }
653
654 /* Optimize away zero columns */
655
2/2
✓ Branch 0 taken 1865996 times.
✓ Branch 1 taken 437636 times.
2303632 for (int j = 0; j < 4; j++) {
656 1865996 const uint32_t col = SWS_MASK_COL(j);
657
4/4
✓ Branch 0 taken 114418 times.
✓ Branch 1 taken 1751578 times.
✓ Branch 2 taken 77458 times.
✓ Branch 3 taken 36960 times.
1865996 if (!(prev->comps.flags[j] & SWS_COMP_ZERO) || !(op->lin.mask & col))
658 1829036 continue;
659
2/2
✓ Branch 0 taken 147840 times.
✓ Branch 1 taken 36960 times.
184800 for (int i = 0; i < 4; i++)
660 147840 op->lin.m[i][j] = Q(i == j);
661 36960 op->lin.mask &= ~col;
662 36960 goto retry;
663 }
664
665 /* Optimize away unused rows */
666
2/2
✓ Branch 0 taken 1718156 times.
✓ Branch 1 taken 364348 times.
2082504 for (int i = 0; i < 4; i++) {
667 1718156 const uint32_t row = SWS_MASK_ROW(i);
668
4/4
✓ Branch 0 taken 607034 times.
✓ Branch 1 taken 1111122 times.
✓ Branch 2 taken 533746 times.
✓ Branch 3 taken 73288 times.
1718156 if (SWS_OP_NEEDED(op, i) || !(op->lin.mask & row))
669 1644868 continue;
670
2/2
✓ Branch 0 taken 366440 times.
✓ Branch 1 taken 73288 times.
439728 for (int j = 0; j < 5; j++)
671 366440 op->lin.m[i][j] = Q(i == j);
672 73288 op->lin.mask &= ~row;
673 73288 goto retry;
674 }
675
676 /* Convert constant rows to explicit clear instruction */
677
2/2
✓ Branch 1 taken 23584 times.
✓ Branch 2 taken 340764 times.
364348 if (extract_constant_rows(&op->lin, prev->comps, &clear)) {
678
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 23584 times.
23584 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 23584 goto retry;
685 }
686
687 /* Multiplication by scalar constant */
688
2/2
✓ Branch 1 taken 27026 times.
✓ Branch 2 taken 313738 times.
340764 if (extract_scalar(&op->lin, op->comps, prev->comps, &scale)) {
689 27026 op->op = SWS_OP_SCALE;
690 27026 op->scale = scale;
691 27026 goto retry;
692 }
693
694 /* Swizzle by fixed pattern */
695
2/2
✓ Branch 1 taken 6912 times.
✓ Branch 2 taken 306826 times.
313738 if (extract_swizzle(&op->lin, prev->comps, &swizzle)) {
696
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 6912 times.
6912 RET(ff_sws_op_list_insert_at(ops, n, &(SwsOp) {
697 .op = SWS_OP_SWIZZLE,
698 .type = op->type,
699 .swizzle = swizzle,
700 }));
701 6912 goto retry;
702 }
703 306826 break;
704 }
705
706 236999 case SWS_OP_SCALE: {
707 236999 const int factor2 = exact_log2_q(op->scale.factor);
708
709 /* No-op scaling */
710
3/4
✓ Branch 0 taken 56432 times.
✓ Branch 1 taken 180567 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 56432 times.
236999 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 (that don't overflow) */
716
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 236999 times.
236999 if (next->op == SWS_OP_SCALE) {
717 int64_t p = op->scale.factor.num * (int64_t) next->scale.factor.num;
718 int64_t q = op->scale.factor.den * (int64_t) next->scale.factor.den;
719 if (FFABS(p) <= INT_MAX && FFABS(q) <= INT_MAX) {
720 av_reduce(&op->scale.factor.num, &op->scale.factor.den, p, q, INT_MAX);
721 ff_sws_op_list_remove_at(ops, n + 1, 1);
722 goto retry;
723 }
724 }
725
726 /* Scaling by exact power of two */
727
4/4
✓ Branch 0 taken 42723 times.
✓ Branch 1 taken 194276 times.
✓ Branch 2 taken 978 times.
✓ Branch 3 taken 41745 times.
236999 if (factor2 && ff_sws_pixel_type_is_int(op->type)) {
728
1/2
✓ Branch 0 taken 978 times.
✗ Branch 1 not taken.
978 op->op = factor2 > 0 ? SWS_OP_LSHIFT : SWS_OP_RSHIFT;
729 978 op->shift.amount = FFABS(factor2);
730 978 goto retry;
731 }
732 236021 break;
733 }
734
735 744304 case SWS_OP_FILTER_H:
736 case SWS_OP_FILTER_V:
737 /* Merge with prior simple planar read */
738
4/4
✓ Branch 0 taken 224270 times.
✓ Branch 1 taken 520034 times.
✓ Branch 2 taken 170166 times.
✓ Branch 3 taken 54104 times.
744304 if (prev->op == SWS_OP_READ && !prev->rw.filter &&
739
4/4
✓ Branch 0 taken 75804 times.
✓ Branch 1 taken 94362 times.
✓ Branch 2 taken 69696 times.
✓ Branch 3 taken 6108 times.
170166 !prev->rw.packed && !prev->rw.frac) {
740 69696 prev->rw.filter = op->op;
741 69696 prev->rw.kernel = av_refstruct_ref(op->filter.kernel);
742 69696 ff_sws_op_list_remove_at(ops, n, 1);
743 69696 goto retry;
744 }
745 674608 break;
746 }
747 }
748
749 /* Push clears to the back to void any unused components */
750
2/2
✓ Branch 0 taken 2382237 times.
✓ Branch 1 taken 287645 times.
2669882 for (int n = 0; n < ops->num_ops - 1; n++) {
751 2382237 SwsOp *op = &ops->ops[n];
752 2382237 SwsOp *next = &ops->ops[n + 1];
753
754
2/2
✓ Branch 0 taken 157349 times.
✓ Branch 1 taken 2224888 times.
2382237 switch (op->op) {
755 157349 case SWS_OP_CLEAR:
756
2/2
✓ Branch 1 taken 75347 times.
✓ Branch 2 taken 82002 times.
157349 if (op_commute_clear(op, next)) {
757 75347 FFSWAP(SwsOp, *op, *next);
758 75347 goto retry;
759 }
760 82002 break;
761 }
762 }
763
764 /* Apply any remaining preferential re-ordering optimizations; do these
765 * last because they are more likely to block other optimizations if done
766 * too aggressively */
767
2/2
✓ Branch 0 taken 1542407 times.
✓ Branch 1 taken 198024 times.
1740431 for (int n = 0; n < ops->num_ops - 1; n++) {
768 1542407 SwsOp *op = &ops->ops[n];
769 1542407 SwsOp *next = &ops->ops[n + 1];
770
771
3/3
✓ Branch 0 taken 183770 times.
✓ Branch 1 taken 85205 times.
✓ Branch 2 taken 1273432 times.
1542407 switch (op->op) {
772 183770 case SWS_OP_SWIZZLE: {
773 /* Try to push swizzles towards the output */
774
2/2
✓ Branch 1 taken 87797 times.
✓ Branch 2 taken 95973 times.
183770 if (op_commute_swizzle(op, next)) {
775 87797 FFSWAP(SwsOp, *op, *next);
776 87797 goto retry;
777 }
778 95973 break;
779 }
780
781 85205 case SWS_OP_SCALE:
782 /* Exact integer multiplication */
783
4/4
✓ Branch 0 taken 23696 times.
✓ Branch 1 taken 61509 times.
✓ Branch 2 taken 7228 times.
✓ Branch 3 taken 16468 times.
85205 if (op->scale.factor.den == 1 && next->op == SWS_OP_CONVERT &&
784
3/4
✓ Branch 0 taken 7228 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1824 times.
✓ Branch 3 taken 5404 times.
14456 ff_sws_pixel_type_is_int(next->convert.to) &&
785 7228 op_result_is_exact(op))
786 {
787 1824 op->type = next->convert.to;
788 1824 FFSWAP(SwsOp, *op, *next);
789 1824 goto retry;
790 }
791 83381 break;
792 }
793 }
794
795 198024 return 0;
796 }
797
798 20440 int ff_sws_solve_shuffle(const SwsOpList *const ops, uint8_t shuffle[],
799 int size, uint8_t clear_val,
800 int *read_bytes, int *write_bytes)
801 {
802
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 20440 times.
20440 if (!ops->num_ops)
803 return AVERROR(EINVAL);
804
805 20440 const SwsOp *read = ff_sws_op_list_input(ops);
806
5/6
✓ Branch 0 taken 20440 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 20188 times.
✓ Branch 3 taken 252 times.
✓ Branch 4 taken 17500 times.
✓ Branch 5 taken 2688 times.
20440 if (!read || read->rw.frac || read->rw.filter ||
807
4/4
✓ Branch 0 taken 11228 times.
✓ Branch 1 taken 6272 times.
✓ Branch 2 taken 9162 times.
✓ Branch 3 taken 2066 times.
17500 (!read->rw.packed && read->rw.elems > 1))
808 12102 return AVERROR(ENOTSUP);
809
810 8338 const int read_size = ff_sws_pixel_type_size(read->type);
811 8338 uint32_t mask[4] = {0};
812
2/2
✓ Branch 0 taken 18383 times.
✓ Branch 1 taken 8338 times.
26721 for (int i = 0; i < read->rw.elems; i++)
813 18383 mask[i] = 0x01010101 * i * read_size + 0x03020100;
814
815
1/2
✓ Branch 0 taken 11590 times.
✗ Branch 1 not taken.
11590 for (int opidx = 1; opidx < ops->num_ops; opidx++) {
816 11590 const SwsOp *op = &ops->ops[opidx];
817
6/6
✓ Branch 0 taken 379 times.
✓ Branch 1 taken 2663 times.
✓ Branch 2 taken 210 times.
✓ Branch 3 taken 4840 times.
✓ Branch 4 taken 528 times.
✓ Branch 5 taken 2970 times.
11590 switch (op->op) {
818 379 case SWS_OP_SWIZZLE: {
819 379 uint32_t orig[4] = { mask[0], mask[1], mask[2], mask[3] };
820
2/2
✓ Branch 0 taken 1516 times.
✓ Branch 1 taken 379 times.
1895 for (int i = 0; i < 4; i++)
821 1516 mask[i] = orig[op->swizzle.in[i]];
822 379 break;
823 }
824
825 2663 case SWS_OP_SWAP_BYTES:
826
2/2
✓ Branch 0 taken 10652 times.
✓ Branch 1 taken 2663 times.
13315 for (int i = 0; i < 4; i++) {
827
2/3
✓ Branch 0 taken 10172 times.
✓ Branch 1 taken 480 times.
✗ Branch 2 not taken.
10652 switch (ff_sws_pixel_type_size(op->type)) {
828 10172 case 2: mask[i] = av_bswap16(mask[i]); break;
829 480 case 4: mask[i] = av_bswap32(mask[i]); break;
830 }
831 }
832 2663 break;
833
834 210 case SWS_OP_CLEAR:
835
2/2
✓ Branch 0 taken 758 times.
✓ Branch 1 taken 54 times.
812 for (int i = 0; i < 4; i++) {
836
2/2
✓ Branch 0 taken 548 times.
✓ Branch 1 taken 210 times.
758 if (!SWS_COMP_TEST(op->clear.mask, i))
837 548 continue;
838
3/4
✓ Branch 0 taken 54 times.
✓ Branch 1 taken 156 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 54 times.
210 if (op->clear.value[i].num != 0 || !clear_val)
839 156 return AVERROR(ENOTSUP);
840 54 mask[i] = 0x1010101ul * clear_val;
841 }
842 54 break;
843
844 4840 case SWS_OP_CONVERT: {
845
2/2
✓ Branch 0 taken 4684 times.
✓ Branch 1 taken 156 times.
4840 if (!op->convert.expand)
846 4684 return AVERROR(ENOTSUP);
847
2/2
✓ Branch 0 taken 624 times.
✓ Branch 1 taken 156 times.
780 for (int i = 0; i < 4; i++) {
848
1/3
✓ Branch 0 taken 624 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
624 switch (ff_sws_pixel_type_size(op->type)) {
849 624 case 1: mask[i] = 0x01010101 * (mask[i] & 0xFF); break;
850 case 2: mask[i] = 0x00010001 * (mask[i] & 0xFFFF); break;
851 }
852 }
853 156 break;
854 }
855
856 528 case SWS_OP_WRITE: {
857
3/4
✓ Branch 0 taken 514 times.
✓ Branch 1 taken 14 times.
✓ Branch 2 taken 514 times.
✗ Branch 3 not taken.
528 if (op->rw.frac || op->rw.filter ||
858
4/4
✓ Branch 0 taken 262 times.
✓ Branch 1 taken 252 times.
✓ Branch 2 taken 211 times.
✓ Branch 3 taken 51 times.
514 (!op->rw.packed && op->rw.elems > 1))
859 225 return AVERROR(ENOTSUP);
860
861 /* Initialize to no-op */
862 303 memset(shuffle, clear_val, size);
863
864 303 const int write_size = ff_sws_pixel_type_size(op->type);
865 303 const int read_chunk = read->rw.elems * read_size;
866 303 const int write_chunk = op->rw.elems * write_size;
867 303 const int num_groups = size / FFMAX(read_chunk, write_chunk);
868
2/2
✓ Branch 0 taken 1166 times.
✓ Branch 1 taken 303 times.
1469 for (int n = 0; n < num_groups; n++) {
869 1166 const int base_in = n * read_chunk;
870 1166 const int base_out = n * write_chunk;
871
2/2
✓ Branch 0 taken 2898 times.
✓ Branch 1 taken 1166 times.
4064 for (int i = 0; i < op->rw.elems; i++) {
872 2898 const int offset = base_out + i * write_size;
873
2/2
✓ Branch 0 taken 4354 times.
✓ Branch 1 taken 2898 times.
7252 for (int b = 0; b < write_size; b++) {
874 4354 const uint8_t idx = mask[i] >> (b * 8);
875
2/2
✓ Branch 0 taken 4138 times.
✓ Branch 1 taken 216 times.
4354 if (idx != clear_val)
876 4138 shuffle[offset + b] = base_in + idx;
877 }
878 }
879 }
880
881 303 *read_bytes = num_groups * read_chunk;
882 303 *write_bytes = num_groups * write_chunk;
883 303 return num_groups;
884 }
885
886 2970 default:
887 2970 return AVERROR(ENOTSUP);
888 }
889 }
890
891 return AVERROR(EINVAL);
892 }
893
894 /**
895 * Determine a suitable intermediate buffer format for a given combination
896 * of pixel types and number of planes. The exact interpretation of these
897 * formats does not matter at all; since they will only ever be used as
898 * temporary intermediate buffers. We still need to pick *some* format as
899 * a consequence of ff_sws_graph_add_pass() taking an AVPixelFormat for the
900 * output buffer.
901 */
902 57024 static enum AVPixelFormat get_planar_fmt(SwsPixelType type, int nb_planes)
903 {
904
3/4
✓ Branch 0 taken 14256 times.
✓ Branch 1 taken 21780 times.
✓ Branch 2 taken 20988 times.
✗ Branch 3 not taken.
57024 switch (ff_sws_pixel_type_size(type)) {
905 14256 case 1:
906
4/5
✓ Branch 0 taken 1299 times.
✓ Branch 1 taken 189 times.
✓ Branch 2 taken 11805 times.
✓ Branch 3 taken 963 times.
✗ Branch 4 not taken.
14256 switch (nb_planes) {
907 1299 case 1: return AV_PIX_FMT_GRAY8;
908 189 case 2: return AV_PIX_FMT_YUV444P; // FIXME: no 2-plane planar fmt
909 11805 case 3: return AV_PIX_FMT_YUV444P;
910 963 case 4: return AV_PIX_FMT_YUVA444P;
911 }
912 break;
913 21780 case 2:
914
4/5
✓ Branch 0 taken 3774 times.
✓ Branch 1 taken 378 times.
✓ Branch 2 taken 15702 times.
✓ Branch 3 taken 1926 times.
✗ Branch 4 not taken.
21780 switch (nb_planes) {
915 3774 case 1: return AV_PIX_FMT_GRAY16;
916 378 case 2: return AV_PIX_FMT_YUV444P16; // FIXME: no 2-plane planar fmt
917 15702 case 3: return AV_PIX_FMT_YUV444P16;
918 1926 case 4: return AV_PIX_FMT_YUVA444P16;
919 }
920 break;
921 20988 case 4:
922
4/5
✓ Branch 0 taken 4030 times.
✓ Branch 1 taken 598 times.
✓ Branch 2 taken 14318 times.
✓ Branch 3 taken 2042 times.
✗ Branch 4 not taken.
20988 switch (nb_planes) {
923 4030 case 1: return AV_PIX_FMT_GRAYF32;
924 598 case 2: return AV_PIX_FMT_GBRPF32; // FIXME: no 2-plane planar fmt
925 14318 case 3: return AV_PIX_FMT_GBRPF32;
926 2042 case 4: return AV_PIX_FMT_GBRAPF32;
927 }
928 break;
929 }
930
931 av_unreachable("Invalid pixel type or number of planes?");
932 return AV_PIX_FMT_NONE;
933 }
934
935 57024 static void get_input_size(const SwsOpList *ops, SwsFormat *fmt)
936 {
937 57024 fmt->width = ops->src.width;
938 57024 fmt->height = ops->src.height;
939
940 57024 const SwsOp *read = ff_sws_op_list_input(ops);
941
2/4
✓ Branch 0 taken 57024 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 57024 times.
57024 if (read && read->rw.filter == SWS_OP_FILTER_V) {
942 fmt->height = read->rw.kernel->dst_size;
943
3/4
✓ Branch 0 taken 57024 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 17424 times.
✓ Branch 3 taken 39600 times.
57024 } else if (read && read->rw.filter == SWS_OP_FILTER_H) {
944 17424 fmt->width = read->rw.kernel->dst_size;
945 }
946 57024 }
947
948 100848 int ff_sws_op_list_subpass(SwsOpList *ops1, SwsOpList **out_rest)
949 {
950 const SwsOp *op;
951 int ret, idx;
952
953
2/2
✓ Branch 0 taken 447297 times.
✓ Branch 1 taken 43824 times.
491121 for (idx = 0; idx < ops1->num_ops; idx++) {
954 447297 op = &ops1->ops[idx];
955
4/4
✓ Branch 0 taken 420897 times.
✓ Branch 1 taken 26400 times.
✓ Branch 2 taken 390273 times.
✓ Branch 3 taken 30624 times.
447297 if (op->op == SWS_OP_FILTER_H || op->op == SWS_OP_FILTER_V)
956 break;
957 }
958
959
2/2
✓ Branch 0 taken 43824 times.
✓ Branch 1 taken 57024 times.
100848 if (idx == ops1->num_ops) {
960 43824 *out_rest = NULL;
961 43824 return 0;
962 }
963
964
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 57024 times.
57024 av_assert0(idx > 0);
965 57024 const SwsOp *prev = &ops1->ops[idx - 1];
966
967 57024 SwsOpList *ops2 = ff_sws_op_list_duplicate(ops1);
968
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 57024 times.
57024 if (!ops2)
969 return AVERROR(ENOMEM);
970
971 /**
972 * Not all components may be needed; but we need the ones that *are*
973 * used to be contiguous for the write/read operations. So, first
974 * compress them into a linearly ascending list of components
975 */
976 57024 int nb_planes = 0;
977 57024 SwsSwizzleOp swiz_wr = SWS_SWIZZLE(0, 1, 2, 3);
978 57024 SwsSwizzleOp swiz_rd = SWS_SWIZZLE(0, 1, 2, 3);
979
2/2
✓ Branch 0 taken 228096 times.
✓ Branch 1 taken 57024 times.
285120 for (int i = 0; i < 4; i++) {
980
2/2
✓ Branch 0 taken 156632 times.
✓ Branch 1 taken 71464 times.
228096 if (SWS_OP_NEEDED(prev, i)) {
981 156632 const int o = nb_planes++;
982 156632 swiz_wr.in[o] = i;
983 156632 swiz_rd.in[i] = o;
984 }
985 }
986
987 /* Determine metadata for the intermediate format */
988 57024 const SwsPixelType type = op->type;
989 57024 ops2->src.format = get_planar_fmt(type, nb_planes);
990 57024 ops2->src.desc = av_pix_fmt_desc_get(ops2->src.format);
991 57024 get_input_size(ops1, &ops2->src);
992 57024 ops1->dst = ops2->src;
993
994
2/2
✓ Branch 0 taken 156632 times.
✓ Branch 1 taken 57024 times.
213656 for (int i = 0; i < nb_planes; i++) {
995 156632 ops1->plane_dst[i] = ops2->plane_src[i] = i;
996 156632 ops2->comps_src.flags[i] = prev->comps.flags[swiz_wr.in[i]];
997 }
998
999 57024 ff_sws_op_list_remove_at(ops1, idx, ops1->num_ops - idx);
1000 57024 ff_sws_op_list_remove_at(ops2, 0, idx);
1001 57024 op = NULL; /* the above command may invalidate op */
1002
1003
2/2
✓ Branch 0 taken 5028 times.
✓ Branch 1 taken 51996 times.
57024 if (swiz_wr.mask != SWS_SWIZZLE(0, 1, 2, 3).mask) {
1004 5028 ret = ff_sws_op_list_append(ops1, &(SwsOp) {
1005 .op = SWS_OP_SWIZZLE,
1006 .type = type,
1007 .swizzle = swiz_wr,
1008 });
1009
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5028 times.
5028 if (ret < 0)
1010 goto fail;
1011 }
1012
1013 57024 ret = ff_sws_op_list_append(ops1, &(SwsOp) {
1014 .op = SWS_OP_WRITE,
1015 .type = type,
1016 .rw.elems = nb_planes,
1017 });
1018
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 57024 times.
57024 if (ret < 0)
1019 goto fail;
1020
1021 57024 ret = ff_sws_op_list_insert_at(ops2, 0, &(SwsOp) {
1022 .op = SWS_OP_READ,
1023 .type = type,
1024 .rw.elems = nb_planes,
1025 });
1026
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 57024 times.
57024 if (ret < 0)
1027 goto fail;
1028
1029
2/2
✓ Branch 0 taken 5028 times.
✓ Branch 1 taken 51996 times.
57024 if (swiz_rd.mask != SWS_SWIZZLE(0, 1, 2, 3).mask) {
1030 5028 ret = ff_sws_op_list_insert_at(ops2, 1, &(SwsOp) {
1031 .op = SWS_OP_SWIZZLE,
1032 .type = type,
1033 .swizzle = swiz_rd,
1034 });
1035
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5028 times.
5028 if (ret < 0)
1036 goto fail;
1037 }
1038
1039 57024 ret = ff_sws_op_list_optimize(ops1);
1040
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 57024 times.
57024 if (ret < 0)
1041 goto fail;
1042
1043 57024 ret = ff_sws_op_list_optimize(ops2);
1044
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 57024 times.
57024 if (ret < 0)
1045 goto fail;
1046
1047 57024 *out_rest = ops2;
1048 57024 return 0;
1049
1050 fail:
1051 ff_sws_op_list_free(&ops2);
1052 return ret;
1053 }
1054