From 3b27d41c72eedce9e1e97fcff80a0b3a5d441d26 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 3 Oct 2023 13:52:10 -0600 Subject: [PATCH] Checkpoint --- crates/gpui3/src/geometry.rs | 79 ++++++++++++++++ crates/gpui3/src/platform.rs | 12 ++- .../gpui3/src/platform/mac/metal_renderer.rs | 8 +- crates/gpui3/src/platform/mac/text_system.rs | 91 ++++++++++++------- crates/gpui3/src/platform/mac/window.rs | 12 +-- crates/gpui3/src/scene.rs | 10 +- crates/gpui3/src/text_system.rs | 12 ++- crates/gpui3/src/text_system/line.rs | 43 ++------- crates/gpui3/src/window.rs | 62 ++++++++++++- 9 files changed, 235 insertions(+), 94 deletions(-) diff --git a/crates/gpui3/src/geometry.rs b/crates/gpui3/src/geometry.rs index e5dadbd58da43c3c60480b21bdd283f26d82dd35..5814730763214877c9adcc7f336a8425f2928adb 100644 --- a/crates/gpui3/src/geometry.rs +++ b/crates/gpui3/src/geometry.rs @@ -618,6 +618,12 @@ impl From for DevicePixels { } } +impl From for ScaledPixels { + fn from(device: DevicePixels) -> Self { + ScaledPixels(device.0 as f32) + } +} + #[derive(Clone, Copy, Default, Add, Sub, Mul, Div)] pub struct Rems(f32); @@ -802,3 +808,76 @@ impl From<()> for Length { Self::Definite(DefiniteLength::default()) } } + +pub trait IsZero { + fn is_zero(&self) -> bool; +} + +impl IsZero for DevicePixels { + fn is_zero(&self) -> bool { + self.0 == 0 + } +} + +impl IsZero for ScaledPixels { + fn is_zero(&self) -> bool { + self.0 == 0. + } +} + +impl IsZero for Pixels { + fn is_zero(&self) -> bool { + self.0 == 0. + } +} + +impl IsZero for Rems { + fn is_zero(&self) -> bool { + self.0 == 0. + } +} + +impl IsZero for AbsoluteLength { + fn is_zero(&self) -> bool { + match self { + AbsoluteLength::Pixels(pixels) => pixels.is_zero(), + AbsoluteLength::Rems(rems) => rems.is_zero(), + } + } +} + +impl IsZero for DefiniteLength { + fn is_zero(&self) -> bool { + match self { + DefiniteLength::Absolute(length) => length.is_zero(), + DefiniteLength::Fraction(fraction) => *fraction == 0., + } + } +} + +impl IsZero for Length { + fn is_zero(&self) -> bool { + match self { + Length::Definite(length) => length.is_zero(), + Length::Auto => false, + } + } +} + +impl IsZero for Point { + fn is_zero(&self) -> bool { + self.x.is_zero() && self.y.is_zero() + } +} + +impl IsZero for Size { + fn is_zero(&self) -> bool { + self.width.is_zero() && self.height.is_zero() + } +} + +impl IsZero for Bounds { + fn is_zero(&self) -> bool { + self.origin.is_zero() && self.size.is_zero() + } +} diff --git a/crates/gpui3/src/platform.rs b/crates/gpui3/src/platform.rs index 940633caaf0e2ce25a86310f2bcee66864b2ca90..9552bb227c4b03a4aeb2a86e9e57e48ba41502b9 100644 --- a/crates/gpui3/src/platform.rs +++ b/crates/gpui3/src/platform.rs @@ -6,8 +6,8 @@ mod mac; mod test; use crate::{ - AnyWindowHandle, Bounds, DevicePixels, Font, FontId, FontMetrics, GlyphId, Pixels, Point, - RasterizeGlyphParams, Result, Scene, ShapedLine, SharedString, Size, + AnyWindowHandle, Bounds, DevicePixels, Font, FontId, FontMetrics, GlyphId, + GlyphRasterizationParams, Pixels, Point, Result, Scene, ShapedLine, SharedString, Size, }; use anyhow::anyhow; use async_task::Runnable; @@ -147,7 +147,7 @@ pub trait PlatformWindow { fn is_topmost_for_position(&self, position: Point) -> bool; fn draw(&self, scene: Scene); - fn glyph_atlas(&self) -> Arc>; + fn glyph_atlas(&self) -> Arc>; } pub trait PlatformDispatcher: Send + Sync { @@ -163,9 +163,13 @@ pub trait PlatformTextSystem: Send + Sync { fn typographic_bounds(&self, font_id: FontId, glyph_id: GlyphId) -> Result>; fn advance(&self, font_id: FontId, glyph_id: GlyphId) -> Result>; fn glyph_for_char(&self, font_id: FontId, ch: char) -> Option; + fn glyph_raster_bounds( + &self, + params: &GlyphRasterizationParams, + ) -> Result>; fn rasterize_glyph( &self, - glyph_id: &RasterizeGlyphParams, + params: &GlyphRasterizationParams, ) -> Result<(Size, Vec)>; fn layout_line(&self, text: &str, font_size: Pixels, runs: &[(usize, FontId)]) -> ShapedLine; fn wrap_line( diff --git a/crates/gpui3/src/platform/mac/metal_renderer.rs b/crates/gpui3/src/platform/mac/metal_renderer.rs index dc93a8cfa0520d57616feb1b0b3ce483dea00f15..11866dae6889f649a94febf1473d47fc3fc36552 100644 --- a/crates/gpui3/src/platform/mac/metal_renderer.rs +++ b/crates/gpui3/src/platform/mac/metal_renderer.rs @@ -1,6 +1,6 @@ use crate::{ - point, size, AtlasTextureId, DevicePixels, MetalAtlas, MonochromeSprite, Quad, - RasterizeGlyphParams, Scene, Size, + point, size, AtlasTextureId, DevicePixels, GlyphRasterizationParams, MetalAtlas, + MonochromeSprite, Quad, Scene, Size, }; use cocoa::{ base::{NO, YES}, @@ -22,7 +22,7 @@ pub struct MetalRenderer { sprites_pipeline_state: metal::RenderPipelineState, unit_vertices: metal::Buffer, instances: metal::Buffer, - glyph_atlas: Arc>, + glyph_atlas: Arc>, } impl MetalRenderer { @@ -126,7 +126,7 @@ impl MetalRenderer { &*self.layer } - pub fn glyph_atlas(&self) -> &Arc> { + pub fn glyph_atlas(&self) -> &Arc> { &self.glyph_atlas } diff --git a/crates/gpui3/src/platform/mac/text_system.rs b/crates/gpui3/src/platform/mac/text_system.rs index ea7c6a03022d60915fb0f7a3d42722637240a0bc..9aae748079f8736995e3c920a2783969678dd300 100644 --- a/crates/gpui3/src/platform/mac/text_system.rs +++ b/crates/gpui3/src/platform/mac/text_system.rs @@ -1,6 +1,6 @@ use crate::{ point, px, size, Bounds, DevicePixels, Font, FontFeatures, FontId, FontMetrics, FontStyle, - FontWeight, GlyphId, Pixels, PlatformTextSystem, Point, RasterizeGlyphParams, Result, + FontWeight, GlyphId, GlyphRasterizationParams, Pixels, PlatformTextSystem, Point, Result, ShapedGlyph, ShapedLine, ShapedRun, SharedString, Size, SUBPIXEL_VARIANTS, }; use anyhow::anyhow; @@ -134,9 +134,16 @@ impl PlatformTextSystem for MacTextSystem { self.0.read().glyph_for_char(font_id, ch) } + fn glyph_raster_bounds( + &self, + params: &GlyphRasterizationParams, + ) -> Result> { + self.0.read().raster_bounds(params) + } + fn rasterize_glyph( &self, - glyph_id: &RasterizeGlyphParams, + glyph_id: &GlyphRasterizationParams, ) -> Result<(Size, Vec)> { self.0.read().rasterize_glyph(glyph_id) } @@ -230,37 +237,54 @@ impl MacTextSystemState { }) } + fn raster_bounds(&self, params: &GlyphRasterizationParams) -> Result> { + let font = &self.fonts[params.font_id.0]; + let scale = Transform2F::from_scale(params.scale_factor); + Ok(font + .raster_bounds( + params.glyph_id.into(), + params.font_size.into(), + scale, + HintingOptions::None, + font_kit::canvas::RasterizationOptions::GrayscaleAa, + )? + .into()) + } + fn rasterize_glyph( &self, - glyph_id: &RasterizeGlyphParams, + params: &GlyphRasterizationParams, ) -> Result<(Size, Vec)> { - let font = &self.fonts[glyph_id.font_id.0]; - let scale = Transform2F::from_scale(glyph_id.scale_factor); - let glyph_bounds = font.raster_bounds( - glyph_id.glyph_id.into(), - glyph_id.font_size.into(), - scale, - HintingOptions::None, - font_kit::canvas::RasterizationOptions::GrayscaleAa, - )?; - - if glyph_bounds.width() == 0 || glyph_bounds.height() == 0 { + let glyph_bounds = self.raster_bounds(params)?; + + // let scale = Transform2F::from_scale(params.scale_factor); + // let glyph_bounds = font.raster_bounds( + // params.glyph_id.into(), + // params.font_size.into(), + // scale, + // HintingOptions::None, + // font_kit::canvas::RasterizationOptions::GrayscaleAa, + // )?; + + if glyph_bounds.size.width.0 == 0 || glyph_bounds.size.height.0 == 0 { Err(anyhow!("glyph bounds are empty")) } else { - // Make room for subpixel variants. - let subpixel_padding = Vector2I::new( - glyph_id.subpixel_variant.x.min(1) as i32, - glyph_id.subpixel_variant.y.min(1) as i32, - ); - let bitmap_size = glyph_bounds.size() + subpixel_padding; + // Add an extra pixel when the subpixel variant isn't zero to make room for anti-aliasing. + let mut bitmap_size = glyph_bounds.size; + if params.subpixel_variant.x > 0 { + bitmap_size.width += DevicePixels(1); + } + if params.subpixel_variant.y > 0 { + bitmap_size.height += DevicePixels(1); + } - let mut bytes = vec![0; bitmap_size.x() as usize * bitmap_size.y() as usize]; + let mut bytes = vec![0; bitmap_size.width.0 as usize * bitmap_size.height.0 as usize]; let cx = CGContext::create_bitmap_context( Some(bytes.as_mut_ptr() as *mut _), - bitmap_size.x() as usize, - bitmap_size.y() as usize, + bitmap_size.width.0 as usize, + bitmap_size.height.0 as usize, 8, - bitmap_size.x() as usize, + bitmap_size.width.0 as usize, &CGColorSpace::create_device_gray(), kCGImageAlphaOnly, ); @@ -268,26 +292,27 @@ impl MacTextSystemState { // Move the origin to bottom left and account for scaling, this // makes drawing text consistent with the font-kit's raster_bounds. cx.translate( - -glyph_bounds.origin_x() as CGFloat, - (glyph_bounds.origin_y() + glyph_bounds.height()) as CGFloat, + -glyph_bounds.origin.x.0 as CGFloat, + (glyph_bounds.origin.y.0 + glyph_bounds.size.height.0) as CGFloat, ); cx.scale( - glyph_id.scale_factor as CGFloat, - glyph_id.scale_factor as CGFloat, + params.scale_factor as CGFloat, + params.scale_factor as CGFloat, ); - let subpixel_shift = glyph_id + let subpixel_shift = params .subpixel_variant - .map(|v| v as f32 / SUBPIXEL_VARIANTS as f32 / glyph_id.scale_factor); + .map(|v| v as f32 / SUBPIXEL_VARIANTS as f32 / params.scale_factor); cx.set_allows_font_subpixel_positioning(true); cx.set_should_subpixel_position_fonts(true); cx.set_allows_font_subpixel_quantization(false); cx.set_should_subpixel_quantize_fonts(false); - font.native_font() - .clone_with_font_size(f32::from(glyph_id.font_size) as CGFloat) + self.fonts[params.font_id.0] + .native_font() + .clone_with_font_size(f32::from(params.font_size) as CGFloat) .draw_glyphs( - &[u32::from(glyph_id.glyph_id) as CGGlyph], + &[u32::from(params.glyph_id) as CGGlyph], &[CGPoint::new( subpixel_shift.x as CGFloat, subpixel_shift.y as CGFloat, diff --git a/crates/gpui3/src/platform/mac/window.rs b/crates/gpui3/src/platform/mac/window.rs index 7d27976563a43ce7811e94276e50a510a8b80922..4c3b8e1a8c1b4595fb2234cdd724a482aeb76c42 100644 --- a/crates/gpui3/src/platform/mac/window.rs +++ b/crates/gpui3/src/platform/mac/window.rs @@ -1,10 +1,10 @@ use super::{ns_string, MetalRenderer, NSRange}; use crate::{ - point, px, size, AnyWindowHandle, Bounds, Event, KeyDownEvent, Keystroke, MacScreen, Modifiers, - ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMovedEvent, MouseUpEvent, NSRectExt, - Pixels, Platform, PlatformAtlas, PlatformDispatcher, PlatformInputHandler, PlatformScreen, - PlatformWindow, Point, RasterizeGlyphParams, Scene, Size, Timer, WindowAppearance, - WindowBounds, WindowKind, WindowOptions, WindowPromptLevel, + point, px, size, AnyWindowHandle, Bounds, Event, GlyphRasterizationParams, KeyDownEvent, + Keystroke, MacScreen, Modifiers, ModifiersChangedEvent, MouseButton, MouseDownEvent, + MouseMovedEvent, MouseUpEvent, NSRectExt, Pixels, Platform, PlatformAtlas, PlatformDispatcher, + PlatformInputHandler, PlatformScreen, PlatformWindow, Point, Scene, Size, Timer, + WindowAppearance, WindowBounds, WindowKind, WindowOptions, WindowPromptLevel, }; use block::ConcreteBlock; use cocoa::{ @@ -886,7 +886,7 @@ impl PlatformWindow for MacWindow { } } - fn glyph_atlas(&self) -> Arc> { + fn glyph_atlas(&self) -> Arc> { self.0.lock().renderer.glyph_atlas().clone() } } diff --git a/crates/gpui3/src/scene.rs b/crates/gpui3/src/scene.rs index b4752ab3e9b03f882d8d2184f5b999cf4f01cf02..5f55dd73fba8eadf81eedb63eb580f2b85289204 100644 --- a/crates/gpui3/src/scene.rs +++ b/crates/gpui3/src/scene.rs @@ -1,6 +1,6 @@ use std::{iter::Peekable, mem}; -use super::{Bounds, Hsla, Pixels, Point}; +use super::{Bounds, Hsla, Point}; use crate::{AtlasTextureId, AtlasTile, Corners, Edges, ScaledPixels}; use collections::BTreeMap; use smallvec::SmallVec; @@ -35,7 +35,7 @@ impl Scene { let primitive = primitive.into(); match primitive { - Primitive::Quad(mut quad) => { + Primitive::Quad(quad) => { layer.quads.push(quad); } Primitive::Sprite(sprite) => { @@ -233,9 +233,9 @@ impl From for Primitive { #[repr(C)] pub struct MonochromeSprite { pub order: u32, - pub bounds: Bounds, - pub clip_bounds: Bounds, - pub clip_corner_radii: Corners, + pub bounds: Bounds, + pub clip_bounds: Bounds, + pub clip_corner_radii: Corners, pub color: Hsla, pub tile: AtlasTile, } diff --git a/crates/gpui3/src/text_system.rs b/crates/gpui3/src/text_system.rs index ec0a85ca1656593b61009081c5d42a4b6b8643af..899554e4b02d360334ce756bb94e623ecf3ed4ed 100644 --- a/crates/gpui3/src/text_system.rs +++ b/crates/gpui3/src/text_system.rs @@ -215,9 +215,13 @@ impl TextSystem { }) } + pub fn raster_bounds(&self, params: &GlyphRasterizationParams) -> Result> { + self.platform_text_system.glyph_raster_bounds(params) + } + pub fn rasterize_glyph( &self, - glyph_id: &RasterizeGlyphParams, + glyph_id: &GlyphRasterizationParams, ) -> Result<(Size, Vec)> { self.platform_text_system.rasterize_glyph(glyph_id) } @@ -380,7 +384,7 @@ pub struct ShapedGlyph { } #[derive(Clone, Debug, PartialEq)] -pub struct RasterizeGlyphParams { +pub struct GlyphRasterizationParams { pub(crate) font_id: FontId, pub(crate) glyph_id: GlyphId, pub(crate) font_size: Pixels, @@ -388,9 +392,9 @@ pub struct RasterizeGlyphParams { pub(crate) scale_factor: f32, } -impl Eq for RasterizeGlyphParams {} +impl Eq for GlyphRasterizationParams {} -impl Hash for RasterizeGlyphParams { +impl Hash for GlyphRasterizationParams { fn hash(&self, state: &mut H) { self.font_id.0.hash(state); self.glyph_id.0.hash(state); diff --git a/crates/gpui3/src/text_system/line.rs b/crates/gpui3/src/text_system/line.rs index a3a578c7b1cedfdc5c69a9bfb58f016acf6852ba..3cccd51977df52f56a9bfe19db8a11006654e1b0 100644 --- a/crates/gpui3/src/text_system/line.rs +++ b/crates/gpui3/src/text_system/line.rs @@ -1,11 +1,10 @@ use crate::{ - black, point, px, Bounds, Corners, FontId, Hsla, Layout, MonochromeSprite, Pixels, Point, - RunStyle, ShapedBoundary, ShapedLine, ShapedRun, UnderlineStyle, WindowContext, + black, point, px, Bounds, FontId, Hsla, Layout, Pixels, Point, RunStyle, ShapedBoundary, + ShapedLine, ShapedRun, UnderlineStyle, WindowContext, }; use anyhow::Result; use smallvec::SmallVec; use std::sync::Arc; -use util::ResultExt; #[derive(Default, Debug, Clone)] pub struct Line { @@ -162,36 +161,14 @@ impl Line { if glyph.is_emoji { todo!() } else { - if let Some(tile) = cx - .rasterize_glyph( - run.font_id, - glyph.id, - self.layout.font_size, - glyph_origin, - cx.scale_factor(), - ) - .log_err() - { - let layer_id = cx.current_layer_id(); - - let bounds = Bounds { - origin: glyph_origin + todo!(), - size: todo!(), - }; - // cx.text_system().raster_bounds() - - cx.scene().insert( - layer_id, - MonochromeSprite { - order: layout.order, - bounds, - clip_bounds: bounds, - clip_corner_radii: Corners::default(), - color, - tile, - }, - ); - } + cx.paint_glyph( + glyph_origin, + layout.order, + run.font_id, + glyph.id, + self.layout.font_size, + color, + )?; } } diff --git a/crates/gpui3/src/window.rs b/crates/gpui3/src/window.rs index dc7dca03269bf90f48575a278962c26e83e67f20..58f06874823f48cd9a53a273e100db2dd68c044d 100644 --- a/crates/gpui3/src/window.rs +++ b/crates/gpui3/src/window.rs @@ -1,8 +1,9 @@ use crate::{ px, AnyView, AppContext, AtlasTile, AvailableSpace, Bounds, Context, Effect, Element, EntityId, - FontId, GlyphId, Handle, LayoutId, MainThread, MainThreadOnly, Pixels, PlatformAtlas, - PlatformWindow, Point, RasterizeGlyphParams, Reference, Scene, Size, StackContext, - StackingOrder, Style, TaffyLayoutEngine, WeakHandle, WindowOptions, SUBPIXEL_VARIANTS, + FontId, GlyphId, GlyphRasterizationParams, Handle, Hsla, IsZero, LayoutId, MainThread, + MainThreadOnly, MonochromeSprite, Pixels, PlatformAtlas, PlatformWindow, Point, Reference, + Scene, Size, StackContext, StackingOrder, Style, TaffyLayoutEngine, WeakHandle, WindowOptions, + SUBPIXEL_VARIANTS, }; use anyhow::Result; use futures::Future; @@ -15,7 +16,7 @@ pub struct AnyWindow {} pub struct Window { handle: AnyWindowHandle, platform_window: MainThreadOnly>, - glyph_atlas: Arc>, + glyph_atlas: Arc>, rem_size: Pixels, content_size: Size, layout_engine: TaffyLayoutEngine, @@ -165,6 +166,57 @@ impl<'a, 'w> WindowContext<'a, 'w> { }) } + pub fn paint_glyph( + &mut self, + origin: Point, + order: u32, + font_id: FontId, + glyph_id: GlyphId, + font_size: Pixels, + color: Hsla, + ) -> Result<()> { + let scale_factor = self.scale_factor(); + let origin = origin.scale(scale_factor); + let subpixel_variant = Point { + x: (origin.x.0.fract() * SUBPIXEL_VARIANTS as f32).floor() as u8, + y: (origin.y.0.fract() * SUBPIXEL_VARIANTS as f32).floor() as u8, + }; + let params = GlyphRasterizationParams { + font_id, + glyph_id, + font_size, + subpixel_variant, + scale_factor, + }; + + let raster_bounds = self.text_system().raster_bounds(¶ms)?; + + if !raster_bounds.is_zero() { + let layer_id = self.current_layer_id(); + let bounds = Bounds { + origin: origin + raster_bounds.origin.map(Into::into), + size: raster_bounds.size.map(Into::into), + }; + let tile = self + .window + .glyph_atlas + .get_or_insert_with(¶ms, &mut || self.text_system().rasterize_glyph(¶ms))?; + + self.window.scene.insert( + layer_id, + MonochromeSprite { + order, + bounds, + clip_bounds: bounds, + clip_corner_radii: Default::default(), + color, + tile, + }, + ); + } + Ok(()) + } + pub fn rasterize_glyph( &self, font_id: FontId, @@ -178,7 +230,7 @@ impl<'a, 'w> WindowContext<'a, 'w> { x: (target_position.x.0.fract() * SUBPIXEL_VARIANTS as f32).floor() as u8, y: (target_position.y.0.fract() * SUBPIXEL_VARIANTS as f32).floor() as u8, }; - let rasterized_glyph_id = RasterizeGlyphParams { + let rasterized_glyph_id = GlyphRasterizationParams { font_id, glyph_id, font_size,