Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Blame | Last modification | View Log | Download | RSS feed

  1. /* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
  2. /* cairo - a vector graphics library with display and print output
  3.  *
  4.  * Copyright © 2002 University of Southern California
  5.  *
  6.  * This library is free software; you can redistribute it and/or
  7.  * modify it either under the terms of the GNU Lesser General Public
  8.  * License version 2.1 as published by the Free Software Foundation
  9.  * (the "LGPL") or, at your option, under the terms of the Mozilla
  10.  * Public License Version 1.1 (the "MPL"). If you do not alter this
  11.  * notice, a recipient may use your version of this file under either
  12.  * the MPL or the LGPL.
  13.  *
  14.  * You should have received a copy of the LGPL along with this library
  15.  * in the file COPYING-LGPL-2.1; if not, write to the Free Software
  16.  * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
  17.  * You should have received a copy of the MPL along with this library
  18.  * in the file COPYING-MPL-1.1
  19.  *
  20.  * The contents of this file are subject to the Mozilla Public License
  21.  * Version 1.1 (the "License"); you may not use this file except in
  22.  * compliance with the License. You may obtain a copy of the License at
  23.  * http://www.mozilla.org/MPL/
  24.  *
  25.  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
  26.  * OF ANY KIND, either express or implied. See the LGPL or the MPL for
  27.  * the specific language governing rights and limitations.
  28.  *
  29.  * The Original Code is the cairo graphics library.
  30.  *
  31.  * The Initial Developer of the Original Code is University of Southern
  32.  * California.
  33.  *
  34.  * Contributor(s):
  35.  *      Carl D. Worth <cworth@cworth.org>
  36.  *      Chris Wilson <chris@chris-wilson.co.uk>
  37.  */
  38.  
  39. #define _BSD_SOURCE /* for hypot() */
  40. #include "cairoint.h"
  41.  
  42. #include "cairo-box-inline.h"
  43. #include "cairo-boxes-private.h"
  44. #include "cairo-error-private.h"
  45. #include "cairo-path-fixed-private.h"
  46. #include "cairo-slope-private.h"
  47. #include "cairo-stroke-dash-private.h"
  48.  
  49. typedef struct _segment_t {
  50.     cairo_point_t p1, p2;
  51.     unsigned flags;
  52. #define HORIZONTAL 0x1
  53. #define FORWARDS 0x2
  54. #define JOIN 0x4
  55. } segment_t;
  56.  
  57. typedef struct _cairo_rectilinear_stroker {
  58.     const cairo_stroke_style_t *stroke_style;
  59.     const cairo_matrix_t *ctm;
  60.     cairo_antialias_t antialias;
  61.  
  62.     cairo_fixed_t half_line_x, half_line_y;
  63.     cairo_boxes_t *boxes;
  64.     cairo_point_t current_point;
  65.     cairo_point_t first_point;
  66.     cairo_bool_t open_sub_path;
  67.  
  68.     cairo_stroker_dash_t dash;
  69.  
  70.     cairo_bool_t has_bounds;
  71.     cairo_box_t bounds;
  72.  
  73.     int num_segments;
  74.     int segments_size;
  75.     segment_t *segments;
  76.     segment_t segments_embedded[8]; /* common case is a single rectangle */
  77. } cairo_rectilinear_stroker_t;
  78.  
  79. static void
  80. _cairo_rectilinear_stroker_limit (cairo_rectilinear_stroker_t *stroker,
  81.                                   const cairo_box_t *boxes,
  82.                                   int num_boxes)
  83. {
  84.     stroker->has_bounds = TRUE;
  85.     _cairo_boxes_get_extents (boxes, num_boxes, &stroker->bounds);
  86.  
  87.     stroker->bounds.p1.x -= stroker->half_line_x;
  88.     stroker->bounds.p2.x += stroker->half_line_x;
  89.  
  90.     stroker->bounds.p1.y -= stroker->half_line_y;
  91.     stroker->bounds.p2.y += stroker->half_line_y;
  92. }
  93.  
  94. static cairo_bool_t
  95. _cairo_rectilinear_stroker_init (cairo_rectilinear_stroker_t    *stroker,
  96.                                  const cairo_stroke_style_t     *stroke_style,
  97.                                  const cairo_matrix_t           *ctm,
  98.                                  cairo_antialias_t               antialias,
  99.                                  cairo_boxes_t                  *boxes)
  100. {
  101.     /* This special-case rectilinear stroker only supports
  102.      * miter-joined lines (not curves) and a translation-only matrix
  103.      * (though it could probably be extended to support a matrix with
  104.      * uniform, integer scaling).
  105.      *
  106.      * It also only supports horizontal and vertical line_to
  107.      * elements. But we don't catch that here, but instead return
  108.      * UNSUPPORTED from _cairo_rectilinear_stroker_line_to if any
  109.      * non-rectilinear line_to is encountered.
  110.      */
  111.     if (stroke_style->line_join != CAIRO_LINE_JOIN_MITER)
  112.         return FALSE;
  113.  
  114.     /* If the miter limit turns right angles into bevels, then we
  115.      * can't use this optimization. Remember, the ratio is
  116.      * 1/sin(ɸ/2). So the cutoff is 1/sin(π/4.0) or ⎷2,
  117.      * which we round for safety. */
  118.     if (stroke_style->miter_limit < M_SQRT2)
  119.         return FALSE;
  120.  
  121.     if (! (stroke_style->line_cap == CAIRO_LINE_CAP_BUTT ||
  122.            stroke_style->line_cap == CAIRO_LINE_CAP_SQUARE))
  123.     {
  124.         return FALSE;
  125.     }
  126.  
  127.     if (! _cairo_matrix_is_scale (ctm))
  128.         return FALSE;
  129.  
  130.     stroker->stroke_style = stroke_style;
  131.     stroker->ctm = ctm;
  132.     stroker->antialias = antialias;
  133.  
  134.     stroker->half_line_x =
  135.         _cairo_fixed_from_double (fabs(ctm->xx) * stroke_style->line_width / 2.0);
  136.     stroker->half_line_y =
  137.         _cairo_fixed_from_double (fabs(ctm->yy) * stroke_style->line_width / 2.0);
  138.  
  139.     stroker->open_sub_path = FALSE;
  140.     stroker->segments = stroker->segments_embedded;
  141.     stroker->segments_size = ARRAY_LENGTH (stroker->segments_embedded);
  142.     stroker->num_segments = 0;
  143.  
  144.     _cairo_stroker_dash_init (&stroker->dash, stroke_style);
  145.  
  146.     stroker->has_bounds = FALSE;
  147.  
  148.     stroker->boxes = boxes;
  149.  
  150.     return TRUE;
  151. }
  152.  
  153. static void
  154. _cairo_rectilinear_stroker_fini (cairo_rectilinear_stroker_t    *stroker)
  155. {
  156.     if (stroker->segments != stroker->segments_embedded)
  157.         free (stroker->segments);
  158. }
  159.  
  160. static cairo_status_t
  161. _cairo_rectilinear_stroker_add_segment (cairo_rectilinear_stroker_t *stroker,
  162.                                         const cairo_point_t     *p1,
  163.                                         const cairo_point_t     *p2,
  164.                                         unsigned                 flags)
  165. {
  166.     if (CAIRO_INJECT_FAULT ())
  167.         return _cairo_error (CAIRO_STATUS_NO_MEMORY);
  168.  
  169.     if (stroker->num_segments == stroker->segments_size) {
  170.         int new_size = stroker->segments_size * 2;
  171.         segment_t *new_segments;
  172.  
  173.         if (stroker->segments == stroker->segments_embedded) {
  174.             new_segments = _cairo_malloc_ab (new_size, sizeof (segment_t));
  175.             if (unlikely (new_segments == NULL))
  176.                 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
  177.  
  178.             memcpy (new_segments, stroker->segments,
  179.                     stroker->num_segments * sizeof (segment_t));
  180.         } else {
  181.             new_segments = _cairo_realloc_ab (stroker->segments,
  182.                                               new_size, sizeof (segment_t));
  183.             if (unlikely (new_segments == NULL))
  184.                 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
  185.         }
  186.  
  187.         stroker->segments_size = new_size;
  188.         stroker->segments = new_segments;
  189.     }
  190.  
  191.     stroker->segments[stroker->num_segments].p1 = *p1;
  192.     stroker->segments[stroker->num_segments].p2 = *p2;
  193.     stroker->segments[stroker->num_segments].flags = flags;
  194.     stroker->num_segments++;
  195.  
  196.     return CAIRO_STATUS_SUCCESS;
  197. }
  198.  
  199. static cairo_status_t
  200. _cairo_rectilinear_stroker_emit_segments (cairo_rectilinear_stroker_t *stroker)
  201. {
  202.     cairo_line_cap_t line_cap = stroker->stroke_style->line_cap;
  203.     cairo_fixed_t half_line_x = stroker->half_line_x;
  204.     cairo_fixed_t half_line_y = stroker->half_line_y;
  205.     cairo_status_t status;
  206.     int i, j;
  207.  
  208.     /* For each segment we generate a single rectangle.
  209.      * This rectangle is based on a perpendicular extension (by half the
  210.      * line width) of the segment endpoints * after some adjustments of the
  211.      * endpoints to account for caps and joins.
  212.      */
  213.     for (i = 0; i < stroker->num_segments; i++) {
  214.         cairo_bool_t lengthen_initial, lengthen_final;
  215.         cairo_point_t *a, *b;
  216.         cairo_box_t box;
  217.  
  218.         a = &stroker->segments[i].p1;
  219.         b = &stroker->segments[i].p2;
  220.  
  221.         /* We adjust the initial point of the segment to extend the
  222.          * rectangle to include the previous cap or join, (this
  223.          * adjustment applies to all segments except for the first
  224.          * segment of open, butt-capped paths). However, we must be
  225.          * careful not to emit a miter join across a degenerate segment
  226.          * which has been elided.
  227.          *
  228.          * Overlapping segments will be eliminated by the tessellation.
  229.          * Ideally, we would not emit these self-intersections at all,
  230.          * but that is tricky with segments shorter than half_line_width.
  231.          */
  232.         j = i == 0 ? stroker->num_segments - 1 : i-1;
  233.         lengthen_initial = (stroker->segments[i].flags ^ stroker->segments[j].flags) & HORIZONTAL;
  234.         j = i == stroker->num_segments - 1 ? 0 : i+1;
  235.         lengthen_final = (stroker->segments[i].flags ^ stroker->segments[j].flags) & HORIZONTAL;
  236.         if (stroker->open_sub_path) {
  237.             if (i == 0)
  238.                 lengthen_initial = line_cap != CAIRO_LINE_CAP_BUTT;
  239.  
  240.             if (i == stroker->num_segments - 1)
  241.                 lengthen_final = line_cap != CAIRO_LINE_CAP_BUTT;
  242.         }
  243.  
  244.         /* Perform the adjustments of the endpoints. */
  245.         if (lengthen_initial | lengthen_final) {
  246.             if (a->y == b->y) {
  247.                 if (a->x < b->x) {
  248.                     if (lengthen_initial)
  249.                         a->x -= half_line_x;
  250.                     if (lengthen_final)
  251.                         b->x += half_line_x;
  252.                 } else {
  253.                     if (lengthen_initial)
  254.                         a->x += half_line_x;
  255.                     if (lengthen_final)
  256.                         b->x -= half_line_x;
  257.                 }
  258.             } else {
  259.                 if (a->y < b->y) {
  260.                     if (lengthen_initial)
  261.                         a->y -= half_line_y;
  262.                     if (lengthen_final)
  263.                         b->y += half_line_y;
  264.                 } else {
  265.                     if (lengthen_initial)
  266.                         a->y += half_line_y;
  267.                     if (lengthen_final)
  268.                         b->y -= half_line_y;
  269.                 }
  270.             }
  271.         }
  272.  
  273.         /* Form the rectangle by expanding by half the line width in
  274.          * either perpendicular direction. */
  275.         if (a->y == b->y) {
  276.             a->y -= half_line_y;
  277.             b->y += half_line_y;
  278.         } else {
  279.             a->x -= half_line_x;
  280.             b->x += half_line_x;
  281.         }
  282.  
  283.         if (a->x < b->x) {
  284.             box.p1.x = a->x;
  285.             box.p2.x = b->x;
  286.         } else {
  287.             box.p1.x = b->x;
  288.             box.p2.x = a->x;
  289.         }
  290.         if (a->y < b->y) {
  291.             box.p1.y = a->y;
  292.             box.p2.y = b->y;
  293.         } else {
  294.             box.p1.y = b->y;
  295.             box.p2.y = a->y;
  296.         }
  297.  
  298.         status = _cairo_boxes_add (stroker->boxes, stroker->antialias, &box);
  299.         if (unlikely (status))
  300.             return status;
  301.     }
  302.  
  303.     stroker->num_segments = 0;
  304.     return CAIRO_STATUS_SUCCESS;
  305. }
  306.  
  307. static cairo_status_t
  308. _cairo_rectilinear_stroker_emit_segments_dashed (cairo_rectilinear_stroker_t *stroker)
  309. {
  310.     cairo_status_t status;
  311.     cairo_line_cap_t line_cap = stroker->stroke_style->line_cap;
  312.     cairo_fixed_t half_line_x = stroker->half_line_x;
  313.     cairo_fixed_t half_line_y = stroker->half_line_y;
  314.     int i;
  315.  
  316.     for (i = 0; i < stroker->num_segments; i++) {
  317.         cairo_point_t *a, *b;
  318.         cairo_bool_t is_horizontal;
  319.         cairo_box_t box;
  320.  
  321.         a = &stroker->segments[i].p1;
  322.         b = &stroker->segments[i].p2;
  323.  
  324.         is_horizontal = stroker->segments[i].flags & HORIZONTAL;
  325.  
  326.         /* Handle the joins for a potentially degenerate segment. */
  327.         if (line_cap == CAIRO_LINE_CAP_BUTT &&
  328.             stroker->segments[i].flags & JOIN &&
  329.             (i != stroker->num_segments - 1 ||
  330.              (! stroker->open_sub_path && stroker->dash.dash_starts_on)))
  331.         {
  332.             cairo_slope_t out_slope;
  333.             int j = (i + 1) % stroker->num_segments;
  334.             cairo_bool_t forwards = !!(stroker->segments[i].flags & FORWARDS);
  335.  
  336.             _cairo_slope_init (&out_slope,
  337.                                &stroker->segments[j].p1,
  338.                                &stroker->segments[j].p2);
  339.             box.p2 = box.p1 = stroker->segments[i].p2;
  340.  
  341.             if (is_horizontal) {
  342.                 if (forwards)
  343.                     box.p2.x += half_line_x;
  344.                 else
  345.                     box.p1.x -= half_line_x;
  346.  
  347.                 if (out_slope.dy > 0)
  348.                     box.p1.y -= half_line_y;
  349.                 else
  350.                     box.p2.y += half_line_y;
  351.             } else {
  352.                 if (forwards)
  353.                     box.p2.y += half_line_y;
  354.                 else
  355.                     box.p1.y -= half_line_y;
  356.  
  357.                 if (out_slope.dx > 0)
  358.                     box.p1.x -= half_line_x;
  359.                 else
  360.                     box.p2.x += half_line_x;
  361.             }
  362.  
  363.             status = _cairo_boxes_add (stroker->boxes, stroker->antialias, &box);
  364.             if (unlikely (status))
  365.                 return status;
  366.         }
  367.  
  368.         /* Perform the adjustments of the endpoints. */
  369.         if (is_horizontal) {
  370.             if (line_cap == CAIRO_LINE_CAP_SQUARE) {
  371.                 if (a->x <= b->x) {
  372.                     a->x -= half_line_x;
  373.                     b->x += half_line_x;
  374.                 } else {
  375.                     a->x += half_line_x;
  376.                     b->x -= half_line_x;
  377.                 }
  378.             }
  379.  
  380.             a->y += half_line_y;
  381.             b->y -= half_line_y;
  382.         } else {
  383.             if (line_cap == CAIRO_LINE_CAP_SQUARE) {
  384.                 if (a->y <= b->y) {
  385.                     a->y -= half_line_y;
  386.                     b->y += half_line_y;
  387.                 } else {
  388.                     a->y += half_line_y;
  389.                     b->y -= half_line_y;
  390.                 }
  391.             }
  392.  
  393.             a->x += half_line_x;
  394.             b->x -= half_line_x;
  395.         }
  396.  
  397.         if (a->x == b->x && a->y == b->y)
  398.             continue;
  399.  
  400.         if (a->x < b->x) {
  401.             box.p1.x = a->x;
  402.             box.p2.x = b->x;
  403.         } else {
  404.             box.p1.x = b->x;
  405.             box.p2.x = a->x;
  406.         }
  407.         if (a->y < b->y) {
  408.             box.p1.y = a->y;
  409.             box.p2.y = b->y;
  410.         } else {
  411.             box.p1.y = b->y;
  412.             box.p2.y = a->y;
  413.         }
  414.  
  415.         status = _cairo_boxes_add (stroker->boxes, stroker->antialias, &box);
  416.         if (unlikely (status))
  417.             return status;
  418.     }
  419.  
  420.     stroker->num_segments = 0;
  421.  
  422.     return CAIRO_STATUS_SUCCESS;
  423. }
  424.  
  425. static cairo_status_t
  426. _cairo_rectilinear_stroker_move_to (void                *closure,
  427.                                     const cairo_point_t *point)
  428. {
  429.     cairo_rectilinear_stroker_t *stroker = closure;
  430.     cairo_status_t status;
  431.  
  432.     if (stroker->dash.dashed)
  433.         status = _cairo_rectilinear_stroker_emit_segments_dashed (stroker);
  434.     else
  435.         status = _cairo_rectilinear_stroker_emit_segments (stroker);
  436.     if (unlikely (status))
  437.         return status;
  438.  
  439.     /* reset the dash pattern for new sub paths */
  440.     _cairo_stroker_dash_start (&stroker->dash);
  441.  
  442.     stroker->current_point = *point;
  443.     stroker->first_point = *point;
  444.  
  445.     return CAIRO_STATUS_SUCCESS;
  446. }
  447.  
  448. static cairo_status_t
  449. _cairo_rectilinear_stroker_line_to (void                *closure,
  450.                                     const cairo_point_t *b)
  451. {
  452.     cairo_rectilinear_stroker_t *stroker = closure;
  453.     cairo_point_t *a = &stroker->current_point;
  454.     cairo_status_t status;
  455.  
  456.     /* We only support horizontal or vertical elements. */
  457.     assert (a->x == b->x || a->y == b->y);
  458.  
  459.     /* We don't draw anything for degenerate paths. */
  460.     if (a->x == b->x && a->y == b->y)
  461.         return CAIRO_STATUS_SUCCESS;
  462.  
  463.     status = _cairo_rectilinear_stroker_add_segment (stroker, a, b,
  464.                                                      (a->y == b->y) | JOIN);
  465.  
  466.     stroker->current_point = *b;
  467.     stroker->open_sub_path = TRUE;
  468.  
  469.     return status;
  470. }
  471.  
  472. static cairo_status_t
  473. _cairo_rectilinear_stroker_line_to_dashed (void         *closure,
  474.                                            const cairo_point_t  *point)
  475. {
  476.     cairo_rectilinear_stroker_t *stroker = closure;
  477.     const cairo_point_t *a = &stroker->current_point;
  478.     const cairo_point_t *b = point;
  479.     cairo_bool_t fully_in_bounds;
  480.     double sf, sign, remain;
  481.     cairo_fixed_t mag;
  482.     cairo_status_t status;
  483.     cairo_line_t segment;
  484.     cairo_bool_t dash_on = FALSE;
  485.     unsigned is_horizontal;
  486.  
  487.     /* We don't draw anything for degenerate paths. */
  488.     if (a->x == b->x && a->y == b->y)
  489.         return CAIRO_STATUS_SUCCESS;
  490.  
  491.     /* We only support horizontal or vertical elements. */
  492.     assert (a->x == b->x || a->y == b->y);
  493.  
  494.     fully_in_bounds = TRUE;
  495.     if (stroker->has_bounds &&
  496.         (! _cairo_box_contains_point (&stroker->bounds, a) ||
  497.          ! _cairo_box_contains_point (&stroker->bounds, b)))
  498.     {
  499.         fully_in_bounds = FALSE;
  500.     }
  501.  
  502.     is_horizontal = a->y == b->y;
  503.     if (is_horizontal) {
  504.         mag = b->x - a->x;
  505.         sf = fabs (stroker->ctm->xx);
  506.     } else {
  507.         mag = b->y - a->y;
  508.         sf = fabs (stroker->ctm->yy);
  509.     }
  510.     if (mag < 0) {
  511.         remain = _cairo_fixed_to_double (-mag);
  512.         sign = 1.;
  513.     } else {
  514.         remain = _cairo_fixed_to_double (mag);
  515.         is_horizontal |= FORWARDS;
  516.         sign = -1.;
  517.     }
  518.  
  519.     segment.p2 = segment.p1 = *a;
  520.     while (remain > 0.) {
  521.         double step_length;
  522.  
  523.         step_length = MIN (sf * stroker->dash.dash_remain, remain);
  524.         remain -= step_length;
  525.  
  526.         mag = _cairo_fixed_from_double (sign*remain);
  527.         if (is_horizontal & 0x1)
  528.             segment.p2.x = b->x + mag;
  529.         else
  530.             segment.p2.y = b->y + mag;
  531.  
  532.         if (stroker->dash.dash_on &&
  533.             (fully_in_bounds ||
  534.              _cairo_box_intersects_line_segment (&stroker->bounds, &segment)))
  535.         {
  536.             status = _cairo_rectilinear_stroker_add_segment (stroker,
  537.                                                              &segment.p1,
  538.                                                              &segment.p2,
  539.                                                              is_horizontal | (remain <= 0.) << 2);
  540.             if (unlikely (status))
  541.                 return status;
  542.  
  543.             dash_on = TRUE;
  544.         }
  545.         else
  546.         {
  547.             dash_on = FALSE;
  548.         }
  549.  
  550.         _cairo_stroker_dash_step (&stroker->dash, step_length / sf);
  551.         segment.p1 = segment.p2;
  552.     }
  553.  
  554.     if (stroker->dash.dash_on && ! dash_on &&
  555.         (fully_in_bounds ||
  556.          _cairo_box_intersects_line_segment (&stroker->bounds, &segment)))
  557.     {
  558.  
  559.         /* This segment ends on a transition to dash_on, compute a new face
  560.          * and add cap for the beginning of the next dash_on step.
  561.          */
  562.  
  563.         status = _cairo_rectilinear_stroker_add_segment (stroker,
  564.                                                          &segment.p1,
  565.                                                          &segment.p1,
  566.                                                          is_horizontal | JOIN);
  567.         if (unlikely (status))
  568.             return status;
  569.     }
  570.  
  571.     stroker->current_point = *point;
  572.     stroker->open_sub_path = TRUE;
  573.  
  574.     return CAIRO_STATUS_SUCCESS;
  575. }
  576.  
  577. static cairo_status_t
  578. _cairo_rectilinear_stroker_close_path (void *closure)
  579. {
  580.     cairo_rectilinear_stroker_t *stroker = closure;
  581.     cairo_status_t status;
  582.  
  583.     /* We don't draw anything for degenerate paths. */
  584.     if (! stroker->open_sub_path)
  585.         return CAIRO_STATUS_SUCCESS;
  586.  
  587.     if (stroker->dash.dashed) {
  588.         status = _cairo_rectilinear_stroker_line_to_dashed (stroker,
  589.                                                             &stroker->first_point);
  590.     } else {
  591.         status = _cairo_rectilinear_stroker_line_to (stroker,
  592.                                                      &stroker->first_point);
  593.     }
  594.     if (unlikely (status))
  595.         return status;
  596.  
  597.     stroker->open_sub_path = FALSE;
  598.  
  599.     if (stroker->dash.dashed)
  600.         status = _cairo_rectilinear_stroker_emit_segments_dashed (stroker);
  601.     else
  602.         status = _cairo_rectilinear_stroker_emit_segments (stroker);
  603.     if (unlikely (status))
  604.         return status;
  605.  
  606.     return CAIRO_STATUS_SUCCESS;
  607. }
  608.  
  609. cairo_int_status_t
  610. _cairo_path_fixed_stroke_rectilinear_to_boxes (const cairo_path_fixed_t *path,
  611.                                                const cairo_stroke_style_t       *stroke_style,
  612.                                                const cairo_matrix_t     *ctm,
  613.                                                cairo_antialias_t         antialias,
  614.                                                cairo_boxes_t            *boxes)
  615. {
  616.     cairo_rectilinear_stroker_t rectilinear_stroker;
  617.     cairo_int_status_t status;
  618.     cairo_box_t box;
  619.  
  620.     assert (_cairo_path_fixed_stroke_is_rectilinear (path));
  621.  
  622.     if (! _cairo_rectilinear_stroker_init (&rectilinear_stroker,
  623.                                            stroke_style, ctm, antialias,
  624.                                            boxes))
  625.     {
  626.         return CAIRO_INT_STATUS_UNSUPPORTED;
  627.     }
  628.  
  629.     if (! rectilinear_stroker.dash.dashed &&
  630.         _cairo_path_fixed_is_stroke_box (path, &box) &&
  631.         /* if the segments overlap we need to feed them into the tessellator */
  632.         box.p2.x - box.p1.x > 2* rectilinear_stroker.half_line_x &&
  633.         box.p2.y - box.p1.y > 2* rectilinear_stroker.half_line_y)
  634.     {
  635.         cairo_box_t b;
  636.  
  637.         /* top */
  638.         b.p1.x = box.p1.x - rectilinear_stroker.half_line_x;
  639.         b.p2.x = box.p2.x + rectilinear_stroker.half_line_x;
  640.         b.p1.y = box.p1.y - rectilinear_stroker.half_line_y;
  641.         b.p2.y = box.p1.y + rectilinear_stroker.half_line_y;
  642.         status = _cairo_boxes_add (boxes, antialias, &b);
  643.         assert (status == CAIRO_INT_STATUS_SUCCESS);
  644.  
  645.         /* left  (excluding top/bottom) */
  646.         b.p1.x = box.p1.x - rectilinear_stroker.half_line_x;
  647.         b.p2.x = box.p1.x + rectilinear_stroker.half_line_x;
  648.         b.p1.y = box.p1.y + rectilinear_stroker.half_line_y;
  649.         b.p2.y = box.p2.y - rectilinear_stroker.half_line_y;
  650.         status = _cairo_boxes_add (boxes, antialias, &b);
  651.         assert (status == CAIRO_INT_STATUS_SUCCESS);
  652.  
  653.         /* right  (excluding top/bottom) */
  654.         b.p1.x = box.p2.x - rectilinear_stroker.half_line_x;
  655.         b.p2.x = box.p2.x + rectilinear_stroker.half_line_x;
  656.         b.p1.y = box.p1.y + rectilinear_stroker.half_line_y;
  657.         b.p2.y = box.p2.y - rectilinear_stroker.half_line_y;
  658.         status = _cairo_boxes_add (boxes, antialias, &b);
  659.         assert (status == CAIRO_INT_STATUS_SUCCESS);
  660.  
  661.         /* bottom */
  662.         b.p1.x = box.p1.x - rectilinear_stroker.half_line_x;
  663.         b.p2.x = box.p2.x + rectilinear_stroker.half_line_x;
  664.         b.p1.y = box.p2.y - rectilinear_stroker.half_line_y;
  665.         b.p2.y = box.p2.y + rectilinear_stroker.half_line_y;
  666.         status = _cairo_boxes_add (boxes, antialias, &b);
  667.         assert (status == CAIRO_INT_STATUS_SUCCESS);
  668.  
  669.         goto done;
  670.     }
  671.  
  672.     if (boxes->num_limits) {
  673.         _cairo_rectilinear_stroker_limit (&rectilinear_stroker,
  674.                                           boxes->limits,
  675.                                           boxes->num_limits);
  676.     }
  677.  
  678.     status = _cairo_path_fixed_interpret (path,
  679.                                           _cairo_rectilinear_stroker_move_to,
  680.                                           rectilinear_stroker.dash.dashed ?
  681.                                           _cairo_rectilinear_stroker_line_to_dashed :
  682.                                           _cairo_rectilinear_stroker_line_to,
  683.                                           NULL,
  684.                                           _cairo_rectilinear_stroker_close_path,
  685.                                           &rectilinear_stroker);
  686.     if (unlikely (status))
  687.         goto BAIL;
  688.  
  689.     if (rectilinear_stroker.dash.dashed)
  690.         status = _cairo_rectilinear_stroker_emit_segments_dashed (&rectilinear_stroker);
  691.     else
  692.         status = _cairo_rectilinear_stroker_emit_segments (&rectilinear_stroker);
  693.     if (unlikely (status))
  694.         goto BAIL;
  695.  
  696.     /* As we incrementally tessellate, we do not eliminate self-intersections */
  697.     status = _cairo_bentley_ottmann_tessellate_boxes (boxes,
  698.                                                       CAIRO_FILL_RULE_WINDING,
  699.                                                       boxes);
  700.     if (unlikely (status))
  701.         goto BAIL;
  702.  
  703. done:
  704.     _cairo_rectilinear_stroker_fini (&rectilinear_stroker);
  705.     return CAIRO_STATUS_SUCCESS;
  706.  
  707. BAIL:
  708.     _cairo_rectilinear_stroker_fini (&rectilinear_stroker);
  709.     _cairo_boxes_clear (boxes);
  710.     return status;
  711. }
  712.