Coverage Report

Created: 2025-06-24 07:01

/src/ghostpdl/base/gscsepr.c
Line
Count
Source (jump to first uncovered line)
1
/* Copyright (C) 2001-2024 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
862
ENUM_PTRS_BEGIN(cs_Separation_enum_ptrs) return 0;
73
862
    ENUM_PTR(0, gs_color_space, params.separation.map);
74
862
ENUM_PTRS_END
75
431
static RELOC_PTRS_BEGIN(cs_Separation_reloc_ptrs)
76
431
{
77
431
    RELOC_PTR(gs_color_space, params.separation.map);
78
431
}
79
431
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
512k
{
86
512k
    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
512k
    if (pgs->color_component_map.use_alt_cspace) {
99
        /* Need to handle PS CIE space */
100
459k
        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
459k
        return cs_concrete_space(pcs->base_space, pgs);
108
459k
    }
109
    /*
110
     * Separation color spaces are concrete (when not using alt. color space).
111
     */
112
52.4k
    return pcs;
113
512k
}
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
61.0k
{
122
61.0k
    byte *pname = (byte *)pcs->params.separation.sep_name;
123
61.0k
    uint name_size = strlen(pcs->params.separation.sep_name);
124
125
    /* Classify */
126
61.0k
    if (strncmp((char *)pname, "None", name_size) == 0 ||
127
61.0k
        strncmp((char *)pname, "All", name_size) == 0) {
128
0
        return SEP_ENUM;
129
61.0k
    } else {
130
61.0k
        if (strncmp((char *)pname, "Cyan", name_size) == 0 ||
131
61.0k
            strncmp((char *)pname, "Magenta", name_size) == 0 ||
132
61.0k
            strncmp((char *)pname, "Yellow", name_size) == 0 ||
133
61.0k
            strncmp((char *)pname, "Black", name_size) == 0) {
134
28.7k
            return SEP_PURE_CMYK;
135
32.2k
        } else if (strncmp((char *)pname, "Red", name_size) == 0 ||
136
32.2k
            strncmp((char *)pname, "Green", name_size) == 0 ||
137
32.2k
            strncmp((char *)pname, "Blue", name_size) == 0) {
138
0
            return SEP_PURE_RGB;
139
32.2k
        } else {
140
32.2k
            return SEP_MIX;
141
32.2k
        }
142
61.0k
    }
143
61.0k
}
144
145
/* Install a Separation color space. */
146
static int
147
gx_install_Separation(gs_color_space * pcs, gs_gstate * pgs)
148
61.0k
{
149
61.0k
    int code;
150
151
61.0k
    code = check_Separation_component_name(pcs, pgs);
152
61.0k
    if (code < 0)
153
0
       return code;
154
155
61.0k
    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
61.0k
    pcs->params.separation.color_type =
161
61.0k
        gx_check_process_names_Separation(pcs, pgs);
162
163
61.0k
    gs_currentcolorspace_inline(pgs)->params.separation.use_alt_cspace =
164
61.0k
        using_alt_color_space(pgs);
165
61.0k
    if (gs_currentcolorspace_inline(pgs)->params.separation.use_alt_cspace) {
166
54.0k
        code = (pcs->base_space->type->install_cspace)
167
54.0k
            (pcs->base_space, pgs);
168
54.0k
    } 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
6.98k
        if (dev_proc(pgs->device, update_spot_equivalent_colors))
174
6.98k
           code = dev_proc(pgs->device, update_spot_equivalent_colors)
175
6.98k
                                                        (pgs->device, pgs, pcs);
176
6.98k
    }
177
61.0k
    return code;
178
61.0k
}
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
49.1k
{
184
49.1k
    gs_devicen_color_map *  pcmap = &pgs->color_component_map;
185
186
49.1k
    if (pcmap->use_alt_cspace)
187
15.5k
        return gx_set_no_overprint(pgs);
188
33.6k
    else {
189
33.6k
        gs_overprint_params_t params = { 0 };
190
191
33.6k
        params.retain_any_comps = (((pgs->overprint && pgs->is_fill_color) ||
192
33.6k
                                   (pgs->stroke_overprint && !pgs->is_fill_color)) &&
193
33.6k
                                   (pcs->params.separation.sep_type != SEP_ALL));
194
33.6k
        params.is_fill_color = pgs->is_fill_color;
195
33.6k
        params.drawn_comps = 0;
196
33.6k
        params.op_state = OP_STATE_NONE;
197
33.6k
        if (params.retain_any_comps) {
198
8.58k
            if (pcs->params.separation.sep_type != SEP_NONE) {
199
8.58k
                int mcomp = pcmap->color_map[0];
200
201
8.58k
                if (mcomp >= 0)
202
8.58k
                    gs_overprint_set_drawn_comp( params.drawn_comps, mcomp);
203
8.58k
            }
204
8.58k
        }
205
        /* Only DeviceCMYK can use overprint mode */
206
33.6k
        params.effective_opm = pgs->color[0].effective_opm = 0;
207
33.6k
        return gs_gstate_update_overprint(pgs, &params);
208
33.6k
    }
209
49.1k
}
210
211
/* Finalize contents of a Separation color space. */
212
static void
213
gx_final_Separation(gs_color_space * pcs)
214
60.7k
{
215
60.7k
    rc_adjust_const(pcs->params.separation.map, -1,
216
60.7k
                    "gx_adjust_Separation");
217
60.7k
    pcs->params.separation.map = NULL;
218
60.7k
    gs_free_object(pcs->params.separation.mem, pcs->params.separation.sep_name, "gx_final_Separation");
219
60.7k
    pcs->params.separation.sep_name = NULL;
220
60.7k
}
221
222
/* ------ Constructors/accessors ------ */
223
224
/*
225
 * Construct a new separation color space.
226
 */
227
int
228
gs_cspace_new_Separation(
229
    gs_color_space **ppcs,
230
    gs_color_space * palt_cspace,
231
    gs_memory_t * pmem
232
)
233
60.7k
{
234
60.7k
    gs_color_space *pcs;
235
60.7k
    int code;
236
237
60.7k
    if (palt_cspace == 0 || !palt_cspace->type->can_be_alt_space)
238
0
        return_error(gs_error_rangecheck);
239
240
60.7k
    pcs = gs_cspace_alloc(pmem, &gs_color_space_type_Separation);
241
60.7k
    if (pcs == NULL)
242
0
        return_error(gs_error_VMerror);
243
60.7k
    pcs->params.separation.map = NULL;
244
60.7k
    pcs->params.separation.named_color_supported = false;
245
246
60.7k
    code = alloc_device_n_map(&pcs->params.separation.map, pmem,
247
60.7k
                              "gs_cspace_build_Separation");
248
60.7k
    if (code < 0) {
249
0
        gs_free_object(pmem, pcs, "gs_cspace_build_Separation");
250
0
        return_error(code);
251
0
    }
252
60.7k
    pcs->base_space = palt_cspace;
253
60.7k
    rc_increment_cs(palt_cspace);
254
60.7k
    *ppcs = pcs;
255
60.7k
    return 0;
256
60.7k
}
257
258
#if 0 /* Unused; Unsupported by gx_serialize_device_n_map. */
259
/*
260
 * Set the tint transformation procedure used by a Separation color space.
261
 */
262
int
263
gs_cspace_set_sepr_proc(gs_color_space * pcspace,
264
                        int (*proc)(const float *,
265
                                    float *,
266
                                    const gs_gstate *,
267
                                    void *
268
                                    ),
269
                        void *proc_data
270
                        )
271
{
272
    gs_device_n_map *pimap;
273
274
    if (gs_color_space_get_index(pcspace) != gs_color_space_index_Separation)
275
        return_error(gs_error_rangecheck);
276
    pimap = pcspace->params.separation.map;
277
    pimap->tint_transform = proc;
278
    pimap->tint_transform_data = proc_data;
279
    pimap->cache_valid = false;
280
281
    return 0;
282
}
283
#endif
284
285
/*
286
 * Set the Separation tint transformation procedure to a Function.
287
 */
288
int
289
gs_cspace_set_sepr_function(const gs_color_space *pcspace, gs_function_t *pfn)
290
60.7k
{
291
60.7k
    gs_device_n_map *pimap;
292
293
60.7k
    if (gs_color_space_get_index(pcspace) != gs_color_space_index_Separation ||
294
60.7k
        pfn->params.m != 1 || pfn->params.n !=
295
60.7k
          gs_color_space_num_components(pcspace->base_space)
296
60.7k
        )
297
0
        return_error(gs_error_rangecheck);
298
60.7k
    pimap = pcspace->params.separation.map;
299
60.7k
    pimap->tint_transform = map_devn_using_function;
300
60.7k
    pimap->tint_transform_data = pfn;
301
60.7k
    pimap->cache_valid = false;
302
60.7k
    return 0;
303
60.7k
}
304
305
/*
306
 * If the Separation tint transformation procedure is a Function,
307
 * return the function object, otherwise return 0.
308
 */
