X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavfilter%2Fvf_curves.c;h=69ec1084bb3e0933bda9ed3128897bb32fc10502;hb=d13740f3a207668f53ce167cf96f353379ac2c14;hp=5b569682f850e0ff7a5b8a284b8efa94b9efc0ac;hpb=4eee06ae873095ad7413830753c9bd7d793d31ba;p=ffmpeg diff --git a/libavfilter/vf_curves.c b/libavfilter/vf_curves.c index 5b569682f85..69ec1084bb3 100644 --- a/libavfilter/vf_curves.c +++ b/libavfilter/vf_curves.c @@ -63,11 +63,13 @@ typedef struct { int preset; char *comp_points_str[NB_COMP + 1]; char *comp_points_str_all; - uint8_t graph[NB_COMP + 1][256]; + uint16_t *graph[NB_COMP + 1]; + int lut_size; char *psfile; uint8_t rgba_map[4]; int step; char *plot_filename; + int is_16bit; } CurvesContext; typedef struct ThreadData { @@ -147,10 +149,12 @@ static struct keypoint *make_point(double x, double y, struct keypoint *next) return point; } -static int parse_points_str(AVFilterContext *ctx, struct keypoint **points, const char *s) +static int parse_points_str(AVFilterContext *ctx, struct keypoint **points, const char *s, + int lut_size) { char *p = (char *)s; // strtod won't alter the string struct keypoint *last = NULL; + const int scale = lut_size - 1; /* construct a linked list based on the key points string */ while (p && *p) { @@ -167,7 +171,7 @@ static int parse_points_str(AVFilterContext *ctx, struct keypoint **points, cons if (!*points) *points = point; if (last) { - if ((int)(last->x * 255) >= (int)(point->x * 255)) { + if ((int)(last->x * scale) >= (int)(point->x * scale)) { av_log(ctx, AV_LOG_ERROR, "Key point coordinates (%f;%f) " "and (%f;%f) are too close from each other or not " "strictly increasing on the x-axis\n", @@ -204,25 +208,31 @@ static int get_nb_points(const struct keypoint *d) * Finding curves using Cubic Splines notes by Steven Rauch and John Stockie. * @see http://people.math.sfu.ca/~stockie/teaching/macm316/notes/splines.pdf */ -static int interpolate(AVFilterContext *ctx, uint8_t *y, const struct keypoint *points) + +#define CLIP(v) (nbits == 8 ? av_clip_uint8(v) : av_clip_uint16(v)) + +static inline int interpolate(void *log_ctx, uint16_t *y, + const struct keypoint *points, int nbits) { int i, ret = 0; const struct keypoint *point = points; double xprev = 0; + const int lut_size = 1<y * 255); + for (i = 0; i < lut_size; i++) + y[i] = CLIP(point->y * scale); return 0; } @@ -279,8 +289,8 @@ static int interpolate(AVFilterContext *ctx, uint8_t *y, const struct keypoint * point = points; /* left padding */ - for (i = 0; i < (int)(point->x * 255); i++) - y[i] = av_clip_uint8(point->y * 255); + for (i = 0; i < (int)(point->x * scale); i++) + y[i] = CLIP(point->y * scale); /* compute the graph with x=[x0..xN] */ i = 0; @@ -295,17 +305,17 @@ static int interpolate(AVFilterContext *ctx, uint8_t *y, const struct keypoint * const double d = (r[i+1] - r[i]) / (6.*h[i]); int x; - const int x_start = point->x * 255; - const int x_end = point->next->x * 255; + const int x_start = point->x * scale; + const int x_end = point->next->x * scale; - av_assert0(x_start >= 0 && x_start <= 255 && - x_end >= 0 && x_end <= 255); + av_assert0(x_start >= 0 && x_start < lut_size && + x_end >= 0 && x_end < lut_size); for (x = x_start; x <= x_end; x++) { - const double xx = (x - x_start) * 1/255.; + const double xx = (x - x_start) * 1./scale; const double yy = a + b*xx + c*xx*xx + d*xx*xx*xx; - y[x] = av_clip_uint8(yy * 255); - av_log(ctx, AV_LOG_DEBUG, "f(%f)=%f -> y[%d]=%d\n", xx, yy, x, y[x]); + y[x] = CLIP(yy * scale); + av_log(log_ctx, AV_LOG_DEBUG, "f(%f)=%f -> y[%d]=%d\n", xx, yy, x, y[x]); } point = point->next; @@ -313,8 +323,8 @@ static int interpolate(AVFilterContext *ctx, uint8_t *y, const struct keypoint * } /* right padding */ - for (i = (int)(point->x * 255); i <= 255; i++) - y[i] = av_clip_uint8(point->y * 255); + for (i = (int)(point->x * scale); i < lut_size; i++) + y[i] = CLIP(point->y * scale); end: av_free(matrix); @@ -323,6 +333,16 @@ end: return ret; } +#define DECLARE_INTERPOLATE_FUNC(nbits) \ +static int interpolate##nbits(void *log_ctx, uint16_t *y, \ + const struct keypoint *points) \ +{ \ + return interpolate(log_ctx, y, points, nbits); \ +} + +DECLARE_INTERPOLATE_FUNC(8) +DECLARE_INTERPOLATE_FUNC(16) + static int parse_psfile(AVFilterContext *ctx, const char *fname) { CurvesContext *curves = ctx->priv; @@ -379,11 +399,13 @@ end: return ret; } -static int dump_curves(const char *fname, uint8_t graph[NB_COMP + 1][256], - struct keypoint *comp_points[NB_COMP + 1]) +static int dump_curves(const char *fname, uint16_t *graph[NB_COMP + 1], + struct keypoint *comp_points[NB_COMP + 1], + int lut_size) { int i; AVBPrint buf; + const double scale = 1. / (lut_size - 1); static const char * const colors[] = { "red", "green", "blue", "#404040", }; FILE *f = av_fopen_utf8(fname, "w"); @@ -416,8 +438,8 @@ static int dump_curves(const char *fname, uint8_t graph[NB_COMP + 1][256], int x; /* plot generated values */ - for (x = 0; x < 256; x++) - av_bprintf(&buf, "%f %f\n", x/255., graph[i][x]/255.); + for (x = 0; x < lut_size; x++) + av_bprintf(&buf, "%f %f\n", x * scale, graph[i][x] * scale); av_bprintf(&buf, "e\n"); /* plot user knots */ @@ -438,11 +460,10 @@ static int dump_curves(const char *fname, uint8_t graph[NB_COMP + 1][256], return 0; } -static av_cold int init(AVFilterContext *ctx) +static av_cold int curves_init(AVFilterContext *ctx) { - int i, j, ret; + int i, ret; CurvesContext *curves = ctx->priv; - struct keypoint *comp_points[NB_COMP + 1] = {0}; char **pts = curves->comp_points_str; const char *allp = curves->comp_points_str_all; @@ -478,18 +499,57 @@ static av_cold int init(AVFilterContext *ctx) SET_COMP_IF_NOT_SET(3, master); } + return 0; +} + +static int query_formats(AVFilterContext *ctx) +{ + 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); + return ff_set_common_formats(ctx, fmts_list); +} + +static int config_input(AVFilterLink *inlink) +{ + int i, j, ret; + AVFilterContext *ctx = inlink->dst; + CurvesContext *curves = ctx->priv; + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format); + char **pts = curves->comp_points_str; + struct keypoint *comp_points[NB_COMP + 1] = {0}; + + 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->step = av_get_padded_bits_per_pixel(desc) >> (3 + curves->is_16bit); + for (i = 0; i < NB_COMP + 1; i++) { - ret = parse_points_str(ctx, comp_points + i, curves->comp_points_str[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; - ret = interpolate(ctx, curves->graph[i], comp_points[i]); + if (curves->is_16bit) ret = interpolate16(ctx, curves->graph[i], comp_points[i]); + else ret = interpolate8(ctx, curves->graph[i], comp_points[i]); if (ret < 0) return ret; } if (pts[NB_COMP]) { for (i = 0; i < NB_COMP; i++) - for (j = 0; j < 256; j++) + for (j = 0; j < curves->lut_size; j++) curves->graph[i][j] = curves->graph[NB_COMP][curves->graph[i][j]]; } @@ -505,7 +565,7 @@ static av_cold int init(AVFilterContext *ctx) } if (curves->plot_filename) - dump_curves(curves->plot_filename, curves->graph, comp_points); + dump_curves(curves->plot_filename, curves->graph, comp_points, curves->lut_size); for (i = 0; i < NB_COMP + 1; i++) { struct keypoint *point = comp_points[i]; @@ -519,33 +579,6 @@ static av_cold int init(AVFilterContext *ctx) return 0; } -static int query_formats(AVFilterContext *ctx) -{ - 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_NONE - }; - AVFilterFormats *fmts_list = ff_make_format_list(pix_fmts); - if (!fmts_list) - return AVERROR(ENOMEM); - return ff_set_common_formats(ctx, fmts_list); -} - -static int config_input(AVFilterLink *inlink) -{ - CurvesContext *curves = inlink->dst->priv; - const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format); - - ff_fill_rgba_map(curves->rgba_map, inlink->format); - curves->step = av_get_padded_bits_per_pixel(desc) >> 3; - - return 0; -} - static int filter_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) { int x, y; @@ -561,19 +594,35 @@ static int filter_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) 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; - 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]; + + 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]; } - dst += out->linesize[0]; - src += in ->linesize[0]; } return 0; } @@ -598,7 +647,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, ctx->graph->nb_threads)); + ctx->internal->execute(ctx, filter_slice, &td, NULL, FFMIN(outlink->h, ff_filter_get_nb_threads(ctx))); if (out != in) av_frame_free(&in); @@ -606,6 +655,15 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) return ff_filter_frame(outlink, out); } +static av_cold void curves_uninit(AVFilterContext *ctx) +{ + int i; + CurvesContext *curves = ctx->priv; + + for (i = 0; i < NB_COMP + 1; i++) + av_freep(&curves->graph[i]); +} + static const AVFilterPad curves_inputs[] = { { .name = "default", @@ -628,7 +686,8 @@ AVFilter ff_vf_curves = { .name = "curves", .description = NULL_IF_CONFIG_SMALL("Adjust components curves."), .priv_size = sizeof(CurvesContext), - .init = init, + .init = curves_init, + .uninit = curves_uninit, .query_formats = query_formats, .inputs = curves_inputs, .outputs = curves_outputs,