Start on image rendering
This commit is contained in:
parent
201b923248
commit
96ade8668f
13 changed files with 510 additions and 75 deletions
65
Cargo.lock
generated
65
Cargo.lock
generated
|
@ -814,7 +814,7 @@ dependencies = [
|
||||||
"error-chain",
|
"error-chain",
|
||||||
"glob 0.2.11",
|
"glob 0.2.11",
|
||||||
"icns",
|
"icns",
|
||||||
"image",
|
"image 0.12.4",
|
||||||
"libflate",
|
"libflate",
|
||||||
"md5",
|
"md5",
|
||||||
"msi",
|
"msi",
|
||||||
|
@ -2102,6 +2102,16 @@ dependencies = [
|
||||||
"lzw",
|
"lzw",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gif"
|
||||||
|
version = "0.11.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5a668f699973d0f573d15749b7002a9ac9e1f9c6b220e7b165601334c173d8de"
|
||||||
|
dependencies = [
|
||||||
|
"color_quant",
|
||||||
|
"weezl",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gimli"
|
name = "gimli"
|
||||||
version = "0.23.0"
|
version = "0.23.0"
|
||||||
|
@ -2167,6 +2177,7 @@ dependencies = [
|
||||||
"font-kit",
|
"font-kit",
|
||||||
"foreign-types",
|
"foreign-types",
|
||||||
"gpui_macros",
|
"gpui_macros",
|
||||||
|
"image 0.23.14",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"log",
|
"log",
|
||||||
"metal",
|
"metal",
|
||||||
|
@ -2462,15 +2473,34 @@ checksum = "d95816db758249fe16f23a4e23f1a3a817fe11892dbfd1c5836f625324702158"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"enum_primitive",
|
"enum_primitive",
|
||||||
"gif",
|
"gif 0.9.2",
|
||||||
"jpeg-decoder",
|
"jpeg-decoder",
|
||||||
"num-iter",
|
"num-iter",
|
||||||
"num-rational",
|
"num-rational 0.1.42",
|
||||||
"num-traits 0.1.43",
|
"num-traits 0.1.43",
|
||||||
"png 0.6.2",
|
"png 0.6.2",
|
||||||
"scoped_threadpool",
|
"scoped_threadpool",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "image"
|
||||||
|
version = "0.23.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "24ffcb7e7244a9bf19d35bf2883b9c080c4ced3c07a9895572178cdb8f13f6a1"
|
||||||
|
dependencies = [
|
||||||
|
"bytemuck",
|
||||||
|
"byteorder",
|
||||||
|
"color_quant",
|
||||||
|
"gif 0.11.2",
|
||||||
|
"jpeg-decoder",
|
||||||
|
"num-iter",
|
||||||
|
"num-rational 0.3.2",
|
||||||
|
"num-traits 0.2.14",
|
||||||
|
"png 0.16.8",
|
||||||
|
"scoped_threadpool",
|
||||||
|
"tiff",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "indexmap"
|
name = "indexmap"
|
||||||
version = "1.6.2"
|
version = "1.6.2"
|
||||||
|
@ -3014,6 +3044,17 @@ dependencies = [
|
||||||
"num-traits 0.2.14",
|
"num-traits 0.2.14",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-rational"
|
||||||
|
version = "0.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg 1.0.1",
|
||||||
|
"num-integer",
|
||||||
|
"num-traits 0.2.14",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-traits"
|
name = "num-traits"
|
||||||
version = "0.1.43"
|
version = "0.1.43"
|
||||||
|
@ -5129,6 +5170,17 @@ dependencies = [
|
||||||
"tide",
|
"tide",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tiff"
|
||||||
|
version = "0.6.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9a53f4706d65497df0c4349241deddf35f84cee19c87ed86ea8ca590f4464437"
|
||||||
|
dependencies = [
|
||||||
|
"jpeg-decoder",
|
||||||
|
"miniz_oxide 0.4.4",
|
||||||
|
"weezl",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "time"
|
name = "time"
|
||||||
version = "0.1.44"
|
version = "0.1.44"
|
||||||
|
@ -5694,6 +5746,12 @@ dependencies = [
|
||||||
"webpki",
|
"webpki",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "weezl"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d8b77fdfd5a253be4ab714e4ffa3c49caf146b4de743e97510c0656cf90f1e8e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wepoll-sys"
|
name = "wepoll-sys"
|
||||||
version = "3.0.1"
|
version = "3.0.1"
|
||||||
|
@ -5836,6 +5894,7 @@ dependencies = [
|
||||||
"gpui",
|
"gpui",
|
||||||
"http-auth-basic",
|
"http-auth-basic",
|
||||||
"ignore",
|
"ignore",
|
||||||
|
"image 0.23.14",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
|
|
|
@ -11,6 +11,7 @@ backtrace = "0.3"
|
||||||
ctor = "0.1"
|
ctor = "0.1"
|
||||||
etagere = "0.2"
|
etagere = "0.2"
|
||||||
gpui_macros = { path = "../gpui_macros" }
|
gpui_macros = { path = "../gpui_macros" }
|
||||||
|
image = "0.23"
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
num_cpus = "1.13"
|
num_cpus = "1.13"
|
||||||
|
|
|
@ -6,6 +6,7 @@ mod empty;
|
||||||
mod event_handler;
|
mod event_handler;
|
||||||
mod flex;
|
mod flex;
|
||||||
mod hook;
|
mod hook;
|
||||||
|
mod image;
|
||||||
mod label;
|
mod label;
|
||||||
mod line_box;
|
mod line_box;
|
||||||
mod list;
|
mod list;
|
||||||
|
@ -16,25 +17,12 @@ mod svg;
|
||||||
mod text;
|
mod text;
|
||||||
mod uniform_list;
|
mod uniform_list;
|
||||||
|
|
||||||
|
pub use self::{
|
||||||
|
align::*, canvas::*, constrained_box::*, container::*, empty::*, event_handler::*, flex::*,
|
||||||
|
hook::*, image::*, label::*, line_box::*, list::*, mouse_event_handler::*, overlay::*,
|
||||||
|
stack::*, svg::*, text::*, uniform_list::*,
|
||||||
|
};
|
||||||
pub use crate::presenter::ChildView;
|
pub use crate::presenter::ChildView;
|
||||||
pub use align::*;
|
|
||||||
pub use canvas::*;
|
|
||||||
pub use constrained_box::*;
|
|
||||||
pub use container::*;
|
|
||||||
pub use empty::*;
|
|
||||||
pub use event_handler::*;
|
|
||||||
pub use flex::*;
|
|
||||||
pub use hook::*;
|
|
||||||
pub use label::*;
|
|
||||||
pub use line_box::*;
|
|
||||||
pub use list::*;
|
|
||||||
pub use mouse_event_handler::*;
|
|
||||||
pub use overlay::*;
|
|
||||||
pub use stack::*;
|
|
||||||
pub use svg::*;
|
|
||||||
pub use text::*;
|
|
||||||
pub use uniform_list::*;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
geometry::{rect::RectF, vector::Vector2F},
|
geometry::{rect::RectF, vector::Vector2F},
|
||||||
json, DebugContext, Event, EventContext, LayoutContext, PaintContext, SizeConstraint,
|
json, DebugContext, Event, EventContext, LayoutContext, PaintContext, SizeConstraint,
|
||||||
|
|
65
gpui/src/elements/image.rs
Normal file
65
gpui/src/elements/image.rs
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
use crate::{
|
||||||
|
geometry::{rect::RectF, vector::Vector2F},
|
||||||
|
json::{json, ToJson},
|
||||||
|
scene, DebugContext, Element, Event, EventContext, ImageData, LayoutContext, PaintContext,
|
||||||
|
SizeConstraint,
|
||||||
|
};
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
pub struct Image(Arc<ImageData>);
|
||||||
|
|
||||||
|
impl Image {
|
||||||
|
pub fn new(data: Arc<ImageData>) -> Self {
|
||||||
|
Self(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Element for Image {
|
||||||
|
type LayoutState = ();
|
||||||
|
type PaintState = ();
|
||||||
|
|
||||||
|
fn layout(
|
||||||
|
&mut self,
|
||||||
|
constraint: SizeConstraint,
|
||||||
|
_: &mut LayoutContext,
|
||||||
|
) -> (Vector2F, Self::LayoutState) {
|
||||||
|
(constraint.max, ())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn paint(
|
||||||
|
&mut self,
|
||||||
|
bounds: RectF,
|
||||||
|
_: RectF,
|
||||||
|
_: &mut Self::LayoutState,
|
||||||
|
cx: &mut PaintContext,
|
||||||
|
) -> Self::PaintState {
|
||||||
|
cx.scene.push_image(scene::Image {
|
||||||
|
bounds,
|
||||||
|
data: self.0.clone(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dispatch_event(
|
||||||
|
&mut self,
|
||||||
|
_: &Event,
|
||||||
|
_: RectF,
|
||||||
|
_: &mut Self::LayoutState,
|
||||||
|
_: &mut Self::PaintState,
|
||||||
|
_: &mut EventContext,
|
||||||
|
) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn debug(
|
||||||
|
&self,
|
||||||
|
bounds: RectF,
|
||||||
|
_: &Self::LayoutState,
|
||||||
|
_: &Self::PaintState,
|
||||||
|
_: &DebugContext,
|
||||||
|
) -> serde_json::Value {
|
||||||
|
json!({
|
||||||
|
"type": "Image",
|
||||||
|
"bounds": bounds.to_json(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
31
gpui/src/image_data.rs
Normal file
31
gpui/src/image_data.rs
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
use crate::geometry::vector::{vec2i, Vector2I};
|
||||||
|
use image::{Bgra, ImageBuffer};
|
||||||
|
use std::sync::{
|
||||||
|
atomic::{AtomicUsize, Ordering::SeqCst},
|
||||||
|
Arc,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct ImageData {
|
||||||
|
pub id: usize,
|
||||||
|
data: ImageBuffer<Bgra<u8>, Vec<u8>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ImageData {
|
||||||
|
pub fn new(data: ImageBuffer<Bgra<u8>, Vec<u8>>) -> Arc<Self> {
|
||||||
|
static NEXT_ID: AtomicUsize = AtomicUsize::new(0);
|
||||||
|
|
||||||
|
Arc::new(Self {
|
||||||
|
id: NEXT_ID.fetch_add(1, SeqCst),
|
||||||
|
data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_bytes(&self) -> &[u8] {
|
||||||
|
&self.data
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn size(&self) -> Vector2I {
|
||||||
|
let (width, height) = self.data.dimensions();
|
||||||
|
vec2i(width as i32, height as i32)
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,6 +7,8 @@ mod test;
|
||||||
pub use assets::*;
|
pub use assets::*;
|
||||||
pub mod elements;
|
pub mod elements;
|
||||||
pub mod font_cache;
|
pub mod font_cache;
|
||||||
|
mod image_data;
|
||||||
|
pub use crate::image_data::ImageData;
|
||||||
pub mod views;
|
pub mod views;
|
||||||
pub use font_cache::FontCache;
|
pub use font_cache::FontCache;
|
||||||
mod clipboard;
|
mod clipboard;
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
use crate::geometry::vector::{vec2i, Vector2I};
|
use crate::geometry::{
|
||||||
|
rect::RectI,
|
||||||
|
vector::{vec2i, Vector2I},
|
||||||
|
};
|
||||||
use etagere::BucketedAtlasAllocator;
|
use etagere::BucketedAtlasAllocator;
|
||||||
use foreign_types::ForeignType;
|
use foreign_types::ForeignType;
|
||||||
use metal::{self, Device, TextureDescriptor};
|
use metal::{self, Device, TextureDescriptor};
|
||||||
|
@ -11,6 +14,12 @@ pub struct AtlasAllocator {
|
||||||
free_atlases: Vec<Atlas>,
|
free_atlases: Vec<Atlas>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub struct AllocId {
|
||||||
|
pub atlas_id: usize,
|
||||||
|
alloc_id: etagere::AllocId,
|
||||||
|
}
|
||||||
|
|
||||||
impl AtlasAllocator {
|
impl AtlasAllocator {
|
||||||
pub fn new(device: Device, texture_descriptor: TextureDescriptor) -> Self {
|
pub fn new(device: Device, texture_descriptor: TextureDescriptor) -> Self {
|
||||||
let mut me = Self {
|
let mut me = Self {
|
||||||
|
@ -31,20 +40,40 @@ impl AtlasAllocator {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn allocate(&mut self, requested_size: Vector2I) -> anyhow::Result<(usize, Vector2I)> {
|
pub fn allocate(&mut self, requested_size: Vector2I) -> (AllocId, Vector2I) {
|
||||||
let origin = self
|
let (alloc_id, origin) = self
|
||||||
.atlases
|
.atlases
|
||||||
.last_mut()
|
.last_mut()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.allocate(requested_size)
|
.allocate(requested_size)
|
||||||
.unwrap_or_else(|| {
|
.unwrap_or_else(|| {
|
||||||
let mut atlas = self.new_atlas(requested_size);
|
let mut atlas = self.new_atlas(requested_size);
|
||||||
let origin = atlas.allocate(requested_size).unwrap();
|
let (id, origin) = atlas.allocate(requested_size).unwrap();
|
||||||
self.atlases.push(atlas);
|
self.atlases.push(atlas);
|
||||||
origin
|
(id, origin)
|
||||||
});
|
});
|
||||||
|
|
||||||
Ok((self.atlases.len() - 1, origin))
|
let id = AllocId {
|
||||||
|
atlas_id: self.atlases.len() - 1,
|
||||||
|
alloc_id,
|
||||||
|
};
|
||||||
|
(id, origin)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn upload(&mut self, size: Vector2I, bytes: &[u8]) -> (AllocId, RectI) {
|
||||||
|
let (alloc_id, origin) = self.allocate(size);
|
||||||
|
let bounds = RectI::new(origin, size);
|
||||||
|
self.atlases[alloc_id.atlas_id].upload(bounds, bytes);
|
||||||
|
(alloc_id, bounds)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deallocate(&mut self, id: AllocId) {
|
||||||
|
if let Some(atlas) = self.atlases.get_mut(id.atlas_id) {
|
||||||
|
atlas.deallocate(id.alloc_id);
|
||||||
|
if atlas.is_empty() {
|
||||||
|
self.free_atlases.push(self.atlases.remove(id.atlas_id));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clear(&mut self) {
|
pub fn clear(&mut self) {
|
||||||
|
@ -102,13 +131,44 @@ impl Atlas {
|
||||||
vec2i(size.width, size.height)
|
vec2i(size.width, size.height)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn allocate(&mut self, size: Vector2I) -> Option<Vector2I> {
|
fn allocate(&mut self, size: Vector2I) -> Option<(etagere::AllocId, Vector2I)> {
|
||||||
let origin = self
|
let alloc = self
|
||||||
.allocator
|
.allocator
|
||||||
.allocate(etagere::Size::new(size.x(), size.y()))?
|
.allocate(etagere::Size::new(size.x(), size.y()))?;
|
||||||
.rectangle
|
let origin = alloc.rectangle.min;
|
||||||
.min;
|
Some((alloc.id, vec2i(origin.x, origin.y)))
|
||||||
Some(vec2i(origin.x, origin.y))
|
}
|
||||||
|
|
||||||
|
fn upload(&mut self, bounds: RectI, bytes: &[u8]) {
|
||||||
|
let region = metal::MTLRegion::new_2d(
|
||||||
|
bounds.origin().x() as u64,
|
||||||
|
bounds.origin().y() as u64,
|
||||||
|
bounds.size().x() as u64,
|
||||||
|
bounds.size().y() as u64,
|
||||||
|
);
|
||||||
|
self.texture.replace_region(
|
||||||
|
region,
|
||||||
|
0,
|
||||||
|
bytes.as_ptr() as *const _,
|
||||||
|
(bounds.size().x() * self.bytes_per_pixel() as i32) as u64,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bytes_per_pixel(&self) -> u8 {
|
||||||
|
use metal::MTLPixelFormat::*;
|
||||||
|
match self.texture.pixel_format() {
|
||||||
|
A8Unorm | R8Unorm => 1,
|
||||||
|
RGBA8Unorm | BGRA8Unorm => 4,
|
||||||
|
_ => unimplemented!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deallocate(&mut self, id: etagere::AllocId) {
|
||||||
|
self.allocator.deallocate(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_empty(&self) -> bool {
|
||||||
|
self.allocator.is_empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clear(&mut self) {
|
fn clear(&mut self) {
|
||||||
|
|
|
@ -1,13 +1,15 @@
|
||||||
use super::{atlas::AtlasAllocator, sprite_cache::SpriteCache};
|
use super::{
|
||||||
|
atlas::{self, AtlasAllocator},
|
||||||
|
sprite_cache::SpriteCache,
|
||||||
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
color::Color,
|
color::Color,
|
||||||
geometry::{
|
geometry::{
|
||||||
rect::RectF,
|
rect::{RectF, RectI},
|
||||||
vector::{vec2f, vec2i, Vector2F},
|
vector::{vec2f, vec2i, Vector2F},
|
||||||
},
|
},
|
||||||
platform,
|
platform,
|
||||||
scene::{Glyph, Icon, Layer, Quad, Shadow},
|
scene::{Glyph, Icon, Image, Layer, Quad, Scene, Shadow},
|
||||||
Scene,
|
|
||||||
};
|
};
|
||||||
use cocoa::foundation::NSUInteger;
|
use cocoa::foundation::NSUInteger;
|
||||||
use metal::{MTLPixelFormat, MTLResourceOptions, NSRange};
|
use metal::{MTLPixelFormat, MTLResourceOptions, NSRange};
|
||||||
|
@ -21,9 +23,13 @@ const INSTANCE_BUFFER_SIZE: usize = 1024 * 1024; // This is an arbitrary decisio
|
||||||
pub struct Renderer {
|
pub struct Renderer {
|
||||||
sprite_cache: SpriteCache,
|
sprite_cache: SpriteCache,
|
||||||
path_atlases: AtlasAllocator,
|
path_atlases: AtlasAllocator,
|
||||||
|
image_atlases: AtlasAllocator,
|
||||||
|
prev_rendered_images: HashMap<usize, (atlas::AllocId, RectI)>,
|
||||||
|
curr_rendered_images: HashMap<usize, (atlas::AllocId, RectI)>,
|
||||||
quad_pipeline_state: metal::RenderPipelineState,
|
quad_pipeline_state: metal::RenderPipelineState,
|
||||||
shadow_pipeline_state: metal::RenderPipelineState,
|
shadow_pipeline_state: metal::RenderPipelineState,
|
||||||
sprite_pipeline_state: metal::RenderPipelineState,
|
sprite_pipeline_state: metal::RenderPipelineState,
|
||||||
|
image_pipeline_state: metal::RenderPipelineState,
|
||||||
path_atlas_pipeline_state: metal::RenderPipelineState,
|
path_atlas_pipeline_state: metal::RenderPipelineState,
|
||||||
unit_vertices: metal::Buffer,
|
unit_vertices: metal::Buffer,
|
||||||
instances: metal::Buffer,
|
instances: metal::Buffer,
|
||||||
|
@ -64,7 +70,10 @@ impl Renderer {
|
||||||
);
|
);
|
||||||
|
|
||||||
let sprite_cache = SpriteCache::new(device.clone(), vec2i(1024, 768), fonts);
|
let sprite_cache = SpriteCache::new(device.clone(), vec2i(1024, 768), fonts);
|
||||||
let path_atlases = build_path_atlas_allocator(MTLPixelFormat::R8Unorm, &device);
|
let path_atlases =
|
||||||
|
AtlasAllocator::new(device.clone(), build_path_atlas_texture_descriptor());
|
||||||
|
let image_atlases =
|
||||||
|
AtlasAllocator::new(device.clone(), build_image_atlas_texture_descriptor());
|
||||||
let quad_pipeline_state = build_pipeline_state(
|
let quad_pipeline_state = build_pipeline_state(
|
||||||
&device,
|
&device,
|
||||||
&library,
|
&library,
|
||||||
|
@ -89,6 +98,14 @@ impl Renderer {
|
||||||
"sprite_fragment",
|
"sprite_fragment",
|
||||||
pixel_format,
|
pixel_format,
|
||||||
);
|
);
|
||||||
|
let image_pipeline_state = build_pipeline_state(
|
||||||
|
&device,
|
||||||
|
&library,
|
||||||
|
"image",
|
||||||
|
"image_vertex",
|
||||||
|
"image_fragment",
|
||||||
|
pixel_format,
|
||||||
|
);
|
||||||
let path_atlas_pipeline_state = build_path_atlas_pipeline_state(
|
let path_atlas_pipeline_state = build_path_atlas_pipeline_state(
|
||||||
&device,
|
&device,
|
||||||
&library,
|
&library,
|
||||||
|
@ -100,9 +117,13 @@ impl Renderer {
|
||||||
Self {
|
Self {
|
||||||
sprite_cache,
|
sprite_cache,
|
||||||
path_atlases,
|
path_atlases,
|
||||||
|
image_atlases,
|
||||||
|
prev_rendered_images: Default::default(),
|
||||||
|
curr_rendered_images: Default::default(),
|
||||||
quad_pipeline_state,
|
quad_pipeline_state,
|
||||||
shadow_pipeline_state,
|
shadow_pipeline_state,
|
||||||
sprite_pipeline_state,
|
sprite_pipeline_state,
|
||||||
|
image_pipeline_state,
|
||||||
path_atlas_pipeline_state,
|
path_atlas_pipeline_state,
|
||||||
unit_vertices,
|
unit_vertices,
|
||||||
instances,
|
instances,
|
||||||
|
@ -117,6 +138,12 @@ impl Renderer {
|
||||||
output: &metal::TextureRef,
|
output: &metal::TextureRef,
|
||||||
) {
|
) {
|
||||||
let mut offset = 0;
|
let mut offset = 0;
|
||||||
|
|
||||||
|
mem::swap(
|
||||||
|
&mut self.curr_rendered_images,
|
||||||
|
&mut self.prev_rendered_images,
|
||||||
|
);
|
||||||
|
|
||||||
let path_sprites = self.render_path_atlases(scene, &mut offset, command_buffer);
|
let path_sprites = self.render_path_atlases(scene, &mut offset, command_buffer);
|
||||||
self.render_layers(
|
self.render_layers(
|
||||||
scene,
|
scene,
|
||||||
|
@ -130,6 +157,11 @@ impl Renderer {
|
||||||
location: 0,
|
location: 0,
|
||||||
length: offset as NSUInteger,
|
length: offset as NSUInteger,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
for (id, _) in self.prev_rendered_images.values() {
|
||||||
|
self.image_atlases.deallocate(*id);
|
||||||
|
}
|
||||||
|
self.prev_rendered_images.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_path_atlases(
|
fn render_path_atlases(
|
||||||
|
@ -146,11 +178,11 @@ impl Renderer {
|
||||||
for path in layer.paths() {
|
for path in layer.paths() {
|
||||||
let origin = path.bounds.origin() * scene.scale_factor();
|
let origin = path.bounds.origin() * scene.scale_factor();
|
||||||
let size = (path.bounds.size() * scene.scale_factor()).ceil();
|
let size = (path.bounds.size() * scene.scale_factor()).ceil();
|
||||||
let (atlas_id, atlas_origin) = self.path_atlases.allocate(size.to_i32()).unwrap();
|
let (alloc_id, atlas_origin) = self.path_atlases.allocate(size.to_i32());
|
||||||
let atlas_origin = atlas_origin.to_f32();
|
let atlas_origin = atlas_origin.to_f32();
|
||||||
sprites.push(PathSprite {
|
sprites.push(PathSprite {
|
||||||
layer_id,
|
layer_id,
|
||||||
atlas_id,
|
atlas_id: alloc_id.atlas_id,
|
||||||
shader_data: shaders::GPUISprite {
|
shader_data: shaders::GPUISprite {
|
||||||
origin: origin.floor().to_float2(),
|
origin: origin.floor().to_float2(),
|
||||||
target_size: size.to_float2(),
|
target_size: size.to_float2(),
|
||||||
|
@ -162,7 +194,7 @@ impl Renderer {
|
||||||
});
|
});
|
||||||
|
|
||||||
if let Some(current_atlas_id) = current_atlas_id {
|
if let Some(current_atlas_id) = current_atlas_id {
|
||||||
if atlas_id != current_atlas_id {
|
if alloc_id.atlas_id != current_atlas_id {
|
||||||
self.render_paths_to_atlas(
|
self.render_paths_to_atlas(
|
||||||
offset,
|
offset,
|
||||||
&vertices,
|
&vertices,
|
||||||
|
@ -173,7 +205,7 @@ impl Renderer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
current_atlas_id = Some(atlas_id);
|
current_atlas_id = Some(alloc_id.atlas_id);
|
||||||
|
|
||||||
for vertex in &path.vertices {
|
for vertex in &path.vertices {
|
||||||
let xy_position =
|
let xy_position =
|
||||||
|
@ -316,6 +348,13 @@ impl Renderer {
|
||||||
drawable_size,
|
drawable_size,
|
||||||
command_encoder,
|
command_encoder,
|
||||||
);
|
);
|
||||||
|
self.render_images(
|
||||||
|
layer.images(),
|
||||||
|
scale_factor,
|
||||||
|
offset,
|
||||||
|
drawable_size,
|
||||||
|
command_encoder,
|
||||||
|
);
|
||||||
self.render_quads(
|
self.render_quads(
|
||||||
layer.underlines(),
|
layer.underlines(),
|
||||||
scale_factor,
|
scale_factor,
|
||||||
|
@ -602,6 +641,97 @@ impl Renderer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn render_images(
|
||||||
|
&mut self,
|
||||||
|
images: &[Image],
|
||||||
|
scale_factor: f32,
|
||||||
|
offset: &mut usize,
|
||||||
|
drawable_size: Vector2F,
|
||||||
|
command_encoder: &metal::RenderCommandEncoderRef,
|
||||||
|
) {
|
||||||
|
if images.is_empty() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut images_by_atlas = HashMap::new();
|
||||||
|
for image in images {
|
||||||
|
let origin = image.bounds.origin() * scale_factor;
|
||||||
|
let target_size = image.bounds.size() * scale_factor;
|
||||||
|
let (alloc_id, atlas_bounds) = self
|
||||||
|
.prev_rendered_images
|
||||||
|
.remove(&image.data.id)
|
||||||
|
.or_else(|| self.curr_rendered_images.get(&image.data.id).copied())
|
||||||
|
.unwrap_or_else(|| {
|
||||||
|
self.image_atlases
|
||||||
|
.upload(image.data.size(), image.data.as_bytes())
|
||||||
|
});
|
||||||
|
self.curr_rendered_images
|
||||||
|
.insert(image.data.id, (alloc_id, atlas_bounds));
|
||||||
|
images_by_atlas
|
||||||
|
.entry(alloc_id.atlas_id)
|
||||||
|
.or_insert_with(Vec::new)
|
||||||
|
.push(shaders::GPUIImage {
|
||||||
|
origin: origin.to_float2(),
|
||||||
|
target_size: target_size.to_float2(),
|
||||||
|
source_size: atlas_bounds.size().to_float2(),
|
||||||
|
atlas_origin: atlas_bounds.origin().to_float2(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
command_encoder.set_render_pipeline_state(&self.image_pipeline_state);
|
||||||
|
command_encoder.set_vertex_buffer(
|
||||||
|
shaders::GPUIImageVertexInputIndex_GPUIImageVertexInputIndexVertices as u64,
|
||||||
|
Some(&self.unit_vertices),
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
command_encoder.set_vertex_bytes(
|
||||||
|
shaders::GPUIImageVertexInputIndex_GPUIImageVertexInputIndexViewportSize as u64,
|
||||||
|
mem::size_of::<shaders::vector_float2>() as u64,
|
||||||
|
[drawable_size.to_float2()].as_ptr() as *const c_void,
|
||||||
|
);
|
||||||
|
|
||||||
|
for (atlas_id, images) in images_by_atlas {
|
||||||
|
align_offset(offset);
|
||||||
|
let next_offset = *offset + images.len() * mem::size_of::<shaders::GPUIImage>();
|
||||||
|
assert!(
|
||||||
|
next_offset <= INSTANCE_BUFFER_SIZE,
|
||||||
|
"instance buffer exhausted"
|
||||||
|
);
|
||||||
|
|
||||||
|
let texture = self.image_atlases.texture(atlas_id).unwrap();
|
||||||
|
command_encoder.set_vertex_buffer(
|
||||||
|
shaders::GPUIImageVertexInputIndex_GPUIImageVertexInputIndexImages as u64,
|
||||||
|
Some(&self.instances),
|
||||||
|
*offset as u64,
|
||||||
|
);
|
||||||
|
command_encoder.set_vertex_bytes(
|
||||||
|
shaders::GPUIImageVertexInputIndex_GPUIImageVertexInputIndexAtlasSize as u64,
|
||||||
|
mem::size_of::<shaders::vector_float2>() as u64,
|
||||||
|
[vec2i(texture.width() as i32, texture.height() as i32).to_float2()].as_ptr()
|
||||||
|
as *const c_void,
|
||||||
|
);
|
||||||
|
command_encoder.set_fragment_texture(
|
||||||
|
shaders::GPUIImageFragmentInputIndex_GPUIImageFragmentInputIndexAtlas as u64,
|
||||||
|
Some(texture),
|
||||||
|
);
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let buffer_contents = (self.instances.contents() as *mut u8)
|
||||||
|
.offset(*offset as isize)
|
||||||
|
as *mut shaders::GPUIImage;
|
||||||
|
std::ptr::copy_nonoverlapping(images.as_ptr(), buffer_contents, images.len());
|
||||||
|
}
|
||||||
|
|
||||||
|
command_encoder.draw_primitives_instanced(
|
||||||
|
metal::MTLPrimitiveType::Triangle,
|
||||||
|
0,
|
||||||
|
6,
|
||||||
|
images.len() as u64,
|
||||||
|
);
|
||||||
|
*offset = next_offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn render_path_sprites(
|
fn render_path_sprites(
|
||||||
&mut self,
|
&mut self,
|
||||||
layer_id: usize,
|
layer_id: usize,
|
||||||
|
@ -708,19 +838,23 @@ impl Renderer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_path_atlas_allocator(
|
fn build_path_atlas_texture_descriptor() -> metal::TextureDescriptor {
|
||||||
pixel_format: MTLPixelFormat,
|
|
||||||
device: &metal::Device,
|
|
||||||
) -> AtlasAllocator {
|
|
||||||
let texture_descriptor = metal::TextureDescriptor::new();
|
let texture_descriptor = metal::TextureDescriptor::new();
|
||||||
texture_descriptor.set_width(2048);
|
texture_descriptor.set_width(2048);
|
||||||
texture_descriptor.set_height(2048);
|
texture_descriptor.set_height(2048);
|
||||||
texture_descriptor.set_pixel_format(pixel_format);
|
texture_descriptor.set_pixel_format(MTLPixelFormat::R8Unorm);
|
||||||
texture_descriptor
|
texture_descriptor
|
||||||
.set_usage(metal::MTLTextureUsage::RenderTarget | metal::MTLTextureUsage::ShaderRead);
|
.set_usage(metal::MTLTextureUsage::RenderTarget | metal::MTLTextureUsage::ShaderRead);
|
||||||
texture_descriptor.set_storage_mode(metal::MTLStorageMode::Private);
|
texture_descriptor.set_storage_mode(metal::MTLStorageMode::Private);
|
||||||
let path_atlases = AtlasAllocator::new(device.clone(), texture_descriptor);
|
texture_descriptor
|
||||||
path_atlases
|
}
|
||||||
|
|
||||||
|
fn build_image_atlas_texture_descriptor() -> metal::TextureDescriptor {
|
||||||
|
let texture_descriptor = metal::TextureDescriptor::new();
|
||||||
|
texture_descriptor.set_width(2048);
|
||||||
|
texture_descriptor.set_height(2048);
|
||||||
|
texture_descriptor.set_pixel_format(MTLPixelFormat::BGRA8Unorm);
|
||||||
|
texture_descriptor
|
||||||
}
|
}
|
||||||
|
|
||||||
fn align_offset(offset: &mut usize) {
|
fn align_offset(offset: &mut usize) {
|
||||||
|
@ -803,9 +937,10 @@ mod shaders {
|
||||||
#![allow(non_camel_case_types)]
|
#![allow(non_camel_case_types)]
|
||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
|
|
||||||
use pathfinder_geometry::vector::Vector2I;
|
use crate::{
|
||||||
|
color::Color,
|
||||||
use crate::{color::Color, geometry::vector::Vector2F};
|
geometry::vector::{Vector2F, Vector2I},
|
||||||
|
};
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
include!(concat!(env!("OUT_DIR"), "/shaders.rs"));
|
include!(concat!(env!("OUT_DIR"), "/shaders.rs"));
|
||||||
|
|
|
@ -1,16 +1,19 @@
|
||||||
#include <simd/simd.h>
|
#include <simd/simd.h>
|
||||||
|
|
||||||
typedef struct {
|
typedef struct
|
||||||
|
{
|
||||||
vector_float2 viewport_size;
|
vector_float2 viewport_size;
|
||||||
} GPUIUniforms;
|
} GPUIUniforms;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum
|
||||||
|
{
|
||||||
GPUIQuadInputIndexVertices = 0,
|
GPUIQuadInputIndexVertices = 0,
|
||||||
GPUIQuadInputIndexQuads = 1,
|
GPUIQuadInputIndexQuads = 1,
|
||||||
GPUIQuadInputIndexUniforms = 2,
|
GPUIQuadInputIndexUniforms = 2,
|
||||||
} GPUIQuadInputIndex;
|
} GPUIQuadInputIndex;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct
|
||||||
|
{
|
||||||
vector_float2 origin;
|
vector_float2 origin;
|
||||||
vector_float2 size;
|
vector_float2 size;
|
||||||
vector_uchar4 background_color;
|
vector_uchar4 background_color;
|
||||||
|
@ -22,13 +25,15 @@ typedef struct {
|
||||||
float corner_radius;
|
float corner_radius;
|
||||||
} GPUIQuad;
|
} GPUIQuad;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum
|
||||||
|
{
|
||||||
GPUIShadowInputIndexVertices = 0,
|
GPUIShadowInputIndexVertices = 0,
|
||||||
GPUIShadowInputIndexShadows = 1,
|
GPUIShadowInputIndexShadows = 1,
|
||||||
GPUIShadowInputIndexUniforms = 2,
|
GPUIShadowInputIndexUniforms = 2,
|
||||||
} GPUIShadowInputIndex;
|
} GPUIShadowInputIndex;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct
|
||||||
|
{
|
||||||
vector_float2 origin;
|
vector_float2 origin;
|
||||||
vector_float2 size;
|
vector_float2 size;
|
||||||
float corner_radius;
|
float corner_radius;
|
||||||
|
@ -36,18 +41,21 @@ typedef struct {
|
||||||
vector_uchar4 color;
|
vector_uchar4 color;
|
||||||
} GPUIShadow;
|
} GPUIShadow;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum
|
||||||
|
{
|
||||||
GPUISpriteVertexInputIndexVertices = 0,
|
GPUISpriteVertexInputIndexVertices = 0,
|
||||||
GPUISpriteVertexInputIndexSprites = 1,
|
GPUISpriteVertexInputIndexSprites = 1,
|
||||||
GPUISpriteVertexInputIndexViewportSize = 2,
|
GPUISpriteVertexInputIndexViewportSize = 2,
|
||||||
GPUISpriteVertexInputIndexAtlasSize = 3,
|
GPUISpriteVertexInputIndexAtlasSize = 3,
|
||||||
} GPUISpriteVertexInputIndex;
|
} GPUISpriteVertexInputIndex;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum
|
||||||
|
{
|
||||||
GPUISpriteFragmentInputIndexAtlas = 0,
|
GPUISpriteFragmentInputIndexAtlas = 0,
|
||||||
} GPUISpriteFragmentInputIndex;
|
} GPUISpriteFragmentInputIndex;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct
|
||||||
|
{
|
||||||
vector_float2 origin;
|
vector_float2 origin;
|
||||||
vector_float2 target_size;
|
vector_float2 target_size;
|
||||||
vector_float2 source_size;
|
vector_float2 source_size;
|
||||||
|
@ -56,14 +64,37 @@ typedef struct {
|
||||||
uint8_t compute_winding;
|
uint8_t compute_winding;
|
||||||
} GPUISprite;
|
} GPUISprite;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum
|
||||||
|
{
|
||||||
GPUIPathAtlasVertexInputIndexVertices = 0,
|
GPUIPathAtlasVertexInputIndexVertices = 0,
|
||||||
GPUIPathAtlasVertexInputIndexAtlasSize = 1,
|
GPUIPathAtlasVertexInputIndexAtlasSize = 1,
|
||||||
} GPUIPathAtlasVertexInputIndex;
|
} GPUIPathAtlasVertexInputIndex;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct
|
||||||
|
{
|
||||||
vector_float2 xy_position;
|
vector_float2 xy_position;
|
||||||
vector_float2 st_position;
|
vector_float2 st_position;
|
||||||
vector_float2 clip_rect_origin;
|
vector_float2 clip_rect_origin;
|
||||||
vector_float2 clip_rect_size;
|
vector_float2 clip_rect_size;
|
||||||
} GPUIPathVertex;
|
} GPUIPathVertex;
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
GPUIImageVertexInputIndexVertices = 0,
|
||||||
|
GPUIImageVertexInputIndexImages = 1,
|
||||||
|
GPUIImageVertexInputIndexViewportSize = 2,
|
||||||
|
GPUIImageVertexInputIndexAtlasSize = 3,
|
||||||
|
} GPUIImageVertexInputIndex;
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
GPUIImageFragmentInputIndexAtlas = 0,
|
||||||
|
} GPUIImageFragmentInputIndex;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
vector_float2 origin;
|
||||||
|
vector_float2 target_size;
|
||||||
|
vector_float2 source_size;
|
||||||
|
vector_float2 atlas_origin;
|
||||||
|
} GPUIImage;
|
||||||
|
|
|
@ -217,6 +217,39 @@ fragment float4 sprite_fragment(
|
||||||
return color;
|
return color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct ImageFragmentInput {
|
||||||
|
float4 position [[position]];
|
||||||
|
float2 atlas_position;
|
||||||
|
};
|
||||||
|
|
||||||
|
vertex ImageFragmentInput image_vertex(
|
||||||
|
uint unit_vertex_id [[vertex_id]],
|
||||||
|
uint image_id [[instance_id]],
|
||||||
|
constant float2 *unit_vertices [[buffer(GPUIImageVertexInputIndexVertices)]],
|
||||||
|
constant GPUIImage *images [[buffer(GPUIImageVertexInputIndexImages)]],
|
||||||
|
constant float2 *viewport_size [[buffer(GPUIImageVertexInputIndexViewportSize)]],
|
||||||
|
constant float2 *atlas_size [[buffer(GPUIImageVertexInputIndexAtlasSize)]]
|
||||||
|
) {
|
||||||
|
float2 unit_vertex = unit_vertices[unit_vertex_id];
|
||||||
|
GPUIImage image = images[image_id];
|
||||||
|
float2 position = unit_vertex * image.target_size + image.origin;
|
||||||
|
float4 device_position = to_device_position(position, *viewport_size);
|
||||||
|
float2 atlas_position = (unit_vertex * image.source_size + image.atlas_origin) / *atlas_size;
|
||||||
|
|
||||||
|
return ImageFragmentInput {
|
||||||
|
device_position,
|
||||||
|
atlas_position,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fragment float4 image_fragment(
|
||||||
|
ImageFragmentInput input [[stage_in]],
|
||||||
|
texture2d<float> atlas [[ texture(GPUIImageFragmentInputIndexAtlas) ]]
|
||||||
|
) {
|
||||||
|
constexpr sampler atlas_sampler(mag_filter::linear, min_filter::linear);
|
||||||
|
return atlas.sample(atlas_sampler, input.atlas_position);
|
||||||
|
}
|
||||||
|
|
||||||
struct PathAtlasVertexOutput {
|
struct PathAtlasVertexOutput {
|
||||||
float4 position [[position]];
|
float4 position [[position]];
|
||||||
float2 st_position;
|
float2 st_position;
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use std::borrow::Cow;
|
use std::{borrow::Cow, sync::Arc};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
color::Color,
|
color::Color,
|
||||||
fonts::{FontId, GlyphId},
|
fonts::{FontId, GlyphId},
|
||||||
geometry::{rect::RectF, vector::Vector2F},
|
geometry::{rect::RectF, vector::Vector2F},
|
||||||
json::ToJson,
|
json::ToJson,
|
||||||
|
ImageData,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct Scene {
|
pub struct Scene {
|
||||||
|
@ -25,6 +26,7 @@ pub struct Layer {
|
||||||
clip_bounds: Option<RectF>,
|
clip_bounds: Option<RectF>,
|
||||||
quads: Vec<Quad>,
|
quads: Vec<Quad>,
|
||||||
underlines: Vec<Quad>,
|
underlines: Vec<Quad>,
|
||||||
|
images: Vec<Image>,
|
||||||
shadows: Vec<Shadow>,
|
shadows: Vec<Shadow>,
|
||||||
glyphs: Vec<Glyph>,
|
glyphs: Vec<Glyph>,
|
||||||
icons: Vec<Icon>,
|
icons: Vec<Icon>,
|
||||||
|
@ -124,6 +126,11 @@ pub struct PathVertex {
|
||||||
pub st_position: Vector2F,
|
pub st_position: Vector2F,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct Image {
|
||||||
|
pub bounds: RectF,
|
||||||
|
pub data: Arc<ImageData>,
|
||||||
|
}
|
||||||
|
|
||||||
impl Scene {
|
impl Scene {
|
||||||
pub fn new(scale_factor: f32) -> Self {
|
pub fn new(scale_factor: f32) -> Self {
|
||||||
let stacking_context = StackingContext::new(None);
|
let stacking_context = StackingContext::new(None);
|
||||||
|
@ -166,6 +173,10 @@ impl Scene {
|
||||||
self.active_layer().push_quad(quad)
|
self.active_layer().push_quad(quad)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn push_image(&mut self, image: Image) {
|
||||||
|
self.active_layer().push_image(image)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn push_underline(&mut self, underline: Quad) {
|
pub fn push_underline(&mut self, underline: Quad) {
|
||||||
self.active_layer().push_underline(underline)
|
self.active_layer().push_underline(underline)
|
||||||
}
|
}
|
||||||
|
@ -240,6 +251,7 @@ impl Layer {
|
||||||
clip_bounds,
|
clip_bounds,
|
||||||
quads: Vec::new(),
|
quads: Vec::new(),
|
||||||
underlines: Vec::new(),
|
underlines: Vec::new(),
|
||||||
|
images: Vec::new(),
|
||||||
shadows: Vec::new(),
|
shadows: Vec::new(),
|
||||||
glyphs: Vec::new(),
|
glyphs: Vec::new(),
|
||||||
icons: Vec::new(),
|
icons: Vec::new(),
|
||||||
|
@ -267,6 +279,14 @@ impl Layer {
|
||||||
self.underlines.as_slice()
|
self.underlines.as_slice()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn push_image(&mut self, image: Image) {
|
||||||
|
self.images.push(image);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn images(&self) -> &[Image] {
|
||||||
|
self.images.as_slice()
|
||||||
|
}
|
||||||
|
|
||||||
fn push_shadow(&mut self, shadow: Shadow) {
|
fn push_shadow(&mut self, shadow: Shadow) {
|
||||||
self.shadows.push(shadow);
|
self.shadows.push(shadow);
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,7 @@ futures = "0.3"
|
||||||
gpui = { path = "../gpui" }
|
gpui = { path = "../gpui" }
|
||||||
http-auth-basic = "0.1.3"
|
http-auth-basic = "0.1.3"
|
||||||
ignore = "0.4"
|
ignore = "0.4"
|
||||||
|
image = "0.23"
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
libc = "0.2"
|
libc = "0.2"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
|
|
|
@ -21,7 +21,7 @@ use gpui::{
|
||||||
json::to_string_pretty,
|
json::to_string_pretty,
|
||||||
keymap::Binding,
|
keymap::Binding,
|
||||||
platform::WindowOptions,
|
platform::WindowOptions,
|
||||||
AnyViewHandle, AppContext, ClipboardItem, Entity, ModelHandle, MutableAppContext,
|
AnyViewHandle, AppContext, ClipboardItem, Entity, ImageData, ModelHandle, MutableAppContext,
|
||||||
PathPromptOptions, PromptLevel, RenderContext, Task, View, ViewContext, ViewHandle,
|
PathPromptOptions, PromptLevel, RenderContext, Task, View, ViewContext, ViewHandle,
|
||||||
WeakModelHandle,
|
WeakModelHandle,
|
||||||
};
|
};
|
||||||
|
@ -354,10 +354,19 @@ pub struct Workspace {
|
||||||
(usize, Arc<Path>),
|
(usize, Arc<Path>),
|
||||||
postage::watch::Receiver<Option<Result<Box<dyn ItemHandle>, Arc<anyhow::Error>>>>,
|
postage::watch::Receiver<Option<Result<Box<dyn ItemHandle>, Arc<anyhow::Error>>>>,
|
||||||
>,
|
>,
|
||||||
|
image: Arc<ImageData>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Workspace {
|
impl Workspace {
|
||||||
pub fn new(app_state: &AppState, cx: &mut ViewContext<Self>) -> Self {
|
pub fn new(app_state: &AppState, cx: &mut ViewContext<Self>) -> Self {
|
||||||
|
let image_bytes = crate::assets::Assets::get("images/as-cii.jpeg").unwrap();
|
||||||
|
let image = image::io::Reader::new(std::io::Cursor::new(&*image_bytes.data))
|
||||||
|
.with_guessed_format()
|
||||||
|
.unwrap()
|
||||||
|
.decode()
|
||||||
|
.unwrap()
|
||||||
|
.into_bgra8();
|
||||||
|
|
||||||
let pane = cx.add_view(|_| Pane::new(app_state.settings.clone()));
|
let pane = cx.add_view(|_| Pane::new(app_state.settings.clone()));
|
||||||
let pane_id = pane.id();
|
let pane_id = pane.id();
|
||||||
cx.subscribe(&pane, move |me, _, event, cx| {
|
cx.subscribe(&pane, move |me, _, event, cx| {
|
||||||
|
@ -401,6 +410,7 @@ impl Workspace {
|
||||||
worktrees: Default::default(),
|
worktrees: Default::default(),
|
||||||
items: Default::default(),
|
items: Default::default(),
|
||||||
loading_items: Default::default(),
|
loading_items: Default::default(),
|
||||||
|
image: ImageData::new(image),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -954,17 +964,16 @@ impl View for Workspace {
|
||||||
Flex::column()
|
Flex::column()
|
||||||
.with_child(
|
.with_child(
|
||||||
ConstrainedBox::new(
|
ConstrainedBox::new(
|
||||||
Container::new(
|
Image::new(self.image.clone()).boxed()
|
||||||
Align::new(
|
// Container::new(
|
||||||
Label::new(
|
// Align::new(
|
||||||
"zed".into(),
|
// Label::new("zed".into(), theme.workspace.titlebar.label.clone())
|
||||||
theme.workspace.titlebar.label.clone()
|
// .boxed(),
|
||||||
).boxed()
|
// )
|
||||||
)
|
// .boxed(),
|
||||||
.boxed()
|
// )
|
||||||
)
|
// .with_style(&theme.workspace.titlebar.container)
|
||||||
.with_style(&theme.workspace.titlebar.container)
|
// .boxed(),
|
||||||
.boxed(),
|
|
||||||
)
|
)
|
||||||
.with_height(32.)
|
.with_height(32.)
|
||||||
.named("titlebar"),
|
.named("titlebar"),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue