xref: /plan9/sys/src/cmd/gs/src/gdevvec.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
1 /* Copyright (C) 1997, 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: gdevvec.c,v 1.27 2005/08/23 11:26:26 igor Exp $ */
18 /* Utilities for "vector" devices */
19 #include "math_.h"
20 #include "memory_.h"
21 #include "string_.h"
22 #include "gx.h"
23 #include "gp.h"
24 #include "gserrors.h"
25 #include "gsparam.h"
26 #include "gsutil.h"
27 #include "gxfixed.h"
28 #include "gdevvec.h"
29 #include "gscspace.h"
30 #include "gxdcolor.h"
31 #include "gxpaint.h"		/* requires gx_path, ... */
32 #include "gzpath.h"
33 #include "gzcpath.h"
34 
35 /* Structure descriptors */
36 public_st_device_vector();
37 public_st_vector_image_enum();
38 
39 /* ================ Default implementations of vector procs ================ */
40 
41 int
gdev_vector_setflat(gx_device_vector * vdev,floatp flatness)42 gdev_vector_setflat(gx_device_vector * vdev, floatp flatness)
43 {
44     return 0;
45 }
46 
47 /* Put a path on the output file. */
48 private bool
coord_between(fixed start,fixed mid,fixed end)49 coord_between(fixed start, fixed mid, fixed end)
50 {
51     return (start <= end ? start <= mid && mid <= end :
52 	    start >= mid && mid >= end);
53 }
54 int
gdev_vector_dopath(gx_device_vector * vdev,const gx_path * ppath,gx_path_type_t type,const gs_matrix * pmat)55 gdev_vector_dopath(gx_device_vector *vdev, const gx_path * ppath,
56 		   gx_path_type_t type, const gs_matrix *pmat)
57 {
58     bool do_close =
59 	(type & (gx_path_type_stroke | gx_path_type_always_close)) != 0;
60     gs_fixed_rect rbox;
61     gx_path_rectangular_type rtype = gx_path_is_rectangular(ppath, &rbox);
62     gs_path_enum cenum;
63     gdev_vector_dopath_state_t state;
64     gs_fixed_point line_start, line_end;
65     bool incomplete_line = false;
66     bool need_moveto = false;
67     int code;
68 
69     gdev_vector_dopath_init(&state, vdev, type, pmat);
70     /*
71      * if the path type is stroke, we only recognize closed
72      * rectangles; otherwise, we recognize all rectangles.
73      * Note that for stroking with a transformation, we can't use dorect,
74      * which requires (untransformed) device coordinates.
75      */
76     if (rtype != prt_none &&
77 	(!(type & gx_path_type_stroke) || rtype == prt_closed) &&
78 	(pmat == 0 || is_xxyy(pmat) || is_xyyx(pmat)) &&
79 	(state.scale_mat.xx == 1.0 && state.scale_mat.yy == 1.0 &&
80 	 is_xxyy(&state.scale_mat) &&
81 	 is_fzero2(state.scale_mat.tx, state.scale_mat.ty))
82 	) {
83 	gs_point p, q;
84 
85 	gs_point_transform_inverse((floatp)rbox.p.x, (floatp)rbox.p.y,
86 				   &state.scale_mat, &p);
87 	gs_point_transform_inverse((floatp)rbox.q.x, (floatp)rbox.q.y,
88 				   &state.scale_mat, &q);
89 	code = vdev_proc(vdev, dorect)(vdev, (fixed)p.x, (fixed)p.y,
90 				       (fixed)q.x, (fixed)q.y, type);
91 	if (code >= 0)
92 	    return code;
93 	/* If the dorect proc failed, use a general path. */
94     }
95     code = vdev_proc(vdev, beginpath)(vdev, type);
96     if (code < 0)
97 	return code;
98     gx_path_enum_init(&cenum, ppath);
99     for (;;) {
100 	gs_fixed_point vs[3];
101 	int pe_op = gx_path_enum_next(&cenum, vs);
102 
103     sw:
104 	if (type & gx_path_type_optimize) {
105 	opt:
106 	    if (pe_op == gs_pe_lineto) {
107 		if (!incomplete_line) {
108 		    line_end = vs[0];
109 		    incomplete_line = true;
110 		    continue;
111 		}
112 		/*
113 		 * Merge collinear horizontal or vertical line segments
114 		 * going in the same direction.
115 		 */
116 		if (vs[0].x == line_end.x) {
117 		    if (vs[0].x == line_start.x &&
118 			coord_between(line_start.y, line_end.y, vs[0].y)
119 			) {
120 			line_end.y = vs[0].y;
121 			continue;
122 		    }
123 		} else if (vs[0].y == line_end.y) {
124 		    if (vs[0].y == line_start.y &&
125 			coord_between(line_start.x, line_end.x, vs[0].x)
126 			) {
127 			line_end.x = vs[0].x;
128 			continue;
129 		    }
130 		}
131 	    }
132 	    if (incomplete_line) {
133 		if (need_moveto) {	/* see gs_pe_moveto case */
134 		    code = gdev_vector_dopath_segment(&state, gs_pe_moveto,
135 						      &line_start);
136 		    if (code < 0)
137 			return code;
138 		    need_moveto = false;
139 		}
140 		code = gdev_vector_dopath_segment(&state, gs_pe_lineto,
141 						  &line_end);
142 		if (code < 0)
143 		    return code;
144 		line_start = line_end;
145 		incomplete_line = false;
146 		goto opt;
147 	    }
148 	}
149 	switch (pe_op) {
150 	case 0:		/* done */
151 	done:
152 	    code = vdev_proc(vdev, endpath)(vdev, type);
153 	    return (code < 0 ? code : 0);
154 	case gs_pe_curveto:
155 	    if (need_moveto) {	/* see gs_pe_moveto case */
156 		code = gdev_vector_dopath_segment(&state, gs_pe_moveto,
157 						  &line_start);
158 		if (code < 0)
159 		    return code;
160 		need_moveto = false;
161 	    }
162 	    line_start = vs[2];
163 	    goto draw;
164 	case gs_pe_moveto:
165 	    /*
166 	     * A bug in Acrobat Reader 4 causes it to draw a single pixel
167 	     * for a fill with an isolated moveto.  If we're doing a fill
168 	     * without a stroke, defer emitting a moveto until we know that
169 	     * the subpath has more elements.
170 	     */
171 	    line_start = vs[0];
172 	    if (!(type & gx_path_type_stroke) && (type & gx_path_type_fill)) {
173 		need_moveto = true;
174 		continue;
175 	    }
176 	    goto draw;
177 	case gs_pe_lineto:
178 	    if (need_moveto) {	/* see gs_pe_moveto case */
179 		code = gdev_vector_dopath_segment(&state, gs_pe_moveto,
180 						  &line_start);
181 		if (code < 0)
182 		    return code;
183 		need_moveto = false;
184 	    }
185 	    line_start = vs[0];
186 	    goto draw;
187 	case gs_pe_closepath:
188 	    if (need_moveto) {	/* see gs_pe_moveto case */
189 		need_moveto = false;
190 		continue;
191 	    }
192 	    if (!do_close) {
193 		pe_op = gx_path_enum_next(&cenum, vs);
194 		if (pe_op == 0)
195 		    goto done;
196 		code = gdev_vector_dopath_segment(&state, gs_pe_closepath, vs);
197 		if (code < 0)
198 		    return code;
199 		goto sw;
200 	    }
201 	    /* falls through */
202 	draw:
203 	    code = gdev_vector_dopath_segment(&state, pe_op, vs);
204 	    if (code < 0)
205 		return code;
206 	}
207 	incomplete_line = false; /* only needed if optimizing */
208     }
209 }
210 
211 int
gdev_vector_dorect(gx_device_vector * vdev,fixed x0,fixed y0,fixed x1,fixed y1,gx_path_type_t type)212 gdev_vector_dorect(gx_device_vector * vdev, fixed x0, fixed y0, fixed x1,
213 		   fixed y1, gx_path_type_t type)
214 {
215     int code = (*vdev_proc(vdev, beginpath)) (vdev, type);
216 
217     if (code < 0)
218 	return code;
219     code = gdev_vector_write_rectangle(vdev, x0, y0, x1, y1,
220 				       (type & gx_path_type_stroke) != 0,
221 				       gx_rect_x_first);
222     if (code < 0)
223 	return code;
224     return (*vdev_proc(vdev, endpath)) (vdev, type);
225 }
226 
227 /* ================ Utility procedures ================ */
228 
229 /* Recompute the cached color values. */
230 private void
gdev_vector_load_cache(gx_device_vector * vdev)231 gdev_vector_load_cache(gx_device_vector * vdev)
232 {
233     vdev->black = gx_device_black((gx_device *)vdev);
234     vdev->white = gx_device_white((gx_device *)vdev);
235 }
236 
237 /* Initialize the state. */
238 void
gdev_vector_init(gx_device_vector * vdev)239 gdev_vector_init(gx_device_vector * vdev)
240 {
241     gdev_vector_reset(vdev);
242     vdev->scale.x = vdev->scale.y = 1.0;
243     vdev->in_page = false;
244     gdev_vector_load_cache(vdev);
245 }
246 
247 /* Reset the remembered graphics state. */
248 void
gdev_vector_reset(gx_device_vector * vdev)249 gdev_vector_reset(gx_device_vector * vdev)
250 {
251     static const gs_imager_state state_initial =
252     {gs_imager_state_initial(1)};
253 
254     vdev->state = state_initial;
255     gx_hld_saved_color_init(&vdev->saved_fill_color);
256     gx_hld_saved_color_init(&vdev->saved_stroke_color);
257     vdev->clip_path_id =
258 	vdev->no_clip_path_id = gs_next_ids(vdev->memory, 1);
259 }
260 
261 /* Open the output file and stream. */
262 int
gdev_vector_open_file_options(gx_device_vector * vdev,uint strmbuf_size,int open_options)263 gdev_vector_open_file_options(gx_device_vector * vdev, uint strmbuf_size,
264 			      int open_options)
265 {
266     bool binary = !(open_options & VECTOR_OPEN_FILE_ASCII);
267     int code = -1;		/* (only for testing, never returned) */
268 
269     /* Open the file as seekable or sequential, as requested. */
270     if (!(open_options & VECTOR_OPEN_FILE_SEQUENTIAL)) {
271 	/* Try to open as seekable. */
272 	code =
273 	    gx_device_open_output_file((gx_device *)vdev, vdev->fname,
274 				       binary, true, &vdev->file);
275     }
276     if (code < 0 && (open_options & (VECTOR_OPEN_FILE_SEQUENTIAL |
277 				     VECTOR_OPEN_FILE_SEQUENTIAL_OK))) {
278 	/* Try to open as sequential. */
279 	code = gx_device_open_output_file((gx_device *)vdev, vdev->fname,
280 					  binary, false, &vdev->file);
281     }
282     if (code < 0)
283 	return code;
284     if ((vdev->strmbuf = gs_alloc_bytes(vdev->v_memory, strmbuf_size,
285 					"vector_open(strmbuf)")) == 0 ||
286 	(vdev->strm = s_alloc(vdev->v_memory,
287 			      "vector_open(strm)")) == 0 ||
288 	((open_options & VECTOR_OPEN_FILE_BBOX) &&
289 	 (vdev->bbox_device =
290 	  gs_alloc_struct_immovable(vdev->v_memory,
291 				    gx_device_bbox, &st_device_bbox,
292 				    "vector_open(bbox_device)")) == 0)
293 	) {
294 	if (vdev->bbox_device)
295 	    gs_free_object(vdev->v_memory, vdev->bbox_device,
296 			   "vector_open(bbox_device)");
297 	vdev->bbox_device = 0;
298 	if (vdev->strm)
299 	    gs_free_object(vdev->v_memory, vdev->strm,
300 			   "vector_open(strm)");
301 	vdev->strm = 0;
302 	if (vdev->strmbuf)
303 	    gs_free_object(vdev->v_memory, vdev->strmbuf,
304 			   "vector_open(strmbuf)");
305 	vdev->strmbuf = 0;
306 	fclose(vdev->file);
307 	vdev->file = 0;
308 	return_error(gs_error_VMerror);
309     }
310     vdev->strmbuf_size = strmbuf_size;
311     swrite_file(vdev->strm, vdev->file, vdev->strmbuf, strmbuf_size);
312     vdev->open_options = open_options;
313     /*
314      * We don't want finalization to close the file, but we do want it
315      * to flush the stream buffer.
316      */
317     vdev->strm->procs.close = vdev->strm->procs.flush;
318     if (vdev->bbox_device) {
319 	gx_device_bbox_init(vdev->bbox_device, NULL, vdev->v_memory);
320         rc_increment(vdev->bbox_device);
321 	gx_device_set_resolution((gx_device *) vdev->bbox_device,
322 				 vdev->HWResolution[0],
323 				 vdev->HWResolution[1]);
324 	/* Do the right thing about upright vs. inverted. */
325 	/* (This is dangerous in general, since the procedure */
326 	/* might reference non-standard elements.) */
327 	set_dev_proc(vdev->bbox_device, get_initial_matrix,
328 		     dev_proc(vdev, get_initial_matrix));
329 	(*dev_proc(vdev->bbox_device, open_device))
330 	    ((gx_device *) vdev->bbox_device);
331     }
332     return 0;
333 }
334 
335 /* Get the current stream, calling beginpage if in_page is false. */
336 stream *
gdev_vector_stream(gx_device_vector * vdev)337 gdev_vector_stream(gx_device_vector * vdev)
338 {
339     if (!vdev->in_page) {
340 	(*vdev_proc(vdev, beginpage)) (vdev);
341 	vdev->in_page = true;
342     }
343     return vdev->strm;
344 }
345 
346 /* Update the logical operation. */
347 int
gdev_vector_update_log_op(gx_device_vector * vdev,gs_logical_operation_t lop)348 gdev_vector_update_log_op(gx_device_vector * vdev, gs_logical_operation_t lop)
349 {
350     gs_logical_operation_t diff = lop ^ vdev->state.log_op;
351 
352     if (diff != 0) {
353 	int code = (*vdev_proc(vdev, setlogop)) (vdev, lop, diff);
354 
355 	if (code < 0)
356 	    return code;
357 	vdev->state.log_op = lop;
358     }
359     return 0;
360 }
361 
362 /* Update color (fill or stroke). */
363 private int
gdev_vector_update_color(gx_device_vector * vdev,const gs_imager_state * pis,const gx_drawing_color * pdcolor,gx_hl_saved_color * sc,int (* setcolor)(gx_device_vector * vdev,const gs_imager_state * pis,const gx_drawing_color * pdc))364 gdev_vector_update_color(gx_device_vector * vdev,
365 			      const gs_imager_state * pis,
366 			      const gx_drawing_color * pdcolor,
367 			      gx_hl_saved_color *sc,
368 			      int (*setcolor) (gx_device_vector * vdev,
369 			                       const gs_imager_state * pis,
370 					       const gx_drawing_color * pdc))
371 {
372     gx_hl_saved_color temp;
373     int code;
374     bool hl_color = (*vdev_proc(vdev, can_handle_hl_color)) (vdev, pis, pdcolor);
375     const gs_imager_state *pis_for_hl_color = (hl_color ? pis : NULL);
376 
377     gx_hld_save_color(pis_for_hl_color, pdcolor, &temp);
378     if (gx_hld_saved_color_equal(&temp, sc))
379 	return 0;
380     code = (*setcolor) (vdev, pis_for_hl_color, pdcolor);
381     if (code < 0)
382 	return code;
383     *sc = temp;
384     return 0;
385 }
386 
387 /* Update the fill color. */
388 int
gdev_vector_update_fill_color(gx_device_vector * vdev,const gs_imager_state * pis,const gx_drawing_color * pdcolor)389 gdev_vector_update_fill_color(gx_device_vector * vdev,
390 			      const gs_imager_state * pis,
391 			      const gx_drawing_color * pdcolor)
392 {
393     return gdev_vector_update_color(vdev, pis, pdcolor, &vdev->saved_fill_color,
394                                     vdev_proc(vdev, setfillcolor));
395 }
396 
397 /* Update the state for filling a region. */
398 private int
update_fill(gx_device_vector * vdev,const gs_imager_state * pis,const gx_drawing_color * pdcolor,gs_logical_operation_t lop)399 update_fill(gx_device_vector * vdev, const gs_imager_state * pis,
400 	    const gx_drawing_color * pdcolor, gs_logical_operation_t lop)
401 {
402     int code = gdev_vector_update_fill_color(vdev, pis, pdcolor);
403 
404     if (code < 0)
405 	return code;
406     return gdev_vector_update_log_op(vdev, lop);
407 }
408 
409 /* Bring state up to date for filling. */
410 int
gdev_vector_prepare_fill(gx_device_vector * vdev,const gs_imager_state * pis,const gx_fill_params * params,const gx_drawing_color * pdcolor)411 gdev_vector_prepare_fill(gx_device_vector * vdev, const gs_imager_state * pis,
412 	    const gx_fill_params * params, const gx_drawing_color * pdcolor)
413 {
414     if (params->flatness != vdev->state.flatness) {
415 	int code = (*vdev_proc(vdev, setflat)) (vdev, params->flatness);
416 
417 	if (code < 0)
418 	    return code;
419 	vdev->state.flatness = params->flatness;
420     }
421     return update_fill(vdev, pis, pdcolor, pis->log_op);
422 }
423 
424 /* Compare two dash patterns. */
425 private bool
dash_pattern_eq(const float * stored,const gx_dash_params * set,floatp scale)426 dash_pattern_eq(const float *stored, const gx_dash_params * set, floatp scale)
427 {
428     int i;
429 
430     for (i = 0; i < set->pattern_size; ++i)
431 	if (stored[i] != (float)(set->pattern[i] * scale))
432 	    return false;
433     return true;
434 }
435 
436 /* Bring state up to date for stroking. */
437 int
gdev_vector_prepare_stroke(gx_device_vector * vdev,const gs_imager_state * pis,const gx_stroke_params * params,const gx_drawing_color * pdcolor,floatp scale)438 gdev_vector_prepare_stroke(gx_device_vector * vdev,
439 			   const gs_imager_state * pis,	/* may be NULL */
440 			   const gx_stroke_params * params, /* may be NULL */
441 			   const gx_drawing_color * pdcolor, /* may be NULL */
442 			   floatp scale)
443 {
444     if (pis) {
445 	int pattern_size = pis->line_params.dash.pattern_size;
446 	float dash_offset = pis->line_params.dash.offset * scale;
447 	float half_width = pis->line_params.half_width * scale;
448 
449 	if (pattern_size > max_dash)
450 	    return_error(gs_error_limitcheck);
451 	if (dash_offset != vdev->state.line_params.dash.offset ||
452 	    pattern_size != vdev->state.line_params.dash.pattern_size ||
453 	    (pattern_size != 0 &&
454 	     !dash_pattern_eq(vdev->dash_pattern, &pis->line_params.dash,
455 			      scale))
456 	    ) {
457 	    float pattern[max_dash];
458 	    int i, code;
459 
460 	    for (i = 0; i < pattern_size; ++i)
461 		pattern[i] = pis->line_params.dash.pattern[i] * scale;
462 	    code = (*vdev_proc(vdev, setdash))
463 		(vdev, pattern, pattern_size, dash_offset);
464 	    if (code < 0)
465 		return code;
466 	    memcpy(vdev->dash_pattern, pattern, pattern_size * sizeof(float));
467 
468 	    vdev->state.line_params.dash.pattern_size = pattern_size;
469 	    vdev->state.line_params.dash.offset = dash_offset;
470 	}
471 	if (half_width != vdev->state.line_params.half_width) {
472 	    int code = (*vdev_proc(vdev, setlinewidth))
473 		(vdev, half_width * 2);
474 
475 	    if (code < 0)
476 		return code;
477 	    vdev->state.line_params.half_width = half_width;
478 	}
479 	if (pis->line_params.miter_limit != vdev->state.line_params.miter_limit) {
480 	    int code = (*vdev_proc(vdev, setmiterlimit))
481 		(vdev, pis->line_params.miter_limit);
482 
483 	    if (code < 0)
484 		return code;
485 	    gx_set_miter_limit(&vdev->state.line_params,
486 			       pis->line_params.miter_limit);
487 	}
488 	if (pis->line_params.cap != vdev->state.line_params.cap) {
489 	    int code = (*vdev_proc(vdev, setlinecap))
490 		(vdev, pis->line_params.cap);
491 
492 	    if (code < 0)
493 		return code;
494 	    vdev->state.line_params.cap = pis->line_params.cap;
495 	}
496 	if (pis->line_params.join != vdev->state.line_params.join) {
497 	    int code = (*vdev_proc(vdev, setlinejoin))
498 		(vdev, pis->line_params.join);
499 
500 	    if (code < 0)
501 		return code;
502 	    vdev->state.line_params.join = pis->line_params.join;
503 	} {
504 	    int code = gdev_vector_update_log_op(vdev, pis->log_op);
505 
506 	    if (code < 0)
507 		return code;
508 	}
509     }
510     if (params) {
511 	if (params->flatness != vdev->state.flatness) {
512 	    int code = (*vdev_proc(vdev, setflat)) (vdev, params->flatness);
513 
514 	    if (code < 0)
515 		return code;
516 	    vdev->state.flatness = params->flatness;
517 	}
518     }
519     if (pdcolor) {
520 	int code = gdev_vector_update_color(vdev, pis, pdcolor,
521 		    &vdev->saved_stroke_color, vdev_proc(vdev, setstrokecolor));
522 
523 	if (code < 0)
524 	    return code;
525     }
526     return 0;
527 }
528 
529 /*
530  * Compute the scale for transforming the line width and dash pattern for a
531  * stroke operation, and, if necessary to handle anisotropic scaling, a full
532  * transformation matrix to be inverse-applied to the path elements as well.
533  * Return 0 if only scaling, 1 if a full matrix is needed.
534  */
535 int
gdev_vector_stroke_scaling(const gx_device_vector * vdev,const gs_imager_state * pis,double * pscale,gs_matrix * pmat)536 gdev_vector_stroke_scaling(const gx_device_vector *vdev,
537 			   const gs_imager_state *pis,
538 			   double *pscale, gs_matrix *pmat)
539 {
540     bool set_ctm = true;
541     double scale = 1;
542 
543     /*
544      * If the CTM is not uniform, stroke width depends on angle.
545      * We'd like to avoid resetting the CTM, so we check for uniform
546      * CTMs explicitly.  Note that in PDF, unlike PostScript, it is
547      * the CTM at the time of the stroke operation, not the CTM at
548      * the time the path was constructed, that is used for transforming
549      * the points of the path; so if we have to reset the CTM, we must
550      * do it before constructing the path, and inverse-transform all
551      * the coordinates.
552      */
553     if (is_xxyy(&pis->ctm)) {
554 	scale = fabs(pis->ctm.xx);
555 	set_ctm = fabs(pis->ctm.yy) != scale;
556     } else if (is_xyyx(&pis->ctm)) {
557 	scale = fabs(pis->ctm.xy);
558 	set_ctm = fabs(pis->ctm.yx) != scale;
559     } else if ((pis->ctm.xx == pis->ctm.yy && pis->ctm.xy == -pis->ctm.yx) ||
560 	       (pis->ctm.xx == -pis->ctm.yy && pis->ctm.xy == pis->ctm.yx)
561 	) {
562 	scale = hypot(pis->ctm.xx, pis->ctm.xy);
563 	set_ctm = false;
564     }
565     if (set_ctm) {
566 	/*
567 	 * Adobe Acrobat Reader has limitations on the maximum user
568 	 * coordinate value.  If we scale the matrix down too far, the
569 	 * coordinates will get too big: limit the scale factor to prevent
570 	 * this from happening.  (This does no harm for other output
571 	 * formats.)
572 	 */
573 	double
574 	    mxx = pis->ctm.xx / vdev->scale.x,
575 	    mxy = pis->ctm.xy / vdev->scale.y,
576 	    myx = pis->ctm.yx / vdev->scale.x,
577 	    myy = pis->ctm.yy / vdev->scale.y;
578 
579 	scale = 0.5 * (fabs(mxx) + fabs(mxy) + fabs(myx) + fabs(myy));
580 	pmat->xx = mxx / scale, pmat->xy = mxy / scale;
581 	pmat->yx = myx / scale, pmat->yy = myy / scale;
582 	pmat->tx = pmat->ty = 0;
583     }
584     *pscale = scale;
585     return (int)set_ctm;
586 }
587 
588 /* Initialize for writing a path using the default implementation. */
589 void
gdev_vector_dopath_init(gdev_vector_dopath_state_t * state,gx_device_vector * vdev,gx_path_type_t type,const gs_matrix * pmat)590 gdev_vector_dopath_init(gdev_vector_dopath_state_t *state,
591 			gx_device_vector *vdev, gx_path_type_t type,
592 			const gs_matrix *pmat)
593 {
594     state->vdev = vdev;
595     state->type = type;
596     if (pmat) {
597 	state->scale_mat = *pmat;
598 	/*
599 	 * The path element writing procedures all divide the coordinates
600 	 * by the scale, so we must compensate for that here.
601 	 */
602 	gs_matrix_scale(&state->scale_mat, 1.0 / vdev->scale.x,
603 			1.0 / vdev->scale.y, &state->scale_mat);
604     } else {
605 	gs_make_scaling(vdev->scale.x, vdev->scale.y, &state->scale_mat);
606     }
607     state->first = true;
608 }
609 
610 /*
611  * Put a segment of an enumerated path on the output file.
612  * pe_op is assumed to be valid and non-zero.
613  */
614 int
gdev_vector_dopath_segment(gdev_vector_dopath_state_t * state,int pe_op,gs_fixed_point vs[3])615 gdev_vector_dopath_segment(gdev_vector_dopath_state_t *state, int pe_op,
616 			   gs_fixed_point vs[3])
617 {
618     gx_device_vector *vdev = state->vdev;
619     const gs_matrix *const pmat = &state->scale_mat;
620     gs_point vp[3];
621     int code;
622 
623     switch (pe_op) {
624 	case gs_pe_moveto:
625 	    code = gs_point_transform_inverse(fixed2float(vs[0].x),
626 				       fixed2float(vs[0].y), pmat, &vp[0]);
627 	    if (code < 0)
628 		return code;
629 	    if (state->first)
630 		state->start = vp[0], state->first = false;
631 	    code = vdev_proc(vdev, moveto)
632 		(vdev, 0/*unused*/, 0/*unused*/, vp[0].x, vp[0].y,
633 		 state->type);
634 	    state->prev = vp[0];
635 	    break;
636 	case gs_pe_lineto:
637 	    code = gs_point_transform_inverse(fixed2float(vs[0].x),
638 				       fixed2float(vs[0].y), pmat, &vp[0]);
639 	    if (code < 0)
640 		return code;
641 	    code = vdev_proc(vdev, lineto)
642 		(vdev, state->prev.x, state->prev.y, vp[0].x, vp[0].y,
643 		 state->type);
644 	    state->prev = vp[0];
645 	    break;
646 	case gs_pe_curveto:
647 	    code = gs_point_transform_inverse(fixed2float(vs[0].x),
648 				       fixed2float(vs[0].y), pmat, &vp[0]);
649 	    if (code < 0)
650 		return code;
651 	    code = gs_point_transform_inverse(fixed2float(vs[1].x),
652 				       fixed2float(vs[1].y), pmat, &vp[1]);
653 	    if (code < 0)
654 		return code;
655 	    gs_point_transform_inverse(fixed2float(vs[2].x),
656 				       fixed2float(vs[2].y), pmat, &vp[2]);
657 	    code = vdev_proc(vdev, curveto)
658 		(vdev, state->prev.x, state->prev.y, vp[0].x, vp[0].y,
659 		 vp[1].x, vp[1].y, vp[2].x, vp[2].y, state->type);
660 	    state->prev = vp[2];
661 	    break;
662 	case gs_pe_closepath:
663 	    code = vdev_proc(vdev, closepath)
664 		(vdev, state->prev.x, state->prev.y, state->start.x,
665 		 state->start.y, state->type);
666 	    state->prev = state->start;
667 	    break;
668 	default:		/* can't happen */
669 	    return -1;
670     }
671     return code;
672 }
673 
674 /* Write a polygon as part of a path. */
675 /* May call beginpath, moveto, lineto, closepath, endpath. */
676 int
gdev_vector_write_polygon(gx_device_vector * vdev,const gs_fixed_point * points,uint count,bool close,gx_path_type_t type)677 gdev_vector_write_polygon(gx_device_vector * vdev, const gs_fixed_point * points,
678 			  uint count, bool close, gx_path_type_t type)
679 {
680     int code = 0;
681 
682     if (type != gx_path_type_none &&
683 	(code = (*vdev_proc(vdev, beginpath)) (vdev, type)) < 0
684 	)
685 	return code;
686     if (count > 0) {
687 	double x = fixed2float(points[0].x) / vdev->scale.x, y = fixed2float(points[0].y) / vdev->scale.y;
688 	double x_start = x, y_start = y, x_prev, y_prev;
689 	uint i;
690 
691 	code = (*vdev_proc(vdev, moveto))
692 	    (vdev, 0.0, 0.0, x, y, type);
693 	if (code >= 0)
694 	    for (i = 1; i < count && code >= 0; ++i) {
695 		x_prev = x, y_prev = y;
696 		code = (*vdev_proc(vdev, lineto))
697 		    (vdev, x_prev, y_prev,
698 		     (x = fixed2float(points[i].x) / vdev->scale.x),
699 		     (y = fixed2float(points[i].y) / vdev->scale.y),
700 		     type);
701 	    }
702 	if (code >= 0 && close)
703 	    code = (*vdev_proc(vdev, closepath))
704 		(vdev, x, y, x_start, y_start, type);
705     }
706     return (code >= 0 && type != gx_path_type_none ?
707 	    (*vdev_proc(vdev, endpath)) (vdev, type) : code);
708 }
709 
710 /* Write a rectangle as part of a path. */
711 /* May call moveto, lineto, closepath. */
712 int
gdev_vector_write_rectangle(gx_device_vector * vdev,fixed x0,fixed y0,fixed x1,fixed y1,bool close,gx_rect_direction_t direction)713 gdev_vector_write_rectangle(gx_device_vector * vdev, fixed x0, fixed y0,
714 	      fixed x1, fixed y1, bool close, gx_rect_direction_t direction)
715 {
716     gs_fixed_point points[4];
717 
718     points[0].x = x0, points[0].y = y0;
719     points[2].x = x1, points[2].y = y1;
720     if (direction == gx_rect_x_first)
721 	points[1].x = x1, points[1].y = y0,
722 	    points[3].x = x0, points[3].y = y1;
723     else
724 	points[1].x = x0, points[1].y = y1,
725 	    points[3].x = x1, points[3].y = y0;
726     return gdev_vector_write_polygon(vdev, points, 4, close,
727 				     gx_path_type_none);
728 }
729 
730 /* Write a clipping path by calling the path procedures. */
731 int
gdev_vector_write_clip_path(gx_device_vector * vdev,const gx_clip_path * pcpath)732 gdev_vector_write_clip_path(gx_device_vector * vdev,
733 			    const gx_clip_path * pcpath)
734 {
735     const gx_clip_rect *prect;
736     gx_clip_rect page_rect;
737     int code;
738 
739     if (pcpath == 0) {
740 	/* There's no special provision for initclip. */
741 	/* Write a rectangle that covers the entire page. */
742 	page_rect.xmin = page_rect.ymin = 0;
743 	page_rect.xmax = vdev->width;
744 	page_rect.ymax = vdev->height;
745 	page_rect.next = 0;
746 	prect = &page_rect;
747     } else if (pcpath->path_valid) {
748 	return (*vdev_proc(vdev, dopath))
749 	    (vdev, &pcpath->path,
750 	     (pcpath->rule <= 0 ?
751 	      gx_path_type_clip | gx_path_type_winding_number :
752 	      gx_path_type_clip | gx_path_type_even_odd),
753 	     NULL);
754     } else {
755 	const gx_clip_list *list = gx_cpath_list(pcpath);
756 
757 	prect = list->head;
758 	if (prect == 0)
759 	    prect = &list->single;
760     }
761     /* Write out the rectangles. */
762     code = (*vdev_proc(vdev, beginpath)) (vdev, gx_path_type_clip);
763     for (; code >= 0 && prect != 0; prect = prect->next)
764 	if (prect->xmax > prect->xmin && prect->ymax > prect->ymin)
765 	    code = gdev_vector_write_rectangle
766 		(vdev, int2fixed(prect->xmin), int2fixed(prect->ymin),
767 		 int2fixed(prect->xmax), int2fixed(prect->ymax),
768 		 false, gx_rect_x_first);
769     if (code >= 0)
770 	code = (*vdev_proc(vdev, endpath)) (vdev, gx_path_type_clip);
771     return code;
772 }
773 
774 /* Update the clipping path if needed. */
775 int
gdev_vector_update_clip_path(gx_device_vector * vdev,const gx_clip_path * pcpath)776 gdev_vector_update_clip_path(gx_device_vector * vdev,
777 			     const gx_clip_path * pcpath)
778 {
779     if (pcpath) {
780 	if (pcpath->id != vdev->clip_path_id) {
781 	    int code = gdev_vector_write_clip_path(vdev, pcpath);
782 
783 	    if (code < 0)
784 		return code;
785 	    vdev->clip_path_id = pcpath->id;
786 	}
787     } else {
788 	if (vdev->clip_path_id != vdev->no_clip_path_id) {
789 	    int code = gdev_vector_write_clip_path(vdev, NULL);
790 
791 	    if (code < 0)
792 		return code;
793 	    vdev->clip_path_id = vdev->no_clip_path_id;
794 	}
795     }
796     return 0;
797 }
798 
799 /* Close the output file and stream. */
800 int
gdev_vector_close_file(gx_device_vector * vdev)801 gdev_vector_close_file(gx_device_vector * vdev)
802 {
803     FILE *f = vdev->file;
804     int err;
805 
806     gs_free_object(vdev->v_memory, vdev->bbox_device,
807 		   "vector_close(bbox_device)");
808     vdev->bbox_device = 0;
809     sclose(vdev->strm);
810     gs_free_object(vdev->v_memory, vdev->strm, "vector_close(strm)");
811     vdev->strm = 0;
812     gs_free_object(vdev->v_memory, vdev->strmbuf, "vector_close(strmbuf)");
813     vdev->strmbuf = 0;
814     vdev->file = 0;
815     err = ferror(f);
816     /* We prevented sclose from closing the file. */
817     if (gx_device_close_output_file((gx_device *)vdev, vdev->fname, f) != 0
818 	|| err != 0)
819 	return_error(gs_error_ioerror);
820     return 0;
821 }
822 
823 /* ---------------- Image enumeration ---------------- */
824 
825 /* Initialize for enumerating an image. */
826 int
gdev_vector_begin_image(gx_device_vector * vdev,const gs_imager_state * pis,const gs_image_t * pim,gs_image_format_t format,const gs_int_rect * prect,const gx_drawing_color * pdcolor,const gx_clip_path * pcpath,gs_memory_t * mem,const gx_image_enum_procs_t * pprocs,gdev_vector_image_enum_t * pie)827 gdev_vector_begin_image(gx_device_vector * vdev,
828 			const gs_imager_state * pis, const gs_image_t * pim,
829 			gs_image_format_t format, const gs_int_rect * prect,
830 	      const gx_drawing_color * pdcolor, const gx_clip_path * pcpath,
831 		    gs_memory_t * mem, const gx_image_enum_procs_t * pprocs,
832 			gdev_vector_image_enum_t * pie)
833 {
834     const gs_color_space *pcs = pim->ColorSpace;
835     int num_components;
836     int bits_per_pixel;
837     int code;
838 
839     if (pim->ImageMask)
840 	bits_per_pixel = num_components = 1;
841     else
842 	num_components = gs_color_space_num_components(pcs),
843 	    bits_per_pixel = pim->BitsPerComponent;
844     code = gx_image_enum_common_init((gx_image_enum_common_t *) pie,
845 				     (const gs_data_image_t *)pim,
846 				     pprocs, (gx_device *) vdev,
847 				     num_components, format);
848     if (code < 0)
849 	return code;
850     pie->bits_per_pixel = bits_per_pixel * num_components /
851 	pie->num_planes;
852     pie->default_info = 0;
853     pie->bbox_info = 0;
854     if ((code = gdev_vector_update_log_op(vdev, pis->log_op)) < 0 ||
855 	(code = gdev_vector_update_clip_path(vdev, pcpath)) < 0 ||
856 	((pim->ImageMask ||
857 	  (pim->CombineWithColor && rop3_uses_T(pis->log_op))) &&
858 	 (code = gdev_vector_update_fill_color(vdev, pis, pdcolor)) < 0) ||
859 	(vdev->bbox_device &&
860 	 (code = (*dev_proc(vdev->bbox_device, begin_image))
861 	  ((gx_device *) vdev->bbox_device, pis, pim, format, prect,
862 	   pdcolor, pcpath, mem, &pie->bbox_info)) < 0)
863 	)
864 	return code;
865     pie->memory = mem;
866     if (prect)
867 	pie->width = prect->q.x - prect->p.x,
868 	    pie->height = prect->q.y - prect->p.y;
869     else
870 	pie->width = pim->Width, pie->height = pim->Height;
871     pie->bits_per_row = pie->width * pie->bits_per_pixel;
872     pie->y = 0;
873     return 0;
874 }
875 
876 /* End an image, optionally supplying any necessary blank padding rows. */
877 /* Return 0 if we used the default implementation, 1 if not. */
878 int
gdev_vector_end_image(gx_device_vector * vdev,gdev_vector_image_enum_t * pie,bool draw_last,gx_color_index pad)879 gdev_vector_end_image(gx_device_vector * vdev,
880 	 gdev_vector_image_enum_t * pie, bool draw_last, gx_color_index pad)
881 {
882     int code;
883 
884     if (pie->default_info) {
885 	code = gx_default_end_image((gx_device *) vdev, pie->default_info,
886 				    draw_last);
887 	if (code >= 0)
888 	    code = 0;
889     } else {			/* Fill out to the full image height. */
890 	if (pie->y < pie->height && pad != gx_no_color_index) {
891 	    uint bytes_per_row = (pie->bits_per_row + 7) >> 3;
892 	    byte *row = gs_alloc_bytes(pie->memory, bytes_per_row,
893 				       "gdev_vector_end_image(fill)");
894 
895 	    if (row == 0)
896 		return_error(gs_error_VMerror);
897 /****** FILL VALUE IS WRONG ******/
898 	    memset(row, (byte) pad, bytes_per_row);
899 	    for (; pie->y < pie->height; pie->y++)
900 		gx_image_data((gx_image_enum_common_t *) pie,
901 			      (const byte **)&row, 0,
902 			      bytes_per_row, 1);
903 	    gs_free_object(pie->memory, row,
904 			   "gdev_vector_end_image(fill)");
905 	}
906 	code = 1;
907     }
908     if (vdev->bbox_device) {
909 	int bcode = gx_image_end(pie->bbox_info, draw_last);
910 
911 	if (bcode < 0)
912 	    code = bcode;
913     }
914     gs_free_object(pie->memory, pie, "gdev_vector_end_image");
915     return code;
916 }
917 
918 /* ================ Device procedures ================ */
919 
920 #define vdev ((gx_device_vector *)dev)
921 
922 /* Get parameters. */
923 int
gdev_vector_get_params(gx_device * dev,gs_param_list * plist)924 gdev_vector_get_params(gx_device * dev, gs_param_list * plist)
925 {
926     int code = gx_default_get_params(dev, plist);
927     int ecode;
928     gs_param_string ofns;
929 
930     if (code < 0)
931 	return code;
932     ofns.data = (const byte *)vdev->fname,
933 	ofns.size = strlen(vdev->fname),
934 	ofns.persistent = false;
935     if ((ecode = param_write_string(plist, "OutputFile", &ofns)) < 0)
936 	return ecode;
937     return code;
938 }
939 
940 /* Put parameters. */
941 int
gdev_vector_put_params(gx_device * dev,gs_param_list * plist)942 gdev_vector_put_params(gx_device * dev, gs_param_list * plist)
943 {
944     int ecode = 0;
945     int code;
946     gs_param_name param_name;
947     gs_param_string ofns;
948 
949     switch (code = param_read_string(plist, (param_name = "OutputFile"), &ofns)) {
950 	case 0:
951 	    /*
952 	     * Vector devices typically write header information at the
953 	     * beginning of the file: changing the file name after writing
954 	     * any pages should be an error.
955 	     */
956 	    if (ofns.size > fname_size)
957 		ecode = gs_error_limitcheck;
958 	    else if (!bytes_compare(ofns.data, ofns.size,
959 				    (const byte *)vdev->fname,
960 				    strlen(vdev->fname))
961 		     ) {
962 		/* The new name is the same as the old name.  Do nothing. */
963 		ofns.data = 0;
964 		break;
965 	    } else if (dev->LockSafetyParams ||
966 	    		(dev->is_open && vdev->strm != 0 &&
967 		       stell(vdev->strm) != 0)
968 		       )
969 		ecode = (dev->LockSafetyParams) ? gs_error_invalidaccess :
970 				gs_error_rangecheck;
971 	    else
972 		break;
973 	    goto ofe;
974 	default:
975 	    ecode = code;
976 	  ofe:param_signal_error(plist, param_name, ecode);
977 	case 1:
978 	    ofns.data = 0;
979 	    break;
980     }
981 
982     if (ecode < 0)
983 	return ecode;
984     {
985 	bool open = dev->is_open;
986 
987 	/* Don't let gx_default_put_params close the device. */
988 	dev->is_open = false;
989 	code = gx_default_put_params(dev, plist);
990 	dev->is_open = open;
991     }
992     if (code < 0)
993 	return code;
994 
995     if (ofns.data != 0) {
996 	memcpy(vdev->fname, ofns.data, ofns.size);
997 	vdev->fname[ofns.size] = 0;
998 	if (vdev->file != 0) {
999 	    gx_device_bbox *bbdev = vdev->bbox_device;
1000 
1001 	    vdev->bbox_device = 0; /* don't let it be freed */
1002 	    code = gdev_vector_close_file(vdev);
1003 	    vdev->bbox_device = bbdev;
1004 	    if (code < 0)
1005 		return code;
1006 	    return gdev_vector_open_file_options(vdev, vdev->strmbuf_size,
1007 						 vdev->open_options);
1008 	}
1009     }
1010     return 0;
1011 }
1012 
1013 /* ---------------- Defaults ---------------- */
1014 
1015 int
gdev_vector_fill_rectangle(gx_device * dev,int x,int y,int w,int h,gx_color_index color)1016 gdev_vector_fill_rectangle(gx_device * dev, int x, int y, int w, int h,
1017 			   gx_color_index color)
1018 {
1019     gx_drawing_color dcolor;
1020 
1021     /* Ignore the initial fill with white. */
1022     if (!vdev->in_page && color == vdev->white)
1023 	return 0;
1024     /*
1025      * The original colorspace and client color are unknown so use
1026      * set_nonclient_dev_color instead of color_set_pure.
1027      */
1028     set_nonclient_dev_color(&dcolor, color);
1029     {
1030 	/* Make sure we aren't being clipped. */
1031 	int code = gdev_vector_update_clip_path(vdev, NULL);
1032 
1033 	if (code < 0)
1034 	    return code;
1035 	if ((code = update_fill(vdev, NULL, &dcolor, rop3_T)) < 0)
1036 	    return code;
1037     }
1038     if (vdev->bbox_device) {
1039 	int code = (*dev_proc(vdev->bbox_device, fill_rectangle))
1040 	((gx_device *) vdev->bbox_device, x, y, w, h, color);
1041 
1042 	if (code < 0)
1043 	    return code;
1044     }
1045     return (*vdev_proc(vdev, dorect)) (vdev, int2fixed(x), int2fixed(y),
1046 				       int2fixed(x + w), int2fixed(y + h),
1047 				       gx_path_type_fill);
1048 }
1049 
1050 int
gdev_vector_fill_path(gx_device * dev,const gs_imager_state * pis,gx_path * ppath,const gx_fill_params * params,const gx_device_color * pdevc,const gx_clip_path * pcpath)1051 gdev_vector_fill_path(gx_device * dev, const gs_imager_state * pis,
1052 		      gx_path * ppath, const gx_fill_params * params,
1053 		 const gx_device_color * pdevc, const gx_clip_path * pcpath)
1054 {
1055     int code;
1056 
1057     if ((code = gdev_vector_prepare_fill(vdev, pis, params, pdevc)) < 0 ||
1058 	(code = gdev_vector_update_clip_path(vdev, pcpath)) < 0 ||
1059 	(vdev->bbox_device &&
1060 	 (code = (*dev_proc(vdev->bbox_device, fill_path))
1061 	  ((gx_device *) vdev->bbox_device, pis, ppath, params,
1062 	   pdevc, pcpath)) < 0) ||
1063 	(code = (*vdev_proc(vdev, dopath))
1064 	 (vdev, ppath,
1065 	  (params->rule > 0 ? gx_path_type_even_odd :
1066 	   gx_path_type_winding_number) | gx_path_type_fill |
1067 	   vdev->fill_options,
1068 	 NULL)) < 0
1069 	)
1070 	return gx_default_fill_path(dev, pis, ppath, params, pdevc, pcpath);
1071     return code;
1072 }
1073 
1074 int
gdev_vector_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)1075 gdev_vector_stroke_path(gx_device * dev, const gs_imager_state * pis,
1076 			gx_path * ppath, const gx_stroke_params * params,
1077 	      const gx_drawing_color * pdcolor, const gx_clip_path * pcpath)
1078 {
1079     int code;
1080     double scale;
1081     int set_ctm;
1082     gs_matrix mat;
1083 
1084     if ((set_ctm = gdev_vector_stroke_scaling(vdev, pis, &scale, &mat)) != 0 ||
1085 	(code = gdev_vector_prepare_stroke(vdev, pis, params, pdcolor, scale)) < 0 ||
1086 	(code = gdev_vector_update_clip_path(vdev, pcpath)) < 0 ||
1087 	(vdev->bbox_device &&
1088 	 (code = (*dev_proc(vdev->bbox_device, stroke_path))
1089 	  ((gx_device *) vdev->bbox_device, pis, ppath, params,
1090 	   pdcolor, pcpath)) < 0) ||
1091 	(code = (*vdev_proc(vdev, dopath))
1092 	 (vdev, ppath, gx_path_type_stroke | vdev->stroke_options, NULL)) < 0
1093 	)
1094 	return gx_default_stroke_path(dev, pis, ppath, params, pdcolor, pcpath);
1095     return code;
1096 }
1097 
1098 int
gdev_vector_fill_trapezoid(gx_device * dev,const gs_fixed_edge * left,const gs_fixed_edge * right,fixed ybot,fixed ytop,bool swap_axes,const gx_device_color * pdevc,gs_logical_operation_t lop)1099 gdev_vector_fill_trapezoid(gx_device * dev, const gs_fixed_edge * left,
1100 	const gs_fixed_edge * right, fixed ybot, fixed ytop, bool swap_axes,
1101 		  const gx_device_color * pdevc, gs_logical_operation_t lop)
1102 {
1103     fixed xl = left->start.x;
1104     fixed wl = left->end.x - xl;
1105     fixed yl = left->start.y;
1106     fixed hl = left->end.y - yl;
1107     fixed xr = right->start.x;
1108     fixed wr = right->end.x - xr;
1109     fixed yr = right->start.y;
1110     fixed hr = right->end.y - yr;
1111     fixed x0l = xl + fixed_mult_quo(wl, ybot - yl, hl);
1112     fixed x1l = xl + fixed_mult_quo(wl, ytop - yl, hl);
1113     fixed x0r = xr + fixed_mult_quo(wr, ybot - yr, hr);
1114     fixed x1r = xr + fixed_mult_quo(wr, ytop - yr, hr);
1115 
1116 #define y0 ybot
1117 #define y1 ytop
1118     int code = update_fill(vdev, NULL, pdevc, lop);
1119     gs_fixed_point points[4];
1120 
1121     if (code < 0)
1122 	return gx_default_fill_trapezoid(dev, left, right, ybot, ytop,
1123 					 swap_axes, pdevc, lop);
1124     /* Make sure we aren't being clipped. */
1125     code = gdev_vector_update_clip_path(vdev, NULL);
1126     if (code < 0)
1127 	return code;
1128     if (swap_axes)
1129 	points[0].y = x0l, points[1].y = x0r,
1130 	    points[0].x = points[1].x = y0,
1131 	    points[2].y = x1r, points[3].y = x1l,
1132 	    points[2].x = points[3].x = y1;
1133     else
1134 	points[0].x = x0l, points[1].x = x0r,
1135 	    points[0].y = points[1].y = y0,
1136 	    points[2].x = x1r, points[3].x = x1l,
1137 	    points[2].y = points[3].y = y1;
1138 #undef y0
1139 #undef y1
1140     if (vdev->bbox_device) {
1141 	int code = (*dev_proc(vdev->bbox_device, fill_trapezoid))
1142 	((gx_device *) vdev->bbox_device, left, right, ybot, ytop,
1143 	 swap_axes, pdevc, lop);
1144 
1145 	if (code < 0)
1146 	    return code;
1147     }
1148     return gdev_vector_write_polygon(vdev, points, 4, true,
1149 				     gx_path_type_fill);
1150 }
1151 
1152 int
gdev_vector_fill_parallelogram(gx_device * dev,fixed px,fixed py,fixed ax,fixed ay,fixed bx,fixed by,const gx_device_color * pdevc,gs_logical_operation_t lop)1153 gdev_vector_fill_parallelogram(gx_device * dev,
1154 		 fixed px, fixed py, fixed ax, fixed ay, fixed bx, fixed by,
1155 		  const gx_device_color * pdevc, gs_logical_operation_t lop)
1156 {
1157     fixed pax = px + ax, pay = py + ay;
1158     int code = update_fill(vdev, NULL, pdevc, lop);
1159     gs_fixed_point points[4];
1160 
1161     if (code < 0)
1162 	return gx_default_fill_parallelogram(dev, px, py, ax, ay, bx, by,
1163 					     pdevc, lop);
1164     /* Make sure we aren't being clipped. */
1165     code = gdev_vector_update_clip_path(vdev, NULL);
1166     if (code < 0)
1167 	return code;
1168     if (vdev->bbox_device) {
1169 	code = (*dev_proc(vdev->bbox_device, fill_parallelogram))
1170 	    ((gx_device *) vdev->bbox_device, px, py, ax, ay, bx, by,
1171 	     pdevc, lop);
1172 	if (code < 0)
1173 	    return code;
1174     }
1175     points[0].x = px, points[0].y = py;
1176     points[1].x = pax, points[1].y = pay;
1177     points[2].x = pax + bx, points[2].y = pay + by;
1178     points[3].x = px + bx, points[3].y = py + by;
1179     return gdev_vector_write_polygon(vdev, points, 4, true,
1180 				     gx_path_type_fill);
1181 }
1182 
1183 int
gdev_vector_fill_triangle(gx_device * dev,fixed px,fixed py,fixed ax,fixed ay,fixed bx,fixed by,const gx_device_color * pdevc,gs_logical_operation_t lop)1184 gdev_vector_fill_triangle(gx_device * dev,
1185 		 fixed px, fixed py, fixed ax, fixed ay, fixed bx, fixed by,
1186 		  const gx_device_color * pdevc, gs_logical_operation_t lop)
1187 {
1188     int code = update_fill(vdev, NULL, pdevc, lop);
1189     gs_fixed_point points[3];
1190 
1191     if (code < 0)
1192 	return gx_default_fill_triangle(dev, px, py, ax, ay, bx, by,
1193 					pdevc, lop);
1194     /* Make sure we aren't being clipped. */
1195     code = gdev_vector_update_clip_path(vdev, NULL);
1196     if (code < 0)
1197 	return code;
1198     if (vdev->bbox_device) {
1199 	code = (*dev_proc(vdev->bbox_device, fill_triangle))
1200 	    ((gx_device *) vdev->bbox_device, px, py, ax, ay, bx, by,
1201 	     pdevc, lop);
1202 	if (code < 0)
1203 	    return code;
1204     }
1205     points[0].x = px, points[0].y = py;
1206     points[1].x = px + ax, points[1].y = py + ay;
1207     points[2].x = px + bx, points[2].y = py + by;
1208     return gdev_vector_write_polygon(vdev, points, 3, true,
1209 				     gx_path_type_fill);
1210 }
1211 
1212 #undef vdev
1213