Coverage Report

Created: 2025-06-24 07:01

/src/ghostpdl/base/siscale.c
Line
Count
Source (jump to first uncovered line)
1
/* Copyright (C) 2001-2023 Artifex Software, Inc.
2
   All Rights Reserved.
3
4
   This software is provided AS-IS with no warranty, either express or
5
   implied.
6
7
   This software is distributed under license and may not be copied,
8
   modified or distributed except as expressly authorized under the terms
9
   of the license contained in the file LICENSE in this distribution.
10
11
   Refer to licensing information at http://www.artifex.com or contact
12
   Artifex Software, Inc.,  39 Mesa Street, Suite 108A, San Francisco,
13
   CA 94129, USA, for further information.
14
*/
15
16
17
/* Image scaling filters */
18
#include "math_.h"
19
#include "memory_.h"
20
#include "stdio_.h"
21
#include "stdint_.h"
22
#include "gdebug.h"
23
#include "strimpl.h"
24
#include "siscale.h"
25
#include "gxfrac.h"
26
27
/*
28
 *    Image scaling code is based on public domain code from
29
 *      Graphics Gems III (pp. 414-424), Academic Press, 1992.
30
 */
31
32
/* ---------------- ImageScaleEncode/Decode ---------------- */
33
34
0
#define CONTRIB_SHIFT 12
35
0
#define CONTRIB_SCALE (1<<CONTRIB_SHIFT)
36
0
#define CONTRIB_ROUND (1<<(CONTRIB_SHIFT-1))
37
38
/* Auxiliary structures. */
39
typedef struct {
40
    int weight;               /* float or scaled fraction */
41
} CONTRIB;
42
43
typedef struct {
44
    int index;                  /* index of first element in list of */
45
    /* contributors */
46
    int n;                      /* number of contributors */
47
    /* (not multiplied by stride) */
48
    int first_pixel;            /* offset of first value in source data */
49
} CLIST;
50
51
typedef void (zoom_y_fn)(void *dst,
52
       const byte * gs_restrict tmp, int skip, int WidthOut, int Stride,
53
       int Colors, const CLIST * gs_restrict contrib, const CONTRIB * gs_restrict items);
54
typedef void (zoom_x_fn)(byte * gs_restrict tmp, const void /*PixelIn */ * gs_restrict src,
55
       int skip, int tmp_width, int Colors, const CLIST * gs_restrict contrib,
56
       const CONTRIB * gs_restrict items);
57
58
/* ImageScaleEncode / ImageScaleDecode */
59
typedef struct stream_IScale_state_s {
60
    /* The client sets the params values before initialization. */
61
    stream_image_scale_state_common;  /* = state_common + params */
62
    /* The init procedure sets the following. */
63
    int sizeofPixelIn;          /* bytes per input value, 1 or 2 */
64
    int sizeofPixelOut;         /* bytes per output value, 1 or 2 */
65
    void /*PixelIn */ *src;
66
    void /*PixelOut */ *dst;
67
    byte *tmp;
68
    CLIST *contrib;
69
    CONTRIB *items;
70
    /* The following are updated dynamically. */
71
    int src_y;
72
    uint src_offset, src_size;
73
    int dst_y;
74
    int src_y_offset;
75
    uint dst_offset, dst_size;
76
    CLIST dst_next_list;        /* for next output value */
77
    int dst_last_index;         /* highest index used in list */
78
    /* Vertical filter details */
79
    int filter_width;
80
    int max_support;
81
    double (*filter)(double);
82
    double min_scale;
83
    CONTRIB *dst_items; /* ditto */
84
    zoom_y_fn *zoom_y;
85
    zoom_x_fn *zoom_x;
86
} stream_IScale_state;
87
88
gs_private_st_ptrs6(st_IScale_state, stream_IScale_state,
89
    "ImageScaleEncode/Decode state",
90
    iscale_state_enum_ptrs, iscale_state_reloc_ptrs,
91
    dst, src, tmp, contrib, items, dst_items);
92
93
/* ------ Digital filter definition ------ */
94
95
/* Mitchell filter definition */
96
0
#define Mitchell_support 2
97
#define Mitchell_min_scale ((Mitchell_support * 2) / (MAX_ISCALE_SUPPORT - 1.01))
98
0
#define B (1.0 / 3.0)
99
0
#define C (1.0 / 3.0)
100
static double
101
Mitchell_filter(double t)
102
0
{
103
0
    double t2 = t * t;
104
105
0
    if (t < 0)
106
0
        t = -t;
107
108
0
    if (t < 1)
109
0
        return
110
0
            ((12 - 9 * B - 6 * C) * (t * t2) +
111
0
             (-18 + 12 * B + 6 * C) * t2 +
112
0
             (6 - 2 * B)) / 6;
113
0
    else if (t < 2)
114
0
        return
115
0
            ((-1 * B - 6 * C) * (t * t2) +
116
0
             (6 * B + 30 * C) * t2 +
117
0
             (-12 * B - 48 * C) * t +
118
0
             (8 * B + 24 * C)) / 6;
119
0
    else
120
0
        return 0;
121
0
}
122
123
/* Interpolated filter definition */
124
0
#define Interp_support 1
125
#define Interp_min_scale 0
126
static double
127
Interp_filter(double t)
128
0
{
129
0
    if (t < 0)
130
0
        t = -t;
131
132
0
    if (t >= 1)
133
0
        return 0;
134
0
    return 1 + (2*t -3)*t*t;
135
0
}
136
137
/*
138
 * The environment provides the following definitions:
139
 *      double fproc(double t)
140
 *      double fWidthIn
141
 *      PixelTmp {min,max,unit}PixelTmp
142
 */
143
#define CLAMP(v, mn, mx)\
144
0
  (v < mn ? mn : v > mx ? mx : v)
