Skip to content

Commit 6c374e9

Browse files
committed
Migrate analysis from parley to parley_core
This introduces `Analyzer` and `Analysis` as scratch, moves over `analyze_text` from Parley into Parley Core, as well some types like `CharInfo`, `Boundary`. Parley now calls into Parley Core for analysis. Analysis is unchanged, except Parley Core is given word break spans by Parley, where Parley previously reached into the style runs during analysis. Quite a bit is `pub` so Parley can reach into Core's fields. `CharInfo` has different fields in linebender#634 (the big PR where all of shaping was migrated to `parley_core`), and we may move to something like that later. For now, this should be fine. Benches neutral as expected: <details> <summary>Benchmark results</summary> ``` $ cargo export target/benchmarks -- bench --bench=main $ cargo bench -q --bench=main -- compare ../target/benchmarks/main -t 8. Default Style - arabic 20 characters [ 8.7 us ... 8.7 us ] -0.26% Default Style - latin 20 characters [ 4.1 us ... 4.2 us ] +1.69%* Default Style - japanese 20 characters [ 8.1 us ... 8.2 us ] +0.49% Default Style - arabic 1 paragraph [ 47.0 us ... 46.7 us ] -0.66% Default Style - latin 1 paragraph [ 16.0 us ... 16.5 us ] +2.78%* Default Style - japanese 1 paragraph [ 69.1 us ... 68.7 us ] -0.55% Default Style - arabic 4 paragraph [ 203.1 us ... 196.7 us ] -3.13%* Default Style - latin 4 paragraph [ 61.5 us ... 61.7 us ] +0.39% Default Style - japanese 4 paragraph [ 98.1 us ... 97.0 us ] -1.14%* Styled - arabic 20 characters [ 9.7 us ... 9.7 us ] +0.04% Styled - latin 20 characters [ 5.3 us ... 5.3 us ] +1.55%* Styled - japanese 20 characters [ 8.6 us ... 8.6 us ] -0.46% Styled - arabic 1 paragraph [ 49.3 us ... 49.0 us ] -0.76% Styled - latin 1 paragraph [ 20.4 us ... 20.6 us ] +0.93% Styled - japanese 1 paragraph [ 75.0 us ... 74.4 us ] -0.90% Styled - arabic 4 paragraph [ 220.1 us ... 215.6 us ] -2.02%* Styled - latin 4 paragraph [ 79.5 us ... 80.0 us ] +0.59% Styled - japanese 4 paragraph [ 106.2 us ... 105.3 us ] -0.85% ``` </details>
1 parent 18e3eb4 commit 6c374e9

15 files changed

Lines changed: 342 additions & 118 deletions

File tree

Cargo.lock

Lines changed: 4 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

parley/Cargo.toml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,11 @@ fontique = { workspace = true }
3434
parlance = { workspace = true }
3535
parley_core = { workspace = true }
3636
core_maths = { version = "0.1.1", optional = true }
37-
parley_data = { workspace = true, features = ["baked"] }
3837
accesskit = { workspace = true, optional = true }
3938
hashbrown = { workspace = true }
4039
harfrust = { workspace = true }
4140
icu_normalizer = { workspace = true, features = ["compiled_data"] }
4241
icu_properties = { workspace = true, features = ["compiled_data"] }
43-
icu_segmenter = { workspace = true, features = ["compiled_data"] }
4442

4543
[dev-dependencies]
4644
parley_dev = { workspace = true }

