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<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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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())
|
||||||
|
|
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