1 /* Copyright (C) 2001-2017 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 /* Image scaling filters */
17 #include "math_.h"
18 #include "memory_.h"
19 #include "stdio_.h"
20 #include "stdint_.h"
21 #include "gdebug.h"
22 #include "strimpl.h"
23 #include "siscale.h"
24 #include "gxfrac.h"
25 #include "cal.h"
26 #include "assert_.h"
27 
28 /* ImageScaleEncode / ImageScaleDecode */
29 typedef struct stream_IScale_cal_state_s {
30     /* The client sets the params values before initialization. */
31     stream_image_scale_state_common;  /* = state_common + params */
32     /* The init procedure sets the following. */
33     cal_rescaler *rescaler;
34     uint8_t *src;
35     uint8_t *dst;
36     byte *tmp;
37     int pre_scan_bytes;
38     int post_scan_bytes;
39     /* The following are updated dynamically. */
40     int src_y;
41     uint src_offset, src_size;
42     int dst_y;
43     uint dst_offset, dst_size;
44 } stream_IScale_cal_state;
45 
46 /* FIXME: */
47 gs_private_st_ptrs2(st_IScale_cal_state, stream_IScale_cal_state,
48     "ImageScaleEncode/Decode state",
49     iscale_state_enum_ptrs, iscale_state_reloc_ptrs,
50     dst, src);
51 
52 /* ------ Stream implementation ------ */
53 
54 /* Forward references */
55 static void s_IScale_cal_release(stream_state * st);
56 
57 /* Set default parameter values (actually, just clear pointers) */
58 static void
s_IScale_cal_set_defaults(stream_state * st)59 s_IScale_cal_set_defaults(stream_state * st)
60 {
61     stream_IScale_cal_state *const ss = (stream_IScale_cal_state *) st;
62 
63     ss->rescaler = NULL;
64 }
65 
66 /*
67 
68     Some notes:
69 
70     (ss->params.XXXX is shown as XXXX in the following for sanity)
71 
72     Conceptually we are scaling a bitmap that was EntireWidthIn x EntireHeightIn
73     in size, to be EntireWidthOut x EntireHeightOut in size.
74 
75     But, we only actually care about a sub rectangle of this in the destination,
76     given by (LeftMarginOut, TopMarginOut) + (PatchWidthOut, PatchHeightOut).
77     Anything else is clipped away. There are times when this sub rectangle can
78     be very "sub" indeed, so the ability to avoid rescaling all the data we
79     don't care about is a vital one.
80 
81     To confuse this further, we don't get fed scanlines of EntireWidthIn pixels,
82     instead we get WidthIn pixels. Similarly we don't feed scanlines out of
83     EntireWidthOut pixels, but rather of WidthOut pixels. Width{In,Out} are not
84     (always) the same as PatchWidth{In,Out} either.
85 
86     Accordingly there may be padding before and after the active region. We make
87     the effort to ensure these bytes are set to zero.
88 */
89 
90 
91 static int
s_IScale_cal_init(stream_state * st)92 s_IScale_cal_init(stream_state * st)
93 {
94     stream_IScale_cal_state *const ss = (stream_IScale_cal_state *) st;
95     gs_memory_t *mem = ss->memory;
96     int abs_interp_limit = ss->params.abs_interp_limit;
97     int limited_WidthOut = (ss->params.WidthOut + abs_interp_limit - 1) / abs_interp_limit;
98     int limited_PatchWidthOut = (ss->params.PatchWidthOut + abs_interp_limit - 1) / abs_interp_limit;
99     int limited_PatchHeightOut = (ss->params.PatchHeightOut2 + abs_interp_limit - 1) / abs_interp_limit;
100     int limited_EntireWidthOut = (ss->params.EntireWidthOut + abs_interp_limit - 1) / abs_interp_limit;
101     int limited_EntireHeightOut = (ss->params.EntireHeightOut + abs_interp_limit - 1) / abs_interp_limit;
102     int limited_LeftMarginOut = (ss->params.LeftMarginOut) / abs_interp_limit;
103     int limited_TopMarginOut = (ss->params.TopMarginOut2) / abs_interp_limit;
104     int limited_PadY = (ss->params.pad_y + abs_interp_limit/2 ) / abs_interp_limit;
105     int dst_bytes_per_pixel = ss->params.BitsPerComponentOut / 8;
106     int src_bytes_per_pixel = ss->params.BitsPerComponentIn / 8;
107 
108     ss->src_offset = 0;
109     ss->src_size = ss->params.WidthIn * ss->params.spp_interp * src_bytes_per_pixel;
110     ss->dst_offset = 0;
111     ss->dst_size = limited_WidthOut * ss->params.spp_interp * dst_bytes_per_pixel;
112     ss->dst_y = -limited_PadY;
113     ss->pre_scan_bytes = limited_LeftMarginOut * ss->params.spp_interp * dst_bytes_per_pixel;
114     ss->post_scan_bytes = (limited_WidthOut - limited_PatchWidthOut - limited_LeftMarginOut) * ss->params.spp_interp * dst_bytes_per_pixel;
115 
116     ss->dst = gs_alloc_byte_array(mem, ss->dst_size, 1, "image_scale dst");
117     if (ss->dst == NULL)
118         goto fail;
119 
120     ss->src = gs_alloc_byte_array(mem, ss->params.EntireWidthIn * ss->params.spp_interp,
121                                   src_bytes_per_pixel, "image_scale dst");
122     if (ss->src == NULL)
123         goto fail;
124 
125     ss->rescaler = cal_rescaler_init(mem->gs_lib_ctx->core->cal_ctx,
126                                      mem->non_gc_memory,
127                                      ss->params.EntireWidthIn,
128                                      ss->params.EntireHeightIn,
129                                      0,
130                                      ss->params.src_y_offset,
131                                      ss->params.WidthIn,
132                                      ss->params.HeightIn,
133                                      limited_EntireWidthOut,
134                                      limited_EntireHeightOut,
135                                      limited_LeftMarginOut,
136                                      limited_TopMarginOut,
137                                      limited_PatchWidthOut,
138                                      limited_PatchHeightOut,
139                                      CAL_MITCHELL,
140                                      src_bytes_per_pixel,
141                                      dst_bytes_per_pixel,
142                                      ss->params.spp_interp,
143                                      ss->params.MaxValueIn,
144                                      ss->params.MaxValueOut);
145     if (ss->rescaler == NULL)
146         goto fail;
147 
148     if (ss->pre_scan_bytes)
149         memset(ss->dst, 0, ss->pre_scan_bytes);
150     if (ss->post_scan_bytes)
151         memset(ss->dst + ss->dst_size - ss->post_scan_bytes, 0, ss->post_scan_bytes);
152 
153     return 0;
154 
155 fail:
156     if (ss->rescaler)
157         cal_rescaler_fin(ss->rescaler, mem->non_gc_memory);
158     gs_free_object(mem, ss->src, "image_scale src");
159     gs_free_object(mem, ss->dst, "image_scale dst");
160     return ERRC;
161 }
162 
163 /* Process a buffer.  Note that this handles Encode and Decode identically. */
164 static int
s_IScale_cal_process(stream_state * st,stream_cursor_read * pr,stream_cursor_write * pw,bool last)165 s_IScale_cal_process(stream_state * st, stream_cursor_read * pr,
166                      stream_cursor_write * pw, bool last)
167 {
168     stream_IScale_cal_state *const ss = (stream_IScale_cal_state *) st;
169     gs_memory_t *mem = ss->memory;
170     int abs_interp_limit = ss->params.abs_interp_limit;
171     int limited_HeightOut = (ss->params.HeightOut + abs_interp_limit - 1) / abs_interp_limit;
172     uint wleft;
173     uint rleft;
174     int any_output = 0;
175     const byte *input = NULL;
176 
177     /* If we have no more data to pull out, we're done. */
178     if (ss->dst_y == limited_HeightOut)
179         return EOFC;
180 
181     /* How much room do we have left in the output buffer? */
182     wleft = pw->limit - pw->ptr;
183 
184     /* If no room left, exit */
185     if (wleft == 0)
186         return 1;
187 
188     /* If we need to send some padding at the top, do so */
189     if (ss->dst_y < 0)
190     {
191         uint wcount = ss->dst_size - ss->dst_offset;
192         uint ncopy = wcount;
193 
194         if (ncopy > wleft)
195             ncopy = wleft;
196         memset(pw->ptr + 1, 0, ncopy);
197         pw->ptr += ncopy;
198         wcount -= ncopy;
199         if (wcount == 0)
200         {
201             ss->dst_offset = 0;
202             ss->dst_y++;
203         }
204         else
205             ss->dst_offset += ncopy;
206         wleft -= ncopy;
207         /* Unless we can get a whole new line out, pass out what we have */
208         if (wleft < ss->dst_size)
209             return 1;
210         any_output = 1;
211     }
212 
213     /* Pass out any buffered data we have */
214     if (ss->dst_offset != 0)
215     {
216         uint wcount = ss->dst_size - ss->dst_offset;
217         uint ncopy = wcount;
218 
219         if (ncopy > wleft)
220             ncopy = wleft;
221         memcpy(pw->ptr + 1, (byte *) ss->dst + ss->dst_offset, ncopy);
222         pw->ptr += ncopy;
223         wcount -= ncopy;
224         if (wcount == 0)
225         {
226             ss->dst_offset = 0;
227             ss->dst_y++;
228         }
229         else
230             ss->dst_offset += ncopy;
231         wleft -= ncopy;
232         /* Unless we can get a whole new line out, pass out what we have */
233         if (wleft < ss->dst_size)
234             return 1;
235         any_output = 1;
236     }
237 
238     /* How much data do we have in the incoming buffer? */
239     rleft = pr->limit - pr->ptr;
240     if (rleft > 0 && ss->src_offset > 0) {
241         /* We have part of a line buffered. Let's fill that out. */
242         uint ncopy = ss->src_size - ss->src_offset;
243         if (ncopy > rleft)
244             ncopy = rleft;
245         memcpy(ss->src + ss->src_offset, pr->ptr + 1, ncopy);
246         pr->ptr += ncopy;
247         rleft -= ncopy;
248         ss->src_offset += ncopy;
249         if (ss->src_offset == ss->src_size)
250         {
251             ss->src_offset = 0;
252             input = ss->src;
253         }
254     }
255     else if (rleft >= ss->src_size)
256         input = pr->ptr+1;
257 
258     /* If we can extract a whole extra output line, then try for that.
259      * Try anyway if we haven't managed to output anything. */
260     while (wleft >= ss->dst_size || any_output == 0)
261     {
262         uint8_t *row;
263         int ret;
264 
265         if (wleft >= ss->dst_size) {
266             /* We can scale the row directly into the output. */
267             row = pw->ptr + 1;
268         } else {
269             /* We'll have to buffer the row. */
270             row = ss->dst;
271         }
272         ret = cal_rescaler_process(ss->rescaler, mem, input, row + ss->pre_scan_bytes);
273         if (ret & 1)
274         {
275             /* Input consumed */
276             if (input != ss->src)
277             {
278                 pr->ptr += ss->src_size;
279                 rleft -= ss->src_size;
280             }
281             input = (rleft >= ss->src_size) ? pr->ptr+1 : NULL;
282             ss->src_y++;
283         }
284         if (ret & 2)
285         {
286             /* Output given */
287             if (row == ss->dst)
288             {
289                 /* Copy as much of the the buffered data out as
290                  * possible - we can't manage a whole line. */
291                 memcpy(pw->ptr+1, ss->dst, wleft);
292                 pw->ptr += wleft;
293                 ss->dst_offset = wleft;
294                 return 1;
295             }
296             else
297             {
298                 /* We are scaling direct into the output. Clear any pre and
299                  * post sections for neatness. */
300                 if (ss->pre_scan_bytes != 0)
301                     memset(row, 0, ss->pre_scan_bytes);
302                 if (ss->post_scan_bytes != 0)
303                     memset(row + ss->dst_size - ss->post_scan_bytes, 0, ss->post_scan_bytes);
304             }
305             pw->ptr += ss->dst_size;
306             ss->dst_y++;
307             wleft -= ss->dst_size;
308             any_output = 1;
309         }
310         /* If nothing happened, nothing to be gained from calling again */
311         if (ret == 0)
312             break;
313     }
314 
315     if (any_output == 0 && rleft > 0)
316     {
317         /* We've not managed to output anything, so the rescaler
318          * must be waiting for input data. If we had a whole line of
319          * data, we'd have tried to pass it in above. So we must
320          * have only the start of a line. */
321         assert(rleft < ss->src_size && ss->dst_offset == 0);
322         memcpy(ss->src, pr->ptr + 1, rleft);
323         ss->src_offset += rleft;
324         pr->ptr += rleft;
325         return 1;
326     }
327 
328     return any_output;
329 }
330 
331 /* Release the filter's storage. */
332 static void
s_IScale_cal_release(stream_state * st)333 s_IScale_cal_release(stream_state * st)
334 {
335     stream_IScale_cal_state *const ss = (stream_IScale_cal_state *) st;
336     gs_memory_t *mem = ss->memory;
337 
338     if (ss->rescaler)
339     {
340         cal_rescaler_fin(ss->rescaler, mem->non_gc_memory);
341         ss->rescaler = NULL;
342     }
343     gs_free_object(mem, ss->src, "image_scale src");
344     ss->src = NULL;
345     gs_free_object(mem, ss->dst, "image_scale dst");
346     ss->dst = NULL;
347 }
348 
349 /* Stream template */
350 const stream_template s_IScale_template = {
351     &st_IScale_cal_state, s_IScale_cal_init, s_IScale_cal_process, 1, 1,
352     s_IScale_cal_release, s_IScale_cal_set_defaults
353 };
354