Coverage Report

Created: 2025-06-24 07:01

/src/ghostpdl/base/gsicc_monitorcm.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
/* gsicc handling for monitoring colors.  Used for detecting gray only pages */
17
18
19
#include <stdlib.h> /* abs() */
20
21
#include "std.h"
22
#include "stdpre.h"
23
#include "gstypes.h"
24
#include "gsmemory.h"
25
#include "gxdevcli.h"
26
#include "gxcspace.h"
27
#include "gsicc_cms.h"
28
#include "gsicc_cache.h"
29
#include "gxcvalue.h"
30
#include "gxdevsop.h"
31
#include "gdevp14.h"
32
#include "string_.h"
33
34
static int gsicc_mcm_transform_general(gx_device *dev, gsicc_link_t *icclink,
35
                                       void *inputcolor, void *outputcolor,
36
                                       int num_bytes_in, int num_bytes_out);
37
38
/* Functions that should be optimized later to do planar/chunky with
39
   color conversions.  Just putting in something that should work
40
   right now */
41
static int
42
gsicc_mcm_planar_to_planar(gx_device *dev, gsicc_link_t *icclink,
43
                                  gsicc_bufferdesc_t *input_buff_desc,
44
                                  gsicc_bufferdesc_t *output_buff_desc,
45
                                  void *inputbuffer, void *outputbuffer)
46
0
{
47
0
    int k, j;
48
0
    byte *inputpos[4];
49
0
    byte *outputpos[4];
50
0
    byte *in_buffer_ptr = (byte *) inputbuffer;
51
0
    byte *out_buffer_ptr = (byte *) outputbuffer;
52
0
    byte in_color[4], out_color[4];
53
0
    int code;
54
55
0
    for (k = 0; k < input_buff_desc->num_chan; k++) {
56
0
        inputpos[k] = in_buffer_ptr + k * input_buff_desc->plane_stride;
57
0
    }
58
0
    for (k = 0; k < output_buff_desc->num_chan; k++) {
59
0
        outputpos[k] = out_buffer_ptr + k * output_buff_desc->plane_stride;
60
0
    }
61
    /* Note to self.  We currently only do this in the transparency buffer
62
       case which has byte representation so just stepping through
63
       plane_stride is ok at this time.  */
64
0
    for (k = 0; k < input_buff_desc->plane_stride ; k++) {
65
0
        for (j = 0; j < input_buff_desc->num_chan; j++) {
66
0
            in_color[j] = *(inputpos[j]);
67
0
            inputpos[j] += input_buff_desc->bytes_per_chan;
68
0
        }
69
0
        code = gsicc_mcm_transform_general(dev, icclink,
70
0
                                           (void*) &(in_color[0]),
71
0
                                           (void*) &(out_color[0]), 1, 1);
72
0
        if (code < 0)
73
0
            return code;
74
0
        for (j = 0; j < output_buff_desc->num_chan; j++) {
75
0
            *(outputpos[j]) = out_color[j];
76
0
            outputpos[j] += output_buff_desc->bytes_per_chan;
77
0
        }
78
0
    }
79
0
    return 0;
80
0
}
81
82
/* This is not really used yet */
83
static int
84
gsicc_mcm_planar_to_chunky(gx_device *dev, gsicc_link_t *icclink,
85
                                  gsicc_bufferdesc_t *input_buff_desc,
86
                                  gsicc_bufferdesc_t *output_buff_desc,
87
                                  void *inputbuffer, void *outputbuffer)
88
0
{
89
0
    return 0;
90
0
}
91
92
/* This is used with the fast thresholding code when doing -dUseFastColor
93
   and going out to a planar device */
94
static int
95
gsicc_mcm_chunky_to_planar(gx_device *dev, gsicc_link_t *icclink,
96
                                  gsicc_bufferdesc_t *input_buff_desc,
97
                                  gsicc_bufferdesc_t *output_buff_desc,
98
                                  void *inputbuffer, void *outputbuffer)
