PATCH cairo-www: Simpler explanation for how to get sharp, single-pixel-wide lines

Submitted by Lawrence D'Oliveiro on May 22, 2017, 11:52 p.m.


Message ID
State New
Headers show
Series "PATCH cairo-www: Simpler explanation for how to get sharp, single-pixel-wide lines" ( rev: 3 ) in Cairo

Not browsing as part of any series.

Commit Message

Lawrence D'Oliveiro May 22, 2017, 11:52 p.m.
Any more comments on this? Any more points to raise that have nothing
to do with the original question? Or things you think I should address
in my answer, which I already did?

Troll now, and avoid the rush!

Patch hide | download patch | download mbox

From d6023c23bfffa990d0a7c05fdeef4bac60ad7606 Mon Sep 17 00:00:00 2001
From: Lawrence D'Oliveiro <>
Date: Sun, 24 Apr 2016 22:08:39 +0000
Subject: [PATCH 2/2] Simpler explanation for how to get sharp,
 single-pixel-wide lines.

 src/FAQ.mdwn | 73 +++++++++++++-----------------------------------------------
 1 file changed, 15 insertions(+), 58 deletions(-)

diff --git a/src/FAQ.mdwn b/src/FAQ.mdwn
index 0d86520..7673ece 100644
--- a/src/FAQ.mdwn
+++ b/src/FAQ.mdwn
@@ -183,65 +183,22 @@  Answer: This problem usually shows up with code like the following,
 The user expects 10 pixels in one row to be affected, but instead the
 line is "smeared" over 20 pixels in two rows.
-The reason this happens is easy to explain to someone who believes
-pixels are little squares. By default, integer coordinates map to the
-intersections of the pixel squares. So a width-1 horizontal/vertical
-line is centered on the boundary between pixel rows and extends half
-way into the pixels on either side.
-When some people hear pixels described as little squares it sets their
-teeth on edge. For them we have this alternate explanation. By
-default, integer coordinates map to points half way between sample
-point locations. So a width-1 horizontal/vertical will be centered
-between two rows of pixel sample points and will contribute equally to
-the pixels on either side, (assuming a symmetric filter which is
-always the case when synthesizing images in cairo).
-Either way, you can avoid the issue by using an even-integer line
-width, (note that the default line width in cairo is 2.0). A line
-drawn this way will still affect just as many pixels, but they will be
-affected at full intensity.
-Otherwise, if you really want to light up a single row of pixels at
-full intensity, you can do that by adjusting the endpoints by 0.5 in
-the appropriate direction. For example, by changing the above code to:
-	cairo_move_to (cr, 10, 10.5);
-	cairo_line_to (cr, 20, 10.5);
-	cairo_set_line_width (cr, 1);
-	cairo_stroke (cr);
+The answer is simple: turn off antialiasing, by preceding the above
+sequence with a call like this:
+    cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE);
+Now you will get nice, sharp lines for both stroking and filling,
+with all drawing automatically being snapped to exact device pixels,
+instead of being smeared across adjacent pixels to emulate in-between
+The drawback is that, if your lines are not exactly vertical or horizontal,
+you will get horrible “jaggies”. This is why antialiasing is on
+by default, and should be left that way for most text and shape drawing.
+To restore the default, end your aliased drawing sequence with
-The reason that cairo does it this way is so that fills align nicely,
-at the cost that some strokes do not. It is not possible to set up
-sample locations in a way so that both fills and strokes with integer
-coordinates work nicely, so one had to be preferred over the
-other. One argument in favor of preferring fills is that all fills
-with integer coordinates align nicely this way. The best that can be
-done with strokes is to make all even-integer-width strokes align
-nicely (as they do in cairo) or to make odd-integer-width strokes
-align (which would then break the fill alignment).
-It is important to note that all of this discussion assumes the
-default transformation matrix. Under other transformations, the
-relationship between user-space coordinates and device-space pixels
-may be quite different. Guaranteeing alignment with the device-pixel
-grid, "snapping", in such cases is still possible and is often worth
-doing (except in situations such as animated zooms where the snapping
-would lead to undesired jitter in the result). The trick to snapping
-is simply to perform rounding in device-space so that rectilinear
-geometry lands at integer position in the device-space grid. So, the
-process might look something like:
-	cairo_user_to_device (cr, &x, &y);
-	do_rounding (&x, &y);
-	cairo_device_to_user (cr, &x, &y);
-and then using the resulting x and y values to construct a path. Note
-that the `do_rounding` function will likely need to be different
-depending on how the coordinates are being used. For example, for a
-fill or a stroke with an even-integer line width, this function might
-round to the nearest integer. For a stroke with an odd-integer line
-width, it might round to the nearest half integer.
+    cairo_set_antialias(cr, CAIRO_ANTIALIAS_DEFAULT);
 <h2 id="using_pango">How do I use pango instead of cairo's "toy" text