xref: /plan9/sys/src/cmd/gs/src/gxclpath.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
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