summaryrefslogtreecommitdiff
path: root/devices/gdevpcx.c
diff options
context:
space:
mode:
Diffstat (limited to 'devices/gdevpcx.c')
-rw-r--r--devices/gdevpcx.c465
1 files changed, 465 insertions, 0 deletions
diff --git a/devices/gdevpcx.c b/devices/gdevpcx.c
new file mode 100644
index 000000000..416859849
--- /dev/null
+++ b/devices/gdevpcx.c
@@ -0,0 +1,465 @@
+/* Copyright (C) 2001-2012 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 the license contained in the file LICENSE in this distribution.
+
+ 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.
+*/
+
+
+/* PCX file format drivers */
+#include "gdevprn.h"
+#include "gdevpccm.h"
+#include "gxlum.h"
+
+/* Thanks to Phil Conrad for donating the original version */
+/* of these drivers to Aladdin Enterprises. */
+
+/* ------ The device descriptors ------ */
+
+/*
+ * Default X and Y resolution.
+ */
+#define X_DPI 72
+#define Y_DPI 72
+
+/* Monochrome. */
+
+static dev_proc_print_page(pcxmono_print_page);
+
+/* Use the default RGB->color map, so we get black=0, white=1. */
+/* Since the print_page doesn't alter the device, this device can print in the background */
+static const gx_device_procs pcxmono_procs =
+prn_color_procs(gdev_prn_open, gdev_prn_bg_output_page, gdev_prn_close,
+ gx_default_map_rgb_color, gx_default_map_color_rgb);
+const gx_device_printer gs_pcxmono_device =
+prn_device(pcxmono_procs, "pcxmono",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI,
+ 0, 0, 0, 0, /* margins */
+ 1, pcxmono_print_page);
+
+/* Chunky 8-bit gray scale. */
+
+static dev_proc_print_page(pcx256_print_page);
+
+/* Since the print_page doesn't alter the device, this device can print in the background */
+static const gx_device_procs pcxgray_procs =
+prn_color_procs(gdev_prn_open, gdev_prn_bg_output_page, gdev_prn_close,
+ gx_default_gray_map_rgb_color, gx_default_gray_map_color_rgb);
+const gx_device_printer gs_pcxgray_device =
+{prn_device_body(gx_device_printer, pcxgray_procs, "pcxgray",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI,
+ 0, 0, 0, 0, /* margins */
+ 1, 8, 255, 255, 256, 256, pcx256_print_page)
+};
+
+/* 4-bit planar (EGA/VGA-style) color. */
+
+static dev_proc_print_page(pcx16_print_page);
+
+/* Since the print_page doesn't alter the device, this device can print in the background */
+static const gx_device_procs pcx16_procs =
+prn_color_procs(gdev_prn_open, gdev_prn_bg_output_page, gdev_prn_close,
+ pc_4bit_map_rgb_color, pc_4bit_map_color_rgb);
+const gx_device_printer gs_pcx16_device =
+{prn_device_body(gx_device_printer, pcx16_procs, "pcx16",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI,
+ 0, 0, 0, 0, /* margins */
+ 3, 4, 1, 1, 2, 2, pcx16_print_page)
+};
+
+/* Chunky 8-bit (SuperVGA-style) color. */
+/* (Uses a fixed palette of 3,3,2 bits.) */
+
+/* Since the print_page doesn't alter the device, this device can print in the background */
+static const gx_device_procs pcx256_procs =
+prn_color_procs(gdev_prn_open, gdev_prn_bg_output_page, gdev_prn_close,
+ pc_8bit_map_rgb_color, pc_8bit_map_color_rgb);
+const gx_device_printer gs_pcx256_device =
+{prn_device_body(gx_device_printer, pcx256_procs, "pcx256",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI,
+ 0, 0, 0, 0, /* margins */
+ 3, 8, 5, 5, 6, 6, pcx256_print_page)
+};
+
+/* 24-bit color, 3 8-bit planes. */
+
+static dev_proc_print_page(pcx24b_print_page);
+
+/* Since the print_page doesn't alter the device, this device can print in the background */
+static const gx_device_procs pcx24b_procs =
+prn_color_procs(gdev_prn_open, gdev_prn_bg_output_page, gdev_prn_close,
+ gx_default_rgb_map_rgb_color, gx_default_rgb_map_color_rgb);
+const gx_device_printer gs_pcx24b_device =
+prn_device(pcx24b_procs, "pcx24b",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI,
+ 0, 0, 0, 0, /* margins */
+ 24, pcx24b_print_page);
+
+/* 4-bit chunky CMYK color. */
+
+static dev_proc_print_page(pcxcmyk_print_page);
+
+static const gx_device_procs pcxcmyk_procs =
+{
+ gdev_prn_open,
+ NULL, /* get_initial_matrix */
+ NULL, /* sync_output */
+/* Since the print_page doesn't alter the device, this device can print in the background */
+ gdev_prn_bg_output_page,
+ gdev_prn_close,
+ NULL, /* map_rgb_color */
+ cmyk_1bit_map_color_rgb,
+ NULL, /* fill_rectangle */
+ NULL, /* tile_rectangle */
+ NULL, /* copy_mono */
+ NULL, /* copy_color */
+ NULL, /* draw_line */
+ NULL, /* get_bits */
+ gdev_prn_get_params,
+ gdev_prn_put_params,
+ cmyk_1bit_map_cmyk_color,
+ NULL, /* get_xfont_procs */
+ NULL, /* get_xfont_device */
+ NULL, /* map_rgb_alpha_color */
+ gx_page_device_get_page_device
+};
+const gx_device_printer gs_pcxcmyk_device =
+{prn_device_body(gx_device_printer, pcxcmyk_procs, "pcxcmyk",
+ DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI,
+ 0, 0, 0, 0, /* margins */
+ 4, 4, 1, 1, 2, 2, pcxcmyk_print_page)
+};
+
+/* ------ Private definitions ------ */
+
+/* All two-byte quantities are stored LSB-first! */
+#if arch_is_big_endian
+# define assign_ushort(a,v) a = ((v) >> 8) + ((v) << 8)
+#else
+# define assign_ushort(a,v) a = (v)
+#endif
+
+typedef struct pcx_header_s {
+ byte manuf; /* always 0x0a */
+ byte version;
+#define version_2_5 0
+#define version_2_8_with_palette 2
+#define version_2_8_without_palette 3
+#define version_3_0 /* with palette */ 5
+ byte encoding; /* 1=RLE */
+ byte bpp; /* bits per pixel per plane */
+ ushort x1; /* X of upper left corner */
+ ushort y1; /* Y of upper left corner */
+ ushort x2; /* x1 + width - 1 */
+ ushort y2; /* y1 + height - 1 */
+ ushort hres; /* horz. resolution (dots per inch) */
+ ushort vres; /* vert. resolution (dots per inch) */
+ byte palette[16 * 3]; /* color palette */
+ byte reserved;
+ byte nplanes; /* number of color planes */
+ ushort bpl; /* number of bytes per line (uncompressed) */
+ ushort palinfo;
+#define palinfo_color 1
+#define palinfo_gray 2
+ byte xtra[58]; /* fill out header to 128 bytes */
+} pcx_header;
+
+/* Define the prototype header. */
+static const pcx_header pcx_header_prototype =
+{
+ 10, /* manuf */
+ 0, /* version (variable) */
+ 1, /* encoding */
+ 0, /* bpp (variable) */
+ 00, 00, /* x1, y1 */
+ 00, 00, /* x2, y2 (variable) */
+ 00, 00, /* hres, vres (variable) */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* palette (variable) */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ 0, /* reserved */
+ 0, /* nplanes (variable) */
+ 00, /* bpl (variable) */
+ 00, /* palinfo (variable) */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* xtra */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
+};
+
+/*
+ * Define the DCX header. We don't actually use this yet.
+ * All quantities are stored little-endian!
+ bytes 0-3: ID = 987654321
+ bytes 4-7: file offset of page 1
+ [... up to 1023 entries ...]
+ bytes N-N+3: 0 to mark end of page list
+ * This is followed by the pages in order, each of which is a PCX file.
+ */
+#define dcx_magic 987654321
+#define dcx_max_pages 1023
+
+/* Forward declarations */
+static void pcx_write_rle(const byte *, const byte *, int, FILE *);
+static int pcx_write_page(gx_device_printer *, FILE *, pcx_header *, bool);
+
+/* Write a monochrome PCX page. */
+static int
+pcxmono_print_page(gx_device_printer * pdev, FILE * file)
+{
+ pcx_header header;
+
+ header = pcx_header_prototype;
+ header.version = version_2_8_with_palette;
+ header.bpp = 1;
+ header.nplanes = 1;
+ assign_ushort(header.palinfo, palinfo_gray);
+ /* Set the first two entries of the short palette. */
+ memcpy((byte *) header.palette, "\000\000\000\377\377\377", 6);
+ return pcx_write_page(pdev, file, &header, false);
+}
+
+/* Write an "old" PCX page. */
+static const byte pcx_ega_palette[16 * 3] =
+{
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0x00, 0xaa, 0xaa,
+ 0xaa, 0x00, 0x00, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa,
+ 0x55, 0x55, 0x55, 0x55, 0x55, 0xff, 0x55, 0xff, 0x55, 0x55, 0xff, 0xff,
+ 0xff, 0x55, 0x55, 0xff, 0x55, 0xff, 0xff, 0xff, 0x55, 0xff, 0xff, 0xff
+};
+static int
+pcx16_print_page(gx_device_printer * pdev, FILE * file)
+{
+ pcx_header header;
+
+ header = pcx_header_prototype;
+ header.version = version_2_8_with_palette;
+ header.bpp = 1;
+ header.nplanes = 4;
+ /* Fill the EGA palette appropriately. */
+ memcpy((byte *) header.palette, pcx_ega_palette,
+ sizeof(pcx_ega_palette));
+ return pcx_write_page(pdev, file, &header, true);
+}
+
+/* Write a "new" PCX page. */
+static int
+pcx256_print_page(gx_device_printer * pdev, FILE * file)
+{
+ pcx_header header;
+ int code;
+
+ header = pcx_header_prototype;
+ header.version = version_3_0;
+ header.bpp = 8;
+ header.nplanes = 1;
+ assign_ushort(header.palinfo,
+ (pdev->color_info.num_components > 1 ?
+ palinfo_color : palinfo_gray));
+ code = pcx_write_page(pdev, file, &header, false);
+ if (code >= 0) { /* Write out the palette. */
+ fputc(0x0c, file);
+ code = pc_write_palette((gx_device *) pdev, 256, file);
+ }
+ return code;
+}
+
+/* Write a 24-bit color PCX page. */
+static int
+pcx24b_print_page(gx_device_printer * pdev, FILE * file)
+{
+ pcx_header header;
+
+ header = pcx_header_prototype;
+ header.version = version_3_0;
+ header.bpp = 8;
+ header.nplanes = 3;
+ assign_ushort(header.palinfo, palinfo_color);
+ return pcx_write_page(pdev, file, &header, true);
+}
+
+/* Write a 4-bit chunky CMYK color PCX page. */
+static const byte pcx_cmyk_palette[16 * 3] =
+{
+ 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x0f, 0x0f, 0x00,
+ 0xff, 0x00, 0xff, 0x0f, 0x00, 0x0f, 0xff, 0x00, 0x00, 0x0f, 0x00, 0x00,
+ 0x00, 0xff, 0xff, 0x00, 0x0f, 0x0f, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x00,
+ 0x00, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x1f, 0x1f, 0x1f, 0x0f, 0x0f, 0x0f,
+};
+static int
+pcxcmyk_print_page(gx_device_printer * pdev, FILE * file)
+{
+ pcx_header header;
+
+ header = pcx_header_prototype;
+ header.version = 2;
+ header.bpp = 4;
+ header.nplanes = 1;
+ /* Fill the palette appropriately. */
+ memcpy((byte *) header.palette, pcx_cmyk_palette,
+ sizeof(pcx_cmyk_palette));
+ return pcx_write_page(pdev, file, &header, false);
+}
+
+/* Write out a page in PCX format. */
+/* This routine is used for all formats. */
+/* The caller has set header->bpp, nplanes, and palette. */
+static int
+pcx_write_page(gx_device_printer * pdev, FILE * file, pcx_header * phdr,
+ bool planar)
+{
+ int raster = gdev_prn_raster(pdev);
+ uint rsize = ROUND_UP((pdev->width * phdr->bpp + 7) >> 3, 2); /* PCX format requires even */
+ int height = pdev->height;
+ int depth = pdev->color_info.depth;
+ uint lsize = raster + rsize;
+ byte *line = gs_alloc_bytes(pdev->memory, lsize, "pcx file buffer");
+ byte *plane = line + raster;
+ int y;
+ int code = 0; /* return code */
+
+ if (line == 0) /* can't allocate line buffer */
+ return_error(gs_error_VMerror);
+
+ /* Fill in the other variable entries in the header struct. */
+
+ assign_ushort(phdr->x2, pdev->width - 1);
+ assign_ushort(phdr->y2, height - 1);
+ assign_ushort(phdr->hres, (int)pdev->x_pixels_per_inch);
+ assign_ushort(phdr->vres, (int)pdev->y_pixels_per_inch);
+ assign_ushort(phdr->bpl, (planar || depth == 1 ? rsize :
+ raster + (raster & 1)));
+
+ /* Write the header. */
+
+ if (fwrite((const char *)phdr, 1, 128, file) < 128) {
+ code = gs_error_ioerror;
+ goto pcx_done;
+ }
+ /* Write the contents of the image. */
+ for (y = 0; y < height; y++) {
+ byte *row;
+ byte *end;
+
+ code = gdev_prn_get_bits(pdev, y, line, &row);
+ if (code < 0)
+ break;
+ end = row + raster;
+ if (!planar) { /* Just write the bits. */
+ if (raster & 1) { /* Round to even, with predictable padding. */
+ *end = end[-1];
+ ++end;
+ }
+ pcx_write_rle(row, end, 1, file);
+ } else
+ switch (depth) {
+
+ case 4:
+ {
+ byte *pend = plane + rsize;
+ int shift;
+
+ for (shift = 0; shift < 4; shift++) {
+ register byte *from, *to;
+ register int bright = 1 << shift;
+ register int bleft = bright << 4;
+
+ for (from = row, to = plane;
+ from < end; from += 4
+ ) {
+ *to++ =
+ (from[0] & bleft ? 0x80 : 0) |
+ (from[0] & bright ? 0x40 : 0) |
+ (from[1] & bleft ? 0x20 : 0) |
+ (from[1] & bright ? 0x10 : 0) |
+ (from[2] & bleft ? 0x08 : 0) |
+ (from[2] & bright ? 0x04 : 0) |
+ (from[3] & bleft ? 0x02 : 0) |
+ (from[3] & bright ? 0x01 : 0);
+ }
+ /* We might be one byte short of rsize. */
+ if (to < pend)
+ *to = to[-1];
+ pcx_write_rle(plane, pend, 1, file);
+ }
+ }
+ break;
+
+ case 24:
+ {
+ int pnum;
+
+ for (pnum = 0; pnum < 3; ++pnum) {
+ pcx_write_rle(row + pnum, row + raster, 3, file);
+ if (pdev->width & 1)
+ fputc(0, file); /* pad to even */
+ }
+ }
+ break;
+
+ default:
+ code = gs_note_error(gs_error_rangecheck);
+ goto pcx_done;
+
+ }
+ }
+
+ pcx_done:
+ gs_free_object(pdev->memory, line, "pcx file buffer");
+
+ return code;
+}
+
+/* ------ Internal routines ------ */
+
+/* Write one line in PCX run-length-encoded format. */
+static void
+pcx_write_rle(const byte * from, const byte * end, int step, FILE * file)
+{ /*
+ * The PCX format theoretically allows encoding runs of 63
+ * identical bytes, but some readers can't handle repetition
+ * counts greater than 15.
+ */
+#define MAX_RUN_COUNT 15
+ int max_run = step * MAX_RUN_COUNT;
+
+ while (from < end) {
+ byte data = *from;
+
+ from += step;
+ if (data != *from || from == end) {
+ if (data >= 0xc0)
+ putc(0xc1, file);
+ } else {
+ const byte *start = from;
+
+ while ((from < end) && (*from == data))
+ from += step;
+ /* Now (from - start) / step + 1 is the run length. */
+ while (from - start >= max_run) {
+ putc(0xc0 + MAX_RUN_COUNT, file);
+ putc(data, file);
+ start += max_run;
+ }
+ if (from > start || data >= 0xc0)
+ putc((from - start) / step + 0xc1, file);
+ }
+ putc(data, file);
+ }
+#undef MAX_RUN_COUNT
+}