FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libswscale/ops_chain.c
Date: 2026-04-24 15:23:18
Exec Total Coverage
Lines: 152 172 88.4%
Functions: 9 9 100.0%
Branches: 128 157 81.5%

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/avassert.h"
22 #include "libavutil/mem.h"
23 #include "libavutil/rational.h"
24
25 #include "ops_chain.h"
26
27 #define Q(N) ((AVRational) { N, 1 })
28
29 33538 SwsOpChain *ff_sws_op_chain_alloc(void)
30 {
31 33538 return av_mallocz(sizeof(SwsOpChain));
32 }
33
34 33538 void ff_sws_op_chain_free_cb(void *ptr)
35 {
36
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 33538 times.
33538 if (!ptr)
37 return;
38
39 33538 SwsOpChain *chain = ptr;
40
2/2
✓ Branch 0 taken 190379 times.
✓ Branch 1 taken 33538 times.
223917 for (int i = 0; i < chain->num_impl + 1; i++) {
41
2/2
✓ Branch 0 taken 22983 times.
✓ Branch 1 taken 167396 times.
190379 if (chain->free[i])
42 22983 chain->free[i](&chain->impl[i].priv);
43 }
44
45 33538 av_free(chain);
46 }
47
48 156841 int ff_sws_op_chain_append(SwsOpChain *chain, SwsFuncPtr func,
49 void (*free)(SwsOpPriv *), const SwsOpPriv *priv)
50 {
51 156841 const int idx = chain->num_impl;
52
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 156841 times.
156841 if (idx == SWS_MAX_OPS)
53 return AVERROR(EINVAL);
54
55 av_assert1(func);
56 156841 chain->impl[idx].cont = func;
57 156841 chain->impl[idx + 1].priv = *priv;
58 156841 chain->free[idx + 1] = free;
59 156841 chain->num_impl++;
60 156841 return 0;
61 }
62
63 /**
64 * Match an operation against a reference operation. Returns a score for how
65 * well the reference matches the operation, or 0 if there is no match.
66 *
67 * For unfiltered SWS_OP_READ/SWS_OP_WRITE, SWS_OP_SWAP_BYTES and
68 * SWS_OP_SWIZZLE, the exact type is not checked, just the size.
69 *
70 * Components marked SWS_COMP_GARBAGE are ignored when matching. If `flexible`
71 * is true, the op body is ignored - only the operation, pixel type, and
72 * component masks are checked.
73 */
74 38042299 static int op_match(const SwsOp *op, const SwsOpEntry *entry)
75 {
76 38042299 int score = 10;
77
2/2
✓ Branch 0 taken 33876101 times.
✓ Branch 1 taken 4166198 times.
38042299 if (op->op != entry->op)
78 33876101 return 0;
79
80
3/3
✓ Branch 0 taken 1779198 times.
✓ Branch 1 taken 555690 times.
✓ Branch 2 taken 1831310 times.
4166198 switch (op->op) {
81 1779198 case SWS_OP_READ:
82 case SWS_OP_WRITE:
83
4/4
✓ Branch 0 taken 319872 times.
✓ Branch 1 taken 1459326 times.
✓ Branch 2 taken 239904 times.
✓ Branch 3 taken 79968 times.
1779198 if (op->rw.filter && op->type != entry->type)
84 239904 return 0;
85 /* fall through */;
86 case SWS_OP_SWAP_BYTES:
87 case SWS_OP_SWIZZLE:
88 /* Only the size matters for these operations */
89
2/2
✓ Branch 0 taken 949620 times.
✓ Branch 1 taken 1145364 times.
2094984 if (ff_sws_pixel_type_size(op->type) != ff_sws_pixel_type_size(entry->type))
90 949620 return 0;
91 1145364 break;
92 1831310 default:
93
2/2
✓ Branch 0 taken 1079990 times.
✓ Branch 1 taken 751320 times.
1831310 if (op->type != entry->type)
94 1079990 return 0;
95 751320 break;
96 }
97
98 1896684 const SwsCompMask needed = ff_sws_comp_mask_needed(op);
99
2/2
✓ Branch 0 taken 438758 times.
✓ Branch 1 taken 1457926 times.
1896684 if (needed & ~entry->mask)
100 438758 return 0; /* Entry doesn't compute all needed components */
101
102 /* Otherwise, operating on fewer components is better */
103 1457926 score += av_popcount(SWS_COMP_INV(entry->mask));
104
105 /* Flexible variants always match, but lower the score to prioritize more
106 * specific implementations if they exist */
107
2/2
✓ Branch 0 taken 44554 times.
✓ Branch 1 taken 1413372 times.
1457926 if (entry->flexible)
108 44554 return score - 5;
109
110
9/15
✗ Branch 0 not taken.
✓ Branch 1 taken 539355 times.
✓ Branch 2 taken 27373 times.
✓ Branch 3 taken 15472 times.
✓ Branch 4 taken 116257 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 325050 times.
✓ Branch 7 taken 159142 times.
✓ Branch 8 taken 81746 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 148912 times.
✓ Branch 11 taken 65 times.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
1413372 switch (op->op) {
111 case SWS_OP_INVALID:
112 return 0;
113 539355 case SWS_OP_READ:
114 case SWS_OP_WRITE:
115
2/2
✓ Branch 0 taken 198433 times.
✓ Branch 1 taken 340922 times.
539355 if (op->rw.elems != entry->rw.elems ||
116
2/2
✓ Branch 0 taken 175106 times.
✓ Branch 1 taken 23327 times.
198433 op->rw.frac != entry->rw.frac ||
117
2/2
✓ Branch 0 taken 120892 times.
✓ Branch 1 taken 54214 times.
175106 op->rw.filter != entry->rw.filter ||
118
4/4
✓ Branch 0 taken 102524 times.
✓ Branch 1 taken 18368 times.
✓ Branch 2 taken 47266 times.
✓ Branch 3 taken 55258 times.
120892 (op->rw.elems > 1 && op->rw.packed != entry->rw.packed))
119 465729 return 0;
120 73626 return score;
121 27373 case SWS_OP_SWAP_BYTES:
122 27373 return score;
123 15472 case SWS_OP_PACK:
124 case SWS_OP_UNPACK:
125
4/4
✓ Branch 0 taken 34372 times.
✓ Branch 1 taken 1424 times.
✓ Branch 2 taken 30164 times.
✓ Branch 3 taken 4208 times.
35796 for (int i = 0; i < 4 && op->pack.pattern[i]; i++) {
126
2/2
✓ Branch 0 taken 9840 times.
✓ Branch 1 taken 20324 times.
30164 if (op->pack.pattern[i] != entry->pack.pattern[i])
127 9840 return 0;
128 }
129 5632 return score;
130 116257 case SWS_OP_CLEAR:
131 /* Clear mask must match exactly */
132
2/2
✓ Branch 0 taken 98384 times.
✓ Branch 1 taken 17873 times.
116257 if (op->clear.mask != entry->clear.mask)
133 98384 return 0;
134
2/2
✓ Branch 0 taken 68555 times.
✓ Branch 1 taken 10715 times.
79270 for (int i = 0; i < 4; i++) {
135
3/4
✓ Branch 0 taken 20242 times.
✓ Branch 1 taken 48313 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 20242 times.
68555 if (!SWS_COMP_TEST(op->clear.mask, i) || !SWS_OP_NEEDED(op, i))
136 48313 continue;
137
2/2
✓ Branch 0 taken 9572 times.
✓ Branch 1 taken 10670 times.
20242 else if (!entry->clear.value[i].den)
138 9572 continue; /* Any clear value supported */
139
2/2
✓ Branch 1 taken 7158 times.
✓ Branch 2 taken 3512 times.
10670 else if (av_cmp_q(op->clear.value[i], entry->clear.value[i]))
140 7158 return 0;
141 }
142 10715 return score;
143 case SWS_OP_LSHIFT:
144 case SWS_OP_RSHIFT:
145 av_assert1(entry->flexible);
146 break;
147 325050 case SWS_OP_SWIZZLE:
148
2/2
✓ Branch 0 taken 477941 times.
✓ Branch 1 taken 19619 times.
497560 for (int i = 0; i < 4; i++) {
149
4/4
✓ Branch 0 taken 429540 times.
✓ Branch 1 taken 48401 times.
✓ Branch 2 taken 305431 times.
✓ Branch 3 taken 124109 times.
477941 if (SWS_OP_NEEDED(op, i) && op->swizzle.in[i] != entry->swizzle.in[i])
150 305431 return 0;
151 }
152 19619 return score;
153 159142 case SWS_OP_CONVERT:
154
2/2
✓ Branch 0 taken 58442 times.
✓ Branch 1 taken 100700 times.
159142 if (op->convert.to != entry->convert.to ||
155
2/2
✓ Branch 0 taken 1102 times.
✓ Branch 1 taken 57340 times.
58442 op->convert.expand != entry->convert.expand)
156 101802 return 0;
157 57340 return score;
158 81746 case SWS_OP_DITHER:
159
2/2
✓ Branch 0 taken 8072 times.
✓ Branch 1 taken 73674 times.
81746 return op->dither.size_log2 == entry->dither_size ? score : 0;
160 case SWS_OP_MIN:
161 case SWS_OP_MAX:
162 av_assert1(entry->flexible);
163 break;
164 148912 case SWS_OP_LINEAR:
165
2/2
✓ Branch 0 taken 139605 times.
✓ Branch 1 taken 9307 times.
148912 if (op->lin.mask != entry->linear_mask)
166 139605 return 0;
167 9307 return score;
168 65 case SWS_OP_SCALE:
169
2/2
✓ Branch 1 taken 31 times.
✓ Branch 2 taken 34 times.
65 return av_cmp_q(op->scale.factor, entry->scale) ? 0 : score;
170 case SWS_OP_FILTER_H:
171 case SWS_OP_FILTER_V:
172 return score;
173 case SWS_OP_TYPE_NB:
174 break;
175 }
176
177 av_unreachable("Invalid operation type!");
178 return 0;
179 }
180
181 162355 int ff_sws_op_compile_tables(SwsContext *ctx, const SwsOpTable *const tables[],
182 int num_tables, SwsOpList *ops, int ops_index,
183 const int block_size, SwsOpChain *chain)
184 {
185 162355 const SwsOp *op = &ops->ops[ops_index];
186 162355 const unsigned cpu_flags = av_get_cpu_flags();
187 162355 const SwsOpEntry *best = NULL;
188 162355 const SwsOpTable *best_table = NULL;
189 162355 int ret, best_score = 0;
190
191 162355 SwsImplParams params = {
192 .ctx = ctx,
193 .op = op
194 };
195
196
2/2
✓ Branch 0 taken 1012450 times.
✓ Branch 1 taken 162355 times.
1174805 for (int n = 0; n < num_tables; n++) {
197 1012450 const SwsOpTable *table = tables[n];
198
3/4
✓ Branch 0 taken 1012450 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 498446 times.
✓ Branch 3 taken 514004 times.
1012450 if (table->block_size && table->block_size != block_size ||
199
2/2
✓ Branch 0 taken 10692 times.
✓ Branch 1 taken 487754 times.
498446 table->cpu_flags & ~cpu_flags)
200 524696 continue;
201
202 487754 params.table = table;
203
2/2
✓ Branch 0 taken 38042299 times.
✓ Branch 1 taken 487754 times.
38530053 for (int i = 0; table->entries[i]; i++) {
204 38042299 const SwsOpEntry *entry = table->entries[i];
205 38042299 int score = op_match(op, entry);
206
2/2
✓ Branch 0 taken 37885238 times.
✓ Branch 1 taken 157061 times.
38042299 if (score <= best_score)
207 37885238 continue;
208
4/4
✓ Branch 0 taken 288 times.
✓ Branch 1 taken 156773 times.
✓ Branch 3 taken 220 times.
✓ Branch 4 taken 68 times.
157061 if (entry->check && !entry->check(&params))
209 220 continue;
210 156841 best_score = score;
211 156841 best_table = table;
212 156841 best = entry;
213 }
214 }
215
216
2/2
✓ Branch 0 taken 5514 times.
✓ Branch 1 taken 156841 times.
162355 if (!best)
217 5514 return AVERROR(ENOTSUP);
218
219 156841 params.table = best_table;
220
221 156841 SwsImplResult res = {0};
222
2/2
✓ Branch 0 taken 88624 times.
✓ Branch 1 taken 68217 times.
156841 if (best->setup) {
223 88624 ret = best->setup(&params, &res);
224
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 88624 times.
88624 if (ret < 0)
225 return ret;
226 }
227
228
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 156841 times.
156841 ret = ff_sws_op_chain_append(chain, res.func ? res.func : best->func,
229 res.free, &res.priv);
230
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 156841 times.
156841 if (ret < 0) {
231 if (res.free)
232 res.free(&res.priv);
233 return ret;
234 }
235
236 156841 chain->cpu_flags |= best_table->cpu_flags;
237 156841 chain->over_read = FFMAX(chain->over_read, res.over_read);
238 156841 chain->over_write = FFMAX(chain->over_write, res.over_write);
239 156841 return 0;
240 }
241
242 #define q2pixel(type, q) ((q).den ? (type) (q).num / (q).den : 0)
243
244 1869 int ff_sws_setup_shift(const SwsImplParams *params, SwsImplResult *out)
245 {
246 1869 out->priv.u8[0] = params->op->shift.amount;
247 1869 return 0;
248 }
249
250 4493 int ff_sws_setup_scale(const SwsImplParams *params, SwsImplResult *out)
251 {
252 4493 const SwsOp *op = params->op;
253 4493 const AVRational factor = op->scale.factor;
254
4/5
✓ Branch 0 taken 160 times.
✓ Branch 1 taken 242 times.
✓ Branch 2 taken 122 times.
✓ Branch 3 taken 3969 times.
✗ Branch 4 not taken.
4493 switch (op->type) {
255
1/2
✓ Branch 0 taken 160 times.
✗ Branch 1 not taken.
160 case SWS_PIXEL_U8: out->priv.u8[0] = q2pixel(uint8_t, factor); break;
256
1/2
✓ Branch 0 taken 242 times.
✗ Branch 1 not taken.
242 case SWS_PIXEL_U16: out->priv.u16[0] = q2pixel(uint16_t, factor); break;
257
1/2
✓ Branch 0 taken 122 times.
✗ Branch 1 not taken.
122 case SWS_PIXEL_U32: out->priv.u32[0] = q2pixel(uint32_t, factor); break;
258
1/2
✓ Branch 0 taken 3969 times.
✗ Branch 1 not taken.
3969 case SWS_PIXEL_F32: out->priv.f32[0] = q2pixel(float, factor); break;
259 default: return AVERROR(EINVAL);
260 }
261
262 4493 return 0;
263 }
264
265 11714 int ff_sws_setup_clamp(const SwsImplParams *params, SwsImplResult *out)
266 {
267 11714 const SwsOp *op = params->op;
268
2/2
✓ Branch 0 taken 46856 times.
✓ Branch 1 taken 11714 times.
58570 for (int i = 0; i < 4; i++) {
269 46856 const AVRational limit = op->clamp.limit[i];
270
4/5
✓ Branch 0 taken 448 times.
✓ Branch 1 taken 448 times.
✓ Branch 2 taken 448 times.
✓ Branch 3 taken 45512 times.
✗ Branch 4 not taken.
46856 switch (op->type) {
271
1/2
✓ Branch 0 taken 448 times.
✗ Branch 1 not taken.
448 case SWS_PIXEL_U8: out->priv.u8[i] = q2pixel(uint8_t, limit); break;
272
1/2
✓ Branch 0 taken 448 times.
✗ Branch 1 not taken.
448 case SWS_PIXEL_U16: out->priv.u16[i] = q2pixel(uint16_t, limit); break;
273
1/2
✓ Branch 0 taken 448 times.
✗ Branch 1 not taken.
448 case SWS_PIXEL_U32: out->priv.u32[i] = q2pixel(uint32_t, limit); break;
274
2/2
✓ Branch 0 taken 37333 times.
✓ Branch 1 taken 8179 times.
45512 case SWS_PIXEL_F32: out->priv.f32[i] = q2pixel(float, limit); break;
275 default: return AVERROR(EINVAL);
276 }
277 }
278
279 11714 return 0;
280 }
281
282 5329 int ff_sws_setup_clear(const SwsImplParams *params, SwsImplResult *out)
283 {
284 5329 const SwsOp *op = params->op;
285
2/2
✓ Branch 0 taken 21316 times.
✓ Branch 1 taken 5329 times.
26645 for (int i = 0; i < 4; i++) {
286 21316 const AVRational value = op->clear.value[i];
287
2/2
✓ Branch 0 taken 14344 times.
✓ Branch 1 taken 6972 times.
21316 if (!value.den)
288 14344 continue;
289
4/5
✓ Branch 0 taken 2280 times.
✓ Branch 1 taken 4099 times.
✓ Branch 2 taken 404 times.
✓ Branch 3 taken 189 times.
✗ Branch 4 not taken.
6972 switch (op->type) {
290
1/2
✓ Branch 0 taken 2280 times.
✗ Branch 1 not taken.
2280 case SWS_PIXEL_U8: out->priv.u8[i] = q2pixel(uint8_t, value); break;
291
1/2
✓ Branch 0 taken 4099 times.
✗ Branch 1 not taken.
4099 case SWS_PIXEL_U16: out->priv.u16[i] = q2pixel(uint16_t, value); break;
292
1/2
✓ Branch 0 taken 404 times.
✗ Branch 1 not taken.
404 case SWS_PIXEL_U32: out->priv.u32[i] = q2pixel(uint32_t, value); break;
293
1/2
✓ Branch 0 taken 189 times.
✗ Branch 1 not taken.
189 case SWS_PIXEL_F32: out->priv.f32[i] = q2pixel(float, value); break;
294 default: return AVERROR(EINVAL);
295 }
296 }
297
298 5329 return 0;
299 }
300