FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libswscale/uops.c
Date: 2026-08-26 10:34:04
Exec Total Coverage
Lines: 407 430 94.7%
Functions: 22 22 100.0%
Branches: 295 327 90.2%

Line Branch Exec Source
1 /**
2 * Copyright (C) 2026 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 <stdbool.h>
22
23 #include "libavutil/avassert.h"
24 #include "libavutil/mem.h"
25 #include "libavutil/refstruct.h"
26
27 #include "ops.h"
28 #include "uops.h"
29 #include "uops_list.h"
30
31 152168309 int ff_sws_uop_cmp(const SwsUOp *a, const SwsUOp *b)
32 {
33
2/2
✓ Branch 0 taken 81967283 times.
✓ Branch 1 taken 70201026 times.
152168309 if (a->type != b->type)
34 81967283 return (int) a->type - b->type;
35
2/2
✓ Branch 0 taken 50131349 times.
✓ Branch 1 taken 20069677 times.
70201026 if (a->uop != b->uop)
36 50131349 return (int) a->uop - b->uop;
37
2/2
✓ Branch 0 taken 8730468 times.
✓ Branch 1 taken 11339209 times.
20069677 if (a->mask != b->mask)
38 8730468 return (int) a->mask - b->mask;
39 11339209 return memcmp(&a->par, &b->par, sizeof(a->par));
40 }
41
42 static const struct {
43 char abbr[32];
44 } uop_names[SWS_UOP_TYPE_NB] = {
45 #define UOP_NAME(OP, ABBR) [OP] = { ABBR },
46 UOPS_LIST(UOP_NAME)
47 #undef UOP_NAME
48 };
49
50 147787449 static SwsPixel pixel_from_q64(SwsPixelType type, AVRational64 val)
51 {
52 av_assert1(val.den != 0);
53
4/6
✓ Branch 0 taken 83367 times.
✓ Branch 1 taken 160586 times.
✓ Branch 2 taken 36464 times.
✓ Branch 3 taken 147507032 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
147787449 switch (type) {
54 147787449 case SWS_PIXEL_U8: return (SwsPixel) { .u8 = val.num / val.den };
55 160586 case SWS_PIXEL_U16: return (SwsPixel) { .u16 = val.num / val.den };
56 36464 case SWS_PIXEL_U32: return (SwsPixel) { .u32 = val.num / val.den };
57 147507032 case SWS_PIXEL_F32: return (SwsPixel) { .f32 = (float) val.num / val.den };
58 case SWS_PIXEL_NONE:
59 case SWS_PIXEL_TYPE_NB: break;
60 }
61
62 av_unreachable("Invalid pixel type!");
63 return (SwsPixel) {0};
64 }
65
66 #define Q2PIXEL(val) pixel_from_q64(op->type, val)
67
68 306265 static bool pixel_is_1s(SwsPixelType type, SwsPixel val)
69 {
70
3/4
✓ Branch 0 taken 46779 times.
✓ Branch 1 taken 130126 times.
✓ Branch 2 taken 129360 times.
✗ Branch 3 not taken.
306265 switch (ff_sws_pixel_type_size(type)) {
71 46779 case 1: return val.u8 == UINT8_MAX;
72 130126 case 2: return val.u16 == UINT16_MAX;
73 129360 case 4: return val.u32 == UINT32_MAX;
74 default: break;
75 }
76
77 av_unreachable("Invalid pixel type!");
78 return false;
79 }
80
81 1127637 void ff_sws_uop_name(const SwsUOp *op, char buf[SWS_UOP_NAME_MAX])
82 {
83 AVBPrint bp;
84 1127637 av_bprint_init_for_buffer(&bp, buf, SWS_UOP_NAME_MAX);
85
86
1/2
✓ Branch 0 taken 1127637 times.
✗ Branch 1 not taken.
1127637 if (op->type != SWS_PIXEL_NONE)
87 1127637 av_bprintf(&bp, "%s_", ff_sws_pixel_type_name(op->type));
88 1127637 av_bprintf(&bp, "%s", uop_names[op->uop].abbr);
89
90
1/2
✓ Branch 0 taken 1127637 times.
✗ Branch 1 not taken.
1127637 if (op->mask)
91 1127637 av_bprintf(&bp, "_%s", ff_sws_comp_mask_str(op->mask));
92
93 1127637 const SwsUOpParams *par = &op->par;
94
10/10
✓ Branch 0 taken 114278 times.
✓ Branch 1 taken 2609 times.
✓ Branch 2 taken 18014 times.
✓ Branch 3 taken 64587 times.
✓ Branch 4 taken 45109 times.
✓ Branch 5 taken 41264 times.
✓ Branch 6 taken 73360 times.
✓ Branch 7 taken 63659 times.
✓ Branch 8 taken 92 times.
✓ Branch 9 taken 704665 times.
1127637 switch (op->uop) {
95 114278 case SWS_UOP_READ_PLANAR_FH:
96 case SWS_UOP_READ_PLANAR_FV:
97 case SWS_UOP_READ_PLANAR_FV_FMA:
98 114278 av_bprintf(&bp, "_%s", ff_sws_pixel_type_name(par->filter.type));
99 114278 break;
100 2609 case SWS_UOP_RW_SHUFFLE:
101 2609 av_bprintf(&bp, "_%x_%u_%u", par->shuffle.clear_value,
102 2609 par->shuffle.read_size, par->shuffle.write_size);
103 2609 break;
104 18014 case SWS_UOP_LSHIFT:
105 case SWS_UOP_RSHIFT:
106 18014 av_bprintf(&bp, "_%u", par->shift.amount);
107 18014 break;
108 64587 case SWS_UOP_PERMUTE:
109 case SWS_UOP_COPY:
110 64587 av_bprint_chars(&bp, '_', 1);
111
2/2
✓ Branch 0 taken 172939 times.
✓ Branch 1 taken 64587 times.
237526 for (int i = 0; i < par->move.num_moves; i++)
112 172939 av_bprint_chars(&bp, "txyzw"[par->move.dst[i] + 1], 1);
113 64587 av_bprint_chars(&bp, '_', 1);
114
2/2
✓ Branch 0 taken 172939 times.
✓ Branch 1 taken 64587 times.
237526 for (int i = 0; i < par->move.num_moves; i++)
115 172939 av_bprint_chars(&bp, "txyzw"[par->move.src[i] + 1], 1);
116 64587 break;
117 45109 case SWS_UOP_PACK:
118 case SWS_UOP_UNPACK:
119 45109 av_bprint_chars(&bp, '_', 1);
120
4/4
✓ Branch 0 taken 180436 times.
✓ Branch 1 taken 14522 times.
✓ Branch 2 taken 149849 times.
✓ Branch 3 taken 30587 times.
194958 for (int i = 0; i < 4 && par->pack.pattern[i]; i++)
121 149849 av_bprintf(&bp, "%x", par->pack.pattern[i]);
122 45109 break;
123 41264 case SWS_UOP_CLEAR:
124 41264 av_bprint_chars(&bp, '_', 1);
125
2/2
✓ Branch 0 taken 165056 times.
✓ Branch 1 taken 41264 times.
206320 for (int i = 0; i < 4; i++) {
126
2/2
✓ Branch 0 taken 115080 times.
✓ Branch 1 taken 49976 times.
165056 if (!SWS_COMP_TEST(op->mask, i))
127 115080 continue;
128
2/2
✓ Branch 0 taken 11754 times.
✓ Branch 1 taken 38222 times.
49976 else if (SWS_COMP_TEST(par->clear.one, i))
129 11754 av_bprint_chars(&bp, '1', 1);
130
2/2
✓ Branch 0 taken 7723 times.
✓ Branch 1 taken 30499 times.
38222 else if (SWS_COMP_TEST(par->clear.zero, i))
131 7723 av_bprint_chars(&bp, '0', 1);
132 else
133 30499 av_bprint_chars(&bp, 'x', 1);
134 }
135 41264 break;
136 73360 case SWS_UOP_LINEAR:
137 case SWS_UOP_LINEAR_FMA:
138
2/2
✓ Branch 0 taken 293440 times.
✓ Branch 1 taken 73360 times.
366800 for (int i = 0; i < 4; i++) {
139
2/2
✓ Branch 0 taken 107402 times.
✓ Branch 1 taken 186038 times.
293440 if (!SWS_COMP_TEST(op->mask, i))
140 107402 continue;
141 186038 av_bprint_chars(&bp, '_', 1);
142
2/2
✓ Branch 0 taken 930190 times.
✓ Branch 1 taken 186038 times.
1116228 for (int j = 0; j < 5; j++) {
143
2/2
✓ Branch 0 taken 303 times.
✓ Branch 1 taken 929887 times.
930190 if (par->lin.one & SWS_MASK(i, j))
144 303 av_bprint_chars(&bp, '1', 1);
145
2/2
✓ Branch 0 taken 385060 times.
✓ Branch 1 taken 544827 times.
929887 else if (par->lin.zero & SWS_MASK(i, j))
146 385060 av_bprint_chars(&bp, '0', 1);
147
2/2
✓ Branch 0 taken 109220 times.
✓ Branch 1 taken 435607 times.
544827 else if (par->lin.exact & SWS_MASK(i, j))
148 109220 av_bprint_chars(&bp, 'X', 1);
149 else
150 435607 av_bprint_chars(&bp, 'x', 1);
151 }
152 }
153 73360 break;
154 63659 case SWS_UOP_DITHER:
155
2/2
✓ Branch 0 taken 254636 times.
✓ Branch 1 taken 63659 times.
318295 for (int i = 0; i < 4; i++) {
156
2/2
✓ Branch 0 taken 171594 times.
✓ Branch 1 taken 83042 times.
254636 if (SWS_COMP_TEST(op->mask, i))
157 171594 av_bprintf(&bp, "_%d", par->dither.y_offset[i]);
158 }
159 63659 const unsigned size = 1u << par->dither.size_log2;
160 63659 av_bprintf(&bp, "_%ux%u", size, size);
161 63659 break;
162 92 case SWS_UOP_LUT_3D:
163
2/2
✓ Branch 0 taken 46 times.
✓ Branch 1 taken 46 times.
92 av_bprintf(&bp, "_%s", par->lut3d.dynamic ? "dynamic" : "static");
164 92 break;
165 }
166
167
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1127637 times.
1127637 av_assert0(av_bprint_is_complete(&bp));
168 1127637 }
169
170 10793995 static void uop_uninit(SwsUOp *uop)
171 {
172
4/4
✓ Branch 0 taken 518245 times.
✓ Branch 1 taken 1101427 times.
✓ Branch 2 taken 280896 times.
✓ Branch 3 taken 8893427 times.
10793995 switch (uop->uop) {
173 518245 case SWS_UOP_DITHER:
174 518245 av_refstruct_unref(&uop->data.ptr);
175 518245 break;
176 1101427 case SWS_UOP_READ_PLANAR_FH:
177 case SWS_UOP_READ_PLANAR_FV:
178 case SWS_UOP_READ_PLANAR_FV_FMA:
179 1101427 av_refstruct_unref(&uop->data.kernel);
180 1101427 break;
181 280896 case SWS_UOP_LUT_3D:
182 280896 av_refstruct_unref(&uop->data.lut3d);
183 280896 break;
184 }
185
186 10793995 *uop = (SwsUOp) {0};
187 10793995 }
188
189 2292578 void ff_sws_uop_list_free(SwsUOpList **p_ops)
190 {
191 2292578 SwsUOpList *ops = *p_ops;
192
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2292578 times.
2292578 if (!ops)
193 return;
194
195
2/2
✓ Branch 0 taken 10707492 times.
✓ Branch 1 taken 2292578 times.
13000070 for (int i = 0; i < ops->num_ops; i++)
196 10707492 uop_uninit(&ops->ops[i]);
197
198 2292578 av_freep(&ops->ops);
199 2292578 av_free(ops);
200 2292578 *p_ops = NULL;
201 }
202
203 2292578 SwsUOpList *ff_sws_uop_list_alloc(void)
204 {
205 2292578 return av_mallocz(sizeof(SwsUOpList));
206 }
207
208 10793995 int ff_sws_uop_list_append(SwsUOpList *uops, SwsUOp *uop)
209 {
210
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 10793995 times.
10793995 if (!av_dynarray2_add((void **) &uops->ops, &uops->num_ops,
211 sizeof(*uop), (uint8_t *) uop))
212 {
213 uop_uninit(uop);
214 return AVERROR(ENOMEM);
215 }
216
217 10793995 *uop = (SwsUOp) {0};
218 10793995 return 0;
219 }
220
221 29831 void ff_sws_uop_list_remove_at(SwsUOpList *uops, int index, int count)
222 {
223 29831 const int end = uops->num_ops - count;
224 av_assert2(index >= 0 && count >= 0 && index + count <= uops->num_ops);
225
2/2
✓ Branch 0 taken 86503 times.
✓ Branch 1 taken 29831 times.
116334 for (int i = 0; i < count; i++)
226 86503 uop_uninit(&uops->ops[index + i]);
227
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 29831 times.
29831 for (int i = index; i < end; i++)
228 uops->ops[i] = uops->ops[i + count];
229 29831 uops->num_ops = end;
230 29831 }
231
232 522311 int ff_sws_dither_height(const SwsDitherUOp *dither)
233 {
234 522311 int max_offset = 0;
235
2/2
✓ Branch 0 taken 2089244 times.
✓ Branch 1 taken 522311 times.
2611555 for (int i = 0; i < 4; i++)
236 2089244 max_offset = FFMAX(max_offset, dither->y_offset[i]);
237 522311 return (1 << dither->size_log2) + max_offset;
238 }
239
240 4751336 static SwsPixelType pixel_type_to_int(const SwsPixelType type)
241 {
242
3/4
✓ Branch 0 taken 910581 times.
✓ Branch 1 taken 2640357 times.
✓ Branch 2 taken 1200398 times.
✗ Branch 3 not taken.
4751336 switch (ff_sws_pixel_type_size(type)) {
243 910581 case 1: return SWS_PIXEL_U8;
244 2640357 case 2: return SWS_PIXEL_U16;
245 1200398 case 4: return SWS_PIXEL_U32;
246 default: break;
247 }
248
249 av_unreachable("Invalid pixel type!");
250 return SWS_PIXEL_NONE;
251 }
252
253 1583216 static bool exact_product_f32(float a, float b)
254 {
255 1583216 volatile float prod = a * b;
256
2/2
✓ Branch 0 taken 801919 times.
✓ Branch 1 taken 781297 times.
1583216 volatile float result = b ? prod / b : 0.0f;
257
4/4
✓ Branch 0 taken 801919 times.
✓ Branch 1 taken 781297 times.
✓ Branch 2 taken 797121 times.
✓ Branch 3 taken 4798 times.
1583216 return !b || result == a;
258 }
259
260 868434 static bool exact_prod(SwsPixelType type, SwsPixel coef,
261 const SwsComps *comps, int idx)
262 {
263 868434 const AVRational64 minq = comps->min[idx];
264 868434 const AVRational64 maxq = comps->max[idx];
265
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 868434 times.
868434 if (ff_sws_pixel_type_is_int(type))
266 return true;
267
3/4
✓ Branch 0 taken 791844 times.
✓ Branch 1 taken 76590 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 791844 times.
868434 else if (!minq.den || !maxq.den)
268 76590 return false; /* unknown bounds */
269
270 791844 const SwsPixel min = pixel_from_q64(type, minq);
271 791844 const SwsPixel max = pixel_from_q64(type, maxq);
272
1/2
✓ Branch 0 taken 791844 times.
✗ Branch 1 not taken.
791844 switch (type) {
273 791844 case SWS_PIXEL_F32:
274
4/4
✓ Branch 1 taken 791372 times.
✓ Branch 2 taken 472 times.
✓ Branch 3 taken 787046 times.
✓ Branch 4 taken 4326 times.
1583216 return exact_product_f32(coef.f32, min.f32) &&
275 791372 exact_product_f32(coef.f32, max.f32);
276 }
277
278 av_unreachable("Invalid pixel type!");
279 return false;
280 }
281
282 477049 static bool check_filter_fma(SwsContext *ctx, SwsUOpFlags flags, const SwsOp *op)
283 {
284
2/2
✓ Branch 0 taken 247565 times.
✓ Branch 1 taken 229484 times.
477049 if (!(flags & SWS_UOP_FLAG_FMA))
285 247565 return false;
286
2/2
✓ Branch 0 taken 155101 times.
✓ Branch 1 taken 74383 times.
229484 if (!(ctx->flags & SWS_BITEXACT))
287 155101 return true;
288
2/2
✓ Branch 0 taken 41599 times.
✓ Branch 1 taken 32784 times.
74383 if (!ff_sws_pixel_type_is_int(op->type))
289 41599 return false;
290
291 32784 const int bits = ff_sws_pixel_type_size(op->type) * 8;
292 32784 const uint64_t max_val = UINT64_MAX >> (64 - bits);
293
294 /* Maximum value representable losslessly as float. Note that this is
295 * currently true only for U8, but that may change if we ever update the
296 * value of SWS_FILTER_SCALE. */
297 32784 return max_val * SWS_FILTER_SCALE <= (1 << 22);
298 }
299
300 4034860 static int translate_rw_op(SwsContext *ctx, SwsUOpList *ops, SwsUOpFlags flags,
301 const SwsOp *op)
302 {
303 8069720 SwsUOp uop = {
304 4034860 .type = op->type,
305
6/6
✓ Branch 0 taken 2973317 times.
✓ Branch 1 taken 1061543 times.
✓ Branch 2 taken 2832333 times.
✓ Branch 3 taken 1202527 times.
✓ Branch 4 taken 724422 times.
✓ Branch 5 taken 3310438 times.
4034860 .mask = SWS_COMP_MASK(op->rw.elems > 0, op->rw.elems > 1,
306 op->rw.elems > 2, op->rw.elems > 3),
307 };
308
309 /* Non-filtered reads don't care about the exact pixel contents */
310
2/2
✓ Branch 0 taken 2933433 times.
✓ Branch 1 taken 1101427 times.
4034860 if (!op->rw.filter.op)
311 2933433 uop.type = pixel_type_to_int(op->type);
312
313 4034860 const bool is_read = op->op == SWS_OP_READ;
314
2/2
✓ Branch 0 taken 1101427 times.
✓ Branch 1 taken 2933433 times.
4034860 if (op->rw.filter.op) {
315
3/6
✓ Branch 0 taken 1101427 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1101427 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 1101427 times.
1101427 if (op->op == SWS_OP_WRITE || op->rw.frac || op->rw.mode != SWS_RW_PLANAR)
316 return AVERROR(ENOTSUP);
317 1101427 uop.par.filter.type = op->rw.filter.type;
318 1101427 uop.data.kernel = av_refstruct_ref(op->rw.filter.kernel);
319
2/2
✓ Branch 0 taken 624378 times.
✓ Branch 1 taken 477049 times.
1101427 if (op->rw.filter.op == SWS_OP_FILTER_H) {
320 624378 uop.uop = SWS_UOP_READ_PLANAR_FH;
321
2/2
✓ Branch 1 taken 166743 times.
✓ Branch 2 taken 310306 times.
477049 } else if (check_filter_fma(ctx, flags, op)) {
322 166743 uop.uop = SWS_UOP_READ_PLANAR_FV_FMA;
323 } else {
324 310306 uop.uop = SWS_UOP_READ_PLANAR_FV;
325 }
326
4/4
✓ Branch 0 taken 1131836 times.
✓ Branch 1 taken 1801597 times.
✓ Branch 2 taken 669815 times.
✓ Branch 3 taken 462021 times.
2933433 } else if (op->rw.mode == SWS_RW_PACKED && op->rw.elems > 1) {
327
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 669815 times.
669815 if (op->rw.frac)
328 return AVERROR(ENOTSUP);
329
2/2
✓ Branch 0 taken 394447 times.
✓ Branch 1 taken 275368 times.
669815 uop.uop = is_read ? SWS_UOP_READ_PACKED : SWS_UOP_WRITE_PACKED;
330
2/2
✓ Branch 0 taken 9636 times.
✓ Branch 1 taken 2253982 times.
2263618 } else if (op->rw.mode == SWS_RW_PALETTE) {
331
2/4
✓ Branch 0 taken 9636 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 9636 times.
9636 if (op->rw.frac || !is_read)
332 return AVERROR(ENOTSUP);
333 9636 uop.uop = SWS_UOP_READ_PALETTE;
334
2/2
✓ Branch 0 taken 34925 times.
✓ Branch 1 taken 2219057 times.
2253982 } else if (op->rw.frac == 3) {
335
2/2
✓ Branch 0 taken 20504 times.
✓ Branch 1 taken 14421 times.
34925 uop.uop = is_read ? SWS_UOP_READ_BIT : SWS_UOP_WRITE_BIT;
336
2/2
✓ Branch 0 taken 36189 times.
✓ Branch 1 taken 2182868 times.
2219057 } else if (op->rw.frac == 1) {
337
2/2
✓ Branch 0 taken 21738 times.
✓ Branch 1 taken 14451 times.
36189 uop.uop = is_read ? SWS_UOP_READ_NIBBLE : SWS_UOP_WRITE_NIBBLE;
338 } else {
339
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2182868 times.
2182868 av_assert0(!op->rw.frac);
340
2/2
✓ Branch 0 taken 661382 times.
✓ Branch 1 taken 1521486 times.
2182868 uop.uop = is_read ? SWS_UOP_READ_PLANAR : SWS_UOP_WRITE_PLANAR;
341 }
342
343 4034860 const int planes = ff_sws_rw_op_planes(op);
344
2/2
✓ Branch 0 taken 2209134 times.
✓ Branch 1 taken 1825726 times.
4034860 if (op->op == SWS_OP_READ) {
345 2209134 ops->planes_in |= SWS_COMP_ELEMS(planes);
346 } else {
347 1825726 ops->planes_out |= SWS_COMP_ELEMS(planes);
348 }
349
350 4034860 return ff_sws_uop_list_append(ops, &uop);
351 }
352
353 2456809 static int count_idx(const int *arr, size_t size, int val)
354 {
355 2456809 int num = 0;
356
2/2
✓ Branch 0 taken 12284045 times.
✓ Branch 1 taken 2456809 times.
14740854 for (size_t i = 0; i < size; i++) {
357
2/2
✓ Branch 0 taken 3385957 times.
✓ Branch 1 taken 8898088 times.
12284045 if (arr[i] == val)
358 3385957 num++;
359 }
360
361 2456809 return num;
362 }
363
364 592726 static int translate_swizzle(SwsUOpList *ops, const SwsOp *op)
365 {
366 1778178 SwsUOp uop = {
367 .uop = SWS_UOP_PERMUTE,
368 592726 .type = pixel_type_to_int(op->type),
369 592726 .mask = ff_sws_comp_mask_needed(op),
370 };
371 592726 SwsMoveUOp *par = &uop.par.move;
372
373 /* Mask of components that are not yet satisfied */
374 592726 SwsCompMask todo = uop.mask;
375
2/2
✓ Branch 0 taken 2370904 times.
✓ Branch 1 taken 592726 times.
2963630 for (int i = 0; i < 4; i++) {
376
2/2
✓ Branch 0 taken 915965 times.
✓ Branch 1 taken 1454939 times.
2370904 if (op->swizzle.in[i] == i)
377 915965 todo &= ~SWS_COMP(i);
378 }
379
380 /* Mask of components whose value is required for the final output */
381 592726 SwsCompMask needed = 0;
382
2/2
✓ Branch 0 taken 2370904 times.
✓ Branch 1 taken 592726 times.
2963630 for (int i = 0; i < 4; i++) {
383
2/2
✓ Branch 0 taken 1790587 times.
✓ Branch 1 taken 580317 times.
2370904 if (SWS_OP_NEEDED(op, i))
384 1790587 needed |= SWS_COMP(op->swizzle.in[i]);
385 }
386
387 /* Current mapping of registers to components */
388 592726 int idx[4 + 1] = { 0, 1, 2, 3, -1 }; /* +1 for tmp */
389
390 /* Decompose the swizzle mask into a series of register-register moves */
391
2/2
✓ Branch 0 taken 1656633 times.
✓ Branch 1 taken 592726 times.
2249359 while (todo) {
392 1656633 int dst = -1, src = -1;
393
394 /* Find next unsatisfied dst <- src move that doesn't clobber a value */
395
2/2
✓ Branch 0 taken 4270687 times.
✓ Branch 1 taken 353722 times.
4624409 for (dst = 0; dst < 4; dst++) {
396
2/2
✓ Branch 0 taken 1813878 times.
✓ Branch 1 taken 2456809 times.
4270687 if (!SWS_COMP_TEST(todo, dst))
397 1813878 continue; /* already satisfied */
398 2456809 const int cur = idx[dst];
399
4/4
✓ Branch 1 taken 1527661 times.
✓ Branch 2 taken 929148 times.
✓ Branch 3 taken 1153898 times.
✓ Branch 4 taken 373763 times.
2456809 if (count_idx(idx, FF_ARRAY_ELEMS(idx), cur) == 1 && SWS_COMP_TEST(needed, cur))
400 1153898 continue; /* clobbers last remaining, still-needed value */
401
1/2
✓ Branch 0 taken 4039679 times.
✗ Branch 1 not taken.
4039679 for (src = 0; src < FF_ARRAY_ELEMS(idx); src++) {
402
2/2
✓ Branch 0 taken 1302911 times.
✓ Branch 1 taken 2736768 times.
4039679 if (idx[src] == op->swizzle.in[dst]) {
403 /* Prevent read-after-write dependency. */
404
4/4
✓ Branch 0 taken 1031715 times.
✓ Branch 1 taken 271196 times.
✓ Branch 2 taken 384 times.
✓ Branch 3 taken 1031331 times.
1302911 if (par->num_moves > 0 && src == par->dst[par->num_moves - 1])
405 384 src = par->src[par->num_moves - 1];
406 1302911 break;
407 }
408 }
409 av_assert1(src < FF_ARRAY_ELEMS(idx));
410 1302911 todo &= ~SWS_COMP(dst);
411 1302911 break;
412 }
413
414
2/2
✓ Branch 0 taken 353722 times.
✓ Branch 1 taken 1302911 times.
1656633 if (dst == 4) {
415 /* Stuck in a cycle, break it by saving to the scratch register */
416 353722 dst = 4;
417
1/2
✓ Branch 0 taken 386829 times.
✗ Branch 1 not taken.
386829 for (src = 0; src < 4; src++) {
418
2/2
✓ Branch 0 taken 353722 times.
✓ Branch 1 taken 33107 times.
386829 if (SWS_COMP_TEST(todo, src)) {
419 353722 needed &= ~SWS_COMP(idx[src]);
420 353722 break;
421 }
422 }
423 av_assert1(src < 4);
424 }
425
426
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1656633 times.
1656633 av_assert0(par->num_moves < SWS_UOP_MOVE_MAX);
427
2/2
✓ Branch 0 taken 1302911 times.
✓ Branch 1 taken 353722 times.
1656633 par->dst[par->num_moves] = dst > 3 ? -1 : dst;
428
2/2
✓ Branch 0 taken 1302911 times.
✓ Branch 1 taken 353722 times.
1656633 par->src[par->num_moves] = src > 3 ? -1 : src;
429 1656633 par->num_moves++;
430 1656633 idx[dst] = idx[src];
431 }
432
433 /* Check for duplicates in the final register map */
434 592726 SwsCompMask seen = 0;
435
2/2
✓ Branch 0 taken 2180725 times.
✓ Branch 1 taken 495063 times.
2675788 for (int i = 0; i < 4; i++) {
436
2/2
✓ Branch 0 taken 496095 times.
✓ Branch 1 taken 1684630 times.
2180725 if (!SWS_COMP_TEST(uop.mask, i))
437 496095 continue;
438 av_assert2(idx[i] >= 0); /* should be no tmp register */
439 1684630 const SwsCompMask bit = SWS_COMP(idx[i]);
440
2/2
✓ Branch 0 taken 97663 times.
✓ Branch 1 taken 1586967 times.
1684630 if (seen & bit) {
441 97663 uop.uop = SWS_UOP_COPY;
442 97663 break;
443 }
444 1586967 seen |= bit;
445 }
446
447 /* Add any extra unused components to the mask, to prevent generating
448 * duplicate uops like permute_xyz_txy_xyt and permute_xyzw_txy_xyt */
449
2/2
✓ Branch 0 taken 2370904 times.
✓ Branch 1 taken 592726 times.
2963630 for (int i = 0; i < 4; i++) {
450 2370904 const SwsCompMask bit = SWS_COMP(i);
451
4/4
✓ Branch 0 taken 783937 times.
✓ Branch 1 taken 1586967 times.
✓ Branch 2 taken 401880 times.
✓ Branch 3 taken 382057 times.
2370904 if (!(seen & bit) && idx[i] == i)
452 401880 uop.mask |= bit;
453 }
454
455 592726 return ff_sws_uop_list_append(ops, &uop);
456 }
457
458 616226 static int translate_dither_op(SwsUOpList *ops, const SwsOp *op)
459 {
460 616226 SwsUOp uop = {
461 616226 .type = op->type,
462 .uop = SWS_UOP_DITHER,
463 616226 .par.dither.size_log2 = op->dither.size_log2,
464 };
465
466
2/2
✓ Branch 0 taken 97981 times.
✓ Branch 1 taken 518245 times.
616226 if (op->dither.size_log2 == 0) {
467 /* Constant offset */
468 97981 const SwsPixel val = Q2PIXEL(op->dither.matrix[0]);
469 97981 uop.uop = SWS_UOP_ADD;
470
2/2
✓ Branch 0 taken 391924 times.
✓ Branch 1 taken 97981 times.
489905 for (int i = 0; i < 4; i++) {
471
4/4
✓ Branch 0 taken 247344 times.
✓ Branch 1 taken 144580 times.
✓ Branch 2 taken 1312 times.
✓ Branch 3 taken 246032 times.
391924 if (!SWS_OP_NEEDED(op, i) || op->dither.y_offset[i] < 0)
472 145892 continue;
473 246032 uop.mask |= SWS_COMP(i);
474 246032 uop.data.vec4[i] = val;
475 }
476
477 97981 return ff_sws_uop_list_append(ops, &uop);
478 }
479
480 518245 const int size = 1 << op->dither.size_log2;
481
2/2
✓ Branch 0 taken 2072980 times.
✓ Branch 1 taken 518245 times.
2591225 for (int i = 0; i < 4; i++) {
482
4/4
✓ Branch 0 taken 1462844 times.
✓ Branch 1 taken 610136 times.
✓ Branch 2 taken 40807 times.
✓ Branch 3 taken 1422037 times.
2072980 if (!SWS_OP_NEEDED(op, i) || op->dither.y_offset[i] < 0)
483 650943 continue;
484 1422037 const uint8_t off = op->dither.y_offset[i] & (size - 1);
485 1422037 uop.mask |= SWS_COMP(i);
486 1422037 uop.par.dither.y_offset[i] = off;
487 }
488
489 /* Allocate extra rows to allow over-reading for row offsets. Note that
490 * y_offset is currently never larger than 5, so the extra space needed
491 * for this over-allocation is bounded by 5 * size * sizeof(float),
492 * typically 320 bytes for a 16x16 dither matrix. */
493 518245 const int stride = size * sizeof(SwsPixel);
494 518245 const int num_rows = ff_sws_dither_height(&uop.par.dither);
495 518245 SwsPixel *matrix = uop.data.ptr = av_refstruct_allocz(num_rows * stride);
496
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 518245 times.
518245 if (!matrix)
497 return AVERROR(ENOMEM);
498
499
2/2
✓ Branch 0 taken 132670720 times.
✓ Branch 1 taken 518245 times.
133188965 for (int i = 0; i < size * size; i++)
500 132670720 matrix[i] = Q2PIXEL(op->dither.matrix[i]);
501 518245 memcpy(&matrix[size * size], matrix, (num_rows - size) * stride);
502
503 518245 return ff_sws_uop_list_append(ops, &uop);
504 }
505
506 751949 static int translate_linear_op(SwsContext *ctx, SwsUOpList *ops,
507 SwsUOpFlags flags, const SwsOp *op,
508 const SwsComps *input)
509 {
510 751949 SwsUOp uop = {
511 751949 .type = op->type,
512 .uop = SWS_UOP_LINEAR,
513 };
514
515 751949 const uint32_t mask = ff_sws_linear_mask(&op->lin);
516 751949 const bool bitexact = ctx->flags & SWS_BITEXACT;
517 751949 uint32_t exact = 0;
518
519
2/2
✓ Branch 0 taken 3007796 times.
✓ Branch 1 taken 751949 times.
3759745 for (int i = 0; i < 4; i++) {
520
4/4
✓ Branch 0 taken 2117827 times.
✓ Branch 1 taken 889969 times.
✓ Branch 2 taken 90027 times.
✓ Branch 3 taken 2027800 times.
3007796 if (!SWS_OP_NEEDED(op, i) || !(mask & SWS_MASK_ROW(i))) {
521 979996 uop.par.lin.zero |= SWS_MASK_ROW(i);
522 979996 continue;
523 }
524 2027800 uop.mask |= SWS_COMP(i);
525 2027800 bool nonzero = (op->lin.m[i][4].num != 0);
526
2/2
✓ Branch 0 taken 10139000 times.
✓ Branch 1 taken 2027800 times.
12166800 for (int j = 0; j < 5; j++) {
527 10139000 const AVRational64 k = op->lin.m[i][j];
528 10139000 const SwsPixel px = Q2PIXEL(k);
529 10139000 uop.data.mat4[i][j] = px;
530
2/2
✓ Branch 0 taken 4643244 times.
✓ Branch 1 taken 5495756 times.
10139000 if (k.num == 0)
531 4643244 uop.par.lin.zero |= SWS_MASK(i, j);
532
4/4
✓ Branch 0 taken 4217270 times.
✓ Branch 1 taken 1278486 times.
✓ Branch 2 taken 1380 times.
✓ Branch 3 taken 4215890 times.
5495756 else if (j < 4 && k.num == k.den)
533 1380 uop.par.lin.one |= SWS_MASK(i, j);
534
8/8
✓ Branch 0 taken 4215890 times.
✓ Branch 1 taken 1278486 times.
✓ Branch 2 taken 3466576 times.
✓ Branch 3 taken 749314 times.
✓ Branch 4 taken 868434 times.
✓ Branch 5 taken 2598142 times.
✓ Branch 7 taken 787046 times.
✓ Branch 8 taken 81388 times.
5494376 else if (j < 4 && nonzero && (!bitexact || exact_prod(uop.type, px, input, j)))
535 3385188 exact |= SWS_MASK(i, j);
536
2/2
✓ Branch 0 taken 5495756 times.
✓ Branch 1 taken 4643244 times.
10139000 if (k.num != 0)
537 5495756 nonzero = true;
538 }
539 }
540
541
2/2
✓ Branch 0 taken 367142 times.
✓ Branch 1 taken 384807 times.
751949 if (flags & SWS_UOP_FLAG_FMA) {
542 /* multiplication by 1 and 0 are always exact by definition */
543 367142 uop.uop = SWS_UOP_LINEAR_FMA;
544 367142 uop.par.lin.exact = exact | uop.par.lin.zero | uop.par.lin.one;
545 }
546
547 751949 return ff_sws_uop_list_append(ops, &uop);
548 }
549
550 430414 static bool is_expand_bit(SwsPixelType type, AVRational64 factor)
551 {
552
2/2
✓ Branch 0 taken 352656 times.
✓ Branch 1 taken 77758 times.
430414 if (factor.den != 1)
553 352656 return false;
554
555
4/6
✓ Branch 0 taken 643 times.
✓ Branch 1 taken 1516 times.
✓ Branch 2 taken 230 times.
✓ Branch 3 taken 75369 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
77758 switch (type) {
556 643 case SWS_PIXEL_U8: return factor.num == UINT8_MAX;
557 1516 case SWS_PIXEL_U16: return factor.num == UINT16_MAX;
558 230 case SWS_PIXEL_U32: return factor.num == UINT32_MAX;
559 75369 case SWS_PIXEL_F32: return false;
560 case SWS_PIXEL_NONE:
561 case SWS_PIXEL_TYPE_NB: break;
562 }
563
564 av_unreachable("Invalid pixel type!");
565 return false;
566 }
567
568 11231016 static int translate_op(SwsContext *ctx, SwsUOpList *uops, SwsUOpFlags flags,
569 const SwsOp *op, const SwsComps *input)
570 {
571
6/6
✓ Branch 0 taken 466852 times.
✓ Branch 1 taken 4034860 times.
✓ Branch 2 taken 592726 times.
✓ Branch 3 taken 616226 times.
✓ Branch 4 taken 751949 times.
✓ Branch 5 taken 4768403 times.
11231016 switch (op->op) {
572 466852 case SWS_OP_FILTER_H:
573 case SWS_OP_FILTER_V:
574 466852 return AVERROR(ENOTSUP); /* always handled by subpass splitting */
575 4034860 case SWS_OP_READ:
576 case SWS_OP_WRITE:
577 4034860 return translate_rw_op(ctx, uops, flags, op);
578 592726 case SWS_OP_SWIZZLE:
579 592726 return translate_swizzle(uops, op);
580 616226 case SWS_OP_DITHER:
581 616226 return translate_dither_op(uops, op);
582 751949 case SWS_OP_LINEAR:
583 751949 return translate_linear_op(ctx, uops, flags, op, input);
584 4768403 default:
585 4768403 break;
586 }
587
588 /* Default handling for "simple" ops */
589 9536806 SwsUOp uop = {
590 4768403 .type = op->type,
591 .uop = SWS_UOP_INVALID,
592 4768403 .mask = ff_sws_comp_mask_needed(op),
593 };
594
595
8/9
✓ Branch 0 taken 1241492 times.
✓ Branch 1 taken 461701 times.
✓ Branch 2 taken 179496 times.
✓ Branch 3 taken 325676 times.
✓ Branch 4 taken 430414 times.
✓ Branch 5 taken 949227 times.
✓ Branch 6 taken 899501 times.
✓ Branch 7 taken 280896 times.
✗ Branch 8 not taken.
4768403 switch (op->op) {
596 1241492 case SWS_OP_CONVERT:
597
2/2
✓ Branch 0 taken 1908 times.
✓ Branch 1 taken 1239584 times.
1241492 if (op->convert.expand) {
598
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1908 times.
1908 av_assert0(op->type == SWS_PIXEL_U8);
599
1/3
✓ Branch 0 taken 1908 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
1908 switch (op->convert.to) {
600 1908 case SWS_PIXEL_U16: uop.uop = SWS_UOP_EXPAND_PAIR; break;
601 case SWS_PIXEL_U32: uop.uop = SWS_UOP_EXPAND_QUAD; break;
602 }
603 } else {
604
4/5
✓ Branch 0 taken 320990 times.
✓ Branch 1 taken 650778 times.
✓ Branch 2 taken 59752 times.
✓ Branch 3 taken 208064 times.
✗ Branch 4 not taken.
1239584 switch (op->convert.to) {
605 320990 case SWS_PIXEL_U8: uop.uop = SWS_UOP_TO_U8; break;
606 650778 case SWS_PIXEL_U16: uop.uop = SWS_UOP_TO_U16; break;
607 59752 case SWS_PIXEL_U32: uop.uop = SWS_UOP_TO_U32; break;
608 208064 case SWS_PIXEL_F32: uop.uop = SWS_UOP_TO_F32; break;
609 }
610 }
611 1241492 break;
612 461701 case SWS_OP_UNPACK:
613 case SWS_OP_PACK:
614
2/2
✓ Branch 0 taken 189861 times.
✓ Branch 1 taken 271840 times.
461701 uop.uop = op->op == SWS_OP_PACK ? SWS_UOP_PACK : SWS_UOP_UNPACK;
615 461701 uop.mask = 0;
616
4/4
✓ Branch 0 taken 1846804 times.
✓ Branch 1 taken 146052 times.
✓ Branch 2 taken 1531155 times.
✓ Branch 3 taken 315649 times.
1992856 for (int i = 0; i < 4 && op->pack.pattern[i]; i++) {
617 1531155 uop.par.pack.pattern[i] = op->pack.pattern[i];
618
4/4
✓ Branch 0 taken 901484 times.
✓ Branch 1 taken 629671 times.
✓ Branch 2 taken 806326 times.
✓ Branch 3 taken 95158 times.
1531155 if (op->op == SWS_OP_PACK || SWS_OP_NEEDED(op, i))
619 1435997 uop.mask |= SWS_COMP(i);
620 }
621 461701 break;
622 179496 case SWS_OP_LSHIFT:
623 case SWS_OP_RSHIFT:
624
2/2
✓ Branch 0 taken 76485 times.
✓ Branch 1 taken 103011 times.
179496 uop.uop = op->op == SWS_OP_LSHIFT ? SWS_UOP_LSHIFT : SWS_UOP_RSHIFT;
625 179496 uop.par.shift.amount = op->shift.amount;
626 179496 break;
627 325676 case SWS_OP_CLEAR:
628 325676 uop.uop = SWS_UOP_CLEAR;
629 325676 uop.type = pixel_type_to_int(op->type);
630 325676 uop.mask &= op->clear.mask;
631
2/2
✓ Branch 0 taken 1302704 times.
✓ Branch 1 taken 325676 times.
1628380 for (int i = 0; i < 4; i++) {
632
2/2
✓ Branch 0 taken 931236 times.
✓ Branch 1 taken 371468 times.
1302704 if (!SWS_COMP_TEST(op->clear.mask, i))
633 931236 continue;
634 371468 const AVRational64 v = op->clear.value[i];
635 371468 const SwsPixel px = Q2PIXEL(op->clear.value[i]);
636 371468 uop.data.vec4[i] = px;
637
2/2
✓ Branch 0 taken 65203 times.
✓ Branch 1 taken 306265 times.
371468 if (v.num == 0)
638 65203 uop.par.clear.zero |= SWS_COMP(i);
639
2/2
✓ Branch 1 taken 76491 times.
✓ Branch 2 taken 229774 times.
306265 else if (pixel_is_1s(op->type, px))
640 76491 uop.par.clear.one |= SWS_COMP(i);
641 }
642 325676 break;
643 430414 case SWS_OP_SCALE:
644
2/2
✓ Branch 1 taken 314 times.
✓ Branch 2 taken 430100 times.
430414 if (is_expand_bit(op->type, op->scale.factor)) {
645 314 uop.uop = SWS_UOP_EXPAND_BIT;
646 } else {
647 430100 uop.uop = SWS_UOP_SCALE;
648 430100 uop.data.scalar = Q2PIXEL(op->scale.factor);
649 }
650 430414 break;
651 949227 case SWS_OP_MIN:
652 case SWS_OP_MAX:
653
2/2
✓ Branch 0 taken 642610 times.
✓ Branch 1 taken 306617 times.
949227 uop.uop = op->op == SWS_OP_MIN ? SWS_UOP_MIN : SWS_UOP_MAX;
654 949227 uop.mask &= ff_sws_comp_mask_q4(op->clamp.limit);
655
2/2
✓ Branch 0 taken 3796908 times.
✓ Branch 1 taken 949227 times.
4746135 for (int i = 0; i < 4; i++) {
656
2/2
✓ Branch 0 taken 2494492 times.
✓ Branch 1 taken 1302416 times.
3796908 if (SWS_COMP_TEST(uop.mask, i))
657 2494492 uop.data.vec4[i] = Q2PIXEL(op->clamp.limit[i]);
658 }
659 949227 break;
660 899501 case SWS_OP_SWAP_BYTES:
661 899501 uop.uop = SWS_UOP_SWAP_BYTES;
662 899501 uop.type = pixel_type_to_int(op->type);
663 899501 break;
664 280896 case SWS_OP_LUT_3D:
665 280896 uop.uop = SWS_UOP_LUT_3D;
666 280896 uop.par.lut3d.dynamic = op->lut3d.dynamic;
667 280896 uop.data.lut3d = av_refstruct_ref_c(op->lut3d.lut);
668 280896 break;
669 default:
670 return AVERROR(ENOTSUP);
671 }
672
673
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4768403 times.
4768403 av_assert0(uop.uop != SWS_UOP_INVALID);
674 4768403 return ff_sws_uop_list_append(uops, &uop);
675 }
676
677 2292578 int ff_sws_ops_translate(SwsContext *ctx, const SwsOpList *ops,
678 SwsUOpFlags flags, SwsUOpList *uops)
679 {
680 2292578 SwsComps input = ops->comps_src;
681
2/2
✓ Branch 0 taken 11231016 times.
✓ Branch 1 taken 1825726 times.
13056742 for (int i = 0; i < ops->num_ops; i++) {
682 11231016 const SwsOp *op = &ops->ops[i];
683 11231016 const int pixel_size = ff_sws_pixel_type_size(op->type);
684
2/2
✓ Branch 0 taken 3268800 times.
✓ Branch 1 taken 7962216 times.
11231016 if (pixel_size > uops->pixel_size_max)
685 3268800 uops->pixel_size_max = pixel_size;
686
687 11231016 int ret = translate_op(ctx, uops, flags, op, &input);
688
2/2
✓ Branch 0 taken 466852 times.
✓ Branch 1 taken 10764164 times.
11231016 if (ret < 0)
689 466852 return ret;
690 10764164 input = ops->ops[i].comps;
691 }
692
693 1825726 return ff_sws_uop_list_optimize(ctx, flags, uops);
694 }
695