/src/ghostpdl/base/gsicc_blacktext.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 | | /* Handling of color spaces stored during replacement of all |
17 | | * text with black. |
18 | | */ |
19 | | |
20 | | #include "gsmemory.h" |
21 | | #include "gsstruct.h" |
22 | | #include "gzstate.h" |
23 | | #include "gsicc_blacktext.h" |
24 | | #include "gsicc_cache.h" |
25 | | |
26 | | /* gsicc_blacktextvec_state_t is going to be storing GCed items |
27 | | (color spaces and client colors) and so will need to be GCed */ |
28 | | gs_private_st_ptrs4(st_blacktextvec_state, gsicc_blacktextvec_state_t, |
29 | | "gsicc_blacktextvec_state", blacktextvec_state_enum_ptrs, |
30 | | blacktextvec_state_reloc_ptrs, pcs, pcs_alt, pcc, pcc_alt); |
31 | | |
32 | | static void |
33 | | rc_gsicc_blacktextvec_state_free(gs_memory_t *mem, void *ptr_in, |
34 | | client_name_t cname) |
35 | 0 | { |
36 | 0 | gsicc_blacktextvec_state_t *state = (gsicc_blacktextvec_state_t*)ptr_in; |
37 | |
|
38 | 0 | rc_decrement_cs(state->pcs, "rc_gsicc_blacktextvec_state_free"); |
39 | 0 | rc_decrement_cs(state->pcs_alt, "rc_gsicc_blacktextvec_state_free"); |
40 | |
|
41 | 0 | gs_free_object(state->memory, state, |
42 | 0 | "rc_gsicc_blacktextvec_state_free"); |
43 | 0 | } |
44 | | |
45 | | gsicc_blacktextvec_state_t* |
46 | | gsicc_blacktextvec_state_new(gs_memory_t *memory, bool is_text) |
47 | 0 | { |
48 | 0 | gsicc_blacktextvec_state_t *result; |
49 | |
|
50 | 0 | result = gs_alloc_struct(memory->stable_memory, gsicc_blacktextvec_state_t, |
51 | 0 | &st_blacktextvec_state, "gsicc_blacktextvec_state_new"); |
52 | 0 | if (result == NULL) |
53 | 0 | return NULL; |
54 | 0 | rc_init_free(result, memory->stable_memory, 1, rc_gsicc_blacktextvec_state_free); |
55 | 0 | result->memory = memory->stable_memory; |
56 | 0 | result->pcs = NULL; |
57 | 0 | result->pcs_alt = NULL; |
58 | 0 | result->pcc = NULL; |
59 | 0 | result->pcc_alt = NULL; |
60 | 0 | result->is_text = is_text; |
61 | |
|
62 | 0 | return result; |
63 | 0 | } |
64 | | |
65 | | /* Crude white color check. Only valid for ICC based RGB, CMYK, Gray, and LAB CS. |
66 | | Makes some assumptions about profile. Also may want some tolerance check. */ |
67 | | bool gsicc_is_white_blacktextvec(gs_gstate *pgs, gx_device *dev, gs_color_space* pcs, gs_client_color* pcc) |
68 | 0 | { |
69 | 0 | double Lstar = 0; |
70 | 0 | double astar = 0; |
71 | 0 | double bstar = 0; |
72 | 0 | cmm_dev_profile_t* dev_profile; |
73 | |
|
74 | 0 | dev_proc(dev, get_profile)(dev, &dev_profile); |
75 | |
|
76 | 0 | if (gs_color_space_get_index(pcs) == gs_color_space_index_ICC) { |
77 | 0 | if (pcs->cmm_icc_profile_data->data_cs == gsCIELAB) { |
78 | 0 | if (pcc->paint.values[0] >= dev_profile->blackthresholdL && |
79 | 0 | fabs(pcc->paint.values[1]) < dev_profile->blackthresholdC && |
80 | 0 | fabs(pcc->paint.values[2]) < dev_profile->blackthresholdC) |
81 | 0 | return true; |
82 | 0 | else |
83 | 0 | return false; |
84 | 0 | } |
85 | | /* For all others, lets get to CIELAB value */ |
86 | 0 | if (pgs->icc_manager->lab_profile != NULL) { |
87 | 0 | gsicc_link_t *icc_link; |
88 | 0 | gsicc_rendering_param_t rendering_params; |
89 | 0 | unsigned short psrc[4]; |
90 | 0 | unsigned short pdes[3]; |
91 | |
|
92 | 0 | rendering_params.black_point_comp = gsBLACKPTCOMP_ON; |
93 | 0 | rendering_params.graphics_type_tag = GS_UNKNOWN_TAG; |
94 | 0 | rendering_params.override_icc = false; |
95 | 0 | rendering_params.preserve_black = gsBKPRESNOTSPECIFIED; |
96 | 0 | rendering_params.rendering_intent = gsRELATIVECOLORIMETRIC; |
97 | 0 | rendering_params.cmm = gsCMM_DEFAULT; |
98 | |
|
99 | 0 | icc_link = gsicc_get_link_profile(pgs, NULL, pcs->cmm_icc_profile_data, |
100 | 0 | pgs->icc_manager->lab_profile, &rendering_params, |
101 | 0 | pgs->memory, false); |
102 | 0 | if (icc_link == NULL) |
103 | 0 | return false; |
104 | | |
105 | 0 | switch (pcs->cmm_icc_profile_data->data_cs) { |
106 | 0 | case gsGRAY: |
107 | 0 | psrc[0] = (unsigned short)(pcc->paint.values[0] * 65535); |
108 | 0 | break; |
109 | 0 | case gsRGB: |
110 | 0 | psrc[0] = (unsigned short)(pcc->paint.values[0] * 65535); |
111 | 0 | psrc[1] = (unsigned short)(pcc->paint.values[1] * 65535); |
112 | 0 | psrc[2] = (unsigned short)(pcc->paint.values[2] * 65535); |
113 | 0 | break; |
114 | 0 | case gsCMYK: |
115 | 0 | psrc[0] = (unsigned short)(pcc->paint.values[0] * 65535); |
116 | 0 | psrc[1] = (unsigned short)(pcc->paint.values[1] * 65535); |
117 | 0 | psrc[2] = (unsigned short)(pcc->paint.values[2] * 65535); |
118 | 0 | psrc[3] = (unsigned short)(pcc->paint.values[3] * 65535); |
119 | 0 | break; |
120 | 0 | default: |
121 | 0 | gsicc_release_link(icc_link); |
122 | 0 | return false; |
123 | 0 | } |
124 | 0 | (icc_link->procs.map_color)(NULL, icc_link, psrc, pdes, 2); |
125 | 0 | gsicc_release_link(icc_link); |
126 | |
|
127 | 0 | Lstar = pdes[0] * 100.0 / 65535.0; |
128 | 0 | astar = pdes[1] * 256.0 / 65535.0 - 128.0; |
129 | 0 | bstar = pdes[2] * 256.0 / 65535.0 - 128.0; |
130 | |
|
131 | 0 | if (Lstar >= dev_profile->blackthresholdL && |
132 | 0 | fabs(astar) < dev_profile->blackthresholdC && |
133 | 0 | fabs(bstar) < dev_profile->blackthresholdC) |
134 | 0 | return true; |
135 | 0 | else |
136 | 0 | return false; |
137 | 0 | } else { |
138 | | /* Something to fall back on */ |
139 | 0 | switch (pcs->cmm_icc_profile_data->data_cs) { |
140 | 0 | case gsGRAY: |
141 | 0 | if (pcc->paint.values[0] == 1.0) |
142 | 0 | return true; |
143 | 0 | else |
144 | 0 | return false; |
145 | 0 | break; |
146 | 0 | case gsRGB: |
147 | 0 | if (pcc->paint.values[0] == 1.0 && pcc->paint.values[1] == 1.0 && |
148 | 0 | pcc->paint.values[2] == 1.0) |
149 | 0 | return true; |
150 | 0 | else |
151 | 0 | return false; |
152 | 0 | break; |
153 | 0 | case gsCMYK: |
154 | 0 | if (pcc->paint.values[0] == 0.0 && pcc->paint.values[1] == 0.0 && |
155 | 0 | pcc->paint.values[2] == 0.0 && pcc->paint.values[3] == 0.0) |
156 | 0 | return true; |
157 | 0 | else |
158 | 0 | return false; |
159 | 0 | break; |
160 | 0 | default: |
161 | 0 | return false; |
162 | 0 | } |
163 | 0 | } |
164 | 0 | } else |
165 | 0 | return false; |
166 | 0 | } |
167 | | |
168 | | bool gsicc_setup_blacktextvec(gs_gstate *pgs, gx_device *dev, bool is_text) |
169 | 0 | { |
170 | 0 | gs_color_space *pcs_curr = gs_currentcolorspace_inline(pgs); |
171 | 0 | gs_color_space *pcs_alt = gs_swappedcolorspace_inline(pgs); |
172 | | |
173 | | /* If neither space is ICC then we are not doing anything */ |
174 | 0 | if (!gs_color_space_is_ICC(pcs_curr) && !gs_color_space_is_ICC(pcs_alt)) |
175 | 0 | return false; |
176 | | |
177 | | /* Create a new object to hold the cs details */ |
178 | 0 | pgs->black_textvec_state = gsicc_blacktextvec_state_new(pgs->memory, is_text); |
179 | 0 | if (pgs->black_textvec_state == NULL) |
180 | 0 | return false; /* No error just move on */ |
181 | | |
182 | | /* If curr space is ICC then store it */ |
183 | 0 | if (gs_color_space_is_ICC(pcs_curr)) { |
184 | 0 | rc_increment_cs(pcs_curr); /* We are storing the cs. Will decrement when structure is released */ |
185 | 0 | pgs->black_textvec_state->pcs = pcs_curr; |
186 | 0 | pgs->black_textvec_state->pcc = pgs->color[0].ccolor; |
187 | 0 | cs_adjust_color_count(pgs, 1); /* The set_gray will do a decrement, only need if pattern */ |
188 | 0 | pgs->black_textvec_state->value[0] = pgs->color[0].ccolor->paint.values[0]; |
189 | |
|
190 | 0 | if (gsicc_is_white_blacktextvec(pgs, dev, pcs_curr, pgs->color[0].ccolor)) |
191 | 0 | gs_setgray(pgs, 1.0); |
192 | 0 | else |
193 | 0 | gs_setgray(pgs, 0.0); |
194 | 0 | } |
195 | | |
196 | | /* If alt space is ICC then store it */ |
197 | 0 | if (gs_color_space_is_ICC(pcs_alt)) { |
198 | 0 | rc_increment_cs(pcs_alt); /* We are storing the cs. Will decrement when structure is released */ |
199 | 0 | pgs->black_textvec_state->pcs_alt = pcs_alt; |
200 | |
|
201 | 0 | gs_swapcolors_quick(pgs); /* Have to swap for set_gray and adjust color count */ |
202 | 0 | pgs->black_textvec_state->pcc_alt = pgs->color[0].ccolor; |
203 | 0 | cs_adjust_color_count(pgs, 1); /* The set_gray will do a decrement, only need if pattern */ |
204 | 0 | pgs->black_textvec_state->value[1] = pgs->color[0].ccolor->paint.values[0]; |
205 | |
|
206 | 0 | if (gsicc_is_white_blacktextvec(pgs, dev, pcs_alt, pgs->color[0].ccolor)) |
207 | 0 | gs_setgray(pgs, 1.0); |
208 | 0 | else |
209 | 0 | gs_setgray(pgs, 0.0); |
210 | 0 | gs_swapcolors_quick(pgs); |
211 | 0 | } |
212 | |
|
213 | 0 | pgs->black_textvec_state->is_fill = pgs->is_fill_color; |
214 | 0 | return true; /* Need to clean up */ |
215 | 0 | } |
216 | | |
217 | | void |
218 | | gsicc_restore_blacktextvec(gs_gstate *pgs, bool is_text) |
219 | 0 | { |
220 | 0 | gsicc_blacktextvec_state_t *state = pgs->black_textvec_state; |
221 | 0 | int code; |
222 | |
|
223 | 0 | if (state == NULL) |
224 | 0 | return; |
225 | | |
226 | 0 | if (is_text != state->is_text) |
227 | 0 | return; |
228 | | |
229 | | /* Make sure state and original are same fill_color condition */ |
230 | 0 | if (state->rc.ref_count == 1) { |
231 | 0 | if ((state->is_fill && pgs->is_fill_color) || (!state->is_fill && !pgs->is_fill_color)) { |
232 | 0 | if (pgs->black_textvec_state->pcs != NULL) { |
233 | 0 | if ((code = gs_setcolorspace_only(pgs, pgs->black_textvec_state->pcs)) >= 0) { |
234 | | /* current client color is gray. no need to decrement */ |
235 | 0 | pgs->color[0].ccolor = pgs->black_textvec_state->pcc; |
236 | 0 | pgs->color[0].ccolor->paint.values[0] = pgs->black_textvec_state->value[0]; |
237 | 0 | } |
238 | 0 | gx_unset_dev_color(pgs); |
239 | 0 | } |
240 | 0 | if (pgs->black_textvec_state->pcs_alt != NULL) { |
241 | 0 | gs_swapcolors_quick(pgs); |
242 | 0 | if ((code = gs_setcolorspace_only(pgs, pgs->black_textvec_state->pcs_alt)) >= 0) { |
243 | 0 | pgs->color[0].ccolor = pgs->black_textvec_state->pcc_alt; |
244 | 0 | pgs->color[0].ccolor->paint.values[0] = pgs->black_textvec_state->value[1]; |
245 | 0 | } |
246 | 0 | gs_swapcolors_quick(pgs); |
247 | 0 | gx_unset_alt_dev_color(pgs); |
248 | 0 | } |
249 | 0 | } else { |
250 | 0 | if (pgs->black_textvec_state->pcs_alt != NULL) { |
251 | 0 | if ((code = gs_setcolorspace_only(pgs, pgs->black_textvec_state->pcs_alt)) >= 0) { |
252 | 0 | pgs->color[0].ccolor = pgs->black_textvec_state->pcc_alt; |
253 | 0 | pgs->color[0].ccolor->paint.values[0] = pgs->black_textvec_state->value[1]; |
254 | 0 | } |
255 | 0 | gx_unset_dev_color(pgs); |
256 | 0 | } |
257 | 0 | if (pgs->black_textvec_state->pcs != NULL) { |
258 | 0 | gs_swapcolors_quick(pgs); |
259 | 0 | if ((code = gs_setcolorspace_only(pgs, pgs->black_textvec_state->pcs)) >= 0) { |
260 | 0 | pgs->color[0].ccolor = pgs->black_textvec_state->pcc; |
261 | 0 | pgs->color[0].ccolor->paint.values[0] = pgs->black_textvec_state->value[0]; |
262 | 0 | } |
263 | 0 | gs_swapcolors_quick(pgs); |
264 | 0 | gx_unset_alt_dev_color(pgs); |
265 | 0 | } |
266 | 0 | } |
267 | 0 | } |
268 | 0 | rc_decrement(state, "gsicc_restore_black_text"); |
269 | 0 | pgs->black_textvec_state = NULL; |
270 | 0 | } |