145
146
/* ------ Auxiliary procedures ------ */
147
148
/* Calculate the support for a given scale. */
149
/* The value is always in the range 1..max_support (was MAX_ISCALE_SUPPORT). */
150
static int
151
Interp_contrib_pixels(double scale)
152
0
{
153
0
    if (scale == 0.0)
154
0
        return 1;
155
0
    return (int)(((float)Interp_support) / (scale >= 1.0 ? 1.0 : scale)
156
0
                 * 2 + 1.5);
157
0
}
158
159
static int
160
Mitchell_contrib_pixels(double scale)
161
0
{
162
0
    if (scale == 0.0)
163
0
        return 1;
164
0
    return (int)(((float)Mitchell_support) / (scale >= 1.0 ? 1.0 : max(scale, Mitchell_min_scale))
165
0
                 * 2 + 1.5);
166
0
}
167
168
/* Pre-calculate filter contributions for a row or a column. */
169
/* Return the highest input pixel index used. */
170
static int
171
calculate_contrib(
172
        /* Return weight list parameters in contrib[0 .. size-1]. */
173
                     CLIST * contrib,
174
        /* Store weights in items[0 .. contrib_pixels(scale)*size-1]. */
175
        /* (Less space than this may actually be needed.) */
176
                     CONTRIB * items,
177
        /* The output image is scaled by 'scale' relative to the input. */
178
                     double scale,
179
        /* Start generating weights for input pixel 'starting_output_index'. */
180
                     int starting_output_index,
181
        /* Offset of input subimage (data) from the input image start. */
182
                     int src_y_offset,
183
        /* Entire output image size. */
184
                     int dst_size,
185
        /* Entire input image size. */
186
                     int src_size,
187
        /* Generate 'size' weight lists. */
188
                     int size,
189
        /* Limit pixel indices to 'limit', for clamping at the edges */
190
        /* of the image. */
191
                     int limit,
192
        /* Wrap pixel indices modulo 'modulus'. */
193
                     int modulus,
194
        /* Successive pixel values are 'stride' distance apart -- */
195
        /* normally, the number of color components. */
196
                     int stride,
197
        /* The unit of output is 'rescale_factor' times the unit of input. */
198
                     double rescale_factor,
199
        /* The filters width */
200
                     int fWidthIn,
201
        /* The filter to use */
202
                     double (*fproc)(double),
203
        /* minimum scale factor to use */
204
                     double min_scale
205
)
206
0
{
207
0
    double WidthIn, fscale;
208
0
    bool squeeze;
209
0
    int npixels;
210
0
    int i, j;
211
0
    int last_index = -1;
212
213
0
    if_debug1('W', "[W]calculate_contrib scale=%lg\n", scale);
214
0
    if (scale < 1.0) {
215
0
        double clamped_scale = max(scale, min_scale);
216
0
        WidthIn = ((double)fWidthIn) / clamped_scale;
217
0
        fscale = 1.0 / clamped_scale;
218
0
        squeeze = true;
219
0
    } else {
220
0
        WidthIn = (double)fWidthIn;
221
0
        fscale = 1.0;
222
0
        squeeze = false;
223
0
    }
224
0
    npixels = (int)(WidthIn * 2 + 1);
225
226
0
    for (i = 0; i < size; ++i) {
227
        /* Here we need :
228
           double scale = (double)dst_size / src_size;
229
           float dst_offset_fraction = floor(dst_offset) - dst_offset;
230
           double center = (starting_output_index  + i + dst_offset_fraction + 0.5) / scale - 0.5;
231
           int left = (int)ceil(center - WidthIn);
232
           int right = (int)floor(center + WidthIn);
233
           We can't compute 'right' in floats because float arithmetics is not associative.
234
           In older versions tt caused a 1 pixel bias of image bands due to
235
           rounding direction appears to depend on src_y_offset. So compute in rationals.
236
           Since pixel center fall to half integers, we subtract 0.5
237
           in the image space and add 0.5 in the device space.
238
         */
239
0
        int dst_y_offset_fraction_num = (int)((int64_t)src_y_offset * dst_size % src_size) * 2 <= src_size
240
0
                        ? -(int)((int64_t)src_y_offset * dst_size % src_size)
241
0
                        : src_size - (int)((int64_t)src_y_offset * dst_size % src_size);
242
0
        int center_denom = dst_size * 2;
243
0
        int64_t center_num = /* center * center_denom * 2 = */
244
0
            (starting_output_index  + i) * (int64_t)src_size * 2 + src_size + dst_y_offset_fraction_num * 2 - dst_size;
245
0
        int left = (int)ceil((center_num - WidthIn * center_denom) / center_denom);
246
0
        int right = (int)floor((center_num + WidthIn * center_denom) / center_denom);
247
0
        double center = (double)center_num / center_denom;
248
0
#define clamp_pixel(j) (j < 0 ? 0 : j >= limit ? limit - 1 : j)
249
0
        int first_pixel = clamp_pixel(left);
250
0
        int last_pixel = clamp_pixel(right);
251
0
        CONTRIB *p;
252
253
0
        if_debug4('W', "[W]i=%d, i+offset=%lg scale=%lg center=%lg : ", starting_output_index + i,
254
0
                starting_output_index + i + (double)src_y_offset / src_size * dst_size, scale, center);
255
0
        if (last_pixel > last_index)
256
0
            last_index = last_pixel;
257
0
        contrib[i].first_pixel = (first_pixel % modulus) * stride;
258
0
        contrib[i].n = last_pixel - first_pixel + 1;
259
0
        contrib[i].index = i * npixels;
260
0
        p = items + contrib[i].index;
261
0
        for (j = 0; j < npixels; ++j)
262
0
            p[j].weight = 0;
263
0
        if (squeeze) {
264
0
            double sum = 0;
265
0
            double e = 0;
266
0
            for (j = left; j <= right; ++j)
267
0
                sum += fproc((center - j) / fscale) / fscale;
268
0
            for (j = left; j <= right; ++j) {
269
0
                double weight = fproc((center - j) / fscale) / fscale / sum;
270
0
                int n = clamp_pixel(j);
271
0
                int k = n - first_pixel;
272
0
                int ie;
273
274
0
                e += (weight * rescale_factor) * CONTRIB_SCALE;
275
0
                ie = (int)(e + 0.5);
276
0
                p[k].weight += ie;
277
0
                e -= ie;
278
0
                if_debug2('W', " %d %f", k, (float)p[k].weight);
279
0
            }
280
281
0
        } else {
282
0
            double sum = 0;
283
0
            double e = 0;
284
0
            for (j = left; j <= right; ++j)
285
0
                sum += fproc(center - j);
286
0
            for (j = left; j <= right; ++j) {
287
0
                double weight = fproc(center - j) / sum;
288
0
                int n = clamp_pixel(j);
289
0
                int k = n - first_pixel;
290
0
                int ie;
291
292
0
                e += (weight * rescale_factor) * CONTRIB_SCALE;
293
0
                ie = (int)(e + 0.5);
294
0
                p[k].weight += ie;
295
0
                e -= ie;
296
0
                if_debug2('W', " %d %f", k, (float)p[k].weight);
297
0
            }
298
0
        }
299
0
        if_debug0('W', "\n");
300
0
    }
301
0
    return last_index;
302
0
}
303
304
/* Apply filter to zoom horizontally from src to tmp. */
305
static void
306
zoom_x1(byte * gs_restrict tmp, const void /*PixelIn */ * gs_restrict src,
307
                 int skip, int tmp_width, int Colors, const CLIST * gs_restrict contrib,
308
                 const CONTRIB * gs_restrict items)
309
0
{
310
0
    int c, i;
311
312
0
    contrib += skip;
313
0
    tmp += Colors * skip;
314
315
0
    for (c = 0; c < Colors; ++c) {
316
0
        byte *gs_restrict tp = tmp + c;
317
0
        const CLIST *gs_restrict clp = contrib;
318
0
        const byte *gs_restrict raster = (const byte *)src + c;
319
320
0
        if_debug1('W', "[W]zoom_x color %d:", c);
321
322
0
        for ( i = 0; i < tmp_width; tp += Colors, ++clp, ++i ) {
323
0
            int weight = 0;
324
0
            int j = clp->n;
325
0
            const byte *gs_restrict pp = raster + clp->first_pixel;
326
0
            const CONTRIB *gs_restrict cp = items + clp->index;
327
328
0
            for ( ; j > 0; pp += Colors, ++cp, --j )
329
0
                 weight += *pp * cp->weight;
330
0
            weight = (weight + CONTRIB_ROUND)>>CONTRIB_SHIFT;
331
0
            if_debug1('W', " %x", weight);
332
0
            *tp = (byte)CLAMP(weight, 0, 255);
333
0
        }
334
0
        if_debug0('W', "\n");
335
0
    }
336
0
}
337
338
static void
339
zoom_x1_1(byte * gs_restrict tmp, const void /*PixelIn */ * gs_restrict src,
340
          int skip, int tmp_width, int Colors, const CLIST * gs_restrict contrib,
341
          const CONTRIB * gs_restrict items)
