Coverage Report

Created: 2026-04-01 07:17

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ghostpdl/base/gscsepr.c
Line
Count
Source
1
/* Copyright (C) 2001-2026 Artifex Software, Inc.
2
   All Rights Reserved.
3
4
   This software is provided AS-IS with no warranty, either express or
5
   implied.
6
7
   This software is distributed under license and may not be copied,
8
   modified or distributed except as expressly authorized under the terms
9
   of the license contained in the file LICENSE in this distribution.
10
11
   Refer to licensing information at http://www.artifex.com or contact
12
   Artifex Software, Inc.,  39 Mesa Street, Suite 108A, San Francisco,
13
   CA 94129, USA, for further information.
14
*/
15
16
17
/* Separation color space and operation definition */
18
#include "memory_.h"
19
#include "gx.h"
20
#include "gserrors.h"
21
#include "gsfunc.h"
22
#include "gsrefct.h"
23
#include "gsmatrix.h"   /* for gscolor2.h */
24
#include "gscsepr.h"
25
#include "gxcspace.h"
26
#include "gxfixed.h"    /* for gxcolor2.h */
27
#include "gxcolor2.h"   /* for gs_indexed_map */
28
#include "gzstate.h"    /* for pgs->overprint */
29
#include "gscdevn.h"    /* for alloc_device_n_map */
30
#include "gxcdevn.h"    /* for gs_device_n_map_s */
31
#include "gxcmap.h"
32
#include "gxdevcli.h"
33
#include "gsovrc.h"
34
#include "stream.h"
35
#include "gsicc_cache.h"
36
#include "gxdevice.h"
37
#include "gxcie.h"
38
#include "gxdevsop.h"
39
40
/* ---------------- Color space ---------------- */
41
42
gs_private_st_composite(st_color_space_Separation, gs_color_space,
43
                        "gs_color_space_Separation",
44
                        cs_Separation_enum_ptrs, cs_Separation_reloc_ptrs);
45
46
/* Define the Separation color space type. */
47
static cs_proc_init_color(gx_init_Separation);
48
static cs_proc_concrete_space(gx_concrete_space_Separation);
49
static cs_proc_concretize_color(gx_concretize_Separation);
50
static cs_proc_remap_concrete_color(gx_remap_concrete_Separation);
51
static cs_proc_remap_color(gx_remap_Separation);
52
static cs_proc_install_cspace(gx_install_Separation);
53
static cs_proc_set_overprint(gx_set_overprint_Separation);
54
static cs_proc_final(gx_final_Separation);
55
static cs_proc_serialize(gx_serialize_Separation);
56
const gs_color_space_type gs_color_space_type_Separation = {
57
    gs_color_space_index_Separation, true, false,
58
    &st_color_space_Separation, gx_num_components_1,
59
    gx_init_Separation, gx_restrict01_paint_1,
60
    gx_concrete_space_Separation,
61
    gx_concretize_Separation, gx_remap_concrete_Separation,
62
    gx_remap_Separation, gx_install_Separation,
63
    gx_set_overprint_Separation,
64
    gx_final_Separation, gx_no_adjust_color_count,
65
    gx_serialize_Separation,
66
    gx_cspace_is_linear_default, gx_polarity_subtractive
67
};
68
69
/* GC procedures */
70
71
static
72
1.10k
ENUM_PTRS_BEGIN(cs_Separation_enum_ptrs) return 0;
73
1.10k
    ENUM_PTR(0, gs_color_space, params.separation.map);
74
1.10k
ENUM_PTRS_END
75
551
static RELOC_PTRS_BEGIN(cs_Separation_reloc_ptrs)
76
551
{
77
551
    RELOC_PTR(gs_color_space, params.separation.map);
78
551
}
79
551
RELOC_PTRS_END
80
81
/* Get the concrete space for a Separation space. */
82
static const gs_color_space *
83
gx_concrete_space_Separation(const gs_color_space * pcs,
84
                             const gs_gstate * pgs)
