summaryrefslogtreecommitdiff
path: root/gs/base/gxht.c
diff options
context:
space:
mode:
Diffstat (limited to 'gs/base/gxht.c')
-rw-r--r--gs/base/gxht.c860
1 files changed, 860 insertions, 0 deletions
diff --git a/gs/base/gxht.c b/gs/base/gxht.c
new file mode 100644
index 000000000..d49730ee2
--- /dev/null
+++ b/gs/base/gxht.c
@@ -0,0 +1,860 @@
+/* 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$ */
+/* Halftone rendering for imaging library */
+#include "memory_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gsstruct.h"
+#include "gsbitops.h"
+#include "gsutil.h" /* for gs_next_ids */
+#include "gxdcolor.h"
+#include "gxfixed.h"
+#include "gxdevice.h" /* for gzht.h */
+#include "gxistate.h"
+#include "gzht.h"
+#include "gsserial.h"
+
+/* Define the binary halftone device color type. */
+/* The type descriptor must be public for Pattern types. */
+gs_public_st_composite(st_dc_ht_binary, gx_device_color, "dc_ht_binary",
+ dc_ht_binary_enum_ptrs, dc_ht_binary_reloc_ptrs);
+static dev_color_proc_save_dc(gx_dc_ht_binary_save_dc);
+static dev_color_proc_get_dev_halftone(gx_dc_ht_binary_get_dev_halftone);
+static dev_color_proc_load(gx_dc_ht_binary_load);
+static dev_color_proc_fill_rectangle(gx_dc_ht_binary_fill_rectangle);
+static dev_color_proc_fill_masked(gx_dc_ht_binary_fill_masked);
+static dev_color_proc_equal(gx_dc_ht_binary_equal);
+static dev_color_proc_write(gx_dc_ht_binary_write);
+static dev_color_proc_read(gx_dc_ht_binary_read);
+const gx_device_color_type_t
+ gx_dc_type_data_ht_binary =
+{&st_dc_ht_binary,
+ gx_dc_ht_binary_save_dc, gx_dc_ht_binary_get_dev_halftone,
+ gx_dc_ht_get_phase,
+ gx_dc_ht_binary_load, gx_dc_ht_binary_fill_rectangle,
+ gx_dc_ht_binary_fill_masked, gx_dc_ht_binary_equal,
+ gx_dc_ht_binary_write, gx_dc_ht_binary_read,
+ gx_dc_ht_binary_get_nonzero_comps
+};
+
+#undef gx_dc_type_ht_binary
+const gx_device_color_type_t *const gx_dc_type_ht_binary =
+&gx_dc_type_data_ht_binary;
+
+#define gx_dc_type_ht_binary (&gx_dc_type_data_ht_binary)
+/* GC procedures */
+static
+ENUM_PTRS_WITH(dc_ht_binary_enum_ptrs, gx_device_color *cptr) return 0;
+ENUM_PTR(0, gx_device_color, colors.binary.b_ht);
+case 1:
+{
+ gx_ht_tile *tile = cptr->colors.binary.b_tile;
+
+ ENUM_RETURN(tile ? tile - tile->index : 0);
+}
+ENUM_PTRS_END
+static RELOC_PTRS_WITH(dc_ht_binary_reloc_ptrs, gx_device_color *cptr)
+{
+ gx_ht_tile *tile = cptr->colors.binary.b_tile;
+ uint index = tile ? tile->index : 0;
+
+ RELOC_PTR(gx_device_color, colors.binary.b_ht);
+ RELOC_TYPED_OFFSET_PTR(gx_device_color, colors.binary.b_tile, index);
+}
+RELOC_PTRS_END
+#undef cptr
+
+/* Other GC procedures */
+private_st_ht_tiles();
+static
+ENUM_PTRS_BEGIN_PROC(ht_tiles_enum_ptrs)
+{
+ return 0;
+}
+ENUM_PTRS_END_PROC
+static RELOC_PTRS_BEGIN(ht_tiles_reloc_ptrs)
+{
+ /* Reset the bitmap pointers in the tiles. */
+ /* We know the first tile points to the base of the bits. */
+ gx_ht_tile *ht_tiles = vptr;
+ byte *bits = ht_tiles->tiles.data;
+ uint diff;
+
+ if (bits == 0)
+ return;
+ RELOC_VAR(bits);
+ if (size == size_of(gx_ht_tile)) { /* only 1 tile */
+ ht_tiles->tiles.data = bits;
+ return;
+ }
+ diff = ht_tiles[1].tiles.data - ht_tiles[0].tiles.data;
+ for (; size; ht_tiles++, size -= size_of(gx_ht_tile), bits += diff) {
+ ht_tiles->tiles.data = bits;
+ }
+}
+RELOC_PTRS_END
+private_st_ht_cache();
+
+/* Return the default sizes of the halftone cache. */
+uint
+gx_ht_cache_default_tiles(void)
+{
+ return (gs_debug_c('.') ? max_ht_cached_tiles_SMALL :
+ max_ht_cached_tiles);
+}
+uint
+gx_ht_cache_default_bits_size(void)
+{
+ return (gs_debug_c('.') ? max_ht_cache_bits_size_SMALL :
+ max_ht_cache_bits_size);
+}
+
+/* Allocate a halftone cache. max_bits_size is number of bytes */
+gx_ht_cache *
+gx_ht_alloc_cache(gs_memory_t * mem, uint max_tiles, uint max_bits_size)
+{
+ gx_ht_cache *pcache =
+ gs_alloc_struct(mem, gx_ht_cache, &st_ht_cache,
+ "alloc_ht_cache(struct)");
+ byte *tbits =
+ gs_alloc_bytes(mem, max_bits_size, "alloc_ht_cache(bits)");
+ gx_ht_tile *ht_tiles =
+ gs_alloc_struct_array(mem, max_tiles, gx_ht_tile, &st_ht_tiles,
+ "alloc_ht_cache(ht_tiles)");
+
+ if (pcache == 0 || tbits == 0 || ht_tiles == 0) {
+ gs_free_object(mem, ht_tiles, "alloc_ht_cache(ht_tiles)");
+ gs_free_object(mem, tbits, "alloc_ht_cache(bits)");
+ gs_free_object(mem, pcache, "alloc_ht_cache(struct)");
+ return 0;
+ }
+ pcache->bits = tbits;
+ pcache->bits_size = max_bits_size;
+ pcache->ht_tiles = ht_tiles;
+ pcache->num_tiles = max_tiles;
+ pcache->order.cache = pcache;
+ pcache->order.transfer = 0;
+ gx_ht_clear_cache(pcache);
+ return pcache;
+}
+
+/* Free a halftone cache. */
+void
+gx_ht_free_cache(gs_memory_t * mem, gx_ht_cache * pcache)
+{
+ gs_free_object(mem, pcache->ht_tiles, "free_ht_cache(ht_tiles)");
+ gs_free_object(mem, pcache->bits, "free_ht_cache(bits)");
+ gs_free_object(mem, pcache, "free_ht_cache(struct)");
+}
+
+/* Check whether the tile cache corresponds to the current order */
+bool
+gx_check_tile_cache_current(const gs_imager_state * pis)
+{
+ /* TO_DO_DEVICEN - this routine is no longer used - delete. */
+ return false;
+}
+
+/* Make the cache order current, and return whether */
+/* there is room for all possible tiles in the cache. */
+bool
+gx_check_tile_cache(const gs_imager_state * pis)
+{
+ /* TO_DO_DEVICEN - this routine is no longer used - delete. */
+ return false;
+}
+
+/*
+ * Determine whether a given (width, y, height) might fit into a single
+ * (non-strip) tile. If so, return the byte offset of the appropriate row
+ * from the beginning of the tile, and set *ppx to the x phase offset
+ * within the tile; if not, return -1.
+ *
+ * This routine cannot be supported in the DeviceN code.
+ */
+int
+gx_check_tile_size(const gs_imager_state * pis, int w, int y, int h,
+ gs_color_select_t select, int *ppx)
+{
+ /* TO_DO_DEVICEN - this routine is no longer used - delete. */
+ return -1;
+}
+
+/* Render a given level into a halftone cache. */
+static int render_ht(gx_ht_tile *, int, const gx_ht_order *,
+ gx_bitmap_id);
+static gx_ht_tile *
+gx_render_ht_default(gx_ht_cache * pcache, int b_level)
+{
+ const gx_ht_order *porder = &pcache->order;
+ int level = porder->levels[b_level];
+ gx_ht_tile *bt;
+
+ if (pcache->num_cached < porder->num_levels )
+ bt = &pcache->ht_tiles[level / pcache->levels_per_tile];
+ else
+ bt = &pcache->ht_tiles[b_level]; /* one tile per b_level */
+
+ if (bt->level != level) {
+ int code = render_ht(bt, level, porder, pcache->base_id + b_level);
+
+ if (code < 0)
+ return 0;
+ }
+ return bt;
+}
+/* Faster code if num_tiles == 1. */
+static gx_ht_tile *
+gx_render_ht_1_tile(gx_ht_cache * pcache, int b_level)
+{
+ const gx_ht_order *porder = &pcache->order;
+ int level = porder->levels[b_level];
+ gx_ht_tile *bt = &pcache->ht_tiles[0];
+
+ if (bt->level != level) {
+ int code = render_ht(bt, level, porder, pcache->base_id + b_level);
+
+ if (code < 0)
+ return 0;
+ }
+ return bt;
+}
+/* Faster code if levels_per_tile == 1. */
+static gx_ht_tile *
+gx_render_ht_1_level(gx_ht_cache * pcache, int b_level)
+{
+ const gx_ht_order *porder = &pcache->order;
+ int level = porder->levels[b_level];
+ gx_ht_tile *bt;
+
+ if (pcache->num_cached < porder->num_levels )
+ bt = &pcache->ht_tiles[level / pcache->levels_per_tile];
+ else
+ bt = &pcache->ht_tiles[b_level]; /* one tile per b_level */
+
+ if (bt->level != level) {
+ int code = render_ht(bt, level, porder, pcache->base_id + b_level);
+
+ if (code < 0)
+ return 0;
+ }
+ return bt;
+}
+
+/* save information about the operand binary halftone color */
+static void
+gx_dc_ht_binary_save_dc(const gx_device_color * pdevc,
+ gx_device_color_saved * psdc)
+{
+ psdc->type = pdevc->type;
+ psdc->colors.binary.b_color[0] = pdevc->colors.binary.color[0];
+ psdc->colors.binary.b_color[1] = pdevc->colors.binary.color[1];
+ psdc->colors.binary.b_level = pdevc->colors.binary.b_level;
+ psdc->colors.binary.b_index = pdevc->colors.binary.b_index;
+ psdc->phase = pdevc->phase;
+}
+
+/* get the halftone used for a binary halftone color */
+static const gx_device_halftone *
+gx_dc_ht_binary_get_dev_halftone(const gx_device_color * pdevc)
+{
+ return pdevc->colors.binary.b_ht;
+}
+
+/* Load the device color into the halftone cache if needed. */
+static int
+gx_dc_ht_binary_load(gx_device_color * pdevc, const gs_imager_state * pis,
+ gx_device * dev, gs_color_select_t select)
+{
+ int component_index = pdevc->colors.binary.b_index;
+ const gx_ht_order *porder =
+ (component_index < 0 ?
+ &pdevc->colors.binary.b_ht->order :
+ &pdevc->colors.binary.b_ht->components[component_index].corder);
+ gx_ht_cache *pcache = porder->cache;
+
+ if (pcache->order.bit_data != porder->bit_data)
+ gx_ht_init_cache(pis->memory, pcache, porder);
+ /*
+ * We do not load the cache now. Instead we wait until we are ready
+ * to actually render the color. This allows multiple colors to be
+ * loaded without cache conflicts. (Cache conflicts can occur when
+ * if two device colors use the same cache elements. This can occur
+ * when the tile size is large enough that we do not have a separate
+ * tile for each half tone level.) See gx_dc_ht_binary_load_cache.
+ */
+ pdevc->colors.binary.b_tile = NULL;
+ return 0;
+}
+
+/*
+ * Load the half tone tile in the halftone cache.
+ */
+static int
+gx_dc_ht_binary_load_cache(const gx_device_color * pdevc)
+{
+ int component_index = pdevc->colors.binary.b_index;
+ const gx_ht_order *porder =
+ &pdevc->colors.binary.b_ht->components[component_index].corder;
+ gx_ht_cache *pcache = porder->cache;
+ int b_level = pdevc->colors.binary.b_level;
+ int level = porder->levels[b_level];
+ gx_ht_tile *bt;
+
+ if (pcache->num_cached < porder->num_levels )
+ bt = &pcache->ht_tiles[level / pcache->levels_per_tile];
+ else
+ bt = &pcache->ht_tiles[b_level]; /* one tile per b_level */
+
+ if (bt->level != level) {
+ int code = render_ht(bt, level, porder, pcache->base_id + b_level);
+
+ if (code < 0)
+ return_error(gs_error_Fatal);
+ }
+ ((gx_device_color *)pdevc)->colors.binary.b_tile = bt;
+ return 0;
+}
+
+/* Fill a rectangle with a binary halftone. */
+/* Note that we treat this as "texture" for RasterOp. */
+static int
+gx_dc_ht_binary_fill_rectangle(const gx_device_color * pdevc, int x, int y,
+ int w, int h, gx_device * dev, gs_logical_operation_t lop,
+ const gx_rop_source_t * source)
+{
+ gx_rop_source_t no_source;
+
+ /* Load the halftone cache for the color */
+ gx_dc_ht_binary_load_cache(pdevc);
+ /*
+ * Observation of H-P devices and documentation yields confusing
+ * evidence about whether white pixels in halftones are always
+ * opaque. It appears that for black-and-white devices, these
+ * pixels are *not* opaque.
+ */
+ if (dev->color_info.depth > 1)
+ lop &= ~lop_T_transparent;
+ if (source == NULL && lop_no_S_is_T(lop))
+ return (*dev_proc(dev, strip_tile_rectangle)) (dev,
+ &pdevc->colors.binary.b_tile->tiles,
+ x, y, w, h, pdevc->colors.binary.color[0],
+ pdevc->colors.binary.color[1],
+ pdevc->phase.x, pdevc->phase.y);
+ /* Adjust the logical operation per transparent colors. */
+ if (pdevc->colors.binary.color[0] == gx_no_color_index)
+ lop = rop3_use_D_when_T_0(lop);
+ if (pdevc->colors.binary.color[1] == gx_no_color_index)
+ lop = rop3_use_D_when_T_1(lop);
+ if (source == NULL)
+ set_rop_no_source(source, no_source, dev);
+ return (*dev_proc(dev, strip_copy_rop)) (dev, source->sdata,
+ source->sourcex, source->sraster, source->id,
+ (source->use_scolors ? source->scolors : NULL),
+ &pdevc->colors.binary.b_tile->tiles,
+ pdevc->colors.binary.color,
+ x, y, w, h, pdevc->phase.x, pdevc->phase.y,
+ lop);
+}
+
+static int
+gx_dc_ht_binary_fill_masked(const gx_device_color * pdevc, const byte * data,
+ int data_x, int raster, gx_bitmap_id id, int x, int y, int w, int h,
+ gx_device * dev, gs_logical_operation_t lop, bool invert)
+{
+ /*
+ * Load the halftone cache for the color. We do not do it earlier
+ * because for small halftone caches, each cache tile may be used for
+ * for more than one halftone level. This can cause conflicts if more
+ * than one device color has been set and they use the same cache
+ * entry.
+ */
+ int code = gx_dc_ht_binary_load_cache(pdevc);
+
+ if (code < 0)
+ return code;
+ return gx_dc_default_fill_masked(pdevc, data, data_x, raster, id,
+ x, y, w, h, dev, lop, invert);
+}
+
+/* Compare two binary halftones for equality. */
+static bool
+gx_dc_ht_binary_equal(const gx_device_color * pdevc1,
+ const gx_device_color * pdevc2)
+{
+ return pdevc2->type == pdevc1->type &&
+ pdevc1->phase.x == pdevc2->phase.x &&
+ pdevc1->phase.y == pdevc2->phase.y &&
+ gx_dc_binary_color0(pdevc1) == gx_dc_binary_color0(pdevc2) &&
+ gx_dc_binary_color1(pdevc1) == gx_dc_binary_color1(pdevc2) &&
+ pdevc1->colors.binary.b_level == pdevc2->colors.binary.b_level;
+}
+
+
+/*
+ * Flags to indicate the pieces of a binary halftone that are included
+ * in its string representation. The first byte of the string holds this
+ * set of flags.
+ *
+ * The binary halftone tile is never transmitted as part of the string
+ * representation, so there is also no flag bit for it.
+ */
+static const int dc_ht_binary_has_color0 = 0x01;
+static const int dc_ht_binary_has_color1 = 0x02;
+static const int dc_ht_binary_has_level = 0x04;
+static const int dc_ht_binary_has_index = 0x08;
+
+
+/*
+ * Serialize a binany halftone device color.
+ *
+ * Operands:
+ *
+ * pdevc pointer to device color to be serialized
+ *
+ * psdc pointer ot saved version of last serialized color (for
+ * this band)
+ *
+ * dev pointer to the current device, used to retrieve process
+ * color model information
+ *
+ * pdata pointer to buffer in which to write the data
+ *
+ * psize pointer to a location that, on entry, contains the size of
+ * the buffer pointed to by pdata; on return, the size of
+ * the data required or actually used will be written here.
+ *
+ * Returns:
+ * 1, with *psize set to 0, if *psdc and *pdevc represent the same color
+ *
+ * 0, with *psize set to the amount of data written, if everything OK
+ *
+ * gs_error_rangecheck, with *psize set to the size of buffer required,
+ * if *psize was not large enough
+ *
+ * < 0, != gs_error_rangecheck, in the event of some other error; in this
+ * case *psize is not changed.
+ */
+static int
+gx_dc_ht_binary_write(
+ const gx_device_color * pdevc,
+ const gx_device_color_saved * psdc0,
+ const gx_device * dev,
+ uint offset,
+ byte * pdata,
+ uint * psize )
+{
+ int req_size = 1; /* flag bits */
+ int flag_bits = 0;
+ uint tmp_size;
+ byte * pdata0 = pdata;
+ const gx_device_color_saved * psdc = psdc0;
+ int code;
+
+ if (offset != 0)
+ return_error(gs_error_unregistered); /* Not implemented yet. */
+
+ /* check if operand and saved colors are the same type */
+ if (psdc != 0 && psdc->type != pdevc->type)
+ psdc = 0;
+
+ /* check for the information that must be transmitted */
+ if ( psdc == 0 ||
+ pdevc->colors.binary.color[0] != psdc->colors.binary.b_color[0] ) {
+ flag_bits |= dc_ht_binary_has_color0;
+ tmp_size = 0;
+ (void)gx_dc_write_color( pdevc->colors.binary.color[0],
+ dev,
+ pdata,
+ &tmp_size );
+ req_size += tmp_size;
+ }
+ if ( psdc == 0 ||
+ pdevc->colors.binary.color[1] != psdc->colors.binary.b_color[1] ) {
+ flag_bits |= dc_ht_binary_has_color1;
+ tmp_size = 0;
+ (void)gx_dc_write_color( pdevc->colors.binary.color[1],
+ dev,
+ pdata,
+ &tmp_size );
+ req_size += tmp_size;
+ }
+
+ if ( psdc == 0 ||
+ pdevc->colors.binary.b_level != psdc->colors.binary.b_level ) {
+ flag_bits |= dc_ht_binary_has_level;
+ req_size += enc_u_sizew(pdevc->colors.binary.b_level);
+ }
+
+ if ( psdc == 0 ||
+ pdevc->colors.binary.b_index != psdc->colors.binary.b_index ) {
+ flag_bits |= dc_ht_binary_has_index;
+ req_size += 1;
+ }
+
+ /* check if there is anything to be done */
+ if (flag_bits == 0) {
+ *psize = 0;
+ return 1;
+ }
+
+ /* check if sufficient space has been provided */
+ if (req_size > *psize) {
+ *psize = req_size;
+ return gs_error_rangecheck;
+ }
+
+ /* write out the flag byte */
+ *pdata++ = (byte)flag_bits;
+
+ /* write out such other parts of the device color as are required */
+ if ((flag_bits & dc_ht_binary_has_color0) != 0) {
+ tmp_size = req_size - (pdata - pdata0);
+ code = gx_dc_write_color( pdevc->colors.binary.color[0],
+ dev,
+ pdata,
+ &tmp_size );
+ if (code < 0)
+ return code;
+ pdata += tmp_size;
+ }
+ if ((flag_bits & dc_ht_binary_has_color1) != 0) {
+ tmp_size = req_size - (pdata - pdata0);
+ code = gx_dc_write_color( pdevc->colors.binary.color[1],
+ dev,
+ pdata,
+ &tmp_size );
+ if (code < 0)
+ return code;
+ pdata += tmp_size;
+ }
+ if ((flag_bits & dc_ht_binary_has_level) != 0)
+ enc_u_putw(pdevc->colors.binary.b_level, pdata);
+ if ((flag_bits & dc_ht_binary_has_index) != 0)
+ *pdata++ = pdevc->colors.binary.b_index;
+
+ *psize = pdata - pdata0;
+ return 0;
+}
+
+/*
+ * Reconstruct a binary halftone device color from its serial representation.
+ *
+ * Operands:
+ *
+ * pdevc pointer to the location in which to write the
+ * reconstructed device color
+ *
+ * pis pointer to the current imager state (to access the
+ * current halftone)
+ *
+ * prior_devc pointer to the current device color (this is provided
+ * separately because the device color is not part of the
+ * imager state)
+ *
+ * dev pointer to the current device, used to retrieve process
+ * color model information
+ *
+ * pdata pointer to the buffer to be read
+ *
+ * size size of the buffer to be read; this should be large
+ * enough to hold the entire color description
+ *
+ * mem pointer to the memory to be used for allocations
+ * (ignored here)
+ *
+ * Returns:
+ *
+ * # of bytes read if everthing OK, < 0 in the event of an error
+ */
+static int
+gx_dc_ht_binary_read(
+ gx_device_color * pdevc,
+ const gs_imager_state * pis,
+ const gx_device_color * prior_devc,
+ const gx_device * dev, /* ignored */
+ uint offset,
+ const byte * pdata,
+ uint size,
+ gs_memory_t * mem ) /* ignored */
+{
+ gx_device_color devc;
+ const byte * pdata0 = pdata;
+ int code, flag_bits;
+
+ if (offset != 0)
+ return_error(gs_error_unregistered); /* Not implemented yet. */
+
+ /* if prior information is available, use it */
+ if (prior_devc != 0 && prior_devc->type == gx_dc_type_ht_binary)
+ devc = *prior_devc;
+ else
+ memset(&devc, 0, sizeof(devc)); /* clear pointers */
+ devc.type = gx_dc_type_ht_binary;
+
+ /* the halftone is always taken from the imager state */
+ devc.colors.binary.b_ht = pis->dev_ht;
+
+ /* cache is not porvided until the device color is used */
+ devc.colors.binary.b_tile = 0;
+
+ /* verify the minimum amount of information */
+ if (size == 0)
+ return_error(gs_error_rangecheck);
+ size --;
+ flag_bits = *pdata++;
+
+ /* read the other information provided */
+ if ((flag_bits & dc_ht_binary_has_color0) != 0) {
+ code = gx_dc_read_color( &devc.colors.binary.color[0],
+ dev,
+ pdata,
+ size );
+ if (code < 0)
+ return code;
+ size -= code;
+ pdata += code;
+ }
+ if ((flag_bits & dc_ht_binary_has_color1) != 0) {
+ code = gx_dc_read_color( &devc.colors.binary.color[1],
+ dev,
+ pdata,
+ size );
+ if (code < 0)
+ return code;
+ size -= code;
+ pdata += code;
+ }
+ if ((flag_bits & dc_ht_binary_has_level) != 0) {
+ const byte * pdata_start = pdata;
+
+ if (size < 1)
+ return_error(gs_error_rangecheck);
+ enc_u_getw(devc.colors.binary.b_level, pdata);
+ size -= pdata - pdata_start;
+ }
+ if ((flag_bits & dc_ht_binary_has_index) != 0) {
+ if (size == 0)
+ return_error(gs_error_rangecheck);
+ --size;
+ devc.colors.binary.b_index = *pdata++;
+ }
+
+ if (pis->dev_ht == NULL)
+ return_error(gs_error_unregistered); /* Must not happen. */
+ /* set the phase as required (select value is arbitrary) */
+ color_set_phase_mod( &devc,
+ pis->screen_phase[0].x,
+ pis->screen_phase[0].y,
+ pis->dev_ht->lcm_width,
+ pis->dev_ht->lcm_height );
+
+ /* everything looks good */
+ *pdevc = devc;
+ return pdata - pdata0;
+}
+
+
+/*
+ * Get the nonzero components of a binary halftone. This is used to
+ * distinguish components that are given zero intensity due to halftoning
+ * from those for which the original color intensity was in fact zero.
+ *
+ * Since this device color type involves only a single halftone component,
+ * we can reasonably assume that b_level != 0. Hence, we need to check
+ * for components with identical intensities in color[0] and color[1].
+ */
+int
+gx_dc_ht_binary_get_nonzero_comps(
+ const gx_device_color * pdevc,
+ const gx_device * dev,
+ gx_color_index * pcomp_bits )
+{
+ int code;
+ gx_color_value cvals_0[GX_DEVICE_COLOR_MAX_COMPONENTS],
+ cvals_1[GX_DEVICE_COLOR_MAX_COMPONENTS];
+
+ if ( (code = dev_proc(dev, decode_color)( (gx_device *)dev,
+ pdevc->colors.binary.color[0],
+ cvals_0 )) >= 0 &&
+ (code = dev_proc(dev, decode_color)( (gx_device *)dev,
+ pdevc->colors.binary.color[1],
+ cvals_1 )) >= 0 ) {
+ int i, ncomps = dev->color_info.num_components;
+ int mask = 0x1, comp_bits = 0;
+
+ for (i = 0; i < ncomps; i++, mask <<= 1) {
+ if (cvals_0[i] != 0 || cvals_1[i] != 0)
+ comp_bits |= mask;
+ }
+ *pcomp_bits = comp_bits;
+ code = 0;
+ }
+
+ return code;
+}
+
+
+/* Initialize the tile cache for a given screen. */
+/* Cache as many different levels as will fit. */
+void
+gx_ht_init_cache(const gs_memory_t *mem, gx_ht_cache * pcache, const gx_ht_order * porder)
+{
+ uint width = porder->width;
+ uint height = porder->height;
+ uint size = width * height + 1;
+ int width_unit =
+ (width <= ht_mask_bits / 2 ? ht_mask_bits / width * width :
+ width);
+ int height_unit = height;
+ uint raster = porder->raster;
+ uint tile_bytes = raster * height;
+ uint shift = porder->shift;
+ int num_cached;
+ int i;
+ byte *tbits = pcache->bits;
+
+ /* Non-monotonic halftones may have more bits than size. */
+ if (porder->num_bits >= size)
+ size = porder->num_bits + 1;
+ /* Make sure num_cached is within bounds */
+ num_cached = pcache->bits_size / tile_bytes;
+ if (num_cached > size)
+ num_cached = size;
+ if (num_cached > pcache->num_tiles)
+ num_cached = pcache->num_tiles;
+ if (num_cached == size &&
+ tile_bytes * num_cached <= pcache->bits_size / 2
+ ) {
+ /*
+ * We can afford to replicate every tile in the cache,
+ * which will reduce breakage when tiling. Since
+ * horizontal breakage is more expensive than vertical,
+ * and since wide shallow fills are more common than
+ * narrow deep fills, we replicate the tile horizontally.
+ * We do have to be careful not to replicate the tile
+ * to an absurdly large size, however.
+ */
+ uint rep_raster =
+ ((pcache->bits_size / num_cached) / height) &
+ ~(align_bitmap_mod - 1);
+ uint rep_count = rep_raster * 8 / width;
+
+ /*
+ * There's no real value in replicating the tile
+ * beyond the point where the byte width of the replicated
+ * tile is a multiple of a long.
+ */
+ if (rep_count > sizeof(ulong) * 8)
+ rep_count = sizeof(ulong) * 8;
+ width_unit = width * rep_count;
+ raster = bitmap_raster(width_unit);
+ tile_bytes = raster * height;
+ }
+ pcache->base_id = gs_next_ids(mem, porder->num_levels + 1);
+ pcache->order = *porder;
+ /* The transfer function is irrelevant, and might become dangling. */
+ pcache->order.transfer = 0;
+ pcache->num_cached = num_cached;
+ pcache->levels_per_tile = (size + num_cached - 1) / num_cached;
+ pcache->tiles_fit = -1;
+ memset(tbits, 0, pcache->bits_size);
+ for (i = 0; i < num_cached; i++, tbits += tile_bytes) {
+ register gx_ht_tile *bt = &pcache->ht_tiles[i];
+
+ bt->level = 0;
+ bt->index = i;
+ bt->tiles.data = tbits;
+ bt->tiles.raster = raster;
+ bt->tiles.size.x = width_unit;
+ bt->tiles.size.y = height_unit;
+ bt->tiles.rep_width = width;
+ bt->tiles.rep_height = height;
+ bt->tiles.shift = bt->tiles.rep_shift = shift;
+ }
+ pcache->render_ht =
+ (pcache->num_tiles == 1 ? gx_render_ht_1_tile :
+ pcache->levels_per_tile == 1 ? gx_render_ht_1_level :
+ gx_render_ht_default);
+}
+
+/*
+ * Compute and save the rendering of a given gray level
+ * with the current halftone. The cache holds multiple tiles,
+ * where each tile covers a range of possible levels.
+ * We adjust the tile whose range includes the desired level incrementally;
+ * this saves a lot of time for the average image, where gray levels
+ * don't change abruptly. Note that the "level" is the number of bits,
+ * not the index in the levels vector.
+ */
+static int
+render_ht(gx_ht_tile * pbt, int level /* [1..num_bits-1] */ ,
+ const gx_ht_order * porder, gx_bitmap_id new_id)
+{
+ byte *data = pbt->tiles.data;
+ int code;
+
+ if_debug7('H', "[H]Halftone cache slot 0x%lx: old=%d, new=%d, w=%d(%d), h=%d(%d):\n",
+ (ulong) data, pbt->level, level,
+ pbt->tiles.size.x, porder->width,
+ pbt->tiles.size.y, porder->num_bits / porder->width);
+#ifdef DEBUG
+ if (level < 0 || level > porder->num_bits) {
+ lprintf3("Error in render_ht: level=%d, old level=%d, num_bits=%d\n",
+ level, pbt->level, porder->num_bits);
+ return_error(gs_error_Fatal);
+ }
+#endif
+ code = porder->procs->render(pbt, level, porder);
+ if (code < 0)
+ return code;
+ pbt->level = level;
+ pbt->tiles.id = new_id;
+ /*
+ * Check whether we want to replicate the tile in the cache.
+ * Since we only do this when all the renderings will fit
+ * in the cache, we only do it once per level, and it doesn't
+ * have to be very efficient.
+ */
+ /****** TEST IS WRONG if width > rep_width but tile.raster ==
+ ****** order raster.
+ ******/
+ if (pbt->tiles.raster > porder->raster)
+ bits_replicate_horizontally(data, pbt->tiles.rep_width,
+ pbt->tiles.rep_height, porder->raster,
+ pbt->tiles.size.x, pbt->tiles.raster);
+ if (pbt->tiles.size.y > pbt->tiles.rep_height &&
+ pbt->tiles.shift == 0
+ )
+ bits_replicate_vertically(data, pbt->tiles.rep_height,
+ pbt->tiles.raster, pbt->tiles.size.y);
+#ifdef DEBUG
+ if (gs_debug_c('H')) {
+ const byte *p = pbt->tiles.data;
+ int wb = pbt->tiles.raster;
+ const byte *ptr = p + wb * pbt->tiles.size.y;
+
+ while (p < ptr) {
+ dprintf8(" %d%d%d%d%d%d%d%d",
+ *p >> 7, (*p >> 6) & 1, (*p >> 5) & 1,
+ (*p >> 4) & 1, (*p >> 3) & 1, (*p >> 2) & 1,
+ (*p >> 1) & 1, *p & 1);
+ if ((++p - data) % wb == 0)
+ dputc('\n');
+ }
+ }
+#endif
+ return 0;
+}