diff options
author | Phil Jones <philj56@gmail.com> | 2022-06-10 17:26:49 +0100 |
---|---|---|
committer | Phil Jones <philj56@gmail.com> | 2022-06-10 17:26:49 +0100 |
commit | 03c39b749d86a0b83d866197fd9fa2d48fd061e4 (patch) | |
tree | 8b575c3c7f28d96642b51b2d6c66f4eb3bbcc093 | |
parent | 89718c6b241d56106dad866252fb5538885e1b80 (diff) |
Cleanup and comment HarfBuzz code.
-rw-r--r-- | src/entry_backend/harfbuzz.c | 137 |
1 files changed, 92 insertions, 45 deletions
diff --git a/src/entry_backend/harfbuzz.c b/src/entry_backend/harfbuzz.c index 03e9ed0..161cefa 100644 --- a/src/entry_backend/harfbuzz.c +++ b/src/entry_backend/harfbuzz.c @@ -6,17 +6,38 @@ #include <pango/pangocairo.h> #include <pango/pango.h> #include <wchar.h> +#include "harfbuzz.h" #include "../entry.h" #include "../log.h" #include "../nelem.h" #include "../xmalloc.h" /* + * FreeType is normally compiled without error strings, so we have to do this + * funky macro trick to get them. See <freetype/fterrors.h> for more details. + */ +#undef FTERRORS_H_ +#define FT_ERRORDEF( e, v, s ) { e, s }, +#define FT_ERROR_START_LIST { +#define FT_ERROR_END_LIST { 0, NULL } }; + +const struct { + int err_code; + const char *err_msg; +} ft_errors[] = + +#include <freetype/fterrors.h> + +/* * Cairo / FreeType use 72 Pts per inch, but Pango uses 96 DPI, so we have to * rescale for consistency. */ #define PT_TO_DPI (96.0 / 72.0) +/* + * hb_buffer_clear_contents also clears some basic script information, so group + * them here for convenience. + */ static void setup_hb_buffer(hb_buffer_t *buffer) { hb_buffer_set_direction(buffer, HB_DIRECTION_LTR); @@ -25,39 +46,36 @@ static void setup_hb_buffer(hb_buffer_t *buffer) } -static hb_buffer_t *create_hb_buffer(void) -{ - hb_buffer_t *buffer = hb_buffer_create(); - hb_buffer_set_unicode_funcs(buffer, hb_glib_get_unicode_funcs()); - setup_hb_buffer(buffer); - - return buffer; -} - +/* + * Render a hb_buffer with Cairo, and return the width of the rendered area in + * Cairo units. + */ static uint32_t render_hb_buffer(cairo_t *cr, hb_buffer_t *buffer, uint32_t scale) { cairo_save(cr); + /* + * 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_matrix_t font_matrix; - cairo_get_font_matrix(cr, &font_matrix); - double baseline = (font_matrix.xx - font_extents.height) / 2 + font_extents.ascent; - cairo_translate(cr, 0, baseline); + cairo_translate(cr, 0, font_extents.ascent); unsigned int glyph_count; hb_glyph_info_t *glyph_info = hb_buffer_get_glyph_infos(buffer, &glyph_count); hb_glyph_position_t *glyph_pos = hb_buffer_get_glyph_positions(buffer, &glyph_count); cairo_glyph_t *cairo_glyphs = xmalloc(sizeof(cairo_glyph_t) * glyph_count); - double width = 0; - for (unsigned int i=0; i < glyph_count; ++i) { - width += glyph_pos[i].x_advance / 64.0 / scale; - } - double x = 0; double y = 0; - for (unsigned int i=0; i < glyph_count; ++i) { + for (unsigned int i=0; i < glyph_count; i++) { + /* + * The coordinates returned by HarfBuzz are in 26.6 fixed-point + * format, so we divide by 64.0 (2^6) to get floats. + * We also divide by the display's scale factor, to counter + * Cairo's scaling of coordinates and sizes. + */ cairo_glyphs[i].index = glyph_info[i].codepoint; 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; @@ -71,28 +89,54 @@ static uint32_t render_hb_buffer(cairo_t *cr, hb_buffer_t *buffer, uint32_t scal cairo_restore(cr); + double width = 0; + for (unsigned int i=0; i < glyph_count; i++) { + width += glyph_pos[i].x_advance / 64.0 / scale; + } return ceil(width); } -void entry_backend_init(struct entry *entry, uint32_t *width, uint32_t *height, uint32_t scale) +void entry_backend_init( + struct entry *entry, + uint32_t *width, + uint32_t *height, + uint32_t scale) { cairo_t *cr = entry->cairo[0].cr; /* Setup FreeType. */ log_debug("Creating FreeType library.\n"); - FT_Init_FreeType(&entry->backend.ft_library); + int err; + err = FT_Init_FreeType(&entry->backend.ft_library); + if (err) { + log_error("Error initialising FreeType: %s\n", + ft_errors[err].err_msg); + exit(EXIT_FAILURE); + } log_debug("Loading FreeType font.\n"); - FT_New_Face(entry->backend.ft_library, "font.ttf", 0, &entry->backend.ft_face); - FT_Set_Char_Size( + err = FT_New_Face( + entry->backend.ft_library, "font.ttf", 0, + &entry->backend.ft_face); + if (err) { + log_error("Error loading font: %s\n", ft_errors[err].err_msg); + exit(EXIT_FAILURE); + } + err = FT_Set_Char_Size( entry->backend.ft_face, - entry->font_size * 64 * scale * PT_TO_DPI, - entry->font_size * 64 * scale * PT_TO_DPI, - 0, - 0); + entry->font_size * 64 * scale, + entry->font_size * 64 * scale, + 96, + 96); + if (err) { + log_error("Error setting font size: %s\n", + ft_errors[err].err_msg); + exit(EXIT_FAILURE); + } log_debug("Creating Cairo font.\n"); - entry->backend.cairo_face = cairo_ft_font_face_create_for_ft_face(entry->backend.ft_face, 0); + entry->backend.cairo_face = + cairo_ft_font_face_create_for_ft_face(entry->backend.ft_face, 0); struct color color = entry->foreground_color; cairo_set_source_rgba(cr, color.r, color.g, color.b, color.a); @@ -104,18 +148,22 @@ void entry_backend_init(struct entry *entry, uint32_t *width, uint32_t *height, cairo_set_font_size(entry->cairo[1].cr, entry->font_size * PT_TO_DPI); log_debug("Creating Harfbuzz font.\n"); - entry->backend.hb_font = hb_ft_font_create_referenced(entry->backend.ft_face); + entry->backend.hb_font = + hb_ft_font_create_referenced(entry->backend.ft_face); log_debug("Creating Harfbuzz buffer.\n"); - entry->backend.hb_buffer = create_hb_buffer(); + hb_buffer_t *buffer = hb_buffer_create(); + entry->backend.hb_buffer = buffer; + hb_buffer_set_unicode_funcs(buffer, hb_glib_get_unicode_funcs()); + setup_hb_buffer(buffer); /* Draw the prompt now, as this only needs to be done once */ log_debug("Drawing prompt.\n"); hb_buffer_add_utf8(entry->backend.hb_buffer, "run: ", -1, 0, -1); hb_shape(entry->backend.hb_font, entry->backend.hb_buffer, NULL, 0); + uint32_t prompt_width = render_hb_buffer(cr, buffer, scale); - /* Move and clip so we don't draw over the prompt */ - uint32_t prompt_width = render_hb_buffer(cr, entry->backend.hb_buffer, scale); + /* Move and clip so we don't draw over the prompt later */ cairo_translate(cr, prompt_width, 0); *width -= prompt_width; cairo_rectangle(cr, 0, 0, *width, *height); @@ -133,27 +181,26 @@ void entry_backend_destroy(struct entry *entry) void entry_backend_update(struct entry *entry) { cairo_t *cr = entry->cairo[entry->index].cr; + hb_buffer_t *buffer = entry->backend.hb_buffer; - hb_buffer_clear_contents(entry->backend.hb_buffer); - setup_hb_buffer(entry->backend.hb_buffer); - hb_buffer_add_utf8(entry->backend.hb_buffer, entry->input_mb, -1, 0, -1); - hb_shape(entry->backend.hb_font, entry->backend.hb_buffer, NULL, 0); - render_hb_buffer(cr, entry->backend.hb_buffer, entry->image.scale); + /* Render the entry text */ + hb_buffer_clear_contents(buffer); + setup_hb_buffer(buffer); + hb_buffer_add_utf8(buffer, entry->input_mb, -1, 0, -1); + hb_shape(entry->backend.hb_font, buffer, NULL, 0); + render_hb_buffer(cr, buffer, entry->image.scale); cairo_font_extents_t font_extents; cairo_font_extents(cr, &font_extents); - for (size_t i = 0; i < 5; i++) { + /* Render our results entry text */ + for (size_t i = 0; i < 5 && i < entry->results.count; i++) { cairo_translate(cr, 0, font_extents.height); - hb_buffer_t *buffer = entry->backend.hb_buffer; - hb_buffer_clear_contents(buffer); setup_hb_buffer(buffer); - if (i < entry->results.count) { - hb_buffer_add_utf8(buffer, entry->results.buf[i], -1, 0, -1); - hb_shape(entry->backend.hb_font, buffer, NULL, 0); - render_hb_buffer(cr, buffer, entry->image.scale); - } + hb_buffer_add_utf8(buffer, entry->results.buf[i], -1, 0, -1); + hb_shape(entry->backend.hb_font, buffer, NULL, 0); + render_hb_buffer(cr, buffer, entry->image.scale); } } |