Coverage Report

Created: 2026-08-08 08:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ghostpdl/base/gscicach.c
Line
Count
Source
1
/* Copyright (C) 2001-2025 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
/* A color index cache. */
18
#include "gx.h"
19
#include "gserrors.h"
20
#include "gsccolor.h"
21
#include "gxcspace.h"
22
#include "gxdcolor.h"
23
#include "gscicach.h"
24
#include "memory_.h"
25
26
72.8M
#define COLOR_INDEX_CACHE_SIZE 256
27
55.2M
#define COLOR_INDEX_CACHE_CHAINS (COLOR_INDEX_CACHE_SIZE / 16)
28
29
typedef struct gs_color_index_cache_elem_s gs_color_index_cache_elem_t;
30
31
struct gs_color_index_cache_elem_s {
32
    union _color {
33
      gx_color_index cindex;
34
      ushort devn[GS_CLIENT_COLOR_MAX_COMPONENTS];
35
    } color;
36
    gx_device_color_type color_type;
37
    uint chain;
38
    uint prev, next; /* NULL for unused. */
39
    uint touch_prev, touch_next;
40
    bool frac_values_done;
41
};
42
43
struct gs_color_index_cache_s {
44
    const gs_color_space *direct_space;
45
    gs_gstate *pgs;
46
    gx_device *dev;
47
    gx_device *trans_dev;
48
    int client_num_components;
49
    int device_num_components;
50
    gs_memory_t *memory;
51
    int used;
52
    gs_color_index_cache_elem_t *buf;
53
    uint recent_touch;
54
    float *paint_values;
55
    frac31 *frac_values;
56
    int chains[COLOR_INDEX_CACHE_CHAINS];
57
    /* Note : the 0th element of buf, paint_values, frac_values is never used,
58
       because we consider the index 0 as NULL
59
       just for a faster initialization. */
60
113M
#   define MYNULL 0
61
};
62
63
gs_private_st_ptrs6(st_color_index_cache, gs_color_index_cache_t, "gs_color_index_cache_t",
64
                    gs_color_index_cache_elem_ptrs, gs_color_index_cache_reloc_ptrs,
65
                    direct_space, memory, buf, paint_values, frac_values, trans_dev);
66
67
gs_color_index_cache_t *
68
gs_color_index_cache_create(gs_memory_t *memory, const gs_color_space *direct_space, gx_device *dev,
69
                            gs_gstate *pgs, bool need_frac, gx_device *trans_dev)
