From e0c43dac257a60310d925e784bd45b7c3003e26b Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 6 Apr 2021 13:44:38 +0200 Subject: [PATCH] Implement SVG rendering --- Cargo.lock | 314 +++++++++++++++++++++++++- gpui/Cargo.toml | 3 + gpui/src/assets.rs | 16 +- gpui/src/elements/svg.rs | 101 +++++---- gpui/src/platform/mac/renderer.rs | 29 ++- gpui/src/platform/mac/sprite_cache.rs | 64 ++++++ gpui/src/scene.rs | 23 +- zed/assets/icons/file-16.svg | 1 + zed/src/file_finder.rs | 3 +- 9 files changed, 505 insertions(+), 49 deletions(-) create mode 100644 zed/assets/icons/file-16.svg diff --git a/Cargo.lock b/Cargo.lock index a046ea2a87..4cd3076580 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,5 +1,11 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + [[package]] name = "adler32" version = "1.2.0" @@ -297,6 +303,12 @@ version = "3.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "63396b8a4b9de3f4fdfb320ab6080762242f66a8ef174c49d8e19b674db4cdbe" +[[package]] +name = "bytemuck" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bed57e2090563b83ba8f83366628ce535a7584c9afa4c9fc0612a03925c6df58" + [[package]] name = "byteorder" version = "1.4.2" @@ -545,6 +557,15 @@ dependencies = [ "syn", ] +[[package]] +name = "data-url" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d33fe99ccedd6e84bc035f1931bb2e6be79739d6242bd895e7311c886c50dc9c" +dependencies = [ + "matches", +] + [[package]] name = "deflate" version = "0.8.6" @@ -671,6 +692,24 @@ dependencies = [ "instant", ] +[[package]] +name = "flate2" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd3aec53de10fe96d7d8c565eb17f2c687bb5518a2ec453b5b1252964526abe0" +dependencies = [ + "cfg-if 1.0.0", + "crc32fast", + "libc", + "miniz_oxide 0.4.4", +] + +[[package]] +name = "float-cmp" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75224bec9bfe1a65e2d34132933f2de7fe79900c96a0174307554244ece8150e" + [[package]] name = "float-ord" version = "0.2.0" @@ -707,6 +746,17 @@ dependencies = [ "winapi", ] +[[package]] +name = "fontdb" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "428948a0f39fb83fe55991d4423e35a793cdbb0322ebe23853f6024124a330d7" +dependencies = [ + "log", + "memmap2 0.1.0", + "ttf-parser 0.9.0", +] + [[package]] name = "foreign-types" version = "0.3.2" @@ -879,10 +929,13 @@ dependencies = [ "png", "rand 0.8.3", "replace_with", + "resvg", "simplelog", "smallvec", "smol", + "tiny-skia", "tree-sitter", + "usvg", ] [[package]] @@ -933,6 +986,12 @@ version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" +[[package]] +name = "jpeg-decoder" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "229d53d58899083193af11e15917b5640cd40b29ff475a1fe4ef725deb02d0f2" + [[package]] name = "js-sys" version = "0.3.50" @@ -942,6 +1001,15 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "kurbo" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e30b1df631d23875f230ed3ddd1a88c231f269a04b2044eb6ca87e763b5f4c42" +dependencies = [ + "arrayvec", +] + [[package]] name = "kv-log-macro" version = "1.0.7" @@ -1018,6 +1086,12 @@ dependencies = [ "libc", ] +[[package]] +name = "matches" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" + [[package]] name = "maybe-uninit" version = "2.0.0" @@ -1030,6 +1104,24 @@ version = "2.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" +[[package]] +name = "memmap2" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b70ca2a6103ac8b665dc150b142ef0e4e89df640c9e6cf295d189c3caebe5a" +dependencies = [ + "libc", +] + +[[package]] +name = "memmap2" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "397d1a6d6d0563c0f5462bbdae662cf6c784edf5e828e40c7257f85d82bf56dd" +dependencies = [ + "libc", +] + [[package]] name = "metal" version = "0.21.0" @@ -1053,6 +1145,16 @@ dependencies = [ "adler32", ] +[[package]] +name = "miniz_oxide" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" +dependencies = [ + "adler", + "autocfg", +] + [[package]] name = "nb-connect" version = "1.0.3" @@ -1201,6 +1303,12 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" +[[package]] +name = "pico-args" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d70072c20945e1ab871c472a285fc772aefd4f5407723c206242f2c6f94595d6" + [[package]] name = "pin-project-lite" version = "0.2.4" @@ -1228,7 +1336,7 @@ dependencies = [ "bitflags", "crc32fast", "deflate", - "miniz_oxide", + "miniz_oxide 0.3.7", ] [[package]] @@ -1336,6 +1444,12 @@ dependencies = [ "rand_core 0.6.2", ] +[[package]] +name = "rctree" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be9e29cb19c8fe84169fcb07f8f11e66bc9e6e0280efd4715c54818296f8a4a8" + [[package]] name = "rdrand" version = "0.4.0" @@ -1414,6 +1528,40 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3a8614ee435691de62bcffcf4a66d91b3594bf1428a5722e79103249a095690" +[[package]] +name = "resvg" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac9efbe9c239253e11e518352c5f015ec0c69e73658eed153670e853e1b78e40" +dependencies = [ + "jpeg-decoder", + "log", + "pico-args", + "png", + "rgb", + "svgfilters", + "tiny-skia", + "usvg", +] + +[[package]] +name = "rgb" +version = "0.8.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fddb3b23626145d1776addfc307e1a1851f60ef6ca64f376bcb889697144cf0" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "roxmltree" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "921904a62e410e37e215c40381b7117f830d9d89ba60ab5236170541dd25646b" +dependencies = [ + "xmlparser", +] + [[package]] name = "rust-argon2" version = "0.8.3" @@ -1474,12 +1622,37 @@ dependencies = [ "semver", ] +[[package]] +name = "rustybuzz" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ab463a295d00f3692e0974a0bfd83c7a9bcd119e27e07c2beecdb1b44a09d10" +dependencies = [ + "bitflags", + "bytemuck", + "smallvec", + "ttf-parser 0.9.0", + "unicode-bidi-mirroring", + "unicode-ccc", + "unicode-general-category", + "unicode-script", +] + [[package]] name = "ryu" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" +[[package]] +name = "safe_arch" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1ff3d6d9696af502cc3110dacce942840fb06ff4514cad92236ecc455f2ce05" +dependencies = [ + "bytemuck", +] + [[package]] name = "same-file" version = "1.0.6" @@ -1579,6 +1752,15 @@ dependencies = [ "libc", ] +[[package]] +name = "simplecss" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "596554e63596d556a0dbd681416342ca61c75f1a45203201e7e77d3fa2fa9014" +dependencies = [ + "log", +] + [[package]] name = "simplelog" version = "0.9.0" @@ -1590,6 +1772,12 @@ dependencies = [ "termcolor", ] +[[package]] +name = "siphasher" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac" + [[package]] name = "slab" version = "0.4.2" @@ -1643,6 +1831,26 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8fb1df15f412ee2e9dfc1c504260fa695c1c3f10fe9f4a6ee2d2184d7d6450e2" +[[package]] +name = "svgfilters" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb0dce2fee79ac40c21dafba48565ff7a5fa275e23ffe9ce047a40c9574ba34e" +dependencies = [ + "float-cmp", + "rgb", +] + +[[package]] +name = "svgtypes" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c536faaff1a10837cfe373142583f6e27d81e96beba339147e77b67c9f260ff" +dependencies = [ + "float-cmp", + "siphasher", +] + [[package]] name = "syn" version = "1.0.67" @@ -1702,6 +1910,20 @@ dependencies = [ "winapi", ] +[[package]] +name = "tiny-skia" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bf81f2900d2e235220e6f31ec9f63ade6a7f59090c556d74fe949bb3b15e9fe" +dependencies = [ + "arrayref", + "arrayvec", + "bytemuck", + "cfg-if 1.0.0", + "png", + "safe_arch", +] + [[package]] name = "tree-sitter" version = "0.17.1" @@ -1712,6 +1934,57 @@ dependencies = [ "regex", ] +[[package]] +name = "ttf-parser" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62ddb402ac6c2af6f7a2844243887631c4e94b51585b229fcfddb43958cd55ca" + +[[package]] +name = "ttf-parser" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85e00391c1f3d171490a3f8bd79999b0002ae38d3da0d6a3a306c754b053d71b" + +[[package]] +name = "unicode-bidi" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" +dependencies = [ + "matches", +] + +[[package]] +name = "unicode-bidi-mirroring" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56d12260fb92d52f9008be7e4bca09f584780eb2266dc8fecc6a192bec561694" + +[[package]] +name = "unicode-ccc" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28ae07c514c335bbd0251147bb1de333e28ebc8f57d792014f919ed212d119f6" + +[[package]] +name = "unicode-general-category" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f9af028e052a610d99e066b33304625dea9613170a2563314490a4e6ec5cf7f" + +[[package]] +name = "unicode-script" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79bf4d5fc96546fdb73f9827097810bbda93b11a6770ff3a54e1f445d4135787" + +[[package]] +name = "unicode-vo" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1d386ff53b415b7fe27b50bb44679e2cc4660272694b7b6f3326d8480823a94" + [[package]] name = "unicode-width" version = "0.1.8" @@ -1730,6 +2003,33 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f14ee04d9415b52b3aeab06258a3f07093182b88ba0f9b8d203f211a7a7d41c7" +[[package]] +name = "usvg" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ffbeb91d06989028c9c5e44d14d78b0cacdec56a613bb146e7a70007b1b6163" +dependencies = [ + "base64", + "data-url", + "flate2", + "fontdb", + "kurbo", + "log", + "memmap2 0.2.2", + "pico-args", + "rctree", + "roxmltree", + "rustybuzz", + "simplecss", + "siphasher", + "svgtypes", + "ttf-parser 0.12.0", + "unicode-bidi", + "unicode-script", + "unicode-vo", + "xmlwriter", +] + [[package]] name = "value-bag" version = "1.0.0-alpha.6" @@ -1920,6 +2220,18 @@ dependencies = [ "winapi", ] +[[package]] +name = "xmlparser" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "114ba2b24d2167ef6d67d7d04c8cc86522b87f490025f39f0303b7db5bf5e3d8" + +[[package]] +name = "xmlwriter" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9" + [[package]] name = "zed" version = "0.1.0" diff --git a/gpui/Cargo.toml b/gpui/Cargo.toml index 4a65170c24..2b0e90bedc 100644 --- a/gpui/Cargo.toml +++ b/gpui/Cargo.toml @@ -17,9 +17,12 @@ pathfinder_color = "0.5" pathfinder_geometry = "0.5" rand = "0.8.3" replace_with = "0.1.7" +resvg = "0.14" smallvec = "1.6.1" smol = "1.2" +tiny-skia = "0.5" tree-sitter = "0.17" +usvg = "0.14" [build-dependencies] bindgen = "0.57" diff --git a/gpui/src/assets.rs b/gpui/src/assets.rs index a17ac03ac5..63c6c07570 100644 --- a/gpui/src/assets.rs +++ b/gpui/src/assets.rs @@ -1,5 +1,5 @@ use anyhow::{anyhow, Result}; -use std::borrow::Cow; +use std::{borrow::Cow, cell::RefCell, collections::HashMap}; pub trait AssetSource: 'static { fn load(&self, path: &str) -> Result>; @@ -16,12 +16,26 @@ impl AssetSource for () { pub struct AssetCache { source: Box, + svgs: RefCell>, } impl AssetCache { pub fn new(source: impl AssetSource) -> Self { Self { source: Box::new(source), + svgs: RefCell::new(HashMap::new()), + } + } + + pub fn svg(&self, path: &str) -> Result { + let mut svgs = self.svgs.borrow_mut(); + if let Some(svg) = svgs.get(path) { + Ok(svg.clone()) + } else { + let bytes = self.source.load(path)?; + let svg = usvg::Tree::from_data(&bytes, &usvg::Options::default())?; + svgs.insert(path.to_string(), svg.clone()); + Ok(svg) } } } diff --git a/gpui/src/elements/svg.rs b/gpui/src/elements/svg.rs index 058fefd285..9afc5eeab8 100644 --- a/gpui/src/elements/svg.rs +++ b/gpui/src/elements/svg.rs @@ -1,73 +1,85 @@ use crate::{ - geometry::vector::Vector2F, AfterLayoutContext, Element, Event, EventContext, LayoutContext, - PaintContext, SizeConstraint, + color::ColorU, + geometry::{ + rect::RectF, + vector::{vec2f, Vector2F}, + }, + scene, AfterLayoutContext, Element, Event, EventContext, LayoutContext, PaintContext, + SizeConstraint, }; pub struct Svg { path: String, + color: ColorU, } impl Svg { pub fn new(path: String) -> Self { - Self { path } + Self { + path, + color: ColorU::black(), + } + } + + pub fn with_color(mut self, color: ColorU) -> Self { + self.color = color; + self } } impl Element for Svg { - type LayoutState = (); + type LayoutState = Option; type PaintState = (); fn layout( &mut self, - _: SizeConstraint, - _: &mut LayoutContext, + constraint: SizeConstraint, + ctx: &mut LayoutContext, ) -> (Vector2F, Self::LayoutState) { - // let size; - // match ctx.asset_cache.svg(&self.path) { - // Ok(tree) => { - // size = if constraint.max.x().is_infinite() && constraint.max.y().is_infinite() { - // let rect = usvg_rect_to_euclid_rect(&tree.svg_node().view_box.rect); - // rect.size() - // } else { - // let max_size = constraint.max; - // let svg_size = usvg_rect_to_euclid_rect(&tree.svg_node().view_box.rect).size(); + match ctx.asset_cache.svg(&self.path) { + Ok(tree) => { + let size = if constraint.max.x().is_infinite() && constraint.max.y().is_infinite() { + let rect = from_usvg_rect(tree.svg_node().view_box.rect); + rect.size() + } else { + let max_size = constraint.max; + let svg_size = from_usvg_rect(tree.svg_node().view_box.rect).size(); - // if max_size.x().is_infinite() - // || max_size.x() / max_size.y() > svg_size.x() / svg_size.y() - // { - // vec2f(svg_size.x() * max_size.y() / svg_size.y(), max_size.y()) - // } else { - // vec2f(max_size.x(), svg_size.y() * max_size.x() / svg_size.x()) - // } - // }; - // self.tree = Some(tree); - // } - // Err(error) => { - // log::error!("{}", error); - // size = constraint.min; - // } - // }; - - // size - - todo!() + if max_size.x().is_infinite() + || max_size.x() / max_size.y() > svg_size.x() / svg_size.y() + { + vec2f(svg_size.x() * max_size.y() / svg_size.y(), max_size.y()) + } else { + vec2f(max_size.x(), svg_size.y() * max_size.x() / svg_size.x()) + } + }; + (size, Some(tree)) + } + Err(error) => { + log::error!("{}", error); + (constraint.min, None) + } + } } fn after_layout(&mut self, _: Vector2F, _: &mut Self::LayoutState, _: &mut AfterLayoutContext) { } - fn paint( - &mut self, - _: pathfinder_geometry::rect::RectF, - _: &mut Self::LayoutState, - _: &mut PaintContext, - ) -> Self::PaintState { + fn paint(&mut self, bounds: RectF, svg: &mut Self::LayoutState, ctx: &mut PaintContext) { + if let Some(svg) = svg.clone() { + ctx.scene.push_icon(scene::Icon { + bounds, + svg, + path: self.path.clone(), + color: self.color, + }); + } } fn dispatch_event( &mut self, _: &Event, - _: pathfinder_geometry::rect::RectF, + _: RectF, _: &mut Self::LayoutState, _: &mut Self::PaintState, _: &mut EventContext, @@ -75,3 +87,10 @@ impl Element for Svg { false } } + +fn from_usvg_rect(rect: usvg::Rect) -> RectF { + RectF::new( + vec2f(rect.x() as f32, rect.y() as f32), + vec2f(rect.width() as f32, rect.height() as f32), + ) +} diff --git a/gpui/src/platform/mac/renderer.rs b/gpui/src/platform/mac/renderer.rs index daa27a99bc..55cf45e94c 100644 --- a/gpui/src/platform/mac/renderer.rs +++ b/gpui/src/platform/mac/renderer.rs @@ -296,7 +296,7 @@ impl Renderer { drawable_size, command_encoder, ); - self.render_glyph_sprites(scene, layer, offset, drawable_size, command_encoder); + self.render_sprites(scene, layer, offset, drawable_size, command_encoder); } command_encoder.end_encoding(); @@ -465,7 +465,7 @@ impl Renderer { *offset = next_offset; } - fn render_glyph_sprites( + fn render_sprites( &mut self, scene: &Scene, layer: &Layer, @@ -473,11 +473,12 @@ impl Renderer { drawable_size: Vector2F, command_encoder: &metal::RenderCommandEncoderRef, ) { - if layer.glyphs().is_empty() { + if layer.glyphs().is_empty() && layer.icons().is_empty() { return; } let mut sprites_by_atlas = HashMap::new(); + for glyph in layer.glyphs() { if let Some(sprite) = self.sprite_cache.render_glyph( glyph.font_id, @@ -501,6 +502,28 @@ impl Renderer { } } + for icon in layer.icons() { + let sprite = self.sprite_cache.render_icon( + icon.bounds.size(), + icon.path.clone(), + icon.svg.clone(), + scene.scale_factor(), + ); + + // Snap sprite to pixel grid. + let origin = (icon.bounds.origin() * scene.scale_factor()).floor(); + sprites_by_atlas + .entry(sprite.atlas_id) + .or_insert_with(Vec::new) + .push(shaders::GPUISprite { + origin: origin.to_float2(), + size: sprite.size.to_float2(), + atlas_origin: sprite.atlas_origin.to_float2(), + color: icon.color.to_uchar4(), + compute_winding: 0, + }); + } + command_encoder.set_render_pipeline_state(&self.sprite_pipeline_state); command_encoder.set_vertex_buffer( shaders::GPUISpriteVertexInputIndex_GPUISpriteVertexInputIndexVertices as u64, diff --git a/gpui/src/platform/mac/sprite_cache.rs b/gpui/src/platform/mac/sprite_cache.rs index 687938df6d..f73ebc6292 100644 --- a/gpui/src/platform/mac/sprite_cache.rs +++ b/gpui/src/platform/mac/sprite_cache.rs @@ -27,12 +27,27 @@ pub struct GlyphSprite { pub size: Vector2I, } +#[derive(Hash, Eq, PartialEq)] +struct IconDescriptor { + path: String, + width: i32, + height: i32, +} + +#[derive(Clone)] +pub struct IconSprite { + pub atlas_id: usize, + pub atlas_origin: Vector2I, + pub size: Vector2I, +} + pub struct SpriteCache { device: metal::Device, atlas_size: Vector2I, fonts: Arc, atlases: Vec, glyphs: HashMap>, + icons: HashMap, } impl SpriteCache { @@ -48,6 +63,7 @@ impl SpriteCache { fonts, atlases, glyphs: Default::default(), + icons: Default::default(), } } @@ -119,6 +135,54 @@ impl SpriteCache { .clone() } + pub fn render_icon( + &mut self, + size: Vector2F, + path: String, + svg: usvg::Tree, + scale_factor: f32, + ) -> IconSprite { + let atlases = &mut self.atlases; + let atlas_size = self.atlas_size; + let device = &self.device; + let size = (size * scale_factor).round().to_i32(); + assert!(size.x() < atlas_size.x()); + assert!(size.y() < atlas_size.y()); + self.icons + .entry(IconDescriptor { + path, + width: size.x(), + height: size.y(), + }) + .or_insert_with(|| { + let mut pixmap = tiny_skia::Pixmap::new(size.x() as u32, size.y() as u32).unwrap(); + resvg::render(&svg, usvg::FitTo::Width(size.x() as u32), pixmap.as_mut()); + let mask = pixmap + .pixels() + .iter() + .map(|a| a.alpha()) + .collect::>(); + + let atlas_bounds = atlases + .last_mut() + .unwrap() + .try_insert(size, &mask) + .unwrap_or_else(|| { + let mut atlas = Atlas::new(device, atlas_size); + let bounds = atlas.try_insert(size, &mask).unwrap(); + atlases.push(atlas); + bounds + }); + + IconSprite { + atlas_id: atlases.len() - 1, + atlas_origin: atlas_bounds.origin(), + size, + } + }) + .clone() + } + pub fn atlas_texture(&self, atlas_id: usize) -> Option<&metal::TextureRef> { self.atlases.get(atlas_id).map(|a| a.texture.as_ref()) } diff --git a/gpui/src/scene.rs b/gpui/src/scene.rs index d002804dae..cde5dc60d0 100644 --- a/gpui/src/scene.rs +++ b/gpui/src/scene.rs @@ -10,12 +10,13 @@ pub struct Scene { active_layer_stack: Vec, } -#[derive(Default, Debug)] +#[derive(Default)] pub struct Layer { clip_bounds: Option, quads: Vec, shadows: Vec, glyphs: Vec, + icons: Vec, paths: Vec, } @@ -44,6 +45,13 @@ pub struct Glyph { pub color: ColorU, } +pub struct Icon { + pub bounds: RectF, + pub svg: usvg::Tree, + pub path: String, + pub color: ColorU, +} + #[derive(Clone, Copy, Default, Debug)] pub struct Border { pub width: f32, @@ -107,6 +115,10 @@ impl Scene { self.active_layer().push_glyph(glyph) } + pub fn push_icon(&mut self, icon: Icon) { + self.active_layer().push_icon(icon) + } + pub fn push_path(&mut self, path: Path) { self.active_layer().push_path(path); } @@ -123,6 +135,7 @@ impl Layer { quads: Vec::new(), shadows: Vec::new(), glyphs: Vec::new(), + icons: Vec::new(), paths: Vec::new(), } } @@ -155,6 +168,14 @@ impl Layer { self.glyphs.as_slice() } + pub fn push_icon(&mut self, icon: Icon) { + self.icons.push(icon); + } + + pub fn icons(&self) -> &[Icon] { + self.icons.as_slice() + } + fn push_path(&mut self, path: Path) { if !path.bounds.is_empty() { self.paths.push(path); diff --git a/zed/assets/icons/file-16.svg b/zed/assets/icons/file-16.svg new file mode 100644 index 0000000000..b487e21e37 --- /dev/null +++ b/zed/assets/icons/file-16.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/zed/src/file_finder.rs b/zed/src/file_finder.rs index 296e910b63..fcf32ea64c 100644 --- a/zed/src/file_finder.rs +++ b/zed/src/file_finder.rs @@ -175,8 +175,7 @@ impl FileFinder { LineBox::new( settings.ui_font_family, settings.ui_font_size, - Empty::new().boxed(), - // Svg::new("icons/file-16.svg".into()).boxed(), + Svg::new("icons/file-16.svg".into()).boxed(), ) .boxed(), )