342
0
{
343
0
    contrib += skip;
344
0
    tmp += Colors * skip;
345
346
0
    if_debug0('W', "[W]zoom_x:");
347
348
0
    for ( ; tmp_width != 0; --tmp_width ) {
349
0
        int j = contrib->n;
350
0
        const byte *gs_restrict pp = ((const byte *)src) + contrib->first_pixel;
351
0
        const CONTRIB *gs_restrict cp = items + (contrib++)->index;
352
0
        int weight0 = 0;
353
354
0
        for ( ; j > 0; --j ) {
355
0
            weight0 += *pp++ * (cp++)->weight;
356
0
        }
357
0
        if_debug1('W', " %x", weight0);
358
0
        weight0 = (weight0 + CONTRIB_ROUND)>>CONTRIB_SHIFT;
359
0
        *tmp++ = (byte)CLAMP(weight0, 0, 255);
360
0
    }
361
0
    if_debug0('W', "\n");
362
0
}
363
364
static void
365
zoom_x1_3(byte * gs_restrict tmp, const void /*PixelIn */ * gs_restrict src,
366
          int skip, int tmp_width, int Colors, const CLIST * gs_restrict contrib,
367
          const CONTRIB * gs_restrict items)
368
0
{
369
0
    contrib += skip;
370
0
    tmp += Colors * skip;
371
372
0
    if_debug0('W', "[W]zoom_x:");
373
374
0
    for ( ; tmp_width != 0; --tmp_width ) {
375
0
        int j = contrib->n;
376
0
        const byte *gs_restrict pp = ((const byte *)src) + contrib->first_pixel;
377
0
        const CONTRIB *gs_restrict cp = items + (contrib++)->index;
378
0
        int weight0 = 0;
379
0
        int weight1 = 0;
380
0
        int weight2 = 0;
381
382
0
        for ( ; j > 0; --j ) {
383
0
            int weight = (cp++)->weight;
384
0
            weight0 += *pp++ * weight;
385
0
            weight1 += *pp++ * weight;
386
0
            weight2 += *pp++ * weight;
387
0
        }
388
0
        if_debug3('W', " (%x %x %x)", weight0, weight1, weight2);
389
0
        weight0 = (weight0 + CONTRIB_ROUND)>>CONTRIB_SHIFT;
390
0
        weight1 = (weight1 + CONTRIB_ROUND)>>CONTRIB_SHIFT;
391
0
        weight2 = (weight2 + CONTRIB_ROUND)>>CONTRIB_SHIFT;
392
0
        *tmp++ = (byte)CLAMP(weight0, 0, 255);
393
0
        *tmp++ = (byte)CLAMP(weight1, 0, 255);
394
0
        *tmp++ = (byte)CLAMP(weight2, 0, 255);
395
0
    }
396
0
    if_debug0('W', "\n");
397
0
}
398
399
static void
400
zoom_x1_4(byte * gs_restrict tmp, const void /*PixelIn */ * gs_restrict src,
401
          int skip, int tmp_width, int Colors, const CLIST * gs_restrict contrib,
402
          const CONTRIB * gs_restrict items)
403
0
{
404
0
    contrib += skip;
405
0
    tmp += Colors * skip;
406
407
0
    if_debug0('W', "[W]zoom_x:");
408
409
0
    for ( ; tmp_width != 0; --tmp_width ) {
410
0
        int j = contrib->n;
411
0
        const byte *gs_restrict pp = ((const byte *)src) + contrib->first_pixel;
412
0
        const CONTRIB *gs_restrict cp = items + (contrib++)->index;
413
0
        int weight0 = 0;
414
0
        int weight1 = 0;
415
0
        int weight2 = 0;
416
0
        int weight3 = 0;
417
418
0
        for ( ; j > 0; --j ) {
419
0
            int weight = (cp++)->weight;
420
0
            weight0 += *pp++ * weight;
421
0
            weight1 += *pp++ * weight;
422
0
            weight2 += *pp++ * weight;
423
0
            weight3 += *pp++ * weight;
424
0
        }
425
0
        if_debug4('W', " (%x %x %x %x)", weight0, weight1, weight2, weight3);
426
0
        weight0 = (weight0 + CONTRIB_ROUND)>>CONTRIB_SHIFT;
427
0
        weight1 = (weight1 + CONTRIB_ROUND)>>CONTRIB_SHIFT;
428
0
        weight2 = (weight2 + CONTRIB_ROUND)>>CONTRIB_SHIFT;
429
0
        weight3 = (weight3 + CONTRIB_ROUND)>>CONTRIB_SHIFT;
430
0
        *tmp++ = (byte)CLAMP(weight0, 0, 255);
431
0
        *tmp++ = (byte)CLAMP(weight1, 0, 255);
432
0
        *tmp++ = (byte)CLAMP(weight2, 0, 255);
433
0
        *tmp++ = (byte)CLAMP(weight3, 0, 255);
434
0
    }
435
0
    if_debug0('W', "\n");
436
0
}
437
438
static void
439
zoom_x2(byte * gs_restrict tmp, const void /*PixelIn */ * gs_restrict src,
440
        int skip, int tmp_width, int Colors, const CLIST * gs_restrict contrib,
441
        const CONTRIB * gs_restrict items)
442
0
{
443
0
    int c, i;
444
445
0
    contrib += skip;
446
0
    tmp += Colors * skip;
447
448
0
    for (c = 0; c < Colors; ++c) {
449
0
        byte *gs_restrict tp = tmp + c;
450
0
        const CLIST *gs_restrict clp = contrib;
451
0
        const bits16 *gs_restrict raster = (const bits16 *)src + c;
452
453
0
        if_debug1('W', "[W]zoom_x color %d:", c);
454
0
        for ( i = 0; i < tmp_width; tp += Colors, ++clp, ++i ) {
455
0
            int weight = 0;
456
0
            int j = clp->n;
457
0
            const bits16 *gs_restrict pp = raster + clp->first_pixel;
458
0
            const CONTRIB *gs_restrict cp = items + clp->index;
459
460
0
            switch ( Colors ) {
461
0
                case 1:
462
0
                    for ( ; j > 0; pp += 1, ++cp, --j )
463
0
                        weight += *pp * cp->weight;
464
0
                    break;
465
0
                case 3:
466
0
                    for ( ; j > 0; pp += 3, ++cp, --j )
467
0
                        weight += *pp * cp->weight;
468
0
                    break;
469
0
                default:
470
0
                    for ( ; j > 0; pp += Colors, ++cp, --j )
471
0
                        weight += *pp * cp->weight;
472
0
            }
473
0
            weight = (weight + CONTRIB_ROUND)>>CONTRIB_SHIFT;
474
0
            if_debug1('W', " %x", weight);
475
0
            *tp = (byte)CLAMP(weight, 0, 255);
476
0
        }
477
0
        if_debug0('W', "\n");
478
0
    }
479
0
}
480
481
/*
482
 * Apply filter to zoom vertically from tmp to dst.
483
 * This is simpler because we can treat all columns identically
484
 * without regard to the number of samples per pixel.
485
 */
