Checkpoint: emojis rendering

Antonio Scandurra created

Change summary

crates/gpui3/build.rs                           |   5 
crates/gpui3/src/platform.rs                    |  18 
crates/gpui3/src/platform/mac/metal_atlas.rs    |  66 ++---
crates/gpui3/src/platform/mac/metal_renderer.rs | 197 ++++++++----------
crates/gpui3/src/platform/mac/shaders.metal     |  82 +++++--
crates/gpui3/src/platform/mac/text_system.rs    |  52 +++-
crates/gpui3/src/platform/mac/window.rs         |   8 
crates/gpui3/src/scene.rs                       | 106 +++++-----
crates/gpui3/src/text_system.rs                 |  63 ++---
crates/gpui3/src/text_system/line.rs            | 169 ++++++++--------
crates/gpui3/src/window.rs                      |  98 ++++----
crates/storybook2/src/collab_panel.rs           |   2 
12 files changed, 446 insertions(+), 420 deletions(-)

Detailed changes

crates/gpui3/build.rs 🔗

@@ -48,10 +48,11 @@ fn generate_shader_bindings() -> PathBuf {
         "ScaledContentMask".into(),
         "Uniforms".into(),
         "AtlasTile".into(),
-        "Quad".into(),
         "QuadInputIndex".into(),
+        "Quad".into(),
+        "SpriteInputIndex".into(),
         "MonochromeSprite".into(),
-        "MonochromeSpriteInputIndex".into(),
+        "PolychromeSprite".into(),
     ]);
     config.no_includes = true;
     config.enumeration.prefix_with_name = true;

crates/gpui3/src/platform.rs 🔗

@@ -147,8 +147,7 @@ pub trait PlatformWindow {
     fn is_topmost_for_position(&self, position: Point<Pixels>) -> bool;
     fn draw(&self, scene: Scene);
 
-    fn monochrome_sprite_atlas(&self) -> Arc<dyn PlatformAtlas>;
-    fn polychrome_sprite_atlas(&self) -> Arc<dyn PlatformAtlas>;
+    fn sprite_atlas(&self) -> Arc<dyn PlatformAtlas>;
 }
 
 pub trait PlatformDispatcher: Send + Sync {
@@ -182,6 +181,15 @@ pub enum AtlasKey {
     Svg(RenderSvgParams),
 }
 
+impl AtlasKey {
+    pub fn is_monochrome(&self) -> bool {
+        match self {
+            AtlasKey::Glyph(params) => !params.is_emoji,
+            AtlasKey::Svg(_) => true,
+        }
+    }
+}
+
 impl From<RenderGlyphParams> for AtlasKey {
     fn from(params: RenderGlyphParams) -> Self {
         Self::Glyph(params)
@@ -250,12 +258,6 @@ pub trait PlatformInputHandler {
 #[derive(Copy, Clone, Debug, PartialEq)]
 pub struct ScreenId(pub(crate) Uuid);
 
-#[derive(Copy, Clone, Debug)]
-pub enum RasterizationOptions {
-    Alpha,
-    Bgra,
-}
-
 #[derive(Debug)]
 pub struct WindowOptions {
     pub bounds: WindowBounds,

crates/gpui3/src/platform/mac/metal_atlas.rs 🔗

@@ -5,26 +5,15 @@ use anyhow::{anyhow, Result};
 use collections::HashMap;
 use derive_more::{Deref, DerefMut};
 use etagere::BucketedAtlasAllocator;
-use foreign_types::ForeignType;
-use metal::{Device, TextureDescriptor};
-use objc::{msg_send, sel, sel_impl};
+use metal::Device;
 use parking_lot::Mutex;
 
 pub struct MetalAtlas(Mutex<MetalAtlasState>);
 
 impl MetalAtlas {
-    pub fn new(
-        size: Size<DevicePixels>,
-        pixel_format: metal::MTLPixelFormat,
-        device: Device,
-    ) -> Self {
-        let texture_descriptor = metal::TextureDescriptor::new();
-        texture_descriptor.set_pixel_format(pixel_format);
-        texture_descriptor.set_width(size.width.into());
-        texture_descriptor.set_height(size.height.into());
+    pub fn new(device: Device) -> Self {
         MetalAtlas(Mutex::new(MetalAtlasState {
             device: AssertSend(device),
-            texture_descriptor: AssertSend(texture_descriptor),
             textures: Default::default(),
             tiles_by_key: Default::default(),
         }))
@@ -37,7 +26,6 @@ impl MetalAtlas {
 
 struct MetalAtlasState {
     device: AssertSend<Device>,
-    texture_descriptor: AssertSend<TextureDescriptor>,
     textures: Vec<MetalAtlasTexture>,
     tiles_by_key: HashMap<AtlasKey, AtlasTile>,
 }
@@ -57,9 +45,15 @@ impl PlatformAtlas for MetalAtlas {
                 .textures
                 .iter_mut()
                 .rev()
-                .find_map(|texture| texture.upload(size, &bytes))
+                .find_map(|texture| {
+                    if texture.monochrome == key.is_monochrome() {
+                        texture.upload(size, &bytes)
+                    } else {
+                        None
+                    }
+                })
                 .or_else(|| {
-                    let texture = lock.push_texture(size);
+                    let texture = lock.push_texture(size, key.is_monochrome());
                     texture.upload(size, &bytes)
                 })
                 .ok_or_else(|| anyhow!("could not allocate in new texture"))?;
@@ -74,35 +68,32 @@ impl PlatformAtlas for MetalAtlas {
 }
 
 impl MetalAtlasState {
-    fn push_texture(&mut self, min_size: Size<DevicePixels>) -> &mut MetalAtlasTexture {
-        let default_atlas_size = Size {
-            width: self.texture_descriptor.width().into(),
-            height: self.texture_descriptor.height().into(),
+    fn push_texture(
+        &mut self,
+        min_size: Size<DevicePixels>,
+        monochrome: bool,
+    ) -> &mut MetalAtlasTexture {
+        const DEFAULT_ATLAS_SIZE: Size<DevicePixels> = Size {
+            width: DevicePixels(1024),
+            height: DevicePixels(1024),
         };
-        let size;
-        let metal_texture;
-
-        if min_size.width > default_atlas_size.width || min_size.height > default_atlas_size.height
-        {
-            let descriptor = unsafe {
-                let descriptor_ptr: *mut metal::MTLTextureDescriptor =
-                    msg_send![*self.texture_descriptor, copy];
-                metal::TextureDescriptor::from_ptr(descriptor_ptr)
-            };
-            descriptor.set_width(min_size.width.into());
-            descriptor.set_height(min_size.height.into());
-            descriptor.set_pixel_format(metal::MTLPixelFormat::Depth32Float);
-            size = min_size;
-            metal_texture = self.device.new_texture(&descriptor);
+
+        let size = min_size.max(&DEFAULT_ATLAS_SIZE);
+        let texture_descriptor = metal::TextureDescriptor::new();
+        texture_descriptor.set_width(size.width.into());
+        texture_descriptor.set_height(size.height.into());
+        if monochrome {
+            texture_descriptor.set_pixel_format(metal::MTLPixelFormat::A8Unorm);
         } else {
-            size = default_atlas_size;
-            metal_texture = self.device.new_texture(&self.texture_descriptor);
+            texture_descriptor.set_pixel_format(metal::MTLPixelFormat::BGRA8Unorm);
         }
+        let metal_texture = self.device.new_texture(&texture_descriptor);
 
         let atlas_texture = MetalAtlasTexture {
             id: AtlasTextureId(self.textures.len() as u32),
             allocator: etagere::BucketedAtlasAllocator::new(size.into()),
             metal_texture: AssertSend(metal_texture),
+            monochrome,
         };
         self.textures.push(atlas_texture);
         self.textures.last_mut().unwrap()
@@ -113,6 +104,7 @@ struct MetalAtlasTexture {
     id: AtlasTextureId,
     allocator: BucketedAtlasAllocator,
     metal_texture: AssertSend<metal::Texture>,
+    monochrome: bool,
 }
 
 impl MetalAtlasTexture {

crates/gpui3/src/platform/mac/metal_renderer.rs 🔗

@@ -18,11 +18,11 @@ pub struct MetalRenderer {
     layer: metal::MetalLayer,
     command_queue: CommandQueue,
     quads_pipeline_state: metal::RenderPipelineState,
-    sprites_pipeline_state: metal::RenderPipelineState,
+    monochrome_sprites_pipeline_state: metal::RenderPipelineState,
+    polychrome_sprites_pipeline_state: metal::RenderPipelineState,
     unit_vertices: metal::Buffer,
     instances: metal::Buffer,
-    monochrome_sprite_atlas: Arc<MetalAtlas>,
-    polychrome_sprite_atlas: Arc<MetalAtlas>,
+    sprite_atlas: Arc<MetalAtlas>,
 }
 
 impl MetalRenderer {
@@ -90,43 +90,35 @@ impl MetalRenderer {
             "quad_fragment",
             PIXEL_FORMAT,
         );
-
-        let sprites_pipeline_state = build_pipeline_state(
+        let monochrome_sprites_pipeline_state = build_pipeline_state(
             &device,
             &library,
-            "sprites",
+            "monochrome_sprites",
             "monochrome_sprite_vertex",
             "monochrome_sprite_fragment",
             PIXEL_FORMAT,
         );
+        let polychrome_sprites_pipeline_state = build_pipeline_state(
+            &device,
+            &library,
+            "polychrome_sprites",
+            "polychrome_sprite_vertex",
+            "polychrome_sprite_fragment",
+            PIXEL_FORMAT,
+        );
 
         let command_queue = device.new_command_queue();
-        let monochrome_sprite_atlas = Arc::new(MetalAtlas::new(
-            Size {
-                width: DevicePixels(1024),
-                height: DevicePixels(768),
-            },
-            MTLPixelFormat::A8Unorm,
-            device.clone(),
-        ));
-        let polychrome_sprite_atlas = Arc::new(MetalAtlas::new(
-            Size {
-                width: DevicePixels(1024),
-                height: DevicePixels(768),
-            },
-            MTLPixelFormat::BGRA8Unorm,
-            device.clone(),
-        ));
+        let sprite_atlas = Arc::new(MetalAtlas::new(device.clone()));
 
         Self {
             layer,
             command_queue,
             quads_pipeline_state,
-            sprites_pipeline_state,
+            monochrome_sprites_pipeline_state,
+            polychrome_sprites_pipeline_state,
             unit_vertices,
             instances,
-            monochrome_sprite_atlas,
-            polychrome_sprite_atlas,
+            sprite_atlas,
         }
     }
 
@@ -134,12 +126,8 @@ impl MetalRenderer {
         &*self.layer
     }
 
-    pub fn monochrome_sprite_atlas(&self) -> &Arc<MetalAtlas> {
-        &self.monochrome_sprite_atlas
-    }
-
-    pub fn polychrome_sprite_atlas(&self) -> &Arc<MetalAtlas> {
-        &self.polychrome_sprite_atlas
+    pub fn sprite_atlas(&self) -> &Arc<MetalAtlas> {
+        &self.sprite_atlas
     }
 
     pub fn draw(&mut self, scene: &mut Scene) {
@@ -304,41 +292,38 @@ impl MetalRenderer {
         }
         align_offset(offset);
 
-        let texture = self.monochrome_sprite_atlas.texture(texture_id);
+        let texture = self.sprite_atlas.texture(texture_id);
         let texture_size = size(
             DevicePixels(texture.width() as i32),
             DevicePixels(texture.height() as i32),
         );
-        command_encoder.set_render_pipeline_state(&self.sprites_pipeline_state);
+        command_encoder.set_render_pipeline_state(&self.monochrome_sprites_pipeline_state);
         command_encoder.set_vertex_buffer(
-            MonochromeSpriteInputIndex::Vertices as u64,
+            SpriteInputIndex::Vertices as u64,
             Some(&self.unit_vertices),
             0,
         );
         command_encoder.set_vertex_buffer(
-            MonochromeSpriteInputIndex::Sprites as u64,
+            SpriteInputIndex::Sprites as u64,
             Some(&self.instances),
             *offset as u64,
         );
         command_encoder.set_vertex_bytes(
-            MonochromeSpriteInputIndex::ViewportSize as u64,
+            SpriteInputIndex::ViewportSize as u64,
             mem::size_of_val(&viewport_size) as u64,
             &viewport_size as *const Size<DevicePixels> as *const _,
         );
         command_encoder.set_vertex_bytes(
-            MonochromeSpriteInputIndex::AtlasTextureSize as u64,
+            SpriteInputIndex::AtlasTextureSize as u64,
             mem::size_of_val(&texture_size) as u64,
             &texture_size as *const Size<DevicePixels> as *const _,
         );
         command_encoder.set_fragment_buffer(
-            MonochromeSpriteInputIndex::Sprites as u64,
+            SpriteInputIndex::Sprites as u64,
             Some(&self.instances),
             *offset as u64,
         );
-        command_encoder.set_fragment_texture(
-            MonochromeSpriteInputIndex::AtlasTexture as u64,
-            Some(&texture),
-        );
+        command_encoder.set_fragment_texture(SpriteInputIndex::AtlasTexture as u64, Some(&texture));
 
         let sprite_bytes_len = mem::size_of::<MonochromeSprite>() * sprites.len();
         let buffer_contents = unsafe { (self.instances.contents() as *mut u8).add(*offset) };
@@ -373,71 +358,67 @@ impl MetalRenderer {
         viewport_size: Size<DevicePixels>,
         command_encoder: &metal::RenderCommandEncoderRef,
     ) {
-        todo!()
-        // if sprites.is_empty() {
-        //     return;
-        // }
-        // align_offset(offset);
-
-        // let texture = self.monochrome_sprite_atlas.texture(texture_id);
-        // let texture_size = size(
-        //     DevicePixels(texture.width() as i32),
-        //     DevicePixels(texture.height() as i32),
-        // );
-        // command_encoder.set_render_pipeline_state(&self.sprites_pipeline_state);
-        // command_encoder.set_vertex_buffer(
-        //     MonochromeSpriteInputIndex::Vertices as u64,
-        //     Some(&self.unit_vertices),
-        //     0,
-        // );
-        // command_encoder.set_vertex_buffer(
-        //     MonochromeSpriteInputIndex::Sprites as u64,
-        //     Some(&self.instances),
-        //     *offset as u64,
-        // );
-        // command_encoder.set_vertex_bytes(
-        //     MonochromeSpriteInputIndex::ViewportSize as u64,
-        //     mem::size_of_val(&viewport_size) as u64,
-        //     &viewport_size as *const Size<DevicePixels> as *const _,
-        // );
-        // command_encoder.set_vertex_bytes(
-        //     MonochromeSpriteInputIndex::AtlasTextureSize as u64,
-        //     mem::size_of_val(&texture_size) as u64,
-        //     &texture_size as *const Size<DevicePixels> as *const _,
-        // );
-        // command_encoder.set_fragment_buffer(
-        //     MonochromeSpriteInputIndex::Sprites as u64,
-        //     Some(&self.instances),
-        //     *offset as u64,
-        // );
-        // command_encoder.set_fragment_texture(
-        //     MonochromeSpriteInputIndex::AtlasTexture as u64,
-        //     Some(&texture),
-        // );
-
-        // let sprite_bytes_len = mem::size_of::<MonochromeSprite>() * sprites.len();
-        // let buffer_contents = unsafe { (self.instances.contents() as *mut u8).add(*offset) };
-        // unsafe {
-        //     ptr::copy_nonoverlapping(
-        //         sprites.as_ptr() as *const u8,
-        //         buffer_contents,
-        //         sprite_bytes_len,
-        //     );
-        // }
-
-        // let next_offset = *offset + sprite_bytes_len;
-        // assert!(
-        //     next_offset <= INSTANCE_BUFFER_SIZE,
-        //     "instance buffer exhausted"
-        // );
-
-        // command_encoder.draw_primitives_instanced(
-        //     metal::MTLPrimitiveType::Triangle,
-        //     0,
-        //     6,
-        //     sprites.len() as u64,
-        // );
-        // *offset = next_offset;
+        if sprites.is_empty() {
+            return;
+        }
+        align_offset(offset);
+
+        let texture = self.sprite_atlas.texture(texture_id);
+        let texture_size = size(
+            DevicePixels(texture.width() as i32),
+            DevicePixels(texture.height() as i32),
+        );
+        command_encoder.set_render_pipeline_state(&self.polychrome_sprites_pipeline_state);
+        command_encoder.set_vertex_buffer(
+            SpriteInputIndex::Vertices as u64,
+            Some(&self.unit_vertices),
+            0,
+        );
+        command_encoder.set_vertex_buffer(
+            SpriteInputIndex::Sprites as u64,
+            Some(&self.instances),
+            *offset as u64,
+        );
+        command_encoder.set_vertex_bytes(
+            SpriteInputIndex::ViewportSize as u64,
+            mem::size_of_val(&viewport_size) as u64,
+            &viewport_size as *const Size<DevicePixels> as *const _,
+        );
+        command_encoder.set_vertex_bytes(
+            SpriteInputIndex::AtlasTextureSize as u64,
+            mem::size_of_val(&texture_size) as u64,
+            &texture_size as *const Size<DevicePixels> as *const _,
+        );
+        command_encoder.set_fragment_buffer(
+            SpriteInputIndex::Sprites as u64,
+            Some(&self.instances),
+            *offset as u64,
+        );
+        command_encoder.set_fragment_texture(SpriteInputIndex::AtlasTexture as u64, Some(&texture));
+
+        let sprite_bytes_len = mem::size_of::<PolychromeSprite>() * sprites.len();
+        let buffer_contents = unsafe { (self.instances.contents() as *mut u8).add(*offset) };
+        unsafe {
+            ptr::copy_nonoverlapping(
+                sprites.as_ptr() as *const u8,
+                buffer_contents,
+                sprite_bytes_len,
+            );
+        }
+
+        let next_offset = *offset + sprite_bytes_len;
+        assert!(
+            next_offset <= INSTANCE_BUFFER_SIZE,
+            "instance buffer exhausted"
+        );
+
+        command_encoder.draw_primitives_instanced(
+            metal::MTLPrimitiveType::Triangle,
+            0,
+            6,
+            sprites.len() as u64,
+        );
+        *offset = next_offset;
     }
 }
 
@@ -489,7 +470,7 @@ enum QuadInputIndex {
 }
 
 #[repr(C)]
-enum MonochromeSpriteInputIndex {
+enum SpriteInputIndex {
     Vertices = 0,
     Sprites = 1,
     ViewportSize = 2,

crates/gpui3/src/platform/mac/shaders.metal 🔗

@@ -7,7 +7,10 @@ float4 hsla_to_rgba(Hsla hsla);
 float4 to_device_position(float2 unit_vertex, Bounds_ScaledPixels bounds,
                           Bounds_ScaledPixels clip_bounds,
                           constant Size_DevicePixels *viewport_size);
-float quad_sdf(float2 point, Bounds_ScaledPixels bounds, Corners_ScaledPixels corner_radii);
+float2 to_tile_position(float2 unit_vertex, AtlasTile tile,
+                        constant Size_DevicePixels *atlas_size);
+float quad_sdf(float2 point, Bounds_ScaledPixels bounds,
+               Corners_ScaledPixels corner_radii);
 
 struct QuadVertexOutput {
   float4 position [[position]];
@@ -119,27 +122,18 @@ struct MonochromeSpriteVertexOutput {
 
 vertex MonochromeSpriteVertexOutput monochrome_sprite_vertex(
     uint unit_vertex_id [[vertex_id]], uint sprite_id [[instance_id]],
-    constant float2 *unit_vertices
-    [[buffer(MonochromeSpriteInputIndex_Vertices)]],
-    constant MonochromeSprite *sprites
-    [[buffer(MonochromeSpriteInputIndex_Sprites)]],
+    constant float2 *unit_vertices [[buffer(SpriteInputIndex_Vertices)]],
+    constant MonochromeSprite *sprites [[buffer(SpriteInputIndex_Sprites)]],
     constant Size_DevicePixels *viewport_size
-    [[buffer(MonochromeSpriteInputIndex_ViewportSize)]],
+    [[buffer(SpriteInputIndex_ViewportSize)]],
     constant Size_DevicePixels *atlas_size
-    [[buffer(MonochromeSpriteInputIndex_AtlasTextureSize)]]) {
+    [[buffer(SpriteInputIndex_AtlasTextureSize)]]) {
 
   float2 unit_vertex = unit_vertices[unit_vertex_id];
   MonochromeSprite sprite = sprites[sprite_id];
   float4 device_position = to_device_position(
       unit_vertex, sprite.bounds, sprite.content_mask.bounds, viewport_size);
-
-  float2 tile_origin =
-      float2(sprite.tile.bounds.origin.x, sprite.tile.bounds.origin.y);
-  float2 tile_size =
-      float2(sprite.tile.bounds.size.width, sprite.tile.bounds.size.height);
-  float2 tile_position =
-      (tile_origin + unit_vertex * tile_size) /
-      float2((float)atlas_size->width, (float)atlas_size->height);
+  float2 tile_position = to_tile_position(unit_vertex, sprite.tile, atlas_size);
   float4 color = hsla_to_rgba(sprite.color);
   return MonochromeSpriteVertexOutput{device_position, tile_position, color,
                                       sprite_id};
@@ -147,22 +141,60 @@ vertex MonochromeSpriteVertexOutput monochrome_sprite_vertex(
 
 fragment float4 monochrome_sprite_fragment(
     MonochromeSpriteVertexOutput input [[stage_in]],
-    constant MonochromeSprite *sprites
-    [[buffer(MonochromeSpriteInputIndex_Sprites)]],
-    texture2d<float> atlas_texture
-    [[texture(MonochromeSpriteInputIndex_AtlasTexture)]]) {
+    constant MonochromeSprite *sprites [[buffer(SpriteInputIndex_Sprites)]],
+    texture2d<float> atlas_texture [[texture(SpriteInputIndex_AtlasTexture)]]) {
   MonochromeSprite sprite = sprites[input.sprite_id];
   constexpr sampler atlas_texture_sampler(mag_filter::linear,
                                           min_filter::linear);
   float4 sample =
       atlas_texture.sample(atlas_texture_sampler, input.tile_position);
-  float clip_distance =
-      quad_sdf(input.position.xy, sprite.content_mask.bounds, sprite.content_mask.corner_radii);
+  float clip_distance = quad_sdf(input.position.xy, sprite.content_mask.bounds,
+                                 sprite.content_mask.corner_radii);
   float4 color = input.color;
   color.a *= sample.a * saturate(0.5 - clip_distance);
   return color;
 }
 
+struct PolychromeSpriteVertexOutput {
+  float4 position [[position]];
+  float2 tile_position;
+  uint sprite_id [[flat]];
+};
+
+vertex PolychromeSpriteVertexOutput polychrome_sprite_vertex(
+    uint unit_vertex_id [[vertex_id]], uint sprite_id [[instance_id]],
+    constant float2 *unit_vertices [[buffer(SpriteInputIndex_Vertices)]],
+    constant PolychromeSprite *sprites [[buffer(SpriteInputIndex_Sprites)]],
+    constant Size_DevicePixels *viewport_size
+    [[buffer(SpriteInputIndex_ViewportSize)]],
+    constant Size_DevicePixels *atlas_size
+    [[buffer(SpriteInputIndex_AtlasTextureSize)]]) {
+
+  float2 unit_vertex = unit_vertices[unit_vertex_id];
+  PolychromeSprite sprite = sprites[sprite_id];
+  float4 device_position = to_device_position(
+      unit_vertex, sprite.bounds, sprite.content_mask.bounds, viewport_size);
+  float2 tile_position = to_tile_position(unit_vertex, sprite.tile, atlas_size);
+  return PolychromeSpriteVertexOutput{device_position, tile_position,
+                                      sprite_id};
+}
+
+fragment float4 polychrome_sprite_fragment(
+    PolychromeSpriteVertexOutput input [[stage_in]],
+    constant PolychromeSprite *sprites [[buffer(SpriteInputIndex_Sprites)]],
+    texture2d<float> atlas_texture [[texture(SpriteInputIndex_AtlasTexture)]]) {
+  PolychromeSprite sprite = sprites[input.sprite_id];
+  constexpr sampler atlas_texture_sampler(mag_filter::linear,
+                                          min_filter::linear);
+  float4 sample =
+      atlas_texture.sample(atlas_texture_sampler, input.tile_position);
+  float clip_distance = quad_sdf(input.position.xy, sprite.content_mask.bounds,
+                                 sprite.content_mask.corner_radii);
+  float4 color = sample;
+  color.a *= saturate(0.5 - clip_distance);
+  return color;
+}
+
 float4 hsla_to_rgba(Hsla hsla) {
   float h = hsla.h * 6.0; // Now, it's an angle but scaled in [0, 6) range
   float s = hsla.s;
@@ -229,6 +261,14 @@ float4 to_device_position(float2 unit_vertex, Bounds_ScaledPixels bounds,
   return float4(device_position, 0., 1.);
 }
 
+float2 to_tile_position(float2 unit_vertex, AtlasTile tile,
+                        constant Size_DevicePixels *atlas_size) {
+  float2 tile_origin = float2(tile.bounds.origin.x, tile.bounds.origin.y);
+  float2 tile_size = float2(tile.bounds.size.width, tile.bounds.size.height);
+  return (tile_origin + unit_vertex * tile_size) /
+         float2((float)atlas_size->width, (float)atlas_size->height);
+}
+
 float quad_sdf(float2 point, Bounds_ScaledPixels bounds,
                Corners_ScaledPixels corner_radii) {
   float2 half_size = float2(bounds.size.width, bounds.size.height) / 2.;

crates/gpui3/src/platform/mac/text_system.rs 🔗

@@ -12,7 +12,11 @@ use core_foundation::{
     base::{CFRange, TCFType},
     string::CFString,
 };
-use core_graphics::{base::CGGlyph, color_space::CGColorSpace, context::CGContext};
+use core_graphics::{
+    base::{kCGImageAlphaPremultipliedLast, CGGlyph},
+    color_space::CGColorSpace,
+    context::CGContext,
+};
 use core_text::{font::CTFont, line::CTLine, string_attributes::kCTFontAttributeName};
 use font_kit::{
     font::Font as FontKitFont,
@@ -262,16 +266,31 @@ impl MacTextSystemState {
                 bitmap_size.height += DevicePixels(1);
             }
 
-            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.width.0 as usize,
-                bitmap_size.height.0 as usize,
-                8,
-                bitmap_size.width.0 as usize,
-                &CGColorSpace::create_device_gray(),
-                kCGImageAlphaOnly,
-            );
+            let mut bytes;
+            let cx;
+            if params.is_emoji {
+                bytes = vec![0; bitmap_size.width.0 as usize * 4 * bitmap_size.height.0 as usize];
+                cx = CGContext::create_bitmap_context(
+                    Some(bytes.as_mut_ptr() as *mut _),
+                    bitmap_size.width.0 as usize,
+                    bitmap_size.height.0 as usize,
+                    8,
+                    bitmap_size.width.0 as usize * 4,
+                    &CGColorSpace::create_device_rgb(),
+                    kCGImageAlphaPremultipliedLast,
+                );
+            } else {
+                bytes = vec![0; bitmap_size.width.0 as usize * bitmap_size.height.0 as usize];
+                cx = CGContext::create_bitmap_context(
+                    Some(bytes.as_mut_ptr() as *mut _),
+                    bitmap_size.width.0 as usize,
+                    bitmap_size.height.0 as usize,
+                    8,
+                    bitmap_size.width.0 as usize,
+                    &CGColorSpace::create_device_gray(),
+                    kCGImageAlphaOnly,
+                );
+            }
 
             // Move the origin to bottom left and account for scaling, this
             // makes drawing text consistent with the font-kit's raster_bounds.
@@ -303,6 +322,17 @@ impl MacTextSystemState {
                     cx,
                 );
 
+            if params.is_emoji {
+                // Convert from RGBA with premultiplied alpha to BGRA with straight alpha.
+                for pixel in bytes.chunks_exact_mut(4) {
+                    pixel.swap(0, 2);
+                    let a = pixel[3] as f32 / 255.;
+                    pixel[0] = (pixel[0] as f32 / a) as u8;
+                    pixel[1] = (pixel[1] as f32 / a) as u8;
+                    pixel[2] = (pixel[2] as f32 / a) as u8;
+                }
+            }
+
             Ok((bitmap_size.into(), bytes))
         }
     }

crates/gpui3/src/platform/mac/window.rs 🔗

@@ -886,12 +886,8 @@ impl PlatformWindow for MacWindow {
         }
     }
 
-    fn monochrome_sprite_atlas(&self) -> Arc<dyn PlatformAtlas> {
-        self.0.lock().renderer.monochrome_sprite_atlas().clone()
-    }
-
-    fn polychrome_sprite_atlas(&self) -> Arc<dyn PlatformAtlas> {
-        self.0.lock().renderer.polychrome_sprite_atlas().clone()
+    fn sprite_atlas(&self) -> Arc<dyn PlatformAtlas> {
+        self.0.lock().renderer.sprite_atlas().clone()
     }
 }
 

crates/gpui3/src/scene.rs 🔗

@@ -1,4 +1,4 @@
-use std::{iter::Peekable, mem};
+use std::{iter::Peekable, mem, slice};
 
 use super::{Bounds, Hsla, Point};
 use crate::{AtlasTextureId, AtlasTile, Corners, Edges, ScaledContentMask, ScaledPixels};
@@ -63,42 +63,46 @@ impl SceneLayer {
     pub fn batches(&mut self) -> impl Iterator<Item = PrimitiveBatch> {
         self.quads.sort_unstable();
         self.monochrome_sprites.sort_unstable();
-
-        BatchIterator::new(
-            &self.quads,
-            self.quads.iter().peekable(),
-            &self.monochrome_sprites,
-            self.monochrome_sprites.iter().peekable(),
-        )
+        self.polychrome_sprites.sort_unstable();
+        BatchIterator {
+            quads: &self.quads,
+            quads_start: 0,
+            quads_iter: self.quads.iter().peekable(),
+            monochrome_sprites: &self.monochrome_sprites,
+            monochrome_sprites_start: 0,
+            monochrome_sprites_iter: self.monochrome_sprites.iter().peekable(),
+            polychrome_sprites: &self.polychrome_sprites,
+            polychrome_sprites_start: 0,
+            polychrome_sprites_iter: self.polychrome_sprites.iter().peekable(),
+        }
     }
 }
 
-struct BatchIterator<'a, Q, S>
-where
-    Q: Iterator<Item = &'a Quad>,
-    S: Iterator<Item = &'a MonochromeSprite>,
-{
+struct BatchIterator<'a> {
     quads: &'a [Quad],
-    sprites: &'a [MonochromeSprite],
     quads_start: usize,
-    sprites_start: usize,
-    quads_iter: Peekable<Q>,
-    sprites_iter: Peekable<S>,
+    quads_iter: Peekable<slice::Iter<'a, Quad>>,
+    monochrome_sprites: &'a [MonochromeSprite],
+    monochrome_sprites_start: usize,
+    monochrome_sprites_iter: Peekable<slice::Iter<'a, MonochromeSprite>>,
+    polychrome_sprites: &'a [PolychromeSprite],
+    polychrome_sprites_start: usize,
+    polychrome_sprites_iter: Peekable<slice::Iter<'a, PolychromeSprite>>,
 }
 
-impl<'a, Q: 'a, S: 'a> Iterator for BatchIterator<'a, Q, S>
-where
-    Q: Iterator<Item = &'a Quad>,
-    S: Iterator<Item = &'a MonochromeSprite>,
-{
+impl<'a> Iterator for BatchIterator<'a> {
     type Item = PrimitiveBatch<'a>;
 
     fn next(&mut self) -> Option<Self::Item> {
         let mut kinds_and_orders = [
             (PrimitiveKind::Quad, self.quads_iter.peek().map(|q| q.order)),
             (
-                PrimitiveKind::Sprite,
-                self.sprites_iter.peek().map(|s| s.order),
+                PrimitiveKind::MonochromeSprite,
+                self.monochrome_sprites_iter.peek().map(|s| s.order),
+            ),
+            (
+                PrimitiveKind::PolychromeSprite,
+                self.polychrome_sprites_iter.peek().map(|s| s.order),
             ),
         ];
         kinds_and_orders.sort_by_key(|(_, order)| order.unwrap_or(u32::MAX));
@@ -123,45 +127,40 @@ where
                 self.quads_start = quads_end;
                 Some(PrimitiveBatch::Quads(&self.quads[quads_start..quads_end]))
             }
-            PrimitiveKind::Sprite => {
-                let texture_id = self.sprites_iter.peek().unwrap().tile.texture_id;
-                let sprites_start = self.sprites_start;
+            PrimitiveKind::MonochromeSprite => {
+                let texture_id = self.monochrome_sprites_iter.peek().unwrap().tile.texture_id;
+                let sprites_start = self.monochrome_sprites_start;
                 let sprites_end = sprites_start
                     + self
-                        .sprites_iter
+                        .monochrome_sprites_iter
                         .by_ref()
                         .take_while(|sprite| {
                             sprite.order <= max_order && sprite.tile.texture_id == texture_id
                         })
                         .count();
-                self.sprites_start = sprites_end;
+                self.monochrome_sprites_start = sprites_end;
                 Some(PrimitiveBatch::MonochromeSprites {
                     texture_id,
-                    sprites: &self.sprites[sprites_start..sprites_end],
+                    sprites: &self.monochrome_sprites[sprites_start..sprites_end],
+                })
+            }
+            PrimitiveKind::PolychromeSprite => {
+                let texture_id = self.polychrome_sprites_iter.peek().unwrap().tile.texture_id;
+                let sprites_start = self.polychrome_sprites_start;
+                let sprites_end = sprites_start
+                    + self
+                        .polychrome_sprites_iter
+                        .by_ref()
+                        .take_while(|sprite| {
+                            sprite.order <= max_order && sprite.tile.texture_id == texture_id
+                        })
+                        .count();
+                self.polychrome_sprites_start = sprites_end;
+                Some(PrimitiveBatch::PolychromeSprites {
+                    texture_id,
+                    sprites: &self.polychrome_sprites[sprites_start..sprites_end],
                 })
             }
-        }
-    }
-}
-
-impl<'a, Q: 'a, S: 'a> BatchIterator<'a, Q, S>
-where
-    Q: Iterator<Item = &'a Quad>,
-    S: Iterator<Item = &'a MonochromeSprite>,
-{
-    fn new(
-        quads: &'a [Quad],
-        quads_iter: Peekable<Q>,
-        sprites: &'a [MonochromeSprite],
-        sprites_iter: Peekable<S>,
-    ) -> Self {
-        Self {
-            quads,
-            quads_start: 0,
-            quads_iter,
-            sprites,
-            sprites_start: 0,
-            sprites_iter,
         }
     }
 }
@@ -169,7 +168,8 @@ where
 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
 pub enum PrimitiveKind {
     Quad,
-    Sprite,
+    MonochromeSprite,
+    PolychromeSprite,
 }
 
 #[derive(Clone, Debug)]

crates/gpui3/src/text_system.rs 🔗

@@ -36,8 +36,7 @@ pub struct TextSystem {
     text_layout_cache: Arc<TextLayoutCache>,
     platform_text_system: Arc<dyn PlatformTextSystem>,
     font_ids_by_font: RwLock<HashMap<Font, FontId>>,
-    fonts_by_font_id: RwLock<HashMap<FontId, Font>>,
-    font_metrics: RwLock<HashMap<Font, FontMetrics>>,
+    font_metrics: RwLock<HashMap<FontId, FontMetrics>>,
     wrapper_pool: Mutex<HashMap<FontIdWithSize, Vec<LineWrapper>>>,
     font_runs_pool: Mutex<Vec<Vec<(usize, FontId)>>>,
 }
@@ -49,7 +48,6 @@ impl TextSystem {
             platform_text_system,
             font_metrics: RwLock::new(HashMap::default()),
             font_ids_by_font: RwLock::new(HashMap::default()),
-            fonts_by_font_id: RwLock::new(HashMap::default()),
             wrapper_pool: Mutex::new(HashMap::default()),
             font_runs_pool: Default::default(),
         }
@@ -62,30 +60,20 @@ impl TextSystem {
         } else {
             let font_id = self.platform_text_system.font_id(font)?;
             self.font_ids_by_font.write().insert(font.clone(), font_id);
-            self.fonts_by_font_id.write().insert(font_id, font.clone());
             Ok(font_id)
         }
     }
 
-    pub fn with_font<T>(&self, font_id: FontId, f: impl FnOnce(&Self, &Font) -> T) -> Result<T> {
-        self.fonts_by_font_id
-            .read()
-            .get(&font_id)
-            .ok_or_else(|| anyhow!("font not found"))
-            .map(|font| f(self, font))
-    }
-
-    pub fn bounding_box(&self, font: &Font, font_size: Pixels) -> Result<Bounds<Pixels>> {
-        self.read_metrics(&font, |metrics| metrics.bounding_box(font_size))
+    pub fn bounding_box(&self, font_id: FontId, font_size: Pixels) -> Result<Bounds<Pixels>> {
+        self.read_metrics(font_id, |metrics| metrics.bounding_box(font_size))
     }
 
     pub fn typographic_bounds(
         &self,
-        font: &Font,
+        font_id: FontId,
         font_size: Pixels,
         character: char,
     ) -> Result<Bounds<Pixels>> {
-        let font_id = self.font_id(font)?;
         let glyph_id = self
             .platform_text_system
             .glyph_for_char(font_id, character)
@@ -93,65 +81,63 @@ impl TextSystem {
         let bounds = self
             .platform_text_system
             .typographic_bounds(font_id, glyph_id)?;
-        self.read_metrics(font, |metrics| {
+        self.read_metrics(font_id, |metrics| {
             (bounds / metrics.units_per_em as f32 * font_size.0).map(px)
         })
     }
 
-    pub fn advance(&self, font: &Font, font_size: Pixels, ch: char) -> Result<Size<Pixels>> {
-        let font_id = self.font_id(font)?;
+    pub fn advance(&self, font_id: FontId, font_size: Pixels, ch: char) -> Result<Size<Pixels>> {
         let glyph_id = self
             .platform_text_system
             .glyph_for_char(font_id, ch)
             .ok_or_else(|| anyhow!("glyph not found for character '{}'", ch))?;
-        let result =
-            self.platform_text_system.advance(font_id, glyph_id)? / self.units_per_em(font)? as f32;
+        let result = self.platform_text_system.advance(font_id, glyph_id)?
+            / self.units_per_em(font_id)? as f32;
 
         Ok(result * font_size)
     }
 
-    pub fn units_per_em(&self, font: &Font) -> Result<u32> {
-        self.read_metrics(font, |metrics| metrics.units_per_em as u32)
+    pub fn units_per_em(&self, font_id: FontId) -> Result<u32> {
+        self.read_metrics(font_id, |metrics| metrics.units_per_em as u32)
     }
 
-    pub fn cap_height(&self, font: &Font, font_size: Pixels) -> Result<Pixels> {
-        self.read_metrics(font, |metrics| metrics.cap_height(font_size))
+    pub fn cap_height(&self, font_id: FontId, font_size: Pixels) -> Result<Pixels> {
+        self.read_metrics(font_id, |metrics| metrics.cap_height(font_size))
     }
 
-    pub fn x_height(&self, font: &Font, font_size: Pixels) -> Result<Pixels> {
-        self.read_metrics(font, |metrics| metrics.x_height(font_size))
+    pub fn x_height(&self, font_id: FontId, font_size: Pixels) -> Result<Pixels> {
+        self.read_metrics(font_id, |metrics| metrics.x_height(font_size))
     }
 
-    pub fn ascent(&self, font: &Font, font_size: Pixels) -> Result<Pixels> {
-        self.read_metrics(font, |metrics| metrics.ascent(font_size))
+    pub fn ascent(&self, font_id: FontId, font_size: Pixels) -> Result<Pixels> {
+        self.read_metrics(font_id, |metrics| metrics.ascent(font_size))
     }
 
-    pub fn descent(&self, font: &Font, font_size: Pixels) -> Result<Pixels> {
-        self.read_metrics(font, |metrics| metrics.descent(font_size))
+    pub fn descent(&self, font_id: FontId, font_size: Pixels) -> Result<Pixels> {
+        self.read_metrics(font_id, |metrics| metrics.descent(font_size))
     }
 
     pub fn baseline_offset(
         &self,
-        font: &Font,
+        font_id: FontId,
         font_size: Pixels,
         line_height: Pixels,
     ) -> Result<Pixels> {
-        let ascent = self.ascent(font, font_size)?;
-        let descent = self.descent(font, font_size)?;
+        let ascent = self.ascent(font_id, font_size)?;
+        let descent = self.descent(font_id, font_size)?;
         let padding_top = (line_height - ascent - descent) / 2.;
         Ok(padding_top + ascent)
     }
 
-    fn read_metrics<T>(&self, font: &Font, read: impl FnOnce(&FontMetrics) -> T) -> Result<T> {
+    fn read_metrics<T>(&self, font_id: FontId, read: impl FnOnce(&FontMetrics) -> T) -> Result<T> {
         let lock = self.font_metrics.upgradable_read();
 
-        if let Some(metrics) = lock.get(font) {
+        if let Some(metrics) = lock.get(&font_id) {
             Ok(read(metrics))
         } else {
-            let font_id = self.platform_text_system.font_id(&font)?;
             let mut lock = RwLockUpgradableReadGuard::upgrade(lock);
             let metrics = lock
-                .entry(font.clone())
+                .entry(font_id)
                 .or_insert_with(|| self.platform_text_system.font_metrics(font_id));
             Ok(read(metrics))
         }
@@ -390,6 +376,7 @@ pub struct RenderGlyphParams {
     pub(crate) font_size: Pixels,
     pub(crate) subpixel_variant: Point<u8>,
     pub(crate) scale_factor: f32,
+    pub(crate) is_emoji: bool,
 }
 
 impl Eq for RenderGlyphParams {}

crates/gpui3/src/text_system/line.rs 🔗

@@ -109,77 +109,74 @@ impl Line {
         let text_system = cx.text_system().clone();
 
         for run in &self.layout.runs {
-            text_system.with_font(run.font_id, |system, font| {
-                let max_glyph_width = system.bounding_box(font, self.layout.font_size)?.size.width;
+            let max_glyph_width = text_system
+                .bounding_box(run.font_id, self.layout.font_size)?
+                .size
+                .width;
 
-                for glyph in &run.glyphs {
-                    let glyph_origin = origin + baseline_offset + glyph.position;
-                    if glyph_origin.x > visible_bounds.upper_right().x {
-                        break;
-                    }
+            for glyph in &run.glyphs {
+                let glyph_origin = origin + baseline_offset + glyph.position;
+                if glyph_origin.x > visible_bounds.upper_right().x {
+                    break;
+                }
 
-                    let mut finished_underline: Option<(Point<Pixels>, UnderlineStyle)> = None;
-                    if glyph.index >= run_end {
-                        if let Some(style_run) = style_runs.next() {
-                            if let Some((_, underline_style)) = &mut underline {
-                                if style_run.underline != *underline_style {
-                                    finished_underline = underline.take();
-                                }
-                            }
-                            if style_run.underline.thickness > px(0.) {
-                                underline.get_or_insert((
-                                    point(
-                                        glyph_origin.x,
-                                        origin.y
-                                            + baseline_offset.y
-                                            + (self.layout.descent * 0.618),
-                                    ),
-                                    UnderlineStyle {
-                                        color: style_run.underline.color,
-                                        thickness: style_run.underline.thickness,
-                                        squiggly: style_run.underline.squiggly,
-                                    },
-                                ));
+                let mut finished_underline: Option<(Point<Pixels>, UnderlineStyle)> = None;
+                if glyph.index >= run_end {
+                    if let Some(style_run) = style_runs.next() {
+                        if let Some((_, underline_style)) = &mut underline {
+                            if style_run.underline != *underline_style {
+                                finished_underline = underline.take();
                             }
-
-                            run_end += style_run.len as usize;
-                            color = style_run.color;
-                        } else {
-                            run_end = self.layout.len;
-                            finished_underline = underline.take();
                         }
-                    }
+                        if style_run.underline.thickness > px(0.) {
+                            underline.get_or_insert((
+                                point(
+                                    glyph_origin.x,
+                                    origin.y + baseline_offset.y + (self.layout.descent * 0.618),
+                                ),
+                                UnderlineStyle {
+                                    color: style_run.underline.color,
+                                    thickness: style_run.underline.thickness,
+                                    squiggly: style_run.underline.squiggly,
+                                },
+                            ));
+                        }
 
-                    if glyph_origin.x + max_glyph_width < visible_bounds.origin.x {
-                        continue;
+                        run_end += style_run.len as usize;
+                        color = style_run.color;
+                    } else {
+                        run_end = self.layout.len;
+                        finished_underline = underline.take();
                     }
+                }
 
-                    if let Some((_underline_origin, _underline_style)) = finished_underline {
-                        todo!()
-                    }
+                if glyph_origin.x + max_glyph_width < visible_bounds.origin.x {
+                    continue;
+                }
 
-                    if glyph.is_emoji {
-                        cx.paint_emoji(
-                            glyph_origin,
-                            layout.order,
-                            run.font_id,
-                            glyph.id,
-                            self.layout.font_size,
-                        )?;
-                    } else {
-                        cx.paint_glyph(
-                            glyph_origin,
-                            layout.order,
-                            run.font_id,
-                            glyph.id,
-                            self.layout.font_size,
-                            color,
-                        )?;
-                    }
+                if let Some((_underline_origin, _underline_style)) = finished_underline {
+                    todo!()
                 }
 
-                anyhow::Ok(())
-            })??;
+                if glyph.is_emoji {
+                    cx.paint_emoji(
+                        glyph_origin,
+                        layout.order,
+                        run.font_id,
+                        glyph.id,
+                        self.layout.font_size,
+                    )?;
+                } else {
+                    cx.paint_glyph(
+                        glyph_origin,
+                        layout.order,
+                        run.font_id,
+                        glyph.id,
+                        self.layout.font_size,
+                        color,
+                    )?;
+                }
+            }
         }
 
         if let Some((_underline_start, _underline_style)) = underline.take() {
@@ -281,31 +278,31 @@ impl Line {
                     // });
                 }
 
-                cx.text_system().with_font(run.font_id, |system, font| {
-                    let _glyph_bounds = Bounds {
-                        origin: glyph_origin,
-                        size: system.bounding_box(font, self.layout.font_size)?.size,
-                    };
-                    // if glyph_bounds.intersects(visible_bounds) {
-                    //     if glyph.is_emoji {
-                    //         cx.scene().push_image_glyph(scene::ImageGlyph {
-                    //             font_id: run.font_id,
-                    //             font_size: self.layout.font_size,
-                    //             id: glyph.id,
-                    //             origin: glyph_bounds.origin() + baseline_offset,
-                    //         });
-                    //     } else {
-                    //         cx.scene().push_glyph(scene::Glyph {
-                    //             font_id: run.font_id,
-                    //             font_size: self.layout.font_size,
-                    //             id: glyph.id,
-                    //             origin: glyph_bounds.origin() + baseline_offset,
-                    //             color,
-                    //         });
-                    //     }
-                    // }
-                    anyhow::Ok(())
-                })??;
+                let text_system = cx.text_system();
+                let _glyph_bounds = Bounds {
+                    origin: glyph_origin,
+                    size: text_system
+                        .bounding_box(run.font_id, self.layout.font_size)?
+                        .size,
+                };
+                // if glyph_bounds.intersects(visible_bounds) {
+                //     if glyph.is_emoji {
+                //         cx.scene().push_image_glyph(scene::ImageGlyph {
+                //             font_id: run.font_id,
+                //             font_size: self.layout.font_size,
+                //             id: glyph.id,
+                //             origin: glyph_bounds.origin() + baseline_offset,
+                //         });
+                //     } else {
+                //         cx.scene().push_glyph(scene::Glyph {
+                //             font_id: run.font_id,
+                //             font_size: self.layout.font_size,
+                //             id: glyph.id,
+                //             origin: glyph_bounds.origin() + baseline_offset,
+                //             color,
+                //         });
+                //     }
+                // }
             }
         }
 

crates/gpui3/src/window.rs 🔗

@@ -16,8 +16,7 @@ pub struct AnyWindow {}
 pub struct Window {
     handle: AnyWindowHandle,
     platform_window: MainThreadOnly<Box<dyn PlatformWindow>>,
-    monochrome_sprite_atlas: Arc<dyn PlatformAtlas>,
-    polychrome_sprite_atlas: Arc<dyn PlatformAtlas>,
+    sprite_atlas: Arc<dyn PlatformAtlas>,
     rem_size: Pixels,
     content_size: Size<Pixels>,
     layout_engine: TaffyLayoutEngine,
@@ -36,8 +35,7 @@ impl Window {
         cx: &mut MainThread<AppContext>,
     ) -> Self {
         let platform_window = cx.platform().open_window(handle, options);
-        let monochrome_sprite_atlas = platform_window.monochrome_sprite_atlas();
-        let polychrome_sprite_atlas = platform_window.polychrome_sprite_atlas();
+        let sprite_atlas = platform_window.sprite_atlas();
         let mouse_position = platform_window.mouse_position();
         let content_size = platform_window.content_size();
         let scale_factor = platform_window.scale_factor();
@@ -60,8 +58,7 @@ impl Window {
         Window {
             handle,
             platform_window,
-            monochrome_sprite_atlas,
-            polychrome_sprite_atlas,
+            sprite_atlas,
             rem_size: px(16.),
             content_size,
             layout_engine: TaffyLayoutEngine::new(),
@@ -231,6 +228,7 @@ impl<'a, 'w> WindowContext<'a, 'w> {
             font_size,
             subpixel_variant,
             scale_factor,
+            is_emoji: false,
         };
 
         let raster_bounds = self.text_system().raster_bounds(&params)?;
@@ -238,7 +236,7 @@ impl<'a, 'w> WindowContext<'a, 'w> {
             let layer_id = self.current_layer_id();
             let tile = self
                 .window
-                .monochrome_sprite_atlas
+                .sprite_atlas
                 .get_or_insert_with(&params.clone().into(), &mut || {
                     self.text_system().rasterize_glyph(&params)
                 })?;
@@ -262,47 +260,6 @@ impl<'a, 'w> WindowContext<'a, 'w> {
         Ok(())
     }
 
-    pub fn paint_svg(
-        &mut self,
-        bounds: Bounds<Pixels>,
-        order: u32,
-        path: SharedString,
-        color: Hsla,
-    ) -> Result<()> {
-        let scale_factor = self.scale_factor();
-        let bounds = bounds.scale(scale_factor);
-        // Render the SVG at twice the size to get a higher quality result.
-        let params = RenderSvgParams {
-            path,
-            size: bounds
-                .size
-                .map(|pixels| DevicePixels::from((pixels.0 * 2.).ceil() as i32)),
-        };
-
-        let layer_id = self.current_layer_id();
-        let tile = self.window.monochrome_sprite_atlas.get_or_insert_with(
-            &params.clone().into(),
-            &mut || {
-                let bytes = self.svg_renderer.render(&params)?;
-                Ok((params.size, bytes))
-            },
-        )?;
-        let content_mask = self.content_mask().scale(scale_factor);
-
-        self.window.scene.insert(
-            layer_id,
-            MonochromeSprite {
-                order,
-                bounds,
-                content_mask,
-                color,
-                tile,
-            },
-        );
-
-        Ok(())
-    }
-
     pub fn paint_emoji(
         &mut self,
         origin: Point<Pixels>,
@@ -317,8 +274,10 @@ impl<'a, 'w> WindowContext<'a, 'w> {
             font_id,
             glyph_id,
             font_size,
+            // We don't render emojis with subpixel variants.
             subpixel_variant: Default::default(),
             scale_factor,
+            is_emoji: true,
         };
 
         let raster_bounds = self.text_system().raster_bounds(&params)?;
@@ -326,7 +285,7 @@ impl<'a, 'w> WindowContext<'a, 'w> {
             let layer_id = self.current_layer_id();
             let tile = self
                 .window
-                .polychrome_sprite_atlas
+                .sprite_atlas
                 .get_or_insert_with(&params.clone().into(), &mut || {
                     self.text_system().rasterize_glyph(&params)
                 })?;
@@ -349,6 +308,47 @@ impl<'a, 'w> WindowContext<'a, 'w> {
         Ok(())
     }
 
+    pub fn paint_svg(
+        &mut self,
+        bounds: Bounds<Pixels>,
+        order: u32,
+        path: SharedString,
+        color: Hsla,
+    ) -> Result<()> {
+        let scale_factor = self.scale_factor();
+        let bounds = bounds.scale(scale_factor);
+        // Render the SVG at twice the size to get a higher quality result.
+        let params = RenderSvgParams {
+            path,
+            size: bounds
+                .size
+                .map(|pixels| DevicePixels::from((pixels.0 * 2.).ceil() as i32)),
+        };
+
+        let layer_id = self.current_layer_id();
+        let tile =
+            self.window
+                .sprite_atlas
+                .get_or_insert_with(&params.clone().into(), &mut || {
+                    let bytes = self.svg_renderer.render(&params)?;
+                    Ok((params.size, bytes))
+                })?;
+        let content_mask = self.content_mask().scale(scale_factor);
+
+        self.window.scene.insert(
+            layer_id,
+            MonochromeSprite {
+                order,
+                bounds,
+                content_mask,
+                color,
+                tile,
+            },
+        );
+
+        Ok(())
+    }
+
     pub(crate) fn draw(&mut self) -> Result<()> {
         let unit_entity = self.unit_entity.clone();
         self.update_entity(&unit_entity, |_, cx| {

crates/storybook2/src/collab_panel.rs 🔗

@@ -51,7 +51,7 @@ impl CollabPanel {
                             //:: https://tailwindcss.com/docs/hover-focus-and-other-states#styling-based-on-parent-state
                             // .group()
                             // List Section Header
-                            .child(self.list_section_header("#CRDB", true, theme))
+                            .child(self.list_section_header("#CRDB 🗃️", true, theme))
                             // List Item Large
                             .child(self.list_item(
                                 "http://github.com/maxbrunsfeld.png?s=50",