Make basic text shaping the default shaping strategy

This commit is contained in:
Héctor Ramón Jiménez 2023-04-19 01:19:56 +02:00
parent 57a276e165
commit 33b5a90019
No known key found for this signature in database
GPG key ID: 140CC052C94F138E
24 changed files with 140 additions and 10 deletions

View file

@ -62,6 +62,7 @@ impl text::Renderer for Null {
_size: f32,
_font: Font,
_bounds: Size,
_needs_shaping: bool,
) -> (f32, f32) {
(0.0, 20.0)
}
@ -74,6 +75,7 @@ impl text::Renderer for Null {
_bounds: Size,
_point: Point,
_nearest_only: bool,
_advanced_shape: bool,
) -> Option<text::Hit> {
None
}

View file

@ -27,6 +27,15 @@ pub struct Text<'a, Font> {
/// The vertical alignment of the [`Text`].
pub vertical_alignment: alignment::Vertical,
/// Whether the [`Text`] needs advanced shaping and font fallback.
///
/// You will need to enable this flag if the text contains a complex
/// script, the font used needs it, and/or multiple fonts in your system
/// may be needed to display all of the glyphs.
///
/// Advanced shaping is expensive! You should only enable it when necessary.
pub advanced_shape: bool,
}
/// The result of hit testing on text.
@ -77,11 +86,19 @@ pub trait Renderer: crate::Renderer {
size: f32,
font: Self::Font,
bounds: Size,
advanced_shape: bool,
) -> (f32, f32);
/// Measures the width of the text as if it were laid out in a single line.
fn measure_width(&self, content: &str, size: f32, font: Self::Font) -> f32 {
let (width, _) = self.measure(content, size, font, Size::INFINITY);
fn measure_width(
&self,
content: &str,
size: f32,
font: Self::Font,
advanced_shape: bool,
) -> f32 {
let (width, _) =
self.measure(content, size, font, Size::INFINITY, advanced_shape);
width
}
@ -101,6 +118,7 @@ pub trait Renderer: crate::Renderer {
bounds: Size,
point: Point,
nearest_only: bool,
advanced_shape: bool,
) -> Option<Hit>;
/// Loads a [`Self::Font`] from its bytes.

View file

@ -25,6 +25,7 @@ where
vertical_alignment: alignment::Vertical,
font: Option<Renderer::Font>,
style: <Renderer::Theme as StyleSheet>::Style,
advanced_shape: bool,
}
impl<'a, Renderer> Text<'a, Renderer>
@ -43,6 +44,7 @@ where
horizontal_alignment: alignment::Horizontal::Left,
vertical_alignment: alignment::Vertical::Top,
style: Default::default(),
advanced_shape: false,
}
}
@ -98,6 +100,20 @@ where
self.vertical_alignment = alignment;
self
}
/// Enables advanced text shaping and font fallback for the [`Text`].
///
/// You will need to enable this if the text contains a complex script, the
/// font used needs it, and/or multiple fonts in your system may be needed
/// to display all of the glyphs.
///
/// If your text isn't displaying properly, try enabling this!
///
/// Advanced shaping is expensive! You should only enable it when necessary.
pub fn advanced_shape(mut self) -> Self {
self.advanced_shape = true;
self
}
}
impl<'a, Message, Renderer> Widget<Message, Renderer> for Text<'a, Renderer>
@ -129,6 +145,7 @@ where
size,
self.font.unwrap_or_else(|| renderer.default_font()),
bounds,
self.advanced_shape,
);
let size = limits.resolve(Size::new(width, height));
@ -156,6 +173,7 @@ where
theme.appearance(self.style.clone()),
self.horizontal_alignment,
self.vertical_alignment,
self.advanced_shape,
);
}
}
@ -180,6 +198,7 @@ pub fn draw<Renderer>(
appearance: Appearance,
horizontal_alignment: alignment::Horizontal,
vertical_alignment: alignment::Vertical,
advanced_shape: bool,
) where
Renderer: text::Renderer,
{
@ -205,6 +224,7 @@ pub fn draw<Renderer>(
font: font.unwrap_or_else(|| renderer.default_font()),
horizontal_alignment,
vertical_alignment,
advanced_shape,
});
}
@ -234,6 +254,7 @@ where
vertical_alignment: self.vertical_alignment,
font: self.font,
style: self.style.clone(),
advanced_shape: self.advanced_shape,
}
}
}

View file

@ -48,6 +48,7 @@ pub trait Text {
size: f32,
font: Font,
bounds: Size,
advanced_shape: bool,
) -> (f32, f32);
/// Tests whether the provided point is within the boundaries of [`Text`]
@ -65,6 +66,7 @@ pub trait Text {
bounds: Size,
point: Point,
nearest_only: bool,
advanced_shape: bool,
) -> Option<text::Hit>;
/// Loads a [`Font`] from its bytes.

View file

@ -25,6 +25,15 @@ pub struct Text {
pub horizontal_alignment: alignment::Horizontal,
/// The vertical alignment of the text
pub vertical_alignment: alignment::Vertical,
/// Whether the text needs advanced shaping and font fallback.
///
/// You will need to enable this flag if the text contains a complex
/// script, the font used needs it, and/or multiple fonts in your system
/// may be needed to display all of the glyphs.
///
/// Advanced shaping is expensive! You should only enable it when
/// necessary.
pub advanced_shape: bool,
}
impl Default for Text {
@ -37,6 +46,7 @@ impl Default for Text {
font: Font::default(),
horizontal_alignment: alignment::Horizontal::Left,
vertical_alignment: alignment::Vertical::Top,
advanced_shape: false,
}
}
}

View file

@ -26,6 +26,15 @@ pub enum Primitive {
horizontal_alignment: alignment::Horizontal,
/// The vertical alignment of the text
vertical_alignment: alignment::Vertical,
/// Whether the text needs advanced shaping and font fallback.
///
/// You will need to enable this flag if the text contains a complex
/// script, the font used needs it, and/or multiple fonts in your system
/// may be needed to display all of the glyphs.
///
/// Advanced shaping is expensive! You should only enable it when
/// necessary.
advanced_shape: bool,
},
/// A quad primitive
Quad {

View file

@ -138,8 +138,10 @@ where
size: f32,
font: Font,
bounds: Size,
needs_shaping: bool,
) -> (f32, f32) {
self.backend().measure(content, size, font, bounds)
self.backend()
.measure(content, size, font, bounds, needs_shaping)
}
fn hit_test(
@ -150,6 +152,7 @@ where
bounds: Size,
point: Point,
nearest_only: bool,
advanced_shape: bool,
) -> Option<text::Hit> {
self.backend().hit_test(
content,
@ -158,6 +161,7 @@ where
bounds,
point,
nearest_only,
advanced_shape,
)
}
@ -174,6 +178,7 @@ where
font: text.font,
horizontal_alignment: text.horizontal_alignment,
vertical_alignment: text.vertical_alignment,
advanced_shape: text.advanced_shape,
});
}
}

View file