parley/src/analysis.rs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// Copyright 2025 the Parley Authors
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
4+
pub(crate) mod cluster;
5+
6+
use crate::{Brush, LayoutContext};
7+
8+
use icu_properties::props::{GeneralCategory, Script};
9+
use parley_core::break_overrides::LineBreakOverrideFn;
10+
11+
use parley_core::{AnalysisDataSources, AnalysisOptions};
12+
13+
pub(crate) fn analyze_text<B: Brush>(
14+
lcx: &mut LayoutContext<B>,
15+
text: &str,
16+
line_break_override: Option<&LineBreakOverrideFn>,
17+
) {
18+
let text = if text.is_empty() { " " } else { text };
19+
20+
// Collect the style runs word breaks. Gaps are `WordBreak::Normal`, so only non-`Normal`s need
21+
// an entry.
22+
lcx.word_break.clear();
23+
lcx.word_break.extend(lcx.style_runs.iter().map(|sr| {
24+
(
25+
sr.range.clone(),
26+
lcx.style_table[sr.style_index as usize].word_break,
27+
)
28+
}));
29+
30+
let options = AnalysisOptions {
31+
word_break: &lcx.word_break,
32+
line_break_override,
33+
};
34+
lcx.analyzer.analyze(text, &options, &mut lcx.analysis);
35+
36+
// Pair the char infos with style indices (which we set later in the builder).
37+
lcx.info.clear();
38+
lcx.info
39+
.extend(lcx.analysis.char_infos().iter().map(|&ci| (ci, 0)));
40+
}
41+
42+
/// All characters contribute to shaping except:
43+
/// - Control characters
44+
/// - Format characters, unless they use the "Inherited" script
45+
#[inline(always)]
46+
pub(crate) fn contributes_to_shaping(general_category: GeneralCategory, script: Script) -> bool {
47+
if matches!(
48+
general_category,
49+
GeneralCategory::Control
50+
| GeneralCategory::LineSeparator
51+
| GeneralCategory::ParagraphSeparator
52+
) {
53+
return false;
54+
}
55+
56+
!(general_category == GeneralCategory::Format && script != Script::Inherited)
57+
}

