2 |
- |
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
|
|
|
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
|
|
|
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
|
|
|
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
|
|
|
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, ¶m, 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
|
|
|
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
|
|
|
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
|
|
|
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
|
|
|
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(¶ms, 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, ¶ms, pim, pis, resolution, lossless);
|
|
|
499 |
} else {
|
|
|
500 |
code = setup_image_compression(pbw, ¶ms, 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(¶ms, pim, resolution)) {
|
|
|
521 |
code = setup_downsampling(pbw, ¶ms, pim, pis, resolution, lossless);
|
|
|
522 |
} else {
|
|
|
523 |
code = setup_image_compression(pbw, ¶ms, 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
|
|
|
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
|
|
|
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
|
|
|
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
|
|
|
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 |
}
|