@ -48,8 +48,13 @@ impl backend::Text for Backend {
size: f32,
font: Font,
bounds: Size,
advanced_shape: bool,
) -> (f32, f32) {
delegate!(self, backend, backend.measure(contents, size, font, bounds))
delegate!(
self,
backend,
backend.measure(contents, size, font, bounds, advanced_shape)
)
}
fn hit_test(
@ -60,6 +65,7 @@ impl backend::Text for Backend {
bounds: Size,
position: Point,
nearest_only: bool,
advanced_shape: bool,
) -> Option<text::Hit> {
delegate!(
self,
@ -70,7 +76,8 @@ impl backend::Text for Backend {
font,
bounds,
position,
nearest_only
nearest_only,
advanced_shape
)
)
}

View file

@ -12,7 +12,6 @@ geometry = ["iced_graphics/geometry"]
raw-window-handle = "0.5"
softbuffer = "0.2"
tiny-skia = "0.9"
cosmic-text = "0.8"
bytemuck = "1"
rustc-hash = "1.1"
kurbo = "0.9"
@ -23,6 +22,10 @@ version = "0.8"
path = "../graphics"
features = ["tiny-skia"]
[dependencies.cosmic-text]
git = "https://github.com/hecrj/cosmic-text.git"
rev = "ad111a1df10d5da503620f4b841de5d41ebd4e73"
[dependencies.twox-hash]
version = "1.6"
default-features = false

View file

@ -219,6 +219,7 @@ impl Backend {
font,
horizontal_alignment,
vertical_alignment,
advanced_shape,
} => {
let physical_bounds =
(primitive.bounds() + translation) * scale_factor;
@ -238,6 +239,7 @@ impl Backend {
*font,
*horizontal_alignment,
*vertical_alignment,
*advanced_shape,
pixels,
clip_mask,
);
@ -626,8 +628,10 @@ impl backend::Text for Backend {
size: f32,
font: Font,
bounds: Size,
advanced_shape: bool,
) -> (f32, f32) {
self.text_pipeline.measure(contents, size, font, bounds)
self.text_pipeline
.measure(contents, size, font, bounds, advanced_shape)
}
fn hit_test(
@ -638,6 +642,7 @@ impl backend::Text for Backend {
bounds: Size,
point: Point,
nearest_only: bool,
advanced_shape: bool,
) -> Option<text::Hit> {
self.text_pipeline.hit_test(
contents,
@ -646,6 +651,7 @@ impl backend::Text for Backend {
bounds,
point,
nearest_only,
advanced_shape,
)
}

View file

@ -114,6 +114,7 @@ impl Frame {
font: text.font,
horizontal_alignment: text.horizontal_alignment,
vertical_alignment: text.vertical_alignment,
advanced_shape: text.advanced_shape,
});
}

View file

@ -49,6 +49,7 @@ impl Pipeline {
font: Font,
horizontal_alignment: alignment::Horizontal,
vertical_alignment: alignment::Vertical,
advanced_shape: bool,
pixels: &mut tiny_skia::PixmapMut<'_>,
clip_mask: Option<&tiny_skia::Mask>,
) {
@ -63,6 +64,7 @@ impl Pipeline {
content,
font,
size,
advanced_shape,
};
let (_, buffer) = self.render_cache.allocate(font_system, key);
@ -130,6 +132,7 @@ impl Pipeline {
size: f32,
font: Font,
bounds: Size,
advanced_shape: bool,
) -> (f32, f32) {
let mut measurement_cache = self.measurement_cache.borrow_mut();
@ -140,6 +143,7 @@ impl Pipeline {
size,
font,
bounds,
advanced_shape,
},
);
@ -161,6 +165,7 @@ impl Pipeline {
bounds: Size,
point: Point,
_nearest_only: bool,
advanced_shape: bool,
) -> Option<Hit> {
let mut measurement_cache = self.measurement_cache.borrow_mut();
@ -171,6 +176,7 @@ impl Pipeline {
size,
font,
bounds,
advanced_shape,
},
);
@ -390,6 +396,7 @@ impl Cache {
.family(to_family(key.font.family))
.weight(to_weight(key.font.weight))
.stretch(to_stretch(key.font.stretch)),
!key.advanced_shape,
);
let _ = entry.insert(buffer);
@ -420,6 +427,7 @@ struct Key<'a> {
size: f32,
font: Font,
bounds: Size,
advanced_shape: bool,
}
type KeyHash = u64;

View file

@ -44,7 +44,7 @@ path = "../graphics"
[dependencies.glyphon]
version = "0.2"
git = "https://github.com/hecrj/glyphon.git"
rev = "1d26d92b19407c5dabe4625944d4a6babbbf0715"
rev = "446cf0803065b52ba5fb9a30fe0addb6d7b5f9d9"
[dependencies.encase]
version = "0.3.0"

View file

@ -354,8 +354,10 @@ impl backend::Text for Backend {
size: f32,
font: Font,
bounds: Size,
advanced_shape: bool,
) -> (f32, f32) {
self.text_pipeline.measure(contents, size, font, bounds)
self.text_pipeline
.measure(contents, size, font, bounds, advanced_shape)
}
fn hit_test(
@ -366,6 +368,7 @@ impl backend::Text for Backend {
bounds: Size,
point: Point,
nearest_only: bool,
advanced_shape: bool,
) -> Option<core::text::Hit> {
self.text_pipeline.hit_test(
contents,
@ -374,6 +377,7 @@ impl backend::Text for Backend {
bounds,
point,
nearest_only,
advanced_shape,
)
}

View file

@ -334,6 +334,7 @@ impl Frame {
font: text.font,
horizontal_alignment: text.horizontal_alignment,
vertical_alignment: text.vertical_alignment,
advanced_shape: text.advanced_shape,
});
}

View file

@ -64,6 +64,7 @@ impl<'a> Layer<'a> {
font: Font::MONOSPACE,
horizontal_alignment: alignment::Horizontal::Left,
vertical_alignment: alignment::Vertical::Top,
advanced_shape: false,
};
overlay.text.push(text);
@ -116,6 +117,7 @@ impl<'a> Layer<'a> {
font,
horizontal_alignment,
vertical_alignment,
advanced_shape,
} => {
let layer = &mut layers[current_layer];
@ -127,6 +129,7 @@ impl<'a> Layer<'a> {
font: *font,
horizontal_alignment: *horizontal_alignment,
vertical_alignment: *vertical_alignment,
advanced_shape: *advanced_shape,
});
}
Primitive::Quad {

View file

@ -24,4 +24,14 @@ pub struct Text<'a> {
/// The vertical alignment of the [`Text`].
pub vertical_alignment: alignment::Vertical,
/// Whether the text needs advanced shaping and font fallback.
///
/// You will need to enable this flag if the text contains a complex
/// script, the font used needs it, and/or multiple fonts in your system
/// may be needed to display all of the glyphs.
///
/// Advanced shaping is expensive! You should only enable it when
/// necessary.
pub advanced_shape: bool,
}

View file

@ -83,6 +83,7 @@ impl Pipeline {
height: (section.bounds.height * scale_factor)
.ceil(),
},
advanced_shape: section.advanced_shape,
},
);
@ -213,6 +214,7 @@ impl Pipeline {
size: f32,
font: Font,
bounds: Size,
advanced_shape: bool,
) -> (f32, f32) {
let mut measurement_cache = self.measurement_cache.borrow_mut();
@ -223,6 +225,7 @@ impl Pipeline {
size,
font,
bounds,
advanced_shape,
},
);
@ -244,6 +247,7 @@ impl Pipeline {
bounds: Size,
point: Point,
_nearest_only: bool,
advanced_shape: bool,
) -> Option<Hit> {
let mut measurement_cache = self.measurement_cache.borrow_mut();
@ -254,6 +258,7 @@ impl Pipeline {
size,
font,
bounds,
advanced_shape,
},
);
@ -364,6 +369,7 @@ impl Cache {
.family(to_family(key.font.family))
.weight(to_weight(key.font.weight))
.stretch(to_stretch(key.font.stretch)),
!key.advanced_shape,
);
let _ = entry.insert(buffer);
@ -388,6 +394,7 @@ struct Key<'a> {
size: f32,
font: Font,
bounds: Size,
advanced_shape: bool,
}
type KeyHash = u64;

