FFmpeg
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 "filters.h"
34 #include "video.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 = (height * jobnr ) / nb_jobs;
392  const int slice_end = (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 
465 static int config_output(AVFilterLink *outlink)
466 {
467  AVFilterContext *ctx = outlink->src;
468  HQXContext *hqx = ctx->priv;
469  AVFilterLink *inlink = ctx->inputs[0];
470 
471  outlink->w = inlink->w * hqx->n;
472  outlink->h = inlink->h * hqx->n;
473  av_log(inlink->dst, AV_LOG_VERBOSE, "fmt:%s size:%dx%d -> size:%dx%d\n",
474  av_get_pix_fmt_name(inlink->format),
475  inlink->w, inlink->h, outlink->w, outlink->h);
476  return 0;
477 }
478 
480 {
481  AVFilterContext *ctx = inlink->dst;
482  AVFilterLink *outlink = ctx->outputs[0];
483  HQXContext *hqx = ctx->priv;
484  ThreadData td;
485  AVFrame *out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
486  if (!out) {
487  av_frame_free(&in);
488  return AVERROR(ENOMEM);
489  }
491  out->width = outlink->w;
492  out->height = outlink->h;
493 
494  td.in = in;
495  td.out = out;
496  td.rgbtoyuv = hqx->rgbtoyuv;
497  ff_filter_execute(ctx, hqx->func, &td, NULL,
499 
500  av_frame_free(&in);
501  return ff_filter_frame(outlink, out);
502 }
503 
505 {
506  HQXContext *hqx = ctx->priv;
507  static const hqxfunc_t hqxfuncs[] = {hq2x, hq3x, hq4x};
508 
509  uint32_t c;
510  int bg, rg, g;
511 
512  for (bg=-255; bg<256; bg++) {
513  for (rg=-255; rg<256; rg++) {
514  const uint32_t u = (uint32_t)((-169*rg + 500*bg)/1000) + 128;
515  const uint32_t v = (uint32_t)(( 500*rg - 81*bg)/1000) + 128;
516  int startg = FFMAX3(-bg, -rg, 0);
517  int endg = FFMIN3(255-bg, 255-rg, 255);
518  uint32_t y = (uint32_t)(( 299*rg + 1000*startg + 114*bg)/1000);
519  c = bg + rg * (1 << 16) + 0x010101 * startg;
520  for (g = startg; g <= endg; g++) {
521  hqx->rgbtoyuv[c] = ((y++) << 16) + (u << 8) + v;
522  c+= 0x010101;
523  }
524  }
525  }
526 
527  hqx->func = hqxfuncs[hqx->n - 2];
528  return 0;
529 }
530 
531 static const AVFilterPad hqx_inputs[] = {
532  {
533  .name = "default",
534  .type = AVMEDIA_TYPE_VIDEO,
535  .filter_frame = filter_frame,
536  },
537 };
538 
539 static const AVFilterPad hqx_outputs[] = {
540  {
541  .name = "default",
542  .type = AVMEDIA_TYPE_VIDEO,
543  .config_props = config_output,
544  },
545 };
546 
548  .name = "hqx",
549  .description = NULL_IF_CONFIG_SMALL("Scale the input by 2, 3 or 4 using the hq*x magnification algorithm."),
550  .priv_size = sizeof(HQXContext),
551  .init = init,
555  .priv_class = &hqx_class,
557 };
ff_get_video_buffer
AVFrame * ff_get_video_buffer(AVFilterLink *link, int w, int h)
Request a picture buffer with a specific set of permissions.
Definition: video.c:116
HQXContext::n
int n
Definition: vf_hqx.c:40
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
opt.h
ABSDIFF
#define ABSDIFF(a, b)
out
FILE * out
Definition: movenc.c:55
u
#define u(width, name, range_min, range_max)
Definition: cbs_h2645.c:251
P
#define P(m, r)
Definition: vf_hqx.c:93
ff_filter_frame
int ff_filter_frame(AVFilterLink *link, AVFrame *frame)
Send a frame of data to the next filter.
Definition: avfilter.c:1061
inlink
The exact code depends on how similar the blocks are and how related they are to the and needs to apply these operations to the correct inlink or outlink if there are several Macros are available to factor that when no extra processing is inlink
Definition: filter_design.txt:212
av_frame_free
void av_frame_free(AVFrame **frame)
Free the frame and any dynamically allocated objects in it, e.g.
Definition: frame.c:163
rgb2yuv
static av_always_inline uint32_t rgb2yuv(const uint32_t *r2y, uint32_t c)
Definition: vf_hqx.c:59
FILTER_INPUTS
#define FILTER_INPUTS(array)
Definition: filters.h:262
AVFrame
This structure describes decoded (raw) audio or video data.
Definition: frame.h:403
pixdesc.h
AVFrame::width
int width
Definition: frame.h:475
w
uint8_t w
Definition: llviddspenc.c:38
interp_3px
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
AVOption
AVOption.
Definition: opt.h:429
filter_frame
static int filter_frame(AVFilterLink *inlink, AVFrame *in)
Definition: vf_hqx.c:479
AV_LOG_VERBOSE
#define AV_LOG_VERBOSE
Detailed information.
Definition: log.h:225
AVFilter::name
const char * name
Filter name.
Definition: avfilter.h:205
c1
static const uint64_t c1
Definition: murmur3.c:52
init
static av_cold int init(AVFilterContext *ctx)
Definition: vf_hqx.c:504
ThreadData::out
AVFrame * out
Definition: af_adeclick.c:526
video.h
ThreadData::in
AVFrame * in
Definition: af_adecorrelate.c:155
AVFrame::data
uint8_t * data[AV_NUM_DATA_POINTERS]
pointer to the picture/channel planes.
Definition: frame.h:424
UMASK
#define UMASK
slice_end
static int slice_end(AVCodecContext *avctx, AVFrame *pict, int *got_output)
Handle slice ends.
Definition: mpeg12dec.c:1720
WDIFF
#define WDIFF(c1, c2)
Definition: vf_hqx.c:103
VMASK
#define VMASK
AVFilterPad
A filter pad used for either input or output.
Definition: filters.h:38
rotate
static void rotate(const float rot_quaternion[2][4], float *vec)
Rotate vector with given rotation quaternion.
Definition: vf_v360.c:4071
avassert.h
av_cold
#define av_cold
Definition: attributes.h:90
yuv_diff
static av_always_inline int yuv_diff(uint32_t yuv1, uint32_t yuv2)
Definition: vf_hqx.c:64
hq3x_interp_2x1
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
ff_vf_hqx
const AVFilter ff_vf_hqx
Definition: vf_hqx.c:547
AVFILTER_DEFINE_CLASS
AVFILTER_DEFINE_CLASS(hqx)
s
#define s(width, name)
Definition: cbs_vp9.c:198
g
const char * g
Definition: vf_curves.c:128
av_assert0
#define av_assert0(cond)
assert() equivalent, that is always enabled.
Definition: avassert.h:40
filters.h
ctx
AVFormatContext * ctx
Definition: movenc.c:49
FILTER_OUTPUTS
#define FILTER_OUTPUTS(array)
Definition: filters.h:263
hqx_filter
static av_always_inline void hqx_filter(const ThreadData *td, int jobnr, int nb_jobs, int n)
Definition: vf_hqx.c:384
interp_2px
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
arg
const char * arg
Definition: jacosubdec.c:67
OFFSET
#define OFFSET(x)
Definition: vf_hqx.c:50
AVClass
Describe the class of an AVClass context structure.
Definition: log.h:75
NULL
#define NULL
Definition: coverity.c:32
av_frame_copy_props
int av_frame_copy_props(AVFrame *dst, const AVFrame *src)
Copy only "metadata" fields from src to dst.
Definition: frame.c:726
HQXContext::rgbtoyuv
uint32_t rgbtoyuv[1<< 24]
Definition: vf_hqx.c:42
c
Undefined Behavior In the C some operations are like signed integer dereferencing freed accessing outside allocated Undefined Behavior must not occur in a C it is not safe even if the output of undefined operations is unused The unsafety may seem nit picking but Optimizing compilers have in fact optimized code on the assumption that no undefined Behavior occurs Optimizing code based on wrong assumptions can and has in some cases lead to effects beyond the output of computations The signed integer overflow problem in speed critical code Code which is highly optimized and works with signed integers sometimes has the problem that often the output of the computation does not c
Definition: undefined.txt:32
hqx_inputs
static const AVFilterPad hqx_inputs[]
Definition: vf_hqx.c:531
NULL_IF_CONFIG_SMALL
#define NULL_IF_CONFIG_SMALL(x)
Return NULL if CONFIG_SMALL is true, otherwise the argument without modification.
Definition: internal.h:94
height
#define height
Definition: dsp.h:85
dst
uint8_t ptrdiff_t const uint8_t ptrdiff_t int intptr_t intptr_t int int16_t * dst
Definition: dsp.h:83
YMASK
#define YMASK
AV_PIX_FMT_RGB32
#define AV_PIX_FMT_RGB32
Definition: pixfmt.h:475
ThreadData::rgbtoyuv
const uint32_t * rgbtoyuv
Definition: vf_hqx.c:47
HQX_FUNC
#define HQX_FUNC(size)
Definition: vf_hqx.c:454
HQXContext
Definition: hqx.h:63
hqx_outputs
static const AVFilterPad hqx_outputs[]
Definition: vf_hqx.c:539
FFMIN3
#define FFMIN3(a, b, c)
Definition: macros.h:50
ff_filter_get_nb_threads
int ff_filter_get_nb_threads(AVFilterContext *ctx)
Get number of threads for current filter instance.
Definition: avfilter.c:840
ThreadData
Used for passing data between threads.
Definition: dsddec.c:71
av_always_inline
#define av_always_inline
Definition: attributes.h:49
FFMIN
#define FFMIN(a, b)
Definition: macros.h:49
config_output
static int config_output(AVFilterLink *outlink)
Definition: vf_hqx.c:465
AVFilterPad::name
const char * name
Pad name.
Definition: filters.h:44
hqxfunc_t
int(* hqxfunc_t)(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
Definition: vf_hqx.c:36
INTERP_BOOTSTRAP
#define INTERP_BOOTSTRAP(rot)
Definition: vf_hqx.c:108
slice_start
static int slice_start(SliceContext *sc, VVCContext *s, VVCFrameContext *fc, const CodedBitstreamUnit *unit, const int is_first_slice)
Definition: dec.c:736
AVFilter
Filter definition.
Definition: avfilter.h:201
HQXContext::func
hqxfunc_t func
Definition: vf_hqx.c:41
hq4x_interp_2x2
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
AVFrame::height
int height
Definition: frame.h:475
c2
static const uint64_t c2
Definition: murmur3.c:53
ff_filter_execute
int ff_filter_execute(AVFilterContext *ctx, avfilter_action_func *func, void *arg, int *ret, int nb_jobs)
Definition: avfilter.c:1666
AV_OPT_TYPE_INT
@ AV_OPT_TYPE_INT
Underlying C type is int.
Definition: opt.h:259
FLAGS
#define FLAGS
Definition: vf_hqx.c:51
AVFilterContext
An instance of a filter.
Definition: avfilter.h:457
AVFILTER_FLAG_SLICE_THREADS
#define AVFILTER_FLAG_SLICE_THREADS
The filter supports multithreading by splitting frames into multiple parts and processing them concur...
Definition: avfilter.h:152
AVMEDIA_TYPE_VIDEO
@ AVMEDIA_TYPE_VIDEO
Definition: avutil.h:201
FFMAX3
#define FFMAX3(a, b, c)
Definition: macros.h:48
AVFrame::linesize
int linesize[AV_NUM_DATA_POINTERS]
For video, a positive or negative value, which is typically indicating the size in bytes of each pict...
Definition: frame.h:448
av_log
#define av_log(a,...)
Definition: tableprint_vlc.h:27
hq2x_interp_1x1
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
width
#define width
Definition: dsp.h:85
FILTER_SINGLE_PIXFMT
#define FILTER_SINGLE_PIXFMT(pix_fmt_)
Definition: filters.h:252
src
#define src
Definition: vp8dsp.c:248
hqx_options
static const AVOption hqx_options[]
Definition: vf_hqx.c:52
av_get_pix_fmt_name
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:3090