Skip to content

Commit 420d99d

Browse files
committed
Add font embolden
switch to gpu embolden
1 parent 6f22a89 commit 420d99d

File tree

11 files changed

+601
-93
lines changed

11 files changed

+601
-93
lines changed

vello/src/scene.rs

Lines changed: 49 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use peniko::{
99
BlendMode, Blob, Brush, BrushRef, Color, ColorStop, ColorStops, ColorStopsSource, Compose,
1010
Extend, Fill, Font, Gradient, Image, Mix, StyleRef,
1111
color::{AlphaColor, DynamicColor, Srgb, palette},
12-
kurbo::{Affine, BezPath, Point, Rect, Shape, Stroke, StrokeOpts, Vec2},
12+
kurbo::{Affine, BezPath, Join, Point, Rect, Shape, Stroke, StrokeOpts, Vec2},
1313
};
1414
use png::{BitDepth, ColorType, Transformations};
1515
use skrifa::{
@@ -22,7 +22,7 @@ use skrifa::{
2222
};
2323
#[cfg(feature = "bump_estimate")]
2424
use vello_encoding::BumpAllocatorMemory;
25-
use vello_encoding::{Encoding, Glyph, GlyphRun, NormalizedCoord, Patch, Transform};
25+
use vello_encoding::{EmboldenStyle, Encoding, Glyph, GlyphRun, NormalizedCoord, Patch, Transform};
2626

2727
// TODO - Document invariants and edge cases (#470)
2828
// - What happens when we pass a transform matrix with NaN values to the Scene?
@@ -361,6 +361,7 @@ impl<'a> DrawGlyphs<'a> {
361361
transform: Transform::IDENTITY,
362362
glyph_transform: None,
363363
font_size: 16.0,
364+
embolden_style: EmboldenStyle::default(),
364365
hint: false,
365366
normalized_coords: coords_start..coords_start,
366367
style: Fill::NonZero.into(),
@@ -411,6 +412,37 @@ impl<'a> DrawGlyphs<'a> {
411412
self
412413
}
413414

415+
/// Sets the amount of emboldening to apply.
416+
///
417+
/// The value represents the amount to embolden in em units.
418+
/// A value of 0.0 means no emboldening (the default).
419+
/// Typical values range from 0.01 to 0.1.
420+
///
421+
/// The default value is 0.0 (no emboldening).
422+
#[must_use]
423+
pub fn embolden(mut self, amount: f32) -> Self {
424+
self.run.embolden_style.embolden = amount;
425+
self
426+
}
427+
428+
/// Sets the join to use when emboldening.
429+
///
430+
/// The default value is Miter.
431+
#[must_use]
432+
pub fn embolden_join(mut self, join: Join) -> Self {
433+
self.run.embolden_style.join = join;
434+
self
435+
}
436+
437+
/// Sets the miter limit to use when emboldening with a miter join.
438+
///
439+
/// The default value is 4.0.
440+
#[must_use]
441+
pub fn embolden_miter_limit(mut self, miter_limit: f32) -> Self {
442+
self.run.embolden_style.miter_limit = miter_limit;
443+
self
444+
}
445+
414446
/// Sets the normalized design space coordinates for a variable font instance.
415447
#[must_use]
416448
pub fn normalized_coords(mut self, coords: &[NormalizedCoord]) -> Self {
@@ -796,15 +828,18 @@ impl ColorPainter for DrawColorGlyphs<'_> {
796828
return;
797829
};
798830

799-
let mut path = BezPathOutline(BezPath::new());
800831
let draw_settings = DrawSettings::unhinted(Size::unscaled(), self.location);
801-
802-
let Ok(_) = outline.draw(draw_settings, &mut path) else {
803-
return;
832+
let path = {
833+
let mut path = BezPathOutline(BezPath::new());
834+
let Ok(_) = outline.draw(draw_settings, &mut path) else {
835+
return;
836+
};
837+
path.0
804838
};
839+
805840
self.clip_depth += 1;
806841
self.scene
807-
.push_layer(Mix::Clip, 1.0, self.last_transform().to_kurbo(), &path.0);
842+
.push_layer(Mix::Clip, 1.0, self.last_transform().to_kurbo(), &path);
808843
}
809844

810845
fn push_clip_box(&mut self, clip_box: skrifa::raw::types::BoundingBox<f32>) {
@@ -878,11 +913,14 @@ impl ColorPainter for DrawColorGlyphs<'_> {
878913
return;
879914
};
880915

881-
let mut path = BezPathOutline(BezPath::new());
882916
let draw_settings = DrawSettings::unhinted(Size::unscaled(), self.location);
883917

884-
let Ok(_) = outline.draw(draw_settings, &mut path) else {
885-
return;
918+
let path = {
919+
let mut path = BezPathOutline(BezPath::new());
920+
let Ok(_) = outline.draw(draw_settings, &mut path) else {
921+
return;
922+
};
923+
path.0
886924
};
887925

888926
let transform = self.last_transform();
@@ -893,7 +931,7 @@ impl ColorPainter for DrawColorGlyphs<'_> {
893931
brush_transform
894932
.map(conv_skrifa_transform)
895933
.map(|it| it.to_kurbo()),
896-
&path.0,
934+
&path,
897935
);
898936
}
899937
}

