From c4f291bc3a32d867b2409a30cf377da399b6dc23 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Fri, 21 Mar 2025 20:49:17 -0400 Subject: [PATCH 1/4] Add api to turn subpixel positioning on or off Add cairo_font_options_set_subpixel_positions() to explicitly turn subpixel positioning on or off. --- src/cairo-font-options.c | 52 ++++++++++++++++++++++++++++-- src/cairo-image-compositor.c | 21 ++++++++---- src/cairo-types-private.h | 1 + src/cairo-user-font.c | 6 ++-- src/cairo-xlib-render-compositor.c | 15 +++++---- src/cairo.h | 26 +++++++++++++++ src/win32/cairo-dwrite-font.cpp | 8 +++-- 7 files changed, 111 insertions(+), 18 deletions(-) diff --git a/src/cairo-font-options.c b/src/cairo-font-options.c index d5c6663be..8590fc143 100644 --- a/src/cairo-font-options.c +++ b/src/cairo-font-options.c @@ -60,6 +60,7 @@ static const cairo_font_options_t _cairo_font_options_nil = { CAIRO_COLOR_MODE_DEFAULT, CAIRO_COLOR_PALETTE_DEFAULT, NULL, 0, /* custom palette */ + CAIRO_SUBPIXEL_POSITIONS_DEFAULT, }; /** @@ -82,6 +83,7 @@ _cairo_font_options_init_default (cairo_font_options_t *options) options->palette_index = CAIRO_COLOR_PALETTE_DEFAULT; options->custom_palette = NULL; options->custom_palette_size = 0; + options->subpixel_positions = CAIRO_SUBPIXEL_POSITIONS_DEFAULT; } void @@ -103,6 +105,7 @@ _cairo_font_options_init_copy (cairo_font_options_t *options, options->custom_palette = (cairo_palette_color_t *) malloc (sizeof (cairo_palette_color_t) * options->custom_palette_size); memcpy (options->custom_palette, other->custom_palette, sizeof (cairo_palette_color_t) * options->custom_palette_size); } + options->subpixel_positions = other->subpixel_positions; } cairo_bool_t @@ -117,7 +120,8 @@ _cairo_font_options_compare (const cairo_font_options_t *a, a->round_glyph_positions != b->round_glyph_positions || a->color_mode != b->color_mode || a->palette_index != b->palette_index || - a->custom_palette_size != b->custom_palette_size) + a->custom_palette_size != b->custom_palette_size || + a->subpixel_positions != b->subpixel_positions) { return FALSE; } @@ -287,6 +291,8 @@ cairo_font_options_merge (cairo_font_options_t *options, options->hint_metrics = other->hint_metrics; if (other->round_glyph_positions != CAIRO_ROUND_GLYPH_POS_DEFAULT) options->round_glyph_positions = other->round_glyph_positions; + if (other->subpixel_positions != CAIRO_SUBPIXEL_POSITIONS_DEFAULT) + options->subpixel_positions = other->subpixel_positions; if (other->variations) { if (options->variations) { @@ -349,6 +355,7 @@ cairo_font_options_equal (const cairo_font_options_t *options, options->hint_style == other->hint_style && options->hint_metrics == other->hint_metrics && options->round_glyph_positions == other->round_glyph_positions && + options->subpixel_positions == other->subpixel_positions && ((options->variations == NULL && other->variations == NULL) || (options->variations != NULL && other->variations != NULL && strcmp (options->variations, other->variations) == 0)) && @@ -393,7 +400,8 @@ cairo_font_options_hash (const cairo_font_options_t *options) (options->lcd_filter << 8) | (options->hint_style << 12) | (options->hint_metrics << 16) | - (options->color_mode << 20)) ^ hash; + (options->subpixel_positions << 20) | + (options->color_mode << 22)) ^ hash; } /** @@ -869,3 +877,43 @@ cairo_font_options_get_custom_palette_color (cairo_font_options_t *options, return CAIRO_STATUS_INVALID_INDEX; } + +/** + * cairo_font_options_set_subpixel_positions: + * @options: a #cairo_font_options_t + * @subpixel_positions: the new value + * + * Sets the subpixel positions value for the font options object. + * This controls whether glyph positions are rounded when rendering. + * + * Since: 1.20 + **/ +void +cairo_font_options_set_subpixel_positions (cairo_font_options_t *options, + cairo_subpixel_positions_t subpixel_positions) +{ + if (cairo_font_options_status (options)) + return; + + options->subpixel_positions = subpixel_positions; +} + +/** + * cairo_font_options_get_subpixel_positions: + * @options: a #cairo_font_options_t + * + * Gets the subpixel positions value for the font options object. + * See the documentation for #cairo_subpixel_positions_t for full details. + * + * Return value: the subpixel positions value for the font options object + * + * Since: 1.20 + */ +cairo_public cairo_subpixel_positions_t +cairo_font_options_get_subpixel_positions (const cairo_font_options_t *options) +{ + if (cairo_font_options_status ((cairo_font_options_t *) options)) + return CAIRO_SUBPIXEL_POSITIONS_DEFAULT; + + return options->subpixel_positions; +} diff --git a/src/cairo-image-compositor.c b/src/cairo-image-compositor.c index 8a3b2629f..ff90f094b 100644 --- a/src/cairo-image-compositor.c +++ b/src/cairo-image-compositor.c @@ -889,12 +889,15 @@ composite_glyphs (void *_dst, for (i = 0; i < info->num_glyphs; i++) { unsigned long index = info->glyphs[i].index; const void *glyph; - unsigned long xphase, yphase; - xphase = PHASE(info->glyphs[i].x); - yphase = PHASE(info->glyphs[i].y); + if (info->font->options.subpixel_positions != CAIRO_SUBPIXEL_POSITIONS_OFF) { + unsigned long xphase, yphase; - index = index | (xphase << 24) | (yphase << 26); + xphase = PHASE(info->glyphs[i].x); + yphase = PHASE(info->glyphs[i].y); + + index = index | (xphase << 24) | (yphase << 26); + } glyph = pixman_glyph_cache_lookup (glyph_cache, info->font, (void *)(uintptr_t)index); if (!glyph) { @@ -925,8 +928,14 @@ composite_glyphs (void *_dst, } } - pg->x = POSITION (info->glyphs[i].x); - pg->y = POSITION (info->glyphs[i].y); + if (info->font->options.subpixel_positions != CAIRO_SUBPIXEL_POSITIONS_OFF) { + pg->x = POSITION (info->glyphs[i].x); + pg->y = POSITION (info->glyphs[i].y); + } + else { + pg->x = _cairo_lround (info->glyphs[i].x); + pg->y = _cairo_lround (info->glyphs[i].y); + } pg->glyph = glyph; pg++; } diff --git a/src/cairo-types-private.h b/src/cairo-types-private.h index 74a366c23..4914640f8 100644 --- a/src/cairo-types-private.h +++ b/src/cairo-types-private.h @@ -204,6 +204,7 @@ struct _cairo_font_options { unsigned int palette_index; cairo_palette_color_t *custom_palette; unsigned int custom_palette_size; + cairo_subpixel_positions_t subpixel_positions; }; struct _cairo_glyph_text_info { diff --git a/src/cairo-user-font.c b/src/cairo-user-font.c index 56525d5c1..400158341 100644 --- a/src/cairo-user-font.c +++ b/src/cairo-user-font.c @@ -207,8 +207,10 @@ _cairo_user_scaled_glyph_init_record_glyph (cairo_user_scaled_font_t *scaled_fon if (recording_surface) cairo_surface_destroy (recording_surface); recording_surface = _cairo_user_scaled_font_create_recording_surface (scaled_font, FALSE, foreground_color); - recording_surface->device_transform.x0 = .25 * _cairo_scaled_glyph_xphase (scaled_glyph); - recording_surface->device_transform.y0 = .25 * _cairo_scaled_glyph_yphase (scaled_glyph); + if (scaled_font->base.options.subpixel_positions != CAIRO_SUBPIXEL_POSITIONS_OFF) { + recording_surface->device_transform.x0 = .25 * _cairo_scaled_glyph_xphase (scaled_glyph); + recording_surface->device_transform.y0 = .25 * _cairo_scaled_glyph_yphase (scaled_glyph); + } cr = _cairo_user_scaled_font_create_recording_context (scaled_font, recording_surface, FALSE); diff --git a/src/cairo-xlib-render-compositor.c b/src/cairo-xlib-render-compositor.c index a870c00e1..2d8b29138 100644 --- a/src/cairo-xlib-render-compositor.c +++ b/src/cairo-xlib-render-compositor.c @@ -1608,14 +1608,17 @@ composite_glyphs (void *surface, op = _render_operator (op), _cairo_xlib_surface_ensure_picture (dst); for (i = 0; i < num_glyphs; i++) { - unsigned long xphase, yphase; int this_x, this_y; int old_width; - xphase = PHASE(glyphs[i].d.x); - yphase = PHASE(glyphs[i].d.y); + if (info->font->options.subpixel_positions != CAIRO_SUBPIXEL_POSITIONS_OFF) { + unsigned long xphase, yphase; - glyphs[i].index |= (xphase << 24) | (yphase << 26); + xphase = PHASE(glyphs[i].d.x); + yphase = PHASE(glyphs[i].d.y); + + glyphs[i].index |= (xphase << 24) | (yphase << 26); + } status = _cairo_scaled_glyph_lookup (info->font, glyphs[i].index, @@ -1625,8 +1628,8 @@ composite_glyphs (void *surface, if (unlikely (status)) return status; - this_x = POSITION (glyphs[i].d.x); - this_y = POSITION (glyphs[i].d.y); + this_x = POSITION (glyphs[i].d.x); + this_y = POSITION (glyphs[i].d.y); /* Send unsent glyphs to the server */ if (glyph->dev_private_key != display) { diff --git a/src/cairo.h b/src/cairo.h index 89e4a070e..c0ba973f7 100644 --- a/src/cairo.h +++ b/src/cairo.h @@ -1437,6 +1437,25 @@ typedef enum _cairo_color_mode { CAIRO_COLOR_MODE_COLOR } cairo_color_mode_t; +/** + * cairo_subpixel_positions_t: + * @CAIRO_SUBPIXEL_POSITONS_DEFAULT: Use subpixel positions in + * the default manner for the font backend and target device, since 1.20 + * @CAIRO_SUBPIXEL_POSITIONS_OFF: Do not use subpixel positions, since 1.20 + * @CAIRO_SUBPIXEL_POSITIONS_ON: Use subpixel positions, since 1.20 + * + * Specifies whether to use subpixel positions when rendering glyphs. + * Without subpixel positions, glyph positions will be rounded to pixels. + * With subpixel positions, glyph positions rounded to a subpixel grid. + * + * Since: 1.20 + */ +typedef enum _cairo_subpixel_positions { + CAIRO_SUBPIXEL_POSITIONS_DEFAULT, + CAIRO_SUBPIXEL_POSITIONS_OFF, + CAIRO_SUBPIXEL_POSITIONS_ON, +} cairo_subpixel_positions_t; + /** * cairo_font_options_t: * @@ -1542,6 +1561,13 @@ cairo_font_options_get_custom_palette_color (cairo_font_options_t *options, double *red, double *green, double *blue, double *alpha); +cairo_public cairo_subpixel_positions_t +cairo_font_options_get_subpixel_positions (const cairo_font_options_t *options); + +cairo_public void +cairo_font_options_set_subpixel_positions (cairo_font_options_t *options, + cairo_subpixel_positions_t value); + /* This interface is for dealing with text as text, not caring about the font object inside the cairo_t. */ diff --git a/src/win32/cairo-dwrite-font.cpp b/src/win32/cairo-dwrite-font.cpp index bf5191967..a8c6ba654 100644 --- a/src/win32/cairo-dwrite-font.cpp +++ b/src/win32/cairo-dwrite-font.cpp @@ -1109,8 +1109,12 @@ _cairo_dwrite_scaled_font_init_glyph_color_surface(cairo_dwrite_scaled_font_t *s FLOAT advance = 0; UINT16 index = (UINT16)_cairo_scaled_glyph_index (scaled_glyph); DWRITE_GLYPH_OFFSET offset; - double x = -x1 + .25 * _cairo_scaled_glyph_xphase (scaled_glyph); - double y = -y1 + .25 * _cairo_scaled_glyph_yphase (scaled_glyph); + double x = -x1; + double y = -y1; + if (scaled_font->base.options.subpixel_positions != CAIRO_SUBPIXEL_POSITIONS_OFF) { + x += .25 * _cairo_scaled_glyph_xphase (scaled_glyph); + y += .25 * _cairo_scaled_glyph_yphase (scaled_glyph); + } DWRITE_MATRIX matrix; D2D1_POINT_2F origin = {0, 0}; RefPtr run_enumerator; From 3625b20901a1a02e8935ebe3c1a81ef6b136958c Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sat, 22 Mar 2025 10:36:42 -0400 Subject: [PATCH 2/4] Improve the subpixel positioning machinery Extract the resolution of the subpixel grid. This is in preparation for allowing a finer subpixel grid. No functional change. --- src/cairo-ft-font.c | 5 ++-- src/cairo-image-compositor.c | 17 +++++------- src/cairo-user-font.c | 5 ++-- src/cairo-xlib-render-compositor.c | 20 +++++++------- src/cairoint.h | 44 +++++++++++++++++++++++++++--- src/win32/cairo-dwrite-font.cpp | 5 ++-- 6 files changed, 66 insertions(+), 30 deletions(-) diff --git a/src/cairo-ft-font.c b/src/cairo-ft-font.c index b5d08ee1b..74f957b73 100644 --- a/src/cairo-ft-font.c +++ b/src/cairo-ft-font.c @@ -2449,10 +2449,11 @@ _cairo_ft_scaled_glyph_load_glyph (cairo_ft_scaled_font_t *scaled_font, _cairo_ft_scaled_glyph_vertical_layout_bearing_fix (scaled_font, face->glyph); if (face->glyph->format == FT_GLYPH_FORMAT_OUTLINE) { + unsigned int bits = _cairo_subpixel_bits (scaled_font->base.options.subpixel_positions); FT_Pos xshift, yshift; - xshift = _cairo_scaled_glyph_xphase (scaled_glyph) << 4; - yshift = _cairo_scaled_glyph_yphase (scaled_glyph) << 4; + xshift = _cairo_scaled_glyph_xphase (scaled_glyph) << (6 - bits); + yshift = _cairo_scaled_glyph_yphase (scaled_glyph) << (6 - bits); FT_Outline_Translate (&face->glyph->outline, xshift, -yshift); } diff --git a/src/cairo-image-compositor.c b/src/cairo-image-compositor.c index ff90f094b..98f3a35b6 100644 --- a/src/cairo-image-compositor.c +++ b/src/cairo-image-compositor.c @@ -845,9 +845,6 @@ _cairo_image_scaled_glyph_fini (cairo_scaled_font_t *scaled_font, CAIRO_MUTEX_UNLOCK (_cairo_glyph_cache_mutex); } -#define PHASE(x) ((int)(floor (4 * (x + 0.125)) - 4 * floor (x + 0.125))) -#define POSITION(x) ((int) floor (x + 0.125)) - static cairo_int_status_t composite_glyphs (void *_dst, cairo_operator_t op, @@ -891,12 +888,11 @@ composite_glyphs (void *_dst, const void *glyph; if (info->font->options.subpixel_positions != CAIRO_SUBPIXEL_POSITIONS_OFF) { + int res = _cairo_subpixel_resolution (info->font->options.subpixel_positions); unsigned long xphase, yphase; - - xphase = PHASE(info->glyphs[i].x); - yphase = PHASE(info->glyphs[i].y); - - index = index | (xphase << 24) | (yphase << 26); + xphase = _cairo_subpixel_phase (info->glyphs[i].x, res); + yphase = _cairo_subpixel_phase (info->glyphs[i].y, res); + index = index | (xphase << XSHIFT) | (yphase << YSHIFT); } glyph = pixman_glyph_cache_lookup (glyph_cache, info->font, (void *)(uintptr_t)index); @@ -929,8 +925,9 @@ composite_glyphs (void *_dst, } if (info->font->options.subpixel_positions != CAIRO_SUBPIXEL_POSITIONS_OFF) { - pg->x = POSITION (info->glyphs[i].x); - pg->y = POSITION (info->glyphs[i].y); + int res = _cairo_subpixel_resolution (info->font->options.subpixel_positions); + pg->x = _cairo_subpixel_position (info->glyphs[i].x, res); + pg->y = _cairo_subpixel_position (info->glyphs[i].y, res); } else { pg->x = _cairo_lround (info->glyphs[i].x); diff --git a/src/cairo-user-font.c b/src/cairo-user-font.c index 400158341..01b6b378d 100644 --- a/src/cairo-user-font.c +++ b/src/cairo-user-font.c @@ -208,8 +208,9 @@ _cairo_user_scaled_glyph_init_record_glyph (cairo_user_scaled_font_t *scaled_fon cairo_surface_destroy (recording_surface); recording_surface = _cairo_user_scaled_font_create_recording_surface (scaled_font, FALSE, foreground_color); if (scaled_font->base.options.subpixel_positions != CAIRO_SUBPIXEL_POSITIONS_OFF) { - recording_surface->device_transform.x0 = .25 * _cairo_scaled_glyph_xphase (scaled_glyph); - recording_surface->device_transform.y0 = .25 * _cairo_scaled_glyph_yphase (scaled_glyph); + double res = _cairo_subpixel_resolution (scaled_font->base.options.subpixel_positions); + recording_surface->device_transform.x0 = _cairo_scaled_glyph_xphase (scaled_glyph) / res; + recording_surface->device_transform.y0 = _cairo_scaled_glyph_yphase (scaled_glyph) / res; } cr = _cairo_user_scaled_font_create_recording_context (scaled_font, recording_surface, FALSE); diff --git a/src/cairo-xlib-render-compositor.c b/src/cairo-xlib-render-compositor.c index 2d8b29138..4c7e509e7 100644 --- a/src/cairo-xlib-render-compositor.c +++ b/src/cairo-xlib-render-compositor.c @@ -1570,9 +1570,6 @@ check_composite_glyphs (const cairo_composite_rectangles_t *extents, * enough room for padding */ #define _cairo_sz_xGlyphElt (sz_xGlyphElt + 4) -#define PHASE(x) ((int)(floor (4 * (x + 0.125)) - 4 * floor (x + 0.125))) -#define POSITION(x) ((int) floor (x + 0.125)) - static cairo_int_status_t composite_glyphs (void *surface, cairo_operator_t op, @@ -1612,12 +1609,18 @@ composite_glyphs (void *surface, int old_width; if (info->font->options.subpixel_positions != CAIRO_SUBPIXEL_POSITIONS_OFF) { + int res = _cairo_subpixel_resolution (info->font->options.subpixel_positions); unsigned long xphase, yphase; + xphase = _cairo_subpixel_phase (glyphs[i].d.x, res); + yphase = _cairo_subpixel_phase (glyphs[i].d.y, res); + glyphs[i].index |= (xphase << XSHIFT) | (yphase << YSHIFT); - xphase = PHASE(glyphs[i].d.x); - yphase = PHASE(glyphs[i].d.y); - - glyphs[i].index |= (xphase << 24) | (yphase << 26); + this_x = _cairo_subpixel_position (glyphs[i].d.x, res); + this_y = _cairo_subpixel_position (glyphs[i].d.y, res); + } + else { + this_x = _cairo_lround (glyphs[i].d.x); + this_y = _cairo_lround (glyphs[i].d.y); } status = _cairo_scaled_glyph_lookup (info->font, @@ -1628,9 +1631,6 @@ composite_glyphs (void *surface, if (unlikely (status)) return status; - this_x = POSITION (glyphs[i].d.x); - this_y = POSITION (glyphs[i].d.y); - /* Send unsent glyphs to the server */ if (glyph->dev_private_key != display) { status = _cairo_xlib_surface_add_glyph (display, info->font, &glyph); diff --git a/src/cairoint.h b/src/cairoint.h index be1a4ba2a..1552139da 100644 --- a/src/cairoint.h +++ b/src/cairoint.h @@ -400,10 +400,46 @@ cairo_private uintptr_t _cairo_hash_uintptr (uintptr_t hash, uintptr_t u); -/* We use bits 24-27 to store phases for subpixel positions */ -#define _cairo_scaled_glyph_index(g) ((unsigned long)((g)->hash_entry.hash & 0xffffff)) -#define _cairo_scaled_glyph_xphase(g) (int)(((g)->hash_entry.hash >> 24) & 3) -#define _cairo_scaled_glyph_yphase(g) (int)(((g)->hash_entry.hash >> 26) & 3) +/* We use the upper 8 bits to store phases for 64x64 subpixel positions */ + +static inline int _cairo_subpixel_bits (cairo_subpixel_positions_t subpixel_positions) +{ + int bits[] = { + [CAIRO_SUBPIXEL_POSITIONS_DEFAULT] = 2, + [CAIRO_SUBPIXEL_POSITIONS_OFF] = 0, + [CAIRO_SUBPIXEL_POSITIONS_ON] = 2, + }; + + return bits[subpixel_positions]; +} + +static inline int _cairo_subpixel_resolution (cairo_subpixel_positions_t subpixel_positions) +{ + return 1 << _cairo_subpixel_bits (subpixel_positions); +} + +static inline int _cairo_subpixel_phase (double pos, int res) +{ + double shifted_pos = pos + 1.0 / (2 * res); + return (int) (floor (res * shifted_pos) - res * floor (shifted_pos)); +} + +static inline double _cairo_subpixel_position (double pos, int res) +{ + return floor (pos + 1.0 / (2 * res)); +} + +#define MAX_PHASE_BITS 4 + +#define XSHIFT (32 - 2 * MAX_PHASE_BITS) +#define YSHIFT (32 - MAX_PHASE_BITS) + +#define PHASE_MASK ((1 << MAX_PHASE_BITS) - 1) +#define INDEX_MASK ((1 << XSHIFT) - 1) + +#define _cairo_scaled_glyph_index(g) ((unsigned long)((g)->hash_entry.hash & INDEX_MASK)) +#define _cairo_scaled_glyph_xphase(g) (unsigned int)(((g)->hash_entry.hash >> XSHIFT) & PHASE_MASK) +#define _cairo_scaled_glyph_yphase(g) ((unsigned int)(((g)->hash_entry.hash >> YSHIFT) & PHASE_MASK)) #define _cairo_scaled_glyph_set_index(g, i) ((g)->hash_entry.hash = (i)) #include "cairo-scaled-font-private.h" diff --git a/src/win32/cairo-dwrite-font.cpp b/src/win32/cairo-dwrite-font.cpp index a8c6ba654..a7990a780 100644 --- a/src/win32/cairo-dwrite-font.cpp +++ b/src/win32/cairo-dwrite-font.cpp @@ -1112,8 +1112,9 @@ _cairo_dwrite_scaled_font_init_glyph_color_surface(cairo_dwrite_scaled_font_t *s double x = -x1; double y = -y1; if (scaled_font->base.options.subpixel_positions != CAIRO_SUBPIXEL_POSITIONS_OFF) { - x += .25 * _cairo_scaled_glyph_xphase (scaled_glyph); - y += .25 * _cairo_scaled_glyph_yphase (scaled_glyph); + double res = _cairo_subpixel_resolution (scaled_font->base.options.subpixel_positions); + x += _cairo_scaled_glyph_xphase (scaled_glyph) / res; + y += _cairo_scaled_glyph_yphase (scaled_glyph) / res; } DWRITE_MATRIX matrix; D2D1_POINT_2F origin = {0, 0}; From a5ca8899e421a7a69023d8fb9616defb3fbf3c8a Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sat, 22 Mar 2025 10:24:11 -0400 Subject: [PATCH 3/4] Optionally increase subpixel resolution Add CAIRO_SUBPIXEL_POSITIONS_FINE, for a 64x64 subpixel grid. This helps in GTK, where we found that a shift by 0.25 can be large enough to make the rasterized glyph touch new pixels, and that breaks our invalidation. --- src/cairo.h | 2 ++ src/cairoint.h | 1 + 2 files changed, 3 insertions(+) diff --git a/src/cairo.h b/src/cairo.h index c0ba973f7..23a02f605 100644 --- a/src/cairo.h +++ b/src/cairo.h @@ -1443,6 +1443,7 @@ typedef enum _cairo_color_mode { * the default manner for the font backend and target device, since 1.20 * @CAIRO_SUBPIXEL_POSITIONS_OFF: Do not use subpixel positions, since 1.20 * @CAIRO_SUBPIXEL_POSITIONS_ON: Use subpixel positions, since 1.20 + * @CAIRO_SUBPIXEL_POSITIONS_FINE: Use fine subpixel positions, since 1.20 * * Specifies whether to use subpixel positions when rendering glyphs. * Without subpixel positions, glyph positions will be rounded to pixels. @@ -1454,6 +1455,7 @@ typedef enum _cairo_subpixel_positions { CAIRO_SUBPIXEL_POSITIONS_DEFAULT, CAIRO_SUBPIXEL_POSITIONS_OFF, CAIRO_SUBPIXEL_POSITIONS_ON, + CAIRO_SUBPIXEL_POSITIONS_FINE } cairo_subpixel_positions_t; /** diff --git a/src/cairoint.h b/src/cairoint.h index 1552139da..20fc287b2 100644 --- a/src/cairoint.h +++ b/src/cairoint.h @@ -408,6 +408,7 @@ static inline int _cairo_subpixel_bits (cairo_subpixel_positions_t subpixel_posi [CAIRO_SUBPIXEL_POSITIONS_DEFAULT] = 2, [CAIRO_SUBPIXEL_POSITIONS_OFF] = 0, [CAIRO_SUBPIXEL_POSITIONS_ON] = 2, + [CAIRO_SUBPIXEL_POSITIONS_FINE] = 4, }; return bits[subpixel_positions]; From 024617e5ecd199dfb426abb6c75a2c1ff6b01a7b Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sat, 22 Mar 2025 10:28:18 -0400 Subject: [PATCH 4/4] Don't cache glyphs with fine subpixel grid Having up to 16x16 variants of each glyph in the cache is a bit too much, and GTK does not need any caching, since it is doing its own caching in a texture atlas. --- src/cairo-image-compositor.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/cairo-image-compositor.c b/src/cairo-image-compositor.c index 98f3a35b6..88bb90ce3 100644 --- a/src/cairo-image-compositor.c +++ b/src/cairo-image-compositor.c @@ -960,6 +960,20 @@ composite_glyphs (void *_dst, glyph_cache, pg - pglyphs, pglyphs); } + /* Don't keep 64x64 versions of each glyph in the cache */ + if (info->font->options.subpixel_positions == CAIRO_SUBPIXEL_POSITIONS_FINE) { + int res = _cairo_subpixel_resolution (info->font->options.subpixel_positions); + for (i = 0; i < info->num_glyphs; i++) { + unsigned long index = info->glyphs[i].index; + unsigned long xphase, yphase; + xphase = _cairo_subpixel_phase (info->glyphs[i].x, res); + yphase = _cairo_subpixel_phase (info->glyphs[i].y, res); + index = index | (xphase << XSHIFT) | (yphase << YSHIFT); + + pixman_glyph_cache_remove (glyph_cache, info->font, (void *)(uintptr_t)index); + } + } + out_thaw: pixman_glyph_cache_thaw (glyph_cache);