Subject: bug#35781: 27.0.50; [PATCH] Improve font display on Cairo builds

From: Kévin Le Gouguec <[email-masked]>
Date: Fri, 17 May 2019 19:39:12 +0200

Hi,

The attached patch makes fonts in the Cairo build more readable[1] on
some setups I use.

diff --git a/src/ftcrfont.c b/src/ftcrfont.c
index e7c73eac4d..51890dc18c 100644
--- a/src/ftcrfont.c
+++ b/src/ftcrfont.c
@@ -146,6 +146,7 @@ ftcrfont_open (struct frame *f, Lisp_Object entity, int pixel_size)
       cairo_matrix_init_scale (&font_matrix, pixel_size, pixel_size);
       cairo_matrix_init_identity (&ctm);
       cairo_font_options_t *options = cairo_font_options_create ();
+      cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_SLIGHT);
       ftcrfont_info->cr_scaled_font =
 	cairo_scaled_font_create (font_face, &font_matrix, &ctm, options);
       cairo_font_face_destroy (font_face);

More context: I started trying out builds --with-cairo recently to
check out Emacs's support of multicolor fonts.  While these fonts seem
to work fine (emojis! 🙌), I found that the fonts for regular text
look… sharper?  In an aggressive-and-too-contrasted kind of way?

This screenshot shows the difference on one of my desktops[2]:

Each frame is a different instance of 'emacs -Q'.  The left one is
configured without Cairo, the middle one --with-cairo *with my patch
applied*, the right one --with-cairo without my patch.

Same visual difference on my laptop[3]:

Each frame is a different instance of 'emacs -Q -fn "DejaVu Sans
Mono-9"'[4].  The left frame is configured --with-cairo, without my
patch; the top-right one is without cairo, the bottom-right
--with-cairo with my patch.

On the third setup I could test[5], all three builds look the same
AFAICT; no idea what this can be attributed to.

Does this patch look like something that can be applied
unconditionally?

- CAIRO_HINT_STYLE_SLIGHT has been available from version 1.0, and we
  require 1.12, so AFAICT the setting can be used without checking for
  any CAIRO_… feature.

- On the other hand, maybe this setting should be conditioned by a
  user option: as far as I could test, …_SLIGHT gives the "best"
  visual result[6], but maybe this is specific to my setups.

  I guess that a customizable option would need to specify a :set
  function tasked with making a few Cairo calls for the change to be
  effective without restarting; I have not spent much time looking at
  ftcrfont.c and its users yet, but I imagine that this hypothetical
  setter function should do something along the lines of:

  1. retrieve all 'struct font_info' objects created with
     ftcrfont_open,
  2. call cairo_scaled_font_get_font_options on their 'cr_scaled_font'
     field,
  3. call …set_hint_style on the returned options.

  (… This is absolutely untested guesswork.)

Caveat: I do not know the first thing about font rendering (or any
kind of rendering for that matter), nor font hinting.  This patch is
the result of me googling around for "cairo smooth font" and trying
out random snippets to tweak cairo_font_options_* knobs.  Some
snippets I have seen call …set_{antialias,subpixel_order} in
conjunction with …set_hint_style; as far as I tested, the former two
have no impact, so I left them out of the patch.

Let me know if I should provide more information (font configuration,
results after tweaking other settings… though I cannot access my
desktops until Monday), and thank you for your time!

[1] Objectively-speaking, the patch merely makes fonts "visually
    equivalent to the non-Cairo build"; whether it makes them "more
    readable" is a matter of taste I guess.

[2] OS: Xubuntu 16.04 (libcairo2: 1.14.6)
    xrandr digest: DP-2 1920x1080 509mm x 286mm
    emacs-repository-version: … probably a commit on master from these
                              past two weeks

[3] OS: Debian stretch (libcairo2: 1.14.8)
    xrandr digest: LVDS1 1024x600 230mm x 140mm
    emacs-repository-version: e0ee41d155b210327eb9c9ad5334f80ed59439f4

[4] emacs -Q defaults to a bigger font size than what I use, and the
    "sharpness" difference is less visible then.

