[cairo-commit] 2 commits - src/cairo-backend-private.h src/cairo.c src/cairo-compositor.c src/cairo-default-context.c src/cairo-gstate.c src/cairo-gstate-private.h src/cairo.h src/cairoint.h src/cairo-script-surface.c src/cairo-stroke-style.c src/cairo-svg-surface.c src/cairo-types-private.h src/win32 test/hairline.c test/Makefile.sources test/meson.build test/reference util/cairo-script
GitLab Mirror
gitlab-mirror at kemper.freedesktop.org
Sun Aug 15 06:58:58 UTC 2021
src/cairo-backend-private.h | 2
src/cairo-compositor.c | 66 +++
src/cairo-default-context.c | 18 +
src/cairo-gstate-private.h | 6
src/cairo-gstate.c | 30 +
src/cairo-script-surface.c | 22 +
src/cairo-stroke-style.c | 4
src/cairo-svg-surface.c | 24 -
src/cairo-types-private.h | 2
src/cairo.c | 61 +++
src/cairo.h | 6
src/cairoint.h | 2
src/win32/cairo-win32-printing-surface.c | 10
test/Makefile.sources | 1
test/hairline.c | 175 ++++++++++
test/meson.build | 1
test/reference/hairline-anisotropic-incorrect.image16.ref.png |binary
test/reference/hairline-anisotropic-incorrect.pdf.ref.png |binary
test/reference/hairline-anisotropic-incorrect.quartz.ref.png |binary
test/reference/hairline-anisotropic-incorrect.ref.png |binary
test/reference/hairline-anisotropic-incorrect.svg11.argb32.ref.png |binary
test/reference/hairline-anisotropic-incorrect.svg11.rgb24.ref.png |binary
test/reference/hairline-anisotropic-incorrect.xcb-window&.ref.png |binary
test/reference/hairline-anisotropic-incorrect.xcb-window.ref.png |binary
test/reference/hairline-anisotropic-incorrect.xcb.ref.png |binary
test/reference/hairline-anisotropic-incorrect.xlib-window.ref.png |binary
test/reference/hairline-anisotropic-incorrect.xlib.ref.png |binary
test/reference/hairline-anisotropic.image16.ref.png |binary
test/reference/hairline-anisotropic.pdf.ref.png |binary
test/reference/hairline-anisotropic.quartz.ref.png |binary
test/reference/hairline-anisotropic.ref.png |binary
test/reference/hairline-anisotropic.xcb-window&.ref.png |binary
test/reference/hairline-anisotropic.xcb-window.ref.png |binary
test/reference/hairline-anisotropic.xcb.ref.png |binary
test/reference/hairline-anisotropic.xlib-window.ref.png |binary
test/reference/hairline-anisotropic.xlib.ref.png |binary
test/reference/hairline-big.image16.ref.png |binary
test/reference/hairline-big.pdf.ref.png |binary
test/reference/hairline-big.quartz.ref.png |binary
test/reference/hairline-big.ref.png |binary
test/reference/hairline-big.xcb-window&.ref.png |binary
test/reference/hairline-big.xcb-window.ref.png |binary
test/reference/hairline-big.xcb.ref.png |binary
test/reference/hairline-big.xlib-window.ref.png |binary
test/reference/hairline-big.xlib.ref.png |binary
test/reference/hairline-scaled.image16.ref.png |binary
test/reference/hairline-scaled.pdf.ref.png |binary
test/reference/hairline-scaled.quartz.ref.png |binary
test/reference/hairline-scaled.ref.png |binary
test/reference/hairline-scaled.svg11.ref.png |binary
test/reference/hairline-scaled.xcb-window&.ref.png |binary
test/reference/hairline-scaled.xcb-window.ref.png |binary
test/reference/hairline-scaled.xcb.ref.png |binary
test/reference/hairline-scaled.xlib-window.ref.png |binary
test/reference/hairline-scaled.xlib.ref.png |binary
test/reference/hairline.image16.ref.png |binary
test/reference/hairline.pdf.ref.png |binary
test/reference/hairline.quartz.ref.png |binary
test/reference/hairline.ref.png |binary
test/reference/hairline.svg11.ref.png |binary
test/reference/hairline.xcb-window&.ref.png |binary
test/reference/hairline.xcb-window.ref.png |binary
test/reference/hairline.xcb.ref.png |binary
test/reference/hairline.xlib-window.ref.png |binary
test/reference/hairline.xlib.ref.png |binary
util/cairo-script/cairo-script-operators.c | 22 +
66 files changed, 427 insertions(+), 25 deletions(-)
New commits:
commit c773060195c5aee7d84208188bed5740a28747a8
Merge: 9f973bc43 ecec0419f
Author: Adrian Johnson <ajohnson at redneon.com>
Date: Sun Aug 15 06:58:54 2021 +0000
Merge branch 'HairlineStroke' into 'master'
Added hairline support to cairo
See merge request cairo/cairo!21
commit ecec0419f8e178d71e449b52acfdfe9ac03aed37
Author: Rick Yorgason <rick at firefang.com>
Date: Sun Aug 15 06:58:54 2021 +0000
Added hairline support to cairo
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;
@@ -175,6 +175,48 @@ _cairo_compositor_stroke (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)
+{
+ 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,
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
@@ -403,6 +403,14 @@ _cairo_default_context_set_line_width (void *abstract_cr,
return _cairo_gstate_set_line_width (cr->gstate, line_width);
}
+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
@@ -139,6 +139,12 @@ _cairo_gstate_set_line_width (cairo_gstate_t *gstate, double width);
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);
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;
}
@@ -481,6 +484,29 @@ _cairo_gstate_get_line_width (cairo_gstate_t *gstate)
return gstate->stroke_style.line_width;
}
+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)
{
@@ -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
@@ -708,6 +708,24 @@ _emit_line_width (cairo_script_surface_t *surface,
return CAIRO_STATUS_SUCCESS;
}
+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
@@ -1184,6 +1184,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
@@ -4057,6 +4098,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
new file mode 100644
index 000000000..c69a90d37
Binary files /dev/null and b/test/reference/hairline-anisotropic-incorrect.image16.ref.png differ
diff --git a/test/reference/hairline-anisotropic-incorrect.pdf.ref.png b/test/reference/hairline-anisotropic-incorrect.pdf.ref.png
new file mode 100644
index 000000000..37acfaa55
Binary files /dev/null and b/test/reference/hairline-anisotropic-incorrect.pdf.ref.png differ
diff --git a/test/reference/hairline-anisotropic-incorrect.quartz.ref.png b/test/reference/hairline-anisotropic-incorrect.quartz.ref.png
new file mode 100644
index 000000000..ec6ded3f3
Binary files /dev/null and b/test/reference/hairline-anisotropic-incorrect.quartz.ref.png differ
diff --git a/test/reference/hairline-anisotropic-incorrect.ref.png b/test/reference/hairline-anisotropic-incorrect.ref.png
new file mode 100644
index 000000000..54929db44
Binary files /dev/null and b/test/reference/hairline-anisotropic-incorrect.ref.png differ
diff --git a/test/reference/hairline-anisotropic-incorrect.svg11.argb32.ref.png b/test/reference/hairline-anisotropic-incorrect.svg11.argb32.ref.png
new file mode 100644
index 000000000..22b8cd51c
Binary files /dev/null and b/test/reference/hairline-anisotropic-incorrect.svg11.argb32.ref.png differ
diff --git a/test/reference/hairline-anisotropic-incorrect.svg11.rgb24.ref.png b/test/reference/hairline-anisotropic-incorrect.svg11.rgb24.ref.png
new file mode 100644
index 000000000..d0f0f2e25
Binary files /dev/null and b/test/reference/hairline-anisotropic-incorrect.svg11.rgb24.ref.png differ
diff --git a/test/reference/hairline-anisotropic-incorrect.xcb-window&.ref.png b/test/reference/hairline-anisotropic-incorrect.xcb-window&.ref.png
new file mode 100644
index 000000000..6c498ab46
Binary files /dev/null and b/test/reference/hairline-anisotropic-incorrect.xcb-window&.ref.png differ
diff --git a/test/reference/hairline-anisotropic-incorrect.xcb-window.ref.png b/test/reference/hairline-anisotropic-incorrect.xcb-window.ref.png
new file mode 100644
index 000000000..6c498ab46
Binary files /dev/null and b/test/reference/hairline-anisotropic-incorrect.xcb-window.ref.png differ
diff --git a/test/reference/hairline-anisotropic-incorrect.xcb.ref.png b/test/reference/hairline-anisotropic-incorrect.xcb.ref.png
new file mode 100644
index 000000000..6c498ab46
Binary files /dev/null and b/test/reference/hairline-anisotropic-incorrect.xcb.ref.png differ
diff --git a/test/reference/hairline-anisotropic-incorrect.xlib-window.ref.png b/test/reference/hairline-anisotropic-incorrect.xlib-window.ref.png
new file mode 100644
index 000000000..042715325
Binary files /dev/null and b/test/reference/hairline-anisotropic-incorrect.xlib-window.ref.png differ
diff --git a/test/reference/hairline-anisotropic-incorrect.xlib.ref.png b/test/reference/hairline-anisotropic-incorrect.xlib.ref.png
new file mode 100644
index 000000000..042715325
Binary files /dev/null and b/test/reference/hairline-anisotropic-incorrect.xlib.ref.png differ
diff --git a/test/reference/hairline-anisotropic.image16.ref.png b/test/reference/hairline-anisotropic.image16.ref.png
new file mode 100644
index 000000000..a5a7217ca
Binary files /dev/null and b/test/reference/hairline-anisotropic.image16.ref.png differ
diff --git a/test/reference/hairline-anisotropic.pdf.ref.png b/test/reference/hairline-anisotropic.pdf.ref.png
new file mode 100644
index 000000000..acb2ffbcf
Binary files /dev/null and b/test/reference/hairline-anisotropic.pdf.ref.png differ
diff --git a/test/reference/hairline-anisotropic.quartz.ref.png b/test/reference/hairline-anisotropic.quartz.ref.png
new file mode 100644
index 000000000..ebf9fd64d
Binary files /dev/null and b/test/reference/hairline-anisotropic.quartz.ref.png differ
diff --git a/test/reference/hairline-anisotropic.ref.png b/test/reference/hairline-anisotropic.ref.png
new file mode 100644
index 000000000..bb3ba7793
Binary files /dev/null and b/test/reference/hairline-anisotropic.ref.png differ
diff --git a/test/reference/hairline-anisotropic.xcb-window&.ref.png b/test/reference/hairline-anisotropic.xcb-window&.ref.png
new file mode 100644
index 000000000..41a56f808
Binary files /dev/null and b/test/reference/hairline-anisotropic.xcb-window&.ref.png differ
diff --git a/test/reference/hairline-anisotropic.xcb-window.ref.png b/test/reference/hairline-anisotropic.xcb-window.ref.png
new file mode 100644
index 000000000..41a56f808
Binary files /dev/null and b/test/reference/hairline-anisotropic.xcb-window.ref.png differ
diff --git a/test/reference/hairline-anisotropic.xcb.ref.png b/test/reference/hairline-anisotropic.xcb.ref.png
new file mode 100644
index 000000000..41a56f808
Binary files /dev/null and b/test/reference/hairline-anisotropic.xcb.ref.png differ
diff --git a/test/reference/hairline-anisotropic.xlib-window.ref.png b/test/reference/hairline-anisotropic.xlib-window.ref.png
new file mode 100644
index 000000000..b84c2f449
Binary files /dev/null and b/test/reference/hairline-anisotropic.xlib-window.ref.png differ
diff --git a/test/reference/hairline-anisotropic.xlib.ref.png b/test/reference/hairline-anisotropic.xlib.ref.png
new file mode 100644
index 000000000..b84c2f449
Binary files /dev/null and b/test/reference/hairline-anisotropic.xlib.ref.png differ
diff --git a/test/reference/hairline-big.image16.ref.png b/test/reference/hairline-big.image16.ref.png
new file mode 100644
index 000000000..9d63e8e1b
Binary files /dev/null and b/test/reference/hairline-big.image16.ref.png differ
diff --git a/test/reference/hairline-big.pdf.ref.png b/test/reference/hairline-big.pdf.ref.png
new file mode 100644
index 000000000..6ed7e7ccf
Binary files /dev/null and b/test/reference/hairline-big.pdf.ref.png differ
diff --git a/test/reference/hairline-big.quartz.ref.png b/test/reference/hairline-big.quartz.ref.png
new file mode 100644
index 000000000..1f153dd70
Binary files /dev/null and b/test/reference/hairline-big.quartz.ref.png differ
diff --git a/test/reference/hairline-big.ref.png b/test/reference/hairline-big.ref.png
new file mode 100644
index 000000000..98ea2428f
Binary files /dev/null and b/test/reference/hairline-big.ref.png differ
diff --git a/test/reference/hairline-big.xcb-window&.ref.png b/test/reference/hairline-big.xcb-window&.ref.png
new file mode 100644
index 000000000..a662668db
Binary files /dev/null and b/test/reference/hairline-big.xcb-window&.ref.png differ
diff --git a/test/reference/hairline-big.xcb-window.ref.png b/test/reference/hairline-big.xcb-window.ref.png
new file mode 100644
index 000000000..a662668db
Binary files /dev/null and b/test/reference/hairline-big.xcb-window.ref.png differ
diff --git a/test/reference/hairline-big.xcb.ref.png b/test/reference/hairline-big.xcb.ref.png
new file mode 100644
index 000000000..a662668db
Binary files /dev/null and b/test/reference/hairline-big.xcb.ref.png differ
diff --git a/test/reference/hairline-big.xlib-window.ref.png b/test/reference/hairline-big.xlib-window.ref.png
new file mode 100644
index 000000000..ce6aaaeed
Binary files /dev/null and b/test/reference/hairline-big.xlib-window.ref.png differ
diff --git a/test/reference/hairline-big.xlib.ref.png b/test/reference/hairline-big.xlib.ref.png
new file mode 100644
index 000000000..ce6aaaeed
Binary files /dev/null and b/test/reference/hairline-big.xlib.ref.png differ
diff --git a/test/reference/hairline-scaled.image16.ref.png b/test/reference/hairline-scaled.image16.ref.png
new file mode 100644
index 000000000..5b82d0dc8
Binary files /dev/null and b/test/reference/hairline-scaled.image16.ref.png differ
diff --git a/test/reference/hairline-scaled.pdf.ref.png b/test/reference/hairline-scaled.pdf.ref.png
new file mode 100644
index 000000000..ef0973242
Binary files /dev/null and b/test/reference/hairline-scaled.pdf.ref.png differ
diff --git a/test/reference/hairline-scaled.quartz.ref.png b/test/reference/hairline-scaled.quartz.ref.png
new file mode 100644
index 000000000..c0653a12c
Binary files /dev/null and b/test/reference/hairline-scaled.quartz.ref.png differ
diff --git a/test/reference/hairline-scaled.ref.png b/test/reference/hairline-scaled.ref.png
new file mode 100644
index 000000000..f059b4288
Binary files /dev/null and b/test/reference/hairline-scaled.ref.png differ
diff --git a/test/reference/hairline-scaled.svg11.ref.png b/test/reference/hairline-scaled.svg11.ref.png
new file mode 100644
index 000000000..f45367bb0
Binary files /dev/null and b/test/reference/hairline-scaled.svg11.ref.png differ
diff --git a/test/reference/hairline-scaled.xcb-window&.ref.png b/test/reference/hairline-scaled.xcb-window&.ref.png
new file mode 100644
index 000000000..22ba5e291
Binary files /dev/null and b/test/reference/hairline-scaled.xcb-window&.ref.png differ
diff --git a/test/reference/hairline-scaled.xcb-window.ref.png b/test/reference/hairline-scaled.xcb-window.ref.png
new file mode 100644
index 000000000..22ba5e291
Binary files /dev/null and b/test/reference/hairline-scaled.xcb-window.ref.png differ
diff --git a/test/reference/hairline-scaled.xcb.ref.png b/test/reference/hairline-scaled.xcb.ref.png
new file mode 100644
index 000000000..22ba5e291
Binary files /dev/null and b/test/reference/hairline-scaled.xcb.ref.png differ
diff --git a/test/reference/hairline-scaled.xlib-window.ref.png b/test/reference/hairline-scaled.xlib-window.ref.png
new file mode 100644
index 000000000..a59338584
Binary files /dev/null and b/test/reference/hairline-scaled.xlib-window.ref.png differ
diff --git a/test/reference/hairline-scaled.xlib.ref.png b/test/reference/hairline-scaled.xlib.ref.png
new file mode 100644
index 000000000..a59338584
Binary files /dev/null and b/test/reference/hairline-scaled.xlib.ref.png differ
diff --git a/test/reference/hairline.image16.ref.png b/test/reference/hairline.image16.ref.png
new file mode 100644
index 000000000..fa3318cb6
Binary files /dev/null and b/test/reference/hairline.image16.ref.png differ
diff --git a/test/reference/hairline.pdf.ref.png b/test/reference/hairline.pdf.ref.png
new file mode 100644
index 000000000..0a0ecb33e
Binary files /dev/null and b/test/reference/hairline.pdf.ref.png differ
diff --git a/test/reference/hairline.quartz.ref.png b/test/reference/hairline.quartz.ref.png
new file mode 100644
index 000000000..e51ea63c9
Binary files /dev/null and b/test/reference/hairline.quartz.ref.png differ
diff --git a/test/reference/hairline.ref.png b/test/reference/hairline.ref.png
new file mode 100644
index 000000000..e33683115
Binary files /dev/null and b/test/reference/hairline.ref.png differ
diff --git a/test/reference/hairline.svg11.ref.png b/test/reference/hairline.svg11.ref.png
new file mode 100644
index 000000000..834f3a43a
Binary files /dev/null and b/test/reference/hairline.svg11.ref.png differ
diff --git a/test/reference/hairline.xcb-window&.ref.png b/test/reference/hairline.xcb-window&.ref.png
new file mode 100644
index 000000000..1175b14a9
Binary files /dev/null and b/test/reference/hairline.xcb-window&.ref.png differ
diff --git a/test/reference/hairline.xcb-window.ref.png b/test/reference/hairline.xcb-window.ref.png
new file mode 100644
index 000000000..1175b14a9
Binary files /dev/null and b/test/reference/hairline.xcb-window.ref.png differ
diff --git a/test/reference/hairline.xcb.ref.png b/test/reference/hairline.xcb.ref.png
new file mode 100644
index 000000000..1175b14a9
Binary files /dev/null and b/test/reference/hairline.xcb.ref.png differ
diff --git a/test/reference/hairline.xlib-window.ref.png b/test/reference/hairline.xlib-window.ref.png
new file mode 100644
index 000000000..ed55291f3
Binary files /dev/null and b/test/reference/hairline.xlib-window.ref.png differ
diff --git a/test/reference/hairline.xlib.ref.png b/test/reference/hairline.xlib.ref.png
new file mode 100644
index 000000000..ed55291f3
Binary files /dev/null and b/test/reference/hairline.xlib.ref.png differ
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
@@ -5161,6 +5161,27 @@ _set_line_width (csi_t *ctx)
return CSI_STATUS_SUCCESS;
}
+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)
{
@@ -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 },
More information about the cairo-commit
mailing list