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 ff_fill_rgba_map(cm
->rgba_map
, outlink
->format
);
96 switch (outlink
->format
) {
97 case AV_PIX_FMT_RGB48
:
98 case AV_PIX_FMT_BGR48
:
99 case AV_PIX_FMT_RGBA64
:
100 case AV_PIX_FMT_BGRA64
:
107 cm
->buffer
= buffer
= av_malloc(16 * size
* sizeof(*cm
->buffer
));
109 return AVERROR(ENOMEM
);
111 for (i
= 0; i
< 4; i
++)
112 for (j
= 0; j
< 4; j
++, buffer
+= size
)
113 cm
->lut
[i
][j
] = buffer
;
115 for (i
= 0; i
< size
; i
++) {
116 cm
->lut
[R
][R
][i
] = round(i
* cm
->rr
);
117 cm
->lut
[R
][G
][i
] = round(i
* cm
->rg
);
118 cm
->lut
[R
][B
][i
] = round(i
* cm
->rb
);
119 cm
->lut
[R
][A
][i
] = round(i
* cm
->ra
);
121 cm
->lut
[G
][R
][i
] = round(i
* cm
->gr
);
122 cm
->lut
[G
][G
][i
] = round(i
* cm
->gg
);
123 cm
->lut
[G
][B
][i
] = round(i
* cm
->gb
);
124 cm
->lut
[G
][A
][i
] = round(i
* cm
->ga
);
126 cm
->lut
[B
][R
][i
] = round(i
* cm
->br
);
127 cm
->lut
[B
][G
][i
] = round(i
* cm
->bg
);
128 cm
->lut
[B
][B
][i
] = round(i
* cm
->bb
);
129 cm
->lut
[B
][A
][i
] = round(i
* cm
->ba
);
131 cm
->lut
[A
][R
][i
] = round(i
* cm
->ar
);
132 cm
->lut
[A
][G
][i
] = round(i
* cm
->ag
);
133 cm
->lut
[A
][B
][i
] = round(i
* cm
->ab
);
134 cm
->lut
[A
][A
][i
] = round(i
* cm
->aa
);
140 static int filter_frame(AVFilterLink
*inlink
, AVFrame
*in
)
142 AVFilterContext
*ctx
= inlink
->dst
;
143 ColorChannelMixerContext
*cm
= ctx
->priv
;
144 AVFilterLink
*outlink
= ctx
->outputs
[0];
145 const uint8_t roffset
= cm
->rgba_map
[R
];
146 const uint8_t goffset
= cm
->rgba_map
[G
];
147 const uint8_t boffset
= cm
->rgba_map
[B
];
148 const uint8_t aoffset
= cm
->rgba_map
[A
];
149 const uint8_t *srcrow
= in
->data
[0];
154 if (av_frame_is_writable(in
)) {
157 out
= ff_get_video_buffer(outlink
, outlink
->w
, outlink
->h
);
160 return AVERROR(ENOMEM
);
162 av_frame_copy_props(out
, in
);
165 dstrow
= out
->data
[0];
166 switch (outlink
->format
) {
167 case AV_PIX_FMT_BGR24
:
168 case AV_PIX_FMT_RGB24
:
169 for (i
= 0; i
< outlink
->h
; i
++) {
170 const uint8_t *src
= srcrow
;
171 uint8_t *dst
= dstrow
;
173 for (j
= 0; j
< outlink
->w
* 3; j
+= 3) {
174 const uint8_t rin
= src
[j
+ roffset
];
175 const uint8_t gin
= src
[j
+ goffset
];
176 const uint8_t bin
= src
[j
+ boffset
];
178 dst
[j
+ roffset
] = av_clip_uint8(cm
->lut
[R
][R
][rin
] +
181 dst
[j
+ goffset
] = av_clip_uint8(cm
->lut
[G
][R
][rin
] +
184 dst
[j
+ boffset
] = av_clip_uint8(cm
->lut
[B
][R
][rin
] +
189 srcrow
+= in
->linesize
[0];
190 dstrow
+= out
->linesize
[0];
193 case AV_PIX_FMT_0BGR
:
194 case AV_PIX_FMT_0RGB
:
195 case AV_PIX_FMT_BGR0
:
196 case AV_PIX_FMT_RGB0
:
197 for (i
= 0; i
< outlink
->h
; i
++) {
198 const uint8_t *src
= srcrow
;
199 uint8_t *dst
= dstrow
;
201 for (j
= 0; j
< outlink
->w
* 4; j
+= 4) {
202 const uint8_t rin
= src
[j
+ roffset
];
203 const uint8_t gin
= src
[j
+ goffset
];
204 const uint8_t bin
= src
[j
+ boffset
];
206 dst
[j
+ roffset
] = av_clip_uint8(cm
->lut
[R
][R
][rin
] +
209 dst
[j
+ goffset
] = av_clip_uint8(cm
->lut
[G
][R
][rin
] +
212 dst
[j
+ boffset
] = av_clip_uint8(cm
->lut
[B
][R
][rin
] +
216 dst
[j
+ aoffset
] = 0;
219 srcrow
+= in
->linesize
[0];
220 dstrow
+= out
->linesize
[0];
223 case AV_PIX_FMT_ABGR
:
224 case AV_PIX_FMT_ARGB
:
225 case AV_PIX_FMT_BGRA
:
226 case AV_PIX_FMT_RGBA
:
227 for (i
= 0; i
< outlink
->h
; i
++) {
228 const uint8_t *src
= srcrow
;
229 uint8_t *dst
= dstrow
;
231 for (j
= 0; j
< outlink
->w
* 4; j
+= 4) {
232 const uint8_t rin
= src
[j
+ roffset
];
233 const uint8_t gin
= src
[j
+ goffset
];
234 const uint8_t bin
= src
[j
+ boffset
];
235 const uint8_t ain
= src
[j
+ aoffset
];
237 dst
[j
+ roffset
] = av_clip_uint8(cm
->lut
[R
][R
][rin
] +
241 dst
[j
+ goffset
] = av_clip_uint8(cm
->lut
[G
][R
][rin
] +
245 dst
[j
+ boffset
] = av_clip_uint8(cm
->lut
[B
][R
][rin
] +
249 dst
[j
+ aoffset
] = av_clip_uint8(cm
->lut
[A
][R
][rin
] +
255 srcrow
+= in
->linesize
[0];
256 dstrow
+= out
->linesize
[0];
259 case AV_PIX_FMT_BGR48
:
260 case AV_PIX_FMT_RGB48
:
261 for (i
= 0; i
< outlink
->h
; i
++) {
262 const uint16_t *src
= (const uint16_t *)srcrow
;
263 uint16_t *dst
= (uint16_t *)dstrow
;
265 for (j
= 0; j
< outlink
->w
* 3; j
+= 3) {
266 const uint16_t rin
= src
[j
+ roffset
];
267 const uint16_t gin
= src
[j
+ goffset
];
268 const uint16_t bin
= src
[j
+ boffset
];
270 dst
[j
+ roffset
] = av_clip_uint16(cm
->lut
[R
][R
][rin
] +
273 dst
[j
+ goffset
] = av_clip_uint16(cm
->lut
[G
][R
][rin
] +
276 dst
[j
+ boffset
] = av_clip_uint16(cm
->lut
[B
][R
][rin
] +
281 srcrow
+= in
->linesize
[0];
282 dstrow
+= out
->linesize
[0];
285 case AV_PIX_FMT_BGRA64
:
286 case AV_PIX_FMT_RGBA64
:
287 for (i
= 0; i
< outlink
->h
; i
++) {
288 const uint16_t *src
= (const uint16_t *)srcrow
;
289 uint16_t *dst
= (uint16_t *)dstrow
;
291 for (j
= 0; j
< outlink
->w
* 4; j
+= 4) {
292 const uint16_t rin
= src
[j
+ roffset
];
293 const uint16_t gin
= src
[j
+ goffset
];
294 const uint16_t bin
= src
[j
+ boffset
];
295 const uint16_t ain
= src
[j
+ aoffset
];
297 dst
[j
+ roffset
] = av_clip_uint16(cm
->lut
[R
][R
][rin
] +
301 dst
[j
+ goffset
] = av_clip_uint16(cm
->lut
[G
][R
][rin
] +
305 dst
[j
+ boffset
] = av_clip_uint16(cm
->lut
[B
][R
][rin
] +
309 dst
[j
+ aoffset
] = av_clip_uint16(cm
->lut
[A
][R
][rin
] +
315 srcrow
+= in
->linesize
[0];
316 dstrow
+= out
->linesize
[0];
322 return ff_filter_frame(ctx
->outputs
[0], out
);
325 static av_cold
void uninit(AVFilterContext
*ctx
)
327 ColorChannelMixerContext
*cm
= ctx
->priv
;
329 av_freep(&cm
->buffer
);
332 static const AVFilterPad colorchannelmixer_inputs
[] = {
335 .type
= AVMEDIA_TYPE_VIDEO
,
336 .filter_frame
= filter_frame
,
341 static const AVFilterPad colorchannelmixer_outputs
[] = {
344 .type
= AVMEDIA_TYPE_VIDEO
,
345 .config_props
= config_output
,
350 AVFilter ff_vf_colorchannelmixer
= {
351 .name
= "colorchannelmixer",
352 .description
= NULL_IF_CONFIG_SMALL("Adjust colors by mixing color channels."),
353 .priv_size
= sizeof(ColorChannelMixerContext
),
354 .priv_class
= &colorchannelmixer_class
,
356 .query_formats
= query_formats
,
357 .inputs
= colorchannelmixer_inputs
,
358 .outputs
= colorchannelmixer_outputs
,
359 .flags
= AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC
,