[5] OS: openSUSE Tumbleweed (cairo: 1.16.0)
    xrandr digest: DVI-I-0 1680x1050 474mm x 296mm
    emacs-repository-version: … probably a commit on master from these
                              past two weeks

[6] Compiling with any other enum value looked equivalent to not
    setting this option at all IIRC.

In GNU Emacs 27.0.50 (build 3, i686-pc-linux-gnu, GTK+ Version 3.22.11)
 of 2019-05-13 built on nc10-laptop
Repository revision: d2d4916046e31e46598f0a0edbc65e75b8cb4cc3
Repository branch: master
Windowing system distributor 'The X.Org Foundation', version 11.0.11902000
System Description: BunsenLabs GNU/Linux 9.8 (Helium)

Recent messages:
nnimap read 178k from imap.gmail.com (initial sync of 11 groups; please wait)
nnimap read 222k from imap.gmail.com (initial sync of 11 groups; please wait)
nnimap read 257k from imap.gmail.com (initial sync of 11 groups; please wait)
Reading active file from archive via nnfolder...done
Reading active file via nndraft...done
Checking new news...done
nnimap read 0k from imap.gmail.com
No more unseen articles
Mark set
Quit [2 times]

Configured using:
 'configure --with-xwidgets'

Configured features:
XPM JPEG TIFF GIF PNG RSVG IMAGEMAGICK SOUND GPM DBUS GSETTINGS GLIB
NOTIFY INOTIFY ACL LIBSELINUX GNUTLS LIBXML2 FREETYPE M17N_FLT LIBOTF
XFT ZLIB TOOLKIT_SCROLL_BARS GTK3 X11 XDBE XIM THREADS XWIDGETS JSON
PDUMPER LCMS2 GMP

Important settings:
  value of $LANG: en_US.UTF-8
  locale-coding-system: utf-8-unix

Major mode: Summary

Minor modes in effect:
  global-magit-file-mode: t
  magit-auto-revert-mode: t
  global-git-commit-mode: t
  async-bytecomp-package-mode: t
  shell-dirtrack-mode: t
  show-paren-mode: t
  minibuffer-depth-indicate-mode: t
  icomplete-mode: t
  global-page-break-lines-mode: t
  page-break-lines-mode: t
  electric-pair-mode: t
  diff-hl-flydiff-mode: t
  global-diff-hl-mode: t
  delete-selection-mode: t
  tooltip-mode: t
  global-eldoc-mode: t
  electric-indent-mode: t
  mouse-wheel-mode: t
  file-name-shadow-mode: t
  global-font-lock-mode: t
  font-lock-mode: t
  blink-cursor-mode: t
  auto-composition-mode: t
  auto-encryption-mode: t
  auto-compression-mode: t
  buffer-read-only: t
  column-number-mode: t
  line-number-mode: t
  transient-mark-mode: t

Load-path shadows:
None found.

