diff options
author | Rick Yorgason <rick@firefang.com> | 2021-08-15 06:58:54 +0000 |
---|---|---|
committer | Adrian Johnson <ajohnson@redneon.com> | 2021-08-15 06:58:54 +0000 |
commit | ecec0419f8e178d71e449b52acfdfe9ac03aed37 (patch) | |
tree | e6ede3cdc9a0ae02a6f56dfec38ff8b05cdcf9bb | |
parent | 4dd48f0979f693dfc515eb3eff266ec6429f8b33 (diff) | |
download | cairo-ecec0419f8e178d71e449b52acfdfe9ac03aed37.tar.gz |
Added hairline support to cairo
66 files changed, 427 insertions, 25 deletions
diff --git a/src/cairo-backend-private.h b/src/cairo-backend-private.h index 67607c12f..86689c795 100644 --- a/src/cairo-backend-private.h +++ b/src/cairo-backend-private.h @@ -68,6 +68,7 @@ struct _cairo_backend { cairo_status_t (*set_line_cap) (void *cr, cairo_line_cap_t line_cap); cairo_status_t (*set_line_join) (void *cr, cairo_line_join_t line_join); cairo_status_t (*set_line_width) (void *cr, double line_width); + cairo_status_t (*set_hairline) (void *cr, cairo_bool_t set_hairline); cairo_status_t (*set_miter_limit) (void *cr, double limit); cairo_status_t (*set_opacity) (void *cr, double opacity); cairo_status_t (*set_operator) (void *cr, cairo_operator_t op); @@ -79,6 +80,7 @@ struct _cairo_backend { cairo_line_cap_t (*get_line_cap) (void *cr); cairo_line_join_t (*get_line_join) (void *cr); double (*get_line_width) (void *cr); + cairo_bool_t (*get_hairline) (void *cr); double (*get_miter_limit) (void *cr); double (*get_opacity) (void *cr); cairo_operator_t (*get_operator) (void *cr); diff --git a/src/cairo-compositor.c b/src/cairo-compositor.c index b31413b99..2f49f7f20 100644 --- a/src/cairo-compositor.c +++ b/src/cairo-compositor.c @@ -122,18 +122,18 @@ _cairo_compositor_mask (const cairo_compositor_t *compositor, return status; } -cairo_int_status_t -_cairo_compositor_stroke (const cairo_compositor_t *compositor, - cairo_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_path_fixed_t *path, - const cairo_stroke_style_t *style, - const cairo_matrix_t *ctm, - const cairo_matrix_t *ctm_inverse, - double tolerance, - cairo_antialias_t antialias, - const cairo_clip_t *clip) +static cairo_int_status_t +_cairo_compositor_stroke_impl (const cairo_compositor_t *compositor, + cairo_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_path_fixed_t *path, + const cairo_stroke_style_t *style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_antialias_t antialias, + const cairo_clip_t *clip) { cairo_composite_rectangles_t extents; cairo_int_status_t status; @@ -176,6 +176,48 @@ _cairo_compositor_stroke (const cairo_compositor_t *compositor, } cairo_int_status_t +_cairo_compositor_stroke (const cairo_compositor_t *compositor, + cairo_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_path_fixed_t *path, + const cairo_stroke_style_t *style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_antialias_t antialias, + const cairo_clip_t *clip) +{ + if (!style->is_hairline) + return _cairo_compositor_stroke_impl (compositor, surface, + op, source, path, + style, ctm, ctm_inverse, + tolerance, antialias, clip); + else { + cairo_stroke_style_t hairline_style; + cairo_status_t status; + cairo_matrix_t identity; + + status = _cairo_stroke_style_init_copy (&hairline_style, style); + if (unlikely (status)) + return status; + + hairline_style.line_width = 1.0; + + cairo_matrix_init_identity (&identity); + + status = _cairo_compositor_stroke_impl (compositor, surface, + op, source, path, + &hairline_style, &identity, &identity, + tolerance, antialias, clip); + + _cairo_stroke_style_fini (&hairline_style); + + return status; + } +} + +cairo_int_status_t _cairo_compositor_fill (const cairo_compositor_t *compositor, cairo_surface_t *surface, cairo_operator_t op, diff --git a/src/cairo-default-context.c b/src/cairo-default-context.c index d2c9cae10..567c5d4d5 100644 --- a/src/cairo-default-context.c +++ b/src/cairo-default-context.c @@ -404,6 +404,14 @@ _cairo_default_context_set_line_width (void *abstract_cr, } static cairo_status_t +_cairo_default_context_set_hairline (void *abstract_cr, cairo_bool_t set_hairline) +{ + cairo_default_context_t *cr = abstract_cr; + + return _cairo_gstate_set_hairline (cr->gstate, set_hairline); +} + +static cairo_status_t _cairo_default_context_set_line_cap (void *abstract_cr, cairo_line_cap_t line_cap) { @@ -477,6 +485,14 @@ _cairo_default_context_get_line_width (void *abstract_cr) return _cairo_gstate_get_line_width (cr->gstate); } +static cairo_bool_t +_cairo_default_context_get_hairline (void *abstract_cr) +{ + cairo_default_context_t *cr = abstract_cr; + + return _cairo_gstate_get_hairline (cr->gstate); +} + static cairo_line_cap_t _cairo_default_context_get_line_cap (void *abstract_cr) { @@ -1365,6 +1381,7 @@ static const cairo_backend_t _cairo_default_context_backend = { _cairo_default_context_set_line_cap, _cairo_default_context_set_line_join, _cairo_default_context_set_line_width, + _cairo_default_context_set_hairline, _cairo_default_context_set_miter_limit, _cairo_default_context_set_opacity, _cairo_default_context_set_operator, @@ -1375,6 +1392,7 @@ static const cairo_backend_t _cairo_default_context_backend = { _cairo_default_context_get_line_cap, _cairo_default_context_get_line_join, _cairo_default_context_get_line_width, + _cairo_default_context_get_hairline, _cairo_default_context_get_miter_limit, _cairo_default_context_get_opacity, _cairo_default_context_get_operator, diff --git a/src/cairo-gstate-private.h b/src/cairo-gstate-private.h index 198c66998..fe1556b03 100644 --- a/src/cairo-gstate-private.h +++ b/src/cairo-gstate-private.h @@ -140,6 +140,12 @@ cairo_private double _cairo_gstate_get_line_width (cairo_gstate_t *gstate); cairo_private cairo_status_t +_cairo_gstate_set_hairline (cairo_gstate_t *gstate, cairo_bool_t set_hairline); + +cairo_private cairo_bool_t +_cairo_gstate_get_hairline (cairo_gstate_t *gstate); + +cairo_private cairo_status_t _cairo_gstate_set_line_cap (cairo_gstate_t *gstate, cairo_line_cap_t line_cap); cairo_private cairo_line_cap_t diff --git a/src/cairo-gstate.c b/src/cairo-gstate.c index 64060c4fc..a8c67e718 100644 --- a/src/cairo-gstate.c +++ b/src/cairo-gstate.c @@ -470,7 +470,10 @@ _cairo_gstate_get_fill_rule (cairo_gstate_t *gstate) cairo_status_t _cairo_gstate_set_line_width (cairo_gstate_t *gstate, double width) { - gstate->stroke_style.line_width = width; + if (gstate->stroke_style.is_hairline) + gstate->stroke_style.pre_hairline_line_width = width; + else + gstate->stroke_style.line_width = width; return CAIRO_STATUS_SUCCESS; } @@ -482,6 +485,29 @@ _cairo_gstate_get_line_width (cairo_gstate_t *gstate) } cairo_status_t +_cairo_gstate_set_hairline (cairo_gstate_t *gstate, cairo_bool_t set_hairline) +{ + if (gstate->stroke_style.is_hairline != set_hairline) { + gstate->stroke_style.is_hairline = set_hairline; + + if (set_hairline) { + gstate->stroke_style.pre_hairline_line_width = gstate->stroke_style.line_width; + gstate->stroke_style.line_width = 0.0; + } else { + gstate->stroke_style.line_width = gstate->stroke_style.pre_hairline_line_width; + } + } + + return CAIRO_STATUS_SUCCESS; +} + +cairo_bool_t +_cairo_gstate_get_hairline (cairo_gstate_t *gstate) +{ + return gstate->stroke_style.is_hairline; +} + +cairo_status_t _cairo_gstate_set_line_cap (cairo_gstate_t *gstate, cairo_line_cap_t line_cap) { gstate->stroke_style.line_cap = line_cap; @@ -1172,7 +1198,7 @@ _cairo_gstate_stroke (cairo_gstate_t *gstate, cairo_path_fixed_t *path) if (gstate->op == CAIRO_OPERATOR_DEST) return CAIRO_STATUS_SUCCESS; - if (gstate->stroke_style.line_width <= 0.0) + if (gstate->stroke_style.line_width <= 0.0 && !gstate->stroke_style.is_hairline) return CAIRO_STATUS_SUCCESS; if (_cairo_clip_is_all_clipped (gstate->clip)) diff --git a/src/cairo-script-surface.c b/src/cairo-script-surface.c index 800db0780..9d6b954c1 100644 --- a/src/cairo-script-surface.c +++ b/src/cairo-script-surface.c @@ -709,6 +709,24 @@ _emit_line_width (cairo_script_surface_t *surface, } static cairo_status_t +_emit_hairline (cairo_script_surface_t *surface, cairo_bool_t set_hairline) +{ + assert (target_is_active (surface)); + + if (surface->cr.current_style.is_hairline == set_hairline) + { + return CAIRO_STATUS_SUCCESS; + } + + surface->cr.current_style.is_hairline = set_hairline; + + _cairo_output_stream_printf (to_context (surface)->stream, + "%d set-hairline\n", + set_hairline); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t _emit_line_cap (cairo_script_surface_t *surface, cairo_line_cap_t line_cap) { @@ -858,6 +876,10 @@ _emit_stroke_style (cairo_script_surface_t *surface, if (unlikely (status)) return status; + status = _emit_hairline (surface, style->is_hairline); + if (unlikely (status)) + return status; + status = _emit_dash (surface, style->dash, style->num_dashes, style->dash_offset, force); diff --git a/src/cairo-stroke-style.c b/src/cairo-stroke-style.c index 9c373c333..856cb341b 100644 --- a/src/cairo-stroke-style.c +++ b/src/cairo-stroke-style.c @@ -49,6 +49,8 @@ _cairo_stroke_style_init (cairo_stroke_style_t *style) style->dash = NULL; style->num_dashes = 0; style->dash_offset = 0.0; + + style->is_hairline = FALSE; } cairo_status_t @@ -80,6 +82,8 @@ _cairo_stroke_style_init_copy (cairo_stroke_style_t *style, style->dash_offset = other->dash_offset; + style->is_hairline = other->is_hairline; + return CAIRO_STATUS_SUCCESS; } diff --git a/src/cairo-svg-surface.c b/src/cairo-svg-surface.c index 412b32aff..fb6081c48 100644 --- a/src/cairo-svg-surface.c +++ b/src/cairo-svg-surface.c @@ -2992,13 +2992,23 @@ _cairo_svg_surface_emit_stroke_style (cairo_svg_stream_t *output, ASSERT_NOT_REACHED; } - _cairo_svg_stream_printf (output, - " stroke-width=\"%f\"" - " stroke-linecap=\"%s\"" - " stroke-linejoin=\"%s\"", - stroke_style->line_width, - line_cap, - line_join); + if (stroke_style->is_hairline) { + _cairo_svg_stream_printf (output, + " stroke-width=\"1px\"" + " stroke-linecap=\"%s\"" + " stroke-linejoin=\"%s\"" + " style=\"vector-effect: non-scaling-stroke\"", + line_cap, + line_join); + } else { + _cairo_svg_stream_printf (output, + " stroke-width=\"%f\"" + " stroke-linecap=\"%s\"" + " stroke-linejoin=\"%s\"", + stroke_style->line_width, + line_cap, + line_join); + } status = _cairo_svg_surface_emit_pattern (surface, source, output, TRUE, parent_matrix); if (unlikely (status)) { diff --git a/src/cairo-types-private.h b/src/cairo-types-private.h index 0f46214ed..2ec2ce67b 100644 --- a/src/cairo-types-private.h +++ b/src/cairo-types-private.h @@ -376,6 +376,8 @@ typedef struct _cairo_stroke_style { double *dash; unsigned int num_dashes; double dash_offset; + cairo_bool_t is_hairline; + double pre_hairline_line_width; } cairo_stroke_style_t; typedef struct _cairo_format_masks { diff --git a/src/cairo.c b/src/cairo.c index b2bda657d..d141b56d2 100644 --- a/src/cairo.c +++ b/src/cairo.c @@ -1185,6 +1185,47 @@ cairo_set_line_width (cairo_t *cr, double width) slim_hidden_def (cairo_set_line_width); /** + * cairo_set_hairline: + * @cr: a #cairo_t + * @set_hairline: whether or not to set hairline mode + * + * Sets lines within the cairo context to be hairlines. + * Hairlines are logically zero-width lines that are drawn at the + * thinnest renderable width possible in the current context. + * + * On surfaces with native hairline support, the native hairline + * functionality will be used. Surfaces that support hairlines include: + * - pdf/ps: Encoded as 0-width line. + * - win32_printing: Rendered with PS_COSMETIC pen. + * - svg: Encoded as 1px non-scaling-stroke. + * - script: Encoded with set-hairline function. + * + * Cairo will always render hairlines at 1 device unit wide, even if + * an anisotropic scaling was applied to the stroke width. In the wild, + * handling of this situation is not well-defined. Some PDF, PS, and SVG + * renderers match Cairo's output, but some very popular implementations + * (Acrobat, Chrome, rsvg) will scale the hairline unevenly. + * As such, best practice is to reset any anisotropic scaling before calling + * cairo_stroke(). See https://cairographics.org/cookbook/ellipses/ + * for an example. + * + * Since: 1.18 + **/ +void +cairo_set_hairline (cairo_t *cr, cairo_bool_t set_hairline) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + status = cr->backend->set_hairline (cr, set_hairline); + if (unlikely (status)) + _cairo_set_error (cr, status); +} +slim_hidden_def (cairo_set_hairline); + +/** * cairo_set_line_cap: * @cr: a cairo context * @line_cap: a line cap style @@ -4058,6 +4099,26 @@ cairo_get_line_width (cairo_t *cr) slim_hidden_def (cairo_get_line_width); /** + * cairo_get_hairline: + * @cr: a cairo context + * + * Returns whether or not hairline mode is set, as set by cairo_set_hairline(). + * + * Return value: whether hairline mode is set. + * + * Since: 1.18 + **/ +cairo_bool_t +cairo_get_hairline (cairo_t *cr) +{ + if (unlikely (cr->status)) + return FALSE; + + return cr->backend->get_hairline (cr); +} +slim_hidden_def (cairo_get_hairline); + +/** * cairo_get_line_cap: * @cr: a cairo context * diff --git a/src/cairo.h b/src/cairo.h index 96427b425..5ab47c112 100644 --- a/src/cairo.h +++ b/src/cairo.h @@ -765,6 +765,9 @@ cairo_set_fill_rule (cairo_t *cr, cairo_fill_rule_t fill_rule); cairo_public void cairo_set_line_width (cairo_t *cr, double width); +cairo_public void +cairo_set_hairline (cairo_t *cr, cairo_bool_t set_hairline); + /** * cairo_line_cap_t: * @CAIRO_LINE_CAP_BUTT: start(stop) the line exactly at the start(end) point (Since 1.0) @@ -1957,6 +1960,9 @@ cairo_get_fill_rule (cairo_t *cr); cairo_public double cairo_get_line_width (cairo_t *cr); +cairo_public cairo_bool_t +cairo_get_hairline (cairo_t *cr); + cairo_public cairo_line_cap_t cairo_get_line_cap (cairo_t *cr); diff --git a/src/cairoint.h b/src/cairoint.h index 64ed2aa4d..6affe5601 100644 --- a/src/cairoint.h +++ b/src/cairoint.h @@ -1959,6 +1959,7 @@ slim_hidden_proto (cairo_font_options_status); slim_hidden_proto (cairo_format_stride_for_width); slim_hidden_proto (cairo_get_current_point); slim_hidden_proto (cairo_get_line_width); +slim_hidden_proto (cairo_get_hairline); slim_hidden_proto (cairo_get_matrix); slim_hidden_proto (cairo_get_scaled_font); slim_hidden_proto (cairo_get_target); @@ -2031,6 +2032,7 @@ slim_hidden_proto (cairo_set_font_size); slim_hidden_proto (cairo_set_line_cap); slim_hidden_proto (cairo_set_line_join); slim_hidden_proto (cairo_set_line_width); +slim_hidden_proto (cairo_set_hairline); slim_hidden_proto (cairo_set_matrix); slim_hidden_proto (cairo_set_operator); slim_hidden_proto (cairo_set_source); diff --git a/src/win32/cairo-win32-printing-surface.c b/src/win32/cairo-win32-printing-surface.c index 094068c15..6e3636104 100644 --- a/src/win32/cairo-win32-printing-surface.c +++ b/src/win32/cairo-win32-printing-surface.c @@ -1520,7 +1520,7 @@ _cairo_win32_printing_surface_stroke (void *abstract_surface, cairo_matrix_multiply (&mat, stroke_ctm, &surface->ctm); _cairo_matrix_factor_out_scale (&mat, &scale); - pen_style = PS_GEOMETRIC; + pen_style = style->is_hairline ? PS_COSMETIC : PS_GEOMETRIC; dash_array = NULL; if (style->num_dashes) { pen_style |= PS_USERSTYLE; @@ -1546,10 +1546,12 @@ _cairo_win32_printing_surface_stroke (void *abstract_surface, brush.lbStyle = BS_SOLID; brush.lbColor = color; brush.lbHatch = 0; - pen_style |= _cairo_win32_line_cap (style->line_cap); - pen_style |= _cairo_win32_line_join (style->line_join); + if (!style->is_hairline) { + pen_style |= _cairo_win32_line_cap (style->line_cap); + pen_style |= _cairo_win32_line_join (style->line_join); + } pen = ExtCreatePen(pen_style, - scale * style->line_width, + style->is_hairline ? 1 : scale * style->line_width, &brush, style->num_dashes, dash_array); diff --git a/test/Makefile.sources b/test/Makefile.sources index b887b054d..12b9790b1 100644 --- a/test/Makefile.sources +++ b/test/Makefile.sources @@ -162,6 +162,7 @@ test_sources = \ group-paint.c \ group-state.c \ group-unaligned.c \ + hairline.c \ half-coverage.c \ halo.c \ hatchings.c \ diff --git a/test/hairline.c b/test/hairline.c new file mode 100644 index 000000000..a14b944e3 --- /dev/null +++ b/test/hairline.c @@ -0,0 +1,175 @@ +#include "cairo-test.h"
+
+/**
+ * draw:
+ * @cr: a #cairo_t
+ * @width: Width of the test image
+ * @height: Height of the test image
+ * @scale_width: Percentage to scale the width
+ * @scale_height: Percentage to scale the height
+ * @fit_to_scale: Whether or not to adjust the image to fit in the scale parameters
+ * regardless of the scale parameters
+ * @correct_scale: Tests if the hairlines render correctly regardless of
+ * whether or not the scale is set "correctly", as per
+ * https://cairographics.org/cookbook/ellipses/
+ */
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height, double scale_width, double scale_height, cairo_bool_t fit_to_scale, cairo_bool_t correct_scale)
+{
+ cairo_matrix_t save_matrix;
+ double fit_width = fit_to_scale ? scale_width : 1.0;
+ double fit_height = fit_to_scale ? scale_height : 1.0;
+ double fit_max = MAX(fit_width, fit_height);
+ double dash[] = {3.0};
+
+ if (cairo_get_hairline (cr) == TRUE) {
+ return CAIRO_TEST_ERROR;
+ }
+
+ /* Clear background */
+ cairo_set_source_rgb (cr, 1, 1, 1);
+ cairo_paint (cr);
+
+ cairo_set_source_rgb (cr, 0, 0, 0);
+ cairo_set_line_width (cr, 100.0); /* If everything is working right, this value should never get used */
+
+ /* Hairline sample */
+ if (correct_scale) {
+ cairo_get_matrix (cr, &save_matrix);
+ }
+ cairo_scale (cr, scale_width, scale_height);
+
+ cairo_set_hairline (cr, TRUE);
+ if (cairo_get_hairline (cr) == FALSE) {
+ return CAIRO_TEST_ERROR;
+ }
+
+ cairo_move_to (cr, 0, 0);
+ cairo_line_to (cr, width/fit_width/2, height/fit_height/2);
+ cairo_move_to (cr, width/fit_width/2, 0);
+ cairo_line_to (cr, width/fit_width/2, height/fit_height/2);
+ cairo_move_to (cr, 0, height/fit_height/2);
+ cairo_line_to (cr, width/fit_width/2, height/fit_height/2);
+ cairo_move_to (cr, width/fit_width/4, 0);
+ cairo_line_to (cr, width/fit_width/2, height/fit_height/2);
+ cairo_arc (cr, width/fit_width/2, height/fit_height/2, width/fit_max/4, M_PI*0.5, M_PI*1.0);
+
+ if (correct_scale) {
+ cairo_set_matrix (cr, &save_matrix);
+ }
+ cairo_stroke (cr);
+
+ /* Dashed sample */
+ if (correct_scale) {
+ cairo_get_matrix (cr, &save_matrix);
+ cairo_scale (cr, scale_width, scale_height);
+ }
+ cairo_set_dash (cr, dash, 1, 0);
+ cairo_arc (cr, width/fit_width/2, height/fit_height/2, width/fit_max/4, M_PI*1.0, M_PI*1.5);
+ if (correct_scale) {
+ cairo_set_matrix (cr, &save_matrix);
+ }
+ cairo_stroke (cr);
+
+ /* Control sample */
+ if (correct_scale) {
+ cairo_get_matrix (cr, &save_matrix);
+ cairo_scale (cr, scale_width, scale_height);
+ }
+
+ cairo_set_line_width (cr, 3.0);
+ cairo_set_hairline (cr, FALSE);
+ if (cairo_get_hairline (cr) == TRUE) {
+ return CAIRO_TEST_ERROR;
+ }
+
+ cairo_set_dash (cr, 0, 0, 0);
+
+ cairo_move_to (cr, width/fit_width, height/fit_height);
+ cairo_line_to (cr, width/fit_width/2, height/fit_height/2);
+ cairo_move_to (cr, width/fit_width/2, height/fit_height);
+ cairo_line_to (cr, width/fit_width/2, height/fit_height/2);
+ cairo_move_to (cr, width/fit_width, height/fit_height/2);
+ cairo_line_to (cr, width/fit_width/2, height/fit_height/2);
+ cairo_move_to (cr, width/fit_width*0.75, height/fit_height);
+ cairo_line_to (cr, width/fit_width/2, height/fit_height/2);
+ cairo_arc (cr, width/fit_width/2, height/fit_height/2, width/fit_max/4, M_PI*1.5, M_PI*2.0);
+
+ if (correct_scale) {
+ cairo_set_matrix (cr, &save_matrix);
+ }
+ cairo_stroke (cr);
+
+ /* Dashed sample */
+ if (correct_scale) {
+ cairo_get_matrix (cr, &save_matrix);
+ cairo_scale (cr, scale_width, scale_height);
+ }
+ cairo_set_dash (cr, dash, 1, 0);
+ cairo_arc (cr, width/fit_width/2, height/fit_height/2, width/fit_max/4, 0, M_PI*0.5);
+ if (correct_scale) {
+ cairo_set_matrix (cr, &save_matrix);
+ }
+ cairo_stroke (cr);
+
+ return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+draw_typical (cairo_t *cr, int width, int height)
+{
+ return draw (cr, width, height, 1.0, 1.0, TRUE, TRUE);
+}
+
+static cairo_test_status_t
+draw_scaled (cairo_t *cr, int width, int height)
+{
+ return draw (cr, width, height, 0.5, 0.5, FALSE, TRUE);
+}
+
+static cairo_test_status_t
+draw_anisotropic (cairo_t *cr, int width, int height)
+{
+ return draw (cr, width, height, 2.0, 5.0, TRUE, TRUE);
+}
+
+static cairo_test_status_t
+draw_anisotropic_incorrect (cairo_t *cr, int width, int height)
+{
+ return draw (cr, width, height, 2.0, 5.0, TRUE, FALSE);
+}
+
+CAIRO_TEST (hairline,
+ "Tests hairlines are drawn at a single pixel width",
+ "path, stroke, hairline", /* keywords */
+ NULL, /* requirements */
+ 49, 49,
+ NULL, draw_typical)
+
+CAIRO_TEST (hairline_big,
+ "Tests hairlines are drawn at a single pixel width",
+ "path, stroke, hairline", /* keywords */
+ NULL, /* requirements */
+ 99, 99,
+ NULL, draw_typical)
+
+CAIRO_TEST (hairline_scaled,
+ "Tests hairlines are drawn at a single pixel width",
+ "path, stroke, hairline", /* keywords */
+ NULL, /* requirements */
+ 99, 99,
+ NULL, draw_scaled)
+
+CAIRO_TEST (hairline_anisotropic,
+ "Tests hairlines with a really lopsided scale parameter",
+ "path, stroke, hairline", /* keywords */
+ NULL, /* requirements */
+ 99, 99,
+ NULL, draw_anisotropic)
+
+CAIRO_TEST (hairline_anisotropic_incorrect,
+ "Tests hairlines with a really lopsided scale parameter",
+ "path, stroke, hairline", /* keywords */
+ NULL, /* requirements */
+ 99, 99,
+ NULL, draw_anisotropic_incorrect)
\ No newline at end of file diff --git a/test/meson.build b/test/meson.build index 9d84d048d..ed8ec2cfd 100644 --- a/test/meson.build +++ b/test/meson.build @@ -162,6 +162,7 @@ test_sources = [ 'group-paint.c', 'group-state.c', 'group-unaligned.c', + 'hairline.c', 'half-coverage.c', 'halo.c', 'hatchings.c', diff --git a/test/reference/hairline-anisotropic-incorrect.image16.ref.png b/test/reference/hairline-anisotropic-incorrect.image16.ref.png Binary files differnew file mode 100644 index 000000000..c69a90d37 --- /dev/null +++ b/test/reference/hairline-anisotropic-incorrect.image16.ref.png diff --git a/test/reference/hairline-anisotropic-incorrect.pdf.ref.png b/test/reference/hairline-anisotropic-incorrect.pdf.ref.png Binary files differnew file mode 100644 index 000000000..37acfaa55 --- /dev/null +++ b/test/reference/hairline-anisotropic-incorrect.pdf.ref.png diff --git a/test/reference/hairline-anisotropic-incorrect.quartz.ref.png b/test/reference/hairline-anisotropic-incorrect.quartz.ref.png Binary files differnew file mode 100644 index 000000000..ec6ded3f3 --- /dev/null +++ b/test/reference/hairline-anisotropic-incorrect.quartz.ref.png diff --git a/test/reference/hairline-anisotropic-incorrect.ref.png b/test/reference/hairline-anisotropic-incorrect.ref.png Binary files differnew file mode 100644 index 000000000..54929db44 --- /dev/null +++ b/test/reference/hairline-anisotropic-incorrect.ref.png diff --git a/test/reference/hairline-anisotropic-incorrect.svg11.argb32.ref.png b/test/reference/hairline-anisotropic-incorrect.svg11.argb32.ref.png Binary files differnew file mode 100644 index 000000000..22b8cd51c --- /dev/null +++ b/test/reference/hairline-anisotropic-incorrect.svg11.argb32.ref.png diff --git a/test/reference/hairline-anisotropic-incorrect.svg11.rgb24.ref.png b/test/reference/hairline-anisotropic-incorrect.svg11.rgb24.ref.png Binary files differnew file mode 100644 index 000000000..d0f0f2e25 --- /dev/null +++ b/test/reference/hairline-anisotropic-incorrect.svg11.rgb24.ref.png diff --git a/test/reference/hairline-anisotropic-incorrect.xcb-window&.ref.png b/test/reference/hairline-anisotropic-incorrect.xcb-window&.ref.png Binary files differnew file mode 100644 index 000000000..6c498ab46 --- /dev/null +++ b/test/reference/hairline-anisotropic-incorrect.xcb-window&.ref.png diff --git a/test/reference/hairline-anisotropic-incorrect.xcb-window.ref.png b/test/reference/hairline-anisotropic-incorrect.xcb-window.ref.png Binary files differnew file mode 100644 index 000000000..6c498ab46 --- /dev/null +++ b/test/reference/hairline-anisotropic-incorrect.xcb-window.ref.png diff --git a/test/reference/hairline-anisotropic-incorrect.xcb.ref.png b/test/reference/hairline-anisotropic-incorrect.xcb.ref.png Binary files differnew file mode 100644 index 000000000..6c498ab46 --- /dev/null +++ b/test/reference/hairline-anisotropic-incorrect.xcb.ref.png diff --git a/test/reference/hairline-anisotropic-incorrect.xlib-window.ref.png b/test/reference/hairline-anisotropic-incorrect.xlib-window.ref.png Binary files differnew file mode 100644 index 000000000..042715325 --- /dev/null +++ b/test/reference/hairline-anisotropic-incorrect.xlib-window.ref.png diff --git a/test/reference/hairline-anisotropic-incorrect.xlib.ref.png b/test/reference/hairline-anisotropic-incorrect.xlib.ref.png Binary files differnew file mode 100644 index 000000000..042715325 --- /dev/null +++ b/test/reference/hairline-anisotropic-incorrect.xlib.ref.png diff --git a/test/reference/hairline-anisotropic.image16.ref.png b/test/reference/hairline-anisotropic.image16.ref.png Binary files differnew file mode 100644 index 000000000..a5a7217ca --- /dev/null +++ b/test/reference/hairline-anisotropic.image16.ref.png diff --git a/test/reference/hairline-anisotropic.pdf.ref.png b/test/reference/hairline-anisotropic.pdf.ref.png Binary files differnew file mode 100644 index 000000000..acb2ffbcf --- /dev/null +++ b/test/reference/hairline-anisotropic.pdf.ref.png diff --git a/test/reference/hairline-anisotropic.quartz.ref.png b/test/reference/hairline-anisotropic.quartz.ref.png Binary files differnew file mode 100644 index 000000000..ebf9fd64d --- /dev/null +++ b/test/reference/hairline-anisotropic.quartz.ref.png diff --git a/test/reference/hairline-anisotropic.ref.png b/test/reference/hairline-anisotropic.ref.png Binary files differnew file mode 100644 index 000000000..bb3ba7793 --- /dev/null +++ b/test/reference/hairline-anisotropic.ref.png diff --git a/test/reference/hairline-anisotropic.xcb-window&.ref.png b/test/reference/hairline-anisotropic.xcb-window&.ref.png Binary files differnew file mode 100644 index 000000000..41a56f808 --- /dev/null +++ b/test/reference/hairline-anisotropic.xcb-window&.ref.png diff --git a/test/reference/hairline-anisotropic.xcb-window.ref.png b/test/reference/hairline-anisotropic.xcb-window.ref.png Binary files differnew file mode 100644 index 000000000..41a56f808 --- /dev/null +++ b/test/reference/hairline-anisotropic.xcb-window.ref.png diff --git a/test/reference/hairline-anisotropic.xcb.ref.png b/test/reference/hairline-anisotropic.xcb.ref.png Binary files differnew file mode 100644 index 000000000..41a56f808 --- /dev/null +++ b/test/reference/hairline-anisotropic.xcb.ref.png diff --git a/test/reference/hairline-anisotropic.xlib-window.ref.png b/test/reference/hairline-anisotropic.xlib-window.ref.png Binary files differnew file mode 100644 index 000000000..b84c2f449 --- /dev/null +++ b/test/reference/hairline-anisotropic.xlib-window.ref.png diff --git a/test/reference/hairline-anisotropic.xlib.ref.png b/test/reference/hairline-anisotropic.xlib.ref.png Binary files differnew file mode 100644 index 000000000..b84c2f449 --- /dev/null +++ b/test/reference/hairline-anisotropic.xlib.ref.png diff --git a/test/reference/hairline-big.image16.ref.png b/test/reference/hairline-big.image16.ref.png Binary files differnew file mode 100644 index 000000000..9d63e8e1b --- /dev/null +++ b/test/reference/hairline-big.image16.ref.png diff --git a/test/reference/hairline-big.pdf.ref.png b/test/reference/hairline-big.pdf.ref.png Binary files differnew file mode 100644 index 000000000..6ed7e7ccf --- /dev/null +++ b/test/reference/hairline-big.pdf.ref.png diff --git a/test/reference/hairline-big.quartz.ref.png b/test/reference/hairline-big.quartz.ref.png Binary files differnew file mode 100644 index 000000000..1f153dd70 --- /dev/null +++ b/test/reference/hairline-big.quartz.ref.png diff --git a/test/reference/hairline-big.ref.png b/test/reference/hairline-big.ref.png Binary files differnew file mode 100644 index 000000000..98ea2428f --- /dev/null +++ b/test/reference/hairline-big.ref.png diff --git a/test/reference/hairline-big.xcb-window&.ref.png b/test/reference/hairline-big.xcb-window&.ref.png Binary files differnew file mode 100644 index 000000000..a662668db --- /dev/null +++ b/test/reference/hairline-big.xcb-window&.ref.png diff --git a/test/reference/hairline-big.xcb-window.ref.png b/test/reference/hairline-big.xcb-window.ref.png Binary files differnew file mode 100644 index 000000000..a662668db --- /dev/null +++ b/test/reference/hairline-big.xcb-window.ref.png diff --git a/test/reference/hairline-big.xcb.ref.png b/test/reference/hairline-big.xcb.ref.png Binary files differnew file mode 100644 index 000000000..a662668db --- /dev/null +++ b/test/reference/hairline-big.xcb.ref.png diff --git a/test/reference/hairline-big.xlib-window.ref.png b/test/reference/hairline-big.xlib-window.ref.png Binary files differnew file mode 100644 index 000000000..ce6aaaeed --- /dev/null +++ b/test/reference/hairline-big.xlib-window.ref.png diff --git a/test/reference/hairline-big.xlib.ref.png b/test/reference/hairline-big.xlib.ref.png Binary files differnew file mode 100644 index 000000000..ce6aaaeed --- /dev/null +++ b/test/reference/hairline-big.xlib.ref.png diff --git a/test/reference/hairline-scaled.image16.ref.png b/test/reference/hairline-scaled.image16.ref.png Binary files differnew file mode 100644 index 000000000..5b82d0dc8 --- /dev/null +++ b/test/reference/hairline-scaled.image16.ref.png diff --git a/test/reference/hairline-scaled.pdf.ref.png b/test/reference/hairline-scaled.pdf.ref.png Binary files differnew file mode 100644 index 000000000..ef0973242 --- /dev/null +++ b/test/reference/hairline-scaled.pdf.ref.png diff --git a/test/reference/hairline-scaled.quartz.ref.png b/test/reference/hairline-scaled.quartz.ref.png Binary files differnew file mode 100644 index 000000000..c0653a12c --- /dev/null +++ b/test/reference/hairline-scaled.quartz.ref.png diff --git a/test/reference/hairline-scaled.ref.png b/test/reference/hairline-scaled.ref.png Binary files differnew file mode 100644 index 000000000..f059b4288 --- /dev/null +++ b/test/reference/hairline-scaled.ref.png diff --git a/test/reference/hairline-scaled.svg11.ref.png b/test/reference/hairline-scaled.svg11.ref.png Binary files differnew file mode 100644 index 000000000..f45367bb0 --- /dev/null +++ b/test/reference/hairline-scaled.svg11.ref.png diff --git a/test/reference/hairline-scaled.xcb-window&.ref.png b/test/reference/hairline-scaled.xcb-window&.ref.png Binary files differnew file mode 100644 index 000000000..22ba5e291 --- /dev/null +++ b/test/reference/hairline-scaled.xcb-window&.ref.png diff --git a/test/reference/hairline-scaled.xcb-window.ref.png b/test/reference/hairline-scaled.xcb-window.ref.png Binary files differnew file mode 100644 index 000000000..22ba5e291 --- /dev/null +++ b/test/reference/hairline-scaled.xcb-window.ref.png diff --git a/test/reference/hairline-scaled.xcb.ref.png b/test/reference/hairline-scaled.xcb.ref.png Binary files differnew file mode 100644 index 000000000..22ba5e291 --- /dev/null +++ b/test/reference/hairline-scaled.xcb.ref.png diff --git a/test/reference/hairline-scaled.xlib-window.ref.png b/test/reference/hairline-scaled.xlib-window.ref.png Binary files differnew file mode 100644 index 000000000..a59338584 --- /dev/null +++ b/test/reference/hairline-scaled.xlib-window.ref.png diff --git a/test/reference/hairline-scaled.xlib.ref.png b/test/reference/hairline-scaled.xlib.ref.png Binary files differnew file mode 100644 index 000000000..a59338584 --- /dev/null +++ b/test/reference/hairline-scaled.xlib.ref.png diff --git a/test/reference/hairline.image16.ref.png b/test/reference/hairline.image16.ref.png Binary files differnew file mode 100644 index 000000000..fa3318cb6 --- /dev/null +++ b/test/reference/hairline.image16.ref.png diff --git a/test/reference/hairline.pdf.ref.png b/test/reference/hairline.pdf.ref.png Binary files differnew file mode 100644 index 000000000..0a0ecb33e --- /dev/null +++ b/test/reference/hairline.pdf.ref.png diff --git a/test/reference/hairline.quartz.ref.png b/test/reference/hairline.quartz.ref.png Binary files differnew file mode 100644 index 000000000..e51ea63c9 --- /dev/null +++ b/test/reference/hairline.quartz.ref.png diff --git a/test/reference/hairline.ref.png b/test/reference/hairline.ref.png Binary files differnew file mode 100644 index 000000000..e33683115 --- /dev/null +++ b/test/reference/hairline.ref.png diff --git a/test/reference/hairline.svg11.ref.png b/test/reference/hairline.svg11.ref.png Binary files differnew file mode 100644 index 000000000..834f3a43a --- /dev/null +++ b/test/reference/hairline.svg11.ref.png diff --git a/test/reference/hairline.xcb-window&.ref.png b/test/reference/hairline.xcb-window&.ref.png Binary files differnew file mode 100644 index 000000000..1175b14a9 --- /dev/null +++ b/test/reference/hairline.xcb-window&.ref.png diff --git a/test/reference/hairline.xcb-window.ref.png b/test/reference/hairline.xcb-window.ref.png Binary files differnew file mode 100644 index 000000000..1175b14a9 --- /dev/null +++ b/test/reference/hairline.xcb-window.ref.png diff --git a/test/reference/hairline.xcb.ref.png b/test/reference/hairline.xcb.ref.png Binary files differnew file mode 100644 index 000000000..1175b14a9 --- /dev/null +++ b/test/reference/hairline.xcb.ref.png diff --git a/test/reference/hairline.xlib-window.ref.png b/test/reference/hairline.xlib-window.ref.png Binary files differnew file mode 100644 index 000000000..ed55291f3 --- /dev/null +++ b/test/reference/hairline.xlib-window.ref.png diff --git a/test/reference/hairline.xlib.ref.png b/test/reference/hairline.xlib.ref.png Binary files differnew file mode 100644 index 000000000..ed55291f3 --- /dev/null +++ b/test/reference/hairline.xlib.ref.png diff --git a/util/cairo-script/cairo-script-operators.c b/util/cairo-script/cairo-script-operators.c index df8886ef6..d0452d338 100644 --- a/util/cairo-script/cairo-script-operators.c +++ b/util/cairo-script/cairo-script-operators.c @@ -5162,6 +5162,27 @@ _set_line_width (csi_t *ctx) } static csi_status_t +_set_hairline (csi_t *ctx) +{ + csi_status_t status; + cairo_t *cr; + cairo_bool_t set_hairline = FALSE; /* silence the compiler */ + + check (2); + + status = _csi_ostack_get_boolean (ctx, 0, &set_hairline); + if (_csi_unlikely (status)) + return status; + status = _csi_ostack_get_context (ctx, 1, &cr); + if (_csi_unlikely (status)) + return status; + + cairo_set_hairline (cr, set_hairline); + pop (1); + return CSI_STATUS_SUCCESS; +} + +static csi_status_t _set_matrix (csi_t *ctx) { csi_object_t *obj; @@ -6625,6 +6646,7 @@ _defs[] = { { "set-line-cap", _set_line_cap }, { "set-line-join", _set_line_join }, { "set-line-width", _set_line_width }, + { "set-hairline", _set_hairline }, { "set-matrix", _set_matrix }, { "set-miter-limit", _set_miter_limit }, { "set-mime-data", _set_mime_data }, |