xref: /plan9/sys/src/cmd/gs/src/zcolor.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
1 /* Copyright (C) 1989, 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: zcolor.c,v 1.21 2004/10/01 23:35:02 ghostgum Exp $ */
18 /* Color operators */
19 #include "memory_.h"
20 #include "ghost.h"
21 #include "oper.h"
22 #include "estack.h"
23 #include "ialloc.h"
24 #include "igstate.h"
25 #include "iutil.h"
26 #include "store.h"
27 #include "gxfixed.h"
28 #include "gxmatrix.h"
29 #include "gzstate.h"
30 #include "gxdcolor.h"		/* for gxpcolor.h */
31 #include "gxdevice.h"
32 #include "gxdevmem.h"		/* for gxpcolor.h */
33 #include "gxcmap.h"
34 #include "gxcspace.h"
35 #include "gxcolor2.h"
36 #include "gxpcolor.h"
37 #include "idict.h"
38 #include "icolor.h"
39 #include "idparam.h"
40 #include "iname.h"
41 
42 /* imported from gsht.c */
43 extern  void    gx_set_effective_transfer(gs_state *);
44 
45 /* define the number of stack slots needed for zcolor_remap_one */
46 const int   zcolor_remap_one_ostack = 4;
47 const int   zcolor_remap_one_estack = 3;
48 
49 
50 /* utility to test whether a Pattern instance uses a base space */
51 private inline bool
pattern_instance_uses_base_space(const gs_pattern_instance_t * pinst)52 pattern_instance_uses_base_space(const gs_pattern_instance_t * pinst)
53 {
54     return pinst->type->procs.uses_base_space(
55                    pinst->type->procs.get_pattern(pinst) );
56 }
57 
58 /*
59  *  -   currentcolor   <param1>  ...  <paramN>
60  *
61  * Return the current color. <paramN> may be a dictionary or a null
62  * object, if the current color space is a pattern color space. The
63  * other parameters will be numeric.
64  *
65  * Note that the results of this operator differ slightly from those of
66  * most currentcolor implementations. If a color component value is
67  * integral (e.g.: 0, 1), it will be pushed on the stack as an integer.
68  * Most currentcolor implementations, including the earlier
69  * implementation in Ghostscript, would push real objects for all
70  * color spaces except indexed color space. The approach taken here is
71  * equally legitimate, and avoids special handling of indexed color
72  * spaces.
73  */
74 private int
zcurrentcolor(i_ctx_t * i_ctx_p)75 zcurrentcolor(i_ctx_t * i_ctx_p)
76 {
77     os_ptr                  op = osp;
78     const gs_color_space *  pcs = gs_currentcolorspace(igs);
79     const gs_client_color * pcc = gs_currentcolor(igs);
80     int                     i, n = cs_num_components(pcs);
81     bool                    push_pattern = n < 0;
82 
83     /* check for pattern */
84     if (push_pattern) {
85         gs_pattern_instance_t * pinst = pcc->pattern;
86 
87         if (pinst == 0 || !pattern_instance_uses_base_space(pinst))
88             n = 1;
89         else
90             n = -n;
91     }
92 
93     /* check for sufficient space on the stack */
94     push(n);
95     op -= n - 1;
96 
97     /* push the numeric operands, if any */
98     if (push_pattern)
99         --n;
100     for (i = 0; i < n; i++, op++) {
101         float   rval = pcc->paint.values[i];
102         int     ival = (int)rval;
103 
104         /* the following handles indexed color spaces */
105         if (rval == ival)
106             make_int(op, ival);
107         else
108             make_real(op, rval);
109     }
110 
111     /* push the pattern dictionary or null object, if appropriate */
112     if (push_pattern)
113         *op = istate->pattern;
114 
115     return 0;
116 }
117 
118 /*
119  *  -   .currentcolorspace   <array>
120  *
121  * Return the current color space. Unlike the prior implementation, the
122  * istate->color_space.array field will now always have a legitimate
123  * (array) value.
124  */
125 private int
zcurrentcolorspace(i_ctx_t * i_ctx_p)126 zcurrentcolorspace(i_ctx_t * i_ctx_p)
127 {
128     os_ptr  op = osp;   /* required by "push" macro */
129 
130     push(1);
131     if ( gs_color_space_get_index(igs->color_space) == gs_color_space_index_DeviceGray ) {
132         ref gray, graystr;
133         ref csa = istate->colorspace.array;
134         if (array_get(imemory, &csa, 0, &gray) >= 0 &&
135             r_has_type(&gray, t_name) &&
136 	    (name_string_ref(imemory, &gray, &graystr),
137 	    r_size(&graystr) == 10 &&
138 	    !memcmp(graystr.value.bytes, "DeviceGray", 10))) {
139 
140             *op = istate->colorspace.array;
141         } else {
142 	    int code = ialloc_ref_array(op, a_all, 1, "currentcolorspace");
143 	    if (code < 0)
144 	        return code;
145 	    return name_enter_string(imemory, "DeviceGray", op->value.refs);
146         }
147     } else
148         *op = istate->colorspace.array;
149     return 0;
150 }
151 
152 /*
153  *  -   .getuseciecolor   <bool>
154  *
155  * Return the current setting of the use_cie_color graphic state parameter,
156  * which tracks the UseCIEColor page device parameter. This parameter may be
157  * read (via this operator) at all language leves, but may only be set (via
158  * the .setuseciecolor operator; see zcolor3.c) only in language level 3.
159  *
160  * We handle this parameter separately from the page device primarily for
161  * performance reasons (the parameter may be queried frequently), but as a
162  * side effect achieve proper behavior relative to the language level. The
163  * interpreter is always initialized with this parameter set to false, and
164  * it can only be updated (via setpagedevice) in language level 3.
165  */
166 private int
zgetuseciecolor(i_ctx_t * i_ctx_p)167 zgetuseciecolor(i_ctx_t * i_ctx_p)
168 {
169     os_ptr  op = osp;
170 
171     push(1);
172     *op = istate->use_cie_color;
173     return 0;
174 }
175 
176 /*
177  *  <param1>  ...  <paramN>   setcolor   -
178  *
179  * Set the current color. All of the parameters except the topmost (paramN) are
180  * numbers; the topmost (and possibly only) entry may be pattern dictionary or
181  * a null object.
182  *
183  * The use of one operator to set both patterns and "normal" colors is
184  * consistent with Adobe's documentation, but primarily reflects the use of
185  * gs_setcolor for both purposes in the graphic library. An alternate
186  * implementation would use a .setpattern operator, which would interface with
187  * gs_setpattern.
188  *
189  * This operator is hidden by a pseudo-operator of the same name, so it will
190  * only be invoked under controlled situations. Hence, it does no operand
191  * checking.
192  */
193 private int
zsetcolor(i_ctx_t * i_ctx_p)194 zsetcolor(i_ctx_t * i_ctx_p)
195 {
196     os_ptr                  op = osp;
197     const gs_color_space *  pcs = gs_currentcolorspace(igs);
198     gs_client_color         cc;
199     int                     n_comps, n_numeric_comps, num_offset = 0, code;
200     bool                    is_ptype2 = 0;
201 
202     /* initialize the client color pattern pointer for GC */
203     cc.pattern = 0;
204 
205     /* check for a pattern color space */
206     if ((n_comps = cs_num_components(pcs)) < 0) {
207         n_comps = -n_comps;
208         if (r_has_type(op, t_dictionary)) {
209             ref *   pImpl;
210             int     ptype;
211 
212             dict_find_string(op, "Implementation", &pImpl);
213             cc.pattern = r_ptr(pImpl, gs_pattern_instance_t);
214             n_numeric_comps = ( pattern_instance_uses_base_space(cc.pattern)
215                                   ? n_comps - 1
216                                   : 0 );
217             (void)dict_int_param(op, "PatternType", 1, 2, 1, &ptype);
218             is_ptype2 = ptype == 2;
219         } else
220             n_numeric_comps = 0;
221         num_offset = 1;
222     } else
223         n_numeric_comps = n_comps;
224 
225     /* gather the numeric operands */
226     float_params(op - num_offset, n_numeric_comps, cc.paint.values);
227 
228     /* pass the color to the graphic library */
229     if ((code = gs_setcolor(igs, &cc)) >= 0) {
230 
231         if (n_comps > n_numeric_comps) {
232             istate->pattern = *op;      /* save pattern dict or null */
233             n_comps = n_numeric_comps + 1;
234         }
235         pop(n_comps);
236     }
237 
238     return code;
239 }
240 
241 /*
242  *  <array>   setcolorspace   -
243  *
244  * Set the nominal color space. This color space will be pushd by the
245  * currentcolorspace operator, but is not directly used to pass color
246  * space information to the graphic library.
247  *
248  * This operator can only be called from within the setcolorspace
249  * pseudo-operator; the definition of the latter will override this
250  * definition. Because error cheching is performed by the pseudo-
251  * operator, it need not be repeated here.
252  */
253 private int
zsetcolorspace(i_ctx_t * i_ctx_p)254 zsetcolorspace(i_ctx_t * i_ctx_p)
255 {
256     os_ptr  op = osp;
257 
258     istate->colorspace.array = *op;
259     pop(1);
260     return 0;
261 }
262 
263 /*
264  *  <name> .includecolorspace -
265  *
266  * See the comment for gs_includecolorspace in gscolor2.c .
267  */
268 private int
zincludecolorspace(i_ctx_t * i_ctx_p)269 zincludecolorspace(i_ctx_t * i_ctx_p)
270 {
271     os_ptr  op = osp;
272     ref nsref;
273     int code;
274 
275     check_type(*op, t_name);
276     name_string_ref(imemory, op, &nsref);
277     code =  gs_includecolorspace(igs, nsref.value.const_bytes, r_size(&nsref));
278     if (!code)
279 	pop(1);
280     return code;
281 }
282 
283 
284 /*
285  *  <int>   .setdevcspace   -
286  *
287  * Set a parameterless color space. This is now used to set the
288  * DeviceGray, DeviceRGB, and DeviceCMYK color spaces, rather than
289  * the setgray/setrgbcolor/setcmykcolor operators. All PostScript-based
290  * color space substitution will have been accomplished before this
291  * operator is called.
292  *
293  * The use of an integer to indicate the specific color space is
294  * historical and on the whole not particularly desirable, as it ties
295  * the PostScript code to a specific enumeration. This may be modified
296  * in the future.
297  *
298  * As with setcolorspace, this operator is called only under controlled
299  * circumstances, hence it does no operand error checking.
300  */
301 private int
zsetdevcspace(i_ctx_t * i_ctx_p)302 zsetdevcspace(i_ctx_t * i_ctx_p)
303 {
304 
305     gs_color_space  cs;
306     int             code;
307 
308     switch((gs_color_space_index)osp->value.intval) {
309       default:  /* can't happen */
310       case gs_color_space_index_DeviceGray:
311 	gs_cspace_init_DeviceGray(imemory, &cs);
312         break;
313 
314       case gs_color_space_index_DeviceRGB:
315         gs_cspace_init_DeviceRGB(imemory, &cs);
316         break;
317 
318       case gs_color_space_index_DeviceCMYK:
319         gs_cspace_init_DeviceCMYK(imemory, &cs);
320         break;
321     }
322     if ((code = gs_setcolorspace(igs, &cs)) >= 0)
323         pop(1);
324     return code;
325 }
326 
327 
328 /*  -   currenttransfer   <proc> */
329 private int
zcurrenttransfer(i_ctx_t * i_ctx_p)330 zcurrenttransfer(i_ctx_t *i_ctx_p)
331 {
332     os_ptr  op = osp;
333 
334     push(1);
335     *op = istate->transfer_procs.gray;
336     return 0;
337 }
338 
339 /*
340  *  -   processcolors   <int>  -
341  *
342  * Note: this is an undocumented operator that is not supported
343  * in Level 2.
344  */
345 private int
zprocesscolors(i_ctx_t * i_ctx_p)346 zprocesscolors(i_ctx_t * i_ctx_p)
347 {
348     os_ptr  op = osp;
349 
350     push(1);
351     make_int(op, gs_currentdevice(igs)->color_info.num_components);
352     return 0;
353 }
354 
355 /* <proc> settransfer - */
356 private int
zsettransfer(i_ctx_t * i_ctx_p)357 zsettransfer(i_ctx_t * i_ctx_p)
358 {
359     os_ptr  op = osp;
360     int     code;
361 
362     check_proc(*op);
363     check_ostack(zcolor_remap_one_ostack - 1);
364     check_estack(1 + zcolor_remap_one_estack);
365     istate->transfer_procs.red =
366         istate->transfer_procs.green =
367         istate->transfer_procs.blue =
368         istate->transfer_procs.gray = *op;
369     if ((code = gs_settransfer_remap(igs, gs_mapped_transfer, false)) < 0)
370         return code;
371     push_op_estack(zcolor_reset_transfer);
372     pop(1);
373     return zcolor_remap_one( i_ctx_p,
374                              &istate->transfer_procs.gray,
375                              igs->set_transfer.gray,
376                              igs,
377                              zcolor_remap_one_finish );
378 }
379 
380 
381 /*
382  * Internal routines
383  */
384 
385 /*
386  * Prepare to remap one color component (also used for black generation
387  * and undercolor removal). Use the 'for' operator to gather the values.
388  * The caller must have done the necessary check_ostack and check_estack.
389  */
390 int
zcolor_remap_one(i_ctx_t * i_ctx_p,const ref * pproc,gx_transfer_map * pmap,const gs_state * pgs,op_proc_t finish_proc)391 zcolor_remap_one(
392     i_ctx_t *           i_ctx_p,
393     const ref *         pproc,
394     gx_transfer_map *   pmap,
395     const gs_state *    pgs,
396     op_proc_t           finish_proc )
397 {
398     os_ptr              op;
399 
400     /*
401      * Detect the identity function, which is a common value for one or
402      * more of these functions.
403      */
404     if (r_size(pproc) == 0) {
405 	gx_set_identity_transfer(pmap);
406 	/*
407 	 * Even though we don't actually push anything on the e-stack, all
408 	 * clients do, so we return o_push_estack in this case.  This is
409 	 * needed so that clients' finishing procedures will get run.
410 	 */
411 	return o_push_estack;
412     }
413     op = osp += 4;
414     make_real(op - 3, 0);
415     make_int(op - 2, transfer_map_size - 1);
416     make_real(op - 1, 1);
417     *op = *pproc;
418     ++esp;
419     make_struct(esp, imemory_space((gs_ref_memory_t *) pgs->memory),
420 		pmap);
421     push_op_estack(finish_proc);
422     push_op_estack(zfor_samples);
423     return o_push_estack;
424 }
425 
426 /* Store the result of remapping a component. */
427 private int
zcolor_remap_one_store(i_ctx_t * i_ctx_p,floatp min_value)428 zcolor_remap_one_store(i_ctx_t *i_ctx_p, floatp min_value)
429 {
430     int i;
431     gx_transfer_map *pmap = r_ptr(esp, gx_transfer_map);
432 
433     if (ref_stack_count(&o_stack) < transfer_map_size)
434 	return_error(e_stackunderflow);
435     for (i = 0; i < transfer_map_size; i++) {
436 	double v;
437 	int code =
438 	    real_param(ref_stack_index(&o_stack, transfer_map_size - 1 - i),
439 		       &v);
440 
441 	if (code < 0)
442 	    return code;
443 	pmap->values[i] =
444 	    (v < min_value ? float2frac(min_value) :
445 	     v >= 1.0 ? frac_1 :
446 	     float2frac(v));
447     }
448     ref_stack_pop(&o_stack, transfer_map_size);
449     esp--;			/* pop pointer to transfer map */
450     return o_pop_estack;
451 }
452 int
zcolor_remap_one_finish(i_ctx_t * i_ctx_p)453 zcolor_remap_one_finish(i_ctx_t *i_ctx_p)
454 {
455     return zcolor_remap_one_store(i_ctx_p, 0.0);
456 }
457 int
zcolor_remap_one_signed_finish(i_ctx_t * i_ctx_p)458 zcolor_remap_one_signed_finish(i_ctx_t *i_ctx_p)
459 {
460     return zcolor_remap_one_store(i_ctx_p, -1.0);
461 }
462 
463 /* Finally, reset the effective transfer functions and */
464 /* invalidate the current color. */
465 int
zcolor_reset_transfer(i_ctx_t * i_ctx_p)466 zcolor_reset_transfer(i_ctx_t *i_ctx_p)
467 {
468     gx_set_effective_transfer(igs);
469     return zcolor_remap_color(i_ctx_p);
470 }
471 int
zcolor_remap_color(i_ctx_t * i_ctx_p)472 zcolor_remap_color(i_ctx_t *i_ctx_p)
473 {
474     gx_unset_dev_color(igs);
475     return 0;
476 }
477 
478 /*
479  * <param1> ... <paramN> .color_test <param1> ... <paramN>
480  *
481  * encode and decode color to allow mapping to be tested.
482  */
483 private int
zcolor_test(i_ctx_t * i_ctx_p)484 zcolor_test(i_ctx_t *i_ctx_p)
485 {
486     gx_color_value cv[GX_DEVICE_COLOR_MAX_COMPONENTS];
487     gx_device *dev = gs_currentdevice(igs);
488     int ncomp = dev->color_info.num_components;
489     gx_color_index color;
490     os_ptr op = osp - (ncomp-1);
491     int i;
492     if (ref_stack_count(&o_stack) < ncomp)
493 	return_error(e_stackunderflow);
494     for (i = 0; i < ncomp; i++) {
495 	if (r_has_type(op+i, t_real))
496 	    cv[i] = (gx_color_value)
497 		(op[i].value.realval * gx_max_color_value);
498 	else if (r_has_type(op+i, t_integer))
499 	    cv[i] = (gx_color_value)
500 		(op[i].value.intval * gx_max_color_value);
501 	else
502 	    return_error(e_typecheck);
503     }
504     color = (*dev_proc(dev, encode_color)) (dev, cv);
505     (*dev_proc(dev, decode_color)) (dev, color, cv);
506     for (i = 0; i < ncomp; i++)
507         make_real(op+i, (float)cv[i] / (float)gx_max_color_value);
508     return 0;
509 }
510 
511 /*
512  * <levels> .color_test_all <value0> ... <valueN>
513  *
514  * Test encode/decode color procedures for a range of values.
515  * Return value with the worst error in a single component.
516  */
517 private int
zcolor_test_all(i_ctx_t * i_ctx_p)518 zcolor_test_all(i_ctx_t *i_ctx_p)
519 {
520     os_ptr                  op = osp;
521     gx_color_value cv[GX_DEVICE_COLOR_MAX_COMPONENTS];
522     gx_color_value cvout[GX_DEVICE_COLOR_MAX_COMPONENTS];
523     gx_color_value cvbad[GX_DEVICE_COLOR_MAX_COMPONENTS];
524     int counter[GX_DEVICE_COLOR_MAX_COMPONENTS];
525     gx_device *dev = gs_currentdevice(igs);
526     int ncomp = dev->color_info.num_components;
527     int steps;
528     int maxerror = 0;
529     int err;
530     int acceptable_error;
531     int linsep = dev->color_info.separable_and_linear == GX_CINFO_SEP_LIN;
532     int linsepfailed = 0;
533     int lsmaxerror = 0;
534     gx_color_index color, lscolor;
535     int i, j, k;
536     int finished = 0;
537 
538     if (ncomp == 1)
539 	acceptable_error = gx_max_color_value / dev->color_info.max_gray + 1;
540     else
541 	acceptable_error = gx_max_color_value / dev->color_info.max_color + 1;
542 
543     if (ref_stack_count(&o_stack) < 1)
544 	return_error(e_stackunderflow);
545     if (!r_has_type(&osp[0], t_integer))
546         return_error(e_typecheck);
547     steps = osp[0].value.intval;
548     for (i = 0; i < ncomp; i++) {
549         counter[i] = 0;
550 	cvbad[i] = 0;
551     }
552 
553     dprintf1("Number of components = %d\n", ncomp);
554     dprintf1("Depth = %d\n", dev->color_info.depth);
555     dprintf2("max_gray = %d   dither_grays = %d\n",
556 	dev->color_info.max_gray, dev->color_info.dither_grays);
557     dprintf2("max_color = %d   dither_colors = %d\n",
558  	dev->color_info.max_color, dev->color_info.dither_colors);
559     dprintf1("polarity = %s\n",
560       dev->color_info.polarity == GX_CINFO_POLARITY_ADDITIVE ? "Additive" :
561       dev->color_info.polarity == GX_CINFO_POLARITY_SUBTRACTIVE ?"Subtractive":
562       "Unknown");
563     /* Indicate color index value with all colorants = zero */
564     for (i = 0; i < ncomp; i++)
565 	cv[i] = 0;
566     color = (*dev_proc(dev, encode_color)) (dev, cv);
567     dprintf1("Zero color index:  %8x\n", color);
568 
569     dprintf1("separable_and_linear = %s\n",
570       linsep == GX_CINFO_SEP_LIN_NONE ? "No" :
571       linsep == GX_CINFO_SEP_LIN ? "Yes" :
572       "Unknown");
573     if (dev->color_info.gray_index == GX_CINFO_COMP_INDEX_UNKNOWN)
574         dprintf("gray_index is unknown\n");
575     else
576         dprintf1("gray_index = %d\n", dev->color_info.gray_index);
577     if (linsep) {
578         dprintf(" Shift     Mask  Bits\n");
579         for (i = 0; i < ncomp; i++) {
580             dprintf3(" %5d %8x  %4d\n",
581 		(int)(dev->color_info.comp_shift[i]),
582 		(int)(dev->color_info.comp_mask[i]),
583 		(int)(dev->color_info.comp_bits[i]));
584         }
585     }
586 
587     while (!finished) {
588 	for (j = 0; j <= steps; j++) {
589 	    for (i = 0; i < ncomp; i++)
590 		cv[i] = counter[i] * gx_max_color_value / steps;
591 	    color = (*dev_proc(dev, encode_color)) (dev, cv);
592 	    if (linsep) {
593 		/* Derive it the other way */
594 		lscolor = gx_default_encode_color(dev, cv);
595 		if ((color != lscolor) && (linsepfailed < 5)) {
596 		    linsepfailed++;
597 		    dprintf("Failed separable_and_linear for");
598 		    for (i = 0; i < ncomp; i++)
599 			dprintf1(" %d", cv[i]);
600 		    dprintf("\n");
601 		    dprintf2("encode_color=%x  gx_default_encode_color=%x\n",
602 			(int)color, (int)lscolor);
603 		}
604 	    }
605 	    (*dev_proc(dev, decode_color)) (dev, color, cvout);
606 	    for (i = 0; i < ncomp; i++) {
607 		err = (int)cvout[i] - (int)cv[i];
608 		if (err < 0)
609 		    err = -err;
610 		if (err > maxerror) {
611 		    maxerror = err;
612 		    for (k=0; k < ncomp; k++)
613 			cvbad[k] = cv[k];
614 		}
615 	    }
616 	    if (linsep) {
617 	        gx_default_decode_color(dev, color, cvout);
618 		for (i = 0; i < ncomp; i++) {
619 		    err = (int)cvout[i] - (int)cv[i];
620 		    if (err < 0)
621 			err = -err;
622 		    if (err > lsmaxerror) {
623 			lsmaxerror = err;
624 		    }
625 		}
626 	    }
627 	    counter[0] += 1;
628 	}
629 	counter[0] = 0;
630 	i = 1;
631 	while (i < ncomp) {
632 	    counter[i] += 1;
633 	    if (counter[i] > steps) {
634 		counter[i] = 0;
635 		i++;
636 	    }
637 	    else
638 		break;
639 	}
640 	if (i >= ncomp)
641 	    finished = 1;
642     }
643 
644     dprintf2("Maximum error %g %s\n",
645 	(float)maxerror / (float)gx_max_color_value,
646 	maxerror <= acceptable_error ? "is Ok" :
647 	maxerror <= 3*acceptable_error/2 ? "is POOR" : "FAILED");
648 
649     if (linsep)
650       dprintf2("Maximum linear_and_separable error %g %s\n",
651 	(float)lsmaxerror / (float)gx_max_color_value,
652 	lsmaxerror <= acceptable_error ? "is Ok" :
653 	lsmaxerror <= 3*acceptable_error/2 ? "is POOR" : "FAILED");
654 
655     /* push worst value */
656     push(ncomp-1);
657     op -= ncomp - 1;
658     for (i = 0; i < ncomp; i++)
659         make_real(op+i, (float)cvbad[i] / (float)gx_max_color_value);
660 
661     return 0;
662 }
663 
664 
665 /* ------ Initialization procedure ------ */
666 
667 const op_def    zcolor_op_defs[] =
668 {
669     { "0currentcolor", zcurrentcolor },
670     { "0currentcolorspace", zcurrentcolorspace },
671     { "0.getuseciecolor", zgetuseciecolor },
672     { "1setcolor", zsetcolor },
673     { "1setcolorspace", zsetcolorspace },
674     { "1.setdevcspace", zsetdevcspace },
675 
676     /* basic transfer operators */
677     { "0currenttransfer", zcurrenttransfer },
678     { "0processcolors", zprocesscolors },
679     { "1settransfer", zsettransfer },
680 
681     /* internal operators */
682     { "1%zcolor_remap_one_finish", zcolor_remap_one_finish },
683     { "1%zcolor_remap_one_signed_finish", zcolor_remap_one_signed_finish },
684     { "0%zcolor_reset_transfer", zcolor_reset_transfer },
685     { "0%zcolor_remap_color", zcolor_remap_color },
686     { "0.color_test", zcolor_test },
687     { "1.color_test_all", zcolor_test_all },
688 
689 
690     /* high level device support */
691     { "0.includecolorspace", zincludecolorspace },
692     op_def_end(0)
693 };
694