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