Checkpoint: render SVGs
This commit is contained in:
parent
a1ee2db6d1
commit
4cf2ba20c2
13 changed files with 187 additions and 175 deletions
|
@ -8,9 +8,9 @@ pub use model_context::*;
|
||||||
use refineable::Refineable;
|
use refineable::Refineable;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
current_platform, run_on_main, spawn_on_main, Context, LayoutId, MainThread, MainThreadOnly,
|
current_platform, run_on_main, spawn_on_main, AssetSource, Context, LayoutId, MainThread,
|
||||||
Platform, PlatformDispatcher, RootView, TextStyle, TextStyleRefinement, TextSystem, Window,
|
MainThreadOnly, Platform, PlatformDispatcher, RootView, SvgRenderer, TextStyle,
|
||||||
WindowContext, WindowHandle, WindowId,
|
TextStyleRefinement, TextSystem, Window, WindowContext, WindowHandle, WindowId,
|
||||||
};
|
};
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use collections::{HashMap, VecDeque};
|
use collections::{HashMap, VecDeque};
|
||||||
|
@ -29,16 +29,18 @@ use util::ResultExt;
|
||||||
pub struct App(Arc<Mutex<MainThread<AppContext>>>);
|
pub struct App(Arc<Mutex<MainThread<AppContext>>>);
|
||||||
|
|
||||||
impl App {
|
impl App {
|
||||||
pub fn production() -> Self {
|
pub fn production(asset_source: Arc<dyn AssetSource>) -> Self {
|
||||||
Self::new(current_platform())
|
Self::new(current_platform(), asset_source)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(test, feature = "test"))]
|
#[cfg(any(test, feature = "test"))]
|
||||||
pub fn test() -> Self {
|
pub fn test() -> Self {
|
||||||
Self::new(Arc::new(super::TestPlatform::new()))
|
let platform = Arc::new(super::TestPlatform::new());
|
||||||
|
let asset_source = Arc::new(());
|
||||||
|
Self::new(platform, asset_source)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new(platform: Arc<dyn Platform>) -> Self {
|
fn new(platform: Arc<dyn Platform>, asset_source: Arc<dyn AssetSource>) -> Self {
|
||||||
let dispatcher = platform.dispatcher();
|
let dispatcher = platform.dispatcher();
|
||||||
let text_system = Arc::new(TextSystem::new(platform.text_system()));
|
let text_system = Arc::new(TextSystem::new(platform.text_system()));
|
||||||
let entities = EntityMap::new();
|
let entities = EntityMap::new();
|
||||||
|
@ -49,6 +51,7 @@ impl App {
|
||||||
platform: MainThreadOnly::new(platform, dispatcher.clone()),
|
platform: MainThreadOnly::new(platform, dispatcher.clone()),
|
||||||
dispatcher,
|
dispatcher,
|
||||||
text_system,
|
text_system,
|
||||||
|
svg_renderer: SvgRenderer::new(asset_source),
|
||||||
pending_updates: 0,
|
pending_updates: 0,
|
||||||
text_style_stack: Vec::new(),
|
text_style_stack: Vec::new(),
|
||||||
state_stacks_by_type: HashMap::default(),
|
state_stacks_by_type: HashMap::default(),
|
||||||
|
@ -83,6 +86,7 @@ pub struct AppContext {
|
||||||
dispatcher: Arc<dyn PlatformDispatcher>,
|
dispatcher: Arc<dyn PlatformDispatcher>,
|
||||||
text_system: Arc<TextSystem>,
|
text_system: Arc<TextSystem>,
|
||||||
pending_updates: usize,
|
pending_updates: usize,
|
||||||
|
pub(crate) svg_renderer: SvgRenderer,
|
||||||
pub(crate) text_style_stack: Vec<TextStyleRefinement>,
|
pub(crate) text_style_stack: Vec<TextStyleRefinement>,
|
||||||
pub(crate) state_stacks_by_type: HashMap<TypeId, Vec<Box<dyn Any + Send + Sync>>>,
|
pub(crate) state_stacks_by_type: HashMap<TypeId, Vec<Box<dyn Any + Send + Sync>>>,
|
||||||
pub(crate) unit_entity: Handle<()>,
|
pub(crate) unit_entity: Handle<()>,
|
||||||
|
|
|
@ -44,6 +44,10 @@ impl ImageData {
|
||||||
&self.data
|
&self.data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn into_bytes(self) -> Vec<u8> {
|
||||||
|
self.data.into_raw()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn size(&self) -> Size<DevicePixels> {
|
pub fn size(&self) -> Size<DevicePixels> {
|
||||||
let (width, height) = self.data.dimensions();
|
let (width, height) = self.data.dimensions();
|
||||||
size(width.into(), height.into())
|
size(width.into(), height.into())
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
use crate::{Element, Layout, LayoutId, Result, Style, StyleHelpers, Styled};
|
use crate::{Element, Layout, LayoutId, Result, SharedString, Style, StyleHelpers, Styled};
|
||||||
use refineable::RefinementCascade;
|
use refineable::RefinementCascade;
|
||||||
use std::{borrow::Cow, marker::PhantomData};
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
pub struct Svg<S> {
|
pub struct Svg<S> {
|
||||||
path: Option<Cow<'static, str>>,
|
path: Option<SharedString>,
|
||||||
style: RefinementCascade<Style>,
|
style: RefinementCascade<Style>,
|
||||||
state_type: PhantomData<S>,
|
state_type: PhantomData<S>,
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@ pub fn svg<S>() -> Svg<S> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S> Svg<S> {
|
impl<S> Svg<S> {
|
||||||
pub fn path(mut self, path: impl Into<Cow<'static, str>>) -> Self {
|
pub fn path(mut self, path: impl Into<SharedString>) -> Self {
|
||||||
self.path = Some(path.into());
|
self.path = Some(path.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
@ -41,28 +41,18 @@ impl<S: 'static> Element for Svg<S> {
|
||||||
|
|
||||||
fn paint(
|
fn paint(
|
||||||
&mut self,
|
&mut self,
|
||||||
_layout: Layout,
|
layout: Layout,
|
||||||
_: &mut Self::State,
|
_: &mut Self::State,
|
||||||
_: &mut Self::FrameState,
|
_: &mut Self::FrameState,
|
||||||
_cx: &mut crate::ViewContext<S>,
|
cx: &mut crate::ViewContext<S>,
|
||||||
) -> Result<()>
|
) -> Result<()>
|
||||||
where
|
where
|
||||||
Self: Sized,
|
Self: Sized,
|
||||||
{
|
{
|
||||||
// todo!
|
let fill_color = self.computed_style().fill.and_then(|fill| fill.color());
|
||||||
// let fill_color = self.computed_style().fill.and_then(|fill| fill.color());
|
if let Some((path, fill_color)) = self.path.as_ref().zip(fill_color) {
|
||||||
// if let Some((path, fill_color)) = self.path.as_ref().zip(fill_color) {
|
cx.paint_svg(layout.bounds, layout.order, path.clone(), fill_color)?;
|
||||||
// if let Some(svg_tree) = cx.asset_cache.svg(path).log_err() {
|
}
|
||||||
// let icon = scene::Icon {
|
|
||||||
// bounds: layout.bounds + parent_origin,
|
|
||||||
// svg: svg_tree,
|
|
||||||
// path: path.clone(),
|
|
||||||
// color: Rgba::from(fill_color).into(),
|
|
||||||
// };
|
|
||||||
|
|
||||||
// cx.scene().push_icon(icon);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ mod scene;
|
||||||
mod style;
|
mod style;
|
||||||
mod style_helpers;
|
mod style_helpers;
|
||||||
mod styled;
|
mod styled;
|
||||||
mod svg_library;
|
mod svg_renderer;
|
||||||
mod taffy;
|
mod taffy;
|
||||||
mod text_system;
|
mod text_system;
|
||||||
mod util;
|
mod util;
|
||||||
|
@ -26,7 +26,7 @@ pub use elements::*;
|
||||||
pub use executor::*;
|
pub use executor::*;
|
||||||
pub use geometry::*;
|
pub use geometry::*;
|
||||||
pub use gpui3_macros::*;
|
pub use gpui3_macros::*;
|
||||||
pub use svg_library::*;
|
pub use svg_renderer::*;
|
||||||
|
|
||||||
pub use platform::*;
|
pub use platform::*;
|
||||||
pub use refineable::*;
|
pub use refineable::*;
|
||||||
|
|
|
@ -7,7 +7,7 @@ mod test;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
AnyWindowHandle, Bounds, DevicePixels, Font, FontId, FontMetrics, GlyphId, Pixels, Point,
|
AnyWindowHandle, Bounds, DevicePixels, Font, FontId, FontMetrics, GlyphId, Pixels, Point,
|
||||||
RenderGlyphParams, Result, Scene, ShapedLine, SharedString, Size,
|
RenderGlyphParams, RenderSvgParams, Result, Scene, ShapedLine, SharedString, Size,
|
||||||
};
|
};
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use async_task::Runnable;
|
use async_task::Runnable;
|
||||||
|
@ -147,7 +147,7 @@ pub trait PlatformWindow {
|
||||||
fn is_topmost_for_position(&self, position: Point<Pixels>) -> bool;
|
fn is_topmost_for_position(&self, position: Point<Pixels>) -> bool;
|
||||||
fn draw(&self, scene: Scene);
|
fn draw(&self, scene: Scene);
|
||||||
|
|
||||||
fn glyph_atlas(&self) -> Arc<dyn PlatformAtlas>;
|
fn monochrome_sprite_atlas(&self) -> Arc<dyn PlatformAtlas>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait PlatformDispatcher: Send + Sync {
|
pub trait PlatformDispatcher: Send + Sync {
|
||||||
|
@ -178,7 +178,7 @@ pub trait PlatformTextSystem: Send + Sync {
|
||||||
#[derive(PartialEq, Eq, Hash, Clone)]
|
#[derive(PartialEq, Eq, Hash, Clone)]
|
||||||
pub enum AtlasKey {
|
pub enum AtlasKey {
|
||||||
Glyph(RenderGlyphParams),
|
Glyph(RenderGlyphParams),
|
||||||
// Svg(RenderSvgParams),
|
Svg(RenderSvgParams),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<RenderGlyphParams> for AtlasKey {
|
impl From<RenderGlyphParams> for AtlasKey {
|
||||||
|
@ -187,6 +187,12 @@ impl From<RenderGlyphParams> for AtlasKey {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<RenderSvgParams> for AtlasKey {
|
||||||
|
fn from(params: RenderSvgParams) -> Self {
|
||||||
|
Self::Svg(params)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub trait PlatformAtlas: Send + Sync {
|
pub trait PlatformAtlas: Send + Sync {
|
||||||
fn get_or_insert_with(
|
fn get_or_insert_with(
|
||||||
&self,
|
&self,
|
||||||
|
|
|
@ -20,7 +20,7 @@ pub struct MetalRenderer {
|
||||||
sprites_pipeline_state: metal::RenderPipelineState,
|
sprites_pipeline_state: metal::RenderPipelineState,
|
||||||
unit_vertices: metal::Buffer,
|
unit_vertices: metal::Buffer,
|
||||||
instances: metal::Buffer,
|
instances: metal::Buffer,
|
||||||
glyph_atlas: Arc<MetalAtlas>,
|
monochrome_sprite_atlas: Arc<MetalAtlas>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MetalRenderer {
|
impl MetalRenderer {
|
||||||
|
@ -99,7 +99,7 @@ impl MetalRenderer {
|
||||||
);
|
);
|
||||||
|
|
||||||
let command_queue = device.new_command_queue();
|
let command_queue = device.new_command_queue();
|
||||||
let glyph_atlas = Arc::new(MetalAtlas::new(
|
let monochrome_sprite_atlas = Arc::new(MetalAtlas::new(
|
||||||
Size {
|
Size {
|
||||||
width: DevicePixels(1024),
|
width: DevicePixels(1024),
|
||||||
height: DevicePixels(768),
|
height: DevicePixels(768),
|
||||||
|
@ -115,7 +115,7 @@ impl MetalRenderer {
|
||||||
sprites_pipeline_state,
|
sprites_pipeline_state,
|
||||||
unit_vertices,
|
unit_vertices,
|
||||||
instances,
|
instances,
|
||||||
glyph_atlas,
|
monochrome_sprite_atlas,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,8 +123,8 @@ impl MetalRenderer {
|
||||||
&*self.layer
|
&*self.layer
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn glyph_atlas(&self) -> &Arc<MetalAtlas> {
|
pub fn monochrome_sprite_atlas(&self) -> &Arc<MetalAtlas> {
|
||||||
&self.glyph_atlas
|
&self.monochrome_sprite_atlas
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw(&mut self, scene: &mut Scene) {
|
pub fn draw(&mut self, scene: &mut Scene) {
|
||||||
|
@ -277,7 +277,7 @@ impl MetalRenderer {
|
||||||
}
|
}
|
||||||
align_offset(offset);
|
align_offset(offset);
|
||||||
|
|
||||||
let texture = self.glyph_atlas.texture(texture_id);
|
let texture = self.monochrome_sprite_atlas.texture(texture_id);
|
||||||
let texture_size = size(
|
let texture_size = size(
|
||||||
DevicePixels(texture.width() as i32),
|
DevicePixels(texture.width() as i32),
|
||||||
DevicePixels(texture.height() as i32),
|
DevicePixels(texture.height() as i32),
|
||||||
|
|
|
@ -886,8 +886,8 @@ impl PlatformWindow for MacWindow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn glyph_atlas(&self) -> Arc<dyn PlatformAtlas> {
|
fn monochrome_sprite_atlas(&self) -> Arc<dyn PlatformAtlas> {
|
||||||
self.0.lock().renderer.glyph_atlas().clone()
|
self.0.lock().renderer.monochrome_sprite_atlas().clone()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,102 +0,0 @@
|
||||||
use crate::{AssetSource, DevicePixels, ImageData, IsZero, Result, SharedString, Size};
|
|
||||||
use anyhow::anyhow;
|
|
||||||
use collections::HashMap;
|
|
||||||
use parking_lot::{RwLock, RwLockUpgradableReadGuard};
|
|
||||||
use std::hash::Hash;
|
|
||||||
use std::sync::Arc;
|
|
||||||
use usvg::Tree as SvgTree;
|
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Hash, Eq)]
|
|
||||||
pub struct SvgRenderParams {
|
|
||||||
path: SharedString,
|
|
||||||
size: Size<DevicePixels>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct SvgRenderer {
|
|
||||||
asset_source: Arc<dyn AssetSource>,
|
|
||||||
trees_by_path: RwLock<HashMap<SharedString, SvgTree>>,
|
|
||||||
rendered: RwLock<HashMap<SvgRenderParams, Arc<ImageData>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SvgRenderer {
|
|
||||||
pub fn render(&self, params: SvgRenderParams) -> Result<Arc<ImageData>> {
|
|
||||||
if params.size.is_zero() {
|
|
||||||
return Err(anyhow!("can't render at a zero size"));
|
|
||||||
}
|
|
||||||
|
|
||||||
let rendered = self.rendered.upgradable_read();
|
|
||||||
if let Some(image_data) = rendered.get(¶ms) {
|
|
||||||
Ok(image_data.clone())
|
|
||||||
} else {
|
|
||||||
// There's no rendered SVG for the path at the requested size.
|
|
||||||
// Have we already loaded a tree for the path?
|
|
||||||
let trees_by_path = self.trees_by_path.upgradable_read();
|
|
||||||
let tree = if let Some(tree) = trees_by_path.get(¶ms.path) {
|
|
||||||
tree.clone()
|
|
||||||
} else {
|
|
||||||
// Load the tree
|
|
||||||
let bytes = self.asset_source.load(¶ms.path)?;
|
|
||||||
let tree = usvg::Tree::from_data(&bytes, &usvg::Options::default())?;
|
|
||||||
let mut trees_by_path = RwLockUpgradableReadGuard::upgrade(trees_by_path);
|
|
||||||
trees_by_path.insert(params.path.clone(), tree.clone());
|
|
||||||
tree
|
|
||||||
};
|
|
||||||
|
|
||||||
// Render the SVG to a pixmap with the specified width and height.
|
|
||||||
// Convert the pixmap's pixels into an image data and cache it in `rendered`.
|
|
||||||
let mut pixmap =
|
|
||||||
tiny_skia::Pixmap::new(params.size.width.into(), params.size.height.into())
|
|
||||||
.unwrap();
|
|
||||||
resvg::render(
|
|
||||||
&tree,
|
|
||||||
usvg::FitTo::Width(params.size.width.into()),
|
|
||||||
pixmap.as_mut(),
|
|
||||||
);
|
|
||||||
let alpha_mask = pixmap
|
|
||||||
.pixels()
|
|
||||||
.iter()
|
|
||||||
.map(|p| p.alpha())
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
let mut rendered = RwLockUpgradableReadGuard::upgrade(rendered);
|
|
||||||
let image_data = Arc::new(ImageData::from_raw(params.size, alpha_mask));
|
|
||||||
rendered.insert(params, image_data.clone());
|
|
||||||
|
|
||||||
Ok(image_data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// impl SvgRenderer {
|
|
||||||
// pub fn render_svg(
|
|
||||||
// &mut self,
|
|
||||||
// size: Vector2I,
|
|
||||||
// path: Cow<'static, str>,
|
|
||||||
// svg: usvg::Tree,
|
|
||||||
// ) -> Option<IconSprite> {
|
|
||||||
// let mut pixmap = tiny_skia::Pixmap::new(size.x() as u32, size.y() as u32)?;
|
|
||||||
// resvg::render(&svg, usvg::FitTo::Width(size.x() as u32), pixmap.as_mut());
|
|
||||||
|
|
||||||
// let atlases = &mut self.atlases;
|
|
||||||
// match self.icons.entry(IconDescriptor {
|
|
||||||
// path,
|
|
||||||
// width: size.x(),
|
|
||||||
// height: size.y(),
|
|
||||||
// }) {
|
|
||||||
// Entry::Occupied(entry) => Some(entry.get().clone()),
|
|
||||||
// Entry::Vacant(entry) => {
|
|
||||||
// let mask = pixmap
|
|
||||||
// .pixels()
|
|
||||||
// .iter()
|
|
||||||
// .map(|a| a.alpha())
|
|
||||||
// .collect::<Vec<_>>();
|
|
||||||
// let (alloc_id, atlas_bounds) = atlases.upload(size, &mask)?;
|
|
||||||
// let icon_sprite = IconSprite {
|
|
||||||
// atlas_id: alloc_id.atlas_id,
|
|
||||||
// atlas_origin: atlas_bounds.origin(),
|
|
||||||
// size,
|
|
||||||
// };
|
|
||||||
// Some(entry.insert(icon_sprite).clone())
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
47
crates/gpui3/src/svg_renderer.rs
Normal file
47
crates/gpui3/src/svg_renderer.rs
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
use crate::{AssetSource, DevicePixels, IsZero, Result, SharedString, Size};
|
||||||
|
use anyhow::anyhow;
|
||||||
|
use std::hash::Hash;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq, Hash, Eq)]
|
||||||
|
pub struct RenderSvgParams {
|
||||||
|
pub(crate) path: SharedString,
|
||||||
|
pub(crate) size: Size<DevicePixels>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SvgRenderer {
|
||||||
|
asset_source: Arc<dyn AssetSource>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SvgRenderer {
|
||||||
|
pub fn new(asset_source: Arc<dyn AssetSource>) -> Self {
|
||||||
|
Self { asset_source }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render(&self, params: &RenderSvgParams) -> Result<Vec<u8>> {
|
||||||
|
if params.size.is_zero() {
|
||||||
|
return Err(anyhow!("can't render at a zero size"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load the tree.
|
||||||
|
let bytes = self.asset_source.load(¶ms.path)?;
|
||||||
|
let tree = usvg::Tree::from_data(&bytes, &usvg::Options::default())?;
|
||||||
|
|
||||||
|
// Render the SVG to a pixmap with the specified width and height.
|
||||||
|
let mut pixmap =
|
||||||
|
tiny_skia::Pixmap::new(params.size.width.into(), params.size.height.into()).unwrap();
|
||||||
|
resvg::render(
|
||||||
|
&tree,
|
||||||
|
usvg::FitTo::Width(params.size.width.into()),
|
||||||
|
pixmap.as_mut(),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Convert the pixmap's pixels into an alpha mask.
|
||||||
|
let alpha_mask = pixmap
|
||||||
|
.pixels()
|
||||||
|
.iter()
|
||||||
|
.map(|p| p.alpha())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
Ok(alpha_mask)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,9 +1,9 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
px, AnyView, AppContext, AvailableSpace, BorrowAppContext, Bounds, Context, Corners, Effect,
|
px, AnyView, AppContext, AvailableSpace, BorrowAppContext, Bounds, Context, Corners,
|
||||||
Element, EntityId, FontId, GlyphId, Handle, Hsla, IsZero, LayerId, LayoutId, MainThread,
|
DevicePixels, Effect, Element, EntityId, FontId, GlyphId, Handle, Hsla, IsZero, LayerId,
|
||||||
MainThreadOnly, MonochromeSprite, Pixels, PlatformAtlas, PlatformWindow, Point, Reference,
|
LayoutId, MainThread, MainThreadOnly, MonochromeSprite, Pixels, PlatformAtlas, PlatformWindow,
|
||||||
RenderGlyphParams, ScaledPixels, Scene, Size, Style, TaffyLayoutEngine, WeakHandle,
|
Point, Reference, RenderGlyphParams, RenderSvgParams, ScaledPixels, Scene, SharedString, Size,
|
||||||
WindowOptions, SUBPIXEL_VARIANTS,
|
Style, TaffyLayoutEngine, WeakHandle, WindowOptions, SUBPIXEL_VARIANTS,
|
||||||
};
|
};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use futures::Future;
|
use futures::Future;
|
||||||
|
@ -16,7 +16,7 @@ pub struct AnyWindow {}
|
||||||
pub struct Window {
|
pub struct Window {
|
||||||
handle: AnyWindowHandle,
|
handle: AnyWindowHandle,
|
||||||
platform_window: MainThreadOnly<Box<dyn PlatformWindow>>,
|
platform_window: MainThreadOnly<Box<dyn PlatformWindow>>,
|
||||||
glyph_atlas: Arc<dyn PlatformAtlas>,
|
monochrome_sprite_atlas: Arc<dyn PlatformAtlas>,
|
||||||
rem_size: Pixels,
|
rem_size: Pixels,
|
||||||
content_size: Size<Pixels>,
|
content_size: Size<Pixels>,
|
||||||
layout_engine: TaffyLayoutEngine,
|
layout_engine: TaffyLayoutEngine,
|
||||||
|
@ -35,7 +35,7 @@ impl Window {
|
||||||
cx: &mut MainThread<AppContext>,
|
cx: &mut MainThread<AppContext>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let platform_window = cx.platform().open_window(handle, options);
|
let platform_window = cx.platform().open_window(handle, options);
|
||||||
let glyph_atlas = platform_window.glyph_atlas();
|
let monochrome_sprite_atlas = platform_window.monochrome_sprite_atlas();
|
||||||
let mouse_position = platform_window.mouse_position();
|
let mouse_position = platform_window.mouse_position();
|
||||||
let content_size = platform_window.content_size();
|
let content_size = platform_window.content_size();
|
||||||
let scale_factor = platform_window.scale_factor();
|
let scale_factor = platform_window.scale_factor();
|
||||||
|
@ -58,7 +58,7 @@ impl Window {
|
||||||
Window {
|
Window {
|
||||||
handle,
|
handle,
|
||||||
platform_window,
|
platform_window,
|
||||||
glyph_atlas,
|
monochrome_sprite_atlas,
|
||||||
rem_size: px(16.),
|
rem_size: px(16.),
|
||||||
content_size,
|
content_size,
|
||||||
layout_engine: TaffyLayoutEngine::new(),
|
layout_engine: TaffyLayoutEngine::new(),
|
||||||
|
@ -235,7 +235,7 @@ impl<'a, 'w> WindowContext<'a, 'w> {
|
||||||
let layer_id = self.current_layer_id();
|
let layer_id = self.current_layer_id();
|
||||||
let tile = self
|
let tile = self
|
||||||
.window
|
.window
|
||||||
.glyph_atlas
|
.monochrome_sprite_atlas
|
||||||
.get_or_insert_with(¶ms.clone().into(), &mut || {
|
.get_or_insert_with(¶ms.clone().into(), &mut || {
|
||||||
self.text_system().rasterize_glyph(¶ms)
|
self.text_system().rasterize_glyph(¶ms)
|
||||||
})?;
|
})?;
|
||||||
|
@ -259,6 +259,47 @@ impl<'a, 'w> WindowContext<'a, 'w> {
|
||||||
Ok(())
|
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(
|
||||||
|
¶ms.clone().into(),
|
||||||
|
&mut || {
|
||||||
|
let bytes = self.svg_renderer.render(¶ms)?;
|
||||||
|
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<()> {
|
pub(crate) fn draw(&mut self) -> Result<()> {
|
||||||
let unit_entity = self.unit_entity.clone();
|
let unit_entity = self.unit_entity.clone();
|
||||||
self.update_entity(&unit_entity, |_, cx| {
|
self.update_entity(&unit_entity, |_, cx| {
|
||||||
|
|
30
crates/storybook2/src/assets.rs
Normal file
30
crates/storybook2/src/assets.rs
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
use std::borrow::Cow;
|
||||||
|
|
||||||
|
use anyhow::{anyhow, Result};
|
||||||
|
use gpui3::{AssetSource, SharedString};
|
||||||
|
use rust_embed::RustEmbed;
|
||||||
|
|
||||||
|
#[derive(RustEmbed)]
|
||||||
|
#[folder = "../../assets"]
|
||||||
|
#[include = "fonts/**/*"]
|
||||||
|
#[include = "icons/**/*"]
|
||||||
|
#[include = "themes/**/*"]
|
||||||
|
#[include = "sounds/**/*"]
|
||||||
|
#[include = "*.md"]
|
||||||
|
#[exclude = "*.DS_Store"]
|
||||||
|
pub struct Assets;
|
||||||
|
|
||||||
|
impl AssetSource for Assets {
|
||||||
|
fn load(&self, path: &SharedString) -> Result<Cow<[u8]>> {
|
||||||
|
Self::get(path.as_ref())
|
||||||
|
.map(|f| f.data)
|
||||||
|
.ok_or_else(|| anyhow!("could not find asset at path \"{}\"", path))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn list(&self, path: &SharedString) -> Result<Vec<SharedString>> {
|
||||||
|
Ok(Self::iter()
|
||||||
|
.filter(|p| p.starts_with(path.as_ref()))
|
||||||
|
.map(SharedString::from)
|
||||||
|
.collect())
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,9 +1,13 @@
|
||||||
#![allow(dead_code, unused_variables)]
|
#![allow(dead_code, unused_variables)]
|
||||||
|
|
||||||
|
use assets::Assets;
|
||||||
use gpui3::{px, size, Bounds, WindowBounds, WindowOptions};
|
use gpui3::{px, size, Bounds, WindowBounds, WindowOptions};
|
||||||
use log::LevelFilter;
|
use log::LevelFilter;
|
||||||
use simplelog::SimpleLogger;
|
use simplelog::SimpleLogger;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use workspace::workspace;
|
||||||
|
|
||||||
|
mod assets;
|
||||||
mod collab_panel;
|
mod collab_panel;
|
||||||
mod theme;
|
mod theme;
|
||||||
mod themes;
|
mod themes;
|
||||||
|
@ -19,7 +23,8 @@ fn main() {
|
||||||
|
|
||||||
SimpleLogger::init(LevelFilter::Info, Default::default()).expect("could not initialize logger");
|
SimpleLogger::init(LevelFilter::Info, Default::default()).expect("could not initialize logger");
|
||||||
|
|
||||||
gpui3::App::production().run(|cx| {
|
let asset_source = Arc::new(Assets);
|
||||||
|
gpui3::App::production(asset_source).run(|cx| {
|
||||||
let window = cx.open_window(
|
let window = cx.open_window(
|
||||||
WindowOptions {
|
WindowOptions {
|
||||||
bounds: WindowBounds::Fixed(Bounds {
|
bounds: WindowBounds::Fixed(Bounds {
|
||||||
|
@ -35,29 +40,6 @@ fn main() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
use rust_embed::RustEmbed;
|
|
||||||
use workspace::workspace;
|
|
||||||
|
|
||||||
#[derive(RustEmbed)]
|
|
||||||
#[folder = "../../assets"]
|
|
||||||
#[include = "themes/**/*"]
|
|
||||||
#[include = "fonts/**/*"]
|
|
||||||
#[include = "icons/**/*"]
|
|
||||||
#[exclude = "*.DS_Store"]
|
|
||||||
pub struct Assets;
|
|
||||||
|
|
||||||
// impl AssetSource for Assets {
|
|
||||||
// fn load(&self, path: &str) -> Result<std::borrow::Cow<[u8]>> {
|
|
||||||
// Self::get(path)
|
|
||||||
// .map(|f| f.data)
|
|
||||||
// .ok_or_else(|| anyhow!("could not find asset at path \"{}\"", path))
|
|
||||||
// }
|
|
||||||
|
|
||||||
// fn list(&self, path: &str) -> Vec<std::borrow::Cow<'static, str>> {
|
|
||||||
// Self::iter().filter(|p| p.starts_with(path)).collect()
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// fn load_embedded_fonts(platform: &dyn gpui2::Platform) {
|
// fn load_embedded_fonts(platform: &dyn gpui2::Platform) {
|
||||||
// let font_paths = Assets.list("fonts");
|
// let font_paths = Assets.list("fonts");
|
||||||
// let mut embedded_fonts = Vec::new();
|
// let mut embedded_fonts = Vec::new();
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use std::{
|
use std::{
|
||||||
|
borrow::Cow,
|
||||||
fmt::{self, Debug},
|
fmt::{self, Debug},
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
};
|
};
|
||||||
|
@ -47,6 +48,15 @@ impl From<String> for ArcCow<'_, str> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a> From<Cow<'a, str>> for ArcCow<'a, str> {
|
||||||
|
fn from(value: Cow<'a, str>) -> Self {
|
||||||
|
match value {
|
||||||
|
Cow::Borrowed(borrowed) => Self::Borrowed(borrowed),
|
||||||
|
Cow::Owned(owned) => Self::Owned(owned.into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a, T: ?Sized + ToOwned> std::borrow::Borrow<T> for ArcCow<'a, T> {
|
impl<'a, T: ?Sized + ToOwned> std::borrow::Borrow<T> for ArcCow<'a, T> {
|
||||||
fn borrow(&self) -> &T {
|
fn borrow(&self) -> &T {
|
||||||
match self {
|
match self {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue