From 7460f0fd0829b5bf550fb0283d6d9f8a21e7467a Mon Sep 17 00:00:00 2001
From: Paul B Mahol <onemda@gmail.com>
Date: Mon, 15 Aug 2016 19:54:29 +0200
Subject: [PATCH] avfilter/vf_geq: add interpolation option

---
 doc/filters.texi     |  3 +++
 libavfilter/vf_geq.c | 32 +++++++++++++++++++++++++++++++-
 2 files changed, 34 insertions(+), 1 deletion(-)

diff --git a/doc/filters.texi b/doc/filters.texi
index c595fed..475a929 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -8221,6 +8221,9 @@ Set the red expression.
 Set the green expression.
 @item blue_expr, b
 Set the blue expression.
+@item interpolation, i
+Set pixel interpolation. Can be @code{nearest} or @code{bilinear}.
+Default is @code{bicubic}.
 @end table
 
 The colorspace is selected according to the specified options. If one
diff --git a/libavfilter/vf_geq.c b/libavfilter/vf_geq.c
index 88d3b75..4bbb6ae 100644
--- a/libavfilter/vf_geq.c
+++ b/libavfilter/vf_geq.c
@@ -41,6 +41,7 @@ typedef struct {
     int hsub, vsub;             ///< chroma subsampling
     int planes;                 ///< number of planes
     int is_rgb;
+    int interpolate;
 } GEQContext;
 
 enum { Y = 0, U, V, A, G, B, R };
@@ -63,6 +64,10 @@ static const AVOption geq_options[] = {
     { "g",          "set green expression",       OFFSET(expr_str[G]), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS },
     { "blue_expr",  "set blue expression",        OFFSET(expr_str[B]), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS },
     { "b",          "set blue expression",        OFFSET(expr_str[B]), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS },
+    { "interpolation", "set pixel interpolation", OFFSET(interpolate), AV_OPT_TYPE_INT,    {.i64=1}, 0, 1, FLAGS, "interpolate" },
+    { "i",             "set pixel interpolation", OFFSET(interpolate), AV_OPT_TYPE_INT,    {.i64=1}, 0, 1, FLAGS, "interpolate" },
+    { "nearest",    NULL,                                           0, AV_OPT_TYPE_CONST,  {.i64=0}, 0, 1, FLAGS, "interpolate" },
+    { "bilinear",   NULL,                                           0, AV_OPT_TYPE_CONST,  {.i64=1}, 0, 1, FLAGS, "interpolate" },
     {NULL},
 };
 
@@ -91,6 +96,24 @@ static inline double getpix(void *priv, double x, double y, int plane)
           +   y *((1-x)*src[xi + (yi+1) * linesize] + x*src[xi + 1 + (yi+1) * linesize]);
 }
 
+static inline double getpixn(void *priv, double x, double y, int plane)
+{
+    GEQContext *geq = priv;
+    AVFrame *picref = geq->picref;
+    const uint8_t *src = picref->data[plane];
+    const int linesize = picref->linesize[plane];
+    const int w = (plane == 1 || plane == 2) ? AV_CEIL_RSHIFT(picref->width,  geq->hsub) : picref->width;
+    const int h = (plane == 1 || plane == 2) ? AV_CEIL_RSHIFT(picref->height, geq->vsub) : picref->height;
+
+    if (!src)
+        return 0;
+
+    x = av_clipf(x, 0, w - 1);
+    y = av_clipf(y, 0, h - 1);
+
+    return src[(int)x + (int)y * linesize];
+}
+
 //TODO: cubic interpolate
 //TODO: keep the last few frames
 static double lum(void *priv, double x, double y) { return getpix(priv, x, y, 0); }
@@ -98,6 +121,11 @@ static double  cb(void *priv, double x, double y) { return getpix(priv, x, y, 1)
 static double  cr(void *priv, double x, double y) { return getpix(priv, x, y, 2); }
 static double alpha(void *priv, double x, double y) { return getpix(priv, x, y, 3); }
 
+static double lumn(void *priv, double x, double y) { return getpixn(priv, x, y, 0); }
+static double  cbn(void *priv, double x, double y) { return getpixn(priv, x, y, 1); }
+static double  crn(void *priv, double x, double y) { return getpixn(priv, x, y, 2); }
+static double alphan(void *priv, double x, double y) { return getpixn(priv, x, y, 3); }
+
 static const char *const var_names[] = {   "X",   "Y",   "W",   "H",   "N",   "SW",   "SH",   "T",        NULL };
 enum                                   { VAR_X, VAR_Y, VAR_W, VAR_H, VAR_N, VAR_SW, VAR_SH, VAR_T, VAR_VARS_NB };
 
@@ -148,13 +176,15 @@ static av_cold int geq_init(AVFilterContext *ctx)
 
     for (plane = 0; plane < 4; plane++) {
         static double (*p[])(void *, double, double) = { lum, cb, cr, alpha };
+        static double (*pn[])(void *, double, double) = { lumn, cbn, crn, alphan };
         static const char *const func2_yuv_names[]    = { "lum", "cb", "cr", "alpha", "p", NULL };
         static const char *const func2_rgb_names[]    = { "g", "b", "r", "alpha", "p", NULL };
         const char *const *func2_names       = geq->is_rgb ? func2_rgb_names : func2_yuv_names;
         double (*func2[])(void *, double, double) = { lum, cb, cr, alpha, p[plane], NULL };
+        double (*func2n[])(void *, double, double) = { lumn, cbn, crn, alphan, pn[plane], NULL };
 
         ret = av_expr_parse(&geq->e[plane], geq->expr_str[plane < 3 && geq->is_rgb ? plane+4 : plane], var_names,
-                            NULL, NULL, func2_names, func2, 0, ctx);
+                            NULL, NULL, func2_names, geq->interpolate == 1 ? func2 : func2n, 0, ctx);
         if (ret < 0)
             break;
     }
-- 
2.8.3

