Redesign iced_wgpu layering architecture
This commit is contained in:
parent
99a904112c
commit
b05e61f5c8
36 changed files with 2781 additions and 2048 deletions
|
|
@ -5,7 +5,7 @@ use std::sync::Arc;
|
|||
/// A piece of data that can be cached.
|
||||
pub trait Cached: Sized {
|
||||
/// The type of cache produced.
|
||||
type Cache;
|
||||
type Cache: Clone;
|
||||
|
||||
/// Loads the [`Cache`] into a proper instance.
|
||||
///
|
||||
|
|
@ -15,7 +15,7 @@ pub trait Cached: Sized {
|
|||
/// Caches this value, producing its corresponding [`Cache`].
|
||||
///
|
||||
/// [`Cache`]: Self::Cache
|
||||
fn cache(self) -> Self::Cache;
|
||||
fn cache(self, previous: Option<Self::Cache>) -> Self::Cache;
|
||||
}
|
||||
|
||||
impl<T> Cached for Primitive<T> {
|
||||
|
|
@ -27,7 +27,7 @@ impl<T> Cached for Primitive<T> {
|
|||
}
|
||||
}
|
||||
|
||||
fn cache(self) -> Arc<Self> {
|
||||
fn cache(self, _previous: Option<Arc<Self>>) -> Arc<Self> {
|
||||
Arc::new(self)
|
||||
}
|
||||
}
|
||||
|
|
@ -38,5 +38,5 @@ impl Cached for () {
|
|||
|
||||
fn load(_cache: &Self::Cache) -> Self {}
|
||||
|
||||
fn cache(self) -> Self::Cache {}
|
||||
fn cache(self, _previous: Option<Self::Cache>) -> Self::Cache {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ where
|
|||
) -> Renderer::Geometry {
|
||||
use std::ops::Deref;
|
||||
|
||||
if let State::Filled {
|
||||
let previous = if let State::Filled {
|
||||
bounds: cached_bounds,
|
||||
geometry,
|
||||
} = self.state.borrow().deref()
|
||||
|
|
@ -57,12 +57,16 @@ where
|
|||
if *cached_bounds == bounds {
|
||||
return Cached::load(geometry);
|
||||
}
|
||||
}
|
||||
|
||||
Some(geometry.clone())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let mut frame = Frame::new(renderer, bounds);
|
||||
draw_fn(&mut frame);
|
||||
|
||||
let geometry = frame.into_geometry().cache();
|
||||
let geometry = frame.into_geometry().cache(previous);
|
||||
let result = Cached::load(&geometry);
|
||||
|
||||
*self.state.borrow_mut() = State::Filled { bounds, geometry };
|
||||
|
|
|
|||
|
|
@ -1,14 +1,94 @@
|
|||
//! Load and operate on images.
|
||||
use crate::core::image::{Data, Handle};
|
||||
|
||||
use bitflags::bitflags;
|
||||
|
||||
#[cfg(feature = "image")]
|
||||
pub use ::image as image_rs;
|
||||
|
||||
use crate::core::image;
|
||||
use crate::core::svg;
|
||||
use crate::core::{Color, Rectangle};
|
||||
|
||||
/// A raster or vector image.
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Image {
|
||||
/// A raster image.
|
||||
Raster {
|
||||
/// The handle of a raster image.
|
||||
handle: image::Handle,
|
||||
|
||||
/// The filter method of a raster image.
|
||||
filter_method: image::FilterMethod,
|
||||
|
||||
/// The bounds of the image.
|
||||
bounds: Rectangle,
|
||||
},
|
||||
/// A vector image.
|
||||
Vector {
|
||||
/// The handle of a vector image.
|
||||
handle: svg::Handle,
|
||||
|
||||
/// The [`Color`] filter
|
||||
color: Option<Color>,
|
||||
|
||||
/// The bounds of the image.
|
||||
bounds: Rectangle,
|
||||
},
|
||||
}
|
||||
|
||||
#[cfg(feature = "image")]
|
||||
/// Tries to load an image by its [`Handle`].
|
||||
pub fn load(handle: &Handle) -> image_rs::ImageResult<image_rs::DynamicImage> {
|
||||
pub fn load(
|
||||
handle: &image::Handle,
|
||||
) -> ::image::ImageResult<::image::DynamicImage> {
|
||||
use bitflags::bitflags;
|
||||
|
||||
bitflags! {
|
||||
struct Operation: u8 {
|
||||
const FLIP_HORIZONTALLY = 0b001;
|
||||
const ROTATE_180 = 0b010;
|
||||
const FLIP_DIAGONALLY = 0b100;
|
||||
}
|
||||
}
|
||||
|
||||
impl Operation {
|
||||
// Meaning of the returned value is described e.g. at:
|
||||
// https://magnushoff.com/articles/jpeg-orientation/
|
||||
fn from_exif<R>(reader: &mut R) -> Result<Self, exif::Error>
|
||||
where
|
||||
R: std::io::BufRead + std::io::Seek,
|
||||
{
|
||||
let exif = exif::Reader::new().read_from_container(reader)?;
|
||||
|
||||
Ok(exif
|
||||
.get_field(exif::Tag::Orientation, exif::In::PRIMARY)
|
||||
.and_then(|field| field.value.get_uint(0))
|
||||
.and_then(|value| u8::try_from(value).ok())
|
||||
.and_then(|value| Self::from_bits(value.saturating_sub(1)))
|
||||
.unwrap_or_else(Self::empty))
|
||||
}
|
||||
|
||||
fn perform(
|
||||
self,
|
||||
mut image: ::image::DynamicImage,
|
||||
) -> ::image::DynamicImage {
|
||||
use ::image::imageops;
|
||||
|
||||
if self.contains(Self::FLIP_DIAGONALLY) {
|
||||
imageops::flip_vertical_in_place(&mut image);
|
||||
}
|
||||
|
||||
if self.contains(Self::ROTATE_180) {
|
||||
imageops::rotate180_in_place(&mut image);
|
||||
}
|
||||
|
||||
if self.contains(Self::FLIP_HORIZONTALLY) {
|
||||
imageops::flip_horizontal_in_place(&mut image);
|
||||
}
|
||||
|
||||
image
|
||||
}
|
||||
}
|
||||
|
||||
match handle.data() {
|
||||
Data::Path(path) => {
|
||||
image::Data::Path(path) => {
|
||||
let image = ::image::open(path)?;
|
||||
|
||||
let operation = std::fs::File::open(path)
|
||||
|
|
@ -19,7 +99,7 @@ pub fn load(handle: &Handle) -> image_rs::ImageResult<image_rs::DynamicImage> {
|
|||
|
||||
Ok(operation.perform(image))
|
||||
}
|
||||
Data::Bytes(bytes) => {
|
||||
image::Data::Bytes(bytes) => {
|
||||
let image = ::image::load_from_memory(bytes)?;
|
||||
let operation =
|
||||
Operation::from_exif(&mut std::io::Cursor::new(bytes))
|
||||
|
|
@ -28,68 +108,22 @@ pub fn load(handle: &Handle) -> image_rs::ImageResult<image_rs::DynamicImage> {
|
|||
|
||||
Ok(operation.perform(image))
|
||||
}
|
||||
Data::Rgba {
|
||||
image::Data::Rgba {
|
||||
width,
|
||||
height,
|
||||
pixels,
|
||||
} => {
|
||||
if let Some(image) = image_rs::ImageBuffer::from_vec(
|
||||
*width,
|
||||
*height,
|
||||
pixels.to_vec(),
|
||||
) {
|
||||
Ok(image_rs::DynamicImage::ImageRgba8(image))
|
||||
if let Some(image) =
|
||||
::image::ImageBuffer::from_vec(*width, *height, pixels.to_vec())
|
||||
{
|
||||
Ok(::image::DynamicImage::ImageRgba8(image))
|
||||
} else {
|
||||
Err(image_rs::error::ImageError::Limits(
|
||||
image_rs::error::LimitError::from_kind(
|
||||
image_rs::error::LimitErrorKind::DimensionError,
|
||||
Err(::image::error::ImageError::Limits(
|
||||
::image::error::LimitError::from_kind(
|
||||
::image::error::LimitErrorKind::DimensionError,
|
||||
),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
struct Operation: u8 {
|
||||
const FLIP_HORIZONTALLY = 0b001;
|
||||
const ROTATE_180 = 0b010;
|
||||
const FLIP_DIAGONALLY = 0b100;
|
||||
}
|
||||
}
|
||||
|
||||
impl Operation {
|
||||
// Meaning of the returned value is described e.g. at:
|
||||
// https://magnushoff.com/articles/jpeg-orientation/
|
||||
fn from_exif<R>(reader: &mut R) -> Result<Self, exif::Error>
|
||||
where
|
||||
R: std::io::BufRead + std::io::Seek,
|
||||
{
|
||||
let exif = exif::Reader::new().read_from_container(reader)?;
|
||||
|
||||
Ok(exif
|
||||
.get_field(exif::Tag::Orientation, exif::In::PRIMARY)
|
||||
.and_then(|field| field.value.get_uint(0))
|
||||
.and_then(|value| u8::try_from(value).ok())
|
||||
.and_then(|value| Self::from_bits(value.saturating_sub(1)))
|
||||
.unwrap_or_else(Self::empty))
|
||||
}
|
||||
|
||||
fn perform(self, mut image: image::DynamicImage) -> image::DynamicImage {
|
||||
use image::imageops;
|
||||
|
||||
if self.contains(Self::FLIP_DIAGONALLY) {
|
||||
imageops::flip_vertical_in_place(&mut image);
|
||||
}
|
||||
|
||||
if self.contains(Self::ROTATE_180) {
|
||||
imageops::rotate180_in_place(&mut image);
|
||||
}
|
||||
|
||||
if self.contains(Self::FLIP_HORIZONTALLY) {
|
||||
imageops::flip_horizontal_in_place(&mut image);
|
||||
}
|
||||
|
||||
image
|
||||
}
|
||||
}
|
||||
|
|
|
|||
47
graphics/src/layer.rs
Normal file
47
graphics/src/layer.rs
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
pub trait Layer {
|
||||
type Cache;
|
||||
|
||||
fn new() -> Self;
|
||||
|
||||
fn clear(&mut self);
|
||||
}
|
||||
|
||||
pub struct Recorder<T: Layer> {
|
||||
layers: Vec<T>,
|
||||
caches: Vec<T::Cache>,
|
||||
stack: Vec<Kind>,
|
||||
current: usize,
|
||||
}
|
||||
|
||||
enum Kind {
|
||||
Fresh(usize),
|
||||
Cache(usize),
|
||||
}
|
||||
|
||||
impl<T: Layer> Recorder<T> {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
layers: vec![Layer::new()],
|
||||
caches: Vec::new(),
|
||||
stack: Vec::new(),
|
||||
current: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fill_quad(&mut self) {}
|
||||
|
||||
pub fn push_cache(&mut self, cache: T::Cache) {
|
||||
self.caches.push(cache);
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
self.caches.clear();
|
||||
self.stack.clear();
|
||||
|
||||
for mut layer in self.layers {
|
||||
layer.clear();
|
||||
}
|
||||
|
||||
self.current = 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -28,6 +28,7 @@ pub mod compositor;
|
|||
pub mod damage;
|
||||
pub mod error;
|
||||
pub mod gradient;
|
||||
pub mod image;
|
||||
pub mod mesh;
|
||||
pub mod renderer;
|
||||
pub mod text;
|
||||
|
|
@ -35,9 +36,6 @@ pub mod text;
|
|||
#[cfg(feature = "geometry")]
|
||||
pub mod geometry;
|
||||
|
||||
#[cfg(feature = "image")]
|
||||
pub mod image;
|
||||
|
||||
pub use antialiasing::Antialiasing;
|
||||
pub use backend::Backend;
|
||||
pub use cached::Cached;
|
||||
|
|
@ -45,10 +43,12 @@ pub use compositor::Compositor;
|
|||
pub use damage::Damage;
|
||||
pub use error::Error;
|
||||
pub use gradient::Gradient;
|
||||
pub use image::Image;
|
||||
pub use mesh::Mesh;
|
||||
pub use primitive::Primitive;
|
||||
pub use renderer::Renderer;
|
||||
pub use settings::Settings;
|
||||
pub use text::Text;
|
||||
pub use viewport::Viewport;
|
||||
|
||||
pub use iced_core as core;
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
//! Draw triangles!
|
||||
use crate::color;
|
||||
use crate::core::{Rectangle, Size};
|
||||
use crate::core::{Rectangle, Size, Transformation};
|
||||
use crate::gradient;
|
||||
use crate::Damage;
|
||||
|
||||
use bytemuck::{Pod, Zeroable};
|
||||
|
||||
|
|
@ -14,9 +13,10 @@ pub enum Mesh {
|
|||
/// 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.
|
||||
/// The [`Transformation`] for the vertices of the [`Mesh`].
|
||||
transformation: Transformation,
|
||||
|
||||
/// The [`Size`] of the [`Mesh`].
|
||||
size: Size,
|
||||
},
|
||||
/// A mesh with a gradient.
|
||||
|
|
@ -24,19 +24,44 @@ pub enum Mesh {
|
|||
/// 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.
|
||||
/// The [`Transformation`] for the vertices of the [`Mesh`].
|
||||
transformation: Transformation,
|
||||
|
||||
/// The [`Size`] of the [`Mesh`].
|
||||
size: Size,
|
||||
},
|
||||
}
|
||||
|
||||
impl Damage for Mesh {
|
||||
fn bounds(&self) -> Rectangle {
|
||||
impl Mesh {
|
||||
/// Returns the indices of the [`Mesh`].
|
||||
pub fn indices(&self) -> &[u32] {
|
||||
match self {
|
||||
Self::Solid { size, .. } | Self::Gradient { size, .. } => {
|
||||
Rectangle::with_size(*size)
|
||||
Self::Solid { buffers, .. } => &buffers.indices,
|
||||
Self::Gradient { buffers, .. } => &buffers.indices,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the [`Transformation`] of the [`Mesh`].
|
||||
pub fn transformation(&self) -> Transformation {
|
||||
match self {
|
||||
Self::Solid { transformation, .. }
|
||||
| Self::Gradient { transformation, .. } => *transformation,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the clip bounds of the [`Mesh`].
|
||||
pub fn clip_bounds(&self) -> Rectangle {
|
||||
match self {
|
||||
Self::Solid {
|
||||
size,
|
||||
transformation,
|
||||
..
|
||||
}
|
||||
| Self::Gradient {
|
||||
size,
|
||||
transformation,
|
||||
..
|
||||
} => Rectangle::with_size(*size) * *transformation,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -75,6 +100,47 @@ pub struct GradientVertex2D {
|
|||
pub gradient: gradient::Packed,
|
||||
}
|
||||
|
||||
/// The result of counting the attributes of a set of meshes.
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
pub struct AttributeCount {
|
||||
/// The total amount of solid vertices.
|
||||
pub solid_vertices: usize,
|
||||
|
||||
/// The total amount of solid meshes.
|
||||
pub solids: usize,
|
||||
|
||||
/// The total amount of gradient vertices.
|
||||
pub gradient_vertices: usize,
|
||||
|
||||
/// The total amount of gradient meshes.
|
||||
pub gradients: usize,
|
||||
|
||||
/// The total amount of indices.
|
||||
pub indices: usize,
|
||||
}
|
||||
|
||||
/// Returns the number of total vertices & total indices of all [`Mesh`]es.
|
||||
pub fn attribute_count_of(meshes: &[Mesh]) -> AttributeCount {
|
||||
meshes
|
||||
.iter()
|
||||
.fold(AttributeCount::default(), |mut count, mesh| {
|
||||
match mesh {
|
||||
Mesh::Solid { buffers, .. } => {
|
||||
count.solids += 1;
|
||||
count.solid_vertices += buffers.vertices.len();
|
||||
count.indices += buffers.indices.len();
|
||||
}
|
||||
Mesh::Gradient { buffers, .. } => {
|
||||
count.gradients += 1;
|
||||
count.gradient_vertices += buffers.vertices.len();
|
||||
count.indices += buffers.indices.len();
|
||||
}
|
||||
}
|
||||
|
||||
count
|
||||
})
|
||||
}
|
||||
|
||||
/// A renderer capable of drawing a [`Mesh`].
|
||||
pub trait Renderer {
|
||||
/// Draws the given [`Mesh`].
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ impl<B: Backend> Renderer<B> {
|
|||
}
|
||||
|
||||
impl<B: Backend> iced_core::Renderer for Renderer<B> {
|
||||
fn start_layer(&mut self) {
|
||||
fn start_layer(&mut self, _bounds: Rectangle) {
|
||||
self.stack.push(std::mem::take(&mut self.primitives));
|
||||
}
|
||||
|
||||
|
|
@ -75,7 +75,7 @@ impl<B: Backend> iced_core::Renderer for Renderer<B> {
|
|||
self.primitives.push(Primitive::group(layer).clip(bounds));
|
||||
}
|
||||
|
||||
fn start_transformation(&mut self) {
|
||||
fn start_transformation(&mut self, _transformation: Transformation) {
|
||||
self.stack.push(std::mem::take(&mut self.primitives));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,14 +9,67 @@ pub use paragraph::Paragraph;
|
|||
|
||||
pub use cosmic_text;
|
||||
|
||||
use crate::core::alignment;
|
||||
use crate::core::font::{self, Font};
|
||||
use crate::core::text::Shaping;
|
||||
use crate::core::{Color, Point, Rectangle, Size};
|
||||
use crate::core::text::{LineHeight, Shaping};
|
||||
use crate::core::{Color, Pixels, Point, Rectangle, Size, Transformation};
|
||||
|
||||
use once_cell::sync::OnceCell;
|
||||
use std::borrow::Cow;
|
||||
use std::sync::{Arc, RwLock, Weak};
|
||||
|
||||
/// A text primitive.
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Text {
|
||||
/// A paragraph.
|
||||
#[allow(missing_docs)]
|
||||
Paragraph {
|
||||
paragraph: paragraph::Weak,
|
||||
position: Point,
|
||||
color: Color,
|
||||
clip_bounds: Rectangle,
|
||||
transformation: Transformation,
|
||||
},
|
||||
/// An editor.
|
||||
#[allow(missing_docs)]
|
||||
Editor {
|
||||
editor: editor::Weak,
|
||||
position: Point,
|
||||
color: Color,
|
||||
clip_bounds: Rectangle,
|
||||
transformation: Transformation,
|
||||
},
|
||||
/// Some cached text.
|
||||
Cached {
|
||||
/// The contents of the text.
|
||||
content: String,
|
||||
/// The bounds of the text.
|
||||
bounds: Rectangle,
|
||||
/// The color of the text.
|
||||
color: Color,
|
||||
/// The size of the text in logical pixels.
|
||||
size: Pixels,
|
||||
/// The line height of the text.
|
||||
line_height: LineHeight,
|
||||
/// The font of the text.
|
||||
font: Font,
|
||||
/// The horizontal alignment of the text.
|
||||
horizontal_alignment: alignment::Horizontal,
|
||||
/// The vertical alignment of the text.
|
||||
vertical_alignment: alignment::Vertical,
|
||||
/// The shaping strategy of the text.
|
||||
shaping: Shaping,
|
||||
/// The clip bounds of the text.
|
||||
clip_bounds: Rectangle,
|
||||
},
|
||||
/// Some raw text.
|
||||
#[allow(missing_docs)]
|
||||
Raw {
|
||||
raw: Raw,
|
||||
transformation: Transformation,
|
||||
},
|
||||
}
|
||||
|
||||
/// The regular variant of the [Fira Sans] font.
|
||||
///
|
||||
/// It is loaded as part of the default fonts in Wasm builds.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue