]> git.sesse.net Git - ffmpeg/blob - libavfilter/vf_floodfill.c
Merge commit '28a8b5413b64b831dfb8650208bccd8b78360484'
[ffmpeg] / libavfilter / vf_floodfill.c
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/opt.h"
22 #include "libavutil/imgutils.h"
23 #include "libavutil/intreadwrite.h"
24 #include "avfilter.h"
25 #include "formats.h"
26 #include "internal.h"
27 #include "video.h"
28
29 typedef struct Points {
30     uint16_t x, y;
31 } Points;
32
33 typedef struct FloodfillContext {
34     const AVClass *class;
35
36     int x, y;
37     int s0, s1, s2, s3;
38     int d0, d1, d2, d3;
39
40     int back, front;
41     Points *points;
42
43     int (*is_same)(AVFrame *frame, int x, int y,
44                    unsigned s0, unsigned s1, unsigned s2, unsigned s3);
45     void (*set_pixel)(AVFrame *frame, int x, int y,
46                       unsigned d0, unsigned d1, unsigned d2, unsigned d3);
47     void (*pick_pixel)(AVFrame *frame, int x, int y,
48                        int *s0, int *s1, int *s2, int *s3);
49 } FloodfillContext;
50
51 static int is_inside(int x, int y, int w, int h)
52 {
53     if (x >= 0 && x < w && y >= 0 && y < h)
54         return 1;
55     return 0;
56 }
57
58 static int is_same4(AVFrame *frame, int x, int y,
59                     unsigned s0, unsigned s1, unsigned s2, unsigned s3)
60 {
61     unsigned c0 = frame->data[0][y * frame->linesize[0] + x];
62     unsigned c1 = frame->data[1][y * frame->linesize[1] + x];
63     unsigned c2 = frame->data[2][y * frame->linesize[2] + x];
64     unsigned c3 = frame->data[3][y * frame->linesize[3] + x];
65
66     if (s0 == c0 && s1 == c1 && s2 == c2 && s3 == c3)
67         return 1;
68     return 0;
69 }
70
71 static int is_same4_16(AVFrame *frame, int x, int y,
72                        unsigned s0, unsigned s1, unsigned s2, unsigned s3)
73 {
74     unsigned c0 = AV_RN16(frame->data[0] + y * frame->linesize[0] + 2 * x);
75     unsigned c1 = AV_RN16(frame->data[1] + y * frame->linesize[1] + 2 * x);
76     unsigned c2 = AV_RN16(frame->data[2] + y * frame->linesize[2] + 2 * x);
77     unsigned c3 = AV_RN16(frame->data[3] + y * frame->linesize[3] + 2 * x);
78
79     if (s0 == c0 && s1 == c1 && s2 == c2 && s3 == c3)
80         return 1;
81     return 0;
82 }
83
84 static int is_same3(AVFrame *frame, int x, int y,
85                     unsigned s0, unsigned s1, unsigned s2, unsigned s3)
86 {
87     unsigned c0 = frame->data[0][y * frame->linesize[0] + x];
88     unsigned c1 = frame->data[1][y * frame->linesize[1] + x];
89     unsigned c2 = frame->data[2][y * frame->linesize[2] + x];
90
91     if (s0 == c0 && s1 == c1 && s2 == c2)
92         return 1;
93     return 0;
94 }
95
96 static int is_same3_16(AVFrame *frame, int x, int y,
97                        unsigned s0, unsigned s1, unsigned s2, unsigned s3)
98 {
99     unsigned c0 = AV_RN16(frame->data[0] + y * frame->linesize[0] + 2 * x);
100     unsigned c1 = AV_RN16(frame->data[1] + y * frame->linesize[1] + 2 * x);
101     unsigned c2 = AV_RN16(frame->data[2] + y * frame->linesize[2] + 2 * x);
102
103     if (s0 == c0 && s1 == c1 && s2 == c2)
104         return 1;
105     return 0;
106 }
107
108 static int is_same1(AVFrame *frame, int x, int y,
109                     unsigned s0, unsigned s1, unsigned s2, unsigned s3)
110 {
111     unsigned c0 = frame->data[0][y * frame->linesize[0] + x];
112
113     if (s0 == c0)
114         return 1;
115     return 0;
116 }
117
118 static int is_same1_16(AVFrame *frame, int x, int y,
119                        unsigned s0, unsigned s1, unsigned s2, unsigned s3)
120 {
121     unsigned c0 = AV_RN16(frame->data[0] + y * frame->linesize[0] + 2 * x);
122
123     if (s0 == c0)
124         return 1;
125     return 0;
126 }
127
128 static void set_pixel1(AVFrame *frame, int x, int y,
129                        unsigned d0, unsigned d1, unsigned d2, unsigned d3)
130 {
131     frame->data[0][y * frame->linesize[0] + x] = d0;
132 }
133
134 static void set_pixel1_16(AVFrame *frame, int x, int y,
135                           unsigned d0, unsigned d1, unsigned d2, unsigned d3)
136 {
137     AV_WN16(frame->data[0] + y * frame->linesize[0] + 2 * x, d0);
138 }
139
140 static void set_pixel3(AVFrame *frame, int x, int y,
141                        unsigned d0, unsigned d1, unsigned d2, unsigned d3)
142 {
143     frame->data[0][y * frame->linesize[0] + x] = d0;
144     frame->data[1][y * frame->linesize[1] + x] = d1;
145     frame->data[2][y * frame->linesize[2] + x] = d2;
146 }
147
148 static void set_pixel3_16(AVFrame *frame, int x, int y,
149                           unsigned d0, unsigned d1, unsigned d2, unsigned d3)
150 {
151     AV_WN16(frame->data[0] + y * frame->linesize[0] + 2 * x, d0);
152     AV_WN16(frame->data[1] + y * frame->linesize[1] + 2 * x, d1);
153     AV_WN16(frame->data[2] + y * frame->linesize[2] + 2 * x, d2);
154 }
155
156 static void set_pixel4(AVFrame *frame, int x, int y,
157                        unsigned d0, unsigned d1, unsigned d2, unsigned d3)
158 {
159     frame->data[0][y * frame->linesize[0] + x] = d0;
160     frame->data[1][y * frame->linesize[1] + x] = d1;
161     frame->data[2][y * frame->linesize[2] + x] = d2;
162     frame->data[3][y * frame->linesize[3] + x] = d3;
163 }
164
165 static void set_pixel4_16(AVFrame *frame, int x, int y,
166                           unsigned d0, unsigned d1, unsigned d2, unsigned d3)
167 {
168     AV_WN16(frame->data[0] + y * frame->linesize[0] + 2 * x, d0);
169     AV_WN16(frame->data[1] + y * frame->linesize[1] + 2 * x, d1);
170     AV_WN16(frame->data[2] + y * frame->linesize[2] + 2 * x, d2);
171     AV_WN16(frame->data[3] + y * frame->linesize[3] + 2 * x, d3);
172 }
173
174 static void pick_pixel1(AVFrame *frame, int x, int y,
175                         int *s0, int *s1, int *s2, int *s3)
176 {
177     if (*s0 < 0)
178         *s0 = frame->data[0][y * frame->linesize[0] + x];
179 }
180
181 static void pick_pixel1_16(AVFrame *frame, int x, int y,
182                            int *s0, int *s1, int *s2, int *s3)
183 {
184     if (*s0 < 0)
185         *s0 = AV_RN16(frame->data[0] + y * frame->linesize[0] + 2 * x);
186 }
187
188 static void pick_pixel3(AVFrame *frame, int x, int y,
189                         int *s0, int *s1, int *s2, int *s3)
190 {
191     if (*s0 < 0)
192         *s0 = frame->data[0][y * frame->linesize[0] + x];
193     if (*s1 < 0)
194         *s1 = frame->data[1][y * frame->linesize[1] + x];
195     if (*s2 < 0)
196         *s2 = frame->data[2][y * frame->linesize[2] + x];
197 }
198
199 static void pick_pixel3_16(AVFrame *frame, int x, int y,
200                            int *s0, int *s1, int *s2, int *s3)
201 {
202     if (*s0 < 0)
203         *s0 = AV_RN16(frame->data[0] + y * frame->linesize[0] + 2 * x);
204     if (*s1 < 0)
205         *s1 = AV_RN16(frame->data[1] + y * frame->linesize[1] + 2 * x);
206     if (*s2 < 0)
207         *s2 = AV_RN16(frame->data[2] + y * frame->linesize[2] + 2 * x);
208 }
209
210 static void pick_pixel4(AVFrame *frame, int x, int y,
211                         int *s0, int *s1, int *s2, int *s3)
212 {
213     if (*s0 < 0)
214         *s0 = frame->data[0][y * frame->linesize[0] + x];
215     if (*s1 < 0)
216         *s1 = frame->data[1][y * frame->linesize[1] + x];
217     if (*s2 < 0)
218         *s2 = frame->data[2][y * frame->linesize[2] + x];
219     if (*s3 < 0)
220         *s3 = frame->data[3][y * frame->linesize[3] + x];
221 }
222
223 static void pick_pixel4_16(AVFrame *frame, int x, int y,
224                            int *s0, int *s1, int *s2, int *s3)
225 {
226     if (*s0 < 0)
227         *s0 = AV_RN16(frame->data[0] + y * frame->linesize[0] + 2 * x);
228     if (*s1 < 0)
229         *s1 = AV_RN16(frame->data[1] + y * frame->linesize[1] + 2 * x);
230     if (*s2 < 0)
231         *s2 = AV_RN16(frame->data[2] + y * frame->linesize[2] + 2 * x);
232     if (*s3 < 0)
233         *s3 = AV_RN16(frame->data[3] + y * frame->linesize[3] + 2 * x);
234 }
235
236 static int config_input(AVFilterLink *inlink)
237 {
238     const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
239     AVFilterContext *ctx = inlink->dst;
240     FloodfillContext *s = ctx->priv;
241     int nb_planes = av_pix_fmt_count_planes(inlink->format);
242     int depth;
243
244     depth = desc->comp[0].depth;
245     if (depth == 8) {
246         switch (nb_planes) {
247         case 1: s->set_pixel  = set_pixel1;
248                 s->is_same    = is_same1;
249                 s->pick_pixel = pick_pixel1; break;
250         case 3: s->set_pixel  = set_pixel3;
251                 s->is_same    = is_same3;
252                 s->pick_pixel = pick_pixel3; break;
253         case 4: s->set_pixel  = set_pixel4;
254                 s->is_same    = is_same4;
255                 s->pick_pixel = pick_pixel4; break;
256        }
257     } else {
258         switch (nb_planes) {
259         case 1: s->set_pixel  = set_pixel1_16;
260                 s->is_same    = is_same1_16;
261                 s->pick_pixel = pick_pixel1_16; break;
262         case 3: s->set_pixel  = set_pixel3_16;
263                 s->is_same    = is_same3_16;
264                 s->pick_pixel = pick_pixel3_16; break;
265         case 4: s->set_pixel  = set_pixel4_16;
266                 s->is_same    = is_same4_16;
267                 s->pick_pixel = pick_pixel4_16; break;
268        }
269     }
270
271     s->front = s->back = 0;
272     s->points = av_calloc(inlink->w * inlink->h, 4 * sizeof(Points));
273     if (!s->points)
274         return AVERROR(ENOMEM);
275
276     return 0;
277 }
278
279 static int filter_frame(AVFilterLink *link, AVFrame *frame)
280 {
281     AVFilterContext *ctx = link->dst;
282     FloodfillContext *s = ctx->priv;
283     const unsigned d0 = s->d0;
284     const unsigned d1 = s->d1;
285     const unsigned d2 = s->d2;
286     const unsigned d3 = s->d3;
287     int s0 = s->s0;
288     int s1 = s->s1;
289     int s2 = s->s2;
290     int s3 = s->s3;
291     const int w = frame->width;
292     const int h = frame->height;
293     int ret;
294
295     if (ret = av_frame_make_writable(frame))
296         return ret;
297
298     if (is_inside(s->x, s->y, w, h)) {
299         s->pick_pixel(frame, s->x, s->y, &s0, &s1, &s2, &s3);
300
301         if (s->is_same(frame, s->x, s->y, s0, s1, s2, s3)) {
302             s->points[s->front].x = s->x;
303             s->points[s->front].y = s->y;
304             s->front++;
305         }
306
307         while (s->front > s->back) {
308             int x, y;
309
310             s->front--;
311             x = s->points[s->front].x;
312             y = s->points[s->front].y;
313
314             if (s->is_same(frame, x, y, s0, s1, s2, s3)) {
315                 s->set_pixel(frame, x, y, d0, d1, d2, d3);
316
317                 if (is_inside(x + 1, y, w, h)) {
318                     s->points[s->front]  .x = x + 1;
319                     s->points[s->front++].y = y;
320                 }
321
322                 if (is_inside(x - 1, y, w, h)) {
323                     s->points[s->front]  .x = x - 1;
324                     s->points[s->front++].y = y;
325                 }
326
327                 if (is_inside(x, y + 1, w, h)) {
328                     s->points[s->front]  .x = x;
329                     s->points[s->front++].y = y + 1;
330                 }
331
332                 if (is_inside(x, y - 1, w, h)) {
333                     s->points[s->front]  .x = x;
334                     s->points[s->front++].y = y - 1;
335                 }
336             }
337         }
338     }
339
340     return ff_filter_frame(ctx->outputs[0], frame);
341 }
342
343 static av_cold int query_formats(AVFilterContext *ctx)
344 {
345     static const enum AVPixelFormat pixel_fmts[] = {
346         AV_PIX_FMT_GRAY8,
347         AV_PIX_FMT_YUV444P,
348         AV_PIX_FMT_YUVA444P,
349         AV_PIX_FMT_GBRP,
350         AV_PIX_FMT_GBRP9,
351         AV_PIX_FMT_GBRP10,
352         AV_PIX_FMT_GBRAP10,
353         AV_PIX_FMT_GBRP12,
354         AV_PIX_FMT_GBRAP12,
355         AV_PIX_FMT_GBRP14,
356         AV_PIX_FMT_GBRP16,
357         AV_PIX_FMT_GBRAP16,
358         AV_PIX_FMT_GBRAP,
359         AV_PIX_FMT_YUV444P9,
360         AV_PIX_FMT_YUVA444P9,
361         AV_PIX_FMT_YUV444P10,
362         AV_PIX_FMT_YUVA444P10,
363         AV_PIX_FMT_YUV444P12,
364         AV_PIX_FMT_YUV444P14,
365         AV_PIX_FMT_GRAY16,
366         AV_PIX_FMT_YUV444P16,
367         AV_PIX_FMT_YUVA444P16,
368         AV_PIX_FMT_NONE
369     };
370     AVFilterFormats *formats;
371
372     formats = ff_make_format_list(pixel_fmts);
373     if (!formats)
374         return AVERROR(ENOMEM);
375
376     return ff_set_common_formats(ctx, formats);
377 }
378
379 static av_cold void uninit(AVFilterContext *ctx)
380 {
381     FloodfillContext *s = ctx->priv;
382
383     av_freep(&s->points);
384 }
385
386 static const AVFilterPad floodfill_inputs[] = {
387     {
388         .name         = "default",
389         .type         = AVMEDIA_TYPE_VIDEO,
390         .filter_frame = filter_frame,
391         .config_props = config_input,
392     },
393     { NULL }
394 };
395
396 static const AVFilterPad floodfill_outputs[] = {
397     {
398         .name = "default",
399         .type = AVMEDIA_TYPE_VIDEO,
400     },
401     { NULL }
402 };
403
404 #define OFFSET(x) offsetof(FloodfillContext, x)
405 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
406
407 static const AVOption floodfill_options[] = {
408     { "x",  "set pixel x coordinate",             OFFSET(x),  AV_OPT_TYPE_INT, {.i64=0}, 0, UINT16_MAX, FLAGS },
409     { "y",  "set pixel y coordinate",             OFFSET(y),  AV_OPT_TYPE_INT, {.i64=0}, 0, UINT16_MAX, FLAGS },
410     { "s0", "set source #0 component value",      OFFSET(s0), AV_OPT_TYPE_INT, {.i64=0},-1, UINT16_MAX, FLAGS },
411     { "s1", "set source #1 component value",      OFFSET(s1), AV_OPT_TYPE_INT, {.i64=0},-1, UINT16_MAX, FLAGS },
412     { "s2", "set source #2 component value",      OFFSET(s2), AV_OPT_TYPE_INT, {.i64=0},-1, UINT16_MAX, FLAGS },
413     { "s3", "set source #3 component value",      OFFSET(s3), AV_OPT_TYPE_INT, {.i64=0},-1, UINT16_MAX, FLAGS },
414     { "d0", "set destination #0 component value", OFFSET(d0), AV_OPT_TYPE_INT, {.i64=0}, 0, UINT16_MAX, FLAGS },
415     { "d1", "set destination #1 component value", OFFSET(d1), AV_OPT_TYPE_INT, {.i64=0}, 0, UINT16_MAX, FLAGS },
416     { "d2", "set destination #2 component value", OFFSET(d2), AV_OPT_TYPE_INT, {.i64=0}, 0, UINT16_MAX, FLAGS },
417     { "d3", "set destination #3 component value", OFFSET(d3), AV_OPT_TYPE_INT, {.i64=0}, 0, UINT16_MAX, FLAGS },
418     { NULL }
419 };
420
421 AVFILTER_DEFINE_CLASS(floodfill);
422
423 AVFilter ff_vf_floodfill = {
424     .name          = "floodfill",
425     .description   = NULL_IF_CONFIG_SMALL("Fill area with same color with another color."),
426     .priv_size     = sizeof(FloodfillContext),
427     .priv_class    = &floodfill_class,
428     .query_formats = query_formats,
429     .uninit        = uninit,
430     .inputs        = floodfill_inputs,
431     .outputs       = floodfill_outputs,
432     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
433 };