summaryrefslogtreecommitdiff
path: root/pcl/pgdraw.c
diff options
context:
space:
mode:
Diffstat (limited to 'pcl/pgdraw.c')
-rw-r--r--pcl/pgdraw.c1642
1 files changed, 1642 insertions, 0 deletions
diff --git a/pcl/pgdraw.c b/pcl/pgdraw.c
new file mode 100644
index 000000000..108108e61
--- /dev/null
+++ b/pcl/pgdraw.c
@@ -0,0 +1,1642 @@
+/* Portions Copyright (C) 2001 artofcode LLC.
+ Portions Copyright (C) 1996, 2001 Artifex Software Inc.
+ Portions Copyright (C) 1988, 2000 Aladdin Enterprises.
+ This software is based in part on the work of the Independent JPEG Group.
+ All Rights Reserved.
+
+ This software is distributed under license and may not be copied, modified
+ or distributed except as expressly authorized under the terms of that
+ license. Refer to licensing information at http://www.artifex.com/ or
+ contact Artifex Software, Inc., 101 Lucas Valley Road #110,
+ San Rafael, CA 94903, (415)492-9861, for further information. */
+/*$Id$ */
+
+/* pgdraw.c - HP-GL/2 line drawing/path building routines. */
+
+#include "stdio_.h"
+#include "math_.h"
+#include "gdebug.h"
+#include "gstypes.h" /* for gsstate.h */
+#include "gsmatrix.h" /* for gsstate.h */
+#include "gsmemory.h" /* for gsstate.h */
+#include "gsstate.h"
+#include "gscoord.h"
+#include "gspath.h"
+#include "gspaint.h"
+#include "gsrop.h" /* for gs_setrasterop */
+#include "gxfarith.h" /* for sincos */
+#include "gxfixed.h"
+#include "pgmand.h"
+#include "pgdraw.h"
+#include "pggeom.h"
+#include "pgmisc.h"
+#include "pcdraw.h"
+#include "pcpalet.h"
+#include "pcpatrn.h"
+#include "pcpage.h"
+
+
+
+/* hack to quiet compiler warnings */
+#ifndef abs
+extern int abs( int );
+#endif
+
+#define round(x) (((x) < 0.0) ? (ceil ((x) - 0.5)) : (floor ((x) + 0.5)))
+
+
+static inline gs_point
+hpgl_picture_frame_scale(hpgl_state_t *pgls)
+{
+ gs_point scale;
+ scale.x = scale.y = 0;
+
+ /* this should not happen in a real system */
+ if ( (pgls->g.picture_frame_height == 0) ||
+ (pgls->g.picture_frame_width == 0) ||
+ (pgls->g.plot_width == 0) ||
+ (pgls->g.plot_height == 0) ) {
+ dprintf("bad picture frame coordinates\n");
+ } else {
+ scale.x = (pgls->g.plot_size_horizontal_specified) ?
+ ((hpgl_real_t)pgls->g.picture_frame_width /
+ (hpgl_real_t)pgls->g.plot_width) :
+ 1.0;
+ scale.y = (pgls->g.plot_size_vertical_specified) ?
+ ((hpgl_real_t)pgls->g.picture_frame_height /
+ (hpgl_real_t)pgls->g.plot_height) :
+ 1.0;
+ }
+ return scale;
+}
+
+static int
+hpgl_set_picture_frame_scaling(hpgl_state_t *pgls)
+{
+ if ( pgls->g.scaling_type != hpgl_scaling_point_factor ) {
+ gs_point scale = hpgl_picture_frame_scale(pgls);
+ hpgl_call(gs_scale(pgls->pgs, scale.x, scale.y));
+ }
+ return 0;
+}
+
+hpgl_real_t
+hpgl_width_scale(hpgl_state_t *pgls)
+{
+ gs_point sc = hpgl_picture_frame_scale(pgls);
+ return min(sc.x, sc.y);
+}
+
+/* ctm to translate from pcl space to plu space */
+ int
+hpgl_set_pcl_to_plu_ctm(hpgl_state_t *pgls)
+{
+ hpgl_real_t swap_temp;
+ hpgl_real_t fw_plu = (coord_2_plu(pgls->g.picture_frame_width));
+ hpgl_real_t fh_plu = (coord_2_plu(pgls->g.picture_frame_height));
+
+ hpgl_call(pcl_set_ctm(pgls, false));
+ if ( pgls->personality == rtl ) {
+ /* If plot length >= width, y increases across the short
+ edge and x increases down the plot. Rotate the pcl
+ coordinate system -90, scale and flip the x axis. Else
+ for plot width > length the origin is in the upper
+ right and x increases going to the left and y increases
+ going down, translate the pcl coordinate system by the
+ picture frame width, scale and flip x.
+ PLOTSIZEROTATE==OFF forces -90 rotation, top/left 0,0
+ not legal pcl default is ON */
+ if ( pgls->g.picture_frame_height >= pgls->g.picture_frame_width ||
+ !pjl_proc_compare(pgls->pjls,
+ pjl_proc_get_envvar(pgls->pjls, "plotsizerotate"), "on")) {
+ hpgl_call(gs_rotate(pgls->pgs, -90));
+ /* swap picture frame height and width
+ * for the translation portion of the next RO cmd rotation.
+ */
+ swap_temp = fw_plu;
+ fw_plu = fh_plu;
+ fh_plu = swap_temp;
+ }
+ else
+ hpgl_call(gs_translate(pgls->pgs, pgls->g.picture_frame_width, 0));
+ hpgl_call(gs_scale(pgls->pgs, -(7200.0/1016.0), (7200.0/1016.0)));
+ } else {
+ hpgl_call(gs_translate(pgls->pgs,
+ pgls->g.picture_frame.anchor_point.x,
+ pgls->g.picture_frame.anchor_point.y));
+ /* move the origin */
+ hpgl_call(gs_translate(pgls->pgs, 0, pgls->g.picture_frame_height));
+ /* scale to plotter units and a flip for y */
+ hpgl_call(gs_scale(pgls->pgs, (7200.0/1016.0), -(7200.0/1016.0)));
+ /* account for rotated coordinate system */
+ }
+ hpgl_call(gs_rotate(pgls->pgs, pgls->g.rotation));
+ {
+ switch (pgls->g.rotation)
+ {
+ case 0 :
+ hpgl_call(gs_translate(pgls->pgs, 0, 0));
+ break;
+ case 90 :
+ hpgl_call(gs_translate(pgls->pgs, 0, -fw_plu));
+ break;
+ case 180 :
+ hpgl_call(gs_translate(pgls->pgs, -fw_plu, -fh_plu));
+ break;
+ case 270 :
+ hpgl_call(gs_translate(pgls->pgs, -fh_plu, 0));
+ break;
+ }
+ }
+ hpgl_call(hpgl_set_picture_frame_scaling(pgls));
+ {
+ gs_matrix mat;
+ gs_currentmatrix(pgls->pgs, &mat);
+ mat.ty = floor(mat.ty); mat.tx = floor(mat.tx);
+ gs_setmatrix(pgls->pgs, &mat);
+ }
+ hpgl_call(gs_setdotorientation(pgls->pgs));
+ return 0;
+}
+
+/* Set the CTM to map PLU to device units, regardless of scaling. */
+/* (We need this for labels when scaling is on.) */
+ int
+hpgl_set_plu_ctm(hpgl_state_t *pgls)
+{
+ hpgl_call(hpgl_set_pcl_to_plu_ctm(pgls));
+ return 0;
+}
+
+ int
+hpgl_compute_user_units_to_plu_ctm(const hpgl_state_t *pgls, gs_matrix *pmat)
+{ floatp origin_x = pgls->g.P1.x, origin_y = pgls->g.P1.y;
+
+ switch ( pgls->g.scaling_type )
+ {
+ case hpgl_scaling_none:
+ gs_make_identity(pmat);
+ break;
+ case hpgl_scaling_point_factor:
+ hpgl_call(gs_make_translation(origin_x, origin_y, pmat));
+ hpgl_call(gs_matrix_scale(pmat, pgls->g.scaling_params.factor.x,
+ pgls->g.scaling_params.factor.y, pmat));
+ hpgl_call(gs_matrix_translate(pmat,
+ -pgls->g.scaling_params.pmin.x,
+ -pgls->g.scaling_params.pmin.y, pmat));
+ break;
+ default:
+ /*case hpgl_scaling_anisotropic:*/
+ /*case hpgl_scaling_isotropic:*/
+ {
+ floatp window_x = pgls->g.P2.x - origin_x,
+ range_x = pgls->g.scaling_params.pmax.x -
+ pgls->g.scaling_params.pmin.x,
+ scale_x = window_x / range_x;
+ floatp window_y = pgls->g.P2.y - origin_y,
+ range_y = pgls->g.scaling_params.pmax.y -
+ pgls->g.scaling_params.pmin.y,
+ scale_y = window_y / range_y;
+#define SIGN(x) ((x) < 0 ? -1.0 : 1.0)
+
+ if ( pgls->g.scaling_type == hpgl_scaling_isotropic ) {
+ if ( fabs(scale_x) > fabs(scale_y) ) { /* Reduce the X scaling. */
+ origin_x += SIGN(scale_x) * (range_x * (fabs(scale_x) - fabs(scale_y)) *
+ (pgls->g.scaling_params.left / 100.0));
+ scale_x = SIGN(scale_x) * fabs(scale_y);
+ } else { /* Reduce the Y scaling. */
+ origin_y += SIGN(scale_y) * (range_y * (fabs(scale_y) - fabs(scale_x)) *
+ (pgls->g.scaling_params.bottom / 100.0));
+ scale_y = SIGN(scale_y) * fabs(scale_x);
+ }
+ }
+
+ hpgl_call(gs_make_translation(origin_x, origin_y, pmat));
+ hpgl_call(gs_matrix_scale(pmat, scale_x, scale_y, pmat));
+ hpgl_call(gs_matrix_translate(pmat,
+ -pgls->g.scaling_params.pmin.x,
+ -pgls->g.scaling_params.pmin.y, pmat));
+ break;
+ }
+ }
+ return 0;
+
+}
+
+/* set up ctm's. Uses the current render mode to figure out which ctm
+ is appropriate */
+ int
+hpgl_set_ctm(hpgl_state_t *pgls)
+{
+ hpgl_call(hpgl_set_plu_ctm(pgls));
+ if ( pgls->g.scaling_type != hpgl_scaling_none ) {
+ gs_matrix mat;
+ hpgl_call(hpgl_compute_user_units_to_plu_ctm(pgls, &mat));
+ hpgl_call(gs_concat(pgls->pgs, &mat));
+ hpgl_call(gs_currentmatrix(pgls->pgs, &mat));
+ mat.tx = round(mat.tx);
+ mat.ty = round(mat.ty);
+ hpgl_call(gs_setmatrix(pgls->pgs, &mat));
+ }
+ return 0;
+}
+
+/* Compute the pattern length. Normally, if the pattern length is
+ relative it is 4% of the diagonal distance from P1 to P2, for
+ isotropic scaling we need 4% of the distance of the plotter unit
+ equivalent of the scaling SC coordinates xmin, xmax, ymin, and
+ ymax.. */
+static floatp
+hpgl_get_line_pattern_length(hpgl_state_t *pgls)
+{
+ /* dispense with the unusual "isotropic relative" case first. The
+ normal calculation afterward is straightforward */
+ if ( (pgls->g.line.current.pattern_length_relative == 0) &&
+ (pgls->g.scaling_type == hpgl_scaling_isotropic) ) {
+ /* the box in user space */
+ gs_rect isotropic_user_box;
+ /* box in plotter units we compute 4% of the diagonal of this box */
+ gs_rect isotropic_plu_box;
+ gs_matrix user_to_plu_mat;
+
+ hpgl_call(hpgl_compute_user_units_to_plu_ctm(pgls, &user_to_plu_mat));
+
+ isotropic_user_box.p = pgls->g.scaling_params.pmin;
+ isotropic_user_box.q = pgls->g.scaling_params.pmax;
+
+ hpgl_call(gs_bbox_transform(&isotropic_user_box,
+ &user_to_plu_mat,
+ &isotropic_plu_box));
+
+ return (pgls->g.line.current.pattern_length / 100.0) *
+ hpgl_compute_distance(isotropic_plu_box.p.x,
+ isotropic_plu_box.p.y,
+ isotropic_plu_box.q.x,
+ isotropic_plu_box.q.y);
+ }
+
+ /* simple case 4% of the diagonal of P1 and P2 or absolute in millimeters */
+ return ((pgls->g.line.current.pattern_length_relative == 0) ?
+ ((pgls->g.line.current.pattern_length / 100.0) *
+ hpgl_compute_distance(pgls->g.P1.x,
+ pgls->g.P1.y,
+ pgls->g.P2.x,
+ pgls->g.P2.y) *
+ hpgl_width_scale(pgls)) :
+ (mm_2_plu(pgls->g.line.current.pattern_length)));
+}
+
+static int
+hpgl_set_graphics_dash_state(hpgl_state_t *pgls)
+{
+ int entry = abs(pgls->g.line.current.type);
+ bool adaptive;
+ const hpgl_line_type_t *pat;
+ float length;
+ float pattern[20];
+ float offset;
+ int count;
+ int i;
+
+ /* handle the simple case (no dash) and return */
+ if ( pgls->g.line.current.is_solid )
+ {
+ /* use a 0 count pattern to turn off dashing in case it is
+ set, and allow drawing dots */
+ hpgl_call(gs_setdash(pgls->pgs, pattern, 0, 0));
+ hpgl_call(gs_setdotlength(pgls->pgs, 0.00098, true));
+ return 0;
+ }
+
+ hpgl_call(gs_setdotlength(pgls->pgs, 0.0, false));
+ if ( entry == 0 )
+ {
+ /* dot length NOTE this is in absolute 1/72" units not
+ user space */
+ /* Use an adaptive pattern with an infinitely long segment length
+ to get the dots drawn just at the ends of lines. */
+ pattern[0] = 0;
+ pattern[1] = 1.0e6; /* "infinity" */
+ hpgl_call(gs_setdash(pgls->pgs, pattern, 2, 0));
+ gs_setdashadapt(pgls->pgs, true);
+ return 0;
+ }
+
+ adaptive = ( pgls->g.line.current.type < 0 );
+ pat = ((adaptive) ?
+ (&pgls->g.adaptive_line_type[entry - 1]) :
+ (&pgls->g.fixed_line_type[entry - 1]));
+
+ length = hpgl_get_line_pattern_length(pgls);
+ gs_setdashadapt(pgls->pgs, adaptive);
+ /*
+ * The graphics library interprets odd pattern counts differently
+ * from GL: if the pattern count is odd, we need to do something
+ * a little special.
+ */
+ count = pat->count;
+ for ( i = 0; i < count; i++ )
+ pattern[i] = length * pat->gap[i];
+ offset = pgls->g.line.current.pattern_offset * hpgl_get_line_pattern_length(pgls);
+ if ( count & 1 )
+ {
+ /*
+ * Combine the last gap with the first one, and change the
+ * offset to compensate. NOTE: this doesn't work right with
+ * adaptive line type: we may need to change the library to
+ * make this work properly.
+ */
+ --count;
+ pattern[0] += pattern[count];
+ offset += pattern[count];
+ }
+
+ hpgl_call(gs_setdash(pgls->pgs, pattern, count, offset));
+
+ return 0;
+}
+
+/* catch pen not being in the palette */
+ int
+hpgl_get_selected_pen(hpgl_state_t *pgls)
+{
+ /* get the current pen */
+ int pen = pgls->g.pen.selected;
+ /* 0 is the first pen */
+ int num_entries = pcl_palette_get_num_entries(pgls->ppalet);
+ /* this is bad */
+ if ((pen < 0) || (pen >= num_entries)) {
+ pen %= num_entries;
+ if (pen < 0)
+ pen += num_entries;
+ }
+ return pen;
+}
+
+/*
+ * set up joins, caps, miter limit, and line width
+ */
+static int
+hpgl_set_graphics_line_attribute_state(
+ hpgl_state_t * pgls,
+ hpgl_rendering_mode_t render_mode
+)
+{
+ const gs_line_cap cap_map[] = { gs_cap_butt, /* 0 not supported */
+ gs_cap_butt, /* 1 butt end */
+ gs_cap_square, /* 2 square end */
+ gs_cap_triangle, /* 3 triag. end */
+ gs_cap_round /* 4 round cap */
+ };
+
+ const gs_line_join join_map[] = { gs_join_none, /* 0 not supported */
+ gs_join_miter, /* 1 mitered */
+ gs_join_miter, /* 2 mitrd/bevld */
+ gs_join_triangle, /* 3 triag. join */
+ gs_join_round, /* 4 round join */
+ gs_join_bevel, /* 5 bevel join */
+ gs_join_none /* 6 no join */
+ };
+ const float * widths = pcl_palette_get_pen_widths(pgls->ppalet);
+ floatp pen_wid = widths[hpgl_get_selected_pen(pgls)];
+
+ gs_setfilladjust(pgls->pgs, 0.0, 0.0);
+
+ /* character mode has already set up all of this information in
+ the build character routine for the font */
+ if ( render_mode == hpgl_rm_character )
+ return 0;
+ /*
+ * HP appears to use default line attributes if the the pen
+ * width is less than or equal to .35mm or 14.0 plu. This
+ * is not documented PCLTRM. Pen widths are maintained in
+ * plotter units
+ */
+ if ( render_mode != hpgl_rm_character && pen_wid <= 14.0 ) {
+ hpgl_call(gs_setlinejoin(pgls->pgs, gs_join_miter));
+ hpgl_call(gs_setlinecap(pgls->pgs, gs_cap_butt));
+ hpgl_call(gs_setlinewidth(pgls->pgs, pen_wid));
+ hpgl_call(gs_setmiterlimit(pgls->pgs, 5.0));
+ return 0;
+ }
+
+ switch (render_mode) {
+
+ case hpgl_rm_character:
+ case hpgl_rm_polygon:
+ case hpgl_rm_clip_and_fill_polygon:
+ hpgl_call(gs_setlinejoin(pgls->pgs, gs_join_round));
+ hpgl_call(gs_setlinecap(pgls->pgs, gs_cap_round));
+ break;
+
+ case hpgl_rm_vector_fill:
+ case hpgl_rm_vector:
+vector:
+ hpgl_call(gs_setlinejoin(pgls->pgs, join_map[pgls->g.line.join]));
+ hpgl_call(gs_setlinecap(pgls->pgs, cap_map[pgls->g.line.cap]));
+ hpgl_call(gs_setlinewidth(pgls->pgs, pen_wid));
+ break;
+
+ default:
+ /* shouldn't happen; we must have a mode to properly parse hpgl file. */
+ dprintf("warning no hpgl rendering mode set using vector mode\n");
+ goto vector;
+ }
+
+#ifdef COMMENT
+ /* I do not remember the rational for the large miter */
+ hpgl_call(gs_setmiterlimit( pgls->pgs,
+ (pgls->g.line.join == 1)
+ ? 5000.0
+ : pgls->g.miter_limit
+ ) );
+#endif
+ hpgl_call(gs_setmiterlimit(pgls->pgs, pgls->g.miter_limit));
+ return 0;
+}
+
+/*
+ * A bounding box for the current polygon -- used for HPGL/2 vector
+ * fills.
+ */
+static int
+hpgl_polyfill_bbox(
+ hpgl_state_t * pgls,
+ gs_rect * bbox
+)
+{
+ /* get the bounding box for the current path / polygon */
+ hpgl_call(gs_pathbbox(pgls->pgs, bbox));
+ return 0;
+}
+
+/* set up an hpgl clipping region -- intersection of IW command and
+ picture frame. */
+ int
+hpgl_set_clipping_region(hpgl_state_t *pgls, hpgl_rendering_mode_t render_mode)
+{
+ /* if we are doing vector fill a clipping path has already
+ been set up using the last polygon */
+ if ( render_mode == hpgl_rm_vector_fill )
+ return 0;
+ else
+ {
+ gs_fixed_rect fixed_box;
+ gs_rect pcl_clip_box;
+ gs_rect dev_clip_box;
+ gs_matrix save_ctm;
+ gs_matrix pcl_ctm;
+
+ /* get pcl to device ctm and restore the current ctm */
+ hpgl_call(gs_currentmatrix(pgls->pgs, &save_ctm));
+ hpgl_call(pcl_set_ctm(pgls, false));
+ hpgl_call(gs_currentmatrix(pgls->pgs, &pcl_ctm));
+ hpgl_call(gs_setmatrix(pgls->pgs, &save_ctm));
+ /* find the clipping region defined by the picture frame
+ which is defined in pcl coordinates */
+ pcl_clip_box.p.x = pgls->g.picture_frame.anchor_point.x;
+ pcl_clip_box.p.y = pgls->g.picture_frame.anchor_point.y;
+ pcl_clip_box.q.x = pcl_clip_box.p.x + pgls->g.picture_frame_width;
+ pcl_clip_box.q.y = pcl_clip_box.p.y + pgls->g.picture_frame_height;
+
+ hpgl_call(gs_bbox_transform(&pcl_clip_box,
+ &pcl_ctm,
+ &dev_clip_box));
+ /* the clip box defined by the picture frame appears to be
+ open and the clip box defined by IW is closed */
+ dev_clip_box.q.x += 1.0;
+ dev_clip_box.q.y += 1.0;
+ /* if the clipping window is active calculate the new clip
+ box derived from IW and the intersection of the device
+ space boxes replace the current box. Note that IW
+ coordinates are in current units and and the picture
+ frame in pcl coordinates. */
+ if ( pgls->g.soft_clip_window.active ) {
+ gs_rect dev_soft_window_box;
+ gs_matrix ctm;
+ if (pgls->g.soft_clip_window.isbound) {
+ /* we need the plotter unit matrix */
+ hpgl_call(gs_currentmatrix(pgls->pgs, &save_ctm));
+ hpgl_call(hpgl_set_plu_ctm(pgls));
+ hpgl_call(gs_currentmatrix(pgls->pgs, &ctm));
+ hpgl_call(gs_setmatrix(pgls->pgs, &save_ctm));
+ } else {
+ hpgl_call(gs_currentmatrix(pgls->pgs, &ctm));
+ }
+ hpgl_call(gs_bbox_transform(&pgls->g.soft_clip_window.rect,
+ &ctm,
+ &dev_soft_window_box));
+ /* Enlarge IW by 1 device dot to compensate for it's
+ 'on the line' is not clipped behavior. */
+ dev_clip_box.p.x = max(dev_clip_box.p.x, dev_soft_window_box.p.x - 1.0);
+ dev_clip_box.p.y = max(dev_clip_box.p.y, dev_soft_window_box.p.y - 1.0);
+ dev_clip_box.q.x = min(dev_clip_box.q.x, dev_soft_window_box.q.x + 1.0);
+ dev_clip_box.q.y = min(dev_clip_box.q.y, dev_soft_window_box.q.y + 1.0);
+
+ }
+ /* convert intersection box to fixed point and clip */
+ fixed_box.p.x = float2fixed(floor(dev_clip_box.p.x));
+ fixed_box.p.y = float2fixed(floor(dev_clip_box.p.y));
+ fixed_box.q.x = float2fixed(ceil(dev_clip_box.q.x));
+ fixed_box.q.y = float2fixed(ceil(dev_clip_box.q.y));
+ /* intersect with pcl clipping region */
+ fixed_box.p.x = max(fixed_box.p.x, pgls->xfm_state.dev_print_rect.p.x);
+ fixed_box.p.y = max(fixed_box.p.y, pgls->xfm_state.dev_print_rect.p.y);
+ fixed_box.q.x = min(fixed_box.q.x, pgls->xfm_state.dev_print_rect.q.x);
+ fixed_box.q.y = min(fixed_box.q.y, pgls->xfm_state.dev_print_rect.q.y);
+ hpgl_call(gx_clip_to_rectangle(pgls->pgs, &fixed_box));
+ }
+ return 0;
+}
+
+/* Plot one vector for vector fill all these use absolute coordinates. */
+static int
+hpgl_draw_vector_absolute(
+ hpgl_state_t * pgls,
+ hpgl_real_t x0,
+ hpgl_real_t y0,
+ hpgl_real_t x1,
+ hpgl_real_t y1,
+ hpgl_rendering_mode_t render_mode
+)
+{
+ bool set_ctm = (render_mode != hpgl_rm_character);
+
+ hpgl_call(hpgl_add_point_to_path( pgls,
+ x0,
+ y0,
+ hpgl_plot_move_absolute,
+ set_ctm
+ ) );
+ hpgl_call(hpgl_add_point_to_path( pgls,
+ x1,
+ y1,
+ hpgl_plot_draw_absolute,
+ set_ctm
+ ) );
+ hpgl_call(hpgl_draw_current_path(pgls, hpgl_rm_vector_fill));
+ return 0;
+}
+
+static int
+hpgl_get_adjusted_corner(
+ hpgl_real_t x_fill_increment,
+ hpgl_real_t y_fill_increment,
+ gs_rect * bbox,
+ gs_point * current_anchor_corner,
+ gs_point * adjusted_anchor_corner
+)
+{
+ adjusted_anchor_corner->x = current_anchor_corner->x;
+ adjusted_anchor_corner->y = current_anchor_corner->y;
+ /* account for anchor corner greater than origin */
+ if (x_fill_increment != 0) {
+ while (adjusted_anchor_corner->x > bbox->p.x)
+ adjusted_anchor_corner->x -= x_fill_increment;
+ } else if (adjusted_anchor_corner->x > bbox->p.x)
+ adjusted_anchor_corner->x = bbox->p.x;
+ if (y_fill_increment != 0) {
+ while (adjusted_anchor_corner->y > bbox->p.y)
+ adjusted_anchor_corner->y -= y_fill_increment;
+ } else if (adjusted_anchor_corner->y > bbox->p.y)
+ adjusted_anchor_corner->y = bbox->p.y;
+ return 0;
+}
+
+static void
+hpgl_alternate_line_pattern_offset(hpgl_state_t *pgls, uint lines_filled)
+{
+ if ( lines_filled & 1 )
+ pgls->g.line.current.pattern_offset = 0.5;
+ else
+ pgls->g.line.current.pattern_offset = 0.0;
+}
+
+/* this definition is used to factor out the effect of the orientation
+ of the pcl logical page. HP printers prior to the 4500 series
+ oriented the hpgl/2 fill lines relative to the logical page
+ orientation (expected result), later printers do not. Comment out
+ the following definition to support the old (expected) behavior. */
+#define FILL_IGNORES_PCL_ORIENTATION
+
+/*
+ * HAS should replicate lines beginning at the anchor corner to +X and
+ * +Y. Not quite right - anchor corner not yet supported.
+ * pgls->g.anchor_corner needs to be used to set dash offsets
+ */
+static int
+hpgl_polyfill(
+ hpgl_state_t * pgls,
+ hpgl_rendering_mode_t render_mode
+)
+{
+ hpgl_real_t diag_mag, endx, endy;
+ gs_sincos_t sincos;
+ gs_point start;
+
+#define sin_dir sincos.sin
+#define cos_dir sincos.cos
+
+ gs_rect bbox;
+ gs_matrix user_to_plu_mat;
+ hpgl_pen_state_t saved_pen_state;
+ hpgl_real_t x_fill_increment, y_fill_increment;
+ hpgl_FT_pattern_source_t type = pgls->g.fill.type;
+ bool cross = (type == hpgl_FT_pattern_two_lines);
+ const hpgl_hatch_params_t * params = (cross ? &pgls->g.fill.param.crosshatch
+ : &pgls->g.fill.param.hatch);
+ gs_point spacing;
+#ifdef FILL_IGNORES_PCL_ORIENTATION
+ hpgl_real_t direction =
+ params->angle + pgls->xfm_state.lp_orient * 90;
+#else
+ hpgl_real_t direction = params->angle;
+#endif
+
+ hpgl_real_t unscaled_direction;
+ float saved_line_pattern_offset = pgls->g.line.current.pattern_offset;
+ int lines_filled;
+
+ /* spacing is always relevant to the scaling of the x-axis. It
+ can be specified in user space if provided to FT or by default
+ it is 1% of the distance from P1 to P2 */
+ spacing.x = params->spacing;
+ /* save the pen position */
+ hpgl_save_pen_state(pgls, &saved_pen_state, hpgl_pen_pos);
+ hpgl_call(hpgl_compute_user_units_to_plu_ctm(pgls, &user_to_plu_mat));
+ if (params->spacing == 0) {
+ /* Per TRM 22-12, use 1% of the P1/P2 distance. */
+
+ spacing.x = 0.01 * hpgl_compute_distance( pgls->g.P1.x,
+ pgls->g.P1.y,
+ pgls->g.P2.x,
+ pgls->g.P2.y
+ );
+
+ /* 1% is in plu, convert back to user units */
+ spacing.y = spacing.x / fabs(user_to_plu_mat.yy);
+ spacing.x /= fabs(user_to_plu_mat.xx);
+ }
+ else {
+ /* set spacing.y based on ratio of x/y scaling, still in user units */
+ spacing.y = spacing.x * fabs(user_to_plu_mat.xx) / fabs(user_to_plu_mat.yy);
+ }
+ /* get the bounding box */
+ hpgl_call(hpgl_polyfill_bbox(pgls, &bbox));
+ /*
+ * if the line width exceeds the spacing we use the line width
+ * to avoid overlapping of the fill lines. HAS this can be
+ * integrated with the logic above for spacing as not to
+ * duplicate alot of code.
+ */
+ {
+ const float * widths = pcl_palette_get_pen_widths(pgls->ppalet);
+ hpgl_real_t line_width = widths[hpgl_get_selected_pen(pgls)];
+
+ line_width /= min(fabs(user_to_plu_mat.xx), fabs(user_to_plu_mat.yy));
+ if (line_width >= spacing.x || line_width >= spacing.y) {
+ hpgl_call(hpgl_draw_current_path(pgls, hpgl_rm_polygon));
+ return 0;
+ }
+ }
+
+ /* get rid of the current path */
+
+ hpgl_call(hpgl_clear_current_path(pgls));
+
+
+ start: /* Change direction, come back and draw crosshatch lines. */
+
+ /* hatch angles are isotropic; ie 45degrees is not affected by y scale != x scale.
+ unscaled_direction ::= user requested angle
+ direction ::= the angle that when scaled will generate user requested angle */
+
+ /* save unscaled angle */
+ unscaled_direction = direction;
+
+ /* not necessery if direction is orthogonal */
+ if ( !equal(fmod( direction, 90 ), 0 ) ) {
+ hpgl_real_t slope, scaled_slope;
+ gs_sincos_degrees(direction, &sincos);
+ /* take the tangent by dividing sin by cos. Since we know
+ the angle is non-orthogonal the cosine is non-zero */
+ slope = sin_dir / cos_dir;
+ /* scale the slope by the ratio of the scaling factors */
+ scaled_slope = (user_to_plu_mat.xx / user_to_plu_mat.yy) * slope;
+ /* preserved angle in user space */
+ direction = radians_to_degrees * atan(scaled_slope);
+ }
+
+ lines_filled = 0;
+
+ /* spacing is done with the unscaled angle
+ spacing.x .y is already scaled hence the unscaled angle is used.
+ scale * spacing * sin(unscaled_angle) */
+ gs_sincos_degrees(unscaled_direction, &sincos);
+ if (sin_dir < 0)
+ sin_dir = -sin_dir, cos_dir = -cos_dir; /* ensure y_inc >= 0 */
+ x_fill_increment = (sin_dir != 0) ? fabs(spacing.x / sin_dir) : 0;
+ y_fill_increment = (cos_dir != 0) ? fabs(spacing.y / cos_dir) : 0;
+
+ /* scaled angle is used for end point calculation
+ distance * sin( scaled_angle )
+ scale is applyed once and only once per calculation */
+ gs_sincos_degrees(direction, &sincos);
+ if (sin_dir < 0)
+ sin_dir = -sin_dir, cos_dir = -cos_dir; /* ensure y_inc >= 0 */
+
+ hpgl_call(hpgl_get_adjusted_corner( x_fill_increment,
+ y_fill_increment,
+ &bbox,
+ &pgls->g.anchor_corner,
+ &start
+ ) );
+
+ /*
+ * calculate the diagonals magnitude. Note we clip this
+ * latter in the library. If desired we could clip to the
+ * actual bbox here to save processing time. For now we simply
+ * draw all fill lines using the diagonals magnitude
+ */
+ diag_mag = hpgl_compute_distance(start.x, start.y, bbox.q.x, bbox.q.y);
+ endx = (diag_mag * cos_dir) + start.x;
+ endy = (diag_mag * sin_dir) + start.y;
+ hpgl_alternate_line_pattern_offset(pgls, lines_filled++);
+ hpgl_call(hpgl_draw_vector_absolute( pgls,
+ start.x,
+ start.y,
+ endx,
+ endy,
+ render_mode
+ ) );
+ /* Travel along +x using current spacing. */
+ if (x_fill_increment != 0) {
+ while ( endx += x_fill_increment,
+ (start.x += x_fill_increment) <= bbox.q.x ) {
+
+ hpgl_alternate_line_pattern_offset(pgls, lines_filled++);
+ hpgl_call(hpgl_draw_vector_absolute( pgls,
+ start.x,
+ start.y,
+ endx,
+ endy,
+ render_mode
+ ) );
+ }
+ }
+
+ /* Travel along +Y similarly. */
+ if (y_fill_increment != 0) {
+ /*
+ * If the slope is negative, we have to travel along the right
+ * edge of the box rather than the left edge. Fortuitously,
+ * the X loop left everything set up exactly right for this case.
+ */
+ if (cos_dir >= 0) {
+ hpgl_call(hpgl_get_adjusted_corner( x_fill_increment,
+ y_fill_increment,
+ &bbox,
+ &pgls->g.anchor_corner,
+ &start
+ ) );
+ endx = (diag_mag * cos_dir) + start.x;
+ endy = (diag_mag * sin_dir) + start.y;
+ } else
+ start.y -= y_fill_increment, endy -= y_fill_increment;
+
+ while ( endy += y_fill_increment,
+ (start.y += y_fill_increment) <= bbox.q.y ) {
+ hpgl_alternate_line_pattern_offset(pgls, lines_filled++);
+ hpgl_call(hpgl_draw_vector_absolute( pgls,
+ start.x,
+ start.y,
+ endx,
+ endy,
+ render_mode
+ ) );
+ }
+
+ }
+ if (cross) {
+ /* go back and draw the perpendicular lines, +90degress */
+ cross = false;
+ direction = unscaled_direction + 90;
+ if ( direction >= 180 )
+ direction -= 180;
+ goto start;
+ }
+ hpgl_restore_pen_state(pgls, &saved_pen_state, hpgl_pen_pos);
+ pgls->g.line.current.pattern_offset = saved_line_pattern_offset;
+ return 0;
+
+#undef sin_dir
+#undef cos_dir
+
+}
+
+/* gl/2 vector filled objects always have a white background. It can
+ be either a transparent or white. In the former case we don't have
+ to do anything. We expect the fill area of the object to already
+ be defined in the graphics state. */
+static int
+hpgl_fill_polyfill_background(hpgl_state_t *pgls)
+{
+ /* conditionally mark page as dirty */
+ pcl_mark_page_for_path(pgls);
+ /* if we are drawing on a transparent background */
+ if ( pgls->g.source_transparent )
+ return 0;
+ /* preserve the current foreground color */
+ hpgl_call(hpgl_gsave(pgls));
+ /* fill a white region. */
+ hpgl_call(gs_setgray(pgls->pgs, 1.0));
+ hpgl_call(gs_fill(pgls->pgs));
+ /* restore the foreground color */
+ hpgl_call(hpgl_grestore(pgls));
+ return 0;
+}
+
+static int
+hpgl_polyfill_using_current_line_type(
+ hpgl_state_t * pgls,
+ hpgl_rendering_mode_t render_mode
+)
+{
+ /* gsave and grestore used to preserve the clipping region */
+ hpgl_call(hpgl_gsave(pgls));
+
+ /*
+ * Use the current path to set up a clipping path
+ * beginning at the anchor corner replicate lines
+ */
+ if (pgls->g.fill_type == hpgl_even_odd_rule)
+ hpgl_call(gs_eoclip(pgls->pgs));
+ else
+ hpgl_call(gs_clip(pgls->pgs));
+ hpgl_call(hpgl_fill_polyfill_background(pgls));
+ hpgl_call(hpgl_polyfill(pgls, render_mode));
+ hpgl_call(hpgl_grestore(pgls));
+ return 0;
+}
+
+static gs_rop3_t
+hpgl_rop(hpgl_state_t *pgls, hpgl_rendering_mode_t render_mode)
+{
+ gs_rop3_t rop = pgls->logical_op;
+ if ( render_mode == hpgl_rm_vector || render_mode == hpgl_rm_vector_fill) {
+ if ( rop == 0 || rop == 160 || rop == 170 || rop == 240 || rop == 250 || rop == 255 ) {
+ return rop;
+ } else {
+ return rop3_default;
+ }
+ }
+ return rop;
+}
+
+
+ int
+hpgl_set_drawing_color(
+ hpgl_state_t * pgls,
+ hpgl_rendering_mode_t render_mode
+)
+{
+ int code = 0;
+ pcl_pattern_set_proc_t set_proc;
+ byte pixel_placement_mode = 0;
+ switch (render_mode) {
+
+ case hpgl_rm_clip_and_fill_polygon:
+ hpgl_call(hpgl_polyfill_using_current_line_type(pgls, render_mode));
+ return 0;
+
+ case hpgl_rm_character:
+ switch (pgls->g.character.fill_mode) {
+
+ case hpgl_char_solid_edge: /* fall through */
+ case hpgl_char_edge:
+ set_proc = pcl_pattern_get_proc_FT(hpgl_FT_pattern_solid_pen1);
+ code = set_proc(pgls, hpgl_get_selected_pen(pgls), false);
+ break;
+
+ case hpgl_char_fill:
+ case hpgl_char_fill_edge:
+ if ( (pgls->g.fill.type == hpgl_FT_pattern_one_line) ||
+ (pgls->g.fill.type == hpgl_FT_pattern_two_lines) ) {
+ hpgl_call(hpgl_polyfill_using_current_line_type( pgls,
+ render_mode
+ ) );
+ return 0;
+ } else
+ goto fill;
+
+ default:
+ dprintf("hpgl_set_drawing_color: internal error illegal fill\n");
+ return 0;
+ }
+ break;
+
+ /* fill like a polygon */
+ case hpgl_rm_polygon:
+fill:
+ /* pixel placement mode is only relevant to polygon fills */
+ pixel_placement_mode = pgls->pp_mode;
+ set_proc = pcl_pattern_get_proc_FT(pgls->g.fill.type);
+ switch (pgls->g.fill.type) {
+
+ case hpgl_FT_pattern_solid_pen1:
+ case hpgl_FT_pattern_solid_pen2:
+ /*
+ * this is documented incorrectly PCLTRM 22-12 says
+ * these should be solid black but they are actually
+ * set to the value of the current pen - (i.e pen 0 is
+ * solid white
+ */
+ code = set_proc(pgls, hpgl_get_selected_pen(pgls), false);
+ break;
+
+ case hpgl_FT_pattern_one_line:
+ case hpgl_FT_pattern_two_lines:
+ set_proc = pcl_pattern_get_proc_FT(hpgl_FT_pattern_solid_pen1);
+ code = set_proc(pgls, hpgl_get_selected_pen(pgls), false);
+ break;
+
+ case hpgl_FT_pattern_cross_hatch:
+ code = set_proc( pgls,
+ pgls->g.fill.param.pattern_type,
+ hpgl_get_selected_pen(pgls)
+ );
+ break;
+
+ case hpgl_FT_pattern_shading:
+ code = set_proc( pgls,
+ pgls->g.fill.param.shading,
+ hpgl_get_selected_pen(pgls)
+ );
+ break;
+
+ case hpgl_FT_pattern_user_defined:
+ code = set_proc( pgls,
+ pgls->g.fill.param.pattern_id,
+ hpgl_get_selected_pen(pgls)
+ );
+ break;
+
+ case hpgl_FT_pattern_RF:
+ /* pcl5e does not care about the current pen it always uses black (1). */
+ if ( pgls->personality == pcl5e )
+ code = set_proc( pgls,
+ pgls->g.fill.param.user_defined.pattern_index,
+ 1 );
+ else
+ code = set_proc( pgls,
+ pgls->g.fill.param.user_defined.pattern_index,
+ (pgls->g.fill.param.user_defined.use_current_pen
+ ? hpgl_get_selected_pen(pgls)
+ : -hpgl_get_selected_pen(pgls))
+ );
+
+ break;
+ default:
+ dprintf("hpgl_set_drawing_color: internal error illegal fill\n");
+ break;
+ }
+ break;
+
+ case hpgl_rm_vector:
+ case hpgl_rm_vector_fill:
+ set_proc = pcl_pattern_get_proc_SV(pgls->g.screen.type);
+ switch(pgls->g.screen.type) {
+
+ case hpgl_SV_pattern_solid_pen:
+ code = set_proc(pgls, hpgl_get_selected_pen(pgls), false);
+ break;
+
+ case hpgl_SV_pattern_shade:
+ code = set_proc( pgls,
+ pgls->g.screen.param.shading,
+ hpgl_get_selected_pen(pgls)
+ );
+ break;
+
+ case hpgl_SV_pattern_cross_hatch:
+ code = set_proc( pgls,
+ pgls->g.screen.param.pattern_type,
+ hpgl_get_selected_pen(pgls)
+ );
+ break;
+
+ case hpgl_SV_pattern_RF:
+ code = set_proc( pgls,
+ pgls->g.screen.param.user_defined.pattern_index,
+ (pgls->g.screen.param.user_defined.use_current_pen
+ ? hpgl_get_selected_pen(pgls)
+ : -hpgl_get_selected_pen(pgls))
+ );
+ break;
+
+ case hpgl_SV_pattern_user_defined:
+ code = set_proc( pgls,
+ pgls->g.screen.param.pattern_id,
+ hpgl_get_selected_pen(pgls)
+ );
+ break;
+
+ default:
+ dprintf("hpgl_set_drawing_color: internal error illegal fill\n");
+ break;
+ }
+ break;
+
+ default:
+ dprintf("hpgl_set_drawing_color: internal error illegal mode\n");
+ break;
+ }
+
+ if (code >= 0) {
+ gs_setrasterop(pgls->pgs, hpgl_rop(pgls, render_mode));
+ }
+ return code;
+}
+
+static int
+hpgl_set_drawing_state(
+ hpgl_state_t * pgls,
+ hpgl_rendering_mode_t render_mode
+)
+{
+ /* do dash stuff. */
+ hpgl_call(hpgl_set_graphics_dash_state(pgls));
+
+ /* joins, caps, and line width. */
+ hpgl_call(hpgl_set_graphics_line_attribute_state(pgls, render_mode));
+
+ /* set up a clipping region */
+ hpgl_call(hpgl_set_clipping_region(pgls, render_mode));
+
+ /* set up the hpgl fills (GL's source transp. is PCL's pattern trasp. */
+ pgls->pattern_transparent = pgls->g.source_transparent;
+ hpgl_call(hpgl_set_drawing_color(pgls, render_mode));
+
+ return 0;
+}
+
+ int
+hpgl_get_current_position(
+ hpgl_state_t * pgls,
+ gs_point * pt
+)
+{
+ *pt = pgls->g.pos;
+ return 0;
+}
+
+ int
+hpgl_update_carriage_return_pos(hpgl_state_t *pgls)
+{
+ pgls->g.carriage_return_pos = pgls->g.pos;
+ return 0;
+}
+
+ int
+hpgl_set_current_position(
+ hpgl_state_t * pgls,
+ gs_point * pt
+)
+{
+ pgls->g.pos = *pt;
+ return 0;
+}
+
+ int
+hpgl_add_point_to_path(
+ hpgl_state_t * pgls,
+ floatp x,
+ floatp y,
+ hpgl_plot_function_t func,
+ bool set_ctm
+)
+{
+ static int (*const gs_procs[])(gs_state *, floatp, floatp)
+ = { hpgl_plot_function_procedures };
+
+ /* HP lunacy... if we are starting a polygon path the path
+ machinery (hpgl_plot()) has already made sure the first point
+ is a pen up - if we are drawing for some other reason the flag
+ is moot - so we set it to false. */
+ pgls->g.subpolygon_started = false;
+ if (gx_path_is_null(gx_current_path(pgls->pgs))) {
+ /* Start a new GL/2 path. */
+ gs_point current_pt;
+
+ if (set_ctm)
+ hpgl_call(hpgl_set_ctm(pgls));
+ hpgl_call(gs_newpath(pgls->pgs));
+
+ /* moveto the current position */
+ hpgl_call(hpgl_get_current_position(pgls, &current_pt));
+ hpgl_call_check_lost( gs_moveto( pgls->pgs,
+ current_pt.x,
+ current_pt.y
+ ) );
+ }
+ {
+ int code = (*gs_procs[func])(pgls->pgs, x, y);
+ if (code < 0) {
+ if (code == gs_error_limitcheck)
+ hpgl_set_lost_mode(pgls, hpgl_lost_mode_entered);
+ } else {
+ gs_point point;
+
+ if (hpgl_plot_is_absolute(func))
+ hpgl_set_lost_mode(pgls, hpgl_lost_mode_cleared);
+
+ /* update hpgl's state position */
+ if (hpgl_plot_is_absolute(func)) {
+ point.x = x;
+ point.y = y;
+ } else {
+ hpgl_call(hpgl_get_current_position(pgls, &point));
+ point.x += x; point.y += y;
+ }
+ hpgl_call(hpgl_set_current_position(pgls, &point));
+ }
+ }
+ return 0;
+}
+
+/* destroy the current path. */
+ int
+hpgl_clear_current_path(hpgl_state_t *pgls)
+{
+ hpgl_call(gs_newpath(pgls->pgs));
+ return 0;
+}
+
+/* closes the current path, making the first point and last point coincident */
+ int
+hpgl_close_current_path(hpgl_state_t *pgls)
+{
+ hpgl_call(gs_closepath(pgls->pgs));
+ return 0;
+}
+
+/* converts pcl coordinate to device space and back to hpgl space */
+ int
+hpgl_add_pcl_point_to_path(hpgl_state_t *pgls, const gs_point *pcl_pt)
+{
+ gs_point dev_pt, hpgl_pt;
+
+ hpgl_call(hpgl_clear_current_path(pgls));
+ pcl_set_ctm(pgls, true);
+ hpgl_call(gs_transform(pgls->pgs, pcl_pt->x, pcl_pt->y, &dev_pt));
+ hpgl_call(hpgl_set_ctm(pgls));
+ hpgl_call(gs_itransform(pgls->pgs, dev_pt.x, dev_pt.y, &hpgl_pt));
+ hpgl_call(hpgl_add_point_to_path(pgls, hpgl_pt.x, hpgl_pt.y,
+ hpgl_plot_move_absolute, true));
+ return 0;
+}
+
+static floatp
+compute_chord_angle(floatp chord_angle)
+{
+ floatp ca = fmod(chord_angle, 360);
+ if (ca >= 0) {
+ if (ca > 180)
+ ca = 360 - ca;
+ if (ca < 5)
+ ca = 5;
+ } else /* negative ca */ {
+ if (ca > -5)
+ ca = -5;
+ }
+ return ca;
+}
+
+ int
+hpgl_add_arc_to_path(hpgl_state_t *pgls, floatp center_x, floatp center_y,
+ floatp radius, floatp start_angle, floatp sweep_angle,
+ floatp chord_angle, bool start_moveto, hpgl_plot_function_t draw,
+ bool set_ctm)
+{
+ floatp num_chordsf = sweep_angle / compute_chord_angle(chord_angle);
+ int num_chords = (int)(num_chordsf >= 0.0 ? ceil(num_chordsf) : floor(num_chordsf));
+ floatp integral_chord_angle = fabs(sweep_angle / num_chords);
+ int i;
+ floatp arccoord_x, arccoord_y;
+
+ (void)hpgl_compute_arc_coords(radius, center_x, center_y,
+ start_angle,
+ &arccoord_x, &arccoord_y);
+ hpgl_call(hpgl_add_point_to_path(pgls, arccoord_x, arccoord_y,
+ (draw && !start_moveto ?
+ hpgl_plot_draw_absolute :
+ hpgl_plot_move_absolute), set_ctm));
+
+ for ( i = 0; i < abs(num_chords); i++ ) {
+ if (sweep_angle > 0)
+ start_angle += integral_chord_angle;
+ else
+ start_angle -= integral_chord_angle;
+ hpgl_compute_arc_coords(radius, center_x, center_y,
+ start_angle, &arccoord_x, &arccoord_y);
+ hpgl_call(hpgl_add_point_to_path(pgls, arccoord_x, arccoord_y,
+ (draw ? hpgl_plot_draw_absolute :
+ hpgl_plot_move_absolute), set_ctm));
+ }
+ /* NB this is suspicious - why not any +- multiple of 360 if this
+ is correct at all. */
+ if (sweep_angle - 360.0 > -0.0001)
+ hpgl_call(hpgl_close_current_path(pgls));
+ return 0;
+}
+
+/* add a 3 point arc to the path */
+ int
+hpgl_add_arc_3point_to_path(hpgl_state_t *pgls, floatp start_x, floatp
+ start_y, floatp inter_x, floatp inter_y,
+ floatp end_x, floatp end_y, floatp chord_angle,
+ hpgl_plot_function_t draw)
+{
+ /* handle unusual cases as per pcltrm */
+ if ( hpgl_3_same_points(start_x, start_y,
+ inter_x, inter_y,
+ end_x, end_y) )
+ {
+ hpgl_call(hpgl_add_point_to_path(pgls, start_x, start_y, draw, true));
+ return 0;
+ }
+ if ( hpgl_3_no_intermediate(start_x, start_y,
+ inter_x, inter_y,
+ end_x, end_y) )
+ {
+ hpgl_call(hpgl_add_point_to_path(pgls, start_x, start_y, draw, true));
+ hpgl_call(hpgl_add_point_to_path(pgls, end_x, end_y, draw, true));
+ return 0;
+ }
+ if ( hpgl_3_same_endpoints(start_x, start_y,
+ inter_x, y_inter,
+ end_x, end_y) )
+ {
+ hpgl_call(hpgl_add_arc_to_path(pgls, (start_x + inter_x) / 2.0,
+ (start_y + inter_y) / 2.0,
+ (hypot((inter_x - start_x),
+ (inter_y - start_y)) / 2.0),
+ 0.0, 360.0, chord_angle, false,
+ draw, true));
+ return 0;
+ }
+
+ if ( hpgl_3_colinear_points(start_x, start_y, inter_x, inter_y, end_x, end_y) )
+ {
+ if ( hpgl_3_intermediate_between(start_x, start_y,
+ inter_x, inter_y,
+ end_x, end_y) )
+ {
+ hpgl_call(hpgl_add_point_to_path(pgls, start_x, start_y, draw, true));
+ hpgl_call(hpgl_add_point_to_path(pgls, end_x, end_x, draw, true));
+ }
+ else
+ {
+ hpgl_call(hpgl_add_point_to_path(pgls, start_x, start_y, draw, true));
+ hpgl_call(hpgl_add_point_to_path(pgls, inter_x, inter_y, draw, true));
+ hpgl_call(hpgl_add_point_to_path(pgls, end_x, end_y, draw, true));
+ }
+ return 0;
+ }
+
+ /* normal 3 point arc case */
+ {
+ hpgl_real_t center_x, center_y, radius;
+ hpgl_real_t start_angle, inter_angle, end_angle;
+ hpgl_real_t sweep_angle;
+
+ hpgl_call(hpgl_compute_arc_center(start_x, start_y,
+ inter_x, inter_y,
+ end_x, end_y,
+ &center_x, &center_y));
+
+ radius = hypot(start_x - center_x, start_y - center_y);
+ start_angle = radians_to_degrees *
+ hpgl_compute_angle(start_x - center_x, start_y - center_y);
+
+ inter_angle = radians_to_degrees *
+ hpgl_compute_angle(inter_x - center_x, inter_y - center_y);
+
+ end_angle = radians_to_degrees *
+ hpgl_compute_angle(end_x - center_x, end_y - center_y);
+ sweep_angle = end_angle - start_angle;
+
+ /*
+ * Figure out which direction to draw the arc, depending on the
+ * relative position of start, inter, and end. Case analysis
+ * shows that we should draw the arc counter-clockwise from S to
+ * E iff exactly 2 of S<I, I<E, and E<S are true, and clockwise
+ * if exactly 1 of these relations is true. (These are the only
+ * possible cases if no 2 of the points coincide.)
+ */
+
+ if ( (start_angle < inter_angle) + (inter_angle < end_angle) +
+ (end_angle < start_angle) == 1
+ )
+ {
+ if ( sweep_angle > 0 )
+ sweep_angle -= 360;
+ }
+ else
+ {
+ if ( sweep_angle < 0 )
+ sweep_angle += 360;
+ }
+
+ hpgl_call(hpgl_add_arc_to_path(pgls, center_x, center_y,
+ radius, start_angle, sweep_angle,
+ (sweep_angle < 0.0 ) ?
+ -chord_angle : chord_angle, false,
+ draw, true));
+ return 0;
+ }
+}
+
+/* Bezier's are handled a bit differently than arcs as we use the
+ gs_curveto() operator directly in lieue of flattening and using
+ gs_moveto() and gs_lineto(). */
+
+ int
+hpgl_add_bezier_to_path(hpgl_state_t *pgls, floatp x1, floatp y1,
+ floatp x2, floatp y2, floatp x3, floatp y3,
+ floatp x4, floatp y4, hpgl_plot_function_t draw)
+{
+ gx_path * ppath = gx_current_path(pgls->pgs);
+ /* Don't add superflous points in polygon mode */
+ if ( ( !pgls->g.polygon_mode ) ||
+ ( !ppath->current_subpath ) )
+ hpgl_call(hpgl_add_point_to_path(pgls, x1, y1,
+ draw ?
+ hpgl_plot_draw_absolute :
+ hpgl_plot_move_absolute, true));
+ if ( draw )
+ hpgl_call(gs_curveto(pgls->pgs, x2, y2, x3, y3, x4, y4));
+ /* update hpgl's state position to last point of the curve. */
+ {
+ gs_point point;
+ point.x = x4; point.y = y4;
+ hpgl_call(hpgl_set_current_position(pgls, &point));
+ }
+ return 0;
+}
+
+/*
+ * an implicit gl/2 style closepath. If the first and last point are
+ * the same the path gets closed. HAS - eliminate CSE gx_current_path
+ */
+ int
+hpgl_close_path(
+ hpgl_state_t * pgls
+)
+{
+ gs_point first, last;
+ gs_fixed_point first_device;
+ gx_path *ppath = gx_current_path(pgls->pgs);
+ if (!ppath->current_subpath)
+ return 0;
+ /* if we do not have a subpath there is nothing to do, get the
+ first points of the path in device space and convert to floats */
+ if ( gx_path_subpath_start_point(gx_current_path(pgls->pgs),
+ &first_device) < 0 )
+ return 0;
+ first.x = fixed2float(first_device.x);
+ first.y = fixed2float(first_device.y);
+ /* get gl/2 current position -- always current units */
+ hpgl_call(hpgl_get_current_position(pgls, &last));
+ /* convert to device space using the current ctm */
+ hpgl_call(gs_transform(pgls->pgs, last.x, last.y, &last));
+ /*
+ * if the first and last are the same close the path (i.e
+ * force gs to apply join and miter)
+ */
+ if (equal(first.x, last.x) && equal(first.y, last.y))
+ hpgl_call(gs_closepath(pgls->pgs));
+ return 0;
+}
+
+
+/* To implement centered we move the points in the path to a pixel boundary.
+ Note this routine is sensitive to the orientation of the device.
+ Since rounding 0.5 is used here and 0.5 fill adjust this can fail to reduce and
+ object in size by 1 pixel left/bottom for some user scale factors. This is not a
+ grave concern as the objects will still seam together. */
+
+int
+hpgl_set_special_pixel_placement(hpgl_state_t *pgls, hpgl_rendering_mode_t render_mode)
+{
+ if ( pgls->pp_mode == 1 ) {
+ gs_matrix default_matrix;
+ gs_point distance, adjust;
+ gx_path *ppath = gx_current_path(pgls->pgs);
+ /* arbitrary just need the signs after transformation to
+ device space */
+ gs_setfilladjust(pgls->pgs, 0, 0);
+ adjust.x = -1;
+ adjust.y = -1;
+ /* determine the adjustment in device space */
+ hpgl_call(gs_defaultmatrix(pgls->pgs, &default_matrix));
+ hpgl_call(gs_distance_transform(adjust.x, adjust.y, &default_matrix, &distance));
+ /* modify the path but first it should be "unshared" so the
+ translation (next statement) does not update the polygon
+ buffer */
+ hpgl_call(gx_path_unshare(ppath));
+
+ /* translate all points in the path by the adjustment. */
+ hpgl_call(gx_path_translate(ppath,
+ float2fixed(distance.x / fabs(distance.x)),
+ float2fixed(distance.y / fabs(distance.y))));
+ }
+ return 0;
+}
+
+/*
+ * Draw (stroke or fill) the current path.
+ */
+ int
+hpgl_draw_current_path(
+ hpgl_state_t * pgls,
+ hpgl_rendering_mode_t render_mode
+)
+{
+ gs_state * pgs = pgls->pgs;
+ pcl_pattern_set_proc_t set_proc;
+ int code = 0;
+ gx_path * ppath = gx_current_path(pgls->pgs);
+
+ /* check if we have a current path - we don't need the current
+ point */
+ if (!ppath->current_subpath) {
+ hpgl_call(hpgl_clear_current_path(pgls));
+ return 0;
+ }
+
+ if ( render_mode == hpgl_rm_vector_no_close )
+ render_mode = hpgl_rm_vector;
+ else
+ hpgl_call(hpgl_close_path(pgls));
+
+ hpgl_call(hpgl_set_drawing_state(pgls, render_mode));
+
+ switch (render_mode) {
+ case hpgl_rm_character:
+ {
+ /* HAS need to set attributes in set_drawing color (next 2) */
+
+ /* Intellifonts require eofill, but TrueType require fill. */
+ /****** HACK: look at the scaling technology of ******/
+ /****** the current font to decide. ******/
+ int (*fill)(gs_state *);
+
+ if (pgls->g.font->scaling_technology == plfst_Intellifont)
+ fill = gs_eofill;
+ else
+ fill = gs_fill;
+
+ switch (pgls->g.character.fill_mode) {
+
+ case hpgl_char_solid_edge:
+ set_proc = pcl_pattern_get_proc_FT(hpgl_FT_pattern_solid_pen1);
+ if ((code = set_proc(pgls, hpgl_get_selected_pen(pgls), false)) < 0)
+ return code;
+ hpgl_call((*fill)(pgs));
+ /* falls through */
+
+ case hpgl_char_edge:
+ if (pgls->g.bitmap_fonts_allowed)
+ break; /* no edging */
+ set_proc = pcl_pattern_get_proc_FT(hpgl_FT_pattern_solid_pen1);
+ if ((code = set_proc(pgls, hpgl_get_character_edge_pen(pgls), false)) < 0)
+ return code;
+ hpgl_call(hpgl_set_plu_ctm(pgls));
+ {
+ gs_point scale = hpgl_current_char_scale(pgls);
+ /* NB fix and comment */
+ hpgl_call(gs_setlinewidth(pgls->pgs, min(scale.x, scale.y) * 0.0375 * 0.2835));
+ }
+ pcl_mark_page_for_path(pgls);
+ hpgl_call(gs_stroke(pgls->pgs));
+ break;
+
+ case hpgl_char_fill:
+ /* the fill has already been done if the fill type is
+ hpgl/2 vector fills. This was handled when we set
+ the drawing color */
+ if ((pgls->g.fill.type != hpgl_FT_pattern_one_line) &&
+ (pgls->g.fill.type != hpgl_FT_pattern_two_lines))
+ hpgl_call((*fill)(pgs));
+ else
+ hpgl_call(hpgl_clear_current_path(pgls));
+ break;
+ case hpgl_char_fill_edge:
+ /* the fill has already been done if the fill type is
+ hpgl/2 vector fills. This was handled when we set
+ the drawing color. gsave to preserve the path for
+ subsequent edging */
+ if ((pgls->g.fill.type != hpgl_FT_pattern_one_line) &&
+ (pgls->g.fill.type != hpgl_FT_pattern_two_lines)) {
+ hpgl_call(hpgl_gsave(pgls));
+ /* all character fills appear to have 0 fill adjustment */
+ gs_setfilladjust(pgls->pgs, 0, 0);
+ hpgl_call((*fill)(pgs));
+ hpgl_call(hpgl_grestore(pgls));
+ }
+ if (pgls->g.bitmap_fonts_allowed) /* no edging */
+ hpgl_call(hpgl_clear_current_path(pgls));
+ else {
+ set_proc = pcl_pattern_get_proc_FT(hpgl_FT_pattern_solid_pen1);
+ if ((code = set_proc(pgls, hpgl_get_character_edge_pen(pgls), false)) < 0)
+ return code;
+ hpgl_call(hpgl_set_plu_ctm(pgls));
+ /* use the default raster operation for the edge. It
+ is automaticall restored next drawing operation */
+ hpgl_call(gs_setrasterop(pgls->pgs, (gs_rop3_t)252));
+ hpgl_call(gs_setlinewidth(pgls->pgs,
+ pgls->g.font_selection[pgls->g.font_selected].params.height_4ths * 0.0375));
+ hpgl_call(gs_stroke(pgls->pgs));
+ }
+ break;
+
+ }
+ break;
+ }
+ break;
+ case hpgl_rm_polygon:
+ hpgl_set_special_pixel_placement(pgls, hpgl_rm_polygon);
+ pcl_mark_page_for_path(pgls);
+ if (pgls->g.fill_type == hpgl_even_odd_rule)
+ hpgl_call(gs_eofill(pgs));
+ else /* hpgl_winding_number_rule */
+ hpgl_call(gs_fill(pgs));
+ break;
+
+ case hpgl_rm_clip_and_fill_polygon:
+ /*
+ * A bizarre HPISM - If the pen is white we do a solid
+ * white fill this is true for *all* fill types (arg!) as
+ * tested on the 6mp. If pen 1 (black)
+ * hpgl_set_drawing_color() handles this case by drawing
+ * the lines that comprise the vector fill
+ */
+ if (hpgl_get_selected_pen(pgls) == 0) {
+ pcl_mark_page_for_path(pgls);
+ hpgl_call(gs_fill(pgls->pgs));
+ }
+ hpgl_call(hpgl_clear_current_path(pgls));
+ break;
+
+ case hpgl_rm_vector:
+ case hpgl_rm_vector_fill:
+ /*
+ * we reset the ctm before stroking to preserve the line width
+ * information, then restore the ctm.
+ */
+ {
+ gs_matrix save_ctm;
+ int save_scaling_type = pgls->g.scaling_type;
+
+ hpgl_call(gs_currentmatrix(pgs, &save_ctm));
+
+ /* force no picture frame scaling */
+ pgls->g.scaling_type = hpgl_scaling_point_factor;
+ hpgl_call(hpgl_set_plu_ctm(pgls));
+ pgls->g.scaling_type = save_scaling_type;
+
+ /* NB: what does reversing the path do? Currently DEAD code see pglfill.c */
+ if ( !pgls->g.line.current.is_solid && (pgls->g.line.current.type == 0) )
+ hpgl_call(gs_reversepath(pgls->pgs));
+ pcl_mark_page_for_path(pgls);
+ hpgl_call(gs_stroke(pgls->pgs));
+ gs_setmatrix(pgs, &save_ctm);
+ break;
+ }
+ default :
+ dprintf("unknown render mode\n");
+ }
+
+ return 0;
+}
+
+ int
+hpgl_copy_current_path_to_polygon_buffer(
+ hpgl_state_t * pgls
+)
+{
+ gx_path * ppath = gx_current_path(pgls->pgs);
+ gx_path_assign_preserve(&pgls->g.polygon.buffer.path, ppath);
+ return 0;
+}
+
+ int
+hpgl_copy_polygon_buffer_to_current_path(
+ hpgl_state_t * pgls
+)
+{
+ gx_path * ppath = gx_current_path(pgls->pgs);
+ gx_path_assign_preserve(ppath, &pgls->g.polygon.buffer.path);
+ return 0;
+}