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 https://github.com/pop-os/cosmic-text/pull/143.
This commit is contained in:
		
					parent
					
						
							
								595e09a497
							
						
					
				
			
			
				commit
				
					
						81dedbd042
					
				
			
		
					 4 changed files with 34 additions and 19 deletions
				
			
		| 
						 | 
					@ -10,7 +10,7 @@ license = "MIT OR Apache-2.0 OR Zlib"
 | 
				
			||||||
[dependencies]
 | 
					[dependencies]
 | 
				
			||||||
wgpu = "0.16"
 | 
					wgpu = "0.16"
 | 
				
			||||||
etagere = "0.2.8"
 | 
					etagere = "0.2.8"
 | 
				
			||||||
cosmic-text = "0.8"
 | 
					cosmic-text = "0.9"
 | 
				
			||||||
lru = "0.9"
 | 
					lru = "0.9"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[dev-dependencies]
 | 
					[dev-dependencies]
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,6 @@
 | 
				
			||||||
use glyphon::{
 | 
					use glyphon::{
 | 
				
			||||||
    Attrs, Buffer, Color, Family, FontSystem, Metrics, Resolution, SwashCache, TextArea, TextAtlas,
 | 
					    Attrs, Buffer, Color, Family, FontSystem, Metrics, Resolution, Shaping, SwashCache, TextArea,
 | 
				
			||||||
    TextBounds, TextRenderer,
 | 
					    TextAtlas, TextBounds, TextRenderer,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
use wgpu::{
 | 
					use wgpu::{
 | 
				
			||||||
    CommandEncoderDescriptor, CompositeAlphaMode, DeviceDescriptor, Features, Instance,
 | 
					    CommandEncoderDescriptor, CompositeAlphaMode, DeviceDescriptor, Features, Instance,
 | 
				
			||||||
| 
						 | 
					@ -73,7 +73,7 @@ async fn run() {
 | 
				
			||||||
    let physical_height = (height as f64 * scale_factor) as f32;
 | 
					    let physical_height = (height as f64 * scale_factor) as f32;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    buffer.set_size(&mut font_system, physical_width, physical_height);
 | 
					    buffer.set_size(&mut font_system, physical_width, physical_height);
 | 
				
			||||||
    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));
 | 
					    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);
 | 
				
			||||||
    buffer.shape_until_scroll(&mut font_system);
 | 
					    buffer.shape_until_scroll(&mut font_system);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    event_loop.run(move |event, _, control_flow| {
 | 
					    event_loop.run(move |event, _, control_flow| {
 | 
				
			||||||
| 
						 | 
					@ -103,8 +103,9 @@ async fn run() {
 | 
				
			||||||
                        },
 | 
					                        },
 | 
				
			||||||
                        [TextArea {
 | 
					                        [TextArea {
 | 
				
			||||||
                            buffer: &buffer,
 | 
					                            buffer: &buffer,
 | 
				
			||||||
                            left: 10,
 | 
					                            left: 10.0,
 | 
				
			||||||
                            top: 10,
 | 
					                            top: 10.0,
 | 
				
			||||||
 | 
					                            scale: 1.0,
 | 
				
			||||||
                            bounds: TextBounds {
 | 
					                            bounds: TextBounds {
 | 
				
			||||||
                                left: 0,
 | 
					                                left: 0,
 | 
				
			||||||
                                top: 0,
 | 
					                                top: 0,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -19,7 +19,8 @@ pub use cosmic_text::{
 | 
				
			||||||
    self, fontdb, Action, Affinity, Attrs, AttrsList, AttrsOwned, Buffer, BufferLine, CacheKey,
 | 
					    self, fontdb, Action, Affinity, Attrs, AttrsList, AttrsOwned, Buffer, BufferLine, CacheKey,
 | 
				
			||||||
    Color, Command, Cursor, Edit, Editor, Family, FamilyOwned, Font, FontSystem, LayoutCursor,
 | 
					    Color, Command, Cursor, Edit, Editor, Family, FamilyOwned, Font, FontSystem, LayoutCursor,
 | 
				
			||||||
    LayoutGlyph, LayoutLine, LayoutRun, LayoutRunIter, Metrics, ShapeGlyph, ShapeLine, ShapeSpan,
 | 
					    LayoutGlyph, LayoutLine, LayoutRun, LayoutRunIter, Metrics, ShapeGlyph, ShapeLine, ShapeSpan,
 | 
				
			||||||
    ShapeWord, Stretch, Style, SubpixelBin, SwashCache, SwashContent, SwashImage, Weight, Wrap,
 | 
					    ShapeWord, Shaping, Stretch, Style, SubpixelBin, SwashCache, SwashContent, SwashImage, Weight,
 | 
				
			||||||
 | 
					    Wrap,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use etagere::AllocId;
 | 
					use etagere::AllocId;
 | 
				
			||||||
| 
						 | 
					@ -101,9 +102,11 @@ pub struct TextArea<'a> {
 | 
				
			||||||
    /// The buffer containing the text to be rendered.
 | 
					    /// The buffer containing the text to be rendered.
 | 
				
			||||||
    pub buffer: &'a Buffer,
 | 
					    pub buffer: &'a Buffer,
 | 
				
			||||||
    /// The left edge of the buffer.
 | 
					    /// The left edge of the buffer.
 | 
				
			||||||
    pub left: i32,
 | 
					    pub left: f32,
 | 
				
			||||||
    /// The top edge of the buffer.
 | 
					    /// The top edge of the buffer.
 | 
				
			||||||
    pub top: i32,
 | 
					    pub top: f32,
 | 
				
			||||||
 | 
					    /// The scaling to apply to the buffer.
 | 
				
			||||||
 | 
					    pub scale: f32,
 | 
				
			||||||
    /// The visible bounds of the text area. This is used to clip the text and doesn't have to
 | 
					    /// The visible bounds of the text area. This is used to clip the text and doesn't have to
 | 
				
			||||||
    /// match the `left` and `top` values.
 | 
					    /// match the `left` and `top` values.
 | 
				
			||||||
    pub bounds: TextBounds,
 | 
					    pub bounds: TextBounds,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -93,13 +93,24 @@ impl TextRenderer {
 | 
				
			||||||
        for text_area in text_areas {
 | 
					        for text_area in text_areas {
 | 
				
			||||||
            for run in text_area.buffer.layout_runs() {
 | 
					            for run in text_area.buffer.layout_runs() {
 | 
				
			||||||
                for glyph in run.glyphs.iter() {
 | 
					                for glyph in run.glyphs.iter() {
 | 
				
			||||||
                    if atlas.mask_atlas.glyph_cache.contains(&glyph.cache_key) {
 | 
					                    let physical_glyph =
 | 
				
			||||||
                        atlas.mask_atlas.promote(glyph.cache_key);
 | 
					                        glyph.physical((text_area.left, text_area.top), text_area.scale);
 | 
				
			||||||
                    } else if atlas.color_atlas.glyph_cache.contains(&glyph.cache_key) {
 | 
					
 | 
				
			||||||
                        atlas.color_atlas.promote(glyph.cache_key);
 | 
					                    if atlas
 | 
				
			||||||
 | 
					                        .mask_atlas
 | 
				
			||||||
 | 
					                        .glyph_cache
 | 
				
			||||||
 | 
					                        .contains(&physical_glyph.cache_key)
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        atlas.mask_atlas.promote(physical_glyph.cache_key);
 | 
				
			||||||
 | 
					                    } else if atlas
 | 
				
			||||||
 | 
					                        .color_atlas
 | 
				
			||||||
 | 
					                        .glyph_cache
 | 
				
			||||||
 | 
					                        .contains(&physical_glyph.cache_key)
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        atlas.color_atlas.promote(physical_glyph.cache_key);
 | 
				
			||||||
                    } else {
 | 
					                    } else {
 | 
				
			||||||
                        let Some(image) = cache
 | 
					                        let Some(image) = cache
 | 
				
			||||||
                            .get_image_uncached(font_system, glyph.cache_key) else { continue };
 | 
					                            .get_image_uncached(font_system, physical_glyph.cache_key) else { continue };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        let content_type = match image.content {
 | 
					                        let content_type = match image.content {
 | 
				
			||||||
                            SwashContent::Color => ContentType::Color,
 | 
					                            SwashContent::Color => ContentType::Color,
 | 
				
			||||||
| 
						 | 
					@ -178,7 +189,7 @@ impl TextRenderer {
 | 
				
			||||||
                        };
 | 
					                        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        inner.put(
 | 
					                        inner.put(
 | 
				
			||||||
                            glyph.cache_key,
 | 
					                            physical_glyph.cache_key,
 | 
				
			||||||
                            GlyphDetails {
 | 
					                            GlyphDetails {
 | 
				
			||||||
                                width: width as u16,
 | 
					                                width: width as u16,
 | 
				
			||||||
                                height: height as u16,
 | 
					                                height: height as u16,
 | 
				
			||||||
| 
						 | 
					@ -190,11 +201,11 @@ impl TextRenderer {
 | 
				
			||||||
                        );
 | 
					                        );
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    let details = atlas.glyph(&glyph.cache_key).unwrap();
 | 
					                    let details = atlas.glyph(&physical_glyph.cache_key).unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    let mut x = glyph.x_int + details.left as i32 + text_area.left;
 | 
					                    let mut x = physical_glyph.x + details.left as i32;
 | 
				
			||||||
                    let mut y =
 | 
					                    let mut y = (run.line_y * text_area.scale).round() as i32 + physical_glyph.y
 | 
				
			||||||
                        run.line_y as i32 + glyph.y_int - details.top as i32 + text_area.top;
 | 
					                        - details.top as i32;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    let (mut atlas_x, mut atlas_y, content_type) = match details.gpu_cache {
 | 
					                    let (mut atlas_x, mut atlas_y, content_type) = match details.gpu_cache {
 | 
				
			||||||
                        GpuCacheStatus::InAtlas { x, y, content_type } => (x, y, content_type),
 | 
					                        GpuCacheStatus::InAtlas { x, y, content_type } => (x, y, content_type),
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue