Warning: Attempt to read property "date" on null in /usr/local/www/websvn.planix.org/blame.php on line 247

Warning: Attempt to read property "msg" on null in /usr/local/www/websvn.planix.org/blame.php on line 247
WebSVN – planix.SVN – Blame – /os/branches/feature_fixcpp/sys/src/cmd/gs/src/siscale.c – Rev 2

Subversion Repositories planix.SVN

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 1
/* Copyright (C) 1995, 2000 Aladdin Enterprises.  All rights reserved.
2
 
3
  This software is provided AS-IS with no warranty, either express or
4
  implied.
5
 
6
  This software is distributed under license and may not be copied,
7
  modified or distributed except as expressly authorized under the terms
8
  of the license contained in the file LICENSE in this distribution.
9
 
10
  For more information about licensing, please refer to
11
  http://www.ghostscript.com/licensing/. For information on
12
  commercial licensing, go to http://www.artifex.com/licensing/ or
13
  contact Artifex Software, Inc., 101 Lucas Valley Road #110,
14
  San Rafael, CA  94903, U.S.A., +1(415)492-9861.
15
*/
16
 
17
/* $Id: siscale.c,v 1.7 2002/06/16 03:58:15 lpd Exp $ */
18
/* Image scaling filters */
19
#include "math_.h"
20
#include "memory_.h"
21
#include "stdio_.h"
22
#include <assert.h>
23
#include "gconfigv.h"
24
#include "gdebug.h"
25
#include "strimpl.h"
26
#include "siscale.h"
27
 
28
/*
29
 *    Image scaling code is based on public domain code from
30
 *      Graphics Gems III (pp. 414-424), Academic Press, 1992.
31
 */
32
 
33
/* ---------------- ImageScaleEncode/Decode ---------------- */
34
 
35
/* Define whether to accumulate pixels in fixed or floating point. */
36
#if USE_FPU <= 0
37
 
38
	/* Accumulate pixels in fixed point. */
39
 
40
typedef int PixelWeight;
41
 
42
#  if arch_ints_are_short
43
typedef long AccumTmp;
44
#  else
45
typedef int AccumTmp;
46
#  endif
47
 
48
/*
49
 *    The optimal scaling for fixed point arithmetic is a function of the
50
 *    size of AccumTmp type, the size if the input pixel, the size of the
51
 *    intermediate pixel (PixelTmp) and the size of the output pixel.  This
52
 *    is set by these definitions and the fraction_bits variables in the
53
 *    functions.
54
 */
55
#define num_weight_bits\
56
  ((sizeof(AccumTmp) - maxSizeofPixel) * 8 - (LOG2_MAX_ISCALE_SUPPORT + 1))
57
#define numScaleBits  ((maxSizeofPixel - sizeof(PixelTmp)) * 8 )
58
#define fixedScaleFactor  ((int) (1 << numScaleBits))
59
#define scale_PixelWeight(factor) ((int)((factor) * (1 << num_weight_bits)))
60
#define unscale_AccumTmp(atemp, fraction_bits) arith_rshift(atemp, fraction_bits)
61
#define NEED_FRACTION_BITS
62
 
63
#else /* USE_FPU > 0 */
64
 
65
	/* Accumulate pixels in floating point. */
66
 
67
typedef float PixelWeight;
68
typedef double AccumTmp;
69
 
70
#define num_weight_bits 0		/* Not used for floating point */
71
#define fixedScaleFactor 1		/* Straight scaling for floating point */
72
#define scale_PixelWeight(factor) (factor)
73
#define unscale_AccumTmp(atemp, fraction_bits) ((int)(atemp))
74
/*#undef NEED_FRACTION_BITS*/
75
 
76
#endif /* USE_FPU */
77
 
78
/* Temporary intermediate values */
79
typedef byte PixelTmp;
80
typedef int PixelTmp2;		/* extra width for clamping sum */
81
 
82
#define minPixelTmp 0
83
#define maxPixelTmp 255
84
#define unitPixelTmp 255
85
 