View file

@ -273,6 +273,7 @@ where
color: custom_style.icon_color,
horizontal_alignment: alignment::Horizontal::Center,
vertical_alignment: alignment::Vertical::Center,
advanced_shape: true,
});
}
}
@ -292,6 +293,7 @@ where
},
alignment::Horizontal::Left,
alignment::Vertical::Center,
false,
);
}
}

View file

@ -500,6 +500,7 @@ where
},
horizontal_alignment: alignment::Horizontal::Left,
vertical_alignment: alignment::Vertical::Center,
advanced_shape: false,
});
}
}

View file

@ -366,6 +366,7 @@ where
text_size,
font.unwrap_or_else(|| renderer.default_font()),
Size::new(f32::INFINITY, f32::INFINITY),
false,
);
width.round()
@ -628,6 +629,7 @@ pub fn draw<'a, T, Renderer>(
},
horizontal_alignment: alignment::Horizontal::Right,
vertical_alignment: alignment::Vertical::Center,
advanced_shape: false,
});
}
@ -653,6 +655,7 @@ pub fn draw<'a, T, Renderer>(
},
horizontal_alignment: alignment::Horizontal::Left,
vertical_alignment: alignment::Vertical::Center,
advanced_shape: false,
});
}
}

View file

@ -309,6 +309,7 @@ where
},
alignment::Horizontal::Left,
alignment::Vertical::Center,
false,
);
}
}

View file

@ -463,6 +463,7 @@ where
&icon.code_point.to_string(),
icon.size.unwrap_or_else(|| renderer.default_size()),
icon.font,
true,
);
let mut text_node = layout::Node::new(
@ -975,6 +976,7 @@ pub fn draw<Renderer>(
bounds: icon_layout.bounds(),
horizontal_alignment: alignment::Horizontal::Left,
vertical_alignment: alignment::Vertical::Top,
advanced_shape: true,
});
}
@ -1079,6 +1081,7 @@ pub fn draw<Renderer>(
if text.is_empty() { placeholder } else { &text },
size,
font,
true,
);
let render = |renderer: &mut Renderer| {
@ -1106,6 +1109,7 @@ pub fn draw<Renderer>(
size,
horizontal_alignment: alignment::Horizontal::Left,
vertical_alignment: alignment::Vertical::Center,
advanced_shape: true,
});
};
@ -1311,7 +1315,7 @@ where
let text_before_cursor = value.until(cursor_index).to_string();
let text_value_width =
renderer.measure_width(&text_before_cursor, size, font);
renderer.measure_width(&text_before_cursor, size, font, true);
let offset = ((text_value_width + 5.0) - text_bounds.width).max(0.0);
@ -1346,6 +1350,7 @@ where
Size::INFINITY,
Point::new(x + offset, text_bounds.height / 2.0),
true,
true,
)
.map(text::Hit::cursor)?;

View file

@ -249,6 +249,7 @@ where
Default::default(),
self.text_alignment,
alignment::Vertical::Center,
false,
);
}