/* assumes a has been previously added */ void _cairo_box_add_curve_to (cairo_box_t *extents, const cairo_point_t *a, const cairo_point_t *b, const cairo_point_t *c, const cairo_point_t *d) { _cairo_box_add_point (extents, d); if (!_cairo_box_contains_point (extents, b) || !_cairo_box_contains_point (extents, c)) { cairo_status_t status; status = _cairo_spline_bound (_cairo_box_add_spline_point, extents, a, b, c, d); assert (status == CAIRO_STATUS_SUCCESS); } }
cairo_bool_t _cairo_spline_intersects (const cairo_point_t *a, const cairo_point_t *b, const cairo_point_t *c, const cairo_point_t *d, const cairo_box_t *box) { cairo_box_t bounds; if (_cairo_box_contains_point (box, a) || _cairo_box_contains_point (box, b) || _cairo_box_contains_point (box, c) || _cairo_box_contains_point (box, d)) { return TRUE; } bounds.p2 = bounds.p1 = *a; _cairo_box_add_point (&bounds, b); _cairo_box_add_point (&bounds, c); _cairo_box_add_point (&bounds, d); if (bounds.p2.x <= box->p1.x || bounds.p1.x >= box->p2.x || bounds.p2.y <= box->p1.y || bounds.p1.y >= box->p2.y) { return FALSE; } #if 0 /* worth refining? */ bounds.p2 = bounds.p1 = *a; _cairo_box_add_curve_to (&bounds, b, c, d); if (bounds.p2.x <= box->p1.x || bounds.p1.x >= box->p2.x || bounds.p2.y <= box->p1.y || bounds.p1.y >= box->p2.y) { return FALSE; } #endif return TRUE; }
/* * 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, cairo_bool_t clockwise, struct stroke_contour *c) { cairo_pen_t *pen = &stroker->pen; int start, stop; if (stroker->has_bounds && ! _cairo_box_contains_point (&stroker->bounds, midpt)) return; assert (stroker->pen.num_vertices); if (clockwise) { _cairo_pen_find_active_cw_vertices (pen, in_vector, out_vector, &start, &stop); while (start != stop) { cairo_point_t p = *midpt; translate_point (&p, &pen->vertices[start].point); contour_add_point (stroker, c, &p); if (++start == pen->num_vertices) start = 0; } } else { _cairo_pen_find_active_ccw_vertices (pen, in_vector, out_vector, &start, &stop); while (start != stop) { cairo_point_t p = *midpt; translate_point (&p, &pen->vertices[start].point); contour_add_point (stroker, c, &p); if (start-- == 0) start += pen->num_vertices; } } }
cairo_bool_t _cairo_box_intersects_line_segment (cairo_box_t *box, cairo_line_t *line) { cairo_fixed_t t1=0, t2=0, t3=0, t4=0; cairo_int64_t t1y, t2y, t3x, t4x; cairo_fixed_t xlen, ylen; if (_cairo_box_contains_point (box, &line->p1) || _cairo_box_contains_point (box, &line->p2)) return TRUE; xlen = P2x - P1x; ylen = P2y - P1y; if (xlen) { if (xlen > 0) { t1 = B1x - P1x; t2 = B2x - P1x; } else { t1 = P1x - B2x; t2 = P1x - B1x; xlen = - xlen; } if ((t1 < 0 || t1 > xlen) && (t2 < 0 || t2 > xlen)) return FALSE; } else { /* Fully vertical line -- check that X is in bounds */ if (P1x < B1x || P1x > B2x) return FALSE; } if (ylen) { if (ylen > 0) { t3 = B1y - P1y; t4 = B2y - P1y; } else { t3 = P1y - B2y; t4 = P1y - B1y; ylen = - ylen; } if ((t3 < 0 || t3 > ylen) && (t4 < 0 || t4 > ylen)) return FALSE; } else { /* Fully horizontal line -- check Y */ if (P1y < B1y || P1y > B2y) return FALSE; } /* If we had a horizontal or vertical line, then it's already been checked */ if (P1x == P2x || P1y == P2y) return TRUE; /* Check overlap. Note that t1 < t2 and t3 < t4 here. */ t1y = _cairo_int32x32_64_mul (t1, ylen); t2y = _cairo_int32x32_64_mul (t2, ylen); t3x = _cairo_int32x32_64_mul (t3, xlen); t4x = _cairo_int32x32_64_mul (t4, xlen); if (_cairo_int64_lt(t1y, t4x) && _cairo_int64_lt(t3x, t2y)) return TRUE; return FALSE; }
/* * 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; }
/* * 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; cairo_pen_t *pen = &stroker->pen; int start, stop, num_points = 0; cairo_status_t status; if (stroker->has_bounds && ! _cairo_box_contains_point (&stroker->bounds, midpt)) goto BEVEL; assert (stroker->pen.num_vertices); if (clockwise) { _cairo_pen_find_active_ccw_vertices (pen, in_vector, out_vector, &start, &stop); if (stroker->add_external_edge) { cairo_point_t last; last = *inpt; while (start != stop) { cairo_point_t p = *midpt; _translate_point (&p, &pen->vertices[start].point); status = stroker->add_external_edge (stroker->closure, &last, &p); if (unlikely (status)) return status; last = p; if (start-- == 0) start += pen->num_vertices; } status = stroker->add_external_edge (stroker->closure, &last, outpt); } else { if (start == stop) goto BEVEL; num_points = stop - start; if (num_points < 0) num_points += pen->num_vertices; num_points += 2; if (num_points > ARRAY_LENGTH(stack_points)) { points = _cairo_malloc_ab (num_points, sizeof (cairo_point_t)); if (unlikely (points == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } points[0] = *inpt; num_points = 1; while (start != stop) { points[num_points] = *midpt; _translate_point (&points[num_points], &pen->vertices[start].point); num_points++; if (start-- == 0) start += pen->num_vertices; } points[num_points++] = *outpt; } } else { _cairo_pen_find_active_cw_vertices (pen, in_vector, out_vector, &start, &stop); if (stroker->add_external_edge) { cairo_point_t last; last = *inpt; while (start != stop) { cairo_point_t p = *midpt; _translate_point (&p, &pen->vertices[start].point); status = stroker->add_external_edge (stroker->closure, &p, &last); if (unlikely (status)) return status; last = p; if (++start == pen->num_vertices) start = 0; } status = stroker->add_external_edge (stroker->closure, outpt, &last); } else { if (start == stop) goto BEVEL; num_points = stop - start; if (num_points < 0) num_points += pen->num_vertices; num_points += 2; if (num_points > ARRAY_LENGTH(stack_points)) { points = _cairo_malloc_ab (num_points, sizeof (cairo_point_t)); if (unlikely (points == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } points[0] = *inpt; num_points = 1; while (start != stop) { points[num_points] = *midpt; _translate_point (&points[num_points], &pen->vertices[start].point); num_points++; if (++start == pen->num_vertices) start = 0; } points[num_points++] = *outpt; } } if (num_points) { status = stroker->add_triangle_fan (stroker->closure, midpt, points, num_points); } 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); } }
static cairo_status_t _cairo_rectilinear_stroker_line_to_dashed (void *closure, const cairo_point_t *point) { cairo_rectilinear_stroker_t *stroker = closure; const cairo_point_t *a = &stroker->current_point; const cairo_point_t *b = point; cairo_bool_t fully_in_bounds; double sf, sign, remain; cairo_fixed_t mag; cairo_status_t status; cairo_line_t segment; cairo_bool_t dash_on = FALSE; unsigned is_horizontal; /* We don't draw anything for degenerate paths. */ if (a->x == b->x && a->y == b->y) return CAIRO_STATUS_SUCCESS; /* We only support horizontal or vertical elements. */ assert (a->x == b->x || a->y == b->y); fully_in_bounds = TRUE; if (stroker->has_bounds && (! _cairo_box_contains_point (&stroker->bounds, a) || ! _cairo_box_contains_point (&stroker->bounds, b))) { fully_in_bounds = FALSE; } is_horizontal = a->y == b->y; if (is_horizontal) { mag = b->x - a->x; sf = fabs (stroker->ctm->xx); } else { mag = b->y - a->y; sf = fabs (stroker->ctm->yy); } if (mag < 0) { remain = _cairo_fixed_to_double (-mag); sign = 1.; } else { remain = _cairo_fixed_to_double (mag); is_horizontal |= FORWARDS; sign = -1.; } segment.p2 = segment.p1 = *a; while (remain > 0.) { double step_length; step_length = MIN (sf * stroker->dash.dash_remain, remain); remain -= step_length; mag = _cairo_fixed_from_double (sign*remain); if (is_horizontal & 0x1) segment.p2.x = b->x + mag; else segment.p2.y = b->y + mag; if (stroker->dash.dash_on && (fully_in_bounds || _cairo_box_intersects_line_segment (&stroker->bounds, &segment))) { status = _cairo_rectilinear_stroker_add_segment (stroker, &segment.p1, &segment.p2, is_horizontal | (remain <= 0.) << 2); if (unlikely (status)) return status; dash_on = TRUE; } else { dash_on = FALSE; } _cairo_stroker_dash_step (&stroker->dash, step_length / sf); segment.p1 = segment.p2; } if (stroker->dash.dash_on && ! dash_on && (fully_in_bounds || _cairo_box_intersects_line_segment (&stroker->bounds, &segment))) { /* 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. */ status = _cairo_rectilinear_stroker_add_segment (stroker, &segment.p1, &segment.p1, is_horizontal | JOIN); if (unlikely (status)) return status; } stroker->current_point = *point; stroker->open_sub_path = TRUE; return CAIRO_STATUS_SUCCESS; }