xref: /plan9/sys/src/cmd/gs/src/gdevpsdi.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
1 /* Copyright (C) 1997, 2000 Aladdin Enterprises.  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: gdevpsdi.c,v 1.45 2005/09/29 18:35:18 leonardo Exp $ */
18 /* Image compression for PostScript and PDF writers */
19 #include "stdio_.h"		/* for jpeglib.h */
20 #include "jpeglib_.h"		/* for sdct.h */
21 #include "math_.h"
22 #include "gx.h"
23 #include "gserrors.h"
24 #include "gscspace.h"
25 #include "gdevpsdf.h"
26 #include "gdevpsds.h"
27 #include "gxdevmem.h"
28 #include "gxcspace.h"
29 #include "gsparamx.h"
30 #include "strimpl.h"
31 #include "scfx.h"
32 #include "sdct.h"
33 #include "sjpeg.h"
34 #include "slzwx.h"
35 #include "spngpx.h"
36 #include "srlx.h"
37 #include "szlibx.h"
38 
39 /* Define parameter-setting procedures. */
40 extern stream_state_proc_put_params(s_CF_put_params, stream_CF_state);
41 
42 /* ---------------- Image compression ---------------- */
43 
44 /*
45  * Add a filter to expand or reduce the pixel width if needed.
46  * At least one of bpc_in and bpc_out is 8; the other is 1, 2, 4, or 8,
47  * except if bpc_out is 8, bpc_in may be 12.
48  */
49 private int
pixel_resize(psdf_binary_writer * pbw,int width,int num_components,int bpc_in,int bpc_out)50 pixel_resize(psdf_binary_writer * pbw, int width, int num_components,
51 	     int bpc_in, int bpc_out)
52 {
53     gs_memory_t *mem = pbw->dev->v_memory;
54     const stream_template *template;
55     stream_1248_state *st;
56     int code;
57 
58     if (bpc_out == bpc_in)
59 	return 0;
60     if (bpc_in != 8) {
61 	static const stream_template *const exts[13] = {
62 	    0, &s_1_8_template, &s_2_8_template, 0, &s_4_8_template,
63 	    0, 0, 0, 0, 0, 0, 0, &s_12_8_template
64 	};
65 
66 	template = exts[bpc_in];
67     } else {
68 	static const stream_template *const rets[5] = {
69 	    0, &s_8_1_template, &s_8_2_template, 0, &s_8_4_template
70 	};
71 
72 	template = rets[bpc_out];
73     }
74     st = (stream_1248_state *)
75 	s_alloc_state(mem, template->stype, "pixel_resize state");
76     if (st == 0)
77 	return_error(gs_error_VMerror);
78     code = psdf_encode_binary(pbw, template, (stream_state *) st);
79     if (code < 0) {
80 	gs_free_object(mem, st, "pixel_resize state");
81 	return code;
82     }
83     s_1248_init(st, width, num_components);
84     return 0;
85 }
86 
87 private int
convert_color(gx_device * pdev,const gs_color_space * pcs,const gs_imager_state * pis,gs_client_color * cc,float c[3])88 convert_color(gx_device *pdev, const gs_color_space *pcs, const gs_imager_state * pis,
89 	      gs_client_color *cc, float c[3])
90 {
91     int code;
92     gx_device_color dc;
93 
94     cs_restrict_color(cc, pcs);
95     code = pcs->type->remap_color(cc, pcs, &dc, pis, pdev, gs_color_select_texture);
96     if (code < 0)
97 	return code;
98     c[0] = (float)((int)(dc.colors.pure >> pdev->color_info.comp_shift[0]) & ((1 << pdev->color_info.comp_bits[0]) - 1));
99     c[1] = (float)((int)(dc.colors.pure >> pdev->color_info.comp_shift[1]) & ((1 << pdev->color_info.comp_bits[1]) - 1));
100     c[2] = (float)((int)(dc.colors.pure >> pdev->color_info.comp_shift[2]) & ((1 << pdev->color_info.comp_bits[2]) - 1));
101     return 0;
102 }
103 
104 /* A hewristic choice of DCT compression parameters - see bug 687174. */
105 private int
choose_DCT_params(gx_device * pdev,const gs_color_space * pcs,const gs_imager_state * pis,gs_c_param_list * list,gs_c_param_list ** param,stream_state * st)106 choose_DCT_params(gx_device *pdev, const gs_color_space *pcs,
107 		  const gs_imager_state * pis,
108 		  gs_c_param_list *list, gs_c_param_list **param,
109 		  stream_state *st)
110 {
111     gx_device_memory mdev;
112     gs_client_color cc;
113     int code;
114     float c[4][3];
115     const float MIN_FLOAT = - MAX_FLOAT;
116     const float domination = (float)0.25;
117     const int one = 1, zero = 0;
118 
119     if (pcs->type->num_components(pcs) != 3)
120 	return 0;
121     /* Make a copy of the parameter list since we will modify it. */
122     code = param_list_copy((gs_param_list *)list, (gs_param_list *)*param);
123     if (code < 0)
124 	return code;
125     *param = list;
126 
127     /* Create a local memory device for transforming colors to DeviceRGB. */
128     gs_make_mem_device(&mdev, gdev_mem_device_for_bits(24), pdev->memory, 0, NULL);
129     gx_device_retain((gx_device *)&mdev, true);	/* prevent freeing */
130     set_linear_color_bits_mask_shift((gx_device *)&mdev);
131     mdev.color_info.separable_and_linear = GX_CINFO_SEP_LIN;
132 
133     /* Check for an RGB-like color space.
134        To recognize that we make a matrix as it were a linear operator,
135        suppress an ununiformity by subtracting the image of {0,0,0},
136        and then check for giagonal domination.  */
137     cc.paint.values[0] = cc.paint.values[1] = cc.paint.values[2] = MIN_FLOAT;
138     convert_color((gx_device *)&mdev, pcs, pis, &cc, c[3]);
139     cc.paint.values[0] = MAX_FLOAT; cc.paint.values[1] = MIN_FLOAT; cc.paint.values[2] = MIN_FLOAT;
140     convert_color((gx_device *)&mdev, pcs, pis, &cc, c[0]);
141     cc.paint.values[0] = MIN_FLOAT; cc.paint.values[1] = MAX_FLOAT; cc.paint.values[2] = MIN_FLOAT;
142     convert_color((gx_device *)&mdev, pcs, pis, &cc, c[1]);
143     cc.paint.values[0] = MIN_FLOAT; cc.paint.values[1] = MIN_FLOAT; cc.paint.values[2] = MAX_FLOAT;
144     convert_color((gx_device *)&mdev, pcs, pis, &cc, c[2]);
145     c[0][0] -= c[3][0]; c[0][1] -= c[3][1]; c[0][2] -= c[3][2];
146     c[1][0] -= c[3][0]; c[1][1] -= c[3][1]; c[1][2] -= c[3][2];
147     c[2][0] -= c[3][0]; c[2][1] -= c[3][1]; c[2][2] -= c[3][2];
148     c[0][0] = any_abs(c[0][0]); c[0][1] = any_abs(c[0][1]); c[0][2] = any_abs(c[0][2]);
149     c[1][0] = any_abs(c[1][0]); c[1][1] = any_abs(c[1][1]); c[1][2] = any_abs(c[1][2]);
150     c[2][0] = any_abs(c[2][0]); c[2][1] = any_abs(c[2][1]); c[2][2] = any_abs(c[2][2]);
151     if (c[0][0] * domination > c[0][1] && c[0][0] * domination > c[0][2] &&
152 	c[1][1] * domination > c[1][0] && c[1][1] * domination > c[1][2] &&
153 	c[2][2] * domination > c[2][0] && c[2][2] * domination > c[2][1]) {
154 	/* Yes, it looks like an RGB color space.
155 	   Replace ColorTransform with 1. */
156 	code = param_write_int((gs_param_list *)list, "ColorTransform", &one);
157 	if (code < 0)
158 	    return code;
159 	goto done;
160     }
161 
162     /* Check for a Lab-like color space.
163        Colors {v,0,0} should map to grays. */
164     cc.paint.values[0] = MAX_FLOAT; cc.paint.values[1] = cc.paint.values[2] = 0;
165     convert_color((gx_device *)&mdev, pcs, pis, &cc, c[0]);
166     cc.paint.values[0] /= 2;
167     convert_color((gx_device *)&mdev, pcs, pis, &cc, c[1]);
168     cc.paint.values[0] /= 2;
169     convert_color((gx_device *)&mdev, pcs, pis, &cc, c[2]);
170     c[0][1] -= c[0][0]; c[0][2] -= c[0][0];
171     c[1][1] -= c[1][0]; c[1][2] -= c[1][0];
172     c[2][1] -= c[2][0]; c[2][2] -= c[2][0];
173     c[0][1] = any_abs(c[0][1]); c[0][2] = any_abs(c[0][2]);
174     c[1][1] = any_abs(c[1][1]); c[1][2] = any_abs(c[1][2]);
175     c[2][1] = any_abs(c[2][1]); c[2][2] = any_abs(c[2][2]);
176     if (c[0][0] * domination > c[0][1] && c[0][0] * domination > c[0][2] &&
177 	c[1][0] * domination > c[1][1] && c[1][0] * domination > c[1][2] &&
178 	c[2][0] * domination > c[2][1] && c[2][0] * domination > c[2][2]) {
179 	/* Yes, it looks like an Lab color space.
180 	   Replace ColorTransform with 0. */
181 	code = param_write_int((gs_param_list *)list, "ColorTransform", &zero);
182 	if (code < 0)
183 	    return code;
184     } else {
185 	/* Unknown color space type.
186 	   Replace /HSamples [1 1 1 1] /VSamples [1 1 1 1] to avoid quality degradation. */
187 	gs_param_string a;
188 	static const byte v[4] = {1, 1, 1, 1};
189 
190 	a.data = v;
191 	a.size = 4;
192 	a.persistent = true;
193 	code = param_write_string((gs_param_list *)list, "HSamples", &a);
194 	if (code < 0)
195 	    return code;
196 	code = param_write_string((gs_param_list *)list, "VSamples", &a);
197 	if (code < 0)
198 	    return code;
199     }
200 done:
201     gs_c_param_list_read(list);
202     return 0;
203 }
204 
205 /* Add the appropriate image compression filter, if any. */
206 private int
setup_image_compression(psdf_binary_writer * pbw,const psdf_image_params * pdip,const gs_pixel_image_t * pim,const gs_imager_state * pis,bool lossless)207 setup_image_compression(psdf_binary_writer *pbw, const psdf_image_params *pdip,
208 			const gs_pixel_image_t * pim, const gs_imager_state * pis,
209 			bool lossless)
210 {
211     gx_device_psdf *pdev = pbw->dev;
212     gs_memory_t *mem = pdev->v_memory;
213     const stream_template *template = pdip->filter_template;
214     const stream_template *orig_template = template;
215     const stream_template *lossless_template =
216 	(pdev->params.UseFlateCompression &&
217 	 pdev->version >= psdf_version_ll3 ?
218 	 &s_zlibE_template : &s_LZWE_template);
219     const gs_color_space *pcs = pim->ColorSpace; /* null if mask */
220     int Colors = (pcs ? gs_color_space_num_components(pcs) : 1);
221     bool Indexed =
222 	(pcs != 0 &&
223 	 gs_color_space_get_index(pcs) == gs_color_space_index_Indexed);
224     gs_c_param_list *dict = pdip->Dict;
225     stream_state *st;
226     int code;
227 
228     if (!pdip->Encode)		/* no compression */
229 	return 0;
230     if (pdip->AutoFilter) {
231 	/*
232 	 * Disregard the requested filter.  What we should do at this point
233 	 * is analyze the image to decide whether to use JPEG encoding
234 	 * (DCTEncode with ACSDict) or the lossless filter.  However, since
235 	 * we don't buffer the entire image, we'll make the choice on-fly,
236 	 * forking the image data into 3 streams : (1) JPEG, (2) lossless,
237 	 * (3) the compression chooser. In this case this function is
238 	 * called 2 times with different values of the 'lossless' argument.
239 	 */
240         if (lossless) {
241             orig_template = template = lossless_template;
242         } else {
243             orig_template = template = &s_DCTE_template;
244         }
245 	dict = pdip->ACSDict;
246     } else if (!lossless)
247 	return gs_error_rangecheck; /* Reject the alternative stream. */
248     if (pdev->version < psdf_version_ll3 && template == &s_zlibE_template)
249 	orig_template = template = lossless_template;
250     if (dict) /* NB: rather than dereference NULL lets continue on without a dict */
251 	gs_c_param_list_read(dict);	/* ensure param list is in read mode */
252     if (template == 0)	/* no compression */
253 	return 0;
254     if (pim->Width < 200 && pim->Height < 200) /* Prevent a fixed overflow. */
255 	if (pim->Width * pim->Height * Colors * pim->BitsPerComponent <= 160)
256 	    return 0;  /* not worth compressing */
257     /* Only use DCTE for 8-bit, non-Indexed data. */
258     if (template == &s_DCTE_template) {
259 	if (Indexed ||
260             !(pdip->Downsample ?
261 	      pdip->Depth == 8 ||
262 	      (pdip->Depth == -1 && pim->BitsPerComponent == 8) :
263 	      pim->BitsPerComponent == 8)
264 	    ) {
265 	    /* Use LZW/Flate instead. */
266 	    template = lossless_template;
267 	}
268     }
269     st = s_alloc_state(mem, template->stype, "setup_image_compression");
270     if (st == 0)
271 	return_error(gs_error_VMerror);
272     if (template->set_defaults)
273 	(*template->set_defaults) (st);
274     if (template == &s_CFE_template) {
275 	stream_CFE_state *const ss = (stream_CFE_state *) st;
276 
277 	if (pdip->Dict != 0 && pdip->filter_template == template) {
278 	    s_CF_put_params((gs_param_list *)pdip->Dict,
279 			    (stream_CF_state *)ss); /* ignore errors */
280 	} else {
281 	    ss->K = -1;
282 	    ss->BlackIs1 = true;
283 	}
284 	ss->Columns = pim->Width;
285 	ss->Rows = (ss->EndOfBlock ? 0 : pim->Height);
286     } else if ((template == &s_LZWE_template ||
287 		template == &s_zlibE_template) &&
288 	       pdev->version >= psdf_version_ll3) {
289 	/* If not Indexed, add a PNGPredictor filter. */
290 	if (!Indexed) {
291 	    code = psdf_encode_binary(pbw, template, st);
292 	    if (code < 0)
293 		goto fail;
294 	    template = &s_PNGPE_template;
295 	    st = s_alloc_state(mem, template->stype, "setup_image_compression");
296 	    if (st == 0) {
297 		code = gs_note_error(gs_error_VMerror);
298 		goto fail;
299 	    }
300 	    if (template->set_defaults)
301 		(*template->set_defaults) (st);
302 	    {
303 		stream_PNGP_state *const ss = (stream_PNGP_state *) st;
304 
305 		ss->Colors = Colors;
306 		ss->Columns = pim->Width;
307 	    }
308 	}
309     } else if (template == &s_DCTE_template) {
310 	gs_c_param_list list, *param = dict;
311 
312 	gs_c_param_list_write(&list, mem);
313 	code = choose_DCT_params((gx_device *)pbw->dev, pcs, pis, &list, &param, st);
314 	if (code < 0) {
315     	    gs_c_param_list_release(&list);
316 	    return code;
317 	}
318 	code = psdf_DCT_filter((gs_param_list *)param,
319 			       st, pim->Width, pim->Height, Colors, pbw);
320 	gs_c_param_list_release(&list);
321 	if (code < 0)
322 	    goto fail;
323 	/* psdf_DCT_filter already did the psdf_encode_binary. */
324 	return 0;
325     } else if (template == &s_LZWE_template) {
326 	if (template->set_defaults)
327 	    (*template->set_defaults) (st);
328     }
329     code = psdf_encode_binary(pbw, template, st);
330     if (code >= 0)
331 	return 0;
332  fail:
333     gs_free_object(mem, st, "setup_image_compression");
334     return code;
335 }
336 
337 /* Determine whether an image should be downsampled. */
338 private bool
do_downsample(const psdf_image_params * pdip,const gs_pixel_image_t * pim,floatp resolution)339 do_downsample(const psdf_image_params *pdip, const gs_pixel_image_t *pim,
340 	      floatp resolution)
341 {
342     floatp factor = (int)(resolution / pdip->Resolution);
343 
344     return (pdip->Downsample && factor >= pdip->DownsampleThreshold &&
345 	    factor <= pim->Width && factor <= pim->Height);
346 }
347 
348 /* Add downsampling, antialiasing, and compression filters. */
349 /* Uses AntiAlias, Depth, DownsampleThreshold, DownsampleType, Resolution. */
350 /* Assumes do_downsampling() is true. */
351 private int
setup_downsampling(psdf_binary_writer * pbw,const psdf_image_params * pdip,gs_pixel_image_t * pim,const gs_imager_state * pis,floatp resolution,bool lossless)352 setup_downsampling(psdf_binary_writer * pbw, const psdf_image_params * pdip,
353 		   gs_pixel_image_t * pim, const gs_imager_state * pis,
354 		   floatp resolution, bool lossless)
355 {
356     gx_device_psdf *pdev = pbw->dev;
357     /* Note: Bicubic is currently interpreted as Average. */
358     const stream_template *template =
359 	(pdip->DownsampleType == ds_Subsample ?
360 	 &s_Subsample_template : &s_Average_template);
361     int factor = (int)(resolution / pdip->Resolution);
362     int orig_bpc = pim->BitsPerComponent;
363     int orig_width = pim->Width;
364     int orig_height = pim->Height;
365     stream_state *st;
366     int code;
367 
368     st = s_alloc_state(pdev->v_memory, template->stype,
369 		       "setup_downsampling");
370     if (st == 0)
371 	return_error(gs_error_VMerror);
372     if (template->set_defaults)
373 	template->set_defaults(st);
374     {
375 	stream_Downsample_state *const ss = (stream_Downsample_state *) st;
376 
377 	ss->Colors =
378 	    (pim->ColorSpace == 0 ? 1 /*mask*/ :
379 	     gs_color_space_num_components(pim->ColorSpace));
380 	ss->WidthIn = pim->Width;
381 	ss->HeightIn = pim->Height;
382 	ss->XFactor = ss->YFactor = factor;
383 	ss->AntiAlias = pdip->AntiAlias;
384 	ss->padX = ss->padY = false; /* should be true */
385 	if (template->init)
386 	    template->init(st);
387 	pim->Width = s_Downsample_size_out(pim->Width, factor, ss->padX);
388 	pim->Height = s_Downsample_size_out(pim->Height, factor, ss->padY);
389 	pim->BitsPerComponent = pdip->Depth;
390 	gs_matrix_scale(&pim->ImageMatrix, (double)pim->Width / orig_width,
391 			(double)pim->Height / orig_height,
392 			&pim->ImageMatrix);
393 	/****** NO ANTI-ALIASING YET ******/
394 	if ((code = setup_image_compression(pbw, pdip, pim, pis, lossless)) < 0 ||
395 	    (code = pixel_resize(pbw, pim->Width, ss->Colors,
396 				 8, pdip->Depth)) < 0 ||
397 	    (code = psdf_encode_binary(pbw, template, st)) < 0 ||
398 	    (code = pixel_resize(pbw, orig_width, ss->Colors,
399 				 orig_bpc, 8)) < 0
400 	    ) {
401 	    gs_free_object(pdev->v_memory, st, "setup_image_compression");
402 	    return code;
403 	}
404     }
405     return 0;
406 }
407 
408 /* Decive whether to convert an image to RGB. */
409 bool
psdf_is_converting_image_to_RGB(const gx_device_psdf * pdev,const gs_imager_state * pis,const gs_pixel_image_t * pim)410 psdf_is_converting_image_to_RGB(const gx_device_psdf * pdev,
411 		const gs_imager_state * pis, const gs_pixel_image_t * pim)
412 {
413     return pdev->params.ConvertCMYKImagesToRGB &&
414 	    pis != 0 &&
415 	    gs_color_space_get_index(pim->ColorSpace) ==
416 	    gs_color_space_index_DeviceCMYK;
417 }
418 
419 /* Set up compression and downsampling filters for an image. */
420 /* Note that this may modify the image parameters. */
421 int
psdf_setup_image_filters(gx_device_psdf * pdev,psdf_binary_writer * pbw,gs_pixel_image_t * pim,const gs_matrix * pctm,const gs_imager_state * pis,bool lossless)422 psdf_setup_image_filters(gx_device_psdf * pdev, psdf_binary_writer * pbw,
423 			 gs_pixel_image_t * pim, const gs_matrix * pctm,
424 			 const gs_imager_state * pis, bool lossless)
425 {
426     /*
427      * The following algorithms are per Adobe Tech Note # 5151,
428      * "Acrobat Distiller Parameters", revised 16 September 1996
429      * for Acrobat(TM) Distiller(TM) 3.0.
430      *
431      * The control structure is a little tricky, because filter
432      * pipelines must be constructed back-to-front.
433      */
434     int code = 0;
435     psdf_image_params params;
436     int bpc = pim->BitsPerComponent;
437     int bpc_out = pim->BitsPerComponent = min(bpc, 8);
438     int ncomp;
439     double resolution;
440 
441     /*
442      * The Adobe documentation doesn't say this, but mask images are
443      * compressed on the same basis as 1-bit-deep monochrome images,
444      * except that anti-aliasing (resolution/depth tradeoff) is not
445      * allowed.
446      */
447     if (pim->ColorSpace == NULL) { /* mask image */
448 	params = pdev->params.MonoImage;
449 	params.Depth = 1;
450 	ncomp = 1;
451     } else {
452 	ncomp = gs_color_space_num_components(pim->ColorSpace);
453 	if (ncomp == 1) {
454 	    if (bpc == 1)
455 		params = pdev->params.MonoImage;
456 	    else
457 		params = pdev->params.GrayImage;
458 	    if (params.Depth == -1)
459 		params.Depth = bpc;
460 	} else {
461 	    params = pdev->params.ColorImage;
462 	    /* params.Depth is reset below */
463 	}
464     }
465 
466     /*
467      * We can compute the image resolution by:
468      *    W / (W * ImageMatrix^-1 * CTM / HWResolution).
469      * We can replace W by 1 to simplify the computation.
470      */
471     if (pctm == 0)
472 	resolution = -1;
473     else {
474 	gs_point pt;
475 
476 	/* We could do both X and Y, but why bother? */
477 	code = gs_distance_transform_inverse(1.0, 0.0, &pim->ImageMatrix, &pt);
478 	if (code < 0)
479 	    return code;
480 	gs_distance_transform(pt.x, pt.y, pctm, &pt);
481 	resolution = 1.0 / hypot(pt.x / pdev->HWResolution[0],
482 				 pt.y / pdev->HWResolution[1]);
483     }
484     if (ncomp == 1) {
485 	/* Monochrome, gray, or mask */
486 	/* Check for downsampling. */
487 	if (do_downsample(&params, pim, resolution)) {
488 	    /* Use the downsampled depth, not the original data depth. */
489 	    if (params.Depth == 1) {
490 		params.Filter = pdev->params.MonoImage.Filter;
491 		params.filter_template = pdev->params.MonoImage.filter_template;
492 		params.Dict = pdev->params.MonoImage.Dict;
493 	    } else {
494 		params.Filter = pdev->params.GrayImage.Filter;
495 		params.filter_template = pdev->params.GrayImage.filter_template;
496 		params.Dict = pdev->params.GrayImage.Dict;
497 	    }
498 	    code = setup_downsampling(pbw, &params, pim, pis, resolution, lossless);
499 	} else {
500 	    code = setup_image_compression(pbw, &params, pim, pis, lossless);
501 	}
502 	if (code < 0)
503 	    return code;
504 	code = pixel_resize(pbw, pim->Width, ncomp, bpc, bpc_out);
505     } else {
506 	/* Color */
507 	bool cmyk_to_rgb = psdf_is_converting_image_to_RGB(pdev, pis, pim);
508 
509 	if (cmyk_to_rgb) {
510 	    extern_st(st_color_space);
511 	    gs_memory_t *mem = pdev->v_memory;
512 	    gs_color_space *rgb_cs = gs_alloc_struct(mem,
513 		    gs_color_space, &st_color_space, "psdf_setup_image_filters");
514 
515 	    gs_cspace_init_DeviceRGB(mem, rgb_cs);  /* idempotent initialization */
516 	    pim->ColorSpace = rgb_cs;
517 	}
518 	if (params.Depth == -1)
519 	    params.Depth = (cmyk_to_rgb ? 8 : bpc_out);
520 	if (do_downsample(&params, pim, resolution)) {
521 	    code = setup_downsampling(pbw, &params, pim, pis, resolution, lossless);
522 	} else {
523 	    code = setup_image_compression(pbw, &params, pim, pis, lossless);
524 	}
525 	if (code < 0)
526 	    return code;
527 	if (cmyk_to_rgb) {
528 	    gs_memory_t *mem = pdev->v_memory;
529 	    stream_C2R_state *ss = (stream_C2R_state *)
530 		s_alloc_state(mem, s_C2R_template.stype, "C2R state");
531 	    int code = pixel_resize(pbw, pim->Width, 3, 8, bpc_out);
532 
533 	    if (code < 0 ||
534 		(code = psdf_encode_binary(pbw, &s_C2R_template,
535 					   (stream_state *) ss)) < 0 ||
536 		(code = pixel_resize(pbw, pim->Width, 4, bpc, 8)) < 0
537 		)
538 		return code;
539 	    s_C2R_init(ss, pis);
540 	} else {
541 	    code = pixel_resize(pbw, pim->Width, ncomp, bpc, bpc_out);
542 	    if (code < 0)
543 		return code;
544 	}
545     }
546     return code;
547 }
548 
549 /* Set up compression filters for a lossless image, with no downsampling, */
550 /* no color space conversion, and only lossless filters. */
551 /* Note that this may modify the image parameters. */
552 int
psdf_setup_lossless_filters(gx_device_psdf * pdev,psdf_binary_writer * pbw,gs_pixel_image_t * pim)553 psdf_setup_lossless_filters(gx_device_psdf *pdev, psdf_binary_writer *pbw,
554 			    gs_pixel_image_t *pim)
555 {
556     /*
557      * Set up a device with modified parameters for computing the image
558      * compression filters.  Don't allow downsampling or lossy compression.
559      */
560     gx_device_psdf ipdev;
561 
562     ipdev = *pdev;
563     ipdev.params.ColorImage.AutoFilter = false;
564     ipdev.params.ColorImage.Downsample = false;
565     ipdev.params.ColorImage.Filter = "FlateEncode";
566     ipdev.params.ColorImage.filter_template = &s_zlibE_template;
567     ipdev.params.ConvertCMYKImagesToRGB = false;
568     ipdev.params.GrayImage.AutoFilter = false;
569     ipdev.params.GrayImage.Downsample = false;
570     ipdev.params.GrayImage.Filter = "FlateEncode";
571     ipdev.params.GrayImage.filter_template = &s_zlibE_template;
572     return psdf_setup_image_filters(&ipdev, pbw, pim, NULL, NULL, true);
573 }
574 
575 /* Set up image compression chooser. */
576 int
psdf_setup_compression_chooser(psdf_binary_writer * pbw,gx_device_psdf * pdev,int width,int height,int depth,int bits_per_sample)577 psdf_setup_compression_chooser(psdf_binary_writer *pbw, gx_device_psdf *pdev,
578 		    int width, int height, int depth, int bits_per_sample)
579 {
580     int code;
581     stream_state *ss = s_alloc_state(pdev->memory, s_compr_chooser_template.stype,
582                                      "psdf_setup_compression_chooser");
583 
584     if (ss == 0)
585 	return_error(gs_error_VMerror);
586     pbw->memory = pdev->memory;
587     pbw->strm = pdev->strm; /* just a stub - will not write to it. */
588     pbw->dev = pdev;
589     pbw->target = pbw->strm; /* Since s_add_filter may insert NullEncode to comply buffering,
590 			         will need to close a chain of filetrs. */
591     code = psdf_encode_binary(pbw, &s_compr_chooser_template, ss);
592     if (code < 0)
593 	return code;
594     code = s_compr_chooser_set_dimensions((stream_compr_chooser_state *)ss,
595 		    width, height, depth, bits_per_sample);
596     return code;
597 }
598 
599 /* Set up an "image to mask" filter. */
600 int
psdf_setup_image_to_mask_filter(psdf_binary_writer * pbw,gx_device_psdf * pdev,int width,int height,int depth,int bits_per_sample,uint * MaskColor)601 psdf_setup_image_to_mask_filter(psdf_binary_writer *pbw, gx_device_psdf *pdev,
602 		    int width, int height, int depth, int bits_per_sample, uint *MaskColor)
603 {
604     int code;
605     stream_state *ss = s_alloc_state(pdev->memory, s__image_colors_template.stype,
606 	"psdf_setup_image_colors_filter");
607 
608     if (ss == 0)
609 	return_error(gs_error_VMerror);
610     pbw->memory = pdev->memory;
611     pbw->dev = pdev;
612     code = psdf_encode_binary(pbw, &s__image_colors_template, ss);
613     if (code < 0)
614 	return code;
615     s_image_colors_set_dimensions((stream_image_colors_state *)ss,
616 		    width, height, depth, bits_per_sample);
617     s_image_colors_set_mask_colors((stream_image_colors_state *)ss, MaskColor);
618     return 0;
619 }
620 
621 /* Set up an image colors filter. */
622 int
psdf_setup_image_colors_filter(psdf_binary_writer * pbw,gx_device_psdf * pdev,gs_pixel_image_t * pim,const gs_imager_state * pis,gs_color_space_index output_cspace_index)623 psdf_setup_image_colors_filter(psdf_binary_writer *pbw,
624 			       gx_device_psdf *pdev, gs_pixel_image_t * pim,
625 			       const gs_imager_state *pis,
626 			       gs_color_space_index output_cspace_index)
627 {   /* fixme: currently it's a stub convertion to mask. */
628     int code;
629     extern_st(st_color_space);
630     gs_memory_t *mem = pdev->v_memory;
631     stream_state *ss = s_alloc_state(pdev->memory, s__image_colors_template.stype,
632 	"psdf_setup_image_colors_filter");
633     gs_color_space *cs;
634     int i;
635 
636     if (ss == 0)
637 	return_error(gs_error_VMerror);
638     pbw->memory = pdev->memory;
639     pbw->dev = pdev;
640     code = psdf_encode_binary(pbw, &s__image_colors_template, ss);
641     if (code < 0)
642 	return code;
643     cs = gs_alloc_struct(mem, gs_color_space, &st_color_space,
644 			    "psdf_setup_image_colors_filter");
645     if (cs == NULL)
646 	return_error(gs_error_VMerror);
647     s_image_colors_set_dimensions((stream_image_colors_state *)ss,
648 		    pim->Width, pim->Height,
649 		    gs_color_space_num_components(pim->ColorSpace),
650 		    pim->BitsPerComponent);
651     s_image_colors_set_color_space((stream_image_colors_state *)ss,
652 		    (gx_device *)pdev, pim->ColorSpace, pis, pim->Decode);
653     pim->BitsPerComponent = pdev->color_info.comp_bits[0]; /* Same precision for all components. */
654     for (i = 0; i < pdev->color_info.num_components; i++) {
655 	pim->Decode[i * 2 + 0] = 0;
656 	pim->Decode[i * 2 + 1] = 1;
657     }
658     switch (output_cspace_index) {
659 	case gs_color_space_index_DeviceGray:
660 	    gs_cspace_init_DeviceGray(mem, cs);
661 	    break;
662 	case gs_color_space_index_DeviceRGB:
663 	    gs_cspace_init_DeviceRGB(mem, cs);
664 	    break;
665 	case gs_color_space_index_DeviceCMYK:
666 	    gs_cspace_init_DeviceCMYK(mem, cs);
667 	    break;
668 	default:
669 	    /* Notify the user and terminate.
670 	       Don't emit rangecheck becuause it would fall back
671 	       to a default implementation (rasterisation).
672 	     */
673 	    eprintf("Unsupported ProcessColorModel");
674 	    return_error(gs_error_undefined);
675     }
676     pim->ColorSpace = cs;
677     return 0;
678 }
679