Coverage Report

Created: 2025-06-24 07:01

/src/ghostpdl/base/gsicc_replacecm.c
Line
Count
Source (jump to first uncovered line)
1
/* Copyright (C) 2001-2023 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
/* gsicc handling for direct color replacement. */
18
19
#include "std.h"
20
#include "string_.h"
21
#include "stdpre.h"
22
#include "gstypes.h"
23
#include "gsmemory.h"
24
#include "gsstruct.h"
25
#include "scommon.h"
26
#include "strmio.h"
27
#include "gx.h"
28
#include "gxgstate.h"
29
#include "gxcspace.h"
30
#include "gsicc_cms.h"
31
#include "gsicc_cache.h"
32
33
/* A link structure for our replace color transform */
34
typedef struct rcm_link_s {
35
    byte num_out;
36
    byte num_in;
37
    gsicc_colorbuffer_t data_cs_in;
38
    gs_memory_t *memory;
39
    gx_cm_color_map_procs cm_procs;  /* for the demo */
40
    const gx_device *cmdev;
41
    void *context;  /* For a table and or a set of procs */
42
} rcm_link_t;
43
44
static void gsicc_rcm_transform_general(const gx_device *dev, gsicc_link_t *icclink,
45
                                         void *inputcolor, void *outputcolor,
46
                                         int num_bytes_in, int num_bytes_out);
47
48
/* Functions that should be optimized later to do planar/chunky with
49
   color conversions.  Just putting in something that should work
50
   right now */
51
static void
52
gsicc_rcm_planar_to_planar(const gx_device *dev, gsicc_link_t *icclink,
53
                                  gsicc_bufferdesc_t *input_buff_desc,
54
                                  gsicc_bufferdesc_t *output_buff_desc,
55
                                  void *inputbuffer, void *outputbuffer)
56
0
{
57
0
    int k, j;
58
0
    byte *inputpos[4];
59
0
    byte *outputpos[4];
60
0
    byte *in_buffer_ptr = (byte *) inputbuffer;
61
0
    byte *out_buffer_ptr = (byte *) outputbuffer;
62
0
    byte in_color[4], out_color[4];
63
64
0
    for (k = 0; k < input_buff_desc->num_chan; k++) {
65
0
        inputpos[k] = in_buffer_ptr + k * input_buff_desc->plane_stride;
66
0
    }
67
0
    for (k = 0; k < output_buff_desc->num_chan; k++) {
68
0
        outputpos[k] = out_buffer_ptr + k * output_buff_desc->plane_stride;
69
0
    }
70
    /* Note to self.  We currently only do this in the transparency buffer
71
       case which has byte representation so just stepping through
72
       plane_stride is ok at this time.  */
73
0
    for (k = 0; k < input_buff_desc->plane_stride ; k++) {
74
0
        for (j = 0; j < input_buff_desc->num_chan; j++) {
75
0
            in_color[j] = *(inputpos[j]);
76
0
            inputpos[j] += input_buff_desc->bytes_per_chan;
77
0
        }
78
0
        gsicc_rcm_transform_general(dev, icclink, (void*) &(in_color[0]),
79
0
                                         (void*) &(out_color[0]), 1, 1);
80
0
        for (j = 0; j < output_buff_desc->num_chan; j++) {
81
0
            *(outputpos[j]) = out_color[j];
82
0
            outputpos[j] += output_buff_desc->bytes_per_chan;
83
0
        }
84
0
    }
85
0
}
86
87
/* This is not really used yet */
88
static void
89
gsicc_rcm_planar_to_chunky(const gx_device *dev, gsicc_link_t *icclink,
90
                                  gsicc_bufferdesc_t *input_buff_desc,
91
                                  gsicc_bufferdesc_t *output_buff_desc,
92
                                  void *inputbuffer, void *outputbuffer)
93
0
{
94
95
96
0
}
97
98
/* This is used with the fast thresholding code when doing -dUseFastColor
99
   and going out to a planar device */
100
static void
101
gsicc_rcm_chunky_to_planar(const gx_device *dev, gsicc_link_t *icclink,
102
                                  gsicc_bufferdesc_t *input_buff_desc,
103
                                  gsicc_bufferdesc_t *output_buff_desc,
104
                                  void *inputbuffer, void *outputbuffer)
