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 {
} 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 },
* @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)
}
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)
}
}
- 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) {
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;
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);
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;
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;
}
}
}
- 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];
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;
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);
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;
.outputs = curves_outputs,
.priv_class = &curves_class,
.flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,
+ .process_command = process_command,
};