xref: /plan9/sys/src/cmd/gs/src/gdevpsd.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
1 /* Copyright (C) 2002 artofcode LLC.  All rights reserved.
2 
3   This software is provided AS-IS with no warranty, either express or
4   implied.
5 
6   This software is distributed under license and may not be copied,
7   modified or distributed except as expressly authorized under the terms
8   of the license contained in the file LICENSE in this distribution.
9 
10   For more information about licensing, please refer to
11   http://www.ghostscript.com/licensing/. For information on
12   commercial licensing, go to http://www.artifex.com/licensing/ or
13   contact Artifex Software, Inc., 101 Lucas Valley Road #110,
14   San Rafael, CA  94903, U.S.A., +1(415)492-9861.
15 */
16 
17 /*$Id: gdevpsd.c,v 1.23 2005/08/30 06:38:44 igor Exp $ */
18 /* PhotoShop (PSD) export device, supporting DeviceN color models. */
19 
20 #include "math_.h"
21 #include "gdevprn.h"
22 #include "gsparam.h"
23 #include "gscrd.h"
24 #include "gscrdp.h"
25 #include "gxlum.h"
26 #include "gdevdcrd.h"
27 #include "gstypes.h"
28 #include "icc.h"
29 #include "gxdcconv.h"
30 #include "gdevdevn.h"
31 #include "gsequivc.h"
32 
33 /* Enable logic for a local ICC output profile. */
34 #define ENABLE_ICC_PROFILE 0
35 
36 /* Define the device parameters. */
37 #ifndef X_DPI
38 #  define X_DPI 72
39 #endif
40 #ifndef Y_DPI
41 #  define Y_DPI 72
42 #endif
43 
44 /* The device descriptor */
45 private dev_proc_open_device(psd_prn_open);
46 private dev_proc_get_params(psd_get_params);
47 private dev_proc_put_params(psd_put_params);
48 private dev_proc_print_page(psd_print_page);
49 private dev_proc_map_color_rgb(psd_map_color_rgb);
50 private dev_proc_get_color_mapping_procs(get_psdrgb_color_mapping_procs);
51 private dev_proc_get_color_mapping_procs(get_psd_color_mapping_procs);
52 private dev_proc_get_color_comp_index(psd_get_color_comp_index);
53 private dev_proc_encode_color(psd_encode_color);
54 private dev_proc_decode_color(psd_decode_color);
55 private dev_proc_update_spot_equivalent_colors(psd_update_spot_equivalent_colors);
56 
57 /* This is redundant with color_info.cm_name. We may eliminate this
58    typedef and use the latter string for everything. */
59 typedef enum {
60     psd_DEVICE_GRAY,
61     psd_DEVICE_RGB,
62     psd_DEVICE_CMYK,
63     psd_DEVICE_N
64 } psd_color_model;
65 
66 /*
67  * A structure definition for a DeviceN type device
68  */
69 typedef struct psd_device_s {
70     gx_device_common;
71     gx_prn_device_common;
72 
73     /*        ... device-specific parameters ... */
74 
75     gs_devn_params devn_params;		/* DeviceN generated parameters */
76 
77     equivalent_cmyk_color_params equiv_cmyk_colors;
78 
79     psd_color_model color_model;
80 
81     /* ICC color profile objects, for color conversion. */
82     char profile_rgb_fn[256];
83     icmLuBase *lu_rgb;
84     int lu_rgb_outn;
85 
86     char profile_cmyk_fn[256];
87     icmLuBase *lu_cmyk;
88     int lu_cmyk_outn;
89 
90     char profile_out_fn[256];
91     icmLuBase *lu_out;
92 } psd_device;
93 
94 /* GC procedures */
95 private
ENUM_PTRS_WITH(psd_device_enum_ptrs,psd_device * pdev)96 ENUM_PTRS_WITH(psd_device_enum_ptrs, psd_device *pdev)
97 {
98     if (index < pdev->devn_params.separations.num_separations)
99 	ENUM_RETURN(pdev->devn_params.separations.names[index].data);
100     ENUM_PREFIX(st_device_printer,
101 		    pdev->devn_params.separations.num_separations);
102 }
103 
104 ENUM_PTRS_END
RELOC_PTRS_WITH(psd_device_reloc_ptrs,psd_device * pdev)105 private RELOC_PTRS_WITH(psd_device_reloc_ptrs, psd_device *pdev)
106 {
107     RELOC_PREFIX(st_device_printer);
108     {
109 	int i;
110 
111 	for (i = 0; i < pdev->devn_params.separations.num_separations; ++i) {
112 	    RELOC_PTR(psd_device, devn_params.separations.names[i].data);
113 	}
114     }
115 }
116 RELOC_PTRS_END
117 
118 /* Even though psd_device_finalize is the same as gx_device_finalize, */
119 /* we need to implement it separately because st_composite_final */
120 /* declares all 3 procedures as private. */
121 private void
psd_device_finalize(void * vpdev)122 psd_device_finalize(void *vpdev)
123 {
124     gx_device_finalize(vpdev);
125 }
126 
127 gs_private_st_composite_final(st_psd_device, psd_device,
128     "psd_device", psd_device_enum_ptrs, psd_device_reloc_ptrs,
129     psd_device_finalize);
130 
131 /*
132  * Macro definition for psd device procedures
133  */
134 #define device_procs(get_color_mapping_procs)\
135 {	psd_prn_open,\
136 	gx_default_get_initial_matrix,\
137 	NULL,				/* sync_output */\
138 	gdev_prn_output_page,		/* output_page */\
139 	gdev_prn_close,			/* close */\
140 	NULL,				/* map_rgb_color - not used */\
141 	psd_map_color_rgb,		/* map_color_rgb */\
142 	NULL,				/* fill_rectangle */\
143 	NULL,				/* tile_rectangle */\
144 	NULL,				/* copy_mono */\
145 	NULL,				/* copy_color */\
146 	NULL,				/* draw_line */\
147 	NULL,				/* get_bits */\
148 	psd_get_params,			/* get_params */\
149 	psd_put_params,			/* put_params */\
150 	NULL,				/* map_cmyk_color - not used */\
151 	NULL,				/* get_xfont_procs */\
152 	NULL,				/* get_xfont_device */\
153 	NULL,				/* map_rgb_alpha_color */\
154 	gx_page_device_get_page_device,	/* get_page_device */\
155 	NULL,				/* get_alpha_bits */\
156 	NULL,				/* copy_alpha */\
157 	NULL,				/* get_band */\
158 	NULL,				/* copy_rop */\
159 	NULL,				/* fill_path */\
160 	NULL,				/* stroke_path */\
161 	NULL,				/* fill_mask */\
162 	NULL,				/* fill_trapezoid */\
163 	NULL,				/* fill_parallelogram */\
164 	NULL,				/* fill_triangle */\
165 	NULL,				/* draw_thin_line */\
166 	NULL,				/* begin_image */\
167 	NULL,				/* image_data */\
168 	NULL,				/* end_image */\
169 	NULL,				/* strip_tile_rectangle */\
170 	NULL,				/* strip_copy_rop */\
171 	NULL,				/* get_clipping_box */\
172 	NULL,				/* begin_typed_image */\
173 	NULL,				/* get_bits_rectangle */\
174 	NULL,				/* map_color_rgb_alpha */\
175 	NULL,				/* create_compositor */\
176 	NULL,				/* get_hardware_params */\
177 	NULL,				/* text_begin */\
178 	NULL,				/* finish_copydevice */\
179 	NULL,				/* begin_transparency_group */\
180 	NULL,				/* end_transparency_group */\
181 	NULL,				/* begin_transparency_mask */\
182 	NULL,				/* end_transparency_mask */\
183 	NULL,				/* discard_transparency_layer */\
184 	get_color_mapping_procs,	/* get_color_mapping_procs */\
185 	psd_get_color_comp_index,	/* get_color_comp_index */\
186 	psd_encode_color,		/* encode_color */\
187 	psd_decode_color,		/* decode_color */\
188 	NULL,				/* pattern_manage */\
189 	NULL,				/* fill_rectangle_hl_color */\
190 	NULL,				/* include_color_space */\
191 	NULL,				/* fill_linear_color_scanline */\
192 	NULL,				/* fill_linear_color_trapezoid */\
193 	NULL,				/* fill_linear_color_triangle */\
194 	psd_update_spot_equivalent_colors /* update_spot_equivalent_colors */\
195 }
196 
197 
198 private fixed_colorant_name DeviceGrayComponents[] = {
199 	"Gray",
200 	0		/* List terminator */
201 };
202 
203 private fixed_colorant_name DeviceRGBComponents[] = {
204 	"Red",
205 	"Green",
206 	"Blue",
207 	0		/* List terminator */
208 };
209 
210 #define psd_device_body(procs, dname, ncomp, pol, depth, mg, mc, cn)\
211     std_device_full_body_type_extended(psd_device, &procs, dname,\
212 	  &st_psd_device,\
213 	  (int)((long)(DEFAULT_WIDTH_10THS) * (X_DPI) / 10),\
214 	  (int)((long)(DEFAULT_HEIGHT_10THS) * (Y_DPI) / 10),\
215 	  X_DPI, Y_DPI,\
216     	  GX_DEVICE_COLOR_MAX_COMPONENTS,	/* MaxComponents */\
217 	  ncomp,		/* NumComp */\
218 	  pol,			/* Polarity */\
219 	  depth, 0,		/* Depth, GrayIndex */\
220 	  mg, mc,		/* MaxGray, MaxColor */\
221 	  mg + 1, mc + 1,	/* DitherGray, DitherColor */\
222 	  GX_CINFO_SEP_LIN,	/* Linear & Separable */\
223 	  cn,			/* Process color model name */\
224 	  0, 0,			/* offsets */\
225 	  0, 0, 0, 0		/* margins */\
226 	),\
227 	prn_device_body_rest_(psd_print_page)
228 
229 /*
230  * PSD device with RGB process color model.
231  */
232 private const gx_device_procs spot_rgb_procs = device_procs(get_psdrgb_color_mapping_procs);
233 
234 const psd_device gs_psdrgb_device =
235 {
236     psd_device_body(spot_rgb_procs, "psdrgb", 3, GX_CINFO_POLARITY_ADDITIVE, 24, 255, 255, "DeviceRGB"),
237     /* devn_params specific parameters */
238     { 8,	/* Bits per color - must match ncomp, depth, etc. above */
239       DeviceRGBComponents,	/* Names of color model colorants */
240       3,			/* Number colorants for RGB */
241       0,			/* MaxSeparations has not been specified */
242       {0},			/* SeparationNames */
243       0,			/* SeparationOrder names */
244       {0, 1, 2, 3, 4, 5, 6, 7 }	/* Initial component SeparationOrder */
245     },
246     { true },			/* equivalent CMYK colors for spot colors */
247     /* PSD device specific parameters */
248     psd_DEVICE_RGB,		/* Color model */
249 };
250 
251 /*
252  * Select the default number of components based upon the number of bits
253  * that we have in a gx_color_index
254  */
255 #define NC ((arch_sizeof_color_index <= 8) ? arch_sizeof_color_index : 8)
256 
257 /*
258  * PSD device with CMYK process color model and spot color support.
259  */
260 private const gx_device_procs spot_cmyk_procs
261 		= device_procs(get_psd_color_mapping_procs);
262 
263 const psd_device gs_psdcmyk_device =
264 {
265     psd_device_body(spot_cmyk_procs, "psdcmyk", NC, GX_CINFO_POLARITY_SUBTRACTIVE, NC * 8, 255, 255, "DeviceCMYK"),
266     /* devn_params specific parameters */
267     { 8,	/* Bits per color - must match ncomp, depth, etc. above */
268       DeviceCMYKComponents,	/* Names of color model colorants */
269       4,			/* Number colorants for CMYK */
270       NC,			/* MaxSeparations has not been specified */
271       {0},			/* SeparationNames */
272       0,			/* SeparationOrder names */
273       {0, 1, 2, 3, 4, 5, 6, 7 }	/* Initial component SeparationOrder */
274     },
275     { true },			/* equivalent CMYK colors for spot colors */
276     /* PSD device specific parameters */
277     psd_DEVICE_CMYK,		/* Color model */
278 };
279 
280 #undef NC
281 
282 /* Open the psd devices */
283 int
psd_prn_open(gx_device * pdev)284 psd_prn_open(gx_device * pdev)
285 {
286     int code = gdev_prn_open(pdev);
287 
288     set_linear_color_bits_mask_shift(pdev);
289     pdev->color_info.separable_and_linear = GX_CINFO_SEP_LIN;
290     return code;
291 }
292 
293 /*
294  * The following procedures are used to map the standard color spaces into
295  * the color components for the psdrgb device.
296  */
297 private void
gray_cs_to_psdrgb_cm(gx_device * dev,frac gray,frac out[])298 gray_cs_to_psdrgb_cm(gx_device * dev, frac gray, frac out[])
299 {
300     int i = ((psd_device *)dev)->devn_params.separations.num_separations;
301 
302     out[0] = out[1] = out[2] = gray;
303     for(; i>0; i--)			/* Clear spot colors */
304         out[2 + i] = 0;
305 }
306 
307 private void
rgb_cs_to_psdrgb_cm(gx_device * dev,const gs_imager_state * pis,frac r,frac g,frac b,frac out[])308 rgb_cs_to_psdrgb_cm(gx_device * dev, const gs_imager_state *pis,
309 				  frac r, frac g, frac b, frac out[])
310 {
311     int i = ((psd_device *)dev)->devn_params.separations.num_separations;
312 
313     out[0] = r;
314     out[1] = g;
315     out[2] = b;
316     for(; i>0; i--)			/* Clear spot colors */
317         out[2 + i] = 0;
318 }
319 
320 private void
cmyk_cs_to_psdrgb_cm(gx_device * dev,frac c,frac m,frac y,frac k,frac out[])321 cmyk_cs_to_psdrgb_cm(gx_device * dev,
322 			frac c, frac m, frac y, frac k, frac out[])
323 {
324     int i = ((psd_device *)dev)->devn_params.separations.num_separations;
325 
326     color_cmyk_to_rgb(c, m, y, k, NULL, out);
327     for(; i>0; i--)			/* Clear spot colors */
328         out[2 + i] = 0;
329 }
330 
331 /* Color mapping routines for the psdcmyk device */
332 
333 private void
gray_cs_to_psdcmyk_cm(gx_device * dev,frac gray,frac out[])334 gray_cs_to_psdcmyk_cm(gx_device * dev, frac gray, frac out[])
335 {
336     int * map = ((psd_device *) dev)->devn_params.separation_order_map;
337 
338     gray_cs_to_devn_cm(dev, map, gray, out);
339 }
340 
341 private void
rgb_cs_to_psdcmyk_cm(gx_device * dev,const gs_imager_state * pis,frac r,frac g,frac b,frac out[])342 rgb_cs_to_psdcmyk_cm(gx_device * dev, const gs_imager_state *pis,
343 			   frac r, frac g, frac b, frac out[])
344 {
345     int * map = ((psd_device *) dev)->devn_params.separation_order_map;
346 
347     rgb_cs_to_devn_cm(dev, map, pis, r, g, b, out);
348 }
349 
350 private void
cmyk_cs_to_psdcmyk_cm(gx_device * dev,frac c,frac m,frac y,frac k,frac out[])351 cmyk_cs_to_psdcmyk_cm(gx_device * dev,
352 			frac c, frac m, frac y, frac k, frac out[])
353 {
354     int * map = ((psd_device *) dev)->devn_params.separation_order_map;
355 
356     cmyk_cs_to_devn_cm(dev, map, c, m, y, k, out);
357 }
358 
359 private void
cmyk_cs_to_spotn_cm(gx_device * dev,frac c,frac m,frac y,frac k,frac out[])360 cmyk_cs_to_spotn_cm(gx_device * dev, frac c, frac m, frac y, frac k, frac out[])
361 {
362     psd_device *xdev = (psd_device *)dev;
363     int n = xdev->devn_params.separations.num_separations;
364     icmLuBase *luo = xdev->lu_cmyk;
365     int i;
366 
367     if (luo != NULL) {
368 	double in[4];
369 	double tmp[MAX_CHAN];
370 	int outn = xdev->lu_cmyk_outn;
371 
372 	in[0] = frac2float(c);
373 	in[1] = frac2float(m);
374 	in[2] = frac2float(y);
375 	in[3] = frac2float(k);
376 	luo->lookup(luo, tmp, in);
377 	for (i = 0; i < outn; i++)
378 	    out[i] = float2frac(tmp[i]);
379 	for (; i < n + 4; i++)
380 	    out[i] = 0;
381     } else {
382 	/* If no profile given, assume CMYK */
383 	out[0] = c;
384 	out[1] = m;
385 	out[2] = y;
386 	out[3] = k;
387 	for(i = 0; i < n; i++)			/* Clear spot colors */
388 	    out[4 + i] = 0;
389     }
390 }
391 
392 private void
gray_cs_to_spotn_cm(gx_device * dev,frac gray,frac out[])393 gray_cs_to_spotn_cm(gx_device * dev, frac gray, frac out[])
394 {
395     cmyk_cs_to_spotn_cm(dev, 0, 0, 0, (frac)(frac_1 - gray), out);
396 }
397 
398 private void
rgb_cs_to_spotn_cm(gx_device * dev,const gs_imager_state * pis,frac r,frac g,frac b,frac out[])399 rgb_cs_to_spotn_cm(gx_device * dev, const gs_imager_state *pis,
400 				   frac r, frac g, frac b, frac out[])
401 {
402     psd_device *xdev = (psd_device *)dev;
403     int n = xdev->devn_params.separations.num_separations;
404     icmLuBase *luo = xdev->lu_rgb;
405     int i;
406 
407     if (luo != NULL) {
408 	double in[3];
409 	double tmp[MAX_CHAN];
410 	int outn = xdev->lu_rgb_outn;
411 
412 	in[0] = frac2float(r);
413 	in[1] = frac2float(g);
414 	in[2] = frac2float(b);
415 	luo->lookup(luo, tmp, in);
416 	for (i = 0; i < outn; i++)
417 	    out[i] = float2frac(tmp[i]);
418 	for (; i < n + 4; i++)
419 	    out[i] = 0;
420     } else {
421 	frac cmyk[4];
422 
423 	color_rgb_to_cmyk(r, g, b, pis, cmyk);
424 	cmyk_cs_to_spotn_cm(dev, cmyk[0], cmyk[1], cmyk[2], cmyk[3],
425 			    out);
426     }
427 }
428 
429 private const gx_cm_color_map_procs psdRGB_procs = {
430     gray_cs_to_psdrgb_cm, rgb_cs_to_psdrgb_cm, cmyk_cs_to_psdrgb_cm
431 };
432 
433 private const gx_cm_color_map_procs psdCMYK_procs = {
434     gray_cs_to_psdcmyk_cm, rgb_cs_to_psdcmyk_cm, cmyk_cs_to_psdcmyk_cm
435 };
436 
437 private const gx_cm_color_map_procs psdN_procs = {
438     gray_cs_to_spotn_cm, rgb_cs_to_spotn_cm, cmyk_cs_to_spotn_cm
439 };
440 
441 /*
442  * These are the handlers for returning the list of color space
443  * to color model conversion routines.
444  */
445 private const gx_cm_color_map_procs *
get_psdrgb_color_mapping_procs(const gx_device * dev)446 get_psdrgb_color_mapping_procs(const gx_device * dev)
447 {
448     return &psdRGB_procs;
449 }
450 
451 private const gx_cm_color_map_procs *
get_psd_color_mapping_procs(const gx_device * dev)452 get_psd_color_mapping_procs(const gx_device * dev)
453 {
454     const psd_device *xdev = (const psd_device *)dev;
455 
456     if (xdev->color_model == psd_DEVICE_RGB)
457 	return &psdRGB_procs;
458     else if (xdev->color_model == psd_DEVICE_CMYK)
459 	return &psdCMYK_procs;
460     else if (xdev->color_model == psd_DEVICE_N)
461 	return &psdN_procs;
462     else
463 	return NULL;
464 }
465 
466 /*
467  * Encode a list of colorant values into a gx_color_index_value.
468  */
469 private gx_color_index
psd_encode_color(gx_device * dev,const gx_color_value colors[])470 psd_encode_color(gx_device *dev, const gx_color_value colors[])
471 {
472     int bpc = ((psd_device *)dev)->devn_params.bitspercomponent;
473     int drop = sizeof(gx_color_value) * 8 - bpc;
474     gx_color_index color = 0;
475     int i = 0;
476     int ncomp = dev->color_info.num_components;
477 
478     for (; i<ncomp; i++) {
479 	color <<= bpc;
480         color |= (colors[i] >> drop);
481     }
482     return (color == gx_no_color_index ? color ^ 1 : color);
483 }
484 
485 /*
486  * Decode a gx_color_index value back to a list of colorant values.
487  */
488 private int
psd_decode_color(gx_device * dev,gx_color_index color,gx_color_value * out)489 psd_decode_color(gx_device * dev, gx_color_index color, gx_color_value * out)
490 {
491     int bpc = ((psd_device *)dev)->devn_params.bitspercomponent;
492     int drop = sizeof(gx_color_value) * 8 - bpc;
493     int mask = (1 << bpc) - 1;
494     int i = 0;
495     int ncomp = dev->color_info.num_components;
496 
497     for (; i<ncomp; i++) {
498         out[ncomp - i - 1] = (gx_color_value) ((color & mask) << drop);
499 	color >>= bpc;
500     }
501     return 0;
502 }
503 
504 /*
505  * Convert a gx_color_index to RGB.
506  */
507 private int
psd_map_color_rgb(gx_device * dev,gx_color_index color,gx_color_value rgb[3])508 psd_map_color_rgb(gx_device *dev, gx_color_index color, gx_color_value rgb[3])
509 {
510     psd_device *xdev = (psd_device *)dev;
511 
512     if (xdev->color_model == psd_DEVICE_RGB)
513 	return psd_decode_color(dev, color, rgb);
514     /* TODO: return reasonable values. */
515     rgb[0] = 0;
516     rgb[1] = 0;
517     rgb[2] = 0;
518     return 0;
519 }
520 
521 /*
522  *  Device proc for updating the equivalent CMYK color for spot colors.
523  */
524 private int
psd_update_spot_equivalent_colors(gx_device * pdev,const gs_state * pgs)525 psd_update_spot_equivalent_colors(gx_device *pdev, const gs_state * pgs)
526 {
527     psd_device * psdev = (psd_device *)pdev;
528 
529     update_spot_equivalent_cmyk_colors(pdev, pgs,
530 		    &psdev->devn_params, &psdev->equiv_cmyk_colors);
531     return 0;
532 }
533 
534 #if ENABLE_ICC_PROFILE
535 private int
psd_open_profile(psd_device * xdev,char * profile_fn,icmLuBase ** pluo,int * poutn)536 psd_open_profile(psd_device *xdev, char *profile_fn, icmLuBase **pluo,
537 		 int *poutn)
538 {
539     icmFile *fp;
540     icc *icco;
541     icmLuBase *luo;
542 
543     dlprintf1("psd_open_profile %s\n", profile_fn);
544     fp = new_icmFileStd_name(profile_fn, (char *)"rb");
545     if (fp == NULL)
546 	return_error(gs_error_undefinedfilename);
547     icco = new_icc();
548     if (icco == NULL)
549 	return_error(gs_error_VMerror);
550     if (icco->read(icco, fp, 0))
551 	return_error(gs_error_rangecheck);
552     luo = icco->get_luobj(icco, icmFwd, icmDefaultIntent, icmSigDefaultData, icmLuOrdNorm);
553     if (luo == NULL)
554 	return_error(gs_error_rangecheck);
555     *pluo = luo;
556     luo->spaces(luo, NULL, NULL, NULL, poutn, NULL, NULL, NULL, NULL);
557     return 0;
558 }
559 
560 private int
psd_open_profiles(psd_device * xdev)561 psd_open_profiles(psd_device *xdev)
562 {
563     int code = 0;
564     if (xdev->lu_out == NULL && xdev->profile_out_fn[0]) {
565 	code = psd_open_profile(xdev, xdev->profile_out_fn,
566 				    &xdev->lu_out, NULL);
567     }
568     if (code >= 0 && xdev->lu_rgb == NULL && xdev->profile_rgb_fn[0]) {
569 	code = psd_open_profile(xdev, xdev->profile_rgb_fn,
570 				&xdev->lu_rgb, &xdev->lu_rgb_outn);
571     }
572     if (code >= 0 && xdev->lu_cmyk == NULL && xdev->profile_cmyk_fn[0]) {
573 	code = psd_open_profile(xdev, xdev->profile_cmyk_fn,
574 				&xdev->lu_cmyk, &xdev->lu_cmyk_outn);
575     }
576     return code;
577 }
578 #endif
579 
580 /* Get parameters.  We provide a default CRD. */
581 private int
psd_get_params(gx_device * pdev,gs_param_list * plist)582 psd_get_params(gx_device * pdev, gs_param_list * plist)
583 {
584     psd_device *xdev = (psd_device *)pdev;
585     int code;
586 #if ENABLE_ICC_PROFILE
587     gs_param_string pos;
588     gs_param_string prgbs;
589     gs_param_string pcmyks;
590 #endif
591 
592     code = gdev_prn_get_params(pdev, plist);
593     if (code < 0)
594 	return code;
595     code = devn_get_params(pdev, plist,
596     	&(xdev->devn_params), &(xdev->equiv_cmyk_colors));
597     if (code < 0)
598 	return code;
599 
600 #if ENABLE_ICC_PROFILE
601     pos.data = (const byte *)xdev->profile_out_fn,
602 	pos.size = strlen(xdev->profile_out_fn),
603 	pos.persistent = false;
604     code = param_write_string(plist, "ProfileOut", &pos);
605     if (code < 0)
606 	return code;
607 
608     prgbs.data = (const byte *)xdev->profile_rgb_fn,
609 	prgbs.size = strlen(xdev->profile_rgb_fn),
610 	prgbs.persistent = false;
611     code = param_write_string(plist, "ProfileRgb", &prgbs);
612     if (code < 0)
613 	return code;
614 
615     pcmyks.data = (const byte *)xdev->profile_cmyk_fn,
616 	pcmyks.size = strlen(xdev->profile_cmyk_fn),
617 	pcmyks.persistent = false;
618     code = param_write_string(plist, "ProfileCmyk", &prgbs);
619 #endif
620 
621     return code;
622 }
623 
624 #if ENABLE_ICC_PROFILE
625 private int
psd_param_read_fn(gs_param_list * plist,const char * name,gs_param_string * pstr,uint max_len)626 psd_param_read_fn(gs_param_list *plist, const char *name,
627 		  gs_param_string *pstr, uint max_len)
628 {
629     int code = param_read_string(plist, name, pstr);
630 
631     if (code == 0) {
632 	if (pstr->size >= max_len)
633 	    param_signal_error(plist, name, code = gs_error_rangecheck);
634     } else {
635 	pstr->data = 0;
636     }
637     return code;
638 }
639 #endif
640 
641 /* Compare a C string and a gs_param_string. */
642 static bool
param_string_eq(const gs_param_string * pcs,const char * str)643 param_string_eq(const gs_param_string *pcs, const char *str)
644 {
645     return (strlen(str) == pcs->size &&
646 	    !strncmp(str, (const char *)pcs->data, pcs->size));
647 }
648 
649 private int
psd_set_color_model(psd_device * xdev,psd_color_model color_model)650 psd_set_color_model(psd_device *xdev, psd_color_model color_model)
651 {
652     xdev->color_model = color_model;
653     if (color_model == psd_DEVICE_GRAY) {
654 	xdev->devn_params.std_colorant_names = DeviceGrayComponents;
655 	xdev->devn_params.num_std_colorant_names = 1;
656 	xdev->color_info.cm_name = "DeviceGray";
657 	xdev->color_info.polarity = GX_CINFO_POLARITY_ADDITIVE;
658     } else if (color_model == psd_DEVICE_RGB) {
659 	xdev->devn_params.std_colorant_names = DeviceRGBComponents;
660 	xdev->devn_params.num_std_colorant_names = 3;
661 	xdev->color_info.cm_name = "DeviceRGB";
662 	xdev->color_info.polarity = GX_CINFO_POLARITY_ADDITIVE;
663     } else if (color_model == psd_DEVICE_CMYK) {
664 	xdev->devn_params.std_colorant_names = DeviceCMYKComponents;
665 	xdev->devn_params.num_std_colorant_names = 4;
666 	xdev->color_info.cm_name = "DeviceCMYK";
667 	xdev->color_info.polarity = GX_CINFO_POLARITY_SUBTRACTIVE;
668     } else if (color_model == psd_DEVICE_N) {
669 	xdev->devn_params.std_colorant_names = DeviceCMYKComponents;
670 	xdev->devn_params.num_std_colorant_names = 4;
671 	xdev->color_info.cm_name = "DeviceN";
672 	xdev->color_info.polarity = GX_CINFO_POLARITY_SUBTRACTIVE;
673     } else {
674 	return -1;
675     }
676 
677     return 0;
678 }
679 
680 /* Set parameters.  We allow setting the number of bits per component. */
681 private int
psd_put_params(gx_device * pdev,gs_param_list * plist)682 psd_put_params(gx_device * pdev, gs_param_list * plist)
683 {
684     psd_device * const pdevn = (psd_device *) pdev;
685     int code = 0;
686 #if ENABLE_ICC_PROFILE
687     gs_param_string po;
688     gs_param_string prgb;
689     gs_param_string pcmyk;
690 #endif
691     gs_param_string pcm;
692     psd_color_model color_model = pdevn->color_model;
693     gx_device_color_info save_info = pdevn->color_info;
694 
695 #if ENABLE_ICC_PROFILE
696     code = psd_param_read_fn(plist, "ProfileOut", &po,
697 				 sizeof(pdevn->profile_out_fn));
698     if (code >= 0)
699 	code = psd_param_read_fn(plist, "ProfileRgb", &prgb,
700 				 sizeof(pdevn->profile_rgb_fn));
701     if (code >= 0)
702 	code = psd_param_read_fn(plist, "ProfileCmyk", &pcmyk,
703 				 sizeof(pdevn->profile_cmyk_fn));
704 #endif
705 
706     if (code >= 0)
707 	code = param_read_name(plist, "ProcessColorModel", &pcm);
708     if (code == 0) {
709 	if (param_string_eq (&pcm, "DeviceGray"))
710 	    color_model = psd_DEVICE_GRAY;
711 	else if (param_string_eq (&pcm, "DeviceRGB"))
712 	    color_model = psd_DEVICE_RGB;
713 	else if (param_string_eq (&pcm, "DeviceCMYK"))
714 	    color_model = psd_DEVICE_CMYK;
715 	else if (param_string_eq (&pcm, "DeviceN"))
716 	    color_model = psd_DEVICE_N;
717 	else {
718 	    param_signal_error(plist, "ProcessColorModel",
719 			       code = gs_error_rangecheck);
720 	}
721     }
722 
723     if (code >= 0)
724         code = psd_set_color_model(pdevn, color_model);
725 
726     /* handle the standard DeviceN related parameters */
727     if (code == 0)
728         code = devn_printer_put_params(pdev, plist,
729 		&(pdevn->devn_params), &(pdevn->equiv_cmyk_colors));
730 
731     if (code < 0) {
732 	pdev->color_info = save_info;
733 	return code;
734     }
735 
736 #if ENABLE_ICC_PROFILE
737     /* Open any ICC profiles that have been specified. */
738     if (po.data != 0) {
739 	memcpy(pdevn->profile_out_fn, po.data, po.size);
740 	pdevn->profile_out_fn[po.size] = 0;
741     }
742     if (prgb.data != 0) {
743 	memcpy(pdevn->profile_rgb_fn, prgb.data, prgb.size);
744 	pdevn->profile_rgb_fn[prgb.size] = 0;
745     }
746     if (pcmyk.data != 0) {
747 	memcpy(pdevn->profile_cmyk_fn, pcmyk.data, pcmyk.size);
748 	pdevn->profile_cmyk_fn[pcmyk.size] = 0;
749     }
750     if (memcmp(&pdevn->color_info, &save_info,
751 			    size_of(gx_device_color_info)) != 0)
752         code = psd_open_profiles(pdevn);
753 #endif
754 
755     return code;
756 }
757 
758 
759 /*
760  * This routine will check to see if the color component name  match those
761  * that are available amoung the current device's color components.
762  *
763  * Parameters:
764  *   dev - pointer to device data structure.
765  *   pname - pointer to name (zero termination not required)
766  *   nlength - length of the name
767  *
768  * This routine returns a positive value (0 to n) which is the device colorant
769  * number if the name is found.  It returns a negative value if not found.
770  */
771 private int
psd_get_color_comp_index(gx_device * dev,const char * pname,int name_size,int component_type)772 psd_get_color_comp_index(gx_device * dev, const char * pname,
773 					int name_size, int component_type)
774 {
775     return devn_get_color_comp_index(dev,
776 		&(((psd_device *)dev)->devn_params),
777 		&(((psd_device *)dev)->equiv_cmyk_colors),
778 		pname, name_size, component_type, ENABLE_AUTO_SPOT_COLORS);
779 }
780 
781 
782 /* ------ Private definitions ------ */
783 
784 /* All two-byte quantities are stored MSB-first! */
785 #if arch_is_big_endian
786 #  define assign_u16(a,v) a = (v)
787 #  define assign_u32(a,v) a = (v)
788 #else
789 #  define assign_u16(a,v) a = ((v) >> 8) + ((v) << 8)
790 #  define assign_u32(a,v) a = (((v) >> 24) & 0xff) + (((v) >> 8) & 0xff00) + (((v) & 0xff00) << 8) + (((v) & 0xff) << 24)
791 #endif
792 
793 typedef struct {
794     FILE *f;
795 
796     int width;
797     int height;
798     int base_bytes_pp;	/* almost always 3 (rgb) or 4 (CMYK) */
799     int n_extra_channels;
800     int num_channels;	/* base_bytes_pp + any spot colors that are imaged */
801     /* Map output channel number to original separation number. */
802     int chnl_to_orig_sep[GX_DEVICE_COLOR_MAX_COMPONENTS];
803     /* Map output channel number to gx_color_index position. */
804     int chnl_to_position[GX_DEVICE_COLOR_MAX_COMPONENTS];
805 
806     /* byte offset of image data */
807     int image_data_off;
808 } psd_write_ctx;
809 
810 private int
psd_setup(psd_write_ctx * xc,psd_device * dev)811 psd_setup(psd_write_ctx *xc, psd_device *dev)
812 {
813     int i;
814 
815 #define NUM_CMYK_COMPONENTS 4
816     xc->base_bytes_pp = dev->devn_params.num_std_colorant_names;
817     xc->num_channels = xc->base_bytes_pp;
818     xc->n_extra_channels = dev->devn_params.separations.num_separations;
819     xc->width = dev->width;
820     xc->height = dev->height;
821 
822     /*
823      * Determine the order of the output components.  This is based upon
824      * the SeparationOrder parameter.  This parameter can be used to select
825      * which planes are actually imaged.  For the process color model channels
826      * we image the channels which are requested.  Non requested process color
827      * model channels are simply filled with white.  For spot colors we only
828      * image the requested channels.  Note:  There are no spot colors with
829      * the RGB color model.
830      */
831     for (i = 0; i < xc->base_bytes_pp + xc->n_extra_channels; i++)
832 	xc->chnl_to_position[i] = -1;
833     for (i = 0; i < xc->base_bytes_pp + xc->n_extra_channels; i++) {
834 	int sep_order_num = dev->devn_params.separation_order_map[i];
835 
836 	if (sep_order_num != GX_DEVICE_COLOR_MAX_COMPONENTS) {
837 	    if (i < NUM_CMYK_COMPONENTS)	/* Do not rearrange CMYK */
838 	        xc->chnl_to_position[i] = sep_order_num;
839 	    else {				/* Re arrange separations */
840 	        xc->chnl_to_position[xc->num_channels] = sep_order_num;
841 	        xc->chnl_to_orig_sep[xc->num_channels++] = i;
842 	    }
843 	}
844     }
845 
846     return 0;
847 }
848 
849 private int
psd_write(psd_write_ctx * xc,const byte * buf,int size)850 psd_write(psd_write_ctx *xc, const byte *buf, int size) {
851     int code;
852 
853     code = fwrite(buf, 1, size, xc->f);
854     if (code < 0)
855 	return code;
856     return 0;
857 }
858 
859 private int
psd_write_8(psd_write_ctx * xc,byte v)860 psd_write_8(psd_write_ctx *xc, byte v)
861 {
862     return psd_write(xc, (byte *)&v, 1);
863 }
864 
865 private int
psd_write_16(psd_write_ctx * xc,bits16 v)866 psd_write_16(psd_write_ctx *xc, bits16 v)
867 {
868     bits16 buf;
869 
870     assign_u16(buf, v);
871     return psd_write(xc, (byte *)&buf, 2);
872 }
873 
874 private int
psd_write_32(psd_write_ctx * xc,bits32 v)875 psd_write_32(psd_write_ctx *xc, bits32 v)
876 {
877     bits32 buf;
878 
879     assign_u32(buf, v);
880     return psd_write(xc, (byte *)&buf, 4);
881 }
882 
883 private int
psd_write_header(psd_write_ctx * xc,psd_device * pdev)884 psd_write_header(psd_write_ctx *xc, psd_device *pdev)
885 {
886     int code = 0;
887     int bytes_pp = xc->num_channels;
888     int chan_idx;
889     int chan_names_len = 0;
890     int sep_num;
891     const devn_separation_name *separation_name;
892 
893     psd_write(xc, (const byte *)"8BPS", 4); /* Signature */
894     psd_write_16(xc, 1); /* Version - Always equal to 1*/
895     /* Reserved 6 Bytes - Must be zero */
896     psd_write_32(xc, 0);
897     psd_write_16(xc, 0);
898     psd_write_16(xc, (bits16) bytes_pp); /* Channels (2 Bytes) - Supported range is 1 to 24 */
899     psd_write_32(xc, xc->height); /* Rows */
900     psd_write_32(xc, xc->width); /* Columns */
901     psd_write_16(xc, 8); /* Depth - 1, 8 and 16 */
902     psd_write_16(xc, (bits16) xc->base_bytes_pp); /* Mode - RGB=3, CMYK=4 */
903 
904     /* Color Mode Data */
905     psd_write_32(xc, 0); 	/* No color mode data */
906 
907     /* Image Resources */
908 
909     /* Channel Names */
910     for (chan_idx = NUM_CMYK_COMPONENTS; chan_idx < xc->num_channels; chan_idx++) {
911 	sep_num = xc->chnl_to_orig_sep[chan_idx] - NUM_CMYK_COMPONENTS;
912 	separation_name = &(pdev->devn_params.separations.names[sep_num]);
913 	chan_names_len += (separation_name->size + 1);
914     }
915     psd_write_32(xc, 12 + (chan_names_len + (chan_names_len % 2))
916 			+ (12 + (14 * (xc->num_channels - xc->base_bytes_pp)))
917 			+ 28);
918     psd_write(xc, (const byte *)"8BIM", 4);
919     psd_write_16(xc, 1006); /* 0x03EE */
920     psd_write_16(xc, 0); /* PString */
921     psd_write_32(xc, chan_names_len + (chan_names_len % 2));
922     for (chan_idx = NUM_CMYK_COMPONENTS; chan_idx < xc->num_channels; chan_idx++) {
923 	sep_num = xc->chnl_to_orig_sep[chan_idx] - NUM_CMYK_COMPONENTS;
924 	separation_name = &(pdev->devn_params.separations.names[sep_num]);
925 	psd_write_8(xc, (byte) separation_name->size);
926 	psd_write(xc, separation_name->data, separation_name->size);
927     }
928     if (chan_names_len % 2)
929 	psd_write_8(xc, 0); /* pad */
930 
931     /* DisplayInfo - Colors for each spot channels */
932     psd_write(xc, (const byte *)"8BIM", 4);
933     psd_write_16(xc, 1007); /* 0x03EF */
934     psd_write_16(xc, 0); /* PString */
935     psd_write_32(xc, 14 * (xc->num_channels - xc->base_bytes_pp)); /* Length */
936     for (chan_idx = NUM_CMYK_COMPONENTS; chan_idx < xc->num_channels; chan_idx++) {
937 	sep_num = xc->chnl_to_orig_sep[chan_idx] - NUM_CMYK_COMPONENTS;
938 	psd_write_16(xc, 02); /* CMYK */
939 	/* PhotoShop stores all component values as if they were additive. */
940 	if (pdev->equiv_cmyk_colors.color[sep_num].color_info_valid) {
941 #define convert_color(component) ((bits16)((65535 * ((double)\
942     (frac_1 - pdev->equiv_cmyk_colors.color[sep_num].component)) / frac_1)))
943 	    psd_write_16(xc, convert_color(c)); /* Cyan */
944 	    psd_write_16(xc, convert_color(m)); /* Magenta */
945 	    psd_write_16(xc, convert_color(y)); /* Yellow */
946 	    psd_write_16(xc, convert_color(k)); /* Black */
947 #undef convert_color
948 	}
949 	else {	    /* Else set C = M = Y = 0, K = 1 */
950 	    psd_write_16(xc, 65535); /* Cyan */
951 	    psd_write_16(xc, 65535); /* Magenta */
952 	    psd_write_16(xc, 65535); /* Yellow */
953 	    psd_write_16(xc, 0); /* Black */
954 	}
955 	psd_write_16(xc, 0); /* Opacity 0 to 100 */
956 	psd_write_8(xc, 2); /* Don't know */
957 	psd_write_8(xc, 0); /* Padding - Always Zero */
958     }
959 
960     /* Image resolution */
961     psd_write(xc, (const byte *)"8BIM", 4);
962     psd_write_16(xc, 1005); /* 0x03ED */
963     psd_write_16(xc, 0); /* PString */
964     psd_write_32(xc, 16); /* Length */
965     		/* Resolution is specified as a fixed 16.16 bits */
966     psd_write_32(xc, (int) (pdev->HWResolution[0] * 0x10000 + 0.5));
967     psd_write_16(xc, 1);	/* width:  1 --> resolution is pixels per inch */
968     psd_write_16(xc, 1);	/* width:  1 --> resolution is pixels per inch */
969     psd_write_32(xc, (int) (pdev->HWResolution[1] * 0x10000 + 0.5));
970     psd_write_16(xc, 1);	/* height:  1 --> resolution is pixels per inch */
971     psd_write_16(xc, 1);	/* height:  1 --> resolution is pixels per inch */
972 
973     /* Layer and Mask information */
974     psd_write_32(xc, 0); 	/* No layer or mask information */
975 
976     return code;
977 }
978 
979 private void
psd_calib_row(psd_write_ctx * xc,byte ** tile_data,const byte * row,int channel,icmLuBase * luo)980 psd_calib_row(psd_write_ctx *xc, byte **tile_data, const byte *row,
981 		int channel, icmLuBase *luo)
982 {
983     int base_bytes_pp = xc->base_bytes_pp;
984     int n_extra_channels = xc->n_extra_channels;
985     int channels = base_bytes_pp + n_extra_channels;
986     int inn, outn;
987     int x;
988     double in[MAX_CHAN], out[MAX_CHAN];
989 
990     luo->spaces(luo, NULL, &inn, NULL, &outn, NULL, NULL, NULL, NULL);
991 
992     for (x = 0; x < xc->width; x++) {
993 	if (channel < outn) {
994 	    int plane_idx;
995 
996 	    for (plane_idx = 0; plane_idx < inn; plane_idx++)
997 		in[plane_idx] = row[x*channels+plane_idx] * (1.0 / 255);
998 
999 	    (*tile_data)[x] = (int)(0.5 + 255 * out[channel]);
1000 	    luo->lookup(luo, out, in);
1001 	} else {
1002 	    (*tile_data)[x] = 255 ^ row[x*channels+base_bytes_pp+channel];
1003 	}
1004     }
1005 }
1006 
1007 /*
1008  * Output the image data for the PSD device.  The data for the PSD is
1009  * written in separate planes.  If the device is psdrgb then we simply
1010  * write three planes of RGB data.  The DeviceN parameters (SeparationOrder,
1011  * SeparationCOlorNames, and MaxSeparations) are not applied to the psdrgb
1012  * device.
1013  *
1014  * The DeviceN parameters are applied to the psdcmyk device.  If the
1015  * SeparationOrder parameter is not specified then first we write out the data
1016  * for the CMYK planes and then any separation planes.  If the SeparationOrder
1017  * parameter is specified, then things are more complicated.  Logically we
1018  * would simply write the planes specified by the SeparationOrder data.
1019  * However Photoshop expects there to be CMYK data.  First we will write out
1020  * four planes of data for CMYK.  If any of these colors are present in the
1021  * SeparationOrder data then the plane data will contain the color information.
1022  * If a color is not present then the plane data will be zero.  After the CMYK
1023  * data, we will write out any separation data which is specified in the
1024  * SeparationOrder data.
1025  */
1026 private int
psd_write_image_data(psd_write_ctx * xc,gx_device_printer * pdev)1027 psd_write_image_data(psd_write_ctx *xc, gx_device_printer *pdev)
1028 {
1029     int code = 0;
1030     int raster = gdev_prn_raster(pdev);
1031     int i, j;
1032     byte *line, *sep_line;
1033     int base_bytes_pp = xc->base_bytes_pp;
1034     int bytes_pp =pdev->color_info.num_components;
1035     int chan_idx;
1036     psd_device *xdev = (psd_device *)pdev;
1037     icmLuBase *luo = xdev->lu_out;
1038     byte *row;
1039 
1040     psd_write_16(xc, 0); /* Compression */
1041 
1042     line = gs_alloc_bytes(pdev->memory, raster, "psd_write_image_data");
1043     sep_line = gs_alloc_bytes(pdev->memory, xc->width, "psd_write_sep_line");
1044 
1045     /* Print the output planes */
1046     for (chan_idx = 0; chan_idx < xc->num_channels; chan_idx++) {
1047 	for (j = 0; j < xc->height; ++j) {
1048 	    int data_pos = xc->chnl_to_position[chan_idx];
1049 
1050 	    /* Check if the separation is present in the SeparationOrder */
1051 	    if (data_pos >= 0) {
1052 	        code = gdev_prn_get_bits(pdev, j, line, &row);
1053 	        if (luo == NULL) {
1054 		    for (i = 0; i < xc->width; ++i) {
1055 		        if (base_bytes_pp == 3) {
1056 			    /* RGB */
1057 			    sep_line[i] = row[i*bytes_pp + data_pos];
1058 		        } else {
1059 			    /* CMYK */
1060 			    sep_line[i] = 255 - row[i*bytes_pp + data_pos];
1061 		        }
1062 		    }
1063 	        } else {
1064 		    psd_calib_row(xc, &sep_line, row, data_pos, luo);
1065 	        }
1066 	        psd_write(xc, sep_line, xc->width);
1067 	    } else {
1068 		if (chan_idx < NUM_CMYK_COMPONENTS) {
1069 		    /* Write empty process color */
1070 		    for (i = 0; i < xc->width; ++i)
1071 		        sep_line[i] = 255;
1072 	            psd_write(xc, sep_line, xc->width);
1073 		}
1074 	    }
1075 	}
1076     }
1077 
1078     gs_free_object(pdev->memory, sep_line, "psd_write_sep_line");
1079     gs_free_object(pdev->memory, line, "psd_write_image_data");
1080     return code;
1081 }
1082 
1083 static int
psd_print_page(gx_device_printer * pdev,FILE * file)1084 psd_print_page(gx_device_printer *pdev, FILE *file)
1085 {
1086     psd_write_ctx xc;
1087 
1088     xc.f = file;
1089 
1090     psd_setup(&xc, (psd_device *)pdev);
1091     psd_write_header(&xc, (psd_device *)pdev);
1092     psd_write_image_data(&xc, pdev);
1093 
1094     return 0;
1095 }
1096