/src/ghostpdl/pcl/pcl/pgdraw.c
Line | Count | Source |
1 | | /* Copyright (C) 2001-2026 Artifex Software, Inc. |
2 | | All Rights Reserved. |
3 | | |
4 | | This software is provided AS-IS with no warranty, either express or |
5 | | implied. |
6 | | |
7 | | This software is distributed under license and may not be copied, |
8 | | modified or distributed except as expressly authorized under the terms |
9 | | of the license contained in the file LICENSE in this distribution. |
10 | | |
11 | | Refer to licensing information at http://www.artifex.com or contact |
12 | | Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco, |
13 | | CA 94129, USA, for further information. |
14 | | */ |
15 | | |
16 | | |
17 | | /* pgdraw.c - HP-GL/2 line drawing/path building routines. */ |
18 | | |
19 | | #include "stdio_.h" |
20 | | #include "math_.h" |
21 | | #include "gdebug.h" |
22 | | #include "gstypes.h" /* for gsstate.h */ |
23 | | #include "gsmatrix.h" /* for gsstate.h */ |
24 | | #include "gsmemory.h" /* for gsstate.h */ |
25 | | #include "gsstate.h" |
26 | | #include "gscoord.h" |
27 | | #include "gspath.h" |
28 | | #include "gspaint.h" |
29 | | #include "gsrop.h" /* for gs_setrasterop */ |
30 | | #include "gxfarith.h" /* for sincos */ |
31 | | #include "gxfixed.h" |
32 | | #include "pgmand.h" |
33 | | #include "pgdraw.h" |
34 | | #include "pggeom.h" |
35 | | #include "pgmisc.h" |
36 | | #include "pcdraw.h" |
37 | | #include "pcpalet.h" |
38 | | #include "pcpatrn.h" |
39 | | #include "pcpage.h" |
40 | | |
41 | | #include "gzstate.h" |
42 | | #include "gxdcolor.h" |
43 | | /* hack to quiet compiler warnings */ |
44 | | #ifndef abs |
45 | | extern int abs(int); |
46 | | #endif |
47 | | |
48 | 1.69M | #define round(x) (((x) < 0.0) ? (ceil ((x) - 0.5)) : (floor ((x) + 0.5))) |
49 | | |
50 | | static inline gs_point |
51 | | hpgl_picture_frame_scale(hpgl_state_t * pgls) |
52 | 4.25M | { |
53 | 4.25M | gs_point scale; |
54 | | |
55 | 4.25M | scale.x = scale.y = 0; |
56 | | |
57 | | /* this should not happen in a real system */ |
58 | 4.25M | if ((pgls->g.picture_frame_height == 0) || |
59 | 4.25M | (pgls->g.picture_frame_width == 0) || |
60 | 4.25M | (pgls->g.plot_width == 0) || (pgls->g.plot_height == 0)) { |
61 | 0 | dmprintf(pgls->memory, "bad picture frame coordinates\n"); |
62 | 4.25M | } else { |
63 | 4.25M | scale.x = (pgls->g.plot_size_horizontal_specified) ? |
64 | 4 | ((hpgl_real_t) pgls->g.picture_frame_width / |
65 | 4.25M | (hpgl_real_t) pgls->g.plot_width) : 1.0; |
66 | 4.25M | scale.y = (pgls->g.plot_size_vertical_specified) ? |
67 | 3.29k | ((hpgl_real_t) pgls->g.picture_frame_height / |
68 | 4.24M | (hpgl_real_t) pgls->g.plot_height) : 1.0; |
69 | 4.25M | } |
70 | 4.25M | return scale; |
71 | 4.25M | } |
72 | | |
73 | | static int |
74 | | hpgl_set_picture_frame_scaling(hpgl_state_t * pgls) |
75 | 6.15M | { |
76 | 6.15M | if (pgls->g.scaling_type != hpgl_scaling_point_factor) { |
77 | 3.81M | gs_point scale = hpgl_picture_frame_scale(pgls); |
78 | | |
79 | 3.81M | hpgl_call(gs_scale(pgls->pgs, scale.x, scale.y)); |
80 | 3.81M | } |
81 | 6.15M | return 0; |
82 | 6.15M | } |
83 | | |
84 | | hpgl_real_t |
85 | | hpgl_width_scale(hpgl_state_t * pgls) |
86 | 439k | { |
87 | 439k | gs_point sc = hpgl_picture_frame_scale(pgls); |
88 | | |
89 | 439k | return min(sc.x, sc.y); |
90 | 439k | } |
91 | | |
92 | | /* ctm to translate from pcl space to plu space */ |
93 | | int |
94 | | hpgl_set_pcl_to_plu_ctm(hpgl_state_t * pgls) |
95 | 6.15M | { |
96 | 6.15M | hpgl_real_t fw_plu = (coord_2_plu(pgls->g.picture_frame_width)); |
97 | 6.15M | hpgl_real_t fh_plu = (coord_2_plu(pgls->g.picture_frame_height)); |
98 | | |
99 | | /* set up the default pcl ctm and from that derive the gl/2 |
100 | | transformations */ |
101 | 6.15M | hpgl_call(pcl_set_ctm(pgls, false)); |
102 | | /* NB probably needs correction for RTL */ |
103 | 6.15M | hpgl_call(gs_translate(pgls->pgs, |
104 | 6.15M | pgls->g.picture_frame.anchor_point.x, |
105 | 6.15M | pgls->g.picture_frame.anchor_point.y)); |
106 | | |
107 | | /* the gl/2 coordinate system like postscript (and unlike pcl) is |
108 | | right-handed. */ |
109 | 6.15M | if (pgls->personality != rtl) { |
110 | 6.15M | hpgl_call(gs_translate(pgls->pgs, 0, pgls->g.picture_frame_height)); |
111 | | /* scale to plotter units and a flip for y */ |
112 | 6.15M | hpgl_call(gs_scale(pgls->pgs, (7200.0 / 1016.0), -(7200.0 / 1016.0))); |
113 | 6.15M | } else { |
114 | 0 | if (pgls->g.picture_frame_width >= pgls->g.picture_frame_height) { |
115 | 0 | hpgl_call(gs_rotate(pgls->pgs, -90)); |
116 | 0 | hpgl_call(gs_scale |
117 | 0 | (pgls->pgs, -(7200.0 / 1016.0), (7200.0 / 1016.0))); |
118 | 0 | } else { |
119 | 0 | hpgl_call(gs_translate |
120 | 0 | (pgls->pgs, pgls->g.picture_frame_height, 0)); |
121 | 0 | hpgl_call(gs_rotate(pgls->pgs, 180)); |
122 | 0 | hpgl_call(gs_scale |
123 | 0 | (pgls->pgs, (7200.0 / 1016.0), -(7200.0 / 1016.0))); |
124 | 0 | } |
125 | 0 | } |
126 | | |
127 | 6.15M | hpgl_call(gs_rotate(pgls->pgs, pgls->g.rotation)); |
128 | | |
129 | 6.15M | switch (pgls->g.rotation) { |
130 | 6.15M | case 0: |
131 | 6.15M | hpgl_call(gs_translate(pgls->pgs, 0, 0)); |
132 | 6.15M | break; |
133 | 6.15M | case 90: |
134 | 0 | if (pgls->personality != rtl) { |
135 | 0 | hpgl_call(gs_translate(pgls->pgs, 0, -fw_plu)); |
136 | 0 | } else { |
137 | 0 | if (pgls->g.picture_frame_width > |
138 | 0 | pgls->g.picture_frame_height) |
139 | 0 | hpgl_call(gs_translate(pgls->pgs, 0, -fw_plu)); |
140 | 0 | else |
141 | 0 | hpgl_call(gs_translate(pgls->pgs, 0, -fh_plu)); |
142 | 0 | } |
143 | 0 | break; |
144 | 0 | case 180: |
145 | 0 | if (pgls->personality != rtl) { |
146 | 0 | hpgl_call(gs_translate(pgls->pgs, -fw_plu, -fh_plu)); |
147 | 0 | } else { |
148 | 0 | if (pgls->g.picture_frame_width > |
149 | 0 | pgls->g.picture_frame_height) |
150 | 0 | hpgl_call(gs_translate(pgls->pgs, -fw_plu, -fh_plu)); |
151 | 0 | else |
152 | 0 | hpgl_call(gs_translate(pgls->pgs, -fh_plu, -fw_plu)); |
153 | 0 | } |
154 | 0 | break; |
155 | 0 | case 270: |
156 | 0 | if (pgls->personality != rtl) { |
157 | 0 | hpgl_call(gs_translate(pgls->pgs, -fh_plu, 0)); |
158 | 0 | } else { |
159 | 0 | if (pgls->g.picture_frame_width > |
160 | 0 | pgls->g.picture_frame_height) |
161 | 0 | hpgl_call(gs_translate(pgls->pgs, -fh_plu, 0)); |
162 | 0 | else |
163 | 0 | hpgl_call(gs_translate(pgls->pgs, -fw_plu, 0)); |
164 | 0 | } |
165 | 0 | break; |
166 | 6.15M | } |
167 | 6.15M | hpgl_call(hpgl_set_picture_frame_scaling(pgls)); |
168 | 6.15M | { |
169 | 6.15M | gs_matrix mat; |
170 | | |
171 | 6.15M | gs_currentmatrix(pgls->pgs, &mat); |
172 | 6.15M | mat.ty = floor(mat.ty); |
173 | 6.15M | mat.tx = floor(mat.tx); |
174 | 6.15M | gs_setmatrix(pgls->pgs, &mat); |
175 | 6.15M | } |
176 | 6.15M | hpgl_call(gs_setdotorientation(pgls->pgs)); |
177 | 6.15M | return 0; |
178 | 6.15M | } |
179 | | |
180 | | /* Set the CTM to map PLU to device units, regardless of scaling. */ |
181 | | /* (We need this for labels when scaling is on.) */ |
182 | | int |
183 | | hpgl_set_plu_ctm(hpgl_state_t * pgls) |
184 | 6.15M | { |
185 | 6.15M | hpgl_call(hpgl_set_pcl_to_plu_ctm(pgls)); |
186 | 6.15M | return 0; |
187 | 6.15M | } |
188 | | |
189 | | int |
190 | | hpgl_compute_user_units_to_plu_ctm(const hpgl_state_t * pgls, |
191 | | gs_matrix * pmat) |
192 | 888k | { |
193 | 888k | double origin_x = pgls->g.P1.x, origin_y = pgls->g.P1.y; |
194 | | |
195 | 888k | switch (pgls->g.scaling_type) { |
196 | 37.0k | case hpgl_scaling_none: |
197 | 37.0k | gs_make_identity(pmat); |
198 | 37.0k | break; |
199 | 0 | case hpgl_scaling_point_factor: |
200 | 0 | hpgl_call(gs_make_translation(origin_x, origin_y, pmat)); |
201 | 0 | hpgl_call(gs_matrix_scale(pmat, pgls->g.scaling_params.factor.x, |
202 | 0 | pgls->g.scaling_params.factor.y, pmat)); |
203 | 0 | hpgl_call(gs_matrix_translate(pmat, |
204 | 0 | -pgls->g.scaling_params.pmin.x, |
205 | 0 | -pgls->g.scaling_params.pmin.y, |
206 | 0 | pmat)); |
207 | 0 | break; |
208 | 850k | default: |
209 | | /*case hpgl_scaling_anisotropic: */ |
210 | | /*case hpgl_scaling_isotropic: */ |
211 | 850k | { |
212 | 850k | double window_x = pgls->g.P2.x - origin_x, |
213 | 850k | range_x = pgls->g.scaling_params.pmax.x - |
214 | 850k | pgls->g.scaling_params.pmin.x, |
215 | 850k | scale_x = window_x / range_x; |
216 | 850k | double window_y = pgls->g.P2.y - origin_y, |
217 | 850k | range_y = pgls->g.scaling_params.pmax.y - |
218 | 850k | pgls->g.scaling_params.pmin.y, |
219 | 850k | scale_y = window_y / range_y; |
220 | 850k | #define SIGN(x) ((x) < 0 ? -1.0 : 1.0) |
221 | | |
222 | 850k | if (pgls->g.scaling_type == hpgl_scaling_isotropic) { |
223 | 2 | if (fabs(scale_x) > fabs(scale_y)) { /* Reduce the X scaling. */ |
224 | 0 | origin_x += |
225 | 0 | SIGN(scale_x) * (range_x * |
226 | 0 | (fabs(scale_x) - |
227 | 0 | fabs(scale_y)) * |
228 | 0 | (pgls->g.scaling_params.left / |
229 | 0 | 100.0)); |
230 | 0 | scale_x = SIGN(scale_x) * fabs(scale_y); |
231 | 2 | } else { /* Reduce the Y scaling. */ |
232 | 2 | origin_y += |
233 | 2 | SIGN(scale_y) * (range_y * |
234 | 2 | (fabs(scale_y) - |
235 | 2 | fabs(scale_x)) * |
236 | 2 | (pgls->g.scaling_params.bottom / |
237 | 2 | 100.0)); |
238 | 2 | scale_y = SIGN(scale_y) * fabs(scale_x); |
239 | 2 | } |
240 | 2 | } |
241 | | |
242 | 850k | hpgl_call(gs_make_translation(origin_x, origin_y, pmat)); |
243 | 850k | hpgl_call(gs_matrix_scale(pmat, scale_x, scale_y, pmat)); |
244 | 850k | hpgl_call(gs_matrix_translate(pmat, |
245 | 850k | -pgls->g.scaling_params.pmin.x, |
246 | 850k | -pgls->g.scaling_params.pmin.y, |
247 | 850k | pmat)); |
248 | 850k | break; |
249 | 850k | } |
250 | 888k | } |
251 | 888k | return 0; |
252 | | |
253 | 888k | } |
254 | | |
255 | | /* set up ctm's. Uses the current render mode to figure out which ctm |
256 | | is appropriate */ |
257 | | int |
258 | | hpgl_set_ctm(hpgl_state_t * pgls) |
259 | 2.97M | { |
260 | 2.97M | hpgl_call(hpgl_set_plu_ctm(pgls)); |
261 | 2.97M | if (pgls->g.scaling_type != hpgl_scaling_none) { |
262 | 845k | gs_matrix mat; |
263 | | |
264 | 845k | hpgl_call(hpgl_compute_user_units_to_plu_ctm(pgls, &mat)); |
265 | 845k | hpgl_call(gs_concat(pgls->pgs, &mat)); |
266 | 845k | hpgl_call(gs_currentmatrix(pgls->pgs, &mat)); |
267 | 845k | mat.tx = round(mat.tx); |
268 | 845k | mat.ty = round(mat.ty); |
269 | 845k | hpgl_call(gs_setmatrix(pgls->pgs, &mat)); |
270 | 845k | } |
271 | 2.97M | return 0; |
272 | 2.97M | } |
273 | | |
274 | | /* Compute the pattern length. Normally, if the pattern length is |
275 | | relative it is 4% of the diagonal distance from P1 to P2, for |
276 | | isotropic scaling we need 4% of the distance of the plotter unit |
277 | | equivalent of the scaling SC coordinates xmin, xmax, ymin, and |
278 | | ymax.. */ |
279 | | static double |
280 | | hpgl_get_line_pattern_length(hpgl_state_t * pgls) |
281 | 98 | { |
282 | | /* dispense with the unusual "isotropic relative" case first. The |
283 | | normal calculation afterward is straightforward */ |
284 | 98 | if ((pgls->g.line.current.pattern_length_relative == 0) && |
285 | 98 | (pgls->g.scaling_type == hpgl_scaling_isotropic)) { |
286 | | /* the box in user space */ |
287 | 0 | gs_rect isotropic_user_box; |
288 | | /* box in plotter units we compute 4% of the diagonal of this box */ |
289 | 0 | gs_rect isotropic_plu_box; |
290 | 0 | gs_matrix user_to_plu_mat; |
291 | |
|
292 | 0 | hpgl_call(hpgl_compute_user_units_to_plu_ctm(pgls, &user_to_plu_mat)); |
293 | | |
294 | 0 | isotropic_user_box.p = pgls->g.scaling_params.pmin; |
295 | 0 | isotropic_user_box.q = pgls->g.scaling_params.pmax; |
296 | |
|
297 | 0 | hpgl_call(gs_bbox_transform(&isotropic_user_box, |
298 | 0 | &user_to_plu_mat, &isotropic_plu_box)); |
299 | | |
300 | 0 | return (pgls->g.line.current.pattern_length / 100.0) * |
301 | 0 | hpgl_compute_distance(isotropic_plu_box.p.x, |
302 | 0 | isotropic_plu_box.p.y, |
303 | 0 | isotropic_plu_box.q.x, |
304 | 0 | isotropic_plu_box.q.y); |
305 | 0 | } |
306 | | |
307 | | /* simple case 4% of the diagonal of P1 and P2 or absolute in millimeters */ |
308 | 98 | return ((pgls->g.line.current.pattern_length_relative == 0) ? |
309 | 98 | ((pgls->g.line.current.pattern_length / 100.0) * |
310 | 98 | hpgl_compute_distance(pgls->g.P1.x, |
311 | 98 | pgls->g.P1.y, |
312 | 98 | pgls->g.P2.x, |
313 | 98 | pgls->g.P2.y) * |
314 | 98 | hpgl_width_scale(pgls)) : |
315 | 98 | (mm_2_plu(pgls->g.line.current.pattern_length))); |
316 | 98 | } |
317 | | |
318 | | static int |
319 | | hpgl_set_graphics_dash_state(hpgl_state_t * pgls) |
320 | 2.58M | { |
321 | 2.58M | int entry = abs(pgls->g.line.current.type); |
322 | 2.58M | bool adaptive; |
323 | 2.58M | const hpgl_line_type_t *pat; |
324 | 2.58M | float length; |
325 | 2.58M | float pattern[20] = {0}; |
326 | 2.58M | float offset; |
327 | 2.58M | int count; |
328 | 2.58M | int i; |
329 | | |
330 | | /* handle the simple case (no dash) and return */ |
331 | 2.58M | if (pgls->g.line.current.is_solid) { |
332 | | /* use a 0 count pattern to turn off dashing in case it is |
333 | | set, and allow drawing dots */ |
334 | 2.58M | hpgl_call(gs_setdash(pgls->pgs, pattern, 0, 0)); |
335 | 2.58M | hpgl_call(gs_setdotlength(pgls->pgs, 0.00098, true)); |
336 | 2.58M | return 0; |
337 | 2.58M | } |
338 | | |
339 | 49 | hpgl_call(gs_setdotlength(pgls->pgs, 0.0, false)); |
340 | 49 | if (entry == 0) { |
341 | | /* dot length NOTE this is in absolute 1/72" units not |
342 | | user space */ |
343 | | /* Use an adaptive pattern with an infinitely long segment length |
344 | | to get the dots drawn just at the ends of lines. */ |
345 | 0 | pattern[0] = 0; |
346 | 0 | pattern[1] = 1.0e6; /* "infinity" */ |
347 | 0 | hpgl_call(gs_setdash(pgls->pgs, pattern, 2, 0)); |
348 | 0 | gs_setdashadapt(pgls->pgs, true); |
349 | 0 | return 0; |
350 | 0 | } |
351 | | |
352 | 49 | adaptive = (pgls->g.line.current.type < 0); |
353 | 49 | pat = ((adaptive) ? |
354 | 0 | (&pgls->g.adaptive_line_type[entry - 1]) : |
355 | 49 | (&pgls->g.fixed_line_type[entry - 1])); |
356 | | |
357 | 49 | length = hpgl_get_line_pattern_length(pgls); |
358 | 49 | gs_setdashadapt(pgls->pgs, adaptive); |
359 | | /* |
360 | | * The graphics library interprets odd pattern counts differently |
361 | | * from GL: if the pattern count is odd, we need to do something |
362 | | * a little special. |
363 | | */ |
364 | 49 | count = pat->count; |
365 | 147 | for (i = 0; i < count; i++) |
366 | 98 | pattern[i] = length * pat->gap[i]; |
367 | 49 | offset = |
368 | 49 | pgls->g.line.current.pattern_offset * |
369 | 49 | hpgl_get_line_pattern_length(pgls); |
370 | 49 | if (count & 1) { |
371 | | /* |
372 | | * Combine the last gap with the first one, and change the |
373 | | * offset to compensate. NOTE: this doesn't work right with |
374 | | * adaptive line type: we may need to change the library to |
375 | | * make this work properly. |
376 | | */ |
377 | 0 | --count; |
378 | 0 | pattern[0] += pattern[count]; |
379 | 0 | offset += pattern[count]; |
380 | 0 | } |
381 | | |
382 | 49 | hpgl_call(gs_setdash(pgls->pgs, pattern, count, offset)); |
383 | | |
384 | 49 | return 0; |
385 | 49 | } |
386 | | |
387 | | /* catch pen not being in the palette */ |
388 | | int |
389 | | hpgl_get_selected_pen(hpgl_state_t * pgls) |
390 | 6.13M | { |
391 | | /* get the current pen */ |
392 | 6.13M | int pen = pgls->g.pen.selected; |
393 | | |
394 | | /* 0 is the first pen */ |
395 | 6.13M | int num_entries = pcl_palette_get_num_entries(pgls->ppalet); |
396 | | |
397 | | /* this is bad */ |
398 | 6.13M | if ((pen < 0) || (pen >= num_entries)) { |
399 | 0 | pen %= num_entries; |
400 | 0 | if (pen < 0) |
401 | 0 | pen += num_entries; |
402 | 0 | } |
403 | 6.13M | return pen; |
404 | 6.13M | } |
405 | | |
406 | | /* |
407 | | * set up joins, caps, miter limit, and line width |
408 | | */ |
409 | | static int |
410 | | hpgl_set_graphics_line_attribute_state(hpgl_state_t * pgls, |
411 | | hpgl_rendering_mode_t render_mode) |
412 | 2.58M | { |
413 | 2.58M | const gs_line_cap cap_map[] = { gs_cap_butt, /* 0 not supported */ |
414 | 2.58M | gs_cap_butt, /* 1 butt end */ |
415 | 2.58M | gs_cap_square, /* 2 square end */ |
416 | 2.58M | gs_cap_triangle, /* 3 triag. end */ |
417 | 2.58M | gs_cap_round /* 4 round cap */ |
418 | 2.58M | }; |
419 | | |
420 | 2.58M | const gs_line_join join_map[] = { gs_join_none, /* 0 not supported */ |
421 | 2.58M | gs_join_miter, /* 1 mitered */ |
422 | 2.58M | gs_join_miter, /* 2 mitrd/bevld */ |
423 | 2.58M | gs_join_triangle, /* 3 triag. join */ |
424 | 2.58M | gs_join_round, /* 4 round join */ |
425 | 2.58M | gs_join_bevel, /* 5 bevel join */ |
426 | 2.58M | gs_join_none /* 6 no join */ |
427 | 2.58M | }; |
428 | 2.58M | const float *widths = pcl_palette_get_pen_widths(pgls->ppalet); |
429 | 2.58M | double pen_wid = widths[hpgl_get_selected_pen(pgls)]; |
430 | | |
431 | 2.58M | gs_setfilladjust(pgls->pgs, 0.0, 0.0); |
432 | | |
433 | | /* character mode has already set up all of this information in |
434 | | the build character routine for the font */ |
435 | 2.58M | if (render_mode == hpgl_rm_character) |
436 | 209k | return 0; |
437 | | /* |
438 | | * HP appears to use default line attributes if the the pen |
439 | | * width is less than or equal to .35mm or 14.0 plu. This |
440 | | * is not documented PCLTRM. Pen widths are maintained in |
441 | | * plotter units |
442 | | */ |
443 | 2.37M | if (pen_wid <= 14.0) { |
444 | 81.0k | hpgl_call(gs_setlinejoin(pgls->pgs, gs_join_none)); |
445 | 81.0k | hpgl_call(gs_setlinecap(pgls->pgs, gs_cap_butt)); |
446 | 81.0k | hpgl_call(gs_setlinewidth(pgls->pgs, pen_wid)); |
447 | 81.0k | hpgl_call(gs_setmiterlimit(pgls->pgs, 5.0)); |
448 | 81.0k | return 0; |
449 | 81.0k | } |
450 | | |
451 | 2.29M | switch (render_mode) { |
452 | | |
453 | 25.9k | case hpgl_rm_polygon: |
454 | 26.1k | case hpgl_rm_clip_and_fill_polygon: |
455 | 26.1k | hpgl_call(gs_setlinejoin(pgls->pgs, gs_join_round)); |
456 | 26.1k | hpgl_call(gs_setlinecap(pgls->pgs, gs_cap_round)); |
457 | 26.1k | break; |
458 | | |
459 | 2.25M | case hpgl_rm_vector_fill: |
460 | 2.26M | case hpgl_rm_vector: |
461 | 2.26M | vector: |
462 | 2.26M | hpgl_call(gs_setlinejoin(pgls->pgs, join_map[pgls->g.line.join])); |
463 | 2.26M | hpgl_call(gs_setlinecap(pgls->pgs, cap_map[pgls->g.line.cap])); |
464 | 2.26M | hpgl_call(gs_setlinewidth(pgls->pgs, pen_wid)); |
465 | 2.26M | break; |
466 | | |
467 | 2.26M | default: |
468 | | /* shouldn't happen; we must have a mode to properly parse hpgl file. */ |
469 | 0 | dmprintf(pgls->memory, |
470 | 0 | "warning no hpgl rendering mode set using vector mode\n"); |
471 | 0 | goto vector; |
472 | 2.29M | } |
473 | | |
474 | | #ifdef COMMENT |
475 | | /* I do not remember the rational for the large miter */ |
476 | | hpgl_call(gs_setmiterlimit(pgls->pgs, (pgls->g.line.join == 1) |
477 | | ? 5000.0 : pgls->g.miter_limit)); |
478 | | #endif |
479 | 2.29M | hpgl_call(gs_setmiterlimit(pgls->pgs, pgls->g.miter_limit)); |
480 | 2.29M | return 0; |
481 | 2.29M | } |
482 | | |
483 | | /* |
484 | | * A bounding box for the current polygon -- used for HPGL/2 vector |
485 | | * fills. |
486 | | */ |
487 | | static int |
488 | | hpgl_polyfill_bbox(hpgl_state_t * pgls, gs_rect * bbox) |
489 | 294 | { |
490 | | /* get the bounding box for the current path / polygon */ |
491 | 294 | hpgl_call(gs_pathbbox(pgls->pgs, bbox)); |
492 | 294 | return 0; |
493 | 294 | } |
494 | | |
495 | | /* set up an hpgl clipping region -- intersection of IW command and |
496 | | picture frame. */ |
497 | | int |
498 | | hpgl_set_clipping_region(hpgl_state_t * pgls, |
499 | | hpgl_rendering_mode_t render_mode) |
500 | 2.61M | { |
501 | | /* if we are doing vector fill a clipping path has already |
502 | | been set up using the last polygon */ |
503 | 2.61M | if (render_mode == hpgl_rm_vector_fill) |
504 | 2.26M | return 0; |
505 | 353k | else { |
506 | 353k | gs_fixed_rect fixed_box; |
507 | 353k | gs_rect pcl_clip_box; |
508 | 353k | gs_rect dev_clip_box; |
509 | 353k | gs_matrix save_ctm; |
510 | 353k | gs_matrix pcl_ctm; |
511 | | |
512 | | /* get pcl to device ctm and restore the current ctm */ |
513 | 353k | hpgl_call(gs_currentmatrix(pgls->pgs, &save_ctm)); |
514 | 353k | hpgl_call(pcl_set_ctm(pgls, false)); |
515 | 353k | hpgl_call(gs_currentmatrix(pgls->pgs, &pcl_ctm)); |
516 | 353k | hpgl_call(gs_setmatrix(pgls->pgs, &save_ctm)); |
517 | | /* find the clipping region defined by the picture frame |
518 | | which is defined in pcl coordinates */ |
519 | 353k | if (pgls->personality != rtl) { |
520 | 353k | pcl_clip_box.p.x = pgls->g.picture_frame.anchor_point.x; |
521 | 353k | pcl_clip_box.p.y = pgls->g.picture_frame.anchor_point.y; |
522 | 353k | pcl_clip_box.q.x = pcl_clip_box.p.x + pgls->g.picture_frame_width; |
523 | 353k | pcl_clip_box.q.y = |
524 | 353k | pcl_clip_box.p.y + pgls->g.picture_frame_height; |
525 | 353k | } else { |
526 | 0 | pcl_clip_box.p.x = pgls->g.picture_frame.anchor_point.y; |
527 | 0 | pcl_clip_box.p.y = pgls->g.picture_frame.anchor_point.x; |
528 | 0 | pcl_clip_box.q.x = |
529 | 0 | pcl_clip_box.p.y + pgls->g.picture_frame_height; |
530 | 0 | pcl_clip_box.q.y = pcl_clip_box.p.x + pgls->g.picture_frame_width; |
531 | 0 | } |
532 | | |
533 | 353k | hpgl_call(gs_bbox_transform(&pcl_clip_box, &pcl_ctm, &dev_clip_box)); |
534 | | /* HP cheats and expands the clip box's extant by at least |
535 | | one device pixel in all directions */ |
536 | 353k | dev_clip_box.q.x += 1.0; |
537 | 353k | dev_clip_box.q.y += 1.0; |
538 | 353k | dev_clip_box.p.x -= 1.0; |
539 | 353k | dev_clip_box.p.y -= 1.0; |
540 | | /* if the clipping window is active calculate the new clip |
541 | | box derived from IW and the intersection of the device |
542 | | space boxes replace the current box. Note that IW |
543 | | coordinates are in current units and and the picture |
544 | | frame in pcl coordinates. */ |
545 | 353k | if (pgls->g.soft_clip_window.active) { |
546 | 638 | gs_rect dev_soft_window_box; |
547 | 638 | gs_matrix ctm; |
548 | | |
549 | 638 | if (pgls->g.soft_clip_window.isbound && pgls->personality != rtl) { |
550 | | /* we need the plotter unit matrix */ |
551 | 0 | hpgl_call(gs_currentmatrix(pgls->pgs, &save_ctm)); |
552 | 0 | hpgl_call(hpgl_set_plu_ctm(pgls)); |
553 | 0 | hpgl_call(gs_currentmatrix(pgls->pgs, &ctm)); |
554 | 0 | hpgl_call(gs_setmatrix(pgls->pgs, &save_ctm)); |
555 | 638 | } else { |
556 | 638 | hpgl_call(gs_currentmatrix(pgls->pgs, &ctm)); |
557 | 638 | } |
558 | 638 | hpgl_call(gs_bbox_transform(&pgls->g.soft_clip_window.rect, |
559 | 638 | &ctm, &dev_soft_window_box)); |
560 | | /* Enlarge IW by 1 device dot to compensate for it's |
561 | | 'on the line' is not clipped behavior. */ |
562 | 638 | dev_clip_box.p.x = |
563 | 638 | max(dev_clip_box.p.x, dev_soft_window_box.p.x - 1.0); |
564 | 638 | dev_clip_box.p.y = |
565 | 638 | max(dev_clip_box.p.y, dev_soft_window_box.p.y - 1.0); |
566 | 638 | dev_clip_box.q.x = |
567 | 638 | min(dev_clip_box.q.x, dev_soft_window_box.q.x + 1.0); |
568 | 638 | dev_clip_box.q.y = |
569 | 638 | min(dev_clip_box.q.y, dev_soft_window_box.q.y + 1.0); |
570 | | |
571 | 638 | } |
572 | | /* convert intersection box to fixed point and clip */ |
573 | 353k | fixed_box.p.x = float2fixed(floor(dev_clip_box.p.x)); |
574 | 353k | fixed_box.p.y = float2fixed(floor(dev_clip_box.p.y)); |
575 | 353k | fixed_box.q.x = float2fixed(ceil(dev_clip_box.q.x)); |
576 | 353k | fixed_box.q.y = float2fixed(ceil(dev_clip_box.q.y)); |
577 | 353k | hpgl_call(gx_clip_to_rectangle(pgls->pgs, &fixed_box)); |
578 | 353k | } |
579 | 353k | return 0; |
580 | 2.61M | } |
581 | | |
582 | | /* Plot one vector for vector fill all these use absolute coordinates. */ |
583 | | static int |
584 | | hpgl_draw_vector_absolute(hpgl_state_t * pgls, |
585 | | hpgl_real_t x0, |
586 | | hpgl_real_t y0, |
587 | | hpgl_real_t x1, |
588 | | hpgl_real_t y1, hpgl_rendering_mode_t render_mode) |
589 | 2.26M | { |
590 | 2.26M | bool set_ctm = (render_mode != hpgl_rm_character); |
591 | | |
592 | 2.26M | hpgl_call(hpgl_add_point_to_path(pgls, |
593 | 2.26M | x0, |
594 | 2.26M | y0, hpgl_plot_move_absolute, set_ctm)); |
595 | 2.26M | hpgl_call(hpgl_add_point_to_path(pgls, |
596 | 2.26M | x1, |
597 | 2.26M | y1, hpgl_plot_draw_absolute, set_ctm)); |
598 | 2.26M | hpgl_call(hpgl_draw_current_path(pgls, hpgl_rm_vector_fill)); |
599 | 2.26M | return 0; |
600 | 2.26M | } |
601 | | |
602 | | static int |
603 | | hpgl_get_adjusted_corner(hpgl_real_t x_fill_increment, |
604 | | hpgl_real_t y_fill_increment, |
605 | | gs_rect * bbox, |
606 | | gs_point * current_anchor_corner, |
607 | | gs_point * adjusted_anchor_corner) |
608 | 849 | { |
609 | 849 | adjusted_anchor_corner->x = current_anchor_corner->x; |
610 | 849 | adjusted_anchor_corner->y = current_anchor_corner->y; |
611 | | /* account for anchor corner greater than origin */ |
612 | 849 | if (x_fill_increment != 0) { |
613 | 284 | while (adjusted_anchor_corner->x > bbox->p.x) |
614 | 1 | adjusted_anchor_corner->x -= x_fill_increment; |
615 | 566 | } else if (adjusted_anchor_corner->x > bbox->p.x) |
616 | 2 | adjusted_anchor_corner->x = bbox->p.x; |
617 | 849 | if (y_fill_increment != 0) { |
618 | 586 | while (adjusted_anchor_corner->y > bbox->p.y) |
619 | 20 | adjusted_anchor_corner->y -= y_fill_increment; |
620 | 566 | } else if (adjusted_anchor_corner->y > bbox->p.y) |
621 | 1 | adjusted_anchor_corner->y = bbox->p.y; |
622 | 849 | return 0; |
623 | 849 | } |
624 | | |
625 | | static void |
626 | | hpgl_alternate_line_pattern_offset(hpgl_state_t * pgls, uint lines_filled) |
627 | 2.26M | { |
628 | 2.26M | if (lines_filled & 1) |
629 | 1.13M | pgls->g.line.current.pattern_offset = 0.5; |
630 | 1.13M | else |
631 | 1.13M | pgls->g.line.current.pattern_offset = 0.0; |
632 | 2.26M | } |
633 | | |
634 | | /* this definition is used to factor out the effect of the orientation |
635 | | of the pcl logical page. HP printers prior to the 4500 series |
636 | | oriented the hpgl/2 fill lines relative to the logical page |
637 | | orientation (expected result), later printers do not. Comment out |
638 | | the following definition to support the old (expected) behavior. */ |
639 | | #define FILL_IGNORES_PCL_ORIENTATION |
640 | | |
641 | | /* |
642 | | * HAS should replicate lines beginning at the anchor corner to +X and |
643 | | * +Y. Not quite right - anchor corner not yet supported. |
644 | | * pgls->g.anchor_corner needs to be used to set dash offsets |
645 | | */ |
646 | | static int |
647 | | hpgl_polyfill(hpgl_state_t * pgls, hpgl_rendering_mode_t render_mode) |
648 | 294 | { |
649 | 294 | hpgl_real_t diag_mag, endx, endy; |
650 | 294 | gs_sincos_t sincos; |
651 | 294 | gs_point start; |
652 | 2.83k | #define sin_dir sincos.sin |
653 | 1.98k | #define cos_dir sincos.cos |
654 | 294 | gs_rect bbox; |
655 | 294 | gs_matrix user_to_plu_mat; |
656 | 294 | hpgl_pen_state_t saved_pen_state; |
657 | 294 | hpgl_real_t x_fill_increment, y_fill_increment; |
658 | 294 | hpgl_FT_pattern_source_t type = pgls->g.fill.type; |
659 | 294 | bool cross = (type == hpgl_FT_pattern_two_lines); |
660 | 294 | const hpgl_hatch_params_t *params = |
661 | 294 | (cross ? &pgls->g.fill.param.crosshatch : &pgls->g.fill.param.hatch); |
662 | 294 | gs_point spacing; |
663 | 294 | #ifdef FILL_IGNORES_PCL_ORIENTATION |
664 | 294 | hpgl_real_t direction = params->angle + pgls->xfm_state.lp_orient * 90; |
665 | | #else |
666 | | hpgl_real_t direction = params->angle; |
667 | | #endif |
668 | 294 | hpgl_real_t unscaled_direction; |
669 | 294 | float saved_line_pattern_offset = pgls->g.line.current.pattern_offset; |
670 | 294 | int lines_filled; |
671 | | |
672 | | /* spacing is always relevant to the scaling of the x-axis. It |
673 | | can be specified in user space if provided to FT or by default |
674 | | it is 1% of the distance from P1 to P2 */ |
675 | 294 | spacing.x = params->spacing; |
676 | | /* save the pen position */ |
677 | 294 | hpgl_save_pen_state(pgls, &saved_pen_state, hpgl_pen_pos); |
678 | 294 | hpgl_call(hpgl_compute_user_units_to_plu_ctm(pgls, &user_to_plu_mat)); |
679 | 294 | if (params->spacing == 0) { |
680 | | /* Per TRM 22-12, use 1% of the P1/P2 distance. */ |
681 | | |
682 | 22 | spacing.x = 0.01 * hpgl_compute_distance(pgls->g.P1.x, |
683 | 22 | pgls->g.P1.y, |
684 | 22 | pgls->g.P2.x, pgls->g.P2.y); |
685 | | |
686 | | /* 1% is in plu, convert back to user units */ |
687 | 22 | spacing.y = spacing.x / fabs(user_to_plu_mat.yy); |
688 | 22 | spacing.x /= fabs(user_to_plu_mat.xx); |
689 | 272 | } else { |
690 | | /* set spacing.y based on ratio of x/y scaling, still in user units */ |
691 | 272 | spacing.y = |
692 | 272 | spacing.x * fabs(user_to_plu_mat.xx) / fabs(user_to_plu_mat.yy); |
693 | 272 | } |
694 | | /* get the bounding box */ |
695 | 294 | hpgl_call(hpgl_polyfill_bbox(pgls, &bbox)); |
696 | | /* |
697 | | * if the line width exceeds the spacing we use the line width |
698 | | * to avoid overlapping of the fill lines. HAS this can be |
699 | | * integrated with the logic above for spacing as not to |
700 | | * duplicate alot of code. |
701 | | */ |
702 | 294 | { |
703 | 294 | const float *widths = pcl_palette_get_pen_widths(pgls->ppalet); |
704 | 294 | hpgl_real_t line_width = widths[hpgl_get_selected_pen(pgls)]; |
705 | | |
706 | 294 | line_width /= min(fabs(user_to_plu_mat.xx), fabs(user_to_plu_mat.yy)); |
707 | 294 | if (line_width >= spacing.x || line_width >= spacing.y) { |
708 | 11 | hpgl_call(hpgl_draw_current_path(pgls, hpgl_rm_polygon)); |
709 | 11 | return 0; |
710 | 11 | } |
711 | 294 | } |
712 | | |
713 | | /* get rid of the current path */ |
714 | | |
715 | 283 | hpgl_call(hpgl_clear_current_path(pgls)); |
716 | | |
717 | 566 | start: /* Change direction, come back and draw crosshatch lines. */ |
718 | | |
719 | | /* hatch angles are isotropic; ie 45degrees is not affected by y scale != x scale. |
720 | | unscaled_direction ::= user requested angle |
721 | | direction ::= the angle that when scaled will generate user requested angle */ |
722 | | |
723 | | /* save unscaled angle */ |
724 | 566 | unscaled_direction = direction; |
725 | | |
726 | | /* not necessery if direction is orthogonal */ |
727 | 566 | if (!equal(fmod(direction, 90), 0)) { |
728 | 0 | hpgl_real_t slope, scaled_slope; |
729 | |
|
730 | 0 | gs_sincos_degrees(direction, &sincos); |
731 | | /* take the tangent by dividing sin by cos. Since we know |
732 | | the angle is non-orthogonal the cosine is non-zero */ |
733 | 0 | slope = sin_dir / cos_dir; |
734 | | /* scale the slope by the ratio of the scaling factors */ |
735 | 0 | scaled_slope = (user_to_plu_mat.xx / user_to_plu_mat.yy) * slope; |
736 | | /* preserved angle in user space */ |
737 | 0 | direction = radians_to_degrees * atan(scaled_slope); |
738 | 0 | } |
739 | | |
740 | 566 | lines_filled = 0; |
741 | | |
742 | | /* spacing is done with the unscaled angle |
743 | | spacing.x .y is already scaled hence the unscaled angle is used. |
744 | | scale * spacing * sin(unscaled_angle) */ |
745 | 566 | gs_sincos_degrees(unscaled_direction, &sincos); |
746 | 566 | if (sin_dir < 0) |
747 | 0 | sin_dir = -sin_dir, cos_dir = -cos_dir; /* ensure y_inc >= 0 */ |
748 | 566 | x_fill_increment = (sin_dir != 0) ? fabs(spacing.x / sin_dir) : 0; |
749 | 566 | y_fill_increment = (cos_dir != 0) ? fabs(spacing.y / cos_dir) : 0; |
750 | | |
751 | | /* scaled angle is used for end point calculation |
752 | | distance * sin( scaled_angle ) |
753 | | scale is applyed once and only once per calculation */ |
754 | 566 | gs_sincos_degrees(direction, &sincos); |
755 | 566 | if (sin_dir < 0) |
756 | 0 | sin_dir = -sin_dir, cos_dir = -cos_dir; /* ensure y_inc >= 0 */ |
757 | | |
758 | 566 | hpgl_call(hpgl_get_adjusted_corner(x_fill_increment, |
759 | 566 | y_fill_increment, |
760 | 566 | &bbox, |
761 | 566 | &pgls->g.anchor_corner, &start)); |
762 | | |
763 | | /* |
764 | | * calculate the diagonals magnitude. Note we clip this |
765 | | * latter in the library. If desired we could clip to the |
766 | | * actual bbox here to save processing time. For now we simply |
767 | | * draw all fill lines using the diagonals magnitude |
768 | | */ |
769 | 566 | diag_mag = hpgl_compute_distance(start.x, start.y, bbox.q.x, bbox.q.y); |
770 | 566 | endx = (diag_mag * cos_dir) + start.x; |
771 | 566 | endy = (diag_mag * sin_dir) + start.y; |
772 | 566 | hpgl_alternate_line_pattern_offset(pgls, lines_filled++); |
773 | 566 | hpgl_call(hpgl_draw_vector_absolute(pgls, |
774 | 566 | start.x, |
775 | 566 | start.y, endx, endy, render_mode)); |
776 | | /* Travel along +x using current spacing. */ |
777 | 566 | if (x_fill_increment != 0) { |
778 | 331k | while (endx += x_fill_increment, |
779 | 331k | (start.x += x_fill_increment) <= bbox.q.x) { |
780 | | |
781 | 331k | hpgl_alternate_line_pattern_offset(pgls, lines_filled++); |
782 | 331k | hpgl_call(hpgl_draw_vector_absolute(pgls, |
783 | 331k | start.x, |
784 | 331k | start.y, |
785 | 331k | endx, endy, render_mode)); |
786 | 331k | } |
787 | 283 | } |
788 | | |
789 | | /* Travel along +Y similarly. */ |
790 | 566 | if (y_fill_increment != 0) { |
791 | | /* |
792 | | * If the slope is negative, we have to travel along the right |
793 | | * edge of the box rather than the left edge. Fortuitously, |
794 | | * the X loop left everything set up exactly right for this case. |
795 | | */ |
796 | 283 | if (cos_dir >= 0) { |
797 | 283 | hpgl_call(hpgl_get_adjusted_corner(x_fill_increment, |
798 | 283 | y_fill_increment, |
799 | 283 | &bbox, |
800 | 283 | &pgls->g.anchor_corner, |
801 | 283 | &start)); |
802 | 283 | endx = (diag_mag * cos_dir) + start.x; |
803 | 283 | endy = (diag_mag * sin_dir) + start.y; |
804 | 283 | } else |
805 | 0 | start.y -= y_fill_increment, endy -= y_fill_increment; |
806 | | |
807 | 1.93M | while (endy += y_fill_increment, |
808 | 1.93M | (start.y += y_fill_increment) <= bbox.q.y) { |
809 | 1.93M | hpgl_alternate_line_pattern_offset(pgls, lines_filled++); |
810 | 1.93M | hpgl_call(hpgl_draw_vector_absolute(pgls, |
811 | 1.93M | start.x, |
812 | 1.93M | start.y, |
813 | 1.93M | endx, endy, render_mode)); |
814 | 1.93M | } |
815 | | |
816 | 283 | } |
817 | 566 | if (cross) { |
818 | | /* go back and draw the perpendicular lines, +90degress */ |
819 | 283 | cross = false; |
820 | 283 | direction = unscaled_direction + 90; |
821 | 283 | if (direction >= 180) |
822 | 6 | direction -= 180; |
823 | 283 | goto start; |
824 | 283 | } |
825 | 283 | hpgl_restore_pen_state(pgls, &saved_pen_state, hpgl_pen_pos); |
826 | 283 | pgls->g.line.current.pattern_offset = saved_line_pattern_offset; |
827 | 283 | return 0; |
828 | | |
829 | 566 | #undef sin_dir |
830 | 566 | #undef cos_dir |
831 | | |
832 | 566 | } |
833 | | |
834 | | /* gl/2 vector filled objects always have a white background. It can |
835 | | be either a transparent or white. In the former case we don't have |
836 | | to do anything. We expect the fill area of the object to already |
837 | | be defined in the graphics state. */ |
838 | | static int |
839 | | hpgl_fill_polyfill_background(hpgl_state_t * pgls) |
840 | 294 | { |
841 | 294 | int code, code2; |
842 | | /* conditionally mark page as dirty */ |
843 | 294 | hpgl_call(pcl_mark_page_for_path(pgls)); |
844 | | /* if we are drawing on a transparent background */ |
845 | 294 | if (pgls->g.source_transparent) |
846 | 293 | return 0; |
847 | | /* preserve the current foreground color */ |
848 | 1 | hpgl_call(hpgl_gsave(pgls)); |
849 | | /* fill a white region. */ |
850 | 1 | code = gs_setgray(pgls->pgs, 1.0); |
851 | 1 | if (code >= 0) |
852 | 1 | code = gs_fill(pgls->pgs); |
853 | | /* restore the foreground color */ |
854 | 1 | code2 = hpgl_grestore(pgls); |
855 | 1 | return (code < 0) ? code : code2; |
856 | 1 | } |
857 | | |
858 | | static int |
859 | | hpgl_polyfill_using_current_line_type(hpgl_state_t * pgls, |
860 | | hpgl_rendering_mode_t render_mode) |
861 | 294 | { |
862 | 294 | int code; |
863 | | |
864 | | /* gsave and grestore used to preserve the clipping region */ |
865 | 294 | hpgl_call(hpgl_gsave(pgls)); |
866 | | |
867 | | /* |
868 | | * Use the current path to set up a clipping path |
869 | | * beginning at the anchor corner replicate lines |
870 | | */ |
871 | 294 | if (pgls->g.fill_type == hpgl_even_odd_rule) |
872 | 294 | code = gs_eoclip(pgls->pgs); |
873 | 0 | else |
874 | 0 | code = gs_clip(pgls->pgs); |
875 | 294 | if (code >= 0) code = hpgl_fill_polyfill_background(pgls); |
876 | 294 | if (code >= 0) code = hpgl_polyfill(pgls, render_mode); |
877 | | |
878 | 294 | { |
879 | 294 | int code2 = hpgl_grestore(pgls); |
880 | 294 | if (code >= 0) code = code2; |
881 | 294 | return code; |
882 | 294 | } |
883 | 294 | } |
884 | | |
885 | | static gs_rop3_t |
886 | | hpgl_rop(hpgl_state_t * pgls, hpgl_rendering_mode_t render_mode) |
887 | 2.99M | { |
888 | 2.99M | gs_rop3_t rop = pgls->logical_op; |
889 | | |
890 | 2.99M | if (render_mode == hpgl_rm_vector || render_mode == hpgl_rm_vector_fill) { |
891 | 2.33M | if (rop == 0 || rop == 160 || rop == 170 || rop == 240 || rop == 250 |
892 | 2.33M | || rop == 255) { |
893 | 1.61k | return rop; |
894 | 2.33M | } else { |
895 | 2.33M | return rop3_default; |
896 | 2.33M | } |
897 | 2.33M | } |
898 | 657k | return rop; |
899 | 2.99M | } |
900 | | |
901 | | int |
902 | | hpgl_set_drawing_color(hpgl_state_t * pgls, hpgl_rendering_mode_t render_mode) |
903 | 2.99M | { |
904 | 2.99M | int code = 0; |
905 | | |
906 | 2.99M | pcl_pattern_set_proc_t set_proc; |
907 | | |
908 | 2.99M | switch (render_mode) { |
909 | | |
910 | 294 | case hpgl_rm_clip_and_fill_polygon: |
911 | 294 | hpgl_call(hpgl_polyfill_using_current_line_type |
912 | 294 | (pgls, render_mode)); |
913 | 294 | return 0; |
914 | | |
915 | 622k | case hpgl_rm_character: |
916 | 622k | switch (pgls->g.character.fill_mode) { |
917 | | |
918 | 622k | case hpgl_char_solid_edge: /* fall through */ |
919 | 622k | case hpgl_char_edge: |
920 | 622k | set_proc = |
921 | 622k | pcl_pattern_get_proc_FT(hpgl_FT_pattern_solid_pen1); |
922 | 622k | code = set_proc(pgls, hpgl_get_selected_pen(pgls), false); |
923 | 622k | break; |
924 | | |
925 | 0 | case hpgl_char_fill: |
926 | 902 | case hpgl_char_fill_edge: |
927 | 902 | if ((pgls->g.fill.type == hpgl_FT_pattern_one_line) || |
928 | 902 | (pgls->g.fill.type == hpgl_FT_pattern_two_lines)) { |
929 | 0 | hpgl_call(hpgl_polyfill_using_current_line_type(pgls, |
930 | 0 | render_mode)); |
931 | 0 | return 0; |
932 | 0 | } else |
933 | 902 | goto fill; |
934 | | |
935 | 0 | default: |
936 | 0 | dmprintf(pgls->memory, |
937 | 0 | "hpgl_set_drawing_color: internal error illegal fill\n"); |
938 | 0 | return 0; |
939 | 622k | } |
940 | 622k | break; |
941 | | |
942 | | /* fill like a polygon */ |
943 | 622k | case hpgl_rm_polygon: |
944 | 35.1k | fill: |
945 | 35.1k | set_proc = pcl_pattern_get_proc_FT(pgls->g.fill.type); |
946 | 35.1k | switch (pgls->g.fill.type) { |
947 | | |
948 | 1.76k | case hpgl_FT_pattern_solid_pen1: |
949 | 1.77k | case hpgl_FT_pattern_solid_pen2: |
950 | | /* |
951 | | * this is documented incorrectly PCLTRM 22-12 says |
952 | | * these should be solid black but they are actually |
953 | | * set to the value of the current pen - (i.e pen 0 is |
954 | | * solid white |
955 | | */ |
956 | 1.77k | code = set_proc(pgls, hpgl_get_selected_pen(pgls), false); |
957 | 1.77k | break; |
958 | | |
959 | 0 | case hpgl_FT_pattern_one_line: |
960 | 11 | case hpgl_FT_pattern_two_lines: |
961 | 11 | set_proc = |
962 | 11 | pcl_pattern_get_proc_FT(hpgl_FT_pattern_solid_pen1); |
963 | 11 | code = set_proc(pgls, hpgl_get_selected_pen(pgls), false); |
964 | 11 | break; |
965 | | |
966 | 0 | case hpgl_FT_pattern_cross_hatch: |
967 | 0 | code = set_proc(pgls, |
968 | 0 | pgls->g.fill.param.pattern_type, |
969 | 0 | hpgl_get_selected_pen(pgls) |
970 | 0 | ); |
971 | 0 | break; |
972 | | |
973 | 946 | case hpgl_FT_pattern_shading: |
974 | 946 | code = set_proc(pgls, |
975 | 946 | pgls->g.fill.param.shading, |
976 | 946 | hpgl_get_selected_pen(pgls) |
977 | 946 | ); |
978 | 946 | break; |
979 | | |
980 | 0 | case hpgl_FT_pattern_user_defined: |
981 | 0 | code = set_proc(pgls, |
982 | 0 | pgls->g.fill.param.pattern_id, |
983 | 0 | hpgl_get_selected_pen(pgls) |
984 | 0 | ); |
985 | 0 | break; |
986 | | |
987 | 32.4k | case hpgl_FT_pattern_RF: |
988 | | /* pcl5e does not care about the current pen it always uses black (1). */ |
989 | 32.4k | if (pgls->personality == pcl5e) |
990 | 32.4k | code = set_proc(pgls, |
991 | 32.4k | -pgls->g.fill.param.user_defined. |
992 | 32.4k | pattern_index, 1); |
993 | 0 | else |
994 | 0 | code = set_proc(pgls, |
995 | 0 | (pgls->g.fill.param.user_defined. |
996 | 0 | use_current_pen ? -pgls->g.fill. |
997 | 0 | param.user_defined. |
998 | 0 | pattern_index : pgls->g.fill.param. |
999 | 0 | user_defined.pattern_index), |
1000 | 0 | hpgl_get_selected_pen(pgls) |
1001 | 0 | ); |
1002 | | |
1003 | 32.4k | break; |
1004 | 0 | default: |
1005 | 0 | dmprintf(pgls->memory, |
1006 | 0 | "hpgl_set_drawing_color: internal error illegal fill\n"); |
1007 | 0 | break; |
1008 | 35.1k | } |
1009 | 35.1k | break; |
1010 | | |
1011 | 72.7k | case hpgl_rm_vector: |
1012 | 2.33M | case hpgl_rm_vector_fill: |
1013 | 2.33M | set_proc = pcl_pattern_get_proc_SV(pgls->g.screen.type); |
1014 | 2.33M | switch (pgls->g.screen.type) { |
1015 | | |
1016 | 2.33M | case hpgl_SV_pattern_solid_pen: |
1017 | 2.33M | code = set_proc(pgls, hpgl_get_selected_pen(pgls), false); |
1018 | 2.33M | break; |
1019 | | |
1020 | 0 | case hpgl_SV_pattern_shade: |
1021 | 0 | code = set_proc(pgls, |
1022 | 0 | pgls->g.screen.param.shading, |
1023 | 0 | hpgl_get_selected_pen(pgls) |
1024 | 0 | ); |
1025 | 0 | break; |
1026 | | |
1027 | 0 | case hpgl_SV_pattern_cross_hatch: |
1028 | 0 | code = set_proc(pgls, |
1029 | 0 | pgls->g.screen.param.pattern_type, |
1030 | 0 | hpgl_get_selected_pen(pgls) |
1031 | 0 | ); |
1032 | 0 | break; |
1033 | | |
1034 | 0 | case hpgl_SV_pattern_RF: |
1035 | 0 | code = set_proc(pgls, |
1036 | 0 | (pgls->g.screen.param.user_defined. |
1037 | 0 | use_current_pen ? -pgls->g.screen.param. |
1038 | 0 | user_defined.pattern_index : pgls->g. |
1039 | 0 | screen.param.user_defined.pattern_index), |
1040 | 0 | hpgl_get_selected_pen(pgls) |
1041 | 0 | ); |
1042 | 0 | break; |
1043 | | |
1044 | 0 | case hpgl_SV_pattern_user_defined: |
1045 | 0 | code = set_proc(pgls, |
1046 | 0 | pgls->g.screen.param.pattern_id, |
1047 | 0 | hpgl_get_selected_pen(pgls) |
1048 | 0 | ); |
1049 | 0 | break; |
1050 | | |
1051 | 0 | default: |
1052 | 0 | dmprintf(pgls->memory, |
1053 | 0 | "hpgl_set_drawing_color: internal error illegal fill\n"); |
1054 | 0 | break; |
1055 | 2.33M | } |
1056 | 2.33M | break; |
1057 | | |
1058 | 2.33M | default: |
1059 | 0 | dmprintf(pgls->memory, |
1060 | 0 | "hpgl_set_drawing_color: internal error illegal mode\n"); |
1061 | 0 | break; |
1062 | 2.99M | } |
1063 | | |
1064 | 2.99M | if (code >= 0) { |
1065 | 2.99M | code = check_rasterops(pgls, pgls->logical_op); |
1066 | 2.99M | if (code >= 0) { |
1067 | 2.99M | code = gs_setrasterop(pgls->pgs, hpgl_rop(pgls, render_mode)); |
1068 | 2.99M | if (code < 0) |
1069 | 0 | return code; |
1070 | 2.99M | } |
1071 | 2.99M | code = gx_set_dev_color(pgls->pgs); |
1072 | 2.99M | if (code == gs_error_Remap_Color) |
1073 | 0 | code = pixmap_high_level_pattern(pgls->pgs); |
1074 | 2.99M | } |
1075 | | |
1076 | 2.99M | if (code < 0) |
1077 | 0 | return code; |
1078 | | |
1079 | | /* make sure the halftone is set */ |
1080 | 2.99M | return pcl_ht_set_halftone(pgls); |
1081 | 2.99M | } |
1082 | | |
1083 | | static int |
1084 | | hpgl_set_drawing_state(hpgl_state_t * pgls, hpgl_rendering_mode_t render_mode) |
1085 | 2.58M | { |
1086 | | /* do dash stuff. */ |
1087 | 2.58M | hpgl_call(hpgl_set_graphics_dash_state(pgls)); |
1088 | | |
1089 | | /* joins, caps, and line width. */ |
1090 | 2.58M | hpgl_call(hpgl_set_graphics_line_attribute_state(pgls, render_mode)); |
1091 | | |
1092 | | /* set up a clipping region */ |
1093 | 2.58M | hpgl_call(hpgl_set_clipping_region(pgls, render_mode)); |
1094 | | |
1095 | | /* set up the hpgl fills (GL's source transp. is PCL's pattern trasp. */ |
1096 | 2.58M | pgls->pattern_transparent = pgls->g.source_transparent; |
1097 | 2.58M | hpgl_call(hpgl_set_drawing_color(pgls, render_mode)); |
1098 | | |
1099 | 2.58M | return 0; |
1100 | 2.58M | } |
1101 | | |
1102 | | int |
1103 | | hpgl_get_current_position(hpgl_state_t * pgls, gs_point * pt) |
1104 | 395k | { |
1105 | 395k | *pt = pgls->g.pos; |
1106 | 395k | return 0; |
1107 | 395k | } |
1108 | | |
1109 | | int |
1110 | | hpgl_update_carriage_return_pos(hpgl_state_t * pgls) |
1111 | 630k | { |
1112 | 630k | pgls->g.carriage_return_pos = pgls->g.pos; |
1113 | 630k | return 0; |
1114 | 630k | } |
1115 | | |
1116 | | int |
1117 | | hpgl_set_current_position(hpgl_state_t * pgls, gs_point * pt) |
1118 | 9.54M | { |
1119 | 9.54M | pgls->g.pos = *pt; |
1120 | 9.54M | return 0; |
1121 | 9.54M | } |
1122 | | |
1123 | | static int |
1124 | | update_pos(hpgl_state_t * pgls, double x, double y, hpgl_plot_function_t func) |
1125 | 9.34M | { |
1126 | 9.34M | gs_point point; |
1127 | | |
1128 | 9.34M | if (hpgl_plot_is_absolute(func)) |
1129 | 9.06M | hpgl_set_lost_mode(pgls, hpgl_lost_mode_cleared); |
1130 | | |
1131 | | /* update hpgl's state position */ |
1132 | 9.34M | if (hpgl_plot_is_absolute(func)) { |
1133 | 9.06M | point.x = x; |
1134 | 9.06M | point.y = y; |
1135 | 9.06M | } else { |
1136 | 283k | hpgl_call(hpgl_get_current_position(pgls, &point)); |
1137 | 283k | point.x += x; |
1138 | 283k | point.y += y; |
1139 | 283k | } |
1140 | | |
1141 | 9.34M | hpgl_call(hpgl_set_current_position(pgls, &point)); |
1142 | 9.34M | return 0; |
1143 | 9.34M | } |
1144 | | |
1145 | | int |
1146 | | hpgl_add_point_to_path(hpgl_state_t * pgls, |
1147 | | double x, |
1148 | | double y, hpgl_plot_function_t func, bool set_ctm) |
1149 | 9.34M | { |
1150 | 9.34M | static int (*const gs_procs[]) (gs_gstate *, double, double) |
1151 | 9.34M | = { |
1152 | 9.34M | hpgl_plot_function_procedures}; |
1153 | 9.34M | int code = 0; |
1154 | 9.34M | gx_path *ppath = gx_current_path(pgls->pgs); |
1155 | | |
1156 | 9.34M | if (gx_path_is_null(ppath)) { |
1157 | | /* Start a new GL/2 path. */ |
1158 | 2.74M | gs_point current_pt; |
1159 | | |
1160 | 2.74M | if (set_ctm) |
1161 | 2.74M | hpgl_call(hpgl_set_ctm(pgls)); |
1162 | 2.74M | if ((func != hpgl_plot_move_absolute) && |
1163 | 73.9k | (!pgls->g.subpolygon_started)) { |
1164 | | /* moveto the current position */ |
1165 | 73.9k | hpgl_call(hpgl_get_current_position(pgls, ¤t_pt)); |
1166 | 73.9k | hpgl_call_check_lost(gs_moveto(pgls->pgs, |
1167 | 73.9k | current_pt.x, current_pt.y)); |
1168 | 73.9k | } |
1169 | 2.74M | } |
1170 | | |
1171 | | /* start a new subpath if necessary */ |
1172 | 9.34M | if (pgls->g.subpolygon_started == true) { |
1173 | 35.9k | hpgl_plot_function_t startup_func; |
1174 | | |
1175 | 35.9k | pgls->g.subpolygon_started = false; |
1176 | 35.9k | if (hpgl_plot_is_relative(func)) |
1177 | 3 | startup_func = hpgl_plot_move_relative; |
1178 | 35.9k | else |
1179 | 35.9k | startup_func = hpgl_plot_move_absolute; |
1180 | 35.9k | hpgl_set_hpgl_path_mode(pgls, false); |
1181 | 35.9k | code = (*gs_procs[startup_func]) (pgls->pgs, x, y); |
1182 | 35.9k | hpgl_set_hpgl_path_mode(pgls, true); |
1183 | 35.9k | update_pos(pgls, x, y, func); |
1184 | 9.31M | } else { /* no new subpath */ |
1185 | 9.31M | code = (*gs_procs[func]) (pgls->pgs, x, y); |
1186 | 9.31M | if (code < 0) { |
1187 | 0 | if (code == gs_error_limitcheck) |
1188 | 0 | hpgl_set_lost_mode(pgls, hpgl_lost_mode_entered); |
1189 | 9.31M | } else { |
1190 | 9.31M | update_pos(pgls, x, y, func); |
1191 | 9.31M | } |
1192 | 9.31M | } |
1193 | 9.34M | return code; |
1194 | 9.34M | } |
1195 | | |
1196 | | void |
1197 | | hpgl_set_hpgl_path_mode(hpgl_state_t * pgls, bool enable) |
1198 | 445k | { |
1199 | | /* |
1200 | | * This was used to make paths for high level devices not containn 'gapto' segments. I |
1201 | | * have no idea why, because the vector devices handle gapto and removing it prevents |
1202 | | * proper handling of things like Fill Polygon (FP) HP-GL/2 commands. |
1203 | | * I've removed the test, but left it commented out in case there really is a good reason. |
1204 | | if (!pgls->high_level_device) |
1205 | | */ |
1206 | 445k | gs_sethpglpathmode(pgls->pgs, enable); |
1207 | 445k | } |
1208 | | /* destroy the current path. */ |
1209 | | int |
1210 | | hpgl_clear_current_path(hpgl_state_t * pgls) |
1211 | 1.10M | { |
1212 | 1.10M | hpgl_call(gs_newpath(pgls->pgs)); |
1213 | 1.10M | return 0; |
1214 | 1.10M | } |
1215 | | |
1216 | | /* closes the current path, making the first point and last point coincident */ |
1217 | | int |
1218 | | hpgl_close_current_path(hpgl_state_t * pgls) |
1219 | 108k | { |
1220 | 108k | gx_path *ppath = gx_current_path(pgls->pgs); |
1221 | | |
1222 | 108k | if (ppath->subpath_count) { |
1223 | 74.5k | gs_point pt; |
1224 | | |
1225 | 74.5k | hpgl_call(gs_closepath(pgls->pgs)); |
1226 | 74.5k | hpgl_call(gs_currentpoint(pgls->pgs, &pt)); |
1227 | 74.5k | hpgl_call(hpgl_set_current_position(pgls, &pt)); |
1228 | 74.5k | } |
1229 | 108k | return 0; |
1230 | 108k | } |
1231 | | |
1232 | | /* converts pcl coordinate to device space and back to hpgl space */ |
1233 | | int |
1234 | | hpgl_add_pcl_point_to_path(hpgl_state_t * pgls, const gs_point * pcl_pt) |
1235 | 1.45k | { |
1236 | 1.45k | gs_point dev_pt, hpgl_pt; |
1237 | | |
1238 | 1.45k | hpgl_call(hpgl_clear_current_path(pgls)); |
1239 | 1.45k | pcl_set_ctm(pgls, true); |
1240 | 1.45k | hpgl_call(gs_transform(pgls->pgs, pcl_pt->x, pcl_pt->y, &dev_pt)); |
1241 | 1.45k | hpgl_call(hpgl_set_ctm(pgls)); |
1242 | 1.45k | hpgl_call(gs_itransform(pgls->pgs, dev_pt.x, dev_pt.y, &hpgl_pt)); |
1243 | 1.45k | hpgl_call(hpgl_add_point_to_path(pgls, hpgl_pt.x, hpgl_pt.y, |
1244 | 1.45k | hpgl_plot_move_absolute, true)); |
1245 | 1.45k | return 0; |
1246 | 1.45k | } |
1247 | | |
1248 | | static double |
1249 | | compute_chord_angle(double chord_angle) |
1250 | 35.9k | { |
1251 | 35.9k | double ca = fmod(chord_angle, 360); |
1252 | | |
1253 | 35.9k | if (ca >= 0) { |
1254 | 35.9k | if (ca > 180) |
1255 | 2 | ca = 360 - ca; |
1256 | 35.9k | if (ca < 5) |
1257 | 60 | ca = 5; |
1258 | 35.9k | } else { /* negative ca */ |
1259 | |
|
1260 | 0 | if (ca > -5) |
1261 | 0 | ca = -5; |
1262 | 0 | } |
1263 | 35.9k | return ca; |
1264 | 35.9k | } |
1265 | | |
1266 | | int |
1267 | | hpgl_add_arc_to_path(hpgl_state_t * pgls, double center_x, double center_y, |
1268 | | double radius, double start_angle, double sweep_angle, |
1269 | | double chord_angle, bool start_moveto, |
1270 | | hpgl_plot_function_t draw, bool set_ctm) |
1271 | 35.9k | { |
1272 | 35.9k | double num_chordsf = sweep_angle / compute_chord_angle(chord_angle); |
1273 | 35.9k | int num_chords = |
1274 | 35.9k | (int)(num_chordsf >= 0.0 ? ceil(num_chordsf) : floor(num_chordsf)); |
1275 | 35.9k | double integral_chord_angle = fabs(sweep_angle / num_chords); |
1276 | 35.9k | int i; |
1277 | 35.9k | double arccoord_x, arccoord_y; |
1278 | | |
1279 | 35.9k | if (hpgl_plot_is_draw(draw)) |
1280 | 35.9k | pgls->g.have_drawn_in_path = true; |
1281 | | |
1282 | 35.9k | (void)hpgl_compute_arc_coords(radius, center_x, center_y, |
1283 | 35.9k | start_angle, &arccoord_x, &arccoord_y); |
1284 | | |
1285 | 35.9k | pgls->g.pos.x = arccoord_x; |
1286 | 35.9k | pgls->g.pos.y = arccoord_y; |
1287 | | |
1288 | 35.9k | if (start_moveto) |
1289 | 35.9k | pgls->g.subpolygon_started = true; |
1290 | | |
1291 | 35.9k | hpgl_call(hpgl_add_point_to_path(pgls, arccoord_x, arccoord_y, |
1292 | 35.9k | (draw && !start_moveto ? |
1293 | 35.9k | hpgl_plot_draw_absolute : |
1294 | 35.9k | hpgl_plot_move_absolute), set_ctm)); |
1295 | | |
1296 | 2.62M | for (i = 0; i < abs(num_chords); i++) { |
1297 | 2.58M | if (sweep_angle > 0) |
1298 | 2.58M | start_angle += integral_chord_angle; |
1299 | 0 | else |
1300 | 0 | start_angle -= integral_chord_angle; |
1301 | 2.58M | hpgl_compute_arc_coords(radius, center_x, center_y, |
1302 | 2.58M | start_angle, &arccoord_x, &arccoord_y); |
1303 | 2.58M | hpgl_call(hpgl_add_point_to_path(pgls, arccoord_x, arccoord_y, |
1304 | 2.58M | (draw ? hpgl_plot_draw_absolute : |
1305 | 2.58M | hpgl_plot_move_absolute), set_ctm)); |
1306 | 2.58M | } |
1307 | | /* NB this is suspicious - why not any +- multiple of 360 if this |
1308 | | is correct at all. */ |
1309 | 35.9k | if (sweep_angle - 360.0 > -0.0001) |
1310 | 35.9k | hpgl_call(hpgl_close_current_path(pgls)); |
1311 | 35.9k | return 0; |
1312 | 35.9k | } |
1313 | | |
1314 | | /* add a 3 point arc to the path */ |
1315 | | int |
1316 | | hpgl_add_arc_3point_to_path(hpgl_state_t * pgls, double start_x, double |
1317 | | start_y, double inter_x, double inter_y, |
1318 | | double end_x, double end_y, double chord_angle, |
1319 | | hpgl_plot_function_t draw) |
1320 | 9 | { |
1321 | | /* handle unusual cases as per pcltrm */ |
1322 | 9 | if (hpgl_3_same_points(start_x, start_y, inter_x, inter_y, end_x, end_y)) { |
1323 | 0 | hpgl_call(hpgl_add_point_to_path(pgls, start_x, start_y, draw, true)); |
1324 | 0 | return 0; |
1325 | 0 | } |
1326 | 9 | if (hpgl_3_no_intermediate(start_x, start_y, |
1327 | 9 | inter_x, inter_y, end_x, end_y)) { |
1328 | 0 | hpgl_call(hpgl_add_point_to_path(pgls, start_x, start_y, draw, true)); |
1329 | 0 | hpgl_call(hpgl_add_point_to_path(pgls, end_x, end_y, draw, true)); |
1330 | 0 | return 0; |
1331 | 0 | } |
1332 | 9 | if (hpgl_3_same_endpoints(start_x, start_y, |
1333 | 9 | inter_x, y_inter, end_x, end_y)) { |
1334 | 0 | hpgl_call(hpgl_add_arc_to_path(pgls, (start_x + inter_x) / 2.0, |
1335 | 0 | (start_y + inter_y) / 2.0, |
1336 | 0 | (hypot((inter_x - start_x), |
1337 | 0 | (inter_y - start_y)) / 2.0), |
1338 | 0 | 0.0, 360.0, chord_angle, false, |
1339 | 0 | draw, true)); |
1340 | 0 | return 0; |
1341 | 0 | } |
1342 | | |
1343 | 9 | if (hpgl_3_colinear_points |
1344 | 9 | (start_x, start_y, inter_x, inter_y, end_x, end_y)) { |
1345 | 0 | if (hpgl_3_intermediate_between |
1346 | 0 | (start_x, start_y, inter_x, inter_y, end_x, end_y)) { |
1347 | 0 | hpgl_call(hpgl_add_point_to_path |
1348 | 0 | (pgls, start_x, start_y, draw, true)); |
1349 | 0 | hpgl_call(hpgl_add_point_to_path(pgls, end_x, end_x, draw, true)); |
1350 | 0 | } else { |
1351 | 0 | hpgl_call(hpgl_add_point_to_path |
1352 | 0 | (pgls, start_x, start_y, draw, true)); |
1353 | 0 | hpgl_call(hpgl_add_point_to_path |
1354 | 0 | (pgls, inter_x, inter_y, draw, true)); |
1355 | 0 | hpgl_call(hpgl_add_point_to_path(pgls, end_x, end_y, draw, true)); |
1356 | 0 | } |
1357 | 0 | return 0; |
1358 | 0 | } |
1359 | | |
1360 | | /* normal 3 point arc case */ |
1361 | 9 | { |
1362 | 9 | hpgl_real_t center_x, center_y, radius; |
1363 | 9 | hpgl_real_t start_angle, inter_angle, end_angle; |
1364 | 9 | hpgl_real_t sweep_angle; |
1365 | | |
1366 | 9 | hpgl_call(hpgl_compute_arc_center(start_x, start_y, |
1367 | 9 | inter_x, inter_y, |
1368 | 9 | end_x, end_y, |
1369 | 9 | ¢er_x, ¢er_y)); |
1370 | | |
1371 | 9 | radius = hypot(start_x - center_x, start_y - center_y); |
1372 | 9 | start_angle = radians_to_degrees * |
1373 | 9 | hpgl_compute_angle(start_x - center_x, start_y - center_y); |
1374 | | |
1375 | 9 | inter_angle = radians_to_degrees * |
1376 | 9 | hpgl_compute_angle(inter_x - center_x, inter_y - center_y); |
1377 | | |
1378 | 9 | end_angle = radians_to_degrees * |
1379 | 9 | hpgl_compute_angle(end_x - center_x, end_y - center_y); |
1380 | 9 | sweep_angle = end_angle - start_angle; |
1381 | | |
1382 | | /* |
1383 | | * Figure out which direction to draw the arc, depending on the |
1384 | | * relative position of start, inter, and end. Case analysis |
1385 | | * shows that we should draw the arc counter-clockwise from S to |
1386 | | * E iff exactly 2 of S<I, I<E, and E<S are true, and clockwise |
1387 | | * if exactly 1 of these relations is true. (These are the only |
1388 | | * possible cases if no 2 of the points coincide.) |
1389 | | */ |
1390 | | |
1391 | 9 | if ((start_angle < inter_angle) + (inter_angle < end_angle) + |
1392 | 9 | (end_angle < start_angle) == 1) { |
1393 | 0 | if (sweep_angle > 0) |
1394 | 0 | sweep_angle -= 360; |
1395 | 9 | } else { |
1396 | 9 | if (sweep_angle < 0) |
1397 | 0 | sweep_angle += 360; |
1398 | 9 | } |
1399 | | |
1400 | 9 | hpgl_call(hpgl_add_arc_to_path(pgls, center_x, center_y, |
1401 | 9 | radius, start_angle, sweep_angle, |
1402 | 9 | (sweep_angle < 0.0) ? |
1403 | 9 | -chord_angle : chord_angle, false, |
1404 | 9 | draw, true)); |
1405 | 9 | return 0; |
1406 | 9 | } |
1407 | 9 | } |
1408 | | |
1409 | | /* Bezier's are handled a bit differently than arcs as we use the |
1410 | | gs_curveto() operator directly in lieue of flattening and using |
1411 | | gs_moveto() and gs_lineto(). */ |
1412 | | |
1413 | | int |
1414 | | hpgl_add_bezier_to_path(hpgl_state_t * pgls, double x1, double y1, |
1415 | | double x2, double y2, double x3, double y3, |
1416 | | double x4, double y4, hpgl_plot_function_t draw) |
1417 | 0 | { |
1418 | 0 | gx_path *ppath = gx_current_path(pgls->pgs); |
1419 | | |
1420 | | /* Don't add superflous points in polygon mode */ |
1421 | 0 | if ((!pgls->g.polygon_mode) || (!ppath->current_subpath)) |
1422 | 0 | hpgl_call(hpgl_add_point_to_path(pgls, x1, y1, |
1423 | 0 | draw ? |
1424 | 0 | hpgl_plot_draw_absolute : |
1425 | 0 | hpgl_plot_move_absolute, true)); |
1426 | 0 | if (draw) |
1427 | 0 | hpgl_call(gs_curveto(pgls->pgs, x2, y2, x3, y3, x4, y4)); |
1428 | | /* update hpgl's state position to last point of the curve. */ |
1429 | 0 | { |
1430 | 0 | gs_point point; |
1431 | |
|
1432 | 0 | point.x = x4; |
1433 | 0 | point.y = y4; |
1434 | 0 | hpgl_call(hpgl_set_current_position(pgls, &point)); |
1435 | 0 | } |
1436 | 0 | return 0; |
1437 | 0 | } |
1438 | | |
1439 | | /* |
1440 | | * an implicit gl/2 style closepath. If the first and last point are |
1441 | | * the same the path gets closed. HAS - eliminate CSE gx_current_path |
1442 | | */ |
1443 | | int |
1444 | | hpgl_close_path(hpgl_state_t * pgls) |
1445 | 2.76M | { |
1446 | 2.76M | gs_fixed_point first, current; |
1447 | 2.76M | gx_path *ppath = gx_current_path(pgls->pgs); |
1448 | | |
1449 | 2.76M | if (!ppath->current_subpath) |
1450 | 178k | return 0; |
1451 | | |
1452 | | /* if we do not have a subpath there is nothing to do, get the |
1453 | | first points of the path in device space and convert to floats */ |
1454 | 2.58M | if (gx_path_subpath_start_point(ppath, &first) < 0) |
1455 | 0 | return 0; |
1456 | | |
1457 | 2.58M | if (gx_path_current_point(ppath, ¤t) < 0) |
1458 | 0 | return 0; |
1459 | | |
1460 | 2.58M | if (first.x == current.x && first.y == current.y) |
1461 | 2.58M | hpgl_call(gs_closepath(pgls->pgs)); |
1462 | | |
1463 | 2.58M | return 0; |
1464 | 2.58M | } |
1465 | | |
1466 | | /* To implement centered we move the points in the path to a pixel boundary. |
1467 | | Note this routine is sensitive to the orientation of the device. |
1468 | | Since rounding 0.5 is used here and 0.5 fill adjust this can fail to reduce and |
1469 | | object in size by 1 pixel left/bottom for some user scale factors. This is not a |
1470 | | grave concern as the objects will still seam together. */ |
1471 | | |
1472 | | static int |
1473 | | hpgl_set_special_pixel_placement(hpgl_state_t * pgls, |
1474 | | hpgl_rendering_mode_t render_mode) |
1475 | 34.2k | { |
1476 | 34.2k | if (pgls->pp_mode == 1) { |
1477 | 0 | gs_matrix default_matrix; |
1478 | 0 | gs_point distance, adjust; |
1479 | 0 | gx_path *ppath = gx_current_path(pgls->pgs); |
1480 | | |
1481 | | /* arbitrary just need the signs after transformation to |
1482 | | device space */ |
1483 | 0 | gs_setfilladjust(pgls->pgs, 0, 0); |
1484 | 0 | adjust.x = -1; |
1485 | 0 | adjust.y = -1; |
1486 | | /* determine the adjustment in device space */ |
1487 | 0 | hpgl_call(gs_defaultmatrix(pgls->pgs, &default_matrix)); |
1488 | 0 | hpgl_call(gs_distance_transform |
1489 | 0 | (adjust.x, adjust.y, &default_matrix, &distance)); |
1490 | | /* modify the path but first it should be "unshared" so the |
1491 | | translation (next statement) does not update the polygon |
1492 | | buffer */ |
1493 | 0 | hpgl_call(gx_path_unshare(ppath)); |
1494 | | |
1495 | | /* translate all points in the path by the adjustment. */ |
1496 | 0 | hpgl_call(gx_path_translate(ppath, |
1497 | 0 | float2fixed(distance.x / |
1498 | 0 | fabs(distance.x)), |
1499 | 0 | float2fixed(distance.y / |
1500 | 0 | fabs(distance.y)))); |
1501 | 0 | } |
1502 | 34.2k | return 0; |
1503 | 34.2k | } |
1504 | | |
1505 | | /* |
1506 | | * Draw (stroke or fill) the current path. |
1507 | | */ |
1508 | | int |
1509 | | hpgl_draw_current_path(hpgl_state_t * pgls, hpgl_rendering_mode_t render_mode) |
1510 | 3.36M | { |
1511 | 3.36M | gs_gstate *pgs = pgls->pgs; |
1512 | 3.36M | pcl_pattern_set_proc_t set_proc; |
1513 | 3.36M | int code = 0; |
1514 | 3.36M | gx_path *ppath = gx_current_path(pgls->pgs); |
1515 | | |
1516 | | /* check if we have a current path - we don't need the current |
1517 | | point */ |
1518 | 3.36M | if ((ppath->segments != NULL) && (!ppath->current_subpath)) { |
1519 | | /* NB this clear shouldn't be necessary. */ |
1520 | 782k | hpgl_call(hpgl_clear_current_path(pgls)); |
1521 | 782k | return 0; |
1522 | 782k | } |
1523 | | |
1524 | 2.58M | if (render_mode == hpgl_rm_vector_no_close) |
1525 | 30 | render_mode = hpgl_rm_vector; |
1526 | 2.58M | else |
1527 | 2.58M | hpgl_call(hpgl_close_path(pgls)); |
1528 | | |
1529 | 2.58M | hpgl_call(hpgl_set_drawing_state(pgls, render_mode)); |
1530 | | |
1531 | 2.58M | switch (render_mode) { |
1532 | 209k | case hpgl_rm_character: |
1533 | 209k | { |
1534 | | /* HAS need to set attributes in set_drawing color (next 2) */ |
1535 | | |
1536 | | /* Intellifonts require eofill, but TrueType require fill. */ |
1537 | | /****** HACK: look at the scaling technology of ******/ |
1538 | | /****** the current font to decide. ******/ |
1539 | 209k | int (*fill) (gs_gstate *); |
1540 | | |
1541 | 209k | if (pgls->g.font->scaling_technology == plfst_Intellifont) |
1542 | 0 | fill = gs_eofill; |
1543 | 209k | else |
1544 | 209k | fill = gs_fill; |
1545 | | |
1546 | 209k | switch (pgls->g.character.fill_mode) { |
1547 | | |
1548 | 208k | case hpgl_char_solid_edge: |
1549 | 208k | set_proc = |
1550 | 208k | pcl_pattern_get_proc_FT |
1551 | 208k | (hpgl_FT_pattern_solid_pen1); |
1552 | 208k | if ((code = |
1553 | 208k | set_proc(pgls, hpgl_get_selected_pen(pgls), |
1554 | 208k | false)) < 0) |
1555 | 0 | return code; |
1556 | 208k | hpgl_call((*fill) (pgs)); |
1557 | | /* falls through */ |
1558 | | |
1559 | 208k | case hpgl_char_edge: |
1560 | 208k | if (pgls->g.bitmap_fonts_allowed) |
1561 | 0 | break; /* no edging */ |
1562 | 208k | set_proc = |
1563 | 208k | pcl_pattern_get_proc_FT |
1564 | 208k | (hpgl_FT_pattern_solid_pen1); |
1565 | 208k | if ((code = |
1566 | 208k | set_proc(pgls, hpgl_get_character_edge_pen(pgls), |
1567 | 208k | false)) < 0) |
1568 | 0 | return code; |
1569 | 208k | if (hpgl_is_currentfont_stick_or_arc(pgls)) { |
1570 | 207k | hpgl_call((*fill) (pgs)); |
1571 | 207k | break; |
1572 | 207k | } |
1573 | | |
1574 | 1.79k | hpgl_call(hpgl_set_plu_ctm(pgls)); |
1575 | 1.79k | { |
1576 | 1.79k | gs_point scale = hpgl_current_char_scale(pgls); |
1577 | | |
1578 | | /* NB fix and comment */ |
1579 | 1.79k | hpgl_call(gs_setlinewidth |
1580 | 1.79k | (pgls->pgs, |
1581 | 1.79k | min(scale.x, |
1582 | 1.79k | scale.y) * 0.0375 * 0.2835)); |
1583 | 1.79k | } |
1584 | 1.79k | hpgl_call(pcl_mark_page_for_path(pgls)); |
1585 | 1.79k | hpgl_call(gs_stroke(pgls->pgs)); |
1586 | 1.79k | break; |
1587 | | |
1588 | 1.79k | case hpgl_char_fill: |
1589 | | /* the fill has already been done if the fill type is |
1590 | | hpgl/2 vector fills. This was handled when we set |
1591 | | the drawing color */ |
1592 | 0 | if ((pgls->g.fill.type != hpgl_FT_pattern_one_line) && |
1593 | 0 | (pgls->g.fill.type != hpgl_FT_pattern_two_lines)) |
1594 | 0 | hpgl_call((*fill) (pgs)); |
1595 | 0 | else |
1596 | 0 | hpgl_call(hpgl_clear_current_path(pgls)); |
1597 | 0 | break; |
1598 | 902 | case hpgl_char_fill_edge: |
1599 | | /* the fill has already been done if the fill type is |
1600 | | hpgl/2 vector fills. This was handled when we set |
1601 | | the drawing color. gsave to preserve the path for |
1602 | | subsequent edging */ |
1603 | 902 | if ((pgls->g.fill.type != hpgl_FT_pattern_one_line) && |
1604 | 902 | (pgls->g.fill.type != |
1605 | 902 | hpgl_FT_pattern_two_lines)) { |
1606 | 902 | int code2 = 0; |
1607 | 902 | hpgl_call(hpgl_gsave(pgls)); |
1608 | | /* all character fills appear to have 0 fill adjustment */ |
1609 | 902 | gs_setfilladjust(pgls->pgs, 0, 0); |
1610 | | /* Always restore before handling any error. */ |
1611 | 902 | code = (*fill) (pgs); |
1612 | 902 | code2 = hpgl_grestore(pgls); |
1613 | 902 | if (code >=0 && code2 < 0) { |
1614 | 0 | code = code2; |
1615 | 0 | } |
1616 | 902 | hpgl_call(code); |
1617 | 902 | } |
1618 | 902 | if ((pgls->g.bitmap_fonts_allowed) || (hpgl_is_currentfont_stick_or_arc(pgls))) /* no edging */ |
1619 | 902 | hpgl_call(hpgl_clear_current_path(pgls)); |
1620 | 0 | else { |
1621 | 0 | set_proc = |
1622 | 0 | pcl_pattern_get_proc_FT |
1623 | 0 | (hpgl_FT_pattern_solid_pen1); |
1624 | 0 | if ((code = |
1625 | 0 | set_proc(pgls, |
1626 | 0 | hpgl_get_character_edge_pen(pgls), |
1627 | 0 | false)) < 0) |
1628 | 0 | return code; |
1629 | 0 | hpgl_call(hpgl_set_plu_ctm(pgls)); |
1630 | | /* use the default raster operation for the edge. It |
1631 | | is automaticall restored next drawing operation */ |
1632 | 0 | hpgl_call(gs_setrasterop |
1633 | 0 | (pgls->pgs, (gs_rop3_t) 252)); |
1634 | 0 | hpgl_call(gs_setlinewidth |
1635 | 0 | (pgls->pgs, |
1636 | 0 | pgls->g.font_selection[pgls->g. |
1637 | 0 | font_selected]. |
1638 | 0 | params.height_4ths * 0.0375)); |
1639 | 0 | hpgl_call(gs_stroke(pgls->pgs)); |
1640 | 0 | } |
1641 | 902 | break; |
1642 | | |
1643 | 209k | } |
1644 | 209k | break; |
1645 | 209k | } |
1646 | 209k | break; |
1647 | 209k | case hpgl_rm_polygon: |
1648 | 34.2k | hpgl_set_special_pixel_placement(pgls, hpgl_rm_polygon); |
1649 | 34.2k | if ((code = pcl_mark_page_for_path(pgls)) < 0) |
1650 | 0 | return code; |
1651 | 34.2k | if (pgls->g.fill_type == hpgl_even_odd_rule) |
1652 | 34.2k | hpgl_call(gs_eofill(pgs)); |
1653 | 1 | else /* hpgl_winding_number_rule */ |
1654 | 34.2k | hpgl_call(gs_fill(pgs)); |
1655 | 34.2k | break; |
1656 | | |
1657 | 34.2k | case hpgl_rm_clip_and_fill_polygon: |
1658 | | /* |
1659 | | * A bizarre HPISM - If the pen is white we do a solid |
1660 | | * white fill this is true for *all* fill types (arg!) as |
1661 | | * tested on the 6mp. If pen 1 (black) |
1662 | | * hpgl_set_drawing_color() handles this case by drawing |
1663 | | * the lines that comprise the vector fill |
1664 | | */ |
1665 | 294 | if (hpgl_get_selected_pen(pgls) == 0) { |
1666 | 5 | hpgl_call(pcl_mark_page_for_path(pgls)); |
1667 | 5 | hpgl_call(gs_fill(pgls->pgs)); |
1668 | 5 | } |
1669 | 294 | hpgl_call(hpgl_clear_current_path(pgls)); |
1670 | 294 | break; |
1671 | | |
1672 | 72.7k | case hpgl_rm_vector: |
1673 | 2.33M | case hpgl_rm_vector_fill: |
1674 | | /* |
1675 | | * we reset the ctm before stroking to preserve the line width |
1676 | | * information, then restore the ctm. |
1677 | | */ |
1678 | 2.33M | { |
1679 | 2.33M | gs_matrix save_ctm; |
1680 | 2.33M | int save_scaling_type = pgls->g.scaling_type; |
1681 | | |
1682 | 2.33M | hpgl_call(gs_currentmatrix(pgs, &save_ctm)); |
1683 | | |
1684 | | /* force no picture frame scaling */ |
1685 | 2.33M | pgls->g.scaling_type = hpgl_scaling_point_factor; |
1686 | 2.33M | hpgl_call(hpgl_set_plu_ctm(pgls)); |
1687 | 2.33M | pgls->g.scaling_type = save_scaling_type; |
1688 | | |
1689 | | /* NB: what does reversing the path do? Currently DEAD code see pglfill.c */ |
1690 | 2.33M | if (!pgls->g.line.current.is_solid |
1691 | 49 | && (pgls->g.line.current.type == 0)) |
1692 | 2.33M | hpgl_call(gs_reversepath(pgls->pgs)); |
1693 | 2.33M | hpgl_call(pcl_mark_page_for_path(pgls)); |
1694 | 2.33M | hpgl_call(gs_stroke(pgls->pgs)); |
1695 | 2.33M | gs_setmatrix(pgs, &save_ctm); |
1696 | 2.33M | break; |
1697 | 2.33M | } |
1698 | 0 | default: |
1699 | 0 | dmprintf(pgls->memory, "unknown render mode\n"); |
1700 | 2.58M | } |
1701 | | |
1702 | 2.58M | return 0; |
1703 | 2.58M | } |
1704 | | |
1705 | | int |
1706 | | hpgl_copy_current_path_to_polygon_buffer(hpgl_state_t * pgls) |
1707 | 139k | { |
1708 | 139k | gx_path *ppath = gx_current_path(pgls->pgs); |
1709 | | |
1710 | 139k | gx_path_assign_preserve(&pgls->g.polygon.buffer.path, ppath); |
1711 | 139k | return 0; |
1712 | 139k | } |
1713 | | |
1714 | | int |
1715 | | hpgl_copy_polygon_buffer_to_current_path(hpgl_state_t * pgls) |
1716 | 46.0k | { |
1717 | 46.0k | gx_path *ppath = gx_current_path(pgls->pgs); |
1718 | | |
1719 | 46.0k | gx_path_assign_preserve(ppath, &pgls->g.polygon.buffer.path); |
1720 | 46.0k | return 0; |
1721 | 46.0k | } |