Checkpoint
This commit is contained in:
parent
1e0ff65337
commit
25a2554bdd
4 changed files with 196 additions and 12 deletions
60
crates/gpui3/src/assets.rs
Normal file
60
crates/gpui3/src/assets.rs
Normal 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()
|
||||
}
|
||||
}
|
|
@ -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 From<Size<Option<Pixels>>> for Size<Option<f32>> {
|
||||
fn from(val: Size<Option<Pixels>>) -> Self {
|
||||
fn from(size: Size<Option<Pixels>>) -> Self {
|
||||
Size {
|
||||
width: val.width.map(|p| p.0 as f32),
|
||||
height: val.height.map(|p| p.0 as f32),
|
||||
width: size.width.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 {
|
||||
fn from(val: f64) -> Self {
|
||||
Pixels(val as f32)
|
||||
fn from(pixels: f64) -> Self {
|
||||
Pixels(pixels as f32)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<f32> for Pixels {
|
||||
fn from(val: f32) -> Self {
|
||||
Pixels(val)
|
||||
fn from(pixels: f32) -> Self {
|
||||
Pixels(pixels)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -608,8 +608,20 @@ impl From<DevicePixels> for i32 {
|
|||
}
|
||||
|
||||
impl From<i32> for DevicePixels {
|
||||
fn from(val: i32) -> Self {
|
||||
DevicePixels(val)
|
||||
fn from(device_pixels: i32) -> Self {
|
||||
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 {
|
||||
fn from(val: u64) -> Self {
|
||||
DevicePixels(val as i32)
|
||||
fn from(device_pixels: u64) -> Self {
|
||||
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> {
|
||||
fn is_zero(&self) -> bool {
|
||||
self.width.is_zero() && self.height.is_zero()
|
||||
self.width.is_zero() || self.height.is_zero()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
mod app;
|
||||
mod assets;
|
||||
mod color;
|
||||
mod element;
|
||||
mod elements;
|
||||
|
@ -9,6 +10,7 @@ mod scene;
|
|||
mod style;
|
||||
mod style_helpers;
|
||||
mod styled;
|
||||
mod svg_library;
|
||||
mod taffy;
|
||||
mod text_system;
|
||||
mod util;
|
||||
|
@ -17,12 +19,14 @@ mod window;
|
|||
|
||||
pub use anyhow::Result;
|
||||
pub use app::*;
|
||||
pub use assets::*;
|
||||
pub use color::*;
|
||||
pub use element::*;
|
||||
pub use elements::*;
|
||||
pub use executor::*;
|
||||
pub use geometry::*;
|
||||
pub use gpui3_macros::*;
|
||||
pub use svg_library::*;
|
||||
|
||||
pub use platform::*;
|
||||
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 {
|
||||
fn from(value: T) -> Self {
|
||||
Self(value.into())
|
||||
|
|
102
crates/gpui3/src/svg_library.rs
Normal file
102
crates/gpui3/src/svg_library.rs
Normal 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(¶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())
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
Loading…
Add table
Add a link
Reference in a new issue