309
gs_function_t *
310
gs_cspace_get_sepr_function(const gs_color_space *pcspace)
311
55.8k
{
312
55.8k
    if (gs_color_space_get_index(pcspace) == gs_color_space_index_Separation &&
313
55.8k
        pcspace->params.separation.map->tint_transform ==
314
55.8k
          map_devn_using_function)
315
55.8k
        return pcspace->params.separation.map->tint_transform_data;
316
0
    return 0;
317
55.8k
}
318
319
/* ------ Internal procedures ------ */
320
321
/* Initialize a Separation color. */
322
static void
323
gx_init_Separation(gs_client_color * pcc, const gs_color_space * pcs)
324
74.2k
{
325
74.2k
    pcc->paint.values[0] = 1.0;
326
74.2k
}
327
328
/* Remap a Separation color. */
329
330
static int
331
gx_remap_Separation(const gs_client_color * pcc, const gs_color_space * pcs,
332
        gx_device_color * pdc, const gs_gstate * pgs, gx_device * dev,
333
                       gs_color_select_t select)
334
517k
{
335
517k
    int code = 0;
336
517k
    bool mapped = false;
337
338
517k
    if (pcs->params.separation.sep_type != SEP_NONE) {
339
        /* First see if we want to do a direct remap with a named color table.
340
           Note that this call occurs regardless of the alt color space boolean
341
           value.  A decision can be made in gsicc_transform_named_color to
342
           return false if you don't want those named colors to be mapped */
343
517k
        if (pcs->params.separation.sep_type == SEP_OTHER &&
344
517k
            pgs->icc_manager->device_named != NULL) {
345
            /* Try to apply the direct replacement */
346
0
            mapped = gx_remap_named_color(pcc, pcs, pdc, pgs, dev, select);
347
0
        }
348
517k
        if (!mapped)
349
517k
            code = gx_default_remap_color(pcc, pcs, pdc, pgs, dev, select);
350
517k
    } else {
351
0
        color_set_null(pdc);
352
0
    }
353
    /* Save original color space and color info into dev color */
354
517k
    pdc->ccolor.paint.values[0] = pcc->paint.values[0];
355
517k
    pdc->ccolor_valid = true;
356
517k
    return code;
357
517k
}
358
359
static int
360
gx_concretize_Separation(const gs_client_color *pc, const gs_color_space *pcs,
361
                         frac *pconc, const gs_gstate *pgs, gx_device *dev)
362
517k
{
363
517k
    int code;
364
517k
    gs_client_color cc;
365
517k
    gs_color_space *pacs = pcs->base_space;
366
517k
    bool is_lab;
367
368
517k
    if (pcs->params.separation.sep_type == SEP_OTHER &&
369
517k
        pcs->params.separation.use_alt_cspace) {
370
465k
        gs_device_n_map *map = pcs->params.separation.map;
371
        /* Check the 1-element cache first. */
372
465k
        if (map->cache_valid && map->tint[0] == pc->paint.values[0]) {
373
0
            int i, num_out = gs_color_space_num_components(pacs);
374
375
0
            for (i = 0; i < num_out; ++i)
376
0
                pconc[i] = map->conc[i];
377
0
            return 0;
378
0
        }
379
465k
        code = (*pcs->params.separation.map->tint_transform)
380
465k
            (pc->paint.values, &cc.paint.values[0],
381
465k
             pgs, pcs->params.separation.map->tint_transform_data);
382
465k
        if (code < 0)
383
51
            return code;
384
465k
        (*pacs->type->restrict_color)(&cc, pacs);
385
        /* First check if this was PS based. */
386
465k
        if (gs_color_space_is_PSCIE(pacs)) {
387
            /* We may have to rescale data to 0 to 1 range */
388
0
            rescale_cie_colors(pacs, &cc);
389
            /* If we have not yet created the profile do that now */
390
0
            if (pacs->icc_equivalent == NULL) {
391
0
                code = gs_colorspace_set_icc_equivalent(pacs, &(is_lab), pgs->memory);
392
0
                if (code < 0)
393
0
                   return code;
394
0
            }
395
            /* Use the ICC equivalent color space */
396
0
            pacs = pacs->icc_equivalent;
397
0
        }
398
465k
        if (pacs->cmm_icc_profile_data &&
399
465k
            (pacs->cmm_icc_profile_data->data_cs == gsCIELAB ||
400
465k
            pacs->cmm_icc_profile_data->islab)) {
401
            /* Get the data in a form that is concrete for the CMM */
402
0
            cc.paint.values[0] /= 100.0;
403
0
            cc.paint.values[1] = (cc.paint.values[1]+128)/255.0;
404
0
            cc.paint.values[2] = (cc.paint.values[2]+128)/255.0;
405
0
        }
406
465k
        return cs_concretize_color(&cc, pacs, pconc, pgs, dev);
407
465k
    } else {
408
52.4k
        pconc[0] = gx_unit_frac(pc->paint.values[0]);
409
52.4k
    }
410
52.4k
    return 0;
411
517k
}
412
413
static int
414
gx_remap_concrete_Separation(const gs_color_space * pcs, const frac * pconc,
415
        gx_device_color * pdc, const gs_gstate * pgs, gx_device * dev,
416
                             gs_color_select_t select, const cmm_dev_profile_t *dev_profile)
