]> git.sesse.net Git - ffmpeg/blob - libavfilter/vf_convolution.c
Merge commit 'dad5fd59f3d6a8311365314cfcde0ebcd15c2b01'
[ffmpeg] / libavfilter / vf_convolution.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/avstring.h"
23 #include "libavutil/imgutils.h"
24 #include "libavutil/intreadwrite.h"
25 #include "libavutil/opt.h"
26 #include "libavutil/pixdesc.h"
27 #include "avfilter.h"
28 #include "formats.h"
29 #include "internal.h"
30 #include "video.h"
31
32 enum MatrixMode {
33     MATRIX_SQUARE,
34     MATRIX_ROW,
35     MATRIX_COLUMN,
36     MATRIX_NBMODES,
37 };
38
39 typedef struct ConvolutionContext {
40     const AVClass *class;
41
42     char *matrix_str[4];
43     float rdiv[4];
44     float bias[4];
45     int mode[4];
46     float scale;
47     float delta;
48     int planes;
49
50     int size[4];
51     int depth;
52     int max;
53     int bpc;
54     int nb_planes;
55     int nb_threads;
56     int planewidth[4];
57     int planeheight[4];
58     int matrix[4][49];
59     int matrix_length[4];
60     int copy[4];
61
62     void (*setup[4])(int radius, const uint8_t *c[], const uint8_t *src, int stride,
63                      int x, int width, int y, int height, int bpc);
64     void (*filter[4])(uint8_t *dst, int width,
65                       float rdiv, float bias, const int *const matrix,
66                       const uint8_t *c[], int peak, int radius,
67                       int dstride, int stride);
68 } ConvolutionContext;
69
70 #define OFFSET(x) offsetof(ConvolutionContext, x)
71 #define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
72
73 static const AVOption convolution_options[] = {
74     { "0m", "set matrix for 1st plane", OFFSET(matrix_str[0]), AV_OPT_TYPE_STRING, {.str="0 0 0 0 1 0 0 0 0"}, 0, 0, FLAGS },
75     { "1m", "set matrix for 2nd plane", OFFSET(matrix_str[1]), AV_OPT_TYPE_STRING, {.str="0 0 0 0 1 0 0 0 0"}, 0, 0, FLAGS },
76     { "2m", "set matrix for 3rd plane", OFFSET(matrix_str[2]), AV_OPT_TYPE_STRING, {.str="0 0 0 0 1 0 0 0 0"}, 0, 0, FLAGS },
77     { "3m", "set matrix for 4th plane", OFFSET(matrix_str[3]), AV_OPT_TYPE_STRING, {.str="0 0 0 0 1 0 0 0 0"}, 0, 0, FLAGS },
78     { "0rdiv", "set rdiv for 1st plane", OFFSET(rdiv[0]), AV_OPT_TYPE_FLOAT, {.dbl=0.0}, 0.0, INT_MAX, FLAGS},
79     { "1rdiv", "set rdiv for 2nd plane", OFFSET(rdiv[1]), AV_OPT_TYPE_FLOAT, {.dbl=0.0}, 0.0, INT_MAX, FLAGS},
80     { "2rdiv", "set rdiv for 3rd plane", OFFSET(rdiv[2]), AV_OPT_TYPE_FLOAT, {.dbl=0.0}, 0.0, INT_MAX, FLAGS},
81     { "3rdiv", "set rdiv for 4th plane", OFFSET(rdiv[3]), AV_OPT_TYPE_FLOAT, {.dbl=0.0}, 0.0, INT_MAX, FLAGS},
82     { "0bias", "set bias for 1st plane", OFFSET(bias[0]), AV_OPT_TYPE_FLOAT, {.dbl=0.0}, 0.0, INT_MAX, FLAGS},
83     { "1bias", "set bias for 2nd plane", OFFSET(bias[1]), AV_OPT_TYPE_FLOAT, {.dbl=0.0}, 0.0, INT_MAX, FLAGS},
84     { "2bias", "set bias for 3rd plane", OFFSET(bias[2]), AV_OPT_TYPE_FLOAT, {.dbl=0.0}, 0.0, INT_MAX, FLAGS},
85     { "3bias", "set bias for 4th plane", OFFSET(bias[3]), AV_OPT_TYPE_FLOAT, {.dbl=0.0}, 0.0, INT_MAX, FLAGS},
86     { "0mode", "set matrix mode for 1st plane", OFFSET(mode[0]), AV_OPT_TYPE_INT, {.i64=MATRIX_SQUARE}, 0, MATRIX_NBMODES-1, FLAGS, "mode" },
87     { "1mode", "set matrix mode for 2nd plane", OFFSET(mode[1]), AV_OPT_TYPE_INT, {.i64=MATRIX_SQUARE}, 0, MATRIX_NBMODES-1, FLAGS, "mode" },
88     { "2mode", "set matrix mode for 3rd plane", OFFSET(mode[2]), AV_OPT_TYPE_INT, {.i64=MATRIX_SQUARE}, 0, MATRIX_NBMODES-1, FLAGS, "mode" },
89     { "3mode", "set matrix mode for 4th plane", OFFSET(mode[3]), AV_OPT_TYPE_INT, {.i64=MATRIX_SQUARE}, 0, MATRIX_NBMODES-1, FLAGS, "mode" },
90     { "square", "square matrix",     0, AV_OPT_TYPE_CONST, {.i64=MATRIX_SQUARE}, 0, 0, FLAGS, "mode" },
91     { "row",    "single row matrix", 0, AV_OPT_TYPE_CONST, {.i64=MATRIX_ROW}   , 0, 0, FLAGS, "mode" },
92     { "column", "single column matrix", 0, AV_OPT_TYPE_CONST, {.i64=MATRIX_COLUMN}, 0, 0, FLAGS, "mode" },
93     { NULL }
94 };
95
96 AVFILTER_DEFINE_CLASS(convolution);
97
98 static const int same3x3[9] = {0, 0, 0,
99                                0, 1, 0,
100                                0, 0, 0};
101
102 static const int same5x5[25] = {0, 0, 0, 0, 0,
103                                 0, 0, 0, 0, 0,
104                                 0, 0, 1, 0, 0,
105                                 0, 0, 0, 0, 0,
106                                 0, 0, 0, 0, 0};
107
108 static const int same7x7[49] = {0, 0, 0, 0, 0, 0, 0,
109                                 0, 0, 0, 0, 0, 0, 0,
110                                 0, 0, 0, 0, 0, 0, 0,
111                                 0, 0, 0, 1, 0, 0, 0,
112                                 0, 0, 0, 0, 0, 0, 0,
113                                 0, 0, 0, 0, 0, 0, 0,
114                                 0, 0, 0, 0, 0, 0, 0};
115
116 static int query_formats(AVFilterContext *ctx)
117 {
118     static const enum AVPixelFormat pix_fmts[] = {
119         AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUV440P,
120         AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_YUVJ440P,
121         AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUVA420P, AV_PIX_FMT_YUV420P,
122         AV_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUVJ420P,
123         AV_PIX_FMT_YUVJ411P, AV_PIX_FMT_YUV411P, AV_PIX_FMT_YUV410P,
124         AV_PIX_FMT_YUV420P9, AV_PIX_FMT_YUV422P9, AV_PIX_FMT_YUV444P9,
125         AV_PIX_FMT_YUV420P10, AV_PIX_FMT_YUV422P10, AV_PIX_FMT_YUV444P10,
126         AV_PIX_FMT_YUV420P12, AV_PIX_FMT_YUV422P12, AV_PIX_FMT_YUV444P12, AV_PIX_FMT_YUV440P12,
127         AV_PIX_FMT_YUV420P14, AV_PIX_FMT_YUV422P14, AV_PIX_FMT_YUV444P14,
128         AV_PIX_FMT_YUV420P16, AV_PIX_FMT_YUV422P16, AV_PIX_FMT_YUV444P16,
129         AV_PIX_FMT_YUVA420P9, AV_PIX_FMT_YUVA422P9, AV_PIX_FMT_YUVA444P9,
130         AV_PIX_FMT_YUVA420P10, AV_PIX_FMT_YUVA422P10, AV_PIX_FMT_YUVA444P10,
131         AV_PIX_FMT_YUVA420P16, AV_PIX_FMT_YUVA422P16, AV_PIX_FMT_YUVA444P16,
132         AV_PIX_FMT_GBRP, AV_PIX_FMT_GBRP9, AV_PIX_FMT_GBRP10,
133         AV_PIX_FMT_GBRP12, AV_PIX_FMT_GBRP14, AV_PIX_FMT_GBRP16,
134         AV_PIX_FMT_GBRAP, AV_PIX_FMT_GBRAP10, AV_PIX_FMT_GBRAP12, AV_PIX_FMT_GBRAP16,
135         AV_PIX_FMT_GRAY8, AV_PIX_FMT_GRAY9, AV_PIX_FMT_GRAY10, AV_PIX_FMT_GRAY12, AV_PIX_FMT_GRAY14, AV_PIX_FMT_GRAY16,
136         AV_PIX_FMT_NONE
137     };
138
139     return ff_set_common_formats(ctx, ff_make_format_list(pix_fmts));
140 }
141
142 typedef struct ThreadData {
143     AVFrame *in, *out;
144 } ThreadData;
145
146 static void filter16_prewitt(uint8_t *dstp, int width,
147                              float scale, float delta, const int *const matrix,
148                              const uint8_t *c[], int peak, int radius,
149                              int dstride, int stride)
150 {
151     uint16_t *dst = (uint16_t *)dstp;
152     int x;
153
154     for (x = 0; x < width; x++) {
155         int suma = AV_RN16A(&c[0][2 * x]) * -1 + AV_RN16A(&c[1][2 * x]) * -1 + AV_RN16A(&c[2][2 * x]) * -1 +
156                    AV_RN16A(&c[6][2 * x]) *  1 + AV_RN16A(&c[7][2 * x]) *  1 + AV_RN16A(&c[8][2 * x]) *  1;
157         int sumb = AV_RN16A(&c[0][2 * x]) * -1 + AV_RN16A(&c[2][2 * x]) *  1 + AV_RN16A(&c[3][2 * x]) * -1 +
158                    AV_RN16A(&c[5][2 * x]) *  1 + AV_RN16A(&c[6][2 * x]) * -1 + AV_RN16A(&c[8][2 * x]) *  1;
159
160         dst[x] = av_clip(sqrtf(suma*suma + sumb*sumb) * scale + delta, 0, peak);
161     }
162 }
163
164 static void filter16_roberts(uint8_t *dstp, int width,
165                              float scale, float delta, const int *const matrix,
166                              const uint8_t *c[], int peak, int radius,
167                              int dstride, int stride)
168 {
169     uint16_t *dst = (uint16_t *)dstp;
170     int x;
171
172     for (x = 0; x < width; x++) {
173         int suma = AV_RN16A(&c[0][2 * x]) *  1 + AV_RN16A(&c[1][2 * x]) * -1;
174         int sumb = AV_RN16A(&c[4][2 * x]) *  1 + AV_RN16A(&c[3][2 * x]) * -1;
175
176         dst[x] = av_clip(sqrtf(suma*suma + sumb*sumb) * scale + delta, 0, peak);
177     }
178 }
179
180 static void filter16_sobel(uint8_t *dstp, int width,
181                            float scale, float delta, const int *const matrix,
182                            const uint8_t *c[], int peak, int radius,
183                            int dstride, int stride)
184 {
185     uint16_t *dst = (uint16_t *)dstp;
186     int x;
187
188     for (x = 0; x < width; x++) {
189         int suma = AV_RN16A(&c[0][2 * x]) * -1 + AV_RN16A(&c[1][2 * x]) * -2 + AV_RN16A(&c[2][2 * x]) * -1 +
190                    AV_RN16A(&c[6][2 * x]) *  1 + AV_RN16A(&c[7][2 * x]) *  2 + AV_RN16A(&c[8][2 * x]) *  1;
191         int sumb = AV_RN16A(&c[0][2 * x]) * -1 + AV_RN16A(&c[2][2 * x]) *  1 + AV_RN16A(&c[3][2 * x]) * -2 +
192                    AV_RN16A(&c[5][2 * x]) *  2 + AV_RN16A(&c[6][2 * x]) * -1 + AV_RN16A(&c[8][2 * x]) *  1;
193
194         dst[x] = av_clip(sqrtf(suma*suma + sumb*sumb) * scale + delta, 0, peak);
195     }
196 }
197
198 static void filter_prewitt(uint8_t *dst, int width,
199                            float scale, float delta, const int *const matrix,
200                            const uint8_t *c[], int peak, int radius,
201                            int dstride, int stride)
202 {
203     const uint8_t *c0 = c[0], *c1 = c[1], *c2 = c[2];
204     const uint8_t *c3 = c[3], *c5 = c[5];
205     const uint8_t *c6 = c[6], *c7 = c[7], *c8 = c[8];
206     int x;
207
208     for (x = 0; x < width; x++) {
209         int suma = c0[x] * -1 + c1[x] * -1 + c2[x] * -1 +
210                    c6[x] *  1 + c7[x] *  1 + c8[x] *  1;
211         int sumb = c0[x] * -1 + c2[x] *  1 + c3[x] * -1 +
212                    c5[x] *  1 + c6[x] * -1 + c8[x] *  1;
213
214         dst[x] = av_clip_uint8(sqrtf(suma*suma + sumb*sumb) * scale + delta);
215     }
216 }
217
218 static void filter_roberts(uint8_t *dst, int width,
219                            float scale, float delta, const int *const matrix,
220                            const uint8_t *c[], int peak, int radius,
221                            int dstride, int stride)
222 {
223     int x;
224
225     for (x = 0; x < width; x++) {
226         int suma = c[0][x] *  1 + c[1][x] * -1;
227         int sumb = c[4][x] *  1 + c[3][x] * -1;
228
229         dst[x] = av_clip_uint8(sqrtf(suma*suma + sumb*sumb) * scale + delta);
230     }
231 }
232
233 static void filter_sobel(uint8_t *dst, int width,
234                          float scale, float delta, const int *const matrix,
235                          const uint8_t *c[], int peak, int radius,
236                          int dstride, int stride)
237 {
238     const uint8_t *c0 = c[0], *c1 = c[1], *c2 = c[2];
239     const uint8_t *c3 = c[3], *c5 = c[5];
240     const uint8_t *c6 = c[6], *c7 = c[7], *c8 = c[8];
241     int x;
242
243     for (x = 0; x < width; x++) {
244         int suma = c0[x] * -1 + c1[x] * -2 + c2[x] * -1 +
245                    c6[x] *  1 + c7[x] *  2 + c8[x] *  1;
246         int sumb = c0[x] * -1 + c2[x] *  1 + c3[x] * -2 +
247                    c5[x] *  2 + c6[x] * -1 + c8[x] *  1;
248
249         dst[x] = av_clip_uint8(sqrtf(suma*suma + sumb*sumb) * scale + delta);
250     }
251 }
252
253 static void filter16_3x3(uint8_t *dstp, int width,
254                          float rdiv, float bias, const int *const matrix,
255                          const uint8_t *c[], int peak, int radius,
256                          int dstride, int stride)
257 {
258     uint16_t *dst = (uint16_t *)dstp;
259     int x;
260
261     for (x = 0; x < width; x++) {
262         int sum = AV_RN16A(&c[0][2 * x]) * matrix[0] +
263                   AV_RN16A(&c[1][2 * x]) * matrix[1] +
264                   AV_RN16A(&c[2][2 * x]) * matrix[2] +
265                   AV_RN16A(&c[3][2 * x]) * matrix[3] +
266                   AV_RN16A(&c[4][2 * x]) * matrix[4] +
267                   AV_RN16A(&c[5][2 * x]) * matrix[5] +
268                   AV_RN16A(&c[6][2 * x]) * matrix[6] +
269                   AV_RN16A(&c[7][2 * x]) * matrix[7] +
270                   AV_RN16A(&c[8][2 * x]) * matrix[8];
271         sum = (int)(sum * rdiv + bias + 0.5f);
272         dst[x] = av_clip(sum, 0, peak);
273     }
274 }
275
276 static void filter16_5x5(uint8_t *dstp, int width,
277                          float rdiv, float bias, const int *const matrix,
278                          const uint8_t *c[], int peak, int radius,
279                          int dstride, int stride)
280 {
281     uint16_t *dst = (uint16_t *)dstp;
282     int x;
283
284     for (x = 0; x < width; x++) {
285         int i, sum = 0;
286
287         for (i = 0; i < 25; i++)
288             sum += AV_RN16A(&c[i][2 * x]) * matrix[i];
289
290         sum = (int)(sum * rdiv + bias + 0.5f);
291         dst[x] = av_clip(sum, 0, peak);
292     }
293 }
294
295 static void filter16_7x7(uint8_t *dstp, int width,
296                          float rdiv, float bias, const int *const matrix,
297                          const uint8_t *c[], int peak, int radius,
298                          int dstride, int stride)
299 {
300     uint16_t *dst = (uint16_t *)dstp;
301     int x;
302
303     for (x = 0; x < width; x++) {
304         int i, sum = 0;
305
306         for (i = 0; i < 49; i++)
307             sum += AV_RN16A(&c[i][2 * x]) * matrix[i];
308
309         sum = (int)(sum * rdiv + bias + 0.5f);
310         dst[x] = av_clip(sum, 0, peak);
311     }
312 }
313
314 static void filter16_row(uint8_t *dstp, int width,
315                          float rdiv, float bias, const int *const matrix,
316                          const uint8_t *c[], int peak, int radius,
317                          int dstride, int stride)
318 {
319     uint16_t *dst = (uint16_t *)dstp;
320     int x;
321
322     for (x = 0; x < width; x++) {
323         int i, sum = 0;
324
325         for (i = 0; i < 2 * radius + 1; i++)
326             sum += AV_RN16A(&c[i][2 * x]) * matrix[i];
327
328         sum = (int)(sum * rdiv + bias + 0.5f);
329         dst[x] = av_clip(sum, 0, peak);
330     }
331 }
332
333 static void filter16_column(uint8_t *dstp, int height,
334                             float rdiv, float bias, const int *const matrix,
335                             const uint8_t *c[], int peak, int radius,
336                             int dstride, int stride)
337 {
338     uint16_t *dst = (uint16_t *)dstp;
339     int y;
340
341     for (y = 0; y < height; y++) {
342         int i, sum = 0;
343
344         for (i = 0; i < 2 * radius + 1; i++)
345             sum += AV_RN16A(&c[i][0 + y * stride]) * matrix[i];
346
347         sum = (int)(sum * rdiv + bias + 0.5f);
348         dst[0] = av_clip(sum, 0, peak);
349         dst += dstride / 2;
350     }
351 }
352
353 static void filter_7x7(uint8_t *dst, int width,
354                        float rdiv, float bias, const int *const matrix,
355                        const uint8_t *c[], int peak, int radius,
356                        int dstride, int stride)
357 {
358     int x;
359
360     for (x = 0; x < width; x++) {
361         int i, sum = 0;
362
363         for (i = 0; i < 49; i++)
364             sum += c[i][x] * matrix[i];
365
366         sum = (int)(sum * rdiv + bias + 0.5f);
367         dst[x] = av_clip_uint8(sum);
368     }
369 }
370
371 static void filter_5x5(uint8_t *dst, int width,
372                        float rdiv, float bias, const int *const matrix,
373                        const uint8_t *c[], int peak, int radius,
374                        int dstride, int stride)
375 {
376     int x;
377
378     for (x = 0; x < width; x++) {
379         int i, sum = 0;
380
381         for (i = 0; i < 25; i++)
382             sum += c[i][x] * matrix[i];
383
384         sum = (int)(sum * rdiv + bias + 0.5f);
385         dst[x] = av_clip_uint8(sum);
386     }
387 }
388
389 static void filter_3x3(uint8_t *dst, int width,
390                        float rdiv, float bias, const int *const matrix,
391                        const uint8_t *c[], int peak, int radius,
392                        int dstride, int stride)
393 {
394     const uint8_t *c0 = c[0], *c1 = c[1], *c2 = c[2];
395     const uint8_t *c3 = c[3], *c4 = c[4], *c5 = c[5];
396     const uint8_t *c6 = c[6], *c7 = c[7], *c8 = c[8];
397     int x;
398
399     for (x = 0; x < width; x++) {
400         int sum = c0[x] * matrix[0] + c1[x] * matrix[1] + c2[x] * matrix[2] +
401                   c3[x] * matrix[3] + c4[x] * matrix[4] + c5[x] * matrix[5] +
402                   c6[x] * matrix[6] + c7[x] * matrix[7] + c8[x] * matrix[8];
403         sum = (int)(sum * rdiv + bias + 0.5f);
404         dst[x] = av_clip_uint8(sum);
405     }
406 }
407
408 static void filter_row(uint8_t *dst, int width,
409                        float rdiv, float bias, const int *const matrix,
410                        const uint8_t *c[], int peak, int radius,
411                        int dstride, int stride)
412 {
413     int x;
414
415     for (x = 0; x < width; x++) {
416         int i, sum = 0;
417
418         for (i = 0; i < 2 * radius + 1; i++)
419             sum += c[i][x] * matrix[i];
420
421         sum = (int)(sum * rdiv + bias + 0.5f);
422         dst[x] = av_clip_uint8(sum);
423     }
424 }
425
426 static void filter_column(uint8_t *dst, int height,
427                           float rdiv, float bias, const int *const matrix,
428                           const uint8_t *c[], int peak, int radius,
429                           int dstride, int stride)
430 {
431     int y;
432
433     for (y = 0; y < height; y++) {
434         int i, sum = 0;
435
436         for (i = 0; i < 2 * radius + 1; i++)
437             sum += c[i][0 + y * stride] * matrix[i];
438
439         sum = (int)(sum * rdiv + bias + 0.5f);
440         dst[0] = av_clip_uint8(sum);
441         dst += dstride;
442     }
443 }
444
445 static void setup_3x3(int radius, const uint8_t *c[], const uint8_t *src, int stride,
446                       int x, int w, int y, int h, int bpc)
447 {
448     int i;
449
450     for (i = 0; i < 9; i++) {
451         int xoff = FFABS(x + ((i % 3) - 1));
452         int yoff = FFABS(y + (i / 3) - 1);
453
454         xoff = xoff >= w ? 2 * w - 1 - xoff : xoff;
455         yoff = yoff >= h ? 2 * h - 1 - yoff : yoff;
456
457         c[i] = src + xoff * bpc + yoff * stride;
458     }
459 }
460
461 static void setup_5x5(int radius, const uint8_t *c[], const uint8_t *src, int stride,
462                       int x, int w, int y, int h, int bpc)
463 {
464     int i;
465
466     for (i = 0; i < 25; i++) {
467         int xoff = FFABS(x + ((i % 5) - 2));
468         int yoff = FFABS(y + (i / 5) - 2);
469
470         xoff = xoff >= w ? 2 * w - 1 - xoff : xoff;
471         yoff = yoff >= h ? 2 * h - 1 - yoff : yoff;
472
473         c[i] = src + xoff * bpc + yoff * stride;
474     }
475 }
476
477 static void setup_7x7(int radius, const uint8_t *c[], const uint8_t *src, int stride,
478                       int x, int w, int y, int h, int bpc)
479 {
480     int i;
481
482     for (i = 0; i < 49; i++) {
483         int xoff = FFABS(x + ((i % 7) - 3));
484         int yoff = FFABS(y + (i / 7) - 3);
485
486         xoff = xoff >= w ? 2 * w - 1 - xoff : xoff;
487         yoff = yoff >= h ? 2 * h - 1 - yoff : yoff;
488
489         c[i] = src + xoff * bpc + yoff * stride;
490     }
491 }
492
493 static void setup_row(int radius, const uint8_t *c[], const uint8_t *src, int stride,
494                       int x, int w, int y, int h, int bpc)
495 {
496     int i;
497
498     for (i = 0; i < radius * 2 + 1; i++) {
499         int xoff = FFABS(x + i - radius);
500
501         xoff = xoff >= w ? 2 * w - 1 - xoff : xoff;
502
503         c[i] = src + xoff * bpc + y * stride;
504     }
505 }
506
507 static void setup_column(int radius, const uint8_t *c[], const uint8_t *src, int stride,
508                          int x, int w, int y, int h, int bpc)
509 {
510     int i;
511
512     for (i = 0; i < radius * 2 + 1; i++) {
513         int xoff = FFABS(x + i - radius);
514
515         xoff = xoff >= h ? 2 * h - 1 - xoff : xoff;
516
517         c[i] = src + y * bpc + xoff * stride;
518     }
519 }
520
521 static int filter_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
522 {
523     ConvolutionContext *s = ctx->priv;
524     ThreadData *td = arg;
525     AVFrame *in = td->in;
526     AVFrame *out = td->out;
527     int plane;
528
529     for (plane = 0; plane < s->nb_planes; plane++) {
530         const int mode = s->mode[plane];
531         const int bpc = s->bpc;
532         const int radius = s->size[plane] / 2;
533         const int height = s->planeheight[plane];
534         const int width  = s->planewidth[plane];
535         const int stride = in->linesize[plane];
536         const int dstride = out->linesize[plane];
537         const int sizeh = mode == MATRIX_COLUMN ? width : height;
538         const int sizew = mode == MATRIX_COLUMN ? height : width;
539         const int slice_start = (sizeh * jobnr) / nb_jobs;
540         const int slice_end = (sizeh * (jobnr+1)) / nb_jobs;
541         const float rdiv = s->rdiv[plane];
542         const float bias = s->bias[plane];
543         const uint8_t *src = in->data[plane];
544         const int dst_pos = slice_start * (mode == MATRIX_COLUMN ? bpc : dstride);
545         uint8_t *dst = out->data[plane] + dst_pos;
546         const int *matrix = s->matrix[plane];
547         const uint8_t *c[49];
548         int y, x;
549
550         if (s->copy[plane]) {
551             if (mode == MATRIX_COLUMN)
552                 av_image_copy_plane(dst, dstride, src + slice_start * bpc, stride,
553                                     (slice_end - slice_start) * bpc, height);
554             else
555                 av_image_copy_plane(dst, dstride, src + slice_start * stride, stride,
556                                     width * bpc, slice_end - slice_start);
557             continue;
558         }
559
560         for (y = slice_start; y < slice_end; y++) {
561             const int xoff = mode == MATRIX_COLUMN ? (y - slice_start) * bpc : radius * bpc;
562             const int yoff = mode == MATRIX_COLUMN ? radius * stride : 0;
563
564             for (x = 0; x < radius; x++) {
565                 const int xoff = mode == MATRIX_COLUMN ? (y - slice_start) * bpc : x * bpc;
566                 const int yoff = mode == MATRIX_COLUMN ? x * stride : 0;
567
568                 s->setup[plane](radius, c, src, stride, x, width, y, height, bpc);
569                 s->filter[plane](dst + yoff + xoff, 1, rdiv,
570                                  bias, matrix, c, s->max, radius,
571                                  dstride, stride);
572             }
573             s->setup[plane](radius, c, src, stride, radius, width, y, height, bpc);
574             s->filter[plane](dst + yoff + xoff, sizew - 2 * radius,
575                              rdiv, bias, matrix, c, s->max, radius,
576                              dstride, stride);
577             for (x = sizew - radius; x < sizew; x++) {
578                 const int xoff = mode == MATRIX_COLUMN ? (y - slice_start) * bpc : x * bpc;
579                 const int yoff = mode == MATRIX_COLUMN ? x * stride : 0;
580
581                 s->setup[plane](radius, c, src, stride, x, width, y, height, bpc);
582                 s->filter[plane](dst + yoff + xoff, 1, rdiv,
583                                  bias, matrix, c, s->max, radius,
584                                  dstride, stride);
585             }
586             if (mode != MATRIX_COLUMN)
587                 dst += dstride;
588         }
589     }
590
591     return 0;
592 }
593
594 static int config_input(AVFilterLink *inlink)
595 {
596     AVFilterContext *ctx = inlink->dst;
597     ConvolutionContext *s = ctx->priv;
598     const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
599     int p;
600
601     s->depth = desc->comp[0].depth;
602     s->max = (1 << s->depth) - 1;
603
604     s->planewidth[1] = s->planewidth[2] = AV_CEIL_RSHIFT(inlink->w, desc->log2_chroma_w);
605     s->planewidth[0] = s->planewidth[3] = inlink->w;
606     s->planeheight[1] = s->planeheight[2] = AV_CEIL_RSHIFT(inlink->h, desc->log2_chroma_h);
607     s->planeheight[0] = s->planeheight[3] = inlink->h;
608
609     s->nb_planes = av_pix_fmt_count_planes(inlink->format);
610     s->nb_threads = ff_filter_get_nb_threads(ctx);
611     s->bpc = (s->depth + 7) / 8;
612
613     if (!strcmp(ctx->filter->name, "convolution")) {
614         if (s->depth > 8) {
615             for (p = 0; p < s->nb_planes; p++) {
616                 if (s->mode[p] == MATRIX_ROW)
617                     s->filter[p] = filter16_row;
618                 else if (s->mode[p] == MATRIX_COLUMN)
619                     s->filter[p] = filter16_column;
620                 else if (s->size[p] == 3)
621                     s->filter[p] = filter16_3x3;
622                 else if (s->size[p] == 5)
623                     s->filter[p] = filter16_5x5;
624                 else if (s->size[p] == 7)
625                     s->filter[p] = filter16_7x7;
626             }
627         }
628     } else if (!strcmp(ctx->filter->name, "prewitt")) {
629         if (s->depth > 8)
630             for (p = 0; p < s->nb_planes; p++)
631                 s->filter[p] = filter16_prewitt;
632     } else if (!strcmp(ctx->filter->name, "roberts")) {
633         if (s->depth > 8)
634             for (p = 0; p < s->nb_planes; p++)
635                 s->filter[p] = filter16_roberts;
636     } else if (!strcmp(ctx->filter->name, "sobel")) {
637         if (s->depth > 8)
638             for (p = 0; p < s->nb_planes; p++)
639                 s->filter[p] = filter16_sobel;
640     }
641
642     return 0;
643 }
644
645 static int filter_frame(AVFilterLink *inlink, AVFrame *in)
646 {
647     AVFilterContext *ctx = inlink->dst;
648     ConvolutionContext *s = ctx->priv;
649     AVFilterLink *outlink = ctx->outputs[0];
650     AVFrame *out;
651     ThreadData td;
652
653     out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
654     if (!out) {
655         av_frame_free(&in);
656         return AVERROR(ENOMEM);
657     }
658     av_frame_copy_props(out, in);
659
660     td.in = in;
661     td.out = out;
662     ctx->internal->execute(ctx, filter_slice, &td, NULL, FFMIN3(s->planeheight[1], s->planewidth[1], s->nb_threads));
663
664     av_frame_free(&in);
665     return ff_filter_frame(outlink, out);
666 }
667
668 static av_cold int init(AVFilterContext *ctx)
669 {
670     ConvolutionContext *s = ctx->priv;
671     int i;
672
673     if (!strcmp(ctx->filter->name, "convolution")) {
674         for (i = 0; i < 4; i++) {
675             int *matrix = (int *)s->matrix[i];
676             char *p, *arg, *saveptr = NULL;
677             float sum = 0;
678
679             p = s->matrix_str[i];
680             while (s->matrix_length[i] < 49) {
681                 if (!(arg = av_strtok(p, " ", &saveptr)))
682                     break;
683
684                 p = NULL;
685                 sscanf(arg, "%d", &matrix[s->matrix_length[i]]);
686                 sum += matrix[s->matrix_length[i]];
687                 s->matrix_length[i]++;
688             }
689
690             if (!(s->matrix_length[i] & 1)) {
691                 av_log(ctx, AV_LOG_ERROR, "number of matrix elements must be odd\n");
692                 return AVERROR(EINVAL);
693             }
694             if (s->mode[i] == MATRIX_ROW) {
695                 s->filter[i] = filter_row;
696                 s->setup[i] = setup_row;
697                 s->size[i] = s->matrix_length[i];
698             } else if (s->mode[i] == MATRIX_COLUMN) {
699                 s->filter[i] = filter_column;
700                 s->setup[i] = setup_column;
701                 s->size[i] = s->matrix_length[i];
702             } else if (s->matrix_length[i] == 9) {
703                 s->size[i] = 3;
704                 if (!memcmp(matrix, same3x3, sizeof(same3x3)))
705                     s->copy[i] = 1;
706                 else
707                     s->filter[i] = filter_3x3;
708                 s->setup[i] = setup_3x3;
709             } else if (s->matrix_length[i] == 25) {
710                 s->size[i] = 5;
711                 if (!memcmp(matrix, same5x5, sizeof(same5x5)))
712                     s->copy[i] = 1;
713                 else
714                     s->filter[i] = filter_5x5;
715                 s->setup[i] = setup_5x5;
716             } else if (s->matrix_length[i] == 49) {
717                 s->size[i] = 7;
718                 if (!memcmp(matrix, same7x7, sizeof(same7x7)))
719                     s->copy[i] = 1;
720                 else
721                     s->filter[i] = filter_7x7;
722                 s->setup[i] = setup_7x7;
723             } else {
724                 return AVERROR(EINVAL);
725             }
726
727             if (sum == 0)
728                 sum = 1;
729             if (s->rdiv[i] == 0)
730                 s->rdiv[i] = 1. / sum;
731
732             if (s->copy[i] && (s->rdiv[i] != 1. || s->bias[i] != 0.))
733                 s->copy[i] = 0;
734         }
735     } else if (!strcmp(ctx->filter->name, "prewitt")) {
736         for (i = 0; i < 4; i++) {
737             if ((1 << i) & s->planes)
738                 s->filter[i] = filter_prewitt;
739             else
740                 s->copy[i] = 1;
741             s->size[i] = 3;
742             s->setup[i] = setup_3x3;
743             s->rdiv[i] = s->scale;
744             s->bias[i] = s->delta;
745         }
746     } else if (!strcmp(ctx->filter->name, "roberts")) {
747         for (i = 0; i < 4; i++) {
748             if ((1 << i) & s->planes)
749                 s->filter[i] = filter_roberts;
750             else
751                 s->copy[i] = 1;
752             s->size[i] = 3;
753             s->setup[i] = setup_3x3;
754             s->rdiv[i] = s->scale;
755             s->bias[i] = s->delta;
756         }
757     } else if (!strcmp(ctx->filter->name, "sobel")) {
758         for (i = 0; i < 4; i++) {
759             if ((1 << i) & s->planes)
760                 s->filter[i] = filter_sobel;
761             else
762                 s->copy[i] = 1;
763             s->size[i] = 3;
764             s->setup[i] = setup_3x3;
765             s->rdiv[i] = s->scale;
766             s->bias[i] = s->delta;
767         }
768     }
769
770     return 0;
771 }
772
773 static const AVFilterPad convolution_inputs[] = {
774     {
775         .name         = "default",
776         .type         = AVMEDIA_TYPE_VIDEO,
777         .config_props = config_input,
778         .filter_frame = filter_frame,
779     },
780     { NULL }
781 };
782
783 static const AVFilterPad convolution_outputs[] = {
784     {
785         .name = "default",
786         .type = AVMEDIA_TYPE_VIDEO,
787     },
788     { NULL }
789 };
790
791 #if CONFIG_CONVOLUTION_FILTER
792
793 AVFilter ff_vf_convolution = {
794     .name          = "convolution",
795     .description   = NULL_IF_CONFIG_SMALL("Apply convolution filter."),
796     .priv_size     = sizeof(ConvolutionContext),
797     .priv_class    = &convolution_class,
798     .init          = init,
799     .query_formats = query_formats,
800     .inputs        = convolution_inputs,
801     .outputs       = convolution_outputs,
802     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,
803 };
804
805 #endif /* CONFIG_CONVOLUTION_FILTER */
806
807 #if CONFIG_PREWITT_FILTER
808
809 static const AVOption prewitt_options[] = {
810     { "planes", "set planes to filter", OFFSET(planes), AV_OPT_TYPE_INT,  {.i64=15}, 0, 15, FLAGS},
811     { "scale",  "set scale",            OFFSET(scale), AV_OPT_TYPE_FLOAT, {.dbl=1.0}, 0.0,  65535, FLAGS},
812     { "delta",  "set delta",            OFFSET(delta), AV_OPT_TYPE_FLOAT, {.dbl=0}, -65535, 65535, FLAGS},
813     { NULL }
814 };
815
816 AVFILTER_DEFINE_CLASS(prewitt);
817
818 AVFilter ff_vf_prewitt = {
819     .name          = "prewitt",
820     .description   = NULL_IF_CONFIG_SMALL("Apply prewitt operator."),
821     .priv_size     = sizeof(ConvolutionContext),
822     .priv_class    = &prewitt_class,
823     .init          = init,
824     .query_formats = query_formats,
825     .inputs        = convolution_inputs,
826     .outputs       = convolution_outputs,
827     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,
828 };
829
830 #endif /* CONFIG_PREWITT_FILTER */
831
832 #if CONFIG_SOBEL_FILTER
833
834 static const AVOption sobel_options[] = {
835     { "planes", "set planes to filter", OFFSET(planes), AV_OPT_TYPE_INT,  {.i64=15}, 0, 15, FLAGS},
836     { "scale",  "set scale",            OFFSET(scale), AV_OPT_TYPE_FLOAT, {.dbl=1.0}, 0.0,  65535, FLAGS},
837     { "delta",  "set delta",            OFFSET(delta), AV_OPT_TYPE_FLOAT, {.dbl=0}, -65535, 65535, FLAGS},
838     { NULL }
839 };
840
841 AVFILTER_DEFINE_CLASS(sobel);
842
843 AVFilter ff_vf_sobel = {
844     .name          = "sobel",
845     .description   = NULL_IF_CONFIG_SMALL("Apply sobel operator."),
846     .priv_size     = sizeof(ConvolutionContext),
847     .priv_class    = &sobel_class,
848     .init          = init,
849     .query_formats = query_formats,
850     .inputs        = convolution_inputs,
851     .outputs       = convolution_outputs,
852     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,
853 };
854
855 #endif /* CONFIG_SOBEL_FILTER */
856
857 #if CONFIG_ROBERTS_FILTER
858
859 static const AVOption roberts_options[] = {
860     { "planes", "set planes to filter", OFFSET(planes), AV_OPT_TYPE_INT,  {.i64=15}, 0, 15, FLAGS},
861     { "scale",  "set scale",            OFFSET(scale), AV_OPT_TYPE_FLOAT, {.dbl=1.0}, 0.0,  65535, FLAGS},
862     { "delta",  "set delta",            OFFSET(delta), AV_OPT_TYPE_FLOAT, {.dbl=0}, -65535, 65535, FLAGS},
863     { NULL }
864 };
865
866 AVFILTER_DEFINE_CLASS(roberts);
867
868 AVFilter ff_vf_roberts = {
869     .name          = "roberts",
870     .description   = NULL_IF_CONFIG_SMALL("Apply roberts cross operator."),
871     .priv_size     = sizeof(ConvolutionContext),
872     .priv_class    = &roberts_class,
873     .init          = init,
874     .query_formats = query_formats,
875     .inputs        = convolution_inputs,
876     .outputs       = convolution_outputs,
877     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,
878 };
879
880 #endif /* CONFIG_ROBERTS_FILTER */