From a212cf56bd7408f2e540b53c575658de4b4ed3a1 Mon Sep 17 00:00:00 2001 From: Pierre Le Fevre Date: Sat, 18 Jul 2026 21:07:51 +0800 Subject: [PATCH] Scale fractional glyph atlas quads References isu issue 281. --- crates/render/src/atlas.rs | 45 +++++++++++++++++++++++++++++++++----- 1 file changed, 39 insertions(+), 6 deletions(-) diff --git a/crates/render/src/atlas.rs b/crates/render/src/atlas.rs index 60903ee..9ce5218 100644 --- a/crates/render/src/atlas.rs +++ b/crates/render/src/atlas.rs @@ -375,6 +375,12 @@ impl GlyphAtlas { // the device-pixel bitmap metrics back down by `scale`. Shaping uses // the logical size for advances/offsets. let render_px = size_px * scale; + let raster_px = GlyphCache::quantize_size(render_px) as f32; + let bitmap_to_logical = if raster_px > 0.0 { + (render_px / raster_px) / scale + } else { + 1.0 / scale + }; let shaped = font.shape_text(&line.text, size_px); let color = color_to_f32(&line.color); let synthesize_bold = should_synthesize_bold(line, font); @@ -404,8 +410,8 @@ impl GlyphAtlas { let gx = line.x + sg.x_offset + line.letter_spacing * glyph_index as f32 - + region.bearing_x as f32 / scale; - let gy = baseline - region.bearing_y as f32 / scale; + + region.bearing_x as f32 * bitmap_to_logical; + let gy = baseline - region.bearing_y as f32 * bitmap_to_logical; // UV coordinates in the atlas page. let u0 = region.x as f32 / page_w as f32; @@ -416,8 +422,8 @@ impl GlyphAtlas { quads.push(TexturedQuad { x: gx, y: gy, - width: region.width as f32 / scale, - height: region.height as f32 / scale, + width: region.width as f32 * bitmap_to_logical, + height: region.height as f32 * bitmap_to_logical, u0, v0, u1, @@ -435,8 +441,8 @@ impl GlyphAtlas { quads.push(TexturedQuad { x: gx + offset, y: gy, - width: region.width as f32 / scale, - height: region.height as f32 / scale, + width: region.width as f32 * bitmap_to_logical, + height: region.height as f32 * bitmap_to_logical, u0, v0, u1, @@ -749,6 +755,33 @@ mod tests { ); } + #[test] + fn text_quads_scale_quantized_bitmaps_to_requested_fractional_size() { + let font = we_text::font::load_system_font().expect("system font"); + let mut atlas = GlyphAtlas::with_page_size(256); + + let mut whole = text_line("H", 0.0); + whole.font_size = 16.0; + whole.baseline_offset = 16.0; + let whole_quads = atlas.build_text_quads(&whole, &font, 1.0); + + let mut fractional = text_line("H", 0.0); + fractional.font_size = 15.5; + fractional.baseline_offset = 15.5; + let fractional_quads = atlas.build_text_quads(&fractional, &font, 1.0); + + if whole_quads.is_empty() || fractional_quads.is_empty() { + return; + } + + let expected = fractional.font_size / whole.font_size; + let actual = fractional_quads[0].height / whole_quads[0].height; + assert!( + (actual - expected).abs() < 0.02, + "fractional glyph quad height should scale by {expected}, got {actual}" + ); + } + #[test] fn synthetic_bold_is_skipped_for_resolved_bold_face() { let Some((regular, bold)) = regular_and_bold_system_faces() else { -- 2.51.2