From e37b5d898a3fa65bf895016703a3539d121770c0 Mon Sep 17 00:00:00 2001 From: Chad Brokaw Date: Sat, 17 Jul 2021 05:41:48 -0400 Subject: [PATCH] Move shaping into separate module --- src/builder.rs | 152 ++---------------------------------------------- src/context.rs | 2 +- src/itemize.rs | 2 +- src/lib.rs | 5 +- src/shape.rs | 155 +++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 165 insertions(+), 151 deletions(-) create mode 100644 src/shape.rs diff --git a/src/builder.rs b/src/builder.rs index bfae741..cb6c7fa 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -1,17 +1,12 @@ use super::context::LayoutState; -use super::font::Font; -use super::font_cache::FontCache; use super::itemize::*; -use super::{Glyph, Layout, Run}; +use super::shape::{shape, FontSelector}; +use super::Layout; use core::ops::RangeBounds; -use fount::{FontContext, GenericFamily, Locale}; +use fount::{FontContext, GenericFamily}; use piet::{TextAlignment, TextAttribute, TextStorage}; use std::cell::RefCell; use std::rc::Rc; -use swash::shape::{self, ShapeContext}; -use swash::text::cluster::CharCluster; -use swash::text::Script; -use swash::{Attributes, FontRef, Synthesis}; pub struct LayoutBuilder { state: Rc>, @@ -121,20 +116,10 @@ fn build(state: &mut LayoutState, layout: &mut Layout) { if state.items.is_empty() { return; } - let first_item = &state.items[0]; - let first_span = &state.spans[0]; - let mut selector = FontSelector { - font_cache: &mut state.font_cache, - spans: &state.spans, - span_index: 0, - script: first_item.script, - locale: first_item.locale, - family: first_span.family, - attrs: first_span.attributes(), - }; + let mut font_selector = FontSelector::new(&mut state.font_cache, &state.spans, &state.items[0]); shape( &mut state.shape_context, - &mut selector, + &mut font_selector, &state.spans, &state.items, &mut state.glyphs, @@ -144,130 +129,3 @@ fn build(state: &mut LayoutState, layout: &mut Layout) { layout.glyphs.extend(state.glyphs.drain(..)); layout.runs.extend(state.runs.drain(..)); } - -fn shape( - shape_context: &mut ShapeContext, - font_selector: &mut FontSelector, - spans: &[SpanData], - items: &[ItemData], - glyphs: &mut Vec, - runs: &mut Vec, - layout: &mut Layout, -) { - use swash::text::cluster::*; - for item in items { - font_selector.script = item.script; - font_selector.locale = item.locale; - font_selector - .font_cache - .select_fallbacks(item.script, item.locale, font_selector.attrs); - let options = shape::partition::SimpleShapeOptions { - size: item.size, - script: item.script, - language: item.locale, - ..Default::default() - }; - let start = item.start; - let text = &layout.text.as_str()[start..item.end]; - shape::partition::shape( - shape_context, - font_selector, - &options, - text.char_indices().map(|(i, ch)| Token { - ch, - offset: (start + i) as u32, - len: ch.len_utf8() as u8, - info: ch.into(), - // FIXME: Precompute and store this somewhere - data: match spans.binary_search_by(|probe| probe.start.cmp(&(start + i))) { - Ok(index) => index as u32, - Err(index) => index.saturating_sub(1) as u32, - }, - }), - |font, shaper| { - let glyph_start = glyphs.len(); - let mut text_start = usize::MAX; - let mut text_end = 0; - shaper.shape_with(|cluster| { - for g in cluster.glyphs { - glyphs.push(Glyph { - id: g.id, - x: g.x, - y: g.y, - advance: g.advance, - }); - } - let range = cluster.source.to_range(); - text_start = range.start.min(text_start); - text_end = range.end.max(text_end); - }); - runs.push(Run { - font: font.font.clone(), - glyph_range: glyph_start..glyphs.len(), - text_range: text_start..text_end, - }); - }, - ); - } -} - -struct FontSelector<'a> { - font_cache: &'a mut FontCache, - spans: &'a [SpanData], - span_index: u32, - script: Script, - locale: Option, - family: FamilyKind, - attrs: Attributes, -} - -impl<'a> shape::partition::Selector for FontSelector<'a> { - type SelectedFont = SelectedFont; - - fn select_font(&mut self, cluster: &mut CharCluster) -> Option { - let span_index = cluster.user_data(); - if span_index != self.span_index { - self.span_index = span_index; - let span = &self.spans[span_index as usize]; - let family = span.family; - let diff_family = family != self.family; - let attrs = span.attributes(); - let diff_attrs = attrs != self.attrs; - self.family = family; - self.attrs = attrs; - if diff_attrs || diff_family { - if diff_attrs { - self.font_cache.select_family(family, attrs); - self.font_cache - .select_fallbacks(self.script, self.locale, attrs); - } else { - self.font_cache.select_family(family, attrs); - } - } - } - let font = self.font_cache.map_cluster(cluster)?; - let synthesis = font.as_ref().attributes().synthesize(self.attrs); - Some(SelectedFont { font, synthesis }) - } -} - -struct SelectedFont { - pub font: Font, - pub synthesis: Synthesis, -} - -impl PartialEq for SelectedFont { - fn eq(&self, other: &Self) -> bool { - self.font.key == other.font.key && self.synthesis == other.synthesis - } -} - -impl shape::partition::SelectedFont for SelectedFont { - fn font(&self) -> FontRef { - self.font.as_ref() - } - - fn synthesis(&self) -> Option { - Some(self.synthesis) - } -} diff --git a/src/context.rs b/src/context.rs index 5392bce..ca0b0b7 100644 --- a/src/context.rs +++ b/src/context.rs @@ -1,6 +1,6 @@ use super::font_cache::FontCache; -use super::{Glyph, Layout, LayoutBuilder, Run}; use super::itemize::{ItemData, RangedAttribute, SpanData}; +use super::{Glyph, Layout, LayoutBuilder, Run}; use fount::Library; use piet::{FontFamily, TextAlignment, TextStorage}; use std::cell::RefCell; diff --git a/src/itemize.rs b/src/itemize.rs index 5e03277..2b3fa5b 100644 --- a/src/itemize.rs +++ b/src/itemize.rs @@ -222,7 +222,7 @@ pub fn itemize(text: &str, spans: &mut [SpanData], items: &mut Vec) { .chars() .map(|ch| ch.script()) .find(|&script| real_script(script)) - .unwrap_or(Script::Latin); + .unwrap_or(Script::Latin); let cur_level = 0; let mut start = 0; let mut end = 0; diff --git a/src/lib.rs b/src/lib.rs index eb3d873..8b7b923 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,6 +4,7 @@ mod font; mod font_cache; mod itemize; mod layout; +mod shape; pub use builder::LayoutBuilder; pub use context::LayoutContext; @@ -13,9 +14,9 @@ pub use layout::Glyph; pub use piet; pub use swash; -use std::rc::Rc; -use piet::TextStorage; use core::ops::Range; +use piet::TextStorage; +use std::rc::Rc; #[derive(Clone)] pub struct Layout { diff --git a/src/shape.rs b/src/shape.rs new file mode 100644 index 0000000..c5c2a78 --- /dev/null +++ b/src/shape.rs @@ -0,0 +1,155 @@ +use super::font::Font; +use super::font_cache::FontCache; +use super::itemize::*; +use super::{Glyph, Layout, Run}; +use fount::Locale; +use swash::shape::{self, ShapeContext}; +use swash::text::cluster::CharCluster; +use swash::text::Script; +use swash::{Attributes, FontRef, Synthesis}; + +pub fn shape( + shape_context: &mut ShapeContext, + font_selector: &mut FontSelector, + spans: &[SpanData], + items: &[ItemData], + glyphs: &mut Vec, + runs: &mut Vec, + layout: &mut Layout, +) { + use swash::text::cluster::*; + for item in items { + font_selector.script = item.script; + font_selector.locale = item.locale; + font_selector + .font_cache + .select_fallbacks(item.script, item.locale, font_selector.attrs); + let options = shape::partition::SimpleShapeOptions { + size: item.size, + script: item.script, + language: item.locale, + ..Default::default() + }; + let start = item.start; + let text = &layout.text.as_str()[start..item.end]; + shape::partition::shape( + shape_context, + font_selector, + &options, + text.char_indices().map(|(i, ch)| Token { + ch, + offset: (start + i) as u32, + len: ch.len_utf8() as u8, + info: ch.into(), + // FIXME: Precompute and store this somewhere + data: match spans.binary_search_by(|probe| probe.start.cmp(&(start + i))) { + Ok(index) => index as u32, + Err(index) => index.saturating_sub(1) as u32, + }, + }), + |font, shaper| { + let glyph_start = glyphs.len(); + let mut text_start = usize::MAX; + let mut text_end = 0; + shaper.shape_with(|cluster| { + for g in cluster.glyphs { + glyphs.push(Glyph { + id: g.id, + x: g.x, + y: g.y, + advance: g.advance, + }); + } + let range = cluster.source.to_range(); + text_start = range.start.min(text_start); + text_end = range.end.max(text_end); + }); + runs.push(Run { + font: font.font.clone(), + glyph_range: glyph_start..glyphs.len(), + text_range: text_start..text_end, + }); + }, + ); + } +} + +pub struct FontSelector<'a> { + font_cache: &'a mut FontCache, + spans: &'a [SpanData], + span_index: u32, + script: Script, + locale: Option, + family: FamilyKind, + attrs: Attributes, +} + +impl<'a> FontSelector<'a> { + pub fn new( + font_cache: &'a mut FontCache, + spans: &'a [SpanData], + first_item: &ItemData, + ) -> Self { + let first_span = &spans[0]; + Self { + font_cache, + spans, + span_index: 0, + script: first_item.script, + locale: first_item.locale, + family: first_span.family, + attrs: first_span.attributes(), + } + } +} + +impl<'a> shape::partition::Selector for FontSelector<'a> { + type SelectedFont = SelectedFont; + + fn select_font(&mut self, cluster: &mut CharCluster) -> Option { + let span_index = cluster.user_data(); + if span_index != self.span_index { + self.span_index = span_index; + let span = &self.spans[span_index as usize]; + let family = span.family; + let diff_family = family != self.family; + let attrs = span.attributes(); + let diff_attrs = attrs != self.attrs; + self.family = family; + self.attrs = attrs; + if diff_attrs || diff_family { + if diff_attrs { + self.font_cache.select_family(family, attrs); + self.font_cache + .select_fallbacks(self.script, self.locale, attrs); + } else { + self.font_cache.select_family(family, attrs); + } + } + } + let font = self.font_cache.map_cluster(cluster)?; + let synthesis = font.as_ref().attributes().synthesize(self.attrs); + Some(SelectedFont { font, synthesis }) + } +} + +pub struct SelectedFont { + pub font: Font, + pub synthesis: Synthesis, +} + +impl PartialEq for SelectedFont { + fn eq(&self, other: &Self) -> bool { + self.font.key == other.font.key && self.synthesis == other.synthesis + } +} + +impl shape::partition::SelectedFont for SelectedFont { + fn font(&self) -> FontRef { + self.font.as_ref() + } + + fn synthesis(&self) -> Option { + Some(self.synthesis) + } +}