Move shaping into separate module
This commit is contained in:
parent
3cecc34031
commit
e37b5d898a
152
src/builder.rs
152
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<RefCell<LayoutState>>,
|
||||
|
@ -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<Glyph>,
|
||||
runs: &mut Vec<Run>,
|
||||
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<Locale>,
|
||||
family: FamilyKind,
|
||||
attrs: Attributes,
|
||||
}
|
||||
|
||||
impl<'a> shape::partition::Selector for FontSelector<'a> {
|
||||
type SelectedFont = SelectedFont;
|
||||
|
||||
fn select_font(&mut self, cluster: &mut CharCluster) -> Option<Self::SelectedFont> {
|
||||
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<Synthesis> {
|
||||
Some(self.synthesis)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -222,7 +222,7 @@ pub fn itemize(text: &str, spans: &mut [SpanData], items: &mut Vec<ItemData>) {
|
|||
.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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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<Glyph>,
|
||||
runs: &mut Vec<Run>,
|
||||
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<Locale>,
|
||||
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<Self::SelectedFont> {
|
||||
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<Synthesis> {
|
||||
Some(self.synthesis)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue