13ff48bf5SDavid du Colombier /* Copyright (C) 2001 1999 Aladdin Enterprises. All rights reserved.
23ff48bf5SDavid du Colombier
3*593dc095SDavid du Colombier This software is provided AS-IS with no warranty, either express or
4*593dc095SDavid du Colombier implied.
53ff48bf5SDavid du Colombier
6*593dc095SDavid du Colombier This software is distributed under license and may not be copied,
7*593dc095SDavid du Colombier modified or distributed except as expressly authorized under the terms
8*593dc095SDavid du Colombier of the license contained in the file LICENSE in this distribution.
93ff48bf5SDavid du Colombier
10*593dc095SDavid du Colombier For more information about licensing, please refer to
11*593dc095SDavid du Colombier http://www.ghostscript.com/licensing/. For information on
12*593dc095SDavid du Colombier commercial licensing, go to http://www.artifex.com/licensing/ or
13*593dc095SDavid du Colombier contact Artifex Software, Inc., 101 Lucas Valley Road #110,
14*593dc095SDavid du Colombier San Rafael, CA 94903, U.S.A., +1(415)492-9861.
153ff48bf5SDavid du Colombier */
163ff48bf5SDavid du Colombier
17*593dc095SDavid du Colombier /* $Id: gsicc.c,v 1.13 2004/04/08 07:59:19 igor Exp $ */
183ff48bf5SDavid du Colombier /* Implementation of the ICCBased color space family */
193ff48bf5SDavid du Colombier
203ff48bf5SDavid du Colombier #include "math_.h"
213ff48bf5SDavid du Colombier #include "memory_.h"
223ff48bf5SDavid du Colombier #include "gx.h"
233ff48bf5SDavid du Colombier #include "gserrors.h"
243ff48bf5SDavid du Colombier #include "gsstruct.h"
253ff48bf5SDavid du Colombier #include "stream.h"
263ff48bf5SDavid du Colombier #include "gxcspace.h" /* for gxcie.c */
273ff48bf5SDavid du Colombier #include "gxarith.h"
283ff48bf5SDavid du Colombier #include "gxcie.h"
293ff48bf5SDavid du Colombier #include "gzstate.h"
303ff48bf5SDavid du Colombier #include "stream.h"
313ff48bf5SDavid du Colombier #include "icc.h" /* must precede icc.h */
323ff48bf5SDavid du Colombier #include "gsicc.h"
333ff48bf5SDavid du Colombier
343ff48bf5SDavid du Colombier
353ff48bf5SDavid du Colombier typedef struct _icmFileGs icmFileGs;
363ff48bf5SDavid du Colombier
373ff48bf5SDavid du Colombier struct _icmFileGs {
383ff48bf5SDavid du Colombier ICM_FILE_BASE
393ff48bf5SDavid du Colombier
403ff48bf5SDavid du Colombier /* Private: */
413ff48bf5SDavid du Colombier stream *strp;
423ff48bf5SDavid du Colombier };
433ff48bf5SDavid du Colombier
443ff48bf5SDavid du Colombier /* Garbage collection code */
453ff48bf5SDavid du Colombier
463ff48bf5SDavid du Colombier /*
473ff48bf5SDavid du Colombier * Discard a gs_cie_icc_s structure. This requires that we call the
483ff48bf5SDavid du Colombier * destructor for ICC profile, lookup, and file objects (which are
493ff48bf5SDavid du Colombier * stored in "foreign" memory).
503ff48bf5SDavid du Colombier *
513ff48bf5SDavid du Colombier * No special action is taken with respect to the stream pointer; that is
523ff48bf5SDavid du Colombier * the responsibility of the client. */
533ff48bf5SDavid du Colombier private void
cie_icc_finalize(void * pvicc_info)543ff48bf5SDavid du Colombier cie_icc_finalize(void * pvicc_info)
553ff48bf5SDavid du Colombier {
563ff48bf5SDavid du Colombier gs_cie_icc * picc_info = (gs_cie_icc *)pvicc_info;
573ff48bf5SDavid du Colombier
583ff48bf5SDavid du Colombier if (picc_info->plu != NULL) {
593ff48bf5SDavid du Colombier picc_info->plu->del(picc_info->plu);
603ff48bf5SDavid du Colombier picc_info->plu = NULL;
613ff48bf5SDavid du Colombier }
623ff48bf5SDavid du Colombier if (picc_info->picc != NULL) {
633ff48bf5SDavid du Colombier picc_info->picc->del(picc_info->picc);
643ff48bf5SDavid du Colombier picc_info->picc = NULL;
653ff48bf5SDavid du Colombier }
663ff48bf5SDavid du Colombier if (picc_info->pfile != NULL) {
673ff48bf5SDavid du Colombier picc_info->pfile->del(picc_info->pfile);
683ff48bf5SDavid du Colombier picc_info->pfile = NULL;
693ff48bf5SDavid du Colombier }
703ff48bf5SDavid du Colombier }
713ff48bf5SDavid du Colombier
723ff48bf5SDavid du Colombier private_st_cie_icc();
733ff48bf5SDavid du Colombier
743ff48bf5SDavid du Colombier /*
753ff48bf5SDavid du Colombier * Because the color space structure stores alternative color space in-line,
763ff48bf5SDavid du Colombier * we must enumerate and relocate pointers in these space explicity.
773ff48bf5SDavid du Colombier */
783ff48bf5SDavid du Colombier gs_private_st_composite( st_color_space_CIEICC,
793ff48bf5SDavid du Colombier gs_paint_color_space,
803ff48bf5SDavid du Colombier "gs_color_space_CIEICC",
813ff48bf5SDavid du Colombier cs_CIEICC_enum_ptrs,
823ff48bf5SDavid du Colombier cs_CIEICC_reloc_ptrs );
833ff48bf5SDavid du Colombier
843ff48bf5SDavid du Colombier /* pointer enumeration routine */
853ff48bf5SDavid du Colombier private
863ff48bf5SDavid du Colombier ENUM_PTRS_WITH(cs_CIEICC_enum_ptrs, gs_color_space * pcs)
873ff48bf5SDavid du Colombier return ENUM_USING( *pcs->params.icc.alt_space.type->stype,
883ff48bf5SDavid du Colombier &pcs->params.icc.alt_space,
893ff48bf5SDavid du Colombier sizeof(pcs->params.separation.alt_space),
903ff48bf5SDavid du Colombier index - 1 );
913ff48bf5SDavid du Colombier
923ff48bf5SDavid du Colombier ENUM_PTR(0, gs_color_space, params.icc.picc_info);
933ff48bf5SDavid du Colombier ENUM_PTRS_END
943ff48bf5SDavid du Colombier
953ff48bf5SDavid du Colombier /* pointer relocation routine */
963ff48bf5SDavid du Colombier private
973ff48bf5SDavid du Colombier RELOC_PTRS_WITH(cs_CIEICC_reloc_ptrs, gs_color_space * pcs)
983ff48bf5SDavid du Colombier RELOC_PTR(gs_color_space, params.icc.picc_info);
993ff48bf5SDavid du Colombier RELOC_USING( *pcs->params.icc.alt_space.type->stype,
1003ff48bf5SDavid du Colombier &pcs->params.icc.alt_space,
1013ff48bf5SDavid du Colombier sizeof(pcs->params.separation.alt_space) );
1023ff48bf5SDavid du Colombier RELOC_PTRS_END
1033ff48bf5SDavid du Colombier
1043ff48bf5SDavid du Colombier
1053ff48bf5SDavid du Colombier /*
1063ff48bf5SDavid du Colombier * Color space methods for ICCBased color spaces.
1073ff48bf5SDavid du Colombier *
1083ff48bf5SDavid du Colombier * As documented, ICCBased color spaces may be used as both base and
1093ff48bf5SDavid du Colombier * alternative color spaces. Futhermore,, they can themselves contain paint
1103ff48bf5SDavid du Colombier * color spaces as alternative color space. In this implementation we allow
1113ff48bf5SDavid du Colombier * them to be used as base and alternative color spaces, but only to contain
1123ff48bf5SDavid du Colombier * "small" base color spaces (CIEBased or smaller). This arrangement avoids
1133ff48bf5SDavid du Colombier * breaking the color space heirarchy. Providing a more correct arrangement
1143ff48bf5SDavid du Colombier * requires a major change in the color space mechanism.
1153ff48bf5SDavid du Colombier *
1163ff48bf5SDavid du Colombier * Several of the methods used by ICCBased color space apply as well to
1173ff48bf5SDavid du Colombier * DeviceN color spaces, in that they are generic to color spaces having
1183ff48bf5SDavid du Colombier * a variable number of components. We have elected not to attempt to
1193ff48bf5SDavid du Colombier * extract and combine these operations, because this would save only a
1203ff48bf5SDavid du Colombier * small amount of code, and much more could be saved by intorducing certain
1213ff48bf5SDavid du Colombier * common elements (ranges, number of components, etc.) into the color space
1223ff48bf5SDavid du Colombier * root class.
1233ff48bf5SDavid du Colombier */
1243ff48bf5SDavid du Colombier private cs_proc_num_components(gx_num_components_CIEICC);
1253ff48bf5SDavid du Colombier private cs_proc_base_space(gx_alt_space_CIEICC);
1263ff48bf5SDavid du Colombier private cs_proc_init_color(gx_init_CIEICC);
1273ff48bf5SDavid du Colombier private cs_proc_restrict_color(gx_restrict_CIEICC);
1283ff48bf5SDavid du Colombier private cs_proc_concrete_space(gx_concrete_space_CIEICC);
1293ff48bf5SDavid du Colombier private cs_proc_concretize_color(gx_concretize_CIEICC);
1303ff48bf5SDavid du Colombier private cs_proc_adjust_cspace_count(gx_adjust_cspace_CIEICC);
131*593dc095SDavid du Colombier private cs_proc_serialize(gx_serialize_CIEICC);
1323ff48bf5SDavid du Colombier
1333ff48bf5SDavid du Colombier private const gs_color_space_type gs_color_space_type_CIEICC = {
1343ff48bf5SDavid du Colombier gs_color_space_index_CIEICC, /* index */
1353ff48bf5SDavid du Colombier true, /* can_be_base_space */
1363ff48bf5SDavid du Colombier true, /* can_be_alt_space */
1373ff48bf5SDavid du Colombier &st_color_space_CIEICC, /* stype - structure descriptor */
1383ff48bf5SDavid du Colombier gx_num_components_CIEICC, /* num_components */
1393ff48bf5SDavid du Colombier gx_alt_space_CIEICC, /* base_space */
1403ff48bf5SDavid du Colombier gx_init_CIEICC, /* init_color */
1413ff48bf5SDavid du Colombier gx_restrict_CIEICC, /* restrict_color */
1423ff48bf5SDavid du Colombier gx_concrete_space_CIEICC, /* concrete_space */
1433ff48bf5SDavid du Colombier gx_concretize_CIEICC, /* concreteize_color */
1443ff48bf5SDavid du Colombier NULL, /* remap_concrete_color */
1453ff48bf5SDavid du Colombier gx_default_remap_color, /* remap_color */
1463ff48bf5SDavid du Colombier gx_install_CIE, /* install_cpsace */
147*593dc095SDavid du Colombier gx_spot_colors_set_overprint, /* set_overprint */
1483ff48bf5SDavid du Colombier gx_adjust_cspace_CIEICC, /* adjust_cspace_count */
149*593dc095SDavid du Colombier gx_no_adjust_color_count, /* adjust_color_count */
150*593dc095SDavid du Colombier gx_serialize_CIEICC, /* serialize */
151*593dc095SDavid du Colombier gx_cspace_is_linear_default
1523ff48bf5SDavid du Colombier };
1533ff48bf5SDavid du Colombier
1543ff48bf5SDavid du Colombier
1553ff48bf5SDavid du Colombier /*
1563ff48bf5SDavid du Colombier * Return the number of components used by a ICCBased color space - 1, 3, or 4
1573ff48bf5SDavid du Colombier */
1583ff48bf5SDavid du Colombier private int
gx_num_components_CIEICC(const gs_color_space * pcs)1593ff48bf5SDavid du Colombier gx_num_components_CIEICC(const gs_color_space * pcs)
1603ff48bf5SDavid du Colombier {
1613ff48bf5SDavid du Colombier return pcs->params.icc.picc_info->num_components;
1623ff48bf5SDavid du Colombier }
1633ff48bf5SDavid du Colombier
1643ff48bf5SDavid du Colombier /*
1653ff48bf5SDavid du Colombier * Return the alternative space for an ICCBasee color space, but only if
1663ff48bf5SDavid du Colombier * that space is being used.
1673ff48bf5SDavid du Colombier */
1683ff48bf5SDavid du Colombier private const gs_color_space *
gx_alt_space_CIEICC(const gs_color_space * pcs)1693ff48bf5SDavid du Colombier gx_alt_space_CIEICC(const gs_color_space * pcs)
1703ff48bf5SDavid du Colombier {
1713ff48bf5SDavid du Colombier return (pcs->params.icc.picc_info->picc == NULL)
1723ff48bf5SDavid du Colombier ? (const gs_color_space *)&pcs->params.icc.alt_space
1733ff48bf5SDavid du Colombier : NULL;
1743ff48bf5SDavid du Colombier }
1753ff48bf5SDavid du Colombier
1763ff48bf5SDavid du Colombier /*
1773ff48bf5SDavid du Colombier * Set the initial client color for an ICCBased color space. The convention
1783ff48bf5SDavid du Colombier * suggested by the ICC specification is to set all components to 0.
1793ff48bf5SDavid du Colombier */
1803ff48bf5SDavid du Colombier private void
gx_init_CIEICC(gs_client_color * pcc,const gs_color_space * pcs)1813ff48bf5SDavid du Colombier gx_init_CIEICC(gs_client_color * pcc, const gs_color_space * pcs)
1823ff48bf5SDavid du Colombier {
1833ff48bf5SDavid du Colombier int i, ncomps = pcs->params.icc.picc_info->num_components;
1843ff48bf5SDavid du Colombier
1853ff48bf5SDavid du Colombier for (i = 0; i < ncomps; ++i)
1863ff48bf5SDavid du Colombier pcc->paint.values[i] = 0.0;
1873ff48bf5SDavid du Colombier
1883ff48bf5SDavid du Colombier /* make sure that [ 0, ... 0] is in range */
1893ff48bf5SDavid du Colombier gx_restrict_CIEICC(pcc, pcs);
1903ff48bf5SDavid du Colombier }
1913ff48bf5SDavid du Colombier
1923ff48bf5SDavid du Colombier /*
1933ff48bf5SDavid du Colombier * Restrict an color to the range specified for an ICCBased color space.
1943ff48bf5SDavid du Colombier */
1953ff48bf5SDavid du Colombier private void
gx_restrict_CIEICC(gs_client_color * pcc,const gs_color_space * pcs)1963ff48bf5SDavid du Colombier gx_restrict_CIEICC(gs_client_color * pcc, const gs_color_space * pcs)
1973ff48bf5SDavid du Colombier {
1983ff48bf5SDavid du Colombier int i, ncomps = pcs->params.icc.picc_info->num_components;
1993ff48bf5SDavid du Colombier const gs_range * ranges = pcs->params.icc.picc_info->Range.ranges;
2003ff48bf5SDavid du Colombier
2013ff48bf5SDavid du Colombier for (i = 0; i < ncomps; ++i) {
2023ff48bf5SDavid du Colombier floatp v = pcc->paint.values[i];
2033ff48bf5SDavid du Colombier floatp rmin = ranges[i].rmin, rmax = ranges[i].rmax;
2043ff48bf5SDavid du Colombier
2053ff48bf5SDavid du Colombier if (v < rmin)
2063ff48bf5SDavid du Colombier pcc->paint.values[i] = rmin;
2073ff48bf5SDavid du Colombier else if (v > rmax)
2083ff48bf5SDavid du Colombier pcc->paint.values[i] = rmax;
2093ff48bf5SDavid du Colombier }
2103ff48bf5SDavid du Colombier }
2113ff48bf5SDavid du Colombier
2123ff48bf5SDavid du Colombier /*
2133ff48bf5SDavid du Colombier * Return the conrecte space to which this color space will map. If the
2143ff48bf5SDavid du Colombier * ICCBased color space is being used in native mode, the concrete space
2153ff48bf5SDavid du Colombier * will be dependent on the current color rendering dictionary, as it is
2163ff48bf5SDavid du Colombier * for all CIE bases. If the alternate color space is being used, then
2173ff48bf5SDavid du Colombier * this question is passed on the the appropriate method of that space.
2183ff48bf5SDavid du Colombier */
2193ff48bf5SDavid du Colombier private const gs_color_space *
gx_concrete_space_CIEICC(const gs_color_space * pcs,const gs_imager_state * pis)2203ff48bf5SDavid du Colombier gx_concrete_space_CIEICC(const gs_color_space * pcs, const gs_imager_state * pis)
2213ff48bf5SDavid du Colombier {
2223ff48bf5SDavid du Colombier if (pcs->params.icc.picc_info->picc == NULL) {
2233ff48bf5SDavid du Colombier const gs_color_space * pacs = (const gs_color_space *)
2243ff48bf5SDavid du Colombier &pcs->params.icc.alt_space;
2253ff48bf5SDavid du Colombier
2263ff48bf5SDavid du Colombier return cs_concrete_space(pacs, pis);
2273ff48bf5SDavid du Colombier } else
2283ff48bf5SDavid du Colombier return gx_concrete_space_CIE(NULL, pis);
2293ff48bf5SDavid du Colombier }
2303ff48bf5SDavid du Colombier
2313ff48bf5SDavid du Colombier /*
2323ff48bf5SDavid du Colombier * Convert an ICCBased color space to a concrete color space.
2333ff48bf5SDavid du Colombier */
2343ff48bf5SDavid du Colombier private int
gx_concretize_CIEICC(const gs_client_color * pcc,const gs_color_space * pcs,frac * pconc,const gs_imager_state * pis)2353ff48bf5SDavid du Colombier gx_concretize_CIEICC(
2363ff48bf5SDavid du Colombier const gs_client_color * pcc,
2373ff48bf5SDavid du Colombier const gs_color_space * pcs,
2383ff48bf5SDavid du Colombier frac * pconc,
2393ff48bf5SDavid du Colombier const gs_imager_state * pis )
2403ff48bf5SDavid du Colombier {
2413ff48bf5SDavid du Colombier const gs_icc_params * picc_params = &pcs->params.icc;
2423ff48bf5SDavid du Colombier const gs_cie_icc * picc_info = picc_params->picc_info;
2433ff48bf5SDavid du Colombier stream * instrp = picc_info->instrp;
2443ff48bf5SDavid du Colombier icc * picc = picc_info->picc;
2453ff48bf5SDavid du Colombier double inv[4], outv[3];
2463ff48bf5SDavid du Colombier cie_cached_vector3 vlmn;
2473ff48bf5SDavid du Colombier gs_client_color lcc = *pcc;
2483ff48bf5SDavid du Colombier int i, ncomps = picc_info->num_components;
2493ff48bf5SDavid du Colombier
2503ff48bf5SDavid du Colombier /* use the altenate space concretize if appropriate */
2513ff48bf5SDavid du Colombier if (picc == NULL)
2523ff48bf5SDavid du Colombier return picc_params->alt_space.type->concretize_color(
2533ff48bf5SDavid du Colombier pcc,
2543ff48bf5SDavid du Colombier (const gs_color_space *)&picc_params->alt_space,
2553ff48bf5SDavid du Colombier pconc,
2563ff48bf5SDavid du Colombier pis );
2573ff48bf5SDavid du Colombier
2583ff48bf5SDavid du Colombier /* set up joint cache as required */
2593ff48bf5SDavid du Colombier CIE_CHECK_RENDERING(pcs, pconc, pis, return 0);
2603ff48bf5SDavid du Colombier
2613ff48bf5SDavid du Colombier /* verify and update the stream pointer */
2623ff48bf5SDavid du Colombier if (picc_info->file_id != (instrp->read_id | instrp->write_id))
2633ff48bf5SDavid du Colombier return_error(gs_error_ioerror);
2643ff48bf5SDavid du Colombier ((icmFileGs *)picc->fp)->strp = instrp;
2653ff48bf5SDavid du Colombier
2663ff48bf5SDavid du Colombier /* translate the input components */
2673ff48bf5SDavid du Colombier gx_restrict_CIEICC(&lcc, pcs);
2683ff48bf5SDavid du Colombier for (i = 0; i < ncomps; i++)
2693ff48bf5SDavid du Colombier inv[i] = lcc.paint.values[i];
2703ff48bf5SDavid du Colombier
271*593dc095SDavid du Colombier /* For input Lab color space massage the values into Lab range */
272*593dc095SDavid du Colombier
273*593dc095SDavid du Colombier if (picc_info->plu->e_inSpace == icSigLabData) {
274*593dc095SDavid du Colombier inv[0] *= 100;
275*593dc095SDavid du Colombier inv[1] = inv[1]*255 - 128;
276*593dc095SDavid du Colombier inv[2] = inv[2]*255 - 128;
277*593dc095SDavid du Colombier }
278*593dc095SDavid du Colombier
2793ff48bf5SDavid du Colombier /*
2803ff48bf5SDavid du Colombier * Perform the lookup operation. A return value of 1 indicates that
2813ff48bf5SDavid du Colombier * clipping occurred somewhere in the operation, but the result is
2823ff48bf5SDavid du Colombier * legitimate. Other non-zero return values indicate an error, which
2833ff48bf5SDavid du Colombier * should not occur in practice.
2843ff48bf5SDavid du Colombier */
2853ff48bf5SDavid du Colombier if (picc_info->plu->lookup(picc_info->plu, outv, inv) > 1)
2863ff48bf5SDavid du Colombier return_error(gs_error_unregistered);
2873ff48bf5SDavid du Colombier
2883ff48bf5SDavid du Colombier /* if the output is in the CIE L*a*b* space, convert to XYZ */
2893ff48bf5SDavid du Colombier if (picc_info->pcs_is_cielab) {
2903ff48bf5SDavid du Colombier floatp f[3];
2913ff48bf5SDavid du Colombier const gs_vector3 * pwhtpt = &picc_info->common.points.WhitePoint;
2923ff48bf5SDavid du Colombier
2933ff48bf5SDavid du Colombier
2943ff48bf5SDavid du Colombier f[1] = (outv[0] + 16.0) / 116.0;
2953ff48bf5SDavid du Colombier f[0] = f[1] + outv[1] / 500.0;
2963ff48bf5SDavid du Colombier f[2] = f[1] - outv[2] / 200;
2973ff48bf5SDavid du Colombier
2983ff48bf5SDavid du Colombier for (i = 0; i < 3; i++) {
2993ff48bf5SDavid du Colombier if (f[i] >= 6.0 / 29.0)
3003ff48bf5SDavid du Colombier outv[i] = f[i] * f[i] * f[i];
3013ff48bf5SDavid du Colombier else
3023ff48bf5SDavid du Colombier outv[i] = 108.0 * (f[i] - 4.0 / 29.0) / 841.0;
3033ff48bf5SDavid du Colombier }
3043ff48bf5SDavid du Colombier
3053ff48bf5SDavid du Colombier /*
3063ff48bf5SDavid du Colombier * The connection space white-point is known to be D50, but we
3073ff48bf5SDavid du Colombier * use the more general form in case of future revisions.
3083ff48bf5SDavid du Colombier */
3093ff48bf5SDavid du Colombier outv[0] *= pwhtpt->u;
3103ff48bf5SDavid du Colombier outv[1] *= pwhtpt->v;
3113ff48bf5SDavid du Colombier outv[2] *= pwhtpt->w;
3123ff48bf5SDavid du Colombier }
3133ff48bf5SDavid du Colombier
3143ff48bf5SDavid du Colombier /* translate the output */
3153ff48bf5SDavid du Colombier vlmn.u = float2cie_cached(outv[0]);
3163ff48bf5SDavid du Colombier vlmn.v = float2cie_cached(outv[1]);
3173ff48bf5SDavid du Colombier vlmn.w = float2cie_cached(outv[2]);
3183ff48bf5SDavid du Colombier
3193ff48bf5SDavid du Colombier gx_cie_remap_finish(vlmn, pconc, pis, pcs);
3203ff48bf5SDavid du Colombier return 0;
3213ff48bf5SDavid du Colombier }
3223ff48bf5SDavid du Colombier
3233ff48bf5SDavid du Colombier /*
3243ff48bf5SDavid du Colombier * Handle a reference or de-reference of the prameter structure of an
3253ff48bf5SDavid du Colombier * ICCBased color space. For the purposes of this routine, the color space
3263ff48bf5SDavid du Colombier * is considered a reference rather than an object, and is not itself
3273ff48bf5SDavid du Colombier * reference counted (an unintuitive but otherwise legitimate state of
3283ff48bf5SDavid du Colombier * affairs).
3293ff48bf5SDavid du Colombier *
3303ff48bf5SDavid du Colombier * Because color spaces store alternative/base color space inline, these
3313ff48bf5SDavid du Colombier * need to have their reference count adjusted explicitly.
3323ff48bf5SDavid du Colombier */
3333ff48bf5SDavid du Colombier private void
gx_adjust_cspace_CIEICC(const gs_color_space * pcs,int delta)3343ff48bf5SDavid du Colombier gx_adjust_cspace_CIEICC(const gs_color_space * pcs, int delta)
3353ff48bf5SDavid du Colombier {
3363ff48bf5SDavid du Colombier const gs_icc_params * picc_params = &pcs->params.icc;
3373ff48bf5SDavid du Colombier
3383ff48bf5SDavid du Colombier rc_adjust_const(picc_params->picc_info, delta, "gx_adjust_cspace_CIEICC");
3393ff48bf5SDavid du Colombier picc_params->alt_space.type->adjust_cspace_count(
3403ff48bf5SDavid du Colombier (const gs_color_space *)&picc_params->alt_space, delta );
3413ff48bf5SDavid du Colombier }
3423ff48bf5SDavid du Colombier
343*593dc095SDavid du Colombier /*
344*593dc095SDavid du Colombier * Increment color space reference counts.
345*593dc095SDavid du Colombier */
346*593dc095SDavid du Colombier void
gx_increment_cspace_count(const gs_color_space * pcs)347*593dc095SDavid du Colombier gx_increment_cspace_count(const gs_color_space * pcs)
348*593dc095SDavid du Colombier {
349*593dc095SDavid du Colombier pcs->type->adjust_cspace_count(pcs, 1);
350*593dc095SDavid du Colombier }
351*593dc095SDavid du Colombier
3523ff48bf5SDavid du Colombier private int
icmFileGs_seek(icmFile * pp,long int offset)3533ff48bf5SDavid du Colombier icmFileGs_seek(icmFile *pp, long int offset)
3543ff48bf5SDavid du Colombier {
3553ff48bf5SDavid du Colombier icmFileGs *p = (icmFileGs *)pp;
3563ff48bf5SDavid du Colombier
3573ff48bf5SDavid du Colombier return spseek(p->strp, offset);
3583ff48bf5SDavid du Colombier }
3593ff48bf5SDavid du Colombier
3603ff48bf5SDavid du Colombier private size_t
icmFileGs_read(icmFile * pp,void * buffer,size_t size,size_t count)3613ff48bf5SDavid du Colombier icmFileGs_read(icmFile *pp, void *buffer, size_t size, size_t count)
3623ff48bf5SDavid du Colombier {
3633ff48bf5SDavid du Colombier icmFileGs *p = (icmFileGs *)pp;
3643ff48bf5SDavid du Colombier uint tot;
3653ff48bf5SDavid du Colombier int status = sgets(p->strp, buffer, size * count, &tot);
3663ff48bf5SDavid du Colombier
3673ff48bf5SDavid du Colombier return (status < 0) ? status : tot;
3683ff48bf5SDavid du Colombier }
3693ff48bf5SDavid du Colombier
3703ff48bf5SDavid du Colombier private size_t
icmFileGs_write(icmFile * pp,void * buffer,size_t size,size_t count)3713ff48bf5SDavid du Colombier icmFileGs_write(icmFile *pp, void *buffer, size_t size, size_t count)
3723ff48bf5SDavid du Colombier {
3733ff48bf5SDavid du Colombier icmFileGs *p = (icmFileGs *)pp;
3743ff48bf5SDavid du Colombier uint tot;
3753ff48bf5SDavid du Colombier int status = sputs(p->strp, buffer, size * count, &tot);
3763ff48bf5SDavid du Colombier
3773ff48bf5SDavid du Colombier return (status < 0) ? status : tot;
3783ff48bf5SDavid du Colombier }
3793ff48bf5SDavid du Colombier
3803ff48bf5SDavid du Colombier private int
icmFileGs_flush(icmFile * pp)3813ff48bf5SDavid du Colombier icmFileGs_flush(icmFile *pp)
3823ff48bf5SDavid du Colombier {
3833ff48bf5SDavid du Colombier icmFileGs *p = (icmFileGs *)pp;
3843ff48bf5SDavid du Colombier
3853ff48bf5SDavid du Colombier return s_std_write_flush(p->strp);
3863ff48bf5SDavid du Colombier }
3873ff48bf5SDavid du Colombier
3883ff48bf5SDavid du Colombier private int
icmFileGs_delete(icmFile * pp)3893ff48bf5SDavid du Colombier icmFileGs_delete(icmFile *pp)
3903ff48bf5SDavid du Colombier {
3913ff48bf5SDavid du Colombier free(pp);
3923ff48bf5SDavid du Colombier return 0;
3933ff48bf5SDavid du Colombier }
3943ff48bf5SDavid du Colombier
3953ff48bf5SDavid du Colombier /**
3963ff48bf5SDavid du Colombier * gx_wrap_icc_stream: Wrap a Ghostscript stream as an icclib file.
3973ff48bf5SDavid du Colombier * @strp: The Ghostscript stream.
3983ff48bf5SDavid du Colombier *
3993ff48bf5SDavid du Colombier * Creates an icmFile object that wraps @stream.
4003ff48bf5SDavid du Colombier *
4013ff48bf5SDavid du Colombier * Note: the memory for this object is allocated using malloc, and the
4023ff48bf5SDavid du Colombier * relocation of the stream pointer is done lazily, before an icclu
4033ff48bf5SDavid du Colombier * operation. It would probably be cleaner to allocate the icmFile in
4043ff48bf5SDavid du Colombier * garbage collected memory, and have the relocation happen there, but
4053ff48bf5SDavid du Colombier * I wanted to minimally modify Jan's working code.
4063ff48bf5SDavid du Colombier *
4073ff48bf5SDavid du Colombier * Return value: the stream wrapped as an icmFile object, or NULL on
4083ff48bf5SDavid du Colombier * error.
4093ff48bf5SDavid du Colombier **/
4103ff48bf5SDavid du Colombier private icmFile *
gx_wrap_icc_stream(stream * strp)4113ff48bf5SDavid du Colombier gx_wrap_icc_stream(stream *strp)
4123ff48bf5SDavid du Colombier {
4133ff48bf5SDavid du Colombier icmFileGs *p;
4143ff48bf5SDavid du Colombier
4153ff48bf5SDavid du Colombier if ((p = (icmFileGs *) calloc(1,sizeof(icmFileGs))) == NULL)
4163ff48bf5SDavid du Colombier return NULL;
4173ff48bf5SDavid du Colombier p->seek = icmFileGs_seek;
4183ff48bf5SDavid du Colombier p->read = icmFileGs_read;
4193ff48bf5SDavid du Colombier p->write = icmFileGs_write;
4203ff48bf5SDavid du Colombier p->flush = icmFileGs_flush;
4213ff48bf5SDavid du Colombier p->del = icmFileGs_delete;
4223ff48bf5SDavid du Colombier
4233ff48bf5SDavid du Colombier p->strp = strp;
4243ff48bf5SDavid du Colombier
4253ff48bf5SDavid du Colombier return (icmFile *)p;
4263ff48bf5SDavid du Colombier }
4273ff48bf5SDavid du Colombier
4283ff48bf5SDavid du Colombier int
gx_load_icc_profile(gs_cie_icc * picc_info)4293ff48bf5SDavid du Colombier gx_load_icc_profile(gs_cie_icc *picc_info)
4303ff48bf5SDavid du Colombier {
4313ff48bf5SDavid du Colombier stream * instrp = picc_info->instrp;
4323ff48bf5SDavid du Colombier icc * picc;
4333ff48bf5SDavid du Colombier icmLuBase * plu = NULL;
4343ff48bf5SDavid du Colombier icmFile *pfile = NULL;
4353ff48bf5SDavid du Colombier
4363ff48bf5SDavid du Colombier /* verify that the file is legitimate */
4373ff48bf5SDavid du Colombier if (picc_info->file_id != (instrp->read_id | instrp->write_id))
4383ff48bf5SDavid du Colombier return_error(gs_error_ioerror);
4393ff48bf5SDavid du Colombier /*
4403ff48bf5SDavid du Colombier * Load the top-level ICC profile.
4413ff48bf5SDavid du Colombier *
4423ff48bf5SDavid du Colombier * If an ICC profile fails to load, generate an error.
4433ff48bf5SDavid du Colombier *
4443ff48bf5SDavid du Colombier * Testing demonstrates, however, Acrobat Reader silently
4453ff48bf5SDavid du Colombier * ignores the error and uses the alternate color space.
4463ff48bf5SDavid du Colombier * This behaviour is implemented by catching the error using
4473ff48bf5SDavid du Colombier * a stopped context from within the interpreter (gs_icc.ps).
4483ff48bf5SDavid du Colombier *
4493ff48bf5SDavid du Colombier * Failure to allocate the top-level profile object is considered
4503ff48bf5SDavid du Colombier * a limitcheck rather than a VMerror, as profile data structures
4513ff48bf5SDavid du Colombier * are stored in "foreign" memory.
4523ff48bf5SDavid du Colombier */
4533ff48bf5SDavid du Colombier if ((picc = new_icc()) == NULL)
4543ff48bf5SDavid du Colombier return_error(gs_error_limitcheck);
4553ff48bf5SDavid du Colombier {
4563ff48bf5SDavid du Colombier icProfileClassSignature profile_class;
4573ff48bf5SDavid du Colombier icColorSpaceSignature cspace_type;
4583ff48bf5SDavid du Colombier gs_vector3 * ppt;
4593ff48bf5SDavid du Colombier
4603ff48bf5SDavid du Colombier pfile = gx_wrap_icc_stream (instrp);
4613ff48bf5SDavid du Colombier
4623ff48bf5SDavid du Colombier if ((picc->read(picc, pfile, 0)) != 0)
4633ff48bf5SDavid du Colombier goto return_rangecheck;
4643ff48bf5SDavid du Colombier
4653ff48bf5SDavid du Colombier /* verify the profile type */
4663ff48bf5SDavid du Colombier profile_class = picc->header->deviceClass;
4673ff48bf5SDavid du Colombier if ( profile_class != icSigInputClass &&
4683ff48bf5SDavid du Colombier profile_class != icSigDisplayClass &&
4693ff48bf5SDavid du Colombier profile_class != icSigOutputClass &&
4703ff48bf5SDavid du Colombier profile_class != icSigColorSpaceClass )
4713ff48bf5SDavid du Colombier goto return_rangecheck;
4723ff48bf5SDavid du Colombier
4733ff48bf5SDavid du Colombier /* verify the profile connection space */
4743ff48bf5SDavid du Colombier cspace_type = picc->header->pcs;
4753ff48bf5SDavid du Colombier if (cspace_type == icSigLabData)
4763ff48bf5SDavid du Colombier picc_info->pcs_is_cielab = true;
4773ff48bf5SDavid du Colombier else if (cspace_type == icSigXYZData)
4783ff48bf5SDavid du Colombier picc_info->pcs_is_cielab = false;
4793ff48bf5SDavid du Colombier else
4803ff48bf5SDavid du Colombier goto return_rangecheck;
4813ff48bf5SDavid du Colombier
4823ff48bf5SDavid du Colombier /* verify the source color space */
4833ff48bf5SDavid du Colombier cspace_type = picc->header->colorSpace;
4843ff48bf5SDavid du Colombier if (cspace_type == icSigCmykData) {
4853ff48bf5SDavid du Colombier if (picc_info->num_components != 4)
4863ff48bf5SDavid du Colombier goto return_rangecheck;
4873ff48bf5SDavid du Colombier } else if ( cspace_type == icSigRgbData ||
4883ff48bf5SDavid du Colombier cspace_type == icSigLabData ) {
4893ff48bf5SDavid du Colombier if (picc_info->num_components != 3)
4903ff48bf5SDavid du Colombier goto return_rangecheck;
4913ff48bf5SDavid du Colombier } else if (cspace_type == icSigGrayData) {
4923ff48bf5SDavid du Colombier if (picc_info->num_components != 1)
4933ff48bf5SDavid du Colombier goto return_rangecheck;
4943ff48bf5SDavid du Colombier }
4953ff48bf5SDavid du Colombier
4963ff48bf5SDavid du Colombier /*
4973ff48bf5SDavid du Colombier * Fetch the lookup object.
4983ff48bf5SDavid du Colombier *
4993ff48bf5SDavid du Colombier * PostScript and PDF deal with rendering intent as strictly a
5003ff48bf5SDavid du Colombier * rendering dictionary facility. ICC profiles allow a rendering
5013ff48bf5SDavid du Colombier * intent to be specified for both the input (device ==> pcs) and
5023ff48bf5SDavid du Colombier * output (pcs ==> device) operations. Hence, when using ICCBased
5033ff48bf5SDavid du Colombier * color spaces with PDF, no clue is provided as to which source
5043ff48bf5SDavid du Colombier * mapping to select.
5053ff48bf5SDavid du Colombier *
5063ff48bf5SDavid du Colombier * In the absence of other information, there are two possible
5073ff48bf5SDavid du Colombier * selections. If our understanding is correct, when relative
5083ff48bf5SDavid du Colombier * colorimetry is specified, the icclib code will map source
5093ff48bf5SDavid du Colombier * color values to XYZ or L*a*b* values such that the relationship
5103ff48bf5SDavid du Colombier * of the source color, relative to the source white and black
5113ff48bf5SDavid du Colombier * points, will be the same as the output colors and the
5123ff48bf5SDavid du Colombier * profile connection space illuminant (currently always D50)
5133ff48bf5SDavid du Colombier * and pure black ([0, 0, 0]). In this case, the white and black
5143ff48bf5SDavid du Colombier * points that should be listed in the color space are the
5153ff48bf5SDavid du Colombier * profile connection space illuminant (D50) and pure black.
5163ff48bf5SDavid du Colombier *
5173ff48bf5SDavid du Colombier * If absolute colorimetry is employed, the XYZ or L*a*b* values
5183ff48bf5SDavid du Colombier * generated will be absolute in the chromatic sense (they are
5193ff48bf5SDavid du Colombier * not literally "absolute", as we still must have overall
5203ff48bf5SDavid du Colombier * intensity information inorder to determine weighted spectral
5213ff48bf5SDavid du Colombier * power levels). To achieve relative colorimetry for the output,
5223ff48bf5SDavid du Colombier * these colors must be evaluated relative to the source white
5233ff48bf5SDavid du Colombier * and black points. Hence, in this case, the appropriate white
5243ff48bf5SDavid du Colombier * and black points to list in the color space are the source
5253ff48bf5SDavid du Colombier * white and black points provided in the profile tag array.
5263ff48bf5SDavid du Colombier *
5273ff48bf5SDavid du Colombier * In this implementation, we will always request relative
5283ff48bf5SDavid du Colombier * colorimetry from the icclib, and so will use the profile
5293ff48bf5SDavid du Colombier * connection space illuminant and pure black as the white and
5303ff48bf5SDavid du Colombier * black points of the color space. This approach is somewhat
5313ff48bf5SDavid du Colombier * simpler, as it allows the color space white point to also
5323ff48bf5SDavid du Colombier * be used for L*a*b* to XYZ conversion (otherwise we would
5333ff48bf5SDavid du Colombier * need to store the profile connection space illuminant
5343ff48bf5SDavid du Colombier * separately for that purpose). The approach does reduce to
5353ff48bf5SDavid du Colombier * to some extent the range of mappings that can be achieved
5363ff48bf5SDavid du Colombier * via the color rendering dictionary, but for now we believe
5373ff48bf5SDavid du Colombier * this loss is not significant.
5383ff48bf5SDavid du Colombier *
5393ff48bf5SDavid du Colombier * For reasons that are not clear to us, the icclib code does
5403ff48bf5SDavid du Colombier * not support relative colorimetry for all color profiles. For
5413ff48bf5SDavid du Colombier * this reason, we specify icmDefaultIntent rather than
5423ff48bf5SDavid du Colombier * icRelativeColormetric.
5433ff48bf5SDavid du Colombier *
5443ff48bf5SDavid du Colombier * NB: We are not color experts; our understanding of this area
5453ff48bf5SDavid du Colombier * may well be incorrect.
5463ff48bf5SDavid du Colombier */
5473ff48bf5SDavid du Colombier plu = picc->get_luobj( picc,
5483ff48bf5SDavid du Colombier icmFwd,
5493ff48bf5SDavid du Colombier icmDefaultIntent,
5503ff48bf5SDavid du Colombier 0, /* PCS override */
5513ff48bf5SDavid du Colombier icmLuOrdNorm );
5523ff48bf5SDavid du Colombier if (plu == NULL)
5533ff48bf5SDavid du Colombier goto return_rangecheck;
5543ff48bf5SDavid du Colombier
5553ff48bf5SDavid du Colombier /*
5563ff48bf5SDavid du Colombier * Get the appropriate white and black points. See the note on
5573ff48bf5SDavid du Colombier * rendering intent above for a discussion of why we are using
5583ff48bf5SDavid du Colombier * the profile space illuminant and pure black. (Pure black need
5593ff48bf5SDavid du Colombier * not be set explicitly, as it is the default.)
5603ff48bf5SDavid du Colombier */
5613ff48bf5SDavid du Colombier ppt = &picc_info->common.points.WhitePoint;
5623ff48bf5SDavid du Colombier ppt->u = picc->header->illuminant.X;
5633ff48bf5SDavid du Colombier ppt->v = picc->header->illuminant.Y;
5643ff48bf5SDavid du Colombier ppt->w = picc->header->illuminant.Z;
5653ff48bf5SDavid du Colombier
5663ff48bf5SDavid du Colombier picc_info->picc = picc;
5673ff48bf5SDavid du Colombier picc_info->plu = plu;
5683ff48bf5SDavid du Colombier picc_info->pfile = pfile;
5693ff48bf5SDavid du Colombier }
5703ff48bf5SDavid du Colombier
5713ff48bf5SDavid du Colombier return 0;
5723ff48bf5SDavid du Colombier
5733ff48bf5SDavid du Colombier return_rangecheck:
5743ff48bf5SDavid du Colombier if (plu != NULL)
5753ff48bf5SDavid du Colombier plu->del(plu);
5763ff48bf5SDavid du Colombier if (picc != NULL)
5773ff48bf5SDavid du Colombier picc->del(picc);
5783ff48bf5SDavid du Colombier if (pfile != NULL)
5793ff48bf5SDavid du Colombier pfile->del(pfile);
5803ff48bf5SDavid du Colombier return_error(gs_error_rangecheck);
5813ff48bf5SDavid du Colombier }
5823ff48bf5SDavid du Colombier
5833ff48bf5SDavid du Colombier /*
5843ff48bf5SDavid du Colombier * Install an ICCBased color space.
5853ff48bf5SDavid du Colombier *
5863ff48bf5SDavid du Colombier * Note that an ICCBased color space must be installed before it is known if
5873ff48bf5SDavid du Colombier * the ICC profile or the alternate color space is to be used.
5883ff48bf5SDavid du Colombier */
5893ff48bf5SDavid du Colombier private int
gx_install_CIEICC(const gs_color_space * pcs,gs_state * pgs)5903ff48bf5SDavid du Colombier gx_install_CIEICC(const gs_color_space * pcs, gs_state * pgs)
5913ff48bf5SDavid du Colombier {
5923ff48bf5SDavid du Colombier const gs_icc_params * picc_params = (const gs_icc_params *)&pcs->params.icc;
5933ff48bf5SDavid du Colombier gs_cie_icc * picc_info = picc_params->picc_info;
5943ff48bf5SDavid du Colombier
5953ff48bf5SDavid du Colombier /* update the stub information used by the joint caches */
5963ff48bf5SDavid du Colombier gx_cie_load_common_cache(&picc_info->common, pgs);
5973ff48bf5SDavid du Colombier gx_cie_common_complete(&picc_info->common);
5983ff48bf5SDavid du Colombier return gs_cie_cs_complete(pgs, true);
5993ff48bf5SDavid du Colombier }
6003ff48bf5SDavid du Colombier
6013ff48bf5SDavid du Colombier
6023ff48bf5SDavid du Colombier /*
6033ff48bf5SDavid du Colombier * Constructor for ICCBased color space. As with the other color space
6043ff48bf5SDavid du Colombier * constructors, this provides only minimal initialization.
6053ff48bf5SDavid du Colombier */
6063ff48bf5SDavid du Colombier int
gs_cspace_build_CIEICC(gs_color_space ** ppcspace,void * client_data,gs_memory_t * pmem)6073ff48bf5SDavid du Colombier gs_cspace_build_CIEICC(
6083ff48bf5SDavid du Colombier gs_color_space ** ppcspace,
6093ff48bf5SDavid du Colombier void * client_data,
6103ff48bf5SDavid du Colombier gs_memory_t * pmem )
6113ff48bf5SDavid du Colombier {
6123ff48bf5SDavid du Colombier gs_cie_icc * picc_info;
6133ff48bf5SDavid du Colombier gs_color_space * pcs;
6143ff48bf5SDavid du Colombier
6153ff48bf5SDavid du Colombier /*
6163ff48bf5SDavid du Colombier * The gs_cie_icc_s structure is the only CIE-based color space structure
6173ff48bf5SDavid du Colombier * which accesses additional memory for which it is responsible. We make
6183ff48bf5SDavid du Colombier * use of the finalization procedure to handle this task, so we can use
6193ff48bf5SDavid du Colombier * the generic CIE space build routine (otherwise we would need a
6203ff48bf5SDavid du Colombier * separate build routine that provided its own reference count freeing
6213ff48bf5SDavid du Colombier * procedure).
6223ff48bf5SDavid du Colombier */
6233ff48bf5SDavid du Colombier picc_info = gx_build_cie_space( ppcspace,
6243ff48bf5SDavid du Colombier &gs_color_space_type_CIEICC,
6253ff48bf5SDavid du Colombier &st_cie_icc,
6263ff48bf5SDavid du Colombier pmem );
6273ff48bf5SDavid du Colombier
6283ff48bf5SDavid du Colombier if (picc_info == NULL)
6293ff48bf5SDavid du Colombier return_error(gs_error_VMerror);
6303ff48bf5SDavid du Colombier
6313ff48bf5SDavid du Colombier gx_set_common_cie_defaults(&picc_info->common, client_data);
6323ff48bf5SDavid du Colombier /*
6333ff48bf5SDavid du Colombier * Now set the D50 WhitePoint. The above function does not set any
6343ff48bf5SDavid du Colombier * valid WhitepPoint since PostScript always requires this, but ICC
6353ff48bf5SDavid du Colombier * assumes a D50 WhitePoint as a default
6363ff48bf5SDavid du Colombier */
637*593dc095SDavid du Colombier picc_info->common.points.WhitePoint.u = (float)0.9642; /* Profile illuminant - D50 */
6383ff48bf5SDavid du Colombier picc_info->common.points.WhitePoint.v = 1.0000;
639*593dc095SDavid du Colombier picc_info->common.points.WhitePoint.w = (float)0.8249;
6403ff48bf5SDavid du Colombier picc_info->common.install_cspace = gx_install_CIEICC;
6413ff48bf5SDavid du Colombier picc_info->num_components = 0;
6423ff48bf5SDavid du Colombier picc_info->Range = Range4_default;
6433ff48bf5SDavid du Colombier picc_info->instrp = NULL;
6443ff48bf5SDavid du Colombier picc_info->pcs_is_cielab = false;
6453ff48bf5SDavid du Colombier picc_info->picc = NULL;
6463ff48bf5SDavid du Colombier picc_info->plu = NULL;
6473ff48bf5SDavid du Colombier picc_info->pfile = NULL;
6483ff48bf5SDavid du Colombier
6493ff48bf5SDavid du Colombier pcs = *ppcspace;
6503ff48bf5SDavid du Colombier pcs->params.icc.picc_info = picc_info;
6513ff48bf5SDavid du Colombier return 0;
6523ff48bf5SDavid du Colombier }
653*593dc095SDavid du Colombier
654*593dc095SDavid du Colombier /* ---------------- Serialization. -------------------------------- */
655*593dc095SDavid du Colombier
656*593dc095SDavid du Colombier private int
gx_serialize_CIEICC(const gs_color_space * pcs,stream * s)657*593dc095SDavid du Colombier gx_serialize_CIEICC(const gs_color_space * pcs, stream * s)
658*593dc095SDavid du Colombier {
659*593dc095SDavid du Colombier const gs_icc_params * p = &pcs->params.icc;
660*593dc095SDavid du Colombier gs_cie_icc *picc = p->picc_info;
661*593dc095SDavid du Colombier uint n;
662*593dc095SDavid du Colombier int code = gx_serialize_cspace_type(pcs, s);
663*593dc095SDavid du Colombier long avail, pos, count;
664*593dc095SDavid du Colombier byte buf[100];
665*593dc095SDavid du Colombier
666*593dc095SDavid du Colombier if (code < 0)
667*593dc095SDavid du Colombier return code;
668*593dc095SDavid du Colombier code = gx_serialize_cie_common_elements(pcs, s);
669*593dc095SDavid du Colombier if (code < 0)
670*593dc095SDavid du Colombier return code;
671*593dc095SDavid du Colombier code = sputs(s, (byte *)&picc->num_components, sizeof(picc->num_components), &n);
672*593dc095SDavid du Colombier if (code < 0)
673*593dc095SDavid du Colombier return code;
674*593dc095SDavid du Colombier code = sputs(s, (byte *)&picc->Range, sizeof(picc->Range), &n);
675*593dc095SDavid du Colombier if (code < 0)
676*593dc095SDavid du Colombier return code;
677*593dc095SDavid du Colombier if (sseek(picc->instrp, 0) < 0)
678*593dc095SDavid du Colombier return_error(gs_error_unregistered); /* Unimplemented. */
679*593dc095SDavid du Colombier if (savailable(picc->instrp, &avail) != 0)
680*593dc095SDavid du Colombier return_error(gs_error_unregistered); /* Unimplemented. */
681*593dc095SDavid du Colombier code = sputs(s, (byte *)&avail, sizeof(avail), &n);
682*593dc095SDavid du Colombier if (code < 0)
683*593dc095SDavid du Colombier return code;
684*593dc095SDavid du Colombier for (pos = 0; pos < avail; pos += count) {
685*593dc095SDavid du Colombier count = min(sizeof(buf), avail - pos);
686*593dc095SDavid du Colombier code = sgets(picc->instrp, buf, count, &n);
687*593dc095SDavid du Colombier if (code < 0)
688*593dc095SDavid du Colombier return code;
689*593dc095SDavid du Colombier code = sputs(s, buf, count, &n);
690*593dc095SDavid du Colombier if (code < 0)
691*593dc095SDavid du Colombier return code;
692*593dc095SDavid du Colombier }
693*593dc095SDavid du Colombier return sputs(s, (byte *)&picc->pcs_is_cielab, sizeof(picc->pcs_is_cielab), &n);
694*593dc095SDavid du Colombier }
695