/src/ghostpdl/base/gsimage.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 setup procedures for Ghostscript library */ |
18 | | #include "memory_.h" |
19 | | #include "math_.h" |
20 | | #include "gx.h" |
21 | | #include "gserrors.h" |
22 | | #include "gsstruct.h" |
23 | | #include "gscspace.h" |
24 | | #include "gsmatrix.h" /* for gsiparam.h */ |
25 | | #include "gsimage.h" |
26 | | #include "gxarith.h" /* for igcd */ |
27 | | #include "gxdevice.h" |
28 | | #include "gxiparam.h" |
29 | | #include "gxpath.h" /* for gx_effective_clip_path */ |
30 | | #include "gximask.h" |
31 | | #include "gzstate.h" |
32 | | #include "gsutil.h" |
33 | | #include "gxdevsop.h" |
34 | | #include "gximage.h" |
35 | | |
36 | | /* |
37 | | The main internal invariant for the gs_image machinery is |
38 | | straightforward. The state consists primarily of N plane buffers |
39 | | (planes[]). |
40 | | */ |
41 | | typedef struct image_enum_plane_s { |
42 | | /* |
43 | | The state of each plane consists of: |
44 | | |
45 | | - A row buffer, aligned and (logically) large enough to hold one scan line |
46 | | for that plane. (It may have to be reallocated if the plane width or |
47 | | depth changes.) A row buffer is "full" if it holds exactly a full scan |
48 | | line. |
49 | | */ |
50 | | gs_string row; |
51 | | /* |
52 | | - A position within the row buffer, indicating how many initial bytes are |
53 | | occupied. |
54 | | */ |
55 | | uint pos; |
56 | | /* |
57 | | - A (retained) source string, which may be empty (size = 0). |
58 | | */ |
59 | | gs_const_string source; |
60 | | /* The gs_string 'orig' is only set if the 'txfer_control' flag was set when |
61 | | * the 'source' string data was initally passed in. In this case we now control the lifetime |
62 | | * of the string. So when we empty the source string, free it. We need to know the actual |
63 | | * address of the string, and that gets modified in the peunum->planes->source and size |
64 | | * members, so we use 'orig' as both a marker for the control and the original size and location. |
65 | | */ |
66 | | gs_const_string orig; |
67 | | } image_enum_plane_t; |
68 | | /* |
69 | | The possible states for each plane do not depend on the state of any other |
70 | | plane. Either: |
71 | | |
72 | | - pos = 0, source.size = 0. |
73 | | |
74 | | - If the underlying image processor says the plane is currently wanted, |
75 | | either: |
76 | | |
77 | | - pos = 0, source.size >= one full row of data for this plane. This |
78 | | case allows us to avoid copying the data from the source string to the |
79 | | row buffer if the client is providing data in blocks of at least one |
80 | | scan line. |
81 | | |
82 | | - pos = full, source.size may have any value. |
83 | | |
84 | | - pos > 0, pos < full, source.size = 0; |
85 | | |
86 | | - If the underlying image processor says the plane is not currently |
87 | | wanted: |
88 | | |
89 | | - pos = 0, source.size may have any value. |
90 | | |
91 | | This invariant holds at the beginning and end of each call on |
92 | | gs_image_next_planes. Note that for each plane, the "plane wanted" status |
93 | | and size of a full row may change after each call of plane_data. As |
94 | | documented in gxiparam.h, we assume that a call of plane_data can only |
95 | | change a plane's status from "wanted" to "not wanted", or change the width |
96 | | or depth of a wanted plane, if data for that plane was actually supplied |
97 | | (and used). |
98 | | */ |
99 | | |
100 | | /* Define the enumeration state for this interface layer. */ |
101 | | /*typedef struct gs_image_enum_s gs_image_enum; *//* in gsimage.h */ |
102 | | struct gs_image_enum_s { |
103 | | /* The following are set at initialization time. */ |
104 | | gs_memory_t *memory; |
105 | | gx_device *dev; /* if 0, just skip over the data */ |
106 | | gx_image_enum_common_t *info; /* driver bookkeeping structure */ |
107 | | int num_planes; |
108 | | int height; |
109 | | bool wanted_varies; |
110 | | /* The following are updated dynamically. */ |
111 | | int plane_index; /* index of next plane of data, */ |
112 | | /* only needed for gs_image_next */ |
113 | | int y; |
114 | | bool error; |
115 | | byte wanted[GS_IMAGE_MAX_COMPONENTS]; /* cache gx_image_planes_wanted */ |
116 | | byte client_wanted[GS_IMAGE_MAX_COMPONENTS]; /* see gsimage.h */ |
117 | | image_enum_plane_t planes[GS_IMAGE_MAX_COMPONENTS]; /* see above */ |
118 | | /* |
119 | | * To reduce setup for transferring complete rows, we maintain a |
120 | | * partially initialized parameter array for gx_image_plane_data_rows. |
121 | | * The data member is always set just before calling |
122 | | * gx_image_plane_data_rows; the data_x and raster members are reset |
123 | | * when needed. |
124 | | */ |
125 | | gx_image_plane_t image_planes[GS_IMAGE_MAX_COMPONENTS]; |
126 | | }; |
127 | | |
128 | | gs_private_st_composite(st_gs_image_enum, gs_image_enum, "gs_image_enum", |
129 | | gs_image_enum_enum_ptrs, gs_image_enum_reloc_ptrs); |
130 | 0 | #define gs_image_enum_num_ptrs 2 |
131 | | |
132 | | /* GC procedures */ |
133 | | static |
134 | 0 | ENUM_PTRS_WITH(gs_image_enum_enum_ptrs, gs_image_enum *eptr) |
135 | 0 | { |
136 | | /* Enumerate the data planes. */ |
137 | 0 | index -= gs_image_enum_num_ptrs; |
138 | 0 | if (index < eptr->num_planes) |
139 | 0 | ENUM_RETURN_STRING_PTR(gs_image_enum, planes[index].source); |
140 | 0 | index -= eptr->num_planes; |
141 | 0 | if (index < eptr->num_planes) |
142 | 0 | ENUM_RETURN_STRING_PTR(gs_image_enum, planes[index].row); |
143 | 0 | return 0; |
144 | 0 | } |
145 | 0 | ENUM_PTR(0, gs_image_enum, dev); |
146 | 0 | ENUM_PTR(1, gs_image_enum, info); |
147 | 0 | ENUM_PTRS_END |
148 | 0 | static RELOC_PTRS_WITH(gs_image_enum_reloc_ptrs, gs_image_enum *eptr) |
149 | 0 | { |
150 | 0 | int i; |
151 | |
|
152 | 0 | RELOC_PTR(gs_image_enum, dev); |
153 | 0 | RELOC_PTR(gs_image_enum, info); |
154 | 0 | for (i = 0; i < eptr->num_planes; i++) |
155 | 0 | RELOC_CONST_STRING_PTR(gs_image_enum, planes[i].source); |
156 | 0 | for (i = 0; i < eptr->num_planes; i++) |
157 | 0 | RELOC_STRING_PTR(gs_image_enum, planes[i].row); |
158 | 0 | } |
159 | 0 | RELOC_PTRS_END |
160 | | |
161 | | static int |
162 | | is_image_visible(const gs_image_common_t * pic, gs_gstate * pgs, gx_clip_path *pcpath) |
163 | 21.5k | { |
164 | 21.5k | gs_rect image_rect = {{0, 0}, {0, 0}}; |
165 | 21.5k | gs_rect device_rect; |
166 | 21.5k | gs_int_rect device_int_rect; |
167 | 21.5k | gs_matrix mat; |
168 | 21.5k | int code; |
169 | | |
170 | 21.5k | image_rect.q.x = pic->Width; |
171 | 21.5k | image_rect.q.y = pic->Height; |
172 | 21.5k | if (pic->ImageMatrix.xx == ctm_only(pgs).xx && |
173 | 21.5k | pic->ImageMatrix.xy == ctm_only(pgs).xy && |
174 | 21.5k | pic->ImageMatrix.yx == ctm_only(pgs).yx && |
175 | 21.5k | pic->ImageMatrix.yy == ctm_only(pgs).yy) { |
176 | | /* Handle common special case separately to accept singular matrix */ |
177 | 1.97k | mat.xx = mat.yy = 1.; |
178 | 1.97k | mat.yx = mat.xy = 0.; |
179 | 1.97k | mat.tx = ctm_only(pgs).tx - pic->ImageMatrix.tx; |
180 | 1.97k | mat.ty = ctm_only(pgs).ty - pic->ImageMatrix.ty; |
181 | 19.5k | } else { |
182 | 19.5k | code = gs_matrix_invert(&pic->ImageMatrix, &mat); |
183 | 19.5k | if (code < 0) |
184 | 0 | return code; |
185 | 19.5k | code = gs_matrix_multiply(&mat, &ctm_only(pgs), &mat); |
186 | 19.5k | if (code < 0) |
187 | 0 | return code; |
188 | 19.5k | } |
189 | 21.5k | code = gs_bbox_transform(&image_rect, &mat, &device_rect); |
190 | 21.5k | if (code < 0) |
191 | 0 | return code; |
192 | 21.5k | device_int_rect.p.x = (int)floor(device_rect.p.x); |
193 | 21.5k | device_int_rect.p.y = (int)floor(device_rect.p.y); |
194 | 21.5k | device_int_rect.q.x = (int)ceil(device_rect.q.x); |
195 | 21.5k | device_int_rect.q.y = (int)ceil(device_rect.q.y); |
196 | 21.5k | if (!gx_cpath_rect_visible(pcpath, &device_int_rect)) |
197 | 2.45k | return 0; |
198 | 19.0k | return 1; |
199 | 21.5k | } |
200 | | |
201 | | /* Create an image enumerator given image parameters and a graphics state. */ |
202 | | int |
203 | | gs_image_begin_typed(const gs_image_common_t * pic, gs_gstate * pgs, |
204 | | bool uses_color, bool image_is_text, gx_image_enum_common_t ** ppie) |
205 | 21.5k | { |
206 | 21.5k | gx_device *dev = gs_currentdevice(pgs); |
207 | 21.5k | gx_clip_path *pcpath; |
208 | 21.5k | int code = gx_effective_clip_path(pgs, &pcpath); |
209 | 21.5k | gx_device *dev2 = dev; |
210 | 21.5k | gx_device_color dc_temp, *pdevc = gs_currentdevicecolor_inline(pgs); |
211 | | |
212 | 21.5k | if (code < 0) |
213 | 0 | return code; |
214 | | /* Processing an image object operation, but this may be for a text object */ |
215 | 21.5k | ensure_tag_is_set(pgs, pgs->device, image_is_text ? GS_TEXT_TAG : GS_IMAGE_TAG); /* NB: may unset_dev_color */ |
216 | | |
217 | 21.5k | if (uses_color) { |
218 | 13.4k | code = gx_set_dev_color(pgs); |
219 | 13.4k | if (code != 0) |
220 | 1 | return code; |
221 | 13.4k | code = gs_gstate_color_load(pgs); |
222 | 13.4k | if (code < 0) |
223 | 0 | return code; |
224 | 13.4k | } |
225 | | |
226 | 21.5k | if (pgs->overprint || (!pgs->overprint && dev_proc(pgs->device, dev_spec_op)(pgs->device, |
227 | 21.5k | gxdso_overprint_active, NULL, 0))) { |
228 | 13 | gs_overprint_params_t op_params = { 0 }; |
229 | | |
230 | 13 | if_debug0m(gs_debug_flag_overprint, pgs->memory, |
231 | 13 | "[overprint] Image Overprint\n"); |
232 | 13 | code = gs_do_set_overprint(pgs); |
233 | 13 | if (code < 0) |
234 | 0 | return code; |
235 | | |
236 | 13 | op_params.op_state = OP_STATE_FILL; |
237 | 13 | gs_gstate_update_overprint(pgs, &op_params); |
238 | | |
239 | 13 | dev = gs_currentdevice(pgs); |
240 | 13 | dev2 = dev; |
241 | 13 | } |
242 | | |
243 | | /* Imagemask with shading color needs a special optimization |
244 | | with converting the image into a clipping. |
245 | | Check for such case after gs_gstate_color_load is done, |
246 | | because it can cause interpreter callout. |
247 | | */ |
248 | 21.5k | if (pic->type->begin_typed_image == &gx_begin_image1) { |
249 | 21.4k | gs_image_t *image = (gs_image_t *)pic; |
250 | | |
251 | 21.4k | if(image->ImageMask) { |
252 | 13.4k | bool transpose = false; |
253 | 13.4k | gs_matrix_double mat; |
254 | | |
255 | 13.4k | if((code = gx_image_compute_mat(pgs, NULL, &(image->ImageMatrix), &mat)) < 0) |
256 | 1 | return code; |
257 | 13.4k | if ((any_abs(mat.xy) > any_abs(mat.xx)) && (any_abs(mat.yx) > any_abs(mat.yy))) |
258 | 7.46k | transpose = true; /* pure landscape */ |
259 | 13.4k | code = gx_image_fill_masked_start(dev, gs_currentdevicecolor_inline(pgs), transpose, |
260 | 13.4k | pcpath, pgs->memory, pgs->log_op, &dev2); |
261 | 13.4k | if (code < 0) |
262 | 0 | return code; |
263 | 13.4k | } |
264 | 21.4k | if (dev->interpolate_control < 0) { /* Force interpolation before begin_typed_image */ |
265 | 0 | ((gs_data_image_t *)pic)->Interpolate = true; |
266 | 0 | } |
267 | 21.4k | else if (dev->interpolate_control == 0) { |
268 | 21.4k | ((gs_data_image_t *)pic)->Interpolate = false; /* Suppress interpolation */ |
269 | 21.4k | } |
270 | 21.4k | if (dev2 != dev) { |
271 | 16 | set_nonclient_dev_color(&dc_temp, 1); |
272 | 16 | pdevc = &dc_temp; |
273 | 16 | } |
274 | 21.4k | } |
275 | 21.5k | code = gx_device_begin_typed_image(dev2, (const gs_gstate *)pgs, |
276 | 21.5k | NULL, pic, NULL, pdevc, pcpath, pgs->memory, ppie); |
277 | 21.5k | if (code < 0) |
278 | 5 | return code; |
279 | 21.5k | code = is_image_visible(pic, pgs, pcpath); |
280 | 21.5k | if (code < 0) |
281 | 0 | return code; |
282 | 21.5k | if (!code) |
283 | 2.45k | (*ppie)->skipping = true; |
284 | 21.5k | return 0; |
285 | 21.5k | } |
286 | | |
287 | | /* Allocate an image enumerator. */ |
288 | | static void |
289 | | image_enum_init(gs_image_enum * penum) |
290 | 42.4k | { |
291 | | /* Clean pointers for GC. */ |
292 | 42.4k | penum->info = 0; |
293 | 42.4k | penum->dev = 0; |
294 | 42.4k | penum->plane_index = 0; |
295 | 42.4k | penum->num_planes = 0; |
296 | 42.4k | } |
297 | | gs_image_enum * |
298 | | gs_image_enum_alloc(gs_memory_t * mem, client_name_t cname) |
299 | 21.5k | { |
300 | 21.5k | gs_image_enum *penum = |
301 | 21.5k | gs_alloc_struct(mem, gs_image_enum, &st_gs_image_enum, cname); |
302 | | |
303 | 21.5k | if (penum != 0) { |
304 | 21.5k | penum->memory = mem; |
305 | 21.5k | image_enum_init(penum); |
306 | 21.5k | } |
307 | 21.5k | return penum; |
308 | 21.5k | } |
309 | | |
310 | | /* Start processing an ImageType 1 image. */ |
311 | | int |
312 | | gs_image_init(gs_image_enum * penum, const gs_image_t * pim, bool multi, |
313 | | bool image_is_text, gs_gstate * pgs) |
314 | 1.96k | { |
315 | 1.96k | gs_image_t image; |
316 | 1.96k | gx_image_enum_common_t *pie; |
317 | 1.96k | int code; |
318 | | |
319 | 1.96k | image = *pim; |
320 | 1.96k | if (image.ImageMask) { |
321 | 1.96k | image.ColorSpace = NULL; |
322 | 1.96k | if (pgs->in_cachedevice <= 1) |
323 | 1.96k | image.adjust = false; |
324 | 1.96k | } else { |
325 | 0 | if (pgs->in_cachedevice) |
326 | 0 | return_error(gs_error_undefined); |
327 | 0 | if (image.ColorSpace == NULL) { |
328 | | /* |
329 | | * Use of a non-current color space is potentially |
330 | | * incorrect, but it appears this case doesn't arise. |
331 | | */ |
332 | 0 | image.ColorSpace = gs_cspace_new_DeviceGray(pgs->memory); |
333 | 0 | if (image.ColorSpace == NULL) |
334 | 0 | return_error(gs_error_VMerror); |
335 | 0 | } |
336 | 0 | } |
337 | 1.96k | code = gs_image_begin_typed((const gs_image_common_t *)&image, pgs, |
338 | 1.96k | image.ImageMask | image.CombineWithColor, |
339 | 1.96k | image_is_text, &pie); |
340 | 1.96k | if (code < 0) |
341 | 0 | return code; |
342 | 1.96k | return gs_image_enum_init(penum, pie, (const gs_data_image_t *)&image, |
343 | 1.96k | pgs); |
344 | 1.96k | } |
345 | | |
346 | | /* |
347 | | * Return the number of bytes of data per row for a given plane. |
348 | | */ |
349 | | inline uint |
350 | | gs_image_bytes_per_plane_row(const gs_image_enum * penum, int plane) |
351 | 33.4k | { |
352 | 33.4k | const gx_image_enum_common_t *pie = penum->info; |
353 | | |
354 | 33.4k | return (pie->plane_widths[plane] * pie->plane_depths[plane] + 7) >> 3; |
355 | 33.4k | } |
356 | | |
357 | | /* Cache information when initializing, or after transferring plane data. */ |
358 | | static void |
359 | | cache_planes(gs_image_enum *penum) |
360 | 835k | { |
361 | 835k | int i; |
362 | | |
363 | 835k | if (penum->wanted_varies) { |
364 | 27.2k | penum->wanted_varies = |
365 | 27.2k | !gx_image_planes_wanted(penum->info, penum->wanted); |
366 | 60.7k | for (i = 0; i < penum->num_planes; ++i) |
367 | 33.5k | if (penum->wanted[i]) |
368 | 33.4k | penum->image_planes[i].raster = |
369 | 33.4k | gs_image_bytes_per_plane_row(penum, i); |
370 | 85 | else |
371 | 85 | penum->image_planes[i].data = 0; |
372 | 27.2k | } |
373 | 835k | } |
374 | | /* Advance to the next wanted plane. */ |
375 | | static void |
376 | | next_plane(gs_image_enum *penum) |
377 | 184k | { |
378 | 184k | int px = penum->plane_index; |
379 | | |
380 | 184k | do { |
381 | 184k | if (++px == penum->num_planes) |
382 | 163k | px = 0; |
383 | 184k | } while (!penum->wanted[px]); |
384 | 184k | penum->plane_index = px; |
385 | 184k | } |
386 | | /* |
387 | | * Initialize plane_index and (if appropriate) wanted and |
388 | | * wanted_varies at the beginning of a group of planes. |
389 | | */ |
390 | | static void |
391 | | begin_planes(gs_image_enum *penum) |
392 | 20.9k | { |
393 | 20.9k | cache_planes(penum); |
394 | 20.9k | penum->plane_index = -1; |
395 | 20.9k | next_plane(penum); |
396 | 20.9k | } |
397 | | |
398 | | int |
399 | | gs_image_common_init(gs_image_enum * penum, gx_image_enum_common_t * pie, |
400 | | const gs_data_image_t * pim, gx_device * dev) |
401 | 21.5k | { |
402 | | /* |
403 | | * HACK : For a compatibility with gs_image_cleanup_and_free_enum, |
404 | | * penum->memory must be initialized in advance |
405 | | * with the memory heap that owns *penum. |
406 | | */ |
407 | 21.5k | int i; |
408 | | |
409 | 21.5k | if (pim->Width == 0 || pim->Height == 0) { |
410 | 624 | gx_device *cdev = pie->dev; |
411 | | |
412 | 624 | gx_image_end(pie, false); |
413 | 624 | if (dev_proc(cdev, dev_spec_op)(cdev, |
414 | 624 | gxdso_pattern_is_cpath_accum, NULL, 0)) |
415 | 0 | gx_device_retain((gx_device *)cdev, false); |
416 | 624 | return 1; |
417 | 624 | } |
418 | 20.9k | image_enum_init(penum); |
419 | 20.9k | penum->dev = dev; |
420 | 20.9k | penum->info = pie; |
421 | 20.9k | penum->num_planes = pie->num_planes; |
422 | | /* |
423 | | * Note that for ImageType 3 InterleaveType 2, penum->height (the |
424 | | * expected number of data rows) differs from pim->Height (the height |
425 | | * of the source image in scan lines). This doesn't normally cause |
426 | | * any problems, because penum->height is not used to determine when |
427 | | * all the data has been processed: that is up to the plane_data |
428 | | * procedure for the specific image type. |
429 | | */ |
430 | 20.9k | penum->height = pim->Height; |
431 | 41.8k | for (i = 0; i < pie->num_planes; ++i) { |
432 | 20.9k | penum->planes[i].pos = 0; |
433 | 20.9k | penum->planes[i].source.size = 0; /* for gs_image_next_planes */ |
434 | 20.9k | penum->planes[i].source.data = 0; /* for GC */ |
435 | 20.9k | penum->planes[i].row.data = 0; /* for GC */ |
436 | 20.9k | penum->planes[i].row.size = 0; /* ditto */ |
437 | 20.9k | penum->image_planes[i].data_x = 0; /* just init once, never changes */ |
438 | 20.9k | } |
439 | | /* Initialize the dynamic part of the state. */ |
440 | 20.9k | penum->y = 0; |
441 | 20.9k | penum->error = false; |
442 | 20.9k | penum->wanted_varies = true; |
443 | 20.9k | begin_planes(penum); |
444 | 20.9k | return 0; |
445 | 21.5k | } |
446 | | |
447 | | /* Initialize an enumerator for a general image. |
448 | | penum->memory must be initialized in advance. |
449 | | */ |
450 | | int |
451 | | gs_image_enum_init(gs_image_enum * penum, gx_image_enum_common_t * pie, |
452 | | const gs_data_image_t * pim, gs_gstate *pgs) |
453 | 21.5k | { |
454 | 21.5k | pgs->device->sgr.stroke_stored = false; |
455 | 21.5k | return gs_image_common_init(penum, pie, pim, |
456 | 21.5k | (pgs->in_charpath ? NULL : |
457 | 21.5k | gs_currentdevice_inline(pgs))); |
458 | 21.5k | } |
459 | | |
460 | | /* Return the set of planes wanted. */ |
461 | | const byte * |
462 | | gs_image_planes_wanted(gs_image_enum *penum) |
463 | 13 | { |
464 | 13 | int i; |
465 | | |
466 | | /* |
467 | | * A plane is wanted at this interface if it is wanted by the |
468 | | * underlying machinery and has no buffered or retained data. |
469 | | */ |
470 | 26 | for (i = 0; i < penum->num_planes; ++i) |
471 | 13 | penum->client_wanted[i] = |
472 | 13 | (penum->wanted[i] && |
473 | 13 | penum->planes[i].pos + penum->planes[i].source.size < |
474 | 13 | penum->image_planes[i].raster); |
475 | 13 | return penum->client_wanted; |
476 | 13 | } |
477 | | |
478 | | /* |
479 | | * Return the enumerator memory used for allocating the row buffers. |
480 | | * Because some PostScript files use save/restore within an image data |
481 | | * reading procedure, this must be a stable allocator. |
482 | | */ |
483 | | static gs_memory_t * |
484 | | gs_image_row_memory(const gs_image_enum *penum) |
485 | 1.50M | { |
486 | 1.50M | return gs_memory_stable(penum->memory); |
487 | 1.50M | } |
488 | | |
489 | | /* Free the row buffers when cleaning up. */ |
490 | | static void |
491 | | free_row_buffers(gs_image_enum *penum, int num_planes, client_name_t cname) |
492 | 21.5k | { |
493 | 21.5k | int i; |
494 | | |
495 | 42.4k | for (i = num_planes - 1; i >= 0; --i) { |
496 | 20.9k | if_debug3m('b', penum->memory, "[b]free plane %d row ("PRI_INTPTR",%u)\n", |
497 | 20.9k | i, (intptr_t)penum->planes[i].row.data, |
498 | 20.9k | penum->planes[i].row.size); |
499 | 20.9k | gs_free_string(gs_image_row_memory(penum), penum->planes[i].row.data, |
500 | 20.9k | penum->planes[i].row.size, cname); |
501 | 20.9k | penum->planes[i].row.data = 0; |
502 | 20.9k | penum->planes[i].row.size = 0; |
503 | 20.9k | } |
504 | 21.5k | } |
505 | | |
506 | | /* Process the next piece of an image. */ |
507 | | int |
508 | | gs_image_next(gs_image_enum * penum, const byte * dbytes, uint dsize, |
509 | | uint * pused) |
510 | 163k | { |
511 | 163k | int px = penum->plane_index; |
512 | 163k | int num_planes = penum->num_planes; |
513 | 163k | int i, code; |
514 | 163k | uint used[GS_IMAGE_MAX_COMPONENTS]; |
515 | 163k | gs_const_string plane_data[GS_IMAGE_MAX_COMPONENTS]; |
516 | | |
517 | 163k | if (penum->planes[px].source.size != 0) |
518 | 0 | return_error(gs_error_rangecheck); |
519 | 327k | for (i = 0; i < num_planes; i++) |
520 | 163k | plane_data[i].size = 0; |
521 | 163k | plane_data[px].data = dbytes; |
522 | 163k | plane_data[px].size = dsize; |
523 | 163k | penum->error = false; |
524 | 163k | code = gs_image_next_planes(penum, plane_data, used, false); |
525 | 163k | *pused = used[px]; |
526 | 163k | if (code >= 0) |
527 | 163k | next_plane(penum); |
528 | 163k | return code; |
529 | 163k | } |
530 | | |
531 | | int |
532 | | gs_image_next_planes(gs_image_enum * penum, |
533 | | gs_const_string *plane_data /*[num_planes]*/, |
534 | | uint *used /*[num_planes]*/, bool txfer_control) |
535 | 1.18M | { |
536 | 1.18M | const int num_planes = penum->num_planes; |
537 | 1.18M | int i; |
538 | 1.18M | int code = 0; |
539 | | |
540 | | #ifdef DEBUG |
541 | | if (gs_debug_c('b')) { |
542 | | int pi; |
543 | | |
544 | | for (pi = 0; pi < num_planes; ++pi) |
545 | | dmprintf6(penum->memory, "[b]plane %d source="PRI_INTPTR",%u pos=%u data="PRI_INTPTR",%u\n", |
546 | | pi, (intptr_t)penum->planes[pi].source.data, |
547 | | penum->planes[pi].source.size, penum->planes[pi].pos, |
548 | | (intptr_t)plane_data[pi].data, plane_data[pi].size); |
549 | | } |
550 | | #endif |
551 | 2.38M | for (i = 0; i < num_planes; ++i) { |
552 | 1.19M | used[i] = 0; |
553 | 1.19M | if (penum->wanted[i] && plane_data[i].size != 0) { |
554 | 1.19M | penum->planes[i].source.size = plane_data[i].size; |
555 | 1.19M | penum->planes[i].source.data = plane_data[i].data; |
556 | | /* The gs_string 'orig' in penum->planes is set here if the 'txfer_control' flag is set. |
557 | | * In this case we now control the lifetime of the string. We need to know the actual |
558 | | * address of the string, and that gets modified in the peunum->planes->source and size |
559 | | * members, so we use 'orig' as both a marker for the control and the originalsize and location. |
560 | | */ |
561 | 1.19M | if (txfer_control) { |
562 | 8 | penum->planes[i].orig.data = plane_data[i].data; |
563 | 8 | penum->planes[i].orig.size = plane_data[i].size; |
564 | 1.19M | } else { |
565 | 1.19M | penum->planes[i].orig.data = NULL; |
566 | 1.19M | penum->planes[i].orig.size = 0; |
567 | 1.19M | } |
568 | 1.19M | } |
569 | 1.19M | } |
570 | 1.98M | for (;;) { |
571 | | /* If wanted can vary, only transfer 1 row at a time. */ |
572 | 1.98M | int h = (penum->wanted_varies ? 1 : max_int); |
573 | | |
574 | | /* Move partial rows from source[] to row[]. */ |
575 | 3.97M | for (i = 0; i < num_planes; ++i) { |
576 | 1.99M | int pos, size; |
577 | 1.99M | uint raster; |
578 | | |
579 | 1.99M | if (!penum->wanted[i]) |
580 | 87 | continue; /* skip unwanted planes */ |
581 | 1.99M | pos = penum->planes[i].pos; |
582 | 1.99M | size = penum->planes[i].source.size; |
583 | 1.99M | raster = penum->image_planes[i].raster; |
584 | 1.99M | if (size > 0) { |
585 | 1.76M | if (pos < raster && (pos != 0 || size < raster)) { |
586 | | /* Buffer a partial row. */ |
587 | 1.46M | int copy = min(size, raster - pos); |
588 | 1.46M | uint old_size = penum->planes[i].row.size; |
589 | 1.46M | gs_memory_t *mem = gs_image_row_memory(penum); |
590 | | |
591 | | /* Make sure the row buffer is fully allocated. */ |
592 | 1.46M | if (raster > old_size) { |
593 | 5.52k | byte *old_data = penum->planes[i].row.data; |
594 | 5.52k | byte *row = |
595 | 5.52k | (old_data == 0 ? |
596 | 5.52k | gs_alloc_string(mem, raster, |
597 | 5.52k | "gs_image_next(row)") : |
598 | 5.52k | gs_resize_string(mem, old_data, old_size, raster, |
599 | 5.52k | "gs_image_next(row)")); |
600 | | |
601 | 5.52k | if_debug5m('b', mem, "[b]plane %d row ("PRI_INTPTR",%u) => ("PRI_INTPTR",%u)\n", |
602 | 5.52k | i, (intptr_t)old_data, old_size, |
603 | 5.52k | (intptr_t)row, raster); |
604 | 5.52k | if (row == 0) { |
605 | 0 | code = gs_note_error(gs_error_VMerror); |
606 | 0 | free_row_buffers(penum, i, "gs_image_next(row)"); |
607 | 0 | break; |
608 | 0 | } |
609 | 5.52k | penum->planes[i].row.data = row; |
610 | 5.52k | penum->planes[i].row.size = raster; |
611 | 5.52k | } |
612 | 1.46M | memcpy(penum->planes[i].row.data + pos, |
613 | 1.46M | penum->planes[i].source.data, copy); |
614 | 1.46M | penum->planes[i].source.data += copy; |
615 | 1.46M | penum->planes[i].source.size = size -= copy; |
616 | | /* The gs_string 'orig' is only set if the 'txfer_control' flag was set when |
617 | | * the 'source' string data was initally passed in. In this case we now control the lifetime |
618 | | * of the string. So when we empty the source string, free it. We need to know the actual |
619 | | * address of the string, and that gets modified in the peunum->planes->source and size |
620 | | * members, so we use 'orig' as both a marker for the control and the originalsize and location. |
621 | | */ |
622 | 1.46M | if (penum->planes[i].source.size == 0 && penum->planes[i].orig.size != 0) { |
623 | 4 | gs_free_string(mem, (byte *)penum->planes[i].orig.data, penum->planes[i].orig.size, "gs_image_next_planes"); |
624 | 4 | penum->planes[i].orig.size = 0; |
625 | 4 | penum->planes[i].orig.data = NULL; |
626 | 4 | } |
627 | 1.46M | penum->planes[i].pos = pos += copy; |
628 | 1.46M | used[i] += copy; |
629 | 1.46M | } |
630 | 1.76M | } |
631 | 1.99M | if (h == 0) |
632 | 36 | continue; /* can't transfer any data this cycle */ |
633 | 1.99M | if (pos == raster) { |
634 | | /* |
635 | | * This plane will be transferred from the row buffer, |
636 | | * so we can only transfer one row. |
637 | | */ |
638 | 518k | h = min(h, 1); |
639 | 518k | penum->image_planes[i].data = penum->planes[i].row.data; |
640 | 1.47M | } else if (pos == 0 && size >= raster) { |
641 | | /* We can transfer 1 or more planes from the source. */ |
642 | 304k | if (raster) { |
643 | 304k | h = min(h, size / raster); |
644 | 304k | penum->image_planes[i].data = penum->planes[i].source.data; |
645 | 304k | } |
646 | 0 | else |
647 | 0 | h = 0; |
648 | 304k | } else |
649 | 1.16M | h = 0; /* not enough data in this plane */ |
650 | 1.99M | } |
651 | 1.98M | if (h == 0 || code != 0) |
652 | 1.16M | break; |
653 | | /* Pass rows to the device. */ |
654 | 814k | if (penum->dev == 0) { |
655 | | /* |
656 | | * ****** NOTE: THE FOLLOWING IS NOT CORRECT FOR ImageType 3 |
657 | | * ****** InterleaveType 2, SINCE MASK HEIGHT AND IMAGE HEIGHT |
658 | | * ****** MAY DIFFER (BY AN INTEGER FACTOR). ALSO, plane_depths[0] |
659 | | * ****** AND plane_widths[0] ARE NOT UPDATED. |
660 | | */ |
661 | 0 | if (penum->y + h < penum->height) |
662 | 0 | code = 0; |
663 | 0 | else |
664 | 0 | h = penum->height - penum->y, code = 1; |
665 | 814k | } else { |
666 | 814k | code = gx_image_plane_data_rows(penum->info, penum->image_planes, |
667 | 814k | h, &h); |
668 | 814k | if_debug2m('b', penum->memory, "[b]used %d, code=%d\n", h, code); |
669 | 814k | penum->error = code < 0; |
670 | 814k | } |
671 | 814k | penum->y += h; |
672 | | /* Update positions and sizes. */ |
673 | 814k | if (h == 0) |
674 | 0 | break; |
675 | 1.63M | for (i = 0; i < num_planes; ++i) { |
676 | 820k | int count; |
677 | | |
678 | 820k | if (!penum->wanted[i]) |
679 | 84 | continue; |
680 | 820k | count = penum->image_planes[i].raster * h; |
681 | 820k | if (penum->planes[i].pos) { |
682 | | /* We transferred the row from the row buffer. */ |
683 | 518k | penum->planes[i].pos = 0; |
684 | 518k | } else { |
685 | | /* We transferred the row(s) from the source. */ |
686 | 302k | penum->planes[i].source.data += count; |
687 | 302k | penum->planes[i].source.size -= count; |
688 | | /* The gs_string 'orig' is only set if the 'txfer_control' flag was set when |
689 | | * the 'source' string data was initally passed in. In this case we now control the lifetime |
690 | | * of the string. So when we empty the source string, free it. We need to know the actual |
691 | | * address of the string, and that gets modified in the peunum->planes->source and size |
692 | | * members, so we use 'orig' as both a marker for the control and the originalsize and location. |
693 | | */ |
694 | 302k | if (penum->planes[i].source.size == 0 && penum->planes[i].orig.size != 0) { |
695 | 3 | gs_free_string(gs_image_row_memory(penum), (byte *)penum->planes[i].orig.data, penum->planes[i].orig.size, "gs_image_next_planes"); |
696 | 3 | penum->planes[i].orig.size = 0; |
697 | 3 | penum->planes[i].orig.data = NULL; |
698 | 3 | } |
699 | 302k | used[i] += count; |
700 | 302k | } |
701 | 820k | } |
702 | 814k | cache_planes(penum); |
703 | 814k | if (code != 0) |
704 | 19.3k | break; |
705 | 814k | } |
706 | | /* Return the retained data pointers. */ |
707 | 2.38M | for (i = 0; i < num_planes; ++i) |
708 | 1.19M | plane_data[i] = penum->planes[i].source; |
709 | 1.18M | return code; |
710 | 1.18M | } |
711 | | |
712 | | /* Clean up after processing an image. */ |
713 | | /* Public for ghostpcl. */ |
714 | | int |
715 | | gs_image_cleanup(gs_image_enum * penum, gs_gstate *pgs) |
716 | 21.5k | { |
717 | 21.5k | int code = 0, code1; |
718 | | |
719 | 21.5k | free_row_buffers(penum, penum->num_planes, "gs_image_cleanup(row)"); |
720 | 21.5k | if (penum->info != 0) { |
721 | 20.9k | if (dev_proc(penum->info->dev, dev_spec_op)(penum->info->dev, |
722 | 20.9k | gxdso_pattern_is_cpath_accum, NULL, 0)) { |
723 | | /* Performing a conversion of imagemask into a clipping path. */ |
724 | 16 | gx_device *cdev = penum->info->dev; |
725 | | |
726 | 16 | code = gx_image_end(penum->info, !penum->error); /* Releases penum->info . */ |
727 | 16 | code1 = gx_image_fill_masked_end(cdev, penum->dev, gs_currentdevicecolor_inline(pgs)); |
728 | 16 | if (code == 0) |
729 | 16 | code = code1; |
730 | 16 | } else |
731 | 20.8k | code = gx_image_end(penum->info, !penum->error); |
732 | 20.9k | } |
733 | | /* Don't free the local enumerator -- the client does that. */ |
734 | | |
735 | 21.5k | return code; |
736 | 21.5k | } |
737 | | |
738 | | /* Clean up after processing an image and free the enumerator. */ |
739 | | int |
740 | | gs_image_cleanup_and_free_enum(gs_image_enum * penum, gs_gstate *pgs) |
741 | 21.5k | { |
742 | 21.5k | int code; |
743 | | |
744 | 21.5k | if (penum == NULL) |
745 | 0 | return 0; |
746 | 21.5k | code = gs_image_cleanup(penum, pgs); |
747 | | |
748 | 21.5k | gs_free_object(penum->memory, penum, "gs_image_cleanup_and_free_enum"); |
749 | 21.5k | return code; |
750 | 21.5k | } |