xref: /plan9/sys/src/cmd/gs/src/gdevjpeg.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
1 /* Copyright (C) 1997, 1998, 1999 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: gdevjpeg.c,v 1.10 2005/08/31 15:29:40 ray Exp $ */
18 /* JPEG output driver */
19 #include "stdio_.h"		/* for jpeglib.h */
20 #include "jpeglib_.h"
21 #include "gdevprn.h"
22 #include "stream.h"
23 #include "strimpl.h"
24 #include "sdct.h"
25 #include "sjpeg.h"
26 
27 /* Structure for the JPEG-writing device. */
28 typedef struct gx_device_jpeg_s {
29     gx_device_common;
30     gx_prn_device_common;
31     /* Additional parameters */
32     int JPEGQ;			/* quality on IJG scale */
33     float QFactor;		/* quality per DCTEncode conventions */
34     /* JPEGQ overrides QFactor if both are specified. */
35 } gx_device_jpeg;
36 
37 /* The device descriptor */
38 private dev_proc_get_params(jpeg_get_params);
39 private dev_proc_put_params(jpeg_put_params);
40 private dev_proc_print_page(jpeg_print_page);
41 private dev_proc_map_color_rgb(jpegcmyk_map_color_rgb);
42 private dev_proc_map_cmyk_color(jpegcmyk_map_cmyk_color);
43 
44 /* ------ The device descriptors ------ */
45 
46 /* Default X and Y resolution. */
47 #ifndef X_DPI
48 #  define X_DPI 72
49 #endif
50 #ifndef Y_DPI
51 #  define Y_DPI 72
52 #endif
53 
54 /* 24-bit color RGB */
55 
56 private const gx_device_procs jpeg_procs =
57 prn_color_params_procs(gdev_prn_open, gdev_prn_output_page, gdev_prn_close,
58 		       gx_default_rgb_map_rgb_color,
59 		       gx_default_rgb_map_color_rgb,
60 		       jpeg_get_params, jpeg_put_params);
61 
62 const gx_device_jpeg gs_jpeg_device =
63 {prn_device_std_body(gx_device_jpeg, jpeg_procs, "jpeg",
64 		     DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
65 		     X_DPI, Y_DPI, 0, 0, 0, 0, 24, jpeg_print_page),
66  0,				/* JPEGQ: 0 indicates not specified */
67  0.0				/* QFactor: 0 indicates not specified */
68 };
69 
70 /* 8-bit gray */
71 
72 private const gx_device_procs jpeggray_procs =
73 prn_color_params_procs(gdev_prn_open, gdev_prn_output_page, gdev_prn_close,
74 		       gx_default_gray_map_rgb_color,
75 		       gx_default_gray_map_color_rgb,
76 		       jpeg_get_params, jpeg_put_params);
77 
78 const gx_device_jpeg gs_jpeggray_device =
79 {prn_device_body(gx_device_jpeg, jpeggray_procs, "jpeggray",
80 		 DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
81 		 X_DPI, Y_DPI, 0, 0, 0, 0,
82 		 1, 8, 255, 0, 256, 0,
83 		 jpeg_print_page),
84  0,				/* JPEGQ: 0 indicates not specified */
85  0.0				/* QFactor: 0 indicates not specified */
86 };
87 
88 /* 32-bit CMYK */
89 
90 private const gx_device_procs jpegcmyk_procs =
91 {	gdev_prn_open,
92 	gx_default_get_initial_matrix,
93 	NULL,	/* sync_output */
94 	gdev_prn_output_page,
95 	gdev_prn_close,
96 	NULL,
97         jpegcmyk_map_color_rgb,
98 	NULL,	/* fill_rectangle */
99 	NULL,	/* tile_rectangle */
100 	NULL,	/* copy_mono */
101 	NULL,	/* copy_color */
102 	NULL,	/* draw_line */
103 	NULL,	/* get_bits */
104 	jpeg_get_params,
105 	jpeg_put_params,
106 	jpegcmyk_map_cmyk_color,
107 	NULL,	/* get_xfont_procs */
108 	NULL,	/* get_xfont_device */
109 	NULL,	/* map_rgb_alpha_color */
110 	gx_page_device_get_page_device	/* get_page_device */
111 };
112 
113 const gx_device_jpeg gs_jpegcmyk_device =
114 {prn_device_std_body(gx_device_jpeg, jpegcmyk_procs, "jpegcmyk",
115 		     DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
116 		     X_DPI, Y_DPI, 0, 0, 0, 0, 32, jpeg_print_page),
117  0,				/* JPEGQ: 0 indicates not specified */
118  0.0				/* QFactor: 0 indicates not specified */
119 };
120 
121 
122 /* Apparently Adobe Photoshop and some other applications that	*/
123 /* accept JPEG CMYK images expect color values to be inverted.	*/
124 private int
jpegcmyk_map_color_rgb(gx_device * dev,gx_color_index color,gx_color_value prgb[3])125 jpegcmyk_map_color_rgb(gx_device * dev, gx_color_index color,
126 			gx_color_value prgb[3])
127 {
128     int
129 	not_k = color & 0xff,
130 	r = not_k - ~(color >> 24),
131 	g = not_k - ~((color >> 16) & 0xff),
132 	b = not_k - ~((color >> 8) & 0xff);
133 
134     prgb[0] = (r < 0 ? 0 : gx_color_value_from_byte(r));
135     prgb[1] = (g < 0 ? 0 : gx_color_value_from_byte(g));
136     prgb[2] = (b < 0 ? 0 : gx_color_value_from_byte(b));
137     return 0;
138 }
139 
140 private gx_color_index
jpegcmyk_map_cmyk_color(gx_device * dev,const gx_color_value cv[])141 jpegcmyk_map_cmyk_color(gx_device * dev, const gx_color_value cv[])
142 {
143     gx_color_index color = ~(
144         gx_color_value_to_byte(cv[3]) +
145         ((uint)gx_color_value_to_byte(cv[2]) << 8) +
146         ((uint)gx_color_value_to_byte(cv[1]) << 16) +
147         ((uint)gx_color_value_to_byte(cv[0]) << 24));
148 
149     return (color == gx_no_color_index ? color ^ 1 : color);
150 }
151 
152 /* Get parameters. */
153 private int
jpeg_get_params(gx_device * dev,gs_param_list * plist)154 jpeg_get_params(gx_device * dev, gs_param_list * plist)
155 {
156     gx_device_jpeg *jdev = (gx_device_jpeg *) dev;
157     int code = gdev_prn_get_params(dev, plist);
158     int ecode;
159 
160     if (code < 0)
161 	return code;
162 
163     if ((ecode = param_write_int(plist, "JPEGQ", &jdev->JPEGQ)) < 0)
164 	code = ecode;
165     if ((ecode = param_write_float(plist, "QFactor", &jdev->QFactor)) < 0)
166 	code = ecode;
167 
168     return code;
169 }
170 
171 /* Put parameters. */
172 private int
jpeg_put_params(gx_device * dev,gs_param_list * plist)173 jpeg_put_params(gx_device * dev, gs_param_list * plist)
174 {
175     gx_device_jpeg *jdev = (gx_device_jpeg *) dev;
176     int ecode = 0;
177     int code;
178     gs_param_name param_name;
179     int jq = jdev->JPEGQ;
180     float qf = jdev->QFactor;
181 
182     switch (code = param_read_int(plist, (param_name = "JPEGQ"), &jq)) {
183 	case 0:
184 	    if (jq < 0 || jq > 100)
185 		ecode = gs_error_limitcheck;
186 	    else
187 		break;
188 	    goto jqe;
189 	default:
190 	    ecode = code;
191 	  jqe:param_signal_error(plist, param_name, ecode);
192 	case 1:
193 	    break;
194     }
195 
196     switch (code = param_read_float(plist, (param_name = "QFactor"), &qf)) {
197 	case 0:
198 	    if (qf < 0.0 || qf > 1.0e6)
199 		ecode = gs_error_limitcheck;
200 	    else
201 		break;
202 	    goto qfe;
203 	default:
204 	    ecode = code;
205 	  qfe:param_signal_error(plist, param_name, ecode);
206 	case 1:
207 	    break;
208     }
209 
210     if (ecode < 0)
211 	return ecode;
212     code = gdev_prn_put_params(dev, plist);
213     if (code < 0)
214 	return code;
215 
216     jdev->JPEGQ = jq;
217     jdev->QFactor = qf;
218     return 0;
219 }
220 
221 /* Send the page to the file. */
222 private int
jpeg_print_page(gx_device_printer * pdev,FILE * prn_stream)223 jpeg_print_page(gx_device_printer * pdev, FILE * prn_stream)
224 {
225     gx_device_jpeg *jdev = (gx_device_jpeg *) pdev;
226     gs_memory_t *mem = pdev->memory;
227     int line_size = gdev_mem_bytes_per_scan_line((gx_device *) pdev);
228     byte *in = gs_alloc_bytes(mem, line_size, "jpeg_print_page(in)");
229     jpeg_compress_data *jcdp = gs_alloc_struct_immovable(mem, jpeg_compress_data,
230       &st_jpeg_compress_data, "jpeg_print_page(jpeg_compress_data)");
231     byte *fbuf = 0;
232     uint fbuf_size;
233     byte *jbuf = 0;
234     uint jbuf_size;
235     int lnum;
236     int code;
237     stream_DCT_state state;
238     stream fstrm, jstrm;
239 
240     if (jcdp == 0 || in == 0) {
241 	code = gs_note_error(gs_error_VMerror);
242 	goto fail;
243     }
244     /* Create the DCT decoder state. */
245     jcdp->template = s_DCTE_template;
246     state.template = &jcdp->template;
247     state.memory = 0;
248     if (state.template->set_defaults)
249 	(*state.template->set_defaults) ((stream_state *) & state);
250     state.QFactor = 1.0;	/* disable quality adjustment in zfdcte.c */
251     state.ColorTransform = 1;	/* default for RGB */
252     /* We insert no markers, allowing the IJG library to emit */
253     /* the format it thinks best. */
254     state.NoMarker = true;	/* do not insert our own Adobe marker */
255     state.Markers.data = 0;
256     state.Markers.size = 0;
257     state.data.compress = jcdp;
258     jcdp->memory = state.jpeg_memory = mem;
259     if ((code = gs_jpeg_create_compress(&state)) < 0)
260 	goto fail;
261     jcdp->cinfo.image_width = pdev->width;
262     jcdp->cinfo.image_height = pdev->height;
263     switch (pdev->color_info.depth) {
264 	case 32:
265 	    jcdp->cinfo.input_components = 4;
266 	    jcdp->cinfo.in_color_space = JCS_CMYK;
267 	    break;
268 	case 24:
269 	    jcdp->cinfo.input_components = 3;
270 	    jcdp->cinfo.in_color_space = JCS_RGB;
271 	    break;
272 	case 8:
273 	    jcdp->cinfo.input_components = 1;
274 	    jcdp->cinfo.in_color_space = JCS_GRAYSCALE;
275 	    break;
276     }
277     /* Set compression parameters. */
278     if ((code = gs_jpeg_set_defaults(&state)) < 0)
279 	goto done;
280     if (jdev->JPEGQ > 0) {
281 	code = gs_jpeg_set_quality(&state, jdev->JPEGQ, TRUE);
282 	if (code < 0)
283 	    goto done;
284     } else if (jdev->QFactor > 0.0) {
285 	code = gs_jpeg_set_linear_quality(&state,
286 					  (int)(min(jdev->QFactor, 100.0)
287 						* 100.0 + 0.5),
288 					  TRUE);
289 	if (code < 0)
290 	    goto done;
291     }
292     jcdp->cinfo.restart_interval = 0;
293     jcdp->cinfo.density_unit = 1;	/* dots/inch (no #define or enum) */
294     jcdp->cinfo.X_density = (UINT16)pdev->HWResolution[0];
295     jcdp->cinfo.Y_density = (UINT16)pdev->HWResolution[1];
296     /* Create the filter. */
297     /* Make sure we get at least a full scan line of input. */
298     state.scan_line_size = jcdp->cinfo.input_components *
299 	jcdp->cinfo.image_width;
300     jcdp->template.min_in_size =
301 	max(s_DCTE_template.min_in_size, state.scan_line_size);
302     /* Make sure we can write the user markers in a single go. */
303     jcdp->template.min_out_size =
304 	max(s_DCTE_template.min_out_size, state.Markers.size);
305 
306     /* Set up the streams. */
307     fbuf_size = max(512 /* arbitrary */ , jcdp->template.min_out_size);
308     jbuf_size = jcdp->template.min_in_size;
309     if ((fbuf = gs_alloc_bytes(mem, fbuf_size, "jpeg_print_page(fbuf)")) == 0 ||
310 	(jbuf = gs_alloc_bytes(mem, jbuf_size, "jpeg_print_page(jbuf)")) == 0
311 	) {
312 	code = gs_note_error(gs_error_VMerror);
313 	goto done;
314     }
315     s_init(&fstrm, mem);
316     swrite_file(&fstrm, prn_stream, fbuf, fbuf_size);
317     s_init(&jstrm, mem);
318     s_std_init(&jstrm, jbuf, jbuf_size, &s_filter_write_procs,
319 	       s_mode_write);
320     jstrm.state = (stream_state *) & state;
321     jstrm.procs.process = state.template->process;
322     jstrm.strm = &fstrm;
323     if (state.template->init)
324 	(*state.template->init) (jstrm.state);
325 
326     /* Copy the data to the output. */
327     for (lnum = 0; lnum < pdev->height; ++lnum) {
328 	byte *data;
329 	uint ignore_used;
330 
331 	gdev_prn_get_bits(pdev, lnum, in, &data);
332 	sputs(&jstrm, data, state.scan_line_size, &ignore_used);
333     }
334 
335     /* Wrap up. */
336     sclose(&jstrm);
337     sflush(&fstrm);
338     jcdp = 0;
339   done:
340     gs_free_object(mem, jbuf, "jpeg_print_page(jbuf)");
341     gs_free_object(mem, fbuf, "jpeg_print_page(fbuf)");
342     if (jcdp)
343 	gs_jpeg_destroy(&state);	/* frees *jcdp */
344     gs_free_object(mem, in, "jpeg_print_page(in)");
345     return code;
346   fail:
347     if (jcdp)
348 	gs_free_object(mem, jcdp, "jpeg_print_page(jpeg_compress_data)");
349     gs_free_object(mem, in, "jpeg_print_page(in)");
350     return code;
351 }
352