GNOME Bugzilla – Bug 715008
antialiasing gone wrong when the textview is scrollable
Last modified: 2014-08-22 02:13:58 UTC
Created attachment 261228 [details] gedit screenshot (bad) When the textview widget is scrollable, it starts doing funny things to RGBA-antialiased text, causing very noticeable color fringes. This affects gedit; the 'description' area in gtk3-demo; some other places too. comments from #Gtk+ on IRC: (alex) I think the problem is that it picks a transparent background for the offscreen pixmap (alex) then it composes it again against the white (alex) I think it essentially does the AA against a black background and then applies it on the white, or something like that
Created attachment 261229 [details] gedit screenshot (good) gtk3 3.11.2-11-gda86918 cairo 1.12.16-64-g8c73949 freetype2 2.5.0.1-125-gbc25de6
bisect: commit 1ac13435b7c14dd9b97ad7a9d292acd51337d66e Author: Alexander Larsson <alexl@redhat.com> Date: Wed Oct 2 15:14:56 2013 +0200 GtkTextView: Fix regression in GtkSourceView drawing
Ugh, yeah, that is the commit that made the pixel cache transparent, which is causing the problem because we do // draw text on cache (starting out transparent) pixelcache = OVER(transparent, text) // draw pixel cache over background destination = OVER(background, pixelcache) Which is not the same as destination = OVER(background, text) that we want. However, that change was added to fix gedit drawing, so we can't just revert it without breaking gedit in other ways.
Created attachment 263641 [details] Details enlarged
So afaics the problem is that we lose the per-channel alpha information when doing subpixel antialiasing with an intermediate transparency surface. Example: We have blue text over a white background and the left half of the pixel should be filled. This is the extreme version of what you can see in Alex' comparison for both the "t" glyphs in "content" in the bottommost row. Background: RGBA 0xFFFFFFFF Foreground: RGBA 0xFF0000FF alpha mask: 3 channels 0x0080FF Compositing directly: 0xFF0000FF OVER 0xFFFFFFFF MASK 0x0080FF RESULT 0xFFFF80FF Compositing indirectly: 0xFF0000FF OVER 0x00000000 MASK 0x0080FF RESULT 0xFF0000FF 0xFF0000FF OVER 0xFFFFFFFF RESULT 0xFF0000FF So in the first case we get a pinkish tone 0xFF80FF and in the second case we get blue 0x0000FF.
Example without subpixel AA (or rather, using the same value for all alpha channels): Background: RGBA 0xFFFFFFFF Foreground: RGBA 0xFF0000FF alpha mask: 0x80 Compositing directly: 0xFF0000FF OVER 0xFFFFFFFF MASK 0x80 RESULT 0xFF8080FF Compositing indirectly: 0xFF0000FF OVER 0x00000000 MASK 0x80 RESULT 0x80000080 0x80000080 OVER 0xFFFFFFFF RESULT 0xFF8080FF
*** Bug 721063 has been marked as a duplicate of this bug. ***
Created attachment 265854 [details] [review] PixelCache: Set event window on cairo_t We set the passed in GdkWindow as the event window on the cairo_t so that it will return TRUE from gtk_cairo_should_draw_window() only for that window. It also means we won't be propagating the draw to children that are in another GdkWindow. This seems generally right, and will be needed later to allow GtkTextView to run the entire draw signal handler when drawing the pixelcache without drawing stuff that was meant for other windows.
Created attachment 265855 [details] [review] PixelCache: Refactor pixelcache surface creation _gtk_pixel_cache_create_surface_if_needed now returns TRUE if the cache will be used. Additionally, we avoid painting the cache at all on draw if its not used due to e.g. drawing on a PDF surface.
Created attachment 265856 [details] [review] StyleContext: Add gtk_style_context_get_background_is_static() This lets us check for background that are static, i.e look the same independent of scrolling around on them. For such backgrounds we need not use alpha in any pixel caches, we can just draw the background on a opaque pixel cache before drawing the widget. This is an important optimization.
Created attachment 265857 [details] [review] PixelCache: Add _gtk_pixel_cache_draw_with_bg() This version has a callback for filling in an opaque background, which allows us to always use non-alpha surfaces. If you can guarantee that the background works for this that is a significant optimization.
Created attachment 265858 [details] [review] TreeView: Avoid using alpha pixel cache when possible If the style context background is static we can always render it as a pixel-cache background, so use _gtk_pixel_cache_draw_with_bg() in that case. This means we avoid using alpha pixel cache in the "normal" case where the bin_window is transparent but the normal window is opaque white.
Created attachment 265859 [details] [review] TextView: Fix AA rendering on pixel cache, re-fixing gedit rendering This changes the text view to only use the pixel cache when the background is a solid color, and in that case we draw the widget background first, then re-emit the whole draw signal so that all drawing goes to the pixelcache, including e.g. things drawn before chaining up to the draw method of GtkTextView. This is another fix of bug #708423 where gedit drawing before chaining up was overdrawn by the pixelcache. The previous fix of using an alpha pixelcache has worse performance and problems with rgba AA (bug #715008).
Hmm, so i found a different gedit issue with this. If you enable a right margin overlay it will be drawn twice, once on the pixelcache and then once when drawn over the blitted pixelcache. Anything drawn before the pixelcache will safely be overwritten with opaque paint, but we need to figure out how to disable further drawing.
Created attachment 265903 [details] [review] TextView: Fix AA rendering on pixel cache, re-fixing gedit rendering This changes the text view to only use the pixel cache when the background is a solid color, and in that case we draw the widget background first, then re-run the whole draw vfunc so that all drawing goes to the pixelcache, including e.g. things drawn before chaining up to the draw method of GtkTextView. We draw the pixel cache in a separate after handler in order to make sure we overdraw all the extra stuff that the overriding draw vfunc may draw. This is another fix of bug #708423 where gedit drawing before chaining up was overdrawn by the pixelcache. The previous fix of using an alpha pixelcache has worse performance and problems with rgba AA (bug #715008).
New alternative patch trying to fix the remaining gedit issue. This delays drawing the pixel cache until a separate connect_after signal handler. It also has to play tricks with get_visible_rect() during drawing the pixel cache to make sourceview draw to the not currently visible part of the cache. For some reason i can't understand it still doesn't work fully though. gedit draws the right margin line to the bottom, but the overlay to the right doesn't extend to the bottom. And, this is starting to get pretty ugly... We should probably look at just fixing sourceview to do this in a cleaner way.
*** Bug 729126 has been marked as a duplicate of this bug. ***
Alex, would it make sense to open a bug against sourceview for this issue?
The text of most programs that this bug affects does not have to be looked at for a very long time (e.g. Synaptic and Update Manager). This is not so for Gedit. I had to switch to another text editor. This is because the corrupted font of Gedit causes my eyes to hurt. Is there perhaps a way to workaround the bug while the bug is being fixed? Thank you.
running the application with the env var GTK_DEBUG=no-pixel-cache set should work around the problem.
Yes adding --gtk-debug=no-pixel-cache when starting Gedit fixes this problem. Thank you Benjamin Otte.
I dunno if we need a gtksourview bug or not, but someone needs to update gtksourceview+gtktextview so that we don't have to do the weird workaround with an alpha-based pixelcache. Personally i think the best approach is to add pre-and post draw() vfuncs to GtkTextView, although it is possible that some of the sourceview features can just be moved to textview (like the highlight bg of current row one).
FYI, I found that adding "GTK_DEBUG=no-pixel-cache" to your .profile also works, and you don't have to modify individual application launchers (such as gedit.desktop). I presume the .profile approach would apply this work-around to all applications impacted by this bug. I do not know if there are any performance penalties of applying this parameter globally, but it is working fine for me. $ echo -e "\nexport GTK_DEBUG=no-pixel-cache" | tee --append ~/.profile
@PJSingh5000 - Thanks for that quicker workaround, though it does not work with all programs (e.g. synaptic). Most seem fine though (gedit, gnome-system-log, update-manager).
(In reply to comment #22) > Personally i think the best approach is to add pre-and post draw() vfuncs to > GtkTextView Now that it is done, I think this bug is fixed.
Thank you Sébastien. I'll test this when the package becomes available as an update for my distro. So I can keep an eye-out for it, the change was made in libgtk-3-0 ?
The change is available in GTK+ 3.13.7 (unstable release), and will be available for the 3.14 stable version (in Septembre). You can already test with e.g. Fedora 21, or with jhbuild.