diff options
author | Henry Stiles <henry.stiles@artifex.com> | 1998-07-26 07:36:41 +0000 |
---|---|---|
committer | Henry Stiles <henry.stiles@artifex.com> | 1998-07-26 07:36:41 +0000 |
commit | eec0ef527f18c5978c4476c9490f4de4c4249628 (patch) | |
tree | 5588d5e1300a245186594893c930949a19bcbbce /gs/src/gxclpath.c | |
parent | d4bdba93ef34f68d27148e1b31088d1d3e786e8c (diff) |
Initial revision
git-svn-id: http://svn.ghostscript.com/ghostpcl/trunk/ghostpcl@246 06663e23-700e-0410-b217-a244a6096597
Diffstat (limited to 'gs/src/gxclpath.c')
-rw-r--r-- | gs/src/gxclpath.c | 1230 |
1 files changed, 1230 insertions, 0 deletions
diff --git a/gs/src/gxclpath.c b/gs/src/gxclpath.c new file mode 100644 index 000000000..3bb581efe --- /dev/null +++ b/gs/src/gxclpath.c @@ -0,0 +1,1230 @@ +/* Copyright (C) 1995, 1996, 1997 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. +*/ + +/* gxclpath.c */ +/* Higher-level path operations for band lists */ +#include "math_.h" +#include "memory_.h" +#include "gx.h" +#include "gpcheck.h" +#include "gserrors.h" +#include "gxdevice.h" +#include "gxdevmem.h" /* must precede gxcldev.h */ +#include "gxcldev.h" +#include "gxclpath.h" +#include "gxcolor2.h" +#include "gxpaint.h" /* for gx_fill/stroke_params */ +#include "gzpath.h" +#include "gzcpath.h" + +#define cdev cwdev + +/* Statistics */ +#ifdef DEBUG +ulong cmd_diffs[5]; +#endif + +/* Forward declarations */ +private int cmd_put_path(P8(gx_device_clist_writer *cldev, + gx_clist_state *pcls, const gx_path *ppath, fixed ymin, fixed ymax, byte op, + bool implicit_close, segment_notes keep_notes)); +/* Driver procedures */ +private dev_proc_fill_path(clist_fill_path); +private dev_proc_stroke_path(clist_stroke_path); + +/* ------ Define the extensions to the command set ------ */ + +#ifdef DEBUG +private const char *cmd_misc2_op_names[16] = { cmd_misc2_op_name_strings }; +private const char *cmd_segment_op_names[16] = { cmd_segment_op_name_strings }; +private const char *cmd_path_op_names[16] = { cmd_path_op_name_strings }; +#endif + +/* Initialize the extensions to the command name table. */ +void +gs_clpath_init(gs_memory_t *mem) +{ +#ifdef DEBUG + cmd_op_names[cmd_op_misc2 >> 4] = "(misc2)"; + cmd_sub_op_names[cmd_op_misc2 >> 4] = cmd_misc2_op_names; + cmd_op_names[cmd_op_segment >> 4] = "(segment)"; + cmd_sub_op_names[cmd_op_segment >> 4] = cmd_segment_op_names; + cmd_op_names[cmd_op_path >> 4] = "(path)"; + cmd_sub_op_names[cmd_op_path >> 4] = cmd_path_op_names; +#endif + gs_clist_device_procs.fill_path = clist_fill_path; + gs_clist_device_procs.stroke_path = clist_stroke_path; + cmd_opvar_disable_clip = cmd_opv_disable_clip; + cmd_opvar_enable_clip = cmd_opv_enable_clip; +} + +/* ------ Utilities ------ */ + +/* Write out the color for filling, stroking, or masking. */ +/* We should be able to share this with clist_tile_rectangle, */ +/* but I don't see how to do it without adding a level of procedure. */ +int +cmd_put_drawing_color(gx_device_clist_writer *cldev, gx_clist_state *pcls, + const gx_drawing_color *pdcolor) +{ const gx_strip_bitmap *tile; + gx_color_index color0, color1; + ulong offset_temp; + + if ( gx_dc_is_pure(pdcolor) ) + { gx_color_index color1 = gx_dc_pure_color(pdcolor); + if ( color1 != pcls->colors[1] ) + { int code = cmd_set_color1(cldev, pcls, color1); + if ( code < 0 ) + return code; + } +#ifdef FUTURE + return cmd_dc_type_pure; +#else + return 0; +#endif + } +#ifdef FUTURE + /* Any non-pure color will require the phase. */ + { int px = pdcolor->phase.x, py = pdcolor->phase.y; + if ( px != pcls->tile_phase.x || py != pcls->tile_phase.y ) + { int code = cmd_set_tile_phase(cldev, pcls, px, py); + if ( code < 0 ) + return code; + } + } +#endif + if ( gx_dc_is_binary_halftone(pdcolor) ) + { tile = gx_dc_binary_tile(pdcolor); + color0 = gx_dc_binary_color0(pdcolor); + color1 = gx_dc_binary_color1(pdcolor); + /* Set up tile and colors as for clist_tile_rectangle. */ + if ( !cls_has_tile_id(cldev, pcls, tile->id, offset_temp) ) + { int depth = + (color1 == gx_no_color_index && + color0 == gx_no_color_index ? + cldev->color_info.depth : 1); + if ( tile->id == gx_no_bitmap_id || + clist_change_tile(cldev, pcls, tile, depth) < 0 + ) + return_error(-1); /* can't cache tile */ + } + if ( color1 != pcls->tile_colors[1] || + color0 != pcls->tile_colors[0] + ) + { int code = cmd_set_tile_colors(cldev, pcls, color0, color1); + if ( code < 0 ) + return code; + } +#ifdef FUTURE + return cmd_dc_type_ht; +#endif + } +#ifdef FUTURE + else if ( gx_dc_is_colored_halftone(pdcolor) ) + { const gx_device_halftone *pdht = pdcolor->colors.colored.c_ht; + int num_comp = pdht->num_comp; + byte buf[4 + 4 * cmd_max_intsize(sizeof(pdcolor->colors.colored.c_level[0]))]; + byte *bp = buf; + int i; + uint short_bases = 0; + ulong bases = 0; + byte *dp; + int code; + + /****** HOW TO TELL IF COLOR IS ALREADY SET? ******/ + if ( pdht->id != cldev->device_halftone_id ) + { int code = cmd_put_halftone(cldev, pdht, pdht->type); + if ( code < 0 ) + return code; + cldev->device_halftone_id = pdht->id; + } + for ( i = 0; i < num_comp; ++i ) + { uint base = pdcolor->colors.colored.c_base[i]; + if ( base > 31 ) + return_error(gs_error_rangecheck); + bases |= base << ((3 - i) * 5); + short_bases |= base << (3 - i); + } + if ( bases & 0xf7bde ) + { /* Some base value requires more than 1 bit. */ + *bp++ = 0x10 + (byte)(bases >> 16); + *bp++ = (byte)(bases >> 8); + *bp++ = (byte)bases; + } + else + { /* The bases all fit in 1 bit each. */ + *bp++ = 0x00 + (byte)short_bases; + } + for ( i = 0; i < num_comp; ++i ) + bp = cmd_put_w((uint)pdcolor->colors.colored.c_level[i], bp); + /****** IGNORE alpha ******/ + code = + set_cmd_put_op(dp, cldev, pcls, cmd_opv_set_color, bp - buf + 1); + if (code < 0) + return code; + memcpy(dp + 1, buf, bp - buf); + return cmd_dc_type_color; + } +#endif + else + return_error(-1); +#ifndef FUTURE + { int px = pdcolor->phase.x, py = pdcolor->phase.y; + if ( px != pcls->tile_phase.x || py != pcls->tile_phase.y ) + { int code = cmd_set_tile_phase(cldev, pcls, px, py); + if ( code < 0 ) + return code; + } + } +#endif + return 0; +} + +/* Clear (a) specific 'known' flag(s) for all bands. */ +/* We must do this whenever the value of a 'known' parameter changes. */ +void +cmd_clear_known(gx_device_clist_writer *cldev, uint known) +{ ushort unknown = ~known; + gx_clist_state *pcls = cldev->states; + int i; + + for ( i = cldev->nbands; --i >= 0; ++pcls ) + pcls->known &= unknown; +} + +/* Check whether we need to change the clipping path in the device. */ +bool +cmd_check_clip_path(gx_device_clist_writer *cldev, const gx_clip_path *pcpath) +{ if ( pcpath == NULL ) + return false; + /* The clip path might have moved in memory, so even if the */ + /* ids match, update the pointer. */ + cldev->clip_path = pcpath; + if ( pcpath->id == cldev->clip_path_id ) + return false; + cldev->clip_path_id = pcpath->id; + return true; +} + +/* Construct the parameters for writing out a matrix. */ +/* We need a buffer of at least 1 + 6 * sizeof(float) bytes. */ +byte * +cmd_for_matrix(byte *cbuf, const gs_matrix *pmat) +{ byte *cp = cbuf + 1; + byte b = 0; + float coeffs[6]; + int i; + + coeffs[0] = pmat->xx; + coeffs[1] = pmat->xy; + coeffs[2] = pmat->yx; + coeffs[3] = pmat->yy; + coeffs[4] = pmat->tx; + coeffs[5] = pmat->ty; + for ( i = 0; i < 4; i += 2 ) + { float u = coeffs[i], v = coeffs[i^3]; + b <<= 2; + if ( u != 0 || v != 0 ) + { memcpy(cp, &u, sizeof(float)); + cp += sizeof(float); + if ( v == u ) + b += 1; + else if ( v == -u ) + b += 2; + else + { b += 3; + memcpy(cp, &v, sizeof(float)); + cp += sizeof(float); + } + } + } + for ( ; i < 6; ++i ) + { float v = coeffs[i]; + b <<= 1; + if ( v != 0 ) + { ++b; + memcpy(cp, &v, sizeof(float)); + cp += sizeof(float); + } + } + cbuf[0] = b << 2; + return cp; +} + +/* Write out values of any unknown parameters. */ +int +cmd_write_unknown(gx_device_clist_writer *cldev, gx_clist_state *pcls, + uint must_know) +{ int code; + ushort unknown = ~pcls->known & must_know; + + if ( unknown & flatness_known ) + { byte *dp; + code = set_cmd_put_op(dp, cldev, pcls, cmd_opv_set_flatness, + 1 + sizeof(float)); + if (code < 0) + return code; + memcpy(dp + 1, &cldev->imager_state.flatness, sizeof(float)); + pcls->known |= flatness_known; + } + if ( unknown & fill_adjust_known ) + { byte *dp; + code = set_cmd_put_op(dp, cldev, pcls, cmd_opv_set_fill_adjust, + 1 + sizeof(fixed) * 2); + if (code < 0) + return code; + memcpy(dp + 1, &cldev->imager_state.fill_adjust.x, sizeof(fixed)); + memcpy(dp + 1 + sizeof(fixed), &cldev->imager_state.fill_adjust.y, sizeof(fixed)); + pcls->known |= fill_adjust_known; + } + if ( unknown & ctm_known ) + { byte cbuf[1 + 6 * sizeof(float)]; + uint len = + cmd_for_matrix(cbuf, + (const gs_matrix *)&cldev->imager_state.ctm) - + cbuf; + byte *dp; + + code = set_cmd_put_op(dp, cldev, pcls, cmd_opv_set_ctm, len + 1); + if (code < 0) + return code; + memcpy(dp + 1, cbuf, len); + pcls->known |= ctm_known; + } + if ( unknown & line_width_known ) + { byte *dp; + float width = + gx_current_line_width(&cldev->imager_state.line_params); + + code = set_cmd_put_op(dp, cldev, pcls, cmd_opv_set_line_width, + 1 + sizeof(width)); + if (code < 0) + return code; + memcpy(dp + 1, &width, sizeof(width)); + pcls->known |= line_width_known; + } + if ( unknown & miter_limit_known ) + { byte *dp; + code = set_cmd_put_op(dp, cldev, pcls, cmd_opv_set_miter_limit, + 1 + sizeof(float)); + if (code < 0) + return code; + memcpy(dp + 1, &cldev->imager_state.line_params.miter_limit, sizeof(float)); + pcls->known |= miter_limit_known; + } + if ( unknown & misc0_known ) + { byte *dp; + code = set_cmd_put_op(dp, cldev, pcls, cmd_opv_set_misc2, 2); + if (code < 0) + return code; + dp[1] = cmd_set_misc2_cap_join + + (cldev->imager_state.line_params.cap << 3) + + cldev->imager_state.line_params.join; + pcls->known |= misc0_known; + } + if ( unknown & misc1_known ) + { byte *dp; + code = set_cmd_put_op(dp, cldev, pcls, cmd_opv_set_misc2, 2); + if (code < 0) + return code; + dp[1] = cmd_set_misc2_ac_op_sa + + (cldev->imager_state.accurate_curves ? 4 : 0) + + (cldev->imager_state.overprint ? 2 : 0) + + (cldev->imager_state.stroke_adjust ? 1 : 0); + pcls->known |= misc1_known; + } + if ( unknown & dash_known ) + { byte *dp; + int n = cldev->imager_state.line_params.dash.pattern_size; + + code = set_cmd_put_op(dp, cldev, pcls, cmd_opv_set_dash, + 2 + (n + 2) * sizeof(float)); + if (code < 0) + return code; + dp[1] = n + (cldev->imager_state.line_params.dash.adapt ? 0x80 : 0) + + (cldev->imager_state.line_params.dot_length_absolute ? 0x40 : 0); + memcpy(dp + 2, &cldev->imager_state.line_params.dot_length, + sizeof(float)); + memcpy(dp + 2 + sizeof(float), + &cldev->imager_state.line_params.dash.offset, + sizeof(float)); + if ( n != 0 ) + memcpy(dp + 2 + sizeof(float) * 2, + cldev->imager_state.line_params.dash.pattern, + n * sizeof(float)); + pcls->known |= dash_known; + } + if ( unknown & clip_path_known ) + { /* We can write out the clipping path either as rectangles */ + /* or as a real (filled) path. */ + const gx_clip_path *pcpath = cldev->clip_path; + int band_height = cldev->page_band_height; + int ymin = (pcls - cldev->states) * band_height; + int ymax = min(ymin + band_height, cldev->height); + gs_fixed_rect box; + int punt_to_outer_box = 0; + byte *dp; + int code; + int end_code; + + code = set_cmd_put_op(dp, cldev, pcls, cmd_opv_begin_clip, 1); + if (code < 0) + return code; + if (pcpath->segments_valid) + { + if ( gx_path_is_rectangle(&pcpath->path, &box) && + fixed_is_int(box.p.x | box.p.y | box.q.x | box.q.y) + ) + { /* Write the path as a rectangle. */ + code = cmd_write_rect_cmd(cldev, pcls, cmd_op_fill_rect, + fixed2int_var(box.p.x), + fixed2int_var(box.p.y), + fixed2int(box.q.x - box.p.x), + fixed2int(box.q.y - box.p.y)); + } + else if ( !(cldev->disable_mask & clist_disable_complex_clip) ) + { /* Write the path. */ + code = cmd_put_path(cldev, pcls, &pcpath->path, + int2fixed(ymin - 1), + int2fixed(ymax + 1), + (pcpath->rule == gx_rule_even_odd ? + cmd_opv_eofill : cmd_opv_fill), + true, sn_not_first); + } + else + /* Complex paths disabled: write outer box as clip */ + punt_to_outer_box = 1; + } + else + { /* Write out the rectangles. */ + const gx_clip_rect *prect = pcpath->list.head; + + if ( prect == 0 ) + prect = &pcpath->list.single; + else if (cldev->disable_mask & clist_disable_complex_clip) + punt_to_outer_box = 1; + if (!punt_to_outer_box) + for ( ; prect != 0 && code >= 0; prect = prect->next ) + if ( prect->xmax > prect->xmin && + prect->ymin < ymax && prect->ymax > ymin + ) + { code = + cmd_write_rect_cmd(cldev, pcls, cmd_op_fill_rect, + prect->xmin, prect->ymin, + prect->xmax - prect->xmin, + prect->ymax - prect->ymin); + } + } + if (punt_to_outer_box) + { /* Clip is complex, but disabled. Write out the outer box */ + gs_fixed_rect box; + gx_cpath_outer_box(pcpath, &box); + box.p.x = fixed_floor(box.p.x); + box.p.y = fixed_floor(box.p.y); + code = cmd_write_rect_cmd(cldev, pcls, cmd_op_fill_rect, + fixed2int_var(box.p.x), + fixed2int_var(box.p.y), + fixed2int_ceiling(box.q.x - box.p.x), + fixed2int_ceiling(box.q.y - box.p.y)); + } + end_code = set_cmd_put_op(dp, cldev, pcls, cmd_opv_end_clip, 2); + if (code >= 0) + code = end_code; /* take the first failure seen */ + if (end_code < 0 && cldev->error_is_retryable) + { /* end_clip has to work despite lo-mem to maintain consistency. */ + /* This isn't error recovery, but just to prevent dangling */ + /* cmd_opv_begin_clip's */ + ++cldev->ignore_lo_mem_warnings; + end_code + = set_cmd_put_op(dp, cldev, pcls, cmd_opv_end_clip, 2); + --cldev->ignore_lo_mem_warnings; + } + if (end_code >= 0) + dp[1] = (gx_cpath_is_outside(pcpath) ? 1 : 0); + if (code < 0) + return code; + pcls->clip_enabled = 1; + pcls->known |= clip_path_known; + } + if ( unknown & color_space_known ) + { byte *dp; + + if ( cldev->color_space & 8 ) /* indexed */ + { uint num_values = (cldev->indexed_params.hival + 1) * + gs_color_space_num_components( + (const gs_color_space *)&cldev->indexed_params.base_space); + bool use_proc = cldev->color_space & 4; + const void *map_data; + uint map_size; + + if ( use_proc ) + { map_data = cldev->indexed_params.lookup.map->values; + map_size = num_values * + sizeof(cldev->indexed_params.lookup.map->values[0]); + } + else + { map_data = cldev->indexed_params.lookup.table.data; + map_size = num_values; + } + code = set_cmd_put_op(dp, cldev, pcls, cmd_opv_set_color_space, + 2 + cmd_sizew(cldev->indexed_params.hival) + map_size); + if (code < 0) + return code; + memcpy(cmd_put_w(cldev->indexed_params.hival, dp + 2), + map_data, map_size); + } + else + { code = + set_cmd_put_op(dp, cldev, pcls, cmd_opv_set_color_space, 2); + if (code < 0) + return code; + } + dp[1] = cldev->color_space; + pcls->known |= color_space_known; + } + return 0; +} + +/* ------ Driver procedures ------ */ + +private int +clist_fill_path(gx_device *dev, const gs_imager_state *pis, gx_path *ppath, + const gx_fill_params *params, const gx_drawing_color *pdcolor, + const gx_clip_path *pcpath) +{ uint unknown = 0; + int y, height, y0, y1; + gs_logical_operation_t lop = pis->log_op; + byte op = (byte) + (params->rule == gx_rule_even_odd ? +#ifdef FUTURE + cmd_opv_eofill : cmd_opv_fill +#else + (gx_dc_is_pure(pdcolor) ? cmd_opv_eofill : cmd_opv_hteofill) : + (gx_dc_is_pure(pdcolor) ? cmd_opv_fill : cmd_opv_htfill) +#endif + ); + gs_fixed_point adjust; + + if ( (cdev->disable_mask & clist_disable_fill_path) + || gs_debug_c(',') /* disable path-based banding */ + ) + return gx_default_fill_path(dev, pis, ppath, params, pdcolor, + pcpath); + adjust = params->adjust; + { gs_fixed_rect bbox; + gx_path_bbox(ppath, &bbox); + y = fixed2int(bbox.p.y) - 1; + height = fixed2int_ceiling(bbox.q.y) - y + 1; + fit_fill_yh(dev, y, height); + if ( height <= 0 ) + return 0; + } + y0 = y; + y1 = y + height; + if ( cdev->imager_state.flatness != params->flatness ) + { unknown |= flatness_known; + cdev->imager_state.flatness = params->flatness; + } + if ( cdev->imager_state.fill_adjust.x != adjust.x || + cdev->imager_state.fill_adjust.y != adjust.y + ) + { unknown |= fill_adjust_known; + cdev->imager_state.fill_adjust = adjust; + } + if ( cmd_check_clip_path(cdev, pcpath) ) + unknown |= clip_path_known; + if ( unknown ) + cmd_clear_known(cdev, unknown); + BEGIN_RECT + + int code = cmd_do_write_unknown(cdev, pcls, + flatness_known | fill_adjust_known | + clip_path_known); + if (code < 0) + return code; + if ( ( code = cmd_do_enable_clip(cdev, pcls, pcpath != NULL) ) < 0 ) + return code; + if ( lop == lop_default ) + { if ( ( code = cmd_disable_lop(cdev, pcls) ) < 0 ) + return code; + } + else + { if ( lop != pcls->lop ) + { code = cmd_set_lop(cdev, pcls, lop); + if ( code < 0 ) + return code; + } + if ( ( code = cmd_enable_lop(cdev, pcls) ) < 0 ) + return code; + } + code = cmd_put_drawing_color(cdev, pcls, pdcolor); + if ( code < 0 ) + { /* Something went wrong, use the default implementation. */ + return gx_default_fill_path(dev, pis, ppath, params, pdcolor, + pcpath); + } + code = cmd_put_path(cdev, pcls, ppath, + int2fixed(max(y - 1, y0)), + int2fixed(min(y + height + 1, y1)), +#ifdef FUTURE + op + code, /* cmd_dc_type */ +#else + op, +#endif + true, sn_none /* fill doesn't need the notes */); + if ( code < 0 ) + return code; + END_RECT + return 0; +} + +private int +clist_stroke_path(gx_device *dev, const gs_imager_state *pis, gx_path *ppath, + const gx_stroke_params *params, + const gx_drawing_color *pdcolor, const gx_clip_path *pcpath) +{ int pattern_size = pis->line_params.dash.pattern_size; + uint unknown = 0; + gs_fixed_rect bbox; + gs_fixed_point expansion; + int adjust_y; + int y, height, y0, y1; + gs_logical_operation_t lop = pis->log_op; +#ifndef FUTURE + byte op = (byte) + (gx_dc_is_pure(pdcolor) ? cmd_opv_stroke : cmd_opv_htstroke); +#endif + + if ( (cdev->disable_mask & clist_disable_stroke_path) + || gs_debug_c(',') /* disable path-based banding */ + ) + return gx_default_stroke_path(dev, pis, ppath, params, pdcolor, + pcpath); + gx_path_bbox(ppath, &bbox); + /* We must use the supplied imager state, not our saved one, */ + /* for computing the stroke expansion. */ + if ( gx_stroke_path_expansion(pis, ppath, &expansion) < 0 ) + { /* Expansion is too large: use the entire page. */ + adjust_y = 0; + y = 0; + height = dev->height; + } + else + { adjust_y = fixed2int_ceiling(expansion.y) + 1; + y = fixed2int(bbox.p.y) - adjust_y; + height = fixed2int_ceiling(bbox.q.y) - y + adjust_y; + fit_fill_yh(dev, y, height); + if ( height <= 0 ) + return 0; + } + y0 = y; + y1 = y + height; + /* Check the dash pattern, since we bail out if */ + /* the pattern is too large. */ + cdev->imager_state.line_params.dash.pattern = cdev->dash_pattern; + if ( cdev->imager_state.line_params.dash.pattern_size != pattern_size || + (pattern_size != 0 && + memcmp(cdev->imager_state.line_params.dash.pattern, + pis->line_params.dash.pattern, + pattern_size * sizeof(float))) || + cdev->imager_state.line_params.dash.offset != + pis->line_params.dash.offset || + cdev->imager_state.line_params.dash.adapt != + pis->line_params.dash.adapt || + cdev->imager_state.line_params.dot_length != + pis->line_params.dot_length || + cdev->imager_state.line_params.dot_length_absolute != + pis->line_params.dot_length_absolute + ) + { /* Bail out if the dash pattern is too long. */ + if ( pattern_size > cmd_max_dash ) + return gx_default_stroke_path(dev, pis, ppath, params, + pdcolor, pcpath); + unknown |= dash_known; + gx_set_dash(&cdev->imager_state.line_params.dash, + pis->line_params.dash.pattern, + pis->line_params.dash.pattern_size, + pis->line_params.dash.offset, NULL); + gx_set_dash_adapt(&cdev->imager_state.line_params.dash, + pis->line_params.dash.adapt); + gx_set_dot_length(&cdev->imager_state.line_params, + pis->line_params.dot_length, + pis->line_params.dot_length_absolute); + } + if ( state_neq(flatness) ) + { unknown |= flatness_known; + state_update(flatness); + } + if ( state_neq(fill_adjust.x) || state_neq(fill_adjust.y) ) + { unknown |= fill_adjust_known; + state_update(fill_adjust); + } + if ( state_neq(ctm.xx) || state_neq(ctm.xy) || + state_neq(ctm.yx) || state_neq(ctm.yy) || + /* We don't actually need tx or ty, but we don't want to bother */ + /* tracking them separately from the other coefficients. */ + state_neq(ctm.tx) || state_neq(ctm.ty) + ) + { unknown |= ctm_known; + state_update(ctm); + } + if ( state_neq(line_params.half_width) ) + { unknown |= line_width_known; + state_update(line_params.half_width); + } + if ( state_neq(line_params.miter_limit) ) + { unknown |= miter_limit_known; + gx_set_miter_limit(&cdev->imager_state.line_params, + pis->line_params.miter_limit); + } + if ( state_neq(line_params.cap) || state_neq(line_params.join) ) + { unknown |= misc0_known; + state_update(line_params.cap); + state_update(line_params.join); + } + if ( state_neq(accurate_curves) || state_neq(overprint) || + state_neq(stroke_adjust) + ) + { unknown |= misc1_known; + state_update(accurate_curves); + state_update(overprint); + state_update(stroke_adjust); + } + if ( cmd_check_clip_path(cdev, pcpath) ) + unknown |= clip_path_known; + if ( unknown ) + cmd_clear_known(cdev, unknown); + BEGIN_RECT + + int code = cmd_do_write_unknown(cdev, pcls, stroke_all_known); + if (code < 0) + return code; + if ( ( code = cmd_do_enable_clip(cdev, pcls, pcpath != NULL) ) < 0 ) + return code; + if ( lop == lop_default ) + { if ( ( code = cmd_disable_lop(cdev, pcls) ) < 0 ) + return code; + } + else + { if ( lop != pcls->lop ) + { code = cmd_set_lop(cdev, pcls, lop); + if ( code < 0 ) + return code; + } + if ( ( code = cmd_enable_lop(cdev, pcls) ) < 0 ) + return code; + } + code = cmd_put_drawing_color(cdev, pcls, pdcolor); + if ( code < 0 ) + { /* Something went wrong, use the default implementation. */ + return gx_default_stroke_path(dev, pis, ppath, params, pdcolor, + pcpath); + } + { fixed ymin, ymax; + /* If a dash pattern is active, we can't skip segments */ + /* outside the clipping region, because that would throw off */ + /* the pattern. */ + if ( pattern_size == 0 ) + ymin = int2fixed(max(y - adjust_y, y0)), + ymax = int2fixed(min(y + height + adjust_y, y1)); + else + ymin = min_fixed, + ymax = max_fixed; + code = cmd_put_path(cdev, pcls, ppath, ymin, ymax, +#ifdef FUTURE + cmd_opv_stroke + code, /* cmd_dc_type */ +#else + op, +#endif + false, (segment_notes)~0); + if ( code < 0 ) + return code; + } + END_RECT + return 0; +} + +/* ------ Path utilities ------ */ + +/* Define the state bookkeeping for writing path segments. */ +typedef struct cmd_segment_writer_s { + /* Set at initialization */ + gx_device_clist_writer *cldev; + gx_clist_state *pcls; + /* Updated dynamically */ + segment_notes notes; + byte *dp; + int len; + gs_fixed_point delta_first; + byte cmd[6 * (1 + sizeof(fixed))]; +} cmd_segment_writer; + +/* Put out a path segment command. */ +private int near +cmd_put_segment(cmd_segment_writer _ss *psw, byte op, + const fixed _ss *operands, segment_notes notes) +{ const fixed _ss *optr = operands; + /* Fetch num_operands before possible command merging. */ + int i = clist_segment_op_num_operands[op & 0xf]; + byte *q = psw->cmd - 1; + +#ifdef DEBUG + if ( gs_debug_c('L') ) + { int j; + dprintf2("[L] %s:%d:", cmd_segment_op_names[op & 0xf], + (int)notes); + for ( j = 0; j < i; ++j ) + dprintf1(" %g", fixed2float(operands[j])); + dputs("\n"); + } +#endif + + /* Merge or shorten commands if possible. */ + if ( op == cmd_opv_rlineto ) + { if ( operands[0] == 0 ) + op = cmd_opv_vlineto, optr = ++operands, i = 1; + else if ( operands[1] == 0 ) + op = cmd_opv_hlineto, i = 1; + else + switch ( *psw->dp ) + { + case cmd_opv_rmoveto: + psw->delta_first.x = operands[0]; + psw->delta_first.y = operands[1]; + op = cmd_opv_rmlineto; +merge: cmd_uncount_op(*psw->dp, psw->len); + cmd_shorten_op(psw->cldev, psw->pcls, psw->len); /* delete it */ + q += psw->len - 1; + break; + case cmd_opv_rmlineto: + if ( notes != psw->notes ) + break; + op = cmd_opv_rm2lineto; + goto merge; + case cmd_opv_rm2lineto: + if ( notes != psw->notes ) + break; + if ( operands[0] == -psw->delta_first.x && + operands[1] == -psw->delta_first.y + ) + { cmd_uncount_op(cmd_opv_rm2lineto, psw->len); + *psw->dp = cmd_count_op(cmd_opv_rm3lineto, psw->len); + return 0; + } + break; + default: + ; + } + } + + for ( ; --i >= 0; ++optr ) + { fixed d = *optr, d2; + if ( is_bits(d, _fixed_shift + 11) && + !(d & (float2fixed(0.25) - 1)) + ) + { cmd_count_add1(cmd_diffs[3]); + d = ((d >> (_fixed_shift - 2)) & 0x1fff) + 0xc000; + q += 2; + } + else if ( is_bits(d, 19) && i > 0 && is_bits(d2 = optr[1], 19) ) + { cmd_count_add1(cmd_diffs[0]); + q[1] = (byte)((d >> 13) & 0x3f); + q[2] = (byte)(d >> 5); + q[3] = (byte)((d << 3) + ((d2 >> 16) & 7)); + q[4] = (byte)(d2 >> 8); + q[5] = (byte)d2; + q += 5; + --i, ++optr; + continue; + } + else if ( is_bits(d, 22) ) + { cmd_count_add1(cmd_diffs[1]); + q[1] = (byte)(((d >> 16) & 0x3f) + 0x40); + q += 3; + } + else if ( is_bits(d, 30) ) + { cmd_count_add1(cmd_diffs[2]); + q[1] = (byte)(((d >> 24) & 0x3f) + 0x80); + q[2] = (byte)(d >> 16); + q += 4; + } + else + { int b; + cmd_count_add1(cmd_diffs[4]); + *++q = 0xe0; + for ( b = sizeof(fixed) - 1; b > 1; --b ) + *++q = (byte)(d >> (b * 8)); + q += 2; + } + q[-1] = (byte)(d >> 8); + *q = (byte)d; + } + if ( notes != psw->notes ) + { byte *dp; + + int code = + set_cmd_put_op(dp, psw->cldev, psw->pcls, cmd_opv_set_misc2, 2); + if (code < 0) + return code; + dp[1] = cmd_set_misc2_notes + notes; + psw->notes = notes; + } + { int len = q + 2 - psw->cmd; + byte *dp; + + int code = + set_cmd_put_op(dp, psw->cldev, psw->pcls, op, len); + if (code < 0) + return code; + memcpy(dp + 1, psw->cmd, len - 1); + psw->len = len; + psw->dp = dp; + } + return 0; +} +/* Put out a line segment command. */ +#define cmd_put_rmoveto(psw, operands)\ + cmd_put_segment(psw, cmd_opv_rmoveto, operands, sn_none) +#define cmd_put_rlineto(psw, operands, notes)\ + cmd_put_segment(psw, cmd_opv_rlineto, operands, notes) + +/* + * Write a path. We go to a lot of trouble to omit segments that are + * entirely outside the band. + */ +private int +cmd_put_path(gx_device_clist_writer *cldev, gx_clist_state *pcls, + const gx_path *ppath, fixed ymin, fixed ymax, byte path_op, + bool implicit_close, segment_notes keep_notes) +{ gs_path_enum cenum; + cmd_segment_writer writer; + static byte initial_op = { cmd_opv_end_run }; + /* + * We define the 'side' of a point according to its Y value as + * follows: + */ +#define which_side(y) ((y) < ymin ? -1 : (y) >= ymax ? 1 : 0) + + /* + * While writing a subpath, we need to keep track of any segments + * skipped at the beginning of the subpath and any segments skipped + * just before the current segment. We do this with two sets of + * state variables, one that tracks the actual path segments and one + * that tracks the emitted segments. + * + * The following track the actual segments: + */ + + /* + * The point and side of the last moveto (skipped if + * start_side != 0): + */ + gs_fixed_point start; + int start_side; + /* + * Whether any lines or curves were skipped immediately + * following the moveto: + */ + bool start_skip; + /* The side of the last point: */ + int side; + /* The last point with side != 0: */ + gs_fixed_point out; + /* If the last out-going segment was a lineto, */ + /* its notes: */ + segment_notes out_notes; + + /* + * The following track the emitted segments: + */ + + /* The last point emitted: */ + fixed px = int2fixed(pcls->rect.x); + fixed py = int2fixed(pcls->rect.y); + /* The point of the last emitted moveto: */ + gs_fixed_point first; + /* Information about the last emitted operation: */ + int open = 0; /* -1 if last was moveto, 1 if line/curveto, */ + /* 0 if newpath/closepath */ + + + if_debug4('p', "[p]initial (%g,%g), clip [%g..%g)\n", + fixed2float(px), fixed2float(py), + fixed2float(ymin), fixed2float(ymax)); + gx_path_enum_init(&cenum, ppath); + writer.cldev = cldev; + writer.pcls = pcls; + writer.notes = sn_none; +#define set_first_point() (writer.dp = &initial_op) +#define first_point() (writer.dp == &initial_op) + set_first_point(); + for ( ; ; ) + { fixed vs[6]; +#define A vs[0] +#define B vs[1] +#define C vs[2] +#define D vs[3] +#define E vs[4] +#define F vs[5] + int pe_op = gx_path_enum_next(&cenum, (gs_fixed_point *)vs); + byte *dp; + int code; + + switch ( pe_op ) + { + case 0: + /* If the path is open and needs an implicit close, */ + /* do the close and then come here again. */ + if ( open > 0 && implicit_close ) + goto close; + /* All done. */ + pcls->rect.x = fixed2int_var(px); + pcls->rect.y = fixed2int_var(py); + if_debug2('p', "[p]final (%d,%d)\n", + pcls->rect.x, pcls->rect.y); + return set_cmd_put_op(dp, cldev, pcls, path_op, 1); + case gs_pe_moveto: + /* If the path is open and needs an implicit close, */ + /* do a closepath and then redo the moveto. */ + if ( open > 0 && implicit_close ) + { gx_path_enum_backup(&cenum); + goto close; + } + open = -1; + start.x = A, start.y = B; + start_skip = false; + if ( (start_side = side = which_side(B)) != 0 ) + { out.x = A, out.y = B; + if_debug3('p', "[p]skip moveto (%g,%g) side %d\n", + fixed2float(out.x), fixed2float(out.y), + side); + continue; + } + C = A - px, D = B - py; + first.x = px = A, first.y = py = B; + code = cmd_put_rmoveto(&writer, &C); + if_debug2('p', "[p]moveto (%g,%g)\n", + fixed2float(px), fixed2float(py)); + break; + case gs_pe_lineto: + { int next_side = which_side(B); + segment_notes notes = + gx_path_enum_notes(&cenum) & keep_notes; + + if ( next_side == side && side != 0 ) + { /* Skip a line completely outside the clip region. */ + if ( open < 0 ) + start_skip = true; + out.x = A, out.y = B; + out_notes = notes; + if_debug3('p', "[p]skip lineto (%g,%g) side %d\n", + fixed2float(out.x), fixed2float(out.y), + side); + continue; + } + /* If we skipped any segments, put out a moveto/lineto. */ + if ( side && (px != out.x || py != out.y || first_point()) ) + { C = out.x - px, D = out.y - py; + if ( open < 0 ) + { first = out; + code = cmd_put_rmoveto(&writer, &C); + } + else + code = cmd_put_rlineto(&writer, &C, out_notes); + if ( code < 0 ) + return code; + px = out.x, py = out.y; + if_debug3('p', "[p]catchup %s (%g,%g) for line\n", + (open < 0 ? "moveto" : "lineto"), + fixed2float(px), fixed2float(py)); + } + if ( (side = next_side) != 0 ) + { /* Note a vertex going outside the clip region. */ + out.x = A, out.y = B; + } + C = A - px, D = B - py; + px = A, py = B; + open = 1; + code = cmd_put_rlineto(&writer, &C, notes); + } + if_debug3('p', "[p]lineto (%g,%g) side %d\n", + fixed2float(px), fixed2float(py), side); + break; + case gs_pe_closepath: +#ifdef DEBUG + { gs_path_enum cpenum; + gs_fixed_point cvs[3]; + int op; + cpenum = cenum; + switch ( op = gx_path_enum_next(&cpenum, cvs) ) + { + case 0: case gs_pe_moveto: + break; + default: + lprintf1("closepath followed by %d, not end/moveto!\n", + op); + } + } +#endif + /* A closepath may require drawing an explicit line if */ + /* we skipped any segments at the beginning of the path. */ +close: if ( side != start_side ) + { /* If we skipped any segments, put out a moveto/lineto. */ + if ( side && (px != out.x || py != out.y || first_point()) ) + { C = out.x - px, D = out.y - py; + code = cmd_put_rlineto(&writer, &C, out_notes); + if ( code < 0 ) + return code; + px = out.x, py = out.y; + if_debug2('p', "[p]catchup line (%g,%g) for close\n", + fixed2float(px), fixed2float(py)); + } + if ( open > 0 && start_skip ) + { /* Draw the closing line back to the start. */ + C = start.x - px, D = start.y - py; + code = cmd_put_rlineto(&writer, &C, sn_none); + if ( code < 0 ) + return code; + px = start.x, py = start.y; + if_debug2('p', "[p]draw close to (%g,%g)\n", + fixed2float(px), fixed2float(py)); + } + } + /* + * We don't bother to update side because we know that the + * next element after a closepath, if any, must be a moveto. + * We must handle explicitly the possibility that the entire + * subpath was skipped. + */ + if ( implicit_close || open <= 0 ) + { open = 0; + /* + * Force writing an explicit moveto if the next subpath + * starts with a moveto to the same point where this one + * ends. + */ + set_first_point(); + continue; + } + open = 0; + px = first.x, py = first.y; + code = cmd_put_segment(&writer, cmd_opv_closepath, &A, sn_none); + if_debug0('p', "[p]close\n"); + break; + case gs_pe_curveto: + { segment_notes notes = + gx_path_enum_notes(&cenum) & keep_notes; + { fixed bpy, bqy; + int all_side, out_side; + + /* Compute the Y bounds for the clipping check. */ + if ( B < D ) bpy = B, bqy = D; + else bpy = D, bqy = B; + if ( F < bpy ) bpy = F; + else if ( F > bqy ) bqy = F; + all_side = (bqy < ymin ? -1 : bpy > ymax ? 1 : 0); + if ( all_side != 0 ) + { if ( all_side == side ) + { /* Skip a curve entirely outside the clip region. */ + if ( open < 0 ) + start_skip = true; + out.x = E, out.y = F; + out_notes = notes; + if_debug3('p', "[p]skip curveto (%g,%g) side %d\n", + fixed2float(out.x), fixed2float(out.y), + side); + continue; + } + out_side = all_side; + } + else + out_side = which_side(F); + /* If we skipped any segments, put out a moveto/lineto. */ + if ( side && (px != out.x || py != out.y || first_point()) ) + { fixed diff[2]; + diff[0] = out.x - px, diff[1] = out.y - py; + if ( open < 0 ) + { first = out; + code = cmd_put_rmoveto(&writer, diff); + } + else + code = cmd_put_rlineto(&writer, diff, out_notes); + if ( code < 0 ) + return code; + px = out.x, py = out.y; + if_debug3('p', "[p]catchup %s (%g,%g) for curve\n", + (open < 0 ? "moveto" : "lineto"), + fixed2float(px), fixed2float(py)); + } + if ( (side = out_side) != 0 ) + { /* Note a vertex going outside the clip region. */ + out.x = E, out.y = F; + } + } + { fixed nx = E, ny = F; + const fixed _ss *optr = vs; + byte op; + + if_debug7('p', "[p]curveto (%g,%g; %g,%g; %g,%g) side %d\n", + fixed2float(A), fixed2float(B), + fixed2float(C), fixed2float(D), + fixed2float(E), fixed2float(F), side); + E -= C, F -= D; + C -= A, D -= B; + A -= px, B -= py; + if ( B == 0 && E == 0 ) + { B = A, E = F, optr++, op = cmd_opv_hvcurveto; + if ( (B ^ D) >= 0 ) + { if ( C == D && E == B ) + op = cmd_opv_hqcurveto; + } + else if ( C == -D && E == -B ) + C = D, op = cmd_opv_hqcurveto; + } + else if ( A == 0 && F == 0 ) + { optr++, op = cmd_opv_vhcurveto; + if ( (B ^ C) >= 0 ) + { if ( D == C && E == B ) + op = cmd_opv_vqcurveto; + } + else if ( D == -C && E == -B ) + op = cmd_opv_vqcurveto; + } + else if ( A == 0 && B == 0 ) + optr += 2, op = cmd_opv_nrcurveto; + else if ( E == 0 && F == 0 ) + op = cmd_opv_rncurveto; + else + op = cmd_opv_rrcurveto; + px = nx, py = ny; + open = 1; + code = cmd_put_segment(&writer, op, optr, notes); + } + } break; + default: + return_error(gs_error_rangecheck); + } + if ( code < 0 ) + return code; +#undef A +#undef B +#undef C +#undef D +#undef E +#undef F + } +} |