85
102k
{
86
102k
    bool is_lab = false;
87
#ifdef DEBUG
88
    /*
89
     * Verify that the color space and gs_gstate info match.
90
     */
91
    if (pcs->id != pgs->color_component_map.cspace_id)
92
        dmprintf(pgs->memory, "gx_concretze_space_Separation: color space id mismatch");
93
#endif
94
95
    /*
96
     * Check if we are using the alternate color space.
97
     */
98
102k
    if (pgs->color_component_map.use_alt_cspace) {
99
        /* Need to handle PS CIE space */
100
95.4k
        if (gs_color_space_is_PSCIE(pcs->base_space)) {
101
0
            if (pcs->base_space->icc_equivalent == NULL) {
102
0
                (void)gs_colorspace_set_icc_equivalent(pcs->base_space,
103
0
                                                    &is_lab, pgs->memory);
104
0
            }
105
0
            return (pcs->base_space->icc_equivalent);
106
0
        }
107
95.4k
        return cs_concrete_space(pcs->base_space, pgs);
108
95.4k
    }
109
    /*
110
     * Separation color spaces are concrete (when not using alt. color space).
111
     */
112
6.89k
    return pcs;
113
102k
}
114
115
static int
116
check_Separation_component_name(const gs_color_space * pcs, gs_gstate * pgs);
117
118
/* Check if the colorant is a process colorant */
119
static separation_colors
120
gx_check_process_names_Separation(gs_color_space * pcs, gs_gstate * pgs)
121
31.9k
{
122
31.9k
    byte *pname = (byte *)pcs->params.separation.sep_name;
123
31.9k
    uint name_size = strlen(pcs->params.separation.sep_name);
124
125
    /* Classify */
126
31.9k
    if (strncmp((char *)pname, "None", name_size) == 0 ||
127
31.9k
        strncmp((char *)pname, "All", name_size) == 0) {
128
1
        return SEP_ENUM;
129
31.9k
    } else {
130
31.9k
        if (strncmp((char *)pname, "Cyan", name_size) == 0 ||
131
31.9k
            strncmp((char *)pname, "Magenta", name_size) == 0 ||
132
31.9k
            strncmp((char *)pname, "Yellow", name_size) == 0 ||
133
31.9k
            strncmp((char *)pname, "Black", name_size) == 0) {
134
9.85k
            return SEP_PURE_CMYK;
135
22.0k
        } else if (strncmp((char *)pname, "Red", name_size) == 0 ||
136
22.0k
            strncmp((char *)pname, "Green", name_size) == 0 ||
137
22.0k
            strncmp((char *)pname, "Blue", name_size) == 0) {
138
0
            return SEP_PURE_RGB;
139
22.0k
        } else {
140
22.0k
            return SEP_MIX;
141
22.0k
        }
142
31.9k
    }
143
31.9k
}
144
145
/* Install a Separation color space. */
146
static int
147
gx_install_Separation(gs_color_space * pcs, gs_gstate * pgs)
148
31.9k
{
149
31.9k
    int code;
150
151
31.9k
    code = check_Separation_component_name(pcs, pgs);
152
31.9k
    if (code < 0)
153
0
       return code;
154
155
31.9k
    if (pgs->icc_manager->device_named != NULL) {
156
0
        pcs->params.separation.named_color_supported =
157
0
            gsicc_support_named_color(pcs, pgs);
158
0
    }
159
160
31.9k
    pcs->params.separation.color_type =
161
31.9k
        gx_check_process_names_Separation(pcs, pgs);
162
163
31.9k
    gs_currentcolorspace_inline(pgs)->params.separation.use_alt_cspace =
164
31.9k
        using_alt_color_space(pgs);
165
31.9k
    if (gs_currentcolorspace_inline(pgs)->params.separation.use_alt_cspace) {
166
28.1k
        code = (pcs->base_space->type->install_cspace)
167
28.1k
            (pcs->base_space, pgs);
168
28.1k
    } else {
169
        /*
170
         * Give the device an opportunity to capture equivalent colors for any
171
         * spot colors which might be present in the color space.
172
         */
173
3.73k
        if (dev_proc(pgs->device, update_spot_equivalent_colors))
174
3.73k
           code = dev_proc(pgs->device, update_spot_equivalent_colors)
175
3.73k
                                                        (pgs->device, pgs, pcs);
176
3.73k
    }
177
31.9k
    return code;
178
31.9k
}
179
180
/* Set the overprint information appropriate to a separation color space */
181
static int
182
gx_set_overprint_Separation(const gs_color_space * pcs, gs_gstate * pgs)
183
17.7k
{
184
17.7k
    gs_devicen_color_map *  pcmap = &pgs->color_component_map;
185
186
17.7k
    if (pcmap->use_alt_cspace)
187
9.41k
        return gx_set_no_overprint(pgs);
188
8.35k
    else {
189
8.35k
        gs_overprint_params_t params = { 0 };
190
191
8.35k
        params.retain_any_comps = (((pgs->overprint && pgs->is_fill_color) ||
192
73
                                   (pgs->stroke_overprint && !pgs->is_fill_color)) &&
193
8.28k
                                   (pcs->params.separation.sep_type != SEP_ALL));
194
8.35k
        params.is_fill_color = pgs->is_fill_color;
195
8.35k
        params.drawn_comps = 0;
196
8.35k
        params.op_state = OP_STATE_NONE;
197
8.35k
        if (params.retain_any_comps) {
198
8.28k
            if (pcs->params.separation.sep_type != SEP_NONE) {
199
8.28k
                if (pcs->params.separation.named_color_supported) {
200
                    /* This color will not actually be the device_n color any more.
201
                     * It will have been substituted with the named color replacement.
202
                     * Therefore the drawn comps will be different. */
203
                    /* FIXME: For now, we assume the replacement color uses
204
                     * C,M,Y and K. To do better, we'd either need to look at the
205
                     * device color (but this is problematic, as we may have scaled light
206
                     * components to 0), or we'd need to get this from the named color
207
                     * code itself. Possibly by another entry in color_component_map. */
208
0
                    params.drawn_comps = 15;
209
8.28k
                } else {
210
8.28k
                    int mcomp = pcmap->color_map[0];
211
212
8.28k
                    if (mcomp >= 0)
213
8.28k
                        gs_overprint_set_drawn_comp( params.drawn_comps, mcomp);
214
8.28k
                }
215
8.28k
            }
216
8.28k
        }
217
        /* Only DeviceCMYK can use overprint mode */
218
8.35k
        params.effective_opm = pgs->color[0].effective_opm = 0;
219
8.35k
        return gs_gstate_update_overprint(pgs, &params);
220
8.35k
    }
221
17.7k
}
222
223
/* Finalize contents of a Separation color space. */
224
static void
225
gx_final_Separation(gs_color_space * pcs)
226
31.6k
{
227
31.6k
    rc_adjust_const(pcs->params.separation.map, -1,
228
31.6k
                    "gx_adjust_Separation");
229
31.6k
    pcs->params.separation.map = NULL;
230
31.6k
    gs_free_object(pcs->params.separation.mem, pcs->params.separation.sep_name, "gx_final_Separation");
231
31.6k
    pcs->params.separation.sep_name = NULL;
232
31.6k
}
233
234
/* ------ Constructors/accessors ------ */
235
236
/*
237
 * Construct a new separation color space.
238
 */