99
0
{
100
0
    int k, j, m;
101
0
    byte *inputpos = (byte *) inputbuffer;
102
0
    byte *outputpos = (byte *) outputbuffer;
103
0
    byte *output_loc;
104
0
    byte *inputcolor;
105
0
    byte outputcolor[8];  /* 8 since we have max 4 colorants and 2 bytes/colorant */
106
0
    unsigned short *pos_in_short, *pos_out_short;
107
0
    int num_bytes_in = input_buff_desc->bytes_per_chan;
108
0
    int num_bytes_out = output_buff_desc->bytes_per_chan;
109
0
    int pixel_in_step = num_bytes_in * input_buff_desc->num_chan;
110
0
    int plane_stride = output_buff_desc->plane_stride;
111
0
    int code;
112
113
    /* Do row by row. */
114
0
    for (k = 0; k < input_buff_desc->num_rows ; k++) {
115
0
        inputcolor = inputpos;
116
0
        output_loc = outputpos;
117
118
        /* split the 2 byte 1 byte case here to avoid decision in inner loop */
119
0
        if (output_buff_desc->bytes_per_chan == 1) {
120
0
            for (j = 0; j < input_buff_desc->pixels_per_row; j++) {
121
0
                code = gsicc_mcm_transform_general(dev, icclink,
122
0
                                                   (void*) inputcolor,
123
0
                                                   (void*) &(outputcolor[0]),
124
0
                                                   num_bytes_in,
125
0
                                                   num_bytes_out);
126
0
                if (code < 0)
127
0
                    return code;
128
                /* Stuff the output in the proper planar location */
129
0
                for (m = 0; m < output_buff_desc->num_chan; m++) {
130
0
                    *(output_loc + m * plane_stride + j) = outputcolor[m];
131
0
                }
132
0
                inputcolor += pixel_in_step;
133
0
            }
134
0
            inputpos += input_buff_desc->row_stride;
135
0
            outputpos += output_buff_desc->row_stride;
136
0
        } else {
137
0
            for (j = 0; j < input_buff_desc->pixels_per_row; j++) {
138
0
                code = gsicc_mcm_transform_general(dev, icclink,
139
0
                                                   (void*) inputcolor,
140
0
                                                   (void*) &(outputcolor[0]),
141
0
                                                   num_bytes_in,
142
0
                                                   num_bytes_out);
143
0
                if (code < 0)
144
0
                    return code;
145
                /* Stuff the output in the proper planar location */
146
0
                pos_in_short = (unsigned short*) &(outputcolor[0]);
147
0
                pos_out_short = (unsigned short*) (output_loc);
148
0
                for (m = 0; m < output_buff_desc->num_chan; m++) {
149
0
                    *(pos_out_short + m * plane_stride + j) = pos_in_short[m];
150
0
                }
151
0
                inputcolor += pixel_in_step;
152
0
            }
153
0
            inputpos += input_buff_desc->row_stride;
154
0
            outputpos += output_buff_desc->row_stride;
155
0
        }
156
0
    }
157
0
    return 0;
158
0
}
159
160
static int
161
gsicc_mcm_chunky_to_chunky(gx_device *dev, gsicc_link_t *icclink,
162
                                  gsicc_bufferdesc_t *input_buff_desc,
163
                                  gsicc_bufferdesc_t *output_buff_desc,
164
                                  void *inputbuffer, void *outputbuffer)
165
0
{
166
0
    int k, j;
167
0
    byte *inputpos = (byte *) inputbuffer;
168
0
    byte *outputpos = (byte *) outputbuffer;
169
0
    byte *inputcolor, *outputcolor;
170
0
    int num_bytes_in = input_buff_desc->bytes_per_chan;
171
0
    int num_bytes_out = output_buff_desc->bytes_per_chan;
172
0
    int pixel_in_step = num_bytes_in * input_buff_desc->num_chan;
173
0
    int pixel_out_step = num_bytes_out * output_buff_desc->num_chan;
174
0
    int code;
175
176
    /* Do row by row. */
177
0
    for (k = 0; k < input_buff_desc->num_rows ; k++) {
178
0
        inputcolor = inputpos;
179
0
        outputcolor = outputpos;
180
0
        for (j = 0; j < input_buff_desc->pixels_per_row; j++) {
181
0
            code = gsicc_mcm_transform_general(dev, icclink,
182
0
                                               (void*) inputcolor,
183
0
                                               (void*) outputcolor,
184
0
                                               num_bytes_in,
185
0
                                               num_bytes_out);
186
0
            if (code < 0)
187
0
                return code;
188
0
            inputcolor += pixel_in_step;
189
0
            outputcolor += pixel_out_step;
190
0
        }
191
0
        inputpos += input_buff_desc->row_stride;
192
0
        outputpos += output_buff_desc->row_stride;
193
0
    }
194
0
    return 0;
195
0
}
196
197
/* Transform an entire buffer monitoring and transforming the colors */
198
static int
199
gsicc_mcm_transform_color_buffer(gx_device *dev, gsicc_link_t *icclink,
200
                                  gsicc_bufferdesc_t *input_buff_desc,
201
                                  gsicc_bufferdesc_t *output_buff_desc,
202
                                  void *inputbuffer, void *outputbuffer)
203
0
{
204
0
    if (input_buff_desc->is_planar) {
205
0
        if (output_buff_desc->is_planar) {
206
0
            return gsicc_mcm_planar_to_planar(dev, icclink, input_buff_desc,
207
0
                                              output_buff_desc, inputbuffer,
208
0
                                              outputbuffer);
209
0
        } else {
210
0
            return gsicc_mcm_planar_to_chunky(dev, icclink, input_buff_desc,
211
0
                                              output_buff_desc, inputbuffer,
212
0
                                              outputbuffer);
213
0
        }
214
0
    } else {
215
0
        if (output_buff_desc->is_planar) {
216
0
            return gsicc_mcm_chunky_to_planar(dev, icclink, input_buff_desc,
217
0
                                              output_buff_desc, inputbuffer,
218
0
                                              outputbuffer);
219
0
        } else {
220
0
            return gsicc_mcm_chunky_to_chunky(dev, icclink, input_buff_desc,
221
0
                                              output_buff_desc, inputbuffer,
222
0
                                              outputbuffer);
223
0
        }
224
0
    }
225
0
}
226
227
/* This is where we do the monitoring and the conversion if needed */
228
static int
229
gsicc_mcm_transform_general(gx_device *dev, gsicc_link_t *icclink,
230
                             void *inputcolor, void *outputcolor,
231
                             int num_bytes_in, int num_bytes_out)