105
0
{
106
0
    int k, j, m;
107
0
    byte *inputpos = (byte *) inputbuffer;
108
0
    byte *outputpos = (byte *) outputbuffer;
109
0
    byte *output_loc;
110
0
    byte *inputcolor;
111
0
    byte outputcolor[8];  /* 8 since we have max 4 colorants and 2 bytes/colorant */
112
0
    unsigned short *pos_in_short, *pos_out_short;
113
0
    int num_bytes_in = input_buff_desc->bytes_per_chan;
114
0
    int num_bytes_out = output_buff_desc->bytes_per_chan;
115
0
    int pixel_in_step = num_bytes_in * input_buff_desc->num_chan;
116
0
    int plane_stride = output_buff_desc->plane_stride;
117
118
    /* Do row by row. */
119
0
    for (k = 0; k < input_buff_desc->num_rows ; k++) {
120
0
        inputcolor = inputpos;
121
0
        output_loc = outputpos;
122
123
        /* split the 2 byte 1 byte case here to avoid decision in inner loop */
124
0
        if (output_buff_desc->bytes_per_chan == 1) {
125
0
            for (j = 0; j < input_buff_desc->pixels_per_row; j++) {
126
0
                gsicc_rcm_transform_general(dev, icclink, (void*) inputcolor,
127
0
                                             (void*) &(outputcolor[0]), num_bytes_in,
128
0
                                              num_bytes_out);
129
                /* Stuff the output in the proper planar location */
130
0
                for (m = 0; m < output_buff_desc->num_chan; m++) {
131
0
                    *(output_loc + m * plane_stride + j) = outputcolor[m];
132
0
                }
133
0
                inputcolor += pixel_in_step;
134
0
            }
135
0
            inputpos += input_buff_desc->row_stride;
136
0
            outputpos += output_buff_desc->row_stride;
137
0
        } else {
138
0
            for (j = 0; j < input_buff_desc->pixels_per_row; j++) {
139
0
                gsicc_rcm_transform_general(dev, icclink, (void*) inputcolor,
140
0
                                             (void*) &(outputcolor[0]), num_bytes_in,
141
0
                                              num_bytes_out);
142
                /* Stuff the output in the proper planar location */
143
0
                pos_in_short = (unsigned short*) &(outputcolor[0]);
144
0
                pos_out_short = (unsigned short*) (output_loc);
145
0
                for (m = 0; m < output_buff_desc->num_chan; m++) {
146
0
                    *(pos_out_short + m * plane_stride + j) = pos_in_short[m];
147
0
                }
148
0
                inputcolor += pixel_in_step;
149
0
            }
150
0
            inputpos += input_buff_desc->row_stride;
151
0
            outputpos += output_buff_desc->row_stride;
152
0
        }
153
0
    }
154
0
}
155
156
static void
157
gsicc_rcm_chunky_to_chunky(const gx_device *dev, gsicc_link_t *icclink,
158
                                  gsicc_bufferdesc_t *input_buff_desc,
159
                                  gsicc_bufferdesc_t *output_buff_desc,
160
                                  void *inputbuffer, void *outputbuffer)
161
0
{
162
0
    int k, j;
163
0
    byte *inputpos = (byte *) inputbuffer;
164
0
    byte *outputpos = (byte *) outputbuffer;
165
0
    byte *inputcolor, *outputcolor;
166
0
    int num_bytes_in = input_buff_desc->bytes_per_chan;
167
0
    int num_bytes_out = output_buff_desc->bytes_per_chan;
168
0
    int pixel_in_step = num_bytes_in * input_buff_desc->num_chan;
169
0
    int pixel_out_step = num_bytes_out * output_buff_desc->num_chan;
170
171
    /* Do row by row. */
172
0
    for (k = 0; k < input_buff_desc->num_rows ; k++) {
173
0
        inputcolor = inputpos;
174
0
        outputcolor = outputpos;
175
0
        for (j = 0; j < input_buff_desc->pixels_per_row; j++) {
176
0
            gsicc_rcm_transform_general(dev, icclink, (void*) inputcolor,
177
0
                                         (void*) outputcolor, num_bytes_in,
178
0
                                          num_bytes_out);
179
0
            inputcolor += pixel_in_step;
180
0
            outputcolor += pixel_out_step;
181
0
        }
182
0
        inputpos += input_buff_desc->row_stride;
183
0
        outputpos += output_buff_desc->row_stride;
184
0
    }
185
0
}
186
187
/* Transform an entire buffer using replacement method */
188
static int
189
gsicc_rcm_transform_color_buffer(gx_device *dev, gsicc_link_t *icclink,
190
                                  gsicc_bufferdesc_t *input_buff_desc,
191
                                  gsicc_bufferdesc_t *output_buff_desc,
192
                                  void *inputbuffer, void *outputbuffer)
