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/opt.h"
24 #include "libavutil/eval.h"
25 #include "libavutil/avassert.h"
26 #include "libavutil/pixdesc.h"
32 static const char *const var_names
[] = {
36 "pts", // presentation timestamp expressed in AV_TIME_BASE units
38 "t", // timestamp expressed in seconds
56 const AVPixFmtDescriptor
*desc
;
58 enum EvalMode
{ EVAL_MODE_INIT
, EVAL_MODE_FRAME
, EVAL_MODE_NB
} eval_mode
;
59 #define DEF_EXPR_FIELDS(name) AVExpr *name##_pexpr; char *name##_expr; double name
60 DEF_EXPR_FIELDS(angle
);
63 double var_values
[VAR_NB
];
74 #define OFFSET(x) offsetof(VignetteContext, x)
75 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
76 static const AVOption vignette_options
[] = {
77 { "angle", "set lens angle", OFFSET(angle_expr
), AV_OPT_TYPE_STRING
, {.str
="PI/5"}, .flags
= FLAGS
},
78 { "a", "set lens angle", OFFSET(angle_expr
), AV_OPT_TYPE_STRING
, {.str
="PI/5"}, .flags
= FLAGS
},
79 { "x0", "set circle center position on x-axis", OFFSET(x0_expr
), AV_OPT_TYPE_STRING
, {.str
="w/2"}, .flags
= FLAGS
},
80 { "y0", "set circle center position on y-axis", OFFSET(y0_expr
), AV_OPT_TYPE_STRING
, {.str
="h/2"}, .flags
= FLAGS
},
81 { "mode", "set forward/backward mode", OFFSET(backward
), AV_OPT_TYPE_INT
, {.i64
= 0}, 0, 1, FLAGS
, "mode" },
82 { "forward", NULL
, 0, AV_OPT_TYPE_CONST
, {.i64
= 0}, INT_MIN
, INT_MAX
, FLAGS
, "mode"},
83 { "backward", NULL
, 0, AV_OPT_TYPE_CONST
, {.i64
= 1}, INT_MIN
, INT_MAX
, FLAGS
, "mode"},
84 { "eval", "specify when to evaluate expressions", OFFSET(eval_mode
), AV_OPT_TYPE_INT
, {.i64
= EVAL_MODE_INIT
}, 0, EVAL_MODE_NB
-1, FLAGS
, "eval" },
85 { "init", "eval expressions once during initialization", 0, AV_OPT_TYPE_CONST
, {.i64
=EVAL_MODE_INIT
}, .flags
= FLAGS
, .unit
= "eval" },
86 { "frame", "eval expressions for each frame", 0, AV_OPT_TYPE_CONST
, {.i64
=EVAL_MODE_FRAME
}, .flags
= FLAGS
, .unit
= "eval" },
87 { "dither", "set dithering", OFFSET(do_dither
), AV_OPT_TYPE_INT
, {.i64
= 1}, 0, 1, FLAGS
},
88 { "aspect", "set aspect ratio", OFFSET(aspect
), AV_OPT_TYPE_RATIONAL
, {.dbl
= 1}, 0, DBL_MAX
, .flags
= FLAGS
},
92 AVFILTER_DEFINE_CLASS(vignette
);
94 static av_cold
int init(AVFilterContext
*ctx
)
96 VignetteContext
*s
= ctx
->priv
;
98 #define PARSE_EXPR(name) do { \
99 int ret = av_expr_parse(&s->name##_pexpr, s->name##_expr, var_names, \
100 NULL, NULL, NULL, NULL, 0, ctx); \
102 av_log(ctx, AV_LOG_ERROR, "Unable to parse expression for '" \
103 AV_STRINGIFY(name) "'\n"); \
114 static av_cold
void uninit(AVFilterContext
*ctx
)
116 VignetteContext
*s
= ctx
->priv
;
118 av_expr_free(s
->angle_pexpr
);
119 av_expr_free(s
->x0_pexpr
);
120 av_expr_free(s
->y0_pexpr
);
123 static int query_formats(AVFilterContext
*ctx
)
125 static const enum AVPixelFormat pix_fmts
[] = {
126 AV_PIX_FMT_YUV444P
, AV_PIX_FMT_YUV422P
,
127 AV_PIX_FMT_YUV420P
, AV_PIX_FMT_YUV411P
,
128 AV_PIX_FMT_YUV410P
, AV_PIX_FMT_YUV440P
,
129 AV_PIX_FMT_RGB24
, AV_PIX_FMT_BGR24
,
133 ff_set_common_formats(ctx
, ff_make_format_list(pix_fmts
));
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 #define TS2D(ts) ((ts) == AV_NOPTS_VALUE ? NAN : (double)(ts))
151 #define TS2T(ts, tb) ((ts) == AV_NOPTS_VALUE ? NAN : (double)(ts) * av_q2d(tb))
153 static void update_context(VignetteContext
*s
, AVFilterLink
*inlink
, AVFrame
*frame
)
156 float *dst
= s
->fmap
;
157 int dst_linesize
= s
->fmap_linesize
;
160 s
->var_values
[VAR_N
] = inlink
->frame_count
;
161 s
->var_values
[VAR_T
] = TS2T(frame
->pts
, inlink
->time_base
);
162 s
->var_values
[VAR_PTS
] = TS2D(frame
->pts
);
164 s
->var_values
[VAR_N
] = 0;
165 s
->var_values
[VAR_T
] = NAN
;
166 s
->var_values
[VAR_PTS
] = NAN
;
169 s
->angle
= av_clipf(av_expr_eval(s
->angle_pexpr
, s
->var_values
, NULL
), 0, M_PI_2
);
170 s
->x0
= av_expr_eval(s
->x0_pexpr
, s
->var_values
, NULL
);
171 s
->y0
= av_expr_eval(s
->y0_pexpr
, s
->var_values
, NULL
);
174 for (y
= 0; y
< inlink
->h
; y
++) {
175 for (x
= 0; x
< inlink
->w
; x
++)
176 dst
[x
] = 1. / get_natural_factor(s
, x
, y
);
180 for (y
= 0; y
< inlink
->h
; y
++) {
181 for (x
= 0; x
< inlink
->w
; x
++)
182 dst
[x
] = get_natural_factor(s
, x
, y
);
188 static inline double get_dither_value(VignetteContext
*s
)
192 dv
= s
->dither
/ (double)(1LL<<32);
193 s
->dither
= s
->dither
* 1664525 + 1013904223;
198 static int filter_frame(AVFilterLink
*inlink
, AVFrame
*in
)
200 unsigned x
, y
, direct
= 0;
201 AVFilterContext
*ctx
= inlink
->dst
;
202 VignetteContext
*s
= ctx
->priv
;
203 AVFilterLink
*outlink
= ctx
->outputs
[0];
206 if (av_frame_is_writable(in
)) {
210 out
= ff_get_video_buffer(outlink
, outlink
->w
, outlink
->h
);
213 return AVERROR(ENOMEM
);
215 av_frame_copy_props(out
, in
);
218 if (s
->eval_mode
== EVAL_MODE_FRAME
)
219 update_context(s
, inlink
, in
);
221 if (s
->desc
->flags
& AV_PIX_FMT_FLAG_RGB
) {
222 uint8_t *dst
= out
->data
[0];
223 const uint8_t *src
= in
->data
[0];
224 const float *fmap
= s
->fmap
;
225 const int dst_linesize
= out
->linesize
[0];
226 const int src_linesize
= in
->linesize
[0];
227 const int fmap_linesize
= s
->fmap_linesize
;
229 for (y
= 0; y
< inlink
->h
; y
++) {
231 const uint8_t *srcp
= src
;
233 for (x
= 0; x
< inlink
->w
; x
++, dstp
+= 3, srcp
+= 3) {
234 const float f
= fmap
[x
];
236 dstp
[0] = av_clip_uint8(srcp
[0] * f
+ get_dither_value(s
));
237 dstp
[1] = av_clip_uint8(srcp
[1] * f
+ get_dither_value(s
));
238 dstp
[2] = av_clip_uint8(srcp
[2] * f
+ get_dither_value(s
));
242 fmap
+= fmap_linesize
;
247 for (plane
= 0; plane
< 4 && in
->data
[plane
] && in
->linesize
[plane
]; plane
++) {
248 uint8_t *dst
= out
->data
[plane
];
249 const uint8_t *src
= in
->data
[plane
];
250 const float *fmap
= s
->fmap
;
251 const int dst_linesize
= out
->linesize
[plane
];
252 const int src_linesize
= in
->linesize
[plane
];
253 const int fmap_linesize
= s
->fmap_linesize
;
254 const int chroma
= plane
== 1 || plane
== 2;
255 const int hsub
= chroma
? s
->desc
->log2_chroma_w
: 0;
256 const int vsub
= chroma
? s
->desc
->log2_chroma_h
: 0;
257 const int w
= FF_CEIL_RSHIFT(inlink
->w
, hsub
);
258 const int h
= FF_CEIL_RSHIFT(inlink
->h
, vsub
);
260 for (y
= 0; y
< h
; y
++) {
262 const uint8_t *srcp
= src
;
264 for (x
= 0; x
< w
; x
++) {
265 const double dv
= get_dither_value(s
);
266 if (chroma
) *dstp
++ = av_clip_uint8(fmap
[x
<< hsub
] * (*srcp
++ - 127) + 127 + dv
);
267 else *dstp
++ = av_clip_uint8(fmap
[x
] * *srcp
++ + dv
);
271 fmap
+= fmap_linesize
<< vsub
;
278 return ff_filter_frame(outlink
, out
);
281 static int config_props(AVFilterLink
*inlink
)
283 VignetteContext
*s
= inlink
->dst
->priv
;
284 AVRational sar
= inlink
->sample_aspect_ratio
;
286 s
->desc
= av_pix_fmt_desc_get(inlink
->format
);
287 s
->var_values
[VAR_W
] = inlink
->w
;
288 s
->var_values
[VAR_H
] = inlink
->h
;
289 s
->var_values
[VAR_TB
] = av_q2d(inlink
->time_base
);
290 s
->var_values
[VAR_R
] = inlink
->frame_rate
.num
== 0 || inlink
->frame_rate
.den
== 0 ?
291 NAN
: av_q2d(inlink
->frame_rate
);
293 if (!sar
.num
|| !sar
.den
)
294 sar
.num
= sar
.den
= 1;
295 if (sar
.num
> sar
.den
) {
296 s
->xscale
= av_q2d(av_div_q(sar
, s
->aspect
));
299 s
->yscale
= av_q2d(av_div_q(s
->aspect
, sar
));
302 s
->dmax
= hypot(inlink
->w
/ 2., inlink
->h
/ 2.);
303 av_log(s
, AV_LOG_DEBUG
, "xscale=%f yscale=%f dmax=%f\n",
304 s
->xscale
, s
->yscale
, s
->dmax
);
306 s
->fmap_linesize
= FFALIGN(inlink
->w
, 32);
307 s
->fmap
= av_malloc_array(s
->fmap_linesize
, inlink
->h
* sizeof(*s
->fmap
));
309 return AVERROR(ENOMEM
);
311 if (s
->eval_mode
== EVAL_MODE_INIT
)
312 update_context(s
, inlink
, NULL
);
317 static const AVFilterPad vignette_inputs
[] = {
320 .type
= AVMEDIA_TYPE_VIDEO
,
321 .filter_frame
= filter_frame
,
322 .config_props
= config_props
,
327 static const AVFilterPad vignette_outputs
[] = {
330 .type
= AVMEDIA_TYPE_VIDEO
,
335 AVFilter ff_vf_vignette
= {
337 .description
= NULL_IF_CONFIG_SMALL("Make or reverse a vignette effect."),
338 .priv_size
= sizeof(VignetteContext
),
341 .query_formats
= query_formats
,
342 .inputs
= vignette_inputs
,
343 .outputs
= vignette_outputs
,
344 .priv_class
= &vignette_class
,
345 .flags
= AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC
,