1 /* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
13 CA 94903, U.S.A., +1(415)492-9861, for further information.
14 */
15
16 /* A cache for icc colorspaces that were created from PS CIE color
17 spaces or from PDF cal color spaces.
18 */
19
20 #include "std.h"
21 #include "stdpre.h"
22 #include "gstypes.h"
23 #include "gsmemory.h"
24 #include "gsstruct.h"
25 #include "scommon.h"
26 #include "gx.h"
27 #include "gzstate.h"
28 #include "gscms.h"
29 #include "gsicc_profilecache.h"
30 #include "gserrors.h"
31
32 #define ICC_CACHE_MAXPROFILE 50
33
34 /* Static prototypes */
35 static void rc_gsicc_profile_cache_free(gs_memory_t * mem, void *ptr_in,
36 client_name_t cname);
37 static void gsicc_remove_cs_entry(gsicc_profile_cache_t *profile_cache);
38
39 gs_private_st_ptrs2(st_profile_entry, gsicc_profile_entry_t,
40 "gsicc_profile_entry", profile_entry_enum_ptrs,
41 profile_entry_reloc_ptrs, color_space, next);
42 gs_private_st_ptrs1(st_profile_cache, gsicc_profile_cache_t,
43 "gsicc_profile_cache", profile_list_enum_ptrs,
44 profile_list_reloc_ptrs, head);
45
46 /**
47 * gsicc_cache_new: Allocate a new ICC cache manager
48 * Return value: Pointer to allocated manager, or NULL on failure.
49 **/
50 gsicc_profile_cache_t *
gsicc_profilecache_new(gs_memory_t * memory)51 gsicc_profilecache_new(gs_memory_t *memory)
52 {
53 gsicc_profile_cache_t *result;
54
55 /* We want this to be maintained in stable_memory. It should not be effected by the
56 save and restores */
57 result = gs_alloc_struct(memory->stable_memory, gsicc_profile_cache_t,
58 &st_profile_cache, "gsicc_profilecache_new");
59 if ( result == NULL )
60 return(NULL);
61 rc_init_free(result, memory->stable_memory, 1, rc_gsicc_profile_cache_free);
62 result->head = NULL;
63 result->num_entries = 0;
64 result->memory = memory;
65 return(result);
66 }
67
68 static void
rc_gsicc_profile_cache_free(gs_memory_t * mem,void * ptr_in,client_name_t cname)69 rc_gsicc_profile_cache_free(gs_memory_t * mem, void *ptr_in, client_name_t cname)
70 {
71 gsicc_profile_cache_t *profile_cache = (gsicc_profile_cache_t * ) ptr_in;
72 gsicc_profile_entry_t *curr = profile_cache->head, *next;
73
74 while (curr != NULL ){
75 next = curr->next;
76 rc_decrement(curr->color_space, "rc_gsicc_profile_cache_free");
77 gs_free_object(mem->stable_memory, curr,
78 "rc_gsicc_profile_cache_free");
79 profile_cache->num_entries--;
80 curr = next;
81 }
82 #ifdef DEBUG
83 if (profile_cache->num_entries != 0)
84 eprintf1("gsicc_profile_cache_free, num_entries is %d (should be 0).\n",
85 profile_cache->num_entries);
86 #endif
87 gs_free_object(mem->stable_memory, profile_cache,
88 "rc_gsicc_profile_cache_free");
89 }
90
91 void
gsicc_add_cs(gs_state * pgs,gs_color_space * colorspace,ulong dictkey)92 gsicc_add_cs(gs_state * pgs, gs_color_space * colorspace, ulong dictkey)
93 {
94 gsicc_profile_entry_t *result;
95 gsicc_profile_cache_t *profile_cache = pgs->icc_profile_cache;
96 gs_memory_t *memory = pgs->memory;
97
98 /* The entry has to be added in stable memory. We want them
99 to be maintained across the gsave and grestore process */
100 result = gs_alloc_struct(memory->stable_memory, gsicc_profile_entry_t,
101 &st_profile_entry, "gsicc_add_cs");
102 /* If needed, remove an entry (the last one) */
103 if (profile_cache->num_entries >= ICC_CACHE_MAXPROFILE) {
104 gsicc_remove_cs_entry(profile_cache);
105 }
106 /* Add to the top of the list. That way we find the MRU enty right away.
107 Last entry stays the same. */
108 result->next = profile_cache->head;
109 profile_cache->head = result; /* MRU */
110 result->color_space = colorspace;
111 rc_increment(colorspace);
112 result->key = dictkey;
113 profile_cache->num_entries++;
114 }
115
116 gs_color_space*
gsicc_find_cs(ulong key_test,gs_state * pgs)117 gsicc_find_cs(ulong key_test, gs_state * pgs)
118 {
119 gsicc_profile_cache_t *profile_cache = pgs->icc_profile_cache;
120 gsicc_profile_entry_t *prev = NULL, *curr = profile_cache->head;
121
122 /* Look through the cache for the key. If found, move to MRU */
123 while (curr != NULL ){
124 if (curr->key == key_test){
125 /* If not already at head of list, move this one there */
126 if (curr != profile_cache->head) {
127 /* We need to move found one to the top of the list. */
128 prev->next = curr->next;
129 curr->next = profile_cache->head;
130 profile_cache->head = curr;
131 }
132 return(curr->color_space);
133 }
134 prev = curr;
135 curr = curr->next;
136 }
137 return(NULL);
138 }
139
140 /* Remove the LRU entry, which ideally is at the bottom. Note that there
141 is no need to have a ref_count in this structure since the color
142 space objects that are the member variables are reference counted themselves */
143 static void
gsicc_remove_cs_entry(gsicc_profile_cache_t * profile_cache)144 gsicc_remove_cs_entry(gsicc_profile_cache_t *profile_cache)
145 {
146 gs_memory_t *memory = profile_cache->memory;
147 gsicc_profile_entry_t *prev = NULL, *curr = profile_cache->head;
148
149 #ifdef DEBUG
150 if (curr == NULL) {
151 eprintf(" attempt to remove from an empty profile cache.\n");
152 return; /* gs_abort(); */
153 }
154 #endif
155 while (curr->next != NULL) {
156 prev = curr;
157 curr = curr->next;
158 }
159 profile_cache->num_entries--;
160 if (prev == NULL) {
161 /* No more entries */
162 profile_cache->head = NULL;
163 #ifdef DEBUG
164 if (profile_cache->num_entries != 0) {
165 eprintf1("profile cache list empty, but list has num_entries=%d.\n",
166 profile_cache->num_entries);
167 }
168 #endif
169 } else {
170 prev->next = NULL; /* new tail */
171 }
172 /* Decremented, but someone could still be referencing this */
173 /* If found again in the source document, it will be regenerated
174 and added back into the cache. */
175 rc_decrement(curr->color_space, "gsicc_remove_cs_entry");
176 gs_free_object(memory->stable_memory, curr, "gsicc_remove_cs_entry");
177 }
178