417
52.4k
{
418
#ifdef DEBUG
419
    /*
420
     * Verify that the color space and gs_gstate info match.
421
     */
422
    if (pcs->id != pgs->color_component_map.cspace_id)
423
        dmprintf(pgs->memory, "gx_remap_concrete_Separation: color space id mismatch");
424
#endif
425
426
52.4k
    if (pgs->color_component_map.use_alt_cspace) {
427
0
        const gs_color_space *pacs = pcs->base_space;
428
429
0
        return (*pacs->type->remap_concrete_color)
430
0
                                (pacs, pconc, pdc, pgs, dev, select, dev_profile);
431
52.4k
    } else {
432
        /* We need to determine if we did a direct color replacement */
433
52.4k
        gx_remap_concrete_separation(pconc[0], pdc, pgs, dev, select, pcs);
434
52.4k
        return 0;
435
52.4k
    }
436
52.4k
}
437
438
/*
439
 * Check that the color component name for a Separation color space
440
 * matches the device colorant names.  Also build a gs_devicen_color_map
441
 * structure.
442
 */
443
static int
444
check_Separation_component_name(const gs_color_space * pcs, gs_gstate * pgs)
445
61.0k
{
446
61.0k
    int colorant_number;
447
61.0k
    byte *pname;
448
61.0k
    uint name_size;
449
61.0k
    gs_devicen_color_map * pcolor_component_map
450
61.0k
        = &pgs->color_component_map;
451
61.0k
    gx_device * dev = pgs->device;
452
453
61.0k
    pcolor_component_map->num_components = 1;
454
61.0k
    pcolor_component_map->cspace_id = pcs->id;
455
61.0k
    pcolor_component_map->num_colorants = dev->color_info.num_components;
456
61.0k
    pcolor_component_map->sep_type = pcs->params.separation.sep_type;
457
    /*
458
     * If this is a None or All separation then we do not need to
459
     * use the alternate color space.  Also if the named color
460
     * profile supports the component, don't use the alternate
461
     * tint transform. */
462
61.0k
    if (pcs->params.separation.sep_type != SEP_OTHER ||
463
61.0k
        gsicc_support_named_color(pcs, pgs)) {
464
0
        pcolor_component_map->use_alt_cspace = false;
465
0
        return 0;
466
0
    }
467
    /* If our device is using an additive color model, then we need to
468
     * consider using the alternative color space, as separations are
469
     * generally only used with a subtractive color model. There are
470
     * exceptions, however.
471
     *
472
     * If we don't support devn, then we will certainly have to use the
473
     * alternative color space.
474
     *
475
     * If we are a pdf14 device, and we are doing transparency blending
476
     * in an additive space, we need to keep the spots separated and
477
     * blend them individually as per the PDF specification. Note
478
     * however, if the spot is a CMYK process color and we are doing
479
     * the blend in an additive color space the alternate color space
480
     * is used.  This matches AR.
481
     *
482
     * Otherwise, we will always use the alternate colorspace, unless
483
     * our device specifically claims to be a separation-supporting
484
     * additive device. Possibly all additive devn devices should
485
     * support this now, but I lack to confidence to make this change
486
     * globally. Instead we'll just enable it on a device by device
487
     * basis for now.
488
     *
489
     * This matches logic in check_DeviceN_component_names.
490
     */
491
61.0k
    if (dev->color_info.polarity == GX_CINFO_POLARITY_ADDITIVE) {
492
50.8k
        if (dev_proc(dev, dev_spec_op)(dev, gxdso_supports_devn, NULL, 0) == 0 ||
493
50.8k
            (dev_proc(dev, dev_spec_op)(dev, gxdso_is_pdf14_device, NULL, 0) == 0 &&
494
50.3k
             dev_proc(dev, dev_spec_op)(dev, gxdso_is_sep_supporting_additive_device, NULL, 0) == 0)) {
495
50.3k
            pcolor_component_map->use_alt_cspace = true;
496
50.3k
            return 0;
497
50.3k
        }
498
50.8k
    }
499
500
    /*
501
     * Get the character string and length for the component name.
502
     */
503
10.7k
    pname = (byte *)pcs->params.separation.sep_name;
504
10.7k
    name_size = strlen(pcs->params.separation.sep_name);
505
    /*
506
     * Compare the colorant name to the device's.  If the device's
507
     * compare routine returns GX_DEVICE_COLOR_MAX_COMPONENTS then the
508
     * colorant is in the SeparationNames list but not in the
509
     * SeparationOrder list.
510
     */
511
10.7k
    colorant_number = (*dev_proc(dev, get_color_comp_index))
512
10.7k
                (dev, (const char *)pname, name_size, SEPARATION_NAME);
513
10.7k
    if (colorant_number >= 0 && colorant_number < dev->color_info.max_components) {   /* If valid colorant name */
514
6.98k
        pcolor_component_map->color_map[0] =
515
6.98k
                    (colorant_number == GX_DEVICE_COLOR_MAX_COMPONENTS) ? -1
516
6.98k
                                                           : colorant_number;
517
6.98k
        pcolor_component_map->use_alt_cspace = false;
518
6.98k
    }
519
3.73k
    else
520
3.73k
        pcolor_component_map->use_alt_cspace = true;
521
10.7k
    return 0;
522
61.0k
}
523
524
/* ---------------- Notes on real Separation colors ---------------- */
525
526
typedef ulong gs_separation;  /* BOGUS */
527
528
#define gs_no_separation ((gs_separation)(-1L))
529
530
#define dev_proc_lookup_separation(proc)\
531
  gs_separation proc(gx_device *dev, const byte *sname, uint len,\
532
    gx_color_value *num_levels)
