From 8daf798e5760a0d35d5491027d51a5dd96898b0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Tue, 11 Feb 2020 23:14:25 +0100 Subject: [PATCH 01/45] Add `canvas` feature to `iced_wgpu` And prepare `canvas` module --- wgpu/Cargo.toml | 16 ++++++++++++++-- wgpu/src/lib.rs | 2 +- wgpu/src/widget.rs | 7 +++++++ wgpu/src/widget/canvas.rs | 5 +++++ 4 files changed, 27 insertions(+), 3 deletions(-) create mode 100644 wgpu/src/widget/canvas.rs diff --git a/wgpu/Cargo.toml b/wgpu/Cargo.toml index 60b98b4076..887c2d2167 100644 --- a/wgpu/Cargo.toml +++ b/wgpu/Cargo.toml @@ -9,6 +9,7 @@ repository = "https://github.com/hecrj/iced" [features] svg = ["resvg"] +canvas = ["lyon"] [dependencies] iced_native = { version = "0.1.0", path = "../native" } @@ -20,5 +21,16 @@ raw-window-handle = "0.3" glam = "0.8" font-kit = "0.4" log = "0.4" -resvg = { version = "0.8", features = ["raqote-backend"], optional = true } -image = { version = "0.22", optional = true } + +[dependencies.image] +version = "0.22" +optional = true + +[dependencies.resvg] +version = "0.8" +features = ["raqote-backend"] +optional = true + +[dependencies.lyon] +version = "0.15" +optional = true diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs index e4834818e7..d38e2a31ca 100644 --- a/wgpu/src/lib.rs +++ b/wgpu/src/lib.rs @@ -19,7 +19,7 @@ //! [`wgpu`]: https://github.com/gfx-rs/wgpu-rs //! [WebGPU API]: https://gpuweb.github.io/gpuweb/ //! [`wgpu_glyph`]: https://github.com/hecrj/wgpu_glyph -#![deny(missing_docs)] +//#![deny(missing_docs)] #![deny(missing_debug_implementations)] #![deny(unused_results)] #![forbid(unsafe_code)] diff --git a/wgpu/src/widget.rs b/wgpu/src/widget.rs index e3edda0b66..73cce7e269 100644 --- a/wgpu/src/widget.rs +++ b/wgpu/src/widget.rs @@ -32,3 +32,10 @@ pub use scrollable::Scrollable; pub use slider::Slider; #[doc(no_inline)] pub use text_input::TextInput; + +#[cfg(feature = "canvas")] +pub mod canvas; + +#[cfg(feature = "canvas")] +#[doc(no_inline)] +pub use canvas::Canvas; diff --git a/wgpu/src/widget/canvas.rs b/wgpu/src/widget/canvas.rs new file mode 100644 index 0000000000..ebb841358b --- /dev/null +++ b/wgpu/src/widget/canvas.rs @@ -0,0 +1,5 @@ +//! Draw freely in 2D. + +/// A 2D drawable region. +#[derive(Debug)] +pub struct Canvas; From 36c3160c398c9ba88e407d1f7254f399fd2e44bf Mon Sep 17 00:00:00 2001 From: Louis-Simon Mc Nicoll <> Date: Tue, 11 Feb 2020 18:09:20 -0500 Subject: [PATCH 02/45] Container support for Align::End --- native/src/widget/container.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/native/src/widget/container.rs b/native/src/widget/container.rs index 5682fc87a0..3459a832c1 100644 --- a/native/src/widget/container.rs +++ b/native/src/widget/container.rs @@ -77,6 +77,22 @@ where self.max_height = max_height; self } + + /// Sets the content alignment for the horizontal axis of the [`Container`]. + /// + /// [`Container`]: struct.Container.html + pub fn align_x(mut self, alignment: Align) -> Self { + self.horizontal_alignment = alignment; + self + } + + /// Sets the content alignment for the vertical axis of the [`Container`]. + /// + /// [`Container`]: struct.Container.html + pub fn align_y(mut self, alignment: Align) -> Self { + self.vertical_alignment = alignment; + self + } /// Centers the contents in the horizontal axis of the [`Container`]. /// From f436f20eb86b2324126a54d4164b4cedf2134a45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Wed, 12 Feb 2020 03:47:36 +0100 Subject: [PATCH 03/45] Draft `Canvas` types and `clock` example --- Cargo.toml | 3 + examples/clock/Cargo.toml | 13 +++ examples/clock/src/main.rs | 145 ++++++++++++++++++++++++++++++ src/lib.rs | 2 +- wgpu/src/widget/canvas.rs | 155 +++++++++++++++++++++++++++++++- wgpu/src/widget/canvas/data.rs | 20 +++++ wgpu/src/widget/canvas/frame.rs | 39 ++++++++ wgpu/src/widget/canvas/layer.rs | 41 +++++++++ wgpu/src/widget/canvas/path.rs | 78 ++++++++++++++++ 9 files changed, 494 insertions(+), 2 deletions(-) create mode 100644 examples/clock/Cargo.toml create mode 100644 examples/clock/src/main.rs create mode 100644 wgpu/src/widget/canvas/data.rs create mode 100644 wgpu/src/widget/canvas/frame.rs create mode 100644 wgpu/src/widget/canvas/layer.rs create mode 100644 wgpu/src/widget/canvas/path.rs diff --git a/Cargo.toml b/Cargo.toml index 11cca8b38a..dd099c3264 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,8 @@ categories = ["gui"] image = ["iced_wgpu/image"] # Enables the `Svg` widget svg = ["iced_wgpu/svg"] +# Enables the `Canvas` widget +canvas = ["iced_wgpu/canvas"] # Enables a debug view in native platforms (press F12) debug = ["iced_winit/debug"] # Enables `tokio` as the `executor::Default` on native platforms @@ -36,6 +38,7 @@ members = [ "wgpu", "winit", "examples/bezier_tool", + "examples/clock", "examples/counter", "examples/custom_widget", "examples/events", diff --git a/examples/clock/Cargo.toml b/examples/clock/Cargo.toml new file mode 100644 index 0000000000..941e2bd0a5 --- /dev/null +++ b/examples/clock/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "clock" +version = "0.1.0" +authors = ["Héctor Ramón Jiménez "] +edition = "2018" +publish = false + +[features] +canvas = [] + +[dependencies] +iced = { path = "../..", features = ["canvas", "async-std"] } +chrono = "0.4" diff --git a/examples/clock/src/main.rs b/examples/clock/src/main.rs new file mode 100644 index 0000000000..958846f400 --- /dev/null +++ b/examples/clock/src/main.rs @@ -0,0 +1,145 @@ +use iced::{ + canvas, executor, Application, Canvas, Color, Command, Element, Length, + Point, Settings, +}; + +use std::sync::Arc; + +pub fn main() { + Clock::run(Settings::default()) +} + +struct Clock { + local_time: Arc, + clock: canvas::layer::Cached, +} + +#[derive(Debug, Clone, Copy)] +enum Message { + Tick(chrono::DateTime), +} + +impl Application for Clock { + type Executor = executor::Default; + type Message = Message; + + fn new() -> (Self, Command) { + let now: LocalTime = chrono::Local::now().into(); + + ( + Clock { + local_time: Arc::new(now), + clock: canvas::layer::Cached::new(), + }, + Command::none(), + ) + } + + fn title(&self) -> String { + String::from("Clock - Iced") + } + + fn update(&mut self, message: Message) -> Command { + match message { + Message::Tick(local_time) => {} + } + + Command::none() + } + + fn view(&mut self) -> Element { + Canvas::new() + .width(Length::Fill) + .height(Length::Fill) + .push(self.clock.with(&self.local_time)) + .into() + } +} + +#[derive(Debug)] +struct LocalTime { + hour: u32, + minute: u32, + second: u32, +} + +impl From> for LocalTime { + fn from(date_time: chrono::DateTime) -> LocalTime { + use chrono::Timelike; + + LocalTime { + hour: date_time.hour(), + minute: date_time.minute(), + second: date_time.second(), + } + } +} + +impl canvas::layer::Drawable for LocalTime { + fn draw(&self, frame: &mut canvas::Frame) { + let center = frame.center(); + let radius = frame.width().min(frame.height()) as f32 / 2.0; + + let mut path = canvas::Path::new(); + + path.arc(canvas::path::Arc { + center, + radius, + start_angle: 0.0, + end_angle: 360.0 * 2.0 * std::f32::consts::PI, + }); + + frame.fill( + path, + canvas::Fill::Color(Color::from_rgb8(0x12, 0x93, 0xD8)), + ); + + fn draw_handle( + n: u32, + total: u32, + length: f32, + path: &mut canvas::Path, + ) { + let turns = n as f32 / total as f32; + let t = 2.0 * std::f32::consts::PI * (turns - 0.25); + + let x = length * t.cos(); + let y = length * t.sin(); + + path.line_to(Point::new(x, y)); + } + + let mut path = canvas::Path::new(); + + path.move_to(center); + draw_handle(self.hour, 12, 0.6 * radius, &mut path); + + path.move_to(center); + draw_handle(self.minute, 60, 0.9 * radius, &mut path); + + frame.stroke( + path, + canvas::Stroke { + width: 4.0, + color: Color::WHITE, + line_cap: canvas::LineCap::Round, + ..canvas::Stroke::default() + }, + ); + + let mut path = canvas::Path::new(); + + path.move_to(center); + draw_handle(self.second, 60, 0.9 * radius, &mut path); + + frame.stroke( + path, + canvas::Stroke { + width: 2.0, + color: Color::WHITE, + line_cap: canvas::LineCap::Round, + ..canvas::Stroke::default() + }, + ); + } +} diff --git a/src/lib.rs b/src/lib.rs index 7e658ca234..598706920a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -204,5 +204,5 @@ use iced_web as common; pub use common::{ futures, Align, Background, Color, Command, Font, HorizontalAlignment, - Length, Space, Subscription, Vector, VerticalAlignment, + Length, Point, Space, Subscription, Vector, VerticalAlignment, }; diff --git a/wgpu/src/widget/canvas.rs b/wgpu/src/widget/canvas.rs index ebb841358b..7b84f36cb4 100644 --- a/wgpu/src/widget/canvas.rs +++ b/wgpu/src/widget/canvas.rs @@ -1,5 +1,158 @@ //! Draw freely in 2D. +use crate::{Defaults, Primitive, Renderer}; + +use iced_native::{ + layout, Color, Element, Hasher, Layout, Length, MouseCursor, Point, Size, + Widget, +}; +use std::hash::Hash; + +pub mod layer; +pub mod path; + +mod data; +mod frame; + +pub use data::Data; +pub use frame::Frame; +pub use layer::Layer; +pub use path::Path; /// A 2D drawable region. #[derive(Debug)] -pub struct Canvas; +pub struct Canvas<'a> { + width: Length, + height: Length, + layers: Vec>, +} + +impl<'a> Canvas<'a> { + const DEFAULT_SIZE: u16 = 100; + + pub fn new() -> Self { + Canvas { + width: Length::Units(Self::DEFAULT_SIZE), + height: Length::Units(Self::DEFAULT_SIZE), + layers: Vec::new(), + } + } + + pub fn width(mut self, width: Length) -> Self { + self.width = width; + self + } + + pub fn height(mut self, height: Length) -> Self { + self.height = height; + self + } + + pub fn push(mut self, layer: impl Layer + 'a) -> Self { + self.layers.push(Box::new(layer)); + self + } +} + +impl<'a, Message> Widget for Canvas<'a> { + fn width(&self) -> Length { + self.width + } + + fn height(&self) -> Length { + self.height + } + + fn layout( + &self, + _renderer: &Renderer, + limits: &layout::Limits, + ) -> layout::Node { + let limits = limits.width(self.width).height(self.height); + + let size = limits.resolve(Size::ZERO); + + layout::Node::new(size) + } + + fn draw( + &self, + _renderer: &mut Renderer, + _defaults: &Defaults, + _layout: Layout<'_>, + _cursor_position: Point, + ) -> (Primitive, MouseCursor) { + (Primitive::None, MouseCursor::Idle) + } + + fn hash_layout(&self, state: &mut Hasher) { + std::any::TypeId::of::>().hash(state); + + self.width.hash(state); + self.height.hash(state); + } +} + +impl<'a, Message> From> for Element<'a, Message, Renderer> +where + Message: 'static, +{ + fn from(canvas: Canvas<'a>) -> Element<'a, Message, Renderer> { + Element::new(canvas) + } +} + +#[derive(Debug, Clone, Copy)] +pub struct Stroke { + pub color: Color, + pub width: f32, + pub line_cap: LineCap, + pub line_join: LineJoin, +} + +impl Default for Stroke { + fn default() -> Stroke { + Stroke { + color: Color::BLACK, + width: 1.0, + line_cap: LineCap::default(), + line_join: LineJoin::default(), + } + } +} + +#[derive(Debug, Clone, Copy)] +pub enum LineCap { + Butt, + Square, + Round, +} + +impl Default for LineCap { + fn default() -> LineCap { + LineCap::Butt + } +} + +#[derive(Debug, Clone, Copy)] +pub enum LineJoin { + Miter, + Round, + Bevel, +} + +impl Default for LineJoin { + fn default() -> LineJoin { + LineJoin::Miter + } +} + +#[derive(Debug, Clone, Copy)] +pub enum Fill { + Color(Color), +} + +impl Default for Fill { + fn default() -> Fill { + Fill::Color(Color::BLACK) + } +} diff --git a/wgpu/src/widget/canvas/data.rs b/wgpu/src/widget/canvas/data.rs new file mode 100644 index 0000000000..25d94f4cc3 --- /dev/null +++ b/wgpu/src/widget/canvas/data.rs @@ -0,0 +1,20 @@ +#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)] +pub struct Data { + raw: T, + version: usize, +} + +impl Data { + pub fn new(data: T) -> Self { + Data { + raw: data, + version: 0, + } + } + + pub fn update(&mut self, f: impl FnOnce(&mut T)) { + f(&mut self.raw); + + self.version += 1; + } +} diff --git a/wgpu/src/widget/canvas/frame.rs b/wgpu/src/widget/canvas/frame.rs new file mode 100644 index 0000000000..65c22c0c8b --- /dev/null +++ b/wgpu/src/widget/canvas/frame.rs @@ -0,0 +1,39 @@ +use iced_native::Point; + +use crate::{ + canvas::{Fill, Path, Stroke}, + triangle, +}; + +#[derive(Debug)] +pub struct Frame { + width: u32, + height: u32, + buffers: lyon::tessellation::VertexBuffers, +} + +impl Frame { + pub(crate) fn new(width: u32, height: u32) -> Frame { + Frame { + width, + height, + buffers: lyon::tessellation::VertexBuffers::new(), + } + } + + pub fn width(&self) -> u32 { + self.width + } + + pub fn height(&self) -> u32 { + self.height + } + + pub fn center(&self) -> Point { + Point::new(self.width as f32 / 2.0, self.height as f32 / 2.0) + } + + pub fn fill(&mut self, path: Path, fill: Fill) {} + + pub fn stroke(&mut self, path: Path, stroke: Stroke) {} +} diff --git a/wgpu/src/widget/canvas/layer.rs b/wgpu/src/widget/canvas/layer.rs new file mode 100644 index 0000000000..f97634e4dc --- /dev/null +++ b/wgpu/src/widget/canvas/layer.rs @@ -0,0 +1,41 @@ +use crate::canvas::Frame; + +pub trait Layer: std::fmt::Debug {} + +use std::marker::PhantomData; +use std::sync::{Arc, Weak}; + +#[derive(Debug)] +pub struct Cached { + input: PhantomData, +} + +impl Cached +where + T: Drawable + std::fmt::Debug, +{ + pub fn new() -> Self { + Cached { input: PhantomData } + } + + pub fn clear(&mut self) {} + + pub fn with<'a>(&'a self, input: &'a T) -> impl Layer + 'a { + Bind { + cache: self, + input: input, + } + } +} + +#[derive(Debug)] +struct Bind<'a, T: Drawable> { + cache: &'a Cached, + input: &'a T, +} + +impl<'a, T> Layer for Bind<'a, T> where T: Drawable + std::fmt::Debug {} + +pub trait Drawable { + fn draw(&self, frame: &mut Frame); +} diff --git a/wgpu/src/widget/canvas/path.rs b/wgpu/src/widget/canvas/path.rs new file mode 100644 index 0000000000..2732b1bd01 --- /dev/null +++ b/wgpu/src/widget/canvas/path.rs @@ -0,0 +1,78 @@ +use iced_native::{Point, Vector}; + +#[allow(missing_debug_implementations)] +pub struct Path { + raw: lyon::path::Builder, +} + +impl Path { + pub fn new() -> Path { + Path { + raw: lyon::path::Path::builder(), + } + } + + #[inline] + pub fn move_to(&mut self, point: Point) { + let _ = self.raw.move_to(lyon::math::Point::new(point.x, point.y)); + } + + #[inline] + pub fn line_to(&mut self, point: Point) { + let _ = self.raw.line_to(lyon::math::Point::new(point.x, point.y)); + } + + #[inline] + pub fn arc(&mut self, arc: Arc) { + self.ellipse(arc.into()) + } + + #[inline] + pub fn ellipse(&mut self, ellipse: Ellipse) { + let arc = lyon::geom::Arc { + center: lyon::math::Point::new(ellipse.center.x, ellipse.center.y), + radii: lyon::math::Vector::new(ellipse.radii.x, ellipse.radii.y), + x_rotation: lyon::math::Angle::radians(ellipse.rotation), + start_angle: lyon::math::Angle::radians(ellipse.start_angle), + sweep_angle: lyon::math::Angle::radians(ellipse.end_angle), + }; + + arc.for_each_quadratic_bezier(&mut |curve| { + let _ = self.raw.quadratic_bezier_to(curve.ctrl, curve.to); + }); + } + + #[inline] + pub fn close(&mut self) { + self.raw.close() + } +} + +#[derive(Debug, Clone, Copy)] +pub struct Arc { + pub center: Point, + pub radius: f32, + pub start_angle: f32, + pub end_angle: f32, +} + +#[derive(Debug, Clone, Copy)] +pub struct Ellipse { + pub center: Point, + pub radii: Vector, + pub rotation: f32, + pub start_angle: f32, + pub end_angle: f32, +} + +impl From for Ellipse { + fn from(arc: Arc) -> Ellipse { + Ellipse { + center: arc.center, + radii: Vector::new(arc.radius, arc.radius), + rotation: 0.0, + start_angle: arc.start_angle, + end_angle: arc.end_angle, + } + } +} From 4777f5d787e262aa9093a3099ae62be2b8c0c82f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Wed, 12 Feb 2020 04:00:13 +0100 Subject: [PATCH 04/45] Remove unnecessary `Arc` from `clock` example --- examples/clock/src/main.rs | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/examples/clock/src/main.rs b/examples/clock/src/main.rs index 958846f400..dc2c06cf9b 100644 --- a/examples/clock/src/main.rs +++ b/examples/clock/src/main.rs @@ -3,14 +3,12 @@ use iced::{ Point, Settings, }; -use std::sync::Arc; - pub fn main() { Clock::run(Settings::default()) } struct Clock { - local_time: Arc, + now: LocalTime, clock: canvas::layer::Cached, } @@ -28,7 +26,7 @@ impl Application for Clock { ( Clock { - local_time: Arc::new(now), + now, clock: canvas::layer::Cached::new(), }, Command::none(), @@ -41,7 +39,15 @@ impl Application for Clock { fn update(&mut self, message: Message) -> Command { match message { - Message::Tick(local_time) => {} + Message::Tick(local_time) => { + let now = local_time.into(); + + if now != self.now { + self.now = now; + + self.clock.clear(); + } + } } Command::none() @@ -51,12 +57,12 @@ impl Application for Clock { Canvas::new() .width(Length::Fill) .height(Length::Fill) - .push(self.clock.with(&self.local_time)) + .push(self.clock.with(&self.now)) .into() } } -#[derive(Debug)] +#[derive(Debug, PartialEq, Eq)] struct LocalTime { hour: u32, minute: u32, From 64097983f195ac1e923b6a1bd8cb0fc2c109fabc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Wed, 12 Feb 2020 04:03:24 +0100 Subject: [PATCH 05/45] Expose `Point` in `iced_web` --- web/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/src/lib.rs b/web/src/lib.rs index 7b54a07a01..4dc7aba70c 100644 --- a/web/src/lib.rs +++ b/web/src/lib.rs @@ -73,7 +73,7 @@ pub use dodrio; pub use element::Element; pub use hasher::Hasher; pub use iced_core::{ - Align, Background, Color, Font, HorizontalAlignment, Length, Vector, + Align, Background, Color, Font, HorizontalAlignment, Length, Point, Vector, VerticalAlignment, }; pub use iced_futures::{executor, futures, Command}; From 74dd79e97f83d3e9e13d87444740edeb353f9be8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Wed, 12 Feb 2020 06:41:24 +0100 Subject: [PATCH 06/45] Rename current `Path` to `path::Builder` --- examples/clock/src/main.rs | 42 ++++++++++++++++----------------- wgpu/src/widget/canvas/frame.rs | 4 ++-- wgpu/src/widget/canvas/path.rs | 32 +++++++++++++++++++++---- 3 files changed, 50 insertions(+), 28 deletions(-) diff --git a/examples/clock/src/main.rs b/examples/clock/src/main.rs index dc2c06cf9b..76752d20fd 100644 --- a/examples/clock/src/main.rs +++ b/examples/clock/src/main.rs @@ -86,17 +86,17 @@ impl canvas::layer::Drawable for LocalTime { let center = frame.center(); let radius = frame.width().min(frame.height()) as f32 / 2.0; - let mut path = canvas::Path::new(); - - path.arc(canvas::path::Arc { - center, - radius, - start_angle: 0.0, - end_angle: 360.0 * 2.0 * std::f32::consts::PI, + let path = canvas::Path::new(|path| { + path.arc(canvas::path::Arc { + center, + radius, + start_angle: 0.0, + end_angle: 360.0 * 2.0 * std::f32::consts::PI, + }) }); frame.fill( - path, + &path, canvas::Fill::Color(Color::from_rgb8(0x12, 0x93, 0xD8)), ); @@ -104,7 +104,7 @@ impl canvas::layer::Drawable for LocalTime { n: u32, total: u32, length: f32, - path: &mut canvas::Path, + path: &mut canvas::path::Builder, ) { let turns = n as f32 / total as f32; let t = 2.0 * std::f32::consts::PI * (turns - 0.25); @@ -115,16 +115,16 @@ impl canvas::layer::Drawable for LocalTime { path.line_to(Point::new(x, y)); } - let mut path = canvas::Path::new(); - - path.move_to(center); - draw_handle(self.hour, 12, 0.6 * radius, &mut path); + let path = canvas::Path::new(|path| { + path.move_to(center); + draw_handle(self.hour, 12, 0.6 * radius, path); - path.move_to(center); - draw_handle(self.minute, 60, 0.9 * radius, &mut path); + path.move_to(center); + draw_handle(self.minute, 60, 0.9 * radius, path) + }); frame.stroke( - path, + &path, canvas::Stroke { width: 4.0, color: Color::WHITE, @@ -133,13 +133,13 @@ impl canvas::layer::Drawable for LocalTime { }, ); - let mut path = canvas::Path::new(); - - path.move_to(center); - draw_handle(self.second, 60, 0.9 * radius, &mut path); + let path = canvas::Path::new(|path| { + path.move_to(center); + draw_handle(self.second, 60, 0.9 * radius, path) + }); frame.stroke( - path, + &path, canvas::Stroke { width: 2.0, color: Color::WHITE, diff --git a/wgpu/src/widget/canvas/frame.rs b/wgpu/src/widget/canvas/frame.rs index 65c22c0c8b..e5daeedbbe 100644 --- a/wgpu/src/widget/canvas/frame.rs +++ b/wgpu/src/widget/canvas/frame.rs @@ -33,7 +33,7 @@ impl Frame { Point::new(self.width as f32 / 2.0, self.height as f32 / 2.0) } - pub fn fill(&mut self, path: Path, fill: Fill) {} + pub fn fill(&mut self, path: &Path, fill: Fill) {} - pub fn stroke(&mut self, path: Path, stroke: Stroke) {} + pub fn stroke(&mut self, path: &Path, stroke: Stroke) {} } diff --git a/wgpu/src/widget/canvas/path.rs b/wgpu/src/widget/canvas/path.rs index 2732b1bd01..86326e8e56 100644 --- a/wgpu/src/widget/canvas/path.rs +++ b/wgpu/src/widget/canvas/path.rs @@ -1,13 +1,28 @@ use iced_native::{Point, Vector}; -#[allow(missing_debug_implementations)] +#[derive(Debug, Clone)] pub struct Path { - raw: lyon::path::Builder, + raw: lyon::path::Path, } impl Path { - pub fn new() -> Path { - Path { + pub fn new(f: impl FnOnce(&mut Builder)) -> Self { + let mut builder = Builder::new(); + + f(&mut builder); + + builder.build() + } +} + +#[allow(missing_debug_implementations)] +pub struct Builder { + raw: lyon::path::Builder, +} + +impl Builder { + pub fn new() -> Builder { + Builder { raw: lyon::path::Path::builder(), } } @@ -24,7 +39,7 @@ impl Path { #[inline] pub fn arc(&mut self, arc: Arc) { - self.ellipse(arc.into()) + self.ellipse(arc.into()); } #[inline] @@ -46,6 +61,13 @@ impl Path { pub fn close(&mut self) { self.raw.close() } + + #[inline] + pub fn build(self) -> Path { + Path { + raw: self.raw.build(), + } + } } #[derive(Debug, Clone, Copy)] From f34407bfdaf06c4bf204dc31b152be9451c243b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Wed, 12 Feb 2020 07:08:49 +0100 Subject: [PATCH 07/45] Implement `Frame::fill` and `Frame::stroke` --- wgpu/src/widget/canvas.rs | 20 +++++++++ wgpu/src/widget/canvas/frame.rs | 76 ++++++++++++++++++++++++++++++++- wgpu/src/widget/canvas/path.rs | 6 +++ 3 files changed, 100 insertions(+), 2 deletions(-) diff --git a/wgpu/src/widget/canvas.rs b/wgpu/src/widget/canvas.rs index 7b84f36cb4..6bfeed9a69 100644 --- a/wgpu/src/widget/canvas.rs +++ b/wgpu/src/widget/canvas.rs @@ -133,6 +133,16 @@ impl Default for LineCap { } } +impl From 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, + } + } +} + #[derive(Debug, Clone, Copy)] pub enum LineJoin { Miter, @@ -146,6 +156,16 @@ impl Default for LineJoin { } } +impl From 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, + } + } +} + #[derive(Debug, Clone, Copy)] pub enum Fill { Color(Color), diff --git a/wgpu/src/widget/canvas/frame.rs b/wgpu/src/widget/canvas/frame.rs index e5daeedbbe..82ff526b8b 100644 --- a/wgpu/src/widget/canvas/frame.rs +++ b/wgpu/src/widget/canvas/frame.rs @@ -33,7 +33,79 @@ impl Frame { Point::new(self.width as f32 / 2.0, self.height as f32 / 2.0) } - pub fn fill(&mut self, path: &Path, fill: Fill) {} + pub fn fill(&mut self, path: &Path, fill: Fill) { + use lyon::tessellation::{ + BuffersBuilder, FillOptions, FillTessellator, + }; - pub fn stroke(&mut self, path: &Path, stroke: Stroke) {} + let mut buffers = BuffersBuilder::new( + &mut self.buffers, + FillVertex(match fill { + Fill::Color(color) => color.into_linear(), + }), + ); + + let mut tessellator = FillTessellator::new(); + + let _ = tessellator + .tessellate_path(path.raw(), &FillOptions::default(), &mut buffers) + .expect("Tessellate path"); + } + + pub fn stroke(&mut self, path: &Path, stroke: Stroke) { + use lyon::tessellation::{ + BuffersBuilder, StrokeOptions, StrokeTessellator, + }; + + let mut buffers = BuffersBuilder::new( + &mut self.buffers, + StrokeVertex(stroke.color.into_linear()), + ); + + let mut tessellator = StrokeTessellator::new(); + + let mut options = 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(); + + let _ = tessellator + .tessellate_path(path.raw(), &options, &mut buffers) + .expect("Stroke path"); + } +} + +struct FillVertex([f32; 4]); + +impl lyon::tessellation::FillVertexConstructor + for FillVertex +{ + fn new_vertex( + &mut self, + position: lyon::math::Point, + _attributes: lyon::tessellation::FillAttributes<'_>, + ) -> triangle::Vertex2D { + triangle::Vertex2D { + position: [position.x, position.y], + color: self.0, + } + } +} + +struct StrokeVertex([f32; 4]); + +impl lyon::tessellation::StrokeVertexConstructor + for StrokeVertex +{ + fn new_vertex( + &mut self, + position: lyon::math::Point, + _attributes: lyon::tessellation::StrokeAttributes<'_, '_>, + ) -> triangle::Vertex2D { + triangle::Vertex2D { + position: [position.x, position.y], + color: self.0, + } + } } diff --git a/wgpu/src/widget/canvas/path.rs b/wgpu/src/widget/canvas/path.rs index 86326e8e56..96206256ff 100644 --- a/wgpu/src/widget/canvas/path.rs +++ b/wgpu/src/widget/canvas/path.rs @@ -9,10 +9,16 @@ impl Path { pub fn new(f: impl FnOnce(&mut Builder)) -> Self { let mut builder = Builder::new(); + // TODO: Make it pure instead of side-effect-based (?) f(&mut builder); builder.build() } + + #[inline] + pub(crate) fn raw(&self) -> &lyon::path::Path { + &self.raw + } } #[allow(missing_debug_implementations)] From 578ea4abb8a2dd0d53d7087322796bf9ad541b56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Wed, 12 Feb 2020 08:49:42 +0100 Subject: [PATCH 08/45] Finish `clock` example --- examples/clock/Cargo.toml | 4 +- examples/clock/src/main.rs | 65 +++++++++++++++++++++--- wgpu/src/primitive.rs | 10 +++- wgpu/src/renderer.rs | 12 ++--- wgpu/src/shader/triangle.vert | 4 +- wgpu/src/shader/triangle.vert.spv | Bin 1468 -> 1256 bytes wgpu/src/triangle.rs | 80 +++++++++++++++--------------- wgpu/src/widget/canvas.rs | 21 ++++++-- wgpu/src/widget/canvas/frame.rs | 19 ++++--- wgpu/src/widget/canvas/layer.rs | 63 ++++++++++++++++++++--- wgpu/src/widget/canvas/path.rs | 2 + 11 files changed, 202 insertions(+), 78 deletions(-) diff --git a/examples/clock/Cargo.toml b/examples/clock/Cargo.toml index 941e2bd0a5..308cbfbb0a 100644 --- a/examples/clock/Cargo.toml +++ b/examples/clock/Cargo.toml @@ -9,5 +9,7 @@ publish = false canvas = [] [dependencies] -iced = { path = "../..", features = ["canvas", "async-std"] } +iced = { path = "../..", features = ["canvas", "async-std", "debug"] } +iced_native = { path = "../../native" } chrono = "0.4" +async-std = { version = "1.0", features = ["unstable"] } diff --git a/examples/clock/src/main.rs b/examples/clock/src/main.rs index 76752d20fd..1b8d1ee600 100644 --- a/examples/clock/src/main.rs +++ b/examples/clock/src/main.rs @@ -1,6 +1,6 @@ use iced::{ - canvas, executor, Application, Canvas, Color, Command, Element, Length, - Point, Settings, + canvas, executor, Application, Canvas, Color, Command, Container, Element, + Length, Point, Settings, Subscription, Vector, }; pub fn main() { @@ -53,11 +53,21 @@ impl Application for Clock { Command::none() } + fn subscription(&self) -> Subscription { + time::every(std::time::Duration::from_millis(500)).map(Message::Tick) + } + fn view(&mut self) -> Element { - Canvas::new() + let canvas = Canvas::new() + .width(Length::Units(400)) + .height(Length::Units(400)) + .push(self.clock.with(&self.now)); + + Container::new(canvas) .width(Length::Fill) .height(Length::Fill) - .push(self.clock.with(&self.now)) + .center_x() + .center_y() .into() } } @@ -85,6 +95,7 @@ impl canvas::layer::Drawable for LocalTime { fn draw(&self, frame: &mut canvas::Frame) { let center = frame.center(); let radius = frame.width().min(frame.height()) as f32 / 2.0; + let offset = Vector::new(center.x, center.y); let path = canvas::Path::new(|path| { path.arc(canvas::path::Arc { @@ -104,6 +115,7 @@ impl canvas::layer::Drawable for LocalTime { n: u32, total: u32, length: f32, + offset: Vector, path: &mut canvas::path::Builder, ) { let turns = n as f32 / total as f32; @@ -112,15 +124,15 @@ impl canvas::layer::Drawable for LocalTime { let x = length * t.cos(); let y = length * t.sin(); - path.line_to(Point::new(x, y)); + path.line_to(Point::new(x, y) + offset); } let path = canvas::Path::new(|path| { path.move_to(center); - draw_handle(self.hour, 12, 0.6 * radius, path); + draw_handle(self.hour, 12, 0.5 * radius, offset, path); path.move_to(center); - draw_handle(self.minute, 60, 0.9 * radius, path) + draw_handle(self.minute, 60, 0.8 * radius, offset, path) }); frame.stroke( @@ -135,7 +147,7 @@ impl canvas::layer::Drawable for LocalTime { let path = canvas::Path::new(|path| { path.move_to(center); - draw_handle(self.second, 60, 0.9 * radius, path) + draw_handle(self.second, 60, 0.8 * radius, offset, path) }); frame.stroke( @@ -149,3 +161,40 @@ impl canvas::layer::Drawable for LocalTime { ); } } + +mod time { + use iced::futures; + + pub fn every( + duration: std::time::Duration, + ) -> iced::Subscription> { + iced::Subscription::from_recipe(Every(duration)) + } + + struct Every(std::time::Duration); + + impl iced_native::subscription::Recipe for Every + where + H: std::hash::Hasher, + { + type Output = chrono::DateTime; + + fn hash(&self, state: &mut H) { + use std::hash::Hash; + + std::any::TypeId::of::().hash(state); + self.0.hash(state); + } + + fn stream( + self: Box, + _input: futures::stream::BoxStream<'static, I>, + ) -> futures::stream::BoxStream<'static, Self::Output> { + use futures::stream::StreamExt; + + async_std::stream::interval(self.0) + .map(|_| chrono::Local::now()) + .boxed() + } + } +} diff --git a/wgpu/src/primitive.rs b/wgpu/src/primitive.rs index 481252ef8a..823b4b7260 100644 --- a/wgpu/src/primitive.rs +++ b/wgpu/src/primitive.rs @@ -1,5 +1,5 @@ use iced_native::{ - image, svg, Background, Color, Font, HorizontalAlignment, Rectangle, + image, svg, Background, Color, Font, HorizontalAlignment, Point, Rectangle, Vector, VerticalAlignment, }; @@ -73,7 +73,13 @@ pub enum Primitive { /// A low-level primitive to render a mesh of triangles. /// /// It can be used to render many kinds of geometry freely. - Mesh2D(Arc), + Mesh2D { + /// The top-left coordinate of the mesh + origin: Point, + + /// The vertex and index buffers of the mesh + buffers: Arc, + }, } impl Default for Primitive { diff --git a/wgpu/src/renderer.rs b/wgpu/src/renderer.rs index e93090b802..25b2e99ae0 100644 --- a/wgpu/src/renderer.rs +++ b/wgpu/src/renderer.rs @@ -26,7 +26,7 @@ struct Layer<'a> { offset: Vector, quads: Vec, images: Vec, - meshes: Vec>, + meshes: Vec<(Point, Arc)>, text: Vec>, } @@ -229,8 +229,8 @@ impl Renderer { scale: [bounds.width, bounds.height], }); } - Primitive::Mesh2D(mesh) => { - layer.meshes.push(mesh.clone()); + Primitive::Mesh2D { origin, buffers } => { + layer.meshes.push((*origin, buffers.clone())); } Primitive::Clip { bounds, @@ -313,9 +313,10 @@ impl Renderer { if layer.meshes.len() > 0 { let translated = transformation + * Transformation::scale(scale_factor, scale_factor) * Transformation::translate( - -(layer.offset.x as f32) * scale_factor, - -(layer.offset.y as f32) * scale_factor, + -(layer.offset.x as f32), + -(layer.offset.y as f32), ); self.triangle_pipeline.draw( @@ -323,7 +324,6 @@ impl Renderer { encoder, target, translated, - scale_factor, &layer.meshes, bounds, ); diff --git a/wgpu/src/shader/triangle.vert b/wgpu/src/shader/triangle.vert index fd86ecd60c..1f2c009b83 100644 --- a/wgpu/src/shader/triangle.vert +++ b/wgpu/src/shader/triangle.vert @@ -7,11 +7,9 @@ layout(location = 0) out vec4 o_Color; layout (set = 0, binding = 0) uniform Globals { mat4 u_Transform; - float u_Scale; }; void main() { - vec2 p_Position = i_Position * u_Scale; - gl_Position = u_Transform * vec4(p_Position, 0.0, 1.0); + gl_Position = u_Transform * vec4(i_Position, 0.0, 1.0); o_Color = i_Color; } diff --git a/wgpu/src/shader/triangle.vert.spv b/wgpu/src/shader/triangle.vert.spv index bc39c451e34ba68c71f821afee0a1d4e140c4274..871f4f5520592ea4f48c239b871aafe46d1c01ef 100644 GIT binary patch literal 1256 zcmYk4%We}v5Jek1b^?Js2zda(*l~abQp5rf5(0#RyGLf2sx#ycP5vbT=qyDKR{T+>>+250X>{= z4|65lt6;>=+_{A9eHlBi*f}=nYTq7bsM~mpsEzMp+nlEkvGZ*5Ucz@V*W()dx7lwO zd<}2CT!+Z{Qx9`mm%8itbXPBH?Dv-QoRwHt_4OR_D(2b&dpGt~SBG8Rk(|A6;+;j# z-eT`f&it)9w%!@H@oG-_9b)H^cfZ(sl~aqgcQIGOyAl0*WB(qpbEwb#;(pDU-zTTu6P%-PI&2jYL6_yYS6q!LXn literal 1468 zcmY+E%We}v5Jj8#kw732LLNYH?3j0mVF3sU@lueSMHWFuY%LQE8foH*{E!eUz5t0| z;;Yyoan5+!kdA7q@2%>p>f2UIbIth>&V>1}5FUoX$h{>ush6L<4fzKpM9_S`A(N`al{3VU;{XZ{Ahj=9ViU&TDX3BHCmSFXlR?c&VFH0y5S z)yTMWiT$m~#W|wR=5N6}hj%k>k0$1d_-2m2zcT+Gdu!R-`eNTs&iDhoGsn1ix8R=g zd$E_Ax;*n6#^15Ed^7(S_(m_l`tz9Y^oA@oTgB8_EzNP>HB9~oIi6X^JSYE>F4mN@ zhWD*w)=&p)c=rZYFR(SXG0(l@j6H4@9B#(lE;#RdNN4YS2Q$~0I;!1IY8bnV*~c7b dQM>z?@8c}aDmKr#Jk!F|&zL%k|Df@N(rTT}o5 diff --git a/wgpu/src/triangle.rs b/wgpu/src/triangle.rs index 38157d0039..6f3adbe4a7 100644 --- a/wgpu/src/triangle.rs +++ b/wgpu/src/triangle.rs @@ -1,6 +1,6 @@ //! Draw meshes of triangles. use crate::Transformation; -use iced_native::Rectangle; +use iced_native::{Point, Rectangle}; use std::{mem, sync::Arc}; #[derive(Debug)] @@ -128,47 +128,28 @@ impl Pipeline { encoder: &mut wgpu::CommandEncoder, target: &wgpu::TextureView, transformation: Transformation, - scale: f32, - meshes: &Vec>, + meshes: &Vec<(Point, Arc)>, bounds: Rectangle, ) { - let uniforms = Uniforms { - transform: transformation.into(), - scale, - }; - - let constants_buffer = device - .create_buffer_mapped(1, wgpu::BufferUsage::COPY_SRC) - .fill_from_slice(&[uniforms]); - - encoder.copy_buffer_to_buffer( - &constants_buffer, - 0, - &self.constants_buffer, - 0, - std::mem::size_of::() as u64, - ); - - let mut render_pass = - encoder.begin_render_pass(&wgpu::RenderPassDescriptor { - color_attachments: &[ - wgpu::RenderPassColorAttachmentDescriptor { - attachment: target, - resolve_target: None, - load_op: wgpu::LoadOp::Load, - store_op: wgpu::StoreOp::Store, - clear_color: wgpu::Color { - r: 0.0, - g: 0.0, - b: 0.0, - a: 0.0, - }, - }, - ], - depth_stencil_attachment: None, - }); + for (origin, mesh) in meshes { + let uniforms = Uniforms { + transform: (transformation + * Transformation::translate(origin.x, origin.y)) + .into(), + }; + + let constants_buffer = device + .create_buffer_mapped(1, wgpu::BufferUsage::COPY_SRC) + .fill_from_slice(&[uniforms]); + + encoder.copy_buffer_to_buffer( + &constants_buffer, + 0, + &self.constants_buffer, + 0, + std::mem::size_of::() as u64, + ); - for mesh in meshes { let vertices_buffer = device .create_buffer_mapped( mesh.vertices.len(), @@ -183,6 +164,25 @@ impl Pipeline { ) .fill_from_slice(&mesh.indices); + let mut render_pass = + encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + color_attachments: &[ + wgpu::RenderPassColorAttachmentDescriptor { + attachment: target, + resolve_target: None, + load_op: wgpu::LoadOp::Load, + store_op: wgpu::StoreOp::Store, + clear_color: wgpu::Color { + r: 0.0, + g: 0.0, + b: 0.0, + a: 0.0, + }, + }, + ], + depth_stencil_attachment: None, + }); + render_pass.set_pipeline(&self.pipeline); render_pass.set_bind_group(0, &self.constants, &[]); render_pass.set_index_buffer(&indices_buffer, 0); @@ -203,14 +203,12 @@ impl Pipeline { #[derive(Debug, Clone, Copy)] struct Uniforms { transform: [f32; 16], - scale: f32, } impl Default for Uniforms { fn default() -> Self { Self { transform: *Transformation::identity().as_ref(), - scale: 1.0, } } } diff --git a/wgpu/src/widget/canvas.rs b/wgpu/src/widget/canvas.rs index 6bfeed9a69..c984fee968 100644 --- a/wgpu/src/widget/canvas.rs +++ b/wgpu/src/widget/canvas.rs @@ -68,7 +68,6 @@ impl<'a, Message> Widget for Canvas<'a> { limits: &layout::Limits, ) -> layout::Node { let limits = limits.width(self.width).height(self.height); - let size = limits.resolve(Size::ZERO); layout::Node::new(size) @@ -78,10 +77,26 @@ impl<'a, Message> Widget for Canvas<'a> { &self, _renderer: &mut Renderer, _defaults: &Defaults, - _layout: Layout<'_>, + layout: Layout<'_>, _cursor_position: Point, ) -> (Primitive, MouseCursor) { - (Primitive::None, MouseCursor::Idle) + let bounds = layout.bounds(); + let origin = Point::new(bounds.x, bounds.y); + let size = Size::new(bounds.width, bounds.height); + + ( + Primitive::Group { + primitives: self + .layers + .iter() + .map(|layer| Primitive::Mesh2D { + origin, + buffers: layer.draw(size), + }) + .collect(), + }, + MouseCursor::Idle, + ) } fn hash_layout(&self, state: &mut Hasher) { diff --git a/wgpu/src/widget/canvas/frame.rs b/wgpu/src/widget/canvas/frame.rs index 82ff526b8b..3c667426df 100644 --- a/wgpu/src/widget/canvas/frame.rs +++ b/wgpu/src/widget/canvas/frame.rs @@ -7,13 +7,13 @@ use crate::{ #[derive(Debug)] pub struct Frame { - width: u32, - height: u32, + width: f32, + height: f32, buffers: lyon::tessellation::VertexBuffers, } impl Frame { - pub(crate) fn new(width: u32, height: u32) -> Frame { + pub fn new(width: f32, height: f32) -> Frame { Frame { width, height, @@ -21,16 +21,16 @@ impl Frame { } } - pub fn width(&self) -> u32 { + pub fn width(&self) -> f32 { self.width } - pub fn height(&self) -> u32 { + pub fn height(&self) -> f32 { self.height } pub fn center(&self) -> Point { - Point::new(self.width as f32 / 2.0, self.height as f32 / 2.0) + Point::new(self.width / 2.0, self.height / 2.0) } pub fn fill(&mut self, path: &Path, fill: Fill) { @@ -74,6 +74,13 @@ impl Frame { .tessellate_path(path.raw(), &options, &mut buffers) .expect("Stroke path"); } + + pub fn into_mesh(self) -> triangle::Mesh2D { + triangle::Mesh2D { + vertices: self.buffers.vertices, + indices: self.buffers.indices, + } + } } struct FillVertex([f32; 4]); diff --git a/wgpu/src/widget/canvas/layer.rs b/wgpu/src/widget/canvas/layer.rs index f97634e4dc..c239a2542a 100644 --- a/wgpu/src/widget/canvas/layer.rs +++ b/wgpu/src/widget/canvas/layer.rs @@ -1,13 +1,28 @@ -use crate::canvas::Frame; +use crate::{canvas::Frame, triangle}; -pub trait Layer: std::fmt::Debug {} +use iced_native::Size; +use std::cell::RefCell; +use std::sync::Arc; + +pub trait Layer: std::fmt::Debug { + fn draw(&self, bounds: Size) -> Arc; +} use std::marker::PhantomData; -use std::sync::{Arc, Weak}; #[derive(Debug)] pub struct Cached { input: PhantomData, + cache: RefCell, +} + +#[derive(Debug)] +enum Cache { + Empty, + Filled { + mesh: Arc, + bounds: Size, + }, } impl Cached @@ -15,14 +30,19 @@ where T: Drawable + std::fmt::Debug, { pub fn new() -> Self { - Cached { input: PhantomData } + Cached { + input: PhantomData, + cache: RefCell::new(Cache::Empty), + } } - pub fn clear(&mut self) {} + pub fn clear(&mut self) { + *self.cache.borrow_mut() = Cache::Empty; + } pub fn with<'a>(&'a self, input: &'a T) -> impl Layer + 'a { Bind { - cache: self, + layer: self, input: input, } } @@ -30,11 +50,38 @@ where #[derive(Debug)] struct Bind<'a, T: Drawable> { - cache: &'a Cached, + layer: &'a Cached, input: &'a T, } -impl<'a, T> Layer for Bind<'a, T> where T: Drawable + std::fmt::Debug {} +impl<'a, T> Layer for Bind<'a, T> +where + T: Drawable + std::fmt::Debug, +{ + fn draw(&self, current_bounds: Size) -> Arc { + use std::ops::Deref; + + if let Cache::Filled { mesh, bounds } = + self.layer.cache.borrow().deref() + { + if *bounds == current_bounds { + return mesh.clone(); + } + } + + let mut frame = Frame::new(current_bounds.width, current_bounds.height); + self.input.draw(&mut frame); + + let mesh = Arc::new(frame.into_mesh()); + + *self.layer.cache.borrow_mut() = Cache::Filled { + mesh: mesh.clone(), + bounds: current_bounds, + }; + + mesh + } +} pub trait Drawable { fn draw(&self, frame: &mut Frame); diff --git a/wgpu/src/widget/canvas/path.rs b/wgpu/src/widget/canvas/path.rs index 96206256ff..c8ba10e1a6 100644 --- a/wgpu/src/widget/canvas/path.rs +++ b/wgpu/src/widget/canvas/path.rs @@ -58,6 +58,8 @@ impl Builder { sweep_angle: lyon::math::Angle::radians(ellipse.end_angle), }; + let _ = self.raw.move_to(arc.sample(0.0)); + arc.for_each_quadratic_bezier(&mut |curve| { let _ = self.raw.quadratic_bezier_to(curve.ctrl, curve.to); }); From 96b36d0f9e4beba2ac9cbe9c873adf1b89d31f99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Wed, 12 Feb 2020 09:00:44 +0100 Subject: [PATCH 09/45] Increase line width in `clock` example --- examples/clock/src/main.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/clock/src/main.rs b/examples/clock/src/main.rs index 1b8d1ee600..e70ce91a10 100644 --- a/examples/clock/src/main.rs +++ b/examples/clock/src/main.rs @@ -54,7 +54,7 @@ impl Application for Clock { } fn subscription(&self) -> Subscription { - time::every(std::time::Duration::from_millis(500)).map(Message::Tick) + time::every(std::time::Duration::from_millis(1000)).map(Message::Tick) } fn view(&mut self) -> Element { @@ -138,7 +138,7 @@ impl canvas::layer::Drawable for LocalTime { frame.stroke( &path, canvas::Stroke { - width: 4.0, + width: 6.0, color: Color::WHITE, line_cap: canvas::LineCap::Round, ..canvas::Stroke::default() @@ -153,7 +153,7 @@ impl canvas::layer::Drawable for LocalTime { frame.stroke( &path, canvas::Stroke { - width: 2.0, + width: 3.0, color: Color::WHITE, line_cap: canvas::LineCap::Round, ..canvas::Stroke::default() From 1beeaf9db5e4d9bcf9c1fce89463ad47c708b07d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Wed, 12 Feb 2020 09:12:15 +0100 Subject: [PATCH 10/45] Fix examples using `Mesh2D` --- examples/bezier_tool/src/main.rs | 52 ++++++------- examples/geometry/src/main.rs | 123 ++++++++++++++++--------------- 2 files changed, 84 insertions(+), 91 deletions(-) diff --git a/examples/bezier_tool/src/main.rs b/examples/bezier_tool/src/main.rs index 043d265c96..fbb6fa248f 100644 --- a/examples/bezier_tool/src/main.rs +++ b/examples/bezier_tool/src/main.rs @@ -102,7 +102,7 @@ mod bezier { // Draw rectangle border with lyon. basic_shapes::stroke_rectangle( &lyon::math::Rect::new( - lyon::math::Point::new(bounds.x + 0.5, bounds.y + 0.5), + lyon::math::Point::new(0.5, 0.5), lyon::math::Size::new( bounds.width - 1.0, bounds.height - 1.0, @@ -121,48 +121,35 @@ mod bezier { for curve in self.curves { path_builder.move_to(lyon::math::Point::new( - curve.from.x + bounds.x, - curve.from.y + bounds.y, + curve.from.x, + curve.from.y, )); path_builder.quadratic_bezier_to( - lyon::math::Point::new( - curve.control.x + bounds.x, - curve.control.y + bounds.y, - ), - lyon::math::Point::new( - curve.to.x + bounds.x, - curve.to.y + bounds.y, - ), + lyon::math::Point::new(curve.control.x, curve.control.y), + lyon::math::Point::new(curve.to.x, curve.to.y), ); } match self.state.pending { None => {} Some(Pending::One { from }) => { - path_builder.move_to(lyon::math::Point::new( - from.x + bounds.x, - from.y + bounds.y, - )); + path_builder + .move_to(lyon::math::Point::new(from.x, from.y)); path_builder.line_to(lyon::math::Point::new( - cursor_position.x, - cursor_position.y, + cursor_position.x - bounds.x, + cursor_position.y - bounds.y, )); } Some(Pending::Two { from, to }) => { - path_builder.move_to(lyon::math::Point::new( - from.x + bounds.x, - from.y + bounds.y, - )); + path_builder + .move_to(lyon::math::Point::new(from.x, from.y)); path_builder.quadratic_bezier_to( lyon::math::Point::new( - cursor_position.x, - cursor_position.y, - ), - lyon::math::Point::new( - to.x + bounds.x, - to.y + bounds.y, + cursor_position.x - bounds.x, + cursor_position.y - bounds.y, ), + lyon::math::Point::new(to.x, to.y), ); } } @@ -186,10 +173,13 @@ mod bezier { ) .unwrap(); - let mesh = Primitive::Mesh2D(Arc::new(Mesh2D { - vertices: buffer.vertices, - indices: buffer.indices, - })); + let mesh = Primitive::Mesh2D { + origin: Point::new(bounds.x, bounds.y), + buffers: Arc::new(Mesh2D { + vertices: buffer.vertices, + indices: buffer.indices, + }), + }; ( Primitive::Clip { diff --git a/examples/geometry/src/main.rs b/examples/geometry/src/main.rs index 9d5fd611a9..795c6a717f 100644 --- a/examples/geometry/src/main.rs +++ b/examples/geometry/src/main.rs @@ -69,72 +69,75 @@ mod rainbow { let posn_center = { if b.contains(cursor_position) { - [cursor_position.x, cursor_position.y] + [cursor_position.x - b.x, cursor_position.y - b.y] } else { - [b.x + (b.width / 2.0), b.y + (b.height / 2.0)] + [b.width / 2.0, b.height / 2.0] } }; - let posn_tl = [b.x, b.y]; - let posn_t = [b.x + (b.width / 2.0), b.y]; - let posn_tr = [b.x + b.width, b.y]; - let posn_r = [b.x + b.width, b.y + (b.height / 2.0)]; - let posn_br = [b.x + b.width, b.y + b.height]; - let posn_b = [b.x + (b.width / 2.0), b.y + b.height]; - let posn_bl = [b.x, b.y + b.height]; - let posn_l = [b.x, b.y + (b.height / 2.0)]; + let posn_tl = [0.0, 0.0]; + let posn_t = [b.width / 2.0, 0.0]; + let posn_tr = [b.width, 0.0]; + let posn_r = [b.width, b.height / 2.0]; + let posn_br = [b.width, b.height]; + let posn_b = [(b.width / 2.0), b.height]; + let posn_bl = [0.0, b.height]; + let posn_l = [0.0, b.height / 2.0]; ( - Primitive::Mesh2D(std::sync::Arc::new(Mesh2D { - vertices: vec![ - Vertex2D { - position: posn_center, - color: [1.0, 1.0, 1.0, 1.0], - }, - Vertex2D { - position: posn_tl, - color: color_r, - }, - Vertex2D { - position: posn_t, - color: color_o, - }, - Vertex2D { - position: posn_tr, - color: color_y, - }, - Vertex2D { - position: posn_r, - color: color_g, - }, - Vertex2D { - position: posn_br, - color: color_gb, - }, - Vertex2D { - position: posn_b, - color: color_b, - }, - Vertex2D { - position: posn_bl, - color: color_i, - }, - Vertex2D { - position: posn_l, - color: color_v, - }, - ], - indices: vec![ - 0, 1, 2, // TL - 0, 2, 3, // T - 0, 3, 4, // TR - 0, 4, 5, // R - 0, 5, 6, // BR - 0, 6, 7, // B - 0, 7, 8, // BL - 0, 8, 1, // L - ], - })), + Primitive::Mesh2D { + origin: Point::new(b.x, b.y), + buffers: std::sync::Arc::new(Mesh2D { + vertices: vec![ + Vertex2D { + position: posn_center, + color: [1.0, 1.0, 1.0, 1.0], + }, + Vertex2D { + position: posn_tl, + color: color_r, + }, + Vertex2D { + position: posn_t, + color: color_o, + }, + Vertex2D { + position: posn_tr, + color: color_y, + }, + Vertex2D { + position: posn_r, + color: color_g, + }, + Vertex2D { + position: posn_br, + color: color_gb, + }, + Vertex2D { + position: posn_b, + color: color_b, + }, + Vertex2D { + position: posn_bl, + color: color_i, + }, + Vertex2D { + position: posn_l, + color: color_v, + }, + ], + indices: vec![ + 0, 1, 2, // TL + 0, 2, 3, // T + 0, 3, 4, // TR + 0, 4, 5, // R + 0, 5, 6, // BR + 0, 6, 7, // B + 0, 7, 8, // BL + 0, 8, 1, // L + ], + }), + }, MouseCursor::OutOfBounds, ) } From de8f06b512ec65f625417e334dca30990248c968 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Wed, 12 Feb 2020 09:12:35 +0100 Subject: [PATCH 11/45] Split `Fill` and `Stroke` into their own modules --- wgpu/src/widget/canvas.rs | 83 ++------------------------------ wgpu/src/widget/canvas/fill.rs | 12 +++++ wgpu/src/widget/canvas/stroke.rs | 66 +++++++++++++++++++++++++ 3 files changed, 83 insertions(+), 78 deletions(-) create mode 100644 wgpu/src/widget/canvas/fill.rs create mode 100644 wgpu/src/widget/canvas/stroke.rs diff --git a/wgpu/src/widget/canvas.rs b/wgpu/src/widget/canvas.rs index c984fee968..1fc3ff015d 100644 --- a/wgpu/src/widget/canvas.rs +++ b/wgpu/src/widget/canvas.rs @@ -2,8 +2,7 @@ use crate::{Defaults, Primitive, Renderer}; use iced_native::{ - layout, Color, Element, Hasher, Layout, Length, MouseCursor, Point, Size, - Widget, + layout, Element, Hasher, Layout, Length, MouseCursor, Point, Size, Widget, }; use std::hash::Hash; @@ -11,12 +10,16 @@ pub mod layer; pub mod path; mod data; +mod fill; mod frame; +mod stroke; pub use data::Data; +pub use fill::Fill; pub use frame::Frame; pub use layer::Layer; pub use path::Path; +pub use stroke::{LineCap, LineJoin, Stroke}; /// A 2D drawable region. #[derive(Debug)] @@ -115,79 +118,3 @@ where Element::new(canvas) } } - -#[derive(Debug, Clone, Copy)] -pub struct Stroke { - pub color: Color, - pub width: f32, - pub line_cap: LineCap, - pub line_join: LineJoin, -} - -impl Default for Stroke { - fn default() -> Stroke { - Stroke { - color: Color::BLACK, - width: 1.0, - line_cap: LineCap::default(), - line_join: LineJoin::default(), - } - } -} - -#[derive(Debug, Clone, Copy)] -pub enum LineCap { - Butt, - Square, - Round, -} - -impl Default for LineCap { - fn default() -> LineCap { - LineCap::Butt - } -} - -impl From 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, - } - } -} - -#[derive(Debug, Clone, Copy)] -pub enum LineJoin { - Miter, - Round, - Bevel, -} - -impl Default for LineJoin { - fn default() -> LineJoin { - LineJoin::Miter - } -} - -impl From 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, - } - } -} - -#[derive(Debug, Clone, Copy)] -pub enum Fill { - Color(Color), -} - -impl Default for Fill { - fn default() -> Fill { - Fill::Color(Color::BLACK) - } -} diff --git a/wgpu/src/widget/canvas/fill.rs b/wgpu/src/widget/canvas/fill.rs new file mode 100644 index 0000000000..9c23f9972d --- /dev/null +++ b/wgpu/src/widget/canvas/fill.rs @@ -0,0 +1,12 @@ +use iced_native::Color; + +#[derive(Debug, Clone, Copy)] +pub enum Fill { + Color(Color), +} + +impl Default for Fill { + fn default() -> Fill { + Fill::Color(Color::BLACK) + } +} diff --git a/wgpu/src/widget/canvas/stroke.rs b/wgpu/src/widget/canvas/stroke.rs new file mode 100644 index 0000000000..9bb260b2f9 --- /dev/null +++ b/wgpu/src/widget/canvas/stroke.rs @@ -0,0 +1,66 @@ +use iced_native::Color; + +#[derive(Debug, Clone, Copy)] +pub struct Stroke { + pub color: Color, + pub width: f32, + pub line_cap: LineCap, + pub line_join: LineJoin, +} + +impl Default for Stroke { + fn default() -> Stroke { + Stroke { + color: Color::BLACK, + width: 1.0, + line_cap: LineCap::default(), + line_join: LineJoin::default(), + } + } +} + +#[derive(Debug, Clone, Copy)] +pub enum LineCap { + Butt, + Square, + Round, +} + +impl Default for LineCap { + fn default() -> LineCap { + LineCap::Butt + } +} + +impl From 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, + } + } +} + +#[derive(Debug, Clone, Copy)] +pub enum LineJoin { + Miter, + Round, + Bevel, +} + +impl Default for LineJoin { + fn default() -> LineJoin { + LineJoin::Miter + } +} + +impl From 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, + } + } +} From 4a24392c9c078c56ea26b49c455c9ef183fa79a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Wed, 12 Feb 2020 09:21:13 +0100 Subject: [PATCH 12/45] Simplify `Clock::new` in `clock` example --- examples/clock/src/main.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/examples/clock/src/main.rs b/examples/clock/src/main.rs index e70ce91a10..e30158f7dc 100644 --- a/examples/clock/src/main.rs +++ b/examples/clock/src/main.rs @@ -22,11 +22,9 @@ impl Application for Clock { type Message = Message; fn new() -> (Self, Command) { - let now: LocalTime = chrono::Local::now().into(); - ( Clock { - now, + now: chrono::Local::now().into(), clock: canvas::layer::Cached::new(), }, Command::none(), From 629153582f1a43516092d941d2b4e2372a6ea054 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Wed, 12 Feb 2020 09:24:22 +0100 Subject: [PATCH 13/45] Remove `canvas::Data` leftover --- wgpu/src/widget/canvas.rs | 2 -- wgpu/src/widget/canvas/data.rs | 20 -------------------- 2 files changed, 22 deletions(-) delete mode 100644 wgpu/src/widget/canvas/data.rs diff --git a/wgpu/src/widget/canvas.rs b/wgpu/src/widget/canvas.rs index 1fc3ff015d..e8fdc1e8ee 100644 --- a/wgpu/src/widget/canvas.rs +++ b/wgpu/src/widget/canvas.rs @@ -9,12 +9,10 @@ use std::hash::Hash; pub mod layer; pub mod path; -mod data; mod fill; mod frame; mod stroke; -pub use data::Data; pub use fill::Fill; pub use frame::Frame; pub use layer::Layer; diff --git a/wgpu/src/widget/canvas/data.rs b/wgpu/src/widget/canvas/data.rs deleted file mode 100644 index 25d94f4cc3..0000000000 --- a/wgpu/src/widget/canvas/data.rs +++ /dev/null @@ -1,20 +0,0 @@ -#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)] -pub struct Data { - raw: T, - version: usize, -} - -impl Data { - pub fn new(data: T) -> Self { - Data { - raw: data, - version: 0, - } - } - - pub fn update(&mut self, f: impl FnOnce(&mut T)) { - f(&mut self.raw); - - self.version += 1; - } -} From 265c08661ca7943473c2ef305246a75bfc7847ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Wed, 12 Feb 2020 22:08:49 +0100 Subject: [PATCH 14/45] Fix circle `end_angle` in `clock` example --- examples/clock/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/clock/src/main.rs b/examples/clock/src/main.rs index e30158f7dc..25849d1ae2 100644 --- a/examples/clock/src/main.rs +++ b/examples/clock/src/main.rs @@ -100,7 +100,7 @@ impl canvas::layer::Drawable for LocalTime { center, radius, start_angle: 0.0, - end_angle: 360.0 * 2.0 * std::f32::consts::PI, + end_angle: 2.0 * std::f32::consts::PI, }) }); From 9dc9305d93b9c03d5ea309ecce148857f9bb0745 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Wed, 12 Feb 2020 22:10:00 +0100 Subject: [PATCH 15/45] Remove redundant conversion in `clock` example --- examples/clock/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/clock/src/main.rs b/examples/clock/src/main.rs index 25849d1ae2..8259e382d0 100644 --- a/examples/clock/src/main.rs +++ b/examples/clock/src/main.rs @@ -92,7 +92,7 @@ impl From> for LocalTime { impl canvas::layer::Drawable for LocalTime { fn draw(&self, frame: &mut canvas::Frame) { let center = frame.center(); - let radius = frame.width().min(frame.height()) as f32 / 2.0; + let radius = frame.width().min(frame.height()) / 2.0; let offset = Vector::new(center.x, center.y); let path = canvas::Path::new(|path| { From e7c400a0aaa01320e80aeed726e7ba702af9380b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Wed, 12 Feb 2020 22:13:59 +0100 Subject: [PATCH 16/45] Improve naming in `clock` example --- examples/clock/src/main.rs | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/examples/clock/src/main.rs b/examples/clock/src/main.rs index 8259e382d0..160adffd24 100644 --- a/examples/clock/src/main.rs +++ b/examples/clock/src/main.rs @@ -42,7 +42,6 @@ impl Application for Clock { if now != self.now { self.now = now; - self.clock.clear(); } } @@ -95,7 +94,7 @@ impl canvas::layer::Drawable for LocalTime { let radius = frame.width().min(frame.height()) / 2.0; let offset = Vector::new(center.x, center.y); - let path = canvas::Path::new(|path| { + let clock = canvas::Path::new(|path| { path.arc(canvas::path::Arc { center, radius, @@ -105,11 +104,11 @@ impl canvas::layer::Drawable for LocalTime { }); frame.fill( - &path, + &clock, canvas::Fill::Color(Color::from_rgb8(0x12, 0x93, 0xD8)), ); - fn draw_handle( + fn draw_hand( n: u32, total: u32, length: f32, @@ -125,16 +124,16 @@ impl canvas::layer::Drawable for LocalTime { path.line_to(Point::new(x, y) + offset); } - let path = canvas::Path::new(|path| { + let hour_and_minute_hands = canvas::Path::new(|path| { path.move_to(center); - draw_handle(self.hour, 12, 0.5 * radius, offset, path); + draw_hand(self.hour, 12, 0.5 * radius, offset, path); path.move_to(center); - draw_handle(self.minute, 60, 0.8 * radius, offset, path) + draw_hand(self.minute, 60, 0.8 * radius, offset, path) }); frame.stroke( - &path, + &hour_and_minute_hands, canvas::Stroke { width: 6.0, color: Color::WHITE, @@ -143,13 +142,13 @@ impl canvas::layer::Drawable for LocalTime { }, ); - let path = canvas::Path::new(|path| { + let second_hand = canvas::Path::new(|path| { path.move_to(center); - draw_handle(self.second, 60, 0.8 * radius, offset, path) + draw_hand(self.second, 60, 0.8 * radius, offset, path) }); frame.stroke( - &path, + &second_hand, canvas::Stroke { width: 3.0, color: Color::WHITE, From 979edeb213c2ca0dd394a9a6c68a30dfab371263 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Wed, 12 Feb 2020 22:38:36 +0100 Subject: [PATCH 17/45] Fix `clock` example eventually skipping a second --- examples/clock/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/clock/src/main.rs b/examples/clock/src/main.rs index 160adffd24..25a213cd81 100644 --- a/examples/clock/src/main.rs +++ b/examples/clock/src/main.rs @@ -51,7 +51,7 @@ impl Application for Clock { } fn subscription(&self) -> Subscription { - time::every(std::time::Duration::from_millis(1000)).map(Message::Tick) + time::every(std::time::Duration::from_millis(500)).map(Message::Tick) } fn view(&mut self) -> Element { From df90c478e28892537efc0009d468a521d4d7fee5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Thu, 13 Feb 2020 03:45:07 +0100 Subject: [PATCH 18/45] Move `layer::Cached` to its own module --- wgpu/src/widget/canvas/layer.rs | 80 ++----------------------- wgpu/src/widget/canvas/layer/cached.rs | 82 ++++++++++++++++++++++++++ 2 files changed, 86 insertions(+), 76 deletions(-) create mode 100644 wgpu/src/widget/canvas/layer/cached.rs diff --git a/wgpu/src/widget/canvas/layer.rs b/wgpu/src/widget/canvas/layer.rs index c239a2542a..8c069f184e 100644 --- a/wgpu/src/widget/canvas/layer.rs +++ b/wgpu/src/widget/canvas/layer.rs @@ -1,88 +1,16 @@ +mod cached; + +pub use cached::Cached; + use crate::{canvas::Frame, triangle}; use iced_native::Size; -use std::cell::RefCell; use std::sync::Arc; pub trait Layer: std::fmt::Debug { fn draw(&self, bounds: Size) -> Arc; } -use std::marker::PhantomData; - -#[derive(Debug)] -pub struct Cached { - input: PhantomData, - cache: RefCell, -} - -#[derive(Debug)] -enum Cache { - Empty, - Filled { - mesh: Arc, - bounds: Size, - }, -} - -impl Cached -where - T: Drawable + std::fmt::Debug, -{ - pub fn new() -> Self { - Cached { - input: PhantomData, - cache: RefCell::new(Cache::Empty), - } - } - - pub fn clear(&mut self) { - *self.cache.borrow_mut() = Cache::Empty; - } - - pub fn with<'a>(&'a self, input: &'a T) -> impl Layer + 'a { - Bind { - layer: self, - input: input, - } - } -} - -#[derive(Debug)] -struct Bind<'a, T: Drawable> { - layer: &'a Cached, - input: &'a T, -} - -impl<'a, T> Layer for Bind<'a, T> -where - T: Drawable + std::fmt::Debug, -{ - fn draw(&self, current_bounds: Size) -> Arc { - use std::ops::Deref; - - if let Cache::Filled { mesh, bounds } = - self.layer.cache.borrow().deref() - { - if *bounds == current_bounds { - return mesh.clone(); - } - } - - let mut frame = Frame::new(current_bounds.width, current_bounds.height); - self.input.draw(&mut frame); - - let mesh = Arc::new(frame.into_mesh()); - - *self.layer.cache.borrow_mut() = Cache::Filled { - mesh: mesh.clone(), - bounds: current_bounds, - }; - - mesh - } -} - pub trait Drawable { fn draw(&self, frame: &mut Frame); } diff --git a/wgpu/src/widget/canvas/layer/cached.rs b/wgpu/src/widget/canvas/layer/cached.rs new file mode 100644 index 0000000000..c6741372b4 --- /dev/null +++ b/wgpu/src/widget/canvas/layer/cached.rs @@ -0,0 +1,82 @@ +use crate::{ + canvas::{layer::Drawable, Frame, Layer}, + triangle, +}; + +use iced_native::Size; +use std::cell::RefCell; +use std::marker::PhantomData; +use std::sync::Arc; + +#[derive(Debug)] +pub struct Cached { + input: PhantomData, + cache: RefCell, +} + +#[derive(Debug)] +enum Cache { + Empty, + Filled { + mesh: Arc, + bounds: Size, + }, +} + +impl Cached +where + T: Drawable + std::fmt::Debug, +{ + pub fn new() -> Self { + Cached { + input: PhantomData, + cache: RefCell::new(Cache::Empty), + } + } + + pub fn clear(&mut self) { + *self.cache.borrow_mut() = Cache::Empty; + } + + pub fn with<'a>(&'a self, input: &'a T) -> impl Layer + 'a { + Bind { + layer: self, + input: input, + } + } +} + +#[derive(Debug)] +struct Bind<'a, T: Drawable> { + layer: &'a Cached, + input: &'a T, +} + +impl<'a, T> Layer for Bind<'a, T> +where + T: Drawable + std::fmt::Debug, +{ + fn draw(&self, current_bounds: Size) -> Arc { + use std::ops::Deref; + + if let Cache::Filled { mesh, bounds } = + self.layer.cache.borrow().deref() + { + if *bounds == current_bounds { + return mesh.clone(); + } + } + + let mut frame = Frame::new(current_bounds.width, current_bounds.height); + self.input.draw(&mut frame); + + let mesh = Arc::new(frame.into_mesh()); + + *self.layer.cache.borrow_mut() = Cache::Filled { + mesh: mesh.clone(), + bounds: current_bounds, + }; + + mesh + } +} From 76df374624e5d82dcb2670789a6c4ff228dda9e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Fri, 14 Feb 2020 02:23:41 +0100 Subject: [PATCH 19/45] Implement additional methods in `path::Builder` --- examples/clock/src/main.rs | 9 +--- wgpu/src/widget/canvas/path.rs | 85 ++++++++++++++++++++++++++++++---- 2 files changed, 76 insertions(+), 18 deletions(-) diff --git a/examples/clock/src/main.rs b/examples/clock/src/main.rs index 25a213cd81..0a70709fdb 100644 --- a/examples/clock/src/main.rs +++ b/examples/clock/src/main.rs @@ -94,14 +94,7 @@ impl canvas::layer::Drawable for LocalTime { let radius = frame.width().min(frame.height()) / 2.0; let offset = Vector::new(center.x, center.y); - let clock = canvas::Path::new(|path| { - path.arc(canvas::path::Arc { - center, - radius, - start_angle: 0.0, - end_angle: 2.0 * std::f32::consts::PI, - }) - }); + let clock = canvas::Path::new(|path| path.circle(center, radius)); frame.fill( &clock, diff --git a/wgpu/src/widget/canvas/path.rs b/wgpu/src/widget/canvas/path.rs index c8ba10e1a6..8847ea29fa 100644 --- a/wgpu/src/widget/canvas/path.rs +++ b/wgpu/src/widget/canvas/path.rs @@ -1,4 +1,6 @@ -use iced_native::{Point, Vector}; +use iced_native::{Point, Size, Vector}; + +use lyon::path::builder::{Build, FlatPathBuilder, PathBuilder, SvgBuilder}; #[derive(Debug, Clone)] pub struct Path { @@ -23,13 +25,13 @@ impl Path { #[allow(missing_debug_implementations)] pub struct Builder { - raw: lyon::path::Builder, + raw: lyon::path::builder::SvgPathBuilder, } impl Builder { pub fn new() -> Builder { Builder { - raw: lyon::path::Path::builder(), + raw: lyon::path::Path::builder().with_svg(), } } @@ -48,14 +50,32 @@ impl Builder { self.ellipse(arc.into()); } - #[inline] + pub fn arc_to(&mut self, a: Point, b: Point, radius: f32) { + use lyon::{math, path}; + + let a = math::Point::new(a.x, a.y); + + if self.raw.current_position() != a { + let _ = self.raw.line_to(a); + } + + let _ = self.raw.arc_to( + math::Vector::new(radius, radius), + math::Angle::radians(0.0), + path::ArcFlags::default(), + math::Point::new(b.x, b.y), + ); + } + pub fn ellipse(&mut self, ellipse: Ellipse) { - let arc = lyon::geom::Arc { - center: lyon::math::Point::new(ellipse.center.x, ellipse.center.y), - radii: lyon::math::Vector::new(ellipse.radii.x, ellipse.radii.y), - x_rotation: lyon::math::Angle::radians(ellipse.rotation), - start_angle: lyon::math::Angle::radians(ellipse.start_angle), - sweep_angle: lyon::math::Angle::radians(ellipse.end_angle), + use lyon::{geom, math}; + + let arc = geom::Arc { + center: math::Point::new(ellipse.center.x, ellipse.center.y), + radii: math::Vector::new(ellipse.radii.x, ellipse.radii.y), + x_rotation: math::Angle::radians(ellipse.rotation), + start_angle: math::Angle::radians(ellipse.start_angle), + sweep_angle: math::Angle::radians(ellipse.end_angle), }; let _ = self.raw.move_to(arc.sample(0.0)); @@ -65,6 +85,51 @@ impl Builder { }); } + #[inline] + pub fn bezier_curve_to( + &mut self, + control_a: Point, + 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), + math::Point::new(to.x, to.y), + ); + } + + #[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), + ); + } + + #[inline] + pub fn rectangle(&mut self, p: Point, size: Size) { + self.move_to(p); + self.line_to(Point::new(p.x + size.width, p.y)); + self.line_to(Point::new(p.x + size.width, p.y + size.height)); + self.line_to(Point::new(p.x, p.y + size.height)); + self.close(); + } + + #[inline] + pub fn circle(&mut self, center: Point, radius: f32) { + self.arc(Arc { + center, + radius, + start_angle: 0.0, + end_angle: 2.0 * std::f32::consts::PI, + }); + } + #[inline] pub fn close(&mut self) { self.raw.close() From 558abf648bdeb86d92e7092f4b023d5e55cc673c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Fri, 14 Feb 2020 04:59:31 +0100 Subject: [PATCH 20/45] Add transform stack to `canvas::Frame` --- core/src/color.rs | 9 ++- core/src/point.rs | 7 ++- src/lib.rs | 2 +- web/src/lib.rs | 4 +- wgpu/src/widget/canvas/frame.rs | 102 +++++++++++++++++++++++++++++--- wgpu/src/widget/canvas/path.rs | 10 ++++ 6 files changed, 122 insertions(+), 12 deletions(-) diff --git a/core/src/color.rs b/core/src/color.rs index d6bdd3651a..db509b88ff 100644 --- a/core/src/color.rs +++ b/core/src/color.rs @@ -44,11 +44,18 @@ impl Color { /// /// [`Color`]: struct.Color.html pub fn from_rgb8(r: u8, g: u8, b: u8) -> Color { + Color::from_rgba8(r, g, b, 1.0) + } + + /// Creates a [`Color`] from its RGB8 components and an alpha value. + /// + /// [`Color`]: struct.Color.html + pub fn from_rgba8(r: u8, g: u8, b: u8, a: f32) -> Color { Color { r: f32::from(r) / 255.0, g: f32::from(g) / 255.0, b: f32::from(b) / 255.0, - a: 1.0, + a, } } diff --git a/core/src/point.rs b/core/src/point.rs index 47c8b14247..b9a8149cbe 100644 --- a/core/src/point.rs +++ b/core/src/point.rs @@ -11,10 +11,15 @@ pub struct Point { } impl Point { + /// The origin (i.e. a [`Point`] with both X=0 and Y=0). + /// + /// [`Point`]: struct.Point.html + pub const ORIGIN: Point = Point::new(0.0, 0.0); + /// Creates a new [`Point`] with the given coordinates. /// /// [`Point`]: struct.Point.html - pub fn new(x: f32, y: f32) -> Self { + pub const fn new(x: f32, y: f32) -> Self { Self { x, y } } } diff --git a/src/lib.rs b/src/lib.rs index 598706920a..d492db029f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -204,5 +204,5 @@ use iced_web as common; pub use common::{ futures, Align, Background, Color, Command, Font, HorizontalAlignment, - Length, Point, Space, Subscription, Vector, VerticalAlignment, + Length, Point, Size, Space, Subscription, Vector, VerticalAlignment, }; diff --git a/web/src/lib.rs b/web/src/lib.rs index 4dc7aba70c..258ad9e7b4 100644 --- a/web/src/lib.rs +++ b/web/src/lib.rs @@ -73,8 +73,8 @@ pub use dodrio; pub use element::Element; pub use hasher::Hasher; pub use iced_core::{ - Align, Background, Color, Font, HorizontalAlignment, Length, Point, Vector, - VerticalAlignment, + Align, Background, Color, Font, HorizontalAlignment, Length, Point, Size, + Vector, VerticalAlignment, }; pub use iced_futures::{executor, futures, Command}; pub use subscription::Subscription; diff --git a/wgpu/src/widget/canvas/frame.rs b/wgpu/src/widget/canvas/frame.rs index 3c667426df..687f6c37b4 100644 --- a/wgpu/src/widget/canvas/frame.rs +++ b/wgpu/src/widget/canvas/frame.rs @@ -1,4 +1,4 @@ -use iced_native::Point; +use iced_native::{Point, Size, Vector}; use crate::{ canvas::{Fill, Path, Stroke}, @@ -10,6 +10,20 @@ pub struct Frame { width: f32, height: f32, buffers: lyon::tessellation::VertexBuffers, + + transforms: Transforms, +} + +#[derive(Debug)] +struct Transforms { + previous: Vec, + current: Transform, +} + +#[derive(Debug, Clone, Copy)] +struct Transform { + raw: lyon::math::Transform, + is_identity: bool, } impl Frame { @@ -18,17 +32,32 @@ impl Frame { width, height, buffers: lyon::tessellation::VertexBuffers::new(), + transforms: Transforms { + previous: Vec::new(), + current: Transform { + raw: lyon::math::Transform::identity(), + is_identity: true, + }, + }, } } + #[inline] pub fn width(&self) -> f32 { self.width } + #[inline] pub fn height(&self) -> f32 { self.height } + #[inline] + pub fn size(&self) -> Size { + Size::new(self.width, self.height) + } + + #[inline] pub fn center(&self) -> Point { Point::new(self.width / 2.0, self.height / 2.0) } @@ -47,9 +76,23 @@ impl Frame { let mut tessellator = FillTessellator::new(); - let _ = tessellator - .tessellate_path(path.raw(), &FillOptions::default(), &mut buffers) - .expect("Tessellate path"); + let result = if self.transforms.current.is_identity { + tessellator.tessellate_path( + path.raw(), + &FillOptions::default(), + &mut buffers, + ) + } else { + let path = path.transformed(&self.transforms.current.raw); + + tessellator.tessellate_path( + path.raw(), + &FillOptions::default(), + &mut buffers, + ) + }; + + let _ = result.expect("Tessellate path"); } pub fn stroke(&mut self, path: &Path, stroke: Stroke) { @@ -70,9 +113,54 @@ impl Frame { options.end_cap = stroke.line_cap.into(); options.line_join = stroke.line_join.into(); - let _ = tessellator - .tessellate_path(path.raw(), &options, &mut buffers) - .expect("Stroke path"); + let result = if self.transforms.current.is_identity { + tessellator.tessellate_path(path.raw(), &options, &mut buffers) + } else { + let path = path.transformed(&self.transforms.current.raw); + + tessellator.tessellate_path(path.raw(), &options, &mut buffers) + }; + + let _ = result.expect("Stroke path"); + } + + #[inline] + pub fn with_save(&mut self, f: impl FnOnce(&mut Frame)) { + self.transforms.previous.push(self.transforms.current); + + f(self); + + self.transforms.current = self.transforms.previous.pop().unwrap(); + } + + #[inline] + pub fn translate(&mut self, translation: Vector) { + self.transforms.current.raw = self + .transforms + .current + .raw + .pre_translate(lyon::math::Vector::new( + translation.x, + translation.y, + )); + self.transforms.current.is_identity = false; + } + + #[inline] + pub fn rotate(&mut self, angle: f32) { + self.transforms.current.raw = self + .transforms + .current + .raw + .pre_rotate(lyon::math::Angle::radians(-angle)); + self.transforms.current.is_identity = false; + } + + #[inline] + pub fn scale(&mut self, scale: f32) { + self.transforms.current.raw = + self.transforms.current.raw.pre_scale(scale, scale); + self.transforms.current.is_identity = false; } pub fn into_mesh(self) -> triangle::Mesh2D { diff --git a/wgpu/src/widget/canvas/path.rs b/wgpu/src/widget/canvas/path.rs index 8847ea29fa..b70d0aef9a 100644 --- a/wgpu/src/widget/canvas/path.rs +++ b/wgpu/src/widget/canvas/path.rs @@ -21,6 +21,16 @@ impl Path { pub(crate) fn raw(&self) -> &lyon::path::Path { &self.raw } + + #[inline] + pub(crate) fn transformed( + &self, + transform: &lyon::math::Transform, + ) -> Path { + Path { + raw: self.raw.transformed(transform), + } + } } #[allow(missing_debug_implementations)] From ad3a0a184f877cbca3db4006e802231e201138ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Fri, 14 Feb 2020 05:33:58 +0100 Subject: [PATCH 21/45] Add `solar_system` example --- Cargo.toml | 1 + examples/solar_system/Cargo.toml | 15 ++ examples/solar_system/src/main.rs | 244 ++++++++++++++++++++++++++++++ 3 files changed, 260 insertions(+) create mode 100644 examples/solar_system/Cargo.toml create mode 100644 examples/solar_system/src/main.rs diff --git a/Cargo.toml b/Cargo.toml index dd099c3264..01231b70af 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,6 +46,7 @@ members = [ "examples/integration", "examples/pokedex", "examples/progress_bar", + "examples/solar_system", "examples/stopwatch", "examples/styling", "examples/svg", diff --git a/examples/solar_system/Cargo.toml b/examples/solar_system/Cargo.toml new file mode 100644 index 0000000000..c88cda5043 --- /dev/null +++ b/examples/solar_system/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "solar_system" +version = "0.1.0" +authors = ["Héctor Ramón Jiménez "] +edition = "2018" +publish = false + +[features] +canvas = [] + +[dependencies] +iced = { path = "../..", features = ["canvas", "async-std", "debug"] } +iced_native = { path = "../../native" } +async-std = { version = "1.0", features = ["unstable"] } +rand = "0.7" diff --git a/examples/solar_system/src/main.rs b/examples/solar_system/src/main.rs new file mode 100644 index 0000000000..d05acf841c --- /dev/null +++ b/examples/solar_system/src/main.rs @@ -0,0 +1,244 @@ +//! An animated solar system. +//! +//! This example showcases how to use a `Canvas` widget with transforms to draw +//! using different coordinate systems. +//! +//! Inspired by the example found in the MDN docs[1]. +//! +//! [1]: https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Basic_animations#An_animated_solar_system +use iced::{ + canvas, executor, Application, Canvas, Color, Command, Container, Element, + Length, Point, Settings, Size, Subscription, Vector, +}; + +use std::time::Instant; + +pub fn main() { + SolarSystem::run(Settings::default()) +} + +struct SolarSystem { + state: State, + solar_system: canvas::layer::Cached, +} + +#[derive(Debug, Clone, Copy)] +enum Message { + Tick(Instant), +} + +impl Application for SolarSystem { + type Executor = executor::Default; + type Message = Message; + + fn new() -> (Self, Command) { + ( + SolarSystem { + state: State::new(), + solar_system: canvas::layer::Cached::new(), + }, + Command::none(), + ) + } + + fn title(&self) -> String { + String::from("Solar system - Iced") + } + + fn update(&mut self, message: Message) -> Command { + match message { + Message::Tick(instant) => { + self.state.update(instant); + self.solar_system.clear(); + } + } + + Command::none() + } + + fn subscription(&self) -> Subscription { + time::every(std::time::Duration::from_millis(10)) + .map(|instant| Message::Tick(instant)) + } + + fn view(&mut self) -> Element { + let canvas = Canvas::new() + .width(Length::Fill) + .height(Length::Fill) + .push(self.solar_system.with(&self.state)); + + Container::new(canvas) + .width(Length::Fill) + .height(Length::Fill) + .center_x() + .center_y() + .into() + } +} + +#[derive(Debug)] +struct State { + start: Instant, + current: Instant, + stars: Vec<(Point, f32)>, +} + +impl State { + const SUN_RADIUS: f32 = 70.0; + const ORBIT_RADIUS: f32 = 150.0; + const EARTH_RADIUS: f32 = 12.0; + const MOON_RADIUS: f32 = 4.0; + const MOON_DISTANCE: f32 = 28.0; + + pub fn new() -> State { + let now = Instant::now(); + let (width, height) = Settings::default().window.size; + + State { + start: now, + current: now, + stars: { + use rand::Rng; + + let mut rng = rand::thread_rng(); + + (0..100) + .map(|_| { + ( + Point::new( + rng.gen_range(0.0, width as f32), + rng.gen_range(0.0, height as f32), + ), + rng.gen_range(0.5, 1.0), + ) + }) + .collect() + }, + } + } + + pub fn update(&mut self, now: Instant) { + self.current = now; + } +} + +impl canvas::layer::Drawable for State { + fn draw(&self, frame: &mut canvas::Frame) { + use canvas::{Fill, Path, Stroke}; + use std::f32::consts::PI; + + let center = frame.center(); + + let space = Path::new(|path| { + path.rectangle(Point::new(0.0, 0.0), frame.size()) + }); + + let stars = Path::new(|path| { + for (p, size) in &self.stars { + path.rectangle(*p, Size::new(*size, *size)); + } + }); + + let sun = Path::new(|path| path.circle(center, Self::SUN_RADIUS)); + let orbit = Path::new(|path| path.circle(center, Self::ORBIT_RADIUS)); + + frame.fill(&space, Fill::Color(Color::BLACK)); + frame.fill(&stars, Fill::Color(Color::WHITE)); + frame.fill(&sun, Fill::Color(Color::from_rgb8(0xF9, 0xD7, 0x1C))); + frame.stroke( + &orbit, + Stroke { + width: 1.0, + color: Color::from_rgba8(0, 153, 255, 0.1), + ..Stroke::default() + }, + ); + + let elapsed = self.current - self.start; + let elapsed_seconds = elapsed.as_secs() as f32; + let elapsed_millis = elapsed.subsec_millis() as f32; + + frame.with_save(|frame| { + frame.translate(Vector::new(center.x, center.y)); + frame.rotate( + (2.0 * PI / 60.0) * elapsed_seconds + + (2.0 * PI / 60_000.0) * elapsed_millis, + ); + frame.translate(Vector::new(Self::ORBIT_RADIUS, 0.0)); + + let earth = Path::new(|path| { + path.circle(Point::ORIGIN, Self::EARTH_RADIUS) + }); + + let shadow = Path::new(|path| { + path.rectangle( + Point::new(0.0, -Self::EARTH_RADIUS), + Size::new( + Self::EARTH_RADIUS * 4.0, + Self::EARTH_RADIUS * 2.0, + ), + ) + }); + + frame.fill(&earth, Fill::Color(Color::from_rgb8(0x6B, 0x93, 0xD6))); + + frame.with_save(|frame| { + frame.rotate( + ((2.0 * PI) / 6.0) * elapsed_seconds + + ((2.0 * PI) / 6_000.0) * elapsed_millis, + ); + frame.translate(Vector::new(0.0, Self::MOON_DISTANCE)); + + let moon = Path::new(|path| { + path.circle(Point::ORIGIN, Self::MOON_RADIUS) + }); + + frame.fill(&moon, Fill::Color(Color::WHITE)); + }); + + frame.fill( + &shadow, + Fill::Color(Color { + a: 0.7, + ..Color::BLACK + }), + ); + }); + } +} + +mod time { + use iced::futures; + use std::time::Instant; + + pub fn every(duration: std::time::Duration) -> iced::Subscription { + iced::Subscription::from_recipe(Every(duration)) + } + + struct Every(std::time::Duration); + + impl iced_native::subscription::Recipe for Every + where + H: std::hash::Hasher, + { + type Output = Instant; + + fn hash(&self, state: &mut H) { + use std::hash::Hash; + + std::any::TypeId::of::().hash(state); + self.0.hash(state); + } + + fn stream( + self: Box, + _input: futures::stream::BoxStream<'static, I>, + ) -> futures::stream::BoxStream<'static, Self::Output> { + use futures::stream::StreamExt; + + async_std::stream::interval(self.0) + .map(|_| Instant::now()) + .boxed() + } + } +} From 945dfabd7135d1bd44a14e54d95b716642651ed3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Fri, 14 Feb 2020 05:35:42 +0100 Subject: [PATCH 22/45] Move `Size` to `iced_core` --- core/src/lib.rs | 2 ++ {native => core}/src/size.rs | 0 native/src/lib.rs | 4 +--- 3 files changed, 3 insertions(+), 3 deletions(-) rename {native => core}/src/size.rs (100%) diff --git a/core/src/lib.rs b/core/src/lib.rs index 3cbce743e2..ea5e8b4365 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -22,6 +22,7 @@ mod font; mod length; mod point; mod rectangle; +mod size; mod vector; pub use align::{Align, HorizontalAlignment, VerticalAlignment}; @@ -31,4 +32,5 @@ pub use font::Font; pub use length::Length; pub use point::Point; pub use rectangle::Rectangle; +pub use size::Size; pub use vector::Vector; diff --git a/native/src/size.rs b/core/src/size.rs similarity index 100% rename from native/src/size.rs rename to core/src/size.rs diff --git a/native/src/lib.rs b/native/src/lib.rs index 3b81ef7167..e4e7baee38 100644 --- a/native/src/lib.rs +++ b/native/src/lib.rs @@ -52,12 +52,11 @@ mod event; mod hasher; mod mouse_cursor; mod runtime; -mod size; mod user_interface; pub use iced_core::{ Align, Background, Color, Font, HorizontalAlignment, Length, Point, - Rectangle, Vector, VerticalAlignment, + Rectangle, Size, Vector, VerticalAlignment, }; pub use iced_futures::{executor, futures, Command}; @@ -72,7 +71,6 @@ pub use layout::Layout; pub use mouse_cursor::MouseCursor; pub use renderer::Renderer; pub use runtime::Runtime; -pub use size::Size; pub use subscription::Subscription; pub use user_interface::{Cache, UserInterface}; pub use widget::*; From f5c80a6d75d5022b175d3562f0965598b6398bd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Fri, 14 Feb 2020 05:42:19 +0100 Subject: [PATCH 23/45] Upgrade `Mesh2D` indices from `u16` to `u32` --- examples/bezier_tool/src/main.rs | 2 +- wgpu/src/triangle.rs | 4 ++-- wgpu/src/widget/canvas/frame.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/bezier_tool/src/main.rs b/examples/bezier_tool/src/main.rs index fbb6fa248f..efdb3924ce 100644 --- a/examples/bezier_tool/src/main.rs +++ b/examples/bezier_tool/src/main.rs @@ -94,7 +94,7 @@ mod bezier { layout: Layout<'_>, cursor_position: Point, ) -> (Primitive, MouseCursor) { - let mut buffer: VertexBuffers = VertexBuffers::new(); + let mut buffer: VertexBuffers = VertexBuffers::new(); let mut path_builder = lyon::path::Path::builder(); let bounds = layout.bounds(); diff --git a/wgpu/src/triangle.rs b/wgpu/src/triangle.rs index 6f3adbe4a7..3cc1d3fb10 100644 --- a/wgpu/src/triangle.rs +++ b/wgpu/src/triangle.rs @@ -91,7 +91,7 @@ impl Pipeline { write_mask: wgpu::ColorWrite::ALL, }], depth_stencil_state: None, - index_format: wgpu::IndexFormat::Uint16, + index_format: wgpu::IndexFormat::Uint32, vertex_buffers: &[wgpu::VertexBufferDescriptor { stride: mem::size_of::() as u64, step_mode: wgpu::InputStepMode::Vertex, @@ -233,5 +233,5 @@ pub struct Mesh2D { /// 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, + pub indices: Vec, } diff --git a/wgpu/src/widget/canvas/frame.rs b/wgpu/src/widget/canvas/frame.rs index 687f6c37b4..27d676d69c 100644 --- a/wgpu/src/widget/canvas/frame.rs +++ b/wgpu/src/widget/canvas/frame.rs @@ -9,7 +9,7 @@ use crate::{ pub struct Frame { width: f32, height: f32, - buffers: lyon::tessellation::VertexBuffers, + buffers: lyon::tessellation::VertexBuffers, transforms: Transforms, } From 457d6f616af7359029b07d34d7e6cc1ab6cc6793 Mon Sep 17 00:00:00 2001 From: daxpedda Date: Fri, 14 Feb 2020 15:36:33 +0100 Subject: [PATCH 24/45] Make `Node::align` public. --- native/src/layout/node.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/native/src/layout/node.rs b/native/src/layout/node.rs index ed1cd3da8f..fae6e330d4 100644 --- a/native/src/layout/node.rs +++ b/native/src/layout/node.rs @@ -54,7 +54,11 @@ impl Node { &self.children } - pub(crate) fn align( + /// Aligns item according to [`Align`] and [`Size`]. + /// + /// [`Align`]: ../enum.Align.html + /// [`Size`]: ../struct.Size.html + pub fn align( &mut self, horizontal_alignment: Align, vertical_alignment: Align, From b72bd0b2b5c9c2a5c3f508a13ad9578169046a36 Mon Sep 17 00:00:00 2001 From: daxpedda Date: Fri, 14 Feb 2020 15:57:07 +0100 Subject: [PATCH 25/45] Add `bound` to `Node` constructor. --- native/src/layout/flex.rs | 1 + native/src/layout/node.rs | 10 +++++----- native/src/user_interface.rs | 2 +- native/src/widget/button.rs | 4 ++-- native/src/widget/container.rs | 5 ++--- native/src/widget/image.rs | 2 +- native/src/widget/progress_bar.rs | 2 +- native/src/widget/scrollable.rs | 2 +- native/src/widget/slider.rs | 2 +- native/src/widget/space.rs | 2 +- native/src/widget/svg.rs | 2 +- native/src/widget/text.rs | 2 +- native/src/widget/text_input.rs | 9 +++++++-- 13 files changed, 25 insertions(+), 20 deletions(-) diff --git a/native/src/layout/flex.rs b/native/src/layout/flex.rs index bd37b75a69..02037b1450 100644 --- a/native/src/layout/flex.rs +++ b/native/src/layout/flex.rs @@ -174,6 +174,7 @@ where Node::with_children( Size::new(size.width + padding * 2.0, size.height + padding * 2.0), + Size::ZERO, nodes, ) } diff --git a/native/src/layout/node.rs b/native/src/layout/node.rs index fae6e330d4..e988572571 100644 --- a/native/src/layout/node.rs +++ b/native/src/layout/node.rs @@ -12,19 +12,19 @@ impl Node { /// /// [`Node`]: struct.Node.html /// [`Size`]: ../struct.Size.html - pub fn new(size: Size) -> Self { - Self::with_children(size, Vec::new()) + pub fn new(size: Size, bound: Size) -> Self { + Self::with_children(size, bound, Vec::new()) } /// Creates a new [`Node`] with the given [`Size`] and children. /// /// [`Node`]: struct.Node.html /// [`Size`]: ../struct.Size.html - pub fn with_children(size: Size, children: Vec) -> Self { + pub fn with_children(size: Size, bound: Size, children: Vec) -> Self { Node { bounds: Rectangle { - x: 0.0, - y: 0.0, + x: bound.width, + y: bound.height, width: size.width, height: size.height, }, diff --git a/native/src/user_interface.rs b/native/src/user_interface.rs index 08914bed37..b8fe784888 100644 --- a/native/src/user_interface.rs +++ b/native/src/user_interface.rs @@ -307,7 +307,7 @@ impl Cache { pub fn new() -> Cache { Cache { hash: 0, - layout: layout::Node::new(Size::new(0.0, 0.0)), + layout: layout::Node::new(Size::ZERO, Size::ZERO), bounds: Size::ZERO, cursor_position: Point::new(-1.0, -1.0), } diff --git a/native/src/widget/button.rs b/native/src/widget/button.rs index 5b0d3e41ed..73be4d49a1 100644 --- a/native/src/widget/button.rs +++ b/native/src/widget/button.rs @@ -7,7 +7,7 @@ use crate::{ input::{mouse, ButtonState}, layout, Clipboard, Element, Event, Hasher, Layout, Length, Point, - Rectangle, Widget, + Rectangle, Size, Widget, }; use std::hash::Hash; @@ -174,7 +174,7 @@ where let size = limits.resolve(content.size()).pad(padding); - layout::Node::with_children(size, vec![content]) + layout::Node::with_children(size, Size::ZERO, vec![content]) } fn on_event( diff --git a/native/src/widget/container.rs b/native/src/widget/container.rs index 3459a832c1..d2065234bf 100644 --- a/native/src/widget/container.rs +++ b/native/src/widget/container.rs @@ -2,7 +2,7 @@ use std::hash::Hash; use crate::{ - layout, Align, Clipboard, Element, Event, Hasher, Layout, Length, Point, + layout, Align, Clipboard, Size, Element, Event, Hasher, Layout, Length, Point, Rectangle, Widget, }; @@ -77,7 +77,6 @@ where self.max_height = max_height; self } - /// Sets the content alignment for the horizontal axis of the [`Container`]. /// /// [`Container`]: struct.Container.html @@ -149,7 +148,7 @@ where content.align(self.horizontal_alignment, self.vertical_alignment, size); - layout::Node::with_children(size, vec![content]) + layout::Node::with_children(size, Size::ZERO, vec![content]) } fn on_event( diff --git a/native/src/widget/image.rs b/native/src/widget/image.rs index 200401f9e1..6938f5d540 100644 --- a/native/src/widget/image.rs +++ b/native/src/widget/image.rs @@ -88,7 +88,7 @@ where size.height = height as f32 * size.width / width as f32; } - layout::Node::new(size) + layout::Node::new(size, Size::ZERO) } fn draw( diff --git a/native/src/widget/progress_bar.rs b/native/src/widget/progress_bar.rs index 67d1ab836a..d011cc8a4e 100644 --- a/native/src/widget/progress_bar.rs +++ b/native/src/widget/progress_bar.rs @@ -95,7 +95,7 @@ where let size = limits.resolve(Size::ZERO); - layout::Node::new(size) + layout::Node::new(size, Size::ZERO) } fn draw( diff --git a/native/src/widget/scrollable.rs b/native/src/widget/scrollable.rs index e83f25af9b..6408a3d2b9 100644 --- a/native/src/widget/scrollable.rs +++ b/native/src/widget/scrollable.rs @@ -143,7 +143,7 @@ where let content = self.content.layout(renderer, &child_limits); let size = limits.resolve(content.size()); - layout::Node::with_children(size, vec![content]) + layout::Node::with_children(size, Size::ZERO, vec![content]) } fn on_event( diff --git a/native/src/widget/slider.rs b/native/src/widget/slider.rs index 008203fe32..c554fb9e29 100644 --- a/native/src/widget/slider.rs +++ b/native/src/widget/slider.rs @@ -135,7 +135,7 @@ where let size = limits.resolve(Size::ZERO); - layout::Node::new(size) + layout::Node::new(size, Size::ZERO) } fn on_event( diff --git a/native/src/widget/space.rs b/native/src/widget/space.rs index 24c94bf695..899c258ece 100644 --- a/native/src/widget/space.rs +++ b/native/src/widget/space.rs @@ -62,7 +62,7 @@ where ) -> layout::Node { let limits = limits.width(self.width).height(self.height); - layout::Node::new(limits.resolve(Size::ZERO)) + layout::Node::new(limits.resolve(Size::ZERO), Size::ZERO) } fn draw( diff --git a/native/src/widget/svg.rs b/native/src/widget/svg.rs index 063730bb49..60d403efc4 100644 --- a/native/src/widget/svg.rs +++ b/native/src/widget/svg.rs @@ -85,7 +85,7 @@ where size.height = height as f32 * size.width / width as f32; } - layout::Node::new(size) + layout::Node::new(size, Size::ZERO) } fn draw( diff --git a/native/src/widget/text.rs b/native/src/widget/text.rs index e4490fb6d7..ef8a9dbc9b 100644 --- a/native/src/widget/text.rs +++ b/native/src/widget/text.rs @@ -140,7 +140,7 @@ where let size = limits.resolve(Size::new(width, height)); - layout::Node::new(size) + layout::Node::new(size, Size::ZERO) } fn draw( diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs index 041187556c..42aa3d69b8 100644 --- a/native/src/widget/text_input.rs +++ b/native/src/widget/text_input.rs @@ -183,11 +183,16 @@ where .max_width(self.max_width) .height(Length::Units(text_size)); - let mut text = layout::Node::new(limits.resolve(Size::ZERO)); + let mut text = + layout::Node::new(limits.resolve(Size::ZERO), Size::ZERO); text.bounds.x = padding; text.bounds.y = padding; - layout::Node::with_children(text.size().pad(padding), vec![text]) + layout::Node::with_children( + text.size().pad(padding), + Size::ZERO, + vec![text], + ) } fn on_event( From ebe0d4f47ec644d4c91097763de0f9b02e5f9d8a Mon Sep 17 00:00:00 2001 From: daxpedda Date: Fri, 14 Feb 2020 18:03:59 +0100 Subject: [PATCH 26/45] Fix custom widget example. --- examples/custom_widget/src/main.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/examples/custom_widget/src/main.rs b/examples/custom_widget/src/main.rs index 0a570745e1..19615064d1 100644 --- a/examples/custom_widget/src/main.rs +++ b/examples/custom_widget/src/main.rs @@ -39,10 +39,13 @@ mod circle { _renderer: &Renderer, _limits: &layout::Limits, ) -> layout::Node { - layout::Node::new(Size::new( - f32::from(self.radius) * 2.0, - f32::from(self.radius) * 2.0, - )) + layout::Node::new( + Size::new( + f32::from(self.radius) * 2.0, + f32::from(self.radius) * 2.0, + ), + Size::ZERO, + ) } fn hash_layout(&self, state: &mut Hasher) { From 60b40fdc99647d77febaae1d483fc5642c4ba50d Mon Sep 17 00:00:00 2001 From: daxpedda Date: Fri, 14 Feb 2020 18:17:32 +0100 Subject: [PATCH 27/45] Fix examples. --- examples/bezier_tool/src/main.rs | 2 +- examples/geometry/src/main.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/bezier_tool/src/main.rs b/examples/bezier_tool/src/main.rs index 043d265c96..a08ea57c65 100644 --- a/examples/bezier_tool/src/main.rs +++ b/examples/bezier_tool/src/main.rs @@ -84,7 +84,7 @@ mod bezier { .height(Length::Fill) .width(Length::Fill) .resolve(Size::ZERO); - layout::Node::new(size) + layout::Node::new(size, Size::ZERO) } fn draw( diff --git a/examples/geometry/src/main.rs b/examples/geometry/src/main.rs index 9d5fd611a9..2c9042f00b 100644 --- a/examples/geometry/src/main.rs +++ b/examples/geometry/src/main.rs @@ -43,7 +43,7 @@ mod rainbow { ) -> layout::Node { let size = limits.width(Length::Fill).resolve(Size::ZERO); - layout::Node::new(Size::new(size.width, size.width)) + layout::Node::new(Size::new(size.width, size.width), Size::ZERO) } fn hash_layout(&self, _state: &mut Hasher) {} From f4b8bce837513cdd06df3a3ceba86fd9256d3cc5 Mon Sep 17 00:00:00 2001 From: daxpedda Date: Fri, 14 Feb 2020 21:41:35 +0100 Subject: [PATCH 28/45] Revert changing the constructor and implement new method. --- examples/bezier_tool/src/main.rs | 2 +- examples/custom_widget/src/main.rs | 11 ++++------- examples/geometry/src/main.rs | 2 +- native/src/layout/flex.rs | 6 ++---- native/src/layout/node.rs | 22 +++++++++++++++------- native/src/user_interface.rs | 2 +- native/src/widget/button.rs | 8 +++----- native/src/widget/container.rs | 4 ++-- native/src/widget/image.rs | 2 +- native/src/widget/progress_bar.rs | 2 +- native/src/widget/scrollable.rs | 2 +- native/src/widget/slider.rs | 2 +- native/src/widget/space.rs | 2 +- native/src/widget/svg.rs | 2 +- native/src/widget/text.rs | 2 +- native/src/widget/text_input.rs | 14 ++++---------- 16 files changed, 40 insertions(+), 45 deletions(-) diff --git a/examples/bezier_tool/src/main.rs b/examples/bezier_tool/src/main.rs index a08ea57c65..043d265c96 100644 --- a/examples/bezier_tool/src/main.rs +++ b/examples/bezier_tool/src/main.rs @@ -84,7 +84,7 @@ mod bezier { .height(Length::Fill) .width(Length::Fill) .resolve(Size::ZERO); - layout::Node::new(size, Size::ZERO) + layout::Node::new(size) } fn draw( diff --git a/examples/custom_widget/src/main.rs b/examples/custom_widget/src/main.rs index 19615064d1..0a570745e1 100644 --- a/examples/custom_widget/src/main.rs +++ b/examples/custom_widget/src/main.rs @@ -39,13 +39,10 @@ mod circle { _renderer: &Renderer, _limits: &layout::Limits, ) -> layout::Node { - layout::Node::new( - Size::new( - f32::from(self.radius) * 2.0, - f32::from(self.radius) * 2.0, - ), - Size::ZERO, - ) + layout::Node::new(Size::new( + f32::from(self.radius) * 2.0, + f32::from(self.radius) * 2.0, + )) } fn hash_layout(&self, state: &mut Hasher) { diff --git a/examples/geometry/src/main.rs b/examples/geometry/src/main.rs index 2c9042f00b..9d5fd611a9 100644 --- a/examples/geometry/src/main.rs +++ b/examples/geometry/src/main.rs @@ -43,7 +43,7 @@ mod rainbow { ) -> layout::Node { let size = limits.width(Length::Fill).resolve(Size::ZERO); - layout::Node::new(Size::new(size.width, size.width), Size::ZERO) + layout::Node::new(Size::new(size.width, size.width)) } fn hash_layout(&self, _state: &mut Hasher) {} diff --git a/native/src/layout/flex.rs b/native/src/layout/flex.rs index 02037b1450..2f65f1c1d2 100644 --- a/native/src/layout/flex.rs +++ b/native/src/layout/flex.rs @@ -18,7 +18,7 @@ // limitations under the License. use crate::{ layout::{Limits, Node}, - Align, Element, Size, + Align, Element, Point, Size, }; /// The main axis of a flex layout. @@ -152,8 +152,7 @@ where let (x, y) = axis.pack(main, padding); - node.bounds.x = x; - node.bounds.y = y; + node.move_to(Point::new(x, y)); match axis { Axis::Horizontal => { @@ -174,7 +173,6 @@ where Node::with_children( Size::new(size.width + padding * 2.0, size.height + padding * 2.0), - Size::ZERO, nodes, ) } diff --git a/native/src/layout/node.rs b/native/src/layout/node.rs index e988572571..777a57fbee 100644 --- a/native/src/layout/node.rs +++ b/native/src/layout/node.rs @@ -1,9 +1,9 @@ -use crate::{Align, Rectangle, Size}; +use crate::{Align, Point, Rectangle, Size}; /// The bounds of an element and its children. #[derive(Debug, Clone, Default)] pub struct Node { - pub(crate) bounds: Rectangle, + bounds: Rectangle, children: Vec, } @@ -12,19 +12,19 @@ impl Node { /// /// [`Node`]: struct.Node.html /// [`Size`]: ../struct.Size.html - pub fn new(size: Size, bound: Size) -> Self { - Self::with_children(size, bound, Vec::new()) + pub fn new(size: Size) -> Self { + Self::with_children(size, Vec::new()) } /// Creates a new [`Node`] with the given [`Size`] and children. /// /// [`Node`]: struct.Node.html /// [`Size`]: ../struct.Size.html - pub fn with_children(size: Size, bound: Size, children: Vec) -> Self { + pub fn with_children(size: Size, children: Vec) -> Self { Node { bounds: Rectangle { - x: bound.width, - y: bound.height, + x: 0.0, + y: 0.0, width: size.width, height: size.height, }, @@ -84,4 +84,12 @@ impl Node { } } } + + /// Move item to [`Point`]. + /// + /// [`Point`]: ../struct.Point.html + pub fn move_to(&mut self, position: Point) { + self.bounds.x = position.x; + self.bounds.y = position.y; + } } diff --git a/native/src/user_interface.rs b/native/src/user_interface.rs index b8fe784888..08914bed37 100644 --- a/native/src/user_interface.rs +++ b/native/src/user_interface.rs @@ -307,7 +307,7 @@ impl Cache { pub fn new() -> Cache { Cache { hash: 0, - layout: layout::Node::new(Size::ZERO, Size::ZERO), + layout: layout::Node::new(Size::new(0.0, 0.0)), bounds: Size::ZERO, cursor_position: Point::new(-1.0, -1.0), } diff --git a/native/src/widget/button.rs b/native/src/widget/button.rs index 73be4d49a1..f1d4693682 100644 --- a/native/src/widget/button.rs +++ b/native/src/widget/button.rs @@ -7,7 +7,7 @@ use crate::{ input::{mouse, ButtonState}, layout, Clipboard, Element, Event, Hasher, Layout, Length, Point, - Rectangle, Size, Widget, + Rectangle, Widget, }; use std::hash::Hash; @@ -168,13 +168,11 @@ where .pad(padding); let mut content = self.content.layout(renderer, &limits); - - content.bounds.x = padding; - content.bounds.y = padding; + content.move_to(Point::new(padding, padding)); let size = limits.resolve(content.size()).pad(padding); - layout::Node::with_children(size, Size::ZERO, vec![content]) + layout::Node::with_children(size, vec![content]) } fn on_event( diff --git a/native/src/widget/container.rs b/native/src/widget/container.rs index d2065234bf..07bb62c6d4 100644 --- a/native/src/widget/container.rs +++ b/native/src/widget/container.rs @@ -2,7 +2,7 @@ use std::hash::Hash; use crate::{ - layout, Align, Clipboard, Size, Element, Event, Hasher, Layout, Length, Point, + layout, Align, Clipboard, Element, Event, Hasher, Layout, Length, Point, Rectangle, Widget, }; @@ -148,7 +148,7 @@ where content.align(self.horizontal_alignment, self.vertical_alignment, size); - layout::Node::with_children(size, Size::ZERO, vec![content]) + layout::Node::with_children(size, vec![content]) } fn on_event( diff --git a/native/src/widget/image.rs b/native/src/widget/image.rs index 6938f5d540..200401f9e1 100644 --- a/native/src/widget/image.rs +++ b/native/src/widget/image.rs @@ -88,7 +88,7 @@ where size.height = height as f32 * size.width / width as f32; } - layout::Node::new(size, Size::ZERO) + layout::Node::new(size) } fn draw( diff --git a/native/src/widget/progress_bar.rs b/native/src/widget/progress_bar.rs index d011cc8a4e..67d1ab836a 100644 --- a/native/src/widget/progress_bar.rs +++ b/native/src/widget/progress_bar.rs @@ -95,7 +95,7 @@ where let size = limits.resolve(Size::ZERO); - layout::Node::new(size, Size::ZERO) + layout::Node::new(size) } fn draw( diff --git a/native/src/widget/scrollable.rs b/native/src/widget/scrollable.rs index 6408a3d2b9..e83f25af9b 100644 --- a/native/src/widget/scrollable.rs +++ b/native/src/widget/scrollable.rs @@ -143,7 +143,7 @@ where let content = self.content.layout(renderer, &child_limits); let size = limits.resolve(content.size()); - layout::Node::with_children(size, Size::ZERO, vec![content]) + layout::Node::with_children(size, vec![content]) } fn on_event( diff --git a/native/src/widget/slider.rs b/native/src/widget/slider.rs index c554fb9e29..008203fe32 100644 --- a/native/src/widget/slider.rs +++ b/native/src/widget/slider.rs @@ -135,7 +135,7 @@ where let size = limits.resolve(Size::ZERO); - layout::Node::new(size, Size::ZERO) + layout::Node::new(size) } fn on_event( diff --git a/native/src/widget/space.rs b/native/src/widget/space.rs index 899c258ece..24c94bf695 100644 --- a/native/src/widget/space.rs +++ b/native/src/widget/space.rs @@ -62,7 +62,7 @@ where ) -> layout::Node { let limits = limits.width(self.width).height(self.height); - layout::Node::new(limits.resolve(Size::ZERO), Size::ZERO) + layout::Node::new(limits.resolve(Size::ZERO)) } fn draw( diff --git a/native/src/widget/svg.rs b/native/src/widget/svg.rs index 60d403efc4..063730bb49 100644 --- a/native/src/widget/svg.rs +++ b/native/src/widget/svg.rs @@ -85,7 +85,7 @@ where size.height = height as f32 * size.width / width as f32; } - layout::Node::new(size, Size::ZERO) + layout::Node::new(size) } fn draw( diff --git a/native/src/widget/text.rs b/native/src/widget/text.rs index ef8a9dbc9b..e4490fb6d7 100644 --- a/native/src/widget/text.rs +++ b/native/src/widget/text.rs @@ -140,7 +140,7 @@ where let size = limits.resolve(Size::new(width, height)); - layout::Node::new(size, Size::ZERO) + layout::Node::new(size) } fn draw( diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs index 42aa3d69b8..c068b895d6 100644 --- a/native/src/widget/text_input.rs +++ b/native/src/widget/text_input.rs @@ -183,16 +183,10 @@ where .max_width(self.max_width) .height(Length::Units(text_size)); - let mut text = - layout::Node::new(limits.resolve(Size::ZERO), Size::ZERO); - text.bounds.x = padding; - text.bounds.y = padding; - - layout::Node::with_children( - text.size().pad(padding), - Size::ZERO, - vec![text], - ) + let mut text = layout::Node::new(limits.resolve(Size::ZERO)); + text.move_to(Point::new(padding, padding)); + + layout::Node::with_children(text.size().pad(padding), vec![text]) } fn on_event( From ddceb295f43154543b7f90bbd84964ede0901dbd Mon Sep 17 00:00:00 2001 From: daxpedda Date: Fri, 14 Feb 2020 21:43:34 +0100 Subject: [PATCH 29/45] Revert rustfmt change. --- native/src/widget/container.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/native/src/widget/container.rs b/native/src/widget/container.rs index 07bb62c6d4..3459a832c1 100644 --- a/native/src/widget/container.rs +++ b/native/src/widget/container.rs @@ -77,6 +77,7 @@ where self.max_height = max_height; self } + /// Sets the content alignment for the horizontal axis of the [`Container`]. /// /// [`Container`]: struct.Container.html From 8f83c805b15230a2f35002cfff88d9653ee08690 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Fri, 14 Feb 2020 23:23:45 +0100 Subject: [PATCH 30/45] Improve documentation for new `Node` methods --- native/src/layout/node.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/native/src/layout/node.rs b/native/src/layout/node.rs index 777a57fbee..11e93b725e 100644 --- a/native/src/layout/node.rs +++ b/native/src/layout/node.rs @@ -54,10 +54,9 @@ impl Node { &self.children } - /// Aligns item according to [`Align`] and [`Size`]. - /// - /// [`Align`]: ../enum.Align.html - /// [`Size`]: ../struct.Size.html + /// Aligns the [`Node`] in the given space. + /// + /// [`Node`]: struct.Node.html pub fn align( &mut self, horizontal_alignment: Align, @@ -85,9 +84,9 @@ impl Node { } } - /// Move item to [`Point`]. + /// Moves the [`Node`] to the given position. /// - /// [`Point`]: ../struct.Point.html + /// [`Node`]: struct.Node.html pub fn move_to(&mut self, position: Point) { self.bounds.x = position.x; self.bounds.y = position.y; From dadae122533ae0916bebd04d6efab3de145263d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Sat, 15 Feb 2020 10:08:27 +0100 Subject: [PATCH 31/45] Implement MSAA for `triangle` pipeline in `iced_wgpu` --- examples/bezier_tool/src/main.rs | 5 +- examples/clock/src/main.rs | 5 +- examples/solar_system/src/main.rs | 5 +- src/application.rs | 5 + src/settings.rs | 9 ++ wgpu/src/lib.rs | 2 +- wgpu/src/renderer.rs | 9 +- wgpu/src/settings.rs | 22 +++ wgpu/src/shader/blit.frag | 12 ++ wgpu/src/shader/blit.frag.spv | Bin 0 -> 684 bytes wgpu/src/shader/blit.vert | 26 +++ wgpu/src/shader/blit.vert.spv | Bin 0 -> 1384 bytes wgpu/src/shader/image.vert | 24 --- wgpu/src/triangle.rs | 233 +++++++++++++++++++++------ wgpu/src/triangle/msaa.rs | 255 ++++++++++++++++++++++++++++++ 15 files changed, 539 insertions(+), 73 deletions(-) create mode 100644 wgpu/src/shader/blit.frag create mode 100644 wgpu/src/shader/blit.frag.spv create mode 100644 wgpu/src/shader/blit.vert create mode 100644 wgpu/src/shader/blit.vert.spv delete mode 100644 wgpu/src/shader/image.vert create mode 100644 wgpu/src/triangle/msaa.rs diff --git a/examples/bezier_tool/src/main.rs b/examples/bezier_tool/src/main.rs index efdb3924ce..023eb0f7fc 100644 --- a/examples/bezier_tool/src/main.rs +++ b/examples/bezier_tool/src/main.rs @@ -286,7 +286,10 @@ use iced::{ }; pub fn main() { - Example::run(Settings::default()) + Example::run(Settings { + antialiasing: true, + ..Settings::default() + }); } #[derive(Default)] diff --git a/examples/clock/src/main.rs b/examples/clock/src/main.rs index 0a70709fdb..f7fb6f2d01 100644 --- a/examples/clock/src/main.rs +++ b/examples/clock/src/main.rs @@ -4,7 +4,10 @@ use iced::{ }; pub fn main() { - Clock::run(Settings::default()) + Clock::run(Settings { + antialiasing: true, + ..Settings::default() + }) } struct Clock { diff --git a/examples/solar_system/src/main.rs b/examples/solar_system/src/main.rs index d05acf841c..9e7dba2fb3 100644 --- a/examples/solar_system/src/main.rs +++ b/examples/solar_system/src/main.rs @@ -14,7 +14,10 @@ use iced::{ use std::time::Instant; pub fn main() { - SolarSystem::run(Settings::default()) + SolarSystem::run(Settings { + antialiasing: true, + ..Settings::default() + }) } struct SolarSystem { diff --git a/src/application.rs b/src/application.rs index 0a4b6d9e47..1b73101aa5 100644 --- a/src/application.rs +++ b/src/application.rs @@ -178,6 +178,11 @@ pub trait Application: Sized { _settings.into(), iced_wgpu::Settings { default_font: _settings.default_font, + antialiasing: if _settings.antialiasing { + Some(iced_wgpu::settings::MSAA::X4) + } else { + None + }, }, ); diff --git a/src/settings.rs b/src/settings.rs index 77c7e0b943..f70e577fba 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -16,6 +16,15 @@ pub struct Settings { /// If `None` is provided, a default system font will be chosen. // TODO: Add `name` for web compatibility pub default_font: Option<&'static [u8]>, + + /// If set to true, the renderer will try to use antialiasing for some + /// primitives. + /// + /// Enabling it can produce a smoother result in some widgets, like the + /// `Canvas`, at a performance cost. + /// + /// By default, it is disabled. + pub antialiasing: bool, } #[cfg(not(target_arch = "wasm32"))] diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs index d38e2a31ca..90d283537d 100644 --- a/wgpu/src/lib.rs +++ b/wgpu/src/lib.rs @@ -25,6 +25,7 @@ #![forbid(unsafe_code)] #![forbid(rust_2018_idioms)] pub mod defaults; +pub mod settings; pub mod triangle; pub mod widget; pub mod window; @@ -33,7 +34,6 @@ mod image; mod primitive; mod quad; mod renderer; -mod settings; mod target; mod text; mod transformation; diff --git a/wgpu/src/renderer.rs b/wgpu/src/renderer.rs index 25b2e99ae0..29adcfb657 100644 --- a/wgpu/src/renderer.rs +++ b/wgpu/src/renderer.rs @@ -51,7 +51,8 @@ impl Renderer { let text_pipeline = text::Pipeline::new(device, settings.default_font); let quad_pipeline = quad::Pipeline::new(device); let image_pipeline = crate::image::Pipeline::new(device); - let triangle_pipeline = triangle::Pipeline::new(device); + let triangle_pipeline = + triangle::Pipeline::new(device, settings.antialiasing); Self { quad_pipeline, @@ -105,6 +106,8 @@ impl Renderer { &layer, encoder, target.texture, + width, + height, ); } @@ -308,6 +311,8 @@ impl Renderer { layer: &Layer<'_>, encoder: &mut wgpu::CommandEncoder, target: &wgpu::TextureView, + target_width: u32, + target_height: u32, ) { let bounds = layer.bounds * scale_factor; @@ -323,6 +328,8 @@ impl Renderer { device, encoder, target, + target_width, + target_height, translated, &layer.meshes, bounds, diff --git a/wgpu/src/settings.rs b/wgpu/src/settings.rs index dbe818306e..c8a0cadf8e 100644 --- a/wgpu/src/settings.rs +++ b/wgpu/src/settings.rs @@ -7,4 +7,26 @@ pub struct Settings { /// /// If `None` is provided, a default system font will be chosen. pub default_font: Option<&'static [u8]>, + + /// The antialiasing strategy that will be used for triangle primitives. + pub antialiasing: Option, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum MSAA { + X2, + X4, + X8, + X16, +} + +impl MSAA { + pub(crate) fn sample_count(&self) -> u32 { + match self { + MSAA::X2 => 2, + MSAA::X4 => 4, + MSAA::X8 => 8, + MSAA::X16 => 16, + } + } } diff --git a/wgpu/src/shader/blit.frag b/wgpu/src/shader/blit.frag new file mode 100644 index 0000000000..dfed960f92 --- /dev/null +++ b/wgpu/src/shader/blit.frag @@ -0,0 +1,12 @@ +#version 450 + +layout(location = 0) in vec2 v_Uv; + +layout(set = 0, binding = 0) uniform sampler u_Sampler; +layout(set = 1, binding = 0) uniform texture2D u_Texture; + +layout(location = 0) out vec4 o_Color; + +void main() { + o_Color = texture(sampler2D(u_Texture, u_Sampler), v_Uv); +} diff --git a/wgpu/src/shader/blit.frag.spv b/wgpu/src/shader/blit.frag.spv new file mode 100644 index 0000000000000000000000000000000000000000..2c5638b5dd682be63a5eb64d225284d399e85418 GIT binary patch literal 684 zcmYk3Pfx-?5XA>d0R{OZ3WA9Ny%P^=OpIJOaN&mFu_2)c60oKM@$>nqyqNfYOPh4a zF#F!Vo!Phb@^>Y(oR#(K+A*4z7h_hjqN|hXY&!hft%eVe_b6)SIU$<5m8_sjZkOv1 zHcpgnWkWi64baL{SF;}-^2KKLV9rFrINC(9_I;}g?}NASd$*56t>GGun=QAWn=p1< z!Ob}Y^MjyGl0G*81(-g!O75ECfz||=p6nAT&hFzQ?bnh6JPe#0bL+O&MR9#)NB!^$ z#$R9mWmR4a{w2Y9h3m@osVe%MG8-wTM18{dRMn}|)z#Irt>)HT2#s)pzek~3OQ8u9!hGnKcCfRz)1MRv{l`xps8|TC zQmAH;eXch6Hq3JUlVAmGf);;G>>tD(RI@r`wd4JdKQZLH<-V_@{ZHv(ksiIz57Hy~ zPPjxqGaP;%rL{J}qTNjoD>)$*xa{6-4bwDP;s$1s2b^}h0{t8|fn}Ahz@bG19E+O_M)yhAwW8X*a0`Vzq0$kg5 z?Ag{_BR-9Fubg@LGr-m3uI3hioQRr5{1A>`Ma@|v^YW2fs^eMiT+ONN@g;fwGVly* zxJO@|zIQl}O+cfxZMe#p_s+(?QOsY&w{L4Zqsw(H=M1l6y{Ge!Gx5FM?@X>?&C9Lh zCD^#foUrZ&);TA@K8!bkoOSJQ3%KIku9wmA?>M6y#Bw5XH|uzIZnxl!D_u3W+r;iy z@2vFI>D!+_e0#C?r@W=#OdGi5|H5>z_U_F5y*i^9`@I{#sPD3h@0?$ajbO*Ki2Xb?-FZm, constants: wgpu::BindGroup, - constants_buffer: wgpu::Buffer, + uniforms_buffer: Buffer, + vertex_buffer: Buffer, + index_buffer: Buffer, +} + +#[derive(Debug)] +struct Buffer { + raw: wgpu::Buffer, + size: usize, + usage: wgpu::BufferUsage, + _type: std::marker::PhantomData, +} + +impl Buffer { + pub fn new( + device: &wgpu::Device, + size: usize, + usage: wgpu::BufferUsage, + ) -> Self { + let raw = device.create_buffer(&wgpu::BufferDescriptor { + size: (std::mem::size_of::() * size) as u64, + usage, + }); + + Buffer { + raw, + size, + usage, + _type: std::marker::PhantomData, + } + } + + pub fn ensure_capacity(&mut self, device: &wgpu::Device, size: usize) { + if self.size < size { + self.raw = device.create_buffer(&wgpu::BufferDescriptor { + size: (std::mem::size_of::() * size) as u64, + usage: self.usage, + }); + + self.size = size; + } + } } impl Pipeline { - pub fn new(device: &mut wgpu::Device) -> Pipeline { + pub fn new( + device: &mut wgpu::Device, + antialiasing: Option, + ) -> Pipeline { let constant_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { bindings: &[wgpu::BindGroupLayoutBinding { binding: 0, visibility: wgpu::ShaderStage::VERTEX, - ty: wgpu::BindingType::UniformBuffer { dynamic: false }, + ty: wgpu::BindingType::UniformBuffer { dynamic: true }, }], }); - let constants_buffer = device - .create_buffer_mapped( - 1, - wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST, - ) - .fill_from_slice(&[Uniforms::default()]); + let constants_buffer = Buffer::new( + device, + UNIFORM_BUFFER_SIZE, + wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST, + ); let constant_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { @@ -34,7 +84,7 @@ impl Pipeline { bindings: &[wgpu::Binding { binding: 0, resource: wgpu::BindingResource::Buffer { - buffer: &constants_buffer, + buffer: &constants_buffer.raw, range: 0..std::mem::size_of::() as u64, }, }], @@ -110,15 +160,28 @@ impl Pipeline { }, ], }], - sample_count: 1, + sample_count: antialiasing + .map(|a| a.sample_count()) + .unwrap_or(1), sample_mask: !0, alpha_to_coverage_enabled: false, }); Pipeline { pipeline, + blit: antialiasing.map(|a| msaa::Blit::new(device, a)), constants: constant_bind_group, - constants_buffer, + uniforms_buffer: constants_buffer, + vertex_buffer: Buffer::new( + device, + VERTEX_BUFFER_SIZE, + wgpu::BufferUsage::VERTEX | wgpu::BufferUsage::COPY_DST, + ), + index_buffer: Buffer::new( + device, + INDEX_BUFFER_SIZE, + wgpu::BufferUsage::INDEX | wgpu::BufferUsage::COPY_DST, + ), } } @@ -127,50 +190,116 @@ impl Pipeline { device: &mut wgpu::Device, encoder: &mut wgpu::CommandEncoder, target: &wgpu::TextureView, + target_width: u32, + target_height: u32, transformation: Transformation, meshes: &Vec<(Point, Arc)>, bounds: Rectangle, ) { + // This looks a bit crazy, but we are just counting how many vertices + // and indices we will need to handle. + // TODO: Improve readability + let (total_vertices, total_indices) = meshes + .iter() + .map(|(_, mesh)| (mesh.vertices.len(), mesh.indices.len())) + .fold((0, 0), |(total_v, total_i), (v, i)| { + (total_v + v, total_i + i) + }); + + // Then we ensure the current buffers are big enough, resizing if + // necessary + self.uniforms_buffer.ensure_capacity(device, meshes.len()); + self.vertex_buffer.ensure_capacity(device, total_vertices); + self.index_buffer.ensure_capacity(device, total_indices); + + let mut uniforms: Vec = Vec::with_capacity(meshes.len()); + let mut offsets: Vec<( + wgpu::BufferAddress, + wgpu::BufferAddress, + usize, + )> = Vec::with_capacity(meshes.len()); + let mut last_vertex = 0; + let mut last_index = 0; + + // We upload everything upfront for (origin, mesh) in meshes { - let uniforms = Uniforms { + let transform = Uniforms { transform: (transformation * Transformation::translate(origin.x, origin.y)) .into(), }; - let constants_buffer = device - .create_buffer_mapped(1, wgpu::BufferUsage::COPY_SRC) - .fill_from_slice(&[uniforms]); - - encoder.copy_buffer_to_buffer( - &constants_buffer, - 0, - &self.constants_buffer, - 0, - std::mem::size_of::() as u64, - ); - - let vertices_buffer = device + let vertex_buffer = device .create_buffer_mapped( mesh.vertices.len(), - wgpu::BufferUsage::VERTEX, + wgpu::BufferUsage::COPY_SRC, ) .fill_from_slice(&mesh.vertices); - let indices_buffer = device + let index_buffer = device .create_buffer_mapped( mesh.indices.len(), - wgpu::BufferUsage::INDEX, + wgpu::BufferUsage::COPY_SRC, ) .fill_from_slice(&mesh.indices); + encoder.copy_buffer_to_buffer( + &vertex_buffer, + 0, + &self.vertex_buffer.raw, + last_vertex as u64, + (std::mem::size_of::() * mesh.vertices.len()) as u64, + ); + + encoder.copy_buffer_to_buffer( + &index_buffer, + 0, + &self.index_buffer.raw, + last_index as u64, + (std::mem::size_of::() * mesh.indices.len()) as u64, + ); + + uniforms.push(transform); + offsets.push(( + last_vertex as u64, + last_index as u64, + mesh.indices.len(), + )); + + last_vertex += mesh.vertices.len(); + last_index += mesh.indices.len(); + } + + let uniforms_buffer = device + .create_buffer_mapped(uniforms.len(), wgpu::BufferUsage::COPY_SRC) + .fill_from_slice(&uniforms); + + encoder.copy_buffer_to_buffer( + &uniforms_buffer, + 0, + &self.uniforms_buffer.raw, + 0, + (std::mem::size_of::() * uniforms.len()) as u64, + ); + + { + let (attachment, resolve_target, load_op) = + if let Some(blit) = &mut self.blit { + let (attachment, resolve_target) = + blit.targets(device, target_width, target_height); + + (attachment, Some(resolve_target), wgpu::LoadOp::Clear) + } else { + (target, None, wgpu::LoadOp::Load) + }; + let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { color_attachments: &[ wgpu::RenderPassColorAttachmentDescriptor { - attachment: target, - resolve_target: None, - load_op: wgpu::LoadOp::Load, + attachment, + resolve_target, + load_op, store_op: wgpu::StoreOp::Store, clear_color: wgpu::Color { r: 0.0, @@ -183,18 +312,34 @@ impl Pipeline { depth_stencil_attachment: None, }); - render_pass.set_pipeline(&self.pipeline); - render_pass.set_bind_group(0, &self.constants, &[]); - render_pass.set_index_buffer(&indices_buffer, 0); - render_pass.set_vertex_buffers(0, &[(&vertices_buffer, 0)]); - render_pass.set_scissor_rect( - bounds.x, - bounds.y, - bounds.width, - bounds.height, - ); + for (i, (vertex_offset, index_offset, indices)) in + offsets.drain(..).enumerate() + { + render_pass.set_pipeline(&self.pipeline); + render_pass.set_bind_group( + 0, + &self.constants, + &[(std::mem::size_of::() * i) as u64], + ); + render_pass + .set_index_buffer(&self.index_buffer.raw, index_offset); + render_pass.set_vertex_buffers( + 0, + &[(&self.vertex_buffer.raw, vertex_offset)], + ); + render_pass.set_scissor_rect( + bounds.x, + bounds.y, + bounds.width, + bounds.height, + ); + + render_pass.draw_indexed(0..indices as u32, 0, 0..1); + } + } - render_pass.draw_indexed(0..mesh.indices.len() as u32, 0, 0..1); + if let Some(blit) = &mut self.blit { + blit.draw(encoder, target); } } } diff --git a/wgpu/src/triangle/msaa.rs b/wgpu/src/triangle/msaa.rs new file mode 100644 index 0000000000..93fbe49b26 --- /dev/null +++ b/wgpu/src/triangle/msaa.rs @@ -0,0 +1,255 @@ +use crate::settings; + +#[derive(Debug)] +pub struct Blit { + pipeline: wgpu::RenderPipeline, + constants: wgpu::BindGroup, + texture_layout: wgpu::BindGroupLayout, + sample_count: u32, + targets: Option, +} + +impl Blit { + pub fn new(device: &wgpu::Device, antialiasing: settings::MSAA) -> Blit { + let sampler = device.create_sampler(&wgpu::SamplerDescriptor { + address_mode_u: wgpu::AddressMode::ClampToEdge, + address_mode_v: wgpu::AddressMode::ClampToEdge, + address_mode_w: wgpu::AddressMode::ClampToEdge, + mag_filter: wgpu::FilterMode::Linear, + min_filter: wgpu::FilterMode::Linear, + mipmap_filter: wgpu::FilterMode::Linear, + lod_min_clamp: -100.0, + lod_max_clamp: 100.0, + compare_function: wgpu::CompareFunction::Always, + }); + + let constant_layout = + device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + bindings: &[wgpu::BindGroupLayoutBinding { + binding: 0, + visibility: wgpu::ShaderStage::FRAGMENT, + ty: wgpu::BindingType::Sampler, + }], + }); + + let constant_bind_group = + device.create_bind_group(&wgpu::BindGroupDescriptor { + layout: &constant_layout, + bindings: &[wgpu::Binding { + binding: 0, + resource: wgpu::BindingResource::Sampler(&sampler), + }], + }); + + let texture_layout = + device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + bindings: &[wgpu::BindGroupLayoutBinding { + binding: 0, + visibility: wgpu::ShaderStage::FRAGMENT, + ty: wgpu::BindingType::SampledTexture { + multisampled: false, + dimension: wgpu::TextureViewDimension::D2, + }, + }], + }); + + let layout = + device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + bind_group_layouts: &[&constant_layout, &texture_layout], + }); + + let vs = include_bytes!("../shader/blit.vert.spv"); + let vs_module = device.create_shader_module( + &wgpu::read_spirv(std::io::Cursor::new(&vs[..])) + .expect("Read blit vertex shader as SPIR-V"), + ); + + let fs = include_bytes!("../shader/blit.frag.spv"); + let fs_module = device.create_shader_module( + &wgpu::read_spirv(std::io::Cursor::new(&fs[..])) + .expect("Read blit fragment shader as SPIR-V"), + ); + + let pipeline = + device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + layout: &layout, + vertex_stage: wgpu::ProgrammableStageDescriptor { + module: &vs_module, + entry_point: "main", + }, + fragment_stage: Some(wgpu::ProgrammableStageDescriptor { + module: &fs_module, + entry_point: "main", + }), + rasterization_state: Some(wgpu::RasterizationStateDescriptor { + front_face: wgpu::FrontFace::Cw, + cull_mode: wgpu::CullMode::None, + depth_bias: 0, + depth_bias_slope_scale: 0.0, + depth_bias_clamp: 0.0, + }), + primitive_topology: wgpu::PrimitiveTopology::TriangleList, + color_states: &[wgpu::ColorStateDescriptor { + format: wgpu::TextureFormat::Bgra8UnormSrgb, + color_blend: wgpu::BlendDescriptor { + src_factor: wgpu::BlendFactor::SrcAlpha, + dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha, + operation: wgpu::BlendOperation::Add, + }, + alpha_blend: wgpu::BlendDescriptor { + src_factor: wgpu::BlendFactor::One, + dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha, + operation: wgpu::BlendOperation::Add, + }, + write_mask: wgpu::ColorWrite::ALL, + }], + depth_stencil_state: None, + index_format: wgpu::IndexFormat::Uint16, + vertex_buffers: &[], + sample_count: 1, + sample_mask: !0, + alpha_to_coverage_enabled: false, + }); + + Blit { + pipeline, + constants: constant_bind_group, + texture_layout: texture_layout, + sample_count: antialiasing.sample_count(), + targets: None, + } + } + + pub fn targets( + &mut self, + device: &wgpu::Device, + width: u32, + height: u32, + ) -> (&wgpu::TextureView, &wgpu::TextureView) { + match &mut self.targets { + None => { + self.targets = Some(Targets::new( + &device, + &self.texture_layout, + self.sample_count, + width, + height, + )); + } + Some(targets) => { + if targets.width != width || targets.height != height { + self.targets = Some(Targets::new( + &device, + &self.texture_layout, + self.sample_count, + width, + height, + )); + } + } + } + + let targets = self.targets.as_ref().unwrap(); + + (&targets.attachment, &targets.resolve) + } + + pub fn draw( + &self, + encoder: &mut wgpu::CommandEncoder, + target: &wgpu::TextureView, + ) { + let mut render_pass = + encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + color_attachments: &[ + wgpu::RenderPassColorAttachmentDescriptor { + attachment: target, + resolve_target: None, + load_op: wgpu::LoadOp::Load, + store_op: wgpu::StoreOp::Store, + clear_color: wgpu::Color { + r: 0.0, + g: 0.0, + b: 0.0, + a: 0.0, + }, + }, + ], + depth_stencil_attachment: None, + }); + + render_pass.set_pipeline(&self.pipeline); + render_pass.set_bind_group(0, &self.constants, &[]); + render_pass.set_bind_group( + 1, + &self.targets.as_ref().unwrap().bind_group, + &[], + ); + render_pass.draw(0..6, 0..1); + } +} + +#[derive(Debug)] +struct Targets { + attachment: wgpu::TextureView, + resolve: wgpu::TextureView, + bind_group: wgpu::BindGroup, + width: u32, + height: u32, +} + +impl Targets { + pub fn new( + device: &wgpu::Device, + texture_layout: &wgpu::BindGroupLayout, + sample_count: u32, + width: u32, + height: u32, + ) -> Targets { + let extent = wgpu::Extent3d { + width, + height, + depth: 1, + }; + + let attachment = device.create_texture(&wgpu::TextureDescriptor { + size: extent, + array_layer_count: 1, + mip_level_count: 1, + sample_count, + dimension: wgpu::TextureDimension::D2, + format: wgpu::TextureFormat::Bgra8UnormSrgb, + usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT, + }); + + let resolve = device.create_texture(&wgpu::TextureDescriptor { + size: extent, + array_layer_count: 1, + mip_level_count: 1, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + format: wgpu::TextureFormat::Bgra8UnormSrgb, + usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT + | wgpu::TextureUsage::SAMPLED, + }); + + let attachment = attachment.create_default_view(); + let resolve = resolve.create_default_view(); + + let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { + layout: texture_layout, + bindings: &[wgpu::Binding { + binding: 0, + resource: wgpu::BindingResource::TextureView(&resolve), + }], + }); + + Targets { + attachment, + resolve, + bind_group, + width, + height, + } + } +} From fe61d2fd676beea9b0b6b30471fe595f4f88496d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Sat, 15 Feb 2020 10:45:45 +0100 Subject: [PATCH 32/45] Request high performance adapter if MSAA is enabled --- wgpu/src/window/backend.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/wgpu/src/window/backend.rs b/wgpu/src/window/backend.rs index 6f8a0bb0e5..4c9f289b46 100644 --- a/wgpu/src/window/backend.rs +++ b/wgpu/src/window/backend.rs @@ -18,7 +18,11 @@ impl iced_native::window::Backend for Backend { fn new(settings: Self::Settings) -> (Backend, Renderer) { let adapter = wgpu::Adapter::request(&wgpu::RequestAdapterOptions { - power_preference: wgpu::PowerPreference::Default, + power_preference: if settings.antialiasing.is_none() { + wgpu::PowerPreference::Default + } else { + wgpu::PowerPreference::HighPerformance + }, backends: wgpu::BackendBit::all(), }) .expect("Request adapter"); From 570f769744aabce2d9d9618feadb47e4b92f50ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Sat, 15 Feb 2020 10:50:07 +0100 Subject: [PATCH 33/45] Rename `Settings::antialiasing` to `use_antialiasing` --- examples/bezier_tool/src/main.rs | 2 +- examples/clock/src/main.rs | 2 +- examples/solar_system/src/main.rs | 2 +- src/application.rs | 2 +- src/settings.rs | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/bezier_tool/src/main.rs b/examples/bezier_tool/src/main.rs index 023eb0f7fc..01f8f847ea 100644 --- a/examples/bezier_tool/src/main.rs +++ b/examples/bezier_tool/src/main.rs @@ -287,7 +287,7 @@ use iced::{ pub fn main() { Example::run(Settings { - antialiasing: true, + use_antialiasing: true, ..Settings::default() }); } diff --git a/examples/clock/src/main.rs b/examples/clock/src/main.rs index f7fb6f2d01..d0995e0cb8 100644 --- a/examples/clock/src/main.rs +++ b/examples/clock/src/main.rs @@ -5,7 +5,7 @@ use iced::{ pub fn main() { Clock::run(Settings { - antialiasing: true, + use_antialiasing: true, ..Settings::default() }) } diff --git a/examples/solar_system/src/main.rs b/examples/solar_system/src/main.rs index 9e7dba2fb3..3cabedbbeb 100644 --- a/examples/solar_system/src/main.rs +++ b/examples/solar_system/src/main.rs @@ -15,7 +15,7 @@ use std::time::Instant; pub fn main() { SolarSystem::run(Settings { - antialiasing: true, + use_antialiasing: true, ..Settings::default() }) } diff --git a/src/application.rs b/src/application.rs index 1b73101aa5..8a88b55a0a 100644 --- a/src/application.rs +++ b/src/application.rs @@ -178,7 +178,7 @@ pub trait Application: Sized { _settings.into(), iced_wgpu::Settings { default_font: _settings.default_font, - antialiasing: if _settings.antialiasing { + antialiasing: if _settings.use_antialiasing { Some(iced_wgpu::settings::MSAA::X4) } else { None diff --git a/src/settings.rs b/src/settings.rs index f70e577fba..757dc72f69 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -24,7 +24,7 @@ pub struct Settings { /// `Canvas`, at a performance cost. /// /// By default, it is disabled. - pub antialiasing: bool, + pub use_antialiasing: bool, } #[cfg(not(target_arch = "wasm32"))] From 75d8de93ae48277bdbcb0129241530632d13e850 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Sun, 16 Feb 2020 11:25:10 +0100 Subject: [PATCH 34/45] Wrap application initialization with `Runtime::enter` --- winit/src/application.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/winit/src/application.rs b/winit/src/application.rs index 35a36434c1..326dca25ba 100644 --- a/winit/src/application.rs +++ b/winit/src/application.rs @@ -123,10 +123,10 @@ pub trait Application: Sized { Runtime::new(executor, Proxy::new(event_loop.create_proxy())) }; - let (mut application, init_command) = Self::new(); + let (mut application, init_command) = runtime.enter(|| Self::new()); runtime.spawn(init_command); - let subscription = application.subscription(); + let subscription = runtime.enter(|| application.subscription()); runtime.track(subscription); let mut title = application.title(); From 09cf0b7af306ba92d2ba930bd871ee9733f8a4b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Sun, 16 Feb 2020 11:31:37 +0100 Subject: [PATCH 35/45] Enter executor context only on `Recipe` creation --- futures/src/runtime.rs | 13 ++++++++++--- winit/src/application.rs | 5 ++--- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/futures/src/runtime.rs b/futures/src/runtime.rs index ede529dca7..d204670bec 100644 --- a/futures/src/runtime.rs +++ b/futures/src/runtime.rs @@ -95,11 +95,18 @@ where &mut self, subscription: Subscription, ) { - let futures = - self.subscriptions.update(subscription, self.sender.clone()); + let Runtime { + executor, + subscriptions, + sender, + .. + } = self; + + let futures = executor + .enter(|| subscriptions.update(subscription, sender.clone())); for future in futures { - self.executor.spawn(future); + executor.spawn(future); } } diff --git a/winit/src/application.rs b/winit/src/application.rs index 326dca25ba..824781072b 100644 --- a/winit/src/application.rs +++ b/winit/src/application.rs @@ -126,7 +126,7 @@ pub trait Application: Sized { let (mut application, init_command) = runtime.enter(|| Self::new()); runtime.spawn(init_command); - let subscription = runtime.enter(|| application.subscription()); + let subscription = application.subscription(); runtime.track(subscription); let mut title = application.title(); @@ -255,8 +255,7 @@ pub trait Application: Sized { debug.update_finished(); } - let subscription = - runtime.enter(|| application.subscription()); + let subscription = application.subscription(); runtime.track(subscription); // Update window title From 5345ac785b969301305f6628152671a816e85ed8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Sun, 16 Feb 2020 11:40:19 +0100 Subject: [PATCH 36/45] Fix missing `enter` in `iced::executor::Default` --- src/executor.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/executor.rs b/src/executor.rs index e31bd93dab..b4be526440 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -38,6 +38,10 @@ mod platform { ) { let _ = self.0.spawn(future); } + + fn enter(&self, f: impl FnOnce() -> R) -> R { + self.0.enter(f) + } } } From 668f627532dacd5441f6990a39c327db1da40083 Mon Sep 17 00:00:00 2001 From: Clark Moody Date: Mon, 17 Feb 2020 17:40:01 -0600 Subject: [PATCH 37/45] Add size, spacing, and text_size properties to Checkbox --- native/src/widget/checkbox.rs | 45 +++++++++++++++++++++++++++++------ 1 file changed, 38 insertions(+), 7 deletions(-) diff --git a/native/src/widget/checkbox.rs b/native/src/widget/checkbox.rs index 951659977e..669437bc83 100644 --- a/native/src/widget/checkbox.rs +++ b/native/src/widget/checkbox.rs @@ -30,7 +30,10 @@ pub struct Checkbox { is_checked: bool, on_toggle: Box Message>, label: String, + size: u16, width: Length, + spacing: u16, + text_size: u16, style: Renderer::Style, } @@ -53,11 +56,22 @@ impl Checkbox { is_checked, on_toggle: Box::new(f), label: String::from(label), + size: 20, width: Length::Shrink, + spacing: 15, + text_size: 20, style: Renderer::Style::default(), } } + /// Sets the size of the [`Checkbox`]. + /// + /// [`Checkbox`]: struct.Checkbox.html + pub fn size(mut self, size: u16) -> Self { + self.size = size; + self + } + /// Sets the width of the [`Checkbox`]. /// /// [`Checkbox`]: struct.Checkbox.html @@ -66,6 +80,22 @@ impl Checkbox { self } + /// Sets the spacing between the [`Checkbox`] and the text. + /// + /// [`Checkbox`]: struct.Checkbox.html + pub fn spacing(mut self, spacing: u16) -> Self { + self.spacing = spacing; + self + } + + /// Sets the text size of the [`Checkbox`]. + /// + /// [`Checkbox`]: struct.Checkbox.html + pub fn text_size(mut self, text_size: u16) -> Self { + self.text_size = text_size; + self + } + /// Sets the style of the [`Checkbox`]. /// /// [`Checkbox`]: struct.Checkbox.html @@ -93,18 +123,19 @@ where renderer: &Renderer, limits: &layout::Limits, ) -> layout::Node { - let size = self::Renderer::default_size(renderer); - Row::<(), Renderer>::new() .width(self.width) - .spacing(15) + .spacing(self.spacing) .align_items(Align::Center) .push( Row::new() - .width(Length::Units(size as u16)) - .height(Length::Units(size as u16)), + .width(Length::Units(self.size)) + .height(Length::Units(self.size)), ) - .push(Text::new(&self.label).width(self.width)) + .push( + Text::new(&self.label) + .width(self.width) + .size(self.text_size)) .layout(renderer, limits) } @@ -151,7 +182,7 @@ where defaults, label_layout.bounds(), &self.label, - text::Renderer::default_size(renderer), + self.text_size, Font::Default, None, HorizontalAlignment::Left, From 692216042307c0ff0d792d1bba6928e499a65d1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Tue, 18 Feb 2020 02:28:15 +0100 Subject: [PATCH 38/45] Pull `Checkbox` default constants from its `Renderer` --- native/src/renderer/null.rs | 9 +++------ native/src/widget/checkbox.rs | 26 +++++++++++++++++--------- native/src/widget/radio.rs | 2 +- native/src/widget/text.rs | 8 ++++---- wgpu/src/renderer/widget/checkbox.rs | 7 ++----- wgpu/src/renderer/widget/text.rs | 7 +------ 6 files changed, 28 insertions(+), 31 deletions(-) diff --git a/native/src/renderer/null.rs b/native/src/renderer/null.rs index 2e0b6f3286..0fcce5ada6 100644 --- a/native/src/renderer/null.rs +++ b/native/src/renderer/null.rs @@ -47,9 +47,7 @@ impl row::Renderer for Null { } impl text::Renderer for Null { - fn default_size(&self) -> u16 { - 20 - } + const DEFAULT_SIZE: u16 = 20; fn measure( &self, @@ -179,9 +177,8 @@ impl radio::Renderer for Null { impl checkbox::Renderer for Null { type Style = (); - fn default_size(&self) -> u32 { - 20 - } + const DEFAULT_SIZE: u16 = 20; + const DEFAULT_SPACING: u16 = 15; fn draw( &mut self, diff --git a/native/src/widget/checkbox.rs b/native/src/widget/checkbox.rs index 669437bc83..b36d10a491 100644 --- a/native/src/widget/checkbox.rs +++ b/native/src/widget/checkbox.rs @@ -26,18 +26,20 @@ use crate::{ /// /// ![Checkbox drawn by `iced_wgpu`](https://github.com/hecrj/iced/blob/7760618fb112074bc40b148944521f312152012a/docs/images/checkbox.png?raw=true) #[allow(missing_debug_implementations)] -pub struct Checkbox { +pub struct Checkbox { is_checked: bool, on_toggle: Box Message>, label: String, - size: u16, width: Length, + size: u16, spacing: u16, text_size: u16, style: Renderer::Style, } -impl Checkbox { +impl + Checkbox +{ /// Creates a new [`Checkbox`]. /// /// It expects: @@ -56,10 +58,10 @@ impl Checkbox { is_checked, on_toggle: Box::new(f), label: String::from(label), - size: 20, width: Length::Shrink, - spacing: 15, - text_size: 20, + size: ::DEFAULT_SIZE, + spacing: Renderer::DEFAULT_SPACING, + text_size: ::DEFAULT_SIZE, style: Renderer::Style::default(), } } @@ -135,7 +137,8 @@ where .push( Text::new(&self.label) .width(self.width) - .size(self.text_size)) + .size(self.text_size), + ) .layout(renderer, limits) } @@ -217,10 +220,15 @@ pub trait Renderer: crate::Renderer { /// The style supported by this renderer. type Style: Default; - /// Returns the default size of a [`Checkbox`]. + /// The default size of a [`Checkbox`]. + /// + /// [`Checkbox`]: struct.Checkbox.html + const DEFAULT_SIZE: u16; + + /// The default spacing of a [`Checkbox`]. /// /// [`Checkbox`]: struct.Checkbox.html - fn default_size(&self) -> u32; + const DEFAULT_SPACING: u16; /// Draws a [`Checkbox`]. /// diff --git a/native/src/widget/radio.rs b/native/src/widget/radio.rs index 99743ec3c0..cdc4862cd1 100644 --- a/native/src/widget/radio.rs +++ b/native/src/widget/radio.rs @@ -149,7 +149,7 @@ where defaults, label_layout.bounds(), &self.label, - text::Renderer::default_size(renderer), + ::DEFAULT_SIZE, Font::Default, None, HorizontalAlignment::Left, diff --git a/native/src/widget/text.rs b/native/src/widget/text.rs index e4490fb6d7..7d8cad6ebe 100644 --- a/native/src/widget/text.rs +++ b/native/src/widget/text.rs @@ -131,7 +131,7 @@ where ) -> layout::Node { let limits = limits.width(self.width).height(self.height); - let size = self.size.unwrap_or(renderer.default_size()); + let size = self.size.unwrap_or(Renderer::DEFAULT_SIZE); let bounds = limits.max(); @@ -154,7 +154,7 @@ where defaults, layout.bounds(), &self.content, - self.size.unwrap_or(renderer.default_size()), + self.size.unwrap_or(Renderer::DEFAULT_SIZE), self.font, self.color, self.horizontal_alignment, @@ -179,10 +179,10 @@ where /// [renderer]: ../../renderer/index.html /// [`UserInterface`]: ../../struct.UserInterface.html pub trait Renderer: crate::Renderer { - /// Returns the default size of the [`Text`]. + /// The default size of [`Text`]. /// /// [`Text`]: struct.Text.html - fn default_size(&self) -> u16; + const DEFAULT_SIZE: u16; /// Measures the [`Text`] in the given bounds and returns the minimum /// boundaries that can fit the contents. diff --git a/wgpu/src/renderer/widget/checkbox.rs b/wgpu/src/renderer/widget/checkbox.rs index 17121eea51..1a0585d395 100644 --- a/wgpu/src/renderer/widget/checkbox.rs +++ b/wgpu/src/renderer/widget/checkbox.rs @@ -3,14 +3,11 @@ use iced_native::{ checkbox, HorizontalAlignment, MouseCursor, Rectangle, VerticalAlignment, }; -const SIZE: f32 = 28.0; - impl checkbox::Renderer for Renderer { type Style = Box; - fn default_size(&self) -> u32 { - SIZE as u32 - } + const DEFAULT_SIZE: u16 = 20; + const DEFAULT_SPACING: u16 = 15; fn draw( &mut self, diff --git a/wgpu/src/renderer/widget/text.rs b/wgpu/src/renderer/widget/text.rs index d61c5523d2..33e549cd40 100644 --- a/wgpu/src/renderer/widget/text.rs +++ b/wgpu/src/renderer/widget/text.rs @@ -6,13 +6,8 @@ use iced_native::{ use std::f32; -// TODO: Obtain from renderer configuration -const DEFAULT_TEXT_SIZE: f32 = 20.0; - impl text::Renderer for Renderer { - fn default_size(&self) -> u16 { - DEFAULT_TEXT_SIZE as u16 - } + const DEFAULT_SIZE: u16 = 20; fn measure( &self, From 9c067562fa765cfc49d09cd9b12fbba96d5619fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Tue, 18 Feb 2020 08:48:54 +0100 Subject: [PATCH 39/45] Write documentation for new `canvas` module --- examples/clock/src/main.rs | 6 +- examples/solar_system/src/main.rs | 6 +- src/application.rs | 2 +- wgpu/src/lib.rs | 2 +- wgpu/src/settings.rs | 33 ++-- wgpu/src/triangle.rs | 2 +- wgpu/src/triangle/msaa.rs | 5 +- wgpu/src/widget/canvas.rs | 35 +++- wgpu/src/widget/canvas/drawable.rs | 12 ++ wgpu/src/widget/canvas/fill.rs | 2 + wgpu/src/widget/canvas/frame.rs | 51 ++++- wgpu/src/widget/canvas/layer.rs | 23 ++- .../canvas/layer/{cached.rs => cache.rs} | 45 +++-- wgpu/src/widget/canvas/path.rs | 168 ++--------------- wgpu/src/widget/canvas/path/arc.rs | 44 +++++ wgpu/src/widget/canvas/path/builder.rs | 177 ++++++++++++++++++ wgpu/src/widget/canvas/stroke.rs | 17 ++ 17 files changed, 434 insertions(+), 196 deletions(-) create mode 100644 wgpu/src/widget/canvas/drawable.rs rename wgpu/src/widget/canvas/layer/{cached.rs => cache.rs} (50%) create mode 100644 wgpu/src/widget/canvas/path/arc.rs create mode 100644 wgpu/src/widget/canvas/path/builder.rs diff --git a/examples/clock/src/main.rs b/examples/clock/src/main.rs index d0995e0cb8..559f01922f 100644 --- a/examples/clock/src/main.rs +++ b/examples/clock/src/main.rs @@ -12,7 +12,7 @@ pub fn main() { struct Clock { now: LocalTime, - clock: canvas::layer::Cached, + clock: canvas::layer::Cache, } #[derive(Debug, Clone, Copy)] @@ -28,7 +28,7 @@ impl Application for Clock { ( Clock { now: chrono::Local::now().into(), - clock: canvas::layer::Cached::new(), + clock: canvas::layer::Cache::new(), }, Command::none(), ) @@ -91,7 +91,7 @@ impl From> for LocalTime { } } -impl canvas::layer::Drawable for LocalTime { +impl canvas::Drawable for LocalTime { fn draw(&self, frame: &mut canvas::Frame) { let center = frame.center(); let radius = frame.width().min(frame.height()) / 2.0; diff --git a/examples/solar_system/src/main.rs b/examples/solar_system/src/main.rs index 3cabedbbeb..eee51dc27d 100644 --- a/examples/solar_system/src/main.rs +++ b/examples/solar_system/src/main.rs @@ -22,7 +22,7 @@ pub fn main() { struct SolarSystem { state: State, - solar_system: canvas::layer::Cached, + solar_system: canvas::layer::Cache, } #[derive(Debug, Clone, Copy)] @@ -38,7 +38,7 @@ impl Application for SolarSystem { ( SolarSystem { state: State::new(), - solar_system: canvas::layer::Cached::new(), + solar_system: canvas::layer::Cache::new(), }, Command::none(), ) @@ -125,7 +125,7 @@ impl State { } } -impl canvas::layer::Drawable for State { +impl canvas::Drawable for State { fn draw(&self, frame: &mut canvas::Frame) { use canvas::{Fill, Path, Stroke}; use std::f32::consts::PI; diff --git a/src/application.rs b/src/application.rs index 8a88b55a0a..a569163b44 100644 --- a/src/application.rs +++ b/src/application.rs @@ -179,7 +179,7 @@ pub trait Application: Sized { iced_wgpu::Settings { default_font: _settings.default_font, antialiasing: if _settings.use_antialiasing { - Some(iced_wgpu::settings::MSAA::X4) + Some(iced_wgpu::settings::Antialiasing::MSAAx4) } else { None }, diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs index 90d283537d..4f2b732dd3 100644 --- a/wgpu/src/lib.rs +++ b/wgpu/src/lib.rs @@ -19,7 +19,7 @@ //! [`wgpu`]: https://github.com/gfx-rs/wgpu-rs //! [WebGPU API]: https://gpuweb.github.io/gpuweb/ //! [`wgpu_glyph`]: https://github.com/hecrj/wgpu_glyph -//#![deny(missing_docs)] +#![deny(missing_docs)] #![deny(missing_debug_implementations)] #![deny(unused_results)] #![forbid(unsafe_code)] diff --git a/wgpu/src/settings.rs b/wgpu/src/settings.rs index c8a0cadf8e..65853ce225 100644 --- a/wgpu/src/settings.rs +++ b/wgpu/src/settings.rs @@ -1,6 +1,10 @@ +//! Configure a [`Renderer`]. +//! +//! [`Renderer`]: struct.Renderer.html + /// The settings of a [`Renderer`]. /// -/// [`Renderer`]: struct.Renderer.html +/// [`Renderer`]: ../struct.Renderer.html #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] pub struct Settings { /// The bytes of the font that will be used by default. @@ -9,24 +13,29 @@ pub struct Settings { pub default_font: Option<&'static [u8]>, /// The antialiasing strategy that will be used for triangle primitives. - pub antialiasing: Option, + pub antialiasing: Option, } +/// An antialiasing strategy. #[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum MSAA { - X2, - X4, - X8, - X16, +pub enum Antialiasing { + /// Multisample AA with 2 samples + MSAAx2, + /// Multisample AA with 4 samples + MSAAx4, + /// Multisample AA with 8 samples + MSAAx8, + /// Multisample AA with 16 samples + MSAAx16, } -impl MSAA { +impl Antialiasing { pub(crate) fn sample_count(&self) -> u32 { match self { - MSAA::X2 => 2, - MSAA::X4 => 4, - MSAA::X8 => 8, - MSAA::X16 => 16, + Antialiasing::MSAAx2 => 2, + Antialiasing::MSAAx4 => 4, + Antialiasing::MSAAx8 => 8, + Antialiasing::MSAAx16 => 16, } } } diff --git a/wgpu/src/triangle.rs b/wgpu/src/triangle.rs index 9159b0a217..d149eebc18 100644 --- a/wgpu/src/triangle.rs +++ b/wgpu/src/triangle.rs @@ -61,7 +61,7 @@ impl Buffer { impl Pipeline { pub fn new( device: &mut wgpu::Device, - antialiasing: Option, + antialiasing: Option, ) -> Pipeline { let constant_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { diff --git a/wgpu/src/triangle/msaa.rs b/wgpu/src/triangle/msaa.rs index 93fbe49b26..0def535262 100644 --- a/wgpu/src/triangle/msaa.rs +++ b/wgpu/src/triangle/msaa.rs @@ -10,7 +10,10 @@ pub struct Blit { } impl Blit { - pub fn new(device: &wgpu::Device, antialiasing: settings::MSAA) -> Blit { + pub fn new( + device: &wgpu::Device, + antialiasing: settings::Antialiasing, + ) -> Blit { let sampler = device.create_sampler(&wgpu::SamplerDescriptor { address_mode_u: wgpu::AddressMode::ClampToEdge, address_mode_v: wgpu::AddressMode::ClampToEdge, diff --git a/wgpu/src/widget/canvas.rs b/wgpu/src/widget/canvas.rs index e8fdc1e8ee..38c1ce622b 100644 --- a/wgpu/src/widget/canvas.rs +++ b/wgpu/src/widget/canvas.rs @@ -1,4 +1,11 @@ -//! Draw freely in 2D. +//! 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! +//! +//! [`Canvas`]: struct.Canvas.html +//! [`Frame`]: struct.Frame.html use crate::{Defaults, Primitive, Renderer}; use iced_native::{ @@ -9,17 +16,26 @@ use std::hash::Hash; pub mod layer; pub mod path; +mod drawable; mod fill; mod frame; mod stroke; +pub use drawable::Drawable; pub use fill::Fill; pub use frame::Frame; pub use layer::Layer; pub use path::Path; pub use stroke::{LineCap, LineJoin, Stroke}; -/// A 2D drawable region. +/// A widget capable of drawing 2D graphics. +/// +/// A [`Canvas`] may contain multiple layers. A [`Layer`] is drawn using the +/// painter's algorithm. In other words, layers will be drawn on top of each in +/// the same order they are pushed into the [`Canvas`]. +/// +/// [`Canvas`]: struct.Canvas.html +/// [`Layer`]: layer/trait.Layer.html #[derive(Debug)] pub struct Canvas<'a> { width: Length, @@ -30,6 +46,9 @@ pub struct Canvas<'a> { impl<'a> Canvas<'a> { const DEFAULT_SIZE: u16 = 100; + /// Creates a new [`Canvas`] with no layers. + /// + /// [`Canvas`]: struct.Canvas.html pub fn new() -> Self { Canvas { width: Length::Units(Self::DEFAULT_SIZE), @@ -38,16 +57,28 @@ impl<'a> Canvas<'a> { } } + /// Sets the width of the [`Canvas`]. + /// + /// [`Canvas`]: struct.Canvas.html pub fn width(mut self, width: Length) -> Self { self.width = width; self } + /// Sets the height of the [`Canvas`]. + /// + /// [`Canvas`]: struct.Canvas.html pub fn height(mut self, height: Length) -> Self { self.height = height; self } + /// Adds a [`Layer`] to the [`Canvas`]. + /// + /// It will be drawn on top of previous layers. + /// + /// [`Layer`]: layer/trait.Layer.html + /// [`Canvas`]: struct.Canvas.html pub fn push(mut self, layer: impl Layer + 'a) -> Self { self.layers.push(Box::new(layer)); self diff --git a/wgpu/src/widget/canvas/drawable.rs b/wgpu/src/widget/canvas/drawable.rs new file mode 100644 index 0000000000..6c74071c14 --- /dev/null +++ b/wgpu/src/widget/canvas/drawable.rs @@ -0,0 +1,12 @@ +use crate::canvas::Frame; + +/// A type that can be drawn on a [`Frame`]. +/// +/// [`Frame`]: struct.Frame.html +pub trait Drawable { + /// Draws the [`Drawable`] on the given [`Frame`]. + /// + /// [`Drawable`]: trait.Drawable.html + /// [`Frame`]: struct.Frame.html + fn draw(&self, frame: &mut Frame); +} diff --git a/wgpu/src/widget/canvas/fill.rs b/wgpu/src/widget/canvas/fill.rs index 9c23f9972d..5ce24cf30e 100644 --- a/wgpu/src/widget/canvas/fill.rs +++ b/wgpu/src/widget/canvas/fill.rs @@ -1,7 +1,9 @@ use iced_native::Color; +/// The style used to fill geometry. #[derive(Debug, Clone, Copy)] pub enum Fill { + /// Fill with a color. Color(Color), } diff --git a/wgpu/src/widget/canvas/frame.rs b/wgpu/src/widget/canvas/frame.rs index 27d676d69c..fa6d8c0adc 100644 --- a/wgpu/src/widget/canvas/frame.rs +++ b/wgpu/src/widget/canvas/frame.rs @@ -5,12 +5,14 @@ use crate::{ triangle, }; +/// The frame of a [`Canvas`]. +/// +/// [`Canvas`]: struct.Canvas.html #[derive(Debug)] pub struct Frame { width: f32, height: f32, buffers: lyon::tessellation::VertexBuffers, - transforms: Transforms, } @@ -27,6 +29,12 @@ struct Transform { } impl Frame { + /// Creates a new empty [`Frame`] with the given dimensions. + /// + /// The default coordinate system of a [`Frame`] has its origin at the + /// top-left corner of its bounds. + /// + /// [`Frame`]: struct.Frame.html pub fn new(width: f32, height: f32) -> Frame { Frame { width, @@ -42,26 +50,43 @@ impl Frame { } } + /// Returns the width of the [`Frame`]. + /// + /// [`Frame`]: struct.Frame.html #[inline] pub fn width(&self) -> f32 { self.width } + /// Returns the width of the [`Frame`]. + /// + /// [`Frame`]: struct.Frame.html #[inline] pub fn height(&self) -> f32 { self.height } + /// Returns the dimensions of the [`Frame`]. + /// + /// [`Frame`]: struct.Frame.html #[inline] pub fn size(&self) -> Size { Size::new(self.width, self.height) } + /// Returns the coordinate of the center of the [`Frame`]. + /// + /// [`Frame`]: struct.Frame.html #[inline] pub fn center(&self) -> Point { Point::new(self.width / 2.0, self.height / 2.0) } + /// Draws the given [`Path`] on the [`Frame`] by filling it with the + /// provided style. + /// + /// [`Path`]: path/struct.Path.html + /// [`Frame`]: struct.Frame.html pub fn fill(&mut self, path: &Path, fill: Fill) { use lyon::tessellation::{ BuffersBuilder, FillOptions, FillTessellator, @@ -95,6 +120,11 @@ impl Frame { let _ = result.expect("Tessellate path"); } + /// Draws the stroke of the given [`Path`] on the [`Frame`] with the + /// provided style. + /// + /// [`Path`]: path/struct.Path.html + /// [`Frame`]: struct.Frame.html pub fn stroke(&mut self, path: &Path, stroke: Stroke) { use lyon::tessellation::{ BuffersBuilder, StrokeOptions, StrokeTessellator, @@ -124,6 +154,13 @@ impl Frame { let _ = result.expect("Stroke path"); } + /// Stores the current transform of the [`Frame`] and executes the given + /// drawing operations, restoring the transform afterwards. + /// + /// This method is useful to compose transforms and perform drawing + /// operations in different coordinate systems. + /// + /// [`Frame`]: struct.Frame.html #[inline] pub fn with_save(&mut self, f: impl FnOnce(&mut Frame)) { self.transforms.previous.push(self.transforms.current); @@ -133,6 +170,9 @@ impl Frame { self.transforms.current = self.transforms.previous.pop().unwrap(); } + /// Applies a translation to the current transform of the [`Frame`]. + /// + /// [`Frame`]: struct.Frame.html #[inline] pub fn translate(&mut self, translation: Vector) { self.transforms.current.raw = self @@ -146,6 +186,9 @@ impl Frame { self.transforms.current.is_identity = false; } + /// Applies a rotation to the current transform of the [`Frame`]. + /// + /// [`Frame`]: struct.Frame.html #[inline] pub fn rotate(&mut self, angle: f32) { self.transforms.current.raw = self @@ -156,6 +199,9 @@ impl Frame { self.transforms.current.is_identity = false; } + /// Applies a scaling to the current transform of the [`Frame`]. + /// + /// [`Frame`]: struct.Frame.html #[inline] pub fn scale(&mut self, scale: f32) { self.transforms.current.raw = @@ -163,6 +209,9 @@ impl Frame { self.transforms.current.is_identity = false; } + /// Produces the geometry that has been drawn on the [`Frame`]. + /// + /// [`Frame`]: struct.Frame.html pub fn into_mesh(self) -> triangle::Mesh2D { triangle::Mesh2D { vertices: self.buffers.vertices, diff --git a/wgpu/src/widget/canvas/layer.rs b/wgpu/src/widget/canvas/layer.rs index 8c069f184e..82d647bb13 100644 --- a/wgpu/src/widget/canvas/layer.rs +++ b/wgpu/src/widget/canvas/layer.rs @@ -1,16 +1,25 @@ -mod cached; +//! Produce, store, and reuse geometry. +mod cache; -pub use cached::Cached; +pub use cache::Cache; -use crate::{canvas::Frame, triangle}; +use crate::triangle; use iced_native::Size; use std::sync::Arc; +/// A layer that can be presented at a [`Canvas`]. +/// +/// [`Canvas`]: ../struct.Canvas.html pub trait Layer: std::fmt::Debug { + /// Draws the [`Layer`] in the given bounds and produces [`Mesh2D`] as a + /// result. + /// + /// The [`Layer`] may choose to store the produced [`Mesh2D`] locally and + /// only recompute it when the bounds change, its contents change, or is + /// otherwise explicitly cleared by other means. + /// + /// [`Layer`]: trait.Layer.html + /// [`Mesh2D`]: ../../../triangle/struct.Mesh2D.html fn draw(&self, bounds: Size) -> Arc; } - -pub trait Drawable { - fn draw(&self, frame: &mut Frame); -} diff --git a/wgpu/src/widget/canvas/layer/cached.rs b/wgpu/src/widget/canvas/layer/cache.rs similarity index 50% rename from wgpu/src/widget/canvas/layer/cached.rs rename to wgpu/src/widget/canvas/layer/cache.rs index c6741372b4..3071cce036 100644 --- a/wgpu/src/widget/canvas/layer/cached.rs +++ b/wgpu/src/widget/canvas/layer/cache.rs @@ -1,5 +1,5 @@ use crate::{ - canvas::{layer::Drawable, Frame, Layer}, + canvas::{Drawable, Frame, Layer}, triangle, }; @@ -8,14 +8,21 @@ use std::cell::RefCell; use std::marker::PhantomData; use std::sync::Arc; +/// A simple cache that stores generated geometry to avoid recomputation. +/// +/// A [`Cache`] will not redraw its geometry unless the dimensions of its layer +/// change or it is explicitly cleared. +/// +/// [`Layer`]: ../trait.Layer.html +/// [`Cached`]: struct.Cached.html #[derive(Debug)] -pub struct Cached { +pub struct Cache { input: PhantomData, - cache: RefCell, + state: RefCell, } #[derive(Debug)] -enum Cache { +enum State { Empty, Filled { mesh: Arc, @@ -23,24 +30,36 @@ enum Cache { }, } -impl Cached +impl Cache where T: Drawable + std::fmt::Debug, { + /// Creates a new empty [`Cache`]. + /// + /// [`Cache`]: struct.Cache.html pub fn new() -> Self { - Cached { + Cache { input: PhantomData, - cache: RefCell::new(Cache::Empty), + state: RefCell::new(State::Empty), } } + /// Clears the cache, forcing a redraw the next time it is used. + /// + /// [`Cached`]: struct.Cached.html pub fn clear(&mut self) { - *self.cache.borrow_mut() = Cache::Empty; + *self.state.borrow_mut() = State::Empty; } + /// Binds the [`Cache`] with some data, producing a [`Layer`] that can be + /// added to a [`Canvas`]. + /// + /// [`Cache`]: struct.Cache.html + /// [`Layer`]: ../trait.Layer.html + /// [`Canvas`]: ../../struct.Canvas.html pub fn with<'a>(&'a self, input: &'a T) -> impl Layer + 'a { Bind { - layer: self, + cache: self, input: input, } } @@ -48,7 +67,7 @@ where #[derive(Debug)] struct Bind<'a, T: Drawable> { - layer: &'a Cached, + cache: &'a Cache, input: &'a T, } @@ -59,8 +78,8 @@ where fn draw(&self, current_bounds: Size) -> Arc { use std::ops::Deref; - if let Cache::Filled { mesh, bounds } = - self.layer.cache.borrow().deref() + if let State::Filled { mesh, bounds } = + self.cache.state.borrow().deref() { if *bounds == current_bounds { return mesh.clone(); @@ -72,7 +91,7 @@ where let mesh = Arc::new(frame.into_mesh()); - *self.layer.cache.borrow_mut() = Cache::Filled { + *self.cache.state.borrow_mut() = State::Filled { mesh: mesh.clone(), bounds: current_bounds, }; diff --git a/wgpu/src/widget/canvas/path.rs b/wgpu/src/widget/canvas/path.rs index b70d0aef9a..15c2e85333 100644 --- a/wgpu/src/widget/canvas/path.rs +++ b/wgpu/src/widget/canvas/path.rs @@ -1,13 +1,28 @@ -use iced_native::{Point, Size, Vector}; +//! Build different kinds of 2D shapes. +pub mod arc; -use lyon::path::builder::{Build, FlatPathBuilder, PathBuilder, SvgBuilder}; +mod builder; +pub use arc::Arc; +pub use builder::Builder; + +/// An immutable set of points that may or may not be connected. +/// +/// A single [`Path`] can represent different kinds of 2D shapes! +/// +/// [`Path`]: struct.Path.html #[derive(Debug, Clone)] pub struct Path { raw: lyon::path::Path, } impl Path { + /// Creates a new [`Path`] with the provided closure. + /// + /// Use the [`Builder`] to configure your [`Path`]. + /// + /// [`Path`]: struct.Path.html + /// [`Builder`]: struct.Builder.html pub fn new(f: impl FnOnce(&mut Builder)) -> Self { let mut builder = Builder::new(); @@ -32,152 +47,3 @@ impl Path { } } } - -#[allow(missing_debug_implementations)] -pub struct Builder { - raw: lyon::path::builder::SvgPathBuilder, -} - -impl Builder { - pub fn new() -> Builder { - Builder { - raw: lyon::path::Path::builder().with_svg(), - } - } - - #[inline] - pub fn move_to(&mut self, point: Point) { - let _ = self.raw.move_to(lyon::math::Point::new(point.x, point.y)); - } - - #[inline] - pub fn line_to(&mut self, point: Point) { - let _ = self.raw.line_to(lyon::math::Point::new(point.x, point.y)); - } - - #[inline] - pub fn arc(&mut self, arc: Arc) { - self.ellipse(arc.into()); - } - - pub fn arc_to(&mut self, a: Point, b: Point, radius: f32) { - use lyon::{math, path}; - - let a = math::Point::new(a.x, a.y); - - if self.raw.current_position() != a { - let _ = self.raw.line_to(a); - } - - let _ = self.raw.arc_to( - math::Vector::new(radius, radius), - math::Angle::radians(0.0), - path::ArcFlags::default(), - math::Point::new(b.x, b.y), - ); - } - - pub fn ellipse(&mut self, ellipse: Ellipse) { - use lyon::{geom, math}; - - let arc = geom::Arc { - center: math::Point::new(ellipse.center.x, ellipse.center.y), - radii: math::Vector::new(ellipse.radii.x, ellipse.radii.y), - x_rotation: math::Angle::radians(ellipse.rotation), - start_angle: math::Angle::radians(ellipse.start_angle), - sweep_angle: math::Angle::radians(ellipse.end_angle), - }; - - let _ = self.raw.move_to(arc.sample(0.0)); - - arc.for_each_quadratic_bezier(&mut |curve| { - let _ = self.raw.quadratic_bezier_to(curve.ctrl, curve.to); - }); - } - - #[inline] - pub fn bezier_curve_to( - &mut self, - control_a: Point, - 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), - math::Point::new(to.x, to.y), - ); - } - - #[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), - ); - } - - #[inline] - pub fn rectangle(&mut self, p: Point, size: Size) { - self.move_to(p); - self.line_to(Point::new(p.x + size.width, p.y)); - self.line_to(Point::new(p.x + size.width, p.y + size.height)); - self.line_to(Point::new(p.x, p.y + size.height)); - self.close(); - } - - #[inline] - pub fn circle(&mut self, center: Point, radius: f32) { - self.arc(Arc { - center, - radius, - start_angle: 0.0, - end_angle: 2.0 * std::f32::consts::PI, - }); - } - - #[inline] - pub fn close(&mut self) { - self.raw.close() - } - - #[inline] - pub fn build(self) -> Path { - Path { - raw: self.raw.build(), - } - } -} - -#[derive(Debug, Clone, Copy)] -pub struct Arc { - pub center: Point, - pub radius: f32, - pub start_angle: f32, - pub end_angle: f32, -} - -#[derive(Debug, Clone, Copy)] -pub struct Ellipse { - pub center: Point, - pub radii: Vector, - pub rotation: f32, - pub start_angle: f32, - pub end_angle: f32, -} - -impl From for Ellipse { - fn from(arc: Arc) -> Ellipse { - Ellipse { - center: arc.center, - radii: Vector::new(arc.radius, arc.radius), - rotation: 0.0, - start_angle: arc.start_angle, - end_angle: arc.end_angle, - } - } -} diff --git a/wgpu/src/widget/canvas/path/arc.rs b/wgpu/src/widget/canvas/path/arc.rs new file mode 100644 index 0000000000..343191f15d --- /dev/null +++ b/wgpu/src/widget/canvas/path/arc.rs @@ -0,0 +1,44 @@ +//! Build and draw curves. +use iced_native::{Point, Vector}; + +/// A segment of a differentiable curve. +#[derive(Debug, Clone, Copy)] +pub struct Arc { + /// The center of the arc. + pub center: Point, + /// The radius of the arc. + pub radius: f32, + /// The start of the segment's angle, clockwise rotation. + pub start_angle: f32, + /// The end of the segment's angle, clockwise rotation. + pub end_angle: f32, +} + +/// An elliptical [`Arc`]. +/// +/// [`Arc`]: struct.Arc.html +#[derive(Debug, Clone, Copy)] +pub struct Elliptical { + /// The center of the arc. + pub center: Point, + /// The radii of the arc's ellipse, defining its axes. + pub radii: Vector, + /// The rotation of the arc's ellipse. + pub rotation: f32, + /// The start of the segment's angle, clockwise rotation. + pub start_angle: f32, + /// The end of the segment's angle, clockwise rotation. + pub end_angle: f32, +} + +impl From for Elliptical { + fn from(arc: Arc) -> Elliptical { + Elliptical { + center: arc.center, + radii: Vector::new(arc.radius, arc.radius), + rotation: 0.0, + start_angle: arc.start_angle, + end_angle: arc.end_angle, + } + } +} diff --git a/wgpu/src/widget/canvas/path/builder.rs b/wgpu/src/widget/canvas/path/builder.rs new file mode 100644 index 0000000000..a013149e94 --- /dev/null +++ b/wgpu/src/widget/canvas/path/builder.rs @@ -0,0 +1,177 @@ +use crate::canvas::path::{arc, Arc, Path}; + +use iced_native::{Point, Size}; +use lyon::path::builder::{Build, FlatPathBuilder, PathBuilder, SvgBuilder}; + +/// A [`Path`] builder. +/// +/// Once a [`Path`] is built, it can no longer be mutated. +/// +/// [`Path`]: struct.Path.html +#[allow(missing_debug_implementations)] +pub struct Builder { + raw: lyon::path::builder::SvgPathBuilder, +} + +impl Builder { + /// Creates a new [`Builder`]. + /// + /// [`Builder`]: struct.Builder.html + pub fn new() -> Builder { + Builder { + 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)); + } + + /// Connects the last point in the [`Path`] to the given `Point` with a + /// straight line. + /// + /// [`Path`]: struct.Path.html + #[inline] + pub fn line_to(&mut self, point: Point) { + let _ = self.raw.line_to(lyon::math::Point::new(point.x, point.y)); + } + + /// Adds an [`Arc`] to the [`Path`] from `start_angle` to `end_angle` in + /// a clockwise direction. + /// + /// [`Arc`]: struct.Arc.html + /// [`Path`]: struct.Path.html + #[inline] + pub fn arc(&mut self, arc: Arc) { + self.ellipse(arc.into()); + } + + /// Adds a circular arc to the [`Path`] with the given control points and + /// radius. + /// + /// The arc is connected to the previous point by a straight line, if + /// necessary. + /// + /// [`Path`]: struct.Path.html + pub fn arc_to(&mut self, a: Point, b: Point, radius: f32) { + use lyon::{math, path}; + + let a = math::Point::new(a.x, a.y); + + if self.raw.current_position() != a { + let _ = self.raw.line_to(a); + } + + let _ = self.raw.arc_to( + math::Vector::new(radius, radius), + math::Angle::radians(0.0), + path::ArcFlags::default(), + math::Point::new(b.x, b.y), + ); + } + + /// Adds an [`Ellipse`] to the [`Path`] using a clockwise direction. + /// + /// [`Ellipse`]: struct.Arc.html + /// [`Path`]: struct.Path.html + 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), + x_rotation: math::Angle::radians(arc.rotation), + start_angle: math::Angle::radians(arc.start_angle), + sweep_angle: math::Angle::radians(arc.end_angle), + }; + + let _ = self.raw.move_to(arc.sample(0.0)); + + arc.for_each_quadratic_bezier(&mut |curve| { + let _ = self.raw.quadratic_bezier_to(curve.ctrl, curve.to); + }); + } + + /// Adds a cubic Bézier curve to the [`Path`] given its two control points + /// and its end point. + /// + /// [`Path`]: struct.Path.html + #[inline] + pub fn bezier_curve_to( + &mut self, + control_a: Point, + 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), + math::Point::new(to.x, to.y), + ); + } + + /// Adds a quadratic Bézier curve to the [`Path`] given its control point + /// and its end point. + /// + /// [`Path`]: struct.Path.html + #[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), + ); + } + + /// Adds a rectangle to the [`Path`] given its top-left corner coordinate + /// and its `Size`. + /// + /// [`Path`]: struct.Path.html + #[inline] + pub fn rectangle(&mut self, p: Point, size: Size) { + self.move_to(p); + self.line_to(Point::new(p.x + size.width, p.y)); + self.line_to(Point::new(p.x + size.width, p.y + size.height)); + self.line_to(Point::new(p.x, p.y + size.height)); + self.close(); + } + + /// Adds a circle to the [`Path`] given its center coordinate and its + /// radius. + /// + /// [`Path`]: struct.Path.html + #[inline] + pub fn circle(&mut self, center: Point, radius: f32) { + self.arc(Arc { + center, + radius, + start_angle: 0.0, + end_angle: 2.0 * std::f32::consts::PI, + }); + } + + /// Closes the current sub-path in the [`Path`] with a straight line to + /// the starting point. + /// + /// [`Path`]: struct.Path.html + #[inline] + pub fn close(&mut self) { + self.raw.close() + } + + /// Builds the [`Path`] of this [`Builder`]. + /// + /// [`Path`]: struct.Path.html + /// [`Builder`]: struct.Builder.html + #[inline] + pub fn build(self) -> Path { + Path { + raw: self.raw.build(), + } + } +} diff --git a/wgpu/src/widget/canvas/stroke.rs b/wgpu/src/widget/canvas/stroke.rs index 9bb260b2f9..46d669c42f 100644 --- a/wgpu/src/widget/canvas/stroke.rs +++ b/wgpu/src/widget/canvas/stroke.rs @@ -1,10 +1,16 @@ use iced_native::Color; +/// The style of a stroke. #[derive(Debug, Clone, Copy)] pub struct Stroke { + /// The color of the stroke. pub color: Color, + /// The distance between the two edges of the stroke. pub width: f32, + /// The shape to be used at the end of open subpaths when they are stroked. pub line_cap: LineCap, + /// The shape to be used at the corners of paths or basic shapes when they + /// are stroked. pub line_join: LineJoin, } @@ -19,10 +25,16 @@ impl Default for Stroke { } } +/// The shape used at the end of open subpaths when they are stroked. #[derive(Debug, Clone, Copy)] pub enum LineCap { + /// The stroke for each sub-path does not extend beyond its two endpoints. Butt, + /// At the end of each sub-path, the shape representing the stroke will be + /// extended by a square. Square, + /// At the end of each sub-path, the shape representing the stroke will be + /// extended by a semicircle. Round, } @@ -42,10 +54,15 @@ impl From for lyon::tessellation::LineCap { } } +/// The shape used at the corners of paths or basic shapes when they are +/// stroked. #[derive(Debug, Clone, Copy)] pub enum LineJoin { + /// A sharp corner. Miter, + /// A round corner. Round, + /// A bevelled corner. Bevel, } From 6f7247ca13181bcdfe1a3065215c1b3204723b84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Tue, 18 Feb 2020 09:54:24 +0100 Subject: [PATCH 40/45] Rename `Settings::use_antialiasing` to `antialiasing` --- examples/bezier_tool/src/main.rs | 2 +- examples/clock/src/main.rs | 2 +- examples/solar_system/src/main.rs | 2 +- src/application.rs | 2 +- src/settings.rs | 4 ++-- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/bezier_tool/src/main.rs b/examples/bezier_tool/src/main.rs index 01f8f847ea..023eb0f7fc 100644 --- a/examples/bezier_tool/src/main.rs +++ b/examples/bezier_tool/src/main.rs @@ -287,7 +287,7 @@ use iced::{ pub fn main() { Example::run(Settings { - use_antialiasing: true, + antialiasing: true, ..Settings::default() }); } diff --git a/examples/clock/src/main.rs b/examples/clock/src/main.rs index 559f01922f..d8266f069b 100644 --- a/examples/clock/src/main.rs +++ b/examples/clock/src/main.rs @@ -5,7 +5,7 @@ use iced::{ pub fn main() { Clock::run(Settings { - use_antialiasing: true, + antialiasing: true, ..Settings::default() }) } diff --git a/examples/solar_system/src/main.rs b/examples/solar_system/src/main.rs index eee51dc27d..4c239806d1 100644 --- a/examples/solar_system/src/main.rs +++ b/examples/solar_system/src/main.rs @@ -15,7 +15,7 @@ use std::time::Instant; pub fn main() { SolarSystem::run(Settings { - use_antialiasing: true, + antialiasing: true, ..Settings::default() }) } diff --git a/src/application.rs b/src/application.rs index a569163b44..374810cbde 100644 --- a/src/application.rs +++ b/src/application.rs @@ -178,7 +178,7 @@ pub trait Application: Sized { _settings.into(), iced_wgpu::Settings { default_font: _settings.default_font, - antialiasing: if _settings.use_antialiasing { + antialiasing: if _settings.antialiasing { Some(iced_wgpu::settings::Antialiasing::MSAAx4) } else { None diff --git a/src/settings.rs b/src/settings.rs index 757dc72f69..32ec583c7b 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -17,14 +17,14 @@ pub struct Settings { // TODO: Add `name` for web compatibility pub default_font: Option<&'static [u8]>, - /// If set to true, the renderer will try to use antialiasing for some + /// If set to true, the renderer will try to perform antialiasing for some /// primitives. /// /// Enabling it can produce a smoother result in some widgets, like the /// `Canvas`, at a performance cost. /// /// By default, it is disabled. - pub use_antialiasing: bool, + pub antialiasing: bool, } #[cfg(not(target_arch = "wasm32"))] From 2e457c394fa96de0e5b08940c6b8632becd92ccc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Wed, 19 Feb 2020 14:53:08 +0100 Subject: [PATCH 41/45] Quit application when `Cmd+Q` is pressed on macOS --- winit/src/application.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/winit/src/application.rs b/winit/src/application.rs index 824781072b..f5aa799c64 100644 --- a/winit/src/application.rs +++ b/winit/src/application.rs @@ -347,6 +347,19 @@ pub trait Application: Sized { WindowEvent::CloseRequested => { *control_flow = ControlFlow::Exit; } + #[cfg(target_os = "macos")] + WindowEvent::KeyboardInput { + input: + winit::event::KeyboardInput { + virtual_keycode: + Some(winit::event::VirtualKeyCode::Q), + state: winit::event::ElementState::Pressed, + .. + }, + .. + } if modifiers.logo() => { + *control_flow = ControlFlow::Exit; + } #[cfg(feature = "debug")] WindowEvent::KeyboardInput { input: From 23fe47bdcb5914224365387c04e8e5a9538e5783 Mon Sep 17 00:00:00 2001 From: Piaoger Date: Thu, 20 Feb 2020 17:28:42 +0800 Subject: [PATCH 42/45] Fix sample file path for svg example Sample tiger.svg is move to resources after directory restructuring. This change is to correct the path --- examples/svg/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/svg/src/main.rs b/examples/svg/src/main.rs index 57358e24eb..1fb80534ea 100644 --- a/examples/svg/src/main.rs +++ b/examples/svg/src/main.rs @@ -22,7 +22,7 @@ impl Sandbox for Tiger { fn view(&mut self) -> Element<()> { let content = Column::new().padding(20).push( - Svg::new(format!("{}/tiger.svg", env!("CARGO_MANIFEST_DIR"))) + Svg::new(format!("{}/resources/tiger.svg", env!("CARGO_MANIFEST_DIR"))) .width(Length::Fill) .height(Length::Fill), ); From b0cf47cc2b736080d25a6fe48d17ef5086d6a39a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Fri, 21 Feb 2020 15:12:27 +0100 Subject: [PATCH 43/45] Create `README` files for all the examples --- examples/README.md | 4 +++- examples/bezier_tool/README.md | 19 +++++++++++++++++++ examples/clock/README.md | 16 ++++++++++++++++ examples/counter/README.md | 18 ++++++++++++++++++ examples/custom_widget/README.md | 18 ++++++++++++++++++ examples/events/README.md | 18 ++++++++++++++++++ examples/geometry/README.md | 18 ++++++++++++++++++ examples/integration/README.md | 18 ++++++++++++++++++ examples/progress_bar/README.md | 18 ++++++++++++++++++ examples/solar_system/README.md | 18 ++++++++++++++++++ examples/stopwatch/README.md | 18 ++++++++++++++++++ examples/svg/README.md | 17 +++++++++++++++++ 12 files changed, 199 insertions(+), 1 deletion(-) create mode 100644 examples/bezier_tool/README.md create mode 100644 examples/clock/README.md create mode 100644 examples/counter/README.md create mode 100644 examples/custom_widget/README.md create mode 100644 examples/events/README.md create mode 100644 examples/geometry/README.md create mode 100644 examples/integration/README.md create mode 100644 examples/progress_bar/README.md create mode 100644 examples/solar_system/README.md create mode 100644 examples/stopwatch/README.md create mode 100644 examples/svg/README.md diff --git a/examples/README.md b/examples/README.md index a9ab546f40..04399b932f 100644 --- a/examples/README.md +++ b/examples/README.md @@ -69,7 +69,8 @@ cargo run --package styling ## Extras A bunch of simpler examples exist: -- [`bezier_tool`](bezier_tool), a Paint-like tool for drawing Bezier curves using [`lyon`]. +- [`bezier_tool`](bezier_tool), a Paint-like tool for drawing Bézier curves using [`lyon`]. +- [`clock`](clock), an application that uses the `Canvas` widget to draw a clock and its hands to display the current time. - [`counter`](counter), the classic counter example explained in the [`README`](../README.md). - [`custom_widget`](custom_widget), a demonstration of how to build a custom widget that draws a circle. - [`events`](events), a log of native events displayed using a conditional `Subscription`. @@ -77,6 +78,7 @@ A bunch of simpler examples exist: - [`integration`](integration), a demonstration of how to integrate Iced in an existing graphical application. - [`pokedex`](pokedex), an application that displays a random Pokédex entry (sprite included!) by using the [PokéAPI]. - [`progress_bar`](progress_bar), a simple progress bar that can be filled by using a slider. +- [`solar_system`](solar_system), an animated solar system drawn using the `Canvas` widget and showcasing how to compose different transforms. - [`stopwatch`](stopwatch), a watch with start/stop and reset buttons showcasing how to listen to time. - [`svg`](svg), an application that renders the [Ghostscript Tiger] by leveraging the `Svg` widget. diff --git a/examples/bezier_tool/README.md b/examples/bezier_tool/README.md new file mode 100644 index 0000000000..933f21200e --- /dev/null +++ b/examples/bezier_tool/README.md @@ -0,0 +1,19 @@ +## Bézier tool + +A Paint-like tool for drawing Bézier curves using [`lyon`]. + +The __[`main`]__ file contains all the code of the example. + + + +You can run it with `cargo run`: +``` +cargo run --package bezier_tool +``` + +[`main`]: src/main.rs +[`lyon`]: https://github.com/nical/lyon diff --git a/examples/clock/README.md b/examples/clock/README.md new file mode 100644 index 0000000000..175091805b --- /dev/null +++ b/examples/clock/README.md @@ -0,0 +1,16 @@ +## Clock + +An application that uses the `Canvas` widget to draw a clock and its hands to display the current time. + +The __[`main`]__ file contains all the code of the example. + +
+ +
+ +You can run it with `cargo run`: +``` +cargo run --package clock +``` + +[`main`]: src/main.rs diff --git a/examples/counter/README.md b/examples/counter/README.md new file mode 100644 index 0000000000..4d9fc5b979 --- /dev/null +++ b/examples/counter/README.md @@ -0,0 +1,18 @@ +## Counter + +The classic counter example explained in the [`README`](../../README.md). + +The __[`main`]__ file contains all the code of the example. + + + +You can run it with `cargo run`: +``` +cargo run --package counter +``` + +[`main`]: src/main.rs diff --git a/examples/custom_widget/README.md b/examples/custom_widget/README.md new file mode 100644 index 0000000000..3d6cf902fe --- /dev/null +++ b/examples/custom_widget/README.md @@ -0,0 +1,18 @@ +## Custom widget + +A demonstration of how to build a custom widget that draws a circle. + +The __[`main`]__ file contains all the code of the example. + + + +You can run it with `cargo run`: +``` +cargo run --package custom_widget +``` + +[`main`]: src/main.rs diff --git a/examples/events/README.md b/examples/events/README.md new file mode 100644 index 0000000000..3c9a1cab3e --- /dev/null +++ b/examples/events/README.md @@ -0,0 +1,18 @@ +## Events + +A log of native events displayed using a conditional `Subscription`. + +The __[`main`]__ file contains all the code of the example. + + + +You can run it with `cargo run`: +``` +cargo run --package events +``` + +[`main`]: src/main.rs diff --git a/examples/geometry/README.md b/examples/geometry/README.md new file mode 100644 index 0000000000..4d5c81cbef --- /dev/null +++ b/examples/geometry/README.md @@ -0,0 +1,18 @@ +## Geometry + +A custom widget showcasing how to draw geometry with the `Mesh2D` primitive in [`iced_wgpu`](../../wgpu). + +The __[`main`]__ file contains all the code of the example. + + + +You can run it with `cargo run`: +``` +cargo run --package geometry +``` + +[`main`]: src/main.rs diff --git a/examples/integration/README.md b/examples/integration/README.md new file mode 100644 index 0000000000..d5aabc1970 --- /dev/null +++ b/examples/integration/README.md @@ -0,0 +1,18 @@ +## Integration + +A demonstration of how to integrate Iced in an existing graphical application. + +The __[`main`]__ file contains all the code of the example. + + + +You can run it with `cargo run`: +``` +cargo run --package integration +``` + +[`main`]: src/main.rs diff --git a/examples/progress_bar/README.md b/examples/progress_bar/README.md new file mode 100644 index 0000000000..1e927b3cf3 --- /dev/null +++ b/examples/progress_bar/README.md @@ -0,0 +1,18 @@ +## Progress bar + +A simple progress bar that can be filled by using a slider. + +The __[`main`]__ file contains all the code of the example. + + + +You can run it with `cargo run`: +``` +cargo run --package progress_bar +``` + +[`main`]: src/main.rs diff --git a/examples/solar_system/README.md b/examples/solar_system/README.md new file mode 100644 index 0000000000..acfbc46639 --- /dev/null +++ b/examples/solar_system/README.md @@ -0,0 +1,18 @@ +## Solar system + +An animated solar system drawn using the `Canvas` widget and showcasing how to compose different transforms. + +The __[`main`]__ file contains all the code of the example. + + + +You can run it with `cargo run`: +``` +cargo run --package solar_system +``` + +[`main`]: src/main.rs diff --git a/examples/stopwatch/README.md b/examples/stopwatch/README.md new file mode 100644 index 0000000000..4cf4582ed0 --- /dev/null +++ b/examples/stopwatch/README.md @@ -0,0 +1,18 @@ +## Stopwatch + +A watch with start/stop and reset buttons showcasing how to listen to time. + +The __[`main`]__ file contains all the code of the example. + + + +You can run it with `cargo run`: +``` +cargo run --package stopwatch +``` + +[`main`]: src/main.rs diff --git a/examples/svg/README.md b/examples/svg/README.md new file mode 100644 index 0000000000..9f53c1774c --- /dev/null +++ b/examples/svg/README.md @@ -0,0 +1,17 @@ +## SVG + +An application that renders the [Ghostscript Tiger] by leveraging the `Svg` widget. + +The __[`main`]__ file contains all the code of the example. + +
+ +
+ +You can run it with `cargo run`: +``` +cargo run --package svg +``` + +[`main`]: src/main.rs +[Ghostscript Tiger]: https://commons.wikimedia.org/wiki/File:Ghostscript_Tiger.svg From 126133ead775fda064a6c23503e9a552a10dc2c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Sat, 22 Feb 2020 18:03:49 +0100 Subject: [PATCH 44/45] Fix `Clip` primitive intersection in `iced_wgpu` --- core/src/rectangle.rs | 50 +++++++++++++++++++++++++++++++++++++++++++ wgpu/src/renderer.rs | 29 ++++++++++--------------- 2 files changed, 61 insertions(+), 18 deletions(-) diff --git a/core/src/rectangle.rs b/core/src/rectangle.rs index ee1e38079b..7ed3d2dfdd 100644 --- a/core/src/rectangle.rs +++ b/core/src/rectangle.rs @@ -27,6 +27,34 @@ impl Rectangle { && self.y <= point.y && point.y <= self.y + self.height } + + /// Computes the intersection with the given [`Rectangle`]. + /// + /// [`Rectangle`]: struct.Rectangle.html + pub fn intersection( + &self, + other: &Rectangle, + ) -> Option> { + let x = self.x.max(other.x); + let y = self.y.max(other.y); + + let lower_right_x = (self.x + self.width).min(other.x + other.width); + let lower_right_y = (self.y + self.height).min(other.y + other.height); + + let width = lower_right_x - x; + let height = lower_right_y - y; + + if width > 0.0 && height > 0.0 { + Some(Rectangle { + x, + y, + width, + height, + }) + } else { + None + } + } } impl std::ops::Mul for Rectangle { @@ -41,3 +69,25 @@ impl std::ops::Mul for Rectangle { } } } + +impl From> for Rectangle { + fn from(rectangle: Rectangle) -> Rectangle { + Rectangle { + x: rectangle.x as f32, + y: rectangle.y as f32, + width: rectangle.width as f32, + height: rectangle.height as f32, + } + } +} + +impl From> for Rectangle { + fn from(rectangle: Rectangle) -> Rectangle { + Rectangle { + x: rectangle.x as u32, + y: rectangle.y as u32, + width: rectangle.width.ceil() as u32, + height: rectangle.height.ceil() as u32, + } + } +} diff --git a/wgpu/src/renderer.rs b/wgpu/src/renderer.rs index 29adcfb657..af61804e19 100644 --- a/wgpu/src/renderer.rs +++ b/wgpu/src/renderer.rs @@ -240,25 +240,18 @@ impl Renderer { offset, content, } => { - let x = bounds.x - layer.offset.x as f32; - let y = bounds.y - layer.offset.y as f32; - let width = (bounds.width + x).min(bounds.width); - let height = (bounds.height + y).min(bounds.height); - - // Only draw visible content on-screen - // TODO: Also, check for parent layer bounds to avoid further - // drawing in some circumstances. - if width > 0.0 && height > 0.0 { - let clip_layer = Layer::new( - Rectangle { - x: x.max(0.0).floor() as u32, - y: y.max(0.0).floor() as u32, - width: width.ceil() as u32, - height: height.ceil() as u32, - }, - layer.offset + *offset, - ); + let layer_bounds: Rectangle = layer.bounds.into(); + let clip = Rectangle { + x: bounds.x - layer.offset.x as f32, + y: bounds.y - layer.offset.y as f32, + ..*bounds + }; + + // Only draw visible content + if let Some(clip_bounds) = layer_bounds.intersection(&clip) { + let clip_layer = + Layer::new(clip_bounds.into(), layer.offset + *offset); let new_layer = Layer::new(layer.bounds, layer.offset); layers.push(clip_layer); From be14aca07506385a209e89cd99256744a7ec3c0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Mon, 24 Feb 2020 20:08:40 +0100 Subject: [PATCH 45/45] Make output format of `iced_wgpu` configurable --- examples/integration/src/main.rs | 4 +++- src/application.rs | 1 + wgpu/src/image.rs | 4 ++-- wgpu/src/quad.rs | 7 +++++-- wgpu/src/renderer.rs | 15 ++++++++++----- wgpu/src/settings.rs | 17 ++++++++++++++++- wgpu/src/text.rs | 8 ++++++-- wgpu/src/triangle.rs | 5 +++-- wgpu/src/triangle/msaa.rs | 12 +++++++++--- wgpu/src/window/backend.rs | 12 ++++++++++-- wgpu/src/window/swap_chain.rs | 6 ++++-- 11 files changed, 69 insertions(+), 22 deletions(-) diff --git a/examples/integration/src/main.rs b/examples/integration/src/main.rs index ed36f73624..4be913c17b 100644 --- a/examples/integration/src/main.rs +++ b/examples/integration/src/main.rs @@ -40,11 +40,12 @@ pub fn main() { }); let surface = wgpu::Surface::create(&window); + let format = wgpu::TextureFormat::Bgra8UnormSrgb; let mut swap_chain = { let size = window.inner_size(); - SwapChain::new(&device, &surface, size.width, size.height) + SwapChain::new(&device, &surface, format, size.width, size.height) }; let mut resized = false; @@ -163,6 +164,7 @@ pub fn main() { swap_chain = SwapChain::new( &device, &surface, + format, size.width, size.height, ); diff --git a/src/application.rs b/src/application.rs index 374810cbde..2ee3337f4d 100644 --- a/src/application.rs +++ b/src/application.rs @@ -183,6 +183,7 @@ pub trait Application: Sized { } else { None }, + ..iced_wgpu::Settings::default() }, ); diff --git a/wgpu/src/image.rs b/wgpu/src/image.rs index ccc956a960..f7ed67c3b2 100644 --- a/wgpu/src/image.rs +++ b/wgpu/src/image.rs @@ -28,7 +28,7 @@ pub struct Pipeline { } impl Pipeline { - pub fn new(device: &wgpu::Device) -> Self { + pub fn new(device: &wgpu::Device, format: wgpu::TextureFormat) -> Self { let sampler = device.create_sampler(&wgpu::SamplerDescriptor { address_mode_u: wgpu::AddressMode::ClampToEdge, address_mode_v: wgpu::AddressMode::ClampToEdge, @@ -135,7 +135,7 @@ impl Pipeline { }), primitive_topology: wgpu::PrimitiveTopology::TriangleList, color_states: &[wgpu::ColorStateDescriptor { - format: wgpu::TextureFormat::Bgra8UnormSrgb, + format, color_blend: wgpu::BlendDescriptor { src_factor: wgpu::BlendFactor::SrcAlpha, dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha, diff --git a/wgpu/src/quad.rs b/wgpu/src/quad.rs index fe3276a39e..9047080d96 100644 --- a/wgpu/src/quad.rs +++ b/wgpu/src/quad.rs @@ -14,7 +14,10 @@ pub struct Pipeline { } impl Pipeline { - pub fn new(device: &mut wgpu::Device) -> Pipeline { + pub fn new( + device: &mut wgpu::Device, + format: wgpu::TextureFormat, + ) -> Pipeline { let constant_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { bindings: &[wgpu::BindGroupLayoutBinding { @@ -79,7 +82,7 @@ impl Pipeline { }), primitive_topology: wgpu::PrimitiveTopology::TriangleList, color_states: &[wgpu::ColorStateDescriptor { - format: wgpu::TextureFormat::Bgra8UnormSrgb, + format, color_blend: wgpu::BlendDescriptor { src_factor: wgpu::BlendFactor::SrcAlpha, dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha, diff --git a/wgpu/src/renderer.rs b/wgpu/src/renderer.rs index af61804e19..f67dd1eb48 100644 --- a/wgpu/src/renderer.rs +++ b/wgpu/src/renderer.rs @@ -48,11 +48,16 @@ impl Renderer { /// /// [`Renderer`]: struct.Renderer.html pub fn new(device: &mut wgpu::Device, settings: Settings) -> Self { - let text_pipeline = text::Pipeline::new(device, settings.default_font); - let quad_pipeline = quad::Pipeline::new(device); - let image_pipeline = crate::image::Pipeline::new(device); - let triangle_pipeline = - triangle::Pipeline::new(device, settings.antialiasing); + let text_pipeline = + text::Pipeline::new(device, settings.format, settings.default_font); + let quad_pipeline = quad::Pipeline::new(device, settings.format); + let image_pipeline = + crate::image::Pipeline::new(device, settings.format); + let triangle_pipeline = triangle::Pipeline::new( + device, + settings.format, + settings.antialiasing, + ); Self { quad_pipeline, diff --git a/wgpu/src/settings.rs b/wgpu/src/settings.rs index 65853ce225..f946ce0d4b 100644 --- a/wgpu/src/settings.rs +++ b/wgpu/src/settings.rs @@ -5,8 +5,13 @@ /// The settings of a [`Renderer`]. /// /// [`Renderer`]: ../struct.Renderer.html -#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct Settings { + /// The output format of the [`Renderer`]. + /// + /// [`Renderer`]: ../struct.Renderer.html + pub format: wgpu::TextureFormat, + /// The bytes of the font that will be used by default. /// /// If `None` is provided, a default system font will be chosen. @@ -16,6 +21,16 @@ pub struct Settings { pub antialiasing: Option, } +impl Default for Settings { + fn default() -> Settings { + Settings { + format: wgpu::TextureFormat::Bgra8UnormSrgb, + default_font: None, + antialiasing: None, + } + } +} + /// An antialiasing strategy. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Antialiasing { diff --git a/wgpu/src/text.rs b/wgpu/src/text.rs index ab9a2f7116..c5670102d0 100644 --- a/wgpu/src/text.rs +++ b/wgpu/src/text.rs @@ -22,7 +22,11 @@ pub struct Pipeline { } impl Pipeline { - pub fn new(device: &mut wgpu::Device, default_font: Option<&[u8]>) -> Self { + pub fn new( + device: &mut wgpu::Device, + format: wgpu::TextureFormat, + default_font: Option<&[u8]>, + ) -> Self { // TODO: Font customization let font_source = font::Source::new(); @@ -54,7 +58,7 @@ impl Pipeline { let draw_brush = brush_builder .initial_cache_size((2048, 2048)) - .build(device, wgpu::TextureFormat::Bgra8UnormSrgb); + .build(device, format); Pipeline { draw_brush: RefCell::new(draw_brush), diff --git a/wgpu/src/triangle.rs b/wgpu/src/triangle.rs index d149eebc18..fe34040e66 100644 --- a/wgpu/src/triangle.rs +++ b/wgpu/src/triangle.rs @@ -61,6 +61,7 @@ impl Buffer { impl Pipeline { pub fn new( device: &mut wgpu::Device, + format: wgpu::TextureFormat, antialiasing: Option, ) -> Pipeline { let constant_layout = @@ -127,7 +128,7 @@ impl Pipeline { }), primitive_topology: wgpu::PrimitiveTopology::TriangleList, color_states: &[wgpu::ColorStateDescriptor { - format: wgpu::TextureFormat::Bgra8UnormSrgb, + format, color_blend: wgpu::BlendDescriptor { src_factor: wgpu::BlendFactor::SrcAlpha, dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha, @@ -169,7 +170,7 @@ impl Pipeline { Pipeline { pipeline, - blit: antialiasing.map(|a| msaa::Blit::new(device, a)), + blit: antialiasing.map(|a| msaa::Blit::new(device, format, a)), constants: constant_bind_group, uniforms_buffer: constants_buffer, vertex_buffer: Buffer::new( diff --git a/wgpu/src/triangle/msaa.rs b/wgpu/src/triangle/msaa.rs index 0def535262..7ccfb06236 100644 --- a/wgpu/src/triangle/msaa.rs +++ b/wgpu/src/triangle/msaa.rs @@ -2,6 +2,7 @@ use crate::settings; #[derive(Debug)] pub struct Blit { + format: wgpu::TextureFormat, pipeline: wgpu::RenderPipeline, constants: wgpu::BindGroup, texture_layout: wgpu::BindGroupLayout, @@ -12,6 +13,7 @@ pub struct Blit { impl Blit { pub fn new( device: &wgpu::Device, + format: wgpu::TextureFormat, antialiasing: settings::Antialiasing, ) -> Blit { let sampler = device.create_sampler(&wgpu::SamplerDescriptor { @@ -93,7 +95,7 @@ impl Blit { }), primitive_topology: wgpu::PrimitiveTopology::TriangleList, color_states: &[wgpu::ColorStateDescriptor { - format: wgpu::TextureFormat::Bgra8UnormSrgb, + format, color_blend: wgpu::BlendDescriptor { src_factor: wgpu::BlendFactor::SrcAlpha, dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha, @@ -115,6 +117,7 @@ impl Blit { }); Blit { + format, pipeline, constants: constant_bind_group, texture_layout: texture_layout, @@ -133,6 +136,7 @@ impl Blit { None => { self.targets = Some(Targets::new( &device, + self.format, &self.texture_layout, self.sample_count, width, @@ -143,6 +147,7 @@ impl Blit { if targets.width != width || targets.height != height { self.targets = Some(Targets::new( &device, + self.format, &self.texture_layout, self.sample_count, width, @@ -204,6 +209,7 @@ struct Targets { impl Targets { pub fn new( device: &wgpu::Device, + format: wgpu::TextureFormat, texture_layout: &wgpu::BindGroupLayout, sample_count: u32, width: u32, @@ -221,7 +227,7 @@ impl Targets { mip_level_count: 1, sample_count, dimension: wgpu::TextureDimension::D2, - format: wgpu::TextureFormat::Bgra8UnormSrgb, + format, usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT, }); @@ -231,7 +237,7 @@ impl Targets { mip_level_count: 1, sample_count: 1, dimension: wgpu::TextureDimension::D2, - format: wgpu::TextureFormat::Bgra8UnormSrgb, + format, usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT | wgpu::TextureUsage::SAMPLED, }); diff --git a/wgpu/src/window/backend.rs b/wgpu/src/window/backend.rs index 4c9f289b46..5b269f36bb 100644 --- a/wgpu/src/window/backend.rs +++ b/wgpu/src/window/backend.rs @@ -8,6 +8,7 @@ use raw_window_handle::HasRawWindowHandle; pub struct Backend { device: wgpu::Device, queue: wgpu::Queue, + format: wgpu::TextureFormat, } impl iced_native::window::Backend for Backend { @@ -37,7 +38,14 @@ impl iced_native::window::Backend for Backend { let renderer = Renderer::new(&mut device, settings); - (Backend { device, queue }, renderer) + ( + Backend { + device, + queue, + format: settings.format, + }, + renderer, + ) } fn create_surface( @@ -53,7 +61,7 @@ impl iced_native::window::Backend for Backend { width: u32, height: u32, ) -> SwapChain { - SwapChain::new(&self.device, surface, width, height) + SwapChain::new(&self.device, surface, self.format, width, height) } fn draw>( diff --git a/wgpu/src/window/swap_chain.rs b/wgpu/src/window/swap_chain.rs index 6f545fceac..4ca2901be3 100644 --- a/wgpu/src/window/swap_chain.rs +++ b/wgpu/src/window/swap_chain.rs @@ -18,11 +18,12 @@ impl SwapChain { pub fn new( device: &wgpu::Device, surface: &wgpu::Surface, + format: wgpu::TextureFormat, width: u32, height: u32, ) -> SwapChain { SwapChain { - raw: new_swap_chain(surface, width, height, device), + raw: new_swap_chain(surface, format, width, height, device), viewport: Viewport::new(width, height), } } @@ -38,6 +39,7 @@ impl SwapChain { fn new_swap_chain( surface: &wgpu::Surface, + format: wgpu::TextureFormat, width: u32, height: u32, device: &wgpu::Device, @@ -46,7 +48,7 @@ fn new_swap_chain( &surface, &wgpu::SwapChainDescriptor { usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT, - format: wgpu::TextureFormat::Bgra8UnormSrgb, + format, width, height, present_mode: wgpu::PresentMode::Vsync,