86
/* Max of all pixel sizes */
87
#define maxSizeofPixel 2
88
 
89
/* Auxiliary structures. */
90
 
91
typedef struct {
92
    PixelWeight weight;		/* float or scaled fraction */
93
} CONTRIB;
94
 
95
typedef struct {
96
    int index;			/* index of first element in list of */
97
    /* contributors */
98
    int n;			/* number of contributors */
99
    /* (not multiplied by stride) */
100
    int first_pixel;		/* offset of first value in source data */
101
} CLIST;
102
 
103
/* ImageScaleEncode / ImageScaleDecode */
104
typedef struct stream_IScale_state_s {
105
    /* The client sets the params values before initialization. */
106
    stream_image_scale_state_common;  /* = state_common + params */
107
    /* The init procedure sets the following. */
108
    int sizeofPixelIn;		/* bytes per input value, 1 or 2 */
109
    int sizeofPixelOut;		/* bytes per output value, 1 or 2 */
110
    double xscale, yscale;
111
    void /*PixelIn */ *src;
112
    void /*PixelOut */ *dst;
113
    PixelTmp *tmp;
114
    CLIST *contrib;
115
    CONTRIB *items;
116
    /* The following are updated dynamically. */
117
    int src_y;
118
    uint src_offset, src_size;
119
    int dst_y;
120
    uint dst_offset, dst_size;
121
    CLIST dst_next_list;	/* for next output value */
122
    int dst_last_index;		/* highest index used in list */
123
    CONTRIB dst_items[MAX_ISCALE_SUPPORT];	/* ditto */
124
} stream_IScale_state;
125
 
126
gs_private_st_ptrs5(st_IScale_state, stream_IScale_state,
127
    "ImageScaleEncode/Decode state",
128
    iscale_state_enum_ptrs, iscale_state_reloc_ptrs,
129
    dst, src, tmp, contrib, items);
130
 
131
/* ------ Digital filter definition ------ */
132
 
133
/* Mitchell filter definition */
134
#define Mitchell_support 2.0
135
#define B (1.0 / 3.0)
136
#define C (1.0 / 3.0)
137
private double
138
Mitchell_filter(double t)
139
{
140
    double t2 = t * t;
141
 
142
    if (t < 0)
143
	t = -t;
144
 
145
    if (t < 1)
146
	return
147
	    ((12 - 9 * B - 6 * C) * (t * t2) +
148
	     (-18 + 12 * B + 6 * C) * t2 +
149
	     (6 - 2 * B)) / 6;
150
    else if (t < 2)
151
	return
152
	    ((-1 * B - 6 * C) * (t * t2) +
153
	     (6 * B + 30 * C) * t2 +
154
	     (-12 * B - 48 * C) * t +
155
	     (8 * B + 24 * C)) / 6;
156
    else
157
	return 0;
158
}
159
 
160
#define filter_support Mitchell_support
161
#define filter_proc Mitchell_filter
162
#define fproc(t) filter_proc(t)
163
#define fWidthIn filter_support
164
 
165
/*
166
 * The environment provides the following definitions:
167
 *      typedef PixelTmp, PixelTmp2
168
 *      double fproc(double t)
169
 *      double fWidthIn
170
 *      PixelTmp {min,max,unit}PixelTmp
171
 */
172
#define CLAMP(v, mn, mx)\
173
  (v < mn ? mn : v > mx ? mx : v)
174
 
175
/* ------ Auxiliary procedures ------ */
176
 
177
/* Define the minimum scale. */
178
#define min_scale ((fWidthIn * 2) / (MAX_ISCALE_SUPPORT - 1.01))
179
 
180
/* Calculate the support for a given scale. */
181
/* The value is always in the range 1 .. MAX_ISCALE_SUPPORT. */
182
private int
183
contrib_pixels(double scale)
184
{
185
    return (int)(fWidthIn / (scale >= 1.0 ? 1.0 : max(scale, min_scale))
186
		 * 2 + 1);
187
}
188
 
189
/* Pre-calculate filter contributions for a row or a column. */
190
/* Return the highest input pixel index used. */
191
private int
192
calculate_contrib(
193
	/* Return weight list parameters in contrib[0 .. size-1]. */
194
		     CLIST * contrib,
195
	/* Store weights in items[0 .. contrib_pixels(scale)*size-1]. */
196
	/* (Less space than this may actually be needed.) */
197
		     CONTRIB * items,
198
	/* The output image is scaled by 'scale' relative to the input. */
199
		     double scale,
200
	/* Start generating weights for input pixel 'input_index'. */
201
		     int input_index,
202
	/* Generate 'size' weight lists. */
203
		     int size,
204
	/* Limit pixel indices to 'limit', for clamping at the edges */
205
	/* of the image. */
206
		     int limit,
207
	/* Wrap pixel indices modulo 'modulus'. */
208
		     int modulus,
209
	/* Successive pixel values are 'stride' distance apart -- */
210
	/* normally, the number of color components. */
211
		     int stride,
212
	/* The unit of output is 'rescale_factor' times the unit of input. */
213
		     double rescale_factor
214
)
215
{
216
    double scaled_factor = scale_PixelWeight(rescale_factor);
217
    double WidthIn, fscale;
218
    bool squeeze;
219
    int npixels;
220
    int i, j;
221
    int last_index = -1;
222
 
223
    if (scale < 1.0) {
224
	double clamped_scale = max(scale, min_scale);
225
 
226
	WidthIn = fWidthIn / clamped_scale;
227
	fscale = 1.0 / clamped_scale;
228
	squeeze = true;
229
    } else {
230
	WidthIn = fWidthIn;
231
	fscale = 1.0;
232
	squeeze = false;
233
    }
234
    npixels = (int)(WidthIn * 2 + 1);
235
 
236
    for (i = 0; i < size; ++i) {
237
	double center = (input_index + i) / scale;
238
	int left = (int)ceil(center - WidthIn);
239
	int right = (int)floor(center + WidthIn);
240
 
241
	/*
242
	 * In pathological cases, the limit may be much less
243
	 * than the support.  We do need to deal with this.
244
	 */
245
#define clamp_pixel(j)\
246
  (j < 0 ? (-j >= limit ? limit - 1 : -j) :\
247
   j >= limit ? (j >> 1 >= limit ? 0 : (limit - j) + limit - 1) :\
248
   j)
249
	int lmin =
250
	(left < 0 ? 0 : left);
251
	int lmax =
252
	(left < 0 ? (-left >= limit ? limit - 1 : -left) : left);
253
	int rmin =
254
	(right >= limit ?
255
	 (right >> 1 >= limit ? 0 : (limit - right) + limit - 1) :
256
	 right);
257
	int rmax =
258
	(right >= limit ? limit - 1 : right);
259
	int first_pixel = min(lmin, rmin);
260
	int last_pixel = max(lmax, rmax);
261
	CONTRIB *p;
262
 
263
	if (last_pixel > last_index)
264
	    last_index = last_pixel;
265
	contrib[i].first_pixel = (first_pixel % modulus) * stride;
266
	contrib[i].n = last_pixel - first_pixel + 1;
267
	contrib[i].index = i * npixels;
268
	p = items + contrib[i].index;
269
	for (j = 0; j < npixels; ++j)
270
	    p[j].weight = 0;
271
	if (squeeze) {
272
	    for (j = left; j <= right; ++j) {
273
		double weight =
274
		fproc((center - j) / fscale) / fscale;
275
		int n = clamp_pixel(j);
276
		int k = n - first_pixel;
277
 
278
		p[k].weight +=
279
		    (PixelWeight) (weight * scaled_factor);
280
	    }
281
	} else {
282
	    for (j = left; j <= right; ++j) {
283
		double weight = fproc(center - j);
284
		int n = clamp_pixel(j);
285
		int k = n - first_pixel;
286
 
287
		p[k].weight +=
288
		    (PixelWeight) (weight * scaled_factor);
289
	    }
290
	}
291
    }
292
    return last_index;
293
}
294
 
295
 
296
/* Apply filter to zoom horizontally from src to tmp. */
297
private void
298
zoom_x(PixelTmp * tmp, const void /*PixelIn */ *src, int sizeofPixelIn,
299
       int tmp_width, int WidthIn, int Colors, const CLIST * contrib,
300
       const CONTRIB * items)
301
{
302
    int c, i;
303
#ifdef NEED_FRACTION_BITS
304
    const int fraction_bits =
305
	(sizeofPixelIn - sizeof(PixelTmp)) * 8 + num_weight_bits;
306
#endif
307
 
308
    for (c = 0; c < Colors; ++c) {
309
	PixelTmp *tp = tmp + c;
310
	const CLIST *clp = contrib;
311
 
312
	if_debug1('W', "[W]zoom_x color %d:", c);
313
 
314
#define zoom_x_loop(PixelIn, PixelIn2)\
315
		const PixelIn *raster = (const PixelIn *)src + c;\
316
		for ( i = 0; i < tmp_width; tp += Colors, ++clp, ++i )\
317
		  {	AccumTmp weight = 0;\
318
			{ int j = clp->n;\
319
			  const PixelIn *pp = raster + clp->first_pixel;\
320
			  const CONTRIB *cp = items + clp->index;\
321
			  switch ( Colors )\
322
			  {\
323
			  case 1:\
324
			    for ( ; j > 0; pp += 1, ++cp, --j )\
325
			      weight += *pp * cp->weight;\
326
			    break;\
327
			  case 3:\
328
			    for ( ; j > 0; pp += 3, ++cp, --j )\
329
			      weight += *pp * cp->weight;\
330
			    break;\
331
			  default:\
332
			    for ( ; j > 0; pp += Colors, ++cp, --j )\
333
			      weight += *pp * cp->weight;\
334
			  }\
335
			}\
336
			{ PixelIn2 pixel = unscale_AccumTmp(weight, fraction_bits);\
337
			  if_debug1('W', " %ld", (long)pixel);\
338
			  *tp =\
339
			    (PixelTmp)CLAMP(pixel, minPixelTmp, maxPixelTmp);\
340
			}\
341
		  }
342
 
343
	if (sizeofPixelIn == 1) {
344
	    zoom_x_loop(byte, int)
345
	} else {		/* sizeofPixelIn == 2 */
346
#if arch_ints_are_short
347
	    zoom_x_loop(bits16, long)
348
#else
349
	    zoom_x_loop(bits16, int)
350
#endif
351
	}
352
	if_debug0('W', "\n");
353
    }
354
}
355
 
356
 
357
/*
358
 * Apply filter to zoom vertically from tmp to dst.
359
 * This is simpler because we can treat all columns identically
360
 * without regard to the number of samples per pixel.
361
 */
362
private void
363
zoom_y(void /*PixelOut */ *dst, int sizeofPixelOut, uint MaxValueOut,
364
       const PixelTmp * tmp, int WidthOut, int tmp_width,
365
       int Colors, const CLIST * contrib, const CONTRIB * items)
366
{
367
    int kn = WidthOut * Colors;
368
    int cn = contrib->n;
369
    int first_pixel = contrib->first_pixel;
370
    const CONTRIB *cbp = items + contrib->index;
371
    int kc;
372
    PixelTmp2 max_weight = MaxValueOut;
373
#ifdef NEED_FRACTION_BITS
374
    const int fraction_bits =
375
	(sizeof(PixelTmp) - sizeofPixelOut) * 8 + num_weight_bits;
376
#endif
377
 
378
    if_debug0('W', "[W]zoom_y: ");
379
 
380
#define zoom_y_loop(PixelOut)\
381
	for ( kc = 0; kc < kn; ++kc ) {\
382
		AccumTmp weight = 0;\
383
		{ const PixelTmp *pp = &tmp[kc + first_pixel];\
384
		  int j = cn;\
385
		  const CONTRIB *cp = cbp;\
386
		  for ( ; j > 0; pp += kn, ++cp, --j )\
387
		    weight += *pp * cp->weight;\
388
		}\
389
		{ PixelTmp2 pixel = unscale_AccumTmp(weight, fraction_bits);\
390
		  if_debug1('W', " %d", pixel);\
391
		  ((PixelOut *)dst)[kc] =\
392
		    (PixelOut)CLAMP(pixel, 0, max_weight);\
393
		}\
394
	}
395
 
396
    if (sizeofPixelOut == 1) {
397
	zoom_y_loop(byte)
398
    } else {			/* sizeofPixelOut == 2 */
399
	zoom_y_loop(bits16)
400
    }
401
    if_debug0('W', "\n");
402
}
403
 
404
/* ------ Stream implementation ------ */
405
 
406
#define tmp_width params.WidthOut
407
#define tmp_height params.HeightIn
408
 
409
/* Forward references */
410
private void s_IScale_release(stream_state * st);
411
 
412
/* Calculate the weights for an output row. */
413
private void
414
calculate_dst_contrib(stream_IScale_state * ss, int y)
415
{
416
    uint row_size = ss->params.WidthOut * ss->params.Colors;
417
    int last_index =
418
    calculate_contrib(&ss->dst_next_list, ss->dst_items, ss->yscale,
419
		      y, 1, ss->params.HeightIn, MAX_ISCALE_SUPPORT, row_size,
420
		      (double)ss->params.MaxValueOut / (fixedScaleFactor * unitPixelTmp) );
421
    int first_index_mod = ss->dst_next_list.first_pixel / row_size;
422
 
423
    ss->dst_last_index = last_index;
424
    last_index %= MAX_ISCALE_SUPPORT;
425
    if (last_index < first_index_mod) {		/* Shuffle the indices to account for wraparound. */
426
	CONTRIB shuffle[MAX_ISCALE_SUPPORT];
427
	int i;
428
 
429
	for (i = 0; i < MAX_ISCALE_SUPPORT; ++i)
430
	    shuffle[i].weight =
431
		(i <= last_index ?
432
		 ss->dst_items[i + MAX_ISCALE_SUPPORT - first_index_mod].weight :
433
		 i >= first_index_mod ?
434
		 ss->dst_items[i - first_index_mod].weight :
435
		 0);
436
	memcpy(ss->dst_items, shuffle, MAX_ISCALE_SUPPORT * sizeof(CONTRIB));
437
	ss->dst_next_list.n = MAX_ISCALE_SUPPORT;
438
	ss->dst_next_list.first_pixel = 0;
439
    }
440
#ifdef DEBUG
441
    if (gs_debug_c('w')) {
442
	dprintf1("[w]calc dest contrib for y = %d\n", y);
443
    }
444
#endif
445
}
446
 
447
/* Set default parameter values (actually, just clear pointers). */
448
private void
449
s_IScale_set_defaults(stream_state * st)
450
{
451
    stream_IScale_state *const ss = (stream_IScale_state *) st;
452
 
453
    ss->src = 0;
454
    ss->dst = 0;
455
    ss->tmp = 0;
456
    ss->contrib = 0;
457
    ss->items = 0;
458
}
459
 
460
/* Initialize the filter. */
461
private int
462
s_IScale_init(stream_state * st)
463
{
464
    stream_IScale_state *const ss = (stream_IScale_state *) st;
465
    gs_memory_t *mem = ss->memory;
466
 
467
    ss->sizeofPixelIn = ss->params.BitsPerComponentIn / 8;
468
    ss->sizeofPixelOut = ss->params.BitsPerComponentOut / 8;
469
    ss->xscale = (double)ss->params.WidthOut / (double)ss->params.WidthIn;
470
    ss->yscale = (double)ss->params.HeightOut / (double)ss->params.HeightIn;
471
 
472
    ss->src_y = 0;
473
    ss->src_size = ss->params.WidthIn * ss->sizeofPixelIn * ss->params.Colors;
474
    ss->src_offset = 0;
475
    ss->dst_y = 0;
476
    ss->dst_size = ss->params.WidthOut * ss->sizeofPixelOut * ss->params.Colors;
477
    ss->dst_offset = 0;
478
 
479
    /* create intermediate image to hold horizontal zoom */
480
    ss->tmp = (PixelTmp *) gs_alloc_byte_array(mem,
481
					   min(ss->tmp_height, MAX_ISCALE_SUPPORT),
482
			      ss->tmp_width * ss->params.Colors * sizeof(PixelTmp),
483
					       "image_scale tmp");
484
    ss->contrib = (CLIST *) gs_alloc_byte_array(mem,
485
					   max(ss->params.WidthOut, ss->params.HeightOut),
486
				      sizeof(CLIST), "image_scale contrib");
487
    ss->items = (CONTRIB *) gs_alloc_byte_array(mem,
488
				  contrib_pixels(ss->xscale) * ss->params.WidthOut,
489
				 sizeof(CONTRIB), "image_scale contrib[*]");
490
    /* Allocate buffers for 1 row of source and destination. */
491
    ss->dst = gs_alloc_byte_array(mem, ss->params.WidthOut * ss->params.Colors,
492
				  ss->sizeofPixelOut, "image_scale dst");
493
    ss->src = gs_alloc_byte_array(mem, ss->params.WidthIn * ss->params.Colors,
494
				  ss->sizeofPixelIn, "image_scale src");
495
    if (ss->tmp == 0 || ss->contrib == 0 || ss->items == 0 ||
496
	ss->dst == 0 || ss->src == 0
497
	) {
498
	s_IScale_release(st);
499
	return ERRC;
500
/****** WRONG ******/
501
    }
502
    /* Pre-calculate filter contributions for a row. */
503
    calculate_contrib(ss->contrib, ss->items, ss->xscale,
504
		      0, ss->params.WidthOut, ss->params.WidthIn, ss->params.WidthIn,
505
		      ss->params.Colors, (double)unitPixelTmp * fixedScaleFactor / ss->params.MaxValueIn);
506
 
507
    /* Prepare the weights for the first output row. */
508
    calculate_dst_contrib(ss, 0);
509
 
510
    return 0;
511
 
512
}
513
 
514
/* Process a buffer.  Note that this handles Encode and Decode identically. */
515
private int
516
s_IScale_process(stream_state * st, stream_cursor_read * pr,
517
		 stream_cursor_write * pw, bool last)
