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