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"
22 #include "libavutil/pixdesc.h"
24 #include "drawutils.h"
50 } ColorBalanceContext
;
52 #define OFFSET(x) offsetof(ColorBalanceContext, x)
53 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
54 static const AVOption colorbalance_options
[] = {
55 { "rs", "set red shadows", OFFSET(cyan_red
.shadows
), AV_OPT_TYPE_DOUBLE
, {.dbl
=0}, -1, 1, FLAGS
},
56 { "gs", "set green shadows", OFFSET(magenta_green
.shadows
), AV_OPT_TYPE_DOUBLE
, {.dbl
=0}, -1, 1, FLAGS
},
57 { "bs", "set blue shadows", OFFSET(yellow_blue
.shadows
), AV_OPT_TYPE_DOUBLE
, {.dbl
=0}, -1, 1, FLAGS
},
58 { "rm", "set red midtones", OFFSET(cyan_red
.midtones
), AV_OPT_TYPE_DOUBLE
, {.dbl
=0}, -1, 1, FLAGS
},
59 { "gm", "set green midtones", OFFSET(magenta_green
.midtones
), AV_OPT_TYPE_DOUBLE
, {.dbl
=0}, -1, 1, FLAGS
},
60 { "bm", "set blue midtones", OFFSET(yellow_blue
.midtones
), AV_OPT_TYPE_DOUBLE
, {.dbl
=0}, -1, 1, FLAGS
},
61 { "rh", "set red highlights", OFFSET(cyan_red
.highlights
), AV_OPT_TYPE_DOUBLE
, {.dbl
=0}, -1, 1, FLAGS
},
62 { "gh", "set green highlights", OFFSET(magenta_green
.highlights
), AV_OPT_TYPE_DOUBLE
, {.dbl
=0}, -1, 1, FLAGS
},
63 { "bh", "set blue highlights", OFFSET(yellow_blue
.highlights
), AV_OPT_TYPE_DOUBLE
, {.dbl
=0}, -1, 1, FLAGS
},
67 AVFILTER_DEFINE_CLASS(colorbalance
);
69 static int query_formats(AVFilterContext
*ctx
)
71 static const enum AVPixelFormat pix_fmts
[] = {
72 AV_PIX_FMT_RGB24
, AV_PIX_FMT_BGR24
,
73 AV_PIX_FMT_RGBA
, AV_PIX_FMT_BGRA
,
74 AV_PIX_FMT_ABGR
, AV_PIX_FMT_ARGB
,
75 AV_PIX_FMT_0BGR
, AV_PIX_FMT_0RGB
,
76 AV_PIX_FMT_RGB0
, AV_PIX_FMT_BGR0
,
80 ff_set_common_formats(ctx
, ff_make_format_list(pix_fmts
));
84 static int config_output(AVFilterLink
*outlink
)
86 AVFilterContext
*ctx
= outlink
->src
;
87 ColorBalanceContext
*cb
= ctx
->priv
;
88 const AVPixFmtDescriptor
*desc
= av_pix_fmt_desc_get(outlink
->format
);
89 double *shadows
, *midtones
, *highlights
, *buffer
;
92 buffer
= av_malloc(256 * 3 * sizeof(*buffer
));
94 return AVERROR(ENOMEM
);
96 shadows
= buffer
+ 256 * 0;
97 midtones
= buffer
+ 256 * 1;
98 highlights
= buffer
+ 256 * 2;
100 for (i
= 0; i
< 256; i
++) {
101 double low
= av_clipd((i
- 85.0) / -64.0 + 0.5, 0, 1) * 178.5;
102 double mid
= av_clipd((i
- 85.0) / 64.0 + 0.5, 0, 1) *
103 av_clipd((i
+ 85.0 - 255.0) / -64.0 + 0.5, 0, 1) * 178.5;
107 highlights
[255 - i
] = low
;
110 for (i
= 0; i
< 256; i
++) {
113 r
= av_clip_uint8(r
+ cb
->cyan_red
.shadows
* shadows
[r
]);
114 r
= av_clip_uint8(r
+ cb
->cyan_red
.midtones
* midtones
[r
]);
115 r
= av_clip_uint8(r
+ cb
->cyan_red
.highlights
* highlights
[r
]);
117 g
= av_clip_uint8(g
+ cb
->magenta_green
.shadows
* shadows
[g
]);
118 g
= av_clip_uint8(g
+ cb
->magenta_green
.midtones
* midtones
[g
]);
119 g
= av_clip_uint8(g
+ cb
->magenta_green
.highlights
* highlights
[g
]);
121 b
= av_clip_uint8(b
+ cb
->yellow_blue
.shadows
* shadows
[b
]);
122 b
= av_clip_uint8(b
+ cb
->yellow_blue
.midtones
* midtones
[b
]);
123 b
= av_clip_uint8(b
+ cb
->yellow_blue
.highlights
* highlights
[b
]);
132 ff_fill_rgba_map(cb
->rgba_map
, outlink
->format
);
133 cb
->step
= av_get_padded_bits_per_pixel(desc
) >> 3;
138 static int filter_frame(AVFilterLink
*inlink
, AVFrame
*in
)
140 AVFilterContext
*ctx
= inlink
->dst
;
141 ColorBalanceContext
*cb
= ctx
->priv
;
142 AVFilterLink
*outlink
= ctx
->outputs
[0];
143 const uint8_t roffset
= cb
->rgba_map
[R
];
144 const uint8_t goffset
= cb
->rgba_map
[G
];
145 const uint8_t boffset
= cb
->rgba_map
[B
];
146 const uint8_t aoffset
= cb
->rgba_map
[A
];
147 const int step
= cb
->step
;
148 const uint8_t *srcrow
= in
->data
[0];
153 if (av_frame_is_writable(in
)) {
156 out
= ff_get_video_buffer(outlink
, outlink
->w
, outlink
->h
);
159 return AVERROR(ENOMEM
);
161 av_frame_copy_props(out
, in
);
164 dstrow
= out
->data
[0];
165 for (i
= 0; i
< outlink
->h
; i
++) {
166 const uint8_t *src
= srcrow
;
167 uint8_t *dst
= dstrow
;
169 for (j
= 0; j
< outlink
->w
* step
; j
+= step
) {
170 dst
[j
+ roffset
] = cb
->lut
[R
][src
[j
+ roffset
]];
171 dst
[j
+ goffset
] = cb
->lut
[G
][src
[j
+ goffset
]];
172 dst
[j
+ boffset
] = cb
->lut
[B
][src
[j
+ boffset
]];
173 if (in
!= out
&& step
== 4)
174 dst
[j
+ aoffset
] = src
[j
+ aoffset
];
177 srcrow
+= in
->linesize
[0];
178 dstrow
+= out
->linesize
[0];
183 return ff_filter_frame(ctx
->outputs
[0], out
);
186 static const AVFilterPad colorbalance_inputs
[] = {
189 .type
= AVMEDIA_TYPE_VIDEO
,
190 .filter_frame
= filter_frame
,
195 static const AVFilterPad colorbalance_outputs
[] = {
198 .type
= AVMEDIA_TYPE_VIDEO
,
199 .config_props
= config_output
,
204 AVFilter ff_vf_colorbalance
= {
205 .name
= "colorbalance",
206 .description
= NULL_IF_CONFIG_SMALL("Adjust the color balance."),
207 .priv_size
= sizeof(ColorBalanceContext
),
208 .priv_class
= &colorbalance_class
,
209 .query_formats
= query_formats
,
210 .inputs
= colorbalance_inputs
,
211 .outputs
= colorbalance_outputs
,
212 .flags
= AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC
,