2 * Copyright (c) 2003 Michael Niedermayer
3 * Copyright (c) 2012 Jeremy Tran
5 * This file is part of FFmpeg.
7 * FFmpeg is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * FFmpeg is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License along
18 * with FFmpeg; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 * Apply a hue/saturation filter to the input video
25 * Ported from MPlayer libmpcodecs/vf_hue.c.
28 #include "libavutil/imgutils.h"
29 #include "libavutil/pixdesc.h"
45 static av_cold int init(AVFilterContext *ctx, const char *args)
47 HueContext *hue = ctx->priv;
53 n = sscanf(args, "%f%c%f%c", &h, &c1, &s, &c2);
54 if (n != 0 && n != 1 && (n != 3 || c1 != ':')) {
55 av_log(ctx, AV_LOG_ERROR,
56 "Invalid syntax for argument '%s': "
57 "must be in the form 'hue[:saturation]'\n", args);
58 return AVERROR(EINVAL);
62 if (s < -10 || s > 10) {
63 av_log(ctx, AV_LOG_ERROR,
64 "Invalid value for saturation %0.1f: "
65 "must be included between range -10 and +10\n", s);
66 return AVERROR(EINVAL);
69 /* Convert angle from degree to radian */
70 hue->hue = h * M_PI / 180;
76 static int query_formats(AVFilterContext *ctx)
78 static const enum PixelFormat pix_fmts[] = {
79 PIX_FMT_YUV444P, PIX_FMT_YUV422P,
80 PIX_FMT_YUV420P, PIX_FMT_YUV411P,
81 PIX_FMT_YUV410P, PIX_FMT_YUV440P,
86 ff_set_common_formats(ctx, ff_make_format_list(pix_fmts));
91 static int config_props(AVFilterLink *inlink)
93 HueContext *hue = inlink->dst->priv;
94 const AVPixFmtDescriptor *desc = &av_pix_fmt_descriptors[inlink->format];
96 hue->hsub = desc->log2_chroma_w;
97 hue->vsub = desc->log2_chroma_h;
99 * Scale the value to the norm of the resulting (U,V) vector, that is
101 * This will be useful in the process_chrominance function.
103 hue->hue_sin = rint(sin(hue->hue) * (1 << 16) * hue->saturation);
104 hue->hue_cos = rint(cos(hue->hue) * (1 << 16) * hue->saturation);
109 static void process_chrominance(uint8_t *udst, uint8_t *vdst, const int dst_linesize,
110 uint8_t *usrc, uint8_t *vsrc, const int src_linesize,
112 const int32_t c, const int32_t s)
114 int32_t u, v, new_u, new_v;
118 * If we consider U and V as the components of a 2D vector then its angle
119 * is the hue and the norm is the saturation
122 for (i = 0; i < w; i++) {
123 /* Normalize the components from range [16;140] to [-112;112] */
127 * Apply the rotation of the vector : (c * u) - (s * v)
129 * De-normalize the components (without forgetting to scale 128
131 * Finally scale back the result by >> 16
133 new_u = ((c * u) - (s * v) + (1 << 15) + (128 << 16)) >> 16;
134 new_v = ((s * u) + (c * v) + (1 << 15) + (128 << 16)) >> 16;
136 /* Prevent a potential overflow */
137 udst[i] = av_clip_uint8_c(new_u);
138 vdst[i] = av_clip_uint8_c(new_v);
141 usrc += src_linesize;
142 vsrc += src_linesize;
143 udst += dst_linesize;
144 vdst += dst_linesize;
148 static int draw_slice(AVFilterLink *inlink, int y, int h, int slice_dir)
150 HueContext *hue = inlink->dst->priv;
151 AVFilterBufferRef *inpic = inlink->cur_buf;
152 AVFilterBufferRef *outpic = inlink->dst->outputs[0]->out_buf;
153 uint8_t *inrow[3], *outrow[3]; // 0 : Y, 1 : U, 2 : V
156 inrow[0] = inpic->data[0] + y * inpic->linesize[0];
157 outrow[0] = outpic->data[0] + y * outpic->linesize[0];
159 for (plane = 1; plane < 3; plane++) {
160 inrow[plane] = inpic->data[plane] + (y >> hue->vsub) * inpic->linesize[plane];
161 outrow[plane] = outpic->data[plane] + (y >> hue->vsub) * outpic->linesize[plane];
164 av_image_copy_plane(outrow[0], outpic->linesize[0],
165 inrow[0], inpic->linesize[0],
166 inlink->w, inlink->h);
168 process_chrominance(outrow[1], outrow[2], outpic->linesize[1],
169 inrow[1], inrow[2], inpic->linesize[1],
170 inlink->w >> hue->hsub, inlink->h >> hue->vsub,
171 hue->hue_cos, hue->hue_sin);
173 return ff_draw_slice(inlink->dst->outputs[0], y, h, slice_dir);
176 AVFilter avfilter_vf_hue = {
178 .description = NULL_IF_CONFIG_SMALL("Adjust the hue and saturation of the input video."),
180 .priv_size = sizeof(HueContext),
183 .query_formats = query_formats,
185 .inputs = (const AVFilterPad[]) {
188 .type = AVMEDIA_TYPE_VIDEO,
189 .draw_slice = draw_slice,
190 .config_props = config_props,
191 .min_perms = AV_PERM_READ,
195 .outputs = (const AVFilterPad[]) {
198 .type = AVMEDIA_TYPE_VIDEO,