Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in / Register
Toggle navigation
F
ffmpeg.wasm-core
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Linshizhi
ffmpeg.wasm-core
Commits
308f9b1c
Commit
308f9b1c
authored
Jul 14, 2016
by
Clément Bœsch
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
lavfi/selectivecolor: add 16-bit support
parent
b8aaedcd
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
162 additions
and
111 deletions
+162
-111
Changelog
Changelog
+1
-0
version.h
libavfilter/version.h
+1
-1
vf_selectivecolor.c
libavfilter/vf_selectivecolor.c
+160
-110
No files found.
Changelog
View file @
308f9b1c
...
...
@@ -9,6 +9,7 @@ version <next>:
- VP8 in Ogg muxing
- curves filter doesn't automatically insert points at x=0 and x=1 anymore
- 16-bit support in curves filter
- 16-bit support in selectivecolor filter
version 3.1:
...
...
libavfilter/version.h
View file @
308f9b1c
...
...
@@ -30,7 +30,7 @@
#include "libavutil/version.h"
#define LIBAVFILTER_VERSION_MAJOR 6
#define LIBAVFILTER_VERSION_MINOR 4
8
#define LIBAVFILTER_VERSION_MINOR 4
9
#define LIBAVFILTER_VERSION_MICRO 100
#define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \
...
...
libavfilter/vf_selectivecolor.c
View file @
308f9b1c
/*
* Copyright (c) 2015 Clément Bœsch <u pkh me>
* Copyright (c) 2015
-2016
Clément Bœsch <u pkh me>
*
* This file is part of FFmpeg.
*
...
...
@@ -21,18 +21,24 @@
/**
* @todo
* - use integers so it can be made bitexact and a FATE test can be added
* - >8 bit support
*/
#include "libavutil/avassert.h"
#include "libavutil/file.h"
#include "libavutil/intreadwrite.h"
#include "libavutil/opt.h"
#include "libavutil/pixdesc.h"
#include "avfilter.h"
#include "drawutils.h"
#include "formats.h"
#include "internal.h"
#include "video.h"
#define R 0
#define G 1
#define B 2
#define A 3
enum
color_range
{
// WARNING: do NOT reorder (see parse_psfile())
RANGE_REDS
,
...
...
@@ -77,6 +83,9 @@ typedef struct {
struct
process_range
process_ranges
[
NB_RANGES
];
// color ranges to process
int
nb_process_ranges
;
char
*
psfile
;
uint8_t
rgba_map
[
4
];
int
is_16bit
;
int
step
;
}
SelectiveColorContext
;
#define OFFSET(x) offsetof(SelectiveColorContext, x)
...
...
@@ -141,23 +150,28 @@ static int get_cmy_adjust_range(int r, int g, int b, int min_val, int max_val)
return
mid_val
-
min_val
;
}
static
int
get_neutrals_adjust_range
(
int
r
,
int
g
,
int
b
,
int
min_val
,
int
max_val
)
{
// 1 - (|max-0.5| + |min-0.5|)
return
(
255
*
2
-
(
abs
((
max_val
<<
1
)
-
255
)
+
abs
((
min_val
<<
1
)
-
255
))
+
1
)
>>
1
;
}
static
int
get_whites_adjust_range
(
int
r
,
int
g
,
int
b
,
int
min_val
,
int
max_val
)
{
// (min - 0.5) * 2
return
(
min_val
<<
1
)
-
255
;
}
#define DECLARE_ADJUST_RANGE_FUNCS(nbits) \
static int get_neutrals_adjust_range##nbits(int r, int g, int b, int min_val, int max_val) \
{ \
/* 1 - (|max-0.5| + |min-0.5|) */
\
return (((1<<nbits)-1)*2 - ( abs((max_val<<1) - ((1<<nbits)-1)) \
+ abs((min_val<<1) - ((1<<nbits)-1))) + 1) >> 1; \
} \
\
static int get_whites_adjust_range##nbits(int r, int g, int b, int min_val, int max_val) \
{ \
/* (min - 0.5) * 2 */
\
return (min_val<<1) - ((1<<nbits)-1); \
} \
\
static int get_blacks_adjust_range##nbits(int r, int g, int b, int min_val, int max_val) \
{ \
/* (0.5 - max) * 2 */
\
return ((1<<nbits)-1) - (max_val<<1); \
} \
static
int
get_blacks_adjust_range
(
int
r
,
int
g
,
int
b
,
int
min_val
,
int
max_val
)
{
// (0.5 - max) * 2
return
255
-
(
max_val
<<
1
);
}
DECLARE_ADJUST_RANGE_FUNCS
(
8
)
DECLARE_ADJUST_RANGE_FUNCS
(
16
)
static
int
register_range
(
SelectiveColorContext
*
s
,
int
range_id
)
{
...
...
@@ -182,9 +196,12 @@ static int register_range(SelectiveColorContext *s, int range_id)
pr
->
mask
=
1
<<
range_id
;
if
(
pr
->
mask
&
(
1
<<
RANGE_REDS
|
1
<<
RANGE_GREENS
|
1
<<
RANGE_BLUES
))
pr
->
get_adjust_range
=
get_rgb_adjust_range
;
else
if
(
pr
->
mask
&
(
1
<<
RANGE_CYANS
|
1
<<
RANGE_MAGENTAS
|
1
<<
RANGE_YELLOWS
))
pr
->
get_adjust_range
=
get_cmy_adjust_range
;
else
if
(
pr
->
mask
&
1
<<
RANGE_WHITES
)
pr
->
get_adjust_range
=
get_whites_adjust_range
;
else
if
(
pr
->
mask
&
1
<<
RANGE_NEUTRALS
)
pr
->
get_adjust_range
=
get_neutrals_adjust_range
;
else
if
(
pr
->
mask
&
1
<<
RANGE_BLACKS
)
pr
->
get_adjust_range
=
get_blacks_adjust_range
;
else
if
(
!
s
->
is_16bit
&&
(
pr
->
mask
&
1
<<
RANGE_WHITES
))
pr
->
get_adjust_range
=
get_whites_adjust_range8
;
else
if
(
!
s
->
is_16bit
&&
(
pr
->
mask
&
1
<<
RANGE_NEUTRALS
))
pr
->
get_adjust_range
=
get_neutrals_adjust_range8
;
else
if
(
!
s
->
is_16bit
&&
(
pr
->
mask
&
1
<<
RANGE_BLACKS
))
pr
->
get_adjust_range
=
get_blacks_adjust_range8
;
else
if
(
s
->
is_16bit
&&
(
pr
->
mask
&
1
<<
RANGE_WHITES
))
pr
->
get_adjust_range
=
get_whites_adjust_range16
;
else
if
(
s
->
is_16bit
&&
(
pr
->
mask
&
1
<<
RANGE_NEUTRALS
))
pr
->
get_adjust_range
=
get_neutrals_adjust_range16
;
else
if
(
s
->
is_16bit
&&
(
pr
->
mask
&
1
<<
RANGE_BLACKS
))
pr
->
get_adjust_range
=
get_blacks_adjust_range16
;
else
av_assert0
(
0
);
}
...
...
@@ -244,10 +261,19 @@ end:
return
ret
;
}
static
av_cold
int
init
(
AVFilterContext
*
ctx
)
static
int
config_input
(
AVFilterLink
*
inlink
)
{
int
i
,
ret
;
AVFilterContext
*
ctx
=
inlink
->
dst
;
SelectiveColorContext
*
s
=
ctx
->
priv
;
const
AVPixFmtDescriptor
*
desc
=
av_pix_fmt_desc_get
(
inlink
->
format
);
s
->
is_16bit
=
desc
->
comp
[
0
].
depth
>
8
;
s
->
step
=
av_get_padded_bits_per_pixel
(
desc
)
>>
(
3
+
s
->
is_16bit
);
ret
=
ff_fill_rgba_map
(
s
->
rgba_map
,
inlink
->
format
);
if
(
ret
<
0
)
return
ret
;
/* If the following conditions are not met, it will cause trouble while
* parsing the PS file */
...
...
@@ -287,7 +313,16 @@ static av_cold int init(AVFilterContext *ctx)
static
int
query_formats
(
AVFilterContext
*
ctx
)
{
static
const
enum
AVPixelFormat
pix_fmts
[]
=
{
AV_PIX_FMT_RGB32
,
AV_PIX_FMT_0RGB32
,
AV_PIX_FMT_NONE
};
static
const
enum
AVPixelFormat
pix_fmts
[]
=
{
AV_PIX_FMT_RGB24
,
AV_PIX_FMT_BGR24
,
AV_PIX_FMT_RGBA
,
AV_PIX_FMT_BGRA
,
AV_PIX_FMT_ARGB
,
AV_PIX_FMT_ABGR
,
AV_PIX_FMT_0RGB
,
AV_PIX_FMT_0BGR
,
AV_PIX_FMT_RGB0
,
AV_PIX_FMT_BGR0
,
AV_PIX_FMT_RGB48
,
AV_PIX_FMT_BGR48
,
AV_PIX_FMT_RGBA64
,
AV_PIX_FMT_BGRA64
,
AV_PIX_FMT_NONE
};
AVFilterFormats
*
fmts_list
=
ff_make_format_list
(
pix_fmts
);
if
(
!
fmts_list
)
return
AVERROR
(
ENOMEM
);
...
...
@@ -304,91 +339,101 @@ static inline int comp_adjust(int adjust_range, float value, float adjust, float
return
lrint
(
av_clipf
(
res
,
min
,
max
)
*
adjust_range
);
}
static
inline
int
selective_color
(
AVFilterContext
*
ctx
,
ThreadData
*
td
,
int
jobnr
,
int
nb_jobs
,
int
direct
,
int
correction_method
)
{
int
i
,
x
,
y
;
const
AVFrame
*
in
=
td
->
in
;
AVFrame
*
out
=
td
->
out
;
const
SelectiveColorContext
*
s
=
ctx
->
priv
;
const
int
height
=
in
->
height
;
const
int
width
=
in
->
width
;
const
int
slice_start
=
(
height
*
jobnr
)
/
nb_jobs
;
const
int
slice_end
=
(
height
*
(
jobnr
+
1
))
/
nb_jobs
;
const
int
dst_linesize
=
out
->
linesize
[
0
];
const
int
src_linesize
=
in
->
linesize
[
0
];
uint8_t
*
dst
=
out
->
data
[
0
]
+
slice_start
*
dst_linesize
;
const
uint8_t
*
src
=
in
->
data
[
0
]
+
slice_start
*
src_linesize
;
for
(
y
=
slice_start
;
y
<
slice_end
;
y
++
)
{
const
uint32_t
*
src32
=
(
const
uint32_t
*
)
src
;
uint32_t
*
dst32
=
(
uint32_t
*
)
dst
;
for
(
x
=
0
;
x
<
width
;
x
++
)
{
const
uint32_t
color
=
*
src32
++
;
const
int
r
=
color
>>
16
&
0xff
;
const
int
g
=
color
>>
8
&
0xff
;
const
int
b
=
color
&
0xff
;
const
int
min_color
=
FFMIN3
(
r
,
g
,
b
);
const
int
max_color
=
FFMAX3
(
r
,
g
,
b
);
const
uint32_t
range_flag
=
(
r
==
max_color
)
<<
RANGE_REDS
|
(
r
==
min_color
)
<<
RANGE_CYANS
|
(
g
==
max_color
)
<<
RANGE_GREENS
|
(
g
==
min_color
)
<<
RANGE_MAGENTAS
|
(
b
==
max_color
)
<<
RANGE_BLUES
|
(
b
==
min_color
)
<<
RANGE_YELLOWS
|
(
r
>
128
&&
g
>
128
&&
b
>
128
)
<<
RANGE_WHITES
|
((
r
||
g
||
b
)
&&
(
r
!=
255
||
g
!=
255
||
b
!=
255
))
<<
RANGE_NEUTRALS
|
(
r
<
128
&&
g
<
128
&&
b
<
128
)
<<
RANGE_BLACKS
;
const
float
rnorm
=
r
/
255
.;
const
float
gnorm
=
g
/
255
.;
const
float
bnorm
=
b
/
255
.;
int
adjust_r
=
0
,
adjust_g
=
0
,
adjust_b
=
0
;
for
(
i
=
0
;
i
<
s
->
nb_process_ranges
;
i
++
)
{
const
struct
process_range
*
pr
=
&
s
->
process_ranges
[
i
];
if
(
range_flag
&
pr
->
mask
)
{
const
int
adjust_range
=
pr
->
get_adjust_range
(
r
,
g
,
b
,
min_color
,
max_color
);
if
(
adjust_range
>
0
)
{
const
float
*
cmyk_adjust
=
s
->
cmyk_adjust
[
pr
->
range_id
];
const
float
adj_c
=
cmyk_adjust
[
0
];
const
float
adj_m
=
cmyk_adjust
[
1
];
const
float
adj_y
=
cmyk_adjust
[
2
];
const
float
k
=
cmyk_adjust
[
3
];
adjust_r
+=
comp_adjust
(
adjust_range
,
rnorm
,
adj_c
,
k
,
correction_method
);
adjust_g
+=
comp_adjust
(
adjust_range
,
gnorm
,
adj_m
,
k
,
correction_method
);
adjust_b
+=
comp_adjust
(
adjust_range
,
bnorm
,
adj_y
,
k
,
correction_method
);
}
}
}
if
(
!
direct
||
adjust_r
||
adjust_g
||
adjust_b
)
*
dst32
=
(
color
&
0xff000000
)
|
av_clip_uint8
(
r
+
adjust_r
)
<<
16
|
av_clip_uint8
(
g
+
adjust_g
)
<<
8
|
av_clip_uint8
(
b
+
adjust_b
);
dst32
++
;
}
src
+=
src_linesize
;
dst
+=
dst_linesize
;
}
return
0
;
#define DECLARE_SELECTIVE_COLOR_FUNC(nbits) \
static inline int selective_color_##nbits(AVFilterContext *ctx, ThreadData *td, \
int jobnr, int nb_jobs, int direct, int correction_method) \
{ \
int i, x, y; \
const AVFrame *in = td->in; \
AVFrame *out = td->out; \
const SelectiveColorContext *s = ctx->priv; \
const int height = in->height; \
const int width = in->width; \
const int slice_start = (height * jobnr ) / nb_jobs; \
const int slice_end = (height * (jobnr+1)) / nb_jobs; \
const int dst_linesize = out->linesize[0]; \
const int src_linesize = in->linesize[0]; \
const uint8_t roffset = s->rgba_map[R]; \
const uint8_t goffset = s->rgba_map[G]; \
const uint8_t boffset = s->rgba_map[B]; \
const uint8_t aoffset = s->rgba_map[A]; \
\
for (y = slice_start; y < slice_end; y++) { \
uint##nbits##_t *dst = ( uint##nbits##_t *)(out->data[0] + y * dst_linesize); \
const uint##nbits##_t *src = (const uint##nbits##_t *)( in->data[0] + y * src_linesize); \
\
for (x = 0; x < width * s->step; x += s->step) { \
const int r = src[x + roffset]; \
const int g = src[x + goffset]; \
const int b = src[x + boffset]; \
const int min_color = FFMIN3(r, g, b); \
const int max_color = FFMAX3(r, g, b); \
const int is_white = (r > 1<<(nbits-1) && g > 1<<(nbits-1) && b > 1<<(nbits-1)); \
const int is_neutral = (r || g || b) && \
r != (1<<nbits)-1 && g != (1<<nbits)-1 && b != (1<<nbits)-1; \
const int is_black = (r < 1<<(nbits-1) && g < 1<<(nbits-1) && b < 1<<(nbits-1)); \
const uint32_t range_flag = (r == max_color) << RANGE_REDS \
| (r == min_color) << RANGE_CYANS \
| (g == max_color) << RANGE_GREENS \
| (g == min_color) << RANGE_MAGENTAS \
| (b == max_color) << RANGE_BLUES \
| (b == min_color) << RANGE_YELLOWS \
| is_white << RANGE_WHITES \
| is_neutral << RANGE_NEUTRALS \
| is_black << RANGE_BLACKS; \
\
const float rnorm = r * (1.f / ((1<<nbits)-1)); \
const float gnorm = g * (1.f / ((1<<nbits)-1)); \
const float bnorm = b * (1.f / ((1<<nbits)-1)); \
int adjust_r = 0, adjust_g = 0, adjust_b = 0; \
\
for (i = 0; i < s->nb_process_ranges; i++) { \
const struct process_range *pr = &s->process_ranges[i]; \
\
if (range_flag & pr->mask) { \
const int adjust_range = pr->get_adjust_range(r, g, b, min_color, max_color); \
\
if (adjust_range > 0) { \
const float *cmyk_adjust = s->cmyk_adjust[pr->range_id]; \
const float adj_c = cmyk_adjust[0]; \
const float adj_m = cmyk_adjust[1]; \
const float adj_y = cmyk_adjust[2]; \
const float k = cmyk_adjust[3]; \
\
adjust_r += comp_adjust(adjust_range, rnorm, adj_c, k, correction_method); \
adjust_g += comp_adjust(adjust_range, gnorm, adj_m, k, correction_method); \
adjust_b += comp_adjust(adjust_range, bnorm, adj_y, k, correction_method); \
} \
} \
} \
\
if (!direct || adjust_r || adjust_g || adjust_b) { \
dst[x + roffset] = av_clip_uint##nbits(r + adjust_r); \
dst[x + goffset] = av_clip_uint##nbits(g + adjust_g); \
dst[x + boffset] = av_clip_uint##nbits(b + adjust_b); \
if (!direct && s->step == 4) \
dst[x + aoffset] = src[x + aoffset]; \
} \
} \
} \
return 0; \
}
#define DEF_SELECTIVE_COLOR_FUNC(name, direct, correction_method
)
\
static int selective_color_##name
(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
\
{ \
return selective_color
(ctx, arg, jobnr, nb_jobs, direct, correction_method);
\
#define DEF_SELECTIVE_COLOR_FUNC(name, direct, correction_method
, nbits)
\
static int selective_color_##name
##_##nbits(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
\
{
\
return selective_color
_##nbits(ctx, arg, jobnr, nb_jobs, direct, correction_method);
\
}
DEF_SELECTIVE_COLOR_FUNC
(
indirect_absolute
,
0
,
CORRECTION_METHOD_ABSOLUTE
)
DEF_SELECTIVE_COLOR_FUNC
(
indirect_relative
,
0
,
CORRECTION_METHOD_RELATIVE
)
DEF_SELECTIVE_COLOR_FUNC
(
direct_absolute
,
1
,
CORRECTION_METHOD_ABSOLUTE
)
DEF_SELECTIVE_COLOR_FUNC
(
direct_relative
,
1
,
CORRECTION_METHOD_RELATIVE
)
#define DEF_SELECTIVE_COLOR_FUNCS(nbits) \
DECLARE_SELECTIVE_COLOR_FUNC(nbits) \
DEF_SELECTIVE_COLOR_FUNC(indirect_absolute, 0, CORRECTION_METHOD_ABSOLUTE, nbits) \
DEF_SELECTIVE_COLOR_FUNC(indirect_relative, 0, CORRECTION_METHOD_RELATIVE, nbits) \
DEF_SELECTIVE_COLOR_FUNC( direct_absolute, 1, CORRECTION_METHOD_ABSOLUTE, nbits) \
DEF_SELECTIVE_COLOR_FUNC( direct_relative, 1, CORRECTION_METHOD_RELATIVE, nbits)
DEF_SELECTIVE_COLOR_FUNCS
(
8
)
DEF_SELECTIVE_COLOR_FUNCS
(
16
)
typedef
int
(
*
selective_color_func_type
)(
AVFilterContext
*
ctx
,
void
*
td
,
int
jobnr
,
int
nb_jobs
);
...
...
@@ -400,9 +445,14 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in)
AVFrame
*
out
;
ThreadData
td
;
const
SelectiveColorContext
*
s
=
ctx
->
priv
;
static
const
selective_color_func_type
funcs
[
2
][
2
]
=
{
{
selective_color_indirect_absolute
,
selective_color_indirect_relative
},
{
selective_color_direct_absolute
,
selective_color_direct_relative
},
static
const
selective_color_func_type
funcs
[
2
][
2
][
2
]
=
{
{
{
selective_color_indirect_absolute_8
,
selective_color_indirect_relative_8
},
{
selective_color_direct_absolute_8
,
selective_color_direct_relative_8
},
},{
{
selective_color_indirect_absolute_16
,
selective_color_indirect_relative_16
},
{
selective_color_direct_absolute_16
,
selective_color_direct_relative_16
},
}
};
if
(
av_frame_is_writable
(
in
))
{
...
...
@@ -420,8 +470,8 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in)
td
.
in
=
in
;
td
.
out
=
out
;
ctx
->
internal
->
execute
(
ctx
,
funcs
[
direct
][
s
->
correction_method
],
&
td
,
NULL
,
FFMIN
(
inlink
->
h
,
ctx
->
graph
->
nb_threads
));
ctx
->
internal
->
execute
(
ctx
,
funcs
[
s
->
is_16bit
][
direct
][
s
->
correction_method
]
,
&
td
,
NULL
,
FFMIN
(
inlink
->
h
,
ctx
->
graph
->
nb_threads
));
if
(
!
direct
)
av_frame_free
(
&
in
);
...
...
@@ -433,6 +483,7 @@ static const AVFilterPad selectivecolor_inputs[] = {
.
name
=
"default"
,
.
type
=
AVMEDIA_TYPE_VIDEO
,
.
filter_frame
=
filter_frame
,
.
config_props
=
config_input
,
},
{
NULL
}
};
...
...
@@ -449,7 +500,6 @@ AVFilter ff_vf_selectivecolor = {
.
name
=
"selectivecolor"
,
.
description
=
NULL_IF_CONFIG_SMALL
(
"Apply CMYK adjustments to specific color ranges."
),
.
priv_size
=
sizeof
(
SelectiveColorContext
),
.
init
=
init
,
.
query_formats
=
query_formats
,
.
inputs
=
selectivecolor_inputs
,
.
outputs
=
selectivecolor_outputs
,
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment