textlayout: Add the ability for the line callback to break with a value

This commit is contained in:
Simon Hausmann 2023-04-20 15:10:28 +02:00 committed by Florian Blasius
parent 7fe4cb39aa
commit c8c39e82b4
2 changed files with 109 additions and 77 deletions

View file

@ -1164,65 +1164,68 @@ impl<'a, T: ProcessScene> SceneBuilder<'a, T> {
Font: crate::textlayout::TextShaper<Length = PhysicalLength>,
Font: GlyphRenderer,
{
paragraph.layout_lines(|glyphs, line_x, line_y| {
let baseline_y = line_y + paragraph.layout.font.ascent();
while let Some(positioned_glyph) = glyphs.next() {
let glyph = paragraph.layout.font.render_glyph(positioned_glyph.glyph_id);
paragraph
.layout_lines::<()>(|glyphs, line_x, line_y| {
let baseline_y = line_y + paragraph.layout.font.ascent();
while let Some(positioned_glyph) = glyphs.next() {
let glyph = paragraph.layout.font.render_glyph(positioned_glyph.glyph_id);
let src_rect = PhysicalRect::new(
PhysicalPoint::from_lengths(
line_x + positioned_glyph.x + glyph.x,
baseline_y - glyph.y - glyph.height,
),
glyph.size(),
)
.cast();
let src_rect = PhysicalRect::new(
PhysicalPoint::from_lengths(
line_x + positioned_glyph.x + glyph.x,
baseline_y - glyph.y - glyph.height,
),
glyph.size(),
)
.cast();
if let Some(clipped_src) = src_rect.intersection(&physical_clip) {
let geometry = clipped_src.translate(offset).round();
let origin = (geometry.origin - offset.round()).cast::<usize>();
let actual_x = origin.x - src_rect.origin.x as usize;
let actual_y = origin.y - src_rect.origin.y as usize;
let stride = glyph.width.get() as u16;
let geometry = geometry.cast();
if let Some(clipped_src) = src_rect.intersection(&physical_clip) {
let geometry = clipped_src.translate(offset).round();
let origin = (geometry.origin - offset.round()).cast::<usize>();
let actual_x = origin.x - src_rect.origin.x as usize;
let actual_y = origin.y - src_rect.origin.y as usize;
let stride = glyph.width.get() as u16;
let geometry = geometry.cast();
match &glyph.alpha_map {
fonts::GlyphAlphaMap::Static(data) => {
self.processor.process_texture(
geometry,
SceneTexture {
data: &data[actual_x + actual_y * stride as usize..],
stride,
source_size: geometry.size,
format: PixelFormat::AlphaMap,
color,
// color already is mixed with global alpha
alpha: color.alpha(),
},
);
}
fonts::GlyphAlphaMap::Shared(data) => {
self.processor.process_shared_image_buffer(
geometry,
SharedBufferCommand {
buffer: SharedBufferData::AlphaMap {
data: data.clone(),
width: stride,
match &glyph.alpha_map {
fonts::GlyphAlphaMap::Static(data) => {
self.processor.process_texture(
geometry,
SceneTexture {
data: &data[actual_x + actual_y * stride as usize..],
stride,
source_size: geometry.size,
format: PixelFormat::AlphaMap,
color,
// color already is mixed with global alpha
alpha: color.alpha(),
},
source_rect: PhysicalRect::new(
PhysicalPoint::new(actual_x as _, actual_y as _),
geometry.size,
),
colorize: color,
// color already is mixed with global alpha
alpha: color.alpha(),
},
);
}
};
);
}
fonts::GlyphAlphaMap::Shared(data) => {
self.processor.process_shared_image_buffer(
geometry,
SharedBufferCommand {
buffer: SharedBufferData::AlphaMap {
data: data.clone(),
width: stride,
},
source_rect: PhysicalRect::new(
PhysicalPoint::new(actual_x as _, actual_y as _),
geometry.size,
),
colorize: color,
// color already is mixed with global alpha
alpha: color.alpha(),
},
);
}
};
}
}
}
});
core::ops::ControlFlow::Continue(())
})
.ok();
}
/// Returns the color, mixed with the current_state's alpha

View file

@ -99,15 +99,15 @@ pub struct TextParagraphLayout<'a, Font: AbstractFont> {
impl<'a, Font: AbstractFont> TextParagraphLayout<'a, Font> {
/// Layout the given string in lines, and call the `layout_line` callback with the line to draw at position y.
/// The signature of the `layout_line` function is: `(glyph_iterator, line_x, line_y)`.
/// Returns the baseline y coordinate.
pub fn layout_lines(
/// Returns the baseline y coordinate as Ok, or the break value if `line_callback` returns `core::ops::ControlFlow::Break`.
pub fn layout_lines<R>(
&self,
mut line_callback: impl FnMut(
&mut dyn Iterator<Item = PositionedGlyph<Font::Length>>,
Font::Length,
Font::Length,
),
) -> Font::Length {
) -> core::ops::ControlFlow<R>,
) -> Result<Font::Length, R> {
let wrap = self.wrap == TextWrap::WordWrap;
let elide_glyph = if self.overflow == TextOverflow::Elide {
self.layout.font.glyph_for_char('…').filter(|glyph| glyph.glyph_id.is_some())
@ -186,21 +186,35 @@ impl<'a, Font: AbstractFont> TextParagraphLayout<'a, Font> {
})
});
line_callback(&mut positioned_glyph_it, x, y);
if let core::ops::ControlFlow::Break(break_val) =
line_callback(&mut positioned_glyph_it, x, y)
{
return core::ops::ControlFlow::Break(break_val);
}
y += self.layout.font.height();
core::ops::ControlFlow::Continue(())
};
if let Some(lines_vec) = text_lines.take() {
for line in lines_vec {
process_line(&line, &shape_buffer.glyphs);
if let core::ops::ControlFlow::Break(break_val) =
process_line(&line, &shape_buffer.glyphs)
{
return Err(break_val);
}
}
} else {
for line in new_line_break_iter() {
process_line(&line, &shape_buffer.glyphs);
if let core::ops::ControlFlow::Break(break_val) =
process_line(&line, &shape_buffer.glyphs)
{
return Err(break_val);
}
}
}
baseline_y
Ok(baseline_y)
}
}
@ -283,11 +297,16 @@ fn test_elision() {
overflow: TextOverflow::Elide,
single_line: true,
};
paragraph.layout_lines(|glyphs, _, _| {
lines.push(
glyphs.map(|positioned_glyph| positioned_glyph.glyph_id.clone()).collect::<Vec<_>>(),
);
});
paragraph
.layout_lines::<()>(|glyphs, _, _| {
lines.push(
glyphs
.map(|positioned_glyph| positioned_glyph.glyph_id.clone())
.collect::<Vec<_>>(),
);
core::ops::ControlFlow::Continue(())
})
.unwrap();
assert_eq!(lines.len(), 1);
let rendered_text = lines[0]
@ -319,11 +338,16 @@ fn test_exact_fit() {
overflow: TextOverflow::Elide,
single_line: true,
};
paragraph.layout_lines(|glyphs, _, _| {
lines.push(
glyphs.map(|positioned_glyph| positioned_glyph.glyph_id.clone()).collect::<Vec<_>>(),
);
});
paragraph
.layout_lines::<()>(|glyphs, _, _| {
lines.push(
glyphs
.map(|positioned_glyph| positioned_glyph.glyph_id.clone())
.collect::<Vec<_>>(),
);
core::ops::ControlFlow::Continue(())
})
.unwrap();
assert_eq!(lines.len(), 1);
let rendered_text = lines[0]
@ -355,11 +379,16 @@ fn test_no_line_separators_characters_rendered() {
overflow: TextOverflow::Clip,
single_line: true,
};
paragraph.layout_lines(|glyphs, _, _| {
lines.push(
glyphs.map(|positioned_glyph| positioned_glyph.glyph_id.clone()).collect::<Vec<_>>(),
);
});
paragraph
.layout_lines::<()>(|glyphs, _, _| {
lines.push(
glyphs
.map(|positioned_glyph| positioned_glyph.glyph_id.clone())
.collect::<Vec<_>>(),
);
core::ops::ControlFlow::Continue(())
})
.unwrap();
assert_eq!(lines.len(), 2);
let rendered_text = lines