193
0
{
194
    /* Since we have to do the mappings to and from frac colors we will for
195
       now just call the gsicc_rcm_transform_color as we step through the
196
       buffers.  This process can be significantly sped up */
197
198
0
    if (input_buff_desc->is_planar) {
199
0
        if (output_buff_desc->is_planar) {
200
0
            gsicc_rcm_planar_to_planar(dev, icclink, input_buff_desc,
201
0
                                        output_buff_desc, inputbuffer,
202
0
                                        outputbuffer);
203
0
        } else {
204
0
            gsicc_rcm_planar_to_chunky(dev, icclink, input_buff_desc,
205
0
                                        output_buff_desc, inputbuffer,
206
0
                                        outputbuffer);
207
0
        }
208
0
    } else {
209
0
        if (output_buff_desc->is_planar) {
210
0
            gsicc_rcm_chunky_to_planar(dev, icclink, input_buff_desc,
211
0
                                        output_buff_desc, inputbuffer,
212
0
                                        outputbuffer);
213
0
        } else {
214
0
            gsicc_rcm_chunky_to_chunky(dev, icclink, input_buff_desc,
215
0
                                        output_buff_desc, inputbuffer,
216
0
                                        outputbuffer);
217
0
        }
218
0
    }
219
0
    return 0;
220
0
}
221
222
/* Shared function between the single and buffer conversions.  This is where
223
   we do the actual replacement.   For now, we make the replacement a
224
   negative to show the effect of what using color replacement.  We also use
225
   the device procs to map to the device value.  */
226
static void
227
gsicc_rcm_transform_general(const gx_device *dev, gsicc_link_t *icclink,
228
                             void *inputcolor, void *outputcolor,
229
                             int num_bytes_in, int num_bytes_out)
230
0
{
231
    /* Input data is either single byte or 2 byte color values.  */
232
0
    rcm_link_t *link = (rcm_link_t*) icclink->link_handle;
233
0
    byte num_in = link->num_in;
234
0
    byte num_out = link->num_out;
235
0
    frac frac_in[4];
236
0
    frac frac_out[GX_DEVICE_COLOR_MAX_COMPONENTS];
237
0
    int k;
238
239
    /* Make the negative for the demo.... */
240
0
    if (num_bytes_in == 2) {
241
0
        unsigned short *data = (unsigned short *) inputcolor;
242
0
        for (k = 0; k < num_in; k++) {
243
0
            frac_in[k] = frac_1 - ushort2frac(data[k]);
244
0
        }
245
0
    } else {
246
0
        byte *data = (byte *) inputcolor;
247
0
        for (k = 0; k < num_in; k++) {
248
0
            frac_in[k] = frac_1 - byte2frac(data[k]);
249
0
        }
250
0
    }
251
    /* Use the device procedure */
252
0
    switch (num_in) {
253
0
        case 1:
254
0
            (link->cm_procs.map_gray)(link->cmdev, frac_in[0], frac_out);
255
0
            break;
256
0
        case 3:
257
0
            (link->cm_procs.map_rgb)(link->cmdev, NULL, frac_in[0], frac_in[1],
258
0
                                 frac_in[2], frac_out);
259
0
            break;
260
0
        case 4:
261
0
            (link->cm_procs.map_cmyk)(link->cmdev, frac_in[0], frac_in[1], frac_in[2],
262
0
                                 frac_in[3], frac_out);
263
0
            break;
264
0
        default:
265
0
            memset(&(frac_out[0]), 0, sizeof(frac_out));
266
0
            break;
267
0
    }
268
0
    if (num_bytes_out == 2) {
269
0
        unsigned short *data = (unsigned short *) outputcolor;
270
0
        for (k = 0; k < num_out; k++) {
271
0
            data[k] = frac2ushort(frac_out[k]);
272
0
        }
273
0
    } else {
274
0
        byte *data = (byte *) outputcolor;
275
0
        for (k = 0; k < num_out; k++) {
276
0
            data[k] = frac2byte(frac_out[k]);
277
0
        }
278
0
    }
279
0
    return;
280
0
}
281
282
/* Transform a single color using the generic (non color managed)
283
   transformations */
284
static int
285
gsicc_rcm_transform_color(gx_device *dev, gsicc_link_t *icclink, void *inputcolor,
286
                           void *outputcolor, int num_bytes)
287
0
{
288
0
    gsicc_rcm_transform_general(dev, icclink, inputcolor, outputcolor,
289
0
                                 num_bytes, num_bytes);
290
0
    return 0;
291
0
}
292
293
static void
294
gsicc_rcm_freelink(gsicc_link_t *icclink)
295
0
{
296
0
    rcm_link_t *rcm_link = (rcm_link_t*) icclink->link_handle;
297
0
    if (rcm_link != NULL)
298
0
        gs_free_object(rcm_link->memory, rcm_link, "gsicc_rcm_freelink");
299
0
    icclink->link_handle = NULL;
300
0
}
301
302
/* Get the replacement color management link.  It basically needs to store
303
   the number of components for the source so that we know what we are
304
   coming from (e.g. RGB, CMYK, Gray) */
305
gsicc_link_t*
306
gsicc_rcm_get_link(const gs_gstate *pgs, gx_device *dev,
307
                   gsicc_colorbuffer_t data_cs)
