Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Text rendering improvements #143

Open
32 of 91 tasks
mosra opened this issue Apr 24, 2016 · 12 comments
Open
32 of 91 tasks

Text rendering improvements #143

mosra opened this issue Apr 24, 2016 · 12 comments
Milestone

Comments

@mosra
Copy link
Owner

mosra commented Apr 24, 2016

The text rendering library didn't age well and while trying to use it for the upcoming UI library (mosra/magnum-extras#2, not there yet) I got really angry at the shameful state of it. My previous job revolved around text a lot and, while this library was the prototype that I built upon, the actual real-world text rendering needed a lot more flexibility than what's provided here. Putting this braindump here before it fades away completely.

If somebody wants to tackle one of these things, I will be glad to help you and suggest a direction.

Things that are shitty and need to be fixed ASAP

  • Introduce lower-level reusable & composable APIs that work on singular glyph runs and pre-allocated memory -- 568a420
    • Re-implement the existing Renderer interfaces on top of these -- 568a420
    • Rework (or ditch / completely replace?) the Renderer interfaces to be more efficient
      • Make it use some global index buffer. It's crazy to have it repeated in each and every instance -- it's just 0 1 2 0 2 3 4 5 6 4 6 7 ... over and over again.
      • Make it usable outside the "one GL buffer and mesh for each text block" use case
      • Rename it with a GL suffix to make room for Vulkan, WebGPU, etc. implementations
  • Reduce the amount of allocations and braindead use of std::vector every time we need some array with size known in advance -- 568a420, ab8dc07, 2692ff2...
  • Reduce the amount of virtual calls and batch things more. Currently each glyph on screen equals one virtual call. Crazy. It also unnecessarily complicates things further down, compared to just getting an array of glyph positions and then iterating over it to put them into final positions. -- ab8dc07

Things that need to be improved/fixed before going further

  • Finally implement some proper texture atlas packing, as noted in the ancient Texture atlas creation #2 -- 66bf0b2
  • Properly handled initialization/finalization of plugins with dependencies. Current way with static initialize() function is broken by design: initializing HarfBuzz plugin after FreeType will call FreeTypeFont::initialize() second time, finalizing HarfBuzz plugin will call FreeTypeFont::finalize() even though FreeType plugin is still used and thus breaks it. -- mosra/magnum-plugins@217832f
  • Write a test for TextureTools::distanceField(), ensure that it doesn't have artifacts on older platforms, ensure that it works even on GLES and WebGL -- done in 209cdbc, 2e4beb3 and 8ba2cac
  • Write a test for Text::GlyphCache and Text::DistanceFieldGlyphCache -- 82691fc
  • Write tests for all font plugins and converters -- 4ed4e44, mosra/magnum-plugins@be2d715
  • API-agnostic implementation of glyph cache so it's usable with non-GL APIs -- 834c5fe
    • Great, but now the class is non-movable due to the virtual destructor. Fix that. -- 43efac8

Making the renderer usable with real-world text and font features

