diff --git a/Changelog b/Changelog index a5d25f2ce4..479f1648c0 100644 --- a/Changelog +++ b/Changelog @@ -9,6 +9,7 @@ version : - 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: diff --git a/libavfilter/version.h b/libavfilter/version.h index 89d6fbc9ad..9d21de4309 100644 --- a/libavfilter/version.h +++ b/libavfilter/version.h @@ -30,7 +30,7 @@ #include "libavutil/version.h" #define LIBAVFILTER_VERSION_MAJOR 6 -#define LIBAVFILTER_VERSION_MINOR 48 +#define LIBAVFILTER_VERSION_MINOR 49 #define LIBAVFILTER_VERSION_MICRO 100 #define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \ diff --git a/libavfilter/vf_selectivecolor.c b/libavfilter/vf_selectivecolor.c index 38e6ad5365..33716ec5f5 100644 --- a/libavfilter/vf_selectivecolor.c +++ b/libavfilter/vf_selectivecolor.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015 Clément Bœsch + * Copyright (c) 2015-2016 Clément Bœsch * * 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; -} +#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<> 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<mask = 1 << range_id; if (pr->mask & (1<get_adjust_range = get_rgb_adjust_range; else if (pr->mask & (1<get_adjust_range = get_cmy_adjust_range; - else if (pr->mask & 1<get_adjust_range = get_whites_adjust_range; - else if (pr->mask & 1<get_adjust_range = get_neutrals_adjust_range; - else if (pr->mask & 1<get_adjust_range = get_blacks_adjust_range; + else if (!s->is_16bit && (pr->mask & 1<get_adjust_range = get_whites_adjust_range8; + else if (!s->is_16bit && (pr->mask & 1<get_adjust_range = get_neutrals_adjust_range8; + else if (!s->is_16bit && (pr->mask & 1<get_adjust_range = get_blacks_adjust_range8; + else if ( s->is_16bit && (pr->mask & 1<get_adjust_range = get_whites_adjust_range16; + else if ( s->is_16bit && (pr->mask & 1<get_adjust_range = get_neutrals_adjust_range16; + else if ( s->is_16bit && (pr->mask & 1<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<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,