1 /*
2 * Copyright (c) 2012 Laurent de Soras
3 * Copyright (c) 2013 Fredrik Mellbin
4 * Copyright (c) 2015 Paul B Mahol
5 * Copyright (c) 2015 James Darnley
6 *
7 * This file is part of FFmpeg.
8 *
9 * FFmpeg is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
13 *
14 * FFmpeg is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with FFmpeg; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 */
23
24 #include "libavutil/imgutils.h"
25 #include "libavutil/opt.h"
26 #include "libavutil/pixdesc.h"
27 #include "libavutil/qsort.h"
28 #include "avfilter.h"
29 #include "formats.h"
30 #include "internal.h"
31 #include "removegrain.h"
32 #include "video.h"
33
34 #define OFFSET(x) offsetof(RemoveGrainContext, x)
35 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
36
37 static const AVOption removegrain_options[] = {
38 { "m0", "set mode for 1st plane", OFFSET(mode[0]), AV_OPT_TYPE_INT, {.i64=0}, 0, 24, FLAGS },
39 { "m1", "set mode for 2nd plane", OFFSET(mode[1]), AV_OPT_TYPE_INT, {.i64=0}, 0, 24, FLAGS },
40 { "m2", "set mode for 3rd plane", OFFSET(mode[2]), AV_OPT_TYPE_INT, {.i64=0}, 0, 24, FLAGS },
41 { "m3", "set mode for 4th plane", OFFSET(mode[3]), AV_OPT_TYPE_INT, {.i64=0}, 0, 24, FLAGS },
42 {NULL}
43 };
44
45 AVFILTER_DEFINE_CLASS(removegrain);
46
query_formats(AVFilterContext * ctx)47 static int query_formats(AVFilterContext *ctx)
48 {
49 static const enum AVPixelFormat pix_fmts[] = {
50 AV_PIX_FMT_GRAY8,
51 AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV444P,
52 AV_PIX_FMT_YUVA420P, AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUVA444P,
53 AV_PIX_FMT_YUV440P, AV_PIX_FMT_YUV411P, AV_PIX_FMT_YUV410P,
54 AV_PIX_FMT_YUVJ411P, AV_PIX_FMT_YUVJ420P, AV_PIX_FMT_YUVJ422P,
55 AV_PIX_FMT_YUVJ440P, AV_PIX_FMT_YUVJ444P,
56 AV_PIX_FMT_GBRP, AV_PIX_FMT_GBRAP,
57 AV_PIX_FMT_NONE
58 };
59
60 AVFilterFormats *fmts_list = ff_make_format_list(pix_fmts);
61 if (!fmts_list)
62 return AVERROR(ENOMEM);
63 return ff_set_common_formats(ctx, fmts_list);
64 }
65
66 #define REMOVE_GRAIN_SORT_AXIS \
67 const int ma1 = FFMAX(a1, a8); \
68 const int mi1 = FFMIN(a1, a8); \
69 const int ma2 = FFMAX(a2, a7); \
70 const int mi2 = FFMIN(a2, a7); \
71 const int ma3 = FFMAX(a3, a6); \
72 const int mi3 = FFMIN(a3, a6); \
73 const int ma4 = FFMAX(a4, a5); \
74 const int mi4 = FFMIN(a4, a5);
75
mode01(int c,int a1,int a2,int a3,int a4,int a5,int a6,int a7,int a8)76 static int mode01(int c, int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8)
77 {
78 const int mi = FFMIN(FFMIN(FFMIN(a1, a2), FFMIN(a3, a4)), FFMIN(FFMIN(a5, a6), FFMIN(a7, a8)));
79 const int ma = FFMAX(FFMAX(FFMAX(a1, a2), FFMAX(a3, a4)), FFMAX(FFMAX(a5, a6), FFMAX(a7, a8)));
80
81 return av_clip(c, mi, ma);
82 }
83
cmp_int(const void * p1,const void * p2)84 static int cmp_int(const void *p1, const void *p2)
85 {
86 int left = *(const int *)p1;
87 int right = *(const int *)p2;
88 return FFDIFFSIGN(left, right);
89 }
90
mode02(int c,int a1,int a2,int a3,int a4,int a5,int a6,int a7,int a8)91 static int mode02(int c, int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8)
92 {
93 int a[8] = { a1, a2, a3, a4, a5, a6, a7, a8 };
94
95 AV_QSORT(a, 8, int, cmp_int);
96
97 return av_clip(c, a[2 - 1 ], a[7 - 1]);
98 }
99
mode03(int c,int a1,int a2,int a3,int a4,int a5,int a6,int a7,int a8)100 static int mode03(int c, int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8)
101 {
102 int a[8] = { a1, a2, a3, a4, a5, a6, a7, a8 };
103
104 AV_QSORT(a, 8, int, cmp_int);
105
106 return av_clip(c, a[3 - 1 ], a[6 - 1]);
107 }
108
mode04(int c,int a1,int a2,int a3,int a4,int a5,int a6,int a7,int a8)109 static int mode04(int c, int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8)
110 {
111 int a[8] = { a1, a2, a3, a4, a5, a6, a7, a8 };
112
113 AV_QSORT(a, 8, int, cmp_int);
114
115 return av_clip(c, a[4 - 1 ], a[5 - 1]);
116 }
117
mode05(int c,int a1,int a2,int a3,int a4,int a5,int a6,int a7,int a8)118 static int mode05(int c, int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8)
119 {
120 REMOVE_GRAIN_SORT_AXIS
121
122 const int c1 = FFABS(c - av_clip(c, mi1, ma1));
123 const int c2 = FFABS(c - av_clip(c, mi2, ma2));
124 const int c3 = FFABS(c - av_clip(c, mi3, ma3));
125 const int c4 = FFABS(c - av_clip(c, mi4, ma4));
126
127 const int mindiff = FFMIN(FFMIN(c1, c2), FFMIN(c3, c4));
128
129 /* When adding SIMD notice the return order here: 4, 2, 3, 1. */
130 if (mindiff == c4) {
131 return av_clip(c, mi4, ma4);
132 } else if (mindiff == c2) {
133 return av_clip(c, mi2, ma2);
134 } else if (mindiff == c3) {
135 return av_clip(c, mi3, ma3);
136 }
137
138 return av_clip(c, mi1, ma1);
139 }
140
mode06(int c,int a1,int a2,int a3,int a4,int a5,int a6,int a7,int a8)141 static int mode06(int c, int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8)
142 {
143 REMOVE_GRAIN_SORT_AXIS
144
145 const int d1 = ma1 - mi1;
146 const int d2 = ma2 - mi2;
147 const int d3 = ma3 - mi3;
148 const int d4 = ma4 - mi4;
149
150 const int cli1 = av_clip(c, mi1, ma1);
151 const int cli2 = av_clip(c, mi2, ma2);
152 const int cli3 = av_clip(c, mi3, ma3);
153 const int cli4 = av_clip(c, mi4, ma4);
154
155 const int c1 = av_clip_uint16((FFABS(c - cli1) << 1) + d1);
156 const int c2 = av_clip_uint16((FFABS(c - cli2) << 1) + d2);
157 const int c3 = av_clip_uint16((FFABS(c - cli3) << 1) + d3);
158 const int c4 = av_clip_uint16((FFABS(c - cli4) << 1) + d4);
159
160 const int mindiff = FFMIN(FFMIN(c1, c2), FFMIN(c3, c4));
161
162 if (mindiff == c4) {
163 return cli4;
164 } else if (mindiff == c2) {
165 return cli2;
166 } else if (mindiff == c3) {
167 return cli3;
168 }
169
170 return cli1;
171 }
172
mode07(int c,int a1,int a2,int a3,int a4,int a5,int a6,int a7,int a8)173 static int mode07(int c, int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8)
174 {
175 REMOVE_GRAIN_SORT_AXIS
176
177 const int d1 = ma1 - mi1;
178 const int d2 = ma2 - mi2;
179 const int d3 = ma3 - mi3;
180 const int d4 = ma4 - mi4;
181
182 const int cli1 = av_clip(c, mi1, ma1);
183 const int cli2 = av_clip(c, mi2, ma2);
184 const int cli3 = av_clip(c, mi3, ma3);
185 const int cli4 = av_clip(c, mi4, ma4);
186
187 const int c1 = FFABS(c - cli1) + d1;
188 const int c2 = FFABS(c - cli2) + d2;
189 const int c3 = FFABS(c - cli3) + d3;
190 const int c4 = FFABS(c - cli4) + d4;
191
192 const int mindiff = FFMIN(FFMIN(c1, c2), FFMIN(c3, c4));
193
194 if (mindiff == c4) {
195 return cli4;
196 } else if (mindiff == c2) {
197 return cli2;
198 } else if (mindiff == c3) {
199 return cli3;
200 }
201
202 return cli1;
203 }
204
mode08(int c,int a1,int a2,int a3,int a4,int a5,int a6,int a7,int a8)205 static int mode08(int c, int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8)
206 {
207 REMOVE_GRAIN_SORT_AXIS
208
209 const int d1 = ma1 - mi1;
210 const int d2 = ma2 - mi2;
211 const int d3 = ma3 - mi3;
212 const int d4 = ma4 - mi4;
213
214 const int cli1 = av_clip(c, mi1, ma1);
215 const int cli2 = av_clip(c, mi2, ma2);
216 const int cli3 = av_clip(c, mi3, ma3);
217 const int cli4 = av_clip(c, mi4, ma4);
218
219 const int c1 = av_clip_uint16(FFABS(c - cli1) + (d1 << 1));
220 const int c2 = av_clip_uint16(FFABS(c - cli2) + (d2 << 1));
221 const int c3 = av_clip_uint16(FFABS(c - cli3) + (d3 << 1));
222 const int c4 = av_clip_uint16(FFABS(c - cli4) + (d4 << 1));
223
224 const int mindiff = FFMIN(FFMIN(c1, c2), FFMIN(c3, c4));
225
226 if (mindiff == c4) {
227 return cli4;
228 } else if (mindiff == c2) {
229 return cli2;
230 } else if (mindiff == c3) {
231 return cli3;
232 }
233
234 return cli1;
235 }
236
mode09(int c,int a1,int a2,int a3,int a4,int a5,int a6,int a7,int a8)237 static int mode09(int c, int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8)
238 {
239 REMOVE_GRAIN_SORT_AXIS
240
241 const int d1 = ma1 - mi1;
242 const int d2 = ma2 - mi2;
243 const int d3 = ma3 - mi3;
244 const int d4 = ma4 - mi4;
245
246 const int mindiff = FFMIN(FFMIN(d1, d2), FFMIN(d3, d4));
247
248 if (mindiff == d4) {
249 return av_clip(c, mi4, ma4);
250 } else if (mindiff == d2) {
251 return av_clip(c, mi2, ma2);
252 } else if (mindiff == d3) {
253 return av_clip(c, mi3, ma3);
254 }
255
256 return av_clip(c, mi1, ma1);
257 }
258
mode10(int c,int a1,int a2,int a3,int a4,int a5,int a6,int a7,int a8)259 static int mode10(int c, int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8)
260 {
261 const int d1 = FFABS(c - a1);
262 const int d2 = FFABS(c - a2);
263 const int d3 = FFABS(c - a3);
264 const int d4 = FFABS(c - a4);
265 const int d5 = FFABS(c - a5);
266 const int d6 = FFABS(c - a6);
267 const int d7 = FFABS(c - a7);
268 const int d8 = FFABS(c - a8);
269
270 const int mindiff = FFMIN(FFMIN(FFMIN(d1, d2), FFMIN(d3, d4)),
271 FFMIN(FFMIN(d5, d6), FFMIN(d7, d8)));
272
273 if (mindiff == d7) return a7;
274 if (mindiff == d8) return a8;
275 if (mindiff == d6) return a6;
276 if (mindiff == d2) return a2;
277 if (mindiff == d3) return a3;
278 if (mindiff == d1) return a1;
279 if (mindiff == d5) return a5;
280
281 return a4;
282 }
283
mode1112(int c,int a1,int a2,int a3,int a4,int a5,int a6,int a7,int a8)284 static int mode1112(int c, int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8)
285 {
286 const int sum = 4 * c + 2 * (a2 + a4 + a5 + a7) + a1 + a3 + a6 + a8;
287 const int val = (sum + 8) >> 4;
288
289 return val;
290 }
291
mode1314(int c,int a1,int a2,int a3,int a4,int a5,int a6,int a7,int a8)292 static int mode1314(int c, int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8)
293 {
294 const int d1 = FFABS(a1 - a8);
295 const int d2 = FFABS(a2 - a7);
296 const int d3 = FFABS(a3 - a6);
297
298 const int mindiff = FFMIN(FFMIN(d1, d2), d3);
299
300 if (mindiff == d2) {
301 return (a2 + a7 + 1) >> 1;
302 }
303 if (mindiff == d3) {
304 return (a3 + a6 + 1) >> 1;
305 }
306
307 return (a1 + a8 + 1) >> 1;
308 }
309
mode1516(int c,int a1,int a2,int a3,int a4,int a5,int a6,int a7,int a8)310 static int mode1516(int c, int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8)
311 {
312 const int d1 = FFABS(a1 - a8);
313 const int d2 = FFABS(a2 - a7);
314 const int d3 = FFABS(a3 - a6);
315
316 const int mindiff = FFMIN(FFMIN(d1, d2), d3);
317 const int average = (2 * (a2 + a7) + a1 + a3 + a6 + a8 + 4) >> 3;
318
319 if (mindiff == d2) {
320 return av_clip(average, FFMIN(a2, a7), FFMAX(a2, a7));
321 }
322 if (mindiff == d3) {
323 return av_clip(average, FFMIN(a3, a6), FFMAX(a3, a6));
324 }
325
326 return av_clip(average, FFMIN(a1, a8), FFMAX(a1, a8));
327 }
328
mode17(int c,int a1,int a2,int a3,int a4,int a5,int a6,int a7,int a8)329 static int mode17(int c, int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8)
330 {
331 REMOVE_GRAIN_SORT_AXIS
332
333 const int l = FFMAX(FFMAX(mi1, mi2), FFMAX(mi3, mi4));
334 const int u = FFMIN(FFMIN(ma1, ma2), FFMIN(ma3, ma4));
335
336 return av_clip(c, FFMIN(l, u), FFMAX(l, u));
337 }
338
mode18(int c,int a1,int a2,int a3,int a4,int a5,int a6,int a7,int a8)339 static int mode18(int c, int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8)
340 {
341 const int d1 = FFMAX(FFABS(c - a1), FFABS(c - a8));
342 const int d2 = FFMAX(FFABS(c - a2), FFABS(c - a7));
343 const int d3 = FFMAX(FFABS(c - a3), FFABS(c - a6));
344 const int d4 = FFMAX(FFABS(c - a4), FFABS(c - a5));
345
346 const int mindiff = FFMIN(FFMIN(d1, d2), FFMIN(d3, d4));
347
348 if (mindiff == d4) {
349 return av_clip(c, FFMIN(a4, a5), FFMAX(a4, a5));
350 }
351 if (mindiff == d2) {
352 return av_clip(c, FFMIN(a2, a7), FFMAX(a2, a7));
353 }
354 if (mindiff == d3) {
355 return av_clip(c, FFMIN(a3, a6), FFMAX(a3, a6));
356 }
357
358 return av_clip(c, FFMIN(a1, a8), FFMAX(a1, a8));
359 }
360
mode19(int c,int a1,int a2,int a3,int a4,int a5,int a6,int a7,int a8)361 static int mode19(int c, int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8)
362 {
363 const int sum = a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8;
364 const int val = (sum + 4) >> 3;
365
366 return val;
367 }
368
mode20(int c,int a1,int a2,int a3,int a4,int a5,int a6,int a7,int a8)369 static int mode20(int c, int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8)
370 {
371 const int sum = a1 + a2 + a3 + a4 + c + a5 + a6 + a7 + a8;
372 const int val = (sum + 4) / 9;
373
374 return val;
375 }
376
mode21(int c,int a1,int a2,int a3,int a4,int a5,int a6,int a7,int a8)377 static int mode21(int c, int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8)
378 {
379 const int l1l = (a1 + a8) >> 1;
380 const int l2l = (a2 + a7) >> 1;
381 const int l3l = (a3 + a6) >> 1;
382 const int l4l = (a4 + a5) >> 1;
383
384 const int l1h = (a1 + a8 + 1) >> 1;
385 const int l2h = (a2 + a7 + 1) >> 1;
386 const int l3h = (a3 + a6 + 1) >> 1;
387 const int l4h = (a4 + a5 + 1) >> 1;
388
389 const int mi = FFMIN(FFMIN(l1l, l2l), FFMIN(l3l, l4l));
390 const int ma = FFMAX(FFMAX(l1h, l2h), FFMAX(l3h, l4h));
391
392 return av_clip(c, mi, ma);
393 }
394
mode22(int c,int a1,int a2,int a3,int a4,int a5,int a6,int a7,int a8)395 static int mode22(int c, int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8)
396 {
397 const int l1 = (a1 + a8 + 1) >> 1;
398 const int l2 = (a2 + a7 + 1) >> 1;
399 const int l3 = (a3 + a6 + 1) >> 1;
400 const int l4 = (a4 + a5 + 1) >> 1;
401
402 const int mi = FFMIN(FFMIN(l1, l2), FFMIN(l3, l4));
403 const int ma = FFMAX(FFMAX(l1, l2), FFMAX(l3, l4));
404
405 return av_clip(c, mi, ma);
406 }
407
mode23(int c,int a1,int a2,int a3,int a4,int a5,int a6,int a7,int a8)408 static int mode23(int c, int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8)
409 {
410 REMOVE_GRAIN_SORT_AXIS
411
412 const int linediff1 = ma1 - mi1;
413 const int linediff2 = ma2 - mi2;
414 const int linediff3 = ma3 - mi3;
415 const int linediff4 = ma4 - mi4;
416
417 const int u1 = FFMIN(c - ma1, linediff1);
418 const int u2 = FFMIN(c - ma2, linediff2);
419 const int u3 = FFMIN(c - ma3, linediff3);
420 const int u4 = FFMIN(c - ma4, linediff4);
421 const int u = FFMAX(FFMAX(FFMAX(u1, u2), FFMAX(u3, u4)), 0);
422
423 const int d1 = FFMIN(mi1 - c, linediff1);
424 const int d2 = FFMIN(mi2 - c, linediff2);
425 const int d3 = FFMIN(mi3 - c, linediff3);
426 const int d4 = FFMIN(mi4 - c, linediff4);
427 const int d = FFMAX(FFMAX(FFMAX(d1, d2), FFMAX(d3, d4)), 0);
428
429 return c - u + d; // This probably will never overflow.
430 }
431
mode24(int c,int a1,int a2,int a3,int a4,int a5,int a6,int a7,int a8)432 static int mode24(int c, int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8)
433 {
434 REMOVE_GRAIN_SORT_AXIS
435
436 const int linediff1 = ma1 - mi1;
437 const int linediff2 = ma2 - mi2;
438 const int linediff3 = ma3 - mi3;
439 const int linediff4 = ma4 - mi4;
440
441 const int tu1 = c - ma1;
442 const int tu2 = c - ma2;
443 const int tu3 = c - ma3;
444 const int tu4 = c - ma4;
445
446 const int u1 = FFMIN(tu1, linediff1 - tu1);
447 const int u2 = FFMIN(tu2, linediff2 - tu2);
448 const int u3 = FFMIN(tu3, linediff3 - tu3);
449 const int u4 = FFMIN(tu4, linediff4 - tu4);
450 const int u = FFMAX(FFMAX(FFMAX(u1, u2), FFMAX(u3, u4)), 0);
451
452 const int td1 = mi1 - c;
453 const int td2 = mi2 - c;
454 const int td3 = mi3 - c;
455 const int td4 = mi4 - c;
456
457 const int d1 = FFMIN(td1, linediff1 - td1);
458 const int d2 = FFMIN(td2, linediff2 - td2);
459 const int d3 = FFMIN(td3, linediff3 - td3);
460 const int d4 = FFMIN(td4, linediff4 - td4);
461 const int d = FFMAX(FFMAX(FFMAX(d1, d2), FFMAX(d3, d4)), 0);
462
463 return c - u + d; // This probably will never overflow.
464 }
465
config_input(AVFilterLink * inlink)466 static int config_input(AVFilterLink *inlink)
467 {
468 RemoveGrainContext *s = inlink->dst->priv;
469 const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
470 int i;
471
472 s->nb_planes = av_pix_fmt_count_planes(inlink->format);
473
474 s->planeheight[1] = s->planeheight[2] = AV_CEIL_RSHIFT(inlink->h, desc->log2_chroma_h);
475 s->planeheight[0] = s->planeheight[3] = inlink->h;
476 s->planewidth[1] = s->planewidth[2] = AV_CEIL_RSHIFT(inlink->w, desc->log2_chroma_w);
477 s->planewidth[0] = s->planewidth[3] = inlink->w;
478
479 for (i = 0; i < s->nb_planes; i++) {
480 switch (s->mode[i]) {
481 case 1: s->rg[i] = mode01; break;
482 case 2: s->rg[i] = mode02; break;
483 case 3: s->rg[i] = mode03; break;
484 case 4: s->rg[i] = mode04; break;
485 case 5: s->rg[i] = mode05; break;
486 case 6: s->rg[i] = mode06; break;
487 case 7: s->rg[i] = mode07; break;
488 case 8: s->rg[i] = mode08; break;
489 case 9: s->rg[i] = mode09; break;
490 case 10: s->rg[i] = mode10; break;
491 case 11: s->rg[i] = mode1112; break;
492 case 12: s->rg[i] = mode1112; break;
493 case 13: s->skip_odd = 1;
494 s->rg[i] = mode1314; break;
495 case 14: s->skip_even = 1;
496 s->rg[i] = mode1314; break;
497 case 15: s->skip_odd = 1;
498 s->rg[i] = mode1516; break;
499 case 16: s->skip_even = 1;
500 s->rg[i] = mode1516; break;
501 case 17: s->rg[i] = mode17; break;
502 case 18: s->rg[i] = mode18; break;
503 case 19: s->rg[i] = mode19; break;
504 case 20: s->rg[i] = mode20; break;
505 case 21: s->rg[i] = mode21; break;
506 case 22: s->rg[i] = mode22; break;
507 case 23: s->rg[i] = mode23; break;
508 case 24: s->rg[i] = mode24; break;
509 }
510 }
511
512 if (ARCH_X86)
513 ff_removegrain_init_x86(s);
514
515 return 0;
516 }
517
518 typedef struct ThreadData {
519 AVFrame *in, *out;
520 int plane;
521 } ThreadData;
522
filter_slice(AVFilterContext * ctx,void * arg,int jobnr,int nb_jobs)523 static int filter_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
524 {
525 RemoveGrainContext *s = ctx->priv;
526 ThreadData *td = arg;
527 AVFrame *in = td->in;
528 AVFrame *out = td->out;
529 const int i = td->plane;
530 const int height = s->planeheight[i];
531 const int om = in->linesize[i] - 1;
532 const int o0 = in->linesize[i] ;
533 const int op = in->linesize[i] + 1;
534 int start = (height * jobnr ) / nb_jobs;
535 int end = (height * (jobnr+1)) / nb_jobs;
536 int x, y;
537
538 start = FFMAX(1, start);
539 end = FFMIN(height-1, end);
540 for (y = start; y < end; y++) {
541 uint8_t *dst = out->data[i];
542 uint8_t *src = in->data[i];
543
544 src = in->data[i] + y * in->linesize[i];
545 dst = out->data[i] + y * out->linesize[i];
546
547 if (s->skip_even && !(y & 1)) {
548 memcpy(dst, src, s->planewidth[i]);
549 continue;
550 }
551 if (s->skip_odd && y & 1) {
552 memcpy(dst, src, s->planewidth[i]);
553 continue;
554 }
555
556 *dst++ = *src++;
557
558 if (s->fl[i]) {
559 int w_asm = (s->planewidth[i] - 2) & ~15;
560
561 s->fl[i](dst, src, in->linesize[i], w_asm);
562
563 x = 1 + w_asm;
564 dst += w_asm;
565 src += w_asm;
566 } else
567 x = 1;
568
569 for (; x < s->planewidth[i] - 1; x++) {
570 const int a1 = src[-op];
571 const int a2 = src[-o0];
572 const int a3 = src[-om];
573 const int a4 = src[-1 ];
574 const int c = src[ 0 ];
575 const int a5 = src[ 1 ];
576 const int a6 = src[ om];
577 const int a7 = src[ o0];
578 const int a8 = src[ op];
579
580 const int res = s->rg[i](c, a1, a2, a3, a4, a5, a6, a7, a8);
581
582 *dst = res;
583 dst++, src++;
584 }
585 dst[0] = src[0];
586 }
587
588 return 0;
589 }
590
filter_frame(AVFilterLink * inlink,AVFrame * in)591 static int filter_frame(AVFilterLink *inlink, AVFrame *in)
592 {
593 AVFilterContext *ctx = inlink->dst;
594 AVFilterLink *outlink = ctx->outputs[0];
595 RemoveGrainContext *s = ctx->priv;
596 ThreadData td;
597 AVFrame *out;
598 int i;
599
600 out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
601 if (!out) {
602 av_frame_free(&in);
603 return AVERROR(ENOMEM);
604 }
605 av_frame_copy_props(out, in);
606
607 for (i = 0; i < s->nb_planes; i++) {
608 uint8_t *dst = out->data[i];
609 uint8_t *src = in->data[i];
610
611 if (s->mode[i] == 0) {
612 av_image_copy_plane(dst, out->linesize[i],
613 src, in->linesize[i],
614 s->planewidth[i], s->planeheight[i]);
615 continue;
616 }
617
618 memcpy(dst, src, s->planewidth[i]);
619
620 td.in = in; td.out = out; td.plane = i;
621 ctx->internal->execute(ctx, filter_slice, &td, NULL,
622 FFMIN(s->planeheight[i], ff_filter_get_nb_threads(ctx)));
623
624 src = in->data[i] + (s->planeheight[i] - 1) * in->linesize[i];
625 dst = out->data[i] + (s->planeheight[i] - 1) * out->linesize[i];
626 memcpy(dst, src, s->planewidth[i]);
627 }
628
629 av_frame_free(&in);
630 return ff_filter_frame(outlink, out);
631 }
632
633 static const AVFilterPad removegrain_inputs[] = {
634 {
635 .name = "default",
636 .type = AVMEDIA_TYPE_VIDEO,
637 .filter_frame = filter_frame,
638 .config_props = config_input,
639 },
640 { NULL }
641 };
642
643 static const AVFilterPad removegrain_outputs[] = {
644 {
645 .name = "default",
646 .type = AVMEDIA_TYPE_VIDEO,
647 },
648 { NULL }
649 };
650
651 AVFilter ff_vf_removegrain = {
652 .name = "removegrain",
653 .description = NULL_IF_CONFIG_SMALL("Remove grain."),
654 .priv_size = sizeof(RemoveGrainContext),
655 .query_formats = query_formats,
656 .inputs = removegrain_inputs,
657 .outputs = removegrain_outputs,
658 .priv_class = &removegrain_class,
659 .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,
660 };
661