home *** CD-ROM | disk | FTP | other *** search
- /* Copyright (C) 1989, 1992 Aladdin Enterprises. All rights reserved.
-
- This file is part of Ghostscript.
-
- Ghostscript is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
- to anyone for the consequences of using it or for whether it serves any
- particular purpose or works at all, unless he says so in writing. Refer
- to the Ghostscript General Public License for full details.
-
- Everyone is granted permission to copy, modify and redistribute
- Ghostscript, but only under the conditions described in the Ghostscript
- General Public License. A copy of this license is supposed to have been
- given to you along with Ghostscript so you can know your rights and
- responsibilities. It should be in a file named COPYING. Among other
- things, the copyright notice and this notice must be preserved on all
- copies. */
-
- /* gxht.c */
- /* Halftone rendering routines for Ghostscript imaging library */
- #include "memory_.h"
- #include "gx.h"
- #include "gserrors.h"
- #include "gxfixed.h"
- #include "gxmatrix.h" /* for gxdevice.h */
- #include "gzstate.h"
- #include "gzdevice.h"
- #include "gzcolor.h" /* requires gxdevice.h */
- #include "gzht.h"
-
- extern ulong gs_next_ids(P1(uint));
-
- /*
- * We don't want to remember all the values of the halftone screen,
- * because they would take up space proportional to P^3, where P is
- * the number of pixels in a cell. Instead, we pick some number N of
- * patterns to cache. Each cache slot covers a range of (P+1)/N
- * different gray levels: we "slide" the contents of the slot back and
- * forth within this range by incrementally adding and dropping 1-bits.
- * N>=0 (obviously); N<=P+1 (likewise); also, so that we can simplify things
- * by preallocating the bookkeeping information for the cache, we define
- * a constant max_cached_tiles which is an a priori maximum value for N.
- *
- * Note that the raster for each tile must be a multiple of 32 bits,
- * to satisfy the copy_mono device routine, even though a multiple of
- * 16 bits would otherwise be sufficient.
- */
-
- /*** Big memory machines ***/
- #define max_cached_tiles_LARGE 256
- #define max_ht_bits_LARGE 35000
- /*** Small memory machines ***/
- #define max_cached_tiles_SMALL 25
- #define max_ht_bits_SMALL 1000
-
- #if arch_ints_are_short
- # define max_cached_tiles max_cached_tiles_SMALL
- # define max_ht_bits max_ht_bits_SMALL
- #else
- # define max_cached_tiles max_cached_tiles_LARGE
- # define max_ht_bits max_ht_bits_LARGE
- #endif
-
- typedef struct bit_tile_s {
- int level; /* the cached gray level, i.e. */
- /* the number of spots whitened, */
- /* or -1 if the cache is empty */
- gx_bitmap tile; /* the currently rendered tile */
- } bit_tile;
- typedef struct gx_ht_cache_s {
- /* The following are set when the cache is created. */
- byte *bits; /* the base of the bits */
- uint bits_size; /* the space available for bits */
- /* The following are reset each time the cache is initialized */
- /* for a new screen. */
- ht_bit *order; /* the cached order vector */
- int num_cached; /* actual # of cached tiles */
- int levels_per_tile; /* # of levels per cached tile */
- bit_tile tiles[max_cached_tiles]; /* the cached tiles */
- gx_bitmap_id base_id; /* the base id, to which */
- /* we add the halftone level */
- } gx_ht_cache;
-
- /* Bit masks for whitening vector. The high-order byte always comes */
- /* first. We have to define the masks as bit16 to ensure that */
- /* they will be properly aligned in memory on machines that care. */
- typedef unsigned short bit16;
- #if arch_is_big_endian
- # define b2(hi,lo) (hi<<8)+lo
- #else
- # define b2(hi,lo) (lo<<8)+hi
- #endif
- private const bit16 single_bits[16] =
- { b2(0x80,0), b2(0x40,0), b2(0x20,0), b2(0x10,0),
- b2(8,0), b2(4,0), b2(2,0), b2(1,0),
- b2(0,0x80), b2(0,0x40), b2(0,0x20), b2(0,0x10),
- b2(0,8), b2(0,4), b2(0,2), b2(0,1)
- };
- private const bit16 mb1[1] =
- { b2(0xff,0xff) };
- private const bit16 mb2[2] =
- { b2(0xaa,0xaa), b2(0x55,0x55) };
- private const bit16 mb3[3] =
- { b2(0x92,0x49), b2(0x49,0x24), b2(0x24,0x92) };
- private const bit16 mb4[4] =
- { b2(0x88,0x88), b2(0x44,0x44), b2(0x22,0x22), b2(0x11,0x11) };
- private const bit16 mb5[5] =
- { b2(0x84,0x21), b2(0x42,0x10), b2(0x21,0x08), b2(0x10,0x84),
- b2(0x08,0x42)
- };
- private const bit16 mb6[6] =
- { b2(0x82,0x08), b2(0x41,0x04), b2(0x20,0x82), b2(0x10,0x41),
- b2(0x08,0x20), b2(0x04,0x10)
- };
- private const bit16 mb7[7] =
- { b2(0x81,0x02), b2(0x40,0x81), b2(0x20,0x40), b2(0x10,0x20),
- b2(0x08,0x10), b2(0x04,0x08), b2(0x02,0x04)
- };
- private const bit16 mb8[8] =
- { b2(0x80,0x80), b2(0x40,0x40), b2(0x20,0x20), b2(0x10,0x10),
- b2(0x08,0x08), b2(0x04,0x04), b2(0x02,0x02), b2(0x01,0x01)
- };
- #undef b2
- private const bit16 *multi_bits[9] =
- { 0, mb1, mb2, mb3, mb4, mb5, mb6, mb7, mb8
- };
-
- /* Allocate a halftone cache. */
- int
- gx_alloc_ht_cache(gs_state *pgs)
- { gs_proc_alloc_t palloc = pgs->memory_procs->alloc;
- gx_ht_cache *pcache =
- (gx_ht_cache *)(*palloc)(1, sizeof(gx_ht_cache),
- "alloc_ht_cache(struct)");
- byte *cbits = (byte *)(*palloc)(max_ht_bits, 1,
- "alloc_ht_cache(bits)");
- if ( pcache == 0 || cbits == 0 )
- return_error(gs_error_VMerror);
- pcache->bits = cbits;
- pcache->bits_size = max_ht_bits;
- pgs->ht_cache = pcache;
- return 0;
- }
-
- /* Construct the order vector. order is an array of ht_bits: */
- /* order[i].offset contains the index of the bit position */
- /* that is i'th in the whitening order. */
- int
- gx_ht_construct_order(ht_bit *order, int width, int height)
- { uint i;
- uint size = (uint)(width * height);
- int padding = (-width) & 31;
- if ( (width + padding) / 8 * height > max_ht_bits )
- return_error(gs_error_limitcheck); /* can't cache the rendering */
- /* Convert sequential indices to */
- /* byte indices and mask values. */
- for ( i = 0; i < size; i++ )
- { int pix = order[i].offset;
- pix += pix / width * padding;
- order[i].offset = (pix >> 4) << 1;
- order[i].mask =
- (width <= 8 ?
- multi_bits[width][pix & 15] :
- single_bits[pix & 15]);
- }
- #ifdef DEBUG
- if ( gs_debug['h'] )
- { dprintf1("[h]Halftone order %lx:\n", (ulong)order);
- for ( i = 0; i < size; i++ )
- dprintf3("%4d: %u:%x\n", i, order[i].offset,
- order[i].mask);
- }
- #endif
- return 0;
- }
-
- /* Install a new halftone in the graphics state. */
- void
- gx_ht_install(gs_state *pgs, const halftone_params *pht)
- { *pgs->halftone = *pht;
- /* Clear the cache, to avoid confusion in case */
- /* the address of a new order vector matches that of a */
- /* (deallocated) old one. */
- pgs->ht_cache->order = NULL;
- }
-
- /* Make the cache order current, and return whether */
- /* there is room for all possible tiles in the cache. */
- private void init_ht(P2(gx_ht_cache *, const halftone_params *));
- int
- gx_check_tile_cache(gs_state *pgs)
- { const halftone_params *pht = pgs->halftone;
- gx_ht_cache *pcache = pgs->ht_cache;
- if ( pcache->order != pht->order )
- init_ht(pcache, pht);
- return pcache->levels_per_tile == 1;
- }
-
- /* Determine whether a given (width, y, height) might fit into a */
- /* single tile. If so, return the byte offset of the appropriate row */
- /* from the beginning of the tile; if not, return -1. */
- int
- gx_check_tile_size(gs_state *pgs, int w, int y, int h)
- { int tsy;
- const gx_bitmap *ptile0 =
- &pgs->ht_cache->tiles[0].tile; /* a typical tile */
- #define tile0 (*ptile0)
- if ( h > tile0.rep_height || w > tile0.rep_width )
- return -1;
- tsy = (y + pgs->phase_mod.y) % tile0.rep_height;
- if ( tsy + h > tile0.size.y )
- return -1;
- /* Tile fits in Y, might fit in X. */
- return tsy * tile0.raster;
- #undef tile0
- }
-
- /* Load the device color into the halftone cache if needed. */
- private int render_ht(P4(bit_tile *, int, const halftone_params *, gx_bitmap_id));
- int
- gx_color_load(gx_device_color *pdevc, gs_state *pgs)
- { int level = pdevc->halftone_level;
- const halftone_params *pht;
- gx_ht_cache *pcache;
- bit_tile *bt;
- if ( level == 0 ) return 0; /* no halftone */
- pht = pgs->halftone;
- pcache = pgs->ht_cache;
- if ( pcache->order != pht->order )
- init_ht(pcache, pht);
- bt = &pcache->tiles[level / pcache->levels_per_tile];
- if ( bt->level != level )
- { int code = render_ht(bt, level, pht, pcache->base_id);
- if ( code < 0 ) return code;
- }
- pdevc->tile = &bt->tile;
- return 0;
- }
-
- /* Initialize the tile cache for a given screen. */
- /* Cache as many different levels as will fit. */
- private void
- init_ht(gx_ht_cache *pcache, const halftone_params *pht)
- { int width = pht->width;
- int height = pht->height;
- int size = width * height;
- static int up_to_16[] =
- /* up_to_16[i] = 16 / i * i */
- { 0, 16, 16, 15, 16, 15, 12, 14, 16 };
- int width_unit = (width <= 8 ? up_to_16[width] : width);
- int height_unit = height;
- uint raster = ((width + 31) >> 5) << 2;
- uint tile_bytes = raster * height;
- int num_cached;
- int i;
- byte *tbits = pcache->bits;
- /* Make sure num_cached is within bounds */
- num_cached = max_ht_bits / tile_bytes;
- if ( num_cached > size ) num_cached = size;
- if ( num_cached > max_cached_tiles ) num_cached = max_cached_tiles;
- if ( num_cached == size &&
- tile_bytes * num_cached <= max_ht_bits / 2
- )
- { /* We can afford to replicate every tile vertically, */
- /* which will reduce breakage when tiling. */
- height_unit <<= 1, tile_bytes <<= 1;
- }
- pcache->base_id = gs_next_ids(size);
- for ( i = 0; i < num_cached; i++ )
- { register bit_tile *bt = &pcache->tiles[i];
- bt->level = -1;
- bt->tile.data = tbits;
- bt->tile.raster = raster;
- bt->tile.size.x = width_unit;
- bt->tile.size.y = height_unit;
- bt->tile.rep_width = width;
- bt->tile.rep_height = height;
- tbits += tile_bytes;
- }
- pcache->order = pht->order;
- pcache->num_cached = num_cached;
- pcache->levels_per_tile = (size + num_cached - 1) / num_cached;
- }
-
- /*
- * 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.
- * If the tile whose range includes the desired level is already loaded,
- * we adjust it incrementally: this saves a lot of time for
- * the average image, where gray levels don't change abruptly.
- * Note that we will never be asked to cache levels 0 or order_size,
- * which correspond to black or white respectively.
- */
- private int
- render_ht(bit_tile *pbt, int level /* [1..order_size-1] */,
- const halftone_params *pht, gx_bitmap_id base_id)
- { ht_bit *order = pht->order;
- register ht_bit *p;
- register ht_bit *endp;
- register byte *bits = pbt->tile.data;
- int old_level = pbt->level;
- if ( old_level < 0 )
- { /* The cache is empty. Preload it with */
- /* whichever of all-0s and all-1s will be faster. */
- uint tile_bytes = pbt->tile.raster * pbt->tile.size.y;
- if ( level >= pht->order_size >> 1 )
- { old_level = pht->order_size;
- memset(bits, 0xff, tile_bytes);
- }
- else
- { old_level = 0;
- memset(bits, 0, tile_bytes);
- }
- }
- #ifdef DEBUG
- if ( level < 0 || level > pht->order_size || level == old_level )
- { lprintf3("Error in render_ht: level=%d, old_level=%d, order_size=%d=n", level, old_level, pht->order_size);
- return_error(gs_error_Fatal);
- }
- #endif
- /* Note that we can use the same loop to turn bits either */
- /* on or off, using xor. We use < to compare pointers, */
- /* rather than ==, because Turbo C only compares the */
- /* low 16 bits for < and > but compares all 32 bits for ==. */
- if ( level > old_level )
- p = &order[old_level], endp = &order[level];
- else
- p = &order[level], endp = &order[old_level];
- /* Invert bits between the two pointers */
- do
- { *(bit16 *)&bits[p->offset] ^= p->mask;
- }
- while ( ++p < endp );
- #ifdef DEBUG
- if ( gs_debug['h'] )
- { byte *p = bits;
- int wb = pbt->tile.raster;
- byte *ptr = bits + wb * pbt->tile.size.y;
- dprintf7("[h]Halftone cache %lx: old=%d, new=%d, w=%d(%d), h=%d(%d):\n",
- (ulong)bits, old_level, level, pbt->tile.size.x,
- pht->width, pbt->tile.size.y, pht->height);
- 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 - bits) % wb == 0 ) dputc('\n');
- }
- }
- #endif
- pbt->level = level;
- pbt->tile.id = base_id + level;
- if ( pbt->tile.size.y > pbt->tile.rep_height )
- { /* Replicate the tile in Y. We only do this when */
- /* all the renderings will fit in the cache, */
- /* so we only do it once per level, and it doesn't */
- /* have to be very efficient. */
- uint rh = pbt->tile.rep_height;
- uint h = pbt->tile.size.y;
- uint tsize = pbt->tile.raster * rh;
- do
- { memcpy(bits + tsize, bits, tsize);
- bits += tsize;
- }
- while ( (h -= rh) != 0 );
- }
- return 0;
- }
-