xref: /plan9/sys/src/cmd/gs/src/gscspace.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
1 /* Copyright (C) 1998, 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: gscspace.c,v 1.18 2004/08/19 19:33:09 stefan Exp $ */
18 /* Color space operators and support */
19 #include "memory_.h"
20 #include "gx.h"
21 #include "gserrors.h"
22 #include "gsstruct.h"
23 #include "gsccolor.h"
24 #include "gsutil.h"		/* for gs_next_ids */
25 #include "gxcmap.h"
26 #include "gxcspace.h"
27 #include "gxistate.h"
28 #include "gsovrc.h"
29 #include "gsstate.h"
30 #include "gsdevice.h"
31 #include "gxdevcli.h"
32 #include "gzstate.h"
33 #include "stream.h"
34 
35 /*
36  * Define the standard color space types.  We include DeviceCMYK in the base
37  * build because it's too awkward to omit it, but we don't provide any of
38  * the PostScript operator procedures (setcmykcolor, etc.) for dealing with
39  * it.
40  */
41 private const gs_color_space_type gs_color_space_type_DeviceGray = {
42     gs_color_space_index_DeviceGray, true, true,
43     &st_base_color_space, gx_num_components_1,
44     gx_no_base_space,
45     gx_init_paint_1, gx_restrict01_paint_1,
46     gx_same_concrete_space,
47     gx_concretize_DeviceGray, gx_remap_concrete_DGray,
48     gx_remap_DeviceGray, gx_no_install_cspace,
49     gx_spot_colors_set_overprint,
50     gx_no_adjust_cspace_count, gx_no_adjust_color_count,
51     gx_serialize_cspace_type,
52     gx_cspace_is_linear_default
53 };
54 private const gs_color_space_type gs_color_space_type_DeviceRGB = {
55     gs_color_space_index_DeviceRGB, true, true,
56     &st_base_color_space, gx_num_components_3,
57     gx_no_base_space,
58     gx_init_paint_3, gx_restrict01_paint_3,
59     gx_same_concrete_space,
60     gx_concretize_DeviceRGB, gx_remap_concrete_DRGB,
61     gx_remap_DeviceRGB, gx_no_install_cspace,
62     gx_spot_colors_set_overprint,
63     gx_no_adjust_cspace_count, gx_no_adjust_color_count,
64     gx_serialize_cspace_type,
65     gx_cspace_is_linear_default
66 };
67 
68 private cs_proc_set_overprint(gx_set_overprint_DeviceCMYK);
69 
70 private const gs_color_space_type gs_color_space_type_DeviceCMYK = {
71     gs_color_space_index_DeviceCMYK, true, true,
72     &st_base_color_space, gx_num_components_4,
73     gx_no_base_space,
74     gx_init_paint_4, gx_restrict01_paint_4,
75     gx_same_concrete_space,
76     gx_concretize_DeviceCMYK, gx_remap_concrete_DCMYK,
77     gx_remap_DeviceCMYK, gx_no_install_cspace,
78     gx_set_overprint_DeviceCMYK,
79     gx_no_adjust_cspace_count, gx_no_adjust_color_count,
80     gx_serialize_cspace_type,
81     gx_cspace_is_linear_default
82 };
83 
84 /* Structure descriptors */
85 public_st_color_space();
86 public_st_base_color_space();
87 
88 
89 /* ------ Create/copy/destroy ------ */
90 
91 void
gs_cspace_init(gs_color_space * pcs,const gs_color_space_type * pcstype,gs_memory_t * mem,bool isheap)92 gs_cspace_init(gs_color_space *pcs, const gs_color_space_type * pcstype,
93 	       gs_memory_t *mem, bool isheap)
94 {
95     pcs->type = pcstype;
96     pcs->pmem = isheap ? mem : NULL;
97     pcs->id = gs_next_ids(mem, 1);
98 }
99 
100 int
gs_cspace_alloc(gs_color_space ** ppcspace,const gs_color_space_type * pcstype,gs_memory_t * mem)101 gs_cspace_alloc(gs_color_space ** ppcspace,
102 		const gs_color_space_type * pcstype,
103 		gs_memory_t * mem)
104 {
105     gs_color_space *pcspace =
106 	gs_alloc_struct(mem, gs_color_space, &st_color_space,
107 			"gs_cspace_alloc");
108 
109     if (pcspace == 0)
110 	return_error(gs_error_VMerror);
111     if (pcstype != 0)
112         gs_cspace_init(pcspace, pcstype, mem, true);
113     *ppcspace = pcspace;
114     return 0;
115 }
116 
117 int
gs_cspace_init_DeviceGray(const gs_memory_t * mem,gs_color_space * pcs)118 gs_cspace_init_DeviceGray(const gs_memory_t *mem, gs_color_space *pcs)
119 {
120     /* parameterless color space; no re-entrancy problems */
121     static gs_color_space  dev_gray_proto;
122 
123     if (dev_gray_proto.id == 0)
124         gs_cspace_init( &dev_gray_proto,
125                         &gs_color_space_type_DeviceGray,
126                         (gs_memory_t *)mem, false );
127     *pcs = dev_gray_proto;
128     return 0;
129 }
130 int
gs_cspace_build_DeviceGray(gs_color_space ** ppcspace,gs_memory_t * pmem)131 gs_cspace_build_DeviceGray(gs_color_space ** ppcspace, gs_memory_t * pmem)
132 {
133     int     code = gs_cspace_alloc(ppcspace, NULL, pmem);
134 
135     if (code >= 0)
136         code = gs_cspace_init_DeviceGray(pmem, *ppcspace);
137     return code;
138 }
139 
140 int
gs_cspace_init_DeviceRGB(const gs_memory_t * mem,gs_color_space * pcs)141 gs_cspace_init_DeviceRGB(const gs_memory_t *mem, gs_color_space *pcs)
142 {
143     /* parameterless color space; no re-entrancy problems */
144     static gs_color_space  dev_rgb_proto;
145 
146     if (dev_rgb_proto.id == 0)
147         gs_cspace_init( &dev_rgb_proto,
148                         &gs_color_space_type_DeviceRGB,
149                         (gs_memory_t *)mem, false );
150     *pcs = dev_rgb_proto;
151     return 0;
152 }
153 int
gs_cspace_build_DeviceRGB(gs_color_space ** ppcspace,gs_memory_t * pmem)154 gs_cspace_build_DeviceRGB(gs_color_space ** ppcspace, gs_memory_t * pmem)
155 {
156     int     code = gs_cspace_alloc(ppcspace, NULL, pmem);
157 
158     if (code >= 0)
159         code = gs_cspace_init_DeviceRGB(pmem, *ppcspace);
160     return code;
161 }
162 
163 int
gs_cspace_init_DeviceCMYK(const gs_memory_t * mem,gs_color_space * pcs)164 gs_cspace_init_DeviceCMYK(const gs_memory_t *mem, gs_color_space *pcs)
165 {
166     /* parameterless color space; no re-entrancy problems */
167     static gs_color_space  dev_cmyk_proto;
168 
169     if (dev_cmyk_proto.id == 0)
170         gs_cspace_init( &dev_cmyk_proto,
171                         &gs_color_space_type_DeviceCMYK,
172                         (gs_memory_t *)mem, false );
173     *pcs = dev_cmyk_proto;
174     return 0;
175 }
176 int
gs_cspace_build_DeviceCMYK(gs_color_space ** ppcspace,gs_memory_t * pmem)177 gs_cspace_build_DeviceCMYK(gs_color_space ** ppcspace, gs_memory_t * pmem)
178 {
179     int     code = gs_cspace_alloc(ppcspace, NULL, pmem);
180 
181     if (code >= 0)
182         code = gs_cspace_init_DeviceCMYK(pmem, *ppcspace);
183     return code;
184 }
185 
186 /*
187  * Copy just enough of a color space object.  This will do the right thing
188  * for copying color spaces into the base or alternate color space of a
189  * compound color space when legal, but it can't check that the operation is
190  * actually legal.
191  */
192 inline private void
cs_copy(gs_color_space * pcsto,const gs_color_space * pcsfrom)193 cs_copy(gs_color_space *pcsto, const gs_color_space *pcsfrom)
194 {
195     memcpy(pcsto, pcsfrom, pcsfrom->type->stype->ssize);
196 }
197 
198 /* Copy a color space into one newly allocated by the caller. */
199 void
gs_cspace_init_from(gs_color_space * pcsto,const gs_color_space * pcsfrom)200 gs_cspace_init_from(gs_color_space * pcsto, const gs_color_space * pcsfrom)
201 {
202     cs_copy(pcsto, pcsfrom);
203     (*pcsto->type->adjust_cspace_count)(pcsto, 1);
204 }
205 
206 /* Assign a color space into a previously initialized one. */
207 void
gs_cspace_assign(gs_color_space * pdest,const gs_color_space * psrc)208 gs_cspace_assign(gs_color_space * pdest, const gs_color_space * psrc)
209 {
210     /* check for a = a */
211     if (pdest == psrc)
212 	return;
213     (*psrc->type->adjust_cspace_count)(psrc, 1);
214     (*pdest->type->adjust_cspace_count)(pdest, -1);
215     cs_copy(pdest, psrc);
216 }
217 
218 
219 /* Prepare to free a color space. */
220 void
gs_cspace_release(gs_color_space * pcs)221 gs_cspace_release(gs_color_space * pcs)
222 {
223     (*pcs->type->adjust_cspace_count)(pcs, -1);
224 }
225 
226 /* ------ Accessors ------ */
227 
228 /* Get the index of a color space. */
229 gs_color_space_index
gs_color_space_get_index(const gs_color_space * pcs)230 gs_color_space_get_index(const gs_color_space * pcs)
231 {
232     return pcs->type->index;
233 }
234 
235 /* Get the number of components in a color space. */
236 int
gs_color_space_num_components(const gs_color_space * pcs)237 gs_color_space_num_components(const gs_color_space * pcs)
238 {
239     return cs_num_components(pcs);
240 }
241 
242 /* Restrict a color to its legal range. */
243 void
gs_color_space_restrict_color(gs_client_color * pcc,const gs_color_space * pcs)244 gs_color_space_restrict_color(gs_client_color *pcc, const gs_color_space *pcs)
245 {
246     cs_restrict_color(pcc, pcs);
247 }
248 
249 int
gx_num_components_1(const gs_color_space * pcs)250 gx_num_components_1(const gs_color_space * pcs)
251 {
252     return 1;
253 }
254 int
gx_num_components_3(const gs_color_space * pcs)255 gx_num_components_3(const gs_color_space * pcs)
256 {
257     return 3;
258 }
259 int
gx_num_components_4(const gs_color_space * pcs)260 gx_num_components_4(const gs_color_space * pcs)
261 {
262     return 4;
263 }
264 
265 /*
266  * For color spaces that have a base or alternative color space, return that
267  * color space. Otherwise return null.
268  */
269 const gs_color_space *
gs_cspace_base_space(const gs_color_space * pcspace)270 gs_cspace_base_space(const gs_color_space * pcspace)
271 {
272     return cs_base_space(pcspace);
273 }
274 
275 const gs_color_space *
gx_no_base_space(const gs_color_space * pcspace)276 gx_no_base_space(const gs_color_space * pcspace)
277 {
278     return NULL;
279 }
280 
281 /* ------ Other implementation procedures ------ */
282 
283 /* Null color space installation procedure. */
284 int
gx_no_install_cspace(const gs_color_space * pcs,gs_state * pgs)285 gx_no_install_cspace(const gs_color_space * pcs, gs_state * pgs)
286 {
287     return 0;
288 }
289 
290 /*
291  * Push an overprint compositor onto the current device indicating that,
292  * at most, the spot color parameters are to be preserved.
293  *
294  * This routine should be used for all Device, CIEBased, and ICCBased
295  * color spaces, except for DeviceCMKY. The latter color space requires a
296  * special verson that supports overprint mode.
297  */
298 int
gx_spot_colors_set_overprint(const gs_color_space * pcs,gs_state * pgs)299 gx_spot_colors_set_overprint(const gs_color_space * pcs, gs_state * pgs)
300 {
301     gs_imager_state *       pis = (gs_imager_state *)pgs;
302     gs_overprint_params_t   params;
303 
304     if ((params.retain_any_comps = pis->overprint))
305         params.retain_spot_comps = true;
306     pgs->effective_overprint_mode = 0;
307     return gs_state_update_overprint(pgs, &params);
308 }
309 
310 
311 private bool
check_single_comp(int comp,frac targ_val,int ncomps,const frac * pval)312 check_single_comp(int comp, frac targ_val, int ncomps, const frac * pval)
313 {
314     int     i;
315 
316     for (i = 0; i < ncomps; i++) {
317         if ( (i != comp && pval[i] != frac_0)  ||
318              (i == comp && pval[i] != targ_val)  )
319             return false;
320     }
321     return true;
322 }
323 
324 /*
325  * Determine if the current color model is a "DeviceCMYK" color model, and
326  * if so what are its process color components. This information is required
327  * only if overprint is true and overprint mode is set to 1.
328  *
329  * A color model is considered a "DeviceCMYK" color model if it supports the
330  * cyan, magenta, yellow, and black color components, and maps the DeviceCMYK
331  * color model components directly to these color components. Note that this
332  * does not require any particular component order, allows for additional
333  * spot color components, and does admit DeviceN color spaces if they have
334  * the requisite behavior.
335  *
336  * If the color model is a "DeviceCMYK" color model, return the set of
337  * process color components; otherwise return 0.
338  */
339 private gx_color_index
check_cmyk_color_model_comps(gx_device * dev)340 check_cmyk_color_model_comps(gx_device * dev)
341 {
342     gx_device_color_info *          pcinfo = &dev->color_info;
343     int                             ncomps = pcinfo->num_components;
344     int                             cyan_c, magenta_c, yellow_c, black_c;
345     const gx_cm_color_map_procs *   pprocs;
346     cm_map_proc_cmyk((*map_cmyk));
347     frac                            frac_14 = frac_1 / 4;
348     frac                            out[GX_DEVICE_COLOR_MAX_COMPONENTS];
349     gx_color_index                  process_comps;
350 
351     /* check for the appropriate components */
352     if ( ncomps < 4                                       ||
353          (cyan_c = dev_proc(dev, get_color_comp_index)(
354                        dev,
355                        "Cyan",
356                        sizeof("Cyan") - 1,
357                        NO_COMP_NAME_TYPE )) < 0           ||
358          cyan_c == GX_DEVICE_COLOR_MAX_COMPONENTS         ||
359          (magenta_c = dev_proc(dev, get_color_comp_index)(
360                           dev,
361                           "Magenta",
362                           sizeof("Magenta") - 1,
363                           NO_COMP_NAME_TYPE )) < 0        ||
364          magenta_c == GX_DEVICE_COLOR_MAX_COMPONENTS      ||
365          (yellow_c = dev_proc(dev, get_color_comp_index)(
366                         dev,
367                         "Yellow",
368                         sizeof("Yellow") - 1,
369                         NO_COMP_NAME_TYPE )) < 0               ||
370          yellow_c == GX_DEVICE_COLOR_MAX_COMPONENTS       ||
371          (black_c = dev_proc(dev, get_color_comp_index)(
372                         dev,
373                         "Black",
374                         sizeof("Black") - 1,
375                         NO_COMP_NAME_TYPE )) < 0                         ||
376          black_c == GX_DEVICE_COLOR_MAX_COMPONENTS          )
377         return 0;
378 
379     /* check the mapping */
380     if ( (pprocs = dev_proc(dev, get_color_mapping_procs)(dev)) == 0 ||
381          (map_cmyk = pprocs->map_cmyk) == 0                            )
382         return 0;
383 
384     map_cmyk(dev, frac_14, frac_0, frac_0, frac_0, out);
385     if (!check_single_comp(cyan_c, frac_14, ncomps, out))
386         return 0;
387     map_cmyk(dev, frac_0, frac_14, frac_0, frac_0, out);
388     if (!check_single_comp(magenta_c, frac_14, ncomps, out))
389         return 0;
390     map_cmyk(dev, frac_0, frac_0, frac_14, frac_0, out);
391     if (!check_single_comp(yellow_c, frac_14, ncomps, out))
392         return false;
393     map_cmyk(dev, frac_0, frac_0, frac_0, frac_14, out);
394     if (!check_single_comp(black_c, frac_14, ncomps, out))
395         return 0;
396 
397     process_comps =  ((gx_color_index)1 << cyan_c)
398                    | ((gx_color_index)1 << magenta_c)
399                    | ((gx_color_index)1 << yellow_c)
400                    | ((gx_color_index)1 << black_c);
401     pcinfo->opmode = GX_CINFO_OPMODE;
402     pcinfo->process_comps = process_comps;
403     return process_comps;
404 }
405 
406 /*
407  * This set_overprint method is unique. If overprint is true, overprint
408  * mode is set to 1, the process color model has DeviceCMYK behavior (see
409  * the comment ahead of gx_is_cmyk_color_model above), and the device
410  * color is set, the device color needs to be considered in setting up
411  * the set of drawn components.
412  */
413 private int
gx_set_overprint_DeviceCMYK(const gs_color_space * pcs,gs_state * pgs)414 gx_set_overprint_DeviceCMYK(const gs_color_space * pcs, gs_state * pgs)
415 {
416     gx_device *             dev = pgs->device;
417     gx_device_color_info *  pcinfo = (dev == 0 ? 0 : &dev->color_info);
418     gx_color_index          drawn_comps = 0;
419     gs_overprint_params_t   params;
420 
421     /* check if we require special handling */
422     if ( !pgs->overprint                      ||
423          pgs->overprint_mode != 1             ||
424          pcinfo == 0                          ||
425          pcinfo->opmode == GX_CINFO_OPMODE_NOT  )
426         return gx_spot_colors_set_overprint(pcs, pgs);
427 
428     /* check if color model behavior must be determined */
429     if (pcinfo->opmode == GX_CINFO_OPMODE_UNKNOWN)
430         drawn_comps = check_cmyk_color_model_comps(dev);
431     else
432         drawn_comps = pcinfo->process_comps;
433     if (drawn_comps == 0)
434         return gx_spot_colors_set_overprint(pcs, pgs);
435 
436     /* correct for any zero'ed color components */
437     pgs->effective_overprint_mode = 1;
438     if (color_is_set(pgs->dev_color)) {
439         gx_color_index  nz_comps;
440         int             code;
441         dev_color_proc_get_nonzero_comps((*procp));
442 
443         procp = pgs->dev_color->type->get_nonzero_comps;
444         if ((code = procp(pgs->dev_color, dev, &nz_comps)) < 0)
445             return code;
446         drawn_comps &= nz_comps;
447     }
448 
449     params.retain_any_comps = true;
450     params.retain_spot_comps = false;
451     params.drawn_comps = drawn_comps;
452     return gs_state_update_overprint(pgs, &params);
453 }
454 
455 
456 /* Null reference count adjustment procedure. */
457 void
gx_no_adjust_cspace_count(const gs_color_space * pcs,int delta)458 gx_no_adjust_cspace_count(const gs_color_space * pcs, int delta)
459 {
460 }
461 
462 /* A stub for a color mapping linearity check, when it is inapplicable. */
463 int
gx_cspace_no_linear(gs_direct_color_space * cs,const gs_imager_state * pis,gx_device * dev,const gs_client_color * c0,const gs_client_color * c1,const gs_client_color * c2,const gs_client_color * c3,float smoothness)464 gx_cspace_no_linear(gs_direct_color_space *cs, const gs_imager_state * pis,
465 		gx_device * dev,
466 		const gs_client_color *c0, const gs_client_color *c1,
467 		const gs_client_color *c2, const gs_client_color *c3,
468 		float smoothness)
469 {
470     return_error(gs_error_rangecheck);
471 }
472 
473 private inline int
cc2dc(gs_direct_color_space * cs,const gs_imager_state * pis,gx_device * dev,gx_device_color * dc,const gs_client_color * cc)474 cc2dc(gs_direct_color_space *cs, const gs_imager_state * pis, gx_device *dev,
475 	    gx_device_color *dc, const gs_client_color *cc)
476 {
477     return cs->type->remap_color(cc, (const gs_color_space *)cs, dc, pis, dev, gs_color_select_texture);
478 }
479 
480 private inline void
interpolate_cc(gs_client_color * c,const gs_client_color * c0,const gs_client_color * c1,double t,int n)481 interpolate_cc(gs_client_color *c,
482 	const gs_client_color *c0, const gs_client_color *c1, double t, int n)
483 {
484     int i;
485 
486     for (i = 0; i < n; i++)
487 	c->paint.values[i] = c0->paint.values[i] * t + c1->paint.values[i] * (1 - t);
488 }
489 
490 private inline bool
is_dc_nearly_linear(const gx_device * dev,const gx_device_color * c,const gx_device_color * c0,const gx_device_color * c1,double t,int n,float smoothness)491 is_dc_nearly_linear(const gx_device *dev, const gx_device_color *c,
492 	const gx_device_color *c0, const gx_device_color *c1,
493 	double t, int n, float smoothness)
494 {
495     if (c0->type == &gx_dc_type_data_pure) {
496 	int i;
497 	gx_color_index pure0 = c0->colors.pure;
498 	gx_color_index pure1 = c1->colors.pure;
499 	gx_color_index pure = c->colors.pure;
500 
501 	for (i = 0; i < n; i++) {
502 	    int shift = dev->color_info.comp_shift[i];
503 	    int mask = (1 << dev->color_info.comp_bits[i]) - 1;
504 	    int max_color = (i == dev->color_info.gray_index ? dev->color_info.max_gray
505 							     : dev->color_info.max_color);
506 	    int b0 = (pure0 >> shift) & mask, b1 = (pure1 >> shift) & mask;
507 	    int b = (pure >> shift) & mask;
508 	    double bb = b0 * t + b1 * (1 - t);
509 
510 	    if (any_abs(b - bb) > max_color * smoothness)
511 		return false;
512 	}
513 	return true;
514     } else {
515 	/* Halftones must not paint with fill_linear_color_*. */
516 	return false;
517     }
518 }
519 
520 /* Default color mapping linearity check, a 2-points case. */
521 private int
gx_cspace_is_linear_in_line(gs_direct_color_space * cs,const gs_imager_state * pis,gx_device * dev,const gs_client_color * c0,const gs_client_color * c1,float smoothness)522 gx_cspace_is_linear_in_line(gs_direct_color_space *cs, const gs_imager_state * pis,
523 		gx_device *dev,
524 		const gs_client_color *c0, const gs_client_color *c1,
525 		float smoothness)
526 {
527     gs_client_color c01a, c01b;
528     gx_device_color d[2], d01a, d01b;
529     int n = cs->type->num_components((const gs_color_space *)cs);
530     int code;
531 
532     code = cc2dc(cs, pis, dev, &d[0], c0);
533     if (code < 0)
534 	return code;
535     code = cc2dc(cs, pis, dev, &d[1], c1);
536     if (code < 0)
537 	return code;
538     interpolate_cc(&c01a, c0, c1, 0.3, n);
539     code = cc2dc(cs, pis, dev, &d01a, &c01a);
540     if (code < 0)
541 	return code;
542     if (!is_dc_nearly_linear(dev, &d01a, &d[0], &d[1], 0.3, n, smoothness))
543 	return 0;
544     interpolate_cc(&c01b, c0, c1, 0.7, n);
545     code = cc2dc(cs, pis, dev, &d01b, &c01b);
546     if (code < 0)
547 	return code;
548     if (!is_dc_nearly_linear(dev, &d01b, &d[0], &d[1], 0.7, n, smoothness))
549 	return 0;
550     return 1;
551 }
552 
553 /* Default color mapping linearity check, a triangle case. */
554 private int
gx_cspace_is_linear_in_triangle(gs_direct_color_space * cs,const gs_imager_state * pis,gx_device * dev,const gs_client_color * c0,const gs_client_color * c1,const gs_client_color * c2,float smoothness)555 gx_cspace_is_linear_in_triangle(gs_direct_color_space *cs, const gs_imager_state * pis,
556 		gx_device *dev,
557 		const gs_client_color *c0, const gs_client_color *c1,
558 		const gs_client_color *c2, float smoothness)
559 {
560     /* We check 4 points - the median center, and middle points of 3 sides.
561        Hopely this is enough for reasonable color spaces and color renderings.
562        Note it gives 7 points for a quadrangle. */
563     gs_client_color c01, c12, c20, c012;
564     gx_device_color d[3], d01, d12, d20, d012;
565     int n = cs->type->num_components((const gs_color_space *)cs);
566     int code;
567 
568     code = cc2dc(cs, pis, dev, &d[0], c0);
569     if (code < 0)
570 	return code;
571     code = cc2dc(cs, pis, dev, &d[1], c1);
572     if (code < 0)
573 	return code;
574     code = cc2dc(cs, pis, dev, &d[2], c2);
575     if (code < 0)
576 	return code;
577 
578     interpolate_cc(&c01, c0, c1, 0.5, n);
579     code = cc2dc(cs, pis, dev, &d01, &c01);
580     if (code < 0)
581 	return code;
582     if (!is_dc_nearly_linear(dev, &d01, &d[0], &d[1], 0.5, n, smoothness))
583 	return 0;
584 
585     interpolate_cc(&c012, c2, &c01, 2.0 / 3, n);
586     code = cc2dc(cs, pis, dev, &d012, &c012);
587     if (code < 0)
588 	return code;
589     if (!is_dc_nearly_linear(dev, &d012, &d[2], &d01, 2.0 / 3, n, smoothness))
590 	return 0;
591 
592     interpolate_cc(&c12, c1, c2, 0.5, n);
593     code = cc2dc(cs, pis, dev, &d12, &c12);
594     if (code < 0)
595 	return code;
596     if (!is_dc_nearly_linear(dev, &d12, &d[1], &d[2], 0.5, n, smoothness))
597 	return 0;
598 
599     interpolate_cc(&c20, c2, c0, 0.5, n);
600     code = cc2dc(cs, pis, dev, &d20, &c20);
601     if (code < 0)
602 	return code;
603     if (!is_dc_nearly_linear(dev, &d20, &d[2], &d[0], 0.5, n, smoothness))
604 	return 0;
605     return 1;
606 }
607 
608 /* Default color mapping linearity check. */
609 int
gx_cspace_is_linear_default(gs_direct_color_space * cs,const gs_imager_state * pis,gx_device * dev,const gs_client_color * c0,const gs_client_color * c1,const gs_client_color * c2,const gs_client_color * c3,float smoothness)610 gx_cspace_is_linear_default(gs_direct_color_space *cs, const gs_imager_state * pis,
611 		gx_device *dev,
612 		const gs_client_color *c0, const gs_client_color *c1,
613 		const gs_client_color *c2, const gs_client_color *c3,
614 		float smoothness)
615 {
616     /* Assuming 2 <= nc <= 4. We don't need other cases. */
617     /* With nc == 4 assuming a convex plain quadrangle in the client color space. */
618     int code;
619 
620     if (dev->color_info.separable_and_linear != GX_CINFO_SEP_LIN)
621 	return_error(gs_error_rangecheck);
622     if (c2 == NULL)
623 	return gx_cspace_is_linear_in_line(cs, pis, dev, c0, c1, smoothness);
624     code = gx_cspace_is_linear_in_triangle(cs, pis, dev, c0, c1, c2, smoothness);
625     if (code <= 0)
626 	return code;
627     if (c3 == NULL)
628 	return 1;
629     return gx_cspace_is_linear_in_triangle(cs, pis, dev, c1, c2, c3, smoothness);
630 }
631 
632 /* Serialization. */
633 int
gx_serialize_cspace_type(const gs_color_space * pcs,stream * s)634 gx_serialize_cspace_type(const gs_color_space * pcs, stream * s)
635 {
636     const gs_color_space_type * type = pcs->type;
637     uint n;
638     return sputs(s, (const byte *)&type->index, sizeof(type->index), &n);
639 }
640 
641 /* GC procedures */
642 
643 private
ENUM_PTRS_BEGIN_PROC(color_space_enum_ptrs)644 ENUM_PTRS_BEGIN_PROC(color_space_enum_ptrs)
645 {
646     EV_CONST gs_color_space *pcs = vptr;
647 
648     return ENUM_USING(*pcs->type->stype, vptr, size, index);
649     ENUM_PTRS_END_PROC
650 }
651 private
RELOC_PTRS_WITH(color_space_reloc_ptrs,gs_color_space * pcs)652 RELOC_PTRS_WITH(color_space_reloc_ptrs, gs_color_space *pcs)
653 {
654     RELOC_USING(*pcs->type->stype, vptr, size);
655 }
656 RELOC_PTRS_END
657