Fonts rendering

It’s not that we need real tiny fonts and the custom font is nothing too fancy, but a pretty basic basic sans-serif font.

We use a scalable layout throughout, so the text in question is between 12 and 20 px high. The font rendering is pretty good when rendering dark text on bright background, but the font edges are just rendered too dark for bright text. At some places we hacked around that by manually painting the glyphs twice: first filling their path, and then drawing their outline with a subpixel stroke just to get them to look a bit brighter/sharper. This worked well enough, but can’t be applied in places like listboxes that contain upto 1000 items. That’s why I was hoping that we can solve it with zamrate’s gamma correction patch, but I just don’t get what the AlphaBitmapRenderer he is talking about was replaced with.

From browsing the forum it seems that pixel based fonts require you to choose your font size and positions very carefully to avoid that anti-aliasing is applied. That does not mix well with our plan of a fully scalable plugin gui.

Have you thought about just using a bolder typeface…?

Could you give any quick tips on how to use freetype as a rendering back end for Juce?

Freetype’s already used in the linux build.

Would it be practical to have an option for using Freetype under all builds? It would be in addition to, rather than in lieu of, the existing Windows/Mac font code. I understand that under Linux its the only option.

Sorry, but can you explain why you’re asking? It’d be extremely awkward to do, and I can’t really see the point…

I want to draw Helvetica Neue at small sizes with hinting.

OMG what is it with people and this obsession with hinting??

Maybe instead of asking “how can I make my small fonts more readable?”, you should be asking “Why am I making this text so small? Isn’t there a better way to communicate the same information without making people squint?” Think laterally!

[quote=“jules”]OMG what is it with people and this obsession with hinting??

Maybe instead of asking “how can I make my small fonts more readable?”, you should be asking “Why am I making this text so small? Isn’t there a better way to communicate the same information without making people squint?” Think laterally![/quote]

Jules as much as I would like to sweep this under the rug, you are fighting an uphill battle here. In my case, the design requirement is to duplicate an existing interface. So my hands are tied with respect to changing the way things look. Small typefaces are part of that look.

It seems unfair that Linux gets TrueType font hinting while Windows and Macintosh suffer.

So, once again would it be practical to have Freetype support on Macintosh and Windows as a rendering option? Since the code is already there for Linux.

Sigh. Of all the threads dealing with font-rendering I replied to this one because it used to be about a possible solution for my scenario. Now we’re back at at the general discussion of “we want sharp fonts” vs “you don’t need them” again :roll:

But now now I just can’t resist to join in: :smiley:

As long as I wasn’t able to try the gamma-correction aproach from this thread, I don’t want to switch to a 20px font for readability, or to a pixel-based font that doesn’t scale well with the rest of the GUI.

My usecase for the small fonts is a browser to navigate through a hierarchy of folders and list up thousands of file names. People can read filenames in their finder (or in the windows explorer) quite well at a similar (if not smaller) font size, so it’s not a general interface design issue. See the right example in the attached file to see an what the font currently looks like in the plugin. The letter e is especially troublesome and hard to read. If the font wouldn’t be clearly readable at even smaller sizes in the original photoshop design, I wouldn’t have such a hard time telling everyone it’s impossible to make it look sharper at that size. (Especially if they see other aliased-fonts rendering much sharper right beside the plugin in their host)

I wish the design wouldn’t rely on having bright orange text on dark background, because then everything would be easy fine. But it does, and so I am still very much interested if there’s a way to fiddle with the gamma-correction as zamrate proposed. My attempts to understand the juce font-rendering ended at the point where the EdgeTable got involved.

@jules:
using the bold variant of the font didn’t make any difference.

The linux renderer doesn’t use hinting. It simply gets the glyph shapes from freetype and renders them accurately. And if the fonts on the Mac are making you suffer, please complain to Apple, because you’re looking at the output of the CoreGraphics font engine!

@steffen - in your example image, you’ve got a small, very bold font on the left that looks good, and a thin font on the right that looks bad - surely if you try using the font that was used for the left-hand-text, it’ll look ok, even in orange-on-black? You might also want to try tweaking your sizes by sub-pixel amounts, because some fonts look much better at very specific sizes where their horizontals line up better with on-screen pixels.

But, anyway, gamma-adjustment would be interesting to play with - it’d be something that would be performed in EdgeTable::iterate() - I might have a look at that and see what I can do if I have a moment.

The linux renderer doesn’t use hinting. It simply gets the glyph shapes from freetype and renders them accurately.[/quote]

Right, and the hinting is performed by Freetype right before it hands Juce the glyph shapes. Juce is getting hinted paths.

Of course the bold typeface has a big impact on the readability for those labels, but for a list of folder and file names it looks just plain wrong.