239
int
240
gs_cspace_new_Separation(
241
    gs_color_space **ppcs,
242
    gs_color_space * palt_cspace,
243
    gs_memory_t * pmem
244
)
245
31.6k
{
246
31.6k
    gs_color_space *pcs;
247
31.6k
    int code;
248
249
31.6k
    if (palt_cspace == 0 || !palt_cspace->type->can_be_alt_space)
250
0
        return_error(gs_error_rangecheck);
251
252
31.6k
    pcs = gs_cspace_alloc(pmem, &gs_color_space_type_Separation);
253
31.6k
    if (pcs == NULL)
254
0
        return_error(gs_error_VMerror);
255
31.6k
    pcs->params.separation.map = NULL;
256
31.6k
    pcs->params.separation.named_color_supported = false;
257
258
31.6k
    code = alloc_device_n_map(&pcs->params.separation.map, pmem,
259
31.6k
                              "gs_cspace_build_Separation");
260
31.6k
    if (code < 0) {
261
0
        gs_free_object(pmem, pcs, "gs_cspace_build_Separation");
262
0
        return_error(code);
263
0
    }
264
31.6k
    pcs->base_space = palt_cspace;
265
31.6k
    rc_increment_cs(palt_cspace);
266
31.6k
    *ppcs = pcs;
267
31.6k
    return 0;
268
31.6k
}
269
270
#if 0 /* Unused; Unsupported by gx_serialize_device_n_map. */
271
/*
272
 * Set the tint transformation procedure used by a Separation color space.
273
 */
