Checkpoint

This commit is contained in:
Nathan Sobo 2023-10-03 21:23:32 -06:00
parent 1e0ff65337
commit 25a2554bdd
4 changed files with 196 additions and 12 deletions

View file

@ -0,0 +1,60 @@
use crate::{size, DevicePixels, Result, SharedString, Size};
use anyhow::anyhow;
use image::{Bgra, ImageBuffer};
use std::{
borrow::Cow,
fmt,
sync::atomic::{AtomicUsize, Ordering::SeqCst},
};
pub trait AssetSource: 'static + Send + Sync {
fn load(&self, path: &SharedString) -> Result<Cow<[u8]>>;
fn list(&self, path: &SharedString) -> Result<Vec<SharedString>>;
}
impl AssetSource for () {
fn load(&self, path: &SharedString) -> Result<Cow<[u8]>> {
Err(anyhow!(
"get called on empty asset provider with \"{}\"",
path
))
}
fn list(&self, _path: &SharedString) -> Result<Vec<SharedString>> {
Ok(vec![])
}
}
pub struct ImageData {
pub id: usize,
data: ImageBuffer<Bgra<u8>, Vec<u8>>,
}
impl ImageData {
pub fn from_raw(size: Size<DevicePixels>, bytes: Vec<u8>) -> Self {
static NEXT_ID: AtomicUsize = AtomicUsize::new(0);
Self {
id: NEXT_ID.fetch_add(1, SeqCst),
data: ImageBuffer::from_raw(size.width.into(), size.height.into(), bytes).unwrap(),
}
}
pub fn as_bytes(&self) -> &[u8] {
&self.data
}
pub fn size(&self) -> Size<DevicePixels> {
let (width, height) = self.data.dimensions();
size(width.into(), height.into())
}
}
impl fmt::Debug for ImageData {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ImageData")
.field("id", &self.id)
.field("size", &self.data.dimensions())
.finish()
}
}

View file

@ -182,10 +182,10 @@ impl<T: Clone + Debug + Mul<S, Output = T>, S: Clone> MulAssign<S> for Size<T> {
impl<T: Eq + Debug + Clone> Eq for Size<T> {} impl<T: Eq + Debug + Clone> Eq for Size<T> {}
impl From<Size<Option<Pixels>>> for Size<Option<f32>> { impl From<Size<Option<Pixels>>> for Size<Option<f32>> {
fn from(val: Size<Option<Pixels>>) -> Self { fn from(size: Size<Option<Pixels>>) -> Self {
Size { Size {
width: val.width.map(|p| p.0 as f32), width: size.width.map(|p| p.0 as f32),
height: val.height.map(|p| p.0 as f32), height: size.height.map(|p| p.0 as f32),
} }
} }
} }
@ -548,14 +548,14 @@ impl std::hash::Hash for Pixels {
} }
impl From<f64> for Pixels { impl From<f64> for Pixels {
fn from(val: f64) -> Self { fn from(pixels: f64) -> Self {
Pixels(val as f32) Pixels(pixels as f32)
} }
} }
impl From<f32> for Pixels { impl From<f32> for Pixels {
fn from(val: f32) -> Self { fn from(pixels: f32) -> Self {
Pixels(val) Pixels(pixels)
} }
} }
@ -608,8 +608,20 @@ impl From<DevicePixels> for i32 {
} }
impl From<i32> for DevicePixels { impl From<i32> for DevicePixels {
fn from(val: i32) -> Self { fn from(device_pixels: i32) -> Self {
DevicePixels(val) DevicePixels(device_pixels)
}
}
impl From<u32> for DevicePixels {
fn from(device_pixels: u32) -> Self {
DevicePixels(device_pixels as i32)
}
}
impl From<DevicePixels> for u32 {
fn from(device_pixels: DevicePixels) -> Self {
device_pixels.0 as u32
} }
} }
@ -620,8 +632,8 @@ impl From<DevicePixels> for u64 {
} }
impl From<u64> for DevicePixels { impl From<u64> for DevicePixels {
fn from(val: u64) -> Self { fn from(device_pixels: u64) -> Self {
DevicePixels(val as i32) DevicePixels(device_pixels as i32)
} }
} }
@ -903,7 +915,7 @@ impl<T: IsZero + Debug + Clone> IsZero for Point<T> {
impl<T: IsZero + Debug + Clone> IsZero for Size<T> { impl<T: IsZero + Debug + Clone> IsZero for Size<T> {
fn is_zero(&self) -> bool { fn is_zero(&self) -> bool {
self.width.is_zero() && self.height.is_zero() self.width.is_zero() || self.height.is_zero()
} }
} }

View file

@ -1,4 +1,5 @@
mod app; mod app;
mod assets;
mod color; mod color;
mod element; mod element;
mod elements; mod elements;
@ -9,6 +10,7 @@ mod scene;
mod style; mod style;
mod style_helpers; mod style_helpers;
mod styled; mod styled;
mod svg_library;
mod taffy; mod taffy;
mod text_system; mod text_system;
mod util; mod util;
@ -17,12 +19,14 @@ mod window;
pub use anyhow::Result; pub use anyhow::Result;
pub use app::*; pub use app::*;
pub use assets::*;
pub use color::*; pub use color::*;
pub use element::*; pub use element::*;
pub use elements::*; 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 platform::*; pub use platform::*;
pub use refineable::*; pub use refineable::*;
@ -145,6 +149,12 @@ impl std::fmt::Debug for SharedString {
} }
} }
impl std::fmt::Display for SharedString {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0.as_ref())
}
}
impl<T: Into<ArcCow<'static, str>>> From<T> for SharedString { impl<T: Into<ArcCow<'static, str>>> From<T> for SharedString {
fn from(value: T) -> Self { fn from(value: T) -> Self {
Self(value.into()) Self(value.into())

View file

@ -0,0 +1,102 @@
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(&params) {
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(&params.path) {
tree.clone()
} else {
// Load the tree
let bytes = self.asset_source.load(&params.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())
// }
// }
// }
// }