From bc977fc873d7ad853a84d76325365b2719dddbdd Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 24 Mar 2021 18:20:25 +0100 Subject: [PATCH] Render different variants according to subpixel positioning Co-Authored-By: Nathan Sobo --- gpui/Cargo.toml | 4 ++- gpui/src/fonts.rs | 26 --------------- gpui/src/platform/mac/fonts.rs | 48 +++++++++++++++++++++++---- gpui/src/platform/mac/renderer.rs | 4 +-- gpui/src/platform/mac/sprite_cache.rs | 26 ++++++++++++--- gpui/src/platform/mod.rs | 1 + 6 files changed, 69 insertions(+), 40 deletions(-) diff --git a/gpui/Cargo.toml b/gpui/Cargo.toml index e6843dce2efb2889bfde4c94ddec804990d8c38b..98b53af757a57825e4a872458b6227eaca712bef 100644 --- a/gpui/Cargo.toml +++ b/gpui/Cargo.toml @@ -14,7 +14,6 @@ parking_lot = "0.11.1" pathfinder_color = "0.5" pathfinder_geometry = "0.5" pin-project = "1.0.5" -png = "0.16" rand = "0.8.3" replace_with = "0.1.7" smallvec = "1.6.1" @@ -25,6 +24,9 @@ tree-sitter = "0.17" bindgen = "0.57" cc = "1.0.67" +[dev-dependencies] +png = "0.16" + [target.'cfg(target_os = "macos")'.dependencies] anyhow = "1" cocoa = "0.24" diff --git a/gpui/src/fonts.rs b/gpui/src/fonts.rs index fc86c741fc7d01746dddcbb425527ffd3be5e102..7924558d311c115e57a419df99be07cd137bb8d1 100644 --- a/gpui/src/fonts.rs +++ b/gpui/src/fonts.rs @@ -158,29 +158,3 @@ impl FontCache { metric * font_size / self.metric(font_id, |m| m.units_per_em as f32) } } - -// #[cfg(test)] -// mod tests { -// use std::{fs::File, io::BufWriter, path::Path}; - -// use super::*; - -// #[test] -// fn test_render_glyph() { -// let cache = FontCache::new(); -// let family_id = cache.load_family(&["Fira Code"]).unwrap(); -// let font_id = cache.select_font(family_id, &Default::default()).unwrap(); -// let glyph_id = cache.font(font_id).glyph_for_char('G').unwrap(); -// let (bounds, bytes) = cache.render_glyph(font_id, 16.0, glyph_id, 1.).unwrap(); - -// let path = Path::new(r"/Users/as-cii/Desktop/image.png"); -// let file = File::create(path).unwrap(); -// let ref mut w = BufWriter::new(file); - -// let mut encoder = png::Encoder::new(w, bounds.width() as u32, bounds.height() as u32); -// encoder.set_color(png::ColorType::Grayscale); -// encoder.set_depth(png::BitDepth::Eight); -// let mut writer = encoder.write_header().unwrap(); -// writer.write_image_data(&bytes).unwrap(); -// } -// } diff --git a/gpui/src/platform/mac/fonts.rs b/gpui/src/platform/mac/fonts.rs index 59afa002c3533009369c99bc568ca3543b2c08d8..67041e012af4cdb1415d4f21beb0be08a62e97b6 100644 --- a/gpui/src/platform/mac/fonts.rs +++ b/gpui/src/platform/mac/fonts.rs @@ -3,7 +3,7 @@ use crate::{ geometry::{ rect::{RectF, RectI}, transform2d::Transform2F, - vector::vec2f, + vector::{vec2f, vec2i}, }, platform, text_layout::{Glyph, Line, Run}, @@ -18,9 +18,7 @@ use core_foundation::{ use core_graphics::{ base::CGGlyph, color_space::CGColorSpace, context::CGContext, geometry::CGAffineTransform, }; -use core_text::{ - font_descriptor::kCTFontSizeAttribute, line::CTLine, string_attributes::kCTFontAttributeName, -}; +use core_text::{line::CTLine, string_attributes::kCTFontAttributeName}; use font_kit::{ canvas::RasterizationOptions, hinting::HintingOptions, metrics::Metrics, properties::Properties, source::SystemSource, @@ -73,11 +71,12 @@ impl platform::FontSystem for FontSystem { font_id: FontId, font_size: f32, glyph_id: GlyphId, + horizontal_shift: f32, scale_factor: f32, ) -> Option<(RectI, Vec)> { self.0 .read() - .rasterize_glyph(font_id, font_size, glyph_id, scale_factor) + .rasterize_glyph(font_id, font_size, glyph_id, horizontal_shift, scale_factor) } fn layout_str( @@ -127,6 +126,7 @@ impl FontSystemState { font_id: FontId, font_size: f32, glyph_id: GlyphId, + horizontal_shift: f32, scale_factor: f32, ) -> Option<(RectI, Vec)> { let font = &self.fonts[font_id.0]; @@ -144,6 +144,8 @@ impl FontSystemState { if bounds.width() == 0 || bounds.height() == 0 { None } else { + // Make room for subpixel variants. + let bounds = RectI::new(bounds.origin(), bounds.size() + vec2i(1, 0)); 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 _), @@ -170,7 +172,13 @@ impl FontSystemState { ctx.set_font(&font.native_font().copy_to_CGFont()); ctx.set_font_size(font_size as CGFloat); - ctx.show_glyphs_at_positions(&[glyph_id as CGGlyph], &[CGPoint::new(0.0, 0.0)]); + ctx.show_glyphs_at_positions( + &[glyph_id as CGGlyph], + &[CGPoint::new( + (horizontal_shift / scale_factor) as CGFloat, + 0.0, + )], + ); Some((bounds, pixels)) } @@ -335,4 +343,32 @@ mod tests { ); Ok(()) } + + // #[test] + // fn test_render_glyph() { + // use std::{fs::File, io::BufWriter, path::Path}; + + // let fonts = FontSystem::new(); + // let font_ids = fonts.load_family("Fira Code").unwrap(); + // let font_id = fonts.select_font(&font_ids, &Default::default()).unwrap(); + // let glyph_id = fonts.glyph_for_char(font_id, 'G').unwrap(); + + // for i in 0..2 { + // let variant = 0.5 * i as f32; + // let (bounds, bytes) = fonts + // .rasterize_glyph(font_id, 16.0, glyph_id, variant, 2.) + // .unwrap(); + + // let name = format!("/Users/as-cii/Desktop/twog-{}.png", i); + // let path = Path::new(&name); + // let file = File::create(path).unwrap(); + // let ref mut w = BufWriter::new(file); + + // let mut encoder = png::Encoder::new(w, bounds.width() as u32, bounds.height() as u32); + // encoder.set_color(png::ColorType::Grayscale); + // encoder.set_depth(png::BitDepth::Eight); + // let mut writer = encoder.write_header().unwrap(); + // writer.write_image_data(&bytes).unwrap(); + // } + // } } diff --git a/gpui/src/platform/mac/renderer.rs b/gpui/src/platform/mac/renderer.rs index 084171a1b42464b3b5032046e0838648725cb84c..2710389c1c855682568db810a993454ac3b9f47b 100644 --- a/gpui/src/platform/mac/renderer.rs +++ b/gpui/src/platform/mac/renderer.rs @@ -272,14 +272,14 @@ impl Renderer { glyph.font_id, glyph.font_size, glyph.id, + glyph.origin.x(), scene.scale_factor(), ) { sprites_by_atlas .entry(sprite.atlas_id) .or_insert_with(Vec::new) .push(shaders::GPUISprite { - origin: (glyph.origin * scene.scale_factor() + sprite.offset.to_f32()) - .to_float2(), + origin: (glyph.origin * scene.scale_factor() + sprite.offset).to_float2(), size: sprite.size.to_float2(), atlas_origin: sprite.atlas_origin.to_float2(), color: glyph.color.to_uchar4(), diff --git a/gpui/src/platform/mac/sprite_cache.rs b/gpui/src/platform/mac/sprite_cache.rs index 891ecb42c0f98c3bff6ae9e6e270cfbd1306947a..c48c4577de291a0e316c0b8da3eb4de3d842f107 100644 --- a/gpui/src/platform/mac/sprite_cache.rs +++ b/gpui/src/platform/mac/sprite_cache.rs @@ -2,7 +2,7 @@ use crate::{ fonts::{FontId, GlyphId}, geometry::{ rect::RectI, - vector::{vec2i, Vector2I}, + vector::{vec2i, Vector2F, Vector2I}, }, platform, }; @@ -16,13 +16,14 @@ struct GlyphDescriptor { font_id: FontId, font_size: OrderedFloat, glyph_id: GlyphId, + subpixel_variant: u8, } #[derive(Clone)] pub struct GlyphSprite { pub atlas_id: usize, pub atlas_origin: Vector2I, - pub offset: Vector2I, + pub offset: Vector2F, pub size: Vector2I, } @@ -59,21 +60,34 @@ impl SpriteCache { font_id: FontId, font_size: f32, glyph_id: GlyphId, + target_x: f32, scale_factor: f32, ) -> Option { + const SUBPIXEL_VARIANTS: u8 = 4; + + let target_x = target_x * 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; self.glyphs .entry(GlyphDescriptor { font_id, font_size: OrderedFloat(font_size), glyph_id, + subpixel_variant, }) .or_insert_with(|| { - let (glyph_bounds, mask) = - fonts.rasterize_glyph(font_id, font_size, glyph_id, scale_factor)?; + let horizontal_shift = subpixel_variant as f32 / SUBPIXEL_VARIANTS as f32; + let (glyph_bounds, mask) = fonts.rasterize_glyph( + font_id, + font_size, + glyph_id, + horizontal_shift, + scale_factor, + )?; assert!(glyph_bounds.width() < atlas_size.x()); assert!(glyph_bounds.height() < atlas_size.y()); @@ -88,10 +102,12 @@ impl SpriteCache { bounds }); + let mut offset = glyph_bounds.origin().to_f32(); + offset.set_x(offset.x() - target_x.fract()); Some(GlyphSprite { atlas_id: atlasses.len() - 1, atlas_origin: atlas_bounds.origin(), - offset: glyph_bounds.origin(), + offset, size: glyph_bounds.size(), }) }) diff --git a/gpui/src/platform/mod.rs b/gpui/src/platform/mod.rs index 666f654ac2cea174a9e65a51761a8abcff838683..c329bb46511601f306de72ba7671ac6e083d30ba 100644 --- a/gpui/src/platform/mod.rs +++ b/gpui/src/platform/mod.rs @@ -78,6 +78,7 @@ pub trait FontSystem: Send + Sync { font_id: FontId, font_size: f32, glyph_id: GlyphId, + horizontal_shift: f32, scale_factor: f32, ) -> Option<(RectI, Vec)>; fn layout_str(&self, text: &str, font_size: f32, runs: &[(Range, FontId)]) -> Line;