diff --git a/core/src/image.rs b/core/src/image.rs index 85d9d4758c..9a6011a304 100644 --- a/core/src/image.rs +++ b/core/src/image.rs @@ -5,11 +5,31 @@ use std::hash::{Hash, Hasher as _}; use std::path::PathBuf; use std::sync::Arc; +/// Image filter method +#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum FilterMethod { + /// Bilinear interpolation + #[default] + Linear, + /// Nearest Neighbor + Nearest, +} + +/// Texture filter settings +#[derive(Default, Debug, Clone, PartialEq, Eq, Hash)] +pub struct TextureFilter { + /// Filter for scaling the image down. + pub min: FilterMethod, + /// Filter for scaling the image up. + pub mag: FilterMethod, +} + /// A handle of some image data. #[derive(Debug, Clone, PartialEq, Eq)] pub struct Handle { id: u64, data: Data, + filter: TextureFilter, } impl Handle { @@ -56,6 +76,7 @@ impl Handle { Handle { id: hasher.finish(), data, + filter: TextureFilter::default(), } } @@ -68,6 +89,17 @@ impl Handle { pub fn data(&self) -> &Data { &self.data } + + /// Returns a reference to the [`TextureFilter`]. + pub fn filter(&self) -> &TextureFilter { + &self.filter + } + + /// Sets the texture filtering methods. + pub fn set_filter(mut self, filter: TextureFilter) -> Self { + self.filter = filter; + self + } } impl From for Handle diff --git a/tiny_skia/src/raster.rs b/tiny_skia/src/raster.rs index 3887ec8dc0..32427a6dcc 100644 --- a/tiny_skia/src/raster.rs +++ b/tiny_skia/src/raster.rs @@ -39,12 +39,17 @@ impl Pipeline { let transform = transform.pre_scale(width_scale, height_scale); + let quality = match handle.filter().mag { + raster::FilterMethod::Linear => tiny_skia::FilterQuality::Bilinear, + raster::FilterMethod::Nearest => tiny_skia::FilterQuality::Nearest, + }; + pixels.draw_pixmap( (bounds.x / width_scale) as i32, (bounds.y / height_scale) as i32, image, &tiny_skia::PixmapPaint { - quality: tiny_skia::FilterQuality::Bilinear, + quality: quality, ..Default::default() }, transform, diff --git a/wgpu/src/image.rs b/wgpu/src/image.rs index 553ba33057..a0fe7e8313 100644 --- a/wgpu/src/image.rs +++ b/wgpu/src/image.rs @@ -7,6 +7,7 @@ mod raster; mod vector; use atlas::Atlas; +use iced_graphics::core::image::{TextureFilter, FilterMethod}; use crate::core::{Rectangle, Size}; use crate::graphics::Transformation; @@ -14,6 +15,7 @@ use crate::layer; use crate::Buffer; use std::cell::RefCell; +use std::collections::HashMap; use std::mem; use bytemuck::{Pod, Zeroable}; @@ -37,7 +39,7 @@ pub struct Pipeline { pipeline: wgpu::RenderPipeline, vertices: wgpu::Buffer, indices: wgpu::Buffer, - sampler: wgpu::Sampler, + sampler: HashMap, texture: wgpu::BindGroup, texture_version: usize, texture_atlas: Atlas, @@ -142,15 +144,32 @@ impl Pipeline { pub fn new(device: &wgpu::Device, format: wgpu::TextureFormat) -> Self { use wgpu::util::DeviceExt; - let sampler = device.create_sampler(&wgpu::SamplerDescriptor { - address_mode_u: wgpu::AddressMode::ClampToEdge, - address_mode_v: wgpu::AddressMode::ClampToEdge, - address_mode_w: wgpu::AddressMode::ClampToEdge, - mag_filter: wgpu::FilterMode::Linear, - min_filter: wgpu::FilterMode::Linear, - mipmap_filter: wgpu::FilterMode::Linear, - ..Default::default() - }); + let to_wgpu = |method: FilterMethod| { + match method { + FilterMethod::Linear => wgpu::FilterMode::Linear, + FilterMethod::Nearest => wgpu::FilterMode::Nearest, + } + }; + + let mut sampler = HashMap::new(); + + let filter = [FilterMethod::Linear, FilterMethod::Nearest]; + for min in 0..filter.len() { + for mag in 0..filter.len() { + let _ = sampler.insert(TextureFilter {min: filter[min], mag: filter[mag]}, + device.create_sampler(&wgpu::SamplerDescriptor { + address_mode_u: wgpu::AddressMode::ClampToEdge, + address_mode_v: wgpu::AddressMode::ClampToEdge, + address_mode_w: wgpu::AddressMode::ClampToEdge, + mag_filter: to_wgpu(filter[mag]), + min_filter: to_wgpu(filter[min]), + mipmap_filter: wgpu::FilterMode::Linear, + ..Default::default() + } + )); + } + } + let constant_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { @@ -355,7 +374,7 @@ impl Pipeline { #[cfg(feature = "tracing")] let _ = info_span!("Wgpu::Image", "DRAW").entered(); - let instances: &mut Vec = &mut Vec::new(); + let instances: &mut HashMap> = &mut HashMap::new(); #[cfg(feature = "image")] let mut raster_cache = self.raster_cache.borrow_mut(); @@ -377,7 +396,7 @@ impl Pipeline { [bounds.x, bounds.y], [bounds.width, bounds.height], atlas_entry, - instances, + instances.entry(handle.filter().clone()).or_insert(Vec::new()), ); } } @@ -405,7 +424,7 @@ impl Pipeline { [bounds.x, bounds.y], size, atlas_entry, - instances, + instances.entry(TextureFilter::default()).or_insert(Vec::new()), ); } } @@ -438,18 +457,20 @@ impl Pipeline { self.texture_version = texture_version; } - if self.layers.len() <= self.prepare_layer { - self.layers.push(Layer::new( - device, - &self.constant_layout, - &self.sampler, - )); + for (filter, instances) in instances.iter_mut() { + if self.layers.len() <= self.prepare_layer { + self.layers.push(Layer::new( + device, + &self.constant_layout, + &self.sampler.get(filter).expect("Sampler is registered"), + )); + } + + let layer = &mut self.layers[self.prepare_layer]; + layer.prepare(device, queue, &instances, transformation); + + self.prepare_layer += 1; } - - let layer = &mut self.layers[self.prepare_layer]; - layer.prepare(device, queue, instances, transformation); - - self.prepare_layer += 1; } pub fn render<'a>( diff --git a/widget/src/image.rs b/widget/src/image.rs index abcb6ef22b..e2fd84a1d4 100644 --- a/widget/src/image.rs +++ b/widget/src/image.rs @@ -12,7 +12,7 @@ use crate::core::{ use std::hash::Hash; -pub use image::Handle; +pub use image::{Handle, TextureFilter, FilterMethod}; /// Creates a new [`Viewer`] with the given image `Handle`. pub fn viewer(handle: Handle) -> Viewer {