/* Find active pen vertex for counterclockwise edge of stroke at the given slope. * * Note: See the comments for _cairo_pen_find_active_cw_vertex_index * for some details about the strictness of the inequalities here. */ void _cairo_pen_find_active_ccw_vertex_index (cairo_pen_t *pen, cairo_slope_t *slope, int *active) { int i; cairo_slope_t slope_reverse; slope_reverse = *slope; slope_reverse.dx = -slope_reverse.dx; slope_reverse.dy = -slope_reverse.dy; for (i=pen->num_vertices-1; i >= 0; i--) { if ((_cairo_slope_compare (&pen->vertices[i].slope_ccw, &slope_reverse) >= 0) && (_cairo_slope_compare (&pen->vertices[i].slope_cw, &slope_reverse) < 0)) break; } /* If the desired slope cannot be found between any of the pen * vertices, then we must have a degenerate pen, (such as a pen * that's been transformed to a line). In that case, we consider * the last pen vertex as the appropriate counterclockwise vertex. */ if (i < 0) i = pen->num_vertices - 1; *active = i; }
static int _cairo_hull_vertex_compare (const void *av, const void *bv) { cairo_hull_t *a = (cairo_hull_t *) av; cairo_hull_t *b = (cairo_hull_t *) bv; int ret; ret = _cairo_slope_compare (&a->slope, &b->slope); /* In the case of two vertices with identical slope from the extremal point discard the nearer point. */ if (ret == 0) { cairo_fixed_48_16_t a_dist, b_dist; a_dist = ((cairo_fixed_48_16_t) a->slope.dx * a->slope.dx + (cairo_fixed_48_16_t) a->slope.dy * a->slope.dy); b_dist = ((cairo_fixed_48_16_t) b->slope.dx * b->slope.dx + (cairo_fixed_48_16_t) b->slope.dy * b->slope.dy); /* * Use the point's ids to ensure a total ordering. * a well-defined ordering, and avoid setting discard on * both points. */ if (a_dist < b_dist || (a_dist == b_dist && a->id < b->id)) { a->discard = 1; ret = -1; } else { b->discard = 1; ret = 1; } } return ret; }
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 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 int _cairo_hull_vertex_compare (const void *av, const void *bv) { cairo_hull_t *a = (cairo_hull_t *) av; cairo_hull_t *b = (cairo_hull_t *) bv; int ret; ret = _cairo_slope_compare (&a->slope, &b->slope); /* * In the case of two vertices with identical slope from the * extremal point discard the nearer point. */ if (ret == 0) { int cmp; cmp = _cairo_int64_cmp (_slope_length (&a->slope), _slope_length (&b->slope)); /* * Use the points' ids to ensure a well-defined ordering, * and avoid setting discard on both points. */ if (cmp < 0 || (cmp == 0 && a->id < b->id)) { a->discard = 1; ret = -1; } else { b->discard = 1; ret = 1; } } return ret; }
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; }
void _cairo_pen_find_active_cw_vertices (const cairo_pen_t *pen, const cairo_slope_t *in, const cairo_slope_t *out, int *start, int *stop) { int lo = 0, hi = pen->num_vertices; int i; i = (lo + hi) >> 1; do { if (_cairo_slope_compare (&pen->vertices[i].slope_cw, in) < 0) lo = i; else hi = i; i = (lo + hi) >> 1; } while (hi - lo > 1); if (_cairo_slope_compare (&pen->vertices[i].slope_cw, in) < 0) if (++i == pen->num_vertices) i = 0; *start = i; if (_cairo_slope_compare (out, &pen->vertices[i].slope_ccw) >= 0) { lo = i; hi = i + pen->num_vertices; i = (lo + hi) >> 1; do { int j = i; if (j >= pen->num_vertices) j -= pen->num_vertices; if (_cairo_slope_compare (&pen->vertices[j].slope_cw, out) > 0) hi = i; else lo = i; i = (lo + hi) >> 1; } while (hi - lo > 1); if (i >= pen->num_vertices) i -= pen->num_vertices; }
/* * Find active pen vertex for clockwise edge of stroke at the given slope. * * The strictness of the inequalities here is delicate. The issue is * that the slope_ccw member of one pen vertex will be equivalent to * the slope_cw member of the next pen vertex in a counterclockwise * order. However, for this function, we care strongly about which * vertex is returned. * * [I think the "care strongly" above has to do with ensuring that the * pen's "extra points" from the spline's initial and final slopes are * properly found when beginning the spline stroking.] */ int _cairo_pen_find_active_cw_vertex_index (const cairo_pen_t *pen, const cairo_slope_t *slope) { int i; for (i=0; i < pen->num_vertices; i++) { if ((_cairo_slope_compare (slope, &pen->vertices[i].slope_ccw) < 0) && (_cairo_slope_compare (slope, &pen->vertices[i].slope_cw) >= 0)) break; } /* If the desired slope cannot be found between any of the pen * vertices, then we must have a degenerate pen, (such as a pen * that's been transformed to a line). In that case, we consider * the first pen vertex as the appropriate clockwise vertex. */ if (i == pen->num_vertices) i = 0; return i; }
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 int _cairo_hull_vertex_compare (const void *av, const void *bv) { cairo_hull_t *a = (cairo_hull_t *) av; cairo_hull_t *b = (cairo_hull_t *) bv; int ret; /* Some libraries are reported to actually compare identical * pointers and require the result to be 0. This is the crazy world we * have to live in. */ if (a == b) return 0; ret = _cairo_slope_compare (&a->slope, &b->slope); /* * In the case of two vertices with identical slope from the * extremal point discard the nearer point. */ if (ret == 0) { int cmp; cmp = _cairo_int64_cmp (_slope_length (&a->slope), _slope_length (&b->slope)); /* * Use the points' ids to ensure a well-defined ordering, * and avoid setting discard on both points. */ if (cmp < 0 || (cmp == 0 && a->id < b->id)) { a->discard = 1; ret = -1; } else { b->discard = 1; ret = 1; } } return ret; }
static int join_is_clockwise (const cairo_stroke_face_t *in, const cairo_stroke_face_t *out) { return _cairo_slope_compare (&in->dev_vector, &out->dev_vector) < 0; }
/* * Construct a fan around the midpoint using the vertices from pen between * inpt and outpt. */ static void add_fan (struct stroker *stroker, const cairo_slope_t *in_vector, const cairo_slope_t *out_vector, const cairo_point_t *midpt, const cairo_point_t *inpt, const cairo_point_t *outpt, cairo_bool_t clockwise) { int start, stop, step, i, npoints; if (clockwise) { step = 1; start = _cairo_pen_find_active_cw_vertex_index (&stroker->pen, in_vector); if (_cairo_slope_compare (&stroker->pen.vertices[start].slope_cw, in_vector) < 0) start = range_step (start, 1, stroker->pen.num_vertices); stop = _cairo_pen_find_active_cw_vertex_index (&stroker->pen, out_vector); if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_ccw, out_vector) > 0) { stop = range_step (stop, -1, stroker->pen.num_vertices); if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_cw, in_vector) < 0) return; } npoints = stop - start; } else { step = -1; start = _cairo_pen_find_active_ccw_vertex_index (&stroker->pen, in_vector); if (_cairo_slope_compare (&stroker->pen.vertices[start].slope_ccw, in_vector) < 0) start = range_step (start, -1, stroker->pen.num_vertices); stop = _cairo_pen_find_active_ccw_vertex_index (&stroker->pen, out_vector); if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_cw, out_vector) > 0) { stop = range_step (stop, 1, stroker->pen.num_vertices); if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_ccw, in_vector) < 0) return; } npoints = start - stop; } stop = range_step (stop, step, stroker->pen.num_vertices); if (npoints < 0) npoints += stroker->pen.num_vertices; if (npoints <= 1) return; for (i = start; i != stop; i = range_step (i, step, stroker->pen.num_vertices)) { cairo_point_t p = *midpt; translate_point (&p, &stroker->pen.vertices[i].point); //contour_add_point (stroker, c, &p); } }
/* * Construct a fan around the midpoint using the vertices from pen between * inpt and outpt. */ static cairo_status_t _tessellate_fan (cairo_stroker_t *stroker, const cairo_slope_t *in_vector, const cairo_slope_t *out_vector, const cairo_point_t *midpt, const cairo_point_t *inpt, const cairo_point_t *outpt, cairo_bool_t clockwise) { cairo_point_t stack_points[64], *points = stack_points; int start, stop, step, i, npoints; cairo_status_t status; if (clockwise) { step = -1; start = _cairo_pen_find_active_ccw_vertex_index (&stroker->pen, in_vector); if (_cairo_slope_compare (&stroker->pen.vertices[start].slope_ccw, in_vector) < 0) start = _range_step (start, -1, stroker->pen.num_vertices); stop = _cairo_pen_find_active_ccw_vertex_index (&stroker->pen, out_vector); if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_cw, out_vector) > 0) { stop = _range_step (stop, 1, stroker->pen.num_vertices); if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_ccw, in_vector) < 0) { goto BEVEL; } } npoints = start - stop; } else { step = 1; start = _cairo_pen_find_active_cw_vertex_index (&stroker->pen, in_vector); if (_cairo_slope_compare (&stroker->pen.vertices[start].slope_cw, in_vector) < 0) start = _range_step (start, 1, stroker->pen.num_vertices); stop = _cairo_pen_find_active_cw_vertex_index (&stroker->pen, out_vector); if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_ccw, out_vector) > 0) { stop = _range_step (stop, -1, stroker->pen.num_vertices); if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_cw, in_vector) < 0) { goto BEVEL; } } npoints = stop - start; } stop = _range_step (stop, step, stroker->pen.num_vertices); if (npoints < 0) npoints += stroker->pen.num_vertices; npoints += 3; if (npoints <= 1) goto BEVEL; if (npoints > ARRAY_LENGTH (stack_points)) { points = _cairo_malloc_ab (npoints, sizeof (cairo_point_t)); if (unlikely (points == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } /* Construct the fan. */ npoints = 0; points[npoints++] = *inpt; for (i = start; i != stop; i = _range_step (i, step, stroker->pen.num_vertices)) { points[npoints] = *midpt; _translate_point (&points[npoints], &stroker->pen.vertices[i].point); npoints++; } points[npoints++] = *outpt; if (stroker->add_external_edge != NULL) { for (i = 0; i < npoints - 1; i++) { if (clockwise) { status = stroker->add_external_edge (stroker->closure, &points[i], &points[i+1]); } else { status = stroker->add_external_edge (stroker->closure, &points[i+1], &points[i]); } if (unlikely (status)) break; } } else { status = stroker->add_triangle_fan (stroker->closure, midpt, points, npoints); } if (points != stack_points) free (points); return status; BEVEL: /* Ensure a leak free connection... */ if (stroker->add_external_edge != NULL) { if (clockwise) return stroker->add_external_edge (stroker->closure, inpt, outpt); else return stroker->add_external_edge (stroker->closure, outpt, inpt); } else { stack_points[0] = *midpt; stack_points[1] = *inpt; stack_points[2] = *outpt; return stroker->add_triangle (stroker->closure, stack_points); } }
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; } } }
/* Is a clockwise of b? * * NOTE: The strict equality here is not significant in and of itself, * but there are functions up above that are sensitive to it, * (cf. _cairo_pen_find_active_cw_vertex_index). */ int _cairo_slope_clockwise (cairo_slope_t *a, cairo_slope_t *b) { return _cairo_slope_compare (a, b) < 0; }