1 /* Copyright (C) 2001-2012 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.,  7 Mt. Lassen Drive - Suite A-134, San Rafael,
13    CA  94903, U.S.A., +1(415)492-9861, 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 
26 /*
27  *    Image scaling code is based on public domain code from
28  *      Graphics Gems III (pp. 414-424), Academic Press, 1992.
29  */
30 
31 /* ---------------- ImageScaleEncode/Decode ---------------- */
32 
33 /* Auxiliary structures. */
34 typedef struct {
35     double weight;               /* float or scaled fraction */
36 } CONTRIB;
37 
38 typedef struct {
39     int index;                  /* index of first element in list of */
40     /* contributors */
41     int n;                      /* number of contributors */
42     /* (not multiplied by stride) */
43     int first_pixel;            /* offset of first value in source data */
44 } CLIST;
45 
46 /* ImageScaleEncode / ImageScaleDecode */
47 typedef struct stream_IScale_state_s {
48     /* The client sets the params values before initialization. */
49     stream_image_scale_state_common;  /* = state_common + params */
50     /* The init procedure sets the following. */
51     int sizeofPixelIn;          /* bytes per input value, 1 or 2 */
52     int sizeofPixelOut;         /* bytes per output value, 1 or 2 */
53     void /*PixelIn */ *src;
54     void /*PixelOut */ *dst;
55     byte *tmp;
56     CLIST *contrib;
57     CONTRIB *items;
58     /* The following are updated dynamically. */
59     int src_y;
60     uint src_offset, src_size;
61     int dst_y;
62     int src_y_offset;
63     uint dst_offset, dst_size;
64     CLIST dst_next_list;        /* for next output value */
65     int dst_last_index;         /* highest index used in list */
66     /* Vertical filter details */
67     int filter_width;
68     int max_support;
69     double (*filter)(double);
70     double min_scale;
71     CONTRIB *dst_items; /* ditto */
72 } stream_IScale_state;
73 
74 gs_private_st_ptrs6(st_IScale_state, stream_IScale_state,
75     "ImageScaleEncode/Decode state",
76     iscale_state_enum_ptrs, iscale_state_reloc_ptrs,
77     dst, src, tmp, contrib, items, dst_items);
78 
79 /* ------ Digital filter definition ------ */
80 
81 /* Mitchell filter definition */
82 #define Mitchell_support 2
83 #define Mitchell_min_scale ((Mitchell_support * 2) / (MAX_ISCALE_SUPPORT - 1.01))
84 #define B (1.0 / 3.0)
85 #define C (1.0 / 3.0)
86 static double
Mitchell_filter(double t)87 Mitchell_filter(double t)
88 {
89     double t2 = t * t;
90 
91     if (t < 0)
92         t = -t;
93 
94     if (t < 1)
95         return
96             ((12 - 9 * B - 6 * C) * (t * t2) +
97              (-18 + 12 * B + 6 * C) * t2 +
98              (6 - 2 * B)) / 6;
99     else if (t < 2)
100         return
101             ((-1 * B - 6 * C) * (t * t2) +
102              (6 * B + 30 * C) * t2 +
103              (-12 * B - 48 * C) * t +
104              (8 * B + 24 * C)) / 6;
105     else
106         return 0;
107 }
108 
109 /* Interpolated filter definition */
110 #define Interp_support 1
111 #define Interp_min_scale 0
112 static double
Interp_filter(double t)113 Interp_filter(double t)
114 {
115     if (t < 0)
116         t = -t;
117 
118     if (t >= 1)
119         return 0;
120     return 1 + (2*t -3)*t*t;
121 }
122 
123 /*
124  * The environment provides the following definitions:
125  *      double fproc(double t)
126  *      double fWidthIn
127  *      PixelTmp {min,max,unit}PixelTmp
128  */
129 #define CLAMP(v, mn, mx)\
130   (v < mn ? mn : v > mx ? mx : v)
131 
132 /* ------ Auxiliary procedures ------ */
133 
134 /* Calculate the support for a given scale. */
135 /* The value is always in the range 1..max_support (was MAX_ISCALE_SUPPORT). */
136 static int
Interp_contrib_pixels(double scale)137 Interp_contrib_pixels(double scale)
138 {
139     if (scale == 0.0)
140         return 1;
141     return (int)(((float)Interp_support) / (scale >= 1.0 ? 1.0 : scale)
142                  * 2 + 1.5);
143 }
144 
145 static int
Mitchell_contrib_pixels(double scale)146 Mitchell_contrib_pixels(double scale)
147 {
148     if (scale == 0.0)
149         return 1;
150     return (int)(((float)Mitchell_support) / (scale >= 1.0 ? 1.0 : max(scale, Mitchell_min_scale))
151                  * 2 + 1.5);
152 }
153 
154 /* Pre-calculate filter contributions for a row or a column. */
155 /* Return the highest input pixel index used. */
156 static int
calculate_contrib(CLIST * contrib,CONTRIB * items,double scale,int starting_output_index,int src_y_offset,int dst_size,int src_size,int size,int limit,int modulus,int stride,double rescale_factor,int fWidthIn,double (* fproc)(double),double min_scale)157 calculate_contrib(
158         /* Return weight list parameters in contrib[0 .. size-1]. */
159                      CLIST * contrib,
160         /* Store weights in items[0 .. contrib_pixels(scale)*size-1]. */
161         /* (Less space than this may actually be needed.) */
162                      CONTRIB * items,
163         /* The output image is scaled by 'scale' relative to the input. */
164                      double scale,
165         /* Start generating weights for input pixel 'starting_output_index'. */
166                      int starting_output_index,
167         /* Offset of input subimage (data) from the input image start. */
168                      int src_y_offset,
169         /* Entire output image size. */
170                      int dst_size,
171         /* Entire input image size. */
172                      int src_size,
173         /* Generate 'size' weight lists. */
174                      int size,
175         /* Limit pixel indices to 'limit', for clamping at the edges */
176         /* of the image. */
177                      int limit,
178         /* Wrap pixel indices modulo 'modulus'. */
179                      int modulus,
180         /* Successive pixel values are 'stride' distance apart -- */
181         /* normally, the number of color components. */
182                      int stride,
183         /* The unit of output is 'rescale_factor' times the unit of input. */
184                      double rescale_factor,
185         /* The filters width */
186                      int fWidthIn,
187         /* The filter to use */
188                      double (*fproc)(double),
189         /* minimum scale factor to use */
190                      double min_scale
191 )
192 {
193     double WidthIn, fscale;
194     bool squeeze;
195     int npixels;
196     int i, j;
197     int last_index = -1;
198 
199     if_debug1('w', "[w]calculate_contrib scale=%lg\n", scale);
200     if (scale < 1.0) {
201         double clamped_scale = max(scale, min_scale);
202         WidthIn = ((double)fWidthIn) / clamped_scale;
203         fscale = 1.0 / clamped_scale;
204         squeeze = true;
205     } else {
206         WidthIn = (double)fWidthIn;
207         fscale = 1.0;
208         squeeze = false;
209     }
210     npixels = (int)(WidthIn * 2 + 1);
211 
212     for (i = 0; i < size; ++i) {
213         /* Here we need :
214            double scale = (double)dst_size / src_size;
215            float dst_offset_fraction = floor(dst_offset) - dst_offset;
216            double center = (starting_output_index  + i + dst_offset_fraction + 0.5) / scale - 0.5;
217            int left = (int)ceil(center - WidthIn);
218            int right = (int)floor(center + WidthIn);
219            We can't compute 'right' in floats because float arithmetics is not associative.
220            In older versions tt caused a 1 pixel bias of image bands due to
221            rounding direction appears to depend on src_y_offset. So compute in rationals.
222            Since pixel center fall to half integers, we subtract 0.5
223            in the image space and add 0.5 in the device space.
224          */
225         int dst_y_offset_fraction_num = (int)((int64_t)src_y_offset * dst_size % src_size) * 2 <= src_size
226                         ? -(int)((int64_t)src_y_offset * dst_size % src_size)
227                         : src_size - (int)((int64_t)src_y_offset * dst_size % src_size);
228         int center_denom = dst_size * 2;
229         int64_t center_num = /* center * center_denom * 2 = */
230             (starting_output_index  + i) * src_size * 2 + src_size + dst_y_offset_fraction_num * 2 - dst_size;
231         int left = (int)ceil((center_num - WidthIn * center_denom) / center_denom);
232         int right = (int)floor((center_num + WidthIn * center_denom) / center_denom);
233         double center = (double)center_num / center_denom;
234 #define clamp_pixel(j) (j < 0 ? 0 : j >= limit ? limit - 1 : j)
235         int first_pixel = clamp_pixel(left);
236         int last_pixel = clamp_pixel(right);
237         CONTRIB *p;
238 
239         if_debug4('w', "[w]i=%d, i+offset=%lg scale=%lg center=%lg : ", starting_output_index + i,
240                 starting_output_index + i + (double)src_y_offset / src_size * dst_size, scale, center);
241         if (last_pixel > last_index)
242             last_index = last_pixel;
243         contrib[i].first_pixel = (first_pixel % modulus) * stride;
244         contrib[i].n = last_pixel - first_pixel + 1;
245         contrib[i].index = i * npixels;
246         p = items + contrib[i].index;
247         for (j = 0; j < npixels; ++j)
248             p[j].weight = 0;
249         if (squeeze) {
250             double sum = 0;
251             for (j = left; j <= right; ++j)
252                 sum += fproc((center - j) / fscale) / fscale;
253             for (j = left; j <= right; ++j) {
254                 double weight = fproc((center - j) / fscale) / fscale / sum;
255                 int n = clamp_pixel(j);
256                 int k = n - first_pixel;
257 
258                 p[k].weight +=
259                     (float) (weight * rescale_factor);
260                 if_debug2('w', " %d %f", k, (float)p[k].weight);
261             }
262 
263         } else {
264             double sum = 0;
265             for (j = left; j <= right; ++j)
266                 sum += fproc(center - j);
267             for (j = left; j <= right; ++j) {
268                 double weight = fproc(center - j) / sum;
269                 int n = clamp_pixel(j);
270                 int k = n - first_pixel;
271 
272                 p[k].weight +=
273                     (float) (weight * rescale_factor);
274                 if_debug2('w', " %d %f", k, (float)p[k].weight);
275             }
276         }
277         if_debug0('w', "\n");
278     }
279     return last_index;
280 }
281 
282 /* Apply filter to zoom horizontally from src to tmp. */
283 static void
zoom_x(byte * tmp,const void * src,int sizeofPixelIn,int skip,int tmp_width,int Colors,const CLIST * contrib,const CONTRIB * items)284 zoom_x(byte * tmp, const void /*PixelIn */ *src, int sizeofPixelIn,
285        int skip, int tmp_width, int Colors, const CLIST * contrib,
286        const CONTRIB * items)
287 {
288     int c, i;
289 
290     contrib += skip;
291     tmp += Colors * skip;
292 
293     for (c = 0; c < Colors; ++c) {
294         byte *tp = tmp + c;
295         const CLIST *clp = contrib;
296 
297         if_debug1('W', "[W]zoom_x color %d:", c);
298         if (sizeofPixelIn == 1) {
299             const byte *raster = (const byte *)src + c;
300 
301             for ( i = 0; i < tmp_width; tp += Colors, ++clp, ++i ) {
302                 double weight = 0;
303                 int pixel, j = clp->n;
304                 const byte *pp = raster + clp->first_pixel;
305                 const CONTRIB *cp = items + clp->index;
306 
307                 switch ( Colors ) {
308                   case 1:
309                       for ( ; j > 0; pp += 1, ++cp, --j )
310                           weight += *pp * cp->weight;
311                       break;
312                   case 3:
313                       for ( ; j > 0; pp += 3, ++cp, --j )
314                           weight += *pp * cp->weight;
315                       break;
316                   default:
317                       for ( ; j > 0; pp += Colors, ++cp, --j )
318                           weight += *pp * cp->weight;
319                 }
320                 pixel = (int)(weight + 0.5);
321                 if_debug1('W', " %g", weight);
322                 *tp = (byte)CLAMP(pixel, 0, 255);
323             }
324         } else {                /* sizeofPixelIn == 2 */
325             const bits16 *raster = (const bits16 *)src + c;
326             for ( i = 0; i < tmp_width; tp += Colors, ++clp, ++i ) {
327                 double weight = 0;
328                 int pixel, j = clp->n;
329                 const bits16 *pp = raster + clp->first_pixel;
330                 const CONTRIB *cp = items + clp->index;
331 
332                 switch ( Colors ) {
333                   case 1:
334                       for ( ; j > 0; pp += 1, ++cp, --j )
335                           weight += *pp * cp->weight;
336                       break;
337                   case 3:
338                       for ( ; j > 0; pp += 3, ++cp, --j )
339                           weight += *pp * cp->weight;
340                       break;
341                   default:
342                       for ( ; j > 0; pp += Colors, ++cp, --j )
343                           weight += *pp * cp->weight;
344                 }
345                 pixel = (int)(weight + 0.5);
346                 if_debug1('W', " %g", weight);
347                 *tp = (byte)CLAMP(pixel, 0, 255);
348             }
349         }
350         if_debug0('W', "\n");
351     }
352 }
353 
354 /*
355  * Apply filter to zoom vertically from tmp to dst.
356  * This is simpler because we can treat all columns identically
357  * without regard to the number of samples per pixel.
358  */
359 static void
zoom_y(void * dst,int sizeofPixelOut,uint MaxValueOut,const byte * tmp,int skip,int WidthOut,int Stride,int Colors,const CLIST * contrib,const CONTRIB * items)360 zoom_y(void /*PixelOut */ *dst, int sizeofPixelOut, uint MaxValueOut,
361        const byte * tmp, int skip, int WidthOut, int Stride,
362        int Colors, const CLIST * contrib, const CONTRIB * items)
363 {
364     int kn = Stride * Colors;
365     int width = WidthOut * Colors;
366     int cn = contrib->n;
367     int first_pixel = contrib->first_pixel;
368     const CONTRIB *cbp = items + contrib->index;
369     int kc;
370     int max_weight = MaxValueOut;
371 
372     if_debug0('W', "[W]zoom_y: ");
373 
374     skip *= Colors;
375     width += skip;
376     if (sizeofPixelOut == 1) {
377         for ( kc = skip; kc < width; ++kc ) {
378             double weight = 0;
379             const byte *pp = &tmp[kc + first_pixel];
380             int pixel, j = cn;
381             const CONTRIB *cp = cbp;
382 
383             for ( ; j > 0; pp += kn, ++cp, --j )
384                 weight += *pp * cp->weight;
385             pixel = (int)(weight + 0.5);
386             if_debug1('W', " %x", pixel);
387             ((byte *)dst)[kc] = (byte)CLAMP(pixel, 0, max_weight);
388         }
389     } else {                    /* sizeofPixelOut == 2 */
390         for ( kc = skip; kc < width; ++kc ) {
391             double weight = 0;
392             const byte *pp = &tmp[kc + first_pixel];
393             int pixel, j = cn;
394             const CONTRIB *cp = cbp;
395 
396             for ( ; j > 0; pp += kn, ++cp, --j )
397                 weight += *pp * cp->weight;
398             pixel = (int)(weight + 0.5);
399             if_debug1('W', " %x", pixel);
400             ((bits16 *)dst)[kc] = (bits16)CLAMP(pixel, 0, max_weight);
401         }
402     }
403     if_debug0('W', "\n");
404 }
405 
406 /* ------ Stream implementation ------ */
407 
408 /* Forward references */
409 static void s_IScale_release(stream_state * st);
410 
411 /* Calculate the weights for an output row. */
412 static void
calculate_dst_contrib(stream_IScale_state * ss,int y)413 calculate_dst_contrib(stream_IScale_state * ss, int y)
414 {
415     uint row_size = ss->params.WidthOut * ss->params.spp_interp;
416     int last_index =
417     calculate_contrib(&ss->dst_next_list, ss->dst_items,
418                       (double)ss->params.EntireHeightOut / ss->params.EntireHeightIn,
419                       y, ss->src_y_offset, ss->params.EntireHeightOut, ss->params.EntireHeightIn,
420                       1, ss->params.HeightIn, ss->max_support, row_size,
421                       (double)ss->params.MaxValueOut / 255, ss->filter_width,
422                       ss->filter, ss->min_scale);
423     int first_index_mod = ss->dst_next_list.first_pixel / row_size;
424 
425     if_debug2('w', "[W]calculate_dst_contrib for y = %d, y+offset=%d\n", y, y + ss->src_y_offset);
426     ss->dst_last_index = last_index;
427     last_index %= ss->max_support;
428     if (last_index < first_index_mod) {         /* Shuffle the indices to account for wraparound. */
429         CONTRIB *shuffle = &ss->dst_items[ss->max_support];
430         int i;
431 
432         for (i = 0; i < ss->max_support; ++i) {
433             shuffle[i].weight =
434                 (i <= last_index ?
435                  ss->dst_items[i + ss->max_support - first_index_mod].weight :
436                  i >= first_index_mod ?
437                  ss->dst_items[i - first_index_mod].weight :
438                  0);
439             if_debug1('W', " %f", shuffle[i].weight);
440         }
441         memcpy(ss->dst_items, shuffle, ss->max_support * sizeof(CONTRIB));
442         ss->dst_next_list.n = ss->max_support;
443         ss->dst_next_list.first_pixel = 0;
444     }
445     if_debug0('W', "\n");
446 }
447 
448 /* Set default parameter values (actually, just clear pointers). */
449 static void
s_IScale_set_defaults(stream_state * st)450 s_IScale_set_defaults(stream_state * st)
451 {
452     stream_IScale_state *const ss = (stream_IScale_state *) st;
453 
454     ss->src = 0;
455     ss->dst = 0;
456     ss->tmp = 0;
457     ss->contrib = 0;
458     ss->items = 0;
459 }
460 
461 typedef struct filter_defn_s {
462     double  (*filter)(double);
463     int     filter_width;
464     int     (*contrib_pixels)(double scale);
465     double  min_scale;
466 } filter_defn_s;
467 
468 /* Initialize the filter. */
469 static int
do_init(stream_state * st,const filter_defn_s * horiz,const filter_defn_s * vert)470 do_init(stream_state        *st,
471         const filter_defn_s *horiz,
472         const filter_defn_s *vert)
473 {
474     stream_IScale_state *const ss = (stream_IScale_state *) st;
475     gs_memory_t *mem = ss->memory;
476 
477     ss->sizeofPixelIn = ss->params.BitsPerComponentIn / 8;
478     ss->sizeofPixelOut = ss->params.BitsPerComponentOut / 8;
479 
480     ss->src_y = 0;
481     ss->src_size =
482         ss->params.WidthIn * ss->sizeofPixelIn * ss->params.spp_interp;
483     ss->src_offset = 0;
484     ss->dst_y = 0;
485     ss->src_y_offset = ss->params.src_y_offset;
486     ss->dst_size =
487         ss->params.WidthOut * ss->sizeofPixelOut * ss->params.spp_interp;
488     ss->dst_offset = 0;
489 
490     /* create intermediate image to hold horizontal zoom */
491     ss->max_support  = vert->contrib_pixels((double)ss->params.EntireHeightOut/
492                                             ss->params.EntireHeightIn);
493     ss->filter_width = vert->filter_width;
494     ss->filter       = vert->filter;
495     ss->min_scale    = vert->min_scale;
496     ss->tmp = (byte *) gs_alloc_byte_array(mem,
497                                            ss->max_support,
498                                            (ss->params.WidthOut *
499                                             ss->params.spp_interp),
500                                            "image_scale tmp");
501     ss->contrib = (CLIST *) gs_alloc_byte_array(mem,
502                                                 max(ss->params.WidthOut,
503                                                     ss->params.HeightOut),
504                                                 sizeof(CLIST),
505                                                 "image_scale contrib");
506     ss->items = (CONTRIB *)
507                     gs_alloc_byte_array(mem,
508                                         (horiz->contrib_pixels(
509                                             (double)ss->params.EntireWidthOut /
510                                             ss->params.EntireWidthIn) *
511                                          ss->params.WidthOut),
512                                          sizeof(CONTRIB),
513                                          "image_scale contrib[*]");
514     ss->dst_items = (CONTRIB *) gs_alloc_byte_array(mem,
515                                                     ss->max_support*2,
516                                                     sizeof(CONTRIB), "image_scale contrib_dst[*]");
517     /* Allocate buffers for 1 row of source and destination. */
518     ss->dst =
519         gs_alloc_byte_array(mem, ss->params.WidthOut * ss->params.spp_interp,
520                             ss->sizeofPixelOut, "image_scale dst");
521     ss->src =
522         gs_alloc_byte_array(mem, ss->params.WidthIn * ss->params.spp_interp,
523                             ss->sizeofPixelIn, "image_scale src");
524     if (ss->tmp == 0 || ss->contrib == 0 || ss->items == 0 ||
525         ss->dst_items == 0 || ss->dst == 0 || ss->src == 0
526         ) {
527         s_IScale_release(st);
528         return ERRC;
529 /****** WRONG ******/
530     }
531     /* Pre-calculate filter contributions for a row. */
532     calculate_contrib(ss->contrib, ss->items,
533                       (double)ss->params.EntireWidthOut / ss->params.EntireWidthIn,
534                       0, 0, ss->params.WidthOut, ss->params.WidthIn,
535                       ss->params.WidthOut, ss->params.WidthIn, ss->params.WidthIn,
536                       ss->params.spp_interp, 255. / ss->params.MaxValueIn,
537                       horiz->filter_width, horiz->filter, horiz->min_scale);
538 
539     /* Prepare the weights for the first output row. */
540     calculate_dst_contrib(ss, 0);
541 
542     return 0;
543 
544 }
545 
546 static const filter_defn_s Mitchell_defn =
547 {
548     Mitchell_filter,
549     Mitchell_support,
550     Mitchell_contrib_pixels,
551     Mitchell_min_scale
552 };
553 
554 static const filter_defn_s Interp_defn =
555 {
556     Interp_filter,
557     Interp_support,
558     Interp_contrib_pixels,
559     Interp_min_scale
560 };
561 
562 static int
s_IScale_init(stream_state * st)563 s_IScale_init(stream_state * st)
564 {
565     stream_IScale_state *const ss = (stream_IScale_state *) st;
566     const filter_defn_s *horiz = &Mitchell_defn;
567     const filter_defn_s *vert  = &Mitchell_defn;
568 
569     /* By default we use the mitchell filter, but if we are scaling down
570      * (either on the horizontal or the vertical axis) then use the simple
571      * interpolation filter for that axis. */
572     if (ss->params.EntireWidthOut < ss->params.EntireWidthIn)
573         horiz = &Interp_defn;
574     if (ss->params.EntireHeightOut < ss->params.EntireHeightIn)
575         vert = &Interp_defn;
576 
577     return do_init(st, horiz, vert);
578 }
579 
580 /* Process a buffer.  Note that this handles Encode and Decode identically. */
581 static int
s_IScale_process(stream_state * st,stream_cursor_read * pr,stream_cursor_write * pw,bool last)582 s_IScale_process(stream_state * st, stream_cursor_read * pr,
583                  stream_cursor_write * pw, bool last)
584 {
585     stream_IScale_state *const ss = (stream_IScale_state *) st;
586 
587     /* Check whether we need to deliver any output. */
588 
589   top:
590     ss->params.Active = (ss->src_y >= ss->params.TopMargin &&
591                          ss->src_y <= ss->params.TopMargin + ss->params.PatchHeightIn);
592 
593     while (ss->src_y > ss->dst_last_index) {  /* We have enough horizontally scaled temporary rows */
594         /* to generate a vertically scaled output row. */
595         uint wleft = pw->limit - pw->ptr;
596 
597         if (ss->dst_y == ss->params.HeightOut)
598             return EOFC;
599         if (wleft == 0)
600             return 1;
601         if (ss->dst_offset == 0) {
602             byte *row;
603 
604             if (wleft >= ss->dst_size) {        /* We can scale the row directly into the output. */
605                 row = pw->ptr + 1;
606                 pw->ptr += ss->dst_size;
607             } else {            /* We'll have to buffer the row. */
608                 row = ss->dst;
609             }
610             /* Apply filter to zoom vertically from tmp to dst. */
611             if (ss->params.Active)
612                 zoom_y(row, /* Where to scale to */
613                        ss->sizeofPixelOut, /* 1 (8 bit) or 2 (16bit) output */
614                        ss->params.MaxValueOut, /* output value scale */
615                        ss->tmp, /* Line buffer */
616                        ss->params.LeftMarginOut, /* Skip */
617                        ss->params.PatchWidthOut, /* How many pixels to produce */
618                        ss->params.WidthOut, /* Stride */
619                        ss->params.spp_interp, /* Color count */
620                        &ss->dst_next_list, ss->dst_items);
621             /* Idiotic C coercion rules allow T* and void* to be */
622             /* inter-assigned freely, but not compared! */
623             if ((void *)row != ss->dst)         /* no buffering */
624                 goto adv;
625         }
626         {                       /* We're delivering a buffered output row. */
627             uint wcount = ss->dst_size - ss->dst_offset;
628             uint ncopy = min(wleft, wcount);
629 
630             if (ss->params.Active)
631                 memcpy(pw->ptr + 1, (byte *) ss->dst + ss->dst_offset, ncopy);
632             pw->ptr += ncopy;
633             ss->dst_offset += ncopy;
634             if (ncopy != wcount)
635                 return 1;
636             ss->dst_offset = 0;
637         }
638         /* Advance to the next output row. */
639       adv:++ss->dst_y;
640         if (ss->dst_y != ss->params.HeightOut)
641             calculate_dst_contrib(ss, ss->dst_y);
642     }
643 
644     /* Read input data and scale horizontally into tmp. */
645 
646     {
647         uint rleft = pr->limit - pr->ptr;
648         uint rcount = ss->src_size - ss->src_offset;
649 
650         if (rleft == 0)
651             return 0;           /* need more input */
652         if (ss->src_y >= ss->params.HeightIn)
653             return ERRC;
654         if (rleft >= rcount) {  /* We're going to fill up a row. */
655             const byte *row;
656 
657             if (ss->src_offset == 0) {  /* We have a complete row.  Read the data */
658                 /* directly from the input. */
659                 row = pr->ptr + 1;
660             } else {            /* We're buffering a row in src. */
661                 row = ss->src;
662                 memcpy((byte *) ss->src + ss->src_offset, pr->ptr + 1,
663                        rcount);
664                 ss->src_offset = 0;
665             }
666             /* Apply filter to zoom horizontally from src to tmp. */
667             if_debug2('w', "[w]zoom_x y = %d to tmp row %d\n",
668                       ss->src_y, (ss->src_y % ss->max_support));
669             if (ss->params.Active)
670                 zoom_x(/* Where to scale to (dst line address in tmp buffer) */
671                        ss->tmp + (ss->src_y % ss->max_support) *
672                        ss->params.WidthOut * ss->params.spp_interp,
673                        row, /* Where to scale from */
674                        ss->sizeofPixelIn, /* 1 (8 bit) or 2 (16bit) */
675                        ss->params.LeftMarginOut, /* Line skip */
676                        ss->params.PatchWidthOut, /* How many pixels to produce */
677                        ss->params.spp_interp, /* Color count */
678                        ss->contrib, ss->items);
679             pr->ptr += rcount;
680             ++(ss->src_y);
681             goto top;
682         } else {                /* We don't have a complete row.  Copy data to src buffer. */
683             if (ss->params.Active)
684                 memcpy((byte *) ss->src + ss->src_offset, pr->ptr + 1, rleft);
685             ss->src_offset += rleft;
686             pr->ptr += rleft;
687             return 0;
688         }
689     }
690 }
691 
692 /* Release the filter's storage. */
693 static void
s_IScale_release(stream_state * st)694 s_IScale_release(stream_state * st)
695 {
696     stream_IScale_state *const ss = (stream_IScale_state *) st;
697     gs_memory_t *mem = ss->memory;
698 
699     gs_free_object(mem, (void *)ss->src, "image_scale src");    /* no longer const */
700     ss->src = 0;
701     gs_free_object(mem, ss->dst, "image_scale dst");
702     ss->dst = 0;
703     gs_free_object(mem, ss->items, "image_scale contrib[*]");
704     ss->items = 0;
705     gs_free_object(mem, ss->items, "image_scale contrib_dst[*]");
706     ss->dst_items = 0;
707     gs_free_object(mem, ss->contrib, "image_scale contrib");
708     ss->contrib = 0;
709     gs_free_object(mem, ss->tmp, "image_scale tmp");
710     ss->tmp = 0;
711 }
712 
713 /* Stream template */
714 const stream_template s_IScale_template = {
715     &st_IScale_state, s_IScale_init, s_IScale_process, 1, 1,
716     s_IScale_release, s_IScale_set_defaults
717 };
718