summaryrefslogtreecommitdiffstats
path: root/graphics/src
diff options
context:
space:
mode:
Diffstat (limited to 'graphics/src')
-rw-r--r--graphics/src/cached.rs8
-rw-r--r--graphics/src/geometry/cache.rs39
-rw-r--r--graphics/src/geometry/frame.rs16
-rw-r--r--graphics/src/image.rs156
-rw-r--r--graphics/src/lib.rs6
-rw-r--r--graphics/src/mesh.rs94
-rw-r--r--graphics/src/renderer.rs4
-rw-r--r--graphics/src/text.rs55
8 files changed, 272 insertions, 106 deletions
diff --git a/graphics/src/cached.rs b/graphics/src/cached.rs
index b52f9d9d..1ba82f9f 100644
--- a/graphics/src/cached.rs
+++ b/graphics/src/cached.rs
@@ -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 {}
}
diff --git a/graphics/src/geometry/cache.rs b/graphics/src/geometry/cache.rs
index 37d433c2..665e996b 100644
--- a/graphics/src/geometry/cache.rs
+++ b/graphics/src/geometry/cache.rs
@@ -22,13 +22,20 @@ where
/// Creates a new empty [`Cache`].
pub fn new() -> Self {
Cache {
- state: RefCell::new(State::Empty),
+ state: RefCell::new(State::Empty { previous: None }),
}
}
/// Clears the [`Cache`], forcing a redraw the next time it is used.
pub fn clear(&self) {
- *self.state.borrow_mut() = State::Empty;
+ use std::ops::Deref;
+
+ let previous = match self.state.borrow().deref() {
+ State::Empty { previous } => previous.clone(),
+ State::Filled { geometry, .. } => Some(geometry.clone()),
+ };
+
+ *self.state.borrow_mut() = State::Empty { previous };
}
/// Draws geometry using the provided closure and stores it in the
@@ -49,20 +56,24 @@ where
) -> Renderer::Geometry {
use std::ops::Deref;
- if let State::Filled {
- bounds: cached_bounds,
- geometry,
- } = self.state.borrow().deref()
- {
- if *cached_bounds == bounds {
- return Cached::load(geometry);
+ let previous = match self.state.borrow().deref() {
+ State::Empty { previous } => previous.clone(),
+ State::Filled {
+ bounds: cached_bounds,
+ geometry,
+ } => {
+ if *cached_bounds == bounds {
+ return Cached::load(geometry);
+ }
+
+ Some(geometry.clone())
}
- }
+ };
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 };
@@ -79,7 +90,7 @@ where
let state = self.state.borrow();
match *state {
- State::Empty => write!(f, "Cache::Empty"),
+ State::Empty { .. } => write!(f, "Cache::Empty"),
State::Filled { bounds, .. } => {
write!(f, "Cache::Filled {{ bounds: {bounds:?} }}")
}
@@ -100,7 +111,9 @@ enum State<Geometry>
where
Geometry: Cached,
{
- Empty,
+ Empty {
+ previous: Option<Geometry::Cache>,
+ },
Filled {
bounds: Size,
geometry: Geometry::Cache,
diff --git a/graphics/src/geometry/frame.rs b/graphics/src/geometry/frame.rs
index ad35e8ec..377589d7 100644
--- a/graphics/src/geometry/frame.rs
+++ b/graphics/src/geometry/frame.rs
@@ -113,13 +113,11 @@ where
region: Rectangle,
f: impl FnOnce(&mut Self) -> R,
) -> R {
- let mut frame = self.draft(region.size());
+ let mut frame = self.draft(region);
let result = f(&mut frame);
- let origin = Point::new(region.x, region.y);
-
- self.paste(frame, origin);
+ self.paste(frame, Point::new(region.x, region.y));
result
}
@@ -129,14 +127,14 @@ where
/// Draw its contents back to this [`Frame`] with [`paste`].
///
/// [`paste`]: Self::paste
- pub fn draft(&mut self, size: Size) -> Self {
+ fn draft(&mut self, clip_bounds: Rectangle) -> Self {
Self {
- raw: self.raw.draft(size),
+ raw: self.raw.draft(clip_bounds),
}
}
/// Draws the contents of the given [`Frame`] with origin at the given [`Point`].
- pub fn paste(&mut self, frame: Self, at: Point) {
+ fn paste(&mut self, frame: Self, at: Point) {
self.raw.paste(frame.raw, at);
}
@@ -187,7 +185,7 @@ pub trait Backend: Sized {
fn scale(&mut self, scale: impl Into<f32>);
fn scale_nonuniform(&mut self, scale: impl Into<Vector>);
- fn draft(&mut self, size: Size) -> Self;
+ fn draft(&mut self, clip_bounds: Rectangle) -> Self;
fn paste(&mut self, frame: Self, at: Point);
fn stroke<'a>(&mut self, path: &Path, stroke: impl Into<Stroke<'a>>);
@@ -232,7 +230,7 @@ impl Backend for () {
fn scale(&mut self, _scale: impl Into<f32>) {}
fn scale_nonuniform(&mut self, _scale: impl Into<Vector>) {}
- fn draft(&mut self, _size: Size) -> Self {}
+ fn draft(&mut self, _clip_bounds: Rectangle) -> Self {}
fn paste(&mut self, _frame: Self, _at: Point) {}
fn stroke<'a>(&mut self, _path: &Path, _stroke: impl Into<Stroke<'a>>) {}
diff --git a/graphics/src/image.rs b/graphics/src/image.rs
index d89caace..47a7b30e 100644
--- a/graphics/src/image.rs
+++ b/graphics/src/image.rs
@@ -1,14 +1,96 @@
//! Load and operate on images.
-use crate::core::image::{Data, Handle};
+#[cfg(feature = "image")]
+pub use ::image as image_rs;
-use bitflags::bitflags;
+use crate::core::image;
+use crate::core::svg;
+use crate::core::{Color, Rectangle};
-pub use ::image as image_rs;
+/// 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> {
+///
+/// [`Handle`]: image::Handle
+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 +101,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 +110,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
- }
-}
diff --git a/graphics/src/lib.rs b/graphics/src/lib.rs
index 18bfd981..b79ef70d 100644
--- a/graphics/src/lib.rs
+++ b/graphics/src/lib.rs
@@ -20,6 +20,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;
@@ -27,9 +28,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;
@@ -37,10 +35,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;
diff --git a/graphics/src/mesh.rs b/graphics/src/mesh.rs
index 20692b07..76602319 100644
--- a/graphics/src/mesh.rs
+++ b/graphics/src/mesh.rs
@@ -1,8 +1,7 @@
//! Draw triangles!
use crate::color;
-use crate::core::{Rectangle, Size};
+use crate::core::{Rectangle, Transformation};
use crate::gradient;
-use crate::Damage;
use bytemuck::{Pod, Zeroable};
@@ -14,29 +13,55 @@ 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.
- size: Size,
+ /// The [`Transformation`] for the vertices of the [`Mesh`].
+ transformation: Transformation,
+
+ /// The clip bounds of the [`Mesh`].
+ clip_bounds: Rectangle,
},
/// 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,
+ /// The [`Transformation`] for the vertices of the [`Mesh`].
+ transformation: Transformation,
+
+ /// The clip bounds of the [`Mesh`].
+ clip_bounds: Rectangle,
},
}
-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 { 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, .. } | Self::Gradient { size, .. } => {
- Rectangle::with_size(*size)
+ Self::Solid {
+ clip_bounds,
+ transformation,
+ ..
}
+ | Self::Gradient {
+ clip_bounds,
+ transformation,
+ ..
+ } => *clip_bounds * *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`].
diff --git a/graphics/src/renderer.rs b/graphics/src/renderer.rs
index fb1a0d73..d4f91dab 100644
--- a/graphics/src/renderer.rs
+++ b/graphics/src/renderer.rs
@@ -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));
}
diff --git a/graphics/src/text.rs b/graphics/src/text.rs
index 0310ead7..f9fc1fec 100644
--- a/graphics/src/text.rs
+++ b/graphics/src/text.rs
@@ -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::{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: Pixels,
+ /// 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.