274
int
275
gs_cspace_set_sepr_proc(gs_color_space * pcspace,
276
                        int (*proc)(const float *,
277
                                    float *,
278
                                    const gs_gstate *,
279
                                    void *
280
                                    ),
281
                        void *proc_data
282
                        )
283
{
284
    gs_device_n_map *pimap;
285
286
    if (gs_color_space_get_index(pcspace) != gs_color_space_index_Separation)
287
        return_error(gs_error_rangecheck);
288
    pimap = pcspace->params.separation.map;
289
    pimap->tint_transform = proc;
290
    pimap->tint_transform_data = proc_data;
291
    pimap->cache_valid = false;
292
293
    return 0;
294
}
295
#endif
296
297
/*
298
 * Set the Separation tint transformation procedure to a Function.
299
 */
300
int
301
gs_cspace_set_sepr_function(const gs_color_space *pcspace, gs_function_t *pfn)
302
31.6k
{
303
31.6k
    gs_device_n_map *pimap;
304
305
31.6k
    if (gs_color_space_get_index(pcspace) != gs_color_space_index_Separation ||
306
31.6k
        pfn->params.m != 1 || pfn->params.n !=
307
31.6k
          gs_color_space_num_components(pcspace->base_space)
308
31.6k
        )
309
0
        return_error(gs_error_rangecheck);
310
31.6k
    pimap = pcspace->params.separation.map;
311
31.6k
    pimap->tint_transform = map_devn_using_function;
312
31.6k
    pimap->tint_transform_data = pfn;
313
31.6k
    pimap->cache_valid = false;
314
31.6k
    return 0;
315
31.6k
}
316
317
/*
318
 * If the Separation tint transformation procedure is a Function,
319
 * return the function object, otherwise return 0.
320
 */
321
gs_function_t *
322
gs_cspace_get_sepr_function(const gs_color_space *pcspace)
323
25.8k
{
324
25.8k
    if (gs_color_space_get_index(pcspace) == gs_color_space_index_Separation &&
325
25.8k
        pcspace->params.separation.map->tint_transform ==
326
25.8k
          map_devn_using_function)
327
25.8k
        return pcspace->params.separation.map->tint_transform_data;
328
0
    return 0;
329
25.8k
}
330
331
/* ------ Internal procedures ------ */
332
333
/* Initialize a Separation color. */
334
static void
335
gx_init_Separation(gs_client_color * pcc, const gs_color_space * pcs)
336
32.1k
{
337
32.1k
    pcc->paint.values[0] = 1.0;
338
32.1k
}
339
340
/* Remap a Separation color. */
341
342
static int
343
gx_remap_Separation(const gs_client_color * pcc, const gs_color_space * pcs,
344
        gx_device_color * pdc, const gs_gstate * pgs, gx_device * dev,
345
                       gs_color_select_t select)
