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