Having automatic font fallback would make the engine better than 90% of competitors.

  • Ability to specify glyph IDs or Unicode codepoints to add to the cache in addition to a string -- 631b9b8
  • A way for the renderer to report missing glyphs to the glyph cache
  • Incremental glyph cache updates -- 6707534
    • Incremental TextureTools::Atlas functionality -- 66bf0b2
    • Image::slice() that operate with PixelStorage parameters so we're able to upload image subdata
  • Incremental TextureTools::distanceField() -- ada0645, abf8a75, a2554cb
  • Ability to have multiple fonts in the same glyph cache -- 6707534
  • Ability to use the text renderer with more than one font, selecting the proper one based on glyph availability or some user-provided predicate function. This would require larger redesign, but very rewarding when done. Possible use cases are combining Latin and CJK text because there is no single font that contains both nice-looking Latin and CJK characters or for example having different parts of the same text block in different font (think bold, italic).
    • Decide how to handle the glyph availability. Some systems are doing it per character combination (better looking but harder), some per-codepoint (easier but the combinations may not match).
    • Properly handling shaping across font boundaries (HarfBuzz has a "text context" for this) -- ab8dc07, mosra/magnum-plugins@0782cdf
    • Switching fonts based on special Unicode control characters (or, if there are none, using the private plane for it) wouldn't be able to use the shaping context that way, has to be a separate stream
  • Ability to use Texture2DArray for the glyph cache instead of Texture2D so we can fit also big alphabets inside. And somehow make it optional or backwards compatible with GLES2 / WebGL 1 that doesn't have them.
    • Rename the glyph caches with a GL suffix, to make room for Vulkan, WebGPU etc. implementations -- 42342cd
    • Ability to use cubemaps? Could help on ES2 platforms, it's not an array but it's 6x times as large as a 2D texture nobody cares about ES2 nowadays anymore
  • Specifying OpenType features for (parts of) the shaped text -- 5009d38, ab8dc07, mosra/magnum-plugins@6bc898c
  • Specifying not just font size but also variable OpenType features like interpolating between regular and bold variant
  • Ability to create several "font variants" of different size (or OTF variable font features) inside a single AbstractFont instance, to not have to load the same font file several times
    • HB distinguishes between "face" and "font" for this
    • FT has setFontSize, how expensive is it to call it every time? any HB-like feature for this?
    • stb_truetype has nothing like that, can just use a different font size every time
    • have addVariant(size) / removeVariant(id) and then using that variant ID in createShaper()?
    • such variant then also has to be distinguished by the GlyphCache, i.e. a second argument to findFont() (and third to addFont())
    • have a feature flag that exposes this "variant caching", and a generic fallback maintained by the AbstractFont for fonts that don't need it
    • and a feature flag where the font has just one fixed size, for (shitty) bitmap fonts? or keep it like this?
    • also options to adjust line height, letter spacing etc
  • Fun! Ability to draw RGBA glyphs (emojis!) along with single-channel glyphs -- some sort of two-pass rendering (slow) or a more complex shader that switches between textures on per-character basis (slow) or ...?

Improved text rendering capabilities

  • Font view that allows the user to override font size, line advance etc. for particular rendering
  • Renderer view that allows rendering a part of the text differently (color, fake bold etc.)
  • Shear option for fake italics (easy!)
  • Allow better batching
    • Allowing the renderer to specify different alignment etc. for each new block that is being added
    • Two-dimensional line advance to allow rendering table cells etc. in one row. Can we abuse some special ASCII characters to advance both vertically and horizontally? Tab stops?
    • Renderer block that allows to share a single buffer/mesh but modify subparts of it
  • Aligning a text in a bounding box instead of just to cursor to avoid duplicating that functionality in user code
  • Line breaking

Unicode-aware text shaping

  • Make HarfBuzz the go-to solution on all platforms instead of trying to emulate it poorly using other libraries. Works on Linux, should "just work" on OSX, I was able to compile it for Windows using CMake. What about Android? iOS? Emscripten? No. Instead broaden the API to make it possible to fully control HarfBuzz from it while still making it possible to use stb_truetype or plain bitmap fonts where advanced shaping isn't needed
  • Ability to pass direction, script and language to the shaper -- 03c180d, ab8dc07, mosra/magnum-plugins@9672a9f
  • Ability to autodetect these in the shaper (http://unicode.org/reports/tr24/ -- I read through it and implemented it, but most of it faded away already. HarfBuzz somehow exposes this in the API so one could maybe take it as a starting point.) Just let HarfBuzz do that on its own.

Improved text editing capabilities

For the UI.

  • Moving cursor left/right in UTF-8 text
  • A way to convert cursor position to screen coordinate -- e1c9c4d
    • And back for cursor positioning
    • Handle this properly with combined shapes and ligatures

Plugins

  • Plugin using stb_truetype to make the usage easier on platforms that don't "just have" FreeType (stb_truetype font plugin magnum-plugins#12)
  • Kerning in MagnumFont
  • Kerning in FreeTypeFont
  • Kerning in StbTrueTypeFont
  • Investigate autohinting possibilities in StbTrueTypeFont to make it less blurry and differing in size
  • AnyFont so one can just not care about which plugin is getting used

Tools

Shaders and rendering

Making the renderer usable with BIDI

Specs: http://unicode.org/reports/tr9/

  • Direction autodetection based on script (easy) HarfBuzz does this implicitly as of 0782cdf5e0b2f588bdbde6aa139e9fcb556c5bda
  • Proper BIDI algorithm implementation is probably not feasible to maintain in the context of Magnum, add a AbstractLayouter interface and expose it via plugins
  • https://github.com/HOST-Oman/libraqm, does it make sense to try to integrate it as a whole AbstractLayouter?
    • It's just 2K lines that call into HB, FT or BIDI, and the direct dependency on a font makes it rather hard to expose in a composable way (i.e., couldn't layout with this and use stb_truetype for rendering for example).
    • Doesn't support line wrapping yet, which I'd have to implement myself anyway.