346
110k
{
347
110k
    int code = 0;
348
110k
    bool mapped = false;
349
350
110k
    if (pcs->params.separation.sep_type != SEP_NONE) {
351
        /* First see if we want to do a direct remap with a named color table.
352
           Note that this call occurs regardless of the alt color space boolean
353
           value.  A decision can be made in gsicc_transform_named_color to
354
           return false if you don't want those named colors to be mapped */
355
110k
        if (pcs->params.separation.sep_type == SEP_OTHER &&
356
110k
            pgs->icc_manager->device_named != NULL) {
357
            /* Try to apply the direct replacement */
358
0
            mapped = gx_remap_named_color(pcc, pcs, pdc, pgs, dev, select);
359
0
        }
360
110k
        if (!mapped)
361
110k
            code = gx_default_remap_color(pcc, pcs, pdc, pgs, dev, select);
362
110k
    } else {
363
0
        color_set_null(pdc);
364
0
    }
365
    /* Save original color space and color info into dev color */
366
110k
    pdc->ccolor.paint.values[0] = pcc->paint.values[0];
367
110k
    pdc->ccolor_valid = true;
368
110k
    return code;
369
110k
}
370
371
static int
372
gx_concretize_Separation(const gs_client_color *pc, const gs_color_space *pcs,
373
                         frac *pconc, const gs_gstate *pgs, gx_device *dev)
374
110k
{
375
110k
    int code;
376
110k
    gs_client_color cc;
377
110k
    gs_color_space *pacs = pcs->base_space;
378
110k
    bool is_lab;
379
380
110k
    if (pcs->params.separation.sep_type == SEP_OTHER &&
381
110k
        pcs->params.separation.use_alt_cspace) {
382
103k
        gs_device_n_map *map = pcs->params.separation.map;
383
        /* Check the 1-element cache first. */
384
103k
        if (map->cache_valid && map->tint[0] == pc->paint.values[0]) {
385
0
            int i, num_out = gs_color_space_num_components(pacs);
386
387
0
            for (i = 0; i < num_out; ++i)
388
0
                pconc[i] = map->conc[i];
389
0
            return 0;
390
0
        }
391
103k
        code = (*pcs->params.separation.map->tint_transform)
392
103k
            (pc->paint.values, &cc.paint.values[0],
393
103k
             pgs, pcs->params.separation.map->tint_transform_data);
394
103k
        if (code < 0)
395
96
            return code;
396
103k
        (*pacs->type->restrict_color)(&cc, pacs);
397
        /* First check if this was PS based. */
398
103k
        if (gs_color_space_is_PSCIE(pacs)) {
399
            /* We may have to rescale data to 0 to 1 range */
400
0
            rescale_cie_colors(pacs, &cc);
401
            /* If we have not yet created the profile do that now */
402
0
            if (pacs->icc_equivalent == NULL) {
403
0
                code = gs_colorspace_set_icc_equivalent(pacs, &(is_lab), pgs->memory);
404
0
                if (code < 0)
405
0
                   return code;
406
0
            }
407
            /* Use the ICC equivalent color space */
408
0
            pacs = pacs->icc_equivalent;
409
0
        }
410
103k
        if (pacs->cmm_icc_profile_data &&
411
103k
            (pacs->cmm_icc_profile_data->data_cs == gsCIELAB ||
412
103k
            pacs->cmm_icc_profile_data->islab)) {
413
            /* Get the data in a form that is concrete for the CMM */
414
0
            cc.paint.values[0] /= 100.0;
415
0
            cc.paint.values[1] = (cc.paint.values[1]+128)/255.0;
416
0
            cc.paint.values[2] = (cc.paint.values[2]+128)/255.0;
417
0
        }
418
103k
        return cs_concretize_color(&cc, pacs, pconc, pgs, dev);
419
103k
    } else {
420
6.88k
        pconc[0] = gx_unit_frac(pc->paint.values[0]);
421
6.88k
    }
422
6.88k
    return 0;
423
110k
}
424
425
static int
426
gx_remap_concrete_Separation(const gs_color_space * pcs, const frac * pconc,
427
        gx_device_color * pdc, const gs_gstate * pgs, gx_device * dev,
428
                             gs_color_select_t select, const cmm_dev_profile_t *dev_profile)
