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 <r57sh...@uralweb.ru> --- libswscale/options.c | 1 + libswscale/swscale.h | 1 + libswscale/utils.c | 30 +++++++++++++++++++++++++++++- 3 files changed, 31 insertions(+), 1 deletion(-) 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 | -- 2.17.0.windows.1 _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-requ...@ffmpeg.org with subject "unsubscribe".