1 /* Copyright (C) 2001-2019 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., 1305 Grant Avenue - Suite 200, Novato,
13 CA 94945, 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_debug1m('W', ss->memory, "[W]idownscale_x color %d:", c);
90
91 if (polarity_additive) {
92 for ( i = 0; i < WidthIn; tp += Colors) {
93 int endx;
94 dda_next_assign(ss->dda_x, endx);
95 if (firstline || *pp < *tp)
96 *tp = *pp;
97 i++; pp += Colors;
98 while (i < endx) {
99 if (*pp < *tp)
100 *tp = *pp;
101 i++; pp += Colors;
102 }
103 if_debug1m('W', ss->memory, " %d", *tp);
104 }
105 } else {
106 for ( i = 0; i < WidthIn; tp += Colors) {
107 int endx;
108 dda_next_assign(ss->dda_x, endx);
109 if (firstline || *pp > *tp)
110 *tp = *pp;
111 i++; pp += Colors;
112 while (i < endx) {
113 if (*pp > *tp)
114 *tp = *pp;
115 i++; pp += Colors;
116 }
117 if_debug1m('W', ss->memory, " %d", *tp);
118 }
119 }
120 if_debug0m('W', ss->memory, "\n");
121 }
122 } else { /* sizeofPixelIn == 2 */
123 for (c = 0; c < Colors; ++c) {
124 bits16 *tp = (bits16 *)tmp + c; /* destination */
125 const bits16 *pp = (const bits16 *)src + c;
126
127 ss->dda_x = ss->dda_x_init;
128 if_debug1m('W', ss->memory, "[W]idownscale_x color %d:", c);
129
130 if (polarity_additive) {
131 for ( i = 0; i < WidthIn; tp += Colors) {
132 int endx;
133 dda_next_assign(ss->dda_x,endx);
134 if (firstline || *pp < *tp)
135 *tp = *pp;
136 i++; pp += Colors;
137 while (i < endx) {
138 if (*pp < *tp)
139 *tp = *pp;
140 i++; pp += Colors;
141 }
142 if_debug1m('W', ss->memory, " %d", *tp);
143 }
144 } else {
145 for ( i = 0; i < WidthIn; tp += Colors) {
146 int endx;
147 dda_next_assign(ss->dda_x,endx);
148 if (firstline || *pp > *tp)
149 *tp = *pp;
150 i++; pp += Colors;
151 while (i < endx) {
152 if (*pp > *tp)
153 *tp = *pp;
154 i++; pp += Colors;
155 }
156 if_debug1m('W', ss->memory, " %d", *tp);
157 }
158 }
159 if_debug0m('W', ss->memory, "\n");
160 }
161 }
162 }
163
164 /*
165 * Copy from tmp to dst, taking into account PixelOut vs. PixelIn sizes
166 * We do the conversion from PixelIn to PixelOut here (if needed)
167 * since if the Y is scaled down we will convert less often.
168 * This is simpler because we can treat all columns identically
169 * without regard to the number of samples per pixel.
170 */
171 static void
idownscale_y(void * dst,const void * tmp,stream_ISpecialDownScale_state * const ss)172 idownscale_y(void /*PixelOut */ *dst, const void /* PixelIn */ *tmp,
173 stream_ISpecialDownScale_state *const ss)
174 {
175 int kn = ss->params.WidthOut * ss->params.spp_interp;
176 int kc;
177 float scale = (float) ss->params.MaxValueOut/255.0;
178
179 if_debug0m('W', ss->memory, "[W]idownscale_y: ");
180
181 if (ss->sizeofPixelOut == 1) {
182 if (ss->sizeofPixelIn == 1) {
183 const byte *pp = (byte *)tmp;
184
185 for ( kc = 0; kc < kn; ++kc, pp++ ) {
186 if_debug1m('W', ss->memory, " %d", *pp);
187 ((byte *)dst)[kc] = *pp;
188 }
189 } else { /* sizeofPixelIn == 2 */
190 const bits16 *pp = (bits16 *)tmp;
191
192 for ( kc = 0; kc < kn; ++kc, pp++ ) {
193 if_debug1m('W', ss->memory, " %d", *pp);
194 ((byte *)dst)[kc] = frac2byte(*pp);
195 }
196 }
197 } else { /* sizeofPixelOut == 2 */
198 if (ss->sizeofPixelIn == 1) {
199 const byte *pp = (byte *)tmp;
200
201 for ( kc = 0; kc < kn; ++kc, pp++ ) {
202 if_debug1m('W', ss->memory, " %d", *pp);
203 ((bits16 *)dst)[kc] = (bits16)((*pp)*scale);
204 }
205 } else { /* sizeofPixelIn == 2 */
206 const bits16 *pp = (bits16 *)tmp;
207
208 for ( kc = 0; kc < kn; ++kc, pp++ ) {
209 if_debug1m('W', ss->memory, " %d", *pp);
210 ((bits16 *)dst)[kc] = *pp;
211 }
212 }
213 }
214 if_debug0m('W', ss->memory, "n");
215 }
216
217 /* ------ Stream implementation ------ */
218
219 /* Forward references */
220 static void s_ISpecialDownScale_release(stream_state * st);
221
222 /* Set default parameter values (actually, just clear pointers). */
223 static void
s_ISpecialDownScale_set_defaults(stream_state * st)224 s_ISpecialDownScale_set_defaults(stream_state * st)
225 {
226 stream_ISpecialDownScale_state *const ss = (stream_ISpecialDownScale_state *) st;
227
228 ss->src = 0;
229 ss->dst = 0;
230 ss->tmp = 0;
231 }
232
233 /* Initialize the filter. */
234 static int
s_ISpecialDownScale_init(stream_state * st)235 s_ISpecialDownScale_init(stream_state * st)
236 {
237 stream_ISpecialDownScale_state *const ss = (stream_ISpecialDownScale_state *) st;
238 gs_memory_t *mem = ss->memory;
239
240 ss->sizeofPixelIn = ss->params.BitsPerComponentIn / 8;
241 ss->sizeofPixelOut = ss->params.BitsPerComponentOut / 8;
242
243 ss->src_size =
244 ss->params.WidthIn * ss->sizeofPixelIn * ss->params.spp_interp;
245 ss->dst_size =
246 ss->params.WidthOut * ss->sizeofPixelOut * ss->params.spp_interp;
247
248 /* Initialize destination DDAs. */
249 ss->dst_x = 0;
250 ss->src_offset = ss->dst_offset = 0;
251 dda_init(ss->dda_x, 0, ss->params.WidthIn, ss->params.WidthOut);
252 ss->dda_x_init = ss->dda_x;
253 ss->src_y = ss->dst_y = 0;
254 dda_init(ss->dda_y, 0, ss->params.HeightOut, ss->params.HeightIn);
255
256 /* create intermediate image to hold horizontal zoom */
257 ss->tmp =
258 gs_alloc_byte_array(mem, ss->params.WidthOut * ss->params.spp_interp,
259 ss->sizeofPixelIn, "image_scale tmp");
260 /* Allocate buffers for 1 row of source and destination. */
261 ss->dst =
262 gs_alloc_byte_array(mem, ss->params.WidthOut * ss->params.spp_interp,
263 ss->sizeofPixelOut, "image_scale dst");
264 ss->src =
265 gs_alloc_byte_array(mem, ss->params.WidthIn * ss->params.spp_interp,
266 ss->sizeofPixelIn, "image_scale src");
267 if (ss->tmp == 0 || ss->dst == 0 || ss->src == 0) {
268 s_ISpecialDownScale_release(st);
269 return ERRC;
270 /****** WRONG ******/
271 }
272
273 return 0;
274
275 }
276
277 /* Process a buffer. Note that this handles Encode and Decode identically. */
278 static int
s_ISpecialDownScale_process(stream_state * st,stream_cursor_read * pr,stream_cursor_write * pw,bool last)279 s_ISpecialDownScale_process(stream_state * st, stream_cursor_read * pr,
280 stream_cursor_write * pw, bool last)
281 {
282 stream_ISpecialDownScale_state *const ss = (stream_ISpecialDownScale_state *) st;
283 uint cur_y = dda_current(ss->dda_y);
284
285 /* Check whether we need to deliver any output. */
286
287 top:
288 ss->params.Active = (ss->src_y >= ss->params.TopMarginIn &&
289 ss->src_y <= ss->params.TopMarginIn + ss->params.PatchHeightIn);
290
291 if (cur_y > ss->dst_y) {
292 /* Deliver some or all of the current scaled row. */
293 /* to generate a vertically scaled output row. */
294 uint wleft = pw->limit - pw->ptr;
295
296 if (ss->dst_y == ss->params.HeightOut)
297 return EOFC;
298 if (wleft == 0)
299 return 1;
300 if (ss->dst_offset == 0) {
301 byte *row;
302
303 if (wleft >= ss->dst_size) { /* We can scale the row directly into the output. */
304 row = pw->ptr + 1;
305 pw->ptr += ss->dst_size;
306 } else { /* We'll have to buffer the row. */
307 row = ss->dst;
308 }
309 /* Apply filter to zoom vertically from tmp to dst. */
310 if (ss->params.Active)
311 idownscale_y(row, ss->tmp, ss);
312 /* Idiotic C coercion rules allow T* and void* to be */
313 /* inter-assigned freely, but not compared! */
314 if ((void *)row != ss->dst) /* no buffering */
315 goto adv;
316 } { /* We're delivering a buffered output row. */
317 uint wcount = ss->dst_size - ss->dst_offset;
318 uint ncopy = min(wleft, wcount);
319
320 if (ss->params.Active)
321 memcpy(pw->ptr + 1, (byte *) ss->dst + ss->dst_offset, ncopy);
322 pw->ptr += ncopy;
323 ss->dst_offset += ncopy;
324 if (ncopy != wcount)
325 return 1;
326 ss->dst_offset = 0;
327 }
328 /* Advance to the next output row. */
329 adv: ++(ss->dst_y);
330 }
331
332 /* Read input data and scale horizontally into tmp. */
333
334 {
335 uint rleft = pr->limit - pr->ptr;
336 uint rcount = ss->src_size - ss->src_offset;
337
338 if (rleft == 0)
339 return 0; /* need more input */
340 if (ss->src_y >= ss->params.HeightIn)
341 return ERRC;
342 if (rleft >= rcount) { /* We're going to fill up a row. */
343 const byte *row;
344
345 if (ss->src_offset == 0) { /* We have a complete row. Read the data */
346 /* directly from the input. */
347 row = pr->ptr + 1;
348 } else { /* We're buffering a row in src. */
349 row = ss->src;
350 if (ss->params.Active)
351 memcpy((byte *) ss->src + ss->src_offset, pr->ptr + 1,
352 rcount);
353 ss->src_offset = 0;
354 }
355 /* Apply filter to zoom horizontally from src to tmp. */
356 if_debug2m('w', ss->memory, "[w]idownscale_x y = %d to tmp row %d\n",
357 ss->src_y, (ss->src_y % MAX_ISCALE_SUPPORT));
358 if (ss->params.Active)
359 idownscale_x(ss->tmp, row, ss);
360 pr->ptr += rcount;
361 ++(ss->src_y);
362 dda_next_assign(ss->dda_y,cur_y);
363 goto top;
364 } else { /* We don't have a complete row. Copy data to src buffer. */
365 if (ss->params.Active)
366 memcpy((byte *) ss->src + ss->src_offset, pr->ptr + 1, rleft);
367 ss->src_offset += rleft;
368 pr->ptr += rleft;
369 return 0;
370 }
371 }
372 }
373
374 /* Release the filter's storage. */
375 static void
s_ISpecialDownScale_release(stream_state * st)376 s_ISpecialDownScale_release(stream_state * st)
377 {
378 stream_ISpecialDownScale_state *const ss = (stream_ISpecialDownScale_state *) st;
379 gs_memory_t *mem = ss->memory;
380
381 gs_free_object(mem, (void *)ss->src, "image_scale src"); /* no longer const */
382 ss->src = 0;
383 gs_free_object(mem, ss->dst, "image_scale dst");
384 ss->dst = 0;
385 gs_free_object(mem, ss->tmp, "image_scale tmp");
386 ss->tmp = 0;
387 }
388
389 /* Stream template */
390 const stream_template s_ISpecialDownScale_template = {
391 &st_ISpecialDownScale_state, s_ISpecialDownScale_init, s_ISpecialDownScale_process, 1, 1,
392 s_ISpecialDownScale_release, s_ISpecialDownScale_set_defaults
393 };
394