1 /* cairo - a vector graphics library with display and print output
3 * Copyright © 2012 Samsung Electronics
5 * This library is free software; you can redistribute it and/or
6 * modify it either under the terms of the GNU Lesser General Public
7 * License version 2.1 as published by the Free Software Foundation
8 * (the "LGPL") or, at your option, under the terms of the Mozilla
9 * Public License Version 1.1 (the "MPL"). If you do not alter this
10 * notice, a recipient may use your version of this file under either
11 * the MPL or the LGPL.
13 * You should have received a copy of the LGPL along with this library
14 * in the file COPYING-LGPL-2.1; if not, write to the Free Software
15 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
16 * You should have received a copy of the MPL along with this library
17 * in the file COPYING-MPL-1.1
19 * The contents of this file are subject to the Mozilla Public License
20 * Version 1.1 (the "License"); you may not use this file except in
21 * compliance with the License. You may obtain a copy of the License at
22 * http://www.mozilla.org/MPL/
24 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
25 * OF ANY KIND, either express or implied. See the LGPL or the MPL for
26 * the specific language governing rights and limitations.
28 * The Original Code is the cairo graphics library.
30 * The Initial Developer of the Original Code is Red Hat, Inc.
33 * Henry Song <hsong@sisa.samsung.com>
36 #include "cairo-gl-private.h"
37 #include "cairo-slope-private.h"
39 #define SCALE_TOLERANCE 0.0000001
40 #define POINT_ADJUST 0.5
42 static inline cairo_bool_t
43 _add_cap (cairo_gl_hairline_closure_t *hairline,
46 cairo_bool_t lead_cap,
51 if (hairline->cap_style == CAIRO_LINE_CAP_BUTT)
54 dx = slope_dx * POINT_ADJUST;
55 dy = slope_dy * POINT_ADJUST;
56 hairline->line_last_capped = lead_cap;
57 cairo_matrix_transform_distance (hairline->ctm, &dx, &dy);
58 outp->x += _cairo_fixed_from_double (dx);
59 outp->y += _cairo_fixed_from_double (dy);
64 static inline cairo_bool_t
65 _compute_normalized_device_slope (double *dx, double *dy,
66 const cairo_matrix_t *ctm_inverse,
69 double dx0 = *dx, dy0 = *dy;
72 cairo_matrix_transform_distance (ctm_inverse, &dx0, &dy0);
74 if (dx0 == 0.0 && dy0 == 0.0) {
89 } else if (dy0 == 0.0) {
99 mag = hypot (dx0, dy0);
110 /* We implement hairline optimization for stroke. */
112 _cairo_gl_hairline_style_is_hairline (const cairo_stroke_style_t *style,
113 const cairo_matrix_t *ctm)
116 cairo_status_t status = _cairo_matrix_compute_basis_scale_factors (ctm, &x, &y, TRUE);
118 if (unlikely (status))
124 return style->line_width == 1.0 &&
125 (style->line_join != CAIRO_LINE_JOIN_MITER ||
126 style->miter_limit <= 10.0) &&
127 (x <= SCALE_TOLERANCE && y <= SCALE_TOLERANCE);
130 static cairo_status_t
131 _path_add_first_and_last_cap (cairo_gl_hairline_closure_t *hairline)
134 cairo_status_t status = CAIRO_STATUS_SUCCESS;
135 cairo_bool_t needs_to_cap;
137 /* check last point */
138 if (hairline->initialized) {
139 if (! hairline->line_last_capped) {
140 p[0] = hairline->line_last_point;
141 p[1] = hairline->line_last_point;
142 needs_to_cap = _add_cap (hairline,
143 hairline->line_last_dx,
144 hairline->line_last_dy,
148 status = _cairo_gl_composite_emit_point_as_single_line (hairline->ctx, p);
149 if (unlikely(status))
153 if (! hairline->stroke_first_capped) {
154 p[0] = hairline->stroke_first_point;
155 p[1] = hairline->stroke_first_point;
156 needs_to_cap = _add_cap (hairline,
157 hairline->stroke_first_dx,
158 hairline->stroke_first_dy,
163 status = _cairo_gl_composite_emit_point_as_single_line (hairline->ctx, p);
164 if (unlikely(status))
170 return CAIRO_STATUS_SUCCESS;
174 _cairo_gl_hairline_move_to (void *closure,
175 const cairo_point_t *point)
177 cairo_status_t status;
179 cairo_gl_hairline_closure_t *hairline = (cairo_gl_hairline_closure_t *)closure;
181 hairline->current_point = *point;
182 hairline->moved_to_stroke_first_point = FALSE;
184 /* check last point */
185 if (hairline->initialized) {
186 status = _path_add_first_and_last_cap (hairline);
187 if (unlikely(status))
191 hairline->line_last_capped = TRUE;
192 hairline->stroke_first_capped = FALSE;
193 hairline->stroke_first_point = *point;
194 hairline->initialized = TRUE;
196 if (hairline->dash.dashed) {
197 _cairo_stroker_dash_start (&hairline->dash);
200 return CAIRO_STATUS_SUCCESS;
204 _cairo_gl_hairline_line_to (void *closure,
205 const cairo_point_t *point)
207 double slope_dx, slope_dy;
209 cairo_gl_hairline_closure_t *hairline = (cairo_gl_hairline_closure_t *)closure;
210 cairo_point_t *p1 = &hairline->current_point;
211 cairo_slope_t dev_slope;
214 _cairo_slope_init (&dev_slope, p1, point);
215 slope_dx = _cairo_fixed_to_double (point->x - p1->x);
216 slope_dy = _cairo_fixed_to_double (point->y - p1->y);
218 if (! _compute_normalized_device_slope (&slope_dx, &slope_dy,
219 hairline->ctm_inverse, &mag))
220 return CAIRO_STATUS_SUCCESS;
222 hairline->line_last_point = *point;
223 hairline->line_last_capped = FALSE;
224 hairline->line_last_dx = slope_dx;
225 hairline->line_last_dy = slope_dy;
227 if (! hairline->moved_to_stroke_first_point) {
228 hairline->stroke_first_dx = slope_dx;
229 hairline->stroke_first_dy = slope_dy;
230 hairline->moved_to_stroke_first_point = TRUE;
233 p[0] = hairline->current_point;
235 hairline->current_point = *point;
237 return _cairo_gl_composite_emit_point_as_single_line (hairline->ctx, p);
241 _cairo_gl_hairline_line_to_dashed (void *closure,
242 const cairo_point_t *point)
245 double remain, mag, step_length = 0;
246 double slope_dx, slope_dy;
248 cairo_line_t segment;
249 cairo_gl_hairline_closure_t *hairline = (cairo_gl_hairline_closure_t *)closure;
250 cairo_point_t *p1 = &hairline->current_point;
251 cairo_slope_t dev_slope;
252 cairo_status_t status;
253 cairo_bool_t needs_to_cap;
255 _cairo_slope_init (&dev_slope, p1, point);
256 slope_dx = _cairo_fixed_to_double (point->x - p1->x);
257 slope_dy = _cairo_fixed_to_double (point->y - p1->y);
259 if (! _compute_normalized_device_slope (&slope_dx, &slope_dy,
260 hairline->ctm_inverse, &mag))
261 return CAIRO_STATUS_SUCCESS;
266 step_length = MIN (hairline->dash.dash_remain, remain);
267 remain -= step_length;
268 dx = slope_dx * (mag - remain);
269 dy = slope_dy * (mag - remain);
270 cairo_matrix_transform_distance (hairline->ctm, &dx, &dy);
271 segment.p2.x = _cairo_fixed_from_double (dx) + p1->x;
272 segment.p2.y = _cairo_fixed_from_double (dy) + p1->y;
274 if (hairline->dash.dash_on) {
277 /* Check leading cap. */
278 if (segment.p1.x == hairline->stroke_first_point.x &&
279 segment.p1.y == hairline->stroke_first_point.y) {
280 /* We are at the first stroke point, and we have
281 been here, add a leading cap. */
282 if (hairline->moved_to_stroke_first_point) {
283 if (hairline->dash.dashes[hairline->dash.dash_index] == hairline->dash.dash_remain)
284 needs_to_cap = _add_cap (hairline,
290 /* We have not been in the first stroke point,
291 this is the first line_to call sinece move_to()
292 save the slope, we need use it later. */
293 hairline->stroke_first_dx = slope_dx;
294 hairline->stroke_first_dy = slope_dy;
295 hairline->moved_to_stroke_first_point = TRUE;
298 else if (segment.p1.x == hairline->current_point.x &&
299 segment.p1.y == hairline->current_point.y) {
300 /* Start of the line segment, if we have the entire
301 dash length, we are at the begining of a dash,
302 add a leading cap. */
303 if (hairline->dash.dashes[hairline->dash.dash_index] == hairline->dash.dash_remain)
304 needs_to_cap = _add_cap (hairline,
309 /* We are in the middle of the line segment, add
311 needs_to_cap = _add_cap (hairline,
315 /* Add trailing cap. We have not exhausted line segment,
316 or we have exhausted current dash length, add a
319 hairline->dash.dash_remain - step_length < CAIRO_FIXED_ERROR_DOUBLE)
320 needs_to_cap = _add_cap (hairline,
324 /* We indicate here that we have not added a trailing
325 cap yet. If next move is move_to, we will add a
326 trailing cap, otherwise, it will be ignored. */
327 hairline->line_last_capped = FALSE;
328 hairline->line_last_point = segment.p2;
329 hairline->line_last_dx = slope_dx;
330 hairline->line_last_dy = slope_dy;
333 status = _cairo_gl_composite_emit_point_as_single_line (hairline->ctx, p);
334 if (unlikely (status))
338 _cairo_stroker_dash_step (&hairline->dash, step_length);
339 segment.p1 = segment.p2;
342 hairline->current_point = *point;
343 return CAIRO_STATUS_SUCCESS;
347 _cairo_gl_hairline_curve_to (void *closure,
348 const cairo_point_t *p0,
349 const cairo_point_t *p1,
350 const cairo_point_t *p2)
352 cairo_gl_hairline_closure_t *hairline = (cairo_gl_hairline_closure_t *)closure;
353 cairo_spline_t spline;
354 cairo_path_fixed_line_to_func_t *line_to;
356 line_to = hairline->dash.dashed ?
357 _cairo_gl_hairline_line_to_dashed :
358 _cairo_gl_hairline_line_to;
360 if (! _cairo_spline_init (&spline,
361 (cairo_spline_add_point_func_t)line_to,
363 &hairline->current_point, p0, p1, p2))
364 return _cairo_gl_hairline_line_to (closure, p2);
366 return _cairo_spline_decompose (&spline, hairline->tolerance);
370 _cairo_gl_hairline_close_path (void *closure)
372 cairo_gl_hairline_closure_t *hairline = (cairo_gl_hairline_closure_t *)closure;
374 hairline->line_last_capped = TRUE;
375 hairline->stroke_first_capped = TRUE;
377 if (hairline->dash.dashed)
378 return _cairo_gl_hairline_line_to_dashed (closure,
379 &hairline->stroke_first_point);
380 return _cairo_gl_hairline_line_to (closure, &hairline->stroke_first_point);
384 _cairo_gl_path_fixed_stroke_to_hairline (const cairo_path_fixed_t *path,
385 cairo_gl_hairline_closure_t *closure,
386 const cairo_stroke_style_t *style,
387 const cairo_matrix_t *ctm,
388 const cairo_matrix_t *ctm_inverse,
389 cairo_path_fixed_move_to_func_t *move_to,
390 cairo_path_fixed_line_to_func_t *line_to,
391 cairo_path_fixed_curve_to_func_t *curve_to,
392 cairo_path_fixed_close_path_func_t *close_path)
394 cairo_status_t status;
396 _cairo_stroker_dash_init (&closure->dash, style);
397 closure->ctm = (cairo_matrix_t *)ctm;
398 closure->ctm_inverse = (cairo_matrix_t *)ctm_inverse;
399 closure->cap_style = style->line_cap;
400 closure->initialized = FALSE;
402 status = _cairo_path_fixed_interpret (path,
408 if (unlikely (status))
411 return _path_add_first_and_last_cap (closure);