diff --git a/crates/epaint/src/text/font.rs b/crates/epaint/src/text/font.rs index 7d71434bf..4e342560f 100644 --- a/crates/epaint/src/text/font.rs +++ b/crates/epaint/src/text/font.rs @@ -556,7 +556,6 @@ impl FontFace { glyph_id, advance_width_px, h_pos, - y_offset_points, is_cjk, } = *shaped; @@ -574,28 +573,28 @@ impl FontFace { if let Some(cached) = self.glyph_alloc_cache.get(&cache_key) { let mut alloc = *cached; alloc.advance_width_px = advance_width_px; - alloc.uv_rect.offset.y += y_offset_points; - return (alloc, h_pos_round); + (alloc, h_pos_round) + } else { + let glyph_info = GlyphInfo { + id: Some(glyph_id), + advance_width_unscaled: OrderedFloat(advance_width_px / metrics.px_scale_factor), + }; + + let alloc = self + .font + .allocate_glyph_uncached( + atlas, + metrics, + &glyph_info, + bin, + (&metrics.location).into(), + ) + .unwrap_or_default(); + + self.glyph_alloc_cache.insert(cache_key, alloc); + + (alloc, h_pos_round) } - - let glyph_info = GlyphInfo { - id: Some(glyph_id), - advance_width_unscaled: OrderedFloat(advance_width_px / metrics.px_scale_factor), - }; - - let mut allocation = self - .font - .allocate_glyph_uncached(atlas, metrics, &glyph_info, bin, (&metrics.location).into()) - .unwrap_or_default(); - - // Cache the allocation WITHOUT the shaper y_offset (which varies per call) - self.glyph_alloc_cache.insert(cache_key, allocation); - - // Apply shaper y_offset after caching — the offset varies per call site - // so we cache the base allocation without it. - allocation.uv_rect.offset.y += y_offset_points; - - (allocation, h_pos_round) } } @@ -610,9 +609,6 @@ pub(crate) struct ShapedGlyph { /// Horizontal position of the glyph origin, in physical pixels. pub h_pos: f32, - /// Vertical offset from the baseline, in UI points. - pub y_offset_points: f32, - /// CJK glyphs skip subpixel positioning to save atlas space. pub is_cjk: bool, } diff --git a/crates/epaint/src/text/text_layout.rs b/crates/epaint/src/text/text_layout.rs index 25bbfc2ec..4791abb63 100644 --- a/crates/epaint/src/text/text_layout.rs +++ b/crates/epaint/src/text/text_layout.rs @@ -214,7 +214,7 @@ fn layout_shaped_run( { let glyph_id = skrifa::GlyphId::new(info.glyph_id); let cluster = info.cluster; - let mut x_advance_px = pos.x_advance as f32 * px_scale; + let mut advance_width_px = pos.x_advance as f32 * px_scale; let x_offset_px = pos.x_offset as f32 * px_scale; let y_offset_px = -(pos.y_offset as f32 * px_scale); // harfrust Y+ up → screen Y+ down @@ -227,7 +227,7 @@ fn layout_shaped_run( // Override the advance width to TAB_SIZE × space width. if chr == '\t' { let (_, space_info) = font.glyph_info(' '); - x_advance_px = + advance_width_px = crate::text::TAB_SIZE as f32 * space_info.advance_width_unscaled.0 * px_scale; } @@ -259,17 +259,19 @@ fn layout_shaped_run( ff.styled_metrics(ctx.pixels_per_point, ctx.font_size, &Default::default()) }) .unwrap_or_default(); - let shaped = ShapedGlyph { - glyph_id: glyph_info.id.unwrap_or(skrifa::GlyphId::NOTDEF), - advance_width_px: glyph_info.advance_width_unscaled.0 - * fallback_metrics.px_scale_factor, - h_pos: paragraph.cursor_x_px, - y_offset_points: 0.0, - is_cjk: is_cjk(chr), - }; let (glyph_alloc, physical_x) = if let Some(ff) = font.fonts_by_id.get_mut(&fallback_key) { - ff.allocate_glyph(font.atlas, &fallback_metrics, &shaped) + ff.allocate_glyph( + font.atlas, + &fallback_metrics, + &ShapedGlyph { + glyph_id: glyph_info.id.unwrap_or(skrifa::GlyphId::NOTDEF), + advance_width_px: glyph_info.advance_width_unscaled.0 + * fallback_metrics.px_scale_factor, + h_pos: paragraph.cursor_x_px, + is_cjk: is_cjk(chr), + }, + ) } else { Default::default() }; @@ -289,25 +291,30 @@ fn layout_shaped_run( }); paragraph.cursor_x_px += glyph_alloc.advance_width_px; } else { - let shaped = ShapedGlyph { - glyph_id, - advance_width_px: x_advance_px, - h_pos: paragraph.cursor_x_px + x_offset_px, - y_offset_points: y_offset_px / ctx.pixels_per_point, - is_cjk: is_cjk(chr), - }; - - let (glyph_alloc, physical_x) = + let (mut glyph_alloc, physical_x) = if let Some(ff) = font.fonts_by_id.get_mut(&run.font_key) { - ff.allocate_glyph(font.atlas, face_metrics, &shaped) + ff.allocate_glyph( + font.atlas, + face_metrics, + &ShapedGlyph { + glyph_id, + advance_width_px, + h_pos: paragraph.cursor_x_px + x_offset_px, + is_cjk: is_cjk(chr), + }, + ) } else { Default::default() }; + // Apply shaper y_offset — this varies per glyph instance so it + // is not part of the cached ShapedGlyph / GlyphAllocation. + glyph_alloc.uv_rect.offset.y += y_offset_px / ctx.pixels_per_point; + paragraph.glyphs.push(Glyph { chr, pos: pos2(physical_x as f32 / ctx.pixels_per_point, f32::NAN), - advance_width: x_advance_px / ctx.pixels_per_point, + advance_width: advance_width_px / ctx.pixels_per_point, line_height: ctx.line_height, font_face_height: face_metrics.row_height, font_face_ascent: face_metrics.ascent, @@ -317,7 +324,7 @@ fn layout_shaped_run( section_index: ctx.section_index, first_vertex: 0, }); - paragraph.cursor_x_px += x_advance_px; + paragraph.cursor_x_px += advance_width_px; } } } @@ -698,17 +705,21 @@ fn replace_last_glyph_with_overflow_character( { // we are done - let shaped = ShapedGlyph { - glyph_id: glyph_info.id.unwrap_or(skrifa::GlyphId::NOTDEF), - advance_width_px: glyph_info.advance_width_unscaled.0 - * font_face_metrics.px_scale_factor, - h_pos: overflow_glyph_x * pixels_per_point, - y_offset_points: 0.0, - is_cjk: is_cjk(overflow_character), - }; let (replacement_glyph_alloc, physical_x) = font_face .as_mut() - .map(|f| f.allocate_glyph(font.atlas, &font_face_metrics, &shaped)) + .map(|f| { + f.allocate_glyph( + font.atlas, + &font_face_metrics, + &ShapedGlyph { + glyph_id: glyph_info.id.unwrap_or(skrifa::GlyphId::NOTDEF), + advance_width_px: glyph_info.advance_width_unscaled.0 + * font_face_metrics.px_scale_factor, + h_pos: overflow_glyph_x * pixels_per_point, + is_cjk: is_cjk(overflow_character), + }, + ) + }) .unwrap_or_default(); let font_metrics =