Open soegaard opened 3 years ago
FWIW, I thought the answer to the dancing text was going to be creating a font with 'unaligned
hinting, but that does not solve the problem.
An obversation - made after I wrote the guess below:
The rotate? flag in do-text
is set to (and draw-mode (not (zero? angle)))
.
But what if a rotation has been made with (send dc rotate angle)
then the normal code path is taken.
Is that intended?
The loop in do-text
draws in one of the paths each character individually.
https://github.com/racket/draw/blob/master/draw-lib/racket/draw/private/dc.rkt#L1709
My intution - but I am far from sure - is that the alignments of each individual character gives a different result than using the line "top line" of the rectangular binding box of the string.
Let's say we want to draw the text hello. The bounding box is a rectangle ABCD with the line AB being at the top. Due to rotation the line AB might no be parallel to an axis.
If A is aligned to A1 and we set B1 to B+(A1-A), then the upper left corners of each character ought to be on the line from A1 to B1. However if we align each individual character, then I think there is a risk that two neighbour characters might move in different directions relative to the line when aligned. At least when rotation is involved.
I haven't grooked the Pango part of the draw-text code, but this aligns each individual character: https://github.com/racket/draw/blob/master/draw-lib/racket/draw/private/dc.rkt#L1709
You can avoid the character-by-character loop by providing #t
as the combine?
argument to draw-text
. But I think that doesn't solve the problem here.
Yes, as I remember/understand the code, the normal code path is meant to be taken when rotation is in the transformation.
You are right, using #t
as the combine?
flag for draw-text
didn't help.
@lexi-lambda
You have looked at fonts and text rendering recently.
Have you spotted something that explains the behaviour in the video?
/Jens Axel
Hi, @soegaard—I just took a look at this. After some investigation, I suspect that pango_context_set_round_glyph_positions
may be relevant. By default, Pango rounds all glyph positions to pixels, regardless of hinting settings, but that function can be used to disable that behavior.
Unfortunately, when I actually experimented with disabling rounding, I found that it didn’t seem to affect the rendering at all on my machine. While I’m not sure why that would be the case, after some further reading, I get the sense this may be because the version of Cairo I have on my machine (which is running Ubuntu 20.04) doesn’t support subpixel positioning (which is distinct from what’s normally called “subpixel rendering”!) for the necessary font backend. If you’re on macOS, you might want to give it a try yourself (and disable hinting) and see if that changes anything.
Having said all of this, I think you probably don’t want to be using draw-text
if you’re animating text, anyway. Font rendering is really complicated, and there are so many different moving parts involved in a modern text rendering stack that it makes my head spin. Lots of those parts have to make tough choices about how to place glyphs in a readable fashion, and arranging for all those choices to be temporally consistent under transformation seems like probably a lost cause.
Instead, I think what you almost certainly want to do is convert the text to an outline first using the text-outline
method of dc-path%
, then fill the resulting path using the draw-path
method of dc<%>
. This will run through the whole text shaping and layout pipeline with the glyphs in a well-behaved configuration, namely placed in a straight line, then give you back a set of vector paths. Then, when you call draw-path
, Cairo will render the path itself as an ordinary shape, skipping the OS- and backend-specific text rendering pathways altogether. Cairo certainly knows how to do antialiasing for arbitrary paths, so you’ll get a smooth animation.
There are two major downsides to rendering text this way:
Since Cairo’s drawing the paths itself, all of the fancy logic in your operating system’s text layout and rendering stack will be skipped: you’ll get no font hinting or subpixel antialiasing/LCD smoothing, and any user-controlled, OS-wide font rendering options will be ignored.
In your case, this is more of a feature than a bug, since you want to animate the paths yourself. But it does mean that you may need to take some extra care to avoid blurry edges resulting from misalignment—though, fortunately, dc<%>
’s 'aligned
drawing mode is probably sufficient to avoid that most of the time.
Since you’ve converted the glyphs to a shape, their textual content is necessarily lost before they get to the backend. This means that if you’re using a pdf-dc%
, for example, the rendered text won’t be selectable in a PDF viewer.
This probably doesn’t matter to you, because if you’re rendering an animation, you aren’t going to be using the PDF backend, anyway. Moreover, I’m not sure racket/draw
currently actually sets the relevant options to have the text stored in the PDF in the first place.
tl;dr: I’m not exactly sure what the problem is, but there are probably too many moving parts to make animated text look good in a backend-agnostic way without just drawing the glyphs yourself. Try that, instead.
Hi @lexi-lambda
Thanks for the very thorough answer.
I am currently using draw-text
to implement text
[1] in Sketching.
The text
function optionally accepts a rectangle to draw the text within
and supports left, center and right alignment of the text.
Since it is part of Sketching, I don't know whether the text will be used for animation or for static text.
I'll make an experiment with text-outline
+ draw-path
instead of draw-text
.
If the results look identically I'll just use the new method.
I was hoping there were some low-level Cairo routines I could have used.
This example shows a rotating text.
Zooming in on the text (on macOS use ctrl and swipe up with two fingers to zoom in) shows that the letters are "dancing". It looks as if each letter is rotated individually instead of the text being drawn as a whole.
The expected result can be seen here (using p5.js): https://processing.org/examples/textrotation.html
The example also shows that the thickness of the line varies. Zoom in on the line and it becomes apparent that the line at some angles shrink in width. In an animation this looks odd.
Turning on smoothing fixes the line thickness problem - but the reason for turning it off was for speed.
Screen recording:
https://user-images.githubusercontent.com/461765/124123958-28c71b00-da78-11eb-8ab9-f7c3563dcb23.mov