FFmpeg
ops_impl_conv.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2026 Ramiro Polla
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 /**
22  * NOTE: This file is #include'd directly by both the NEON backend and
23  * the sws_ops_aarch64 tool.
24  */
25 
26 #include "libavutil/error.h"
27 #include "libavutil/rational.h"
28 #include "libswscale/ops.h"
29 
30 #include "ops_impl.h"
31 
32 static void swizzle_emit(SwsAArch64OpImplParams *out, uint8_t dst, uint8_t src)
33 {
34  int idx = out->par.move.num_moves++;
35  out->par.move.dst[idx] = dst;
36  out->par.move.src[idx] = src;
37 }
38 
40 {
41  SwsSwizzleOp swizzle = {
42  .in = {
43  op->swizzle.in[0],
44  op->swizzle.in[1],
45  op->swizzle.in[2],
46  op->swizzle.in[3],
47  }
48  };
49 
50  /* Compute used vectors (src and dst) */
51  uint8_t src_used[4] = { 0 };
52  bool done[4] = { true, true, true, true };
53  LOOP(out->mask, dst) {
54  uint8_t src = swizzle.in[dst];
55  src_used[src]++;
56  done[dst] = false;
57  }
58 
59  /* First perform unobstructed copies. */
60  for (bool progress = true; progress; ) {
61  progress = false;
62  for (int dst = 0; dst < 4; dst++) {
63  if (done[dst] || src_used[dst])
64  continue;
65  uint8_t src = swizzle.in[dst];
67  src_used[src]--;
68  done[dst] = true;
69  progress = true;
70  }
71  }
72 
73  /* Then swap and rotate remaining operations. */
74  for (int dst = 0; dst < 4; dst++) {
75  if (done[dst])
76  continue;
77 
78  swizzle_emit(out, -1, dst);
79 
80  uint8_t cur_dst = dst;
81  uint8_t src = swizzle.in[cur_dst];
82  while (src != dst) {
83  swizzle_emit(out, cur_dst, src);
84  done[cur_dst] = true;
85  cur_dst = src;
86  src = swizzle.in[cur_dst];
87  }
88 
89  swizzle_emit(out, cur_dst, -1);
90  done[cur_dst] = true;
91  }
92 }
93 
94 /**
95  * Convert SwsOp to a SwsAArch64OpImplParams. Read the comments regarding
96  * SwsAArch64OpImplParams in ops_impl.h for more information.
97  */
98 static int convert_to_aarch64_impl(SwsContext *ctx, const SwsOpList *ops, int n,
99  int block_size, SwsAArch64OpImplParams *out)
100 {
101  const SwsOp *op = &ops->ops[n];
102 
103  out->block_size = block_size;
104 
105  /**
106  * Most SwsOp work on fields described by SWS_OP_NEEDED().
107  * The few that don't will override this field later.
108  */
109  out->mask = 0;
110  for (int i = 0; i < 4; i++) {
111  if (SWS_OP_NEEDED(op, i))
112  out->mask |= SWS_COMP(i);
113  }
114 
115  out->type = op->type;
116 
117  /* Map SwsOpType to SwsUOpType */
118  switch (op->op) {
119  case SWS_OP_READ:
120  if (op->rw.filter.op)
121  return AVERROR(ENOTSUP);
122  /**
123  * The different types of read operations have been split into
124  * their own SwsUOpType to simplify the implementation.
125  */
126  if (op->rw.frac == 1)
127  out->uop = SWS_UOP_READ_NIBBLE;
128  else if (op->rw.frac == 3)
129  out->uop = SWS_UOP_READ_BIT;
130  else if (op->rw.mode == SWS_RW_PACKED && op->rw.elems > 1)
131  out->uop = SWS_UOP_READ_PACKED;
132  else if (op->rw.mode == SWS_RW_PACKED || op->rw.mode == SWS_RW_PLANAR)
133  out->uop = SWS_UOP_READ_PLANAR;
134  else
135  return AVERROR(ENOTSUP);
136  break;
137  case SWS_OP_WRITE:
138  if (op->rw.filter.op)
139  return AVERROR(ENOTSUP);
140  /**
141  * The different types of write operations have been split into
142  * their own SwsUOpType to simplify the implementation.
143  */
144  if (op->rw.frac == 1)
145  out->uop = SWS_UOP_WRITE_NIBBLE;
146  else if (op->rw.frac == 3)
147  out->uop = SWS_UOP_WRITE_BIT;
148  else if (op->rw.mode == SWS_RW_PACKED && op->rw.elems > 1)
149  out->uop = SWS_UOP_WRITE_PACKED;
150  else if (op->rw.mode == SWS_RW_PACKED || op->rw.mode == SWS_RW_PLANAR)
151  out->uop = SWS_UOP_WRITE_PLANAR;
152  else
153  return AVERROR(ENOTSUP);
154  break;
155  case SWS_OP_SWAP_BYTES: out->uop = SWS_UOP_SWAP_BYTES; break;
156  case SWS_OP_SWIZZLE: {
157  /**
158  * Detect whether copies are needed or if a simple permute is
159  * enough.
160  */
161  out->uop = SWS_UOP_PERMUTE;
162  SwsCompMask seen = 0;
163  LOOP(out->mask, i) {
164  uint8_t src = op->swizzle.in[i];
165  if (seen & SWS_COMP(src)) {
166  out->uop = SWS_UOP_COPY;
167  break;
168  }
169  seen |= SWS_COMP(src);
170  }
171  break;
172  }
173  case SWS_OP_UNPACK: out->uop = SWS_UOP_UNPACK; break;
174  case SWS_OP_PACK: out->uop = SWS_UOP_PACK; break;
175  case SWS_OP_LSHIFT: out->uop = SWS_UOP_LSHIFT; break;
176  case SWS_OP_RSHIFT: out->uop = SWS_UOP_RSHIFT; break;
177  case SWS_OP_CLEAR: out->uop = SWS_UOP_CLEAR; break;
178  case SWS_OP_CONVERT:
179  if (op->convert.expand) {
180  switch (op->convert.to) {
181  case SWS_PIXEL_U16: out->uop = SWS_UOP_EXPAND_PAIR; break;
182  case SWS_PIXEL_U32: out->uop = SWS_UOP_EXPAND_QUAD; break;
183  }
184  } else {
185  switch (op->convert.to) {
186  case SWS_PIXEL_U8: out->uop = SWS_UOP_TO_U8; break;
187  case SWS_PIXEL_U16: out->uop = SWS_UOP_TO_U16; break;
188  case SWS_PIXEL_U32: out->uop = SWS_UOP_TO_U32; break;
189  case SWS_PIXEL_F32: out->uop = SWS_UOP_TO_F32; break;
190  }
191  }
192  break;
193  case SWS_OP_MIN: out->uop = SWS_UOP_MIN; break;
194  case SWS_OP_MAX: out->uop = SWS_UOP_MAX; break;
195  case SWS_OP_SCALE: out->uop = SWS_UOP_SCALE; break;
196  case SWS_OP_LINEAR:
197  out->uop = (ctx->flags & SWS_BITEXACT)
200  break;
201  case SWS_OP_DITHER: out->uop = SWS_UOP_DITHER; break;
202  case SWS_OP_FILTER_H:
203  case SWS_OP_FILTER_V:
204  return AVERROR(ENOTSUP);
205  }
206 
207  switch (out->uop) {
208  case SWS_UOP_READ_BIT:
209  case SWS_UOP_READ_NIBBLE:
210  case SWS_UOP_READ_PACKED:
211  case SWS_UOP_READ_PLANAR:
212  case SWS_UOP_WRITE_BIT:
216  switch (op->rw.elems) {
217  case 1: out->mask = SWS_COMP_ELEMS(1); break;
218  case 2: out->mask = SWS_COMP_ELEMS(2); break;
219  case 3: out->mask = SWS_COMP_ELEMS(3); break;
220  case 4: out->mask = SWS_COMP_ELEMS(4); break;
221  };
222  break;
223  case SWS_UOP_PERMUTE:
224  case SWS_UOP_COPY:
225  /* Recompute mask taking identity swizzle into account */
226  out->mask = 0;
227  for (int i = 0; i < 4; i++) {
228  if (SWS_OP_NEEDED(op, i) && op->swizzle.in[i] != i)
229  out->mask |= SWS_COMP(i);
230  }
232  /* The element size and type don't matter. */
233  out->block_size = block_size * ff_sws_pixel_type_size(op->type);
234  out->type = SWS_PIXEL_U8;
235  break;
236  case SWS_UOP_UNPACK:
237  for (int i = 0; i < 4; i++)
238  out->par.pack.pattern[i] = op->pack.pattern[i];
239  break;
240  case SWS_UOP_PACK:
241  out->mask = 0;
242  for (int i = 0; i < 4 && op->pack.pattern[i]; i++)
243  out->mask |= SWS_COMP(i);
244  for (int i = 0; i < 4; i++)
245  out->par.pack.pattern[i] = op->pack.pattern[i];
246  break;
247  case SWS_UOP_LSHIFT:
248  case SWS_UOP_RSHIFT:
249  out->par.shift.amount = op->shift.amount;
250  break;
251  case SWS_UOP_CLEAR:
252  out->mask = 0;
253  for (int i = 0; i < 4; i++) {
254  if (op->clear.mask & SWS_COMP(i)) {
255  out->mask |= SWS_COMP(i);
256  if (op->clear.value[i].num == 0) {
257  out->par.clear.zero |= SWS_COMP(i);
258  } else {
259  uint32_t val = op->clear.value[i].num / op->clear.value[i].den;
260  if ((op->type == SWS_PIXEL_U8 && val == UINT8_MAX) ||
261  (op->type == SWS_PIXEL_U16 && val == UINT16_MAX) ||
262  (op->type == SWS_PIXEL_U32 && val == UINT32_MAX))
263  out->par.clear.one |= SWS_COMP(i);
264  }
265  }
266  }
267  break;
268  case SWS_UOP_LINEAR:
269  case SWS_UOP_LINEAR_FMA:
270  out->mask = 0;
271  for (int i = 0; i < 4; i++) {
272  if (!SWS_OP_NEEDED(op, i) || !(op->lin.mask & SWS_MASK_ROW(i))) {
273  for (int j = 0; j < 5; j++)
274  out->par.lin.zero |= SWS_MASK(i, j);
275  continue;
276  }
277  out->mask |= SWS_COMP(i);
278  for (int j = 0; j < 5; j++) {
279  const AVRational64 k = op->lin.m[i][j];
280  if (j < 4 && k.num == k.den)
281  out->par.lin.one |= SWS_MASK(i, j);
282  else if (k.num == 0)
283  out->par.lin.zero |= SWS_MASK(i, j);
284  }
285  }
286  break;
287  case SWS_UOP_DITHER:
288  out->mask = SWS_COMP_MASK(op->dither.y_offset[0] >= 0,
289  op->dither.y_offset[1] >= 0,
290  op->dither.y_offset[2] >= 0,
291  op->dither.y_offset[3] >= 0);
292  LOOP(out->mask, i) {
293  out->par.dither.y_offset[i] = op->dither.y_offset[i];
294  }
295  out->par.dither.size_log2 = op->dither.size_log2;
296  break;
297  }
298 
299  switch (out->uop) {
300  case SWS_UOP_READ_BIT:
301  case SWS_UOP_READ_NIBBLE:
302  case SWS_UOP_READ_PACKED:
303  case SWS_UOP_READ_PLANAR:
304  case SWS_UOP_WRITE_BIT:
308  case SWS_UOP_SWAP_BYTES:
309  case SWS_UOP_CLEAR:
310  /* Only the element size matters, not the type. */
311  if (out->type == SWS_PIXEL_F32)
312  out->type = SWS_PIXEL_U32;
313  break;
314  }
315 
316  return 0;
317 }
SWS_OP_READ
@ SWS_OP_READ
Definition: ops.h:39
SWS_UOP_SCALE
@ SWS_UOP_SCALE
Definition: uops.h:164
SWS_OP_SWIZZLE
@ SWS_OP_SWIZZLE
Definition: ops.h:42
AVERROR
Filter the word “frame” indicates either a video frame or a group of audio as stored in an AVFrame structure Format for each input and each output the list of supported formats For video that means pixel format For audio that means channel sample they are references to shared objects When the negotiation mechanism computes the intersection of the formats supported at each end of a all references to both lists are replaced with a reference to the intersection And when a single format is eventually chosen for a link amongst the remaining all references to the list are updated That means that if a filter requires that its input and output have the same format amongst a supported all it has to do is use a reference to the same list of formats query_formats can leave some formats unset and return AVERROR(EAGAIN) to cause the negotiation mechanism toagain later. That can be used by filters with complex requirements to use the format negotiated on one link to set the formats supported on another. Frame references ownership and permissions
SWS_OP_LSHIFT
@ SWS_OP_LSHIFT
Definition: ops.h:47
SWS_OP_UNPACK
@ SWS_OP_UNPACK
Definition: ops.h:45
SWS_RW_PLANAR
@ SWS_RW_PLANAR
Note: 1-component reads are either SWS_RW_PLANAR or SWS_RW_PACKED, depending on the underlying interp...
Definition: ops.h:100
out
static FILE * out
Definition: movenc.c:55
SWS_UOP_RSHIFT
@ SWS_UOP_RSHIFT
Definition: uops.h:173
SWS_OP_CLEAR
@ SWS_OP_CLEAR
Definition: ops.h:51
rational.h
ops_impl.h
ops.h
SWS_OP_DITHER
@ SWS_OP_DITHER
Definition: ops.h:59
SWS_BITEXACT
@ SWS_BITEXACT
Definition: swscale.h:178
SWS_UOP_LINEAR_FMA
@ SWS_UOP_LINEAR_FMA
Definition: uops.h:176
SWS_UOP_MAX
@ SWS_UOP_MAX
Definition: uops.h:167
SWS_COMP_MASK
#define SWS_COMP_MASK(X, Y, Z, W)
Definition: uops.h:101
SWS_UOP_LSHIFT
@ SWS_UOP_LSHIFT
Definition: uops.h:172
SWS_UOP_TO_U16
@ SWS_UOP_TO_U16
Definition: uops.h:159
SWS_UOP_PACK
@ SWS_UOP_PACK
Definition: uops.h:171
SWS_UOP_PERMUTE
@ SWS_UOP_PERMUTE
Definition: uops.h:150
SwsSwizzleOp
Definition: ops.h:141
val
static double val(void *priv, double ch)
Definition: aeval.c:77
SWS_COMP_ELEMS
#define SWS_COMP_ELEMS(N)
Definition: uops.h:99
SWS_UOP_COPY
@ SWS_UOP_COPY
Definition: uops.h:151
convert_swizzle_to_moves
static void convert_swizzle_to_moves(const SwsOp *op, SwsAArch64OpImplParams *out)
Definition: ops_impl_conv.c:39
SWS_RW_PACKED
@ SWS_RW_PACKED
Definition: ops.h:101
SWS_OP_SCALE
@ SWS_OP_SCALE
Definition: ops.h:55
SWS_UOP_WRITE_NIBBLE
@ SWS_UOP_WRITE_NIBBLE
Definition: uops.h:143
SWS_OP_NEEDED
#define SWS_OP_NEEDED(op, idx)
Definition: ops.h:237
AVFormatContext::flags
int flags
Flags modifying the (de)muxer behaviour.
Definition: avformat.h:1484
LOOP
#define LOOP(mask, idx)
Definition: ops_impl.h:56
SWS_UOP_WRITE_PLANAR
@ SWS_UOP_WRITE_PLANAR
Definition: uops.h:141
op
static int op(uint8_t **dst, const uint8_t *dst_end, GetByteContext *gb, int pixel, int count, int *x, int width, int linesize)
Perform decode operation.
Definition: anm.c:76
SWS_UOP_TO_F32
@ SWS_UOP_TO_F32
Definition: uops.h:161
SWS_UOP_MIN
@ SWS_UOP_MIN
Definition: uops.h:166
SWS_OP_MIN
@ SWS_OP_MIN
Definition: ops.h:53
ctx
static AVFormatContext * ctx
Definition: movenc.c:49
SwsCompMask
uint8_t SwsCompMask
Bit-mask of components.
Definition: uops.h:87
SWS_UOP_READ_PACKED
@ SWS_UOP_READ_PACKED
Definition: uops.h:136
SWS_OP_LINEAR
@ SWS_OP_LINEAR
Definition: ops.h:58
SWS_OP_FILTER_H
@ SWS_OP_FILTER_H
Definition: ops.h:62
ff_sws_pixel_type_size
static av_const int ff_sws_pixel_type_size(SwsPixelType type)
Definition: uops.h:49
SWS_OP_PACK
@ SWS_OP_PACK
Definition: ops.h:46
swizzle_emit
static void swizzle_emit(SwsAArch64OpImplParams *out, uint8_t dst, uint8_t src)
NOTE: This file is #include'd directly by both the NEON backend and the sws_ops_aarch64 tool.
Definition: ops_impl_conv.c:32
SWS_OP_FILTER_V
@ SWS_OP_FILTER_V
Definition: ops.h:63
SWS_UOP_READ_NIBBLE
@ SWS_UOP_READ_NIBBLE
Definition: uops.h:137
SWS_UOP_TO_U32
@ SWS_UOP_TO_U32
Definition: uops.h:160
error.h
SWS_UOP_WRITE_BIT
@ SWS_UOP_WRITE_BIT
Definition: uops.h:144
dst
uint8_t ptrdiff_t const uint8_t ptrdiff_t int intptr_t intptr_t int int16_t * dst
Definition: dsp.h:87
i
#define i(width, name, range_min, range_max)
Definition: cbs_h264.c:63
for
for(k=2;k<=8;++k)
Definition: h264pred_template.c:424
SWS_OP_RSHIFT
@ SWS_OP_RSHIFT
Definition: ops.h:48
AVRational64
64-bit Rational number (pair of numerator and denominator).
Definition: rational64.h:52
SWS_OP_WRITE
@ SWS_OP_WRITE
Definition: ops.h:40
SWS_UOP_UNPACK
@ SWS_UOP_UNPACK
Definition: uops.h:170
SWS_COMP
#define SWS_COMP(X)
Definition: uops.h:96
SWS_PIXEL_U32
@ SWS_PIXEL_U32
Definition: uops.h:42
SWS_MASK_ROW
#define SWS_MASK_ROW(I)
Definition: uops.h:231
SWS_UOP_TO_U8
@ SWS_UOP_TO_U8
Definition: uops.h:158
SWS_UOP_READ_PLANAR
@ SWS_UOP_READ_PLANAR
Definition: uops.h:132
SwsOpList::ops
SwsOp * ops
Definition: ops.h:266
SWS_PIXEL_U8
@ SWS_PIXEL_U8
Definition: uops.h:40
SWS_UOP_SWAP_BYTES
@ SWS_UOP_SWAP_BYTES
Definition: uops.h:154
SWS_UOP_LINEAR
@ SWS_UOP_LINEAR
Definition: uops.h:175
SwsOp
Definition: ops.h:210
AVRational64::den
int64_t den
Denominator.
Definition: rational64.h:54
SWS_OP_MAX
@ SWS_OP_MAX
Definition: ops.h:54
convert_to_aarch64_impl
static int convert_to_aarch64_impl(SwsContext *ctx, const SwsOpList *ops, int n, int block_size, SwsAArch64OpImplParams *out)
Convert SwsOp to a SwsAArch64OpImplParams.
Definition: ops_impl_conv.c:98
SWS_OP_SWAP_BYTES
@ SWS_OP_SWAP_BYTES
Definition: ops.h:41
SWS_UOP_DITHER
@ SWS_UOP_DITHER
Definition: uops.h:177
SWS_UOP_WRITE_PACKED
@ SWS_UOP_WRITE_PACKED
Definition: uops.h:142
SwsAArch64OpImplParams
SwsAArch64OpImplParams describes the parameters for an SwsUOpType operation.
Definition: ops_impl.h:47
SWS_PIXEL_F32
@ SWS_PIXEL_F32
Definition: uops.h:43
SWS_UOP_EXPAND_QUAD
@ SWS_UOP_EXPAND_QUAD
Definition: uops.h:157
AVRational64::num
int64_t num
Numerator.
Definition: rational64.h:53
SwsSwizzleOp::in
uint8_t in[4]
Definition: ops.h:148
SWS_OP_CONVERT
@ SWS_OP_CONVERT
Definition: ops.h:52
SWS_UOP_READ_BIT
@ SWS_UOP_READ_BIT
Definition: uops.h:138
SWS_UOP_CLEAR
@ SWS_UOP_CLEAR
Definition: uops.h:174
SwsOpList
Helper struct for representing a list of operations.
Definition: ops.h:265
SwsContext
Main external API structure.
Definition: swscale.h:227
SWS_PIXEL_U16
@ SWS_PIXEL_U16
Definition: uops.h:41
SWS_MASK
#define SWS_MASK(I, J)
Definition: uops.h:229
src
#define src
Definition: vp8dsp.c:248
SWS_UOP_EXPAND_PAIR
@ SWS_UOP_EXPAND_PAIR
Definition: uops.h:156