summaryrefslogtreecommitdiff
path: root/gs/base/gdevjpeg.c
diff options
context:
space:
mode:
Diffstat (limited to 'gs/base/gdevjpeg.c')
-rw-r--r--gs/base/gdevjpeg.c535
1 files changed, 535 insertions, 0 deletions
diff --git a/gs/base/gdevjpeg.c b/gs/base/gdevjpeg.c
new file mode 100644
index 000000000..6a1e24a67
--- /dev/null
+++ b/gs/base/gdevjpeg.c
@@ -0,0 +1,535 @@
+/* Copyright (C) 2001-2006 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied, modified
+ or distributed except as expressly authorized under the terms of that
+ license. Refer to licensing information at http://www.artifex.com/
+ or contact Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134,
+ San Rafael, CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+/* $Id$ */
+/* JPEG output driver */
+#include "stdio_.h" /* for jpeglib.h */
+#include "jpeglib_.h"
+#include "gdevprn.h"
+#include "stream.h"
+#include "strimpl.h"
+#include "sdct.h"
+#include "sjpeg.h"
+
+/* Structure for the JPEG-writing device. */
+typedef struct gx_device_jpeg_s {
+ gx_device_common;
+ gx_prn_device_common;
+ /* Additional parameters */
+ int JPEGQ; /* quality on IJG scale */
+ float QFactor; /* quality per DCTEncode conventions */
+ /* JPEGQ overrides QFactor if both are specified. */
+
+ /** 1.0 default 2.0 is twice as big
+ */
+ gs_point ViewScale;
+
+ /** translation needs to have scalefactor multiplied in.
+ */
+ gs_point ViewTrans;
+
+} gx_device_jpeg;
+
+/* The device descriptor */
+static dev_proc_get_params(jpeg_get_params);
+static dev_proc_get_initial_matrix(jpeg_get_initial_matrix);
+static dev_proc_put_params(jpeg_put_params);
+static dev_proc_print_page(jpeg_print_page);
+static dev_proc_map_color_rgb(jpegcmyk_map_color_rgb);
+static dev_proc_map_cmyk_color(jpegcmyk_map_cmyk_color);
+
+/* ------ The device descriptors ------ */
+
+/* Default X and Y resolution. */
+#ifndef X_DPI
+# define X_DPI 72
+#endif
+#ifndef Y_DPI
+# define Y_DPI 72
+#endif
+
+/* 24-bit color */
+
+static const gx_device_procs jpeg_procs =
+{
+ gdev_prn_open,
+ jpeg_get_initial_matrix, /* get_initial_matrix */
+ NULL, /* sync_output */
+ gdev_prn_output_page,
+ gdev_prn_close,
+ gx_default_rgb_map_rgb_color,/* map_rgb_color */
+ gx_default_rgb_map_color_rgb,
+ NULL, /* fill_rectangle */
+ NULL, /* tile_rectangle */
+ NULL, /* copy_mono */
+ NULL, /* copy_color */
+ NULL, /* draw_line */
+ NULL, /* get_bits */
+ jpeg_get_params,
+ jpeg_put_params,
+ NULL,
+ NULL, /* get_xfont_procs */
+ NULL, /* get_xfont_device */
+ NULL, /* map_rgb_alpha_color */
+ gx_page_device_get_page_device
+};
+
+const gx_device_jpeg gs_jpeg_device =
+{prn_device_std_body(gx_device_jpeg, jpeg_procs, "jpeg",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI, 0, 0, 0, 0, 24, jpeg_print_page),
+ 0, /* JPEGQ: 0 indicates not specified */
+ 0.0, /* QFactor: 0 indicates not specified */
+ { 1.0, 1.0 }, /* ViewScale 1 to 1 */
+ { 0.0, 0.0 } /* translation 0 */
+};
+
+/* 8-bit gray */
+
+static const gx_device_procs jpeggray_procs =
+{
+ gdev_prn_open,
+ jpeg_get_initial_matrix, /* get_initial_matrix */
+ NULL, /* sync_output */
+ gdev_prn_output_page,
+ gdev_prn_close,
+ gx_default_gray_map_rgb_color,/* map_rgb_color */
+ gx_default_gray_map_color_rgb,
+ NULL, /* fill_rectangle */
+ NULL, /* tile_rectangle */
+ NULL, /* copy_mono */
+ NULL, /* copy_color */
+ NULL, /* draw_line */
+ NULL, /* get_bits */
+ jpeg_get_params,
+ jpeg_put_params,
+ NULL,
+ NULL, /* get_xfont_procs */
+ NULL, /* get_xfont_device */
+ NULL, /* map_rgb_alpha_color */
+ gx_page_device_get_page_device
+};
+
+const gx_device_jpeg gs_jpeggray_device =
+{prn_device_body(gx_device_jpeg, jpeggray_procs, "jpeggray",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI, 0, 0, 0, 0,
+ 1, 8, 255, 0, 256, 0,
+ jpeg_print_page),
+ 0, /* JPEGQ: 0 indicates not specified */
+ 0.0, /* QFactor: 0 indicates not specified */
+ { 1.0, 1.0 }, /* ViewScale 1 to 1 */
+ { 0.0, 0.0 } /* translation 0 */
+};
+/* 32-bit CMYK */
+
+static const gx_device_procs jpegcmyk_procs =
+{ gdev_prn_open,
+ gx_default_get_initial_matrix,
+ NULL, /* sync_output */
+ gdev_prn_output_page,
+ gdev_prn_close,
+ NULL,
+ jpegcmyk_map_color_rgb,
+ NULL, /* fill_rectangle */
+ NULL, /* tile_rectangle */
+ NULL, /* copy_mono */
+ NULL, /* copy_color */
+ NULL, /* draw_line */
+ NULL, /* get_bits */
+ jpeg_get_params,
+ jpeg_put_params,
+ jpegcmyk_map_cmyk_color,
+ NULL, /* get_xfont_procs */
+ NULL, /* get_xfont_device */
+ NULL, /* map_rgb_alpha_color */
+ gx_page_device_get_page_device /* get_page_device */
+};
+
+const gx_device_jpeg gs_jpegcmyk_device =
+{prn_device_std_body(gx_device_jpeg, jpegcmyk_procs, "jpegcmyk",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI, 0, 0, 0, 0, 32, jpeg_print_page),
+ 0, /* JPEGQ: 0 indicates not specified */
+ 0.0, /* QFactor: 0 indicates not specified */
+ { 1.0, 1.0 }, /* ViewScale 1 to 1 */
+ { 0.0, 0.0 } /* translation 0 */
+};
+
+
+/* Apparently Adobe Photoshop and some other applications that */
+/* accept JPEG CMYK images expect color values to be inverted. */
+static int
+jpegcmyk_map_color_rgb(gx_device * dev, gx_color_index color,
+ gx_color_value prgb[3])
+{
+ int
+ not_k = color & 0xff,
+ r = not_k - ~(color >> 24),
+ g = not_k - ~((color >> 16) & 0xff),
+ b = not_k - ~((color >> 8) & 0xff);
+
+ prgb[0] = (r < 0 ? 0 : gx_color_value_from_byte(r));
+ prgb[1] = (g < 0 ? 0 : gx_color_value_from_byte(g));
+ prgb[2] = (b < 0 ? 0 : gx_color_value_from_byte(b));
+ return 0;
+}
+
+static gx_color_index
+jpegcmyk_map_cmyk_color(gx_device * dev, const gx_color_value cv[])
+{
+ gx_color_index color = ~(
+ gx_color_value_to_byte(cv[3]) +
+ ((uint)gx_color_value_to_byte(cv[2]) << 8) +
+ ((uint)gx_color_value_to_byte(cv[1]) << 16) +
+ ((uint)gx_color_value_to_byte(cv[0]) << 24));
+
+ return (color == gx_no_color_index ? color ^ 1 : color);
+}
+
+/* Get parameters. */
+static int
+jpeg_get_params(gx_device * dev, gs_param_list * plist)
+{
+ gx_device_jpeg *jdev = (gx_device_jpeg *) dev;
+ int code = gdev_prn_get_params(dev, plist);
+ int ecode;
+ float float2double;
+ if (code < 0)
+ return code;
+
+ if ((ecode = param_write_int(plist, "JPEGQ", &jdev->JPEGQ)) < 0)
+ code = ecode;
+ if ((ecode = param_write_float(plist, "QFactor", &jdev->QFactor)) < 0)
+ code = ecode;
+ float2double = jdev->ViewScale.x;
+ if ((ecode = param_write_float(plist, "ViewScaleX", &float2double)) < 0)
+ code = ecode;
+ float2double = jdev->ViewScale.y;
+ if ((ecode = param_write_float(plist, "ViewScaleY", &float2double)) < 0)
+ code = ecode;
+ float2double = jdev->ViewTrans.x;
+ if ((ecode = param_write_float(plist, "ViewTransX", &float2double)) < 0)
+ code = ecode;
+ float2double = jdev->ViewTrans.y;
+ if ((ecode = param_write_float(plist, "ViewTransY", &float2double)) < 0)
+ code = ecode;
+
+ return code;
+}
+
+/* Put parameters. */
+static int
+jpeg_put_params(gx_device * dev, gs_param_list * plist)
+{
+ gx_device_jpeg *jdev = (gx_device_jpeg *) dev;
+ int ecode = 0;
+ int code;
+ gs_param_name param_name;
+ int jq = jdev->JPEGQ;
+ float qf = jdev->QFactor;
+ float fparam;
+
+ switch (code = param_read_int(plist, (param_name = "JPEGQ"), &jq)) {
+ case 0:
+ if (jq < 0 || jq > 100)
+ ecode = gs_error_limitcheck;
+ else
+ break;
+ goto jqe;
+ default:
+ ecode = code;
+ jqe:param_signal_error(plist, param_name, ecode);
+ case 1:
+ break;
+ }
+
+ switch (code = param_read_float(plist, (param_name = "QFactor"), &qf)) {
+ case 0:
+ if (qf < 0.0 || qf > 1.0e6)
+ ecode = gs_error_limitcheck;
+ else
+ break;
+ goto qfe;
+ default:
+ ecode = code;
+ qfe:param_signal_error(plist, param_name, ecode);
+ case 1:
+ break;
+ }
+
+
+ code = param_read_float(plist, (param_name = "ViewScaleX"), &fparam);
+ if ( code == 0 ) {
+ if (fparam < 1.0)
+ param_signal_error(plist, param_name, gs_error_limitcheck);
+ else
+ jdev->ViewScale.x = fparam;
+ }
+ else if ( code < 1 ) {
+ ecode = code;
+ param_signal_error(plist, param_name, code);
+ }
+
+ code = param_read_float(plist, (param_name = "ViewScaleY"), &fparam);
+ if ( code == 0 ) {
+ if (fparam < 1.0)
+ param_signal_error(plist, param_name, gs_error_limitcheck);
+ else
+ jdev->ViewScale.y = fparam;
+ }
+ else if ( code < 1 ) {
+ ecode = code;
+ param_signal_error(plist, param_name, code);
+ }
+
+ /* pixels in desired dpi, auto negative ( moves up and left ) */
+ code = param_read_float(plist, (param_name = "ViewTransX"), &fparam);
+ if ( code == 0 ) {
+ jdev->ViewTrans.x = fparam;
+ }
+ else if ( code < 1 ) {
+ ecode = code;
+ param_signal_error(plist, param_name, code);
+ }
+
+ code = param_read_float(plist, (param_name = "ViewTransY"), &fparam);
+ if ( code == 0 ) {
+ jdev->ViewTrans.y = fparam;
+ }
+ else if ( code < 1 ) {
+ ecode = code;
+ param_signal_error(plist, param_name, code);
+ }
+ code = gdev_prn_put_params(dev, plist);
+ if (code < 0)
+ return code;
+
+ if (ecode < 0)
+ return ecode;
+
+ jdev->JPEGQ = jq;
+ jdev->QFactor = qf;
+ return 0;
+}
+
+/******************************************************************
+ This device supports translation and scaling.
+
+0123456
+
+0PPPPPPP 0 is origin
+PPPPPPPP 1 is x1,y1 (2,2)
+PP1vvvPP 2 is x2,y2 (6,6)
+PPvvvvPP v is viewport, P is original page
+PPvvvvPP
+PPPPPP2P
+PPPPPPPP
+
+Given a view port in pixels starting at x1,y1
+where x1 < width, y1 < height in pixels
+
+ViewScaleX = desired Resolution / HWResolution ; 1.0 default
+ViewScaleY = desired Resolution / HWResolution
+
+HWResolutionX = desired dpi at 1:1 scaling ; 72dpi default
+HWResolutionY = desired dpi at 1:1 scaling
+
+ViewTransX = x1 * ViewScaleX ; 0.0 default
+ViewTransY = y1 * ViewScaleY
+
+if initial matrix multiplies ViewScaleX in then translation is limited to
+multiples of the HWResolution.
+
+***************************************************************************/
+
+static void
+jpeg_get_initial_matrix(gx_device *dev, gs_matrix *pmat)
+{
+ gx_device_jpeg *pdev = (gx_device_jpeg *)dev;
+ floatp fs_res = (dev->HWResolution[0] / 72.0) * pdev->ViewScale.x;
+ floatp ss_res = (dev->HWResolution[1] / 72.0) * pdev->ViewScale.y;
+
+ /* NB this device has no paper margins */
+
+ switch(pdev->LeadingEdge) {
+ case 1:
+ pmat->xx = 0;
+ pmat->xy = -ss_res;
+ pmat->yx = -fs_res;
+ pmat->yy = 0;
+ pmat->tx = (pdev->width * pdev->ViewScale.x) - pdev->ViewTrans.x;
+ pmat->ty = (pdev->height * pdev->ViewScale.y) - pdev->ViewTrans.y;
+ break;
+ case 2:
+ pmat->xx = -fs_res;
+ pmat->xy = 0;
+ pmat->yx = 0;
+ pmat->yy = ss_res;
+ pmat->tx = (pdev->width * pdev->ViewScale.x) - pdev->ViewTrans.x;
+ pmat->ty = -pdev->ViewTrans.x;
+ break;
+ case 3:
+ pmat->xx = 0;
+ pmat->xy = ss_res;
+ pmat->yx = fs_res;
+ pmat->yy = 0;
+ pmat->tx = -pdev->ViewTrans.x;
+ pmat->ty = -pdev->ViewTrans.y;
+ break;
+ default:
+ case 0:
+ pmat->xx = fs_res;
+ pmat->xy = 0;
+ pmat->yx = 0;
+ pmat->yy = -ss_res;
+ pmat->tx = -pdev->ViewTrans.x;
+ pmat->ty = (pdev->height * pdev->ViewScale.y) - pdev->ViewTrans.y;
+ break;
+ }
+
+}
+
+/* Send the page to the file. */
+static int
+jpeg_print_page(gx_device_printer * pdev, FILE * prn_stream)
+{
+ gx_device_jpeg *jdev = (gx_device_jpeg *) pdev;
+ gs_memory_t *mem = pdev->memory;
+ int line_size = gdev_mem_bytes_per_scan_line((gx_device *) pdev);
+ byte *in = gs_alloc_bytes(mem, line_size, "jpeg_print_page(in)");
+ jpeg_compress_data *jcdp = gs_alloc_struct_immovable(mem, jpeg_compress_data,
+ &st_jpeg_compress_data, "jpeg_print_page(jpeg_compress_data)");
+ byte *fbuf = 0;
+ uint fbuf_size;
+ byte *jbuf = 0;
+ uint jbuf_size;
+ int lnum;
+ int code;
+ stream_DCT_state state;
+ stream fstrm, jstrm;
+
+ if (jcdp == 0 || in == 0) {
+ code = gs_note_error(gs_error_VMerror);
+ goto fail;
+ }
+ /* Create the DCT encoder state. */
+ jcdp->template = s_DCTE_template;
+ s_init_state((stream_state *)&state, &jcdp->template, 0);
+ if (state.template->set_defaults)
+ (*state.template->set_defaults) ((stream_state *) & state);
+ state.QFactor = 1.0; /* disable quality adjustment in zfdcte.c */
+ state.ColorTransform = 1; /* default for RGB */
+ /* We insert no markers, allowing the IJG library to emit */
+ /* the format it thinks best. */
+ state.NoMarker = true; /* do not insert our own Adobe marker */
+ state.Markers.data = 0;
+ state.Markers.size = 0;
+ state.data.compress = jcdp;
+ jcdp->memory = state.jpeg_memory = mem;
+ if ((code = gs_jpeg_create_compress(&state)) < 0)
+ goto fail;
+ jcdp->cinfo.image_width = pdev->width;
+ jcdp->cinfo.image_height = pdev->height;
+ switch (pdev->color_info.depth) {
+ case 32:
+ jcdp->cinfo.input_components = 4;
+ jcdp->cinfo.in_color_space = JCS_CMYK;
+ break;
+ case 24:
+ jcdp->cinfo.input_components = 3;
+ jcdp->cinfo.in_color_space = JCS_RGB;
+ break;
+ case 8:
+ jcdp->cinfo.input_components = 1;
+ jcdp->cinfo.in_color_space = JCS_GRAYSCALE;
+ break;
+ }
+ /* Set compression parameters. */
+ if ((code = gs_jpeg_set_defaults(&state)) < 0)
+ goto done;
+ if (jdev->JPEGQ > 0) {
+ code = gs_jpeg_set_quality(&state, jdev->JPEGQ, TRUE);
+ if (code < 0)
+ goto done;
+ } else if (jdev->QFactor > 0.0) {
+ code = gs_jpeg_set_linear_quality(&state,
+ (int)(min(jdev->QFactor, 100.0)
+ * 100.0 + 0.5),
+ TRUE);
+ if (code < 0)
+ goto done;
+ }
+ jcdp->cinfo.restart_interval = 0;
+ jcdp->cinfo.density_unit = 1; /* dots/inch (no #define or enum) */
+ jcdp->cinfo.X_density = (UINT16)pdev->HWResolution[0];
+ jcdp->cinfo.Y_density = (UINT16)pdev->HWResolution[1];
+ /* Create the filter. */
+ /* Make sure we get at least a full scan line of input. */
+ state.scan_line_size = jcdp->cinfo.input_components *
+ jcdp->cinfo.image_width;
+ jcdp->template.min_in_size =
+ max(s_DCTE_template.min_in_size, state.scan_line_size);
+ /* Make sure we can write the user markers in a single go. */
+ jcdp->template.min_out_size =
+ max(s_DCTE_template.min_out_size, state.Markers.size);
+
+ /* Set up the streams. */
+ fbuf_size = max(512 /* arbitrary */ , jcdp->template.min_out_size);
+ jbuf_size = jcdp->template.min_in_size;
+ if ((fbuf = gs_alloc_bytes(mem, fbuf_size, "jpeg_print_page(fbuf)")) == 0 ||
+ (jbuf = gs_alloc_bytes(mem, jbuf_size, "jpeg_print_page(jbuf)")) == 0
+ ) {
+ code = gs_note_error(gs_error_VMerror);
+ goto done;
+ }
+ s_init(&fstrm, mem);
+ swrite_file(&fstrm, prn_stream, fbuf, fbuf_size);
+ s_init(&jstrm, mem);
+ s_std_init(&jstrm, jbuf, jbuf_size, &s_filter_write_procs,
+ s_mode_write);
+ jstrm.state = (stream_state *) & state;
+ jstrm.procs.process = state.template->process;
+ jstrm.strm = &fstrm;
+ if (state.template->init)
+ (*state.template->init) (jstrm.state);
+
+ /* Copy the data to the output. */
+ for (lnum = 0; lnum < pdev->height; ++lnum) {
+ byte *data;
+ uint ignore_used;
+
+ if (jstrm.end_status) {
+ code = gs_note_error(gs_error_ioerror);
+ goto done;
+ }
+ gdev_prn_get_bits(pdev, lnum, in, &data);
+ sputs(&jstrm, data, state.scan_line_size, &ignore_used);
+ }
+
+ /* Wrap up. */
+ sclose(&jstrm);
+ sflush(&fstrm);
+ jcdp = 0;
+ done:
+ gs_free_object(mem, jbuf, "jpeg_print_page(jbuf)");
+ gs_free_object(mem, fbuf, "jpeg_print_page(fbuf)");
+ if (jcdp)
+ gs_jpeg_destroy(&state); /* frees *jcdp */
+ gs_free_object(mem, in, "jpeg_print_page(in)");
+ return code;
+ fail:
+ if (jcdp)
+ gs_free_object(mem, jcdp, "jpeg_print_page(jpeg_compress_data)");
+ gs_free_object(mem, in, "jpeg_print_page(in)");
+ return code;
+}