/src/ghostpdl/psi/zimage.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 | | /* Image operators */ |
18 | | #include "math_.h" |
19 | | #include "memory_.h" |
20 | | #include "stat_.h" /* get system header early to avoid name clash on Cygwin */ |
21 | | #include "ghost.h" |
22 | | #include "oper.h" |
23 | | #include "gscolor.h" |
24 | | #include "gscspace.h" |
25 | | #include "gscolor2.h" |
26 | | #include "gsmatrix.h" |
27 | | #include "gsimage.h" |
28 | | #include "gxfixed.h" |
29 | | #include "gsstruct.h" |
30 | | #include "gxiparam.h" |
31 | | #include "idict.h" |
32 | | #include "idparam.h" |
33 | | #include "estack.h" /* for image[mask] */ |
34 | | #include "ialloc.h" |
35 | | #include "igstate.h" |
36 | | #include "ilevel.h" |
37 | | #include "store.h" |
38 | | #include "stream.h" |
39 | | #include "ifilter.h" /* for stream exception handling */ |
40 | | #include "iimage.h" |
41 | | #include "gxcspace.h" |
42 | | |
43 | | /* Forward references */ |
44 | | static int zimage_data_setup(i_ctx_t *i_ctx_p, const gs_pixel_image_t * pim, |
45 | | gx_image_enum_common_t * pie, |
46 | | const ref * sources, int npop); |
47 | | static int image_proc_process(i_ctx_t *); |
48 | | static int image_file_continue(i_ctx_t *); |
49 | | static int image_string_continue(i_ctx_t *); |
50 | | static int image_cleanup(i_ctx_t *); |
51 | | |
52 | | /* Extract and check the parameters for a gs_data_image_t. */ |
53 | | int |
54 | | data_image_params(const gs_memory_t *mem, |
55 | | const ref *op, gs_data_image_t *pim, |
56 | | image_params *pip, bool require_DataSource, |
57 | | int num_components, int max_bits_per_component, |
58 | | bool islab) |
59 | 590k | { |
60 | 590k | int code; |
61 | 590k | ref *pds; |
62 | | |
63 | 590k | check_type(*op, t_dictionary); |
64 | 590k | check_dict_read(*op); |
65 | 590k | code = dict_int_param(op, "Width", 0, max_int_in_fixed/2, -1, &pim->Width); |
66 | 590k | if (code < 0) |
67 | 39 | return code; |
68 | 590k | code = dict_int_param(op, "Height", 0, max_int_in_fixed/2, -1, &pim->Height); |
69 | 590k | if (code < 0) |
70 | 19 | return code; |
71 | 590k | code = dict_matrix_param(mem, op, "ImageMatrix", &pim->ImageMatrix); |
72 | 590k | if (code < 0) |
73 | 18 | return code; |
74 | 590k | code = dict_bool_param(op, "MultipleDataSources", false, &pip->MultipleDataSources); |
75 | 590k | if (code < 0) |
76 | 0 | return code; |
77 | 590k | code = dict_int_param(op, "BitsPerComponent", 1, max_bits_per_component, -1, &pim->BitsPerComponent); |
78 | 590k | if (code < 0) |
79 | 1 | return code; |
80 | 590k | code = dict_bool_param(op, "Interpolate", false, &pim->Interpolate); |
81 | 590k | if (code < 0) |
82 | 0 | return code; |
83 | | |
84 | | /* Decode size pulled out of here to catch case of Lab color space which |
85 | | has a 4 entry range. We also do NOT want to do Lab decoding IF range |
86 | | is the common -128 127 for a and b. Otherwise we end up doing multiple |
87 | | decode operations, since ICC flow will expect encoded data. |
88 | | That is resolved later. Also discovered that PDF write will stick |
89 | | 6 entry range in wich appears to be OK as far as AR is concerned so |
90 | | we have to handle that too. */ |
91 | 590k | if (islab) { |
92 | | /* Note that it is possible that only the ab range values are there |
93 | | or the lab values. I have seen both cases.... */ |
94 | 0 | code = dict_floats_param(mem, op, "Decode", 4, |
95 | 0 | &pim->Decode[2], NULL); |
96 | 0 | if (code < 0) { |
97 | | /* Try for all three */ |
98 | 0 | code = dict_floats_param(mem, op, "Decode", 6, |
99 | 0 | &pim->Decode[0], NULL); |
100 | 0 | } else { |
101 | | /* Set the range on the L */ |
102 | 0 | pim->Decode[0] = 0; |
103 | 0 | pim->Decode[1] = 100.0; |
104 | 0 | } |
105 | 0 | if (code < 0) |
106 | 0 | return code; |
107 | 590k | } else { |
108 | 590k | code = dict_floats_param(mem, op, "Decode", |
109 | 590k | num_components * 2, |
110 | 590k | &pim->Decode[0], NULL); |
111 | 590k | if (code < 0) |
112 | 0 | return code; |
113 | 590k | } |
114 | 590k | pip->pDecode = &pim->Decode[0]; |
115 | | /* Extract and check the data sources. */ |
116 | 590k | if ((code = dict_find_string(op, "DataSource", &pds)) <= 0) { |
117 | 0 | if (require_DataSource) |
118 | 0 | return (code < 0 ? code : gs_note_error(gs_error_rangecheck)); |
119 | 0 | return 1; /* no data source */ |
120 | 0 | } |
121 | 590k | if (pip->MultipleDataSources) { |
122 | 15 | ref *ds = pip->DataSource; |
123 | 15 | long i; |
124 | 15 | if (!r_is_array(pds)) |
125 | 0 | return_error(gs_error_typecheck); |
126 | 15 | if (r_size(pds) != num_components) |
127 | 0 | return_error(gs_error_rangecheck); |
128 | 60 | for (i = 0; i < num_components; ++i) |
129 | 45 | array_get(mem, pds, i, &ds[i]); |
130 | 15 | if (r_type(&ds[0]) == t_string) { |
131 | | /* We don't have a problem with the strings of different length |
132 | | * but Adobe does and CET tast 12-02.ps reports this as an error. |
133 | | */ |
134 | 0 | for (i = 1; i < num_components; ++i) { |
135 | 0 | if (r_type(&ds[i]) == t_string && r_size(&ds[i]) != r_size(&ds[0])) { |
136 | 0 | return_error(gs_error_rangecheck); |
137 | 0 | } |
138 | 0 | } |
139 | 0 | } |
140 | 15 | } else |
141 | 590k | pip->DataSource[0] = *pds; |
142 | 590k | return 0; |
143 | 590k | } |
144 | | |
145 | | /* Extract and check the parameters for a gs_pixel_image_t. */ |
146 | | int |
147 | | pixel_image_params(i_ctx_t *i_ctx_p, const ref *op, gs_pixel_image_t *pim, |
148 | | image_params *pip, int max_bits_per_component, |
149 | | gs_color_space *csp) |
150 | 760 | { |
151 | 760 | bool islab = false; |
152 | 760 | int num_components = |
153 | 760 | gs_color_space_num_components(csp); |
154 | 760 | int code; |
155 | | |
156 | 760 | if (num_components < 1) |
157 | 0 | return_error(gs_error_rangecheck); /* Pattern space not allowed */ |
158 | 760 | pim->ColorSpace = csp; |
159 | | |
160 | 760 | if (pim->ColorSpace->cmm_icc_profile_data != NULL) |
161 | 760 | islab = pim->ColorSpace->cmm_icc_profile_data->islab; |
162 | | |
163 | 760 | code = data_image_params(imemory, op, (gs_data_image_t *) pim, pip, true, |
164 | 760 | num_components, max_bits_per_component, islab); |
165 | 760 | if (code < 0) |
166 | 61 | return code; |
167 | 699 | pim->format = |
168 | 699 | (pip->MultipleDataSources ? gs_image_format_component_planar : |
169 | 699 | gs_image_format_chunky); |
170 | 699 | return dict_bool_param(op, "CombineWithColor", false, |
171 | 699 | &pim->CombineWithColor); |
172 | 760 | } |
173 | | |
174 | | /* Common setup for all Level 1 and 2 images, and ImageType 4 images. */ |
175 | | int |
176 | | zimage_setup(i_ctx_t *i_ctx_p, const gs_pixel_image_t * pim, |
177 | | const ref * sources, bool uses_color, int npop) |
178 | 590k | { |
179 | 590k | gx_image_enum_common_t *pie; |
180 | 590k | int code = |
181 | 590k | gs_image_begin_typed((const gs_image_common_t *)pim, igs, |
182 | 590k | uses_color, false, &pie); |
183 | | |
184 | 590k | if (code < 0) |
185 | 12 | return code; |
186 | 590k | return zimage_data_setup(i_ctx_p, (const gs_pixel_image_t *)pim, pie, |
187 | 590k | sources, npop); |
188 | 590k | } |
189 | | |
190 | | /* <dict> .image1 - */ |
191 | | static int |
192 | | zimage1(i_ctx_t *i_ctx_p) |
193 | 760 | { |
194 | 760 | os_ptr op = osp; |
195 | 760 | gs_image_t image; |
196 | 760 | image_params ip; |
197 | 760 | int code; |
198 | 760 | gs_color_space *csp = gs_currentcolorspace(igs); |
199 | | |
200 | 760 | check_op(1); |
201 | | /* Adobe interpreters accept sampled images when the current color |
202 | | * space is a pattern color space using the base color space instead |
203 | | * of the pattern space. CET 12-07a-12 |
204 | | * If all conditions are not met the pattern color space goes through |
205 | | * triggering a rangecheck error. |
206 | | */ |
207 | 760 | if (gs_currentcpsimode(imemory) && gs_color_space_num_components(csp) < 1) { |
208 | 0 | gs_color_space *bsp = csp->base_space; |
209 | 0 | if (bsp) |
210 | 0 | csp = bsp; |
211 | 0 | } |
212 | | |
213 | 760 | gs_image_t_init(&image, csp); |
214 | 760 | code = pixel_image_params( i_ctx_p, |
215 | 760 | op, |
216 | 760 | (gs_pixel_image_t *)&image, |
217 | 760 | &ip, |
218 | 760 | (level2_enabled ? 16 : 8), |
219 | 760 | csp); |
220 | 760 | if (code < 0) |
221 | 61 | return code; |
222 | | |
223 | 699 | image.Alpha = gs_image_alpha_none; |
224 | | /* swap Width, Height, and ImageMatrix so that it comes out the same */ |
225 | | /* This is only for performance, so only do it for non-skew cases */ |
226 | 699 | if (image.Width == 1 && image.Height > 1 && image.BitsPerComponent == 8 && |
227 | 699 | image.ImageMatrix.xy == 0.0 && image.ImageMatrix.yx == 0.0 && |
228 | 699 | image.ImageMatrix.tx == 0.0) { |
229 | 536 | float ftmp; |
230 | 536 | int itemp; |
231 | | |
232 | 536 | itemp = image.Width; |
233 | 536 | image.Width = image.Height; |
234 | 536 | image.Height = itemp; |
235 | | |
236 | 536 | image.ImageMatrix.xy = image.ImageMatrix.xx; |
237 | 536 | image.ImageMatrix.yx = image.ImageMatrix.yy; |
238 | 536 | image.ImageMatrix.xx = 0.0; |
239 | 536 | image.ImageMatrix.yy = 0.0; |
240 | 536 | ftmp = image.ImageMatrix.tx; |
241 | 536 | image.ImageMatrix.tx = image.ImageMatrix.ty; |
242 | 536 | image.ImageMatrix.ty = ftmp; |
243 | 536 | } |
244 | 699 | return zimage_setup( i_ctx_p, |
245 | 699 | (gs_pixel_image_t *)&image, |
246 | 699 | &ip.DataSource[0], |
247 | 699 | image.CombineWithColor, |
248 | 699 | 1 ); |
249 | 760 | } |
250 | | |
251 | | /* <dict> .imagemask1 - */ |
252 | | static int |
253 | | zimagemask1(i_ctx_t *i_ctx_p) |
254 | 589k | { |
255 | 589k | os_ptr op = osp; |
256 | 589k | gs_image_t image; |
257 | 589k | image_params ip; |
258 | 589k | int code; |
259 | | |
260 | 589k | check_op(1); |
261 | 589k | gs_image_t_init_mask_adjust(&image, false, |
262 | 589k | gs_incachedevice(igs) != CACHE_DEVICE_NONE); |
263 | 589k | code = data_image_params(imemory, op, (gs_data_image_t *) & image, |
264 | 589k | &ip, true, 1, 1, false); |
265 | 589k | if (code < 0) |
266 | 16 | return code; |
267 | 589k | return zimage_setup(i_ctx_p, (gs_pixel_image_t *)&image, &ip.DataSource[0], |
268 | 589k | true, 1); |
269 | 589k | } |
270 | | |
271 | | /* Common setup for all Level 1 and 2 images, and ImageType 3 and 4 images. */ |
272 | | /* |
273 | | * We push the following on the estack. |
274 | | * control mark, |
275 | | * num_sources, |
276 | | * for I = num_sources-1 ... 0: |
277 | | * data source I, |
278 | | * aliasing information: |
279 | | * if source is not file, 1, except that the topmost value |
280 | | * is used for bookkeeping in the procedure case (see below); |
281 | | * if file is referenced by a total of M different sources and |
282 | | * this is the occurrence with the lowest I, M; |
283 | | * otherwise, -J, where J is the lowest I of the same file as |
284 | | * this one; |
285 | | * current plane index, |
286 | | * num_sources, |
287 | | * enumeration structure. |
288 | | */ |
289 | 1.77M | #define NUM_PUSH(nsource) ((nsource) * 2 + 5) |
290 | | /* |
291 | | * We can access these values either from the bottom (esp at control mark - 1, |
292 | | * EBOT macros) or the top (esp = enumeration structure, ETOP macros). |
293 | | * Note that all macros return pointers. |
294 | | */ |
295 | 590k | #define EBOT_NUM_SOURCES(ep) ((ep) + 2) |
296 | | #define EBOT_SOURCE(ep, i)\ |
297 | 590k | ((ep) + 3 + (EBOT_NUM_SOURCES(ep)->value.intval - 1 - (i)) * 2) |
298 | | #define ETOP_SOURCE(ep, i)\ |
299 | 37.2M | ((ep) - 4 - (i) * 2) |
300 | 55.0M | #define ETOP_PLANE_INDEX(ep) ((ep) - 2) |
301 | 37.0M | #define ETOP_NUM_SOURCES(ep) ((ep) - 1) |
302 | | static int |
303 | | zimage_data_setup(i_ctx_t *i_ctx_p, const gs_pixel_image_t * pim, |
304 | | gx_image_enum_common_t * pie, const ref * sources, int npop) |
305 | 590k | { |
306 | 590k | int num_sources = pie->num_planes; |
307 | 590k | int inumpush = NUM_PUSH(num_sources); |
308 | 590k | int code; |
309 | 590k | gs_image_enum *penum; |
310 | 590k | int px; |
311 | 590k | const ref *pp; |
312 | 590k | bool string_sources = true; |
313 | | |
314 | 590k | check_estack(inumpush + 2); /* stuff above, + continuation + proc */ |
315 | 590k | make_int(EBOT_NUM_SOURCES(esp), num_sources); |
316 | | /* |
317 | | * Note that the data sources may be procedures, strings, or (Level |
318 | | * 2 only) files. (The Level 1 reference manual says that Level 1 |
319 | | * requires procedures, but Adobe Level 1 interpreters also accept |
320 | | * strings.) The sources must all be of the same type. |
321 | | * |
322 | | * The Adobe documentation explicitly says that if two or more of the |
323 | | * data sources are the same or inter-dependent files, the result is not |
324 | | * defined. We don't have a problem with the bookkeeping for |
325 | | * inter-dependent files, since each one has its own buffer, but we do |
326 | | * have to be careful if two or more sources are actually the same file. |
327 | | * That is the reason for the aliasing information described above. |
328 | | */ |
329 | 1.18M | for (px = 0, pp = sources; px < num_sources; px++, pp++) { |
330 | 590k | es_ptr ep = EBOT_SOURCE(esp, px); |
331 | | |
332 | 590k | make_int(ep + 1, 1); /* default is no aliasing */ |
333 | 590k | switch (r_type(pp)) { |
334 | 12 | case t_file: |
335 | 12 | if (!level2_enabled) |
336 | 0 | return_error(gs_error_typecheck); |
337 | | /* Check for aliasing. */ |
338 | 12 | { |
339 | 12 | int pi; |
340 | | |
341 | 12 | for (pi = 0; pi < px; ++pi) |
342 | 0 | if (sources[pi].value.pfile == pp->value.pfile) { |
343 | | /* Record aliasing */ |
344 | 0 | make_int(ep + 1, -pi); |
345 | 0 | EBOT_SOURCE(esp, pi)[1].value.intval++; |
346 | 0 | break; |
347 | 0 | } |
348 | 12 | } |
349 | 12 | string_sources = false; |
350 | | /* falls through */ |
351 | 19 | case t_string: |
352 | 19 | if (r_type(pp) != r_type(sources)) { |
353 | 0 | gx_image_end(pie, false); /* Clean up pie */ |
354 | 0 | return_error(gs_error_typecheck); |
355 | 0 | } |
356 | 19 | check_read(*pp); |
357 | 19 | break; |
358 | 590k | default: |
359 | 590k | if (!r_is_proc(sources)) { |
360 | 0 | static const char ds[] = "DataSource"; |
361 | 0 | gx_image_end(pie, false); /* Clean up pie */ |
362 | 0 | gs_errorinfo_put_pair(i_ctx_p, ds, sizeof(ds) - 1, pp); |
363 | 0 | return_error(gs_error_typecheck); |
364 | 0 | } |
365 | 590k | check_proc(*pp); |
366 | 590k | string_sources = false; |
367 | 590k | } |
368 | 590k | *ep = *pp; |
369 | 590k | } |
370 | | /* Always place the image enumerator into local memory, |
371 | | because pie may have local objects inherited from igs, |
372 | | which may be local when the current allocation mode is global. |
373 | | Bug 688140. */ |
374 | 590k | if ((penum = gs_image_enum_alloc(imemory_local, "image_setup")) == 0) |
375 | 0 | return_error(gs_error_VMerror); |
376 | 590k | code = gs_image_enum_init(penum, pie, (const gs_data_image_t *)pim, igs); |
377 | 590k | if (code != 0 || (pie->skipping && string_sources)) { /* error, or empty image */ |
378 | 41 | int code1 = gs_image_cleanup_and_free_enum(penum, igs); |
379 | | |
380 | 41 | if (code >= 0) /* empty image */ |
381 | 41 | pop(npop); |
382 | 41 | if (code >= 0 && code1 < 0) |
383 | 0 | code = code1; |
384 | 41 | return code; |
385 | 41 | } |
386 | 590k | push_mark_estack(es_other, image_cleanup); |
387 | 590k | esp += inumpush - 1; |
388 | 590k | make_int(ETOP_PLANE_INDEX(esp), 0); |
389 | 590k | make_int(ETOP_NUM_SOURCES(esp), num_sources); |
390 | 590k | make_struct(esp, avm_local, penum); |
391 | 590k | switch (r_type(sources)) { |
392 | 12 | case t_file: |
393 | 12 | push_op_estack(image_file_continue); |
394 | 12 | break; |
395 | 7 | case t_string: |
396 | 7 | push_op_estack(image_string_continue); |
397 | 7 | break; |
398 | 590k | default: /* procedure */ |
399 | 590k | push_op_estack(image_proc_process); |
400 | 590k | break; |
401 | 590k | } |
402 | 590k | pop(npop); |
403 | 590k | return o_push_estack; |
404 | 590k | } |
405 | | /* Pop all the control information off the e-stack. */ |
406 | | static es_ptr |
407 | | zimage_pop_estack(es_ptr tep) |
408 | 590k | { |
409 | 590k | return tep - NUM_PUSH(ETOP_NUM_SOURCES(tep)->value.intval); |
410 | 590k | } |
411 | | |
412 | | /* |
413 | | * Continuation for procedure data source. We use the topmost aliasing slot |
414 | | * to remember whether we've just called the procedure (1) or whether we're |
415 | | * returning from a RemapColor callout (0). |
416 | | */ |
417 | | static int |
418 | | image_proc_continue(i_ctx_t *i_ctx_p) |
419 | 18.5M | { |
420 | 18.5M | os_ptr op = osp; |
421 | 18.5M | gs_image_enum *penum = r_ptr(esp, gs_image_enum); |
422 | 18.5M | int px = ETOP_PLANE_INDEX(esp)->value.intval; |
423 | 18.5M | int num_sources = ETOP_NUM_SOURCES(esp)->value.intval; |
424 | 18.5M | uint size, used[GS_IMAGE_MAX_COMPONENTS]; |
425 | 18.5M | gs_const_string plane_data[GS_IMAGE_MAX_COMPONENTS]; |
426 | 18.5M | const byte *wanted; |
427 | 18.5M | int i, code; |
428 | | |
429 | 18.5M | if (!r_has_type_attrs(op, t_string, a_read)) { |
430 | 36 | check_op(1); |
431 | | /* Procedure didn't return a (readable) string. Quit. */ |
432 | 4 | esp = zimage_pop_estack(esp); |
433 | 4 | image_cleanup(i_ctx_p); |
434 | 4 | return_error(!r_has_type(op, t_string) ? gs_error_typecheck : gs_error_invalidaccess); |
435 | 36 | } |
436 | 18.5M | size = r_size(op); |
437 | 18.5M | if (size == 0 && ETOP_SOURCE(esp, 0)[1].value.intval == 0) |
438 | 103 | code = 1; |
439 | 18.5M | else { |
440 | 37.1M | for (i = 0; i < num_sources; i++) |
441 | 18.5M | plane_data[i].size = 0; |
442 | | |
443 | | /* Make a copy of the string source data in 'stable' memory (immune to save/restore) |
444 | | * We need this because of bug #706867 where one of the procedure data sources does |
445 | | * a 'restore' back to a point where the string returned (and saved) by one of the |
446 | | * other procedure data sources is freed. By copying the string to stable memory |
447 | | * this can't happen. |
448 | | */ |
449 | 18.5M | plane_data[px].data = gs_alloc_string(imemory->stable_memory, size, "image_proc_continue"); |
450 | 18.5M | if (plane_data[px].data == NULL) |
451 | 0 | return_error(gs_error_VMerror); |
452 | 18.5M | memcpy((byte *)plane_data[px].data, op->value.bytes, size); |
453 | 18.5M | plane_data[px].size = size; |
454 | | /* Set the txfer_control flag to true to transfer control of the string (which must be allocated |
455 | | * in stable memory for this) to the gs_image_next_planes() routine. |
456 | | */ |
457 | 18.5M | code = gs_image_next_planes(penum, plane_data, used, true); |
458 | 18.5M | if (code == gs_error_Remap_Color) { |
459 | 0 | op->value.bytes += used[px]; /* skip used data */ |
460 | 0 | r_dec_size(op, used[px]); |
461 | 0 | ETOP_SOURCE(esp, 0)[1].value.intval = 0; /* RemapColor callout */ |
462 | 0 | return code; |
463 | 0 | } |
464 | 18.5M | } |
465 | 18.5M | if (code) { /* Stop now. */ |
466 | 590k | esp = zimage_pop_estack(esp); |
467 | 590k | pop(1); |
468 | 590k | image_cleanup(i_ctx_p); |
469 | 590k | return (code < 0 ? code : o_pop_estack); |
470 | 590k | } |
471 | 17.9M | pop(1); |
472 | 17.9M | wanted = gs_image_planes_wanted(penum); |
473 | 17.9M | do { |
474 | 17.9M | if (++px == num_sources) |
475 | 17.9M | px = 0; |
476 | 17.9M | } while (!wanted[px]); |
477 | 17.9M | ETOP_PLANE_INDEX(esp)->value.intval = px; |
478 | 17.9M | return image_proc_process(i_ctx_p); |
479 | 18.5M | } |
480 | | static int |
481 | | image_proc_process(i_ctx_t *i_ctx_p) |
482 | 18.5M | { |
483 | 18.5M | int px = ETOP_PLANE_INDEX(esp)->value.intval; |
484 | 18.5M | gs_image_enum *penum = r_ptr(esp, gs_image_enum); |
485 | 18.5M | const byte *wanted = gs_image_planes_wanted(penum); |
486 | 18.5M | int num_sources = ETOP_NUM_SOURCES(esp)->value.intval; |
487 | 18.5M | const ref *pp; |
488 | | |
489 | 18.5M | ETOP_SOURCE(esp, 0)[1].value.intval = 0; /* procedure callout */ |
490 | 18.5M | while (!wanted[px]) { |
491 | 0 | if (++px == num_sources) |
492 | 0 | px = 0; |
493 | 0 | ETOP_PLANE_INDEX(esp)->value.intval = px; |
494 | 0 | } |
495 | 18.5M | pp = ETOP_SOURCE(esp, px); |
496 | 18.5M | push_op_estack(image_proc_continue); |
497 | 18.5M | *++esp = *pp; |
498 | 18.5M | return o_push_estack; |
499 | 18.5M | } |
500 | | |
501 | | /* Continue processing data from an image with file data sources. */ |
502 | | static int |
503 | | image_file_continue(i_ctx_t *i_ctx_p) |
504 | 12 | { |
505 | 12 | gs_image_enum *penum = r_ptr(esp, gs_image_enum); |
506 | 12 | int num_sources = ETOP_NUM_SOURCES(esp)->value.intval; |
507 | | |
508 | 656 | for (;;) { |
509 | 656 | uint min_avail = max_int; |
510 | 656 | gs_const_string plane_data[GS_IMAGE_MAX_COMPONENTS]; |
511 | 656 | int code; |
512 | 656 | int px; |
513 | 656 | const ref *pp; |
514 | 656 | int at_eof_count = 0; |
515 | 656 | int total_used; |
516 | | |
517 | | /* |
518 | | * Do a first pass through the files to ensure that at least |
519 | | * one has data available in its buffer. |
520 | | */ |
521 | | |
522 | 1.31k | for (px = 0, pp = ETOP_SOURCE(esp, 0); px < num_sources; |
523 | 656 | ++px, pp -= 2 |
524 | 656 | ) { |
525 | 656 | int num_aliases = pp[1].value.intval; |
526 | 656 | stream *s = pp->value.pfile; |
527 | 656 | int min_left; |
528 | 656 | uint avail; |
529 | | |
530 | 656 | if (num_aliases <= 0) |
531 | 0 | num_aliases = ETOP_SOURCE(esp, -num_aliases)[1].value.intval; |
532 | 1.30k | while ((avail = sbufavailable(s)) <= |
533 | 1.30k | (min_left = sbuf_min_left(s)) + num_aliases - 1) { |
534 | 656 | int next = s->end_status; |
535 | | |
536 | 656 | switch (next) { |
537 | 646 | case 0: |
538 | 646 | s_process_read_buf(s); |
539 | 646 | continue; |
540 | 9 | case EOFC: |
541 | 9 | at_eof_count++; |
542 | 9 | break; /* with no data available */ |
543 | 0 | case INTC: |
544 | 0 | case CALLC: |
545 | 0 | return |
546 | 0 | s_handle_read_exception(i_ctx_p, next, pp, |
547 | 0 | NULL, 0, image_file_continue); |
548 | 1 | default: |
549 | | /* case ERRC: */ |
550 | 1 | return_error(gs_error_ioerror); |
551 | 656 | } |
552 | 9 | break; /* for EOFC */ |
553 | 656 | } |
554 | | /* |
555 | | * Note that in the EOF case, we can get here with no data |
556 | | * available. |
557 | | */ |
558 | 655 | if (avail >= min_left) |
559 | 655 | avail = (avail - min_left) / num_aliases; /* may be 0 */ |
560 | 655 | if (avail < min_avail) |
561 | 655 | min_avail = avail; |
562 | 655 | plane_data[px].data = sbufptr(s); |
563 | 655 | plane_data[px].size = avail; |
564 | 655 | } |
565 | | |
566 | | /* |
567 | | * Now pass the available buffered data to the image processor. |
568 | | * Even if there is no available data, we must call |
569 | | * gs_image_next_planes one more time to finish processing any |
570 | | * retained data. |
571 | | */ |
572 | | |
573 | 655 | { |
574 | 655 | int pi; |
575 | 655 | uint used[GS_IMAGE_MAX_COMPONENTS]; |
576 | | |
577 | 655 | code = gs_image_next_planes(penum, plane_data, used, false); |
578 | | /* Now that used has been set, update the streams. */ |
579 | 655 | total_used = 0; |
580 | 1.31k | for (pi = 0, pp = ETOP_SOURCE(esp, 0); pi < num_sources; |
581 | 655 | ++pi, pp -= 2 ) { |
582 | 655 | (void)sbufskip(pp->value.pfile, used[pi]); |
583 | 655 | total_used += used[pi]; |
584 | 655 | } |
585 | 655 | if (code == gs_error_Remap_Color) |
586 | 0 | return code; |
587 | 655 | } |
588 | 655 | if (at_eof_count >= num_sources || (at_eof_count && total_used == 0)) |
589 | 9 | code = 1; |
590 | 655 | if (code) { |
591 | 11 | int code1; |
592 | | |
593 | 11 | esp = zimage_pop_estack(esp); |
594 | 11 | code1 = image_cleanup(i_ctx_p); |
595 | 11 | return (code < 0 ? code : code1 < 0 ? code1 : o_pop_estack); |
596 | 11 | } |
597 | 655 | } |
598 | 12 | } |
599 | | |
600 | | /* Process data from an image with string data sources. */ |
601 | | /* This may still encounter a RemapColor callback. */ |
602 | | static int |
603 | | image_string_continue(i_ctx_t *i_ctx_p) |
604 | 7 | { |
605 | 7 | gs_image_enum *penum = r_ptr(esp, gs_image_enum); |
606 | 7 | int num_sources = ETOP_NUM_SOURCES(esp)->value.intval; |
607 | 7 | gs_const_string sources[GS_IMAGE_MAX_COMPONENTS]; |
608 | 7 | uint used[GS_IMAGE_MAX_COMPONENTS]; |
609 | | |
610 | | /* Pass no data initially, to find out how much is retained. */ |
611 | 7 | memset(sources, 0, sizeof(sources[0]) * num_sources); |
612 | 123k | for (;;) { |
613 | 123k | int px; |
614 | 123k | int code = gs_image_next_planes(penum, sources, used, false); |
615 | | |
616 | 123k | if (code == gs_error_Remap_Color) |
617 | 0 | return code; |
618 | 123k | stop_now: |
619 | 123k | if (code) { /* Stop now. */ |
620 | 7 | esp -= NUM_PUSH(num_sources); |
621 | 7 | image_cleanup(i_ctx_p); |
622 | 7 | return (code < 0 ? code : o_pop_estack); |
623 | 7 | } |
624 | 246k | for (px = 0; px < num_sources; ++px) |
625 | 123k | if (sources[px].size == 0) { |
626 | 123k | const ref *psrc = ETOP_SOURCE(esp, px); |
627 | 123k | uint size = r_size(psrc); |
628 | | |
629 | 123k | if (size == 0) { /* empty source */ |
630 | 0 | code = 1; |
631 | 0 | goto stop_now; |
632 | 0 | } |
633 | 123k | sources[px].data = psrc->value.bytes; |
634 | 123k | sources[px].size = size; |
635 | 123k | } |
636 | 123k | } |
637 | 7 | } |
638 | | |
639 | | /* Clean up after enumerating an image */ |
640 | | static int |
641 | | image_cleanup(i_ctx_t *i_ctx_p) |
642 | 590k | { |
643 | 590k | es_ptr ep_top = esp + NUM_PUSH(EBOT_NUM_SOURCES(esp)->value.intval); |
644 | 590k | gs_image_enum *penum = r_ptr(ep_top, gs_image_enum); |
645 | | |
646 | 590k | return gs_image_cleanup_and_free_enum(penum, igs); |
647 | 590k | } |
648 | | |
649 | | /* ------ Initialization procedure ------ */ |
650 | | |
651 | | const op_def zimage_op_defs[] = |
652 | | { |
653 | | {"1.image1", zimage1}, |
654 | | {"1.imagemask1", zimagemask1}, |
655 | | /* Internal operators */ |
656 | | {"1%image_proc_continue", image_proc_continue}, |
657 | | {"0%image_file_continue", image_file_continue}, |
658 | | {"0%image_string_continue", image_string_continue}, |
659 | | op_def_end(0) |
660 | | }; |