FFmpeg
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
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 
35 typedef int (*hqxfunc_t)(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs);
36 
37 typedef struct {
38  const AVClass *class;
39  int n;
41  uint32_t rgbtoyuv[1<<24];
42 } HQXContext;
43 
44 typedef struct ThreadData {
45  AVFrame *in, *out;
46  const uint32_t *rgbtoyuv;
47 } ThreadData;
48 
49 #define OFFSET(x) offsetof(HQXContext, x)
50 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
51 static const AVOption hqx_options[] = {
52  { "n", "set scale factor", OFFSET(n), AV_OPT_TYPE_INT, {.i64 = 3}, 2, 4, .flags = FLAGS },
53  { NULL }
54 };
55 
57 
58 static av_always_inline uint32_t rgb2yuv(const uint32_t *r2y, uint32_t c)
59 {
60  return r2y[c & 0xffffff];
61 }
62 
63 static av_always_inline int yuv_diff(uint32_t yuv1, uint32_t yuv2)
64 {
65 #define YMASK 0xff0000
66 #define UMASK 0x00ff00
67 #define VMASK 0x0000ff
68  return abs((yuv1 & YMASK) - (yuv2 & YMASK)) > (48 << 16) ||
69  abs((yuv1 & UMASK) - (yuv2 & UMASK)) > ( 7 << 8) ||
70  abs((yuv1 & VMASK) - (yuv2 & VMASK)) > ( 6 << 0);
71 }
72 
73 /* (c1*w1 + c2*w2) >> s */
74 static av_always_inline uint32_t interp_2px(uint32_t c1, int w1, uint32_t c2, int w2, int s)
75 {
76  return (((((c1 & 0xff00ff00) >> 8) * w1 + ((c2 & 0xff00ff00) >> 8) * w2) << (8 - s)) & 0xff00ff00) |
77  (((((c1 & 0x00ff00ff) ) * w1 + ((c2 & 0x00ff00ff) ) * w2) >> s ) & 0x00ff00ff);
78 }
79 
80 /* (c1*w1 + c2*w2 + c3*w3) >> s */
81 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)
82 {
83  return (((((c1 & 0xff00ff00) >> 8) * w1 + ((c2 & 0xff00ff00) >> 8) * w2 + ((c3 & 0xff00ff00) >> 8) * w3) << (8 - s)) & 0xff00ff00) |
84  (((((c1 & 0x00ff00ff) ) * w1 + ((c2 & 0x00ff00ff) ) * w2 + ((c3 & 0x00ff00ff) ) * w3) >> s ) & 0x00ff00ff);
85 }
86 
87 /* m is the mask of diff with the center pixel that matters in the pattern, and
88  * r is the expected result (bit set to 1 if there is difference with the
89  * center, 0 otherwise) */
90 #define P(m, r) ((k_shuffled & (m)) == (r))
91 
92 /* adjust 012345678 to 01235678: the mask doesn't contain the (null) diff
93  * between the center/current pixel and itself */
94 #define DROP4(z) ((z) > 4 ? (z)-1 : (z))
95 
96 /* shuffle the input mask: move bit n (4-adjusted) to position stored in p<n> */
97 #define SHF(x, rot, n) (((x) >> ((rot) ? 7-DROP4(n) : DROP4(n)) & 1) << DROP4(p##n))
98 
99 /* used to check if there is YUV difference between 2 pixels */
100 #define WDIFF(c1, c2) yuv_diff(rgb2yuv(r2y, c1), rgb2yuv(r2y, c2))
101 
102 /* bootstrap template for every interpolation code. It defines the shuffled
103  * masks and surrounding pixels. The rot flag is used to indicate if it's a
104  * rotation; its basic effect is to shuffle k using p8..p0 instead of p0..p8 */
105 #define INTERP_BOOTSTRAP(rot) \
106  const int k_shuffled = SHF(k,rot,0) | SHF(k,rot,1) | SHF(k,rot,2) \
107  | SHF(k,rot,3) | 0 | SHF(k,rot,5) \
108  | SHF(k,rot,6) | SHF(k,rot,7) | SHF(k,rot,8); \
109  \
110  const uint32_t w0 = w[p0], w1 = w[p1], \
111  w3 = w[p3], w4 = w[p4], w5 = w[p5], \
112  w7 = w[p7]
113 
114 /* Assuming p0..p8 is mapped to pixels 0..8, this function interpolates the
115  * top-left pixel in the total of the 2x2 pixels to interpolates. The function
116  * is also used for the 3 other pixels */
117 static av_always_inline uint32_t hq2x_interp_1x1(const uint32_t *r2y, int k,
118  const uint32_t *w,
119  int p0, int p1, int p2,
120  int p3, int p4, int p5,
121  int p6, int p7, int p8)
122 {
123  INTERP_BOOTSTRAP(0);
124 
125  if ((P(0xbf,0x37) || P(0xdb,0x13)) && WDIFF(w1, w5))
126  return interp_2px(w4, 3, w3, 1, 2);
127  if ((P(0xdb,0x49) || P(0xef,0x6d)) && WDIFF(w7, w3))
128  return interp_2px(w4, 3, w1, 1, 2);
129  if ((P(0x0b,0x0b) || P(0xfe,0x4a) || P(0xfe,0x1a)) && WDIFF(w3, w1))
130  return w4;
131  if ((P(0x6f,0x2a) || P(0x5b,0x0a) || P(0xbf,0x3a) || P(0xdf,0x5a) ||
132  P(0x9f,0x8a) || P(0xcf,0x8a) || P(0xef,0x4e) || P(0x3f,0x0e) ||
133  P(0xfb,0x5a) || P(0xbb,0x8a) || P(0x7f,0x5a) || P(0xaf,0x8a) ||
134  P(0xeb,0x8a)) && WDIFF(w3, w1))
135  return interp_2px(w4, 3, w0, 1, 2);
136  if (P(0x0b,0x08))
137  return interp_3px(w4, 2, w0, 1, w1, 1, 2);
138  if (P(0x0b,0x02))
139  return interp_3px(w4, 2, w0, 1, w3, 1, 2);
140  if (P(0x2f,0x2f))
141  return interp_3px(w4, 14, w3, 1, w1, 1, 4);
142  if (P(0xbf,0x37) || P(0xdb,0x13))
143  return interp_3px(w4, 5, w1, 2, w3, 1, 3);
144  if (P(0xdb,0x49) || P(0xef,0x6d))
145  return interp_3px(w4, 5, w3, 2, w1, 1, 3);
146  if (P(0x1b,0x03) || P(0x4f,0x43) || P(0x8b,0x83) || P(0x6b,0x43))
147  return interp_2px(w4, 3, w3, 1, 2);
148  if (P(0x4b,0x09) || P(0x8b,0x89) || P(0x1f,0x19) || P(0x3b,0x19))
149  return interp_2px(w4, 3, w1, 1, 2);
150  if (P(0x7e,0x2a) || P(0xef,0xab) || P(0xbf,0x8f) || P(0x7e,0x0e))
151  return interp_3px(w4, 2, w3, 3, w1, 3, 3);
152  if (P(0xfb,0x6a) || P(0x6f,0x6e) || P(0x3f,0x3e) || P(0xfb,0xfa) ||
153  P(0xdf,0xde) || P(0xdf,0x1e))
154  return interp_2px(w4, 3, w0, 1, 2);
155  if (P(0x0a,0x00) || P(0x4f,0x4b) || P(0x9f,0x1b) || P(0x2f,0x0b) ||
156  P(0xbe,0x0a) || P(0xee,0x0a) || P(0x7e,0x0a) || P(0xeb,0x4b) ||
157  P(0x3b,0x1b))
158  return interp_3px(w4, 2, w3, 1, w1, 1, 2);
159  return interp_3px(w4, 6, w3, 1, w1, 1, 3);
160 }
161 
162 /* Assuming p0..p8 is mapped to pixels 0..8, this function interpolates the
163  * top-left and top-center pixel in the total of the 3x3 pixels to
164  * interpolates. The function is also used for the 3 other couples of pixels
165  * defining the outline. The center pixel is not defined through this function,
166  * since it's just the same as the original value. */
167 static av_always_inline void hq3x_interp_2x1(uint32_t *dst, int dst_linesize,
168  const uint32_t *r2y, int k,
169  const uint32_t *w,
170  int pos00, int pos01,
171  int p0, int p1, int p2,
172  int p3, int p4, int p5,
173  int p6, int p7, int p8,
174  int rotate)
175 {
176  INTERP_BOOTSTRAP(rotate);
177 
178  uint32_t *dst00 = &dst[dst_linesize*(pos00>>1) + (pos00&1)];
179  uint32_t *dst01 = &dst[dst_linesize*(pos01>>1) + (pos01&1)];
180 
181  if ((P(0xdb,0x49) || P(0xef,0x6d)) && WDIFF(w7, w3))
182  *dst00 = interp_2px(w4, 3, w1, 1, 2);
183  else if ((P(0xbf,0x37) || P(0xdb,0x13)) && WDIFF(w1, w5))
184  *dst00 = interp_2px(w4, 3, w3, 1, 2);
185  else if ((P(0x0b,0x0b) || P(0xfe,0x4a) || P(0xfe,0x1a)) && WDIFF(w3, w1))
186  *dst00 = w4;
187  else if ((P(0x6f,0x2a) || P(0x5b,0x0a) || P(0xbf,0x3a) || P(0xdf,0x5a) ||
188  P(0x9f,0x8a) || P(0xcf,0x8a) || P(0xef,0x4e) || P(0x3f,0x0e) ||
189  P(0xfb,0x5a) || P(0xbb,0x8a) || P(0x7f,0x5a) || P(0xaf,0x8a) ||
190  P(0xeb,0x8a)) && WDIFF(w3, w1))
191  *dst00 = interp_2px(w4, 3, w0, 1, 2);
192  else if (P(0x4b,0x09) || P(0x8b,0x89) || P(0x1f,0x19) || P(0x3b,0x19))
193  *dst00 = interp_2px(w4, 3, w1, 1, 2);
194  else if (P(0x1b,0x03) || P(0x4f,0x43) || P(0x8b,0x83) || P(0x6b,0x43))
195  *dst00 = interp_2px(w4, 3, w3, 1, 2);
196  else if (P(0x7e,0x2a) || P(0xef,0xab) || P(0xbf,0x8f) || P(0x7e,0x0e))
197  *dst00 = interp_2px(w3, 1, w1, 1, 1);
198  else if (P(0x4f,0x4b) || P(0x9f,0x1b) || P(0x2f,0x0b) || P(0xbe,0x0a) ||
199  P(0xee,0x0a) || P(0x7e,0x0a) || P(0xeb,0x4b) || P(0x3b,0x1b))
200  *dst00 = interp_3px(w4, 2, w3, 7, w1, 7, 4);
201  else if (P(0x0b,0x08) || P(0xf9,0x68) || P(0xf3,0x62) || P(0x6d,0x6c) ||
202  P(0x67,0x66) || P(0x3d,0x3c) || P(0x37,0x36) || P(0xf9,0xf8) ||
203  P(0xdd,0xdc) || P(0xf3,0xf2) || P(0xd7,0xd6) || P(0xdd,0x1c) ||
204  P(0xd7,0x16) || P(0x0b,0x02))
205  *dst00 = interp_2px(w4, 3, w0, 1, 2);
206  else
207  *dst00 = interp_3px(w4, 2, w3, 1, w1, 1, 2);
208 
209  if ((P(0xfe,0xde) || P(0x9e,0x16) || P(0xda,0x12) || P(0x17,0x16) ||
210  P(0x5b,0x12) || P(0xbb,0x12)) && WDIFF(w1, w5))
211  *dst01 = w4;
212  else if ((P(0x0f,0x0b) || P(0x5e,0x0a) || P(0xfb,0x7b) || P(0x3b,0x0b) ||
213  P(0xbe,0x0a) || P(0x7a,0x0a)) && WDIFF(w3, w1))
214  *dst01 = w4;
215  else if (P(0xbf,0x8f) || P(0x7e,0x0e) || P(0xbf,0x37) || P(0xdb,0x13))
216  *dst01 = interp_2px(w1, 3, w4, 1, 2);
217  else if (P(0x02,0x00) || P(0x7c,0x28) || P(0xed,0xa9) || P(0xf5,0xb4) ||
218  P(0xd9,0x90))
219  *dst01 = interp_2px(w4, 3, w1, 1, 2);
220  else if (P(0x4f,0x4b) || P(0xfb,0x7b) || P(0xfe,0x7e) || P(0x9f,0x1b) ||
221  P(0x2f,0x0b) || P(0xbe,0x0a) || P(0x7e,0x0a) || P(0xfb,0x4b) ||
222  P(0xfb,0xdb) || P(0xfe,0xde) || P(0xfe,0x56) || P(0x57,0x56) ||
223  P(0x97,0x16) || P(0x3f,0x1e) || P(0xdb,0x12) || P(0xbb,0x12))
224  *dst01 = interp_2px(w4, 7, w1, 1, 3);
225  else
226  *dst01 = w4;
227 }
228 
229 /* Assuming p0..p8 is mapped to pixels 0..8, this function interpolates the
230  * top-left block of 2x2 pixels in the total of the 4x4 pixels (or 4 blocks) to
231  * interpolates. The function is also used for the 3 other blocks of 2x2
232  * pixels. */
233 static av_always_inline void hq4x_interp_2x2(uint32_t *dst, int dst_linesize,
234  const uint32_t *r2y, int k,
235  const uint32_t *w,
236  int pos00, int pos01,
237  int pos10, int pos11,
238  int p0, int p1, int p2,
239  int p3, int p4, int p5,
240  int p6, int p7, int p8)
241 {
242  INTERP_BOOTSTRAP(0);
243 
244  uint32_t *dst00 = &dst[dst_linesize*(pos00>>1) + (pos00&1)];
245  uint32_t *dst01 = &dst[dst_linesize*(pos01>>1) + (pos01&1)];
246  uint32_t *dst10 = &dst[dst_linesize*(pos10>>1) + (pos10&1)];
247  uint32_t *dst11 = &dst[dst_linesize*(pos11>>1) + (pos11&1)];
248 
249  const int cond00 = (P(0xbf,0x37) || P(0xdb,0x13)) && WDIFF(w1, w5);
250  const int cond01 = (P(0xdb,0x49) || P(0xef,0x6d)) && WDIFF(w7, w3);
251  const int cond02 = (P(0x6f,0x2a) || P(0x5b,0x0a) || P(0xbf,0x3a) ||
252  P(0xdf,0x5a) || P(0x9f,0x8a) || P(0xcf,0x8a) ||
253  P(0xef,0x4e) || P(0x3f,0x0e) || P(0xfb,0x5a) ||
254  P(0xbb,0x8a) || P(0x7f,0x5a) || P(0xaf,0x8a) ||
255  P(0xeb,0x8a)) && WDIFF(w3, w1);
256  const int cond03 = P(0xdb,0x49) || P(0xef,0x6d);
257  const int cond04 = P(0xbf,0x37) || P(0xdb,0x13);
258  const int cond05 = P(0x1b,0x03) || P(0x4f,0x43) || P(0x8b,0x83) ||
259  P(0x6b,0x43);
260  const int cond06 = P(0x4b,0x09) || P(0x8b,0x89) || P(0x1f,0x19) ||
261  P(0x3b,0x19);
262  const int cond07 = P(0x0b,0x08) || P(0xf9,0x68) || P(0xf3,0x62) ||
263  P(0x6d,0x6c) || P(0x67,0x66) || P(0x3d,0x3c) ||
264  P(0x37,0x36) || P(0xf9,0xf8) || P(0xdd,0xdc) ||
265  P(0xf3,0xf2) || P(0xd7,0xd6) || P(0xdd,0x1c) ||
266  P(0xd7,0x16) || P(0x0b,0x02);
267  const int cond08 = (P(0x0f,0x0b) || P(0x2b,0x0b) || P(0xfe,0x4a) ||
268  P(0xfe,0x1a)) && WDIFF(w3, w1);
269  const int cond09 = P(0x2f,0x2f);
270  const int cond10 = P(0x0a,0x00);
271  const int cond11 = P(0x0b,0x09);
272  const int cond12 = P(0x7e,0x2a) || P(0xef,0xab);
273  const int cond13 = P(0xbf,0x8f) || P(0x7e,0x0e);
274  const int cond14 = P(0x4f,0x4b) || P(0x9f,0x1b) || P(0x2f,0x0b) ||
275  P(0xbe,0x0a) || P(0xee,0x0a) || P(0x7e,0x0a) ||
276  P(0xeb,0x4b) || P(0x3b,0x1b);
277  const int cond15 = P(0x0b,0x03);
278 
279  if (cond00)
280  *dst00 = interp_2px(w4, 5, w3, 3, 3);
281  else if (cond01)
282  *dst00 = interp_2px(w4, 5, w1, 3, 3);
283  else if ((P(0x0b,0x0b) || P(0xfe,0x4a) || P(0xfe,0x1a)) && WDIFF(w3, w1))
284  *dst00 = w4;
285  else if (cond02)
286  *dst00 = interp_2px(w4, 5, w0, 3, 3);
287  else if (cond03)
288  *dst00 = interp_2px(w4, 3, w3, 1, 2);
289  else if (cond04)
290  *dst00 = interp_2px(w4, 3, w1, 1, 2);
291  else if (cond05)
292  *dst00 = interp_2px(w4, 5, w3, 3, 3);
293  else if (cond06)
294  *dst00 = interp_2px(w4, 5, w1, 3, 3);
295  else if (P(0x0f,0x0b) || P(0x5e,0x0a) || P(0x2b,0x0b) || P(0xbe,0x0a) ||
296  P(0x7a,0x0a) || P(0xee,0x0a))
297  *dst00 = interp_2px(w1, 1, w3, 1, 1);
298  else if (cond07)
299  *dst00 = interp_2px(w4, 5, w0, 3, 3);
300  else
301  *dst00 = interp_3px(w4, 2, w1, 1, w3, 1, 2);
302 
303  if (cond00)
304  *dst01 = interp_2px(w4, 7, w3, 1, 3);
305  else if (cond08)
306  *dst01 = w4;
307  else if (cond02)
308  *dst01 = interp_2px(w4, 3, w0, 1, 2);
309  else if (cond09)
310  *dst01 = w4;
311  else if (cond10)
312  *dst01 = interp_3px(w4, 5, w1, 2, w3, 1, 3);
313  else if (P(0x0b,0x08))
314  *dst01 = interp_3px(w4, 5, w1, 2, w0, 1, 3);
315  else if (cond11)
316  *dst01 = interp_2px(w4, 5, w1, 3, 3);
317  else if (cond04)
318  *dst01 = interp_2px(w1, 3, w4, 1, 2);
319  else if (cond12)
320  *dst01 = interp_3px(w1, 2, w4, 1, w3, 1, 2);
321  else if (cond13)
322  *dst01 = interp_2px(w1, 5, w3, 3, 3);
323  else if (cond05)
324  *dst01 = interp_2px(w4, 7, w3, 1, 3);
325  else if (P(0xf3,0x62) || P(0x67,0x66) || P(0x37,0x36) || P(0xf3,0xf2) ||
326  P(0xd7,0xd6) || P(0xd7,0x16) || P(0x0b,0x02))
327  *dst01 = interp_2px(w4, 3, w0, 1, 2);
328  else if (cond14)
329  *dst01 = interp_2px(w1, 1, w4, 1, 1);
330  else
331  *dst01 = interp_2px(w4, 3, w1, 1, 2);
332 
333  if (cond01)
334  *dst10 = interp_2px(w4, 7, w1, 1, 3);
335  else if (cond08)
336  *dst10 = w4;
337  else if (cond02)
338  *dst10 = interp_2px(w4, 3, w0, 1, 2);
339  else if (cond09)
340  *dst10 = w4;
341  else if (cond10)
342  *dst10 = interp_3px(w4, 5, w3, 2, w1, 1, 3);
343  else if (P(0x0b,0x02))
344  *dst10 = interp_3px(w4, 5, w3, 2, w0, 1, 3);
345  else if (cond15)
346  *dst10 = interp_2px(w4, 5, w3, 3, 3);
347  else if (cond03)
348  *dst10 = interp_2px(w3, 3, w4, 1, 2);
349  else if (cond13)
350  *dst10 = interp_3px(w3, 2, w4, 1, w1, 1, 2);
351  else if (cond12)
352  *dst10 = interp_2px(w3, 5, w1, 3, 3);
353  else if (cond06)
354  *dst10 = interp_2px(w4, 7, w1, 1, 3);
355  else if (P(0x0b,0x08) || P(0xf9,0x68) || P(0x6d,0x6c) || P(0x3d,0x3c) ||
356  P(0xf9,0xf8) || P(0xdd,0xdc) || P(0xdd,0x1c))
357  *dst10 = interp_2px(w4, 3, w0, 1, 2);
358  else if (cond14)
359  *dst10 = interp_2px(w3, 1, w4, 1, 1);
360  else
361  *dst10 = interp_2px(w4, 3, w3, 1, 2);
362 
363  if ((P(0x7f,0x2b) || P(0xef,0xab) || P(0xbf,0x8f) || P(0x7f,0x0f)) &&
364  WDIFF(w3, w1))
365  *dst11 = w4;
366  else if (cond02)
367  *dst11 = interp_2px(w4, 7, w0, 1, 3);
368  else if (cond15)
369  *dst11 = interp_2px(w4, 7, w3, 1, 3);
370  else if (cond11)
371  *dst11 = interp_2px(w4, 7, w1, 1, 3);
372  else if (P(0x0a,0x00) || P(0x7e,0x2a) || P(0xef,0xab) || P(0xbf,0x8f) ||
373  P(0x7e,0x0e))
374  *dst11 = interp_3px(w4, 6, w3, 1, w1, 1, 3);
375  else if (cond07)
376  *dst11 = interp_2px(w4, 7, w0, 1, 3);
377  else
378  *dst11 = w4;
379 }
380 
381 static av_always_inline void hqx_filter(const ThreadData *td, int jobnr, int nb_jobs, int n)
382 {
383  int x, y;
384  AVFrame *in = td->in, *out = td->out;
385  const uint32_t *r2y = td->rgbtoyuv;
386  const int height = in->height;
387  const int width = in->width;
388  const int slice_start = (height * jobnr ) / nb_jobs;
389  const int slice_end = (height * (jobnr+1)) / nb_jobs;
390  const int dst_linesize = out->linesize[0];
391  const int src_linesize = in->linesize[0];
392  uint8_t *dst = out->data[0] + slice_start * dst_linesize * n;
393  const uint8_t *src = in->data[0] + slice_start * src_linesize;
394 
395  const int dst32_linesize = dst_linesize >> 2;
396  const int src32_linesize = src_linesize >> 2;
397 
398  for (y = slice_start; y < slice_end; y++) {
399  const uint32_t *src32 = (const uint32_t *)src;
400  uint32_t *dst32 = (uint32_t *)dst;
401  const int prevline = y > 0 ? -src32_linesize : 0;
402  const int nextline = y < height - 1 ? src32_linesize : 0;
403 
404  for (x = 0; x < width; x++) {
405  const int prevcol = x > 0 ? -1 : 0;
406  const int nextcol = x < width -1 ? 1 : 0;
407  const uint32_t w[3*3] = {
408  src32[prevcol + prevline], src32[prevline], src32[prevline + nextcol],
409  src32[prevcol ], src32[ 0], src32[ nextcol],
410  src32[prevcol + nextline], src32[nextline], src32[nextline + nextcol]
411  };
412  const uint32_t yuv1 = rgb2yuv(r2y, w[4]);
413  const int pattern = (w[4] != w[0] ? (yuv_diff(yuv1, rgb2yuv(r2y, w[0]))) : 0)
414  | (w[4] != w[1] ? (yuv_diff(yuv1, rgb2yuv(r2y, w[1]))) : 0) << 1
415  | (w[4] != w[2] ? (yuv_diff(yuv1, rgb2yuv(r2y, w[2]))) : 0) << 2
416  | (w[4] != w[3] ? (yuv_diff(yuv1, rgb2yuv(r2y, w[3]))) : 0) << 3
417  | (w[4] != w[5] ? (yuv_diff(yuv1, rgb2yuv(r2y, w[5]))) : 0) << 4
418  | (w[4] != w[6] ? (yuv_diff(yuv1, rgb2yuv(r2y, w[6]))) : 0) << 5
419  | (w[4] != w[7] ? (yuv_diff(yuv1, rgb2yuv(r2y, w[7]))) : 0) << 6
420  | (w[4] != w[8] ? (yuv_diff(yuv1, rgb2yuv(r2y, w[8]))) : 0) << 7;
421 
422  if (n == 2) {
423  dst32[dst32_linesize*0 + 0] = hq2x_interp_1x1(r2y, pattern, w, 0,1,2,3,4,5,6,7,8); // 00
424  dst32[dst32_linesize*0 + 1] = hq2x_interp_1x1(r2y, pattern, w, 2,1,0,5,4,3,8,7,6); // 01 (vert mirrored)
425  dst32[dst32_linesize*1 + 0] = hq2x_interp_1x1(r2y, pattern, w, 6,7,8,3,4,5,0,1,2); // 10 (horiz mirrored)
426  dst32[dst32_linesize*1 + 1] = hq2x_interp_1x1(r2y, pattern, w, 8,7,6,5,4,3,2,1,0); // 11 (center mirrored)
427  } else if (n == 3) {
428  hq3x_interp_2x1(dst32, dst32_linesize, r2y, pattern, w, 0,1, 0,1,2,3,4,5,6,7,8, 0); // 00 01
429  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)
430  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)
431  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)
432  dst32[dst32_linesize + 1] = w[4]; // 11
433  } else if (n == 4) {
434  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
435  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)
436  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)
437  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)
438  } else {
439  av_assert0(0);
440  }
441 
442  src32 += 1;
443  dst32 += n;
444  }
445 
446  src += src_linesize;
447  dst += dst_linesize * n;
448  }
449 }
450 
451 #define HQX_FUNC(size) \
452 static int hq##size##x(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) \
453 { \
454  hqx_filter(arg, jobnr, nb_jobs, size); \
455  return 0; \
456 }
457 
458 HQX_FUNC(2)
459 HQX_FUNC(3)
460 HQX_FUNC(4)
461 
463 {
464  static const enum AVPixelFormat pix_fmts[] = {AV_PIX_FMT_RGB32, AV_PIX_FMT_NONE};
466  return 0;
467 }
468 
469 static int config_output(AVFilterLink *outlink)
470 {
471  AVFilterContext *ctx = outlink->src;
472  HQXContext *hqx = ctx->priv;
473  AVFilterLink *inlink = ctx->inputs[0];
474 
475  outlink->w = inlink->w * hqx->n;
476  outlink->h = inlink->h * hqx->n;
477  av_log(inlink->dst, AV_LOG_VERBOSE, "fmt:%s size:%dx%d -> size:%dx%d\n",
478  av_get_pix_fmt_name(inlink->format),
479  inlink->w, inlink->h, outlink->w, outlink->h);
480  return 0;
481 }
482 
483 static int filter_frame(AVFilterLink *inlink, AVFrame *in)
484 {
485  AVFilterContext *ctx = inlink->dst;
486  AVFilterLink *outlink = ctx->outputs[0];
487  HQXContext *hqx = ctx->priv;
488  ThreadData td;
489  AVFrame *out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
490  if (!out) {
491  av_frame_free(&in);
492  return AVERROR(ENOMEM);
493  }
494  av_frame_copy_props(out, in);
495  out->width = outlink->w;
496  out->height = outlink->h;
497 
498  td.in = in;
499  td.out = out;
500  td.rgbtoyuv = hqx->rgbtoyuv;
501  ctx->internal->execute(ctx, hqx->func, &td, NULL, FFMIN(inlink->h, ctx->graph->nb_threads));
502 
503  av_frame_free(&in);
504  return ff_filter_frame(outlink, out);
505 }
506 
507 static av_cold int init(AVFilterContext *ctx)
508 {
509  HQXContext *hqx = ctx->priv;
510  static const hqxfunc_t hqxfuncs[] = {hq2x, hq3x, hq4x};
511 
512  uint32_t c;
513  int bg, rg, g;
514 
515  for (bg=-255; bg<256; bg++) {
516  for (rg=-255; rg<256; rg++) {
517  const uint32_t u = (uint32_t)((-169*rg + 500*bg)/1000) + 128;
518  const uint32_t v = (uint32_t)(( 500*rg - 81*bg)/1000) + 128;
519  int startg = FFMAX3(-bg, -rg, 0);
520  int endg = FFMIN3(255-bg, 255-rg, 255);
521  uint32_t y = (uint32_t)(( 299*rg + 1000*startg + 114*bg)/1000);
522  c = bg + (rg<<16) + 0x010101 * startg;
523  for (g = startg; g <= endg; g++) {
524  hqx->rgbtoyuv[c] = ((y++) << 16) + (u << 8) + v;
525  c+= 0x010101;
526  }
527  }
528  }
529 
530  hqx->func = hqxfuncs[hqx->n - 2];
531  return 0;
532 }
533 
534 static const AVFilterPad hqx_inputs[] = {
535  {
536  .name = "default",
537  .type = AVMEDIA_TYPE_VIDEO,
538  .filter_frame = filter_frame,
539  },
540  { NULL }
541 };
542 
543 static const AVFilterPad hqx_outputs[] = {
544  {
545  .name = "default",
546  .type = AVMEDIA_TYPE_VIDEO,
547  .config_props = config_output,
548  },
549  { NULL }
550 };
551 
553  .name = "hqx",
554  .description = NULL_IF_CONFIG_SMALL("Scale the input by 2, 3 or 4 using the hq*x magnification algorithm."),
555  .priv_size = sizeof(HQXContext),
556  .init = init,
558  .inputs = hqx_inputs,
559  .outputs = hqx_outputs,
560  .priv_class = &hqx_class,
562 };