1 /* Copyright (C) 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
3 This file is part of GNU Ghostscript.
5 GNU Ghostscript is distributed in the hope that it will be useful, but
6 WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
7 to anyone for the consequences of using it or for whether it serves any
8 particular purpose or works at all, unless he says so in writing. Refer
9 to the GNU General Public License for full details.
11 Everyone is granted permission to copy, modify and redistribute GNU
12 Ghostscript, but only under the conditions described in the GNU General
13 Public License. A copy of this license is supposed to have been given
14 to you along with GNU Ghostscript so you can know your rights and
15 responsibilities. It should be in a file named COPYING. Among other
16 things, the copyright notice and this notice must be preserved on all
19 Aladdin Enterprises supports the work of the GNU Project, but is not
20 affiliated with the Free Software Foundation or the GNU Project. GNU
21 Ghostscript, as distributed by Aladdin Enterprises, does not require any
22 GNU software to build or run it.
26 /* Higher-level path operations for band lists */
33 #include "gxdevmem.h" /* must precede gxcldev.h */
37 #include "gxpaint.h" /* for gx_fill/stroke_params */
43 ulong stats_cmd_diffs
[5];
46 /* Forward declarations */
47 private int cmd_put_path(P8(gx_device_clist_writer
* cldev
,
48 gx_clist_state
* pcls
, const gx_path
* ppath
, fixed ymin
, fixed ymax
, byte op
,
49 bool implicit_close
, segment_notes keep_notes
));
51 /* ------ Utilities ------ */
53 /* Write out the color for filling, stroking, or masking. */
54 /* We should be able to share this with clist_tile_rectangle, */
55 /* but I don't see how to do it without adding a level of procedure. */
57 cmd_put_drawing_color(gx_device_clist_writer
* cldev
, gx_clist_state
* pcls
,
58 const gx_drawing_color
* pdcolor
)
60 const gx_strip_bitmap
*tile
;
61 gx_color_index color0
, color1
;
64 if (gx_dc_is_pure(pdcolor
)) {
65 gx_color_index color1
= gx_dc_pure_color(pdcolor
);
67 if (color1
!= pcls
->colors
[1]) {
68 int code
= cmd_set_color1(cldev
, pcls
, color1
);
73 return cmd_dc_type_pure
;
75 /* Any non-pure color will require the phase. */
77 int px
= pdcolor
->phase
.x
, py
= pdcolor
->phase
.y
;
79 if (px
!= pcls
->tile_phase
.x
|| py
!= pcls
->tile_phase
.y
) {
80 int code
= cmd_set_tile_phase(cldev
, pcls
, px
, py
);
86 if (gx_dc_is_binary_halftone(pdcolor
)) {
87 tile
= gx_dc_binary_tile(pdcolor
);
88 color0
= gx_dc_binary_color0(pdcolor
);
89 color1
= gx_dc_binary_color1(pdcolor
);
90 /* Set up tile and colors as for clist_tile_rectangle. */
91 if (!cls_has_tile_id(cldev
, pcls
, tile
->id
, offset_temp
)) {
93 (color1
== gx_no_color_index
&&
94 color0
== gx_no_color_index
?
95 cldev
->color_info
.depth
: 1);
97 if (tile
->id
== gx_no_bitmap_id
||
98 clist_change_tile(cldev
, pcls
, tile
, depth
) < 0
100 return_error(-1); /* can't cache tile */
102 if (color1
!= pcls
->tile_colors
[1] ||
103 color0
!= pcls
->tile_colors
[0]
105 int code
= cmd_set_tile_colors(cldev
, pcls
, color0
, color1
);
110 return cmd_dc_type_ht
;
111 } else if (gx_dc_is_colored_halftone(pdcolor
)) {
112 const gx_device_halftone
*pdht
= pdcolor
->colors
.colored
.c_ht
;
113 int num_comp
= pdht
->num_comp
;
114 byte buf
[4 + 4 * cmd_max_intsize(sizeof(pdcolor
->colors
.colored
.c_level
[0]))];
117 uint short_bases
= 0;
122 /****** HOW TO TELL IF COLOR IS ALREADY SET? ******/
123 if (pdht
->id
!= cldev
->device_halftone_id
) {
124 int code
= cmd_put_halftone(cldev
, pdht
, pdht
->type
);
128 cldev
->device_halftone_id
= pdht
->id
;
130 for (i
= 0; i
< num_comp
; ++i
) {
131 uint base
= pdcolor
->colors
.colored
.c_base
[i
];
134 return_error(gs_error_rangecheck
);
135 bases
|= base
<< ((3 - i
) * 5);
136 short_bases
|= base
<< (3 - i
);
138 if (bases
& 0xf7bde) { /* Some base value requires more than 1 bit. */
139 *bp
++ = 0x10 + (byte
) (bases
>> 16);
140 *bp
++ = (byte
) (bases
>> 8);
141 *bp
++ = (byte
) bases
;
142 } else { /* The bases all fit in 1 bit each. */
143 *bp
++ = 0x00 + (byte
) short_bases
;
145 for (i
= 0; i
< num_comp
; ++i
)
146 bp
= cmd_put_w((uint
) pdcolor
->colors
.colored
.c_level
[i
], bp
);
147 /****** IGNORE alpha ******/
148 code
= set_cmd_put_op(dp
, cldev
, pcls
, cmd_opv_set_color
, bp
- buf
+ 1);
151 memcpy(dp
+ 1, buf
, bp
- buf
);
152 return cmd_dc_type_color
;
157 /* Clear (a) specific 'known' flag(s) for all bands. */
158 /* We must do this whenever the value of a 'known' parameter changes. */
160 cmd_clear_known(gx_device_clist_writer
* cldev
, uint known
)
162 ushort unknown
= ~known
;
163 gx_clist_state
*pcls
= cldev
->states
;
166 for (i
= cldev
->nbands
- 1; i
>= 0; i
--, pcls
++)
167 pcls
->known
&= unknown
;
170 /* Check whether we need to change the clipping path in the device. */
172 cmd_check_clip_path(gx_device_clist_writer
* cldev
, const gx_clip_path
* pcpath
)
176 /* The clip path might have moved in memory, so even if the */
177 /* ids match, update the pointer. */
178 cldev
->clip_path
= pcpath
;
179 if (pcpath
->id
== cldev
->clip_path_id
)
181 cldev
->clip_path_id
= pcpath
->id
;
185 /* Construct the parameters for writing out a matrix. */
186 /* We need a buffer of at least 1 + 6 * sizeof(float) bytes. */
188 cmd_for_matrix(byte
* cbuf
, const gs_matrix
* pmat
)
195 coeffs
[0] = pmat
->xx
;
196 coeffs
[1] = pmat
->xy
;
197 coeffs
[2] = pmat
->yx
;
198 coeffs
[3] = pmat
->yy
;
199 coeffs
[4] = pmat
->tx
;
200 coeffs
[5] = pmat
->ty
;
201 for (i
= 0; i
< 4; i
+= 2) {
202 float u
= coeffs
[i
], v
= coeffs
[i
^ 3];
205 if (u
!= 0 || v
!= 0) {
206 memcpy(cp
, &u
, sizeof(float));
215 memcpy(cp
, &v
, sizeof(float));
226 memcpy(cp
, &v
, sizeof(float));
234 /* Write out values of any unknown parameters. */
236 cmd_write_unknown(gx_device_clist_writer
* cldev
, gx_clist_state
* pcls
,
239 ushort unknown
= ~pcls
->known
& must_know
;
243 if (unknown
& flatness_known
) {
244 code
= set_cmd_put_op(dp
, cldev
, pcls
, cmd_opv_set_flatness
,
248 memcpy(dp
+ 1, &cldev
->imager_state
.flatness
, sizeof(float));
249 pcls
->known
|= flatness_known
;
251 if (unknown
& fill_adjust_known
) {
252 code
= set_cmd_put_op(dp
, cldev
, pcls
, cmd_opv_set_fill_adjust
,
253 1 + sizeof(fixed
) * 2);
256 memcpy(dp
+ 1, &cldev
->imager_state
.fill_adjust
.x
, sizeof(fixed
));
257 memcpy(dp
+ 1 + sizeof(fixed
), &cldev
->imager_state
.fill_adjust
.y
, sizeof(fixed
));
258 pcls
->known
|= fill_adjust_known
;
260 if (unknown
& ctm_known
) {
261 byte cbuf
[1 + 6 * sizeof(float)];
264 (const gs_matrix
*)&cldev
->imager_state
.ctm
) - cbuf
;
266 code
= set_cmd_put_op(dp
, cldev
, pcls
, cmd_opv_set_ctm
, len
+ 1);
269 memcpy(dp
+ 1, cbuf
, len
);
270 pcls
->known
|= ctm_known
;
272 if (unknown
& line_width_known
) {
274 gx_current_line_width(&cldev
->imager_state
.line_params
);
276 code
= set_cmd_put_op(dp
, cldev
, pcls
, cmd_opv_set_line_width
,
280 memcpy(dp
+ 1, &width
, sizeof(width
));
281 pcls
->known
|= line_width_known
;
283 if (unknown
& miter_limit_known
) {
284 code
= set_cmd_put_op(dp
, cldev
, pcls
, cmd_opv_set_miter_limit
,
288 memcpy(dp
+ 1, &cldev
->imager_state
.line_params
.miter_limit
,
290 pcls
->known
|= miter_limit_known
;
292 if (unknown
& misc0_known
) {
293 code
= set_cmd_put_op(dp
, cldev
, pcls
, cmd_opv_set_misc2
, 2);
296 dp
[1] = cmd_set_misc2_cap_join
+
297 (cldev
->imager_state
.line_params
.cap
<< 3) +
298 cldev
->imager_state
.line_params
.join
;
299 pcls
->known
|= misc0_known
;
301 if (unknown
& misc1_known
) {
302 code
= set_cmd_put_op(dp
, cldev
, pcls
, cmd_opv_set_misc2
, 2);
305 dp
[1] = cmd_set_misc2_ac_op_sa
+
306 (cldev
->imager_state
.accurate_curves
? 4 : 0) +
307 (cldev
->imager_state
.overprint
? 2 : 0) +
308 (cldev
->imager_state
.stroke_adjust
? 1 : 0);
309 pcls
->known
|= misc1_known
;
311 if (unknown
& dash_known
) {
312 int n
= cldev
->imager_state
.line_params
.dash
.pattern_size
;
314 code
= set_cmd_put_op(dp
, cldev
, pcls
, cmd_opv_set_dash
,
315 2 + (n
+ 2) * sizeof(float));
318 dp
[1] = n
+ (cldev
->imager_state
.line_params
.dash
.adapt
? 0x80 : 0) +
319 (cldev
->imager_state
.line_params
.dot_length_absolute
? 0x40 : 0);
320 memcpy(dp
+ 2, &cldev
->imager_state
.line_params
.dot_length
,
322 memcpy(dp
+ 2 + sizeof(float),
323 &cldev
->imager_state
.line_params
.dash
.offset
,
326 memcpy(dp
+ 2 + sizeof(float) * 2,
327 cldev
->imager_state
.line_params
.dash
.pattern
,
329 pcls
->known
|= dash_known
;
331 if (unknown
& alpha_known
) {
332 code
= set_cmd_put_op(dp
, cldev
, pcls
, cmd_opv_set_misc2
,
333 2 + sizeof(cldev
->imager_state
.alpha
));
336 dp
[1] = cmd_set_misc2_alpha
;
337 memcpy(dp
+ 2, &cldev
->imager_state
.alpha
,
338 sizeof(cldev
->imager_state
.alpha
));
339 pcls
->known
|= alpha_known
;
341 if (unknown
& clip_path_known
) {
343 * We can write out the clipping path either as rectangles
344 * or as a real (filled) path.
346 const gx_clip_path
*pcpath
= cldev
->clip_path
;
347 int band_height
= cldev
->page_band_height
;
348 int ymin
= (pcls
- cldev
->states
) * band_height
;
349 int ymax
= min(ymin
+ band_height
, cldev
->height
);
351 bool punt_to_outer_box
= false;
354 code
= set_cmd_put_op(dp
, cldev
, pcls
, cmd_opv_begin_clip
, 1);
357 if (pcpath
->path_valid
) {
358 if (gx_path_is_rectangle(&pcpath
->path
, &box
) &&
359 fixed_is_int(box
.p
.x
| box
.p
.y
| box
.q
.x
| box
.q
.y
)
361 /* Write the path as a rectangle. */
362 code
= cmd_write_rect_cmd(cldev
, pcls
, cmd_op_fill_rect
,
363 fixed2int_var(box
.p
.x
),
364 fixed2int_var(box
.p
.y
),
365 fixed2int(box
.q
.x
- box
.p
.x
),
366 fixed2int(box
.q
.y
- box
.p
.y
));
367 } else if ( !(cldev
->disable_mask
& clist_disable_complex_clip
) ) {
368 /* Write the path. */
369 code
= cmd_put_path(cldev
, pcls
, &pcpath
->path
,
372 (pcpath
->rule
== gx_rule_even_odd
?
373 cmd_opv_eofill
: cmd_opv_fill
),
376 /* Complex paths disabled: write outer box as clip */
377 punt_to_outer_box
= true;
379 } else { /* Write out the rectangles. */
380 const gx_clip_list
*list
= gx_cpath_list(pcpath
);
381 const gx_clip_rect
*prect
= list
->head
;
384 prect
= &list
->single
;
385 else if (cldev
->disable_mask
& clist_disable_complex_clip
)
386 punt_to_outer_box
= true;
387 if (!punt_to_outer_box
) {
388 for (; prect
!= 0 && code
>= 0; prect
= prect
->next
)
389 if (prect
->xmax
> prect
->xmin
&&
390 prect
->ymin
< ymax
&& prect
->ymax
> ymin
393 cmd_write_rect_cmd(cldev
, pcls
, cmd_op_fill_rect
,
394 prect
->xmin
, prect
->ymin
,
395 prect
->xmax
- prect
->xmin
,
396 prect
->ymax
- prect
->ymin
);
400 if (punt_to_outer_box
) {
401 /* Clip is complex, but disabled. Write out the outer box */
404 gx_cpath_outer_box(pcpath
, &box
);
405 box
.p
.x
= fixed_floor(box
.p
.x
);
406 box
.p
.y
= fixed_floor(box
.p
.y
);
407 code
= cmd_write_rect_cmd(cldev
, pcls
, cmd_op_fill_rect
,
408 fixed2int_var(box
.p
.x
),
409 fixed2int_var(box
.p
.y
),
410 fixed2int_ceiling(box
.q
.x
- box
.p
.x
),
411 fixed2int_ceiling(box
.q
.y
- box
.p
.y
));
415 set_cmd_put_op(dp
, cldev
, pcls
, cmd_opv_end_clip
, 2);
418 code
= end_code
; /* take the first failure seen */
419 if (end_code
< 0 && cldev
->error_is_retryable
) {
421 * end_clip has to work despite lo-mem to maintain consistency.
422 * This isn't error recovery, but just to prevent dangling
423 * cmd_opv_begin_clip's.
425 ++cldev
->ignore_lo_mem_warnings
;
427 set_cmd_put_op(dp
, cldev
, pcls
, cmd_opv_end_clip
, 2);
428 --cldev
->ignore_lo_mem_warnings
;
431 dp
[1] = (gx_cpath_is_outside(pcpath
) ? 1 : 0);
435 pcls
->clip_enabled
= 1;
436 pcls
->known
|= clip_path_known
;
438 if (unknown
& color_space_known
) {
441 if (cldev
->color_space
& 8) { /* indexed */
442 uint num_values
= (cldev
->indexed_params
.hival
+ 1) *
443 gs_color_space_num_components(
444 (const gs_color_space
*)&cldev
->indexed_params
.base_space
);
445 bool use_proc
= cldev
->color_space
& 4;
446 const void *map_data
;
450 map_data
= cldev
->indexed_params
.lookup
.map
->values
;
451 map_size
= num_values
*
452 sizeof(cldev
->indexed_params
.lookup
.map
->values
[0]);
454 map_data
= cldev
->indexed_params
.lookup
.table
.data
;
455 map_size
= num_values
;
457 code
= set_cmd_put_op(dp
, cldev
, pcls
, cmd_opv_set_color_space
,
458 2 + cmd_sizew(cldev
->indexed_params
.hival
) + map_size
);
461 memcpy(cmd_put_w(cldev
->indexed_params
.hival
, dp
+ 2),
464 code
= set_cmd_put_op(dp
, cldev
, pcls
, cmd_opv_set_color_space
, 2);
468 dp
[1] = cldev
->color_space
;
469 pcls
->known
|= color_space_known
;
474 /* ------ Driver procedures ------ */
477 clist_fill_path(gx_device
* dev
, const gs_imager_state
* pis
, gx_path
* ppath
,
478 const gx_fill_params
* params
, const gx_drawing_color
* pdcolor
,
479 const gx_clip_path
* pcpath
)
481 gx_device_clist_writer
* const cdev
=
482 &((gx_device_clist
*)dev
)->writer
;
484 int y
, height
, y0
, y1
;
485 gs_logical_operation_t lop
= pis
->log_op
;
487 (params
->rule
== gx_rule_even_odd
?
488 cmd_opv_eofill
: cmd_opv_fill
);
489 gs_fixed_point adjust
;
491 if ( (cdev
->disable_mask
& clist_disable_fill_path
) ||
494 /* Disable path-based banding. */
495 return gx_default_fill_path(dev
, pis
, ppath
, params
, pdcolor
,
498 adjust
= params
->adjust
;
502 gx_path_bbox(ppath
, &bbox
);
503 y
= fixed2int(bbox
.p
.y
) - 1;
504 height
= fixed2int_ceiling(bbox
.q
.y
) - y
+ 1;
505 fit_fill_y(dev
, y
, height
);
506 fit_fill_h(dev
, y
, height
);
512 if (cdev
->imager_state
.flatness
!= params
->flatness
) {
513 unknown
|= flatness_known
;
514 cdev
->imager_state
.flatness
= params
->flatness
;
516 if (cdev
->imager_state
.fill_adjust
.x
!= adjust
.x
||
517 cdev
->imager_state
.fill_adjust
.y
!= adjust
.y
519 unknown
|= fill_adjust_known
;
520 cdev
->imager_state
.fill_adjust
= adjust
;
522 if (cdev
->imager_state
.alpha
!= pis
->alpha
) {
523 unknown
|= alpha_known
;
526 if (cmd_check_clip_path(cdev
, pcpath
))
527 unknown
|= clip_path_known
;
529 cmd_clear_known(cdev
, unknown
);
532 cmd_do_write_unknown(cdev
, pcls
,
533 flatness_known
| fill_adjust_known
|
534 alpha_known
| clip_path_known
);
538 if ((code
= cmd_do_enable_clip(cdev
, pcls
, pcpath
!= NULL
)) < 0 ||
539 (code
= cmd_update_lop(cdev
, pcls
, lop
)) < 0
542 code
= cmd_put_drawing_color(cdev
, pcls
, pdcolor
);
543 if (code
< 0) { /* Something went wrong, use the default implementation. */
544 return gx_default_fill_path(dev
, pis
, ppath
, params
, pdcolor
,
547 code
= cmd_put_path(cdev
, pcls
, ppath
,
548 int2fixed(max(y
- 1, y0
)),
549 int2fixed(min(y
+ height
+ 1, y1
)),
550 op
+ code
, /* cmd_dc_type */
551 true, sn_none
/* fill doesn't need the notes */ );
559 clist_stroke_path(gx_device
* dev
, const gs_imager_state
* pis
, gx_path
* ppath
,
560 const gx_stroke_params
* params
,
561 const gx_drawing_color
* pdcolor
, const gx_clip_path
* pcpath
)
563 gx_device_clist_writer
* const cdev
=
564 &((gx_device_clist
*)dev
)->writer
;
565 int pattern_size
= pis
->line_params
.dash
.pattern_size
;
568 gs_fixed_point expansion
;
570 int y
, height
, y0
, y1
;
571 gs_logical_operation_t lop
= pis
->log_op
;
573 if ((cdev
->disable_mask
& clist_disable_stroke_path
) ||
576 /* Disable path-based banding. */
577 return gx_default_stroke_path(dev
, pis
, ppath
, params
, pdcolor
,
580 gx_path_bbox(ppath
, &bbox
);
581 /* We must use the supplied imager state, not our saved one, */
582 /* for computing the stroke expansion. */
583 if (gx_stroke_path_expansion(pis
, ppath
, &expansion
) < 0) {
584 /* Expansion is too large: use the entire page. */
587 height
= dev
->height
;
589 adjust_y
= fixed2int_ceiling(expansion
.y
) + 1;
590 y
= fixed2int(bbox
.p
.y
) - adjust_y
;
591 height
= fixed2int_ceiling(bbox
.q
.y
) - y
+ adjust_y
;
592 fit_fill_y(dev
, y
, height
);
593 fit_fill_h(dev
, y
, height
);
599 /* Check the dash pattern, since we bail out if */
600 /* the pattern is too large. */
601 cdev
->imager_state
.line_params
.dash
.pattern
= cdev
->dash_pattern
;
602 if (cdev
->imager_state
.line_params
.dash
.pattern_size
!= pattern_size
||
603 (pattern_size
!= 0 &&
604 memcmp(cdev
->imager_state
.line_params
.dash
.pattern
,
605 pis
->line_params
.dash
.pattern
,
606 pattern_size
* sizeof(float))) ||
607 cdev
->imager_state
.line_params
.dash
.offset
!=
608 pis
->line_params
.dash
.offset
||
609 cdev
->imager_state
.line_params
.dash
.adapt
!=
610 pis
->line_params
.dash
.adapt
||
611 cdev
->imager_state
.line_params
.dot_length
!=
612 pis
->line_params
.dot_length
||
613 cdev
->imager_state
.line_params
.dot_length_absolute
!=
614 pis
->line_params
.dot_length_absolute
615 ) { /* Bail out if the dash pattern is too long. */
616 if (pattern_size
> cmd_max_dash
)
617 return gx_default_stroke_path(dev
, pis
, ppath
, params
,
619 unknown
|= dash_known
;
620 gx_set_dash(&cdev
->imager_state
.line_params
.dash
,
621 pis
->line_params
.dash
.pattern
,
622 pis
->line_params
.dash
.pattern_size
,
623 pis
->line_params
.dash
.offset
, NULL
);
624 gx_set_dash_adapt(&cdev
->imager_state
.line_params
.dash
,
625 pis
->line_params
.dash
.adapt
);
626 gx_set_dot_length(&cdev
->imager_state
.line_params
,
627 pis
->line_params
.dot_length
,
628 pis
->line_params
.dot_length_absolute
);
630 if (state_neq(flatness
)) {
631 unknown
|= flatness_known
;
632 state_update(flatness
);
634 if (state_neq(fill_adjust
.x
) || state_neq(fill_adjust
.y
)) {
635 unknown
|= fill_adjust_known
;
636 state_update(fill_adjust
);
638 if (state_neq(ctm
.xx
) || state_neq(ctm
.xy
) ||
639 state_neq(ctm
.yx
) || state_neq(ctm
.yy
) ||
640 /* We don't actually need tx or ty, but we don't want to bother */
641 /* tracking them separately from the other coefficients. */
642 state_neq(ctm
.tx
) || state_neq(ctm
.ty
)
644 unknown
|= ctm_known
;
647 if (state_neq(line_params
.half_width
)) {
648 unknown
|= line_width_known
;
649 state_update(line_params
.half_width
);
651 if (state_neq(line_params
.miter_limit
)) {
652 unknown
|= miter_limit_known
;
653 gx_set_miter_limit(&cdev
->imager_state
.line_params
,
654 pis
->line_params
.miter_limit
);
656 if (state_neq(line_params
.cap
) || state_neq(line_params
.join
)) {
657 unknown
|= misc0_known
;
658 state_update(line_params
.cap
);
659 state_update(line_params
.join
);
661 if (state_neq(accurate_curves
) || state_neq(overprint
) ||
662 state_neq(stroke_adjust
)
664 unknown
|= misc1_known
;
665 state_update(accurate_curves
);
666 state_update(overprint
);
667 state_update(stroke_adjust
);
669 if (cdev
->imager_state
.alpha
!= pis
->alpha
) {
670 unknown
|= alpha_known
;
673 if (cmd_check_clip_path(cdev
, pcpath
))
674 unknown
|= clip_path_known
;
676 cmd_clear_known(cdev
, unknown
);
680 if ((code
= cmd_do_write_unknown(cdev
, pcls
, stroke_all_known
)) < 0 ||
681 (code
= cmd_do_enable_clip(cdev
, pcls
, pcpath
!= NULL
)) < 0 ||
682 (code
= cmd_update_lop(cdev
, pcls
, lop
)) < 0
685 code
= cmd_put_drawing_color(cdev
, pcls
, pdcolor
);
687 /* Something went wrong, use the default implementation. */
688 return gx_default_stroke_path(dev
, pis
, ppath
, params
, pdcolor
,
695 * If a dash pattern is active, we can't skip segments
696 * outside the clipping region, because that would throw off
699 if (pattern_size
== 0)
700 ymin
= int2fixed(max(y
- adjust_y
, y0
)),
701 ymax
= int2fixed(min(y
+ height
+ adjust_y
, y1
));
705 code
= cmd_put_path(cdev
, pcls
, ppath
, ymin
, ymax
,
706 cmd_opv_stroke
+ code
, /* cmd_dc_type */
707 false, (segment_notes
) ~ 0);
715 /* ------ Path utilities ------ */
717 /* Define the state bookkeeping for writing path segments. */
718 typedef struct cmd_segment_writer_s
{
719 /* Set at initialization */
720 gx_device_clist_writer
*cldev
;
721 gx_clist_state
*pcls
;
722 /* Updated dynamically */
726 gs_fixed_point delta_first
;
727 byte cmd
[6 * (1 + sizeof(fixed
))];
731 /* Put out a path segment command. */
733 cmd_put_segment(cmd_segment_writer
* psw
, byte op
,
734 const fixed
* operands
, segment_notes notes
)
736 const fixed
*optr
= operands
;
738 /* Fetch num_operands before possible command merging. */
739 int i
= clist_segment_op_num_operands
[op
& 0xf];
740 byte
*q
= psw
->cmd
- 1;
743 if (gs_debug_c('L')) {
746 dlprintf2("[L] %s:%d:", cmd_sub_op_names
[op
>> 4][op
& 0xf],
748 for (j
= 0; j
< i
; ++j
)
749 dprintf1(" %g", fixed2float(operands
[j
]));
754 /* Merge or shorten commands if possible. */
755 if (op
== cmd_opv_rlineto
) {
756 if (operands
[0] == 0)
757 op
= cmd_opv_vlineto
, optr
= ++operands
, i
= 1;
758 else if (operands
[1] == 0)
759 op
= cmd_opv_hlineto
, i
= 1;
762 case cmd_opv_rmoveto
:
763 psw
->delta_first
.x
= operands
[0];
764 psw
->delta_first
.y
= operands
[1];
765 op
= cmd_opv_rmlineto
;
766 merge
:cmd_uncount_op(*psw
->dp
, psw
->len
);
767 cmd_shorten_op(psw
->cldev
, psw
->pcls
, psw
->len
); /* delete it */
770 case cmd_opv_rmlineto
:
771 if (notes
!= psw
->notes
)
773 op
= cmd_opv_rm2lineto
;
775 case cmd_opv_rm2lineto
:
776 if (notes
!= psw
->notes
)
778 if (operands
[0] == -psw
->delta_first
.x
&&
779 operands
[1] == -psw
->delta_first
.y
781 cmd_uncount_op(cmd_opv_rm2lineto
, psw
->len
);
782 *psw
->dp
= cmd_count_op(cmd_opv_rm3lineto
, psw
->len
);
790 for (; --i
>= 0; ++optr
) {
793 if (is_bits(d
, _fixed_shift
+ 11) &&
794 !(d
& (float2fixed(0.25) - 1))
796 cmd_count_add1(stats_cmd_diffs
[3]);
797 d
= ((d
>> (_fixed_shift
- 2)) & 0x1fff) + 0xc000;
799 } else if (is_bits(d
, 19) && i
> 0 && is_bits(d2
= optr
[1], 19)) {
800 cmd_count_add1(stats_cmd_diffs
[0]);
801 q
[1] = (byte
) ((d
>> 13) & 0x3f);
802 q
[2] = (byte
) (d
>> 5);
803 q
[3] = (byte
) ((d
<< 3) + ((d2
>> 16) & 7));
804 q
[4] = (byte
) (d2
>> 8);
809 } else if (is_bits(d
, 22)) {
810 cmd_count_add1(stats_cmd_diffs
[1]);
811 q
[1] = (byte
) (((d
>> 16) & 0x3f) + 0x40);
813 } else if (is_bits(d
, 30)) {
814 cmd_count_add1(stats_cmd_diffs
[2]);
815 q
[1] = (byte
) (((d
>> 24) & 0x3f) + 0x80);
816 q
[2] = (byte
) (d
>> 16);
821 cmd_count_add1(stats_cmd_diffs
[4]);
823 for (b
= sizeof(fixed
) - 1; b
> 1; --b
)
824 *++q
= (byte
) (d
>> (b
* 8));
827 q
[-1] = (byte
) (d
>> 8);
830 if (notes
!= psw
->notes
) {
833 set_cmd_put_op(dp
, psw
->cldev
, psw
->pcls
, cmd_opv_set_misc2
, 2);
837 dp
[1] = cmd_set_misc2_notes
+ notes
;
840 int len
= q
+ 2 - psw
->cmd
;
842 int code
= set_cmd_put_op(dp
, psw
->cldev
, psw
->pcls
, op
, len
);
846 memcpy(dp
+ 1, psw
->cmd
, len
- 1);
852 /* Put out a line segment command. */
853 #define cmd_put_rmoveto(psw, operands)\
854 cmd_put_segment(psw, cmd_opv_rmoveto, operands, sn_none)
855 #define cmd_put_rlineto(psw, operands, notes)\
856 cmd_put_segment(psw, cmd_opv_rlineto, operands, notes)
859 * Write a path. We go to a lot of trouble to omit segments that are
860 * entirely outside the band.
863 cmd_put_path(gx_device_clist_writer
* cldev
, gx_clist_state
* pcls
,
864 const gx_path
* ppath
, fixed ymin
, fixed ymax
, byte path_op
,
865 bool implicit_close
, segment_notes keep_notes
)
868 cmd_segment_writer writer
;
871 * initial_op is logically const, so we declare it as such,
872 * since some systems really dislike non-const statics.
873 * This entails an otherwise pointless cast in set_first_point().
875 static const byte initial_op
= cmd_opv_end_run
;
878 * We define the 'side' of a point according to its Y value as
881 #define which_side(y) ((y) < ymin ? -1 : (y) >= ymax ? 1 : 0)
884 * While writing a subpath, we need to keep track of any segments
885 * skipped at the beginning of the subpath and any segments skipped
886 * just before the current segment. We do this with two sets of
887 * state variables, one that tracks the actual path segments and one
888 * that tracks the emitted segments.
890 * The following track the actual segments:
894 * The point and side of the last moveto (skipped if
897 gs_fixed_point start
;
901 * Whether any lines or curves were skipped immediately
902 * following the moveto:
906 /* The side of the last point: */
909 /* The last point with side != 0: */
912 /* If the last out-going segment was a lineto, */
914 segment_notes out_notes
;
917 * The following track the emitted segments:
920 /* The last point emitted: */
921 fixed px
= int2fixed(pcls
->rect
.x
);
922 fixed py
= int2fixed(pcls
->rect
.y
);
924 /* The point of the last emitted moveto: */
925 gs_fixed_point first
;
927 /* Information about the last emitted operation: */
928 int open
= 0; /* -1 if last was moveto, 1 if line/curveto, */
930 /* 0 if newpath/closepath */
933 if_debug4('p', "[p]initial (%g,%g), clip [%g..%g)\n",
934 fixed2float(px
), fixed2float(py
),
935 fixed2float(ymin
), fixed2float(ymax
));
936 gx_path_enum_init(&cenum
, ppath
);
937 writer
.cldev
= cldev
;
939 writer
.notes
= sn_none
;
940 #define set_first_point() (writer.dp = (byte *)&initial_op)
941 #define first_point() (writer.dp == &initial_op)
952 int pe_op
= gx_path_enum_next(&cenum
, (gs_fixed_point
*) vs
);
958 /* If the path is open and needs an implicit close, */
959 /* do the close and then come here again. */
960 if (open
> 0 && implicit_close
)
963 pcls
->rect
.x
= fixed2int_var(px
);
964 pcls
->rect
.y
= fixed2int_var(py
);
965 if_debug2('p', "[p]final (%d,%d)\n",
966 pcls
->rect
.x
, pcls
->rect
.y
);
967 return set_cmd_put_op(dp
, cldev
, pcls
, path_op
, 1);
969 /* If the path is open and needs an implicit close, */
970 /* do a closepath and then redo the moveto. */
971 if (open
> 0 && implicit_close
) {
972 gx_path_enum_backup(&cenum
);
976 start
.x
= A
, start
.y
= B
;
978 if ((start_side
= side
= which_side(B
)) != 0) {
979 out
.x
= A
, out
.y
= B
;
980 if_debug3('p', "[p]skip moveto (%g,%g) side %d\n",
981 fixed2float(out
.x
), fixed2float(out
.y
),
985 C
= A
- px
, D
= B
- py
;
986 first
.x
= px
= A
, first
.y
= py
= B
;
987 code
= cmd_put_rmoveto(&writer
, &C
);
988 if_debug2('p', "[p]moveto (%g,%g)\n",
989 fixed2float(px
), fixed2float(py
));
993 int next_side
= which_side(B
);
994 segment_notes notes
=
995 gx_path_enum_notes(&cenum
) & keep_notes
;
997 if (next_side
== side
&& side
!= 0) { /* Skip a line completely outside the clip region. */
1000 out
.x
= A
, out
.y
= B
;
1002 if_debug3('p', "[p]skip lineto (%g,%g) side %d\n",
1003 fixed2float(out
.x
), fixed2float(out
.y
),
1007 /* If we skipped any segments, put out a moveto/lineto. */
1008 if (side
&& (px
!= out
.x
|| py
!= out
.y
|| first_point())) {
1009 C
= out
.x
- px
, D
= out
.y
- py
;
1012 code
= cmd_put_rmoveto(&writer
, &C
);
1014 code
= cmd_put_rlineto(&writer
, &C
, out_notes
);
1017 px
= out
.x
, py
= out
.y
;
1018 if_debug3('p', "[p]catchup %s (%g,%g) for line\n",
1019 (open
< 0 ? "moveto" : "lineto"),
1020 fixed2float(px
), fixed2float(py
));
1022 if ((side
= next_side
) != 0) { /* Note a vertex going outside the clip region. */
1023 out
.x
= A
, out
.y
= B
;
1025 C
= A
- px
, D
= B
- py
;
1028 code
= cmd_put_rlineto(&writer
, &C
, notes
);
1030 if_debug3('p', "[p]lineto (%g,%g) side %d\n",
1031 fixed2float(px
), fixed2float(py
), side
);
1033 case gs_pe_closepath
:
1036 gs_path_enum cpenum
;
1037 gs_fixed_point cvs
[3];
1041 switch (op
= gx_path_enum_next(&cpenum
, cvs
)) {
1046 lprintf1("closepath followed by %d, not end/moveto!\n",
1051 /* A closepath may require drawing an explicit line if */
1052 /* we skipped any segments at the beginning of the path. */
1053 close
:if (side
!= start_side
) { /* If we skipped any segments, put out a moveto/lineto. */
1054 if (side
&& (px
!= out
.x
|| py
!= out
.y
|| first_point())) {
1055 C
= out
.x
- px
, D
= out
.y
- py
;
1056 code
= cmd_put_rlineto(&writer
, &C
, out_notes
);
1059 px
= out
.x
, py
= out
.y
;
1060 if_debug2('p', "[p]catchup line (%g,%g) for close\n",
1061 fixed2float(px
), fixed2float(py
));
1063 if (open
> 0 && start_skip
) { /* Draw the closing line back to the start. */
1064 C
= start
.x
- px
, D
= start
.y
- py
;
1065 code
= cmd_put_rlineto(&writer
, &C
, sn_none
);
1068 px
= start
.x
, py
= start
.y
;
1069 if_debug2('p', "[p]draw close to (%g,%g)\n",
1070 fixed2float(px
), fixed2float(py
));
1074 * We don't bother to update side because we know that the
1075 * next element after a closepath, if any, must be a moveto.
1076 * We must handle explicitly the possibility that the entire
1077 * subpath was skipped.
1079 if (implicit_close
|| open
<= 0) {
1082 * Force writing an explicit moveto if the next subpath
1083 * starts with a moveto to the same point where this one
1090 px
= first
.x
, py
= first
.y
;
1091 code
= cmd_put_segment(&writer
, cmd_opv_closepath
, &A
, sn_none
);
1092 if_debug0('p', "[p]close\n");
1096 segment_notes notes
=
1097 gx_path_enum_notes(&cenum
) & keep_notes
;
1101 int all_side
, out_side
;
1103 /* Compute the Y bounds for the clipping check. */
1112 all_side
= (bqy
< ymin
? -1 : bpy
> ymax
? 1 : 0);
1113 if (all_side
!= 0) {
1114 if (all_side
== side
) { /* Skip a curve entirely outside the clip region. */
1117 out
.x
= E
, out
.y
= F
;
1119 if_debug3('p', "[p]skip curveto (%g,%g) side %d\n",
1120 fixed2float(out
.x
), fixed2float(out
.y
),
1124 out_side
= all_side
;
1126 out_side
= which_side(F
);
1127 /* If we skipped any segments, put out a moveto/lineto. */
1128 if (side
&& (px
!= out
.x
|| py
!= out
.y
|| first_point())) {
1131 diff
[0] = out
.x
- px
, diff
[1] = out
.y
- py
;
1134 code
= cmd_put_rmoveto(&writer
, diff
);
1136 code
= cmd_put_rlineto(&writer
, diff
, out_notes
);
1139 px
= out
.x
, py
= out
.y
;
1140 if_debug3('p', "[p]catchup %s (%g,%g) for curve\n",
1141 (open
< 0 ? "moveto" : "lineto"),
1142 fixed2float(px
), fixed2float(py
));
1144 if ((side
= out_side
) != 0) { /* Note a vertex going outside the clip region. */
1145 out
.x
= E
, out
.y
= F
;
1149 fixed nx
= E
, ny
= F
;
1150 const fixed
*optr
= vs
;
1153 if_debug7('p', "[p]curveto (%g,%g; %g,%g; %g,%g) side %d\n",
1154 fixed2float(A
), fixed2float(B
),
1155 fixed2float(C
), fixed2float(D
),
1156 fixed2float(E
), fixed2float(F
), side
);
1160 if (B
== 0 && E
== 0) {
1161 B
= A
, E
= F
, optr
++, op
= cmd_opv_hvcurveto
;
1163 if (C
== D
&& E
== B
)
1164 op
= cmd_opv_hqcurveto
;
1165 } else if (C
== -D
&& E
== -B
)
1166 C
= D
, op
= cmd_opv_hqcurveto
;
1167 } else if (A
== 0 && F
== 0) {
1168 optr
++, op
= cmd_opv_vhcurveto
;
1170 if (D
== C
&& E
== B
)
1171 op
= cmd_opv_vqcurveto
;
1172 } else if (D
== -C
&& E
== -B
)
1173 op
= cmd_opv_vqcurveto
;
1174 } else if (A
== 0 && B
== 0)
1175 optr
+= 2, op
= cmd_opv_nrcurveto
;
1176 else if (E
== 0 && F
== 0)
1177 op
= cmd_opv_rncurveto
;
1179 op
= cmd_opv_rrcurveto
;
1182 code
= cmd_put_segment(&writer
, op
, optr
, notes
);
1187 return_error(gs_error_rangecheck
);