486
static inline void
487
zoom_y1_4(void /*PixelOut */ * gs_restrict dst,
488
          const byte * gs_restrict tmp, int skip, int WidthOut, int Stride,
489
          int Colors, const CLIST * gs_restrict contrib, const CONTRIB * gs_restrict items)
490
0
{
491
0
    int kn = Stride * Colors;
492
0
    int width = WidthOut * Colors;
493
0
    int first_pixel = contrib->first_pixel;
494
0
    const CONTRIB *gs_restrict cbp = items + contrib->index;
495
0
    int w0 = cbp[0].weight;
496
0
    int w1 = cbp[1].weight;
497
0
    int w2 = cbp[2].weight;
498
0
    int w3 = cbp[3].weight;
499
0
    byte *gs_restrict d;
500
501
0
    if_debug0('W', "[W]zoom_y: ");
502
503
0
    skip *= Colors;
504
0
    tmp += first_pixel + skip;
505
0
    d = ((byte *)dst)+skip;
506
0
    for (; width > 0; width--) {
507
0
        int weight;
508
509
0
        weight  = tmp[   0] * w0;
510
0
        weight += tmp[  kn] * w1;
511
0
        weight += tmp[2*kn] * w2;
512
0
        weight += tmp[3*kn] * w3;
513
0
        tmp++;
514
515
0
        weight = (weight + CONTRIB_ROUND)>>CONTRIB_SHIFT;
516
0
        if_debug1('W', " %x", weight);
517
0
        *d++ = (byte)CLAMP(weight, 0, 0xff);
518
0
    }
519
0
    if_debug0('W', "\n");
520
0
}
521
static inline void
522
zoom_y1_5(void /*PixelOut */ * gs_restrict dst,
523
          const byte * gs_restrict tmp, int skip, int WidthOut, int Stride,
524
          int Colors, const CLIST * gs_restrict contrib, const CONTRIB * gs_restrict items)
525
0
{
526
0
    int kn = Stride * Colors;
527
0
    int width = WidthOut * Colors;
528
0
    int first_pixel = contrib->first_pixel;
529
0
    const CONTRIB *gs_restrict cbp = items + contrib->index;
530
0
    int w0 = cbp[0].weight;
531
0
    int w1 = cbp[1].weight;
532
0
    int w2 = cbp[2].weight;
533
0
    int w3 = cbp[3].weight;
534
0
    int w4 = cbp[4].weight;
535
0
    byte *gs_restrict d;
536
537
0
    if_debug0('W', "[W]zoom_y: ");
538
539
0
    skip *= Colors;
540
0
    tmp += first_pixel + skip;
541
0
    d = ((byte *)dst)+skip;
542
0
    for (; width > 0; width--) {
543
0
        int weight;
544
545
0
        weight  = tmp[   0] * w0;
546
0
        weight += tmp[  kn] * w1;
547
0
        weight += tmp[2*kn] * w2;
548
0
        weight += tmp[3*kn] * w3;
549
0
        weight += tmp[4*kn] * w4;
550
0
        tmp++;
551
552
0
        weight = (weight + CONTRIB_ROUND)>>CONTRIB_SHIFT;
553
0
        if_debug1('W', " %x", weight);
554
0
        *d++ = (byte)CLAMP(weight, 0, 0xff);
555
0
    }
556
0
    if_debug0('W', "\n");
557
0
}
558
static inline void
559
template_zoom_y1(void /*PixelOut */ * gs_restrict dst,
560
                 const byte * gs_restrict tmp, int skip, int WidthOut, int Stride,
561
                 int Colors, const CLIST * gs_restrict contrib, const CONTRIB * gs_restrict items, int n)
562
0
{
563
0
    int kn = Stride * Colors;
564
0
    int width = WidthOut * Colors;
565
0
    int cn = contrib->n;
566
0
    int first_pixel = contrib->first_pixel;
567
0
    const CONTRIB *gs_restrict cbp = items + contrib->index;
568
0
    byte *gs_restrict d;
569
570
0
    if_debug0('W', "[W]zoom_y: ");
571
572
0
    skip *= Colors;
573
0
    tmp += first_pixel + skip;
574
0
    d = ((byte *)dst)+skip;
575
0
    for (; width > 0; width--) {
576
0
        int weight = 0;
577
0
        const byte *gs_restrict pp = tmp++;
578
0
        int pixel, j;
579
0
        const CONTRIB *cp = cbp;
580
581
0
        for (j = cn; j > 0; pp += kn, ++cp, --j)
582
0
            weight += *pp * cp->weight;
583
0
        pixel = (weight + CONTRIB_ROUND)>>CONTRIB_SHIFT;
584
0
        if_debug1('W', " %x", pixel);
585
0
        *d++ = (byte)CLAMP(pixel, 0, 0xff);
586
0
    }
587
0
    if_debug0('W', "\n");
588
0
}
589
590
static void zoom_y1(void /*PixelOut */ * gs_restrict dst,
591
                 const byte * gs_restrict tmp, int skip, int WidthOut, int Stride,
592
                 int Colors, const CLIST * gs_restrict contrib, const CONTRIB * gs_restrict items)
593
0
{
594
0
    switch(contrib->n) {
595
0
        case 4:
596
0
            zoom_y1_4(dst, tmp, skip, WidthOut, Stride, Colors, contrib, items);
597
0
            break;
598
0
        case 5:
599
0
            zoom_y1_5(dst, tmp, skip, WidthOut, Stride, Colors, contrib, items);
600
0
            break;
601
0
        default:
602
0
            template_zoom_y1(dst, tmp, skip, WidthOut, Stride, Colors, contrib, items, contrib->n);
603
0
            break;
604
0
    }
605
0
}
606
607
static inline void
608
zoom_y2_4(void /*PixelOut */ * gs_restrict dst,
609
          const byte * gs_restrict tmp, int skip, int WidthOut, int Stride,
610
          int Colors, const CLIST * gs_restrict contrib, const CONTRIB * gs_restrict items)
611
0
{
612
0
    int kn = Stride * Colors;
613
0
    int width = WidthOut * Colors;
614
0
    int first_pixel = contrib->first_pixel;
615
0
    const CONTRIB *gs_restrict cbp = items + contrib->index;
616
0
    bits16 *gs_restrict d;
617
0
    int w0 = cbp[0].weight;
618
0
    int w1 = cbp[1].weight;
619
0
    int w2 = cbp[2].weight;
620
0
    int w3 = cbp[3].weight;
621
622
0
    if_debug0('W', "[W]zoom_y: ");
623
624
0
    skip *= Colors;
625
0
    tmp += first_pixel + skip;
626
0
    d = ((bits16 *)dst) + skip;
627
0
    for (; width > 0; width--) {
628
0
        int weight;
629
0
        int pixel;
630
631
0
        weight  = tmp[   0] * w0;
632
0
        weight += tmp[  kn] * w1;
633
0
        weight += tmp[2*kn] * w2;
634
0
        weight += tmp[3*kn] * w3;
635
0
        tmp++;
636
0
        pixel = (weight + CONTRIB_ROUND)>>CONTRIB_SHIFT;
637
0
        if_debug1('W', " %x", pixel);
638
0
        *d++ = (bits16)CLAMP(pixel, 0, 0xffff);
639
0
    }
640
0
    if_debug0('W', "\n");
641
0
}
642
static inline void
643
zoom_y2_5(void /*PixelOut */ * gs_restrict dst,
644
          const byte * gs_restrict tmp, int skip, int WidthOut, int Stride,
645
          int Colors, const CLIST * gs_restrict contrib, const CONTRIB * gs_restrict items)