533
534
#define dev_proc_map_tint_color(proc)\
535
  gx_color_index proc(gx_device *dev, gs_separation sepr, bool overprint,\
536
    gx_color_value tint)
537
538
/*
539
 * This next comment is outdated since the Separation color space no longer
540
 * has the multi element cache (lookup table) however the remainder is
541
 * still appropriate.
542
 *
543
 * In principle, setting a Separation color space, or setting the device
544
 * when the current color space is a Separation space, calls the
545
 * lookup_separation device procedure to obtain the separation ID and
546
 * the number of achievable levels.  Currently, the only hooks for doing
547
 * this are unsuitable: gx_set_cmap_procs isn't called when the color
548
 * space changes, and doing it in gx_remap_Separation is inefficient.
549
 * Probably the best approach is to call gx_set_cmap_procs whenever the
550
 * color space changes.  In fact, if we do this, we can probably short-cut
551
 * two levels of procedure call in color remapping (gx_remap_color, by
552
 * turning it into a macro, and gx_remap_DeviceXXX, by calling the
553
 * cmap_proc procedure directly).  Some care will be required for the
554
 * implicit temporary resetting of the color space in [color]image.
555
 */
556
557
/* ---------------- Serialization. -------------------------------- */
558
559
static int
560
gx_serialize_Separation(const gs_color_space * pcs, stream * s)
561
53.6k
{
562
53.6k
    const gs_separation_params * p = &pcs->params.separation;
563
53.6k
    uint n;
564
53.6k
    int code = gx_serialize_cspace_type(pcs, s);
565
566
53.6k
    if (code < 0)
567
0
        return code;
568
53.6k
    code = sputs(s, (const byte *)p->sep_name, strlen(p->sep_name) + 1, &n);
569
53.6k
    if (code < 0)
570
0
        return code;
571
53.6k
    code = cs_serialize(pcs->base_space, s);
572
53.6k
    if (code < 0)
573
0
        return code;
574
53.6k
    code = gx_serialize_device_n_map(pcs, p->map, s);
575
53.6k
    if (code < 0)
576
0
        return code;
577
53.6k
    return sputs(s, (const byte *)&p->sep_type, sizeof(p->sep_type), &n);
578
    /* p->use_alt_cspace isn't a property of the space. */
579
53.6k
}