Features:
(shadow emacsbug rect cl-print debug backtrace magit-extras magit-patch
magit-subtree magit-ediff ediff-merg ediff-wind ediff-diff ediff-mult
ediff-help ediff-init ediff-util ediff org-indent org-rmail org-mhe
org-irc org-info org-gnus org-docview doc-view image-mode org-bibtex
bibtex org-bbdb org-w3m org-element avl-tree org org-macro org-footnote
org-pcomplete org-list org-faces org-entities org-version ob-emacs-lisp
ob ob-tangle org-src ob-ref ob-lob ob-table ob-keys ob-exp ob-comint
ob-core ob-eval org-compat org-macs org-loaddefs cal-menu calendar
cal-loaddefs bug-reference magit-submodule magit-obsolete magit-blame
magit-stash magit-reflog magit-bisect magit-push magit-pull magit-fetch
magit-clone magit-remote magit-commit magit-sequence magit-notes
magit-worktree magit-tag magit-merge magit-branch magit-reset
magit-files magit-refs magit-status magit magit-repos magit-apply
magit-wip magit-log which-func imenu magit-diff smerge-mode magit-core
magit-autorevert autorevert filenotify magit-margin magit-transient
magit-process magit-mode transient git-commit magit-git magit-section
log-edit pcvs-util add-log with-editor async-bytecomp async server
url-util sendmail nnir eieio-opt speedbar sb-image ezimage dframe
gnus-fun shr-color color shr svg dom browse-url sort gnus-cite
mm-archive mail-extr gnus-async gnus-bcklg qp gnus-ml nndraft nnmh
nnfolder utf-7 epa-file gnutls network-stream nsm gnus-agent gnus-srvr
gnus-score score-mode nnvirtual gnus-msg gnus-art mm-uu mml2015 mm-view
mml-smime smime dig mailcap nntp gnus-cache gnus-sum gnus-group
gnus-undo gnus-start gnus-cloud nnimap nnmail mail-source utf7 netrc
nnoo parse-time gnus-spec gnus-int gnus-range gnus-win gnus nnheader
find-func help-fns radix-tree conf-mode magit-utils crm dash jka-compr
pulse find-dired ffap etags fileloop generator xref cus-edit wid-edit
whitespace notifications dbus xml thingatpt noutline outline view
message rmc puny dired dired-loaddefs format-spec rfc822 mml mml-sec epa
derived epg gnus-util rmail rmail-loaddefs text-property-search
time-date mm-decode mm-bodies mm-encode mail-parse rfc2231 rfc2047
rfc2045 mm-util ietf-drums mail-prsvr mailabbrev mail-utils gmm-utils
mailheader flyspell ispell executable shell pcomplete misearch
multi-isearch vc-mtn vc-hg cc-mode cc-fonts cc-guess cc-menus cc-cmds
cc-styles cc-align cc-engine cc-vars cc-defs footnote rx vc-git vc-bzr
vc-src vc-sccs vc-svn vc-cvs vc-rcs project delight advice
eighters-theme quail cl-extra help-mode rg rg-ibuffer rg-result wgrep-rg
wgrep s rg-history rg-header rg-compat ibuf-ext ibuffer ibuffer-loaddefs
grep compile comint ansi-color ring edmacro kmacro disp-table paren
mb-depth icomplete page-break-lines elec-pair diff-hl-flydiff diff
diff-hl vc-dir ewoc vc vc-dispatcher diff-mode easy-mmode delsel
cus-start cus-load mule-util tex-site info package easymenu epg-config
url-handlers url-parse auth-source cl-seq eieio eieio-core cl-macs
eieio-loaddefs password-cache json subr-x map url-vars seq byte-opt gv
bytecomp byte-compile cconv cl-loaddefs cl-lib tooltip eldoc electric
uniquify ediff-hook vc-hooks lisp-float-type mwheel term/x-win x-win
term/common-win x-dnd tool-bar dnd fontset image regexp-opt fringe
tabulated-list replace newcomment text-mode elisp-mode lisp-mode
prog-mode register page menu-bar rfn-eshadow isearch timer select
scroll-bar mouse jit-lock font-lock syntax facemenu font-core
term/tty-colors frame cl-generic cham georgian utf-8-lang misc-lang
vietnamese tibetan thai tai-viet lao korean japanese eucjp-ms cp51932
hebrew greek romanian slovak czech european ethiopic indian cyrillic
chinese composite charscript charprop case-table epa-hook jka-cmpr-hook
help simple abbrev obarray minibuffer cl-preloaded nadvice loaddefs
button faces cus-face macroexp files text-properties overlay sha1 md5
base64 format env code-pages mule custom widget hashtable-print-readable
backquote threads dbusbind inotify lcms2 dynamic-setting
system-font-setting font-render-setting xwidget-internal move-toolbar
gtk x-toolkit x multi-tty make-network-process emacs)

Memory information:
((conses 8 510108 60620)
 (symbols 24 35458 1)
 (strings 16 125803 16010)
 (string-bytes 1 4146628)
 (vectors 8 59808)
 (vector-slots 4 1472134 107810)
 (floats 8 508 454)
 (intervals 28 32090 73)
 (buffers 564 69))