LCOV - code coverage report
Current view: directory - gfx/cairo/cairo/src - cairo-path-stroke.c (source / functions) Found Hit Coverage
Test: app.info Lines: 912 0 0.0 %
Date: 2012-06-02 Functions: 39 0 0.0 %

       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-boxes-private.h"
      43                 : #include "cairo-error-private.h"
      44                 : #include "cairo-path-fixed-private.h"
      45                 : #include "cairo-slope-private.h"
      46                 : 
      47                 : typedef struct _cairo_stroker_dash {
      48                 :     cairo_bool_t dashed;
      49                 :     unsigned int dash_index;
      50                 :     cairo_bool_t dash_on;
      51                 :     cairo_bool_t dash_starts_on;
      52                 :     double dash_remain;
      53                 : 
      54                 :     double dash_offset;
      55                 :     const double *dashes;
      56                 :     unsigned int num_dashes;
      57                 : } cairo_stroker_dash_t;
      58                 : 
      59                 : typedef struct cairo_stroker {
      60                 :     cairo_stroke_style_t style;
      61                 : 
      62                 :     const cairo_matrix_t *ctm;
      63                 :     const cairo_matrix_t *ctm_inverse;
      64                 :     double tolerance;
      65                 :     double ctm_determinant;
      66                 :     cairo_bool_t ctm_det_positive;
      67                 : 
      68                 :     void *closure;
      69                 :     cairo_status_t (*add_external_edge) (void *closure,
      70                 :                                          const cairo_point_t *p1,
      71                 :                                          const cairo_point_t *p2);
      72                 :     cairo_status_t (*add_triangle) (void *closure,
      73                 :                                     const cairo_point_t triangle[3]);
      74                 :     cairo_status_t (*add_triangle_fan) (void *closure,
      75                 :                                         const cairo_point_t *midpt,
      76                 :                                         const cairo_point_t *points,
      77                 :                                         int npoints);
      78                 :     cairo_status_t (*add_convex_quad) (void *closure,
      79                 :                                        const cairo_point_t quad[4]);
      80                 : 
      81                 :     cairo_pen_t   pen;
      82                 : 
      83                 :     cairo_point_t current_point;
      84                 :     cairo_point_t first_point;
      85                 : 
      86                 :     cairo_bool_t has_initial_sub_path;
      87                 : 
      88                 :     cairo_bool_t has_current_face;
      89                 :     cairo_stroke_face_t current_face;
      90                 : 
      91                 :     cairo_bool_t has_first_face;
      92                 :     cairo_stroke_face_t first_face;
      93                 : 
      94                 :     cairo_stroker_dash_t dash;
      95                 : 
      96                 :     cairo_bool_t has_bounds;
      97                 :     cairo_box_t bounds;
      98                 : } cairo_stroker_t;
      99                 : 
     100                 : static void
     101               0 : _cairo_stroker_dash_start (cairo_stroker_dash_t *dash)
     102                 : {
     103                 :     double offset;
     104               0 :     cairo_bool_t on = TRUE;
     105               0 :     unsigned int i = 0;
     106                 : 
     107               0 :     if (! dash->dashed)
     108               0 :         return;
     109                 : 
     110               0 :     offset = dash->dash_offset;
     111                 : 
     112                 :     /* We stop searching for a starting point as soon as the
     113                 :        offset reaches zero.  Otherwise when an initial dash
     114                 :        segment shrinks to zero it will be skipped over. */
     115               0 :     while (offset > 0.0 && offset >= dash->dashes[i]) {
     116               0 :         offset -= dash->dashes[i];
     117               0 :         on = !on;
     118               0 :         if (++i == dash->num_dashes)
     119               0 :             i = 0;
     120                 :     }
     121                 : 
     122               0 :     dash->dash_index = i;
     123               0 :     dash->dash_on = dash->dash_starts_on = on;
     124               0 :     dash->dash_remain = dash->dashes[i] - offset;
     125                 : }
     126                 : 
     127                 : static void
     128               0 : _cairo_stroker_dash_step (cairo_stroker_dash_t *dash, double step)
     129                 : {
     130               0 :     dash->dash_remain -= step;
     131               0 :     if (dash->dash_remain <= 0.) {
     132               0 :         if (++dash->dash_index == dash->num_dashes)
     133               0 :             dash->dash_index = 0;
     134                 : 
     135               0 :         dash->dash_on = ! dash->dash_on;
     136               0 :         dash->dash_remain = dash->dashes[dash->dash_index];
     137                 :     }
     138               0 : }
     139                 : 
     140                 : static void
     141               0 : _cairo_stroker_dash_init (cairo_stroker_dash_t *dash,
     142                 :                           const cairo_stroke_style_t *style)
     143                 : {
     144               0 :     dash->dashed = style->dash != NULL;
     145               0 :     if (! dash->dashed)
     146               0 :         return;
     147                 : 
     148               0 :     dash->dashes = style->dash;
     149               0 :     dash->num_dashes = style->num_dashes;
     150               0 :     dash->dash_offset = style->dash_offset;
     151                 : 
     152               0 :     _cairo_stroker_dash_start (dash);
     153                 : }
     154                 : 
     155                 : static cairo_status_t
     156               0 : _cairo_stroker_init (cairo_stroker_t            *stroker,
     157                 :                      const cairo_stroke_style_t *stroke_style,
     158                 :                      const cairo_matrix_t       *ctm,
     159                 :                      const cairo_matrix_t       *ctm_inverse,
     160                 :                      double                      tolerance)
     161                 : {
     162                 :     cairo_status_t status;
     163                 : 
     164               0 :     stroker->style = *stroke_style;
     165               0 :     stroker->ctm = ctm;
     166               0 :     stroker->ctm_inverse = ctm_inverse;
     167               0 :     stroker->tolerance = tolerance;
     168                 : 
     169               0 :     stroker->ctm_determinant = _cairo_matrix_compute_determinant (stroker->ctm);
     170               0 :     stroker->ctm_det_positive = stroker->ctm_determinant >= 0.0;
     171                 : 
     172               0 :     status = _cairo_pen_init (&stroker->pen,
     173               0 :                               stroke_style->line_width / 2.0,
     174                 :                               tolerance, ctm);
     175               0 :     if (unlikely (status))
     176               0 :         return status;
     177                 : 
     178               0 :     stroker->has_bounds = FALSE;
     179                 : 
     180               0 :     stroker->has_current_face = FALSE;
     181               0 :     stroker->has_first_face = FALSE;
     182               0 :     stroker->has_initial_sub_path = FALSE;
     183                 : 
     184               0 :     _cairo_stroker_dash_init (&stroker->dash, stroke_style);
     185                 : 
     186               0 :     stroker->add_external_edge = NULL;
     187                 : 
     188               0 :     return CAIRO_STATUS_SUCCESS;
     189                 : }
     190                 : 
     191                 : static void
     192               0 : _cairo_stroker_limit (cairo_stroker_t *stroker,
     193                 :                       const cairo_box_t *boxes,
     194                 :                       int num_boxes)
     195                 : {
     196                 :     double dx, dy;
     197                 :     cairo_fixed_t fdx, fdy;
     198                 : 
     199               0 :     stroker->has_bounds = TRUE;
     200               0 :     _cairo_boxes_get_extents (boxes, num_boxes, &stroker->bounds);
     201                 : 
     202                 :     /* Extend the bounds in each direction to account for the maximum area
     203                 :      * we might generate trapezoids, to capture line segments that are outside
     204                 :      * of the bounds but which might generate rendering that's within bounds.
     205                 :      */
     206                 : 
     207               0 :     _cairo_stroke_style_max_distance_from_path (&stroker->style, stroker->ctm,
     208                 :                                                 &dx, &dy);
     209                 : 
     210               0 :     fdx = _cairo_fixed_from_double (dx);
     211               0 :     fdy = _cairo_fixed_from_double (dy);
     212                 : 
     213               0 :     stroker->bounds.p1.x -= fdx;
     214               0 :     stroker->bounds.p2.x += fdx;
     215                 : 
     216               0 :     stroker->bounds.p1.y -= fdy;
     217               0 :     stroker->bounds.p2.y += fdy;
     218               0 : }
     219                 : 
     220                 : static void
     221               0 : _cairo_stroker_fini (cairo_stroker_t *stroker)
     222                 : {
     223               0 :     _cairo_pen_fini (&stroker->pen);
     224               0 : }
     225                 : 
     226                 : static void
     227               0 : _translate_point (cairo_point_t *point, const cairo_point_t *offset)
     228                 : {
     229               0 :     point->x += offset->x;
     230               0 :     point->y += offset->y;
     231               0 : }
     232                 : 
     233                 : static int
     234               0 : _cairo_stroker_join_is_clockwise (const cairo_stroke_face_t *in,
     235                 :                                   const cairo_stroke_face_t *out)
     236                 : {
     237                 :     cairo_slope_t in_slope, out_slope;
     238                 : 
     239               0 :     _cairo_slope_init (&in_slope, &in->point, &in->cw);
     240               0 :     _cairo_slope_init (&out_slope, &out->point, &out->cw);
     241                 : 
     242               0 :     return _cairo_slope_compare (&in_slope, &out_slope) < 0;
     243                 : }
     244                 : 
     245                 : /**
     246                 :  * _cairo_slope_compare_sgn
     247                 :  *
     248                 :  * Return -1, 0 or 1 depending on the relative slopes of
     249                 :  * two lines.
     250                 :  */
     251                 : static int
     252               0 : _cairo_slope_compare_sgn (double dx1, double dy1, double dx2, double dy2)
     253                 : {
     254               0 :     double  c = (dx1 * dy2 - dx2 * dy1);
     255                 : 
     256               0 :     if (c > 0) return 1;
     257               0 :     if (c < 0) return -1;
     258               0 :     return 0;
     259                 : }
     260                 : 
     261                 : static inline int
     262               0 : _range_step (int i, int step, int max)
     263                 : {
     264               0 :     i += step;
     265               0 :     if (i < 0)
     266               0 :         i = max - 1;
     267               0 :     if (i >= max)
     268               0 :         i = 0;
     269               0 :     return i;
     270                 : }
     271                 : 
     272                 : /*
     273                 :  * Construct a fan around the midpoint using the vertices from pen between
     274                 :  * inpt and outpt.
     275                 :  */
     276                 : static cairo_status_t
     277               0 : _tessellate_fan (cairo_stroker_t *stroker,
     278                 :                  const cairo_slope_t *in_vector,
     279                 :                  const cairo_slope_t *out_vector,
     280                 :                  const cairo_point_t *midpt,
     281                 :                  const cairo_point_t *inpt,
     282                 :                  const cairo_point_t *outpt,
     283                 :                  cairo_bool_t clockwise)
     284                 : {
     285               0 :     cairo_point_t stack_points[64], *points = stack_points;
     286                 :     int start, stop, step, i, npoints;
     287                 :     cairo_status_t status;
     288                 : 
     289               0 :     if (clockwise) {
     290               0 :         step  = -1;
     291                 : 
     292               0 :         start = _cairo_pen_find_active_ccw_vertex_index (&stroker->pen,
     293                 :                                                          in_vector);
     294               0 :         if (_cairo_slope_compare (&stroker->pen.vertices[start].slope_ccw,
     295                 :                                   in_vector) < 0)
     296               0 :             start = _range_step (start, -1, stroker->pen.num_vertices);
     297                 : 
     298               0 :         stop  = _cairo_pen_find_active_ccw_vertex_index (&stroker->pen,
     299                 :                                                          out_vector);
     300               0 :         if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_cw,
     301                 :                                   out_vector) > 0)
     302                 :         {
     303               0 :             stop = _range_step (stop, 1, stroker->pen.num_vertices);
     304               0 :             if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_ccw,
     305                 :                                       in_vector) < 0)
     306                 :             {
     307               0 :                 goto BEVEL;
     308                 :             }
     309                 :         }
     310                 : 
     311               0 :         npoints = start - stop;
     312                 :     } else {
     313               0 :         step  = 1;
     314                 : 
     315               0 :         start = _cairo_pen_find_active_cw_vertex_index (&stroker->pen,
     316                 :                                                         in_vector);
     317               0 :         if (_cairo_slope_compare (&stroker->pen.vertices[start].slope_cw,
     318                 :                                   in_vector) < 0)
     319               0 :             start = _range_step (start, 1, stroker->pen.num_vertices);
     320                 : 
     321               0 :         stop  = _cairo_pen_find_active_cw_vertex_index (&stroker->pen,
     322                 :                                                         out_vector);
     323               0 :         if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_ccw,
     324                 :                                   out_vector) > 0)
     325                 :         {
     326               0 :             stop = _range_step (stop, -1, stroker->pen.num_vertices);
     327               0 :             if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_cw,
     328                 :                                       in_vector) < 0)
     329                 :             {
     330               0 :                 goto BEVEL;
     331                 :             }
     332                 :         }
     333                 : 
     334               0 :         npoints = stop - start;
     335                 :     }
     336               0 :     stop = _range_step (stop, step, stroker->pen.num_vertices);
     337                 : 
     338               0 :     if (npoints < 0)
     339               0 :         npoints += stroker->pen.num_vertices;
     340               0 :     npoints += 3;
     341                 : 
     342               0 :     if (npoints <= 1)
     343               0 :         goto BEVEL;
     344                 : 
     345               0 :     if (npoints > ARRAY_LENGTH (stack_points)) {
     346               0 :         points = _cairo_malloc_ab (npoints, sizeof (cairo_point_t));
     347               0 :         if (unlikely (points == NULL))
     348               0 :             return _cairo_error (CAIRO_STATUS_NO_MEMORY);
     349                 :     }
     350                 : 
     351                 : 
     352                 :     /* Construct the fan. */
     353               0 :     npoints = 0;
     354               0 :     points[npoints++] = *inpt;
     355               0 :     for (i = start;
     356                 :          i != stop;
     357               0 :         i = _range_step (i, step, stroker->pen.num_vertices))
     358                 :     {
     359               0 :         points[npoints] = *midpt;
     360               0 :         _translate_point (&points[npoints], &stroker->pen.vertices[i].point);
     361               0 :         npoints++;
     362                 :     }
     363               0 :     points[npoints++] = *outpt;
     364                 : 
     365               0 :     if (stroker->add_external_edge != NULL) {
     366               0 :         for (i = 0; i < npoints - 1; i++) {
     367               0 :             if (clockwise) {
     368               0 :                 status = stroker->add_external_edge (stroker->closure,
     369               0 :                                                      &points[i], &points[i+1]);
     370                 :             } else {
     371               0 :                 status = stroker->add_external_edge (stroker->closure,
     372               0 :                                                      &points[i+1], &points[i]);
     373                 :             }
     374               0 :             if (unlikely (status))
     375               0 :                 break;
     376                 :         }
     377                 :     } else {
     378               0 :         status = stroker->add_triangle_fan (stroker->closure,
     379                 :                                             midpt, points, npoints);
     380                 :     }
     381                 : 
     382               0 :     if (points != stack_points)
     383               0 :         free (points);
     384                 : 
     385               0 :     return status;
     386                 : 
     387                 : BEVEL:
     388                 :     /* Ensure a leak free connection... */
     389               0 :     if (stroker->add_external_edge != NULL) {
     390               0 :         if (clockwise)
     391               0 :             return stroker->add_external_edge (stroker->closure, inpt, outpt);
     392                 :         else
     393               0 :             return stroker->add_external_edge (stroker->closure, outpt, inpt);
     394                 :     } else {
     395               0 :         stack_points[0] = *midpt;
     396               0 :         stack_points[1] = *inpt;
     397               0 :         stack_points[2] = *outpt;
     398               0 :         return stroker->add_triangle (stroker->closure, stack_points);
     399                 :     }
     400                 : }
     401                 : 
     402                 : static cairo_status_t
     403               0 : _cairo_stroker_join (cairo_stroker_t *stroker,
     404                 :                      const cairo_stroke_face_t *in,
     405                 :                      const cairo_stroke_face_t *out)
     406                 : {
     407               0 :     int  clockwise = _cairo_stroker_join_is_clockwise (out, in);
     408                 :     const cairo_point_t *inpt, *outpt;
     409                 :     cairo_point_t points[4];
     410                 :     cairo_status_t status;
     411                 : 
     412               0 :     if (in->cw.x  == out->cw.x  && in->cw.y  == out->cw.y &&
     413               0 :         in->ccw.x == out->ccw.x && in->ccw.y == out->ccw.y)
     414                 :     {
     415               0 :         return CAIRO_STATUS_SUCCESS;
     416                 :     }
     417                 : 
     418               0 :     if (clockwise) {
     419               0 :         if (stroker->add_external_edge != NULL) {
     420               0 :             status = stroker->add_external_edge (stroker->closure,
     421                 :                                                  &out->cw, &in->point);
     422               0 :             if (unlikely (status))
     423               0 :                 return status;
     424                 : 
     425               0 :             status = stroker->add_external_edge (stroker->closure,
     426                 :                                                  &in->point, &in->cw);
     427               0 :             if (unlikely (status))
     428               0 :                 return status;
     429                 :         }
     430                 : 
     431               0 :         inpt = &in->ccw;
     432               0 :         outpt = &out->ccw;
     433                 :     } else {
     434               0 :         if (stroker->add_external_edge != NULL) {
     435               0 :             status = stroker->add_external_edge (stroker->closure,
     436                 :                                                  &in->ccw, &in->point);
     437               0 :             if (unlikely (status))
     438               0 :                 return status;
     439                 : 
     440               0 :             status = stroker->add_external_edge (stroker->closure,
     441                 :                                                  &in->point, &out->ccw);
     442               0 :             if (unlikely (status))
     443               0 :                 return status;
     444                 :         }
     445                 : 
     446               0 :         inpt = &in->cw;
     447               0 :         outpt = &out->cw;
     448                 :     }
     449                 : 
     450               0 :     switch (stroker->style.line_join) {
     451                 :     case CAIRO_LINE_JOIN_ROUND:
     452                 :         /* construct a fan around the common midpoint */
     453               0 :         return _tessellate_fan (stroker,
     454                 :                                 &in->dev_vector,
     455                 :                                 &out->dev_vector,
     456                 :                                 &in->point, inpt, outpt,
     457                 :                                 clockwise);
     458                 : 
     459                 :     case CAIRO_LINE_JOIN_MITER:
     460                 :     default: {
     461                 :         /* dot product of incoming slope vector with outgoing slope vector */
     462               0 :         double  in_dot_out = -in->usr_vector.x * out->usr_vector.x +
     463               0 :                              -in->usr_vector.y * out->usr_vector.y;
     464               0 :         double  ml = stroker->style.miter_limit;
     465                 : 
     466                 :         /* Check the miter limit -- lines meeting at an acute angle
     467                 :          * can generate long miters, the limit converts them to bevel
     468                 :          *
     469                 :          * Consider the miter join formed when two line segments
     470                 :          * meet at an angle psi:
     471                 :          *
     472                 :          *         /.\
     473                 :          *        /. .\
     474                 :          *       /./ \.\
     475                 :          *      /./psi\.\
     476                 :          *
     477                 :          * We can zoom in on the right half of that to see:
     478                 :          *
     479                 :          *          |\
     480                 :          *          | \ psi/2
     481                 :          *          |  \
     482                 :          *          |   \
     483                 :          *          |    \
     484                 :          *          |     \
     485                 :          *        miter    \
     486                 :          *       length     \
     487                 :          *          |        \
     488                 :          *          |        .\
     489                 :          *          |    .     \
     490                 :          *          |.   line   \
     491                 :          *           \    width  \
     492                 :          *            \           \
     493                 :          *
     494                 :          *
     495                 :          * The right triangle in that figure, (the line-width side is
     496                 :          * shown faintly with three '.' characters), gives us the
     497                 :          * following expression relating miter length, angle and line
     498                 :          * width:
     499                 :          *
     500                 :          *      1 /sin (psi/2) = miter_length / line_width
     501                 :          *
     502                 :          * The right-hand side of this relationship is the same ratio
     503                 :          * in which the miter limit (ml) is expressed. We want to know
     504                 :          * when the miter length is within the miter limit. That is
     505                 :          * when the following condition holds:
     506                 :          *
     507                 :          *      1/sin(psi/2) <= ml
     508                 :          *      1 <= ml sin(psi/2)
     509                 :          *      1 <= ml² sin²(psi/2)
     510                 :          *      2 <= ml² 2 sin²(psi/2)
     511                 :          *                              2·sin²(psi/2) = 1-cos(psi)
     512                 :          *      2 <= ml² (1-cos(psi))
     513                 :          *
     514                 :          *                              in · out = |in| |out| cos (psi)
     515                 :          *
     516                 :          * in and out are both unit vectors, so:
     517                 :          *
     518                 :          *                              in · out = cos (psi)
     519                 :          *
     520                 :          *      2 <= ml² (1 - in · out)
     521                 :          *
     522                 :          */
     523               0 :         if (2 <= ml * ml * (1 - in_dot_out)) {
     524                 :             double              x1, y1, x2, y2;
     525                 :             double              mx, my;
     526                 :             double              dx1, dx2, dy1, dy2;
     527                 :             double              ix, iy;
     528                 :             double              fdx1, fdy1, fdx2, fdy2;
     529                 :             double              mdx, mdy;
     530                 : 
     531                 :             /*
     532                 :              * we've got the points already transformed to device
     533                 :              * space, but need to do some computation with them and
     534                 :              * also need to transform the slope from user space to
     535                 :              * device space
     536                 :              */
     537                 :             /* outer point of incoming line face */
     538               0 :             x1 = _cairo_fixed_to_double (inpt->x);
     539               0 :             y1 = _cairo_fixed_to_double (inpt->y);
     540               0 :             dx1 = in->usr_vector.x;
     541               0 :             dy1 = in->usr_vector.y;
     542               0 :             cairo_matrix_transform_distance (stroker->ctm, &dx1, &dy1);
     543                 : 
     544                 :             /* outer point of outgoing line face */
     545               0 :             x2 = _cairo_fixed_to_double (outpt->x);
     546               0 :             y2 = _cairo_fixed_to_double (outpt->y);
     547               0 :             dx2 = out->usr_vector.x;
     548               0 :             dy2 = out->usr_vector.y;
     549               0 :             cairo_matrix_transform_distance (stroker->ctm, &dx2, &dy2);
     550                 : 
     551                 :             /*
     552                 :              * Compute the location of the outer corner of the miter.
     553                 :              * That's pretty easy -- just the intersection of the two
     554                 :              * outer edges.  We've got slopes and points on each
     555                 :              * of those edges.  Compute my directly, then compute
     556                 :              * mx by using the edge with the larger dy; that avoids
     557                 :              * dividing by values close to zero.
     558                 :              */
     559               0 :             my = (((x2 - x1) * dy1 * dy2 - y2 * dx2 * dy1 + y1 * dx1 * dy2) /
     560               0 :                   (dx1 * dy2 - dx2 * dy1));
     561               0 :             if (fabs (dy1) >= fabs (dy2))
     562               0 :                 mx = (my - y1) * dx1 / dy1 + x1;
     563                 :             else
     564               0 :                 mx = (my - y2) * dx2 / dy2 + x2;
     565                 : 
     566                 :             /*
     567                 :              * When the two outer edges are nearly parallel, slight
     568                 :              * perturbations in the position of the outer points of the lines
     569                 :              * caused by representing them in fixed point form can cause the
     570                 :              * intersection point of the miter to move a large amount. If
     571                 :              * that moves the miter intersection from between the two faces,
     572                 :              * then draw a bevel instead.
     573                 :              */
     574                 : 
     575               0 :             ix = _cairo_fixed_to_double (in->point.x);
     576               0 :             iy = _cairo_fixed_to_double (in->point.y);
     577                 : 
     578                 :             /* slope of one face */
     579               0 :             fdx1 = x1 - ix; fdy1 = y1 - iy;
     580                 : 
     581                 :             /* slope of the other face */
     582               0 :             fdx2 = x2 - ix; fdy2 = y2 - iy;
     583                 : 
     584                 :             /* slope from the intersection to the miter point */
     585               0 :             mdx = mx - ix; mdy = my - iy;
     586                 : 
     587                 :             /*
     588                 :              * Make sure the miter point line lies between the two
     589                 :              * faces by comparing the slopes
     590                 :              */
     591               0 :             if (_cairo_slope_compare_sgn (fdx1, fdy1, mdx, mdy) !=
     592               0 :                 _cairo_slope_compare_sgn (fdx2, fdy2, mdx, mdy))
     593                 :             {
     594               0 :                 if (stroker->add_external_edge != NULL) {
     595               0 :                     points[0].x = _cairo_fixed_from_double (mx);
     596               0 :                     points[0].y = _cairo_fixed_from_double (my);
     597                 : 
     598               0 :                     if (clockwise) {
     599               0 :                         status = stroker->add_external_edge (stroker->closure,
     600                 :                                                              inpt, &points[0]);
     601               0 :                         if (unlikely (status))
     602               0 :                             return status;
     603                 : 
     604               0 :                         status = stroker->add_external_edge (stroker->closure,
     605                 :                                                              &points[0], outpt);
     606               0 :                         if (unlikely (status))
     607               0 :                             return status;
     608                 :                     } else {
     609               0 :                         status = stroker->add_external_edge (stroker->closure,
     610                 :                                                              outpt, &points[0]);
     611               0 :                         if (unlikely (status))
     612               0 :                             return status;
     613                 : 
     614               0 :                         status = stroker->add_external_edge (stroker->closure,
     615                 :                                                              &points[0], inpt);
     616               0 :                         if (unlikely (status))
     617               0 :                             return status;
     618                 :                     }
     619                 : 
     620               0 :                     return CAIRO_STATUS_SUCCESS;
     621                 :                 } else {
     622               0 :                     points[0] = in->point;
     623               0 :                     points[1] = *inpt;
     624               0 :                     points[2].x = _cairo_fixed_from_double (mx);
     625               0 :                     points[2].y = _cairo_fixed_from_double (my);
     626               0 :                     points[3] = *outpt;
     627                 : 
     628               0 :                     return stroker->add_convex_quad (stroker->closure, points);
     629                 :                 }
     630                 :             }
     631                 :         }
     632                 :     }
     633                 : 
     634                 :     /* fall through ... */
     635                 : 
     636                 :     case CAIRO_LINE_JOIN_BEVEL:
     637               0 :         if (stroker->add_external_edge != NULL) {
     638               0 :             if (clockwise) {
     639               0 :                 return stroker->add_external_edge (stroker->closure,
     640                 :                                                    inpt, outpt);
     641                 :             } else {
     642               0 :                 return stroker->add_external_edge (stroker->closure,
     643                 :                                                    outpt, inpt);
     644                 :             }
     645                 :         } else {
     646               0 :             points[0] = in->point;
     647               0 :             points[1] = *inpt;
     648               0 :             points[2] = *outpt;
     649                 : 
     650               0 :             return stroker->add_triangle (stroker->closure, points);
     651                 :         }
     652                 :     }
     653                 : }
     654                 : 
     655                 : static cairo_status_t
     656               0 : _cairo_stroker_add_cap (cairo_stroker_t *stroker,
     657                 :                         const cairo_stroke_face_t *f)
     658                 : {
     659               0 :     switch (stroker->style.line_cap) {
     660                 :     case CAIRO_LINE_CAP_ROUND: {
     661                 :         cairo_slope_t slope;
     662                 : 
     663               0 :         slope.dx = -f->dev_vector.dx;
     664               0 :         slope.dy = -f->dev_vector.dy;
     665                 : 
     666               0 :         return _tessellate_fan (stroker,
     667                 :                                 &f->dev_vector,
     668                 :                                 &slope,
     669                 :                                 &f->point, &f->cw, &f->ccw,
     670                 :                                 FALSE);
     671                 : 
     672                 :     }
     673                 : 
     674                 :     case CAIRO_LINE_CAP_SQUARE: {
     675                 :         double dx, dy;
     676                 :         cairo_slope_t   fvector;
     677                 :         cairo_point_t   quad[4];
     678                 : 
     679               0 :         dx = f->usr_vector.x;
     680               0 :         dy = f->usr_vector.y;
     681               0 :         dx *= stroker->style.line_width / 2.0;
     682               0 :         dy *= stroker->style.line_width / 2.0;
     683               0 :         cairo_matrix_transform_distance (stroker->ctm, &dx, &dy);
     684               0 :         fvector.dx = _cairo_fixed_from_double (dx);
     685               0 :         fvector.dy = _cairo_fixed_from_double (dy);
     686                 : 
     687               0 :         quad[0] = f->ccw;
     688               0 :         quad[1].x = f->ccw.x + fvector.dx;
     689               0 :         quad[1].y = f->ccw.y + fvector.dy;
     690               0 :         quad[2].x = f->cw.x + fvector.dx;
     691               0 :         quad[2].y = f->cw.y + fvector.dy;
     692               0 :         quad[3] = f->cw;
     693                 : 
     694               0 :         if (stroker->add_external_edge != NULL) {
     695                 :             cairo_status_t status;
     696                 : 
     697               0 :             status = stroker->add_external_edge (stroker->closure,
     698                 :                                                  &quad[0], &quad[1]);
     699               0 :             if (unlikely (status))
     700               0 :                 return status;
     701                 : 
     702               0 :             status = stroker->add_external_edge (stroker->closure,
     703                 :                                                  &quad[1], &quad[2]);
     704               0 :             if (unlikely (status))
     705               0 :                 return status;
     706                 : 
     707               0 :             status = stroker->add_external_edge (stroker->closure,
     708                 :                                                  &quad[2], &quad[3]);
     709               0 :             if (unlikely (status))
     710               0 :                 return status;
     711                 : 
     712               0 :             return CAIRO_STATUS_SUCCESS;
     713                 :         } else {
     714               0 :             return stroker->add_convex_quad (stroker->closure, quad);
     715                 :         }
     716                 :     }
     717                 : 
     718                 :     case CAIRO_LINE_CAP_BUTT:
     719                 :     default:
     720               0 :         if (stroker->add_external_edge != NULL) {
     721               0 :             return stroker->add_external_edge (stroker->closure,
     722                 :                                                &f->ccw, &f->cw);
     723                 :         } else {
     724               0 :             return CAIRO_STATUS_SUCCESS;
     725                 :         }
     726                 :     }
     727                 : }
     728                 : 
     729                 : static cairo_status_t
     730               0 : _cairo_stroker_add_leading_cap (cairo_stroker_t     *stroker,
     731                 :                                 const cairo_stroke_face_t *face)
     732                 : {
     733                 :     cairo_stroke_face_t reversed;
     734                 :     cairo_point_t t;
     735                 : 
     736               0 :     reversed = *face;
     737                 : 
     738                 :     /* The initial cap needs an outward facing vector. Reverse everything */
     739               0 :     reversed.usr_vector.x = -reversed.usr_vector.x;
     740               0 :     reversed.usr_vector.y = -reversed.usr_vector.y;
     741               0 :     reversed.dev_vector.dx = -reversed.dev_vector.dx;
     742               0 :     reversed.dev_vector.dy = -reversed.dev_vector.dy;
     743               0 :     t = reversed.cw;
     744               0 :     reversed.cw = reversed.ccw;
     745               0 :     reversed.ccw = t;
     746                 : 
     747               0 :     return _cairo_stroker_add_cap (stroker, &reversed);
     748                 : }
     749                 : 
     750                 : static cairo_status_t
     751               0 : _cairo_stroker_add_trailing_cap (cairo_stroker_t     *stroker,
     752                 :                                  const cairo_stroke_face_t *face)
     753                 : {
     754               0 :     return _cairo_stroker_add_cap (stroker, face);
     755                 : }
     756                 : 
     757                 : static inline cairo_bool_t
     758               0 : _compute_normalized_device_slope (double *dx, double *dy,
     759                 :                                   const cairo_matrix_t *ctm_inverse,
     760                 :                                   double *mag_out)
     761                 : {
     762               0 :     double dx0 = *dx, dy0 = *dy;
     763                 :     double mag;
     764                 : 
     765               0 :     cairo_matrix_transform_distance (ctm_inverse, &dx0, &dy0);
     766                 : 
     767               0 :     if (dx0 == 0.0 && dy0 == 0.0) {
     768               0 :         if (mag_out)
     769               0 :             *mag_out = 0.0;
     770               0 :         return FALSE;
     771                 :     }
     772                 : 
     773               0 :     if (dx0 == 0.0) {
     774               0 :         *dx = 0.0;
     775               0 :         if (dy0 > 0.0) {
     776               0 :             mag = dy0;
     777               0 :             *dy = 1.0;
     778                 :         } else {
     779               0 :             mag = -dy0;
     780               0 :             *dy = -1.0;
     781                 :         }
     782               0 :     } else if (dy0 == 0.0) {
     783               0 :         *dy = 0.0;
     784               0 :         if (dx0 > 0.0) {
     785               0 :             mag = dx0;
     786               0 :             *dx = 1.0;
     787                 :         } else {
     788               0 :             mag = -dx0;
     789               0 :             *dx = -1.0;
     790                 :         }
     791                 :     } else {
     792               0 :         mag = hypot (dx0, dy0);
     793               0 :         *dx = dx0 / mag;
     794               0 :         *dy = dy0 / mag;
     795                 :     }
     796                 : 
     797               0 :     if (mag_out)
     798               0 :         *mag_out = mag;
     799                 : 
     800               0 :     return TRUE;
     801                 : }
     802                 : 
     803                 : static void
     804               0 : _compute_face (const cairo_point_t *point, cairo_slope_t *dev_slope,
     805                 :                double slope_dx, double slope_dy,
     806                 :                cairo_stroker_t *stroker, cairo_stroke_face_t *face)
     807                 : {
     808                 :     double face_dx, face_dy;
     809                 :     cairo_point_t offset_ccw, offset_cw;
     810                 : 
     811                 :     /*
     812                 :      * rotate to get a line_width/2 vector along the face, note that
     813                 :      * the vector must be rotated the right direction in device space,
     814                 :      * but by 90° in user space. So, the rotation depends on
     815                 :      * whether the ctm reflects or not, and that can be determined
     816                 :      * by looking at the determinant of the matrix.
     817                 :      */
     818               0 :     if (stroker->ctm_det_positive)
     819                 :     {
     820               0 :         face_dx = - slope_dy * (stroker->style.line_width / 2.0);
     821               0 :         face_dy = slope_dx * (stroker->style.line_width / 2.0);
     822                 :     }
     823                 :     else
     824                 :     {
     825               0 :         face_dx = slope_dy * (stroker->style.line_width / 2.0);
     826               0 :         face_dy = - slope_dx * (stroker->style.line_width / 2.0);
     827                 :     }
     828                 : 
     829                 :     /* back to device space */
     830               0 :     cairo_matrix_transform_distance (stroker->ctm, &face_dx, &face_dy);
     831                 : 
     832               0 :     offset_ccw.x = _cairo_fixed_from_double (face_dx);
     833               0 :     offset_ccw.y = _cairo_fixed_from_double (face_dy);
     834               0 :     offset_cw.x = -offset_ccw.x;
     835               0 :     offset_cw.y = -offset_ccw.y;
     836                 : 
     837               0 :     face->ccw = *point;
     838               0 :     _translate_point (&face->ccw, &offset_ccw);
     839                 : 
     840               0 :     face->point = *point;
     841                 : 
     842               0 :     face->cw = *point;
     843               0 :     _translate_point (&face->cw, &offset_cw);
     844                 : 
     845               0 :     face->usr_vector.x = slope_dx;
     846               0 :     face->usr_vector.y = slope_dy;
     847                 : 
     848               0 :     face->dev_vector = *dev_slope;
     849               0 : }
     850                 : 
     851                 : static cairo_status_t
     852               0 : _cairo_stroker_add_caps (cairo_stroker_t *stroker)
     853                 : {
     854                 :     cairo_status_t status;
     855                 : 
     856                 :     /* check for a degenerative sub_path */
     857               0 :     if (stroker->has_initial_sub_path
     858               0 :         && ! stroker->has_first_face
     859               0 :         && ! stroker->has_current_face
     860               0 :         && stroker->style.line_cap == CAIRO_LINE_JOIN_ROUND)
     861                 :     {
     862                 :         /* pick an arbitrary slope to use */
     863               0 :         double dx = 1.0, dy = 0.0;
     864               0 :         cairo_slope_t slope = { CAIRO_FIXED_ONE, 0 };
     865                 :         cairo_stroke_face_t face;
     866                 : 
     867               0 :         _compute_normalized_device_slope (&dx, &dy,
     868                 :                                           stroker->ctm_inverse, NULL);
     869                 : 
     870                 :         /* arbitrarily choose first_point
     871                 :          * first_point and current_point should be the same */
     872               0 :         _compute_face (&stroker->first_point, &slope, dx, dy, stroker, &face);
     873                 : 
     874               0 :         status = _cairo_stroker_add_leading_cap (stroker, &face);
     875               0 :         if (unlikely (status))
     876               0 :             return status;
     877                 : 
     878               0 :         status = _cairo_stroker_add_trailing_cap (stroker, &face);
     879               0 :         if (unlikely (status))
     880               0 :             return status;
     881                 :     }
     882                 : 
     883               0 :     if (stroker->has_first_face) {
     884               0 :         status = _cairo_stroker_add_leading_cap (stroker,
     885               0 :                                                  &stroker->first_face);
     886               0 :         if (unlikely (status))
     887               0 :             return status;
     888                 :     }
     889                 : 
     890               0 :     if (stroker->has_current_face) {
     891               0 :         status = _cairo_stroker_add_trailing_cap (stroker,
     892               0 :                                                   &stroker->current_face);
     893               0 :         if (unlikely (status))
     894               0 :             return status;
     895                 :     }
     896                 : 
     897               0 :     return CAIRO_STATUS_SUCCESS;
     898                 : }
     899                 : 
     900                 : static cairo_status_t
     901               0 : _cairo_stroker_add_sub_edge (cairo_stroker_t *stroker,
     902                 :                              const cairo_point_t *p1,
     903                 :                              const cairo_point_t *p2,
     904                 :                              cairo_slope_t *dev_slope,
     905                 :                              double slope_dx, double slope_dy,
     906                 :                              cairo_stroke_face_t *start,
     907                 :                              cairo_stroke_face_t *end)
     908                 : {
     909               0 :     _compute_face (p1, dev_slope, slope_dx, slope_dy, stroker, start);
     910               0 :     *end = *start;
     911                 : 
     912               0 :     if (p1->x == p2->x && p1->y == p2->y)
     913               0 :         return CAIRO_STATUS_SUCCESS;
     914                 : 
     915               0 :     end->point = *p2;
     916               0 :     end->ccw.x += p2->x - p1->x;
     917               0 :     end->ccw.y += p2->y - p1->y;
     918               0 :     end->cw.x += p2->x - p1->x;
     919               0 :     end->cw.y += p2->y - p1->y;
     920                 : 
     921               0 :     if (stroker->add_external_edge != NULL) {
     922                 :         cairo_status_t status;
     923                 : 
     924               0 :         status = stroker->add_external_edge (stroker->closure,
     925               0 :                                              &end->cw, &start->cw);
     926               0 :         if (unlikely (status))
     927               0 :             return status;
     928                 : 
     929               0 :         status = stroker->add_external_edge (stroker->closure,
     930               0 :                                              &start->ccw, &end->ccw);
     931               0 :         if (unlikely (status))
     932               0 :             return status;
     933                 : 
     934               0 :         return CAIRO_STATUS_SUCCESS;
     935                 :     } else {
     936                 :         cairo_point_t quad[4];
     937                 : 
     938               0 :         quad[0] = start->cw;
     939               0 :         quad[1] = end->cw;
     940               0 :         quad[2] = end->ccw;
     941               0 :         quad[3] = start->ccw;
     942                 : 
     943               0 :         return stroker->add_convex_quad (stroker->closure, quad);
     944                 :     }
     945                 : }
     946                 : 
     947                 : static cairo_status_t
     948               0 : _cairo_stroker_move_to (void *closure,
     949                 :                         const cairo_point_t *point)
     950                 : {
     951               0 :     cairo_stroker_t *stroker = closure;
     952                 :     cairo_status_t status;
     953                 : 
     954                 :     /* reset the dash pattern for new sub paths */
     955               0 :     _cairo_stroker_dash_start (&stroker->dash);
     956                 : 
     957                 :     /* Cap the start and end of the previous sub path as needed */
     958               0 :     status = _cairo_stroker_add_caps (stroker);
     959               0 :     if (unlikely (status))
     960               0 :         return status;
     961                 : 
     962               0 :     stroker->first_point = *point;
     963               0 :     stroker->current_point = *point;
     964                 : 
     965               0 :     stroker->has_first_face = FALSE;
     966               0 :     stroker->has_current_face = FALSE;
     967               0 :     stroker->has_initial_sub_path = FALSE;
     968                 : 
     969               0 :     return CAIRO_STATUS_SUCCESS;
     970                 : }
     971                 : 
     972                 : static cairo_status_t
     973               0 : _cairo_stroker_line_to (void *closure,
     974                 :                         const cairo_point_t *point)
     975                 : {
     976               0 :     cairo_stroker_t *stroker = closure;
     977                 :     cairo_stroke_face_t start, end;
     978               0 :     cairo_point_t *p1 = &stroker->current_point;
     979                 :     cairo_slope_t dev_slope;
     980                 :     double slope_dx, slope_dy;
     981                 :     cairo_status_t status;
     982                 : 
     983               0 :     stroker->has_initial_sub_path = TRUE;
     984                 : 
     985               0 :     if (p1->x == point->x && p1->y == point->y)
     986               0 :         return CAIRO_STATUS_SUCCESS;
     987                 : 
     988               0 :     _cairo_slope_init (&dev_slope, p1, point);
     989               0 :     slope_dx = _cairo_fixed_to_double (point->x - p1->x);
     990               0 :     slope_dy = _cairo_fixed_to_double (point->y - p1->y);
     991               0 :     _compute_normalized_device_slope (&slope_dx, &slope_dy,
     992                 :                                       stroker->ctm_inverse, NULL);
     993                 : 
     994               0 :     status = _cairo_stroker_add_sub_edge (stroker,
     995                 :                                           p1, point,
     996                 :                                           &dev_slope,
     997                 :                                           slope_dx, slope_dy,
     998                 :                                           &start, &end);
     999               0 :     if (unlikely (status))
    1000               0 :         return status;
    1001                 : 
    1002               0 :     if (stroker->has_current_face) {
    1003                 :         /* Join with final face from previous segment */
    1004               0 :         status = _cairo_stroker_join (stroker,
    1005               0 :                                       &stroker->current_face,
    1006                 :                                       &start);
    1007               0 :         if (unlikely (status))
    1008               0 :             return status;
    1009               0 :     } else if (! stroker->has_first_face) {
    1010                 :         /* Save sub path's first face in case needed for closing join */
    1011               0 :         stroker->first_face = start;
    1012               0 :         stroker->has_first_face = TRUE;
    1013                 :     }
    1014               0 :     stroker->current_face = end;
    1015               0 :     stroker->has_current_face = TRUE;
    1016                 : 
    1017               0 :     stroker->current_point = *point;
    1018                 : 
    1019               0 :     return CAIRO_STATUS_SUCCESS;
    1020                 : }
    1021                 : 
    1022                 : /*
    1023                 :  * Dashed lines.  Cap each dash end, join around turns when on
    1024                 :  */
    1025                 : static cairo_status_t
    1026               0 : _cairo_stroker_line_to_dashed (void *closure,
    1027                 :                                const cairo_point_t *p2)
    1028                 : {
    1029               0 :     cairo_stroker_t *stroker = closure;
    1030               0 :     double mag, remain, step_length = 0;
    1031                 :     double slope_dx, slope_dy;
    1032                 :     double dx2, dy2;
    1033                 :     cairo_stroke_face_t sub_start, sub_end;
    1034               0 :     cairo_point_t *p1 = &stroker->current_point;
    1035                 :     cairo_slope_t dev_slope;
    1036                 :     cairo_line_t segment;
    1037                 :     cairo_bool_t fully_in_bounds;
    1038                 :     cairo_status_t status;
    1039                 : 
    1040               0 :     stroker->has_initial_sub_path = stroker->dash.dash_starts_on;
    1041                 : 
    1042               0 :     if (p1->x == p2->x && p1->y == p2->y)
    1043               0 :         return CAIRO_STATUS_SUCCESS;
    1044                 : 
    1045               0 :     fully_in_bounds = TRUE;
    1046               0 :     if (stroker->has_bounds &&
    1047               0 :         (! _cairo_box_contains_point (&stroker->bounds, p1) ||
    1048               0 :          ! _cairo_box_contains_point (&stroker->bounds, p2)))
    1049                 :     {
    1050               0 :         fully_in_bounds = FALSE;
    1051                 :     }
    1052                 : 
    1053               0 :     _cairo_slope_init (&dev_slope, p1, p2);
    1054                 : 
    1055               0 :     slope_dx = _cairo_fixed_to_double (p2->x - p1->x);
    1056               0 :     slope_dy = _cairo_fixed_to_double (p2->y - p1->y);
    1057                 : 
    1058               0 :     if (! _compute_normalized_device_slope (&slope_dx, &slope_dy,
    1059                 :                                             stroker->ctm_inverse, &mag))
    1060                 :     {
    1061               0 :         return CAIRO_STATUS_SUCCESS;
    1062                 :     }
    1063                 : 
    1064               0 :     remain = mag;
    1065               0 :     segment.p1 = *p1;
    1066               0 :     while (remain) {
    1067               0 :         step_length = MIN (stroker->dash.dash_remain, remain);
    1068               0 :         remain -= step_length;
    1069               0 :         dx2 = slope_dx * (mag - remain);
    1070               0 :         dy2 = slope_dy * (mag - remain);
    1071               0 :         cairo_matrix_transform_distance (stroker->ctm, &dx2, &dy2);
    1072               0 :         segment.p2.x = _cairo_fixed_from_double (dx2) + p1->x;
    1073               0 :         segment.p2.y = _cairo_fixed_from_double (dy2) + p1->y;
    1074                 : 
    1075               0 :         if (stroker->dash.dash_on &&
    1076               0 :             (fully_in_bounds ||
    1077               0 :              (! stroker->has_first_face && stroker->dash.dash_starts_on) ||
    1078               0 :              _cairo_box_intersects_line_segment (&stroker->bounds, &segment)))
    1079                 :         {
    1080               0 :             status = _cairo_stroker_add_sub_edge (stroker,
    1081                 :                                                   &segment.p1, &segment.p2,
    1082                 :                                                   &dev_slope,
    1083                 :                                                   slope_dx, slope_dy,
    1084                 :                                                   &sub_start, &sub_end);
    1085               0 :             if (unlikely (status))
    1086               0 :                 return status;
    1087                 : 
    1088               0 :             if (stroker->has_current_face)
    1089                 :             {
    1090                 :                 /* Join with final face from previous segment */
    1091               0 :                 status = _cairo_stroker_join (stroker,
    1092               0 :                                               &stroker->current_face,
    1093                 :                                               &sub_start);
    1094               0 :                 if (unlikely (status))
    1095               0 :                     return status;
    1096                 : 
    1097               0 :                 stroker->has_current_face = FALSE;
    1098                 :             }
    1099               0 :             else if (! stroker->has_first_face &&
    1100               0 :                        stroker->dash.dash_starts_on)
    1101                 :             {
    1102                 :                 /* Save sub path's first face in case needed for closing join */
    1103               0 :                 stroker->first_face = sub_start;
    1104               0 :                 stroker->has_first_face = TRUE;
    1105                 :             }
    1106                 :             else
    1107                 :             {
    1108                 :                 /* Cap dash start if not connecting to a previous segment */
    1109               0 :                 status = _cairo_stroker_add_leading_cap (stroker, &sub_start);
    1110               0 :                 if (unlikely (status))
    1111               0 :                     return status;
    1112                 :             }
    1113                 : 
    1114               0 :             if (remain) {
    1115                 :                 /* Cap dash end if not at end of segment */
    1116               0 :                 status = _cairo_stroker_add_trailing_cap (stroker, &sub_end);
    1117               0 :                 if (unlikely (status))
    1118               0 :                     return status;
    1119                 :             } else {
    1120               0 :                 stroker->current_face = sub_end;
    1121               0 :                 stroker->has_current_face = TRUE;
    1122                 :             }
    1123                 :         } else {
    1124               0 :             if (stroker->has_current_face) {
    1125                 :                 /* Cap final face from previous segment */
    1126               0 :                 status = _cairo_stroker_add_trailing_cap (stroker,
    1127               0 :                                                           &stroker->current_face);
    1128               0 :                 if (unlikely (status))
    1129               0 :                     return status;
    1130                 : 
    1131               0 :                 stroker->has_current_face = FALSE;
    1132                 :             }
    1133                 :         }
    1134                 : 
    1135               0 :         _cairo_stroker_dash_step (&stroker->dash, step_length);
    1136               0 :         segment.p1 = segment.p2;
    1137                 :     }
    1138                 : 
    1139               0 :     if (stroker->dash.dash_on && ! stroker->has_current_face) {
    1140                 :         /* This segment ends on a transition to dash_on, compute a new face
    1141                 :          * and add cap for the beginning of the next dash_on step.
    1142                 :          *
    1143                 :          * Note: this will create a degenerate cap if this is not the last line
    1144                 :          * in the path. Whether this behaviour is desirable or not is debatable.
    1145                 :          * On one side these degenerate caps can not be reproduced with regular
    1146                 :          * path stroking.
    1147                 :          * On the other hand, Acroread 7 also produces the degenerate caps.
    1148                 :          */
    1149               0 :         _compute_face (p2, &dev_slope,
    1150                 :                        slope_dx, slope_dy,
    1151                 :                        stroker,
    1152                 :                        &stroker->current_face);
    1153                 : 
    1154               0 :         status = _cairo_stroker_add_leading_cap (stroker,
    1155               0 :                                                  &stroker->current_face);
    1156               0 :         if (unlikely (status))
    1157               0 :             return status;
    1158                 : 
    1159               0 :         stroker->has_current_face = TRUE;
    1160                 :     }
    1161                 : 
    1162               0 :     stroker->current_point = *p2;
    1163                 : 
    1164               0 :     return CAIRO_STATUS_SUCCESS;
    1165                 : }
    1166                 : 
    1167                 : static cairo_status_t
    1168               0 : _cairo_stroker_curve_to (void *closure,
    1169                 :                          const cairo_point_t *b,
    1170                 :                          const cairo_point_t *c,
    1171                 :                          const cairo_point_t *d)
    1172                 : {
    1173               0 :     cairo_stroker_t *stroker = closure;
    1174                 :     cairo_spline_t spline;
    1175                 :     cairo_line_join_t line_join_save;
    1176                 :     cairo_stroke_face_t face;
    1177                 :     double slope_dx, slope_dy;
    1178                 :     cairo_path_fixed_line_to_func_t *line_to;
    1179               0 :     cairo_status_t status = CAIRO_STATUS_SUCCESS;
    1180                 : 
    1181               0 :     line_to = stroker->dash.dashed ?
    1182                 :         _cairo_stroker_line_to_dashed :
    1183                 :         _cairo_stroker_line_to;
    1184                 : 
    1185               0 :     if (! _cairo_spline_init (&spline,
    1186                 :                               line_to, stroker,
    1187               0 :                               &stroker->current_point, b, c, d))
    1188                 :     {
    1189               0 :         return line_to (closure, d);
    1190                 :     }
    1191                 : 
    1192                 :     /* If the line width is so small that the pen is reduced to a
    1193                 :        single point, then we have nothing to do. */
    1194               0 :     if (stroker->pen.num_vertices <= 1)
    1195               0 :         return CAIRO_STATUS_SUCCESS;
    1196                 : 
    1197                 :     /* Compute the initial face */
    1198               0 :     if (! stroker->dash.dashed || stroker->dash.dash_on) {
    1199               0 :         slope_dx = _cairo_fixed_to_double (spline.initial_slope.dx);
    1200               0 :         slope_dy = _cairo_fixed_to_double (spline.initial_slope.dy);
    1201               0 :         if (_compute_normalized_device_slope (&slope_dx, &slope_dy,
    1202                 :                                               stroker->ctm_inverse, NULL))
    1203                 :         {
    1204               0 :             _compute_face (&stroker->current_point,
    1205                 :                            &spline.initial_slope,
    1206                 :                            slope_dx, slope_dy,
    1207                 :                            stroker, &face);
    1208                 :         }
    1209               0 :         if (stroker->has_current_face) {
    1210               0 :             status = _cairo_stroker_join (stroker,
    1211               0 :                                           &stroker->current_face, &face);
    1212               0 :             if (unlikely (status))
    1213               0 :                 return status;
    1214               0 :         } else if (! stroker->has_first_face) {
    1215               0 :             stroker->first_face = face;
    1216               0 :             stroker->has_first_face = TRUE;
    1217                 :         }
    1218                 : 
    1219               0 :         stroker->current_face = face;
    1220               0 :         stroker->has_current_face = TRUE;
    1221                 :     }
    1222                 : 
    1223                 :     /* Temporarily modify the stroker to use round joins to guarantee
    1224                 :      * smooth stroked curves. */
    1225               0 :     line_join_save = stroker->style.line_join;
    1226               0 :     stroker->style.line_join = CAIRO_LINE_JOIN_ROUND;
    1227                 : 
    1228               0 :     status = _cairo_spline_decompose (&spline, stroker->tolerance);
    1229               0 :     if (unlikely (status))
    1230               0 :         return status;
    1231                 : 
    1232                 :     /* And join the final face */
    1233               0 :     if (! stroker->dash.dashed || stroker->dash.dash_on) {
    1234               0 :         slope_dx = _cairo_fixed_to_double (spline.final_slope.dx);
    1235               0 :         slope_dy = _cairo_fixed_to_double (spline.final_slope.dy);
    1236               0 :         if (_compute_normalized_device_slope (&slope_dx, &slope_dy,
    1237                 :                                               stroker->ctm_inverse, NULL))
    1238                 :         {
    1239               0 :             _compute_face (&stroker->current_point,
    1240                 :                            &spline.final_slope,
    1241                 :                            slope_dx, slope_dy,
    1242                 :                            stroker, &face);
    1243                 :         }
    1244                 : 
    1245               0 :         status = _cairo_stroker_join (stroker, &stroker->current_face, &face);
    1246               0 :         if (unlikely (status))
    1247               0 :             return status;
    1248                 : 
    1249               0 :         stroker->current_face = face;
    1250                 :     }
    1251                 : 
    1252               0 :     stroker->style.line_join = line_join_save;
    1253                 : 
    1254               0 :     return CAIRO_STATUS_SUCCESS;
    1255                 : }
    1256                 : 
    1257                 : static cairo_status_t
    1258               0 : _cairo_stroker_close_path (void *closure)
    1259                 : {
    1260               0 :     cairo_stroker_t *stroker = closure;
    1261                 :     cairo_status_t status;
    1262                 : 
    1263               0 :     if (stroker->dash.dashed)
    1264               0 :         status = _cairo_stroker_line_to_dashed (stroker, &stroker->first_point);
    1265                 :     else
    1266               0 :         status = _cairo_stroker_line_to (stroker, &stroker->first_point);
    1267               0 :     if (unlikely (status))
    1268               0 :         return status;
    1269                 : 
    1270               0 :     if (stroker->has_first_face && stroker->has_current_face) {
    1271                 :         /* Join first and final faces of sub path */
    1272               0 :         status = _cairo_stroker_join (stroker,
    1273               0 :                                       &stroker->current_face,
    1274               0 :                                       &stroker->first_face);
    1275               0 :         if (unlikely (status))
    1276               0 :             return status;
    1277                 :     } else {
    1278                 :         /* Cap the start and end of the sub path as needed */
    1279               0 :         status = _cairo_stroker_add_caps (stroker);
    1280               0 :         if (unlikely (status))
    1281               0 :             return status;
    1282                 :     }
    1283                 : 
    1284               0 :     stroker->has_initial_sub_path = FALSE;
    1285               0 :     stroker->has_first_face = FALSE;
    1286               0 :     stroker->has_current_face = FALSE;
    1287                 : 
    1288               0 :     return CAIRO_STATUS_SUCCESS;
    1289                 : }
    1290                 : 
    1291                 : cairo_status_t
    1292               0 : _cairo_path_fixed_stroke_to_shaper (cairo_path_fixed_t  *path,
    1293                 :                                     const cairo_stroke_style_t  *stroke_style,
    1294                 :                                     const cairo_matrix_t        *ctm,
    1295                 :                                     const cairo_matrix_t        *ctm_inverse,
    1296                 :                                     double               tolerance,
    1297                 :                                     cairo_status_t (*add_triangle) (void *closure,
    1298                 :                                                                     const cairo_point_t triangle[3]),
    1299                 :                                     cairo_status_t (*add_triangle_fan) (void *closure,
    1300                 :                                                                         const cairo_point_t *midpt,
    1301                 :                                                                         const cairo_point_t *points,
    1302                 :                                                                         int npoints),
    1303                 :                                     cairo_status_t (*add_convex_quad) (void *closure,
    1304                 :                                                                        const cairo_point_t quad[4]),
    1305                 :                                     void *closure)
    1306                 : {
    1307                 :     cairo_stroker_t stroker;
    1308                 :     cairo_status_t status;
    1309                 : 
    1310               0 :     status = _cairo_stroker_init (&stroker, stroke_style,
    1311                 :                                   ctm, ctm_inverse, tolerance);
    1312               0 :     if (unlikely (status))
    1313               0 :         return status;
    1314                 : 
    1315               0 :     stroker.add_triangle = add_triangle;
    1316               0 :     stroker.add_triangle_fan = add_triangle_fan;
    1317               0 :     stroker.add_convex_quad = add_convex_quad;
    1318               0 :     stroker.closure = closure;
    1319                 : 
    1320               0 :     status = _cairo_path_fixed_interpret (path,
    1321                 :                                           CAIRO_DIRECTION_FORWARD,
    1322                 :                                           _cairo_stroker_move_to,
    1323               0 :                                           stroker.dash.dashed ?
    1324                 :                                           _cairo_stroker_line_to_dashed :
    1325                 :                                           _cairo_stroker_line_to,
    1326                 :                                           _cairo_stroker_curve_to,
    1327                 :                                           _cairo_stroker_close_path,
    1328                 :                                           &stroker);
    1329                 : 
    1330               0 :     if (unlikely (status))
    1331               0 :         goto BAIL;
    1332                 : 
    1333                 :     /* Cap the start and end of the final sub path as needed */
    1334               0 :     status = _cairo_stroker_add_caps (&stroker);
    1335                 : 
    1336                 : BAIL:
    1337               0 :     _cairo_stroker_fini (&stroker);
    1338                 : 
    1339               0 :     return status;
    1340                 : }
    1341                 : 
    1342                 : cairo_status_t
    1343               0 : _cairo_path_fixed_stroke_to_polygon (const cairo_path_fixed_t   *path,
    1344                 :                                      const cairo_stroke_style_t *stroke_style,
    1345                 :                                      const cairo_matrix_t       *ctm,
    1346                 :                                      const cairo_matrix_t       *ctm_inverse,
    1347                 :                                      double              tolerance,
    1348                 :                                      cairo_polygon_t *polygon)
    1349                 : {
    1350                 :     cairo_stroker_t stroker;
    1351                 :     cairo_status_t status;
    1352                 : 
    1353               0 :     status = _cairo_stroker_init (&stroker, stroke_style,
    1354                 :                                   ctm, ctm_inverse, tolerance);
    1355               0 :     if (unlikely (status))
    1356               0 :         return status;
    1357                 : 
    1358               0 :     stroker.add_external_edge = _cairo_polygon_add_external_edge,
    1359               0 :     stroker.closure = polygon;
    1360                 : 
    1361               0 :     if (polygon->num_limits)
    1362               0 :         _cairo_stroker_limit (&stroker, polygon->limits, polygon->num_limits);
    1363                 : 
    1364               0 :     status = _cairo_path_fixed_interpret (path,
    1365                 :                                           CAIRO_DIRECTION_FORWARD,
    1366                 :                                           _cairo_stroker_move_to,
    1367               0 :                                           stroker.dash.dashed ?
    1368                 :                                           _cairo_stroker_line_to_dashed :
    1369                 :                                           _cairo_stroker_line_to,
    1370                 :                                           _cairo_stroker_curve_to,
    1371                 :                                           _cairo_stroker_close_path,
    1372                 :                                           &stroker);
    1373                 : 
    1374               0 :     if (unlikely (status))
    1375               0 :         goto BAIL;
    1376                 : 
    1377                 :     /* Cap the start and end of the final sub path as needed */
    1378               0 :     status = _cairo_stroker_add_caps (&stroker);
    1379                 : 
    1380                 : BAIL:
    1381               0 :     _cairo_stroker_fini (&stroker);
    1382                 : 
    1383               0 :     return status;
    1384                 : }
    1385                 : 
    1386                 : cairo_status_t
    1387               0 : _cairo_path_fixed_stroke_to_traps (const cairo_path_fixed_t     *path,
    1388                 :                                    const cairo_stroke_style_t   *stroke_style,
    1389                 :                                    const cairo_matrix_t *ctm,
    1390                 :                                    const cairo_matrix_t *ctm_inverse,
    1391                 :                                    double                tolerance,
    1392                 :                                    cairo_traps_t        *traps)
    1393                 : {
    1394                 :     cairo_status_t status;
    1395                 :     cairo_polygon_t polygon;
    1396                 : 
    1397                 :     /* Before we do anything else, we attempt the rectilinear
    1398                 :      * stroker. It's careful to generate trapezoids that align to
    1399                 :      * device-pixel boundaries when possible. Many backends can render
    1400                 :      * those much faster than non-aligned trapezoids, (by using clip
    1401                 :      * regions, etc.) */
    1402               0 :     if (path->is_rectilinear) {
    1403               0 :         status = _cairo_path_fixed_stroke_rectilinear_to_traps (path,
    1404                 :                                                                 stroke_style,
    1405                 :                                                                 ctm,
    1406                 :                                                                 traps);
    1407               0 :         if (status != CAIRO_INT_STATUS_UNSUPPORTED)
    1408               0 :             return status;
    1409                 :     }
    1410                 : 
    1411               0 :     _cairo_polygon_init (&polygon);
    1412               0 :     if (traps->num_limits)
    1413               0 :         _cairo_polygon_limit (&polygon, traps->limits, traps->num_limits);
    1414                 : 
    1415               0 :     status = _cairo_path_fixed_stroke_to_polygon (path,
    1416                 :                                                   stroke_style,
    1417                 :                                                   ctm,
    1418                 :                                                   ctm_inverse,
    1419                 :                                                   tolerance,
    1420                 :                                                   &polygon);
    1421               0 :     if (unlikely (status))
    1422               0 :         goto BAIL;
    1423                 : 
    1424               0 :     status = _cairo_polygon_status (&polygon);
    1425               0 :     if (unlikely (status))
    1426               0 :         goto BAIL;
    1427                 : 
    1428               0 :     status = _cairo_bentley_ottmann_tessellate_polygon (traps, &polygon,
    1429                 :                                                         CAIRO_FILL_RULE_WINDING);
    1430                 : 
    1431                 : BAIL:
    1432               0 :     _cairo_polygon_fini (&polygon);
    1433                 : 
    1434               0 :     return status;
    1435                 : }
    1436                 : 
    1437                 : typedef struct _segment_t {
    1438                 :     cairo_point_t p1, p2;
    1439                 :     cairo_bool_t is_horizontal;
    1440                 :     cairo_bool_t has_join;
    1441                 : } segment_t;
    1442                 : 
    1443                 : typedef struct _cairo_rectilinear_stroker {
    1444                 :     const cairo_stroke_style_t *stroke_style;
    1445                 :     const cairo_matrix_t *ctm;
    1446                 : 
    1447                 :     cairo_fixed_t half_line_width;
    1448                 :     cairo_bool_t do_traps;
    1449                 :     void *container;
    1450                 :     cairo_point_t current_point;
    1451                 :     cairo_point_t first_point;
    1452                 :     cairo_bool_t open_sub_path;
    1453                 : 
    1454                 :     cairo_stroker_dash_t dash;
    1455                 : 
    1456                 :     cairo_bool_t has_bounds;
    1457                 :     cairo_box_t bounds;
    1458                 : 
    1459                 :     int num_segments;
    1460                 :     int segments_size;
    1461                 :     segment_t *segments;
    1462                 :     segment_t segments_embedded[8]; /* common case is a single rectangle */
    1463                 : } cairo_rectilinear_stroker_t;
    1464                 : 
    1465                 : static void
    1466               0 : _cairo_rectilinear_stroker_limit (cairo_rectilinear_stroker_t *stroker,
    1467                 :                                   const cairo_box_t *boxes,
    1468                 :                                   int num_boxes)
    1469                 : {
    1470               0 :     stroker->has_bounds = TRUE;
    1471               0 :     _cairo_boxes_get_extents (boxes, num_boxes, &stroker->bounds);
    1472                 : 
    1473               0 :     stroker->bounds.p1.x -= stroker->half_line_width;
    1474               0 :     stroker->bounds.p2.x += stroker->half_line_width;
    1475                 : 
    1476               0 :     stroker->bounds.p1.y -= stroker->half_line_width;
    1477               0 :     stroker->bounds.p2.y += stroker->half_line_width;
    1478               0 : }
    1479                 : 
    1480                 : static cairo_bool_t
    1481               0 : _cairo_rectilinear_stroker_init (cairo_rectilinear_stroker_t    *stroker,
    1482                 :                                  const cairo_stroke_style_t     *stroke_style,
    1483                 :                                  const cairo_matrix_t           *ctm,
    1484                 :                                  cairo_bool_t                    do_traps,
    1485                 :                                  void                           *container)
    1486                 : {
    1487                 :     /* This special-case rectilinear stroker only supports
    1488                 :      * miter-joined lines (not curves) and a translation-only matrix
    1489                 :      * (though it could probably be extended to support a matrix with
    1490                 :      * uniform, integer scaling).
    1491                 :      *
    1492                 :      * It also only supports horizontal and vertical line_to
    1493                 :      * elements. But we don't catch that here, but instead return
    1494                 :      * UNSUPPORTED from _cairo_rectilinear_stroker_line_to if any
    1495                 :      * non-rectilinear line_to is encountered.
    1496                 :      */
    1497               0 :     if (stroke_style->line_join      != CAIRO_LINE_JOIN_MITER)
    1498               0 :         return FALSE;
    1499                 : 
    1500                 :     /* If the miter limit turns right angles into bevels, then we
    1501                 :      * can't use this optimization. Remember, the ratio is
    1502                 :      * 1/sin(ɸ/2). So the cutoff is 1/sin(π/4.0) or ⎷2,
    1503                 :      * which we round for safety. */
    1504               0 :     if (stroke_style->miter_limit < M_SQRT2)
    1505               0 :         return FALSE;
    1506                 : 
    1507               0 :     if (! (stroke_style->line_cap == CAIRO_LINE_CAP_BUTT ||
    1508               0 :            stroke_style->line_cap == CAIRO_LINE_CAP_SQUARE))
    1509                 :     {
    1510               0 :         return FALSE;
    1511                 :     }
    1512                 : 
    1513               0 :     if (! _cairo_matrix_has_unity_scale (ctm))
    1514               0 :         return FALSE;
    1515                 : 
    1516               0 :     stroker->stroke_style = stroke_style;
    1517               0 :     stroker->ctm = ctm;
    1518                 : 
    1519               0 :     stroker->half_line_width =
    1520               0 :         _cairo_fixed_from_double (stroke_style->line_width / 2.0);
    1521               0 :     stroker->open_sub_path = FALSE;
    1522               0 :     stroker->segments = stroker->segments_embedded;
    1523               0 :     stroker->segments_size = ARRAY_LENGTH (stroker->segments_embedded);
    1524               0 :     stroker->num_segments = 0;
    1525                 : 
    1526               0 :     _cairo_stroker_dash_init (&stroker->dash, stroke_style);
    1527                 : 
    1528               0 :     stroker->has_bounds = FALSE;
    1529                 : 
    1530               0 :     stroker->do_traps = do_traps;
    1531               0 :     stroker->container = container;
    1532                 : 
    1533               0 :     return TRUE;
    1534                 : }
    1535                 : 
    1536                 : static void
    1537               0 : _cairo_rectilinear_stroker_fini (cairo_rectilinear_stroker_t    *stroker)
    1538                 : {
    1539               0 :     if (stroker->segments != stroker->segments_embedded)
    1540               0 :         free (stroker->segments);
    1541               0 : }
    1542                 : 
    1543                 : static cairo_status_t
    1544               0 : _cairo_rectilinear_stroker_add_segment (cairo_rectilinear_stroker_t *stroker,
    1545                 :                                         const cairo_point_t     *p1,
    1546                 :                                         const cairo_point_t     *p2,
    1547                 :                                         cairo_bool_t             is_horizontal,
    1548                 :                                         cairo_bool_t             has_join)
    1549                 : {
    1550                 :     if (CAIRO_INJECT_FAULT ())
    1551                 :         return _cairo_error (CAIRO_STATUS_NO_MEMORY);
    1552                 : 
    1553               0 :     if (stroker->num_segments == stroker->segments_size) {
    1554               0 :         int new_size = stroker->segments_size * 2;
    1555                 :         segment_t *new_segments;
    1556                 : 
    1557               0 :         if (stroker->segments == stroker->segments_embedded) {
    1558               0 :             new_segments = _cairo_malloc_ab (new_size, sizeof (segment_t));
    1559               0 :             if (unlikely (new_segments == NULL))
    1560               0 :                 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
    1561                 : 
    1562               0 :             memcpy (new_segments, stroker->segments,
    1563               0 :                     stroker->num_segments * sizeof (segment_t));
    1564                 :         } else {
    1565               0 :             new_segments = _cairo_realloc_ab (stroker->segments,
    1566                 :                                               new_size, sizeof (segment_t));
    1567               0 :             if (unlikely (new_segments == NULL))
    1568               0 :                 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
    1569                 :         }
    1570                 : 
    1571               0 :         stroker->segments_size = new_size;
    1572               0 :         stroker->segments = new_segments;
    1573                 :     }
    1574                 : 
    1575               0 :     stroker->segments[stroker->num_segments].p1 = *p1;
    1576               0 :     stroker->segments[stroker->num_segments].p2 = *p2;
    1577               0 :     stroker->segments[stroker->num_segments].has_join = has_join;
    1578               0 :     stroker->segments[stroker->num_segments].is_horizontal = is_horizontal;
    1579               0 :     stroker->num_segments++;
    1580                 : 
    1581               0 :     return CAIRO_STATUS_SUCCESS;
    1582                 : }
    1583                 : 
    1584                 : static cairo_status_t
    1585               0 : _cairo_rectilinear_stroker_emit_segments (cairo_rectilinear_stroker_t *stroker)
    1586                 : {
    1587                 :     cairo_status_t status;
    1588               0 :     cairo_line_cap_t line_cap = stroker->stroke_style->line_cap;
    1589               0 :     cairo_fixed_t half_line_width = stroker->half_line_width;
    1590                 :     int i;
    1591                 : 
    1592               0 :     for (i = 0; i < stroker->num_segments; i++) {
    1593                 :         cairo_point_t *a, *b;
    1594                 :         cairo_bool_t lengthen_initial, shorten_final, lengthen_final;
    1595                 : 
    1596               0 :         a = &stroker->segments[i].p1;
    1597               0 :         b = &stroker->segments[i].p2;
    1598                 : 
    1599                 :         /* For each segment we generate a single rectangular
    1600                 :          * trapezoid. This rectangle is based on a perpendicular
    1601                 :          * extension (by half the line width) of the segment endpoints
    1602                 :          * after some adjustments of the endpoints to account for caps
    1603                 :          * and joins.
    1604                 :          */
    1605                 : 
    1606                 :         /* We adjust the initial point of the segment to extend the
    1607                 :          * rectangle to include the previous cap or join, (this
    1608                 :          * adjustment applies to all segments except for the first
    1609                 :          * segment of open, butt-capped paths).
    1610                 :          */
    1611               0 :         lengthen_initial = TRUE;
    1612               0 :         if (i == 0 && stroker->open_sub_path && line_cap == CAIRO_LINE_CAP_BUTT)
    1613               0 :             lengthen_initial = FALSE;
    1614                 : 
    1615                 :         /* The adjustment of the final point is trickier. For all but
    1616                 :          * the last segment we shorten the segment at the final
    1617                 :          * endpoint to not overlap with the subsequent join. For the
    1618                 :          * last segment we do the same shortening if the path is
    1619                 :          * closed. If the path is open and butt-capped we do no
    1620                 :          * adjustment, while if it's open and square-capped we do a
    1621                 :          * lengthening adjustment instead to include the cap.
    1622                 :          */
    1623               0 :         shorten_final = TRUE;
    1624               0 :         lengthen_final = FALSE;
    1625               0 :         if (i == stroker->num_segments - 1 && stroker->open_sub_path) {
    1626               0 :             shorten_final = FALSE;
    1627               0 :             if (line_cap == CAIRO_LINE_CAP_SQUARE)
    1628               0 :                 lengthen_final = TRUE;
    1629                 :         }
    1630                 : 
    1631                 :         /* Perform the adjustments of the endpoints. */
    1632               0 :         if (a->y == b->y) {
    1633               0 :             if (a->x < b->x) {
    1634               0 :                 if (lengthen_initial)
    1635               0 :                     a->x -= half_line_width;
    1636               0 :                 if (shorten_final)
    1637               0 :                     b->x -= half_line_width;
    1638               0 :                 else if (lengthen_final)
    1639               0 :                     b->x += half_line_width;
    1640                 :             } else {
    1641               0 :                 if (lengthen_initial)
    1642               0 :                     a->x += half_line_width;
    1643               0 :                 if (shorten_final)
    1644               0 :                     b->x += half_line_width;
    1645               0 :                 else if (lengthen_final)
    1646               0 :                     b->x -= half_line_width;
    1647                 :             }
    1648                 : 
    1649               0 :             if (a->x > b->x) {
    1650                 :                 cairo_point_t *t;
    1651                 : 
    1652               0 :                 t = a;
    1653               0 :                 a = b;
    1654               0 :                 b = t;
    1655                 :             }
    1656                 :         } else {
    1657               0 :             if (a->y < b->y) {
    1658               0 :                 if (lengthen_initial)
    1659               0 :                     a->y -= half_line_width;
    1660               0 :                 if (shorten_final)
    1661               0 :                     b->y -= half_line_width;
    1662               0 :                 else if (lengthen_final)
    1663               0 :                     b->y += half_line_width;
    1664                 :             } else {
    1665               0 :                 if (lengthen_initial)
    1666               0 :                     a->y += half_line_width;
    1667               0 :                 if (shorten_final)
    1668               0 :                     b->y += half_line_width;
    1669               0 :                 else if (lengthen_final)
    1670               0 :                     b->y -= half_line_width;
    1671                 :             }
    1672                 : 
    1673               0 :             if (a->y > b->y) {
    1674                 :                 cairo_point_t *t;
    1675                 : 
    1676               0 :                 t = a;
    1677               0 :                 a = b;
    1678               0 :                 b = t;
    1679                 :             }
    1680                 :         }
    1681                 : 
    1682                 :         /* Form the rectangle by expanding by half the line width in
    1683                 :          * either perpendicular direction. */
    1684               0 :         if (a->y == b->y) {
    1685               0 :             a->y -= half_line_width;
    1686               0 :             b->y += half_line_width;
    1687                 :         } else {
    1688               0 :             a->x -= half_line_width;
    1689               0 :             b->x += half_line_width;
    1690                 :         }
    1691                 : 
    1692               0 :         if (stroker->do_traps) {
    1693               0 :             status = _cairo_traps_tessellate_rectangle (stroker->container, a, b);
    1694                 :         } else {
    1695                 :             cairo_box_t box;
    1696                 : 
    1697               0 :             box.p1 = *a;
    1698               0 :             box.p2 = *b;
    1699                 : 
    1700               0 :             status = _cairo_boxes_add (stroker->container, &box);
    1701                 :         }
    1702               0 :         if (unlikely (status))
    1703               0 :             return status;
    1704                 :     }
    1705                 : 
    1706               0 :     stroker->num_segments = 0;
    1707                 : 
    1708               0 :     return CAIRO_STATUS_SUCCESS;
    1709                 : }
    1710                 : 
    1711                 : static cairo_status_t
    1712               0 : _cairo_rectilinear_stroker_emit_segments_dashed (cairo_rectilinear_stroker_t *stroker)
    1713                 : {
    1714                 :     cairo_status_t status;
    1715               0 :     cairo_line_cap_t line_cap = stroker->stroke_style->line_cap;
    1716               0 :     cairo_fixed_t half_line_width = stroker->half_line_width;
    1717                 :     int i;
    1718                 : 
    1719               0 :     for (i = 0; i < stroker->num_segments; i++) {
    1720                 :         cairo_point_t *a, *b;
    1721                 :         cairo_bool_t is_horizontal;
    1722                 : 
    1723               0 :         a = &stroker->segments[i].p1;
    1724               0 :         b = &stroker->segments[i].p2;
    1725                 : 
    1726               0 :         is_horizontal = stroker->segments[i].is_horizontal;
    1727                 : 
    1728                 :         /* Handle the joins for a potentially degenerate segment. */
    1729               0 :         if (line_cap == CAIRO_LINE_CAP_BUTT &&
    1730               0 :             stroker->segments[i].has_join &&
    1731               0 :             (i != stroker->num_segments - 1 ||
    1732               0 :              (! stroker->open_sub_path && stroker->dash.dash_starts_on)))
    1733                 :         {
    1734               0 :             cairo_point_t p1 = stroker->segments[i].p1;
    1735               0 :             cairo_point_t p2 = stroker->segments[i].p2;
    1736                 :             cairo_slope_t out_slope;
    1737               0 :             int j = (i + 1) % stroker->num_segments;
    1738                 : 
    1739               0 :             _cairo_slope_init (&out_slope,
    1740               0 :                                &stroker->segments[j].p1,
    1741               0 :                                &stroker->segments[j].p2);
    1742                 : 
    1743               0 :             if (is_horizontal) {
    1744               0 :                 if (p1.x <= p2.x) {
    1745               0 :                     p1.x = p2.x;
    1746               0 :                     p2.x += half_line_width;
    1747                 :                 } else {
    1748               0 :                     p1.x = p2.x - half_line_width;
    1749                 :                 }
    1750               0 :                 if (out_slope.dy >= 0)
    1751               0 :                     p1.y -= half_line_width;
    1752               0 :                 if (out_slope.dy <= 0)
    1753               0 :                     p2.y += half_line_width;
    1754                 :             } else {
    1755               0 :                 if (p1.y <= p2.y) {
    1756               0 :                     p1.y = p2.y;
    1757               0 :                     p2.y += half_line_width;
    1758                 :                 } else {
    1759               0 :                     p1.y = p2.y - half_line_width;
    1760                 :                 }
    1761               0 :                 if (out_slope.dx >= 0)
    1762               0 :                     p1.x -= half_line_width;
    1763               0 :                 if (out_slope.dx <= 0)
    1764               0 :                     p2.x += half_line_width;
    1765                 :             }
    1766                 : 
    1767               0 :             if (stroker->do_traps) {
    1768               0 :                 status = _cairo_traps_tessellate_rectangle (stroker->container, &p1, &p2);
    1769                 :             } else {
    1770                 :                 cairo_box_t box;
    1771                 : 
    1772               0 :                 box.p1 = p1;
    1773               0 :                 box.p2 = p2;
    1774                 : 
    1775               0 :                 status = _cairo_boxes_add (stroker->container, &box);
    1776                 :             }
    1777               0 :             if (unlikely (status))
    1778               0 :                 return status;
    1779                 :         }
    1780                 : 
    1781                 :         /* Perform the adjustments of the endpoints. */
    1782               0 :         if (is_horizontal) {
    1783               0 :             if (line_cap == CAIRO_LINE_CAP_SQUARE) {
    1784               0 :                 if (a->x <= b->x) {
    1785               0 :                     a->x -= half_line_width;
    1786               0 :                     b->x += half_line_width;
    1787                 :                 } else {
    1788               0 :                     a->x += half_line_width;
    1789               0 :                     b->x -= half_line_width;
    1790                 :                 }
    1791                 :             }
    1792                 : 
    1793               0 :             if (a->x > b->x) {
    1794                 :                 cairo_point_t *t;
    1795                 : 
    1796               0 :                 t = a;
    1797               0 :                 a = b;
    1798               0 :                 b = t;
    1799                 :             }
    1800                 : 
    1801               0 :             a->y -= half_line_width;
    1802               0 :             b->y += half_line_width;
    1803                 :         } else {
    1804               0 :             if (line_cap == CAIRO_LINE_CAP_SQUARE) {
    1805               0 :                 if (a->y <= b->y) {
    1806               0 :                     a->y -= half_line_width;
    1807               0 :                     b->y += half_line_width;
    1808                 :                 } else {
    1809               0 :                     a->y += half_line_width;
    1810               0 :                     b->y -= half_line_width;
    1811                 :                 }
    1812                 :             }
    1813                 : 
    1814               0 :             if (a->y > b->y) {
    1815                 :                 cairo_point_t *t;
    1816                 : 
    1817               0 :                 t = a;
    1818               0 :                 a = b;
    1819               0 :                 b = t;
    1820                 :             }
    1821                 : 
    1822               0 :             a->x -= half_line_width;
    1823               0 :             b->x += half_line_width;
    1824                 :         }
    1825                 : 
    1826               0 :         if (a->x == b->x && a->y == b->y)
    1827               0 :             continue;
    1828                 : 
    1829               0 :         if (stroker->do_traps) {
    1830               0 :             status = _cairo_traps_tessellate_rectangle (stroker->container, a, b);
    1831                 :         } else {
    1832                 :             cairo_box_t box;
    1833                 : 
    1834               0 :             box.p1 = *a;
    1835               0 :             box.p2 = *b;
    1836                 : 
    1837               0 :             status = _cairo_boxes_add (stroker->container, &box);
    1838                 :         }
    1839               0 :         if (unlikely (status))
    1840               0 :             return status;
    1841                 :     }
    1842                 : 
    1843               0 :     stroker->num_segments = 0;
    1844                 : 
    1845               0 :     return CAIRO_STATUS_SUCCESS;
    1846                 : }
    1847                 : 
    1848                 : static cairo_status_t
    1849               0 : _cairo_rectilinear_stroker_move_to (void                *closure,
    1850                 :                                     const cairo_point_t *point)
    1851                 : {
    1852               0 :     cairo_rectilinear_stroker_t *stroker = closure;
    1853                 :     cairo_status_t status;
    1854                 : 
    1855               0 :     if (stroker->dash.dashed)
    1856               0 :         status = _cairo_rectilinear_stroker_emit_segments_dashed (stroker);
    1857                 :     else
    1858               0 :         status = _cairo_rectilinear_stroker_emit_segments (stroker);
    1859               0 :     if (unlikely (status))
    1860               0 :         return status;
    1861                 : 
    1862                 :     /* reset the dash pattern for new sub paths */
    1863               0 :     _cairo_stroker_dash_start (&stroker->dash);
    1864                 : 
    1865               0 :     stroker->current_point = *point;
    1866               0 :     stroker->first_point = *point;
    1867                 : 
    1868               0 :     return CAIRO_STATUS_SUCCESS;
    1869                 : }
    1870                 : 
    1871                 : static cairo_status_t
    1872               0 : _cairo_rectilinear_stroker_line_to (void                *closure,
    1873                 :                                     const cairo_point_t *b)
    1874                 : {
    1875               0 :     cairo_rectilinear_stroker_t *stroker = closure;
    1876               0 :     cairo_point_t *a = &stroker->current_point;
    1877                 :     cairo_status_t status;
    1878                 : 
    1879                 :     /* We only support horizontal or vertical elements. */
    1880               0 :     assert (a->x == b->x || a->y == b->y);
    1881                 : 
    1882                 :     /* We don't draw anything for degenerate paths. */
    1883               0 :     if (a->x == b->x && a->y == b->y)
    1884               0 :         return CAIRO_STATUS_SUCCESS;
    1885                 : 
    1886               0 :     status = _cairo_rectilinear_stroker_add_segment (stroker, a, b,
    1887               0 :                                                      a->y == b->y,
    1888                 :                                                      TRUE);
    1889                 : 
    1890               0 :     stroker->current_point = *b;
    1891               0 :     stroker->open_sub_path = TRUE;
    1892                 : 
    1893               0 :     return status;
    1894                 : }
    1895                 : 
    1896                 : static cairo_status_t
    1897               0 : _cairo_rectilinear_stroker_line_to_dashed (void         *closure,
    1898                 :                                            const cairo_point_t  *point)
    1899                 : {
    1900               0 :     cairo_rectilinear_stroker_t *stroker = closure;
    1901               0 :     const cairo_point_t *a = &stroker->current_point;
    1902               0 :     const cairo_point_t *b = point;
    1903                 :     cairo_bool_t fully_in_bounds;
    1904                 :     double sign, remain;
    1905                 :     cairo_fixed_t mag;
    1906                 :     cairo_status_t status;
    1907                 :     cairo_line_t segment;
    1908               0 :     cairo_bool_t dash_on = FALSE;
    1909                 :     cairo_bool_t is_horizontal;
    1910                 : 
    1911                 :     /* We don't draw anything for degenerate paths. */
    1912               0 :     if (a->x == b->x && a->y == b->y)
    1913               0 :         return CAIRO_STATUS_SUCCESS;
    1914                 : 
    1915                 :     /* We only support horizontal or vertical elements. */
    1916               0 :     assert (a->x == b->x || a->y == b->y);
    1917                 : 
    1918               0 :     fully_in_bounds = TRUE;
    1919               0 :     if (stroker->has_bounds &&
    1920               0 :         (! _cairo_box_contains_point (&stroker->bounds, a) ||
    1921               0 :          ! _cairo_box_contains_point (&stroker->bounds, b)))
    1922                 :     {
    1923               0 :         fully_in_bounds = FALSE;
    1924                 :     }
    1925                 : 
    1926               0 :     is_horizontal = a->y == b->y;
    1927               0 :     if (is_horizontal)
    1928               0 :         mag = b->x - a->x;
    1929                 :     else
    1930               0 :         mag = b->y - a->y;
    1931               0 :     if (mag < 0) {
    1932               0 :         remain = _cairo_fixed_to_double (-mag);
    1933               0 :         sign = 1.;
    1934                 :     } else {
    1935               0 :         remain = _cairo_fixed_to_double (mag);
    1936               0 :         sign = -1.;
    1937                 :     }
    1938                 : 
    1939               0 :     segment.p2 = segment.p1 = *a;
    1940               0 :     while (remain > 0.) {
    1941                 :         double step_length;
    1942                 : 
    1943               0 :         step_length = MIN (stroker->dash.dash_remain, remain);
    1944               0 :         remain -= step_length;
    1945                 : 
    1946               0 :         mag = _cairo_fixed_from_double (sign*remain);
    1947               0 :         if (is_horizontal)
    1948               0 :             segment.p2.x = b->x + mag;
    1949                 :         else
    1950               0 :             segment.p2.y = b->y + mag;
    1951                 : 
    1952               0 :         if (stroker->dash.dash_on &&
    1953               0 :             (fully_in_bounds ||
    1954               0 :              _cairo_box_intersects_line_segment (&stroker->bounds, &segment)))
    1955                 :         {
    1956               0 :             status = _cairo_rectilinear_stroker_add_segment (stroker,
    1957                 :                                                              &segment.p1,
    1958                 :                                                              &segment.p2,
    1959                 :                                                              is_horizontal,
    1960                 :                                                              remain <= 0.);
    1961               0 :             if (unlikely (status))
    1962               0 :                 return status;
    1963                 : 
    1964               0 :             dash_on = TRUE;
    1965                 :         }
    1966                 :         else
    1967                 :         {
    1968               0 :             dash_on = FALSE;
    1969                 :         }
    1970                 : 
    1971               0 :         _cairo_stroker_dash_step (&stroker->dash, step_length);
    1972               0 :         segment.p1 = segment.p2;
    1973                 :     }
    1974                 : 
    1975               0 :     if (stroker->dash.dash_on && ! dash_on &&
    1976               0 :         (fully_in_bounds ||
    1977               0 :          _cairo_box_intersects_line_segment (&stroker->bounds, &segment)))
    1978                 :     {
    1979                 : 
    1980                 :         /* This segment ends on a transition to dash_on, compute a new face
    1981                 :          * and add cap for the beginning of the next dash_on step.
    1982                 :          */
    1983                 : 
    1984               0 :         status = _cairo_rectilinear_stroker_add_segment (stroker,
    1985                 :                                                          &segment.p1,
    1986                 :                                                          &segment.p1,
    1987                 :                                                          is_horizontal,
    1988                 :                                                          TRUE);
    1989               0 :         if (unlikely (status))
    1990               0 :             return status;
    1991                 :     }
    1992                 : 
    1993               0 :     stroker->current_point = *point;
    1994               0 :     stroker->open_sub_path = TRUE;
    1995                 : 
    1996               0 :     return CAIRO_STATUS_SUCCESS;
    1997                 : }
    1998                 : 
    1999                 : static cairo_status_t
    2000               0 : _cairo_rectilinear_stroker_close_path (void *closure)
    2001                 : {
    2002               0 :     cairo_rectilinear_stroker_t *stroker = closure;
    2003                 :     cairo_status_t status;
    2004                 : 
    2005                 :     /* We don't draw anything for degenerate paths. */
    2006               0 :     if (! stroker->open_sub_path)
    2007               0 :         return CAIRO_STATUS_SUCCESS;
    2008                 : 
    2009               0 :     if (stroker->dash.dashed) {
    2010               0 :         status = _cairo_rectilinear_stroker_line_to_dashed (stroker,
    2011               0 :                                                             &stroker->first_point);
    2012                 :     } else {
    2013               0 :         status = _cairo_rectilinear_stroker_line_to (stroker,
    2014               0 :                                                      &stroker->first_point);
    2015                 :     }
    2016               0 :     if (unlikely (status))
    2017               0 :         return status;
    2018                 : 
    2019               0 :     stroker->open_sub_path = FALSE;
    2020                 : 
    2021               0 :     if (stroker->dash.dashed)
    2022               0 :         status = _cairo_rectilinear_stroker_emit_segments_dashed (stroker);
    2023                 :     else
    2024               0 :         status = _cairo_rectilinear_stroker_emit_segments (stroker);
    2025               0 :     if (unlikely (status))
    2026               0 :         return status;
    2027                 : 
    2028               0 :     return CAIRO_STATUS_SUCCESS;
    2029                 : }
    2030                 : 
    2031                 : cairo_int_status_t
    2032               0 : _cairo_path_fixed_stroke_rectilinear_to_traps (const cairo_path_fixed_t *path,
    2033                 :                                                const cairo_stroke_style_t       *stroke_style,
    2034                 :                                                const cairo_matrix_t     *ctm,
    2035                 :                                                cairo_traps_t            *traps)
    2036                 : {
    2037                 :     cairo_rectilinear_stroker_t rectilinear_stroker;
    2038                 :     cairo_int_status_t status;
    2039                 : 
    2040               0 :     assert (path->is_rectilinear);
    2041                 : 
    2042               0 :     if (! _cairo_rectilinear_stroker_init (&rectilinear_stroker,
    2043                 :                                            stroke_style, ctm,
    2044                 :                                            TRUE, traps))
    2045                 :     {
    2046               0 :         return CAIRO_INT_STATUS_UNSUPPORTED;
    2047                 :     }
    2048                 : 
    2049               0 :     if (traps->num_limits) {
    2050               0 :         _cairo_rectilinear_stroker_limit (&rectilinear_stroker,
    2051                 :                                           traps->limits,
    2052                 :                                           traps->num_limits);
    2053                 :     }
    2054                 : 
    2055               0 :     status = _cairo_path_fixed_interpret (path,
    2056                 :                                           CAIRO_DIRECTION_FORWARD,
    2057                 :                                           _cairo_rectilinear_stroker_move_to,
    2058               0 :                                           rectilinear_stroker.dash.dashed ?
    2059                 :                                           _cairo_rectilinear_stroker_line_to_dashed :
    2060                 :                                           _cairo_rectilinear_stroker_line_to,
    2061                 :                                           NULL,
    2062                 :                                           _cairo_rectilinear_stroker_close_path,
    2063                 :                                           &rectilinear_stroker);
    2064               0 :     if (unlikely (status))
    2065               0 :         goto BAIL;
    2066                 : 
    2067               0 :     if (rectilinear_stroker.dash.dashed)
    2068               0 :         status = _cairo_rectilinear_stroker_emit_segments_dashed (&rectilinear_stroker);
    2069                 :     else
    2070               0 :         status = _cairo_rectilinear_stroker_emit_segments (&rectilinear_stroker);
    2071                 : 
    2072               0 :     traps->is_rectilinear = 1;
    2073               0 :     traps->is_rectangular = 1;
    2074                 :     /* As we incrementally tessellate, we do not eliminate self-intersections */
    2075               0 :     traps->has_intersections = traps->num_traps > 1;
    2076                 : BAIL:
    2077               0 :     _cairo_rectilinear_stroker_fini (&rectilinear_stroker);
    2078                 : 
    2079               0 :     if (unlikely (status))
    2080               0 :         _cairo_traps_clear (traps);
    2081                 : 
    2082               0 :     return status;
    2083                 : }
    2084                 : 
    2085                 : cairo_int_status_t
    2086               0 : _cairo_path_fixed_stroke_rectilinear_to_boxes (const cairo_path_fixed_t *path,
    2087                 :                                                const cairo_stroke_style_t       *stroke_style,
    2088                 :                                                const cairo_matrix_t     *ctm,
    2089                 :                                                cairo_boxes_t            *boxes)
    2090                 : {
    2091                 :     cairo_rectilinear_stroker_t rectilinear_stroker;
    2092                 :     cairo_int_status_t status;
    2093                 : 
    2094               0 :     assert (path->is_rectilinear);
    2095                 : 
    2096               0 :     if (! _cairo_rectilinear_stroker_init (&rectilinear_stroker,
    2097                 :                                            stroke_style, ctm,
    2098                 :                                            FALSE, boxes))
    2099                 :     {
    2100               0 :         return CAIRO_INT_STATUS_UNSUPPORTED;
    2101                 :     }
    2102                 : 
    2103               0 :     if (boxes->num_limits) {
    2104               0 :         _cairo_rectilinear_stroker_limit (&rectilinear_stroker,
    2105                 :                                           boxes->limits,
    2106                 :                                           boxes->num_limits);
    2107                 :     }
    2108                 : 
    2109               0 :     status = _cairo_path_fixed_interpret (path,
    2110                 :                                           CAIRO_DIRECTION_FORWARD,
    2111                 :                                           _cairo_rectilinear_stroker_move_to,
    2112               0 :                                           rectilinear_stroker.dash.dashed ?
    2113                 :                                           _cairo_rectilinear_stroker_line_to_dashed :
    2114                 :                                           _cairo_rectilinear_stroker_line_to,
    2115                 :                                           NULL,
    2116                 :                                           _cairo_rectilinear_stroker_close_path,
    2117                 :                                           &rectilinear_stroker);
    2118               0 :     if (unlikely (status))
    2119               0 :         goto BAIL;
    2120                 : 
    2121               0 :     if (rectilinear_stroker.dash.dashed)
    2122               0 :         status = _cairo_rectilinear_stroker_emit_segments_dashed (&rectilinear_stroker);
    2123                 :     else
    2124               0 :         status = _cairo_rectilinear_stroker_emit_segments (&rectilinear_stroker);
    2125               0 :     if (unlikely (status))
    2126               0 :         goto BAIL;
    2127                 : 
    2128                 :     /* As we incrementally tessellate, we do not eliminate self-intersections */
    2129               0 :     status = _cairo_bentley_ottmann_tessellate_boxes (boxes,
    2130                 :                                                       CAIRO_FILL_RULE_WINDING,
    2131                 :                                                       boxes);
    2132               0 :     if (unlikely (status))
    2133               0 :         goto BAIL;
    2134                 : 
    2135               0 :     _cairo_rectilinear_stroker_fini (&rectilinear_stroker);
    2136                 : 
    2137               0 :     return CAIRO_STATUS_SUCCESS;
    2138                 : 
    2139                 : BAIL:
    2140               0 :     _cairo_rectilinear_stroker_fini (&rectilinear_stroker);
    2141               0 :     _cairo_boxes_clear (boxes);
    2142               0 :     return status;
    2143                 : }

Generated by: LCOV version 1.7