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