Browse code

lavfi: add xbr filter xBR

Signed-off-by: Michael Niedermayer <michaelni@gmx.at>

Arwa Arif authored on 2014/10/31 01:36:20
Showing 7 changed files
... ...
@@ -10,6 +10,7 @@ version <next>:
10 10
 - libutvideo YUV 4:2:2 10bit support
11 11
 - XCB-based screen-grabber
12 12
 - UDP-Lite support (RFC 3828)
13
+- xBR scaler
13 14
 
14 15
 version 2.4:
15 16
 - Icecast protocol
... ...
@@ -49,6 +49,7 @@ Specifically, the GPL parts of FFmpeg are:
49 49
     - vf_stereo3d.c
50 50
     - vf_super2xsai.c
51 51
     - vf_tinterlace.c
52
+    - vf_xbr.c
52 53
     - vsrc_mptestsrc.c
53 54
 
54 55
 Should you, for whatever reason, prefer to use version 3 of the (L)GPL, then
... ...
@@ -2608,6 +2608,7 @@ vidstabtransform_filter_deps="libvidstab"
2608 2608
 pixfmts_super2xsai_test_deps="super2xsai_filter"
2609 2609
 tinterlace_merge_test_deps="tinterlace_filter"
2610 2610
 tinterlace_pad_test_deps="tinterlace_filter"
2611
+xbr_filter_deps="gpl"
2611 2612
 zmq_filter_deps="libzmq"
2612 2613
 zoompan_filter_deps="swscale"
2613 2614
 
... ...
@@ -9196,6 +9196,10 @@ Only deinterlace frames marked as interlaced.
9196 9196
 Default value is @samp{all}.
9197 9197
 @end table
9198 9198
 
