From 5dc06ce1f99e8ea25386bf3823b995be5f1cac53 Mon Sep 17 00:00:00 2001 From: Phil Jones Date: Mon, 26 Dec 2022 17:12:31 +0000 Subject: Convert to using Cairo scale. Instead of scaling various theme parameters ourselves, just set the scale in Cairo. This shouldn't result in any visible changes, but lays the ground work for fractional scaling support. --- src/config.c | 120 +++++++++++++------------------------------ src/entry.c | 16 ++---- src/entry_backend/harfbuzz.c | 55 ++++++++++++-------- src/entry_backend/harfbuzz.h | 3 ++ src/main.c | 31 ++++++----- 5 files changed, 96 insertions(+), 129 deletions(-) diff --git a/src/config.c b/src/config.c index ab1e4e3..23dbc7f 100644 --- a/src/config.c +++ b/src/config.c @@ -74,8 +74,7 @@ struct uint32_percent { static char *strip(const char *str); static bool parse_option(struct tofi *tofi, const char *filename, size_t lineno, const char *option, const char *value); static char *get_config_path(void); -static uint32_t fixup_percentage(uint32_t value, uint32_t base, bool is_percent, uint32_t scale, bool use_scale); -static void fixup_text_theme(struct text_theme *theme, uint32_t scale); +static uint32_t fixup_percentage(uint32_t value, uint32_t base, bool is_percent); static uint32_t parse_anchor(const char *filename, size_t lineno, const char *str, bool *err); static enum cursor_style parse_cursor_style(const char *filename, size_t lineno, const char *str, bool *err); @@ -753,112 +752,69 @@ bool config_apply(struct tofi *tofi, const char *option, const char *value) return parse_option(tofi, "", 0, option, value); } -uint32_t fixup_percentage(uint32_t value, uint32_t base, bool is_percent, uint32_t scale, bool use_scale) +uint32_t fixup_percentage(uint32_t value, uint32_t base, bool is_percent) { if (is_percent) { return value * base / 100; - } else if (use_scale) { - return value * scale; } return value; } -void fixup_text_theme(struct text_theme *theme, uint32_t scale) -{ - theme->background_corner_radius *= scale; - theme->padding.top *= scale; - theme->padding.bottom *= scale; - theme->padding.left *= scale; - theme->padding.right *= scale; -} - void config_fixup_values(struct tofi *tofi) { uint32_t scale = tofi->window.scale; + uint32_t base_width = tofi->output_width; + uint32_t base_height = tofi->output_height; + /* + * If we're going to be scaling these values in Cairo, + * we need to apply the inverse scale here. + */ if (tofi->use_scale) { - struct entry *entry = &tofi->window.entry; - - entry->font_size *= scale; - entry->prompt_padding *= scale; - entry->corner_radius *= scale; - entry->result_spacing *= scale; - entry->input_width *= scale; - entry->outline_width *= scale; - entry->border_width *= scale; - - entry->cursor_theme.corner_radius *= scale; - entry->cursor_theme.thickness *= scale; - - fixup_text_theme(&entry->prompt_theme, scale); - fixup_text_theme(&entry->placeholder_theme, scale); - fixup_text_theme(&entry->input_theme, scale); - fixup_text_theme(&entry->default_result_theme, scale); - fixup_text_theme(&entry->alternate_result_theme, scale); - fixup_text_theme(&entry->selection_theme, scale); + base_width /= scale; + base_height /= scale; } - /* These values should only be scaled if they're not percentages. */ tofi->window.width = fixup_percentage( tofi->window.width, - tofi->output_width, - tofi->window.width_is_percent, - tofi->window.scale, - tofi->use_scale); + base_width, + tofi->window.width_is_percent); tofi->window.height = fixup_percentage( tofi->window.height, - tofi->output_height, - tofi->window.height_is_percent, - tofi->window.scale, - tofi->use_scale); + base_height, + tofi->window.height_is_percent); tofi->window.margin_top = fixup_percentage( tofi->window.margin_top, - tofi->output_height, - tofi->window.margin_top_is_percent, - tofi->window.scale, - tofi->use_scale); + base_height, + tofi->window.margin_top_is_percent); tofi->window.margin_bottom = fixup_percentage( tofi->window.margin_bottom, - tofi->output_height, - tofi->window.margin_bottom_is_percent, - tofi->window.scale, - tofi->use_scale); + base_height, + tofi->window.margin_bottom_is_percent); tofi->window.margin_left = fixup_percentage( tofi->window.margin_left, - tofi->output_width, - tofi->window.margin_left_is_percent, - tofi->window.scale, - tofi->use_scale); + base_width, + tofi->window.margin_left_is_percent); tofi->window.margin_right = fixup_percentage( tofi->window.margin_right, - tofi->output_width, - tofi->window.margin_right_is_percent, - tofi->window.scale, - tofi->use_scale); + base_width, + tofi->window.margin_right_is_percent); tofi->window.entry.padding_top = fixup_percentage( tofi->window.entry.padding_top, - tofi->output_height, - tofi->window.entry.padding_top_is_percent, - tofi->window.scale, - tofi->use_scale); + base_height, + tofi->window.entry.padding_top_is_percent); tofi->window.entry.padding_bottom = fixup_percentage( tofi->window.entry.padding_bottom, - tofi->output_height, - tofi->window.entry.padding_bottom_is_percent, - tofi->window.scale, - tofi->use_scale); + base_height, + tofi->window.entry.padding_bottom_is_percent); tofi->window.entry.padding_left = fixup_percentage( tofi->window.entry.padding_left, - tofi->output_width, - tofi->window.entry.padding_left_is_percent, - tofi->window.scale, - tofi->use_scale); + base_width, + tofi->window.entry.padding_left_is_percent); tofi->window.entry.padding_right = fixup_percentage( tofi->window.entry.padding_right, - tofi->output_width, - tofi->window.entry.padding_right_is_percent, - tofi->window.scale, - tofi->use_scale); + base_width, + tofi->window.entry.padding_right_is_percent); /* Don't attempt percentage handling if exclusive_zone is set to -1. */ if (tofi->window.exclusive_zone > 0) { @@ -868,24 +824,20 @@ void config_fixup_values(struct tofi *tofi) case ANCHOR_BOTTOM: tofi->window.exclusive_zone = fixup_percentage( tofi->window.exclusive_zone, - tofi->output_height, - tofi->window.exclusive_zone_is_percent, - tofi->window.scale, - tofi->use_scale); + base_height, + tofi->window.exclusive_zone_is_percent); break; case ANCHOR_LEFT: case ANCHOR_RIGHT: tofi->window.exclusive_zone = fixup_percentage( tofi->window.exclusive_zone, - tofi->output_width, - tofi->window.exclusive_zone_is_percent, - tofi->window.scale, - tofi->use_scale); + base_width, + tofi->window.exclusive_zone_is_percent); break; default: /* - * Exclusive zone >0 is meaningless for other anchor - * positions. + * Exclusive zone >0 is meaningless for other + * anchor positions. */ tofi->window.exclusive_zone = MIN(tofi->window.exclusive_zone, 0); diff --git a/src/entry.c b/src/entry.c index 6a55741..4ad825a 100644 --- a/src/entry.c +++ b/src/entry.c @@ -64,6 +64,7 @@ void entry_init(struct entry *entry, uint8_t *restrict buffer, uint32_t width, u height, width * sizeof(uint32_t) ); + cairo_surface_set_device_scale(surface, scale, scale); cairo_t *cr = cairo_create(surface); entry->cairo[0].surface = surface; @@ -76,8 +77,12 @@ void entry_init(struct entry *entry, uint8_t *restrict buffer, uint32_t width, u height, width * sizeof(uint32_t) ); + cairo_surface_set_device_scale(entry->cairo[1].surface, scale, scale); entry->cairo[1].cr = cairo_create(entry->cairo[1].surface); + /* If we're scaling with Cairo, remember to account for that here. */ + width /= scale; + height /= scale; log_debug("Drawing window.\n"); /* Draw the background */ @@ -196,17 +201,6 @@ void entry_init(struct entry *entry, uint8_t *restrict buffer, uint32_t width, u entry->cursor_theme.text_color = entry->background_color; } - /* - * TODO: - * This is a dirty hack. The proper thing to do is probably just to - * stop scaling everything manually, set cairo_scale and have done - * with. The reason that isn't how things are currently done is due to - * historic scaling behaviour of tofi. - */ - if (!entry->cursor_theme.thickness_specified) { - entry->cursor_theme.thickness *= scale; - } - /* * Perform an initial render of the text. * This is done here rather than by calling entry_update to avoid the diff --git a/src/entry_backend/harfbuzz.c b/src/entry_backend/harfbuzz.c index ae26d67..3bb8a8f 100644 --- a/src/entry_backend/harfbuzz.c +++ b/src/entry_backend/harfbuzz.c @@ -84,7 +84,7 @@ static void setup_hb_buffer(hb_buffer_t *buffer) * Render a hb_buffer with Cairo, and return the extents of the rendered text * in Cairo units. */ -static cairo_text_extents_t render_hb_buffer(cairo_t *cr, hb_buffer_t *buffer) +static cairo_text_extents_t render_hb_buffer(cairo_t *cr, hb_font_extents_t *font_extents, hb_buffer_t *buffer, double scale) { cairo_save(cr); @@ -92,9 +92,7 @@ static cairo_text_extents_t render_hb_buffer(cairo_t *cr, hb_buffer_t *buffer) * Cairo uses y-down coordinates, but HarfBuzz uses y-up, so we * shift the text down by its ascent height to compensate. */ - cairo_font_extents_t font_extents; - cairo_font_extents(cr, &font_extents); - cairo_translate(cr, 0, font_extents.ascent); + cairo_translate(cr, 0, font_extents->ascender / 64.0); unsigned int glyph_count; hb_glyph_info_t *glyph_info = hb_buffer_get_glyph_infos(buffer, &glyph_count); @@ -107,12 +105,17 @@ static cairo_text_extents_t render_hb_buffer(cairo_t *cr, hb_buffer_t *buffer) /* * The coordinates returned by HarfBuzz are in 26.6 fixed-point * format, so we divide by 64.0 (2^6) to get floats. + * + * For whatever reason, the coordinates are also scaled by + * Cairo's scale factor, so we have to also divide by the scale + * factor to account for this. */ cairo_glyphs[i].index = glyph_info[i].codepoint; - cairo_glyphs[i].x = x + glyph_pos[i].x_offset / 64.0; - cairo_glyphs[i].y = y - glyph_pos[i].y_offset / 64.0; - x += glyph_pos[i].x_advance / 64.0; - y -= glyph_pos[i].y_advance / 64.0; + cairo_glyphs[i].x = x + glyph_pos[i].x_offset / 64.0 / scale; + cairo_glyphs[i].y = y - glyph_pos[i].y_offset / 64.0 / scale; + + x += glyph_pos[i].x_advance / 64.0 / scale; + y -= glyph_pos[i].y_advance / 64.0 / scale; } cairo_show_glyphs(cr, cairo_glyphs, glyph_count); @@ -121,7 +124,7 @@ static cairo_text_extents_t render_hb_buffer(cairo_t *cr, hb_buffer_t *buffer) cairo_glyph_extents(cr, cairo_glyphs, glyph_count, &extents); /* Account for the shifted baseline in our returned text extents. */ - extents.y_bearing += font_extents.ascent; + extents.y_bearing += font_extents->ascender / 64.0; free(cairo_glyphs); @@ -143,7 +146,7 @@ static cairo_text_extents_t render_text( setup_hb_buffer(hb->hb_buffer); hb_buffer_add_utf8(hb->hb_buffer, text, -1, 0, -1); hb_shape(hb->hb_font, hb->hb_buffer, hb->hb_features, hb->num_features); - return render_hb_buffer(cr, hb->hb_buffer); + return render_hb_buffer(cr, &hb->hb_font_extents, hb->hb_buffer, hb->scale); } @@ -259,7 +262,7 @@ static cairo_text_extents_t render_input( setup_hb_buffer(hb->hb_buffer); hb_buffer_add_utf32(hb->hb_buffer, text, -1, 0, -1); hb_shape(hb->hb_font, hb->hb_buffer, hb->hb_features, hb->num_features); - cairo_text_extents_t extents = render_hb_buffer(cr, hb->hb_buffer); + cairo_text_extents_t extents = render_hb_buffer(cr, &hb->hb_font_extents, hb->hb_buffer, hb->scale); /* * If the cursor is at the end of text, we need to account for it in @@ -304,7 +307,7 @@ static cairo_text_extents_t render_input( color = theme->foreground_color; cairo_set_source_rgba(cr, color.r, color.g, color.b, color.a); - render_hb_buffer(cr, hb->hb_buffer); + render_hb_buffer(cr, &hb->hb_font_extents, hb->hb_buffer, hb->scale); } if (!cursor_theme->show) { @@ -385,8 +388,8 @@ static cairo_text_extents_t render_input( cursor_end += x_advance; } /* Convert from HarfBuzz 26.6 fixed-point to float. */ - cursor_x = cursor_start / 64.0; - cursor_width = (cursor_end - cursor_start) / 64.0; + cursor_x = cursor_start / 64.0 / hb->scale; + cursor_width = (cursor_end - cursor_start) / 64.0 / hb->scale; } cairo_save(cr); @@ -405,7 +408,7 @@ static cairo_text_extents_t render_input( cairo_translate(cr, -cursor_x, 0); color = cursor_theme->text_color; cairo_set_source_rgba(cr, color.r, color.g, color.b, color.a); - render_hb_buffer(cr, hb->hb_buffer); + render_hb_buffer(cr, &hb->hb_font_extents, hb->hb_buffer, hb->scale); break; case CURSOR_STYLE_UNDERSCORE: cairo_translate(cr, 0, cursor_theme->underline_depth); @@ -444,6 +447,7 @@ void entry_backend_harfbuzz_init( struct entry_backend_harfbuzz *hb = &entry->harfbuzz; cairo_t *cr = entry->cairo[0].cr; uint32_t font_size = floor(entry->font_size * PT_TO_DPI); + cairo_surface_get_device_scale(entry->cairo[0].surface, &hb->scale, NULL); /* * Setting up our font has three main steps: @@ -582,6 +586,11 @@ void entry_backend_harfbuzz_init( log_debug("Creating Harfbuzz buffer.\n"); hb->hb_buffer = hb_buffer_create(); + hb_font_get_h_extents(hb->hb_font, &hb->hb_font_extents); + if (hb->hb_font_extents.line_gap == 0) { + hb->hb_font_extents.line_gap = (hb->hb_font_extents.ascender - hb->hb_font_extents.descender); + } + log_debug("Creating Cairo font.\n"); hb->cairo_face = cairo_ft_font_face_create_for_ft_face(hb->ft_face, 0); @@ -619,13 +628,17 @@ void entry_backend_harfbuzz_update(struct entry *entry) cairo_t *cr = entry->cairo[entry->index].cr; cairo_text_extents_t extents; - cairo_font_extents_t font_extents; - cairo_font_extents(cr, &font_extents); - cairo_save(cr); /* Render the prompt */ extents = render_text_themed(cr, entry, entry->prompt_text, &entry->prompt_theme); + { + struct entry_backend_harfbuzz *hb = &entry->harfbuzz; + hb_buffer_clear_contents(hb->hb_buffer); + setup_hb_buffer(hb->hb_buffer); + hb_buffer_add_utf8(hb->hb_buffer, "test", -1, 0, -1); + hb_shape(hb->hb_font, hb->hb_buffer, hb->hb_features, hb->num_features); + } cairo_translate(cr, extents.x_advance, 0); cairo_translate(cr, entry->prompt_padding, 0); @@ -683,7 +696,7 @@ void entry_backend_harfbuzz_update(struct entry *entry) if (entry->horizontal) { cairo_translate(cr, extents.x_advance + entry->result_spacing, 0); } else { - cairo_translate(cr, 0, font_extents.height + entry->result_spacing); + cairo_translate(cr, 0, entry->harfbuzz.hb_font_extents.line_gap / 64.0 + entry->result_spacing); } if (entry->num_results == 0) { if (size_overflows(entry, 0, 0)) { @@ -729,7 +742,7 @@ void entry_backend_harfbuzz_update(struct entry *entry) * The height of the text doesn't change, so * we don't need to re-measure it each time. */ - if (size_overflows(entry, 0, font_extents.height)) { + if (size_overflows(entry, 0, entry->harfbuzz.hb_font_extents.line_gap / 64.0)) { break; } else { extents = render_text_themed(cr, entry, result, theme); @@ -871,7 +884,7 @@ void entry_backend_harfbuzz_update(struct entry *entry) rounded_rectangle( cr, ceil(extents.width + padding.left + padding.right), - ceil(font_extents.height + padding.top + padding.bottom), + ceil(entry->harfbuzz.hb_font_extents.line_gap / 64.0 + padding.top + padding.bottom), entry->selection_theme.background_corner_radius ); cairo_fill(cr); diff --git a/src/entry_backend/harfbuzz.h b/src/entry_backend/harfbuzz.h index 17b5945..acaa2eb 100644 --- a/src/entry_backend/harfbuzz.h +++ b/src/entry_backend/harfbuzz.h @@ -19,12 +19,15 @@ struct entry_backend_harfbuzz { cairo_font_face_t *cairo_face; hb_font_t *hb_font; + hb_font_extents_t hb_font_extents; hb_buffer_t *hb_buffer; hb_variation_t hb_variations[MAX_FONT_VARIATIONS]; hb_feature_t hb_features[MAX_FONT_FEATURES]; uint8_t num_variations; uint8_t num_features; + double scale; + bool disable_hinting; }; diff --git a/src/main.c b/src/main.c index dbfb718..04e8529 100644 --- a/src/main.c +++ b/src/main.c @@ -104,11 +104,11 @@ static void zwlr_layer_surface_configure( * We want actual pixel width / height, so we have to scale the * values provided by Wayland. */ - tofi->window.width = width * tofi->window.scale; - tofi->window.height = height * tofi->window.scale; + tofi->window.width = width; + tofi->window.height = height; - tofi->window.surface.width = tofi->window.width; - tofi->window.surface.height = tofi->window.height; + tofi->window.surface.width = width * tofi->window.scale; + tofi->window.surface.height = height * tofi->window.scale; zwlr_layer_surface_v1_ack_configure( tofi->window.zwlr_layer_surface, @@ -1462,16 +1462,21 @@ int main(int argc, char *argv[]) zwlr_layer_surface_v1_set_exclusive_zone( tofi.window.zwlr_layer_surface, tofi.window.exclusive_zone); + /* + * No matter whether we're scaling via Cairo or not, we're presenting a + * scaled buffer to Wayland, so scale the window size here if we + * haven't already done so. + */ zwlr_layer_surface_v1_set_size( tofi.window.zwlr_layer_surface, - tofi.window.width / tofi.window.scale, - tofi.window.height / tofi.window.scale); + tofi.window.width / (tofi.use_scale ? 1 : tofi.window.scale), + tofi.window.height / (tofi.use_scale ? 1 : tofi.window.scale)); zwlr_layer_surface_v1_set_margin( tofi.window.zwlr_layer_surface, - tofi.window.margin_top / tofi.window.scale, - tofi.window.margin_right / tofi.window.scale, - tofi.window.margin_bottom / tofi.window.scale, - tofi.window.margin_left / tofi.window.scale); + tofi.window.margin_top, + tofi.window.margin_right, + tofi.window.margin_bottom, + tofi.window.margin_left); wl_surface_commit(tofi.window.surface.wl_surface); /* @@ -1524,8 +1529,8 @@ int main(int argc, char *argv[]) entry_init( &tofi.window.entry, tofi.window.surface.shm_pool_data, - tofi.window.width, - tofi.window.height, + tofi.window.surface.width, + tofi.window.surface.height, tofi.use_scale ? tofi.window.scale : 1); log_unindent(); log_debug("Renderer initialised.\n"); @@ -1546,7 +1551,7 @@ int main(int argc, char *argv[]) memcpy( cairo_image_surface_get_data(tofi.window.entry.cairo[1].surface), cairo_image_surface_get_data(tofi.window.entry.cairo[0].surface), - tofi.window.entry.image.width * tofi.window.entry.image.height * sizeof(uint32_t) + tofi.window.surface.width * tofi.window.surface.height * sizeof(uint32_t) ); log_debug("Second buffer initialised.\n"); -- cgit v1.2.3