]> git.mdlowis.com Git - proto/labwc.git/commitdiff
common: render text buffers with opaque background
authorJohn Lindgren <john@jlindgren.net>
Sun, 17 Mar 2024 02:20:40 +0000 (22:20 -0400)
committerJohan Malm <johanmalm@users.noreply.github.com>
Sun, 17 Mar 2024 19:09:53 +0000 (19:09 +0000)
After a roundabout discussion[1] with wlroots devs, it's become apparent
that subpixel text rendering (a.k.a. "ClearType") does not work properly
when rendering over a transparent background, as labwc currently does.

Basically it comes down to the fact that the color of semi-transparent
pixels (which is adjusted redder or bluer to compensate for RGB subpixel
alignment) depends somewhat on background color. When rendering over
transparency, the text engine doesn't know the intended background color
and can't adjust the pixel colors correctly.

With Pango/Cairo, the end result can range from grayscale rendering (no
subpixel rendering at all) to wrong/oversaturated colors (for example,
bright pink pixels when rendering white text on blue background).

This change solves the issue by first filling the text buffer with an
opaque background color before rendering the text over it. Currently,
this is easy since the background is always a solid color. It may be a
little more complex (but doable) if we implement gradients in future.

Note that GTK 4 (and to some degree, recent versions of Microsoft
Windows) avoid this issue by disabling subpixel rendering altogether. I
would much prefer that labwc NOT do this -- it results in noticeably
blurrier text on non-retina LCD screens, which are still common.

[1] https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/3822

include/common/font.h
include/common/graphic-helpers.h
include/common/scaled_font_buffer.h
src/common/font.c
src/common/graphic-helpers.c
src/common/scaled_font_buffer.c
src/menu/menu.c
src/ssd/resize_indicator.c
src/ssd/ssd_titlebar.c

index 38e56b944ebd578d42a6381ecab73111dd2c65eb..ddca15aaf96d2096c6118ab2f82ab339577a11b7 100644 (file)
@@ -42,11 +42,12 @@ int font_width(struct font *font, const char *string);
  * @text: text to be generated as texture
  * @font: font description
  * @color: foreground color in rgba format
+ * @bg_color: background color in rgba format
  * @arrow: arrow (utf8) character to show or NULL for none
  */
 void font_buffer_create(struct lab_data_buffer **buffer, int max_width,
-       const char *text, struct font *font, float *color, const char *arrow,
-       double scale);
+       const char *text, struct font *font, const float *color,
+       const float *bg_color, const char *arrow, double scale);
 
 /**
  * font_finish - free some font related resources
index ccf170223be261bb3fa5c1c8a93795d748829634..5463d4dc7af0aade731cadd983f4fd0ec15ce290 100644 (file)
@@ -42,7 +42,7 @@ void multi_rect_set_size(struct multi_rect *rect, int width, int height);
  * Sets the cairo color.
  * Splits a float[4] single color array into its own arguments
  */
-void set_cairo_color(cairo_t *cairo, float *color);
+void set_cairo_color(cairo_t *cairo, const float *color);
 
 /* Draws a border with a specified line width */
 void draw_cairo_border(cairo_t *cairo, struct wlr_fbox fbox, double line_width);
