From 2c7c42ee93a61f39562590f6a75eb2dd8b220fb8 Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Mon, 31 Oct 2022 13:37:56 -0700 Subject: Move image/svg handling into `iced_graphics` The `TextureStore` trait is implemented by the atlas, and can also be implemented in the glow renderer or in a software renderer. The API here may be improved in the future, but API stability is presumably not a huge issue since these types will only be used by renderer backends. --- graphics/src/image.rs | 34 +++++++ graphics/src/image/raster.rs | 236 +++++++++++++++++++++++++++++++++++++++++++ graphics/src/image/vector.rs | 183 +++++++++++++++++++++++++++++++++ graphics/src/lib.rs | 1 + 4 files changed, 454 insertions(+) create mode 100644 graphics/src/image.rs create mode 100644 graphics/src/image/raster.rs create mode 100644 graphics/src/image/vector.rs (limited to 'graphics/src') diff --git a/graphics/src/image.rs b/graphics/src/image.rs new file mode 100644 index 00000000..f1153882 --- /dev/null +++ b/graphics/src/image.rs @@ -0,0 +1,34 @@ +//! Image loading and caching + +use std::fmt::Debug; + +#[cfg(feature = "image_rs")] +pub mod raster; + +#[cfg(feature = "svg")] +pub mod vector; + +/// Entry in the texture store +pub trait TextureStoreEntry: Debug { + /// Width and height of the entry + fn size(&self) -> (u32, u32); +} + +/// Stores cached image data for use in rendering +pub trait TextureStore { + /// Entry in the texture store + type Entry: TextureStoreEntry; + /// State passed to upload/remove + type State<'a>; + + /// Upload image data + fn upload( + &mut self, + width: u32, + height: u32, + data: &[u8], + state: &mut Self::State<'_>, + ) -> Option; + /// Remome image from store + fn remove(&mut self, entry: &Self::Entry, state: &mut Self::State<'_>); +} diff --git a/graphics/src/image/raster.rs b/graphics/src/image/raster.rs new file mode 100644 index 00000000..f9672dd5 --- /dev/null +++ b/graphics/src/image/raster.rs @@ -0,0 +1,236 @@ +//! Raster image loading and caching + +use iced_native::image; +use std::collections::{HashMap, HashSet}; + +use bitflags::bitflags; + +use super::{TextureStore, TextureStoreEntry}; + +/// Entry in cache corresponding to an image handle +#[derive(Debug)] +pub enum Memory { + /// Image data on host + Host(::image_rs::ImageBuffer<::image_rs::Rgba, Vec>), + /// Texture store entry + Device(T::Entry), + /// Image not found + NotFound, + /// Invalid image data + Invalid, +} + +impl Memory { + /// Width and height of image + pub fn dimensions(&self) -> (u32, u32) { + match self { + Memory::Host(image) => image.dimensions(), + Memory::Device(entry) => entry.size(), + Memory::NotFound => (1, 1), + Memory::Invalid => (1, 1), + } + } +} + +/// Caches image raster data +#[derive(Debug)] +pub struct Cache { + map: HashMap>, + hits: HashSet, +} + +impl Cache { + /// Load image + pub fn load(&mut self, handle: &image::Handle) -> &mut Memory { + if self.contains(handle) { + return self.get(handle).unwrap(); + } + + let memory = match handle.data() { + image::Data::Path(path) => { + if let Ok(image) = image_rs::open(path) { + let operation = std::fs::File::open(path) + .ok() + .map(std::io::BufReader::new) + .and_then(|mut reader| { + Operation::from_exif(&mut reader).ok() + }) + .unwrap_or_else(Operation::empty); + + Memory::Host(operation.perform(image.to_rgba8())) + } else { + Memory::NotFound + } + } + image::Data::Bytes(bytes) => { + if let Ok(image) = image_rs::load_from_memory(bytes) { + let operation = + Operation::from_exif(&mut std::io::Cursor::new(bytes)) + .ok() + .unwrap_or_else(Operation::empty); + + Memory::Host(operation.perform(image.to_rgba8())) + } else { + Memory::Invalid + } + } + image::Data::Pixels { + width, + height, + pixels, + } => { + if let Some(image) = image_rs::ImageBuffer::from_vec( + *width, + *height, + pixels.to_vec(), + ) { + Memory::Host(image) + } else { + Memory::Invalid + } + } + }; + + self.insert(handle, memory); + self.get(handle).unwrap() + } + + /// Load image and upload raster data + pub fn upload( + &mut self, + handle: &image::Handle, + state: &mut T::State<'_>, + store: &mut T, + ) -> Option<&T::Entry> { + let memory = self.load(handle); + + if let Memory::Host(image) = memory { + let (width, height) = image.dimensions(); + + let entry = store.upload(width, height, image, state)?; + + *memory = Memory::Device(entry); + } + + if let Memory::Device(allocation) = memory { + Some(allocation) + } else { + None + } + } + + /// Trim cache misses from cache + pub fn trim(&mut self, store: &mut T, state: &mut T::State<'_>) { + let hits = &self.hits; + + self.map.retain(|k, memory| { + let retain = hits.contains(k); + + if !retain { + if let Memory::Device(entry) = memory { + store.remove(entry, state); + } + } + + retain + }); + + self.hits.clear(); + } + + fn get(&mut self, handle: &image::Handle) -> Option<&mut Memory> { + let _ = self.hits.insert(handle.id()); + + self.map.get_mut(&handle.id()) + } + + fn insert(&mut self, handle: &image::Handle, memory: Memory) { + let _ = self.map.insert(handle.id(), memory); + } + + fn contains(&self, handle: &image::Handle) -> bool { + self.map.contains_key(&handle.id()) + } +} + +impl Default for Cache { + fn default() -> Self { + Self { + map: HashMap::new(), + hits: HashSet::new(), + } + } +} + +bitflags! { + struct Operation: u8 { + const FLIP_HORIZONTALLY = 0b001; + const ROTATE_180 = 0b010; + const FLIP_DIAGONALLY = 0b100; + } +} + +impl Operation { + // Meaning of the returned value is described e.g. at: + // https://magnushoff.com/articles/jpeg-orientation/ + fn from_exif(reader: &mut R) -> Result + where + R: std::io::BufRead + std::io::Seek, + { + let exif = exif::Reader::new().read_from_container(reader)?; + + Ok(exif + .get_field(exif::Tag::Orientation, exif::In::PRIMARY) + .and_then(|field| field.value.get_uint(0)) + .and_then(|value| u8::try_from(value).ok()) + .and_then(|value| Self::from_bits(value.saturating_sub(1))) + .unwrap_or_else(Self::empty)) + } + + fn perform

