2 * Image Scaling Functions
3 * Copyright (c) 2011 David A. Schleef <ds@schleef.org>
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
19 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
23 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
24 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25 * POSSIBILITY OF SUCH DAMAGE.
29 * Modified Lanczos scaling algorithm
30 * ==================================
32 * This algorithm was developed by the author. The primary goals of
33 * the algorithm are high-quality video downscaling for medium scale
34 * factors (in the range of 1.3x to 5.0x) using methods that can be
35 * converted to SIMD code. Concerns with existing algorithms were
36 * mainly related to either over-soft filtering (Lanczos) or aliasing
37 * (bilinear or any other method with inadequate sampling).
39 * The problems with bilinear scaling are apparent when downscaling
40 * more than a factor of 2. For example, when downscaling by a factor
41 * of 3, only two-thirds of the input pixels contribute to the output
42 * pixels. This is only considering scaling in one direction; after
43 * scaling both vertically and horizontally in a 2-D image, fewer than
44 * half of the input pixels contribute to the output, so it should not
45 * be surprising that the output is suboptimal.
47 * The problems with Lanczos scaling are more subtle. From a theoretical
48 * perspective, Lanczos is an optimal algorithm for resampling equally-
49 * spaced values. This theoretical perspective is based on analysis
50 * done in frequency space, thus, Lanczos works very well for audio
51 * resampling, since the ear hears primarily in frequency space. The
52 * human visual system is sensitive primarily in the spatial domain,
53 * therefore any resampling algorithm should take this into account.
54 * This difference is immediately clear in the size of resampling
55 * window or envelope that is chosen for resampling: for audio, an
56 * envelope of a=64 is typical, in image scaling, the envelope is
59 * One result of the HVS being sensitive in the spatial domain (and
60 * also probably due to oversampling capabilities of the retina and
61 * visual cortex) is that it is less sensitive to the exact magnitude
62 * of high-frequency visual signals than to the appropriate amount of
63 * energy in the nearby frequency band. A Lanczos kernel with a=2
64 * or a=3 strongly decreases the amount of energy in the high frequency
65 * bands. The energy in this area can be increased by increasing a,
66 * which brings in energy from different areas of the image (bad for
67 * reasons mentioned above), or by oversampling the input data. We
68 * have chosen two methods for doing the latter. Firstly, there is
69 * a sharpness parameter, which increases the cutoff frequency of the
70 * filter, aliasing higher frequency noise into the passband. And
71 * secondly, there is the sharpen parameter, which increases the
72 * contribution of high-frequency (but in-band) components.
74 * An alternate explanation of the usefulness of a sharpening filter
75 * is that many natural images have a roughly 1/f spectrum. In order
76 * for a downsampled image to look more "natural" when high frequencies
77 * are removed, the frequencies in the pass band near the cutoff
78 * frequency are amplified, causing the spectrum to be more roughly
79 * 1/f. I said "roughly", not "literally".
81 * This alternate explanation is useful for understanding the author's
82 * secondary motivation for developing this algorithm, namely, as a
83 * method of video compression. Several recent techniques (such as
84 * HTTP Live Streaming and SVC) use image scaling as a method to get
85 * increased compression out of nominally non-scalable codecs such as
86 * H.264. For optimal quality, it is thusly important to consider
87 * the scaler and encoder as a combined unit. Tuning of the sharpness
88 * and sharpen parameters was performed using the Toro encoder tuner,
89 * where scaled and encoded video was compared to unscaled and encoded
90 * video. This tuning suggested values that were very close to the
91 * values chosen by manual inspection of scaled images and video.
93 * The optimal values of sharpen and sharpness were slightly different
94 * depending whether the comparison was still images or video. Video
95 * comparisons were more sensitive to aliasing, since the aliasing
96 * artifacts tended to move or "crawl" around the video. The default
97 * values are for video; image scaling may prefer higher values.
99 * A number of related techniques were rejected for various reasons.
100 * An early technique of selecting the sharpness factor locally based
101 * on edge detection (in order to use a higher sharpness values without
102 * the corresponding aliasing on edges) worked very well for still
103 * images, but caused too much "crawling" on textures in video. Also,
104 * this method is slow, as it does not parallelize well.
106 * Non-separable techniques were rejected because the fastest would
107 * have been at least 4x slower.
109 * It is infrequently appreciated that image scaling should ideally be
110 * done in linear light space. Converting to linear light space has
111 * a similar effect to a sharpening filter. This approach was not
112 * taken because the added benefit is minor compared to the additional
113 * computational cost. Morever, the benefit is decreased by increasing
114 * the strength of the sharpening filter.
119 #include "vs_scanline.h"
120 #include "vs_image.h"
122 #include "gstvideoscaleorc.h"
126 #define NEED_CLAMP(x,a,b) ((x) < (a) || (x) > (b))
128 #define ROUND_UP_2(x) (((x)+1)&~1)
129 #define ROUND_UP_4(x) (((x)+3)&~3)
130 #define ROUND_UP_8(x) (((x)+7)&~7)
132 #define SRC_LINE(i) (scale->src->pixels + scale->src->stride * (i))
134 #define TMP_LINE_S16(i) ((gint16 *)scale->tmpdata + (i)*(scale->dest->width))
135 #define TMP_LINE_S32(i) ((gint32 *)scale->tmpdata + (i)*(scale->dest->width))
136 #define TMP_LINE_FLOAT(i) ((float *)scale->tmpdata + (i)*(scale->dest->width))
137 #define TMP_LINE_DOUBLE(i) ((double *)scale->tmpdata + (i)*(scale->dest->width))
138 #define TMP_LINE_S16_AYUV(i) ((gint16 *)scale->tmpdata + (i)*4*(scale->dest->width))
139 #define TMP_LINE_S32_AYUV(i) ((gint32 *)scale->tmpdata + (i)*4*(scale->dest->width))
140 #define TMP_LINE_FLOAT_AYUV(i) ((float *)scale->tmpdata + (i)*4*(scale->dest->width))
141 #define TMP_LINE_DOUBLE_AYUV(i) ((double *)scale->tmpdata + (i)*4*(scale->dest->width))
143 #define PTR_OFFSET(a,b) ((void *)((char *)(a) + (b)))
145 typedef void (*HorizResampleFunc) (void *dest, const gint32 * offsets,
146 const void *taps, const void *src, int n_taps, int shift, int n);
148 typedef struct _Scale1D Scale1D;
164 typedef struct _Scale Scale;
175 HorizResampleFunc horiz_resample_func;
182 vs_image_scale_lanczos_Y_int16 (const VSImage * dest, const VSImage * src,
183 uint8_t * tmpbuf, double sharpness, gboolean dither, double a,
185 static void vs_image_scale_lanczos_Y_int32 (const VSImage * dest,
186 const VSImage * src, uint8_t * tmpbuf, double sharpness, gboolean dither,
187 double a, double sharpen);
188 static void vs_image_scale_lanczos_Y_float (const VSImage * dest,
189 const VSImage * src, uint8_t * tmpbuf, double sharpness, gboolean dither,
190 double a, double sharpen);
191 static void vs_image_scale_lanczos_Y_double (const VSImage * dest,
192 const VSImage * src, uint8_t * tmpbuf, double sharpness, gboolean dither,
193 double a, double sharpen);
195 vs_image_scale_lanczos_AYUV_int16 (const VSImage * dest, const VSImage * src,
196 uint8_t * tmpbuf, double sharpness, gboolean dither, double a,
198 static void vs_image_scale_lanczos_AYUV_int32 (const VSImage * dest,
199 const VSImage * src, uint8_t * tmpbuf, double sharpness, gboolean dither,
200 double a, double sharpen);
201 static void vs_image_scale_lanczos_AYUV_float (const VSImage * dest,
202 const VSImage * src, uint8_t * tmpbuf, double sharpness, gboolean dither,
203 double a, double sharpen);
204 static void vs_image_scale_lanczos_AYUV_double (const VSImage * dest,
205 const VSImage * src, uint8_t * tmpbuf, double sharpness, gboolean dither,
206 double a, double sharpen);
213 return sin (G_PI * x) / (G_PI * x);
219 if (x <= -1 || x >= 1)
225 scale1d_get_n_taps (int src_size, int dest_size, double a, double sharpness)
231 scale = src_size / (double) dest_size;
233 fx = (1.0 / scale) * sharpness;
235 fx = (1.0) * sharpness;
243 scale1d_cleanup (Scale1D * scale)
245 g_free (scale->taps);
246 g_free (scale->offsets);
250 * Calculates a set of taps for each destination element in double
251 * format. Each set of taps sums to 1.0.
255 scale1d_calculate_taps (Scale1D * scale, int src_size, int dest_size,
256 int n_taps, double a, double sharpness, double sharpen)
262 double scale_increment;
267 scale->scale = src_size / (double) dest_size;
268 scale->offset = scale->scale / 2 - 0.5;
270 if (scale->scale > 1.0) {
271 scale->fx = (1.0 / scale->scale) * sharpness;
273 scale->fx = (1.0) * sharpness;
275 scale->ex = scale->fx / a;
276 scale->dx = ceil (a / scale->fx);
278 g_assert (n_taps >= 2 * scale->dx);
279 scale->n_taps = n_taps;
281 scale->taps = g_malloc (sizeof (double) * scale->n_taps * dest_size);
282 scale->offsets = g_malloc (sizeof (gint32) * dest_size);
283 tap_array = scale->taps;
284 offsets = scale->offsets;
286 scale_offset = scale->offset;
287 scale_increment = scale->scale;
292 for (j = 0; j < dest_size; j++) {
299 x = scale_offset + scale_increment * j;
300 x = CLAMP (x, 0, src_size);
305 taps = tap_array + j * n_taps;
307 for (l = 0; l < n_taps; l++) {
309 taps[l] = sinc ((x - xl) * fx) * envelope ((x - xl) * ex);
310 taps[l] -= sharpen * envelope ((x - xl) * ex);
313 g_assert (envelope ((x - (xi - 1)) * ex) == 0);
314 g_assert (envelope ((x - (xi + n_taps)) * ex) == 0);
315 for (l = 0; l < n_taps; l++) {
322 for (l = 0; l < shift; l++) {
323 taps[shift] += taps[l];
325 for (l = 0; l < n_taps - shift; l++) {
326 taps[l] = taps[shift + l];
328 for (; l < n_taps; l++) {
334 if (xi > src_size - n_taps) {
335 int shift = xi - (src_size - n_taps);
337 for (l = 0; l < shift; l++) {
338 taps[n_taps - shift - 1] += taps[n_taps - shift + l];
340 for (l = 0; l < n_taps - shift; l++) {
341 taps[n_taps - 1 - l] = taps[n_taps - 1 - shift - l];
343 for (l = 0; l < shift; l++) {
352 * Calculates a set of taps for each destination element in float
353 * format. Each set of taps sums to 1.0.
356 scale1d_calculate_taps_float (Scale1D * scale, int src_size, int dest_size,
357 int n_taps, double a, double sharpness, double sharpen)
363 scale1d_calculate_taps (scale, src_size, dest_size, n_taps, a, sharpness,
366 taps_d = scale->taps;
367 taps_f = g_malloc (sizeof (float) * scale->n_taps * dest_size);
369 for (j = 0; j < dest_size * n_taps; j++) {
370 taps_f[j] = taps_d[j];
374 scale->taps = taps_f;
378 * Calculates a set of taps for each destination element in gint32
379 * format. Each set of taps sums to (very nearly) (1<<shift). A
380 * typical value for shift is 10 to 15, so that applying the taps to
381 * uint8 values and summing will fit in a (signed) int32.
384 scale1d_calculate_taps_int32 (Scale1D * scale, int src_size, int dest_size,
385 int n_taps, double a, double sharpness, double sharpen, int shift)
393 scale1d_calculate_taps (scale, src_size, dest_size, n_taps, a, sharpness,
396 taps_d = scale->taps;
397 taps_i = g_malloc (sizeof (gint32) * scale->n_taps * dest_size);
399 multiplier = (1 << shift);
401 for (j = 0; j < dest_size; j++) {
402 for (i = 0; i < n_taps; i++) {
403 taps_i[j * n_taps + i] =
404 floor (0.5 + taps_d[j * n_taps + i] * multiplier);
409 scale->taps = taps_i;
413 * Calculates a set of taps for each destination element in gint16
414 * format. Each set of taps sums to (1<<shift). A typical value
415 * for shift is 7, so that applying the taps to uint8 values and
416 * summing will fit in a (signed) int16.
419 scale1d_calculate_taps_int16 (Scale1D * scale, int src_size, int dest_size,
420 int n_taps, double a, double sharpness, double sharpen, int shift)
428 scale1d_calculate_taps (scale, src_size, dest_size, n_taps, a, sharpness,
431 taps_d = scale->taps;
432 taps_i = g_malloc (sizeof (gint16) * scale->n_taps * dest_size);
434 multiplier = (1 << shift);
436 /* Various methods for converting floating point taps to integer.
437 * The dB values are the SSIM value between scaling an image via
438 * the floating point pathway vs. the integer pathway using the
439 * given code to generate the taps. Only one image was tested,
440 * scaling from 1920x1080 to 640x360. Several variations of the
441 * methods were also tested, with nothing appearing useful. */
443 /* Standard round to integer. This causes bad DC errors. */
445 for (j = 0; j < dest_size; j++) {
446 for (i = 0; i < n_taps; i++) {
447 taps_i[j * n_taps + i] =
448 floor (0.5 + taps_d[j * n_taps + i] * multiplier);
453 /* Dithering via error propogation. Works pretty well, but
454 * really we want to propogate errors across rows, which would
455 * mean having several sets of tap arrays. Possible, but more work,
456 * and it may not even be better. */
460 for (j = 0; j < dest_size; j++) {
461 for (i = 0; i < n_taps; i++) {
462 err += taps_d[j * n_taps + i] * multiplier;
463 taps_i[j * n_taps + i] = floor (err);
470 /* Round to integer, but with an adjustable bias that we use to
471 * eliminate the DC error. This search method is a bit crude, and
472 * could perhaps be improved somewhat. */
474 for (j = 0; j < dest_size; j++) {
476 for (k = 0; k < 100; k++) {
481 for (i = 0; i < n_taps; i++) {
482 taps_i[j * n_taps + i] =
483 floor (offset + taps_d[j * n_taps + i] * multiplier);
484 sum += taps_i[j * n_taps + i];
487 if (sum >= (1 << shift))
493 /* Round to integer, but adjust the multiplier. The search method is
494 * wrong a lot, but was sufficient enough to calculate dB error. */
496 for (j = 0; j < dest_size; j++) {
499 for (k = 0; k < 200; k++) {
502 multiplier = (1 << shift) - 1.0 + k * 0.01;
503 for (i = 0; i < n_taps; i++) {
504 taps_i[j * n_taps + i] =
505 floor (0.5 + taps_d[j * n_taps + i] * multiplier);
506 sum += taps_i[j * n_taps + i];
509 if (sum >= (1 << shift))
512 if (sum != (1 << shift)) {
513 GST_ERROR ("%g %d", multiplier, sum);
518 /* Round to integer, but subtract the error from the largest tap */
520 for (j = 0; j < dest_size; j++) {
521 int err = -multiplier;
522 for (i = 0; i < n_taps; i++) {
523 taps_i[j * n_taps + i] =
524 floor (0.5 + taps_d[j * n_taps + i] * multiplier);
525 err += taps_i[j * n_taps + i];
527 if (taps_i[j * n_taps + (n_taps / 2 - 1)] >
528 taps_i[j * n_taps + (n_taps / 2)]) {
529 taps_i[j * n_taps + (n_taps / 2 - 1)] -= err;
531 taps_i[j * n_taps + (n_taps / 2)] -= err;
537 scale->taps = taps_i;
542 vs_image_scale_lanczos_Y (const VSImage * dest, const VSImage * src,
543 uint8_t * tmpbuf, double sharpness, gboolean dither, int submethod,
544 double a, double sharpen)
549 vs_image_scale_lanczos_Y_int16 (dest, src, tmpbuf, sharpness, dither, a,
553 vs_image_scale_lanczos_Y_int32 (dest, src, tmpbuf, sharpness, dither, a,
557 vs_image_scale_lanczos_Y_float (dest, src, tmpbuf, sharpness, dither, a,
561 vs_image_scale_lanczos_Y_double (dest, src, tmpbuf, sharpness, dither, a,
568 vs_image_scale_lanczos_AYUV (const VSImage * dest, const VSImage * src,
569 uint8_t * tmpbuf, double sharpness, gboolean dither, int submethod,
570 double a, double sharpen)
575 vs_image_scale_lanczos_AYUV_int16 (dest, src, tmpbuf, sharpness, dither,
579 vs_image_scale_lanczos_AYUV_int32 (dest, src, tmpbuf, sharpness, dither,
583 vs_image_scale_lanczos_AYUV_float (dest, src, tmpbuf, sharpness, dither,
587 vs_image_scale_lanczos_AYUV_double (dest, src, tmpbuf, sharpness, dither,
595 #define RESAMPLE_HORIZ_FLOAT(function, dest_type, tap_type, src_type, _n_taps) \
597 function (dest_type *dest, const gint32 *offsets, \
598 const tap_type *taps, const src_type *src, int n_taps, int shift, int n) \
603 const src_type *srcline; \
604 const tap_type *tapsline; \
605 for (i = 0; i < n; i++) { \
606 srcline = src + offsets[i]; \
607 tapsline = taps + i * _n_taps; \
609 for (k = 0; k < _n_taps; k++) { \
610 sum += srcline[k] * tapsline[k]; \
616 #define RESAMPLE_HORIZ(function, dest_type, tap_type, src_type, _n_taps, _shift) \
618 function (dest_type *dest, const gint32 *offsets, \
619 const tap_type *taps, const src_type *src, int n_taps, int shift, int n) \
624 const src_type *srcline; \
625 const tap_type *tapsline; \
627 if (_shift > 0) offset = (1<<_shift)>>1; \
629 for (i = 0; i < n; i++) { \
630 srcline = src + offsets[i]; \
631 tapsline = taps + i * _n_taps; \
633 for (k = 0; k < _n_taps; k++) { \
634 sum += srcline[k] * tapsline[k]; \
636 dest[i] = (sum + offset) >> _shift; \
640 #define RESAMPLE_HORIZ_AYUV_FLOAT(function, dest_type, tap_type, src_type, _n_taps) \
642 function (dest_type *dest, const gint32 *offsets, \
643 const tap_type *taps, const src_type *src, int n_taps, int shift, int n) \
651 const src_type *srcline; \
652 const tap_type *tapsline; \
653 for (i = 0; i < n; i++) { \
654 srcline = src + 4*offsets[i]; \
655 tapsline = taps + i * _n_taps; \
660 for (k = 0; k < _n_taps; k++) { \
661 sum1 += srcline[k*4+0] * tapsline[k]; \
662 sum2 += srcline[k*4+1] * tapsline[k]; \
663 sum3 += srcline[k*4+2] * tapsline[k]; \
664 sum4 += srcline[k*4+3] * tapsline[k]; \
666 dest[i*4+0] = sum1; \
667 dest[i*4+1] = sum2; \
668 dest[i*4+2] = sum3; \
669 dest[i*4+3] = sum4; \
673 #define RESAMPLE_HORIZ_AYUV(function, dest_type, tap_type, src_type, _n_taps, _shift) \
675 function (dest_type *dest, const gint32 *offsets, \
676 const tap_type *taps, const src_type *src, int n_taps, int shift, int n) \
684 const src_type *srcline; \
685 const tap_type *tapsline; \
687 if (_shift > 0) offset = (1<<_shift)>>1; \
689 for (i = 0; i < n; i++) { \
690 srcline = src + 4*offsets[i]; \
691 tapsline = taps + i * _n_taps; \
696 for (k = 0; k < _n_taps; k++) { \
697 sum1 += srcline[k*4+0] * tapsline[k]; \
698 sum2 += srcline[k*4+1] * tapsline[k]; \
699 sum3 += srcline[k*4+2] * tapsline[k]; \
700 sum4 += srcline[k*4+3] * tapsline[k]; \
702 dest[i*4+0] = (sum1 + offset) >> _shift; \
703 dest[i*4+1] = (sum2 + offset) >> _shift; \
704 dest[i*4+2] = (sum3 + offset) >> _shift; \
705 dest[i*4+3] = (sum4 + offset) >> _shift; \
710 RESAMPLE_HORIZ_FLOAT (resample_horiz_double_u8_generic, double, double,
712 RESAMPLE_HORIZ_FLOAT (resample_horiz_float_u8_generic, float, float,
714 RESAMPLE_HORIZ_AYUV_FLOAT (resample_horiz_double_ayuv_generic, double, double,
716 RESAMPLE_HORIZ_AYUV_FLOAT (resample_horiz_float_ayuv_generic, float, float,
719 RESAMPLE_HORIZ (resample_horiz_int32_int32_u8_generic, gint32, gint32,
720 guint8, n_taps, shift)
721 RESAMPLE_HORIZ (resample_horiz_int16_int16_u8_generic, gint16, gint16,
722 guint8, n_taps, shift)
723 RESAMPLE_HORIZ_AYUV (resample_horiz_int32_int32_ayuv_generic, gint32, gint32,
724 guint8, n_taps, shift)
725 RESAMPLE_HORIZ_AYUV (resample_horiz_int16_int16_ayuv_generic, gint16, gint16,
726 guint8, n_taps, shift)
728 /* Candidates for orcification */
729 RESAMPLE_HORIZ (resample_horiz_int32_int32_u8_taps16_shift0, gint32, gint32,
731 RESAMPLE_HORIZ (resample_horiz_int32_int32_u8_taps12_shift0, gint32, gint32,
733 RESAMPLE_HORIZ (resample_horiz_int32_int32_u8_taps8_shift0, gint32, gint32,
735 RESAMPLE_HORIZ (resample_horiz_int32_int32_u8_taps4_shift0, gint32, gint32,
737 RESAMPLE_HORIZ (resample_horiz_int16_int16_u8_taps16_shift0, gint16, gint16,
739 RESAMPLE_HORIZ (resample_horiz_int16_int16_u8_taps12_shift0, gint16, gint16,
741 RESAMPLE_HORIZ (resample_horiz_int16_int16_u8_taps8_shift0, gint16, gint16,
743 RESAMPLE_HORIZ (resample_horiz_int16_int16_u8_taps4_shift0, gint16, gint16,
746 RESAMPLE_HORIZ_AYUV (resample_horiz_int32_int32_ayuv_taps16_shift0, gint32, gint32,
748 RESAMPLE_HORIZ_AYUV (resample_horiz_int32_int32_ayuv_taps12_shift0, gint32, gint32,
750 RESAMPLE_HORIZ_AYUV (resample_horiz_int32_int32_ayuv_taps8_shift0, gint32, gint32,
752 RESAMPLE_HORIZ_AYUV (resample_horiz_int32_int32_ayuv_taps4_shift0, gint32, gint32,
754 RESAMPLE_HORIZ_AYUV (resample_horiz_int16_int16_ayuv_taps16_shift0, gint16, gint16,
756 RESAMPLE_HORIZ_AYUV (resample_horiz_int16_int16_ayuv_taps12_shift0, gint16, gint16,
758 RESAMPLE_HORIZ_AYUV (resample_horiz_int16_int16_ayuv_taps8_shift0, gint16, gint16,
760 RESAMPLE_HORIZ_AYUV (resample_horiz_int16_int16_ayuv_taps4_shift0, gint16, gint16,
764 #define RESAMPLE_VERT(function, tap_type, src_type, _n_taps, _shift) \
766 function (guint8 *dest, \
767 const tap_type *taps, const src_type *src, int stride, int n_taps, \
773 gint32 offset = (1<<_shift) >> 1; \
774 for (i = 0; i < n; i++) { \
776 for (l = 0; l < n_taps; l++) { \
777 const src_type *line = PTR_OFFSET(src, stride * l); \
778 sum_y += line[i] * taps[l]; \
780 dest[i] = CLAMP ((sum_y + offset) >> _shift, 0, 255); \
784 #define RESAMPLE_VERT_DITHER(function, tap_type, src_type, _n_taps, _shift) \
786 function (guint8 *dest, \
787 const tap_type *taps, const src_type *src, int stride, int n_taps, \
794 gint32 mask = (1<<_shift) - 1; \
795 for (i = 0; i < n; i++) { \
797 for (l = 0; l < n_taps; l++) { \
798 const src_type *line = PTR_OFFSET(src, stride * l); \
799 sum_y += line[i] * taps[l]; \
802 dest[i] = CLAMP (err_y >> _shift, 0, 255); \
808 RESAMPLE_VERT (resample_vert_int32_generic, gint32, gint32, n_taps, shift)
809 RESAMPLE_VERT_DITHER (resample_vert_dither_int32_generic, gint32, gint32,
811 RESAMPLE_VERT (resample_vert_int16_generic, gint16, gint16, n_taps, shift);
812 RESAMPLE_VERT_DITHER (resample_vert_dither_int16_generic, gint16, gint16,
816 #define RESAMPLE_VERT_FLOAT(function, tap_type, src_type, _n_taps, _shift) \
818 function (guint8 *dest, \
819 const tap_type *taps, const src_type *src, int stride, int n_taps, \
825 for (i = 0; i < n; i++) { \
827 for (l = 0; l < n_taps; l++) { \
828 const src_type *line = PTR_OFFSET(src, stride * l); \
829 sum_y += line[i] * taps[l]; \
831 dest[i] = CLAMP (floor(0.5 + sum_y), 0, 255); \
835 #define RESAMPLE_VERT_FLOAT_DITHER(function, tap_type, src_type, _n_taps, _shift) \
837 function (guint8 *dest, \
838 const tap_type *taps, const src_type *src, int stride, int n_taps, \
844 src_type err_y = 0; \
845 for (i = 0; i < n; i++) { \
847 for (l = 0; l < n_taps; l++) { \
848 const src_type *line = PTR_OFFSET(src, stride * l); \
849 sum_y += line[i] * taps[l]; \
852 dest[i] = CLAMP (floor (err_y), 0, 255); \
853 err_y -= floor (err_y); \
858 RESAMPLE_VERT_FLOAT (resample_vert_double_generic, double, double, n_taps,
860 RESAMPLE_VERT_FLOAT_DITHER (resample_vert_dither_double_generic, double, double,
863 RESAMPLE_VERT_FLOAT (resample_vert_float_generic, float, float, n_taps, shift)
864 RESAMPLE_VERT_FLOAT_DITHER (resample_vert_dither_float_generic, float, float,
870 #define S16_MIDSHIFT 0
871 #define S16_POSTSHIFT (S16_SHIFT1+S16_SHIFT2-S16_MIDSHIFT)
874 vs_scale_lanczos_Y_int16 (Scale * scale)
882 for (j = 0; j < scale->dest->height; j++) {
886 destline = scale->dest->pixels + scale->dest->stride * j;
888 yi = scale->y_scale1d.offsets[j];
890 while (tmp_yi < yi + scale->y_scale1d.n_taps) {
891 scale->horiz_resample_func (TMP_LINE_S16 (tmp_yi),
892 scale->x_scale1d.offsets, scale->x_scale1d.taps, SRC_LINE (tmp_yi),
893 scale->x_scale1d.n_taps, S16_MIDSHIFT, scale->dest->width);
897 taps = (gint16 *) scale->y_scale1d.taps + j * scale->y_scale1d.n_taps;
899 resample_vert_dither_int16_generic (destline,
900 taps, TMP_LINE_S16 (scale->y_scale1d.offsets[j]),
901 sizeof (gint16) * scale->dest->width, scale->y_scale1d.n_taps,
902 S16_POSTSHIFT, scale->dest->width);
904 resample_vert_int16_generic (destline,
905 taps, TMP_LINE_S16 (scale->y_scale1d.offsets[j]),
906 sizeof (gint16) * scale->dest->width, scale->y_scale1d.n_taps,
907 S16_POSTSHIFT, scale->dest->width);
913 vs_image_scale_lanczos_Y_int16 (const VSImage * dest, const VSImage * src,
914 uint8_t * tmpbuf, double sharpness, gboolean dither, double a,
924 n_taps = scale1d_get_n_taps (src->width, dest->width, a, sharpness);
925 n_taps = ROUND_UP_4 (n_taps);
926 scale1d_calculate_taps_int16 (&scale->x_scale1d,
927 src->width, dest->width, n_taps, a, sharpness, sharpen, S16_SHIFT1);
929 n_taps = scale1d_get_n_taps (src->height, dest->height, a, sharpness);
930 scale1d_calculate_taps_int16 (&scale->y_scale1d,
931 src->height, dest->height, n_taps, a, sharpness, sharpen, S16_SHIFT2);
933 scale->dither = dither;
935 switch (scale->x_scale1d.n_taps) {
937 scale->horiz_resample_func =
938 (HorizResampleFunc) resample_horiz_int16_int16_u8_taps4_shift0;
941 scale->horiz_resample_func =
942 (HorizResampleFunc) resample_horiz_int16_int16_u8_taps8_shift0;
945 scale->horiz_resample_func =
946 (HorizResampleFunc) resample_horiz_int16_int16_u8_taps12_shift0;
949 scale->horiz_resample_func =
950 (HorizResampleFunc) resample_horiz_int16_int16_u8_taps16_shift0;
953 scale->horiz_resample_func =
954 (HorizResampleFunc) resample_horiz_int16_int16_u8_generic;
959 g_malloc (sizeof (gint16) * scale->dest->width * scale->src->height);
961 vs_scale_lanczos_Y_int16 (scale);
963 scale1d_cleanup (&scale->x_scale1d);
964 scale1d_cleanup (&scale->y_scale1d);
965 g_free (scale->tmpdata);
969 #define S32_SHIFT1 11
970 #define S32_SHIFT2 11
971 #define S32_MIDSHIFT 0
972 #define S32_POSTSHIFT (S32_SHIFT1+S32_SHIFT2-S32_MIDSHIFT)
975 vs_scale_lanczos_Y_int32 (Scale * scale)
983 for (j = 0; j < scale->dest->height; j++) {
987 destline = scale->dest->pixels + scale->dest->stride * j;
989 yi = scale->y_scale1d.offsets[j];
991 while (tmp_yi < yi + scale->y_scale1d.n_taps) {
992 scale->horiz_resample_func (TMP_LINE_S32 (tmp_yi),
993 scale->x_scale1d.offsets, scale->x_scale1d.taps, SRC_LINE (tmp_yi),
994 scale->x_scale1d.n_taps, S32_MIDSHIFT, scale->dest->width);
998 taps = (gint32 *) scale->y_scale1d.taps + j * scale->y_scale1d.n_taps;
1000 resample_vert_dither_int32_generic (destline,
1001 taps, TMP_LINE_S32 (scale->y_scale1d.offsets[j]),
1002 sizeof (gint32) * scale->dest->width,
1003 scale->y_scale1d.n_taps, S32_POSTSHIFT, scale->dest->width);
1005 resample_vert_int32_generic (destline,
1006 taps, TMP_LINE_S32 (scale->y_scale1d.offsets[j]),
1007 sizeof (gint32) * scale->dest->width,
1008 scale->y_scale1d.n_taps, S32_POSTSHIFT, scale->dest->width);
1014 vs_image_scale_lanczos_Y_int32 (const VSImage * dest, const VSImage * src,
1015 uint8_t * tmpbuf, double sharpness, gboolean dither, double a,
1025 n_taps = scale1d_get_n_taps (src->width, dest->width, a, sharpness);
1026 n_taps = ROUND_UP_4 (n_taps);
1027 scale1d_calculate_taps_int32 (&scale->x_scale1d,
1028 src->width, dest->width, n_taps, a, sharpness, sharpen, S32_SHIFT1);
1030 n_taps = scale1d_get_n_taps (src->height, dest->height, a, sharpness);
1031 scale1d_calculate_taps_int32 (&scale->y_scale1d,
1032 src->height, dest->height, n_taps, a, sharpness, sharpen, S32_SHIFT2);
1034 scale->dither = dither;
1036 switch (scale->x_scale1d.n_taps) {
1038 scale->horiz_resample_func =
1039 (HorizResampleFunc) resample_horiz_int32_int32_u8_taps4_shift0;
1042 scale->horiz_resample_func =
1043 (HorizResampleFunc) resample_horiz_int32_int32_u8_taps8_shift0;
1046 scale->horiz_resample_func =
1047 (HorizResampleFunc) resample_horiz_int32_int32_u8_taps12_shift0;
1050 scale->horiz_resample_func =
1051 (HorizResampleFunc) resample_horiz_int32_int32_u8_taps16_shift0;
1054 scale->horiz_resample_func =
1055 (HorizResampleFunc) resample_horiz_int32_int32_u8_generic;
1060 g_malloc (sizeof (int32_t) * scale->dest->width * scale->src->height);
1062 vs_scale_lanczos_Y_int32 (scale);
1064 scale1d_cleanup (&scale->x_scale1d);
1065 scale1d_cleanup (&scale->y_scale1d);
1066 g_free (scale->tmpdata);
1070 vs_scale_lanczos_Y_double (Scale * scale)
1078 for (j = 0; j < scale->dest->height; j++) {
1082 destline = scale->dest->pixels + scale->dest->stride * j;
1084 yi = scale->y_scale1d.offsets[j];
1086 while (tmp_yi < yi + scale->y_scale1d.n_taps) {
1087 scale->horiz_resample_func (TMP_LINE_DOUBLE (tmp_yi),
1088 scale->x_scale1d.offsets, scale->x_scale1d.taps, SRC_LINE (tmp_yi),
1089 scale->x_scale1d.n_taps, 0, scale->dest->width);
1093 taps = (double *) scale->y_scale1d.taps + j * scale->y_scale1d.n_taps;
1094 if (scale->dither) {
1095 resample_vert_dither_double_generic (destline,
1096 taps, TMP_LINE_DOUBLE (scale->y_scale1d.offsets[j]),
1097 sizeof (double) * scale->dest->width,
1098 scale->y_scale1d.n_taps, 0, scale->dest->width);
1100 resample_vert_double_generic (destline,
1101 taps, TMP_LINE_DOUBLE (scale->y_scale1d.offsets[j]),
1102 sizeof (double) * scale->dest->width,
1103 scale->y_scale1d.n_taps, 0, scale->dest->width);
1109 vs_image_scale_lanczos_Y_double (const VSImage * dest, const VSImage * src,
1110 uint8_t * tmpbuf, double sharpness, gboolean dither, double a,
1120 n_taps = scale1d_get_n_taps (src->width, dest->width, a, sharpness);
1121 scale1d_calculate_taps (&scale->x_scale1d,
1122 src->width, dest->width, n_taps, a, sharpness, sharpen);
1124 n_taps = scale1d_get_n_taps (src->height, dest->height, a, sharpness);
1125 scale1d_calculate_taps (&scale->y_scale1d,
1126 src->height, dest->height, n_taps, a, sharpness, sharpen);
1128 scale->dither = dither;
1130 scale->horiz_resample_func =
1131 (HorizResampleFunc) resample_horiz_double_u8_generic;
1134 g_malloc (sizeof (double) * scale->dest->width * scale->src->height);
1136 vs_scale_lanczos_Y_double (scale);
1138 scale1d_cleanup (&scale->x_scale1d);
1139 scale1d_cleanup (&scale->y_scale1d);
1140 g_free (scale->tmpdata);
1144 vs_scale_lanczos_Y_float (Scale * scale)
1152 for (j = 0; j < scale->dest->height; j++) {
1156 destline = scale->dest->pixels + scale->dest->stride * j;
1158 yi = scale->y_scale1d.offsets[j];
1160 while (tmp_yi < yi + scale->y_scale1d.n_taps) {
1161 scale->horiz_resample_func (TMP_LINE_FLOAT (tmp_yi),
1162 scale->x_scale1d.offsets, scale->x_scale1d.taps, SRC_LINE (tmp_yi),
1163 scale->x_scale1d.n_taps, 0, scale->dest->width);
1167 taps = (float *) scale->y_scale1d.taps + j * scale->y_scale1d.n_taps;
1168 if (scale->dither) {
1169 resample_vert_dither_float_generic (destline,
1170 taps, TMP_LINE_FLOAT (scale->y_scale1d.offsets[j]),
1171 sizeof (float) * scale->dest->width,
1172 scale->y_scale1d.n_taps, 0, scale->dest->width);
1174 resample_vert_float_generic (destline,
1175 taps, TMP_LINE_FLOAT (scale->y_scale1d.offsets[j]),
1176 sizeof (float) * scale->dest->width,
1177 scale->y_scale1d.n_taps, 0, scale->dest->width);
1183 vs_image_scale_lanczos_Y_float (const VSImage * dest, const VSImage * src,
1184 uint8_t * tmpbuf, double sharpness, gboolean dither, double a,
1194 n_taps = scale1d_get_n_taps (src->width, dest->width, a, sharpness);
1195 scale1d_calculate_taps_float (&scale->x_scale1d,
1196 src->width, dest->width, n_taps, a, sharpness, sharpen);
1198 n_taps = scale1d_get_n_taps (src->height, dest->height, a, sharpness);
1199 scale1d_calculate_taps_float (&scale->y_scale1d,
1200 src->height, dest->height, n_taps, a, sharpness, sharpen);
1202 scale->dither = dither;
1204 scale->horiz_resample_func =
1205 (HorizResampleFunc) resample_horiz_float_u8_generic;
1208 g_malloc (sizeof (float) * scale->dest->width * scale->src->height);
1210 vs_scale_lanczos_Y_float (scale);
1212 scale1d_cleanup (&scale->x_scale1d);
1213 scale1d_cleanup (&scale->y_scale1d);
1214 g_free (scale->tmpdata);
1222 vs_scale_lanczos_AYUV_int16 (Scale * scale)
1230 for (j = 0; j < scale->dest->height; j++) {
1234 destline = scale->dest->pixels + scale->dest->stride * j;
1236 yi = scale->y_scale1d.offsets[j];
1238 while (tmp_yi < yi + scale->y_scale1d.n_taps) {
1239 scale->horiz_resample_func (TMP_LINE_S16_AYUV (tmp_yi),
1240 scale->x_scale1d.offsets, scale->x_scale1d.taps, SRC_LINE (tmp_yi),
1241 scale->x_scale1d.n_taps, S16_MIDSHIFT, scale->dest->width);
1245 taps = (gint16 *) scale->y_scale1d.taps + j * scale->y_scale1d.n_taps;
1246 if (scale->dither) {
1247 resample_vert_dither_int16_generic (destline,
1248 taps, TMP_LINE_S16_AYUV (scale->y_scale1d.offsets[j]),
1249 sizeof (gint16) * 4 * scale->dest->width,
1250 scale->y_scale1d.n_taps, S16_POSTSHIFT, scale->dest->width * 4);
1252 resample_vert_int16_generic (destline,
1253 taps, TMP_LINE_S16_AYUV (scale->y_scale1d.offsets[j]),
1254 sizeof (gint16) * 4 * scale->dest->width,
1255 scale->y_scale1d.n_taps, S16_POSTSHIFT, scale->dest->width * 4);
1261 vs_image_scale_lanczos_AYUV_int16 (const VSImage * dest, const VSImage * src,
1262 uint8_t * tmpbuf, double sharpness, gboolean dither, double a,
1272 n_taps = scale1d_get_n_taps (src->width, dest->width, a, sharpness);
1273 n_taps = ROUND_UP_4 (n_taps);
1274 scale1d_calculate_taps_int16 (&scale->x_scale1d,
1275 src->width, dest->width, n_taps, a, sharpness, sharpen, S16_SHIFT1);
1277 n_taps = scale1d_get_n_taps (src->height, dest->height, a, sharpness);
1278 scale1d_calculate_taps_int16 (&scale->y_scale1d,
1279 src->height, dest->height, n_taps, a, sharpness, sharpen, S16_SHIFT2);
1281 scale->dither = dither;
1283 switch (scale->x_scale1d.n_taps) {
1285 scale->horiz_resample_func =
1286 (HorizResampleFunc) resample_horiz_int16_int16_ayuv_taps4_shift0;
1289 scale->horiz_resample_func =
1290 (HorizResampleFunc) resample_horiz_int16_int16_ayuv_taps8_shift0;
1293 scale->horiz_resample_func =
1294 (HorizResampleFunc) resample_horiz_int16_int16_ayuv_taps12_shift0;
1297 scale->horiz_resample_func =
1298 (HorizResampleFunc) resample_horiz_int16_int16_ayuv_taps16_shift0;
1301 scale->horiz_resample_func =
1302 (HorizResampleFunc) resample_horiz_int16_int16_ayuv_generic;
1307 g_malloc (sizeof (gint16) * scale->dest->width * scale->src->height * 4);
1309 vs_scale_lanczos_AYUV_int16 (scale);
1311 scale1d_cleanup (&scale->x_scale1d);
1312 scale1d_cleanup (&scale->y_scale1d);
1313 g_free (scale->tmpdata);
1318 vs_scale_lanczos_AYUV_int32 (Scale * scale)
1326 for (j = 0; j < scale->dest->height; j++) {
1330 destline = scale->dest->pixels + scale->dest->stride * j;
1332 yi = scale->y_scale1d.offsets[j];
1334 while (tmp_yi < yi + scale->y_scale1d.n_taps) {
1335 scale->horiz_resample_func (TMP_LINE_S32_AYUV (tmp_yi),
1336 scale->x_scale1d.offsets, scale->x_scale1d.taps, SRC_LINE (tmp_yi),
1337 scale->x_scale1d.n_taps, S32_MIDSHIFT, scale->dest->width);
1341 taps = (gint32 *) scale->y_scale1d.taps + j * scale->y_scale1d.n_taps;
1342 if (scale->dither) {
1343 resample_vert_dither_int32_generic (destline,
1344 taps, TMP_LINE_S32_AYUV (scale->y_scale1d.offsets[j]),
1345 sizeof (gint32) * 4 * scale->dest->width, scale->y_scale1d.n_taps,
1346 S32_POSTSHIFT, scale->dest->width * 4);
1348 resample_vert_int32_generic (destline,
1349 taps, TMP_LINE_S32_AYUV (scale->y_scale1d.offsets[j]),
1350 sizeof (gint32) * 4 * scale->dest->width, scale->y_scale1d.n_taps,
1351 S32_POSTSHIFT, scale->dest->width * 4);
1357 vs_image_scale_lanczos_AYUV_int32 (const VSImage * dest, const VSImage * src,
1358 uint8_t * tmpbuf, double sharpness, gboolean dither, double a,
1368 n_taps = scale1d_get_n_taps (src->width, dest->width, a, sharpness);
1369 n_taps = ROUND_UP_4 (n_taps);
1370 scale1d_calculate_taps_int32 (&scale->x_scale1d,
1371 src->width, dest->width, n_taps, a, sharpness, sharpen, S32_SHIFT1);
1373 n_taps = scale1d_get_n_taps (src->height, dest->height, a, sharpness);
1374 scale1d_calculate_taps_int32 (&scale->y_scale1d,
1375 src->height, dest->height, n_taps, a, sharpness, sharpen, S32_SHIFT2);
1377 scale->dither = dither;
1379 switch (scale->x_scale1d.n_taps) {
1381 scale->horiz_resample_func =
1382 (HorizResampleFunc) resample_horiz_int32_int32_ayuv_taps4_shift0;
1385 scale->horiz_resample_func =
1386 (HorizResampleFunc) resample_horiz_int32_int32_ayuv_taps8_shift0;
1389 scale->horiz_resample_func =
1390 (HorizResampleFunc) resample_horiz_int32_int32_ayuv_taps12_shift0;
1393 scale->horiz_resample_func =
1394 (HorizResampleFunc) resample_horiz_int32_int32_ayuv_taps16_shift0;
1397 scale->horiz_resample_func =
1398 (HorizResampleFunc) resample_horiz_int32_int32_ayuv_generic;
1403 g_malloc (sizeof (int32_t) * scale->dest->width * scale->src->height * 4);
1405 vs_scale_lanczos_AYUV_int32 (scale);
1407 scale1d_cleanup (&scale->x_scale1d);
1408 scale1d_cleanup (&scale->y_scale1d);
1409 g_free (scale->tmpdata);
1413 vs_scale_lanczos_AYUV_double (Scale * scale)
1421 for (j = 0; j < scale->dest->height; j++) {
1425 destline = scale->dest->pixels + scale->dest->stride * j;
1427 yi = scale->y_scale1d.offsets[j];
1429 while (tmp_yi < yi + scale->y_scale1d.n_taps) {
1430 scale->horiz_resample_func (TMP_LINE_DOUBLE_AYUV (tmp_yi),
1431 scale->x_scale1d.offsets, scale->x_scale1d.taps, SRC_LINE (tmp_yi),
1432 scale->x_scale1d.n_taps, 0, scale->dest->width);
1436 taps = (double *) scale->y_scale1d.taps + j * scale->y_scale1d.n_taps;
1437 if (scale->dither) {
1438 resample_vert_dither_double_generic (destline,
1439 taps, TMP_LINE_DOUBLE_AYUV (scale->y_scale1d.offsets[j]),
1440 sizeof (double) * 4 * scale->dest->width,
1441 scale->y_scale1d.n_taps, 0, scale->dest->width * 4);
1443 resample_vert_double_generic (destline,
1444 taps, TMP_LINE_DOUBLE_AYUV (scale->y_scale1d.offsets[j]),
1445 sizeof (double) * 4 * scale->dest->width,
1446 scale->y_scale1d.n_taps, 0, scale->dest->width * 4);
1452 vs_image_scale_lanczos_AYUV_double (const VSImage * dest, const VSImage * src,
1453 uint8_t * tmpbuf, double sharpness, gboolean dither, double a,
1463 n_taps = scale1d_get_n_taps (src->width, dest->width, a, sharpness);
1464 scale1d_calculate_taps (&scale->x_scale1d,
1465 src->width, dest->width, n_taps, a, sharpness, sharpen);
1467 n_taps = scale1d_get_n_taps (src->height, dest->height, a, sharpness);
1468 scale1d_calculate_taps (&scale->y_scale1d,
1469 src->height, dest->height, n_taps, a, sharpness, sharpen);
1471 scale->dither = dither;
1473 scale->horiz_resample_func =
1474 (HorizResampleFunc) resample_horiz_double_ayuv_generic;
1477 g_malloc (sizeof (double) * scale->dest->width * scale->src->height * 4);
1479 vs_scale_lanczos_AYUV_double (scale);
1481 scale1d_cleanup (&scale->x_scale1d);
1482 scale1d_cleanup (&scale->y_scale1d);
1483 g_free (scale->tmpdata);
1487 vs_scale_lanczos_AYUV_float (Scale * scale)
1495 for (j = 0; j < scale->dest->height; j++) {
1499 destline = scale->dest->pixels + scale->dest->stride * j;
1501 yi = scale->y_scale1d.offsets[j];
1503 while (tmp_yi < yi + scale->y_scale1d.n_taps) {
1504 scale->horiz_resample_func (TMP_LINE_FLOAT_AYUV (tmp_yi),
1505 scale->x_scale1d.offsets, scale->x_scale1d.taps, SRC_LINE (tmp_yi),
1506 scale->x_scale1d.n_taps, 0, scale->dest->width);
1510 taps = (float *) scale->y_scale1d.taps + j * scale->y_scale1d.n_taps;
1511 if (scale->dither) {
1512 resample_vert_dither_float_generic (destline,
1513 taps, TMP_LINE_FLOAT_AYUV (scale->y_scale1d.offsets[j]),
1514 sizeof (float) * 4 * scale->dest->width, scale->y_scale1d.n_taps, 0,
1515 scale->dest->width * 4);
1517 resample_vert_float_generic (destline,
1518 taps, TMP_LINE_FLOAT_AYUV (scale->y_scale1d.offsets[j]),
1519 sizeof (float) * 4 * scale->dest->width, scale->y_scale1d.n_taps, 0,
1520 scale->dest->width * 4);
1526 vs_image_scale_lanczos_AYUV_float (const VSImage * dest, const VSImage * src,
1527 uint8_t * tmpbuf, double sharpness, gboolean dither, double a,
1537 n_taps = scale1d_get_n_taps (src->width, dest->width, a, sharpness);
1538 scale1d_calculate_taps_float (&scale->x_scale1d,
1539 src->width, dest->width, n_taps, a, sharpness, sharpen);
1541 n_taps = scale1d_get_n_taps (src->height, dest->height, a, sharpness);
1542 scale1d_calculate_taps_float (&scale->y_scale1d,
1543 src->height, dest->height, n_taps, a, sharpness, sharpen);
1545 scale->dither = dither;
1547 scale->horiz_resample_func =
1548 (HorizResampleFunc) resample_horiz_float_ayuv_generic;
1551 g_malloc (sizeof (float) * scale->dest->width * scale->src->height * 4);
1553 vs_scale_lanczos_AYUV_float (scale);
1555 scale1d_cleanup (&scale->x_scale1d);
1556 scale1d_cleanup (&scale->y_scale1d);
1557 g_free (scale->tmpdata);