unofficial mirror of bug-gnu-emacs@gnu.org 
 help / color / mirror / code / Atom feed
* bug#35871: 27.0.50; [PATCH] Fix SVG transparency with Cairo
@ 2019-05-23 20:05 Kévin Le Gouguec
  2019-05-24  0:02 ` YAMAMOTO Mitsuharu
  0 siblings, 1 reply; 10+ messages in thread
From: Kévin Le Gouguec @ 2019-05-23 20:05 UTC (permalink / raw)
  To: 35871

[-- Attachment #1: Type: text/plain, Size: 368 bytes --]

Hello,

While trying out the Cairo build, I noticed that some SVG images did
not look as smooth as in the "regular" build.  More specifically,
semi-transparent parts of SVG images (when the alpha value is neither
0% nor 100%) did not seem to be displayed properly.

This is especially noticeable on the splash screen (see attached
screenshot) and when starting Gnus.


[-- Attachment #2: cairo-svg.png --]
[-- Type: image/png, Size: 154308 bytes --]

[-- Attachment #3: Type: text/plain, Size: 1624 bytes --]


Each window in the screenshot was obtained with:

- ./src/emacs -Q -rv,
- C-h C-a (for some reason the splash screen wasn't displayed at
  startup)
- C-x 2, moving to the *scratch* buffer, and displaying
  - system-configuration-options,
  - system-configuration-features,
  - emacs-repository-version.

I could reproduce this on OpenSUSE Tumbleweed, Debian stretch and
Xubuntu 16.04.

I looked at the SVG section in src/image.c, in particular at any
USE_CAIRO section; I stumbled on this bit:

> if (iconptr[3] == 0)
>   *dataptr = bgcolor;
> else
>   *dataptr = (iconptr[0] << 16)
>     | (iconptr[1] << 8)
>     | iconptr[2]
>     | (iconptr[3] << 24);

Looking at create_cairo_image_surface, I saw that we use
CAIRO_FORMAT_ARGB32, which according to the documentation works with
"premultiplied alpha" values.

Quoting <https://www.cairographics.org/manual/cairo-Image-Surfaces.html#cairo-format-t>:

> CAIRO_FORMAT_ARGB32      each pixel is a 32-bit quantity, with alpha
>                          in the upper 8 bits, then red, then green,
>                          then blue. The 32-bit quantities are stored
>                          native-endian. Pre-multiplied alpha is
>                          used. (That is, 50% transparent red is
>                          0x80800000, not 0x80ff0000.) (Since 1.0)

AFAICU, this means that Cairo expects the RGB values to be scaled down
according to the alpha value.  Having no idea whether this was the issue
(I had no idea whether gdk-pixbuf gave us premultiplied alpha or not), I
added that multiplication and AFAICT, this does solve the issue (see
other screenshot).


[-- Attachment #4: cairo-svg-fixed.png --]
[-- Type: image/png, Size: 161037 bytes --]

[-- Attachment #5: Type: text/plain, Size: 140 bytes --]


NB: the commit named "a7c9365f7956d9d7a089a2f161d2b9d06fc91d58" in the
left screenshot is the one which contains my patch, attached here:


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #6: 0001-Fix-transparency-handling-in-SVG-images-with-Cairo.patch --]
[-- Type: text/x-patch, Size: 1842 bytes --]

From a7c9365f7956d9d7a089a2f161d2b9d06fc91d58 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?K=C3=A9vin=20Le=20Gouguec?= <kevin.legouguec@gmail.com>
Date: Thu, 23 May 2019 20:48:35 +0200
Subject: [PATCH] Fix transparency handling in SVG images with Cairo

We create cairo_surface_t objects with CAIRO_FORMAT_ARGB32, which is
meant to be used with "premultiplied alpha" values, i.e. RGB values
must be scaled down according to the alpha value.

Some references:

https://www.cairographics.org/manual/cairo-Image-Surfaces.html#cairo-format-t
https://wiki.gnome.org/Attic/GtkCairoIntegration

* src/image.c (gdk_pixbuf_to_argb32): New function to convert
GdkPixbuf to Cairo ARGB.
(svg_load_image): Use it.
---
 src/image.c | 16 ++++++++++++----
 1 file changed, 12 insertions(+), 4 deletions(-)

diff --git a/src/image.c b/src/image.c
index 57b405f6db..7d9aea6f3e 100644
--- a/src/image.c
+++ b/src/image.c
@@ -1084,6 +1084,17 @@ emacs_color_to_argb32 (Emacs_Color *ec)
 	  | ((ec->green / 256) << 8) | (ec->blue / 256));
 }
 
+static uint32_t
+gdk_pixbuf_to_argb32 (const guchar iconptr[4])
+{
+  /* Convert to pre-multiplied alpha. */
+  guchar a = iconptr[3];
+  return a<<24
+    | ((iconptr[0]*a / 256) << 16)
+    | ((iconptr[1]*a / 256) << 8)
+    | (iconptr[2]*a / 256);
+}
+
 static uint32_t
 get_spec_bg_or_alpha_as_argb (struct image *img,
                               struct frame *f)
@@ -9271,10 +9282,7 @@ svg_load_image (struct frame *f, struct image *img, char *contents,
             if (iconptr[3] == 0)
               *dataptr = bgcolor;
             else
-              *dataptr = (iconptr[0] << 16)
-                | (iconptr[1] << 8)
-                | iconptr[2]
-                | (iconptr[3] << 24);
+              *dataptr = gdk_pixbuf_to_argb32 (iconptr);
 
             iconptr += 4;
             ++dataptr;
-- 
2.21.0


[-- Attachment #7: Type: text/plain, Size: 114 bytes --]


Here is a before-after-patch screenshot with a grey background, which I
find makes the problem more noticeable:


[-- Attachment #8: cairo-svg-fixed-personal-config.png --]
[-- Type: image/png, Size: 229017 bytes --]

[-- Attachment #9: Type: text/plain, Size: 8587 bytes --]



Looking for references afterward, I found this bit about Cairo
integration in GTK+ in the er… GNOME Wiki Attic?

https://wiki.gnome.org/Attic/GtkCairoIntegration

> gdk-pixbuf only handles the pixel formats packed RGB and RGBA. Both of
> these formats are inefficient memory and performance wise.
>
> […]
>
> 2. Most Xservers uses the color format ARGB so to blit a GdkPixbuf you
>    need to convert each pixel from RGBA to ARGB which means that you are
>    taking an unnecessary performance hit. It is even worse if you are
>    blitting pixbufs using Cairo because then you have to convert from
>    premultiplied to non-premultiplied alpha.

See also the note on "Pixel formats" from a GNOME developer:

https://people.gnome.org/~federico/blog/my-gdk-pixbuf-braindump.html

> Every time we paint a GdkPixbuf to a cairo_t, there is a conversion
> from unpremultiplied RGBA to premultiplied, platform-endian ARGB.

(I find the GNOME Wiki Attic page a bit perplexing; they talk about
converting *from* premultiplied *to* non-premultiplied when working with
Cairo, which contradicts the Cairo manual, the GNOME developer's blog,
and my observations.)


Some questions I have about my patch:

- Is it OK to have array sizes in argument lists?  I know they are
  mostly decorative (e.g. sizeof still returns the size of a pointer),
  but I find the practice useful as a way to document intent (no idea
  if static analyzers are smart enough to use them nowadays).
  
  I can't say off the top of my head whether they come from C99 or
  later, but I found a few other functions using them
  (prepare_standard_handles in src/w32.h,
  get_lface_attributes_no_remap in src/xfaces.c), so I figured we
  already support the feature in practice.

- I originally divided by 255, since I figured that would be the
  maximal value for any given component and we want x*a / max to yield
  x when a is 255; however I later noticed that emacs_color_to_argb32
  divides by 256, so I went for that instead.  I am still not sure
  whether it's correct, or whether it matters much.

- I added my static function to the "Image type independent image
  structures" section so that it ends up close to emacs_color_to_argb32,
  but maybe I should move it down to the "SVG" section?  In which case I
  will probably need to add a new #ifdef USE_CAIRO block.

  (Alternately, should I just inline the function?)

- I haven't thought really hard about that "if (iconptr[3] == 0)"
  branch.  Is it still relevant?


Thank you for your time.



In GNU Emacs 27.0.50 (build 2, x86_64-pc-linux-gnu, GTK+ Version 3.24.8, cairo version 1.16.0)
 of 2019-05-15 built on my-little-tumbleweed
Repository revision: 4fa6029f7ee30aa3316d6d73db61462730042908
Repository branch: master
Windowing system distributor 'The X.Org Foundation', version 11.0.12004000
System Description: openSUSE Tumbleweed

Configured using:
 'configure --with-xwidgets --with-cairo'

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

Important settings:
  value of $LC_CTYPE: en_US.UTF-8
  value of $LANG: en_US.UTF-8
  value of $XMODIFIERS: @im=local
  locale-coding-system: utf-8-unix

Major mode: Magit Process

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 help-fns radix-tree ffap log-view misearch
multi-isearch browse-url cus-edit whitespace magit-patch cc-mode
cc-fonts cc-guess cc-menus cc-cmds cc-styles cc-align cc-engine cc-vars
cc-defs diff-hl-dired notifications dbus xml 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 generator 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 find-func cal-menu calendar cal-loaddefs find-dired xref
vc-bzr vc-src vc-sccs vc-svn vc-cvs vc-rcs project magit-ediff
ediff-merg ediff-wind ediff-diff ediff-mult ediff-help ediff-init
ediff-util ediff bug-reference flyspell ispell view markdown-mode rx
color thingatpt noutline outline magit-extras sh-script smie
magit-submodule magit-obsolete magit-popup 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 magit-core magit-autorevert autorevert
filenotify magit-margin magit-transient magit-process magit-mode
transient git-commit magit-git magit-section magit-utils crm log-edit
pcvs-util add-log with-editor async-bytecomp async shell pcomplete
server dash smerge-mode jka-compr mm-archive executable vc-git mailalias
smtpmail sendmail iso-transl nnir sort gnus-cite 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 message rmc puny dired dired-loaddefs format-spec rfc822 mml
mml-sec epa derived epg mm-decode mm-bodies mm-encode mail-parse rfc2231
mailabbrev gmm-utils mailheader gnus-win gnus nnheader gnus-util rmail
rmail-loaddefs rfc2047 rfc2045 ietf-drums text-property-search time-date
mail-utils mm-util mail-prsvr wid-edit 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 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 cairo
move-toolbar gtk x-toolkit x multi-tty make-network-process emacs)

Memory information:
((conses 16 444631 125454)
 (symbols 48 47237 83)
 (strings 32 176476 9183)
 (string-bytes 1 5274843)
 (vectors 16 64009)
 (vector-slots 8 2012096 107118)
 (floats 8 522 596)
 (intervals 56 17166 2584)
 (buffers 992 85))

^ permalink raw reply related	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2019-05-30 16:42 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2019-05-23 20:05 bug#35871: 27.0.50; [PATCH] Fix SVG transparency with Cairo Kévin Le Gouguec
2019-05-24  0:02 ` YAMAMOTO Mitsuharu
2019-05-24 21:19   ` Kévin Le Gouguec
2019-05-28  0:52   ` YAMAMOTO Mitsuharu
2019-05-28 10:02     ` Kévin Le Gouguec
2019-05-29  9:07       ` YAMAMOTO Mitsuharu
2019-05-30  0:18         ` Basil L. Contovounesios
2019-05-30  1:09           ` YAMAMOTO Mitsuharu
2019-05-30  1:55             ` Basil L. Contovounesios
2019-05-30 16:42         ` Kévin Le Gouguec

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).