Skip to content

Commit 81dedbd

Browse files
hecrjgrovesNL
authored andcommitted
Implement subpixel glyph positioning and scaling
The latest `cosmic-text` release performs bucketing calculations after layouting. This allows us to provide proper subpixel offsets, as well as scale any buffer thanks to its linear layout properties. See pop-os/cosmic-text#143.
1 parent 595e09a commit 81dedbd

4 files changed

Lines changed: 34 additions & 19 deletions

File tree

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ license = "MIT OR Apache-2.0 OR Zlib"
1010
[dependencies]
1111
wgpu = "0.16"
1212
etagere = "0.2.8"
13-
cosmic-text = "0.8"
13+
cosmic-text = "0.9"
1414
lru = "0.9"
1515

1616
[dev-dependencies]

examples/hello-world.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use glyphon::{
2-
Attrs, Buffer, Color, Family, FontSystem, Metrics, Resolution, SwashCache, TextArea, TextAtlas,
3-
TextBounds, TextRenderer,
2+
Attrs, Buffer, Color, Family, FontSystem, Metrics, Resolution, Shaping, SwashCache, TextArea,
3+
TextAtlas, TextBounds, TextRenderer,
44
};
55
use wgpu::{
66
CommandEncoderDescriptor, CompositeAlphaMode, DeviceDescriptor, Features, Instance,
@@ -73,7 +73,7 @@ async fn run() {
7373
let physical_height = (height as f64 * scale_factor) as f32;
7474

7575
buffer.set_size(&mut font_system, physical_width, physical_height);
76-
buffer.set_text(&mut font_system, "Hello world! 👋\nThis is rendered with 🦅 glyphon 🦁\nThe text below should be partially clipped.\na b c d e f g h i j k l m n o p q r s t u v w x y z", Attrs::new().family(Family::SansSerif));
76+
buffer.set_text(&mut font_system, "Hello world! 👋\nThis is rendered with 🦅 glyphon 🦁\nThe text below should be partially clipped.\na b c d e f g h i j k l m n o p q r s t u v w x y z", Attrs::new().family(Family::SansSerif), Shaping::Advanced);
7777
buffer.shape_until_scroll(&mut font_system);
7878

7979
event_loop.run(move |event, _, control_flow| {
@@ -103,8 +103,9 @@ async fn run() {
103103
},
104104
[TextArea {
105105
buffer: &buffer,
106-
left: 10,
107-
top: 10,
106+
left: 10.0,
107+
top: 10.0,
108+
scale: 1.0,
108109
bounds: TextBounds {
109110
left: 0,
110111
top: 0,

src/lib.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ pub use cosmic_text::{
1919
self, fontdb, Action, Affinity, Attrs, AttrsList, AttrsOwned, Buffer, BufferLine, CacheKey,
2020
Color, Command, Cursor, Edit, Editor, Family, FamilyOwned, Font, FontSystem, LayoutCursor,
2121
LayoutGlyph, LayoutLine, LayoutRun, LayoutRunIter, Metrics, ShapeGlyph, ShapeLine, ShapeSpan,
22-
ShapeWord, Stretch, Style, SubpixelBin, SwashCache, SwashContent, SwashImage, Weight, Wrap,
22+
ShapeWord, Shaping, Stretch, Style, SubpixelBin, SwashCache, SwashContent, SwashImage, Weight,
23+
Wrap,
2324
};
2425

2526
use etagere::AllocId;
@@ -101,9 +102,11 @@ pub struct TextArea<'a> {
101102
/// The buffer containing the text to be rendered.
102103
pub buffer: &'a Buffer,
103104
/// The left edge of the buffer.
104-
pub left: i32,
105+
pub left: f32,
105106
/// The top edge of the buffer.
106-
pub top: i32,
107+
pub top: f32,
108+
/// The scaling to apply to the buffer.
109+
pub scale: f32,
107110
/// The visible bounds of the text area. This is used to clip the text and doesn't have to
108111
/// match the `left` and `top` values.
109112
pub bounds: TextBounds,

src/text_render.rs

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -93,13 +93,24 @@ impl TextRenderer {
9393
for text_area in text_areas {
9494
for run in text_area.buffer.layout_runs() {
9595
for glyph in run.glyphs.iter() {
96-
if atlas.mask_atlas.glyph_cache.contains(&glyph.cache_key) {
97-
atlas.mask_atlas.promote(glyph.cache_key);
98-
} else if atlas.color_atlas.glyph_cache.contains(&glyph.cache_key) {
99-
atlas.color_atlas.promote(glyph.cache_key);
96+
let physical_glyph =
97+
glyph.physical((text_area.left, text_area.top), text_area.scale);
98+
99+
if atlas
100+
.mask_atlas
101+
.glyph_cache
102+
.contains(&physical_glyph.cache_key)
103+
{
104+
atlas.mask_atlas.promote(physical_glyph.cache_key);
105+
} else if atlas
106+
.color_atlas
107+
.glyph_cache
108+
.contains(&physical_glyph.cache_key)
109+
{
110+
atlas.color_atlas.promote(physical_glyph.cache_key);
100111
} else {
101112
let Some(image) = cache
102-
.get_image_uncached(font_system, glyph.cache_key) else { continue };
113+
.get_image_uncached(font_system, physical_glyph.cache_key) else { continue };
103114

104115
let content_type = match image.content {
105116
SwashContent::Color => ContentType::Color,
@@ -178,7 +189,7 @@ impl TextRenderer {
178189
};
179190

180191
inner.put(
181-
glyph.cache_key,
192+
physical_glyph.cache_key,
182193
GlyphDetails {
183194
width: width as u16,
184195
height: height as u16,
@@ -190,11 +201,11 @@ impl TextRenderer {
190201
);
191202
}
192203

193-
let details = atlas.glyph(&glyph.cache_key).unwrap();
204+
let details = atlas.glyph(&physical_glyph.cache_key).unwrap();
194205

195-
let mut x = glyph.x_int + details.left as i32 + text_area.left;
196-
let mut y =
197-
run.line_y as i32 + glyph.y_int - details.top as i32 + text_area.top;
206+
let mut x = physical_glyph.x + details.left as i32;
207+
let mut y = (run.line_y * text_area.scale).round() as i32 + physical_glyph.y
208+
- details.top as i32;
198209

199210
let (mut atlas_x, mut atlas_y, content_type) = match details.gpu_cache {
200211
GpuCacheStatus::InAtlas { x, y, content_type } => (x, y, content_type),

0 commit comments

Comments
 (0)