void _cairo_debug_print_boxes (FILE *stream, const cairo_boxes_t *boxes) { const struct _cairo_boxes_chunk *chunk; cairo_box_t extents; int i; _cairo_boxes_extents (boxes, &extents); fprintf (stream, "boxes x %d: (%f, %f) x (%f, %f)\n", boxes->num_boxes, _cairo_fixed_to_double (extents.p1.x), _cairo_fixed_to_double (extents.p1.y), _cairo_fixed_to_double (extents.p2.x), _cairo_fixed_to_double (extents.p2.y)); for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { for (i = 0; i < chunk->count; i++) { fprintf (stderr, " box[%d]: (%f, %f), (%f, %f)\n", i, _cairo_fixed_to_double (chunk->base[i].p1.x), _cairo_fixed_to_double (chunk->base[i].p1.y), _cairo_fixed_to_double (chunk->base[i].p2.x), _cairo_fixed_to_double (chunk->base[i].p2.y)); } } }
static cairo_status_t _cairo_path_to_qpainterpath_line_to (void *closure, const cairo_point_t *point) { qpainter_path_data *pdata = static_cast <qpainter_path_data *> (closure); double x = _cairo_fixed_to_double (point->x); double y = _cairo_fixed_to_double (point->y); if (pdata->ctm_inverse) cairo_matrix_transform_point (pdata->ctm_inverse, &x, &y); pdata->path.lineTo(x, y); return CAIRO_STATUS_SUCCESS; }
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_status_t _cairo_xml_surface_emit_clip_boxes (cairo_xml_surface_t *surface, const cairo_clip_t *clip) { cairo_box_t *box; cairo_xml_t *xml; int n; if (clip->num_boxes == 0) return CAIRO_STATUS_SUCCESS; /* skip the trivial clip covering the surface extents */ if (surface->width >= 0 && surface->height >= 0 && clip->num_boxes == 1) { box = &clip->boxes[0]; if (box->p1.x <= 0 && box->p1.y <= 0 && box->p2.x - box->p1.x >= _cairo_fixed_from_double (surface->width) && box->p2.y - box->p1.y >= _cairo_fixed_from_double (surface->height)) { return CAIRO_STATUS_SUCCESS; } } xml = to_xml (surface); _cairo_xml_printf (xml, "<clip>"); _cairo_xml_indent (xml, 2); _cairo_xml_printf (xml, "<path>"); _cairo_xml_indent (xml, 2); for (n = 0; n < clip->num_boxes; n++) { box = &clip->boxes[n]; _cairo_xml_printf_start (xml, "%f %f m", _cairo_fixed_to_double (box->p1.x), _cairo_fixed_to_double (box->p1.y)); _cairo_xml_printf_continue (xml, " %f %f l", _cairo_fixed_to_double (box->p2.x), _cairo_fixed_to_double (box->p1.y)); _cairo_xml_printf_continue (xml, " %f %f l", _cairo_fixed_to_double (box->p2.x), _cairo_fixed_to_double (box->p2.y)); _cairo_xml_printf_continue (xml, " %f %f l", _cairo_fixed_to_double (box->p1.x), _cairo_fixed_to_double (box->p2.y)); _cairo_xml_printf_end (xml, " h"); } _cairo_xml_indent (xml, -2); _cairo_xml_printf (xml, "</path>"); _cairo_xml_emit_double (xml, "tolerance", 1.0); _cairo_xml_emit_string (xml, "antialias", _antialias_to_string (CAIRO_ANTIALIAS_NONE)); _cairo_xml_emit_string (xml, "fill-rule", _fill_rule_to_string (CAIRO_FILL_RULE_WINDING)); _cairo_xml_indent (xml, -2); _cairo_xml_printf (xml, "</clip>"); return CAIRO_STATUS_SUCCESS; }
static cairo_status_t cpc_line_to (void *closure, const cairo_point_t *point) { struct cpc *cpc = static_cast <struct cpc *> (closure); if (cpc->matrix) { double x = _cairo_fixed_to_double (point->x); double y = _cairo_fixed_to_double (point->y); cairo_matrix_transform_point (cpc->matrix, &x, &y); cpc->skPath.lineTo (SkFloatToScalar (x), SkFloatToScalar (y)); } else { cpc->skPath.lineTo (CAIRO_FIXED_TO_SK_SCALAR (point->x), CAIRO_FIXED_TO_SK_SCALAR (point->y)); } return CAIRO_STATUS_SUCCESS; }
cairo_status_t _cairo_path_fixed_stroke_extents (const cairo_path_fixed_t *path, const cairo_stroke_style_t *stroke_style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, double tolerance, cairo_rectangle_int_t *extents) { cairo_polygon_t polygon; cairo_status_t status; cairo_stroke_style_t style; /* When calculating extents for vector surfaces, ensure lines thinner * than the fixed point resolution are not optimized away. */ double min_line_width = _cairo_fixed_to_double (CAIRO_FIXED_EPSILON*2); if (stroke_style->line_width < min_line_width) { style = *stroke_style; style.line_width = min_line_width; stroke_style = &style; } _cairo_polygon_init (&polygon, NULL, 0); status = _cairo_path_fixed_stroke_to_polygon (path, stroke_style, ctm, ctm_inverse, tolerance, &polygon); _cairo_box_round_to_rectangle (&polygon.extents, extents); _cairo_polygon_fini (&polygon); return status; }
static cairo_status_t _cairo_xml_emit_linear (cairo_xml_t *xml, const cairo_linear_pattern_t *linear) { _cairo_xml_printf (xml, "<linear x1='%f' y1='%f' x2='%f' y2='%f'>", _cairo_fixed_to_double (linear->p1.x), _cairo_fixed_to_double (linear->p1.y), _cairo_fixed_to_double (linear->p2.x), _cairo_fixed_to_double (linear->p2.y)); _cairo_xml_indent (xml, 2); _cairo_xml_emit_gradient (xml, &linear->base); _cairo_xml_indent (xml, -2); _cairo_xml_printf (xml, "</linear>"); return CAIRO_STATUS_SUCCESS; }
static cairo_status_t _cairo_pdf_path_rectangle (pdf_path_info_t *info, cairo_box_t *box) { double x1 = _cairo_fixed_to_double (box->p1.x); double y1 = _cairo_fixed_to_double (box->p1.y); double x2 = _cairo_fixed_to_double (box->p2.x); double y2 = _cairo_fixed_to_double (box->p2.y); cairo_matrix_transform_point (info->path_transform, &x1, &y1); cairo_matrix_transform_point (info->path_transform, &x2, &y2); _cairo_output_stream_printf (info->output, "%g %g %g %g re ", x1, y1, x2 - x1, y2 - y1); return _cairo_output_stream_get_status (info->output); }
static cairo_status_t _cairo_pdf_path_move_to (void *closure, const cairo_point_t *point) { pdf_path_info_t *info = closure; double x = _cairo_fixed_to_double (point->x); double y = _cairo_fixed_to_double (point->y); info->last_move_to_point = *point; info->has_sub_path = FALSE; cairo_matrix_transform_point (info->path_transform, &x, &y); _cairo_output_stream_printf (info->output, "%g %g m ", x, y); return _cairo_output_stream_get_status (info->output); }
static void _cairo_quartz_path_apply_func (void *info, const CGPathElement *el) { cairo_path_fixed_t *path = (cairo_path_fixed_t *) info; switch (el->type) { case kCGPathElementMoveToPoint: _cairo_path_fixed_move_to (path, _cairo_fixed_from_double(el->points[0].x), _cairo_fixed_from_double(el->points[0].y)); break; case kCGPathElementAddLineToPoint: _cairo_path_fixed_line_to (path, _cairo_fixed_from_double(el->points[0].x), _cairo_fixed_from_double(el->points[0].y)); break; case kCGPathElementAddQuadCurveToPoint: { cairo_fixed_t fx, fy; double x, y; if (!_cairo_path_fixed_get_current_point (path, &fx, &fy)) fx = fy = 0; x = _cairo_fixed_to_double (fx); y = _cairo_fixed_to_double (fy); _cairo_path_fixed_curve_to (path, _cairo_fixed_from_double((x + el->points[0].x * 2.0) / 3.0), _cairo_fixed_from_double((y + el->points[0].y * 2.0) / 3.0), _cairo_fixed_from_double((el->points[0].x * 2.0 + el->points[1].x) / 3.0), _cairo_fixed_from_double((el->points[0].y * 2.0 + el->points[1].y) / 3.0), _cairo_fixed_from_double(el->points[1].x), _cairo_fixed_from_double(el->points[1].y)); } break; case kCGPathElementAddCurveToPoint: _cairo_path_fixed_curve_to (path, _cairo_fixed_from_double(el->points[0].x), _cairo_fixed_from_double(el->points[0].y), _cairo_fixed_from_double(el->points[1].x), _cairo_fixed_from_double(el->points[1].y), _cairo_fixed_from_double(el->points[2].x), _cairo_fixed_from_double(el->points[2].y)); break; case kCGPathElementCloseSubpath: _cairo_path_fixed_close_path (path); break; } }
static cairo_status_t _cairo_stroker_spline_to (void *closure, const cairo_point_t *point, const cairo_slope_t *tangent) { cairo_stroker_t *stroker = closure; cairo_stroke_face_t new_face; double slope_dx, slope_dy; cairo_point_t points[3]; stroker->has_initial_sub_path = TRUE; if (stroker->current_point.x == point->x && stroker->current_point.y == point->y) return CAIRO_STATUS_SUCCESS; slope_dx = _cairo_fixed_to_double (tangent->dx); slope_dy = _cairo_fixed_to_double (tangent->dy); if (! _compute_normalized_device_slope (&slope_dx, &slope_dy, stroker->ctm_inverse, NULL)) return CAIRO_STATUS_SUCCESS; _compute_face (point, tangent, slope_dx, slope_dy, stroker, &new_face); assert(stroker->has_current_face); points[0] = stroker->current_face.cw; points[1] = stroker->current_face.ccw; points[2] = new_face.cw; stroker->add_triangle (stroker->closure, points); points[0] = stroker->current_face.ccw; points[1] = new_face.cw; points[2] = new_face.ccw; stroker->add_triangle (stroker->closure, points); stroker->current_face = new_face; stroker->has_current_face = TRUE; stroker->current_point = *point; return CAIRO_STATUS_SUCCESS; }
static cairo_bool_t _cairo_default_context_get_current_point (void *abstract_cr, double *x, double *y) { cairo_default_context_t *cr = abstract_cr; cairo_fixed_t x_fixed, y_fixed; if (_cairo_path_fixed_get_current_point (cr->path, &x_fixed, &y_fixed)) { *x = _cairo_fixed_to_double (x_fixed); *y = _cairo_fixed_to_double (y_fixed); _cairo_gstate_backend_to_user (cr->gstate, x, y); return TRUE; } else { return FALSE; } }
static cairo_status_t cpc_curve_to (void *closure, const cairo_point_t *p0, const cairo_point_t *p1, const cairo_point_t *p2) { struct cpc *cpc = static_cast <struct cpc *> (closure); if (cpc->matrix) { double x0 = _cairo_fixed_to_double (p0->x); double y0 = _cairo_fixed_to_double (p0->y); double x1 = _cairo_fixed_to_double (p1->x); double y1 = _cairo_fixed_to_double (p1->y); double x2 = _cairo_fixed_to_double (p2->x); double y2 = _cairo_fixed_to_double (p2->y); cairo_matrix_transform_point (cpc->matrix, &x0, &y0); cairo_matrix_transform_point (cpc->matrix, &x1, &y1); cairo_matrix_transform_point (cpc->matrix, &x2, &y2); cpc->skPath.cubicTo (SkFloatToScalar (x0), SkFloatToScalar (y0), SkFloatToScalar (x1), SkFloatToScalar (y1), SkFloatToScalar (x2), SkFloatToScalar (y2)); } else { cpc->skPath.cubicTo (CAIRO_FIXED_TO_SK_SCALAR (p0->x), CAIRO_FIXED_TO_SK_SCALAR (p0->y), CAIRO_FIXED_TO_SK_SCALAR (p1->x), CAIRO_FIXED_TO_SK_SCALAR (p1->y), CAIRO_FIXED_TO_SK_SCALAR (p2->x), CAIRO_FIXED_TO_SK_SCALAR (p2->y)); } return CAIRO_STATUS_SUCCESS; }
static cairo_status_t _cairo_pdf_path_line_to (void *closure, const cairo_point_t *point) { pdf_path_info_t *info = closure; double x = _cairo_fixed_to_double (point->x); double y = _cairo_fixed_to_double (point->y); if (info->line_cap != CAIRO_LINE_CAP_ROUND && ! info->has_sub_path && point->x == info->last_move_to_point.x && point->y == info->last_move_to_point.y) { return CAIRO_STATUS_SUCCESS; } info->has_sub_path = TRUE; cairo_matrix_transform_point (info->path_transform, &x, &y); _cairo_output_stream_printf (info->output, "%g %g l ", x, y); return _cairo_output_stream_get_status (info->output); }
static cairo_status_t _cpp_line_to (void *closure, const cairo_point_t *point) { cpp_t *cpp = closure; cairo_path_data_t *data = cpp->data; double x, y; x = _cairo_fixed_to_double (point->x); y = _cairo_fixed_to_double (point->y); _cairo_backend_to_user (cpp->cr, &x, &y); data->header.type = CAIRO_PATH_LINE_TO; data->header.length = 2; /* We index from 1 to leave room for data->header */ data[1].point.x = x; data[1].point.y = y; cpp->data += data->header.length; return CAIRO_STATUS_SUCCESS; }
static cairo_status_t _cairo_xml_curve_to (void *closure, const cairo_point_t *p1, const cairo_point_t *p2, const cairo_point_t *p3) { _cairo_xml_printf_continue (closure, " %f %f %f %f %f %f c", _cairo_fixed_to_double (p1->x), _cairo_fixed_to_double (p1->y), _cairo_fixed_to_double (p2->x), _cairo_fixed_to_double (p2->y), _cairo_fixed_to_double (p3->x), _cairo_fixed_to_double (p3->y)); return CAIRO_STATUS_SUCCESS; }
static cairo_status_t _cairo_xml_emit_radial (cairo_xml_t *xml, const cairo_radial_pattern_t *radial) { _cairo_xml_printf (xml, "<radial x1='%f' y1='%f' r1='%f' x2='%f' y2='%f' r2='%f'>", _cairo_fixed_to_double (radial->c1.x), _cairo_fixed_to_double (radial->c1.y), _cairo_fixed_to_double (radial->r1), _cairo_fixed_to_double (radial->c2.x), _cairo_fixed_to_double (radial->c2.y), _cairo_fixed_to_double (radial->r2)); _cairo_xml_indent (xml, 2); _cairo_xml_emit_gradient (xml, &radial->base); _cairo_xml_indent (xml, -2); _cairo_xml_printf (xml, "</radial>"); return CAIRO_STATUS_SUCCESS; }
/* Adjusts the fill extents (above) by the device-space pen. */ void _cairo_path_fixed_approximate_stroke_extents (const cairo_path_fixed_t *path, const cairo_stroke_style_t *style, const cairo_matrix_t *ctm, cairo_bool_t is_vector, cairo_rectangle_int_t *extents) { if (path->has_extents) { cairo_box_t box_extents; double dx, dy; _cairo_stroke_style_max_distance_from_path (style, path, ctm, &dx, &dy); if (is_vector) { /* When calculating extents for vector surfaces, ensure lines thinner * than the fixed point resolution are not optimized away. */ double min = _cairo_fixed_to_double (CAIRO_FIXED_EPSILON*2); if (dx < min) dx = min; if (dy < min) dy = min; } box_extents = path->extents; box_extents.p1.x -= _cairo_fixed_from_double (dx); box_extents.p1.y -= _cairo_fixed_from_double (dy); box_extents.p2.x += _cairo_fixed_from_double (dx); box_extents.p2.y += _cairo_fixed_from_double (dy); _cairo_box_round_to_rectangle (&box_extents, extents); } else { extents->x = extents->y = 0; extents->width = extents->height = 0; } }
static cairo_status_t _cpp_curve_to (void *closure, const cairo_point_t *p1, const cairo_point_t *p2, const cairo_point_t *p3) { cpp_t *cpp = closure; cairo_path_data_t *data = cpp->data; double x1, y1; double x2, y2; double x3, y3; x1 = _cairo_fixed_to_double (p1->x); y1 = _cairo_fixed_to_double (p1->y); _cairo_gstate_backend_to_user (cpp->gstate, &x1, &y1); x2 = _cairo_fixed_to_double (p2->x); y2 = _cairo_fixed_to_double (p2->y); _cairo_gstate_backend_to_user (cpp->gstate, &x2, &y2); x3 = _cairo_fixed_to_double (p3->x); y3 = _cairo_fixed_to_double (p3->y); _cairo_gstate_backend_to_user (cpp->gstate, &x3, &y3); data->header.type = CAIRO_PATH_CURVE_TO; data->header.length = 4; /* We index from 1 to leave room for data->header */ data[1].point.x = x1; data[1].point.y = y1; data[2].point.x = x2; data[2].point.y = y2; data[3].point.x = x3; data[3].point.y = y3; cpp->data += data->header.length; cpp->current_point = *p3; return CAIRO_STATUS_SUCCESS; }
static cairo_status_t _cairo_path_to_qpainterpath_curve_to (void *closure, const cairo_point_t *p0, const cairo_point_t *p1, const cairo_point_t *p2) { qpainter_path_data *pdata = static_cast <qpainter_path_data *> (closure); double x0 = _cairo_fixed_to_double (p0->x); double y0 = _cairo_fixed_to_double (p0->y); double x1 = _cairo_fixed_to_double (p1->x); double y1 = _cairo_fixed_to_double (p1->y); double x2 = _cairo_fixed_to_double (p2->x); double y2 = _cairo_fixed_to_double (p2->y); if (pdata->ctm_inverse) { cairo_matrix_transform_point (pdata->ctm_inverse, &x0, &y0); cairo_matrix_transform_point (pdata->ctm_inverse, &x1, &y1); cairo_matrix_transform_point (pdata->ctm_inverse, &x2, &y2); } pdata->path.cubicTo (x0, y0, x1, y1, x2, y2); return CAIRO_STATUS_SUCCESS; }
static cairo_status_t _cairo_pdf_path_curve_to (void *closure, const cairo_point_t *b, const cairo_point_t *c, const cairo_point_t *d) { pdf_path_info_t *info = closure; double bx = _cairo_fixed_to_double (b->x); double by = _cairo_fixed_to_double (b->y); double cx = _cairo_fixed_to_double (c->x); double cy = _cairo_fixed_to_double (c->y); double dx = _cairo_fixed_to_double (d->x); double dy = _cairo_fixed_to_double (d->y); info->has_sub_path = TRUE; cairo_matrix_transform_point (info->path_transform, &bx, &by); cairo_matrix_transform_point (info->path_transform, &cx, &cy); cairo_matrix_transform_point (info->path_transform, &dx, &dy); _cairo_output_stream_printf (info->output, "%g %g %g %g %g %g c ", bx, by, cx, cy, dx, dy); return _cairo_output_stream_get_status (info->output); }
/* * 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 double _compute_x_intercept (cairo_line_t *l, double inverse_slope) { return _cairo_fixed_to_double (l->p1.x) - inverse_slope * _cairo_fixed_to_double (l->p1.y); }
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_path_fixed_line_to_func_t *line_to; cairo_status_t status = CAIRO_STATUS_SUCCESS; line_to = stroker->dash.dashed ? _cairo_stroker_line_to_dashed : _cairo_stroker_line_to; if (! _cairo_spline_init (&spline, (cairo_spline_add_point_func_t)line_to, stroker, &stroker->current_point, b, c, d)) { return line_to (closure, d); } /* 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; }
cairo_status_t _cairo_type3_glyph_surface_emit_glyph (void *abstract_surface, cairo_output_stream_t *stream, unsigned long glyph_index, cairo_box_t *bbox, double *width) { cairo_type3_glyph_surface_t *surface = abstract_surface; cairo_scaled_glyph_t *scaled_glyph; cairo_status_t status, status2; double x_advance, y_advance; cairo_output_stream_t *mem_stream; cairo_matrix_t font_matrix_inverse; _cairo_type3_glyph_surface_set_stream (surface, stream); status = _cairo_scaled_glyph_lookup (surface->scaled_font, glyph_index, CAIRO_SCALED_GLYPH_INFO_METRICS | CAIRO_SCALED_GLYPH_INFO_META_SURFACE, &scaled_glyph); if (status && status != CAIRO_INT_STATUS_UNSUPPORTED) return status; if (status == CAIRO_INT_STATUS_UNSUPPORTED) { status = _cairo_scaled_glyph_lookup (surface->scaled_font, glyph_index, CAIRO_SCALED_GLYPH_INFO_METRICS, &scaled_glyph); if (status) return status; status = CAIRO_INT_STATUS_IMAGE_FALLBACK; } x_advance = scaled_glyph->metrics.x_advance; y_advance = scaled_glyph->metrics.y_advance; font_matrix_inverse = surface->scaled_font->font_matrix; status2 = cairo_matrix_invert (&font_matrix_inverse); /* The invertability of font_matrix is tested in * pdf_operators_show_glyphs before any glyphs are mappped to the * subset. */ assert (status2 == CAIRO_STATUS_SUCCESS); cairo_matrix_transform_distance (&font_matrix_inverse, &x_advance, &y_advance); *width = x_advance; *bbox = scaled_glyph->bbox; _cairo_matrix_transform_bounding_box_fixed (&surface->scaled_font->scale_inverse, bbox, NULL); _cairo_output_stream_printf (surface->stream, "%f 0 %f %f %f %f d1\n", x_advance, _cairo_fixed_to_double (bbox->p1.x), - _cairo_fixed_to_double (bbox->p2.y), _cairo_fixed_to_double (bbox->p2.x), - _cairo_fixed_to_double (bbox->p1.y)); if (status == CAIRO_STATUS_SUCCESS) { mem_stream = _cairo_memory_stream_create (); _cairo_type3_glyph_surface_set_stream (surface, mem_stream); _cairo_output_stream_printf (surface->stream, "q\n"); status = _cairo_meta_surface_replay (scaled_glyph->meta_surface, &surface->base); status = _cairo_pdf_operators_flush (&surface->pdf_operators); if (status) return status; _cairo_output_stream_printf (surface->stream, "Q\n"); _cairo_type3_glyph_surface_set_stream (surface, stream); if (status == CAIRO_STATUS_SUCCESS) _cairo_memory_stream_copy (mem_stream, stream); status2 = _cairo_output_stream_destroy (mem_stream); if (status2) return status2; } if (status == CAIRO_INT_STATUS_IMAGE_FALLBACK) status = _cairo_type3_glyph_surface_emit_fallback_image (surface, glyph_index); return status; }
static cairo_status_t _cairo_stroker_join (cairo_stroker_t *stroker, cairo_stroke_face_t *in, cairo_stroke_face_t *out) { int clockwise = _cairo_stroker_face_clockwise (out, in); cairo_point_t *inpt, *outpt; cairo_status_t status; if (in->cw.x == out->cw.x && in->cw.y == out->cw.y && in->ccw.x == out->ccw.x && in->ccw.y == out->ccw.y) { return CAIRO_STATUS_SUCCESS; } if (clockwise) { inpt = &in->ccw; outpt = &out->ccw; } else { inpt = &in->cw; outpt = &out->cw; } switch (stroker->style->line_join) { case CAIRO_LINE_JOIN_ROUND: { int i; int start, step, stop; cairo_point_t tri[3]; cairo_pen_t *pen = &stroker->pen; tri[0] = in->point; if (clockwise) { start = _cairo_pen_find_active_ccw_vertex_index (pen, &in->dev_vector); stop = _cairo_pen_find_active_ccw_vertex_index (pen, &out->dev_vector); step = -1; } else { start = _cairo_pen_find_active_cw_vertex_index (pen, &in->dev_vector); stop = _cairo_pen_find_active_cw_vertex_index (pen, &out->dev_vector); step = +1; } i = start; tri[1] = *inpt; while (i != stop) { tri[2] = in->point; _translate_point (&tri[2], &pen->vertices[i].point); status = _cairo_traps_tessellate_triangle (stroker->traps, tri); if (unlikely (status)) return status; tri[1] = tri[2]; i += step; if (i < 0) i = pen->num_vertices - 1; if (i >= pen->num_vertices) i = 0; } tri[2] = *outpt; return _cairo_traps_tessellate_triangle (stroker->traps, tri); } case CAIRO_LINE_JOIN_MITER: default: { /* dot product of incoming slope vector with outgoing slope vector */ double in_dot_out = ((-in->usr_vector.x * out->usr_vector.x)+ (-in->usr_vector.y * out->usr_vector.y)); double ml = stroker->style->miter_limit; /* Check the miter limit -- lines meeting at an acute angle * can generate long miters, the limit converts them to bevel * * Consider the miter join formed when two line segments * meet at an angle psi: * * /.\ * /. .\ * /./ \.\ * /./psi\.\ * * We can zoom in on the right half of that to see: * * |\ * | \ psi/2 * | \ * | \ * | \ * | \ * miter \ * length \ * | \ * | .\ * | . \ * |. line \ * \ width \ * \ \ * * * The right triangle in that figure, (the line-width side is * shown faintly with three '.' characters), gives us the * following expression relating miter length, angle and line * width: * * 1 /sin (psi/2) = miter_length / line_width * * The right-hand side of this relationship is the same ratio * in which the miter limit (ml) is expressed. We want to know * when the miter length is within the miter limit. That is * when the following condition holds: * * 1/sin(psi/2) <= ml * 1 <= ml sin(psi/2) * 1 <= ml² sin²(psi/2) * 2 <= ml² 2 sin²(psi/2) * 2·sin²(psi/2) = 1-cos(psi) * 2 <= ml² (1-cos(psi)) * * in · out = |in| |out| cos (psi) * * in and out are both unit vectors, so: * * in · out = cos (psi) * * 2 <= ml² (1 - in · out) * */ if (2 <= ml * ml * (1 - in_dot_out)) { double x1, y1, x2, y2; double mx, my; double dx1, dx2, dy1, dy2; cairo_point_t outer; cairo_point_t quad[4]; double ix, iy; double fdx1, fdy1, fdx2, fdy2; double mdx, mdy; /* * we've got the points already transformed to device * space, but need to do some computation with them and * also need to transform the slope from user space to * device space */ /* outer point of incoming line face */ x1 = _cairo_fixed_to_double (inpt->x); y1 = _cairo_fixed_to_double (inpt->y); dx1 = in->usr_vector.x; dy1 = in->usr_vector.y; cairo_matrix_transform_distance (stroker->ctm, &dx1, &dy1); /* outer point of outgoing line face */ x2 = _cairo_fixed_to_double (outpt->x); y2 = _cairo_fixed_to_double (outpt->y); dx2 = out->usr_vector.x; dy2 = out->usr_vector.y; cairo_matrix_transform_distance (stroker->ctm, &dx2, &dy2); /* * Compute the location of the outer corner of the miter. * That's pretty easy -- just the intersection of the two * outer edges. We've got slopes and points on each * of those edges. Compute my directly, then compute * mx by using the edge with the larger dy; that avoids * dividing by values close to zero. */ my = (((x2 - x1) * dy1 * dy2 - y2 * dx2 * dy1 + y1 * dx1 * dy2) / (dx1 * dy2 - dx2 * dy1)); if (fabs (dy1) >= fabs (dy2)) mx = (my - y1) * dx1 / dy1 + x1; else mx = (my - y2) * dx2 / dy2 + x2; /* * When the two outer edges are nearly parallel, slight * perturbations in the position of the outer points of the lines * caused by representing them in fixed point form can cause the * intersection point of the miter to move a large amount. If * that moves the miter intersection from between the two faces, * then draw a bevel instead. */ ix = _cairo_fixed_to_double (in->point.x); iy = _cairo_fixed_to_double (in->point.y); /* slope of one face */ fdx1 = x1 - ix; fdy1 = y1 - iy; /* slope of the other face */ fdx2 = x2 - ix; fdy2 = y2 - iy; /* slope from the intersection to the miter point */ mdx = mx - ix; mdy = my - iy; /* * Make sure the miter point line lies between the two * faces by comparing the slopes */ if (_cairo_slope_compare_sgn (fdx1, fdy1, mdx, mdy) != _cairo_slope_compare_sgn (fdx2, fdy2, mdx, mdy)) { /* * Draw the quadrilateral */ outer.x = _cairo_fixed_from_double (mx); outer.y = _cairo_fixed_from_double (my); quad[0] = in->point; quad[1] = *inpt; quad[2] = outer; quad[3] = *outpt; return _cairo_traps_tessellate_convex_quad (stroker->traps, quad); } } /* fall through ... */ } case CAIRO_LINE_JOIN_BEVEL: { cairo_point_t tri[3]; tri[0] = in->point; tri[1] = *inpt; tri[2] = *outpt; return _cairo_traps_tessellate_triangle (stroker->traps, tri); } } }
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_pen_stroke_spline_t spline_pen; cairo_stroke_face_t start, end; cairo_point_t extra_points[4]; cairo_point_t *a = &stroker->current_point; double initial_slope_dx, initial_slope_dy; double final_slope_dx, final_slope_dy; cairo_status_t status; status = _cairo_pen_stroke_spline_init (&spline_pen, &stroker->pen, a, b, c, d); if (status == CAIRO_INT_STATUS_DEGENERATE) return _cairo_stroker_line_to (closure, d); else if (unlikely (status)) return status; initial_slope_dx = _cairo_fixed_to_double (spline_pen.spline.initial_slope.dx); initial_slope_dy = _cairo_fixed_to_double (spline_pen.spline.initial_slope.dy); final_slope_dx = _cairo_fixed_to_double (spline_pen.spline.final_slope.dx); final_slope_dy = _cairo_fixed_to_double (spline_pen.spline.final_slope.dy); if (_compute_normalized_device_slope (&initial_slope_dx, &initial_slope_dy, stroker->ctm_inverse, NULL)) { _compute_face (a, &spline_pen.spline.initial_slope, initial_slope_dx, initial_slope_dy, stroker, &start); } if (_compute_normalized_device_slope (&final_slope_dx, &final_slope_dy, stroker->ctm_inverse, NULL)) { _compute_face (d, &spline_pen.spline.final_slope, final_slope_dx, final_slope_dy, stroker, &end); } if (stroker->has_current_face) { status = _cairo_stroker_join (stroker, &stroker->current_face, &start); if (unlikely (status)) goto CLEANUP_PEN; } else if (! stroker->has_first_face) { stroker->first_face = start; stroker->has_first_face = TRUE; } stroker->current_face = end; stroker->has_current_face = TRUE; extra_points[0] = start.cw; extra_points[0].x -= start.point.x; extra_points[0].y -= start.point.y; extra_points[1] = start.ccw; extra_points[1].x -= start.point.x; extra_points[1].y -= start.point.y; extra_points[2] = end.cw; extra_points[2].x -= end.point.x; extra_points[2].y -= end.point.y; extra_points[3] = end.ccw; extra_points[3].x -= end.point.x; extra_points[3].y -= end.point.y; status = _cairo_pen_add_points (&spline_pen.pen, extra_points, 4); if (unlikely (status)) goto CLEANUP_PEN; status = _cairo_pen_stroke_spline (&spline_pen, stroker->tolerance, stroker->traps); CLEANUP_PEN: _cairo_pen_stroke_spline_fini (&spline_pen); stroker->current_point = *d; return status; }
static void outer_join (struct stroker *stroker, const cairo_stroke_face_t *in, const cairo_stroke_face_t *out, int clockwise) { const cairo_point_t *inpt, *outpt; if (in->cw.x == out->cw.x && in->cw.y == out->cw.y && in->ccw.x == out->ccw.x && in->ccw.y == out->ccw.y) { return; } if (clockwise) { inpt = &in->cw; outpt = &out->cw; } else { inpt = &in->ccw; outpt = &out->ccw; } switch (stroker->style.line_join) { case CAIRO_LINE_JOIN_ROUND: /* construct a fan around the common midpoint */ add_fan (stroker, &in->dev_vector, &out->dev_vector, &in->point, inpt, outpt, clockwise); break; case CAIRO_LINE_JOIN_MITER: default: { /* dot product of incoming slope vector with outgoing slope vector */ double in_dot_out = -in->usr_vector.x * out->usr_vector.x + -in->usr_vector.y * out->usr_vector.y; double ml = stroker->style.miter_limit; /* Check the miter limit -- lines meeting at an acute angle * can generate long miters, the limit converts them to bevel * * Consider the miter join formed when two line segments * meet at an angle psi: * * /.\ * /. .\ * /./ \.\ * /./psi\.\ * * We can zoom in on the right half of that to see: * * |\ * | \ psi/2 * | \ * | \ * | \ * | \ * miter \ * length \ * | \ * | .\ * | . \ * |. line \ * \ width \ * \ \ * * * The right triangle in that figure, (the line-width side is * shown faintly with three '.' characters), gives us the * following expression relating miter length, angle and line * width: * * 1 /sin (psi/2) = miter_length / line_width * * The right-hand side of this relationship is the same ratio * in which the miter limit (ml) is expressed. We want to know * when the miter length is within the miter limit. That is * when the following condition holds: * * 1/sin(psi/2) <= ml * 1 <= ml sin(psi/2) * 1 <= ml² sin²(psi/2) * 2 <= ml² 2 sin²(psi/2) * 2·sin²(psi/2) = 1-cos(psi) * 2 <= ml² (1-cos(psi)) * * in · out = |in| |out| cos (psi) * * in and out are both unit vectors, so: * * in · out = cos (psi) * * 2 <= ml² (1 - in · out) * */ if (2 <= ml * ml * (1 - in_dot_out)) { double x1, y1, x2, y2; double mx, my; double dx1, dx2, dy1, dy2; double ix, iy; double fdx1, fdy1, fdx2, fdy2; double mdx, mdy; /* * we've got the points already transformed to device * space, but need to do some computation with them and * also need to transform the slope from user space to * device space */ /* outer point of incoming line face */ x1 = _cairo_fixed_to_double (inpt->x); y1 = _cairo_fixed_to_double (inpt->y); dx1 = in->usr_vector.x; dy1 = in->usr_vector.y; cairo_matrix_transform_distance (stroker->ctm, &dx1, &dy1); /* outer point of outgoing line face */ x2 = _cairo_fixed_to_double (outpt->x); y2 = _cairo_fixed_to_double (outpt->y); dx2 = out->usr_vector.x; dy2 = out->usr_vector.y; cairo_matrix_transform_distance (stroker->ctm, &dx2, &dy2); /* * Compute the location of the outer corner of the miter. * That's pretty easy -- just the intersection of the two * outer edges. We've got slopes and points on each * of those edges. Compute my directly, then compute * mx by using the edge with the larger dy; that avoids * dividing by values close to zero. */ my = (((x2 - x1) * dy1 * dy2 - y2 * dx2 * dy1 + y1 * dx1 * dy2) / (dx1 * dy2 - dx2 * dy1)); if (fabs (dy1) >= fabs (dy2)) mx = (my - y1) * dx1 / dy1 + x1; else mx = (my - y2) * dx2 / dy2 + x2; /* * When the two outer edges are nearly parallel, slight * perturbations in the position of the outer points of the lines * caused by representing them in fixed point form can cause the * intersection point of the miter to move a large amount. If * that moves the miter intersection from between the two faces, * then draw a bevel instead. */ ix = _cairo_fixed_to_double (in->point.x); iy = _cairo_fixed_to_double (in->point.y); /* slope of one face */ fdx1 = x1 - ix; fdy1 = y1 - iy; /* slope of the other face */ fdx2 = x2 - ix; fdy2 = y2 - iy; /* slope from the intersection to the miter point */ mdx = mx - ix; mdy = my - iy; /* * Make sure the miter point line lies between the two * faces by comparing the slopes */ if (slope_compare_sgn (fdx1, fdy1, mdx, mdy) != slope_compare_sgn (fdx2, fdy2, mdx, mdy)) { cairo_point_t p; p.x = _cairo_fixed_from_double (mx); p.y = _cairo_fixed_from_double (my); //*_cairo_contour_last_point (&outer->contour) = p; return; } } break; } case CAIRO_LINE_JOIN_BEVEL: break; } //contour_add_point (stroker,outer, outpt); }
static void compute_face (const cairo_point_t *point, const cairo_slope_t *dev_slope, struct stroker *stroker, cairo_stroke_face_t *face) { double face_dx, face_dy; cairo_point_t offset_ccw, offset_cw; double slope_dx, slope_dy; slope_dx = _cairo_fixed_to_double (dev_slope->dx); slope_dy = _cairo_fixed_to_double (dev_slope->dy); face->length = normalize_slope (&slope_dx, &slope_dy); face->dev_slope.x = slope_dx; face->dev_slope.y = slope_dy; /* * rotate to get a line_width/2 vector along the face, note that * the vector must be rotated the right direction in device space, * but by 90° in user space. So, the rotation depends on * whether the ctm reflects or not, and that can be determined * by looking at the determinant of the matrix. */ if (! _cairo_matrix_is_identity (stroker->ctm_inverse)) { /* Normalize the matrix! */ cairo_matrix_transform_distance (stroker->ctm_inverse, &slope_dx, &slope_dy); normalize_slope (&slope_dx, &slope_dy); if (stroker->ctm_det_positive) { face_dx = - slope_dy * (stroker->style.line_width / 2.0); face_dy = slope_dx * (stroker->style.line_width / 2.0); } else { face_dx = slope_dy * (stroker->style.line_width / 2.0); face_dy = - slope_dx * (stroker->style.line_width / 2.0); } /* back to device space */ cairo_matrix_transform_distance (stroker->ctm, &face_dx, &face_dy); } else { face_dx = - slope_dy * (stroker->style.line_width / 2.0); face_dy = slope_dx * (stroker->style.line_width / 2.0); } offset_ccw.x = _cairo_fixed_from_double (face_dx); offset_ccw.y = _cairo_fixed_from_double (face_dy); offset_cw.x = -offset_ccw.x; offset_cw.y = -offset_ccw.y; face->ccw = *point; translate_point (&face->ccw, &offset_ccw); face->point = *point; face->cw = *point; translate_point (&face->cw, &offset_cw); face->usr_vector.x = slope_dx; face->usr_vector.y = slope_dy; face->dev_vector = *dev_slope; }
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; }