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