Honor vertical subpixel positions

Antonio Scandurra and Nathan Sobo created

Co-Authored-By: Nathan Sobo <nathan@zed.dev>

Change summary

gpui/src/platform/mac/fonts.rs        | 14 +++++++-------
gpui/src/platform/mac/renderer.rs     |  2 +-
gpui/src/platform/mac/sprite_cache.rs | 29 +++++++++++++++++++----------
gpui/src/platform/mod.rs              |  2 +-
4 files changed, 28 insertions(+), 19 deletions(-)

Detailed changes

gpui/src/platform/mac/fonts.rs 🔗

@@ -3,7 +3,7 @@ use crate::{
     geometry::{
         rect::{RectF, RectI},
         transform2d::Transform2F,
-        vector::{vec2f, vec2i},
+        vector::{vec2f, vec2i, Vector2F},
     },
     platform,
     text_layout::{Glyph, Line, Run},
@@ -71,12 +71,12 @@ impl platform::FontSystem for FontSystem {
         font_id: FontId,
         font_size: f32,
         glyph_id: GlyphId,
-        horizontal_shift: f32,
+        subpixel_shift: Vector2F,
         scale_factor: f32,
     ) -> Option<(RectI, Vec<u8>)> {
         self.0
             .read()
-            .rasterize_glyph(font_id, font_size, glyph_id, horizontal_shift, scale_factor)
+            .rasterize_glyph(font_id, font_size, glyph_id, subpixel_shift, scale_factor)
     }
 
     fn layout_str(
@@ -126,7 +126,7 @@ impl FontSystemState {
         font_id: FontId,
         font_size: f32,
         glyph_id: GlyphId,
-        horizontal_shift: f32,
+        subpixel_shift: Vector2F,
         scale_factor: f32,
     ) -> Option<(RectI, Vec<u8>)> {
         let font = &self.fonts[font_id.0];
@@ -145,7 +145,7 @@ impl FontSystemState {
             None
         } else {
             // Make room for subpixel variants.
-            let bounds = RectI::new(bounds.origin(), bounds.size() + vec2i(1, 0));
+            let bounds = RectI::new(bounds.origin(), bounds.size() + vec2i(1, 1));
             let mut pixels = vec![0; bounds.width() as usize * bounds.height() as usize];
             let ctx = CGContext::create_bitmap_context(
                 Some(pixels.as_mut_ptr() as *mut _),
@@ -175,8 +175,8 @@ impl FontSystemState {
             ctx.show_glyphs_at_positions(
                 &[glyph_id as CGGlyph],
                 &[CGPoint::new(
-                    (horizontal_shift / scale_factor) as CGFloat,
-                    0.0,
+                    (subpixel_shift.x() / scale_factor) as CGFloat,
+                    (subpixel_shift.y() / scale_factor) as CGFloat,
                 )],
             );
 

gpui/src/platform/mac/renderer.rs 🔗

@@ -272,7 +272,7 @@ impl Renderer {
                 glyph.font_id,
                 glyph.font_size,
                 glyph.id,
-                glyph.origin.x(),
+                glyph.origin,
                 scene.scale_factor(),
             ) {
                 sprites_by_atlas

gpui/src/platform/mac/sprite_cache.rs 🔗

@@ -2,7 +2,7 @@ use crate::{
     fonts::{FontId, GlyphId},
     geometry::{
         rect::RectI,
-        vector::{vec2i, Vector2F, Vector2I},
+        vector::{vec2f, vec2i, Vector2F, Vector2I},
     },
     platform,
 };
@@ -16,7 +16,7 @@ struct GlyphDescriptor {
     font_id: FontId,
     font_size: OrderedFloat<f32>,
     glyph_id: GlyphId,
-    subpixel_variant: u8,
+    subpixel_variant: (u8, u8),
 }
 
 #[derive(Clone)]
@@ -60,18 +60,22 @@ impl SpriteCache {
         font_id: FontId,
         font_size: f32,
         glyph_id: GlyphId,
-        target_x: f32,
+        target_position: Vector2F,
         scale_factor: f32,
     ) -> Option<GlyphSprite> {
         const SUBPIXEL_VARIANTS: u8 = 4;
 
-        let target_x = target_x * scale_factor;
+        let target_position = target_position * scale_factor;
         let fonts = &self.fonts;
         let atlasses = &mut self.atlasses;
         let atlas_size = self.atlas_size;
         let device = &self.device;
-        let subpixel_variant =
-            (target_x.fract() * SUBPIXEL_VARIANTS as f32).round() as u8 % SUBPIXEL_VARIANTS;
+        let subpixel_variant = (
+            (target_position.x().fract() * SUBPIXEL_VARIANTS as f32).round() as u8
+                % SUBPIXEL_VARIANTS,
+            (target_position.y().fract() * SUBPIXEL_VARIANTS as f32).round() as u8
+                % SUBPIXEL_VARIANTS,
+        );
         self.glyphs
             .entry(GlyphDescriptor {
                 font_id,
@@ -80,12 +84,15 @@ impl SpriteCache {
                 subpixel_variant,
             })
             .or_insert_with(|| {
-                let horizontal_shift = subpixel_variant as f32 / SUBPIXEL_VARIANTS as f32;
+                let subpixel_shift = vec2f(
+                    subpixel_variant.0 as f32 / SUBPIXEL_VARIANTS as f32,
+                    subpixel_variant.1 as f32 / SUBPIXEL_VARIANTS as f32,
+                );
                 let (glyph_bounds, mask) = fonts.rasterize_glyph(
                     font_id,
                     font_size,
                     glyph_id,
-                    horizontal_shift,
+                    subpixel_shift,
                     scale_factor,
                 )?;
                 assert!(glyph_bounds.width() < atlas_size.x());
@@ -102,8 +109,10 @@ impl SpriteCache {
                         bounds
                     });
 
-                let mut offset = glyph_bounds.origin().to_f32();
-                offset.set_x(offset.x() - target_x.fract());
+                // Snap sprite to pixel grid.
+                let offset = glyph_bounds.origin().to_f32()
+                    - vec2f(target_position.x().fract(), target_position.y().fract());
+
                 Some(GlyphSprite {
                     atlas_id: atlasses.len() - 1,
                     atlas_origin: atlas_bounds.origin(),

gpui/src/platform/mod.rs 🔗

@@ -78,7 +78,7 @@ pub trait FontSystem: Send + Sync {
         font_id: FontId,
         font_size: f32,
         glyph_id: GlyphId,
-        horizontal_shift: f32,
+        subpixel_shift: Vector2F,
         scale_factor: f32,
     ) -> Option<(RectI, Vec<u8>)>;
     fn layout_str(&self, text: &str, font_size: f32, runs: &[(Range<usize>, FontId)]) -> Line;