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