xref: /plan9/sys/src/cmd/gs/src/gsicc.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
1 /* Copyright (C) 2001 1999 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: gsicc.c,v 1.13 2004/04/08 07:59:19 igor Exp $ */
18 /* Implementation of the ICCBased color space family */
19 
20 #include "math_.h"
21 #include "memory_.h"
22 #include "gx.h"
23 #include "gserrors.h"
24 #include "gsstruct.h"
25 #include "stream.h"
26 #include "gxcspace.h"		/* for gxcie.c */
27 #include "gxarith.h"
28 #include "gxcie.h"
29 #include "gzstate.h"
30 #include "stream.h"
31 #include "icc.h"		/* must precede icc.h */
32 #include "gsicc.h"
33 
34 
35 typedef struct _icmFileGs icmFileGs;
36 
37 struct _icmFileGs {
38     ICM_FILE_BASE
39 
40     /* Private: */
41     stream *strp;
42 };
43 
44 /* Garbage collection code */
45 
46 /*
47  * Discard a gs_cie_icc_s structure. This requires that we call the
48  * destructor for ICC profile, lookup, and file objects (which are
49  * stored in "foreign" memory).
50  *
51  * No special action is taken with respect to the stream pointer; that is
52  * the responsibility of the client.  */
53 private void
cie_icc_finalize(void * pvicc_info)54 cie_icc_finalize(void * pvicc_info)
55 {
56     gs_cie_icc *    picc_info = (gs_cie_icc *)pvicc_info;
57 
58     if (picc_info->plu != NULL) {
59         picc_info->plu->del(picc_info->plu);
60         picc_info->plu = NULL;
61     }
62     if (picc_info->picc != NULL) {
63         picc_info->picc->del(picc_info->picc);
64         picc_info->picc = NULL;
65     }
66     if (picc_info->pfile != NULL) {
67         picc_info->pfile->del(picc_info->pfile);
68         picc_info->pfile = NULL;
69     }
70 }
71 
72 private_st_cie_icc();
73 
74 /*
75  * Because the color space structure stores alternative color space in-line,
76  * we must enumerate and relocate pointers in these space explicity.
77  */
78 gs_private_st_composite( st_color_space_CIEICC,
79                          gs_paint_color_space,
80                          "gs_color_space_CIEICC",
81                          cs_CIEICC_enum_ptrs,
82                          cs_CIEICC_reloc_ptrs );
83 
84 /* pointer enumeration routine */
85 private
86 ENUM_PTRS_WITH(cs_CIEICC_enum_ptrs, gs_color_space * pcs)
87         return ENUM_USING( *pcs->params.icc.alt_space.type->stype,
88                            &pcs->params.icc.alt_space,
89                            sizeof(pcs->params.separation.alt_space),
90                            index - 1 );
91 
92         ENUM_PTR(0, gs_color_space, params.icc.picc_info);
93 ENUM_PTRS_END
94 
95 /* pointer relocation routine */
96 private
97 RELOC_PTRS_WITH(cs_CIEICC_reloc_ptrs, gs_color_space * pcs)
98     RELOC_PTR(gs_color_space, params.icc.picc_info);
99     RELOC_USING( *pcs->params.icc.alt_space.type->stype,
100                  &pcs->params.icc.alt_space,
101                  sizeof(pcs->params.separation.alt_space) );
102 RELOC_PTRS_END
103 
104 
105 /*
106  * Color space methods for ICCBased color spaces.
107  *
108  * As documented, ICCBased color spaces may be used as both base and
109  * alternative color spaces. Futhermore,, they can themselves contain paint
110  * color spaces as alternative color space. In this implementation we allow
111  * them to be used as base and alternative color spaces, but only to contain
112  * "small" base color spaces (CIEBased or smaller). This arrangement avoids
113  * breaking the color space heirarchy. Providing a more correct arrangement
114  * requires a major change in the color space mechanism.
115  *
116  * Several of the methods used by ICCBased color space apply as well to
117  * DeviceN color spaces, in that they are generic to color spaces having
118  * a variable number of components. We have elected not to attempt to
119  * extract and combine these operations, because this would save only a
120  * small amount of code, and much more could be saved by intorducing certain
121  * common elements (ranges, number of components, etc.) into the color space
122  * root class.
123  */
124 private cs_proc_num_components(gx_num_components_CIEICC);
125 private cs_proc_base_space(gx_alt_space_CIEICC);
126 private cs_proc_init_color(gx_init_CIEICC);
127 private cs_proc_restrict_color(gx_restrict_CIEICC);
128 private cs_proc_concrete_space(gx_concrete_space_CIEICC);
129 private cs_proc_concretize_color(gx_concretize_CIEICC);
130 private cs_proc_adjust_cspace_count(gx_adjust_cspace_CIEICC);
131 private cs_proc_serialize(gx_serialize_CIEICC);
132 
133 private const gs_color_space_type gs_color_space_type_CIEICC = {
134     gs_color_space_index_CIEICC,    /* index */
135     true,                           /* can_be_base_space */
136     true,                           /* can_be_alt_space */
137     &st_color_space_CIEICC,         /* stype - structure descriptor */
138     gx_num_components_CIEICC,       /* num_components */
139     gx_alt_space_CIEICC,            /* base_space */
140     gx_init_CIEICC,                 /* init_color */
141     gx_restrict_CIEICC,             /* restrict_color */
142     gx_concrete_space_CIEICC,       /* concrete_space */
143     gx_concretize_CIEICC,           /* concreteize_color */
144     NULL,                           /* remap_concrete_color */
145     gx_default_remap_color,         /* remap_color */
146     gx_install_CIE,                 /* install_cpsace */
147     gx_spot_colors_set_overprint,   /* set_overprint */
148     gx_adjust_cspace_CIEICC,        /* adjust_cspace_count */
149     gx_no_adjust_color_count,       /* adjust_color_count */
150     gx_serialize_CIEICC,		    /* serialize */
151     gx_cspace_is_linear_default
152 };
153 
154 
155 /*
156  * Return the number of components used by a ICCBased color space - 1, 3, or 4
157  */
158 private int
gx_num_components_CIEICC(const gs_color_space * pcs)159 gx_num_components_CIEICC(const gs_color_space * pcs)
160 {
161     return pcs->params.icc.picc_info->num_components;
162 }
163 
164 /*
165  * Return the alternative space for an ICCBasee color space, but only if
166  * that space is being used.
167  */
168 private const gs_color_space *
gx_alt_space_CIEICC(const gs_color_space * pcs)169 gx_alt_space_CIEICC(const gs_color_space * pcs)
170 {
171     return (pcs->params.icc.picc_info->picc == NULL)
172                 ? (const gs_color_space *)&pcs->params.icc.alt_space
173                 : NULL;
174 }
175 
176 /*
177  * Set the initial client color for an ICCBased color space. The convention
178  * suggested by the ICC specification is to set all components to 0.
179  */
180 private void
gx_init_CIEICC(gs_client_color * pcc,const gs_color_space * pcs)181 gx_init_CIEICC(gs_client_color * pcc, const gs_color_space * pcs)
182 {
183     int     i, ncomps = pcs->params.icc.picc_info->num_components;
184 
185     for (i = 0; i < ncomps; ++i)
186 	pcc->paint.values[i] = 0.0;
187 
188     /* make sure that [ 0, ... 0] is in range */
189     gx_restrict_CIEICC(pcc, pcs);
190 }
191 
192 /*
193  * Restrict an color to the range specified for an ICCBased color space.
194  */
195 private void
gx_restrict_CIEICC(gs_client_color * pcc,const gs_color_space * pcs)196 gx_restrict_CIEICC(gs_client_color * pcc, const gs_color_space * pcs)
197 {
198     int                 i, ncomps = pcs->params.icc.picc_info->num_components;
199     const gs_range *    ranges = pcs->params.icc.picc_info->Range.ranges;
200 
201     for (i = 0; i < ncomps; ++i) {
202         floatp  v = pcc->paint.values[i];
203         floatp  rmin = ranges[i].rmin, rmax = ranges[i].rmax;
204 
205         if (v < rmin)
206             pcc->paint.values[i] = rmin;
207         else if (v > rmax)
208             pcc->paint.values[i] = rmax;
209     }
210 }
211 
212 /*
213  * Return the conrecte space to which this color space will map. If the
214  * ICCBased color space is being used in native mode, the concrete space
215  * will be dependent on the current color rendering dictionary, as it is
216  * for all CIE bases. If the alternate color space is being used, then
217  * this question is passed on the the appropriate method of that space.
218  */
219 private const gs_color_space *
gx_concrete_space_CIEICC(const gs_color_space * pcs,const gs_imager_state * pis)220 gx_concrete_space_CIEICC(const gs_color_space * pcs, const gs_imager_state * pis)
221 {
222     if (pcs->params.icc.picc_info->picc == NULL) {
223         const gs_color_space *  pacs = (const gs_color_space *)
224                                         &pcs->params.icc.alt_space;
225 
226         return cs_concrete_space(pacs, pis);
227     } else
228         return gx_concrete_space_CIE(NULL, pis);
229 }
230 
231 /*
232  * Convert an ICCBased color space to a concrete color space.
233  */
234 private int
gx_concretize_CIEICC(const gs_client_color * pcc,const gs_color_space * pcs,frac * pconc,const gs_imager_state * pis)235 gx_concretize_CIEICC(
236     const gs_client_color * pcc,
237     const gs_color_space *  pcs,
238     frac *                  pconc,
239     const gs_imager_state * pis )
240 {
241     const gs_icc_params *   picc_params = &pcs->params.icc;
242     const gs_cie_icc *      picc_info = picc_params->picc_info;
243     stream *                instrp = picc_info->instrp;
244     icc *                   picc = picc_info->picc;
245     double                  inv[4], outv[3];
246     cie_cached_vector3      vlmn;
247     gs_client_color         lcc = *pcc;
248     int                     i, ncomps = picc_info->num_components;
249 
250     /* use the altenate space concretize if appropriate */
251     if (picc == NULL)
252         return picc_params->alt_space.type->concretize_color(
253                             pcc,
254                             (const gs_color_space *)&picc_params->alt_space,
255                             pconc,
256                             pis );
257 
258     /* set up joint cache as required */
259     CIE_CHECK_RENDERING(pcs, pconc, pis, return 0);
260 
261     /* verify and update the stream pointer */
262     if (picc_info->file_id != (instrp->read_id | instrp->write_id))
263         return_error(gs_error_ioerror);
264     ((icmFileGs *)picc->fp)->strp = instrp;
265 
266     /* translate the input components */
267     gx_restrict_CIEICC(&lcc, pcs);
268     for (i = 0; i < ncomps; i++)
269         inv[i] = lcc.paint.values[i];
270 
271     /* For input Lab color space massage the values into Lab range */
272 
273     if (picc_info->plu->e_inSpace == icSigLabData) {
274         inv[0] *= 100;
275         inv[1] = inv[1]*255 - 128;
276         inv[2] = inv[2]*255 - 128;
277     }
278 
279     /*
280      * Perform the lookup operation. A return value of 1 indicates that
281      * clipping occurred somewhere in the operation, but the result is
282      * legitimate. Other non-zero return values indicate an error, which
283      * should not occur in practice.
284      */
285     if (picc_info->plu->lookup(picc_info->plu, outv, inv) > 1)
286         return_error(gs_error_unregistered);
287 
288     /* if the output is in the CIE L*a*b* space, convert to XYZ */
289     if (picc_info->pcs_is_cielab) {
290         floatp              f[3];
291         const gs_vector3 *  pwhtpt = &picc_info->common.points.WhitePoint;
292 
293 
294         f[1] = (outv[0] + 16.0) / 116.0;
295         f[0] = f[1] + outv[1] / 500.0;
296         f[2] = f[1] - outv[2] / 200;
297 
298         for (i = 0; i < 3; i++) {
299             if (f[i] >= 6.0 / 29.0)
300                 outv[i] = f[i] * f[i] * f[i];
301             else
302                 outv[i] = 108.0 * (f[i] - 4.0 / 29.0) / 841.0;
303         }
304 
305         /*
306          * The connection space white-point is known to be D50, but we
307          * use the more general form in case of future revisions.
308          */
309         outv[0] *= pwhtpt->u;
310         outv[1] *= pwhtpt->v;
311         outv[2] *= pwhtpt->w;
312     }
313 
314     /* translate the output */
315     vlmn.u = float2cie_cached(outv[0]);
316     vlmn.v = float2cie_cached(outv[1]);
317     vlmn.w = float2cie_cached(outv[2]);
318 
319     gx_cie_remap_finish(vlmn, pconc, pis, pcs);
320     return 0;
321 }
322 
323 /*
324  * Handle a reference or de-reference of the prameter structure of an
325  * ICCBased color space. For the purposes of this routine, the color space
326  * is considered a reference rather than an object, and is not itself
327  * reference counted (an unintuitive but otherwise legitimate state of
328  * affairs).
329  *
330  * Because color spaces store alternative/base color space inline, these
331  * need to have their reference count adjusted explicitly.
332  */
333 private void
gx_adjust_cspace_CIEICC(const gs_color_space * pcs,int delta)334 gx_adjust_cspace_CIEICC(const gs_color_space * pcs, int delta)
335 {
336     const gs_icc_params *   picc_params = &pcs->params.icc;
337 
338     rc_adjust_const(picc_params->picc_info, delta, "gx_adjust_cspace_CIEICC");
339     picc_params->alt_space.type->adjust_cspace_count(
340                 (const gs_color_space *)&picc_params->alt_space, delta );
341 }
342 
343 /*
344  * Increment color space reference counts.
345  */
346 void
gx_increment_cspace_count(const gs_color_space * pcs)347 gx_increment_cspace_count(const gs_color_space * pcs)
348 {
349     pcs->type->adjust_cspace_count(pcs, 1);
350 }
351 
352 private int
icmFileGs_seek(icmFile * pp,long int offset)353 icmFileGs_seek(icmFile *pp, long int offset)
354 {
355     icmFileGs *p = (icmFileGs *)pp;
356 
357     return spseek(p->strp, offset);
358 }
359 
360 private size_t
icmFileGs_read(icmFile * pp,void * buffer,size_t size,size_t count)361 icmFileGs_read(icmFile *pp, void *buffer, size_t size, size_t count)
362 {
363     icmFileGs *p = (icmFileGs *)pp;
364     uint    tot;
365     int     status = sgets(p->strp, buffer, size * count, &tot);
366 
367     return (status < 0) ? status : tot;
368 }
369 
370 private size_t
icmFileGs_write(icmFile * pp,void * buffer,size_t size,size_t count)371 icmFileGs_write(icmFile *pp, void *buffer, size_t size, size_t count)
372 {
373     icmFileGs *p = (icmFileGs *)pp;
374     uint    tot;
375     int     status = sputs(p->strp, buffer, size * count, &tot);
376 
377     return (status < 0) ? status : tot;
378 }
379 
380 private int
icmFileGs_flush(icmFile * pp)381 icmFileGs_flush(icmFile *pp)
382 {
383     icmFileGs *p = (icmFileGs *)pp;
384 
385     return s_std_write_flush(p->strp);
386 }
387 
388 private int
icmFileGs_delete(icmFile * pp)389 icmFileGs_delete(icmFile *pp)
390 {
391     free(pp);
392     return 0;
393 }
394 
395 /**
396  * gx_wrap_icc_stream: Wrap a Ghostscript stream as an icclib file.
397  * @strp: The Ghostscript stream.
398  *
399  * Creates an icmFile object that wraps @stream.
400  *
401  * Note: the memory for this object is allocated using malloc, and the
402  * relocation of the stream pointer is done lazily, before an icclu
403  * operation. It would probably be cleaner to allocate the icmFile in
404  * garbage collected memory, and have the relocation happen there, but
405  * I wanted to minimally modify Jan's working code.
406  *
407  * Return value: the stream wrapped as an icmFile object, or NULL on
408  * error.
409  **/
410 private icmFile *
gx_wrap_icc_stream(stream * strp)411 gx_wrap_icc_stream(stream *strp)
412 {
413     icmFileGs *p;
414 
415     if ((p = (icmFileGs *) calloc(1,sizeof(icmFileGs))) == NULL)
416 	return NULL;
417     p->seek  = icmFileGs_seek;
418     p->read  = icmFileGs_read;
419     p->write = icmFileGs_write;
420     p->flush = icmFileGs_flush;
421     p->del   = icmFileGs_delete;
422 
423     p->strp = strp;
424 
425     return (icmFile *)p;
426 }
427 
428 int
gx_load_icc_profile(gs_cie_icc * picc_info)429 gx_load_icc_profile(gs_cie_icc *picc_info)
430 {
431     stream *        instrp = picc_info->instrp;
432     icc *           picc;
433     icmLuBase * plu = NULL;
434     icmFile *pfile = NULL;
435 
436     /* verify that the file is legitimate */
437     if (picc_info->file_id != (instrp->read_id | instrp->write_id))
438 	return_error(gs_error_ioerror);
439     /*
440      * Load the top-level ICC profile.
441      *
442      * If an ICC profile fails to load, generate an error.
443      *
444      * Testing demonstrates, however, Acrobat Reader silently
445      * ignores the error and uses the alternate color space.
446      * This behaviour is implemented by catching the error using
447      * a stopped context from within the interpreter (gs_icc.ps).
448      *
449      * Failure to allocate the top-level profile object is considered
450      * a limitcheck rather than a VMerror, as profile data structures
451      * are stored in "foreign" memory.
452      */
453     if ((picc = new_icc()) == NULL)
454 	return_error(gs_error_limitcheck);
455     {
456 	icProfileClassSignature profile_class;
457 	icColorSpaceSignature   cspace_type;
458 	gs_vector3 *            ppt;
459 
460 	pfile = gx_wrap_icc_stream (instrp);
461 
462 	if ((picc->read(picc, pfile, 0)) != 0)
463 	    goto return_rangecheck;
464 
465 	/* verify the profile type */
466 	profile_class = picc->header->deviceClass;
467 	if ( profile_class != icSigInputClass     &&
468 	     profile_class != icSigDisplayClass   &&
469 	     profile_class != icSigOutputClass    &&
470 	     profile_class != icSigColorSpaceClass  )
471 	    goto return_rangecheck;
472 
473 	/* verify the profile connection space */
474 	cspace_type = picc->header->pcs;
475 	if (cspace_type == icSigLabData)
476 	    picc_info->pcs_is_cielab = true;
477 	else if (cspace_type == icSigXYZData)
478 	    picc_info->pcs_is_cielab = false;
479 	else
480 	    goto return_rangecheck;
481 
482 	/* verify the source color space */
483 	cspace_type = picc->header->colorSpace;
484 	if (cspace_type == icSigCmykData) {
485 	    if (picc_info->num_components != 4)
486 		goto return_rangecheck;
487 	} else if ( cspace_type == icSigRgbData ||
488 		    cspace_type == icSigLabData   ) {
489 	    if (picc_info->num_components != 3)
490 		goto return_rangecheck;
491 	} else if (cspace_type == icSigGrayData) {
492 	    if (picc_info->num_components != 1)
493 		goto return_rangecheck;
494 	}
495 
496 	/*
497 	 * Fetch the lookup object.
498 	 *
499 	 * PostScript and PDF deal with rendering intent as strictly a
500 	 * rendering dictionary facility. ICC profiles allow a rendering
501 	 * intent to be specified for both the input (device ==> pcs) and
502 	 * output (pcs ==> device) operations. Hence, when using ICCBased
503 	 * color spaces with PDF, no clue is provided as to which source
504 	 * mapping to select.
505 	 *
506 	 * In the absence of other information, there are two possible
507 	 * selections. If our understanding is correct, when relative
508 	 * colorimetry is specified, the icclib code will map source
509 	 * color values to XYZ or L*a*b* values such that the relationship
510 	 * of the source color, relative to the source white and black
511 	 * points, will be the same as the output colors and the
512 	 * profile connection space illuminant (currently always D50)
513 	 * and pure black ([0, 0, 0]). In this case, the white and black
514 	 * points that should be listed in the color space are the
515 	 * profile connection space illuminant (D50) and pure black.
516 	 *
517 	 * If absolute colorimetry is employed, the XYZ or L*a*b* values
518 	 * generated will be absolute in the chromatic sense (they are
519 	 * not literally "absolute", as we still must have overall
520 	 * intensity information inorder to determine weighted spectral
521 	 * power levels). To achieve relative colorimetry for the output,
522 	 * these colors must be evaluated relative to the source white
523 	 * and black points. Hence, in this case, the appropriate white
524 	 * and black points to list in the color space are the source
525 	 * white and black points provided in the profile tag array.
526 	 *
527 	 * In this implementation, we will always request relative
528 	 * colorimetry from the icclib, and so will use the profile
529 	 * connection space illuminant and pure black as the white and
530 	 * black points of the color space. This approach is somewhat
531 	 * simpler, as it allows the color space white point to also
532 	 * be used for L*a*b* to XYZ conversion (otherwise we would
533 	 * need to store the profile connection space illuminant
534 	 * separately for that purpose). The approach does reduce to
535 	 * to some extent the range of mappings that can be achieved
536 	 * via the color rendering dictionary, but for now we believe
537 	 * this loss is not significant.
538 	 *
539 	 * For reasons that are not clear to us, the icclib code does
540 	 * not support relative colorimetry for all color profiles. For
541 	 * this reason, we specify icmDefaultIntent rather than
542 	 * icRelativeColormetric.
543 	 *
544 	 * NB: We are not color experts; our understanding of this area
545 	 *     may well be incorrect.
546 	 */
547 	plu = picc->get_luobj( picc,
548 			       icmFwd,
549 			       icmDefaultIntent,
550 			       0, /* PCS override */
551 			       icmLuOrdNorm );
552 	if (plu == NULL)
553 	    goto return_rangecheck;
554 
555 	/*
556 	 * Get the appropriate white and black points. See the note on
557 	 * rendering intent above for a discussion of why we are using
558 	 * the profile space illuminant and pure black. (Pure black need
559 	 * not be set explicitly, as it is the default.)
560 	 */
561 	ppt = &picc_info->common.points.WhitePoint;
562 	ppt->u = picc->header->illuminant.X;
563 	ppt->v = picc->header->illuminant.Y;
564 	ppt->w = picc->header->illuminant.Z;
565 
566 	picc_info->picc = picc;
567 	picc_info->plu = plu;
568 	picc_info->pfile = pfile;
569     }
570 
571     return 0;
572 
573  return_rangecheck:
574     if (plu != NULL)
575 	plu->del(plu);
576     if (picc != NULL)
577 	picc->del(picc);
578     if (pfile != NULL)
579 	pfile->del(pfile);
580     return_error(gs_error_rangecheck);
581 }
582 
583 /*
584  * Install an ICCBased color space.
585  *
586  * Note that an ICCBased color space must be installed before it is known if
587  * the ICC profile or the alternate color space is to be used.
588  */
589 private int
gx_install_CIEICC(const gs_color_space * pcs,gs_state * pgs)590 gx_install_CIEICC(const gs_color_space * pcs, gs_state * pgs)
591 {
592     const gs_icc_params * picc_params = (const gs_icc_params *)&pcs->params.icc;
593     gs_cie_icc *    picc_info = picc_params->picc_info;
594 
595     /* update the stub information used by the joint caches */
596     gx_cie_load_common_cache(&picc_info->common, pgs);
597     gx_cie_common_complete(&picc_info->common);
598     return gs_cie_cs_complete(pgs, true);
599 }
600 
601 
602 /*
603  * Constructor for ICCBased color space. As with the other color space
604  * constructors, this provides only minimal initialization.
605  */
606 int
gs_cspace_build_CIEICC(gs_color_space ** ppcspace,void * client_data,gs_memory_t * pmem)607 gs_cspace_build_CIEICC(
608     gs_color_space **   ppcspace,
609     void *              client_data,
610     gs_memory_t *       pmem )
611 {
612     gs_cie_icc *        picc_info;
613     gs_color_space *    pcs;
614 
615     /*
616      * The gs_cie_icc_s structure is the only CIE-based color space structure
617      * which accesses additional memory for which it is responsible. We make
618      * use of the finalization procedure to handle this task, so we can use
619      * the generic CIE space build routine (otherwise we would need a
620      * separate build routine that provided its own reference count freeing
621      * procedure).
622      */
623     picc_info = gx_build_cie_space( ppcspace,
624                                     &gs_color_space_type_CIEICC,
625                                     &st_cie_icc,
626                                     pmem );
627 
628     if (picc_info == NULL)
629         return_error(gs_error_VMerror);
630 
631     gx_set_common_cie_defaults(&picc_info->common, client_data);
632     /*
633      * Now set the D50 WhitePoint. The above function does not set any
634      * valid WhitepPoint since PostScript always requires this, but ICC
635      * assumes a D50 WhitePoint as a default
636      */
637     picc_info->common.points.WhitePoint.u = (float)0.9642;		/* Profile illuminant - D50 */
638     picc_info->common.points.WhitePoint.v = 1.0000;
639     picc_info->common.points.WhitePoint.w = (float)0.8249;
640     picc_info->common.install_cspace = gx_install_CIEICC;
641     picc_info->num_components = 0;
642     picc_info->Range = Range4_default;
643     picc_info->instrp = NULL;
644     picc_info->pcs_is_cielab = false;
645     picc_info->picc = NULL;
646     picc_info->plu = NULL;
647     picc_info->pfile = NULL;
648 
649     pcs = *ppcspace;
650     pcs->params.icc.picc_info = picc_info;
651     return 0;
652 }
653 
654 /* ---------------- Serialization. -------------------------------- */
655 
656 private int
gx_serialize_CIEICC(const gs_color_space * pcs,stream * s)657 gx_serialize_CIEICC(const gs_color_space * pcs, stream * s)
658 {
659     const gs_icc_params * p = &pcs->params.icc;
660     gs_cie_icc *picc = p->picc_info;
661     uint n;
662     int code = gx_serialize_cspace_type(pcs, s);
663     long avail, pos, count;
664     byte buf[100];
665 
666     if (code < 0)
667 	return code;
668     code = gx_serialize_cie_common_elements(pcs, s);
669     if (code < 0)
670 	return code;
671     code = sputs(s, (byte *)&picc->num_components, sizeof(picc->num_components), &n);
672     if (code < 0)
673 	return code;
674     code = sputs(s, (byte *)&picc->Range, sizeof(picc->Range), &n);
675     if (code < 0)
676 	return code;
677     if (sseek(picc->instrp, 0) < 0)
678 	return_error(gs_error_unregistered); /* Unimplemented. */
679     if (savailable(picc->instrp, &avail) != 0)
680 	return_error(gs_error_unregistered); /* Unimplemented. */
681     code = sputs(s, (byte *)&avail, sizeof(avail), &n);
682     if (code < 0)
683 	return code;
684     for (pos = 0; pos < avail; pos += count) {
685 	count = min(sizeof(buf), avail - pos);
686 	code = sgets(picc->instrp, buf, count, &n);
687 	if (code < 0)
688 	    return code;
689 	code = sputs(s, buf, count, &n);
690 	if (code < 0)
691 	    return code;
692     }
693     return sputs(s, (byte *)&picc->pcs_is_cielab, sizeof(picc->pcs_is_cielab), &n);
694 }
695