646
0
{
647
0
    int kn = Stride * Colors;
648
0
    int width = WidthOut * Colors;
649
0
    int first_pixel = contrib->first_pixel;
650
0
    const CONTRIB *gs_restrict cbp = items + contrib->index;
651
0
    bits16 *gs_restrict d;
652
0
    int w0 = cbp[0].weight;
653
0
    int w1 = cbp[1].weight;
654
0
    int w2 = cbp[2].weight;
655
0
    int w3 = cbp[3].weight;
656
0
    int w4 = cbp[4].weight;
657
658
0
    if_debug0('W', "[W]zoom_y: ");
659
660
0
    skip *= Colors;
661
0
    tmp += first_pixel + skip;
662
0
    d = ((bits16 *)dst) + skip;
663
0
    for (; width > 0; width--) {
664
0
        int weight;
665
0
        int pixel;
666
667
0
        weight  = tmp[   0] * w0;
668
0
        weight += tmp[  kn] * w1;
669
0
        weight += tmp[2*kn] * w2;
670
0
        weight += tmp[3*kn] * w3;
671
0
        weight += tmp[4*kn] * w4;
672
0
        tmp++;
673
0
        pixel = (weight + CONTRIB_ROUND)>>CONTRIB_SHIFT;
674
0
        if_debug1('W', " %x", pixel);
675
0
        *d++ = (bits16)CLAMP(pixel, 0, 0xffff);
676
0
    }
677
0
    if_debug0('W', "\n");
678
0
}
679
static inline void
680
zoom_y2_n(void /*PixelOut */ * gs_restrict dst,
681
          const byte * gs_restrict tmp, int skip, int WidthOut, int Stride,
682
          int Colors, const CLIST * gs_restrict contrib, const CONTRIB * gs_restrict items)
683
0
{
684
0
    int kn = Stride * Colors;
685
0
    int width = WidthOut * Colors;
686
0
    int cn = contrib->n;
687
0
    int first_pixel = contrib->first_pixel;
688
0
    const CONTRIB *gs_restrict cbp = items + contrib->index;
689
0
    bits16 *gs_restrict d;
690
691
0
    if_debug0('W', "[W]zoom_y: ");
692
693
0
    skip *= Colors;
694
0
    tmp += first_pixel + skip;
695
0
    d = ((bits16 *)dst) + skip;
696
0
    for (; width > 0; width--) {
697
0
        int weight = 0;
698
0
        const byte *gs_restrict pp = tmp++;
699
0
        int pixel, j;
700
0
        const CONTRIB *gs_restrict cp = cbp;
701
702
0
        for (j = cn; j > 0; pp += kn, ++cp, --j)
703
0
            weight += *pp * cp->weight;
704
0
        pixel = (weight + CONTRIB_ROUND)>>CONTRIB_SHIFT;
705
0
        if_debug1('W', " %x", pixel);
706
0
        *d++ = (bits16)CLAMP(pixel, 0, 0xffff);
707
0
    }
708
0
    if_debug0('W', "\n");
709
0
}
710
static void
711
zoom_y2(void /*PixelOut */ * gs_restrict dst,
712
       const byte * gs_restrict tmp, int skip, int WidthOut, int Stride,
713
       int Colors, const CLIST * gs_restrict contrib, const CONTRIB * gs_restrict items)
714
0
{
715
0
    switch (contrib->n) {
716
0
        case 4:
717
0
            zoom_y2_4(dst, tmp, skip, WidthOut, Stride, Colors, contrib, items);
718
0
            break;
719
0
        case 5:
720
0
            zoom_y2_5(dst, tmp, skip, WidthOut, Stride, Colors, contrib, items);
721
0
            break;
722
0
        default:
723
0
            zoom_y2_n(dst, tmp, skip, WidthOut, Stride, Colors, contrib, items);
724
0
            break;
725
0
    }
726
0
}
727
728
static inline void
729
zoom_y2_frac_4(void /*PixelOut */ * gs_restrict dst,
730
               const byte * gs_restrict tmp, int skip, int WidthOut, int Stride,
731
               int Colors, const CLIST * gs_restrict contrib, const CONTRIB * gs_restrict items)
732
0
{
733
0
    int kn = Stride * Colors;
734
0
    int width = WidthOut * Colors;
735
0
    int first_pixel = contrib->first_pixel;
736
0
    const CONTRIB *gs_restrict cbp = items + contrib->index;
737
0
    bits16 *gs_restrict d;
738
0
    int w0 = cbp[0].weight;
739
0
    int w1 = cbp[1].weight;
740
0
    int w2 = cbp[2].weight;
741
0
    int w3 = cbp[3].weight;
742
743
0
    if_debug0('W', "[W]zoom_y: ");
744
745
0
    skip *= Colors;
746
0
    tmp += first_pixel + skip;
747
0
    d = ((bits16 *)dst) + skip;
748
0
    for (; width > 0; width--) {
749
0
        int weight;
750
0
        int pixel;
751
752
0
        weight  = tmp[   0]  * w0;
753
0
        weight += tmp[  kn]  * w1;
754
0
        weight += tmp[2*kn]  * w2;
755
0
        weight += tmp[3*kn]  * w3;
756
0
        tmp++;
757
0
        pixel = (weight + CONTRIB_ROUND)>>CONTRIB_SHIFT;
758
0
        if_debug1('W', " %x", pixel);
759
0
        *d++ = (bits16)CLAMP(pixel, 0, frac_1);
760
0
    }
761
0
    if_debug0('W', "\n");
762
0
}
763
static inline void
764
zoom_y2_frac_5(void /*PixelOut */ * gs_restrict dst,
765
               const byte * gs_restrict tmp, int skip, int WidthOut, int Stride,
766
               int Colors, const CLIST * gs_restrict contrib, const CONTRIB * gs_restrict items)
767
0
{
768
0
    int kn = Stride * Colors;
769
0
    int width = WidthOut * Colors;
770
0
    int first_pixel = contrib->first_pixel;
771
0
    const CONTRIB *gs_restrict cbp = items + contrib->index;
772
0
    bits16 *gs_restrict d;
773
0
    int w0 = cbp[0].weight;
774
0
    int w1 = cbp[1].weight;
775
0
    int w2 = cbp[2].weight;
776
0
    int w3 = cbp[3].weight;
777
0
    int w4 = cbp[4].weight;
778
779
0
    if_debug0('W', "[W]zoom_y: ");
780
781
0
    skip *= Colors;
782
0
    tmp += first_pixel + skip;
783
0
    d = ((bits16 *)dst) + skip;
784
0
    for (; width > 0; width--) {
785
0
        int weight;
786
0
        int pixel;
787
788
0
        weight  = tmp[   0]  * w0;
789
0
        weight += tmp[  kn]  * w1;
790
0
        weight += tmp[2*kn]  * w2;
791
0
        weight += tmp[3*kn]  * w3;
792
0
        weight += tmp[4*kn]  * w4;
793
0
        tmp++;
794
0
        pixel = (weight + CONTRIB_ROUND)>>CONTRIB_SHIFT;
795
0
        if_debug1('W', " %x", pixel);
796
0
        *d++ = (bits16)CLAMP(pixel, 0, frac_1);
797
0
    }
798
0
    if_debug0('W', "\n");
799
0
}
800
static inline void
801
zoom_y2_frac_n(void /*PixelOut */ * gs_restrict dst,
802
               const byte * gs_restrict tmp, int skip, int WidthOut, int Stride,
803
               int Colors, const CLIST * gs_restrict contrib, const CONTRIB * gs_restrict items)
