1 /* Copyright (C) 1995, 2000 Aladdin Enterprises. All rights reserved.
2
3 This software is provided AS-IS with no warranty, either express or
4 implied.
5
6 This software is distributed under license and may not be copied,
7 modified or distributed except as expressly authorized under the terms
8 of the license contained in the file LICENSE in this distribution.
9
10 For more information about licensing, please refer to
11 http://www.ghostscript.com/licensing/. For information on
12 commercial licensing, go to http://www.artifex.com/licensing/ or
13 contact Artifex Software, Inc., 101 Lucas Valley Road #110,
14 San Rafael, CA 94903, U.S.A., +1(415)492-9861.
15 */
16
17 /*$Id: gxclpath.c,v 1.21 2005/10/10 18:58:18 leonardo Exp $ */
18 /* Higher-level path operations for band lists */
19 #include "math_.h"
20 #include "memory_.h"
21 #include "gx.h"
22 #include "gpcheck.h"
23 #include "gserrors.h"
24 #include "gxdevice.h"
25 #include "gxdevmem.h" /* must precede gxcldev.h */
26 #include "gxcldev.h"
27 #include "gxclpath.h"
28 #include "gxcolor2.h"
29 #include "gxdcolor.h"
30 #include "gxpaint.h" /* for gx_fill/stroke_params */
31 #include "gzpath.h"
32 #include "gzcpath.h"
33 #include "stream.h"
34 #include "gsserial.h"
35
36 /* Statistics */
37 #ifdef DEBUG
38 ulong stats_cmd_diffs[5];
39 #endif
40
41 /* Forward declarations */
42 private int cmd_put_path(gx_device_clist_writer * cldev,
43 gx_clist_state * pcls, const gx_path * ppath,
44 fixed ymin, fixed ymax, byte op,
45 bool implicit_close, segment_notes keep_notes);
46
47 /* ------ Utilities ------ */
48
49 /* Compute the colors used by a colored halftone. */
50 private gx_color_index
colored_halftone_colors_used(gx_device_clist_writer * cldev,const gx_drawing_color * pdcolor)51 colored_halftone_colors_used(gx_device_clist_writer *cldev,
52 const gx_drawing_color *pdcolor)
53 {
54 /*
55 * We only know how to compute an accurate color set for the
56 * standard CMYK color mapping function.
57 */
58 if (dev_proc(cldev, map_cmyk_color) != cmyk_1bit_map_cmyk_color)
59 return ((gx_color_index)1 << cldev->color_info.depth) - 1;
60 /*
61 * Note that c_base[0], and the low-order bit of plane_mask,
62 * correspond to cyan: this requires reversing the bit order of
63 * the plane mask.
64 */
65 return
66 ((pdcolor->colors.colored.c_base[0] << 3) |
67 (pdcolor->colors.colored.c_base[1] << 2) |
68 (pdcolor->colors.colored.c_base[2] << 1) |
69 (pdcolor->colors.colored.c_base[3]) |
70 (byte_reverse_bits[pdcolor->colors.colored.plane_mask] >> 4));
71 }
72
73 /*
74 * Compute whether a drawing operation will require the slow (full-pixel)
75 * RasterOp implementation. If pdcolor is not NULL, it is the texture for
76 * the RasterOp.
77 */
78 bool
cmd_slow_rop(gx_device * dev,gs_logical_operation_t lop,const gx_drawing_color * pdcolor)79 cmd_slow_rop(gx_device *dev, gs_logical_operation_t lop,
80 const gx_drawing_color *pdcolor)
81 {
82 gs_rop3_t rop = lop_rop(lop);
83
84 if (pdcolor != 0 && gx_dc_is_pure(pdcolor)) {
85 gx_color_index color = gx_dc_pure_color(pdcolor);
86
87 if (color == gx_device_black(dev))
88 rop = rop3_know_T_0(rop);
89 else if (color == gx_device_white(dev))
90 rop = rop3_know_T_1(rop);
91 }
92 return !(rop == rop3_0 || rop == rop3_1 ||
93 rop == rop3_D || rop == rop3_S || rop == rop3_T);
94 }
95
96 /* Write out the color for filling, stroking, or masking. */
97 /* We should be able to share this with clist_tile_rectangle, */
98 /* but I don't see how to do it without adding a level of procedure. */
99 int
cmd_put_drawing_color(gx_device_clist_writer * cldev,gx_clist_state * pcls,const gx_drawing_color * pdcolor)100 cmd_put_drawing_color(gx_device_clist_writer * cldev, gx_clist_state * pcls,
101 const gx_drawing_color * pdcolor)
102 {
103 const gx_device_halftone * pdht = pdcolor->type->get_dev_halftone(pdcolor);
104 int code, di;
105 uint dc_size = 0, req_size;
106 gx_device_color_saved * psdc = &pcls->sdc;
107 byte * dp;
108 byte * dp0;
109 gs_int_point color_phase;
110
111 /* see if the halftone must be inserted in the command list */
112 if ( pdht != NULL &&
113 pdht->id != cldev->device_halftone_id ) {
114 if ((code = cmd_put_halftone(cldev, pdht)) < 0)
115 return code;
116 color_unset(psdc);
117 }
118
119 /* see if phase informaiton must be inserted in the command list */
120 if ( pdcolor->type->get_phase(pdcolor, &color_phase) &&
121 (color_phase.x != pcls->tile_phase.x ||
122 color_phase.y != pcls->tile_phase.y ) &&
123 (code = cmd_set_tile_phase( cldev,
124 pcls,
125 color_phase.x,
126 color_phase.y )) < 0 )
127 return code;
128
129 /*
130 * Get the device color type index and the required size.
131 *
132 * The complete cmd_opv_ext_put_drawing_color consists of:
133 * comand code (2 bytes)
134 * device color type index (1)
135 * length of serialized device color (enc_u_sizew(dc_size))
136 * the serialized device color itself (dc_size)
137 */
138 di = gx_get_dc_type_index(pdcolor);
139 code = pdcolor->type->write( pdcolor,
140 psdc,
141 (gx_device *)cldev,
142 0,
143 &dc_size );
144
145 /* if the returned value is > 0, no change in the color is necessary */
146 if (code > 0)
147 return 0;
148 else if (code < 0 && code != gs_error_rangecheck)
149 return code;
150 req_size = dc_size + 2 + 1 + enc_u_sizew(dc_size);
151
152 /*
153 * Encoded device colors are small in comparison to the command
154 * buffer size (< 64 bytes), so we can just clear space in the
155 * command buffer for them.
156 */
157 if ((code = set_cmd_put_op(dp, cldev, pcls, cmd_opv_extend, req_size)) < 0)
158 return code;
159 dp0 = dp;
160 dp[1] = cmd_opv_ext_put_drawing_color;
161 dp += 2;
162 *dp++ = di;
163 enc_u_putw(dc_size, dp);
164 code = pdcolor->type->write( pdcolor,
165 &pcls->sdc,
166 (gx_device *)cldev,
167 dp,
168 &dc_size );
169 if (code < 0) {
170 cldev->cnext = dp0;
171 return code;
172 }
173
174 /* should properly calculate colors_used, but for now just punt */
175 pcls->colors_used.or = ((gx_color_index)1 << cldev->color_info.depth) - 1;
176
177 /* record the color we have just serialized color */
178 pdcolor->type->save_dc(pdcolor, &pcls->sdc);
179
180 return code;
181 }
182
183 /* Compute the colors used by a drawing color. */
184 gx_color_index
cmd_drawing_colors_used(gx_device_clist_writer * cldev,const gx_drawing_color * pdcolor)185 cmd_drawing_colors_used(gx_device_clist_writer *cldev,
186 const gx_drawing_color * pdcolor)
187 {
188 if (gx_dc_is_pure(pdcolor))
189 return gx_dc_pure_color(pdcolor);
190 else if (gx_dc_is_binary_halftone(pdcolor))
191 return gx_dc_binary_color0(pdcolor) | gx_dc_binary_color1(pdcolor);
192 else if (gx_dc_is_colored_halftone(pdcolor))
193 return colored_halftone_colors_used(cldev, pdcolor);
194 else
195 return ((gx_color_index)1 << cldev->color_info.depth) - 1;
196 }
197
198
199 /* Clear (a) specific 'known' flag(s) for all bands. */
200 /* We must do this whenever the value of a 'known' parameter changes. */
201 void
cmd_clear_known(gx_device_clist_writer * cldev,uint known)202 cmd_clear_known(gx_device_clist_writer * cldev, uint known)
203 {
204 uint unknown = ~known;
205 gx_clist_state *pcls = cldev->states;
206 int i;
207
208 for (i = cldev->nbands; --i >= 0; ++pcls)
209 pcls->known &= unknown;
210 }
211
212 /* Check whether we need to change the clipping path in the device. */
213 bool
cmd_check_clip_path(gx_device_clist_writer * cldev,const gx_clip_path * pcpath)214 cmd_check_clip_path(gx_device_clist_writer * cldev, const gx_clip_path * pcpath)
215 {
216 if (pcpath == NULL)
217 return false;
218 /* The clip path might have moved in memory, so even if the */
219 /* ids match, update the pointer. */
220 cldev->clip_path = pcpath;
221 if (pcpath->id == cldev->clip_path_id)
222 return false;
223 cldev->clip_path_id = pcpath->id;
224 return true;
225 }
226
227 /*
228 * Check the graphics state elements that need to be up to date for filling
229 * or stroking.
230 */
231 #define FILL_KNOWN\
232 (cj_ac_sa_known | flatness_known | op_bm_tk_known | opacity_alpha_known |\
233 shape_alpha_known | fill_adjust_known | alpha_known | clip_path_known)
234 private void
cmd_check_fill_known(gx_device_clist_writer * cdev,const gs_imager_state * pis,floatp flatness,const gs_fixed_point * padjust,const gx_clip_path * pcpath,uint * punknown)235 cmd_check_fill_known(gx_device_clist_writer *cdev, const gs_imager_state *pis,
236 floatp flatness, const gs_fixed_point *padjust,
237 const gx_clip_path *pcpath, uint *punknown)
238 {
239 /*
240 * stroke_adjust is not needed for fills, and none of these are needed
241 * if the path has no curves, but it's easier to update them all.
242 */
243 if (state_neq(line_params.curve_join) || state_neq(accurate_curves) ||
244 state_neq(stroke_adjust)
245 ) {
246 *punknown |= cj_ac_sa_known;
247 state_update(line_params.curve_join);
248 state_update(accurate_curves);
249 state_update(stroke_adjust);
250 }
251 if (cdev->imager_state.flatness != flatness) {
252 *punknown |= flatness_known;
253 cdev->imager_state.flatness = flatness;
254 }
255 /*
256 * Note: overprint and overprint_mode are implemented via a compositor
257 * device, which is passed separately through the command list. Hence,
258 * though both parameters are passed in the state as well, this usually
259 * has no effect.
260 */
261 if (state_neq(overprint) || state_neq(overprint_mode) ||
262 state_neq(blend_mode) || state_neq(text_knockout)
263 ) {
264 *punknown |= op_bm_tk_known;
265 state_update(overprint);
266 state_update(overprint_mode);
267 state_update(blend_mode);
268 state_update(text_knockout);
269 }
270 if (state_neq(opacity.alpha)) {
271 *punknown |= opacity_alpha_known;
272 state_update(opacity.alpha);
273 }
274 if (state_neq(shape.alpha)) {
275 *punknown |= shape_alpha_known;
276 state_update(shape.alpha);
277 }
278 if (cdev->imager_state.fill_adjust.x != padjust->x ||
279 cdev->imager_state.fill_adjust.y != padjust->y
280 ) {
281 *punknown |= fill_adjust_known;
282 cdev->imager_state.fill_adjust = *padjust;
283 }
284 if (cdev->imager_state.alpha != pis->alpha) {
285 *punknown |= alpha_known;
286 state_update(alpha);
287 }
288 if (cmd_check_clip_path(cdev, pcpath))
289 *punknown |= clip_path_known;
290 }
291
292 /* Compute the written CTM length. */
293 int
cmd_write_ctm_return_length(gx_device_clist_writer * cldev,const gs_matrix * m)294 cmd_write_ctm_return_length(gx_device_clist_writer * cldev, const gs_matrix *m)
295 {
296 stream s;
297
298 s_init(&s, cldev->memory);
299 swrite_position_only(&s);
300 sput_matrix(&s, m);
301 return (uint)stell(&s);
302 }
303
304 /* Write out CTM. */
305 int
cmd_write_ctm(const gs_matrix * m,byte * dp,int len)306 cmd_write_ctm(const gs_matrix *m, byte *dp, int len)
307 {
308 stream s;
309
310 swrite_string(&s, dp + 1, len);
311 sput_matrix(&s, m);
312 return 0;
313 }
314
315 /* Write out values of any unknown parameters. */
316 int
cmd_write_unknown(gx_device_clist_writer * cldev,gx_clist_state * pcls,uint must_know)317 cmd_write_unknown(gx_device_clist_writer * cldev, gx_clist_state * pcls,
318 uint must_know)
319 {
320 uint unknown = ~pcls->known & must_know;
321 uint misc2_unknown = unknown & misc2_all_known;
322 byte *dp;
323 int code;
324
325 if (misc2_unknown) {
326 byte buf[
327 1 + /* cap_join */
328 1 + /* cj_ac_sa */
329 sizeof(float) + /* flatness */
330 sizeof(float) + /* line width */
331 sizeof(float) + /* miter limit */
332 1 + /* op_bm_tk */
333 sizeof(float) * 2 + /* opacity/shape alpha */
334 sizeof(cldev->imager_state.alpha)
335 ];
336 byte *bp = buf;
337
338 if (unknown & cap_join_known) {
339 *bp++ = (cldev->imager_state.line_params.cap << 3) +
340 cldev->imager_state.line_params.join;
341 }
342 if (unknown & cj_ac_sa_known) {
343 *bp++ =
344 ((cldev->imager_state.line_params.curve_join + 1) << 2) +
345 (cldev->imager_state.accurate_curves ? 2 : 0) +
346 (cldev->imager_state.stroke_adjust ? 1 : 0);
347 }
348 if (unknown & flatness_known) {
349 memcpy(bp, &cldev->imager_state.flatness, sizeof(float));
350 bp += sizeof(float);
351 }
352 if (unknown & line_width_known) {
353 float width =
354 gx_current_line_width(&cldev->imager_state.line_params);
355
356 memcpy(bp, &width, sizeof(width));
357 bp += sizeof(width);
358 }
359 if (unknown & miter_limit_known) {
360 memcpy(bp, &cldev->imager_state.line_params.miter_limit,
361 sizeof(float));
362 bp += sizeof(float);
363 }
364 if (unknown & op_bm_tk_known) {
365 *bp++ =
366 ((int)cldev->imager_state.blend_mode << 3) +
367 (cldev->imager_state.text_knockout << 2) +
368 (cldev->imager_state.overprint_mode << 1) +
369 cldev->imager_state.overprint;
370 }
371 if (unknown & opacity_alpha_known) {
372 memcpy(bp, &cldev->imager_state.opacity.alpha, sizeof(float));
373 bp += sizeof(float);
374 }
375 if (unknown & shape_alpha_known) {
376 memcpy(bp, &cldev->imager_state.shape.alpha, sizeof(float));
377 bp += sizeof(float);
378 }
379 if (unknown & alpha_known) {
380 memcpy(bp, &cldev->imager_state.alpha,
381 sizeof(cldev->imager_state.alpha));
382 bp += sizeof(cldev->imager_state.alpha);
383 }
384 code = set_cmd_put_op(dp, cldev, pcls, cmd_opv_set_misc2,
385 1 + cmd_sizew(misc2_unknown) + bp - buf);
386 if (code < 0)
387 return 0;
388 memcpy(cmd_put_w(misc2_unknown, dp + 1), buf, bp - buf);
389 pcls->known |= misc2_unknown;
390 }
391 if (unknown & fill_adjust_known) {
392 code = set_cmd_put_op(dp, cldev, pcls, cmd_opv_set_fill_adjust,
393 1 + sizeof(fixed) * 2);
394 if (code < 0)
395 return code;
396 memcpy(dp + 1, &cldev->imager_state.fill_adjust.x, sizeof(fixed));
397 memcpy(dp + 1 + sizeof(fixed), &cldev->imager_state.fill_adjust.y, sizeof(fixed));
398 pcls->known |= fill_adjust_known;
399 }
400 if (unknown & ctm_known) {
401 int len = cmd_write_ctm_return_length(cldev, &ctm_only(&cldev->imager_state));
402
403 code = set_cmd_put_op(dp, cldev, pcls, cmd_opv_set_ctm, len + 1);
404 if (code < 0)
405 return code;
406 code = cmd_write_ctm(&ctm_only(&cldev->imager_state), dp, len);
407 if (code < 0)
408 return code;
409 pcls->known |= ctm_known;
410 }
411 if (unknown & dash_known) {
412 int n = cldev->imager_state.line_params.dash.pattern_size;
413
414 code = set_cmd_put_op(dp, cldev, pcls, cmd_opv_set_dash,
415 2 + (n + 2) * sizeof(float));
416 if (code < 0)
417 return code;
418 dp[1] = n + (cldev->imager_state.line_params.dash.adapt ? 0x80 : 0) +
419 (cldev->imager_state.line_params.dot_length_absolute ? 0x40 : 0);
420 memcpy(dp + 2, &cldev->imager_state.line_params.dot_length,
421 sizeof(float));
422 memcpy(dp + 2 + sizeof(float),
423 &cldev->imager_state.line_params.dash.offset,
424 sizeof(float));
425 if (n != 0)
426 memcpy(dp + 2 + sizeof(float) * 2,
427 cldev->dash_pattern, n * sizeof(float));
428 pcls->known |= dash_known;
429 }
430 if (unknown & clip_path_known) {
431 /*
432 * We can write out the clipping path either as rectangles
433 * or as a real (filled) path.
434 */
435 const gx_clip_path *pcpath = cldev->clip_path;
436 int band_height = cldev->page_band_height;
437 int ymin = (pcls - cldev->states) * band_height;
438 int ymax = min(ymin + band_height, cldev->height);
439 gs_fixed_rect box;
440 bool punt_to_outer_box = false;
441 int code;
442
443 code = set_cmd_put_op(dp, cldev, pcls, cmd_opv_begin_clip, 1);
444 if (code < 0)
445 return code;
446 if (pcpath->path_valid) {
447 if (gx_path_is_rectangle(&pcpath->path, &box) &&
448 fixed_is_int(box.p.x | box.p.y | box.q.x | box.q.y)
449 ) {
450 /* Write the path as a rectangle. */
451 code = cmd_write_rect_cmd(cldev, pcls, cmd_op_fill_rect,
452 fixed2int_var(box.p.x),
453 fixed2int_var(box.p.y),
454 fixed2int(box.q.x - box.p.x),
455 fixed2int(box.q.y - box.p.y));
456 } else if ( !(cldev->disable_mask & clist_disable_complex_clip) ) {
457 /* Write the path. */
458 code = cmd_put_path(cldev, pcls, &pcpath->path,
459 int2fixed(ymin - 1),
460 int2fixed(ymax + 1),
461 (byte)(pcpath->rule == gx_rule_even_odd ?
462 cmd_opv_eofill : cmd_opv_fill),
463 true, sn_not_first);
464 } else {
465 /* Complex paths disabled: write outer box as clip */
466 punt_to_outer_box = true;
467 }
468 } else { /* Write out the rectangles. */
469 const gx_clip_list *list = gx_cpath_list(pcpath);
470 const gx_clip_rect *prect = list->head;
471
472 if (prect == 0)
473 prect = &list->single;
474 else if (cldev->disable_mask & clist_disable_complex_clip)
475 punt_to_outer_box = true;
476 if (!punt_to_outer_box) {
477 for (; prect != 0 && code >= 0; prect = prect->next)
478 if (prect->xmax > prect->xmin &&
479 prect->ymin < ymax && prect->ymax > ymin
480 ) {
481 code =
482 cmd_write_rect_cmd(cldev, pcls, cmd_op_fill_rect,
483 prect->xmin, prect->ymin,
484 prect->xmax - prect->xmin,
485 prect->ymax - prect->ymin);
486 }
487 }
488 }
489 if (punt_to_outer_box) {
490 /* Clip is complex, but disabled. Write out the outer box */
491 gs_fixed_rect box;
492
493 gx_cpath_outer_box(pcpath, &box);
494 box.p.x = fixed_floor(box.p.x);
495 box.p.y = fixed_floor(box.p.y);
496 code = cmd_write_rect_cmd(cldev, pcls, cmd_op_fill_rect,
497 fixed2int_var(box.p.x),
498 fixed2int_var(box.p.y),
499 fixed2int_ceiling(box.q.x - box.p.x),
500 fixed2int_ceiling(box.q.y - box.p.y));
501 }
502 {
503 int end_code =
504 set_cmd_put_op(dp, cldev, pcls, cmd_opv_end_clip, 1);
505
506 if (code >= 0)
507 code = end_code; /* take the first failure seen */
508 if (end_code < 0 && cldev->error_is_retryable) {
509 /*
510 * end_clip has to work despite lo-mem to maintain consistency.
511 * This isn't error recovery, but just to prevent dangling
512 * cmd_opv_begin_clip's.
513 */
514 ++cldev->ignore_lo_mem_warnings;
515 end_code =
516 set_cmd_put_op(dp, cldev, pcls, cmd_opv_end_clip, 1);
517 --cldev->ignore_lo_mem_warnings;
518 }
519 }
520 if (code < 0)
521 return code;
522 pcls->clip_enabled = 1;
523 pcls->known |= clip_path_known;
524 }
525 if (unknown & color_space_known) {
526 byte *dp;
527
528 if (cldev->color_space.byte1 & 8) { /* indexed */
529 const gs_color_space *pcs = cldev->color_space.space;
530 int hival = pcs->params.indexed.hival;
531 uint num_values = (hival + 1) *
532 gs_color_space_num_components(
533 (const gs_color_space *)&pcs->params.indexed.base_space);
534 bool use_proc = cldev->color_space.byte1 & 4;
535 const void *map_data;
536 uint map_size;
537
538 if (use_proc) {
539 map_data = pcs->params.indexed.lookup.map->values;
540 map_size = num_values *
541 sizeof(pcs->params.indexed.lookup.map->values[0]);
542 } else {
543 map_data = pcs->params.indexed.lookup.table.data;
544 map_size = num_values;
545 }
546 code = set_cmd_put_op(dp, cldev, pcls, cmd_opv_set_color_space,
547 2 + cmd_sizew(hival) + map_size);
548 if (code < 0)
549 return code;
550 memcpy(cmd_put_w(hival, dp + 2), map_data, map_size);
551 } else {
552 code = set_cmd_put_op(dp, cldev, pcls, cmd_opv_set_color_space, 2);
553 if (code < 0)
554 return code;
555 }
556 dp[1] = cldev->color_space.byte1;
557 pcls->known |= color_space_known;
558 }
559 /****** HANDLE masks ******/
560 return 0;
561 }
562
563 /* ------ Driver procedures ------ */
564
565 int
clist_fill_path(gx_device * dev,const gs_imager_state * pis,gx_path * ppath,const gx_fill_params * params,const gx_drawing_color * pdcolor,const gx_clip_path * pcpath)566 clist_fill_path(gx_device * dev, const gs_imager_state * pis, gx_path * ppath,
567 const gx_fill_params * params, const gx_drawing_color * pdcolor,
568 const gx_clip_path * pcpath)
569 {
570 gx_device_clist_writer * const cdev =
571 &((gx_device_clist *)dev)->writer;
572 uint unknown = 0;
573 int y, height, y0, y1;
574 gs_logical_operation_t lop = pis->log_op;
575 byte op = (byte)
576 (params->rule == gx_rule_even_odd ?
577 cmd_opv_eofill : cmd_opv_fill);
578 gs_fixed_point adjust;
579 bool slow_rop = cmd_slow_rop(dev, lop_know_S_0(lop), pdcolor);
580
581 if ( (cdev->disable_mask & clist_disable_fill_path) ||
582 gs_debug_c(',')
583 ) {
584 /* Disable path-based banding. */
585 return gx_default_fill_path(dev, pis, ppath, params, pdcolor,
586 pcpath);
587 }
588 adjust = params->adjust;
589 {
590 gs_fixed_rect bbox;
591
592 gx_path_bbox(ppath, &bbox);
593 y = fixed2int(bbox.p.y) - 1;
594 height = fixed2int_ceiling(bbox.q.y) - y + 1;
595 fit_fill_y(dev, y, height);
596 fit_fill_h(dev, y, height);
597 if (height <= 0)
598 return 0;
599 }
600 y0 = y;
601 y1 = y + height;
602 cmd_check_fill_known(cdev, pis, params->flatness, &adjust, pcpath,
603 &unknown);
604 if (unknown)
605 cmd_clear_known(cdev, unknown);
606 FOR_RECTS_NO_ERROR {
607 int code = cmd_do_write_unknown(cdev, pcls, FILL_KNOWN);
608
609 if (code < 0)
610 return code;
611 if ((code = cmd_do_enable_clip(cdev, pcls, pcpath != NULL)) < 0 ||
612 (code = cmd_update_lop(cdev, pcls, lop)) < 0
613 )
614 return code;
615 code = cmd_put_drawing_color(cdev, pcls, pdcolor);
616 if (code < 0) {
617 /* Something went wrong, use the default implementation. */
618 return gx_default_fill_path(dev, pis, ppath, params, pdcolor,
619 pcpath);
620 }
621 pcls->colors_used.slow_rop |= slow_rop;
622 code = cmd_put_path(cdev, pcls, ppath,
623 int2fixed(max(y - 1, y0)),
624 int2fixed(min(y + height + 1, y1)),
625 op,
626 true, sn_none /* fill doesn't need the notes */ );
627 if (code < 0)
628 return code;
629 } END_RECTS_NO_ERROR;
630 return 0;
631 }
632
633 int
clist_stroke_path(gx_device * dev,const gs_imager_state * pis,gx_path * ppath,const gx_stroke_params * params,const gx_drawing_color * pdcolor,const gx_clip_path * pcpath)634 clist_stroke_path(gx_device * dev, const gs_imager_state * pis, gx_path * ppath,
635 const gx_stroke_params * params,
636 const gx_drawing_color * pdcolor, const gx_clip_path * pcpath)
637 {
638 gx_device_clist_writer * const cdev =
639 &((gx_device_clist *)dev)->writer;
640 int pattern_size = pis->line_params.dash.pattern_size;
641 uint unknown = 0;
642 gs_fixed_rect bbox;
643 gs_fixed_point expansion;
644 int adjust_y, expansion_code;
645 int y, height;
646 gs_logical_operation_t lop = pis->log_op;
647 bool slow_rop = cmd_slow_rop(dev, lop_know_S_0(lop), pdcolor);
648
649 if ((cdev->disable_mask & clist_disable_stroke_path) ||
650 gs_debug_c(',')
651 ) {
652 /* Disable path-based banding. */
653 return gx_default_stroke_path(dev, pis, ppath, params, pdcolor,
654 pcpath);
655 }
656 gx_path_bbox(ppath, &bbox);
657 /* We must use the supplied imager state, not our saved one, */
658 /* for computing the stroke expansion. */
659 expansion_code = gx_stroke_path_expansion(pis, ppath, &expansion);
660 if (expansion_code < 0) {
661 /* Expansion is too large: use the entire page. */
662 adjust_y = 0;
663 y = 0;
664 height = dev->height;
665 } else {
666 adjust_y = fixed2int_ceiling(expansion.y) + 1;
667 y = fixed2int(bbox.p.y) - adjust_y;
668 height = fixed2int_ceiling(bbox.q.y) - y + adjust_y;
669 fit_fill_y(dev, y, height);
670 fit_fill_h(dev, y, height);
671 if (height <= 0)
672 return 0;
673 }
674 /* Check the dash pattern, since we bail out if */
675 /* the pattern is too large. */
676 if (cdev->imager_state.line_params.dash.pattern_size != pattern_size ||
677 (pattern_size != 0 &&
678 memcmp(cdev->dash_pattern, pis->line_params.dash.pattern,
679 pattern_size * sizeof(float))) ||
680 cdev->imager_state.line_params.dash.offset !=
681 pis->line_params.dash.offset ||
682 cdev->imager_state.line_params.dash.adapt !=
683 pis->line_params.dash.adapt ||
684 cdev->imager_state.line_params.dot_length !=
685 pis->line_params.dot_length ||
686 cdev->imager_state.line_params.dot_length_absolute !=
687 pis->line_params.dot_length_absolute
688 ) {
689 /* Bail out if the dash pattern is too long. */
690 if (pattern_size > cmd_max_dash)
691 return gx_default_stroke_path(dev, pis, ppath, params,
692 pdcolor, pcpath);
693 unknown |= dash_known;
694 /*
695 * Temporarily reset the dash pattern pointer for gx_set_dash,
696 * but don't leave it set, since that would confuse the GC.
697 */
698 cdev->imager_state.line_params.dash.pattern = cdev->dash_pattern;
699 gx_set_dash(&cdev->imager_state.line_params.dash,
700 pis->line_params.dash.pattern,
701 pis->line_params.dash.pattern_size,
702 pis->line_params.dash.offset, NULL);
703 cdev->imager_state.line_params.dash.pattern = 0;
704 gx_set_dash_adapt(&cdev->imager_state.line_params.dash,
705 pis->line_params.dash.adapt);
706 gx_set_dot_length(&cdev->imager_state.line_params,
707 pis->line_params.dot_length,
708 pis->line_params.dot_length_absolute);
709 }
710 if (state_neq(line_params.cap) || state_neq(line_params.join)) {
711 unknown |= cap_join_known;
712 state_update(line_params.cap);
713 state_update(line_params.join);
714 }
715 cmd_check_fill_known(cdev, pis, params->flatness, &pis->fill_adjust,
716 pcpath, &unknown);
717 if (state_neq(line_params.half_width)) {
718 unknown |= line_width_known;
719 state_update(line_params.half_width);
720 }
721 if (state_neq(line_params.miter_limit)) {
722 unknown |= miter_limit_known;
723 gx_set_miter_limit(&cdev->imager_state.line_params,
724 pis->line_params.miter_limit);
725 }
726 if (state_neq(ctm.xx) || state_neq(ctm.xy) ||
727 state_neq(ctm.yx) || state_neq(ctm.yy) ||
728 /* We don't actually need tx or ty, but we don't want to bother */
729 /* tracking them separately from the other coefficients. */
730 state_neq(ctm.tx) || state_neq(ctm.ty)
731 ) {
732 unknown |= ctm_known;
733 state_update(ctm);
734 }
735 if (unknown)
736 cmd_clear_known(cdev, unknown);
737 FOR_RECTS_NO_ERROR {
738 int code;
739
740 if ((code = cmd_do_write_unknown(cdev, pcls, stroke_all_known)) < 0 ||
741 (code = cmd_do_enable_clip(cdev, pcls, pcpath != NULL)) < 0 ||
742 (code = cmd_update_lop(cdev, pcls, lop)) < 0
743 )
744 return code;
745 code = cmd_put_drawing_color(cdev, pcls, pdcolor);
746 if (code < 0) {
747 /* Something went wrong, use the default implementation. */
748 return gx_default_stroke_path(dev, pis, ppath, params, pdcolor,
749 pcpath);
750 }
751 pcls->colors_used.slow_rop |= slow_rop;
752 {
753 fixed ymin, ymax;
754
755 /*
756 * If a dash pattern is active, we can't skip segments
757 * outside the clipping region, because that would throw off
758 * the pattern.
759 * Don't skip segments when expansion is unknown.
760 */
761
762 if (pattern_size || expansion_code < 0 ) {
763 ymin = min_fixed;
764 ymax = max_fixed;
765 } else {
766 ymin = int2fixed(y - adjust_y);
767 ymax = int2fixed(y + height + adjust_y);
768 }
769 code = cmd_put_path(cdev, pcls, ppath, ymin, ymax,
770 cmd_opv_stroke,
771 false, (segment_notes)~0);
772 if (code < 0)
773 return code;
774 }
775 } END_RECTS_NO_ERROR;
776 return 0;
777 }
778
779 /*
780 * Fill_parallelogram and fill_triangle aren't very efficient. This isn't
781 * important right now, since the non-degenerate case is only used for
782 * smooth shading. However, the rectangular case of fill_parallelogram is
783 * sometimes used for images, so its performance does matter.
784 */
785
786 private int
clist_put_polyfill(gx_device * dev,fixed px,fixed py,const gs_fixed_point * points,int num_points,const gx_drawing_color * pdcolor,gs_logical_operation_t lop)787 clist_put_polyfill(gx_device *dev, fixed px, fixed py,
788 const gs_fixed_point *points, int num_points,
789 const gx_drawing_color *pdcolor, gs_logical_operation_t lop)
790 {
791 gx_path path;
792 gs_memory_t *mem = dev->memory;
793 int code;
794 gx_device_clist_writer * const cdev =
795 &((gx_device_clist *)dev)->writer;
796 gs_fixed_rect bbox;
797 int y, height, y0, y1;
798 bool slow_rop = cmd_slow_rop(dev, lop_know_S_0(lop), pdcolor);
799
800 if (gs_debug_c(','))
801 return -1; /* path-based banding is disabled */
802 gx_path_init_local(&path, mem);
803 if ((code = gx_path_add_point(&path, px, py)) < 0 ||
804 (code = gx_path_add_lines(&path, points, num_points)) < 0
805 )
806 goto out;
807 gx_path_bbox(&path, &bbox);
808 y = fixed2int(bbox.p.y) - 1;
809 height = fixed2int_ceiling(bbox.q.y) - y + 1;
810 fit_fill_y(dev, y, height);
811 fit_fill_h(dev, y, height);
812 if (height <= 0)
813 return 0;
814 y0 = y;
815 y1 = y + height;
816 FOR_RECTS_NO_ERROR {
817 if ((code = cmd_update_lop(cdev, pcls, lop)) < 0 ||
818 (code = cmd_put_drawing_color(cdev, pcls, pdcolor)) < 0)
819 goto out;
820 pcls->colors_used.slow_rop |= slow_rop;
821 code = cmd_put_path(cdev, pcls, &path,
822 int2fixed(max(y - 1, y0)),
823 int2fixed(min(y + height + 1, y1)),
824 cmd_opv_polyfill,
825 true, sn_none /* fill doesn't need the notes */ );
826 if (code < 0)
827 goto out;
828 } END_RECTS_NO_ERROR;
829 out:
830 gx_path_free(&path, "clist_put_polyfill");
831 return code;
832 }
833
834 int
clist_fill_parallelogram(gx_device * dev,fixed px,fixed py,fixed ax,fixed ay,fixed bx,fixed by,const gx_drawing_color * pdcolor,gs_logical_operation_t lop)835 clist_fill_parallelogram(gx_device *dev, fixed px, fixed py,
836 fixed ax, fixed ay, fixed bx, fixed by,
837 const gx_drawing_color *pdcolor,
838 gs_logical_operation_t lop)
839 {
840 gs_fixed_point pts[3];
841 int code;
842
843 if (PARALLELOGRAM_IS_RECT(ax, ay, bx, by)) {
844 gs_int_rect r;
845
846 INT_RECT_FROM_PARALLELOGRAM(&r, px, py, ax, ay, bx, by);
847 return gx_fill_rectangle_device_rop(r.p.x, r.p.y, r.q.x - r.p.x,
848 r.q.y - r.p.y, pdcolor, dev, lop);
849 }
850 pts[0].x = px + ax, pts[0].y = py + ay;
851 pts[1].x = pts[0].x + bx, pts[1].y = pts[0].y + by;
852 pts[2].x = px + bx, pts[2].y = py + by;
853 code = clist_put_polyfill(dev, px, py, pts, 3, pdcolor, lop);
854 return (code >= 0 ? code :
855 gx_default_fill_parallelogram(dev, px, py, ax, ay, bx, by,
856 pdcolor, lop));
857 }
858
859 int
clist_fill_triangle(gx_device * dev,fixed px,fixed py,fixed ax,fixed ay,fixed bx,fixed by,const gx_drawing_color * pdcolor,gs_logical_operation_t lop)860 clist_fill_triangle(gx_device *dev, fixed px, fixed py,
861 fixed ax, fixed ay, fixed bx, fixed by,
862 const gx_drawing_color *pdcolor,
863 gs_logical_operation_t lop)
864 {
865 gs_fixed_point pts[2];
866 int code;
867
868 pts[0].x = px + ax, pts[0].y = py + ay;
869 pts[1].x = px + bx, pts[1].y = py + by;
870 code = clist_put_polyfill(dev, px, py, pts, 2, pdcolor, lop);
871 return (code >= 0 ? code :
872 gx_default_fill_triangle(dev, px, py, ax, ay, bx, by,
873 pdcolor, lop));
874 }
875
876 /* ------ Path utilities ------ */
877
878 /* Define the state bookkeeping for writing path segments. */
879 typedef struct cmd_segment_writer_s {
880 /* Set at initialization */
881 gx_device_clist_writer *cldev;
882 gx_clist_state *pcls;
883 /* Updated dynamically */
884 segment_notes notes;
885 byte *dp;
886 int len;
887 gs_fixed_point delta_first;
888 byte cmd[6 * (1 + sizeof(fixed))];
889 }
890 cmd_segment_writer;
891
892 /* Put out a path segment command. */
893 private int
cmd_put_segment(cmd_segment_writer * psw,byte op,const fixed * operands,segment_notes notes)894 cmd_put_segment(cmd_segment_writer * psw, byte op,
895 const fixed * operands, segment_notes notes)
896 {
897 const fixed *optr = operands;
898 /* Fetch num_operands before possible command merging. */
899 static const byte op_num_operands[] = {
900 cmd_segment_op_num_operands_values
901 };
902 int i = op_num_operands[op & 0xf];
903 /* One picky compiler complains if we initialize to psw->cmd - 1. */
904 byte *q = psw->cmd;
905
906 --q;
907
908 #ifdef DEBUG
909 if (gs_debug_c('L')) {
910 int j;
911
912 dlprintf2("[L] %s:%d:", cmd_sub_op_names[op >> 4][op & 0xf],
913 (int)notes);
914 for (j = 0; j < i; ++j)
915 dprintf1(" %g", fixed2float(operands[j]));
916 dputs("\n");
917 }
918 #endif
919
920 /* Merge or shorten commands if possible. */
921 if (op == cmd_opv_rlineto) {
922 if (operands[0] == 0)
923 op = cmd_opv_vlineto, optr = ++operands, i = 1;
924 else if (operands[1] == 0)
925 op = cmd_opv_hlineto, i = 1;
926 else
927 switch (*psw->dp) {
928 case cmd_opv_rmoveto:
929 psw->delta_first.x = operands[0];
930 psw->delta_first.y = operands[1];
931 op = cmd_opv_rmlineto;
932 merge:cmd_uncount_op(*psw->dp, psw->len);
933 cmd_shorten_op(psw->cldev, psw->pcls, psw->len); /* delete it */
934 q += psw->len - 1;
935 break;
936 case cmd_opv_rmlineto:
937 if (notes != psw->notes)
938 break;
939 op = cmd_opv_rm2lineto;
940 goto merge;
941 case cmd_opv_rm2lineto:
942 if (notes != psw->notes)
943 break;
944 if (operands[0] == -psw->delta_first.x &&
945 operands[1] == -psw->delta_first.y
946 ) {
947 cmd_uncount_op(cmd_opv_rm2lineto, psw->len);
948 *psw->dp = cmd_count_op(cmd_opv_rm3lineto, psw->len);
949 return 0;
950 }
951 break;
952 default:
953 ;
954 }
955 }
956 for (; --i >= 0; ++optr) {
957 fixed d = *optr, d2;
958
959 if (is_bits(d, _fixed_shift + 11) &&
960 !(d & (float2fixed(0.25) - 1))
961 ) {
962 cmd_count_add1(stats_cmd_diffs[3]);
963 d = ((d >> (_fixed_shift - 2)) & 0x1fff) + 0xc000;
964 q += 2;
965 } else if (is_bits(d, 19) && i > 0 && is_bits(d2 = optr[1], 19)) {
966 cmd_count_add1(stats_cmd_diffs[0]);
967 q[1] = (byte) ((d >> 13) & 0x3f);
968 q[2] = (byte) (d >> 5);
969 q[3] = (byte) ((d << 3) + ((d2 >> 16) & 7));
970 q[4] = (byte) (d2 >> 8);
971 q[5] = (byte) d2;
972 q += 5;
973 --i, ++optr;
974 continue;
975 } else if (is_bits(d, 22)) {
976 cmd_count_add1(stats_cmd_diffs[1]);
977 q[1] = (byte) (((d >> 16) & 0x3f) + 0x40);
978 q += 3;
979 } else if (is_bits(d, 30)) {
980 cmd_count_add1(stats_cmd_diffs[2]);
981 q[1] = (byte) (((d >> 24) & 0x3f) + 0x80);
982 q[2] = (byte) (d >> 16);
983 q += 4;
984 } else {
985 int b;
986
987 cmd_count_add1(stats_cmd_diffs[4]);
988 *++q = 0xe0;
989 for (b = sizeof(fixed) - 1; b > 1; --b)
990 *++q = (byte) (d >> (b * 8));
991 q += 2;
992 }
993 q[-1] = (byte) (d >> 8);
994 *q = (byte) d;
995 }
996 if (notes != psw->notes) {
997 byte *dp;
998 int code =
999 set_cmd_put_op(dp, psw->cldev, psw->pcls, cmd_opv_set_misc2, 3);
1000
1001 if (code < 0)
1002 return code;
1003 dp[1] = segment_notes_known;
1004 dp[2] = notes;
1005 psw->notes = notes;
1006 } {
1007 int len = q + 2 - psw->cmd;
1008 byte *dp;
1009 int code = set_cmd_put_op(dp, psw->cldev, psw->pcls, op, len);
1010
1011 if (code < 0)
1012 return code;
1013 memcpy(dp + 1, psw->cmd, len - 1);
1014 psw->len = len;
1015 psw->dp = dp;
1016 }
1017 return 0;
1018 }
1019 /* Put out a line segment command. */
1020 #define cmd_put_rmoveto(psw, operands)\
1021 cmd_put_segment(psw, cmd_opv_rmoveto, operands, sn_none)
1022 #define cmd_put_rlineto(psw, operands, notes)\
1023 cmd_put_segment(psw, cmd_opv_rlineto, operands, notes)
1024
1025 /*
1026 * Write a path. We go to a lot of trouble to omit segments that are
1027 * entirely outside the band.
1028 */
1029 private int
cmd_put_path(gx_device_clist_writer * cldev,gx_clist_state * pcls,const gx_path * ppath,fixed ymin,fixed ymax,byte path_op,bool implicit_close,segment_notes keep_notes)1030 cmd_put_path(gx_device_clist_writer * cldev, gx_clist_state * pcls,
1031 const gx_path * ppath, fixed ymin, fixed ymax, byte path_op,
1032 bool implicit_close, segment_notes keep_notes)
1033 {
1034 gs_path_enum cenum;
1035 cmd_segment_writer writer;
1036
1037 /*
1038 * initial_op is logically const. We would like to declare it as
1039 * static const, since some systems really dislike non-const statics,
1040 * but this would entail a cast in set_first_point() that provokes a
1041 * warning message from gcc. Instead, we pay the (tiny) cost of an
1042 * unnecessary dynamic initialization.
1043 */
1044 byte initial_op = cmd_opv_end_run;
1045
1046 /*
1047 * We define the 'side' of a point according to its Y value as
1048 * follows:
1049 */
1050 #define which_side(y) ((y) < ymin ? -1 : (y) >= ymax ? 1 : 0)
1051
1052 /*
1053 * While writing a subpath, we need to keep track of any segments
1054 * skipped at the beginning of the subpath and any segments skipped
1055 * just before the current segment. We do this with two sets of
1056 * state variables, one that tracks the actual path segments and one
1057 * that tracks the emitted segments.
1058 *
1059 * The following track the actual segments:
1060 */
1061
1062 /*
1063 * The point and side of the last moveto (skipped if
1064 * start_side != 0):
1065 */
1066 gs_fixed_point start;
1067 int start_side = 0x7badf00d; /* Initialize against indeterminizm. */
1068
1069 /*
1070 * Whether any lines or curves were skipped immediately
1071 * following the moveto:
1072 */
1073 bool start_skip = 0x7badf00d; /* Initialize against indeterminizm. */
1074
1075 /* The side of the last point: */
1076 int side = 0x7badf00d; /* Initialize against indeterminizm. */
1077
1078 /* The last point with side != 0: */
1079 gs_fixed_point out;
1080
1081 /* If the last out-going segment was a lineto, */
1082 /* its notes: */
1083 segment_notes out_notes = 0x7badf00d; /* Initialize against indeterminizm. */
1084
1085 /*
1086 * The following track the emitted segments:
1087 */
1088
1089 /* The last point emitted: */
1090 fixed px = int2fixed(pcls->rect.x);
1091 fixed py = int2fixed(pcls->rect.y);
1092
1093 /* The point of the last emitted moveto: */
1094 gs_fixed_point first;
1095
1096 /* Information about the last emitted operation: */
1097 int open = 0; /* -1 if last was moveto, 1 if line/curveto, */
1098
1099 /* 0 if newpath/closepath */
1100
1101
1102 if_debug4('p', "[p]initial (%g,%g), clip [%g..%g)\n",
1103 fixed2float(px), fixed2float(py),
1104 fixed2float(ymin), fixed2float(ymax));
1105 gx_path_enum_init(&cenum, ppath);
1106 writer.cldev = cldev;
1107 writer.pcls = pcls;
1108 writer.notes = sn_none;
1109 #define set_first_point() (writer.dp = &initial_op)
1110 #define first_point() (writer.dp == &initial_op)
1111 set_first_point();
1112 for (;;) {
1113 fixed vs[6];
1114 struct { fixed vs[6]; } prev;
1115
1116 #define A vs[0]
1117 #define B vs[1]
1118 #define C vs[2]
1119 #define D vs[3]
1120 #define E vs[4]
1121 #define F vs[5]
1122 int pe_op = gx_path_enum_next(&cenum, (gs_fixed_point *) vs);
1123 byte *dp;
1124 int code;
1125
1126 switch (pe_op) {
1127 case 0:
1128 /* If the path is open and needs an implicit close, */
1129 /* do the close and then come here again. */
1130 if (open > 0 && implicit_close)
1131 goto close;
1132 /* All done. */
1133 pcls->rect.x = fixed2int_var(px);
1134 pcls->rect.y = fixed2int_var(py);
1135 if_debug2('p', "[p]final (%d,%d)\n",
1136 pcls->rect.x, pcls->rect.y);
1137 return set_cmd_put_op(dp, cldev, pcls, path_op, 1);
1138 case gs_pe_moveto:
1139 /* If the path is open and needs an implicit close, */
1140 /* do a closepath and then redo the moveto. */
1141 if (open > 0 && implicit_close) {
1142 gx_path_enum_backup(&cenum);
1143 goto close;
1144 }
1145 open = -1;
1146 start.x = A, start.y = B;
1147 start_skip = false;
1148 if ((start_side = side = which_side(B)) != 0) {
1149 out.x = A, out.y = B;
1150 if_debug3('p', "[p]skip moveto (%g,%g) side %d\n",
1151 fixed2float(out.x), fixed2float(out.y),
1152 side);
1153 continue;
1154 }
1155 C = A - px, D = B - py;
1156 first.x = px = A, first.y = py = B;
1157 code = cmd_put_rmoveto(&writer, &C);
1158 if_debug2('p', "[p]moveto (%g,%g)\n",
1159 fixed2float(px), fixed2float(py));
1160 break;
1161 case gs_pe_lineto:
1162 {
1163 int next_side = which_side(B);
1164 segment_notes notes =
1165 gx_path_enum_notes(&cenum) & keep_notes;
1166
1167 if (next_side == side && side != 0) { /* Skip a line completely outside the clip region. */
1168 if (open < 0)
1169 start_skip = true;
1170 out.x = A, out.y = B;
1171 out_notes = notes;
1172 if_debug3('p', "[p]skip lineto (%g,%g) side %d\n",
1173 fixed2float(out.x), fixed2float(out.y),
1174 side);
1175 continue;
1176 }
1177 /* If we skipped any segments, put out a moveto/lineto. */
1178 if (side && (px != out.x || py != out.y || first_point())) {
1179 C = out.x - px, D = out.y - py;
1180 if (open < 0) {
1181 first = out;
1182 code = cmd_put_rmoveto(&writer, &C);
1183 } else
1184 code = cmd_put_rlineto(&writer, &C, out_notes);
1185 if (code < 0)
1186 return code;
1187 px = out.x, py = out.y;
1188 if_debug3('p', "[p]catchup %s (%g,%g) for line\n",
1189 (open < 0 ? "moveto" : "lineto"),
1190 fixed2float(px), fixed2float(py));
1191 }
1192 if ((side = next_side) != 0) { /* Note a vertex going outside the clip region. */
1193 out.x = A, out.y = B;
1194 }
1195 C = A - px, D = B - py;
1196 px = A, py = B;
1197 open = 1;
1198 code = cmd_put_rlineto(&writer, &C, notes);
1199 }
1200 if_debug3('p', "[p]lineto (%g,%g) side %d\n",
1201 fixed2float(px), fixed2float(py), side);
1202 break;
1203 case gs_pe_closepath:
1204 #ifdef DEBUG
1205 {
1206 gs_path_enum cpenum;
1207 gs_fixed_point cvs[3];
1208 int op;
1209
1210 cpenum = cenum;
1211 switch (op = gx_path_enum_next(&cpenum, cvs)) {
1212 case 0:
1213 case gs_pe_moveto:
1214 break;
1215 default:
1216 lprintf1("closepath followed by %d, not end/moveto!\n",
1217 op);
1218 }
1219 }
1220 #endif
1221 /* A closepath may require drawing an explicit line if */
1222 /* we skipped any segments at the beginning of the path. */
1223 close:if (side != start_side) { /* If we skipped any segments, put out a moveto/lineto. */
1224 if (side && (px != out.x || py != out.y || first_point())) {
1225 C = out.x - px, D = out.y - py;
1226 code = cmd_put_rlineto(&writer, &C, out_notes);
1227 if (code < 0)
1228 return code;
1229 px = out.x, py = out.y;
1230 if_debug2('p', "[p]catchup line (%g,%g) for close\n",
1231 fixed2float(px), fixed2float(py));
1232 }
1233 if (open > 0 && start_skip) { /* Draw the closing line back to the start. */
1234 C = start.x - px, D = start.y - py;
1235 code = cmd_put_rlineto(&writer, &C, sn_none);
1236 if (code < 0)
1237 return code;
1238 px = start.x, py = start.y;
1239 if_debug2('p', "[p]draw close to (%g,%g)\n",
1240 fixed2float(px), fixed2float(py));
1241 }
1242 }
1243 /*
1244 * We don't bother to update side because we know that the
1245 * next element after a closepath, if any, must be a moveto.
1246 * We must handle explicitly the possibility that the entire
1247 * subpath was skipped.
1248 */
1249 if (implicit_close || open <= 0) {
1250 /*
1251 * Force writing an explicit moveto if the next subpath
1252 * starts with a moveto to the same point where this one
1253 * ends.
1254 */
1255 set_first_point();
1256 if (side != 0) {
1257 open = 0;
1258 continue;
1259 }
1260 }
1261 open = 0;
1262 px = first.x, py = first.y;
1263 code = cmd_put_segment(&writer, cmd_opv_closepath, &A, sn_none);
1264 if_debug0('p', "[p]close\n");
1265 break;
1266 case gs_pe_curveto:
1267 {
1268 segment_notes notes =
1269 gx_path_enum_notes(&cenum) & keep_notes;
1270
1271 {
1272 fixed bpy, bqy;
1273 int all_side, out_side;
1274
1275 /* Compute the Y bounds for the clipping check. */
1276 if (B < D)
1277 bpy = B, bqy = D;
1278 else
1279 bpy = D, bqy = B;
1280 if (F < bpy)
1281 bpy = F;
1282 else if (F > bqy)
1283 bqy = F;
1284 all_side = (bqy < ymin ? -1 : bpy > ymax ? 1 : 0);
1285 if (all_side != 0) {
1286 if (all_side == side) { /* Skip a curve entirely outside the clip region. */
1287 if (open < 0)
1288 start_skip = true;
1289 out.x = E, out.y = F;
1290 out_notes = notes;
1291 if_debug3('p', "[p]skip curveto (%g,%g) side %d\n",
1292 fixed2float(out.x), fixed2float(out.y),
1293 side);
1294 continue;
1295 }
1296 out_side = all_side;
1297 } else
1298 out_side = which_side(F);
1299 /* If we skipped any segments, put out a moveto/lineto. */
1300 if (side && (px != out.x || py != out.y || first_point())) {
1301 fixed diff[2];
1302
1303 diff[0] = out.x - px, diff[1] = out.y - py;
1304 if (open < 0) {
1305 first = out;
1306 code = cmd_put_rmoveto(&writer, diff);
1307 } else
1308 code = cmd_put_rlineto(&writer, diff, out_notes);
1309 if (code < 0)
1310 return code;
1311 px = out.x, py = out.y;
1312 if_debug3('p', "[p]catchup %s (%g,%g) for curve\n",
1313 (open < 0 ? "moveto" : "lineto"),
1314 fixed2float(px), fixed2float(py));
1315 }
1316 if ((side = out_side) != 0) { /* Note a vertex going outside the clip region. */
1317 out.x = E, out.y = F;
1318 }
1319 }
1320 {
1321 fixed nx = E, ny = F;
1322 const fixed *optr = vs;
1323 byte op;
1324
1325 if_debug7('p', "[p]curveto (%g,%g; %g,%g; %g,%g) side %d\n",
1326 fixed2float(A), fixed2float(B),
1327 fixed2float(C), fixed2float(D),
1328 fixed2float(E), fixed2float(F), side);
1329 E -= C, F -= D;
1330 C -= A, D -= B;
1331 A -= px, B -= py;
1332 if (*writer.dp >= cmd_opv_min_curveto &&
1333 *writer.dp <= cmd_opv_max_curveto &&
1334 ((prev.A == 0 &&
1335 A == prev.E && C == prev.C && E == prev.A &&
1336 B == -prev.F && D == -prev.D && F == -prev.B) ||
1337 (prev.A != 0 &&
1338 A == -prev.E && C == -prev.C && E == -prev.A &&
1339 B == prev.F && D == prev.D && F == prev.B))
1340 )
1341 op = cmd_opv_scurveto;
1342 else if (B == 0 && E == 0) {
1343 B = A, E = F, optr++, op = cmd_opv_hvcurveto;
1344 if ((B ^ D) >= 0) {
1345 if (C == D && E == B)
1346 op = cmd_opv_hqcurveto;
1347 } else if (C == -D && E == -B)
1348 C = D, op = cmd_opv_hqcurveto;
1349 } else if (A == 0 && F == 0) {
1350 optr++, op = cmd_opv_vhcurveto;
1351 if ((B ^ C) >= 0) {
1352 if (D == C && E == B)
1353 op = cmd_opv_vqcurveto;
1354 } else if (D == -C && E == -B)
1355 op = cmd_opv_vqcurveto;
1356 } else if (A == 0 && B == 0)
1357 optr += 2, op = cmd_opv_nrcurveto;
1358 else if (E == 0 && F == 0)
1359 op = cmd_opv_rncurveto;
1360 else
1361 op = cmd_opv_rrcurveto;
1362 memcpy(prev.vs, vs, sizeof(prev.vs));
1363 px = nx, py = ny;
1364 open = 1;
1365 code = cmd_put_segment(&writer, op, optr, notes);
1366 }
1367 }
1368 break;
1369 default:
1370 return_error(gs_error_rangecheck);
1371 }
1372 if (code < 0)
1373 return code;
1374 #undef A
1375 #undef B
1376 #undef C
1377 #undef D
1378 #undef E
1379 #undef F
1380 }
1381 }
1382