diff options
Diffstat (limited to 'gs/base/gxdhtserial.c')
-rw-r--r-- | gs/base/gxdhtserial.c | 680 |
1 files changed, 680 insertions, 0 deletions
diff --git a/gs/base/gxdhtserial.c b/gs/base/gxdhtserial.c new file mode 100644 index 000000000..8f300faa8 --- /dev/null +++ b/gs/base/gxdhtserial.c @@ -0,0 +1,680 @@ +/* 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$ */ +/* Serialization and de-serialization for (traditional) halftones */ + +#include "memory_.h" +#include "gx.h" +#include "gscdefs.h" +#include "gserrors.h" +#include "gsstruct.h" +#include "gsutil.h" /* for gs_next_ids */ +#include "gzstate.h" +#include "gxdevice.h" /* for gzht.h */ +#include "gzht.h" +#include "gswts.h" +#include "gxdhtres.h" +#include "gsserial.h" +#include "gxdhtserial.h" + + +/* + * Declare the set of procedures that return resident halftones. This + * declares both the array of procedures and their type. It is used + * only to check if a transmitted halftone order matches one in ROM. + */ +extern_gx_device_halftone_list(); + + +/* + * An enumeration of halftone transfer functions. These must distinguish + * between cases in which no transfer function is present, and when one + * is present but provides the identity transformation (an empty + * PostScript array). + */ +typedef enum { + gx_ht_tf_none = 0, + gx_ht_tf_identity, + gx_ht_tf_complete +} gx_ht_tf_type_t; + +/* enumeration to distinguish well-tempered screening orders from others */ +typedef enum { + gx_ht_traditional, + gx_ht_wts +} gx_ht_order_type_t; + + +/* + * Serialize a transfer function. These will occupy one byte if they are + * not present or provide an identity mapping, + * 1 + transfer_map_size * sizeof(frac) otherwise. + * + * Returns: + * + * 0, with *psize set the the amount of space required, if successful + * + * gs_error_rangecheck, with *psize set to the size required, if the + * original *psize was not large enough + */ +static int +gx_ht_write_tf( + const gx_transfer_map * pmap, + byte * data, + uint * psize ) +{ + int req_size = 1; /* minimum of one byte */ + + /* check for sufficient space */ + if ( pmap != 0 && pmap->proc != gs_identity_transfer) + req_size += sizeof(pmap->values); + if (req_size > *psize) { + *psize = req_size; + return gs_error_rangecheck; + } + + if (req_size == 1) + *data = (byte)(pmap == 0 ? gx_ht_tf_none : gx_ht_tf_identity); + else { + *data++ = (byte)gx_ht_tf_complete; + memcpy(data, pmap->values, sizeof(pmap->values)); + } + + *psize = req_size; + return 0; +} + +/* + * Reconstruct a transfer function from its serial representation. The + * buffer provided is expected to be large enough to hold the entire + * transfer function. + * + * Returns the number of bytes read, or < 0 in the event of an error. + */ +static int +gx_ht_read_tf( + gx_transfer_map ** ppmap, + const byte * data, + uint size, + gs_memory_t * mem ) +{ + gx_ht_tf_type_t tf_type; + gx_transfer_map * pmap; + + /* read the type byte */ + if (size == 0) + return_error(gs_error_rangecheck); + --size; + tf_type = (gx_ht_tf_type_t)*data++; + + /* if no transfer function, exit now */ + if (tf_type == gx_ht_tf_none) { + *ppmap = 0; + return 1; + } + + /* allocate a transfer map */ + rc_alloc_struct_1( pmap, + gx_transfer_map, + &st_transfer_map, + mem, + return_error(gs_error_VMerror), + "gx_ht_read_tf" ); + + pmap->id = gs_next_ids(mem, 1); + pmap->closure.proc = 0; + pmap->closure.data = 0; + if (tf_type == gx_ht_tf_identity) { + gx_set_identity_transfer(pmap); + return 1; + } else if (tf_type == gx_ht_tf_complete && size >= sizeof(pmap->values)) { + memcpy(pmap->values, data, sizeof(pmap->values)); + pmap->proc = gs_mapped_transfer; + *ppmap = pmap; + return 1 + sizeof(pmap->values); + } else { + rc_decrement(pmap, "gx_ht_read_tf"); + return_error(gs_error_rangecheck); + } +} + +static int +gx_ht_write_component_wts(const wts_screen_t *wts, byte *data, uint *psize) +{ + uint hdr_size = wts_size(wts); + uint cell_nsamples = wts->cell_width * wts->cell_height; + uint cell_size = cell_nsamples * sizeof(wts_screen_sample_t); + uint req_size = 1 + hdr_size + cell_size; + + if (req_size > *psize) { + *psize = req_size; + return gs_error_rangecheck; + } + + /* identify this as a wts halftone. */ + *data++ = (byte)gx_ht_wts; + + /* copy in wts header */ + memcpy(data, wts, hdr_size); + ((wts_screen_t *)data)->samples = NULL; + data += hdr_size; + + /* copy in treshold cell */ + memcpy(data, wts->samples, cell_size); + *psize = req_size; + return 0; +} + +/* + * Serialize a halftone component. The only part that is serialized is the + * halftone order; the other two components are only required during + * halftone construction. + * + * Returns: + * + * 0, with *psize set the the amount of space required, if successful + * + * gs_error_rangecheck, with *psize set to the size required, if the + * original *psize was not large enough + * + * some other error code, with *psize unchanged, in the event of an + * error other than lack of space + */ +static int +gx_ht_write_component( + const gx_ht_order_component * pcomp, + byte * data, + uint * psize ) +{ + const gx_ht_order * porder = &pcomp->corder; + byte * data0 = data; + int code, levels_size, bits_size; + uint tmp_size = 0; + int req_size; + + /* + * There is no need to transmit the comp_number field, as this must be + * the same as the index in the component array (see gx_ht_write). + * + * There is also no reason to transmit the colorant name (cname), as + * this is only used by some high-level devices that would not be targets + * of the command list device (and even those devices should be able to + * get the information from their color models). + * + * This leaves the order itself. + * + * Check if we are a well-tempered-screening order. Serialization of these + * is handled in a separate function. + */ + if (porder->wts != 0) + return gx_ht_write_component_wts(porder->wts, data, psize); + + /* + * The following order fields are not transmitted: + * + * params Only required during halftone cell construction + * + * wse, wts Only used for well-tempered screens (see above) + * + * raster Can be re-calculated by the renderer from the width + * + * orig_height, The only potential use for these parameters is in + * orig_shift this routine; they are not useful to the renderer. + * + * full_height Can be re-calculated by the renderer from the + * height, width, and shift values. + * + * data_memory Must be provided by the renderer. + * + * cache Must be provided by the renderer. + * + * screen_params Ony required during halftone cell construction + * + * In addition, the procs parameter is passed as an index into the + * ht_order_procs_table, as the renderer may not be in the same address + * space as the writer. + * + * Calculate the size required. + */ + levels_size = porder->num_levels * sizeof(porder->levels[0]); + bits_size = porder->num_bits * porder->procs->bit_data_elt_size; + req_size = 1 /* gx_ht_type_t */ + + enc_u_sizew(porder->width) + + enc_u_sizew(porder->height) + + enc_u_sizew(porder->shift) + + enc_u_sizew(porder->num_levels) + + enc_u_sizew(porder->num_bits) + + 1 /* order procs, as index into table */ + + levels_size + + bits_size; + code = gx_ht_write_tf(porder->transfer, data, &tmp_size); + if (code < 0 && code != gs_error_rangecheck) + return code; + req_size += tmp_size; + if (req_size > *psize) { + *psize = req_size; + return gs_error_rangecheck; + } + + /* identify this as a traditional halftone */ + *data++ = (byte)gx_ht_traditional; + + /* write out the dimensional data */ + enc_u_putw(porder->width, data); + enc_u_putw(porder->height, data); + enc_u_putw(porder->shift, data); + enc_u_putw(porder->num_levels, data); + enc_u_putw(porder->num_bits, data); + + /* white out the procs index */ + *data++ = porder->procs - ht_order_procs_table; + + /* copy the levels array and whitening order array */ + memcpy(data, porder->levels, levels_size); + data += levels_size; + memcpy(data, porder->bit_data, bits_size); + data += bits_size; + + /* write out the transfer function */ + tmp_size = *psize - (data - data0); + if ((code = gx_ht_write_tf(porder->transfer, data, &tmp_size)) == 0) + *psize = tmp_size + (data - data0); + return code; +} + +static int +gx_ht_read_component_wts(gx_ht_order_component *pcomp, + const byte *data, uint size, + gs_memory_t *mem) +{ + const wts_screen_t *ws = (const wts_screen_t *)data; + int hdr_size = wts_size(ws); + int cell_size = ws->cell_width * ws->cell_height * + sizeof(wts_screen_sample_t); + int bufsize = 1+hdr_size+cell_size; + + memset(&pcomp->corder, 0, sizeof(pcomp->corder)); + + if (size < bufsize) + return -1; + pcomp->corder.wts = gs_wts_from_buf(data, bufsize); + pcomp->cname = 0; + if (pcomp->corder.wts == NULL) + return -1; + + return bufsize; +} + +/* + * Reconstruct a halftone component from its serial representation. The + * buffer provided is expected to be large enough to hold the entire + * halftone component. + * + * Because halftone components are allocated in arrays (an unfortunate + * arrangement, as it prevents component sharing), a pointer to an + * already allocated component structure is passed as an operand, as + * opposed to the more normal mechanism that would have a read routine + * allocate the component. The memory pointer is still passed, however, + * as the levels and bit_data arrays must be allocated. + * + * Returns the number of bytes read, or < 0 in the event of an error. + */ +static int +gx_ht_read_component( + gx_ht_order_component * pcomp, + const byte * data, + uint size, + gs_memory_t * mem ) +{ + gx_ht_order new_order; + const byte * data0 = data; + const byte * data_lim = data + size; + gx_ht_order_type_t order_type; + int i, code, levels_size, bits_size; + const gx_dht_proc * phtrp = gx_device_halftone_list; + + /* check the order type */ + if (size == 0) + return_error(gs_error_rangecheck); + --size; + order_type = (gx_ht_order_type_t)*data++; + + /* currently only the traditional halftone order are supported */ + if (order_type != gx_ht_traditional) + return gx_ht_read_component_wts(pcomp, data, size, mem); + + /* + * For performance reasons, the number encoding macros do not + * support full buffer size verification. The code below verifies + * that a minimum number of bytes is available, then converts + * blindly and does not check again until the various integers are + * read. Obviously this can be hazardous, but should not be a + * problem in practice, as the calling code should have verified + * that the data provided holds the entire halftone. + */ + if (size < 7) + return_error(gs_error_rangecheck); + enc_u_getw(new_order.width, data); + enc_u_getw(new_order.height, data); + enc_u_getw(new_order.shift, data); + enc_u_getw(new_order.num_levels, data); + enc_u_getw(new_order.num_bits, data); + if (data >= data_lim) + return_error(gs_error_rangecheck); + new_order.procs = &ht_order_procs_table[*data++]; + + /* calculate the space required for levels and bit data */ + levels_size = new_order.num_levels * sizeof(new_order.levels[0]); + bits_size = new_order.num_bits * new_order.procs->bit_data_elt_size; + + /* + 1 below is for the minimal transfer function */ + if (data + bits_size + levels_size + 1 > data_lim) + return_error(gs_error_rangecheck); + + /* + * Allocate the levels and bit data structures. The gx_ht_alloc_ht_order + * has a name that is both strange and misleading. The routine does + * not allocate a halftone order. Rather, it initializes the order, + * and allocates the levels and bit data arrays. In particular, it + * sets all of the following fields: + * + * wse = 0, + * wts = 0, + * width = operand width + * height = operand height + * raster = bitmap_raster(operand width) + * shift = operand shift + * orig_height = operand height + * orig_shift = operand strip_shift + * num_levels = operand num_levels + * num_bits = operand num_bits + * procs = operand procs + * levels = newly allocated array + * bit_data = new allocated array + * cache = 0 + * transfer = 0 + * + * Since several of the list fields are already set, this call + * effectively sets them to the values they already have. This is a + * bit peculiar but not otherwise harmful. + * + * For reasons that are not known and are probably historical, the + * procedure does not initialize the params or screen_params fields. + * In the unlikely event that these fields are ever contain pointers, + * we initialize them explicitly here. Wse, params, and scrren_params + * probably should not occur in the device halftone at all; they are + * themselves historical artifacts. + */ + code = gx_ht_alloc_ht_order( &new_order, + new_order.width, + new_order.height, + new_order.num_levels, + new_order.num_bits, + new_order.shift, + new_order.procs, + mem ); + if (code < 0) + return code; + memset(&new_order.params, 0, sizeof(new_order.params)); + memset(&new_order.screen_params, 0, sizeof(new_order.screen_params)); + + /* fill in the levels and bit_data arrays */ + memcpy(new_order.levels, data, levels_size); + data += levels_size; + memcpy(new_order.bit_data, data, bits_size); + data += bits_size; + + /* process the transfer function */ + code = gx_ht_read_tf(&new_order.transfer, data, data_lim - data, mem); + if (code < 0) { + gx_ht_order_release(&new_order, mem, false); + return code; + } + data += code; + + /* + * Check to see if the order is in ROM. Since it is possible (if not + * particularly likely) that the command list writer and renderer do + * not have the same set of ROM-based halftones, the full halftone + * order is transmitted and compared against the set ROM set provided + * by the renderer. If there is a match, the transmitted version is + * discarded and the ROM version used. + * + * It is not clear which, if any or the currently used devices + * provide a ROM-based halftone order set. + */ + for (i = 0; phtrp[i] != 0; i++) { + const gx_device_halftone_resource_t *const * pphtr = phtrp[i](); + const gx_device_halftone_resource_t * phtr; + + while ((phtr = *pphtr++) != 0) { + /* + * This test does not check for strict equality of the order, + * nor is strict equality necessary. The ROM data will replace + * just the levels and bit_data arrays of the transmitted + * order, so only these must be the same. We don't even care + * if the ROM's levels and bit_data arrays are larger; we + * will never check values beyond the range required by the + * current order. + */ + if ( phtr->num_levels * sizeof(phtr->levels[0]) >= levels_size && + phtr->Width * phtr->Height * phtr->elt_size >= bits_size && + memcmp(phtr->levels, new_order.levels, levels_size) == 0 && + memcmp(phtr->bit_data, new_order.bit_data, bits_size) == 0 ) { + /* the casts below are required to discard const qualifiers */ + gs_free_object(mem, new_order.bit_data, "gx_ht_read_component"); + new_order.bit_data = (void *)phtr->bit_data; + gs_free_object(mem, new_order.levels, "gx_ht_read_component"); + new_order.levels = (uint *)phtr->levels; + goto done; + } + } + } + + done: + /* everything OK, save the order and return the # of bytes read */ + pcomp->corder = new_order; + pcomp->cname = 0; + return data - data0; +} + + +/* + * Serialize a halftone. The essential step is the serialization of the + * halftone orders; beyond this only the halftone type must be + * transmitted. + * + * Returns: + * + * 0, with *psize set the the amount of space required, if successful + * + * gs_error_rangecheck, with *psize set to the size required, if the + * original *psize was not large enough + * + * some other error code, with *psize unchange, in the event of an + * error other than lack of space + */ +int +gx_ht_write( + const gx_device_halftone * pdht, + const gx_device * dev, + byte * data, + uint * psize ) +{ + int num_dev_comps = pdht->num_dev_comp; + int i, code; + uint req_size = 2, used_size = 2; + /* 1 for halftone type, 1 for num_dev_comps */ + + /* + * With the introduction of color models, there should never be a + * halftone that includes just one component. Enforce this + * restriction, even though it is not present in much of the rest + * of the code. + * + * NB: the pdht->order field is ignored by this code. + */ + if (pdht == 0 || pdht->components == 0) + return_error(gs_error_unregistered); /* Must not happen. */ + + /* + * The following fields do not need to be transmitted: + * + * order Ignored by this code (see above). + * + * rc, id Recreated by the allocation code on the renderer. + * + * lcm_width, Can be recreated by the de-serialization code on the + * lcm_height the renderer. Since halftones are transmitted + * infrequently (for normal jobs), the time required + * for re-calculation is not significant. + * + * Hence, the only fields that must be serialized are the type,and + * the number of components. (The number of components for the halftone + * may not match the device's if we are compositing with a process color + * model which does not match the output device. + * + * Several halftone components may be identical, but there is + * currently no simple way to determine this. Halftones are normally + * transmitted only once per page, so it is not clear that use of + * such information would significantly reduce command list size. + */ + + /* calculate the required data space */ + for ( i = 0, code = gs_error_rangecheck; + i < num_dev_comps && code == gs_error_rangecheck; + i++) { + uint tmp_size = 0; + + /* sanity check */ + if (i != pdht->components[i].comp_number) + return_error(gs_error_unregistered); /* Must not happen. */ + + code = gx_ht_write_component( &pdht->components[i], + data, + &tmp_size ); + req_size += tmp_size; + } + if (code < 0 && code != gs_error_rangecheck) + return code; + else if (*psize < req_size) { + *psize = req_size; + return 0; + } + req_size = *psize; + + /* the halftone type is known to fit in a byte */ + *data++ = (byte)pdht->type; + /* the number of components is known to fit in a byte */ + *data++ = (byte)num_dev_comps; + + /* serialize the halftone components */ + for (i = 0, code = 0; i < num_dev_comps && code == 0; i++) { + uint tmp_size = req_size - used_size; + + code = gx_ht_write_component( &pdht->components[i], + data, + &tmp_size ); + used_size += tmp_size; + data += tmp_size; + } + + if (code < 0) { + if (code == gs_error_rangecheck) + code = gs_error_unknownerror; + return code; + } + + *psize = used_size; + return 0; +} + +/* + * Reconstruct a halftone from its serial representation, and install it + * as the current halftone. The buffer provided is expected to be large + * enough to hold the entire halftone. + * + * The reading and installation phases are combined in this routine so as + * to avoid unnecessarily allocating a device halftone and its component + * array, just to release them immediately after installation is complete. + * There is also not much reason to reconstuct a halftone except to make + * it the current halftone. + * + * Returns the number of bytes read, or <0 in the event of an error. + */ +int +gx_ht_read_and_install( + gs_imager_state * pis, + const gx_device * dev, + const byte * data, + uint size, + gs_memory_t * mem ) +{ + gx_ht_order_component components[GX_DEVICE_COLOR_MAX_COMPONENTS]; + gx_ht_order_component components_save[GX_DEVICE_COLOR_MAX_COMPONENTS]; + const byte * data0 = data; + gx_device_halftone dht; + int num_dev_comps; + int i, code; + + /* fill in some fixed fields */ + memset(&dht.order, 0, sizeof(dht.order)); + memset(&dht.rc, 0, sizeof(dht.rc)); + dht.id = gs_no_id; /* updated during installation */ + dht.components = components; + dht.lcm_width = 1; /* recalculated during installation */ + dht.lcm_height = 1; + + /* clear pointers in the components array in case we need to abort */ + memset(components, 0, sizeof(components)); + + /* get the halftone type */ + if (size < 2) + return_error(gs_error_rangecheck); + dht.type = (gs_halftone_type)(*data++); + num_dev_comps = dht.num_dev_comp = dht.num_comp = *data++; + size -= 2; + + /* process the component orders */ + for (i = 0, code = 0; i < num_dev_comps && code >= 0; i++) { + components[i].comp_number = i; + code = gx_ht_read_component(&components[i], data, size, mem); + if (code >= 0) { + size -= code; + data += code; + } + } + + /* if everything is OK, install the halftone */ + if (code >= 0) { + /* save since the 'install' copies the order, but then clears the source order */ + for (i = 0; i < num_dev_comps; i++) + components_save[i] = components[i]; + code = gx_imager_dev_ht_install(pis, &dht, dht.type, dev); + for (i = 0; i < num_dev_comps; i++) + gx_ht_order_release(&components_save[i].corder, mem, false); + } + + /* + * If installation failed, discard the allocated elements. We can't + * use the gx_device_halftone_release procedure, as the components + * array is on the stack rather than in the heap. + */ + if (code < 0) { + for (i = 0; i < num_dev_comps; i++) + gx_ht_order_release(&components[i].corder, mem, false); + } + + return code < 0 ? code : data - data0; +} |