804
0
{
805
0
    int kn = Stride * Colors;
806
0
    int width = WidthOut * Colors;
807
0
    int cn = contrib->n;
808
0
    int first_pixel = contrib->first_pixel;
809
0
    const CONTRIB *gs_restrict cbp = items + contrib->index;
810
0
    bits16 *gs_restrict d;
811
812
0
    if_debug0('W', "[W]zoom_y: ");
813
814
0
    skip *= Colors;
815
0
    tmp += first_pixel + skip;
816
0
    d = ((bits16 *)dst) + skip;
817
0
    for (; width > 0; width--) {
818
0
        int weight = 0;
819
0
        const byte *gs_restrict pp = tmp++;
820
0
        int pixel, j;
821
0
        const CONTRIB *gs_restrict cp = cbp;
822
823
0
        for (j = cn; j > 0; pp += kn, ++cp, --j)
824
0
            weight += *pp * cp->weight;
825
0
        pixel = (weight + CONTRIB_ROUND)>>CONTRIB_SHIFT;
826
0
        if_debug1('W', " %x", pixel);
827
0
        *d++ = (bits16)CLAMP(pixel, 0, frac_1);
828
0
    }
829
0
    if_debug0('W', "\n");
830
0
}
831
832
static void
833
zoom_y2_frac(void /*PixelOut */ * gs_restrict dst,
834
             const byte * gs_restrict tmp, int skip, int WidthOut, int Stride,
835
            int Colors, const CLIST * gs_restrict contrib, const CONTRIB * gs_restrict items)
836
0
{
837
0
    switch (contrib->n) {
838
0
        case 4:
839
0
            zoom_y2_frac_4(dst, tmp, skip, WidthOut, Stride, Colors, contrib, items);
840
0
            break;
841
0
        case 5:
842
0
            zoom_y2_frac_5(dst, tmp, skip, WidthOut, Stride, Colors, contrib, items);
843
0
            break;
844
0
        default:
845
0
            zoom_y2_frac_n(dst, tmp, skip, WidthOut, Stride, Colors, contrib, items);
846
0
            break;
847
0
    }
848
0
}
849
/* ------ Stream implementation ------ */
850
851
/* Forward references */
852
static void s_IScale_release(stream_state * st);
853
854
/* Calculate the weights for an output row. */
855
static void
856
calculate_dst_contrib(stream_IScale_state * ss, int y)
857
0
{
858
859
0
    int abs_interp_limit = ss->params.abs_interp_limit;
860
0
    int limited_WidthOut = (ss->params.WidthOut + abs_interp_limit - 1) / abs_interp_limit;
861
0
    int limited_EntireHeightOut = (ss->params.EntireHeightOut + abs_interp_limit - 1) / abs_interp_limit;
862
0
    uint row_size = limited_WidthOut * ss->params.spp_interp;
863
0
    int last_index, first_index_mod;
864
865
0
    if_debug2m('W', ss->memory, "[W]calculate_dst_contrib for y = %d, y+offset=%d\n", y, y + ss->src_y_offset);
866
867
0
    last_index = calculate_contrib(&ss->dst_next_list, ss->dst_items,
868
0
                      (double)limited_EntireHeightOut / ss->params.EntireHeightIn,
869
0
                      y, ss->src_y_offset, limited_EntireHeightOut, ss->params.EntireHeightIn,
870
0
                      1, ss->params.HeightIn, ss->max_support, row_size,
871
0
                      (double)ss->params.MaxValueOut / 255, ss->filter_width,
872
0
                      ss->filter, ss->min_scale);
873
0
    first_index_mod = ss->dst_next_list.first_pixel / row_size;
874
875
0
    ss->dst_last_index = last_index;
876
0
    last_index %= ss->max_support;
877
0
    if (last_index < first_index_mod) {         /* Shuffle the indices to account for wraparound. */
878
0
        CONTRIB *shuffle = &ss->dst_items[ss->max_support];
879
0
        int i;
880
881
0
        for (i = 0; i < ss->max_support; ++i) {
882
0
            shuffle[i].weight =
883
0
                (i <= last_index ?
884
0
                 ss->dst_items[i + ss->max_support - first_index_mod].weight :
885
0
                 i >= first_index_mod ?
886
0
                 ss->dst_items[i - first_index_mod].weight :
887
0
                 0);
888
0
            if_debug1m('W', ss->memory, " %d", shuffle[i].weight);
889
0
        }
890
0
        memcpy(ss->dst_items, shuffle, ss->max_support * sizeof(CONTRIB));
891
0
        ss->dst_next_list.n = ss->max_support;
892
0
        ss->dst_next_list.first_pixel = 0;
893
0
    }
894
0
    if_debug0m('W', ss->memory, "\n");
895
0
}
896
897
/* Set default parameter values (actually, just clear pointers) */
898
static void
899
s_IScale_set_defaults(stream_state * st)
900
0
{
901
0
    stream_IScale_state *const ss = (stream_IScale_state *) st;
902
903
0
    ss->src = 0;
904
0
    ss->dst = 0;
905
0
    ss->tmp = 0;
906
0
    ss->contrib = 0;
907
0
    ss->items = 0;
908
0
}
909
910
typedef struct filter_defn_s {
911
    double  (*filter)(double);
912
    int     filter_width;
913
    int     (*contrib_pixels)(double scale);
914
    double  min_scale;
915
} filter_defn_s;
916
917
/* Initialize the filter. */
918
static int
919
do_init(stream_state        *st,
920
        const filter_defn_s *horiz,
921
        const filter_defn_s *vert)
