2 * Copyright (c) 2013 Clément Bœsch
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 <float.h> /* DBL_MAX */
23 #include "libavutil/mem.h"
24 #include "libavutil/opt.h"
25 #include "libavutil/eval.h"
26 #include "libavutil/pixdesc.h"
31 static const char *const var_names
[] = {
35 "pts", // presentation timestamp expressed in AV_TIME_BASE units
37 "t", // timestamp expressed in seconds
59 typedef struct VignetteContext
{
61 const AVPixFmtDescriptor
*desc
;
63 int eval_mode
; ///< EvalMode
64 #define DEF_EXPR_FIELDS(name) AVExpr *name##_pexpr; char *name##_expr; double name
65 DEF_EXPR_FIELDS(angle
);
68 double var_values
[VAR_NB
];
79 #define OFFSET(x) offsetof(VignetteContext, x)
80 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
81 static const AVOption vignette_options
[] = {
82 { "angle", "set lens angle", OFFSET(angle_expr
), AV_OPT_TYPE_STRING
, {.str
="PI/5"}, .flags
= FLAGS
},
83 { "a", "set lens angle", OFFSET(angle_expr
), AV_OPT_TYPE_STRING
, {.str
="PI/5"}, .flags
= FLAGS
},
84 { "x0", "set circle center position on x-axis", OFFSET(x0_expr
), AV_OPT_TYPE_STRING
, {.str
="w/2"}, .flags
= FLAGS
},
85 { "y0", "set circle center position on y-axis", OFFSET(y0_expr
), AV_OPT_TYPE_STRING
, {.str
="h/2"}, .flags
= FLAGS
},
86 { "mode", "set forward/backward mode", OFFSET(backward
), AV_OPT_TYPE_INT
, {.i64
= 0}, 0, 1, FLAGS
, .unit
= "mode" },
87 { "forward", NULL
, 0, AV_OPT_TYPE_CONST
, {.i64
= 0}, INT_MIN
, INT_MAX
, FLAGS
, .unit
= "mode"},
88 { "backward", NULL
, 0, AV_OPT_TYPE_CONST
, {.i64
= 1}, INT_MIN
, INT_MAX
, FLAGS
, .unit
= "mode"},
89 { "eval", "specify when to evaluate expressions", OFFSET(eval_mode
), AV_OPT_TYPE_INT
, {.i64
= EVAL_MODE_INIT
}, 0, EVAL_MODE_NB
-1, FLAGS
, .unit
= "eval" },
90 { "init", "eval expressions once during initialization", 0, AV_OPT_TYPE_CONST
, {.i64
=EVAL_MODE_INIT
}, .flags
= FLAGS
, .unit
= "eval" },
91 { "frame", "eval expressions for each frame", 0, AV_OPT_TYPE_CONST
, {.i64
=EVAL_MODE_FRAME
}, .flags
= FLAGS
, .unit
= "eval" },
92 { "dither", "set dithering", OFFSET(do_dither
), AV_OPT_TYPE_BOOL
, {.i64
= 1}, 0, 1, FLAGS
},
93 { "aspect", "set aspect ratio", OFFSET(aspect
), AV_OPT_TYPE_RATIONAL
, {.dbl
= 1}, 0, DBL_MAX
, .flags
= FLAGS
},
97 AVFILTER_DEFINE_CLASS(vignette
);
99 static av_cold
int init(AVFilterContext
*ctx
)
101 VignetteContext
*s
= ctx
->priv
;
103 #define PARSE_EXPR(name) do { \
104 int ret = av_expr_parse(&s->name##_pexpr, s->name##_expr, var_names, \
105 NULL, NULL, NULL, NULL, 0, ctx); \
107 av_log(ctx, AV_LOG_ERROR, "Unable to parse expression for '" \
108 AV_STRINGIFY(name) "'\n"); \
119 static av_cold
void uninit(AVFilterContext
*ctx
)
121 VignetteContext
*s
= ctx
->priv
;
123 av_expr_free(s
->angle_pexpr
);
124 av_expr_free(s
->x0_pexpr
);
125 av_expr_free(s
->y0_pexpr
);
128 static const enum AVPixelFormat pix_fmts
[] = {
129 AV_PIX_FMT_YUV444P
, AV_PIX_FMT_YUV422P
,
130 AV_PIX_FMT_YUV420P
, AV_PIX_FMT_YUV411P
,
131 AV_PIX_FMT_YUV410P
, AV_PIX_FMT_YUV440P
,
132 AV_PIX_FMT_RGB24
, AV_PIX_FMT_BGR24
,
137 static double get_natural_factor(const VignetteContext
*s
, int x
, int y
)
139 const int xx
= (x
- s
->x0
) * s
->xscale
;
140 const int yy
= (y
- s
->y0
) * s
->yscale
;
141 const double dnorm
= hypot(xx
, yy
) / s
->dmax
;
145 const double c
= cos(s
->angle
* dnorm
);
146 return (c
*c
)*(c
*c
); // do not remove braces, it helps compilers
150 static void update_context(VignetteContext
*s
, AVFilterLink
*inlink
, AVFrame
*frame
)
152 FilterLink
*inl
= ff_filter_link(inlink
);
154 float *dst
= s
->fmap
;
155 int dst_linesize
= s
->fmap_linesize
;
158 s
->var_values
[VAR_N
] = inl
->frame_count_out
;
159 s
->var_values
[VAR_T
] = TS2T(frame
->pts
, inlink
->time_base
);
160 s
->var_values
[VAR_PTS
] = TS2D(frame
->pts
);
162 s
->var_values
[VAR_N
] = NAN
;
163 s
->var_values
[VAR_T
] = NAN
;
164 s
->var_values
[VAR_PTS
] = NAN
;
167 s
->angle
= av_expr_eval(s
->angle_pexpr
, s
->var_values
, NULL
);
168 s
->x0
= av_expr_eval(s
->x0_pexpr
, s
->var_values
, NULL
);
169 s
->y0
= av_expr_eval(s
->y0_pexpr
, s
->var_values
, NULL
);
171 if (isnan(s
->x0
) || isnan(s
->y0
) || isnan(s
->angle
))
172 s
->eval_mode
= EVAL_MODE_FRAME
;
174 s
->angle
= av_clipf(s
->angle
, 0, M_PI_2
);
177 for (y
= 0; y
< inlink
->h
; y
++) {
178 for (x
= 0; x
< inlink
->w
; x
++)
179 dst
[x
] = 1. / get_natural_factor(s
, x
, y
);
183 for (y
= 0; y
< inlink
->h
; y
++) {
184 for (x
= 0; x
< inlink
->w
; x
++)
185 dst
[x
] = get_natural_factor(s
, x
, y
);
191 static inline double get_dither_value(VignetteContext
*s
)
195 dv
= s
->dither
/ (double)(1LL<<32);
196 s
->dither
= s
->dither
* 1664525 + 1013904223;
201 static int filter_frame(AVFilterLink
*inlink
, AVFrame
*in
)
203 unsigned x
, y
, direct
= 0;
204 AVFilterContext
*ctx
= inlink
->dst
;
205 VignetteContext
*s
= ctx
->priv
;
206 AVFilterLink
*outlink
= ctx
->outputs
[0];
209 if (av_frame_is_writable(in
)) {
213 out
= ff_get_video_buffer(outlink
, outlink
->w
, outlink
->h
);
216 return AVERROR(ENOMEM
);
218 av_frame_copy_props(out
, in
);
221 if (s
->eval_mode
== EVAL_MODE_FRAME
)
222 update_context(s
, inlink
, in
);
224 if (s
->desc
->flags
& AV_PIX_FMT_FLAG_RGB
) {
225 uint8_t *dst
= out
->data
[0];
226 const uint8_t *src
= in
->data
[0];
227 const float *fmap
= s
->fmap
;
228 const int dst_linesize
= out
->linesize
[0];
229 const int src_linesize
= in
->linesize
[0];
230 const int fmap_linesize
= s
->fmap_linesize
;
232 for (y
= 0; y
< inlink
->h
; y
++) {
234 const uint8_t *srcp
= src
;
236 for (x
= 0; x
< inlink
->w
; x
++, dstp
+= 3, srcp
+= 3) {
237 const float f
= fmap
[x
];
239 dstp
[0] = av_clip_uint8(srcp
[0] * f
+ get_dither_value(s
));
240 dstp
[1] = av_clip_uint8(srcp
[1] * f
+ get_dither_value(s
));
241 dstp
[2] = av_clip_uint8(srcp
[2] * f
+ get_dither_value(s
));
245 fmap
+= fmap_linesize
;
250 for (plane
= 0; plane
< 4 && in
->data
[plane
] && in
->linesize
[plane
]; plane
++) {
251 uint8_t *dst
= out
->data
[plane
];
252 const uint8_t *src
= in
->data
[plane
];
253 const float *fmap
= s
->fmap
;
254 const int dst_linesize
= out
->linesize
[plane
];
255 const int src_linesize
= in
->linesize
[plane
];
256 const int fmap_linesize
= s
->fmap_linesize
;
257 const int chroma
= plane
== 1 || plane
== 2;
258 const int hsub
= chroma
? s
->desc
->log2_chroma_w
: 0;
259 const int vsub
= chroma
? s
->desc
->log2_chroma_h
: 0;
260 const int w
= AV_CEIL_RSHIFT(inlink
->w
, hsub
);
261 const int h
= AV_CEIL_RSHIFT(inlink
->h
, vsub
);
263 for (y
= 0; y
< h
; y
++) {
265 const uint8_t *srcp
= src
;
267 for (x
= 0; x
< w
; x
++) {
268 const double dv
= get_dither_value(s
);
269 if (chroma
) *dstp
++ = av_clip_uint8(fmap
[x
<< hsub
] * (*srcp
++ - 127) + 127 + dv
);
270 else *dstp
++ = av_clip_uint8(fmap
[x
] * *srcp
++ + dv
);
274 fmap
+= fmap_linesize
<< vsub
;
281 return ff_filter_frame(outlink
, out
);
284 static int config_props(AVFilterLink
*inlink
)
286 VignetteContext
*s
= inlink
->dst
->priv
;
287 FilterLink
*l
= ff_filter_link(inlink
);
288 AVRational sar
= inlink
->sample_aspect_ratio
;
290 s
->desc
= av_pix_fmt_desc_get(inlink
->format
);
291 s
->var_values
[VAR_W
] = inlink
->w
;
292 s
->var_values
[VAR_H
] = inlink
->h
;
293 s
->var_values
[VAR_TB
] = av_q2d(inlink
->time_base
);
294 s
->var_values
[VAR_R
] = l
->frame_rate
.num
== 0 || l
->frame_rate
.den
== 0 ?
295 NAN
: av_q2d(l
->frame_rate
);
297 if (!sar
.num
|| !sar
.den
)
298 sar
.num
= sar
.den
= 1;
299 if (sar
.num
> sar
.den
) {
300 s
->xscale
= av_q2d(av_div_q(sar
, s
->aspect
));
303 s
->yscale
= av_q2d(av_div_q(s
->aspect
, sar
));
306 s
->dmax
= hypot(inlink
->w
/ 2., inlink
->h
/ 2.);
307 av_log(s
, AV_LOG_DEBUG
, "xscale=%f yscale=%f dmax=%f\n",
308 s
->xscale
, s
->yscale
, s
->dmax
);
310 s
->fmap_linesize
= FFALIGN(inlink
->w
, 32);
311 s
->fmap
= av_malloc_array(s
->fmap_linesize
, inlink
->h
* sizeof(*s
->fmap
));
313 return AVERROR(ENOMEM
);
315 if (s
->eval_mode
== EVAL_MODE_INIT
)
316 update_context(s
, inlink
, NULL
);
321 static const AVFilterPad vignette_inputs
[] = {
324 .type
= AVMEDIA_TYPE_VIDEO
,
325 .filter_frame
= filter_frame
,
326 .config_props
= config_props
,
330 const FFFilter ff_vf_vignette
= {
331 .p
.name
= "vignette",
332 .p
.description
= NULL_IF_CONFIG_SMALL("Make or reverse a vignette effect."),
333 .p
.priv_class
= &vignette_class
,
334 .p
.flags
= AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC
,
335 .priv_size
= sizeof(VignetteContext
),
338 FILTER_INPUTS(vignette_inputs
),
339 FILTER_OUTPUTS(ff_video_default_filterpad
),
340 FILTER_PIXFMTS_ARRAY(pix_fmts
),