diff options
Diffstat (limited to '')
-rw-r--r-- | core/src/gradient.rs (renamed from graphics/src/gradient.rs) | 0 | ||||
-rw-r--r-- | core/src/gradient/linear.rs (renamed from graphics/src/gradient/linear.rs) | 0 | ||||
-rw-r--r-- | graphics/src/backend.rs | 8 | ||||
-rw-r--r-- | graphics/src/compositor.rs (renamed from graphics/src/window/compositor.rs) | 8 | ||||
-rw-r--r-- | graphics/src/geometry.rs | 36 | ||||
-rw-r--r-- | graphics/src/geometry/fill.rs (renamed from graphics/src/widget/canvas/fill.rs) | 19 | ||||
-rw-r--r-- | graphics/src/geometry/path.rs (renamed from graphics/src/widget/canvas/path.rs) | 52 | ||||
-rw-r--r-- | graphics/src/geometry/path/arc.rs (renamed from graphics/src/widget/canvas/path/arc.rs) | 2 | ||||
-rw-r--r-- | graphics/src/geometry/path/builder.rs (renamed from graphics/src/widget/canvas/path/builder.rs) | 27 | ||||
-rw-r--r-- | graphics/src/geometry/stroke.rs (renamed from graphics/src/widget/canvas/stroke.rs) | 24 | ||||
-rw-r--r-- | graphics/src/geometry/style.rs (renamed from graphics/src/widget/canvas/style.rs) | 2 | ||||
-rw-r--r-- | graphics/src/geometry/text.rs (renamed from graphics/src/widget/canvas/text.rs) | 4 | ||||
-rw-r--r-- | graphics/src/image.rs | 98 | ||||
-rw-r--r-- | graphics/src/image/raster.rs | 242 | ||||
-rw-r--r-- | graphics/src/image/storage.rs | 2 | ||||
-rw-r--r-- | graphics/src/lib.rs | 30 | ||||
-rw-r--r-- | graphics/src/overlay.rs | 2 | ||||
-rw-r--r-- | graphics/src/overlay/menu.rs | 3 | ||||
-rw-r--r-- | graphics/src/primitive.rs | 134 | ||||
-rw-r--r-- | graphics/src/renderer.rs | 68 | ||||
-rw-r--r-- | graphics/src/triangle.rs | 32 | ||||
-rw-r--r-- | graphics/src/viewport.rs | 4 | ||||
-rw-r--r-- | graphics/src/widget/canvas/geometry.rs | 24 | ||||
-rw-r--r-- | graphics/src/window.rs | 10 | ||||
-rw-r--r-- | graphics/src/window/gl_compositor.rs | 71 | ||||
-rw-r--r-- | renderer/src/geometry/cache.rs (renamed from graphics/src/widget/canvas/cache.rs) | 59 | ||||
-rw-r--r-- | renderer/src/widget.rs (renamed from graphics/src/widget.rs) | 5 | ||||
-rw-r--r-- | wgpu/src/geometry.rs (renamed from graphics/src/widget/canvas/frame.rs) | 158 | ||||
-rw-r--r-- | wgpu/src/image/vector.rs (renamed from graphics/src/image/vector.rs) | 61 | ||||
-rw-r--r-- | wgpu/src/layer.rs (renamed from graphics/src/layer.rs) | 78 | ||||
-rw-r--r-- | wgpu/src/layer/image.rs (renamed from graphics/src/layer/image.rs) | 6 | ||||
-rw-r--r-- | wgpu/src/layer/mesh.rs (renamed from graphics/src/layer/mesh.rs) | 8 | ||||
-rw-r--r-- | wgpu/src/layer/quad.rs (renamed from graphics/src/layer/quad.rs) | 0 | ||||
-rw-r--r-- | wgpu/src/layer/text.rs (renamed from graphics/src/layer/text.rs) | 3 | ||||
-rw-r--r-- | widget/src/canvas.rs (renamed from graphics/src/widget/canvas.rs) | 132 | ||||
-rw-r--r-- | widget/src/canvas/cursor.rs (renamed from graphics/src/widget/canvas/cursor.rs) | 2 | ||||
-rw-r--r-- | widget/src/canvas/event.rs (renamed from graphics/src/widget/canvas/event.rs) | 8 | ||||
-rw-r--r-- | widget/src/canvas/program.rs (renamed from graphics/src/widget/canvas/program.rs) | 31 | ||||
-rw-r--r-- | widget/src/qr_code.rs (renamed from graphics/src/widget/qr_code.rs) | 100 |
39 files changed, 653 insertions, 900 deletions
diff --git a/graphics/src/gradient.rs b/core/src/gradient.rs index 61e919d6..61e919d6 100644 --- a/graphics/src/gradient.rs +++ b/core/src/gradient.rs diff --git a/graphics/src/gradient/linear.rs b/core/src/gradient/linear.rs index c886db47..c886db47 100644 --- a/graphics/src/gradient/linear.rs +++ b/core/src/gradient/linear.rs diff --git a/graphics/src/backend.rs b/graphics/src/backend.rs index 8658cffe..dd2888ab 100644 --- a/graphics/src/backend.rs +++ b/graphics/src/backend.rs @@ -1,8 +1,8 @@ //! Write a graphics backend. -use iced_native::image; -use iced_native::svg; -use iced_native::text; -use iced_native::{Font, Point, Size}; +use iced_core::image; +use iced_core::svg; +use iced_core::text; +use iced_core::{Font, Point, Size}; use std::borrow::Cow; diff --git a/graphics/src/window/compositor.rs b/graphics/src/compositor.rs index db4ba45d..d55e801a 100644 --- a/graphics/src/window/compositor.rs +++ b/graphics/src/compositor.rs @@ -1,6 +1,8 @@ //! A compositor is responsible for initializing a renderer and managing window //! surfaces. -use crate::{Color, Error, Viewport}; +use crate::{Error, Viewport}; + +use iced_core::Color; use raw_window_handle::{HasRawDisplayHandle, HasRawWindowHandle}; use thiserror::Error; @@ -11,7 +13,7 @@ pub trait Compositor: Sized { type Settings: Default; /// The iced renderer of the backend. - type Renderer: iced_native::Renderer; + type Renderer: iced_core::Renderer; /// The surface of the backend. type Surface; @@ -28,6 +30,8 @@ pub trait Compositor: Sized { fn create_surface<W: HasRawWindowHandle + HasRawDisplayHandle>( &mut self, window: &W, + width: u32, + height: u32, ) -> Self::Surface; /// Configures a new [`Surface`] with the given dimensions. diff --git a/graphics/src/geometry.rs b/graphics/src/geometry.rs new file mode 100644 index 00000000..8db1594a --- /dev/null +++ b/graphics/src/geometry.rs @@ -0,0 +1,36 @@ +//! Draw 2D graphics for your users. +//! +//! A [`Canvas`] widget can be used to draw different kinds of 2D shapes in a +//! [`Frame`]. It can be used for animation, data visualization, game graphics, +//! and more! +pub mod fill; +pub mod path; +pub mod stroke; + +mod style; +mod text; + +pub use fill::Fill; +pub use path::Path; +pub use stroke::{LineCap, LineDash, LineJoin, Stroke}; +pub use style::Style; +pub use text::Text; + +pub use iced_core::gradient::{self, Gradient}; + +use crate::Primitive; + +#[derive(Debug, Clone)] +pub struct Geometry(pub Primitive); + +impl From<Geometry> for Primitive { + fn from(geometry: Geometry) -> Self { + geometry.0 + } +} + +pub trait Renderer: iced_core::Renderer { + type Geometry; + + fn draw(&mut self, geometry: Vec<Self::Geometry>); +} diff --git a/graphics/src/widget/canvas/fill.rs b/graphics/src/geometry/fill.rs index e954ebb5..2e8c1669 100644 --- a/graphics/src/widget/canvas/fill.rs +++ b/graphics/src/geometry/fill.rs @@ -1,7 +1,7 @@ //! Fill [crate::widget::canvas::Geometry] with a certain style. -use crate::{Color, Gradient}; +use iced_core::{Color, Gradient}; -pub use crate::widget::canvas::Style; +pub use crate::geometry::Style; /// The style used to fill geometry. #[derive(Debug, Clone)] @@ -19,14 +19,14 @@ pub struct Fill { /// By default, it is set to `NonZero`. /// /// [1]: https://www.w3.org/TR/SVG/painting.html#FillRuleProperty - pub rule: FillRule, + pub rule: Rule, } impl Default for Fill { fn default() -> Self { Self { style: Style::Solid(Color::BLACK), - rule: FillRule::NonZero, + rule: Rule::NonZero, } } } @@ -57,16 +57,7 @@ impl From<Gradient> for Fill { /// [1]: https://www.w3.org/TR/SVG/painting.html#FillRuleProperty #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[allow(missing_docs)] -pub enum FillRule { +pub enum Rule { NonZero, EvenOdd, } - -impl From<FillRule> for lyon::tessellation::FillRule { - fn from(rule: FillRule) -> lyon::tessellation::FillRule { - match rule { - FillRule::NonZero => lyon::tessellation::FillRule::NonZero, - FillRule::EvenOdd => lyon::tessellation::FillRule::EvenOdd, - } - } -} diff --git a/graphics/src/widget/canvas/path.rs b/graphics/src/geometry/path.rs index aeb2589e..c3127bdf 100644 --- a/graphics/src/widget/canvas/path.rs +++ b/graphics/src/geometry/path.rs @@ -7,18 +7,16 @@ mod builder; pub use arc::Arc; pub use builder::Builder; -use crate::widget::canvas::LineDash; +pub use lyon_path; -use iced_native::{Point, Size}; -use lyon::algorithms::walk::{walk_along_path, RepeatedPattern, WalkerEvent}; -use lyon::path::iterator::PathIterator; +use iced_core::{Point, Size}; /// An immutable set of points that may or may not be connected. /// /// A single [`Path`] can represent different kinds of 2D shapes! #[derive(Debug, Clone)] pub struct Path { - raw: lyon::path::Path, + raw: lyon_path::Path, } impl Path { @@ -56,54 +54,14 @@ impl Path { } #[inline] - pub(crate) fn raw(&self) -> &lyon::path::Path { + pub fn raw(&self) -> &lyon_path::Path { &self.raw } #[inline] - pub(crate) fn transformed( - &self, - transform: &lyon::math::Transform, - ) -> Path { + pub fn transform(&self, transform: &lyon_path::math::Transform) -> Path { Path { raw: self.raw.clone().transformed(transform), } } } - -pub(super) fn dashed(path: &Path, line_dash: LineDash<'_>) -> Path { - Path::new(|builder| { - let segments_odd = (line_dash.segments.len() % 2 == 1) - .then(|| [line_dash.segments, line_dash.segments].concat()); - - let mut draw_line = false; - - walk_along_path( - path.raw().iter().flattened(0.01), - 0.0, - lyon::tessellation::StrokeOptions::DEFAULT_TOLERANCE, - &mut RepeatedPattern { - callback: |event: WalkerEvent<'_>| { - let point = Point { - x: event.position.x, - y: event.position.y, - }; - - if draw_line { - builder.line_to(point); - } else { - builder.move_to(point); - } - - draw_line = !draw_line; - - true - }, - index: line_dash.offset, - intervals: segments_odd - .as_deref() - .unwrap_or(line_dash.segments), - }, - ); - }) -} diff --git a/graphics/src/widget/canvas/path/arc.rs b/graphics/src/geometry/path/arc.rs index b8e72daf..2cdebb66 100644 --- a/graphics/src/widget/canvas/path/arc.rs +++ b/graphics/src/geometry/path/arc.rs @@ -1,5 +1,5 @@ //! Build and draw curves. -use iced_native::{Point, Vector}; +use iced_core::{Point, Vector}; /// A segment of a differentiable curve. #[derive(Debug, Clone, Copy)] diff --git a/graphics/src/widget/canvas/path/builder.rs b/graphics/src/geometry/path/builder.rs index 5121aa68..794dd3bc 100644 --- a/graphics/src/widget/canvas/path/builder.rs +++ b/graphics/src/geometry/path/builder.rs @@ -1,35 +1,38 @@ -use crate::widget::canvas::path::{arc, Arc, Path}; +use crate::geometry::path::{arc, Arc, Path}; -use iced_native::{Point, Size}; -use lyon::path::builder::SvgPathBuilder; +use iced_core::{Point, Size}; + +use lyon_path::builder::{self, SvgPathBuilder}; +use lyon_path::geom; +use lyon_path::math; /// A [`Path`] builder. /// /// Once a [`Path`] is built, it can no longer be mutated. #[allow(missing_debug_implementations)] pub struct Builder { - raw: lyon::path::builder::WithSvg<lyon::path::path::BuilderImpl>, + raw: builder::WithSvg<lyon_path::path::BuilderImpl>, } impl Builder { /// Creates a new [`Builder`]. pub fn new() -> Builder { Builder { - raw: lyon::path::Path::builder().with_svg(), + raw: lyon_path::Path::builder().with_svg(), } } /// Moves the starting point of a new sub-path to the given `Point`. #[inline] pub fn move_to(&mut self, point: Point) { - let _ = self.raw.move_to(lyon::math::Point::new(point.x, point.y)); + let _ = self.raw.move_to(math::Point::new(point.x, point.y)); } /// Connects the last point in the [`Path`] to the given `Point` with a /// straight line. #[inline] pub fn line_to(&mut self, point: Point) { - let _ = self.raw.line_to(lyon::math::Point::new(point.x, point.y)); + let _ = self.raw.line_to(math::Point::new(point.x, point.y)); } /// Adds an [`Arc`] to the [`Path`] from `start_angle` to `end_angle` in @@ -53,8 +56,6 @@ impl Builder { /// See [the HTML5 specification of `arcTo`](https://html.spec.whatwg.org/multipage/canvas.html#building-paths:dom-context-2d-arcto) /// for more details and examples. pub fn arc_to(&mut self, a: Point, b: Point, radius: f32) { - use lyon::{math, path}; - let start = self.raw.current_position(); let mid = math::Point::new(a.x, a.y); let end = math::Point::new(b.x, b.y); @@ -92,7 +93,7 @@ impl Builder { self.raw.arc_to( math::Vector::new(radius, radius), math::Angle::radians(0.0), - path::ArcFlags { + lyon_path::ArcFlags { large_arc: false, sweep, }, @@ -102,8 +103,6 @@ impl Builder { /// Adds an ellipse to the [`Path`] using a clockwise direction. pub fn ellipse(&mut self, arc: arc::Elliptical) { - use lyon::{geom, math}; - let arc = geom::Arc { center: math::Point::new(arc.center.x, arc.center.y), radii: math::Vector::new(arc.radii.x, arc.radii.y), @@ -128,8 +127,6 @@ impl Builder { control_b: Point, to: Point, ) { - use lyon::math; - let _ = self.raw.cubic_bezier_to( math::Point::new(control_a.x, control_a.y), math::Point::new(control_b.x, control_b.y), @@ -141,8 +138,6 @@ impl Builder { /// and its end point. #[inline] pub fn quadratic_curve_to(&mut self, control: Point, to: Point) { - use lyon::math; - let _ = self.raw.quadratic_bezier_to( math::Point::new(control.x, control.y), math::Point::new(to.x, to.y), diff --git a/graphics/src/widget/canvas/stroke.rs b/graphics/src/geometry/stroke.rs index 4c19251d..2d760a6c 100644 --- a/graphics/src/widget/canvas/stroke.rs +++ b/graphics/src/geometry/stroke.rs @@ -1,7 +1,7 @@ //! Create lines from a [crate::widget::canvas::Path] and assigns them various attributes/styles. -pub use crate::widget::canvas::Style; +pub use crate::geometry::Style; -use iced_native::Color; +use iced_core::Color; /// The style of a stroke. #[derive(Debug, Clone)] @@ -77,16 +77,6 @@ impl Default for LineCap { } } -impl From<LineCap> for lyon::tessellation::LineCap { - fn from(line_cap: LineCap) -> lyon::tessellation::LineCap { - match line_cap { - LineCap::Butt => lyon::tessellation::LineCap::Butt, - LineCap::Square => lyon::tessellation::LineCap::Square, - LineCap::Round => lyon::tessellation::LineCap::Round, - } - } -} - /// The shape used at the corners of paths or basic shapes when they are /// stroked. #[derive(Debug, Clone, Copy)] @@ -105,16 +95,6 @@ impl Default for LineJoin { } } -impl From<LineJoin> for lyon::tessellation::LineJoin { - fn from(line_join: LineJoin) -> lyon::tessellation::LineJoin { - match line_join { - LineJoin::Miter => lyon::tessellation::LineJoin::Miter, - LineJoin::Round => lyon::tessellation::LineJoin::Round, - LineJoin::Bevel => lyon::tessellation::LineJoin::Bevel, - } - } -} - /// The dash pattern used when stroking the line. #[derive(Debug, Clone, Copy, Default)] pub struct LineDash<'a> { diff --git a/graphics/src/widget/canvas/style.rs b/graphics/src/geometry/style.rs index 6794f2e7..be9ee376 100644 --- a/graphics/src/widget/canvas/style.rs +++ b/graphics/src/geometry/style.rs @@ -1,4 +1,4 @@ -use crate::{Color, Gradient}; +use iced_core::{Color, Gradient}; /// The coloring style of some drawing. #[derive(Debug, Clone, PartialEq)] diff --git a/graphics/src/widget/canvas/text.rs b/graphics/src/geometry/text.rs index 8c0b2dfb..06e0b4d0 100644 --- a/graphics/src/widget/canvas/text.rs +++ b/graphics/src/geometry/text.rs @@ -1,5 +1,5 @@ -use crate::alignment; -use crate::{Color, Font, Point}; +use iced_core::alignment; +use iced_core::{Color, Font, Point}; /// A bunch of text that can be drawn to a canvas #[derive(Debug, Clone)] diff --git a/graphics/src/image.rs b/graphics/src/image.rs index 04f4ff9d..2f634252 100644 --- a/graphics/src/image.rs +++ b/graphics/src/image.rs @@ -1,10 +1,94 @@ -//! Render images. -#[cfg(feature = "image_rs")] -pub mod raster; +//! Load and operate on images. +use crate::core::image::{Data, Handle}; -#[cfg(feature = "svg")] -pub mod vector; +use bitflags::bitflags; -pub mod storage; +pub use ::image as image_rs; -pub use storage::Storage; +pub fn load(handle: &Handle) -> image_rs::ImageResult<image_rs::DynamicImage> { + match handle.data() { + Data::Path(path) => { + let image = ::image::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); + + Ok(operation.perform(image)) + } + Data::Bytes(bytes) => { + let image = ::image::load_from_memory(bytes)?; + let operation = + Operation::from_exif(&mut std::io::Cursor::new(bytes)) + .ok() + .unwrap_or_else(Operation::empty); + + Ok(operation.perform(image)) + } + Data::Rgba { + width, + height, + pixels, + } => { + if let Some(image) = image_rs::ImageBuffer::from_vec( + *width, + *height, + pixels.to_vec(), + ) { + Ok(image_rs::DynamicImage::ImageRgba8(image)) + } else { + Err(image_rs::error::ImageError::Limits( + image_rs::error::LimitError::from_kind( + image_rs::error::LimitErrorKind::DimensionError, + ), + )) + } + } + } +} + +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<R>(reader: &mut R) -> Result<Self, exif::Error> + 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, mut image: image::DynamicImage) -> image::DynamicImage { + use image::imageops; + + if self.contains(Self::FLIP_DIAGONALLY) { + imageops::flip_vertical_in_place(&mut 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 + } +} diff --git a/graphics/src/image/raster.rs b/graphics/src/image/raster.rs deleted file mode 100644 index da46c30f..00000000 --- a/graphics/src/image/raster.rs +++ /dev/null @@ -1,242 +0,0 @@ -//! Raster image loading and caching. -use crate::image::Storage; -use crate::Size; - -use iced_native::image; - -use bitflags::bitflags; -use std::collections::{HashMap, HashSet}; - -/// Entry in cache corresponding to an image handle -#[derive(Debug)] -pub enum Memory<T: Storage> { - /// Image data on host - Host(::image_rs::ImageBuffer<::image_rs::Rgba<u8>, Vec<u8>>), - /// Storage entry - Device(T::Entry), - /// Image not found - NotFound, - /// Invalid image data - Invalid, -} - -impl<T: Storage> Memory<T> { - /// Width and height of image - pub fn dimensions(&self) -> Size<u32> { - use crate::image::storage::Entry; - - match self { - Memory::Host(image) => { - let (width, height) = image.dimensions(); - - Size::new(width, height) - } - Memory::Device(entry) => entry.size(), - Memory::NotFound => Size::new(1, 1), - Memory::Invalid => Size::new(1, 1), - } - } -} - -/// Caches image raster data -#[derive(Debug)] -pub struct Cache<T: Storage> { - map: HashMap<u64, Memory<T>>, - hits: HashSet<u64>, -} - -impl<T: Storage> Cache<T> { - /// Load image - pub fn load(&mut self, handle: &image::Handle) -> &mut Memory<T> { - 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::Rgba { - 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<'_>, - storage: &mut T, - ) -> Option<&T::Entry> { - let memory = self.load(handle); - - if let Memory::Host(image) = memory { - let (width, height) = image.dimensions(); - - let entry = storage.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, storage: &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 { - storage.remove(entry, state); - } - } - - retain - }); - - self.hits.clear(); - } - - fn get(&mut self, handle: &image::Handle) -> Option<&mut Memory<T>> { - let _ = self.hits.insert(handle.id()); - - self.map.get_mut(&handle.id()) - } - - fn insert(&mut self, handle: &image::Handle, memory: Memory<T>) { - let _ = self.map.insert(handle.id(), memory); - } - - fn contains(&self, handle: &image::Handle) -> bool { - self.map.contains_key(&handle.id()) - } -} - -impl<T: Storage> Default for Cache<T> { - 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<R>(reader: &mut R) -> Result<Self, exif::Error> - 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<P>( - self, - image: image_rs::ImageBuffer<P, Vec<P::Subpixel>>, - ) -> image_rs::ImageBuffer<P, Vec<P::Subpixel>> - 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<I>( - image: I, -) -> image_rs::ImageBuffer<I::Pixel, Vec<<I::Pixel as image_rs::Pixel>::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/storage.rs b/graphics/src/image/storage.rs index 1b5b5c35..4caa6141 100644 --- a/graphics/src/image/storage.rs +++ b/graphics/src/image/storage.rs @@ -1,5 +1,5 @@ //! Store images. -use crate::Size; +use iced_core::Size; use std::fmt::Debug; diff --git a/graphics/src/lib.rs b/graphics/src/lib.rs index 41bef2c3..0c50db52 100644 --- a/graphics/src/lib.rs +++ b/graphics/src/lib.rs @@ -9,7 +9,7 @@ )] #![deny( missing_debug_implementations, - missing_docs, + //missing_docs, unsafe_code, unused_results, clippy::extra_unused_lifetimes, @@ -23,32 +23,30 @@ #![cfg_attr(docsrs, feature(doc_cfg))] mod antialiasing; mod error; -mod primitive; mod transformation; mod viewport; pub mod backend; -pub mod gradient; -pub mod image; -pub mod layer; -pub mod overlay; +pub mod compositor; +pub mod primitive; pub mod renderer; -pub mod triangle; -pub mod widget; -pub mod window; + +#[cfg(feature = "geometry")] +pub mod geometry; + +#[cfg(feature = "image")] +pub mod image; pub use antialiasing::Antialiasing; pub use backend::Backend; +pub use compositor::Compositor; pub use error::Error; -pub use gradient::Gradient; -pub use layer::Layer; pub use primitive::Primitive; pub use renderer::Renderer; pub use transformation::Transformation; pub use viewport::Viewport; -pub use window::compositor; -pub use iced_native::alignment; -pub use iced_native::{ - Alignment, Background, Color, Font, Point, Rectangle, Size, Vector, -}; +#[cfg(feature = "geometry")] +pub use geometry::Geometry; + +pub use iced_core as core; diff --git a/graphics/src/overlay.rs b/graphics/src/overlay.rs deleted file mode 100644 index bc0ed744..00000000 --- a/graphics/src/overlay.rs +++ /dev/null @@ -1,2 +0,0 @@ -//! Display interactive elements on top of other widgets. -pub mod menu; diff --git a/graphics/src/overlay/menu.rs b/graphics/src/overlay/menu.rs deleted file mode 100644 index 8b489e5e..00000000 --- a/graphics/src/overlay/menu.rs +++ /dev/null @@ -1,3 +0,0 @@ -//! Build and show dropdown menus. - -pub use iced_style::menu::{Appearance, StyleSheet}; diff --git a/graphics/src/primitive.rs b/graphics/src/primitive.rs index 5a163a2f..195b62da 100644 --- a/graphics/src/primitive.rs +++ b/graphics/src/primitive.rs @@ -1,23 +1,15 @@ -use iced_native::image; -use iced_native::svg; -use iced_native::{Background, Color, Font, Rectangle, Size, Vector}; - -use crate::alignment; -use crate::gradient::Gradient; -use crate::triangle; +use iced_core::alignment; +use iced_core::image; +use iced_core::svg; +use iced_core::{Background, Color, Font, Gradient, Rectangle, Size, Vector}; +use bytemuck::{Pod, Zeroable}; use std::sync::Arc; /// A rendering primitive. #[derive(Debug, Clone)] +#[non_exhaustive] pub enum Primitive { - /// An empty primitive - None, - /// A group of primitives - Group { - /// The primitives of the group - primitives: Vec<Primitive>, - }, /// A text primitive Text { /// The contents of the text @@ -66,27 +58,12 @@ pub enum Primitive { /// The bounds of the viewport bounds: Rectangle, }, - /// A clip primitive - Clip { - /// The bounds of the clip - bounds: Rectangle, - /// The content of the clip - content: Box<Primitive>, - }, - /// A primitive that applies a translation - Translate { - /// The translation vector - translation: Vector, - - /// The primitive to translate - content: Box<Primitive>, - }, /// 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. SolidMesh { /// The vertices and indices of the mesh. - buffers: triangle::Mesh2D<triangle::ColoredVertex2D>, + buffers: Mesh2D<ColoredVertex2D>, /// The size of the drawable region of the mesh. /// @@ -98,7 +75,7 @@ pub enum Primitive { /// It can be used to render many kinds of geometry freely. GradientMesh { /// The vertices and indices of the mesh. - buffers: triangle::Mesh2D<triangle::Vertex2D>, + buffers: Mesh2D<Vertex2D>, /// The size of the drawable region of the mesh. /// @@ -108,18 +85,103 @@ pub enum Primitive { /// The [`Gradient`] to apply to the mesh. gradient: Gradient, }, + #[cfg(feature = "tiny-skia")] + Fill { + path: tiny_skia::Path, + paint: tiny_skia::Paint<'static>, + rule: tiny_skia::FillRule, + transform: tiny_skia::Transform, + }, + #[cfg(feature = "tiny-skia")] + Stroke { + path: tiny_skia::Path, + paint: tiny_skia::Paint<'static>, + stroke: tiny_skia::Stroke, + transform: tiny_skia::Transform, + }, + /// A group of primitives + Group { + /// The primitives of the group + primitives: Vec<Primitive>, + }, + /// A clip primitive + Clip { + /// The bounds of the clip + bounds: Rectangle, + /// The content of the clip + content: Box<Primitive>, + }, + /// A primitive that applies a translation + Translate { + /// The translation vector + translation: Vector, + + /// The primitive to translate + content: Box<Primitive>, + }, /// A cached primitive. /// /// This can be useful if you are implementing a widget where primitive /// generation is expensive. - Cached { + Cache { /// The cached primitive - cache: Arc<Primitive>, + content: Arc<Primitive>, }, } -impl Default for Primitive { - fn default() -> Primitive { - Primitive::None +impl Primitive { + pub fn group(primitives: Vec<Self>) -> Self { + Self::Group { primitives } + } + + pub fn clip(self, bounds: Rectangle) -> Self { + Self::Clip { + bounds, + content: Box::new(self), + } + } + + pub fn translate(self, translation: Vector) -> Self { + Self::Translate { + translation, + content: Box::new(self), + } + } +} + +/// A set of [`Vertex2D`] and indices representing a list of triangles. +#[derive(Clone, Debug)] +pub struct Mesh2D<T> { + /// The vertices of the mesh + pub vertices: Vec<T>, + + /// 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. + pub indices: Vec<u32>, +} + +/// A two-dimensional vertex. +#[derive(Copy, Clone, Debug, Zeroable, Pod)] +#[repr(C)] +pub struct Vertex2D { + /// The vertex position in 2D space. + pub position: [f32; 2], +} + +/// 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], + + /// The color of the vertex in __linear__ RGBA. + pub color: [f32; 4], +} + +impl From<()> for Primitive { + fn from(_: ()) -> Self { + Self::Group { primitives: vec![] } } } diff --git a/graphics/src/renderer.rs b/graphics/src/renderer.rs index b052c094..7bc462ef 100644 --- a/graphics/src/renderer.rs +++ b/graphics/src/renderer.rs @@ -1,14 +1,15 @@ //! Create a renderer from a [`Backend`]. use crate::backend::{self, Backend}; -use crate::{Primitive, Vector}; -use iced_native::image; -use iced_native::layout; -use iced_native::renderer; -use iced_native::svg; -use iced_native::text::{self, Text}; -use iced_native::{Background, Color, Element, Font, Point, Rectangle, Size}; +use crate::Primitive; -pub use iced_native::renderer::Style; +use iced_core::image; +use iced_core::layout; +use iced_core::renderer; +use iced_core::svg; +use iced_core::text::{self, Text}; +use iced_core::{ + Background, Color, Element, Font, Point, Rectangle, Size, Vector, +}; use std::borrow::Cow; use std::marker::PhantomData; @@ -43,12 +44,15 @@ impl<B: Backend, T> Renderer<B, T> { /// Runs the given closure with the [`Backend`] and the recorded primitives /// of the [`Renderer`]. - pub fn with_primitives(&mut self, f: impl FnOnce(&mut B, &[Primitive])) { - f(&mut self.backend, &self.primitives); + pub fn with_primitives<O>( + &mut self, + f: impl FnOnce(&mut B, &[Primitive]) -> O, + ) -> O { + f(&mut self.backend, &self.primitives) } } -impl<B, T> iced_native::Renderer for Renderer<B, T> +impl<B, T> iced_core::Renderer for Renderer<B, T> where B: Backend, { @@ -67,19 +71,13 @@ where } fn with_layer(&mut self, bounds: Rectangle, f: impl FnOnce(&mut Self)) { - let current_primitives = std::mem::take(&mut self.primitives); + let current = std::mem::take(&mut self.primitives); f(self); - let layer_primitives = - std::mem::replace(&mut self.primitives, current_primitives); + let layer = std::mem::replace(&mut self.primitives, current); - self.primitives.push(Primitive::Clip { - bounds, - content: Box::new(Primitive::Group { - primitives: layer_primitives, - }), - }); + self.primitives.push(Primitive::group(layer).clip(bounds)); } fn with_translation( @@ -87,19 +85,14 @@ where translation: Vector, f: impl FnOnce(&mut Self), ) { - let current_primitives = std::mem::take(&mut self.primitives); + let current = std::mem::take(&mut self.primitives); f(self); - let layer_primitives = - std::mem::replace(&mut self.primitives, current_primitives); + let layer = std::mem::replace(&mut self.primitives, current); - self.primitives.push(Primitive::Translate { - translation, - content: Box::new(Primitive::Group { - primitives: layer_primitives, - }), - }); + self.primitives + .push(Primitive::group(layer).translate(translation)); } fn fill_quad( @@ -196,7 +189,7 @@ where } fn draw(&mut self, handle: image::Handle, bounds: Rectangle) { - self.draw_primitive(Primitive::Image { handle, bounds }) + self.primitives.push(Primitive::Image { handle, bounds }) } } @@ -214,10 +207,23 @@ where color: Option<Color>, bounds: Rectangle, ) { - self.draw_primitive(Primitive::Svg { + self.primitives.push(Primitive::Svg { handle, color, bounds, }) } } + +#[cfg(feature = "geometry")] +impl<B, T> crate::geometry::Renderer for Renderer<B, T> +where + B: Backend, +{ + type Geometry = crate::Geometry; + + fn draw(&mut self, layers: Vec<Self::Geometry>) { + self.primitives + .extend(layers.into_iter().map(crate::Geometry::into)); + } +} diff --git a/graphics/src/triangle.rs b/graphics/src/triangle.rs index f52b2339..09b61767 100644 --- a/graphics/src/triangle.rs +++ b/graphics/src/triangle.rs @@ -1,33 +1 @@ //! Draw geometry using meshes of triangles. -use bytemuck::{Pod, Zeroable}; - -/// A set of [`Vertex2D`] and indices representing a list of triangles. -#[derive(Clone, Debug)] -pub struct Mesh2D<T> { - /// The vertices of the mesh - pub vertices: Vec<T>, - - /// 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. - pub indices: Vec<u32>, -} - -/// A two-dimensional vertex. -#[derive(Copy, Clone, Debug, Zeroable, Pod)] -#[repr(C)] -pub struct Vertex2D { - /// The vertex position in 2D space. - pub position: [f32; 2], -} - -/// 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], - - /// The color of the vertex in __linear__ RGBA. - pub color: [f32; 4], -} diff --git a/graphics/src/viewport.rs b/graphics/src/viewport.rs index 2c0b541a..5792555d 100644 --- a/graphics/src/viewport.rs +++ b/graphics/src/viewport.rs @@ -1,4 +1,6 @@ -use crate::{Size, Transformation}; +use crate::Transformation; + +use iced_core::Size; /// A viewing region for displaying computer graphics. #[derive(Debug, Clone)] diff --git a/graphics/src/widget/canvas/geometry.rs b/graphics/src/widget/canvas/geometry.rs deleted file mode 100644 index e8ac621d..00000000 --- a/graphics/src/widget/canvas/geometry.rs +++ /dev/null @@ -1,24 +0,0 @@ -use crate::Primitive; - -/// A bunch of shapes that can be drawn. -/// -/// [`Geometry`] can be easily generated with a [`Frame`] or stored in a -/// [`Cache`]. -/// -/// [`Frame`]: crate::widget::canvas::Frame -/// [`Cache`]: crate::widget::canvas::Cache -#[derive(Debug, Clone)] -pub struct Geometry(Primitive); - -impl Geometry { - pub(crate) fn from_primitive(primitive: Primitive) -> Self { - Self(primitive) - } - - /// Turns the [`Geometry`] into a [`Primitive`]. - /// - /// This can be useful if you are building a custom widget. - pub fn into_primitive(self) -> Primitive { - self.0 - } -} diff --git a/graphics/src/window.rs b/graphics/src/window.rs deleted file mode 100644 index a38b81f3..00000000 --- a/graphics/src/window.rs +++ /dev/null @@ -1,10 +0,0 @@ -//! Draw graphics to window surfaces. -pub mod compositor; - -#[cfg(feature = "opengl")] -pub mod gl_compositor; - -pub use compositor::Compositor; - -#[cfg(feature = "opengl")] -pub use gl_compositor::GLCompositor; diff --git a/graphics/src/window/gl_compositor.rs b/graphics/src/window/gl_compositor.rs deleted file mode 100644 index a45a7ca1..00000000 --- a/graphics/src/window/gl_compositor.rs +++ /dev/null @@ -1,71 +0,0 @@ -//! A compositor is responsible for initializing a renderer and managing window -//! surfaces. -use crate::compositor::Information; -use crate::{Color, Error, Size, Viewport}; - -use core::ffi::c_void; - -/// A basic OpenGL compositor. -/// -/// A compositor is responsible for initializing a renderer and managing window -/// surfaces. -/// -/// For now, this compositor only deals with a single global surface -/// for drawing. However, the trait will most likely change in the near future -/// to handle multiple surfaces at once. -/// -/// If you implement an OpenGL renderer, you can implement this trait to ease -/// integration with existing windowing shells, like `iced_glutin`. -pub trait GLCompositor: Sized { - /// The renderer of the [`GLCompositor`]. - /// - /// This should point to your renderer type, which could be a type alias - /// of the [`Renderer`] provided in this crate with with a specific - /// [`Backend`]. - /// - /// [`Renderer`]: crate::Renderer - /// [`Backend`]: crate::Backend - type Renderer: iced_native::Renderer; - - /// The settings of the [`GLCompositor`]. - /// - /// It's up to you to decide the configuration supported by your renderer! - type Settings: Default; - - /// Creates a new [`GLCompositor`] and [`Renderer`] with the given - /// [`Settings`] and an OpenGL address loader function. - /// - /// # Safety - /// The `loader_function` should resolve to valid OpenGL bindings. - /// - /// [`Renderer`]: crate::Renderer - /// [`Backend`]: crate::Backend - /// [`Settings`]: Self::Settings - #[allow(unsafe_code)] - unsafe fn new( - settings: Self::Settings, - loader_function: impl FnMut(&str) -> *const c_void, - ) -> Result<(Self, Self::Renderer), Error>; - - /// Returns the amount of samples that should be used when configuring - /// an OpenGL context for this [`GLCompositor`]. - fn sample_count(settings: &Self::Settings) -> u32; - - /// Resizes the viewport of the [`GLCompositor`]. - fn resize_viewport(&mut self, physical_size: Size<u32>); - - /// Returns [`Information`] used by this [`GLCompositor`]. - fn fetch_information(&self) -> Information; - - /// Presents the primitives of the [`Renderer`] to the next frame of the - /// [`GLCompositor`]. - /// - /// [`Renderer`]: crate::Renderer - fn present<T: AsRef<str>>( - &mut self, - renderer: &mut Self::Renderer, - viewport: &Viewport, - background_color: Color, - overlay: &[T], - ); -} diff --git a/graphics/src/widget/canvas/cache.rs b/renderer/src/geometry/cache.rs index 52217bbb..2a3534d0 100644 --- a/graphics/src/widget/canvas/cache.rs +++ b/renderer/src/geometry/cache.rs @@ -1,22 +1,11 @@ -use crate::widget::canvas::{Frame, Geometry}; -use crate::Primitive; +use crate::core::Size; +use crate::geometry::{Frame, Geometry}; +use crate::graphics::Primitive; +use crate::Renderer; -use iced_native::Size; -use std::{cell::RefCell, sync::Arc}; +use std::cell::RefCell; +use std::sync::Arc; -enum State { - Empty, - Filled { - bounds: Size, - primitive: Arc<Primitive>, - }, -} - -impl Default for State { - fn default() -> Self { - State::Empty - } -} /// A simple cache that stores generated [`Geometry`] to avoid recomputation. /// /// A [`Cache`] will not redraw its geometry unless the dimensions of its layer @@ -26,6 +15,16 @@ pub struct Cache { state: RefCell<State>, } +#[derive(Debug, Default)] +enum State { + #[default] + Empty, + Filled { + bounds: Size, + primitive: Arc<Primitive>, + }, +} + impl Cache { /// Creates a new empty [`Cache`]. pub fn new() -> Self { @@ -49,8 +48,9 @@ impl Cache { /// Otherwise, the previously stored [`Geometry`] will be returned. The /// [`Cache`] is not cleared in this case. In other words, it will keep /// returning the stored [`Geometry`] if needed. - pub fn draw( + pub fn draw<Theme>( &self, + renderer: &Renderer<Theme>, bounds: Size, draw_fn: impl FnOnce(&mut Frame), ) -> Geometry { @@ -62,19 +62,19 @@ impl Cache { } = self.state.borrow().deref() { if *cached_bounds == bounds { - return Geometry::from_primitive(Primitive::Cached { - cache: primitive.clone(), + return Geometry(Primitive::Cache { + content: primitive.clone(), }); } } - let mut frame = Frame::new(bounds); + let mut frame = Frame::new(renderer, bounds); draw_fn(&mut frame); let primitive = { let geometry = frame.into_geometry(); - Arc::new(geometry.into_primitive()) + Arc::new(geometry.0) }; *self.state.borrow_mut() = State::Filled { @@ -82,19 +82,6 @@ impl Cache { primitive: primitive.clone(), }; - Geometry::from_primitive(Primitive::Cached { cache: primitive }) - } -} - -impl std::fmt::Debug for State { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - State::Empty => write!(f, "Empty"), - State::Filled { primitive, bounds } => f - .debug_struct("Filled") - .field("primitive", primitive) - .field("bounds", bounds) - .finish(), - } + Geometry(Primitive::Cache { content: primitive }) } } diff --git a/graphics/src/widget.rs b/renderer/src/widget.rs index e7fab97c..6c0c2a83 100644 --- a/graphics/src/widget.rs +++ b/renderer/src/widget.rs @@ -1,16 +1,11 @@ -//! Use the graphical widgets supported out-of-the-box. #[cfg(feature = "canvas")] -#[cfg_attr(docsrs, doc(cfg(feature = "canvas")))] pub mod canvas; #[cfg(feature = "canvas")] -#[doc(no_inline)] pub use canvas::Canvas; #[cfg(feature = "qr_code")] -#[cfg_attr(docsrs, doc(cfg(feature = "qr_code")))] pub mod qr_code; #[cfg(feature = "qr_code")] -#[doc(no_inline)] pub use qr_code::QRCode; diff --git a/graphics/src/widget/canvas/frame.rs b/wgpu/src/geometry.rs index d68548ae..59ec31fe 100644 --- a/graphics/src/widget/canvas/frame.rs +++ b/wgpu/src/geometry.rs @@ -1,9 +1,9 @@ -use crate::gradient::Gradient; -use crate::triangle; -use crate::widget::canvas::{path, Fill, Geometry, Path, Stroke, Style, Text}; -use crate::Primitive; - -use iced_native::{Point, Rectangle, Size, Vector}; +use crate::core::{Gradient, Point, Rectangle, Size, Vector}; +use crate::graphics::geometry::fill::{self, Fill}; +use crate::graphics::geometry::{ + LineCap, LineDash, LineJoin, Path, Stroke, Style, Text, +}; +use crate::graphics::primitive::{self, Primitive}; use lyon::geom::euclid; use lyon::tessellation; @@ -23,9 +23,9 @@ pub struct Frame { } enum Buffer { - Solid(tessellation::VertexBuffers<triangle::ColoredVertex2D, u32>), + Solid(tessellation::VertexBuffers<primitive::ColoredVertex2D, u32>), Gradient( - tessellation::VertexBuffers<triangle::Vertex2D, u32>, + tessellation::VertexBuffers<primitive::Vertex2D, u32>, Gradient, ), } @@ -196,8 +196,8 @@ impl Frame { .buffers .get_fill(&self.transforms.current.transform_style(style)); - let options = - tessellation::FillOptions::default().with_fill_rule(rule.into()); + let options = tessellation::FillOptions::default() + .with_fill_rule(into_fill_rule(rule)); if self.transforms.current.is_identity { self.fill_tessellator.tessellate_path( @@ -206,7 +206,7 @@ impl Frame { buffer.as_mut(), ) } else { - let path = path.transformed(&self.transforms.current.raw); + let path = path.transform(&self.transforms.current.raw); self.fill_tessellator.tessellate_path( path.raw(), @@ -241,8 +241,8 @@ impl Frame { lyon::math::Vector::new(size.width, size.height), ); - let options = - tessellation::FillOptions::default().with_fill_rule(rule.into()); + let options = tessellation::FillOptions::default() + .with_fill_rule(into_fill_rule(rule)); self.fill_tessellator .tessellate_rectangle( @@ -264,14 +264,14 @@ impl Frame { let mut options = tessellation::StrokeOptions::default(); options.line_width = stroke.width; - options.start_cap = stroke.line_cap.into(); - options.end_cap = stroke.line_cap.into(); - options.line_join = stroke.line_join.into(); + options.start_cap = into_line_cap(stroke.line_cap); + options.end_cap = into_line_cap(stroke.line_cap); + options.line_join = into_line_join(stroke.line_join); let path = if stroke.line_dash.segments.is_empty() { Cow::Borrowed(path) } else { - Cow::Owned(path::dashed(path, stroke.line_dash)) + Cow::Owned(dashed(path, stroke.line_dash)) }; if self.transforms.current.is_identity { @@ -281,7 +281,7 @@ impl Frame { buffer.as_mut(), ) } else { - let path = path.transformed(&self.transforms.current.raw); + let path = path.transform(&self.transforms.current.raw); self.stroke_tessellator.tessellate_path( path.raw(), @@ -344,10 +344,18 @@ impl Frame { /// operations in different coordinate systems. #[inline] pub fn with_save(&mut self, f: impl FnOnce(&mut Frame)) { - self.transforms.previous.push(self.transforms.current); + self.push_transform(); f(self); + self.pop_transform(); + } + + pub fn push_transform(&mut self) { + self.transforms.previous.push(self.transforms.current); + } + + pub fn pop_transform(&mut self) { self.transforms.current = self.transforms.previous.pop().unwrap(); } @@ -363,14 +371,19 @@ impl Frame { f(&mut frame); + let translation = Vector::new(region.x, region.y); + + self.clip(frame, translation); + } + + pub fn clip(&mut self, frame: Frame, translation: Vector) { + let size = frame.size(); let primitives = frame.into_primitives(); let (text, meshes) = primitives .into_iter() .partition(|primitive| matches!(primitive, Primitive::Text { .. })); - let translation = Vector::new(region.x, region.y); - self.primitives.push(Primitive::Group { primitives: vec![ Primitive::Translate { @@ -380,7 +393,7 @@ impl Frame { Primitive::Translate { translation, content: Box::new(Primitive::Clip { - bounds: Rectangle::with_size(region.size()), + bounds: Rectangle::with_size(size), content: Box::new(Primitive::Group { primitives: text, }), @@ -423,11 +436,11 @@ impl Frame { self.transforms.current.is_identity = false; } - /// Produces the [`Geometry`] representing everything drawn on the [`Frame`]. - pub fn into_geometry(self) -> Geometry { - Geometry::from_primitive(Primitive::Group { + /// Produces the [`Primitive`] representing everything drawn on the [`Frame`]. + pub fn into_primitive(self) -> Primitive { + Primitive::Group { primitives: self.into_primitives(), - }) + } } fn into_primitives(mut self) -> Vec<Primitive> { @@ -436,7 +449,7 @@ impl Frame { Buffer::Solid(buffer) => { if !buffer.indices.is_empty() { self.primitives.push(Primitive::SolidMesh { - buffers: triangle::Mesh2D { + buffers: primitive::Mesh2D { vertices: buffer.vertices, indices: buffer.indices, }, @@ -447,7 +460,7 @@ impl Frame { Buffer::Gradient(buffer, gradient) => { if !buffer.indices.is_empty() { self.primitives.push(Primitive::GradientMesh { - buffers: triangle::Mesh2D { + buffers: primitive::Mesh2D { vertices: buffer.vertices, indices: buffer.indices, }, @@ -465,31 +478,31 @@ impl Frame { struct Vertex2DBuilder; -impl tessellation::FillVertexConstructor<triangle::Vertex2D> +impl tessellation::FillVertexConstructor<primitive::Vertex2D> for Vertex2DBuilder { fn new_vertex( &mut self, vertex: tessellation::FillVertex<'_>, - ) -> triangle::Vertex2D { + ) -> primitive::Vertex2D { let position = vertex.position(); - triangle::Vertex2D { + primitive::Vertex2D { position: [position.x, position.y], } } } -impl tessellation::StrokeVertexConstructor<triangle::Vertex2D> +impl tessellation::StrokeVertexConstructor<primitive::Vertex2D> for Vertex2DBuilder { fn new_vertex( &mut self, vertex: tessellation::StrokeVertex<'_, '_>, - ) -> triangle::Vertex2D { + ) -> primitive::Vertex2D { let position = vertex.position(); - triangle::Vertex2D { + primitive::Vertex2D { position: [position.x, position.y], } } @@ -497,34 +510,99 @@ impl tessellation::StrokeVertexConstructor<triangle::Vertex2D> struct TriangleVertex2DBuilder([f32; 4]); -impl tessellation::FillVertexConstructor<triangle::ColoredVertex2D> +impl tessellation::FillVertexConstructor<primitive::ColoredVertex2D> for TriangleVertex2DBuilder { fn new_vertex( &mut self, vertex: tessellation::FillVertex<'_>, - ) -> triangle::ColoredVertex2D { + ) -> primitive::ColoredVertex2D { let position = vertex.position(); - triangle::ColoredVertex2D { + primitive::ColoredVertex2D { position: [position.x, position.y], color: self.0, } } } -impl tessellation::StrokeVertexConstructor<triangle::ColoredVertex2D> +impl tessellation::StrokeVertexConstructor<primitive::ColoredVertex2D> for TriangleVertex2DBuilder { fn new_vertex( &mut self, vertex: tessellation::StrokeVertex<'_, '_>, - ) -> triangle::ColoredVertex2D { + ) -> primitive::ColoredVertex2D { let position = vertex.position(); - triangle::ColoredVertex2D { + primitive::ColoredVertex2D { position: [position.x, position.y], color: self.0, } } } + +fn into_line_join(line_join: LineJoin) -> lyon::tessellation::LineJoin { + match line_join { + LineJoin::Miter => lyon::tessellation::LineJoin::Miter, + LineJoin::Round => lyon::tessellation::LineJoin::Round, + LineJoin::Bevel => lyon::tessellation::LineJoin::Bevel, + } +} + +fn into_line_cap(line_cap: LineCap) -> lyon::tessellation::LineCap { + match line_cap { + LineCap::Butt => lyon::tessellation::LineCap::Butt, + LineCap::Square => lyon::tessellation::LineCap::Square, + LineCap::Round => lyon::tessellation::LineCap::Round, + } +} + +fn into_fill_rule(rule: fill::Rule) -> lyon::tessellation::FillRule { + match rule { + fill::Rule::NonZero => lyon::tessellation::FillRule::NonZero, + fill::Rule::EvenOdd => lyon::tessellation::FillRule::EvenOdd, + } +} + +pub(super) fn dashed(path: &Path, line_dash: LineDash<'_>) -> Path { + use lyon::algorithms::walk::{ + walk_along_path, RepeatedPattern, WalkerEvent, + }; + use lyon::path::iterator::PathIterator; + + Path::new(|builder| { + let segments_odd = (line_dash.segments.len() % 2 == 1) + .then(|| [line_dash.segments, line_dash.segments].concat()); + + let mut draw_line = false; + + walk_along_path( + path.raw().iter().flattened(0.01), + 0.0, + lyon::tessellation::StrokeOptions::DEFAULT_TOLERANCE, + &mut RepeatedPattern { + callback: |event: WalkerEvent<'_>| { + let point = Point { + x: event.position.x, + y: event.position.y, + }; + + if draw_line { + builder.line_to(point); + } else { + builder.move_to(point); + } + + draw_line = !draw_line; + + true + }, + index: line_dash.offset, + intervals: segments_odd + .as_deref() + .unwrap_or(line_dash.segments), + }, + ); + }) +} diff --git a/graphics/src/image/vector.rs b/wgpu/src/image/vector.rs index 82d77aff..3624e46b 100644 --- a/graphics/src/image/vector.rs +++ b/wgpu/src/image/vector.rs @@ -1,10 +1,9 @@ -//! Vector image loading and caching -use crate::image::Storage; -use crate::Color; - -use iced_native::svg; -use iced_native::Size; +use crate::core::svg; +use crate::core::{Color, Size}; +use crate::image::atlas::{self, Atlas}; +use resvg::tiny_skia; +use resvg::usvg; use std::collections::{HashMap, HashSet}; use std::fs; @@ -21,7 +20,7 @@ impl Svg { pub fn viewport_dimensions(&self) -> Size<u32> { match self { Svg::Loaded(tree) => { - let size = tree.svg_node().size; + let size = tree.size; Size::new(size.width() as u32, size.height() as u32) } @@ -31,17 +30,17 @@ impl Svg { } /// Caches svg vector and raster data -#[derive(Debug)] -pub struct Cache<T: Storage> { +#[derive(Debug, Default)] +pub struct Cache { svgs: HashMap<u64, Svg>, - rasterized: HashMap<(u64, u32, u32, ColorFilter), T::Entry>, + rasterized: HashMap<(u64, u32, u32, ColorFilter), atlas::Entry>, svg_hits: HashSet<u64>, rasterized_hits: HashSet<(u64, u32, u32, ColorFilter)>, } type ColorFilter = Option<[u8; 4]>; -impl<T: Storage> Cache<T> { +impl Cache { /// Load svg pub fn load(&mut self, handle: &svg::Handle) -> &Svg { if self.svgs.contains_key(&handle.id()) { @@ -51,20 +50,14 @@ impl<T: Storage> Cache<T> { 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() + usvg::Tree::from_str(&contents, &usvg::Options::default()) + .ok() }); tree.map(Svg::Loaded).unwrap_or(Svg::NotFound) } svg::Data::Bytes(bytes) => { - match usvg::Tree::from_data( - bytes, - &usvg::Options::default().to_ref(), - ) { + match usvg::Tree::from_data(bytes, &usvg::Options::default()) { Ok(tree) => Svg::Loaded(tree), Err(_) => Svg::NotFound, } @@ -78,13 +71,15 @@ impl<T: Storage> Cache<T> { /// Load svg and upload raster data pub fn upload( &mut self, + device: &wgpu::Device, + queue: &wgpu::Queue, + encoder: &mut wgpu::CommandEncoder, handle: &svg::Handle, color: Option<Color>, [width, height]: [f32; 2], scale: f32, - state: &mut T::State<'_>, - storage: &mut T, - ) -> Option<&T::Entry> { + atlas: &mut Atlas, + ) -> Option<&atlas::Entry> { let id = handle.id(); let (width, height) = ( @@ -125,6 +120,7 @@ impl<T: Storage> Cache<T> { } else { usvg::FitTo::Height(height) }, + tiny_skia::Transform::default(), img.as_mut(), )?; @@ -140,7 +136,9 @@ impl<T: Storage> Cache<T> { }); } - let allocation = storage.upload(width, height, &rgba, state)?; + let allocation = atlas + .upload(device, queue, encoder, width, height, &rgba)?; + log::debug!("allocating {} {}x{}", id, width, height); let _ = self.svg_hits.insert(id); @@ -154,7 +152,7 @@ impl<T: Storage> Cache<T> { } /// Load svg and upload raster data - pub fn trim(&mut self, storage: &mut T, state: &mut T::State<'_>) { + pub fn trim(&mut self, atlas: &mut Atlas) { let svg_hits = &self.svg_hits; let rasterized_hits = &self.rasterized_hits; @@ -163,7 +161,7 @@ impl<T: Storage> Cache<T> { let retain = rasterized_hits.contains(k); if !retain { - storage.remove(entry, state); + atlas.remove(entry); } retain @@ -173,17 +171,6 @@ impl<T: Storage> Cache<T> { } } -impl<T: Storage> Default for Cache<T> { - 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 { diff --git a/graphics/src/layer.rs b/wgpu/src/layer.rs index f6eb2fdd..cb9d5e2f 100644 --- a/graphics/src/layer.rs +++ b/wgpu/src/layer.rs @@ -10,11 +10,9 @@ pub use mesh::Mesh; pub use quad::Quad; pub use text::Text; -use crate::alignment; -use crate::{ - Background, Color, Font, Point, Primitive, Rectangle, Size, Vector, - Viewport, -}; +use crate::core::alignment; +use crate::core::{Background, Color, Font, Point, Rectangle, Size, Vector}; +use crate::graphics::{Primitive, Viewport}; /// A group of primitives that should be clipped together. #[derive(Debug)] @@ -110,18 +108,6 @@ impl<'a> Layer<'a> { current_layer: usize, ) { match primitive { - Primitive::None => {} - Primitive::Group { primitives } => { - // TODO: Inspect a bit and regroup (?) - for primitive in primitives { - Self::process_primitive( - layers, - translation, - primitive, - current_layer, - ) - } - } Primitive::Text { content, bounds, @@ -167,6 +153,27 @@ impl<'a> Layer<'a> { border_color: border_color.into_linear(), }); } + Primitive::Image { handle, bounds } => { + let layer = &mut layers[current_layer]; + + layer.images.push(Image::Raster { + handle: handle.clone(), + bounds: *bounds + translation, + }); + } + Primitive::Svg { + handle, + color, + bounds, + } => { + let layer = &mut layers[current_layer]; + + layer.images.push(Image::Vector { + handle: handle.clone(), + color: *color, + bounds: *bounds + translation, + }); + } Primitive::SolidMesh { buffers, size } => { let layer = &mut layers[current_layer]; @@ -206,6 +213,17 @@ impl<'a> Layer<'a> { }); } } + Primitive::Group { primitives } => { + // TODO: Inspect a bit and regroup (?) + for primitive in primitives { + Self::process_primitive( + layers, + translation, + primitive, + current_layer, + ) + } + } Primitive::Clip { bounds, content } => { let layer = &mut layers[current_layer]; let translated_bounds = *bounds + translation; @@ -236,34 +254,16 @@ impl<'a> Layer<'a> { current_layer, ); } - Primitive::Cached { cache } => { + Primitive::Cache { content } => { Self::process_primitive( layers, translation, - cache, + content, current_layer, ); } - Primitive::Image { handle, bounds } => { - let layer = &mut layers[current_layer]; - - layer.images.push(Image::Raster { - handle: handle.clone(), - bounds: *bounds + translation, - }); - } - Primitive::Svg { - handle, - color, - bounds, - } => { - let layer = &mut layers[current_layer]; - - layer.images.push(Image::Vector { - handle: handle.clone(), - color: *color, - bounds: *bounds + translation, - }); + _ => { + // Unsupported! } } } diff --git a/graphics/src/layer/image.rs b/wgpu/src/layer/image.rs index 3eff2397..0de589f8 100644 --- a/graphics/src/layer/image.rs +++ b/wgpu/src/layer/image.rs @@ -1,6 +1,6 @@ -use crate::{Color, Rectangle}; - -use iced_native::{image, svg}; +use crate::core::image; +use crate::core::svg; +use crate::core::{Color, Rectangle}; /// A raster or vector image. #[derive(Debug, Clone)] diff --git a/graphics/src/layer/mesh.rs b/wgpu/src/layer/mesh.rs index 7661c5c9..9dd14391 100644 --- a/graphics/src/layer/mesh.rs +++ b/wgpu/src/layer/mesh.rs @@ -1,6 +1,6 @@ //! A collection of triangle primitives. -use crate::triangle; -use crate::{Gradient, Point, Rectangle}; +use crate::core::{Gradient, Point, Rectangle}; +use crate::graphics::primitive; /// A mesh of triangles. #[derive(Debug, Clone, Copy)] @@ -11,7 +11,7 @@ pub enum Mesh<'a> { origin: Point, /// The vertex and index buffers of the [`Mesh`]. - buffers: &'a triangle::Mesh2D<triangle::ColoredVertex2D>, + buffers: &'a primitive::Mesh2D<primitive::ColoredVertex2D>, /// The clipping bounds of the [`Mesh`]. clip_bounds: Rectangle<f32>, @@ -22,7 +22,7 @@ pub enum Mesh<'a> { origin: Point, /// The vertex and index buffers of the [`Mesh`]. - buffers: &'a triangle::Mesh2D<triangle::Vertex2D>, + buffers: &'a primitive::Mesh2D<primitive::Vertex2D>, /// The clipping bounds of the [`Mesh`]. clip_bounds: Rectangle<f32>, diff --git a/graphics/src/layer/quad.rs b/wgpu/src/layer/quad.rs index 0d8bde9d..0d8bde9d 100644 --- a/graphics/src/layer/quad.rs +++ b/wgpu/src/layer/quad.rs diff --git a/graphics/src/layer/text.rs b/wgpu/src/layer/text.rs index 38d62616..fdbdaafb 100644 --- a/graphics/src/layer/text.rs +++ b/wgpu/src/layer/text.rs @@ -1,4 +1,5 @@ -use crate::{alignment, Color, Font, Rectangle}; +use crate::core::alignment; +use crate::core::{Color, Font, Rectangle}; /// A paragraph of text. #[derive(Debug, Clone, Copy)] diff --git a/graphics/src/widget/canvas.rs b/widget/src/canvas.rs index a8d050f5..171c4534 100644 --- a/graphics/src/widget/canvas.rs +++ b/widget/src/canvas.rs @@ -1,43 +1,24 @@ //! Draw 2D graphics for your users. -//! -//! A [`Canvas`] widget can be used to draw different kinds of 2D shapes in a -//! [`Frame`]. It can be used for animation, data visualization, game graphics, -//! and more! pub mod event; -pub mod fill; -pub mod path; -pub mod stroke; -mod cache; mod cursor; -mod frame; -mod geometry; mod program; -mod style; -mod text; -pub use crate::gradient::{self, Gradient}; -pub use cache::Cache; pub use cursor::Cursor; pub use event::Event; -pub use fill::{Fill, FillRule}; -pub use frame::Frame; -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}; +pub use crate::graphics::geometry::*; +pub use crate::renderer::geometry::*; -use iced_native::layout::{self, Layout}; -use iced_native::mouse; -use iced_native::renderer; -use iced_native::widget::tree::{self, Tree}; -use iced_native::{ - Clipboard, Element, Length, Point, Rectangle, Shell, Size, Vector, Widget, -}; +use crate::core; +use crate::core::layout::{self, Layout}; +use crate::core::mouse; +use crate::core::renderer; +use crate::core::widget::tree::{self, Tree}; +use crate::core::{Clipboard, Element, Shell, Widget}; +use crate::core::{Length, Point, Rectangle, Size, Vector}; +use crate::graphics::geometry; use std::marker::PhantomData; @@ -47,15 +28,11 @@ use std::marker::PhantomData; /// If you want to get a quick overview, here's how we can draw a simple circle: /// /// ```no_run -/// # mod iced { -/// # pub mod widget { -/// # pub use iced_graphics::widget::canvas; -/// # } -/// # pub use iced_native::{Color, Rectangle, Theme}; -/// # } -/// use iced::widget::canvas::{self, Canvas, Cursor, Fill, Frame, Geometry, Path, Program}; -/// use iced::{Color, Rectangle, Theme}; -/// +/// # use iced_widget::canvas::{self, Canvas, Cursor, Fill, Frame, Geometry, Path, Program}; +/// # use iced_widget::core::{Color, Rectangle}; +/// # use iced_widget::style::Theme; +/// # +/// # pub type Renderer = iced_widget::renderer::Renderer<Theme>; /// // First, we define the data we need for drawing /// #[derive(Debug)] /// struct Circle { @@ -66,9 +43,9 @@ use std::marker::PhantomData; /// impl Program<()> for Circle { /// type State = (); /// -/// fn draw(&self, _state: &(), _theme: &Theme, bounds: Rectangle, _cursor: Cursor) -> Vec<Geometry>{ +/// fn draw(&self, _state: &(), renderer: &Renderer, _theme: &Theme, bounds: Rectangle, _cursor: Cursor) -> Vec<Geometry>{ /// // We prepare a new `Frame` -/// let mut frame = Frame::new(bounds.size()); +/// let mut frame = Frame::new(renderer, bounds.size()); /// /// // We create a `Path` representing a simple circle /// let circle = Path::circle(frame.center(), self.radius); @@ -85,20 +62,22 @@ use std::marker::PhantomData; /// let canvas = Canvas::new(Circle { radius: 50.0 }); /// ``` #[derive(Debug)] -pub struct Canvas<Message, Theme, P> +pub struct Canvas<P, Message, Renderer = crate::Renderer> where - P: Program<Message, Theme>, + Renderer: geometry::Renderer, + P: Program<Message, Renderer>, { width: Length, height: Length, program: P, message_: PhantomData<Message>, - theme_: PhantomData<Theme>, + theme_: PhantomData<Renderer>, } -impl<Message, Theme, P> Canvas<Message, Theme, P> +impl<P, Message, Renderer> Canvas<P, Message, Renderer> where - P: Program<Message, Theme>, + Renderer: geometry::Renderer, + P: Program<Message, Renderer>, { const DEFAULT_SIZE: f32 = 100.0; @@ -126,10 +105,11 @@ where } } -impl<Message, P, B, T> Widget<Message, Renderer<B, T>> for Canvas<Message, T, P> +impl<P, Message, Renderer> Widget<Message, Renderer> + for Canvas<P, Message, Renderer> where - P: Program<Message, T>, - B: Backend, + Renderer: geometry::Renderer, + P: Program<Message, Renderer>, { fn tag(&self) -> tree::Tag { struct Tag<T>(T); @@ -150,7 +130,7 @@ where fn layout( &self, - _renderer: &Renderer<B, T>, + _renderer: &Renderer, limits: &layout::Limits, ) -> layout::Node { let limits = limits.width(self.width).height(self.height); @@ -162,23 +142,19 @@ where fn on_event( &mut self, tree: &mut Tree, - event: iced_native::Event, + event: core::Event, layout: Layout<'_>, cursor_position: Point, - _renderer: &Renderer<B, T>, + _renderer: &Renderer, _clipboard: &mut dyn Clipboard, shell: &mut Shell<'_, Message>, ) -> event::Status { let bounds = layout.bounds(); let canvas_event = match event { - iced_native::Event::Mouse(mouse_event) => { - Some(Event::Mouse(mouse_event)) - } - iced_native::Event::Touch(touch_event) => { - Some(Event::Touch(touch_event)) - } - iced_native::Event::Keyboard(keyboard_event) => { + core::Event::Mouse(mouse_event) => Some(Event::Mouse(mouse_event)), + core::Event::Touch(touch_event) => Some(Event::Touch(touch_event)), + core::Event::Keyboard(keyboard_event) => { Some(Event::Keyboard(keyboard_event)) } _ => None, @@ -208,7 +184,7 @@ where layout: Layout<'_>, cursor_position: Point, _viewport: &Rectangle, - _renderer: &Renderer<B, T>, + _renderer: &Renderer, ) -> mouse::Interaction { let bounds = layout.bounds(); let cursor = Cursor::from_window_position(cursor_position); @@ -220,49 +196,43 @@ where fn draw( &self, tree: &Tree, - renderer: &mut Renderer<B, T>, - theme: &T, + renderer: &mut Renderer, + theme: &Renderer::Theme, _style: &renderer::Style, layout: Layout<'_>, cursor_position: Point, _viewport: &Rectangle, ) { - use iced_native::Renderer as _; - let bounds = layout.bounds(); if bounds.width < 1.0 || bounds.height < 1.0 { return; } - let translation = Vector::new(bounds.x, bounds.y); let cursor = Cursor::from_window_position(cursor_position); let state = tree.state.downcast_ref::<P::State>(); - renderer.with_translation(translation, |renderer| { - renderer.draw_primitive(Primitive::Group { - primitives: self - .program - .draw(state, theme, bounds, cursor) - .into_iter() - .map(Geometry::into_primitive) - .collect(), - }); - }); + renderer.with_translation( + Vector::new(bounds.x, bounds.y), + |renderer| { + renderer.draw( + self.program.draw(state, renderer, theme, bounds, cursor), + ); + }, + ); } } -impl<'a, Message, P, B, T> From<Canvas<Message, T, P>> - for Element<'a, Message, Renderer<B, T>> +impl<'a, P, Message, Renderer> From<Canvas<P, Message, Renderer>> + for Element<'a, Message, Renderer> where Message: 'a, - P: Program<Message, T> + 'a, - B: Backend, - T: 'a, + Renderer: 'a + geometry::Renderer, + P: Program<Message, Renderer> + 'a, { fn from( - canvas: Canvas<Message, T, P>, - ) -> Element<'a, Message, Renderer<B, T>> { + canvas: Canvas<P, Message, Renderer>, + ) -> Element<'a, Message, Renderer> { Element::new(canvas) } } diff --git a/graphics/src/widget/canvas/cursor.rs b/widget/src/canvas/cursor.rs index 9588d129..5a65e9a7 100644 --- a/graphics/src/widget/canvas/cursor.rs +++ b/widget/src/canvas/cursor.rs @@ -1,4 +1,4 @@ -use iced_native::{Point, Rectangle}; +use crate::core::{Point, Rectangle}; /// The mouse cursor state. #[derive(Debug, Clone, Copy, PartialEq)] diff --git a/graphics/src/widget/canvas/event.rs b/widget/src/canvas/event.rs index 7c733a4d..4508c184 100644 --- a/graphics/src/widget/canvas/event.rs +++ b/widget/src/canvas/event.rs @@ -1,9 +1,9 @@ //! Handle events of a canvas. -use iced_native::keyboard; -use iced_native::mouse; -use iced_native::touch; +use crate::core::keyboard; +use crate::core::mouse; +use crate::core::touch; -pub use iced_native::event::Status; +pub use crate::core::event::Status; /// A [`Canvas`] event. /// diff --git a/graphics/src/widget/canvas/program.rs b/widget/src/canvas/program.rs index 656dbfa6..efb33c56 100644 --- a/graphics/src/widget/canvas/program.rs +++ b/widget/src/canvas/program.rs @@ -1,7 +1,8 @@ -use crate::widget::canvas::event::{self, Event}; -use crate::widget::canvas::mouse; -use crate::widget::canvas::{Cursor, Geometry}; -use crate::Rectangle; +use crate::canvas::event::{self, Event}; +use crate::canvas::mouse; +use crate::canvas::Cursor; +use crate::core::Rectangle; +use crate::graphics::geometry; /// The state and logic of a [`Canvas`]. /// @@ -9,7 +10,10 @@ use crate::Rectangle; /// application. /// /// [`Canvas`]: crate::widget::Canvas -pub trait Program<Message, Theme = iced_native::Theme> { +pub trait Program<Message, Renderer = crate::Renderer> +where + Renderer: geometry::Renderer, +{ /// The internal state mutated by the [`Program`]. type State: Default + 'static; @@ -44,10 +48,11 @@ pub trait Program<Message, Theme = iced_native::Theme> { fn draw( &self, state: &Self::State, - theme: &Theme, + renderer: &Renderer, + theme: &Renderer::Theme, bounds: Rectangle, cursor: Cursor, - ) -> Vec<Geometry>; + ) -> Vec<Renderer::Geometry>; /// Returns the current mouse interaction of the [`Program`]. /// @@ -65,9 +70,10 @@ pub trait Program<Message, Theme = iced_native::Theme> { } } -impl<Message, Theme, T> Program<Message, Theme> for &T +impl<Message, Renderer, T> Program<Message, Renderer> for &T where - T: Program<Message, Theme>, + Renderer: geometry::Renderer, + T: Program<Message, Renderer>, { type State = T::State; @@ -84,11 +90,12 @@ where fn draw( &self, state: &Self::State, - theme: &Theme, + renderer: &Renderer, + theme: &Renderer::Theme, bounds: Rectangle, cursor: Cursor, - ) -> Vec<Geometry> { - T::draw(self, state, theme, bounds, cursor) + ) -> Vec<Renderer::Geometry> { + T::draw(self, state, renderer, theme, bounds, cursor) } fn mouse_interaction( diff --git a/graphics/src/widget/qr_code.rs b/widget/src/qr_code.rs index 12ce5b1f..7709125f 100644 --- a/graphics/src/widget/qr_code.rs +++ b/widget/src/qr_code.rs @@ -1,13 +1,12 @@ //! Encode and display information in a QR code. -use crate::renderer::{self, Renderer}; -use crate::widget::canvas; -use crate::Backend; - -use iced_native::layout; -use iced_native::widget::Tree; -use iced_native::{ +use crate::canvas; +use crate::core::layout; +use crate::core::renderer::{self, Renderer as _}; +use crate::core::widget::Tree; +use crate::core::{ Color, Element, Layout, Length, Point, Rectangle, Size, Vector, Widget, }; +use crate::Renderer; use thiserror::Error; const DEFAULT_CELL_SIZE: u16 = 4; @@ -48,10 +47,7 @@ impl<'a> QRCode<'a> { } } -impl<'a, Message, B, T> Widget<Message, Renderer<B, T>> for QRCode<'a> -where - B: Backend, -{ +impl<'a, Message, Theme> Widget<Message, Renderer<Theme>> for QRCode<'a> { fn width(&self) -> Length { Length::Shrink } @@ -62,7 +58,7 @@ where fn layout( &self, - _renderer: &Renderer<B, T>, + _renderer: &Renderer<Theme>, _limits: &layout::Limits, ) -> layout::Node { let side_length = (self.state.width + 2 * QUIET_ZONE) as f32 @@ -74,63 +70,63 @@ where fn draw( &self, _state: &Tree, - renderer: &mut Renderer<B, T>, - _theme: &T, + renderer: &mut Renderer<Theme>, + _theme: &Theme, _style: &renderer::Style, layout: Layout<'_>, _cursor_position: Point, _viewport: &Rectangle, ) { - use iced_native::Renderer as _; - let bounds = layout.bounds(); let side_length = self.state.width + 2 * QUIET_ZONE; // Reuse cache if possible - let geometry = self.state.cache.draw(bounds.size(), |frame| { - // Scale units to cell size - frame.scale(f32::from(self.cell_size)); - - // Draw background - frame.fill_rectangle( - Point::ORIGIN, - Size::new(side_length as f32, side_length as f32), - self.light, - ); - - // Avoid drawing on the quiet zone - frame.translate(Vector::new(QUIET_ZONE as f32, QUIET_ZONE as f32)); - - // Draw contents - self.state - .contents - .iter() - .enumerate() - .filter(|(_, value)| **value == qrcode::Color::Dark) - .for_each(|(index, _)| { - let row = index / self.state.width; - let column = index % self.state.width; - - frame.fill_rectangle( - Point::new(column as f32, row as f32), - Size::UNIT, - self.dark, - ); - }); - }); + let geometry = + self.state.cache.draw(renderer, bounds.size(), |frame| { + // Scale units to cell size + frame.scale(f32::from(self.cell_size)); + + // Draw background + frame.fill_rectangle( + Point::ORIGIN, + Size::new(side_length as f32, side_length as f32), + self.light, + ); + + // Avoid drawing on the quiet zone + frame.translate(Vector::new( + QUIET_ZONE as f32, + QUIET_ZONE as f32, + )); + + // Draw contents + self.state + .contents + .iter() + .enumerate() + .filter(|(_, value)| **value == qrcode::Color::Dark) + .for_each(|(index, _)| { + let row = index / self.state.width; + let column = index % self.state.width; + + frame.fill_rectangle( + Point::new(column as f32, row as f32), + Size::UNIT, + self.dark, + ); + }); + }); let translation = Vector::new(bounds.x, bounds.y); renderer.with_translation(translation, |renderer| { - renderer.draw_primitive(geometry.into_primitive()); + renderer.draw_primitive(geometry.0); }); } } -impl<'a, Message, B, T> From<QRCode<'a>> - for Element<'a, Message, Renderer<B, T>> -where - B: Backend, +impl<'a, Message, Theme> From<QRCode<'a>> + for Element<'a, Message, Renderer<Theme>> { fn from(qr_code: QRCode<'a>) -> Self { Self::new(qr_code) |