70
24.3k
{
71
24.3k
    size_t client_num_components = cs_num_components(direct_space);
72
24.3k
    size_t device_num_components = trans_dev->color_info.num_components;
73
24.3k
    gs_color_index_cache_elem_t *buf = ( gs_color_index_cache_elem_t *)gs_alloc_byte_array(memory, COLOR_INDEX_CACHE_SIZE,
74
24.3k
                    sizeof(gs_color_index_cache_elem_t), "gs_color_index_cache_create");
75
24.3k
    float *paint_values = (float *)gs_alloc_byte_array(memory, COLOR_INDEX_CACHE_SIZE * client_num_components,
76
24.3k
                    sizeof(float), "gs_color_index_cache_create");
77
24.3k
    frac31 *frac_values = (need_frac ? (frac31 *)gs_alloc_byte_array(memory, COLOR_INDEX_CACHE_SIZE * device_num_components,
78
24.3k
                                            sizeof(frac31), "gs_color_index_cache_create") : NULL);
79
24.3k
    gs_color_index_cache_t *pcic = gs_alloc_struct(memory, gs_color_index_cache_t, &st_color_index_cache, "gs_color_index_cache_create");
80
81
24.3k
    if (buf == NULL || paint_values == NULL || (need_frac && frac_values == NULL) || pcic == NULL) {
82
0
        gs_free_object(memory, buf, "gs_color_index_cache_create");
83
0
        gs_free_object(memory, paint_values, "gs_color_index_cache_create");
84
0
        gs_free_object(memory, frac_values, "gs_color_index_cache_create");
85
0
        gs_free_object(memory, pcic, "gs_color_index_cache_create");
86
0
        return NULL;
87
0
    }
88
24.3k
    memset(pcic, 0, sizeof(*pcic));
89
24.3k
    memset(buf, 0, COLOR_INDEX_CACHE_SIZE * sizeof(gs_color_index_cache_elem_t));
90
24.3k
    pcic->direct_space = direct_space;
91
24.3k
    pcic->pgs = pgs;
92
24.3k
    pcic->dev = dev;
93
24.3k
    pcic->trans_dev = trans_dev;
94
24.3k
    pcic->device_num_components = device_num_components;
95
24.3k
    pcic->client_num_components = client_num_components;
96
24.3k
    pcic->memory = memory;
97
24.3k
    pcic->used = 1; /* Never use the 0th element. */
98
24.3k
    pcic->buf = buf;
99
24.3k
    pcic->recent_touch = MYNULL;
100
24.3k
    pcic->paint_values = paint_values;
101
24.3k
    pcic->frac_values = frac_values;
102
24.3k
    return pcic;
103
24.3k
}
104
105
void
106
gs_color_index_cache_destroy(gs_color_index_cache_t *pcic)
107
24.3k
{
108
24.3k
    gs_free_object(pcic->memory, pcic->buf, "gs_color_index_cache_create");
109
24.3k
    gs_free_object(pcic->memory, pcic->paint_values, "gs_color_index_cache_create");
110
24.3k
    gs_free_object(pcic->memory, pcic->frac_values, "gs_color_index_cache_create");
111
24.3k
    pcic->buf = NULL;
112
24.3k
    pcic->paint_values = NULL;
113
24.3k
    pcic->frac_values = NULL;
114
24.3k
    gs_free_object(pcic->memory, pcic, "gs_color_index_cache_create");
115
24.3k
}
116
117
static inline int
118
hash_paint_values(const gs_color_index_cache_t *self, const float *paint_values)
119
55.2M
{
120
55.2M
    int i;
121
55.2M
    float v = 0;
122
55.2M
    uint k = 0;
123
55.2M
    const uint a_prime = 79;
124
125
258M
    for (i = 0; i < self->client_num_components; i++)
126
203M
        v = v * a_prime + paint_values[i];
127
    /* Don't know the range of v, so hash its bytes : */
128
276M
    for(i = 0; i < sizeof(v); i++)
129
220M
        k = k * a_prime + ((byte *)&v)[i];
130
55.2M
    return k % COLOR_INDEX_CACHE_CHAINS;
131
55.2M
}
132
133
static inline void
134
exclude_from_chain(gs_color_index_cache_t *self, uint i)
135
25.4M
{
136
25.4M
    uint co = self->buf[i].chain;
137
25.4M
    uint ip = self->buf[i].prev, in = self->buf[i].next;
138
139
25.4M
    self->buf[ip].next = in;
140
25.4M
    self->buf[in].prev = ip;
141
25.4M
    if (self->chains[co] == i)
142
14.3M
        self->chains[co] = in;
143
25.4M
}
144
145
static inline void
146
include_into_chain(gs_color_index_cache_t *self, uint i, uint c)
147
25.7M
{
148
25.7M
    if (self->chains[c] != MYNULL) {
149
25.6M
        uint in = self->chains[c], ip = self->buf[in].prev;
150
151
25.6M
        self->buf[i].next = in;
152
25.6M
        self->buf[i].prev = ip;
153
25.6M
        self->buf[in].prev = i;
154
25.6M
        self->buf[ip].next = i;
155
25.6M
    } else
156
75.0k
        self->buf[i].prev = self->buf[i].next = i;
157
25.7M
    self->chains[c] = i;
158
25.7M
    self->buf[i].chain = c;
159
25.7M
}
160
161
static inline void
162
exclude_from_touch_list(gs_color_index_cache_t *self, uint i)
163
31.9M
{
164
31.9M
    uint ip = self->buf[i].touch_prev, in = self->buf[i].touch_next;
165
166
31.9M
    self->buf[ip].touch_next = in;
167
31.9M
    self->buf[in].touch_prev = ip;
168
31.9M
    if (self->recent_touch == i) {
169
0
        if (i == in)
170
0
            self->recent_touch = MYNULL;
171
0
        else
172
0
            self->recent_touch = in;
173
0
    }
174
31.9M
}
175
176
static inline void
177
include_into_touch_list(gs_color_index_cache_t *self, uint i)
178
32.1M
{
179
32.1M
    if (self->recent_touch != MYNULL) {
180
32.1M
        uint in = self->recent_touch, ip = self->buf[in].touch_prev;
181
182
32.1M
        self->buf[i].touch_next = in;
183
32.1M
        self->buf[i].touch_prev = ip;
184
32.1M
        self->buf[in].touch_prev = i;
185
32.1M
        self->buf[ip].touch_next = i;
186
32.1M
    } else
187
23.2k
        self->buf[i].touch_prev = self->buf[i].touch_next = i;
188
32.1M
    self->recent_touch = i;
189
32.1M
}
190
191
static int
192
get_color_index_cache_elem(gs_color_index_cache_t *self,
193
                           const float *paint_values, uint *pi)
