2 * This file is part of FFmpeg.
4 * Copyright (c) 2011, 2012 Hyllian/Jararaca <sergiogdb@gmail.com>
5 * Copyright (c) 2014 Arwa Arif <arwaarif1994@gmail.com>
7 * FFmpeg is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * FFmpeg is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with FFmpeg; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
24 * XBR Filter is used for depixelization of image.
25 * This is based on Hyllian's xBR shader.
27 * @see http://www.libretro.com/forums/viewtopic.php?f=6&t=134
28 * @see https://github.com/yoyofr/iFBA/blob/master/fba_src/src/intf/video/scalers/xbr.cpp
31 #include "libavutil/opt.h"
32 #include "libavutil/avassert.h"
33 #include "libavutil/pixdesc.h"
36 #define RGB_MASK 0x00FFFFFF
37 #define LB_MASK 0x00FEFEFE
38 #define RED_BLUE_MASK 0x00FF00FF
39 #define GREEN_MASK 0x0000FF00
41 typedef int (*xbrfunc_t
)(AVFilterContext
*ctx
, void *arg
, int jobnr
, int nb_jobs
);
47 uint32_t rgbtoyuv
[1<<24];
50 typedef struct ThreadData
{
52 const uint32_t *rgbtoyuv
;
55 #define OFFSET(x) offsetof(XBRContext, x)
56 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
57 static const AVOption xbr_options
[] = {
58 { "n", "set scale factor", OFFSET(n
), AV_OPT_TYPE_INT
, {.i64
= 3}, 2, 4, .flags
= FLAGS
},
62 AVFILTER_DEFINE_CLASS(xbr
);
64 static uint32_t pixel_diff(uint32_t x
, uint32_t y
, const uint32_t *r2y
)
66 #define YMASK 0xff0000
67 #define UMASK 0x00ff00
68 #define VMASK 0x0000ff
70 uint32_t yuv1
= r2y
[x
& 0xffffff];
71 uint32_t yuv2
= r2y
[y
& 0xffffff];
73 return (abs((yuv1
& YMASK
) - (yuv2
& YMASK
)) >> 16) +
74 (abs((yuv1
& UMASK
) - (yuv2
& UMASK
)) >> 8) +
75 abs((yuv1
& VMASK
) - (yuv2
& VMASK
));
78 #define ALPHA_BLEND_128_W(dst, src) dst = ((src & LB_MASK) >> 1) + ((dst & LB_MASK) >> 1)
80 #define ALPHA_BLEND_32_W(dst, src) \
81 dst = ((RED_BLUE_MASK & ((dst & RED_BLUE_MASK) + ((((src & RED_BLUE_MASK) - \
82 (dst & RED_BLUE_MASK))) >> 3))) | (GREEN_MASK & ((dst & GREEN_MASK) + \
83 ((((src & GREEN_MASK) - (dst & GREEN_MASK))) >> 3))))
85 #define ALPHA_BLEND_64_W(dst, src) \
86 dst = ((RED_BLUE_MASK & ((dst & RED_BLUE_MASK) + ((((src & RED_BLUE_MASK) - \
87 (dst & RED_BLUE_MASK))) >> 2))) | (GREEN_MASK & ((dst & GREEN_MASK) + \
88 ((((src & GREEN_MASK) - (dst & GREEN_MASK))) >> 2))))
90 #define ALPHA_BLEND_192_W(dst, src) \
91 dst = ((RED_BLUE_MASK & ((dst & RED_BLUE_MASK) + ((((src & RED_BLUE_MASK) - \
92 (dst & RED_BLUE_MASK)) * 3) >> 2))) | (GREEN_MASK & ((dst & GREEN_MASK) + \
93 ((((src & GREEN_MASK) - (dst & GREEN_MASK)) * 3) >> 2))))
95 #define ALPHA_BLEND_224_W(dst, src) \
96 dst = ((RED_BLUE_MASK & ((dst & RED_BLUE_MASK) + ((((src & RED_BLUE_MASK) - \
97 (dst & RED_BLUE_MASK)) * 7) >> 3))) | (GREEN_MASK & ((dst & GREEN_MASK) + \
98 ((((src & GREEN_MASK) - (dst & GREEN_MASK)) * 7) >> 3))))
100 #define df(A, B) pixel_diff(A, B, r2y)
101 #define eq(A, B) (df(A, B) < 155)
103 #define FILT2(PE, PI, PH, PF, PG, PC, PD, PB, PA, G5, C4, G0, D0, C1, B1, F4, I4, H5, I5, A0, A1, \
104 N0, N1, N2, N3) do { \
105 if (PE != PH && PE != PF) { \
106 const unsigned e = df(PE,PC) + df(PE,PG) + df(PI,H5) + df(PI,F4) + (df(PH,PF)<<2); \
107 const unsigned i = df(PH,PD) + df(PH,I5) + df(PF,I4) + df(PF,PB) + (df(PE,PI)<<2); \
108 if (e < i && (!eq(PF,PB) && !eq(PH,PD) || eq(PE,PI) \
109 && (!eq(PF,I4) && !eq(PH,I5)) \
110 || eq(PE,PG) || eq(PE,PC))) { \
111 const unsigned ke = df(PF,PG); \
112 const unsigned ki = df(PH,PC); \
113 const int left = ke<<1 <= ki && PE != PG && PD != PG; \
114 const int up = ke >= ki<<1 && PE != PC && PB != PC; \
115 const unsigned px = df(PE,PF) <= df(PE,PH) ? PF : PH; \
117 ALPHA_BLEND_224_W(E[N3], px); \
118 ALPHA_BLEND_64_W( E[N2], px); \
121 ALPHA_BLEND_192_W(E[N3], px); \
122 ALPHA_BLEND_64_W( E[N2], px); \
124 ALPHA_BLEND_192_W(E[N3], px); \
125 ALPHA_BLEND_64_W( E[N1], px); \
126 } else { /* diagonal */ \
127 ALPHA_BLEND_128_W(E[N3], px); \
129 } else if (e <= i) { \
130 ALPHA_BLEND_128_W( E[N3], ((df(PE,PF) <= df(PE,PH)) ? PF : PH)); \
135 #define FILT3(PE, PI, PH, PF, PG, PC, PD, PB, PA, G5, C4, G0, D0, C1, B1, F4, I4, H5, I5, A0, A1, \
136 N0, N1, N2, N3, N4, N5, N6, N7, N8) do { \
137 if (PE != PH && PE != PF) { \
138 const unsigned e = df(PE,PC) + df(PE,PG) + df(PI,H5) + df(PI,F4) + (df(PH,PF)<<2); \
139 const unsigned i = df(PH,PD) + df(PH,I5) + df(PF,I4) + df(PF,PB) + (df(PE,PI)<<2); \
140 if (e < i && (!eq(PF,PB) && !eq(PF,PC) || !eq(PH,PD) && !eq(PH,PG) || eq(PE,PI) \
141 && (!eq(PF,F4) && !eq(PF,I4) || !eq(PH,H5) && !eq(PH,I5)) \
142 || eq(PE,PG) || eq(PE,PC))) { \
143 const unsigned ke = df(PF,PG); \
144 const unsigned ki = df(PH,PC); \
145 const int left = ke<<1 <= ki && PE != PG && PD != PG; \
146 const int up = ke >= ki<<1 && PE != PC && PB != PC; \
147 const unsigned px = df(PE,PF) <= df(PE,PH) ? PF : PH; \
149 ALPHA_BLEND_192_W(E[N7], px); \
150 ALPHA_BLEND_64_W( E[N6], px); \
155 ALPHA_BLEND_192_W(E[N7], px); \
156 ALPHA_BLEND_64_W( E[N5], px); \
157 ALPHA_BLEND_64_W( E[N6], px); \
160 ALPHA_BLEND_192_W(E[N5], px); \
161 ALPHA_BLEND_64_W( E[N7], px); \
162 ALPHA_BLEND_64_W( E[N2], px); \
164 } else { /* diagonal */ \
165 ALPHA_BLEND_224_W(E[N8], px); \
166 ALPHA_BLEND_32_W( E[N5], px); \
167 ALPHA_BLEND_32_W( E[N7], px); \
169 } else if (e <= i) { \
170 ALPHA_BLEND_128_W(E[N8], ((df(PE,PF) <= df(PE,PH)) ? PF : PH)); \
175 #define FILT4(PE, PI, PH, PF, PG, PC, PD, PB, PA, G5, C4, G0, D0, C1, B1, F4, I4, H5, I5, A0, A1, \
176 N15, N14, N11, N3, N7, N10, N13, N12, N9, N6, N2, N1, N5, N8, N4, N0) do { \
177 if (PE != PH && PE != PF) { \
178 const unsigned e = df(PE,PC) + df(PE,PG) + df(PI,H5) + df(PI,F4) + (df(PH,PF)<<2); \
179 const unsigned i = df(PH,PD) + df(PH,I5) + df(PF,I4) + df(PF,PB) + (df(PE,PI)<<2); \
180 if (e < i && (!eq(PF,PB) && !eq(PH,PD) || eq(PE,PI) \
181 && (!eq(PF,I4) && !eq(PH,I5)) \
182 || eq(PE,PG) || eq(PE,PC))) { \
183 const unsigned ke = df(PF,PG); \
184 const unsigned ki = df(PH,PC); \
185 const int left = ke<<1 <= ki && PE != PG && PD != PG; \
186 const int up = ke >= ki<<1 && PE != PC && PB != PC; \
187 const unsigned px = df(PE,PF) <= df(PE,PH) ? PF : PH; \
189 ALPHA_BLEND_192_W(E[N13], px); \
190 ALPHA_BLEND_64_W( E[N12], px); \
191 E[N15] = E[N14] = E[N11] = px; \
192 E[N10] = E[N3] = E[N12]; \
195 ALPHA_BLEND_192_W(E[N11], px); \
196 ALPHA_BLEND_192_W(E[N13], px); \
197 ALPHA_BLEND_64_W( E[N10], px); \
198 ALPHA_BLEND_64_W( E[N12], px); \
202 ALPHA_BLEND_192_W(E[N14], px); \
203 ALPHA_BLEND_192_W(E[N7 ], px); \
204 ALPHA_BLEND_64_W( E[N10], px); \
205 ALPHA_BLEND_64_W( E[N3 ], px); \
208 } else { /* diagonal */ \
209 ALPHA_BLEND_128_W(E[N11], px); \
210 ALPHA_BLEND_128_W(E[N14], px); \
213 } else if (e <= i) { \
214 ALPHA_BLEND_128_W( E[N15], ((df(PE,PF) <= df(PE,PH)) ? PF : PH)); \
219 static av_always_inline
void xbr_filter(const ThreadData
*td
, int jobnr
, int nb_jobs
, int n
)
222 const AVFrame
*input
= td
->in
;
223 AVFrame
*output
= td
->out
;
224 const uint32_t *r2y
= td
->rgbtoyuv
;
225 const int slice_start
= (input
->height
* jobnr
) / nb_jobs
;
226 const int slice_end
= (input
->height
* (jobnr
+1)) / nb_jobs
;
227 const int nl
= output
->linesize
[0] >> 2;
228 const int nl1
= nl
+ nl
;
229 const int nl2
= nl1
+ nl
;
231 for (y
= slice_start
; y
< slice_end
; y
++) {
233 uint32_t *E
= (uint32_t *)(output
->data
[0] + y
* output
->linesize
[0] * n
);
234 const uint32_t *sa2
= (uint32_t *)(input
->data
[0] + y
* input
->linesize
[0] - 8); /* center */
235 const uint32_t *sa1
= sa2
- (input
->linesize
[0]>>2); /* up x1 */
236 const uint32_t *sa0
= sa1
- (input
->linesize
[0]>>2); /* up x2 */
237 const uint32_t *sa3
= sa2
+ (input
->linesize
[0]>>2); /* down x1 */
238 const uint32_t *sa4
= sa3
+ (input
->linesize
[0]>>2); /* down x2 */
247 if (y
>= input
->height
- 2) {
249 if (y
== input
->height
- 1) {
254 for (x
= 0; x
< input
->width
; x
++) {
255 const uint32_t B1
= sa0
[2];
256 const uint32_t PB
= sa1
[2];
257 const uint32_t PE
= sa2
[2];
258 const uint32_t PH
= sa3
[2];
259 const uint32_t H5
= sa4
[2];
261 const int pprev
= 2 - (x
> 0);
262 const uint32_t A1
= sa0
[pprev
];
263 const uint32_t PA
= sa1
[pprev
];
264 const uint32_t PD
= sa2
[pprev
];
265 const uint32_t PG
= sa3
[pprev
];
266 const uint32_t G5
= sa4
[pprev
];
268 const int pprev2
= pprev
- (x
> 1);
269 const uint32_t A0
= sa1
[pprev2
];
270 const uint32_t D0
= sa2
[pprev2
];
271 const uint32_t G0
= sa3
[pprev2
];
273 const int pnext
= 3 - (x
== input
->width
- 1);
274 const uint32_t C1
= sa0
[pnext
];
275 const uint32_t PC
= sa1
[pnext
];
276 const uint32_t PF
= sa2
[pnext
];
277 const uint32_t PI
= sa3
[pnext
];
278 const uint32_t I5
= sa4
[pnext
];
280 const int pnext2
= pnext
+ 1 - (x
>= input
->width
- 2);
281 const uint32_t C4
= sa1
[pnext2
];
282 const uint32_t F4
= sa2
[pnext2
];
283 const uint32_t I4
= sa3
[pnext2
];
286 E
[0] = E
[1] = // 0, 1
287 E
[nl
] = E
[nl
+ 1] = PE
; // 2, 3
289 FILT2(PE
, PI
, PH
, PF
, PG
, PC
, PD
, PB
, PA
, G5
, C4
, G0
, D0
, C1
, B1
, F4
, I4
, H5
, I5
, A0
, A1
, 0, 1, nl
, nl
+1);
290 FILT2(PE
, PC
, PF
, PB
, PI
, PA
, PH
, PD
, PG
, I4
, A1
, I5
, H5
, A0
, D0
, B1
, C1
, F4
, C4
, G5
, G0
, nl
, 0, nl
+1, 1);
291 FILT2(PE
, PA
, PB
, PD
, PC
, PG
, PF
, PH
, PI
, C1
, G0
, C4
, F4
, G5
, H5
, D0
, A0
, B1
, A1
, I4
, I5
, nl
+1, nl
, 1, 0);
292 FILT2(PE
, PG
, PD
, PH
, PA
, PI
, PB
, PF
, PC
, A0
, I5
, A1
, B1
, I4
, F4
, H5
, G5
, D0
, G0
, C1
, C4
, 1, nl
+1, 0, nl
);
294 E
[0] = E
[1] = E
[2] = // 0, 1, 2
295 E
[nl
] = E
[nl
+1] = E
[nl
+2] = // 3, 4, 5
296 E
[nl1
] = E
[nl1
+1] = E
[nl1
+2] = PE
; // 6, 7, 8
298 FILT3(PE
, PI
, PH
, PF
, PG
, PC
, PD
, PB
, PA
, G5
, C4
, G0
, D0
, C1
, B1
, F4
, I4
, H5
, I5
, A0
, A1
, 0, 1, 2, nl
, nl
+1, nl
+2, nl1
, nl1
+1, nl1
+2);
299 FILT3(PE
, PC
, PF
, PB
, PI
, PA
, PH
, PD
, PG
, I4
, A1
, I5
, H5
, A0
, D0
, B1
, C1
, F4
, C4
, G5
, G0
, nl1
, nl
, 0, nl1
+1, nl
+1, 1, nl1
+2, nl
+2, 2);
300 FILT3(PE
, PA
, PB
, PD
, PC
, PG
, PF
, PH
, PI
, C1
, G0
, C4
, F4
, G5
, H5
, D0
, A0
, B1
, A1
, I4
, I5
, nl1
+2, nl1
+1, nl1
, nl
+2, nl
+1, nl
, 2, 1, 0);
301 FILT3(PE
, PG
, PD
, PH
, PA
, PI
, PB
, PF
, PC
, A0
, I5
, A1
, B1
, I4
, F4
, H5
, G5
, D0
, G0
, C1
, C4
, 2, nl
+2, nl1
+2, 1, nl
+1, nl1
+1, 0, nl
, nl1
);
303 E
[0] = E
[1] = E
[2] = E
[3] = // 0, 1, 2, 3
304 E
[nl
] = E
[nl
+1] = E
[nl
+2] = E
[nl
+3] = // 4, 5, 6, 7
305 E
[nl1
] = E
[nl1
+1] = E
[nl1
+2] = E
[nl1
+3] = // 8, 9, 10, 11
306 E
[nl2
] = E
[nl2
+1] = E
[nl2
+2] = E
[nl2
+3] = PE
; // 12, 13, 14, 15
308 FILT4(PE
, PI
, PH
, PF
, PG
, PC
, PD
, PB
, PA
, G5
, C4
, G0
, D0
, C1
, B1
, F4
, I4
, H5
, I5
, A0
, A1
, nl2
+3, nl2
+2, nl1
+3, 3, nl
+3, nl1
+2, nl2
+1, nl2
, nl1
+1, nl
+2, 2, 1, nl
+1, nl1
, nl
, 0);
309 FILT4(PE
, PC
, PF
, PB
, PI
, PA
, PH
, PD
, PG
, I4
, A1
, I5
, H5
, A0
, D0
, B1
, C1
, F4
, C4
, G5
, G0
, 3, nl
+3, 2, 0, 1, nl
+2, nl1
+3, nl2
+3, nl1
+2, nl
+1, nl
, nl1
, nl1
+1, nl2
+2, nl2
+1, nl2
);
310 FILT4(PE
, PA
, PB
, PD
, PC
, PG
, PF
, PH
, PI
, C1
, G0
, C4
, F4
, G5
, H5
, D0
, A0
, B1
, A1
, I4
, I5
, 0, 1, nl
, nl2
, nl1
, nl
+1, 2, 3, nl
+2, nl1
+1, nl2
+1, nl2
+2, nl1
+2, nl
+3, nl1
+3, nl2
+3);
311 FILT4(PE
, PG
, PD
, PH
, PA
, PI
, PB
, PF
, PC
, A0
, I5
, A1
, B1
, I4
, F4
, H5
, G5
, D0
, G0
, C1
, C4
, nl2
, nl1
, nl2
+1, nl2
+3, nl2
+2, nl1
+1, nl
, 0, nl
+1, nl1
+2, nl1
+3, nl
+3, nl
+2, 1, 2, 3);
325 #define XBR_FUNC(size) \
326 static int xbr##size##x(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) \
328 xbr_filter(arg, jobnr, nb_jobs, size); \
337 static int config_output(AVFilterLink
*outlink
)
339 AVFilterContext
*ctx
= outlink
->src
;
340 XBRContext
*xbr
= ctx
->priv
;
341 AVFilterLink
*inlink
= ctx
->inputs
[0];
343 outlink
->w
= inlink
->w
* xbr
->n
;
344 outlink
->h
= inlink
->h
* xbr
->n
;
348 static int query_formats(AVFilterContext
*ctx
)
350 static const enum AVPixelFormat pix_fmts
[] = {
351 AV_PIX_FMT_0RGB32
, AV_PIX_FMT_NONE
,
354 ff_set_common_formats(ctx
, ff_make_format_list(pix_fmts
));
358 static int filter_frame(AVFilterLink
*inlink
, AVFrame
*in
)
360 AVFilterContext
*ctx
= inlink
->dst
;
361 AVFilterLink
*outlink
= ctx
->outputs
[0];
362 XBRContext
*xbr
= ctx
->priv
;
365 AVFrame
*out
= ff_get_video_buffer(outlink
, outlink
->w
, outlink
->h
);
368 return AVERROR(ENOMEM
);
371 av_frame_copy_props(out
, in
);
375 td
.rgbtoyuv
= xbr
->rgbtoyuv
;
376 ctx
->internal
->execute(ctx
, xbr
->func
, &td
, NULL
, FFMIN(inlink
->h
, ctx
->graph
->nb_threads
));
378 out
->width
= outlink
->w
;
379 out
->height
= outlink
->h
;
382 return ff_filter_frame(outlink
, out
);
385 static int init(AVFilterContext
*ctx
)
387 XBRContext
*xbr
= ctx
->priv
;
388 static const xbrfunc_t xbrfuncs
[] = {xbr2x
, xbr3x
, xbr4x
};
393 for (bg
= -255; bg
< 256; bg
++) {
394 for (rg
= -255; rg
< 256; rg
++) {
395 const uint32_t u
= (uint32_t)((-169*rg
+ 500*bg
)/1000) + 128;
396 const uint32_t v
= (uint32_t)(( 500*rg
- 81*bg
)/1000) + 128;
397 int startg
= FFMAX3(-bg
, -rg
, 0);
398 int endg
= FFMIN3(255-bg
, 255-rg
, 255);
399 uint32_t y
= (uint32_t)(( 299*rg
+ 1000*startg
+ 114*bg
)/1000);
400 c
= bg
+ (rg
<<16) + 0x010101 * startg
;
401 for (g
= startg
; g
<= endg
; g
++) {
402 xbr
->rgbtoyuv
[c
] = ((y
++) << 16) + (u
<< 8) + v
;
408 xbr
->func
= xbrfuncs
[xbr
->n
- 2];
412 static const AVFilterPad xbr_inputs
[] = {
415 .type
= AVMEDIA_TYPE_VIDEO
,
416 .filter_frame
= filter_frame
,
421 static const AVFilterPad xbr_outputs
[] = {
424 .type
= AVMEDIA_TYPE_VIDEO
,
425 .config_props
= config_output
,
430 AVFilter ff_vf_xbr
= {
432 .description
= NULL_IF_CONFIG_SMALL("Scale the input using xBR algorithm."),
433 .inputs
= xbr_inputs
,
434 .outputs
= xbr_outputs
,
435 .query_formats
= query_formats
,
436 .priv_size
= sizeof(XBRContext
),
437 .priv_class
= &xbr_class
,
439 .flags
= AVFILTER_FLAG_SLICE_THREADS
,