Merge pull request #1932 from iced-rs/generic-graphics-primitive
Backend-specific primitives
This commit is contained in:
commit
c6b583113d
35 changed files with 869 additions and 482 deletions
|
|
@ -5,26 +5,13 @@ mod null;
|
|||
#[cfg(debug_assertions)]
|
||||
pub use null::Null;
|
||||
|
||||
use crate::layout;
|
||||
use crate::{Background, BorderRadius, Color, Element, Rectangle, Vector};
|
||||
use crate::{Background, BorderRadius, Color, Rectangle, Vector};
|
||||
|
||||
/// A component that can be used by widgets to draw themselves on a screen.
|
||||
pub trait Renderer: Sized {
|
||||
/// The supported theme of the [`Renderer`].
|
||||
type Theme;
|
||||
|
||||
/// Lays out the elements of a user interface.
|
||||
///
|
||||
/// You should override this if you need to perform any operations before or
|
||||
/// after layouting. For instance, trimming the measurements cache.
|
||||
fn layout<Message>(
|
||||
&mut self,
|
||||
element: &Element<'_, Message, Self>,
|
||||
limits: &layout::Limits,
|
||||
) -> layout::Node {
|
||||
element.as_widget().layout(self, limits)
|
||||
}
|
||||
|
||||
/// Draws the primitives recorded in the given closure in a new layer.
|
||||
///
|
||||
/// The layer will clip its contents to the provided `bounds`.
|
||||
|
|
|
|||
|
|
@ -7,4 +7,3 @@ publish = false
|
|||
|
||||
[dependencies]
|
||||
iced = { path = "../..", features = ["advanced"] }
|
||||
iced_graphics = { path = "../../graphics" }
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
//! This example showcases a simple native custom widget that renders using
|
||||
//! arbitrary low-level geometry.
|
||||
mod rainbow {
|
||||
use iced_graphics::primitive::{ColoredVertex2D, Primitive};
|
||||
|
||||
use iced::advanced::graphics::color;
|
||||
use iced::advanced::layout::{self, Layout};
|
||||
use iced::advanced::renderer;
|
||||
|
|
@ -46,8 +44,8 @@ mod rainbow {
|
|||
cursor: mouse::Cursor,
|
||||
_viewport: &Rectangle,
|
||||
) {
|
||||
use iced::advanced::graphics::mesh::{self, Mesh, SolidVertex2D};
|
||||
use iced::advanced::Renderer as _;
|
||||
use iced_graphics::primitive::Mesh2D;
|
||||
|
||||
let bounds = layout.bounds();
|
||||
|
||||
|
|
@ -78,43 +76,43 @@ mod rainbow {
|
|||
let posn_bl = [0.0, bounds.height];
|
||||
let posn_l = [0.0, bounds.height / 2.0];
|
||||
|
||||
let mesh = Primitive::SolidMesh {
|
||||
let mesh = Mesh::Solid {
|
||||
size: bounds.size(),
|
||||
buffers: Mesh2D {
|
||||
buffers: mesh::Indexed {
|
||||
vertices: vec![
|
||||
ColoredVertex2D {
|
||||
SolidVertex2D {
|
||||
position: posn_center,
|
||||
color: color::pack([1.0, 1.0, 1.0, 1.0]),
|
||||
},
|
||||
ColoredVertex2D {
|
||||
SolidVertex2D {
|
||||
position: posn_tl,
|
||||
color: color::pack(color_r),
|
||||
},
|
||||
ColoredVertex2D {
|
||||
SolidVertex2D {
|
||||
position: posn_t,
|
||||
color: color::pack(color_o),
|
||||
},
|
||||
ColoredVertex2D {
|
||||
SolidVertex2D {
|
||||
position: posn_tr,
|
||||
color: color::pack(color_y),
|
||||
},
|
||||
ColoredVertex2D {
|
||||
SolidVertex2D {
|
||||
position: posn_r,
|
||||
color: color::pack(color_g),
|
||||
},
|
||||
ColoredVertex2D {
|
||||
SolidVertex2D {
|
||||
position: posn_br,
|
||||
color: color::pack(color_gb),
|
||||
},
|
||||
ColoredVertex2D {
|
||||
SolidVertex2D {
|
||||
position: posn_b,
|
||||
color: color::pack(color_b),
|
||||
},
|
||||
ColoredVertex2D {
|
||||
SolidVertex2D {
|
||||
position: posn_bl,
|
||||
color: color::pack(color_i),
|
||||
},
|
||||
ColoredVertex2D {
|
||||
SolidVertex2D {
|
||||
position: posn_l,
|
||||
color: color::pack(color_v),
|
||||
},
|
||||
|
|
@ -135,7 +133,7 @@ mod rainbow {
|
|||
renderer.with_translation(
|
||||
Vector::new(bounds.x, bounds.y),
|
||||
|renderer| {
|
||||
renderer.draw_primitive(mesh);
|
||||
renderer.draw_mesh(mesh);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -358,7 +358,9 @@ where
|
|||
renderer.with_translation(
|
||||
Vector::new(bounds.x, bounds.y),
|
||||
|renderer| {
|
||||
renderer.draw_primitive(geometry.0);
|
||||
use iced::advanced::graphics::geometry::Renderer as _;
|
||||
|
||||
renderer.draw(vec![geometry]);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,10 +32,6 @@ features = ["derive"]
|
|||
version = "0.9"
|
||||
path = "../core"
|
||||
|
||||
[dependencies.tiny-skia]
|
||||
version = "0.9"
|
||||
optional = true
|
||||
|
||||
[dependencies.image]
|
||||
version = "0.24"
|
||||
optional = true
|
||||
|
|
|
|||
|
|
@ -6,6 +6,14 @@ use iced_core::{Font, Point, Size};
|
|||
|
||||
use std::borrow::Cow;
|
||||
|
||||
/// The graphics backend of a [`Renderer`].
|
||||
///
|
||||
/// [`Renderer`]: crate::Renderer
|
||||
pub trait Backend {
|
||||
/// The custom kind of primitives this [`Backend`] supports.
|
||||
type Primitive;
|
||||
}
|
||||
|
||||
/// A graphics backend that supports text rendering.
|
||||
pub trait Text {
|
||||
/// The icon font of the backend.
|
||||
|
|
|
|||
|
|
@ -1,11 +1,66 @@
|
|||
//! Track and compute the damage of graphical primitives.
|
||||
use crate::core::alignment;
|
||||
use crate::core::{Rectangle, Size};
|
||||
use crate::Primitive;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
/// Computes the damage regions between the two given primitives.
|
||||
pub fn regions(a: &Primitive, b: &Primitive) -> Vec<Rectangle> {
|
||||
/// A type that has some damage bounds.
|
||||
pub trait Damage: PartialEq {
|
||||
/// Returns the bounds of the [`Damage`].
|
||||
fn bounds(&self) -> Rectangle;
|
||||
}
|
||||
|
||||
impl<T: Damage> Damage for Primitive<T> {
|
||||
fn bounds(&self) -> Rectangle {
|
||||
match self {
|
||||
Self::Text {
|
||||
bounds,
|
||||
horizontal_alignment,
|
||||
vertical_alignment,
|
||||
..
|
||||
} => {
|
||||
let mut bounds = *bounds;
|
||||
|
||||
bounds.x = match horizontal_alignment {
|
||||
alignment::Horizontal::Left => bounds.x,
|
||||
alignment::Horizontal::Center => {
|
||||
bounds.x - bounds.width / 2.0
|
||||
}
|
||||
alignment::Horizontal::Right => bounds.x - bounds.width,
|
||||
};
|
||||
|
||||
bounds.y = match vertical_alignment {
|
||||
alignment::Vertical::Top => bounds.y,
|
||||
alignment::Vertical::Center => {
|
||||
bounds.y - bounds.height / 2.0
|
||||
}
|
||||
alignment::Vertical::Bottom => bounds.y - bounds.height,
|
||||
};
|
||||
|
||||
bounds.expand(1.5)
|
||||
}
|
||||
Self::Quad { bounds, .. }
|
||||
| Self::Image { bounds, .. }
|
||||
| Self::Svg { bounds, .. } => bounds.expand(1.0),
|
||||
Self::Clip { bounds, .. } => bounds.expand(1.0),
|
||||
Self::Group { primitives } => primitives
|
||||
.iter()
|
||||
.map(Self::bounds)
|
||||
.fold(Rectangle::with_size(Size::ZERO), |a, b| {
|
||||
Rectangle::union(&a, &b)
|
||||
}),
|
||||
Self::Translate {
|
||||
translation,
|
||||
content,
|
||||
} => content.bounds() + *translation,
|
||||
Self::Cache { content } => content.bounds(),
|
||||
Self::Custom(custom) => custom.bounds(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn regions<T: Damage>(a: &Primitive<T>, b: &Primitive<T>) -> Vec<Rectangle> {
|
||||
match (a, b) {
|
||||
(
|
||||
Primitive::Group {
|
||||
|
|
@ -76,7 +131,10 @@ pub fn regions(a: &Primitive, b: &Primitive) -> Vec<Rectangle> {
|
|||
}
|
||||
|
||||
/// Computes the damage regions between the two given lists of primitives.
|
||||
pub fn list(previous: &[Primitive], current: &[Primitive]) -> Vec<Rectangle> {
|
||||
pub fn list<T: Damage>(
|
||||
previous: &[Primitive<T>],
|
||||
current: &[Primitive<T>],
|
||||
) -> Vec<Rectangle> {
|
||||
let damage = previous
|
||||
.iter()
|
||||
.zip(current)
|
||||
|
|
@ -93,7 +151,7 @@ pub fn list(previous: &[Primitive], current: &[Primitive]) -> Vec<Rectangle> {
|
|||
|
||||
// Extend damage by the added/removed primitives
|
||||
damage
|
||||
.chain(bigger[smaller.len()..].iter().map(Primitive::bounds))
|
||||
.chain(bigger[smaller.len()..].iter().map(Damage::bounds))
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,20 +14,11 @@ pub use text::Text;
|
|||
|
||||
pub use crate::gradient::{self, Gradient};
|
||||
|
||||
use crate::Primitive;
|
||||
|
||||
/// A bunch of shapes that can be drawn.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Geometry(pub Primitive);
|
||||
|
||||
impl From<Geometry> for Primitive {
|
||||
fn from(geometry: Geometry) -> Self {
|
||||
geometry.0
|
||||
}
|
||||
}
|
||||
|
||||
/// A renderer capable of drawing some [`Geometry`].
|
||||
pub trait Renderer: crate::core::Renderer {
|
||||
/// The kind of geometry this renderer can draw.
|
||||
type Geometry;
|
||||
|
||||
/// Draws the given layers of [`Geometry`].
|
||||
fn draw(&mut self, layers: Vec<Geometry>);
|
||||
fn draw(&mut self, layers: Vec<Self::Geometry>);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ use crate::color;
|
|||
use crate::core::gradient::ColorStop;
|
||||
use crate::core::{self, Color, Point, Rectangle};
|
||||
|
||||
use bytemuck::{Pod, Zeroable};
|
||||
use half::f16;
|
||||
use std::cmp::Ordering;
|
||||
|
||||
|
|
@ -135,7 +136,7 @@ impl Linear {
|
|||
}
|
||||
|
||||
/// Packed [`Gradient`] data for use in shader code.
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Zeroable, Pod)]
|
||||
#[repr(C)]
|
||||
pub struct Packed {
|
||||
// 8 colors, each channel = 16 bit float, 2 colors packed into 1 u32
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@
|
|||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||
mod antialiasing;
|
||||
mod error;
|
||||
mod primitive;
|
||||
mod transformation;
|
||||
mod viewport;
|
||||
|
||||
|
|
@ -31,7 +32,7 @@ pub mod color;
|
|||
pub mod compositor;
|
||||
pub mod damage;
|
||||
pub mod gradient;
|
||||
pub mod primitive;
|
||||
pub mod mesh;
|
||||
pub mod renderer;
|
||||
|
||||
#[cfg(feature = "geometry")]
|
||||
|
|
@ -41,15 +42,15 @@ pub mod geometry;
|
|||
pub mod image;
|
||||
|
||||
pub use antialiasing::Antialiasing;
|
||||
pub use backend::Backend;
|
||||
pub use compositor::Compositor;
|
||||
pub use damage::Damage;
|
||||
pub use error::Error;
|
||||
pub use gradient::Gradient;
|
||||
pub use mesh::Mesh;
|
||||
pub use primitive::Primitive;
|
||||
pub use renderer::Renderer;
|
||||
pub use transformation::Transformation;
|
||||
pub use viewport::Viewport;
|
||||
|
||||
#[cfg(feature = "geometry")]
|
||||
pub use geometry::Geometry;
|
||||
|
||||
pub use iced_core as core;
|
||||
|
|
|
|||
76
graphics/src/mesh.rs
Normal file
76
graphics/src/mesh.rs
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
//! Draw triangles!
|
||||
use crate::color;
|
||||
use crate::core::{Rectangle, Size};
|
||||
use crate::gradient;
|
||||
use crate::Damage;
|
||||
|
||||
use bytemuck::{Pod, Zeroable};
|
||||
|
||||
/// A low-level primitive to render a mesh of triangles.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Mesh {
|
||||
/// A mesh with a solid color.
|
||||
Solid {
|
||||
/// The vertices and indices of the mesh.
|
||||
buffers: Indexed<SolidVertex2D>,
|
||||
|
||||
/// The size of the drawable region of the mesh.
|
||||
///
|
||||
/// Any geometry that falls out of this region will be clipped.
|
||||
size: Size,
|
||||
},
|
||||
/// A mesh with a gradient.
|
||||
Gradient {
|
||||
/// The vertices and indices of the mesh.
|
||||
buffers: Indexed<GradientVertex2D>,
|
||||
|
||||
/// The size of the drawable region of the mesh.
|
||||
///
|
||||
/// Any geometry that falls out of this region will be clipped.
|
||||
size: Size,
|
||||
},
|
||||
}
|
||||
|
||||
impl Damage for Mesh {
|
||||
fn bounds(&self) -> Rectangle {
|
||||
match self {
|
||||
Self::Solid { size, .. } | Self::Gradient { size, .. } => {
|
||||
Rectangle::with_size(*size)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A set of [`Vertex2D`] and indices representing a list of triangles.
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct Indexed<T> {
|
||||
/// The vertices of the mesh
|
||||
pub vertices: Vec<T>,
|
||||
|
||||
/// The list of vertex indices that defines the triangles of the mesh.
|
||||
///
|
||||
/// Therefore, this list should always have a length that is a multiple of 3.
|
||||
pub indices: Vec<u32>,
|
||||
}
|
||||
|
||||
/// A two-dimensional vertex with a color.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Zeroable, Pod)]
|
||||
#[repr(C)]
|
||||
pub struct SolidVertex2D {
|
||||
/// The vertex position in 2D space.
|
||||
pub position: [f32; 2],
|
||||
|
||||
/// The color of the vertex in __linear__ RGBA.
|
||||
pub color: color::Packed,
|
||||
}
|
||||
|
||||
/// A vertex which contains 2D position & packed gradient data.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Zeroable, Pod)]
|
||||
#[repr(C)]
|
||||
pub struct GradientVertex2D {
|
||||
/// The vertex position in 2D space.
|
||||
pub position: [f32; 2],
|
||||
|
||||
/// The packed vertex data of the gradient.
|
||||
pub gradient: gradient::Packed,
|
||||
}
|
||||
|
|
@ -1,19 +1,15 @@
|
|||
//! Draw using different graphical primitives.
|
||||
use crate::color;
|
||||
use crate::core::alignment;
|
||||
use crate::core::image;
|
||||
use crate::core::svg;
|
||||
use crate::core::text;
|
||||
use crate::core::{Background, Color, Font, Rectangle, Size, Vector};
|
||||
use crate::gradient;
|
||||
use crate::core::{Background, Color, Font, Rectangle, Vector};
|
||||
|
||||
use bytemuck::{Pod, Zeroable};
|
||||
use std::sync::Arc;
|
||||
|
||||
/// A rendering primitive.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[non_exhaustive]
|
||||
pub enum Primitive {
|
||||
pub enum Primitive<T> {
|
||||
/// A text primitive
|
||||
Text {
|
||||
/// The contents of the text
|
||||
|
|
@ -66,65 +62,17 @@ pub enum Primitive {
|
|||
/// The bounds of the viewport
|
||||
bounds: Rectangle,
|
||||
},
|
||||
/// A low-level primitive to render a mesh of triangles with a solid color.
|
||||
///
|
||||
/// It can be used to render many kinds of geometry freely.
|
||||
SolidMesh {
|
||||
/// The vertices and indices of the mesh.
|
||||
buffers: Mesh2D<ColoredVertex2D>,
|
||||
|
||||
/// The size of the drawable region of the mesh.
|
||||
///
|
||||
/// Any geometry that falls out of this region will be clipped.
|
||||
size: Size,
|
||||
},
|
||||
/// A low-level primitive to render a mesh of triangles with a gradient.
|
||||
///
|
||||
/// It can be used to render many kinds of geometry freely.
|
||||
GradientMesh {
|
||||
/// The vertices and indices of the mesh.
|
||||
buffers: Mesh2D<GradientVertex2D>,
|
||||
|
||||
/// The size of the drawable region of the mesh.
|
||||
///
|
||||
/// Any geometry that falls out of this region will be clipped.
|
||||
size: Size,
|
||||
},
|
||||
/// A [`tiny_skia`] path filled with some paint.
|
||||
#[cfg(feature = "tiny-skia")]
|
||||
Fill {
|
||||
/// The path to fill.
|
||||
path: tiny_skia::Path,
|
||||
/// The paint to use.
|
||||
paint: tiny_skia::Paint<'static>,
|
||||
/// The fill rule to follow.
|
||||
rule: tiny_skia::FillRule,
|
||||
/// The transform to apply to the path.
|
||||
transform: tiny_skia::Transform,
|
||||
},
|
||||
/// A [`tiny_skia`] path stroked with some paint.
|
||||
#[cfg(feature = "tiny-skia")]
|
||||
Stroke {
|
||||
/// The path to stroke.
|
||||
path: tiny_skia::Path,
|
||||
/// The paint to use.
|
||||
paint: tiny_skia::Paint<'static>,
|
||||
/// The stroke settings.
|
||||
stroke: tiny_skia::Stroke,
|
||||
/// The transform to apply to the path.
|
||||
transform: tiny_skia::Transform,
|
||||
},
|
||||
/// A group of primitives
|
||||
Group {
|
||||
/// The primitives of the group
|
||||
primitives: Vec<Primitive>,
|
||||
primitives: Vec<Primitive<T>>,
|
||||
},
|
||||
/// A clip primitive
|
||||
Clip {
|
||||
/// The bounds of the clip
|
||||
bounds: Rectangle,
|
||||
/// The content of the clip
|
||||
content: Box<Primitive>,
|
||||
content: Box<Primitive<T>>,
|
||||
},
|
||||
/// A primitive that applies a translation
|
||||
Translate {
|
||||
|
|
@ -132,7 +80,7 @@ pub enum Primitive {
|
|||
translation: Vector,
|
||||
|
||||
/// The primitive to translate
|
||||
content: Box<Primitive>,
|
||||
content: Box<Primitive<T>>,
|
||||
},
|
||||
/// A cached primitive.
|
||||
///
|
||||
|
|
@ -140,11 +88,13 @@ pub enum Primitive {
|
|||
/// generation is expensive.
|
||||
Cache {
|
||||
/// The cached primitive
|
||||
content: Arc<Primitive>,
|
||||
content: Arc<Primitive<T>>,
|
||||
},
|
||||
/// A backend-specific primitive.
|
||||
Custom(T),
|
||||
}
|
||||
|
||||
impl Primitive {
|
||||
impl<T> Primitive<T> {
|
||||
/// Creates a [`Primitive::Group`].
|
||||
pub fn group(primitives: Vec<Self>) -> Self {
|
||||
Self::Group { primitives }
|
||||
|
|
@ -165,112 +115,4 @@ impl Primitive {
|
|||
content: Box::new(self),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the bounds of the [`Primitive`].
|
||||
pub fn bounds(&self) -> Rectangle {
|
||||
match self {
|
||||
Self::Text {
|
||||
bounds,
|
||||
horizontal_alignment,
|
||||
vertical_alignment,
|
||||
..
|
||||
} => {
|
||||
let mut bounds = *bounds;
|
||||
|
||||
bounds.x = match horizontal_alignment {
|
||||
alignment::Horizontal::Left => bounds.x,
|
||||
alignment::Horizontal::Center => {
|
||||
bounds.x - bounds.width / 2.0
|
||||
}
|
||||
alignment::Horizontal::Right => bounds.x - bounds.width,
|
||||
};
|
||||
|
||||
bounds.y = match vertical_alignment {
|
||||
alignment::Vertical::Top => bounds.y,
|
||||
alignment::Vertical::Center => {
|
||||
bounds.y - bounds.height / 2.0
|
||||
}
|
||||
alignment::Vertical::Bottom => bounds.y - bounds.height,
|
||||
};
|
||||
|
||||
bounds.expand(1.5)
|
||||
}
|
||||
Self::Quad { bounds, .. }
|
||||
| Self::Image { bounds, .. }
|
||||
| Self::Svg { bounds, .. } => bounds.expand(1.0),
|
||||
Self::Clip { bounds, .. } => bounds.expand(1.0),
|
||||
Self::SolidMesh { size, .. } | Self::GradientMesh { size, .. } => {
|
||||
Rectangle::with_size(*size)
|
||||
}
|
||||
#[cfg(feature = "tiny-skia")]
|
||||
Self::Fill { path, .. } | Self::Stroke { path, .. } => {
|
||||
let bounds = path.bounds();
|
||||
|
||||
Rectangle {
|
||||
x: bounds.x(),
|
||||
y: bounds.y(),
|
||||
width: bounds.width(),
|
||||
height: bounds.height(),
|
||||
}
|
||||
.expand(1.0)
|
||||
}
|
||||
Self::Group { primitives } => primitives
|
||||
.iter()
|
||||
.map(Self::bounds)
|
||||
.fold(Rectangle::with_size(Size::ZERO), |a, b| {
|
||||
Rectangle::union(&a, &b)
|
||||
}),
|
||||
Self::Translate {
|
||||
translation,
|
||||
content,
|
||||
} => content.bounds() + *translation,
|
||||
Self::Cache { content } => content.bounds(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A set of [`Vertex2D`] and indices representing a list of triangles.
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct Mesh2D<T> {
|
||||
/// The vertices of the mesh
|
||||
pub vertices: Vec<T>,
|
||||
|
||||
/// The list of vertex indices that defines the triangles of the mesh.
|
||||
///
|
||||
/// Therefore, this list should always have a length that is a multiple of 3.
|
||||
pub indices: Vec<u32>,
|
||||
}
|
||||
|
||||
/// A two-dimensional vertex with a color.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Zeroable, Pod)]
|
||||
#[repr(C)]
|
||||
pub struct ColoredVertex2D {
|
||||
/// The vertex position in 2D space.
|
||||
pub position: [f32; 2],
|
||||
|
||||
/// The color of the vertex in __linear__ RGBA.
|
||||
pub color: color::Packed,
|
||||
}
|
||||
|
||||
/// A vertex which contains 2D position & packed gradient data.
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
#[repr(C)]
|
||||
pub struct GradientVertex2D {
|
||||
/// The vertex position in 2D space.
|
||||
pub position: [f32; 2],
|
||||
|
||||
/// The packed vertex data of the gradient.
|
||||
pub gradient: gradient::Packed,
|
||||
}
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
unsafe impl Zeroable for GradientVertex2D {}
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
unsafe impl Pod for GradientVertex2D {}
|
||||
|
||||
impl From<()> for Primitive {
|
||||
fn from(_: ()) -> Self {
|
||||
Self::Group { primitives: vec![] }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,28 +1,25 @@
|
|||
//! Create a renderer from a [`Backend`].
|
||||
use crate::backend;
|
||||
use crate::backend::{self, Backend};
|
||||
use crate::Primitive;
|
||||
|
||||
use iced_core::image;
|
||||
use iced_core::layout;
|
||||
use iced_core::renderer;
|
||||
use iced_core::svg;
|
||||
use iced_core::text::{self, Text};
|
||||
use iced_core::{
|
||||
Background, Color, Element, Font, Point, Rectangle, Size, Vector,
|
||||
};
|
||||
use iced_core::{Background, Color, Font, Point, Rectangle, Size, Vector};
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
/// A backend-agnostic renderer that supports all the built-in widgets.
|
||||
#[derive(Debug)]
|
||||
pub struct Renderer<B, Theme> {
|
||||
pub struct Renderer<B: Backend, Theme> {
|
||||
backend: B,
|
||||
primitives: Vec<Primitive>,
|
||||
primitives: Vec<Primitive<B::Primitive>>,
|
||||
theme: PhantomData<Theme>,
|
||||
}
|
||||
|
||||
impl<B, T> Renderer<B, T> {
|
||||
impl<B: Backend, T> Renderer<B, T> {
|
||||
/// Creates a new [`Renderer`] from the given [`Backend`].
|
||||
pub fn new(backend: B) -> Self {
|
||||
Self {
|
||||
|
|
@ -38,7 +35,7 @@ impl<B, T> Renderer<B, T> {
|
|||
}
|
||||
|
||||
/// Enqueues the given [`Primitive`] in the [`Renderer`] for drawing.
|
||||
pub fn draw_primitive(&mut self, primitive: Primitive) {
|
||||
pub fn draw_primitive(&mut self, primitive: Primitive<B::Primitive>) {
|
||||
self.primitives.push(primitive);
|
||||
}
|
||||
|
||||
|
|
@ -46,31 +43,54 @@ impl<B, T> Renderer<B, T> {
|
|||
/// of the [`Renderer`].
|
||||
pub fn with_primitives<O>(
|
||||
&mut self,
|
||||
f: impl FnOnce(&mut B, &[Primitive]) -> O,
|
||||
f: impl FnOnce(&mut B, &[Primitive<B::Primitive>]) -> O,
|
||||
) -> O {
|
||||
f(&mut self.backend, &self.primitives)
|
||||
}
|
||||
}
|
||||
|
||||
impl<B, T> iced_core::Renderer for Renderer<B, T> {
|
||||
type Theme = T;
|
||||
|
||||
fn layout<Message>(
|
||||
&mut self,
|
||||
element: &Element<'_, Message, Self>,
|
||||
limits: &layout::Limits,
|
||||
) -> layout::Node {
|
||||
element.as_widget().layout(self, limits)
|
||||
/// Starts recording a new layer.
|
||||
pub fn start_layer(&mut self) -> Vec<Primitive<B::Primitive>> {
|
||||
std::mem::take(&mut self.primitives)
|
||||
}
|
||||
|
||||
/// Ends the recording of a layer.
|
||||
pub fn end_layer(
|
||||
&mut self,
|
||||
primitives: Vec<Primitive<B::Primitive>>,
|
||||
bounds: Rectangle,
|
||||
) {
|
||||
let layer = std::mem::replace(&mut self.primitives, primitives);
|
||||
|
||||
self.primitives.push(Primitive::group(layer).clip(bounds));
|
||||
}
|
||||
|
||||
/// Starts recording a translation.
|
||||
pub fn start_translation(&mut self) -> Vec<Primitive<B::Primitive>> {
|
||||
std::mem::take(&mut self.primitives)
|
||||
}
|
||||
|
||||
/// Ends the recording of a translation.
|
||||
pub fn end_translation(
|
||||
&mut self,
|
||||
primitives: Vec<Primitive<B::Primitive>>,
|
||||
translation: Vector,
|
||||
) {
|
||||
let layer = std::mem::replace(&mut self.primitives, primitives);
|
||||
|
||||
self.primitives
|
||||
.push(Primitive::group(layer).translate(translation));
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: Backend, T> iced_core::Renderer for Renderer<B, T> {
|
||||
type Theme = T;
|
||||
|
||||
fn with_layer(&mut self, bounds: Rectangle, f: impl FnOnce(&mut Self)) {
|
||||
let current = std::mem::take(&mut self.primitives);
|
||||
let current = self.start_layer();
|
||||
|
||||
f(self);
|
||||
|
||||
let layer = std::mem::replace(&mut self.primitives, current);
|
||||
|
||||
self.primitives.push(Primitive::group(layer).clip(bounds));
|
||||
self.end_layer(current, bounds);
|
||||
}
|
||||
|
||||
fn with_translation(
|
||||
|
|
@ -78,14 +98,11 @@ impl<B, T> iced_core::Renderer for Renderer<B, T> {
|
|||
translation: Vector,
|
||||
f: impl FnOnce(&mut Self),
|
||||
) {
|
||||
let current = std::mem::take(&mut self.primitives);
|
||||
let current = self.start_translation();
|
||||
|
||||
f(self);
|
||||
|
||||
let layer = std::mem::replace(&mut self.primitives, current);
|
||||
|
||||
self.primitives
|
||||
.push(Primitive::group(layer).translate(translation));
|
||||
self.end_translation(current, translation);
|
||||
}
|
||||
|
||||
fn fill_quad(
|
||||
|
|
@ -109,7 +126,7 @@ impl<B, T> iced_core::Renderer for Renderer<B, T> {
|
|||
|
||||
impl<B, T> text::Renderer for Renderer<B, T>
|
||||
where
|
||||
B: backend::Text,
|
||||
B: Backend + backend::Text,
|
||||
{
|
||||
type Font = Font;
|
||||
|
||||
|
|
@ -188,7 +205,7 @@ where
|
|||
|
||||
impl<B, T> image::Renderer for Renderer<B, T>
|
||||
where
|
||||
B: backend::Image,
|
||||
B: Backend + backend::Image,
|
||||
{
|
||||
type Handle = image::Handle;
|
||||
|
||||
|
|
@ -203,7 +220,7 @@ where
|
|||
|
||||
impl<B, T> svg::Renderer for Renderer<B, T>
|
||||
where
|
||||
B: backend::Svg,
|
||||
B: Backend + backend::Svg,
|
||||
{
|
||||
fn dimensions(&self, handle: &svg::Handle) -> Size<u32> {
|
||||
self.backend().viewport_dimensions(handle)
|
||||
|
|
@ -222,11 +239,3 @@ where
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "geometry")]
|
||||
impl<B, T> crate::geometry::Renderer for Renderer<B, T> {
|
||||
fn draw(&mut self, layers: Vec<crate::Geometry>) {
|
||||
self.primitives
|
||||
.extend(layers.into_iter().map(crate::Geometry::into));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ web-colors = ["iced_wgpu?/web-colors"]
|
|||
[dependencies]
|
||||
raw-window-handle = "0.5"
|
||||
thiserror = "1"
|
||||
log = "0.4"
|
||||
|
||||
[dependencies.iced_graphics]
|
||||
version = "0.8"
|
||||
|
|
|
|||
|
|
@ -100,26 +100,28 @@ impl<Theme> crate::graphics::Compositor for Compositor<Theme> {
|
|||
background_color: Color,
|
||||
overlay: &[T],
|
||||
) -> Result<(), SurfaceError> {
|
||||
renderer.with_primitives(|backend, primitives| {
|
||||
match (self, backend, surface) {
|
||||
(
|
||||
Self::TinySkia(_compositor),
|
||||
crate::Backend::TinySkia(backend),
|
||||
Surface::TinySkia(surface),
|
||||
) => iced_tiny_skia::window::compositor::present(
|
||||
match (self, renderer, surface) {
|
||||
(
|
||||
Self::TinySkia(_compositor),
|
||||
crate::Renderer::TinySkia(renderer),
|
||||
Surface::TinySkia(surface),
|
||||
) => renderer.with_primitives(|backend, primitives| {
|
||||
iced_tiny_skia::window::compositor::present(
|
||||
backend,
|
||||
surface,
|
||||
primitives,
|
||||
viewport,
|
||||
background_color,
|
||||
overlay,
|
||||
),
|
||||
#[cfg(feature = "wgpu")]
|
||||
(
|
||||
Self::Wgpu(compositor),
|
||||
crate::Backend::Wgpu(backend),
|
||||
Surface::Wgpu(surface),
|
||||
) => iced_wgpu::window::compositor::present(
|
||||
)
|
||||
}),
|
||||
#[cfg(feature = "wgpu")]
|
||||
(
|
||||
Self::Wgpu(compositor),
|
||||
crate::Renderer::Wgpu(renderer),
|
||||
Surface::Wgpu(surface),
|
||||
) => renderer.with_primitives(|backend, primitives| {
|
||||
iced_wgpu::window::compositor::present(
|
||||
compositor,
|
||||
backend,
|
||||
surface,
|
||||
|
|
@ -127,14 +129,14 @@ impl<Theme> crate::graphics::Compositor for Compositor<Theme> {
|
|||
viewport,
|
||||
background_color,
|
||||
overlay,
|
||||
),
|
||||
#[allow(unreachable_patterns)]
|
||||
_ => panic!(
|
||||
"The provided renderer or surface are not compatible \
|
||||
)
|
||||
}),
|
||||
#[allow(unreachable_patterns)]
|
||||
_ => panic!(
|
||||
"The provided renderer or surface are not compatible \
|
||||
with the compositor."
|
||||
),
|
||||
}
|
||||
})
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
fn screenshot<T: AsRef<str>>(
|
||||
|
|
@ -145,12 +147,27 @@ impl<Theme> crate::graphics::Compositor for Compositor<Theme> {
|
|||
background_color: Color,
|
||||
overlay: &[T],
|
||||
) -> Vec<u8> {
|
||||
renderer.with_primitives(|backend, primitives| match (self, backend, surface) {
|
||||
(Self::TinySkia(_compositor), crate::Backend::TinySkia(backend), Surface::TinySkia(surface)) => {
|
||||
iced_tiny_skia::window::compositor::screenshot(surface, backend, primitives, viewport, background_color, overlay)
|
||||
},
|
||||
match (self, renderer, surface) {
|
||||
(
|
||||
Self::TinySkia(_compositor),
|
||||
Renderer::TinySkia(renderer),
|
||||
Surface::TinySkia(surface),
|
||||
) => renderer.with_primitives(|backend, primitives| {
|
||||
iced_tiny_skia::window::compositor::screenshot(
|
||||
surface,
|
||||
backend,
|
||||
primitives,
|
||||
viewport,
|
||||
background_color,
|
||||
overlay,
|
||||
)
|
||||
}),
|
||||
#[cfg(feature = "wgpu")]
|
||||
(Self::Wgpu(compositor), crate::Backend::Wgpu(backend), Surface::Wgpu(_)) => {
|
||||
(
|
||||
Self::Wgpu(compositor),
|
||||
Renderer::Wgpu(renderer),
|
||||
Surface::Wgpu(_),
|
||||
) => renderer.with_primitives(|backend, primitives| {
|
||||
iced_wgpu::window::compositor::screenshot(
|
||||
compositor,
|
||||
backend,
|
||||
|
|
@ -159,12 +176,13 @@ impl<Theme> crate::graphics::Compositor for Compositor<Theme> {
|
|||
background_color,
|
||||
overlay,
|
||||
)
|
||||
},
|
||||
}),
|
||||
#[allow(unreachable_patterns)]
|
||||
_ => panic!(
|
||||
"The provided renderer or backend are not compatible with the compositor."
|
||||
"The provided renderer or backend are not compatible \
|
||||
with the compositor."
|
||||
),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -215,7 +233,7 @@ impl Candidate {
|
|||
|
||||
Ok((
|
||||
Compositor::TinySkia(compositor),
|
||||
Renderer::new(crate::Backend::TinySkia(backend)),
|
||||
Renderer::TinySkia(iced_tiny_skia::Renderer::new(backend)),
|
||||
))
|
||||
}
|
||||
#[cfg(feature = "wgpu")]
|
||||
|
|
@ -232,7 +250,7 @@ impl Candidate {
|
|||
|
||||
Ok((
|
||||
Compositor::Wgpu(compositor),
|
||||
Renderer::new(crate::Backend::Wgpu(backend)),
|
||||
Renderer::Wgpu(iced_wgpu::Renderer::new(backend)),
|
||||
))
|
||||
}
|
||||
#[cfg(not(feature = "wgpu"))]
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@ mod cache;
|
|||
pub use cache::Cache;
|
||||
|
||||
use crate::core::{Point, Rectangle, Size, Vector};
|
||||
use crate::graphics::geometry::{Fill, Geometry, Path, Stroke, Text};
|
||||
use crate::Backend;
|
||||
use crate::graphics::geometry::{Fill, Path, Stroke, Text};
|
||||
use crate::Renderer;
|
||||
|
||||
pub enum Frame {
|
||||
TinySkia(iced_tiny_skia::geometry::Frame),
|
||||
|
|
@ -12,6 +12,12 @@ pub enum Frame {
|
|||
Wgpu(iced_wgpu::geometry::Frame),
|
||||
}
|
||||
|
||||
pub enum Geometry {
|
||||
TinySkia(iced_tiny_skia::Primitive),
|
||||
#[cfg(feature = "wgpu")]
|
||||
Wgpu(iced_wgpu::Primitive),
|
||||
}
|
||||
|
||||
macro_rules! delegate {
|
||||
($frame:expr, $name:ident, $body:expr) => {
|
||||
match $frame {
|
||||
|
|
@ -23,13 +29,13 @@ macro_rules! delegate {
|
|||
}
|
||||
|
||||
impl Frame {
|
||||
pub fn new<Theme>(renderer: &crate::Renderer<Theme>, size: Size) -> Self {
|
||||
match renderer.backend() {
|
||||
Backend::TinySkia(_) => {
|
||||
pub fn new<Theme>(renderer: &Renderer<Theme>, size: Size) -> Self {
|
||||
match renderer {
|
||||
Renderer::TinySkia(_) => {
|
||||
Frame::TinySkia(iced_tiny_skia::geometry::Frame::new(size))
|
||||
}
|
||||
#[cfg(feature = "wgpu")]
|
||||
Backend::Wgpu(_) => {
|
||||
Renderer::Wgpu(_) => {
|
||||
Frame::Wgpu(iced_wgpu::geometry::Frame::new(size))
|
||||
}
|
||||
}
|
||||
|
|
@ -169,6 +175,10 @@ impl Frame {
|
|||
}
|
||||
|
||||
pub fn into_geometry(self) -> Geometry {
|
||||
Geometry(delegate!(self, frame, frame.into_primitive()))
|
||||
match self {
|
||||
Self::TinySkia(frame) => Geometry::TinySkia(frame.into_primitive()),
|
||||
#[cfg(feature = "wgpu")]
|
||||
Self::Wgpu(frame) => Geometry::Wgpu(frame.into_primitive()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
use crate::core::Size;
|
||||
use crate::geometry::{Frame, Geometry};
|
||||
use crate::graphics::Primitive;
|
||||
use crate::Renderer;
|
||||
|
||||
use std::cell::RefCell;
|
||||
|
|
@ -21,10 +20,17 @@ enum State {
|
|||
Empty,
|
||||
Filled {
|
||||
bounds: Size,
|
||||
primitive: Arc<Primitive>,
|
||||
primitive: Internal,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
enum Internal {
|
||||
TinySkia(Arc<iced_tiny_skia::Primitive>),
|
||||
#[cfg(feature = "wgpu")]
|
||||
Wgpu(Arc<iced_wgpu::Primitive>),
|
||||
}
|
||||
|
||||
impl Cache {
|
||||
/// Creates a new empty [`Cache`].
|
||||
pub fn new() -> Self {
|
||||
|
|
@ -62,9 +68,21 @@ impl Cache {
|
|||
} = self.state.borrow().deref()
|
||||
{
|
||||
if *cached_bounds == bounds {
|
||||
return Geometry(Primitive::Cache {
|
||||
content: primitive.clone(),
|
||||
});
|
||||
match primitive {
|
||||
Internal::TinySkia(primitive) => {
|
||||
return Geometry::TinySkia(
|
||||
iced_tiny_skia::Primitive::Cache {
|
||||
content: primitive.clone(),
|
||||
},
|
||||
);
|
||||
}
|
||||
#[cfg(feature = "wgpu")]
|
||||
Internal::Wgpu(primitive) => {
|
||||
return Geometry::Wgpu(iced_wgpu::Primitive::Cache {
|
||||
content: primitive.clone(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -74,7 +92,15 @@ impl Cache {
|
|||
let primitive = {
|
||||
let geometry = frame.into_geometry();
|
||||
|
||||
Arc::new(geometry.0)
|
||||
match geometry {
|
||||
Geometry::TinySkia(primitive) => {
|
||||
Internal::TinySkia(Arc::new(primitive))
|
||||
}
|
||||
#[cfg(feature = "wgpu")]
|
||||
Geometry::Wgpu(primitive) => {
|
||||
Internal::Wgpu(Arc::new(primitive))
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
*self.state.borrow_mut() = State::Filled {
|
||||
|
|
@ -82,6 +108,18 @@ impl Cache {
|
|||
primitive: primitive.clone(),
|
||||
};
|
||||
|
||||
Geometry(Primitive::Cache { content: primitive })
|
||||
match primitive {
|
||||
Internal::TinySkia(primitive) => {
|
||||
Geometry::TinySkia(iced_tiny_skia::Primitive::Cache {
|
||||
content: primitive,
|
||||
})
|
||||
}
|
||||
#[cfg(feature = "wgpu")]
|
||||
Internal::Wgpu(primitive) => {
|
||||
Geometry::Wgpu(iced_wgpu::Primitive::Cache {
|
||||
content: primitive,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,17 +3,267 @@ pub mod compositor;
|
|||
#[cfg(feature = "geometry")]
|
||||
pub mod geometry;
|
||||
|
||||
mod backend;
|
||||
mod settings;
|
||||
|
||||
pub use iced_graphics as graphics;
|
||||
pub use iced_graphics::core;
|
||||
|
||||
pub use backend::Backend;
|
||||
pub use compositor::Compositor;
|
||||
pub use settings::Settings;
|
||||
|
||||
#[cfg(feature = "geometry")]
|
||||
pub use geometry::Geometry;
|
||||
|
||||
use crate::core::renderer;
|
||||
use crate::core::text::{self, Text};
|
||||
use crate::core::{Background, Font, Point, Rectangle, Size, Vector};
|
||||
use crate::graphics::Mesh;
|
||||
|
||||
use std::borrow::Cow;
|
||||
|
||||
/// The default graphics renderer for [`iced`].
|
||||
///
|
||||
/// [`iced`]: https://github.com/iced-rs/iced
|
||||
pub type Renderer<Theme> = iced_graphics::Renderer<Backend, Theme>;
|
||||
pub enum Renderer<Theme> {
|
||||
TinySkia(iced_tiny_skia::Renderer<Theme>),
|
||||
#[cfg(feature = "wgpu")]
|
||||
Wgpu(iced_wgpu::Renderer<Theme>),
|
||||
}
|
||||
|
||||
macro_rules! delegate {
|
||||
($renderer:expr, $name:ident, $body:expr) => {
|
||||
match $renderer {
|
||||
Self::TinySkia($name) => $body,
|
||||
#[cfg(feature = "wgpu")]
|
||||
Self::Wgpu($name) => $body,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl<T> Renderer<T> {
|
||||
pub fn draw_mesh(&mut self, mesh: Mesh) {
|
||||
match self {
|
||||
Self::TinySkia(_) => {
|
||||
log::warn!("Unsupported mesh primitive: {:?}", mesh)
|
||||
}
|
||||
#[cfg(feature = "wgpu")]
|
||||
Self::Wgpu(renderer) => {
|
||||
renderer.draw_primitive(iced_wgpu::Primitive::Custom(
|
||||
iced_wgpu::primitive::Custom::Mesh(mesh),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> core::Renderer for Renderer<T> {
|
||||
type Theme = T;
|
||||
|
||||
fn with_layer(&mut self, bounds: Rectangle, f: impl FnOnce(&mut Self)) {
|
||||
match self {
|
||||
Self::TinySkia(renderer) => {
|
||||
let primitives = renderer.start_layer();
|
||||
|
||||
f(self);
|
||||
|
||||
match self {
|
||||
Self::TinySkia(renderer) => {
|
||||
renderer.end_layer(primitives, bounds);
|
||||
}
|
||||
#[cfg(feature = "wgpu")]
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "wgpu")]
|
||||
Self::Wgpu(renderer) => {
|
||||
let primitives = renderer.start_layer();
|
||||
|
||||
f(self);
|
||||
|
||||
match self {
|
||||
#[cfg(feature = "wgpu")]
|
||||
Self::Wgpu(renderer) => {
|
||||
renderer.end_layer(primitives, bounds);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn with_translation(
|
||||
&mut self,
|
||||
translation: Vector,
|
||||
f: impl FnOnce(&mut Self),
|
||||
) {
|
||||
match self {
|
||||
Self::TinySkia(renderer) => {
|
||||
let primitives = renderer.start_translation();
|
||||
|
||||
f(self);
|
||||
|
||||
match self {
|
||||
Self::TinySkia(renderer) => {
|
||||
renderer.end_translation(primitives, translation);
|
||||
}
|
||||
#[cfg(feature = "wgpu")]
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "wgpu")]
|
||||
Self::Wgpu(renderer) => {
|
||||
let primitives = renderer.start_translation();
|
||||
|
||||
f(self);
|
||||
|
||||
match self {
|
||||
#[cfg(feature = "wgpu")]
|
||||
Self::Wgpu(renderer) => {
|
||||
renderer.end_translation(primitives, translation);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn fill_quad(
|
||||
&mut self,
|
||||
quad: renderer::Quad,
|
||||
background: impl Into<Background>,
|
||||
) {
|
||||
delegate!(self, renderer, renderer.fill_quad(quad, background));
|
||||
}
|
||||
|
||||
fn clear(&mut self) {
|
||||
delegate!(self, renderer, renderer.clear());
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> text::Renderer for Renderer<T> {
|
||||
type Font = Font;
|
||||
|
||||
const ICON_FONT: Font = iced_tiny_skia::Renderer::<T>::ICON_FONT;
|
||||
const CHECKMARK_ICON: char = iced_tiny_skia::Renderer::<T>::CHECKMARK_ICON;
|
||||
const ARROW_DOWN_ICON: char =
|
||||
iced_tiny_skia::Renderer::<T>::ARROW_DOWN_ICON;
|
||||
|
||||
fn default_font(&self) -> Self::Font {
|
||||
delegate!(self, renderer, renderer.default_font())
|
||||
}
|
||||
|
||||
fn default_size(&self) -> f32 {
|
||||
delegate!(self, renderer, renderer.default_size())
|
||||
}
|
||||
|
||||
fn measure(
|
||||
&self,
|
||||
content: &str,
|
||||
size: f32,
|
||||
line_height: text::LineHeight,
|
||||
font: Font,
|
||||
bounds: Size,
|
||||
shaping: text::Shaping,
|
||||
) -> Size {
|
||||
delegate!(
|
||||
self,
|
||||
renderer,
|
||||
renderer.measure(content, size, line_height, font, bounds, shaping)
|
||||
)
|
||||
}
|
||||
|
||||
fn hit_test(
|
||||
&self,
|
||||
content: &str,
|
||||
size: f32,
|
||||
line_height: text::LineHeight,
|
||||
font: Font,
|
||||
bounds: Size,
|
||||
shaping: text::Shaping,
|
||||
point: Point,
|
||||
nearest_only: bool,
|
||||
) -> Option<text::Hit> {
|
||||
delegate!(
|
||||
self,
|
||||
renderer,
|
||||
renderer.hit_test(
|
||||
content,
|
||||
size,
|
||||
line_height,
|
||||
font,
|
||||
bounds,
|
||||
shaping,
|
||||
point,
|
||||
nearest_only
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fn load_font(&mut self, bytes: Cow<'static, [u8]>) {
|
||||
delegate!(self, renderer, renderer.load_font(bytes));
|
||||
}
|
||||
|
||||
fn fill_text(&mut self, text: Text<'_, Self::Font>) {
|
||||
delegate!(self, renderer, renderer.fill_text(text));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "image")]
|
||||
impl<T> crate::core::image::Renderer for Renderer<T> {
|
||||
type Handle = crate::core::image::Handle;
|
||||
|
||||
fn dimensions(&self, handle: &crate::core::image::Handle) -> Size<u32> {
|
||||
delegate!(self, renderer, renderer.dimensions(handle))
|
||||
}
|
||||
|
||||
fn draw(&mut self, handle: crate::core::image::Handle, bounds: Rectangle) {
|
||||
delegate!(self, renderer, renderer.draw(handle, bounds));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "svg")]
|
||||
impl<T> crate::core::svg::Renderer for Renderer<T> {
|
||||
fn dimensions(&self, handle: &crate::core::svg::Handle) -> Size<u32> {
|
||||
delegate!(self, renderer, renderer.dimensions(handle))
|
||||
}
|
||||
|
||||
fn draw(
|
||||
&mut self,
|
||||
handle: crate::core::svg::Handle,
|
||||
color: Option<crate::core::Color>,
|
||||
bounds: Rectangle,
|
||||
) {
|
||||
delegate!(self, renderer, renderer.draw(handle, color, bounds))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "geometry")]
|
||||
impl<T> crate::graphics::geometry::Renderer for Renderer<T> {
|
||||
type Geometry = crate::Geometry;
|
||||
|
||||
fn draw(&mut self, layers: Vec<Self::Geometry>) {
|
||||
match self {
|
||||
Self::TinySkia(renderer) => {
|
||||
for layer in layers {
|
||||
match layer {
|
||||
crate::Geometry::TinySkia(primitive) => {
|
||||
renderer.draw_primitive(primitive);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "wgpu")]
|
||||
Self::Wgpu(renderer) => {
|
||||
for layer in layers {
|
||||
match layer {
|
||||
crate::Geometry::Wgpu(primitive) => {
|
||||
renderer.draw_primitive(primitive);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -95,8 +95,9 @@ where
|
|||
let Cache { mut state } = cache;
|
||||
state.diff(root.as_widget());
|
||||
|
||||
let base =
|
||||
renderer.layout(&root, &layout::Limits::new(Size::ZERO, bounds));
|
||||
let base = root
|
||||
.as_widget()
|
||||
.layout(renderer, &layout::Limits::new(Size::ZERO, bounds));
|
||||
|
||||
UserInterface {
|
||||
root,
|
||||
|
|
@ -226,8 +227,8 @@ where
|
|||
if shell.is_layout_invalid() {
|
||||
let _ = ManuallyDrop::into_inner(manual_overlay);
|
||||
|
||||
self.base = renderer.layout(
|
||||
&self.root,
|
||||
self.base = self.root.as_widget().layout(
|
||||
renderer,
|
||||
&layout::Limits::new(Size::ZERO, self.bounds),
|
||||
);
|
||||
|
||||
|
|
@ -322,8 +323,8 @@ where
|
|||
}
|
||||
|
||||
shell.revalidate_layout(|| {
|
||||
self.base = renderer.layout(
|
||||
&self.root,
|
||||
self.base = self.root.as_widget().layout(
|
||||
renderer,
|
||||
&layout::Limits::new(Size::ZERO, self.bounds),
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ log = "0.4"
|
|||
[dependencies.iced_graphics]
|
||||
version = "0.8"
|
||||
path = "../graphics"
|
||||
features = ["tiny-skia"]
|
||||
|
||||
[dependencies.cosmic-text]
|
||||
git = "https://github.com/hecrj/cosmic-text.git"
|
||||
|
|
|
|||
|
|
@ -2,7 +2,8 @@ use crate::core::text;
|
|||
use crate::core::Gradient;
|
||||
use crate::core::{Background, Color, Font, Point, Rectangle, Size, Vector};
|
||||
use crate::graphics::backend;
|
||||
use crate::graphics::{Primitive, Viewport};
|
||||
use crate::graphics::{Damage, Viewport};
|
||||
use crate::primitive::{self, Primitive};
|
||||
use crate::Settings;
|
||||
|
||||
use std::borrow::Cow;
|
||||
|
|
@ -419,6 +420,13 @@ impl Backend {
|
|||
self.raster_pipeline
|
||||
.draw(handle, *bounds, pixels, transform, clip_mask);
|
||||
}
|
||||
#[cfg(not(feature = "image"))]
|
||||
Primitive::Image { .. } => {
|
||||
log::warn!(
|
||||
"Unsupported primitive in `iced_tiny_skia`: {:?}",
|
||||
primitive
|
||||
);
|
||||
}
|
||||
#[cfg(feature = "svg")]
|
||||
Primitive::Svg {
|
||||
handle,
|
||||
|
|
@ -442,12 +450,19 @@ impl Backend {
|
|||
clip_mask,
|
||||
);
|
||||
}
|
||||
Primitive::Fill {
|
||||
#[cfg(not(feature = "svg"))]
|
||||
Primitive::Svg { .. } => {
|
||||
log::warn!(
|
||||
"Unsupported primitive in `iced_tiny_skia`: {:?}",
|
||||
primitive
|
||||
);
|
||||
}
|
||||
Primitive::Custom(primitive::Custom::Fill {
|
||||
path,
|
||||
paint,
|
||||
rule,
|
||||
transform,
|
||||
} => {
|
||||
}) => {
|
||||
let bounds = path.bounds();
|
||||
|
||||
let physical_bounds = (Rectangle {
|
||||
|
|
@ -475,12 +490,12 @@ impl Backend {
|
|||
clip_mask,
|
||||
);
|
||||
}
|
||||
Primitive::Stroke {
|
||||
Primitive::Custom(primitive::Custom::Stroke {
|
||||
path,
|
||||
paint,
|
||||
stroke,
|
||||
transform,
|
||||
} => {
|
||||
}) => {
|
||||
let bounds = path.bounds();
|
||||
|
||||
let physical_bounds = (Rectangle {
|
||||
|
|
@ -580,21 +595,6 @@ impl Backend {
|
|||
translation,
|
||||
);
|
||||
}
|
||||
Primitive::SolidMesh { .. } | Primitive::GradientMesh { .. } => {
|
||||
// Not supported!
|
||||
// TODO: Draw a placeholder (?)
|
||||
log::warn!(
|
||||
"Unsupported primitive in `iced_tiny_skia`: {:?}",
|
||||
primitive
|
||||
);
|
||||
}
|
||||
_ => {
|
||||
// Not supported!
|
||||
log::warn!(
|
||||
"Unsupported primitive in `iced_tiny_skia`: {:?}",
|
||||
primitive
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -766,6 +766,10 @@ fn adjust_clip_mask(clip_mask: &mut tiny_skia::Mask, bounds: Rectangle) {
|
|||
);
|
||||
}
|
||||
|
||||
impl iced_graphics::Backend for Backend {
|
||||
type Primitive = primitive::Custom;
|
||||
}
|
||||
|
||||
impl backend::Text for Backend {
|
||||
const ICON_FONT: Font = Font::with_name("Iced-Icons");
|
||||
const CHECKMARK_ICON: char = '\u{f00c}';
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use crate::graphics::geometry::fill::{self, Fill};
|
|||
use crate::graphics::geometry::stroke::{self, Stroke};
|
||||
use crate::graphics::geometry::{Path, Style, Text};
|
||||
use crate::graphics::Gradient;
|
||||
use crate::graphics::Primitive;
|
||||
use crate::primitive::{self, Primitive};
|
||||
|
||||
pub struct Frame {
|
||||
size: Size,
|
||||
|
|
@ -42,12 +42,13 @@ impl Frame {
|
|||
let Some(path) = convert_path(path) else { return };
|
||||
let fill = fill.into();
|
||||
|
||||
self.primitives.push(Primitive::Fill {
|
||||
path,
|
||||
paint: into_paint(fill.style),
|
||||
rule: into_fill_rule(fill.rule),
|
||||
transform: self.transform,
|
||||
});
|
||||
self.primitives
|
||||
.push(Primitive::Custom(primitive::Custom::Fill {
|
||||
path,
|
||||
paint: into_paint(fill.style),
|
||||
rule: into_fill_rule(fill.rule),
|
||||
transform: self.transform,
|
||||
}));
|
||||
}
|
||||
|
||||
pub fn fill_rectangle(
|
||||
|
|
@ -59,15 +60,16 @@ impl Frame {
|
|||
let Some(path) = convert_path(&Path::rectangle(top_left, size)) else { return };
|
||||
let fill = fill.into();
|
||||
|
||||
self.primitives.push(Primitive::Fill {
|
||||
path,
|
||||
paint: tiny_skia::Paint {
|
||||
anti_alias: false,
|
||||
..into_paint(fill.style)
|
||||
},
|
||||
rule: into_fill_rule(fill.rule),
|
||||
transform: self.transform,
|
||||
});
|
||||
self.primitives
|
||||
.push(Primitive::Custom(primitive::Custom::Fill {
|
||||
path,
|
||||
paint: tiny_skia::Paint {
|
||||
anti_alias: false,
|
||||
..into_paint(fill.style)
|
||||
},
|
||||
rule: into_fill_rule(fill.rule),
|
||||
transform: self.transform,
|
||||
}));
|
||||
}
|
||||
|
||||
pub fn stroke<'a>(&mut self, path: &Path, stroke: impl Into<Stroke<'a>>) {
|
||||
|
|
@ -76,12 +78,13 @@ impl Frame {
|
|||
let stroke = stroke.into();
|
||||
let skia_stroke = into_stroke(&stroke);
|
||||
|
||||
self.primitives.push(Primitive::Stroke {
|
||||
path,
|
||||
paint: into_paint(stroke.style),
|
||||
stroke: skia_stroke,
|
||||
transform: self.transform,
|
||||
});
|
||||
self.primitives
|
||||
.push(Primitive::Custom(primitive::Custom::Stroke {
|
||||
path,
|
||||
paint: into_paint(stroke.style),
|
||||
stroke: skia_stroke,
|
||||
transform: self.transform,
|
||||
}));
|
||||
}
|
||||
|
||||
pub fn fill_text(&mut self, text: impl Into<Text>) {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
pub mod window;
|
||||
|
||||
mod backend;
|
||||
mod primitive;
|
||||
mod settings;
|
||||
mod text;
|
||||
|
||||
|
|
@ -17,6 +18,7 @@ pub use iced_graphics as graphics;
|
|||
pub use iced_graphics::core;
|
||||
|
||||
pub use backend::Backend;
|
||||
pub use primitive::Primitive;
|
||||
pub use settings::Settings;
|
||||
|
||||
/// A [`tiny-skia`] graphics renderer for [`iced`].
|
||||
|
|
|
|||
48
tiny_skia/src/primitive.rs
Normal file
48
tiny_skia/src/primitive.rs
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
use crate::core::Rectangle;
|
||||
use crate::graphics::Damage;
|
||||
|
||||
pub type Primitive = crate::graphics::Primitive<Custom>;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Custom {
|
||||
/// A path filled with some paint.
|
||||
Fill {
|
||||
/// The path to fill.
|
||||
path: tiny_skia::Path,
|
||||
/// The paint to use.
|
||||
paint: tiny_skia::Paint<'static>,
|
||||
/// The fill rule to follow.
|
||||
rule: tiny_skia::FillRule,
|
||||
/// The transform to apply to the path.
|
||||
transform: tiny_skia::Transform,
|
||||
},
|
||||
/// A path stroked with some paint.
|
||||
Stroke {
|
||||
/// The path to stroke.
|
||||
path: tiny_skia::Path,
|
||||
/// The paint to use.
|
||||
paint: tiny_skia::Paint<'static>,
|
||||
/// The stroke settings.
|
||||
stroke: tiny_skia::Stroke,
|
||||
/// The transform to apply to the path.
|
||||
transform: tiny_skia::Transform,
|
||||
},
|
||||
}
|
||||
|
||||
impl Damage for Custom {
|
||||
fn bounds(&self) -> Rectangle {
|
||||
match self {
|
||||
Self::Fill { path, .. } | Self::Stroke { path, .. } => {
|
||||
let bounds = path.bounds();
|
||||
|
||||
Rectangle {
|
||||
x: bounds.x(),
|
||||
y: bounds.y(),
|
||||
width: bounds.width(),
|
||||
height: bounds.height(),
|
||||
}
|
||||
.expand(1.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
use crate::core::{Color, Rectangle, Size};
|
||||
use crate::graphics::compositor::{self, Information};
|
||||
use crate::graphics::damage;
|
||||
use crate::graphics::{Error, Primitive, Viewport};
|
||||
use crate::{Backend, Renderer, Settings};
|
||||
use crate::graphics::{Error, Viewport};
|
||||
use crate::{Backend, Primitive, Renderer, Settings};
|
||||
|
||||
use raw_window_handle::{HasRawDisplayHandle, HasRawWindowHandle};
|
||||
use std::marker::PhantomData;
|
||||
|
|
|
|||
|
|
@ -2,7 +2,8 @@ use crate::core;
|
|||
use crate::core::{Color, Font, Point, Size};
|
||||
use crate::graphics::backend;
|
||||
use crate::graphics::color;
|
||||
use crate::graphics::{Primitive, Transformation, Viewport};
|
||||
use crate::graphics::{Transformation, Viewport};
|
||||
use crate::primitive::{self, Primitive};
|
||||
use crate::quad;
|
||||
use crate::text;
|
||||
use crate::triangle;
|
||||
|
|
@ -334,6 +335,10 @@ impl Backend {
|
|||
}
|
||||
}
|
||||
|
||||
impl crate::graphics::Backend for Backend {
|
||||
type Primitive = primitive::Custom;
|
||||
}
|
||||
|
||||
impl backend::Text for Backend {
|
||||
const ICON_FONT: Font = Font::with_name("Iced-Icons");
|
||||
const CHECKMARK_ICON: char = '\u{f00c}';
|
||||
|
|
|
|||
|
|
@ -5,10 +5,10 @@ use crate::graphics::geometry::fill::{self, Fill};
|
|||
use crate::graphics::geometry::{
|
||||
LineCap, LineDash, LineJoin, Path, Stroke, Style, Text,
|
||||
};
|
||||
use crate::graphics::primitive::{self, Primitive};
|
||||
use crate::graphics::Gradient;
|
||||
use crate::graphics::gradient::{self, Gradient};
|
||||
use crate::graphics::mesh::{self, Mesh};
|
||||
use crate::primitive::{self, Primitive};
|
||||
|
||||
use iced_graphics::gradient;
|
||||
use lyon::geom::euclid;
|
||||
use lyon::tessellation;
|
||||
use std::borrow::Cow;
|
||||
|
|
@ -25,8 +25,8 @@ pub struct Frame {
|
|||
}
|
||||
|
||||
enum Buffer {
|
||||
Solid(tessellation::VertexBuffers<primitive::ColoredVertex2D, u32>),
|
||||
Gradient(tessellation::VertexBuffers<primitive::GradientVertex2D, u32>),
|
||||
Solid(tessellation::VertexBuffers<mesh::SolidVertex2D, u32>),
|
||||
Gradient(tessellation::VertexBuffers<mesh::GradientVertex2D, u32>),
|
||||
}
|
||||
|
||||
struct BufferStack {
|
||||
|
|
@ -464,24 +464,28 @@ impl Frame {
|
|||
match buffer {
|
||||
Buffer::Solid(buffer) => {
|
||||
if !buffer.indices.is_empty() {
|
||||
self.primitives.push(Primitive::SolidMesh {
|
||||
buffers: primitive::Mesh2D {
|
||||
vertices: buffer.vertices,
|
||||
indices: buffer.indices,
|
||||
},
|
||||
size: self.size,
|
||||
})
|
||||
self.primitives.push(Primitive::Custom(
|
||||
primitive::Custom::Mesh(Mesh::Solid {
|
||||
buffers: mesh::Indexed {
|
||||
vertices: buffer.vertices,
|
||||
indices: buffer.indices,
|
||||
},
|
||||
size: self.size,
|
||||
}),
|
||||
))
|
||||
}
|
||||
}
|
||||
Buffer::Gradient(buffer) => {
|
||||
if !buffer.indices.is_empty() {
|
||||
self.primitives.push(Primitive::GradientMesh {
|
||||
buffers: primitive::Mesh2D {
|
||||
vertices: buffer.vertices,
|
||||
indices: buffer.indices,
|
||||
},
|
||||
size: self.size,
|
||||
})
|
||||
self.primitives.push(Primitive::Custom(
|
||||
primitive::Custom::Mesh(Mesh::Gradient {
|
||||
buffers: mesh::Indexed {
|
||||
vertices: buffer.vertices,
|
||||
indices: buffer.indices,
|
||||
},
|
||||
size: self.size,
|
||||
}),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -495,32 +499,32 @@ struct GradientVertex2DBuilder {
|
|||
gradient: gradient::Packed,
|
||||
}
|
||||
|
||||
impl tessellation::FillVertexConstructor<primitive::GradientVertex2D>
|
||||
impl tessellation::FillVertexConstructor<mesh::GradientVertex2D>
|
||||
for GradientVertex2DBuilder
|
||||
{
|
||||
fn new_vertex(
|
||||
&mut self,
|
||||
vertex: tessellation::FillVertex<'_>,
|
||||
) -> primitive::GradientVertex2D {
|
||||
) -> mesh::GradientVertex2D {
|
||||
let position = vertex.position();
|
||||
|
||||
primitive::GradientVertex2D {
|
||||
mesh::GradientVertex2D {
|
||||
position: [position.x, position.y],
|
||||
gradient: self.gradient,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl tessellation::StrokeVertexConstructor<primitive::GradientVertex2D>
|
||||
impl tessellation::StrokeVertexConstructor<mesh::GradientVertex2D>
|
||||
for GradientVertex2DBuilder
|
||||
{
|
||||
fn new_vertex(
|
||||
&mut self,
|
||||
vertex: tessellation::StrokeVertex<'_, '_>,
|
||||
) -> primitive::GradientVertex2D {
|
||||
) -> mesh::GradientVertex2D {
|
||||
let position = vertex.position();
|
||||
|
||||
primitive::GradientVertex2D {
|
||||
mesh::GradientVertex2D {
|
||||
position: [position.x, position.y],
|
||||
gradient: self.gradient,
|
||||
}
|
||||
|
|
@ -529,32 +533,32 @@ impl tessellation::StrokeVertexConstructor<primitive::GradientVertex2D>
|
|||
|
||||
struct TriangleVertex2DBuilder(color::Packed);
|
||||
|
||||
impl tessellation::FillVertexConstructor<primitive::ColoredVertex2D>
|
||||
impl tessellation::FillVertexConstructor<mesh::SolidVertex2D>
|
||||
for TriangleVertex2DBuilder
|
||||
{
|
||||
fn new_vertex(
|
||||
&mut self,
|
||||
vertex: tessellation::FillVertex<'_>,
|
||||
) -> primitive::ColoredVertex2D {
|
||||
) -> mesh::SolidVertex2D {
|
||||
let position = vertex.position();
|
||||
|
||||
primitive::ColoredVertex2D {
|
||||
mesh::SolidVertex2D {
|
||||
position: [position.x, position.y],
|
||||
color: self.0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl tessellation::StrokeVertexConstructor<primitive::ColoredVertex2D>
|
||||
impl tessellation::StrokeVertexConstructor<mesh::SolidVertex2D>
|
||||
for TriangleVertex2DBuilder
|
||||
{
|
||||
fn new_vertex(
|
||||
&mut self,
|
||||
vertex: tessellation::StrokeVertex<'_, '_>,
|
||||
) -> primitive::ColoredVertex2D {
|
||||
) -> mesh::SolidVertex2D {
|
||||
let position = vertex.position();
|
||||
|
||||
primitive::ColoredVertex2D {
|
||||
mesh::SolidVertex2D {
|
||||
position: [position.x, position.y],
|
||||
color: self.0,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,8 +11,10 @@ pub use text::Text;
|
|||
use crate::core;
|
||||
use crate::core::alignment;
|
||||
use crate::core::{Color, Font, Point, Rectangle, Size, Vector};
|
||||
use crate::graphics;
|
||||
use crate::graphics::color;
|
||||
use crate::graphics::{Primitive, Viewport};
|
||||
use crate::graphics::Viewport;
|
||||
use crate::primitive::{self, Primitive};
|
||||
use crate::quad::{self, Quad};
|
||||
|
||||
/// A group of primitives that should be clipped together.
|
||||
|
|
@ -179,40 +181,6 @@ impl<'a> Layer<'a> {
|
|||
bounds: *bounds + translation,
|
||||
});
|
||||
}
|
||||
Primitive::SolidMesh { buffers, size } => {
|
||||
let layer = &mut layers[current_layer];
|
||||
|
||||
let bounds = Rectangle::new(
|
||||
Point::new(translation.x, translation.y),
|
||||
*size,
|
||||
);
|
||||
|
||||
// Only draw visible content
|
||||
if let Some(clip_bounds) = layer.bounds.intersection(&bounds) {
|
||||
layer.meshes.push(Mesh::Solid {
|
||||
origin: Point::new(translation.x, translation.y),
|
||||
buffers,
|
||||
clip_bounds,
|
||||
});
|
||||
}
|
||||
}
|
||||
Primitive::GradientMesh { buffers, size } => {
|
||||
let layer = &mut layers[current_layer];
|
||||
|
||||
let bounds = Rectangle::new(
|
||||
Point::new(translation.x, translation.y),
|
||||
*size,
|
||||
);
|
||||
|
||||
// Only draw visible content
|
||||
if let Some(clip_bounds) = layer.bounds.intersection(&bounds) {
|
||||
layer.meshes.push(Mesh::Gradient {
|
||||
origin: Point::new(translation.x, translation.y),
|
||||
buffers,
|
||||
clip_bounds,
|
||||
});
|
||||
}
|
||||
}
|
||||
Primitive::Group { primitives } => {
|
||||
// TODO: Inspect a bit and regroup (?)
|
||||
for primitive in primitives {
|
||||
|
|
@ -262,13 +230,54 @@ impl<'a> Layer<'a> {
|
|||
current_layer,
|
||||
);
|
||||
}
|
||||
_ => {
|
||||
// Not supported!
|
||||
log::warn!(
|
||||
"Unsupported primitive in `iced_wgpu`: {:?}",
|
||||
primitive
|
||||
);
|
||||
}
|
||||
Primitive::Custom(custom) => match custom {
|
||||
primitive::Custom::Mesh(mesh) => match mesh {
|
||||
graphics::Mesh::Solid { buffers, size } => {
|
||||
let layer = &mut layers[current_layer];
|
||||
|
||||
let bounds = Rectangle::new(
|
||||
Point::new(translation.x, translation.y),
|
||||
*size,
|
||||
);
|
||||
|
||||
// Only draw visible content
|
||||
if let Some(clip_bounds) =
|
||||
layer.bounds.intersection(&bounds)
|
||||
{
|
||||
layer.meshes.push(Mesh::Solid {
|
||||
origin: Point::new(
|
||||
translation.x,
|
||||
translation.y,
|
||||
),
|
||||
buffers,
|
||||
clip_bounds,
|
||||
});
|
||||
}
|
||||
}
|
||||
graphics::Mesh::Gradient { buffers, size } => {
|
||||
let layer = &mut layers[current_layer];
|
||||
|
||||
let bounds = Rectangle::new(
|
||||
Point::new(translation.x, translation.y),
|
||||
*size,
|
||||
);
|
||||
|
||||
// Only draw visible content
|
||||
if let Some(clip_bounds) =
|
||||
layer.bounds.intersection(&bounds)
|
||||
{
|
||||
layer.meshes.push(Mesh::Gradient {
|
||||
origin: Point::new(
|
||||
translation.x,
|
||||
translation.y,
|
||||
),
|
||||
buffers,
|
||||
clip_bounds,
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
//! A collection of triangle primitives.
|
||||
use crate::core::{Point, Rectangle};
|
||||
use crate::graphics::primitive;
|
||||
use crate::graphics::mesh;
|
||||
|
||||
/// A mesh of triangles.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
|
|
@ -11,7 +11,7 @@ pub enum Mesh<'a> {
|
|||
origin: Point,
|
||||
|
||||
/// The vertex and index buffers of the [`Mesh`].
|
||||
buffers: &'a primitive::Mesh2D<primitive::ColoredVertex2D>,
|
||||
buffers: &'a mesh::Indexed<mesh::SolidVertex2D>,
|
||||
|
||||
/// The clipping bounds of the [`Mesh`].
|
||||
clip_bounds: Rectangle<f32>,
|
||||
|
|
@ -22,7 +22,7 @@ pub enum Mesh<'a> {
|
|||
origin: Point,
|
||||
|
||||
/// The vertex and index buffers of the [`Mesh`].
|
||||
buffers: &'a primitive::Mesh2D<primitive::GradientVertex2D>,
|
||||
buffers: &'a mesh::Indexed<mesh::GradientVertex2D>,
|
||||
|
||||
/// The clipping bounds of the [`Mesh`].
|
||||
clip_bounds: Rectangle<f32>,
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@
|
|||
#![allow(clippy::inherent_to_string, clippy::type_complexity)]
|
||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||
pub mod layer;
|
||||
pub mod primitive;
|
||||
pub mod settings;
|
||||
pub mod window;
|
||||
|
||||
|
|
@ -60,6 +61,7 @@ pub use wgpu;
|
|||
|
||||
pub use backend::Backend;
|
||||
pub use layer::Layer;
|
||||
pub use primitive::Primitive;
|
||||
pub use settings::Settings;
|
||||
|
||||
#[cfg(any(feature = "image", feature = "svg"))]
|
||||
|
|
|
|||
21
wgpu/src/primitive.rs
Normal file
21
wgpu/src/primitive.rs
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
//! Draw using different graphical primitives.
|
||||
use crate::core::Rectangle;
|
||||
use crate::graphics::{Damage, Mesh};
|
||||
|
||||
/// The graphical primitives supported by `iced_wgpu`.
|
||||
pub type Primitive = crate::graphics::Primitive<Custom>;
|
||||
|
||||
/// The custom primitives supported by `iced_wgpu`.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Custom {
|
||||
/// A mesh primitive.
|
||||
Mesh(Mesh),
|
||||
}
|
||||
|
||||
impl Damage for Custom {
|
||||
fn bounds(&self) -> Rectangle {
|
||||
match self {
|
||||
Self::Mesh(mesh) => mesh.bounds(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -393,7 +393,7 @@ impl Uniforms {
|
|||
}
|
||||
|
||||
mod solid {
|
||||
use crate::graphics::primitive;
|
||||
use crate::graphics::mesh;
|
||||
use crate::graphics::Antialiasing;
|
||||
use crate::triangle;
|
||||
use crate::Buffer;
|
||||
|
|
@ -406,7 +406,7 @@ mod solid {
|
|||
|
||||
#[derive(Debug)]
|
||||
pub struct Layer {
|
||||
pub vertices: Buffer<primitive::ColoredVertex2D>,
|
||||
pub vertices: Buffer<mesh::SolidVertex2D>,
|
||||
pub uniforms: Buffer<triangle::Uniforms>,
|
||||
pub constants: wgpu::BindGroup,
|
||||
}
|
||||
|
|
@ -493,38 +493,40 @@ mod solid {
|
|||
),
|
||||
});
|
||||
|
||||
let pipeline = device.create_render_pipeline(
|
||||
&wgpu::RenderPipelineDescriptor {
|
||||
label: Some("iced_wgpu::triangle::solid pipeline"),
|
||||
layout: Some(&layout),
|
||||
vertex: wgpu::VertexState {
|
||||
module: &shader,
|
||||
entry_point: "solid_vs_main",
|
||||
buffers: &[wgpu::VertexBufferLayout {
|
||||
array_stride: std::mem::size_of::<
|
||||
primitive::ColoredVertex2D,
|
||||
>()
|
||||
as u64,
|
||||
step_mode: wgpu::VertexStepMode::Vertex,
|
||||
attributes: &wgpu::vertex_attr_array!(
|
||||
// Position
|
||||
0 => Float32x2,
|
||||
// Color
|
||||
1 => Float32x4,
|
||||
),
|
||||
}],
|
||||
let pipeline =
|
||||
device.create_render_pipeline(
|
||||
&wgpu::RenderPipelineDescriptor {
|
||||
label: Some("iced_wgpu::triangle::solid pipeline"),
|
||||
layout: Some(&layout),
|
||||
vertex: wgpu::VertexState {
|
||||
module: &shader,
|
||||
entry_point: "solid_vs_main",
|
||||
buffers: &[wgpu::VertexBufferLayout {
|
||||
array_stride: std::mem::size_of::<
|
||||
mesh::SolidVertex2D,
|
||||
>(
|
||||
)
|
||||
as u64,
|
||||
step_mode: wgpu::VertexStepMode::Vertex,
|
||||
attributes: &wgpu::vertex_attr_array!(
|
||||
// Position
|
||||
0 => Float32x2,
|
||||
// Color
|
||||
1 => Float32x4,
|
||||
),
|
||||
}],
|
||||
},
|
||||
fragment: Some(wgpu::FragmentState {
|
||||
module: &shader,
|
||||
entry_point: "solid_fs_main",
|
||||
targets: &[triangle::fragment_target(format)],
|
||||
}),
|
||||
primitive: triangle::primitive_state(),
|
||||
depth_stencil: None,
|
||||
multisample: triangle::multisample_state(antialiasing),
|
||||
multiview: None,
|
||||
},
|
||||
fragment: Some(wgpu::FragmentState {
|
||||
module: &shader,
|
||||
entry_point: "solid_fs_main",
|
||||
targets: &[triangle::fragment_target(format)],
|
||||
}),
|
||||
primitive: triangle::primitive_state(),
|
||||
depth_stencil: None,
|
||||
multisample: triangle::multisample_state(antialiasing),
|
||||
multiview: None,
|
||||
},
|
||||
);
|
||||
);
|
||||
|
||||
Self {
|
||||
pipeline,
|
||||
|
|
@ -535,7 +537,8 @@ mod solid {
|
|||
}
|
||||
|
||||
mod gradient {
|
||||
use crate::graphics::{primitive, Antialiasing};
|
||||
use crate::graphics::mesh;
|
||||
use crate::graphics::Antialiasing;
|
||||
use crate::triangle;
|
||||
use crate::Buffer;
|
||||
|
||||
|
|
@ -547,7 +550,7 @@ mod gradient {
|
|||
|
||||
#[derive(Debug)]
|
||||
pub struct Layer {
|
||||
pub vertices: Buffer<primitive::GradientVertex2D>,
|
||||
pub vertices: Buffer<mesh::GradientVertex2D>,
|
||||
pub uniforms: Buffer<triangle::Uniforms>,
|
||||
pub constants: wgpu::BindGroup,
|
||||
}
|
||||
|
|
@ -645,7 +648,7 @@ mod gradient {
|
|||
entry_point: "gradient_vs_main",
|
||||
buffers: &[wgpu::VertexBufferLayout {
|
||||
array_stride: std::mem::size_of::<
|
||||
primitive::GradientVertex2D,
|
||||
mesh::GradientVertex2D,
|
||||
>()
|
||||
as u64,
|
||||
step_mode: wgpu::VertexStepMode::Vertex,
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@ use crate::core::{Color, Size};
|
|||
use crate::graphics;
|
||||
use crate::graphics::color;
|
||||
use crate::graphics::compositor;
|
||||
use crate::graphics::{Error, Primitive, Viewport};
|
||||
use crate::{Backend, Renderer, Settings};
|
||||
use crate::graphics::{Error, Viewport};
|
||||
use crate::{Backend, Primitive, Renderer, Settings};
|
||||
|
||||
use futures::stream::{self, StreamExt};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use crate::canvas::event::{self, Event};
|
||||
use crate::canvas::mouse;
|
||||
use crate::core::Rectangle;
|
||||
use crate::graphics::geometry::{self, Geometry};
|
||||
use crate::graphics::geometry;
|
||||
|
||||
/// The state and logic of a [`Canvas`].
|
||||
///
|
||||
|
|
@ -51,7 +51,7 @@ where
|
|||
theme: &Renderer::Theme,
|
||||
bounds: Rectangle,
|
||||
cursor: mouse::Cursor,
|
||||
) -> Vec<Geometry>;
|
||||
) -> Vec<Renderer::Geometry>;
|
||||
|
||||
/// Returns the current mouse interaction of the [`Program`].
|
||||
///
|
||||
|
|
@ -93,7 +93,7 @@ where
|
|||
theme: &Renderer::Theme,
|
||||
bounds: Rectangle,
|
||||
cursor: mouse::Cursor,
|
||||
) -> Vec<Geometry> {
|
||||
) -> Vec<Renderer::Geometry> {
|
||||
T::draw(self, state, renderer, theme, bounds, cursor)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ use crate::core::widget::Tree;
|
|||
use crate::core::{
|
||||
Color, Element, Layout, Length, Point, Rectangle, Size, Vector, Widget,
|
||||
};
|
||||
use crate::graphics::geometry::Renderer as _;
|
||||
use crate::Renderer;
|
||||
use thiserror::Error;
|
||||
|
||||
|
|
@ -121,7 +122,7 @@ impl<'a, Message, Theme> Widget<Message, Renderer<Theme>> for QRCode<'a> {
|
|||
let translation = Vector::new(bounds.x, bounds.y);
|
||||
|
||||
renderer.with_translation(translation, |renderer| {
|
||||
renderer.draw_primitive(geometry.0);
|
||||
renderer.draw(vec![geometry]);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue