/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 | | }; |