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 /* Sample implementation for client custom processing of color spaces. */
17 
18 /*
19  * If this flag is 1 then we also do custom processing for the DeviceGray,
20  * DeviceRGB, and DeviceCMYK color spaces.  For these color spaces, we
21  * convert all text to shades of red, all images to shades of green and
22  * all lines and fills to shades of blue.  if the flag is 0 then our example
23  * only handles PANTONE colors (see comment below).
24  */
25 
26 /* Added these two defines so that I could compare to ghost script rendering color */
27 
28 #define OBJECT_TYPE_EXAMPLE 1		/* 0 --> disabled, 1 --> enabled */
29 #define PANTONE_ONLY 0
30 /*
31  * This module has been created to demonstrate how to support the use of
32  * PANTONE colors to the Ghostscript graphics library.  PANTONE colors
33  * are specified in both PostScript and PDF files via the use of DeviceN
34  * or Separation color spaces.
35  *
36  * PANTONE is a registered trademark and PANTONE colors are a
37  * licensed product of Pantone Inc. See http://www.pantone.com
38  * for more information.
39  *
40  * See the comments at the start of src/gsnamecl.c for description of the
41  * client color processing routines.
42  *
43  * Since this is only a 'demo' implementation, the example implementation does
44  * not have some features which might be expected in a 'real' implementation.
45  *
46  * 1.  The Pantone color data table does not have actual entries for all
47  *     of the different Pantone colors.  This data is not included since
48  *     the values are dependent upon specific details of the output device,
49  *     inks, etc.
50  * 2.  Colors in PostScript and PDF are specified with by values between
51  *     0 and 1.  The output colorant values are scaled linearly.
52  * 3.  DeviceN color spaces can specify multiple colors.  However this
53  *     implementation assumes that if a PANTONE color is specified in a
54  *     DeviceN color space, then only PANTONE colors or CMYK are present.
55  *     This was done to keep the code simple.  If other colors are present,
56  *     then this implementation falls back to using the alternate color space
57  *     specified with the DeviceN color space.  (This is the normal PS
58  *     and PDF operation.)
59  *
60  * See also src/zsncdummy.c for an example custom color callback.
61  */
62 
63 #include "stdpre.h"
64 #include "math_.h"
65 #include "memory_.h"
66 #include "gx.h"
67 #include "gserrors.h"
68 #include "gscdefs.h"
69 #include "gscspace.h"
70 #include "gxcspace.h"
71 #include "gscie.h"
72 #include "gsicc.h"
73 #include "gxdevice.h"
74 #include "gzstate.h"
75 #include "gsutil.h"
76 #include "gxcie.h"
77 #include "gsncdummy.h"
78 
79 #if ENABLE_CUSTOM_COLOR_CALLBACK		/* Defined in src/gsnamecl.h */
80 
81 /*
82  * Since this is only a 'demo' list, the list does have not entries for all
83  * of the different PANTONE colors.  Creation of a real list is left as an
84  * exercise for the user.
85  */
86 const pantone_list_t pantone_list[] = {
87     { "PantoneCyan",	1, 0, 0, 0 },
88     { "PantoneMagenta",	0, 1, 0, 0 },
89     { "PantoneYellow",	0, 0, 1, 0 },
90     { "PantoneBlack",	0, 0, 0, 1 },
91     { "Orange",	0, 1, .5, 0 }
92 };
93 
94 /*
95  * We will handle color spaces that include both PANTONE colors, CMYK, and
96  * 'None'.  'None' is a special case in DeviceN color spaces.  It has no
97  * effects upon the output color but it can be present in DeviceN color
98  * spaces in a PDF file.  To simplify the code, we need pantone index values
99  * for these five 'colors'.
100  */
101 #define PANTONE_NONE    count_of(pantone_list)
102 #define PANTONE_CYAN    (PANTONE_NONE + 1)
103 #define PANTONE_MAGENTA (PANTONE_NONE + 2)
104 #define PANTONE_YELLOW  (PANTONE_NONE + 3)
105 #define PANTONE_BLACK   (PANTONE_NONE + 4)
106 
107 /* Compare two names */
108 #define compare_names(name1, name_size1, name2, name_size2) \
109     (name_size1 == name_size2 && \
110         (memcmp((const char *)name1, (const char *)name2, name_size1) == 0))
111 
112 /*
113  * Define a structure for holding our client specific data.  In our demo,
114  * we are only supporting Separation and DeviceN color spaces.  To make
115  * life simpler, we are using the same data structure for both types
116  * of color spaces.
117  */
118 typedef struct demo_color_space_data_s {
119     /*
120      * All client color space data blocks must begin with a routine for
121      * handling the reference counts for the data block.
122      */
123     cs_proc_adjust_client_cspace_count((*client_adjust_cspace_count));
124 
125     /* Use a reference count for knowing when to release the data block. */
126     int ref_count;
127 
128     /* the memory allocator used */
129     gs_memory_t *memory;
130 
131     /* A flag which indicates the client wants to process the color space. */
132     bool client_is_going_to_handle_color_space;
133 
134     /*
135      * We store an index into our Pantone color translation table for each
136      * colorant in the color space.
137      */
138     int color_index[GS_CLIENT_COLOR_MAX_COMPONENTS];
139     gs_imager_state *CIEtoXYZ_pis;	/* Used to map CIE spaces to XYZ */
140                                         /* refer to gx_cie_to_xyz_alloc	 */
141 } demo_color_space_data_t;
142 
143 gs_private_st_ptrs1(st_demo_color_space_data, demo_color_space_data_t,
144             "demo_color_space_data_t", demo_color_space_enum_ptrs,
145             demo_color_space_reloc_ptrs, CIEtoXYZ_pis);
146 
147 /*
148  * Dummy install routine for color spaces which are not handled by the client.
149  */
150 static bool
client_install_no_op(client_custom_color_params_t * pparams,gs_color_space * pcs,gs_state * pgs)151 client_install_no_op(client_custom_color_params_t * pparams,
152             gs_color_space * pcs, gs_state * pgs)
153 {
154     return false;	/* Do nothing */
155 }
156 
157 /*
158  * Adjust the reference count on our client data,
159  * freeing it if necessary.
160  */
161 static void
client_adjust_cspace_count(const gs_color_space * pcs,int delta)162 client_adjust_cspace_count(const gs_color_space * pcs, int delta)
163 {
164     demo_color_space_data_t * pdata =
165         (demo_color_space_data_t *)(pcs->pclient_color_space_data);
166 
167     pdata->ref_count += delta;
168     if (pdata->ref_count <= 0) {
169         /* Free up the CIE to XYZ imager state if it was allocated */
170         if (pdata->CIEtoXYZ_pis) {
171             gx_cie_to_xyz_free(pdata->CIEtoXYZ_pis);
172         }
173         gs_free_object(pdata->memory, pdata, "client_adjust_cspace_count(pdata)");
174     }
175 }
176 
177 /*
178  * Allocate a data block for holding our data for the client specific
179  * data for a color space.  In our demo, we are only supporting the
180  * Separation and DeviceN color spaces.  We use a single data structure
181  * to make the code simpler. We also provide sample hooks for a client
182  * that wants to convert ALL CIEBased color spaces to XYZ (or Lab).
183  */
184 static demo_color_space_data_t *
allocate_client_data_block(int initial_ref_count,gs_memory_t * mem)185 allocate_client_data_block(int initial_ref_count, gs_memory_t *mem)
186 {
187     /*
188      * We allocate this with normal GC structure declarations since
189      * we need this to be able to allocate the gs_imager_state for XYZ
190      * conversion.
191      * Since this is in stable memory, we use a simple reference count.
192      * See client_adjust_cspace_count.
193      */
194     demo_color_space_data_t * pdata =
195         (demo_color_space_data_t *)gs_alloc_struct(mem, demo_color_space_data_t,
196                         &st_demo_color_space_data, "allocate_client_data_block(pdata)");
197 
198     if (pdata != NULL) {
199         memset(pdata, 0, sizeof(demo_color_space_data_t));
200         /*
201          * All client color space data blocks must have a pointer to a
202          * reference count adjust routine as their first field.
203          */
204         pdata->client_adjust_cspace_count = client_adjust_cspace_count;
205         pdata->ref_count = 1;
206         pdata->memory = mem;
207     }
208 
209     return pdata;
210 }
211 
212 static bool
client_install_generic(client_custom_color_params_t * pparams,gs_color_space * pcs,gs_state * pgs)213 client_install_generic(client_custom_color_params_t * pparams,
214             gs_color_space * pcs, gs_state * pgs)
215 {
216         demo_color_space_data_t * pclient_data;
217 
218         /* Exit if we have already installed this color space. */
219         if (pcs->pclient_color_space_data != NULL)
220                 return true;
221 
222         pclient_data = allocate_client_data_block(1, pcs->rc.memory->stable_memory);
223         pcs->pclient_color_space_data = (client_color_space_data_t *) pclient_data;
224         if (pclient_data)
225         {
226                 pclient_data->client_is_going_to_handle_color_space = 1;
227                 return true;
228         }
229         return false;
230 }
231 
232 /*
233  * Check if we want to use the PANTONE color processing logic for the given
234  * Separation color space.
235  */
236 static bool
client_pantone_install_Separation(client_custom_color_params_t * pparam,gs_color_space * pcs,gs_state * pgs)237 client_pantone_install_Separation(client_custom_color_params_t * pparam,
238                         gs_color_space * pcs, gs_state * pgs)
239 {
240     const gs_separation_name name = pcs->params.separation.sep_name;
241     int pan_index;
242     byte * pname;
243     uint name_size;
244     gx_device * dev = pgs->device;
245     int num_pantone_colors = count_of(pantone_list);
246     bool use_custom_color_callback = false;
247 
248     /* Exit if we have already installed this color space. */
249     if (pcs->pclient_color_space_data != NULL)
250                 return true;
251 
252     /*
253      * Get the character string and length for the component name.
254      */
255     pcs->params.separation.get_colorname_string(dev->memory, name,
256                                                 &pname, &name_size);
257     /*
258     * Compare the colorant name to those in our PANTONE color list.
259     */
260     for (pan_index = 0; pan_index < num_pantone_colors ; pan_index++) {
261         const char * pan_name = pantone_list[pan_index].name;
262 
263         if (compare_names(pname, name_size, pan_name, strlen(pan_name))) {
264             use_custom_color_callback = true;
265             break;
266         }
267     }
268 
269     if (use_custom_color_callback) {
270         demo_color_space_data_t * pclient_data =
271                 allocate_client_data_block(1, pcs->rc.memory->stable_memory);
272 
273         if (pclient_data == NULL)
274                 return false;
275         pclient_data->color_index[0] = pan_index;
276         pcs->pclient_color_space_data =
277                (client_color_space_data_t *) pclient_data;
278     }
279     return use_custom_color_callback;
280 }
281 
282 /*
283  * Check if we want to use the PANTONE color processing logic for the given
284  * DeviceN color space.
285  */
286 static bool
client_pantone_install_DeviceN(client_custom_color_params_t * pparam,gs_color_space * pcs,gs_state * pgs)287 client_pantone_install_DeviceN(client_custom_color_params_t * pparam,
288                         gs_color_space * pcs, gs_state * pgs)
289 {
290     const gs_separation_name *names = pcs->params.device_n.names;
291     int num_comp = pcs->params.device_n.num_components;
292     int i;
293     int pan_index;
294     byte * pname;
295     uint name_size;
296     gx_device * dev = pgs->device;
297     int num_pantone_colors = count_of(pantone_list);
298     bool pantone_found = false;
299     bool other_separation_found = false;
300     bool use_pantone;
301     const char none_str[] = "None";
302     const uint none_size = strlen(none_str);
303     const char cyan_str[] = "Cyan";
304     const uint cyan_size = strlen(cyan_str);
305     const char magenta_str[] = "Magenta";
306     const uint magenta_size = strlen(magenta_str);
307     const char yellow_str[] = "Yellow";
308     const uint yellow_size = strlen(yellow_str);
309     const char black_str[] = "Black";
310     const uint black_size = strlen(black_str);
311     int pantone_color_index[GS_CLIENT_COLOR_MAX_COMPONENTS];
312 
313     /* Exit if we have already installed this color space. */
314     if (pcs->pclient_color_space_data != NULL)
315                 return true;
316 
317     /*
318      * Now check the names of the color components.
319      */
320     for(i = 0; i < num_comp; i++ ) {
321         bool match = false;
322 
323         /*
324          * Get the character string and length for the component name.
325          */
326         pcs->params.device_n.get_colorname_string(dev->memory, names[i],
327                                                         &pname, &name_size);
328         /*
329          * Postscript does not include /None as a color component but it is
330          * allowed in PDF so we accept it.  We simply skip components named
331          * 'None'.
332          */
333         if (compare_names(none_str, none_size, pname, name_size)) {
334             pantone_color_index[i] = PANTONE_NONE;
335             continue;
336         }
337         /*
338          * Check if our color space includes the CMYK process colors.
339          */
340         if (compare_names(cyan_str, cyan_size, pname, name_size)) {
341             pantone_color_index[i] = PANTONE_CYAN;
342             continue;
343         }
344         if (compare_names(magenta_str, magenta_size, pname, name_size)) {
345             pantone_color_index[i] = PANTONE_MAGENTA;
346             continue;
347         }
348         if (compare_names(yellow_str, yellow_size, pname, name_size)) {
349             pantone_color_index[i] = PANTONE_YELLOW;
350             continue;
351         }
352         if (compare_names(black_str, black_size, pname, name_size)) {
353             pantone_color_index[i] = PANTONE_BLACK;
354             continue;
355         }
356         /*
357          * Compare the colorant name to those in our Pantone color list.
358          */
359         for (pan_index = 0; pan_index < num_pantone_colors ; pan_index++) {
360             const char * pan_name = pantone_list[pan_index].name;
361 
362             if (compare_names(pname, name_size, pan_name, strlen(pan_name))) {
363                 pantone_color_index[i] = pan_index;
364                 match = pantone_found = true;
365                 break;
366             }
367         }
368         if (!match) {		/* Exit if we find a non Pantone color */
369             other_separation_found = true;
370             break;
371         }
372     }
373     /*
374      * Handle this color space as a 'pantone color space' if we have only
375      * PANTONE colors and CMYK.  Any other separations will force us to
376      * use the normal Ghostscript processing for a DeviceN color space.
377      */
378     use_pantone = pantone_found && !other_separation_found;
379     if (use_pantone) {
380         demo_color_space_data_t * pclient_data =
381                 allocate_client_data_block(1, pcs->rc.memory->stable_memory);
382 
383         if (pclient_data == NULL)
384             return false;
385         for(i = 0; i < num_comp; i++ )
386             pclient_data->color_index[i] = pantone_color_index[i];
387         pcs->pclient_color_space_data =
388                (client_color_space_data_t *) pclient_data;
389     }
390     return use_pantone;
391 }
392 
393 /*
394  * Convert a set of color values in a 'PANTONE color space' into a device
395  * color values.
396  *
397  * This routine creates an equivalent CMYK color and then uses
398  * gx_remap_concrete_cmyk to convert this into device colorants.  Note:  It
399  * is possible to go directy to the output device colorants.  However the
400  * pantone_install_xxx routines should verify that the expected device
401  * colorants match the actual device colorants.  (For instance, Ghostscript
402  * can install temporary compositing devices for functions like handling
403  * PDF 1.4 transparency.  The compositing devices may have a process color
404  * models which differ from the final output device.)
405  */
406 static int
client_pantone_remap_color(client_custom_color_params_t * pparam,const frac * pconc,const demo_color_space_data_t * pparams,gx_device_color * pdc,const gs_imager_state * pis,gx_device * dev,gs_color_select_t select,int num_comp)407 client_pantone_remap_color(client_custom_color_params_t * pparam,
408         const frac * pconc, const demo_color_space_data_t * pparams,
409         gx_device_color * pdc, const gs_imager_state * pis, gx_device * dev,
410         gs_color_select_t select, int num_comp)
411 {
412     int i, pantone_index, cvalue;
413     int cyan = 0;
414     int magenta = 0;
415     int yellow = 0;
416     int black = 0;
417     frac cc, cm, cy, ck;
418     const pantone_list_t * plist;
419 
420     /*
421      * If the client color space data pointer is NULL then we are not processing
422      * this color space.  The rangecheck error will indicate that GS should do
423      * its normal color space processing.
424      */
425     if (pparams == NULL)
426         return_error(gs_error_rangecheck);
427 
428     /*
429      * Create a CMYK representation of the various colors in our color space.
430      * Note:  If we have multiple components, then we do a simple sum of the
431      * CMYK equivalent for each color.  If desired, a more complex handling is
432      * left to the user.
433      */
434     for (i = 0; i < num_comp; i++) {
435         cvalue = pconc[i];
436         pantone_index = pparams->color_index[i];
437         switch (pantone_index) {
438             case PANTONE_NONE:
439                 break;
440             case PANTONE_CYAN:
441                 cyan += cvalue;
442                 break;
443             case PANTONE_MAGENTA:
444                 magenta += cvalue;
445                 break;
446             case PANTONE_YELLOW:
447                 yellow += cvalue;
448                 break;
449             case PANTONE_BLACK:
450                 black += cvalue;
451                 break;
452             default:
453                 plist = &(pantone_list[pantone_index]);
454                 cyan += (int) floor(cvalue * plist->c);
455                 magenta += (int) floor(cvalue * plist->m);
456                 yellow += (int) floor(cvalue * plist->y);
457                 black += (int) floor(cvalue * plist->k);
458                 break;
459         }
460     }
461     /* Clamp our color values */
462     cc = (cyan > frac_1) ? frac_1 : (cyan < frac_0) ? frac_0 : cyan;
463     cm = (magenta > frac_1) ? frac_1 : (magenta < frac_0) ? frac_0 : magenta;
464     cy = (yellow > frac_1) ? frac_1 : (yellow < frac_0) ? frac_0 : yellow;
465     ck = (black > frac_1) ? frac_1 : (black < frac_0) ? frac_0 : black;
466     gx_remap_concrete_cmyk(cc, cm, cy, ck, pdc, pis, dev, select);
467     return 0;
468 }
469 
470 /*
471  * Convert a Separation color (with PANTONE colorants) into device color.
472  */
473 static int
client_pantone_remap_Separation(client_custom_color_params_t * pparam,const frac * pconc,const gs_color_space * pcs,gx_device_color * pdc,const gs_imager_state * pis,gx_device * dev,gs_color_select_t select)474 client_pantone_remap_Separation(client_custom_color_params_t * pparam,
475         const frac * pconc, const gs_color_space * pcs, gx_device_color * pdc,
476         const gs_imager_state * pis, gx_device * dev, gs_color_select_t select)
477 {
478     return client_pantone_remap_color(pparam, pconc,
479         (demo_color_space_data_t *)(pcs->pclient_color_space_data),
480         pdc, pis, dev, select, 1);
481 }
482 
483 /*
484  * Convert a DeviceN color (with PANTONE colorants) into device color.
485  */
486 static int
client_pantone_remap_DeviceN(client_custom_color_params_t * pparam,const frac * pconc,const gs_color_space * pcs,gx_device_color * pdc,const gs_imager_state * pis,gx_device * dev,gs_color_select_t select)487 client_pantone_remap_DeviceN(client_custom_color_params_t * pparam,
488         const frac * pconc, const gs_color_space * pcs, gx_device_color * pdc,
489         const gs_imager_state * pis, gx_device * dev, gs_color_select_t select)
490 {
491         return client_pantone_remap_color(pparam, pconc,
492         (demo_color_space_data_t *)(pcs->pclient_color_space_data),
493         pdc, pis, dev, select, gs_color_space_num_components(pcs));
494 }
495 
496 #if !PANTONE_ONLY
497 
498 /*
499  * Install a DeviceGray color space.
500  */
501 static bool
client_install_DeviceGray(client_custom_color_params_t * pparams,gs_color_space * pcs,gs_state * pgs)502 client_install_DeviceGray(client_custom_color_params_t * pparams,
503             gs_color_space * pcs, gs_state * pgs)
504 {
505     /* Nothing to do in our demo */
506     return true;
507 }
508 
509 /*
510  * For demo and debug purposes, make our colors a function of the
511  * intensity of the given colors and the object type.
512  */
513 static int
convert_intensity_into_device_color(const frac intensity,gx_device_color * pdc,const gs_imager_state * pis,gx_device * dev,gs_color_select_t select)514 convert_intensity_into_device_color(const frac intensity,
515         gx_device_color * pdc, const gs_imager_state * pis, gx_device * dev,
516         gs_color_select_t select)
517 {
518     frac cc, cm, cy, ck;
519 
520     switch (dev->graphics_type_tag & ~GS_DEVICE_ENCODES_TAGS) {
521         case GS_TEXT_TAG:		/* Make text red. */
522                 cc = ck = 0;
523                 cm = cy = frac_1 - intensity;
524                 break;
525         case GS_IMAGE_TAG:		/* Make images green. */
526                 cm = ck = 0;
527                 cc = cy = frac_1 - intensity;
528                 break;
529         case GS_PATH_TAG:		/* Make lines and fills blue. */
530         default:
531                 cy = ck = 0;
532                 cc = cm = frac_1 - intensity;
533                 break;
534     }
535 
536     /* Send CMYK colors to the device */
537     gx_remap_concrete_cmyk(cc, cm, cy, ck, pdc, pis, dev, select);
538     return 0;
539 }
540 
541 /*
542  * Convert a DeviceGray color into device color.
543  */
544 static int
client_remap_DeviceGray(client_custom_color_params_t * pparams,const frac * pconc,const gs_color_space * pcs,gx_device_color * pdc,const gs_imager_state * pis,gx_device * dev,gs_color_select_t select)545 client_remap_DeviceGray(client_custom_color_params_t * pparams,
546     const frac * pconc, const gs_color_space * pcs, gx_device_color * pdc,
547     const gs_imager_state * pis, gx_device * dev, gs_color_select_t select)
548 {
549 
550         #if OBJECT_TYPE_EXAMPLE
551 
552                  /* For demo and debug purposes, make our colors a function of the
553                  * intensity of the given colors and the object type. */
554 
555                 frac intensity = pconc[0];
556                 convert_intensity_into_device_color(intensity, pdc, pis, dev, select);
557 
558         #else
559 
560                 /* If desired, replace with your own color transformation */
561 
562                 gx_remap_concrete_gray(pconc[0], pdc, pis, dev, select);
563 
564         #endif
565 
566     return 0;
567 }
568 
569 /*
570  * Install a DeviceRGB color space.
571  */
572 static bool
client_install_DeviceRGB(client_custom_color_params_t * pparams,gs_color_space * pcs,gs_state * pgs)573 client_install_DeviceRGB(client_custom_color_params_t * pparams,
574             gs_color_space * pcs, gs_state * pgs)
575 {
576     /* Nothing to do in our demo */
577     dlprintf1("client_install_DeviceRGB ri = %d\n", pgs->renderingintent);
578     return true;
579 }
580 
581 /*
582  * Convert a DeviceRGB color into device color.
583  */
584 static int
client_remap_DeviceRGB(client_custom_color_params_t * pparams,const frac * pconc,const gs_color_space * pcs,gx_device_color * pdc,const gs_imager_state * pis,gx_device * dev,gs_color_select_t select)585 client_remap_DeviceRGB(client_custom_color_params_t * pparams,
586         const frac * pconc, const gs_color_space * pcs, gx_device_color * pdc,
587         const gs_imager_state * pis, gx_device * dev, gs_color_select_t select)
588 {
589 
590         #if OBJECT_TYPE_EXAMPLE
591 
592                 /* For demo and debug purposes, make our colors a function of the
593                  * intensity of the given colors and the object type. */
594 
595                 frac intensity = (frac)(pconc[0] * 0.30 + pconc[1] * 0.59 + pconc[2] * 0.11);
596                 convert_intensity_into_device_color(intensity, pdc, pis, dev, select);
597 
598         #else
599 
600                  /* If desired, replace with your own color transformation */
601 
602                  gx_remap_concrete_rgb(pconc[0], pconc[1], pconc[2], pdc, pis, dev, select);
603 
604         #endif
605 
606     return 0;
607 }
608 
609 /*
610  * Install a DeviceCMYK color space.
611  */
612 static bool
client_install_DeviceCMYK(client_custom_color_params_t * pparams,gs_color_space * pcs,gs_state * pgs)613 client_install_DeviceCMYK(client_custom_color_params_t * pparams,
614             gs_color_space * pcs, gs_state * pgs)
615 {
616     /* Nothing to do in our demo */
617     return true;
618 }
619 
620 /*
621  * Convert a DeviceGray color into device color.
622  */
623 static int
client_remap_DeviceCMYK(client_custom_color_params_t * pparams,const frac * pconc,const gs_color_space * pcs,gx_device_color * pdc,const gs_imager_state * pis,gx_device * dev,gs_color_select_t select)624 client_remap_DeviceCMYK(client_custom_color_params_t * pparams,
625         const frac * pconc, const gs_color_space * pcs, gx_device_color * pdc,
626         const gs_imager_state * pis, gx_device * dev, gs_color_select_t select)
627 {
628 
629         #if OBJECT_TYPE_EXAMPLE
630 
631            /*
632                  * For demo and debug purposes, make our colors a function of the
633                  * intensity of the given colors and the object type.  */
634 
635                 frac intensity = frac_1 - (frac)(pconc[0] * 0.30 + pconc[1] * 0.59
636                                 + pconc[2] * 0.11 + pconc[3]);
637 
638                 if (intensity < frac_0)
639                 intensity = frac_0;
640                 convert_intensity_into_device_color(intensity, pdc, pis, dev, select);
641 
642         #else
643 
644                  /* If desired, replace with your own color transformation */
645                  gx_remap_concrete_cmyk(pconc[0], pconc[1], pconc[2], pconc[3],pdc, pis, dev, select);
646 
647         #endif
648 
649     return 0;
650 }
651 
652 /*
653  * Convert a floating point color value into a fixed (frac) color value
654  * given a specified floating point range.
655  */
656 #define convert2frac(color, range) \
657         (color <= range.rmin) ? frac_0 \
658             : (color >= range.rmax)  ? frac_1 \
659                 : (frac) (frac_1 * \
660                         (color - range.rmin) / (range.rmax - range.rmin))
661 
662 static bool
client_install_CIEtoA(client_custom_color_params_t * pparams,gs_color_space * pcs,gs_state * pgs)663 client_install_CIEtoA(client_custom_color_params_t * pparams,
664             gs_color_space * pcs, gs_state * pgs)
665 {
666     /* get ready for converting to XYZ */
667     demo_color_space_data_t * pdata;
668 
669     /* Exit if we have already installed this color space. */
670     if (pcs->pclient_color_space_data != NULL)
671         return true;
672 
673     pdata = allocate_client_data_block(1, pcs->rc.memory->stable_memory);
674     pcs->pclient_color_space_data = (client_color_space_data_t *) pdata;
675     if (pdata)
676     {
677         int code;
678         gs_cie_a *pcie = pcs->params.a;
679         gs_sample_loop_params_t lp;
680         int i;
681 
682         pdata->client_is_going_to_handle_color_space = 1;
683 
684         /* Fill the caches we need in the CIE color space */
685         gs_cie_cache_init(&pcie->caches.DecodeA.floats.params, &lp,
686                           &pcie->RangeA, "DecodeA");
687         for (i = 0; i <= lp.N; ++i) {
688             float in = SAMPLE_LOOP_VALUE(i, lp);
689 
690             pcie->caches.DecodeA.floats.values[i] = (*pcie->DecodeA)(in, pcie);
691         }
692         gx_cie_load_common_cache(&pcie->common, pgs);
693         gs_cie_a_complete(pcie);
694         if ((code=gs_cie_cs_complete(pgs, true)) >= 0) {
695             /* Now allocate the conversion imager state in stable_memory */
696             /* so that the garbage collector won't free it		 */
697             code = gx_cie_to_xyz_alloc(&pdata->CIEtoXYZ_pis, pcs,
698                                         pcs->rc.memory->stable_memory);
699         }
700         if (code < 0) {
701             client_adjust_cspace_count(pcs, -1);  /* free it up */
702             return false;
703         }
704     }
705     return true;
706 }
707 
708 static bool
client_install_CIEtoXYZ(client_custom_color_params_t * pparams,gs_color_space * pcs,gs_state * pgs)709 client_install_CIEtoXYZ(client_custom_color_params_t * pparams,
710             gs_color_space * pcs, gs_state * pgs)
711 {
712     /* get ready for converting to XYZ */
713     demo_color_space_data_t * pdata;
714 
715     /* Exit if we have already installed this color space. */
716     if (pcs->pclient_color_space_data != NULL)
717         return true;
718 
719     pdata = allocate_client_data_block(1, pcs->rc.memory->stable_memory);
720     pcs->pclient_color_space_data = (client_color_space_data_t *) pdata;
721     if (pdata)
722     {
723         int code;
724         const gs_cie_abc *pcie;
725 
726         pdata->client_is_going_to_handle_color_space = 1;
727         switch (pcs->type->index) {
728           case gs_color_space_index_CIEDEF: {
729             gs_cie_def *pcie_def = pcs->params.def;
730 
731             pcie = (gs_cie_abc *)pcie_def;
732             CIE_LOAD_CACHE_BODY(pcie_def->caches_def.DecodeDEF, pcie_def->RangeDEF.ranges,
733                             &pcie_def->DecodeDEF, DecodeDEF_default, pcie_def,
734                             "DecodeDEF");
735             break;
736           }
737           case gs_color_space_index_CIEDEFG: {
738             gs_cie_defg *pcie_defg = pcs->params.defg;
739 
740             pcie = (gs_cie_abc *)pcie_defg;
741             CIE_LOAD_CACHE_BODY(pcie_defg->caches_defg.DecodeDEFG, pcie_defg->RangeDEFG.ranges,
742                             &pcie_defg->DecodeDEFG, DecodeDEFG_default, pcie_defg,
743                             "DecodeDEFG");
744             break;
745           }
746           case gs_color_space_index_CIEABC: {
747             pcie = pcs->params.abc;
748             break;
749           }
750           default:
751             /* can't happen since we only come here for certain color spaces */
752             return false;
753         }
754         /* Fill the caches we need in the CIE color space */
755         if ((code=gx_install_cie_abc((gs_cie_abc *)pcie, pgs)) >= 0) {
756             /* Now allocate the conversion imager state in stable_memory */
757             /* so that the garbage collector won't free it               */
758             code = gx_cie_to_xyz_alloc(&pdata->CIEtoXYZ_pis, pcs,
759                                     pcs->rc.memory->stable_memory);
760         }
761         if (code < 0) {
762             client_adjust_cspace_count(pcs, -1);  /* free it up */
763             return false;
764         }
765     }
766     return true;
767 }
768 
769 static bool
client_install_ICCtoXYZ(client_custom_color_params_t * pparams,gs_color_space * pcs,gs_state * pgs)770 client_install_ICCtoXYZ(client_custom_color_params_t * pparams,
771             gs_color_space * pcs, gs_state * pgs)
772 {
773     int code;
774     const gs_icc_params * picc_params = (const gs_icc_params *)&pcs->params.icc;
775     gs_cie_icc *    picc_info = picc_params->picc_info;
776     demo_color_space_data_t * pdata;
777 
778     if (pcs->pclient_color_space_data != NULL)
779         return true;
780 
781     pdata = allocate_client_data_block(1, pcs->rc.memory->stable_memory);
782     pcs->pclient_color_space_data = (client_color_space_data_t *) pdata;
783 
784         /* Need to initialize the client data.  The imager_state is what is needed in pdata->CIEtoXZY_ps */
785 
786     /* update the stub information used by the joint caches  */
787     gx_cie_load_common_cache(&picc_info->common, pgs);
788     gx_cie_common_complete(&picc_info->common);
789 
790     if ((code=gs_cie_cs_complete(pgs, true)) < 0) {
791         client_adjust_cspace_count(pcs, -1);  /* free it up  */
792         return false;
793     }
794 
795         /* Now allocate the conversion imager state in stable_memory	*/
796         /* so that the garbage collector won't free it			*/
797         code = gx_cie_to_xyz_alloc(&pdata->CIEtoXYZ_pis, pcs,
798                                 pcs->rc.memory->stable_memory);
799 
800         if (code < 0) {
801             client_adjust_cspace_count(pcs, -1);  /* free it up */
802             return false;
803         }
804 
805     return true;
806 }
807 
808 /*
809  * Convert a CIEBasedA color into device color.
810  */
811 static int
client_remap_CIEBasedA(client_custom_color_params_t * pparams,const gs_client_color * pc,const gs_color_space * pcs,gx_device_color * pdc,const gs_imager_state * pis,gx_device * dev,gs_color_select_t select)812 client_remap_CIEBasedA(client_custom_color_params_t * pparams,
813     const gs_client_color * pc, const gs_color_space * pcs,
814     gx_device_color * pdc, const gs_imager_state * pis, gx_device * dev,
815     gs_color_select_t select)
816 {
817     demo_color_space_data_t * pdata =
818         (demo_color_space_data_t *)(pcs->pclient_color_space_data);
819     frac gray = convert2frac(pc->paint.values[0], pcs->params.a->RangeA);
820 
821     /*** Demonstrate method to convert to XYZ ***/
822     if (pdata->CIEtoXYZ_pis) {
823         frac xyz[3];
824 
825         cs_concretize_color(pc, pcs, xyz, pdata->CIEtoXYZ_pis);
826         /* We don't really do anything with these values, but this */
827         /* is where a real client could convert to a device color  */
828         if_debug4('|', "[c]client_remap CIEA [%g] -> XYZ [%g, %g, %g]\n",
829                   pc->paint.values[0],
830                   frac2float(xyz[0]), frac2float(xyz[1]), frac2float(xyz[2]));
831 
832     }
833     /*
834      * For demo and debug purposes, make our colors a function of the
835      * intensity of the given color value and the object type.
836      */
837     return client_remap_DeviceGray(pparams, &gray, pcs, pdc, pis, dev, select);
838 }
839 
840 /*
841  * Convert a CIEBasedABC color into device color.
842  */
843 static int
client_remap_CIEBasedABC(client_custom_color_params_t * pparams,const gs_client_color * pc,const gs_color_space * pcs,gx_device_color * pdc,const gs_imager_state * pis,gx_device * dev,gs_color_select_t select)844 client_remap_CIEBasedABC(client_custom_color_params_t * pparams,
845     const gs_client_color * pc, const gs_color_space * pcs,
846     gx_device_color * pdc, const gs_imager_state * pis, gx_device * dev,
847     gs_color_select_t select)
848 {
849     demo_color_space_data_t * pdata =
850         (demo_color_space_data_t *)(pcs->pclient_color_space_data);
851     frac rgb[3];
852     int i;
853 
854     /*** Demonstrate method to convert to XYZ ***/
855     if (pdata->CIEtoXYZ_pis) {
856         frac xyz[3];
857 
858         cs_concretize_color(pc, pcs, xyz, pdata->CIEtoXYZ_pis);
859         /* We don't really do anything with these values, but this */
860         /* is where a real client could convert to a device color  */
861         if_debug6('|', "[c]client_remap CIEABC [%g, %g, %g] -> XYZ [%g, %g, %g]\n",
862                   pc->paint.values[0], pc->paint.values[1], pc->paint.values[2],
863                   frac2float(xyz[0]), frac2float(xyz[1]), frac2float(xyz[2]));
864     }
865     /*
866      * For demo and debug purposes, make our colors a function of the
867      * intensity of the given color value and the object type.  The color
868      * values could represent almost anything.  However we are assuming
869      * that they are RGB values.
870      */
871     for (i = 0; i < 3; i++)
872         rgb[i] = convert2frac(pc->paint.values[i],
873                         pcs->params.abc->RangeABC.ranges[i]);
874     return client_remap_DeviceRGB(pparams, rgb, pcs, pdc, pis, dev, select);
875 }
876 
877 /*
878  * Convert a CIEBasedDEF color into device color.
879  */
880 static int
client_remap_CIEBasedDEF(client_custom_color_params_t * pparams,const gs_client_color * pc,const gs_color_space * pcs,gx_device_color * pdc,const gs_imager_state * pis,gx_device * dev,gs_color_select_t select)881 client_remap_CIEBasedDEF(client_custom_color_params_t * pparams,
882     const gs_client_color * pc, const gs_color_space * pcs,
883     gx_device_color * pdc, const gs_imager_state * pis, gx_device * dev,
884     gs_color_select_t select)
885 {
886     demo_color_space_data_t * pdata =
887         (demo_color_space_data_t *)(pcs->pclient_color_space_data);
888     frac rgb[3];
889     int i;
890 
891     /*** Demonstrate method to convert to XYZ ***/
892     if (pdata->CIEtoXYZ_pis) {
893         frac xyz[3];
894 
895         cs_concretize_color(pc, pcs, xyz, pdata->CIEtoXYZ_pis);
896         /* We don't really do anything with these values, but this */
897         /* is where a real client could convert to a device color  */
898         if_debug6('|', "[c]client_remap CIEDEF [%g, %g, %g] -> XYZ [%g, %g, %g]\n",
899                   pc->paint.values[0], pc->paint.values[1], pc->paint.values[2],
900                   frac2float(xyz[0]), frac2float(xyz[1]), frac2float(xyz[2]));
901     }
902     /*
903      * For demo and debug purposes, make our colors a function of the
904      * intensity of the given color value and the object type.  The color
905      * values could represent almost anything.  However we are assuming
906      * that they are RGB values.
907      */
908     for (i = 0; i < 3; i++)
909         rgb[i] = convert2frac(pc->paint.values[i],
910                         pcs->params.def->RangeDEF.ranges[i]);
911     return client_remap_DeviceRGB(pparams, rgb, pcs, pdc, pis, dev, select);
912 }
913 
914 /*
915  * Convert a CIEBasedDEFG color into device color.
916  */
917 static int
client_remap_CIEBasedDEFG(client_custom_color_params_t * pparams,const gs_client_color * pc,const gs_color_space * pcs,gx_device_color * pdc,const gs_imager_state * pis,gx_device * dev,gs_color_select_t select)918 client_remap_CIEBasedDEFG(client_custom_color_params_t * pparams,
919     const gs_client_color * pc, const gs_color_space * pcs,
920     gx_device_color * pdc, const gs_imager_state * pis, gx_device * dev,
921     gs_color_select_t select)
922 {
923     demo_color_space_data_t * pdata =
924         (demo_color_space_data_t *)(pcs->pclient_color_space_data);
925     frac cmyk[4];
926     int i;
927 
928     /*** Demonstrate method to convert to XYZ ***/
929     if (pdata->CIEtoXYZ_pis) {
930         frac xyz[3];
931 
932         cs_concretize_color(pc, pcs, xyz, pdata->CIEtoXYZ_pis);
933         /* We don't really do anything with these values, but this */
934         /* is where a real client could convert to a device color  */
935         if_debug7('|', "[c]client_remap CIEDEFG [%g, %g, %g] -> XYZ [%g, %g, %g]\n",
936                   pc->paint.values[0], pc->paint.values[1], pc->paint.values[2],
937                   pc->paint.values[3],
938                   frac2float(xyz[0]), frac2float(xyz[1]), frac2float(xyz[2]));
939     }
940     /*
941      * For demo and debug purposes, make our colors a function of the
942      * intensity of the given color value and the object type.  The color
943      * values could represent almost anything.  However we are assuming
944      * that they are CMYK values.
945      */
946     for (i = 0; i < 4; i++)
947         cmyk[i] = convert2frac(pc->paint.values[i],
948                         pcs->params.defg->RangeDEFG.ranges[i]);
949     return client_remap_DeviceRGB(pparams, cmyk, pcs, pdc, pis, dev, select);
950 }
951 
952 /*
953  * Convert a ICCBased color into device color.
954  */
955 static int
client_remap_ICCBased(client_custom_color_params_t * pparams,const gs_client_color * pc,const gs_color_space * pcs,gx_device_color * pdc,const gs_imager_state * pis,gx_device * dev,gs_color_select_t select)956 client_remap_ICCBased(client_custom_color_params_t * pparams,
957     const gs_client_color * pc, const gs_color_space * pcs,
958     gx_device_color * pdc, const gs_imager_state * pis, gx_device * dev,
959     gs_color_select_t select)
960 {
961     demo_color_space_data_t * pdata =
962         (demo_color_space_data_t *)(pcs->pclient_color_space_data);
963     frac frac_color[GS_CLIENT_COLOR_MAX_COMPONENTS];
964     int i, num_values = pcs->params.icc.picc_info->num_components;
965 
966     /*** Demonstrate method to convert to XYZ ***/
967     if (pdata->CIEtoXYZ_pis) {
968 
969                 frac xyz[3];
970 
971                 cs_concretize_color(pc, pcs, xyz, pdata->CIEtoXYZ_pis);
972                 /* We don't really do anything with these values, but this */
973                 /* is where a real client could convert to a device color  */
974                 if_debug6('|', "[c]client_remap ICCBased [%g, %g, %g] -> XYZ [%g, %g, %g]\n",
975                           pc->paint.values[0], pc->paint.values[1], pc->paint.values[2],
976                           frac2float(xyz[0]), frac2float(xyz[1]), frac2float(xyz[2]));
977     }
978 
979     /*
980      * For demo and debug purposes, make our colors a function of the
981      * intensity of the given color value and the object type.  The color
982      * values could represent almost anything.  However based upon the
983      * number of color values, we are assuming that they are either
984      * gray, RGB, or CMYK values.
985      */
986     for (i = 0; i < num_values; i++)
987         frac_color[i] = convert2frac(pc->paint.values[i],
988                         pcs->params.icc.picc_info->Range.ranges[i]);
989     switch (num_values) {
990         case 0:
991         case 2:
992             return_error(gs_error_rangecheck);
993         case 1:
994             return client_remap_DeviceGray(pparams, frac_color, pcs,
995                                          pdc, pis, dev, select);
996         case 3:
997             return client_remap_DeviceRGB(pparams, frac_color, pcs,
998                                          pdc, pis, dev, select);
999         case 4:
1000         default:
1001             return client_remap_DeviceCMYK(pparams, frac_color, pcs,
1002                                          pdc, pis, dev, select);
1003     }
1004 }
1005 
1006 #undef convert2frac
1007 
1008 #endif 		/* NOT PANTONE_ONLY */
1009 
1010 #if PANTONE_ONLY
1011 
1012         /*
1013          * For PANTONE colors, we only need to handle Separation and DeviceN
1014          * color spaces.  These are the only color spaces that can have PANTONE
1015          * colors.
1016          */
1017         client_custom_color_procs_t demo_procs = {
1018                 client_install_no_op,		/* DeviceGray */
1019                 NULL,
1020                 client_install_no_op,		/* DeviceRGB */
1021                 NULL,
1022                 client_install_no_op,		/* DeviceCMYK */
1023                 NULL,
1024                 client_pantone_install_Separation,	/* Separation */
1025                 client_pantone_remap_Separation,
1026                 client_pantone_install_DeviceN,	/* DeviceN */
1027                 client_pantone_remap_DeviceN,
1028                 client_install_no_op,		/* CIEBasedA */
1029                 NULL,
1030                 client_install_no_op,		/* CIEBasedABC */
1031                 NULL,
1032                 client_install_no_op,		/* CIEBasedDEF */
1033                 NULL,
1034                 client_install_no_op,		/* CIEBasedDEFG */
1035                 NULL,
1036                 client_install_no_op,		/* ICCBased */
1037                 NULL
1038         };
1039 
1040 #else			/* Not PANTONE_ONLY special */
1041 
1042 /*
1043  * Client call back procedures for our demo which illustrates color
1044  * processing based upon object type.
1045  */
1046 client_custom_color_procs_t demo_procs = {
1047     client_install_DeviceGray,		/* DeviceGray */
1048     client_remap_DeviceGray,
1049     client_install_DeviceRGB,		/* DeviceRGB */
1050     client_remap_DeviceRGB,
1051     client_install_DeviceCMYK,		/* DeviceCMYK */
1052     client_remap_DeviceCMYK,
1053     client_pantone_install_Separation,	/* Separation */
1054     client_pantone_remap_Separation,
1055     client_pantone_install_DeviceN,	/* DeviceN */
1056     client_pantone_remap_DeviceN,
1057     client_install_CIEtoA,		/* CIEBasedA */
1058     client_remap_CIEBasedA,
1059     client_install_CIEtoXYZ,		/* CIEBasedABC */
1060     client_remap_CIEBasedABC,
1061     client_install_CIEtoXYZ,		/* CIEBasedDEF */
1062     client_remap_CIEBasedDEF,
1063     client_install_CIEtoXYZ,		/* CIEBasedDEFG */
1064     client_remap_CIEBasedDEFG,
1065     client_install_ICCtoXYZ,		/* ICCBased */
1066     client_remap_ICCBased
1067 };
1068 
1069 #endif		/* PANTONE_ONLY_EXAMPLE */
1070 
1071 #endif		/* ENABLE_CUSTOM_COLOR_CALLBACK */
1072