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