avfilter/vf_neighbor: simplify code little
[ffmpeg.git] / libavfilter / vf_neighbor.c
1 /*
2  * Copyright (c) 2012-2013 Oka Motofumi (chikuzen.mo at gmail dot com)
3  * Copyright (c) 2015 Paul B Mahol
4  *
5  * This file is part of FFmpeg.
6  *
7  * FFmpeg is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * FFmpeg is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with FFmpeg; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20  */
21
22 #include "libavutil/imgutils.h"
23 #include "libavutil/pixdesc.h"
24 #include "libavutil/opt.h"
25 #include "avfilter.h"
26 #include "formats.h"
27 #include "internal.h"
28 #include "video.h"
29
30 typedef struct NContext {
31     const AVClass *class;
32     int planeheight[4];
33     int planewidth[4];
34     int nb_planes;
35     int threshold[4];
36     int coordinates;
37
38     void (*filter)(uint8_t *dst, const uint8_t *p1, int width,
39                    int threshold, const uint8_t *coordinates[], int coord);
40 } NContext;
41
42 static int query_formats(AVFilterContext *ctx)
43 {
44     static const enum AVPixelFormat pix_fmts[] = {
45         AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUVA420P,
46         AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_YUVJ440P, AV_PIX_FMT_YUVJ422P,AV_PIX_FMT_YUVJ420P, AV_PIX_FMT_YUVJ411P,
47         AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUV440P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV411P, AV_PIX_FMT_YUV410P,
48         AV_PIX_FMT_GBRP, AV_PIX_FMT_GBRAP, AV_PIX_FMT_GRAY8, AV_PIX_FMT_NONE
49     };
50
51     return ff_set_common_formats(ctx, ff_make_format_list(pix_fmts));
52 }
53
54 static void erosion(uint8_t *dst, const uint8_t *p1, int width,
55                     int threshold, const uint8_t *coordinates[], int coord)
56 {
57     int x, i;
58
59     for (x = 0; x < width; x++) {
60         int min = p1[x];
61         int limit = FFMAX(min - threshold, 0);
62
63         for (i = 0; i < 8; i++) {
64             if (coord & (1 << i)) {
65                 min = FFMIN(min, *(coordinates[i] + x));
66             }
67             min = FFMAX(min, limit);
68         }
69
70         dst[x] = min;
71     }
72 }
73
74 static void dilation(uint8_t *dst, const uint8_t *p1, int width,
75                      int threshold, const uint8_t *coordinates[], int coord)
76 {
77     int x, i;
78
79     for (x = 0; x < width; x++) {
80         int max = p1[x];
81         int limit = FFMIN(max + threshold, 255);
82
83         for (i = 0; i < 8; i++) {
84             if (coord & (1 << i)) {
85                 max = FFMAX(max, *(coordinates[i] + x));
86             }
87             max = FFMIN(max, limit);
88         }
89
90         dst[x] = max;
91     }
92 }
93
94 static void deflate(uint8_t *dst, const uint8_t *p1, int width,
95                     int threshold, const uint8_t *coordinates[], int coord)
96 {
97     int x, i;
98
99     for (x = 0; x < width; x++) {
100         int sum = 0;
101         int limit = FFMAX(p1[x] - threshold, 0);
102
103         for (i = 0; i < 8; sum += *(coordinates[i++] + x));
104
105         dst[x] = FFMAX(FFMIN(sum / 8, p1[x]), limit);
106     }
107 }
108
109 static void inflate(uint8_t *dst, const uint8_t *p1, int width,
110                     int threshold, const uint8_t *coordinates[], int coord)
111 {
112     int x, i;
113
114     for (x = 0; x < width; x++) {
115         int sum = 0;
116         int limit = FFMIN(p1[x] + threshold, 255);
117
118         for (i = 0; i < 8; sum += *(coordinates[i++] + x));
119
120         dst[x] = FFMIN(FFMAX(sum / 8, p1[x]), limit);
121     }
122 }
123
124 static int config_input(AVFilterLink *inlink)
125 {
126     AVFilterContext *ctx = inlink->dst;
127     NContext *s = ctx->priv;
128     const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
129     int ret;
130
131     if ((ret = av_image_fill_linesizes(s->planewidth, inlink->format, inlink->w)) < 0)
132         return ret;
133
134     s->planeheight[1] = s->planeheight[2] = AV_CEIL_RSHIFT(inlink->h, desc->log2_chroma_h);
135     s->planeheight[0] = s->planeheight[3] = inlink->h;
136
137     s->nb_planes = av_pix_fmt_count_planes(inlink->format);
138
139     if (!strcmp(ctx->filter->name, "erosion"))
140         s->filter = erosion;
141     else if (!strcmp(ctx->filter->name, "dilation"))
142         s->filter = dilation;
143     else if (!strcmp(ctx->filter->name, "deflate"))
144         s->filter = deflate;
145     else if (!strcmp(ctx->filter->name, "inflate"))
146         s->filter = inflate;
147
148     return 0;
149 }
150
151 static int filter_frame(AVFilterLink *inlink, AVFrame *in)
152 {
153     AVFilterContext *ctx = inlink->dst;
154     AVFilterLink *outlink = ctx->outputs[0];
155     NContext *s = ctx->priv;
156     AVFrame *out;
157     int plane, y;
158
159     out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
160     if (!out) {
161         av_frame_free(&in);
162         return AVERROR(ENOMEM);
163     }
164     av_frame_copy_props(out, in);
165
166     for (plane = 0; plane < s->nb_planes; plane++) {
167         const int threshold = s->threshold[plane];
168         const int stride = in->linesize[plane];
169         const int dstride = out->linesize[plane];
170         const uint8_t *src = in->data[plane];
171         uint8_t *dst = out->data[plane];
172         const int height = s->planeheight[plane];
173         const int width  = s->planewidth[plane];
174
175         if (!threshold) {
176             av_image_copy_plane(dst, dstride, src, stride, width, height);
177             continue;
178         }
179
180         for (y = 0; y < height; y++) {
181             const int nh = y > 0;
182             const int ph = y < height - 1;
183             const uint8_t *coordinates[] = { src - nh * stride, src + 1 - nh * stride, src + 2 - nh * stride,
184                                              src,                                      src + 2,
185                                              src + ph * stride, src + 1 + ph * stride, src + 2 + ph * stride};
186
187             const uint8_t *coordinateslb[] = { src - nh * stride, src - nh * stride, src + 1 - nh * stride,
188                                                src,                                  src + 1,
189                                                src + ph * stride, src + ph * stride, src + 1 + ph * stride};
190
191             const uint8_t *coordinatesrb[] = { src + width - 2 - nh * stride, src + width - 1 - nh * stride, src + width - 1 - nh * stride,
192                                                src + width - 2,                                              src + width - 1,
193                                                src + width - 2 + ph * stride, src + width - 1 + ph * stride, src + width - 1 + ph * stride};
194
195             s->filter(dst,             src,             1,         threshold, coordinateslb, s->coordinates);
196             s->filter(dst         + 1, src         + 1, width - 2, threshold, coordinates,   s->coordinates);
197             s->filter(dst + width - 1, src + width - 1, 1,         threshold, coordinatesrb, s->coordinates);
198
199             src += stride;
200             dst += dstride;
201         }
202     }
203
204     av_frame_free(&in);
205     return ff_filter_frame(outlink, out);
206 }
207
208 static const AVFilterPad neighbor_inputs[] = {
209     {
210         .name         = "default",
211         .type         = AVMEDIA_TYPE_VIDEO,
212         .filter_frame = filter_frame,
213         .config_props = config_input,
214     },
215     { NULL }
216 };
217
218 static const AVFilterPad neighbor_outputs[] = {
219     {
220         .name = "default",
221         .type = AVMEDIA_TYPE_VIDEO,
222     },
223     { NULL }
224 };
225
226 #define OFFSET(x) offsetof(NContext, x)
227 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
228
229 #define DEFINE_NEIGHBOR_FILTER(name_, description_)          \
230 AVFILTER_DEFINE_CLASS(name_);                                \
231                                                              \
232 AVFilter ff_vf_##name_ = {                                   \
233     .name          = #name_,                                 \
234     .description   = NULL_IF_CONFIG_SMALL(description_),     \
235     .priv_size     = sizeof(NContext),                       \
236     .priv_class    = &name_##_class,                         \
237     .query_formats = query_formats,                          \
238     .inputs        = neighbor_inputs,                        \
239     .outputs       = neighbor_outputs,                       \
240     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, \
241 }
242
243 #if CONFIG_EROSION_FILTER
244
245 static const AVOption erosion_options[] = {
246     { "threshold0",  "set threshold for 1st plane",   OFFSET(threshold[0]),   AV_OPT_TYPE_INT, {.i64=65535}, 0, 65535, FLAGS },
247     { "threshold1",  "set threshold for 2nd plane",   OFFSET(threshold[1]),   AV_OPT_TYPE_INT, {.i64=65535}, 0, 65535, FLAGS },
248     { "threshold2",  "set threshold for 3rd plane",   OFFSET(threshold[2]),   AV_OPT_TYPE_INT, {.i64=65535}, 0, 65535, FLAGS },
249     { "threshold3",  "set threshold for 4th plane",   OFFSET(threshold[3]),   AV_OPT_TYPE_INT, {.i64=65535}, 0, 65535, FLAGS },
250     { "coordinates", "set coordinates",               OFFSET(coordinates),    AV_OPT_TYPE_INT, {.i64=255},   0, 255,   FLAGS },
251     { NULL }
252 };
253
254 DEFINE_NEIGHBOR_FILTER(erosion, "Apply erosion effect.");
255
256 #endif /* CONFIG_EROSION_FILTER */
257
258 #if CONFIG_DILATION_FILTER
259
260 static const AVOption dilation_options[] = {
261     { "threshold0",  "set threshold for 1st plane",   OFFSET(threshold[0]),   AV_OPT_TYPE_INT, {.i64=65535}, 0, 65535, FLAGS },
262     { "threshold1",  "set threshold for 2nd plane",   OFFSET(threshold[1]),   AV_OPT_TYPE_INT, {.i64=65535}, 0, 65535, FLAGS },
263     { "threshold2",  "set threshold for 3rd plane",   OFFSET(threshold[2]),   AV_OPT_TYPE_INT, {.i64=65535}, 0, 65535, FLAGS },
264     { "threshold3",  "set threshold for 4th plane",   OFFSET(threshold[3]),   AV_OPT_TYPE_INT, {.i64=65535}, 0, 65535, FLAGS },
265     { "coordinates", "set coordinates",               OFFSET(coordinates),    AV_OPT_TYPE_INT, {.i64=255},   0, 255,   FLAGS },
266     { NULL }
267 };
268
269 DEFINE_NEIGHBOR_FILTER(dilation, "Apply dilation effect.");
270
271 #endif /* CONFIG_DILATION_FILTER */
272
273 #if CONFIG_DEFLATE_FILTER
274
275 static const AVOption deflate_options[] = {
276     { "threshold0", "set threshold for 1st plane",   OFFSET(threshold[0]),   AV_OPT_TYPE_INT, {.i64=65535}, 0, 65535, FLAGS },
277     { "threshold1", "set threshold for 2nd plane",   OFFSET(threshold[1]),   AV_OPT_TYPE_INT, {.i64=65535}, 0, 65535, FLAGS },
278     { "threshold2", "set threshold for 3rd plane",   OFFSET(threshold[2]),   AV_OPT_TYPE_INT, {.i64=65535}, 0, 65535, FLAGS },
279     { "threshold3", "set threshold for 4th plane",   OFFSET(threshold[3]),   AV_OPT_TYPE_INT, {.i64=65535}, 0, 65535, FLAGS },
280     { NULL }
281 };
282
283 DEFINE_NEIGHBOR_FILTER(deflate, "Apply deflate effect.");
284
285 #endif /* CONFIG_DEFLATE_FILTER */
286
287 #if CONFIG_INFLATE_FILTER
288
289 static const AVOption inflate_options[] = {
290     { "threshold0", "set threshold for 1st plane",   OFFSET(threshold[0]),   AV_OPT_TYPE_INT, {.i64=65535}, 0, 65535, FLAGS },
291     { "threshold1", "set threshold for 2nd plane",   OFFSET(threshold[1]),   AV_OPT_TYPE_INT, {.i64=65535}, 0, 65535, FLAGS },
292     { "threshold2", "set threshold for 3rd plane",   OFFSET(threshold[2]),   AV_OPT_TYPE_INT, {.i64=65535}, 0, 65535, FLAGS },
293     { "threshold3", "set threshold for 4th plane",   OFFSET(threshold[3]),   AV_OPT_TYPE_INT, {.i64=65535}, 0, 65535, FLAGS },
294     { NULL }
295 };
296
297 DEFINE_NEIGHBOR_FILTER(inflate, "Apply inflate effect.");
298
299 #endif /* CONFIG_INFLATE_FILTER */