initial support for custom brushes

This commit is contained in:
Chad Brokaw 2021-08-04 05:00:49 -04:00
parent bb16bccf28
commit 7596ee8b03
7 changed files with 80 additions and 61 deletions

View File

@ -2,6 +2,7 @@ use super::context::LayoutState;
use super::font::system::{Font, SystemFontCollection}; use super::font::system::{Font, SystemFontCollection};
use super::font::{FontCollection, FontFamilyHandle, GenericFontFamily}; use super::font::{FontCollection, FontFamilyHandle, GenericFontFamily};
use super::itemize::*; use super::itemize::*;
use super::Color;
use super::Layout; use super::Layout;
use fount::FamilyId; use fount::FamilyId;
use piet::kurbo::{Point, Rect, Size}; use piet::kurbo::{Point, Rect, Size};
@ -174,7 +175,10 @@ impl piet::TextLayoutBuilder for PietTextLayoutBuilder {
} }
} }
fn convert_attr(attr: &TextAttribute, fonts: &mut SystemFontCollection) -> AttributeKind<FamilyId> { fn convert_attr(
attr: &TextAttribute,
fonts: &mut SystemFontCollection,
) -> AttributeKind<FamilyId, ()> {
use piet::FontStyle; use piet::FontStyle;
use swash::{Style, Weight}; use swash::{Style, Weight};
match attr { match attr {
@ -201,7 +205,7 @@ fn convert_attr(attr: &TextAttribute, fonts: &mut SystemFontCollection) -> Attri
}), }),
TextAttribute::TextColor(color) => { TextAttribute::TextColor(color) => {
let (r, g, b, a) = color.as_rgba8(); let (r, g, b, a) = color.as_rgba8();
AttributeKind::Color([r, g, b, a]) AttributeKind::Color(Color::Solid([r, g, b, a]))
} }
TextAttribute::Underline(yes) => AttributeKind::Underline(*yes), TextAttribute::Underline(yes) => AttributeKind::Underline(*yes),
TextAttribute::Strikethrough(yes) => AttributeKind::Strikethrough(*yes), TextAttribute::Strikethrough(yes) => AttributeKind::Strikethrough(*yes),

View File

