lavfi/hue: reindent after previous commits
[ffmpeg.git] / libavfilter / vf_hue.c
1 /*
2  * Copyright (c) 2003 Michael Niedermayer
3  * Copyright (c) 2012 Jeremy Tran
4  *
5  * This file is part of FFmpeg.
6  *
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.
11  *
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.
16  *
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.
20  */
21
22 /**
23  * @file
24  * Apply a hue/saturation filter to the input video
25  * Ported from MPlayer libmpcodecs/vf_hue.c.
26  */
27
28 #include <float.h>
29 #include "libavutil/imgutils.h"
30 #include "libavutil/opt.h"
31 #include "libavutil/pixdesc.h"
32
33 #include "avfilter.h"
34 #include "formats.h"
35 #include "internal.h"
36 #include "video.h"
37
38 #define HUE_DEFAULT_VAL 0
39 #define SAT_DEFAULT_VAL 1
40
41 typedef struct {
42     const    AVClass *class;
43     float    hue_deg; /* hue expressed in degrees */
44     float    hue; /* hue expressed in radians */
45     float    saturation;
46     int      hsub;
47     int      vsub;
48     int32_t hue_sin;
49     int32_t hue_cos;
50 } HueContext;
51
52 #define OFFSET(x) offsetof(HueContext, x)
53 static const AVOption hue_options[] = {
54     { "h", "set the hue angle degrees", OFFSET(hue_deg), AV_OPT_TYPE_FLOAT,
55       { -FLT_MAX }, -FLT_MAX, FLT_MAX, AV_OPT_FLAG_VIDEO_PARAM },
56     { "H", "set the hue angle radians", OFFSET(hue), AV_OPT_TYPE_FLOAT,
57       { -FLT_MAX }, -FLT_MAX, FLT_MAX, AV_OPT_FLAG_VIDEO_PARAM },
58     { "s", "set the saturation value", OFFSET(saturation), AV_OPT_TYPE_FLOAT,
59       { SAT_DEFAULT_VAL }, -10, 10, AV_OPT_FLAG_VIDEO_PARAM },
60     { NULL }
61 };
62
63 AVFILTER_DEFINE_CLASS(hue);
64
65 static av_cold int init(AVFilterContext *ctx, const char *args)
66 {
67     HueContext *hue = ctx->priv;
68     int n, ret;
69     char c1 = 0, c2 = 0;
70     char *equal;
71
72     hue->class = &hue_class;
73     av_opt_set_defaults(hue);
74
75     if (args) {
76         /* named options syntax */
77         if (equal = strchr(args, '=')) {
78             if ((ret = av_set_options_string(hue, args, "=", ":")) < 0)
79                 return ret;
80             if (hue->hue != -FLT_MAX && hue->hue_deg != -FLT_MAX) {
81                 av_log(ctx, AV_LOG_ERROR,
82                        "H and h options are incompatible and cannot be specified "
83                        "at the same time\n");
84                 return AVERROR(EINVAL);
85             }
86         /* compatibility h:s syntax */
87         } else {
88             n = sscanf(args, "%f%c%f%c", &hue->hue_deg, &c1, &hue->saturation, &c2);
89             if (n != 1 && (n != 3 || c1 != ':')) {
90                 av_log(ctx, AV_LOG_ERROR,
91                        "Invalid syntax for argument '%s': "
92                        "must be in the form 'hue[:saturation]'\n", args);
93                 return AVERROR(EINVAL);
94             }
95
96             if (hue->saturation < -10 || hue->saturation > 10) {
97                 av_log(ctx, AV_LOG_ERROR,
98                        "Invalid value for saturation %0.1f: "
99                        "must be included between range -10 and +10\n", hue->saturation);
100                 return AVERROR(EINVAL);
101             }
102         }
103     }
104
105     if (hue->saturation == -FLT_MAX)
106         hue->hue = SAT_DEFAULT_VAL;
107     if (hue->hue == -FLT_MAX)
108         hue->hue = HUE_DEFAULT_VAL;
109     if (hue->hue_deg != -FLT_MAX)
110         /* Convert angle from degrees to radians */
111         hue->hue = hue->hue_deg * M_PI / 180;
112
113     return 0;
114 }
115
116 static av_cold void uninit(AVFilterContext *ctx)
117 {
118     HueContext *hue = ctx->priv;
119
120     av_opt_free(hue);
121 }
122
123 static int query_formats(AVFilterContext *ctx)
124 {
125     static const enum PixelFormat pix_fmts[] = {
126         PIX_FMT_YUV444P,      PIX_FMT_YUV422P,
127         PIX_FMT_YUV420P,      PIX_FMT_YUV411P,
128         PIX_FMT_YUV410P,      PIX_FMT_YUV440P,
129         PIX_FMT_YUVA420P,
130         PIX_FMT_NONE
131     };
132
133     ff_set_common_formats(ctx, ff_make_format_list(pix_fmts));
134
135     return 0;
136 }
137
138 static int config_props(AVFilterLink *inlink)
139 {
140     HueContext *hue = inlink->dst->priv;
141     const AVPixFmtDescriptor *desc = &av_pix_fmt_descriptors[inlink->format];
142
143     hue->hsub = desc->log2_chroma_w;
144     hue->vsub = desc->log2_chroma_h;
145     /*
146      * Scale the value to the norm of the resulting (U,V) vector, that is
147      * the saturation.
148      * This will be useful in the process_chrominance function.
149      */
150     hue->hue_sin = rint(sin(hue->hue) * (1 << 16) * hue->saturation);
151     hue->hue_cos = rint(cos(hue->hue) * (1 << 16) * hue->saturation);
152
153     return 0;
154 }
155
156 static void process_chrominance(uint8_t *udst, uint8_t *vdst, const int dst_linesize,
157                                 uint8_t *usrc, uint8_t *vsrc, const int src_linesize,
158                                 int w, int h,
159                                 const int32_t c, const int32_t s)
160 {
161     int32_t u, v, new_u, new_v;
162     int i;
163
164     /*
165      * If we consider U and V as the components of a 2D vector then its angle
166      * is the hue and the norm is the saturation
167      */
168     while (h--) {
169         for (i = 0; i < w; i++) {
170             /* Normalize the components from range [16;140] to [-112;112] */
171             u = usrc[i] - 128;
172             v = vsrc[i] - 128;
173             /*
174              * Apply the rotation of the vector : (c * u) - (s * v)
175              *                                    (s * u) + (c * v)
176              * De-normalize the components (without forgetting to scale 128
177              * by << 16)
178              * Finally scale back the result by >> 16
179              */
180             new_u = ((c * u) - (s * v) + (1 << 15) + (128 << 16)) >> 16;
181             new_v = ((s * u) + (c * v) + (1 << 15) + (128 << 16)) >> 16;
182
183             /* Prevent a potential overflow */
184             udst[i] = av_clip_uint8_c(new_u);
185             vdst[i] = av_clip_uint8_c(new_v);
186         }
187
188         usrc += src_linesize;
189         vsrc += src_linesize;
190         udst += dst_linesize;
191         vdst += dst_linesize;
192     }
193 }
194
195 static int draw_slice(AVFilterLink *inlink, int y, int h, int slice_dir)
196 {
197     HueContext        *hue    = inlink->dst->priv;
198     AVFilterBufferRef *inpic  = inlink->cur_buf;
199     AVFilterBufferRef *outpic = inlink->dst->outputs[0]->out_buf;
200     uint8_t *inrow[3], *outrow[3]; // 0 : Y, 1 : U, 2 : V
201     int plane;
202
203     inrow[0]  = inpic->data[0]  + y * inpic->linesize[0];
204     outrow[0] = outpic->data[0] + y * outpic->linesize[0];
205
206     for (plane = 1; plane < 3; plane++) {
207         inrow[plane]  = inpic->data[plane]  + (y >> hue->vsub) * inpic->linesize[plane];
208         outrow[plane] = outpic->data[plane] + (y >> hue->vsub) * outpic->linesize[plane];
209     }
210
211     av_image_copy_plane(outrow[0], outpic->linesize[0],
212                         inrow[0],  inpic->linesize[0],
213                         inlink->w, inlink->h);
214
215     process_chrominance(outrow[1], outrow[2], outpic->linesize[1],
216                         inrow[1], inrow[2], inpic->linesize[1],
217                         inlink->w >> hue->hsub, inlink->h >> hue->vsub,
218                         hue->hue_cos, hue->hue_sin);
219
220     return ff_draw_slice(inlink->dst->outputs[0], y, h, slice_dir);
221 }
222
223 AVFilter avfilter_vf_hue = {
224     .name        = "hue",
225     .description = NULL_IF_CONFIG_SMALL("Adjust the hue and saturation of the input video."),
226
227     .priv_size = sizeof(HueContext),
228
229     .init          = init,
230     .uninit        = uninit,
231     .query_formats = query_formats,
232
233     .inputs = (const AVFilterPad[]) {
234         {
235             .name         = "default",
236             .type         = AVMEDIA_TYPE_VIDEO,
237             .draw_slice   = draw_slice,
238             .config_props = config_props,
239             .min_perms    = AV_PERM_READ,
240         },
241         { .name = NULL }
242     },
243     .outputs = (const AVFilterPad[]) {
244         {
245             .name         = "default",
246             .type         = AVMEDIA_TYPE_VIDEO,
247         },
248         { .name = NULL }
249     }
250 };