Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Emacs frame background color and default face background are set equal, but appear different by #010101

Tags:

emacs

I've set the frame background color and the default face background color to both be #262626:

(add-to-list 'default-frame-alist '(background-color . "#262626"))
(set-face-attribute 'default nil
                    :background "#262626")

In spite of this, they render slightly differently:

Broken rendering.

The difference is just barely noticeable. If you look at this in GIMP or similar, you can see that the background where there is text is #262626, while the background where there is no text (the blank lines) is #252525. This bothers me.

Strangely, the same thing does not occur when setting #242424 for both:

Non-broken rendering.

So why not just use #242424 and be done with it? I insist on #262626 because I want Emacs to be the same color as my xterms, and my xterms are that color because it's number 235 in the 256-color table. I want 256-color-capable apps running in xterm to be able to exactly reproduce the background color.

My question, then: How do I get Emacs to render the frame background and text background the same?

(I realize I sound like a crazy person for caring about this, but there's no reason why it shouldn't work properly and it's driving me nuts.)

This is Emacs 24.3 on Arch Linux.

like image 201
Sam Harada Avatar asked Oct 21 '22 13:10

Sam Harada


1 Answers

Aha! I have discovered the problem. It's an accursed rounding error.

  • This is bzr rev 116690 (git commit 08885465fcc080f369f0958fcd85e37e28a7526f).
  • ./autogen.sh, then:

    ./configure --prefix=/opt/emacs/usr --sysconfdir=/opt/emacs/etc \
        --libexecdir=/opt/emacs/usr/lib --localstatedir=/opt/emacs/var \
        --with-x-toolkit=gtk3 --with-xft CFLAGS=-g
    
  • Install dirs are under /opt/emacs to avoid clobbering the existing install.

  • make, (sudo) make install, gdb src/emacs.

The culprit function is xg_set_widget_bg in src/gtkutil.c. Set a breakpoint at line 1039, which sets the background from floating-point color values:

gtk_widget_override_background_color (w, GTK_STATE_FLAG_NORMAL, &bg);

In my case, the first call to xg_set_widget_bg is irrelevant. The second is where the fun happens. Poke the struct:

Breakpoint 1, xg_set_widget_bg (f=0x1209908, w=0x1658110, pixel=2500134) at gtkutil.c:1039
1039          gtk_widget_override_background_color (w, GTK_STATE_FLAG_NORMAL, &bg);
(gdb) print xbg
$1 = {pixel = 2500134, red = 9764, green = 9764, blue = 9764, flags = 7 '\a', pad = 0 '\000'}
(gdb) print bg
$2 = {red = 0.14898908979934386, green = 0.14898908979934386, blue = 0.14898908979934386, alpha = 1}
(gdb) print bg.red * 0xffff
$3 = 9763.9999999999982
(gdb) print bg.red * 0xff
$4 = 37.992217898832678
(gdb) set bg.red = bg.green = bg.blue = (double)0x26 / 0xff
(gdb) print bg
$5 = {red = 0.14901960784313725, green = 0.14901960784313725, blue = 0.14901960784313725, alpha = 1}
(gdb) print bg.red * 0xffff
$6 = 9765.9999999999982
(gdb) print bg.red * 0xff
$7 = 37.999999999999993

cont, and see the problem magically disappear. Debuggers are pretty cool.

If you want to see how Emacs gets the values in xbg, also look at xfns.c. When you say (set-background-color "#262626"), Emacs turns each component into 0x2600 and then asks X for the closest color value. Here, that's 9764 (0x2624). You can verify this with xmag, since it reports 16-bit components.

Apparently, GTK is scaling its pixel values to 0xff, and there's enough error to make it round (floor?) to 37 (0x25) instead of 38 (0x26). Text rendering is done somewhere else and doesn't have the same problem.

In the end, I suppose it's mostly GTK's fault. I don't really want to touch the GTK source for now, but at least I know what's going on.

like image 52
Sam Harada Avatar answered Oct 24 '22 00:10

Sam Harada