From 3485e5517facf98e6f305882f4b3e596f6f240c4 Mon Sep 17 00:00:00 2001 From: Nico Burns Date: Tue, 1 Jul 2025 10:42:52 +0100 Subject: [PATCH] Better encapsulate LayoutContext Signed-off-by: Nico Burns --- parley/src/builder.rs | 88 +++++++--------------------------------- parley/src/context.rs | 94 +++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 104 insertions(+), 78 deletions(-) diff --git a/parley/src/builder.rs b/parley/src/builder.rs index c59bedf0..242d3402 100644 --- a/parley/src/builder.rs +++ b/parley/src/builder.rs @@ -10,6 +10,7 @@ use super::style::{Brush, StyleProperty, TextStyle, WhiteSpaceCollapse}; use super::layout::Layout; use alloc::string::String; +use alloc::vec::Vec; use core::ops::RangeBounds; use crate::inline_box::InlineBox; @@ -28,7 +29,6 @@ impl RangedBuilder<'_, B> { pub fn push_default<'a>(&mut self, property: impl Into>) { let resolved = self .lcx - .rcx .resolve_property(self.fcx, &property.into(), self.scale); self.lcx.ranged_style_builder.push_default(resolved); } @@ -40,7 +40,6 @@ impl RangedBuilder<'_, B> { ) { let resolved = self .lcx - .rcx .resolve_property(self.fcx, &property.into(), self.scale); self.lcx.ranged_style_builder.push(resolved, range); } @@ -54,14 +53,8 @@ impl RangedBuilder<'_, B> { self.lcx.ranged_style_builder.finish(&mut self.lcx.styles); // Call generic layout builder method - build_into_layout( - layout, - self.scale, - self.quantize, - text.as_ref(), - self.lcx, - self.fcx, - ); + self.lcx + .build_into_layout(layout, self.scale, self.quantize, text.as_ref(), self.fcx); } pub fn build(self, text: impl AsRef) -> Layout { @@ -84,7 +77,6 @@ impl TreeBuilder<'_, B> { pub fn push_style_span(&mut self, style: TextStyle<'_, B>) { let resolved = self .lcx - .rcx .resolve_entire_style_set(self.fcx, &style, self.scale); self.lcx.tree_style_builder.push_style_span(resolved); } @@ -96,11 +88,15 @@ impl TreeBuilder<'_, B> { 's: 'iter, B: 'iter, { - self.lcx.tree_style_builder.push_style_modification_span( - properties - .into_iter() - .map(|p| self.lcx.rcx.resolve_property(self.fcx, p, self.scale)), - ); + // FIXME: eliminate allocation if/when the "style builders" are extracted + // from the LayoutContext + let resolved_properties: Vec<_> = properties + .into_iter() + .map(|p| self.lcx.resolve_property(self.fcx, p, self.scale)) + .collect(); + self.lcx + .tree_style_builder + .push_style_modification_span(resolved_properties.into_iter()); } pub fn pop_style_span(&mut self) { @@ -134,7 +130,8 @@ impl TreeBuilder<'_, B> { let text = self.lcx.tree_style_builder.finish(&mut self.lcx.styles); // Call generic layout builder method - build_into_layout(layout, self.scale, self.quantize, &text, self.lcx, self.fcx); + self.lcx + .build_into_layout(layout, self.scale, self.quantize, &text, self.fcx); text } @@ -146,60 +143,3 @@ impl TreeBuilder<'_, B> { (layout, text) } } - -fn build_into_layout( - layout: &mut Layout, - scale: f32, - quantize: bool, - text: &str, - lcx: &mut LayoutContext, - fcx: &mut FontContext, -) { - lcx.analyze_text(text); - - layout.data.clear(); - layout.data.scale = scale; - layout.data.quantize = quantize; - layout.data.has_bidi = !lcx.bidi.levels().is_empty(); - layout.data.base_level = lcx.bidi.base_level(); - layout.data.text_len = text.len(); - - let mut char_index = 0; - for (i, style) in lcx.styles.iter().enumerate() { - for _ in text[style.range.clone()].chars() { - lcx.info[char_index].1 = i as u16; - char_index += 1; - } - } - - // Copy the visual styles into the layout - layout - .data - .styles - .extend(lcx.styles.iter().map(|s| s.style.as_layout_style())); - - // Sort the inline boxes as subsequent code assumes that they are in text index order. - // Note: It's important that this is a stable sort to allow users to control the order of contiguous inline boxes - lcx.inline_boxes.sort_by_key(|b| b.index); - - { - let query = fcx.collection.query(&mut fcx.source_cache); - super::shape::shape_text( - &lcx.rcx, - query, - &lcx.styles, - &lcx.inline_boxes, - &lcx.info, - lcx.bidi.levels(), - &mut lcx.scx, - text, - layout, - ); - } - - // Move inline boxes into the layout - layout.data.inline_boxes.clear(); - core::mem::swap(&mut layout.data.inline_boxes, &mut lcx.inline_boxes); - - layout.data.finish(); -} diff --git a/parley/src/context.rs b/parley/src/context.rs index dc9898b2..112d7363 100644 --- a/parley/src/context.rs +++ b/parley/src/context.rs @@ -16,15 +16,16 @@ use super::style::{Brush, TextStyle}; use swash::shape::ShapeContext; use swash::text::cluster::CharInfo; +use crate::Layout; +use crate::StyleProperty; use crate::builder::TreeBuilder; use crate::inline_box::InlineBox; +use crate::resolve::ResolvedProperty; /// Shared scratch space used when constructing text layouts. /// /// This type is designed to be a global resource with only one per-application (or per-thread). pub struct LayoutContext { - pub(crate) bidi: bidi::BidiResolver, - pub(crate) rcx: ResolveContext, pub(crate) styles: Vec>, pub(crate) inline_boxes: Vec, @@ -32,8 +33,11 @@ pub struct LayoutContext { pub(crate) ranged_style_builder: RangedStyleBuilder, pub(crate) tree_style_builder: TreeStyleBuilder, - pub(crate) info: Vec<(CharInfo, u16)>, - pub(crate) scx: ShapeContext, + // Internal contexts + bidi: bidi::BidiResolver, + rcx: ResolveContext, + scx: ShapeContext, + info: Vec<(CharInfo, u16)>, } impl LayoutContext { @@ -179,6 +183,88 @@ impl LayoutContext { } } + pub(crate) fn resolve_property( + &mut self, + fcx: &mut FontContext, + property: &StyleProperty<'_, B>, + scale: f32, + ) -> ResolvedProperty { + self.rcx.resolve_property(fcx, property, scale) + } + + pub(crate) fn resolve_entire_style_set( + &mut self, + fcx: &mut FontContext, + raw_style: &TextStyle<'_, B>, + scale: f32, + ) -> ResolvedStyle { + self.rcx.resolve_entire_style_set(fcx, raw_style, scale) + } + + pub(crate) fn shape_into_layout( + &mut self, + fcx: &mut FontContext, + text: &str, + layout: &mut Layout, + ) { + let query = fcx.collection.query(&mut fcx.source_cache); + super::shape::shape_text( + &self.rcx, + query, + &self.styles, + &self.inline_boxes, + &self.info, + self.bidi.levels(), + &mut self.scx, + text, + layout, + ); + } + + pub(crate) fn build_into_layout( + &mut self, + layout: &mut Layout, + scale: f32, + quantize: bool, + text: &str, + fcx: &mut FontContext, + ) { + self.analyze_text(text); + + layout.data.clear(); + layout.data.scale = scale; + layout.data.quantize = quantize; + layout.data.has_bidi = !self.bidi.levels().is_empty(); + layout.data.base_level = self.bidi.base_level(); + layout.data.text_len = text.len(); + + let mut char_index = 0; + for (i, style) in self.styles.iter().enumerate() { + for _ in text[style.range.clone()].chars() { + self.info[char_index].1 = i as u16; + char_index += 1; + } + } + + // Copy the visual styles into the layout + layout + .data + .styles + .extend(self.styles.iter().map(|s| s.style.as_layout_style())); + + // Sort the inline boxes as subsequent code assumes that they are in text index order. + // Note: It's important that this is a stable sort to allow users to control the order of contiguous inline boxes + self.inline_boxes.sort_by_key(|b| b.index); + + self.shape_into_layout(fcx, text, layout); + + // Move inline boxes into the layout + layout.data.inline_boxes.clear(); + core::mem::swap(&mut layout.data.inline_boxes, &mut self.inline_boxes); + + layout.data.finish(); + } + fn begin(&mut self) { self.rcx.clear(); self.styles.clear();