diff mbox series

[FFmpeg-devel,1/1] libswscale: add area upscale

Message ID 20200202002913.4864-2-r57shell@uralweb.ru
State New
Headers show
Series libswscale: add area upscale
Related show

Checks

Context Check Description
andriy/ffmpeg-patchwork pending
andriy/ffmpeg-patchwork success Applied patch
andriy/ffmpeg-patchwork success Configure finished
andriy/ffmpeg-patchwork success Make finished
andriy/ffmpeg-patchwork success Make fate finished

Commit Message

Pavel Klimov Feb. 2, 2020, 12:29 a.m. UTC
area upscale is similar to neighbor upscale,
just better with non integer factors.
math comes from assumption that
neighbor filter works fine,
and then integrate it over pixel width.

Signed-off-by: Pavel Klimov <r57shell@uralweb.ru>
---
 libswscale/options.c |  1 +
 libswscale/swscale.h |  1 +
 libswscale/utils.c   | 30 +++++++++++++++++++++++++++++-
 3 files changed, 31 insertions(+), 1 deletion(-)
diff mbox series

Patch

diff --git a/libswscale/options.c b/libswscale/options.c
index 7eb2752543..bbf71997fb 100644
--- a/libswscale/options.c
+++ b/libswscale/options.c
@@ -41,6 +41,7 @@  static const AVOption swscale_options[] = {
     { "experimental",    "experimental",                  0,                 AV_OPT_TYPE_CONST,  { .i64  = SWS_X              }, INT_MIN, INT_MAX,        VE, "sws_flags" },
     { "neighbor",        "nearest neighbor",              0,                 AV_OPT_TYPE_CONST,  { .i64  = SWS_POINT          }, INT_MIN, INT_MAX,        VE, "sws_flags" },
     { "area",            "averaging area",                0,                 AV_OPT_TYPE_CONST,  { .i64  = SWS_AREA           }, INT_MIN, INT_MAX,        VE, "sws_flags" },
+    { "area_upscale",    "averaging area upscale",        0,                 AV_OPT_TYPE_CONST,  { .i64  = SWS_AREA_UPSCALE   }, INT_MIN, INT_MAX,        VE, "sws_flags" },
     { "bicublin",        "luma bicubic, chroma bilinear", 0,                 AV_OPT_TYPE_CONST,  { .i64  = SWS_BICUBLIN       }, INT_MIN, INT_MAX,        VE, "sws_flags" },
     { "gauss",           "Gaussian",                      0,                 AV_OPT_TYPE_CONST,  { .i64  = SWS_GAUSS          }, INT_MIN, INT_MAX,        VE, "sws_flags" },
     { "sinc",            "sinc",                          0,                 AV_OPT_TYPE_CONST,  { .i64  = SWS_SINC           }, INT_MIN, INT_MAX,        VE, "sws_flags" },
diff --git a/libswscale/swscale.h b/libswscale/swscale.h
index 7713f51ec6..1d677e0a94 100644
--- a/libswscale/swscale.h
+++ b/libswscale/swscale.h
@@ -66,6 +66,7 @@  const char *swscale_license(void);
 #define SWS_SINC          0x100
 #define SWS_LANCZOS       0x200
 #define SWS_SPLINE        0x400
+#define SWS_AREA_UPSCALE  0x800
 
 #define SWS_SRC_V_CHR_DROP_MASK     0x30000
 #define SWS_SRC_V_CHR_DROP_SHIFT    16
diff --git a/libswscale/utils.c b/libswscale/utils.c
index b2c08a5983..b713c40812 100644
--- a/libswscale/utils.c
+++ b/libswscale/utils.c
@@ -316,6 +316,7 @@  typedef struct {
 
 static const ScaleAlgorithm scale_algorithms[] = {
     { SWS_AREA,          "area averaging",                  1 /* downscale only, for upscale it is bilinear */ },
+    { SWS_AREA_UPSCALE,  "area averaging upscale",          1 },
     { SWS_BICUBIC,       "bicubic",                         4 },
     { SWS_BICUBLIN,      "luma bicubic / chroma bilinear", -1 },
     { SWS_BILINEAR,      "bilinear",                        2 },
@@ -398,6 +399,32 @@  static av_cold int initFilter(int16_t **outFilter, int32_t **filterPos,
             }
             xDstInSrc += xInc;
         }
+    } else if (xInc <= (1 << 16) && (flags & SWS_AREA_UPSCALE)) { // area upscale
+        int i;
+        int64_t xDstInSrc;
+        double dInc, x, x1;
+
+        filterSize = 2;
+        FF_ALLOC_ARRAY_OR_GOTO(NULL, filter,
+                               dstW, sizeof(*filter) * filterSize, fail);
+
+        xDstInSrc = ((dstPos*(int64_t)xInc)>>8) - ((srcPos*0x8000LL)>>7);
+
+        xDstInSrc += (1 << 15) - xInc / 2;
+        dInc = (double)srcW / dstW * (1 << 16);
+        for (i = 0; i < dstW; i++) {
+            x = i * dInc;
+
+            (*filterPos)[i] = (xDstInSrc + (int)x) >> 16;
+            x1 = xDstInSrc - ((*filterPos)[i] << 16) + x;
+            if (x1 + dInc <= (1 << 16)) {
+                filter[i * filterSize + 0] = fone;
+                filter[i * filterSize + 1] = 0;
+            } else {
+                filter[i * filterSize + 0] = fone * (((1 << 16) - x1) / dInc);
+                filter[i * filterSize + 1] = fone - filter[i * filterSize + 0];
+            }
+        }
     } else {
         int64_t xDstInSrc;
         int sizeFactor = -1;
@@ -471,7 +498,7 @@  static av_cold int initFilter(int16_t **outFilter, int32_t **filterPos,
                     else
                         c = pow(c, A);
                     coeff = (c * 0.5 + 0.5) * fone;
-                } else if (flags & SWS_AREA) {
+                } else if (flags & (SWS_AREA | SWS_AREA_UPSCALE)) {
                     int64_t d2 = d - (1 << 29);
                     if (d2 * xInc < -(1LL << (29 + 16)))
                         coeff = 1.0 * (1LL << (30 + 16));
@@ -1229,6 +1256,7 @@  av_cold int sws_init_context(SwsContext *c, SwsFilter *srcFilter,
 
     i = flags & (SWS_POINT         |
                  SWS_AREA          |
+                 SWS_AREA_UPSCALE  |
                  SWS_BILINEAR      |
                  SWS_FAST_BILINEAR |
                  SWS_BICUBIC       |