194
55.2M
{
195
55.2M
    int client_num_components = self->client_num_components;
196
55.2M
    uint c = hash_paint_values(self, paint_values);
197
55.2M
    uint i = self->chains[c], j;
198
199
55.2M
    if (i != MYNULL) {
200
55.1M
        uint tries = 16; /* Arbitrary. */
201
202
55.1M
        if (!memcmp(paint_values, self->paint_values + i * client_num_components,
203
55.1M
                    sizeof(*paint_values) * client_num_components)) {
204
29.5M
            if (self->recent_touch != i) {
205
23.7M
                exclude_from_touch_list(self, i);
206
23.7M
                include_into_touch_list(self, i);
207
23.7M
            }
208
29.5M
            *pi = i;
209
29.5M
            return 1;
210
29.5M
        }
211
278M
        for (j = self->buf[i].next; tries -- && j != i; j = self->buf[j].next) {
212
261M
            if (!memcmp(paint_values, self->paint_values + j * client_num_components,
213
261M
                        sizeof(*paint_values) * client_num_components)) {
214
8.14M
                exclude_from_chain(self, j);
215
8.14M
                include_into_chain(self, j, c);
216
8.14M
                if (self->recent_touch != j) {
217
8.14M
                    exclude_from_touch_list(self, j);
218
8.14M
                    include_into_touch_list(self, j);
219
8.14M
                }
220
8.14M
                *pi = j;
221
8.14M
                return 1;
222
8.14M
            }
223
261M
        }
224
25.6M
    }
225
17.5M
    if (self->used < COLOR_INDEX_CACHE_SIZE) {
226
        /* Use a new one */
227
244k
        i = self->used++;
228
244k
        include_into_touch_list(self, i);
229
17.3M
    } else {
230
17.3M
        i = self->recent_touch;
231
17.3M
        self->recent_touch = self->buf[i].touch_prev; /* Assuming the cyclic list,
232
                                                      just move the head pointer to the last element. */
233
17.3M
        exclude_from_chain(self, i);
234
17.3M
    }
235
17.5M
    include_into_chain(self, i, c);
236
17.5M
    *pi = i;
237
17.5M
    return 0;
238
55.2M
}
239
240
static inline void
241
compute_frac_values(gs_color_index_cache_t *self, uint i)
242
17.3M
{
243
244
17.3M
    const gx_device_color_info *cinfo = &self->trans_dev->color_info;
245
17.3M
    int device_num_components = self->device_num_components;
246
17.3M
    int j;
247
17.3M
    gx_color_index c;
248
249
17.3M
    if (self->buf[i].color_type == &gx_dc_type_data_pure) {
250
11.3M
        c = self->buf[i].color.cindex;
251
36.0M
        for (j = 0; j < device_num_components; j++) {
252
24.7M
                int shift = cinfo->comp_shift[j];
253
24.7M
                int bits = cinfo->comp_bits[j];
254
24.7M
                self->frac_values[i * device_num_components + j] =
255
24.7M
                    ((c >> shift) & ((1 << bits) - 1)) <<
256
24.7M
                    (sizeof(frac31) * 8 - 1 - bits);
257
24.7M
        }
258
11.3M
        self->buf[i].frac_values_done = true;
259
11.3M
    } else {
260
        /* Must be devn */
261
30.0M
        for (j = 0; j < device_num_components; j++) {
262
24.0M
            self->frac_values[i * device_num_components + j] =
263
24.0M
                cv2frac31(self->buf[i].color.devn[j]);
264
24.0M
        }
265
6.02M
        self->buf[i].frac_values_done = true;
266
6.02M
    }
267
17.3M
}
268
269
int
270
gs_cached_color_index(gs_color_index_cache_t *self, const float *paint_values,
271
                      gx_device_color *pdevc, frac31 *frac_values)
