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

Poor on-screen font antialiasing/smoothing in mplcairo #55

Open
AgilentGCMS opened this issue Dec 28, 2023 · 18 comments
Open

Poor on-screen font antialiasing/smoothing in mplcairo #55

AgilentGCMS opened this issue Dec 28, 2023 · 18 comments

Comments

@AgilentGCMS
Copy link

I'm trying to switch to using the mplcairo backend because it does certain things better than the MacOSX backend, especially when colormaps have transparency. However, I've noticed that the font smoothing, at least for plots displayed on the screen, is noticeably poorer. The code below offers a MWE:

import numpy as np
from matplotlib import pyplot as plt

fig = plt.figure()
plt.plot(np.linspace(0,1,10), np.linspace(0,1,10), '-o')
plt.setp(plt.gca().get_xticklabels(), size=14)
plt.setp(plt.gca().get_yticklabels(), size=14)

With the mplcairo backend, the tick labels look jagged and not smooth, as below.
Screenshot 2023-12-28 at 4 32 03 PM
Whereas with the MacOSX backend gives me noticeably smoother fonts.
Screenshot 2023-12-28 at 4 33 00 PM

Everything is identical for the production of the two figures except the backend, which I switch in .matplotlibrc. Am I missing something? Is there a way to get mplcairo to render fonts with proper smoothing?

import mplcairo
mplcairo.get_versions()
{'python': '3.11.6 (main, Oct 12 2023, 21:46:57) [Clang 15.0.0 (clang-1500.0.40.1)]',
 'mplcairo': '0.5.post32+ge771c74',
 'matplotlib': '3.8.0',
 'cairo': '1.17.6 @ /Users/sbasu1/packages/macports/lib/libcairo.2.dylib',
 'freetype': '2.13.2 @ /Users/sbasu1/packages/macports/lib/libfreetype.6.dylib',
 'pybind11': '2.11.1',
 'raqm': None,
 'harfbuzz': None}
@anntzer
Copy link
Collaborator

anntzer commented Jan 2, 2024

Unfortunately I cannot reproduce the issue. What is the value of rcParams["text.antialiased"]? If you apply the patch below, what antialiasing value gets printed?

diff --git i/ext/_util.cpp w/ext/_util.cpp
index 55ed9d2..57da84d 100644
--- i/ext/_util.cpp
+++ w/ext/_util.cpp
@@ -856,6 +856,7 @@ void adjust_font_options(cairo_t* cr)
   // The hint style is not set here: it is passed directly as load_flags to
   // cairo_ft_font_face_create_for_ft_face.
   cairo_set_font_options(cr, options);
+  py::print(cairo_font_options_get_antialias(options));
   cairo_font_options_destroy(options);
 }
 

Also, can you try with cairo 1.18?

@AgilentGCMS
Copy link
Author

AgilentGCMS commented Jan 3, 2024

from matplotlib import pyplot as plt
plt.rcParams['text.antialiased']
True

I would be happy to apply the patch you mentioned, but could you tell me how? Due to this issue, I can't install from a source tarball. Instead, I do

pip install -v git+https://github.com/anntzer/mplcairo

So where/how do I apply that patch?

OK, I will try updating cairo.

@anntzer
Copy link
Collaborator

anntzer commented Jan 3, 2024

To test the patch: Clone the repository, apply the patch, then pip install . from the root directory.

Also, perhaps try the module://mplcairo.qt or module://mplcairo.tk backends? (I think you're on the mplcairo.macosx backend right now; perhaps there are some issues with HiDPI.)

@AgilentGCMS
Copy link
Author

With the patch, antialias_t.SUBPIXEL gets printed. I tried module://mplcairo.tk and got this.

Screenshot 2024-01-03 at 8 28 05 AM

So, same issue with font smoothing as mplcairo.macosx.

@AgilentGCMS
Copy link
Author

I should also add that this seems to be a problem only for font sizes at the smaller end. In the above example, when I replace with size=16, I get this, which is smooth enough.

Screenshot 2024-01-03 at 8 32 35 AM

@anntzer
Copy link
Collaborator

anntzer commented Jan 3, 2024

Do you have anything special in your matplotlibrc? Does the issue remain if you remove all its contents except for the backend setting, or if you call matplotlib.rcdefaults() first?

@AgilentGCMS
Copy link
Author

AgilentGCMS commented Jan 3, 2024

I do have some stuff in my .matplotlibrc to set the font family and color order for plots, so I replaced all that with a .matplotlibrc where the only non-commented line is backend : module://mplcairo.macosx. This is the result.
Screenshot 2024-01-03 at 11 46 59 AM

@AgilentGCMS
Copy link
Author

And with the same "blank" .matplotlibrc, if I switch back to backend : MacOSX this is what I get.
Screenshot 2024-01-03 at 11 51 12 AM

@anntzer
Copy link
Collaborator

anntzer commented Jan 4, 2024

Do you also have the problem with mathtext? (plt.text(.5, .5, "$1.23$" at whatever relevant size)

@AgilentGCMS
Copy link
Author

With the following (to get a few different sized mathtext texts)

plt.text(.6, .2, r"$1.23$", ha='center', va='center', size=14)
plt.text(.6, .1, r"$1.23$", ha='center', va='center', size=12)
plt.text(.6, .3, r"$1.23$", ha='center', va='center', size=10)

I get the following, where the mathtext instances are smooth even down to size 10.
Screenshot 2024-01-04 at 9 42 08 AM
I should say here that I do have raqm and harfbuzz installed on the system, so I'm not sure why mplcairo does not use those (according to mplcairo.get_versions()).

@anntzer
Copy link
Collaborator

anntzer commented Jan 4, 2024

Hmm, that is interesting. What happens if you try to force loading raqm with mplcairo.set_options(raqm=True)? What if you hard-code the path of the raqm dylib in load_raqm() (in _raqm.cpp), replacing the whole initial part (up to just before #define LOAD_API) with raqm::_handle = os::dlopen("/path/to/your/libraqm.dylib")?

@AgilentGCMS
Copy link
Author

OK, this is interesting and slightly puzzling. I have raqm installed via macports,

$ ls -l /Users/sbasu1/packages/macports/lib/libraqm.dylib
lrwxr-xr-x 1 sbasu1 staff 15 2024 Jan 04 01:32:15 /Users/sbasu1/packages/macports/lib/libraqm.dylib -> libraqm.0.dylib

and that path is both in LD_LIBRARY_PATH and LD_RUN_PATH,

$ env | grep LD_
LD_LIBRARY_PATH=/Users/sbasu1/packages/lib:/Users/sbasu1/packages/macports/lib:
LD_RUN_PATH=/Users/sbasu1/packages/lib:/Users/sbasu1/packages/macports/lib:

Yet, when I try to force load as follows,

In [1]: import mplcairo

In [2]: mplcairo.set_options(raqm=True)
---------------------------------------------------------------------------
OSError                                   Traceback (most recent call last)
Cell In[2], line 1
----> 1 mplcairo.set_options(raqm=True)

OSError: dlopen(libraqm.dylib, 0x0001): tried: '/opt/intel/compilers_and_libraries_2019.5.281/mac/compiler/lib/libraqm.dylib' (no such file), '/opt/intel/compilers_and_libraries_2019.5.281/mac/compiler/lib/intel64/libraqm.dylib' (no such file), '/opt/intel/compilers_and_libraries_2019.5.281/mac/tbb/lib/libraqm.dylib' (no such file), '/opt/intel/compilers_and_libraries_2019.5.281/mac/compiler/lib/libraqm.dylib' (no such file), '/opt/intel/compilers_and_libraries_2019.5.281/mac/mkl/lib/libraqm.dylib' (no such file), 'libraqm.dylib' (no such file), '/System/Volumes/Preboot/Cryptexes/OSlibraqm.dylib' (no such file), '/usr/lib/libraqm.dylib' (no such file, not in dyld cache), 'libraqm.dylib' (no such file), '/usr/local/lib/libraqm.dylib' (no such file), '/usr/lib/libraqm.dylib' (no such file, not in dyld cache)

It's not even looking in LD_LIBRARY_PATH and LD_RUN_PATH!

@AgilentGCMS
Copy link
Author

So, I hardcoded the path to libraqm.dylib as you suggested, and indeed mplcairo now knows about raqm (and also harfbuzz, somehow, although I did no hardcoding for that),

In [1]: import mplcairo

In [2]: mplcairo.get_versions()
Out[2]:
{'python': '3.11.7 (main, Jan  3 2024, 08:15:29) [Clang 15.0.0 (clang-1500.1.0.2.5)]',
 'mplcairo': '0.5.post32+ge771c74',
 'matplotlib': '3.8.2',
 'cairo': '1.17.6 @ /Users/sbasu1/packages/macports/lib/libcairo.2.dylib',
 'freetype': '2.13.2 @ /Users/sbasu1/packages/macports/lib/libfreetype.6.dylib',
 'pybind11': '2.11.1',
 'raqm': '0.10.1 @ /Users/sbasu1/packages/macports/lib/libraqm.0.dylib',
 'harfbuzz': '8.3.0 @ /Users/sbasu1/packages/macports/lib/libharfbuzz.0.dylib'}

However, the tick label fonts are still not smooth
Screenshot 2024-01-07 at 2 21 12 PM

@anntzer
Copy link
Collaborator

anntzer commented Jan 7, 2024

Thanks for trying. Not any bright idea right now...

@Artoria2e5
Copy link

Artoria2e5 commented Jan 31, 2024

My silly guess is that

  • the size-dependent smoothness at size 16 has something to do with the font's built-in bitmaps being used (macOS, as far as I know, doesn't like to use it at all)
  • the different threshold for plt.text is because it handles retina screen scale (DisplayFramebufferScale) differently

... I think there's something in fontconfig that might turn off embeddedbitmaps -- if it works, my hunch would be in the right direction.

@anntzer
Copy link
Collaborator

anntzer commented Feb 1, 2024

Thanks for the suggestion. Do you know how to test the hypothesis?

@Artoria2e5
Copy link

Having a bit of an issue reproducing on my end (but don't worry, anntzer also failed to repro). Probably something to do with how homebrew cairo and the whole stack below is configured differently compared to macports.

You should be able to (hopefully) turn off those bitmaps with a new file. Write the following into a new file called /opt/local/etc/fonts/conf.d/95-noemb.conf:

<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "urn:fontconfig:fonts.dtd">
<fontconfig>
  <match target="font">
    <edit name="embeddedbitmap" mode="assign">
      <bool>false</bool>
    </edit>
  </match>
</fontconfig>

@AgilentGCMS
Copy link
Author

@Artoria2e5 I tried your 95-noemb.conf and no luck, the tick labels are still not smooth. This is a screenshot.
Screenshot 2024-02-20 at 10 44 36 AM
Note that if I save the figure with plt.savefig('jagged_ticklabels.png') they are rendered smooth for some DPI's but not all. I would have thought that below a certain DPI the fonts would have been jagged, and smooth above that threshold. However, what I see is that at 90 DPI, they're smooth.
jagged_ticklabels_90
At 100 DPI, they're jagged.
jagged_ticklabels_100
And then at 120 DPI, they're smooth again.
jagged_ticklabels_120
Does this provide a clue, perhaps?

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

No branches or pull requests

3 participants