Resolve a text draw's glyph origin once

This commit is contained in:
iris-ai committed 2026-09-17 16:17:47 -04:00
1 parent 7601aa2a5d
commit a7307d95fd
2 files changed
+93 -5

No files matched your search

+24 -5
View File
@@ -80,6 +80,21 @@ impl<'a> Painter<'a> {
/// Takes the kind, for a caller writing many of one primitive.
fn write<P: Primitive>(&mut self, kind: PrimitiveKind<P>, primitive: P, region: DrawRegion) {
self.write_resolved(
kind,
primitive,
region,
region.resolve(self.region, self.placement),
);
}
fn write_resolved<P: Primitive>(
&mut self,
kind: PrimitiveKind<P>,
primitive: P,
region: DrawRegion,
resolved: UiRegion,
) {
#[cfg(feature = "layout-diagnostics")]
diag::bump(Counter::PrimitiveWrites);
let h = self.state.layers.write(
@@ -88,7 +103,7 @@ impl<'a> Painter<'a> {
kind,
id: self.id,
primitive,
region: region.resolve(self.region, self.placement),
region: resolved,
mask_idx: self.mask,
move_idx: self.move_idx,
},
@@ -404,9 +419,12 @@ impl<'a> Painter<'a> {
// TODO: merge the text methods into the primitive ones.
pub fn glyphs(&mut self, text: &RenderedText, origin: impl Into<DrawRegion>) {
let origin = origin.into();
// Glyph offsets and sizes are pixels, which compose additively.
// Only the shared origin needs the frame/extent composition.
let resolved = origin.resolve(self.region, self.placement);
let kind = self.rsc.ui_mut().primitives.kind::<GlyphPrimitive>();
for glyph in text.glyphs.iter() {
let region = origin.map(|mut region| {
let place = |mut region: UiRegion| {
region.x.end = region.x.start;
region.y.end = region.y.start;
let mut region = region.offset(UiVec2::from_px(glyph.offset));
@@ -417,8 +435,8 @@ impl<'a> Painter<'a> {
region.x.end = region.x.start.offset(size.x);
region.y.end = region.y.start.offset(size.y);
region
});
self.write(
};
self.write_resolved(
kind,
GlyphPrimitive {
uv_min: glyph.entry.uv_min,
@@ -427,7 +445,8 @@ impl<'a> Painter<'a> {
color: text.color,
flags: glyph.entry.flags(),
},
region,
origin.map(place),
place(resolved),
);
}
}
+69
View File
@@ -951,3 +951,72 @@ fn a_span_does_not_place_its_measurement_before_assigning_the_childs_slot() {
vec![h.region(&leaf.id()).unwrap()]
);
}
#[test]
fn glyph_origins_compose_identically_when_drawn_and_when_retained() {
struct Glyphs {
buffer: TextBuffer,
draws: Rc<Cell<usize>>,
}
impl Widget for Glyphs {
fn draw(&mut self, painter: &mut Painter) -> Size {
self.draws.set(self.draws.get() + 1);
let text = painter.render_text(&mut self.buffer, &TextAttrs::default(), None);
let origin = UiRegion::new(
UiSpan::new(Len::rel(0.23) + Len::px(-7.125), Len::FULL),
UiSpan::new(Len::rel(0.37) + Len::px(3.25), Len::FULL),
);
painter.glyphs(text, DrawRegion::Frame(origin));
painter.glyphs(text, DrawRegion::Extent(origin));
Size::LEFTOVER
}
}
struct Frame {
child: StrongWidget,
region: UiRegion,
extent: UiRegion,
}
impl Widget for Frame {
fn draw(&mut self, painter: &mut Painter) -> Size {
painter.widget_at(
&self.child,
self.region,
[Some(self.extent.x), Some(self.extent.y)],
);
Size::LEFTOVER
}
}
for node in [false, true] {
let mut h = Harness::new((403, 211));
let draws = Rc::new(Cell::new(0));
let text = Glyphs {
buffer: TextBuffer::new("Glyphs: gj AV\nsecond line"),
draws: draws.clone(),
}
.add(&mut h.rsc);
h.rsc.widgets_mut().set_region_node(text, node);
let root = Frame {
child: text.add_strong(&mut h.rsc),
region: UiRegion::FULL,
extent: UiRegion::FULL,
}
.add(&mut h.rsc);
h.set_root(root);
for (start, end) in [(0.13, 0.83), (-0.17, 1.23), (0.31, 0.67)] {
let before = draws.get();
h.rsc[root].region.x = UiSpan::new(Len::px(13.125), Len::px(287.375));
h.rsc[root].extent = UiRegion::new(
UiSpan::new(Len::rel(start), Len::rel(end)),
UiSpan::new(Len::px(7.25), Len::rel(end)),
);
h.frame();
assert_eq!(draws.get(), before);
let retained = primitive_bounds(&h, text.id());
assert!(!retained.is_empty());
let _ = h.rsc.widgets_mut().get_dyn_mut(text.id());
h.frame();
assert!(draws.get() > before);
assert_eq!(retained, primitive_bounds(&h, text.id()));
}
}
}