/* * Image Scaling Functions * Copyright (c) 2011 David A. Schleef * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * * Modified Lanczos scaling algorithm * ================================== * * This algorithm was developed by the author. The primary goals of * the algorithm are high-quality video downscaling for medium scale * factors (in the range of 1.3x to 5.0x) using methods that can be * converted to SIMD code. Concerns with existing algorithms were * mainly related to either over-soft filtering (Lanczos) or aliasing * (bilinear or any other method with inadequate sampling). * * The problems with bilinear scaling are apparent when downscaling * more than a factor of 2. For example, when downscaling by a factor * of 3, only two-thirds of the input pixels contribute to the output * pixels. This is only considering scaling in one direction; after * scaling both vertically and horizontally in a 2-D image, fewer than * half of the input pixels contribute to the output, so it should not * be surprising that the output is suboptimal. * * The problems with Lanczos scaling are more subtle. From a theoretical * perspective, Lanczos is an optimal algorithm for resampling equally- * spaced values. This theoretical perspective is based on analysis * done in frequency space, thus, Lanczos works very well for audio * resampling, since the ear hears primarily in frequency space. The * human visual system is sensitive primarily in the spatial domain, * therefore any resampling algorithm should take this into account. * This difference is immediately clear in the size of resampling * window or envelope that is chosen for resampling: for audio, an * envelope of a=64 is typical, in image scaling, the envelope is * usually a=2 or a=3. * * One result of the HVS being sensitive in the spatial domain (and * also probably due to oversampling capabilities of the retina and * visual cortex) is that it is less sensitive to the exact magnitude * of high-frequency visual signals than to the appropriate amount of * energy in the nearby frequency band. A Lanczos kernel with a=2 * or a=3 strongly decreases the amount of energy in the high frequency * bands. The energy in this area can be increased by increasing a, * which brings in energy from different areas of the image (bad for * reasons mentioned above), or by oversampling the input data. We * have chosen two methods for doing the latter. Firstly, there is * a sharpness parameter, which increases the cutoff frequency of the * filter, aliasing higher frequency noise into the passband. And * secondly, there is the sharpen parameter, which increases the * contribution of high-frequency (but in-band) components. * * An alternate explanation of the usefulness of a sharpening filter * is that many natural images have a roughly 1/f spectrum. In order * for a downsampled image to look more "natural" when high frequencies * are removed, the frequencies in the pass band near the cutoff * frequency are amplified, causing the spectrum to be more roughly * 1/f. I said "roughly", not "literally". * * This alternate explanation is useful for understanding the author's * secondary motivation for developing this algorithm, namely, as a * method of video compression. Several recent techniques (such as * HTTP Live Streaming and SVC) use image scaling as a method to get * increased compression out of nominally non-scalable codecs such as * H.264. For optimal quality, it is thusly important to consider * the scaler and encoder as a combined unit. Tuning of the sharpness * and sharpen parameters was performed using the Toro encoder tuner, * where scaled and encoded video was compared to unscaled and encoded * video. This tuning suggested values that were very close to the * values chosen by manual inspection of scaled images and video. * * The optimal values of sharpen and sharpness were slightly different * depending whether the comparison was still images or video. Video * comparisons were more sensitive to aliasing, since the aliasing * artifacts tended to move or "crawl" around the video. The default * values are for video; image scaling may prefer higher values. * * A number of related techniques were rejected for various reasons. * An early technique of selecting the sharpness factor locally based * on edge detection (in order to use a higher sharpness values without * the corresponding aliasing on edges) worked very well for still * images, but caused too much "crawling" on textures in video. Also, * this method is slow, as it does not parallelize well. * * Non-separable techniques were rejected because the fastest would * have been at least 4x slower. * * It is infrequently appreciated that image scaling should ideally be * done in linear light space. Converting to linear light space has * a similar effect to a sharpening filter. This approach was not * taken because the added benefit is minor compared to the additional * computational cost. Morever, the benefit is decreased by increasing * the strength of the sharpening filter. * */ #include #include "vs_scanline.h" #include "vs_image.h" #include "gstvideoscaleorc.h" #include #include #define NEED_CLAMP(x,a,b) ((x) < (a) || (x) > (b)) #define ROUND_UP_2(x) (((x)+1)&~1) #define ROUND_UP_4(x) (((x)+3)&~3) #define ROUND_UP_8(x) (((x)+7)&~7) #define SRC_LINE(i) (scale->src->pixels + scale->src->stride * (i)) #define TMP_LINE_S16(i) ((gint16 *)scale->tmpdata + (i)*(scale->dest->width)) #define TMP_LINE_S32(i) ((gint32 *)scale->tmpdata + (i)*(scale->dest->width)) #define TMP_LINE_FLOAT(i) ((float *)scale->tmpdata + (i)*(scale->dest->width)) #define TMP_LINE_DOUBLE(i) ((double *)scale->tmpdata + (i)*(scale->dest->width)) #define TMP_LINE_S16_AYUV(i) ((gint16 *)scale->tmpdata + (i)*4*(scale->dest->width)) #define TMP_LINE_S32_AYUV(i) ((gint32 *)scale->tmpdata + (i)*4*(scale->dest->width)) #define TMP_LINE_FLOAT_AYUV(i) ((float *)scale->tmpdata + (i)*4*(scale->dest->width)) #define TMP_LINE_DOUBLE_AYUV(i) ((double *)scale->tmpdata + (i)*4*(scale->dest->width)) #define PTR_OFFSET(a,b) ((void *)((char *)(a) + (b))) typedef void (*HorizResampleFunc) (void *dest, const gint32 * offsets, const void *taps, const void *src, int n_taps, int shift, int n); typedef struct _Scale1D Scale1D; struct _Scale1D { int n; double offset; double scale; double fx; double ex; int dx; int n_taps; gint32 *offsets; void *taps; }; typedef struct _Scale Scale; struct _Scale { const VSImage *dest; const VSImage *src; double sharpness; gboolean dither; void *tmpdata; HorizResampleFunc horiz_resample_func; Scale1D x_scale1d; Scale1D y_scale1d; }; static void vs_image_scale_lanczos_Y_int16 (const VSImage * dest, const VSImage * src, uint8_t * tmpbuf, double sharpness, gboolean dither, double a, double sharpen); static void vs_image_scale_lanczos_Y_int32 (const VSImage * dest, const VSImage * src, uint8_t * tmpbuf, double sharpness, gboolean dither, double a, double sharpen); static void vs_image_scale_lanczos_Y_float (const VSImage * dest, const VSImage * src, uint8_t * tmpbuf, double sharpness, gboolean dither, double a, double sharpen); static void vs_image_scale_lanczos_Y_double (const VSImage * dest, const VSImage * src, uint8_t * tmpbuf, double sharpness, gboolean dither, double a, double sharpen); static void vs_image_scale_lanczos_AYUV_int16 (const VSImage * dest, const VSImage * src, uint8_t * tmpbuf, double sharpness, gboolean dither, double a, double sharpen); static void vs_image_scale_lanczos_AYUV_int32 (const VSImage * dest, const VSImage * src, uint8_t * tmpbuf, double sharpness, gboolean dither, double a, double sharpen); static void vs_image_scale_lanczos_AYUV_float (const VSImage * dest, const VSImage * src, uint8_t * tmpbuf, double sharpness, gboolean dither, double a, double sharpen); static void vs_image_scale_lanczos_AYUV_double (const VSImage * dest, const VSImage * src, uint8_t * tmpbuf, double sharpness, gboolean dither, double a, double sharpen); static void vs_image_scale_lanczos_AYUV64_double (const VSImage * dest, const VSImage * src, uint8_t * tmpbuf, double sharpness, gboolean dither, double a, double sharpen); static double sinc (double x) { if (x == 0) return 1; return sin (G_PI * x) / (G_PI * x); } static double envelope (double x) { if (x <= -1 || x >= 1) return 0; return sinc (x); } static int scale1d_get_n_taps (int src_size, int dest_size, double a, double sharpness) { double scale; double fx; int dx; scale = src_size / (double) dest_size; if (scale > 1.0) { fx = (1.0 / scale) * sharpness; } else { fx = (1.0) * sharpness; } dx = ceil (a / fx); return 2 * dx; } static void scale1d_cleanup (Scale1D * scale) { g_free (scale->taps); g_free (scale->offsets); } /* * Calculates a set of taps for each destination element in double * format. Each set of taps sums to 1.0. * */ static void scale1d_calculate_taps (Scale1D * scale, int src_size, int dest_size, int n_taps, double a, double sharpness, double sharpen) { int j; double *tap_array; gint32 *offsets; double scale_offset; double scale_increment; int dx; double fx; double ex; scale->scale = src_size / (double) dest_size; scale->offset = scale->scale / 2 - 0.5; if (scale->scale > 1.0) { scale->fx = (1.0 / scale->scale) * sharpness; } else { scale->fx = (1.0) * sharpness; } scale->ex = scale->fx / a; scale->dx = ceil (a / scale->fx); g_assert (n_taps >= 2 * scale->dx); scale->n_taps = n_taps; scale->taps = g_malloc (sizeof (double) * scale->n_taps * dest_size); scale->offsets = g_malloc (sizeof (gint32) * dest_size); tap_array = scale->taps; offsets = scale->offsets; scale_offset = scale->offset; scale_increment = scale->scale; dx = scale->dx; fx = scale->fx; ex = scale->ex; for (j = 0; j < dest_size; j++) { double x; int xi; int l; double weight; double *taps; x = scale_offset + scale_increment * j; x = CLAMP (x, 0, src_size); xi = ceil (x) - dx; offsets[j] = xi; weight = 0; taps = tap_array + j * n_taps; for (l = 0; l < n_taps; l++) { int xl = xi + l; taps[l] = sinc ((x - xl) * fx) * envelope ((x - xl) * ex); taps[l] -= sharpen * envelope ((x - xl) * ex); weight += taps[l]; } g_assert (envelope ((x - (xi - 1)) * ex) == 0); g_assert (envelope ((x - (xi + n_taps)) * ex) == 0); for (l = 0; l < n_taps; l++) { taps[l] /= weight; } if (xi < 0) { int shift = -xi; for (l = 0; l < shift; l++) { taps[shift] += taps[l]; } for (l = 0; l < n_taps - shift; l++) { taps[l] = taps[shift + l]; } for (; l < n_taps; l++) { taps[l] = 0; } offsets[j] += shift; } if (xi > src_size - n_taps) { int shift = xi - (src_size - n_taps); for (l = 0; l < shift; l++) { taps[n_taps - shift - 1] += taps[n_taps - shift + l]; } for (l = 0; l < n_taps - shift; l++) { taps[n_taps - 1 - l] = taps[n_taps - 1 - shift - l]; } for (l = 0; l < shift; l++) { taps[l] = 0; } offsets[j] -= shift; } } } /* * Calculates a set of taps for each destination element in float * format. Each set of taps sums to 1.0. */ static void scale1d_calculate_taps_float (Scale1D * scale, int src_size, int dest_size, int n_taps, double a, double sharpness, double sharpen) { double *taps_d; float *taps_f; int j; scale1d_calculate_taps (scale, src_size, dest_size, n_taps, a, sharpness, sharpen); taps_d = scale->taps; taps_f = g_malloc (sizeof (float) * scale->n_taps * dest_size); for (j = 0; j < dest_size * n_taps; j++) { taps_f[j] = taps_d[j]; } g_free (taps_d); scale->taps = taps_f; } /* * Calculates a set of taps for each destination element in gint32 * format. Each set of taps sums to (very nearly) (1<taps; taps_i = g_malloc (sizeof (gint32) * scale->n_taps * dest_size); multiplier = (1 << shift); for (j = 0; j < dest_size; j++) { for (i = 0; i < n_taps; i++) { taps_i[j * n_taps + i] = floor (0.5 + taps_d[j * n_taps + i] * multiplier); } } g_free (taps_d); scale->taps = taps_i; } /* * Calculates a set of taps for each destination element in gint16 * format. Each set of taps sums to (1<taps; taps_i = g_malloc (sizeof (gint16) * scale->n_taps * dest_size); multiplier = (1 << shift); /* Various methods for converting floating point taps to integer. * The dB values are the SSIM value between scaling an image via * the floating point pathway vs. the integer pathway using the * given code to generate the taps. Only one image was tested, * scaling from 1920x1080 to 640x360. Several variations of the * methods were also tested, with nothing appearing useful. */ #if 0 /* Standard round to integer. This causes bad DC errors. */ /* 44.588 dB */ for (j = 0; j < dest_size; j++) { for (i = 0; i < n_taps; i++) { taps_i[j * n_taps + i] = floor (0.5 + taps_d[j * n_taps + i] * multiplier); } } #endif #if 0 /* Dithering via error propogation. Works pretty well, but * really we want to propogate errors across rows, which would * mean having several sets of tap arrays. Possible, but more work, * and it may not even be better. */ /* 57.0961 dB */ { double err = 0; for (j = 0; j < dest_size; j++) { for (i = 0; i < n_taps; i++) { err += taps_d[j * n_taps + i] * multiplier; taps_i[j * n_taps + i] = floor (err); err -= floor (err); } } } #endif #if 1 /* Round to integer, but with an adjustable bias that we use to * eliminate the DC error. This search method is a bit crude, and * could perhaps be improved somewhat. */ /* 60.4851 dB */ for (j = 0; j < dest_size; j++) { int k; for (k = 0; k < 100; k++) { int sum = 0; double offset; offset = k * 0.01; for (i = 0; i < n_taps; i++) { taps_i[j * n_taps + i] = floor (offset + taps_d[j * n_taps + i] * multiplier); sum += taps_i[j * n_taps + i]; } if (sum >= (1 << shift)) break; } } #endif #if 0 /* Round to integer, but adjust the multiplier. The search method is * wrong a lot, but was sufficient enough to calculate dB error. */ /* 58.6517 dB */ for (j = 0; j < dest_size; j++) { int k; int sum = 0; for (k = 0; k < 200; k++) { sum = 0; multiplier = (1 << shift) - 1.0 + k * 0.01; for (i = 0; i < n_taps; i++) { taps_i[j * n_taps + i] = floor (0.5 + taps_d[j * n_taps + i] * multiplier); sum += taps_i[j * n_taps + i]; } if (sum >= (1 << shift)) break; } if (sum != (1 << shift)) { GST_ERROR ("%g %d", multiplier, sum); } } #endif #if 0 /* Round to integer, but subtract the error from the largest tap */ /* 58.3677 dB */ for (j = 0; j < dest_size; j++) { int err = -multiplier; for (i = 0; i < n_taps; i++) { taps_i[j * n_taps + i] = floor (0.5 + taps_d[j * n_taps + i] * multiplier); err += taps_i[j * n_taps + i]; } if (taps_i[j * n_taps + (n_taps / 2 - 1)] > taps_i[j * n_taps + (n_taps / 2)]) { taps_i[j * n_taps + (n_taps / 2 - 1)] -= err; } else { taps_i[j * n_taps + (n_taps / 2)] -= err; } } #endif g_free (taps_d); scale->taps = taps_i; } void vs_image_scale_lanczos_Y (const VSImage * dest, const VSImage * src, uint8_t * tmpbuf, double sharpness, gboolean dither, int submethod, double a, double sharpen) { switch (submethod) { case 0: default: vs_image_scale_lanczos_Y_int16 (dest, src, tmpbuf, sharpness, dither, a, sharpen); break; case 1: vs_image_scale_lanczos_Y_int32 (dest, src, tmpbuf, sharpness, dither, a, sharpen); break; case 2: vs_image_scale_lanczos_Y_float (dest, src, tmpbuf, sharpness, dither, a, sharpen); break; case 3: vs_image_scale_lanczos_Y_double (dest, src, tmpbuf, sharpness, dither, a, sharpen); break; } } void vs_image_scale_lanczos_AYUV (const VSImage * dest, const VSImage * src, uint8_t * tmpbuf, double sharpness, gboolean dither, int submethod, double a, double sharpen) { switch (submethod) { case 0: default: vs_image_scale_lanczos_AYUV_int16 (dest, src, tmpbuf, sharpness, dither, a, sharpen); break; case 1: vs_image_scale_lanczos_AYUV_int32 (dest, src, tmpbuf, sharpness, dither, a, sharpen); break; case 2: vs_image_scale_lanczos_AYUV_float (dest, src, tmpbuf, sharpness, dither, a, sharpen); break; case 3: vs_image_scale_lanczos_AYUV_double (dest, src, tmpbuf, sharpness, dither, a, sharpen); break; } } void vs_image_scale_lanczos_AYUV64 (const VSImage * dest, const VSImage * src, uint8_t * tmpbuf, double sharpness, gboolean dither, int submethod, double a, double sharpen) { vs_image_scale_lanczos_AYUV64_double (dest, src, tmpbuf, sharpness, dither, a, sharpen); } #define RESAMPLE_HORIZ_FLOAT(function, dest_type, tap_type, src_type, _n_taps) \ static void \ function (dest_type *dest, const gint32 *offsets, \ const tap_type *taps, const src_type *src, int n_taps, int shift, int n) \ { \ int i; \ int k; \ dest_type sum; \ const src_type *srcline; \ const tap_type *tapsline; \ for (i = 0; i < n; i++) { \ srcline = src + offsets[i]; \ tapsline = taps + i * _n_taps; \ sum = 0; \ for (k = 0; k < _n_taps; k++) { \ sum += srcline[k] * tapsline[k]; \ } \ dest[i] = sum; \ } \ } #define RESAMPLE_HORIZ(function, dest_type, tap_type, src_type, _n_taps, _shift) \ static void \ function (dest_type *dest, const gint32 *offsets, \ const tap_type *taps, const src_type *src, int n_taps, int shift, int n) \ { \ int i; \ int k; \ dest_type sum; \ const src_type *srcline; \ const tap_type *tapsline; \ int offset; \ if (_shift > 0) offset = (1<<_shift)>>1; \ else offset = 0; \ for (i = 0; i < n; i++) { \ srcline = src + offsets[i]; \ tapsline = taps + i * _n_taps; \ sum = 0; \ for (k = 0; k < _n_taps; k++) { \ sum += srcline[k] * tapsline[k]; \ } \ dest[i] = (sum + offset) >> _shift; \ } \ } #define RESAMPLE_HORIZ_AYUV_FLOAT(function, dest_type, tap_type, src_type, _n_taps) \ static void \ function (dest_type *dest, const gint32 *offsets, \ const tap_type *taps, const src_type *src, int n_taps, int shift, int n) \ { \ int i; \ int k; \ dest_type sum1; \ dest_type sum2; \ dest_type sum3; \ dest_type sum4; \ const src_type *srcline; \ const tap_type *tapsline; \ for (i = 0; i < n; i++) { \ srcline = src + 4*offsets[i]; \ tapsline = taps + i * _n_taps; \ sum1 = 0; \ sum2 = 0; \ sum3 = 0; \ sum4 = 0; \ for (k = 0; k < _n_taps; k++) { \ sum1 += srcline[k*4+0] * tapsline[k]; \ sum2 += srcline[k*4+1] * tapsline[k]; \ sum3 += srcline[k*4+2] * tapsline[k]; \ sum4 += srcline[k*4+3] * tapsline[k]; \ } \ dest[i*4+0] = sum1; \ dest[i*4+1] = sum2; \ dest[i*4+2] = sum3; \ dest[i*4+3] = sum4; \ } \ } #define RESAMPLE_HORIZ_AYUV(function, dest_type, tap_type, src_type, _n_taps, _shift) \ static void \ function (dest_type *dest, const gint32 *offsets, \ const tap_type *taps, const src_type *src, int n_taps, int shift, int n) \ { \ int i; \ int k; \ dest_type sum1; \ dest_type sum2; \ dest_type sum3; \ dest_type sum4; \ const src_type *srcline; \ const tap_type *tapsline; \ int offset; \ if (_shift > 0) offset = (1<<_shift)>>1; \ else offset = 0; \ for (i = 0; i < n; i++) { \ srcline = src + 4*offsets[i]; \ tapsline = taps + i * _n_taps; \ sum1 = 0; \ sum2 = 0; \ sum3 = 0; \ sum4 = 0; \ for (k = 0; k < _n_taps; k++) { \ sum1 += srcline[k*4+0] * tapsline[k]; \ sum2 += srcline[k*4+1] * tapsline[k]; \ sum3 += srcline[k*4+2] * tapsline[k]; \ sum4 += srcline[k*4+3] * tapsline[k]; \ } \ dest[i*4+0] = (sum1 + offset) >> _shift; \ dest[i*4+1] = (sum2 + offset) >> _shift; \ dest[i*4+2] = (sum3 + offset) >> _shift; \ dest[i*4+3] = (sum4 + offset) >> _shift; \ } \ } /* *INDENT-OFF* */ RESAMPLE_HORIZ_FLOAT (resample_horiz_double_u8_generic, double, double, guint8, n_taps) RESAMPLE_HORIZ_FLOAT (resample_horiz_float_u8_generic, float, float, guint8, n_taps) RESAMPLE_HORIZ_AYUV_FLOAT (resample_horiz_double_ayuv_generic, double, double, guint8, n_taps) RESAMPLE_HORIZ_AYUV_FLOAT (resample_horiz_float_ayuv_generic, float, float, guint8, n_taps) RESAMPLE_HORIZ_AYUV_FLOAT (resample_horiz_double_ayuv_generic_s16, double, double, guint16, n_taps) RESAMPLE_HORIZ (resample_horiz_int32_int32_u8_generic, gint32, gint32, guint8, n_taps, shift) RESAMPLE_HORIZ (resample_horiz_int16_int16_u8_generic, gint16, gint16, guint8, n_taps, shift) RESAMPLE_HORIZ_AYUV (resample_horiz_int32_int32_ayuv_generic, gint32, gint32, guint8, n_taps, shift) RESAMPLE_HORIZ_AYUV (resample_horiz_int16_int16_ayuv_generic, gint16, gint16, guint8, n_taps, shift) /* Candidates for orcification */ RESAMPLE_HORIZ (resample_horiz_int32_int32_u8_taps16_shift0, gint32, gint32, guint8, 16, 0) RESAMPLE_HORIZ (resample_horiz_int32_int32_u8_taps12_shift0, gint32, gint32, guint8, 12, 0) RESAMPLE_HORIZ (resample_horiz_int32_int32_u8_taps8_shift0, gint32, gint32, guint8, 8, 0) RESAMPLE_HORIZ (resample_horiz_int32_int32_u8_taps4_shift0, gint32, gint32, guint8, 4, 0) RESAMPLE_HORIZ (resample_horiz_int16_int16_u8_taps16_shift0, gint16, gint16, guint8, 16, 0) RESAMPLE_HORIZ (resample_horiz_int16_int16_u8_taps12_shift0, gint16, gint16, guint8, 12, 0) RESAMPLE_HORIZ (resample_horiz_int16_int16_u8_taps8_shift0, gint16, gint16, guint8, 8, 0) RESAMPLE_HORIZ (resample_horiz_int16_int16_u8_taps4_shift0, gint16, gint16, guint8, 4, 0) RESAMPLE_HORIZ_AYUV (resample_horiz_int32_int32_ayuv_taps16_shift0, gint32, gint32, guint8, 16, 0) RESAMPLE_HORIZ_AYUV (resample_horiz_int32_int32_ayuv_taps12_shift0, gint32, gint32, guint8, 12, 0) RESAMPLE_HORIZ_AYUV (resample_horiz_int32_int32_ayuv_taps8_shift0, gint32, gint32, guint8, 8, 0) RESAMPLE_HORIZ_AYUV (resample_horiz_int32_int32_ayuv_taps4_shift0, gint32, gint32, guint8, 4, 0) RESAMPLE_HORIZ_AYUV (resample_horiz_int16_int16_ayuv_taps16_shift0, gint16, gint16, guint8, 16, 0) RESAMPLE_HORIZ_AYUV (resample_horiz_int16_int16_ayuv_taps12_shift0, gint16, gint16, guint8, 12, 0) RESAMPLE_HORIZ_AYUV (resample_horiz_int16_int16_ayuv_taps8_shift0, gint16, gint16, guint8, 8, 0) RESAMPLE_HORIZ_AYUV (resample_horiz_int16_int16_ayuv_taps4_shift0, gint16, gint16, guint8, 4, 0) /* *INDENT-ON* */ #define RESAMPLE_VERT(function, tap_type, src_type, _n_taps, _shift) \ static void \ function (guint8 *dest, \ const tap_type *taps, const src_type *src, int stride, int n_taps, \ int shift, int n) \ { \ int i; \ int l; \ gint32 sum_y; \ gint32 offset = (1<<_shift) >> 1; \ for (i = 0; i < n; i++) { \ sum_y = 0; \ for (l = 0; l < n_taps; l++) { \ const src_type *line = PTR_OFFSET(src, stride * l); \ sum_y += line[i] * taps[l]; \ } \ dest[i] = CLAMP ((sum_y + offset) >> _shift, 0, 255); \ } \ } #define RESAMPLE_VERT_DITHER(function, tap_type, src_type, _n_taps, _shift) \ static void \ function (guint8 *dest, \ const tap_type *taps, const src_type *src, int stride, int n_taps, \ int shift, int n) \ { \ int i; \ int l; \ gint32 sum_y; \ gint32 err_y = 0; \ gint32 mask = (1<<_shift) - 1; \ for (i = 0; i < n; i++) { \ sum_y = 0; \ for (l = 0; l < n_taps; l++) { \ const src_type *line = PTR_OFFSET(src, stride * l); \ sum_y += line[i] * taps[l]; \ } \ err_y += sum_y; \ dest[i] = CLAMP (err_y >> _shift, 0, 255); \ err_y &= mask; \ } \ } /* *INDENT-OFF* */ RESAMPLE_VERT (resample_vert_int32_generic, gint32, gint32, n_taps, shift) RESAMPLE_VERT_DITHER (resample_vert_dither_int32_generic, gint32, gint32, n_taps, shift) RESAMPLE_VERT (resample_vert_int16_generic, gint16, gint16, n_taps, shift); RESAMPLE_VERT_DITHER (resample_vert_dither_int16_generic, gint16, gint16, n_taps, shift) /* *INDENT-ON* */ #define RESAMPLE_VERT_FLOAT(function, dest_type, clamp, tap_type, src_type, _n_taps, _shift) \ static void \ function (dest_type *dest, \ const tap_type *taps, const src_type *src, int stride, int n_taps, \ int shift, int n) \ { \ int i; \ int l; \ src_type sum_y; \ for (i = 0; i < n; i++) { \ sum_y = 0; \ for (l = 0; l < n_taps; l++) { \ const src_type *line = PTR_OFFSET(src, stride * l); \ sum_y += line[i] * taps[l]; \ } \ dest[i] = CLAMP (floor(0.5 + sum_y), 0, clamp); \ } \ } #define RESAMPLE_VERT_FLOAT_DITHER(function, dest_type, clamp, tap_type, src_type, _n_taps, _shift) \ static void \ function (dest_type *dest, \ const tap_type *taps, const src_type *src, int stride, int n_taps, \ int shift, int n) \ { \ int i; \ int l; \ src_type sum_y; \ src_type err_y = 0; \ for (i = 0; i < n; i++) { \ sum_y = 0; \ for (l = 0; l < n_taps; l++) { \ const src_type *line = PTR_OFFSET(src, stride * l); \ sum_y += line[i] * taps[l]; \ } \ err_y += sum_y; \ dest[i] = CLAMP (floor (err_y), 0, clamp); \ err_y -= floor (err_y); \ } \ } /* *INDENT-OFF* */ RESAMPLE_VERT_FLOAT (resample_vert_double_generic, guint8, 255, double, double, n_taps, shift) RESAMPLE_VERT_FLOAT_DITHER (resample_vert_dither_double_generic, guint8, 255, double, double, n_taps, shift) RESAMPLE_VERT_FLOAT (resample_vert_double_generic_u16, guint16, 65535, double, double, n_taps, shift) RESAMPLE_VERT_FLOAT_DITHER (resample_vert_dither_double_generic_u16, guint16, 65535, double, double, n_taps, shift) RESAMPLE_VERT_FLOAT (resample_vert_float_generic, guint8, 255, float, float, n_taps, shift) RESAMPLE_VERT_FLOAT_DITHER (resample_vert_dither_float_generic, guint8, 255, float, float, n_taps, shift) /* *INDENT-ON* */ #define S16_SHIFT1 7 #define S16_SHIFT2 7 #define S16_MIDSHIFT 0 #define S16_POSTSHIFT (S16_SHIFT1+S16_SHIFT2-S16_MIDSHIFT) static void vs_scale_lanczos_Y_int16 (Scale * scale) { int j; int yi; int tmp_yi; tmp_yi = 0; for (j = 0; j < scale->dest->height; j++) { guint8 *destline; gint16 *taps; destline = scale->dest->pixels + scale->dest->stride * j; yi = scale->y_scale1d.offsets[j]; while (tmp_yi < yi + scale->y_scale1d.n_taps) { scale->horiz_resample_func (TMP_LINE_S16 (tmp_yi), scale->x_scale1d.offsets, scale->x_scale1d.taps, SRC_LINE (tmp_yi), scale->x_scale1d.n_taps, S16_MIDSHIFT, scale->dest->width); tmp_yi++; } taps = (gint16 *) scale->y_scale1d.taps + j * scale->y_scale1d.n_taps; if (scale->dither) { resample_vert_dither_int16_generic (destline, taps, TMP_LINE_S16 (scale->y_scale1d.offsets[j]), sizeof (gint16) * scale->dest->width, scale->y_scale1d.n_taps, S16_POSTSHIFT, scale->dest->width); } else { resample_vert_int16_generic (destline, taps, TMP_LINE_S16 (scale->y_scale1d.offsets[j]), sizeof (gint16) * scale->dest->width, scale->y_scale1d.n_taps, S16_POSTSHIFT, scale->dest->width); } } } void vs_image_scale_lanczos_Y_int16 (const VSImage * dest, const VSImage * src, uint8_t * tmpbuf, double sharpness, gboolean dither, double a, double sharpen) { Scale s = { 0 }; Scale *scale = &s; int n_taps; scale->dest = dest; scale->src = src; n_taps = scale1d_get_n_taps (src->width, dest->width, a, sharpness); n_taps = ROUND_UP_4 (n_taps); scale1d_calculate_taps_int16 (&scale->x_scale1d, src->width, dest->width, n_taps, a, sharpness, sharpen, S16_SHIFT1); n_taps = scale1d_get_n_taps (src->height, dest->height, a, sharpness); scale1d_calculate_taps_int16 (&scale->y_scale1d, src->height, dest->height, n_taps, a, sharpness, sharpen, S16_SHIFT2); scale->dither = dither; switch (scale->x_scale1d.n_taps) { case 4: scale->horiz_resample_func = (HorizResampleFunc) resample_horiz_int16_int16_u8_taps4_shift0; break; case 8: scale->horiz_resample_func = (HorizResampleFunc) resample_horiz_int16_int16_u8_taps8_shift0; break; case 12: scale->horiz_resample_func = (HorizResampleFunc) resample_horiz_int16_int16_u8_taps12_shift0; break; case 16: scale->horiz_resample_func = (HorizResampleFunc) resample_horiz_int16_int16_u8_taps16_shift0; break; default: scale->horiz_resample_func = (HorizResampleFunc) resample_horiz_int16_int16_u8_generic; break; } scale->tmpdata = g_malloc (sizeof (gint16) * scale->dest->width * scale->src->height); vs_scale_lanczos_Y_int16 (scale); scale1d_cleanup (&scale->x_scale1d); scale1d_cleanup (&scale->y_scale1d); g_free (scale->tmpdata); } #define S32_SHIFT1 11 #define S32_SHIFT2 11 #define S32_MIDSHIFT 0 #define S32_POSTSHIFT (S32_SHIFT1+S32_SHIFT2-S32_MIDSHIFT) static void vs_scale_lanczos_Y_int32 (Scale * scale) { int j; int yi; int tmp_yi; tmp_yi = 0; for (j = 0; j < scale->dest->height; j++) { guint8 *destline; gint32 *taps; destline = scale->dest->pixels + scale->dest->stride * j; yi = scale->y_scale1d.offsets[j]; while (tmp_yi < yi + scale->y_scale1d.n_taps) { scale->horiz_resample_func (TMP_LINE_S32 (tmp_yi), scale->x_scale1d.offsets, scale->x_scale1d.taps, SRC_LINE (tmp_yi), scale->x_scale1d.n_taps, S32_MIDSHIFT, scale->dest->width); tmp_yi++; } taps = (gint32 *) scale->y_scale1d.taps + j * scale->y_scale1d.n_taps; if (scale->dither) { resample_vert_dither_int32_generic (destline, taps, TMP_LINE_S32 (scale->y_scale1d.offsets[j]), sizeof (gint32) * scale->dest->width, scale->y_scale1d.n_taps, S32_POSTSHIFT, scale->dest->width); } else { resample_vert_int32_generic (destline, taps, TMP_LINE_S32 (scale->y_scale1d.offsets[j]), sizeof (gint32) * scale->dest->width, scale->y_scale1d.n_taps, S32_POSTSHIFT, scale->dest->width); } } } void vs_image_scale_lanczos_Y_int32 (const VSImage * dest, const VSImage * src, uint8_t * tmpbuf, double sharpness, gboolean dither, double a, double sharpen) { Scale s = { 0 }; Scale *scale = &s; int n_taps; scale->dest = dest; scale->src = src; n_taps = scale1d_get_n_taps (src->width, dest->width, a, sharpness); n_taps = ROUND_UP_4 (n_taps); scale1d_calculate_taps_int32 (&scale->x_scale1d, src->width, dest->width, n_taps, a, sharpness, sharpen, S32_SHIFT1); n_taps = scale1d_get_n_taps (src->height, dest->height, a, sharpness); scale1d_calculate_taps_int32 (&scale->y_scale1d, src->height, dest->height, n_taps, a, sharpness, sharpen, S32_SHIFT2); scale->dither = dither; switch (scale->x_scale1d.n_taps) { case 4: scale->horiz_resample_func = (HorizResampleFunc) resample_horiz_int32_int32_u8_taps4_shift0; break; case 8: scale->horiz_resample_func = (HorizResampleFunc) resample_horiz_int32_int32_u8_taps8_shift0; break; case 12: scale->horiz_resample_func = (HorizResampleFunc) resample_horiz_int32_int32_u8_taps12_shift0; break; case 16: scale->horiz_resample_func = (HorizResampleFunc) resample_horiz_int32_int32_u8_taps16_shift0; break; default: scale->horiz_resample_func = (HorizResampleFunc) resample_horiz_int32_int32_u8_generic; break; } scale->tmpdata = g_malloc (sizeof (int32_t) * scale->dest->width * scale->src->height); vs_scale_lanczos_Y_int32 (scale); scale1d_cleanup (&scale->x_scale1d); scale1d_cleanup (&scale->y_scale1d); g_free (scale->tmpdata); } static void vs_scale_lanczos_Y_double (Scale * scale) { int j; int yi; int tmp_yi; tmp_yi = 0; for (j = 0; j < scale->dest->height; j++) { guint8 *destline; double *taps; destline = scale->dest->pixels + scale->dest->stride * j; yi = scale->y_scale1d.offsets[j]; while (tmp_yi < yi + scale->y_scale1d.n_taps) { scale->horiz_resample_func (TMP_LINE_DOUBLE (tmp_yi), scale->x_scale1d.offsets, scale->x_scale1d.taps, SRC_LINE (tmp_yi), scale->x_scale1d.n_taps, 0, scale->dest->width); tmp_yi++; } taps = (double *) scale->y_scale1d.taps + j * scale->y_scale1d.n_taps; if (scale->dither) { resample_vert_dither_double_generic (destline, taps, TMP_LINE_DOUBLE (scale->y_scale1d.offsets[j]), sizeof (double) * scale->dest->width, scale->y_scale1d.n_taps, 0, scale->dest->width); } else { resample_vert_double_generic (destline, taps, TMP_LINE_DOUBLE (scale->y_scale1d.offsets[j]), sizeof (double) * scale->dest->width, scale->y_scale1d.n_taps, 0, scale->dest->width); } } } void vs_image_scale_lanczos_Y_double (const VSImage * dest, const VSImage * src, uint8_t * tmpbuf, double sharpness, gboolean dither, double a, double sharpen) { Scale s = { 0 }; Scale *scale = &s; int n_taps; scale->dest = dest; scale->src = src; n_taps = scale1d_get_n_taps (src->width, dest->width, a, sharpness); scale1d_calculate_taps (&scale->x_scale1d, src->width, dest->width, n_taps, a, sharpness, sharpen); n_taps = scale1d_get_n_taps (src->height, dest->height, a, sharpness); scale1d_calculate_taps (&scale->y_scale1d, src->height, dest->height, n_taps, a, sharpness, sharpen); scale->dither = dither; scale->horiz_resample_func = (HorizResampleFunc) resample_horiz_double_u8_generic; scale->tmpdata = g_malloc (sizeof (double) * scale->dest->width * scale->src->height); vs_scale_lanczos_Y_double (scale); scale1d_cleanup (&scale->x_scale1d); scale1d_cleanup (&scale->y_scale1d); g_free (scale->tmpdata); } static void vs_scale_lanczos_Y_float (Scale * scale) { int j; int yi; int tmp_yi; tmp_yi = 0; for (j = 0; j < scale->dest->height; j++) { guint8 *destline; float *taps; destline = scale->dest->pixels + scale->dest->stride * j; yi = scale->y_scale1d.offsets[j]; while (tmp_yi < yi + scale->y_scale1d.n_taps) { scale->horiz_resample_func (TMP_LINE_FLOAT (tmp_yi), scale->x_scale1d.offsets, scale->x_scale1d.taps, SRC_LINE (tmp_yi), scale->x_scale1d.n_taps, 0, scale->dest->width); tmp_yi++; } taps = (float *) scale->y_scale1d.taps + j * scale->y_scale1d.n_taps; if (scale->dither) { resample_vert_dither_float_generic (destline, taps, TMP_LINE_FLOAT (scale->y_scale1d.offsets[j]), sizeof (float) * scale->dest->width, scale->y_scale1d.n_taps, 0, scale->dest->width); } else { resample_vert_float_generic (destline, taps, TMP_LINE_FLOAT (scale->y_scale1d.offsets[j]), sizeof (float) * scale->dest->width, scale->y_scale1d.n_taps, 0, scale->dest->width); } } } void vs_image_scale_lanczos_Y_float (const VSImage * dest, const VSImage * src, uint8_t * tmpbuf, double sharpness, gboolean dither, double a, double sharpen) { Scale s = { 0 }; Scale *scale = &s; int n_taps; scale->dest = dest; scale->src = src; n_taps = scale1d_get_n_taps (src->width, dest->width, a, sharpness); scale1d_calculate_taps_float (&scale->x_scale1d, src->width, dest->width, n_taps, a, sharpness, sharpen); n_taps = scale1d_get_n_taps (src->height, dest->height, a, sharpness); scale1d_calculate_taps_float (&scale->y_scale1d, src->height, dest->height, n_taps, a, sharpness, sharpen); scale->dither = dither; scale->horiz_resample_func = (HorizResampleFunc) resample_horiz_float_u8_generic; scale->tmpdata = g_malloc (sizeof (float) * scale->dest->width * scale->src->height); vs_scale_lanczos_Y_float (scale); scale1d_cleanup (&scale->x_scale1d); scale1d_cleanup (&scale->y_scale1d); g_free (scale->tmpdata); } static void vs_scale_lanczos_AYUV_int16 (Scale * scale) { int j; int yi; int tmp_yi; tmp_yi = 0; for (j = 0; j < scale->dest->height; j++) { guint8 *destline; gint16 *taps; destline = scale->dest->pixels + scale->dest->stride * j; yi = scale->y_scale1d.offsets[j]; while (tmp_yi < yi + scale->y_scale1d.n_taps) { scale->horiz_resample_func (TMP_LINE_S16_AYUV (tmp_yi), scale->x_scale1d.offsets, scale->x_scale1d.taps, SRC_LINE (tmp_yi), scale->x_scale1d.n_taps, S16_MIDSHIFT, scale->dest->width); tmp_yi++; } taps = (gint16 *) scale->y_scale1d.taps + j * scale->y_scale1d.n_taps; if (scale->dither) { resample_vert_dither_int16_generic (destline, taps, TMP_LINE_S16_AYUV (scale->y_scale1d.offsets[j]), sizeof (gint16) * 4 * scale->dest->width, scale->y_scale1d.n_taps, S16_POSTSHIFT, scale->dest->width * 4); } else { resample_vert_int16_generic (destline, taps, TMP_LINE_S16_AYUV (scale->y_scale1d.offsets[j]), sizeof (gint16) * 4 * scale->dest->width, scale->y_scale1d.n_taps, S16_POSTSHIFT, scale->dest->width * 4); } } } void vs_image_scale_lanczos_AYUV_int16 (const VSImage * dest, const VSImage * src, uint8_t * tmpbuf, double sharpness, gboolean dither, double a, double sharpen) { Scale s = { 0 }; Scale *scale = &s; int n_taps; scale->dest = dest; scale->src = src; n_taps = scale1d_get_n_taps (src->width, dest->width, a, sharpness); n_taps = ROUND_UP_4 (n_taps); scale1d_calculate_taps_int16 (&scale->x_scale1d, src->width, dest->width, n_taps, a, sharpness, sharpen, S16_SHIFT1); n_taps = scale1d_get_n_taps (src->height, dest->height, a, sharpness); scale1d_calculate_taps_int16 (&scale->y_scale1d, src->height, dest->height, n_taps, a, sharpness, sharpen, S16_SHIFT2); scale->dither = dither; switch (scale->x_scale1d.n_taps) { case 4: scale->horiz_resample_func = (HorizResampleFunc) resample_horiz_int16_int16_ayuv_taps4_shift0; break; case 8: scale->horiz_resample_func = (HorizResampleFunc) resample_horiz_int16_int16_ayuv_taps8_shift0; break; case 12: scale->horiz_resample_func = (HorizResampleFunc) resample_horiz_int16_int16_ayuv_taps12_shift0; break; case 16: scale->horiz_resample_func = (HorizResampleFunc) resample_horiz_int16_int16_ayuv_taps16_shift0; break; default: scale->horiz_resample_func = (HorizResampleFunc) resample_horiz_int16_int16_ayuv_generic; break; } scale->tmpdata = g_malloc (sizeof (gint16) * scale->dest->width * scale->src->height * 4); vs_scale_lanczos_AYUV_int16 (scale); scale1d_cleanup (&scale->x_scale1d); scale1d_cleanup (&scale->y_scale1d); g_free (scale->tmpdata); } static void vs_scale_lanczos_AYUV_int32 (Scale * scale) { int j; int yi; int tmp_yi; tmp_yi = 0; for (j = 0; j < scale->dest->height; j++) { guint8 *destline; gint32 *taps; destline = scale->dest->pixels + scale->dest->stride * j; yi = scale->y_scale1d.offsets[j]; while (tmp_yi < yi + scale->y_scale1d.n_taps) { scale->horiz_resample_func (TMP_LINE_S32_AYUV (tmp_yi), scale->x_scale1d.offsets, scale->x_scale1d.taps, SRC_LINE (tmp_yi), scale->x_scale1d.n_taps, S32_MIDSHIFT, scale->dest->width); tmp_yi++; } taps = (gint32 *) scale->y_scale1d.taps + j * scale->y_scale1d.n_taps; if (scale->dither) { resample_vert_dither_int32_generic (destline, taps, TMP_LINE_S32_AYUV (scale->y_scale1d.offsets[j]), sizeof (gint32) * 4 * scale->dest->width, scale->y_scale1d.n_taps, S32_POSTSHIFT, scale->dest->width * 4); } else { resample_vert_int32_generic (destline, taps, TMP_LINE_S32_AYUV (scale->y_scale1d.offsets[j]), sizeof (gint32) * 4 * scale->dest->width, scale->y_scale1d.n_taps, S32_POSTSHIFT, scale->dest->width * 4); } } } void vs_image_scale_lanczos_AYUV_int32 (const VSImage * dest, const VSImage * src, uint8_t * tmpbuf, double sharpness, gboolean dither, double a, double sharpen) { Scale s = { 0 }; Scale *scale = &s; int n_taps; scale->dest = dest; scale->src = src; n_taps = scale1d_get_n_taps (src->width, dest->width, a, sharpness); n_taps = ROUND_UP_4 (n_taps); scale1d_calculate_taps_int32 (&scale->x_scale1d, src->width, dest->width, n_taps, a, sharpness, sharpen, S32_SHIFT1); n_taps = scale1d_get_n_taps (src->height, dest->height, a, sharpness); scale1d_calculate_taps_int32 (&scale->y_scale1d, src->height, dest->height, n_taps, a, sharpness, sharpen, S32_SHIFT2); scale->dither = dither; switch (scale->x_scale1d.n_taps) { case 4: scale->horiz_resample_func = (HorizResampleFunc) resample_horiz_int32_int32_ayuv_taps4_shift0; break; case 8: scale->horiz_resample_func = (HorizResampleFunc) resample_horiz_int32_int32_ayuv_taps8_shift0; break; case 12: scale->horiz_resample_func = (HorizResampleFunc) resample_horiz_int32_int32_ayuv_taps12_shift0; break; case 16: scale->horiz_resample_func = (HorizResampleFunc) resample_horiz_int32_int32_ayuv_taps16_shift0; break; default: scale->horiz_resample_func = (HorizResampleFunc) resample_horiz_int32_int32_ayuv_generic; break; } scale->tmpdata = g_malloc (sizeof (int32_t) * scale->dest->width * scale->src->height * 4); vs_scale_lanczos_AYUV_int32 (scale); scale1d_cleanup (&scale->x_scale1d); scale1d_cleanup (&scale->y_scale1d); g_free (scale->tmpdata); } static void vs_scale_lanczos_AYUV_double (Scale * scale) { int j; int yi; int tmp_yi; tmp_yi = 0; for (j = 0; j < scale->dest->height; j++) { guint8 *destline; double *taps; destline = scale->dest->pixels + scale->dest->stride * j; yi = scale->y_scale1d.offsets[j]; while (tmp_yi < yi + scale->y_scale1d.n_taps) { scale->horiz_resample_func (TMP_LINE_DOUBLE_AYUV (tmp_yi), scale->x_scale1d.offsets, scale->x_scale1d.taps, SRC_LINE (tmp_yi), scale->x_scale1d.n_taps, 0, scale->dest->width); tmp_yi++; } taps = (double *) scale->y_scale1d.taps + j * scale->y_scale1d.n_taps; if (scale->dither) { resample_vert_dither_double_generic (destline, taps, TMP_LINE_DOUBLE_AYUV (scale->y_scale1d.offsets[j]), sizeof (double) * 4 * scale->dest->width, scale->y_scale1d.n_taps, 0, scale->dest->width * 4); } else { resample_vert_double_generic (destline, taps, TMP_LINE_DOUBLE_AYUV (scale->y_scale1d.offsets[j]), sizeof (double) * 4 * scale->dest->width, scale->y_scale1d.n_taps, 0, scale->dest->width * 4); } } } void vs_image_scale_lanczos_AYUV_double (const VSImage * dest, const VSImage * src, uint8_t * tmpbuf, double sharpness, gboolean dither, double a, double sharpen) { Scale s = { 0 }; Scale *scale = &s; int n_taps; scale->dest = dest; scale->src = src; n_taps = scale1d_get_n_taps (src->width, dest->width, a, sharpness); scale1d_calculate_taps (&scale->x_scale1d, src->width, dest->width, n_taps, a, sharpness, sharpen); n_taps = scale1d_get_n_taps (src->height, dest->height, a, sharpness); scale1d_calculate_taps (&scale->y_scale1d, src->height, dest->height, n_taps, a, sharpness, sharpen); scale->dither = dither; scale->horiz_resample_func = (HorizResampleFunc) resample_horiz_double_ayuv_generic; scale->tmpdata = g_malloc (sizeof (double) * scale->dest->width * scale->src->height * 4); vs_scale_lanczos_AYUV_double (scale); scale1d_cleanup (&scale->x_scale1d); scale1d_cleanup (&scale->y_scale1d); g_free (scale->tmpdata); } static void vs_scale_lanczos_AYUV_float (Scale * scale) { int j; int yi; int tmp_yi; tmp_yi = 0; for (j = 0; j < scale->dest->height; j++) { guint8 *destline; float *taps; destline = scale->dest->pixels + scale->dest->stride * j; yi = scale->y_scale1d.offsets[j]; while (tmp_yi < yi + scale->y_scale1d.n_taps) { scale->horiz_resample_func (TMP_LINE_FLOAT_AYUV (tmp_yi), scale->x_scale1d.offsets, scale->x_scale1d.taps, SRC_LINE (tmp_yi), scale->x_scale1d.n_taps, 0, scale->dest->width); tmp_yi++; } taps = (float *) scale->y_scale1d.taps + j * scale->y_scale1d.n_taps; if (scale->dither) { resample_vert_dither_float_generic (destline, taps, TMP_LINE_FLOAT_AYUV (scale->y_scale1d.offsets[j]), sizeof (float) * 4 * scale->dest->width, scale->y_scale1d.n_taps, 0, scale->dest->width * 4); } else { resample_vert_float_generic (destline, taps, TMP_LINE_FLOAT_AYUV (scale->y_scale1d.offsets[j]), sizeof (float) * 4 * scale->dest->width, scale->y_scale1d.n_taps, 0, scale->dest->width * 4); } } } void vs_image_scale_lanczos_AYUV_float (const VSImage * dest, const VSImage * src, uint8_t * tmpbuf, double sharpness, gboolean dither, double a, double sharpen) { Scale s = { 0 }; Scale *scale = &s; int n_taps; scale->dest = dest; scale->src = src; n_taps = scale1d_get_n_taps (src->width, dest->width, a, sharpness); scale1d_calculate_taps_float (&scale->x_scale1d, src->width, dest->width, n_taps, a, sharpness, sharpen); n_taps = scale1d_get_n_taps (src->height, dest->height, a, sharpness); scale1d_calculate_taps_float (&scale->y_scale1d, src->height, dest->height, n_taps, a, sharpness, sharpen); scale->dither = dither; scale->horiz_resample_func = (HorizResampleFunc) resample_horiz_float_ayuv_generic; scale->tmpdata = g_malloc (sizeof (float) * scale->dest->width * scale->src->height * 4); vs_scale_lanczos_AYUV_float (scale); scale1d_cleanup (&scale->x_scale1d); scale1d_cleanup (&scale->y_scale1d); g_free (scale->tmpdata); } static void vs_scale_lanczos_AYUV64_double (Scale * scale) { int j; int yi; int tmp_yi; tmp_yi = 0; for (j = 0; j < scale->dest->height; j++) { guint16 *destline; double *taps; destline = (guint16 *) (scale->dest->pixels + scale->dest->stride * j); yi = scale->y_scale1d.offsets[j]; while (tmp_yi < yi + scale->y_scale1d.n_taps) { scale->horiz_resample_func (TMP_LINE_DOUBLE_AYUV (tmp_yi), scale->x_scale1d.offsets, scale->x_scale1d.taps, SRC_LINE (tmp_yi), scale->x_scale1d.n_taps, 0, scale->dest->width); tmp_yi++; } taps = (double *) scale->y_scale1d.taps + j * scale->y_scale1d.n_taps; if (scale->dither) { resample_vert_dither_double_generic_u16 (destline, taps, TMP_LINE_DOUBLE_AYUV (scale->y_scale1d.offsets[j]), sizeof (double) * 4 * scale->dest->width, scale->y_scale1d.n_taps, 0, scale->dest->width * 4); } else { resample_vert_double_generic_u16 (destline, taps, TMP_LINE_DOUBLE_AYUV (scale->y_scale1d.offsets[j]), sizeof (double) * 4 * scale->dest->width, scale->y_scale1d.n_taps, 0, scale->dest->width * 4); } } } void vs_image_scale_lanczos_AYUV64_double (const VSImage * dest, const VSImage * src, uint8_t * tmpbuf, double sharpness, gboolean dither, double a, double sharpen) { Scale s = { 0 }; Scale *scale = &s; int n_taps; scale->dest = dest; scale->src = src; n_taps = scale1d_get_n_taps (src->width, dest->width, a, sharpness); scale1d_calculate_taps (&scale->x_scale1d, src->width, dest->width, n_taps, a, sharpness, sharpen); n_taps = scale1d_get_n_taps (src->height, dest->height, a, sharpness); scale1d_calculate_taps (&scale->y_scale1d, src->height, dest->height, n_taps, a, sharpness, sharpen); scale->dither = dither; scale->horiz_resample_func = (HorizResampleFunc) resample_horiz_double_ayuv_generic_s16; scale->tmpdata = g_malloc (sizeof (double) * scale->dest->width * scale->src->height * 4); vs_scale_lanczos_AYUV64_double (scale); scale1d_cleanup (&scale->x_scale1d); scale1d_cleanup (&scale->y_scale1d); g_free (scale->tmpdata); }