summaryrefslogtreecommitdiff
path: root/src/cairo-gl-hairline-stroke.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cairo-gl-hairline-stroke.c')
-rwxr-xr-xsrc/cairo-gl-hairline-stroke.c412
1 files changed, 412 insertions, 0 deletions
diff --git a/src/cairo-gl-hairline-stroke.c b/src/cairo-gl-hairline-stroke.c
new file mode 100755
index 000000000..9ff59e502
--- /dev/null
+++ b/src/cairo-gl-hairline-stroke.c
@@ -0,0 +1,412 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2012 Samsung Electronics
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ * Henry Song <hsong@sisa.samsung.com>
+ */
+
+#include "cairo-gl-private.h"
+#include "cairo-slope-private.h"
+
+#define SCALE_TOLERANCE 0.0000001
+#define POINT_ADJUST 0.5
+
+static inline cairo_bool_t
+_add_cap (cairo_gl_hairline_closure_t *hairline,
+ double slope_dx,
+ double slope_dy,
+ cairo_bool_t lead_cap,
+ cairo_point_t *outp)
+{
+ double dx, dy;
+
+ if (hairline->cap_style == CAIRO_LINE_CAP_BUTT)
+ return FALSE;
+
+ dx = slope_dx * POINT_ADJUST;
+ dy = slope_dy * POINT_ADJUST;
+ hairline->line_last_capped = lead_cap;
+ cairo_matrix_transform_distance (hairline->ctm, &dx, &dy);
+ outp->x += _cairo_fixed_from_double (dx);
+ outp->y += _cairo_fixed_from_double (dy);
+
+ return TRUE;
+}
+
+static inline cairo_bool_t
+_compute_normalized_device_slope (double *dx, double *dy,
+ const cairo_matrix_t *ctm_inverse,
+ double *mag_out)
+{
+ double dx0 = *dx, dy0 = *dy;
+ double mag;
+
+ cairo_matrix_transform_distance (ctm_inverse, &dx0, &dy0);
+
+ if (dx0 == 0.0 && dy0 == 0.0) {
+ if (mag_out)
+ *mag_out = 0.0;
+ return FALSE;
+ }
+
+ if (dx0 == 0.0) {
+ *dx = 0.0;
+ if (dy0 > 0.0) {
+ mag = dy0;
+ *dy = 1.0;
+ } else {
+ mag = -dy0;
+ *dy = -1.0;
+ }
+ } else if (dy0 == 0.0) {
+ *dy = 0.0;
+ if (dx0 > 0.0) {
+ mag = dx0;
+ *dx = 1.0;
+ } else {
+ mag = -dx0;
+ *dx = -1.0;
+ }
+ } else {
+ mag = hypot (dx0, dy0);
+ *dx = dx0 / mag;
+ *dy = dy0 / mag;
+ }
+
+ if (mag_out)
+ *mag_out = mag;
+
+ return TRUE;
+}
+
+/* We implement hairline optimization for stroke. */
+cairo_bool_t
+_cairo_gl_hairline_style_is_hairline (const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm)
+{
+ double x, y;
+ cairo_status_t status = _cairo_matrix_compute_basis_scale_factors (ctm, &x, &y, TRUE);
+
+ if (unlikely (status))
+ return FALSE;
+
+ x = fabs (x - 1.0);
+ y = fabs (y - 1.0);
+
+ return style->line_width == 1.0 &&
+ (style->line_join != CAIRO_LINE_JOIN_MITER ||
+ style->miter_limit <= 10.0) &&
+ (x <= SCALE_TOLERANCE && y <= SCALE_TOLERANCE);
+}
+
+static cairo_status_t
+_path_add_first_and_last_cap (cairo_gl_hairline_closure_t *hairline)
+{
+ cairo_point_t p[2];
+ cairo_status_t status = CAIRO_STATUS_SUCCESS;
+ cairo_bool_t needs_to_cap;
+
+ /* check last point */
+ if (hairline->initialized) {
+ if (! hairline->line_last_capped) {
+ p[0] = hairline->line_last_point;
+ p[1] = hairline->line_last_point;
+ needs_to_cap = _add_cap (hairline,
+ hairline->line_last_dx,
+ hairline->line_last_dy,
+ FALSE,
+ &p[1]);
+ if (needs_to_cap) {
+ status = _cairo_gl_composite_emit_point_as_single_line (hairline->ctx, p);
+ if (unlikely(status))
+ return status;
+ }
+ }
+ if (! hairline->stroke_first_capped) {
+ p[0] = hairline->stroke_first_point;
+ p[1] = hairline->stroke_first_point;
+ needs_to_cap = _add_cap (hairline,
+ hairline->stroke_first_dx,
+ hairline->stroke_first_dy,
+ TRUE,
+ &p[0]);
+
+ if (needs_to_cap) {
+ status = _cairo_gl_composite_emit_point_as_single_line (hairline->ctx, p);
+ if (unlikely(status))
+ return status;
+ }
+ }
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+_cairo_gl_hairline_move_to (void *closure,
+ const cairo_point_t *point)
+{
+ cairo_status_t status;
+
+ cairo_gl_hairline_closure_t *hairline = (cairo_gl_hairline_closure_t *)closure;
+
+ hairline->current_point = *point;
+ hairline->moved_to_stroke_first_point = FALSE;
+
+ /* check last point */
+ if (hairline->initialized) {
+ status = _path_add_first_and_last_cap (hairline);
+ if (unlikely(status))
+ return status;
+ }
+
+ hairline->line_last_capped = TRUE;
+ hairline->stroke_first_capped = FALSE;
+ hairline->stroke_first_point = *point;
+ hairline->initialized = TRUE;
+
+ if (hairline->dash.dashed) {
+ _cairo_stroker_dash_start (&hairline->dash);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+_cairo_gl_hairline_line_to (void *closure,
+ const cairo_point_t *point)
+{
+ double slope_dx, slope_dy;
+ double mag;
+ cairo_gl_hairline_closure_t *hairline = (cairo_gl_hairline_closure_t *)closure;
+ cairo_point_t *p1 = &hairline->current_point;
+ cairo_slope_t dev_slope;
+ cairo_point_t p[2];
+
+ _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);
+
+ if (! _compute_normalized_device_slope (&slope_dx, &slope_dy,
+ hairline->ctm_inverse, &mag))
+ return CAIRO_STATUS_SUCCESS;
+
+ hairline->line_last_point = *point;
+ hairline->line_last_capped = FALSE;
+ hairline->line_last_dx = slope_dx;
+ hairline->line_last_dy = slope_dy;
+
+ if (! hairline->moved_to_stroke_first_point) {
+ hairline->stroke_first_dx = slope_dx;
+ hairline->stroke_first_dy = slope_dy;
+ hairline->moved_to_stroke_first_point = TRUE;
+ }
+
+ p[0] = hairline->current_point;
+ p[1] = *point;
+ hairline->current_point = *point;
+
+ return _cairo_gl_composite_emit_point_as_single_line (hairline->ctx, p);
+}
+
+cairo_status_t
+_cairo_gl_hairline_line_to_dashed (void *closure,
+ const cairo_point_t *point)
+{
+ cairo_point_t p[2];
+ double remain, mag, step_length = 0;
+ double slope_dx, slope_dy;
+ double dx, dy;
+ cairo_line_t segment;
+ cairo_gl_hairline_closure_t *hairline = (cairo_gl_hairline_closure_t *)closure;
+ cairo_point_t *p1 = &hairline->current_point;
+ cairo_slope_t dev_slope;
+ cairo_status_t status;
+ cairo_bool_t needs_to_cap;
+
+ _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);
+
+ if (! _compute_normalized_device_slope (&slope_dx, &slope_dy,
+ hairline->ctm_inverse, &mag))
+ return CAIRO_STATUS_SUCCESS;
+
+ remain = mag;
+ segment.p1 = *p1;
+ while (remain) {
+ step_length = MIN (hairline->dash.dash_remain, remain);
+ remain -= step_length;
+ dx = slope_dx * (mag - remain);
+ dy = slope_dy * (mag - remain);
+ cairo_matrix_transform_distance (hairline->ctm, &dx, &dy);
+ segment.p2.x = _cairo_fixed_from_double (dx) + p1->x;
+ segment.p2.y = _cairo_fixed_from_double (dy) + p1->y;
+
+ if (hairline->dash.dash_on) {
+ p[0] = segment.p1;
+ p[1] = segment.p2;
+ /* Check leading cap. */
+ if (segment.p1.x == hairline->stroke_first_point.x &&
+ segment.p1.y == hairline->stroke_first_point.y) {
+ /* We are at the first stroke point, and we have
+ been here, add a leading cap. */
+ if (hairline->moved_to_stroke_first_point) {
+ if (hairline->dash.dashes[hairline->dash.dash_index] == hairline->dash.dash_remain)
+ needs_to_cap = _add_cap (hairline,
+ slope_dx,
+ slope_dy,
+ TRUE, &p[0]);
+ }
+ else {
+ /* We have not been in the first stroke point,
+ this is the first line_to call sinece move_to()
+ save the slope, we need use it later. */
+ hairline->stroke_first_dx = slope_dx;
+ hairline->stroke_first_dy = slope_dy;
+ hairline->moved_to_stroke_first_point = TRUE;
+ }
+ }
+ else if (segment.p1.x == hairline->current_point.x &&
+ segment.p1.y == hairline->current_point.y) {
+ /* Start of the line segment, if we have the entire
+ dash length, we are at the begining of a dash,
+ add a leading cap. */
+ if (hairline->dash.dashes[hairline->dash.dash_index] == hairline->dash.dash_remain)
+ needs_to_cap = _add_cap (hairline,
+ slope_dx, slope_dy,
+ TRUE, &p[0]);
+ }
+ else
+ /* We are in the middle of the line segment, add
+ a leading cap. */
+ needs_to_cap = _add_cap (hairline,
+ slope_dx, slope_dy,
+ TRUE, &p[0]);
+
+ /* Add trailing cap. We have not exhausted line segment,
+ or we have exhausted current dash length, add a
+ trailing cap. */
+ if (remain ||
+ hairline->dash.dash_remain - step_length < CAIRO_FIXED_ERROR_DOUBLE)
+ needs_to_cap = _add_cap (hairline,
+ slope_dx, slope_dy,
+ FALSE, &p[1]);
+ else {
+ /* We indicate here that we have not added a trailing
+ cap yet. If next move is move_to, we will add a
+ trailing cap, otherwise, it will be ignored. */
+ hairline->line_last_capped = FALSE;
+ hairline->line_last_point = segment.p2;
+ hairline->line_last_dx = slope_dx;
+ hairline->line_last_dy = slope_dy;
+ }
+
+ status = _cairo_gl_composite_emit_point_as_single_line (hairline->ctx, p);
+ if (unlikely (status))
+ return status;
+ }
+
+ _cairo_stroker_dash_step (&hairline->dash, step_length);
+ segment.p1 = segment.p2;
+ }
+
+ hairline->current_point = *point;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+_cairo_gl_hairline_curve_to (void *closure,
+ const cairo_point_t *p0,
+ const cairo_point_t *p1,
+ const cairo_point_t *p2)
+{
+ cairo_gl_hairline_closure_t *hairline = (cairo_gl_hairline_closure_t *)closure;
+ cairo_spline_t spline;
+ cairo_path_fixed_line_to_func_t *line_to;
+
+ line_to = hairline->dash.dashed ?
+ _cairo_gl_hairline_line_to_dashed :
+ _cairo_gl_hairline_line_to;
+
+ if (! _cairo_spline_init (&spline,
+ (cairo_spline_add_point_func_t)line_to,
+ closure,
+ &hairline->current_point, p0, p1, p2))
+ return _cairo_gl_hairline_line_to (closure, p2);
+
+ return _cairo_spline_decompose (&spline, hairline->tolerance);
+}
+
+cairo_status_t
+_cairo_gl_hairline_close_path (void *closure)
+{
+ cairo_gl_hairline_closure_t *hairline = (cairo_gl_hairline_closure_t *)closure;
+
+ hairline->line_last_capped = TRUE;
+ hairline->stroke_first_capped = TRUE;
+
+ if (hairline->dash.dashed)
+ return _cairo_gl_hairline_line_to_dashed (closure,
+ &hairline->stroke_first_point);
+ return _cairo_gl_hairline_line_to (closure, &hairline->stroke_first_point);
+}
+
+cairo_status_t
+_cairo_gl_path_fixed_stroke_to_hairline (const cairo_path_fixed_t *path,
+ cairo_gl_hairline_closure_t *closure,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ cairo_path_fixed_move_to_func_t *move_to,
+ cairo_path_fixed_line_to_func_t *line_to,
+ cairo_path_fixed_curve_to_func_t *curve_to,
+ cairo_path_fixed_close_path_func_t *close_path)
+{
+ cairo_status_t status;
+
+ _cairo_stroker_dash_init (&closure->dash, style);
+ closure->ctm = (cairo_matrix_t *)ctm;
+ closure->ctm_inverse = (cairo_matrix_t *)ctm_inverse;
+ closure->cap_style = style->line_cap;
+ closure->initialized = FALSE;
+
+ status = _cairo_path_fixed_interpret (path,
+ move_to,
+ line_to,
+ curve_to,
+ close_path,
+ (void *) closure);
+ if (unlikely (status))
+ return status;
+
+ return _path_add_first_and_last_cap (closure);
+}