vello_encoding/src/encoding.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
// Copyright 2022 the Vello Authors
22
// SPDX-License-Identifier: Apache-2.0 OR MIT
33

4+
use crate::path::{EmboldenStyle, WindingPathEncoder};
5+
46
use super::{
57
DrawBlurRoundedRect, DrawColor, DrawImage, DrawLinearGradient, DrawRadialGradient,
68
DrawSweepGradient, DrawTag, Glyph, GlyphRun, NormalizedCoord, Patch, PathEncoder, PathTag,
@@ -33,6 +35,8 @@ pub struct Encoding {
3335
pub transforms: Vec<Transform>,
3436
/// The style stream
3537
pub styles: Vec<Style>,
38+
/// Winding information for subpaths of filled shapes
39+
pub path_windings: Vec<f32>,
3640
/// Late bound resource data.
3741
pub resources: Resources,
3842
/// Number of encoded paths.
@@ -77,6 +81,7 @@ impl Encoding {
7781
self.styles.clear();
7882
self.draw_data.clear();
7983
self.draw_tags.clear();
84+
self.path_windings.clear();
8085
self.n_paths = 0;
8186
self.n_path_segments = 0;
8287
self.n_clips = 0;
@@ -112,6 +117,7 @@ impl Encoding {
112117
run.stream_offsets.draw_data += offsets.draw_data;
113118
run.stream_offsets.transforms += offsets.transforms;
114119
run.stream_offsets.styles += offsets.styles;
120+
run.stream_offsets.path_windings += offsets.path_windings;
115121
run
116122
}));
117123
self.resources
@@ -149,6 +155,7 @@ impl Encoding {
149155
self.path_data.extend_from_slice(&other.path_data);
150156
self.draw_tags.extend_from_slice(&other.draw_tags);
151157
self.draw_data.extend_from_slice(&other.draw_data);
158+
self.path_windings.extend_from_slice(&other.path_windings);
152159
self.n_paths += other.n_paths;
153160
self.n_path_segments += other.n_path_segments;
154161
self.n_clips += other.n_clips;
@@ -175,6 +182,7 @@ impl Encoding {
175182
draw_data: self.draw_data.len(),
176183
transforms: self.transforms.len(),
177184
styles: self.styles.len(),
185+
path_windings: self.path_windings.len(),
178186
}
179187
}
180188

@@ -188,6 +196,16 @@ impl Encoding {
188196
self.encode_style(Style::from_stroke(stroke));
189197
}
190198

199+
/// Encodes a fill style with embolden.
200+
pub fn encode_fill_style_embolden(&mut self, fill: Fill, embolden_fill: EmboldenStyle) {
201+
self.encode_style(Style::from_embolden_fill(fill, embolden_fill));
202+
}
203+
204+
/// Encodes a stroke style with embolden.
205+
pub fn encode_stroke_style_embolden(&mut self, stroke: &Stroke, embolden: f32) {
206+
self.encode_style(Style::from_stroke(stroke).with_embolden(embolden));
207+
}
208+
191209
fn encode_style(&mut self, style: Style) {
192210
if self.flags & Self::FORCE_NEXT_STYLE != 0 || self.styles.last() != Some(&style) {
193211
self.path_tags.push(PathTag::STYLE);
@@ -225,6 +243,18 @@ impl Encoding {
225243
)
226244
}
227245

246+
/// Returns an encoder for encoding a filled path with winding data for expansion.
247+
pub fn encode_winding_path(&mut self, is_fill: bool) -> WindingPathEncoder<'_> {
248+
WindingPathEncoder::new(
249+
&mut self.path_tags,
250+
&mut self.path_data,
251+
&mut self.n_path_segments,
252+
&mut self.n_paths,
253+
&mut self.path_windings,
254+
is_fill,
255+
)
256+
}
257+
228258
/// Encodes a shape. If `is_fill` is true, all subpaths will be automatically closed.
229259
/// Returns true if a non-zero number of segments were encoded.
230260
pub fn encode_shape(&mut self, shape: &impl Shape, is_fill: bool) -> bool {
@@ -572,6 +602,8 @@ pub struct StreamOffsets {
572602
pub transforms: usize,
573603
/// Current length of style stream.
574604
pub styles: usize,
605+
/// Current length of path winding stream.
606+
pub path_windings: usize,
575607
}
576608

577609
impl StreamOffsets {
@@ -582,6 +614,7 @@ impl StreamOffsets {
582614
self.draw_data += other.draw_data;
583615
self.transforms += other.transforms;
584616
self.styles += other.styles;
617+
self.path_windings += other.path_windings;
585618
}
586619
}
587620

vello_encoding/src/glyph.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@
33

44
use std::ops::Range;
55

6-
use peniko::{Font, Style};
6+
use crate::path::EmboldenStyle;
77

88
use super::{StreamOffsets, Transform};
9+
use peniko::{Font, Style};
910

1011
/// Positioned glyph.
1112
#[derive(Copy, Clone, Default, Debug)]
@@ -29,6 +30,8 @@ pub struct GlyphRun {
2930
pub glyph_transform: Option<Transform>,
3031
/// Size of the font in pixels per em.
3132
pub font_size: f32,
33+
/// The style to use when emboldening with embolden in em.
34+
pub embolden_style: EmboldenStyle,
3235
/// True if hinting is enabled.
3336
pub hint: bool,
3437
/// Range of normalized coordinates in the parent encoding.

vello_encoding/src/glyph_cache.rs

Lines changed: 46 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
use std::collections::HashMap;
55
use std::sync::Arc;
66

7+
use crate::path::EmboldenStyle;
8+
79
use super::{Encoding, StreamOffsets};
810

911
use peniko::{Font, Style};
@@ -30,6 +32,7 @@ impl GlyphCache {
3032
size: f32,
3133
hint: bool,
3234
style: &'a Style,
35+
embolden_fill: EmboldenStyle,
3336
) -> Option<GlyphCacheSession<'a>> {
3437
let font_id = font.data.id();
3538
let font_index = font.index;
@@ -64,10 +67,12 @@ impl GlyphCache {
6467
};
6568
// TODO: we're ignoring dashing for now
6669
let style_bits = match style {
67-
Style::Fill(fill) => super::path::Style::from_fill(*fill),
68-
Style::Stroke(stroke) => super::path::Style::from_stroke(stroke),
70+
Style::Fill(fill) => super::path::Style::from_embolden_fill(*fill, embolden_fill),
71+
Style::Stroke(stroke) => {
72+
super::path::Style::from_stroke(stroke).with_embolden(embolden_fill.embolden)
73+
}
6974
};
70-
let style_bits: [u32; 2] = bytemuck::cast(style_bits);
75+
let style_bits: [u32; 3] = bytemuck::cast(style_bits);
7176
Some(GlyphCacheSession {
7277
free_list: &mut self.free_list,
7378
map,
@@ -82,6 +87,7 @@ impl GlyphCache {
8287
hinter,
8388
serial: self.serial,
8489
cached_count: &mut self.cached_count,
90+
embolden_fill_style: embolden_fill,
8591
})
8692
}
8793

@@ -141,11 +147,12 @@ pub(crate) struct GlyphCacheSession<'a> {
141147
size: Size,
142148
size_bits: u32,
143149
style: &'a Style,
144-
style_bits: [u32; 2],
150+
style_bits: [u32; 3],
145151
outlines: OutlineGlyphCollection<'a>,
146152
hinter: Option<&'a HintingInstance>,
147153
serial: u64,
148154
cached_count: &'a mut usize,
155+
embolden_fill_style: EmboldenStyle,
149156
}
150157

151158
impl GlyphCacheSession<'_> {
@@ -171,30 +178,53 @@ impl GlyphCacheSession<'_> {
171178
encoding_ptr.reset();
172179
let is_fill = match &self.style {
173180
Style::Fill(fill) => {
174-
encoding_ptr.encode_fill_style(*fill);
181+
encoding_ptr.encode_fill_style_embolden(*fill, self.embolden_fill_style);
175182
true
176183
}
177184
Style::Stroke(stroke) => {
178-
encoding_ptr.encode_stroke_style(stroke);
185+
encoding_ptr
186+
.encode_stroke_style_embolden(stroke, self.embolden_fill_style.embolden);
179187
false
180188
}
181189
};
182190
use skrifa::outline::DrawSettings;
183-
let mut path = encoding_ptr.encode_path(is_fill);
184-
let draw_settings = if key.hint {
185-
if let Some(hinter) = self.hinter {
186-
DrawSettings::hinted(hinter, false)
191+
let fill_style = self.embolden_fill_style;
192+
if fill_style.embolden != 0. {
193+
let mut path = encoding_ptr.encode_winding_path(is_fill);
194+
let draw_settings = if key.hint {
195+
if let Some(hinter) = self.hinter {
196+
DrawSettings::hinted(hinter, false)
197+
} else {
198+
DrawSettings::unhinted(self.size, self.coords)
199+
}
187200
} else {
188201
DrawSettings::unhinted(self.size, self.coords)
202+
};
203+
204+
outline.draw(draw_settings, &mut path).ok()?;
205+
if path.finish(false) == 0 {
206+
encoding_ptr.reset();
189207
}
190208
} else {
191-
DrawSettings::unhinted(self.size, self.coords)
192-
};
193-
outline.draw(draw_settings, &mut path).ok()?;
194-
if path.finish(false) == 0 {
195-
encoding_ptr.reset();
209+
let mut path = encoding_ptr.encode_path(is_fill);
210+
let draw_settings = if key.hint {
211+
if let Some(hinter) = self.hinter {
212+
DrawSettings::hinted(hinter, false)
213+
} else {
214+
DrawSettings::unhinted(self.size, self.coords)
215+
}
216+
} else {
217+
DrawSettings::unhinted(self.size, self.coords)
218+
};
219+
220+
outline.draw(draw_settings, &mut path).ok()?;
221+
if path.finish(false) == 0 {
222+
encoding_ptr.reset();
223+
}
196224
}
225+
197226
let stream_sizes = encoding_ptr.stream_offsets();
227+
198228
self.map.insert(
199229
key,
200230
GlyphEntry {
@@ -214,7 +244,7 @@ struct GlyphKey {
214244
font_index: u32,
215245
glyph_id: u32,
216246
font_size_bits: u32,
217-
style_bits: [u32; 2],
247+
style_bits: [u32; 3],
218248
hint: bool,
219249
}
220250

vello_encoding/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,8 @@ pub use mask::{make_mask_lut, make_mask_lut_16};
6363
pub use math::Transform;
6464
pub use monoid::Monoid;
6565
pub use path::{
66-
Cubic, LineSoup, Path, PathBbox, PathEncoder, PathMonoid, PathSegment, PathSegmentType,
67-
PathTag, SegmentCount, Style, Tile,
66+
Cubic, EmboldenStyle, LineSoup, Path, PathBbox, PathEncoder, PathMonoid, PathSegment,
67+
PathSegmentType, PathTag, SegmentCount, Style, Tile,
6868
};
6969
pub use ramp_cache::Ramps;
7070
pub use resolve::{Layout, Patch, Resolver, resolve_solid_paths_only};

0 commit comments

Comments
 (0)