To Hint or Not To Hint? That Is The Question


#1

Except for a static interface in a traditional desktop application.

This is all true, but irrelevant for a static interface in a traditional desktop application. For example, I don’t draw paragraphs of text in my app, and the user can’t zoom into my controls. Having the option to turn hinting on and off, addresses your scenario.

I agree with you completely. But in my application, I don’t shear text, nor do I rotate (or zoom) it. Hinting would be useful for small fonts. Having the option to turn hinting on or off would be useful.

This may be true, but I think that designing an application to be resolution-independent, for the “someday” when monitors for traditional desktop systems are uber high-res, is not a requirement for my current project. If it becomes an issue, I will simply create by hand two interfaces. One with hinting turned on, where the text labels are as small as I can make them while still being legible (dedicating the maximum amount of screen real estate for the user data, which is non-textual), and another interface that scales with resolution, where hinting is turned off. This way the user can choose which one they want. Note that once again, having the option for hinting is useful.


#2

I’m really sorry that what you’re doing happens to be something that would benefit from hinting, but the bottom line here is that I’m just not willing or able to add it to the library.

All my attention at the moment is focused on pushing things in the exact opposite direction - i.e. making the graphics engine more scalable, less static, using transformations, and letting the underlying OS handle the font rendering where possible. All of these goals are incompatible with what you’re asking for.

If you want to do it yourself, I think your best option would be to build freetype into your app, and use it to render just the relevant bits of text to a bitmap, and then push that onto the screen. To try to bend the general-purpose font engine to support it would be an absolute nightmare, and would end up being detrimental to all the normal-sized text in your app.


#3

Do you remember when we had CRT monitor ?
Resolution by that time (1999) used to overcome the 2MP (lIRC I had a 2536x2048 res or so).
Then we were switching to LCD monitor, with native resolution. The LCD monitor resolution by that time were, at best 1920x1200 (DELL for one, sold those monitor).

Now, you can’t even find a LCD monitor with a higher resolution than FullHD (1920x1080) (or you’ll pay a lung for a 3MP professional version).
For me, I’m not sure the monitor’s resolution increase is still alive.
The HDMI standard doesn’t support more than FullHD resolution @ 60Hz, so it’s unlikely it’ll change in the next 2 year ever.

I agree with Jules that delegating more hardware specific code to the OS is the right way to handle it, it’s less time consuming (don’t waste time re-inventing the wheel, like font rendering).
Probably, one day, no one will either speak in pixels.


#4

Is there a way we can have a #define to turn on that will allow Freetype to be an additional option under Macintosh and Windows builds? The same way that OpenGL is turned on:

#define JUCE_FREETYPEFACES 1

If the option was turned on then on a per-Typeface basis, depending on how the Typeface was constructed (there would be additional routines to construct a Typeface from a binary input stream, using Freetype), it would use Freetype or the existing code (Windows or Macintosh APIs).

I know you don’t want this, but it seems a lot of people are asking for it, and if it would not blow up the code I would be willing to invest some time to get it to work if you could give me some pointers.

This is a comparison of Freetype versus the unhinted Win32 glyph renderings:

[attachment=0]juce_vs_freetype.png[/attachment]

Drawing it all to a separate bitmap using Freetype and then copying that to the Graphics using an Image is not an option, it would cut the framerate down especially for things like scrolling through a table view (since its pretty much all text).


#5

Not an option? It’s a perfectly reasonable option. You only need to redraw the text if it has to change; for the first update sure, you’d have text rendering and an image draw, but then for the rest you only need to draw your cached image. If you have the system in place to render the text to an image, it’s no big job to knock together a text surface that only actually re-renders itself when necessary. I don’t see that being anything like the frame-rate hit you seem to expect it to be.


#6

Honestly, in the two images above, I prefer the text on the left. Maybe I’m just weird, but to me it looks more like a real piece of typography. The hinting on the right has distorted the proportions of the letter sizes, and to me the overall layout looks unnatural. I admit that I’m a bit of a typography geek, but I’m happy to lose a little sharpness if it means the overall composition is more accurate.

There’s no easy way you could add freetype support on mac/windows - how would people install, compile and link the freetype library into their project? Sorry, it’d be a huge amount of tedious messing about for an end result that I find ugly!

Have you considered redirecting your obsession away from hinting and towards cleartype-style RGB sub-pixel rendering? That’s a technology that does look good, and which could be fairly easily added to the software renderer…


#7

Well, the right image looks 10 times better to me.

I guess we’ll have to integrate freetype ourselves. But is there a problem with integrating it into Juce like the png codec? It’s an attribution-only license afaik.


#8

It should be done in such a way that if the programmer wants to enable Freetype in Juce on Mac and Linux, then they have to download the latest version of the Freetype sources, use the Makefile or whatever comes with it to produce the lib, and set some environment variables so Juce can find the sources. Obviously we don’t want to integrate the Freetype source code into Juce.

There shouldn’t be any issue with licensing. However if someone decides to use it then like you said, they would have to give an attribution. Which I’m perfectly okay with.

From http://www.freetype.org/autohinting/background.html#hinting

[quote]A good anti-aliasing scan-converter generates a gray-levels image that compute the exact outline coverage on each pixel, or an approximation of it. These images have much more detail than a monochrome bitmap. Generally, many people argue that rendering anti-aliased scaled outlines is sufficient and that no additional grid-fitting is needed.

However, experience proves that grid-fitting anti-aliased glyphs helps a lot by enhancing the contrast of certain glyph features, mainly edges, in order to make them more readable. Indeed, most users consider than non-hinted anti-aliased outlines are fuzzy and painful to read.[/quote]


