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