HN user

vorporeal

29 karma
Posts1
Comments13
View on HN

MSDF seems like a great choice, depending on your use-case. Given that we don't need to draw text at multiple scales, it doesn't seem to provide much benefit for us over caching rasterized glyphs provided by Core Text. (Copying a rasterized glyph is certainly faster than evaluating an SDF.)

Using Core Text also means our text looks the same as it does in other applications, which avoids bug reports from users like "text in Warp looks different than in iTerm". :)

1. I guess the answer is that it is both global and local? An application window draws its contents to a buffer (basically, an RGBA image), which the operating system can copy to the buffer that represents the full contents of the screen. The buffer for a given window is effectively a grid of pixels; when we lay out the contents of our application, we compute an (x,y) position and (w,h) size for each primitive (whether a rectangle or a glyph). These four values (x,y,w,h) don't have to be integral, but if they're not, some extra work has to be done in order to correctly set the colors of the buffer's pixels that "sit beneath" the objects that we're drawing.

2. We do use Core Text, in fact, via a cross-platform Rust library called font-kit. It provides shaping and glyph rasterization APIs for us, but for performance reasons, we cache the results of shaping at a line level but the results of rasterization at a glyph level (as individual glyphs frequently appear in multiple places in a single frame).

3. We're currently delegating rasterization to Core Text; I'm not sure what it uses internally. My sense is that most of the development of new rasterization strategies is for applications like game engines, which need to be able to draw text efficiently at a variety of scales, a requirement that doesn't apply for a terminal. We prioritize crispness of text (and, to some extent, consistency with text rendering in other applications) over size scalability, as people will spend a lot of time reading text at a single size.

4. That's a good question, and I don't have an answer handy. We load (most) font data in the background, as we only use two fonts at any time (one for monospace text, like in the terminal grid, and one for UI strings). We currently don't do any shaping in the terminal grid (and therefore don't support ligatures there). That said, we cache shaping results at a per-line level and rasterization results at a per-glyph level, so don't need to do either step frequently. In aggregate, over a user session, the vast majority of the time will be spent "copying" an already-rasterized glyph from our atlas to the window's backing buffer.

From my research, it seems like SDF is a best suited for game engines to use for text, especially non-UI text (that may appear at arbitrary sizes), as it is a fast, space-efficient, and easily scalable approximation.

Unfortunately, SDF tends to lose precision at corners, making it less good for text-oriented applications. It does seem like multi-channel SDF improves on the corner precision issues (as highlighted by the repo you linked to), but in a terminal, we don't frequently need to render text at different scales, so not sure there is much benefit. (If a user changes font sizes, we can clear out and rebuild the atlas sufficiently quickly.)

That said, I could borrow some ideas from MSDF to improve on space efficiency by encoding the three different offset variants into separate color channels at the same atlas location, reducing size of the atlas itself as well as the hashmap used to look up atlas position from glyph ID and whatnot.

Good call-out - definitely a bit of a simplification there. We account for this in our terminal grid by allocating an empty cell following any double-width glyphs (e.g.: emoji and wide chars, as you mentioned).

Tried out your example in Warp, and the alignment is as you described: https://i.ibb.co/Jcwcnwn/image.png

Core Text supports rasterizing at sub-pixel offsets (though some configuration of the graphics context is necessary to do so properly) by applying a transformation to the graphics context before the rasterization call. I'll definitely have to figure out the FreeType angle when we start working on Linux support; if I uncover anything (and remember this comment thread), I'll report back with my findings. :)

Blog post author here - great questions.

My understanding is that some, but not all, shaping libraries will handle fallback fonts. For example, I believe Core Text (on MacOS) will specify which sections of a laid-out line should be rendered using which fonts, factoring in fallback font selections. Kerning should work within a run of a single font, but I'm not sure there's any way to "properly" kern a glyph pair from two separate fonts - there wouldn't be any information available about proper alignment (as kerning data is part of a font file). In terms of sizing, one would hope that font creators all respect the em-square when constructing glyph vectors, leading to two different fonts using the same point size having comparably-sized glyphs. If one doesn't want to rely on that, font metrics like ascent and descent could be utilized in an attempt to normalize sizing across different fonts.

In terms of cache size: at the moment, we rarely empty out the cache (but we should do it more often). I have some ideas around triggers for emptying the cache and letting it get rebuilt (e.g.: changes to font size, changes to font family), but haven't wired it up yet. In addition, we could consider clearing the cache periodically when the application is sitting in the background (allowing us to re-rasterize the needed glyphs without blocking painting a frame). So tl;dr: we don't currently but we should and will do so in the future.

NPR One 12 years ago

This seems to be due to Google's new grouping of permissions for Android apps. The permission group you're referring to is described as "Uses one or more of: files on the device such as images, videos, or audio, the device's external storage". That means that if the app wants to use some of its own data which is located on external storage (which is pretty common), you're also giving it permission to access your media files on the device, because Google thinks (for some incomprehensible reason) that these are similar permissions and grouping them makes it easier for users to understand what's going on.

This is the fault of your implementation, not the pattern. Event types (names) should be enums, not arbitrary strings. This makes it very easy to change the way the code reads if the meaning of the event slightly changes, and equally easy to obfuscate the event key in a production build.

The primary issue is that the design is very obviously driven by the goal of monetization. On my laptop, the top half of the page is dominated by the navigation bar at the top (starting with "Premium Membership") and the "Learn From Proven Entrepreneurs" box, featuring a giant "Go Premium?" button, which ends up being right in the middle of the page. I'd estimate that <20% of the space above the fold is useful content.

In addition, there are a number of polish issues that exacerbate the spammy feeling caused by what I described above. There's an escaping bug in the title of the login widget on the right side ("YOU\'RE LOGGED OUT"). You use a number of different background colors (the search widget has a blue one, the "What's new here" has an off-white one). There are too many groups of social media buttons (on the left of the main container, under the search box, under the video clip). There are a lot of horizontal and vertical separators, and they have differing styles and amounts of padding.

Hope that helps!

Social and Promotions should basically just contain the "unimportant crap". If something is mis-categorized, you can drag it to the correct section and Gmail should learn from its mistake.

Also, as mentioned below, you can revert to the un-tabbed inbox by going to the Inbox section of the settings and disabling all tabs other than Primary, or by selecting one of the split inboxes (Unread first, Priority, etc).

Disclaimer: I'm a Gmail engineer.

I think it's more of "The information you requested, were it to exist, would be highly classified (due to its type), and therefore we can neither confirm nor deny its existence."

This is a web page 13 years ago

Drawings are the heart and soul of civilization. For one, they originated ~37 thousand years before the first written language. Second, understanding text requires knowing how the information it contains is encoded (what language it is written in). Drawings are far more universal.