I’ll give it a try, but it’s gonna be a tedious job to do this not only for a fixed size, but for a range of possible sizes. Anyone struggling with the same issue can use this component & jucer project to help with that task. It will render a couple of rows with the same text at a range of font sizes and allows you to choose colours/typeface and to finetune each row in small steps.

/*
  ==============================================================================

    FontRenderingComponent.h
    Created: 2 Dec 2010 12:23:00am
    Author:  steffen

  ==============================================================================
*/

#ifndef __FONTRENDERINGCOMPONENT_H_C2B80DC7__
#define __FONTRENDERINGCOMPONENT_H_C2B80DC7__


#include "../JuceLibraryCode/JuceHeader.h"

class FontRenderingComponent : public Component,
                               public SliderListener,
                               public ChangeListener,
                               public ComboBoxListener,
                               public ButtonListener
{
public:
    FontRenderingComponent();

    void paint(Graphics& g);
    void resized();

    void sliderValueChanged (Slider* slider);
    void changeListenerCallback (ChangeBroadcaster* source);
    void comboBoxChanged (ComboBox* comboBoxThatHasChanged);
    void buttonClicked (Button* button);

private:

    static const int spaceBetweenRows = 10;

    static const int minFontSize = 12;
    static const int maxFontSize = 26;

    OwnedArray<Slider> sliders;
    
    ColourSelector textColourSelector;
    ColourSelector backgroundColourSelector;

    ComboBox typefaceCombo;
    TextButton boldButton;
    TextButton italicButton;
    TextButton underlinedButton;
};



#endif  // __FONTRENDERINGCOMPONENT_H_C2B80DC7__
/*
  ==============================================================================

    FontRenderingComponent.cpp
    Created: 2 Dec 2010 12:23:00am
    Author:  steffen

  ==============================================================================
*/

#include "FontRenderingComponent.h"

FontRenderingComponent::FontRenderingComponent()
{
    for (int i=minFontSize; i<=maxFontSize; i++)
    {
        Slider* slider = new Slider();

        slider->setRange(i-1, i, 0.01);
        slider->setValue(i, false);
        slider->setSliderStyle(Slider::IncDecButtons);
        slider->setIncDecButtonsMode(Slider::incDecButtonsDraggable_Vertical);
        slider->setColour(Slider::backgroundColourId, Colours::grey);
        slider->setTextBoxStyle(Slider::TextBoxLeft, false, 70, 20);
        slider->addListener(this);

        sliders.add(slider);
        addAndMakeVisible(slider);
    }


    textColourSelector.setCurrentColour(Colour::fromRGB(255, 92, 0));
    textColourSelector.addChangeListener(this);
    addAndMakeVisible(&textColourSelector);

    backgroundColourSelector.setCurrentColour(Colours::black);
    backgroundColourSelector.addChangeListener(this);
    addAndMakeVisible(&backgroundColourSelector);


    StringArray typefaceNames = Font::findAllTypefaceNames();
    for (int i=0; i<typefaceNames.size(); i++)
    {
        typefaceCombo.addItem(typefaceNames[i], i+1);
    }
    typefaceCombo.addListener(this);
    addAndMakeVisible(&typefaceCombo);


    boldButton.setButtonText("bold");
    boldButton.setClickingTogglesState(true);
    boldButton.addButtonListener(this);
    addAndMakeVisible(&boldButton);

    italicButton.setButtonText("italic");
    italicButton.setClickingTogglesState(true);
    italicButton.addButtonListener(this);
    addAndMakeVisible(&italicButton);

    underlinedButton.setButtonText("underlined");
    underlinedButton.setClickingTogglesState(true);
    underlinedButton.addButtonListener(this);
    addAndMakeVisible(&underlinedButton);
}

void FontRenderingComponent::paint( Graphics& g )
{
    static String text = T("Xaver schreibt für Wikipedia zum Spaß quälend lang über Yoga, Soja und Öko");

    g.setColour(backgroundColourSelector.getCurrentColour());
    g.fillRect(getLocalBounds());

    g.setColour(textColourSelector.getCurrentColour());
    int fontStyleFlags = Font::plain;
    if (boldButton.getToggleState()) fontStyleFlags |= Font::bold;
    if (italicButton.getToggleState()) fontStyleFlags |= Font::italic;
    if (underlinedButton.getToggleState()) fontStyleFlags |= Font::underlined;

    int y = 5;
    for (int i=0; i<sliders.size(); i++)
    {
        int fontSize = minFontSize + i;
        int rowHeight = fontSize + spaceBetweenRows;
        float subpixelSize = (float)sliders[i]->getValue();
        Font font (typefaceCombo.getText(), subpixelSize, fontStyleFlags);
        g.setFont(font);
        
        g.drawText(text.toLowerCase(), 85, y, getWidth() - 60, fontSize, Justification::topLeft, false);
        y+= rowHeight;

        g.drawText(text.toUpperCase(), 85, y, getWidth() - 60, fontSize, Justification::topLeft, false);
        y+= rowHeight;
    }

}

