Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Bad font hinting / quality with Agg renderer #719

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

Merged
merged 2 commits into from
Feb 29, 2012

Conversation

mdboom
Copy link
Member

@mdboom mdboom commented Feb 27, 2012

When using the agg backend to create PNGs, fonts are poorly hinted, and always look blurry. Setting text.hinting = True has no effect.

The below script creates two images that demonstrate the problem. It generates some text in multiple sizes, with both the agg and the cairo backends. While the progression of sizes generally looks smoother with the agg backend, there seems to be no way to replicate the crispness of the cairo backend, particularly around sizes 9, 10, 11, and 12.

I have copies of the images, and zoomed-in versions for comparision, available at
http://jim.sh/~jim/tmp/matplotlib-font

# Test rendering of different font sizes with Cairo vs. Agg backend

import sys, os
if "DISPLAY" in os.environ:
    del os.environ["DISPLAY"]

for backend in [ 'cairo', 'agg' ]:
    # Reload with proper backend
    print "Rendering with " + backend
    for module in list(sys.modules):
        if module.startswith(('matplotlib','pylab')):
            sys.modules.pop(module)
    import matplotlib
    matplotlib.use(backend)
    import pylab

    # This has no effect, but try it anyway
    matplotlib.rcParams['text.hinting'] = True

    # Make plot
    fig = pylab.figure(figsize=[2,5])
    ax = fig.add_subplot(1,1,1)
    ax.set_frame_on(False)
    ax.axes.get_xaxis().set_visible(False)
    ax.axes.get_yaxis().set_visible(False)
    ax.set_xlim([0, 1])
    ax.set_ylim([15, 0])
    pylab.title(backend)

    for size in range(1,25):
        ax.text(0.1, (size ** 1.6) / 10, "hello " + str(size), 
                fontdict = { "size" : size, "family" : "arial" })
    pylab.savefig("test-" + backend + ".png", format="png")
    pylab.clf()

@ghost ghost assigned mdboom Feb 27, 2012
The hinting factor is the amount of softness in hinting is applied in
the horizontal direction.  For years, matplotlib has hinted to 1/8
pixels in the horizontal direction and whole pixels in the vertical
direction.  This results in text that is often more
legible (particularly at smaller sizes) than standard 1:1 hinting.
This is based on idea from this paper from the author of Agg:

    http://www.antigrain.com/research/font_rasterization/

However, sometimes the user may want full-on hinting, so this value is
now tweakable using the `text.hinting_factor` rcParam.
@mdboom
Copy link
Member

mdboom commented Feb 27, 2012

What you're seeing is the effect of "soft hinting" where it hints to subpixels in the x direction. Years ago we did a sort of survey on the mailing list of which kind of hinting looked best and that's what we determined. Of course, it doesn't have to be hardcoded, so I've attached some changes that make the amount of softness tweakable with an rcParam. Can you try this out (setting "text.hinting_factor" to 1) and let me know if this resolves your issue? I'm hesitant to change the default as many users (and the testing framework) are relying on the present behavior.

Attached is a pull request that makes the "hinting factor" tweakable at runtime.

The hinting factor is the amount of softness in hinting is applied in
the horizontal direction.  For years, matplotlib has hinted to 1/8
pixels in the horizontal direction and whole pixels in the vertical
direction.  This results in text that is often more
legible (particularly at smaller sizes) than standard 1:1 hinting.
This is based on idea from this paper from the author of Agg:

    http://www.antigrain.com/research/font_rasterization/

However, sometimes the user may want full-on hinting, so this value is
now tweakable using the `text.hinting_factor` rcParam.

@jimparis
Copy link
Author

Thanks for the patch! It does improve things, but the hinting still leaves a bit to be desired, at least compared to the Cairo backend. I've updated the images at http://jim.sh/~jim/tmp/matplotlib-font to show how the text.hinting_factor = 1 changes things. I also included a plot that attempts to force hinting with text.hinting = True, but that doesn't seem to make a difference. Notice how vertical letters like the Ls are now sharper horizontally, but still don't fully snap to pixels like in the Cairo case.

Regarding the default, I agree that the present behavior shouldn't be changed at this point. I'm fine with setting a variable in my own scripts.

@jimparis
Copy link
Author

I think I figured it out: currently, text.hinting can choose between the FreeType options LOAD_NO_HINTING or LOAD_FORCE_AUTOHINT. The option I need is LOAD_DEFAULT -- use the font's native hinting if available. If I hack lib/matplotlib/backends/backend_agg.py so that _get_hinting_flag always returns LOAD_DEFAULT, then the Agg font rendering matches Cairo exactly.

Maybe text.hinting could be extended in a compatible way, e.g.:

{
False: LOAD_NO_HINTING,
True: LOAD_FORCE_AUTOHINT,
"auto": LOAD_FORCE_AUTOHINT,
"native": LOAD_NO_AUTOHINT,
"either": LOAD_DEFAULT,
}

@mdboom
Copy link
Member

mdboom commented Feb 28, 2012

Ok. I've incorporated this.

@jimparis
Copy link
Author

Works perfectly. Now text.hinting_factor: 1 and text.hinting: native gives me exactly what I wanted. Thanks so much!

@mdboom
Copy link
Member

mdboom commented Feb 29, 2012

Great! Merging...

mdboom added a commit that referenced this pull request Feb 29, 2012
Bad font hinting / quality with Agg renderer
@mdboom mdboom merged commit d9186fe into matplotlib:master Feb 29, 2012
@mdboom mdboom deleted the hinting branch March 3, 2015 18:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants