forked from linebender/vello
-
Notifications
You must be signed in to change notification settings - Fork 0
[Sparse Strip]: Text API (outlines only) #3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Changes from all commits
Commits
Show all changes
23 commits
Select commit
Hold shift + click to select a range
33ad8f8
Outline text support
taj-p 5d20087
.
taj-p e1ba82d
.
taj-p 6529b19
Use builder and iterator
taj-p e688489
.
taj-p 5308248
.
taj-p 9fb5117
.
taj-p 9f70096
.
taj-p 74f74d9
..
taj-p 43847b5
.
taj-p 9947b56
.
taj-p 1ef5c5c
.
taj-p 1cda9ce
.
taj-p 77ee3b1
.
taj-p 2f5328f
.
taj-p c43afda
.
taj-p 16f53b5
.
taj-p da12137
.
taj-p 54a2d43
CPU tests
taj-p 33540a4
.
taj-p 5853725
.
taj-p 0b7bb7b
.
taj-p 6a6d158
.
taj-p File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| // Copyright 2025 the Vello Authors | ||
| // SPDX-License-Identifier: Apache-2.0 OR MIT | ||
|
|
||
| //! Types for glyphs. | ||
|
|
||
| /// Positioned glyph. | ||
| #[derive(Copy, Clone, Default, Debug)] | ||
| pub struct Glyph { | ||
| /// The font-specific identifier for this glyph. | ||
| /// | ||
| /// This ID is specific to the font being used and corresponds to the | ||
| /// glyph index within that font. It is *not* a Unicode code point. | ||
| pub id: u32, | ||
| /// X-offset in run, relative to transform. | ||
| pub x: f32, | ||
| /// Y-offset in run, relative to transform. | ||
| pub y: f32, | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -12,4 +12,5 @@ pub use peniko; | |
| pub use peniko::color; | ||
| pub use peniko::kurbo; | ||
| pub mod execute; | ||
| pub mod glyph; | ||
| pub mod paint; | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,190 @@ | ||
| // Copyright 2025 the Vello Authors | ||
| // SPDX-License-Identifier: Apache-2.0 OR MIT | ||
|
|
||
| //! Processing and drawing glyphs. | ||
|
|
||
| use crate::peniko::Font; | ||
| use skrifa::instance::{NormalizedCoord, Size}; | ||
| use skrifa::outline::DrawSettings; | ||
| use skrifa::{ | ||
| GlyphId, MetadataProvider, | ||
| outline::{HintingInstance, HintingOptions, OutlinePen}, | ||
| }; | ||
| use vello_api::kurbo::{Affine, BezPath, Vec2}; | ||
|
|
||
| pub use vello_api::glyph::*; | ||
|
|
||
| /// A glyph prepared for rendering. | ||
| #[derive(Debug)] | ||
| pub enum PreparedGlyph { | ||
| /// A glyph defined by its outline. | ||
| Outline(OutlineGlyph), | ||
| // TODO: Image and Colr variants. | ||
| } | ||
|
|
||
| /// A glyph defined by a path (its outline) and a local transform. | ||
| #[derive(Debug)] | ||
| pub struct OutlineGlyph { | ||
| /// The path of the glyph. | ||
| pub path: BezPath, | ||
| /// The local transform of the glyph. | ||
| pub local_transform: Affine, | ||
| } | ||
|
|
||
| /// Trait for types that can render glyphs. | ||
| pub trait GlyphRenderer { | ||
| /// Fill glyphs with the current paint and fill rule. | ||
| fn fill_glyphs(&mut self, glyphs: impl Iterator<Item = PreparedGlyph>); | ||
|
|
||
| /// Stroke glyphs with the current paint and stroke settings. | ||
| fn stroke_glyphs(&mut self, glyphs: impl Iterator<Item = PreparedGlyph>); | ||
| } | ||
|
|
||
| /// A builder for configuring and drawing glyphs. | ||
| #[derive(Debug)] | ||
| pub struct GlyphRunBuilder<'a, T: GlyphRenderer + 'a> { | ||
| run: GlyphRun, | ||
| renderer: &'a mut T, | ||
| } | ||
|
|
||
| impl<'a, T: GlyphRenderer + 'a> GlyphRunBuilder<'a, T> { | ||
| /// Creates a new builder for drawing glyphs. | ||
| pub fn new(font: Font, renderer: &'a mut T) -> Self { | ||
| Self { | ||
| run: GlyphRun { | ||
| font, | ||
| font_size: 16.0, | ||
| glyph_transform: None, | ||
| hint: true, | ||
| normalized_coords: Vec::new(), | ||
| }, | ||
| renderer, | ||
| } | ||
| } | ||
|
|
||
| /// Set the font size in pixels per em. | ||
| pub fn font_size(mut self, size: f32) -> Self { | ||
| self.run.font_size = size; | ||
| self | ||
| } | ||
|
|
||
| /// Set the per-glyph transform. Can be used to apply skew to simulate italic text. | ||
| pub fn glyph_transform(mut self, transform: Affine) -> Self { | ||
| self.run.glyph_transform = Some(transform); | ||
| self | ||
| } | ||
|
|
||
| /// Set whether font hinting is enabled. | ||
| pub fn hint(mut self, hint: bool) -> Self { | ||
| self.run.hint = hint; | ||
| self | ||
| } | ||
|
|
||
| /// Set normalized variation coordinates for variable fonts. | ||
| pub fn normalized_coords(mut self, coords: Vec<NormalizedCoord>) -> Self { | ||
| self.run.normalized_coords = coords; | ||
| self | ||
| } | ||
|
|
||
| /// Consumes the builder and fills the glyphs with the current configuration. | ||
| pub fn fill_glyphs(self, glyphs: impl Iterator<Item = &'a Glyph>) { | ||
| self.renderer | ||
| .fill_glyphs(Self::prepare_glyphs(&self.run, glyphs)); | ||
| } | ||
|
|
||
| /// Consumes the builder and strokes the glyphs with the current configuration. | ||
| pub fn stroke_glyphs(self, glyphs: impl Iterator<Item = &'a Glyph>) { | ||
| self.renderer | ||
| .stroke_glyphs(Self::prepare_glyphs(&self.run, glyphs)); | ||
| } | ||
|
|
||
| fn prepare_glyphs( | ||
| run: &GlyphRun, | ||
| glyphs: impl Iterator<Item = &'a Glyph>, | ||
| ) -> impl Iterator<Item = PreparedGlyph> { | ||
| let font = skrifa::FontRef::from_index(run.font.data.as_ref(), run.font.index).unwrap(); | ||
| let outlines = font.outline_glyphs(); | ||
| let size = Size::new(run.font_size); | ||
| let normalized_coords = run.normalized_coords.as_slice(); | ||
| let hinting_instance = if run.hint { | ||
| // TODO: Cache hinting instance. | ||
| HintingInstance::new(&outlines, size, normalized_coords, HINTING_OPTIONS).ok() | ||
| } else { | ||
| None | ||
| }; | ||
| glyphs.filter_map(move |glyph| { | ||
| let draw_settings = if let Some(hinting_instance) = &hinting_instance { | ||
| DrawSettings::hinted(hinting_instance, false) | ||
| } else { | ||
| DrawSettings::unhinted(size, normalized_coords) | ||
| }; | ||
| let outline = outlines.get(GlyphId::new(glyph.id))?; | ||
| let mut path = OutlinePath(BezPath::new()); | ||
| outline.draw(draw_settings, &mut path).ok()?; | ||
| let mut transform = Affine::translate(Vec2::new(glyph.x as f64, glyph.y as f64)); | ||
| if let Some(glyph_transform) = run.glyph_transform { | ||
| transform *= glyph_transform; | ||
| } | ||
| Some(PreparedGlyph::Outline(OutlineGlyph { | ||
| path: path.0, | ||
| local_transform: transform, | ||
| })) | ||
| }) | ||
| } | ||
| } | ||
|
|
||
| /// A sequence of glyphs with shared rendering properties. | ||
| #[derive(Clone, Debug)] | ||
| struct GlyphRun { | ||
| /// Font for all glyphs in the run. | ||
| pub font: Font, | ||
| /// Size of the font in pixels per em. | ||
| pub font_size: f32, | ||
| /// Per-glyph transform. Can be used to apply skew to simulate italic text. | ||
| pub glyph_transform: Option<Affine>, | ||
| /// Normalized variation coordinates for variable fonts. | ||
| pub normalized_coords: Vec<NormalizedCoord>, | ||
| /// Controls whether font hinting is enabled. | ||
| pub hint: bool, | ||
| } | ||
|
|
||
| // TODO: Although these are sane defaults, we might want to make them | ||
| // configurable. | ||
| const HINTING_OPTIONS: HintingOptions = HintingOptions { | ||
| engine: skrifa::outline::Engine::AutoFallback, | ||
| target: skrifa::outline::Target::Smooth { | ||
| mode: skrifa::outline::SmoothMode::Lcd, | ||
| symmetric_rendering: false, | ||
| preserve_linear_metrics: true, | ||
| }, | ||
| }; | ||
|
|
||
| struct OutlinePath(BezPath); | ||
|
|
||
| // Note that we flip the y-axis to match our coordinate system. | ||
| impl OutlinePen for OutlinePath { | ||
| #[inline] | ||
| fn move_to(&mut self, x: f32, y: f32) { | ||
| self.0.move_to((x, -y)); | ||
| } | ||
|
|
||
| #[inline] | ||
| fn line_to(&mut self, x: f32, y: f32) { | ||
| self.0.line_to((x, -y)); | ||
| } | ||
|
|
||
| #[inline] | ||
| fn curve_to(&mut self, cx0: f32, cy0: f32, cx1: f32, cy1: f32, x: f32, y: f32) { | ||
| self.0.curve_to((cx0, -cy0), (cx1, -cy1), (x, -y)); | ||
| } | ||
|
|
||
| #[inline] | ||
| fn quad_to(&mut self, cx: f32, cy: f32, x: f32, y: f32) { | ||
| self.0.quad_to((cx, -cy), (x, -y)); | ||
| } | ||
|
|
||
| #[inline] | ||
| fn close(&mut self) { | ||
| self.0.close_path(); | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.