Coverage Report

Created: 2026-02-14 07:09

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.12k
ENUM_PTRS_BEGIN(cs_Separation_enum_ptrs) return 0;
73
1.12k
    ENUM_PTR(0, gs_color_space, params.separation.map);
74
1.12k
ENUM_PTRS_END
75
561
static RELOC_PTRS_BEGIN(cs_Separation_reloc_ptrs)
76
561
{
77
561
    RELOC_PTR(gs_color_space, params.separation.map);
78
561
}
79
561
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
2.69M
{
86
2.69M
    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
2.69M
    if (pgs->color_component_map.use_alt_cspace) {
99
        /* Need to handle PS CIE space */
100
2.68M
        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
2.68M
        return cs_concrete_space(pcs->base_space, pgs);
108
2.68M
    }
109
    /*
110
     * Separation color spaces are concrete (when not using alt. color space).
111
     */
112
7.09k
    return pcs;
113
2.69M
}
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
34.5k
{
122
34.5k
    byte *pname = (byte *)pcs->params.separation.sep_name;
123
34.5k
    uint name_size = strlen(pcs->params.separation.sep_name);
124
125
    /* Classify */
126
34.5k
    if (strncmp((char *)pname, "None", name_size) == 0 ||
127
34.5k
        strncmp((char *)pname, "All", name_size) == 0) {
128
2
        return SEP_ENUM;
129
34.5k
    } else {
130
34.5k
        if (strncmp((char *)pname, "Cyan", name_size) == 0 ||
131
34.5k
            strncmp((char *)pname, "Magenta", name_size) == 0 ||
132
34.5k
            strncmp((char *)pname, "Yellow", name_size) == 0 ||
133
34.5k
            strncmp((char *)pname, "Black", name_size) == 0) {
134
12.8k
            return SEP_PURE_CMYK;
135
21.7k
        } else if (strncmp((char *)pname, "Red", name_size) == 0 ||
136
21.7k
            strncmp((char *)pname, "Green", name_size) == 0 ||
137
21.7k
            strncmp((char *)pname, "Blue", name_size) == 0) {
138
0
            return SEP_PURE_RGB;
139
21.7k
        } else {
140
21.7k
            return SEP_MIX;
141
21.7k
        }
142
34.5k
    }
143
34.5k
}
144
145
/* Install a Separation color space. */
146
static int
147
gx_install_Separation(gs_color_space * pcs, gs_gstate * pgs)
148
34.5k
{
149
34.5k
    int code;
150
151
34.5k
    code = check_Separation_component_name(pcs, pgs);
152
34.5k
    if (code < 0)
153
0
       return code;
154
155
34.5k
    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
34.5k
    pcs->params.separation.color_type =
161
34.5k
        gx_check_process_names_Separation(pcs, pgs);
162
163
34.5k
    gs_currentcolorspace_inline(pgs)->params.separation.use_alt_cspace =
164
34.5k
        using_alt_color_space(pgs);
165
34.5k
    if (gs_currentcolorspace_inline(pgs)->params.separation.use_alt_cspace) {
166
30.7k
        code = (pcs->base_space->type->install_cspace)
167
30.7k
            (pcs->base_space, pgs);
168
30.7k
    } 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.75k
        if (dev_proc(pgs->device, update_spot_equivalent_colors))
174
3.75k
           code = dev_proc(pgs->device, update_spot_equivalent_colors)
175
3.75k
                                                        (pgs->device, pgs, pcs);
176
3.75k
    }
177
34.5k
    return code;
178
34.5k
}
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
18.5k
{
184
18.5k
    gs_devicen_color_map *  pcmap = &pgs->color_component_map;
185
186
18.5k
    if (pcmap->use_alt_cspace)
187
7.08k
        return gx_set_no_overprint(pgs);
188
11.4k
    else {
189
11.4k
        gs_overprint_params_t params = { 0 };
190
191
11.4k
        params.retain_any_comps = (((pgs->overprint && pgs->is_fill_color) ||
192
4.21k
                                   (pgs->stroke_overprint && !pgs->is_fill_color)) &&
193
7.20k
                                   (pcs->params.separation.sep_type != SEP_ALL));
194
11.4k
        params.is_fill_color = pgs->is_fill_color;
195
11.4k
        params.drawn_comps = 0;
196
11.4k
        params.op_state = OP_STATE_NONE;
197
11.4k
        if (params.retain_any_comps) {
198
7.20k
            if (pcs->params.separation.sep_type != SEP_NONE) {
199
7.20k
                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
7.20k
                } else {
210
7.20k
                    int mcomp = pcmap->color_map[0];
211
212
7.20k
                    if (mcomp >= 0)
213
7.20k
                        gs_overprint_set_drawn_comp( params.drawn_comps, mcomp);
214
7.20k
                }
215
7.20k
            }
216
7.20k
        }
217
        /* Only DeviceCMYK can use overprint mode */
218
11.4k
        params.effective_opm = pgs->color[0].effective_opm = 0;
219
11.4k
        return gs_gstate_update_overprint(pgs, &params);
220
11.4k
    }
221
18.5k
}
222
223
/* Finalize contents of a Separation color space. */
224
static void
225
gx_final_Separation(gs_color_space * pcs)
226
34.2k
{
227
34.2k
    rc_adjust_const(pcs->params.separation.map, -1,
228
34.2k
                    "gx_adjust_Separation");
229
34.2k
    pcs->params.separation.map = NULL;
230
34.2k
    gs_free_object(pcs->params.separation.mem, pcs->params.separation.sep_name, "gx_final_Separation");
231
34.2k
    pcs->params.separation.sep_name = NULL;
232
34.2k
}
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
34.2k
{
246
34.2k
    gs_color_space *pcs;
247
34.2k
    int code;
248
249
34.2k
    if (palt_cspace == 0 || !palt_cspace->type->can_be_alt_space)
250
0
        return_error(gs_error_rangecheck);
251
252
34.2k
    pcs = gs_cspace_alloc(pmem, &gs_color_space_type_Separation);
253
34.2k
    if (pcs == NULL)
254
0
        return_error(gs_error_VMerror);
255
34.2k
    pcs->params.separation.map = NULL;
256
34.2k
    pcs->params.separation.named_color_supported = false;
257
258
34.2k
    code = alloc_device_n_map(&pcs->params.separation.map, pmem,
259
34.2k
                              "gs_cspace_build_Separation");
260
34.2k
    if (code < 0) {
261
0
        gs_free_object(pmem, pcs, "gs_cspace_build_Separation");
262
0
        return_error(code);
263
0
    }
264
34.2k
    pcs->base_space = palt_cspace;
265
34.2k
    rc_increment_cs(palt_cspace);
266
34.2k
    *ppcs = pcs;
267
34.2k
    return 0;
268
34.2k
}
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
34.2k
{
303
34.2k
    gs_device_n_map *pimap;
304
305
34.2k
    if (gs_color_space_get_index(pcspace) != gs_color_space_index_Separation ||
306
34.2k
        pfn->params.m != 1 || pfn->params.n !=
307
34.2k
          gs_color_space_num_components(pcspace->base_space)
308
34.2k
        )
309
0
        return_error(gs_error_rangecheck);
310
34.2k
    pimap = pcspace->params.separation.map;
311
34.2k
    pimap->tint_transform = map_devn_using_function;
312
34.2k
    pimap->tint_transform_data = pfn;
313
34.2k
    pimap->cache_valid = false;
314
34.2k
    return 0;
315
34.2k
}
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
28.4k
{
324
28.4k
    if (gs_color_space_get_index(pcspace) == gs_color_space_index_Separation &&
325
28.4k
        pcspace->params.separation.map->tint_transform ==
326
28.4k
          map_devn_using_function)
327
28.4k
        return pcspace->params.separation.map->tint_transform_data;
328
0
    return 0;
329
28.4k
}
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
36.0k
{
337
36.0k
    pcc->paint.values[0] = 1.0;
338
36.0k
}
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
2.70M
{
347
2.70M
    int code = 0;
348
2.70M
    bool mapped = false;
349
350
2.70M
    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
2.70M
        if (pcs->params.separation.sep_type == SEP_OTHER &&
356
2.70M
            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
2.70M
        if (!mapped)
361
2.70M
            code = gx_default_remap_color(pcc, pcs, pdc, pgs, dev, select);
362
2.70M
    } else {
363
0
        color_set_null(pdc);
364
0
    }
365
    /* Save original color space and color info into dev color */
366
2.70M
    pdc->ccolor.paint.values[0] = pcc->paint.values[0];
367
2.70M
    pdc->ccolor_valid = true;
368
2.70M
    return code;
369
2.70M
}
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
2.70M
{
375
2.70M
    int code;
376
2.70M
    gs_client_color cc;
377
2.70M
    gs_color_space *pacs = pcs->base_space;
378
2.70M
    bool is_lab;
379
380
2.70M
    if (pcs->params.separation.sep_type == SEP_OTHER &&
381
2.70M
        pcs->params.separation.use_alt_cspace) {
382
2.69M
        gs_device_n_map *map = pcs->params.separation.map;
383
        /* Check the 1-element cache first. */
384
2.69M
        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
2.69M
        code = (*pcs->params.separation.map->tint_transform)
392
2.69M
            (pc->paint.values, &cc.paint.values[0],
393
2.69M
             pgs, pcs->params.separation.map->tint_transform_data);
394
2.69M
        if (code < 0)
395
125
            return code;
396
2.69M
        (*pacs->type->restrict_color)(&cc, pacs);
397
        /* First check if this was PS based. */
398
2.69M
        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
2.69M
        if (pacs->cmm_icc_profile_data &&
411
2.69M
            (pacs->cmm_icc_profile_data->data_cs == gsCIELAB ||
412
2.69M
            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
2.69M
        return cs_concretize_color(&cc, pacs, pconc, pgs, dev);
419
2.69M
    } else {
420
7.08k
        pconc[0] = gx_unit_frac(pc->paint.values[0]);
421
7.08k
    }
422
7.08k
    return 0;
423
2.70M
}
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
7.08k
{
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
7.08k
    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
7.08k
    } else {
444
        /* We need to determine if we did a direct color replacement */
445
7.08k
        gx_remap_concrete_separation(pconc[0], pdc, pgs, dev, select, pcs);
446
7.08k
        return 0;
447
7.08k
    }
448
7.08k
}
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
34.5k
{
458
34.5k
    int colorant_number;
459
34.5k
    byte *pname;
460
34.5k
    uint name_size;
461
34.5k
    gs_devicen_color_map * pcolor_component_map
462
34.5k
        = &pgs->color_component_map;
463
34.5k
    gx_device * dev = pgs->device;
464
465
34.5k
    pcolor_component_map->num_components = 1;
466
34.5k
    pcolor_component_map->cspace_id = pcs->id;
467
34.5k
    pcolor_component_map->num_colorants = dev->color_info.num_components;
468
34.5k
    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
34.5k
    if (pcs->params.separation.sep_type != SEP_OTHER ||
475
34.5k
        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
34.5k
    if (dev->color_info.polarity == GX_CINFO_POLARITY_ADDITIVE) {
504
30.1k
        if (dev_proc(dev, dev_spec_op)(dev, gxdso_supports_devn, NULL, 0) == 0 ||
505
594
            (dev_proc(dev, dev_spec_op)(dev, gxdso_is_pdf14_device, NULL, 0) == 0 &&
506
29.5k
             dev_proc(dev, dev_spec_op)(dev, gxdso_is_sep_supporting_additive_device, NULL, 0) == 0)) {
507
29.5k
            pcolor_component_map->use_alt_cspace = true;
508
29.5k
            return 0;
509
29.5k
        }
510
30.1k
    }
511
512
    /*
513
     * Get the character string and length for the component name.
514
     */
515
4.98k
    pname = (byte *)pcs->params.separation.sep_name;
516
4.98k
    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
4.98k
    colorant_number = (*dev_proc(dev, get_color_comp_index))
524
4.98k
                (dev, (const char *)pname, name_size, SEPARATION_NAME);
525
4.98k
    if (colorant_number >= 0 && colorant_number < dev->color_info.max_components) {   /* If valid colorant name */
526
3.75k
        pcolor_component_map->color_map[0] =
527
3.75k
                    (colorant_number == GX_DEVICE_COLOR_MAX_COMPONENTS) ? -1
528
3.75k
                                                           : colorant_number;
529
3.75k
        pcolor_component_map->use_alt_cspace = false;
530
3.75k
    }
531
1.22k
    else
532
1.22k
        pcolor_component_map->use_alt_cspace = true;
533
4.98k
    return 0;
534
34.5k
}
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
29.1k
{
574
29.1k
    const gs_separation_params * p = &pcs->params.separation;
575
29.1k
    uint n;
576
29.1k
    int code = gx_serialize_cspace_type(pcs, s);
577
578
29.1k
    if (code < 0)
579
0
        return code;
580
29.1k
    code = sputs(s, (const byte *)p->sep_name, strlen(p->sep_name) + 1, &n);
581
29.1k
    if (code < 0)
582
0
        return code;
583
29.1k
    code = cs_serialize(pcs->base_space, s);
584
29.1k
    if (code < 0)
585
0
        return code;
586
29.1k
    code = gx_serialize_device_n_map(pcs, p->map, s);
587
29.1k
    if (code < 0)
588
0
        return code;
589
29.1k
    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
29.1k
}