home *** CD-ROM | disk | FTP | other *** search
/ AmigActive 21 / AACD 21.iso / AACD / Utilities / Ghostscript / src / gdevvec.c < prev    next >
Encoding:
C/C++ Source or Header  |  2001-01-01  |  33.9 KB  |  1,172 lines

  1. /* Copyright (C) 1997, 2000 Aladdin Enterprises.  All rights reserved.
  2.   
  3.   This file is part of AFPL Ghostscript.
  4.   
  5.   AFPL Ghostscript is distributed with NO WARRANTY OF ANY KIND.  No author or
  6.   distributor accepts any responsibility for the consequences of using it, or
  7.   for whether it serves any particular purpose or works at all, unless he or
  8.   she says so in writing.  Refer to the Aladdin Free Public License (the
  9.   "License") for full details.
  10.   
  11.   Every copy of AFPL Ghostscript must include a copy of the License, normally
  12.   in a plain ASCII text file named PUBLIC.  The License grants you the right
  13.   to copy, modify and redistribute AFPL Ghostscript, but only under certain
  14.   conditions described in the License.  Among other things, the License
  15.   requires that the copyright notice and this notice be preserved on all
  16.   copies.
  17. */
  18.  
  19. /*$Id: gdevvec.c,v 1.7.2.2 2000/11/14 17:20:05 rayjj 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
  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
  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
  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
  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
  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
  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
  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
  265. gdev_vector_open_file_bbox(gx_device_vector * vdev, uint strmbuf_size,
  266.                bool bbox)
  267. {                /* Open the file as positionable if possible. */
  268.     int code = gx_device_open_output_file((gx_device *) vdev, vdev->fname,
  269.                       true, true, &vdev->file);
  270.  
  271.     if (code < 0)
  272.     return code;
  273.     if ((vdev->strmbuf = gs_alloc_bytes(vdev->v_memory, strmbuf_size,
  274.                     "vector_open(strmbuf)")) == 0 ||
  275.     (vdev->strm = s_alloc(vdev->v_memory,
  276.                   "vector_open(strm)")) == 0 ||
  277.     (bbox &&
  278.      (vdev->bbox_device =
  279.       gs_alloc_struct_immovable(vdev->v_memory,
  280.                     gx_device_bbox, &st_device_bbox,
  281.                     "vector_open(bbox_device)")) == 0)
  282.     ) {
  283.     if (vdev->bbox_device)
  284.         gs_free_object(vdev->v_memory, vdev->bbox_device,
  285.                "vector_open(bbox_device)");
  286.     vdev->bbox_device = 0;
  287.     if (vdev->strm)
  288.         gs_free_object(vdev->v_memory, vdev->strm,
  289.                "vector_open(strm)");
  290.     vdev->strm = 0;
  291.     if (vdev->strmbuf)
  292.         gs_free_object(vdev->v_memory, vdev->strmbuf,
  293.                "vector_open(strmbuf)");
  294.     vdev->strmbuf = 0;
  295.     fclose(vdev->file);
  296.     vdev->file = 0;
  297.     return_error(gs_error_VMerror);
  298.     }
  299.     vdev->strmbuf_size = strmbuf_size;
  300.     swrite_file(vdev->strm, vdev->file, vdev->strmbuf, strmbuf_size);
  301.     /*
  302.      * We don't want finalization to close the file, but we do want it
  303.      * to flush the stream buffer.
  304.      */
  305.     vdev->strm->procs.close = vdev->strm->procs.flush;
  306.     if (vdev->bbox_device) {
  307.     gx_device_bbox_init(vdev->bbox_device, NULL);
  308.     gx_device_set_resolution((gx_device *) vdev->bbox_device,
  309.                  vdev->HWResolution[0],
  310.                  vdev->HWResolution[1]);
  311.     /* Do the right thing about upright vs. inverted. */
  312.     /* (This is dangerous in general, since the procedure */
  313.     /* might reference non-standard elements.) */
  314.     set_dev_proc(vdev->bbox_device, get_initial_matrix,
  315.              dev_proc(vdev, get_initial_matrix));
  316.     (*dev_proc(vdev->bbox_device, open_device))
  317.         ((gx_device *) vdev->bbox_device);
  318.     }
  319.     return 0;
  320. }
  321.  
  322. /* Get the current stream, calling beginpage if in_page is false. */
  323. stream *
  324. gdev_vector_stream(gx_device_vector * vdev)
  325. {
  326.     if (!vdev->in_page) {
  327.     (*vdev_proc(vdev, beginpage)) (vdev);
  328.     vdev->in_page = true;
  329.     }
  330.     return vdev->strm;
  331. }
  332.  
  333. /* Compare two drawing colors. */
  334. /* Right now we don't attempt to handle non-pure colors. */
  335. private bool
  336. drawing_color_eq(const gx_drawing_color * pdc1, const gx_drawing_color * pdc2)
  337. {
  338.     return (gx_dc_is_pure(pdc1) ?
  339.         gx_dc_is_pure(pdc2) &&
  340.         gx_dc_pure_color(pdc1) == gx_dc_pure_color(pdc2) :
  341.         gx_dc_is_null(pdc1) ?
  342.         gx_dc_is_null(pdc2) :
  343.         false);
  344. }
  345.  
  346. /* Update the logical operation. */
  347. int
  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 the fill color. */
  363. int
  364. gdev_vector_update_fill_color(gx_device_vector * vdev,
  365.                   const gx_drawing_color * pdcolor)
  366. {
  367.     if (!drawing_color_eq(pdcolor, &vdev->fill_color)) {
  368.     int code = (*vdev_proc(vdev, setfillcolor)) (vdev, pdcolor);
  369.  
  370.     if (code < 0)
  371.         return code;
  372.     vdev->fill_color = *pdcolor;
  373.     }
  374.     return 0;
  375. }
  376.  
  377. /* Update the state for filling a region. */
  378. private int
  379. update_fill(gx_device_vector * vdev, const gx_drawing_color * pdcolor,
  380.         gs_logical_operation_t lop)
  381. {
  382.     int code = gdev_vector_update_fill_color(vdev, pdcolor);
  383.  
  384.     if (code < 0)
  385.     return code;
  386.     return gdev_vector_update_log_op(vdev, lop);
  387. }
  388.  
  389. /* Bring state up to date for filling. */
  390. int
  391. gdev_vector_prepare_fill(gx_device_vector * vdev, const gs_imager_state * pis,
  392.         const gx_fill_params * params, const gx_drawing_color * pdcolor)
  393. {
  394.     if (params->flatness != vdev->state.flatness) {
  395.     int code = (*vdev_proc(vdev, setflat)) (vdev, params->flatness);
  396.  
  397.     if (code < 0)
  398.         return code;
  399.     vdev->state.flatness = params->flatness;
  400.     }
  401.     return update_fill(vdev, pdcolor, pis->log_op);
  402. }
  403.  
  404. /* Compare two dash patterns. */
  405. private bool
  406. dash_pattern_eq(const float *stored, const gx_dash_params * set, floatp scale)
  407. {
  408.     int i;
  409.  
  410.     for (i = 0; i < set->pattern_size; ++i)
  411.     if (stored[i] != (float)(set->pattern[i] * scale))
  412.         return false;
  413.     return true;
  414. }
  415.  
  416. /* Bring state up to date for stroking. */
  417. int
  418. gdev_vector_prepare_stroke(gx_device_vector * vdev, const gs_imager_state * pis,
  419.       const gx_stroke_params * params, const gx_drawing_color * pdcolor,
  420.                floatp scale)
  421. {
  422.     int pattern_size = pis->line_params.dash.pattern_size;
  423.     float dash_offset = pis->line_params.dash.offset * scale;
  424.     float half_width = pis->line_params.half_width * scale;
  425.  
  426.     if (pattern_size > max_dash)
  427.     return_error(gs_error_limitcheck);
  428.     if (dash_offset != vdev->state.line_params.dash.offset ||
  429.     pattern_size != vdev->state.line_params.dash.pattern_size ||
  430.     (pattern_size != 0 &&
  431.      !dash_pattern_eq(vdev->dash_pattern, &pis->line_params.dash,
  432.               scale))
  433.     ) {
  434.     float pattern[max_dash];
  435.     int i, code;
  436.  
  437.     for (i = 0; i < pattern_size; ++i)
  438.         pattern[i] = pis->line_params.dash.pattern[i] * scale;
  439.     code = (*vdev_proc(vdev, setdash))
  440.         (vdev, pattern, pattern_size, dash_offset);
  441.     if (code < 0)
  442.         return code;
  443.     memcpy(vdev->dash_pattern, pattern, pattern_size * sizeof(float));
  444.  
  445.     vdev->state.line_params.dash.pattern_size = pattern_size;
  446.     vdev->state.line_params.dash.offset = dash_offset;
  447.     }
  448.     if (params->flatness != vdev->state.flatness) {
  449.     int code = (*vdev_proc(vdev, setflat)) (vdev, params->flatness);
  450.  
  451.     if (code < 0)
  452.         return code;
  453.     vdev->state.flatness = params->flatness;
  454.     }
  455.     if (half_width != vdev->state.line_params.half_width) {
  456.     int code = (*vdev_proc(vdev, setlinewidth))
  457.         (vdev, half_width * 2);
  458.  
  459.     if (code < 0)
  460.         return code;
  461.     vdev->state.line_params.half_width = half_width;
  462.     }
  463.     if (pis->line_params.miter_limit != vdev->state.line_params.miter_limit) {
  464.     int code = (*vdev_proc(vdev, setmiterlimit))
  465.         (vdev, pis->line_params.miter_limit);
  466.  
  467.     if (code < 0)
  468.         return code;
  469.     gx_set_miter_limit(&vdev->state.line_params,
  470.                pis->line_params.miter_limit);
  471.     }
  472.     if (pis->line_params.cap != vdev->state.line_params.cap) {
  473.     int code = (*vdev_proc(vdev, setlinecap))
  474.         (vdev, pis->line_params.cap);
  475.  
  476.     if (code < 0)
  477.         return code;
  478.     vdev->state.line_params.cap = pis->line_params.cap;
  479.     }
  480.     if (pis->line_params.join != vdev->state.line_params.join) {
  481.     int code = (*vdev_proc(vdev, setlinejoin))
  482.         (vdev, pis->line_params.join);
  483.  
  484.     if (code < 0)
  485.         return code;
  486.     vdev->state.line_params.join = pis->line_params.join;
  487.     } {
  488.     int code = gdev_vector_update_log_op(vdev, pis->log_op);
  489.  
  490.     if (code < 0)
  491.         return code;
  492.     }
  493.     if (!drawing_color_eq(pdcolor, &vdev->stroke_color)) {
  494.     int code = (*vdev_proc(vdev, setstrokecolor)) (vdev, pdcolor);
  495.  
  496.     if (code < 0)
  497.         return code;
  498.     vdev->stroke_color = *pdcolor;
  499.     }
  500.     return 0;
  501. }
  502.  
  503. /*
  504.  * Compute the scale or transformation matrix for transforming the line
  505.  * width and dash pattern for a stroke operation.  Return 0 if scaling,
  506.  * 1 if a full matrix is needed.
  507.  */
  508. int
  509. gdev_vector_stroke_scaling(const gx_device_vector *vdev,
  510.                const gs_imager_state *pis,
  511.                double *pscale, gs_matrix *pmat)
  512. {
  513.     bool set_ctm = true;
  514.     double scale = 1;
  515.  
  516.     /*
  517.      * If the CTM is not uniform, stroke width depends on angle.
  518.      * We'd like to avoid resetting the CTM, so we check for uniform
  519.      * CTMs explicitly.  Note that in PDF, unlike PostScript, it is
  520.      * the CTM at the time of the stroke operation, not the CTM at
  521.      * the time the path was constructed, that is used for transforming
  522.      * the points of the path; so if we have to reset the CTM, we must
  523.      * do it before constructing the path, and inverse-transform all
  524.      * the coordinates.
  525.      */
  526.     if (is_xxyy(&pis->ctm)) {
  527.     scale = fabs(pis->ctm.xx);
  528.     set_ctm = fabs(pis->ctm.yy) != scale;
  529.     } else if (is_xyyx(&pis->ctm)) {
  530.     scale = fabs(pis->ctm.xy);
  531.     set_ctm = fabs(pis->ctm.yx) != scale;
  532.     } else if ((pis->ctm.xx == pis->ctm.yy && pis->ctm.xy == -pis->ctm.yx) ||
  533.            (pis->ctm.xx == -pis->ctm.yy && pis->ctm.xy == pis->ctm.yx)
  534.     ) {
  535.     scale = hypot(pis->ctm.xx, pis->ctm.xy);
  536.     set_ctm = false;
  537.     }
  538.     if (set_ctm) {
  539.     /*
  540.      * Adobe Acrobat Reader can't handle user coordinates larger than
  541.      * 32K.  If we scale the matrix down too far, the coordinates will
  542.      * get too big: don't allow this to happen.  (This does no harm
  543.      * for other output formats.)
  544.      */
  545.     double
  546.         mxx = pis->ctm.xx / vdev->scale.x,
  547.         mxy = pis->ctm.xy / vdev->scale.y,
  548.         myx = pis->ctm.yx / vdev->scale.x,
  549.         myy = pis->ctm.yy / vdev->scale.y;
  550.  
  551.     scale = 0.5 * (fabs(mxx) + fabs(mxy) + fabs(myx) + fabs(myy));
  552.     pmat->xx = mxx / scale, pmat->xy = mxy / scale;
  553.     pmat->yx = myx / scale, pmat->yy = myy / scale;
  554.     pmat->tx = pmat->ty = 0;
  555.     }
  556.     *pscale = scale;
  557.     return (int)set_ctm;
  558. }
  559.  
  560. /* Initialize for writing a path using the default implementation. */
  561. void
  562. gdev_vector_dopath_init(gdev_vector_dopath_state_t *state,
  563.             gx_device_vector *vdev, gx_path_type_t type,
  564.             const gs_matrix *pmat)
  565. {
  566.     state->vdev = vdev;
  567.     state->type = type;
  568.     if (pmat) {
  569.     state->scale_mat = *pmat;
  570.     /*
  571.      * The path element writing procedures all divide the coordinates
  572.      * by the scale, so we must compensate for that here.
  573.      */
  574.     gs_matrix_scale(&state->scale_mat, 1.0 / vdev->scale.x,
  575.             1.0 / vdev->scale.y, &state->scale_mat);
  576.     } else {
  577.     gs_make_scaling(vdev->scale.x, vdev->scale.y, &state->scale_mat);
  578.     }
  579.     state->first = true;
  580. }
  581.  
  582. /*
  583.  * Put a segment of an enumerated path on the output file.
  584.  * pe_op is assumed to be valid and non-zero.
  585.  */
  586. int
  587. gdev_vector_dopath_segment(gdev_vector_dopath_state_t *state, int pe_op,
  588.                gs_fixed_point vs[3])
  589. {
  590.     gx_device_vector *vdev = state->vdev;
  591.     const gs_matrix *const pmat = &state->scale_mat;
  592.     gs_point vp[3];
  593.     int code;
  594.  
  595.     switch (pe_op) {
  596.     case gs_pe_moveto:
  597.         gs_point_transform_inverse(fixed2float(vs[0].x),
  598.                        fixed2float(vs[0].y), pmat, &vp[0]);
  599.         if (state->first)
  600.         state->start = vp[0], state->first = false;
  601.         code = vdev_proc(vdev, moveto)
  602.         (vdev, state->prev.x, state->prev.y, vp[0].x, vp[0].y,
  603.          state->type);
  604.         state->prev = vp[0];
  605.         break;
  606.     case gs_pe_lineto:
  607.         gs_point_transform_inverse(fixed2float(vs[0].x),
  608.                        fixed2float(vs[0].y), pmat, &vp[0]);
  609.         code = vdev_proc(vdev, lineto)
  610.         (vdev, state->prev.x, state->prev.y, vp[0].x, vp[0].y,
  611.          state->type);
  612.         state->prev = vp[0];
  613.         break;
  614.     case gs_pe_curveto:
  615.         gs_point_transform_inverse(fixed2float(vs[0].x),
  616.                        fixed2float(vs[0].y), pmat, &vp[0]);
  617.         gs_point_transform_inverse(fixed2float(vs[1].x),
  618.                        fixed2float(vs[1].y), pmat, &vp[1]);
  619.         gs_point_transform_inverse(fixed2float(vs[2].x),
  620.                        fixed2float(vs[2].y), pmat, &vp[2]);
  621.         code = vdev_proc(vdev, curveto)
  622.         (vdev, state->prev.x, state->prev.y, vp[0].x, vp[0].y,
  623.          vp[1].x, vp[1].y, vp[2].x, vp[2].y, state->type);
  624.         state->prev = vp[2];
  625.         break;
  626.     case gs_pe_closepath:
  627.         code = vdev_proc(vdev, closepath)
  628.         (vdev, state->prev.x, state->prev.y, state->start.x,
  629.          state->start.y, state->type);
  630.         state->prev = state->start;
  631.         break;
  632.     default:        /* can't happen */
  633.         return -1;
  634.     }
  635.     return code;
  636. }
  637.  
  638. /* Write a polygon as part of a path. */
  639. /* May call beginpath, moveto, lineto, closepath, endpath. */
  640. int
  641. gdev_vector_write_polygon(gx_device_vector * vdev, const gs_fixed_point * points,
  642.               uint count, bool close, gx_path_type_t type)
  643. {
  644.     int code = 0;
  645.  
  646.     if (type != gx_path_type_none &&
  647.     (code = (*vdev_proc(vdev, beginpath)) (vdev, type)) < 0
  648.     )
  649.     return code;
  650.     if (count > 0) {
  651.     double x = fixed2float(points[0].x) / vdev->scale.x, y = fixed2float(points[0].y) / vdev->scale.y;
  652.     double x_start = x, y_start = y, x_prev, y_prev;
  653.     uint i;
  654.  
  655.     code = (*vdev_proc(vdev, moveto))
  656.         (vdev, 0.0, 0.0, x, y, type);
  657.     if (code >= 0)
  658.         for (i = 1; i < count && code >= 0; ++i) {
  659.         x_prev = x, y_prev = y;
  660.         code = (*vdev_proc(vdev, lineto))
  661.             (vdev, x_prev, y_prev,
  662.              (x = fixed2float(points[i].x) / vdev->scale.x),
  663.              (y = fixed2float(points[i].y) / vdev->scale.y),
  664.              type);
  665.         }
  666.     if (code >= 0 && close)
  667.         code = (*vdev_proc(vdev, closepath))
  668.         (vdev, x, y, x_start, y_start, type);
  669.     }
  670.     return (code >= 0 && type != gx_path_type_none ?
  671.         (*vdev_proc(vdev, endpath)) (vdev, type) : code);
  672. }
  673.  
  674. /* Write a rectangle as part of a path. */
  675. /* May call moveto, lineto, closepath. */
  676. int
  677. gdev_vector_write_rectangle(gx_device_vector * vdev, fixed x0, fixed y0,
  678.           fixed x1, fixed y1, bool close, gx_rect_direction_t direction)
  679. {
  680.     gs_fixed_point points[4];
  681.  
  682.     points[0].x = x0, points[0].y = y0;
  683.     points[2].x = x1, points[2].y = y1;
  684.     if (direction == gx_rect_x_first)
  685.     points[1].x = x1, points[1].y = y0,
  686.         points[3].x = x0, points[3].y = y1;
  687.     else
  688.     points[1].x = x0, points[1].y = y1,
  689.         points[3].x = x1, points[3].y = y0;
  690.     return gdev_vector_write_polygon(vdev, points, 4, close,
  691.                      gx_path_type_none);
  692. }
  693.  
  694. /* Write a clipping path by calling the path procedures. */
  695. int
  696. gdev_vector_write_clip_path(gx_device_vector * vdev,
  697.                 const gx_clip_path * pcpath)
  698. {
  699.     const gx_clip_rect *prect;
  700.     gx_clip_rect page_rect;
  701.     int code;
  702.  
  703.     if (pcpath == 0) {
  704.     /* There's no special provision for initclip. */
  705.     /* Write a rectangle that covers the entire page. */
  706.     page_rect.xmin = page_rect.ymin = 0;
  707.     page_rect.xmax = vdev->width;
  708.     page_rect.ymax = vdev->height;
  709.     page_rect.next = 0;
  710.     prect = &page_rect;
  711.     } else if (pcpath->path_valid) {
  712.     return (*vdev_proc(vdev, dopath))
  713.         (vdev, &pcpath->path,
  714.          (pcpath->rule <= 0 ?
  715.           gx_path_type_clip | gx_path_type_winding_number :
  716.           gx_path_type_clip | gx_path_type_even_odd),
  717.          NULL);
  718.     } else {
  719.     const gx_clip_list *list = gx_cpath_list(pcpath);
  720.  
  721.     prect = list->head;
  722.     if (prect == 0)
  723.         prect = &list->single;
  724.     }
  725.     /* Write out the rectangles. */
  726.     code = (*vdev_proc(vdev, beginpath)) (vdev, gx_path_type_clip);
  727.     for (; code >= 0 && prect != 0; prect = prect->next)
  728.     if (prect->xmax > prect->xmin && prect->ymax > prect->ymin)
  729.         code = gdev_vector_write_rectangle
  730.         (vdev, int2fixed(prect->xmin), int2fixed(prect->ymin),
  731.          int2fixed(prect->xmax), int2fixed(prect->ymax),
  732.          false, gx_rect_x_first);
  733.     if (code >= 0)
  734.     code = (*vdev_proc(vdev, endpath)) (vdev, gx_path_type_clip);
  735.     return code;
  736. }
  737.  
  738. /* Update the clipping path if needed. */
  739. int
  740. gdev_vector_update_clip_path(gx_device_vector * vdev,
  741.                  const gx_clip_path * pcpath)
  742. {
  743.     if (pcpath) {
  744.     if (pcpath->id != vdev->clip_path_id) {
  745.         int code = gdev_vector_write_clip_path(vdev, pcpath);
  746.  
  747.         if (code < 0)
  748.         return code;
  749.         vdev->clip_path_id = pcpath->id;
  750.     }
  751.     } else {
  752.     if (vdev->clip_path_id != vdev->no_clip_path_id) {
  753.         int code = gdev_vector_write_clip_path(vdev, NULL);
  754.  
  755.         if (code < 0)
  756.         return code;
  757.         vdev->clip_path_id = vdev->no_clip_path_id;
  758.     }
  759.     }
  760.     return 0;
  761. }
  762.  
  763. /* Close the output file and stream. */
  764. int
  765. gdev_vector_close_file(gx_device_vector * vdev)
  766. {
  767.     FILE *f = vdev->file;
  768.     int err;
  769.  
  770.     gs_free_object(vdev->v_memory, vdev->bbox_device,
  771.            "vector_close(bbox_device)");
  772.     vdev->bbox_device = 0;
  773.     sclose(vdev->strm);
  774.     gs_free_object(vdev->v_memory, vdev->strm, "vector_close(strm)");
  775.     vdev->strm = 0;
  776.     gs_free_object(vdev->v_memory, vdev->strmbuf, "vector_close(strmbuf)");
  777.     vdev->strmbuf = 0;
  778.     vdev->file = 0;
  779.     err = ferror(f);
  780.     /* We prevented sclose from closing the file. */
  781.     if (fclose(f) != 0 || err != 0)
  782.     return_error(gs_error_ioerror);
  783.     return 0;
  784. }
  785.  
  786. /* ---------------- Image enumeration ---------------- */
  787.  
  788. /* Initialize for enumerating an image. */
  789. int
  790. gdev_vector_begin_image(gx_device_vector * vdev,
  791.             const gs_imager_state * pis, const gs_image_t * pim,
  792.             gs_image_format_t format, const gs_int_rect * prect,
  793.           const gx_drawing_color * pdcolor, const gx_clip_path * pcpath,
  794.             gs_memory_t * mem, const gx_image_enum_procs_t * pprocs,
  795.             gdev_vector_image_enum_t * pie)
  796. {
  797.     const gs_color_space *pcs = pim->ColorSpace;
  798.     int num_components;
  799.     int bits_per_pixel;
  800.     int code;
  801.  
  802.     if (pim->ImageMask)
  803.     bits_per_pixel = num_components = 1;
  804.     else
  805.     num_components = gs_color_space_num_components(pcs),
  806.         bits_per_pixel = pim->BitsPerComponent;
  807.     code = gx_image_enum_common_init((gx_image_enum_common_t *) pie,
  808.                      (const gs_data_image_t *)pim,
  809.                      pprocs, (gx_device *) vdev,
  810.                      num_components, format);
  811.     if (code < 0)
  812.     return code;
  813.     pie->bits_per_pixel = bits_per_pixel * num_components /
  814.     pie->num_planes;
  815.     pie->default_info = 0;
  816.     pie->bbox_info = 0;
  817.     if ((code = gdev_vector_update_log_op(vdev, pis->log_op)) < 0 ||
  818.     (code = gdev_vector_update_clip_path(vdev, pcpath)) < 0 ||
  819.     ((pim->ImageMask ||
  820.       (pim->CombineWithColor && rop3_uses_T(pis->log_op))) &&
  821.      (code = gdev_vector_update_fill_color(vdev, pdcolor)) < 0) ||
  822.     (vdev->bbox_device &&
  823.      (code = (*dev_proc(vdev->bbox_device, begin_image))
  824.       ((gx_device *) vdev->bbox_device, pis, pim, format, prect,
  825.        pdcolor, pcpath, mem, &pie->bbox_info)) < 0)
  826.     )
  827.     return code;
  828.     pie->memory = mem;
  829.     if (prect)
  830.     pie->width = prect->q.x - prect->p.x,
  831.         pie->height = prect->q.y - prect->p.y;
  832.     else
  833.     pie->width = pim->Width, pie->height = pim->Height;
  834.     pie->bits_per_row = pie->width * pie->bits_per_pixel;
  835.     pie->y = 0;
  836.     return 0;
  837. }
  838.  
  839. /* End an image, optionally supplying any necessary blank padding rows. */
  840. /* Return 0 if we used the default implementation, 1 if not. */
  841. int
  842. gdev_vector_end_image(gx_device_vector * vdev,
  843.      gdev_vector_image_enum_t * pie, bool draw_last, gx_color_index pad)
  844. {
  845.     int code;
  846.  
  847.     if (pie->default_info) {
  848.     code = gx_default_end_image((gx_device *) vdev, pie->default_info,
  849.                     draw_last);
  850.     if (code >= 0)
  851.         code = 0;
  852.     } else {            /* Fill out to the full image height. */
  853.     if (pie->y < pie->height && pad != gx_no_color_index) {
  854.         uint bytes_per_row = (pie->bits_per_row + 7) >> 3;
  855.         byte *row = gs_alloc_bytes(pie->memory, bytes_per_row,
  856.                        "gdev_vector_end_image(fill)");
  857.  
  858.         if (row == 0)
  859.         return_error(gs_error_VMerror);
  860. /****** FILL VALUE IS WRONG ******/
  861.         memset(row, (byte) pad, bytes_per_row);
  862.         for (; pie->y < pie->height; pie->y++)
  863.         gx_image_data((gx_image_enum_common_t *) pie,
  864.                   (const byte **)&row, 0,
  865.                   bytes_per_row, 1);
  866.         gs_free_object(pie->memory, row,
  867.                "gdev_vector_end_image(fill)");
  868.     }
  869.     code = 1;
  870.     }
  871.     if (vdev->bbox_device) {
  872.     int bcode = gx_image_end(pie->bbox_info, draw_last);
  873.  
  874.     if (bcode < 0)
  875.         code = bcode;
  876.     }
  877.     gs_free_object(pie->memory, pie, "gdev_vector_end_image");
  878.     return code;
  879. }
  880.  
  881. /* ================ Device procedures ================ */
  882.  
  883. #define vdev ((gx_device_vector *)dev)
  884.  
  885. /* Get parameters. */
  886. int
  887. gdev_vector_get_params(gx_device * dev, gs_param_list * plist)
  888. {
  889.     int code = gx_default_get_params(dev, plist);
  890.     int ecode;
  891.     gs_param_string ofns;
  892.  
  893.     if (code < 0)
  894.     return code;
  895.     ofns.data = (const byte *)vdev->fname,
  896.     ofns.size = strlen(vdev->fname),
  897.     ofns.persistent = false;
  898.     if ((ecode = param_write_string(plist, "OutputFile", &ofns)) < 0)
  899.     return ecode;
  900.     return code;
  901. }
  902.  
  903. /* Put parameters. */
  904. int
  905. gdev_vector_put_params(gx_device * dev, gs_param_list * plist)
  906. {
  907.     int ecode = 0;
  908.     int code;
  909.     gs_param_name param_name;
  910.     gs_param_string ofns;
  911.  
  912.     switch (code = param_read_string(plist, (param_name = "OutputFile"), &ofns)) {
  913.     case 0:
  914.         /*
  915.          * Vector devices typically write header information at the
  916.          * beginning of the file: changing the file name after writing
  917.          * any pages should be an error.
  918.          */
  919.         if (ofns.size > fname_size)
  920.         ecode = gs_error_limitcheck;
  921.         else if (!bytes_compare(ofns.data, ofns.size,
  922.                     (const byte *)vdev->fname,
  923.                     strlen(vdev->fname))
  924.              ) {
  925.         /* The new name is the same as the old name.  Do nothing. */
  926.         ofns.data = 0;
  927.         break;
  928.         } else if (dev->is_open && vdev->strm != 0 &&
  929.                stell(vdev->strm) != 0
  930.                )
  931.         ecode = gs_error_rangecheck;
  932.         else
  933.         break;
  934.         goto ofe;
  935.     default:
  936.         ecode = code;
  937.       ofe:param_signal_error(plist, param_name, ecode);
  938.     case 1:
  939.         ofns.data = 0;
  940.         break;
  941.     }
  942.  
  943.     if (ecode < 0)
  944.     return ecode;
  945.     {
  946.     bool open = dev->is_open;
  947.  
  948.     /* Don't let gx_default_put_params close the device. */
  949.     dev->is_open = false;
  950.     code = gx_default_put_params(dev, plist);
  951.     dev->is_open = open;
  952.     }
  953.     if (code < 0)
  954.     return code;
  955.  
  956.     if (ofns.data != 0) {
  957.     memcpy(vdev->fname, ofns.data, ofns.size);
  958.     vdev->fname[ofns.size] = 0;
  959.     if (vdev->file != 0) {
  960.         gx_device_bbox *bbdev = vdev->bbox_device;
  961.  
  962.         vdev->bbox_device = 0; /* don't let it be freed */
  963.         code = gdev_vector_close_file(vdev);
  964.         vdev->bbox_device = bbdev;
  965.         if (code < 0)
  966.         return code;
  967.         return gdev_vector_open_file_bbox(vdev, vdev->strmbuf_size,
  968.                           bbdev != 0);
  969.     }
  970.     }
  971.     gdev_vector_load_cache(vdev);    /* in case color mapping changed */
  972.     return 0;
  973. }
  974.  
  975. /* ---------------- Defaults ---------------- */
  976.  
  977. int
  978. gdev_vector_fill_rectangle(gx_device * dev, int x, int y, int w, int h,
  979.                gx_color_index color)
  980. {
  981.     gx_drawing_color dcolor;
  982.  
  983.     /* Ignore the initial fill with white. */
  984.     if (!vdev->in_page && color == vdev->white)
  985.     return 0;
  986.     color_set_pure(&dcolor, color);
  987.     {
  988.     int code = update_fill(vdev, &dcolor, rop3_T);
  989.  
  990.     if (code < 0)
  991.         return code;
  992.     /* Make sure we aren't being clipped. */
  993.     code = gdev_vector_update_clip_path(vdev, NULL);
  994.     if (code < 0)
  995.         return code;
  996.     }
  997.     if (vdev->bbox_device) {
  998.     int code = (*dev_proc(vdev->bbox_device, fill_rectangle))
  999.     ((gx_device *) vdev->bbox_device, x, y, w, h, color);
  1000.  
  1001.     if (code < 0)
  1002.         return code;
  1003.     }
  1004.     return (*vdev_proc(vdev, dorect)) (vdev, int2fixed(x), int2fixed(y),
  1005.                        int2fixed(x + w), int2fixed(y + h),
  1006.                        gx_path_type_fill);
  1007. }
  1008.  
  1009. int
  1010. gdev_vector_fill_path(gx_device * dev, const gs_imager_state * pis,
  1011.               gx_path * ppath, const gx_fill_params * params,
  1012.          const gx_device_color * pdevc, const gx_clip_path * pcpath)
  1013. {
  1014.     int code;
  1015.  
  1016.     if ((code = gdev_vector_prepare_fill(vdev, pis, params, pdevc)) < 0 ||
  1017.     (code = gdev_vector_update_clip_path(vdev, pcpath)) < 0 ||
  1018.     (vdev->bbox_device &&
  1019.      (code = (*dev_proc(vdev->bbox_device, fill_path))
  1020.       ((gx_device *) vdev->bbox_device, pis, ppath, params,
  1021.        pdevc, pcpath)) < 0) ||
  1022.     (code = (*vdev_proc(vdev, dopath))
  1023.      (vdev, ppath,
  1024.       (params->rule > 0 ? gx_path_type_even_odd :
  1025.        gx_path_type_winding_number) | gx_path_type_fill |
  1026.        vdev->fill_options,
  1027.      NULL)) < 0
  1028.     )
  1029.     return gx_default_fill_path(dev, pis, ppath, params, pdevc, pcpath);
  1030.     return code;
  1031. }
  1032.  
  1033. int
  1034. gdev_vector_stroke_path(gx_device * dev, const gs_imager_state * pis,
  1035.             gx_path * ppath, const gx_stroke_params * params,
  1036.           const gx_drawing_color * pdcolor, const gx_clip_path * pcpath)
  1037. {
  1038.     int code;
  1039.     double scale;
  1040.     int set_ctm;
  1041.     gs_matrix mat;
  1042.  
  1043.     if ((set_ctm = gdev_vector_stroke_scaling(vdev, pis, &scale, &mat)) != 0 ||
  1044.     (code = gdev_vector_prepare_stroke(vdev, pis, params, pdcolor, scale)) < 0 ||
  1045.     (code = gdev_vector_update_clip_path(vdev, pcpath)) < 0 ||
  1046.     (vdev->bbox_device &&
  1047.      (code = (*dev_proc(vdev->bbox_device, stroke_path))
  1048.       ((gx_device *) vdev->bbox_device, pis, ppath, params,
  1049.        pdcolor, pcpath)) < 0) ||
  1050.     (code = (*vdev_proc(vdev, dopath))
  1051.      (vdev, ppath, gx_path_type_stroke | vdev->stroke_options, NULL)) < 0
  1052.     )
  1053.     return gx_default_stroke_path(dev, pis, ppath, params, pdcolor, pcpath);
  1054.     return code;
  1055. }
  1056.  
  1057. int
  1058. gdev_vector_fill_trapezoid(gx_device * dev, const gs_fixed_edge * left,
  1059.     const gs_fixed_edge * right, fixed ybot, fixed ytop, bool swap_axes,
  1060.           const gx_device_color * pdevc, gs_logical_operation_t lop)
  1061. {
  1062.     fixed xl = left->start.x;
  1063.     fixed wl = left->end.x - xl;
  1064.     fixed yl = left->start.y;
  1065.     fixed hl = left->end.y - yl;
  1066.     fixed xr = right->start.x;
  1067.     fixed wr = right->end.x - xr;
  1068.     fixed yr = right->start.y;
  1069.     fixed hr = right->end.y - yr;
  1070.     fixed x0l = xl + fixed_mult_quo(wl, ybot - yl, hl);
  1071.     fixed x1l = xl + fixed_mult_quo(wl, ytop - yl, hl);
  1072.     fixed x0r = xr + fixed_mult_quo(wr, ybot - yr, hr);
  1073.     fixed x1r = xr + fixed_mult_quo(wr, ytop - yr, hr);
  1074.  
  1075. #define y0 ybot
  1076. #define y1 ytop
  1077.     int code = update_fill(vdev, pdevc, lop);
  1078.     gs_fixed_point points[4];
  1079.  
  1080.     if (code < 0)
  1081.     return gx_default_fill_trapezoid(dev, left, right, ybot, ytop,
  1082.                      swap_axes, pdevc, lop);
  1083.     /* Make sure we aren't being clipped. */
  1084.     code = gdev_vector_update_clip_path(vdev, NULL);
  1085.     if (code < 0)
  1086.     return code;
  1087.     if (swap_axes)
  1088.     points[0].y = x0l, points[1].y = x0r,
  1089.         points[0].x = points[1].x = y0,
  1090.         points[2].y = x1r, points[3].y = x1l,
  1091.         points[2].x = points[3].x = y1;
  1092.     else
  1093.     points[0].x = x0l, points[1].x = x0r,
  1094.         points[0].y = points[1].y = y0,
  1095.         points[2].x = x1r, points[3].x = x1l,
  1096.         points[2].y = points[3].y = y1;
  1097. #undef y0
  1098. #undef y1
  1099.     if (vdev->bbox_device) {
  1100.     int code = (*dev_proc(vdev->bbox_device, fill_trapezoid))
  1101.     ((gx_device *) vdev->bbox_device, left, right, ybot, ytop,
  1102.      swap_axes, pdevc, lop);
  1103.  
  1104.     if (code < 0)
  1105.         return code;
  1106.     }
  1107.     return gdev_vector_write_polygon(vdev, points, 4, true,
  1108.                      gx_path_type_fill);
  1109. }
  1110.  
  1111. int
  1112. gdev_vector_fill_parallelogram(gx_device * dev,
  1113.          fixed px, fixed py, fixed ax, fixed ay, fixed bx, fixed by,
  1114.           const gx_device_color * pdevc, gs_logical_operation_t lop)
  1115. {
  1116.     fixed pax = px + ax, pay = py + ay;
  1117.     int code = update_fill(vdev, pdevc, lop);
  1118.     gs_fixed_point points[4];
  1119.  
  1120.     if (code < 0)
  1121.     return gx_default_fill_parallelogram(dev, px, py, ax, ay, bx, by,
  1122.                          pdevc, lop);
  1123.     /* Make sure we aren't being clipped. */
  1124.     code = gdev_vector_update_clip_path(vdev, NULL);
  1125.     if (code < 0)
  1126.     return code;
  1127.     if (vdev->bbox_device) {
  1128.     code = (*dev_proc(vdev->bbox_device, fill_parallelogram))
  1129.         ((gx_device *) vdev->bbox_device, px, py, ax, ay, bx, by,
  1130.          pdevc, lop);
  1131.     if (code < 0)
  1132.         return code;
  1133.     }
  1134.     points[0].x = px, points[0].y = py;
  1135.     points[1].x = pax, points[1].y = pay;
  1136.     points[2].x = pax + bx, points[2].y = pay + by;
  1137.     points[3].x = px + bx, points[3].y = py + by;
  1138.     return gdev_vector_write_polygon(vdev, points, 4, true,
  1139.                      gx_path_type_fill);
  1140. }
  1141.  
  1142. int
  1143. gdev_vector_fill_triangle(gx_device * dev,
  1144.          fixed px, fixed py, fixed ax, fixed ay, fixed bx, fixed by,
  1145.           const gx_device_color * pdevc, gs_logical_operation_t lop)
  1146. {
  1147.     int code = update_fill(vdev, pdevc, lop);
  1148.     gs_fixed_point points[3];
  1149.  
  1150.     if (code < 0)
  1151.     return gx_default_fill_triangle(dev, px, py, ax, ay, bx, by,
  1152.                     pdevc, lop);
  1153.     /* Make sure we aren't being clipped. */
  1154.     code = gdev_vector_update_clip_path(vdev, NULL);
  1155.     if (code < 0)
  1156.     return code;
  1157.     if (vdev->bbox_device) {
  1158.     code = (*dev_proc(vdev->bbox_device, fill_triangle))
  1159.         ((gx_device *) vdev->bbox_device, px, py, ax, ay, bx, by,
  1160.          pdevc, lop);
  1161.     if (code < 0)
  1162.         return code;
  1163.     }
  1164.     points[0].x = px, points[0].y = py;
  1165.     points[1].x = px + ax, points[1].y = py + ay;
  1166.     points[2].x = px + bx, points[2].y = py + by;
  1167.     return gdev_vector_write_polygon(vdev, points, 3, true,
  1168.                      gx_path_type_fill);
  1169. }
  1170.  
  1171. #undef vdev
  1172.