diff options
Diffstat (limited to 'gs/base/gdevcgm.c')
-rw-r--r-- | gs/base/gdevcgm.c | 495 |
1 files changed, 495 insertions, 0 deletions
diff --git a/gs/base/gdevcgm.c b/gs/base/gdevcgm.c new file mode 100644 index 000000000..eee9721e6 --- /dev/null +++ b/gs/base/gdevcgm.c @@ -0,0 +1,495 @@ +/* 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$ */ +/* CGM (Computer Graphics Metafile) driver */ +#include "memory_.h" +#include "gx.h" +#include "gserrors.h" +#include "gxdevice.h" +#include "gp.h" +#include "gsparam.h" +#include "gsutil.h" +#include "gdevcgml.h" +#include "gdevpccm.h" + +/**************** Future optimizations: + Do tile_rectangle with pattern + Keep track of painted area, + do masked copy_mono with cell array if possible + ****************/ + +typedef struct gx_device_cgm_s { + gx_device_common; + char fname[gp_file_name_sizeof]; + FILE *file; + cgm_state *st; + bool in_picture; +} gx_device_cgm; + +/* GC descriptor */ +gs_private_st_suffix_add1_final(st_device_cgm, gx_device_cgm, + "gx_device_cgm", device_cgm_enum_ptrs, device_cgm_reloc_ptrs, + gx_device_finalize, st_device, st); + +/* Device procedures */ +static dev_proc_open_device(cgm_open); +static dev_proc_output_page(cgm_output_page); +static dev_proc_close_device(cgm_close); +static dev_proc_fill_rectangle(cgm_fill_rectangle); + +#if 0 +static dev_proc_tile_rectangle(cgm_tile_rectangle); + +#else +#define cgm_tile_rectangle NULL +#endif +static dev_proc_copy_mono(cgm_copy_mono); +static dev_proc_copy_color(cgm_copy_color); +static dev_proc_get_params(cgm_get_params); +static dev_proc_put_params(cgm_put_params); + +/* In principle, all the drawing operations should be polymorphic, */ +/* but it's just as easy just to test the depth, since we're not */ +/* very concerned about performance. */ +#define cgm_device(dname, depth, max_value, dither, map_rgb_color, map_color_rgb)\ +{ std_device_color_stype_body(gx_device_cgm, 0, dname, &st_device_cgm,\ + 850, 1100, 100, 100, depth, max_value, dither),\ + { cgm_open,\ + NULL, /* get_initial_matrix */\ + NULL, /* sync_output */\ + cgm_output_page,\ + cgm_close,\ + map_rgb_color,\ + map_color_rgb,\ + cgm_fill_rectangle,\ + cgm_tile_rectangle,\ + cgm_copy_mono,\ + cgm_copy_color,\ + NULL, /* draw_line */\ + NULL, /* get_bits */\ + cgm_get_params,\ + cgm_put_params\ + },\ + { 0 }, /* fname */\ + 0, /* file */\ + 0, /* st */\ + 0 /*false*/ /* in_picture */\ +} + +gx_device_cgm gs_cgmmono_device = +cgm_device("cgmmono", 1, 1, 2, + gx_default_map_rgb_color, gx_default_w_b_map_color_rgb); + +gx_device_cgm gs_cgm8_device = +cgm_device("cgm8", 8, 5, 6, + pc_8bit_map_rgb_color, pc_8bit_map_color_rgb); + +gx_device_cgm gs_cgm24_device = +cgm_device("cgm24", 24, 255, 255, + gx_default_rgb_map_rgb_color, gx_default_rgb_map_color_rgb); + +/* Define allocator procedures for the CGM library. */ +static void * +cgm_gs_alloc(void *private_data, uint size) +{ + gx_device_cgm *cdev = private_data; + + return gs_alloc_bytes(cdev->memory, size, "cgm_gs_alloc"); +} +static void +cgm_gs_free(void *private_data, void *obj) +{ + gx_device_cgm *cdev = private_data; + + gs_free_object(cdev->memory, obj, "cgm_gs_free"); +} + +/* ---------------- Utilities ---------------- */ + +/* Convert a CGM result code to our error values. */ +static int +cgm_error_code(cgm_result result) +{ + switch (result) { + default: + case cgm_result_wrong_state: + return gs_error_unknownerror; + case cgm_result_out_of_range: + return gs_error_rangecheck; + case cgm_result_io_error: + return gs_error_ioerror; + } +} +#define check_result(result)\ + if ( result != cgm_result_ok ) return_error(cgm_error_code(result)) + +/* ---------------- Device control ---------------- */ + +/* Open the device */ +static int +cgm_open(gx_device * dev) +{ + gx_device_cgm *cdev = (gx_device_cgm *) dev; + cgm_allocator cal; + static const int elements[] = + {-1, 1}; + cgm_metafile_elements meta; + cgm_result result; + + cdev->file = fopen(cdev->fname, "wb"); + if (cdev->file == 0) + return_error(gs_error_ioerror); + cal.private_data = cdev; + cal.alloc = cgm_gs_alloc; + cal.free = cgm_gs_free; + cdev->st = cgm_initialize(cdev->file, &cal); + if (cdev->st == 0) + return_error(gs_error_VMerror); + result = cgm_BEGIN_METAFILE(cdev->st, "", 0); + check_result(result); + meta.metafile_version = 1; + meta.vdc_type = cgm_vdc_integer; + meta.integer_precision = sizeof(cgm_int) * 8; + meta.index_precision = sizeof(cgm_int) * 8; + meta.color_precision = 8; + /* If we use color indices at all, they are only 1 byte. */ + meta.color_index_precision = 8; + meta.maximum_color_index = (1L << cdev->color_info.depth) - 1; + meta.metafile_element_list = elements, + meta.metafile_element_list_count = countof(elements) / 2; + result = cgm_set_metafile_elements(cdev->st, &meta, + cgm_set_METAFILE_VERSION | + cgm_set_VDC_TYPE | + cgm_set_INTEGER_PRECISION | + cgm_set_INDEX_PRECISION | + cgm_set_COLOR_PRECISION | + cgm_set_COLOR_INDEX_PRECISION | + cgm_set_MAXIMUM_COLOR_INDEX | + cgm_set_METAFILE_ELEMENT_LIST); + check_result(result); + cdev->in_picture = false; + return 0; +} + +/* Output a page */ +static int +cgm_output_page(gx_device * dev, int num_copies, int flush) +{ + gx_device_cgm *cdev = (gx_device_cgm *) dev; + + if (cdev->in_picture) { + cgm_result result = cgm_END_PICTURE(cdev->st); + + check_result(result); + cdev->in_picture = false; + return gx_finish_output_page(dev, num_copies, flush); + } + return 0; +} + +/* Close the device */ +static int +cgm_close(gx_device * dev) +{ + gx_device_cgm *cdev = (gx_device_cgm *) dev; + int code = cgm_output_page(dev, 1, 0); + cgm_result result; + + if (code < 0) + return code; + result = cgm_END_METAFILE(cdev->st); + check_result(result); + result = cgm_terminate(cdev->st); + check_result(result); + cdev->st = 0; + fclose(cdev->file); + cdev->file = 0; + return 0; +} + +/* Get parameters. CGM devices add OutputFile to the default set. */ +static int +cgm_get_params(gx_device * dev, gs_param_list * plist) +{ + gx_device_cgm *cdev = (gx_device_cgm *) dev; + int code = gx_default_get_params(dev, plist); + gs_param_string ofns; + + if (code < 0) + return code; + ofns.data = (const byte *)cdev->fname, + ofns.size = strlen(cdev->fname), + ofns.persistent = false; + return param_write_string(plist, "OutputFile", &ofns); +} + +/* Put parameters. */ +static int +cgm_put_params(gx_device * dev, gs_param_list * plist) +{ + gx_device_cgm *cdev = (gx_device_cgm *) dev; + int ecode = 0; + int code; + const char *param_name; + gs_param_string ofs; + + switch (code = param_read_string(plist, (param_name = "OutputFile"), &ofs)) { + case 0: + if (dev->LockSafetyParams && + bytes_compare(ofs.data, ofs.size, + (const byte *)cdev->fname, strlen(cdev->fname))) { + ecode = gs_note_error(gs_error_invalidaccess); + goto ofe; + } + if (ofs.size >= gp_file_name_sizeof) + ecode = gs_error_limitcheck; + else + break; + goto ofe; + default: + ecode = code; + ofe:param_signal_error(plist, param_name, ecode); + case 1: + ofs.data = 0; + break; + } + + if (ecode < 0) + return ecode; + code = gx_default_put_params(dev, plist); + if (code < 0) + return code; + + if (ofs.data != 0) { /* Close the file if it's open. */ + if (cdev->file != 0) { + fclose(cdev->file); + cdev->file = 0; + } + memcpy(cdev->fname, ofs.data, ofs.size); + cdev->fname[ofs.size] = 0; + cdev->file = fopen(cdev->fname, "wb"); + if (cdev->file == 0) + return_error(gs_error_ioerror); + } + return 0; +} + +/* ---------------- Drawing ---------------- */ + +/* Set the corner points for a rectangle. It appears (although */ +/* this is not obvious from the CGM specification) that rectangles */ +/* are specified with closed, rather than half-open, intervals. */ +#define cgm_set_rect(points, xo, yo, w, h)\ + points[1].integer.x = (points[0].integer.x = xo) + (w) - 1,\ + points[1].integer.y = (points[0].integer.y = yo) + (h) - 1 + +/* Set the points for a cell array. */ +#define cgm_set_cell_points(pqr, xo, yo, w, h)\ + pqr[0].integer.x = (xo),\ + pqr[0].integer.y = (yo),\ + pqr[1].integer.x = (xo) + (w),\ + pqr[1].integer.y = (yo) + (h),\ + pqr[2].integer.x = (xo) + (w),\ + pqr[2].integer.y = (yo) + +/* Begin a picture if necessary. */ +#define begin_picture(cdev)\ + if ( !cdev->in_picture ) cgm_begin_picture(cdev) +static int +cgm_begin_picture(gx_device_cgm * cdev) +{ + cgm_picture_elements pic; + cgm_result result; + cgm_edge_width edge; + + result = cgm_BEGIN_PICTURE(cdev->st, "", 0); + check_result(result); + pic.scaling_mode = cgm_scaling_abstract; + pic.color_selection_mode = + (cdev->color_info.depth <= 8 ? + cgm_color_selection_indexed : + cgm_color_selection_direct); + pic.line_width_specification_mode = cgm_line_marker_absolute; + pic.edge_width_specification_mode = cgm_line_marker_absolute; + cgm_set_rect(pic.vdc_extent, 0, 0, cdev->width, cdev->height); + result = cgm_set_picture_elements(cdev->st, &pic, + cgm_set_SCALING_MODE | + cgm_set_COLOR_SELECTION_MODE | + cgm_set_LINE_WIDTH_SPECIFICATION_MODE | + cgm_set_EDGE_WIDTH_SPECIFICATION_MODE | + cgm_set_VDC_EXTENT); + check_result(result); + result = cgm_BEGIN_PICTURE_BODY(cdev->st); + check_result(result); + result = cgm_VDC_INTEGER_PRECISION(cdev->st, + (cdev->width <= 0x7fff && + cdev->height <= 0x7fff ? + 16 : sizeof(cdev->width) * 8)); + check_result(result); + edge.absolute.integer = 0; + result = cgm_EDGE_WIDTH(cdev->st, &edge); + check_result(result); + if (cdev->color_info.depth <= 8) { + cgm_color colors[256]; + int i; + + for (i = 0; i < (1 << cdev->color_info.depth); i++) { + gx_color_value rgb[3]; + + (*dev_proc(cdev, map_color_rgb)) ((gx_device *) cdev, + (gx_color_index) i, rgb); + colors[i].rgb.r = + rgb[0] >> (gx_color_value_bits - 8); + colors[i].rgb.g = + rgb[1] >> (gx_color_value_bits - 8); + colors[i].rgb.b = + rgb[2] >> (gx_color_value_bits - 8); + } + result = cgm_COLOR_TABLE(cdev->st, 0, colors, + 1 << cdev->color_info.depth); + check_result(result); + } + cdev->in_picture = true; + return 0; +} + +/* Convert a gx_color_index to a CGM color. */ +static void +cgm_color_from_color_index(cgm_color * pcc, const gx_device_cgm * cdev, + gx_color_index color) +{ + if (cdev->color_info.depth <= 8) + pcc->index = color; + else { + pcc->rgb.r = color >> 16; + pcc->rgb.g = (color >> 8) & 255; + pcc->rgb.b = color & 255; + } +} + +/* Fill a rectangle. */ +static int +cgm_fill_rectangle(gx_device * dev, int x, int y, int w, int h, + gx_color_index color) +{ + gx_device_cgm *cdev = (gx_device_cgm *) dev; + cgm_color fill_color; + cgm_point points[2]; + cgm_result result; + + fit_fill(dev, x, y, w, h); + if (!cdev->in_picture) { /* Check for erasepage. */ + gx_color_value blank[3] = {gx_max_color_value, gx_max_color_value, + gx_max_color_value}; + if (color == (*dev_proc(dev, encode_color)) (dev, blank)) + return 0; + cgm_begin_picture(cdev); + } + cgm_color_from_color_index(&fill_color, cdev, color); + result = cgm_FILL_COLOR(cdev->st, &fill_color); + check_result(result); + result = cgm_INTERIOR_STYLE(cdev->st, cgm_interior_style_solid); + check_result(result); + cgm_set_rect(points, x, y, w, h); + result = cgm_RECTANGLE(cdev->st, &points[0], &points[1]); + check_result(result); + return 0; +} + +#if 0 +/* Tile a rectangle. We should do this with a pattern if possible. */ +static int +cgm_tile_rectangle(gx_device * dev, const gx_tile_bitmap * tile, + int x, int y, int w, int h, gx_color_index zero, gx_color_index one, + int px, int py) +{ +} +#endif + +/* Copy a monochrome bitmap. Unfortunately, CGM doesn't provide a */ +/* masked fill operation; if one of the colors is transparent, */ +/* we have to do the copy by filling lots of tiny little rectangles. */ +/* A much better way to implement this would be to remember whether */ +/* the destination region is still white; if so, we can use a cell array */ +/* (or, even better, a pattern). However, we still need the slow method */ +/* for the case where we don't know the background color or it isn't white. */ +static int +cgm_copy_mono(gx_device * dev, + const byte * base, int sourcex, int raster, gx_bitmap_id id, + int x, int y, int w, int h, gx_color_index zero, gx_color_index one) +{ + gx_device_cgm *cdev = (gx_device_cgm *) dev; + + /* The current implementation is about as inefficient as */ + /* one could possibly imagine! */ + int ix, iy; + cgm_result result; + + fit_copy(dev, base, sourcex, raster, id, x, y, w, h); + begin_picture(cdev); + if (zero == 0 && one == 1 && cdev->color_info.depth == 1) { + cgm_point pqr[3]; + + cgm_set_cell_points(pqr, x, y, w, h); + result = cgm_CELL_ARRAY(cdev->st, pqr, w, h, 1, + cgm_cell_mode_packed, + base, sourcex, raster); + check_result(result); + } else { + result = cgm_INTERIOR_STYLE(cdev->st, cgm_interior_style_solid); + check_result(result); + for (iy = 0; iy < h; iy++) + for (ix = 0; ix < w; ix++) { + int px = ix + sourcex; + const byte *pixel = &base[iy * raster + (px >> 3)]; + byte mask = 0x80 >> (px & 7); + gx_color_index color = (*pixel & mask ? one : zero); + + if (color != gx_no_color_index) { + cgm_color fill_color; + cgm_point points[2]; + + cgm_color_from_color_index(&fill_color, cdev, color); + cgm_set_rect(points, x, y, 1, 1); + result = cgm_RECTANGLE(cdev->st, &points[0], &points[1]); + check_result(result); + } + } + } + return 0; +} + +/* Copy a color bitmap. */ +static int +cgm_copy_color(gx_device * dev, + const byte * base, int sourcex, int raster, gx_bitmap_id id, + int x, int y, int w, int h) +{ + gx_device_cgm *cdev = (gx_device_cgm *) dev; + int depth = cdev->color_info.depth; + uint source_bit = sourcex * depth; + cgm_point pqr[3]; + cgm_result result; + + if (depth == 1) + return cgm_copy_mono(dev, base, sourcex, raster, id, + x, y, w, h, + (gx_color_index) 0, (gx_color_index) 1); + fit_copy(dev, base, sourcex, raster, id, x, y, w, h); + begin_picture(cdev); + cgm_set_cell_points(pqr, x, y, w, h); + result = cgm_CELL_ARRAY(cdev->st, pqr, w, h, 0, cgm_cell_mode_packed, + base, source_bit, raster); + check_result(result); + return 0; +} |