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