avformat/network: add union for avoiding strict aliassing violations with sockaddr*
[ffmpeg.git] / libavfilter / vf_curves.c
index d83b702..b17c391 100644 (file)
  */
 
 #include "libavutil/opt.h"
+#include "libavutil/bprint.h"
 #include "libavutil/eval.h"
+#include "libavutil/file.h"
+#include "libavutil/intreadwrite.h"
 #include "libavutil/avassert.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
+
 struct keypoint {
     double x, y;
     struct keypoint *next;
@@ -33,66 +43,90 @@ struct keypoint {
 
 #define NB_COMP 3
 
+enum preset {
+    PRESET_NONE,
+    PRESET_COLOR_NEGATIVE,
+    PRESET_CROSS_PROCESS,
+    PRESET_DARKER,
+    PRESET_INCREASE_CONTRAST,
+    PRESET_LIGHTER,
+    PRESET_LINEAR_CONTRAST,
+    PRESET_MEDIUM_CONTRAST,
+    PRESET_NEGATIVE,
+    PRESET_STRONG_CONTRAST,
+    PRESET_VINTAGE,
+    NB_PRESETS,
+};
+
 typedef struct {
     const AVClass *class;
-    char *preset;
-    char *comp_points_str[NB_COMP];
-    uint8_t graph[NB_COMP][256];
+    enum preset preset;
+    char *comp_points_str[NB_COMP + 1];
+    char *comp_points_str_all;
+    uint8_t graph[NB_COMP + 1][256];
+    char *psfile;
+    uint8_t rgba_map[4];
+    int step;
 } CurvesContext;
 
+typedef struct ThreadData {
+    AVFrame *in, *out;
+} ThreadData;
+
 #define OFFSET(x) offsetof(CurvesContext, x)
 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_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" },
+    { "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 },
     { "r",     "set red points coordinates",   OFFSET(comp_points_str[0]), AV_OPT_TYPE_STRING, {.str=NULL}, .flags = FLAGS },
     { "green", "set green points coordinates", OFFSET(comp_points_str[1]), AV_OPT_TYPE_STRING, {.str=NULL}, .flags = FLAGS },
     { "g",     "set green points coordinates", OFFSET(comp_points_str[1]), AV_OPT_TYPE_STRING, {.str=NULL}, .flags = FLAGS },
     { "blue",  "set blue points coordinates",  OFFSET(comp_points_str[2]), AV_OPT_TYPE_STRING, {.str=NULL}, .flags = FLAGS },
     { "b",     "set blue points coordinates",  OFFSET(comp_points_str[2]), AV_OPT_TYPE_STRING, {.str=NULL}, .flags = FLAGS },
-    { "preset", "select a color curves preset", OFFSET(preset), AV_OPT_TYPE_STRING, {.str=NULL}, .flags = FLAGS },
+    { "all",   "set points coordinates for all components", OFFSET(comp_points_str_all), AV_OPT_TYPE_STRING, {.str=NULL}, .flags = FLAGS },
+    { "psfile", "set Photoshop curves file name", OFFSET(psfile), AV_OPT_TYPE_STRING, {.str=NULL}, .flags = FLAGS },
     { NULL }
 };
 
 AVFILTER_DEFINE_CLASS(curves);
 
 static const struct {
-    const char *name;
     const char *r;
     const char *g;
     const char *b;
-} curves_presets[] = { {
-        "color_negative",
+    const char *master;
+} curves_presets[] = {
+    [PRESET_COLOR_NEGATIVE] = {
         "0/1 0.129/1 0.466/0.498 0.725/0 1/0",
         "0/1 0.109/1 0.301/0.498 0.517/0 1/0",
         "0/1 0.098/1 0.235/0.498 0.423/0 1/0",
-    },{
-        "cross_process",
+    },
+    [PRESET_CROSS_PROCESS] = {
         "0.25/0.156 0.501/0.501 0.686/0.745",
         "0.25/0.188 0.38/0.501 0.745/0.815 1/0.815",
         "0.231/0.094 0.709/0.874",
-    },{
-        "darker", "0.5/0.4", "0.5/0.4", "0.5/0.4",
-    },{
-        "increase_contrast",
-        "0.149/0.066 0.831/0.905 0.905/0.98",
-        "0.149/0.066 0.831/0.905 0.905/0.98",
-        "0.149/0.066 0.831/0.905 0.905/0.98",
-    },{
-        "lighter", "0.4/0.5", "0.4/0.5", "0.4/0.5",
-    },{
-        "linear_contrast",
-        "0.305/0.286 0.694/0.713",
-        "0.305/0.286 0.694/0.713",
-        "0.305/0.286 0.694/0.713",
-    },{
-        "medium_contrast",
-        "0.286/0.219 0.639/0.643",
-        "0.286/0.219 0.639/0.643",
-        "0.286/0.219 0.639/0.643",
-    },{
-        "negative", "0/1 1/0", "0/1 1/0", "0/1 1/0",
-    },{
-        "vintage",
+    },
+    [PRESET_DARKER]             = { .master = "0.5/0.4" },
+    [PRESET_INCREASE_CONTRAST]  = { .master = "0.149/0.066 0.831/0.905 0.905/0.98" },
+    [PRESET_LIGHTER]            = { .master = "0.4/0.5" },
+    [PRESET_LINEAR_CONTRAST]    = { .master = "0.305/0.286 0.694/0.713" },
+    [PRESET_MEDIUM_CONTRAST]    = { .master = "0.286/0.219 0.639/0.643" },
+    [PRESET_NEGATIVE]           = { .master = "0/1 1/0" },
+    [PRESET_STRONG_CONTRAST]    = { .master = "0.301/0.196 0.592/0.6 0.686/0.737" },
+    [PRESET_VINTAGE] = {
         "0/0.11 0.42/0.51 1/0.95",
         "0.50/0.48",
         "0/0.22 0.49/0.44 1/0.8",
@@ -221,27 +255,27 @@ static int interpolate(AVFilterContext *ctx, uint8_t *y, const struct keypoint *
         point = point->next;
     }
 
-#define B 0 /* sub  diagonal (below main) */
-#define M 1 /* main diagonal (center) */
-#define A 2 /* sup  diagonal (above main) */
+#define BD 0 /* sub  diagonal (below main) */
+#define MD 1 /* main diagonal (center) */
+#define AD 2 /* sup  diagonal (above main) */
 
     /* left side of the polynomials into a tridiagonal matrix. */
-    matrix[0][M] = matrix[n - 1][M] = 1;
+    matrix[0][MD] = matrix[n - 1][MD] = 1;
     for (i = 1; i < n - 1; i++) {
-        matrix[i][B] = h[i-1];
-        matrix[i][M] = 2 * (h[i-1] + h[i]);
-        matrix[i][A] = h[i];
+        matrix[i][BD] = h[i-1];
+        matrix[i][MD] = 2 * (h[i-1] + h[i]);
+        matrix[i][AD] = h[i];
     }
 
     /* tridiagonal solving of the linear system */
     for (i = 1; i < n; i++) {
-        double den = matrix[i][M] - matrix[i][B] * matrix[i-1][A];
+        double den = matrix[i][MD] - matrix[i][BD] * matrix[i-1][AD];
         double k = den ? 1./den : 1.;
-        matrix[i][A] *= k;
-        r[i] = (r[i] - matrix[i][B] * r[i - 1]) * k;
+        matrix[i][AD] *= k;
+        r[i] = (r[i] - matrix[i][BD] * r[i - 1]) * k;
     }
     for (i = n - 2; i >= 0; i--)
-        r[i] = r[i] - matrix[i][A] * r[i + 1];
+        r[i] = r[i] - matrix[i][AD] * r[i + 1];
 
     /* compute the graph with x=[0..255] */
     i = 0;
@@ -281,46 +315,103 @@ end:
     return ret;
 }
 
-static av_cold int init(AVFilterContext *ctx, const char *args)
+static int parse_psfile(AVFilterContext *ctx, const char *fname)
 {
-    int i, j, ret;
     CurvesContext *curves = ctx->priv;
-    struct keypoint *comp_points[NB_COMP] = {0};
+    uint8_t *buf;
+    size_t size;
+    int i, ret, av_unused(version), nb_curves;
+    AVBPrint ptstr;
+    static const int comp_ids[] = {3, 0, 1, 2};
 
-    curves->class = &curves_class;
-    av_opt_set_defaults(curves);
+    av_bprint_init(&ptstr, 0, AV_BPRINT_SIZE_AUTOMATIC);
 
-    if ((ret = av_set_options_string(curves, args, "=", ":")) < 0)
+    ret = av_file_map(fname, &buf, &size, 0, NULL);
+    if (ret < 0)
         return ret;
 
-    if (curves->preset) {
-        char **pts = curves->comp_points_str;
-        if (pts[0] || pts[1] || pts[2]) {
-            av_log(ctx, AV_LOG_ERROR, "It is not possible to mix a preset "
-                   "with explicit points placements\n");
-            return AVERROR(EINVAL);
+#define READ16(dst) do {                \
+    if (size < 2) {                     \
+        ret = AVERROR_INVALIDDATA;      \
+        goto end;                       \
+    }                                   \
+    dst = AV_RB16(buf);                 \
+    buf  += 2;                          \
+    size -= 2;                          \
+} while (0)
+
+    READ16(version);
+    READ16(nb_curves);
+    for (i = 0; i < FFMIN(nb_curves, FF_ARRAY_ELEMS(comp_ids)); i++) {
+        int nb_points, n;
+        av_bprint_clear(&ptstr);
+        READ16(nb_points);
+        for (n = 0; n < nb_points; n++) {
+            int y, x;
+            READ16(y);
+            READ16(x);
+            av_bprintf(&ptstr, "%f/%f ", x / 255., y / 255.);
         }
-        for (i = 0; i < FF_ARRAY_ELEMS(curves_presets); i++) {
-            if (!strcmp(curves->preset, curves_presets[i].name)) {
-                pts[0] = av_strdup(curves_presets[i].r);
-                pts[1] = av_strdup(curves_presets[i].g);
-                pts[2] = av_strdup(curves_presets[i].b);
-                if (!pts[0] || !pts[1] || !pts[2])
-                    return AVERROR(ENOMEM);
-                break;
+        if (*ptstr.str) {
+            char **pts = &curves->comp_points_str[comp_ids[i]];
+            if (!*pts) {
+                *pts = av_strdup(ptstr.str);
+                av_log(ctx, AV_LOG_DEBUG, "curves %d (intid=%d) [%d points]: [%s]\n",
+                       i, comp_ids[i], nb_points, *pts);
+                if (!*pts) {
+                    ret = AVERROR(ENOMEM);
+                    goto end;
+                }
             }
         }
-        if (i == FF_ARRAY_ELEMS(curves_presets)) {
-            av_log(ctx, AV_LOG_ERROR, "Preset '%s' not found. Available presets:",
-                   curves->preset);
-            for (i = 0; i < FF_ARRAY_ELEMS(curves_presets); i++)
-                av_log(ctx, AV_LOG_ERROR, " %s", curves_presets[i].name);
-            av_log(ctx, AV_LOG_ERROR, ".\n");
-            return AVERROR(EINVAL);
+    }
+end:
+    av_bprint_finalize(&ptstr, NULL);
+    av_file_unmap(buf, size);
+    return ret;
+}
+
+static av_cold int init(AVFilterContext *ctx)
+{
+    int i, j, 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;
+
+    //if (!allp && curves->preset != PRESET_NONE && curves_presets[curves->preset].all)
+    //    allp = curves_presets[curves->preset].all;
+
+    if (allp) {
+        for (i = 0; i < NB_COMP; i++) {
+            if (!pts[i])
+                pts[i] = av_strdup(allp);
+            if (!pts[i])
+                return AVERROR(ENOMEM);
         }
     }
 
-    for (i = 0; i < NB_COMP; i++) {
+    if (curves->psfile) {
+        ret = parse_psfile(ctx, curves->psfile);
+        if (ret < 0)
+            return ret;
+    }
+
+    if (curves->preset != PRESET_NONE) {
+#define SET_COMP_IF_NOT_SET(n, name) do {                           \
+    if (!pts[n] && curves_presets[curves->preset].name) {           \
+        pts[n] = av_strdup(curves_presets[curves->preset].name);    \
+        if (!pts[n])                                                \
+            return AVERROR(ENOMEM);                                 \
+    }                                                               \
+} while (0)
+        SET_COMP_IF_NOT_SET(0, r);
+        SET_COMP_IF_NOT_SET(1, g);
+        SET_COMP_IF_NOT_SET(2, b);
+        SET_COMP_IF_NOT_SET(3, master);
+    }
+
+    for (i = 0; i < NB_COMP + 1; i++) {
         ret = parse_points_str(ctx, comp_points + i, curves->comp_points_str[i]);
         if (ret < 0)
             return ret;
@@ -329,6 +420,12 @@ static av_cold int init(AVFilterContext *ctx, const char *args)
             return ret;
     }
 
+    if (pts[NB_COMP]) {
+        for (i = 0; i < NB_COMP; i++)
+            for (j = 0; j < 256; j++)
+                curves->graph[i][j] = curves->graph[NB_COMP][curves->graph[i][j]];
+    }
+
     if (av_log_get_level() >= AV_LOG_VERBOSE) {
         for (i = 0; i < NB_COMP; i++) {
             struct keypoint *point = comp_points[i];
@@ -345,7 +442,7 @@ static av_cold int init(AVFilterContext *ctx, const char *args)
         }
     }
 
-    for (i = 0; i < NB_COMP; i++) {
+    for (i = 0; i < NB_COMP + 1; i++) {
         struct keypoint *point = comp_points[i];
         while (point) {
             struct keypoint *next = point->next;
@@ -354,29 +451,74 @@ static av_cold int init(AVFilterContext *ctx, const char *args)
         }
     }
 
-    av_opt_free(curves);
     return 0;
 }
 
 static int query_formats(AVFilterContext *ctx)
 {
-    static const enum AVPixelFormat pix_fmts[] = {AV_PIX_FMT_RGB24, 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_NONE
+    };
     ff_set_common_formats(ctx, ff_make_format_list(pix_fmts));
     return 0;
 }
 
+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;
+    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;
+    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)
 {
-    int x, y, i, direct = 0;
     AVFilterContext *ctx = inlink->dst;
-    CurvesContext *curves = ctx->priv;
-    AVFilterLink *outlink = inlink->dst->outputs[0];
+    AVFilterLink *outlink = ctx->outputs[0];
     AVFrame *out;
-    uint8_t *dst;
-    const uint8_t *src;
+    ThreadData td;
 
     if (av_frame_is_writable(in)) {
-        direct = 1;
         out = in;
     } else {
         out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
@@ -387,21 +529,11 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in)
         av_frame_copy_props(out, in);
     }
 
-    dst = out->data[0];
-    src = in ->data[0];
-
-    for (y = 0; y < inlink->h; y++) {
-        uint8_t *dstp = dst;
-        const uint8_t *srcp = src;
+    td.in  = in;
+    td.out = out;
+    ctx->internal->execute(ctx, filter_slice, &td, NULL, FFMIN(outlink->h, ctx->graph->nb_threads));
 
-        for (x = 0; x < inlink->w; x++)
-            for (i = 0; i < NB_COMP; i++, dstp++, srcp++)
-                *dstp = curves->graph[i][*srcp];
-        dst += out->linesize[0];
-        src += in ->linesize[0];
-    }
-
-    if (!direct)
+    if (out != in)
         av_frame_free(&in);
 
     return ff_filter_frame(outlink, out);
@@ -412,19 +544,20 @@ static const AVFilterPad curves_inputs[] = {
         .name         = "default",
         .type         = AVMEDIA_TYPE_VIDEO,
         .filter_frame = filter_frame,
+        .config_props = config_input,
     },
     { NULL }
 };
 
 static const AVFilterPad curves_outputs[] = {
-     {
-         .name = "default",
-         .type = AVMEDIA_TYPE_VIDEO,
-     },
-     { NULL }
+    {
+        .name = "default",
+        .type = AVMEDIA_TYPE_VIDEO,
+    },
+    { NULL }
 };
 
-AVFilter avfilter_vf_curves = {
+AVFilter ff_vf_curves = {
     .name          = "curves",
     .description   = NULL_IF_CONFIG_SMALL("Adjust components curves."),
     .priv_size     = sizeof(CurvesContext),
@@ -433,4 +566,5 @@ AVFilter avfilter_vf_curves = {
     .inputs        = curves_inputs,
     .outputs       = curves_outputs,
     .priv_class    = &curves_class,
+    .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,
 };