FFmpeg  4.4.8
vf_hqx.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2014 Clément Bœsch
3  *
4  * This file is part of FFmpeg.
5  *
6  * Permission to use, copy, modify, and/or distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 /**
20  * @file
21  * hqx magnification filters (hq2x, hq3x, hq4x)
22  *
23  * Originally designed by Maxim Stephin.
24  *
25  * @see http://en.wikipedia.org/wiki/Hqx
26  * @see http://web.archive.org/web/20131114143602/http://www.hiend3d.com/hq3x.html
27  * @see http://blog.pkh.me/p/19-butchering-hqx-scaling-filters.html
28  */
29 
30 #include "libavutil/opt.h"
31 #include "libavutil/avassert.h"
32 #include "libavutil/pixdesc.h"
33 #include "internal.h"
34 #include "filters.h"
35 
36 typedef int (*hqxfunc_t)(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs);
37 
38 typedef struct HQXContext {
39  const AVClass *class;
40  int n;
42  uint32_t rgbtoyuv[1<<24];
43 } HQXContext;
44 
45 typedef struct ThreadData {
46  AVFrame *in, *out;
47  const uint32_t *rgbtoyuv;
48 } ThreadData;
49 
50 #define OFFSET(x) offsetof(HQXContext, x)
51 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
52 static const AVOption hqx_options[] = {
53  { "n", "set scale factor", OFFSET(n), AV_OPT_TYPE_INT, {.i64 = 3}, 2, 4, .flags = FLAGS },
54  { NULL }
55 };
56 
58 
59 static av_always_inline uint32_t rgb2yuv(const uint32_t *r2y, uint32_t c)
60 {
61  return r2y[c & 0xffffff];
62 }
63 
64 static av_always_inline int yuv_diff(uint32_t yuv1, uint32_t yuv2)
65 {
66 #define YMASK 0xff0000
67 #define UMASK 0x00ff00
68 #define VMASK 0x0000ff
69 #define ABSDIFF(a,b) (abs((int)(a)-(int)(b)))
70 
71  return ABSDIFF(yuv1 & YMASK, yuv2 & YMASK) > (48 << 16) ||
72  ABSDIFF(yuv1 & UMASK, yuv2 & UMASK) > ( 7 << 8) ||
73  ABSDIFF(yuv1 & VMASK, yuv2 & VMASK) > ( 6 << 0);
74 }
75 
76 /* (c1*w1 + c2*w2) >> s */
77 static av_always_inline uint32_t interp_2px(uint32_t c1, int w1, uint32_t c2, int w2, int s)
78 {
79  return (((((c1 & 0xff00ff00) >> 8) * w1 + ((c2 & 0xff00ff00) >> 8) * w2) << (8 - s)) & 0xff00ff00) |
80  (((((c1 & 0x00ff00ff) ) * w1 + ((c2 & 0x00ff00ff) ) * w2) >> s ) & 0x00ff00ff);
81 }
82 
83 /* (c1*w1 + c2*w2 + c3*w3) >> s */
84 static av_always_inline uint32_t interp_3px(uint32_t c1, int w1, uint32_t c2, int w2, uint32_t c3, int w3, int s)
85 {
86  return (((((c1 & 0xff00ff00) >> 8) * w1 + ((c2 & 0xff00ff00) >> 8) * w2 + ((c3 & 0xff00ff00) >> 8) * w3) << (8 - s)) & 0xff00ff00) |
87  (((((c1 & 0x00ff00ff) ) * w1 + ((c2 & 0x00ff00ff) ) * w2 + ((c3 & 0x00ff00ff) ) * w3) >> s ) & 0x00ff00ff);
88 }
89 
90 /* m is the mask of diff with the center pixel that matters in the pattern, and
91  * r is the expected result (bit set to 1 if there is difference with the
92  * center, 0 otherwise) */
93 #define P(m, r) ((k_shuffled & (m)) == (r))
94 
95 /* adjust 012345678 to 01235678: the mask doesn't contain the (null) diff
96  * between the center/current pixel and itself */
97 #define DROP4(z) ((z) > 4 ? (z)-1 : (z))
98 
99 /* shuffle the input mask: move bit n (4-adjusted) to position stored in p<n> */
100 #define SHF(x, rot, n) (((x) >> ((rot) ? 7-DROP4(n) : DROP4(n)) & 1) << DROP4(p##n))
101 
102 /* used to check if there is YUV difference between 2 pixels */
103 #define WDIFF(c1, c2) yuv_diff(rgb2yuv(r2y, c1), rgb2yuv(r2y, c2))
104 
105 /* bootstrap template for every interpolation code. It defines the shuffled
106  * masks and surrounding pixels. The rot flag is used to indicate if it's a
107  * rotation; its basic effect is to shuffle k using p8..p0 instead of p0..p8 */
108 #define INTERP_BOOTSTRAP(rot) \
109  const int k_shuffled = SHF(k,rot,0) | SHF(k,rot,1) | SHF(k,rot,2) \
110  | SHF(k,rot,3) | 0 | SHF(k,rot,5) \
111  | SHF(k,rot,6) | SHF(k,rot,7) | SHF(k,rot,8); \
112  \
113  const uint32_t w0 = w[p0], w1 = w[p1], \
114  w3 = w[p3], w4 = w[p4], w5 = w[p5], \
115  w7 = w[p7]
116 
117 /* Assuming p0..p8 is mapped to pixels 0..8, this function interpolates the
118  * top-left pixel in the total of the 2x2 pixels to interpolates. The function
119  * is also used for the 3 other pixels */
120 static av_always_inline uint32_t hq2x_interp_1x1(const uint32_t *r2y, int k,
121  const uint32_t *w,
122  int p0, int p1, int p2,
123  int p3, int p4, int p5,
124  int p6, int p7, int p8)
125 {
126  INTERP_BOOTSTRAP(0);
127 
128  if ((P(0xbf,0x37) || P(0xdb,0x13)) && WDIFF(w1, w5))
129  return interp_2px(w4, 3, w3, 1, 2);
130  if ((P(0xdb,0x49) || P(0xef,0x6d)) && WDIFF(w7, w3))
131  return interp_2px(w4, 3, w1, 1, 2);
132  if ((P(0x0b,0x0b) || P(0xfe,0x4a) || P(0xfe,0x1a)) && WDIFF(w3, w1))
133  return w4;
134  if ((P(0x6f,0x2a) || P(0x5b,0x0a) || P(0xbf,0x3a) || P(0xdf,0x5a) ||
135  P(0x9f,0x8a) || P(0xcf,0x8a) || P(0xef,0x4e) || P(0x3f,0x0e) ||
136  P(0xfb,0x5a) || P(0xbb,0x8a) || P(0x7f,0x5a) || P(0xaf,0x8a) ||
137  P(0xeb,0x8a)) && WDIFF(w3, w1))
138  return interp_2px(w4, 3, w0, 1, 2);
139  if (P(0x0b,0x08))
140  return interp_3px(w4, 2, w0, 1, w1, 1, 2);
141  if (P(0x0b,0x02))
142  return interp_3px(w4, 2, w0, 1, w3, 1, 2);
143  if (P(0x2f,0x2f))
144  return interp_3px(w4, 14, w3, 1, w1, 1, 4);
145  if (P(0xbf,0x37) || P(0xdb,0x13))
146  return interp_3px(w4, 5, w1, 2, w3, 1, 3);
147  if (P(0xdb,0x49) || P(0xef,0x6d))
148  return interp_3px(w4, 5, w3, 2, w1, 1, 3);
149  if (P(0x1b,0x03) || P(0x4f,0x43) || P(0x8b,0x83) || P(0x6b,0x43))
150  return interp_2px(w4, 3, w3, 1, 2);
151  if (P(0x4b,0x09) || P(0x8b,0x89) || P(0x1f,0x19) || P(0x3b,0x19))
152  return interp_2px(w4, 3, w1, 1, 2);
153  if (P(0x7e,0x2a) || P(0xef,0xab) || P(0xbf,0x8f) || P(0x7e,0x0e))
154  return interp_3px(w4, 2, w3, 3, w1, 3, 3);
155  if (P(0xfb,0x6a) || P(0x6f,0x6e) || P(0x3f,0x3e) || P(0xfb,0xfa) ||
156  P(0xdf,0xde) || P(0xdf,0x1e))
157  return interp_2px(w4, 3, w0, 1, 2);
158  if (P(0x0a,0x00) || P(0x4f,0x4b) || P(0x9f,0x1b) || P(0x2f,0x0b) ||
159  P(0xbe,0x0a) || P(0xee,0x0a) || P(0x7e,0x0a) || P(0xeb,0x4b) ||
160  P(0x3b,0x1b))
161  return interp_3px(w4, 2, w3, 1, w1, 1, 2);
162  return interp_3px(w4, 6, w3, 1, w1, 1, 3);
163 }
164 
165 /* Assuming p0..p8 is mapped to pixels 0..8, this function interpolates the
166  * top-left and top-center pixel in the total of the 3x3 pixels to
167  * interpolates. The function is also used for the 3 other couples of pixels
168  * defining the outline. The center pixel is not defined through this function,
169  * since it's just the same as the original value. */
170 static av_always_inline void hq3x_interp_2x1(uint32_t *dst, int dst_linesize,
171  const uint32_t *r2y, int k,
172  const uint32_t *w,
173  int pos00, int pos01,
174  int p0, int p1, int p2,
175  int p3, int p4, int p5,
176  int p6, int p7, int p8,
177  int rotate)
178 {
180 
181  uint32_t *dst00 = &dst[dst_linesize*(pos00>>1) + (pos00&1)];
182  uint32_t *dst01 = &dst[dst_linesize*(pos01>>1) + (pos01&1)];
183 
184  if ((P(0xdb,0x49) || P(0xef,0x6d)) && WDIFF(w7, w3))
185  *dst00 = interp_2px(w4, 3, w1, 1, 2);
186  else if ((P(0xbf,0x37) || P(0xdb,0x13)) && WDIFF(w1, w5))
187  *dst00 = interp_2px(w4, 3, w3, 1, 2);
188  else if ((P(0x0b,0x0b) || P(0xfe,0x4a) || P(0xfe,0x1a)) && WDIFF(w3, w1))
189  *dst00 = w4;
190  else if ((P(0x6f,0x2a) || P(0x5b,0x0a) || P(0xbf,0x3a) || P(0xdf,0x5a) ||
191  P(0x9f,0x8a) || P(0xcf,0x8a) || P(0xef,0x4e) || P(0x3f,0x0e) ||
192  P(0xfb,0x5a) || P(0xbb,0x8a) || P(0x7f,0x5a) || P(0xaf,0x8a) ||
193  P(0xeb,0x8a)) && WDIFF(w3, w1))
194  *dst00 = interp_2px(w4, 3, w0, 1, 2);
195  else if (P(0x4b,0x09) || P(0x8b,0x89) || P(0x1f,0x19) || P(0x3b,0x19))
196  *dst00 = interp_2px(w4, 3, w1, 1, 2);
197  else if (P(0x1b,0x03) || P(0x4f,0x43) || P(0x8b,0x83) || P(0x6b,0x43))
198  *dst00 = interp_2px(w4, 3, w3, 1, 2);
199  else if (P(0x7e,0x2a) || P(0xef,0xab) || P(0xbf,0x8f) || P(0x7e,0x0e))
200  *dst00 = interp_2px(w3, 1, w1, 1, 1);
201  else if (P(0x4f,0x4b) || P(0x9f,0x1b) || P(0x2f,0x0b) || P(0xbe,0x0a) ||
202  P(0xee,0x0a) || P(0x7e,0x0a) || P(0xeb,0x4b) || P(0x3b,0x1b))
203  *dst00 = interp_3px(w4, 2, w3, 7, w1, 7, 4);
204  else if (P(0x0b,0x08) || P(0xf9,0x68) || P(0xf3,0x62) || P(0x6d,0x6c) ||
205  P(0x67,0x66) || P(0x3d,0x3c) || P(0x37,0x36) || P(0xf9,0xf8) ||
206  P(0xdd,0xdc) || P(0xf3,0xf2) || P(0xd7,0xd6) || P(0xdd,0x1c) ||
207  P(0xd7,0x16) || P(0x0b,0x02))
208  *dst00 = interp_2px(w4, 3, w0, 1, 2);
209  else
210  *dst00 = interp_3px(w4, 2, w3, 1, w1, 1, 2);
211 
212  if ((P(0xfe,0xde) || P(0x9e,0x16) || P(0xda,0x12) || P(0x17,0x16) ||
213  P(0x5b,0x12) || P(0xbb,0x12)) && WDIFF(w1, w5))
214  *dst01 = w4;
215  else if ((P(0x0f,0x0b) || P(0x5e,0x0a) || P(0xfb,0x7b) || P(0x3b,0x0b) ||
216  P(0xbe,0x0a) || P(0x7a,0x0a)) && WDIFF(w3, w1))
217  *dst01 = w4;
218  else if (P(0xbf,0x8f) || P(0x7e,0x0e) || P(0xbf,0x37) || P(0xdb,0x13))
219  *dst01 = interp_2px(w1, 3, w4, 1, 2);
220  else if (P(0x02,0x00) || P(0x7c,0x28) || P(0xed,0xa9) || P(0xf5,0xb4) ||
221  P(0xd9,0x90))
222  *dst01 = interp_2px(w4, 3, w1, 1, 2);
223  else if (P(0x4f,0x4b) || P(0xfb,0x7b) || P(0xfe,0x7e) || P(0x9f,0x1b) ||
224  P(0x2f,0x0b) || P(0xbe,0x0a) || P(0x7e,0x0a) || P(0xfb,0x4b) ||
225  P(0xfb,0xdb) || P(0xfe,0xde) || P(0xfe,0x56) || P(0x57,0x56) ||
226  P(0x97,0x16) || P(0x3f,0x1e) || P(0xdb,0x12) || P(0xbb,0x12))
227  *dst01 = interp_2px(w4, 7, w1, 1, 3);
228  else
229  *dst01 = w4;
230 }
231 
232 /* Assuming p0..p8 is mapped to pixels 0..8, this function interpolates the
233  * top-left block of 2x2 pixels in the total of the 4x4 pixels (or 4 blocks) to
234  * interpolates. The function is also used for the 3 other blocks of 2x2
235  * pixels. */
236 static av_always_inline void hq4x_interp_2x2(uint32_t *dst, int dst_linesize,
237  const uint32_t *r2y, int k,
238  const uint32_t *w,
239  int pos00, int pos01,
240  int pos10, int pos11,
241  int p0, int p1, int p2,
242  int p3, int p4, int p5,
243  int p6, int p7, int p8)
244 {
245  INTERP_BOOTSTRAP(0);
246 
247  uint32_t *dst00 = &dst[dst_linesize*(pos00>>1) + (pos00&1)];
248  uint32_t *dst01 = &dst[dst_linesize*(pos01>>1) + (pos01&1)];
249  uint32_t *dst10 = &dst[dst_linesize*(pos10>>1) + (pos10&1)];
250  uint32_t *dst11 = &dst[dst_linesize*(pos11>>1) + (pos11&1)];
251 
252  const int cond00 = (P(0xbf,0x37) || P(0xdb,0x13)) && WDIFF(w1, w5);
253  const int cond01 = (P(0xdb,0x49) || P(0xef,0x6d)) && WDIFF(w7, w3);
254  const int cond02 = (P(0x6f,0x2a) || P(0x5b,0x0a) || P(0xbf,0x3a) ||
255  P(0xdf,0x5a) || P(0x9f,0x8a) || P(0xcf,0x8a) ||
256  P(0xef,0x4e) || P(0x3f,0x0e) || P(0xfb,0x5a) ||
257  P(0xbb,0x8a) || P(0x7f,0x5a) || P(0xaf,0x8a) ||
258  P(0xeb,0x8a)) && WDIFF(w3, w1);
259  const int cond03 = P(0xdb,0x49) || P(0xef,0x6d);
260  const int cond04 = P(0xbf,0x37) || P(0xdb,0x13);
261  const int cond05 = P(0x1b,0x03) || P(0x4f,0x43) || P(0x8b,0x83) ||
262  P(0x6b,0x43);
263  const int cond06 = P(0x4b,0x09) || P(0x8b,0x89) || P(0x1f,0x19) ||
264  P(0x3b,0x19);
265  const int cond07 = P(0x0b,0x08) || P(0xf9,0x68) || P(0xf3,0x62) ||
266  P(0x6d,0x6c) || P(0x67,0x66) || P(0x3d,0x3c) ||
267  P(0x37,0x36) || P(0xf9,0xf8) || P(0xdd,0xdc) ||
268  P(0xf3,0xf2) || P(0xd7,0xd6) || P(0xdd,0x1c) ||
269  P(0xd7,0x16) || P(0x0b,0x02);
270  const int cond08 = (P(0x0f,0x0b) || P(0x2b,0x0b) || P(0xfe,0x4a) ||
271  P(0xfe,0x1a)) && WDIFF(w3, w1);
272  const int cond09 = P(0x2f,0x2f);
273  const int cond10 = P(0x0a,0x00);
274  const int cond11 = P(0x0b,0x09);
275  const int cond12 = P(0x7e,0x2a) || P(0xef,0xab);
276  const int cond13 = P(0xbf,0x8f) || P(0x7e,0x0e);
277  const int cond14 = P(0x4f,0x4b) || P(0x9f,0x1b) || P(0x2f,0x0b) ||
278  P(0xbe,0x0a) || P(0xee,0x0a) || P(0x7e,0x0a) ||
279  P(0xeb,0x4b) || P(0x3b,0x1b);
280  const int cond15 = P(0x0b,0x03);
281 
282  if (cond00)
283  *dst00 = interp_2px(w4, 5, w3, 3, 3);
284  else if (cond01)
285  *dst00 = interp_2px(w4, 5, w1, 3, 3);
286  else if ((P(0x0b,0x0b) || P(0xfe,0x4a) || P(0xfe,0x1a)) && WDIFF(w3, w1))
287  *dst00 = w4;
288  else if (cond02)
289  *dst00 = interp_2px(w4, 5, w0, 3, 3);
290  else if (cond03)
291  *dst00 = interp_2px(w4, 3, w3, 1, 2);
292  else if (cond04)
293  *dst00 = interp_2px(w4, 3, w1, 1, 2);
294  else if (cond05)
295  *dst00 = interp_2px(w4, 5, w3, 3, 3);
296  else if (cond06)
297  *dst00 = interp_2px(w4, 5, w1, 3, 3);
298  else if (P(0x0f,0x0b) || P(0x5e,0x0a) || P(0x2b,0x0b) || P(0xbe,0x0a) ||
299  P(0x7a,0x0a) || P(0xee,0x0a))
300  *dst00 = interp_2px(w1, 1, w3, 1, 1);
301  else if (cond07)
302  *dst00 = interp_2px(w4, 5, w0, 3, 3);
303  else
304  *dst00 = interp_3px(w4, 2, w1, 1, w3, 1, 2);
305 
306  if (cond00)
307  *dst01 = interp_2px(w4, 7, w3, 1, 3);
308  else if (cond08)
309  *dst01 = w4;
310  else if (cond02)
311  *dst01 = interp_2px(w4, 3, w0, 1, 2);
312  else if (cond09)
313  *dst01 = w4;
314  else if (cond10)
315  *dst01 = interp_3px(w4, 5, w1, 2, w3, 1, 3);
316  else if (P(0x0b,0x08))
317  *dst01 = interp_3px(w4, 5, w1, 2, w0, 1, 3);
318  else if (cond11)
319  *dst01 = interp_2px(w4, 5, w1, 3, 3);
320  else if (cond04)
321  *dst01 = interp_2px(w1, 3, w4, 1, 2);
322  else if (cond12)
323  *dst01 = interp_3px(w1, 2, w4, 1, w3, 1, 2);
324  else if (cond13)
325  *dst01 = interp_2px(w1, 5, w3, 3, 3);
326  else if (cond05)
327  *dst01 = interp_2px(w4, 7, w3, 1, 3);
328  else if (P(0xf3,0x62) || P(0x67,0x66) || P(0x37,0x36) || P(0xf3,0xf2) ||
329  P(0xd7,0xd6) || P(0xd7,0x16) || P(0x0b,0x02))
330  *dst01 = interp_2px(w4, 3, w0, 1, 2);
331  else if (cond14)
332  *dst01 = interp_2px(w1, 1, w4, 1, 1);
333  else
334  *dst01 = interp_2px(w4, 3, w1, 1, 2);
335 
336  if (cond01)
337  *dst10 = interp_2px(w4, 7, w1, 1, 3);
338  else if (cond08)
339  *dst10 = w4;
340  else if (cond02)
341  *dst10 = interp_2px(w4, 3, w0, 1, 2);
342  else if (cond09)
343  *dst10 = w4;
344  else if (cond10)
345  *dst10 = interp_3px(w4, 5, w3, 2, w1, 1, 3);
346  else if (P(0x0b,0x02))
347  *dst10 = interp_3px(w4, 5, w3, 2, w0, 1, 3);
348  else if (cond15)
349  *dst10 = interp_2px(w4, 5, w3, 3, 3);
350  else if (cond03)
351  *dst10 = interp_2px(w3, 3, w4, 1, 2);
352  else if (cond13)
353  *dst10 = interp_3px(w3, 2, w4, 1, w1, 1, 2);
354  else if (cond12)
355  *dst10 = interp_2px(w3, 5, w1, 3, 3);
356  else if (cond06)
357  *dst10 = interp_2px(w4, 7, w1, 1, 3);
358  else if (P(0x0b,0x08) || P(0xf9,0x68) || P(0x6d,0x6c) || P(0x3d,0x3c) ||
359  P(0xf9,0xf8) || P(0xdd,0xdc) || P(0xdd,0x1c))
360  *dst10 = interp_2px(w4, 3, w0, 1, 2);
361  else if (cond14)
362  *dst10 = interp_2px(w3, 1, w4, 1, 1);
363  else
364  *dst10 = interp_2px(w4, 3, w3, 1, 2);
365 
366  if ((P(0x7f,0x2b) || P(0xef,0xab) || P(0xbf,0x8f) || P(0x7f,0x0f)) &&
367  WDIFF(w3, w1))
368  *dst11 = w4;
369  else if (cond02)
370  *dst11 = interp_2px(w4, 7, w0, 1, 3);
371  else if (cond15)
372  *dst11 = interp_2px(w4, 7, w3, 1, 3);
373  else if (cond11)
374  *dst11 = interp_2px(w4, 7, w1, 1, 3);
375  else if (P(0x0a,0x00) || P(0x7e,0x2a) || P(0xef,0xab) || P(0xbf,0x8f) ||
376  P(0x7e,0x0e))
377  *dst11 = interp_3px(w4, 6, w3, 1, w1, 1, 3);
378  else if (cond07)
379  *dst11 = interp_2px(w4, 7, w0, 1, 3);
380  else
381  *dst11 = w4;
382 }
383 
384 static av_always_inline void hqx_filter(const ThreadData *td, int jobnr, int nb_jobs, int n)
385 {
386  int x, y;
387  AVFrame *in = td->in, *out = td->out;
388  const uint32_t *r2y = td->rgbtoyuv;
389  const int height = in->height;
390  const int width = in->width;
391  const int slice_start = ff_slice_pos(height, jobnr, nb_jobs);
392  const int slice_end = ff_slice_pos(height, jobnr + 1, nb_jobs);
393  const int dst_linesize = out->linesize[0];
394  const int src_linesize = in->linesize[0];
395  uint8_t *dst = out->data[0] + slice_start * dst_linesize * n;
396  const uint8_t *src = in->data[0] + slice_start * src_linesize;
397 
398  const int dst32_linesize = dst_linesize >> 2;
399  const int src32_linesize = src_linesize >> 2;
400 
401  for (y = slice_start; y < slice_end; y++) {
402  const uint32_t *src32 = (const uint32_t *)src;
403  uint32_t *dst32 = (uint32_t *)dst;
404  const int prevline = y > 0 ? -src32_linesize : 0;
405  const int nextline = y < height - 1 ? src32_linesize : 0;
406 
407  for (x = 0; x < width; x++) {
408  const int prevcol = x > 0 ? -1 : 0;
409  const int nextcol = x < width -1 ? 1 : 0;
410  const uint32_t w[3*3] = {
411  src32[prevcol + prevline], src32[prevline], src32[prevline + nextcol],
412  src32[prevcol ], src32[ 0], src32[ nextcol],
413  src32[prevcol + nextline], src32[nextline], src32[nextline + nextcol]
414  };
415  const uint32_t yuv1 = rgb2yuv(r2y, w[4]);
416  const int pattern = (w[4] != w[0] ? (yuv_diff(yuv1, rgb2yuv(r2y, w[0]))) : 0)
417  | (w[4] != w[1] ? (yuv_diff(yuv1, rgb2yuv(r2y, w[1]))) : 0) << 1
418  | (w[4] != w[2] ? (yuv_diff(yuv1, rgb2yuv(r2y, w[2]))) : 0) << 2
419  | (w[4] != w[3] ? (yuv_diff(yuv1, rgb2yuv(r2y, w[3]))) : 0) << 3
420  | (w[4] != w[5] ? (yuv_diff(yuv1, rgb2yuv(r2y, w[5]))) : 0) << 4
421  | (w[4] != w[6] ? (yuv_diff(yuv1, rgb2yuv(r2y, w[6]))) : 0) << 5
422  | (w[4] != w[7] ? (yuv_diff(yuv1, rgb2yuv(r2y, w[7]))) : 0) << 6
423  | (w[4] != w[8] ? (yuv_diff(yuv1, rgb2yuv(r2y, w[8]))) : 0) << 7;
424 
425  if (n == 2) {
426  dst32[dst32_linesize*0 + 0] = hq2x_interp_1x1(r2y, pattern, w, 0,1,2,3,4,5,6,7,8); // 00
427  dst32[dst32_linesize*0 + 1] = hq2x_interp_1x1(r2y, pattern, w, 2,1,0,5,4,3,8,7,6); // 01 (vert mirrored)
428  dst32[dst32_linesize*1 + 0] = hq2x_interp_1x1(r2y, pattern, w, 6,7,8,3,4,5,0,1,2); // 10 (horiz mirrored)
429  dst32[dst32_linesize*1 + 1] = hq2x_interp_1x1(r2y, pattern, w, 8,7,6,5,4,3,2,1,0); // 11 (center mirrored)
430  } else if (n == 3) {
431  hq3x_interp_2x1(dst32, dst32_linesize, r2y, pattern, w, 0,1, 0,1,2,3,4,5,6,7,8, 0); // 00 01
432  hq3x_interp_2x1(dst32 + 1, dst32_linesize, r2y, pattern, w, 1,3, 2,5,8,1,4,7,0,3,6, 1); // 02 12 (rotated to the right)
433  hq3x_interp_2x1(dst32 + 1*dst32_linesize, dst32_linesize, r2y, pattern, w, 2,0, 6,3,0,7,4,1,8,5,2, 1); // 20 10 (rotated to the left)
434  hq3x_interp_2x1(dst32 + 1*dst32_linesize + 1, dst32_linesize, r2y, pattern, w, 3,2, 8,7,6,5,4,3,2,1,0, 0); // 22 21 (center mirrored)
435  dst32[dst32_linesize + 1] = w[4]; // 11
436  } else if (n == 4) {
437  hq4x_interp_2x2(dst32, dst32_linesize, r2y, pattern, w, 0,1,2,3, 0,1,2,3,4,5,6,7,8); // 00 01 10 11
438  hq4x_interp_2x2(dst32 + 2, dst32_linesize, r2y, pattern, w, 1,0,3,2, 2,1,0,5,4,3,8,7,6); // 02 03 12 13 (vert mirrored)
439  hq4x_interp_2x2(dst32 + 2*dst32_linesize, dst32_linesize, r2y, pattern, w, 2,3,0,1, 6,7,8,3,4,5,0,1,2); // 20 21 30 31 (horiz mirrored)
440  hq4x_interp_2x2(dst32 + 2*dst32_linesize + 2, dst32_linesize, r2y, pattern, w, 3,2,1,0, 8,7,6,5,4,3,2,1,0); // 22 23 32 33 (center mirrored)
441  } else {
442  av_assert0(0);
443  }
444 
445  src32 += 1;
446  dst32 += n;
447  }
448 
449  src += src_linesize;
450  dst += dst_linesize * n;
451  }
452 }
453 
454 #define HQX_FUNC(size) \
455 static int hq##size##x(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) \
456 { \
457  hqx_filter(arg, jobnr, nb_jobs, size); \
458  return 0; \
459 }
460 
461 HQX_FUNC(2)
462 HQX_FUNC(3)
463 HQX_FUNC(4)
464 
466 {
467  static const enum AVPixelFormat pix_fmts[] = {AV_PIX_FMT_RGB32, AV_PIX_FMT_NONE};
469  if (!fmts_list)
470  return AVERROR(ENOMEM);
471  return ff_set_common_formats(ctx, fmts_list);
472 }
473 
474 static int config_output(AVFilterLink *outlink)
475 {
476  AVFilterContext *ctx = outlink->src;
477  HQXContext *hqx = ctx->priv;
478  AVFilterLink *inlink = ctx->inputs[0];
479 
480  outlink->w = inlink->w * hqx->n;
481  outlink->h = inlink->h * hqx->n;
482  av_log(inlink->dst, AV_LOG_VERBOSE, "fmt:%s size:%dx%d -> size:%dx%d\n",
483  av_get_pix_fmt_name(inlink->format),
484  inlink->w, inlink->h, outlink->w, outlink->h);
485  return 0;
486 }
487 
488 static int filter_frame(AVFilterLink *inlink, AVFrame *in)
489 {
490  AVFilterContext *ctx = inlink->dst;
491  AVFilterLink *outlink = ctx->outputs[0];
492  HQXContext *hqx = ctx->priv;
493  ThreadData td;
494  AVFrame *out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
495  if (!out) {
496  av_frame_free(&in);
497  return AVERROR(ENOMEM);
498  }
500  out->width = outlink->w;
501  out->height = outlink->h;
502 
503  td.in = in;
504  td.out = out;
505  td.rgbtoyuv = hqx->rgbtoyuv;
506  ctx->internal->execute(ctx, hqx->func, &td, NULL, FFMIN(inlink->h, ff_filter_get_nb_threads(ctx)));
507 
508  av_frame_free(&in);
509  return ff_filter_frame(outlink, out);
510 }
511 
513 {
514  HQXContext *hqx = ctx->priv;
515  static const hqxfunc_t hqxfuncs[] = {hq2x, hq3x, hq4x};
516 
517  uint32_t c;
518  int bg, rg, g;
519 
520  for (bg=-255; bg<256; bg++) {
521  for (rg=-255; rg<256; rg++) {
522  const uint32_t u = (uint32_t)((-169*rg + 500*bg)/1000) + 128;
523  const uint32_t v = (uint32_t)(( 500*rg - 81*bg)/1000) + 128;
524  int startg = FFMAX3(-bg, -rg, 0);
525  int endg = FFMIN3(255-bg, 255-rg, 255);
526  uint32_t y = (uint32_t)(( 299*rg + 1000*startg + 114*bg)/1000);
527  c = bg + rg * (1 << 16) + 0x010101 * startg;
528  for (g = startg; g <= endg; g++) {
529  hqx->rgbtoyuv[c] = ((y++) << 16) + (u << 8) + v;
530  c+= 0x010101;
531  }
532  }
533  }
534 
535  hqx->func = hqxfuncs[hqx->n - 2];
536  return 0;
537 }
538 
539 static const AVFilterPad hqx_inputs[] = {
540  {
541  .name = "default",
542  .type = AVMEDIA_TYPE_VIDEO,
543  .filter_frame = filter_frame,
544  },
545  { NULL }
546 };
547 
548 static const AVFilterPad hqx_outputs[] = {
549  {
550  .name = "default",
551  .type = AVMEDIA_TYPE_VIDEO,
552  .config_props = config_output,
553  },
554  { NULL }
555 };
556 
558  .name = "hqx",
559  .description = NULL_IF_CONFIG_SMALL("Scale the input by 2, 3 or 4 using the hq*x magnification algorithm."),
560  .priv_size = sizeof(HQXContext),
561  .init = init,
563  .inputs = hqx_inputs,
564  .outputs = hqx_outputs,
565  .priv_class = &hqx_class,
567 };
static const AVFilterPad inputs[]
Definition: af_acontrast.c:193
static const AVFilterPad outputs[]
Definition: af_acontrast.c:203
#define av_always_inline
Definition: attributes.h:45
#define av_cold
Definition: attributes.h:88
uint8_t pi<< 24) CONV_FUNC_GROUP(AV_SAMPLE_FMT_FLT, float, AV_SAMPLE_FMT_U8, uint8_t,(*(const uint8_t *) pi - 0x80) *(1.0f/(1<< 7))) CONV_FUNC_GROUP(AV_SAMPLE_FMT_DBL, double, AV_SAMPLE_FMT_U8, uint8_t,(*(const uint8_t *) pi - 0x80) *(1.0/(1<< 7))) CONV_FUNC_GROUP(AV_SAMPLE_FMT_U8, uint8_t, AV_SAMPLE_FMT_S16, int16_t,(*(const int16_t *) pi >> 8)+0x80) CONV_FUNC_GROUP(AV_SAMPLE_FMT_FLT, float, AV_SAMPLE_FMT_S16, int16_t, *(const int16_t *) pi *(1.0f/(1<< 15))) CONV_FUNC_GROUP(AV_SAMPLE_FMT_DBL, double, AV_SAMPLE_FMT_S16, int16_t, *(const int16_t *) pi *(1.0/(1<< 15))) CONV_FUNC_GROUP(AV_SAMPLE_FMT_U8, uint8_t, AV_SAMPLE_FMT_S32, int32_t,(*(const int32_t *) pi >> 24)+0x80) CONV_FUNC_GROUP(AV_SAMPLE_FMT_FLT, float, AV_SAMPLE_FMT_S32, int32_t, *(const int32_t *) pi *(1.0f/(1U<< 31))) CONV_FUNC_GROUP(AV_SAMPLE_FMT_DBL, double, AV_SAMPLE_FMT_S32, int32_t, *(const int32_t *) pi *(1.0/(1U<< 31))) CONV_FUNC_GROUP(AV_SAMPLE_FMT_U8, uint8_t, AV_SAMPLE_FMT_FLT, float, av_clip_uint8(lrintf(*(const float *) pi *(1<< 7))+0x80)) CONV_FUNC_GROUP(AV_SAMPLE_FMT_S16, int16_t, AV_SAMPLE_FMT_FLT, float, av_clip_int16(lrintf(*(const float *) pi *(1<< 15)))) CONV_FUNC_GROUP(AV_SAMPLE_FMT_S32, int32_t, AV_SAMPLE_FMT_FLT, float, av_clipl_int32(llrintf(*(const float *) pi *(1U<< 31)))) CONV_FUNC_GROUP(AV_SAMPLE_FMT_U8, uint8_t, AV_SAMPLE_FMT_DBL, double, av_clip_uint8(lrint(*(const double *) pi *(1<< 7))+0x80)) CONV_FUNC_GROUP(AV_SAMPLE_FMT_S16, int16_t, AV_SAMPLE_FMT_DBL, double, av_clip_int16(lrint(*(const double *) pi *(1<< 15)))) CONV_FUNC_GROUP(AV_SAMPLE_FMT_S32, int32_t, AV_SAMPLE_FMT_DBL, double, av_clipl_int32(llrint(*(const double *) pi *(1U<< 31)))) #define SET_CONV_FUNC_GROUP(ofmt, ifmt) static void set_generic_function(AudioConvert *ac) { } void ff_audio_convert_free(AudioConvert **ac) { if(! *ac) return;ff_dither_free(&(*ac) ->dc);av_freep(ac);} AudioConvert *ff_audio_convert_alloc(AVAudioResampleContext *avr, enum AVSampleFormat out_fmt, enum AVSampleFormat in_fmt, int channels, int sample_rate, int apply_map) { AudioConvert *ac;int in_planar, out_planar;ac=av_mallocz(sizeof(*ac));if(!ac) return NULL;ac->avr=avr;ac->out_fmt=out_fmt;ac->in_fmt=in_fmt;ac->channels=channels;ac->apply_map=apply_map;if(avr->dither_method !=AV_RESAMPLE_DITHER_NONE &&av_get_packed_sample_fmt(out_fmt)==AV_SAMPLE_FMT_S16 &&av_get_bytes_per_sample(in_fmt) > 2) { ac->dc=ff_dither_alloc(avr, out_fmt, in_fmt, channels, sample_rate, apply_map);if(!ac->dc) { av_free(ac);return NULL;} return ac;} in_planar=ff_sample_fmt_is_planar(in_fmt, channels);out_planar=ff_sample_fmt_is_planar(out_fmt, channels);if(in_planar==out_planar) { ac->func_type=CONV_FUNC_TYPE_FLAT;ac->planes=in_planar ? ac->channels :1;} else if(in_planar) ac->func_type=CONV_FUNC_TYPE_INTERLEAVE;else ac->func_type=CONV_FUNC_TYPE_DEINTERLEAVE;set_generic_function(ac);if(ARCH_AARCH64) ff_audio_convert_init_aarch64(ac);if(ARCH_ARM) ff_audio_convert_init_arm(ac);if(ARCH_X86) ff_audio_convert_init_x86(ac);return ac;} int ff_audio_convert(AudioConvert *ac, AudioData *out, AudioData *in) { int use_generic=1;int len=in->nb_samples;int p;if(ac->dc) { av_log(ac->avr, AV_LOG_TRACE, "%d samples - audio_convert: %s to %s (dithered)\n", len, av_get_sample_fmt_name(ac->in_fmt), av_get_sample_fmt_name(ac->out_fmt));return ff_convert_dither(ac-> in
uint8_t
simple assert() macros that are a bit more flexible than ISO C assert().
#define av_assert0(cond)
assert() equivalent, that is always enabled.
Definition: avassert.h:37
int ff_filter_frame(AVFilterLink *link, AVFrame *frame)
Send a frame of data to the next filter.
Definition: avfilter.c:1096
int ff_filter_get_nb_threads(AVFilterContext *ctx)
Get number of threads for current filter instance.
Definition: avfilter.c:802
#define flags(name, subs,...)
Definition: cbs_av1.c:572
#define u(width, name, range_min, range_max)
Definition: cbs_h2645.c:264
#define s(width, name)
Definition: cbs_vp9.c:257
#define FFMAX3(a, b, c)
Definition: common.h:104
#define FFMIN(a, b)
Definition: common.h:105
#define FFMIN3(a, b, c)
Definition: common.h:106
#define NULL
Definition: coverity.c:32
int
static int ff_slice_pos(int total, int jobnr, int nb_jobs)
Compute the boundary index for a slice when work of size total is split into nb_jobs slices.
Definition: filters.h:271
int ff_set_common_formats(AVFilterContext *ctx, AVFilterFormats *formats)
A helper for query_formats() which sets all links to the same list of formats.
Definition: formats.c:587
AVFilterFormats * ff_make_format_list(const int *fmts)
Create a list of supported formats.
Definition: formats.c:286
@ AV_OPT_TYPE_INT
Definition: opt.h:225
#define AVFILTER_FLAG_SLICE_THREADS
The filter supports multithreading by splitting frames into multiple parts and processing them concur...
Definition: avfilter.h:117
#define AVERROR(e)
Definition: error.h:43
void av_frame_free(AVFrame **frame)
Free the frame and any dynamically allocated objects in it, e.g.
Definition: frame.c:203
int av_frame_copy_props(AVFrame *dst, const AVFrame *src)
Copy only "metadata" fields from src to dst.
Definition: frame.c:658
#define AV_LOG_VERBOSE
Detailed information.
Definition: log.h:210
@ AVMEDIA_TYPE_VIDEO
Definition: avutil.h:201
const char * arg
Definition: jacosubdec.c:66
common internal API header
#define NULL_IF_CONFIG_SMALL(x)
Return NULL if CONFIG_SMALL is true, otherwise the argument without modification.
Definition: internal.h:117
static enum AVPixelFormat pix_fmts[]
Definition: libkvazaar.c:309
uint8_t w
Definition: llviddspenc.c:39
static int slice_end(AVCodecContext *avctx, AVFrame *pict)
Handle slice ends.
Definition: mpeg12dec.c:2033
static const uint64_t c2
Definition: murmur3.c:52
static const uint64_t c1
Definition: murmur3.c:51
AVOptions.
const char * av_get_pix_fmt_name(enum AVPixelFormat pix_fmt)
Return the short name for a pixel format, NULL in case pix_fmt is unknown.
Definition: pixdesc.c:2489
AVPixelFormat
Pixel format.
Definition: pixfmt.h:64
@ AV_PIX_FMT_NONE
Definition: pixfmt.h:65
#define AV_PIX_FMT_RGB32
Definition: pixfmt.h:372
#define td
Definition: regdef.h:70
Describe the class of an AVClass context structure.
Definition: log.h:67
An instance of a filter.
Definition: avfilter.h:341
A list of supported formats for one end of a filter link.
Definition: formats.h:65
A filter pad used for either input or output.
Definition: internal.h:54
const char * name
Pad name.
Definition: internal.h:60
Filter definition.
Definition: avfilter.h:145
const char * name
Filter name.
Definition: avfilter.h:149
AVFormatInternal * internal
An opaque field for libavformat internal usage.
Definition: avformat.h:1699
This structure describes decoded (raw) audio or video data.
Definition: frame.h:318
AVOption.
Definition: opt.h:248
Definition: hqx.h:63
int n
Definition: vf_hqx.c:40
hqxfunc_t func
Definition: vf_hqx.c:41
uint32_t rgbtoyuv[1<< 24]
Definition: vf_hqx.c:42
Used for passing data between threads.
Definition: dsddec.c:67
const uint32_t * rgbtoyuv
Definition: vf_hqx.c:47
AVFrame * out
Definition: af_adeclick.c:502
AVFrame * in
Definition: af_adenorm.c:223
#define av_log(a,...)
#define src
Definition: vp8dsp.c:255
FILE * out
Definition: movenc.c:54
AVFormatContext * ctx
Definition: movenc.c:48
#define height
#define width
const char * g
Definition: vf_curves.c:118
static av_always_inline void hqx_filter(const ThreadData *td, int jobnr, int nb_jobs, int n)
Definition: vf_hqx.c:384
static av_always_inline uint32_t rgb2yuv(const uint32_t *r2y, uint32_t c)
Definition: vf_hqx.c:59
static av_always_inline void hq3x_interp_2x1(uint32_t *dst, int dst_linesize, const uint32_t *r2y, int k, const uint32_t *w, int pos00, int pos01, int p0, int p1, int p2, int p3, int p4, int p5, int p6, int p7, int p8, int rotate)
Definition: vf_hqx.c:170
#define INTERP_BOOTSTRAP(rot)
Definition: vf_hqx.c:108
static av_always_inline int yuv_diff(uint32_t yuv1, uint32_t yuv2)
Definition: vf_hqx.c:64
#define ABSDIFF(a, b)
static av_always_inline void hq4x_interp_2x2(uint32_t *dst, int dst_linesize, const uint32_t *r2y, int k, const uint32_t *w, int pos00, int pos01, int pos10, int pos11, int p0, int p1, int p2, int p3, int p4, int p5, int p6, int p7, int p8)
Definition: vf_hqx.c:236
static const AVFilterPad hqx_outputs[]
Definition: vf_hqx.c:548
static int query_formats(AVFilterContext *ctx)
Definition: vf_hqx.c:465
#define YMASK
#define FLAGS
Definition: vf_hqx.c:51
AVFilter ff_vf_hqx
Definition: vf_hqx.c:557
static int filter_frame(AVFilterLink *inlink, AVFrame *in)
Definition: vf_hqx.c:488
#define VMASK
static av_always_inline uint32_t interp_2px(uint32_t c1, int w1, uint32_t c2, int w2, int s)
Definition: vf_hqx.c:77
AVFILTER_DEFINE_CLASS(hqx)
static const AVFilterPad hqx_inputs[]
Definition: vf_hqx.c:539
static av_cold int init(AVFilterContext *ctx)
Definition: vf_hqx.c:512
#define UMASK
#define OFFSET(x)
Definition: vf_hqx.c:50
static int config_output(AVFilterLink *outlink)
Definition: vf_hqx.c:474
#define P(m, r)
Definition: vf_hqx.c:93
#define WDIFF(c1, c2)
Definition: vf_hqx.c:103
int(* hqxfunc_t)(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
Definition: vf_hqx.c:36
static av_always_inline uint32_t hq2x_interp_1x1(const uint32_t *r2y, int k, const uint32_t *w, int p0, int p1, int p2, int p3, int p4, int p5, int p6, int p7, int p8)
Definition: vf_hqx.c:120
static const AVOption hqx_options[]
Definition: vf_hqx.c:52
#define HQX_FUNC(size)
Definition: vf_hqx.c:454
static av_always_inline uint32_t interp_3px(uint32_t c1, int w1, uint32_t c2, int w2, uint32_t c3, int w3, int s)
Definition: vf_hqx.c:84
static void rotate(const float rot_quaternion[2][4], float *vec)
Rotate vector with given rotation quaternion.
Definition: vf_v360.c:3923
AVFrame * ff_get_video_buffer(AVFilterLink *link, int w, int h)
Request a picture buffer with a specific set of permissions.
Definition: video.c:104
static double c[64]