Line |
Branch |
Exec |
Source |
1 |
|
|
/* |
2 |
|
|
* Copyright (c) 2017 Paul B Mahol |
3 |
|
|
* |
4 |
|
|
* This file is part of FFmpeg. |
5 |
|
|
* |
6 |
|
|
* FFmpeg is free software; you can redistribute it and/or |
7 |
|
|
* modify it under the terms of the GNU Lesser General Public |
8 |
|
|
* License as published by the Free Software Foundation; either |
9 |
|
|
* version 2.1 of the License, or (at your option) any later version. |
10 |
|
|
* |
11 |
|
|
* FFmpeg is distributed in the hope that it will be useful, |
12 |
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 |
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
14 |
|
|
* Lesser General Public License for more details. |
15 |
|
|
* |
16 |
|
|
* You should have received a copy of the GNU Lesser General Public |
17 |
|
|
* License along with FFmpeg; if not, write to the Free Software |
18 |
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
19 |
|
|
*/ |
20 |
|
|
|
21 |
|
|
#include "libavutil/avassert.h" |
22 |
|
|
#include "libavutil/colorspace.h" |
23 |
|
|
#include "libavutil/common.h" |
24 |
|
|
#include "libavutil/opt.h" |
25 |
|
|
#include "libavutil/pixdesc.h" |
26 |
|
|
#include "avfilter.h" |
27 |
|
|
#include "drawutils.h" |
28 |
|
|
#include "filters.h" |
29 |
|
|
#include "video.h" |
30 |
|
|
|
31 |
|
|
enum { Y, U, V, A }; |
32 |
|
|
enum { R, G, B }; |
33 |
|
|
|
34 |
|
|
enum FillMode { FM_SMEAR, FM_MIRROR, FM_FIXED, FM_REFLECT, FM_WRAP, FM_FADE, FM_MARGINS, FM_NB_MODES }; |
35 |
|
|
|
36 |
|
|
typedef struct Borders { |
37 |
|
|
int left, right, top, bottom; |
38 |
|
|
} Borders; |
39 |
|
|
|
40 |
|
|
typedef struct FillBordersContext { |
41 |
|
|
const AVClass *class; |
42 |
|
|
int left, right, top, bottom; |
43 |
|
|
int mode; |
44 |
|
|
|
45 |
|
|
int nb_planes; |
46 |
|
|
int depth; |
47 |
|
|
Borders borders[4]; |
48 |
|
|
int planewidth[4]; |
49 |
|
|
int planeheight[4]; |
50 |
|
|
uint8_t fill[4]; |
51 |
|
|
uint8_t yuv_color[4]; |
52 |
|
|
uint8_t rgba_color[4]; |
53 |
|
|
|
54 |
|
|
void (*fillborders)(struct FillBordersContext *s, AVFrame *frame); |
55 |
|
|
} FillBordersContext; |
56 |
|
|
|
57 |
|
|
static const enum AVPixelFormat pix_fmts[] = { |
58 |
|
|
AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUV440P, |
59 |
|
|
AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_YUVJ440P, |
60 |
|
|
AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUVA420P, AV_PIX_FMT_YUV420P, |
61 |
|
|
AV_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUVJ420P, |
62 |
|
|
AV_PIX_FMT_YUVJ411P, AV_PIX_FMT_YUV411P, AV_PIX_FMT_YUV410P, |
63 |
|
|
AV_PIX_FMT_YUV420P9, AV_PIX_FMT_YUV422P9, AV_PIX_FMT_YUV444P9, |
64 |
|
|
AV_PIX_FMT_YUV420P10, AV_PIX_FMT_YUV422P10, AV_PIX_FMT_YUV444P10, |
65 |
|
|
AV_PIX_FMT_YUV420P12, AV_PIX_FMT_YUV422P12, AV_PIX_FMT_YUV444P12, AV_PIX_FMT_YUV440P12, |
66 |
|
|
AV_PIX_FMT_YUV420P14, AV_PIX_FMT_YUV422P14, AV_PIX_FMT_YUV444P14, |
67 |
|
|
AV_PIX_FMT_YUV420P16, AV_PIX_FMT_YUV422P16, AV_PIX_FMT_YUV444P16, |
68 |
|
|
AV_PIX_FMT_YUVA420P9, AV_PIX_FMT_YUVA422P9, AV_PIX_FMT_YUVA444P9, |
69 |
|
|
AV_PIX_FMT_YUVA420P10, AV_PIX_FMT_YUVA422P10, AV_PIX_FMT_YUVA444P10, |
70 |
|
|
AV_PIX_FMT_YUVA422P12, AV_PIX_FMT_YUVA444P12, |
71 |
|
|
AV_PIX_FMT_YUVA420P16, AV_PIX_FMT_YUVA422P16, AV_PIX_FMT_YUVA444P16, |
72 |
|
|
AV_PIX_FMT_GBRP, AV_PIX_FMT_GBRP9, AV_PIX_FMT_GBRP10, |
73 |
|
|
AV_PIX_FMT_GBRP12, AV_PIX_FMT_GBRP14, AV_PIX_FMT_GBRP16, |
74 |
|
|
AV_PIX_FMT_GBRAP, AV_PIX_FMT_GBRAP10, AV_PIX_FMT_GBRAP12, AV_PIX_FMT_GBRAP16, |
75 |
|
|
AV_PIX_FMT_GRAY8, AV_PIX_FMT_GRAY9, AV_PIX_FMT_GRAY10, AV_PIX_FMT_GRAY12, AV_PIX_FMT_GRAY14, AV_PIX_FMT_GRAY16, |
76 |
|
|
AV_PIX_FMT_NONE |
77 |
|
|
}; |
78 |
|
|
|
79 |
|
✗ |
static void smear_borders8(FillBordersContext *s, AVFrame *frame) |
80 |
|
|
{ |
81 |
|
|
int p, y; |
82 |
|
|
|
83 |
|
✗ |
for (p = 0; p < s->nb_planes; p++) { |
84 |
|
✗ |
uint8_t *ptr = frame->data[p]; |
85 |
|
✗ |
ptrdiff_t linesize = frame->linesize[p]; |
86 |
|
|
|
87 |
|
✗ |
for (y = s->borders[p].top; y < s->planeheight[p] - s->borders[p].bottom; y++) { |
88 |
|
✗ |
memset(ptr + y * linesize, |
89 |
|
✗ |
*(ptr + y * linesize + s->borders[p].left), |
90 |
|
✗ |
s->borders[p].left); |
91 |
|
✗ |
memset(ptr + y * linesize + s->planewidth[p] - s->borders[p].right, |
92 |
|
✗ |
*(ptr + y * linesize + s->planewidth[p] - s->borders[p].right - 1), |
93 |
|
✗ |
s->borders[p].right); |
94 |
|
|
} |
95 |
|
|
|
96 |
|
✗ |
for (y = 0; y < s->borders[p].top; y++) { |
97 |
|
✗ |
memcpy(ptr + y * linesize, |
98 |
|
✗ |
ptr + s->borders[p].top * linesize, s->planewidth[p]); |
99 |
|
|
} |
100 |
|
|
|
101 |
|
✗ |
for (y = s->planeheight[p] - s->borders[p].bottom; y < s->planeheight[p]; y++) { |
102 |
|
✗ |
memcpy(ptr + y * linesize, |
103 |
|
✗ |
ptr + (s->planeheight[p] - s->borders[p].bottom - 1) * linesize, |
104 |
|
✗ |
s->planewidth[p]); |
105 |
|
|
} |
106 |
|
|
} |
107 |
|
✗ |
} |
108 |
|
|
|
109 |
|
✗ |
static void smear_borders16(FillBordersContext *s, AVFrame *frame) |
110 |
|
|
{ |
111 |
|
|
int p, y, x; |
112 |
|
|
|
113 |
|
✗ |
for (p = 0; p < s->nb_planes; p++) { |
114 |
|
✗ |
uint16_t *ptr = (uint16_t *)frame->data[p]; |
115 |
|
✗ |
ptrdiff_t linesize = frame->linesize[p] / 2; |
116 |
|
|
|
117 |
|
✗ |
for (y = s->borders[p].top; y < s->planeheight[p] - s->borders[p].bottom; y++) { |
118 |
|
✗ |
for (x = 0; x < s->borders[p].left; x++) { |
119 |
|
✗ |
ptr[y * linesize + x] = *(ptr + y * linesize + s->borders[p].left); |
120 |
|
|
} |
121 |
|
|
|
122 |
|
✗ |
for (x = 0; x < s->borders[p].right; x++) { |
123 |
|
✗ |
ptr[y * linesize + s->planewidth[p] - s->borders[p].right + x] = |
124 |
|
✗ |
*(ptr + y * linesize + s->planewidth[p] - s->borders[p].right - 1); |
125 |
|
|
} |
126 |
|
|
} |
127 |
|
|
|
128 |
|
✗ |
for (y = 0; y < s->borders[p].top; y++) { |
129 |
|
✗ |
memcpy(ptr + y * linesize, |
130 |
|
✗ |
ptr + s->borders[p].top * linesize, s->planewidth[p] * 2); |
131 |
|
|
} |
132 |
|
|
|
133 |
|
✗ |
for (y = s->planeheight[p] - s->borders[p].bottom; y < s->planeheight[p]; y++) { |
134 |
|
✗ |
memcpy(ptr + y * linesize, |
135 |
|
✗ |
ptr + (s->planeheight[p] - s->borders[p].bottom - 1) * linesize, |
136 |
|
✗ |
s->planewidth[p] * 2); |
137 |
|
|
} |
138 |
|
|
} |
139 |
|
✗ |
} |
140 |
|
|
|
141 |
|
✗ |
static void mirror_borders8(FillBordersContext *s, AVFrame *frame) |
142 |
|
|
{ |
143 |
|
|
int p, y, x; |
144 |
|
|
|
145 |
|
✗ |
for (p = 0; p < s->nb_planes; p++) { |
146 |
|
✗ |
uint8_t *ptr = frame->data[p]; |
147 |
|
✗ |
ptrdiff_t linesize = frame->linesize[p]; |
148 |
|
|
|
149 |
|
✗ |
for (y = s->borders[p].top; y < s->planeheight[p] - s->borders[p].bottom; y++) { |
150 |
|
✗ |
for (x = 0; x < s->borders[p].left; x++) { |
151 |
|
✗ |
ptr[y * linesize + x] = ptr[y * linesize + s->borders[p].left * 2 - 1 - x]; |
152 |
|
|
} |
153 |
|
|
|
154 |
|
✗ |
for (x = 0; x < s->borders[p].right; x++) { |
155 |
|
✗ |
ptr[y * linesize + s->planewidth[p] - s->borders[p].right + x] = |
156 |
|
✗ |
ptr[y * linesize + s->planewidth[p] - s->borders[p].right - 1 - x]; |
157 |
|
|
} |
158 |
|
|
} |
159 |
|
|
|
160 |
|
✗ |
for (y = 0; y < s->borders[p].top; y++) { |
161 |
|
✗ |
memcpy(ptr + y * linesize, |
162 |
|
✗ |
ptr + (s->borders[p].top * 2 - 1 - y) * linesize, |
163 |
|
✗ |
s->planewidth[p]); |
164 |
|
|
} |
165 |
|
|
|
166 |
|
✗ |
for (y = 0; y < s->borders[p].bottom; y++) { |
167 |
|
✗ |
memcpy(ptr + (s->planeheight[p] - s->borders[p].bottom + y) * linesize, |
168 |
|
✗ |
ptr + (s->planeheight[p] - s->borders[p].bottom - 1 - y) * linesize, |
169 |
|
✗ |
s->planewidth[p]); |
170 |
|
|
} |
171 |
|
|
} |
172 |
|
✗ |
} |
173 |
|
|
|
174 |
|
✗ |
static void mirror_borders16(FillBordersContext *s, AVFrame *frame) |
175 |
|
|
{ |
176 |
|
|
int p, y, x; |
177 |
|
|
|
178 |
|
✗ |
for (p = 0; p < s->nb_planes; p++) { |
179 |
|
✗ |
uint16_t *ptr = (uint16_t *)frame->data[p]; |
180 |
|
✗ |
ptrdiff_t linesize = frame->linesize[p] / 2; |
181 |
|
|
|
182 |
|
✗ |
for (y = s->borders[p].top; y < s->planeheight[p] - s->borders[p].bottom; y++) { |
183 |
|
✗ |
for (x = 0; x < s->borders[p].left; x++) { |
184 |
|
✗ |
ptr[y * linesize + x] = ptr[y * linesize + s->borders[p].left * 2 - 1 - x]; |
185 |
|
|
} |
186 |
|
|
|
187 |
|
✗ |
for (x = 0; x < s->borders[p].right; x++) { |
188 |
|
✗ |
ptr[y * linesize + s->planewidth[p] - s->borders[p].right + x] = |
189 |
|
✗ |
ptr[y * linesize + s->planewidth[p] - s->borders[p].right - 1 - x]; |
190 |
|
|
} |
191 |
|
|
} |
192 |
|
|
|
193 |
|
✗ |
for (y = 0; y < s->borders[p].top; y++) { |
194 |
|
✗ |
memcpy(ptr + y * linesize, |
195 |
|
✗ |
ptr + (s->borders[p].top * 2 - 1 - y) * linesize, |
196 |
|
✗ |
s->planewidth[p] * 2); |
197 |
|
|
} |
198 |
|
|
|
199 |
|
✗ |
for (y = 0; y < s->borders[p].bottom; y++) { |
200 |
|
✗ |
memcpy(ptr + (s->planeheight[p] - s->borders[p].bottom + y) * linesize, |
201 |
|
✗ |
ptr + (s->planeheight[p] - s->borders[p].bottom - 1 - y) * linesize, |
202 |
|
✗ |
s->planewidth[p] * 2); |
203 |
|
|
} |
204 |
|
|
} |
205 |
|
✗ |
} |
206 |
|
|
|
207 |
|
✗ |
static void fixed_borders8(FillBordersContext *s, AVFrame *frame) |
208 |
|
|
{ |
209 |
|
|
int p, y; |
210 |
|
|
|
211 |
|
✗ |
for (p = 0; p < s->nb_planes; p++) { |
212 |
|
✗ |
uint8_t *ptr = frame->data[p]; |
213 |
|
✗ |
uint8_t fill = s->fill[p]; |
214 |
|
✗ |
ptrdiff_t linesize = frame->linesize[p]; |
215 |
|
|
|
216 |
|
✗ |
for (y = s->borders[p].top; y < s->planeheight[p] - s->borders[p].bottom; y++) { |
217 |
|
✗ |
memset(ptr + y * linesize, fill, s->borders[p].left); |
218 |
|
✗ |
memset(ptr + y * linesize + s->planewidth[p] - s->borders[p].right, fill, |
219 |
|
✗ |
s->borders[p].right); |
220 |
|
|
} |
221 |
|
|
|
222 |
|
✗ |
for (y = 0; y < s->borders[p].top; y++) { |
223 |
|
✗ |
memset(ptr + y * linesize, fill, s->planewidth[p]); |
224 |
|
|
} |
225 |
|
|
|
226 |
|
✗ |
for (y = s->planeheight[p] - s->borders[p].bottom; y < s->planeheight[p]; y++) { |
227 |
|
✗ |
memset(ptr + y * linesize, fill, s->planewidth[p]); |
228 |
|
|
} |
229 |
|
|
} |
230 |
|
✗ |
} |
231 |
|
|
|
232 |
|
✗ |
static void fixed_borders16(FillBordersContext *s, AVFrame *frame) |
233 |
|
|
{ |
234 |
|
|
int p, y, x; |
235 |
|
|
|
236 |
|
✗ |
for (p = 0; p < s->nb_planes; p++) { |
237 |
|
✗ |
uint16_t *ptr = (uint16_t *)frame->data[p]; |
238 |
|
✗ |
uint16_t fill = s->fill[p] << (s->depth - 8); |
239 |
|
✗ |
ptrdiff_t linesize = frame->linesize[p] / 2; |
240 |
|
|
|
241 |
|
✗ |
for (y = s->borders[p].top; y < s->planeheight[p] - s->borders[p].bottom; y++) { |
242 |
|
✗ |
for (x = 0; x < s->borders[p].left; x++) { |
243 |
|
✗ |
ptr[y * linesize + x] = fill; |
244 |
|
|
} |
245 |
|
|
|
246 |
|
✗ |
for (x = 0; x < s->borders[p].right; x++) { |
247 |
|
✗ |
ptr[y * linesize + s->planewidth[p] - s->borders[p].right + x] = fill; |
248 |
|
|
} |
249 |
|
|
} |
250 |
|
|
|
251 |
|
✗ |
for (y = 0; y < s->borders[p].top; y++) { |
252 |
|
✗ |
for (x = 0; x < s->planewidth[p]; x++) { |
253 |
|
✗ |
ptr[y * linesize + x] = fill; |
254 |
|
|
} |
255 |
|
|
} |
256 |
|
|
|
257 |
|
✗ |
for (y = s->planeheight[p] - s->borders[p].bottom; y < s->planeheight[p]; y++) { |
258 |
|
✗ |
for (x = 0; x < s->planewidth[p]; x++) { |
259 |
|
✗ |
ptr[y * linesize + x] = fill; |
260 |
|
|
} |
261 |
|
|
} |
262 |
|
|
} |
263 |
|
✗ |
} |
264 |
|
|
|
265 |
|
✗ |
static void reflect_borders8(FillBordersContext *s, AVFrame *frame) |
266 |
|
|
{ |
267 |
|
|
int p, y, x; |
268 |
|
|
|
269 |
|
✗ |
for (p = 0; p < s->nb_planes; p++) { |
270 |
|
✗ |
uint8_t *ptr = frame->data[p]; |
271 |
|
✗ |
ptrdiff_t linesize = frame->linesize[p]; |
272 |
|
|
|
273 |
|
✗ |
for (y = s->borders[p].top; y < s->planeheight[p] - s->borders[p].bottom; y++) { |
274 |
|
✗ |
for (x = 0; x < s->borders[p].left; x++) { |
275 |
|
✗ |
ptr[y * linesize + x] = ptr[y * linesize + s->borders[p].left * 2 - x]; |
276 |
|
|
} |
277 |
|
|
|
278 |
|
✗ |
for (x = 0; x < s->borders[p].right; x++) { |
279 |
|
✗ |
ptr[y * linesize + s->planewidth[p] - s->borders[p].right + x] = |
280 |
|
✗ |
ptr[y * linesize + s->planewidth[p] - s->borders[p].right - 2 - x]; |
281 |
|
|
} |
282 |
|
|
} |
283 |
|
|
|
284 |
|
✗ |
for (y = 0; y < s->borders[p].top; y++) { |
285 |
|
✗ |
memcpy(ptr + y * linesize, |
286 |
|
✗ |
ptr + (s->borders[p].top * 2 - y) * linesize, |
287 |
|
✗ |
s->planewidth[p]); |
288 |
|
|
} |
289 |
|
|
|
290 |
|
✗ |
for (y = 0; y < s->borders[p].bottom; y++) { |
291 |
|
✗ |
memcpy(ptr + (s->planeheight[p] - s->borders[p].bottom + y) * linesize, |
292 |
|
✗ |
ptr + (s->planeheight[p] - s->borders[p].bottom - 2 - y) * linesize, |
293 |
|
✗ |
s->planewidth[p]); |
294 |
|
|
} |
295 |
|
|
} |
296 |
|
✗ |
} |
297 |
|
|
|
298 |
|
✗ |
static void reflect_borders16(FillBordersContext *s, AVFrame *frame) |
299 |
|
|
{ |
300 |
|
|
int p, y, x; |
301 |
|
|
|
302 |
|
✗ |
for (p = 0; p < s->nb_planes; p++) { |
303 |
|
✗ |
uint16_t *ptr = (uint16_t *)frame->data[p]; |
304 |
|
✗ |
ptrdiff_t linesize = frame->linesize[p] / 2; |
305 |
|
|
|
306 |
|
✗ |
for (y = s->borders[p].top; y < s->planeheight[p] - s->borders[p].bottom; y++) { |
307 |
|
✗ |
for (x = 0; x < s->borders[p].left; x++) { |
308 |
|
✗ |
ptr[y * linesize + x] = ptr[y * linesize + s->borders[p].left * 2 - x]; |
309 |
|
|
} |
310 |
|
|
|
311 |
|
✗ |
for (x = 0; x < s->borders[p].right; x++) { |
312 |
|
✗ |
ptr[y * linesize + s->planewidth[p] - s->borders[p].right + x] = |
313 |
|
✗ |
ptr[y * linesize + s->planewidth[p] - s->borders[p].right - 2 - x]; |
314 |
|
|
} |
315 |
|
|
} |
316 |
|
|
|
317 |
|
✗ |
for (y = 0; y < s->borders[p].top; y++) { |
318 |
|
✗ |
memcpy(ptr + y * linesize, |
319 |
|
✗ |
ptr + (s->borders[p].top * 2 - y) * linesize, |
320 |
|
✗ |
s->planewidth[p] * 2); |
321 |
|
|
} |
322 |
|
|
|
323 |
|
✗ |
for (y = 0; y < s->borders[p].bottom; y++) { |
324 |
|
✗ |
memcpy(ptr + (s->planeheight[p] - s->borders[p].bottom + y) * linesize, |
325 |
|
✗ |
ptr + (s->planeheight[p] - s->borders[p].bottom - 2 - y) * linesize, |
326 |
|
✗ |
s->planewidth[p] * 2); |
327 |
|
|
} |
328 |
|
|
} |
329 |
|
✗ |
} |
330 |
|
|
|
331 |
|
✗ |
static void wrap_borders8(FillBordersContext *s, AVFrame *frame) |
332 |
|
|
{ |
333 |
|
|
int p, y, x; |
334 |
|
|
|
335 |
|
✗ |
for (p = 0; p < s->nb_planes; p++) { |
336 |
|
✗ |
uint8_t *ptr = frame->data[p]; |
337 |
|
✗ |
ptrdiff_t linesize = frame->linesize[p]; |
338 |
|
|
|
339 |
|
✗ |
for (y = s->borders[p].top; y < s->planeheight[p] - s->borders[p].bottom; y++) { |
340 |
|
✗ |
for (x = 0; x < s->borders[p].left; x++) { |
341 |
|
✗ |
ptr[y * linesize + x] = ptr[y * linesize + s->planewidth[p] - s->borders[p].right - s->borders[p].left + x]; |
342 |
|
|
} |
343 |
|
|
|
344 |
|
✗ |
for (x = 0; x < s->borders[p].right; x++) { |
345 |
|
✗ |
ptr[y * linesize + s->planewidth[p] - s->borders[p].right + x] = |
346 |
|
✗ |
ptr[y * linesize + s->borders[p].left + x]; |
347 |
|
|
} |
348 |
|
|
} |
349 |
|
|
|
350 |
|
✗ |
for (y = 0; y < s->borders[p].top; y++) { |
351 |
|
✗ |
memcpy(ptr + y * linesize, |
352 |
|
✗ |
ptr + (s->planeheight[p] - s->borders[p].bottom - s->borders[p].top + y) * linesize, |
353 |
|
✗ |
s->planewidth[p]); |
354 |
|
|
} |
355 |
|
|
|
356 |
|
✗ |
for (y = 0; y < s->borders[p].bottom; y++) { |
357 |
|
✗ |
memcpy(ptr + (s->planeheight[p] - s->borders[p].bottom + y) * linesize, |
358 |
|
✗ |
ptr + (s->borders[p].top + y) * linesize, |
359 |
|
✗ |
s->planewidth[p]); |
360 |
|
|
} |
361 |
|
|
} |
362 |
|
✗ |
} |
363 |
|
|
|
364 |
|
✗ |
static void wrap_borders16(FillBordersContext *s, AVFrame *frame) |
365 |
|
|
{ |
366 |
|
|
int p, y, x; |
367 |
|
|
|
368 |
|
✗ |
for (p = 0; p < s->nb_planes; p++) { |
369 |
|
✗ |
uint16_t *ptr = (uint16_t *)frame->data[p]; |
370 |
|
✗ |
ptrdiff_t linesize = frame->linesize[p] / 2; |
371 |
|
|
|
372 |
|
✗ |
for (y = s->borders[p].top; y < s->planeheight[p] - s->borders[p].bottom; y++) { |
373 |
|
✗ |
for (x = 0; x < s->borders[p].left; x++) { |
374 |
|
✗ |
ptr[y * linesize + x] = ptr[y * linesize + s->planewidth[p] - s->borders[p].right - s->borders[p].left + x]; |
375 |
|
|
} |
376 |
|
|
|
377 |
|
✗ |
for (x = 0; x < s->borders[p].right; x++) { |
378 |
|
✗ |
ptr[y * linesize + s->planewidth[p] - s->borders[p].right + x] = |
379 |
|
✗ |
ptr[y * linesize + s->borders[p].left + x]; |
380 |
|
|
} |
381 |
|
|
} |
382 |
|
|
|
383 |
|
✗ |
for (y = 0; y < s->borders[p].top; y++) { |
384 |
|
✗ |
memcpy(ptr + y * linesize, |
385 |
|
✗ |
ptr + (s->planeheight[p] - s->borders[p].bottom - s->borders[p].top + y) * linesize, |
386 |
|
✗ |
s->planewidth[p] * 2); |
387 |
|
|
} |
388 |
|
|
|
389 |
|
✗ |
for (y = 0; y < s->borders[p].bottom; y++) { |
390 |
|
✗ |
memcpy(ptr + (s->planeheight[p] - s->borders[p].bottom + y) * linesize, |
391 |
|
✗ |
ptr + (s->borders[p].top + y) * linesize, |
392 |
|
✗ |
s->planewidth[p] * 2); |
393 |
|
|
} |
394 |
|
|
} |
395 |
|
✗ |
} |
396 |
|
|
|
397 |
|
✗ |
static int lerp8(int fill, int src, int pos, int size) |
398 |
|
|
{ |
399 |
|
✗ |
return av_clip_uint8(((fill * 256 * pos / size) + (src * 256 * (size - pos) / size)) >> 8); |
400 |
|
|
} |
401 |
|
|
|
402 |
|
✗ |
static int lerp16(int fill, int src, int pos, int size, int depth) |
403 |
|
|
{ |
404 |
|
✗ |
return av_clip_uintp2_c(((fill * (1LL << depth) * pos / size) + (src * (1LL << depth) * (size - pos) / size)) >> depth, depth); |
405 |
|
|
} |
406 |
|
|
|
407 |
|
✗ |
static void fade_borders8(FillBordersContext *s, AVFrame *frame) |
408 |
|
|
{ |
409 |
|
|
int p, y, x; |
410 |
|
|
|
411 |
|
✗ |
for (p = 0; p < s->nb_planes; p++) { |
412 |
|
✗ |
uint8_t *ptr = frame->data[p]; |
413 |
|
✗ |
const uint8_t fill = s->fill[p]; |
414 |
|
✗ |
const ptrdiff_t linesize = frame->linesize[p]; |
415 |
|
✗ |
const int start_left = s->borders[p].left; |
416 |
|
✗ |
const int start_right = s->planewidth[p] - s->borders[p].right; |
417 |
|
✗ |
const int start_top = s->borders[p].top; |
418 |
|
✗ |
const int start_bottom = s->planeheight[p] - s->borders[p].bottom; |
419 |
|
|
|
420 |
|
✗ |
for (y = 0; y < start_top; y++) { |
421 |
|
✗ |
for (x = 0; x < s->planewidth[p]; x++) { |
422 |
|
✗ |
int src = ptr[y * linesize + x]; |
423 |
|
✗ |
ptr[y * linesize + x] = lerp8(fill, src, start_top - y, start_top); |
424 |
|
|
} |
425 |
|
|
} |
426 |
|
|
|
427 |
|
✗ |
for (y = start_bottom; y < s->planeheight[p]; y++) { |
428 |
|
✗ |
for (x = 0; x < s->planewidth[p]; x++) { |
429 |
|
✗ |
int src = ptr[y * linesize + x]; |
430 |
|
✗ |
ptr[y * linesize + x] = lerp8(fill, src, y - start_bottom, s->borders[p].bottom); |
431 |
|
|
} |
432 |
|
|
} |
433 |
|
|
|
434 |
|
✗ |
for (y = 0; y < s->planeheight[p]; y++) { |
435 |
|
✗ |
for (x = 0; x < start_left; x++) { |
436 |
|
✗ |
int src = ptr[y * linesize + x]; |
437 |
|
✗ |
ptr[y * linesize + x] = lerp8(fill, src, start_left - x, start_left); |
438 |
|
|
} |
439 |
|
|
|
440 |
|
✗ |
for (x = 0; x < s->borders[p].right; x++) { |
441 |
|
✗ |
int src = ptr[y * linesize + start_right + x]; |
442 |
|
✗ |
ptr[y * linesize + start_right + x] = lerp8(fill, src, x, s->borders[p].right); |
443 |
|
|
} |
444 |
|
|
} |
445 |
|
|
} |
446 |
|
✗ |
} |
447 |
|
|
|
448 |
|
✗ |
static void fade_borders16(FillBordersContext *s, AVFrame *frame) |
449 |
|
|
{ |
450 |
|
✗ |
const int depth = s->depth; |
451 |
|
|
int p, y, x; |
452 |
|
|
|
453 |
|
✗ |
for (p = 0; p < s->nb_planes; p++) { |
454 |
|
✗ |
uint16_t *ptr = (uint16_t *)frame->data[p]; |
455 |
|
✗ |
const uint16_t fill = s->fill[p] << (depth - 8); |
456 |
|
✗ |
const ptrdiff_t linesize = frame->linesize[p] / 2; |
457 |
|
✗ |
const int start_left = s->borders[p].left; |
458 |
|
✗ |
const int start_right = s->planewidth[p] - s->borders[p].right; |
459 |
|
✗ |
const int start_top = s->borders[p].top; |
460 |
|
✗ |
const int start_bottom = s->planeheight[p] - s->borders[p].bottom; |
461 |
|
|
|
462 |
|
✗ |
for (y = 0; y < start_top; y++) { |
463 |
|
✗ |
for (x = 0; x < s->planewidth[p]; x++) { |
464 |
|
✗ |
int src = ptr[y * linesize + x]; |
465 |
|
✗ |
ptr[y * linesize + x] = lerp16(fill, src, start_top - y, start_top, depth); |
466 |
|
|
} |
467 |
|
|
} |
468 |
|
|
|
469 |
|
✗ |
for (y = start_bottom; y < s->planeheight[p]; y++) { |
470 |
|
✗ |
for (x = 0; x < s->planewidth[p]; x++) { |
471 |
|
✗ |
int src = ptr[y * linesize + x]; |
472 |
|
✗ |
ptr[y * linesize + x] = lerp16(fill, src, y - start_bottom, s->borders[p].bottom, depth); |
473 |
|
|
} |
474 |
|
|
} |
475 |
|
|
|
476 |
|
✗ |
for (y = 0; y < s->planeheight[p]; y++) { |
477 |
|
✗ |
for (x = 0; x < start_left; x++) { |
478 |
|
✗ |
int src = ptr[y * linesize + x]; |
479 |
|
✗ |
ptr[y * linesize + x] = lerp16(fill, src, start_left - x, start_left, depth); |
480 |
|
|
} |
481 |
|
|
|
482 |
|
✗ |
for (x = 0; x < s->borders[p].right; x++) { |
483 |
|
✗ |
int src = ptr[y * linesize + start_right + x]; |
484 |
|
✗ |
ptr[y * linesize + start_right + x] = lerp16(fill, src, x, s->borders[p].right, depth); |
485 |
|
|
} |
486 |
|
|
} |
487 |
|
|
} |
488 |
|
✗ |
} |
489 |
|
|
|
490 |
|
✗ |
static void margins_borders8(FillBordersContext *s, AVFrame *frame) |
491 |
|
|
{ |
492 |
|
✗ |
for (int p = 0; p < s->nb_planes; p++) { |
493 |
|
✗ |
uint8_t *ptr = (uint8_t *)frame->data[p]; |
494 |
|
✗ |
const ptrdiff_t linesize = frame->linesize[p]; |
495 |
|
✗ |
const int left = s->borders[p].left; |
496 |
|
✗ |
const int right = s->borders[p].right; |
497 |
|
✗ |
const int top = s->borders[p].top; |
498 |
|
✗ |
const int bottom = s->borders[p].bottom; |
499 |
|
✗ |
const int width = s->planewidth[p]; |
500 |
|
✗ |
const int height = s->planeheight[p]; |
501 |
|
|
|
502 |
|
✗ |
for (int y = top; y < height - bottom; y++) { |
503 |
|
✗ |
memset(ptr + linesize * y, ptr[linesize * y + left], left); |
504 |
|
✗ |
memset(ptr + linesize * y + width - right, (ptr + linesize * y + width - right)[-1], right); |
505 |
|
|
} |
506 |
|
|
|
507 |
|
✗ |
for (int y = top - 1; y >= 0; y--) { |
508 |
|
✗ |
ptr[linesize * y] = ptr[linesize * (y + 1)]; |
509 |
|
✗ |
memcpy(ptr + linesize * y + width - 8, ptr + linesize * (y + 1) + width - 8, 8); |
510 |
|
|
|
511 |
|
✗ |
for (int x = 1; x < width - 8; x++) { |
512 |
|
✗ |
int prev = ptr[linesize * (y + 1) + x - 1]; |
513 |
|
✗ |
int cur = ptr[linesize * (y + 1) + x]; |
514 |
|
✗ |
int next = ptr[linesize * (y + 1) + x + 1]; |
515 |
|
|
|
516 |
|
✗ |
ptr[linesize * y + x] = (3 * prev + 2 * cur + 3 * next + 4) >> 3; |
517 |
|
|
} |
518 |
|
|
} |
519 |
|
|
|
520 |
|
✗ |
for (int y = height - bottom; y < height; y++) { |
521 |
|
✗ |
ptr[linesize * y] = ptr[linesize * (y - 1)]; |
522 |
|
✗ |
memcpy(ptr + linesize * y + width - 8, ptr + linesize * (y - 1) + width - 8, 8); |
523 |
|
|
|
524 |
|
✗ |
for (int x = 1; x < width - 8; x++) { |
525 |
|
✗ |
int prev = ptr[linesize * (y - 1) + x - 1]; |
526 |
|
✗ |
int cur = ptr[linesize * (y - 1) + x]; |
527 |
|
✗ |
int next = ptr[linesize * (y - 1) + x + 1]; |
528 |
|
|
|
529 |
|
✗ |
ptr[linesize * y + x] = (3 * prev + 2 * cur + 3 * next + 4) >> 3; |
530 |
|
|
} |
531 |
|
|
} |
532 |
|
|
} |
533 |
|
✗ |
} |
534 |
|
|
|
535 |
|
✗ |
static void margins_borders16(FillBordersContext *s, AVFrame *frame) |
536 |
|
|
{ |
537 |
|
✗ |
for (int p = 0; p < s->nb_planes; p++) { |
538 |
|
✗ |
uint16_t *ptr = (uint16_t *)frame->data[p]; |
539 |
|
✗ |
const ptrdiff_t linesize = frame->linesize[p] / 2; |
540 |
|
✗ |
const int left = s->borders[p].left; |
541 |
|
✗ |
const int right = s->borders[p].right; |
542 |
|
✗ |
const int top = s->borders[p].top; |
543 |
|
✗ |
const int bottom = s->borders[p].bottom; |
544 |
|
✗ |
const int width = s->planewidth[p]; |
545 |
|
✗ |
const int height = s->planeheight[p]; |
546 |
|
|
|
547 |
|
✗ |
for (int y = top; y < height - bottom; y++) { |
548 |
|
✗ |
for (int x = 0; x < left; x++) |
549 |
|
✗ |
ptr[linesize * y + x] = ptr[linesize * y + left]; |
550 |
|
|
|
551 |
|
✗ |
for (int x = 0; x < right; x++) |
552 |
|
✗ |
ptr[linesize * y + width - right + x] = ptr[linesize * y + width - right - 1]; |
553 |
|
|
} |
554 |
|
|
|
555 |
|
✗ |
for (int y = top - 1; y >= 0; y--) { |
556 |
|
✗ |
ptr[linesize * y] = ptr[linesize * (y + 1)]; |
557 |
|
✗ |
memcpy(ptr + linesize * y + width - 8, ptr + linesize * (y + 1) + width - 8, 16); |
558 |
|
|
|
559 |
|
✗ |
for (int x = 1; x < width - 8; x++) { |
560 |
|
✗ |
int prev = ptr[linesize * (y + 1) + x - 1]; |
561 |
|
✗ |
int cur = ptr[linesize * (y + 1) + x]; |
562 |
|
✗ |
int next = ptr[linesize * (y + 1) + x + 1]; |
563 |
|
|
|
564 |
|
✗ |
ptr[linesize * y + x] = (3 * prev + 2 * cur + 3 * next + 4) >> 3; |
565 |
|
|
} |
566 |
|
|
} |
567 |
|
|
|
568 |
|
✗ |
for (int y = height - bottom; y < height; y++) { |
569 |
|
✗ |
ptr[linesize * y] = ptr[linesize * (y - 1)]; |
570 |
|
✗ |
memcpy(ptr + linesize * y + width - 8, ptr + linesize * (y - 1) + width - 8, 16); |
571 |
|
|
|
572 |
|
✗ |
for (int x = 1; x < width - 8; x++) { |
573 |
|
✗ |
int prev = ptr[linesize * (y - 1) + x - 1]; |
574 |
|
✗ |
int cur = ptr[linesize * (y - 1) + x]; |
575 |
|
✗ |
int next = ptr[linesize * (y - 1) + x + 1]; |
576 |
|
|
|
577 |
|
✗ |
ptr[linesize * y + x] = (3 * prev + 2 * cur + 3 * next + 4) >> 3; |
578 |
|
|
} |
579 |
|
|
} |
580 |
|
|
} |
581 |
|
✗ |
} |
582 |
|
|
|
583 |
|
✗ |
static int filter_frame(AVFilterLink *inlink, AVFrame *frame) |
584 |
|
|
{ |
585 |
|
✗ |
FillBordersContext *s = inlink->dst->priv; |
586 |
|
|
|
587 |
|
✗ |
s->fillborders(s, frame); |
588 |
|
|
|
589 |
|
✗ |
return ff_filter_frame(inlink->dst->outputs[0], frame); |
590 |
|
|
} |
591 |
|
|
|
592 |
|
✗ |
static int config_input(AVFilterLink *inlink) |
593 |
|
|
{ |
594 |
|
✗ |
AVFilterContext *ctx = inlink->dst; |
595 |
|
✗ |
FillBordersContext *s = ctx->priv; |
596 |
|
✗ |
const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format); |
597 |
|
|
|
598 |
|
✗ |
s->nb_planes = desc->nb_components; |
599 |
|
✗ |
s->depth = desc->comp[0].depth; |
600 |
|
|
|
601 |
|
✗ |
s->planeheight[1] = s->planeheight[2] = AV_CEIL_RSHIFT(inlink->h, desc->log2_chroma_h); |
602 |
|
✗ |
s->planeheight[0] = s->planeheight[3] = inlink->h; |
603 |
|
✗ |
s->planewidth[1] = s->planewidth[2] = AV_CEIL_RSHIFT(inlink->w, desc->log2_chroma_w); |
604 |
|
✗ |
s->planewidth[0] = s->planewidth[3] = inlink->w; |
605 |
|
|
|
606 |
|
✗ |
if (inlink->w < s->left + s->right || |
607 |
|
✗ |
inlink->w <= s->left || |
608 |
|
✗ |
inlink->w <= s->right || |
609 |
|
✗ |
inlink->h < s->top + s->bottom || |
610 |
|
✗ |
inlink->h <= s->top || |
611 |
|
✗ |
inlink->h <= s->bottom || |
612 |
|
✗ |
inlink->w < s->left * 2 || |
613 |
|
✗ |
inlink->w < s->right * 2 || |
614 |
|
✗ |
inlink->h < s->top * 2 || |
615 |
|
✗ |
inlink->h < s->bottom * 2) { |
616 |
|
✗ |
av_log(ctx, AV_LOG_ERROR, "Borders are bigger than input frame size.\n"); |
617 |
|
✗ |
return AVERROR(EINVAL); |
618 |
|
|
} |
619 |
|
|
|
620 |
|
✗ |
s->borders[0].left = s->borders[3].left = s->left; |
621 |
|
✗ |
s->borders[0].right = s->borders[3].right = s->right; |
622 |
|
✗ |
s->borders[0].top = s->borders[3].top = s->top; |
623 |
|
✗ |
s->borders[0].bottom = s->borders[3].bottom = s->bottom; |
624 |
|
|
|
625 |
|
✗ |
s->borders[1].left = s->left >> desc->log2_chroma_w; |
626 |
|
✗ |
s->borders[1].right = s->right >> desc->log2_chroma_w; |
627 |
|
✗ |
s->borders[1].top = s->top >> desc->log2_chroma_h; |
628 |
|
✗ |
s->borders[1].bottom = s->bottom >> desc->log2_chroma_h; |
629 |
|
|
|
630 |
|
✗ |
s->borders[2].left = s->left >> desc->log2_chroma_w; |
631 |
|
✗ |
s->borders[2].right = s->right >> desc->log2_chroma_w; |
632 |
|
✗ |
s->borders[2].top = s->top >> desc->log2_chroma_h; |
633 |
|
✗ |
s->borders[2].bottom = s->bottom >> desc->log2_chroma_h; |
634 |
|
|
|
635 |
|
✗ |
switch (s->mode) { |
636 |
|
✗ |
case FM_SMEAR: s->fillborders = s->depth <= 8 ? smear_borders8 : smear_borders16; break; |
637 |
|
✗ |
case FM_MIRROR: s->fillborders = s->depth <= 8 ? mirror_borders8 : mirror_borders16; break; |
638 |
|
✗ |
case FM_FIXED: s->fillborders = s->depth <= 8 ? fixed_borders8 : fixed_borders16; break; |
639 |
|
✗ |
case FM_REFLECT:s->fillborders = s->depth <= 8 ? reflect_borders8: reflect_borders16;break; |
640 |
|
✗ |
case FM_WRAP: s->fillborders = s->depth <= 8 ? wrap_borders8 : wrap_borders16; break; |
641 |
|
✗ |
case FM_FADE: s->fillborders = s->depth <= 8 ? fade_borders8 : fade_borders16; break; |
642 |
|
✗ |
case FM_MARGINS:s->fillborders = s->depth <= 8 ? margins_borders8: margins_borders16;break; |
643 |
|
✗ |
default: av_assert0(0); |
644 |
|
|
} |
645 |
|
|
|
646 |
|
✗ |
s->yuv_color[Y] = RGB_TO_Y_CCIR(s->rgba_color[R], s->rgba_color[G], s->rgba_color[B]); |
647 |
|
✗ |
s->yuv_color[U] = RGB_TO_U_CCIR(s->rgba_color[R], s->rgba_color[G], s->rgba_color[B], 0); |
648 |
|
✗ |
s->yuv_color[V] = RGB_TO_V_CCIR(s->rgba_color[R], s->rgba_color[G], s->rgba_color[B], 0); |
649 |
|
✗ |
s->yuv_color[A] = s->rgba_color[A]; |
650 |
|
|
|
651 |
|
✗ |
if (desc->flags & AV_PIX_FMT_FLAG_RGB) { |
652 |
|
|
uint8_t rgba_map[4]; |
653 |
|
|
int i; |
654 |
|
|
|
655 |
|
✗ |
ff_fill_rgba_map(rgba_map, inlink->format); |
656 |
|
✗ |
for (i = 0; i < 4; i++) |
657 |
|
✗ |
s->fill[rgba_map[i]] = s->rgba_color[i]; |
658 |
|
|
} else { |
659 |
|
✗ |
memcpy(s->fill, s->yuv_color, sizeof(s->yuv_color)); |
660 |
|
|
} |
661 |
|
|
|
662 |
|
✗ |
return 0; |
663 |
|
|
} |
664 |
|
|
|
665 |
|
✗ |
static int process_command(AVFilterContext *ctx, const char *cmd, const char *args, |
666 |
|
|
char *res, int res_len, int flags) |
667 |
|
|
{ |
668 |
|
|
int ret; |
669 |
|
|
|
670 |
|
✗ |
ret = ff_filter_process_command(ctx, cmd, args, res, res_len, flags); |
671 |
|
✗ |
if (ret < 0) |
672 |
|
✗ |
return ret; |
673 |
|
|
|
674 |
|
✗ |
return config_input(ctx->inputs[0]); |
675 |
|
|
} |
676 |
|
|
|
677 |
|
|
#define OFFSET(x) offsetof(FillBordersContext, x) |
678 |
|
|
#define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_RUNTIME_PARAM |
679 |
|
|
|
680 |
|
|
static const AVOption fillborders_options[] = { |
681 |
|
|
{ "left", "set the left fill border", OFFSET(left), AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX, FLAGS }, |
682 |
|
|
{ "right", "set the right fill border", OFFSET(right), AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX, FLAGS }, |
683 |
|
|
{ "top", "set the top fill border", OFFSET(top), AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX, FLAGS }, |
684 |
|
|
{ "bottom", "set the bottom fill border", OFFSET(bottom), AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX, FLAGS }, |
685 |
|
|
{ "mode", "set the fill borders mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=FM_SMEAR}, 0, FM_NB_MODES-1, FLAGS, .unit = "mode" }, |
686 |
|
|
{ "smear", NULL, 0, AV_OPT_TYPE_CONST, {.i64=FM_SMEAR}, 0, 0, FLAGS, .unit = "mode" }, |
687 |
|
|
{ "mirror", NULL, 0, AV_OPT_TYPE_CONST, {.i64=FM_MIRROR}, 0, 0, FLAGS, .unit = "mode" }, |
688 |
|
|
{ "fixed", NULL, 0, AV_OPT_TYPE_CONST, {.i64=FM_FIXED}, 0, 0, FLAGS, .unit = "mode" }, |
689 |
|
|
{ "reflect",NULL, 0, AV_OPT_TYPE_CONST, {.i64=FM_REFLECT},0, 0, FLAGS, .unit = "mode" }, |
690 |
|
|
{ "wrap", NULL, 0, AV_OPT_TYPE_CONST, {.i64=FM_WRAP}, 0, 0, FLAGS, .unit = "mode" }, |
691 |
|
|
{ "fade", NULL, 0, AV_OPT_TYPE_CONST, {.i64=FM_FADE}, 0, 0, FLAGS, .unit = "mode" }, |
692 |
|
|
{ "margins",NULL, 0, AV_OPT_TYPE_CONST, {.i64=FM_MARGINS},0, 0, FLAGS, .unit = "mode" }, |
693 |
|
|
{ "color", "set the color for the fixed/fade mode", OFFSET(rgba_color), AV_OPT_TYPE_COLOR, {.str = "black"}, .flags = FLAGS }, |
694 |
|
|
{ NULL } |
695 |
|
|
}; |
696 |
|
|
|
697 |
|
|
AVFILTER_DEFINE_CLASS(fillborders); |
698 |
|
|
|
699 |
|
|
static const AVFilterPad fillborders_inputs[] = { |
700 |
|
|
{ |
701 |
|
|
.name = "default", |
702 |
|
|
.type = AVMEDIA_TYPE_VIDEO, |
703 |
|
|
.flags = AVFILTERPAD_FLAG_NEEDS_WRITABLE, |
704 |
|
|
.config_props = config_input, |
705 |
|
|
.filter_frame = filter_frame, |
706 |
|
|
}, |
707 |
|
|
}; |
708 |
|
|
|
709 |
|
|
const AVFilter ff_vf_fillborders = { |
710 |
|
|
.name = "fillborders", |
711 |
|
|
.description = NULL_IF_CONFIG_SMALL("Fill borders of the input video."), |
712 |
|
|
.priv_size = sizeof(FillBordersContext), |
713 |
|
|
.priv_class = &fillborders_class, |
714 |
|
|
FILTER_INPUTS(fillborders_inputs), |
715 |
|
|
FILTER_OUTPUTS(ff_video_default_filterpad), |
716 |
|
|
FILTER_PIXFMTS_ARRAY(pix_fmts), |
717 |
|
|
.flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, |
718 |
|
|
.process_command = process_command, |
719 |
|
|
}; |
720 |
|
|
|