diff options
Diffstat (limited to 'devices/gdevxcf.c')
-rw-r--r-- | devices/gdevxcf.c | 1406 |
1 files changed, 1406 insertions, 0 deletions
diff --git a/devices/gdevxcf.c b/devices/gdevxcf.c new file mode 100644 index 00000000..39c72831 --- /dev/null +++ b/devices/gdevxcf.c @@ -0,0 +1,1406 @@ +/* Copyright (C) 2001-2019 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., 1305 Grant Avenue - Suite 200, Novato, + CA 94945, U.S.A., +1(415)492-9861, for further information. +*/ + + +/* Gimp (XCF) export device, supporting DeviceN color models. */ + +#include "math_.h" +#include "gdevprn.h" +#include "gsparam.h" +#include "gscrd.h" +#include "gscrdp.h" +#include "gxlum.h" +#include "gdevdcrd.h" +#include "gstypes.h" +#include "gxdcconv.h" +#include "gsicc_cache.h" +#include "gsicc_manage.h" +#include "gsicc_cms.h" +#include "gdevdevn.h" + +#ifndef MAX_CHAN +# define MAX_CHAN 8 +#endif + +/* Define the device parameters. */ +#ifndef X_DPI +# define X_DPI 72 +#endif +#ifndef Y_DPI +# define Y_DPI 72 +#endif + +/* The device descriptor */ +static dev_proc_get_params(xcf_get_params); +static dev_proc_close_device(xcf_prn_close); +static dev_proc_put_params(xcf_put_params); +static dev_proc_print_page(xcf_print_page); +static dev_proc_map_color_rgb(xcf_map_color_rgb); +static dev_proc_get_color_mapping_procs(get_spotrgb_color_mapping_procs); +#if 0 +static dev_proc_get_color_mapping_procs(get_spotcmyk_color_mapping_procs); +#endif +static dev_proc_get_color_mapping_procs(get_xcf_color_mapping_procs); +static dev_proc_get_color_comp_index(xcf_get_color_comp_index); +static dev_proc_encode_color(xcf_encode_color); +static dev_proc_decode_color(xcf_decode_color); + +/* + * Structure for holding SeparationNames and SeparationOrder elements. + */ +typedef struct gs_separation_names_s { + int num_names; + const gs_param_string * names[GX_DEVICE_COLOR_MAX_COMPONENTS]; +} gs_separation_names; + +/* This is redundant with color_info.cm_name. We may eliminate this + typedef and use the latter string for everything. */ +typedef enum { + XCF_DEVICE_GRAY, + XCF_DEVICE_RGB, + XCF_DEVICE_CMYK, + XCF_DEVICE_N +} xcf_color_model; + +/* + * A structure definition for a DeviceN type device + */ +typedef struct xcf_device_s { + gx_device_common; + gx_prn_device_common; + + /* ... device-specific parameters ... */ + + xcf_color_model color_model; + + /* + * Bits per component (device colorant). Currently only 1 and 8 are + * supported. + */ + int bitspercomponent; + + /* + * Pointer to the colorant names for the color model. This will be + * null if we have DeviceN type device. The actual possible colorant + * names are those in this list plus those in the separation_names + * list (below). + */ + fixed_colorant_names_list std_colorant_names; + int num_std_colorant_names; /* Number of names in list */ + + /* + * Separation names (if any). + */ + gs_separation_names separation_names; + + /* + * Separation Order (if specified). + */ + gs_separation_names separation_order; + + /* ICC color profile objects, for color conversion. + These are all device link profiles. At least that + is how it appears looking at how this code + was written to work with the old icclib. Just + doing minimal updates here so that it works + with the new CMM API. I would be interested + to hear how people are using this. */ + + char profile_rgb_fn[256]; + cmm_profile_t *rgb_profile; + gcmmhlink_t rgb_icc_link; + + char profile_cmyk_fn[256]; + cmm_profile_t *cmyk_profile; + gcmmhlink_t cmyk_icc_link; + + char profile_out_fn[256]; + cmm_profile_t *output_profile; + gcmmhlink_t output_icc_link; + +} xcf_device; + +/* + * Macro definition for DeviceN procedures + */ +#define device_procs(get_color_mapping_procs)\ +{ gdev_prn_open,\ + gx_default_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, /* output_page */\ + xcf_prn_close, /* close */\ + NULL, /* map_rgb_color - not used */\ + xcf_map_color_rgb, /* map_color_rgb */\ + NULL, /* fill_rectangle */\ + NULL, /* tile_rectangle */\ + NULL, /* copy_mono */\ + NULL, /* copy_color */\ + NULL, /* draw_line */\ + NULL, /* get_bits */\ + xcf_get_params, /* get_params */\ + xcf_put_params, /* put_params */\ + NULL, /* map_cmyk_color - not used */\ + NULL, /* get_xfont_procs */\ + NULL, /* get_xfont_device */\ + NULL, /* map_rgb_alpha_color */\ + gx_page_device_get_page_device, /* get_page_device */\ + NULL, /* get_alpha_bits */\ + NULL, /* copy_alpha */\ + NULL, /* get_band */\ + NULL, /* copy_rop */\ + NULL, /* fill_path */\ + NULL, /* stroke_path */\ + NULL, /* fill_mask */\ + NULL, /* fill_trapezoid */\ + NULL, /* fill_parallelogram */\ + NULL, /* fill_triangle */\ + NULL, /* draw_thin_line */\ + NULL, /* begin_image */\ + NULL, /* image_data */\ + NULL, /* end_image */\ + NULL, /* strip_tile_rectangle */\ + NULL, /* strip_copy_rop */\ + NULL, /* get_clipping_box */\ + NULL, /* begin_typed_image */\ + NULL, /* get_bits_rectangle */\ + NULL, /* map_color_rgb_alpha */\ + NULL, /* create_compositor */\ + NULL, /* get_hardware_params */\ + NULL, /* text_begin */\ + NULL, /* finish_copydevice */\ + NULL, /* begin_transparency_group */\ + NULL, /* end_transparency_group */\ + NULL, /* begin_transparency_mask */\ + NULL, /* end_transparency_mask */\ + NULL, /* discard_transparency_layer */\ + get_color_mapping_procs, /* get_color_mapping_procs */\ + xcf_get_color_comp_index, /* get_color_comp_index */\ + xcf_encode_color, /* encode_color */\ + xcf_decode_color /* decode_color */\ +} + +/* + * Example device with RGB and spot color support + */ +static const gx_device_procs spot_rgb_procs = device_procs(get_spotrgb_color_mapping_procs); + +const xcf_device gs_xcf_device = +{ + prn_device_body_extended(xcf_device, spot_rgb_procs, "xcf", + DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS, + X_DPI, Y_DPI, /* X and Y hardware resolution */ + 0, 0, 0, 0, /* margins */ + GX_DEVICE_COLOR_MAX_COMPONENTS, 3, /* MaxComponents, NumComp */ + GX_CINFO_POLARITY_ADDITIVE, /* Polarity */ + 24, 0, /* Depth, Gray_index, */ + 255, 255, 256, 256, /* MaxGray, MaxColor, DitherGray, DitherColor */ + GX_CINFO_UNKNOWN_SEP_LIN, /* Let check_device_separable set up values */ + "DeviceN", /* Process color model name */ + xcf_print_page), /* Printer page print routine */ + /* DeviceN device specific parameters */ + XCF_DEVICE_RGB, /* Color model */ + 8, /* Bits per color - must match ncomp, depth, etc. above */ + DeviceRGBComponents, /* Names of color model colorants */ + 3, /* Number colorants for RGB */ + {0}, /* SeparationNames */ + {0} /* SeparationOrder names */ +}; + +static const gx_device_procs spot_cmyk_procs = device_procs(get_xcf_color_mapping_procs); + +const xcf_device gs_xcfcmyk_device = +{ + prn_device_body_extended(xcf_device, spot_cmyk_procs, "xcfcmyk", + DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS, + X_DPI, Y_DPI, /* X and Y hardware resolution */ + 0, 0, 0, 0, /* margins */ + GX_DEVICE_COLOR_MAX_COMPONENTS, 4, /* MaxComponents, NumComp */ + GX_CINFO_POLARITY_SUBTRACTIVE, /* Polarity */ + 32, 0, /* Depth, Gray_index, */ + 255, 255, 256, 256, /* MaxGray, MaxColor, DitherGray, DitherColor */ + GX_CINFO_UNKNOWN_SEP_LIN, /* Let check_device_separable set up values */ + "DeviceN", /* Process color model name */ + xcf_print_page), /* Printer page print routine */ + /* DeviceN device specific parameters */ + XCF_DEVICE_CMYK, /* Color model */ + 8, /* Bits per color - must match ncomp, depth, etc. above */ + DeviceCMYKComponents, /* Names of color model colorants */ + 4, /* Number colorants for RGB */ + {0}, /* SeparationNames */ + {0} /* SeparationOrder names */ +}; + +/* + * The following procedures are used to map the standard color spaces into + * the color components for the spotrgb device. + */ +static void +gray_cs_to_spotrgb_cm(gx_device * dev, frac gray, frac out[]) +{ +/* TO_DO_DEVICEN This routine needs to include the effects of the SeparationOrder array */ + int i = ((xcf_device *)dev)->separation_names.num_names; + + out[0] = out[1] = out[2] = gray; + for(; i>0; i--) /* Clear spot colors */ + out[2 + i] = 0; +} + +static void +rgb_cs_to_spotrgb_cm(gx_device * dev, const gs_gstate *pgs, + frac r, frac g, frac b, frac out[]) +{ +/* TO_DO_DEVICEN This routine needs to include the effects of the SeparationOrder array */ + int i = ((xcf_device *)dev)->separation_names.num_names; + + out[0] = r; + out[1] = g; + out[2] = b; + for(; i>0; i--) /* Clear spot colors */ + out[2 + i] = 0; +} + +static void +cmyk_cs_to_spotrgb_cm(gx_device * dev, frac c, frac m, frac y, frac k, frac out[]) +{ +/* TO_DO_DEVICEN This routine needs to include the effects of the SeparationOrder array */ + int i = ((xcf_device *)dev)->separation_names.num_names; + + color_cmyk_to_rgb(c, m, y, k, NULL, out, dev->memory); + for(; i>0; i--) /* Clear spot colors */ + out[2 + i] = 0; +} + +static void +gray_cs_to_spotcmyk_cm(gx_device * dev, frac gray, frac out[]) +{ +/* TO_DO_DEVICEN This routine needs to include the effects of the SeparationOrder array */ + int i = ((xcf_device *)dev)->separation_names.num_names; + + out[0] = out[1] = out[2] = 0; + out[3] = frac_1 - gray; + for(; i>0; i--) /* Clear spot colors */ + out[3 + i] = 0; +} + +static void +rgb_cs_to_spotcmyk_cm(gx_device * dev, const gs_gstate *pgs, + frac r, frac g, frac b, frac out[]) +{ +/* TO_DO_DEVICEN This routine needs to include the effects of the SeparationOrder array */ + xcf_device *xdev = (xcf_device *)dev; + int n = xdev->separation_names.num_names; + int i; + + color_rgb_to_cmyk(r, g, b, pgs, out, dev->memory); + for(i = 0; i < n; i++) /* Clear spot colors */ + out[4 + i] = 0; +} + +static void +cmyk_cs_to_spotcmyk_cm(gx_device * dev, frac c, frac m, frac y, frac k, frac out[]) +{ +/* TO_DO_DEVICEN This routine needs to include the effects of the SeparationOrder array */ + xcf_device *xdev = (xcf_device *)dev; + int n = xdev->separation_names.num_names; + int i; + + out[0] = c; + out[1] = m; + out[2] = y; + out[3] = k; + for(i = 0; i < n; i++) /* Clear spot colors */ + out[4 + i] = 0; +} + +static void +cmyk_cs_to_spotn_cm(gx_device * dev, frac c, frac m, frac y, frac k, frac out[]) +{ +/* TO_DO_DEVICEN This routine needs to include the effects of the SeparationOrder array */ + xcf_device *xdev = (xcf_device *)dev; + int n = xdev->separation_names.num_names; + + gcmmhlink_t link = xdev->cmyk_icc_link; + int i; + + if (link != NULL) { + unsigned short in[4]; + unsigned short tmp[MAX_CHAN]; + int outn = xdev->cmyk_profile->num_comps_out; + + in[0] = frac2ushort(c); + in[1] = frac2ushort(m); + in[2] = frac2ushort(y); + in[3] = frac2ushort(k); + + gscms_transform_color(dev, link, &(in[0]), &(tmp[0]), 2); + for (i = 0; i < outn; i++) + out[i] = ushort2frac(tmp[i]); + for (; i < n + 4; i++) + out[i] = 0; + + } else { + /* If no profile given, assume CMYK */ + out[0] = c; + out[1] = m; + out[2] = y; + out[3] = k; + for(i = 0; i < n; i++) /* Clear spot colors */ + out[4 + i] = 0; + } +} + +static void +gray_cs_to_spotn_cm(gx_device * dev, frac gray, frac out[]) +{ +/* TO_DO_DEVICEN This routine needs to include the effects of the SeparationOrder array */ + + cmyk_cs_to_spotn_cm(dev, 0, 0, 0, frac_1 - gray, out); +} + +static void +rgb_cs_to_spotn_cm(gx_device * dev, const gs_gstate *pgs, + frac r, frac g, frac b, frac out[]) +{ +/* TO_DO_DEVICEN This routine needs to include the effects of the SeparationOrder array */ + xcf_device *xdev = (xcf_device *)dev; + int n = xdev->separation_names.num_names; + gcmmhlink_t link = xdev->rgb_icc_link; + int i; + + if (link != NULL) { + unsigned short in[3]; + unsigned short tmp[MAX_CHAN]; + int outn = xdev->rgb_profile->num_comps_out; + + in[0] = frac2ushort(r); + in[1] = frac2ushort(g); + in[2] = frac2ushort(b); + + gscms_transform_color(dev, link, &(in[0]), &(tmp[0]), 2); + + for (i = 0; i < outn; i++) + out[i] = ushort2frac(tmp[i]); + for (; i < n + 4; i++) + out[i] = 0; + } else { + frac cmyk[4]; + + color_rgb_to_cmyk(r, g, b, pgs, cmyk, dev->memory); + cmyk_cs_to_spotn_cm(dev, cmyk[0], cmyk[1], cmyk[2], cmyk[3], + out); + } +} + +static const gx_cm_color_map_procs spotRGB_procs = { + gray_cs_to_spotrgb_cm, rgb_cs_to_spotrgb_cm, cmyk_cs_to_spotrgb_cm +}; + +static const gx_cm_color_map_procs spotCMYK_procs = { + gray_cs_to_spotcmyk_cm, rgb_cs_to_spotcmyk_cm, cmyk_cs_to_spotcmyk_cm +}; + +static const gx_cm_color_map_procs spotN_procs = { + gray_cs_to_spotn_cm, rgb_cs_to_spotn_cm, cmyk_cs_to_spotn_cm +}; + +/* + * These are the handlers for returning the list of color space + * to color model conversion routines. + */ +static const gx_cm_color_map_procs * +get_spotrgb_color_mapping_procs(const gx_device * dev) +{ + return &spotRGB_procs; +} + +#if 0 +static const gx_cm_color_map_procs * +get_spotcmyk_color_mapping_procs(const gx_device * dev) +{ + return &spotCMYK_procs; +} +#endif + +static const gx_cm_color_map_procs * +get_xcf_color_mapping_procs(const gx_device * dev) +{ + const xcf_device *xdev = (const xcf_device *)dev; + + if (xdev->color_model == XCF_DEVICE_RGB) + return &spotRGB_procs; + else if (xdev->color_model == XCF_DEVICE_CMYK) + return &spotCMYK_procs; + else if (xdev->color_model == XCF_DEVICE_N) + return &spotN_procs; + else + return NULL; +} + +/* + * Encode a list of colorant values into a gx_color_index_value. + */ +static gx_color_index +xcf_encode_color(gx_device *dev, const gx_color_value colors[]) +{ + int bpc = ((xcf_device *)dev)->bitspercomponent; + gx_color_index color = 0; + int i = 0; + int ncomp = dev->color_info.num_components; + COLROUND_VARS; + + COLROUND_SETUP(bpc); + for (; i<ncomp; i++) { + color <<= bpc; + color |= COLROUND_ROUND(colors[i]); + } + return (color == gx_no_color_index ? color ^ 1 : color); +} + +/* + * Decode a gx_color_index value back to a list of colorant values. + */ +static int +xcf_decode_color(gx_device * dev, gx_color_index color, gx_color_value * out) +{ + int bpc = ((xcf_device *)dev)->bitspercomponent; + int mask = (1 << bpc) - 1; + int i = 0; + int ncomp = dev->color_info.num_components; + COLDUP_VARS; + + COLDUP_SETUP(bpc); + for (; i<ncomp; i++) { + out[ncomp - i - 1] = COLDUP_DUP(color & mask); + color >>= bpc; + } + return 0; +} + +/* + * Convert a gx_color_index to RGB. + */ +static int +xcf_map_color_rgb(gx_device *dev, gx_color_index color, gx_color_value rgb[3]) +{ + xcf_device *xdev = (xcf_device *)dev; + + if (xdev->color_model == XCF_DEVICE_RGB) + return xcf_decode_color(dev, color, rgb); + /* TODO: return reasonable values. */ + rgb[0] = 0; + rgb[1] = 0; + rgb[2] = 0; + return 0; +} + +/* + * This routine will extract a specified set of bits from a buffer and pack + * them into a given buffer. + * + * Parameters: + * source - The source of the data + * dest - The destination for the data + * depth - The size of the bits per pixel - must be a multiple of 8 + * first_bit - The location of the first data bit (LSB). + * bit_width - The number of bits to be extracted. + * npixel - The number of pixels. + * + * Returns: + * Length of the output line (in bytes) + * Data in dest. + */ +#if 0 +static int +repack_data(byte * source, byte * dest, int depth, int first_bit, + int bit_width, int npixel) +{ + int in_nbyte = depth >> 3; /* Number of bytes per input pixel */ + int out_nbyte = bit_width >> 3; /* Number of bytes per output pixel */ + gx_color_index mask = 1; + gx_color_index data; + int i, j, length = 0; + int in_byte_loc = 0, out_byte_loc = 0; + byte temp; + byte * out = dest; + int max_bit_byte = 8 - bit_width; + + mask = (mask << bit_width) - 1; + for (i=0; i<npixel; i++) { + /* Get the pixel data */ + if (!in_nbyte) { /* Multiple pixels per byte */ + data = *source; + data >>= in_byte_loc; + in_byte_loc += depth; + if (in_byte_loc >= 8) { /* If finished with byte */ + in_byte_loc = 0; + source++; + } + } + else { /* One or more bytes per pixel */ + data = *source++; + for (j=1; j<in_nbyte; j++) + data = (data << 8) + *source++; + } + data >>= first_bit; + data &= mask; + + /* Put the output data */ + if (!out_nbyte) { /* Multiple pixels per byte */ + temp = *out & ~(mask << out_byte_loc); + *out = temp | (data << out_byte_loc); + out_byte_loc += bit_width; + if (out_byte_loc > max_bit_byte) { /* If finished with byte */ + out_byte_loc = 0; + out++; + } + } + else { /* One or more bytes per pixel */ + *out++ = data >> ((out_nbyte - 1) * 8); + for (j=1; j<out_nbyte; j++) { + *out++ = data >> ((out_nbyte - 1 - j) * 8); + } + } + } + /* Return the number of bytes in the destination buffer. */ + length = out - dest; + if (out_byte_loc) /* If partially filled last byte */ + length++; + return length; +} +#endif /* 0 */ + +static int +xcf_open_profile(const char *profile_out_fn, cmm_profile_t *icc_profile, gcmmhlink_t icc_link, gs_memory_t *memory) +{ + + gsicc_rendering_param_t rendering_params; + + icc_profile = gsicc_get_profile_handle_file(profile_out_fn, + strlen(profile_out_fn), memory); + + if (icc_profile == NULL) + return gs_throw(-1, "Could not create profile for xcf device"); + + /* Set up the rendering parameters */ + + rendering_params.black_point_comp = gsBPNOTSPECIFIED; + rendering_params.graphics_type_tag = GS_UNKNOWN_TAG; /* Already rendered */ + rendering_params.rendering_intent = gsPERCEPTUAL; + + /* Call with a NULL destination profile since we are using a device link profile here */ + icc_link = gscms_get_link(icc_profile, + NULL, &rendering_params, 0, memory); + + if (icc_link == NULL) + return gs_throw(-1, "Could not create link handle for xdev device"); + + return(0); + +} + +static int +xcf_open_profiles(xcf_device *xdev) +{ + int code = 0; + + if (xdev->output_icc_link == NULL && xdev->profile_out_fn[0]) { + + code = xcf_open_profile(xdev->profile_out_fn, xdev->output_profile, + xdev->output_icc_link, xdev->memory); + + } + + if (code >= 0 && xdev->rgb_icc_link == NULL && xdev->profile_rgb_fn[0]) { + + code = xcf_open_profile(xdev->profile_rgb_fn, xdev->rgb_profile, + xdev->rgb_icc_link, xdev->memory); + + } + + if (code >= 0 && xdev->cmyk_icc_link == NULL && xdev->profile_cmyk_fn[0]) { + + code = xcf_open_profile(xdev->profile_cmyk_fn, xdev->cmyk_profile, + xdev->cmyk_icc_link, xdev->memory); + + } + + return code; +} + +#define set_param_array(a, d, s)\ + (a.data = d, a.size = s, a.persistent = false); + +/* Get parameters. We provide a default CRD. */ +static int +xcf_get_params(gx_device * pdev, gs_param_list * plist) +{ + xcf_device *xdev = (xcf_device *)pdev; + int code; + bool seprs = false; + gs_param_string_array scna; + gs_param_string pos; + gs_param_string prgbs; + gs_param_string pcmyks; + + set_param_array(scna, NULL, 0); + + if ( (code = gdev_prn_get_params(pdev, plist)) < 0 || + (code = sample_device_crd_get_params(pdev, plist, "CRDDefault")) < 0 || + (code = param_write_name_array(plist, "SeparationColorNames", &scna)) < 0 || + (code = param_write_bool(plist, "Separations", &seprs)) < 0) + return code; + + pos.data = (const byte *)xdev->profile_out_fn, + pos.size = strlen(xdev->profile_out_fn), + pos.persistent = false; + code = param_write_string(plist, "ProfileOut", &pos); + if (code < 0) + return code; + + prgbs.data = (const byte *)xdev->profile_rgb_fn, + prgbs.size = strlen(xdev->profile_rgb_fn), + prgbs.persistent = false; + code = param_write_string(plist, "ProfileRgb", &prgbs); + if (code < 0) + return code; + + pcmyks.data = (const byte *)xdev->profile_cmyk_fn, + pcmyks.size = strlen(xdev->profile_cmyk_fn), + pcmyks.persistent = false; + code = param_write_string(plist, "ProfileCmyk", &pcmyks); + + return code; +} +#undef set_param_array + +#define compare_color_names(name, name_size, str, str_size) \ + (name_size == str_size && \ + (strncmp((const char *)name, (const char *)str, name_size) == 0)) + +/* + * This routine will check if a name matches any item in a list of process model + * color component names. + */ +static bool +check_process_color_names(fixed_colorant_names_list plist, + const gs_param_string * pstring) +{ + if (plist) { + uint size = pstring->size; + + while( *plist) { + if (compare_color_names(*plist, strlen(*plist), pstring->data, size)) { + return true; + } + plist++; + } + } + return false; +} + +#define BEGIN_ARRAY_PARAM(pread, pname, pa, psize, e)\ + BEGIN\ + switch (code = pread(plist, (param_name = pname), &(pa))) {\ + case 0:\ + if ((pa).size != psize) {\ + ecode = gs_note_error(gs_error_rangecheck);\ + (pa).data = 0; /* mark as not filled */\ + } else +#define END_ARRAY_PARAM(pa, e)\ + goto e;\ + default:\ + ecode = code;\ +e: param_signal_error(plist, param_name, ecode);\ + case 1:\ + (pa).data = 0; /* mark as not filled */\ + }\ + END + +static int +xcf_param_read_fn(gs_param_list *plist, const char *name, + gs_param_string *pstr, int max_len) +{ + int code = param_read_string(plist, name, pstr); + + if (code == 0) { + if (pstr->size >= max_len) + param_signal_error(plist, name, code = gs_error_rangecheck); + } else { + pstr->data = 0; + } + return code; +} + +/* Compare a C string and a gs_param_string. */ +static bool +param_string_eq(const gs_param_string *pcs, const char *str) +{ + return (strlen(str) == pcs->size && + !strncmp(str, (const char *)pcs->data, pcs->size)); +} + +static int +xcf_set_color_model(xcf_device *xdev, xcf_color_model color_model) +{ + xdev->color_model = color_model; + if (color_model == XCF_DEVICE_GRAY) { + xdev->std_colorant_names = DeviceGrayComponents; + xdev->num_std_colorant_names = 1; + xdev->color_info.cm_name = "DeviceGray"; + xdev->color_info.polarity = GX_CINFO_POLARITY_ADDITIVE; + } else if (color_model == XCF_DEVICE_RGB) { + xdev->std_colorant_names = DeviceRGBComponents; + xdev->num_std_colorant_names = 3; + xdev->color_info.cm_name = "DeviceRGB"; + xdev->color_info.polarity = GX_CINFO_POLARITY_ADDITIVE; + } else if (color_model == XCF_DEVICE_CMYK) { + xdev->std_colorant_names = DeviceCMYKComponents; + xdev->num_std_colorant_names = 4; + xdev->color_info.cm_name = "DeviceCMYK"; + xdev->color_info.polarity = GX_CINFO_POLARITY_SUBTRACTIVE; + } else if (color_model == XCF_DEVICE_N) { + xdev->std_colorant_names = DeviceCMYKComponents; + xdev->num_std_colorant_names = 4; + xdev->color_info.cm_name = "DeviceN"; + xdev->color_info.polarity = GX_CINFO_POLARITY_SUBTRACTIVE; + } else { + return -1; + } + + return 0; +} + +/* + * Close device and clean up ICC structures. + */ + +static int +xcf_prn_close(gx_device *dev) +{ + xcf_device * const xdev = (xcf_device *) dev; + + if (xdev->cmyk_icc_link != NULL) { + gscms_release_link(xdev->cmyk_icc_link); + rc_decrement(xdev->cmyk_profile, "xcf_prn_close"); + } + + if (xdev->rgb_icc_link != NULL) { + gscms_release_link(xdev->rgb_icc_link); + rc_decrement(xdev->rgb_profile, "xcf_prn_close"); + } + + if (xdev->output_icc_link != NULL) { + gscms_release_link(xdev->output_icc_link); + rc_decrement(xdev->output_profile, "xcf_prn_close"); + } + + return gdev_prn_close(dev); +} + +/* Set parameters. We allow setting the number of bits per component. */ +static int +xcf_put_params(gx_device * pdev, gs_param_list * plist) +{ + xcf_device * const pdevn = (xcf_device *) pdev; + gx_device_color_info save_info; + gs_param_name param_name; + int npcmcolors; + int num_spot = pdevn->separation_names.num_names; + int ecode = 0; + int code; + gs_param_string_array scna; + gs_param_string po; + gs_param_string prgb; + gs_param_string pcmyk; + gs_param_string pcm; + xcf_color_model color_model = pdevn->color_model; + + BEGIN_ARRAY_PARAM(param_read_name_array, "SeparationColorNames", scna, scna.size, scne) { + break; + } END_ARRAY_PARAM(scna, scne); + + if (code >= 0) + code = xcf_param_read_fn(plist, "ProfileOut", &po, + sizeof(pdevn->profile_out_fn)); + if (code >= 0) + code = xcf_param_read_fn(plist, "ProfileRgb", &prgb, + sizeof(pdevn->profile_rgb_fn)); + if (code >= 0) + code = xcf_param_read_fn(plist, "ProfileCmyk", &pcmyk, + sizeof(pdevn->profile_cmyk_fn)); + + if (code >= 0) + code = param_read_name(plist, "ProcessColorModel", &pcm); + if (code == 0) { + if (param_string_eq (&pcm, "DeviceGray")) + color_model = XCF_DEVICE_GRAY; + else if (param_string_eq (&pcm, "DeviceRGB")) + color_model = XCF_DEVICE_RGB; + else if (param_string_eq (&pcm, "DeviceCMYK")) + color_model = XCF_DEVICE_CMYK; + else if (param_string_eq (&pcm, "DeviceN")) + color_model = XCF_DEVICE_N; + else { + param_signal_error(plist, "ProcessColorModel", + code = gs_error_rangecheck); + } + } + if (code < 0) + return code; + + /* + * Save the color_info in case gdev_prn_put_params fails, and for + * comparison. + */ + save_info = pdevn->color_info; + ecode = xcf_set_color_model(pdevn, color_model); + if (ecode == 0) + ecode = gdev_prn_put_params(pdev, plist); + if (ecode < 0) { + pdevn->color_info = save_info; + return ecode; + } + + /* Separations are only valid with a subrtractive color model */ + if (pdev->color_info.polarity == GX_CINFO_POLARITY_SUBTRACTIVE) { + /* + * Process the separation color names. Remove any names that already + * match the process color model colorant names for the device. + */ + if (scna.data != 0) { + int i; + int num_names = scna.size; + fixed_colorant_names_list pcomp_names = + ((xcf_device *)pdev)->std_colorant_names; + + for (i = num_spot = 0; i < num_names; i++) { + if (!check_process_color_names(pcomp_names, &scna.data[i])) + pdevn->separation_names.names[num_spot++] = &scna.data[i]; + } + pdevn->separation_names.num_names = num_spot; + if (pdevn->is_open) + gs_closedevice(pdev); + } + npcmcolors = pdevn->num_std_colorant_names; + pdevn->color_info.num_components = npcmcolors + num_spot; + /* + * The DeviceN device can have zero components if nothing has been + * specified. This causes some problems so force at least one + * component until something is specified. + */ + if (!pdevn->color_info.num_components) + pdevn->color_info.num_components = 1; + pdevn->color_info.depth = bpc_to_depth(pdevn->color_info.num_components, + pdevn->bitspercomponent); + if (pdevn->color_info.depth != save_info.depth) { + gs_closedevice(pdev); + } + } + + if (po.data != 0) { + memcpy(pdevn->profile_out_fn, po.data, po.size); + pdevn->profile_out_fn[po.size] = 0; + } + if (prgb.data != 0) { + memcpy(pdevn->profile_rgb_fn, prgb.data, prgb.size); + pdevn->profile_rgb_fn[prgb.size] = 0; + } + if (pcmyk.data != 0) { + memcpy(pdevn->profile_cmyk_fn, pcmyk.data, pcmyk.size); + pdevn->profile_cmyk_fn[pcmyk.size] = 0; + } + code = xcf_open_profiles(pdevn); + + return code; +} + +/* + * This routine will check to see if the color component name match those + * that are available amoung the current device's color components. + * + * Parameters: + * dev - pointer to device data structure. + * pname - pointer to name (zero termination not required) + * nlength - length of the name + * + * This routine returns a positive value (0 to n) which is the device colorant + * number if the name is found. It returns a negative value if not found. + */ +static int +xcf_get_color_comp_index(gx_device * dev, const char * pname, int name_size, + int component_type) +{ +/* TO_DO_DEVICEN This routine needs to include the effects of the SeparationOrder array */ + fixed_colorant_name * pcolor = ((const xcf_device *)dev)->std_colorant_names; + int color_component_number = 0; + int i; + + /* Check if the component is in the implied list. */ + if (pcolor) { + while( *pcolor) { + if (compare_color_names(pname, name_size, *pcolor, strlen(*pcolor))) + return color_component_number; + pcolor++; + color_component_number++; + } + } + + /* Check if the component is in the separation names list. */ + { + const gs_separation_names * separations = &((const xcf_device *)dev)->separation_names; + int num_spot = separations->num_names; + + for (i=0; i<num_spot; i++) { + if (compare_color_names((const char *)separations->names[i]->data, + separations->names[i]->size, pname, name_size)) { + return color_component_number; + } + color_component_number++; + } + } + + return -1; +} + +/* ------ Private definitions ------ */ + +/* All two-byte quantities are stored MSB-first! */ +#if ARCH_IS_BIG_ENDIAN +# define assign_u16(a,v) a = (v) +# define assign_u32(a,v) a = (v) +#else +# define assign_u16(a,v) a = ((v) >> 8) + ((v) << 8) +# define assign_u32(a,v) a = (((v) >> 24) & 0xff) + (((v) >> 8) & 0xff00) + (((v) & 0xff00) << 8) + (((v) & 0xff) << 24) +#endif + +typedef struct { + gp_file *f; + int offset; + + int width; + int height; + int base_bytes_pp; /* almost always 3 (rgb) */ + int n_extra_channels; + + int n_tiles_x; + int n_tiles_y; + int n_tiles; + int n_levels; + + /* byte offset of image data */ + int image_data_off; +} xcf_write_ctx; + +#define TILE_WIDTH 64 +#define TILE_HEIGHT 64 + +static int +xcf_calc_levels(int size, int tile_size) +{ + int levels = 1; + while (size > tile_size) { + size >>= 1; + levels++; + } + return levels; +} + +static int +xcf_setup_tiles(xcf_write_ctx *xc, xcf_device *dev) +{ + xc->base_bytes_pp = 3; + xc->n_extra_channels = dev->separation_names.num_names; + xc->width = dev->width; + xc->height = dev->height; + xc->n_tiles_x = (dev->width + TILE_WIDTH - 1) / TILE_WIDTH; + xc->n_tiles_y = (dev->height + TILE_HEIGHT - 1) / TILE_HEIGHT; + xc->n_tiles = xc->n_tiles_x * xc->n_tiles_y; + xc->n_levels = max(xcf_calc_levels(dev->width, TILE_WIDTH), + xcf_calc_levels(dev->height, TILE_HEIGHT)); + + return 0; +} + +/* Return value: Size of tile in pixels. */ +static int +xcf_tile_sizeof(xcf_write_ctx *xc, int tile_idx) +{ + int tile_i = tile_idx % xc->n_tiles_x; + int tile_j = tile_idx / xc->n_tiles_x; + int tile_size_x = min(TILE_WIDTH, xc->width - tile_i * TILE_WIDTH); + int tile_size_y = min(TILE_HEIGHT, xc->height - tile_j * TILE_HEIGHT); + return tile_size_x * tile_size_y; +} + +static int +xcf_write(xcf_write_ctx *xc, const byte *buf, int size) { + int code; + + code = gp_fwrite(buf, 1, size, xc->f); + if (code < 0) + return code; + xc->offset += code; + return 0; +} + +static int +xcf_write_32(xcf_write_ctx *xc, bits32 v) +{ + bits32 buf; + + assign_u32(buf, v); + return xcf_write(xc, (byte *)&buf, 4); +} + +static int +xcf_write_image_props(xcf_write_ctx *xc) +{ + int code = 0; + + xcf_write_32(xc, 0); + xcf_write_32(xc, 0); + + return code; +} + +/** + * Return value: Number of bytes needed to write layer. + **/ +static int +xcf_base_size(xcf_write_ctx *xc, const char *layer_name) +{ + int bytes_pp = xc->base_bytes_pp + xc->n_extra_channels; + + return 17 + strlen (layer_name) + /* header and name */ + 8 + /* layer props */ + 12 + xc->n_levels * 16 + /* layer tile hierarchy */ + 12 + xc->n_tiles * 4 + /* tile offsets */ + xc->width * xc->height * bytes_pp; /* image data */ +} + +static int +xcf_channel_size(xcf_write_ctx *xc, int name_size) +{ + return 17 + name_size + /* header and name */ + 8 + /* channel props */ + 4 + xc->n_levels * 16 + /* channel tile hiearchy */ + 12 + xc->n_tiles * 4; /* tile offsets */ +} + +static int +xcf_write_header(xcf_write_ctx *xc, xcf_device *pdev) +{ + int code = 0; + const char *layer_name = "Background"; + int level; + int tile_offset; + int tile_idx; + int n_extra_channels = xc->n_extra_channels; + int bytes_pp = xc->base_bytes_pp + n_extra_channels; + int channel_idx; + + xcf_write(xc, (const byte *)"gimp xcf file", 14); + xcf_write_32(xc, xc->width); + xcf_write_32(xc, xc->height); + xcf_write_32(xc, 0); + + xcf_write_image_props(xc); + + /* layer offsets */ + xcf_write_32(xc, xc->offset + 12 + 4 * n_extra_channels); + xcf_write_32(xc, 0); + + /* channel offsets */ + tile_offset = xc->offset + 4 + 4 * n_extra_channels + + xcf_base_size(xc, layer_name); + for (channel_idx = 0; channel_idx < n_extra_channels; channel_idx++) { + const gs_param_string *separation_name = + pdev->separation_names.names[channel_idx]; + dmlprintf1(pdev->memory, "tile offset: %d\n", tile_offset); + xcf_write_32(xc, tile_offset); + tile_offset += xcf_channel_size(xc, separation_name->size); + } + xcf_write_32(xc, 0); + + /* layer */ + xcf_write_32(xc, xc->width); + xcf_write_32(xc, xc->height); + xcf_write_32(xc, 0); + xcf_write_32(xc, strlen(layer_name) + 1); + xcf_write(xc, (const byte *)layer_name, strlen(layer_name) + 1); + + /* layer props */ + xcf_write_32(xc, 0); + xcf_write_32(xc, 0); + + /* layer tile hierarchy */ + xcf_write_32(xc, xc->offset + 8); + xcf_write_32(xc, 0); + + xcf_write_32(xc, xc->width); + xcf_write_32(xc, xc->height); + xcf_write_32(xc, xc->base_bytes_pp); + xcf_write_32(xc, xc->offset + (1 + xc->n_levels) * 4); + tile_offset = xc->offset + xc->width * xc->height * bytes_pp + + xc->n_tiles * 4 + 12; + for (level = 1; level < xc->n_levels; level++) { + xcf_write_32(xc, tile_offset); + tile_offset += 12; + } + xcf_write_32(xc, 0); + + /* layer tile offsets */ + xcf_write_32(xc, xc->width); + xcf_write_32(xc, xc->height); + tile_offset = xc->offset + (xc->n_tiles + 1) * 4; + for (tile_idx = 0; tile_idx < xc->n_tiles; tile_idx++) { + xcf_write_32(xc, tile_offset); + tile_offset += xcf_tile_sizeof(xc, tile_idx) * bytes_pp; + } + xcf_write_32(xc, 0); + + xc->image_data_off = xc->offset; + + return code; +} + +static void +xcf_shuffle_to_tile(xcf_write_ctx *xc, byte **tile_data, const byte *row, + int y) +{ + int tile_j = y / TILE_HEIGHT; + int yrem = y % TILE_HEIGHT; + int tile_i; + int base_bytes_pp = xc->base_bytes_pp; + int n_extra_channels = xc->n_extra_channels; + int row_idx = 0; + + for (tile_i = 0; tile_i < xc->n_tiles_x; tile_i++) { + int x; + int tile_width = min(TILE_WIDTH, xc->width - tile_i * TILE_WIDTH); + int tile_height = min(TILE_HEIGHT, xc->height - tile_j * TILE_HEIGHT); + byte *base_ptr = tile_data[tile_i] + + yrem * tile_width * base_bytes_pp; + int extra_stride = tile_width * tile_height; + byte *extra_ptr = tile_data[tile_i] + extra_stride * base_bytes_pp + + yrem * tile_width; + + int base_idx = 0; + + for (x = 0; x < tile_width; x++) { + int plane_idx; + for (plane_idx = 0; plane_idx < base_bytes_pp; plane_idx++) + base_ptr[base_idx++] = row[row_idx++]; + for (plane_idx = 0; plane_idx < n_extra_channels; plane_idx++) + extra_ptr[plane_idx * extra_stride + x] = 255 ^ row[row_idx++]; + } + } +} + +static void +xcf_icc_to_tile(gx_device_printer *pdev, xcf_write_ctx *xc, byte **tile_data, const byte *row, + int y, gcmmhlink_t link) +{ + int tile_j = y / TILE_HEIGHT; + int yrem = y % TILE_HEIGHT; + int tile_i; + int base_bytes_pp = xc->base_bytes_pp; + int n_extra_channels = xc->n_extra_channels; + int row_idx = 0; + + for (tile_i = 0; tile_i < xc->n_tiles_x; tile_i++) { + int x; + int tile_width = min(TILE_WIDTH, xc->width - tile_i * TILE_WIDTH); + int tile_height = min(TILE_HEIGHT, xc->height - tile_j * TILE_HEIGHT); + byte *base_ptr = tile_data[tile_i] + + yrem * tile_width * base_bytes_pp; + int extra_stride = tile_width * tile_height; + byte *extra_ptr = tile_data[tile_i] + extra_stride * base_bytes_pp + + yrem * tile_width; + + int base_idx = 0; + + for (x = 0; x < tile_width; x++) { + + int plane_idx; + + /* This loop could be optimized. I don't quite + understand what is going on in the loop + with the 255^row[row_idx++] operation */ + + gscms_transform_color((gx_device*) pdev, link, + (void *) (&(row[row_idx])), + &(base_ptr[base_idx]), 1); + + for (plane_idx = 0; plane_idx < n_extra_channels; plane_idx++) + extra_ptr[plane_idx * extra_stride + x] = 255 ^ row[row_idx++]; + } + } +} + +static int +xcf_write_image_data(xcf_write_ctx *xc, gx_device_printer *pdev) +{ + int code = 0; + int raster = gdev_prn_raster(pdev); + int tile_i, tile_j; + byte **tile_data; + byte *line; + int base_bytes_pp = xc->base_bytes_pp; + int n_extra_channels = xc->n_extra_channels; + int bytes_pp = base_bytes_pp + n_extra_channels; + int chan_idx; + xcf_device *xdev = (xcf_device *)pdev; + gcmmhlink_t link = xdev->output_icc_link; + + line = gs_alloc_bytes(pdev->memory, raster, "xcf_write_image_data"); + tile_data = (byte **)gs_alloc_bytes(pdev->memory, + xc->n_tiles_x * sizeof(byte *), + "xcf_write_image_data"); + for (tile_i = 0; tile_i < xc->n_tiles_x; tile_i++) { + int tile_bytes = xcf_tile_sizeof(xc, tile_i) * bytes_pp; + + tile_data[tile_i] = gs_alloc_bytes(pdev->memory, tile_bytes, + "xcf_write_image_data"); + } + for (tile_j = 0; tile_j < xc->n_tiles_y; tile_j++) { + int y0, y1; + int y; + byte *row; + + y0 = tile_j * TILE_HEIGHT; + y1 = min(xc->height, y0 + TILE_HEIGHT); + for (y = y0; y < y1; y++) { + code = gdev_prn_get_bits(pdev, y, line, &row); + if (link == NULL) + xcf_shuffle_to_tile(xc, tile_data, row, y); + else + xcf_icc_to_tile(pdev, xc, tile_data, row, y, link); + } + for (tile_i = 0; tile_i < xc->n_tiles_x; tile_i++) { + int tile_idx = tile_j * xc->n_tiles_x + tile_i; + int tile_size = xcf_tile_sizeof(xc, tile_idx); + int base_size = tile_size * base_bytes_pp; + + xcf_write(xc, tile_data[tile_i], base_size); + for (chan_idx = 0; chan_idx < n_extra_channels; chan_idx++) { + xcf_write(xc, tile_data[tile_i] + base_size + + tile_size * chan_idx, tile_size); + } + } + } + + for (tile_i = 0; tile_i < xc->n_tiles_x; tile_i++) { + gs_free_object(pdev->memory, tile_data[tile_i], + "xcf_write_image_data"); + } + gs_free_object(pdev->memory, tile_data, "xcf_write_image_data"); + gs_free_object(pdev->memory, line, "xcf_write_image_data"); + return code; +} + +static int +xcf_write_fake_hierarchy(xcf_write_ctx *xc) +{ + int widthf = xc->width, heightf = xc->height; + int i; + + for (i = 1; i < xc->n_levels; i++) { + widthf >>= 1; + heightf >>= 1; + xcf_write_32(xc, widthf); + xcf_write_32(xc, heightf); + xcf_write_32(xc, 0); + } + return 0; +} + +static int +xcf_write_footer(xcf_write_ctx *xc, xcf_device *pdev) +{ + int code = 0; + int base_bytes_pp = xc->base_bytes_pp; + int n_extra_channels = xc->n_extra_channels; + int bytes_pp = base_bytes_pp + n_extra_channels; + int chan_idx; + + xcf_write_fake_hierarchy(xc); + + for (chan_idx = 0; chan_idx < xc->n_extra_channels; chan_idx++) { + const gs_param_string *separation_name = + pdev->separation_names.names[chan_idx]; + byte nullbyte[] = { 0 }; + int level; + int offset; + int tile_idx; + + dmlprintf2(pdev->memory, "actual tile offset: %d %d\n", xc->offset, (int)ARCH_SIZEOF_COLOR_INDEX); + xcf_write_32(xc, xc->width); + xcf_write_32(xc, xc->height); + xcf_write_32(xc, separation_name->size + 1); + xcf_write(xc, separation_name->data, separation_name->size); + xcf_write(xc, nullbyte, 1); + + /* channel props */ + xcf_write_32(xc, 0); + xcf_write_32(xc, 0); + + /* channel tile hierarchy */ + xcf_write_32(xc, xc->offset + 4); + + xcf_write_32(xc, xc->width); + xcf_write_32(xc, xc->height); + xcf_write_32(xc, 1); + xcf_write_32(xc, xc->offset + xc->n_levels * 16 - 8); + offset = xc->offset + xc->n_levels * 4; + for (level = 1; level < xc->n_levels; level++) { + xcf_write_32(xc, offset); + offset += 12; + } + xcf_write_32(xc, 0); + xcf_write_fake_hierarchy(xc); + + /* channel tile offsets */ + xcf_write_32(xc, xc->width); + xcf_write_32(xc, xc->height); + offset = xc->image_data_off; + for (tile_idx = 0; tile_idx < xc->n_tiles; tile_idx++) { + int tile_size = xcf_tile_sizeof(xc, tile_idx); + + xcf_write_32(xc, offset + (base_bytes_pp + chan_idx) * tile_size); + offset += bytes_pp * tile_size; + } + xcf_write_32(xc, 0); + + } + return code; +} + +static int +xcf_print_page(gx_device_printer *pdev, gp_file *file) +{ + xcf_write_ctx xc; + + xc.f = file; + xc.offset = 0; + + xcf_setup_tiles(&xc, (xcf_device *)pdev); + xcf_write_header(&xc, (xcf_device *)pdev); + xcf_write_image_data(&xc, pdev); + xcf_write_footer(&xc, (xcf_device *)pdev); + + return 0; +} |