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