]> git.sesse.net Git - ffmpeg/blob - libavfilter/vf_colorcontrast.c
avutil/mem: Also poison new av_realloc-allocated blocks
[ffmpeg] / libavfilter / vf_colorcontrast.c
1 /*
2  * Copyright (c) 2021 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 <float.h>
22
23 #include "libavutil/opt.h"
24 #include "libavutil/imgutils.h"
25 #include "avfilter.h"
26 #include "drawutils.h"
27 #include "formats.h"
28 #include "internal.h"
29 #include "video.h"
30
31 #define R 0
32 #define G 1
33 #define B 2
34
35 typedef struct ColorContrastContext {
36     const AVClass *class;
37
38     float rc, gm, by;
39     float rcw, gmw, byw;
40     float preserve;
41
42     int step;
43     int depth;
44     uint8_t rgba_map[4];
45
46     int (*do_slice)(AVFilterContext *s, void *arg,
47                     int jobnr, int nb_jobs);
48 } ColorContrastContext;
49
50 static inline float lerpf(float v0, float v1, float f)
51 {
52     return v0 + (v1 - v0) * f;
53 }
54
55 #define PROCESS(max)                                                    \
56     br = (b + r) * 0.5f;                                                \
57     gb = (g + b) * 0.5f;                                                \
58     rg = (r + g) * 0.5f;                                                \
59                                                                         \
60     gd = g - br;                                                        \
61     bd = b - rg;                                                        \
62     rd = r - gb;                                                        \
63                                                                         \
64     g0 = g + gd * gm;                                                   \
65     b0 = b - gd * gm;                                                   \
66     r0 = r - gd * gm;                                                   \
67                                                                         \
68     g1 = g - bd * by;                                                   \
69     b1 = b + bd * by;                                                   \
70     r1 = r - bd * by;                                                   \
71                                                                         \
72     g2 = g - rd * rc;                                                   \
73     b2 = b - rd * rc;                                                   \
74     r2 = r + rd * rc;                                                   \
75                                                                         \
76     ng = av_clipf((g0 * gmw + g1 * byw + g2 * rcw) * scale, 0.f, max);  \
77     nb = av_clipf((b0 * gmw + b1 * byw + b2 * rcw) * scale, 0.f, max);  \
78     nr = av_clipf((r0 * gmw + r1 * byw + r2 * rcw) * scale, 0.f, max);  \
79                                                                         \
80     li = FFMAX3(r, g, b) + FFMIN3(r, g, b);                             \
81     lo = FFMAX3(nr, ng, nb) + FFMIN3(nr, ng, nb) + FLT_EPSILON;         \
82     lf = li / lo;                                                       \
83                                                                         \
84     r = nr * lf;                                                        \
85     g = ng * lf;                                                        \
86     b = nb * lf;                                                        \
87                                                                         \
88     nr = lerpf(nr, r, preserve);                                        \
89     ng = lerpf(ng, g, preserve);                                        \
90     nb = lerpf(nb, b, preserve);
91
92 static int colorcontrast_slice8(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
93 {
94     ColorContrastContext *s = ctx->priv;
95     AVFrame *frame = arg;
96     const int width = frame->width;
97     const int height = frame->height;
98     const int slice_start = (height * jobnr) / nb_jobs;
99     const int slice_end = (height * (jobnr + 1)) / nb_jobs;
100     const int glinesize = frame->linesize[0];
101     const int blinesize = frame->linesize[1];
102     const int rlinesize = frame->linesize[2];
103     uint8_t *gptr = frame->data[0] + slice_start * glinesize;
104     uint8_t *bptr = frame->data[1] + slice_start * blinesize;
105     uint8_t *rptr = frame->data[2] + slice_start * rlinesize;
106     const float preserve = s->preserve;
107     const float gm = s->gm * 0.5f;
108     const float by = s->by * 0.5f;
109     const float rc = s->rc * 0.5f;
110     const float gmw = s->gmw;
111     const float byw = s->byw;
112     const float rcw = s->rcw;
113     const float sum = gmw + byw + rcw;
114     const float scale = 1.f / sum;
115
116     for (int y = slice_start; y < slice_end && sum > FLT_EPSILON; y++) {
117         for (int x = 0; x < width; x++) {
118             float g = gptr[x];
119             float b = bptr[x];
120             float r = rptr[x];
121             float g0, g1, g2;
122             float b0, b1, b2;
123             float r0, r1, r2;
124             float gd, bd, rd;
125             float gb, br, rg;
126             float nr, ng, nb;
127             float li, lo, lf;
128
129             PROCESS(255.f);
130
131             gptr[x] = av_clip_uint8(ng);
132             bptr[x] = av_clip_uint8(nb);
133             rptr[x] = av_clip_uint8(nr);
134         }
135
136         gptr += glinesize;
137         bptr += blinesize;
138         rptr += rlinesize;
139     }
140
141     return 0;
142 }
143
144 static int colorcontrast_slice16(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
145 {
146     ColorContrastContext *s = ctx->priv;
147     AVFrame *frame = arg;
148     const int depth = s->depth;
149     const float max = (1 << depth) - 1;
150     const int width = frame->width;
151     const int height = frame->height;
152     const int slice_start = (height * jobnr) / nb_jobs;
153     const int slice_end = (height * (jobnr + 1)) / nb_jobs;
154     const int glinesize = frame->linesize[0] / 2;
155     const int blinesize = frame->linesize[1] / 2;
156     const int rlinesize = frame->linesize[2] / 2;
157     uint16_t *gptr = (uint16_t *)frame->data[0] + slice_start * glinesize;
158     uint16_t *bptr = (uint16_t *)frame->data[1] + slice_start * blinesize;
159     uint16_t *rptr = (uint16_t *)frame->data[2] + slice_start * rlinesize;
160     const float preserve = s->preserve;
161     const float gm = s->gm * 0.5f;
162     const float by = s->by * 0.5f;
163     const float rc = s->rc * 0.5f;
164     const float gmw = s->gmw;
165     const float byw = s->byw;
166     const float rcw = s->rcw;
167     const float sum = gmw + byw + rcw;
168     const float scale = 1.f / sum;
169
170     for (int y = slice_start; y < slice_end && sum > FLT_EPSILON; y++) {
171         for (int x = 0; x < width; x++) {
172             float g = gptr[x];
173             float b = bptr[x];
174             float r = rptr[x];
175             float g0, g1, g2;
176             float b0, b1, b2;
177             float r0, r1, r2;
178             float gd, bd, rd;
179             float gb, br, rg;
180             float nr, ng, nb;
181             float li, lo, lf;
182
183             PROCESS(max);
184
185             gptr[x] = av_clip_uintp2_c(ng, depth);
186             bptr[x] = av_clip_uintp2_c(nb, depth);
187             rptr[x] = av_clip_uintp2_c(nr, depth);
188         }
189
190         gptr += glinesize;
191         bptr += blinesize;
192         rptr += rlinesize;
193     }
194
195     return 0;
196 }
197
198 static int colorcontrast_slice8p(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
199 {
200     ColorContrastContext *s = ctx->priv;
201     AVFrame *frame = arg;
202     const int step = s->step;
203     const int width = frame->width;
204     const int height = frame->height;
205     const int slice_start = (height * jobnr) / nb_jobs;
206     const int slice_end = (height * (jobnr + 1)) / nb_jobs;
207     const int linesize = frame->linesize[0];
208     const uint8_t roffset = s->rgba_map[R];
209     const uint8_t goffset = s->rgba_map[G];
210     const uint8_t boffset = s->rgba_map[B];
211     uint8_t *ptr = frame->data[0] + slice_start * linesize;
212     const float preserve = s->preserve;
213     const float gm = s->gm * 0.5f;
214     const float by = s->by * 0.5f;
215     const float rc = s->rc * 0.5f;
216     const float gmw = s->gmw;
217     const float byw = s->byw;
218     const float rcw = s->rcw;
219     const float sum = gmw + byw + rcw;
220     const float scale = 1.f / sum;
221
222     for (int y = slice_start; y < slice_end && sum > FLT_EPSILON; y++) {
223         for (int x = 0; x < width; x++) {
224             float g = ptr[x * step + goffset];
225             float b = ptr[x * step + boffset];
226             float r = ptr[x * step + roffset];
227             float g0, g1, g2;
228             float b0, b1, b2;
229             float r0, r1, r2;
230             float gd, bd, rd;
231             float gb, br, rg;
232             float nr, ng, nb;
233             float li, lo, lf;
234
235             PROCESS(255.f);
236
237             ptr[x * step + goffset] = av_clip_uint8(ng);
238             ptr[x * step + boffset] = av_clip_uint8(nb);
239             ptr[x * step + roffset] = av_clip_uint8(nr);
240         }
241
242         ptr += linesize;
243     }
244
245     return 0;
246 }
247
248 static int colorcontrast_slice16p(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
249 {
250     ColorContrastContext *s = ctx->priv;
251     AVFrame *frame = arg;
252     const int step = s->step;
253     const int depth = s->depth;
254     const float max = (1 << depth) - 1;
255     const int width = frame->width;
256     const int height = frame->height;
257     const int slice_start = (height * jobnr) / nb_jobs;
258     const int slice_end = (height * (jobnr + 1)) / nb_jobs;
259     const int linesize = frame->linesize[0] / 2;
260     const uint8_t roffset = s->rgba_map[R];
261     const uint8_t goffset = s->rgba_map[G];
262     const uint8_t boffset = s->rgba_map[B];
263     uint16_t *ptr = (uint16_t *)frame->data[0] + slice_start * linesize;
264     const float preserve = s->preserve;
265     const float gm = s->gm * 0.5f;
266     const float by = s->by * 0.5f;
267     const float rc = s->rc * 0.5f;
268     const float gmw = s->gmw;
269     const float byw = s->byw;
270     const float rcw = s->rcw;
271     const float sum = gmw + byw + rcw;
272     const float scale = 1.f / sum;
273
274     for (int y = slice_start; y < slice_end && sum > FLT_EPSILON; y++) {
275         for (int x = 0; x < width; x++) {
276             float g = ptr[x * step + goffset];
277             float b = ptr[x * step + boffset];
278             float r = ptr[x * step + roffset];
279             float g0, g1, g2;
280             float b0, b1, b2;
281             float r0, r1, r2;
282             float gd, bd, rd;
283             float gb, br, rg;
284             float nr, ng, nb;
285             float li, lo, lf;
286
287             PROCESS(max);
288
289             ptr[x * step + goffset] = av_clip_uintp2_c(ng, depth);
290             ptr[x * step + boffset] = av_clip_uintp2_c(nb, depth);
291             ptr[x * step + roffset] = av_clip_uintp2_c(nr, depth);
292         }
293
294         ptr += linesize;
295     }
296
297     return 0;
298 }
299
300 static int filter_frame(AVFilterLink *link, AVFrame *frame)
301 {
302     AVFilterContext *ctx = link->dst;
303     ColorContrastContext *s = ctx->priv;
304     int res;
305
306     if (res = ctx->internal->execute(ctx, s->do_slice, frame, NULL,
307                                        FFMIN(frame->height, ff_filter_get_nb_threads(ctx))))
308         return res;
309
310     return ff_filter_frame(ctx->outputs[0], frame);
311 }
312
313 static av_cold int query_formats(AVFilterContext *ctx)
314 {
315     static const enum AVPixelFormat pixel_fmts[] = {
316         AV_PIX_FMT_RGB24, AV_PIX_FMT_BGR24,
317         AV_PIX_FMT_RGBA, AV_PIX_FMT_BGRA,
318         AV_PIX_FMT_ARGB, AV_PIX_FMT_ABGR,
319         AV_PIX_FMT_0RGB, AV_PIX_FMT_0BGR,
320         AV_PIX_FMT_RGB0, AV_PIX_FMT_BGR0,
321         AV_PIX_FMT_GBRP, AV_PIX_FMT_GBRAP,
322         AV_PIX_FMT_GBRP9, AV_PIX_FMT_GBRP10, AV_PIX_FMT_GBRP12,
323         AV_PIX_FMT_GBRP14, AV_PIX_FMT_GBRP16,
324         AV_PIX_FMT_GBRAP10, AV_PIX_FMT_GBRAP12, AV_PIX_FMT_GBRAP16,
325         AV_PIX_FMT_RGB48,  AV_PIX_FMT_BGR48,
326         AV_PIX_FMT_RGBA64, AV_PIX_FMT_BGRA64,
327         AV_PIX_FMT_NONE
328     };
329
330     AVFilterFormats *formats = NULL;
331
332     formats = ff_make_format_list(pixel_fmts);
333     if (!formats)
334         return AVERROR(ENOMEM);
335
336     return ff_set_common_formats(ctx, formats);
337 }
338
339 static av_cold int config_input(AVFilterLink *inlink)
340 {
341     AVFilterContext *ctx = inlink->dst;
342     ColorContrastContext *s = ctx->priv;
343     const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
344     int planar = desc->flags & AV_PIX_FMT_FLAG_PLANAR;
345
346     s->step = desc->nb_components;
347     if (inlink->format == AV_PIX_FMT_RGB0 ||
348         inlink->format == AV_PIX_FMT_0RGB ||
349         inlink->format == AV_PIX_FMT_BGR0 ||
350         inlink->format == AV_PIX_FMT_0BGR)
351         s->step = 4;
352
353     s->depth = desc->comp[0].depth;
354     s->do_slice = s->depth <= 8 ? colorcontrast_slice8 : colorcontrast_slice16;
355     if (!planar)
356         s->do_slice = s->depth <= 8 ? colorcontrast_slice8p : colorcontrast_slice16p;
357
358     ff_fill_rgba_map(s->rgba_map, inlink->format);
359
360     return 0;
361 }
362
363 static const AVFilterPad colorcontrast_inputs[] = {
364     {
365         .name           = "default",
366         .type           = AVMEDIA_TYPE_VIDEO,
367         .needs_writable = 1,
368         .filter_frame   = filter_frame,
369         .config_props   = config_input,
370     },
371     { NULL }
372 };
373
374 static const AVFilterPad colorcontrast_outputs[] = {
375     {
376         .name = "default",
377         .type = AVMEDIA_TYPE_VIDEO,
378     },
379     { NULL }
380 };
381
382 #define OFFSET(x) offsetof(ColorContrastContext, x)
383 #define VF AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_RUNTIME_PARAM
384
385 static const AVOption colorcontrast_options[] = {
386     { "rc",  "set the red-cyan contrast",      OFFSET(rc),  AV_OPT_TYPE_FLOAT, {.dbl=0}, -1, 1, VF },
387     { "gm",  "set the green-magenta contrast", OFFSET(gm),  AV_OPT_TYPE_FLOAT, {.dbl=0}, -1, 1, VF },
388     { "by",  "set the blue-yellow contrast",   OFFSET(by),  AV_OPT_TYPE_FLOAT, {.dbl=0}, -1, 1, VF },
389     { "rcw", "set the red-cyan weight",        OFFSET(rcw), AV_OPT_TYPE_FLOAT, {.dbl=0},  0, 1, VF },
390     { "gmw", "set the green-magenta weight",   OFFSET(gmw), AV_OPT_TYPE_FLOAT, {.dbl=0},  0, 1, VF },
391     { "byw", "set the blue-yellow weight",     OFFSET(byw), AV_OPT_TYPE_FLOAT, {.dbl=0},  0, 1, VF },
392     { "pl",  "set the amount of preserving lightness", OFFSET(preserve), AV_OPT_TYPE_FLOAT, {.dbl=0}, 0, 1, VF },
393     { NULL }
394 };
395
396 AVFILTER_DEFINE_CLASS(colorcontrast);
397
398 const AVFilter ff_vf_colorcontrast = {
399     .name          = "colorcontrast",
400     .description   = NULL_IF_CONFIG_SMALL("Adjust color contrast between RGB components."),
401     .priv_size     = sizeof(ColorContrastContext),
402     .priv_class    = &colorcontrast_class,
403     .query_formats = query_formats,
404     .inputs        = colorcontrast_inputs,
405     .outputs       = colorcontrast_outputs,
406     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,
407     .process_command = ff_filter_process_command,
408 };