429
6.88k
{
430
#ifdef DEBUG
431
    /*
432
     * Verify that the color space and gs_gstate info match.
433
     */
434
    if (pcs->id != pgs->color_component_map.cspace_id)
435
        dmprintf(pgs->memory, "gx_remap_concrete_Separation: color space id mismatch");
436
#endif
437
438
6.88k
    if (pgs->color_component_map.use_alt_cspace) {
439
0
        const gs_color_space *pacs = pcs->base_space;
440
441
0
        return (*pacs->type->remap_concrete_color)
442
0
                                (pacs, pconc, pdc, pgs, dev, select, dev_profile);
443
6.88k
    } else {
444
        /* We need to determine if we did a direct color replacement */
445
6.88k
        gx_remap_concrete_separation(pconc[0], pdc, pgs, dev, select, pcs);
446
6.88k
        return 0;
447
6.88k
    }
448
6.88k
}
449
450
/*
451
 * Check that the color component name for a Separation color space
452
 * matches the device colorant names.  Also build a gs_devicen_color_map
453
 * structure.
454
 */
455
static int
456
check_Separation_component_name(const gs_color_space * pcs, gs_gstate * pgs)
457
31.9k
{
458
31.9k
    int colorant_number;
459
31.9k
    byte *pname;
460
31.9k
    uint name_size;
461
31.9k
    gs_devicen_color_map * pcolor_component_map
462
31.9k
        = &pgs->color_component_map;
463
31.9k
    gx_device * dev = pgs->device;
464
465
31.9k
    pcolor_component_map->num_components = 1;
466
31.9k
    pcolor_component_map->cspace_id = pcs->id;
467
31.9k
    pcolor_component_map->num_colorants = dev->color_info.num_components;
468
31.9k
    pcolor_component_map->sep_type = pcs->params.separation.sep_type;
469
    /*
470
     * If this is a None or All separation then we do not need to
471
     * use the alternate color space.  Also if the named color
472
     * profile supports the component, don't use the alternate
473
     * tint transform. */
474
31.9k
    if (pcs->params.separation.sep_type != SEP_OTHER ||
475
31.9k
        gsicc_support_named_color(pcs, pgs)) {
476
0
        pcolor_component_map->use_alt_cspace = false;
477
0
        return 0;
478
0
    }
479
    /* If our device is using an additive color model, then we need to
480
     * consider using the alternative color space, as separations are
481
     * generally only used with a subtractive color model. There are
482
     * exceptions, however.
483
     *
484
     * If we don't support devn, then we will certainly have to use the
485
     * alternative color space.
486
     *
487
     * If we are a pdf14 device, and we are doing transparency blending
488
     * in an additive space, we need to keep the spots separated and
489
     * blend them individually as per the PDF specification. Note
490
     * however, if the spot is a CMYK process color and we are doing
491
     * the blend in an additive color space the alternate color space
492
     * is used.  This matches AR.
493
     *
494
     * Otherwise, we will always use the alternate colorspace, unless
495
     * our device specifically claims to be a separation-supporting
496
     * additive device. Possibly all additive devn devices should
497
     * support this now, but I lack to confidence to make this change
498
     * globally. Instead we'll just enable it on a device by device
499
     * basis for now.
500
     *
501
     * This matches logic in check_DeviceN_component_names.
502
     */
503
31.9k
    if (dev->color_info.polarity == GX_CINFO_POLARITY_ADDITIVE) {
504
28.8k
        if (dev_proc(dev, dev_spec_op)(dev, gxdso_supports_devn, NULL, 0) == 0 ||
505
638
            (dev_proc(dev, dev_spec_op)(dev, gxdso_is_pdf14_device, NULL, 0) == 0 &&
506
28.1k
             dev_proc(dev, dev_spec_op)(dev, gxdso_is_sep_supporting_additive_device, NULL, 0) == 0)) {
507
28.1k
            pcolor_component_map->use_alt_cspace = true;
508
28.1k
            return 0;
509
28.1k
        }
510
28.8k
    }
511
512
    /*
513
     * Get the character string and length for the component name.
514
     */
515
3.74k
    pname = (byte *)pcs->params.separation.sep_name;
516
3.74k
    name_size = strlen(pcs->params.separation.sep_name);
517
    /*
518
     * Compare the colorant name to the device's.  If the device's
519
     * compare routine returns GX_DEVICE_COLOR_MAX_COMPONENTS then the
520
     * colorant is in the SeparationNames list but not in the
521
     * SeparationOrder list.
522
     */
523
3.74k
    colorant_number = (*dev_proc(dev, get_color_comp_index))
524
3.74k
                (dev, (const char *)pname, name_size, SEPARATION_NAME);
525
3.74k
    if (colorant_number >= 0 && colorant_number < dev->color_info.max_components) {   /* If valid colorant name */
526
3.73k
        pcolor_component_map->color_map[0] =
527
3.73k
                    (colorant_number == GX_DEVICE_COLOR_MAX_COMPONENTS) ? -1
528
3.73k
                                                           : colorant_number;
529
3.73k
        pcolor_component_map->use_alt_cspace = false;
530
3.73k
    }
531
10
    else
532
10
        pcolor_component_map->use_alt_cspace = true;
533
3.74k
    return 0;
534
31.9k
}
535
536
/* ---------------- Notes on real Separation colors ---------------- */
537
538
typedef ulong gs_separation;  /* BOGUS */
539
540
#define gs_no_separation ((gs_separation)(-1L))
541
542
#define dev_proc_lookup_separation(proc)\
543
  gs_separation proc(gx_device *dev, const byte *sname, uint len,\
544
    gx_color_value *num_levels)
