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.
123 #include "vs_scanline.h"
124 #include "vs_image.h"
126 #include "gstvideoscaleorc.h"
130 #define NEED_CLAMP(x,a,b) ((x) < (a) || (x) > (b))
132 #define ROUND_UP_2(x) (((x)+1)&~1)
133 #define ROUND_UP_4(x) (((x)+3)&~3)
134 #define ROUND_UP_8(x) (((x)+7)&~7)
136 #define SRC_LINE(i) (scale->src->pixels + scale->src->stride * (i))
138 #define TMP_LINE_S16(i) ((gint16 *)scale->tmpdata + (i)*(scale->dest->width))
139 #define TMP_LINE_S32(i) ((gint32 *)scale->tmpdata + (i)*(scale->dest->width))
140 #define TMP_LINE_FLOAT(i) ((float *)scale->tmpdata + (i)*(scale->dest->width))
141 #define TMP_LINE_DOUBLE(i) ((double *)scale->tmpdata + (i)*(scale->dest->width))
142 #define TMP_LINE_S16_AYUV(i) ((gint16 *)scale->tmpdata + (i)*4*(scale->dest->width))
143 #define TMP_LINE_S32_AYUV(i) ((gint32 *)scale->tmpdata + (i)*4*(scale->dest->width))
144 #define TMP_LINE_FLOAT_AYUV(i) ((float *)scale->tmpdata + (i)*4*(scale->dest->width))
145 #define TMP_LINE_DOUBLE_AYUV(i) ((double *)scale->tmpdata + (i)*4*(scale->dest->width))
147 #define PTR_OFFSET(a,b) ((void *)((char *)(a) + (b)))
149 typedef void (*HorizResampleFunc) (void *dest, const gint32 * offsets,
150 const void *taps, const void *src, int n_taps, int shift, int n);
152 typedef struct _Scale1D Scale1D;
168 typedef struct _Scale Scale;
179 HorizResampleFunc horiz_resample_func;
186 vs_image_scale_lanczos_Y_int16 (const VSImage * dest, const VSImage * src,
187 uint8_t * tmpbuf, double sharpness, gboolean dither, double a,
189 static void vs_image_scale_lanczos_Y_int32 (const VSImage * dest,
190 const VSImage * src, uint8_t * tmpbuf, double sharpness, gboolean dither,
191 double a, double sharpen);
192 static void vs_image_scale_lanczos_Y_float (const VSImage * dest,
193 const VSImage * src, uint8_t * tmpbuf, double sharpness, gboolean dither,
194 double a, double sharpen);
195 static void vs_image_scale_lanczos_Y_double (const VSImage * dest,
196 const VSImage * src, uint8_t * tmpbuf, double sharpness, gboolean dither,
197 double a, double sharpen);
199 vs_image_scale_lanczos_AYUV_int16 (const VSImage * dest, const VSImage * src,
200 uint8_t * tmpbuf, double sharpness, gboolean dither, double a,
202 static void vs_image_scale_lanczos_AYUV_int32 (const VSImage * dest,
203 const VSImage * src, uint8_t * tmpbuf, double sharpness, gboolean dither,
204 double a, double sharpen);
205 static void vs_image_scale_lanczos_AYUV_float (const VSImage * dest,
206 const VSImage * src, uint8_t * tmpbuf, double sharpness, gboolean dither,
207 double a, double sharpen);
208 static void vs_image_scale_lanczos_AYUV_double (const VSImage * dest,
209 const VSImage * src, uint8_t * tmpbuf, double sharpness, gboolean dither,
210 double a, double sharpen);
211 static void vs_image_scale_lanczos_AYUV64_double (const VSImage * dest,
212 const VSImage * src, uint8_t * tmpbuf, double sharpness, gboolean dither,
213 double a, double sharpen);
220 return sin (G_PI * x) / (G_PI * x);
226 if (x <= -1 || x >= 1)
232 scale1d_get_n_taps (int src_size, int dest_size, double a, double sharpness)
238 scale = src_size / (double) dest_size;
240 fx = (1.0 / scale) * sharpness;
242 fx = (1.0) * sharpness;
250 scale1d_cleanup (Scale1D * scale)
252 g_free (scale->taps);
253 g_free (scale->offsets);
257 * Calculates a set of taps for each destination element in double
258 * format. Each set of taps sums to 1.0.
262 scale1d_calculate_taps (Scale1D * scale, int src_size, int dest_size,
263 int n_taps, double a, double sharpness, double sharpen)
269 double scale_increment;
274 scale->scale = src_size / (double) dest_size;
275 scale->offset = scale->scale / 2 - 0.5;
277 if (scale->scale > 1.0) {
278 scale->fx = (1.0 / scale->scale) * sharpness;
280 scale->fx = (1.0) * sharpness;
282 scale->ex = scale->fx / a;
283 scale->dx = ceil (a / scale->fx);
285 g_assert (n_taps >= 2 * scale->dx);
286 scale->n_taps = n_taps;
288 scale->taps = g_malloc (sizeof (double) * scale->n_taps * dest_size);
289 scale->offsets = g_malloc (sizeof (gint32) * dest_size);
290 tap_array = scale->taps;
291 offsets = scale->offsets;
293 scale_offset = scale->offset;
294 scale_increment = scale->scale;
299 for (j = 0; j < dest_size; j++) {
306 x = scale_offset + scale_increment * j;
307 x = CLAMP (x, 0, src_size);
312 taps = tap_array + j * n_taps;
314 for (l = 0; l < n_taps; l++) {
316 taps[l] = sinc ((x - xl) * fx) * envelope ((x - xl) * ex);
317 taps[l] -= sharpen * envelope ((x - xl) * ex);
320 g_assert (envelope ((x - (xi - 1)) * ex) == 0);
321 g_assert (envelope ((x - (xi + n_taps)) * ex) == 0);
322 for (l = 0; l < n_taps; l++) {
329 for (l = 0; l < shift; l++) {
330 taps[shift] += taps[l];
332 for (l = 0; l < n_taps - shift; l++) {
333 taps[l] = taps[shift + l];
335 for (; l < n_taps; l++) {
341 if (xi > src_size - n_taps) {
342 int shift = xi - (src_size - n_taps);
344 for (l = 0; l < shift; l++) {
345 taps[n_taps - shift - 1] += taps[n_taps - shift + l];
347 for (l = 0; l < n_taps - shift; l++) {
348 taps[n_taps - 1 - l] = taps[n_taps - 1 - shift - l];
350 for (l = 0; l < shift; l++) {
359 * Calculates a set of taps for each destination element in float
360 * format. Each set of taps sums to 1.0.
363 scale1d_calculate_taps_float (Scale1D * scale, int src_size, int dest_size,
364 int n_taps, double a, double sharpness, double sharpen)
370 scale1d_calculate_taps (scale, src_size, dest_size, n_taps, a, sharpness,
373 taps_d = scale->taps;
374 taps_f = g_malloc (sizeof (float) * scale->n_taps * dest_size);
376 for (j = 0; j < dest_size * n_taps; j++) {
377 taps_f[j] = taps_d[j];
381 scale->taps = taps_f;
385 * Calculates a set of taps for each destination element in gint32
386 * format. Each set of taps sums to (very nearly) (1<<shift). A
387 * typical value for shift is 10 to 15, so that applying the taps to
388 * uint8 values and summing will fit in a (signed) int32.
391 scale1d_calculate_taps_int32 (Scale1D * scale, int src_size, int dest_size,
392 int n_taps, double a, double sharpness, double sharpen, int shift)
400 scale1d_calculate_taps (scale, src_size, dest_size, n_taps, a, sharpness,
403 taps_d = scale->taps;
404 taps_i = g_malloc (sizeof (gint32) * scale->n_taps * dest_size);
406 multiplier = (1 << shift);
408 for (j = 0; j < dest_size; j++) {
409 for (i = 0; i < n_taps; i++) {
410 taps_i[j * n_taps + i] =
411 floor (0.5 + taps_d[j * n_taps + i] * multiplier);
416 scale->taps = taps_i;
420 * Calculates a set of taps for each destination element in gint16
421 * format. Each set of taps sums to (1<<shift). A typical value
422 * for shift is 7, so that applying the taps to uint8 values and
423 * summing will fit in a (signed) int16.
426 scale1d_calculate_taps_int16 (Scale1D * scale, int src_size, int dest_size,
427 int n_taps, double a, double sharpness, double sharpen, int shift)
435 scale1d_calculate_taps (scale, src_size, dest_size, n_taps, a, sharpness,
438 taps_d = scale->taps;
439 taps_i = g_malloc (sizeof (gint16) * scale->n_taps * dest_size);
441 multiplier = (1 << shift);
443 /* Various methods for converting floating point taps to integer.
444 * The dB values are the SSIM value between scaling an image via
445 * the floating point pathway vs. the integer pathway using the
446 * given code to generate the taps. Only one image was tested,
447 * scaling from 1920x1080 to 640x360. Several variations of the
448 * methods were also tested, with nothing appearing useful. */
450 /* Standard round to integer. This causes bad DC errors. */
452 for (j = 0; j < dest_size; j++) {
453 for (i = 0; i < n_taps; i++) {
454 taps_i[j * n_taps + i] =
455 floor (0.5 + taps_d[j * n_taps + i] * multiplier);
460 /* Dithering via error propogation. Works pretty well, but
461 * really we want to propogate errors across rows, which would
462 * mean having several sets of tap arrays. Possible, but more work,
463 * and it may not even be better. */
467 for (j = 0; j < dest_size; j++) {
468 for (i = 0; i < n_taps; i++) {
469 err += taps_d[j * n_taps + i] * multiplier;
470 taps_i[j * n_taps + i] = floor (err);
477 /* Round to integer, but with an adjustable bias that we use to
478 * eliminate the DC error. This search method is a bit crude, and
479 * could perhaps be improved somewhat. */
481 for (j = 0; j < dest_size; j++) {
483 for (k = 0; k < 100; k++) {
488 for (i = 0; i < n_taps; i++) {
489 taps_i[j * n_taps + i] =
490 floor (offset + taps_d[j * n_taps + i] * multiplier);
491 sum += taps_i[j * n_taps + i];
494 if (sum >= (1 << shift))
500 /* Round to integer, but adjust the multiplier. The search method is
501 * wrong a lot, but was sufficient enough to calculate dB error. */
503 for (j = 0; j < dest_size; j++) {
506 for (k = 0; k < 200; k++) {
509 multiplier = (1 << shift) - 1.0 + k * 0.01;
510 for (i = 0; i < n_taps; i++) {
511 taps_i[j * n_taps + i] =
512 floor (0.5 + taps_d[j * n_taps + i] * multiplier);
513 sum += taps_i[j * n_taps + i];
516 if (sum >= (1 << shift))
519 if (sum != (1 << shift)) {
520 GST_ERROR ("%g %d", multiplier, sum);
525 /* Round to integer, but subtract the error from the largest tap */
527 for (j = 0; j < dest_size; j++) {
528 int err = -multiplier;
529 for (i = 0; i < n_taps; i++) {
530 taps_i[j * n_taps + i] =
531 floor (0.5 + taps_d[j * n_taps + i] * multiplier);
532 err += taps_i[j * n_taps + i];
534 if (taps_i[j * n_taps + (n_taps / 2 - 1)] >
535 taps_i[j * n_taps + (n_taps / 2)]) {
536 taps_i[j * n_taps + (n_taps / 2 - 1)] -= err;
538 taps_i[j * n_taps + (n_taps / 2)] -= err;
544 scale->taps = taps_i;
549 vs_image_scale_lanczos_Y (const VSImage * dest, const VSImage * src,
550 uint8_t * tmpbuf, double sharpness, gboolean dither, int submethod,
551 double a, double sharpen)
556 vs_image_scale_lanczos_Y_int16 (dest, src, tmpbuf, sharpness, dither, a,
560 vs_image_scale_lanczos_Y_int32 (dest, src, tmpbuf, sharpness, dither, a,
564 vs_image_scale_lanczos_Y_float (dest, src, tmpbuf, sharpness, dither, a,
568 vs_image_scale_lanczos_Y_double (dest, src, tmpbuf, sharpness, dither, a,
575 vs_image_scale_lanczos_AYUV (const VSImage * dest, const VSImage * src,
576 uint8_t * tmpbuf, double sharpness, gboolean dither, int submethod,
577 double a, double sharpen)
582 vs_image_scale_lanczos_AYUV_int16 (dest, src, tmpbuf, sharpness, dither,
586 vs_image_scale_lanczos_AYUV_int32 (dest, src, tmpbuf, sharpness, dither,
590 vs_image_scale_lanczos_AYUV_float (dest, src, tmpbuf, sharpness, dither,
594 vs_image_scale_lanczos_AYUV_double (dest, src, tmpbuf, sharpness, dither,
601 vs_image_scale_lanczos_AYUV64 (const VSImage * dest, const VSImage * src,
602 uint8_t * tmpbuf, double sharpness, gboolean dither, int submethod,
603 double a, double sharpen)
605 vs_image_scale_lanczos_AYUV64_double (dest, src, tmpbuf, sharpness, dither,
611 #define RESAMPLE_HORIZ_FLOAT(function, dest_type, tap_type, src_type, _n_taps) \
613 function (dest_type *dest, const gint32 *offsets, \
614 const tap_type *taps, const src_type *src, int n_taps, int shift, int n) \
619 const src_type *srcline; \
620 const tap_type *tapsline; \
621 for (i = 0; i < n; i++) { \
622 srcline = src + offsets[i]; \
623 tapsline = taps + i * _n_taps; \
625 for (k = 0; k < _n_taps; k++) { \
626 sum += srcline[k] * tapsline[k]; \
632 #define RESAMPLE_HORIZ(function, dest_type, tap_type, src_type, _n_taps, _shift) \
634 function (dest_type *dest, const gint32 *offsets, \
635 const tap_type *taps, const src_type *src, int n_taps, int shift, int n) \
640 const src_type *srcline; \
641 const tap_type *tapsline; \
643 if (_shift > 0) offset = (1<<_shift)>>1; \
645 for (i = 0; i < n; i++) { \
646 srcline = src + offsets[i]; \
647 tapsline = taps + i * _n_taps; \
649 for (k = 0; k < _n_taps; k++) { \
650 sum += srcline[k] * tapsline[k]; \
652 dest[i] = (sum + offset) >> _shift; \
656 #define RESAMPLE_HORIZ_AYUV_FLOAT(function, dest_type, tap_type, src_type, _n_taps) \
658 function (dest_type *dest, const gint32 *offsets, \
659 const tap_type *taps, const src_type *src, int n_taps, int shift, int n) \
667 const src_type *srcline; \
668 const tap_type *tapsline; \
669 for (i = 0; i < n; i++) { \
670 srcline = src + 4*offsets[i]; \
671 tapsline = taps + i * _n_taps; \
676 for (k = 0; k < _n_taps; k++) { \
677 sum1 += srcline[k*4+0] * tapsline[k]; \
678 sum2 += srcline[k*4+1] * tapsline[k]; \
679 sum3 += srcline[k*4+2] * tapsline[k]; \
680 sum4 += srcline[k*4+3] * tapsline[k]; \
682 dest[i*4+0] = sum1; \
683 dest[i*4+1] = sum2; \
684 dest[i*4+2] = sum3; \
685 dest[i*4+3] = sum4; \
689 #define RESAMPLE_HORIZ_AYUV(function, dest_type, tap_type, src_type, _n_taps, _shift) \
691 function (dest_type *dest, const gint32 *offsets, \
692 const tap_type *taps, const src_type *src, int n_taps, int shift, int n) \
700 const src_type *srcline; \
701 const tap_type *tapsline; \
703 if (_shift > 0) offset = (1<<_shift)>>1; \
705 for (i = 0; i < n; i++) { \
706 srcline = src + 4*offsets[i]; \
707 tapsline = taps + i * _n_taps; \
712 for (k = 0; k < _n_taps; k++) { \
713 sum1 += srcline[k*4+0] * tapsline[k]; \
714 sum2 += srcline[k*4+1] * tapsline[k]; \
715 sum3 += srcline[k*4+2] * tapsline[k]; \
716 sum4 += srcline[k*4+3] * tapsline[k]; \
718 dest[i*4+0] = (sum1 + offset) >> _shift; \
719 dest[i*4+1] = (sum2 + offset) >> _shift; \
720 dest[i*4+2] = (sum3 + offset) >> _shift; \
721 dest[i*4+3] = (sum4 + offset) >> _shift; \
726 RESAMPLE_HORIZ_FLOAT (resample_horiz_double_u8_generic, double, double,
728 RESAMPLE_HORIZ_FLOAT (resample_horiz_float_u8_generic, float, float,
730 RESAMPLE_HORIZ_AYUV_FLOAT (resample_horiz_double_ayuv_generic, double, double,
732 RESAMPLE_HORIZ_AYUV_FLOAT (resample_horiz_float_ayuv_generic, float, float,
735 RESAMPLE_HORIZ_AYUV_FLOAT (resample_horiz_double_ayuv_generic_s16, double, double,
738 RESAMPLE_HORIZ (resample_horiz_int32_int32_u8_generic, gint32, gint32,
739 guint8, n_taps, shift)
740 RESAMPLE_HORIZ (resample_horiz_int16_int16_u8_generic, gint16, gint16,
741 guint8, n_taps, shift)
742 RESAMPLE_HORIZ_AYUV (resample_horiz_int32_int32_ayuv_generic, gint32, gint32,
743 guint8, n_taps, shift)
744 RESAMPLE_HORIZ_AYUV (resample_horiz_int16_int16_ayuv_generic, gint16, gint16,
745 guint8, n_taps, shift)
747 /* Candidates for orcification */
748 RESAMPLE_HORIZ (resample_horiz_int32_int32_u8_taps16_shift0, gint32, gint32,
750 RESAMPLE_HORIZ (resample_horiz_int32_int32_u8_taps12_shift0, gint32, gint32,
752 RESAMPLE_HORIZ (resample_horiz_int32_int32_u8_taps8_shift0, gint32, gint32,
754 RESAMPLE_HORIZ (resample_horiz_int32_int32_u8_taps4_shift0, gint32, gint32,
756 RESAMPLE_HORIZ (resample_horiz_int16_int16_u8_taps16_shift0, gint16, gint16,
758 RESAMPLE_HORIZ (resample_horiz_int16_int16_u8_taps12_shift0, gint16, gint16,
760 RESAMPLE_HORIZ (resample_horiz_int16_int16_u8_taps8_shift0, gint16, gint16,
762 RESAMPLE_HORIZ (resample_horiz_int16_int16_u8_taps4_shift0, gint16, gint16,
765 RESAMPLE_HORIZ_AYUV (resample_horiz_int32_int32_ayuv_taps16_shift0, gint32, gint32,
767 RESAMPLE_HORIZ_AYUV (resample_horiz_int32_int32_ayuv_taps12_shift0, gint32, gint32,
769 RESAMPLE_HORIZ_AYUV (resample_horiz_int32_int32_ayuv_taps8_shift0, gint32, gint32,
771 RESAMPLE_HORIZ_AYUV (resample_horiz_int32_int32_ayuv_taps4_shift0, gint32, gint32,
773 RESAMPLE_HORIZ_AYUV (resample_horiz_int16_int16_ayuv_taps16_shift0, gint16, gint16,
775 RESAMPLE_HORIZ_AYUV (resample_horiz_int16_int16_ayuv_taps12_shift0, gint16, gint16,
777 RESAMPLE_HORIZ_AYUV (resample_horiz_int16_int16_ayuv_taps8_shift0, gint16, gint16,
779 RESAMPLE_HORIZ_AYUV (resample_horiz_int16_int16_ayuv_taps4_shift0, gint16, gint16,
783 #define RESAMPLE_VERT(function, tap_type, src_type, _n_taps, _shift) \
785 function (guint8 *dest, \
786 const tap_type *taps, const src_type *src, int stride, int n_taps, \
792 gint32 offset = (1<<_shift) >> 1; \
793 for (i = 0; i < n; i++) { \
795 for (l = 0; l < n_taps; l++) { \
796 const src_type *line = PTR_OFFSET(src, stride * l); \
797 sum_y += line[i] * taps[l]; \
799 dest[i] = CLAMP ((sum_y + offset) >> _shift, 0, 255); \
803 #define RESAMPLE_VERT_DITHER(function, tap_type, src_type, _n_taps, _shift) \
805 function (guint8 *dest, \
806 const tap_type *taps, const src_type *src, int stride, int n_taps, \
813 gint32 mask = (1<<_shift) - 1; \
814 for (i = 0; i < n; i++) { \
816 for (l = 0; l < n_taps; l++) { \
817 const src_type *line = PTR_OFFSET(src, stride * l); \
818 sum_y += line[i] * taps[l]; \
821 dest[i] = CLAMP (err_y >> _shift, 0, 255); \
827 RESAMPLE_VERT (resample_vert_int32_generic, gint32, gint32, n_taps, shift)
828 RESAMPLE_VERT_DITHER (resample_vert_dither_int32_generic, gint32, gint32,
830 RESAMPLE_VERT (resample_vert_int16_generic, gint16, gint16, n_taps, shift);
831 RESAMPLE_VERT_DITHER (resample_vert_dither_int16_generic, gint16, gint16,
835 #define RESAMPLE_VERT_FLOAT(function, dest_type, clamp, tap_type, src_type, _n_taps, _shift) \
837 function (dest_type *dest, \
838 const tap_type *taps, const src_type *src, int stride, int n_taps, \
844 for (i = 0; i < n; i++) { \
846 for (l = 0; l < n_taps; l++) { \
847 const src_type *line = PTR_OFFSET(src, stride * l); \
848 sum_y += line[i] * taps[l]; \
850 dest[i] = CLAMP (floor(0.5 + sum_y), 0, clamp); \
854 #define RESAMPLE_VERT_FLOAT_DITHER(function, dest_type, clamp, tap_type, src_type, _n_taps, _shift) \
856 function (dest_type *dest, \
857 const tap_type *taps, const src_type *src, int stride, int n_taps, \
863 src_type err_y = 0; \
864 for (i = 0; i < n; i++) { \
866 for (l = 0; l < n_taps; l++) { \
867 const src_type *line = PTR_OFFSET(src, stride * l); \
868 sum_y += line[i] * taps[l]; \
871 dest[i] = CLAMP (floor (err_y), 0, clamp); \
872 err_y -= floor (err_y); \
877 RESAMPLE_VERT_FLOAT (resample_vert_double_generic, guint8, 255, double, double, n_taps,
879 RESAMPLE_VERT_FLOAT_DITHER (resample_vert_dither_double_generic, guint8, 255, double, double,
882 RESAMPLE_VERT_FLOAT (resample_vert_double_generic_u16, guint16, 65535, double, double, n_taps,
884 RESAMPLE_VERT_FLOAT_DITHER (resample_vert_dither_double_generic_u16, guint16, 65535, double, double,
887 RESAMPLE_VERT_FLOAT (resample_vert_float_generic, guint8, 255, float, float, n_taps, shift)
888 RESAMPLE_VERT_FLOAT_DITHER (resample_vert_dither_float_generic, guint8, 255, float, float,
894 #define S16_MIDSHIFT 0
895 #define S16_POSTSHIFT (S16_SHIFT1+S16_SHIFT2-S16_MIDSHIFT)
898 vs_scale_lanczos_Y_int16 (Scale * scale)
906 for (j = 0; j < scale->dest->height; j++) {
910 destline = scale->dest->pixels + scale->dest->stride * j;
912 yi = scale->y_scale1d.offsets[j];
914 while (tmp_yi < yi + scale->y_scale1d.n_taps) {
915 scale->horiz_resample_func (TMP_LINE_S16 (tmp_yi),
916 scale->x_scale1d.offsets, scale->x_scale1d.taps, SRC_LINE (tmp_yi),
917 scale->x_scale1d.n_taps, S16_MIDSHIFT, scale->dest->width);
921 taps = (gint16 *) scale->y_scale1d.taps + j * scale->y_scale1d.n_taps;
923 resample_vert_dither_int16_generic (destline,
924 taps, TMP_LINE_S16 (scale->y_scale1d.offsets[j]),
925 sizeof (gint16) * scale->dest->width, scale->y_scale1d.n_taps,
926 S16_POSTSHIFT, scale->dest->width);
928 resample_vert_int16_generic (destline,
929 taps, TMP_LINE_S16 (scale->y_scale1d.offsets[j]),
930 sizeof (gint16) * scale->dest->width, scale->y_scale1d.n_taps,
931 S16_POSTSHIFT, scale->dest->width);
937 vs_image_scale_lanczos_Y_int16 (const VSImage * dest, const VSImage * src,
938 uint8_t * tmpbuf, double sharpness, gboolean dither, double a,
948 n_taps = scale1d_get_n_taps (src->width, dest->width, a, sharpness);
949 n_taps = ROUND_UP_4 (n_taps);
950 scale1d_calculate_taps_int16 (&scale->x_scale1d,
951 src->width, dest->width, n_taps, a, sharpness, sharpen, S16_SHIFT1);
953 n_taps = scale1d_get_n_taps (src->height, dest->height, a, sharpness);
954 scale1d_calculate_taps_int16 (&scale->y_scale1d,
955 src->height, dest->height, n_taps, a, sharpness, sharpen, S16_SHIFT2);
957 scale->dither = dither;
959 switch (scale->x_scale1d.n_taps) {
961 scale->horiz_resample_func =
962 (HorizResampleFunc) resample_horiz_int16_int16_u8_taps4_shift0;
965 scale->horiz_resample_func =
966 (HorizResampleFunc) resample_horiz_int16_int16_u8_taps8_shift0;
969 scale->horiz_resample_func =
970 (HorizResampleFunc) resample_horiz_int16_int16_u8_taps12_shift0;
973 scale->horiz_resample_func =
974 (HorizResampleFunc) resample_horiz_int16_int16_u8_taps16_shift0;
977 scale->horiz_resample_func =
978 (HorizResampleFunc) resample_horiz_int16_int16_u8_generic;
983 g_malloc (sizeof (gint16) * scale->dest->width * scale->src->height);
985 vs_scale_lanczos_Y_int16 (scale);
987 scale1d_cleanup (&scale->x_scale1d);
988 scale1d_cleanup (&scale->y_scale1d);
989 g_free (scale->tmpdata);
993 #define S32_SHIFT1 11
994 #define S32_SHIFT2 11
995 #define S32_MIDSHIFT 0
996 #define S32_POSTSHIFT (S32_SHIFT1+S32_SHIFT2-S32_MIDSHIFT)
999 vs_scale_lanczos_Y_int32 (Scale * scale)
1007 for (j = 0; j < scale->dest->height; j++) {
1011 destline = scale->dest->pixels + scale->dest->stride * j;
1013 yi = scale->y_scale1d.offsets[j];
1015 while (tmp_yi < yi + scale->y_scale1d.n_taps) {
1016 scale->horiz_resample_func (TMP_LINE_S32 (tmp_yi),
1017 scale->x_scale1d.offsets, scale->x_scale1d.taps, SRC_LINE (tmp_yi),
1018 scale->x_scale1d.n_taps, S32_MIDSHIFT, scale->dest->width);
1022 taps = (gint32 *) scale->y_scale1d.taps + j * scale->y_scale1d.n_taps;
1023 if (scale->dither) {
1024 resample_vert_dither_int32_generic (destline,
1025 taps, TMP_LINE_S32 (scale->y_scale1d.offsets[j]),
1026 sizeof (gint32) * scale->dest->width,
1027 scale->y_scale1d.n_taps, S32_POSTSHIFT, scale->dest->width);
1029 resample_vert_int32_generic (destline,
1030 taps, TMP_LINE_S32 (scale->y_scale1d.offsets[j]),
1031 sizeof (gint32) * scale->dest->width,
1032 scale->y_scale1d.n_taps, S32_POSTSHIFT, scale->dest->width);
1038 vs_image_scale_lanczos_Y_int32 (const VSImage * dest, const VSImage * src,
1039 uint8_t * tmpbuf, double sharpness, gboolean dither, double a,
1049 n_taps = scale1d_get_n_taps (src->width, dest->width, a, sharpness);
1050 n_taps = ROUND_UP_4 (n_taps);
1051 scale1d_calculate_taps_int32 (&scale->x_scale1d,
1052 src->width, dest->width, n_taps, a, sharpness, sharpen, S32_SHIFT1);
1054 n_taps = scale1d_get_n_taps (src->height, dest->height, a, sharpness);
1055 scale1d_calculate_taps_int32 (&scale->y_scale1d,
1056 src->height, dest->height, n_taps, a, sharpness, sharpen, S32_SHIFT2);
1058 scale->dither = dither;
1060 switch (scale->x_scale1d.n_taps) {
1062 scale->horiz_resample_func =
1063 (HorizResampleFunc) resample_horiz_int32_int32_u8_taps4_shift0;
1066 scale->horiz_resample_func =
1067 (HorizResampleFunc) resample_horiz_int32_int32_u8_taps8_shift0;
1070 scale->horiz_resample_func =
1071 (HorizResampleFunc) resample_horiz_int32_int32_u8_taps12_shift0;
1074 scale->horiz_resample_func =
1075 (HorizResampleFunc) resample_horiz_int32_int32_u8_taps16_shift0;
1078 scale->horiz_resample_func =
1079 (HorizResampleFunc) resample_horiz_int32_int32_u8_generic;
1084 g_malloc (sizeof (int32_t) * scale->dest->width * scale->src->height);
1086 vs_scale_lanczos_Y_int32 (scale);
1088 scale1d_cleanup (&scale->x_scale1d);
1089 scale1d_cleanup (&scale->y_scale1d);
1090 g_free (scale->tmpdata);
1094 vs_scale_lanczos_Y_double (Scale * scale)
1102 for (j = 0; j < scale->dest->height; j++) {
1106 destline = scale->dest->pixels + scale->dest->stride * j;
1108 yi = scale->y_scale1d.offsets[j];
1110 while (tmp_yi < yi + scale->y_scale1d.n_taps) {
1111 scale->horiz_resample_func (TMP_LINE_DOUBLE (tmp_yi),
1112 scale->x_scale1d.offsets, scale->x_scale1d.taps, SRC_LINE (tmp_yi),
1113 scale->x_scale1d.n_taps, 0, scale->dest->width);
1117 taps = (double *) scale->y_scale1d.taps + j * scale->y_scale1d.n_taps;
1118 if (scale->dither) {
1119 resample_vert_dither_double_generic (destline,
1120 taps, TMP_LINE_DOUBLE (scale->y_scale1d.offsets[j]),
1121 sizeof (double) * scale->dest->width,
1122 scale->y_scale1d.n_taps, 0, scale->dest->width);
1124 resample_vert_double_generic (destline,
1125 taps, TMP_LINE_DOUBLE (scale->y_scale1d.offsets[j]),
1126 sizeof (double) * scale->dest->width,
1127 scale->y_scale1d.n_taps, 0, scale->dest->width);
1133 vs_image_scale_lanczos_Y_double (const VSImage * dest, const VSImage * src,
1134 uint8_t * tmpbuf, double sharpness, gboolean dither, double a,
1144 n_taps = scale1d_get_n_taps (src->width, dest->width, a, sharpness);
1145 scale1d_calculate_taps (&scale->x_scale1d,
1146 src->width, dest->width, n_taps, a, sharpness, sharpen);
1148 n_taps = scale1d_get_n_taps (src->height, dest->height, a, sharpness);
1149 scale1d_calculate_taps (&scale->y_scale1d,
1150 src->height, dest->height, n_taps, a, sharpness, sharpen);
1152 scale->dither = dither;
1154 scale->horiz_resample_func =
1155 (HorizResampleFunc) resample_horiz_double_u8_generic;
1158 g_malloc (sizeof (double) * scale->dest->width * scale->src->height);
1160 vs_scale_lanczos_Y_double (scale);
1162 scale1d_cleanup (&scale->x_scale1d);
1163 scale1d_cleanup (&scale->y_scale1d);
1164 g_free (scale->tmpdata);
1168 vs_scale_lanczos_Y_float (Scale * scale)
1176 for (j = 0; j < scale->dest->height; j++) {
1180 destline = scale->dest->pixels + scale->dest->stride * j;
1182 yi = scale->y_scale1d.offsets[j];
1184 while (tmp_yi < yi + scale->y_scale1d.n_taps) {
1185 scale->horiz_resample_func (TMP_LINE_FLOAT (tmp_yi),
1186 scale->x_scale1d.offsets, scale->x_scale1d.taps, SRC_LINE (tmp_yi),
1187 scale->x_scale1d.n_taps, 0, scale->dest->width);
1191 taps = (float *) scale->y_scale1d.taps + j * scale->y_scale1d.n_taps;
1192 if (scale->dither) {
1193 resample_vert_dither_float_generic (destline,
1194 taps, TMP_LINE_FLOAT (scale->y_scale1d.offsets[j]),
1195 sizeof (float) * scale->dest->width,
1196 scale->y_scale1d.n_taps, 0, scale->dest->width);
1198 resample_vert_float_generic (destline,
1199 taps, TMP_LINE_FLOAT (scale->y_scale1d.offsets[j]),
1200 sizeof (float) * scale->dest->width,
1201 scale->y_scale1d.n_taps, 0, scale->dest->width);
1207 vs_image_scale_lanczos_Y_float (const VSImage * dest, const VSImage * src,
1208 uint8_t * tmpbuf, double sharpness, gboolean dither, double a,
1218 n_taps = scale1d_get_n_taps (src->width, dest->width, a, sharpness);
1219 scale1d_calculate_taps_float (&scale->x_scale1d,
1220 src->width, dest->width, n_taps, a, sharpness, sharpen);
1222 n_taps = scale1d_get_n_taps (src->height, dest->height, a, sharpness);
1223 scale1d_calculate_taps_float (&scale->y_scale1d,
1224 src->height, dest->height, n_taps, a, sharpness, sharpen);
1226 scale->dither = dither;
1228 scale->horiz_resample_func =
1229 (HorizResampleFunc) resample_horiz_float_u8_generic;
1232 g_malloc (sizeof (float) * scale->dest->width * scale->src->height);
1234 vs_scale_lanczos_Y_float (scale);
1236 scale1d_cleanup (&scale->x_scale1d);
1237 scale1d_cleanup (&scale->y_scale1d);
1238 g_free (scale->tmpdata);
1246 vs_scale_lanczos_AYUV_int16 (Scale * scale)
1254 for (j = 0; j < scale->dest->height; j++) {
1258 destline = scale->dest->pixels + scale->dest->stride * j;
1260 yi = scale->y_scale1d.offsets[j];
1262 while (tmp_yi < yi + scale->y_scale1d.n_taps) {
1263 scale->horiz_resample_func (TMP_LINE_S16_AYUV (tmp_yi),
1264 scale->x_scale1d.offsets, scale->x_scale1d.taps, SRC_LINE (tmp_yi),
1265 scale->x_scale1d.n_taps, S16_MIDSHIFT, scale->dest->width);
1269 taps = (gint16 *) scale->y_scale1d.taps + j * scale->y_scale1d.n_taps;
1270 if (scale->dither) {
1271 resample_vert_dither_int16_generic (destline,
1272 taps, TMP_LINE_S16_AYUV (scale->y_scale1d.offsets[j]),
1273 sizeof (gint16) * 4 * scale->dest->width,
1274 scale->y_scale1d.n_taps, S16_POSTSHIFT, scale->dest->width * 4);
1276 resample_vert_int16_generic (destline,
1277 taps, TMP_LINE_S16_AYUV (scale->y_scale1d.offsets[j]),
1278 sizeof (gint16) * 4 * scale->dest->width,
1279 scale->y_scale1d.n_taps, S16_POSTSHIFT, scale->dest->width * 4);
1285 vs_image_scale_lanczos_AYUV_int16 (const VSImage * dest, const VSImage * src,
1286 uint8_t * tmpbuf, double sharpness, gboolean dither, double a,
1296 n_taps = scale1d_get_n_taps (src->width, dest->width, a, sharpness);
1297 n_taps = ROUND_UP_4 (n_taps);
1298 scale1d_calculate_taps_int16 (&scale->x_scale1d,
1299 src->width, dest->width, n_taps, a, sharpness, sharpen, S16_SHIFT1);
1301 n_taps = scale1d_get_n_taps (src->height, dest->height, a, sharpness);
1302 scale1d_calculate_taps_int16 (&scale->y_scale1d,
1303 src->height, dest->height, n_taps, a, sharpness, sharpen, S16_SHIFT2);
1305 scale->dither = dither;
1307 switch (scale->x_scale1d.n_taps) {
1309 scale->horiz_resample_func =
1310 (HorizResampleFunc) resample_horiz_int16_int16_ayuv_taps4_shift0;
1313 scale->horiz_resample_func =
1314 (HorizResampleFunc) resample_horiz_int16_int16_ayuv_taps8_shift0;
1317 scale->horiz_resample_func =
1318 (HorizResampleFunc) resample_horiz_int16_int16_ayuv_taps12_shift0;
1321 scale->horiz_resample_func =
1322 (HorizResampleFunc) resample_horiz_int16_int16_ayuv_taps16_shift0;
1325 scale->horiz_resample_func =
1326 (HorizResampleFunc) resample_horiz_int16_int16_ayuv_generic;
1331 g_malloc (sizeof (gint16) * scale->dest->width * scale->src->height * 4);
1333 vs_scale_lanczos_AYUV_int16 (scale);
1335 scale1d_cleanup (&scale->x_scale1d);
1336 scale1d_cleanup (&scale->y_scale1d);
1337 g_free (scale->tmpdata);
1342 vs_scale_lanczos_AYUV_int32 (Scale * scale)
1350 for (j = 0; j < scale->dest->height; j++) {
1354 destline = scale->dest->pixels + scale->dest->stride * j;
1356 yi = scale->y_scale1d.offsets[j];
1358 while (tmp_yi < yi + scale->y_scale1d.n_taps) {
1359 scale->horiz_resample_func (TMP_LINE_S32_AYUV (tmp_yi),
1360 scale->x_scale1d.offsets, scale->x_scale1d.taps, SRC_LINE (tmp_yi),
1361 scale->x_scale1d.n_taps, S32_MIDSHIFT, scale->dest->width);
1365 taps = (gint32 *) scale->y_scale1d.taps + j * scale->y_scale1d.n_taps;
1366 if (scale->dither) {
1367 resample_vert_dither_int32_generic (destline,
1368 taps, TMP_LINE_S32_AYUV (scale->y_scale1d.offsets[j]),
1369 sizeof (gint32) * 4 * scale->dest->width, scale->y_scale1d.n_taps,
1370 S32_POSTSHIFT, scale->dest->width * 4);
1372 resample_vert_int32_generic (destline,
1373 taps, TMP_LINE_S32_AYUV (scale->y_scale1d.offsets[j]),
1374 sizeof (gint32) * 4 * scale->dest->width, scale->y_scale1d.n_taps,
1375 S32_POSTSHIFT, scale->dest->width * 4);
1381 vs_image_scale_lanczos_AYUV_int32 (const VSImage * dest, const VSImage * src,
1382 uint8_t * tmpbuf, double sharpness, gboolean dither, double a,
1392 n_taps = scale1d_get_n_taps (src->width, dest->width, a, sharpness);
1393 n_taps = ROUND_UP_4 (n_taps);
1394 scale1d_calculate_taps_int32 (&scale->x_scale1d,
1395 src->width, dest->width, n_taps, a, sharpness, sharpen, S32_SHIFT1);
1397 n_taps = scale1d_get_n_taps (src->height, dest->height, a, sharpness);
1398 scale1d_calculate_taps_int32 (&scale->y_scale1d,
1399 src->height, dest->height, n_taps, a, sharpness, sharpen, S32_SHIFT2);
1401 scale->dither = dither;
1403 switch (scale->x_scale1d.n_taps) {
1405 scale->horiz_resample_func =
1406 (HorizResampleFunc) resample_horiz_int32_int32_ayuv_taps4_shift0;
1409 scale->horiz_resample_func =
1410 (HorizResampleFunc) resample_horiz_int32_int32_ayuv_taps8_shift0;
1413 scale->horiz_resample_func =
1414 (HorizResampleFunc) resample_horiz_int32_int32_ayuv_taps12_shift0;
1417 scale->horiz_resample_func =
1418 (HorizResampleFunc) resample_horiz_int32_int32_ayuv_taps16_shift0;
1421 scale->horiz_resample_func =
1422 (HorizResampleFunc) resample_horiz_int32_int32_ayuv_generic;
1427 g_malloc (sizeof (int32_t) * scale->dest->width * scale->src->height * 4);
1429 vs_scale_lanczos_AYUV_int32 (scale);
1431 scale1d_cleanup (&scale->x_scale1d);
1432 scale1d_cleanup (&scale->y_scale1d);
1433 g_free (scale->tmpdata);
1437 vs_scale_lanczos_AYUV_double (Scale * scale)
1445 for (j = 0; j < scale->dest->height; j++) {
1449 destline = scale->dest->pixels + scale->dest->stride * j;
1451 yi = scale->y_scale1d.offsets[j];
1453 while (tmp_yi < yi + scale->y_scale1d.n_taps) {
1454 scale->horiz_resample_func (TMP_LINE_DOUBLE_AYUV (tmp_yi),
1455 scale->x_scale1d.offsets, scale->x_scale1d.taps, SRC_LINE (tmp_yi),
1456 scale->x_scale1d.n_taps, 0, scale->dest->width);
1460 taps = (double *) scale->y_scale1d.taps + j * scale->y_scale1d.n_taps;
1461 if (scale->dither) {
1462 resample_vert_dither_double_generic (destline,
1463 taps, TMP_LINE_DOUBLE_AYUV (scale->y_scale1d.offsets[j]),
1464 sizeof (double) * 4 * scale->dest->width,
1465 scale->y_scale1d.n_taps, 0, scale->dest->width * 4);
1467 resample_vert_double_generic (destline,
1468 taps, TMP_LINE_DOUBLE_AYUV (scale->y_scale1d.offsets[j]),
1469 sizeof (double) * 4 * scale->dest->width,
1470 scale->y_scale1d.n_taps, 0, scale->dest->width * 4);
1476 vs_image_scale_lanczos_AYUV_double (const VSImage * dest, const VSImage * src,
1477 uint8_t * tmpbuf, double sharpness, gboolean dither, double a,
1487 n_taps = scale1d_get_n_taps (src->width, dest->width, a, sharpness);
1488 scale1d_calculate_taps (&scale->x_scale1d,
1489 src->width, dest->width, n_taps, a, sharpness, sharpen);
1491 n_taps = scale1d_get_n_taps (src->height, dest->height, a, sharpness);
1492 scale1d_calculate_taps (&scale->y_scale1d,
1493 src->height, dest->height, n_taps, a, sharpness, sharpen);
1495 scale->dither = dither;
1497 scale->horiz_resample_func =
1498 (HorizResampleFunc) resample_horiz_double_ayuv_generic;
1501 g_malloc (sizeof (double) * scale->dest->width * scale->src->height * 4);
1503 vs_scale_lanczos_AYUV_double (scale);
1505 scale1d_cleanup (&scale->x_scale1d);
1506 scale1d_cleanup (&scale->y_scale1d);
1507 g_free (scale->tmpdata);
1511 vs_scale_lanczos_AYUV_float (Scale * scale)
1519 for (j = 0; j < scale->dest->height; j++) {
1523 destline = scale->dest->pixels + scale->dest->stride * j;
1525 yi = scale->y_scale1d.offsets[j];
1527 while (tmp_yi < yi + scale->y_scale1d.n_taps) {
1528 scale->horiz_resample_func (TMP_LINE_FLOAT_AYUV (tmp_yi),
1529 scale->x_scale1d.offsets, scale->x_scale1d.taps, SRC_LINE (tmp_yi),
1530 scale->x_scale1d.n_taps, 0, scale->dest->width);
1534 taps = (float *) scale->y_scale1d.taps + j * scale->y_scale1d.n_taps;
1535 if (scale->dither) {
1536 resample_vert_dither_float_generic (destline,
1537 taps, TMP_LINE_FLOAT_AYUV (scale->y_scale1d.offsets[j]),
1538 sizeof (float) * 4 * scale->dest->width, scale->y_scale1d.n_taps, 0,
1539 scale->dest->width * 4);
1541 resample_vert_float_generic (destline,
1542 taps, TMP_LINE_FLOAT_AYUV (scale->y_scale1d.offsets[j]),
1543 sizeof (float) * 4 * scale->dest->width, scale->y_scale1d.n_taps, 0,
1544 scale->dest->width * 4);
1550 vs_image_scale_lanczos_AYUV_float (const VSImage * dest, const VSImage * src,
1551 uint8_t * tmpbuf, double sharpness, gboolean dither, double a,
1561 n_taps = scale1d_get_n_taps (src->width, dest->width, a, sharpness);
1562 scale1d_calculate_taps_float (&scale->x_scale1d,
1563 src->width, dest->width, n_taps, a, sharpness, sharpen);
1565 n_taps = scale1d_get_n_taps (src->height, dest->height, a, sharpness);
1566 scale1d_calculate_taps_float (&scale->y_scale1d,
1567 src->height, dest->height, n_taps, a, sharpness, sharpen);
1569 scale->dither = dither;
1571 scale->horiz_resample_func =
1572 (HorizResampleFunc) resample_horiz_float_ayuv_generic;
1575 g_malloc (sizeof (float) * scale->dest->width * scale->src->height * 4);
1577 vs_scale_lanczos_AYUV_float (scale);
1579 scale1d_cleanup (&scale->x_scale1d);
1580 scale1d_cleanup (&scale->y_scale1d);
1581 g_free (scale->tmpdata);
1585 vs_scale_lanczos_AYUV64_double (Scale * scale)
1593 for (j = 0; j < scale->dest->height; j++) {
1597 destline = (guint16 *) (scale->dest->pixels + scale->dest->stride * j);
1599 yi = scale->y_scale1d.offsets[j];
1601 while (tmp_yi < yi + scale->y_scale1d.n_taps) {
1602 scale->horiz_resample_func (TMP_LINE_DOUBLE_AYUV (tmp_yi),
1603 scale->x_scale1d.offsets, scale->x_scale1d.taps, SRC_LINE (tmp_yi),
1604 scale->x_scale1d.n_taps, 0, scale->dest->width);
1608 taps = (double *) scale->y_scale1d.taps + j * scale->y_scale1d.n_taps;
1609 if (scale->dither) {
1610 resample_vert_dither_double_generic_u16 (destline,
1611 taps, TMP_LINE_DOUBLE_AYUV (scale->y_scale1d.offsets[j]),
1612 sizeof (double) * 4 * scale->dest->width,
1613 scale->y_scale1d.n_taps, 0, scale->dest->width * 4);
1615 resample_vert_double_generic_u16 (destline,
1616 taps, TMP_LINE_DOUBLE_AYUV (scale->y_scale1d.offsets[j]),
1617 sizeof (double) * 4 * scale->dest->width,
1618 scale->y_scale1d.n_taps, 0, scale->dest->width * 4);
1624 vs_image_scale_lanczos_AYUV64_double (const VSImage * dest, const VSImage * src,
1625 uint8_t * tmpbuf, double sharpness, gboolean dither, double a,
1635 n_taps = scale1d_get_n_taps (src->width, dest->width, a, sharpness);
1636 scale1d_calculate_taps (&scale->x_scale1d,
1637 src->width, dest->width, n_taps, a, sharpness, sharpen);
1639 n_taps = scale1d_get_n_taps (src->height, dest->height, a, sharpness);
1640 scale1d_calculate_taps (&scale->y_scale1d,
1641 src->height, dest->height, n_taps, a, sharpness, sharpen);
1643 scale->dither = dither;
1645 scale->horiz_resample_func =
1646 (HorizResampleFunc) resample_horiz_double_ayuv_generic_s16;
1649 g_malloc (sizeof (double) * scale->dest->width * scale->src->height * 4);
1651 vs_scale_lanczos_AYUV64_double (scale);
1653 scale1d_cleanup (&scale->x_scale1d);
1654 scale1d_cleanup (&scale->y_scale1d);
1655 g_free (scale->tmpdata);