2 * Copyright (c) 2014 Clément Bœsch
4 * This file is part of FFmpeg.
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.
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.
21 * hqx magnification filters (hq2x, hq3x, hq4x)
23 * Originally designed by Maxim Stephin.
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
30 #include "libavutil/opt.h"
31 #include "libavutil/avassert.h"
32 #include "libavutil/pixdesc.h"
35 typedef int (*hqxfunc_t)(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs);
41 uint32_t rgbtoyuv[1<<24];
44 typedef struct ThreadData {
46 const uint32_t *rgbtoyuv;
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 },
56 AVFILTER_DEFINE_CLASS(hqx);
58 static av_always_inline uint32_t rgb2yuv(const uint32_t *r2y, uint32_t c)
60 return r2y[c & 0xffffff];
63 static av_always_inline int yuv_diff(uint32_t yuv1, uint32_t yuv2)
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);
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)
76 return (((((c1 & 0xff00ff00) >> 8) * w1 + ((c2 & 0xff00ff00) >> 8) * w2) << (8 - s)) & 0xff00ff00) |
77 (((((c1 & 0x00ff00ff) ) * w1 + ((c2 & 0x00ff00ff) ) * w2) >> s ) & 0x00ff00ff);
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)
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);
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))
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))
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))
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))
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); \
110 const uint32_t w0 = w[p0], w1 = w[p1], \
111 w3 = w[p3], w4 = w[p4], w5 = w[p5], \
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,
119 int p0, int p1, int p2,
120 int p3, int p4, int p5,
121 int p6, int p7, int p8)
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))
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);
137 return interp_3px(w4, 2, w0, 1, w1, 1, 2);
139 return interp_3px(w4, 2, w0, 1, w3, 1, 2);
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) ||
158 return interp_3px(w4, 2, w3, 1, w1, 1, 2);
159 return interp_3px(w4, 6, w3, 1, w1, 1, 3);
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,
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,
176 INTERP_BOOTSTRAP(rotate);
178 uint32_t *dst00 = &dst[dst_linesize*(pos00>>1) + (pos00&1)];
179 uint32_t *dst01 = &dst[dst_linesize*(pos01>>1) + (pos01&1)];
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))
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);
207 *dst00 = interp_3px(w4, 2, w3, 1, w1, 1, 2);
209 if ((P(0xfe,0xde) || P(0x9e,0x16) || P(0xda,0x12) || P(0x17,0x16) ||
210 P(0x5b,0x12) || P(0xbb,0x12)) && WDIFF(w1, w5))
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))
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) ||
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);
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
233 static av_always_inline void hq4x_interp_2x2(uint32_t *dst, int dst_linesize,
234 const uint32_t *r2y, int k,
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)
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)];
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) ||
260 const int cond06 = P(0x4b,0x09) || P(0x8b,0x89) || P(0x1f,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);
280 *dst00 = interp_2px(w4, 5, w3, 3, 3);
282 *dst00 = interp_2px(w4, 5, w1, 3, 3);
283 else if ((P(0x0b,0x0b) || P(0xfe,0x4a) || P(0xfe,0x1a)) && WDIFF(w3, w1))
286 *dst00 = interp_2px(w4, 5, w0, 3, 3);
288 *dst00 = interp_2px(w4, 3, w3, 1, 2);
290 *dst00 = interp_2px(w4, 3, w1, 1, 2);
292 *dst00 = interp_2px(w4, 5, w3, 3, 3);
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);
299 *dst00 = interp_2px(w4, 5, w0, 3, 3);
301 *dst00 = interp_3px(w4, 2, w1, 1, w3, 1, 2);
304 *dst01 = interp_2px(w4, 7, w3, 1, 3);
308 *dst01 = interp_2px(w4, 3, w0, 1, 2);
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);
316 *dst01 = interp_2px(w4, 5, w1, 3, 3);
318 *dst01 = interp_2px(w1, 3, w4, 1, 2);
320 *dst01 = interp_3px(w1, 2, w4, 1, w3, 1, 2);
322 *dst01 = interp_2px(w1, 5, w3, 3, 3);
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);
329 *dst01 = interp_2px(w1, 1, w4, 1, 1);
331 *dst01 = interp_2px(w4, 3, w1, 1, 2);
334 *dst10 = interp_2px(w4, 7, w1, 1, 3);
338 *dst10 = interp_2px(w4, 3, w0, 1, 2);
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);
346 *dst10 = interp_2px(w4, 5, w3, 3, 3);
348 *dst10 = interp_2px(w3, 3, w4, 1, 2);
350 *dst10 = interp_3px(w3, 2, w4, 1, w1, 1, 2);
352 *dst10 = interp_2px(w3, 5, w1, 3, 3);
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);
359 *dst10 = interp_2px(w3, 1, w4, 1, 1);
361 *dst10 = interp_2px(w4, 3, w3, 1, 2);
363 if ((P(0x7f,0x2b) || P(0xef,0xab) || P(0xbf,0x8f) || P(0x7f,0x0f)) &&
367 *dst11 = interp_2px(w4, 7, w0, 1, 3);
369 *dst11 = interp_2px(w4, 7, w3, 1, 3);
371 *dst11 = interp_2px(w4, 7, w1, 1, 3);
372 else if (P(0x0a,0x00) || P(0x7e,0x2a) || P(0xef,0xab) || P(0xbf,0x8f) ||
374 *dst11 = interp_3px(w4, 6, w3, 1, w1, 1, 3);
376 *dst11 = interp_2px(w4, 7, w0, 1, 3);
381 static av_always_inline void hqx_filter(const ThreadData *td, int jobnr, int nb_jobs, int n)
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;
395 const int dst32_linesize = dst_linesize >> 2;
396 const int src32_linesize = src_linesize >> 2;
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;
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]
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;
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)
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
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)
447 dst += dst_linesize * n;
451 #define HQX_FUNC(size) \
452 static int hq##size##x(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) \
454 hqx_filter(arg, jobnr, nb_jobs, size); \
462 static int query_formats(AVFilterContext *ctx)
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));
469 static int config_output(AVFilterLink *outlink)
471 AVFilterContext *ctx = outlink->src;
472 HQXContext *hqx = ctx->priv;
473 AVFilterLink *inlink = ctx->inputs[0];
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);
483 static int filter_frame(AVFilterLink *inlink, AVFrame *in)
485 AVFilterContext *ctx = inlink->dst;
486 AVFilterLink *outlink = ctx->outputs[0];
487 HQXContext *hqx = ctx->priv;
489 AVFrame *out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
492 return AVERROR(ENOMEM);
494 av_frame_copy_props(out, in);
495 out->width = outlink->w;
496 out->height = outlink->h;
500 td.rgbtoyuv = hqx->rgbtoyuv;
501 ctx->internal->execute(ctx, hqx->func, &td, NULL, FFMIN(inlink->h, ctx->graph->nb_threads));
504 return ff_filter_frame(outlink, out);
507 static av_cold int init(AVFilterContext *ctx)
509 HQXContext *hqx = ctx->priv;
510 static const hqxfunc_t hqxfuncs[] = {hq2x, hq3x, hq4x};
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;
530 hqx->func = hqxfuncs[hqx->n - 2];
534 static const AVFilterPad hqx_inputs[] = {
537 .type = AVMEDIA_TYPE_VIDEO,
538 .filter_frame = filter_frame,
543 static const AVFilterPad hqx_outputs[] = {
546 .type = AVMEDIA_TYPE_VIDEO,
547 .config_props = config_output,
552 AVFilter ff_vf_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),
557 .query_formats = query_formats,
558 .inputs = hqx_inputs,
559 .outputs = hqx_outputs,
560 .priv_class = &hqx_class,
561 .flags = AVFILTER_FLAG_SLICE_THREADS,