FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavfilter/vf_fillborders.c
Date: 2024-07-24 19:24:46
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 "internal.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