parley/src/builder.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,7 @@ fn build_into_layout<B: Brush>(
307307
layout.data.clear();
308308
layout.data.scale = scale;
309309
layout.data.quantize = quantize;
310-
layout.data.base_level = lcx.bidi.base_level();
310+
layout.data.base_level = lcx.analysis.paragraph_level();
311311
layout.data.text_len = text.len();
312312

313313
let mut char_index = 0;
@@ -336,7 +336,7 @@ fn build_into_layout<B: Brush>(
336336
&lcx.style_table,
337337
&lcx.inline_boxes,
338338
&lcx.info,
339-
lcx.bidi.levels(),
339+
lcx.analysis.bidi_levels(),
340340
&mut lcx.scx,
341341
text,
342342
layout,

parley/src/context.rs

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,19 @@
33

44
//! Context for layout.
55
6+
use core::ops::Range;
7+
68
use alloc::{vec, vec::Vec};
79

8-
use parley_core::bidi::BidiResolver;
10+
use parlance::WordBreak;
11+
use parley_core::{Analysis, AnalysisDataSources, Analyzer, CharInfo};
912

1013
use super::FontContext;
1114
use super::builder::{RangedBuilder, StyleRunBuilder};
1215
use super::resolve::tree::TreeStyleBuilder;
1316
use super::resolve::{RangedStyleBuilder, ResolveContext, ResolvedStyle, StyleRun};
1417
use super::style::{Brush, TextStyle};
1518

16-
use crate::analysis::{AnalysisDataSources, CharInfo};
1719
use crate::builder::TreeBuilder;
1820
use crate::inline_box::InlineBox;
1921
use crate::shape::ShapeContext;
@@ -26,7 +28,11 @@ pub struct LayoutContext<B: Brush = [u8; 4]> {
2628
pub(crate) style_table: Vec<ResolvedStyle<B>>,
2729
pub(crate) style_runs: Vec<StyleRun>,
2830
pub(crate) inline_boxes: Vec<InlineBox>,
29-
pub(crate) bidi: BidiResolver,
31+
32+
// Reusable text analysis
33+
pub(crate) analyzer: Analyzer,
34+
pub(crate) analysis: Analysis,
35+
pub(crate) word_break: Vec<(Range<usize>, WordBreak)>,
3036

3137
// Reusable style builders (to amortise allocations)
3238
pub(crate) ranged_style_builder: RangedStyleBuilder<B>,
@@ -47,7 +53,9 @@ impl<B: Brush> LayoutContext<B> {
4753
style_table: vec![],
4854
style_runs: vec![],
4955
inline_boxes: vec![],
50-
bidi: BidiResolver::new(),
56+
analyzer: Analyzer::new(),
57+
analysis: Analysis::new(),
58+
word_break: Vec::new(),
5159
ranged_style_builder: RangedStyleBuilder::default(),
5260
tree_style_builder: TreeStyleBuilder::default(),
5361
info: vec![],
@@ -187,7 +195,6 @@ impl<B: Brush> LayoutContext<B> {
187195
self.style_runs.clear();
188196
self.inline_boxes.clear();
189197
self.info.clear();
190-
self.bidi.clear();
191198
}
192199
}
193200

parley/src/convert.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
// Copyright 2024 the Parley Authors
22
// SPDX-License-Identifier: Apache-2.0 OR MIT
33

4-
use crate::analysis::AnalysisDataSources;
5-
64
use icu_properties::props::Script;
5+
use parley_core::AnalysisDataSources;
76

87
pub(crate) fn script_to_fontique(
98
script: Script,

parley/src/layout/data.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ use crate::{FontData, IndentOptions, InlineBoxKind, LineHeight, OverflowWrap, Te
99
use core::ops::Range;
1010

1111
use alloc::vec::Vec;
12+
use parley_core::{Boundary, CharInfo};
1213

1314
use crate::analysis::cluster::Whitespace;
14-
use crate::analysis::{Boundary, CharInfo};
1515

1616
#[derive(Copy, Clone, Debug, PartialEq)]
1717
pub(crate) struct ClusterData {

parley/src/layout/line_break.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ use alloc::vec::Vec;
99
#[allow(unused_imports)]
1010
use core_maths::CoreFloat;
1111

12-
use crate::analysis::Boundary;
1312
use crate::analysis::cluster::Whitespace;
1413
use crate::data::ClusterData;
1514
use crate::layout::{
@@ -20,6 +19,7 @@ use crate::style::Brush;
2019
use crate::{InlineBoxKind, OverflowWrap, TextWrapMode};
2120

2221
use core::ops::Range;
22+
use parley_core::Boundary;
2323

2424
#[derive(Default)]
2525
struct LineLayout {

parley/src/shape/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,12 @@ use alloc::vec::Vec;
88
use core::mem;
99
use core::ops::RangeInclusive;
1010
use harfrust::ShapeOptions;
11+
use parley_core::{AnalysisDataSources, CharInfo};
1112

1213
use super::layout::Layout;
1314
use super::resolve::{ResolveContext, Resolved, ResolvedStyle};
1415
use super::style::{Brush, FontFeature, FontVariation};
1516
use crate::analysis::cluster::{Char, CharCluster, Status};
16-
use crate::analysis::{AnalysisDataSources, CharInfo};
1717
use crate::convert::script_to_harfrust;
1818
use crate::inline_box::InlineBox;
1919
use crate::lru_cache::LruCache;

parley/src/tests/test_analysis.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
// Copyright 2025 the Parley Authors
22
// SPDX-License-Identifier: Apache-2.0 OR MIT
33

4-
use crate::analysis::Boundary;
54
use crate::{FontContext, LayoutContext, RangedBuilder, StyleProperty, WordBreak};
65
use alloc::{vec, vec::Vec};
76
use fontique::FontWeight;
87
use icu_properties::props::{GraphemeClusterBreak, Script};
8+
use parley_core::Boundary;
99

1010
#[derive(Default)]
1111
struct TestContext {
@@ -26,7 +26,7 @@ impl TestContext {
2626
}
2727

2828
fn expect_bidi_embed_level_list(self, expected: Vec<u8>) -> Self {
29-
let actual = self.layout_context.bidi.levels();
29+
let actual = self.layout_context.analysis.bidi_levels();
3030
assert_eq!(actual, expected, "Bidi embed level list mismatch");
3131
self
3232
}

0 commit comments

Comments
 (0)