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(¶ms, 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, ¶ms, pim, resolution);
346 } else {
347 code = setup_image_compression(pbw, ¶ms, 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(¶ms, pim, resolution)) {
365 code = setup_downsampling(pbw, ¶ms, pim, resolution);
366 } else {
367 code = setup_image_compression(pbw, ¶ms, 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