Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

text: Calculate character positions during relayout #18259

Merged
merged 3 commits into from
Oct 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 3 additions & 10 deletions core/src/display_object/edit_text.rs
Original file line number Diff line number Diff line change
Expand Up @@ -909,9 +909,8 @@ impl<'gc> EditText<'gc> {
layout: &Layout<'gc>,
) {
if flags.contains(LayoutDebugBoxesFlag::CHAR) {
let text = &self.text();
for i in 0..text.len() {
if let Some(bounds) = layout.char_bounds(i, text) {
for i in 0..self.text().len() {
if let Some(bounds) = layout.char_bounds(i) {
context.draw_rect_outline(Color::MAGENTA, bounds, Twips::ONE);
}
}
Expand Down Expand Up @@ -2030,13 +2029,7 @@ impl<'gc> EditText<'gc> {

pub fn char_bounds(self, index: usize) -> Option<Rectangle<Twips>> {
let edit_text = self.0.read();
let text = edit_text.text_spans.text();

if index >= text.len() {
return None;
}

let bounds = edit_text.layout.char_bounds(index, text)?;
let bounds = edit_text.layout.char_bounds(index)?;
let padding = Twips::from_pixels(Self::INTERNAL_PADDING);
let bounds = Matrix::translate(padding, padding) * bounds;
Some(bounds)
Expand Down
69 changes: 47 additions & 22 deletions core/src/html/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

use crate::context::UpdateContext;
use crate::drawing::Drawing;
use crate::font::{EvalParameters, Font, FontType, Glyph};
use crate::font::{EvalParameters, Font, FontType};
use crate::html::dimensions::{BoxBounds, Position, Size};
use crate::html::text_format::{FormatSpans, TextFormat, TextSpan};
use crate::string::{utils as string_utils, WStr};
Expand Down Expand Up @@ -637,7 +637,7 @@ impl<'a, 'gc> LayoutContext<'a, 'gc> {
let text_size = Size::from(font.measure(text, params));
let box_origin = self.cursor - (Twips::ZERO, ascent).into();

let mut new_box = LayoutBox::from_text(start, end, font, span);
let mut new_box = LayoutBox::from_text(text, start, end, font, span);
new_box.interior_bounds = BoxBounds::from_position_and_size(box_origin, text_size);
new_box.bounds = BoxBounds::from_position_and_size(
box_origin,
Expand Down Expand Up @@ -823,10 +823,10 @@ impl<'gc> Layout<'gc> {
}

/// Returns char bounds of the given char relative to this layout.
pub fn char_bounds(&self, position: usize, text: &WStr) -> Option<Rectangle<Twips>> {
pub fn char_bounds(&self, position: usize) -> Option<Rectangle<Twips>> {
let line_index = self.find_line_index_by_position(position)?;
let line = self.lines.get(line_index)?;
line.char_bounds(position, text)
line.char_bounds(position)
}
}

Expand Down Expand Up @@ -923,13 +923,13 @@ impl<'gc> LayoutLine<'gc> {
}

/// Returns char bounds of the given char relative to the whole layout.
pub fn char_bounds(&self, position: usize, text: &WStr) -> Option<Rectangle<Twips>> {
pub fn char_bounds(&self, position: usize) -> Option<Rectangle<Twips>> {
let box_index = self.find_box_index_by_position(position)?;
let layout_box = self.boxes.get(box_index)?;

let line_bounds = self.bounds();
let origin_x = layout_box.bounds().origin().x();
let x_bounds = layout_box.char_x_bounds(position, text)?;
let x_bounds = layout_box.char_x_bounds(position)?;

Some(Rectangle {
x_min: origin_x + x_bounds.0,
Expand Down Expand Up @@ -999,6 +999,21 @@ pub enum LayoutContent<'gc> {
/// The color to render the font with.
#[collect(require_static)]
color: swf::Color,

/// List of end positions (relative to this box) for each character.
///
/// By having this here, we do not have to reevaluate the font
/// each time we want to get the position of a character,
/// and we can use this data along with layout box bounds to
/// calculate character bounds.
///
/// For instance, for the text "hello", this field may contain:
///
/// ```text
/// [100, 200, 250, 300, 400]
/// ```
#[collect(require_static)]
char_end_pos: Vec<Twips>,
},

/// A layout box containing a bullet.
Expand Down Expand Up @@ -1062,8 +1077,19 @@ impl<'gc> Debug for LayoutContent<'gc> {

impl<'gc> LayoutBox<'gc> {
/// Construct a text box for a text node.
pub fn from_text(start: usize, end: usize, font: Font<'gc>, span: &TextSpan) -> Self {
pub fn from_text(
text: &WStr,
start: usize,
end: usize,
font: Font<'gc>,
span: &TextSpan,
) -> Self {
let params = EvalParameters::from_span(span);
let mut char_end_pos = Vec::with_capacity(end - start);

font.evaluate(text, Default::default(), params, |_, _, _, advance, x| {
char_end_pos.push(x + advance);
});

Self {
interior_bounds: Default::default(),
Expand All @@ -1075,6 +1101,7 @@ impl<'gc> LayoutBox<'gc> {
font,
params,
color: span.font.color,
char_end_pos,
},
}
}
Expand Down Expand Up @@ -1269,6 +1296,7 @@ impl<'gc> LayoutBox<'gc> {
font,
params,
color,
..
} => Some((
text.slice(*start..*end)?,
text_format,
Expand Down Expand Up @@ -1327,24 +1355,21 @@ impl<'gc> LayoutBox<'gc> {
}

/// Return x-axis char bounds of the given char relative to this layout box.
pub fn char_x_bounds(&self, position: usize, text: &WStr) -> Option<(Twips, Twips)> {
pub fn char_x_bounds(&self, position: usize) -> Option<(Twips, Twips)> {
let relative_position = position.checked_sub(self.start())?;

let mut x_bounds = None;
if let Some((text, _tf, font, params, _color)) = self.as_renderable_text(text) {
font.evaluate(
text,
Default::default(),
params,
|pos, _transform, _glyph: &Glyph, advance, x| {
if pos == relative_position {
x_bounds = Some((x, x + advance));
}
},
);
}
let LayoutContent::Text { char_end_pos, .. } = &self.content else {
return None;
};

x_bounds
Some(if relative_position == 0 {
(Twips::ZERO, *char_end_pos.get(0)?)
} else {
(
*char_end_pos.get(relative_position - 1)?,
*char_end_pos.get(relative_position)?,
)
})
}
}

Expand Down
Loading