void FontRenderingComponent::resized()
{
    int y = 5;
    for (int i=0; i<sliders.size(); i++)
    {
        int fontSize = minFontSize + i;

        sliders[i]->setBounds(2, y , 75, 2*fontSize + spaceBetweenRows);
        y+= (minFontSize+i+spaceBetweenRows) * 2;
    }

    int selectorSize = 200;
    int padding = 5;

    backgroundColourSelector.setBounds(getWidth() - selectorSize - padding, padding, selectorSize, selectorSize);
    textColourSelector.setBounds(getWidth() - 2*(selectorSize + padding), padding, selectorSize, selectorSize);

    typefaceCombo.setBounds(backgroundColourSelector.getX(), selectorSize + 2*padding, selectorSize, 20);
    boldButton.setBounds(backgroundColourSelector.getX(), typefaceCombo.getY() + 25, selectorSize, 20);
    italicButton.setBounds(backgroundColourSelector.getX(), typefaceCombo.getY() + 50, selectorSize, 20);
    underlinedButton.setBounds(backgroundColourSelector.getX(), typefaceCombo.getY() +75, selectorSize, 20);

    repaint();
}

void FontRenderingComponent::sliderValueChanged( Slider* slider )
{
    repaint();
}

void FontRenderingComponent::changeListenerCallback( ChangeBroadcaster* source )
{
    repaint();
}

void FontRenderingComponent::comboBoxChanged( ComboBox* comboBoxThatHasChanged )
{
    repaint();
}

void FontRenderingComponent::buttonClicked( Button* button )
{
    repaint();
}

Turns out that also with fonts like Verdana it’s quite difficult to find any subpixel size below 16px where letters like m/n/u have vertical lines that look equally wide/bright. Even tiny changes in the font size can totally change the appearance of any of these letters, or suddenly make it look bigger than others. It’s much better with dark fonts on a light background, so I’m still hoping there’s a way to around this related to gamma-correction.

If you don’t find the time to look for a general solution, I’ll be grateful for any hints where I could try to tweak EdgeTable::iterate() for my specific use case.

On a related note, I was amused about all the pangrams people create. Like the one from I ended up using that roughly translates to “Just for fun, Xaver writes agonizingly long on wikipedia about yoga, soy and eco”

steffen, just wondering, why don’t you go the Freetype route? I mean, hinting completely solves this problem. I was previously using agg (antigrain c++ rendering library) and freetype together and rendering Helvetica Neue at 8pt size, and it looked as good as a bitmap font.

If you think gamma correction is going to come anywhere close to the readability performance of actually using the hinting information found inside the font, well then you got another thing coming to you!

Another benefit of using Freetype on all platforms is consistent pixel layout for gui elements.

I really hate these font arguments. It does seem to really get people worked up, myself included, even though I try hard not to.

But let me just try to explain why I can’t add hinting to juce:

Historically, UIs were fixed-size, horizontal, fixed-scale, low-resolution and stayed pretty still. Hinting works really well in this context.

In the future, (and as we’re just starting to see in things like the iPhone), UIs will be high-res, arbitrarily scaled, may be tilted to weird angles, and it’s becoming normal to expect objects to zoom or warp smoothly between different sizes. All of these factors make it either impossible or pointless to use hinting. And juce does all of this stuff.

When you hint a font, it changes its size, and affects the width and layout of the characters. So if you draw a paragraph of text, and try to smoothly zoom it by gradually increasing its size by sub-pixel amounts, hinting will make the text jump around, words will hop between different lines, and it’ll look like the text is having a fit rather than being smoothly rescaled. If you try to rotate or shear your text by a tiny amount from the horizontal, then what happens to the hinting? In juce, you can create a text layout, and then render it via a graphics context in a component. But the graphics context may be applying some kind of affine transform - so if the original layout positions were hinted, what happens to the text? And if they weren’t hinted, how could it possibly add hinting it when it draws it without re-arranging all your carefully created layout?

In your own example, you might find that hinting works at the moment, but what happens when monitor resolutions increase, and your UI needs to be bigger? If you rescale as a bitmap, it’ll look blocky. If you rescale using a hinted font, you have no control over exactly how big the font will turn out, so at different scales your text might no longer fit correctly in the space you’ve given it.

These are the sort of reasons why OSX stopped using hinting a long time ago. In Windows 7, they’re phasing it out with Direct2D, and IE9 has finally stopped hinting. None of the modern browsers are hinted. The tide is against it, and as someone who’s written a rendering engine, I totally understand why!

Ok, that’s enough clutter on this thread! I’m going to lock it - feel free to carry on arguing on a fresh thread if you want to - I may or may not join in!