( + self, + image: image_rs::ImageBuffer>, + ) -> image_rs::ImageBuffer> + where + P: image_rs::Pixel + 'static, + { + use image_rs::imageops; + + let mut image = if self.contains(Self::FLIP_DIAGONALLY) { + flip_diagonally(image) + } else { + image + }; + + if self.contains(Self::ROTATE_180) { + imageops::rotate180_in_place(&mut image); + } + + if self.contains(Self::FLIP_HORIZONTALLY) { + imageops::flip_horizontal_in_place(&mut image); + } + + image + } +} + +fn flip_diagonally( + image: I, +) -> image_rs::ImageBuffer::Subpixel>> +where + I: image_rs::GenericImage, + I::Pixel: 'static, +{ + let (width, height) = image.dimensions(); + let mut out = image_rs::ImageBuffer::new(height, width); + + for x in 0..width { + for y in 0..height { + let p = image.get_pixel(x, y); + + out.put_pixel(y, x, p); + } + } + + out +} diff --git a/graphics/src/image/vector.rs b/graphics/src/image/vector.rs new file mode 100644 index 00000000..0062e2ce --- /dev/null +++ b/graphics/src/image/vector.rs @@ -0,0 +1,183 @@ +//! Vector image loading and caching + +use iced_native::svg; + +use std::collections::{HashMap, HashSet}; +use std::fs; + +use super::TextureStore; + +/// Entry in cache corresponding to an svg handle +pub enum Svg { + /// Parsed svg + Loaded(usvg::Tree), + /// Svg not found or failed to parse + NotFound, +} + +impl Svg { + /// Viewport width and height + pub fn viewport_dimensions(&self) -> (u32, u32) { + match self { + Svg::Loaded(tree) => { + let size = tree.svg_node().size; + + (size.width() as u32, size.height() as u32) + } + Svg::NotFound => (1, 1), + } + } +} + +/// Caches svg vector and raster data +#[derive(Debug)] +pub struct Cache { + svgs: HashMap, + rasterized: HashMap<(u64, u32, u32), T::Entry>, + svg_hits: HashSet, + rasterized_hits: HashSet<(u64, u32, u32)>, +} + +impl Cache { + /// Load svg + pub fn load(&mut self, handle: &svg::Handle) -> &Svg { + if self.svgs.contains_key(&handle.id()) { + return self.svgs.get(&handle.id()).unwrap(); + } + + let svg = match handle.data() { + svg::Data::Path(path) => { + let tree = fs::read_to_string(path).ok().and_then(|contents| { + usvg::Tree::from_str( + &contents, + &usvg::Options::default().to_ref(), + ) + .ok() + }); + + tree.map(Svg::Loaded).unwrap_or(Svg::NotFound) + } + svg::Data::Bytes(bytes) => { + match usvg::Tree::from_data( + bytes, + &usvg::Options::default().to_ref(), + ) { + Ok(tree) => Svg::Loaded(tree), + Err(_) => Svg::NotFound, + } + } + }; + + let _ = self.svgs.insert(handle.id(), svg); + self.svgs.get(&handle.id()).unwrap() + } + + /// Load svg and upload raster data + pub fn upload( + &mut self, + handle: &svg::Handle, + [width, height]: [f32; 2], + scale: f32, + state: &mut T::State<'_>, + texture_store: &mut T, + ) -> Option<&T::Entry> { + let id = handle.id(); + + let (width, height) = ( + (scale * width).ceil() as u32, + (scale * height).ceil() as u32, + ); + + // TODO: Optimize! + // We currently rerasterize the SVG when its size changes. This is slow + // as heck. A GPU rasterizer like `pathfinder` may perform better. + // It would be cool to be able to smooth resize the `svg` example. + if self.rasterized.contains_key(&(id, width, height)) { + let _ = self.svg_hits.insert(id); + let _ = self.rasterized_hits.insert((id, width, height)); + + return self.rasterized.get(&(id, width, height)); + } + + match self.load(handle) { + Svg::Loaded(tree) => { + if width == 0 || height == 0 { + return None; + } + + // TODO: Optimize! + // We currently rerasterize the SVG when its size changes. This is slow + // as heck. A GPU rasterizer like `pathfinder` may perform better. + // It would be cool to be able to smooth resize the `svg` example. + let mut img = tiny_skia::Pixmap::new(width, height)?; + + resvg::render( + tree, + if width > height { + usvg::FitTo::Width(width) + } else { + usvg::FitTo::Height(height) + }, + img.as_mut(), + )?; + + let mut rgba = img.take(); + rgba.chunks_exact_mut(4).for_each(|rgba| rgba.swap(0, 2)); + + let allocation = texture_store.upload( + width, + height, + bytemuck::cast_slice(rgba.as_slice()), + state, + )?; + log::debug!("allocating {} {}x{}", id, width, height); + + let _ = self.svg_hits.insert(id); + let _ = self.rasterized_hits.insert((id, width, height)); + let _ = self.rasterized.insert((id, width, height), allocation); + + self.rasterized.get(&(id, width, height)) + } + Svg::NotFound => None, + } + } + + /// Load svg and upload raster data + pub fn trim(&mut self, texture_store: &mut T, state: &mut T::State<'_>) { + let svg_hits = &self.svg_hits; + let rasterized_hits = &self.rasterized_hits; + + self.svgs.retain(|k, _| svg_hits.contains(k)); + self.rasterized.retain(|k, entry| { + let retain = rasterized_hits.contains(k); + + if !retain { + texture_store.remove(entry, state); + } + + retain + }); + self.svg_hits.clear(); + self.rasterized_hits.clear(); + } +} + +impl Default for Cache { + fn default() -> Self { + Self { + svgs: HashMap::new(), + rasterized: HashMap::new(), + svg_hits: HashSet::new(), + rasterized_hits: HashSet::new(), + } + } +} + +impl std::fmt::Debug for Svg { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Svg::Loaded(_) => write!(f, "Svg::Loaded"), + Svg::NotFound => write!(f, "Svg::NotFound"), + } + } +} diff --git a/graphics/src/lib.rs b/graphics/src/lib.rs index ec28ee58..d39dd90c 100644 --- a/graphics/src/lib.rs +++ b/graphics/src/lib.rs @@ -30,6 +30,7 @@ mod viewport; pub mod backend; pub mod font; pub mod gradient; +pub mod image; pub mod layer; pub mod overlay; pub mod renderer; -- cgit From 8ce8d374b1e8d1d394a42a5ee2bca8af790f0b71 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sat, 5 Nov 2022 03:13:04 +0100 Subject: Refactor some `image` traits a bit - Use `Size` were applicable. - Rename `TextureStore` to `image::Storage`. - Rename `TextureStoreEntry` to `image::storage::Entry`. - Wire up `viewport_dimensions` to `iced_glow` for `Svg`. --- graphics/src/backend.rs | 4 ++-- graphics/src/image.rs | 30 +++--------------------------- graphics/src/image/raster.rs | 32 +++++++++++++++++++------------- graphics/src/image/storage.rs | 31 +++++++++++++++++++++++++++++++ graphics/src/image/vector.rs | 16 ++++++++-------- graphics/src/renderer.rs | 4 ++-- 6 files changed, 65 insertions(+), 52 deletions(-) create mode 100644 graphics/src/image/storage.rs (limited to 'graphics/src') diff --git a/graphics/src/backend.rs b/graphics/src/backend.rs index 7e0af2cc..2f8e9fc3 100644 --- a/graphics/src/backend.rs +++ b/graphics/src/backend.rs @@ -66,11 +66,11 @@ pub trait Text { /// A graphics backend that supports image rendering. pub trait Image { /// Returns the dimensions of the provided image. - fn dimensions(&self, handle: &image::Handle) -> (u32, u32); + fn dimensions(&self, handle: &image::Handle) -> Size; } /// A graphics backend that supports SVG rendering. pub trait Svg { /// Returns the viewport dimensions of the provided SVG. - fn viewport_dimensions(&self, handle: &svg::Handle) -> (u32, u32); + fn viewport_dimensions(&self, handle: &svg::Handle) -> Size; } diff --git a/graphics/src/image.rs b/graphics/src/image.rs index f1153882..04f4ff9d 100644 --- a/graphics/src/image.rs +++ b/graphics/src/image.rs @@ -1,34 +1,10 @@ -//! Image loading and caching - -use std::fmt::Debug; - +//! Render images. #[cfg(feature = "image_rs")] pub mod raster; #[cfg(feature = "svg")] pub mod vector; -/// Entry in the texture store -pub trait TextureStoreEntry: Debug { - /// Width and height of the entry - fn size(&self) -> (u32, u32); -} - -/// Stores cached image data for use in rendering -pub trait TextureStore { - /// Entry in the texture store - type Entry: TextureStoreEntry; - /// State passed to upload/remove - type State<'a>; +pub mod storage; - /// Upload image data - fn upload( - &mut self, - width: u32, - height: u32, - data: &[u8], - state: &mut Self::State<'_>, - ) -> Option; - /// Remome image from store - fn remove(&mut self, entry: &Self::Entry, state: &mut Self::State<'_>); -} +pub use storage::Storage; diff --git a/graphics/src/image/raster.rs b/graphics/src/image/raster.rs index f9672dd5..8ed9615d 100644 --- a/graphics/src/image/raster.rs +++ b/graphics/src/image/raster.rs @@ -1,15 +1,15 @@ -//! Raster image loading and caching +//! Raster image loading and caching. +use crate::image::Storage; +use crate::Size; use iced_native::image; -use std::collections::{HashMap, HashSet}; use bitflags::bitflags; - -use super::{TextureStore, TextureStoreEntry}; +use std::collections::{HashMap, HashSet}; /// Entry in cache corresponding to an image handle #[derive(Debug)] -pub enum Memory { +pub enum Memory { /// Image data on host Host(::image_rs::ImageBuffer<::image_rs::Rgba, Vec>), /// Texture store entry @@ -20,26 +20,32 @@ pub enum Memory { Invalid, } -impl Memory { +impl Memory { /// Width and height of image - pub fn dimensions(&self) -> (u32, u32) { + pub fn dimensions(&self) -> Size { + use crate::image::storage::Entry; + match self { - Memory::Host(image) => image.dimensions(), + Memory::Host(image) => { + let (width, height) = image.dimensions(); + + Size::new(width, height) + } Memory::Device(entry) => entry.size(), - Memory::NotFound => (1, 1), - Memory::Invalid => (1, 1), + Memory::NotFound => Size::new(1, 1), + Memory::Invalid => Size::new(1, 1), } } } /// Caches image raster data #[derive(Debug)] -pub struct Cache { +pub struct Cache { map: HashMap>, hits: HashSet, } -impl Cache { +impl Cache { /// Load image pub fn load(&mut self, handle: &image::Handle) -> &mut Memory { if self.contains(handle) { @@ -153,7 +159,7 @@ impl Cache { } } -impl Default for Cache { +impl Default for Cache { fn default() -> Self { Self { map: HashMap::new(), diff --git a/graphics/src/image/storage.rs b/graphics/src/image/storage.rs new file mode 100644 index 00000000..2098c7b2 --- /dev/null +++ b/graphics/src/image/storage.rs @@ -0,0 +1,31 @@ +//! Store images. +use crate::Size; + +use std::fmt::Debug; + +/// Stores cached image data for use in rendering +pub trait Storage { + /// The type of an [`Entry`] in the [`Storage`]. + type Entry: Entry; + + /// State provided to upload or remove a [`Self::Entry`]. + type State<'a>; + + /// Upload the image data of a [`Self::Entry`]. + fn upload( + &mut self, + width: u32, + height: u32, + data: &[u8], + state: &mut Self::State<'_>, + ) -> Option; + + /// Romve a [`Self::Entry`] from the [`Storage`]. + fn remove(&mut self, entry: &Self::Entry, state: &mut Self::State<'_>); +} + +/// An entry in some [`Storage`], +pub trait Entry: Debug { + /// The [`Size`] of the [`Entry`]. + fn size(&self) -> Size; +} diff --git a/graphics/src/image/vector.rs b/graphics/src/image/vector.rs index 0062e2ce..818fdd20 100644 --- a/graphics/src/image/vector.rs +++ b/graphics/src/image/vector.rs @@ -1,12 +1,12 @@ //! Vector image loading and caching +use crate::image::Storage; use iced_native::svg; +use iced_native::Size; use std::collections::{HashMap, HashSet}; use std::fs; -use super::TextureStore; - /// Entry in cache corresponding to an svg handle pub enum Svg { /// Parsed svg @@ -17,28 +17,28 @@ pub enum Svg { impl Svg { /// Viewport width and height - pub fn viewport_dimensions(&self) -> (u32, u32) { + pub fn viewport_dimensions(&self) -> Size { match self { Svg::Loaded(tree) => { let size = tree.svg_node().size; - (size.width() as u32, size.height() as u32) + Size::new(size.width() as u32, size.height() as u32) } - Svg::NotFound => (1, 1), + Svg::NotFound => Size::new(1, 1), } } } /// Caches svg vector and raster data #[derive(Debug)] -pub struct Cache { +pub struct Cache { svgs: HashMap, rasterized: HashMap<(u64, u32, u32), T::Entry>, svg_hits: HashSet, rasterized_hits: HashSet<(u64, u32, u32)>, } -impl Cache { +impl Cache { /// Load svg pub fn load(&mut self, handle: &svg::Handle) -> &Svg { if self.svgs.contains_key(&handle.id()) { @@ -162,7 +162,7 @@ impl Cache { } } -impl Default for Cache { +impl Default for Cache { fn default() -> Self { Self { svgs: HashMap::new(), diff --git a/graphics/src/renderer.rs b/graphics/src/renderer.rs index cdbc4f40..036b398c 100644 --- a/graphics/src/renderer.rs +++ b/graphics/src/renderer.rs @@ -183,7 +183,7 @@ where { type Handle = image::Handle; - fn dimensions(&self, handle: &image::Handle) -> (u32, u32) { + fn dimensions(&self, handle: &image::Handle) -> Size { self.backend().dimensions(handle) } @@ -196,7 +196,7 @@ impl svg::Renderer for Renderer where B: Backend + backend::Svg, { - fn dimensions(&self, handle: &svg::Handle) -> (u32, u32) { + fn dimensions(&self, handle: &svg::Handle) -> Size { self.backend().viewport_dimensions(handle) } -- cgit From 438f97a6d00ad0312e7c84b4c1529968bdfba849 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sat, 5 Nov 2022 03:18:13 +0100 Subject: Use RGBA texture for `image` and `svg` pipelines --- graphics/src/image/raster.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'graphics/src') diff --git a/graphics/src/image/raster.rs b/graphics/src/image/raster.rs index 8ed9615d..bad017a9 100644 --- a/graphics/src/image/raster.rs +++ b/graphics/src/image/raster.rs @@ -80,7 +80,7 @@ impl Cache { Memory::Invalid } } - image::Data::Pixels { + image::Data::Rgba { width, height, pixels, -- cgit From 7a24b4ba69e19459646648634c96d6426eaed462 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sat, 5 Nov 2022 03:39:59 +0100 Subject: Replace `texture_store` and `store` with `storage` --- graphics/src/image/raster.rs | 10 +++++----- graphics/src/image/vector.rs | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) (limited to 'graphics/src') diff --git a/graphics/src/image/raster.rs b/graphics/src/image/raster.rs index bad017a9..da46c30f 100644 --- a/graphics/src/image/raster.rs +++ b/graphics/src/image/raster.rs @@ -12,7 +12,7 @@ use std::collections::{HashMap, HashSet}; pub enum Memory { /// Image data on host Host(::image_rs::ImageBuffer<::image_rs::Rgba, Vec>), - /// Texture store entry + /// Storage entry Device(T::Entry), /// Image not found NotFound, @@ -106,14 +106,14 @@ impl Cache { &mut self, handle: &image::Handle, state: &mut T::State<'_>, - store: &mut T, + storage: &mut T, ) -> Option<&T::Entry> { let memory = self.load(handle); if let Memory::Host(image) = memory { let (width, height) = image.dimensions(); - let entry = store.upload(width, height, image, state)?; + let entry = storage.upload(width, height, image, state)?; *memory = Memory::Device(entry); } @@ -126,7 +126,7 @@ impl Cache { } /// Trim cache misses from cache - pub fn trim(&mut self, store: &mut T, state: &mut T::State<'_>) { + pub fn trim(&mut self, storage: &mut T, state: &mut T::State<'_>) { let hits = &self.hits; self.map.retain(|k, memory| { @@ -134,7 +134,7 @@ impl Cache { if !retain { if let Memory::Device(entry) = memory { - store.remove(entry, state); + storage.remove(entry, state); } } diff --git a/graphics/src/image/vector.rs b/graphics/src/image/vector.rs index 818fdd20..dc271c9e 100644 --- a/graphics/src/image/vector.rs +++ b/graphics/src/image/vector.rs @@ -79,7 +79,7 @@ impl Cache { [width, height]: [f32; 2], scale: f32, state: &mut T::State<'_>, - texture_store: &mut T, + storage: &mut T, ) -> Option<&T::Entry> { let id = handle.id(); @@ -124,7 +124,7 @@ impl Cache { let mut rgba = img.take(); rgba.chunks_exact_mut(4).for_each(|rgba| rgba.swap(0, 2)); - let allocation = texture_store.upload( + let allocation = storage.upload( width, height, bytemuck::cast_slice(rgba.as_slice()), @@ -143,7 +143,7 @@ impl Cache { } /// Load svg and upload raster data - pub fn trim(&mut self, texture_store: &mut T, state: &mut T::State<'_>) { + pub fn trim(&mut self, storage: &mut T, state: &mut T::State<'_>) { let svg_hits = &self.svg_hits; let rasterized_hits = &self.rasterized_hits; @@ -152,7 +152,7 @@ impl Cache { let retain = rasterized_hits.contains(k); if !retain { - texture_store.remove(entry, state); + storage.remove(entry, state); } retain -- cgit From a250aab958b837ccf3a315617c39c7bd3e423c42 Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Fri, 4 Nov 2022 23:25:01 -0700 Subject: Don't convert svg to BGRA before passing to shader Now that the shader is using RGBA, this incorrectly swaps the components. --- graphics/src/image/vector.rs | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) (limited to 'graphics/src') diff --git a/graphics/src/image/vector.rs b/graphics/src/image/vector.rs index dc271c9e..42f4b500 100644 --- a/graphics/src/image/vector.rs +++ b/graphics/src/image/vector.rs @@ -121,15 +121,8 @@ impl Cache { img.as_mut(), )?; - let mut rgba = img.take(); - rgba.chunks_exact_mut(4).for_each(|rgba| rgba.swap(0, 2)); - - let allocation = storage.upload( - width, - height, - bytemuck::cast_slice(rgba.as_slice()), - state, - )?; + let allocation = + storage.upload(width, height, img.data(), state)?; log::debug!("allocating {} {}x{}", id, width, height); let _ = self.svg_hits.insert(id); -- cgit From 1480ab20306e463b69b2229dcd5e81d4c66b2a64 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 10 Nov 2022 00:10:53 +0100 Subject: Fix broken documentation links --- graphics/src/gradient.rs | 7 ++++++- graphics/src/gradient/linear.rs | 5 ++++- graphics/src/widget/canvas/fill.rs | 2 +- graphics/src/widget/canvas/stroke.rs | 2 +- graphics/src/window/compositor.rs | 2 +- graphics/src/window/gl_compositor.rs | 2 +- 6 files changed, 14 insertions(+), 6 deletions(-) (limited to 'graphics/src') diff --git a/graphics/src/gradient.rs b/graphics/src/gradient.rs index 64f9e4b3..83f25238 100644 --- a/graphics/src/gradient.rs +++ b/graphics/src/gradient.rs @@ -9,7 +9,7 @@ use crate::{Color, Point, Size}; /// A fill which transitions colors progressively along a direction, either linearly, radially (TBD), /// or conically (TBD). pub enum Gradient { - /// A linear gradient interpolates colors along a direction from its [`start`] to its [`end`] + /// A linear gradient interpolates colors along a direction from its `start` to its `end` /// point. Linear(Linear), } @@ -23,10 +23,15 @@ impl Gradient { #[derive(Debug, Clone, Copy, PartialEq)] /// A point along the gradient vector where the specified [`color`] is unmixed. +/// +/// [`color`]: Self::color pub struct ColorStop { /// Offset along the gradient vector. pub offset: f32, + /// The color of the gradient at the specified [`offset`]. + /// + /// [`offset`]: Self::offset pub color: Color, } diff --git a/graphics/src/gradient/linear.rs b/graphics/src/gradient/linear.rs index 9928c1eb..c886db47 100644 --- a/graphics/src/gradient/linear.rs +++ b/graphics/src/gradient/linear.rs @@ -2,7 +2,10 @@ use crate::gradient::{ColorStop, Gradient, Position}; use crate::{Color, Point}; -/// A linear gradient that can be used in the style of [`super::Fill`] or [`super::Stroke`]. +/// A linear gradient that can be used in the style of [`Fill`] or [`Stroke`]. +/// +/// [`Fill`]: crate::widget::canvas::Fill +/// [`Stroke`]: crate::widget::canvas::Stroke #[derive(Debug, Clone, PartialEq)] pub struct Linear { /// The point where the linear gradient begins. diff --git a/graphics/src/widget/canvas/fill.rs b/graphics/src/widget/canvas/fill.rs index c69fc0d7..e2fc1cfe 100644 --- a/graphics/src/widget/canvas/fill.rs +++ b/graphics/src/widget/canvas/fill.rs @@ -8,7 +8,7 @@ pub use crate::triangle::Style; pub struct Fill { /// The color or gradient of the fill. /// - /// By default, it is set to [`FillStyle::Solid`] `BLACK`. + /// By default, it is set to [`Style::Solid`] with [`Color::BLACK`]. pub style: Style, /// The fill rule defines how to determine what is inside and what is diff --git a/graphics/src/widget/canvas/stroke.rs b/graphics/src/widget/canvas/stroke.rs index f9b8e447..a882531a 100644 --- a/graphics/src/widget/canvas/stroke.rs +++ b/graphics/src/widget/canvas/stroke.rs @@ -8,7 +8,7 @@ use iced_native::Color; pub struct Stroke<'a> { /// The color or gradient of the stroke. /// - /// By default, it is set to [`StrokeStyle::Solid`] `BLACK`. + /// By default, it is set to a [`Style::Solid`] with [`Color::BLACK`]. pub style: Style, /// The distance between the two edges of the stroke. pub width: f32, diff --git a/graphics/src/window/compositor.rs b/graphics/src/window/compositor.rs index 52255666..db4ba45d 100644 --- a/graphics/src/window/compositor.rs +++ b/graphics/src/window/compositor.rs @@ -40,7 +40,7 @@ pub trait Compositor: Sized { height: u32, ); - /// Returns [`GraphicsInformation`] used by this [`Compositor`]. + /// Returns [`Information`] used by this [`Compositor`]. fn fetch_information(&self) -> Information; /// Presents the [`Renderer`] primitives to the next frame of the given [`Surface`]. diff --git a/graphics/src/window/gl_compositor.rs b/graphics/src/window/gl_compositor.rs index 722e4d9c..a45a7ca1 100644 --- a/graphics/src/window/gl_compositor.rs +++ b/graphics/src/window/gl_compositor.rs @@ -54,7 +54,7 @@ pub trait GLCompositor: Sized { /// Resizes the viewport of the [`GLCompositor`]. fn resize_viewport(&mut self, physical_size: Size); - /// Returns [`GraphicsInformation`] used by this [`Compositor`]. + /// Returns [`Information`] used by this [`GLCompositor`]. fn fetch_information(&self) -> Information; /// Presents the primitives of the [`Renderer`] to the next frame of the -- cgit From 365f37a3ae10e7aff407b84050f77da10820866e Mon Sep 17 00:00:00 2001 From: bungoboingo Date: Thu, 10 Nov 2022 14:43:38 -0800 Subject: Added conditional configurations for WASM target for gradients & storage buffers, since storage buffers are not supported on wgpu WASM target at the moment. --- graphics/src/triangle.rs | 6 +++++- graphics/src/widget/canvas/frame.rs | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) (limited to 'graphics/src') diff --git a/graphics/src/triangle.rs b/graphics/src/triangle.rs index 04ff6d21..8b41bfc4 100644 --- a/graphics/src/triangle.rs +++ b/graphics/src/triangle.rs @@ -1,5 +1,7 @@ //! Draw geometry using meshes of triangles. -use crate::{Color, Gradient}; +use crate::Color; +#[cfg(not(target_arch = "wasm32"))] +use crate::Gradient; use bytemuck::{Pod, Zeroable}; @@ -27,6 +29,7 @@ pub struct Vertex2D { pub enum Style { /// Fill a primitive with a solid color. Solid(Color), + #[cfg(not(target_arch = "wasm32"))] /// Fill a primitive with an interpolated color. Gradient(Gradient), } @@ -37,6 +40,7 @@ impl From for Style { } } +#[cfg(not(target_arch = "wasm32"))] impl From for Style { fn from(gradient: Gradient) -> Self { Self::Gradient(gradient) diff --git a/graphics/src/widget/canvas/frame.rs b/graphics/src/widget/canvas/frame.rs index cf6c6928..a7b88502 100644 --- a/graphics/src/widget/canvas/frame.rs +++ b/graphics/src/widget/canvas/frame.rs @@ -76,6 +76,7 @@ impl Transform { fn transform_style(&self, style: triangle::Style) -> triangle::Style { match style { triangle::Style::Solid(color) => triangle::Style::Solid(color), + #[cfg(not(target_arch = "wasm32"))] triangle::Style::Gradient(gradient) => { triangle::Style::Gradient(self.transform_gradient(gradient)) } -- cgit From 33c3c0c0aa774bb7462e3c42aa04c591a66376a7 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Mon, 14 Nov 2022 00:02:42 +0100 Subject: Group all solid triangles independently of color --- graphics/src/layer.rs | 25 ++++- graphics/src/layer/mesh.rs | 94 ++++++++++++++--- graphics/src/primitive.rs | 25 +++-- graphics/src/triangle.rs | 37 ++----- graphics/src/widget/canvas.rs | 2 + graphics/src/widget/canvas/fill.rs | 2 +- graphics/src/widget/canvas/frame.rs | 196 +++++++++++++++++++++++++++-------- graphics/src/widget/canvas/stroke.rs | 2 +- graphics/src/widget/canvas/style.rs | 11 ++ 9 files changed, 294 insertions(+), 100 deletions(-) create mode 100644 graphics/src/widget/canvas/style.rs (limited to 'graphics/src') diff --git a/graphics/src/layer.rs b/graphics/src/layer.rs index e95934b0..fd670f48 100644 --- a/graphics/src/layer.rs +++ b/graphics/src/layer.rs @@ -166,10 +166,27 @@ impl<'a> Layer<'a> { border_color: border_color.into_linear(), }); } - Primitive::Mesh2D { + Primitive::SolidMesh { buffers, size } => { + let layer = &mut layers[current_layer]; + + let bounds = Rectangle::new( + Point::new(translation.x, translation.y), + *size, + ); + + // Only draw visible content + if let Some(clip_bounds) = layer.bounds.intersection(&bounds) { + layer.meshes.push(Mesh::Solid { + origin: Point::new(translation.x, translation.y), + buffers, + clip_bounds, + }); + } + } + Primitive::GradientMesh { buffers, size, - style, + gradient, } => { let layer = &mut layers[current_layer]; @@ -180,11 +197,11 @@ impl<'a> Layer<'a> { // Only draw visible content if let Some(clip_bounds) = layer.bounds.intersection(&bounds) { - layer.meshes.push(Mesh { + layer.meshes.push(Mesh::Gradient { origin: Point::new(translation.x, translation.y), buffers, clip_bounds, - style, + gradient, }); } } diff --git a/graphics/src/layer/mesh.rs b/graphics/src/layer/mesh.rs index 979081f1..7661c5c9 100644 --- a/graphics/src/layer/mesh.rs +++ b/graphics/src/layer/mesh.rs @@ -1,31 +1,93 @@ //! A collection of triangle primitives. use crate::triangle; -use crate::{Point, Rectangle}; +use crate::{Gradient, Point, Rectangle}; /// A mesh of triangles. #[derive(Debug, Clone, Copy)] -pub struct Mesh<'a> { - /// The origin of the vertices of the [`Mesh`]. - pub origin: Point, +pub enum Mesh<'a> { + /// A mesh of triangles with a solid color. + Solid { + /// The origin of the vertices of the [`Mesh`]. + origin: Point, - /// The vertex and index buffers of the [`Mesh`]. - pub buffers: &'a triangle::Mesh2D, + /// The vertex and index buffers of the [`Mesh`]. + buffers: &'a triangle::Mesh2D, - /// The clipping bounds of the [`Mesh`]. - pub clip_bounds: Rectangle, + /// The clipping bounds of the [`Mesh`]. + clip_bounds: Rectangle, + }, + /// A mesh of triangles with a gradient color. + Gradient { + /// The origin of the vertices of the [`Mesh`]. + origin: Point, - /// The shader of the [`Mesh`]. - pub style: &'a triangle::Style, + /// The vertex and index buffers of the [`Mesh`]. + buffers: &'a triangle::Mesh2D, + + /// The clipping bounds of the [`Mesh`]. + clip_bounds: Rectangle, + + /// The gradient to apply to the [`Mesh`]. + gradient: &'a Gradient, + }, +} + +impl Mesh<'_> { + /// Returns the origin of the [`Mesh`]. + pub fn origin(&self) -> Point { + match self { + Self::Solid { origin, .. } | Self::Gradient { origin, .. } => { + *origin + } + } + } + + /// Returns the indices of the [`Mesh`]. + pub fn indices(&self) -> &[u32] { + match self { + Self::Solid { buffers, .. } => &buffers.indices, + Self::Gradient { buffers, .. } => &buffers.indices, + } + } + + /// Returns the clip bounds of the [`Mesh`]. + pub fn clip_bounds(&self) -> Rectangle { + match self { + Self::Solid { clip_bounds, .. } + | Self::Gradient { clip_bounds, .. } => *clip_bounds, + } + } +} + +/// The result of counting the attributes of a set of meshes. +#[derive(Debug, Clone, Copy, Default)] +pub struct AttributeCount { + /// The total amount of solid vertices. + pub solid_vertices: usize, + + /// The total amount of gradient vertices. + pub gradient_vertices: usize, + + /// The total amount of indices. + pub indices: usize, } /// Returns the number of total vertices & total indices of all [`Mesh`]es. -pub fn attribute_count_of<'a>(meshes: &'a [Mesh<'a>]) -> (usize, usize) { +pub fn attribute_count_of<'a>(meshes: &'a [Mesh<'a>]) -> AttributeCount { meshes .iter() - .map(|Mesh { buffers, .. }| { - (buffers.vertices.len(), buffers.indices.len()) - }) - .fold((0, 0), |(total_v, total_i), (v, i)| { - (total_v + v, total_i + i) + .fold(AttributeCount::default(), |mut count, mesh| { + match mesh { + Mesh::Solid { buffers, .. } => { + count.solid_vertices += buffers.vertices.len(); + count.indices += buffers.indices.len(); + } + Mesh::Gradient { buffers, .. } => { + count.gradient_vertices += buffers.vertices.len(); + count.indices += buffers.indices.len(); + } + } + + count }) } diff --git a/graphics/src/primitive.rs b/graphics/src/primitive.rs index b481ac0b..9759d97a 100644 --- a/graphics/src/primitive.rs +++ b/graphics/src/primitive.rs @@ -3,6 +3,7 @@ use iced_native::svg; use iced_native::{Background, Color, Font, Rectangle, Size, Vector}; use crate::alignment; +use crate::gradient::Gradient; use crate::triangle; use std::sync::Arc; @@ -77,20 +78,32 @@ pub enum Primitive { /// The primitive to translate content: Box, }, - /// A low-level primitive to render a mesh of triangles. + /// A low-level primitive to render a mesh of triangles with a solid color. /// /// It can be used to render many kinds of geometry freely. - Mesh2D { - /// The vertex and index buffers of the mesh - buffers: triangle::Mesh2D, + SolidMesh { + /// The vertices and indices of the mesh. + buffers: triangle::Mesh2D, + + /// The size of the drawable region of the mesh. + /// + /// Any geometry that falls out of this region will be clipped. + size: Size, + }, + /// A low-level primitive to render a mesh of triangles with a gradient. + /// + /// It can be used to render many kinds of geometry freely. + GradientMesh { + /// The vertices and indices of the mesh. + buffers: triangle::Mesh2D, /// The size of the drawable region of the mesh. /// /// Any geometry that falls out of this region will be clipped. size: Size, - /// The shader of the mesh - style: triangle::Style, + /// The [`Gradient`] to apply to the mesh. + gradient: Gradient, }, /// A cached primitive. /// diff --git a/graphics/src/triangle.rs b/graphics/src/triangle.rs index 8b41bfc4..f52b2339 100644 --- a/graphics/src/triangle.rs +++ b/graphics/src/triangle.rs @@ -1,15 +1,12 @@ //! Draw geometry using meshes of triangles. -use crate::Color; -#[cfg(not(target_arch = "wasm32"))] -use crate::Gradient; - use bytemuck::{Pod, Zeroable}; /// A set of [`Vertex2D`] and indices representing a list of triangles. #[derive(Clone, Debug)] -pub struct Mesh2D { +pub struct Mesh2D { /// The vertices of the mesh - pub vertices: Vec, + pub vertices: Vec, + /// The list of vertex indices that defines the triangles of the mesh. /// /// Therefore, this list should always have a length that is a multiple of 3. @@ -24,25 +21,13 @@ pub struct Vertex2D { pub position: [f32; 2], } -#[derive(Debug, Clone, PartialEq)] -/// Supported shaders for triangle primitives. -pub enum Style { - /// Fill a primitive with a solid color. - Solid(Color), - #[cfg(not(target_arch = "wasm32"))] - /// Fill a primitive with an interpolated color. - Gradient(Gradient), -} - -impl From for Style { - fn from(color: Color) -> Self { - Self::Solid(color) - } -} +/// A two-dimensional vertex with a color. +#[derive(Copy, Clone, Debug, Zeroable, Pod)] +#[repr(C)] +pub struct ColoredVertex2D { + /// The vertex position in 2D space. + pub position: [f32; 2], -#[cfg(not(target_arch = "wasm32"))] -impl From for Style { - fn from(gradient: Gradient) -> Self { - Self::Gradient(gradient) - } + /// The color of the vertex in __linear__ RGBA. + pub color: [f32; 4], } diff --git a/graphics/src/widget/canvas.rs b/graphics/src/widget/canvas.rs index a14940d9..b070d0a6 100644 --- a/graphics/src/widget/canvas.rs +++ b/graphics/src/widget/canvas.rs @@ -13,6 +13,7 @@ mod cursor; mod frame; mod geometry; mod program; +mod style; mod text; pub use crate::gradient::{self, Gradient}; @@ -25,6 +26,7 @@ pub use geometry::Geometry; pub use path::Path; pub use program::Program; pub use stroke::{LineCap, LineDash, LineJoin, Stroke}; +pub use style::Style; pub use text::Text; use crate::{Backend, Primitive, Renderer}; diff --git a/graphics/src/widget/canvas/fill.rs b/graphics/src/widget/canvas/fill.rs index e2fc1cfe..e954ebb5 100644 --- a/graphics/src/widget/canvas/fill.rs +++ b/graphics/src/widget/canvas/fill.rs @@ -1,7 +1,7 @@ //! Fill [crate::widget::canvas::Geometry] with a certain style. use crate::{Color, Gradient}; -pub use crate::triangle::Style; +pub use crate::widget::canvas::Style; /// The style used to fill geometry. #[derive(Debug, Clone)] diff --git a/graphics/src/widget/canvas/frame.rs b/graphics/src/widget/canvas/frame.rs index a7b88502..d68548ae 100644 --- a/graphics/src/widget/canvas/frame.rs +++ b/graphics/src/widget/canvas/frame.rs @@ -1,7 +1,6 @@ use crate::gradient::Gradient; use crate::triangle; -use crate::triangle::Vertex2D; -use crate::widget::canvas::{path, Fill, Geometry, Path, Stroke, Text}; +use crate::widget::canvas::{path, Fill, Geometry, Path, Stroke, Style, Text}; use crate::Primitive; use iced_native::{Point, Rectangle, Size, Vector}; @@ -23,8 +22,16 @@ pub struct Frame { stroke_tessellator: tessellation::StrokeTessellator, } +enum Buffer { + Solid(tessellation::VertexBuffers), + Gradient( + tessellation::VertexBuffers, + Gradient, + ), +} + struct BufferStack { - stack: Vec<(tessellation::VertexBuffers, triangle::Style)>, + stack: Vec, } impl BufferStack { @@ -32,22 +39,64 @@ impl BufferStack { Self { stack: Vec::new() } } - fn get( - &mut self, - mesh_style: triangle::Style, - ) -> tessellation::BuffersBuilder<'_, Vertex2D, u32, Vertex2DBuilder> { - match self.stack.last_mut() { - Some((_, current_style)) if current_style == &mesh_style => {} - _ => { - self.stack - .push((tessellation::VertexBuffers::new(), mesh_style)); + fn get_mut(&mut self, style: &Style) -> &mut Buffer { + match style { + Style::Solid(_) => match self.stack.last() { + Some(Buffer::Solid(_)) => {} + _ => { + self.stack.push(Buffer::Solid( + tessellation::VertexBuffers::new(), + )); + } + }, + Style::Gradient(gradient) => match self.stack.last() { + Some(Buffer::Gradient(_, last)) if gradient == last => {} + _ => { + self.stack.push(Buffer::Gradient( + tessellation::VertexBuffers::new(), + gradient.clone(), + )); + } + }, + } + + self.stack.last_mut().unwrap() + } + + fn get_fill<'a>( + &'a mut self, + style: &Style, + ) -> Box { + match (style, self.get_mut(style)) { + (Style::Solid(color), Buffer::Solid(buffer)) => { + Box::new(tessellation::BuffersBuilder::new( + buffer, + TriangleVertex2DBuilder(color.into_linear()), + )) } - }; + (Style::Gradient(_), Buffer::Gradient(buffer, _)) => Box::new( + tessellation::BuffersBuilder::new(buffer, Vertex2DBuilder), + ), + _ => unreachable!(), + } + } - tessellation::BuffersBuilder::new( - &mut self.stack.last_mut().unwrap().0, - Vertex2DBuilder, - ) + fn get_stroke<'a>( + &'a mut self, + style: &Style, + ) -> Box { + match (style, self.get_mut(style)) { + (Style::Solid(color), Buffer::Solid(buffer)) => { + Box::new(tessellation::BuffersBuilder::new( + buffer, + TriangleVertex2DBuilder(color.into_linear()), + )) + } + (Style::Gradient(_), Buffer::Gradient(buffer, _)) => Box::new( + tessellation::BuffersBuilder::new(buffer, Vertex2DBuilder), + ), + _ => unreachable!(), + } } } @@ -73,12 +122,11 @@ impl Transform { point.y = transformed.y; } - fn transform_style(&self, style: triangle::Style) -> triangle::Style { + fn transform_style(&self, style: Style) -> Style { match style { - triangle::Style::Solid(color) => triangle::Style::Solid(color), - #[cfg(not(target_arch = "wasm32"))] - triangle::Style::Gradient(gradient) => { - triangle::Style::Gradient(self.transform_gradient(gradient)) + Style::Solid(color) => Style::Solid(color), + Style::Gradient(gradient) => { + Style::Gradient(self.transform_gradient(gradient)) } } } @@ -146,7 +194,7 @@ impl Frame { let mut buffer = self .buffers - .get(self.transforms.current.transform_style(style)); + .get_fill(&self.transforms.current.transform_style(style)); let options = tessellation::FillOptions::default().with_fill_rule(rule.into()); @@ -155,7 +203,7 @@ impl Frame { self.fill_tessellator.tessellate_path( path.raw(), &options, - &mut buffer, + buffer.as_mut(), ) } else { let path = path.transformed(&self.transforms.current.raw); @@ -163,7 +211,7 @@ impl Frame { self.fill_tessellator.tessellate_path( path.raw(), &options, - &mut buffer, + buffer.as_mut(), ) } .expect("Tessellate path."); @@ -181,7 +229,7 @@ impl Frame { let mut buffer = self .buffers - .get(self.transforms.current.transform_style(style)); + .get_fill(&self.transforms.current.transform_style(style)); let top_left = self.transforms.current.raw.transform_point( @@ -200,7 +248,7 @@ impl Frame { .tessellate_rectangle( &lyon::math::Box2D::new(top_left, top_left + size), &options, - &mut buffer, + buffer.as_mut(), ) .expect("Fill rectangle"); } @@ -212,7 +260,7 @@ impl Frame { let mut buffer = self .buffers - .get(self.transforms.current.transform_style(stroke.style)); + .get_stroke(&self.transforms.current.transform_style(stroke.style)); let mut options = tessellation::StrokeOptions::default(); options.line_width = stroke.width; @@ -230,7 +278,7 @@ impl Frame { self.stroke_tessellator.tessellate_path( path.raw(), &options, - &mut buffer, + buffer.as_mut(), ) } else { let path = path.transformed(&self.transforms.current.raw); @@ -238,7 +286,7 @@ impl Frame { self.stroke_tessellator.tessellate_path( path.raw(), &options, - &mut buffer, + buffer.as_mut(), ) } .expect("Stroke path"); @@ -383,16 +431,31 @@ impl Frame { } fn into_primitives(mut self) -> Vec { - for (buffer, style) in self.buffers.stack { - if !buffer.indices.is_empty() { - self.primitives.push(Primitive::Mesh2D { - buffers: triangle::Mesh2D { - vertices: buffer.vertices, - indices: buffer.indices, - }, - size: self.size, - style, - }) + for buffer in self.buffers.stack { + match buffer { + Buffer::Solid(buffer) => { + if !buffer.indices.is_empty() { + self.primitives.push(Primitive::SolidMesh { + buffers: triangle::Mesh2D { + vertices: buffer.vertices, + indices: buffer.indices, + }, + size: self.size, + }) + } + } + Buffer::Gradient(buffer, gradient) => { + if !buffer.indices.is_empty() { + self.primitives.push(Primitive::GradientMesh { + buffers: triangle::Mesh2D { + vertices: buffer.vertices, + indices: buffer.indices, + }, + size: self.size, + gradient, + }) + } + } } } @@ -402,25 +465,66 @@ impl Frame { struct Vertex2DBuilder; -impl tessellation::FillVertexConstructor for Vertex2DBuilder { - fn new_vertex(&mut self, vertex: tessellation::FillVertex<'_>) -> Vertex2D { +impl tessellation::FillVertexConstructor + for Vertex2DBuilder +{ + fn new_vertex( + &mut self, + vertex: tessellation::FillVertex<'_>, + ) -> triangle::Vertex2D { + let position = vertex.position(); + + triangle::Vertex2D { + position: [position.x, position.y], + } + } +} + +impl tessellation::StrokeVertexConstructor + for Vertex2DBuilder +{ + fn new_vertex( + &mut self, + vertex: tessellation::StrokeVertex<'_, '_>, + ) -> triangle::Vertex2D { + let position = vertex.position(); + + triangle::Vertex2D { + position: [position.x, position.y], + } + } +} + +struct TriangleVertex2DBuilder([f32; 4]); + +impl tessellation::FillVertexConstructor + for TriangleVertex2DBuilder +{ + fn new_vertex( + &mut self, + vertex: tessellation::FillVertex<'_>, + ) -> triangle::ColoredVertex2D { let position = vertex.position(); - Vertex2D { + triangle::ColoredVertex2D { position: [position.x, position.y], + color: self.0, } } } -impl tessellation::StrokeVertexConstructor for Vertex2DBuilder { +impl tessellation::StrokeVertexConstructor + for TriangleVertex2DBuilder +{ fn new_vertex( &mut self, vertex: tessellation::StrokeVertex<'_, '_>, - ) -> Vertex2D { + ) -> triangle::ColoredVertex2D { let position = vertex.position(); - Vertex2D { + triangle::ColoredVertex2D { position: [position.x, position.y], + color: self.0, } } } diff --git a/graphics/src/widget/canvas/stroke.rs b/graphics/src/widget/canvas/stroke.rs index a882531a..4c19251d 100644 --- a/graphics/src/widget/canvas/stroke.rs +++ b/graphics/src/widget/canvas/stroke.rs @@ -1,5 +1,5 @@ //! Create lines from a [crate::widget::canvas::Path] and assigns them various attributes/styles. -pub use crate::triangle::Style; +pub use crate::widget::canvas::Style; use iced_native::Color; diff --git a/graphics/src/widget/canvas/style.rs b/graphics/src/widget/canvas/style.rs new file mode 100644 index 00000000..794109bd --- /dev/null +++ b/graphics/src/widget/canvas/style.rs @@ -0,0 +1,11 @@ +use crate::{Color, Gradient}; + +/// The coloring style of some drawing. +#[derive(Debug, Clone, PartialEq)] +pub enum Style { + /// A solid [`Color`]. + Solid(Color), + + /// A [`Gradient`] color. + Gradient(Gradient), +} -- cgit From 94988bb8f25efb93ee1616e4c827b6f740da8fa5 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Mon, 28 Nov 2022 19:31:26 +0100 Subject: Implement `From` traits for `Style` in `canvas` --- graphics/src/widget/canvas/style.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'graphics/src') diff --git a/graphics/src/widget/canvas/style.rs b/graphics/src/widget/canvas/style.rs index 794109bd..6794f2e7 100644 --- a/graphics/src/widget/canvas/style.rs +++ b/graphics/src/widget/canvas/style.rs @@ -9,3 +9,15 @@ pub enum Style { /// A [`Gradient`] color. Gradient(Gradient), } + +impl From for Style { + fn from(color: Color) -> Self { + Self::Solid(color) + } +} + +impl From for Style { + fn from(gradient: Gradient) -> Self { + Self::Gradient(gradient) + } +} -- cgit