@mosra
Copy link
Owner Author

mosra commented Apr 29, 2016

New tech just keeps appearing: https://github.com/Chlumsky/msdfgen

@mosra
Copy link
Owner Author

mosra commented May 6, 2016

@mosra
Copy link
Owner Author

mosra commented May 6, 2016

Safe-to-break API appearing in HarfBuzz: harfbuzz/harfbuzz#224

@ytain
Copy link

ytain commented May 6, 2016

Also for inspiration to get more creative with text rendering: https://www.youtube.com/watch?v=q3ROZmdu65o (found from there https://news.ycombinator.com/item?id=10824989 )

Also another one https://github.com/behdad/glyphy - Vector based SDF

@ytain
Copy link

ytain commented May 7, 2016

Another article on rendering using vectors instead of bitmaps https://medium.com/@evanwallace/easy-scalable-text-rendering-on-the-gpu-c3f4d782c5ac#.gdsqful7b

@mosra
Copy link
Owner Author

mosra commented May 7, 2016

@ytain wow, that vector rendering is really neat! Knew about Glyphy but forgot to add it. Updated the list, thank you!

@mosra
Copy link
Owner Author

mosra commented May 13, 2016

stb_truetype font plugin merged: mosra/magnum-plugins#12

@mosra
Copy link
Owner Author

mosra commented May 31, 2016

@ghost
Copy link

ghost commented Sep 6, 2016

@mosra I've been using stb_truetype, but will we ever get an AnyFontImporter or something?

@mosra
Copy link
Owner Author

mosra commented Sep 7, 2016

@alicemargatroid there's currently not much use of AnyFont-like plugin, because the engine supports mainly just the TTF format.

I think you might want to use the TrueTypeFont plugin alias -- it maps to StbTrueTypeFont, FreeTypeFont or HarfBuzzFont based on which one of them is available, so then you don't need to bother about particular plugin implementation in you app.

@mosra
Copy link
Owner Author

mosra commented Oct 11, 2018

Qt has a nice new tool for generating distance fields, appending them to the original TTF/OTF file: https://blog.qt.io/blog/2018/10/10/introducing-distance-field-generator/ Support for such files (and ability to generate compatible files directly with magnum-fontconverter) could be very beneficial.

@mosra mosra added this to the 2019.01 milestone Dec 29, 2018
@mosra mosra mentioned this issue Dec 29, 2018
55 tasks
@mosra mosra modified the milestones: 2019.01, 2019.0b Feb 4, 2019
@mosra
Copy link
Owner Author

mosra commented Mar 7, 2019

Progress:

  • Distance field conversion is now tested and confirmed to be working on most platforms since 209cdbc, 2e4beb3 and 8ba2cac
  • API-agnostic glyph cache base done in 834c5fe

@mosra mosra modified the milestones: 2019.0b, 2019.0c Jul 5, 2019
@mosra mosra mentioned this issue Jul 14, 2019
@mosra mosra modified the milestones: 2020.0a, 2020.0b Jun 24, 2020
@mosra mosra mentioned this issue Jun 24, 2020
87 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: In progress
Development

No branches or pull requests

2 participants