X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavfilter%2Fvf_curves.c;h=3524fef1adf14337c7a4f645bfa923acb357d728;hb=a240097ecd4fb1639db99e7becb888ae478405cd;hp=19ab789152e3ef77ffbddfa06db98742c4ba0d7a;hpb=203bbaccfaaeac9548862e83792d38509a8c8167;p=ffmpeg diff --git a/libavfilter/vf_curves.c b/libavfilter/vf_curves.c index 19ab789152e..3524fef1adf 100644 --- a/libavfilter/vf_curves.c +++ b/libavfilter/vf_curves.c @@ -69,7 +69,12 @@ typedef struct CurvesContext { uint8_t rgba_map[4]; int step; char *plot_filename; + int saved_plot; int is_16bit; + int depth; + int parsed_psfile; + + int (*filter_slice)(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs); } CurvesContext; typedef struct ThreadData { @@ -77,20 +82,20 @@ typedef struct ThreadData { } ThreadData; #define OFFSET(x) offsetof(CurvesContext, x) -#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM +#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_RUNTIME_PARAM static const AVOption curves_options[] = { { "preset", "select a color curves preset", OFFSET(preset), AV_OPT_TYPE_INT, {.i64=PRESET_NONE}, PRESET_NONE, NB_PRESETS-1, FLAGS, "preset_name" }, - { "none", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_NONE}, INT_MIN, INT_MAX, FLAGS, "preset_name" }, - { "color_negative", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_COLOR_NEGATIVE}, INT_MIN, INT_MAX, FLAGS, "preset_name" }, - { "cross_process", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_CROSS_PROCESS}, INT_MIN, INT_MAX, FLAGS, "preset_name" }, - { "darker", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_DARKER}, INT_MIN, INT_MAX, FLAGS, "preset_name" }, - { "increase_contrast", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_INCREASE_CONTRAST}, INT_MIN, INT_MAX, FLAGS, "preset_name" }, - { "lighter", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_LIGHTER}, INT_MIN, INT_MAX, FLAGS, "preset_name" }, - { "linear_contrast", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_LINEAR_CONTRAST}, INT_MIN, INT_MAX, FLAGS, "preset_name" }, - { "medium_contrast", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_MEDIUM_CONTRAST}, INT_MIN, INT_MAX, FLAGS, "preset_name" }, - { "negative", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_NEGATIVE}, INT_MIN, INT_MAX, FLAGS, "preset_name" }, - { "strong_contrast", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_STRONG_CONTRAST}, INT_MIN, INT_MAX, FLAGS, "preset_name" }, - { "vintage", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_VINTAGE}, INT_MIN, INT_MAX, FLAGS, "preset_name" }, + { "none", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_NONE}, 0, 0, FLAGS, "preset_name" }, + { "color_negative", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_COLOR_NEGATIVE}, 0, 0, FLAGS, "preset_name" }, + { "cross_process", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_CROSS_PROCESS}, 0, 0, FLAGS, "preset_name" }, + { "darker", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_DARKER}, 0, 0, FLAGS, "preset_name" }, + { "increase_contrast", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_INCREASE_CONTRAST}, 0, 0, FLAGS, "preset_name" }, + { "lighter", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_LIGHTER}, 0, 0, FLAGS, "preset_name" }, + { "linear_contrast", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_LINEAR_CONTRAST}, 0, 0, FLAGS, "preset_name" }, + { "medium_contrast", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_MEDIUM_CONTRAST}, 0, 0, FLAGS, "preset_name" }, + { "negative", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_NEGATIVE}, 0, 0, FLAGS, "preset_name" }, + { "strong_contrast", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_STRONG_CONTRAST}, 0, 0, FLAGS, "preset_name" }, + { "vintage", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_VINTAGE}, 0, 0, FLAGS, "preset_name" }, { "master","set master points coordinates",OFFSET(comp_points_str[NB_COMP]), AV_OPT_TYPE_STRING, {.str=NULL}, .flags = FLAGS }, { "m", "set master points coordinates",OFFSET(comp_points_str[NB_COMP]), AV_OPT_TYPE_STRING, {.str=NULL}, .flags = FLAGS }, { "red", "set red points coordinates", OFFSET(comp_points_str[0]), AV_OPT_TYPE_STRING, {.str=NULL}, .flags = FLAGS }, @@ -209,7 +214,7 @@ static int get_nb_points(const struct keypoint *d) * @see http://people.math.sfu.ca/~stockie/teaching/macm316/notes/splines.pdf */ -#define CLIP(v) (nbits == 8 ? av_clip_uint8(v) : av_clip_uint16(v)) +#define CLIP(v) (nbits == 8 ? av_clip_uint8(v) : av_clip_uintp2_c(v, nbits)) static inline int interpolate(void *log_ctx, uint16_t *y, const struct keypoint *points, int nbits) @@ -341,6 +346,10 @@ static int interpolate##nbits(void *log_ctx, uint16_t *y, \ } DECLARE_INTERPOLATE_FUNC(8) +DECLARE_INTERPOLATE_FUNC(9) +DECLARE_INTERPOLATE_FUNC(10) +DECLARE_INTERPOLATE_FUNC(12) +DECLARE_INTERPOLATE_FUNC(14) DECLARE_INTERPOLATE_FUNC(16) static int parse_psfile(AVFilterContext *ctx, const char *fname) @@ -479,10 +488,11 @@ static av_cold int curves_init(AVFilterContext *ctx) } } - if (curves->psfile) { + if (curves->psfile && !curves->parsed_psfile) { ret = parse_psfile(ctx, curves->psfile); if (ret < 0) return ret; + curves->parsed_psfile = 1; } if (curves->preset != PRESET_NONE) { @@ -497,6 +507,7 @@ static av_cold int curves_init(AVFilterContext *ctx) SET_COMP_IF_NOT_SET(1, g); SET_COMP_IF_NOT_SET(2, b); SET_COMP_IF_NOT_SET(3, master); + curves->preset = PRESET_NONE; } return 0; @@ -512,6 +523,12 @@ static int query_formats(AVFilterContext *ctx) 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_GBRP, AV_PIX_FMT_GBRAP, + AV_PIX_FMT_GBRP9, + AV_PIX_FMT_GBRP10, AV_PIX_FMT_GBRAP10, + AV_PIX_FMT_GBRP12, AV_PIX_FMT_GBRAP12, + AV_PIX_FMT_GBRP14, + AV_PIX_FMT_GBRP16, AV_PIX_FMT_GBRAP16, AV_PIX_FMT_NONE }; AVFilterFormats *fmts_list = ff_make_format_list(pix_fmts); @@ -520,6 +537,120 @@ static int query_formats(AVFilterContext *ctx) return ff_set_common_formats(ctx, fmts_list); } +static int filter_slice_packed(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) +{ + int x, y; + const CurvesContext *curves = ctx->priv; + const ThreadData *td = arg; + const AVFrame *in = td->in; + const AVFrame *out = td->out; + const int direct = out == in; + const int step = curves->step; + const uint8_t r = curves->rgba_map[R]; + const uint8_t g = curves->rgba_map[G]; + const uint8_t b = curves->rgba_map[B]; + const uint8_t a = curves->rgba_map[A]; + const int slice_start = (in->height * jobnr ) / nb_jobs; + const int slice_end = (in->height * (jobnr+1)) / nb_jobs; + + if (curves->is_16bit) { + for (y = slice_start; y < slice_end; y++) { + uint16_t *dstp = ( uint16_t *)(out->data[0] + y * out->linesize[0]); + const uint16_t *srcp = (const uint16_t *)(in ->data[0] + y * in->linesize[0]); + + for (x = 0; x < in->width * step; x += step) { + dstp[x + r] = curves->graph[R][srcp[x + r]]; + dstp[x + g] = curves->graph[G][srcp[x + g]]; + dstp[x + b] = curves->graph[B][srcp[x + b]]; + if (!direct && step == 4) + dstp[x + a] = srcp[x + a]; + } + } + } else { + uint8_t *dst = out->data[0] + slice_start * out->linesize[0]; + const uint8_t *src = in->data[0] + slice_start * in->linesize[0]; + + for (y = slice_start; y < slice_end; y++) { + for (x = 0; x < in->width * step; x += step) { + dst[x + r] = curves->graph[R][src[x + r]]; + dst[x + g] = curves->graph[G][src[x + g]]; + dst[x + b] = curves->graph[B][src[x + b]]; + if (!direct && step == 4) + dst[x + a] = src[x + a]; + } + dst += out->linesize[0]; + src += in ->linesize[0]; + } + } + return 0; +} + +static int filter_slice_planar(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) +{ + int x, y; + const CurvesContext *curves = ctx->priv; + const ThreadData *td = arg; + const AVFrame *in = td->in; + const AVFrame *out = td->out; + const int direct = out == in; + const int step = curves->step; + const uint8_t r = curves->rgba_map[R]; + const uint8_t g = curves->rgba_map[G]; + const uint8_t b = curves->rgba_map[B]; + const uint8_t a = curves->rgba_map[A]; + const int slice_start = (in->height * jobnr ) / nb_jobs; + const int slice_end = (in->height * (jobnr+1)) / nb_jobs; + + if (curves->is_16bit) { + for (y = slice_start; y < slice_end; y++) { + uint16_t *dstrp = ( uint16_t *)(out->data[r] + y * out->linesize[r]); + uint16_t *dstgp = ( uint16_t *)(out->data[g] + y * out->linesize[g]); + uint16_t *dstbp = ( uint16_t *)(out->data[b] + y * out->linesize[b]); + uint16_t *dstap = ( uint16_t *)(out->data[a] + y * out->linesize[a]); + const uint16_t *srcrp = (const uint16_t *)(in ->data[r] + y * in->linesize[r]); + const uint16_t *srcgp = (const uint16_t *)(in ->data[g] + y * in->linesize[g]); + const uint16_t *srcbp = (const uint16_t *)(in ->data[b] + y * in->linesize[b]); + const uint16_t *srcap = (const uint16_t *)(in ->data[a] + y * in->linesize[a]); + + for (x = 0; x < in->width; x++) { + dstrp[x] = curves->graph[R][srcrp[x]]; + dstgp[x] = curves->graph[G][srcgp[x]]; + dstbp[x] = curves->graph[B][srcbp[x]]; + if (!direct && step == 4) + dstap[x] = srcap[x]; + } + } + } else { + uint8_t *dstr = out->data[r] + slice_start * out->linesize[r]; + uint8_t *dstg = out->data[g] + slice_start * out->linesize[g]; + uint8_t *dstb = out->data[b] + slice_start * out->linesize[b]; + uint8_t *dsta = out->data[a] + slice_start * out->linesize[a]; + const uint8_t *srcr = in->data[r] + slice_start * in->linesize[r]; + const uint8_t *srcg = in->data[g] + slice_start * in->linesize[g]; + const uint8_t *srcb = in->data[b] + slice_start * in->linesize[b]; + const uint8_t *srca = in->data[a] + slice_start * in->linesize[a]; + + for (y = slice_start; y < slice_end; y++) { + for (x = 0; x < in->width; x++) { + dstr[x] = curves->graph[R][srcr[x]]; + dstg[x] = curves->graph[G][srcg[x]]; + dstb[x] = curves->graph[B][srcb[x]]; + if (!direct && step == 4) + dsta[x] = srca[x]; + } + dstr += out->linesize[r]; + dstg += out->linesize[g]; + dstb += out->linesize[b]; + dsta += out->linesize[a]; + srcr += in ->linesize[r]; + srcg += in ->linesize[g]; + srcb += in ->linesize[b]; + srca += in ->linesize[a]; + } + } + return 0; +} + static int config_input(AVFilterLink *inlink) { int i, j, ret; @@ -531,18 +662,27 @@ static int config_input(AVFilterLink *inlink) ff_fill_rgba_map(curves->rgba_map, inlink->format); curves->is_16bit = desc->comp[0].depth > 8; - curves->lut_size = curves->is_16bit ? 1<<16 : 1<<8; + curves->depth = desc->comp[0].depth; + curves->lut_size = 1 << curves->depth; curves->step = av_get_padded_bits_per_pixel(desc) >> (3 + curves->is_16bit); + curves->filter_slice = desc->flags & AV_PIX_FMT_FLAG_PLANAR ? filter_slice_planar : filter_slice_packed; for (i = 0; i < NB_COMP + 1; i++) { - curves->graph[i] = av_mallocz_array(curves->lut_size, sizeof(*curves->graph[0])); + if (!curves->graph[i]) + curves->graph[i] = av_mallocz_array(curves->lut_size, sizeof(*curves->graph[0])); if (!curves->graph[i]) return AVERROR(ENOMEM); ret = parse_points_str(ctx, comp_points + i, curves->comp_points_str[i], curves->lut_size); if (ret < 0) return ret; - if (curves->is_16bit) ret = interpolate16(ctx, curves->graph[i], comp_points[i]); - else ret = interpolate8(ctx, curves->graph[i], comp_points[i]); + switch (curves->depth) { + case 8: ret = interpolate8 (ctx, curves->graph[i], comp_points[i]); break; + case 9: ret = interpolate9 (ctx, curves->graph[i], comp_points[i]); break; + case 10: ret = interpolate10(ctx, curves->graph[i], comp_points[i]); break; + case 12: ret = interpolate12(ctx, curves->graph[i], comp_points[i]); break; + case 14: ret = interpolate14(ctx, curves->graph[i], comp_points[i]); break; + case 16: ret = interpolate16(ctx, curves->graph[i], comp_points[i]); break; + } if (ret < 0) return ret; } @@ -564,8 +704,10 @@ static int config_input(AVFilterLink *inlink) } } - if (curves->plot_filename) + if (curves->plot_filename && !curves->saved_plot) { dump_curves(curves->plot_filename, curves->graph, comp_points, curves->lut_size); + curves->saved_plot = 1; + } for (i = 0; i < NB_COMP + 1; i++) { struct keypoint *point = comp_points[i]; @@ -579,57 +721,10 @@ static int config_input(AVFilterLink *inlink) return 0; } -static int filter_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) -{ - int x, y; - const CurvesContext *curves = ctx->priv; - const ThreadData *td = arg; - const AVFrame *in = td->in; - const AVFrame *out = td->out; - const int direct = out == in; - const int step = curves->step; - const uint8_t r = curves->rgba_map[R]; - const uint8_t g = curves->rgba_map[G]; - const uint8_t b = curves->rgba_map[B]; - const uint8_t a = curves->rgba_map[A]; - const int slice_start = (in->height * jobnr ) / nb_jobs; - const int slice_end = (in->height * (jobnr+1)) / nb_jobs; - - if (curves->is_16bit) { - for (y = slice_start; y < slice_end; y++) { - uint16_t *dstp = ( uint16_t *)(out->data[0] + y * out->linesize[0]); - const uint16_t *srcp = (const uint16_t *)(in ->data[0] + y * in->linesize[0]); - - for (x = 0; x < in->width * step; x += step) { - dstp[x + r] = curves->graph[R][srcp[x + r]]; - dstp[x + g] = curves->graph[G][srcp[x + g]]; - dstp[x + b] = curves->graph[B][srcp[x + b]]; - if (!direct && step == 4) - dstp[x + a] = srcp[x + a]; - } - } - } else { - uint8_t *dst = out->data[0] + slice_start * out->linesize[0]; - const uint8_t *src = in->data[0] + slice_start * in->linesize[0]; - - for (y = slice_start; y < slice_end; y++) { - for (x = 0; x < in->width * step; x += step) { - dst[x + r] = curves->graph[R][src[x + r]]; - dst[x + g] = curves->graph[G][src[x + g]]; - dst[x + b] = curves->graph[B][src[x + b]]; - if (!direct && step == 4) - dst[x + a] = src[x + a]; - } - dst += out->linesize[0]; - src += in ->linesize[0]; - } - } - return 0; -} - static int filter_frame(AVFilterLink *inlink, AVFrame *in) { AVFilterContext *ctx = inlink->dst; + CurvesContext *curves = ctx->priv; AVFilterLink *outlink = ctx->outputs[0]; AVFrame *out; ThreadData td; @@ -647,7 +742,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) td.in = in; td.out = out; - ctx->internal->execute(ctx, filter_slice, &td, NULL, FFMIN(outlink->h, ff_filter_get_nb_threads(ctx))); + ctx->internal->execute(ctx, curves->filter_slice, &td, NULL, FFMIN(outlink->h, ff_filter_get_nb_threads(ctx))); if (out != in) av_frame_free(&in); @@ -655,6 +750,42 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) return ff_filter_frame(outlink, out); } +static int process_command(AVFilterContext *ctx, const char *cmd, const char *args, + char *res, int res_len, int flags) +{ + CurvesContext *curves = ctx->priv; + int ret; + + if (!strcmp(cmd, "plot")) { + curves->saved_plot = 0; + } else if (!strcmp(cmd, "all") || !strcmp(cmd, "preset") || !strcmp(cmd, "psfile")) { + if (!strcmp(cmd, "psfile")) + curves->parsed_psfile = 0; + av_freep(&curves->comp_points_str_all); + av_freep(&curves->comp_points_str[0]); + av_freep(&curves->comp_points_str[1]); + av_freep(&curves->comp_points_str[2]); + av_freep(&curves->comp_points_str[NB_COMP]); + } else if (!strcmp(cmd, "red") || !strcmp(cmd, "r")) { + av_freep(&curves->comp_points_str[0]); + } else if (!strcmp(cmd, "green") || !strcmp(cmd, "g")) { + av_freep(&curves->comp_points_str[1]); + } else if (!strcmp(cmd, "blue") || !strcmp(cmd, "b")) { + av_freep(&curves->comp_points_str[2]); + } else if (!strcmp(cmd, "master") || !strcmp(cmd, "m")) { + av_freep(&curves->comp_points_str[NB_COMP]); + } + + ret = ff_filter_process_command(ctx, cmd, args, res, res_len, flags); + if (ret < 0) + return ret; + + ret = curves_init(ctx); + if (ret < 0) + return ret; + return config_input(ctx->inputs[0]); +} + static av_cold void curves_uninit(AVFilterContext *ctx) { int i; @@ -693,4 +824,5 @@ AVFilter ff_vf_curves = { .outputs = curves_outputs, .priv_class = &curves_class, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, + .process_command = process_command, };