FFmpeg
Loading...
Searching...
No Matches
vf_hqx.c
Go to the documentation of this file.
1/*
2 * Copyright (c) 2014 Clément Bœsch
3 *
4 * This file is part of FFmpeg.
5 *
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19/**
20 * @file
21 * hqx magnification filters (hq2x, hq3x, hq4x)
22 *
23 * Originally designed by Maxim Stephin.
24 *
25 * @see http://en.wikipedia.org/wiki/Hqx
26 * @see http://web.archive.org/web/20131114143602/http://www.hiend3d.com/hq3x.html
27 * @see http://blog.pkh.me/p/19-butchering-hqx-scaling-filters.html
28 */
29
30#include "libavutil/opt.h"
31#include "libavutil/avassert.h"
32#include "libavutil/pixdesc.h"
33#include "filters.h"
34#include "video.h"
35
36typedef int (*hqxfunc_t)(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs);
37
38typedef struct HQXContext {
39 const AVClass *class;
40 int n;
42 uint32_t rgbtoyuv[1<<24];
44
45typedef struct ThreadData {
46 AVFrame *in, *out;
47 const uint32_t *rgbtoyuv;
49
50#define OFFSET(x) offsetof(HQXContext, x)
51#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
52static const AVOption hqx_options[] = {
53 { "n", "set scale factor", OFFSET(n), AV_OPT_TYPE_INT, {.i64 = 3}, 2, 4, .flags = FLAGS },
54 { NULL }
55};
56
58
59static av_always_inline uint32_t rgb2yuv(const uint32_t *r2y, uint32_t c)
60{
61 return r2y[c & 0xffffff];
62}
63
64static av_always_inline int yuv_diff(uint32_t yuv1, uint32_t yuv2)
65{
66#define YMASK 0xff0000
67#define UMASK 0x00ff00
68#define VMASK 0x0000ff
69#define ABSDIFF(a,b) (abs((int)(a)-(int)(b)))
70
71 return ABSDIFF(yuv1 & YMASK, yuv2 & YMASK) > (48 << 16) ||
72 ABSDIFF(yuv1 & UMASK, yuv2 & UMASK) > ( 7 << 8) ||
73 ABSDIFF(yuv1 & VMASK, yuv2 & VMASK) > ( 6 << 0);
74}
75
76/* (c1*w1 + c2*w2) >> s */
77static av_always_inline uint32_t interp_2px(uint32_t c1, int w1, uint32_t c2, int w2, int s)
78{
79 return (((((c1 & 0xff00ff00) >> 8) * w1 + ((c2 & 0xff00ff00) >> 8) * w2) << (8 - s)) & 0xff00ff00) |
80 (((((c1 & 0x00ff00ff) ) * w1 + ((c2 & 0x00ff00ff) ) * w2) >> s ) & 0x00ff00ff);
81}
82
83/* (c1*w1 + c2*w2 + c3*w3) >> s */
84static av_always_inline uint32_t interp_3px(uint32_t c1, int w1, uint32_t c2, int w2, uint32_t c3, int w3, int s)
85{
86 return (((((c1 & 0xff00ff00) >> 8) * w1 + ((c2 & 0xff00ff00) >> 8) * w2 + ((c3 & 0xff00ff00) >> 8) * w3) << (8 - s)) & 0xff00ff00) |
87 (((((c1 & 0x00ff00ff) ) * w1 + ((c2 & 0x00ff00ff) ) * w2 + ((c3 & 0x00ff00ff) ) * w3) >> s ) & 0x00ff00ff);
88}
89
90/* m is the mask of diff with the center pixel that matters in the pattern, and
91 * r is the expected result (bit set to 1 if there is difference with the
92 * center, 0 otherwise) */
93#define P(m, r) ((k_shuffled & (m)) == (r))
94
95/* adjust 012345678 to 01235678: the mask doesn't contain the (null) diff
96 * between the center/current pixel and itself */
97#define DROP4(z) ((z) > 4 ? (z)-1 : (z))
98
99/* shuffle the input mask: move bit n (4-adjusted) to position stored in p<n> */
100#define SHF(x, rot, n) (((x) >> ((rot) ? 7-DROP4(n) : DROP4(n)) & 1) << DROP4(p##n))
101
102/* used to check if there is YUV difference between 2 pixels */
103#define WDIFF(c1, c2) yuv_diff(rgb2yuv(r2y, c1), rgb2yuv(r2y, c2))
104
105/* bootstrap template for every interpolation code. It defines the shuffled
106 * masks and surrounding pixels. The rot flag is used to indicate if it's a
107 * rotation; its basic effect is to shuffle k using p8..p0 instead of p0..p8 */
108#define INTERP_BOOTSTRAP(rot) \
109 const int k_shuffled = SHF(k,rot,0) | SHF(k,rot,1) | SHF(k,rot,2) \
110 | SHF(k,rot,3) | 0 | SHF(k,rot,5) \
111 | SHF(k,rot,6) | SHF(k,rot,7) | SHF(k,rot,8); \
112 \
113 const uint32_t w0 = w[p0], w1 = w[p1], \
114 w3 = w[p3], w4 = w[p4], w5 = w[p5], \
115 w7 = w[p7]
116
117/* Assuming p0..p8 is mapped to pixels 0..8, this function interpolates the
118 * top-left pixel in the total of the 2x2 pixels to interpolates. The function
119 * is also used for the 3 other pixels */
120static av_always_inline uint32_t hq2x_interp_1x1(const uint32_t *r2y, int k,
121 const uint32_t *w,
122 int p0, int p1, int p2,
123 int p3, int p4, int p5,
124 int p6, int p7, int p8)
125{
127
128 if ((P(0xbf,0x37) || P(0xdb,0x13)) && WDIFF(w1, w5))
129 return interp_2px(w4, 3, w3, 1, 2);
130 if ((P(0xdb,0x49) || P(0xef,0x6d)) && WDIFF(w7, w3))
131 return interp_2px(w4, 3, w1, 1, 2);
132 if ((P(0x0b,0x0b) || P(0xfe,0x4a) || P(0xfe,0x1a)) && WDIFF(w3, w1))
133 return w4;
134 if ((P(0x6f,0x2a) || P(0x5b,0x0a) || P(0xbf,0x3a) || P(0xdf,0x5a) ||
135 P(0x9f,0x8a) || P(0xcf,0x8a) || P(0xef,0x4e) || P(0x3f,0x0e) ||
136 P(0xfb,0x5a) || P(0xbb,0x8a) || P(0x7f,0x5a) || P(0xaf,0x8a) ||
137 P(0xeb,0x8a)) && WDIFF(w3, w1))
138 return interp_2px(w4, 3, w0, 1, 2);
139 if (P(0x0b,0x08))
140 return interp_3px(w4, 2, w0, 1, w1, 1, 2);
141 if (P(0x0b,0x02))
142 return interp_3px(w4, 2, w0, 1, w3, 1, 2);
143 if (P(0x2f,0x2f))
144 return interp_3px(w4, 14, w3, 1, w1, 1, 4);
145 if (P(0xbf,0x37) || P(0xdb,0x13))
146 return interp_3px(w4, 5, w1, 2, w3, 1, 3);
147 if (P(0xdb,0x49) || P(0xef,0x6d))
148 return interp_3px(w4, 5, w3, 2, w1, 1, 3);
149 if (P(0x1b,0x03) || P(0x4f,0x43) || P(0x8b,0x83) || P(0x6b,0x43))
150 return interp_2px(w4, 3, w3, 1, 2);
151 if (P(0x4b,0x09) || P(0x8b,0x89) || P(0x1f,0x19) || P(0x3b,0x19))
152 return interp_2px(w4, 3, w1, 1, 2);
153 if (P(0x7e,0x2a) || P(0xef,0xab) || P(0xbf,0x8f) || P(0x7e,0x0e))
154 return interp_3px(w4, 2, w3, 3, w1, 3, 3);
155 if (P(0xfb,0x6a) || P(0x6f,0x6e) || P(0x3f,0x3e) || P(0xfb,0xfa) ||
156 P(0xdf,0xde) || P(0xdf,0x1e))
157 return interp_2px(w4, 3, w0, 1, 2);
158 if (P(0x0a,0x00) || P(0x4f,0x4b) || P(0x9f,0x1b) || P(0x2f,0x0b) ||
159 P(0xbe,0x0a) || P(0xee,0x0a) || P(0x7e,0x0a) || P(0xeb,0x4b) ||
160 P(0x3b,0x1b))
161 return interp_3px(w4, 2, w3, 1, w1, 1, 2);
162 return interp_3px(w4, 6, w3, 1, w1, 1, 3);
163}
164
165/* Assuming p0..p8 is mapped to pixels 0..8, this function interpolates the
166 * top-left and top-center pixel in the total of the 3x3 pixels to
167 * interpolates. The function is also used for the 3 other couples of pixels
168 * defining the outline. The center pixel is not defined through this function,
169 * since it's just the same as the original value. */
170static av_always_inline void hq3x_interp_2x1(uint32_t *dst, int dst_linesize,
171 const uint32_t *r2y, int k,
172 const uint32_t *w,
173 int pos00, int pos01,
174 int p0, int p1, int p2,
175 int p3, int p4, int p5,
176 int p6, int p7, int p8,
177 int rotate)
178{
180
181 uint32_t *dst00 = &dst[dst_linesize*(pos00>>1) + (pos00&1)];
182 uint32_t *dst01 = &dst[dst_linesize*(pos01>>1) + (pos01&1)];
183
184 if ((P(0xdb,0x49) || P(0xef,0x6d)) && WDIFF(w7, w3))
185 *dst00 = interp_2px(w4, 3, w1, 1, 2);
186 else if ((P(0xbf,0x37) || P(0xdb,0x13)) && WDIFF(w1, w5))
187 *dst00 = interp_2px(w4, 3, w3, 1, 2);
188 else if ((P(0x0b,0x0b) || P(0xfe,0x4a) || P(0xfe,0x1a)) && WDIFF(w3, w1))
189 *dst00 = w4;
190 else if ((P(0x6f,0x2a) || P(0x5b,0x0a) || P(0xbf,0x3a) || P(0xdf,0x5a) ||
191 P(0x9f,0x8a) || P(0xcf,0x8a) || P(0xef,0x4e) || P(0x3f,0x0e) ||
192 P(0xfb,0x5a) || P(0xbb,0x8a) || P(0x7f,0x5a) || P(0xaf,0x8a) ||
193 P(0xeb,0x8a)) && WDIFF(w3, w1))
194 *dst00 = interp_2px(w4, 3, w0, 1, 2);
195 else if (P(0x4b,0x09) || P(0x8b,0x89) || P(0x1f,0x19) || P(0x3b,0x19))
196 *dst00 = interp_2px(w4, 3, w1, 1, 2);
197 else if (P(0x1b,0x03) || P(0x4f,0x43) || P(0x8b,0x83) || P(0x6b,0x43))
198 *dst00 = interp_2px(w4, 3, w3, 1, 2);
199 else if (P(0x7e,0x2a) || P(0xef,0xab) || P(0xbf,0x8f) || P(0x7e,0x0e))
200 *dst00 = interp_2px(w3, 1, w1, 1, 1);
201 else if (P(0x4f,0x4b) || P(0x9f,0x1b) || P(0x2f,0x0b) || P(0xbe,0x0a) ||
202 P(0xee,0x0a) || P(0x7e,0x0a) || P(0xeb,0x4b) || P(0x3b,0x1b))
203 *dst00 = interp_3px(w4, 2, w3, 7, w1, 7, 4);
204 else if (P(0x0b,0x08) || P(0xf9,0x68) || P(0xf3,0x62) || P(0x6d,0x6c) ||
205 P(0x67,0x66) || P(0x3d,0x3c) || P(0x37,0x36) || P(0xf9,0xf8) ||
206 P(0xdd,0xdc) || P(0xf3,0xf2) || P(0xd7,0xd6) || P(0xdd,0x1c) ||
207 P(0xd7,0x16) || P(0x0b,0x02))
208 *dst00 = interp_2px(w4, 3, w0, 1, 2);
209 else
210 *dst00 = interp_3px(w4, 2, w3, 1, w1, 1, 2);
211
212 if ((P(0xfe,0xde) || P(0x9e,0x16) || P(0xda,0x12) || P(0x17,0x16) ||
213 P(0x5b,0x12) || P(0xbb,0x12)) && WDIFF(w1, w5))
214 *dst01 = w4;
215 else if ((P(0x0f,0x0b) || P(0x5e,0x0a) || P(0xfb,0x7b) || P(0x3b,0x0b) ||
216 P(0xbe,0x0a) || P(0x7a,0x0a)) && WDIFF(w3, w1))
217 *dst01 = w4;
218 else if (P(0xbf,0x8f) || P(0x7e,0x0e) || P(0xbf,0x37) || P(0xdb,0x13))
219 *dst01 = interp_2px(w1, 3, w4, 1, 2);
220 else if (P(0x02,0x00) || P(0x7c,0x28) || P(0xed,0xa9) || P(0xf5,0xb4) ||
221 P(0xd9,0x90))
222 *dst01 = interp_2px(w4, 3, w1, 1, 2);
223 else if (P(0x4f,0x4b) || P(0xfb,0x7b) || P(0xfe,0x7e) || P(0x9f,0x1b) ||
224 P(0x2f,0x0b) || P(0xbe,0x0a) || P(0x7e,0x0a) || P(0xfb,0x4b) ||
225 P(0xfb,0xdb) || P(0xfe,0xde) || P(0xfe,0x56) || P(0x57,0x56) ||
226 P(0x97,0x16) || P(0x3f,0x1e) || P(0xdb,0x12) || P(0xbb,0x12))
227 *dst01 = interp_2px(w4, 7, w1, 1, 3);
228 else
229 *dst01 = w4;
230}
231
232/* Assuming p0..p8 is mapped to pixels 0..8, this function interpolates the
233 * top-left block of 2x2 pixels in the total of the 4x4 pixels (or 4 blocks) to
234 * interpolates. The function is also used for the 3 other blocks of 2x2
235 * pixels. */
236static av_always_inline void hq4x_interp_2x2(uint32_t *dst, int dst_linesize,
237 const uint32_t *r2y, int k,
238 const uint32_t *w,
239 int pos00, int pos01,
240 int pos10, int pos11,
241 int p0, int p1, int p2,
242 int p3, int p4, int p5,
243 int p6, int p7, int p8)
244{
246
247 uint32_t *dst00 = &dst[dst_linesize*(pos00>>1) + (pos00&1)];
248 uint32_t *dst01 = &dst[dst_linesize*(pos01>>1) + (pos01&1)];
249 uint32_t *dst10 = &dst[dst_linesize*(pos10>>1) + (pos10&1)];
250 uint32_t *dst11 = &dst[dst_linesize*(pos11>>1) + (pos11&1)];
251
252 const int cond00 = (P(0xbf,0x37) || P(0xdb,0x13)) && WDIFF(w1, w5);
253 const int cond01 = (P(0xdb,0x49) || P(0xef,0x6d)) && WDIFF(w7, w3);
254 const int cond02 = (P(0x6f,0x2a) || P(0x5b,0x0a) || P(0xbf,0x3a) ||
255 P(0xdf,0x5a) || P(0x9f,0x8a) || P(0xcf,0x8a) ||
256 P(0xef,0x4e) || P(0x3f,0x0e) || P(0xfb,0x5a) ||
257 P(0xbb,0x8a) || P(0x7f,0x5a) || P(0xaf,0x8a) ||
258 P(0xeb,0x8a)) && WDIFF(w3, w1);
259 const int cond03 = P(0xdb,0x49) || P(0xef,0x6d);
260 const int cond04 = P(0xbf,0x37) || P(0xdb,0x13);
261 const int cond05 = P(0x1b,0x03) || P(0x4f,0x43) || P(0x8b,0x83) ||
262 P(0x6b,0x43);
263 const int cond06 = P(0x4b,0x09) || P(0x8b,0x89) || P(0x1f,0x19) ||
264 P(0x3b,0x19);
265 const int cond07 = P(0x0b,0x08) || P(0xf9,0x68) || P(0xf3,0x62) ||
266 P(0x6d,0x6c) || P(0x67,0x66) || P(0x3d,0x3c) ||
267 P(0x37,0x36) || P(0xf9,0xf8) || P(0xdd,0xdc) ||
268 P(0xf3,0xf2) || P(0xd7,0xd6) || P(0xdd,0x1c) ||
269 P(0xd7,0x16) || P(0x0b,0x02);
270 const int cond08 = (P(0x0f,0x0b) || P(0x2b,0x0b) || P(0xfe,0x4a) ||
271 P(0xfe,0x1a)) && WDIFF(w3, w1);
272 const int cond09 = P(0x2f,0x2f);
273 const int cond10 = P(0x0a,0x00);
274 const int cond11 = P(0x0b,0x09);
275 const int cond12 = P(0x7e,0x2a) || P(0xef,0xab);
276 const int cond13 = P(0xbf,0x8f) || P(0x7e,0x0e);
277 const int cond14 = P(0x4f,0x4b) || P(0x9f,0x1b) || P(0x2f,0x0b) ||
278 P(0xbe,0x0a) || P(0xee,0x0a) || P(0x7e,0x0a) ||
279 P(0xeb,0x4b) || P(0x3b,0x1b);
280 const int cond15 = P(0x0b,0x03);
281
282 if (cond00)
283 *dst00 = interp_2px(w4, 5, w3, 3, 3);
284 else if (cond01)
285 *dst00 = interp_2px(w4, 5, w1, 3, 3);
286 else if ((P(0x0b,0x0b) || P(0xfe,0x4a) || P(0xfe,0x1a)) && WDIFF(w3, w1))
287 *dst00 = w4;
288 else if (cond02)
289 *dst00 = interp_2px(w4, 5, w0, 3, 3);
290 else if (cond03)
291 *dst00 = interp_2px(w4, 3, w3, 1, 2);
292 else if (cond04)
293 *dst00 = interp_2px(w4, 3, w1, 1, 2);
294 else if (cond05)
295 *dst00 = interp_2px(w4, 5, w3, 3, 3);
296 else if (cond06)
297 *dst00 = interp_2px(w4, 5, w1, 3, 3);
298 else if (P(0x0f,0x0b) || P(0x5e,0x0a) || P(0x2b,0x0b) || P(0xbe,0x0a) ||
299 P(0x7a,0x0a) || P(0xee,0x0a))
300 *dst00 = interp_2px(w1, 1, w3, 1, 1);
301 else if (cond07)
302 *dst00 = interp_2px(w4, 5, w0, 3, 3);
303 else
304 *dst00 = interp_3px(w4, 2, w1, 1, w3, 1, 2);
305
306 if (cond00)
307 *dst01 = interp_2px(w4, 7, w3, 1, 3);
308 else if (cond08)
309 *dst01 = w4;
310 else if (cond02)
311 *dst01 = interp_2px(w4, 3, w0, 1, 2);
312 else if (cond09)
313 *dst01 = w4;
314 else if (cond10)
315 *dst01 = interp_3px(w4, 5, w1, 2, w3, 1, 3);
316 else if (P(0x0b,0x08))
317 *dst01 = interp_3px(w4, 5, w1, 2, w0, 1, 3);
318 else if (cond11)
319 *dst01 = interp_2px(w4, 5, w1, 3, 3);
320 else if (cond04)
321 *dst01 = interp_2px(w1, 3, w4, 1, 2);
322 else if (cond12)
323 *dst01 = interp_3px(w1, 2, w4, 1, w3, 1, 2);
324 else if (cond13)
325 *dst01 = interp_2px(w1, 5, w3, 3, 3);
326 else if (cond05)
327 *dst01 = interp_2px(w4, 7, w3, 1, 3);
328 else if (P(0xf3,0x62) || P(0x67,0x66) || P(0x37,0x36) || P(0xf3,0xf2) ||
329 P(0xd7,0xd6) || P(0xd7,0x16) || P(0x0b,0x02))
330 *dst01 = interp_2px(w4, 3, w0, 1, 2);
331 else if (cond14)
332 *dst01 = interp_2px(w1, 1, w4, 1, 1);
333 else
334 *dst01 = interp_2px(w4, 3, w1, 1, 2);
335
336 if (cond01)
337 *dst10 = interp_2px(w4, 7, w1, 1, 3);
338 else if (cond08)
339 *dst10 = w4;
340 else if (cond02)
341 *dst10 = interp_2px(w4, 3, w0, 1, 2);
342 else if (cond09)
343 *dst10 = w4;
344 else if (cond10)
345 *dst10 = interp_3px(w4, 5, w3, 2, w1, 1, 3);
346 else if (P(0x0b,0x02))
347 *dst10 = interp_3px(w4, 5, w3, 2, w0, 1, 3);
348 else if (cond15)
349 *dst10 = interp_2px(w4, 5, w3, 3, 3);
350 else if (cond03)
351 *dst10 = interp_2px(w3, 3, w4, 1, 2);
352 else if (cond13)
353 *dst10 = interp_3px(w3, 2, w4, 1, w1, 1, 2);
354 else if (cond12)
355 *dst10 = interp_2px(w3, 5, w1, 3, 3);
356 else if (cond06)
357 *dst10 = interp_2px(w4, 7, w1, 1, 3);
358 else if (P(0x0b,0x08) || P(0xf9,0x68) || P(0x6d,0x6c) || P(0x3d,0x3c) ||
359 P(0xf9,0xf8) || P(0xdd,0xdc) || P(0xdd,0x1c))
360 *dst10 = interp_2px(w4, 3, w0, 1, 2);
361 else if (cond14)
362 *dst10 = interp_2px(w3, 1, w4, 1, 1);
363 else
364 *dst10 = interp_2px(w4, 3, w3, 1, 2);
365
366 if ((P(0x7f,0x2b) || P(0xef,0xab) || P(0xbf,0x8f) || P(0x7f,0x0f)) &&
367 WDIFF(w3, w1))
368 *dst11 = w4;
369 else if (cond02)
370 *dst11 = interp_2px(w4, 7, w0, 1, 3);
371 else if (cond15)
372 *dst11 = interp_2px(w4, 7, w3, 1, 3);
373 else if (cond11)
374 *dst11 = interp_2px(w4, 7, w1, 1, 3);
375 else if (P(0x0a,0x00) || P(0x7e,0x2a) || P(0xef,0xab) || P(0xbf,0x8f) ||
376 P(0x7e,0x0e))
377 *dst11 = interp_3px(w4, 6, w3, 1, w1, 1, 3);
378 else if (cond07)
379 *dst11 = interp_2px(w4, 7, w0, 1, 3);
380 else
381 *dst11 = w4;
382}
383
384static av_always_inline void hqx_filter(const ThreadData *td, int jobnr, int nb_jobs, int n)
385{
386 int x, y;
387 AVFrame *in = td->in, *out = td->out;
388 const uint32_t *r2y = td->rgbtoyuv;
389 const int height = in->height;
390 const int width = in->width;
391 const int slice_start = ff_slice_pos(height, jobnr, nb_jobs);
392 const int slice_end = ff_slice_pos(height, jobnr + 1, nb_jobs);
393 const int dst_linesize = out->linesize[0];
394 const int src_linesize = in->linesize[0];
395 uint8_t *dst = out->data[0] + slice_start * dst_linesize * n;
396 const uint8_t *src = in->data[0] + slice_start * src_linesize;
397
398 const int dst32_linesize = dst_linesize >> 2;
399 const int src32_linesize = src_linesize >> 2;
400
401 for (y = slice_start; y < slice_end; y++) {
402 const uint32_t *src32 = (const uint32_t *)src;
403 uint32_t *dst32 = (uint32_t *)dst;
404 const int prevline = y > 0 ? -src32_linesize : 0;
405 const int nextline = y < height - 1 ? src32_linesize : 0;
406
407 for (x = 0; x < width; x++) {
408 const int prevcol = x > 0 ? -1 : 0;
409 const int nextcol = x < width -1 ? 1 : 0;
410 const uint32_t w[3*3] = {
411 src32[prevcol + prevline], src32[prevline], src32[prevline + nextcol],
412 src32[prevcol ], src32[ 0], src32[ nextcol],
413 src32[prevcol + nextline], src32[nextline], src32[nextline + nextcol]
414 };
415 const uint32_t yuv1 = rgb2yuv(r2y, w[4]);
416 const int pattern = (w[4] != w[0] ? (yuv_diff(yuv1, rgb2yuv(r2y, w[0]))) : 0)
417 | (w[4] != w[1] ? (yuv_diff(yuv1, rgb2yuv(r2y, w[1]))) : 0) << 1
418 | (w[4] != w[2] ? (yuv_diff(yuv1, rgb2yuv(r2y, w[2]))) : 0) << 2
419 | (w[4] != w[3] ? (yuv_diff(yuv1, rgb2yuv(r2y, w[3]))) : 0) << 3
420 | (w[4] != w[5] ? (yuv_diff(yuv1, rgb2yuv(r2y, w[5]))) : 0) << 4
421 | (w[4] != w[6] ? (yuv_diff(yuv1, rgb2yuv(r2y, w[6]))) : 0) << 5
422 | (w[4] != w[7] ? (yuv_diff(yuv1, rgb2yuv(r2y, w[7]))) : 0) << 6
423 | (w[4] != w[8] ? (yuv_diff(yuv1, rgb2yuv(r2y, w[8]))) : 0) << 7;
424
425 if (n == 2) {
426 dst32[dst32_linesize*0 + 0] = hq2x_interp_1x1(r2y, pattern, w, 0,1,2,3,4,5,6,7,8); // 00
427 dst32[dst32_linesize*0 + 1] = hq2x_interp_1x1(r2y, pattern, w, 2,1,0,5,4,3,8,7,6); // 01 (vert mirrored)
428 dst32[dst32_linesize*1 + 0] = hq2x_interp_1x1(r2y, pattern, w, 6,7,8,3,4,5,0,1,2); // 10 (horiz mirrored)
429 dst32[dst32_linesize*1 + 1] = hq2x_interp_1x1(r2y, pattern, w, 8,7,6,5,4,3,2,1,0); // 11 (center mirrored)
430 } else if (n == 3) {
431 hq3x_interp_2x1(dst32, dst32_linesize, r2y, pattern, w, 0,1, 0,1,2,3,4,5,6,7,8, 0); // 00 01
432 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)
433 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)
434 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)
435 dst32[dst32_linesize + 1] = w[4]; // 11
436 } else if (n == 4) {
437 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
438 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)
439 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)
440 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)
441 } else {
442 av_assert0(0);
443 }
444
445 src32 += 1;
446 dst32 += n;
447 }
448
449 src += src_linesize;
450 dst += dst_linesize * n;
451 }
452}
453
454#define HQX_FUNC(size) \
455static int hq##size##x(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) \
456{ \
457 hqx_filter(arg, jobnr, nb_jobs, size); \
458 return 0; \
459}
460
461HQX_FUNC(2)
462HQX_FUNC(3)
463HQX_FUNC(4)
464
465static int config_output(AVFilterLink *outlink)
466{
467 AVFilterContext *ctx = outlink->src;
468 HQXContext *hqx = ctx->priv;
469 AVFilterLink *inlink = ctx->inputs[0];
470
471 outlink->w = inlink->w * hqx->n;
472 outlink->h = inlink->h * hqx->n;
473 av_log(inlink->dst, AV_LOG_VERBOSE, "fmt:%s size:%dx%d -> size:%dx%d\n",
475 inlink->w, inlink->h, outlink->w, outlink->h);
476 return 0;
477}
478
479static int filter_frame(AVFilterLink *inlink, AVFrame *in)
480{
481 AVFilterContext *ctx = inlink->dst;
482 AVFilterLink *outlink = ctx->outputs[0];
483 HQXContext *hqx = ctx->priv;
484 ThreadData td;
485 AVFrame *out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
486 if (!out) {
487 av_frame_free(&in);
488 return AVERROR(ENOMEM);
489 }
491 out->width = outlink->w;
492 out->height = outlink->h;
493
494 td.in = in;
495 td.out = out;
496 td.rgbtoyuv = hqx->rgbtoyuv;
497 ff_filter_execute(ctx, hqx->func, &td, NULL,
499
500 av_frame_free(&in);
501 return ff_filter_frame(outlink, out);
502}
503
505{
506 HQXContext *hqx = ctx->priv;
507 static const hqxfunc_t hqxfuncs[] = {hq2x, hq3x, hq4x};
508
509 uint32_t c;
510 int bg, rg, g;
511
512 for (bg=-255; bg<256; bg++) {
513 for (rg=-255; rg<256; rg++) {
514 const uint32_t u = (uint32_t)((-169*rg + 500*bg)/1000) + 128;
515 const uint32_t v = (uint32_t)(( 500*rg - 81*bg)/1000) + 128;
516 int startg = FFMAX3(-bg, -rg, 0);
517 int endg = FFMIN3(255-bg, 255-rg, 255);
518 uint32_t y = (uint32_t)(( 299*rg + 1000*startg + 114*bg)/1000);
519 c = bg + rg * (1 << 16) + 0x010101 * startg;
520 for (g = startg; g <= endg; g++) {
521 hqx->rgbtoyuv[c] = ((y++) << 16) + (u << 8) + v;
522 c+= 0x010101;
523 }
524 }
525 }
526
527 hqx->func = hqxfuncs[hqx->n - 2];
528 return 0;
529}
530
531static const AVFilterPad hqx_inputs[] = {
532 {
533 .name = "default",
534 .type = AVMEDIA_TYPE_VIDEO,
535 .filter_frame = filter_frame,
536 },
537};
538
539static const AVFilterPad hqx_outputs[] = {
540 {
541 .name = "default",
542 .type = AVMEDIA_TYPE_VIDEO,
543 .config_props = config_output,
544 },
545};
546
548 .p.name = "hqx",
549 .p.description = NULL_IF_CONFIG_SMALL("Scale the input by 2, 3 or 4 using the hq*x magnification algorithm."),
550 .p.priv_class = &hqx_class,
552 .priv_size = sizeof(HQXContext),
553 .init = init,
557};
uint8_t ptrdiff_t const uint8_t ptrdiff_t int intptr_t intptr_t int int16_t * dst
Definition dsp.h:87
const FFFilter ff_vf_hqx
Definition vf_hqx.c:547
static FILE * out
static AVFormatContext * ctx
simple assert() macros that are a bit more flexible than ISO C assert().
#define av_assert0(cond)
assert() equivalent, that is always enabled.
Definition avassert.h:42
int ff_filter_frame(AVFilterLink *link, AVFrame *frame)
Send a frame of data to the next filter.
Definition avfilter.c:1068
int ff_filter_execute(AVFilterContext *ctx, avfilter_action_func *func, void *arg, int *ret, int nb_jobs)
Definition avfilter.c:1696
int ff_filter_get_nb_threads(AVFilterContext *ctx)
Get number of threads for current filter instance.
Definition avfilter.c:846
#define s(width, name)
Definition cbs_vp9.c:198
#define FLAGS
Definition cmdutils.c:598
#define NULL
Definition coverity.c:32
static int filter_frame(DBEDecodeContext *s, AVFrame *frame)
Definition dolby_e.c:1067
int(* init)(AVBSFContext *ctx)
Definition dts2pts.c:608
@ AV_OPT_TYPE_INT
Underlying C type is int.
Definition opt.h:258
#define AVFILTER_FLAG_SLICE_THREADS
The filter supports multithreading by splitting frames into multiple parts and processing them concur...
Definition avfilter.h:166
#define AVERROR(e)
Definition error.h:45
void av_frame_free(AVFrame **frame)
Free the frame and any dynamically allocated objects in it, e.g.
Definition frame.c:64
int av_frame_copy_props(AVFrame *dst, const AVFrame *src)
Copy only "metadata" fields from src to dst.
Definition frame.c:599
#define AV_LOG_VERBOSE
Detailed information.
Definition log.h:226
@ AVMEDIA_TYPE_VIDEO
Definition avutil.h:200
static int config_output(AVBitStreamFilterLink *outlink)
#define u(width, name, range_min, range_max)
Definition cbs_apv.c:68
const char * arg
Definition jacosubdec.c:65
#define FILTER_INPUTS(array)
Definition filters.h:264
#define FILTER_OUTPUTS(array)
Definition filters.h:265
static int ff_slice_pos(int total, int jobnr, int nb_jobs)
Compute the boundary index for a slice when work of size total is split into nb_jobs slices.
Definition filters.h:763
#define FILTER_SINGLE_PIXFMT(pix_fmt_)
Definition filters.h:254
#define AVFILTER_DEFINE_CLASS(fname)
Definition filters.h:478
#define av_always_inline
Definition attributes.h:72
#define av_cold
Definition attributes.h:117
#define NULL_IF_CONFIG_SMALL(x)
Return NULL if CONFIG_SMALL is true, otherwise the argument without modification.
Definition internal.h:88
uint8_t w
Definition llvidencdsp.c:39
#define FFMAX3(a, b, c)
Definition macros.h:48
#define FFMIN(a, b)
Definition macros.h:49
#define FFMIN3(a, b, c)
Definition macros.h:50
static int slice_end(AVCodecContext *avctx, AVFrame *pict, int *got_output)
Handle slice ends.
Definition mpeg12dec.c:1697
static const uint64_t c2
Definition murmur3.c:53
static const uint64_t c1
Definition murmur3.c:52
AVOptions.
const char * av_get_pix_fmt_name(enum AVPixelFormat pix_fmt)
Return the short name for a pixel format, NULL in case pix_fmt is unknown.
Definition pixdesc.c:3380
#define AV_PIX_FMT_RGB32
Definition pixfmt.h:517
Describe the class of an AVClass context structure.
Definition log.h:76
An instance of a filter.
Definition avfilter.h:273
A filter pad used for either input or output.
Definition filters.h:40
This structure describes decoded (raw) audio or video data.
Definition frame.h:472
uint8_t * data[AV_NUM_DATA_POINTERS]
pointer to the picture/channel planes.
Definition frame.h:493
int width
Definition frame.h:544
int height
Definition frame.h:544
int linesize[AV_NUM_DATA_POINTERS]
For video, a positive or negative value, which is typically indicating the size in bytes of each pict...
Definition frame.h:517
AVOption.
Definition opt.h:428
int n
Definition vf_hqx.c:40
hqxfunc_t func
Definition vf_hqx.c:41
uint32_t rgbtoyuv[1<< 24]
Definition vf_hqx.c:42
Used for passing data between threads.
Definition dsddec.c:71
const uint32_t * rgbtoyuv
Definition vf_hqx.c:47
AVFrame * out
#define av_log(a,...)
#define src
Definition vp8dsp.c:248
#define height
Definition dsp.h:89
#define width
Definition dsp.h:89
const char * g
Definition vf_curves.c:128
static av_always_inline void hqx_filter(const ThreadData *td, int jobnr, int nb_jobs, int n)
Definition vf_hqx.c:384
static av_always_inline uint32_t rgb2yuv(const uint32_t *r2y, uint32_t c)
Definition vf_hqx.c:59
static av_always_inline void 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)
Definition vf_hqx.c:170
#define INTERP_BOOTSTRAP(rot)
Definition vf_hqx.c:108
static av_always_inline int yuv_diff(uint32_t yuv1, uint32_t yuv2)
Definition vf_hqx.c:64
#define ABSDIFF(a, b)
static av_always_inline void 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)
Definition vf_hqx.c:236
static const AVFilterPad hqx_outputs[]
Definition vf_hqx.c:539
#define YMASK
static int filter_frame(AVFilterLink *inlink, AVFrame *in)
Definition vf_hqx.c:479
#define VMASK
static av_always_inline uint32_t interp_2px(uint32_t c1, int w1, uint32_t c2, int w2, int s)
Definition vf_hqx.c:77
static const AVFilterPad hqx_inputs[]
Definition vf_hqx.c:531
#define UMASK
#define OFFSET(x)
Definition vf_hqx.c:50
#define P(m, r)
Definition vf_hqx.c:93
#define WDIFF(c1, c2)
Definition vf_hqx.c:103
int(* hqxfunc_t)(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
Definition vf_hqx.c:36
static av_always_inline uint32_t 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)
Definition vf_hqx.c:120
static const AVOption hqx_options[]
Definition vf_hqx.c:52
#define HQX_FUNC(size)
Definition vf_hqx.c:454
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)
Definition vf_hqx.c:84
static void rotate(const float rot_quaternion[2][4], float *vec)
Rotate vector with given rotation quaternion.
Definition vf_v360.c:4081
AVFrame * ff_get_video_buffer(AVFilterLink *link, int w, int h)
Request a picture buffer with a specific set of permissions.
Definition video.c:89
static double c[64]
static int slice_start(SliceContext *sc, VVCContext *s, VVCFrameContext *fc, const CodedBitstreamUnit *unit, const int is_first_slice)
Definition dec.c:844