123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535 |
- /* Copyright (C) 2002 artofcode LLC. 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 the license contained in the file LICENSE in this distribution.
-
- For more information about licensing, please refer to
- http://www.ghostscript.com/licensing/. For information on
- commercial licensing, go to http://www.artifex.com/licensing/ or
- contact Artifex Software, Inc., 101 Lucas Valley Road #110,
- San Rafael, CA 94903, U.S.A., +1(415)492-9861.
- */
- /*$Id: gxwts.c,v 1.5 2002/11/02 07:31:45 raph Exp $ */
- /* Rendering using Well Tempered Screening. */
- #include "stdpre.h"
- #include "memory_.h" /* for memcmp */
- #include <stdlib.h> /* for malloc */
- #include "gx.h"
- #include "gxstate.h"
- #include "gsht.h"
- #include "math_.h"
- #include "gserrors.h"
- #include "gxdcolor.h"
- #include "gxdevcli.h"
- #include "gxdht.h"
- #include "gxwts.h"
- #define GXWTS_USE_DOUBLE
- #ifndef UNIT_TEST
- /* device color type for wts. */
- /* todo: trace and relocate pointers */
- gs_private_st_simple(st_dc_wts, gx_device_color, "dc_wts");
- private dev_color_proc_save_dc(gx_dc_wts_save_dc);
- private dev_color_proc_get_dev_halftone(gx_dc_wts_get_dev_halftone);
- private dev_color_proc_load(gx_dc_wts_load);
- private dev_color_proc_fill_rectangle(gx_dc_wts_fill_rectangle);
- private dev_color_proc_equal(gx_dc_wts_equal);
- private dev_color_proc_write(gx_dc_wts_write);
- private dev_color_proc_read(gx_dc_wts_read);
- private dev_color_proc_get_nonzero_comps(gx_dc_wts_get_nonzero_comps);
- const gx_device_color_type_t gx_dc_type_data_wts = {
- &st_dc_wts,
- gx_dc_wts_save_dc, gx_dc_wts_get_dev_halftone,
- gx_dc_ht_get_phase,
- gx_dc_wts_load, gx_dc_wts_fill_rectangle,
- gx_dc_default_fill_masked, gx_dc_wts_equal,
- gx_dc_wts_write, gx_dc_wts_read,
- gx_dc_wts_get_nonzero_comps
- };
- #undef gx_dc_type_wts
- const gx_device_color_type_t *const gx_dc_type_wts =
- &gx_dc_type_data_wts;
- #endif
- /* Low-level implementation follows. */
- /**
- * mul_shr_16: Multiply and shift right 16.
- * @a: 32-bit signed number.
- * @b: 32-bit signed number.
- *
- * Multiply @a and @b, then shift right 16 bits. Allow intermediate value
- * to overflow 32 bits.
- *
- * Return value: result.
- **/
- #ifdef GXWTS_USE_DOUBLE
- private int
- mul_shr_16 (int a, int b)
- {
- return (int)floor(((double) a) * ((double) b) * (1.0 / (1 << 16)));
- }
- #else
- #error todo: supply mul_shr_16 based on 64 bit integer type
- #endif
- /* Implementation of wts_get_samples for rational cells. */
- #if 0
- private int
- wts_get_samples_rat(const wts_screen_t *ws, int x, int y,
- wts_screen_sample_t **samples, int *p_nsamples)
- {
- int d = y / ws->cell_height;
- int r = y % ws->cell_height;
- int x_ix = ((d * ws->cell_shift) + x) % ws->cell_width;
- *p_nsamples = ws->cell_width - x_ix;
- *samples = ws->samples + x_ix + r * ws->cell_width;
- return 0;
- }
- #endif
- /* Implementation of wts_get_samples for Screen J. */
- private int
- wts_get_samples_j(const wts_screen_t *ws, int x, int y,
- wts_screen_sample_t **samples, int *p_nsamples)
- {
- const wts_screen_j_t *wsj = (const wts_screen_j_t *)ws;
- /* int d = y / ws->cell_height; */
- int y_ix = y;
- int x_ix = x;
- double pad = (wsj->pa) * (1.0 / (1 << 16));
- double pbd = (wsj->pb) * (1.0 / (1 << 16));
- double afrac = x * pad;
- double bfrac = x * pbd;
- int acount = (int)floor(afrac);
- int bcount = (int)floor(bfrac);
- int ccount = mul_shr_16(y, wsj->pc);
- int dcount = mul_shr_16(y, wsj->pd);
- int nsamples;
- x_ix += acount * wsj->XA + bcount * wsj->XB +
- ccount * wsj->XC + dcount * wsj->XD;
- y_ix += acount * wsj->YA + bcount * wsj->YB +
- ccount * wsj->YC + dcount * wsj->YD;
- x_ix += (y_ix / ws->cell_height) * ws->cell_shift;
- x_ix %= ws->cell_width;
- y_ix %= ws->cell_height;
- nsamples = ws->cell_width - x_ix;
- if (floor (afrac + (nsamples - 1) * pad) > acount)
- nsamples = (int)ceil((acount + 1 - afrac) / pad);
- if (floor (bfrac + (nsamples - 1) * pbd) > bcount)
- nsamples = (int)ceil((bcount + 1 - bfrac) / pbd);
- #if 0
- printf("get_samples: (%d, %d) -> (%d, %d) %d (cc=%d)\n",
- x, y, x_ix, y_ix, nsamples, ccount);
- #endif
- *p_nsamples = nsamples;
- *samples = ws->samples + x_ix + y_ix * ws->cell_width;
- return 0;
- }
- private int
- wts_screen_h_offset(int x, double p1, int m1, int m2)
- {
- /* todo: this is a linear search; constant time should be feasible */
- double running_p = 0;
- int width_so_far;
- int this_width;
- for (width_so_far = 0;; width_so_far += this_width) {
- running_p += p1;
- if (running_p >= 0.5) {
- this_width = m1;
- running_p -= 1;
- } else {
- this_width = m2;
- }
- if (width_so_far + this_width > x)
- break;
- }
- return x - width_so_far + (this_width == m1 ? 0 : m1);
- }
- /* Implementation of wts_get_samples for Screen H. */
- private int
- wts_get_samples_h(const wts_screen_t *ws, int x, int y,
- wts_screen_sample_t **samples, int *p_nsamples)
- {
- const wts_screen_h_t *wsh = (const wts_screen_h_t *)ws;
- int x_ix = wts_screen_h_offset(x, wsh->px,
- wsh->x1, ws->cell_width - wsh->x1);
- int y_ix = wts_screen_h_offset(y, wsh->py,
- wsh->y1, ws->cell_height - wsh->y1);
- *p_nsamples = (x_ix >= wsh->x1 ? ws->cell_width : wsh->x1) - x_ix;
- *samples = ws->samples + x_ix + y_ix * ws->cell_width;
- return 0;
- }
- /**
- * wts_get_samples: Get samples from Well Tempered Screening cell.
- * @ws: Well Tempered Screening cell.
- * @x: X coordinate of starting point.
- * @y: Y coordinate of starting point.
- * @samples: Where to store pointer to samples.
- * @p_nsamples: Where to store number of valid samples.
- *
- * Finds samples from the cell for use in halftoning. On success,
- * @p_nsamples is set to the number of valid samples, ie for 0 <= i <
- * nsamples, samples[i] is a valid sample for coordinate (x + i, y).
- * p_nsamples is guaranteed to at least 1. The samples in @samples
- * are valid for the lifetime of the cell, or until the next garbage
- * collection, whichever comes first.
- *
- * Todo: describe meaning of wts_screen_sample_t (particularly edge
- * cases).
- *
- * Note: may want to add a "cursor" to the api as an optimization. It
- * can wait, though.
- *
- * Return value: 0 on success.
- **/
- int
- wts_get_samples(const wts_screen_t *ws, int x, int y,
- wts_screen_sample_t **samples, int *p_nsamples)
- {
- if (ws->type == WTS_SCREEN_J)
- return wts_get_samples_j(ws, x, y, samples, p_nsamples);
- if (ws->type == WTS_SCREEN_H)
- return wts_get_samples_h(ws, x, y, samples, p_nsamples);
- else
- return -1;
- }
- /* Device color methods follow. */
- private void
- gx_dc_wts_save_dc(const gx_device_color * pdevc, gx_device_color_saved * psdc)
- {
- psdc->type = pdevc->type;
- memcpy( psdc->colors.wts.levels,
- pdevc->colors.wts.levels,
- sizeof(psdc->colors.wts.levels) );
- psdc->phase = pdevc->phase;
- }
- private const gx_device_halftone *
- gx_dc_wts_get_dev_halftone(const gx_device_color * pdevc)
- {
- return pdevc->colors.wts.w_ht;
- }
- private int
- gx_dc_wts_load(gx_device_color *pdevc, const gs_imager_state * pis,
- gx_device *ignore_dev, gs_color_select_t select)
- {
- return 0;
- }
- /**
- * wts_draw: Draw a halftoned shade into a 1 bit deep buffer.
- * @ws: WTS screen.
- * @shade: Gray shade to draw.
- * @data: Destination buffer.
- * @data_raster: Rowstride for destination buffer.
- * @x, @y, @w, @h: coordinates of rectangle to draw.
- *
- * This is close to an implementation of the "draw" method for the
- * gx_ht_order class. Currently, only WTS screens implement this
- * method, and only WTS device colors invoke it. However, implementing
- * this for legacy order objects is probably a good idea, to improve
- * halftoning performance as the cell size scales up.
- *
- * However, it's not exactly an implementation of the "draw" method
- * for the gx_ht_order class because the "self" type would need to be
- * gx_ht_order. Currently, however, device colors don't hold a pointer
- * to the order object. Some amount of refactoring seems to be in
- * order.
- *
- * Return value: 0 on success.
- **/
- private int
- wts_draw(wts_screen_t *ws, wts_screen_sample_t shade,
- byte *data, int data_raster,
- int x, int y, int w, int h)
- {
- int xo, yo;
- unsigned char *line_start = data;
- for (yo = 0; yo < h; yo++) {
- unsigned char *line_ptr = line_start;
- int mask = 0x80;
- unsigned char b = 0;
- int imax;
- for (xo = 0; xo < w; xo += imax) {
- wts_screen_sample_t *samples;
- int n_samples, i;
- wts_get_samples(ws, x + xo, y + yo, &samples, &n_samples);
- imax = min(w - xo, n_samples);
- for (i = 0; i < imax; i++) {
- if (shade > samples[i])
- b |= mask;
- mask >>= 1;
- if (mask == 0) {
- *line_ptr++ = b;
- b = 0;
- mask = 0x80;
- }
- }
- }
- if (mask != 0x80)
- *line_ptr = b;
- line_start += data_raster;
- }
- return 0;
- }
- /**
- * Special case implementation for one component. When we do plane_mask,
- * we'll want to generalize this to handle any single-bit plane_mask.
- **/
- private int
- gx_dc_wts_fill_rectangle_1(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; */
- int tile_raster = ((w + 31) & -32) >> 3;
- int tile_size = tile_raster * h;
- unsigned char *tile_data;
- int code = 0;
- gx_ht_order_component *components = pdevc->colors.wts.w_ht->components;
- wts_screen_t *ws = components[0].corder.wts;
- wts_screen_sample_t shade = pdevc->colors.wts.levels[0];
- gx_color_index color0, color1;
- color0 = dev->color_info.separable_and_linear == GX_CINFO_SEP_LIN ? 0 :
- pdevc->colors.wts.plane_vector[1];
- color1 = pdevc->colors.wts.plane_vector[0];
- tile_data = malloc(tile_size);
- wts_draw(ws, shade, tile_data, tile_raster, x, y, w, h);
-
- /* See gx_dc_ht_binary_fill_rectangle() for explanation. */
- if (dev->color_info.depth > 1)
- lop &= ~lop_T_transparent;
- /* Interesting question: should data_x be (x & 7), rather than 0,
- to improve alignment? */
- if (source == NULL && lop_no_S_is_T(lop))
- code = (*dev_proc(dev, copy_mono))
- (dev, tile_data, 0, tile_raster, gx_no_bitmap_id,
- x, y, w, h, color0, color1);
- free(tile_data);
- return code;
- }
- private int
- gx_dc_wts_write(
- const gx_device_color * pdevc,
- const gx_device_color_saved * psdc,
- const gx_device * dev,
- byte * pdata,
- uint * psize )
- {
- /* not yet implemented */
- return_error(gs_error_unknownerror);
- }
- private int
- gx_dc_wts_read(
- gx_device_color * pdevc,
- const gs_imager_state * pis,
- const gx_device_color * prior_devc,
- const gx_device * dev,
- const byte * pdata,
- uint size,
- gs_memory_t * mem )
- {
- /* not yet implemented */
- return_error(gs_error_unknownerror);
- }
- /**
- * wts_repack_tile_4: Repack four 1-bit tiles into chunky nibbles.
- * Note: argument list will change. plane_mask and base_color will
- * probably get added as an optimization.
- *
- * Note: we round w up to an even value. We're counting on the
- * subsequent copy_color to ignore any extra bits.
- **/
- private void
- wts_repack_tile_4(unsigned char *ctile_data, int ctile_raster,
- const unsigned char **tile_data, int tile_raster,
- const gx_color_index *plane_vector, bool invert,
- int w, int h)
- {
- int y;
- int tile_idx_start = 0;
- unsigned char *ctile_start = ctile_data;
- byte inv_byte = invert ? 0xff : 0;
- for (y = 0; y < h; y++) {
- int x;
- int tile_idx = tile_idx_start;
- for (x = 0; x < w; x += 2) {
- byte b = 0;
- byte m0 = 0x80 >> (x & 6);
- byte m1 = m0 >> 1;
- byte td;
- td = tile_data[0][tile_idx] ^ inv_byte;
- if (td & m0) b |= plane_vector[0] << 4;
- if (td & m1) b |= plane_vector[0];
- td = tile_data[1][tile_idx] ^ inv_byte;
- if (td & m0) b |= plane_vector[1] << 4;
- if (td & m1) b |= plane_vector[1];
- td = tile_data[2][tile_idx] ^ inv_byte;
- if (td & m0) b |= plane_vector[2] << 4;
- if (td & m1) b |= plane_vector[2];
- td = tile_data[3][tile_idx] ^ inv_byte;
- if (td & m0) b |= plane_vector[3] << 4;
- if (td & m1) b |= plane_vector[3];
- if ((x & 6) == 6)
- tile_idx++;
- ctile_start[x >> 1] = b;
- }
- tile_idx_start += tile_raster;
- ctile_start += ctile_raster;
- }
- }
- /* Special case implementation for four components. Intermediate color
- * to the order objecttile (for copy_color) is packed 2 to a byte.
- *
- * Looking at this code, it should generalize to more than four
- * components. Probably the repack code should get factored out.
- */
- private int
- gx_dc_wts_fill_rectangle_4(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)
- {
- int num_comp = pdevc->colors.wts.num_components;
- /* gx_rop_source_t no_source; */
- int tile_raster = ((w + 31) & -32) >> 3;
- int tile_size = tile_raster * h;
- unsigned char *tile_data[4];
- int ctile_raster = ((w + 7) & -8) >> 1;
- int ctile_size = ctile_raster * h;
- unsigned char *ctile_data;
- int code = 0;
- bool invert = 0 && dev->color_info.polarity == GX_CINFO_POLARITY_SUBTRACTIVE;
- int i;
- for (i = 0; i < num_comp; i++) {
- wts_screen_sample_t shade = pdevc->colors.wts.levels[i];
- gx_ht_order_component *components = pdevc->colors.wts.w_ht->components;
- wts_screen_t *ws = components[i].corder.wts;
- tile_data[i] = malloc(tile_size);
- wts_draw(ws, shade, tile_data[i], tile_raster, x, y, w, h);
- }
- ctile_data = malloc(ctile_size);
- wts_repack_tile_4(ctile_data, ctile_raster,
- (const unsigned char **)tile_data, tile_raster,
- pdevc->colors.wts.plane_vector, invert, w, h);
-
- /* See gx_dc_ht_binary_fill_rectangle() for explanation. */
- if (dev->color_info.depth > 1)
- lop &= ~lop_T_transparent;
- if (source == NULL && lop_no_S_is_T(lop))
- code = (*dev_proc(dev, copy_color))
- (dev, ctile_data, 0, ctile_raster, gx_no_bitmap_id,
- x, y, w, h);
- free(ctile_data);
- for (i = 0; i < num_comp; i++) {
- free(tile_data[i]);
- }
- return code;
- }
- private int
- gx_dc_wts_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)
- {
- int num_comp = pdevc->colors.wts.num_components;
- if (num_comp == 1)
- return gx_dc_wts_fill_rectangle_1(pdevc, x, y, w, h, dev, lop, source);
- else if (num_comp <= 4)
- return gx_dc_wts_fill_rectangle_4(pdevc, x, y, w, h, dev, lop, source);
- else
- return -1;
- }
- /* Compare two wts colors for equality. */
- private int
- gx_dc_wts_equal(const gx_device_color *pdevc1,
- const gx_device_color *pdevc2)
- {
- uint num_comp = pdevc1->colors.wts.num_components;
- if (pdevc2->type != pdevc1->type ||
- pdevc1->phase.x != pdevc2->phase.x ||
- pdevc1->phase.y != pdevc2->phase.y ||
- num_comp != pdevc2->colors.wts.num_components
- )
- return false;
- return
- !memcmp(pdevc1->colors.wts.levels,
- pdevc2->colors.wts.levels,
- num_comp * sizeof(pdevc1->colors.wts.levels[0]));
- }
- /*
- * Get the nonzero components of a wts 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.
- */
- int
- gx_dc_wts_get_nonzero_comps(
- const gx_device_color * pdevc,
- const gx_device * dev_ignored,
- gx_color_index * pcomp_bits )
- {
- int i, ncomps = pdevc->colors.wts.num_components;
- gx_color_index comp_bits = 0; /* todo: plane_mask */
- for (i = 0; i < ncomps; i++) {
- if (pdevc->colors.wts.levels[i] != 0)
- comp_bits |= ((gx_color_index)1) << i;
- }
- *pcomp_bits = comp_bits;
- return 0;
- }
|