518
{
519
    stream_IScale_state *const ss = (stream_IScale_state *) st;
520
 
521
    /* Check whether we need to deliver any output. */
522
 
523
  top:while (ss->src_y > ss->dst_last_index) {	/* We have enough horizontally scaled temporary rows */
524
	/* to generate a vertically scaled output row. */
525
	uint wleft = pw->limit - pw->ptr;
526
 
527
	if (ss->dst_y == ss->params.HeightOut)
528
	    return EOFC;
529
	if (wleft == 0)
530
	    return 1;
531
	if (ss->dst_offset == 0) {
532
	    byte *row;
533
 
534
	    if (wleft >= ss->dst_size) {	/* We can scale the row directly into the output. */
535
		row = pw->ptr + 1;
536
		pw->ptr += ss->dst_size;
537
	    } else {		/* We'll have to buffer the row. */
538
		row = ss->dst;
539
	    }
540
	    /* Apply filter to zoom vertically from tmp to dst. */
541
	    zoom_y(row, ss->sizeofPixelOut, ss->params.MaxValueOut, ss->tmp,
542
		   ss->params.WidthOut, ss->tmp_width, ss->params.Colors,
543
		   &ss->dst_next_list, ss->dst_items);
544
	    /* Idiotic C coercion rules allow T* and void* to be */
545
	    /* inter-assigned freely, but not compared! */
546
	    if ((void *)row != ss->dst)		/* no buffering */
547
		goto adv;
548
	} {			/* We're delivering a buffered output row. */
549
	    uint wcount = ss->dst_size - ss->dst_offset;
550
	    uint ncopy = min(wleft, wcount);
551
 
552
	    memcpy(pw->ptr + 1, (byte *) ss->dst + ss->dst_offset, ncopy);
553
	    pw->ptr += ncopy;
554
	    ss->dst_offset += ncopy;
555
	    if (ncopy != wcount)
556
		return 1;
557
	    ss->dst_offset = 0;
558
	}
559
	/* Advance to the next output row. */
560
      adv:++(ss->dst_y);
561
	if (ss->dst_y != ss->params.HeightOut)
562
	    calculate_dst_contrib(ss, ss->dst_y);
563
    }
564
 
565
    /* Read input data and scale horizontally into tmp. */
566
 
567
    {
568
	uint rleft = pr->limit - pr->ptr;
569
	uint rcount = ss->src_size - ss->src_offset;
570
 
571
	if (rleft == 0)
572
	    return 0;		/* need more input */
573
#ifdef DEBUG
574
	assert(ss->src_y < ss->params.HeightIn);
575
#endif
576
	if (rleft >= rcount) {	/* We're going to fill up a row. */
577
	    const byte *row;
578
 
579
	    if (ss->src_offset == 0) {	/* We have a complete row.  Read the data */
580
		/* directly from the input. */
581
		row = pr->ptr + 1;
582
	    } else {		/* We're buffering a row in src. */
583
		row = ss->src;
584
		memcpy((byte *) ss->src + ss->src_offset, pr->ptr + 1,
585
		       rcount);
586
		ss->src_offset = 0;
587
	    }
588
	    /* Apply filter to zoom horizontally from src to tmp. */
589
	    if_debug2('w', "[w]zoom_x y = %d to tmp row %d\n",
590
		      ss->src_y, (ss->src_y % MAX_ISCALE_SUPPORT));
591
	    zoom_x(ss->tmp + (ss->src_y % MAX_ISCALE_SUPPORT) *
592
		   ss->tmp_width * ss->params.Colors, row,
593
		   ss->sizeofPixelIn, ss->tmp_width, ss->params.WidthIn,
594
		   ss->params.Colors, ss->contrib, ss->items);
595
	    pr->ptr += rcount;
596
	    ++(ss->src_y);
597
	    goto top;
598
	} else {		/* We don't have a complete row.  Copy data to src buffer. */
599
	    memcpy((byte *) ss->src + ss->src_offset, pr->ptr + 1, rleft);
600
	    ss->src_offset += rleft;
601
	    pr->ptr += rleft;
602
	    return 0;
603
	}
604
    }
605
}
606
 
607
/* Release the filter's storage. */
608
private void
609
s_IScale_release(stream_state * st)
610
{
611
    stream_IScale_state *const ss = (stream_IScale_state *) st;
612
    gs_memory_t *mem = ss->memory;
613
 
614
    gs_free_object(mem, (void *)ss->src, "image_scale src");	/* no longer const */
615
    ss->src = 0;
616
    gs_free_object(mem, ss->dst, "image_scale dst");
617
    ss->dst = 0;
618
    gs_free_object(mem, ss->items, "image_scale contrib[*]");
619
    ss->items = 0;
620
    gs_free_object(mem, ss->contrib, "image_scale contrib");
621
    ss->contrib = 0;
622
    gs_free_object(mem, ss->tmp, "image_scale tmp");
623
    ss->tmp = 0;
624
}
625
 
626
/* Stream template */
627
const stream_template s_IScale_template = {
628
    &st_IScale_state, s_IScale_init, s_IScale_process, 1, 1,
629
    s_IScale_release, s_IScale_set_defaults
630
};