1 /* Copyright (C) 1997, 2000 artofcode LLC.  All rights reserved.
2 
3   This program is free software; you can redistribute it and/or modify it
4   under the terms of the GNU General Public License as published by the
5   Free Software Foundation; either version 2 of the License, or (at your
6   option) any later version.
7 
8   This program is distributed in the hope that it will be useful, but
9   WITHOUT ANY WARRANTY; without even the implied warranty of
10   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11   General Public License for more details.
12 
13   You should have received a copy of the GNU General Public License along
14   with this program; if not, write to the Free Software Foundation, Inc.,
15   59 Temple Place, Suite 330, Boston, MA, 02111-1307.
16 
17 */
18 
19 /*$Id: gdevpsdi.c,v 1.15.2.1.2.1 2003/01/17 00:49:01 giles Exp $ */
20 /* Image compression for PostScript and PDF writers */
21 #include "stdio_.h"		/* for jpeglib.h */
22 #include "jpeglib_.h"		/* for sdct.h */
23 #include "math_.h"
24 #include "gx.h"
25 #include "gserrors.h"
26 #include "gscspace.h"
27 #include "gdevpsdf.h"
28 #include "gdevpsds.h"
29 #include "strimpl.h"
30 #include "scfx.h"
31 #include "sdct.h"
32 #include "sjpeg.h"
33 #include "slzwx.h"
34 #include "spngpx.h"
35 #include "srlx.h"
36 #include "szlibx.h"
37 
38 /* Define parameter-setting procedures. */
39 extern stream_state_proc_put_params(s_CF_put_params, stream_CF_state);
40 
41 /* ---------------- Image compression ---------------- */
42 
43 /*
44  * Add a filter to expand or reduce the pixel width if needed.
45  * At least one of bpc_in and bpc_out is 8; the other is 1, 2, 4, or 8,
46  * except if bpc_out is 8, bpc_in may be 12.
47  */
48 private int
pixel_resize(psdf_binary_writer * pbw,int width,int num_components,int bpc_in,int bpc_out)49 pixel_resize(psdf_binary_writer * pbw, int width, int num_components,
50 	     int bpc_in, int bpc_out)
51 {
52     gs_memory_t *mem = pbw->dev->v_memory;
53     const stream_template *template;
54     stream_1248_state *st;
55     int code;
56 
57     if (bpc_out == bpc_in)
58 	return 0;
59     if (bpc_in != 8) {
60 	static const stream_template *const exts[13] = {
61 	    0, &s_1_8_template, &s_2_8_template, 0, &s_4_8_template,
62 	    0, 0, 0, 0, 0, 0, 0, &s_12_8_template
63 	};
64 
65 	template = exts[bpc_in];
66     } else {
67 	static const stream_template *const rets[5] = {
68 	    0, &s_8_1_template, &s_8_2_template, 0, &s_8_4_template
69 	};
70 
71 	template = rets[bpc_out];
72     }
73     st = (stream_1248_state *)
74 	s_alloc_state(mem, template->stype, "pixel_resize state");
75     if (st == 0)
76 	return_error(gs_error_VMerror);
77     code = psdf_encode_binary(pbw, template, (stream_state *) st);
78     if (code < 0) {
79 	gs_free_object(mem, st, "pixel_resize state");
80 	return code;
81     }
82     s_1248_init(st, width, num_components);
83     return 0;
84 }
85 
86 /* Add the appropriate image compression filter, if any. */
87 private int
setup_image_compression(psdf_binary_writer * pbw,const psdf_image_params * pdip,const gs_pixel_image_t * pim)88 setup_image_compression(psdf_binary_writer *pbw, const psdf_image_params *pdip,
89 			const gs_pixel_image_t * pim)
90 {
91     gx_device_psdf *pdev = pbw->dev;
92     gs_memory_t *mem = pdev->v_memory;
93     const stream_template *template = pdip->filter_template;
94     const stream_template *orig_template = template;
95     const stream_template *lossless_template =
96 	(pdev->params.UseFlateCompression &&
97 	 pdev->version >= psdf_version_ll3 ?
98 	 &s_zlibE_template : &s_LZWE_template);
99     const gs_color_space *pcs = pim->ColorSpace; /* null if mask */
100     int Colors = (pcs ? gs_color_space_num_components(pcs) : 1);
101     bool Indexed =
102 	(pcs != 0 &&
103 	 gs_color_space_get_index(pcs) == gs_color_space_index_Indexed);
104     gs_c_param_list *dict = pdip->Dict;
105     stream_state *st;
106     int code;
107 
108     if (!pdip->Encode)		/* no compression */
109 	return 0;
110     if (pdip->AutoFilter) {
111 	/*
112 	 * Disregard the requested filter: use DCTEncode with ACSDict
113 	 * instead (or the lossless filter if the conditions for JPEG
114 	 * encoding aren't met).
115 	 *
116 	 * Even though this isn't obvious from the Adobe Tech Note,
117 	 * it appears that if UseFlateCompression is true, the default
118 	 * compressor for AutoFilter is FlateEncode, not LZWEncode.
119          */
120 	orig_template = template =
121           ( pim->Width < 64 || pim->Height < 64 ) ? lossless_template : &s_DCTE_template;
122 	dict = pdip->ACSDict;
123     }
124     gs_c_param_list_read(dict);	/* ensure param list is in read mode */
125     if (template == 0)	/* no compression */
126 	return 0;
127     if (pim->Width * pim->Height * Colors * pim->BitsPerComponent <= 160)	/* not worth compressing */
128 	return 0;
129     /* Only use DCTE for 8-bit, non-Indexed data. */
130     if (template == &s_DCTE_template) {
131 	if (Indexed ||
132             !(pdip->Downsample ?
133 	      pdip->Depth == 8 ||
134 	      (pdip->Depth == -1 && pim->BitsPerComponent == 8) :
135 	      pim->BitsPerComponent == 8)
136 	    ) {
137 	    /* Use LZW/Flate instead. */
138 	    template = lossless_template;
139 	}
140     }
141     st = s_alloc_state(mem, template->stype, "setup_image_compression");
142     if (st == 0)
143 	return_error(gs_error_VMerror);
144     if (template->set_defaults)
145 	(*template->set_defaults) (st);
146     if (template == &s_CFE_template) {
147 	stream_CFE_state *const ss = (stream_CFE_state *) st;
148 
149 	if (pdip->Dict != 0 && pdip->filter_template == template) {
150 	    s_CF_put_params((gs_param_list *)pdip->Dict,
151 			    (stream_CF_state *)ss); /* ignore errors */
152 	} else {
153 	    ss->K = -1;
154 	    ss->BlackIs1 = true;
155 	}
156 	ss->Columns = pim->Width;
157 	ss->Rows = (ss->EndOfBlock ? 0 : pim->Height);
158     } else if ((template == &s_LZWE_template ||
159 		template == &s_zlibE_template) &&
160 	       pdev->version >= psdf_version_ll3) {
161 	/* If not Indexed, add a PNGPredictor filter. */
162 	if (!Indexed) {
163 	    code = psdf_encode_binary(pbw, template, st);
164 	    if (code < 0)
165 		goto fail;
166 	    template = &s_PNGPE_template;
167 	    st = s_alloc_state(mem, template->stype, "setup_image_compression");
168 	    if (st == 0) {
169 		code = gs_note_error(gs_error_VMerror);
170 		goto fail;
171 	    }
172 	    if (template->set_defaults)
173 		(*template->set_defaults) (st);
174 	    {
175 		stream_PNGP_state *const ss = (stream_PNGP_state *) st;
176 
177 		ss->Colors = Colors;
178 		ss->Columns = pim->Width;
179 	    }
180 	}
181     } else if (template == &s_DCTE_template) {
182 	code = psdf_DCT_filter((dict != 0 && orig_template == template ?
183 				(gs_param_list *)dict : NULL),
184 			       st, pim->Width, pim->Height, Colors, pbw);
185 	if (code < 0)
186 	    goto fail;
187 	/* psdf_DCT_filter already did the psdf_encode_binary. */
188 	return 0;
189     }
190     code = psdf_encode_binary(pbw, template, st);
191     if (code >= 0)
192 	return 0;
193  fail:
194     gs_free_object(mem, st, "setup_image_compression");
195     return code;
196 }
197 
198 /* Determine whether an image should be downsampled. */
199 private bool
do_downsample(const psdf_image_params * pdip,const gs_pixel_image_t * pim,floatp resolution)200 do_downsample(const psdf_image_params *pdip, const gs_pixel_image_t *pim,
201 	      floatp resolution)
202 {
203     floatp factor = (int)(resolution / pdip->Resolution);
204 
205     return (pdip->Downsample && factor >= pdip->DownsampleThreshold &&
206 	    factor <= pim->Width && factor <= pim->Height);
207 }
208 
209 /* Add downsampling, antialiasing, and compression filters. */
210 /* Uses AntiAlias, Depth, DownsampleThreshold, DownsampleType, Resolution. */
211 /* Assumes do_downsampling() is true. */
212 private int
setup_downsampling(psdf_binary_writer * pbw,const psdf_image_params * pdip,gs_pixel_image_t * pim,floatp resolution)213 setup_downsampling(psdf_binary_writer * pbw, const psdf_image_params * pdip,
214 		   gs_pixel_image_t * pim, floatp resolution)
215 {
216     gx_device_psdf *pdev = pbw->dev;
217     /* Note: Bicubic is currently interpreted as Average. */
218     const stream_template *template =
219 	(pdip->DownsampleType == ds_Subsample ?
220 	 &s_Subsample_template : &s_Average_template);
221     int factor = (int)(resolution / pdip->Resolution);
222     int orig_bpc = pim->BitsPerComponent;
223     int orig_width = pim->Width;
224     int orig_height = pim->Height;
225     stream_state *st;
226     int code;
227 
228     st = s_alloc_state(pdev->v_memory, template->stype,
229 		       "setup_downsampling");
230     if (st == 0)
231 	return_error(gs_error_VMerror);
232     if (template->set_defaults)
233 	template->set_defaults(st);
234     {
235 	stream_Downsample_state *const ss = (stream_Downsample_state *) st;
236 
237 	ss->Colors =
238 	    (pim->ColorSpace == 0 ? 1 /*mask*/ :
239 	     gs_color_space_num_components(pim->ColorSpace));
240 	ss->WidthIn = pim->Width;
241 	ss->HeightIn = pim->Height;
242 	ss->XFactor = ss->YFactor = factor;
243 	ss->AntiAlias = pdip->AntiAlias;
244 	ss->padX = ss->padY = false; /* should be true */
245 	if (template->init)
246 	    template->init(st);
247 	pim->Width = s_Downsample_size_out(pim->Width, factor, ss->padX);
248 	pim->Height = s_Downsample_size_out(pim->Height, factor, ss->padY);
249 	pim->BitsPerComponent = pdip->Depth;
250 	gs_matrix_scale(&pim->ImageMatrix, (double)pim->Width / orig_width,
251 			(double)pim->Height / orig_height,
252 			&pim->ImageMatrix);
253 	/****** NO ANTI-ALIASING YET ******/
254 	if ((code = setup_image_compression(pbw, pdip, pim)) < 0 ||
255 	    (code = pixel_resize(pbw, pim->Width, ss->Colors,
256 				 8, pdip->Depth)) < 0 ||
257 	    (code = psdf_encode_binary(pbw, template, st)) < 0 ||
258 	    (code = pixel_resize(pbw, orig_width, ss->Colors,
259 				 orig_bpc, 8)) < 0
260 	    ) {
261 	    gs_free_object(pdev->v_memory, st, "setup_image_compression");
262 	    return code;
263 	}
264     }
265     return 0;
266 }
267 
268 /* Set up compression and downsampling filters for an image. */
269 /* Note that this may modify the image parameters. */
270 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)271 psdf_setup_image_filters(gx_device_psdf * pdev, psdf_binary_writer * pbw,
272 			 gs_pixel_image_t * pim, const gs_matrix * pctm,
273 			 const gs_imager_state * pis)
274 {
275     /*
276      * The following algorithms are per Adobe Tech Note # 5151,
277      * "Acrobat Distiller Parameters", revised 16 September 1996
278      * for Acrobat(TM) Distiller(TM) 3.0.
279      *
280      * The control structure is a little tricky, because filter
281      * pipelines must be constructed back-to-front.
282      */
283     int code = 0;
284     psdf_image_params params;
285     int bpc = pim->BitsPerComponent;
286     int bpc_out = pim->BitsPerComponent = min(bpc, 8);
287     int ncomp;
288     double resolution;
289 
290     /*
291      * The Adobe documentation doesn't say this, but mask images are
292      * compressed on the same basis as 1-bit-deep monochrome images,
293      * except that anti-aliasing (resolution/depth tradeoff) is not
294      * allowed.
295      */
296     if (pim->ColorSpace == NULL) { /* mask image */
297 	params = pdev->params.MonoImage;
298 	params.Depth = 1;
299 	ncomp = 1;
300     } else {
301 	ncomp = gs_color_space_num_components(pim->ColorSpace);
302 	if (ncomp == 1) {
303 	    if (bpc == 1)
304 		params = pdev->params.MonoImage;
305 	    else
306 		params = pdev->params.GrayImage;
307 	    if (params.Depth == -1)
308 		params.Depth = bpc;
309 	} else {
310 	    params = pdev->params.ColorImage;
311 	    /* params.Depth is reset below */
312 	}
313     }
314 
315     /*
316      * We can compute the image resolution by:
317      *    W / (W * ImageMatrix^-1 * CTM / HWResolution).
318      * We can replace W by 1 to simplify the computation.
319      */
320     if (pctm == 0)
321 	resolution = -1;
322     else {
323 	gs_point pt;
324 
325 	/* We could do both X and Y, but why bother? */
326 	gs_distance_transform_inverse(1.0, 0.0, &pim->ImageMatrix, &pt);
327 	gs_distance_transform(pt.x, pt.y, pctm, &pt);
328 	resolution = 1.0 / hypot(pt.x / pdev->HWResolution[0],
329 				 pt.y / pdev->HWResolution[1]);
330     }
331     if (ncomp == 1) {
332 	/* Monochrome, gray, or mask */
333 	/* Check for downsampling. */
334 	if (do_downsample(&params, pim, resolution)) {
335 	    /* Use the downsampled depth, not the original data depth. */
336 	    if (params.Depth == 1) {
337 		params.Filter = pdev->params.MonoImage.Filter;
338 		params.filter_template = pdev->params.MonoImage.filter_template;
339 		params.Dict = pdev->params.MonoImage.Dict;
340 	    } else {
341 		params.Filter = pdev->params.GrayImage.Filter;
342 		params.filter_template = pdev->params.GrayImage.filter_template;
343 		params.Dict = pdev->params.GrayImage.Dict;
344 	    }
345 	    code = setup_downsampling(pbw, &params, pim, resolution);
346 	} else {
347 	    code = setup_image_compression(pbw, &params, pim);
348 	}
349 	if (code < 0)
350 	    return code;
351 	code = pixel_resize(pbw, pim->Width, ncomp, bpc, bpc_out);
352     } else {
353 	/* Color */
354 	bool cmyk_to_rgb =
355 	    pdev->params.ConvertCMYKImagesToRGB &&
356 	    pis != 0 &&
357 	    gs_color_space_get_index(pim->ColorSpace) ==
358 	    gs_color_space_index_DeviceCMYK;
359 
360 	if (cmyk_to_rgb)
361 	    pim->ColorSpace = gs_cspace_DeviceRGB(pis);
362 	if (params.Depth == -1)
363 	    params.Depth = (cmyk_to_rgb ? 8 : bpc_out);
364 	if (do_downsample(&params, pim, resolution)) {
365 	    code = setup_downsampling(pbw, &params, pim, resolution);
366 	} else {
367 	    code = setup_image_compression(pbw, &params, pim);
368 	}
369 	if (cmyk_to_rgb) {
370 	    gs_memory_t *mem = pdev->v_memory;
371 	    stream_C2R_state *ss = (stream_C2R_state *)
372 		s_alloc_state(mem, s_C2R_template.stype, "C2R state");
373 	    int code = pixel_resize(pbw, pim->Width, 3, 8, bpc_out);
374 
375 	    if (code < 0 ||
376 		(code = psdf_encode_binary(pbw, &s_C2R_template,
377 					   (stream_state *) ss)) < 0 ||
378 		(code = pixel_resize(pbw, pim->Width, 4, bpc, 8)) < 0
379 		)
380 		return code;
381 	    s_C2R_init(ss, pis);
382 	} else {
383 	    code = pixel_resize(pbw, pim->Width, ncomp, bpc, bpc_out);
384 	    if (code < 0)
385 		return code;
386 	}
387     }
388     return code;
389 }
390 
391 /* Set up compression filters for a lossless image, with no downsampling, */
392 /* no color space conversion, and only lossless filters. */
393 /* Note that this may modify the image parameters. */
394 int
psdf_setup_lossless_filters(gx_device_psdf * pdev,psdf_binary_writer * pbw,gs_pixel_image_t * pim)395 psdf_setup_lossless_filters(gx_device_psdf *pdev, psdf_binary_writer *pbw,
396 			    gs_pixel_image_t *pim)
397 {
398     /*
399      * Set up a device with modified parameters for computing the image
400      * compression filters.  Don't allow downsampling or lossy compression.
401      */
402     gx_device_psdf ipdev;
403 
404     ipdev = *pdev;
405     ipdev.params.ColorImage.AutoFilter = false;
406     ipdev.params.ColorImage.Downsample = false;
407     ipdev.params.ColorImage.Filter = "FlateEncode";
408     ipdev.params.ColorImage.filter_template = &s_zlibE_template;
409     ipdev.params.ConvertCMYKImagesToRGB = false;
410     ipdev.params.GrayImage.AutoFilter = false;
411     ipdev.params.GrayImage.Downsample = false;
412     ipdev.params.GrayImage.Filter = "FlateEncode";
413     ipdev.params.GrayImage.filter_template = &s_zlibE_template;
414     return psdf_setup_image_filters(&ipdev, pbw, pim, NULL, NULL);
415 }
416