232
0
{
233
0
    bool is_neutral = false;
234
0
    unsigned short outputcolor_cm[GX_DEVICE_COLOR_MAX_COMPONENTS];
235
0
    void *outputcolor_cm_ptr = (void*) outputcolor_cm;
236
0
    cmm_dev_profile_t *dev_profile;
237
0
    int code;
238
0
    int k;
239
240
0
    code = dev_proc(dev, get_profile)(dev, &dev_profile);
241
0
    if (code < 0)
242
0
        return code;
243
244
    /* Monitor only if gray detection is still true */
245
0
    if (dev_profile->pageneutralcolor)
246
0
        is_neutral = icclink->procs.is_color(inputcolor, num_bytes_in);
247
248
    /* Color found turn off gray detection */
249
0
    if (!is_neutral)
250
0
        dev_profile->pageneutralcolor = false;
251
252
    /* Reset all links so that they will no longer monitor.  This one will finish
253
       the buffer but we will not have any additional ones */
254
0
    if (!dev_profile->pageneutralcolor)
255
0
    {
256
0
        code = gsicc_mcm_end_monitor(icclink->icc_link_cache, dev);
257
0
        if (code < 0)
258
0
            return code;
259
0
    }
260
261
    /* Now apply the color transform using the original color procs, but don't
262
       do this if we had the identity.  We also have to worry about 8 and 16 bit
263
       depth */
264
0
    if (icclink->hashcode.des_hash == icclink->hashcode.src_hash) {
265
0
        if (num_bytes_in == num_bytes_out) {
266
            /* The easy case */
267
0
            memcpy(outputcolor, inputcolor, (size_t)num_bytes_in * icclink->num_input);
268
0
        } else {
269
0
            if (num_bytes_in == 2) {
270
0
                unsigned short *in_ptr = (unsigned short*) inputcolor;
271
0
                byte *out_ptr = (byte*) outputcolor;
272
0
                for (k = 0; k < icclink->num_input; k++) {
273
0
                    out_ptr[k] = gx_color_value_to_byte(in_ptr[k]);
274
0
                }
275
0
            } else {
276
0
                byte *in_ptr = (byte*) inputcolor;
277
0
                unsigned short *out_ptr = (unsigned short*) outputcolor;
278
0
                for (k = 0; k < icclink->num_input; k++) {
279
0
                    out_ptr[k] = gx_color_value_to_byte(in_ptr[k]);
280
0
                }
281
0
            }
282
0
        }
283
0
    } else {
284
0
        if (num_bytes_in == num_bytes_out) {
285
0
            icclink->orig_procs.map_color(dev, icclink, inputcolor, outputcolor,
286
0
                                          num_bytes_in);
287
0
        } else {
288
0
            icclink->orig_procs.map_color(dev, icclink, inputcolor, outputcolor_cm_ptr,
289
0
                                          num_bytes_in);
290
0
            if (num_bytes_in == 2) {
291
0
                unsigned short *in_ptr = (unsigned short*) outputcolor_cm_ptr;
292
0
                byte *out_ptr = (byte*) outputcolor;
293
0
                for (k = 0; k < icclink->num_input; k++) {
294
0
                    out_ptr[k] = gx_color_value_to_byte(in_ptr[k]);
295
0
                }
296
0
            } else {
297
0
                byte *in_ptr = (byte*) outputcolor_cm_ptr;
298
0
                unsigned short *out_ptr = (unsigned short*) outputcolor;
299
0
                for (k = 0; k < icclink->num_input; k++) {
300
0
                    out_ptr[k] = gx_color_value_to_byte(in_ptr[k]);
301
0
                }
302
0
            }
303
0
        }
304
0
    }
305
0
    return 0;
306
0
}
307
308
/* Monitor for color */
309
static int
310
gsicc_mcm_transform_color(gx_device *dev, gsicc_link_t *icclink, void *inputcolor,
311
                           void *outputcolor, int num_bytes)
