/src/ffmpeg/libswscale/uops.c
Line | Count | 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 | | int ff_sws_uop_cmp(const SwsUOp *a, const SwsUOp *b) |
32 | 0 | { |
33 | 0 | if (a->type != b->type) |
34 | 0 | return (int) a->type - b->type; |
35 | 0 | if (a->uop != b->uop) |
36 | 0 | return (int) a->uop - b->uop; |
37 | 0 | if (a->mask != b->mask) |
38 | 0 | return (int) a->mask - b->mask; |
39 | 0 | return memcmp(&a->par, &b->par, sizeof(a->par)); |
40 | 0 | } |
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 | | static SwsPixel pixel_from_q64(SwsPixelType type, AVRational64 val) |
51 | 0 | { |
52 | 0 | av_assert1(val.den != 0); |
53 | 0 | switch (type) { |
54 | 0 | case SWS_PIXEL_U8: return (SwsPixel) { .u8 = val.num / val.den }; |
55 | 0 | case SWS_PIXEL_U16: return (SwsPixel) { .u16 = val.num / val.den }; |
56 | 0 | case SWS_PIXEL_U32: return (SwsPixel) { .u32 = val.num / val.den }; |
57 | 0 | case SWS_PIXEL_F32: return (SwsPixel) { .f32 = (float) val.num / val.den }; |
58 | 0 | case SWS_PIXEL_NONE: |
59 | 0 | case SWS_PIXEL_TYPE_NB: break; |
60 | 0 | } |
61 | | |
62 | 0 | av_unreachable("Invalid pixel type!"); |
63 | 0 | return (SwsPixel) {0}; |
64 | 0 | } |
65 | | |
66 | 0 | #define Q2PIXEL(val) pixel_from_q64(op->type, val) |
67 | | |
68 | | static bool pixel_is_1s(SwsPixelType type, SwsPixel val) |
69 | 0 | { |
70 | 0 | switch (ff_sws_pixel_type_size(type)) { |
71 | 0 | case 1: return val.u8 == UINT8_MAX; |
72 | 0 | case 2: return val.u16 == UINT16_MAX; |
73 | 0 | case 4: return val.u32 == UINT32_MAX; |
74 | 0 | default: break; |
75 | 0 | } |
76 | | |
77 | 0 | av_unreachable("Invalid pixel type!"); |
78 | 0 | return false; |
79 | 0 | } |
80 | | |
81 | | void ff_sws_uop_name(const SwsUOp *op, char buf[SWS_UOP_NAME_MAX]) |
82 | 0 | { |
83 | 0 | AVBPrint bp; |
84 | 0 | av_bprint_init_for_buffer(&bp, buf, SWS_UOP_NAME_MAX); |
85 | |
|
86 | 0 | if (op->type != SWS_PIXEL_NONE) |
87 | 0 | av_bprintf(&bp, "%s_", ff_sws_pixel_type_name(op->type)); |
88 | 0 | av_bprintf(&bp, "%s", uop_names[op->uop].abbr); |
89 | |
|
90 | 0 | if (op->mask) |
91 | 0 | av_bprintf(&bp, "_%s", ff_sws_comp_mask_str(op->mask)); |
92 | |
|
93 | 0 | const SwsUOpParams *par = &op->par; |
94 | 0 | switch (op->uop) { |
95 | 0 | case SWS_UOP_READ_PLANAR_FH: |
96 | 0 | case SWS_UOP_READ_PLANAR_FV: |
97 | 0 | case SWS_UOP_READ_PLANAR_FV_FMA: |
98 | 0 | av_bprintf(&bp, "_%s", ff_sws_pixel_type_name(par->filter.type)); |
99 | 0 | break; |
100 | 0 | case SWS_UOP_LSHIFT: |
101 | 0 | case SWS_UOP_RSHIFT: |
102 | 0 | av_bprintf(&bp, "_%u", par->shift.amount); |
103 | 0 | break; |
104 | 0 | case SWS_UOP_PERMUTE: |
105 | 0 | case SWS_UOP_COPY: |
106 | 0 | av_bprint_chars(&bp, '_', 1); |
107 | 0 | for (int i = 0; i < par->move.num_moves; i++) |
108 | 0 | av_bprint_chars(&bp, "txyzw"[par->move.dst[i] + 1], 1); |
109 | 0 | av_bprint_chars(&bp, '_', 1); |
110 | 0 | for (int i = 0; i < par->move.num_moves; i++) |
111 | 0 | av_bprint_chars(&bp, "txyzw"[par->move.src[i] + 1], 1); |
112 | 0 | break; |
113 | 0 | case SWS_UOP_PACK: |
114 | 0 | case SWS_UOP_UNPACK: |
115 | 0 | av_bprint_chars(&bp, '_', 1); |
116 | 0 | for (int i = 0; i < 4 && par->pack.pattern[i]; i++) |
117 | 0 | av_bprintf(&bp, "%x", par->pack.pattern[i]); |
118 | 0 | break; |
119 | 0 | case SWS_UOP_CLEAR: |
120 | 0 | av_bprint_chars(&bp, '_', 1); |
121 | 0 | for (int i = 0; i < 4; i++) { |
122 | 0 | if (!SWS_COMP_TEST(op->mask, i)) |
123 | 0 | continue; |
124 | 0 | else if (SWS_COMP_TEST(par->clear.one, i)) |
125 | 0 | av_bprint_chars(&bp, '1', 1); |
126 | 0 | else if (SWS_COMP_TEST(par->clear.zero, i)) |
127 | 0 | av_bprint_chars(&bp, '0', 1); |
128 | 0 | else |
129 | 0 | av_bprint_chars(&bp, 'x', 1); |
130 | 0 | } |
131 | 0 | break; |
132 | 0 | case SWS_UOP_LINEAR: |
133 | 0 | case SWS_UOP_LINEAR_FMA: |
134 | 0 | for (int i = 0; i < 4; i++) { |
135 | 0 | if (!SWS_COMP_TEST(op->mask, i)) |
136 | 0 | continue; |
137 | 0 | av_bprint_chars(&bp, '_', 1); |
138 | 0 | for (int j = 0; j < 5; j++) { |
139 | 0 | if (par->lin.one & SWS_MASK(i, j)) |
140 | 0 | av_bprint_chars(&bp, '1', 1); |
141 | 0 | else if (par->lin.zero & SWS_MASK(i, j)) |
142 | 0 | av_bprint_chars(&bp, '0', 1); |
143 | 0 | else if (par->lin.exact & SWS_MASK(i, j)) |
144 | 0 | av_bprint_chars(&bp, 'X', 1); |
145 | 0 | else |
146 | 0 | av_bprint_chars(&bp, 'x', 1); |
147 | 0 | } |
148 | 0 | } |
149 | 0 | break; |
150 | 0 | case SWS_UOP_DITHER: |
151 | 0 | for (int i = 0; i < 4; i++) { |
152 | 0 | if (SWS_COMP_TEST(op->mask, i)) |
153 | 0 | av_bprintf(&bp, "_%d", par->dither.y_offset[i]); |
154 | 0 | } |
155 | 0 | const unsigned size = 1u << par->dither.size_log2; |
156 | 0 | av_bprintf(&bp, "_%ux%u", size, size); |
157 | 0 | break; |
158 | 0 | } |
159 | | |
160 | 0 | av_assert0(av_bprint_is_complete(&bp)); |
161 | 0 | } |
162 | | |
163 | | static void uop_uninit(SwsUOp *uop) |
164 | 0 | { |
165 | 0 | switch (uop->uop) { |
166 | 0 | case SWS_UOP_DITHER: |
167 | 0 | av_refstruct_unref(&uop->data.ptr); |
168 | 0 | break; |
169 | 0 | case SWS_UOP_READ_PLANAR_FH: |
170 | 0 | case SWS_UOP_READ_PLANAR_FV: |
171 | 0 | case SWS_UOP_READ_PLANAR_FV_FMA: |
172 | 0 | av_refstruct_unref(&uop->data.kernel); |
173 | 0 | break; |
174 | 0 | } |
175 | | |
176 | 0 | *uop = (SwsUOp) {0}; |
177 | 0 | } |
178 | | |
179 | | void ff_sws_uop_list_free(SwsUOpList **p_ops) |
180 | 0 | { |
181 | 0 | SwsUOpList *ops = *p_ops; |
182 | 0 | if (!ops) |
183 | 0 | return; |
184 | | |
185 | 0 | for (int i = 0; i < ops->num_ops; i++) |
186 | 0 | uop_uninit(&ops->ops[i]); |
187 | |
|
188 | 0 | av_freep(&ops->ops); |
189 | 0 | av_free(ops); |
190 | 0 | *p_ops = NULL; |
191 | 0 | } |
192 | | |
193 | | SwsUOpList *ff_sws_uop_list_alloc(void) |
194 | 0 | { |
195 | 0 | return av_mallocz(sizeof(SwsUOpList)); |
196 | 0 | } |
197 | | |
198 | | int ff_sws_uop_list_append(SwsUOpList *uops, SwsUOp *uop) |
199 | 0 | { |
200 | 0 | if (!av_dynarray2_add((void **) &uops->ops, &uops->num_ops, |
201 | 0 | sizeof(*uop), (uint8_t *) uop)) |
202 | 0 | { |
203 | 0 | uop_uninit(uop); |
204 | 0 | return AVERROR(ENOMEM); |
205 | 0 | } |
206 | | |
207 | 0 | *uop = (SwsUOp) {0}; |
208 | 0 | return 0; |
209 | 0 | } |
210 | | |
211 | | int ff_sws_dither_height(const SwsDitherUOp *dither) |
212 | 0 | { |
213 | 0 | int max_offset = 0; |
214 | 0 | for (int i = 0; i < 4; i++) |
215 | 0 | max_offset = FFMAX(max_offset, dither->y_offset[i]); |
216 | 0 | return (1 << dither->size_log2) + max_offset; |
217 | 0 | } |
218 | | |
219 | | static SwsPixelType pixel_type_to_int(const SwsPixelType type) |
220 | 0 | { |
221 | 0 | switch (ff_sws_pixel_type_size(type)) { |
222 | 0 | case 1: return SWS_PIXEL_U8; |
223 | 0 | case 2: return SWS_PIXEL_U16; |
224 | 0 | case 4: return SWS_PIXEL_U32; |
225 | 0 | default: break; |
226 | 0 | } |
227 | | |
228 | 0 | av_unreachable("Invalid pixel type!"); |
229 | 0 | return SWS_PIXEL_NONE; |
230 | 0 | } |
231 | | |
232 | | static bool exact_product_f32(float a, float b) |
233 | 0 | { |
234 | 0 | volatile float prod = a * b; |
235 | 0 | volatile float result = b ? prod / b : 0.0f; |
236 | 0 | return !b || result == a; |
237 | 0 | } |
238 | | |
239 | | static bool exact_prod(SwsPixelType type, SwsPixel coef, |
240 | | const SwsComps *comps, int idx) |
241 | 0 | { |
242 | 0 | const AVRational64 minq = comps->min[idx]; |
243 | 0 | const AVRational64 maxq = comps->max[idx]; |
244 | 0 | if (ff_sws_pixel_type_is_int(type)) |
245 | 0 | return true; |
246 | 0 | else if (!minq.den || !maxq.den) |
247 | 0 | return false; /* unknown bounds */ |
248 | | |
249 | 0 | const SwsPixel min = pixel_from_q64(type, minq); |
250 | 0 | const SwsPixel max = pixel_from_q64(type, maxq); |
251 | 0 | switch (type) { |
252 | 0 | case SWS_PIXEL_F32: |
253 | 0 | return exact_product_f32(coef.f32, min.f32) && |
254 | 0 | exact_product_f32(coef.f32, max.f32); |
255 | 0 | } |
256 | | |
257 | 0 | av_unreachable("Invalid pixel type!"); |
258 | 0 | return false; |
259 | 0 | } |
260 | | |
261 | | static bool check_filter_fma(SwsContext *ctx, SwsUOpFlags flags, const SwsOp *op) |
262 | 0 | { |
263 | 0 | if (!(flags & SWS_UOP_FLAG_FMA)) |
264 | 0 | return false; |
265 | 0 | if (!(ctx->flags & SWS_BITEXACT)) |
266 | 0 | return true; |
267 | 0 | if (!ff_sws_pixel_type_is_int(op->type)) |
268 | 0 | return false; |
269 | | |
270 | 0 | const int bits = ff_sws_pixel_type_size(op->type) * 8; |
271 | 0 | const uint64_t max_val = UINT64_MAX >> (64 - bits); |
272 | | |
273 | | /* Maximum value representable losslessly as float. Note that this is |
274 | | * currently true only for U8, but that may change if we ever update the |
275 | | * value of SWS_FILTER_SCALE. */ |
276 | 0 | return max_val * SWS_FILTER_SCALE <= (1 << 22); |
277 | 0 | } |
278 | | |
279 | | static int translate_rw_op(SwsContext *ctx, SwsUOpList *ops, SwsUOpFlags flags, |
280 | | const SwsOp *op) |
281 | 0 | { |
282 | 0 | SwsUOp uop = { |
283 | 0 | .type = op->type, |
284 | 0 | .mask = SWS_COMP_MASK(op->rw.elems > 0, op->rw.elems > 1, |
285 | 0 | op->rw.elems > 2, op->rw.elems > 3), |
286 | 0 | }; |
287 | | |
288 | | /* Non-filtered reads don't care about the exact pixel contents */ |
289 | 0 | if (!op->rw.filter.op) |
290 | 0 | uop.type = pixel_type_to_int(op->type); |
291 | |
|
292 | 0 | const bool is_read = op->op == SWS_OP_READ; |
293 | 0 | if (op->rw.filter.op) { |
294 | 0 | if (op->op == SWS_OP_WRITE || op->rw.frac || op->rw.mode != SWS_RW_PLANAR) |
295 | 0 | return AVERROR(ENOTSUP); |
296 | 0 | uop.par.filter.type = op->rw.filter.type; |
297 | 0 | uop.data.kernel = av_refstruct_ref(op->rw.filter.kernel); |
298 | 0 | if (op->rw.filter.op == SWS_OP_FILTER_H) { |
299 | 0 | uop.uop = SWS_UOP_READ_PLANAR_FH; |
300 | 0 | } else if (check_filter_fma(ctx, flags, op)) { |
301 | 0 | uop.uop = SWS_UOP_READ_PLANAR_FV_FMA; |
302 | 0 | } else { |
303 | 0 | uop.uop = SWS_UOP_READ_PLANAR_FV; |
304 | 0 | } |
305 | 0 | } else if (op->rw.mode == SWS_RW_PACKED && op->rw.elems > 1) { |
306 | 0 | if (op->rw.frac) |
307 | 0 | return AVERROR(ENOTSUP); |
308 | 0 | uop.uop = is_read ? SWS_UOP_READ_PACKED : SWS_UOP_WRITE_PACKED; |
309 | 0 | } else if (op->rw.mode == SWS_RW_PALETTE) { |
310 | 0 | if (op->rw.frac || !is_read) |
311 | 0 | return AVERROR(ENOTSUP); |
312 | 0 | uop.uop = SWS_UOP_READ_PALETTE; |
313 | 0 | } else if (op->rw.frac == 3) { |
314 | 0 | uop.uop = is_read ? SWS_UOP_READ_BIT : SWS_UOP_WRITE_BIT; |
315 | 0 | } else if (op->rw.frac == 1) { |
316 | 0 | uop.uop = is_read ? SWS_UOP_READ_NIBBLE : SWS_UOP_WRITE_NIBBLE; |
317 | 0 | } else { |
318 | 0 | av_assert0(!op->rw.frac); |
319 | 0 | uop.uop = is_read ? SWS_UOP_READ_PLANAR : SWS_UOP_WRITE_PLANAR; |
320 | 0 | } |
321 | | |
322 | 0 | const int planes = ff_sws_rw_op_planes(op); |
323 | 0 | if (op->op == SWS_OP_READ) { |
324 | 0 | ops->planes_in |= SWS_COMP_ELEMS(planes); |
325 | 0 | } else { |
326 | 0 | ops->planes_out |= SWS_COMP_ELEMS(planes); |
327 | 0 | } |
328 | |
|
329 | 0 | return ff_sws_uop_list_append(ops, &uop); |
330 | 0 | } |
331 | | |
332 | | static int count_idx(const int *arr, size_t size, int val) |
333 | 0 | { |
334 | 0 | int num = 0; |
335 | 0 | for (size_t i = 0; i < size; i++) { |
336 | 0 | if (arr[i] == val) |
337 | 0 | num++; |
338 | 0 | } |
339 | |
|
340 | 0 | return num; |
341 | 0 | } |
342 | | |
343 | | static int translate_swizzle(SwsUOpList *ops, const SwsOp *op) |
344 | 0 | { |
345 | 0 | SwsUOp uop = { |
346 | 0 | .uop = SWS_UOP_PERMUTE, |
347 | 0 | .type = pixel_type_to_int(op->type), |
348 | 0 | .mask = ff_sws_comp_mask_needed(op), |
349 | 0 | }; |
350 | 0 | SwsMoveUOp *par = &uop.par.move; |
351 | | |
352 | | /* Mask of components that are not yet satisfied */ |
353 | 0 | SwsCompMask todo = uop.mask; |
354 | 0 | for (int i = 0; i < 4; i++) { |
355 | 0 | if (op->swizzle.in[i] == i) |
356 | 0 | todo &= ~SWS_COMP(i); |
357 | 0 | } |
358 | | |
359 | | /* Mask of components whose value is required for the final output */ |
360 | 0 | SwsCompMask needed = 0; |
361 | 0 | for (int i = 0; i < 4; i++) { |
362 | 0 | if (SWS_OP_NEEDED(op, i)) |
363 | 0 | needed |= SWS_COMP(op->swizzle.in[i]); |
364 | 0 | } |
365 | | |
366 | | /* Current mapping of registers to components */ |
367 | 0 | int idx[4 + 1] = { 0, 1, 2, 3, -1 }; /* +1 for tmp */ |
368 | | |
369 | | /* Decompose the swizzle mask into a series of register-register moves */ |
370 | 0 | while (todo) { |
371 | 0 | int dst = -1, src = -1; |
372 | | |
373 | | /* Find next unsatisfied dst <- src move that doesn't clobber a value */ |
374 | 0 | for (dst = 0; dst < 4; dst++) { |
375 | 0 | if (!SWS_COMP_TEST(todo, dst)) |
376 | 0 | continue; /* already satisfied */ |
377 | 0 | const int cur = idx[dst]; |
378 | 0 | if (count_idx(idx, FF_ARRAY_ELEMS(idx), cur) == 1 && SWS_COMP_TEST(needed, cur)) |
379 | 0 | continue; /* clobbers last remaining, still-needed value */ |
380 | 0 | for (src = 0; src < FF_ARRAY_ELEMS(idx); src++) { |
381 | 0 | if (idx[src] == op->swizzle.in[dst]) { |
382 | | /* Prevent read-after-write dependency. */ |
383 | 0 | if (par->num_moves > 0 && src == par->dst[par->num_moves - 1]) |
384 | 0 | src = par->src[par->num_moves - 1]; |
385 | 0 | break; |
386 | 0 | } |
387 | 0 | } |
388 | 0 | av_assert1(src < FF_ARRAY_ELEMS(idx)); |
389 | 0 | todo &= ~SWS_COMP(dst); |
390 | 0 | break; |
391 | 0 | } |
392 | |
|
393 | 0 | if (dst == 4) { |
394 | | /* Stuck in a cycle, break it by saving to the scratch register */ |
395 | 0 | dst = 4; |
396 | 0 | for (src = 0; src < 4; src++) { |
397 | 0 | if (SWS_COMP_TEST(todo, src)) { |
398 | 0 | needed &= ~SWS_COMP(idx[src]); |
399 | 0 | break; |
400 | 0 | } |
401 | 0 | } |
402 | 0 | av_assert1(src < 4); |
403 | 0 | } |
404 | |
|
405 | 0 | av_assert0(par->num_moves < SWS_UOP_MOVE_MAX); |
406 | 0 | par->dst[par->num_moves] = dst > 3 ? -1 : dst; |
407 | 0 | par->src[par->num_moves] = src > 3 ? -1 : src; |
408 | 0 | par->num_moves++; |
409 | 0 | idx[dst] = idx[src]; |
410 | 0 | } |
411 | | |
412 | | /* Check for duplicates in the final register map */ |
413 | 0 | SwsCompMask seen = 0; |
414 | 0 | for (int i = 0; i < 4; i++) { |
415 | 0 | if (!SWS_COMP_TEST(uop.mask, i)) |
416 | 0 | continue; |
417 | 0 | av_assert2(idx[i] >= 0); /* should be no tmp register */ |
418 | 0 | const SwsCompMask bit = SWS_COMP(idx[i]); |
419 | 0 | if (seen & bit) { |
420 | 0 | uop.uop = SWS_UOP_COPY; |
421 | 0 | break; |
422 | 0 | } |
423 | 0 | seen |= bit; |
424 | 0 | } |
425 | | |
426 | | /* Add any extra unused components to the mask, to prevent generating |
427 | | * duplicate uops like permute_xyz_txy_xyt and permute_xyzw_txy_xyt */ |
428 | 0 | for (int i = 0; i < 4; i++) { |
429 | 0 | const SwsCompMask bit = SWS_COMP(i); |
430 | 0 | if (!(seen & bit) && idx[i] == i) |
431 | 0 | uop.mask |= bit; |
432 | 0 | } |
433 | |
|
434 | 0 | return ff_sws_uop_list_append(ops, &uop); |
435 | 0 | } |
436 | | |
437 | | static int translate_dither_op(SwsUOpList *ops, const SwsOp *op) |
438 | 0 | { |
439 | 0 | SwsUOp uop = { |
440 | 0 | .type = op->type, |
441 | 0 | .uop = SWS_UOP_DITHER, |
442 | 0 | .par.dither.size_log2 = op->dither.size_log2, |
443 | 0 | }; |
444 | |
|
445 | 0 | if (op->dither.size_log2 == 0) { |
446 | | /* Constant offset */ |
447 | 0 | const SwsPixel val = Q2PIXEL(op->dither.matrix[0]); |
448 | 0 | uop.uop = SWS_UOP_ADD; |
449 | 0 | for (int i = 0; i < 4; i++) { |
450 | 0 | if (!SWS_OP_NEEDED(op, i) || op->dither.y_offset[i] < 0) |
451 | 0 | continue; |
452 | 0 | uop.mask |= SWS_COMP(i); |
453 | 0 | uop.data.vec4[i] = val; |
454 | 0 | } |
455 | |
|
456 | 0 | return ff_sws_uop_list_append(ops, &uop); |
457 | 0 | } |
458 | | |
459 | 0 | const int size = 1 << op->dither.size_log2; |
460 | 0 | for (int i = 0; i < 4; i++) { |
461 | 0 | if (!SWS_OP_NEEDED(op, i) || op->dither.y_offset[i] < 0) |
462 | 0 | continue; |
463 | 0 | const uint8_t off = op->dither.y_offset[i] & (size - 1); |
464 | 0 | uop.mask |= SWS_COMP(i); |
465 | 0 | uop.par.dither.y_offset[i] = off; |
466 | 0 | } |
467 | | |
468 | | /* Allocate extra rows to allow over-reading for row offsets. Note that |
469 | | * y_offset is currently never larger than 5, so the extra space needed |
470 | | * for this over-allocation is bounded by 5 * size * sizeof(float), |
471 | | * typically 320 bytes for a 16x16 dither matrix. */ |
472 | 0 | const int stride = size * sizeof(SwsPixel); |
473 | 0 | const int num_rows = ff_sws_dither_height(&uop.par.dither); |
474 | 0 | SwsPixel *matrix = uop.data.ptr = av_refstruct_allocz(num_rows * stride); |
475 | 0 | if (!matrix) |
476 | 0 | return AVERROR(ENOMEM); |
477 | | |
478 | 0 | for (int i = 0; i < size * size; i++) |
479 | 0 | matrix[i] = Q2PIXEL(op->dither.matrix[i]); |
480 | 0 | memcpy(&matrix[size * size], matrix, (num_rows - size) * stride); |
481 | |
|
482 | 0 | return ff_sws_uop_list_append(ops, &uop); |
483 | 0 | } |
484 | | |
485 | | static int translate_linear_op(SwsContext *ctx, SwsUOpList *ops, |
486 | | SwsUOpFlags flags, const SwsOp *op, |
487 | | const SwsComps *input) |
488 | 0 | { |
489 | 0 | SwsUOp uop = { |
490 | 0 | .type = op->type, |
491 | 0 | .uop = SWS_UOP_LINEAR, |
492 | 0 | }; |
493 | |
|
494 | 0 | const bool bitexact = ctx->flags & SWS_BITEXACT; |
495 | 0 | uint32_t exact = 0; |
496 | |
|
497 | 0 | for (int i = 0; i < 4; i++) { |
498 | 0 | if (SWS_OP_NEEDED(op, i) && (op->lin.mask & SWS_MASK_ROW(i))) |
499 | 0 | uop.mask |= SWS_COMP(i); |
500 | 0 | bool nonzero = (op->lin.m[i][4].num != 0); |
501 | 0 | for (int j = 0; j < 5; j++) { |
502 | 0 | const AVRational64 k = op->lin.m[i][j]; |
503 | 0 | const SwsPixel px = Q2PIXEL(k); |
504 | 0 | uop.data.mat4[i][j] = px; |
505 | 0 | if (k.num == 0) |
506 | 0 | uop.par.lin.zero |= SWS_MASK(i, j); |
507 | 0 | else if (j < 4 && k.num == k.den) |
508 | 0 | uop.par.lin.one |= SWS_MASK(i, j); |
509 | 0 | else if (j < 4 && nonzero && (!bitexact || exact_prod(uop.type, px, input, j))) |
510 | 0 | exact |= SWS_MASK(i, j); |
511 | 0 | if (k.num != 0) |
512 | 0 | nonzero = true; |
513 | 0 | } |
514 | 0 | } |
515 | |
|
516 | 0 | if (flags & SWS_UOP_FLAG_FMA) { |
517 | | /* multiplication by 1 and 0 are always exact by definition */ |
518 | 0 | uop.uop = SWS_UOP_LINEAR_FMA; |
519 | 0 | uop.par.lin.exact = exact | uop.par.lin.zero | uop.par.lin.one; |
520 | 0 | } |
521 | |
|
522 | 0 | return ff_sws_uop_list_append(ops, &uop); |
523 | 0 | } |
524 | | |
525 | | static bool is_expand_bit(SwsPixelType type, AVRational64 factor) |
526 | 0 | { |
527 | 0 | if (factor.den != 1) |
528 | 0 | return false; |
529 | | |
530 | 0 | switch (type) { |
531 | 0 | case SWS_PIXEL_U8: return factor.num == UINT8_MAX; |
532 | 0 | case SWS_PIXEL_U16: return factor.num == UINT16_MAX; |
533 | 0 | case SWS_PIXEL_U32: return factor.num == UINT32_MAX; |
534 | 0 | case SWS_PIXEL_F32: return false; |
535 | 0 | case SWS_PIXEL_NONE: |
536 | 0 | case SWS_PIXEL_TYPE_NB: break; |
537 | 0 | } |
538 | | |
539 | 0 | av_unreachable("Invalid pixel type!"); |
540 | 0 | return false; |
541 | 0 | } |
542 | | |
543 | | static int translate_op(SwsContext *ctx, SwsUOpList *uops, SwsUOpFlags flags, |
544 | | const SwsOp *op, const SwsComps *input) |
545 | 0 | { |
546 | 0 | switch (op->op) { |
547 | 0 | case SWS_OP_FILTER_H: |
548 | 0 | case SWS_OP_FILTER_V: |
549 | 0 | return AVERROR(ENOTSUP); /* always handled by subpass splitting */ |
550 | 0 | case SWS_OP_READ: |
551 | 0 | case SWS_OP_WRITE: |
552 | 0 | return translate_rw_op(ctx, uops, flags, op); |
553 | 0 | case SWS_OP_SWIZZLE: |
554 | 0 | return translate_swizzle(uops, op); |
555 | 0 | case SWS_OP_DITHER: |
556 | 0 | return translate_dither_op(uops, op); |
557 | 0 | case SWS_OP_LINEAR: |
558 | 0 | return translate_linear_op(ctx, uops, flags, op, input); |
559 | 0 | default: |
560 | 0 | break; |
561 | 0 | } |
562 | | |
563 | | /* Default handling for "simple" ops */ |
564 | 0 | SwsUOp uop = { |
565 | 0 | .type = op->type, |
566 | 0 | .uop = SWS_UOP_INVALID, |
567 | 0 | .mask = ff_sws_comp_mask_needed(op), |
568 | 0 | }; |
569 | |
|
570 | 0 | switch (op->op) { |
571 | 0 | case SWS_OP_CONVERT: |
572 | 0 | if (op->convert.expand) { |
573 | 0 | av_assert0(op->type == SWS_PIXEL_U8); |
574 | 0 | switch (op->convert.to) { |
575 | 0 | case SWS_PIXEL_U16: uop.uop = SWS_UOP_EXPAND_PAIR; break; |
576 | 0 | case SWS_PIXEL_U32: uop.uop = SWS_UOP_EXPAND_QUAD; break; |
577 | 0 | } |
578 | 0 | } else { |
579 | 0 | switch (op->convert.to) { |
580 | 0 | case SWS_PIXEL_U8: uop.uop = SWS_UOP_TO_U8; break; |
581 | 0 | case SWS_PIXEL_U16: uop.uop = SWS_UOP_TO_U16; break; |
582 | 0 | case SWS_PIXEL_U32: uop.uop = SWS_UOP_TO_U32; break; |
583 | 0 | case SWS_PIXEL_F32: uop.uop = SWS_UOP_TO_F32; break; |
584 | 0 | } |
585 | 0 | } |
586 | 0 | break; |
587 | 0 | case SWS_OP_UNPACK: |
588 | 0 | case SWS_OP_PACK: |
589 | 0 | uop.uop = op->op == SWS_OP_PACK ? SWS_UOP_PACK : SWS_UOP_UNPACK; |
590 | 0 | uop.mask = 0; |
591 | 0 | for (int i = 0; i < 4 && op->pack.pattern[i]; i++) { |
592 | 0 | uop.par.pack.pattern[i] = op->pack.pattern[i]; |
593 | 0 | uop.mask |= SWS_COMP(i); |
594 | 0 | } |
595 | 0 | break; |
596 | 0 | case SWS_OP_LSHIFT: |
597 | 0 | case SWS_OP_RSHIFT: |
598 | 0 | uop.uop = op->op == SWS_OP_LSHIFT ? SWS_UOP_LSHIFT : SWS_UOP_RSHIFT; |
599 | 0 | uop.par.shift.amount = op->shift.amount; |
600 | 0 | break; |
601 | 0 | case SWS_OP_CLEAR: |
602 | 0 | uop.uop = SWS_UOP_CLEAR; |
603 | 0 | uop.type = pixel_type_to_int(op->type); |
604 | 0 | uop.mask &= op->clear.mask; |
605 | 0 | for (int i = 0; i < 4; i++) { |
606 | 0 | if (!SWS_COMP_TEST(op->clear.mask, i)) |
607 | 0 | continue; |
608 | 0 | const AVRational64 v = op->clear.value[i]; |
609 | 0 | const SwsPixel px = Q2PIXEL(op->clear.value[i]); |
610 | 0 | uop.data.vec4[i] = px; |
611 | 0 | if (v.num == 0) |
612 | 0 | uop.par.clear.zero |= SWS_COMP(i); |
613 | 0 | else if (pixel_is_1s(op->type, px)) |
614 | 0 | uop.par.clear.one |= SWS_COMP(i); |
615 | 0 | } |
616 | 0 | break; |
617 | 0 | case SWS_OP_SCALE: |
618 | 0 | if (is_expand_bit(op->type, op->scale.factor)) { |
619 | 0 | uop.uop = SWS_UOP_EXPAND_BIT; |
620 | 0 | } else { |
621 | 0 | uop.uop = SWS_UOP_SCALE; |
622 | 0 | uop.data.scalar = Q2PIXEL(op->scale.factor); |
623 | 0 | } |
624 | 0 | break; |
625 | 0 | case SWS_OP_MIN: |
626 | 0 | case SWS_OP_MAX: |
627 | 0 | uop.uop = op->op == SWS_OP_MIN ? SWS_UOP_MIN : SWS_UOP_MAX; |
628 | 0 | uop.mask &= ff_sws_comp_mask_q4(op->clamp.limit); |
629 | 0 | for (int i = 0; i < 4; i++) { |
630 | 0 | if (SWS_COMP_TEST(uop.mask, i)) |
631 | 0 | uop.data.vec4[i] = Q2PIXEL(op->clamp.limit[i]); |
632 | 0 | } |
633 | 0 | break; |
634 | 0 | case SWS_OP_SWAP_BYTES: |
635 | 0 | uop.uop = SWS_UOP_SWAP_BYTES; |
636 | 0 | uop.type = pixel_type_to_int(op->type); |
637 | 0 | break; |
638 | 0 | default: |
639 | 0 | return AVERROR(ENOTSUP); |
640 | 0 | } |
641 | | |
642 | 0 | av_assert0(uop.uop != SWS_UOP_INVALID); |
643 | 0 | return ff_sws_uop_list_append(uops, &uop); |
644 | 0 | } |
645 | | |
646 | | int ff_sws_ops_translate(SwsContext *ctx, const SwsOpList *ops, |
647 | | SwsUOpFlags flags, SwsUOpList *uops) |
648 | 0 | { |
649 | 0 | SwsComps input = ops->comps_src; |
650 | 0 | for (int i = 0; i < ops->num_ops; i++) { |
651 | 0 | const SwsOp *op = &ops->ops[i]; |
652 | 0 | const int pixel_size = ff_sws_pixel_type_size(op->type); |
653 | 0 | if (pixel_size > uops->pixel_size_max) |
654 | 0 | uops->pixel_size_max = pixel_size; |
655 | |
|
656 | 0 | int ret = translate_op(ctx, uops, flags, op, &input); |
657 | 0 | if (ret < 0) |
658 | 0 | return ret; |
659 | 0 | input = ops->ops[i].comps; |
660 | 0 | } |
661 | 0 | return 0; |
662 | 0 | } |