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