/src/ghostpdl/base/sidscale.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Copyright (C) 2001-2023 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., 39 Mesa Street, Suite 108A, San Francisco, |
13 | | CA 94129, USA, 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 |
68 | | idownscale_x(void /* PixelIn */ * tmp, const void /* PixelIn */ *src, stream_ISpecialDownScale_state *const ss) |
69 | 0 | { |
70 | 0 | int c, i; |
71 | 0 | int Colors = ss->params.spp_interp; |
72 | 0 | int WidthIn = ss->params.WidthIn; |
73 | 0 | int prev_y; |
74 | 0 | int cur_y; |
75 | 0 | bool firstline; |
76 | 0 | bool polarity_additive = ss->params.ColorPolarityAdditive; |
77 | |
|
78 | 0 | dda_previous_assign(ss->dda_y, prev_y); |
79 | 0 | dda_next_assign(ss->dda_y, cur_y); |
80 | 0 | 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 | 0 | if (ss->sizeofPixelIn == 1) { |
84 | 0 | for (c = 0; c < Colors; ++c) { |
85 | 0 | byte *tp = (byte *)tmp + c; /* destination */ |
86 | 0 | const byte *pp = (const byte *)src + c; |
87 | |
|
88 | 0 | ss->dda_x = ss->dda_x_init; |
89 | 0 | if_debug1m('W', ss->memory, "[W]idownscale_x color %d:", c); |
90 | |
|
91 | 0 | if (polarity_additive) { |
92 | 0 | for ( i = 0; i < WidthIn; tp += Colors) { |
93 | 0 | int endx; |
94 | 0 | dda_next_assign(ss->dda_x, endx); |
95 | 0 | if (firstline || *pp < *tp) |
96 | 0 | *tp = *pp; |
97 | 0 | i++; pp += Colors; |
98 | 0 | while (i < endx) { |
99 | 0 | if (*pp < *tp) |
100 | 0 | *tp = *pp; |
101 | 0 | i++; pp += Colors; |
102 | 0 | } |
103 | 0 | if_debug1m('W', ss->memory, " %d", *tp); |
104 | 0 | } |
105 | 0 | } else { |
106 | 0 | for ( i = 0; i < WidthIn; tp += Colors) { |
107 | 0 | int endx; |
108 | 0 | dda_next_assign(ss->dda_x, endx); |
109 | 0 | if (firstline || *pp > *tp) |
110 | 0 | *tp = *pp; |
111 | 0 | i++; pp += Colors; |
112 | 0 | while (i < endx) { |
113 | 0 | if (*pp > *tp) |
114 | 0 | *tp = *pp; |
115 | 0 | i++; pp += Colors; |
116 | 0 | } |
117 | 0 | if_debug1m('W', ss->memory, " %d", *tp); |
118 | 0 | } |
119 | 0 | } |
120 | 0 | if_debug0m('W', ss->memory, "\n"); |
121 | 0 | } |
122 | 0 | } else { /* sizeofPixelIn == 2 */ |
123 | 0 | for (c = 0; c < Colors; ++c) { |
124 | 0 | bits16 *tp = (bits16 *)tmp + c; /* destination */ |
125 | 0 | const bits16 *pp = (const bits16 *)src + c; |
126 | |
|
127 | 0 | ss->dda_x = ss->dda_x_init; |
128 | 0 | if_debug1m('W', ss->memory, "[W]idownscale_x color %d:", c); |
129 | |
|
130 | 0 | if (polarity_additive) { |
131 | 0 | for ( i = 0; i < WidthIn; tp += Colors) { |
132 | 0 | int endx; |
133 | 0 | dda_next_assign(ss->dda_x,endx); |
134 | 0 | if (firstline || *pp < *tp) |
135 | 0 | *tp = *pp; |
136 | 0 | i++; pp += Colors; |
137 | 0 | while (i < endx) { |
138 | 0 | if (*pp < *tp) |
139 | 0 | *tp = *pp; |
140 | 0 | i++; pp += Colors; |
141 | 0 | } |
142 | 0 | if_debug1m('W', ss->memory, " %d", *tp); |
143 | 0 | } |
144 | 0 | } else { |
145 | 0 | for ( i = 0; i < WidthIn; tp += Colors) { |
146 | 0 | int endx; |
147 | 0 | dda_next_assign(ss->dda_x,endx); |
148 | 0 | if (firstline || *pp > *tp) |
149 | 0 | *tp = *pp; |
150 | 0 | i++; pp += Colors; |
151 | 0 | while (i < endx) { |
152 | 0 | if (*pp > *tp) |
153 | 0 | *tp = *pp; |
154 | 0 | i++; pp += Colors; |
155 | 0 | } |
156 | 0 | if_debug1m('W', ss->memory, " %d", *tp); |
157 | 0 | } |
158 | 0 | } |
159 | 0 | if_debug0m('W', ss->memory, "\n"); |
160 | 0 | } |
161 | 0 | } |
162 | 0 | } |
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 |
172 | | idownscale_y(void /*PixelOut */ *dst, const void /* PixelIn */ *tmp, |
173 | | stream_ISpecialDownScale_state *const ss) |
174 | 0 | { |
175 | 0 | int kn = ss->params.WidthOut * ss->params.spp_interp; |
176 | 0 | int kc; |
177 | 0 | float scale = (float) ss->params.MaxValueOut/255.0; |
178 | |
|
179 | 0 | if_debug0m('W', ss->memory, "[W]idownscale_y: "); |
180 | |
|
181 | 0 | if (ss->sizeofPixelOut == 1) { |
182 | 0 | if (ss->sizeofPixelIn == 1) { |
183 | 0 | const byte *pp = (byte *)tmp; |
184 | |
|
185 | 0 | for ( kc = 0; kc < kn; ++kc, pp++ ) { |
186 | 0 | if_debug1m('W', ss->memory, " %d", *pp); |
187 | 0 | ((byte *)dst)[kc] = *pp; |
188 | 0 | } |
189 | 0 | } else { /* sizeofPixelIn == 2 */ |
190 | 0 | const bits16 *pp = (bits16 *)tmp; |
191 | |
|
192 | 0 | for ( kc = 0; kc < kn; ++kc, pp++ ) { |
193 | 0 | if_debug1m('W', ss->memory, " %d", *pp); |
194 | 0 | ((byte *)dst)[kc] = frac2byte(*pp); |
195 | 0 | } |
196 | 0 | } |
197 | 0 | } else { /* sizeofPixelOut == 2 */ |
198 | 0 | if (ss->sizeofPixelIn == 1) { |
199 | 0 | const byte *pp = (byte *)tmp; |
200 | |
|
201 | 0 | for ( kc = 0; kc < kn; ++kc, pp++ ) { |
202 | 0 | if_debug1m('W', ss->memory, " %d", *pp); |
203 | 0 | ((bits16 *)dst)[kc] = (bits16)((*pp)*scale); |
204 | 0 | } |
205 | 0 | } else { /* sizeofPixelIn == 2 */ |
206 | 0 | const bits16 *pp = (bits16 *)tmp; |
207 | |
|
208 | 0 | for ( kc = 0; kc < kn; ++kc, pp++ ) { |
209 | 0 | if_debug1m('W', ss->memory, " %d", *pp); |
210 | 0 | ((bits16 *)dst)[kc] = *pp; |
211 | 0 | } |
212 | 0 | } |
213 | 0 | } |
214 | 0 | if_debug0m('W', ss->memory, "n"); |
215 | 0 | } |
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 |
224 | | s_ISpecialDownScale_set_defaults(stream_state * st) |
225 | 0 | { |
226 | 0 | stream_ISpecialDownScale_state *const ss = (stream_ISpecialDownScale_state *) st; |
227 | |
|
228 | 0 | ss->src = 0; |
229 | 0 | ss->dst = 0; |
230 | 0 | ss->tmp = 0; |
231 | 0 | } |
232 | | |
233 | | /* Initialize the filter. */ |
234 | | static int |
235 | | s_ISpecialDownScale_init(stream_state * st) |
236 | 0 | { |
237 | 0 | stream_ISpecialDownScale_state *const ss = (stream_ISpecialDownScale_state *) st; |
238 | 0 | gs_memory_t *mem = ss->memory; |
239 | |
|
240 | 0 | ss->sizeofPixelIn = ss->params.BitsPerComponentIn / 8; |
241 | 0 | ss->sizeofPixelOut = ss->params.BitsPerComponentOut / 8; |
242 | |
|
243 | 0 | ss->src_size = |
244 | 0 | ss->params.WidthIn * ss->sizeofPixelIn * ss->params.spp_interp; |
245 | 0 | ss->dst_size = |
246 | 0 | ss->params.WidthOut * ss->sizeofPixelOut * ss->params.spp_interp; |
247 | | |
248 | | /* Initialize destination DDAs. */ |
249 | 0 | ss->dst_x = 0; |
250 | 0 | ss->src_offset = ss->dst_offset = 0; |
251 | 0 | dda_init(ss->dda_x, 0, ss->params.WidthIn, ss->params.WidthOut); |
252 | 0 | ss->dda_x_init = ss->dda_x; |
253 | 0 | ss->src_y = ss->dst_y = 0; |
254 | 0 | dda_init(ss->dda_y, 0, ss->params.HeightOut, ss->params.HeightIn); |
255 | | |
256 | | /* create intermediate image to hold horizontal zoom */ |
257 | 0 | ss->tmp = |
258 | 0 | gs_alloc_byte_array(mem, (size_t)ss->params.WidthOut * ss->params.spp_interp, |
259 | 0 | ss->sizeofPixelIn, "image_scale tmp"); |
260 | | /* Allocate buffers for 1 row of source and destination. */ |
261 | 0 | ss->dst = |
262 | 0 | gs_alloc_byte_array(mem, (size_t)ss->params.WidthOut * ss->params.spp_interp, |
263 | 0 | ss->sizeofPixelOut, "image_scale dst"); |
264 | 0 | ss->src = |
265 | 0 | gs_alloc_byte_array(mem, (size_t)ss->params.WidthIn * ss->params.spp_interp, |
266 | 0 | ss->sizeofPixelIn, "image_scale src"); |
267 | 0 | if (ss->tmp == 0 || ss->dst == 0 || ss->src == 0) { |
268 | 0 | s_ISpecialDownScale_release(st); |
269 | 0 | return ERRC; |
270 | | /****** WRONG ******/ |
271 | 0 | } |
272 | | |
273 | 0 | return 0; |
274 | |
|
275 | 0 | } |
276 | | |
277 | | /* Process a buffer. Note that this handles Encode and Decode identically. */ |
278 | | static int |
279 | | s_ISpecialDownScale_process(stream_state * st, stream_cursor_read * pr, |
280 | | stream_cursor_write * pw, bool last) |
281 | 0 | { |
282 | 0 | stream_ISpecialDownScale_state *const ss = (stream_ISpecialDownScale_state *) st; |
283 | 0 | uint cur_y = dda_current(ss->dda_y); |
284 | | |
285 | | /* Check whether we need to deliver any output. */ |
286 | |
|
287 | 0 | top: |
288 | 0 | ss->params.Active = (ss->src_y >= ss->params.TopMarginIn && |
289 | 0 | ss->src_y <= ss->params.TopMarginIn + ss->params.PatchHeightIn); |
290 | |
|
291 | 0 | 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 | 0 | uint wleft = pw->limit - pw->ptr; |
295 | |
|
296 | 0 | if (ss->dst_y == ss->params.HeightOut) |
297 | 0 | return EOFC; |
298 | 0 | if (wleft == 0) |
299 | 0 | return 1; |
300 | 0 | if (ss->dst_offset == 0) { |
301 | 0 | byte *row; |
302 | |
|
303 | 0 | if (wleft >= ss->dst_size) { /* We can scale the row directly into the output. */ |
304 | 0 | row = pw->ptr + 1; |
305 | 0 | pw->ptr += ss->dst_size; |
306 | 0 | } else { /* We'll have to buffer the row. */ |
307 | 0 | row = ss->dst; |
308 | 0 | } |
309 | | /* Apply filter to zoom vertically from tmp to dst. */ |
310 | 0 | if (ss->params.Active) |
311 | 0 | 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 | 0 | if ((void *)row != ss->dst) /* no buffering */ |
315 | 0 | goto adv; |
316 | 0 | } { /* We're delivering a buffered output row. */ |
317 | 0 | uint wcount = ss->dst_size - ss->dst_offset; |
318 | 0 | uint ncopy = min(wleft, wcount); |
319 | |
|
320 | 0 | if (ss->params.Active) |
321 | 0 | memcpy(pw->ptr + 1, (byte *) ss->dst + ss->dst_offset, ncopy); |
322 | 0 | pw->ptr += ncopy; |
323 | 0 | ss->dst_offset += ncopy; |
324 | 0 | if (ncopy != wcount) |
325 | 0 | return 1; |
326 | 0 | ss->dst_offset = 0; |
327 | 0 | } |
328 | | /* Advance to the next output row. */ |
329 | 0 | adv: ++(ss->dst_y); |
330 | 0 | } |
331 | | |
332 | | /* Read input data and scale horizontally into tmp. */ |
333 | | |
334 | 0 | { |
335 | 0 | uint rleft = pr->limit - pr->ptr; |
336 | 0 | uint rcount = ss->src_size - ss->src_offset; |
337 | |
|
338 | 0 | if (rleft == 0) |
339 | 0 | return 0; /* need more input */ |
340 | 0 | if (ss->src_y >= ss->params.HeightIn) |
341 | 0 | return ERRC; |
342 | 0 | if (rleft >= rcount) { /* We're going to fill up a row. */ |
343 | 0 | const byte *row; |
344 | |
|
345 | 0 | if (ss->src_offset == 0) { /* We have a complete row. Read the data */ |
346 | | /* directly from the input. */ |
347 | 0 | row = pr->ptr + 1; |
348 | 0 | } else { /* We're buffering a row in src. */ |
349 | 0 | row = ss->src; |
350 | 0 | if (ss->params.Active) |
351 | 0 | memcpy((byte *) ss->src + ss->src_offset, pr->ptr + 1, |
352 | 0 | rcount); |
353 | 0 | ss->src_offset = 0; |
354 | 0 | } |
355 | | /* Apply filter to zoom horizontally from src to tmp. */ |
356 | 0 | if_debug2m('w', ss->memory, "[w]idownscale_x y = %d to tmp row %d\n", |
357 | 0 | ss->src_y, (ss->src_y % MAX_ISCALE_SUPPORT)); |
358 | 0 | if (ss->params.Active) |
359 | 0 | idownscale_x(ss->tmp, row, ss); |
360 | 0 | pr->ptr += rcount; |
361 | 0 | ++(ss->src_y); |
362 | 0 | dda_next_assign(ss->dda_y,cur_y); |
363 | 0 | goto top; |
364 | 0 | } else { /* We don't have a complete row. Copy data to src buffer. */ |
365 | 0 | if (ss->params.Active) |
366 | 0 | memcpy((byte *) ss->src + ss->src_offset, pr->ptr + 1, rleft); |
367 | 0 | ss->src_offset += rleft; |
368 | 0 | pr->ptr += rleft; |
369 | 0 | return 0; |
370 | 0 | } |
371 | 0 | } |
372 | 0 | } |
373 | | |
374 | | /* Release the filter's storage. */ |
375 | | static void |
376 | | s_ISpecialDownScale_release(stream_state * st) |
377 | 0 | { |
378 | 0 | stream_ISpecialDownScale_state *const ss = (stream_ISpecialDownScale_state *) st; |
379 | 0 | gs_memory_t *mem = ss->memory; |
380 | |
|
381 | 0 | gs_free_object(mem, (void *)ss->src, "image_scale src"); /* no longer const */ |
382 | 0 | ss->src = 0; |
383 | 0 | gs_free_object(mem, ss->dst, "image_scale dst"); |
384 | 0 | ss->dst = 0; |
385 | 0 | gs_free_object(mem, ss->tmp, "image_scale tmp"); |
386 | 0 | ss->tmp = 0; |
387 | 0 | } |
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 | | }; |