]> git.sesse.net Git - ffmpeg/blob - libavfilter/vf_colorcorrect.c
avfilter: Constify all AVFilters
[ffmpeg] / libavfilter / vf_colorcorrect.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 "formats.h"
27 #include "internal.h"
28 #include "video.h"
29
30 typedef struct ColorCorrectContext {
31     const AVClass *class;
32
33     float rl, bl;
34     float rh, bh;
35     float saturation;
36
37     int depth;
38
39     int (*do_slice)(AVFilterContext *s, void *arg,
40                     int jobnr, int nb_jobs);
41 } ColorCorrectContext;
42
43 #define PROCESS()                            \
44     float y = yptr[x] * imax;                \
45     float u = uptr[x] * imax - .5f;          \
46     float v = vptr[x] * imax - .5f;          \
47     float ny, nu, nv;                        \
48                                              \
49     ny = y;                                  \
50     nu = saturation * (u + y * bd + bl);     \
51     nv = saturation * (v + y * rd + rl);
52
53 static int colorcorrect_slice8(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
54 {
55     ColorCorrectContext *s = ctx->priv;
56     AVFrame *frame = arg;
57     const int depth = s->depth;
58     const float max = (1 << depth) - 1;
59     const float imax = 1.f / max;
60     const int width = frame->width;
61     const int height = frame->height;
62     const int slice_start = (height * jobnr) / nb_jobs;
63     const int slice_end = (height * (jobnr + 1)) / nb_jobs;
64     const int ylinesize = frame->linesize[0];
65     const int ulinesize = frame->linesize[1];
66     const int vlinesize = frame->linesize[2];
67     uint8_t *yptr = frame->data[0] + slice_start * ylinesize;
68     uint8_t *uptr = frame->data[1] + slice_start * ulinesize;
69     uint8_t *vptr = frame->data[2] + slice_start * vlinesize;
70     const float saturation = s->saturation;
71     const float bl = s->bl;
72     const float rl = s->rl;
73     const float bd = s->bh - bl;
74     const float rd = s->rh - rl;
75
76     for (int y = slice_start; y < slice_end; y++) {
77         for (int x = 0; x < width; x++) {
78             PROCESS()
79
80             yptr[x] = av_clip_uint8( ny         * max);
81             uptr[x] = av_clip_uint8((nu + 0.5f) * max);
82             vptr[x] = av_clip_uint8((nv + 0.5f) * max);
83         }
84
85         yptr += ylinesize;
86         uptr += ulinesize;
87         vptr += vlinesize;
88     }
89
90     return 0;
91 }
92
93 static int colorcorrect_slice16(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
94 {
95     ColorCorrectContext *s = ctx->priv;
96     AVFrame *frame = arg;
97     const int depth = s->depth;
98     const float max = (1 << depth) - 1;
99     const float imax = 1.f / max;
100     const int width = frame->width;
101     const int height = frame->height;
102     const int slice_start = (height * jobnr) / nb_jobs;
103     const int slice_end = (height * (jobnr + 1)) / nb_jobs;
104     const int ylinesize = frame->linesize[0] / 2;
105     const int ulinesize = frame->linesize[1] / 2;
106     const int vlinesize = frame->linesize[2] / 2;
107     uint16_t *yptr = (uint16_t *)frame->data[0] + slice_start * ylinesize;
108     uint16_t *uptr = (uint16_t *)frame->data[1] + slice_start * ulinesize;
109     uint16_t *vptr = (uint16_t *)frame->data[2] + slice_start * vlinesize;
110     const float saturation = s->saturation;
111     const float bl = s->bl;
112     const float rl = s->rl;
113     const float bd = s->bh - bl;
114     const float rd = s->rh - rl;
115
116     for (int y = slice_start; y < slice_end; y++) {
117         for (int x = 0; x < width; x++) {
118             PROCESS()
119
120             yptr[x] = av_clip_uintp2_c( ny         * max, depth);
121             uptr[x] = av_clip_uintp2_c((nu + 0.5f) * max, depth);
122             vptr[x] = av_clip_uintp2_c((nv + 0.5f) * max, depth);
123         }
124
125         yptr += ylinesize;
126         uptr += ulinesize;
127         vptr += vlinesize;
128     }
129
130     return 0;
131 }
132
133 static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
134 {
135     AVFilterContext *ctx = inlink->dst;
136     ColorCorrectContext *s = ctx->priv;
137
138     ctx->internal->execute(ctx, s->do_slice, frame, NULL,
139                            FFMIN(frame->height, ff_filter_get_nb_threads(ctx)));
140
141     return ff_filter_frame(ctx->outputs[0], frame);
142 }
143
144 static av_cold int query_formats(AVFilterContext *ctx)
145 {
146     static const enum AVPixelFormat pixel_fmts[] = {
147         AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUVJ444P,
148         AV_PIX_FMT_YUV444P9, AV_PIX_FMT_YUV444P10, AV_PIX_FMT_YUV444P12, AV_PIX_FMT_YUV444P14, AV_PIX_FMT_YUV444P16,
149         AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUVA444P9, AV_PIX_FMT_YUVA444P10, AV_PIX_FMT_YUVA444P12, AV_PIX_FMT_YUVA444P16,
150         AV_PIX_FMT_NONE
151     };
152
153     AVFilterFormats *formats = NULL;
154
155     formats = ff_make_format_list(pixel_fmts);
156     if (!formats)
157         return AVERROR(ENOMEM);
158
159     return ff_set_common_formats(ctx, formats);
160 }
161
162 static av_cold int config_input(AVFilterLink *inlink)
163 {
164     AVFilterContext *ctx = inlink->dst;
165     ColorCorrectContext *s = ctx->priv;
166     const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
167
168     s->depth = desc->comp[0].depth;
169     s->do_slice = s->depth <= 8 ? colorcorrect_slice8 : colorcorrect_slice16;
170
171     return 0;
172 }
173
174 static const AVFilterPad colorcorrect_inputs[] = {
175     {
176         .name           = "default",
177         .type           = AVMEDIA_TYPE_VIDEO,
178         .needs_writable = 1,
179         .filter_frame   = filter_frame,
180         .config_props   = config_input,
181     },
182     { NULL }
183 };
184
185 static const AVFilterPad colorcorrect_outputs[] = {
186     {
187         .name = "default",
188         .type = AVMEDIA_TYPE_VIDEO,
189     },
190     { NULL }
191 };
192
193 #define OFFSET(x) offsetof(ColorCorrectContext, x)
194 #define VF AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_RUNTIME_PARAM
195
196 static const AVOption colorcorrect_options[] = {
197     { "rl", "set the red shadow spot",              OFFSET(rl), AV_OPT_TYPE_FLOAT, {.dbl=0}, -1, 1, VF },
198     { "bl", "set the blue shadow spot",             OFFSET(bl), AV_OPT_TYPE_FLOAT, {.dbl=0}, -1, 1, VF },
199     { "rh", "set the red highlight spot",           OFFSET(rh), AV_OPT_TYPE_FLOAT, {.dbl=0}, -1, 1, VF },
200     { "bh", "set the blue highlight spot",          OFFSET(bh), AV_OPT_TYPE_FLOAT, {.dbl=0}, -1, 1, VF },
201     { "saturation", "set the amount of saturation", OFFSET(saturation), AV_OPT_TYPE_FLOAT, {.dbl=1}, -3, 3, VF },
202     { NULL }
203 };
204
205 AVFILTER_DEFINE_CLASS(colorcorrect);
206
207 const AVFilter ff_vf_colorcorrect = {
208     .name          = "colorcorrect",
209     .description   = NULL_IF_CONFIG_SMALL("Adjust color white balance selectively for blacks and whites."),
210     .priv_size     = sizeof(ColorCorrectContext),
211     .priv_class    = &colorcorrect_class,
212     .query_formats = query_formats,
213     .inputs        = colorcorrect_inputs,
214     .outputs       = colorcorrect_outputs,
215     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,
216     .process_command = ff_filter_process_command,
217 };