#9

Actually this isn’t obvious to me. Pnglib, zlib, and flac codecs are integrated at source level…


#10

Actually this isn’t obvious to me. Pnglib, zlib, and flac codecs are integrated at source level…[/quote]

The Freetype code base is very hefty and considering Jules’ position on it, unlikely to be added to Juce. Furthermore it would interfere with the license (I think?). It’s also in development and changes every so often. Considering that for the majority of Juce users, they don’t need Freetype, it seems reasonable to have it external and require some additional effort on behalf of the developer in order to use it.

I was looking at juce_linux_Fonts.cpp and it looks like most of the implementation is already there! The problem is that Juce assumes that one Typeface is good for all sizes. It would require some changes to how Juce and the graphics renderer work with Typeface and Font in order to have different Typeface for each size - I think. Jules feel free to chime in.

I am talking specifically about producing a Typeface from existing truetype binary data. Using Freetype on the actual system fonts (versus a static variable embedded via BinaryBuilder) is something else.


#11

A Typeface represents a typeface in its most general form, and the Font represents a typeface at a particular size and style. Trying to bend those meanings would end in tears, and certainly wouldn’t be something that I’d be interested in changing.


#12

For me the sharpness is not the main issue. The problem I’m still facing is that individual letters appear different depending on their position in the text. Check the vertical lines of the letter ‘u’ in this example, more often than not one of the lines appears to be wider than the other:

This is plain Arial at 15px. It’s not an issue for larger sizes, and less obvious when using bold type font.
Also everything looks much better when rendering black text on white :

I’m unemotional (and clueless) about how to properly address this. In my usecase, I ended up obtaining a bolder variant of the font and carefully adjusted the sizes so that the effect is not so obvious any more. That’s good enough for now. It would be nice to have some hinted font-rendering for small fonts as an option, but I certainly don’t have the time and knowledge for integrating freetype-rendering.

@jules: if you do care about your typo, and your goal is that everything (including fonts) should scale smoothly: please also consider the white-on-black scenario next time you’ll work on the font-rendering. I still hope there’s a way to get the anti-aliasing look as regular as when rendering black-on-white.


#13

This is the real problem right here. Because most of the code to extract outlines using Freetype, already exists! It’s in juce_linux_Fonts.cpp.

Unfortunately, the Typeface is only generated once regardless of the number of different sizes. In order for hinting to work, some class interfaces in Juce (I’m guessing Font, Typeface at a minimum) would have to be changed and the code that uses it modified. As Jules said this might be a non trivial change.

However, there might be a way to do it that is not messy, and incurs no overhead or disturbance on the rest of the application. I would have to study it in more depth.

Sadly, even though Linux uses Freetype, it is unhinted (since outlines are only calculated once).


#14

Okay I looked at the code and here is a rough idea of what would be involved:

  • add a height field to Typeface
  • change TypefaceCache::findTypeFaceFor() to call a member of Typeface to perform the comparison. Right now its based of the name, but we would want to check both the name and the height:
    const Typeface::Ptr findTypefaceFor (const Font& font)
    {
        // ....
            if (face->flags == flags
                 && Typeface::matchingTypefaceFor( *face, font ) ) /* new version */
                 // && face->typefaceName == faceName) /* old version */
            {
                face->lastUsageCount = ++counter;
                return face->typeFace;
            }
        }

Typeface::matchingTypefaceFor() will check the name, and if it’s a Freetype style font then it will additionally perform a test on the height.

Thoughts?


#15

JUCE‘s text really too vague, and unclear. The strange thing is, Jules insists misconception … …


#16

#17

I think we have gotten past the question of whether or not hinting makes small typefaces easier to read (it does).

The problem, as Jules pointed out, is that a Typeface is size-independent, which is at odds with the concept of hinting (which produces a different set of outlines for the same face, depending on the size).

Any further discussions or efforts regarding hinting, should revolve around figuring out the most non-obtrusive, brief, elegant, and clever way to modify the Juce source code to support this use-case. Please see my previous post regarding TypefaceCache::findTypefaceFor ().


#18

I am a novice, English is poor, but like programming, I would like to develop a software.
Can you teach me? Thank you!


#19

After “only” 14 hours of continuous effort I managed to get FreeType with hinting to work in Juce as a CustomTypeface. In order to make hinting work, I did have to modify a little bit of the Juce sources, but the changes are very mild and gentle. Certainly nothing that would disturb any code that does not make use of these hinted CustomTypeface.

The current implementation has a limitation that any hinted FreeType fonts you want to use must come from an in-memory buffer that you provide. For example, you can use the Juce BinaryBuilder to turn your font.ttf file into a static variable that you include in your program. It should be possible to write some platform specific code to dig up the actual installed TrueType file from the hard drive but that’s beyond the scope of what I’m doing.

Here is some example output. The small Juce text looks alright, but the hinted version is noticably more crispy:

[attachment=0]hint_demo.png[/attachment]

Is anyone interested in any of this? Jules are you interested in seeing what I had to change in Juce to make this work?


#20

I had to solve this problem to make hinting work. The reason that letters appear different is because they are drawn at x coordinates with different fractional amounts. For example, one draws at 3.05 and the other draws at 15.6. My solution was to:

  • add a flag to the Typeface to indicate if glyphs should be allowed to have subpixel starting points
  • add a member to the PositionedGlyph to return whether the underlying Typeface allows subpixel starting points
  • round the x-coordinate of a glyph to the nearest integer in a couple of Juce routines based on whether the Typeface wants it