1 /* Copyright (C) 2001-2006 Artifex Software, Inc.
2    All Rights Reserved.
3 
4    This software is provided AS-IS with no warranty, either express or
5    implied.
6 
7    This software is distributed under license and may not be copied, modified
8    or distributed except as expressly authorized under the terms of that
9    license.  Refer to licensing information at http://www.artifex.com/
10    or contact Artifex Software, Inc.,  7 Mt. Lassen Drive - Suite A-134,
11    San Rafael, CA  94903, U.S.A., +1(415)492-9861, for further information.
12 */
13 /* $Id: gdevjpeg.c 8250 2007-09-25 13:31:24Z giles $ */
14 /* JPEG output driver */
15 #include "stdio_.h"		/* for jpeglib.h */
16 #include "jpeglib_.h"
17 #include "gdevprn.h"
18 #include "stream.h"
19 #include "strimpl.h"
20 #include "sdct.h"
21 #include "sjpeg.h"
22 
23 /* Structure for the JPEG-writing device. */
24 typedef struct gx_device_jpeg_s {
25     gx_device_common;
26     gx_prn_device_common;
27     /* Additional parameters */
28     int JPEGQ;			/* quality on IJG scale */
29     float QFactor;		/* quality per DCTEncode conventions */
30     /* JPEGQ overrides QFactor if both are specified. */
31 
32     /** 1.0 default 2.0 is twice as big
33      */
34     gs_point ViewScale;
35 
36     /** translation needs to have scalefactor multiplied in.
37      */
38     gs_point ViewTrans;
39 
40 } gx_device_jpeg;
41 
42 /* The device descriptor */
43 static dev_proc_get_params(jpeg_get_params);
44 static dev_proc_get_initial_matrix(jpeg_get_initial_matrix);
45 static dev_proc_put_params(jpeg_put_params);
46 static dev_proc_print_page(jpeg_print_page);
47 static dev_proc_map_color_rgb(jpegcmyk_map_color_rgb);
48 static dev_proc_map_cmyk_color(jpegcmyk_map_cmyk_color);
49 
50 /* ------ The device descriptors ------ */
51 
52 /* Default X and Y resolution. */
53 #ifndef X_DPI
54 #  define X_DPI 72
55 #endif
56 #ifndef Y_DPI
57 #  define Y_DPI 72
58 #endif
59 
60 /* 24-bit color */
61 
62 static const gx_device_procs jpeg_procs =
63 {
64     gdev_prn_open,
65     jpeg_get_initial_matrix,	/* get_initial_matrix */
66     NULL,			/* sync_output */
67     gdev_prn_output_page,
68     gdev_prn_close,
69     gx_default_rgb_map_rgb_color,/* map_rgb_color */
70     gx_default_rgb_map_color_rgb,
71     NULL,			/* fill_rectangle */
72     NULL,			/* tile_rectangle */
73     NULL,			/* copy_mono */
74     NULL,			/* copy_color */
75     NULL,			/* draw_line */
76     NULL,			/* get_bits */
77     jpeg_get_params,
78     jpeg_put_params,
79     NULL,
80     NULL,			/* get_xfont_procs */
81     NULL,			/* get_xfont_device */
82     NULL,			/* map_rgb_alpha_color */
83     gx_page_device_get_page_device
84 };
85 
86 const gx_device_jpeg gs_jpeg_device =
87 {prn_device_std_body(gx_device_jpeg, jpeg_procs, "jpeg",
88 		     DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
89 		     X_DPI, Y_DPI, 0, 0, 0, 0, 24, jpeg_print_page),
90  0,				/* JPEGQ: 0 indicates not specified */
91  0.0,				/* QFactor: 0 indicates not specified */
92  { 1.0, 1.0 },                  /* ViewScale 1 to 1 */
93  { 0.0, 0.0 }                   /* translation 0 */
94 };
95 
96 /* 8-bit gray */
97 
98 static const gx_device_procs jpeggray_procs =
99 {
100     gdev_prn_open,
101     jpeg_get_initial_matrix,	/* get_initial_matrix */
102     NULL,			/* sync_output */
103     gdev_prn_output_page,
104     gdev_prn_close,
105     gx_default_gray_map_rgb_color,/* map_rgb_color */
106     gx_default_gray_map_color_rgb,
107     NULL,			/* fill_rectangle */
108     NULL,			/* tile_rectangle */
109     NULL,			/* copy_mono */
110     NULL,			/* copy_color */
111     NULL,			/* draw_line */
112     NULL,			/* get_bits */
113     jpeg_get_params,
114     jpeg_put_params,
115     NULL,
116     NULL,			/* get_xfont_procs */
117     NULL,			/* get_xfont_device */
118     NULL,			/* map_rgb_alpha_color */
119     gx_page_device_get_page_device
120 };
121 
122 const gx_device_jpeg gs_jpeggray_device =
123 {prn_device_body(gx_device_jpeg, jpeggray_procs, "jpeggray",
124 		 DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
125 		 X_DPI, Y_DPI, 0, 0, 0, 0,
126 		 1, 8, 255, 0, 256, 0,
127 		 jpeg_print_page),
128  0,				/* JPEGQ: 0 indicates not specified */
129  0.0,				/* QFactor: 0 indicates not specified */
130  { 1.0, 1.0 },                  /* ViewScale 1 to 1 */
131  { 0.0, 0.0 }                   /* translation 0 */
132 };
133 /* 32-bit CMYK */
134 
135 static const gx_device_procs jpegcmyk_procs =
136 {	gdev_prn_open,
137 	gx_default_get_initial_matrix,
138 	NULL,	/* sync_output */
139 	gdev_prn_output_page,
140 	gdev_prn_close,
141 	NULL,
142         jpegcmyk_map_color_rgb,
143 	NULL,	/* fill_rectangle */
144 	NULL,	/* tile_rectangle */
145 	NULL,	/* copy_mono */
146 	NULL,	/* copy_color */
147 	NULL,	/* draw_line */
148 	NULL,	/* get_bits */
149 	jpeg_get_params,
150 	jpeg_put_params,
151 	jpegcmyk_map_cmyk_color,
152 	NULL,	/* get_xfont_procs */
153 	NULL,	/* get_xfont_device */
154 	NULL,	/* map_rgb_alpha_color */
155 	gx_page_device_get_page_device	/* get_page_device */
156 };
157 
158 const gx_device_jpeg gs_jpegcmyk_device =
159 {prn_device_std_body(gx_device_jpeg, jpegcmyk_procs, "jpegcmyk",
160 		     DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
161 		     X_DPI, Y_DPI, 0, 0, 0, 0, 32, jpeg_print_page),
162  0,				/* JPEGQ: 0 indicates not specified */
163  0.0,				/* QFactor: 0 indicates not specified */
164  { 1.0, 1.0 },                  /* ViewScale 1 to 1 */
165  { 0.0, 0.0 }                   /* translation 0 */
166 };
167 
168 
169 /* Apparently Adobe Photoshop and some other applications that	*/
170 /* accept JPEG CMYK images expect color values to be inverted.	*/
171 static int
jpegcmyk_map_color_rgb(gx_device * dev,gx_color_index color,gx_color_value prgb[3])172 jpegcmyk_map_color_rgb(gx_device * dev, gx_color_index color,
173 			gx_color_value prgb[3])
174 {
175     int
176 	not_k = color & 0xff,
177 	r = not_k - ~(color >> 24),
178 	g = not_k - ~((color >> 16) & 0xff),
179 	b = not_k - ~((color >> 8) & 0xff);
180 
181     prgb[0] = (r < 0 ? 0 : gx_color_value_from_byte(r));
182     prgb[1] = (g < 0 ? 0 : gx_color_value_from_byte(g));
183     prgb[2] = (b < 0 ? 0 : gx_color_value_from_byte(b));
184     return 0;
185 }
186 
187 static gx_color_index
jpegcmyk_map_cmyk_color(gx_device * dev,const gx_color_value cv[])188 jpegcmyk_map_cmyk_color(gx_device * dev, const gx_color_value cv[])
189 {
190     gx_color_index color = ~(
191         gx_color_value_to_byte(cv[3]) +
192         ((uint)gx_color_value_to_byte(cv[2]) << 8) +
193         ((uint)gx_color_value_to_byte(cv[1]) << 16) +
194         ((uint)gx_color_value_to_byte(cv[0]) << 24));
195 
196     return (color == gx_no_color_index ? color ^ 1 : color);
197 }
198 
199 /* Get parameters. */
200 static int
jpeg_get_params(gx_device * dev,gs_param_list * plist)201 jpeg_get_params(gx_device * dev, gs_param_list * plist)
202 {
203     gx_device_jpeg *jdev = (gx_device_jpeg *) dev;
204     int code = gdev_prn_get_params(dev, plist);
205     int ecode;
206     float float2double;
207     if (code < 0)
208 	return code;
209 
210     if ((ecode = param_write_int(plist, "JPEGQ", &jdev->JPEGQ)) < 0)
211 	code = ecode;
212     if ((ecode = param_write_float(plist, "QFactor", &jdev->QFactor)) < 0)
213 	code = ecode;
214     float2double = jdev->ViewScale.x;
215     if ((ecode = param_write_float(plist, "ViewScaleX", &float2double)) < 0)
216 	code = ecode;
217     float2double = jdev->ViewScale.y;
218     if ((ecode = param_write_float(plist, "ViewScaleY", &float2double)) < 0)
219 	code = ecode;
220     float2double = jdev->ViewTrans.x;
221     if ((ecode = param_write_float(plist, "ViewTransX", &float2double)) < 0)
222 	code = ecode;
223     float2double = jdev->ViewTrans.y;
224     if ((ecode = param_write_float(plist, "ViewTransY", &float2double)) < 0)
225 	code = ecode;
226 
227     return code;
228 }
229 
230 /* Put parameters. */
231 static int
jpeg_put_params(gx_device * dev,gs_param_list * plist)232 jpeg_put_params(gx_device * dev, gs_param_list * plist)
233 {
234     gx_device_jpeg *jdev = (gx_device_jpeg *) dev;
235     int ecode = 0;
236     int code;
237     gs_param_name param_name;
238     int jq = jdev->JPEGQ;
239     float qf = jdev->QFactor;
240     float fparam;
241 
242     switch (code = param_read_int(plist, (param_name = "JPEGQ"), &jq)) {
243 	case 0:
244 	    if (jq < 0 || jq > 100)
245 		ecode = gs_error_limitcheck;
246 	    else
247 		break;
248 	    goto jqe;
249 	default:
250 	    ecode = code;
251 	  jqe:param_signal_error(plist, param_name, ecode);
252 	case 1:
253 	    break;
254     }
255 
256     switch (code = param_read_float(plist, (param_name = "QFactor"), &qf)) {
257 	case 0:
258 	    if (qf < 0.0 || qf > 1.0e6)
259 		ecode = gs_error_limitcheck;
260 	    else
261 		break;
262 	    goto qfe;
263 	default:
264 	    ecode = code;
265 	  qfe:param_signal_error(plist, param_name, ecode);
266 	case 1:
267 	    break;
268     }
269 
270 
271     code = param_read_float(plist, (param_name = "ViewScaleX"), &fparam);
272     if ( code == 0 ) {
273 	if (fparam < 1.0)
274 	    param_signal_error(plist, param_name, gs_error_limitcheck);
275 	else
276 	    jdev->ViewScale.x = fparam;
277     }
278     else if ( code < 1 ) {
279 	ecode = code;
280 	param_signal_error(plist, param_name, code);
281     }
282 
283     code = param_read_float(plist, (param_name = "ViewScaleY"), &fparam);
284     if ( code == 0 ) {
285 	if (fparam < 1.0)
286 	    param_signal_error(plist, param_name, gs_error_limitcheck);
287 	else
288 	    jdev->ViewScale.y = fparam;
289     }
290     else if ( code < 1 ) {
291 	ecode = code;
292 	param_signal_error(plist, param_name, code);
293     }
294 
295     /* pixels in desired dpi, auto negative ( moves up and left ) */
296     code = param_read_float(plist, (param_name = "ViewTransX"), &fparam);
297     if ( code == 0 ) {
298 	jdev->ViewTrans.x = fparam;
299     }
300     else if ( code < 1 ) {
301 	ecode = code;
302 	param_signal_error(plist, param_name, code);
303     }
304 
305     code = param_read_float(plist, (param_name = "ViewTransY"), &fparam);
306     if ( code == 0 ) {
307 	jdev->ViewTrans.y = fparam;
308     }
309     else if ( code < 1 ) {
310 	ecode = code;
311 	param_signal_error(plist, param_name, code);
312     }
313     code = gdev_prn_put_params(dev, plist);
314     if (code < 0)
315 	return code;
316 
317     if (ecode < 0)
318 	return ecode;
319 
320     jdev->JPEGQ = jq;
321     jdev->QFactor = qf;
322     return 0;
323 }
324 
325 /******************************************************************
326  This device supports translation and scaling.
327 
328 0123456
329 
330 0PPPPPPP  0 is origin
331 PPPPPPPP  1 is x1,y1 (2,2)
332 PP1vvvPP  2 is x2,y2 (6,6)
333 PPvvvvPP  v is viewport, P is original page
334 PPvvvvPP
335 PPPPPP2P
336 PPPPPPPP
337 
338 Given a view port in pixels starting at x1,y1
339 where x1 < width, y1 < height in pixels
340 
341 ViewScaleX = desired Resolution / HWResolution ; 1.0 default
342 ViewScaleY = desired Resolution / HWResolution
343 
344 HWResolutionX = desired dpi at 1:1 scaling     ; 72dpi default
345 HWResolutionY = desired dpi at 1:1 scaling
346 
347 ViewTransX = x1 * ViewScaleX                   ; 0.0 default
348 ViewTransY = y1 * ViewScaleY
349 
350 if initial matrix multiplies ViewScaleX in then translation is limited to
351 multiples of the HWResolution.
352 
353 ***************************************************************************/
354 
355 static void
jpeg_get_initial_matrix(gx_device * dev,gs_matrix * pmat)356 jpeg_get_initial_matrix(gx_device *dev, gs_matrix *pmat)
357 {
358     gx_device_jpeg *pdev = (gx_device_jpeg *)dev;
359     floatp fs_res = (dev->HWResolution[0] / 72.0) * pdev->ViewScale.x;
360     floatp ss_res = (dev->HWResolution[1] / 72.0) * pdev->ViewScale.y;
361 
362     /* NB this device has no paper margins */
363 
364     switch(pdev->LeadingEdge) {
365     case 1:
366         pmat->xx = 0;
367         pmat->xy = -ss_res;
368         pmat->yx = -fs_res;
369         pmat->yy = 0;
370         pmat->tx = (pdev->width * pdev->ViewScale.x) - pdev->ViewTrans.x;
371         pmat->ty = (pdev->height * pdev->ViewScale.y) - pdev->ViewTrans.y;
372         break;
373     case 2:
374         pmat->xx = -fs_res;
375         pmat->xy = 0;
376         pmat->yx = 0;
377         pmat->yy = ss_res;
378         pmat->tx = (pdev->width * pdev->ViewScale.x) - pdev->ViewTrans.x;
379         pmat->ty = -pdev->ViewTrans.x;
380         break;
381     case 3:
382         pmat->xx = 0;
383         pmat->xy = ss_res;
384         pmat->yx = fs_res;
385         pmat->yy = 0;
386         pmat->tx = -pdev->ViewTrans.x;
387         pmat->ty = -pdev->ViewTrans.y;
388         break;
389     default:
390     case 0:
391         pmat->xx = fs_res;
392         pmat->xy = 0;
393         pmat->yx = 0;
394         pmat->yy = -ss_res;
395         pmat->tx = -pdev->ViewTrans.x;
396         pmat->ty = (pdev->height * pdev->ViewScale.y) - pdev->ViewTrans.y;
397         break;
398     }
399 
400 }
401 
402 /* Send the page to the file. */
403 static int
jpeg_print_page(gx_device_printer * pdev,FILE * prn_stream)404 jpeg_print_page(gx_device_printer * pdev, FILE * prn_stream)
405 {
406     gx_device_jpeg *jdev = (gx_device_jpeg *) pdev;
407     gs_memory_t *mem = pdev->memory;
408     int line_size = gdev_mem_bytes_per_scan_line((gx_device *) pdev);
409     byte *in = gs_alloc_bytes(mem, line_size, "jpeg_print_page(in)");
410     jpeg_compress_data *jcdp = gs_alloc_struct_immovable(mem, jpeg_compress_data,
411       &st_jpeg_compress_data, "jpeg_print_page(jpeg_compress_data)");
412     byte *fbuf = 0;
413     uint fbuf_size;
414     byte *jbuf = 0;
415     uint jbuf_size;
416     int lnum;
417     int code;
418     stream_DCT_state state;
419     stream fstrm, jstrm;
420 
421     if (jcdp == 0 || in == 0) {
422 	code = gs_note_error(gs_error_VMerror);
423 	goto fail;
424     }
425     /* Create the DCT encoder state. */
426     jcdp->template = s_DCTE_template;
427     s_init_state((stream_state *)&state, &jcdp->template, 0);
428     if (state.template->set_defaults)
429 	(*state.template->set_defaults) ((stream_state *) & state);
430     state.QFactor = 1.0;	/* disable quality adjustment in zfdcte.c */
431     state.ColorTransform = 1;	/* default for RGB */
432     /* We insert no markers, allowing the IJG library to emit */
433     /* the format it thinks best. */
434     state.NoMarker = true;	/* do not insert our own Adobe marker */
435     state.Markers.data = 0;
436     state.Markers.size = 0;
437     state.data.compress = jcdp;
438     jcdp->memory = state.jpeg_memory = mem;
439     if ((code = gs_jpeg_create_compress(&state)) < 0)
440 	goto fail;
441     jcdp->cinfo.image_width = pdev->width;
442     jcdp->cinfo.image_height = pdev->height;
443     switch (pdev->color_info.depth) {
444 	case 32:
445 	    jcdp->cinfo.input_components = 4;
446 	    jcdp->cinfo.in_color_space = JCS_CMYK;
447 	    break;
448 	case 24:
449 	    jcdp->cinfo.input_components = 3;
450 	    jcdp->cinfo.in_color_space = JCS_RGB;
451 	    break;
452 	case 8:
453 	    jcdp->cinfo.input_components = 1;
454 	    jcdp->cinfo.in_color_space = JCS_GRAYSCALE;
455 	    break;
456     }
457     /* Set compression parameters. */
458     if ((code = gs_jpeg_set_defaults(&state)) < 0)
459 	goto done;
460     if (jdev->JPEGQ > 0) {
461 	code = gs_jpeg_set_quality(&state, jdev->JPEGQ, TRUE);
462 	if (code < 0)
463 	    goto done;
464     } else if (jdev->QFactor > 0.0) {
465 	code = gs_jpeg_set_linear_quality(&state,
466 					  (int)(min(jdev->QFactor, 100.0)
467 						* 100.0 + 0.5),
468 					  TRUE);
469 	if (code < 0)
470 	    goto done;
471     }
472     jcdp->cinfo.restart_interval = 0;
473     jcdp->cinfo.density_unit = 1;	/* dots/inch (no #define or enum) */
474     jcdp->cinfo.X_density = (UINT16)pdev->HWResolution[0];
475     jcdp->cinfo.Y_density = (UINT16)pdev->HWResolution[1];
476     /* Create the filter. */
477     /* Make sure we get at least a full scan line of input. */
478     state.scan_line_size = jcdp->cinfo.input_components *
479 	jcdp->cinfo.image_width;
480     jcdp->template.min_in_size =
481 	max(s_DCTE_template.min_in_size, state.scan_line_size);
482     /* Make sure we can write the user markers in a single go. */
483     jcdp->template.min_out_size =
484 	max(s_DCTE_template.min_out_size, state.Markers.size);
485 
486     /* Set up the streams. */
487     fbuf_size = max(512 /* arbitrary */ , jcdp->template.min_out_size);
488     jbuf_size = jcdp->template.min_in_size;
489     if ((fbuf = gs_alloc_bytes(mem, fbuf_size, "jpeg_print_page(fbuf)")) == 0 ||
490 	(jbuf = gs_alloc_bytes(mem, jbuf_size, "jpeg_print_page(jbuf)")) == 0
491 	) {
492 	code = gs_note_error(gs_error_VMerror);
493 	goto done;
494     }
495     s_init(&fstrm, mem);
496     swrite_file(&fstrm, prn_stream, fbuf, fbuf_size);
497     s_init(&jstrm, mem);
498     s_std_init(&jstrm, jbuf, jbuf_size, &s_filter_write_procs,
499 	       s_mode_write);
500     jstrm.state = (stream_state *) & state;
501     jstrm.procs.process = state.template->process;
502     jstrm.strm = &fstrm;
503     if (state.template->init)
504 	(*state.template->init) (jstrm.state);
505 
506     /* Copy the data to the output. */
507     for (lnum = 0; lnum < pdev->height; ++lnum) {
508 	byte *data;
509 	uint ignore_used;
510 
511         if (jstrm.end_status) {
512 	    code = gs_note_error(gs_error_ioerror);
513             goto done;
514         }
515 	gdev_prn_get_bits(pdev, lnum, in, &data);
516 	sputs(&jstrm, data, state.scan_line_size, &ignore_used);
517     }
518 
519     /* Wrap up. */
520     sclose(&jstrm);
521     sflush(&fstrm);
522     jcdp = 0;
523   done:
524     gs_free_object(mem, jbuf, "jpeg_print_page(jbuf)");
525     gs_free_object(mem, fbuf, "jpeg_print_page(fbuf)");
526     if (jcdp)
527 	gs_jpeg_destroy(&state);	/* frees *jcdp */
528     gs_free_object(mem, in, "jpeg_print_page(in)");
529     return code;
530   fail:
531     if (jcdp)
532 	gs_free_object(mem, jcdp, "jpeg_print_page(jpeg_compress_data)");
533     gs_free_object(mem, in, "jpeg_print_page(in)");
534     return code;
535 }
536