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