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 /* Special Image downsample scaling filters for dithered devices */
18 #include "math_.h"
19 #include "memory_.h"
20 #include "stdio_.h"
21 #include "gdebug.h"
22 #include "gxfixed.h" /* for gxdda.h */
23 #include "gxdda.h"
24 #include "gxfrac.h"
25 #include "strimpl.h"
26 #include "sidscale.h"
27
28 /* Temporary intermediate values */
29 typedef byte PixelTmp;
30
31 #define minPixelTmp 0
32 #define maxPixelTmp 255
33 #define unitPixelTmp 255
34
35 /* Max of all pixel sizes */
36 #define maxSizeofPixel 2
37
38 /* Auxiliary structures. */
39
40 /* ImageSpecialDownScaleEncode / ImageSpecialDownScaleDecode */
41 typedef struct stream_ISpecialDownScale_state_s {
42 /* The client sets the params values before initialization. */
43 stream_image_scale_state_common; /* = state_common + params */
44 /* The init procedure sets the following. */
45 int sizeofPixelIn; /* bytes per input value, 1 or 2 */
46 int sizeofPixelOut; /* bytes per output value, 1 or 2 */
47 void /*PixelIn */ *src;
48 void /*PixelOut */ *dst;
49 void /*PixelIn */ *tmp;
50 gx_dda_int_t dda_x_init; /* initial setting of dda_x */
51 /* The following are updated dynamically. */
52 int dst_x;
53 uint dst_offset, dst_size;
54 gx_dda_int_t dda_x; /* DDA for dest X in current scan line */
55 int src_y;
56 uint src_offset, src_size;
57 int dst_y;
58 gx_dda_int_t dda_y; /* DDA for dest Y */
59 } stream_ISpecialDownScale_state;
60
61 gs_private_st_ptrs3(st_ISpecialDownScale_state, stream_ISpecialDownScale_state,
62 "ImageSpecialDownScaleEncode/Decode state",
63 isdscale_state_enum_ptrs, isdscale_state_reloc_ptrs,
64 dst, src, tmp);
65
66 /* Apply filter to downscale horizontally from src to tmp. */
67 static void
idownscale_x(void * tmp,const void * src,stream_ISpecialDownScale_state * const ss)68 idownscale_x(void /* PixelIn */ * tmp, const void /* PixelIn */ *src, stream_ISpecialDownScale_state *const ss)
69 {
70 int c, i;
71 int Colors = ss->params.spp_interp;
72 int WidthIn = ss->params.WidthIn;
73 int prev_y;
74 int cur_y;
75 bool firstline;
76 bool polarity_additive = ss->params.ColorPolarityAdditive;
77
78 dda_previous_assign(ss->dda_y, prev_y);
79 dda_next_assign(ss->dda_y, cur_y);
80 firstline = prev_y != cur_y; /* at the start of a new group of lines */
81 /* The following could be a macro, BUT macro's with control */
82 /* are not good style and hard to debug */
83 if (ss->sizeofPixelIn == 1) {
84 for (c = 0; c < Colors; ++c) {
85 byte *tp = (byte *)tmp + c; /* destination */
86 const byte *pp = (const byte *)src + c;
87
88 ss->dda_x = ss->dda_x_init;
89 if_debug1('W', "[W]idownscale_x color %d:", c);
90
91 for ( i = 0; i < WidthIn; tp += Colors) {
92 int endx;
93 dda_next_assign(ss->dda_x, endx);
94 if (firstline)
95 *tp = *pp;
96 else {
97 if ((polarity_additive && (*pp < *tp)) ||
98 (!polarity_additive && (*pp > *tp)) )
99 *tp = *pp;
100 }
101 i++; pp += Colors;
102 while (i < endx) {
103 if (*pp < *tp)
104 *tp = *pp;
105 i++; pp += Colors;
106 }
107 if_debug1('W', " %d", *tp);
108 }
109 if_debug0('W', "\n");
110 }
111 } else { /* sizeofPixelIn == 2 */
112 for (c = 0; c < Colors; ++c) {
113 bits16 *tp = (bits16 *)tmp + c; /* destination */
114 const bits16 *pp = (const bits16 *)src + c;
115
116 ss->dda_x = ss->dda_x_init;
117 if_debug1('W', "[W]idownscale_x color %d:", c);
118
119 for ( i = 0; i < WidthIn; tp += Colors) {
120 int endx;
121 dda_next_assign(ss->dda_x,endx);
122 if (firstline)
123 *tp = *pp;
124 else {
125 if ((polarity_additive && (*pp < *tp)) ||
126 (!polarity_additive && (*pp > *tp)) )
127 *tp = *pp;
128 }
129 i++; pp += Colors;
130 while (i < endx) {
131 if (*pp < *tp)
132 *tp = *pp;
133 i++; pp += Colors;
134 }
135 if_debug1('W', " %d", *tp);
136 }
137 if_debug0('W', "\n");
138 }
139 }
140 }
141
142 /*
143 * Copy from tmp to dst, taking into account PixelOut vs. PixelIn sizes
144 * We do the conversion from PixelIn to PixelOut here (if needed)
145 * since if the Y is scaled down we will convert less often.
146 * This is simpler because we can treat all columns identically
147 * without regard to the number of samples per pixel.
148 */
149 static void
idownscale_y(void * dst,const void * tmp,stream_ISpecialDownScale_state * const ss)150 idownscale_y(void /*PixelOut */ *dst, const void /* PixelIn */ *tmp,
151 stream_ISpecialDownScale_state *const ss)
152 {
153 int kn = ss->params.WidthOut * ss->params.spp_interp;
154 int kc;
155 float scale = (float) ss->params.MaxValueOut/255.0;
156
157 if_debug0('W', "[W]idownscale_y: ");
158
159 if (ss->sizeofPixelOut == 1) {
160 if (ss->sizeofPixelIn == 1) {
161 const byte *pp = (byte *)tmp;
162
163 for ( kc = 0; kc < kn; ++kc, pp++ ) {
164 if_debug1('W', " %d", *pp);
165 ((byte *)dst)[kc] = *pp;
166 }
167 } else { /* sizeofPixelIn == 2 */
168 const bits16 *pp = (bits16 *)tmp;
169
170 for ( kc = 0; kc < kn; ++kc, pp++ ) {
171 if_debug1('W', " %d", *pp);
172 ((byte *)dst)[kc] = frac2byte(*pp);
173 }
174 }
175 } else { /* sizeofPixelOut == 2 */
176 if (ss->sizeofPixelIn == 1) {
177 const byte *pp = (byte *)tmp;
178
179 for ( kc = 0; kc < kn; ++kc, pp++ ) {
180 if_debug1('W', " %d", *pp);
181 ((bits16 *)dst)[kc] = (bits16)((*pp)*scale);
182 }
183 } else { /* sizeofPixelIn == 2 */
184 const bits16 *pp = (bits16 *)tmp;
185
186 for ( kc = 0; kc < kn; ++kc, pp++ ) {
187 if_debug1('W', " %d", *pp);
188 ((bits16 *)dst)[kc] = *pp;
189 }
190 }
191 }
192 if_debug0('W', "n");
193 }
194
195 /* ------ Stream implementation ------ */
196
197 /* Forward references */
198 static void s_ISpecialDownScale_release(stream_state * st);
199
200 /* Set default parameter values (actually, just clear pointers). */
201 static void
s_ISpecialDownScale_set_defaults(stream_state * st)202 s_ISpecialDownScale_set_defaults(stream_state * st)
203 {
204 stream_ISpecialDownScale_state *const ss = (stream_ISpecialDownScale_state *) st;
205
206 ss->src = 0;
207 ss->dst = 0;
208 ss->tmp = 0;
209 }
210
211 /* Initialize the filter. */
212 static int
s_ISpecialDownScale_init(stream_state * st)213 s_ISpecialDownScale_init(stream_state * st)
214 {
215 stream_ISpecialDownScale_state *const ss = (stream_ISpecialDownScale_state *) st;
216 gs_memory_t *mem = ss->memory;
217
218 ss->sizeofPixelIn = ss->params.BitsPerComponentIn / 8;
219 ss->sizeofPixelOut = ss->params.BitsPerComponentOut / 8;
220
221 ss->src_size =
222 ss->params.WidthIn * ss->sizeofPixelIn * ss->params.spp_interp;
223 ss->dst_size =
224 ss->params.WidthOut * ss->sizeofPixelOut * ss->params.spp_interp;
225
226 /* Initialize destination DDAs. */
227 ss->dst_x = 0;
228 ss->src_offset = ss->dst_offset = 0;
229 dda_init(ss->dda_x, 0, ss->params.WidthIn, ss->params.WidthOut);
230 ss->dda_x_init = ss->dda_x;
231 ss->src_y = ss->dst_y = 0;
232 dda_init(ss->dda_y, 0, ss->params.HeightOut, ss->params.HeightIn);
233
234 /* create intermediate image to hold horizontal zoom */
235 ss->tmp =
236 gs_alloc_byte_array(mem, ss->params.WidthOut * ss->params.spp_interp,
237 ss->sizeofPixelIn, "image_scale tmp");
238 /* Allocate buffers for 1 row of source and destination. */
239 ss->dst =
240 gs_alloc_byte_array(mem, ss->params.WidthOut * ss->params.spp_interp,
241 ss->sizeofPixelOut, "image_scale dst");
242 ss->src =
243 gs_alloc_byte_array(mem, ss->params.WidthIn * ss->params.spp_interp,
244 ss->sizeofPixelIn, "image_scale src");
245 if (ss->tmp == 0 || ss->dst == 0 || ss->src == 0) {
246 s_ISpecialDownScale_release(st);
247 return ERRC;
248 /****** WRONG ******/
249 }
250
251 return 0;
252
253 }
254
255 /* Process a buffer. Note that this handles Encode and Decode identically. */
256 static int
s_ISpecialDownScale_process(stream_state * st,stream_cursor_read * pr,stream_cursor_write * pw,bool last)257 s_ISpecialDownScale_process(stream_state * st, stream_cursor_read * pr,
258 stream_cursor_write * pw, bool last)
259 {
260 stream_ISpecialDownScale_state *const ss = (stream_ISpecialDownScale_state *) st;
261 uint cur_y = dda_current(ss->dda_y);
262
263 /* Check whether we need to deliver any output. */
264
265 top:
266 if (cur_y > ss->dst_y) {
267 /* Deliver some or all of the current scaled row. */
268 /* to generate a vertically scaled output row. */
269 uint wleft = pw->limit - pw->ptr;
270
271 if (ss->dst_y == ss->params.HeightOut)
272 return EOFC;
273 if (wleft == 0)
274 return 1;
275 if (ss->dst_offset == 0) {
276 byte *row;
277
278 if (wleft >= ss->dst_size) { /* We can scale the row directly into the output. */
279 row = pw->ptr + 1;
280 pw->ptr += ss->dst_size;
281 } else { /* We'll have to buffer the row. */
282 row = ss->dst;
283 }
284 /* Apply filter to zoom vertically from tmp to dst. */
285 idownscale_y(row, ss->tmp, ss);
286 /* Idiotic C coercion rules allow T* and void* to be */
287 /* inter-assigned freely, but not compared! */
288 if ((void *)row != ss->dst) /* no buffering */
289 goto adv;
290 } { /* We're delivering a buffered output row. */
291 uint wcount = ss->dst_size - ss->dst_offset;
292 uint ncopy = min(wleft, wcount);
293
294 memcpy(pw->ptr + 1, (byte *) ss->dst + ss->dst_offset, ncopy);
295 pw->ptr += ncopy;
296 ss->dst_offset += ncopy;
297 if (ncopy != wcount)
298 return 1;
299 ss->dst_offset = 0;
300 }
301 /* Advance to the next output row. */
302 adv: ++(ss->dst_y);
303 }
304
305 /* Read input data and scale horizontally into tmp. */
306
307 {
308 uint rleft = pr->limit - pr->ptr;
309 uint rcount = ss->src_size - ss->src_offset;
310
311 if (rleft == 0)
312 return 0; /* need more input */
313 if (ss->src_y >= ss->params.HeightIn)
314 return ERRC;
315 if (rleft >= rcount) { /* We're going to fill up a row. */
316 const byte *row;
317
318 if (ss->src_offset == 0) { /* We have a complete row. Read the data */
319 /* directly from the input. */
320 row = pr->ptr + 1;
321 } else { /* We're buffering a row in src. */
322 row = ss->src;
323 memcpy((byte *) ss->src + ss->src_offset, pr->ptr + 1,
324 rcount);
325 ss->src_offset = 0;
326 }
327 /* Apply filter to zoom horizontally from src to tmp. */
328 if_debug2('w', "[w]idownscale_x y = %d to tmp row %d\n",
329 ss->src_y, (ss->src_y % MAX_ISCALE_SUPPORT));
330 idownscale_x(ss->tmp, row, ss);
331 pr->ptr += rcount;
332 ++(ss->src_y);
333 dda_next_assign(ss->dda_y,cur_y);
334 goto top;
335 } else { /* We don't have a complete row. Copy data to src buffer. */
336 memcpy((byte *) ss->src + ss->src_offset, pr->ptr + 1, rleft);
337 ss->src_offset += rleft;
338 pr->ptr += rleft;
339 return 0;
340 }
341 }
342 }
343
344 /* Release the filter's storage. */
345 static void
s_ISpecialDownScale_release(stream_state * st)346 s_ISpecialDownScale_release(stream_state * st)
347 {
348 stream_ISpecialDownScale_state *const ss = (stream_ISpecialDownScale_state *) st;
349 gs_memory_t *mem = ss->memory;
350
351 gs_free_object(mem, (void *)ss->src, "image_scale src"); /* no longer const */
352 ss->src = 0;
353 gs_free_object(mem, ss->dst, "image_scale dst");
354 ss->dst = 0;
355 gs_free_object(mem, ss->tmp, "image_scale tmp");
356 ss->tmp = 0;
357 }
358
359 /* Stream template */
360 const stream_template s_ISpecialDownScale_template = {
361 &st_ISpecialDownScale_state, s_ISpecialDownScale_init, s_ISpecialDownScale_process, 1, 1,
362 s_ISpecialDownScale_release, s_ISpecialDownScale_set_defaults
363 };
364