diff options
author | Pierre Joye <pierre.php@gmail.com> | 2021-08-16 13:53:49 +0700 |
---|---|---|
committer | Pierre Joye <pierre.php@gmail.com> | 2021-08-16 13:53:49 +0700 |
commit | 672475e540da84100252e720291c63b30e028d06 (patch) | |
tree | 8a6cab1713f578146a9f2fbb77268cbe300233a1 /src | |
parent | 668f6b5fcffddf40550e2d85c53a249f0e1457e0 (diff) | |
download | libgd-672475e540da84100252e720291c63b30e028d06.tar.gz |
refactor/cleanup scaling 2 passes
Diffstat (limited to 'src')
-rw-r--r-- | src/gd.h | 11 | ||||
-rw-r--r-- | src/gd_interpolation.c | 263 |
2 files changed, 170 insertions, 104 deletions
@@ -93,6 +93,9 @@ extern "C" { # endif #endif +#undef ARG_NOT_USED +#define ARG_NOT_USED(arg) (void) arg + /* gd.h: declarations file for the graphic-draw module. * Permission to use, copy, modify, and distribute this software and its * documentation for any purpose and without fee is hereby granted, provided @@ -329,7 +332,11 @@ typedef enum { GD_TRIANGLE, GD_WEIGHTED4, GD_LINEAR, - GD_METHOD_COUNT = 23 + GD_LANCZOS3, + GD_LANCZOS8, + GD_BLACKMAN_BESSEL, + GD_BLACKMAN_SINC, + GD_METHOD_COUNT = 26 } gdInterpolationMethod; /** @@ -375,7 +382,7 @@ typedef const char *gdHeifChroma; /* define struct with name and func ptr and add it to gdImageStruct gdInterpolationMethod interpolation; */ /* Interpolation function ptr */ -typedef double (* interpolation_method )(double); +typedef double (* interpolation_method )(double, double); /* diff --git a/src/gd_interpolation.c b/src/gd_interpolation.c index 167607c..9daec91 100644 --- a/src/gd_interpolation.c +++ b/src/gd_interpolation.c @@ -133,31 +133,6 @@ typedef struct LineLength; /* Length of line (no. or rows / cols) */ } LineContribType; -/* Each core filter has its own radius */ -#define DEFAULT_FILTER_LINEAR 1.0f -#define DEFAULT_FILTER_BICUBIC 3.0f -#define DEFAULT_FILTER_BOX 0.5f -#define DEFAULT_FILTER_GENERALIZED_CUBIC 0.5f -#define DEFAULT_FILTER_RADIUS 1.0f -#define DEFAULT_LANCZOS8_RADIUS 8.0f -#define DEFAULT_LANCZOS3_RADIUS 3.0f -#define DEFAULT_HERMITE_RADIUS 1.0f -#define DEFAULT_BOX_RADIUS 0.5f -#define DEFAULT_TRIANGLE_RADIUS 1.0f -#define DEFAULT_BELL_RADIUS 1.5f -#define DEFAULT_CUBICSPLINE_RADIUS 2.0f -#define DEFAULT_MITCHELL_RADIUS 2.0f -#define DEFAULT_COSINE_RADIUS 1.0f -#define DEFAULT_CATMULLROM_RADIUS 2.0f -#define DEFAULT_QUADRATIC_RADIUS 1.5f -#define DEFAULT_QUADRATICBSPLINE_RADIUS 1.5f -#define DEFAULT_CUBICCONVOLUTION_RADIUS 3.0f -#define DEFAULT_GAUSSIAN_RADIUS 1.0f -#define DEFAULT_HANNING_RADIUS 1.0f -#define DEFAULT_HAMMING_RADIUS 1.0f -#define DEFAULT_SINC_RADIUS 1.0f -#define DEFAULT_WELSH_RADIUS 1.0f - static double KernelBessel_J1(const double x) { double p, q; @@ -290,20 +265,31 @@ static double KernelBessel_Order1(double x) return (q); } -static double filter_bessel(const double x) +static double filter_sinc(const double x, const double support) { + ARG_NOT_USED(support); + /* X-scaled Sinc(x) function. */ + if (x == 0.0) return(1.0); + return (sin(M_PI * (double) x) / (M_PI * (double) x)); +} + +static double filter_bessel(const double x, const double support) +{ + ARG_NOT_USED(support); if (x == 0.0f) return (double)(M_PI/4.0f); return (KernelBessel_Order1((double)M_PI*x)/(2.0f*x)); } -static double filter_blackman(const double x) +static double filter_blackman(const double x, const double support) { + ARG_NOT_USED(support); return (0.42f+0.5f*(double)cos(M_PI*x)+0.08f*(double)cos(2.0f*M_PI*x)); } -double filter_linear(const double x) { +double filter_linear(const double x, const double support) { + ARG_NOT_USED(support); double ax = fabs(x); if (ax < 1.0f) { return (1.0f - ax); @@ -311,6 +297,17 @@ double filter_linear(const double x) { return 0.0f; } +static double filter_blackman_bessel(const double x, const double support) +{ + ARG_NOT_USED(support); + return(filter_blackman(x/support,support)*filter_bessel(x,support)); +} + +static double filter_blackman_sinc(const double x, const double support) +{ + ARG_NOT_USED(support); + return(filter_blackman(x/support,support)*filter_sinc(x,support)); +} /** * Bicubic interpolation kernel (a=-1): @@ -323,10 +320,11 @@ double filter_linear(const double x) { \endverbatim * ***bd*** 2.2004 */ -static double filter_bicubic(const double t) +static double filter_bicubic(const double t, const double support) { const double abs_t = (double)fabs(t); const double abs_t_sq = abs_t * abs_t; + ARG_NOT_USED(support); if (abs_t<1) return 1-2*abs_t_sq+abs_t_sq*abs_t; if (abs_t<2) return 4 - 8*abs_t +5*abs_t_sq - abs_t_sq*abs_t; return 0; @@ -343,9 +341,9 @@ static double filter_bicubic(const double t) \endverbatim * Often used values for a are -1 and -1/2. */ -static double filter_generalized_cubic(const double t) +static double filter_generalized_cubic(const double t, const double support) { - const double a = -DEFAULT_FILTER_GENERALIZED_CUBIC; + const double a = -support; double abs_t = (double)fabs(t); double abs_t_sq = abs_t * abs_t; if (abs_t < 1) return (a + 2) * abs_t_sq * abs_t - (a + 3) * abs_t_sq + 1; @@ -355,8 +353,9 @@ static double filter_generalized_cubic(const double t) #ifdef FUNCTION_NOT_USED_YET /* CubicSpline filter, default radius 2 */ -static double filter_cubic_spline(const double x1) +static double filter_cubic_spline(const double x1, const double support) { + ARG_NOT_USED(support); const double x = x1 < 0.0 ? -x1 : x1; if (x < 1.0 ) { @@ -373,12 +372,12 @@ static double filter_cubic_spline(const double x1) #ifdef FUNCTION_NOT_USED_YET /* CubicConvolution filter, default radius 3 */ -static double filter_cubic_convolution(const double x1) +static double filter_cubic_convolution(const double x1, const double support) { const double x = x1 < 0.0 ? -x1 : x1; const double x2 = x1 * x1; const double x2_x = x2 * x; - + ARG_NOT_USED(support); if (x <= 1.0) return ((4.0 / 3.0)* x2_x - (7.0 / 3.0) * x2 + 1.0); if (x <= 2.0) return (- (7.0 / 12.0) * x2_x + 3 * x2 - (59.0 / 12.0) * x + 2.5); if (x <= 3.0) return ( (1.0/12.0) * x2_x - (2.0 / 3.0) * x2 + 1.75 * x - 1.5); @@ -386,16 +385,17 @@ static double filter_cubic_convolution(const double x1) } #endif -static double filter_box(double x) { - if (x < - DEFAULT_FILTER_BOX) +static double filter_box(double x, const double support) { + if (x < - support) return 0.0f; - if (x < DEFAULT_FILTER_BOX) + if (x < support) return 1.0f; return 0.0f; } -static double filter_catmullrom(const double x) +static double filter_catmullrom(const double x, const double support) { + ARG_NOT_USED(support); if (x < -2.0) return(0.0f); if (x < -1.0) @@ -409,55 +409,38 @@ static double filter_catmullrom(const double x) return(0.0f); } -#ifdef FUNCTION_NOT_USED_YET -static double filter_filter(double t) -{ - /* f(t) = 2|t|^3 - 3|t|^2 + 1, -1 <= t <= 1 */ - if(t < 0.0) t = -t; - if(t < 1.0) return((2.0 * t - 3.0) * t * t + 1.0); - return(0.0); -} -#endif - -#ifdef FUNCTION_NOT_USED_YET /* Lanczos8 filter, default radius 8 */ -static double filter_lanczos8(const double x1) +static double filter_lanczos8(const double x1, const double support) { const double x = x1 < 0.0 ? -x1 : x1; -#define R DEFAULT_LANCZOS8_RADIUS if ( x == 0.0) return 1; - if ( x < R) { - return R * sin(x*M_PI) * sin(x * M_PI/ R) / (x * M_PI * x * M_PI); + if ( x < support) { + return support * sin(x*M_PI) * sin(x * M_PI/ support) / (x * M_PI * x * M_PI); } return 0.0; -#undef R } -#endif -#ifdef FUNCTION_NOT_USED_YET /* Lanczos3 filter, default radius 3 */ -static double filter_lanczos3(const double x1) +static double filter_lanczos3(const double x1, const double support) { const double x = x1 < 0.0 ? -x1 : x1; -#define R DEFAULT_LANCZOS3_RADIUS if ( x == 0.0) return 1; - if ( x < R) + if ( x < support) { - return R * sin(x*M_PI) * sin(x * M_PI / R) / (x * M_PI * x * M_PI); + return support * sin(x*M_PI) * sin(x * M_PI / support) / (x * M_PI * x * M_PI); } return 0.0; -#undef R } -#endif /* Hermite filter, default radius 1 */ -static double filter_hermite(const double x1) +static double filter_hermite(const double x1, const double support) { const double x = x1 < 0.0 ? -x1 : x1; + ARG_NOT_USED(support); if (x < 1.0) return ((2.0 * x - 3) * x * x + 1.0 ); @@ -465,17 +448,20 @@ static double filter_hermite(const double x1) } /* Trangle filter, default radius 1 */ -static double filter_triangle(const double x1) +static double filter_triangle(const double x1, const double support) { const double x = x1 < 0.0 ? -x1 : x1; + ARG_NOT_USED(support); + if (x < 1.0) return (1.0 - x); return 0.0; } /* Bell filter, default radius 1.5 */ -static double filter_bell(const double x1) +static double filter_bell(const double x1, const double support) { const double x = x1 < 0.0 ? -x1 : x1; + ARG_NOT_USED(support); if (x < 0.5) return (0.75 - x*x); if (x < 1.5) return (0.5 * pow(x - 1.5, 2.0)); @@ -483,8 +469,9 @@ static double filter_bell(const double x1) } /* Mitchell filter, default radius 2.0 */ -static double filter_mitchell(const double x) +static double filter_mitchell(const double x, const double support) { + ARG_NOT_USED(support); #define KM_B (1.0f/3.0f) #define KM_C (1.0f/3.0f) #define KM_P0 (( 6.0f - 2.0f * KM_B ) / 6.0f) @@ -505,6 +492,7 @@ static double filter_mitchell(const double x) return(KM_P0+x*x*(KM_P2+x*KM_P3)); if (x < 2.0f) return(KM_Q0+x*(KM_Q1+x*(KM_Q2+x*KM_Q3))); + return(0.0f); } @@ -512,8 +500,9 @@ static double filter_mitchell(const double x) #ifdef FUNCTION_NOT_USED_YET /* Cosine filter, default radius 1 */ -static double filter_cosine(const double x) +static double filter_cosine(const double x, const double support) { + ARG_NOT_USED(support); if ((x >= -1.0) && (x <= 1.0)) return ((cos(x * M_PI) + 1.0)/2.0); return 0; @@ -521,17 +510,18 @@ static double filter_cosine(const double x) #endif /* Quadratic filter, default radius 1.5 */ -static double filter_quadratic(const double x1) +static double filter_quadratic(const double x1, const double support) { const double x = x1 < 0.0 ? -x1 : x1; - + ARG_NOT_USED(support); if (x <= 0.5) return (- 2.0 * x * x + 1); if (x <= 1.5) return (x * x - 2.5* x + 1.5); return 0.0; } -static double filter_bspline(const double x) +static double filter_bspline(const double x, const double support) { + ARG_NOT_USED(support); if (x>2.0f) { return 0.0f; } else { @@ -550,32 +540,33 @@ static double filter_bspline(const double x) } } -#ifdef FUNCTION_NOT_USED_YET /* QuadraticBSpline filter, default radius 1.5 */ -static double filter_quadratic_bspline(const double x1) +static double filter_quadratic_bspline(const double x1, const double support) { const double x = x1 < 0.0 ? -x1 : x1; - + ARG_NOT_USED(support); if (x <= 0.5) return (- x * x + 0.75); if (x <= 1.5) return (0.5 * x * x - 1.5 * x + 1.125); return 0.0; } -#endif -static double filter_gaussian(const double x) +static double filter_gaussian(const double x, const double support) { + ARG_NOT_USED(support); /* return(exp((double) (-2.0 * x * x)) * sqrt(2.0 / M_PI)); */ return (double)(exp(-2.0f * x * x) * 0.79788456080287f); } -static double filter_hanning(const double x) +static double filter_hanning(const double x, const double support) { + ARG_NOT_USED(support); /* A Cosine windowing function */ return(0.5 + 0.5 * cos(M_PI * x)); } -static double filter_hamming(const double x) +static double filter_hamming(const double x, const double support) { + ARG_NOT_USED(support); /* should be (0.54+0.46*cos(M_PI*(double) x)); but this approximation is sufficient */ @@ -588,23 +579,18 @@ static double filter_hamming(const double x) return 0.0f; } -static double filter_power(const double x) +static double filter_power(const double x, const double support) { + ARG_NOT_USED(support); const double a = 2.0f; if (fabs(x)>1) return 0.0f; return (1.0f - (double)fabs(pow(x,a))); } -static double filter_sinc(const double x) -{ - /* X-scaled Sinc(x) function. */ - if (x == 0.0) return(1.0); - return (sin(M_PI * (double) x) / (M_PI * (double) x)); -} - #ifdef FUNCTION_NOT_USED_YET -static double filter_welsh(const double x) +static double filter_welsh(const double x, const double support) { + ARG_NOT_USED(support); /* Welsh parabolic windowing filter */ if (x < 1.0) return(1 - x*x); @@ -744,7 +730,7 @@ static int getPixelInterpolateWeight(gdImagePtr im, const double x, const double * method - Interpolation method <gdInterpolationMethod> * * Returns: - * GD_TRUE if the affine is rectilinear or GD_FALSE + * the interpolated color or -1 on error * * See also: * <gdSetInterpolationMethod> @@ -775,10 +761,11 @@ int getPixelInterpolated(gdImagePtr im, const double x, const double y, const in return getPixelOverflowPalette(im, xi, yi, bgColor); } } + // TODO Add support if (im->interpolation) { for (i=0; i<4; i++) { - kernel_x[i] = (double) im->interpolation((double)(xi+i-1-x)); - kernel_y[i] = (double) im->interpolation((double)(yi+i-1-y)); + kernel_x[i] = (double) im->interpolation((double)(xi+i-1-x), 1.0); + kernel_y[i] = (double) im->interpolation((double)(yi+i-1-y), 1.0); } } else { return -1; @@ -874,11 +861,11 @@ static inline void _gdContributionsFree(LineContribType * p) gdFree(p); } -static inline LineContribType *_gdContributionsCalc(unsigned int line_size, unsigned int src_size, double scale_d, const interpolation_method pFilter) +static inline LineContribType *_gdContributionsCalc(unsigned int line_size, unsigned int src_size, double scale_d, const double support, const interpolation_method pFilter) { double width_d; double scale_f_d = 1.0; - const double filter_width_d = DEFAULT_BOX_RADIUS; + const double filter_width_d = support; int windows_size; unsigned int u; LineContribType *res; @@ -916,7 +903,7 @@ static inline LineContribType *_gdContributionsCalc(unsigned int line_size, unsi res->ContribRow[u].Right = iRight; for (iSrc = iLeft; iSrc <= iRight; iSrc++) { - dTotalWeight += (res->ContribRow[u].Weights[iSrc-iLeft] = scale_f_d * (*pFilter)(scale_f_d * (dCenter - (double)iSrc))); + dTotalWeight += (res->ContribRow[u].Weights[iSrc-iLeft] = scale_f_d * (*pFilter)(scale_f_d * (dCenter - (double)iSrc), support)); } if (dTotalWeight < 0.0) { @@ -979,7 +966,8 @@ static inline int _gdScalePass(const gdImagePtr pSrc, const unsigned int src_len, const gdImagePtr pDst, const unsigned int dst_len, const unsigned int num_lines, - const gdAxis axis) + const gdAxis axis, + const double support) { unsigned int line_ndx; LineContribType * contrib; @@ -989,6 +977,7 @@ _gdScalePass(const gdImagePtr pSrc, const unsigned int src_len, contrib = _gdContributionsCalc(dst_len, src_len, (double)dst_len / (double)src_len, + support, pSrc->interpolation); if (contrib == NULL) { return 0; @@ -1003,6 +992,38 @@ _gdScalePass(const gdImagePtr pSrc, const unsigned int src_len, }/* _gdScalePass*/ +/* Each core filter has its own radius */ +#define DEFAULT_FILTER_LINEAR 1.0f +#define DEFAULT_FILTER_BICUBIC 3.0f +#define DEFAULT_FILTER_BOX 0.5f +#define DEFAULT_FILTER_GENERALIZED_CUBIC 0.5f +#define DEFAULT_FILTER_RADIUS 1.0f +#define DEFAULT_LANCZOS8_RADIUS 8.0f +#define DEFAULT_LANCZOS3_RADIUS 3.0f +#define DEFAULT_HERMITE_RADIUS 1.0f +#define DEFAULT_BOX_RADIUS 0.5f +#define DEFAULT_TRIANGLE_RADIUS 1.0f +#define DEFAULT_BELL_RADIUS 1.5f +#define DEFAULT_CUBICSPLINE_RADIUS 2.0f +#define DEFAULT_MITCHELL_RADIUS 2.0f +#define DEFAULT_COSINE_RADIUS 1.0f +#define DEFAULT_CATMULLROM_RADIUS 2.0f +#define DEFAULT_QUADRATIC_RADIUS 1.5f +#define DEFAULT_QUADRATICBSPLINE_RADIUS 1.5f +#define DEFAULT_CUBICCONVOLUTION_RADIUS 3.0f +#define DEFAULT_GAUSSIAN_RADIUS 1.0f +#define DEFAULT_HANNING_RADIUS 1.0f +#define DEFAULT_HAMMING_RADIUS 1.0f +#define DEFAULT_SINC_RADIUS 1.0f +#define DEFAULT_WELSH_RADIUS 1.0f + +typedef struct _FilterInfo +{ + double + (*function)(const double,const double), + support; +} FilterInfo; + static gdImagePtr gdImageScaleTwoPass(const gdImagePtr src, const unsigned int new_width, const unsigned int new_height) @@ -1012,8 +1033,38 @@ gdImageScaleTwoPass(const gdImagePtr src, const unsigned int new_width, gdImagePtr tmp_im = NULL; gdImagePtr dst = NULL; int scale_pass_res; - - assert(src != NULL); + static const FilterInfo + filters[GD_METHOD_COUNT+1] = + { + { filter_box, 0.0 }, + { filter_bell, 1.5 }, + { filter_bessel, 0.0 }, + { NULL, 0.0 }, /* NA bilenear/bilinear fixed */ + { NULL, 0.0 }, /* NA bicubic */ + { NULL, 0.0 }, /* NA bicubic fixed */ + { filter_blackman, 1.0 }, + { filter_box, 0.5 }, + { filter_bspline, 1.5 }, + { filter_catmullrom, 2.0 }, + { filter_gaussian, 1.25 }, + { filter_generalized_cubic, 0.5 }, + { filter_hermite, 1.0 }, + { filter_hamming, 1.0 }, + { filter_hanning, 1.0 }, + { filter_mitchell, 2.0 }, + { NULL, 0.0}, /* NA Nearest */ + { filter_power, 0.0 }, + { filter_quadratic, 1.5 }, + { filter_sinc, 1.0 }, + { filter_triangle, 1.0 }, + { NULL, 1.0 }, /* NA weighted4 */ + { filter_linear, 1.0 }, + { filter_lanczos3, 3.0 }, + { filter_lanczos8, 8.0 }, + { filter_blackman_bessel, 3.2383 }, + { filter_blackman_sinc, 4.0 } + }; + const double support = filters[src->interpolation_id].support; /* First, handle the trivial case. */ if (src_width == new_width && src_height == new_height) { @@ -1029,13 +1080,14 @@ gdImageScaleTwoPass(const gdImagePtr src, const unsigned int new_width, if (src_width == new_width) { tmp_im = src; } else { + tmp_im = gdImageCreateTrueColor(new_width, src_height); if (tmp_im == NULL) { return NULL; } gdImageSetInterpolationMethod(tmp_im, src->interpolation_id); - scale_pass_res = _gdScalePass(src, src_width, tmp_im, new_width, src_height, HORIZONTAL); + scale_pass_res = _gdScalePass(src, src_width, tmp_im, new_width, src_height, HORIZONTAL, support); if (scale_pass_res != 1) { gdImageDestroy(tmp_im); return NULL; @@ -1052,7 +1104,7 @@ gdImageScaleTwoPass(const gdImagePtr src, const unsigned int new_width, dst = gdImageCreateTrueColor(new_width, new_height); if (dst != NULL) { gdImageSetInterpolationMethod(dst, src->interpolation_id); - scale_pass_res = _gdScalePass(tmp_im, src_height, dst, new_height, new_width, VERTICAL); + scale_pass_res = _gdScalePass(tmp_im, src_height, dst, new_height, new_width, VERTICAL, support); if (scale_pass_res != 1) { gdImageDestroy(dst); if (src != tmp_im) { @@ -2249,10 +2301,6 @@ BGD_DECLARE(int) gdImageSetInterpolationMethod(gdImagePtr im, gdInterpolationMet case GD_BESSEL: im->interpolation = filter_bessel; break; - case GD_BICUBIC_FIXED: - case GD_BICUBIC: - im->interpolation = filter_bicubic; - break; case GD_BLACKMAN: im->interpolation = filter_blackman; break; @@ -2295,6 +2343,18 @@ BGD_DECLARE(int) gdImageSetInterpolationMethod(gdImagePtr im, gdInterpolationMet case GD_TRIANGLE: im->interpolation = filter_triangle; break; + case GD_LANCZOS3: + im->interpolation = filter_lanczos3; + break; + case GD_LANCZOS8: + im->interpolation = filter_lanczos8; + break; + case GD_BLACKMAN_BESSEL: + im->interpolation = filter_blackman_bessel; + break; + case GD_BLACKMAN_SINC: + im->interpolation = filter_blackman_sinc; + break; case GD_DEFAULT: id = GD_LINEAR; im->interpolation = filter_linear; @@ -2306,7 +2366,6 @@ BGD_DECLARE(int) gdImageSetInterpolationMethod(gdImagePtr im, gdInterpolationMet return 1; } - /** * Function: gdImageGetInterpolationMethod * |