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