Partially based on the port by Niel van der Westhuizen
<nielkie@gmail.com>, done for GCI 2010. Same output as the original
filter and as fast.
See thread:
Subject: [FFmpeg-devel] [PATCH] Port MPlayer 2xSaI filter to libavfilter
Date: Thu, 25 Nov 2010 01:31:24 +1000
... | ... |
@@ -1692,6 +1692,7 @@ ocv_filter_deps="libopencv" |
1692 | 1692 |
pan_filter_deps="swresample" |
1693 | 1693 |
removelogo_filter_deps="avcodec avformat swscale" |
1694 | 1694 |
scale_filter_deps="swscale" |
1695 |
+super2xsai_filter_deps="gpl" |
|
1695 | 1696 |
tinterlace_filter_deps="gpl" |
1696 | 1697 |
yadif_filter_deps="gpl" |
1697 | 1698 |
|
... | ... |
@@ -2716,6 +2716,13 @@ For example: |
2716 | 2716 |
will create two separate outputs from the same input, one cropped and |
2717 | 2717 |
one padded. |
2718 | 2718 |
|
2719 |
+@section super2xsai |
|
2720 |
+ |
|
2721 |
+Scale the input by 2x and smooth using the Super2xSaI (Scale and |
|
2722 |
+Interpolate) pixel art scaling algorithm. |
|
2723 |
+ |
|
2724 |
+Useful for enlarging pixel art images without reducing sharpness. |
|
2725 |
+ |
|
2719 | 2726 |
@section swapuv |
2720 | 2727 |
Swap U & V plane. |
2721 | 2728 |
|
... | ... |
@@ -94,6 +94,7 @@ OBJS-$(CONFIG_SETTB_FILTER) += vf_settb.o |
94 | 94 |
OBJS-$(CONFIG_SHOWINFO_FILTER) += vf_showinfo.o |
95 | 95 |
OBJS-$(CONFIG_SLICIFY_FILTER) += vf_slicify.o |
96 | 96 |
OBJS-$(CONFIG_SPLIT_FILTER) += vf_split.o |
97 |
+OBJS-$(CONFIG_SUPER2XSAI_FILTER) += vf_super2xsai.o |
|
97 | 98 |
OBJS-$(CONFIG_SWAPUV_FILTER) += vf_swapuv.o |
98 | 99 |
OBJS-$(CONFIG_THUMBNAIL_FILTER) += vf_thumbnail.o |
99 | 100 |
OBJS-$(CONFIG_TILE_FILTER) += vf_tile.o |
... | ... |
@@ -99,6 +99,7 @@ void avfilter_register_all(void) |
99 | 99 |
REGISTER_FILTER (SHOWINFO, showinfo, vf); |
100 | 100 |
REGISTER_FILTER (SLICIFY, slicify, vf); |
101 | 101 |
REGISTER_FILTER (SPLIT, split, vf); |
102 |
+ REGISTER_FILTER (SUPER2XSAI, super2xsai, vf); |
|
102 | 103 |
REGISTER_FILTER (SWAPUV, swapuv, vf); |
103 | 104 |
REGISTER_FILTER (THUMBNAIL, thumbnail, vf); |
104 | 105 |
REGISTER_FILTER (TILE, tile, vf); |
... | ... |
@@ -29,7 +29,7 @@ |
29 | 29 |
#include "libavutil/avutil.h" |
30 | 30 |
|
31 | 31 |
#define LIBAVFILTER_VERSION_MAJOR 2 |
32 |
-#define LIBAVFILTER_VERSION_MINOR 68 |
|
32 |
+#define LIBAVFILTER_VERSION_MINOR 69 |
|
33 | 33 |
#define LIBAVFILTER_VERSION_MICRO 100 |
34 | 34 |
|
35 | 35 |
#define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \ |
36 | 36 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,350 @@ |
0 |
+/* |
|
1 |
+ * Copyright (c) 2010 Niel van der Westhuizen <nielkie@gmail.com> |
|
2 |
+ * Copyright (c) 2002 A'rpi |
|
3 |
+ * Copyright (c) 1997-2001 ZSNES Team ( zsknight@zsnes.com / _demo_@zsnes.com ) |
|
4 |
+ * |
|
5 |
+ * This file is part of FFmpeg. |
|
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 |
+ * Super 2xSaI video filter |
|
25 |
+ * Ported from MPlayer libmpcodecs/vf_2xsai.c. |
|
26 |
+ */ |
|
27 |
+ |
|
28 |
+#include "libavutil/pixdesc.h" |
|
29 |
+#include "libavutil/intreadwrite.h" |
|
30 |
+#include "avfilter.h" |
|
31 |
+ |
|
32 |
+typedef struct { |
|
33 |
+ /* masks used for two pixels interpolation */ |
|
34 |
+ uint32_t hi_pixel_mask; |
|
35 |
+ uint32_t lo_pixel_mask; |
|
36 |
+ |
|
37 |
+ /* masks used for four pixels interpolation */ |
|
38 |
+ uint32_t q_hi_pixel_mask; |
|
39 |
+ uint32_t q_lo_pixel_mask; |
|
40 |
+ |
|
41 |
+ int bpp; ///< bytes per pixel, pixel stride for each (packed) pixel |
|
42 |
+} Super2xSaIContext; |
|
43 |
+ |
|
44 |
+#define GET_RESULT(A, B, C, D) ((A != C || A != D) - (B != C || B != D)) |
|
45 |
+ |
|
46 |
+#define INTERPOLATE(A, B) (((A & hi_pixel_mask) >> 1) + ((B & hi_pixel_mask) >> 1) + (A & B & lo_pixel_mask)) |
|
47 |
+ |
|
48 |
+#define Q_INTERPOLATE(A, B, C, D) ((A & q_hi_pixel_mask) >> 2) + ((B & q_hi_pixel_mask) >> 2) + ((C & q_hi_pixel_mask) >> 2) + ((D & q_hi_pixel_mask) >> 2) \ |
|
49 |
+ + ((((A & q_lo_pixel_mask) + (B & q_lo_pixel_mask) + (C & q_lo_pixel_mask) + (D & q_lo_pixel_mask)) >> 2) & q_lo_pixel_mask) |
|
50 |
+ |
|
51 |
+static void super2xsai(AVFilterContext *ctx, |
|
52 |
+ uint8_t *src, int src_linesize, |
|
53 |
+ uint8_t *dst, int dst_linesize, |
|
54 |
+ int width, int height) |
|
55 |
+{ |
|
56 |
+ Super2xSaIContext *sai = ctx->priv; |
|
57 |
+ unsigned int x, y; |
|
58 |
+ uint32_t color[4][4]; |
|
59 |
+ unsigned char *src_line[4]; |
|
60 |
+ const int bpp = sai->bpp; |
|
61 |
+ const uint32_t hi_pixel_mask = sai->hi_pixel_mask; |
|
62 |
+ const uint32_t lo_pixel_mask = sai->lo_pixel_mask; |
|
63 |
+ const uint32_t q_hi_pixel_mask = sai->q_hi_pixel_mask; |
|
64 |
+ const uint32_t q_lo_pixel_mask = sai->q_lo_pixel_mask; |
|
65 |
+ |
|
66 |
+ /* Point to the first 4 lines, first line is duplicated */ |
|
67 |
+ src_line[0] = src; |
|
68 |
+ src_line[1] = src; |
|
69 |
+ src_line[2] = src + src_linesize*FFMIN(1, height-1); |
|
70 |
+ src_line[3] = src + src_linesize*FFMIN(2, height-1); |
|
71 |
+ |
|
72 |
+#define READ_COLOR4(dst, src_line, off) dst = *((const uint32_t *)src_line + off) |
|
73 |
+#define READ_COLOR3(dst, src_line, off) dst = AV_RL24 (src_line + 3*off) |
|
74 |
+#define READ_COLOR2(dst, src_line, off) dst = *((const uint16_t *)src_line + off) |
|
75 |
+ |
|
76 |
+ /* Initialise the color matrix for this row. */ |
|
77 |
+ switch (bpp) { |
|
78 |
+ case 4: |
|
79 |
+ READ_COLOR4(color[0][0], src_line[0], 0); color[0][1] = color[0][0]; READ_COLOR4(color[0][2], src_line[0], 1); READ_COLOR4(color[0][3], src_line[0], 2); |
|
80 |
+ READ_COLOR4(color[1][0], src_line[1], 0); color[1][1] = color[1][0]; READ_COLOR4(color[1][2], src_line[1], 1); READ_COLOR4(color[1][3], src_line[1], 2); |
|
81 |
+ READ_COLOR4(color[2][0], src_line[2], 0); color[2][1] = color[2][0]; READ_COLOR4(color[2][2], src_line[2], 1); READ_COLOR4(color[2][3], src_line[2], 2); |
|
82 |
+ READ_COLOR4(color[3][0], src_line[3], 0); color[3][1] = color[3][0]; READ_COLOR4(color[3][2], src_line[3], 1); READ_COLOR4(color[3][3], src_line[3], 2); |
|
83 |
+ break; |
|
84 |
+ case 3: |
|
85 |
+ READ_COLOR3(color[0][0], src_line[0], 0); color[0][1] = color[0][0]; READ_COLOR3(color[0][2], src_line[0], 1); READ_COLOR3(color[0][3], src_line[0], 2); |
|
86 |
+ READ_COLOR3(color[1][0], src_line[1], 0); color[1][1] = color[1][0]; READ_COLOR3(color[1][2], src_line[1], 1); READ_COLOR3(color[1][3], src_line[1], 2); |
|
87 |
+ READ_COLOR3(color[2][0], src_line[2], 0); color[2][1] = color[2][0]; READ_COLOR3(color[2][2], src_line[2], 1); READ_COLOR3(color[2][3], src_line[2], 2); |
|
88 |
+ READ_COLOR3(color[3][0], src_line[3], 0); color[3][1] = color[3][0]; READ_COLOR3(color[3][2], src_line[3], 1); READ_COLOR3(color[3][3], src_line[3], 2); |
|
89 |
+ break; |
|
90 |
+ default: |
|
91 |
+ READ_COLOR2(color[0][0], src_line[0], 0); color[0][1] = color[0][0]; READ_COLOR2(color[0][2], src_line[0], 1); READ_COLOR2(color[0][3], src_line[0], 2); |
|
92 |
+ READ_COLOR2(color[1][0], src_line[1], 0); color[1][1] = color[1][0]; READ_COLOR2(color[1][2], src_line[1], 1); READ_COLOR2(color[1][3], src_line[1], 2); |
|
93 |
+ READ_COLOR2(color[2][0], src_line[2], 0); color[2][1] = color[2][0]; READ_COLOR2(color[2][2], src_line[2], 1); READ_COLOR2(color[2][3], src_line[2], 2); |
|
94 |
+ READ_COLOR2(color[3][0], src_line[3], 0); color[3][1] = color[3][0]; READ_COLOR2(color[3][2], src_line[3], 1); READ_COLOR2(color[3][3], src_line[3], 2); |
|
95 |
+ } |
|
96 |
+ |
|
97 |
+ for (y = 0; y < height; y++) { |
|
98 |
+ uint8_t *dst_line[2]; |
|
99 |
+ |
|
100 |
+ dst_line[0] = dst + dst_linesize*2*y; |
|
101 |
+ dst_line[1] = dst + dst_linesize*(2*y+1); |
|
102 |
+ |
|
103 |
+ for (x = 0; x < width; x++) { |
|
104 |
+ uint32_t product1a, product1b, product2a, product2b; |
|
105 |
+ |
|
106 |
+//--------------------------------------- B0 B1 B2 B3 0 1 2 3 |
|
107 |
+// 4 5* 6 S2 -> 4 5* 6 7 |
|
108 |
+// 1 2 3 S1 8 9 10 11 |
|
109 |
+// A0 A1 A2 A3 12 13 14 15 |
|
110 |
+//-------------------------------------- |
|
111 |
+ if (color[2][1] == color[1][2] && color[1][1] != color[2][2]) { |
|
112 |
+ product2b = color[2][1]; |
|
113 |
+ product1b = product2b; |
|
114 |
+ } else if (color[1][1] == color[2][2] && color[2][1] != color[1][2]) { |
|
115 |
+ product2b = color[1][1]; |
|
116 |
+ product1b = product2b; |
|
117 |
+ } else if (color[1][1] == color[2][2] && color[2][1] == color[1][2]) { |
|
118 |
+ int r = 0; |
|
119 |
+ |
|
120 |
+ r += GET_RESULT(color[1][2], color[1][1], color[1][0], color[3][1]); |
|
121 |
+ r += GET_RESULT(color[1][2], color[1][1], color[2][0], color[0][1]); |
|
122 |
+ r += GET_RESULT(color[1][2], color[1][1], color[3][2], color[2][3]); |
|
123 |
+ r += GET_RESULT(color[1][2], color[1][1], color[0][2], color[1][3]); |
|
124 |
+ |
|
125 |
+ if (r > 0) |
|
126 |
+ product1b = color[1][2]; |
|
127 |
+ else if (r < 0) |
|
128 |
+ product1b = color[1][1]; |
|
129 |
+ else |
|
130 |
+ product1b = INTERPOLATE(color[1][1], color[1][2]); |
|
131 |
+ |
|
132 |
+ product2b = product1b; |
|
133 |
+ } else { |
|
134 |
+ if (color[1][2] == color[2][2] && color[2][2] == color[3][1] && color[2][1] != color[3][2] && color[2][2] != color[3][0]) |
|
135 |
+ product2b = Q_INTERPOLATE(color[2][2], color[2][2], color[2][2], color[2][1]); |
|
136 |
+ else if (color[1][1] == color[2][1] && color[2][1] == color[3][2] && color[3][1] != color[2][2] && color[2][1] != color[3][3]) |
|
137 |
+ product2b = Q_INTERPOLATE(color[2][1], color[2][1], color[2][1], color[2][2]); |
|
138 |
+ else |
|
139 |
+ product2b = INTERPOLATE(color[2][1], color[2][2]); |
|
140 |
+ |
|
141 |
+ if (color[1][2] == color[2][2] && color[1][2] == color[0][1] && color[1][1] != color[0][2] && color[1][2] != color[0][0]) |
|
142 |
+ product1b = Q_INTERPOLATE(color[1][2], color[1][2], color[1][2], color[1][1]); |
|
143 |
+ else if (color[1][1] == color[2][1] && color[1][1] == color[0][2] && color[0][1] != color[1][2] && color[1][1] != color[0][3]) |
|
144 |
+ product1b = Q_INTERPOLATE(color[1][2], color[1][1], color[1][1], color[1][1]); |
|
145 |
+ else |
|
146 |
+ product1b = INTERPOLATE(color[1][1], color[1][2]); |
|
147 |
+ } |
|
148 |
+ |
|
149 |
+ if (color[1][1] == color[2][2] && color[2][1] != color[1][2] && color[1][0] == color[1][1] && color[1][1] != color[3][2]) |
|
150 |
+ product2a = INTERPOLATE(color[2][1], color[1][1]); |
|
151 |
+ else if (color[1][1] == color[2][0] && color[1][2] == color[1][1] && color[1][0] != color[2][1] && color[1][1] != color[3][0]) |
|
152 |
+ product2a = INTERPOLATE(color[2][1], color[1][1]); |
|
153 |
+ else |
|
154 |
+ product2a = color[2][1]; |
|
155 |
+ |
|
156 |
+ if (color[2][1] == color[1][2] && color[1][1] != color[2][2] && color[2][0] == color[2][1] && color[2][1] != color[0][2]) |
|
157 |
+ product1a = INTERPOLATE(color[2][1], color[1][1]); |
|
158 |
+ else if (color[1][0] == color[2][1] && color[2][2] == color[2][1] && color[2][0] != color[1][1] && color[2][1] != color[0][0]) |
|
159 |
+ product1a = INTERPOLATE(color[2][1], color[1][1]); |
|
160 |
+ else |
|
161 |
+ product1a = color[1][1]; |
|
162 |
+ |
|
163 |
+ /* Set the calculated pixels */ |
|
164 |
+ switch (bpp) { |
|
165 |
+ case 4: |
|
166 |
+ AV_WN32A(dst_line[0] + x * 8, product1a); |
|
167 |
+ AV_WN32A(dst_line[0] + x * 8 + 4, product1b); |
|
168 |
+ AV_WN32A(dst_line[1] + x * 8, product2a); |
|
169 |
+ AV_WN32A(dst_line[1] + x * 8 + 4, product2b); |
|
170 |
+ break; |
|
171 |
+ case 3: |
|
172 |
+ AV_WL24(dst_line[0] + x * 6, product1a); |
|
173 |
+ AV_WL24(dst_line[0] + x * 6 + 3, product1b); |
|
174 |
+ AV_WL24(dst_line[1] + x * 6, product2a); |
|
175 |
+ AV_WL24(dst_line[1] + x * 6 + 3, product2b); |
|
176 |
+ break; |
|
177 |
+ default: // bpp = 2 |
|
178 |
+ AV_WN32A(dst_line[0] + x * 4, product1a | (product1b << 16)); |
|
179 |
+ AV_WN32A(dst_line[1] + x * 4, product2a | (product2b << 16)); |
|
180 |
+ } |
|
181 |
+ |
|
182 |
+ /* Move color matrix forward */ |
|
183 |
+ color[0][0] = color[0][1]; color[0][1] = color[0][2]; color[0][2] = color[0][3]; |
|
184 |
+ color[1][0] = color[1][1]; color[1][1] = color[1][2]; color[1][2] = color[1][3]; |
|
185 |
+ color[2][0] = color[2][1]; color[2][1] = color[2][2]; color[2][2] = color[2][3]; |
|
186 |
+ color[3][0] = color[3][1]; color[3][1] = color[3][2]; color[3][2] = color[3][3]; |
|
187 |
+ |
|
188 |
+ if (x < width - 3) { |
|
189 |
+ x += 3; |
|
190 |
+ switch (bpp) { |
|
191 |
+ case 4: |
|
192 |
+ READ_COLOR4(color[0][3], src_line[0], x); |
|
193 |
+ READ_COLOR4(color[1][3], src_line[1], x); |
|
194 |
+ READ_COLOR4(color[2][3], src_line[2], x); |
|
195 |
+ READ_COLOR4(color[3][3], src_line[3], x); |
|
196 |
+ break; |
|
197 |
+ case 3: |
|
198 |
+ READ_COLOR3(color[0][3], src_line[0], x); |
|
199 |
+ READ_COLOR3(color[1][3], src_line[1], x); |
|
200 |
+ READ_COLOR3(color[2][3], src_line[2], x); |
|
201 |
+ READ_COLOR3(color[3][3], src_line[3], x); |
|
202 |
+ break; |
|
203 |
+ default: /* case 2 */ |
|
204 |
+ READ_COLOR2(color[0][3], src_line[0], x); |
|
205 |
+ READ_COLOR2(color[1][3], src_line[1], x); |
|
206 |
+ READ_COLOR2(color[2][3], src_line[2], x); |
|
207 |
+ READ_COLOR2(color[3][3], src_line[3], x); |
|
208 |
+ } |
|
209 |
+ x -= 3; |
|
210 |
+ } |
|
211 |
+ } |
|
212 |
+ |
|
213 |
+ /* We're done with one line, so we shift the source lines up */ |
|
214 |
+ src_line[0] = src_line[1]; |
|
215 |
+ src_line[1] = src_line[2]; |
|
216 |
+ src_line[2] = src_line[3]; |
|
217 |
+ |
|
218 |
+ /* Read next line */ |
|
219 |
+ src_line[3] = src_line[2]; |
|
220 |
+ if (y < height - 3) |
|
221 |
+ src_line[3] += src_linesize; |
|
222 |
+ |
|
223 |
+ switch (bpp) { |
|
224 |
+ case 4: |
|
225 |
+ READ_COLOR4(color[0][0], src_line[0], 0); color[0][1] = color[0][0]; READ_COLOR4(color[0][2], src_line[0], 1); READ_COLOR4(color[0][3], src_line[0], 2); |
|
226 |
+ READ_COLOR4(color[1][0], src_line[1], 0); color[1][1] = color[1][0]; READ_COLOR4(color[1][2], src_line[1], 1); READ_COLOR4(color[1][3], src_line[1], 2); |
|
227 |
+ READ_COLOR4(color[2][0], src_line[2], 0); color[2][1] = color[2][1]; READ_COLOR4(color[2][2], src_line[2], 1); READ_COLOR4(color[2][3], src_line[2], 2); |
|
228 |
+ READ_COLOR4(color[3][0], src_line[3], 0); color[3][1] = color[3][0]; READ_COLOR4(color[3][2], src_line[3], 1); READ_COLOR4(color[3][3], src_line[3], 2); |
|
229 |
+ break; |
|
230 |
+ case 3: |
|
231 |
+ READ_COLOR3(color[0][0], src_line[0], 0); color[0][1] = color[0][0]; READ_COLOR3(color[0][2], src_line[0], 1); READ_COLOR3(color[0][3], src_line[0], 2); |
|
232 |
+ READ_COLOR3(color[1][0], src_line[1], 0); color[1][1] = color[1][0]; READ_COLOR3(color[1][2], src_line[1], 1); READ_COLOR3(color[1][3], src_line[1], 2); |
|
233 |
+ READ_COLOR3(color[2][0], src_line[2], 0); color[2][1] = color[2][1]; READ_COLOR3(color[2][2], src_line[2], 1); READ_COLOR3(color[2][3], src_line[2], 2); |
|
234 |
+ READ_COLOR3(color[3][0], src_line[3], 0); color[3][1] = color[3][0]; READ_COLOR3(color[3][2], src_line[3], 1); READ_COLOR3(color[3][3], src_line[3], 2); |
|
235 |
+ break; |
|
236 |
+ default: |
|
237 |
+ READ_COLOR2(color[0][0], src_line[0], 0); color[0][1] = color[0][0]; READ_COLOR2(color[0][2], src_line[0], 1); READ_COLOR2(color[0][3], src_line[0], 2); |
|
238 |
+ READ_COLOR2(color[1][0], src_line[1], 0); color[1][1] = color[1][0]; READ_COLOR2(color[1][2], src_line[1], 1); READ_COLOR2(color[1][3], src_line[1], 2); |
|
239 |
+ READ_COLOR2(color[2][0], src_line[2], 0); color[2][1] = color[2][1]; READ_COLOR2(color[2][2], src_line[2], 1); READ_COLOR2(color[2][3], src_line[2], 2); |
|
240 |
+ READ_COLOR2(color[3][0], src_line[3], 0); color[3][1] = color[3][0]; READ_COLOR2(color[3][2], src_line[3], 1); READ_COLOR2(color[3][3], src_line[3], 2); |
|
241 |
+ } |
|
242 |
+ } // y loop |
|
243 |
+} |
|
244 |
+ |
|
245 |
+static int query_formats(AVFilterContext *ctx) |
|
246 |
+{ |
|
247 |
+ static const enum PixelFormat pix_fmts[] = { |
|
248 |
+ PIX_FMT_RGBA, PIX_FMT_BGRA, PIX_FMT_ARGB, PIX_FMT_ABGR, |
|
249 |
+ PIX_FMT_RGB24, PIX_FMT_BGR24, |
|
250 |
+ PIX_FMT_RGB565, PIX_FMT_BGR565, PIX_FMT_RGB555, PIX_FMT_BGR555, |
|
251 |
+ PIX_FMT_NONE |
|
252 |
+ }; |
|
253 |
+ |
|
254 |
+ avfilter_set_common_pixel_formats(ctx, avfilter_make_format_list(pix_fmts)); |
|
255 |
+ return 0; |
|
256 |
+} |
|
257 |
+ |
|
258 |
+static int config_input(AVFilterLink *inlink) |
|
259 |
+{ |
|
260 |
+ Super2xSaIContext *sai = inlink->dst->priv; |
|
261 |
+ |
|
262 |
+ sai->hi_pixel_mask = 0xFEFEFEFE; |
|
263 |
+ sai->lo_pixel_mask = 0x01010101; |
|
264 |
+ sai->q_hi_pixel_mask = 0xFCFCFCFC; |
|
265 |
+ sai->q_lo_pixel_mask = 0x03030303; |
|
266 |
+ sai->bpp = 4; |
|
267 |
+ |
|
268 |
+ switch (inlink->format) { |
|
269 |
+ case PIX_FMT_RGB24: |
|
270 |
+ case PIX_FMT_BGR24: |
|
271 |
+ sai->bpp = 3; |
|
272 |
+ break; |
|
273 |
+ |
|
274 |
+ case PIX_FMT_RGB565: |
|
275 |
+ case PIX_FMT_BGR565: |
|
276 |
+ sai->hi_pixel_mask = 0xF7DEF7DE; |
|
277 |
+ sai->lo_pixel_mask = 0x08210821; |
|
278 |
+ sai->q_hi_pixel_mask = 0xE79CE79C; |
|
279 |
+ sai->q_lo_pixel_mask = 0x18631863; |
|
280 |
+ sai->bpp = 2; |
|
281 |
+ break; |
|
282 |
+ |
|
283 |
+ case PIX_FMT_BGR555: |
|
284 |
+ case PIX_FMT_RGB555: |
|
285 |
+ sai->hi_pixel_mask = 0x7BDE7BDE; |
|
286 |
+ sai->lo_pixel_mask = 0x04210421; |
|
287 |
+ sai->q_hi_pixel_mask = 0x739C739C; |
|
288 |
+ sai->q_lo_pixel_mask = 0x0C630C63; |
|
289 |
+ sai->bpp = 2; |
|
290 |
+ break; |
|
291 |
+ } |
|
292 |
+ |
|
293 |
+ return 0; |
|
294 |
+} |
|
295 |
+ |
|
296 |
+static int config_output(AVFilterLink *outlink) |
|
297 |
+{ |
|
298 |
+ AVFilterLink *inlink = outlink->src->inputs[0]; |
|
299 |
+ |
|
300 |
+ outlink->w = inlink->w*2; |
|
301 |
+ outlink->h = inlink->h*2; |
|
302 |
+ |
|
303 |
+ av_log(inlink->dst, AV_LOG_INFO, "fmt:%s size:%dx%d -> size:%dx%d\n", |
|
304 |
+ av_get_pix_fmt_name(inlink->format), |
|
305 |
+ inlink->w, inlink->h, outlink->w, outlink->h); |
|
306 |
+ |
|
307 |
+ return 0; |
|
308 |
+} |
|
309 |
+ |
|
310 |
+static void null_draw_slice(AVFilterLink *inlink, int y, int h, int slice_dir) { } |
|
311 |
+ |
|
312 |
+static void end_frame(AVFilterLink *inlink) |
|
313 |
+{ |
|
314 |
+ AVFilterLink *outlink = inlink->dst->outputs[0]; |
|
315 |
+ AVFilterBufferRef *inpicref = inlink->cur_buf; |
|
316 |
+ AVFilterBufferRef *outpicref = outlink->out_buf; |
|
317 |
+ |
|
318 |
+ super2xsai(inlink->dst, inpicref->data[0], inpicref->linesize[0], |
|
319 |
+ outpicref->data[0], outpicref->linesize[0], |
|
320 |
+ inlink->w, inlink->h); |
|
321 |
+ |
|
322 |
+ avfilter_unref_buffer(inpicref); |
|
323 |
+ avfilter_draw_slice(outlink, 0, outlink->h, 1); |
|
324 |
+ avfilter_end_frame(outlink); |
|
325 |
+ avfilter_unref_buffer(outpicref); |
|
326 |
+} |
|
327 |
+ |
|
328 |
+AVFilter avfilter_vf_super2xsai = { |
|
329 |
+ .name = "super2xsai", |
|
330 |
+ .description = NULL_IF_CONFIG_SMALL("Scale the input by 2x using the Super2xSaI pixel art algorithm."), |
|
331 |
+ .priv_size = sizeof(Super2xSaIContext), |
|
332 |
+ .query_formats = query_formats, |
|
333 |
+ |
|
334 |
+ .inputs = (const AVFilterPad[]) { |
|
335 |
+ { .name = "default", |
|
336 |
+ .type = AVMEDIA_TYPE_VIDEO, |
|
337 |
+ .config_props = config_input, |
|
338 |
+ .draw_slice = null_draw_slice, |
|
339 |
+ .end_frame = end_frame, |
|
340 |
+ .min_perms = AV_PERM_READ }, |
|
341 |
+ { .name = NULL } |
|
342 |
+ }, |
|
343 |
+ .outputs = (const AVFilterPad[]) { |
|
344 |
+ { .name = "default", |
|
345 |
+ .type = AVMEDIA_TYPE_VIDEO, |
|
346 |
+ .config_props = config_output }, |
|
347 |
+ { .name = NULL } |
|
348 |
+ }, |
|
349 |
+}; |