LCOV - code coverage report
Current view: top level - libavfilter - vf_curves.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 185 297 62.3 %
Date: 2017-12-17 16:07:53 Functions: 11 14 78.6 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2013 Clément Bœsch
       3             :  *
       4             :  * This file is part of FFmpeg.
       5             :  *
       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.
      10             :  *
      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.
      15             :  *
      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
      19             :  */
      20             : 
      21             : #include "libavutil/opt.h"
      22             : #include "libavutil/bprint.h"
      23             : #include "libavutil/eval.h"
      24             : #include "libavutil/file.h"
      25             : #include "libavutil/intreadwrite.h"
      26             : #include "libavutil/avassert.h"
      27             : #include "libavutil/pixdesc.h"
      28             : #include "avfilter.h"
      29             : #include "drawutils.h"
      30             : #include "formats.h"
      31             : #include "internal.h"
      32             : #include "video.h"
      33             : 
      34             : #define R 0
      35             : #define G 1
      36             : #define B 2
      37             : #define A 3
      38             : 
      39             : struct keypoint {
      40             :     double x, y;
      41             :     struct keypoint *next;
      42             : };
      43             : 
      44             : #define NB_COMP 3
      45             : 
      46             : enum preset {
      47             :     PRESET_NONE,
      48             :     PRESET_COLOR_NEGATIVE,
      49             :     PRESET_CROSS_PROCESS,
      50             :     PRESET_DARKER,
      51             :     PRESET_INCREASE_CONTRAST,
      52             :     PRESET_LIGHTER,
      53             :     PRESET_LINEAR_CONTRAST,
      54             :     PRESET_MEDIUM_CONTRAST,
      55             :     PRESET_NEGATIVE,
      56             :     PRESET_STRONG_CONTRAST,
      57             :     PRESET_VINTAGE,
      58             :     NB_PRESETS,
      59             : };
      60             : 
      61             : typedef struct CurvesContext {
      62             :     const AVClass *class;
      63             :     int preset;
      64             :     char *comp_points_str[NB_COMP + 1];
      65             :     char *comp_points_str_all;
      66             :     uint16_t *graph[NB_COMP + 1];
      67             :     int lut_size;
      68             :     char *psfile;
      69             :     uint8_t rgba_map[4];
      70             :     int step;
      71             :     char *plot_filename;
      72             :     int is_16bit;
      73             : } CurvesContext;
      74             : 
      75             : typedef struct ThreadData {
      76             :     AVFrame *in, *out;
      77             : } ThreadData;
      78             : 
      79             : #define OFFSET(x) offsetof(CurvesContext, x)
      80             : #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
      81             : static const AVOption curves_options[] = {
      82             :     { "preset", "select a color curves preset", OFFSET(preset), AV_OPT_TYPE_INT, {.i64=PRESET_NONE}, PRESET_NONE, NB_PRESETS-1, FLAGS, "preset_name" },
      83             :         { "none",               NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_NONE},                 INT_MIN, INT_MAX, FLAGS, "preset_name" },
      84             :         { "color_negative",     NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_COLOR_NEGATIVE},       INT_MIN, INT_MAX, FLAGS, "preset_name" },
      85             :         { "cross_process",      NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_CROSS_PROCESS},        INT_MIN, INT_MAX, FLAGS, "preset_name" },
      86             :         { "darker",             NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_DARKER},               INT_MIN, INT_MAX, FLAGS, "preset_name" },
      87             :         { "increase_contrast",  NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_INCREASE_CONTRAST},    INT_MIN, INT_MAX, FLAGS, "preset_name" },
      88             :         { "lighter",            NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_LIGHTER},              INT_MIN, INT_MAX, FLAGS, "preset_name" },
      89             :         { "linear_contrast",    NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_LINEAR_CONTRAST},      INT_MIN, INT_MAX, FLAGS, "preset_name" },
      90             :         { "medium_contrast",    NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_MEDIUM_CONTRAST},      INT_MIN, INT_MAX, FLAGS, "preset_name" },
      91             :         { "negative",           NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_NEGATIVE},             INT_MIN, INT_MAX, FLAGS, "preset_name" },
      92             :         { "strong_contrast",    NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_STRONG_CONTRAST},      INT_MIN, INT_MAX, FLAGS, "preset_name" },
      93             :         { "vintage",            NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_VINTAGE},              INT_MIN, INT_MAX, FLAGS, "preset_name" },
      94             :     { "master","set master points coordinates",OFFSET(comp_points_str[NB_COMP]), AV_OPT_TYPE_STRING, {.str=NULL}, .flags = FLAGS },
      95             :     { "m",     "set master points coordinates",OFFSET(comp_points_str[NB_COMP]), AV_OPT_TYPE_STRING, {.str=NULL}, .flags = FLAGS },
      96             :     { "red",   "set red points coordinates",   OFFSET(comp_points_str[0]), AV_OPT_TYPE_STRING, {.str=NULL}, .flags = FLAGS },
      97             :     { "r",     "set red points coordinates",   OFFSET(comp_points_str[0]), AV_OPT_TYPE_STRING, {.str=NULL}, .flags = FLAGS },
      98             :     { "green", "set green points coordinates", OFFSET(comp_points_str[1]), AV_OPT_TYPE_STRING, {.str=NULL}, .flags = FLAGS },
      99             :     { "g",     "set green points coordinates", OFFSET(comp_points_str[1]), AV_OPT_TYPE_STRING, {.str=NULL}, .flags = FLAGS },
     100             :     { "blue",  "set blue points coordinates",  OFFSET(comp_points_str[2]), AV_OPT_TYPE_STRING, {.str=NULL}, .flags = FLAGS },
     101             :     { "b",     "set blue points coordinates",  OFFSET(comp_points_str[2]), AV_OPT_TYPE_STRING, {.str=NULL}, .flags = FLAGS },
     102             :     { "all",   "set points coordinates for all components", OFFSET(comp_points_str_all), AV_OPT_TYPE_STRING, {.str=NULL}, .flags = FLAGS },
     103             :     { "psfile", "set Photoshop curves file name", OFFSET(psfile), AV_OPT_TYPE_STRING, {.str=NULL}, .flags = FLAGS },
     104             :     { "plot", "save Gnuplot script of the curves in specified file", OFFSET(plot_filename), AV_OPT_TYPE_STRING, {.str=NULL}, .flags = FLAGS },
     105             :     { NULL }
     106             : };
     107             : 
     108             : AVFILTER_DEFINE_CLASS(curves);
     109             : 
     110             : static const struct {
     111             :     const char *r;
     112             :     const char *g;
     113             :     const char *b;
     114             :     const char *master;
     115             : } curves_presets[] = {
     116             :     [PRESET_COLOR_NEGATIVE] = {
     117             :         "0.129/1 0.466/0.498 0.725/0",
     118             :         "0.109/1 0.301/0.498 0.517/0",
     119             :         "0.098/1 0.235/0.498 0.423/0",
     120             :     },
     121             :     [PRESET_CROSS_PROCESS] = {
     122             :         "0/0 0.25/0.156 0.501/0.501 0.686/0.745 1/1",
     123             :         "0/0 0.25/0.188 0.38/0.501 0.745/0.815 1/0.815",
     124             :         "0/0 0.231/0.094 0.709/0.874 1/1",
     125             :     },
     126             :     [PRESET_DARKER]             = { .master = "0/0 0.5/0.4 1/1" },
     127             :     [PRESET_INCREASE_CONTRAST]  = { .master = "0/0 0.149/0.066 0.831/0.905 0.905/0.98 1/1" },
     128             :     [PRESET_LIGHTER]            = { .master = "0/0 0.4/0.5 1/1" },
     129             :     [PRESET_LINEAR_CONTRAST]    = { .master = "0/0 0.305/0.286 0.694/0.713 1/1" },
     130             :     [PRESET_MEDIUM_CONTRAST]    = { .master = "0/0 0.286/0.219 0.639/0.643 1/1" },
     131             :     [PRESET_NEGATIVE]           = { .master = "0/1 1/0" },
     132             :     [PRESET_STRONG_CONTRAST]    = { .master = "0/0 0.301/0.196 0.592/0.6 0.686/0.737 1/1" },
     133             :     [PRESET_VINTAGE] = {
     134             :         "0/0.11 0.42/0.51 1/0.95",
     135             :         "0/0 0.50/0.48 1/1",
     136             :         "0/0.22 0.49/0.44 1/0.8",
     137             :     }
     138             : };
     139             : 
     140           9 : static struct keypoint *make_point(double x, double y, struct keypoint *next)
     141             : {
     142           9 :     struct keypoint *point = av_mallocz(sizeof(*point));
     143             : 
     144           9 :     if (!point)
     145           0 :         return NULL;
     146           9 :     point->x = x;
     147           9 :     point->y = y;
     148           9 :     point->next = next;
     149           9 :     return point;
     150             : }
     151             : 
     152           4 : static int parse_points_str(AVFilterContext *ctx, struct keypoint **points, const char *s,
     153             :                             int lut_size)
     154             : {
     155           4 :     char *p = (char *)s; // strtod won't alter the string
     156           4 :     struct keypoint *last = NULL;
     157           4 :     const int scale = lut_size - 1;
     158             : 
     159             :     /* construct a linked list based on the key points string */
     160          17 :     while (p && *p) {
     161           9 :         struct keypoint *point = make_point(0, 0, NULL);
     162           9 :         if (!point)
     163           0 :             return AVERROR(ENOMEM);
     164           9 :         point->x = av_strtod(p, &p); if (p && *p) p++;
     165           9 :         point->y = av_strtod(p, &p); if (p && *p) p++;
     166           9 :         if (point->x < 0 || point->x > 1 || point->y < 0 || point->y > 1) {
     167           0 :             av_log(ctx, AV_LOG_ERROR, "Invalid key point coordinates (%f;%f), "
     168             :                    "x and y must be in the [0;1] range.\n", point->x, point->y);
     169           0 :             return AVERROR(EINVAL);
     170             :         }
     171           9 :         if (!*points)
     172           3 :             *points = point;
     173           9 :         if (last) {
     174           6 :             if ((int)(last->x * scale) >= (int)(point->x * scale)) {
     175           0 :                 av_log(ctx, AV_LOG_ERROR, "Key point coordinates (%f;%f) "
     176             :                        "and (%f;%f) are too close from each other or not "
     177             :                        "strictly increasing on the x-axis\n",
     178             :                        last->x, last->y, point->x, point->y);
     179           0 :                 return AVERROR(EINVAL);
     180             :             }
     181           6 :             last->next = point;
     182             :         }
     183           9 :         last = point;
     184             :     }
     185             : 
     186           4 :     if (*points && !(*points)->next) {
     187           0 :         av_log(ctx, AV_LOG_WARNING, "Only one point (at (%f;%f)) is defined, "
     188             :                "this is unlikely to behave as you expect. You probably want"
     189             :                "at least 2 points.",
     190           0 :                (*points)->x, (*points)->y);
     191             :     }
     192             : 
     193           4 :     return 0;
     194             : }
     195             : 
     196           4 : static int get_nb_points(const struct keypoint *d)
     197             : {
     198           4 :     int n = 0;
     199          17 :     while (d) {
     200           9 :         n++;
     201           9 :         d = d->next;
     202             :     }
     203           4 :     return n;
     204             : }
     205             : 
     206             : /**
     207             :  * Natural cubic spline interpolation
     208             :  * Finding curves using Cubic Splines notes by Steven Rauch and John Stockie.
     209             :  * @see http://people.math.sfu.ca/~stockie/teaching/macm316/notes/splines.pdf
     210             :  */
     211             : 
     212             : #define CLIP(v) (nbits == 8 ? av_clip_uint8(v) : av_clip_uint16(v))
     213             : 
     214           4 : static inline int interpolate(void *log_ctx, uint16_t *y,
     215             :                               const struct keypoint *points, int nbits)
     216             : {
     217           4 :     int i, ret = 0;
     218           4 :     const struct keypoint *point = points;
     219           4 :     double xprev = 0;
     220           4 :     const int lut_size = 1<<nbits;
     221           4 :     const int scale = lut_size - 1;
     222             : 
     223             :     double (*matrix)[3];
     224             :     double *h, *r;
     225           4 :     const int n = get_nb_points(points); // number of splines
     226             : 
     227           4 :     if (n == 0) {
     228         257 :         for (i = 0; i < lut_size; i++)
     229         256 :             y[i] = i;
     230           1 :         return 0;
     231             :     }
     232             : 
     233           3 :     if (n == 1) {
     234           0 :         for (i = 0; i < lut_size; i++)
     235           0 :             y[i] = CLIP(point->y * scale);
     236           0 :         return 0;
     237             :     }
     238             : 
     239           3 :     matrix = av_calloc(n, sizeof(*matrix));
     240           3 :     h = av_malloc((n - 1) * sizeof(*h));
     241           3 :     r = av_calloc(n, sizeof(*r));
     242             : 
     243           3 :     if (!matrix || !h || !r) {
     244           0 :         ret = AVERROR(ENOMEM);
     245           0 :         goto end;
     246             :     }
     247             : 
     248             :     /* h(i) = x(i+1) - x(i) */
     249           3 :     i = -1;
     250          12 :     for (point = points; point; point = point->next) {
     251           9 :         if (i != -1)
     252           6 :             h[i] = point->x - xprev;
     253           9 :         xprev = point->x;
     254           9 :         i++;
     255             :     }
     256             : 
     257             :     /* right-side of the polynomials, will be modified to contains the solution */
     258           3 :     point = points;
     259           6 :     for (i = 1; i < n - 1; i++) {
     260           3 :         const double yp = point->y;
     261           3 :         const double yc = point->next->y;
     262           3 :         const double yn = point->next->next->y;
     263           3 :         r[i] = 6 * ((yn-yc)/h[i] - (yc-yp)/h[i-1]);
     264           3 :         point = point->next;
     265             :     }
     266             : 
     267             : #define BD 0 /* sub  diagonal (below main) */
     268             : #define MD 1 /* main diagonal (center) */
     269             : #define AD 2 /* sup  diagonal (above main) */
     270             : 
     271             :     /* left side of the polynomials into a tridiagonal matrix. */
     272           3 :     matrix[0][MD] = matrix[n - 1][MD] = 1;
     273           6 :     for (i = 1; i < n - 1; i++) {
     274           3 :         matrix[i][BD] = h[i-1];
     275           3 :         matrix[i][MD] = 2 * (h[i-1] + h[i]);
     276           3 :         matrix[i][AD] = h[i];
     277             :     }
     278             : 
     279             :     /* tridiagonal solving of the linear system */
     280           9 :     for (i = 1; i < n; i++) {
     281           6 :         const double den = matrix[i][MD] - matrix[i][BD] * matrix[i-1][AD];
     282           6 :         const double k = den ? 1./den : 1.;
     283           6 :         matrix[i][AD] *= k;
     284           6 :         r[i] = (r[i] - matrix[i][BD] * r[i - 1]) * k;
     285             :     }
     286           9 :     for (i = n - 2; i >= 0; i--)
     287           6 :         r[i] = r[i] - matrix[i][AD] * r[i + 1];
     288             : 
     289           3 :     point = points;
     290             : 
     291             :     /* left padding */
     292           3 :     for (i = 0; i < (int)(point->x * scale); i++)
     293           0 :         y[i] = CLIP(point->y * scale);
     294             : 
     295             :     /* compute the graph with x=[x0..xN] */
     296           3 :     i = 0;
     297           3 :     av_assert0(point->next); // always at least 2 key points
     298          12 :     while (point->next) {
     299           6 :         const double yc = point->y;
     300           6 :         const double yn = point->next->y;
     301             : 
     302           6 :         const double a = yc;
     303           6 :         const double b = (yn-yc)/h[i] - h[i]*r[i]/2. - h[i]*(r[i+1]-r[i])/6.;
     304           6 :         const double c = r[i] / 2.;
     305           6 :         const double d = (r[i+1] - r[i]) / (6.*h[i]);
     306             : 
     307             :         int x;
     308           6 :         const int x_start = point->x       * scale;
     309           6 :         const int x_end   = point->next->x * scale;
     310             : 
     311           6 :         av_assert0(x_start >= 0 && x_start < lut_size &&
     312             :                    x_end   >= 0 && x_end   < lut_size);
     313             : 
     314         777 :         for (x = x_start; x <= x_end; x++) {
     315         771 :             const double xx = (x - x_start) * 1./scale;
     316         771 :             const double yy = a + b*xx + c*xx*xx + d*xx*xx*xx;
     317         771 :             y[x] = CLIP(yy * scale);
     318         771 :             av_log(log_ctx, AV_LOG_DEBUG, "f(%f)=%f -> y[%d]=%d\n", xx, yy, x, y[x]);
     319             :         }
     320             : 
     321           6 :         point = point->next;
     322           6 :         i++;
     323             :     }
     324             : 
     325             :     /* right padding */
     326           6 :     for (i = (int)(point->x * scale); i < lut_size; i++)
     327           3 :         y[i] = CLIP(point->y * scale);
     328             : 
     329           3 : end:
     330           3 :     av_free(matrix);
     331           3 :     av_free(h);
     332           3 :     av_free(r);
     333           3 :     return ret;
     334             : }
     335             : 
     336             : #define DECLARE_INTERPOLATE_FUNC(nbits)                                     \
     337             : static int interpolate##nbits(void *log_ctx, uint16_t *y,                   \
     338             :                               const struct keypoint *points)                \
     339             : {                                                                           \
     340             :     return interpolate(log_ctx, y, points, nbits);                          \
     341             : }
     342             : 
     343           4 : DECLARE_INTERPOLATE_FUNC(8)
     344           0 : DECLARE_INTERPOLATE_FUNC(16)
     345             : 
     346           0 : static int parse_psfile(AVFilterContext *ctx, const char *fname)
     347             : {
     348           0 :     CurvesContext *curves = ctx->priv;
     349             :     uint8_t *buf;
     350             :     size_t size;
     351             :     int i, ret, av_unused(version), nb_curves;
     352             :     AVBPrint ptstr;
     353             :     static const int comp_ids[] = {3, 0, 1, 2};
     354             : 
     355           0 :     av_bprint_init(&ptstr, 0, AV_BPRINT_SIZE_AUTOMATIC);
     356             : 
     357           0 :     ret = av_file_map(fname, &buf, &size, 0, NULL);
     358           0 :     if (ret < 0)
     359           0 :         return ret;
     360             : 
     361             : #define READ16(dst) do {                \
     362             :     if (size < 2) {                     \
     363             :         ret = AVERROR_INVALIDDATA;      \
     364             :         goto end;                       \
     365             :     }                                   \
     366             :     dst = AV_RB16(buf);                 \
     367             :     buf  += 2;                          \
     368             :     size -= 2;                          \
     369             : } while (0)
     370             : 
     371           0 :     READ16(version);
     372           0 :     READ16(nb_curves);
     373           0 :     for (i = 0; i < FFMIN(nb_curves, FF_ARRAY_ELEMS(comp_ids)); i++) {
     374             :         int nb_points, n;
     375           0 :         av_bprint_clear(&ptstr);
     376           0 :         READ16(nb_points);
     377           0 :         for (n = 0; n < nb_points; n++) {
     378             :             int y, x;
     379           0 :             READ16(y);
     380           0 :             READ16(x);
     381           0 :             av_bprintf(&ptstr, "%f/%f ", x / 255., y / 255.);
     382             :         }
     383           0 :         if (*ptstr.str) {
     384           0 :             char **pts = &curves->comp_points_str[comp_ids[i]];
     385           0 :             if (!*pts) {
     386           0 :                 *pts = av_strdup(ptstr.str);
     387           0 :                 av_log(ctx, AV_LOG_DEBUG, "curves %d (intid=%d) [%d points]: [%s]\n",
     388             :                        i, comp_ids[i], nb_points, *pts);
     389           0 :                 if (!*pts) {
     390           0 :                     ret = AVERROR(ENOMEM);
     391           0 :                     goto end;
     392             :                 }
     393             :             }
     394             :         }
     395             :     }
     396           0 : end:
     397           0 :     av_bprint_finalize(&ptstr, NULL);
     398           0 :     av_file_unmap(buf, size);
     399           0 :     return ret;
     400             : }
     401             : 
     402           0 : static int dump_curves(const char *fname, uint16_t *graph[NB_COMP + 1],
     403             :                        struct keypoint *comp_points[NB_COMP + 1],
     404             :                        int lut_size)
     405             : {
     406             :     int i;
     407             :     AVBPrint buf;
     408           0 :     const double scale = 1. / (lut_size - 1);
     409             :     static const char * const colors[] = { "red", "green", "blue", "#404040", };
     410           0 :     FILE *f = av_fopen_utf8(fname, "w");
     411             : 
     412             :     av_assert0(FF_ARRAY_ELEMS(colors) == NB_COMP + 1);
     413             : 
     414           0 :     if (!f) {
     415           0 :         int ret = AVERROR(errno);
     416           0 :         av_log(NULL, AV_LOG_ERROR, "Cannot open file '%s' for writing: %s\n",
     417           0 :                fname, av_err2str(ret));
     418           0 :         return ret;
     419             :     }
     420             : 
     421           0 :     av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED);
     422             : 
     423           0 :     av_bprintf(&buf, "set xtics 0.1\n");
     424           0 :     av_bprintf(&buf, "set ytics 0.1\n");
     425           0 :     av_bprintf(&buf, "set size square\n");
     426           0 :     av_bprintf(&buf, "set grid\n");
     427             : 
     428           0 :     for (i = 0; i < FF_ARRAY_ELEMS(colors); i++) {
     429           0 :         av_bprintf(&buf, "%s'-' using 1:2 with lines lc '%s' title ''",
     430             :                    i ? ", " : "plot ", colors[i]);
     431           0 :         if (comp_points[i])
     432           0 :             av_bprintf(&buf, ", '-' using 1:2 with points pointtype 3 lc '%s' title ''",
     433             :                     colors[i]);
     434             :     }
     435           0 :     av_bprintf(&buf, "\n");
     436             : 
     437           0 :     for (i = 0; i < FF_ARRAY_ELEMS(colors); i++) {
     438             :         int x;
     439             : 
     440             :         /* plot generated values */
     441           0 :         for (x = 0; x < lut_size; x++)
     442           0 :             av_bprintf(&buf, "%f %f\n", x * scale, graph[i][x] * scale);
     443           0 :         av_bprintf(&buf, "e\n");
     444             : 
     445             :         /* plot user knots */
     446           0 :         if (comp_points[i]) {
     447           0 :             const struct keypoint *point = comp_points[i];
     448             : 
     449           0 :             while (point) {
     450           0 :                 av_bprintf(&buf, "%f %f\n", point->x, point->y);
     451           0 :                 point = point->next;
     452             :             }
     453           0 :             av_bprintf(&buf, "e\n");
     454             :         }
     455             :     }
     456             : 
     457           0 :     fwrite(buf.str, 1, buf.len, f);
     458           0 :     fclose(f);
     459           0 :     av_bprint_finalize(&buf, NULL);
     460           0 :     return 0;
     461             : }
     462             : 
     463           1 : static av_cold int curves_init(AVFilterContext *ctx)
     464             : {
     465             :     int i, ret;
     466           1 :     CurvesContext *curves = ctx->priv;
     467           1 :     char **pts = curves->comp_points_str;
     468           1 :     const char *allp = curves->comp_points_str_all;
     469             : 
     470             :     //if (!allp && curves->preset != PRESET_NONE && curves_presets[curves->preset].all)
     471             :     //    allp = curves_presets[curves->preset].all;
     472             : 
     473           1 :     if (allp) {
     474           0 :         for (i = 0; i < NB_COMP; i++) {
     475           0 :             if (!pts[i])
     476           0 :                 pts[i] = av_strdup(allp);
     477           0 :             if (!pts[i])
     478           0 :                 return AVERROR(ENOMEM);
     479             :         }
     480             :     }
     481             : 
     482           1 :     if (curves->psfile) {
     483           0 :         ret = parse_psfile(ctx, curves->psfile);
     484           0 :         if (ret < 0)
     485           0 :             return ret;
     486             :     }
     487             : 
     488           1 :     if (curves->preset != PRESET_NONE) {
     489             : #define SET_COMP_IF_NOT_SET(n, name) do {                           \
     490             :     if (!pts[n] && curves_presets[curves->preset].name) {           \
     491             :         pts[n] = av_strdup(curves_presets[curves->preset].name);    \
     492             :         if (!pts[n])                                                \
     493             :             return AVERROR(ENOMEM);                                 \
     494             :     }                                                               \
     495             : } while (0)
     496           1 :         SET_COMP_IF_NOT_SET(0, r);
     497           1 :         SET_COMP_IF_NOT_SET(1, g);
     498           1 :         SET_COMP_IF_NOT_SET(2, b);
     499           1 :         SET_COMP_IF_NOT_SET(3, master);
     500             :     }
     501             : 
     502           1 :     return 0;
     503             : }
     504             : 
     505           1 : static int query_formats(AVFilterContext *ctx)
     506             : {
     507             :     static const enum AVPixelFormat pix_fmts[] = {
     508             :         AV_PIX_FMT_RGB24,  AV_PIX_FMT_BGR24,
     509             :         AV_PIX_FMT_RGBA,   AV_PIX_FMT_BGRA,
     510             :         AV_PIX_FMT_ARGB,   AV_PIX_FMT_ABGR,
     511             :         AV_PIX_FMT_0RGB,   AV_PIX_FMT_0BGR,
     512             :         AV_PIX_FMT_RGB0,   AV_PIX_FMT_BGR0,
     513             :         AV_PIX_FMT_RGB48,  AV_PIX_FMT_BGR48,
     514             :         AV_PIX_FMT_RGBA64, AV_PIX_FMT_BGRA64,
     515             :         AV_PIX_FMT_NONE
     516             :     };
     517           1 :     AVFilterFormats *fmts_list = ff_make_format_list(pix_fmts);
     518           1 :     if (!fmts_list)
     519           0 :         return AVERROR(ENOMEM);
     520           1 :     return ff_set_common_formats(ctx, fmts_list);
     521             : }
     522             : 
     523           1 : static int config_input(AVFilterLink *inlink)
     524             : {
     525             :     int i, j, ret;
     526           1 :     AVFilterContext *ctx = inlink->dst;
     527           1 :     CurvesContext *curves = ctx->priv;
     528           1 :     const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
     529           1 :     char **pts = curves->comp_points_str;
     530           1 :     struct keypoint *comp_points[NB_COMP + 1] = {0};
     531             : 
     532           1 :     ff_fill_rgba_map(curves->rgba_map, inlink->format);
     533           1 :     curves->is_16bit = desc->comp[0].depth > 8;
     534           1 :     curves->lut_size = curves->is_16bit ? 1<<16 : 1<<8;
     535           1 :     curves->step = av_get_padded_bits_per_pixel(desc) >> (3 + curves->is_16bit);
     536             : 
     537           5 :     for (i = 0; i < NB_COMP + 1; i++) {
     538           4 :         curves->graph[i] = av_mallocz_array(curves->lut_size, sizeof(*curves->graph[0]));
     539           4 :         if (!curves->graph[i])
     540           0 :             return AVERROR(ENOMEM);
     541           4 :         ret = parse_points_str(ctx, comp_points + i, curves->comp_points_str[i], curves->lut_size);
     542           4 :         if (ret < 0)
     543           0 :             return ret;
     544           4 :         if (curves->is_16bit) ret = interpolate16(ctx, curves->graph[i], comp_points[i]);
     545           4 :         else                  ret = interpolate8(ctx, curves->graph[i], comp_points[i]);
     546           4 :         if (ret < 0)
     547           0 :             return ret;
     548             :     }
     549             : 
     550           1 :     if (pts[NB_COMP]) {
     551           0 :         for (i = 0; i < NB_COMP; i++)
     552           0 :             for (j = 0; j < curves->lut_size; j++)
     553           0 :                 curves->graph[i][j] = curves->graph[NB_COMP][curves->graph[i][j]];
     554             :     }
     555             : 
     556           1 :     if (av_log_get_level() >= AV_LOG_VERBOSE) {
     557           0 :         for (i = 0; i < NB_COMP; i++) {
     558           0 :             const struct keypoint *point = comp_points[i];
     559           0 :             av_log(ctx, AV_LOG_VERBOSE, "#%d points:", i);
     560           0 :             while (point) {
     561           0 :                 av_log(ctx, AV_LOG_VERBOSE, " (%f;%f)", point->x, point->y);
     562           0 :                 point = point->next;
     563             :             }
     564             :         }
     565             :     }
     566             : 
     567           1 :     if (curves->plot_filename)
     568           0 :         dump_curves(curves->plot_filename, curves->graph, comp_points, curves->lut_size);
     569             : 
     570           5 :     for (i = 0; i < NB_COMP + 1; i++) {
     571           4 :         struct keypoint *point = comp_points[i];
     572          17 :         while (point) {
     573           9 :             struct keypoint *next = point->next;
     574           9 :             av_free(point);
     575           9 :             point = next;
     576             :         }
     577             :     }
     578             : 
     579           1 :     return 0;
     580             : }
     581             : 
     582          45 : static int filter_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
     583             : {
     584             :     int x, y;
     585          45 :     const CurvesContext *curves = ctx->priv;
     586          45 :     const ThreadData *td = arg;
     587          45 :     const AVFrame *in  = td->in;
     588          45 :     const AVFrame *out = td->out;
     589          45 :     const int direct = out == in;
     590          45 :     const int step = curves->step;
     591          45 :     const uint8_t r = curves->rgba_map[R];
     592          45 :     const uint8_t g = curves->rgba_map[G];
     593          45 :     const uint8_t b = curves->rgba_map[B];
     594          45 :     const uint8_t a = curves->rgba_map[A];
     595          45 :     const int slice_start = (in->height *  jobnr   ) / nb_jobs;
     596          45 :     const int slice_end   = (in->height * (jobnr+1)) / nb_jobs;
     597             : 
     598          45 :     if (curves->is_16bit) {
     599           0 :         for (y = slice_start; y < slice_end; y++) {
     600           0 :             uint16_t       *dstp = (      uint16_t *)(out->data[0] + y * out->linesize[0]);
     601           0 :             const uint16_t *srcp = (const uint16_t *)(in ->data[0] + y *  in->linesize[0]);
     602             : 
     603           0 :             for (x = 0; x < in->width * step; x += step) {
     604           0 :                 dstp[x + r] = curves->graph[R][srcp[x + r]];
     605           0 :                 dstp[x + g] = curves->graph[G][srcp[x + g]];
     606           0 :                 dstp[x + b] = curves->graph[B][srcp[x + b]];
     607           0 :                 if (!direct && step == 4)
     608           0 :                     dstp[x + a] = srcp[x + a];
     609             :             }
     610             :         }
     611             :     } else {
     612          45 :         uint8_t       *dst = out->data[0] + slice_start * out->linesize[0];
     613          45 :         const uint8_t *src =  in->data[0] + slice_start *  in->linesize[0];
     614             : 
     615        2445 :         for (y = slice_start; y < slice_end; y++) {
     616     1538400 :             for (x = 0; x < in->width * step; x += step) {
     617     1536000 :                 dst[x + r] = curves->graph[R][src[x + r]];
     618     1536000 :                 dst[x + g] = curves->graph[G][src[x + g]];
     619     1536000 :                 dst[x + b] = curves->graph[B][src[x + b]];
     620     1536000 :                 if (!direct && step == 4)
     621           0 :                     dst[x + a] = src[x + a];
     622             :             }
     623        2400 :             dst += out->linesize[0];
     624        2400 :             src += in ->linesize[0];
     625             :         }
     626             :     }
     627          45 :     return 0;
     628             : }
     629             : 
     630           5 : static int filter_frame(AVFilterLink *inlink, AVFrame *in)
     631             : {
     632           5 :     AVFilterContext *ctx = inlink->dst;
     633           5 :     AVFilterLink *outlink = ctx->outputs[0];
     634             :     AVFrame *out;
     635             :     ThreadData td;
     636             : 
     637           5 :     if (av_frame_is_writable(in)) {
     638           5 :         out = in;
     639             :     } else {
     640           0 :         out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
     641           0 :         if (!out) {
     642           0 :             av_frame_free(&in);
     643           0 :             return AVERROR(ENOMEM);
     644             :         }
     645           0 :         av_frame_copy_props(out, in);
     646             :     }
     647             : 
     648           5 :     td.in  = in;
     649           5 :     td.out = out;
     650           5 :     ctx->internal->execute(ctx, filter_slice, &td, NULL, FFMIN(outlink->h, ff_filter_get_nb_threads(ctx)));
     651             : 
     652           5 :     if (out != in)
     653           0 :         av_frame_free(&in);
     654             : 
     655           5 :     return ff_filter_frame(outlink, out);
     656             : }
     657             : 
     658           1 : static av_cold void curves_uninit(AVFilterContext *ctx)
     659             : {
     660             :     int i;
     661           1 :     CurvesContext *curves = ctx->priv;
     662             : 
     663           5 :     for (i = 0; i < NB_COMP + 1; i++)
     664           4 :         av_freep(&curves->graph[i]);
     665           1 : }
     666             : 
     667             : static const AVFilterPad curves_inputs[] = {
     668             :     {
     669             :         .name         = "default",
     670             :         .type         = AVMEDIA_TYPE_VIDEO,
     671             :         .filter_frame = filter_frame,
     672             :         .config_props = config_input,
     673             :     },
     674             :     { NULL }
     675             : };
     676             : 
     677             : static const AVFilterPad curves_outputs[] = {
     678             :     {
     679             :         .name = "default",
     680             :         .type = AVMEDIA_TYPE_VIDEO,
     681             :     },
     682             :     { NULL }
     683             : };
     684             : 
     685             : AVFilter ff_vf_curves = {
     686             :     .name          = "curves",
     687             :     .description   = NULL_IF_CONFIG_SMALL("Adjust components curves."),
     688             :     .priv_size     = sizeof(CurvesContext),
     689             :     .init          = curves_init,
     690             :     .uninit        = curves_uninit,
     691             :     .query_formats = query_formats,
     692             :     .inputs        = curves_inputs,
     693             :     .outputs       = curves_outputs,
     694             :     .priv_class    = &curves_class,
     695             :     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,
     696             : };

Generated by: LCOV version 1.13