308
0
{
309
0
    gsicc_link_t *result;
310
0
    gsicc_hashlink_t hash;
311
0
    rcm_link_t *rcm_link;
312
0
    gs_memory_t *mem;
313
0
    const gx_cm_color_map_procs * cm_procs;
314
0
    const gx_device *cmdev;
315
0
    bool pageneutralcolor = false;
316
0
    cmm_dev_profile_t *dev_profile;
317
0
    int code;
318
319
0
    if (dev == NULL)
320
0
        return NULL;
321
322
0
    mem = dev->memory->non_gc_memory;
323
    /* Need to check if we need to monitor for color */
324
0
    code = dev_proc(dev, get_profile)(dev,  &dev_profile);
325
0
    if (code < 0)
326
0
        return NULL;
327
0
    if (dev_profile != NULL) {
328
0
        pageneutralcolor = dev_profile->pageneutralcolor;
329
0
    }
330
331
0
    cm_procs = dev_proc(dev, get_color_mapping_procs)(dev, &cmdev);
332
333
0
    hash.rend_hash = gsCMM_REPLACE;
334
0
    hash.des_hash = dev->color_info.num_components;
335
0
    hash.src_hash = data_cs;
336
0
    hash.link_hashcode = data_cs + hash.des_hash * 256 + hash.rend_hash * 4096;
337
338
    /* Check the cache for a hit. */
339
0
    result = gsicc_findcachelink(hash, pgs->icc_link_cache, false, false);
340
0
    if (result != NULL) {
341
0
        return result;
342
0
    }
343
    /* If not, then lets create a new one.  This may actually return a link if
344
       another thread has already created it while we were trying to do so */
345
0
    if (gsicc_alloc_link_entry(pgs->icc_link_cache, &result, hash, false, false))
346
0
        return result;
347
348
0
    if (result == NULL)
349
0
        return result;
350
351
    /* Now compute the link contents */
352
    /* We (this thread) owns this link, so we can update it */
353
0
    result->procs.map_buffer = gsicc_rcm_transform_color_buffer;
354
0
    result->procs.map_color = gsicc_rcm_transform_color;
355
0
    result->procs.free_link = gsicc_rcm_freelink;
356
0
    result->hashcode = hash;
357
0
    result->is_identity = false;
358
0
    rcm_link = (rcm_link_t *) gs_alloc_bytes(mem, sizeof(rcm_link_t),
359
0
                                               "gsicc_rcm_get_link");
360
0
    if (rcm_link == NULL)
361
0
        return NULL;
362
0
    result->link_handle = (void*) rcm_link;
363
0
    rcm_link->memory = mem;
364
0
    rcm_link->num_out = min(dev->color_info.num_components,
365
0
                             GS_CLIENT_COLOR_MAX_COMPONENTS);
366
0
    rcm_link->data_cs_in = data_cs;
367
0
    rcm_link->cm_procs.map_cmyk = cm_procs->map_cmyk;
368
0
    rcm_link->cm_procs.map_rgb = cm_procs->map_rgb;
369
0
    rcm_link->cm_procs.map_gray = cm_procs->map_gray;
370
0
    rcm_link->cmdev = cmdev;
371
372
0
    switch (data_cs) {
373
0
        case gsGRAY:
374
0
            rcm_link->num_in = 1;
375
0
            break;
376
0
        case gsRGB:
377
0
        case gsCIELAB:
378
0
            rcm_link->num_in = 3;
379
0
            break;
380
0
        case gsCMYK:
381
0
            rcm_link->num_in = 4;
382
0
            break;
383
0
        default:
384
0
            result->procs.free_link(result);
385
0
            return NULL;
386
0
    }
387
    /* Likely set if we have something like a table or procs */
388
0
    rcm_link->context = NULL;
389
390
0
    result->num_input = rcm_link->num_in;
391
0
    result->num_output = rcm_link->num_out;
392
0
    result->link_handle = rcm_link;
393
0
    result->hashcode.link_hashcode = hash.link_hashcode;
394
0
    result->hashcode.des_hash = hash.des_hash;
395
0
    result->hashcode.src_hash = hash.src_hash;
396
0
    result->hashcode.rend_hash = hash.rend_hash;
397
0
    result->includes_softproof = false;
398
0
    result->includes_devlink = false;
399
0
    result->is_identity = false;  /* Always do replacement for demo */
400
401
    /* Set up for monitoring non gray color spaces */
402
0
    if (pageneutralcolor && data_cs != gsGRAY)
403
0
        gsicc_mcm_set_link(result);
404
405
0
    result->valid = true;
406
    /* Now release any tasks/threads waiting for these contents by unlocking it */
407
0
    gx_monitor_leave(result->lock); /* done with updating, let everyone run */
408
409
0
    return result;
410
0
}