922
0
{
923
0
    stream_IScale_state *const ss = (stream_IScale_state *) st;
924
0
    gs_memory_t *mem = ss->memory;
925
0
    int abs_interp_limit = ss->params.abs_interp_limit;
926
0
    int limited_WidthOut = (ss->params.WidthOut + abs_interp_limit - 1) / abs_interp_limit;
927
0
    int limited_HeightOut = (ss->params.HeightOut + abs_interp_limit - 1) / abs_interp_limit;
928
0
    int limited_EntireWidthOut = (ss->params.EntireWidthOut + abs_interp_limit - 1) / abs_interp_limit;
929
0
    int limited_EntireHeightOut = (ss->params.EntireHeightOut + abs_interp_limit - 1) / abs_interp_limit;
930
931
0
    ss->sizeofPixelIn = ss->params.BitsPerComponentIn / 8;
932
0
    ss->sizeofPixelOut = ss->params.BitsPerComponentOut / 8;
933
934
0
    ss->src_y = 0;
935
0
    ss->src_size =
936
0
        ss->params.WidthIn * ss->sizeofPixelIn * ss->params.spp_interp;
937
0
    ss->src_offset = 0;
938
0
    ss->dst_y = 0;
939
0
    ss->src_y_offset = ss->params.src_y_offset;
940
0
    ss->dst_size = limited_WidthOut * ss->sizeofPixelOut * ss->params.spp_interp;
941
0
    ss->dst_offset = 0;
942
943
    /* create intermediate image to hold horizontal zoom */
944
0
    ss->max_support  = vert->contrib_pixels((double)limited_EntireHeightOut /
945
0
                                            (abs_interp_limit * ss->params.EntireHeightIn));
946
0
    ss->filter_width = vert->filter_width;
947
0
    ss->filter       = vert->filter;
948
0
    ss->min_scale    = vert->min_scale;
949
0
    ss->tmp = (byte *) gs_alloc_byte_array(mem,
950
0
                                           ss->max_support,
951
0
                                           (limited_WidthOut * ss->params.spp_interp),
952
0
                                           "image_scale tmp");
953
0
    ss->contrib = (CLIST *) gs_alloc_byte_array(mem,
954
0
                                                max(limited_WidthOut,
955
0
                                                    limited_HeightOut),
956
0
                                                sizeof(CLIST),
957
0
                                                "image_scale contrib");
958
0
    ss->items = (CONTRIB *)
959
0
                    gs_alloc_byte_array(mem,
960
0
                                        (horiz->contrib_pixels(
961
0
                                            (double)limited_EntireWidthOut /
962
0
                                            ss->params.EntireWidthIn) *
963
0
                                            limited_WidthOut),
964
0
                                         sizeof(CONTRIB),
965
0
                                         "image_scale contrib[*]");
966
0
    ss->dst_items = (CONTRIB *) gs_alloc_byte_array(mem,
967
0
                                                    ss->max_support*2,
968
0
                                                    sizeof(CONTRIB), "image_scale contrib_dst[*]");
969
    /* Allocate buffers for 1 row of source and destination. */
970
0
    ss->dst =
971
0
        gs_alloc_byte_array(mem, (size_t)limited_WidthOut * ss->params.spp_interp,
972
0
                            ss->sizeofPixelOut, "image_scale dst");
973
0
    ss->src =
974
0
        gs_alloc_byte_array(mem, (size_t)ss->params.WidthIn * ss->params.spp_interp,
975
0
                            ss->sizeofPixelIn, "image_scale src");
976
0
    if (ss->tmp == 0 || ss->contrib == 0 || ss->items == 0 ||
977
0
        ss->dst_items == 0 || ss->dst == 0 || ss->src == 0
978
0
        ) {
979
0
        s_IScale_release(st);
980
0
        return ERRC;
981
/****** WRONG ******/
982
0
    }
983
0
#ifdef PACIFY_VALGRIND
984
    /* When we are scaling a subrectangle of an image, we calculate
985
     * the subrectangle, so that it's slightly larger than it needs
986
     * to be. Some of these 'extra' pixels are calculated using
987
     * bogus values (i.e. ones we don't bother copying/scaling into
988
     * the line buffer). These cause valgrind to be upset. To avoid
989
     * this, we preset the buffer to known values. */
990
0
    memset((byte *)ss->tmp, 0,
991
0
           ss->max_support * limited_WidthOut * ss->params.spp_interp);
992
0
#endif
993
    /* Pre-calculate filter contributions for a row. */
994
0
    calculate_contrib(ss->contrib, ss->items,
995
0
                      (double)limited_EntireWidthOut / ss->params.EntireWidthIn,
996
0
                      0, 0, limited_WidthOut, ss->params.WidthIn,
997
0
                      limited_WidthOut, ss->params.WidthIn, ss->params.WidthIn,
998
0
                      ss->params.spp_interp, 255. / ss->params.MaxValueIn,
999
0
                      horiz->filter_width, horiz->filter, horiz->min_scale);
1000
1001
    /* Prepare the weights for the first output row. */
1002
0
    calculate_dst_contrib(ss, 0);
1003
1004
0
    if (ss->sizeofPixelIn == 2)
1005
0
        ss->zoom_x = zoom_x2;
1006
0
    else {
1007
0
        switch (ss->params.spp_interp) {
1008
0
            case 1:
1009
0
                ss->zoom_x = zoom_x1_1;
1010
0
                break;
1011
0
            case 3:
1012
0
                ss->zoom_x = zoom_x1_3;
1013
0
                break;
1014
0
            case 4:
1015
0
                ss->zoom_x = zoom_x1_4;
1016
0
                break;
1017
0
            default:
1018
0
                ss->zoom_x = zoom_x1;
1019
0
                break;
1020
0
        }
1021
0
    }
1022
1023
0
    if (ss->sizeofPixelOut == 1)
1024
0
        ss->zoom_y = zoom_y1;
1025
0
    else if (ss->params.MaxValueOut == frac_1)
1026
0
        ss->zoom_y = zoom_y2_frac;
1027
0
    else
1028
0
        ss->zoom_y = zoom_y2;
1029
1030
0
    return 0;
1031
0
}
1032
1033
static const filter_defn_s Mitchell_defn =
1034
{
1035
    Mitchell_filter,
1036
    Mitchell_support,
1037
    Mitchell_contrib_pixels,
1038
    Mitchell_min_scale
1039
};
1040
1041
static const filter_defn_s Interp_defn =
1042
{
1043
    Interp_filter,
1044
    Interp_support,
1045
    Interp_contrib_pixels,
1046
    Interp_min_scale
1047
};
1048
1049
static int
1050
s_IScale_init(stream_state * st)
1051
0
{
1052
0
    stream_IScale_state *const ss = (stream_IScale_state *) st;
1053
0
    const filter_defn_s *horiz = &Mitchell_defn;
1054
0
    const filter_defn_s *vert  = &Mitchell_defn;
1055
0
    int abs_interp_limit = ss->params.abs_interp_limit;
1056
0
    int limited_EntireWidthOut = (ss->params.EntireWidthOut + abs_interp_limit - 1) / abs_interp_limit;
1057
0
    int limited_EntireHeightOut = (ss->params.EntireHeightOut + abs_interp_limit - 1) / abs_interp_limit;
1058
1059
    /* By default we use the mitchell filter, but if we are scaling down
1060
     * (either on the horizontal or the vertical axis) then use the simple
1061
     * interpolation filter for that axis. */
1062
0
    if (limited_EntireWidthOut < ss->params.EntireWidthIn)
1063
0
        horiz = &Interp_defn;
1064
0
    if (limited_EntireHeightOut < ss->params.EntireHeightIn)
1065
0
        vert = &Interp_defn;
1066
1067
0
    return do_init(st, horiz, vert);
1068
0
}
1069
1070
/* Process a buffer.  Note that this handles Encode and Decode identically. */
1071
static int
1072
s_IScale_process(stream_state * st, stream_cursor_read * pr,
1073
                 stream_cursor_write * pw, bool last)
