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::{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<FamilyId> {
fn convert_attr(
attr: &TextAttribute,
fonts: &mut SystemFontCollection,
) -> AttributeKind<FamilyId, ()> {
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),

View File

@ -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<C: FontCollection> {
state: LayoutState<C>,
pub struct LayoutContext<C: FontCollection, B: Brush> {
state: LayoutState<C, B>,
}
impl<C: FontCollection> LayoutContext<C> {
impl<C: FontCollection, B: Brush> LayoutContext<C, B> {
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<C: FontCollection> LayoutContext<C> {
}
}
pub struct LayoutBuilder<'a, C: FontCollection> {
state: &'a mut LayoutState<C>,
pub struct LayoutBuilder<'a, C: FontCollection, B: Brush> {
state: &'a mut LayoutState<C, B>,
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<C: FontCollection> {
pub struct LayoutState<C: FontCollection, B: Brush = ()> {
pub shape_context: ShapeContext,
pub defaults: SpanData<C::Family>,
pub attributes: Vec<RangedAttribute<C::Family>>,
pub spans: Vec<SpanData<C::Family>>,
pub defaults: SpanData<C::Family, B>,
pub attributes: Vec<RangedAttribute<C::Family, B>>,
pub spans: Vec<SpanData<C::Family, B>>,
pub items: Vec<ItemData>,
pub glyphs: Vec<Glyph>,
pub runs: Vec<Run<C::Font>>,
@ -47,7 +47,7 @@ pub struct LayoutState<C: FontCollection> {
pub alignment: Alignment,
}
impl<C: FontCollection> LayoutState<C> {
impl<C: FontCollection, B: Brush> LayoutState<C, B> {
pub fn new() -> Self {
Self {
shape_context: ShapeContext::new(),
@ -73,7 +73,7 @@ impl<C: FontCollection> LayoutState<C> {
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);
}
@ -81,7 +81,7 @@ impl<C: FontCollection> LayoutState<C> {
&mut self,
text_len: usize,
range: impl RangeBounds<usize>,
attribute: AttributeKind<C::Family>,
attribute: AttributeKind<C::Family, B>,
) {
let range = resolve_range(range, text_len);
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 {
Attribute::FontFamily(family) => AttributeKind::Family(match family {
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::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),
}

View File

@ -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<Synthesis> {
@ -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);

View File

@ -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()
}

View File

@ -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<F: Clone + PartialEq + Debug> {
pub struct SpanData<F: Clone + PartialEq + Debug, B: Brush> {
pub family: FontFamilyHandle<F>,
pub size: f64,
pub stretch: Stretch,
pub style: Style,
pub weight: Weight,
pub color: [u8; 4],
pub color: Color<B>,
pub underline: bool,
pub strikethrough: bool,
pub start: usize,
@ -19,11 +20,11 @@ pub struct SpanData<F: Clone + PartialEq + Debug> {
pub count: usize,
}
impl<F: Clone + PartialEq + Debug> SpanData<F> {
pub fn apply(&mut self, attr: &AttributeKind<F>) -> bool {
impl<F: Clone + PartialEq + Debug, B: Brush> SpanData<F, B> {
pub fn apply(&mut self, attr: &AttributeKind<F, B>) -> 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<F: Clone + PartialEq + Debug> SpanData<F> {
false
}
pub fn check(&self, attr: &AttributeKind<F>) -> bool {
pub fn check(&self, attr: &AttributeKind<F, B>) -> bool {
match attr {
AttributeKind::Family(family) => self.family == *family,
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 {
Self {
family: FontFamilyHandle::Default,
@ -71,7 +72,7 @@ impl<F: Clone + PartialEq + Debug> Default for SpanData<F> {
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<F: Clone + PartialEq + Debug> {
pub enum AttributeKind<F: Clone + PartialEq + Debug, B: Brush> {
Family(FontFamilyHandle<F>),
Style(Style),
Weight(Weight),
Stretch(Stretch),
Size(f64),
Color([u8; 4]),
Color(Color<B>),
Underline(bool),
Strikethrough(bool),
}
#[derive(Clone)]
pub struct RangedAttribute<F: Clone + PartialEq + Debug> {
pub attr: AttributeKind<F>,
pub struct RangedAttribute<F: Clone + PartialEq + Debug, B: Brush> {
pub attr: AttributeKind<F, B>,
pub start: usize,
pub end: usize,
}
pub fn normalize_spans<F: Clone + PartialEq + Debug>(
attrs: &[RangedAttribute<F>],
defaults: &SpanData<F>,
spans: &mut Vec<SpanData<F>>,
pub fn normalize_spans<F: Clone + PartialEq + Debug, B: Brush>(
attrs: &[RangedAttribute<F, B>],
defaults: &SpanData<F, B>,
spans: &mut Vec<SpanData<F, B>>,
) {
spans.push(defaults.clone());
for attr in attrs {
@ -182,9 +183,9 @@ pub fn normalize_spans<F: Clone + PartialEq + Debug>(
spans.truncate(spans.len() - merged_count);
}
pub fn itemize<F: Clone + PartialEq + Debug>(
pub fn itemize<F: Clone + PartialEq + Debug, B: Brush>(
text: &str,
spans: &mut [SpanData<F>],
spans: &mut [SpanData<F, B>],
items: &mut Vec<ItemData>,
) {
use swash::text::Codepoint as _;
@ -255,9 +256,9 @@ struct SpanSplitRange {
last: Option<usize>,
}
fn span_split_range<F: Clone + PartialEq + Debug>(
attr: &RangedAttribute<F>,
spans: &[SpanData<F>],
fn span_split_range<F: Clone + PartialEq + Debug, B: Brush>(
attr: &RangedAttribute<F, B>,
spans: &[SpanData<F, B>],
) -> SpanSplitRange {
let mut range = SpanSplitRange::default();
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;
#[derive(Clone)]
pub struct Layout<F: FontInstance> {
pub struct Layout<F: FontHandle> {
pub glyphs: Vec<Glyph>,
pub runs: Vec<Run<F>>,
}
#[derive(Clone)]
pub struct Run<F: FontInstance> {
pub struct Run<F: FontHandle> {
pub font: F,
pub text_range: Range<usize>,
pub glyph_range: Range<usize>,
}
#[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<B>),
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<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::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<C: FontCollection>(
pub fn shape<C: FontCollection, B: Brush>(
shape_context: &mut ShapeContext,
font_selector: &mut FontSelector<C>,
spans: &[SpanData<C::Family>],
font_selector: &mut FontSelector<C, B>,
spans: &[SpanData<C::Family, B>],
items: &[ItemData],
text: &str,
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,
spans: &'a [SpanData<C::Family>],
spans: &'a [SpanData<C::Family, B>],
span_index: u32,
script: Script,
locale: Option<Locale>,
@ -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<C::Family>], first_item: &ItemData) -> Self {
impl<'a, C: FontCollection, B: Brush> FontSelector<'a, C, B> {
pub fn new(fonts: &'a mut C, spans: &'a [SpanData<C::Family, B>], 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<C::Font>;
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,
}
impl<F: FontInstance> PartialEq for SelectedFont<F> {
impl<F: FontHandle> PartialEq for SelectedFont<F> {
fn eq(&self, other: &Self) -> bool {
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 {
self.font.as_font_ref()
}