FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libswscale/uops.c
Date: 2026-10-19 08:11:58
Exec Total Coverage
Lines: 385 396 97.2%
Functions: 19 19 100.0%
Branches: 284 306 92.8%

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 239160066 int ff_sws_uop_cmp(const SwsUOp *a, const SwsUOp *b)
32 {
33
2/2
✓ Branch 0 taken 150875282 times.
✓ Branch 1 taken 88284784 times.
239160066 if (a->type != b->type)
34 150875282 return (int) a->type - b->type;
35
2/2
✓ Branch 0 taken 66050005 times.
✓ Branch 1 taken 22234779 times.
88284784 if (a->uop != b->uop)
36 66050005 return (int) a->uop - b->uop;
37
2/2
✓ Branch 0 taken 9923682 times.
✓ Branch 1 taken 12311097 times.
22234779 if (a->mask != b->mask)
38 9923682 return (int) a->mask - b->mask;
39 12311097 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 #define Q2PIXEL(val) ff_sws_pixel_from_q64(op->type, val)
51
52 1517146 void ff_sws_uop_name(const SwsUOp *op, char buf[SWS_UOP_NAME_MAX])
53 {
54 AVBPrint bp;
55 1517146 av_bprint_init_for_buffer(&bp, buf, SWS_UOP_NAME_MAX);
56
57
1/2
✓ Branch 0 taken 1517146 times.
✗ Branch 1 not taken.
1517146 if (op->type != SWS_PIXEL_NONE)
58 1517146 av_bprintf(&bp, "%s_", ff_sws_pixel_type_name(op->type));
59 1517146 av_bprintf(&bp, "%s", uop_names[op->uop].abbr);
60
61
1/2
✓ Branch 0 taken 1517146 times.
✗ Branch 1 not taken.
1517146 if (op->mask)
62 1517146 av_bprintf(&bp, "_%s", ff_sws_comp_mask_str(op->mask));
63
64 1517146 const SwsUOpParams *par = &op->par;
65
10/10
✓ Branch 0 taken 145772 times.
✓ Branch 1 taken 2691 times.
✓ Branch 2 taken 24814 times.
✓ Branch 3 taken 86527 times.
✓ Branch 4 taken 58702 times.
✓ Branch 5 taken 52189 times.
✓ Branch 6 taken 99777 times.
✓ Branch 7 taken 86214 times.
✓ Branch 8 taken 92 times.
✓ Branch 9 taken 960368 times.
1517146 switch (op->uop) {
66 145772 case SWS_UOP_READ_PLANAR_FH:
67 case SWS_UOP_READ_PLANAR_FV:
68 case SWS_UOP_READ_PLANAR_FV_FMA:
69 145772 av_bprintf(&bp, "_%s", ff_sws_pixel_type_name(par->filter.type));
70 145772 break;
71 2691 case SWS_UOP_RW_SHUFFLE:
72 2691 av_bprintf(&bp, "_%x_%u_%u", par->shuffle.clear_value,
73 2691 par->shuffle.read_size, par->shuffle.write_size);
74 2691 break;
75 24814 case SWS_UOP_LSHIFT:
76 case SWS_UOP_RSHIFT:
77 24814 av_bprintf(&bp, "_%u", par->shift.amount);
78 24814 break;
79 86527 case SWS_UOP_PERMUTE:
80 case SWS_UOP_COPY:
81 86527 av_bprint_chars(&bp, '_', 1);
82
2/2
✓ Branch 0 taken 231804 times.
✓ Branch 1 taken 86527 times.
318331 for (int i = 0; i < par->move.num_moves; i++)
83 231804 av_bprint_chars(&bp, "txyzw"[par->move.dst[i] + 1], 1);
84 86527 av_bprint_chars(&bp, '_', 1);
85
2/2
✓ Branch 0 taken 231804 times.
✓ Branch 1 taken 86527 times.
318331 for (int i = 0; i < par->move.num_moves; i++)
86 231804 av_bprint_chars(&bp, "txyzw"[par->move.src[i] + 1], 1);
87 86527 break;
88 58702 case SWS_UOP_PACK:
89 case SWS_UOP_UNPACK:
90 58702 av_bprint_chars(&bp, '_', 1);
91
4/4
✓ Branch 0 taken 234808 times.
✓ Branch 1 taken 16339 times.
✓ Branch 2 taken 192445 times.
✓ Branch 3 taken 42363 times.
251147 for (int i = 0; i < 4 && par->pack.pattern[i]; i++)
92 192445 av_bprintf(&bp, "%x", par->pack.pattern[i]);
93 58702 break;
94 52189 case SWS_UOP_CLEAR:
95 52189 av_bprint_chars(&bp, '_', 1);
96
2/2
✓ Branch 0 taken 208756 times.
✓ Branch 1 taken 52189 times.
260945 for (int i = 0; i < 4; i++) {
97
2/2
✓ Branch 0 taken 145889 times.
✓ Branch 1 taken 62867 times.
208756 if (!SWS_COMP_TEST(op->mask, i))
98 145889 continue;
99
2/2
✓ Branch 0 taken 17888 times.
✓ Branch 1 taken 44979 times.
62867 else if (SWS_COMP_TEST(par->clear.one, i))
100 17888 av_bprint_chars(&bp, '1', 1);
101
2/2
✓ Branch 0 taken 10549 times.
✓ Branch 1 taken 34430 times.
44979 else if (SWS_COMP_TEST(par->clear.zero, i))
102 10549 av_bprint_chars(&bp, '0', 1);
103 else
104 34430 av_bprint_chars(&bp, 'x', 1);
105 }
106 52189 break;
107 99777 case SWS_UOP_LINEAR:
108 case SWS_UOP_LINEAR_FMA:
109
2/2
✓ Branch 0 taken 399108 times.
✓ Branch 1 taken 99777 times.
498885 for (int i = 0; i < 4; i++) {
110
2/2
✓ Branch 0 taken 146731 times.
✓ Branch 1 taken 252377 times.
399108 if (!SWS_COMP_TEST(op->mask, i))
111 146731 continue;
112 252377 av_bprint_chars(&bp, '_', 1);
113
2/2
✓ Branch 0 taken 1261885 times.
✓ Branch 1 taken 252377 times.
1514262 for (int j = 0; j < 5; j++) {
114
2/2
✓ Branch 0 taken 405 times.
✓ Branch 1 taken 1261480 times.
1261885 if (par->lin.one & SWS_MASK(i, j))
115 405 av_bprint_chars(&bp, '1', 1);
116
2/2
✓ Branch 0 taken 521658 times.
✓ Branch 1 taken 739822 times.
1261480 else if (par->lin.zero & SWS_MASK(i, j))
117 521658 av_bprint_chars(&bp, '0', 1);
118
2/2
✓ Branch 0 taken 111215 times.
✓ Branch 1 taken 628607 times.
739822 else if (par->lin.exact & SWS_MASK(i, j))
119 111215 av_bprint_chars(&bp, 'X', 1);
120 else
121 628607 av_bprint_chars(&bp, 'x', 1);
122 }
123 }
124 99777 break;
125 86214 case SWS_UOP_DITHER:
126
2/2
✓ Branch 0 taken 344856 times.
✓ Branch 1 taken 86214 times.
431070 for (int i = 0; i < 4; i++) {
127
2/2
✓ Branch 0 taken 230756 times.
✓ Branch 1 taken 114100 times.
344856 if (SWS_COMP_TEST(op->mask, i))
128 230756 av_bprintf(&bp, "_%d", par->dither.y_offset[i]);
129 }
130 86214 const unsigned size = 1u << par->dither.size_log2;
131 86214 av_bprintf(&bp, "_%ux%u", size, size);
132 86214 break;
133 92 case SWS_UOP_LUT_3D:
134
2/2
✓ Branch 0 taken 46 times.
✓ Branch 1 taken 46 times.
92 av_bprintf(&bp, "_%s", par->lut3d.dynamic ? "dynamic" : "static");
135 92 break;
136 }
137
138
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1517146 times.
1517146 av_assert0(av_bprint_is_complete(&bp));
139 1517146 }
140
141 11333999 static void uop_uninit(SwsUOp *uop)
142 {
143
4/4
✓ Branch 0 taken 549677 times.
✓ Branch 1 taken 1134073 times.
✓ Branch 2 taken 280960 times.
✓ Branch 3 taken 9369289 times.
11333999 switch (uop->uop) {
144 549677 case SWS_UOP_DITHER:
145 549677 av_refstruct_unref(&uop->data.ptr);
146 549677 break;
147 1134073 case SWS_UOP_READ_PLANAR_FH:
148 case SWS_UOP_READ_PLANAR_FV:
149 case SWS_UOP_READ_PLANAR_FV_FMA:
150 1134073 av_refstruct_unref(&uop->data.kernel);
151 1134073 break;
152 280960 case SWS_UOP_LUT_3D:
153 280960 av_refstruct_unref(&uop->data.lut3d);
154 280960 break;
155 }
156
157 11333999 *uop = (SwsUOp) {0};
158 11333999 }
159
160 2388903 void ff_sws_uop_list_free(SwsUOpList **p_ops)
161 {
162 2388903 SwsUOpList *ops = *p_ops;
163
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2388903 times.
2388903 if (!ops)
164 ✗ return;
165
166
2/2
✓ Branch 0 taken 11244890 times.
✓ Branch 1 taken 2388903 times.
13633793 for (int i = 0; i < ops->num_ops; i++)
167 11244890 uop_uninit(&ops->ops[i]);
168
169 2388903 av_freep(&ops->ops);
170 2388903 av_free(ops);
171 2388903 *p_ops = NULL;
172 }
173
174 2388903 SwsUOpList *ff_sws_uop_list_alloc(void)
175 {
176 2388903 return av_mallocz(sizeof(SwsUOpList));
177 }
178
179 11333999 int ff_sws_uop_list_append(SwsUOpList *uops, SwsUOp *uop)
180 {
181
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 11333999 times.
11333999 if (!av_dynarray2_add((void **) &uops->ops, &uops->num_ops,
182 sizeof(*uop), (uint8_t *) uop))
183 {
184 ✗ uop_uninit(uop);
185 ✗ return AVERROR(ENOMEM);
186 }
187
188 11333999 *uop = (SwsUOp) {0};
189 11333999 return 0;
190 }
191
192 32245 void ff_sws_uop_list_remove_at(SwsUOpList *uops, int index, int count)
193 {
194 32245 const int end = uops->num_ops - count;
195 av_assert2(index >= 0 && count >= 0 && index + count <= uops->num_ops);
196
2/2
✓ Branch 0 taken 89109 times.
✓ Branch 1 taken 32245 times.
121354 for (int i = 0; i < count; i++)
197 89109 uop_uninit(&uops->ops[index + i]);
198
2/2
✓ Branch 0 taken 5230 times.
✓ Branch 1 taken 32245 times.
37475 for (int i = index; i < end; i++)
199 5230 uops->ops[i] = uops->ops[i + count];
200 32245 uops->num_ops = end;
201 32245 }
202
203 573022 int ff_sws_dither_height(const SwsDitherUOp *dither)
204 {
205 573022 int max_offset = 0;
206
2/2
✓ Branch 0 taken 2292088 times.
✓ Branch 1 taken 573022 times.
2865110 for (int i = 0; i < 4; i++)
207 2292088 max_offset = FFMAX(max_offset, dither->y_offset[i]);
208 573022 return (1 << dither->size_log2) + max_offset;
209 }
210
211 4999010 static SwsPixelType pixel_type_to_int(const SwsPixelType type)
212 {
213
3/4
✓ Branch 0 taken 964186 times.
✓ Branch 1 taken 2779787 times.
✓ Branch 2 taken 1255037 times.
✗ Branch 3 not taken.
4999010 switch (ff_sws_pixel_type_size(type)) {
214 964186 case 1: return SWS_PIXEL_U8;
215 2779787 case 2: return SWS_PIXEL_U16;
216 1255037 case 4: return SWS_PIXEL_U32;
217 ✗ default: break;
218 }
219
220 ✗ av_unreachable("Invalid pixel type!");
221 return SWS_PIXEL_NONE;
222 }
223
224 1604169 static bool exact_product_f32(float a, float b)
225 {
226 1604169 volatile float prod = a * b;
227
2/2
✓ Branch 0 taken 815479 times.
✓ Branch 1 taken 788690 times.
1604169 volatile float result = b ? prod / b : 0.0f;
228
4/4
✓ Branch 0 taken 815479 times.
✓ Branch 1 taken 788690 times.
✓ Branch 2 taken 809878 times.
✓ Branch 3 taken 5601 times.
1604169 return !b || result == a;
229 }
230
231 897709 static bool exact_prod(SwsPixelType type, SwsPixel coef,
232 const SwsComps *comps, int idx)
233 {
234 897709 const AVRational64 minq = comps->min[idx];
235 897709 const AVRational64 maxq = comps->max[idx];
236
2/2
✓ Branch 0 taken 1158 times.
✓ Branch 1 taken 896551 times.
897709 if (ff_sws_pixel_type_is_int(type))
237 1158 return true;
238
3/4
✓ Branch 0 taken 802346 times.
✓ Branch 1 taken 94205 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 802346 times.
896551 else if (!minq.den || !maxq.den)
239 94205 return false; /* unknown bounds */
240
241 802346 const SwsPixel min = ff_sws_pixel_from_q64(type, minq);
242 802346 const SwsPixel max = ff_sws_pixel_from_q64(type, maxq);
243
1/2
✓ Branch 0 taken 802346 times.
✗ Branch 1 not taken.
802346 switch (type) {
244 802346 case SWS_PIXEL_F32:
245
4/4
✓ Branch 1 taken 801823 times.
✓ Branch 2 taken 523 times.
✓ Branch 3 taken 796745 times.
✓ Branch 4 taken 5078 times.
1604169 return exact_product_f32(coef.f32, min.f32) &&
246 801823 exact_product_f32(coef.f32, max.f32);
247 }
248
249 ✗ av_unreachable("Invalid pixel type!");
250 return false;
251 }
252
253 492393 static bool check_filter_fma(SwsContext *ctx, SwsUOpFlags flags, const SwsOp *op)
254 {
255
2/2
✓ Branch 0 taken 262673 times.
✓ Branch 1 taken 229720 times.
492393 if (!(flags & SWS_UOP_FLAG_FMA))
256 262673 return false;
257
2/2
✓ Branch 0 taken 154248 times.
✓ Branch 1 taken 75472 times.
229720 if (!(ctx->flags & SWS_BITEXACT))
258 154248 return true;
259
2/2
✓ Branch 0 taken 42672 times.
✓ Branch 1 taken 32800 times.
75472 if (!ff_sws_pixel_type_is_int(op->type))
260 42672 return false;
261
262 32800 const int bits = ff_sws_pixel_type_size(op->type) * 8;
263 32800 const uint64_t max_val = UINT64_MAX >> (64 - bits);
264
265 /* Maximum value representable losslessly as float. Note that this is
266 * currently true only for U8, but that may change if we ever update the
267 * value of SWS_FILTER_SCALE. */
268 32800 return max_val * SWS_FILTER_SCALE <= (1 << 22);
269 }
270
271 4218692 static int translate_rw_op(SwsContext *ctx, SwsUOpList *ops, SwsUOpFlags flags,
272 const SwsOp *op)
273 {
274 8437384 SwsUOp uop = {
275 4218692 .type = op->type,
276
6/6
✓ Branch 0 taken 3105548 times.
✓ Branch 1 taken 1113144 times.
✓ Branch 2 taken 2956537 times.
✓ Branch 3 taken 1262155 times.
✓ Branch 4 taken 759177 times.
✓ Branch 5 taken 3459515 times.
4218692 .mask = SWS_COMP_MASK(op->rw.elems > 0, op->rw.elems > 1,
277 op->rw.elems > 2, op->rw.elems > 3),
278 };
279
280 /* Non-filtered reads don't care about the exact pixel contents */
281
2/2
✓ Branch 0 taken 3084619 times.
✓ Branch 1 taken 1134073 times.
4218692 if (!op->rw.filter.op)
282 3084619 uop.type = pixel_type_to_int(op->type);
283
284 4218692 const bool is_read = op->op == SWS_OP_READ;
285
2/2
✓ Branch 0 taken 1134073 times.
✓ Branch 1 taken 3084619 times.
4218692 if (op->rw.filter.op) {
286
3/6
✓ Branch 0 taken 1134073 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1134073 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 1134073 times.
1134073 if (op->op == SWS_OP_WRITE || op->rw.frac || op->rw.mode != SWS_RW_PLANAR)
287 ✗ return AVERROR(ENOTSUP);
288 1134073 uop.par.filter.type = op->rw.filter.type;
289 1134073 uop.data.kernel = av_refstruct_ref(op->rw.filter.kernel);
290
2/2
✓ Branch 0 taken 641680 times.
✓ Branch 1 taken 492393 times.
1134073 if (op->rw.filter.op == SWS_OP_FILTER_H) {
291 641680 uop.uop = SWS_UOP_READ_PLANAR_FH;
292
2/2
✓ Branch 1 taken 165926 times.
✓ Branch 2 taken 326467 times.
492393 } else if (check_filter_fma(ctx, flags, op)) {
293 165926 uop.uop = SWS_UOP_READ_PLANAR_FV_FMA;
294 } else {
295 326467 uop.uop = SWS_UOP_READ_PLANAR_FV;
296 }
297
4/4
✓ Branch 0 taken 1189012 times.
✓ Branch 1 taken 1895607 times.
✓ Branch 2 taken 705830 times.
✓ Branch 3 taken 483182 times.
3084619 } else if (op->rw.mode == SWS_RW_PACKED && op->rw.elems > 1) {
298
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 705830 times.
705830 if (op->rw.frac)
299 ✗ return AVERROR(ENOTSUP);
300
2/2
✓ Branch 0 taken 413790 times.
✓ Branch 1 taken 292040 times.
705830 uop.uop = is_read ? SWS_UOP_READ_PACKED : SWS_UOP_WRITE_PACKED;
301
2/2
✓ Branch 0 taken 9768 times.
✓ Branch 1 taken 2369021 times.
2378789 } else if (op->rw.mode == SWS_RW_PALETTE) {
302
4/6
✓ Branch 0 taken 9636 times.
✓ Branch 1 taken 132 times.
✓ Branch 2 taken 9636 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 9636 times.
9768 if (!(flags & SWS_UOP_FLAG_READ_PALETTE) || op->rw.frac || !is_read)
303 132 return AVERROR(ENOTSUP);
304 9636 uop.uop = SWS_UOP_READ_PALETTE;
305
2/2
✓ Branch 0 taken 36699 times.
✓ Branch 1 taken 2332322 times.
2369021 } else if (op->rw.frac == 3) {
306
2/2
✓ Branch 0 taken 21345 times.
✓ Branch 1 taken 15354 times.
36699 uop.uop = is_read ? SWS_UOP_READ_BIT : SWS_UOP_WRITE_BIT;
307
2/2
✓ Branch 0 taken 39212 times.
✓ Branch 1 taken 2293110 times.
2332322 } else if (op->rw.frac == 1) {
308
2/2
✓ Branch 0 taken 23852 times.
✓ Branch 1 taken 15360 times.
39212 uop.uop = is_read ? SWS_UOP_READ_NIBBLE : SWS_UOP_WRITE_NIBBLE;
309 } else {
310
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2293110 times.
2293110 av_assert0(!op->rw.frac);
311
2/2
✓ Branch 0 taken 696590 times.
✓ Branch 1 taken 1596520 times.
2293110 uop.uop = is_read ? SWS_UOP_READ_PLANAR : SWS_UOP_WRITE_PLANAR;
312 }
313
314 4218560 const int planes = ff_sws_rw_op_planes(op);
315
2/2
✓ Branch 0 taken 2299286 times.
✓ Branch 1 taken 1919274 times.
4218560 if (op->op == SWS_OP_READ) {
316 2299286 ops->planes_in |= SWS_COMP_ELEMS(planes);
317 } else {
318 1919274 ops->planes_out |= SWS_COMP_ELEMS(planes);
319 }
320
321 4218560 return ff_sws_uop_list_append(ops, &uop);
322 }
323
324 2593854 static int count_idx(const int *arr, size_t size, int val)
325 {
326 2593854 int num = 0;
327
2/2
✓ Branch 0 taken 12969270 times.
✓ Branch 1 taken 2593854 times.
15563124 for (size_t i = 0; i < size; i++) {
328
2/2
✓ Branch 0 taken 3577561 times.
✓ Branch 1 taken 9391709 times.
12969270 if (arr[i] == val)
329 3577561 num++;
330 }
331
332 2593854 return num;
333 }
334
335 626817 static int translate_swizzle(SwsUOpList *ops, const SwsOp *op)
336 {
337 1880451 SwsUOp uop = {
338 .uop = SWS_UOP_PERMUTE,
339 626817 .type = pixel_type_to_int(op->type),
340 626817 .mask = ff_sws_comp_mask_needed(op),
341 };
342 626817 SwsMoveUOp *par = &uop.par.move;
343
344 /* Mask of components that are not yet satisfied */
345 626817 SwsCompMask todo = uop.mask;
346
2/2
✓ Branch 0 taken 2507268 times.
✓ Branch 1 taken 626817 times.
3134085 for (int i = 0; i < 4; i++) {
347
2/2
✓ Branch 0 taken 967344 times.
✓ Branch 1 taken 1539924 times.
2507268 if (op->swizzle.in[i] == i)
348 967344 todo &= ~SWS_COMP(i);
349 }
350
351 /* Mask of components whose value is required for the final output */
352 626817 SwsCompMask needed = 0;
353
2/2
✓ Branch 0 taken 2507268 times.
✓ Branch 1 taken 626817 times.
3134085 for (int i = 0; i < 4; i++) {
354
2/2
✓ Branch 0 taken 1891249 times.
✓ Branch 1 taken 616019 times.
2507268 if (SWS_OP_NEEDED(op, i))
355 1891249 needed |= SWS_COMP(op->swizzle.in[i]);
356 }
357
358 /* Current mapping of registers to components */
359 626817 int idx[4 + 1] = { 0, 1, 2, 3, -1 }; /* +1 for tmp */
360
361 /* Decompose the swizzle mask into a series of register-register moves */
362
2/2
✓ Branch 0 taken 1752044 times.
✓ Branch 1 taken 626817 times.
2378861 while (todo) {
363 1752044 int dst = -1, src = -1;
364
365 /* Find next unsatisfied dst <- src move that doesn't clobber a value */
366
2/2
✓ Branch 0 taken 4516019 times.
✓ Branch 1 taken 374634 times.
4890653 for (dst = 0; dst < 4; dst++) {
367
2/2
✓ Branch 0 taken 1922165 times.
✓ Branch 1 taken 2593854 times.
4516019 if (!SWS_COMP_TEST(todo, dst))
368 1922165 continue; /* already satisfied */
369 2593854 const int cur = idx[dst];
370
4/4
✓ Branch 1 taken 1610147 times.
✓ Branch 2 taken 983707 times.
✓ Branch 3 taken 1216444 times.
✓ Branch 4 taken 393703 times.
2593854 if (count_idx(idx, FF_ARRAY_ELEMS(idx), cur) == 1 && SWS_COMP_TEST(needed, cur))
371 1216444 continue; /* clobbers last remaining, still-needed value */
372
1/2
✓ Branch 0 taken 4272371 times.
✗ Branch 1 not taken.
4272371 for (src = 0; src < FF_ARRAY_ELEMS(idx); src++) {
373
2/2
✓ Branch 0 taken 1377410 times.
✓ Branch 1 taken 2894961 times.
4272371 if (idx[src] == op->swizzle.in[dst]) {
374 /* Prevent read-after-write dependency. */
375
4/4
✓ Branch 0 taken 1091284 times.
✓ Branch 1 taken 286126 times.
✓ Branch 2 taken 422 times.
✓ Branch 3 taken 1090862 times.
1377410 if (par->num_moves > 0 && src == par->dst[par->num_moves - 1])
376 422 src = par->src[par->num_moves - 1];
377 1377410 break;
378 }
379 }
380 av_assert1(src < FF_ARRAY_ELEMS(idx));
381 1377410 todo &= ~SWS_COMP(dst);
382 1377410 break;
383 }
384
385
2/2
✓ Branch 0 taken 374634 times.
✓ Branch 1 taken 1377410 times.
1752044 if (dst == 4) {
386 /* Stuck in a cycle, break it by saving to the scratch register */
387 374634 dst = 4;
388
1/2
✓ Branch 0 taken 409573 times.
✗ Branch 1 not taken.
409573 for (src = 0; src < 4; src++) {
389
2/2
✓ Branch 0 taken 374634 times.
✓ Branch 1 taken 34939 times.
409573 if (SWS_COMP_TEST(todo, src)) {
390 374634 needed &= ~SWS_COMP(idx[src]);
391 374634 break;
392 }
393 }
394 av_assert1(src < 4);
395 }
396
397
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1752044 times.
1752044 av_assert0(par->num_moves < SWS_UOP_MOVE_MAX);
398
2/2
✓ Branch 0 taken 1377410 times.
✓ Branch 1 taken 374634 times.
1752044 par->dst[par->num_moves] = dst > 3 ? -1 : dst;
399
2/2
✓ Branch 0 taken 1377410 times.
✓ Branch 1 taken 374634 times.
1752044 par->src[par->num_moves] = src > 3 ? -1 : src;
400 1752044 par->num_moves++;
401 1752044 idx[dst] = idx[src];
402 }
403
404 /* Check for duplicates in the final register map */
405 626817 SwsCompMask seen = 0;
406
2/2
✓ Branch 0 taken 2308120 times.
✓ Branch 1 taken 524485 times.
2832605 for (int i = 0; i < 4; i++) {
407
2/2
✓ Branch 0 taken 527826 times.
✓ Branch 1 taken 1780294 times.
2308120 if (!SWS_COMP_TEST(uop.mask, i))
408 527826 continue;
409 av_assert2(idx[i] >= 0); /* should be no tmp register */
410 1780294 const SwsCompMask bit = SWS_COMP(idx[i]);
411
2/2
✓ Branch 0 taken 102332 times.
✓ Branch 1 taken 1677962 times.
1780294 if (seen & bit) {
412 102332 uop.uop = SWS_UOP_COPY;
413 102332 break;
414 }
415 1677962 seen |= bit;
416 }
417
418 /* Add any extra unused components to the mask, to prevent generating
419 * duplicate uops like permute_xyz_txy_xyt and permute_xyzw_txy_xyt */
420
2/2
✓ Branch 0 taken 2507268 times.
✓ Branch 1 taken 626817 times.
3134085 for (int i = 0; i < 4; i++) {
421 2507268 const SwsCompMask bit = SWS_COMP(i);
422
4/4
✓ Branch 0 taken 829306 times.
✓ Branch 1 taken 1677962 times.
✓ Branch 2 taken 426980 times.
✓ Branch 3 taken 402326 times.
2507268 if (!(seen & bit) && idx[i] == i)
423 426980 uop.mask |= bit;
424 }
425
426 626817 return ff_sws_uop_list_append(ops, &uop);
427 }
428
429 648733 static int translate_dither_op(SwsUOpList *ops, SwsUOpFlags flags, const SwsOp *op)
430 {
431 648733 SwsUOp uop = {
432 648733 .type = op->type,
433 .uop = SWS_UOP_DITHER,
434 648733 .par.dither.size_log2 = op->dither.size_log2,
435 };
436
437
4/4
✓ Branch 0 taken 640205 times.
✓ Branch 1 taken 8528 times.
✓ Branch 2 taken 99056 times.
✓ Branch 3 taken 541149 times.
648733 if ((flags & SWS_UOP_FLAG_ADD) && op->dither.size_log2 == 0) {
438 /* Constant offset */
439 99056 const SwsPixel val = Q2PIXEL(op->dither.matrix[0]);
440 99056 uop.uop = SWS_UOP_ADD;
441
2/2
✓ Branch 0 taken 396224 times.
✓ Branch 1 taken 99056 times.
495280 for (int i = 0; i < 4; i++) {
442
4/4
✓ Branch 0 taken 250118 times.
✓ Branch 1 taken 146106 times.
✓ Branch 2 taken 1324 times.
✓ Branch 3 taken 248794 times.
396224 if (!SWS_OP_NEEDED(op, i) || op->dither.y_offset[i] < 0)
443 147430 continue;
444 248794 uop.mask |= SWS_COMP(i);
445 248794 uop.data.vec4[i] = val;
446 }
447
448 99056 return ff_sws_uop_list_append(ops, &uop);
449 }
450
451 549677 const int size = 1 << op->dither.size_log2;
452
2/2
✓ Branch 0 taken 2198708 times.
✓ Branch 1 taken 549677 times.
2748385 for (int i = 0; i < 4; i++) {
453
4/4
✓ Branch 0 taken 1546450 times.
✓ Branch 1 taken 652258 times.
✓ Branch 2 taken 41884 times.
✓ Branch 3 taken 1504566 times.
2198708 if (!SWS_OP_NEEDED(op, i) || op->dither.y_offset[i] < 0)
454 694142 continue;
455 1504566 const uint8_t off = op->dither.y_offset[i] & (size - 1);
456 1504566 uop.mask |= SWS_COMP(i);
457 1504566 uop.par.dither.y_offset[i] = off;
458 }
459
460 /* Allocate extra rows to allow over-reading for row offsets. Note that
461 * y_offset is currently never larger than 5, so the extra space needed
462 * for this over-allocation is bounded by 5 * size * sizeof(float),
463 * typically 320 bytes for a 16x16 dither matrix. */
464 549677 const int stride = size * sizeof(SwsPixel);
465 549677 const int num_rows = ff_sws_dither_height(&uop.par.dither);
466 549677 SwsPixel *matrix = uop.data.ptr = av_refstruct_allocz(num_rows * stride);
467
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 549677 times.
549677 if (!matrix)
468 ✗ return AVERROR(ENOMEM);
469
470
2/2
✓ Branch 0 taken 140717312 times.
✓ Branch 1 taken 549677 times.
141266989 for (int i = 0; i < size * size; i++)
471 140717312 matrix[i] = Q2PIXEL(op->dither.matrix[i]);
472 549677 memcpy(&matrix[size * size], matrix, (num_rows - size) * stride);
473
474 549677 return ff_sws_uop_list_append(ops, &uop);
475 }
476
477 789148 static int translate_linear_op(SwsContext *ctx, SwsUOpList *ops,
478 SwsUOpFlags flags, const SwsOp *op,
479 const SwsComps *input)
480 {
481 789148 SwsUOp uop = {
482 789148 .type = op->type,
483 .uop = SWS_UOP_LINEAR,
484 };
485
486 789148 const uint32_t mask = ff_sws_linear_mask(&op->lin);
487 789148 const bool bitexact = ctx->flags & SWS_BITEXACT;
488 789148 uint32_t exact = 0;
489
490
2/2
✓ Branch 0 taken 3156592 times.
✓ Branch 1 taken 789148 times.
3945740 for (int i = 0; i < 4; i++) {
491
4/4
✓ Branch 0 taken 2212733 times.
✓ Branch 1 taken 943859 times.
✓ Branch 2 taken 91445 times.
✓ Branch 3 taken 2121288 times.
3156592 if (!SWS_OP_NEEDED(op, i) || !(mask & SWS_MASK_ROW(i))) {
492 1035304 uop.par.lin.zero |= SWS_MASK_ROW(i);
493 1035304 continue;
494 }
495 2121288 uop.mask |= SWS_COMP(i);
496 2121288 bool nonzero = (op->lin.m[i][4].num != 0);
497
2/2
✓ Branch 0 taken 10606440 times.
✓ Branch 1 taken 2121288 times.
12727728 for (int j = 0; j < 5; j++) {
498 10606440 const AVRational64 k = op->lin.m[i][j];
499 10606440 const SwsPixel px = Q2PIXEL(k);
500 10606440 uop.data.mat4x5[i][j] = px;
501
2/2
✓ Branch 0 taken 4836575 times.
✓ Branch 1 taken 5769865 times.
10606440 if (k.num == 0)
502 4836575 uop.par.lin.zero |= SWS_MASK(i, j);
503
4/4
✓ Branch 0 taken 4423732 times.
✓ Branch 1 taken 1346133 times.
✓ Branch 2 taken 1518 times.
✓ Branch 3 taken 4422214 times.
5769865 else if (j < 4 && k.num == k.den)
504 1518 uop.par.lin.one |= SWS_MASK(i, j);
505
8/8
✓ Branch 0 taken 4422214 times.
✓ Branch 1 taken 1346133 times.
✓ Branch 2 taken 3647059 times.
✓ Branch 3 taken 775155 times.
✓ Branch 4 taken 897709 times.
✓ Branch 5 taken 2749350 times.
✓ Branch 7 taken 797903 times.
✓ Branch 8 taken 99806 times.
5768347 else if (j < 4 && nonzero && (!bitexact || exact_prod(uop.type, px, input, j)))
506 3547253 exact |= SWS_MASK(i, j);
507
2/2
✓ Branch 0 taken 5769865 times.
✓ Branch 1 taken 4836575 times.
10606440 if (k.num != 0)
508 5769865 nonzero = true;
509 }
510 }
511
512
4/4
✓ Branch 0 taken 381367 times.
✓ Branch 1 taken 407781 times.
✓ Branch 2 taken 378865 times.
✓ Branch 3 taken 2502 times.
789148 if ((flags & SWS_UOP_FLAG_FMA) && !ff_sws_pixel_type_is_int(op->type)) {
513 /* multiplication by 1 and 0 are always exact by definition */
514 378865 uop.uop = SWS_UOP_LINEAR_FMA;
515 378865 uop.par.lin.exact = exact | uop.par.lin.zero | uop.par.lin.one;
516 }
517
518 789148 return ff_sws_uop_list_append(ops, &uop);
519 }
520
521 11773715 static int translate_op(SwsContext *ctx, SwsUOpList *uops, SwsUOpFlags flags,
522 const SwsOp *op, const SwsComps *input)
523 {
524
6/6
✓ Branch 0 taken 469497 times.
✓ Branch 1 taken 4218692 times.
✓ Branch 2 taken 626817 times.
✓ Branch 3 taken 648733 times.
✓ Branch 4 taken 789148 times.
✓ Branch 5 taken 5020828 times.
11773715 switch (op->op) {
525 469497 case SWS_OP_FILTER_H:
526 case SWS_OP_FILTER_V:
527 469497 return AVERROR(ENOTSUP); /* always handled by subpass splitting */
528 4218692 case SWS_OP_READ:
529 case SWS_OP_WRITE:
530 4218692 return translate_rw_op(ctx, uops, flags, op);
531 626817 case SWS_OP_SWIZZLE:
532 626817 return translate_swizzle(uops, op);
533 648733 case SWS_OP_DITHER:
534 648733 return translate_dither_op(uops, flags, op);
535 789148 case SWS_OP_LINEAR:
536 789148 return translate_linear_op(ctx, uops, flags, op, input);
537 5020828 default:
538 5020828 break;
539 }
540
541 /* Default handling for "simple" ops */
542 10041656 SwsUOp uop = {
543 5020828 .type = op->type,
544 .uop = SWS_UOP_INVALID,
545 5020828 .mask = ff_sws_comp_mask_needed(op),
546 };
547
548
8/9
✓ Branch 0 taken 1319407 times.
✓ Branch 1 taken 482790 times.
✓ Branch 2 taken 189680 times.
✓ Branch 3 taken 342870 times.
✓ Branch 4 taken 449306 times.
✓ Branch 5 taken 1011111 times.
✓ Branch 6 taken 944704 times.
✓ Branch 7 taken 280960 times.
✗ Branch 8 not taken.
5020828 switch (op->op) {
549 1319407 case SWS_OP_CONVERT:
550
4/5
✓ Branch 0 taken 338205 times.
✓ Branch 1 taken 687159 times.
✓ Branch 2 taken 61272 times.
✓ Branch 3 taken 232771 times.
✗ Branch 4 not taken.
1319407 switch (op->convert.to) {
551 338205 case SWS_PIXEL_U8: uop.uop = SWS_UOP_TO_U8; break;
552 687159 case SWS_PIXEL_U16: uop.uop = SWS_UOP_TO_U16; break;
553 61272 case SWS_PIXEL_U32: uop.uop = SWS_UOP_TO_U32; break;
554 232771 case SWS_PIXEL_F32: uop.uop = SWS_UOP_TO_F32; break;
555 }
556 1319407 break;
557 482790 case SWS_OP_UNPACK:
558 case SWS_OP_PACK:
559
2/2
✓ Branch 0 taken 199680 times.
✓ Branch 1 taken 283110 times.
482790 uop.uop = op->op == SWS_OP_PACK ? SWS_UOP_PACK : SWS_UOP_UNPACK;
560 482790 uop.mask = 0;
561
4/4
✓ Branch 0 taken 1931160 times.
✓ Branch 1 taken 150248 times.
✓ Branch 2 taken 1598618 times.
✓ Branch 3 taken 332542 times.
2081408 for (int i = 0; i < 4 && op->pack.pattern[i]; i++) {
562 1598618 uop.par.pack.pattern[i] = op->pack.pattern[i];
563
4/4
✓ Branch 0 taken 937934 times.
✓ Branch 1 taken 660684 times.
✓ Branch 2 taken 839838 times.
✓ Branch 3 taken 98096 times.
1598618 if (op->op == SWS_OP_PACK || SWS_OP_NEEDED(op, i))
564 1500522 uop.mask |= SWS_COMP(i);
565 }
566 482790 break;
567 189680 case SWS_OP_LSHIFT:
568 case SWS_OP_RSHIFT:
569
2/2
✓ Branch 0 taken 82215 times.
✓ Branch 1 taken 107465 times.
189680 uop.uop = op->op == SWS_OP_LSHIFT ? SWS_UOP_LSHIFT : SWS_UOP_RSHIFT;
570 189680 uop.par.shift.amount = op->shift.amount;
571 189680 break;
572 342870 case SWS_OP_CLEAR:
573 342870 uop.uop = SWS_UOP_CLEAR;
574 342870 uop.type = pixel_type_to_int(op->type);
575 342870 uop.mask &= op->clear.mask;
576
2/2
✓ Branch 0 taken 1371480 times.
✓ Branch 1 taken 342870 times.
1714350 for (int i = 0; i < 4; i++) {
577
2/2
✓ Branch 0 taken 979688 times.
✓ Branch 1 taken 391792 times.
1371480 if (!SWS_COMP_TEST(op->clear.mask, i))
578 979688 continue;
579 391792 const AVRational64 v = op->clear.value[i];
580 391792 const SwsPixel px = Q2PIXEL(op->clear.value[i]);
581 391792 uop.data.vec4[i] = px;
582
2/2
✓ Branch 0 taken 69240 times.
✓ Branch 1 taken 322552 times.
391792 if (v.num == 0)
583 69240 uop.par.clear.zero |= SWS_COMP(i);
584
2/2
✓ Branch 1 taken 84684 times.
✓ Branch 2 taken 237868 times.
322552 else if (ff_sws_pixel_is_1s(op->type, px))
585 84684 uop.par.clear.one |= SWS_COMP(i);
586 }
587 342870 break;
588 449306 case SWS_OP_SCALE:
589 449306 uop.uop = SWS_UOP_SCALE;
590 449306 uop.data.scalar = Q2PIXEL(op->scale.factor);
591 449306 break;
592 1011111 case SWS_OP_MIN:
593 case SWS_OP_MAX:
594
2/2
✓ Branch 0 taken 679368 times.
✓ Branch 1 taken 331743 times.
1011111 uop.uop = op->op == SWS_OP_MIN ? SWS_UOP_MIN : SWS_UOP_MAX;
595 1011111 uop.mask &= ff_sws_comp_mask_q4(op->clamp.limit);
596
2/2
✓ Branch 0 taken 4044444 times.
✓ Branch 1 taken 1011111 times.
5055555 for (int i = 0; i < 4; i++) {
597
2/2
✓ Branch 0 taken 2653088 times.
✓ Branch 1 taken 1391356 times.
4044444 if (SWS_COMP_TEST(uop.mask, i))
598 2653088 uop.data.vec4[i] = Q2PIXEL(op->clamp.limit[i]);
599 }
600 1011111 break;
601 944704 case SWS_OP_SWAP_BYTES:
602 944704 uop.uop = SWS_UOP_SWAP_BYTES;
603 944704 uop.type = pixel_type_to_int(op->type);
604 944704 break;
605 280960 case SWS_OP_LUT_3D:
606 280960 uop.uop = SWS_UOP_LUT_3D;
607 280960 uop.par.lut3d.dynamic = op->lut3d.dynamic;
608 280960 uop.data.lut3d = av_refstruct_ref_c(op->lut3d.lut);
609 280960 break;
610 ✗ default:
611 ✗ return AVERROR(ENOTSUP);
612 }
613
614
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5020828 times.
5020828 av_assert0(uop.uop != SWS_UOP_INVALID);
615 5020828 return ff_sws_uop_list_append(uops, &uop);
616 }
617
618 2388903 int ff_sws_ops_translate(SwsContext *ctx, const SwsOpList *ops,
619 SwsUOpFlags flags, SwsUOpList *uops)
620 {
621 2388903 SwsComps input = ops->comps_src;
622
2/2
✓ Branch 0 taken 11773715 times.
✓ Branch 1 taken 1919274 times.
13692989 for (int i = 0; i < ops->num_ops; i++) {
623 11773715 const SwsOp *op = &ops->ops[i];
624 11773715 const int pixel_size = ff_sws_pixel_type_size(op->type);
625
2/2
✓ Branch 0 taken 3411564 times.
✓ Branch 1 taken 8362151 times.
11773715 if (pixel_size > uops->pixel_size_max)
626 3411564 uops->pixel_size_max = pixel_size;
627
628 11773715 int ret = translate_op(ctx, uops, flags, op, &input);
629
2/2
✓ Branch 0 taken 469629 times.
✓ Branch 1 taken 11304086 times.
11773715 if (ret < 0)
630 469629 return ret;
631 11304086 input = ops->ops[i].comps;
632 }
633
634 1919274 return ff_sws_uop_list_optimize(ctx, flags, uops);
635 }
636