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