xref: /plan9/sys/src/cmd/gs/src/zimage.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
1 /* Copyright (C) 1989, 1995, 1996, 1997, 1998, 1999, 2000 Aladdin Enterprises.  All rights reserved.
2 
3   This software is provided AS-IS with no warranty, either express or
4   implied.
5 
6   This software is distributed under license and may not be copied,
7   modified or distributed except as expressly authorized under the terms
8   of the license contained in the file LICENSE in this distribution.
9 
10   For more information about licensing, please refer to
11   http://www.ghostscript.com/licensing/. For information on
12   commercial licensing, go to http://www.artifex.com/licensing/ or
13   contact Artifex Software, Inc., 101 Lucas Valley Road #110,
14   San Rafael, CA  94903, U.S.A., +1(415)492-9861.
15 */
16 
17 /* $Id: zimage.c,v 1.15 2005/06/15 18:40:08 igor Exp $ */
18 /* Image operators */
19 #include "math_.h"
20 #include "memory_.h"
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 
42 /* Forward references */
43 private int zimage_data_setup(i_ctx_t *i_ctx_p, const gs_pixel_image_t * pim,
44 				 gx_image_enum_common_t * pie,
45 				 const ref * sources, int npop);
46 private int image_proc_process(i_ctx_t *);
47 private int image_file_continue(i_ctx_t *);
48 private int image_string_continue(i_ctx_t *);
49 private int image_cleanup(i_ctx_t *);
50 
51 
52 
53 /* Extract and check the parameters for a gs_data_image_t. */
54 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)55 data_image_params(const gs_memory_t *mem,
56 		  const ref *op, gs_data_image_t *pim,
57 		  image_params *pip, bool require_DataSource,
58 		  int num_components, int max_bits_per_component,
59 		  bool has_alpha)
60 {
61     int code;
62     int decode_size;
63     ref *pds;
64 
65     check_type(*op, t_dictionary);
66     check_dict_read(*op);
67     if ((code = dict_int_param(op, "Width", 0, max_int_in_fixed / 2,
68 			       -1, &pim->Width)) < 0 ||
69 	(code = dict_int_param(op, "Height", 0, max_int_in_fixed / 2,
70 			       -1, &pim->Height)) < 0 ||
71 	(code = dict_matrix_param(mem, op, "ImageMatrix",
72 				  &pim->ImageMatrix)) < 0 ||
73 	(code = dict_bool_param(op, "MultipleDataSources", false,
74 				&pip->MultipleDataSources)) < 0 ||
75 	(code = dict_int_param(op, "BitsPerComponent", 1,
76 			       max_bits_per_component, -1,
77 			       &pim->BitsPerComponent)) < 0 ||
78 	(code = decode_size = dict_floats_param(mem, op, "Decode",
79 						num_components * 2,
80 						&pim->Decode[0], NULL)) < 0 ||
81 	(code = dict_bool_param(op, "Interpolate", false,
82 				&pim->Interpolate)) < 0
83 	)
84 	return code;
85     pip->pDecode = &pim->Decode[0];
86     /* Extract and check the data sources. */
87     if ((code = dict_find_string(op, "DataSource", &pds)) <= 0) {
88 	if (require_DataSource)
89 	    return (code < 0 ? code : gs_note_error(e_rangecheck));
90 	return 1;		/* no data source */
91     }
92     if (pip->MultipleDataSources) {
93 	long i, n = num_components + (has_alpha ? 1 : 0);
94         if (!r_is_array(pds))
95             return_error(e_typecheck);
96 	if (r_size(pds) != n)
97 	    return_error(e_rangecheck);
98 	for (i = 0; i < n; ++i)
99 	    array_get(mem, pds, i, &pip->DataSource[i]);
100     } else
101 	pip->DataSource[0] = *pds;
102     return 0;
103 }
104 
105 /* Extract and check the parameters for a gs_pixel_image_t. */
106 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)107 pixel_image_params(i_ctx_t *i_ctx_p, const ref *op, gs_pixel_image_t *pim,
108 		   image_params *pip, int max_bits_per_component,
109 		   bool has_alpha)
110 {
111     int num_components =
112 	gs_color_space_num_components(gs_currentcolorspace(igs));
113     int code;
114 
115     if (num_components < 1)
116 	return_error(e_rangecheck);	/* Pattern space not allowed */
117     pim->ColorSpace = gs_currentcolorspace(igs);
118     code = data_image_params(imemory, op, (gs_data_image_t *) pim, pip, true,
119 			     num_components, max_bits_per_component,
120 			     has_alpha);
121     if (code < 0)
122 	return code;
123     pim->format =
124 	(pip->MultipleDataSources ? gs_image_format_component_planar :
125 	 gs_image_format_chunky);
126     return dict_bool_param(op, "CombineWithColor", false,
127 			   &pim->CombineWithColor);
128 }
129 
130 /* Common setup for all Level 1 and 2 images, and ImageType 4 images. */
131 int
zimage_setup(i_ctx_t * i_ctx_p,const gs_pixel_image_t * pim,const ref * sources,bool uses_color,int npop)132 zimage_setup(i_ctx_t *i_ctx_p, const gs_pixel_image_t * pim,
133 	     const ref * sources, bool uses_color, int npop)
134 {
135     gx_image_enum_common_t *pie;
136     int code =
137 	gs_image_begin_typed((const gs_image_common_t *)pim, igs,
138 			     uses_color, &pie);
139 
140     if (code < 0)
141 	return code;
142     return zimage_data_setup(i_ctx_p, (const gs_pixel_image_t *)pim, pie,
143 			     sources, npop);
144 }
145 
146 /* Common code for .image1 and .alphaimage operators */
147 int
image1_setup(i_ctx_t * i_ctx_p,bool has_alpha)148 image1_setup(i_ctx_t * i_ctx_p, bool has_alpha)
149 {
150     os_ptr          op = osp;
151     gs_image_t      image;
152     image_params    ip;
153     int             code;
154 
155     gs_image_t_init(&image, gs_currentcolorspace(igs));
156     code = pixel_image_params( i_ctx_p,
157                                op,
158                                (gs_pixel_image_t *)&image,
159                                &ip,
160 			       (level2_enabled ? 16 : 8),
161                                has_alpha );
162     if (code < 0)
163 	return code;
164 
165     image.Alpha = (has_alpha ? gs_image_alpha_last : gs_image_alpha_none);
166     return zimage_setup( i_ctx_p,
167                          (gs_pixel_image_t *)&image,
168                          &ip.DataSource[0],
169 			 image.CombineWithColor,
170                          1 );
171 }
172 
173 /* <dict> .image1 - */
174 private int
zimage1(i_ctx_t * i_ctx_p)175 zimage1(i_ctx_t *i_ctx_p)
176 {
177     return image1_setup(i_ctx_p, false);
178 }
179 
180 /* <dict> .imagemask1 - */
181 private int
zimagemask1(i_ctx_t * i_ctx_p)182 zimagemask1(i_ctx_t *i_ctx_p)
183 {
184     os_ptr op = osp;
185     gs_image_t image;
186     image_params ip;
187     int code;
188 
189     gs_image_t_init_mask_adjust(&image, false,
190 				gs_incachedevice(igs) != CACHE_DEVICE_NONE);
191     code = data_image_params(imemory, op, (gs_data_image_t *) & image,
192 			     &ip, true, 1, 1, false);
193     if (code < 0)
194 	return code;
195     if (ip.MultipleDataSources)
196 	return_error(e_rangecheck);
197     return zimage_setup(i_ctx_p, (gs_pixel_image_t *)&image, &ip.DataSource[0],
198 			true, 1);
199 }
200 
201 
202 /* Common setup for all Level 1 and 2 images, and ImageType 3 and 4 images. */
203 /*
204  * We push the following on the estack.
205  *      control mark,
206  *	num_sources,
207  *      for I = num_sources-1 ... 0:
208  *          data source I,
209  *          aliasing information:
210  *              if source is not file, 1, except that the topmost value
211  *		  is used for bookkeeping in the procedure case (see below);
212  *              if file is referenced by a total of M different sources and
213  *                this is the occurrence with the lowest I, M;
214  *              otherwise, -J, where J is the lowest I of the same file as
215  *                this one;
216  *      current plane index,
217  *      num_sources,
218  *      enumeration structure.
219  */
220 #define NUM_PUSH(nsource) ((nsource) * 2 + 5)
221 /*
222  * We can access these values either from the bottom (esp at control mark - 1,
223  * EBOT macros) or the top (esp = enumeration structure, ETOP macros).
224  * Note that all macros return pointers.
225  */
226 #define EBOT_NUM_SOURCES(ep) ((ep) + 2)
227 #define EBOT_SOURCE(ep, i)\
228   ((ep) + 3 + (EBOT_NUM_SOURCES(ep)->value.intval - 1 - (i)) * 2)
229 #define ETOP_SOURCE(ep, i)\
230   ((ep) - 4 - (i) * 2)
231 #define ETOP_PLANE_INDEX(ep) ((ep) - 2)
232 #define ETOP_NUM_SOURCES(ep) ((ep) - 1)
233 private 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)234 zimage_data_setup(i_ctx_t *i_ctx_p, const gs_pixel_image_t * pim,
235 		  gx_image_enum_common_t * pie, const ref * sources, int npop)
236 {
237     int num_sources = pie->num_planes;
238     int inumpush = NUM_PUSH(num_sources);
239     int code;
240     gs_image_enum *penum;
241     int px;
242     const ref *pp;
243 
244     check_estack(inumpush + 2);	/* stuff above, + continuation + proc */
245     make_int(EBOT_NUM_SOURCES(esp), num_sources);
246     /*
247      * Note that the data sources may be procedures, strings, or (Level
248      * 2 only) files.  (The Level 1 reference manual says that Level 1
249      * requires procedures, but Adobe Level 1 interpreters also accept
250      * strings.)  The sources must all be of the same type.
251      *
252      * The Adobe documentation explicitly says that if two or more of the
253      * data sources are the same or inter-dependent files, the result is not
254      * defined.  We don't have a problem with the bookkeeping for
255      * inter-dependent files, since each one has its own buffer, but we do
256      * have to be careful if two or more sources are actually the same file.
257      * That is the reason for the aliasing information described above.
258      */
259     for (px = 0, pp = sources; px < num_sources; px++, pp++) {
260 	es_ptr ep = EBOT_SOURCE(esp, px);
261 
262 	make_int(ep + 1, 1);	/* default is no aliasing */
263 	switch (r_type(pp)) {
264 	    case t_file:
265 		if (!level2_enabled)
266 		    return_error(e_typecheck);
267 		/* Check for aliasing. */
268 		{
269 		    int pi;
270 
271 		    for (pi = 0; pi < px; ++pi)
272 			if (sources[pi].value.pfile == pp->value.pfile) {
273 			    /* Record aliasing */
274 			    make_int(ep + 1, -pi);
275 			    EBOT_SOURCE(esp, pi)[1].value.intval++;
276 			    break;
277 			}
278 		}
279 		/* falls through */
280 	    case t_string:
281 		if (r_type(pp) != r_type(sources)) {
282     		    if (pie != NULL)
283 		        gx_image_end(pie, false);    /* Clean up pie */
284 		    return_error(e_typecheck);
285 		}
286 		check_read(*pp);
287 		break;
288 	    default:
289 		if (!r_is_proc(sources)) {
290     		    if (pie != NULL)
291 		        gx_image_end(pie, false);    /* Clean up pie */
292 		    return_error(e_typecheck);
293 		}
294 		check_proc(*pp);
295 	}
296 	*ep = *pp;
297     }
298     /* Always place the image enumerator into local memory,
299        because pie may have local objects inherited from igs,
300        which may be local when the current allocation mode is global.
301        Bug 688140. */
302     if ((penum = gs_image_enum_alloc(imemory_local, "image_setup")) == 0)
303 	return_error(e_VMerror);
304     code = gs_image_enum_init(penum, pie, (const gs_data_image_t *)pim, igs);
305     if (code != 0) {		/* error, or empty image */
306 	int code1 = gs_image_cleanup_and_free_enum(penum);
307 
308 	if (code >= 0)		/* empty image */
309 	    pop(npop);
310 	if (code >= 0 && code1 < 0)
311 	    code = code1;
312 	return code;
313     }
314     push_mark_estack(es_other, image_cleanup);
315     esp += inumpush - 1;
316     make_int(ETOP_PLANE_INDEX(esp), 0);
317     make_int(ETOP_NUM_SOURCES(esp), num_sources);
318     make_struct(esp, avm_local, penum);
319     switch (r_type(sources)) {
320 	case t_file:
321 	    push_op_estack(image_file_continue);
322 	    break;
323 	case t_string:
324 	    push_op_estack(image_string_continue);
325 	    break;
326 	default:		/* procedure */
327 	    push_op_estack(image_proc_process);
328 	    break;
329     }
330     pop(npop);
331     return o_push_estack;
332 }
333 /* Pop all the control information off the e-stack. */
334 private es_ptr
zimage_pop_estack(es_ptr tep)335 zimage_pop_estack(es_ptr tep)
336 {
337     return tep - NUM_PUSH(ETOP_NUM_SOURCES(tep)->value.intval);
338 }
339 
340 /*
341  * Continuation for procedure data source.  We use the topmost aliasing slot
342  * to remember whether we've just called the procedure (1) or whether we're
343  * returning from a RemapColor callout (0).
344  */
345 private int
image_proc_continue(i_ctx_t * i_ctx_p)346 image_proc_continue(i_ctx_t *i_ctx_p)
347 {
348     os_ptr op = osp;
349     gs_image_enum *penum = r_ptr(esp, gs_image_enum);
350     int px = ETOP_PLANE_INDEX(esp)->value.intval;
351     int num_sources = ETOP_NUM_SOURCES(esp)->value.intval;
352     uint size, used[gs_image_max_planes];
353     gs_const_string plane_data[gs_image_max_planes];
354     const byte *wanted;
355     int i, code;
356 
357     if (!r_has_type_attrs(op, t_string, a_read)) {
358 	check_op(1);
359 	/* Procedure didn't return a (readable) string.  Quit. */
360 	esp = zimage_pop_estack(esp);
361 	image_cleanup(i_ctx_p);
362 	return_error(!r_has_type(op, t_string) ? e_typecheck : e_invalidaccess);
363     }
364     size = r_size(op);
365     if (size == 0 && ETOP_SOURCE(esp, 0)[1].value.intval == 0)
366 	code = 1;
367     else {
368 	for (i = 0; i < num_sources; i++)
369 	    plane_data[i].size = 0;
370 	plane_data[px].data = op->value.bytes;
371 	plane_data[px].size = size;
372 	code = gs_image_next_planes(penum, plane_data, used);
373 	if (code == e_RemapColor) {
374 	    op->value.bytes += used[px]; /* skip used data */
375 	    r_dec_size(op, used[px]);
376 	    ETOP_SOURCE(esp, 0)[1].value.intval = 0; /* RemapColor callout */
377 	    return code;
378 	}
379     }
380     if (code) {			/* Stop now. */
381 	esp = zimage_pop_estack(esp);
382 	pop(1);
383 	image_cleanup(i_ctx_p);
384 	return (code < 0 ? code : o_pop_estack);
385     }
386     pop(1);
387     wanted = gs_image_planes_wanted(penum);
388     do {
389 	if (++px == num_sources)
390 	    px = 0;
391     } while (!wanted[px]);
392     ETOP_PLANE_INDEX(esp)->value.intval = px;
393     return image_proc_process(i_ctx_p);
394 }
395 private int
image_proc_process(i_ctx_t * i_ctx_p)396 image_proc_process(i_ctx_t *i_ctx_p)
397 {
398     int px = ETOP_PLANE_INDEX(esp)->value.intval;
399     gs_image_enum *penum = r_ptr(esp, gs_image_enum);
400     const byte *wanted = gs_image_planes_wanted(penum);
401     int num_sources = ETOP_NUM_SOURCES(esp)->value.intval;
402     const ref *pp;
403 
404     ETOP_SOURCE(esp, 0)[1].value.intval = 0; /* procedure callout */
405     while (!wanted[px]) {
406 	if (++px == num_sources)
407 	    px = 0;
408 	ETOP_PLANE_INDEX(esp)->value.intval = px;
409     }
410     pp = ETOP_SOURCE(esp, px);
411     push_op_estack(image_proc_continue);
412     *++esp = *pp;
413     return o_push_estack;
414 }
415 
416 /* Continue processing data from an image with file data sources. */
417 private int
image_file_continue(i_ctx_t * i_ctx_p)418 image_file_continue(i_ctx_t *i_ctx_p)
419 {
420     gs_image_enum *penum = r_ptr(esp, gs_image_enum);
421     int num_sources = ETOP_NUM_SOURCES(esp)->value.intval;
422 
423     for (;;) {
424 	uint min_avail = max_int;
425 	gs_const_string plane_data[gs_image_max_planes];
426 	int code;
427 	int px;
428 	const ref *pp;
429 	bool at_eof = false;
430 
431 	/*
432 	 * Do a first pass through the files to ensure that at least
433 	 * one has data available in its buffer.
434 	 */
435 
436 	for (px = 0, pp = ETOP_SOURCE(esp, 0); px < num_sources;
437 	     ++px, pp -= 2
438 	    ) {
439 	    int num_aliases = pp[1].value.intval;
440 	    stream *s = pp->value.pfile;
441 	    int min_left;
442 	    uint avail;
443 
444 	    if (num_aliases <= 0)
445 		num_aliases = ETOP_SOURCE(esp, -num_aliases)[1].value.intval;
446 	    while ((avail = sbufavailable(s)) <=
447 		   (min_left = sbuf_min_left(s)) + num_aliases - 1) {
448 		int next = s->end_status;
449 
450 		switch (next) {
451 		case 0:
452 		    s_process_read_buf(s);
453 		    continue;
454 		case EOFC:
455 		    at_eof = true;
456 		    break;	/* with no data available */
457 		case INTC:
458 		case CALLC:
459 		    return
460 			s_handle_read_exception(i_ctx_p, next, pp,
461 						NULL, 0, image_file_continue);
462 		default:
463 		    /* case ERRC: */
464 		    return_error(e_ioerror);
465 		}
466 		break;		/* for EOFC */
467 	    }
468 	    /*
469 	     * Note that in the EOF case, we can get here with no data
470 	     * available.
471 	     */
472 	    if (avail >= min_left)
473 		avail = (avail - min_left) / num_aliases; /* may be 0 */
474 	    if (avail < min_avail)
475 		min_avail = avail;
476 	    plane_data[px].data = sbufptr(s);
477 	    plane_data[px].size = avail;
478 	}
479 
480 	/*
481 	 * Now pass the available buffered data to the image processor.
482 	 * Even if there is no available data, we must call
483 	 * gs_image_next_planes one more time to finish processing any
484 	 * retained data.
485 	 */
486 
487 	{
488 	    int pi;
489 	    uint used[gs_image_max_planes];
490 
491 	    code = gs_image_next_planes(penum, plane_data, used);
492 	    /* Now that used has been set, update the streams. */
493 	    for (pi = 0, pp = ETOP_SOURCE(esp, 0); pi < num_sources;
494 		 ++pi, pp -= 2
495 		 )
496 		sbufskip(pp->value.pfile, used[pi]);
497 	    if (code == e_RemapColor)
498 		return code;
499 	}
500 	if (at_eof)
501 	    code = 1;
502 	if (code) {
503 	    int code1;
504 
505 	    esp = zimage_pop_estack(esp);
506 	    code1 = image_cleanup(i_ctx_p);
507 	    return (code < 0 ? code : code1 < 0 ? code1 : o_pop_estack);
508 	}
509     }
510 }
511 
512 /* Process data from an image with string data sources. */
513 /* This may still encounter a RemapColor callback. */
514 private int
image_string_continue(i_ctx_t * i_ctx_p)515 image_string_continue(i_ctx_t *i_ctx_p)
516 {
517     gs_image_enum *penum = r_ptr(esp, gs_image_enum);
518     int num_sources = ETOP_NUM_SOURCES(esp)->value.intval;
519     gs_const_string sources[gs_image_max_planes];
520     uint used[gs_image_max_planes];
521 
522     /* Pass no data initially, to find out how much is retained. */
523     memset(sources, 0, sizeof(sources[0]) * num_sources);
524     for (;;) {
525 	int px;
526 	int code = gs_image_next_planes(penum, sources, used);
527 
528 	if (code == e_RemapColor)
529 	    return code;
530     stop_now:
531 	if (code) {		/* Stop now. */
532 	    esp -= NUM_PUSH(num_sources);
533 	    image_cleanup(i_ctx_p);
534 	    return (code < 0 ? code : o_pop_estack);
535 	}
536 	for (px = 0; px < num_sources; ++px)
537 	    if (sources[px].size == 0) {
538 		const ref *psrc = ETOP_SOURCE(esp, px);
539 		uint size = r_size(psrc);
540 
541 		if (size == 0) {	    /* empty source */
542 		    code = 1;
543 		    goto stop_now;
544                 }
545 		sources[px].data = psrc->value.bytes;
546 		sources[px].size = size;
547 	    }
548     }
549 }
550 
551 /* Clean up after enumerating an image */
552 private int
image_cleanup(i_ctx_t * i_ctx_p)553 image_cleanup(i_ctx_t *i_ctx_p)
554 {
555     es_ptr ep_top = esp + NUM_PUSH(EBOT_NUM_SOURCES(esp)->value.intval);
556     gs_image_enum *penum = r_ptr(ep_top, gs_image_enum);
557 
558     return gs_image_cleanup_and_free_enum(penum);
559 }
560 
561 /* ------ Initialization procedure ------ */
562 
563 const op_def zimage_op_defs[] =
564 {
565     {"1.image1", zimage1},
566     {"1.imagemask1", zimagemask1},
567 		/* Internal operators */
568     {"1%image_proc_continue", image_proc_continue},
569     {"0%image_file_continue", image_file_continue},
570     {"0%image_string_continue", image_string_continue},
571     op_def_end(0)
572 };
573