1 /* Copyright (C) 2001-2019 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., 1305 Grant Avenue - Suite 200, Novato,
13 CA 94945, U.S.A., +1(415)492-9861, for further information.
14 */
15
16
17 /* gsicc handling for direct color replacement. */
18
19 #include "std.h"
20 #include "string_.h"
21 #include "stdpre.h"
22 #include "gstypes.h"
23 #include "gsmemory.h"
24 #include "gsstruct.h"
25 #include "scommon.h"
26 #include "strmio.h"
27 #include "gx.h"
28 #include "gxgstate.h"
29 #include "gxcspace.h"
30 #include "gsicc_cms.h"
31 #include "gsicc_cache.h"
32
33 /* A link structure for our replace color transform */
34 typedef struct rcm_link_s {
35 byte num_out;
36 byte num_in;
37 gsicc_colorbuffer_t data_cs_in;
38 gs_memory_t *memory;
39 gx_cm_color_map_procs cm_procs; /* for the demo */
40 void *context; /* For a table and or a set of procs */
41 } rcm_link_t;
42
43 static void gsicc_rcm_transform_general(gx_device *dev, gsicc_link_t *icclink,
44 void *inputcolor, void *outputcolor,
45 int num_bytes_in, int num_bytes_out);
46
47 /* Functions that should be optimized later to do planar/chunky with
48 color conversions. Just putting in something that should work
49 right now */
50 static void
gsicc_rcm_planar_to_planar(gx_device * dev,gsicc_link_t * icclink,gsicc_bufferdesc_t * input_buff_desc,gsicc_bufferdesc_t * output_buff_desc,void * inputbuffer,void * outputbuffer)51 gsicc_rcm_planar_to_planar(gx_device *dev, gsicc_link_t *icclink,
52 gsicc_bufferdesc_t *input_buff_desc,
53 gsicc_bufferdesc_t *output_buff_desc,
54 void *inputbuffer, void *outputbuffer)
55 {
56 int k, j;
57 byte *inputpos[4];
58 byte *outputpos[4];
59 byte *in_buffer_ptr = (byte *) inputbuffer;
60 byte *out_buffer_ptr = (byte *) outputbuffer;
61 byte in_color[4], out_color[4];
62
63 for (k = 0; k < input_buff_desc->num_chan; k++) {
64 inputpos[k] = in_buffer_ptr + k * input_buff_desc->plane_stride;
65 }
66 for (k = 0; k < output_buff_desc->num_chan; k++) {
67 outputpos[k] = out_buffer_ptr + k * output_buff_desc->plane_stride;
68 }
69 /* Note to self. We currently only do this in the transparency buffer
70 case which has byte representation so just stepping through
71 plane_stride is ok at this time. */
72 for (k = 0; k < input_buff_desc->plane_stride ; k++) {
73 for (j = 0; j < input_buff_desc->num_chan; j++) {
74 in_color[j] = *(inputpos[j]);
75 inputpos[j] += input_buff_desc->bytes_per_chan;
76 }
77 gsicc_rcm_transform_general(dev, icclink, (void*) &(in_color[0]),
78 (void*) &(out_color[0]), 1, 1);
79 for (j = 0; j < output_buff_desc->num_chan; j++) {
80 *(outputpos[j]) = out_color[j];
81 outputpos[j] += output_buff_desc->bytes_per_chan;
82 }
83 }
84 }
85
86 /* This is not really used yet */
87 static void
gsicc_rcm_planar_to_chunky(gx_device * dev,gsicc_link_t * icclink,gsicc_bufferdesc_t * input_buff_desc,gsicc_bufferdesc_t * output_buff_desc,void * inputbuffer,void * outputbuffer)88 gsicc_rcm_planar_to_chunky(gx_device *dev, gsicc_link_t *icclink,
89 gsicc_bufferdesc_t *input_buff_desc,
90 gsicc_bufferdesc_t *output_buff_desc,
91 void *inputbuffer, void *outputbuffer)
92 {
93
94
95 }
96
97 /* This is used with the fast thresholding code when doing -dUseFastColor
98 and going out to a planar device */
99 static void
gsicc_rcm_chunky_to_planar(gx_device * dev,gsicc_link_t * icclink,gsicc_bufferdesc_t * input_buff_desc,gsicc_bufferdesc_t * output_buff_desc,void * inputbuffer,void * outputbuffer)100 gsicc_rcm_chunky_to_planar(gx_device *dev, gsicc_link_t *icclink,
101 gsicc_bufferdesc_t *input_buff_desc,
102 gsicc_bufferdesc_t *output_buff_desc,
103 void *inputbuffer, void *outputbuffer)
104 {
105 int k, j, m;
106 byte *inputpos = (byte *) inputbuffer;
107 byte *outputpos = (byte *) outputbuffer;
108 byte *output_loc;
109 byte *inputcolor;
110 byte outputcolor[8]; /* 8 since we have max 4 colorants and 2 bytes/colorant */
111 unsigned short *pos_in_short, *pos_out_short;
112 int num_bytes_in = input_buff_desc->bytes_per_chan;
113 int num_bytes_out = output_buff_desc->bytes_per_chan;
114 int pixel_in_step = num_bytes_in * input_buff_desc->num_chan;
115 int plane_stride = output_buff_desc->plane_stride;
116
117 /* Do row by row. */
118 for (k = 0; k < input_buff_desc->num_rows ; k++) {
119 inputcolor = inputpos;
120 output_loc = outputpos;
121
122 /* split the 2 byte 1 byte case here to avoid decision in inner loop */
123 if (output_buff_desc->bytes_per_chan == 1) {
124 for (j = 0; j < input_buff_desc->pixels_per_row; j++) {
125 gsicc_rcm_transform_general(dev, icclink, (void*) inputcolor,
126 (void*) &(outputcolor[0]), num_bytes_in,
127 num_bytes_out);
128 /* Stuff the output in the proper planar location */
129 for (m = 0; m < output_buff_desc->num_chan; m++) {
130 *(output_loc + m * plane_stride + j) = outputcolor[m];
131 }
132 inputcolor += pixel_in_step;
133 }
134 inputpos += input_buff_desc->row_stride;
135 outputpos += output_buff_desc->row_stride;
136 } else {
137 for (j = 0; j < input_buff_desc->pixels_per_row; j++) {
138 gsicc_rcm_transform_general(dev, icclink, (void*) inputcolor,
139 (void*) &(outputcolor[0]), num_bytes_in,
140 num_bytes_out);
141 /* Stuff the output in the proper planar location */
142 pos_in_short = (unsigned short*) &(outputcolor[0]);
143 pos_out_short = (unsigned short*) (output_loc);
144 for (m = 0; m < output_buff_desc->num_chan; m++) {
145 *(pos_out_short + m * plane_stride + j) = pos_in_short[m];
146 }
147 inputcolor += pixel_in_step;
148 }
149 inputpos += input_buff_desc->row_stride;
150 outputpos += output_buff_desc->row_stride;
151 }
152 }
153 }
154
155 static void
gsicc_rcm_chunky_to_chunky(gx_device * dev,gsicc_link_t * icclink,gsicc_bufferdesc_t * input_buff_desc,gsicc_bufferdesc_t * output_buff_desc,void * inputbuffer,void * outputbuffer)156 gsicc_rcm_chunky_to_chunky(gx_device *dev, gsicc_link_t *icclink,
157 gsicc_bufferdesc_t *input_buff_desc,
158 gsicc_bufferdesc_t *output_buff_desc,
159 void *inputbuffer, void *outputbuffer)
160 {
161 int k, j;
162 byte *inputpos = (byte *) inputbuffer;
163 byte *outputpos = (byte *) outputbuffer;
164 byte *inputcolor, *outputcolor;
165 int num_bytes_in = input_buff_desc->bytes_per_chan;
166 int num_bytes_out = output_buff_desc->bytes_per_chan;
167 int pixel_in_step = num_bytes_in * input_buff_desc->num_chan;
168 int pixel_out_step = num_bytes_out * output_buff_desc->num_chan;
169
170 /* Do row by row. */
171 for (k = 0; k < input_buff_desc->num_rows ; k++) {
172 inputcolor = inputpos;
173 outputcolor = outputpos;
174 for (j = 0; j < input_buff_desc->pixels_per_row; j++) {
175 gsicc_rcm_transform_general(dev, icclink, (void*) inputcolor,
176 (void*) outputcolor, num_bytes_in,
177 num_bytes_out);
178 inputcolor += pixel_in_step;
179 outputcolor += pixel_out_step;
180 }
181 inputpos += input_buff_desc->row_stride;
182 outputpos += output_buff_desc->row_stride;
183 }
184 }
185
186 /* Transform an entire buffer using replacement method */
187 static int
gsicc_rcm_transform_color_buffer(gx_device * dev,gsicc_link_t * icclink,gsicc_bufferdesc_t * input_buff_desc,gsicc_bufferdesc_t * output_buff_desc,void * inputbuffer,void * outputbuffer)188 gsicc_rcm_transform_color_buffer(gx_device *dev, gsicc_link_t *icclink,
189 gsicc_bufferdesc_t *input_buff_desc,
190 gsicc_bufferdesc_t *output_buff_desc,
191 void *inputbuffer, void *outputbuffer)
192 {
193 /* Since we have to do the mappings to and from frac colors we will for
194 now just call the gsicc_rcm_transform_color as we step through the
195 buffers. This process can be significantly sped up */
196
197 if (input_buff_desc->is_planar) {
198 if (output_buff_desc->is_planar) {
199 gsicc_rcm_planar_to_planar(dev, icclink, input_buff_desc,
200 output_buff_desc, inputbuffer,
201 outputbuffer);
202 } else {
203 gsicc_rcm_planar_to_chunky(dev, icclink, input_buff_desc,
204 output_buff_desc, inputbuffer,
205 outputbuffer);
206 }
207 } else {
208 if (output_buff_desc->is_planar) {
209 gsicc_rcm_chunky_to_planar(dev, icclink, input_buff_desc,
210 output_buff_desc, inputbuffer,
211 outputbuffer);
212 } else {
213 gsicc_rcm_chunky_to_chunky(dev, icclink, input_buff_desc,
214 output_buff_desc, inputbuffer,
215 outputbuffer);
216 }
217 }
218 return 0;
219 }
220
221 /* Shared function between the single and buffer conversions. This is where
222 we do the actual replacement. For now, we make the replacement a
223 negative to show the effect of what using color replacement. We also use
224 the device procs to map to the device value. */
225 static void
gsicc_rcm_transform_general(gx_device * dev,gsicc_link_t * icclink,void * inputcolor,void * outputcolor,int num_bytes_in,int num_bytes_out)226 gsicc_rcm_transform_general(gx_device *dev, gsicc_link_t *icclink,
227 void *inputcolor, void *outputcolor,
228 int num_bytes_in, int num_bytes_out)
229 {
230 /* Input data is either single byte or 2 byte color values. */
231 rcm_link_t *link = (rcm_link_t*) icclink->link_handle;
232 byte num_in = link->num_in;
233 byte num_out = link->num_out;
234 frac frac_in[4];
235 frac frac_out[GX_DEVICE_COLOR_MAX_COMPONENTS];
236 int k;
237 gx_device *parentmost_dev = subclass_parentmost_device(dev);
238
239 /* Make the negative for the demo.... */
240 if (num_bytes_in == 2) {
241 unsigned short *data = (unsigned short *) inputcolor;
242 for (k = 0; k < num_in; k++) {
243 frac_in[k] = frac_1 - ushort2frac(data[k]);
244 }
245 } else {
246 byte *data = (byte *) inputcolor;
247 for (k = 0; k < num_in; k++) {
248 frac_in[k] = frac_1 - byte2frac(data[k]);
249 }
250 }
251 /* Use the device procedure */
252 switch (num_in) {
253 case 1:
254 (link->cm_procs.map_gray)(parentmost_dev, frac_in[0], frac_out);
255 break;
256 case 3:
257 (link->cm_procs.map_rgb)(parentmost_dev, NULL, frac_in[0], frac_in[1],
258 frac_in[2], frac_out);
259 break;
260 case 4:
261 (link->cm_procs.map_cmyk)(parentmost_dev, frac_in[0], frac_in[1], frac_in[2],
262 frac_in[3], frac_out);
263 break;
264 default:
265 memset(&(frac_out[0]), 0, sizeof(frac_out));
266 break;
267 }
268 if (num_bytes_out == 2) {
269 unsigned short *data = (unsigned short *) outputcolor;
270 for (k = 0; k < num_out; k++) {
271 data[k] = frac2ushort(frac_out[k]);
272 }
273 } else {
274 byte *data = (byte *) outputcolor;
275 for (k = 0; k < num_out; k++) {
276 data[k] = frac2byte(frac_out[k]);
277 }
278 }
279 return;
280 }
281
282 /* Transform a single color using the generic (non color managed)
283 transformations */
284 static int
gsicc_rcm_transform_color(gx_device * dev,gsicc_link_t * icclink,void * inputcolor,void * outputcolor,int num_bytes)285 gsicc_rcm_transform_color(gx_device *dev, gsicc_link_t *icclink, void *inputcolor,
286 void *outputcolor, int num_bytes)
287 {
288 gsicc_rcm_transform_general(dev, icclink, inputcolor, outputcolor,
289 num_bytes, num_bytes);
290 return 0;
291 }
292
293 static void
gsicc_rcm_freelink(gsicc_link_t * icclink)294 gsicc_rcm_freelink(gsicc_link_t *icclink)
295 {
296 rcm_link_t *rcm_link = (rcm_link_t*) icclink->link_handle;
297 if (rcm_link != NULL)
298 gs_free_object(rcm_link->memory, rcm_link, "gsicc_rcm_freelink");
299 icclink->link_handle = NULL;
300 }
301
302 /* Get the replacement color management link. It basically needs to store
303 the number of components for the source so that we know what we are
304 coming from (e.g. RGB, CMYK, Gray) */
305 gsicc_link_t*
gsicc_rcm_get_link(const gs_gstate * pgs,gx_device * dev,gsicc_colorbuffer_t data_cs)306 gsicc_rcm_get_link(const gs_gstate *pgs, gx_device *dev,
307 gsicc_colorbuffer_t data_cs)
308 {
309 gsicc_link_t *result;
310 gsicc_hashlink_t hash;
311 rcm_link_t *rcm_link;
312 gs_memory_t *mem;
313 const gx_cm_color_map_procs * cm_procs;
314 bool pageneutralcolor = false;
315 cmm_dev_profile_t *dev_profile;
316 int code;
317 subclass_color_mappings scm;
318
319 if (dev == NULL)
320 return NULL;
321
322 mem = dev->memory->non_gc_memory;
323 /* Need to check if we need to monitor for color */
324 code = dev_proc(dev, get_profile)(dev, &dev_profile);
325 if (code < 0)
326 return NULL;
327 if (dev_profile != NULL) {
328 pageneutralcolor = dev_profile->pageneutralcolor;
329 }
330
331 scm = get_color_mapping_procs_subclass(dev);
332 cm_procs = scm.procs;
333
334 hash.rend_hash = gsCMM_REPLACE;
335 hash.des_hash = dev->color_info.num_components;
336 hash.src_hash = data_cs;
337 hash.link_hashcode = data_cs + hash.des_hash * 256 + hash.rend_hash * 4096;
338
339 /* Check the cache for a hit. */
340 result = gsicc_findcachelink(hash, pgs->icc_link_cache, false, false);
341 if (result != NULL) {
342 return result;
343 }
344 /* If not, then lets create a new one. This may actually return a link if
345 another thread has already created it while we were trying to do so */
346 if (gsicc_alloc_link_entry(pgs->icc_link_cache, &result, hash, false, false))
347 return result;
348
349 if (result == NULL)
350 return result;
351
352 /* Now compute the link contents */
353 /* We (this thread) owns this link, so we can update it */
354 result->procs.map_buffer = gsicc_rcm_transform_color_buffer;
355 result->procs.map_color = gsicc_rcm_transform_color;
356 result->procs.free_link = gsicc_rcm_freelink;
357 result->hashcode = hash;
358 result->is_identity = false;
359 rcm_link = (rcm_link_t *) gs_alloc_bytes(mem, sizeof(rcm_link_t),
360 "gsicc_rcm_get_link");
361 if (rcm_link == NULL)
362 return NULL;
363 result->link_handle = (void*) rcm_link;
364 rcm_link->memory = mem;
365 rcm_link->num_out = min(dev->color_info.num_components,
366 GS_CLIENT_COLOR_MAX_COMPONENTS);
367 rcm_link->data_cs_in = data_cs;
368 rcm_link->cm_procs.map_cmyk = cm_procs->map_cmyk;
369 rcm_link->cm_procs.map_rgb = cm_procs->map_rgb;
370 rcm_link->cm_procs.map_gray = cm_procs->map_gray;
371
372 switch (data_cs) {
373 case gsGRAY:
374 rcm_link->num_in = 1;
375 break;
376 case gsRGB:
377 case gsCIELAB:
378 rcm_link->num_in = 3;
379 break;
380 case gsCMYK:
381 rcm_link->num_in = 4;
382 break;
383 default:
384 result->procs.free_link(result);
385 return NULL;
386 }
387 /* Likely set if we have something like a table or procs */
388 rcm_link->context = NULL;
389
390 result->num_input = rcm_link->num_in;
391 result->num_output = rcm_link->num_out;
392 result->link_handle = rcm_link;
393 result->hashcode.link_hashcode = hash.link_hashcode;
394 result->hashcode.des_hash = hash.des_hash;
395 result->hashcode.src_hash = hash.src_hash;
396 result->hashcode.rend_hash = hash.rend_hash;
397 result->includes_softproof = false;
398 result->includes_devlink = false;
399 result->is_identity = false; /* Always do replacement for demo */
400
401 /* Set up for monitoring non gray color spaces */
402 if (pageneutralcolor && data_cs != gsGRAY)
403 gsicc_mcm_set_link(result);
404
405 result->valid = true;
406 /* Now release any tasks/threads waiting for these contents by unlocking it */
407 gx_monitor_leave(result->lock); /* done with updating, let everyone run */
408
409 return result;
410 }
411