1 /* Copyright (C) 2001-2012 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
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 "vdtrace.h"
34 #include "gxdevsop.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 } image_enum_plane_t;
61 /*
62 The possible states for each plane do not depend on the state of any other
63 plane. Either:
64
65 - pos = 0, source.size = 0.
66
67 - If the underlying image processor says the plane is currently wanted,
68 either:
69
70 - pos = 0, source.size >= one full row of data for this plane. This
71 case allows us to avoid copying the data from the source string to the
72 row buffer if the client is providing data in blocks of at least one
73 scan line.
74
75 - pos = full, source.size may have any value.
76
77 - pos > 0, pos < full, source.size = 0;
78
79 - If the underlying image processor says the plane is not currently
80 wanted:
81
82 - pos = 0, source.size may have any value.
83
84 This invariant holds at the beginning and end of each call on
85 gs_image_next_planes. Note that for each plane, the "plane wanted" status
86 and size of a full row may change after each call of plane_data. As
87 documented in gxiparam.h, we assume that a call of plane_data can only
88 change a plane's status from "wanted" to "not wanted", or change the width
89 or depth of a wanted plane, if data for that plane was actually supplied
90 (and used).
91 */
92
93 /* Define the enumeration state for this interface layer. */
94 /*typedef struct gs_image_enum_s gs_image_enum; *//* in gsimage.h */
95 struct gs_image_enum_s {
96 /* The following are set at initialization time. */
97 gs_memory_t *memory;
98 gx_device *dev; /* if 0, just skip over the data */
99 gx_image_enum_common_t *info; /* driver bookkeeping structure */
100 int num_planes;
101 int height;
102 bool wanted_varies;
103 /* The following are updated dynamically. */
104 int plane_index; /* index of next plane of data, */
105 /* only needed for gs_image_next */
106 int y;
107 bool error;
108 byte wanted[GS_IMAGE_MAX_COMPONENTS]; /* cache gx_image_planes_wanted */
109 byte client_wanted[GS_IMAGE_MAX_COMPONENTS]; /* see gsimage.h */
110 image_enum_plane_t planes[GS_IMAGE_MAX_COMPONENTS]; /* see above */
111 /*
112 * To reduce setup for transferring complete rows, we maintain a
113 * partially initialized parameter array for gx_image_plane_data_rows.
114 * The data member is always set just before calling
115 * gx_image_plane_data_rows; the data_x and raster members are reset
116 * when needed.
117 */
118 gx_image_plane_t image_planes[GS_IMAGE_MAX_COMPONENTS];
119 };
120
121 gs_private_st_composite(st_gs_image_enum, gs_image_enum, "gs_image_enum",
122 gs_image_enum_enum_ptrs, gs_image_enum_reloc_ptrs);
123 #define gs_image_enum_num_ptrs 2
124
125 /* GC procedures */
126 static
ENUM_PTRS_WITH(gs_image_enum_enum_ptrs,gs_image_enum * eptr)127 ENUM_PTRS_WITH(gs_image_enum_enum_ptrs, gs_image_enum *eptr)
128 {
129 /* Enumerate the data planes. */
130 index -= gs_image_enum_num_ptrs;
131 if (index < eptr->num_planes)
132 ENUM_RETURN_STRING_PTR(gs_image_enum, planes[index].source);
133 index -= eptr->num_planes;
134 if (index < eptr->num_planes)
135 ENUM_RETURN_STRING_PTR(gs_image_enum, planes[index].row);
136 return 0;
137 }
138 ENUM_PTR(0, gs_image_enum, dev);
139 ENUM_PTR(1, gs_image_enum, info);
140 ENUM_PTRS_END
RELOC_PTRS_WITH(gs_image_enum_reloc_ptrs,gs_image_enum * eptr)141 static RELOC_PTRS_WITH(gs_image_enum_reloc_ptrs, gs_image_enum *eptr)
142 {
143 int i;
144
145 RELOC_PTR(gs_image_enum, dev);
146 RELOC_PTR(gs_image_enum, info);
147 for (i = 0; i < eptr->num_planes; i++)
148 RELOC_CONST_STRING_PTR(gs_image_enum, planes[i].source);
149 for (i = 0; i < eptr->num_planes; i++)
150 RELOC_STRING_PTR(gs_image_enum, planes[i].row);
151 }
152 RELOC_PTRS_END
153
154 static int
is_image_visible(const gs_image_common_t * pic,gs_state * pgs,gx_clip_path * pcpath)155 is_image_visible(const gs_image_common_t * pic, gs_state * pgs, gx_clip_path *pcpath)
156 {
157 /* HACK : We need the source image size here,
158 but gs_image_common_t doesn't pass it.
159 We would like to move Width, Height to gs_image_common,
160 but gs_image2_t appears to have those fields of double type.
161 */
162 if (pic->type->begin_typed_image == gx_begin_image1) {
163 gs_image1_t *pim = (gs_image1_t *) pic;
164 gs_rect image_rect = {{0, 0}, {0, 0}};
165 gs_rect device_rect;
166 gs_int_rect device_int_rect;
167 gs_matrix mat;
168 int code;
169
170 image_rect.q.x = pim->Width;
171 image_rect.q.y = pim->Height;
172 if (pic->ImageMatrix.xx == ctm_only(pgs).xx &&
173 pic->ImageMatrix.xy == ctm_only(pgs).xy &&
174 pic->ImageMatrix.yx == ctm_only(pgs).yx &&
175 pic->ImageMatrix.yy == ctm_only(pgs).yy) {
176 /* Handle common special case separately to accept singular matrix */
177 mat.xx = mat.yy = 1.;
178 mat.yx = mat.xy = 0.;
179 mat.tx = ctm_only(pgs).tx - pic->ImageMatrix.tx;
180 mat.ty = ctm_only(pgs).ty - pic->ImageMatrix.ty;
181 } else {
182 code = gs_matrix_invert(&pic->ImageMatrix, &mat);
183 if (code < 0)
184 return code;
185 code = gs_matrix_multiply(&mat, &ctm_only(pgs), &mat);
186 if (code < 0)
187 return code;
188 }
189 code = gs_bbox_transform(&image_rect, &mat, &device_rect);
190 if (code < 0)
191 return code;
192 device_int_rect.p.x = (int)floor(device_rect.p.x);
193 device_int_rect.p.y = (int)floor(device_rect.p.y);
194 device_int_rect.q.x = (int)ceil(device_rect.q.x);
195 device_int_rect.q.y = (int)ceil(device_rect.q.y);
196 if (!gx_cpath_rect_visible(pcpath, &device_int_rect))
197 return 0;
198 }
199 return 1;
200 }
201
202 /* Create an image enumerator given image parameters and a graphics state. */
203 int
gs_image_begin_typed(const gs_image_common_t * pic,gs_state * pgs,bool uses_color,gx_image_enum_common_t ** ppie)204 gs_image_begin_typed(const gs_image_common_t * pic, gs_state * pgs,
205 bool uses_color, gx_image_enum_common_t ** ppie)
206 {
207 gx_device *dev = gs_currentdevice(pgs);
208 gx_clip_path *pcpath;
209 int code = gx_effective_clip_path(pgs, &pcpath);
210 gx_device *dev2 = dev;
211 gx_device_color dc_temp, *pdevc = gs_currentdevicecolor_inline(pgs);
212
213 if (code < 0)
214 return code;
215 /* Processing an image object operation */
216 dev_proc(pgs->device, set_graphics_type_tag)(pgs->device, GS_IMAGE_TAG);
217
218 if (uses_color) {
219 code = gx_set_dev_color(pgs);
220 if (code != 0)
221 return code;
222 code = gs_state_color_load(pgs);
223 if (code < 0)
224 return code;
225 }
226 /* Imagemask with shading color needs a special optimization
227 with converting the image into a clipping.
228 Check for such case after gs_state_color_load is done,
229 because it can cause interpreter callout.
230 */
231 if (pic->type->begin_typed_image == &gx_begin_image1) {
232 gs_image_t *image = (gs_image_t *)pic;
233
234 if(image->ImageMask) {
235 code = gx_image_fill_masked_start(dev, gs_currentdevicecolor_inline(pgs), pcpath, pgs->memory, &dev2);
236 if (code < 0)
237 return code;
238 }
239 if (dev2 != dev) {
240 set_nonclient_dev_color(&dc_temp, 1);
241 pdevc = &dc_temp;
242 }
243 }
244 code = gx_device_begin_typed_image(dev2, (const gs_imager_state *)pgs,
245 NULL, pic, NULL, pdevc, pcpath, pgs->memory, ppie);
246 if (code < 0)
247 return code;
248 code = is_image_visible(pic, pgs, pcpath);
249 if (code < 0)
250 return code;
251 if (!code)
252 (*ppie)->skipping = true;
253 return 0;
254 }
255
256 /* Allocate an image enumerator. */
257 static void
image_enum_init(gs_image_enum * penum)258 image_enum_init(gs_image_enum * penum)
259 {
260 /* Clean pointers for GC. */
261 penum->info = 0;
262 penum->dev = 0;
263 penum->plane_index = 0;
264 penum->num_planes = 0;
265 }
266 gs_image_enum *
gs_image_enum_alloc(gs_memory_t * mem,client_name_t cname)267 gs_image_enum_alloc(gs_memory_t * mem, client_name_t cname)
268 {
269 gs_image_enum *penum =
270 gs_alloc_struct(mem, gs_image_enum, &st_gs_image_enum, cname);
271
272 if (penum != 0) {
273 penum->memory = mem;
274 image_enum_init(penum);
275 }
276 return penum;
277 }
278
279 /* Start processing an ImageType 1 image. */
280 int
gs_image_init(gs_image_enum * penum,const gs_image_t * pim,bool multi,gs_state * pgs)281 gs_image_init(gs_image_enum * penum, const gs_image_t * pim, bool multi,
282 gs_state * pgs)
283 {
284 gs_image_t image;
285 gx_image_enum_common_t *pie;
286 int code;
287
288 image = *pim;
289 if (image.ImageMask) {
290 image.ColorSpace = NULL;
291 if (pgs->in_cachedevice <= 1)
292 image.adjust = false;
293 } else {
294 if (pgs->in_cachedevice)
295 return_error(gs_error_undefined);
296 if (image.ColorSpace == NULL) {
297 /*
298 * Use of a non-current color space is potentially
299 * incorrect, but it appears this case doesn't arise.
300 */
301 image.ColorSpace = gs_cspace_new_DeviceGray(pgs->memory);
302 }
303 }
304 code = gs_image_begin_typed((const gs_image_common_t *)&image, pgs,
305 image.ImageMask | image.CombineWithColor,
306 &pie);
307 if (code < 0)
308 return code;
309 return gs_image_enum_init(penum, pie, (const gs_data_image_t *)&image,
310 pgs);
311 }
312
313 /*
314 * Return the number of bytes of data per row for a given plane.
315 */
316 inline uint
gs_image_bytes_per_plane_row(const gs_image_enum * penum,int plane)317 gs_image_bytes_per_plane_row(const gs_image_enum * penum, int plane)
318 {
319 const gx_image_enum_common_t *pie = penum->info;
320
321 return (pie->plane_widths[plane] * pie->plane_depths[plane] + 7) >> 3;
322 }
323
324 /* Cache information when initializing, or after transferring plane data. */
325 static void
cache_planes(gs_image_enum * penum)326 cache_planes(gs_image_enum *penum)
327 {
328 int i;
329
330 if (penum->wanted_varies) {
331 penum->wanted_varies =
332 !gx_image_planes_wanted(penum->info, penum->wanted);
333 for (i = 0; i < penum->num_planes; ++i)
334 if (penum->wanted[i])
335 penum->image_planes[i].raster =
336 gs_image_bytes_per_plane_row(penum, i);
337 else
338 penum->image_planes[i].data = 0;
339 }
340 }
341 /* Advance to the next wanted plane. */
342 static void
next_plane(gs_image_enum * penum)343 next_plane(gs_image_enum *penum)
344 {
345 int px = penum->plane_index;
346
347 do {
348 if (++px == penum->num_planes)
349 px = 0;
350 } while (!penum->wanted[px]);
351 penum->plane_index = px;
352 }
353 /*
354 * Initialize plane_index and (if appropriate) wanted and
355 * wanted_varies at the beginning of a group of planes.
356 */
357 static void
begin_planes(gs_image_enum * penum)358 begin_planes(gs_image_enum *penum)
359 {
360 cache_planes(penum);
361 penum->plane_index = -1;
362 next_plane(penum);
363 }
364
365 int
gs_image_common_init(gs_image_enum * penum,gx_image_enum_common_t * pie,const gs_data_image_t * pim,gx_device * dev)366 gs_image_common_init(gs_image_enum * penum, gx_image_enum_common_t * pie,
367 const gs_data_image_t * pim, gx_device * dev)
368 {
369 /*
370 * HACK : For a compatibility with gs_image_cleanup_and_free_enum,
371 * penum->memory must be initialized in advance
372 * with the memory heap that owns *penum.
373 */
374 int i;
375
376 if (pim->Width == 0 || pim->Height == 0) {
377 gx_image_end(pie, false);
378 return 1;
379 }
380 image_enum_init(penum);
381 penum->dev = dev;
382 penum->info = pie;
383 penum->num_planes = pie->num_planes;
384 /*
385 * Note that for ImageType 3 InterleaveType 2, penum->height (the
386 * expected number of data rows) differs from pim->Height (the height
387 * of the source image in scan lines). This doesn't normally cause
388 * any problems, because penum->height is not used to determine when
389 * all the data has been processed: that is up to the plane_data
390 * procedure for the specific image type.
391 */
392 penum->height = pim->Height;
393 for (i = 0; i < pie->num_planes; ++i) {
394 penum->planes[i].pos = 0;
395 penum->planes[i].source.size = 0; /* for gs_image_next_planes */
396 penum->planes[i].source.data = 0; /* for GC */
397 penum->planes[i].row.data = 0; /* for GC */
398 penum->planes[i].row.size = 0; /* ditto */
399 penum->image_planes[i].data_x = 0; /* just init once, never changes */
400 }
401 /* Initialize the dynamic part of the state. */
402 penum->y = 0;
403 penum->error = false;
404 penum->wanted_varies = true;
405 begin_planes(penum);
406 return 0;
407 }
408
409 /* Initialize an enumerator for a general image.
410 penum->memory must be initialized in advance.
411 */
412 int
gs_image_enum_init(gs_image_enum * penum,gx_image_enum_common_t * pie,const gs_data_image_t * pim,gs_state * pgs)413 gs_image_enum_init(gs_image_enum * penum, gx_image_enum_common_t * pie,
414 const gs_data_image_t * pim, gs_state *pgs)
415 {
416 pgs->device->sgr.stroke_stored = false;
417 return gs_image_common_init(penum, pie, pim,
418 (pgs->in_charpath ? NULL :
419 gs_currentdevice_inline(pgs)));
420 }
421
422 /* Return the set of planes wanted. */
423 const byte *
gs_image_planes_wanted(gs_image_enum * penum)424 gs_image_planes_wanted(gs_image_enum *penum)
425 {
426 int i;
427
428 /*
429 * A plane is wanted at this interface if it is wanted by the
430 * underlying machinery and has no buffered or retained data.
431 */
432 for (i = 0; i < penum->num_planes; ++i)
433 penum->client_wanted[i] =
434 (penum->wanted[i] &&
435 penum->planes[i].pos + penum->planes[i].source.size <
436 penum->image_planes[i].raster);
437 return penum->client_wanted;
438 }
439
440 /*
441 * Return the enumerator memory used for allocating the row buffers.
442 * Because some PostScript files use save/restore within an image data
443 * reading procedure, this must be a stable allocator.
444 */
445 static gs_memory_t *
gs_image_row_memory(const gs_image_enum * penum)446 gs_image_row_memory(const gs_image_enum *penum)
447 {
448 return gs_memory_stable(penum->memory);
449 }
450
451 /* Free the row buffers when cleaning up. */
452 static void
free_row_buffers(gs_image_enum * penum,int num_planes,client_name_t cname)453 free_row_buffers(gs_image_enum *penum, int num_planes, client_name_t cname)
454 {
455 int i;
456
457 for (i = num_planes - 1; i >= 0; --i) {
458 if_debug3('b', "[b]free plane %d row (0x%lx,%u)\n",
459 i, (ulong)penum->planes[i].row.data,
460 penum->planes[i].row.size);
461 gs_free_string(gs_image_row_memory(penum), penum->planes[i].row.data,
462 penum->planes[i].row.size, cname);
463 penum->planes[i].row.data = 0;
464 penum->planes[i].row.size = 0;
465 }
466 }
467
468 /* Process the next piece of an image. */
469 int
gs_image_next(gs_image_enum * penum,const byte * dbytes,uint dsize,uint * pused)470 gs_image_next(gs_image_enum * penum, const byte * dbytes, uint dsize,
471 uint * pused)
472 {
473 int px = penum->plane_index;
474 int num_planes = penum->num_planes;
475 int i, code;
476 uint used[GS_IMAGE_MAX_COMPONENTS];
477 gs_const_string plane_data[GS_IMAGE_MAX_COMPONENTS];
478
479 if (penum->planes[px].source.size != 0)
480 return_error(gs_error_rangecheck);
481 for (i = 0; i < num_planes; i++)
482 plane_data[i].size = 0;
483 plane_data[px].data = dbytes;
484 plane_data[px].size = dsize;
485 penum->error = false;
486 code = gs_image_next_planes(penum, plane_data, used);
487 *pused = used[px];
488 if (code >= 0)
489 next_plane(penum);
490 return code;
491 }
492
493 int
gs_image_next_planes(gs_image_enum * penum,gs_const_string * plane_data,uint * used)494 gs_image_next_planes(gs_image_enum * penum,
495 gs_const_string *plane_data /*[num_planes]*/,
496 uint *used /*[num_planes]*/)
497 {
498 const int num_planes = penum->num_planes;
499 int i;
500 int code = 0;
501
502 #ifdef DEBUG
503 vd_get_dc('i');
504 vd_set_shift(0, 0);
505 vd_set_scale(0.01);
506 vd_set_origin(0, 0);
507 if (gs_debug_c('b')) {
508 int pi;
509
510 for (pi = 0; pi < num_planes; ++pi)
511 dprintf6("[b]plane %d source=0x%lx,%u pos=%u data=0x%lx,%u\n",
512 pi, (ulong)penum->planes[pi].source.data,
513 penum->planes[pi].source.size, penum->planes[pi].pos,
514 (ulong)plane_data[pi].data, plane_data[pi].size);
515 }
516 #endif
517 for (i = 0; i < num_planes; ++i) {
518 used[i] = 0;
519 if (penum->wanted[i] && plane_data[i].size != 0) {
520 penum->planes[i].source.size = plane_data[i].size;
521 penum->planes[i].source.data = plane_data[i].data;
522 }
523 }
524 for (;;) {
525 /* If wanted can vary, only transfer 1 row at a time. */
526 int h = (penum->wanted_varies ? 1 : max_int);
527
528 /* Move partial rows from source[] to row[]. */
529 for (i = 0; i < num_planes; ++i) {
530 int pos, size;
531 uint raster;
532
533 if (!penum->wanted[i])
534 continue; /* skip unwanted planes */
535 pos = penum->planes[i].pos;
536 size = penum->planes[i].source.size;
537 raster = penum->image_planes[i].raster;
538 if (size > 0) {
539 if (pos < raster && (pos != 0 || size < raster)) {
540 /* Buffer a partial row. */
541 int copy = min(size, raster - pos);
542 uint old_size = penum->planes[i].row.size;
543
544 /* Make sure the row buffer is fully allocated. */
545 if (raster > old_size) {
546 gs_memory_t *mem = gs_image_row_memory(penum);
547 byte *old_data = penum->planes[i].row.data;
548 byte *row =
549 (old_data == 0 ?
550 gs_alloc_string(mem, raster,
551 "gs_image_next(row)") :
552 gs_resize_string(mem, old_data, old_size, raster,
553 "gs_image_next(row)"));
554
555 if_debug5('b', "[b]plane %d row (0x%lx,%u) => (0x%lx,%u)\n",
556 i, (ulong)old_data, old_size,
557 (ulong)row, raster);
558 if (row == 0) {
559 code = gs_note_error(gs_error_VMerror);
560 free_row_buffers(penum, i, "gs_image_next(row)");
561 break;
562 }
563 penum->planes[i].row.data = row;
564 penum->planes[i].row.size = raster;
565 }
566 memcpy(penum->planes[i].row.data + pos,
567 penum->planes[i].source.data, copy);
568 penum->planes[i].source.data += copy;
569 penum->planes[i].source.size = size -= copy;
570 penum->planes[i].pos = pos += copy;
571 used[i] += copy;
572 }
573 }
574 if (h == 0)
575 continue; /* can't transfer any data this cycle */
576 if (pos == raster) {
577 /*
578 * This plane will be transferred from the row buffer,
579 * so we can only transfer one row.
580 */
581 h = min(h, 1);
582 penum->image_planes[i].data = penum->planes[i].row.data;
583 } else if (pos == 0 && size >= raster) {
584 /* We can transfer 1 or more planes from the source. */
585 h = min(h, size / raster);
586 penum->image_planes[i].data = penum->planes[i].source.data;
587 } else
588 h = 0; /* not enough data in this plane */
589 }
590 if (h == 0 || code != 0)
591 break;
592 /* Pass rows to the device. */
593 if (penum->dev == 0) {
594 /*
595 * ****** NOTE: THE FOLLOWING IS NOT CORRECT FOR ImageType 3
596 * ****** InterleaveType 2, SINCE MASK HEIGHT AND IMAGE HEIGHT
597 * ****** MAY DIFFER (BY AN INTEGER FACTOR). ALSO, plane_depths[0]
598 * ****** AND plane_widths[0] ARE NOT UPDATED.
599 */
600 if (penum->y + h < penum->height)
601 code = 0;
602 else
603 h = penum->height - penum->y, code = 1;
604 } else {
605 code = gx_image_plane_data_rows(penum->info, penum->image_planes,
606 h, &h);
607 if_debug2('b', "[b]used %d, code=%d\n", h, code);
608 penum->error = code < 0;
609 }
610 penum->y += h;
611 /* Update positions and sizes. */
612 if (h == 0)
613 break;
614 for (i = 0; i < num_planes; ++i) {
615 int count;
616
617 if (!penum->wanted[i])
618 continue;
619 count = penum->image_planes[i].raster * h;
620 if (penum->planes[i].pos) {
621 /* We transferred the row from the row buffer. */
622 penum->planes[i].pos = 0;
623 } else {
624 /* We transferred the row(s) from the source. */
625 penum->planes[i].source.data += count;
626 penum->planes[i].source.size -= count;
627 used[i] += count;
628 }
629 }
630 cache_planes(penum);
631 if (code > 0)
632 break;
633 }
634 /* Return the retained data pointers. */
635 for (i = 0; i < num_planes; ++i)
636 plane_data[i] = penum->planes[i].source;
637 vd_release_dc;
638 return code;
639 }
640
641 /* Clean up after processing an image. */
642 /* Public for ghostpcl. */
643 int
gs_image_cleanup(gs_image_enum * penum,gs_state * pgs)644 gs_image_cleanup(gs_image_enum * penum, gs_state *pgs)
645 {
646 int code = 0, code1;
647
648 free_row_buffers(penum, penum->num_planes, "gs_image_cleanup(row)");
649 if (penum->info != 0) {
650 if (dev_proc(penum->info->dev, dev_spec_op)(penum->info->dev,
651 gxdso_pattern_is_cpath_accum, NULL, 0)) {
652 /* Performing a conversion of imagemask into a clipping path. */
653 gx_device *cdev = penum->info->dev;
654
655 code = gx_image_end(penum->info, !penum->error); /* Releases penum->info . */
656 code1 = gx_image_fill_masked_end(cdev, penum->dev, gs_currentdevicecolor_inline(pgs));
657 if (code == 0)
658 code = code1;
659 } else
660 code = gx_image_end(penum->info, !penum->error);
661 }
662 /* Don't free the local enumerator -- the client does that. */
663
664 return code;
665 }
666
667 /* Clean up after processing an image and free the enumerator. */
668 int
gs_image_cleanup_and_free_enum(gs_image_enum * penum,gs_state * pgs)669 gs_image_cleanup_and_free_enum(gs_image_enum * penum, gs_state *pgs)
670 {
671 int code = gs_image_cleanup(penum, pgs);
672
673 gs_free_object(penum->memory, penum, "gs_image_cleanup_and_free_enum");
674 return code;
675 }
676