index 42fd3ad51cbc59049dd6fa3ba631715daea2f82a..029a90359b817d7e7c0bf458d0e25c879b223bed 100644 (file)
@@ -17,6 +17,7 @@ struct scaled_font_buffer {
        char *text;
        int max_width;
        float color[4];
+       float bg_color[4];
        char *arrow;
        struct font font;
        struct scaled_scene_buffer *scaled_buffer;
@@ -46,7 +47,8 @@ struct scaled_font_buffer *scaled_font_buffer_create(struct wlr_scene_tree *pare
  * - font and color the same
  */
 void scaled_font_buffer_update(struct scaled_font_buffer *self, const char *text,
-       int max_width, struct font *font, float *color, const char *arrow);
+       int max_width, struct font *font, const float *color,
+       const float *bg_color, const char *arrow);
 
 /**
  * Update the max width of an existing auto scaling font buffer
index 3b9973338bab4da639729b456cbcca9364f61738..88af348941d69e03bcd3ba7fc2862ef2a27244b1 100644 (file)
@@ -77,8 +77,8 @@ font_width(struct font *font, const char *string)
 
 void
 font_buffer_create(struct lab_data_buffer **buffer, int max_width,
-       const char *text, struct font *font, float *color, const char *arrow,
-       double scale)
+       const char *text, struct font *font, const float *color,
+       const float *bg_color, const char *arrow, double scale)
 {
        /* Allow a minimum of one pixel each for text and arrow */
        if (max_width < 2) {
@@ -114,6 +114,15 @@ font_buffer_create(struct lab_data_buffer **buffer, int max_width,
        cairo_t *cairo = (*buffer)->cairo;
        cairo_surface_t *surf = cairo_get_target(cairo);
 
+       /*
+        * Fill background color first - necessary for subpixel
+        * rendering, which does not work properly on transparency
+        */
+       set_cairo_color(cairo, bg_color);
+       cairo_rectangle(cairo, 0, 0, (*buffer)->unscaled_width,
+               (*buffer)->unscaled_height);
+       cairo_fill(cairo);
+
        set_cairo_color(cairo, color);
        cairo_move_to(cairo, 0, 0);
 
index 862decd94a1350bf4803af2ed038c5cf674b3f12..460b6760dade8c2cb8d275dc89b1bc2bb6baaca2 100644 (file)
@@ -83,7 +83,7 @@ draw_cairo_border(cairo_t *cairo, struct wlr_fbox fbox, double line_width)
 
 /* Sets the cairo color. Splits the single color channels */
 void
-set_cairo_color(cairo_t *cairo, float *c)
+set_cairo_color(cairo_t *cairo, const float *c)
 {
        cairo_set_source_rgba(cairo, c[0], c[1], c[2], c[3]);
 }
index a9b33a853749a66f55aae4409f6ce4054db9c00a..1f488c23cf25c868f4a382ca873435d0d6f68374 100644 (file)
@@ -19,7 +19,7 @@ _create_buffer(struct scaled_scene_buffer *scaled_buffer, double scale)
 
        /* Buffer gets free'd automatically along the backing wlr_buffer */
        font_buffer_create(&buffer, self->max_width, self->text,
-               &self->font, self->color, self->arrow, scale);
+               &self->font, self->color, self->bg_color, self->arrow, scale);
 
        self->width = buffer ? buffer->unscaled_width : 0;
        self->height = buffer ? buffer->unscaled_height : 0;
@@ -64,8 +64,8 @@ scaled_font_buffer_create(struct wlr_scene_tree *parent)
 
 void
 scaled_font_buffer_update(struct scaled_font_buffer *self, const char *text,
-               int max_width, struct font *font, float *color,
-               const char *arrow)
+               int max_width, struct font *font, const float *color,
+               const float *bg_color, const char *arrow)
 {
        assert(self);
        assert(text);
@@ -87,6 +87,7 @@ scaled_font_buffer_update(struct scaled_font_buffer *self, const char *text,
        self->font.slant = font->slant;
        self->font.weight = font->weight;
        memcpy(self->color, color, sizeof(self->color));
+       memcpy(self->bg_color, bg_color, sizeof(self->bg_color));
        self->arrow = arrow ? xstrdup(arrow) : NULL;
 
        /* Invalidate cache and force a new render */
index 822f593201a2604019e233e2b49672f71f72871b..c37ebc3b518b41e2f6411af3d86aa5e7b2275b57 100644 (file)
@@ -205,9 +205,11 @@ item_create(struct menu *menu, const char *text, bool show_arrow)
 
        /* Font buffers */
        scaled_font_buffer_update(menuitem->normal.buffer, text, menuitem->native_width,
-               &rc.font_menuitem, theme->menu_items_text_color, arrow);
+               &rc.font_menuitem, theme->menu_items_text_color,
+               theme->menu_items_bg_color, arrow);
        scaled_font_buffer_update(menuitem->selected.buffer, text, menuitem->native_width,
-               &rc.font_menuitem, theme->menu_items_active_text_color, arrow);
+               &rc.font_menuitem, theme->menu_items_active_text_color,
+               theme->menu_items_active_bg_color, arrow);
 
        /* Center font nodes */
        x = theme->menu_item_padding_x;
index e140886d84aa1d1a30455a91a196dfe46bb21c46..912656642333bc1d05fc2e51d2e36555c6ba5522 100644 (file)
@@ -199,7 +199,8 @@ resize_indicator_update(struct view *view)
                (eff_height - indicator->height) / 2);
 
        scaled_font_buffer_update(indicator->text, text, width, &rc.font_osd,
-               rc.theme->osd_label_text_color, NULL /* const char *arrow */);
+               rc.theme->osd_label_text_color, rc.theme->osd_bg_color,
+               NULL /* const char *arrow */);
 }
 
 void
index b0ffe8a04518457f1b12fc6414758ffea11f1745..ca5ff42c7142d502294bad57754a0c6aaa6c280f 100644 (file)
@@ -334,7 +334,8 @@ ssd_update_title(struct ssd *ssd)
        struct ssd_state_title *state = &ssd->state.title;
        bool title_unchanged = state->text && !strcmp(title, state->text);
 
-       float *text_color;
+       const float *text_color;
+       const float *bg_color;
        struct font *font = NULL;
        struct ssd_part *part;
        struct ssd_sub_tree *subtree;
@@ -346,10 +347,12 @@ ssd_update_title(struct ssd *ssd)
                if (subtree == &ssd->titlebar.active) {
                        dstate = &state->active;
                        text_color = theme->window_active_label_text_color;
+                       bg_color = theme->window_active_title_bg_color;
                        font = &rc.font_activewindow;
                } else {
                        dstate = &state->inactive;
                        text_color = theme->window_inactive_label_text_color;
+                       bg_color = theme->window_inactive_title_bg_color;
                        font = &rc.font_inactivewindow;
                }
 
@@ -379,7 +382,7 @@ ssd_update_title(struct ssd *ssd)
                if (part->buffer) {
                        scaled_font_buffer_update(part->buffer, title,
                                title_bg_width, font,
-                               text_color, NULL);
+                               text_color, bg_color, NULL);
                }
 
                /* And finally update the cache */