9199
+@section xbr
9200
+Apply high-quality magnification filter which is designed for pixel art. It follows a set
9201
+of edge-detection rules, see @url{http://www.libretro.com/forums/viewtopic.php?f=6&t=134}.
9202
+
9199 9203
 @anchor{yadif}
9200 9204
 @section yadif
9201 9205
 
... ...
@@ -198,6 +198,7 @@ OBJS-$(CONFIG_VIDSTABDETECT_FILTER)          += vidstabutils.o vf_vidstabdetect.
198 198
 OBJS-$(CONFIG_VIDSTABTRANSFORM_FILTER)       += vidstabutils.o vf_vidstabtransform.o
199 199
 OBJS-$(CONFIG_VIGNETTE_FILTER)               += vf_vignette.o
200 200
 OBJS-$(CONFIG_W3FDIF_FILTER)                 += vf_w3fdif.o
201
+OBJS-$(CONFIG_XBR_FILTER)                    += vf_xbr.o
201 202
 OBJS-$(CONFIG_YADIF_FILTER)                  += vf_yadif.o
202 203
 OBJS-$(CONFIG_ZMQ_FILTER)                    += f_zmq.o
203 204
 OBJS-$(CONFIG_ZOOMPAN_FILTER)                += vf_zoompan.o
... ...
@@ -213,6 +213,7 @@ void avfilter_register_all(void)
213 213
     REGISTER_FILTER(VIDSTABTRANSFORM, vidstabtransform, vf);
214 214
     REGISTER_FILTER(VIGNETTE,       vignette,       vf);
215 215
     REGISTER_FILTER(W3FDIF,         w3fdif,         vf);
216
+    REGISTER_FILTER(XBR,            xbr,            vf);
216 217
     REGISTER_FILTER(YADIF,          yadif,          vf);
217 218
     REGISTER_FILTER(ZMQ,            zmq,            vf);
218 219
     REGISTER_FILTER(ZOOMPAN,        zoompan,        vf);
219 220
new file mode 100644
... ...
@@ -0,0 +1,758 @@
0
+/*
1
+ * This file is part of FFmpeg.
2
+ *
3
+ * Copyright (C) 2011, 2012 Hyllian/Jararaca - sergiogdb@gmail.com
4
+ *
5
+ * Copyright (c) 2014 Arwa Arif <arwaarif1994@gmail.com>
6
+ *
7
+ * FFmpeg is free software; you can redistribute it and/or modify
8
+ * it under the terms of the GNU General Public License as published by
9
+ * the Free Software Foundation; either version 2 of the License, or
10
+ * (at your option) any later version.
11
+ *
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
15
+ * GNU General Public License for more details.
16
+ *
17
+ * You should have received a copy of the GNU General Public License along
18
+ * with FFmpeg; if not, write to the Free Software Foundation, Inc.,
19
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20
+ */
21
+
22
+/**
23
+ * @file
24
+ * XBR Filter is used for depixelization of image.
25
+ * This is based on Hyllian's 2xBR shader.
26
+ *
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
29
+ * Future work : To implement x3 and x4 scale, and threading.
30
+ */
31
+
32
+#include "libavutil/opt.h"
33
+#include "libavutil/avassert.h"
34
+#include "libavutil/pixdesc.h"
35
+#include "internal.h"
36
+
37
+#define RGB_MASK      0x00FFFFFF
38
+#define LB_MASK       0x00FEFEFE
39
+#define RED_BLUE_MASK 0x00FF00FF
40
+#define GREEN_MASK    0x0000FF00
41
+
42
+typedef struct {
43
+    const AVClass *class;
44
+    int n;
45
+    uint32_t rgbtoyuv[1<<24];
46
+} xBRContext;
47
+
48
+#define OFFSET(x) offsetof(xBRContext, x)
49
+static const AVOption xbr_options[] = {
50
+    { "n", "set scale factor", OFFSET(n), AV_OPT_TYPE_INT, {.i64 = 3}, 2, 4, },
51
+    { NULL }
52
+};
53
+
54
+AVFILTER_DEFINE_CLASS(xbr);
55
+
56
+static uint32_t df(uint32_t x, uint32_t y, const uint32_t *r2y)
57
+{
58
+#define YMASK 0xff0000
59
+#define UMASK 0x00ff00
60
+#define VMASK 0x0000ff
61
+
62
+    uint32_t yuv1 = r2y[x & 0xffffff];
63
+    uint32_t yuv2 = r2y[y & 0xffffff];
64
+
65
+    return (abs((yuv1 & YMASK) - (yuv2 & YMASK)) >> 16) +
66
+           (abs((yuv1 & UMASK) - (yuv2 & UMASK)) >>  8) +
67
+           abs((yuv1 & VMASK) - (yuv2 & VMASK));
68
+}
69
+
70
+#define ALPHA_BLEND_128_W(dst, src) dst = ((src & LB_MASK) >> 1) + ((dst & LB_MASK) >> 1)
71
+
72
+#define ALPHA_BLEND_32_W(dst, src) \
73
+    dst = ((RED_BLUE_MASK & ((dst & RED_BLUE_MASK) + ((((src & RED_BLUE_MASK) - \
74
+          (dst & RED_BLUE_MASK))) >>3))) | (GREEN_MASK & ((dst & GREEN_MASK) + \
75
+          ((((src & GREEN_MASK) - (dst & GREEN_MASK))) >>3))))
76
+
77
+#define ALPHA_BLEND_64_W(dst, src) \
78
+    dst = ((RED_BLUE_MASK & ((dst & RED_BLUE_MASK) + ((((src & RED_BLUE_MASK) - \
79
+          (dst & RED_BLUE_MASK))) >>2))) | (GREEN_MASK & ((dst & GREEN_MASK) + \
80
+          ((((src & GREEN_MASK) - (dst & GREEN_MASK))) >>2))))
81
+
82
+#define ALPHA_BLEND_192_W(dst, src) \
83
+    dst = ((RED_BLUE_MASK & ((dst & RED_BLUE_MASK) + ((((src & RED_BLUE_MASK) - \
84
+          (dst & RED_BLUE_MASK)) * 3) >>2))) | (GREEN_MASK & ((dst & GREEN_MASK) + \
85
+          ((((src & GREEN_MASK) - (dst & GREEN_MASK)) * 3) >>2))))
86
+
87
+#define ALPHA_BLEND_224_W(dst, src) \
88
+    dst = ((RED_BLUE_MASK & ((dst & RED_BLUE_MASK) + ((((src & RED_BLUE_MASK) - \
89
+          (dst & RED_BLUE_MASK)) * 7) >>3))) | (GREEN_MASK & ((dst & GREEN_MASK) + \
90
+          ((((src & GREEN_MASK) - (dst & GREEN_MASK)) * 7) >>3))))
91
+
92
+#define LEFT_UP_2_2X(N3, N2, N1, PIXEL)\
93
+    ALPHA_BLEND_224_W(E[N3], PIXEL); \
94
+    ALPHA_BLEND_64_W( E[N2], PIXEL); \
95
+    E[N1] = E[N2]; \
96
+
97
+#define LEFT_2_2X(N3, N2, PIXEL)\
98
+    ALPHA_BLEND_192_W(E[N3], PIXEL); \
99
+    ALPHA_BLEND_64_W( E[N2], PIXEL); \
100
+
101
+#define UP_2_2X(N3, N1, PIXEL)\
102
+    ALPHA_BLEND_192_W(E[N3], PIXEL); \
103
+    ALPHA_BLEND_64_W( E[N1], PIXEL); \
104
+
105
+#define DIA_2X(N3, PIXEL)\
106
+    ALPHA_BLEND_128_W(E[N3], PIXEL); \
107
+
108
+#define eq(A, B, r2y)\
109
+    (df(A, B, r2y) < 155)\
110
+
111
+#define FILTRO(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,r2y) \
112
+     ex   = (PE!=PH && PE!=PF); \
113
+     if ( ex )\
114
+     {\
115
+          e = (df(PE,PC,r2y)+df(PE,PG,r2y)+df(PI,H5,r2y)+df(PI,F4,r2y))+(df(PH,PF,r2y)<<2); \
116
+          i = (df(PH,PD,r2y)+df(PH,I5,r2y)+df(PF,I4,r2y)+df(PF,PB,r2y))+(df(PE,PI,r2y)<<2); \
117
+          if ((e<i)  && ( !eq(PF,PB,r2y) && !eq(PH,PD,r2y) || eq(PE,PI,r2y) && (!eq(PF,I4,r2y) && !eq(PH,I5,r2y)) || eq(PE,PG,r2y) || eq(PE,PC,r2y)) )\
118
+          {\
119
+              ke=df(PF,PG,r2y); ki=df(PH,PC,r2y); \
120
+              ex2 = (PE!=PC && PB!=PC); ex3 = (PE!=PG && PD!=PG); px = (df(PE,PF,r2y) <= df(PE,PH,r2y)) ? PF : PH; \
121
+              if ( ((ke<<1)<=ki) && ex3 && (ke>=(ki<<1)) && ex2 ) \
122
+              {\
123
+                     LEFT_UP_2_2X(N3, N2, N1, px)\
124
+              }\
125
+              else if ( ((ke<<1)<=ki) && ex3 ) \
126
+              {\
127
+                     LEFT_2_2X(N3, N2, px);\
128
+              }\
129
+              else if ( (ke>=(ki<<1)) && ex2 ) \
130
+              {\
131
+                     UP_2_2X(N3, N1, px);\
132
+              }\
133
+              else \
134
+              {\
135
+                     DIA_2X(N3, px);\
136
+              }\
137
+          }\
138
+          else if (e<=i)\
139
+          {\
140
+               ALPHA_BLEND_128_W( E[N3], ((df(PE,PF,r2y) <= df(PE,PH,r2y)) ? PF : PH)); \
141
+          }\
142
+     }\
143
+
144
+static void xbr2x(AVFrame * input, AVFrame * output, const uint32_t * r2y)
145
+{
146
+    unsigned int e, i,px;
147
+    unsigned int ex, ex2, ex3;
148
+    unsigned int ke, ki;
149
+    int x,y;
150
+
151
+    int next_line = output->linesize[0]>>2;
152
+
153
+    for (y = 0; y < input->height; y++) {
154
+
155
+        uint32_t pprev;
156
+        uint32_t pprev2;
157
+
158
+        uint32_t * E = (uint32_t *)(output->data[0] + y * output->linesize[0] * 2);
159
+
160
+        /* middle. Offset of -8 is given */
161
+        uint32_t * sa2 = (uint32_t *)(input->data[0] + y * input->linesize[0] - 8);
162
+        /* up one */
163
+        uint32_t * sa1 = sa2 - (input->linesize[0]>>2);
164
+        /* up two */
165
+        uint32_t * sa0 = sa1 - (input->linesize[0]>>2);
166
+        /* down one */
167
+        uint32_t * sa3 = sa2 + (input->linesize[0]>>2);
168
+        /* down two */
169
+        uint32_t * sa4 = sa3 + (input->linesize[0]>>2);
170
+
171
+        if (y <= 1) {
172
+            sa0 = sa1;
173
+            if (y == 0) {
174
+                sa0 = sa1 = sa2;
175
+            }
176
+        }
177
+
178
+        if (y >= input->height - 2) {
179
+            sa4 = sa3;
180
+            if (y == input->height - 1) {
181
+                sa4 = sa3 = sa2;
182
+            }
183
+        }
184
+
185
+        pprev = pprev2 = 2;
186
+
187
+        for (x = 0; x < input->width; x++) {
188
+            uint32_t B1 = sa0[2];
189
+            uint32_t PB = sa1[2];
190
+            uint32_t PE = sa2[2];
191
+            uint32_t PH = sa3[2];
192
+            uint32_t H5 = sa4[2];
193
+
194
+            uint32_t A1 = sa0[pprev];
195
+            uint32_t PA = sa1[pprev];
196
+            uint32_t PD = sa2[pprev];
197
+            uint32_t PG = sa3[pprev];
198
+            uint32_t G5 = sa4[pprev];
199
+
200
+            uint32_t A0 = sa1[pprev2];
201
+            uint32_t D0 = sa2[pprev2];
202
+            uint32_t G0 = sa3[pprev2];
203
+
204
+            uint32_t C1 = 0;
205
+            uint32_t PC = 0;
206
+            uint32_t PF = 0;
207
+            uint32_t PI = 0;
208
+            uint32_t I5 = 0;
209
+
210
+            uint32_t C4 = 0;
211
+            uint32_t F4 = 0;
212
+            uint32_t I4 = 0;
213
+
214
+            if (x >= input->width - 2) {
215
+                if (x == input->width - 1) {
216
+                    C1 = sa0[2];
217
+                    PC = sa1[2];
218
+                    PF = sa2[2];
219
+                    PI = sa3[2];
220
+                    I5 = sa4[2];
221
+
222
+                    C4 = sa1[2];
223
+                    F4 = sa2[2];
224
+                    I4 = sa3[2];
225
+                } else {
226
+                    C1 = sa0[3];
227
+                    PC = sa1[3];
228
+                    PF = sa2[3];
229
+                    PI = sa3[3];
230
+                    I5 = sa4[3];
231
+
232
+                    C4 = sa1[3];
233
+                    F4 = sa2[3];
234
+                    I4 = sa3[3];
235
+                }
236
+            } else {
237
+                C1 = sa0[3];
238
+                PC = sa1[3];
239
+                PF = sa2[3];
240
+                PI = sa3[3];
241
+                I5 = sa4[3];
242
+
243
+                C4 = sa1[4];
244
+                F4 = sa2[4];
245
+                I4 = sa3[4];
246
+            }
247
+
248
+            E[0] = E[1] = E[next_line] = E[next_line + 1] = PE; // 0, 1, 2, 3
249
+
250
+            FILTRO(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,r2y);
251
+            FILTRO(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,r2y);
252
+            FILTRO(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,r2y);
253
+            FILTRO(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,r2y);
254
+
255
+            sa0 += 1;
256
+            sa1 += 1;
257
+            sa2 += 1;
258
+            sa3 += 1;
259
+            sa4 += 1;
260
+
261
+            E += 2;
262
+
263
+            if (pprev2){
264
+                pprev2--;
265
+                pprev = 1;
266
+            }
267
+        }
268
+    }
269
+}
270
+#undef FILTRO
271
+
272
+#define LEFT_UP_2_3X(N7, N5, N6, N2, N8, PIXEL)\
273
+    ALPHA_BLEND_192_W(E[N7], PIXEL); \
274
+    ALPHA_BLEND_64_W( E[N6], PIXEL); \
275
+    E[N5] = E[N7]; \
276
+    E[N2] = E[N6]; \
277
+    E[N8] =  PIXEL;\
278
+
279
+#define LEFT_2_3X(N7, N5, N6, N8, PIXEL)\
280
+    ALPHA_BLEND_192_W(E[N7], PIXEL); \
281
+    ALPHA_BLEND_64_W( E[N5], PIXEL); \
282
+    ALPHA_BLEND_64_W( E[N6], PIXEL); \
283
+    E[N8] =  PIXEL;\
284
+
285
+#define UP_2_3X(N5, N7, N2, N8, PIXEL)\
286
+    ALPHA_BLEND_192_W(E[N5], PIXEL); \
287
+    ALPHA_BLEND_64_W( E[N7], PIXEL); \
288
+    ALPHA_BLEND_64_W( E[N2], PIXEL); \
289
+    E[N8] =  PIXEL;\
290
+
291
+#define DIA_3X(N8, N5, N7, PIXEL)\
292
+    ALPHA_BLEND_224_W(E[N8], PIXEL); \
293
+    ALPHA_BLEND_32_W(E[N5], PIXEL); \
294
+    ALPHA_BLEND_32_W(E[N7], PIXEL); \
295
+
296
+#define FILTRO(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,r2y) \
297
+     ex   = (PE!=PH && PE!=PF); \
298
+     if ( ex )\
299
+     {\
300
+          e = (df(PE,PC,r2y)+df(PE,PG,r2y)+df(PI,H5,r2y)+df(PI,F4,r2y))+(df(PH,PF,r2y)<<2); \
301
+          i = (df(PH,PD,r2y)+df(PH,I5,r2y)+df(PF,I4,r2y)+df(PF,PB,r2y))+(df(PE,PI,r2y)<<2); \
302
+          if ((e<i)  && ( !eq(PF,PB,r2y) && !eq(PF,PC,r2y) || !eq(PH,PD,r2y) && !eq(PH,PG,r2y) || eq(PE,PI,r2y) && (!eq(PF,F4,r2y) && !eq(PF,I4,r2y) || !eq(PH,H5,r2y) && !eq(PH,I5,r2y)) || eq(PE,PG,r2y) || eq(PE,PC,r2y)) )\
303
+          {\
304
+              ke=df(PF,PG,r2y); ki=df(PH,PC,r2y); \
305
+              ex2 = (PE!=PC && PB!=PC); ex3 = (PE!=PG && PD!=PG); px = (df(PE,PF,r2y) <= df(PE,PH,r2y)) ? PF : PH; \
306
+              if ( ((ke<<1)<=ki) && ex3 && (ke>=(ki<<1)) && ex2 ) \
307
+              {\
308
+                     LEFT_UP_2_3X(N7, N5, N6, N2, N8, px)\
309
+              }\
310
+              else if ( ((ke<<1)<=ki) && ex3 ) \
311
+              {\
312
+                     LEFT_2_3X(N7, N5, N6, N8, px);\
313
+              }\
314
+              else if ( (ke>=(ki<<1)) && ex2 ) \
315
+              {\
316
+                     UP_2_3X(N5, N7, N2, N8, px);\
317
+              }\
318
+              else \
319
+              {\
320
+                     DIA_3X(N8, N5, N7, px);\
321
+              }\
322
+          }\
323
+          else if (e<=i)\
324
+          {\
325
+               ALPHA_BLEND_128_W( E[N8], ((df(PE,PF,r2y) <= df(PE,PH,r2y)) ? PF : PH)); \
326
+          }\
327
+     }\
328
+
329
+static void xbr3x(AVFrame * input, AVFrame * output, const uint32_t * r2y)
330
+{
331
+    const int nl = output->linesize[0]>>2;
332
+    const int nl1 = nl + nl;
333
+
334
+    unsigned int e, i,px;
335
+    unsigned int ex, ex2, ex3;
336
+    unsigned int ke, ki;
337
+
338
+    uint32_t pprev;
339
+    uint32_t pprev2;
340
+
341
+    int x,y;
342
+
343
+    for (y = 0; y < input->height; y++) {
344
+
345
+        uint32_t * E = (uint32_t *)(output->data[0] + y * output->linesize[0] * 3);
346
+
347
+        /* middle. Offset of -8 is given */
348
+        uint32_t * sa2 = (uint32_t *)(input->data[0] + y * input->linesize[0] - 8);
349
+        /* up one */
350
+        uint32_t * sa1 = sa2 - (input->linesize[0]>>2);
351
+        /* up two */
352
+        uint32_t * sa0 = sa1 - (input->linesize[0]>>2);
353
+        /* down one */
354
+        uint32_t * sa3 = sa2 + (input->linesize[0]>>2);
355
+        /* down two */
356
+        uint32_t * sa4 = sa3 + (input->linesize[0]>>2);
357
+
358
+        if (y <= 1){
359
+            sa0 = sa1;
360
+            if (y == 0){
361
+                sa0 = sa1 = sa2;
362
+            }
363
+        }
364
+
365
+        if (y >= input->height - 2){
366
+            sa4 = sa3;
367
+            if (y == input->height - 1){
368
+                sa4 = sa3 = sa2;
369
+            }
370
+        }
371
+
372
+        pprev = pprev2 = 2;
373
+
374
+        for (x = 0; x < input->width; x++){
375
+            uint32_t B1 = sa0[2];
376
+            uint32_t PB = sa1[2];
377
+            uint32_t PE = sa2[2];
378
+            uint32_t PH = sa3[2];
379
+            uint32_t H5 = sa4[2];
380
+
381
+            uint32_t A1 = sa0[pprev];
382
+            uint32_t PA = sa1[pprev];
383
+            uint32_t PD = sa2[pprev];
384
+            uint32_t PG = sa3[pprev];
385
+            uint32_t G5 = sa4[pprev];
386
+
387
+            uint32_t A0 = sa1[pprev2];
388
+            uint32_t D0 = sa2[pprev2];
389
+            uint32_t G0 = sa3[pprev2];
390
+
391
+            uint32_t C1 = 0;
392
+            uint32_t PC = 0;
393
+            uint32_t PF = 0;
394
+            uint32_t PI = 0;
395
+            uint32_t I5 = 0;
396
+
397
+            uint32_t C4 = 0;
398
+            uint32_t F4 = 0;
399
+            uint32_t I4 = 0;
400
+
401
+            if (x >= input->width - 2){
402
+                if (x == input->width - 1){
403
+                    C1 = sa0[2];
404
+                    PC = sa1[2];
405
+                    PF = sa2[2];
406
+                    PI = sa3[2];
407
+                    I5 = sa4[2];
408
+
409
+                    C4 = sa1[2];
410
+                    F4 = sa2[2];
411
+                    I4 = sa3[2];
412
+                } else {
413
+                    C1 = sa0[3];
414
+                    PC = sa1[3];
415
+                    PF = sa2[3];
416
+                    PI = sa3[3];
417
+                    I5 = sa4[3];
418
+
419
+                    C4 = sa1[3];
420
+                    F4 = sa2[3];
421
+                    I4 = sa3[3];
422
+                }
423
+            } else {
424
+                C1 = sa0[3];
425
+                PC = sa1[3];
426
+                PF = sa2[3];
427
+                PI = sa3[3];
428
+                I5 = sa4[3];
429
+
430
+                C4 = sa1[4];
431
+                F4 = sa2[4];
432
+                I4 = sa3[4];
433
+            }
434
+
435
+            E[0]   = E[1]     = E[2]     = PE;
436
+            E[nl]  = E[nl+1]  = E[nl+2]  = PE; // 3, 4, 5
437
+            E[nl1] = E[nl1+1] = E[nl1+2] = PE; // 6, 7, 8
438
+
439
+            FILTRO(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,r2y);
440
+            FILTRO(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,r2y);
441
+            FILTRO(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,r2y);
442
+            FILTRO(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,r2y);
443
+
444
+            sa0 += 1;
445
+            sa1 += 1;
446
+            sa2 += 1;
447
+            sa3 += 1;
448
+            sa4 += 1;
449
+
450
+            E += 3;
451
+
452
+            if (pprev2){
453
+                pprev2--;
454
+                pprev = 1;
455
+            }
456
+        }
457
+    }
458
+}
459
+#undef FILTRO
460
+
461
+#define LEFT_UP_2(N15, N14, N11, N13, N12, N10, N7, N3, PIXEL)\
462
+    ALPHA_BLEND_192_W(E[N13], PIXEL); \
463
+    ALPHA_BLEND_64_W( E[N12], PIXEL); \
464
+    E[N15] = E[N14] = E[N11] = PIXEL; \
465
+    E[N10] = E[N3] = E[N12]; \
466
+    E[N7]  = E[N13]; \
467
+
468
+#define LEFT_2(N15, N14, N11, N13, N12, N10, PIXEL)\
469
+    ALPHA_BLEND_192_W(E[N11], PIXEL); \
470
+    ALPHA_BLEND_192_W(E[N13], PIXEL); \
471
+    ALPHA_BLEND_64_W( E[N10], PIXEL); \
472
+    ALPHA_BLEND_64_W( E[N12], PIXEL); \
473
+    E[N14] = PIXEL; \
474
+    E[N15] = PIXEL; \
475
+
476
+#define UP_2(N15, N14, N11, N3, N7, N10, PIXEL)\
477
+    ALPHA_BLEND_192_W(E[N14], PIXEL); \
478
+    ALPHA_BLEND_192_W(E[N7 ], PIXEL); \
479
+    ALPHA_BLEND_64_W( E[N10], PIXEL); \
480
+    ALPHA_BLEND_64_W( E[N3 ], PIXEL); \
481
+    E[N11] = PIXEL; \
482
+    E[N15] = PIXEL; \
483
+
484
+#define DIA(N15, N14, N11, PIXEL)\
485
+    ALPHA_BLEND_128_W(E[N11], PIXEL); \
486
+    ALPHA_BLEND_128_W(E[N14], PIXEL); \
487
+    E[N15] = PIXEL; \
488
+
489
+#define FILTRO(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,r2y) \
490
+     ex   = (PE!=PH && PE!=PF); \
491
+     if ( ex )\
492
+     {\
493
+          e = (df(PE,PC,r2y)+df(PE,PG,r2y)+df(PI,H5,r2y)+df(PI,F4,r2y))+(df(PH,PF,r2y)<<2); \
494
+          i = (df(PH,PD,r2y)+df(PH,I5,r2y)+df(PF,I4,r2y)+df(PF,PB,r2y))+(df(PE,PI,r2y)<<2); \
495
+          if ((e<i)  && ( !eq(PF,PB,r2y) && !eq(PH,PD,r2y) || eq(PE,PI,r2y) && (!eq(PF,I4,r2y) && !eq(PH,I5,r2y)) || eq(PE,PG,r2y) || eq(PE,PC,r2y)) )\
496
+          {\
497
+              ke=df(PF,PG,r2y); ki=df(PH,PC,r2y); \
498
+              ex2 = (PE!=PC && PB!=PC); ex3 = (PE!=PG && PD!=PG); px = (df(PE,PF,r2y) <= df(PE,PH,r2y)) ? PF : PH; \
499
+              if ( ((ke<<1)<=ki) && ex3 && (ke>=(ki<<1)) && ex2 ) \
500
+              {\
501
+                     LEFT_UP_2(N15, N14, N11, N13, N12, N10, N7, N3, px)\
502
+              }\
503
+              else if ( ((ke<<1)<=ki) && ex3 ) \
504
+              {\
505
+                     LEFT_2(N15, N14, N11, N13, N12, N10, px)\
506
+              }\
507
+              else if ( (ke>=(ki<<1)) && ex2 ) \
508
+              {\
509
+                     UP_2(N15, N14, N11, N3, N7, N10, px)\
510
+              }\
511
+              else \
512
+              {\
513
+                     DIA(N15, N14, N11, px)\
514
+              }\
515
+          }\
516
+          else if (e<=i)\
517
+          {\
518
+               ALPHA_BLEND_128_W( E[N15], ((df(PE,PF,r2y) <= df(PE,PH,r2y)) ? PF : PH)); \
519
+          }\
520
+     }\
521
+
522
+static void xbr4x(AVFrame * input, AVFrame * output, const uint32_t * r2y)
523
+{
524
+
525
+    const int nl = output->linesize[0]>>2;
526
+    const int nl1 = nl + nl;
527
+    const int nl2 = nl1 + nl;
528
+
529
+    unsigned int e, i, px;
530
+    unsigned int ex, ex2, ex3;
531
+    unsigned int ke, ki;
532
+
533
+    uint32_t pprev;
534
+    uint32_t pprev2;
535
+
536
+    int x,y;
537
+
538
+    for (y = 0; y < input->height; y++){
539
+
540
+        uint32_t * E = (uint32_t *)(output->data[0] + y * output->linesize[0] * 4);
541
+
542
+        /* middle. Offset of -8 is given */
543
+        uint32_t * sa2 = (uint32_t *)(input->data[0] + y * input->linesize[0] - 8);
544
+        /* up one */
545
+        uint32_t * sa1 = sa2 - (input->linesize[0]>>2);
546
+        /* up two */
547
+        uint32_t * sa0 = sa1 - (input->linesize[0]>>2);
548
+        /* down one */
549
+        uint32_t * sa3 = sa2 + (input->linesize[0]>>2);
550
+        /* down two */
551
+        uint32_t * sa4 = sa3 + (input->linesize[0]>>2);
552
+
553
+        if (y <= 1){
554
+            sa0 = sa1;
555
+            if (y == 0){
556
+                sa0 = sa1 = sa2;
557
+            }
558
+        }
559
+
560
+        if (y >= input->height - 2){
561
+            sa4 = sa3;
562
+            if (y == input->height - 1){
563
+                sa4 = sa3 = sa2;
564
+            }
565
+        }
566
+
567
+        pprev = pprev2 = 2;
568
+
569
+        for (x = 0; x < input->width; x++){
570
+            uint32_t B1 = sa0[2];
571
+            uint32_t PB = sa1[2];
572
+            uint32_t PE = sa2[2];
573
+            uint32_t PH = sa3[2];
574
+            uint32_t H5 = sa4[2];
575
+
576
+            uint32_t A1 = sa0[pprev];
577
+            uint32_t PA = sa1[pprev];
578
+            uint32_t PD = sa2[pprev];
579
+            uint32_t PG = sa3[pprev];
580
+            uint32_t G5 = sa4[pprev];
581
+
582
+            uint32_t A0 = sa1[pprev2];
583
+            uint32_t D0 = sa2[pprev2];
584
+            uint32_t G0 = sa3[pprev2];
585
+
586
+            uint32_t C1 = 0;
587
+            uint32_t PC = 0;
588
+            uint32_t PF = 0;
589
+            uint32_t PI = 0;
590
+            uint32_t I5 = 0;
591
+
592
+            uint32_t C4 = 0;
593
+            uint32_t F4 = 0;
594
+            uint32_t I4 = 0;
595
+
596
+            if (x >= input->width - 2){
597
+                if (x == input->width - 1){
598
+                    C1 = sa0[2];
599
+                    PC = sa1[2];
600
+                    PF = sa2[2];
601
+                    PI = sa3[2];
602
+                    I5 = sa4[2];
603
+
604
+                    C4 = sa1[2];
605
+                    F4 = sa2[2];
606
+                    I4 = sa3[2];
607
+                } else {
608
+                    C1 = sa0[3];
609
+                    PC = sa1[3];
610
+                    PF = sa2[3];
611
+                    PI = sa3[3];
612
+                    I5 = sa4[3];
613
+
614
+                    C4 = sa1[3];
615
+                    F4 = sa2[3];
616
+                    I4 = sa3[3];
617
+                }
618
+            } else {
619
+                C1 = sa0[3];
620
+                PC = sa1[3];
621
+                PF = sa2[3];
622
+                PI = sa3[3];
623
+                I5 = sa4[3];
624
+
625
+                C4 = sa1[4];
626
+                F4 = sa2[4];
627
+                I4 = sa3[4];
628
+            }
629
+
630
+            E[0]   = E[1]     = E[2]     = E[3]     = PE;
631
+            E[nl]  = E[nl+1]  = E[nl+2]  = E[nl+3]  = PE; //  4,  5,  6,  7
632
+            E[nl1] = E[nl1+1] = E[nl1+2] = E[nl1+3] = PE; //  8,  9, 10, 11
633
+            E[nl2] = E[nl2+1] = E[nl2+2] = E[nl2+3] = PE; // 12, 13, 14, 15
634
+
635
+            FILTRO(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,r2y);
636
+            FILTRO(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,r2y);
637
+            FILTRO(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,r2y);
638
+            FILTRO(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,r2y);
639
+
640
+            sa0 += 1;
641
+            sa1 += 1;
642
+            sa2 += 1;
643
+            sa3 += 1;
644
+            sa4 += 1;
645
+
646
+            E += 4;
647
+
648
+            if (pprev2){
649
+                pprev2--;
650
+                pprev = 1;
651
+            }
652
+        }
653
+    }
654
+}
655
+#undef FILTRO
656
+
657
+static int config_output(AVFilterLink *outlink)
658
+{
659
+    AVFilterContext *ctx = outlink->src;
660
+    xBRContext *xbr = ctx->priv;
661
+    AVFilterLink *inlink = ctx->inputs[0];
662
+
663
+    outlink->w = inlink->w * xbr->n;
664
+    outlink->h = inlink->h * xbr->n;
665
+    return 0;
666
+}
667
+
668
+static int query_formats(AVFilterContext *ctx)
669
+{
670
+    static const enum AVPixelFormat pix_fmts[] = {
671
+        AV_PIX_FMT_0RGB32, AV_PIX_FMT_NONE,
672
+    };
673
+
674
+    ff_set_common_formats(ctx, ff_make_format_list(pix_fmts));
675
+    return 0;
676
+}
677
+
678
+static int filter_frame(AVFilterLink *inlink, AVFrame *in)
679
+{
680
+    AVFilterContext *ctx = inlink->dst;
681
+    AVFilterLink *outlink = ctx->outputs[0];
682
+    xBRContext *xbr = ctx->priv;
683
+    const uint32_t *r2y = xbr->rgbtoyuv;
684
+
685
+    AVFrame *out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
686
+    if (!out) {
687
+        av_frame_free(&in);
688
+        return AVERROR(ENOMEM);
689
+    }
690
+
691
+    av_frame_copy_props(out, in);
692
+    if(xbr->n == 4)
693
+        xbr4x(in,out,r2y);
694
+    else if(xbr->n == 3)
695
+        xbr3x(in,out,r2y);
696
+    else
697
+        xbr2x(in,out,r2y);
698
+
699
+    out->width  = outlink->w;
700
+    out->height = outlink->h;
701
+
702
+    av_frame_free(&in);
703
+    return ff_filter_frame(outlink, out);
704
+}
705
+
706
+static int init(AVFilterContext *ctx)
707
+{
708
+    xBRContext *xbr = ctx->priv;
709
+    uint32_t c;
710
+    int bg, rg, g;
711
+
712
+    for (bg=-255; bg<256; bg++) {
713
+        for (rg=-255; rg<256; rg++) {
714
+            const uint32_t u = (uint32_t)((-169*rg + 500*bg)/1000) + 128;
715
+            const uint32_t v = (uint32_t)(( 500*rg -  81*bg)/1000) + 128;
716
+            int startg = FFMAX3(-bg, -rg, 0);
717
+            int endg = FFMIN3(255-bg, 255-rg, 255);
718
+            uint32_t y = (uint32_t)(( 299*rg + 1000*startg + 114*bg)/1000);
719
+            c = bg + (rg<<16) + 0x010101 * startg;
720
+            for (g = startg; g <= endg; g++) {
721
+                xbr->rgbtoyuv[c] = ((y++) << 16) + (u << 8) + v;
722
+                c+= 0x010101;
723
+            }
724
+        }
725
+    }
726
+
727
+    return 0;
728
+}
729
+
730
+static const AVFilterPad xbr_inputs[] = {
731
+    {
732
+        .name         = "default",
733
+        .type         = AVMEDIA_TYPE_VIDEO,
734
+        .filter_frame = filter_frame,
735
+    },
736
+    { NULL }
737
+};
738
+
739
+static const AVFilterPad xbr_outputs[] = {
740
+    {
741
+        .name         = "default",
742
+        .type         = AVMEDIA_TYPE_VIDEO,
743
+        .config_props = config_output,
744
+    },
745
+    { NULL }
746
+};
747
+
748
+AVFilter ff_vf_xbr = {
749
+    .name          = "xbr",
750
+    .description   = NULL_IF_CONFIG_SMALL("Scale the input by 2 using xbr algorithm."),
751
+    .inputs        = xbr_inputs,
752
+    .outputs       = xbr_outputs,
753
+    .query_formats = query_formats,
754
+    .priv_size     = sizeof(xBRContext),
755
+    .priv_class    = &xbr_class,
756
+    .init          = init,
757
+};