static cairo_status_t _cairo_hull_eliminate_concave (cairo_hull_t *hull, int num_hull) { int i, j, k; cairo_slope_t slope_ij, slope_jk; i = 0; j = _cairo_hull_next_valid (hull, num_hull, i); k = _cairo_hull_next_valid (hull, num_hull, j); do { _cairo_slope_init (&slope_ij, &hull[i].point, &hull[j].point); _cairo_slope_init (&slope_jk, &hull[j].point, &hull[k].point); /* Is the angle formed by ij and jk concave? */ if (_cairo_slope_compare (&slope_ij, &slope_jk) >= 0) { if (i == k) return CAIRO_STATUS_SUCCESS; hull[j].discard = 1; j = i; i = _cairo_hull_prev_valid (hull, num_hull, j); } else { i = j; j = k; k = _cairo_hull_next_valid (hull, num_hull, j); } } while (j != 0); return CAIRO_STATUS_SUCCESS; }
static int _cairo_stroker_face_clockwise (cairo_stroke_face_t *in, cairo_stroke_face_t *out) { cairo_slope_t in_slope, out_slope; _cairo_slope_init (&in_slope, &in->point, &in->cw); _cairo_slope_init (&out_slope, &out->point, &out->cw); return _cairo_slope_compare (&in_slope, &out_slope) < 0; }
static void _cairo_hull_init (cairo_hull_t *hull, cairo_pen_vertex_t *vertices, int num_vertices) { cairo_point_t *p, *extremum, tmp; int i; extremum = &vertices[0].point; for (i = 1; i < num_vertices; i++) { p = &vertices[i].point; if (p->y < extremum->y || (p->y == extremum->y && p->x < extremum->x)) extremum = p; } /* Put the extremal point at the beginning of the array */ tmp = *extremum; *extremum = vertices[0].point; vertices[0].point = tmp; for (i = 0; i < num_vertices; i++) { hull[i].point = vertices[i].point; _cairo_slope_init (&hull[i].slope, &hull[0].point, &hull[i].point); /* give each point a unique id for later comparison */ hull[i].id = i; /* Don't discard by default */ hull[i].discard = 0; /* Discard all points coincident with the extremal point */ if (i != 0 && hull[i].slope.dx == 0 && hull[i].slope.dy == 0) hull[i].discard = 1; } }
static cairo_status_t line_to (void *closure, const cairo_point_t *point) { struct stroker *stroker = closure; cairo_stroke_face_t start; cairo_point_t *p1 = &stroker->current_face.point; cairo_slope_t dev_slope; stroker->has_initial_sub_path = TRUE; if (p1->x == point->x && p1->y == point->y) return CAIRO_STATUS_SUCCESS; #if DEBUG _cairo_contour_add_point (&stroker->path, point); #endif _cairo_slope_init (&dev_slope, p1, point); compute_face (p1, &dev_slope, stroker, &start); if (stroker->has_current_face) { int clockwise = _cairo_slope_compare (&stroker->current_face.dev_vector, &start.dev_vector); if (clockwise) { clockwise = clockwise < 0; /* Join with final face from previous segment */ if (! within_tolerance (&stroker->current_face.ccw, &start.ccw, stroker->contour_tolerance) || ! within_tolerance (&stroker->current_face.cw, &start.cw, stroker->contour_tolerance)) { outer_join (stroker, &stroker->current_face, &start, clockwise); inner_join (stroker, &stroker->current_face, &start, clockwise); } } } else { if (! stroker->has_first_face) { /* Save sub path's first face in case needed for closing join */ stroker->first_face = start; stroker->has_first_face = TRUE; } stroker->has_current_face = TRUE; contour_add_point (stroker, &stroker->cw, &start.cw); contour_add_point (stroker, &stroker->ccw, &start.ccw); } stroker->current_face = start; stroker->current_face.point = *point; stroker->current_face.ccw.x += dev_slope.dx; stroker->current_face.ccw.y += dev_slope.dy; stroker->current_face.cw.x += dev_slope.dx; stroker->current_face.cw.y += dev_slope.dy; contour_add_point (stroker, &stroker->cw, &stroker->current_face.cw); contour_add_point (stroker, &stroker->ccw, &stroker->current_face.ccw); return CAIRO_STATUS_SUCCESS; }
static cairo_status_t _cairo_pen_stroke_spline_half (cairo_pen_t *pen, cairo_spline_t *spline, cairo_direction_t dir, cairo_polygon_t *polygon) { int i; cairo_status_t status; int start, stop, step; int active = 0; cairo_point_t hull_point; cairo_slope_t slope, initial_slope, final_slope; cairo_point_t *point = spline->points; int num_points = spline->num_points; if (dir == CAIRO_DIRECTION_FORWARD) { start = 0; stop = num_points; step = 1; initial_slope = spline->initial_slope; final_slope = spline->final_slope; } else { start = num_points - 1; stop = -1; step = -1; initial_slope = spline->final_slope; initial_slope.dx = -initial_slope.dx; initial_slope.dy = -initial_slope.dy; final_slope = spline->initial_slope; final_slope.dx = -final_slope.dx; final_slope.dy = -final_slope.dy; } _cairo_pen_find_active_cw_vertex_index (pen, &initial_slope, &active); i = start; while (i != stop) { hull_point.x = point[i].x + pen->vertices[active].point.x; hull_point.y = point[i].y + pen->vertices[active].point.y; status = _cairo_polygon_line_to (polygon, &hull_point); if (status) return status; if (i + step == stop) slope = final_slope; else _cairo_slope_init (&slope, &point[i], &point[i+step]); if (_cairo_slope_counter_clockwise (&slope, &pen->vertices[active].slope_ccw)) { if (++active == pen->num_vertices) active = 0; } else if (_cairo_slope_clockwise (&slope, &pen->vertices[active].slope_cw)) { if (--active == -1) active = pen->num_vertices - 1; } else { i += step; } } return CAIRO_STATUS_SUCCESS; }
static void _cairo_pen_compute_slopes (cairo_pen_t *pen) { int i, i_prev; cairo_pen_vertex_t *prev, *v, *next; for (i=0, i_prev = pen->num_vertices - 1; i < pen->num_vertices; i_prev = i++) { prev = &pen->vertices[i_prev]; v = &pen->vertices[i]; next = &pen->vertices[(i + 1) % pen->num_vertices]; _cairo_slope_init (&v->slope_cw, &prev->point, &v->point); _cairo_slope_init (&v->slope_ccw, &v->point, &next->point); } }
cairo_status_t _cairo_polygon_line_to (cairo_polygon_t *polygon, const cairo_point_t *point) { /* squash collinear edges */ if (polygon->has_current_edge) { if (polygon->current_point.x != point->x || polygon->current_point.y != point->y) { cairo_slope_t this; _cairo_slope_init (&this, &polygon->current_point, point); if (_cairo_slope_equal (&polygon->current_edge, &this)) { polygon->current_point = *point; return CAIRO_STATUS_SUCCESS; } _cairo_polygon_add_edge (polygon, &polygon->last_point, &polygon->current_point); polygon->last_point = polygon->current_point; polygon->current_edge = this; } } else if (polygon->has_current_point) { if (polygon->current_point.x != point->x || polygon->current_point.y != point->y) { polygon->last_point = polygon->current_point; _cairo_slope_init (&polygon->current_edge, &polygon->last_point, point); polygon->has_current_edge = TRUE; } } else { polygon->first_point = *point; polygon->has_current_point = TRUE; } polygon->current_point = *point; return polygon->status; }
cairo_bool_t _cairo_spline_init (cairo_spline_t *spline, cairo_spline_add_point_func_t add_point_func, void *closure, const cairo_point_t *a, const cairo_point_t *b, const cairo_point_t *c, const cairo_point_t *d) { spline->add_point_func = add_point_func; spline->closure = closure; spline->knots.a = *a; spline->knots.b = *b; spline->knots.c = *c; spline->knots.d = *d; if (a->x != b->x || a->y != b->y) _cairo_slope_init (&spline->initial_slope, &spline->knots.a, &spline->knots.b); else if (a->x != c->x || a->y != c->y) _cairo_slope_init (&spline->initial_slope, &spline->knots.a, &spline->knots.c); else if (a->x != d->x || a->y != d->y) _cairo_slope_init (&spline->initial_slope, &spline->knots.a, &spline->knots.d); else return FALSE; if (c->x != d->x || c->y != d->y) _cairo_slope_init (&spline->final_slope, &spline->knots.c, &spline->knots.d); else if (b->x != d->x || b->y != d->y) _cairo_slope_init (&spline->final_slope, &spline->knots.b, &spline->knots.d); else _cairo_slope_init (&spline->final_slope, &spline->knots.a, &spline->knots.d); return TRUE; }
cairo_bool_t _cairo_spline_init (cairo_spline_t *spline, cairo_spline_add_point_func_t add_point_func, void *closure, const cairo_point_t *a, const cairo_point_t *b, const cairo_point_t *c, const cairo_point_t *d) { /* If both tangents are zero, this is just a straight line */ if (a->x == b->x && a->y == b->y && c->x == d->x && c->y == d->y) return FALSE; spline->add_point_func = add_point_func; spline->closure = closure; spline->knots.a = *a; spline->knots.b = *b; spline->knots.c = *c; spline->knots.d = *d; if (a->x != b->x || a->y != b->y) _cairo_slope_init (&spline->initial_slope, &spline->knots.a, &spline->knots.b); else if (a->x != c->x || a->y != c->y) _cairo_slope_init (&spline->initial_slope, &spline->knots.a, &spline->knots.c); else if (a->x != d->x || a->y != d->y) _cairo_slope_init (&spline->initial_slope, &spline->knots.a, &spline->knots.d); else return FALSE; if (c->x != d->x || c->y != d->y) _cairo_slope_init (&spline->final_slope, &spline->knots.c, &spline->knots.d); else if (b->x != d->x || b->y != d->y) _cairo_slope_init (&spline->final_slope, &spline->knots.b, &spline->knots.d); else return FALSE; /* just treat this as a straight-line from a -> d */ /* XXX if the initial, final and vector are all equal, this is just a line */ return TRUE; }
static cairo_status_t _cairo_stroker_line_to (void *closure, const cairo_point_t *point) { cairo_stroker_t *stroker = closure; cairo_stroke_face_t start, end; cairo_point_t *p1 = &stroker->current_point; cairo_slope_t dev_slope; double slope_dx, slope_dy; cairo_status_t status; stroker->has_initial_sub_path = TRUE; if (p1->x == point->x && p1->y == point->y) return CAIRO_STATUS_SUCCESS; _cairo_slope_init (&dev_slope, p1, point); slope_dx = _cairo_fixed_to_double (point->x - p1->x); slope_dy = _cairo_fixed_to_double (point->y - p1->y); _compute_normalized_device_slope (&slope_dx, &slope_dy, stroker->ctm_inverse, NULL); status = _cairo_stroker_add_sub_edge (stroker, p1, point, &dev_slope, slope_dx, slope_dy, &start, &end); if (unlikely (status)) return status; if (stroker->has_current_face) { /* Join with final face from previous segment */ status = _cairo_stroker_join (stroker, &stroker->current_face, &start); if (unlikely (status)) return status; } else if (! stroker->has_first_face) { /* Save sub path's first face in case needed for closing join */ stroker->first_face = start; stroker->has_first_face = TRUE; } stroker->current_face = end; stroker->has_current_face = TRUE; stroker->current_point = *point; return CAIRO_STATUS_SUCCESS; }
static cairo_bool_t _cairo_trap_contains (cairo_trapezoid_t *t, cairo_point_t *pt) { cairo_slope_t slope_left, slope_pt, slope_right; if (t->top > pt->y) return FALSE; if (t->bottom < pt->y) return FALSE; _cairo_slope_init (&slope_left, &t->left.p1, &t->left.p2); _cairo_slope_init (&slope_pt, &t->left.p1, pt); if (_cairo_slope_compare (&slope_left, &slope_pt) < 0) return FALSE; _cairo_slope_init (&slope_right, &t->right.p1, &t->right.p2); _cairo_slope_init (&slope_pt, &t->right.p1, pt); if (_cairo_slope_compare (&slope_pt, &slope_right) < 0) return FALSE; return TRUE; }
static cairo_status_t line_to (void *closure, const cairo_point_t *point) { struct stroker *stroker = closure; cairo_stroke_face_t start; cairo_point_t *p1 = &stroker->current_face.point; cairo_slope_t dev_slope; stroker->has_sub_path = TRUE; if (p1->x == point->x && p1->y == point->y) return CAIRO_STATUS_SUCCESS; _cairo_slope_init (&dev_slope, p1, point); compute_face (p1, &dev_slope, stroker, &start); if (stroker->has_current_face) { int clockwise = join_is_clockwise (&stroker->current_face, &start); /* Join with final face from previous segment */ outer_join (stroker, &stroker->current_face, &start, clockwise); inner_join (stroker, &stroker->current_face, &start, clockwise); } else { if (! stroker->has_first_face) { /* Save sub path's first face in case needed for closing join */ stroker->first_face = start; _cairo_tristrip_move_to (stroker->strip, &start.cw); stroker->has_first_face = TRUE; } stroker->has_current_face = TRUE; _cairo_tristrip_add_point (stroker->strip, &start.cw); _cairo_tristrip_add_point (stroker->strip, &start.ccw); } stroker->current_face = start; stroker->current_face.point = *point; stroker->current_face.ccw.x += dev_slope.dx; stroker->current_face.ccw.y += dev_slope.dy; stroker->current_face.cw.x += dev_slope.dx; stroker->current_face.cw.y += dev_slope.dy; _cairo_tristrip_add_point (stroker->strip, &stroker->current_face.cw); _cairo_tristrip_add_point (stroker->strip, &stroker->current_face.ccw); return CAIRO_STATUS_SUCCESS; }
static cairo_status_t _cairo_spline_add_point (cairo_spline_t *spline, const cairo_point_t *point, const cairo_point_t *knot) { cairo_point_t *prev; cairo_slope_t slope; prev = &spline->last_point; if (prev->x == point->x && prev->y == point->y) return CAIRO_STATUS_SUCCESS; _cairo_slope_init (&slope, point, knot); spline->last_point = *point; return spline->add_point_func (spline->closure, point, &slope); }
void _cairo_traps_tessellate_convex_quad (cairo_traps_t *traps, const cairo_point_t q[4]) { int a, b, c, d; int i; cairo_slope_t ab, ad; cairo_bool_t b_left_of_d; cairo_line_t left; cairo_line_t right; /* Choose a as a point with minimal y */ a = 0; for (i = 1; i < 4; i++) if (_compare_point_fixed_by_y (&q[i], &q[a]) < 0) a = i; /* b and d are adjacent to a, while c is opposite */ b = (a + 1) % 4; c = (a + 2) % 4; d = (a + 3) % 4; /* Choose between b and d so that b.y is less than d.y */ if (_compare_point_fixed_by_y (&q[d], &q[b]) < 0) { b = (a + 3) % 4; d = (a + 1) % 4; } /* Without freedom left to choose anything else, we have four * cases to tessellate. * * First, we have to determine the Y-axis sort of the four * vertices, (either abcd or abdc). After that we need to detemine * which edges will be "left" and which will be "right" in the * resulting trapezoids. This can be determined by computing a * slope comparison of ab and ad to determine if b is left of d or * not. * * Note that "left of" here is in the sense of which edges should * be the left vs. right edges of the trapezoid. In particular, b * left of d does *not* mean that b.x is less than d.x. * * This should hopefully be made clear in the lame ASCII art * below. Since the same slope comparison is used in all cases, we * compute it before testing for the Y-value sort. */ /* Note: If a == b then the ab slope doesn't give us any * information. In that case, we can replace it with the ac (or * equivalenly the bc) slope which gives us exactly the same * information we need. At worst the names of the identifiers ab * and b_left_of_d are inaccurate in this case, (would be ac, and * c_left_of_d). */ if (q[a].x == q[b].x && q[a].y == q[b].y) _cairo_slope_init (&ab, &q[a], &q[c]); else _cairo_slope_init (&ab, &q[a], &q[b]); _cairo_slope_init (&ad, &q[a], &q[d]); b_left_of_d = _cairo_slope_compare (&ab, &ad) > 0; if (q[c].y <= q[d].y) { if (b_left_of_d) { /* Y-sort is abcd and b is left of d, (slope(ab) > slope (ad)) * * top bot left right * _a a a * / / /| |\ a.y b.y ab ad * b / b | b \ * / / | | \ \ b.y c.y bc ad * c / c | c \ * | / \| \ \ c.y d.y cd ad * d d d */ left.p1 = q[a]; left.p2 = q[b]; right.p1 = q[a]; right.p2 = q[d]; _cairo_traps_add_clipped_trap (traps, q[a].y, q[b].y, &left, &right); left.p1 = q[b]; left.p2 = q[c]; _cairo_traps_add_clipped_trap (traps, q[b].y, q[c].y, &left, &right); left.p1 = q[c]; left.p2 = q[d]; _cairo_traps_add_clipped_trap (traps, q[c].y, q[d].y, &left, &right); } else { /* Y-sort is abcd and b is right of d, (slope(ab) <= slope (ad)) * * a a a_ * /| |\ \ \ a.y b.y ad ab * / b | b \ b * / / | | \ \ b.y c.y ad bc * / c | c \ c * / / |/ \ | c.y d.y ad cd * d d d */ left.p1 = q[a]; left.p2 = q[d]; right.p1 = q[a]; right.p2 = q[b]; _cairo_traps_add_clipped_trap (traps, q[a].y, q[b].y, &left, &right); right.p1 = q[b]; right.p2 = q[c]; _cairo_traps_add_clipped_trap (traps, q[b].y, q[c].y, &left, &right); right.p1 = q[c]; right.p2 = q[d]; _cairo_traps_add_clipped_trap (traps, q[c].y, q[d].y, &left, &right); } } else { if (b_left_of_d) { /* Y-sort is abdc and b is left of d, (slope (ab) > slope (ad)) * * a a a * // / \ |\ a.y b.y ab ad * /b/ b \ b \ * / / \ \ \ \ b.y d.y bc ad * /d/ \ d \ d * // \ / \| d.y c.y bc dc * c c c */ left.p1 = q[a]; left.p2 = q[b]; right.p1 = q[a]; right.p2 = q[d]; _cairo_traps_add_clipped_trap (traps, q[a].y, q[b].y, &left, &right); left.p1 = q[b]; left.p2 = q[c]; _cairo_traps_add_clipped_trap (traps, q[b].y, q[d].y, &left, &right); right.p1 = q[d]; right.p2 = q[c]; _cairo_traps_add_clipped_trap (traps, q[d].y, q[c].y, &left, &right); } else { /* Y-sort is abdc and b is right of d, (slope (ab) <= slope (ad)) * * a a a * /| / \ \\ a.y b.y ad ab * / b / b \b\ * / / / / \ \ b.y d.y ad bc * d / d / \d\ * |/ \ / \\ d.y c.y dc bc * c c c */ left.p1 = q[a]; left.p2 = q[d]; right.p1 = q[a]; right.p2 = q[b]; _cairo_traps_add_clipped_trap (traps, q[a].y, q[b].y, &left, &right); right.p1 = q[b]; right.p2 = q[c]; _cairo_traps_add_clipped_trap (traps, q[b].y, q[d].y, &left, &right); left.p1 = q[d]; left.p2 = q[c]; _cairo_traps_add_clipped_trap (traps, q[d].y, q[c].y, &left, &right); } } }
static void _cairo_pen_stroke_spline_half (cairo_pen_t *pen, cairo_spline_t *spline, cairo_direction_t dir, cairo_polygon_t *polygon) { int i; int start, stop, step; int active = 0; cairo_point_t hull_point; cairo_slope_t slope, initial_slope, final_slope; cairo_point_t *point = spline->points; int num_points = spline->num_points; if (dir == CAIRO_DIRECTION_FORWARD) { start = 0; stop = num_points; step = 1; initial_slope = spline->initial_slope; final_slope = spline->final_slope; } else { start = num_points - 1; stop = -1; step = -1; initial_slope = spline->final_slope; initial_slope.dx = -initial_slope.dx; initial_slope.dy = -initial_slope.dy; final_slope = spline->initial_slope; final_slope.dx = -final_slope.dx; final_slope.dy = -final_slope.dy; } _cairo_pen_find_active_cw_vertex_index (pen, &initial_slope, &active); i = start; while (i != stop) { hull_point.x = point[i].x + pen->vertices[active].point.x; hull_point.y = point[i].y + pen->vertices[active].point.y; _cairo_polygon_line_to (polygon, &hull_point); if (i + step == stop) slope = final_slope; else _cairo_slope_init (&slope, &point[i], &point[i+step]); /* The strict inequalities here ensure that if a spline slope * compares identically with either of the slopes of the * active vertex, then it remains the active vertex. This is * very important since otherwise we can trigger an infinite * loop in the case of a degenerate pen, (a line), where * neither vertex considers itself active for the slope---one * will consider it as equal and reject, and the other will * consider it unequal and reject. This is due to the inherent * ambiguity when comparing slopes that differ by exactly * pi. */ if (_cairo_slope_compare (&slope, &pen->vertices[active].slope_ccw) > 0) { if (++active == pen->num_vertices) active = 0; } else if (_cairo_slope_compare (&slope, &pen->vertices[active].slope_cw) < 0) { if (--active == -1) active = pen->num_vertices - 1; } else { i += step; } } }
/* * Dashed lines. Cap each dash end, join around turns when on */ static cairo_status_t _cairo_stroker_line_to_dashed (void *closure, const cairo_point_t *p2) { cairo_stroker_t *stroker = closure; double mag, remain, step_length = 0; double slope_dx, slope_dy; double dx2, dy2; cairo_stroke_face_t sub_start, sub_end; cairo_point_t *p1 = &stroker->current_point; cairo_slope_t dev_slope; cairo_line_t segment; cairo_bool_t fully_in_bounds; cairo_status_t status; stroker->has_initial_sub_path = stroker->dash.dash_starts_on; if (p1->x == p2->x && p1->y == p2->y) return CAIRO_STATUS_SUCCESS; fully_in_bounds = TRUE; if (stroker->has_bounds && (! _cairo_box_contains_point (&stroker->bounds, p1) || ! _cairo_box_contains_point (&stroker->bounds, p2))) { fully_in_bounds = FALSE; } _cairo_slope_init (&dev_slope, p1, p2); slope_dx = _cairo_fixed_to_double (p2->x - p1->x); slope_dy = _cairo_fixed_to_double (p2->y - p1->y); if (! _compute_normalized_device_slope (&slope_dx, &slope_dy, stroker->ctm_inverse, &mag)) { return CAIRO_STATUS_SUCCESS; } remain = mag; segment.p1 = *p1; while (remain) { step_length = MIN (stroker->dash.dash_remain, remain); remain -= step_length; dx2 = slope_dx * (mag - remain); dy2 = slope_dy * (mag - remain); cairo_matrix_transform_distance (stroker->ctm, &dx2, &dy2); segment.p2.x = _cairo_fixed_from_double (dx2) + p1->x; segment.p2.y = _cairo_fixed_from_double (dy2) + p1->y; if (stroker->dash.dash_on && (fully_in_bounds || (! stroker->has_first_face && stroker->dash.dash_starts_on) || _cairo_box_intersects_line_segment (&stroker->bounds, &segment))) { status = _cairo_stroker_add_sub_edge (stroker, &segment.p1, &segment.p2, &dev_slope, slope_dx, slope_dy, &sub_start, &sub_end); if (unlikely (status)) return status; if (stroker->has_current_face) { /* Join with final face from previous segment */ status = _cairo_stroker_join (stroker, &stroker->current_face, &sub_start); if (unlikely (status)) return status; stroker->has_current_face = FALSE; } else if (! stroker->has_first_face && stroker->dash.dash_starts_on) { /* Save sub path's first face in case needed for closing join */ stroker->first_face = sub_start; stroker->has_first_face = TRUE; } else { /* Cap dash start if not connecting to a previous segment */ status = _cairo_stroker_add_leading_cap (stroker, &sub_start); if (unlikely (status)) return status; } if (remain) { /* Cap dash end if not at end of segment */ status = _cairo_stroker_add_trailing_cap (stroker, &sub_end); if (unlikely (status)) return status; } else { stroker->current_face = sub_end; stroker->has_current_face = TRUE; } } else { if (stroker->has_current_face) { /* Cap final face from previous segment */ status = _cairo_stroker_add_trailing_cap (stroker, &stroker->current_face); if (unlikely (status)) return status; stroker->has_current_face = FALSE; } } _cairo_stroker_dash_step (&stroker->dash, step_length); segment.p1 = segment.p2; } if (stroker->dash.dash_on && ! stroker->has_current_face) { /* This segment ends on a transition to dash_on, compute a new face * and add cap for the beginning of the next dash_on step. * * Note: this will create a degenerate cap if this is not the last line * in the path. Whether this behaviour is desirable or not is debatable. * On one side these degenerate caps can not be reproduced with regular * path stroking. * On the other hand, Acroread 7 also produces the degenerate caps. */ _compute_face (p2, &dev_slope, slope_dx, slope_dy, stroker, &stroker->current_face); status = _cairo_stroker_add_leading_cap (stroker, &stroker->current_face); if (unlikely (status)) return status; stroker->has_current_face = TRUE; } stroker->current_point = *p2; return CAIRO_STATUS_SUCCESS; }
static cairo_status_t _cairo_rectilinear_stroker_emit_segments_dashed (cairo_rectilinear_stroker_t *stroker) { cairo_status_t status; cairo_line_cap_t line_cap = stroker->stroke_style->line_cap; cairo_fixed_t half_line_width = stroker->half_line_width; int i; for (i = 0; i < stroker->num_segments; i++) { cairo_point_t *a, *b; cairo_bool_t is_horizontal; a = &stroker->segments[i].p1; b = &stroker->segments[i].p2; is_horizontal = stroker->segments[i].is_horizontal; /* Handle the joins for a potentially degenerate segment. */ if (line_cap == CAIRO_LINE_CAP_BUTT && stroker->segments[i].has_join && (i != stroker->num_segments - 1 || (! stroker->open_sub_path && stroker->dash.dash_starts_on))) { cairo_point_t p1 = stroker->segments[i].p1; cairo_point_t p2 = stroker->segments[i].p2; cairo_slope_t out_slope; int j = (i + 1) % stroker->num_segments; _cairo_slope_init (&out_slope, &stroker->segments[j].p1, &stroker->segments[j].p2); if (is_horizontal) { if (p1.x <= p2.x) { p1.x = p2.x; p2.x += half_line_width; } else { p1.x = p2.x - half_line_width; } if (out_slope.dy >= 0) p1.y -= half_line_width; if (out_slope.dy <= 0) p2.y += half_line_width; } else { if (p1.y <= p2.y) { p1.y = p2.y; p2.y += half_line_width; } else { p1.y = p2.y - half_line_width; } if (out_slope.dx >= 0) p1.x -= half_line_width; if (out_slope.dx <= 0) p2.x += half_line_width; } status = _cairo_traps_tessellate_rectangle (stroker->traps, &p1, &p2); if (unlikely (status)) return status; } /* Perform the adjustments of the endpoints. */ if (is_horizontal) { if (line_cap == CAIRO_LINE_CAP_SQUARE) { if (a->x <= b->x) { a->x -= half_line_width; b->x += half_line_width; } else { a->x += half_line_width; b->x -= half_line_width; } } if (a->x > b->x) { cairo_point_t *t; t = a; a = b; b = t; } a->y -= half_line_width; b->y += half_line_width; } else { if (line_cap == CAIRO_LINE_CAP_SQUARE) { if (a->y <= b->y) { a->y -= half_line_width; b->y += half_line_width; } else { a->y += half_line_width; b->y -= half_line_width; } } if (a->y > b->y) { cairo_point_t *t; t = a; a = b; b = t; } a->x -= half_line_width; b->x += half_line_width; } if (a->x == b->x && a->y == b->y) continue; status = _cairo_traps_tessellate_rectangle (stroker->traps, a, b); if (unlikely (status)) return status; } stroker->num_segments = 0; return CAIRO_STATUS_SUCCESS; }
static cairo_status_t _cairo_stroker_curve_to (void *closure, const cairo_point_t *b, const cairo_point_t *c, const cairo_point_t *d) { cairo_stroker_t *stroker = closure; cairo_spline_t spline; cairo_line_join_t line_join_save; cairo_stroke_face_t face; double slope_dx, slope_dy; cairo_spline_add_point_func_t line_to; cairo_spline_add_point_func_t spline_to; cairo_status_t status = CAIRO_STATUS_SUCCESS; line_to = stroker->dash.dashed ? (cairo_spline_add_point_func_t) _cairo_stroker_line_to_dashed : (cairo_spline_add_point_func_t) _cairo_stroker_line_to; /* spline_to is only capable of rendering non-degenerate splines. */ spline_to = stroker->dash.dashed ? (cairo_spline_add_point_func_t) _cairo_stroker_line_to_dashed : (cairo_spline_add_point_func_t) _cairo_stroker_spline_to; if (! _cairo_spline_init (&spline, spline_to, stroker, &stroker->current_point, b, c, d)) { cairo_slope_t fallback_slope; _cairo_slope_init (&fallback_slope, &stroker->current_point, d); return line_to (closure, d, &fallback_slope); } /* If the line width is so small that the pen is reduced to a single point, then we have nothing to do. */ if (stroker->pen.num_vertices <= 1) return CAIRO_STATUS_SUCCESS; /* Compute the initial face */ if (! stroker->dash.dashed || stroker->dash.dash_on) { slope_dx = _cairo_fixed_to_double (spline.initial_slope.dx); slope_dy = _cairo_fixed_to_double (spline.initial_slope.dy); if (_compute_normalized_device_slope (&slope_dx, &slope_dy, stroker->ctm_inverse, NULL)) { _compute_face (&stroker->current_point, &spline.initial_slope, slope_dx, slope_dy, stroker, &face); } if (stroker->has_current_face) { status = _cairo_stroker_join (stroker, &stroker->current_face, &face); if (unlikely (status)) return status; } else if (! stroker->has_first_face) { stroker->first_face = face; stroker->has_first_face = TRUE; } stroker->current_face = face; stroker->has_current_face = TRUE; } /* Temporarily modify the stroker to use round joins to guarantee * smooth stroked curves. */ line_join_save = stroker->style.line_join; stroker->style.line_join = CAIRO_LINE_JOIN_ROUND; status = _cairo_spline_decompose (&spline, stroker->tolerance); if (unlikely (status)) return status; /* And join the final face */ if (! stroker->dash.dashed || stroker->dash.dash_on) { slope_dx = _cairo_fixed_to_double (spline.final_slope.dx); slope_dy = _cairo_fixed_to_double (spline.final_slope.dy); if (_compute_normalized_device_slope (&slope_dx, &slope_dy, stroker->ctm_inverse, NULL)) { _compute_face (&stroker->current_point, &spline.final_slope, slope_dx, slope_dy, stroker, &face); } status = _cairo_stroker_join (stroker, &stroker->current_face, &face); if (unlikely (status)) return status; stroker->current_face = face; } stroker->style.line_join = line_join_save; return CAIRO_STATUS_SUCCESS; }
static cairo_status_t _cairo_rectilinear_stroker_emit_segments_dashed (cairo_rectilinear_stroker_t *stroker) { cairo_status_t status; cairo_line_cap_t line_cap = stroker->stroke_style->line_cap; cairo_fixed_t half_line_x = stroker->half_line_x; cairo_fixed_t half_line_y = stroker->half_line_y; int i; for (i = 0; i < stroker->num_segments; i++) { cairo_point_t *a, *b; cairo_bool_t is_horizontal; cairo_box_t box; a = &stroker->segments[i].p1; b = &stroker->segments[i].p2; is_horizontal = stroker->segments[i].flags & HORIZONTAL; /* Handle the joins for a potentially degenerate segment. */ if (line_cap == CAIRO_LINE_CAP_BUTT && stroker->segments[i].flags & JOIN && (i != stroker->num_segments - 1 || (! stroker->open_sub_path && stroker->dash.dash_starts_on))) { cairo_slope_t out_slope; int j = (i + 1) % stroker->num_segments; cairo_bool_t forwards = !!(stroker->segments[i].flags & FORWARDS); _cairo_slope_init (&out_slope, &stroker->segments[j].p1, &stroker->segments[j].p2); box.p2 = box.p1 = stroker->segments[i].p2; if (is_horizontal) { if (forwards) box.p2.x += half_line_x; else box.p1.x -= half_line_x; if (out_slope.dy > 0) box.p1.y -= half_line_y; else box.p2.y += half_line_y; } else { if (forwards) box.p2.y += half_line_y; else box.p1.y -= half_line_y; if (out_slope.dx > 0) box.p1.x -= half_line_x; else box.p2.x += half_line_x; } status = _cairo_boxes_add (stroker->boxes, stroker->antialias, &box); if (unlikely (status)) return status; } /* Perform the adjustments of the endpoints. */ if (is_horizontal) { if (line_cap == CAIRO_LINE_CAP_SQUARE) { if (a->x <= b->x) { a->x -= half_line_x; b->x += half_line_x; } else { a->x += half_line_x; b->x -= half_line_x; } } a->y += half_line_y; b->y -= half_line_y; } else { if (line_cap == CAIRO_LINE_CAP_SQUARE) { if (a->y <= b->y) { a->y -= half_line_y; b->y += half_line_y; } else { a->y += half_line_y; b->y -= half_line_y; } } a->x += half_line_x; b->x -= half_line_x; } if (a->x == b->x && a->y == b->y) continue; if (a->x < b->x) { box.p1.x = a->x; box.p2.x = b->x; } else { box.p1.x = b->x; box.p2.x = a->x; } if (a->y < b->y) { box.p1.y = a->y; box.p2.y = b->y; } else { box.p1.y = b->y; box.p2.y = a->y; } status = _cairo_boxes_add (stroker->boxes, stroker->antialias, &box); if (unlikely (status)) return status; } stroker->num_segments = 0; return CAIRO_STATUS_SUCCESS; }