From abb742af3affbe00da625a1272d682ac5fd4c964 Mon Sep 17 00:00:00 2001 From: Chris Liddell Date: Fri, 17 Nov 2017 15:17:03 +0000 Subject: Commit of gpdl-shared-device branch. This commit is a squashed version of the gpdl-shared-device branch. Essentially this is a first version of the new language switching mechanism. This does not build as part as "all", but rather as "experimental" or "gpdl". --- devices/gdevchameleon.c | 672 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 672 insertions(+) create mode 100644 devices/gdevchameleon.c (limited to 'devices/gdevchameleon.c') diff --git a/devices/gdevchameleon.c b/devices/gdevchameleon.c new file mode 100644 index 000000000..c944b92a8 --- /dev/null +++ b/devices/gdevchameleon.c @@ -0,0 +1,672 @@ +/* Copyright (C) 2001-2018 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. +*/ + + +/* Chameleon devices to allow for changing bit depths/colors at runtime. */ + +#include "gdevprn.h" +#include "gsparam.h" +#include "gscrd.h" +#include "gscrdp.h" +#include "gxlum.h" +#include "gxdcconv.h" +#include "gdevdcrd.h" +#include "gxdownscale.h" +#include "gxdevsop.h" + +/* 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_encode_color(chameleon_mono_encode_color); +static dev_proc_encode_color(chameleon_rgb_encode_color); +static dev_proc_encode_color(chameleon_cmyk_encode_color); +static dev_proc_decode_color(chameleon_mono_decode_color); +static dev_proc_decode_color(chameleon_rgb_decode_color); +static dev_proc_decode_color(chameleon_cmyk_decode_color); +static dev_proc_get_params(chameleon_get_params); +static dev_proc_put_params(chameleon_put_params); +static dev_proc_print_page(chameleon_print_page); +static dev_proc_dev_spec_op(chameleon_spec_op); + +struct gx_device_chameleon_s { + gx_device_common; + gx_prn_device_common; + int num_components; + int bpc; + int dst_num_components; + int dst_bpc; + int output_as_pxm; + bool language_uses_rops; + gx_downscaler_params downscale; +}; +typedef struct gx_device_chameleon_s gx_device_chameleon; + +static const gx_device_procs chameleon_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, + gdev_prn_close, + chameleon_rgb_encode_color, /* map_rgb_color */ + chameleon_rgb_decode_color, /* map_color_rgb */ + NULL, /* fill_rectangle */ + NULL, /* tile_rectangle */ + NULL, /* copy_mono */ + NULL, /* copy_color */ + NULL, /* draw_line */ + NULL, /* get_bits */ + chameleon_get_params, + chameleon_put_params, + chameleon_rgb_encode_color, /* 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 */ + 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 */ + NULL, /* get_color_mapping_procs */ + NULL, /* get_color_comp_index */ + chameleon_rgb_encode_color,/* encode_color */ + chameleon_rgb_decode_color, /* decode_color */ + NULL, /* pattern_manage */ + NULL, /* fill_rectangle_hl_color */ + NULL, /* include_color_space */ + NULL, /* fill_linear_color_scanline */ + NULL, /* fill_linear_color_trapezoid */ + NULL, /* fill_linear_color_triangle */ + NULL, /* update_spot_equivalent_colors */ + NULL, /* ret_devn_params */ + NULL, /* fillpage */ + NULL, /* push_transparency_state */ + NULL, /* pop_transparency_state */ + NULL, /* put_image */ + chameleon_spec_op /* dev_spec_op */ +}; + +const gx_device_chameleon gs_chameleon_device = +{prn_device_body(gx_device_chameleon, chameleon_procs, "chameleon", + DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS, + X_DPI, Y_DPI, + 0, 0, 0, 0, /* margins */ + 4, /* 3 colors (by default) */ + 4, /* depth */ + 15, /* max grey */ + 15, /* max color */ + 16, /* dither greys */ + 16, /* dither colors */ + chameleon_print_page), + 4, /* Default to CMYK output */ + 1, /* Default to 1bpc output */ + 4, /* Default to CMYK output */ + 1, /* Default to 1bpc output */ + 0, /* Default to outputting headerless data */ + 0, /* Default to not ropping */ + GX_DOWNSCALER_PARAMS_DEFAULTS +}; + +/* Map gray to color. */ +/* Note that 1-bit monochrome is a special case. */ +static gx_color_index +chameleon_mono_encode_color(gx_device * dev, const gx_color_value cv[]) +{ + int bpc = dev->color_info.depth; + int drop = sizeof(gx_color_value) * 8 - bpc; + gx_color_value gray = cv[0]; + + return (bpc == 1 ? gx_max_color_value - gray : gray) >> drop; +} + +static gx_color_index +chameleon_rgb_encode_color(gx_device * dev, const gx_color_value cv[]) +{ + if (dev->color_info.depth == 24) + return gx_color_value_to_byte(cv[2]) + + ((uint) gx_color_value_to_byte(cv[1]) << 8) + + ((ulong) gx_color_value_to_byte(cv[0]) << 16); + else { + COLROUND_VARS; + /* The following needs special handling to avoid bpc=5 when depth=16 */ + int bpc = dev->color_info.depth == 16 ? 4 : dev->color_info.depth / 3; + COLROUND_SETUP(bpc); + + return (((COLROUND_ROUND(cv[0]) << bpc) + + COLROUND_ROUND(cv[1])) << bpc) + + COLROUND_ROUND(cv[2]); + } +} + +/* Map CMYK to color. */ +static gx_color_index +chameleon_cmyk_encode_color(gx_device * dev, const gx_color_value cv[]) +{ + int bpc = dev->color_info.depth / 4; + int drop = sizeof(gx_color_value) * 8 - bpc; + gx_color_index color = + (((((((gx_color_index) cv[0] >> drop) << bpc) + + (cv[1] >> drop)) << bpc) + + (cv[2] >> drop)) << bpc) + + (cv[3] >> drop); + + return (color == gx_no_color_index ? color ^ 1 : color); +} + +/* Map color to RGB. This has 3 separate cases, but since it is rarely */ +/* used, we do a case test rather than providing 3 separate routines. */ +static int +chameleon_mono_decode_color(gx_device * dev, gx_color_index color, gx_color_value cv[4]) +{ + int depth = dev->color_info.depth; + int ncomp = dev->color_info.num_components; + int bpc = depth / ncomp; + uint mask = (1 << bpc) - 1; + +#define cvalue(c) ((gx_color_value)((ulong)(c) * gx_max_color_value / mask)) + + cv[0] = cv[1] = cv[2] = + (depth == 1 ? (color ? 0 : gx_max_color_value) : + cvalue(color)); + return 0; +#undef cvalue +} + +static int +chameleon_rgb_decode_color(gx_device * dev, gx_color_index color, gx_color_value cv[4]) +{ + int depth = dev->color_info.depth; + int ncomp = dev->color_info.num_components; + int bpc = depth / ncomp; + uint mask = (1 << bpc) - 1; + +#define cvalue(c) ((gx_color_value)((ulong)(c) * gx_max_color_value / mask)) + + gx_color_index cshift = color; + + cv[2] = cvalue(cshift & mask); + cshift >>= bpc; + cv[1] = cvalue(cshift & mask); + cv[0] = cvalue(cshift >> bpc); + return 0; +#undef cvalue +} + +static int +chameleon_cmyk_decode_color(gx_device * dev, gx_color_index color, gx_color_value cv[4]) +{ + int depth = dev->color_info.depth; + int ncomp = dev->color_info.num_components; + int bpc = depth / ncomp; + uint mask = (1 << bpc) - 1; + +#define cvalue(c) ((gx_color_value)((ulong)(c) * gx_max_color_value / mask)) + + gx_color_index cshift = color; + uint c, m, y, k; + + k = cshift & mask; + cshift >>= bpc; + y = cshift & mask; + cshift >>= bpc; + m = cshift & mask; + c = cshift >> bpc; + /* We use our improved conversion rule.... */ + cv[0] = cvalue((mask - c) * (mask - k) / mask); + cv[1] = cvalue((mask - m) * (mask - k) / mask); + cv[2] = cvalue((mask - y) * (mask - k) / mask); + return 0; +#undef cvalue +} + +/* # So long, oh, I hate to see you go */ +static void +reconfigure_baby(gx_device_chameleon *pcdev) +{ + int bpc = pcdev->dst_bpc; + int num_comps = pcdev->dst_num_components; + + if (pcdev->language_uses_rops) { + bpc = 8; + num_comps = 3; + } + + if (pcdev->bpc != bpc || + pcdev->num_components != num_comps) { + + gs_closedevice((gx_device *)pcdev); + + pcdev->bpc = bpc; + pcdev->num_components = num_comps; + + switch (num_comps * bpc) { + case 1*1: case 1*2: case 1*4: case 1*8: + case 4*4: case 4*8: + pcdev->color_info.depth = bpc * num_comps; + break; + case 3*1: + pcdev->color_info.depth = 4; + break; + case 3*2: + pcdev->color_info.depth = 8; + break; + case 3*4: + pcdev->color_info.depth = 16; + break; + } + pcdev->color_info.max_gray = pcdev->color_info.max_color = + (pcdev->color_info.dither_grays = pcdev->color_info.dither_colors = 1<color_info.separable_and_linear = GX_CINFO_SEP_LIN; + pcdev->bpc = bpc; + pcdev->num_components = num_comps; + } +} + +/* Get parameters. We provide a default CRD. */ +static int +chameleon_get_params(gx_device * pdev, gs_param_list * plist) +{ + int code, ecode; + gx_device_chameleon *pcdev = (gx_device_chameleon *)pdev; + + ecode = gdev_prn_get_params(pdev, plist); + code = sample_device_crd_get_params(pdev, plist, "CRDDefault"); + if (code < 0) + ecode = code; + + if ((code = param_write_int(plist, "DstBitDepth", &pcdev->dst_bpc)) < 0) + ecode = code; + if ((code = param_write_int(plist, "DstComponents", &pcdev->dst_num_components)) < 0) + ecode = code; + if ((code = gx_downscaler_write_params(plist, &pcdev->downscale, 0)) < 0) + ecode = code; + if ((code = param_write_int(plist, "OutputAsPXM", &pcdev->output_as_pxm)) < 0) + ecode = code; + if ((code = param_write_bool(plist, "LanguageUsesROPs", &pcdev->language_uses_rops)) < 0) + ecode = code; + + return ecode; +} + +/* Set parameters. We allow setting the number of bits per component, and number of components. */ +static int +chameleon_put_params(gx_device * pdev, gs_param_list * plist) +{ + gx_device_chameleon *pcdev = (gx_device_chameleon *)pdev; + gx_device_color_info save_info; + int pxm = pcdev->output_as_pxm; + int dst_num_comps = pcdev->dst_num_components; + int dst_bpc = pcdev->dst_bpc; + bool language_uses_rops = pcdev->language_uses_rops; + int ecode; + int code; + const char *vname; + + ecode = gx_downscaler_read_params(plist, &pcdev->downscale, 0); + + if ((code = param_read_int(plist, (vname = "DstBitDepth"), &dst_bpc)) != 1) { + if (code < 0) + ecode = code; + else + switch (dst_bpc) { + case 1: case 2: case 4: case 8: break; + default: + param_signal_error(plist, vname, + ecode = gs_error_rangecheck); + } + } + + if ((code = param_read_int(plist, (vname = "DstComponents"), &dst_num_comps)) != 1) { + if (code < 0) + ecode = code; + else + switch (dst_num_comps) { + case 1: case 3: case 4: break; + default: + param_signal_error(plist, vname, + ecode = gs_error_rangecheck); + } + } + + if ((code = param_read_int(plist, (vname = "OutputAsPXM"), &pxm)) != 1) { + if (code == gs_error_typecheck) + pxm = 1; + else if (code < 0) + ecode = code; + else + pxm = !!pxm; + } + + if ((code = param_read_bool(plist, (vname = "LanguageUsesROPs"), &language_uses_rops)) != 1) { + if (code == gs_error_typecheck) + language_uses_rops = false; + else if (code < 0) + ecode = code; + } + + ecode = gdev_prn_put_params(pdev, plist); + if (ecode < 0) + return ecode; + + pcdev->dst_bpc = dst_bpc; + pcdev->dst_num_components = dst_num_comps; + pcdev->output_as_pxm = pxm; + pcdev->language_uses_rops = language_uses_rops; + + reconfigure_baby(pcdev); + + return 0; +} + +static int +chameleon_spec_op(gx_device *dev_, int op, void *data, int datasize) +{ + gx_device_chameleon *dev = (gx_device_chameleon *)dev_; + + if (op == gxdso_supports_iccpostrender) { + return true; + } + return gdev_prn_dev_spec_op(dev_, op, data, datasize); +} + +static int +craprgbtocmyk(void *arg, + byte **dst, + byte **src, + int w, + int h, + int raster) +{ + byte *d = *dst; + byte *s = *src; + + while (w--) { + int c = 255-*s++; + int m = 255-*s++; + int y = 255-*s++; + int k = c; + if (k > m) + k = m; + if (k > y) + k = y; + *d++ = c - k; + *d++ = m - k; + *d++ = y - k; + *d++ = k; + } + + return 0; +} + +static int +header_4x1(FILE *file, gx_device_chameleon *pcdev) +{ + fprintf(file, "P7\nWIDTH %d\nHEIGHT %d\nDEPTH 4\nMAXVAL 255\nTUPLTYPE CMYK\nENDHDR\n", + pcdev->width, pcdev->height); + return 0; +} + +static int +write_4x1(const byte *data, int n, FILE *file) +{ + byte b[8]; + n -= 4; + while (n > 0) { + byte d = *data++; + b[0] = (d & 128) ? 255 : 0; + b[1] = (d & 64) ? 255 : 0; + b[2] = (d & 32) ? 255 : 0; + b[3] = (d & 16) ? 255 : 0; + b[4] = (d & 8) ? 255 : 0; + b[5] = (d & 4) ? 255 : 0; + b[6] = (d & 2) ? 255 : 0; + b[7] = (d & 1) ? 255 : 0; + fwrite(b, 1, 8, file); + n -= 8; + } + if (n == 0) { + byte d = *data; + b[0] = (d & 128) ? 255 : 0; + b[1] = (d & 64) ? 255 : 0; + b[2] = (d & 32) ? 255 : 0; + b[3] = (d & 16) ? 255 : 0; + fwrite(b, 1, 4, file); + } + return 0; +} + +static int +header_3x8(FILE *file, gx_device_chameleon *pcdev) +{ + fprintf(file, "P6\n%d %d 255\n", + pcdev->width, pcdev->height); + return 0; +} + +static int +do_fwrite(const byte *data, int n, FILE *file) +{ + return fwrite(data, 1, (n+7)>>3, file); +} + +static int tiff_chunky_post_cm(void *arg, byte **dst, byte **src, int w, int h, + int raster) +{ + gsicc_bufferdesc_t input_buffer_desc, output_buffer_desc; + gsicc_link_t *icclink = (gsicc_link_t*)arg; + + gsicc_init_buffer(&input_buffer_desc, icclink->num_input, 1, false, + false, false, 0, raster, h, w); + gsicc_init_buffer(&output_buffer_desc, icclink->num_output, 1, false, + false, false, 0, raster, h, w); + icclink->procs.map_buffer(NULL, icclink, &input_buffer_desc, &output_buffer_desc, + src[0], dst[0]); + return 0; +} + +static byte ht_data[256] = +{ + 0x0E, 0x8E, 0x2E, 0xAE, 0x06, 0x86, 0x26, 0xA6, 0x0C, 0x8C, 0x2C, 0xAC, 0x04, 0x84, 0x24, 0xA4, + 0xCE, 0x4E, 0xEE, 0x6E, 0xC6, 0x46, 0xE6, 0x66, 0xCC, 0x4C, 0xEC, 0x6C, 0xC4, 0x44, 0xE4, 0x64, + 0x3E, 0xBE, 0x1E, 0x9E, 0x36, 0xB6, 0x16, 0x96, 0x3C, 0xBC, 0x1C, 0x9C, 0x34, 0xB4, 0x14, 0x94, + 0xFE, 0x7E, 0xDE, 0x5E, 0xF6, 0x76, 0xD6, 0x56, 0xFC, 0x7C, 0xDC, 0x5C, 0xF4, 0x74, 0xD4, 0x54, + 0x01, 0x81, 0x21, 0xA1, 0x09, 0x89, 0x29, 0xA9, 0x03, 0x83, 0x23, 0xA3, 0x0B, 0x8B, 0x2B, 0xAB, + 0xC1, 0x41, 0xE1, 0x61, 0xC9, 0x49, 0xE9, 0x69, 0xC3, 0x43, 0xE3, 0x63, 0xCB, 0x4B, 0xEB, 0x6B, + 0x31, 0xB1, 0x11, 0x91, 0x39, 0xB9, 0x19, 0x99, 0x33, 0xB3, 0x13, 0x93, 0x3B, 0xBB, 0x1B, 0x9B, + 0xF1, 0x71, 0xD1, 0x51, 0xF9, 0x79, 0xD9, 0x59, 0xF3, 0x73, 0xD3, 0x53, 0xFB, 0x7B, 0xDB, 0x5B, + 0x0D, 0x8D, 0x2D, 0xAD, 0x05, 0x85, 0x25, 0xA5, 0x0F, 0x8F, 0x2F, 0xAF, 0x07, 0x87, 0x27, 0xA7, + 0xCD, 0x4D, 0xED, 0x6D, 0xC5, 0x45, 0xE5, 0x65, 0xCF, 0x4F, 0xEF, 0x6F, 0xC7, 0x47, 0xE7, 0x67, + 0x3D, 0xBD, 0x1D, 0x9D, 0x35, 0xB5, 0x15, 0x95, 0x3F, 0xBF, 0x1F, 0x9F, 0x37, 0xB7, 0x17, 0x97, + 0xFD, 0x7D, 0xDD, 0x5D, 0xF5, 0x75, 0xD5, 0x55, 0xFF, 0x7F, 0xDF, 0x5F, 0xF7, 0x77, 0xD7, 0x57, + 0x02, 0x82, 0x22, 0xA2, 0x0A, 0x8A, 0x2A, 0xAA, 0x01 /*0x00*/, 0x80, 0x20, 0xA0, 0x08, 0x88, 0x28, 0xA8, + 0xC2, 0x42, 0xE2, 0x62, 0xCA, 0x4A, 0xEA, 0x6A, 0xC0, 0x40, 0xE0, 0x60, 0xC8, 0x48, 0xE8, 0x68, + 0x32, 0xB2, 0x12, 0x92, 0x3A, 0xBA, 0x1A, 0x9A, 0x30, 0xB0, 0x10, 0x90, 0x38, 0xB8, 0x18, 0x98, + 0xF2, 0x72, 0xD2, 0x52, 0xFA, 0x7A, 0xDA, 0x5A, 0xF0, 0x70, 0xD0, 0x50, 0xF8, 0x78, 0xD8, 0x58 +}; + +static gx_downscaler_ht_t default_ht[4] = +{ + { 16, 16, 16, 0, 0, ht_data }, + { 16, 16, 16, 0, 0, ht_data }, + { 16, 16, 16, 0, 0, ht_data }, + { 16, 16, 16, 0, 0, ht_data } +}; + +/* Send the page to the printer. */ +static int +chameleon_print_page(gx_device_printer * pdev, FILE * prn_stream) +{ /* Just dump the bits on the file. */ + gx_device_chameleon *pcdev = (gx_device_chameleon *)pdev; + /* If the file is 'nul', don't even do the writes. */ + int line_size = gdev_mem_bytes_per_scan_line((gx_device *) pdev); + byte *in, *in_alloc; + byte *data; + int nul; + int line_count = pdev->height; + int i, code; + gx_downscaler_t ds = { NULL }; + int depth = pdev->color_info.depth; + int factor = pcdev->downscale.downscale_factor; + int mfs = pcdev->downscale.min_feature_size; + int pxm = pcdev->output_as_pxm; + int dst_bpp; + int (*write)(const byte *, int, FILE *) = do_fwrite; + int (*header)(FILE *, gx_device_chameleon *) = NULL; + int bitwidth; + gx_downscale_cm_fn *col_convert = NULL; + void *col_convert_arg = NULL; + + switch (pcdev->dst_num_components * pcdev->dst_bpc) { + case 1*1: case 1*2: case 1*4: case 1*8: + case 4*4: case 4*8: case 3*8: + dst_bpp = pcdev->dst_num_components * pcdev->dst_bpc; + break; + case 3*1: + dst_bpp = 4; + break; + case 3*2: + dst_bpp = 8; + break; + case 3*4: + dst_bpp = 12; + break; + default: + return gs_error_rangecheck; + } + + bitwidth = pdev->width * dst_bpp; + line_size = (bitwidth+7)>>3; + in_alloc = gs_alloc_bytes(pdev->memory, line_size+32, "chameleon_print_page(in)"); + if (in_alloc == 0) + return_error(gs_error_VMerror); + in = in_alloc + ((32-(intptr_t)in_alloc) & 31); + + if (!strcmp(pdev->fname, "nul") || !strcmp(pdev->fname, "/dev/null")) + write = NULL; + else if (pxm) { + switch (pcdev->dst_num_components) { + case 4: + if (pcdev->dst_bpc == 1) + header = header_4x1, write = write_4x1; + break; + case 3: + if (pcdev->dst_bpc == 8) + header = header_3x8; + break; + } + } + + /* If we have a post render profile, and the number of components + * of that is compatible with the desired destination number of + * components, then use that. */ + if (pcdev->icc_struct->postren_profile && + pcdev->icc_struct->postren_profile->num_comps == pcdev->dst_num_components) { + col_convert = tiff_chunky_post_cm; + col_convert_arg = pcdev->icc_struct->postren_profile; + } + /* Can we get away with no conversion? */ + else if (pcdev->num_components == pcdev->dst_num_components && + pcdev->bpc == pcdev->dst_bpc) { + /* Nothing to do */ + } + /* Otherwise, use some inbuilt crap conversions */ + else if (pcdev->num_components == 3 && pcdev->bpc == 8 && pcdev->dst_num_components == 4) { + col_convert = craprgbtocmyk; + col_convert_arg = NULL; + } else { + emprintf(pdev->memory, "Chameleon device doesn't support this color conversion.\n"); + return gs_error_rangecheck; + } + + code = gx_downscaler_init_trapped_cm_halftone + (&ds, + (gx_device *)pdev, + pcdev->bpc, + pcdev->dst_bpc, + pcdev->num_components, + pcdev->downscale.downscale_factor, + pcdev->downscale.min_feature_size, + NULL, 0, /* Adjust width */ + 0, 0, NULL, /* Trapping w/h/comp_order */ + col_convert, col_convert_arg, /* Color Management */ + pcdev->dst_num_components, + default_ht); + if (code < 0) + goto cleanup; + + if (header) { + code = header(prn_stream, pcdev); + if (code < 0) + goto cleanup; + } + + for (i = 0; i < line_count; i++) { + code = gx_downscaler_getbits(&ds, in, i); + if (code < 0) + break; + if (write != NULL) + write(in, bitwidth, prn_stream); + } + gx_downscaler_fin(&ds); +cleanup: + gs_free_object(pdev->memory, in_alloc, "chameleon_print_page(in)"); + return code; +} -- cgit v1.2.1