Skip to content

Commit

Permalink
Merge pull request #1932 from iced-rs/generic-graphics-primitive
Browse files Browse the repository at this point in the history
Backend-specific primitives
  • Loading branch information
hecrj authored Jun 29, 2023
2 parents 8d65e40 + 6921564 commit c6b5831
Show file tree
Hide file tree
Showing 35 changed files with 867 additions and 480 deletions.
15 changes: 1 addition & 14 deletions core/src/renderer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
Expand Down
1 change: 0 additions & 1 deletion examples/geometry/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,3 @@ publish = false

[dependencies]
iced = { path = "../..", features = ["advanced"] }
iced_graphics = { path = "../../graphics" }
28 changes: 13 additions & 15 deletions examples/geometry/src/main.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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();

Expand Down Expand Up @@ -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),
},
Expand All @@ -135,7 +133,7 @@ mod rainbow {
renderer.with_translation(
Vector::new(bounds.x, bounds.y),
|renderer| {
renderer.draw_primitive(mesh);
renderer.draw_mesh(mesh);
},
);
}
Expand Down
4 changes: 3 additions & 1 deletion examples/loading_spinners/src/circular.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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]);
},
);
}
Expand Down
4 changes: 0 additions & 4 deletions graphics/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 8 additions & 0 deletions graphics/src/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
66 changes: 62 additions & 4 deletions graphics/src/damage.rs
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down Expand Up @@ -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)
Expand All @@ -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()
}
}
Expand Down
17 changes: 4 additions & 13 deletions graphics/src/geometry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>);
}
3 changes: 2 additions & 1 deletion graphics/src/gradient.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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
Expand Down
9 changes: 5 additions & 4 deletions graphics/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
mod antialiasing;
mod error;
mod primitive;
mod transformation;
mod viewport;

Expand All @@ -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")]
Expand All @@ -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 changes: 76 additions & 0 deletions graphics/src/mesh.rs
Original file line number Diff line number Diff line change
@@ -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,
}
Loading

0 comments on commit c6b5831

Please sign in to comment.