home *** CD-ROM | disk | FTP | other *** search
- /* Copyright (C) 1989, 1992, 1993 Aladdin Enterprises. All rights reserved.
-
- This file is part of Aladdin Ghostscript.
-
- Aladdin Ghostscript is distributed with NO WARRANTY OF ANY KIND. No author
- or distributor accepts any responsibility for the consequences of using it,
- or for whether it serves any particular purpose or works at all, unless he
- or she says so in writing. Refer to the Aladdin Ghostscript Free Public
- License (the "License") for full details.
-
- Every copy of Aladdin Ghostscript must include a copy of the License,
- normally in a plain ASCII text file named PUBLIC. The License grants you
- the right to copy, modify and redistribute Aladdin Ghostscript, but only
- under certain conditions described in the License. Among other things, the
- License requires that the copyright notice and this notice be preserved on
- all copies.
- */
-
- /* gsht.c */
- /* Halftone operators for Ghostscript library */
- #include "memory_.h"
- #include <stdlib.h> /* for qsort */
- #include "gx.h"
- #include "gserrors.h"
- #include "gsstruct.h"
- #include "gzstate.h"
- #include "gxdevice.h" /* for gzht.h */
- #include "gzht.h"
-
- /*** Big memory machines ***/
- #define max_tile_bytes_LARGE 4096
- /*** Small memory machines ***/
- #define max_tile_bytes_SMALL 512
-
- #if arch_ints_are_short
- # define max_tile_cache_bytes max_tile_bytes_SMALL
- #else
- # define max_tile_cache_bytes max_tile_bytes_LARGE
- #endif
-
- /* Forward declarations */
- private int process_threshold(P3(gx_ht_order *, gs_state *,
- gs_threshold_halftone *));
- private int process_screen(P3(gs_screen_enum *, gs_state *,
- gs_screen_halftone *));
- private void gx_sort_ht_order(P2(gx_ht_bit *, uint));
-
- /* Structure types */
- public_st_ht_order();
- private_st_ht_order_component();
- private_st_ht_order_comp_element();
- public_st_halftone();
- public_st_device_halftone();
-
- /* GC procedures */
-
- #define hptr ((gs_halftone *)vptr)
-
- private ENUM_PTRS_BEGIN(halftone_enum_ptrs) return 0;
- case 0:
- switch ( hptr->type )
- {
- case ht_type_threshold:
- *pep = (void *)hptr->params.threshold.thresholds; break; /* discard const */
- case ht_type_multiple:
- *pep = hptr->params.multiple.components; break;
- default:
- return 0;
- }
- break;
- ENUM_PTRS_END
-
- private RELOC_PTRS_BEGIN(halftone_reloc_ptrs) {
- switch ( hptr->type )
- {
- case ht_type_threshold:
- RELOC_PTR(gs_halftone, params.threshold.thresholds);
- break;
- case ht_type_multiple:
- RELOC_PTR(gs_halftone, params.multiple.components);
- break;
- case ht_type_none:
- case ht_type_screen:
- case ht_type_colorscreen:
- case ht_type_spot:
- break;
- }
- } RELOC_PTRS_END
-
- #undef hptr
-
- /* setscreen */
- int
- gs_setscreen(gs_state *pgs, gs_screen_halftone *phsp)
- { gs_screen_enum senum;
- int code = process_screen(&senum, pgs, phsp);
- if ( code < 0 )
- return code;
- return gs_screen_install(&senum);
- }
-
- /* currentscreen */
- int
- gs_currentscreen(gs_state *pgs, gs_screen_halftone *phsp)
- { switch ( pgs->halftone->type )
- {
- case ht_type_screen:
- *phsp = pgs->halftone->params.screen;
- return 0;
- case ht_type_colorscreen:
- *phsp = pgs->halftone->params.colorscreen.screens.colored.gray;
- return 0;
- default:
- return_error(gs_error_undefined);
- }
- }
-
- /* setcolorscreen */
- int
- gs_setcolorscreen(gs_state *pgs, gs_colorscreen_halftone *pht)
- { gs_halftone ht;
- ht.type = ht_type_colorscreen;
- ht.params.colorscreen = *pht;
- return gs_sethalftone(pgs, &ht);
- }
-
- /* currentcolorscreen */
- int
- gs_currentcolorscreen(gs_state *pgs, gs_colorscreen_halftone *pht)
- { int code;
- switch ( pgs->halftone->type )
- {
- case ht_type_colorscreen:
- *pht = pgs->halftone->params.colorscreen;
- return 0;
- default:
- code = gs_currentscreen(pgs, &pht->screens.colored.gray);
- if ( code < 0 )
- return code;
- pht->screens.colored.red = pht->screens.colored.gray;
- pht->screens.colored.green = pht->screens.colored.gray;
- pht->screens.colored.blue = pht->screens.colored.gray;
- return 0;
- }
- }
-
- /* Set the halftone in the graphics state. */
- int
- gs_sethalftone(gs_state *pgs, gs_halftone *pht)
- { gx_device_halftone dev_ht;
- int code = gs_sethalftone_prepare(pgs, pht, &dev_ht);
- if ( code < 0 )
- return code;
- return gx_ht_install(pgs, pht, &dev_ht);
- }
- /* Prepare the halftone, but don't install it. */
- int
- gs_sethalftone_prepare(gs_state *pgs, gs_halftone *pht,
- gx_device_halftone *pdht)
- { gs_memory_t *mem = pgs->memory;
- gx_ht_order_component *pocs = 0;
- int code = 0;
- /* Currently we disregard the transfer function override. */
- switch ( pht->type )
- {
- case ht_type_colorscreen:
- { gs_screen_halftone *phc =
- pht->params.colorscreen.screens.indexed;
- static const gs_ht_separation_name cnames[4] =
- { gs_ht_separation_Default, gs_ht_separation_Red,
- gs_ht_separation_Green, gs_ht_separation_Blue
- };
- static const int cindex[4] = { 3, 0, 1, 2 };
- int i;
- pocs = gs_alloc_struct_array(mem, 4,
- gx_ht_order_component,
- &st_ht_order_component_element,
- "gs_sethalftone");
- if ( pocs == 0 )
- return_error(gs_error_VMerror);
- for ( i = 0; i < 4; i++ )
- { gs_screen_enum senum;
- int ci = cindex[i];
- gx_ht_order_component *poc = &pocs[i];
- code = process_screen(&senum, pgs, &phc[ci]);
- if ( code < 0 )
- break;
- #define sorder senum.order
- poc->corder = sorder;
- poc->cname = cnames[i];
- if ( i == 0 ) /* Gray = Default */
- pdht->order = sorder;
- else
- { uint tile_bytes =
- sorder.raster * sorder.height;
- uint num_tiles =
- max_tile_cache_bytes / tile_bytes + 1;
- gx_ht_cache *pcache =
- gx_ht_alloc_cache(mem, num_tiles,
- tile_bytes * num_tiles);
- if ( pcache == 0 )
- { code = gs_note_error(gs_error_VMerror);
- break;
- }
- poc->corder.cache = pcache;
- gx_ht_init_cache(pcache, &poc->corder);
- }
- #undef sorder
- }
- if ( code < 0 )
- break;
- pdht->components = pocs;
- pdht->num_comp = 4;
- } break;
- case ht_type_spot:
- { gs_screen_enum senum;
- code = process_screen(&senum, pgs, &pht->params.spot.screen);
- if ( code < 0 )
- return code;
- pdht->order = senum.order;
- pdht->components = 0;
- } break;
- case ht_type_threshold:
- code = process_threshold(&pdht->order, pgs,
- &pht->params.threshold);
- if ( code < 0 )
- return code;
- pdht->components = 0;
- break;
- case ht_type_multiple:
- { uint count = pht->params.multiple.num_comp;
- bool have_Default = false;
- uint i;
- gs_halftone_component *phc = pht->params.multiple.components;
- gx_ht_order_component *poc_next;
- pocs = gs_alloc_struct_array(mem, count,
- gx_ht_order_component,
- &st_ht_order_component_element,
- "gs_sethalftone");
- if ( pocs == 0 )
- return_error(gs_error_VMerror);
- poc_next = pocs + 1;
- for ( i = 0; i < count; i++, phc++ )
- { gx_ht_order_component *poc;
- if ( phc->cname == gs_ht_separation_Default )
- { if ( have_Default )
- { /* Duplicate Default */
- code = gs_note_error(gs_error_rangecheck);
- break;
- }
- poc = pocs;
- have_Default = true;
- }
- else if ( i == count - 1 && !have_Default )
- { /* No Default */
- code = gs_note_error(gs_error_rangecheck);
- break;
- }
- else
- poc = poc_next++;
- poc->cname = phc->cname;
- switch ( phc->type )
- {
- case ht_type_spot:
- { gs_screen_enum senum;
- code = process_screen(&senum, pgs,
- &phc->params.spot.screen);
- if ( code < 0 )
- break;
- poc->corder = senum.order;
- } break;
- case ht_type_threshold:
- code = process_threshold(&poc->corder, pgs,
- &phc->params.threshold);
- if ( code < 0 )
- break;
- break;
- default:
- code = gs_note_error(gs_error_rangecheck);
- break;
- }
- if ( code < 0 )
- break;
- if ( poc != pocs )
- { gx_ht_cache *pcache =
- gx_ht_alloc_cache(mem, 1,
- poc->corder.raster *
- poc->corder.height);
- if ( pcache == 0 )
- { code = gs_note_error(gs_error_VMerror);
- break;
- }
- poc->corder.cache = pcache;
- gx_ht_init_cache(pcache, &poc->corder);
- }
- }
- if ( code < 0 )
- break;
- pdht->order = pocs[0].corder; /* Default */
- if ( count == 1 )
- { /* We have only a Default; */
- /* we don't need components. */
- gs_free_object(mem, pocs, "gs_sethalftone");
- pdht->components = 0;
- }
- else
- { pdht->components = pocs;
- pdht->num_comp = count;
- }
- } break;
- default:
- return_error(gs_error_rangecheck);
- }
- if ( code < 0 )
- gs_free_object(mem, pocs, "gs_sethalftone");
- return code;
- }
-
- /* Return the halftone in the graphics state. */
- int
- gs_currenthalftone(gs_state *pgs, gs_halftone *pht)
- { *pht = *pgs->halftone;
- return 0;
- }
-
- /* sethalftonephase */
- int
- gs_sethalftonephase(gs_state *pgs, int x, int y)
- { pgs->ht_phase.x = x;
- pgs->ht_phase.y = y;
- gx_ht_set_phase(pgs);
- return 0;
- }
-
- /* currenthalftonephase */
- int
- gs_currenthalftonephase(gs_state *pgs, gs_int_point *pphase)
- { *pphase = pgs->ht_phase;
- return 0;
- }
-
- /* ------ Internal routines ------ */
-
- /* Process one screen plane. */
- private int
- process_screen(gs_screen_enum *penum, gs_state *pgs, gs_screen_halftone *phsp)
- { gs_point pt;
- int code = gs_screen_init(penum, pgs, phsp);
- if ( code < 0 ) return code;
- while ( (code = gs_screen_currentpoint(penum, &pt)) == 0 )
- if ( (code = gs_screen_next(penum, (*phsp->spot_function)(pt.x, pt.y))) < 0 )
- return code;
- return 0;
- }
-
- /* Process a threshold plane. */
- private int
- process_threshold(gx_ht_order *porder, gs_state *pgs,
- gs_threshold_halftone *phtp)
- { int code = gx_ht_init_order(porder, phtp->width, phtp->height,
- 256, pgs->memory);
- if ( code < 0 )
- return code;
- gx_ht_construct_threshold_order(porder, phtp->thresholds);
- return 0;
- }
-
- /* Compute the negated halftone phase mod the tile size. */
- /* This is the displacement of the tile relative to the device coordinates. */
- private void near
- order_set_phase(register gx_ht_order *porder, gs_state *pgs)
- { if ( porder->width == 0 )
- porder->phase.x = 0;
- else
- { if ( (porder->phase.x = -pgs->ht_phase.x % porder->width) < 0 )
- porder->phase.x += porder->width;
- }
- if ( porder->height == 0 )
- porder->phase.y = 0;
- else
- { if ( (porder->phase.y = -pgs->ht_phase.y % porder->height) < 0 )
- porder->phase.y += porder->height;
- }
- }
- void
- gx_ht_set_phase(gs_state *pgs)
- { gx_device_halftone *pdht = pgs->dev_ht;
- order_set_phase(&pdht->order, pgs);
- if ( pdht->components != 0 )
- { uint i;
- for ( i = 0; i < pdht->num_comp; i++ )
- order_set_phase(&pdht->components[i].corder, pgs);
- }
- }
-
- /* Initialize a halftone order. */
- int
- gx_ht_init_order(register gx_ht_order *porder, uint width, uint height,
- uint num_levels, gs_memory_t *mem)
- { uint size = width * height;
- uint i;
- gx_ht_order order;
- order = *porder;
- order.width = width;
- order.height = height;
- order.raster = bitmap_raster(width);
- order.num_levels = num_levels;
- order.num_bits = size;
- order.levels =
- (uint *)gs_alloc_byte_array(mem, num_levels, sizeof(uint),
- "ht order(levels)");
- order.bits =
- (gx_ht_bit *)gs_alloc_byte_array(mem, size, sizeof(gx_ht_bit),
- "ht order(bits)");
- if ( order.levels == 0 || order.bits == 0 )
- { gs_free_object(mem, order.bits, "ht order(bits)");
- gs_free_object(mem, order.levels, "ht order(levels)");
- return_error(gs_error_VMerror);
- }
- order.cache = 0;
- /* Tag each sample with its index, for sorting. */
- for ( i = 0; i < size; i++ )
- order.bits[i].offset = i;
- *porder = order;
- return 0;
- }
-
- /* Compare keys ("masks", actually sample values) for qsort. */
- private int
- compare_masks(const void *p1, const void *p2)
- { ht_mask_t m1 = ((const gx_ht_bit *)p1)->mask;
- ht_mask_t m2 = ((const gx_ht_bit *)p2)->mask;
- return (m1 < m2 ? -1 : m1 > m2 ? 1 : 0);
- }
- /* Sort the haltone order by sample value. */
- private void
- gx_sort_ht_order(gx_ht_bit *recs, uint N)
- { qsort((void *)recs, N, sizeof(*recs), compare_masks);
- #ifdef DEBUG
- if ( gs_debug_c('h') )
- { uint i;
- dprintf("[h]Sorted samples:\n");
- for ( i = 0; i < N; i++ )
- dprintf3("%5u: %5u: %5u\n",
- i, recs[i].offset, recs[i].mask);
- }
- #endif
- }
-
- /* Construct the halftone order from a sampled spot function. */
- /* Only width x strip samples have been filled in; */
- /* we must replicate the resulting sorted order vertically, */
- /* shifting it by shift each time. */
- void
- gx_ht_construct_spot_order(gx_ht_order *porder, uint strip, uint shift)
- { uint *levels = porder->levels;
- uint num_levels = porder->num_levels; /* = width x strip */
- uint width = porder->width;
- uint height = porder->height;
- uint copies = height / strip;
- gx_ht_bit *bits = porder->bits;
- gx_ht_bit *bp = bits + porder->num_bits - 1;
- uint i;
- gx_sort_ht_order(bits, porder->num_levels);
- /* Replicate the sorted order vertically. */
- for ( i = num_levels; i > 0; )
- { uint offset = bits[--i].offset;
- uint x = offset % height;
- uint hy = offset - x;
- uint k;
- levels[i] = i * copies;
- for ( k = 0; k < copies;
- k++, bp--, hy += num_levels, x = (x + shift) % width
- )
- bp->offset = hy + x;
- }
- gx_ht_construct_bits(porder);
- }
-
- /* Construct the halftone order from a threshold array. */
- void
- gx_ht_construct_threshold_order(gx_ht_order *porder, const byte *thresholds)
- { uint size = porder->num_bits;
- uint *levels = porder->levels;
- gx_ht_bit *bits = porder->bits;
- uint i, j;
- for ( i = 0; i < size; i++ )
- bits[i].mask = max(1, thresholds[i]);
- gx_sort_ht_order(bits, size);
- /* We want to set levels[j] to the lowest value of i */
- /* such that bits[i].mask > j. */
- for ( i = 0, j = 0; i < size; i++ )
- { if ( bits[i].mask != j )
- { if_debug3('h', "[h]levels[%u..%u] = %u\n",
- j, (uint)bits[i].mask, i);
- while ( j < bits[i].mask )
- levels[j++] = i;
- }
- }
- while ( j < 256 )
- levels[j++] = size;
- gx_ht_construct_bits(porder);
- }
-
- /* Construct offset/masks from the whitening order. */
- /* porder->bits[i].offset contains the index of the bit position */
- /* that is i'th in the whitening order. */
- void
- gx_ht_construct_bits(gx_ht_order *porder)
- { uint width = porder->width;
- uint size = porder->num_bits;
- gx_ht_bit *bits = porder->bits;
- uint i;
- gx_ht_bit *phb;
- byte *pb;
- uint padding = porder->raster * 8 - width;
- for ( i = 0, phb = bits; i < size; i++, phb++ )
- { int pix = phb->offset;
- ht_mask_t mask;
- pix += pix / width * padding;
- phb->offset = (pix >> 3) & -sizeof(mask);
- mask = (ht_mask_t)1 << (~pix & (ht_mask_bits - 1));
- /* Replicate the mask bits. */
- pix = ht_mask_bits - width;
- while ( (pix -= width) >= 0 )
- mask |= mask >> width;
- /* Store the mask, reversing bytes if necessary. */
- phb->mask = 0;
- for ( pb = (byte *)&phb->mask + (sizeof(mask) - 1);
- mask != 0;
- mask >>= 8, pb--
- )
- *pb = (byte)mask;
- }
- #ifdef DEBUG
- if ( gs_debug_c('h') )
- { dprintf1("[h]Halftone order bits 0x%lx:\n", (ulong)bits);
- for ( i = 0, phb = bits; i < size; i++, phb++ )
- dprintf3("%4d: %u:0x%lx\n", i, phb->offset,
- (ulong)phb->mask);
- }
- #endif
- }
-
- /* Install a new halftone in the graphics state. */
- int
- gx_ht_install(gs_state *pgs, const gs_halftone *pht,
- const gx_device_halftone *pdht)
- { gx_device_halftone *pgdht = pgs->dev_ht;
- if ( (ulong)pdht->order.raster * pdht->order.height >
- pgs->ht_cache->bits_size
- )
- return_error(gs_error_limitcheck);
- *pgs->halftone = *pht;
- *pgdht = *pdht;
- gx_ht_set_phase(pgs);
- /* Clear the cache, to avoid confusion in case the address of */
- /* a new order vector matches that of a (deallocated) old one. */
- gx_ht_clear_cache(pgs->ht_cache);
- /* Set the color_indices according to the device color_info. */
- if ( pdht->components != 0 )
- { static const gs_ht_separation_name dcnames[5][4] =
- { { gs_ht_separation_Default }, /* not used */
- { gs_ht_separation_Gray, gs_ht_separation_Default,
- gs_ht_separation_Default, gs_ht_separation_Default
- },
- { gs_ht_separation_Default }, /* not used */
- { gs_ht_separation_Red, gs_ht_separation_Green,
- gs_ht_separation_Blue, gs_ht_separation_Default
- },
- { gs_ht_separation_Cyan, gs_ht_separation_Magenta,
- gs_ht_separation_Yellow, gs_ht_separation_Black
- }
- };
- const gs_ht_separation_name _ds *cnames =
- dcnames[gs_currentdevice_inline(pgs)->color_info.
- num_components];
- uint i;
- memset(pgdht->color_indices, 0, sizeof(pdht->color_indices));
- for ( i = 0; i < pdht->num_comp; i++ )
- { int j;
- for ( j = 0; j < 4; j++ )
- { if ( pdht->components[i].cname == cnames[j] )
- pgdht->color_indices[j] = i;
- }
- }
- }
- gx_unset_dev_color(pgs);
- return 0;
- }
-