272
55.2M
{
273
    /* Must return 2 if the color is not pure.
274
       See patch_color_to_device_color. */
275
55.2M
    const gs_color_space *pcs = self->direct_space;
276
55.2M
    int client_num_components = self->client_num_components;
277
55.2M
    int device_num_components = self->device_num_components;
278
55.2M
    uint i, j;
279
55.2M
    int code;
280
281
55.2M
    if (get_color_index_cache_elem(self, paint_values, &i)) {
282
37.6M
        if (pdevc != NULL) {
283
37.6M
            if (self->buf[i].color_type == &gx_dc_type_data_pure) {
284
27.8M
                pdevc->colors.pure = self->buf[i].color.cindex;
285
27.8M
                pdevc->type = &gx_dc_type_data_pure;
286
27.8M
                memcpy(pdevc->ccolor.paint.values, paint_values,
287
27.8M
                       sizeof(*paint_values) * client_num_components);
288
27.8M
            } else {
289
                /* devn case */
290
49.1M
                for (j = 0; j < device_num_components; j++) {
291
39.3M
                    pdevc->colors.devn.values[j] = self->buf[i].color.devn[j];
292
39.3M
                }
293
9.83M
                pdevc->type = &gx_dc_type_data_devn;
294
9.83M
                memcpy(pdevc->ccolor.paint.values, paint_values,
295
9.83M
                       sizeof(*paint_values) * client_num_components);
296
9.83M
            }
297
37.6M
            pdevc->ccolor_valid = true;
298
37.6M
        }
299
37.6M
        if (frac_values != NULL && !self->buf[i].frac_values_done)
300
81.7k
            compute_frac_values(self, i);
301
37.6M
    } else {
302
17.5M
        gx_device_color devc_local;
303
17.5M
        gs_client_color fcc;
304
305
17.5M
        if (pdevc == NULL)
306
0
            pdevc = &devc_local;
307
17.5M
        memcpy(self->paint_values + i * client_num_components, paint_values,
308
17.5M
               sizeof(*paint_values) * client_num_components);
309
17.5M
        memcpy(fcc.paint.values, paint_values,
310
17.5M
               sizeof(*paint_values) * client_num_components);
311
17.5M
        code = pcs->type->remap_color(&fcc, pcs, pdevc, self->pgs,
312
17.5M
                                      self->trans_dev, gs_color_select_texture);
313
17.5M
        if (code < 0)
314
0
            return code;
315
17.5M
        if (pdevc->type == &gx_dc_type_data_pure) {
316
11.4M
            self->buf[i].color.cindex = pdevc->colors.pure;
317
11.4M
        } else if (pdevc->type == &gx_dc_type_data_devn) {
318
30.4M
            for (j = 0; j < device_num_components; j++) {
319
24.3M
                self->buf[i].color.devn[j] = pdevc->colors.devn.values[j];
320
24.3M
            }
321
6.08M
        } else {
322
0
            return 2;
323
0
        }
324
17.5M
        self->buf[i].color_type = pdevc->type;
325
17.5M
        if (frac_values != NULL)
326
17.2M
            compute_frac_values(self, i);
327
283k
        else
328
283k
            self->buf[i].frac_values_done = false;
329
17.5M
    }
330
55.2M
    if (frac_values != NULL)
331
54.6M
        memcpy(frac_values, self->frac_values + i * device_num_components,
332
54.6M
               sizeof(*frac_values) * device_num_components);
333
55.2M
    return 0;
334
55.2M
}