1074
0
{
1075
0
    stream_IScale_state *const ss = (stream_IScale_state *) st;
1076
0
    int abs_interp_limit = ss->params.abs_interp_limit;
1077
0
    int limited_WidthOut = (ss->params.WidthOut + abs_interp_limit - 1) / abs_interp_limit;
1078
0
    int limited_HeightOut = (ss->params.HeightOut + abs_interp_limit - 1) / abs_interp_limit;
1079
0
    int limited_PatchWidthOut = (ss->params.PatchWidthOut + abs_interp_limit - 1) / abs_interp_limit;
1080
0
    int limited_LeftMarginOut = (ss->params.LeftMarginOut) / abs_interp_limit;
1081
1082
    /* Check whether we need to deliver any output. */
1083
1084
0
  top:
1085
0
    ss->params.Active = (ss->src_y >= ss->params.TopMarginIn &&
1086
0
                         ss->src_y <= ss->params.TopMarginIn + ss->params.PatchHeightIn);
1087
1088
0
    while (ss->src_y > ss->dst_last_index) {  /* We have enough horizontally scaled temporary rows */
1089
        /* to generate a vertically scaled output row. */
1090
0
        uint wleft = pw->limit - pw->ptr;
1091
1092
0
        if (ss->dst_y == limited_HeightOut)
1093
0
            return EOFC;
1094
0
        if (wleft == 0)
1095
0
            return 1;
1096
0
        if (ss->dst_offset == 0) {
1097
0
            byte *row;
1098
1099
0
            if (wleft >= ss->dst_size) {        /* We can scale the row directly into the output. */
1100
0
                row = pw->ptr + 1;
1101
0
                pw->ptr += ss->dst_size;
1102
0
            } else {            /* We'll have to buffer the row. */
1103
0
                row = ss->dst;
1104
0
            }
1105
            /* Apply filter to zoom vertically from tmp to dst. */
1106
0
            if (ss->params.Active) {
1107
0
                if_debug1('w', "[w]zoom_y y = %d\n", ss->dst_y);
1108
0
                ss->zoom_y(row, /* Where to scale to */
1109
0
                       ss->tmp, /* Line buffer */
1110
0
                       limited_LeftMarginOut, /* Skip */
1111
0
                       limited_PatchWidthOut, /* How many pixels to produce */
1112
0
                       limited_WidthOut, /* Stride */
1113
0
                       ss->params.spp_interp, /* Color count */
1114
0
                       &ss->dst_next_list, ss->dst_items);
1115
0
            }
1116
            /* Idiotic C coercion rules allow T* and void* to be */
1117
            /* inter-assigned freely, but not compared! */
1118
0
            if ((void *)row != ss->dst)         /* no buffering */
1119
0
                goto adv;
1120
0
        }
1121
0
        {                       /* We're delivering a buffered output row. */
1122
0
            uint wcount = ss->dst_size - ss->dst_offset;
1123
0
            uint ncopy = min(wleft, wcount);
1124
1125
0
            if (ss->params.Active)
1126
0
                memcpy(pw->ptr + 1, (byte *) ss->dst + ss->dst_offset, ncopy);
1127
0
            pw->ptr += ncopy;
1128
0
            ss->dst_offset += ncopy;
1129
0
            if (ncopy != wcount)
1130
0
                return 1;
1131
0
            ss->dst_offset = 0;
1132
0
        }
1133
        /* Advance to the next output row. */
1134
0
      adv:++ss->dst_y;
1135
0
        if (ss->dst_y != limited_HeightOut)
1136
0
            calculate_dst_contrib(ss, ss->dst_y);
1137
0
    }
1138
1139
    /* Read input data and scale horizontally into tmp. */
1140
1141
0
    {
1142
0
        uint rleft = pr->limit - pr->ptr;
1143
0
        uint rcount = ss->src_size - ss->src_offset;
1144
1145
0
        if (rleft == 0)
1146
0
            return 0;           /* need more input */
1147
0
        if (ss->src_y >= ss->params.HeightIn)
1148
0
            return ERRC;
1149
0
        if (rleft >= rcount) {  /* We're going to fill up a row. */
1150
0
            const byte *row;
1151
1152
0
            if (ss->src_offset == 0) {  /* We have a complete row.  Read the data */
1153
                /* directly from the input. */
1154
0
                row = pr->ptr + 1;
1155
0
            } else {            /* We're buffering a row in src. */
1156
0
                row = ss->src;
1157
0
                if (ss->params.Active)
1158
0
                    memcpy((byte *) ss->src + ss->src_offset, pr->ptr + 1,
1159
0
                           rcount);
1160
0
                ss->src_offset = 0;
1161
0
            }
1162
            /* Apply filter to zoom horizontally from src to tmp. */
1163
0
            if_debug3('w', "[w]zoom_x y = %d to tmp row %d%s\n",
1164
0
                      ss->src_y, (ss->src_y % ss->max_support),
1165
0
                      ss->params.Active ? "" : " (Inactive)");
1166
0
            if (ss->params.Active)
1167
0
                ss->zoom_x(/* Where to scale to (dst line address in tmp buffer) */
1168
0
                       ss->tmp + (ss->src_y % ss->max_support) *
1169
0
                       limited_WidthOut * ss->params.spp_interp,
1170
0
                       row, /* Where to scale from */
1171
0
                       limited_LeftMarginOut, /* Line skip */
1172
0
                       limited_PatchWidthOut, /* How many pixels to produce */
1173
0
                       ss->params.spp_interp, /* Color count */
1174
0
                       ss->contrib, ss->items);
1175
0
            pr->ptr += rcount;
1176
0
            ++(ss->src_y);
1177
0
            goto top;
1178
0
        } else {                /* We don't have a complete row.  Copy data to src buffer. */
1179
0
            if (ss->params.Active)
1180
0
                memcpy((byte *) ss->src + ss->src_offset, pr->ptr + 1, rleft);
1181
0
            ss->src_offset += rleft;
1182
0
            pr->ptr += rleft;
1183
0
            return 0;
1184
0
        }
1185
0
    }
1186
0
}
1187
1188
/* Release the filter's storage. */
1189
static void
1190
s_IScale_release(stream_state * st)
1191
0
{
1192
0
    stream_IScale_state *const ss = (stream_IScale_state *) st;
1193
0
    gs_memory_t *mem = ss->memory;
1194
1195
0
    gs_free_object(mem, (void *)ss->src, "image_scale src");    /* no longer const */
1196
0
    ss->src = 0;
1197
0
    gs_free_object(mem, ss->dst, "image_scale dst");
1198
0
    ss->dst = 0;
1199
0
    gs_free_object(mem, ss->items, "image_scale contrib[*]");
1200
0
    ss->items = 0;
1201
0
    gs_free_object(mem, ss->dst_items, "image_scale contrib_dst[*]");
1202
0
    ss->dst_items = 0;
1203
0
    gs_free_object(mem, ss->contrib, "image_scale contrib");
1204
0
    ss->contrib = 0;
1205
0
    gs_free_object(mem, ss->tmp, "image_scale tmp");
1206
0
    ss->tmp = 0;
1207
0
}
1208
1209
/* Stream template */
1210
const stream_template s_IScale_template = {
1211
    &st_IScale_state, s_IScale_init, s_IScale_process, 1, 1,
1212
    s_IScale_release, s_IScale_set_defaults
1213
};