2 * Copyright (c) 2018 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"
33 typedef struct VibranceContext
{
45 int (*do_slice
)(AVFilterContext
*s
, void *arg
,
46 int jobnr
, int nb_jobs
);
49 static inline float lerpf(float v0
, float v1
, float f
)
51 return v0
+ (v1
- v0
) * f
;
54 typedef struct ThreadData
{
58 static int vibrance_slice8(AVFilterContext
*avctx
, void *arg
, int jobnr
, int nb_jobs
)
60 VibranceContext
*s
= avctx
->priv
;
62 AVFrame
*frame
= td
->out
;
64 const int width
= frame
->width
;
65 const int height
= frame
->height
;
66 const float scale
= 1.f
/ 255.f
;
67 const float gc
= s
->lcoeffs
[0];
68 const float bc
= s
->lcoeffs
[1];
69 const float rc
= s
->lcoeffs
[2];
70 const float intensity
= s
->intensity
;
71 const float alternate
= s
->alternate
? 1.f
: -1.f
;
72 const float gintensity
= intensity
* s
->balance
[0];
73 const float bintensity
= intensity
* s
->balance
[1];
74 const float rintensity
= intensity
* s
->balance
[2];
75 const float sgintensity
= alternate
* FFSIGN(gintensity
);
76 const float sbintensity
= alternate
* FFSIGN(bintensity
);
77 const float srintensity
= alternate
* FFSIGN(rintensity
);
78 const int slice_start
= (height
* jobnr
) / nb_jobs
;
79 const int slice_end
= (height
* (jobnr
+ 1)) / nb_jobs
;
80 const ptrdiff_t glinesize
= frame
->linesize
[0];
81 const ptrdiff_t blinesize
= frame
->linesize
[1];
82 const ptrdiff_t rlinesize
= frame
->linesize
[2];
83 const ptrdiff_t alinesize
= frame
->linesize
[3];
84 const ptrdiff_t gslinesize
= in
->linesize
[0];
85 const ptrdiff_t bslinesize
= in
->linesize
[1];
86 const ptrdiff_t rslinesize
= in
->linesize
[2];
87 const ptrdiff_t aslinesize
= in
->linesize
[3];
88 const uint8_t *gsrc
= in
->data
[0] + slice_start
* glinesize
;
89 const uint8_t *bsrc
= in
->data
[1] + slice_start
* blinesize
;
90 const uint8_t *rsrc
= in
->data
[2] + slice_start
* rlinesize
;
91 uint8_t *gptr
= frame
->data
[0] + slice_start
* glinesize
;
92 uint8_t *bptr
= frame
->data
[1] + slice_start
* blinesize
;
93 uint8_t *rptr
= frame
->data
[2] + slice_start
* rlinesize
;
94 const uint8_t *asrc
= in
->data
[3];
95 uint8_t *aptr
= frame
->data
[3];
97 for (int y
= slice_start
; y
< slice_end
; y
++) {
98 for (int x
= 0; x
< width
; x
++) {
99 float g
= gsrc
[x
] * scale
;
100 float b
= bsrc
[x
] * scale
;
101 float r
= rsrc
[x
] * scale
;
102 float max_color
= FFMAX3(r
, g
, b
);
103 float min_color
= FFMIN3(r
, g
, b
);
104 float color_saturation
= max_color
- min_color
;
105 float luma
= g
* gc
+ r
* rc
+ b
* bc
;
106 const float cg
= 1.f
+ gintensity
* (1.f
- sgintensity
* color_saturation
);
107 const float cb
= 1.f
+ bintensity
* (1.f
- sbintensity
* color_saturation
);
108 const float cr
= 1.f
+ rintensity
* (1.f
- srintensity
* color_saturation
);
110 g
= lerpf(luma
, g
, cg
);
111 b
= lerpf(luma
, b
, cb
);
112 r
= lerpf(luma
, r
, cr
);
114 gptr
[x
] = av_clip_uint8(g
* 255.f
);
115 bptr
[x
] = av_clip_uint8(b
* 255.f
);
116 rptr
[x
] = av_clip_uint8(r
* 255.f
);
119 if (aptr
&& alinesize
&& frame
!= in
)
120 memcpy(aptr
+ alinesize
* y
, asrc
+ aslinesize
* y
, width
);
133 static int vibrance_slice16(AVFilterContext
*avctx
, void *arg
, int jobnr
, int nb_jobs
)
135 VibranceContext
*s
= avctx
->priv
;
136 ThreadData
*td
= arg
;
137 AVFrame
*frame
= td
->out
;
138 AVFrame
*in
= td
->in
;
139 const int depth
= s
->depth
;
140 const float max
= (1 << depth
) - 1;
141 const float scale
= 1.f
/ max
;
142 const float gc
= s
->lcoeffs
[0];
143 const float bc
= s
->lcoeffs
[1];
144 const float rc
= s
->lcoeffs
[2];
145 const int width
= frame
->width
;
146 const int height
= frame
->height
;
147 const float intensity
= s
->intensity
;
148 const float alternate
= s
->alternate
? 1.f
: -1.f
;
149 const float gintensity
= intensity
* s
->balance
[0];
150 const float bintensity
= intensity
* s
->balance
[1];
151 const float rintensity
= intensity
* s
->balance
[2];
152 const float sgintensity
= alternate
* FFSIGN(gintensity
);
153 const float sbintensity
= alternate
* FFSIGN(bintensity
);
154 const float srintensity
= alternate
* FFSIGN(rintensity
);
155 const int slice_start
= (height
* jobnr
) / nb_jobs
;
156 const int slice_end
= (height
* (jobnr
+ 1)) / nb_jobs
;
157 const ptrdiff_t gslinesize
= in
->linesize
[0] / 2;
158 const ptrdiff_t bslinesize
= in
->linesize
[1] / 2;
159 const ptrdiff_t rslinesize
= in
->linesize
[2] / 2;
160 const ptrdiff_t aslinesize
= in
->linesize
[3] / 2;
161 const ptrdiff_t glinesize
= frame
->linesize
[0] / 2;
162 const ptrdiff_t blinesize
= frame
->linesize
[1] / 2;
163 const ptrdiff_t rlinesize
= frame
->linesize
[2] / 2;
164 const ptrdiff_t alinesize
= frame
->linesize
[3] / 2;
165 const uint16_t *gsrc
= (const uint16_t *)in
->data
[0] + slice_start
* gslinesize
;
166 const uint16_t *bsrc
= (const uint16_t *)in
->data
[1] + slice_start
* bslinesize
;
167 const uint16_t *rsrc
= (const uint16_t *)in
->data
[2] + slice_start
* rslinesize
;
168 uint16_t *gptr
= (uint16_t *)frame
->data
[0] + slice_start
* glinesize
;
169 uint16_t *bptr
= (uint16_t *)frame
->data
[1] + slice_start
* blinesize
;
170 uint16_t *rptr
= (uint16_t *)frame
->data
[2] + slice_start
* rlinesize
;
171 const uint16_t *asrc
= (const uint16_t *)in
->data
[3];
172 uint16_t *aptr
= (uint16_t *)frame
->data
[3];
174 for (int y
= slice_start
; y
< slice_end
; y
++) {
175 for (int x
= 0; x
< width
; x
++) {
176 float g
= gsrc
[x
] * scale
;
177 float b
= bsrc
[x
] * scale
;
178 float r
= rsrc
[x
] * scale
;
179 float max_color
= FFMAX3(r
, g
, b
);
180 float min_color
= FFMIN3(r
, g
, b
);
181 float color_saturation
= max_color
- min_color
;
182 float luma
= g
* gc
+ r
* rc
+ b
* bc
;
183 const float cg
= 1.f
+ gintensity
* (1.f
- sgintensity
* color_saturation
);
184 const float cb
= 1.f
+ bintensity
* (1.f
- sbintensity
* color_saturation
);
185 const float cr
= 1.f
+ rintensity
* (1.f
- srintensity
* color_saturation
);
187 g
= lerpf(luma
, g
, cg
);
188 b
= lerpf(luma
, b
, cb
);
189 r
= lerpf(luma
, r
, cr
);
191 gptr
[x
] = av_clip_uintp2_c(g
* max
, depth
);
192 bptr
[x
] = av_clip_uintp2_c(b
* max
, depth
);
193 rptr
[x
] = av_clip_uintp2_c(r
* max
, depth
);
196 if (aptr
&& alinesize
&& frame
!= in
)
197 memcpy(aptr
+ alinesize
* y
, asrc
+ aslinesize
* y
, width
* 2);
210 static int vibrance_slice8p(AVFilterContext
*avctx
, void *arg
, int jobnr
, int nb_jobs
)
212 VibranceContext
*s
= avctx
->priv
;
213 ThreadData
*td
= arg
;
214 AVFrame
*frame
= td
->out
;
215 AVFrame
*in
= td
->in
;
216 const int step
= s
->step
;
217 const int width
= frame
->width
;
218 const int height
= frame
->height
;
219 const float scale
= 1.f
/ 255.f
;
220 const float gc
= s
->lcoeffs
[0];
221 const float bc
= s
->lcoeffs
[1];
222 const float rc
= s
->lcoeffs
[2];
223 const uint8_t roffset
= s
->rgba_map
[R
];
224 const uint8_t goffset
= s
->rgba_map
[G
];
225 const uint8_t boffset
= s
->rgba_map
[B
];
226 const uint8_t aoffset
= s
->rgba_map
[A
];
227 const float intensity
= s
->intensity
;
228 const float alternate
= s
->alternate
? 1.f
: -1.f
;
229 const float gintensity
= intensity
* s
->balance
[0];
230 const float bintensity
= intensity
* s
->balance
[1];
231 const float rintensity
= intensity
* s
->balance
[2];
232 const float sgintensity
= alternate
* FFSIGN(gintensity
);
233 const float sbintensity
= alternate
* FFSIGN(bintensity
);
234 const float srintensity
= alternate
* FFSIGN(rintensity
);
235 const int slice_start
= (height
* jobnr
) / nb_jobs
;
236 const int slice_end
= (height
* (jobnr
+ 1)) / nb_jobs
;
237 const ptrdiff_t linesize
= frame
->linesize
[0];
238 const ptrdiff_t slinesize
= in
->linesize
[0];
239 const uint8_t *src
= in
->data
[0] + slice_start
* slinesize
;
240 uint8_t *ptr
= frame
->data
[0] + slice_start
* linesize
;
242 for (int y
= slice_start
; y
< slice_end
; y
++) {
243 for (int x
= 0; x
< width
; x
++) {
244 float g
= src
[x
* step
+ goffset
] * scale
;
245 float b
= src
[x
* step
+ boffset
] * scale
;
246 float r
= src
[x
* step
+ roffset
] * scale
;
247 float max_color
= FFMAX3(r
, g
, b
);
248 float min_color
= FFMIN3(r
, g
, b
);
249 float color_saturation
= max_color
- min_color
;
250 float luma
= g
* gc
+ r
* rc
+ b
* bc
;
251 const float cg
= 1.f
+ gintensity
* (1.f
- sgintensity
* color_saturation
);
252 const float cb
= 1.f
+ bintensity
* (1.f
- sbintensity
* color_saturation
);
253 const float cr
= 1.f
+ rintensity
* (1.f
- srintensity
* color_saturation
);
255 g
= lerpf(luma
, g
, cg
);
256 b
= lerpf(luma
, b
, cb
);
257 r
= lerpf(luma
, r
, cr
);
259 ptr
[x
* step
+ goffset
] = av_clip_uint8(g
* 255.f
);
260 ptr
[x
* step
+ boffset
] = av_clip_uint8(b
* 255.f
);
261 ptr
[x
* step
+ roffset
] = av_clip_uint8(r
* 255.f
);
264 ptr
[x
* step
+ aoffset
] = src
[x
* step
+ aoffset
];
274 static int vibrance_slice16p(AVFilterContext
*avctx
, void *arg
, int jobnr
, int nb_jobs
)
276 VibranceContext
*s
= avctx
->priv
;
277 ThreadData
*td
= arg
;
278 AVFrame
*frame
= td
->out
;
279 AVFrame
*in
= td
->in
;
280 const int step
= s
->step
;
281 const int depth
= s
->depth
;
282 const float max
= (1 << depth
) - 1;
283 const float scale
= 1.f
/ max
;
284 const float gc
= s
->lcoeffs
[0];
285 const float bc
= s
->lcoeffs
[1];
286 const float rc
= s
->lcoeffs
[2];
287 const uint8_t roffset
= s
->rgba_map
[R
];
288 const uint8_t goffset
= s
->rgba_map
[G
];
289 const uint8_t boffset
= s
->rgba_map
[B
];
290 const uint8_t aoffset
= s
->rgba_map
[A
];
291 const int width
= frame
->width
;
292 const int height
= frame
->height
;
293 const float intensity
= s
->intensity
;
294 const float alternate
= s
->alternate
? 1.f
: -1.f
;
295 const float gintensity
= intensity
* s
->balance
[0];
296 const float bintensity
= intensity
* s
->balance
[1];
297 const float rintensity
= intensity
* s
->balance
[2];
298 const float sgintensity
= alternate
* FFSIGN(gintensity
);
299 const float sbintensity
= alternate
* FFSIGN(bintensity
);
300 const float srintensity
= alternate
* FFSIGN(rintensity
);
301 const int slice_start
= (height
* jobnr
) / nb_jobs
;
302 const int slice_end
= (height
* (jobnr
+ 1)) / nb_jobs
;
303 const ptrdiff_t linesize
= frame
->linesize
[0] / 2;
304 const ptrdiff_t slinesize
= in
->linesize
[0] / 2;
305 const uint16_t *src
= (const uint16_t *)in
->data
[0] + slice_start
* slinesize
;
306 uint16_t *ptr
= (uint16_t *)frame
->data
[0] + slice_start
* linesize
;
308 for (int y
= slice_start
; y
< slice_end
; y
++) {
309 for (int x
= 0; x
< width
; x
++) {
310 float g
= src
[x
* step
+ goffset
] * scale
;
311 float b
= src
[x
* step
+ boffset
] * scale
;
312 float r
= src
[x
* step
+ roffset
] * scale
;
313 float max_color
= FFMAX3(r
, g
, b
);
314 float min_color
= FFMIN3(r
, g
, b
);
315 float color_saturation
= max_color
- min_color
;
316 float luma
= g
* gc
+ r
* rc
+ b
* bc
;
317 const float cg
= 1.f
+ gintensity
* (1.f
- sgintensity
* color_saturation
);
318 const float cb
= 1.f
+ bintensity
* (1.f
- sbintensity
* color_saturation
);
319 const float cr
= 1.f
+ rintensity
* (1.f
- srintensity
* color_saturation
);
321 g
= lerpf(luma
, g
, cg
);
322 b
= lerpf(luma
, b
, cb
);
323 r
= lerpf(luma
, r
, cr
);
325 ptr
[x
* step
+ goffset
] = av_clip_uintp2_c(g
* max
, depth
);
326 ptr
[x
* step
+ boffset
] = av_clip_uintp2_c(b
* max
, depth
);
327 ptr
[x
* step
+ roffset
] = av_clip_uintp2_c(r
* max
, depth
);
329 ptr
[x
* step
+ aoffset
] = src
[x
* step
+ aoffset
];
339 static int filter_frame(AVFilterLink
*link
, AVFrame
*in
)
341 AVFilterContext
*avctx
= link
->dst
;
342 AVFilterLink
*outlink
= avctx
->outputs
[0];
343 VibranceContext
*s
= avctx
->priv
;
348 if (av_frame_is_writable(in
)) {
351 out
= ff_get_video_buffer(outlink
, outlink
->w
, outlink
->h
);
354 return AVERROR(ENOMEM
);
356 av_frame_copy_props(out
, in
);
361 if (res
= ff_filter_execute(avctx
, s
->do_slice
, &td
, NULL
,
362 FFMIN(out
->height
, ff_filter_get_nb_threads(avctx
))))
367 return ff_filter_frame(outlink
, out
);
370 static const enum AVPixelFormat pixel_fmts
[] = {
371 AV_PIX_FMT_RGB24
, AV_PIX_FMT_BGR24
,
372 AV_PIX_FMT_RGBA
, AV_PIX_FMT_BGRA
,
373 AV_PIX_FMT_ARGB
, AV_PIX_FMT_ABGR
,
374 AV_PIX_FMT_0RGB
, AV_PIX_FMT_0BGR
,
375 AV_PIX_FMT_RGB0
, AV_PIX_FMT_BGR0
,
376 AV_PIX_FMT_GBRP
, AV_PIX_FMT_GBRAP
,
377 AV_PIX_FMT_GBRP9
, AV_PIX_FMT_GBRP10
, AV_PIX_FMT_GBRP12
,
378 AV_PIX_FMT_GBRP14
, AV_PIX_FMT_GBRP16
,
379 AV_PIX_FMT_GBRAP10
, AV_PIX_FMT_GBRAP12
, AV_PIX_FMT_GBRAP16
,
380 AV_PIX_FMT_RGB48
, AV_PIX_FMT_BGR48
,
381 AV_PIX_FMT_RGBA64
, AV_PIX_FMT_BGRA64
,
385 static av_cold
int config_input(AVFilterLink
*inlink
)
387 AVFilterContext
*avctx
= inlink
->dst
;
388 VibranceContext
*s
= avctx
->priv
;
389 const AVPixFmtDescriptor
*desc
= av_pix_fmt_desc_get(inlink
->format
);
390 int planar
= desc
->flags
& AV_PIX_FMT_FLAG_PLANAR
;
392 s
->step
= desc
->nb_components
;
393 if (inlink
->format
== AV_PIX_FMT_RGB0
||
394 inlink
->format
== AV_PIX_FMT_0RGB
||
395 inlink
->format
== AV_PIX_FMT_BGR0
||
396 inlink
->format
== AV_PIX_FMT_0BGR
)
399 s
->depth
= desc
->comp
[0].depth
;
400 s
->do_slice
= s
->depth
<= 8 ? vibrance_slice8
: vibrance_slice16
;
402 s
->do_slice
= s
->depth
<= 8 ? vibrance_slice8p
: vibrance_slice16p
;
404 ff_fill_rgba_map(s
->rgba_map
, inlink
->format
);
409 static const AVFilterPad vibrance_inputs
[] = {
412 .type
= AVMEDIA_TYPE_VIDEO
,
413 .filter_frame
= filter_frame
,
414 .config_props
= config_input
,
418 #define OFFSET(x) offsetof(VibranceContext, x)
419 #define VF AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_RUNTIME_PARAM
421 static const AVOption vibrance_options
[] = {
422 { "intensity", "set the intensity value", OFFSET(intensity
), AV_OPT_TYPE_FLOAT
, {.dbl
=0}, -2, 2, VF
},
423 { "rbal", "set the red balance value", OFFSET(balance
[2]), AV_OPT_TYPE_FLOAT
, {.dbl
=1}, -10, 10, VF
},
424 { "gbal", "set the green balance value", OFFSET(balance
[0]), AV_OPT_TYPE_FLOAT
, {.dbl
=1}, -10, 10, VF
},
425 { "bbal", "set the blue balance value", OFFSET(balance
[1]), AV_OPT_TYPE_FLOAT
, {.dbl
=1}, -10, 10, VF
},
426 { "rlum", "set the red luma coefficient", OFFSET(lcoeffs
[2]), AV_OPT_TYPE_FLOAT
, {.dbl
=0.072186}, 0, 1, VF
},
427 { "glum", "set the green luma coefficient", OFFSET(lcoeffs
[0]), AV_OPT_TYPE_FLOAT
, {.dbl
=0.715158}, 0, 1, VF
},
428 { "blum", "set the blue luma coefficient", OFFSET(lcoeffs
[1]), AV_OPT_TYPE_FLOAT
, {.dbl
=0.212656}, 0, 1, VF
},
429 { "alternate", "use alternate colors", OFFSET(alternate
), AV_OPT_TYPE_BOOL
, {.i64
=0}, 0, 1, VF
},
433 AVFILTER_DEFINE_CLASS(vibrance
);
435 const FFFilter ff_vf_vibrance
= {
436 .p
.name
= "vibrance",
437 .p
.description
= NULL_IF_CONFIG_SMALL("Boost or alter saturation."),
438 .p
.priv_class
= &vibrance_class
,
439 .p
.flags
= AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC
| AVFILTER_FLAG_SLICE_THREADS
,
440 .priv_size
= sizeof(VibranceContext
),
441 FILTER_INPUTS(vibrance_inputs
),
442 FILTER_OUTPUTS(ff_video_default_filterpad
),
443 FILTER_PIXFMTS_ARRAY(pixel_fmts
),
444 .process_command
= ff_filter_process_command
,