545
546
#define dev_proc_map_tint_color(proc)\
547
  gx_color_index proc(gx_device *dev, gs_separation sepr, bool overprint,\
548
    gx_color_value tint)
549
550
/*
551
 * This next comment is outdated since the Separation color space no longer
552
 * has the multi element cache (lookup table) however the remainder is
553
 * still appropriate.
554
 *
555
 * In principle, setting a Separation color space, or setting the device
556
 * when the current color space is a Separation space, calls the
557
 * lookup_separation device procedure to obtain the separation ID and
558
 * the number of achievable levels.  Currently, the only hooks for doing
559
 * this are unsuitable: gx_set_cmap_procs isn't called when the color
560
 * space changes, and doing it in gx_remap_Separation is inefficient.
561
 * Probably the best approach is to call gx_set_cmap_procs whenever the
562
 * color space changes.  In fact, if we do this, we can probably short-cut
563
 * two levels of procedure call in color remapping (gx_remap_color, by
564
 * turning it into a macro, and gx_remap_DeviceXXX, by calling the
565
 * cmap_proc procedure directly).  Some care will be required for the
566
 * implicit temporary resetting of the color space in [color]image.
567
 */
568
569
/* ---------------- Serialization. -------------------------------- */
570
571
static int
572
gx_serialize_Separation(const gs_color_space * pcs, stream * s)
573
23.6k
{
574
23.6k
    const gs_separation_params * p = &pcs->params.separation;
575
23.6k
    uint n;
576
23.6k
    int code = gx_serialize_cspace_type(pcs, s);
577
578
23.6k
    if (code < 0)
579
0
        return code;
580
23.6k
    code = sputs(s, (const byte *)p->sep_name, strlen(p->sep_name) + 1, &n);
581
23.6k
    if (code < 0)
582
0
        return code;
583
23.6k
    code = cs_serialize(pcs->base_space, s);
584
23.6k
    if (code < 0)
585
0
        return code;
586
23.6k
    code = gx_serialize_device_n_map(pcs, p->map, s);
587
23.6k
    if (code < 0)
588
0
        return code;
589
23.6k
    return sputs(s, (const byte *)&p->sep_type, sizeof(p->sep_type), &n);
590
    /* p->use_alt_cspace isn't a property of the space. */
591
23.6k
}