FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavfilter/vf_fillborders.c
Date: 2026-09-26 14:26:21
Exec Total Coverage
Lines: 0 361 0.0%
Functions: 0 19 0.0%
Branches: 0 224 0.0%

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 FFFilter ff_vf_fillborders = {
710 .p.name = "fillborders",
711 .p.description = NULL_IF_CONFIG_SMALL("Fill borders of the input video."),
712 .p.priv_class = &fillborders_class,
713 .p.flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
714 .priv_size = sizeof(FillBordersContext),
715 FILTER_INPUTS(fillborders_inputs),
716 FILTER_OUTPUTS(ff_video_default_filterpad),
717 FILTER_PIXFMTS_ARRAY(pix_fmts),
718 .process_command = process_command,
719 };
720