diff options
| author | 2023-03-07 03:47:49 +0100 | |
|---|---|---|
| committer | 2023-03-07 03:47:49 +0100 | |
| commit | 3a26baa564524b0f25c5cb180b592c8b004b68a9 (patch) | |
| tree | c6a51e9116e7f29552130778fe071efa9b1d1262 /graphics/src | |
| parent | 9b4bcd287a7f4822314e158990d1dc023d5aab51 (diff) | |
| download | iced-3a26baa564524b0f25c5cb180b592c8b004b68a9.tar.gz iced-3a26baa564524b0f25c5cb180b592c8b004b68a9.tar.bz2 iced-3a26baa564524b0f25c5cb180b592c8b004b68a9.zip | |
Remove `image` abstractions in `iced_graphics`
Diffstat (limited to '')
| -rw-r--r-- | graphics/src/image.rs | 98 | ||||
| -rw-r--r-- | graphics/src/image/raster.rs | 242 | ||||
| -rw-r--r-- | graphics/src/lib.rs | 4 | ||||
| -rw-r--r-- | wgpu/src/image/vector.rs (renamed from graphics/src/image/vector.rs) | 43 | 
4 files changed, 111 insertions, 276 deletions
| diff --git a/graphics/src/image.rs b/graphics/src/image.rs index 04f4ff9d..2f634252 100644 --- a/graphics/src/image.rs +++ b/graphics/src/image.rs @@ -1,10 +1,94 @@ -//! Render images. -#[cfg(feature = "image_rs")] -pub mod raster; +//! Load and operate on images. +use crate::core::image::{Data, Handle}; -#[cfg(feature = "svg")] -pub mod vector; +use bitflags::bitflags; -pub mod storage; +pub use ::image as image_rs; -pub use storage::Storage; +pub fn load(handle: &Handle) -> image_rs::ImageResult<image_rs::DynamicImage> { +    match handle.data() { +        Data::Path(path) => { +            let image = ::image::open(path)?; + +            let operation = std::fs::File::open(path) +                .ok() +                .map(std::io::BufReader::new) +                .and_then(|mut reader| Operation::from_exif(&mut reader).ok()) +                .unwrap_or_else(Operation::empty); + +            Ok(operation.perform(image)) +        } +        Data::Bytes(bytes) => { +            let image = ::image::load_from_memory(bytes)?; +            let operation = +                Operation::from_exif(&mut std::io::Cursor::new(bytes)) +                    .ok() +                    .unwrap_or_else(Operation::empty); + +            Ok(operation.perform(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)) +            } else { +                Err(image_rs::error::ImageError::Limits( +                    image_rs::error::LimitError::from_kind( +                        image_rs::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/image/raster.rs b/graphics/src/image/raster.rs deleted file mode 100644 index 03211160..00000000 --- a/graphics/src/image/raster.rs +++ /dev/null @@ -1,242 +0,0 @@ -//! Raster image loading and caching. -use crate::image::Storage; - -use iced_core::image; -use iced_core::Size; - -use bitflags::bitflags; -use std::collections::{HashMap, HashSet}; - -/// Entry in cache corresponding to an image handle -#[derive(Debug)] -pub enum Memory<T: Storage> { -    /// Image data on host -    Host(::image_rs::ImageBuffer<::image_rs::Rgba<u8>, Vec<u8>>), -    /// Storage entry -    Device(T::Entry), -    /// Image not found -    NotFound, -    /// Invalid image data -    Invalid, -} - -impl<T: Storage> Memory<T> { -    /// Width and height of image -    pub fn dimensions(&self) -> Size<u32> { -        use crate::image::storage::Entry; - -        match self { -            Memory::Host(image) => { -                let (width, height) = image.dimensions(); - -                Size::new(width, height) -            } -            Memory::Device(entry) => entry.size(), -            Memory::NotFound => Size::new(1, 1), -            Memory::Invalid => Size::new(1, 1), -        } -    } -} - -/// Caches image raster data -#[derive(Debug)] -pub struct Cache<T: Storage> { -    map: HashMap<u64, Memory<T>>, -    hits: HashSet<u64>, -} - -impl<T: Storage> Cache<T> { -    /// Load image -    pub fn load(&mut self, handle: &image::Handle) -> &mut Memory<T> { -        if self.contains(handle) { -            return self.get(handle).unwrap(); -        } - -        let memory = match handle.data() { -            image::Data::Path(path) => { -                if let Ok(image) = image_rs::open(path) { -                    let operation = std::fs::File::open(path) -                        .ok() -                        .map(std::io::BufReader::new) -                        .and_then(|mut reader| { -                            Operation::from_exif(&mut reader).ok() -                        }) -                        .unwrap_or_else(Operation::empty); - -                    Memory::Host(operation.perform(image.to_rgba8())) -                } else { -                    Memory::NotFound -                } -            } -            image::Data::Bytes(bytes) => { -                if let Ok(image) = image_rs::load_from_memory(bytes) { -                    let operation = -                        Operation::from_exif(&mut std::io::Cursor::new(bytes)) -                            .ok() -                            .unwrap_or_else(Operation::empty); - -                    Memory::Host(operation.perform(image.to_rgba8())) -                } else { -                    Memory::Invalid -                } -            } -            image::Data::Rgba { -                width, -                height, -                pixels, -            } => { -                if let Some(image) = image_rs::ImageBuffer::from_vec( -                    *width, -                    *height, -                    pixels.to_vec(), -                ) { -                    Memory::Host(image) -                } else { -                    Memory::Invalid -                } -            } -        }; - -        self.insert(handle, memory); -        self.get(handle).unwrap() -    } - -    /// Load image and upload raster data -    pub fn upload( -        &mut self, -        handle: &image::Handle, -        state: &mut T::State<'_>, -        storage: &mut T, -    ) -> Option<&T::Entry> { -        let memory = self.load(handle); - -        if let Memory::Host(image) = memory { -            let (width, height) = image.dimensions(); - -            let entry = storage.upload(width, height, image, state)?; - -            *memory = Memory::Device(entry); -        } - -        if let Memory::Device(allocation) = memory { -            Some(allocation) -        } else { -            None -        } -    } - -    /// Trim cache misses from cache -    pub fn trim(&mut self, storage: &mut T, state: &mut T::State<'_>) { -        let hits = &self.hits; - -        self.map.retain(|k, memory| { -            let retain = hits.contains(k); - -            if !retain { -                if let Memory::Device(entry) = memory { -                    storage.remove(entry, state); -                } -            } - -            retain -        }); - -        self.hits.clear(); -    } - -    fn get(&mut self, handle: &image::Handle) -> Option<&mut Memory<T>> { -        let _ = self.hits.insert(handle.id()); - -        self.map.get_mut(&handle.id()) -    } - -    fn insert(&mut self, handle: &image::Handle, memory: Memory<T>) { -        let _ = self.map.insert(handle.id(), memory); -    } - -    fn contains(&self, handle: &image::Handle) -> bool { -        self.map.contains_key(&handle.id()) -    } -} - -impl<T: Storage> Default for Cache<T> { -    fn default() -> Self { -        Self { -            map: HashMap::new(), -            hits: HashSet::new(), -        } -    } -} - -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<P>( -        self, -        image: image_rs::ImageBuffer<P, Vec<P::Subpixel>>, -    ) -> image_rs::ImageBuffer<P, Vec<P::Subpixel>> -    where -        P: image_rs::Pixel + 'static, -    { -        use image_rs::imageops; - -        let mut image = if self.contains(Self::FLIP_DIAGONALLY) { -            flip_diagonally(image) -        } else { -            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 -    } -} - -fn flip_diagonally<I>( -    image: I, -) -> image_rs::ImageBuffer<I::Pixel, Vec<<I::Pixel as image_rs::Pixel>::Subpixel>> -where -    I: image_rs::GenericImage, -    I::Pixel: 'static, -{ -    let (width, height) = image.dimensions(); -    let mut out = image_rs::ImageBuffer::new(height, width); - -    for x in 0..width { -        for y in 0..height { -            let p = image.get_pixel(x, y); - -            out.put_pixel(y, x, p); -        } -    } - -    out -} diff --git a/graphics/src/lib.rs b/graphics/src/lib.rs index c6f9cf57..0c50db52 100644 --- a/graphics/src/lib.rs +++ b/graphics/src/lib.rs @@ -28,13 +28,15 @@ mod viewport;  pub mod backend;  pub mod compositor; -pub mod image;  pub mod primitive;  pub mod renderer;  #[cfg(feature = "geometry")]  pub mod geometry; +#[cfg(feature = "image")] +pub mod image; +  pub use antialiasing::Antialiasing;  pub use backend::Backend;  pub use compositor::Compositor; diff --git a/graphics/src/image/vector.rs b/wgpu/src/image/vector.rs index 32729acd..3624e46b 100644 --- a/graphics/src/image/vector.rs +++ b/wgpu/src/image/vector.rs @@ -1,8 +1,6 @@ -//! Vector image loading and caching -use crate::image::Storage; - -use iced_core::svg; -use iced_core::{Color, Size}; +use crate::core::svg; +use crate::core::{Color, Size}; +use crate::image::atlas::{self, Atlas};  use resvg::tiny_skia;  use resvg::usvg; @@ -32,17 +30,17 @@ impl Svg {  }  /// Caches svg vector and raster data -#[derive(Debug)] -pub struct Cache<T: Storage> { +#[derive(Debug, Default)] +pub struct Cache {      svgs: HashMap<u64, Svg>, -    rasterized: HashMap<(u64, u32, u32, ColorFilter), T::Entry>, +    rasterized: HashMap<(u64, u32, u32, ColorFilter), atlas::Entry>,      svg_hits: HashSet<u64>,      rasterized_hits: HashSet<(u64, u32, u32, ColorFilter)>,  }  type ColorFilter = Option<[u8; 4]>; -impl<T: Storage> Cache<T> { +impl Cache {      /// Load svg      pub fn load(&mut self, handle: &svg::Handle) -> &Svg {          if self.svgs.contains_key(&handle.id()) { @@ -73,13 +71,15 @@ impl<T: Storage> Cache<T> {      /// Load svg and upload raster data      pub fn upload(          &mut self, +        device: &wgpu::Device, +        queue: &wgpu::Queue, +        encoder: &mut wgpu::CommandEncoder,          handle: &svg::Handle,          color: Option<Color>,          [width, height]: [f32; 2],          scale: f32, -        state: &mut T::State<'_>, -        storage: &mut T, -    ) -> Option<&T::Entry> { +        atlas: &mut Atlas, +    ) -> Option<&atlas::Entry> {          let id = handle.id();          let (width, height) = ( @@ -136,7 +136,9 @@ impl<T: Storage> Cache<T> {                      });                  } -                let allocation = storage.upload(width, height, &rgba, state)?; +                let allocation = atlas +                    .upload(device, queue, encoder, width, height, &rgba)?; +                  log::debug!("allocating {} {}x{}", id, width, height);                  let _ = self.svg_hits.insert(id); @@ -150,7 +152,7 @@ impl<T: Storage> Cache<T> {      }      /// Load svg and upload raster data -    pub fn trim(&mut self, storage: &mut T, state: &mut T::State<'_>) { +    pub fn trim(&mut self, atlas: &mut Atlas) {          let svg_hits = &self.svg_hits;          let rasterized_hits = &self.rasterized_hits; @@ -159,7 +161,7 @@ impl<T: Storage> Cache<T> {              let retain = rasterized_hits.contains(k);              if !retain { -                storage.remove(entry, state); +                atlas.remove(entry);              }              retain @@ -169,17 +171,6 @@ impl<T: Storage> Cache<T> {      }  } -impl<T: Storage> Default for Cache<T> { -    fn default() -> Self { -        Self { -            svgs: HashMap::new(), -            rasterized: HashMap::new(), -            svg_hits: HashSet::new(), -            rasterized_hits: HashSet::new(), -        } -    } -} -  impl std::fmt::Debug for Svg {      fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {          match self { | 
