/* Copyright (C) 1989, 1996, 1997, 1998, 1999 artofcode LLC. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA, 02111-1307. */ /*$Id: gsht.c,v 1.2.6.1.2.1 2003/01/17 00:49:02 giles Exp $ */ /* setscreen operator for Ghostscript library */ #include "memory_.h" #include /* for qsort */ #include "gx.h" #include "gserrors.h" #include "gsstruct.h" #include "gsutil.h" /* for gs_next_ids */ #include "gxarith.h" /* for igcd */ #include "gzstate.h" #include "gxdevice.h" /* for gzht.h */ #include "gzht.h" /* Forward declarations */ void gx_set_effective_transfer(P1(gs_state *)); /* Structure types */ public_st_ht_order(); private_st_ht_order_component(); public_st_ht_order_comp_element(); public_st_halftone(); public_st_device_halftone(); /* GC procedures */ private ENUM_PTRS_WITH(ht_order_enum_ptrs, gx_ht_order *porder) return 0; case 0: ENUM_RETURN((porder->data_memory ? porder->levels : 0)); case 1: ENUM_RETURN((porder->data_memory ? porder->bit_data : 0)); case 2: ENUM_RETURN(porder->cache); case 3: ENUM_RETURN(porder->transfer); ENUM_PTRS_END private RELOC_PTRS_WITH(ht_order_reloc_ptrs, gx_ht_order *porder) { if (porder->data_memory) { RELOC_VAR(porder->levels); RELOC_VAR(porder->bit_data); } RELOC_VAR(porder->cache); RELOC_VAR(porder->transfer); } RELOC_PTRS_END private ENUM_PTRS_WITH(halftone_enum_ptrs, gs_halftone *hptr) return 0; case 0: switch (hptr->type) { case ht_type_spot: ENUM_RETURN((hptr->params.spot.transfer == 0 ? hptr->params.spot.transfer_closure.data : 0)); case ht_type_threshold: ENUM_RETURN_CONST_STRING_PTR(gs_halftone, params.threshold.thresholds); case ht_type_threshold2: return ENUM_CONST_BYTESTRING(&hptr->params.threshold2.thresholds); case ht_type_client_order: ENUM_RETURN(hptr->params.client_order.client_data); case ht_type_multiple: case ht_type_multiple_colorscreen: ENUM_RETURN(hptr->params.multiple.components); case ht_type_none: case ht_type_screen: case ht_type_colorscreen: return 0; } case 1: switch (hptr->type) { case ht_type_threshold: ENUM_RETURN((hptr->params.threshold.transfer == 0 ? hptr->params.threshold.transfer_closure.data : 0)); case ht_type_threshold2: ENUM_RETURN(hptr->params.threshold2.transfer_closure.data); case ht_type_client_order: ENUM_RETURN(hptr->params.client_order.transfer_closure.data); default: return 0; } ENUM_PTRS_END private RELOC_PTRS_WITH(halftone_reloc_ptrs, gs_halftone *hptr) { switch (hptr->type) { case ht_type_spot: if (hptr->params.spot.transfer == 0) RELOC_PTR(gs_halftone, params.spot.transfer_closure.data); break; case ht_type_threshold: RELOC_CONST_STRING_PTR(gs_halftone, params.threshold.thresholds); if (hptr->params.threshold.transfer == 0) RELOC_PTR(gs_halftone, params.threshold.transfer_closure.data); break; case ht_type_threshold2: RELOC_CONST_BYTESTRING_VAR(hptr->params.threshold2.thresholds); RELOC_OBJ_VAR(hptr->params.threshold2.transfer_closure.data); break; case ht_type_client_order: RELOC_PTR(gs_halftone, params.client_order.client_data); RELOC_PTR(gs_halftone, params.client_order.transfer_closure.data); break; case ht_type_multiple: case ht_type_multiple_colorscreen: RELOC_PTR(gs_halftone, params.multiple.components); break; case ht_type_none: case ht_type_screen: case ht_type_colorscreen: break; } } RELOC_PTRS_END /* setscreen */ int gs_setscreen(gs_state * pgs, gs_screen_halftone * phsp) { gs_screen_enum senum; int code = gx_ht_process_screen(&senum, pgs, phsp, gs_currentaccuratescreens()); if (code < 0) return code; return gs_screen_install(&senum); } /* currentscreen */ int gs_currentscreen(const 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); } } /* .currentscreenlevels */ int gs_currentscreenlevels(const gs_state * pgs) { return pgs->dev_ht->order.num_levels; } /* .setscreenphase */ int gx_imager_setscreenphase(gs_imager_state * pis, int x, int y, gs_color_select_t select) { if (select == gs_color_select_all) { int i; for (i = 0; i < gs_color_select_count; ++i) gx_imager_setscreenphase(pis, x, y, (gs_color_select_t) i); return 0; } else if (select < 0 || select >= gs_color_select_count) return_error(gs_error_rangecheck); pis->screen_phase[select].x = x; pis->screen_phase[select].y = y; return 0; } int gs_setscreenphase(gs_state * pgs, int x, int y, gs_color_select_t select) { int code = gx_imager_setscreenphase((gs_imager_state *) pgs, x, y, select); /* * If we're only setting the source phase, we don't need to do * unset_dev_color, because the source phase doesn't affect painting * with the current color. */ if (code >= 0 && (select == gs_color_select_texture || select == gs_color_select_all) ) gx_unset_dev_color(pgs); return code; } /* .currentscreenphase */ int gs_currentscreenphase(const gs_state * pgs, gs_int_point * pphase, gs_color_select_t select) { if (select < 0 || select >= gs_color_select_count) return_error(gs_error_rangecheck); *pphase = pgs->screen_phase[select]; return 0; } /* currenthalftone */ int gs_currenthalftone(gs_state * pgs, gs_halftone * pht) { *pht = *pgs->halftone; return 0; } /* ------ Internal routines ------ */ /* Process one screen plane. */ int gx_ht_process_screen_memory(gs_screen_enum * penum, gs_state * pgs, gs_screen_halftone * phsp, bool accurate, gs_memory_t * mem) { gs_point pt; int code = gs_screen_init_memory(penum, pgs, phsp, accurate, mem); 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; } /* * Internal procedure to allocate and initialize either an internally * generated or a client-defined halftone order. For spot halftones, * the client is responsible for calling gx_compute_cell_values. */ int gx_ht_alloc_ht_order(gx_ht_order * porder, uint width, uint height, uint num_levels, uint num_bits, uint strip_shift, const gx_ht_order_procs_t *procs, gs_memory_t * mem) { porder->width = width; porder->height = height; porder->raster = bitmap_raster(width); porder->shift = strip_shift; porder->orig_height = porder->height; porder->orig_shift = porder->shift; porder->full_height = ht_order_full_height(porder); porder->num_levels = num_levels; porder->num_bits = num_bits; porder->procs = procs; porder->data_memory = mem; porder->levels = (uint *)gs_alloc_byte_array(mem, porder->num_levels, sizeof(uint), "alloc_ht_order_data(levels)"); porder->bit_data = gs_alloc_byte_array(mem, porder->num_bits, porder->procs->bit_data_elt_size, "alloc_ht_order_data(bit_data)"); if (porder->levels == 0 || porder->bit_data == 0) { gs_free_object(mem, porder->bit_data, "alloc_ht_order_data(bit_data)"); porder->bit_data = 0; gs_free_object(mem, porder->levels, "alloc_ht_order_data(levels)"); porder->levels = 0; return_error(gs_error_VMerror); } porder->cache = 0; porder->transfer = 0; return 0; } /* Allocate and initialize the contents of a halftone order. */ /* The client must have set the defining values in porder->params. */ int gx_ht_alloc_order(gx_ht_order * porder, uint width, uint height, uint strip_shift, uint num_levels, gs_memory_t * mem) { gx_ht_order order; int code; order = *porder; gx_compute_cell_values(&order.params); code = gx_ht_alloc_ht_order(&order, width, height, num_levels, width * height, strip_shift, &ht_order_procs_default, mem); if (code < 0) return code; *porder = order; return 0; } /* * Allocate and initialize a threshold order, which may use the short * representation. */ int gx_ht_alloc_threshold_order(gx_ht_order * porder, uint width, uint height, uint num_levels, gs_memory_t * mem) { gx_ht_order order; uint num_bits = width * height; const gx_ht_order_procs_t *procs = (num_bits > 2000 && num_bits <= max_ushort ? &ht_order_procs_short : &ht_order_procs_default); int code; order = *porder; gx_compute_cell_values(&order.params); code = gx_ht_alloc_ht_order(&order, width, height, num_levels, width * height, 0, procs, mem); if (code < 0) return code; *porder = order; return 0; } /* Allocate and initialize the contents of a client-defined halftone order. */ int gx_ht_alloc_client_order(gx_ht_order * porder, uint width, uint height, uint num_levels, uint num_bits, gs_memory_t * mem) { gx_ht_order order; int code; order = *porder; order.params.M = width, order.params.N = 0; order.params.R = 1; order.params.M1 = height, order.params.N1 = 0; order.params.R1 = 1; gx_compute_cell_values(&order.params); code = gx_ht_alloc_ht_order(&order, width, height, num_levels, num_bits, 0, &ht_order_procs_default, mem); if (code < 0) return code; *porder = order; return 0; } /* Compare keys ("masks", actually sample values) for qsort. */ private int compare_samples(const void *p1, const void *p2) { ht_sample_t m1 = ((const gx_ht_bit *)p1)->mask; ht_sample_t m2 = ((const gx_ht_bit *)p2)->mask; return (m1 < m2 ? -1 : m1 > m2 ? 1 : 0); } /* Sort the halftone order by sample value. */ void gx_sort_ht_order(gx_ht_bit * recs, uint N) { int i; /* Tag each sample with its index, for sorting. */ for (i = 0; i < N; i++) recs[i].offset = i; qsort((void *)recs, N, sizeof(*recs), compare_samples); #ifdef DEBUG if (gs_debug_c('H')) { uint i; dlputs("[H]Sorted samples:\n"); for (i = 0; i < N; i++) dlprintf3("%5u: %5u: %u\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. See gxdht.h regarding * the invariants that must be restored. */ void gx_ht_construct_spot_order(gx_ht_order * porder) { uint width = porder->width; uint num_levels = porder->num_levels; /* = width x strip */ uint strip = num_levels / width; gx_ht_bit *bits = (gx_ht_bit *)porder->bit_data; uint *levels = porder->levels; uint shift = porder->orig_shift; uint full_height = porder->full_height; uint num_bits = porder->num_bits; uint copies = num_bits / (width * strip); gx_ht_bit *bp = bits + num_bits - 1; uint i; gx_sort_ht_order(bits, num_levels); if_debug5('h', "[h]spot order: num_levels=%u w=%u h=%u strip=%u shift=%u\n", num_levels, width, porder->orig_height, strip, shift); /* Fill in the levels array, replicating the bits vertically */ /* if needed. */ for (i = num_levels; i > 0;) { uint offset = bits[--i].offset; uint x = offset % width; uint hy = offset - x; uint k; levels[i] = i * copies; for (k = 0; k < copies; k++, bp--, hy += num_levels, x = (x + width - shift) % width ) bp->offset = hy + x; } /* If we have a complete halftone, restore the invariant. */ if (num_bits == width * full_height) { porder->height = full_height; porder->shift = 0; } gx_ht_construct_bits(porder); } /* Construct a single offset/mask. */ void gx_ht_construct_bit(gx_ht_bit * bit, int width, int bit_num) { uint padding = bitmap_raster(width) * 8 - width; int pix = bit_num; ht_mask_t mask; byte *pb; pix += pix / width * padding; bit->offset = (pix >> 3) & -size_of(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. */ bit->mask = 0; for (pb = (byte *) & bit->mask + (sizeof(mask) - 1); mask != 0; mask >>= 8, pb-- ) *pb = (byte) mask; } /* 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 i; gx_ht_bit *phb; for (i = 0, phb = (gx_ht_bit *)porder->bit_data; i < porder->num_bits; i++, phb++) gx_ht_construct_bit(phb, porder->width, phb->offset); #ifdef DEBUG if (gs_debug_c('H')) { dlprintf1("[H]Halftone order bits 0x%lx:\n", (ulong)porder->bit_data); for (i = 0, phb = (gx_ht_bit *)porder->bit_data; i < porder->num_bits; i++, phb++) dlprintf3("%4d: %u:0x%lx\n", i, phb->offset, (ulong) phb->mask); } #endif } /* Release a gx_device_halftone by freeing its components. */ /* (Don't free the gx_device_halftone itself.) */ void gx_ht_order_release(gx_ht_order * porder, gs_memory_t * mem, bool free_cache) { if (free_cache && porder->cache) gx_ht_free_cache(mem, porder->cache); gs_free_object(mem, porder->transfer, "gx_ht_order_release(transfer)"); if (porder->data_memory) { gs_free_object(porder->data_memory, porder->bit_data, "gx_ht_order_release(bit_data)"); gs_free_object(porder->data_memory, porder->levels, "gx_ht_order_release(levels)"); } } void gx_device_halftone_release(gx_device_halftone * pdht, gs_memory_t * mem) { if (pdht->components) { int i; /* One of the components might be the same as the default */ /* order, so check that we don't free it twice. */ for (i = 0; i < pdht->num_comp; ++i) if (pdht->components[i].corder.bit_data != pdht->order.bit_data ) { /* Currently, all orders except the default one */ /* own their caches. */ gx_ht_order_release(&pdht->components[i].corder, mem, true); } gs_free_object(mem, pdht->components, "gx_dev_ht_release(components)"); pdht->components = 0; pdht->num_comp = 0; } gx_ht_order_release(&pdht->order, mem, false); } /* * Install a device halftone in an imager state. Note that this does not * read or update the client halftone. There is a special check for pdht == * pis->dev_ht, for the benefit of the band rendering code. */ int gx_imager_dev_ht_install(gs_imager_state * pis, const gx_device_halftone * pdht, gs_halftone_type type, const gx_device * dev) { gx_device_halftone *pgdht = pis->dev_ht; if ((ulong) pdht->order.raster * (pdht->order.num_bits / pdht->order.width) > pis->ht_cache->bits_size ) return_error(gs_error_limitcheck); if (pdht == pgdht) DO_NOTHING; /* special hack for band renderer */ else if (pgdht != 0 && pgdht->rc.ref_count == 1 && pgdht->rc.memory == pdht->rc.memory ) { /* The current device halftone isn't shared. */ /* Just release its components. */ gx_device_halftone_release(pgdht, pgdht->rc.memory); } else { /* The device halftone is shared or not yet allocated. */ rc_unshare_struct(pis->dev_ht, gx_device_halftone, &st_device_halftone, pdht->rc.memory, return_error(gs_error_VMerror), "gx_imager_dev_ht_install"); pgdht = pis->dev_ht; } { rc_header rc; rc = pgdht->rc; *pgdht = *pdht; pgdht->rc = rc; } pgdht->id = gs_next_ids(1); pgdht->type = type; /* 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(pis->ht_cache); /* Set the color_indices according to the device color_info. */ /* Also compute the LCM of the primary color cell sizes. */ /* Note that for strip halftones, the "cell size" is the */ /* theoretical fully expanded size with shift = 0. */ if (pdht->components != 0) { static const gs_ht_separation_name dcnames[5][4] = { {gs_ht_separation_Default}, /* not used */ {gs_ht_separation_Default, gs_ht_separation_Default, gs_ht_separation_Default, gs_ht_separation_Gray }, {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 } }; static const gs_ht_separation_name cscnames[4] = {gs_ht_separation_Red, gs_ht_separation_Green, gs_ht_separation_Blue, gs_ht_separation_Default }; int num_comps = dev->color_info.num_components; const gs_ht_separation_name *cnames = dcnames[num_comps]; int lcm_width = 1, lcm_height = 1; uint i; /* Halftones set by setcolorscreen, and (we think) */ /* Type 2 and Type 4 halftones, are supposed to work */ /* for both RGB and CMYK, so we need a special check here. */ if (num_comps == 4 && (type == ht_type_colorscreen || type == ht_type_multiple_colorscreen) ) cnames = cscnames; if_debug4('h', "[h]dcnames=%lu,%lu,%lu,%lu\n", (ulong) cnames[0], (ulong) cnames[1], (ulong) cnames[2], (ulong) cnames[3]); memset(pgdht->color_indices, 0, sizeof(pdht->color_indices)); for (i = 0; i < pdht->num_comp; i++) { const gx_ht_order_component *pcomp = &pdht->components[i]; int j; if_debug2('h', "[h]cname[%d]=%lu\n", i, (ulong) pcomp->cname); for (j = 0; j < 4; j++) { if (pcomp->cname == cnames[j]) { if_debug2('h', "[h]color_indices[%d]=%d\n", j, i); pgdht->color_indices[j] = i; } } } /* Now do a second pass to compute the LCM. */ /* We have to do it this way in case some entry in */ /* color_indices is still 0. */ for (i = 0; i < 4; ++i) { const gx_ht_order_component *pcomp = &pdht->components[pgdht->color_indices[i]]; uint cw = pcomp->corder.width; uint ch = pcomp->corder.full_height; int dw = lcm_width / igcd(lcm_width, cw); int dh = lcm_height / igcd(lcm_height, ch); lcm_width = (cw > max_int / dw ? max_int : cw * dw); lcm_height = (ch > max_int / dh ? max_int : ch * dh); } pgdht->lcm_width = lcm_width; pgdht->lcm_height = lcm_height; } else { /* Only one component. */ pgdht->lcm_width = pgdht->order.width; pgdht->lcm_height = pgdht->order.full_height; } if_debug2('h', "[h]LCM=(%d,%d)\n", pgdht->lcm_width, pgdht->lcm_height); gx_imager_set_effective_xfer(pis); return 0; } /* * Install a new halftone in the graphics state. Note that we copy the top * level of the gs_halftone and the gx_device_halftone, and take ownership * of any substructures. */ int gx_ht_install(gs_state * pgs, const gs_halftone * pht, const gx_device_halftone * pdht) { gs_memory_t *mem = pht->rc.memory; gs_halftone *old_ht = pgs->halftone; gs_halftone *new_ht; int code; if (old_ht != 0 && old_ht->rc.memory == mem && old_ht->rc.ref_count == 1 ) new_ht = old_ht; else rc_alloc_struct_1(new_ht, gs_halftone, &st_halftone, mem, return_error(gs_error_VMerror), "gx_ht_install(new halftone)"); code = gx_imager_dev_ht_install((gs_imager_state *) pgs, pdht, pht->type, gs_currentdevice_inline(pgs)); if (code < 0) { if (new_ht != old_ht) gs_free_object(mem, new_ht, "gx_ht_install(new halftone)"); return code; } if (new_ht != old_ht) rc_decrement(old_ht, "gx_ht_install(old halftone)"); { rc_header rc; rc = new_ht->rc; *new_ht = *pht; new_ht->rc = rc; } pgs->halftone = new_ht; gx_unset_dev_color(pgs); return 0; } /* Reestablish the effective transfer functions, taking into account */ /* any overrides from halftone dictionaries. */ void gx_imager_set_effective_xfer(gs_imager_state * pis) { const gx_device_halftone *pdht = pis->dev_ht; pis->effective_transfer = pis->set_transfer; /* default */ if (pdht == 0) return; /* not initialized yet */ if (pdht->components == 0) { /* Check for transfer function override in single halftone */ gx_transfer_map *pmap = pdht->order.transfer; if (pmap != 0) pis->effective_transfer.indexed[0] = pis->effective_transfer.indexed[1] = pis->effective_transfer.indexed[2] = pis->effective_transfer.indexed[3] = pmap; } else { /* Check in all 4 standard separations */ int i; for (i = 0; i < 4; ++i) { gx_transfer_map *pmap = pdht->components[pdht->color_indices[i]].corder. transfer; if (pmap != 0) pis->effective_transfer.indexed[i] = pmap; } } } void gx_set_effective_transfer(gs_state * pgs) { gx_imager_set_effective_xfer((gs_imager_state *) pgs); }