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