]> git.sesse.net Git - ffmpeg/blob - libavfilter/vf_hqx.c
fate/hevc: update fate rext tests
[ffmpeg] / 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  * @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;
40     hqxfunc_t func;
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
56 AVFILTER_DEFINE_CLASS(hqx);
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
462 static int query_formats(AVFilterContext *ctx)
463 {
464     static const enum AVPixelFormat pix_fmts[] = {AV_PIX_FMT_RGB32, AV_PIX_FMT_NONE};
465     ff_set_common_formats(ctx, ff_make_format_list(pix_fmts));
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
552 AVFilter ff_vf_hqx = {
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,
557     .query_formats = query_formats,
558     .inputs        = hqx_inputs,
559     .outputs       = hqx_outputs,
560     .priv_class    = &hqx_class,
561     .flags         = AVFILTER_FLAG_SLICE_THREADS,
562 };