From 7596ee8b03e9883af19e03131cb76cddb2eb51bb Mon Sep 17 00:00:00 2001 From: Chad Brokaw Date: Wed, 4 Aug 2021 05:00:49 -0400 Subject: [PATCH] initial support for custom brushes --- src/adapter.rs | 8 ++++++-- src/context.rs | 34 ++++++++++++++++----------------- src/font/mod.rs | 4 ++-- src/font/system/mod.rs | 4 ++-- src/itemize.rs | 43 +++++++++++++++++++++--------------------- src/lib.rs | 24 ++++++++++++++++++----- src/shape.rs | 24 +++++++++++------------ 7 files changed, 80 insertions(+), 61 deletions(-) diff --git a/src/adapter.rs b/src/adapter.rs index 48ded98..525bfc3 100644 --- a/src/adapter.rs +++ b/src/adapter.rs @@ -2,6 +2,7 @@ use super::context::LayoutState; use super::font::system::{Font, SystemFontCollection}; use super::font::{FontCollection, FontFamilyHandle, GenericFontFamily}; use super::itemize::*; +use super::Color; use super::Layout; use fount::FamilyId; use piet::kurbo::{Point, Rect, Size}; @@ -174,7 +175,10 @@ impl piet::TextLayoutBuilder for PietTextLayoutBuilder { } } -fn convert_attr(attr: &TextAttribute, fonts: &mut SystemFontCollection) -> AttributeKind { +fn convert_attr( + attr: &TextAttribute, + fonts: &mut SystemFontCollection, +) -> AttributeKind { use piet::FontStyle; use swash::{Style, Weight}; match attr { @@ -201,7 +205,7 @@ fn convert_attr(attr: &TextAttribute, fonts: &mut SystemFontCollection) -> Attri }), TextAttribute::TextColor(color) => { 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::Strikethrough(yes) => AttributeKind::Strikethrough(*yes), diff --git a/src/context.rs b/src/context.rs index 03a27dc..5dccd4e 100644 --- a/src/context.rs +++ b/src/context.rs @@ -1,23 +1,23 @@ use super::font::{FontCollection, FontFamily, FontFamilyHandle, GenericFontFamily}; use super::itemize::{AttributeKind, ItemData, RangedAttribute, SpanData}; -use super::{Alignment, Attribute}; +use super::{Alignment, Attribute, Brush}; use super::{Glyph, Layout, Run}; use core::ops::{Bound, Range, RangeBounds}; use fount::Library; use swash::shape::ShapeContext; -pub struct LayoutContext { - state: LayoutState, +pub struct LayoutContext { + state: LayoutState, } -impl LayoutContext { +impl LayoutContext { pub fn new() -> Self { Self { 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(); LayoutBuilder { state: &mut self.state, @@ -27,19 +27,19 @@ impl LayoutContext { } } -pub struct LayoutBuilder<'a, C: FontCollection> { - state: &'a mut LayoutState, +pub struct LayoutBuilder<'a, C: FontCollection, B: Brush> { + state: &'a mut LayoutState, fonts: &'a mut C, text: &'a str, } -impl<'a, C: FontCollection> LayoutBuilder<'a, C> {} +impl<'a, C: FontCollection, B: Brush> LayoutBuilder<'a, C, B> {} -pub struct LayoutState { +pub struct LayoutState { pub shape_context: ShapeContext, - pub defaults: SpanData, - pub attributes: Vec>, - pub spans: Vec>, + pub defaults: SpanData, + pub attributes: Vec>, + pub spans: Vec>, pub items: Vec, pub glyphs: Vec, pub runs: Vec>, @@ -47,7 +47,7 @@ pub struct LayoutState { pub alignment: Alignment, } -impl LayoutState { +impl LayoutState { pub fn new() -> Self { Self { shape_context: ShapeContext::new(), @@ -73,7 +73,7 @@ impl LayoutState { self.alignment = Alignment::Start; } - pub fn default_attribute(&mut self, attribute: AttributeKind) { + pub fn default_attribute(&mut self, attribute: AttributeKind) { self.defaults.apply(&attribute); } @@ -81,7 +81,7 @@ impl LayoutState { &mut self, text_len: usize, range: impl RangeBounds, - attribute: AttributeKind, + attribute: AttributeKind, ) { let range = resolve_range(range, text_len); if !range.is_empty() { @@ -116,7 +116,7 @@ impl LayoutState { } } -fn convert_attr(attr: &Attribute, fonts: &mut C) -> AttributeKind { +fn convert_attr(attr: &Attribute, fonts: &mut C) -> AttributeKind { match attr { Attribute::FontFamily(family) => AttributeKind::Family(match family { FontFamily::Named(name) => fonts @@ -129,7 +129,7 @@ fn convert_attr(attr: &Attribute, fonts: &mut C) -> Attribute Attribute::FontWeight(weight) => AttributeKind::Weight(*weight), Attribute::FontStyle(style) => AttributeKind::Style(*style), 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::Strikethrough(yes) => AttributeKind::Strikethrough(*yes), } diff --git a/src/font/mod.rs b/src/font/mod.rs index 90f26a8..c3bfadb 100644 --- a/src/font/mod.rs +++ b/src/font/mod.rs @@ -10,7 +10,7 @@ pub use swash::{Stretch as FontStretch, Style as FontStyle, Weight as FontWeight pub mod system; -pub trait FontInstance: Clone + PartialEq { +pub trait FontHandle: Clone + PartialEq { fn as_font_ref(&self) -> FontRef; fn synthesis(&self) -> Option { @@ -20,7 +20,7 @@ pub trait FontInstance: Clone + PartialEq { pub trait FontCollection { type Family: Clone + PartialEq + Debug; - type Font: FontInstance; + type Font: FontHandle; /// Begins a layout sesion with this collection. fn begin_session(&mut self); diff --git a/src/font/system/mod.rs b/src/font/system/mod.rs index 802f054..30a002e 100644 --- a/src/font/system/mod.rs +++ b/src/font/system/mod.rs @@ -1,12 +1,12 @@ mod collection; mod font; -use super::FontInstance; +use super::FontHandle; pub use collection::SystemFontCollection; pub use font::Font; use swash::{FontRef, Synthesis}; -impl FontInstance for Font { +impl FontHandle for Font { fn as_font_ref(&self) -> FontRef { self.as_ref() } diff --git a/src/itemize.rs b/src/itemize.rs index 8ee9292..983d34d 100644 --- a/src/itemize.rs +++ b/src/itemize.rs @@ -3,15 +3,16 @@ use core::fmt::Debug; use fount::{FamilyId, GenericFamily, Locale}; use swash::text::Script; use swash::{Attributes, Stretch, Style, Weight}; +use super::{Brush, Color}; #[derive(Clone, Debug)] -pub struct SpanData { +pub struct SpanData { pub family: FontFamilyHandle, pub size: f64, pub stretch: Stretch, pub style: Style, pub weight: Weight, - pub color: [u8; 4], + pub color: Color, pub underline: bool, pub strikethrough: bool, pub start: usize, @@ -19,11 +20,11 @@ pub struct SpanData { pub count: usize, } -impl SpanData { - pub fn apply(&mut self, attr: &AttributeKind) -> bool { +impl SpanData { + pub fn apply(&mut self, attr: &AttributeKind) -> bool { match attr { 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::Stretch(stretch) => self.stretch = *stretch, AttributeKind::Style(style) => self.style = *style, @@ -34,7 +35,7 @@ impl SpanData { false } - pub fn check(&self, attr: &AttributeKind) -> bool { + pub fn check(&self, attr: &AttributeKind) -> bool { match attr { AttributeKind::Family(family) => self.family == *family, AttributeKind::Size(size) => self.size == *size, @@ -63,7 +64,7 @@ impl SpanData { } } -impl Default for SpanData { +impl Default for SpanData { fn default() -> Self { Self { family: FontFamilyHandle::Default, @@ -71,7 +72,7 @@ impl Default for SpanData { stretch: Stretch::NORMAL, style: Style::Normal, weight: Weight::NORMAL, - color: [0, 0, 0, 255], + color: Color::Solid([0, 0, 0, 255]), underline: false, strikethrough: false, start: 0, @@ -93,28 +94,28 @@ pub struct ItemData { } #[derive(Clone, PartialEq)] -pub enum AttributeKind { +pub enum AttributeKind { Family(FontFamilyHandle), Style(Style), Weight(Weight), Stretch(Stretch), Size(f64), - Color([u8; 4]), + Color(Color), Underline(bool), Strikethrough(bool), } #[derive(Clone)] -pub struct RangedAttribute { - pub attr: AttributeKind, +pub struct RangedAttribute { + pub attr: AttributeKind, pub start: usize, pub end: usize, } -pub fn normalize_spans( - attrs: &[RangedAttribute], - defaults: &SpanData, - spans: &mut Vec>, +pub fn normalize_spans( + attrs: &[RangedAttribute], + defaults: &SpanData, + spans: &mut Vec>, ) { spans.push(defaults.clone()); for attr in attrs { @@ -182,9 +183,9 @@ pub fn normalize_spans( spans.truncate(spans.len() - merged_count); } -pub fn itemize( +pub fn itemize( text: &str, - spans: &mut [SpanData], + spans: &mut [SpanData], items: &mut Vec, ) { use swash::text::Codepoint as _; @@ -255,9 +256,9 @@ struct SpanSplitRange { last: Option, } -fn span_split_range( - attr: &RangedAttribute, - spans: &[SpanData], +fn span_split_range( + attr: &RangedAttribute, + spans: &[SpanData], ) -> SpanSplitRange { let mut range = SpanSplitRange::default(); let start_span_index = match spans.binary_search_by(|span| span.start.cmp(&attr.start)) { diff --git a/src/lib.rs b/src/lib.rs index 52c6798..8764b70 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,26 +19,26 @@ pub use swash; use core::ops::Range; #[derive(Clone)] -pub struct Layout { +pub struct Layout { pub glyphs: Vec, pub runs: Vec>, } #[derive(Clone)] -pub struct Run { +pub struct Run { pub font: F, pub text_range: Range, pub glyph_range: Range, } -#[derive(Copy, Clone, PartialEq, Debug)] -pub enum Attribute<'a> { +#[derive(Clone, PartialEq, Debug)] +pub enum Attribute<'a, B: Brush = ()> { FontFamily(FontFamily<'a>), FontSize(f32), FontStretch(FontStretch), FontStyle(FontStyle), FontWeight(FontWeight), - Color([u8; 4]), + Color(Color), Underline(bool), Strikethrough(bool), } @@ -74,3 +74,17 @@ pub enum Alignment { Center, Justified, } + +/// Rendering style for glyphs and text decorations. +#[derive(Clone, PartialEq, Debug)] +pub enum Color { + /// 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 () {} diff --git a/src/shape.rs b/src/shape.rs index eb50216..98590ce 100644 --- a/src/shape.rs +++ b/src/shape.rs @@ -1,16 +1,16 @@ use super::font::*; use super::itemize::*; -use super::{Glyph, Layout, Run}; +use super::{Glyph, Layout, Run, Brush}; 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( +pub fn shape( shape_context: &mut ShapeContext, - font_selector: &mut FontSelector, - spans: &[SpanData], + font_selector: &mut FontSelector, + spans: &[SpanData], items: &[ItemData], text: &str, glyphs: &mut Vec, @@ -73,9 +73,9 @@ pub fn shape( } } -pub struct FontSelector<'a, C: FontCollection> { +pub struct FontSelector<'a, C: FontCollection, B: Brush> { fonts: &'a mut C, - spans: &'a [SpanData], + spans: &'a [SpanData], span_index: u32, script: Script, locale: Option, @@ -83,8 +83,8 @@ pub struct FontSelector<'a, C: FontCollection> { attrs: Attributes, } -impl<'a, C: FontCollection> FontSelector<'a, C> { - pub fn new(fonts: &'a mut C, spans: &'a [SpanData], first_item: &ItemData) -> Self { +impl<'a, C: FontCollection, B: Brush> FontSelector<'a, C, B> { + pub fn new(fonts: &'a mut C, spans: &'a [SpanData], first_item: &ItemData) -> Self { let first_span = &spans[0]; let attrs = first_span.attributes(); 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; fn select_font(&mut self, cluster: &mut CharCluster) -> Option { @@ -136,17 +136,17 @@ impl<'a, C: FontCollection> shape::partition::Selector for FontSelector<'a, C> { } } -pub struct SelectedFont { +pub struct SelectedFont { pub font: F, } -impl PartialEq for SelectedFont { +impl PartialEq for SelectedFont { fn eq(&self, other: &Self) -> bool { self.font == other.font } } -impl shape::partition::SelectedFont for SelectedFont { +impl shape::partition::SelectedFont for SelectedFont { fn font(&self) -> FontRef { self.font.as_font_ref() }