2 * This file is part of FFmpeg.
4 * Copyright (C) 2011, 2012 Hyllian/Jararaca - sergiogdb@gmail.com
6 * Copyright (c) 2014 Arwa Arif <arwaarif1994@gmail.com>
8 * FFmpeg is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * FFmpeg is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with FFmpeg; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
25 * XBR Filter is used for depixelization of image.
26 * This is based on Hyllian's xBR shader.
28 * @see http://www.libretro.com/forums/viewtopic.php?f=6&t=134
29 * @see https://github.com/yoyofr/iFBA/blob/master/fba_src/src/intf/video/scalers/xbr.cpp
31 * @todo add threading and FATE test
34 #include "libavutil/opt.h"
35 #include "libavutil/avassert.h"
36 #include "libavutil/pixdesc.h"
39 #define RGB_MASK 0x00FFFFFF
40 #define LB_MASK 0x00FEFEFE
41 #define RED_BLUE_MASK 0x00FF00FF
42 #define GREEN_MASK 0x0000FF00
47 uint32_t rgbtoyuv[1<<24];
50 #define OFFSET(x) offsetof(XBRContext, x)
51 static const AVOption xbr_options[] = {
52 { "n", "set scale factor", OFFSET(n), AV_OPT_TYPE_INT, {.i64 = 3}, 2, 4, },
56 AVFILTER_DEFINE_CLASS(xbr);
58 static uint32_t pixel_diff(uint32_t x, uint32_t y, const uint32_t *r2y)
60 #define YMASK 0xff0000
61 #define UMASK 0x00ff00
62 #define VMASK 0x0000ff
64 uint32_t yuv1 = r2y[x & 0xffffff];
65 uint32_t yuv2 = r2y[y & 0xffffff];
67 return (abs((yuv1 & YMASK) - (yuv2 & YMASK)) >> 16) +
68 (abs((yuv1 & UMASK) - (yuv2 & UMASK)) >> 8) +
69 abs((yuv1 & VMASK) - (yuv2 & VMASK));
72 #define ALPHA_BLEND_128_W(dst, src) dst = ((src & LB_MASK) >> 1) + ((dst & LB_MASK) >> 1)
74 #define ALPHA_BLEND_32_W(dst, src) \
75 dst = ((RED_BLUE_MASK & ((dst & RED_BLUE_MASK) + ((((src & RED_BLUE_MASK) - \
76 (dst & RED_BLUE_MASK))) >>3))) | (GREEN_MASK & ((dst & GREEN_MASK) + \
77 ((((src & GREEN_MASK) - (dst & GREEN_MASK))) >>3))))
79 #define ALPHA_BLEND_64_W(dst, src) \
80 dst = ((RED_BLUE_MASK & ((dst & RED_BLUE_MASK) + ((((src & RED_BLUE_MASK) - \
81 (dst & RED_BLUE_MASK))) >>2))) | (GREEN_MASK & ((dst & GREEN_MASK) + \
82 ((((src & GREEN_MASK) - (dst & GREEN_MASK))) >>2))))
84 #define ALPHA_BLEND_192_W(dst, src) \
85 dst = ((RED_BLUE_MASK & ((dst & RED_BLUE_MASK) + ((((src & RED_BLUE_MASK) - \
86 (dst & RED_BLUE_MASK)) * 3) >>2))) | (GREEN_MASK & ((dst & GREEN_MASK) + \
87 ((((src & GREEN_MASK) - (dst & GREEN_MASK)) * 3) >>2))))
89 #define ALPHA_BLEND_224_W(dst, src) \
90 dst = ((RED_BLUE_MASK & ((dst & RED_BLUE_MASK) + ((((src & RED_BLUE_MASK) - \
91 (dst & RED_BLUE_MASK)) * 7) >>3))) | (GREEN_MASK & ((dst & GREEN_MASK) + \
92 ((((src & GREEN_MASK) - (dst & GREEN_MASK)) * 7) >>3))))
94 #define df(A, B) pixel_diff(A, B, r2y)
99 #define FILT2(PE, PI, PH, PF, PG, PC, PD, PB, PA, G5, C4, G0, D0, C1, B1, F4, I4, H5, I5, A0, A1, N0, N1, N2, N3) do { \
100 unsigned ex = (PE!=PH && PE!=PF); \
103 unsigned e = (df(PE,PC)+df(PE,PG)+df(PI,H5)+df(PI,F4))+(df(PH,PF)<<2); \
104 unsigned i = (df(PH,PD)+df(PH,I5)+df(PF,I4)+df(PF,PB))+(df(PE,PI)<<2); \
105 if ((e<i) && ( !eq(PF,PB) && !eq(PH,PD) || eq(PE,PI) && (!eq(PF,I4) && !eq(PH,I5)) || eq(PE,PG) || eq(PE,PC)) )\
107 unsigned ke = df(PF,PG); \
108 unsigned ki = df(PH,PC); \
109 unsigned ex2 = (PE!=PC && PB!=PC); \
110 unsigned ex3 = (PE!=PG && PD!=PG); \
111 unsigned px = (df(PE,PF) <= df(PE,PH)) ? PF : PH; \
112 if (ke<<1 <= ki && ex3 && ke >= ki<<1 && ex2) { /* left-up */ \
113 ALPHA_BLEND_224_W(E[N3], px); \
114 ALPHA_BLEND_64_W( E[N2], px); \
116 } else if (ke<<1 <= ki && ex3) { /* left */ \
117 ALPHA_BLEND_192_W(E[N3], px); \
118 ALPHA_BLEND_64_W( E[N2], px); \
119 } else if (ke >= ki<<1 && ex2) { /* up */ \
120 ALPHA_BLEND_192_W(E[N3], px); \
121 ALPHA_BLEND_64_W( E[N1], px); \
122 } else { /* diagonal */ \
123 ALPHA_BLEND_128_W(E[N3], px); \
128 ALPHA_BLEND_128_W( E[N3], ((df(PE,PF) <= df(PE,PH)) ? PF : PH)); \
133 static void xbr2x(AVFrame * input, AVFrame * output, const uint32_t * r2y)
136 int next_line = output->linesize[0]>>2;
138 for (y = 0; y < input->height; y++) {
143 uint32_t * E = (uint32_t *)(output->data[0] + y * output->linesize[0] * 2);
145 /* middle. Offset of -8 is given */
146 uint32_t * sa2 = (uint32_t *)(input->data[0] + y * input->linesize[0] - 8);
148 uint32_t * sa1 = sa2 - (input->linesize[0]>>2);
150 uint32_t * sa0 = sa1 - (input->linesize[0]>>2);
152 uint32_t * sa3 = sa2 + (input->linesize[0]>>2);
154 uint32_t * sa4 = sa3 + (input->linesize[0]>>2);
163 if (y >= input->height - 2) {
165 if (y == input->height - 1) {
172 for (x = 0; x < input->width; x++) {
173 uint32_t B1 = sa0[2];
174 uint32_t PB = sa1[2];
175 uint32_t PE = sa2[2];
176 uint32_t PH = sa3[2];
177 uint32_t H5 = sa4[2];
179 uint32_t A1 = sa0[pprev];
180 uint32_t PA = sa1[pprev];
181 uint32_t PD = sa2[pprev];
182 uint32_t PG = sa3[pprev];
183 uint32_t G5 = sa4[pprev];
185 uint32_t A0 = sa1[pprev2];
186 uint32_t D0 = sa2[pprev2];
187 uint32_t G0 = sa3[pprev2];
199 if (x >= input->width - 2) {
200 if (x == input->width - 1) {
233 E[0] = E[1] = E[next_line] = E[next_line + 1] = PE; // 0, 1, 2, 3
235 FILT2(PE, PI, PH, PF, PG, PC, PD, PB, PA, G5, C4, G0, D0, C1, B1, F4, I4, H5, I5, A0, A1, 0, 1, next_line, next_line+1);
236 FILT2(PE, PC, PF, PB, PI, PA, PH, PD, PG, I4, A1, I5, H5, A0, D0, B1, C1, F4, C4, G5, G0, next_line, 0, next_line+1, 1);
237 FILT2(PE, PA, PB, PD, PC, PG, PF, PH, PI, C1, G0, C4, F4, G5, H5, D0, A0, B1, A1, I4, I5, next_line+1, next_line, 1, 0);
238 FILT2(PE, PG, PD, PH, PA, PI, PB, PF, PC, A0, I5, A1, B1, I4, F4, H5, G5, D0, G0, C1, C4, 1, next_line+1, 0, next_line);
256 #define FILT3(PE, PI, PH, PF, PG, PC, PD, PB, PA, G5, C4, G0, D0, C1, B1, F4, I4, H5, I5, A0, A1, N0, N1, N2, N3, N4, N5, N6, N7, N8) do { \
257 unsigned ex = (PE!=PH && PE!=PF); \
260 unsigned e = (df(PE,PC)+df(PE,PG)+df(PI,H5)+df(PI,F4))+(df(PH,PF)<<2); \
261 unsigned i = (df(PH,PD)+df(PH,I5)+df(PF,I4)+df(PF,PB))+(df(PE,PI)<<2); \
262 if ((e<i) && ( !eq(PF,PB) && !eq(PF,PC) || !eq(PH,PD) && !eq(PH,PG) || eq(PE,PI) && (!eq(PF,F4) && !eq(PF,I4) || !eq(PH,H5) && !eq(PH,I5)) || eq(PE,PG) || eq(PE,PC)) )\
264 unsigned ke = df(PF,PG); \
265 unsigned ki = df(PH,PC); \
266 unsigned ex2 = (PE!=PC && PB!=PC); \
267 unsigned ex3 = (PE!=PG && PD!=PG); \
268 unsigned px = (df(PE,PF) <= df(PE,PH)) ? PF : PH; \
269 if (ke<<1 <= ki && ex3 && ke >= ki<<1 && ex2) { /* left-up */ \
270 ALPHA_BLEND_192_W(E[N7], px); \
271 ALPHA_BLEND_64_W( E[N6], px); \
275 } else if (ke<<1 <= ki && ex3) { /* left */ \
276 ALPHA_BLEND_192_W(E[N7], px); \
277 ALPHA_BLEND_64_W( E[N5], px); \
278 ALPHA_BLEND_64_W( E[N6], px); \
280 } else if (ke >= ki<<1 && ex2) { /* up */ \
281 ALPHA_BLEND_192_W(E[N5], px); \
282 ALPHA_BLEND_64_W( E[N7], px); \
283 ALPHA_BLEND_64_W( E[N2], px); \
285 } else { /* diagonal */ \
286 ALPHA_BLEND_224_W(E[N8], px); \
287 ALPHA_BLEND_32_W(E[N5], px); \
288 ALPHA_BLEND_32_W(E[N7], px); \
293 ALPHA_BLEND_128_W( E[N8], ((df(PE,PF) <= df(PE,PH)) ? PF : PH)); \
298 static void xbr3x(AVFrame *input, AVFrame *output, const uint32_t *r2y)
300 const int nl = output->linesize[0]>>2;
301 const int nl1 = nl + nl;
307 for (y = 0; y < input->height; y++) {
309 uint32_t * E = (uint32_t *)(output->data[0] + y * output->linesize[0] * 3);
311 /* middle. Offset of -8 is given */
312 uint32_t * sa2 = (uint32_t *)(input->data[0] + y * input->linesize[0] - 8);
314 uint32_t * sa1 = sa2 - (input->linesize[0]>>2);
316 uint32_t * sa0 = sa1 - (input->linesize[0]>>2);
318 uint32_t * sa3 = sa2 + (input->linesize[0]>>2);
320 uint32_t * sa4 = sa3 + (input->linesize[0]>>2);
329 if (y >= input->height - 2){
331 if (y == input->height - 1){
338 for (x = 0; x < input->width; x++){
339 uint32_t B1 = sa0[2];
340 uint32_t PB = sa1[2];
341 uint32_t PE = sa2[2];
342 uint32_t PH = sa3[2];
343 uint32_t H5 = sa4[2];
345 uint32_t A1 = sa0[pprev];
346 uint32_t PA = sa1[pprev];
347 uint32_t PD = sa2[pprev];
348 uint32_t PG = sa3[pprev];
349 uint32_t G5 = sa4[pprev];
351 uint32_t A0 = sa1[pprev2];
352 uint32_t D0 = sa2[pprev2];
353 uint32_t G0 = sa3[pprev2];
365 if (x >= input->width - 2){
366 if (x == input->width - 1){
399 E[0] = E[1] = E[2] = PE;
400 E[nl] = E[nl+1] = E[nl+2] = PE; // 3, 4, 5
401 E[nl1] = E[nl1+1] = E[nl1+2] = PE; // 6, 7, 8
403 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);
404 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);
405 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);
406 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);
424 #define FILT4(PE, PI, PH, PF, PG, PC, PD, PB, PA, G5, C4, G0, D0, C1, B1, F4, I4, H5, I5, A0, A1, N15, N14, N11, N3, N7, N10, N13, N12, N9, N6, N2, N1, N5, N8, N4, N0) do { \
425 unsigned ex = (PE!=PH && PE!=PF); \
428 unsigned e = (df(PE,PC)+df(PE,PG)+df(PI,H5)+df(PI,F4))+(df(PH,PF)<<2); \
429 unsigned i = (df(PH,PD)+df(PH,I5)+df(PF,I4)+df(PF,PB))+(df(PE,PI)<<2); \
430 if ((e<i) && ( !eq(PF,PB) && !eq(PH,PD) || eq(PE,PI) && (!eq(PF,I4) && !eq(PH,I5)) || eq(PE,PG) || eq(PE,PC)) )\
432 unsigned ke = df(PF,PG); \
433 unsigned ki = df(PH,PC); \
434 unsigned ex2 = (PE!=PC && PB!=PC); \
435 unsigned ex3 = (PE!=PG && PD!=PG); \
436 unsigned px = (df(PE,PF) <= df(PE,PH)) ? PF : PH; \
437 if (ke<<1 <= ki && ex3 && ke >= ki<<1 && ex2) { /* left-up */ \
438 ALPHA_BLEND_192_W(E[N13], px); \
439 ALPHA_BLEND_64_W( E[N12], px); \
440 E[N15] = E[N14] = E[N11] = px; \
441 E[N10] = E[N3] = E[N12]; \
443 } else if (ke<<1 <= ki && ex3) { /* left */ \
444 ALPHA_BLEND_192_W(E[N11], px); \
445 ALPHA_BLEND_192_W(E[N13], px); \
446 ALPHA_BLEND_64_W( E[N10], px); \
447 ALPHA_BLEND_64_W( E[N12], px); \
450 } else if (ke >= ki<<1 && ex2) { /* up */ \
451 ALPHA_BLEND_192_W(E[N14], px); \
452 ALPHA_BLEND_192_W(E[N7 ], px); \
453 ALPHA_BLEND_64_W( E[N10], px); \
454 ALPHA_BLEND_64_W( E[N3 ], px); \
457 } else { /* diagonal */ \
458 ALPHA_BLEND_128_W(E[N11], px); \
459 ALPHA_BLEND_128_W(E[N14], px); \
465 ALPHA_BLEND_128_W( E[N15], ((df(PE,PF) <= df(PE,PH)) ? PF : PH)); \
470 static void xbr4x(AVFrame *input, AVFrame *output, const uint32_t *r2y)
473 const int nl = output->linesize[0]>>2;
474 const int nl1 = nl + nl;
475 const int nl2 = nl1 + nl;
481 for (y = 0; y < input->height; y++) {
483 uint32_t * E = (uint32_t *)(output->data[0] + y * output->linesize[0] * 4);
485 /* middle. Offset of -8 is given */
486 uint32_t * sa2 = (uint32_t *)(input->data[0] + y * input->linesize[0] - 8);
488 uint32_t * sa1 = sa2 - (input->linesize[0]>>2);
490 uint32_t * sa0 = sa1 - (input->linesize[0]>>2);
492 uint32_t * sa3 = sa2 + (input->linesize[0]>>2);
494 uint32_t * sa4 = sa3 + (input->linesize[0]>>2);
503 if (y >= input->height - 2) {
505 if (y == input->height - 1) {
512 for (x = 0; x < input->width; x++) {
513 uint32_t B1 = sa0[2];
514 uint32_t PB = sa1[2];
515 uint32_t PE = sa2[2];
516 uint32_t PH = sa3[2];
517 uint32_t H5 = sa4[2];
519 uint32_t A1 = sa0[pprev];
520 uint32_t PA = sa1[pprev];
521 uint32_t PD = sa2[pprev];
522 uint32_t PG = sa3[pprev];
523 uint32_t G5 = sa4[pprev];
525 uint32_t A0 = sa1[pprev2];
526 uint32_t D0 = sa2[pprev2];
527 uint32_t G0 = sa3[pprev2];
539 if (x >= input->width - 2) {
540 if (x == input->width - 1) {
573 E[0] = E[1] = E[2] = E[3] = PE;
574 E[nl] = E[nl+1] = E[nl+2] = E[nl+3] = PE; // 4, 5, 6, 7
575 E[nl1] = E[nl1+1] = E[nl1+2] = E[nl1+3] = PE; // 8, 9, 10, 11
576 E[nl2] = E[nl2+1] = E[nl2+2] = E[nl2+3] = PE; // 12, 13, 14, 15
578 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);
579 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);
580 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);
581 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);
599 static int config_output(AVFilterLink *outlink)
601 AVFilterContext *ctx = outlink->src;
602 XBRContext *xbr = ctx->priv;
603 AVFilterLink *inlink = ctx->inputs[0];
605 outlink->w = inlink->w * xbr->n;
606 outlink->h = inlink->h * xbr->n;
610 static int query_formats(AVFilterContext *ctx)
612 static const enum AVPixelFormat pix_fmts[] = {
613 AV_PIX_FMT_0RGB32, AV_PIX_FMT_NONE,
616 ff_set_common_formats(ctx, ff_make_format_list(pix_fmts));
620 static int filter_frame(AVFilterLink *inlink, AVFrame *in)
622 AVFilterContext *ctx = inlink->dst;
623 AVFilterLink *outlink = ctx->outputs[0];
624 XBRContext *xbr = ctx->priv;
625 const uint32_t *r2y = xbr->rgbtoyuv;
627 AVFrame *out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
630 return AVERROR(ENOMEM);
633 av_frame_copy_props(out, in);
636 else if (xbr->n == 3)
641 out->width = outlink->w;
642 out->height = outlink->h;
645 return ff_filter_frame(outlink, out);
648 static int init(AVFilterContext *ctx)
650 XBRContext *xbr = ctx->priv;
654 for (bg = -255; bg < 256; bg++) {
655 for (rg = -255; rg < 256; rg++) {
656 const uint32_t u = (uint32_t)((-169*rg + 500*bg)/1000) + 128;
657 const uint32_t v = (uint32_t)(( 500*rg - 81*bg)/1000) + 128;
658 int startg = FFMAX3(-bg, -rg, 0);
659 int endg = FFMIN3(255-bg, 255-rg, 255);
660 uint32_t y = (uint32_t)(( 299*rg + 1000*startg + 114*bg)/1000);
661 c = bg + (rg<<16) + 0x010101 * startg;
662 for (g = startg; g <= endg; g++) {
663 xbr->rgbtoyuv[c] = ((y++) << 16) + (u << 8) + v;
672 static const AVFilterPad xbr_inputs[] = {
675 .type = AVMEDIA_TYPE_VIDEO,
676 .filter_frame = filter_frame,
681 static const AVFilterPad xbr_outputs[] = {
684 .type = AVMEDIA_TYPE_VIDEO,
685 .config_props = config_output,
690 AVFilter ff_vf_xbr = {
692 .description = NULL_IF_CONFIG_SMALL("Scale the input using xBR algorithm."),
693 .inputs = xbr_inputs,
694 .outputs = xbr_outputs,
695 .query_formats = query_formats,
696 .priv_size = sizeof(XBRContext),
697 .priv_class = &xbr_class,