2 * Copyright (c) 2013 Paul B Mahol
4 * This file is part of FFmpeg.
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.
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.
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
21 #include "libavutil/opt.h"
23 #include "drawutils.h"
35 double rr, rg, rb, ra;
36 double gr, gg, gb, ga;
37 double br, bg, bb, ba;
38 double ar, ag, ab, aa;
45 } ColorChannelMixerContext;
47 #define OFFSET(x) offsetof(ColorChannelMixerContext, x)
48 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
49 static const AVOption colorchannelmixer_options[] = {
50 { "rr", "set the red gain for the red channel", OFFSET(rr), AV_OPT_TYPE_DOUBLE, {.dbl=1}, -2, 2, FLAGS },
51 { "rg", "set the green gain for the red channel", OFFSET(rg), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -2, 2, FLAGS },
52 { "rb", "set the blue gain for the red channel", OFFSET(rb), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -2, 2, FLAGS },
53 { "ra", "set the alpha gain for the red channel", OFFSET(ra), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -2, 2, FLAGS },
54 { "gr", "set the red gain for the green channel", OFFSET(gr), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -2, 2, FLAGS },
55 { "gg", "set the green gain for the green channel", OFFSET(gg), AV_OPT_TYPE_DOUBLE, {.dbl=1}, -2, 2, FLAGS },
56 { "gb", "set the blue gain for the green channel", OFFSET(gb), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -2, 2, FLAGS },
57 { "ga", "set the alpha gain for the green channel", OFFSET(ga), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -2, 2, FLAGS },
58 { "br", "set the red gain for the blue channel", OFFSET(br), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -2, 2, FLAGS },
59 { "bg", "set the green gain for the blue channel", OFFSET(bg), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -2, 2, FLAGS },
60 { "bb", "set the blue gain for the blue channel", OFFSET(bb), AV_OPT_TYPE_DOUBLE, {.dbl=1}, -2, 2, FLAGS },
61 { "ba", "set the alpha gain for the blue channel", OFFSET(ba), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -2, 2, FLAGS },
62 { "ar", "set the red gain for the alpha channel", OFFSET(ar), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -2, 2, FLAGS },
63 { "ag", "set the green gain for the alpha channel", OFFSET(ag), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -2, 2, FLAGS },
64 { "ab", "set the blue gain for the alpha channel", OFFSET(ab), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -2, 2, FLAGS },
65 { "aa", "set the alpha gain for the alpha channel", OFFSET(aa), AV_OPT_TYPE_DOUBLE, {.dbl=1}, -2, 2, FLAGS },
69 AVFILTER_DEFINE_CLASS(colorchannelmixer);
71 static int query_formats(AVFilterContext *ctx)
73 static const enum AVPixelFormat pix_fmts[] = {
74 AV_PIX_FMT_RGB24, AV_PIX_FMT_BGR24,
75 AV_PIX_FMT_RGBA, AV_PIX_FMT_BGRA,
76 AV_PIX_FMT_ARGB, AV_PIX_FMT_ABGR,
77 AV_PIX_FMT_0RGB, AV_PIX_FMT_0BGR,
78 AV_PIX_FMT_RGB0, AV_PIX_FMT_BGR0,
79 AV_PIX_FMT_RGB48, AV_PIX_FMT_BGR48,
80 AV_PIX_FMT_RGBA64, AV_PIX_FMT_BGRA64,
84 ff_set_common_formats(ctx, ff_make_format_list(pix_fmts));
88 static int config_output(AVFilterLink *outlink)
90 AVFilterContext *ctx = outlink->src;
91 ColorChannelMixerContext *cm = ctx->priv;
92 int i, j, size, *buffer;
94 switch (outlink->format) {
95 case AV_PIX_FMT_RGB48:
96 case AV_PIX_FMT_BGR48:
97 case AV_PIX_FMT_RGBA64:
98 case AV_PIX_FMT_BGRA64:
99 if (outlink->format == AV_PIX_FMT_RGB48 ||
100 outlink->format == AV_PIX_FMT_RGBA64) {
114 ff_fill_rgba_map(cm->rgba_map, outlink->format);
118 cm->buffer = buffer = av_malloc(16 * size * sizeof(*cm->buffer));
120 return AVERROR(ENOMEM);
122 for (i = 0; i < 4; i++)
123 for (j = 0; j < 4; j++, buffer += size)
124 cm->lut[i][j] = buffer;
126 for (i = 0; i < size; i++) {
127 cm->lut[R][R][i] = i * cm->rr;
128 cm->lut[R][G][i] = i * cm->rg;
129 cm->lut[R][B][i] = i * cm->rb;
130 cm->lut[R][A][i] = i * cm->ra;
132 cm->lut[G][R][i] = i * cm->gr;
133 cm->lut[G][G][i] = i * cm->gg;
134 cm->lut[G][B][i] = i * cm->gb;
135 cm->lut[G][A][i] = i * cm->ga;
137 cm->lut[B][R][i] = i * cm->br;
138 cm->lut[B][G][i] = i * cm->bg;
139 cm->lut[B][B][i] = i * cm->bb;
140 cm->lut[B][A][i] = i * cm->ba;
142 cm->lut[A][R][i] = i * cm->ar;
143 cm->lut[A][G][i] = i * cm->ag;
144 cm->lut[A][B][i] = i * cm->ab;
145 cm->lut[A][A][i] = i * cm->aa;
151 static int filter_frame(AVFilterLink *inlink, AVFrame *in)
153 AVFilterContext *ctx = inlink->dst;
154 ColorChannelMixerContext *cm = ctx->priv;
155 AVFilterLink *outlink = ctx->outputs[0];
156 const uint8_t roffset = cm->rgba_map[R];
157 const uint8_t goffset = cm->rgba_map[G];
158 const uint8_t boffset = cm->rgba_map[B];
159 const uint8_t aoffset = cm->rgba_map[A];
160 const uint8_t *srcrow = in->data[0];
165 if (av_frame_is_writable(in)) {
168 out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
171 return AVERROR(ENOMEM);
173 av_frame_copy_props(out, in);
176 dstrow = out->data[0];
177 switch (outlink->format) {
178 case AV_PIX_FMT_BGR24:
179 case AV_PIX_FMT_RGB24:
180 for (i = 0; i < outlink->h; i++) {
181 const uint8_t *src = srcrow;
182 uint8_t *dst = dstrow;
184 for (j = 0; j < outlink->w * 3; j += 3) {
185 const uint8_t rin = src[j + roffset];
186 const uint8_t gin = src[j + goffset];
187 const uint8_t bin = src[j + boffset];
189 dst[j + roffset] = av_clip_uint8(cm->lut[R][R][rin] +
192 dst[j + goffset] = av_clip_uint8(cm->lut[G][R][rin] +
195 dst[j + boffset] = av_clip_uint8(cm->lut[B][R][rin] +
200 srcrow += in->linesize[0];
201 dstrow += out->linesize[0];
204 case AV_PIX_FMT_0BGR:
205 case AV_PIX_FMT_0RGB:
206 case AV_PIX_FMT_BGR0:
207 case AV_PIX_FMT_RGB0:
208 for (i = 0; i < outlink->h; i++) {
209 const uint8_t *src = srcrow;
210 uint8_t *dst = dstrow;
212 for (j = 0; j < outlink->w * 4; j += 4) {
213 const uint8_t rin = src[j + roffset];
214 const uint8_t gin = src[j + goffset];
215 const uint8_t bin = src[j + boffset];
217 dst[j + roffset] = av_clip_uint8(cm->lut[R][R][rin] +
220 dst[j + goffset] = av_clip_uint8(cm->lut[G][R][rin] +
223 dst[j + boffset] = av_clip_uint8(cm->lut[B][R][rin] +
227 dst[j + aoffset] = 0;
230 srcrow += in->linesize[0];
231 dstrow += out->linesize[0];
234 case AV_PIX_FMT_ABGR:
235 case AV_PIX_FMT_ARGB:
236 case AV_PIX_FMT_BGRA:
237 case AV_PIX_FMT_RGBA:
238 for (i = 0; i < outlink->h; i++) {
239 const uint8_t *src = srcrow;
240 uint8_t *dst = dstrow;
242 for (j = 0; j < outlink->w * 4; j += 4) {
243 const uint8_t rin = src[j + roffset];
244 const uint8_t gin = src[j + goffset];
245 const uint8_t bin = src[j + boffset];
246 const uint8_t ain = src[j + aoffset];
248 dst[j + roffset] = av_clip_uint8(cm->lut[R][R][rin] +
252 dst[j + goffset] = av_clip_uint8(cm->lut[G][R][rin] +
256 dst[j + boffset] = av_clip_uint8(cm->lut[B][R][rin] +
260 dst[j + aoffset] = av_clip_uint8(cm->lut[A][R][rin] +
266 srcrow += in->linesize[0];
267 dstrow += out->linesize[0];
270 case AV_PIX_FMT_BGR48:
271 case AV_PIX_FMT_RGB48:
272 for (i = 0; i < outlink->h; i++) {
273 const uint16_t *src = (const uint16_t *)srcrow;
274 uint16_t *dst = (uint16_t *)dstrow;
276 for (j = 0; j < outlink->w * 3; j += 3) {
277 const uint16_t rin = src[j + roffset];
278 const uint16_t gin = src[j + goffset];
279 const uint16_t bin = src[j + boffset];
281 dst[j + roffset] = av_clip_uint16(cm->lut[R][R][rin] +
284 dst[j + goffset] = av_clip_uint16(cm->lut[G][R][rin] +
287 dst[j + boffset] = av_clip_uint16(cm->lut[B][R][rin] +
292 srcrow += in->linesize[0];
293 dstrow += out->linesize[0];
296 case AV_PIX_FMT_BGRA64:
297 case AV_PIX_FMT_RGBA64:
298 for (i = 0; i < outlink->h; i++) {
299 const uint16_t *src = (const uint16_t *)srcrow;
300 uint16_t *dst = (uint16_t *)dstrow;
302 for (j = 0; j < outlink->w * 4; j += 4) {
303 const uint16_t rin = src[j + roffset];
304 const uint16_t gin = src[j + goffset];
305 const uint16_t bin = src[j + boffset];
306 const uint16_t ain = src[j + aoffset];
308 dst[j + roffset] = av_clip_uint16(cm->lut[R][R][rin] +
312 dst[j + goffset] = av_clip_uint16(cm->lut[G][R][rin] +
316 dst[j + boffset] = av_clip_uint16(cm->lut[B][R][rin] +
320 dst[j + aoffset] = av_clip_uint16(cm->lut[A][R][rin] +
326 srcrow += in->linesize[0];
327 dstrow += out->linesize[0];
333 return ff_filter_frame(ctx->outputs[0], out);
336 static av_cold void uninit(AVFilterContext *ctx)
338 ColorChannelMixerContext *cm = ctx->priv;
340 av_freep(&cm->buffer);
343 static const AVFilterPad colorchannelmixer_inputs[] = {
346 .type = AVMEDIA_TYPE_VIDEO,
347 .filter_frame = filter_frame,
352 static const AVFilterPad colorchannelmixer_outputs[] = {
355 .type = AVMEDIA_TYPE_VIDEO,
356 .config_props = config_output,
361 AVFilter avfilter_vf_colorchannelmixer = {
362 .name = "colorchannelmixer",
363 .description = NULL_IF_CONFIG_SMALL("Adjust colors by mixing color channels."),
364 .priv_size = sizeof(ColorChannelMixerContext),
365 .priv_class = &colorchannelmixer_class,
367 .query_formats = query_formats,
368 .inputs = colorchannelmixer_inputs,
369 .outputs = colorchannelmixer_outputs,
370 .flags = AVFILTER_FLAG_SUPPORT_TIMELINE,