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