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