@ -1,23 +1,23 @@
use super::font::{FontCollection, FontFamily, FontFamilyHandle, GenericFontFamily}; use super::font::{FontCollection, FontFamily, FontFamilyHandle, GenericFontFamily};
use super::itemize::{AttributeKind, ItemData, RangedAttribute, SpanData}; use super::itemize::{AttributeKind, ItemData, RangedAttribute, SpanData};
use super::{Alignment, Attribute}; use super::{Alignment, Attribute, Brush};
use super::{Glyph, Layout, Run}; use super::{Glyph, Layout, Run};
use core::ops::{Bound, Range, RangeBounds}; use core::ops::{Bound, Range, RangeBounds};
use fount::Library; use fount::Library;
use swash::shape::ShapeContext; use swash::shape::ShapeContext;
pub struct LayoutContext<C: FontCollection> { pub struct LayoutContext<C: FontCollection, B: Brush> {
state: LayoutState<C>, state: LayoutState<C, B>,
} }
impl<C: FontCollection> LayoutContext<C> { impl<C: FontCollection, B: Brush> LayoutContext<C, B> {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
state: LayoutState::new(), state: LayoutState::new(),
} }
} }
pub fn new_layout<'a>(&'a mut self, fonts: &'a mut C, text: &'a str) -> LayoutBuilder<'a, C> { pub fn new_layout<'a>(&'a mut self, fonts: &'a mut C, text: &'a str) -> LayoutBuilder<'a, C, B> {
fonts.begin_session(); fonts.begin_session();
LayoutBuilder { LayoutBuilder {
state: &mut self.state, state: &mut self.state,
@ -27,19 +27,19 @@ impl<C: FontCollection> LayoutContext<C> {
} }
} }
pub struct LayoutBuilder<'a, C: FontCollection> { pub struct LayoutBuilder<'a, C: FontCollection, B: Brush> {
state: &'a mut LayoutState<C>, state: &'a mut LayoutState<C, B>,
fonts: &'a mut C, fonts: &'a mut C,
text: &'a str, text: &'a str,
} }
impl<'a, C: FontCollection> LayoutBuilder<'a, C> {} impl<'a, C: FontCollection, B: Brush> LayoutBuilder<'a, C, B> {}
pub struct LayoutState<C: FontCollection> { pub struct LayoutState<C: FontCollection, B: Brush = ()> {
pub shape_context: ShapeContext, pub shape_context: ShapeContext,
pub defaults: SpanData<C::Family>, pub defaults: SpanData<C::Family, B>,
pub attributes: Vec<RangedAttribute<C::Family>>, pub attributes: Vec<RangedAttribute<C::Family, B>>,
pub spans: Vec<SpanData<C::Family>>, pub spans: Vec<SpanData<C::Family, B>>,
pub items: Vec<ItemData>, pub items: Vec<ItemData>,
pub glyphs: Vec<Glyph>, pub glyphs: Vec<Glyph>,
pub runs: Vec<Run<C::Font>>, pub runs: Vec<Run<C::Font>>,
@ -47,7 +47,7 @@ pub struct LayoutState<C: FontCollection> {
pub alignment: Alignment, pub alignment: Alignment,
} }
impl<C: FontCollection> LayoutState<C> { impl<C: FontCollection, B: Brush> LayoutState<C, B> {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
shape_context: ShapeContext::new(), shape_context: ShapeContext::new(),
@ -73,7 +73,7 @@ impl<C: FontCollection> LayoutState<C> {
self.alignment = Alignment::Start; self.alignment = Alignment::Start;
} }
pub fn default_attribute(&mut self, attribute: AttributeKind<C::Family>) { pub fn default_attribute(&mut self, attribute: AttributeKind<C::Family, B>) {
self.defaults.apply(&attribute); self.defaults.apply(&attribute);
} }
@ -81,7 +81,7 @@ impl<C: FontCollection> LayoutState<C> {
&mut self, &mut self,
text_len: usize, text_len: usize,
range: impl RangeBounds<usize>, range: impl RangeBounds<usize>,
attribute: AttributeKind<C::Family>, attribute: AttributeKind<C::Family, B>,
) { ) {
let range = resolve_range(range, text_len); let range = resolve_range(range, text_len);
if !range.is_empty() { if !range.is_empty() {
@ -116,7 +116,7 @@ impl<C: FontCollection> LayoutState<C> {
} }
} }
fn convert_attr<C: FontCollection>(attr: &Attribute, fonts: &mut C) -> AttributeKind<C::Family> { fn convert_attr<C: FontCollection, B: Brush>(attr: &Attribute<B>, fonts: &mut C) -> AttributeKind<C::Family, B> {
match attr { match attr {
Attribute::FontFamily(family) => AttributeKind::Family(match family { Attribute::FontFamily(family) => AttributeKind::Family(match family {
FontFamily::Named(name) => fonts FontFamily::Named(name) => fonts
@ -129,7 +129,7 @@ fn convert_attr<C: FontCollection>(attr: &Attribute, fonts: &mut C) -> Attribute
Attribute::FontWeight(weight) => AttributeKind::Weight(*weight), Attribute::FontWeight(weight) => AttributeKind::Weight(*weight),
Attribute::FontStyle(style) => AttributeKind::Style(*style), Attribute::FontStyle(style) => AttributeKind::Style(*style),
Attribute::FontStretch(stretch) => AttributeKind::Stretch(*stretch), Attribute::FontStretch(stretch) => AttributeKind::Stretch(*stretch),
Attribute::Color(color) => AttributeKind::Color(*color), Attribute::Color(color) => AttributeKind::Color(color.clone()),
Attribute::Underline(yes) => AttributeKind::Underline(*yes), Attribute::Underline(yes) => AttributeKind::Underline(*yes),
Attribute::Strikethrough(yes) => AttributeKind::Strikethrough(*yes), Attribute::Strikethrough(yes) => AttributeKind::Strikethrough(*yes),
} }

View File

@ -10,7 +10,7 @@ pub use swash::{Stretch as FontStretch, Style as FontStyle, Weight as FontWeight
pub mod system; pub mod system;
pub trait FontInstance: Clone + PartialEq { pub trait FontHandle: Clone + PartialEq {
fn as_font_ref(&self) -> FontRef; fn as_font_ref(&self) -> FontRef;
fn synthesis(&self) -> Option<Synthesis> { fn synthesis(&self) -> Option<Synthesis> {
@ -20,7 +20,7 @@ pub trait FontInstance: Clone + PartialEq {
pub trait FontCollection { pub trait FontCollection {
type Family: Clone + PartialEq + Debug; type Family: Clone + PartialEq + Debug;
type Font: FontInstance; type Font: FontHandle;
/// Begins a layout sesion with this collection. /// Begins a layout sesion with this collection.
fn begin_session(&mut self); fn begin_session(&mut self);

View File

@ -1,12 +1,12 @@
mod collection; mod collection;
mod font; mod font;
use super::FontInstance; use super::FontHandle;
pub use collection::SystemFontCollection; pub use collection::SystemFontCollection;
pub use font::Font; pub use font::Font;
use swash::{FontRef, Synthesis}; use swash::{FontRef, Synthesis};
impl FontInstance for Font { impl FontHandle for Font {
fn as_font_ref(&self) -> FontRef { fn as_font_ref(&self) -> FontRef {
self.as_ref() self.as_ref()
} }

View File

@ -3,15 +3,16 @@ use core::fmt::Debug;
use fount::{FamilyId, GenericFamily, Locale}; use fount::{FamilyId, GenericFamily, Locale};
use swash::text::Script; use swash::text::Script;
use swash::{Attributes, Stretch, Style, Weight}; use swash::{Attributes, Stretch, Style, Weight};
use super::{Brush, Color};
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct SpanData<F: Clone + PartialEq + Debug> { pub struct SpanData<F: Clone + PartialEq + Debug, B: Brush> {
pub family: FontFamilyHandle<F>, pub family: FontFamilyHandle<F>,
pub size: f64, pub size: f64,
pub stretch: Stretch, pub stretch: Stretch,
pub style: Style, pub style: Style,
pub weight: Weight, pub weight: Weight,
pub color: [u8; 4], pub color: Color<B>,
pub underline: bool, pub underline: bool,
pub strikethrough: bool, pub strikethrough: bool,
pub start: usize, pub start: usize,
@ -19,11 +20,11 @@ pub struct SpanData<F: Clone + PartialEq + Debug> {
pub count: usize, pub count: usize,
} }
impl<F: Clone + PartialEq + Debug> SpanData<F> { impl<F: Clone + PartialEq + Debug, B: Brush> SpanData<F, B> {
pub fn apply(&mut self, attr: &AttributeKind<F>) -> bool { pub fn apply(&mut self, attr: &AttributeKind<F, B>) -> bool {
match attr { match attr {
AttributeKind::Family(family) => self.family = family.clone(), AttributeKind::Family(family) => self.family = family.clone(),
AttributeKind::Color(color) => self.color = *color, AttributeKind::Color(color) => self.color = color.clone(),
AttributeKind::Size(size) => self.size = *size, AttributeKind::Size(size) => self.size = *size,
AttributeKind::Stretch(stretch) => self.stretch = *stretch, AttributeKind::Stretch(stretch) => self.stretch = *stretch,
AttributeKind::Style(style) => self.style = *style, AttributeKind::Style(style) => self.style = *style,
@ -34,7 +35,7 @@ impl<F: Clone + PartialEq + Debug> SpanData<F> {
false false
} }
pub fn check(&self, attr: &AttributeKind<F>) -> bool { pub fn check(&self, attr: &AttributeKind<F, B>) -> bool {
match attr { match attr {
AttributeKind::Family(family) => self.family == *family, AttributeKind::Family(family) => self.family == *family,
AttributeKind::Size(size) => self.size == *size, AttributeKind::Size(size) => self.size == *size,
@ -63,7 +64,7 @@ impl<F: Clone + PartialEq + Debug> SpanData<F> {
} }
} }
impl<F: Clone + PartialEq + Debug> Default for SpanData<F> { impl<F: Clone + PartialEq + Debug, B: Brush> Default for SpanData<F, B> {
fn default() -> Self { fn default() -> Self {
Self { Self {
family: FontFamilyHandle::Default, family: FontFamilyHandle::Default,
@ -71,7 +72,7 @@ impl<F: Clone + PartialEq + Debug> Default for SpanData<F> {
stretch: Stretch::NORMAL, stretch: Stretch::NORMAL,
style: Style::Normal, style: Style::Normal,
weight: Weight::NORMAL, weight: Weight::NORMAL,
color: [0, 0, 0, 255], color: Color::Solid([0, 0, 0, 255]),
underline: false, underline: false,
strikethrough: false, strikethrough: false,
start: 0, start: 0,
@ -93,28 +94,28 @@ pub struct ItemData {
} }
#[derive(Clone, PartialEq)] #[derive(Clone, PartialEq)]
pub enum AttributeKind<F: Clone + PartialEq + Debug> { pub enum AttributeKind<F: Clone + PartialEq + Debug, B: Brush> {
Family(FontFamilyHandle<F>), Family(FontFamilyHandle<F>),
Style(Style), Style(Style),
Weight(Weight), Weight(Weight),
Stretch(Stretch), Stretch(Stretch),
Size(f64), Size(f64),
Color([u8; 4]), Color(Color<B>),
Underline(bool), Underline(bool),
Strikethrough(bool), Strikethrough(bool),
} }
#[derive(Clone)] #[derive(Clone)]
pub struct RangedAttribute<F: Clone + PartialEq + Debug> { pub struct RangedAttribute<F: Clone + PartialEq + Debug, B: Brush> {
pub attr: AttributeKind<F>, pub attr: AttributeKind<F, B>,
pub start: usize, pub start: usize,
pub end: usize, pub end: usize,
} }
pub fn normalize_spans<F: Clone + PartialEq + Debug>( pub fn normalize_spans<F: Clone + PartialEq + Debug, B: Brush>(
attrs: &[RangedAttribute<F>], attrs: &[RangedAttribute<F, B>],
defaults: &SpanData<F>, defaults: &SpanData<F, B>,
spans: &mut Vec<SpanData<F>>, spans: &mut Vec<SpanData<F, B>>,
) { ) {
spans.push(defaults.clone()); spans.push(defaults.clone());
for attr in attrs { for attr in attrs {
@ -182,9 +183,9 @@ pub fn normalize_spans<F: Clone + PartialEq + Debug>(
spans.truncate(spans.len() - merged_count); spans.truncate(spans.len() - merged_count);
} }
pub fn itemize<F: Clone + PartialEq + Debug>( pub fn itemize<F: Clone + PartialEq + Debug, B: Brush>(
text: &str, text: &str,
spans: &mut [SpanData<F>], spans: &mut [SpanData<F, B>],
items: &mut Vec<ItemData>, items: &mut Vec<ItemData>,
) { ) {
use swash::text::Codepoint as _; use swash::text::Codepoint as _;
@ -255,9 +256,9 @@ struct SpanSplitRange {
last: Option<usize>, last: Option<usize>,
} }
fn span_split_range<F: Clone + PartialEq + Debug>( fn span_split_range<F: Clone + PartialEq + Debug, B: Brush>(
attr: &RangedAttribute<F>, attr: &RangedAttribute<F, B>,
spans: &[SpanData<F>], spans: &[SpanData<F, B>],
) -> SpanSplitRange { ) -> SpanSplitRange {
let mut range = SpanSplitRange::default(); let mut range = SpanSplitRange::default();
let start_span_index = match spans.binary_search_by(|span| span.start.cmp(&attr.start)) { let start_span_index = match spans.binary_search_by(|span| span.start.cmp(&attr.start)) {

View File

@ -19,26 +19,26 @@ pub use swash;
use core::ops::Range; use core::ops::Range;
#[derive(Clone)] #[derive(Clone)]
pub struct Layout<F: FontInstance> { pub struct Layout<F: FontHandle> {
pub glyphs: Vec<Glyph>, pub glyphs: Vec<Glyph>,
pub runs: Vec<Run<F>>, pub runs: Vec<Run<F>>,
} }
#[derive(Clone)] #[derive(Clone)]
pub struct Run<F: FontInstance> { pub struct Run<F: FontHandle> {
pub font: F, pub font: F,
pub text_range: Range<usize>, pub text_range: Range<usize>,
pub glyph_range: Range<usize>, pub glyph_range: Range<usize>,
} }
#[derive(Copy, Clone, PartialEq, Debug)] #[derive(Clone, PartialEq, Debug)]
pub enum Attribute<'a> { pub enum Attribute<'a, B: Brush = ()> {
FontFamily(FontFamily<'a>), FontFamily(FontFamily<'a>),
FontSize(f32), FontSize(f32),
FontStretch(FontStretch), FontStretch(FontStretch),
FontStyle(FontStyle), FontStyle(FontStyle),
FontWeight(FontWeight), FontWeight(FontWeight),
Color([u8; 4]), Color(Color<B>),
Underline(bool), Underline(bool),
Strikethrough(bool), Strikethrough(bool),
} }
@ -74,3 +74,17 @@ pub enum Alignment {
Center, Center,
Justified, Justified,
} }
/// Rendering style for glyphs and text decorations.
#[derive(Clone, PartialEq, Debug)]
pub enum Color<B: Brush = ()> {
/// 32-bit color in RGBA order.
Solid([u8; 4]),
/// Custom brush.
Brush(B)
}
/// Trait for types that represent custom rendering styles.
pub trait Brush: Clone + PartialEq + core::fmt::Debug {}
impl Brush for () {}

View File

@ -1,16 +1,16 @@
use super::font::*; use super::font::*;
use super::itemize::*; use super::itemize::*;
use super::{Glyph, Layout, Run}; use super::{Glyph, Layout, Run, Brush};
use fount::Locale; use fount::Locale;
use swash::shape::{self, ShapeContext}; use swash::shape::{self, ShapeContext};
use swash::text::cluster::CharCluster; use swash::text::cluster::CharCluster;
use swash::text::Script; use swash::text::Script;
use swash::{Attributes, FontRef, Synthesis}; use swash::{Attributes, FontRef, Synthesis};
pub fn shape<C: FontCollection>( pub fn shape<C: FontCollection, B: Brush>(
shape_context: &mut ShapeContext, shape_context: &mut ShapeContext,
font_selector: &mut FontSelector<C>, font_selector: &mut FontSelector<C, B>,
spans: &[SpanData<C::Family>], spans: &[SpanData<C::Family, B>],
items: &[ItemData], items: &[ItemData],
text: &str, text: &str,
glyphs: &mut Vec<Glyph>, glyphs: &mut Vec<Glyph>,
@ -73,9 +73,9 @@ pub fn shape<C: FontCollection>(
} }
} }
pub struct FontSelector<'a, C: FontCollection> { pub struct FontSelector<'a, C: FontCollection, B: Brush> {
fonts: &'a mut C, fonts: &'a mut C,
spans: &'a [SpanData<C::Family>], spans: &'a [SpanData<C::Family, B>],
span_index: u32, span_index: u32,
script: Script, script: Script,
locale: Option<Locale>, locale: Option<Locale>,
@ -83,8 +83,8 @@ pub struct FontSelector<'a, C: FontCollection> {
attrs: Attributes, attrs: Attributes,
} }
impl<'a, C: FontCollection> FontSelector<'a, C> { impl<'a, C: FontCollection, B: Brush> FontSelector<'a, C, B> {
pub fn new(fonts: &'a mut C, spans: &'a [SpanData<C::Family>], first_item: &ItemData) -> Self { pub fn new(fonts: &'a mut C, spans: &'a [SpanData<C::Family, B>], first_item: &ItemData) -> Self {
let first_span = &spans[0]; let first_span = &spans[0];
let attrs = first_span.attributes(); let attrs = first_span.attributes();
Self { Self {
@ -99,7 +99,7 @@ impl<'a, C: FontCollection> FontSelector<'a, C> {
} }
} }
impl<'a, C: FontCollection> shape::partition::Selector for FontSelector<'a, C> { impl<'a, C: FontCollection, B: Brush> shape::partition::Selector for FontSelector<'a, C, B> {
type SelectedFont = SelectedFont<C::Font>; type SelectedFont = SelectedFont<C::Font>;
fn select_font(&mut self, cluster: &mut CharCluster) -> Option<Self::SelectedFont> { fn select_font(&mut self, cluster: &mut CharCluster) -> Option<Self::SelectedFont> {
@ -136,17 +136,17 @@ impl<'a, C: FontCollection> shape::partition::Selector for FontSelector<'a, C> {
} }
} }
pub struct SelectedFont<F: FontInstance> { pub struct SelectedFont<F: FontHandle> {
pub font: F, pub font: F,
} }
impl<F: FontInstance> PartialEq for SelectedFont<F> { impl<F: FontHandle> PartialEq for SelectedFont<F> {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
self.font == other.font self.font == other.font
} }
} }
impl<F: FontInstance> shape::partition::SelectedFont for SelectedFont<F> { impl<F: FontHandle> shape::partition::SelectedFont for SelectedFont<F> {
fn font(&self) -> FontRef { fn font(&self) -> FontRef {
self.font.as_font_ref() self.font.as_font_ref()
} }