312
0
{
313
0
    return gsicc_mcm_transform_general(dev, icclink, inputcolor, outputcolor,
314
0
                                       num_bytes, num_bytes);
315
0
}
316
317
bool gsicc_mcm_monitor_rgb(void *inputcolor, int num_bytes)
318
0
{
319
0
    if (num_bytes == 1) {
320
0
        byte *rgb_val = (byte*) inputcolor;
321
0
        int rg_diff = (int) abs((int) rgb_val[0] - (int) rgb_val[1]);
322
0
        int rb_diff = (int) abs((int) rgb_val[0] - (int) rgb_val[2]);
323
0
        int bg_diff = (int) abs((int) rgb_val[1] - (int) rgb_val[2]);
324
0
        return (rg_diff < DEV_NEUTRAL_8 && rb_diff < DEV_NEUTRAL_8
325
0
                && bg_diff < DEV_NEUTRAL_8);
326
0
    } else {
327
0
        unsigned short *rgb_val = (unsigned short*) inputcolor;
328
0
        int rg_diff = (int) abs((int) rgb_val[0] - (int) rgb_val[1]);
329
0
        int rb_diff = (int) abs((int) rgb_val[0] - (int) rgb_val[2]);
330
0
        int bg_diff = (int) abs((int) rgb_val[1] - (int) rgb_val[2]);
331
0
        return (rg_diff < DEV_NEUTRAL_16 && rb_diff < DEV_NEUTRAL_16
332
0
                && bg_diff < DEV_NEUTRAL_16);
333
0
    }
334
0
}
335
336
bool gsicc_mcm_monitor_cmyk(void *inputcolor, int num_bytes)
337
0
{
338
#ifdef PESSIMISTIC_CMYK_NEUTRAL
339
    if (num_bytes == 1) {
340
        byte *cmyk = (byte*) inputcolor;
341
        return (cmyk[0] < DEV_NEUTRAL_8 && cmyk[1] < DEV_NEUTRAL_8
342
                && cmyk[2] < DEV_NEUTRAL_8);
343
    } else {
344
        unsigned short *cmyk = (unsigned short*) inputcolor;
345
        return (cmyk[0] < DEV_NEUTRAL_16 && cmyk[1] < DEV_NEUTRAL_16
346
                && cmyk[2] < DEV_NEUTRAL_16);
347
    }
348
#else /* The code below is more similar to RGB in that C = M = Y (or close) */
349
      /* will be treated as neutral.                                        */
350
0
    if (num_bytes == 1) {
351
0
        byte *cmyk = (byte*) inputcolor;
352
0
        int cm_diff = (int) abs((int) cmyk[0] - (int) cmyk[1]);
353
0
        int cy_diff = (int) abs((int) cmyk[0] - (int) cmyk[2]);
354
0
        int my_diff = (int) abs((int) cmyk[1] - (int) cmyk[2]);
355
0
        return (cm_diff < DEV_NEUTRAL_8 && cy_diff < DEV_NEUTRAL_8
356
0
                && my_diff < DEV_NEUTRAL_8);
357
0
    } else {
358
0
        unsigned short *cmyk = (unsigned short*) inputcolor;
359
0
        int cm_diff = (int) abs((int) cmyk[0] - (int) cmyk[1]);
360
0
        int cy_diff = (int) abs((int) cmyk[0] - (int) cmyk[2]);
361
0
        int my_diff = (int) abs((int) cmyk[1] - (int) cmyk[2]);
362
0
        return (cm_diff < DEV_NEUTRAL_16 && cy_diff < DEV_NEUTRAL_16
363
0
                && my_diff < DEV_NEUTRAL_16);
364
0
    }
365
0
#endif
366
0
}
367
368
bool gsicc_mcm_monitor_lab(void *inputcolor, int num_bytes)
369
0
{
370
0
    if (num_bytes == 1) {
371
0
        byte *lab = (byte*) inputcolor;
372
0
        int diffa = (int) abs((int) lab[1] - (int) 0x80);
373
0
        int diffb = (int) abs((int) lab[2] - (int) 0x80);
374
0
        return (diffa < AB_NEUTRAL_8 && diffb < AB_NEUTRAL_8);
375
0
    } else {
376
0
        unsigned short *lab = (unsigned short*) inputcolor;
377
0
        int diffa = (int) abs((int) lab[1] - (int) 0x8000);
378
0
        int diffb = (int) abs((int) lab[2] - (int) 0x8000);
379
0
        return (diffa < AB_NEUTRAL_16 && diffb < AB_NEUTRAL_16);
380
0
    }
381
0
}
382
383
/* Set the link up to monitor */
384
void
385
gsicc_mcm_set_link(gsicc_link_t* link)
386
0
{
387
0
    link->orig_procs = link->procs;
388
0
    link->is_monitored = true;
389
0
    link->is_identity = false;
390
391
0
    link->procs.map_buffer = gsicc_mcm_transform_color_buffer;
392
0
    link->procs.map_color = gsicc_mcm_transform_color;
393
394
0
    switch (link->data_cs) {
395
0
        case gsRGB:
396
0
            link->procs.is_color = gsicc_mcm_monitor_rgb;
397
0
            break;
398
0
        case gsCIELAB:
399
0
            link->procs.is_color = gsicc_mcm_monitor_lab;
400
0
            break;
401
0
        case gsCMYK:
402
0
            link->procs.is_color = gsicc_mcm_monitor_cmyk;
403
0
            break;
404
0
        default:
405
0
            break;
406
0
    }
407
0
}
408
409
/* This gets rid of the monitoring */
410
int
411
gsicc_mcm_end_monitor(gsicc_link_cache_t *cache, gx_device *dev)
412
0
{
413
0
    gx_monitor_t *lock = cache->lock;
414
0
    gsicc_link_t *curr;
415
0
    int code;
416
0
    cmm_dev_profile_t *dev_profile;
417
418
419
    /* Get the device profile */
420
0
    code = dev_proc(dev, get_profile)(dev, &dev_profile);
421
0
    if (code < 0)
422
0
        return code;
423
0
    dev_profile->pageneutralcolor = false;
424
    /* If this device is a pdf14 device, then we may need to take care of the
425
       profile in the target device also.  This is a special case since the
426
       pdf14 device has its own profile different from the target device */
427
0
    if (dev_proc(dev, dev_spec_op)(dev, gxdso_is_pdf14_device, NULL, 0) > 0) {
428
0
        gs_pdf14_device_color_mon_set(dev, false);
429
0
    }
430
431
    /* Lock the cache as we remove monitoring from the links */
432
0
    gx_monitor_enter(lock);
433
0
    curr = cache->head;
434
0
    while (curr != NULL ) {
435
0
        if (curr->is_monitored) {
436
0
            curr->procs = curr->orig_procs;
437
0
            if (curr->hashcode.des_hash == curr->hashcode.src_hash)
438
0
                curr->is_identity = true;
439
0
            curr->is_monitored = false;
440
0
        }
441
        /* Now release any tasks/threads waiting for these contents */
442
0
        gx_monitor_leave(curr->lock);
443
0
        curr = curr->next;
444
0
    }
445
0
    gx_monitor_leave(lock); /* done with updating, let everyone run */
446
0
    return 0;
447
0
}
448
449
/* Conversely to the above, this gets restores monitoring, needed after
450
 * monitoring was turned off above (for the next page)
451
*/
452
int
453
gsicc_mcm_begin_monitor(gsicc_link_cache_t *cache, gx_device *dev)
454
0
{
455
0
    gx_monitor_t *lock = cache->lock;
456
0
    gsicc_link_t *curr;
457
0
    int code;
458
0
    cmm_dev_profile_t *dev_profile;
459
460
    /* Get the device profile */
461
0
    code = dev_proc(dev, get_profile)(dev, &dev_profile);
462
0
    if (code < 0)
463
0
        return code;
464
0
    dev_profile->pageneutralcolor = true;
465
    /* If this device is a pdf14 device, then we may need to take care of the
466
       profile in the target device also.  This is a special case since the
467
       pdf14 device has its own profile different from the target device */
468
0
    if (dev_proc(dev, dev_spec_op)(dev, gxdso_is_pdf14_device, NULL, 0) > 0) {
469
0
        gs_pdf14_device_color_mon_set(dev, true);
470
0
    }
471
472
    /* Lock the cache as we remove monitoring from the links */
473
0
    gx_monitor_enter(lock);
474
475
0
    curr = cache->head;
476
0
    while (curr != NULL ) {
477
0
        if (curr->data_cs != gsGRAY) {
478
0
            gsicc_mcm_set_link(curr);
479
            /* Now release any tasks/threads waiting for these contents */
480
0
            gx_monitor_leave(curr->lock);
481
0
        }
482
0
        curr = curr->next;
483
0
    }
484
0
    gx_monitor_leave(lock); /* done with updating, let everyone run */
485
0
    return 0;
486
0
}