Skip to content

Commit

Permalink
Refactor texture image filtering
Browse files Browse the repository at this point in the history
- Support only `Linear` or `Nearest`
- Simplify `Layer` groups
- Move `FilterMethod` to `Image` and `image::Viewer`
  • Loading branch information
hecrj committed Nov 11, 2023
1 parent 75c9afc commit a5125d6
Show file tree
Hide file tree
Showing 12 changed files with 254 additions and 164 deletions.
49 changes: 16 additions & 33 deletions core/src/image.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,31 +5,11 @@ 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 {
Expand Down Expand Up @@ -76,7 +56,6 @@ impl Handle {
Handle {
id: hasher.finish(),
data,
filter: TextureFilter::default(),
}
}

Expand All @@ -89,17 +68,6 @@ 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<T> From<T> for Handle
Expand Down Expand Up @@ -196,6 +164,16 @@ impl std::fmt::Debug for Data {
}
}

/// Image filtering strategy.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum FilterMethod {
/// Bilinear interpolation.
#[default]
Linear,
/// Nearest neighbor.
Nearest,
}

/// A [`Renderer`] that can render raster graphics.
///
/// [renderer]: crate::renderer
Expand All @@ -210,5 +188,10 @@ pub trait Renderer: crate::Renderer {

/// Draws an image with the given [`Handle`] and inside the provided
/// `bounds`.
fn draw(&mut self, handle: Self::Handle, bounds: Rectangle);
fn draw(
&mut self,
handle: Self::Handle,
filter_method: FilterMethod,
bounds: Rectangle,
);
}
44 changes: 37 additions & 7 deletions examples/tour/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use iced::alignment;
use iced::alignment::{self, Alignment};
use iced::theme;
use iced::widget::{
checkbox, column, container, horizontal_space, image, radio, row,
Expand Down Expand Up @@ -126,7 +126,10 @@ impl Steps {
Step::Toggler {
can_continue: false,
},
Step::Image { width: 300 },
Step::Image {
width: 300,
filter_method: image::FilterMethod::Linear,
},
Step::Scrollable,
Step::TextInput {
value: String::new(),
Expand Down Expand Up @@ -195,6 +198,7 @@ enum Step {
},
Image {
width: u16,
filter_method: image::FilterMethod,
},
Scrollable,
TextInput {
Expand All @@ -215,6 +219,7 @@ pub enum StepMessage {
TextColorChanged(Color),
LanguageSelected(Language),
ImageWidthChanged(u16),
ImageUseNearestToggled(bool),
InputChanged(String),
ToggleSecureInput(bool),
ToggleTextInputIcon(bool),
Expand Down Expand Up @@ -265,6 +270,15 @@ impl<'a> Step {
*width = new_width;
}
}
StepMessage::ImageUseNearestToggled(use_nearest) => {
if let Step::Image { filter_method, .. } = self {
*filter_method = if use_nearest {
image::FilterMethod::Nearest
} else {
image::FilterMethod::Linear
};
}
}
StepMessage::InputChanged(new_value) => {
if let Step::TextInput { value, .. } = self {
*value = new_value;
Expand Down Expand Up @@ -330,7 +344,10 @@ impl<'a> Step {
Step::Toggler { can_continue } => Self::toggler(*can_continue),
Step::Slider { value } => Self::slider(*value),
Step::Text { size, color } => Self::text(*size, *color),
Step::Image { width } => Self::image(*width),
Step::Image {
width,
filter_method,
} => Self::image(*width, *filter_method),
Step::RowsAndColumns { layout, spacing } => {
Self::rows_and_columns(*layout, *spacing)
}
Expand Down Expand Up @@ -525,16 +542,25 @@ impl<'a> Step {
)
}

fn image(width: u16) -> Column<'a, StepMessage> {
fn image(
width: u16,
filter_method: image::FilterMethod,
) -> Column<'a, StepMessage> {
Self::container("Image")
.push("An image that tries to keep its aspect ratio.")
.push(ferris(width))
.push(ferris(width, filter_method))
.push(slider(100..=500, width, StepMessage::ImageWidthChanged))
.push(
text(format!("Width: {width} px"))
.width(Length::Fill)
.horizontal_alignment(alignment::Horizontal::Center),
)
.push(checkbox(
"Use nearest interpolation",
filter_method == image::FilterMethod::Nearest,
StepMessage::ImageUseNearestToggled,
))
.align_items(Alignment::Center)
}

fn scrollable() -> Column<'a, StepMessage> {
Expand All @@ -555,7 +581,7 @@ impl<'a> Step {
.horizontal_alignment(alignment::Horizontal::Center),
)
.push(vertical_space(4096))
.push(ferris(300))
.push(ferris(300, image::FilterMethod::Linear))
.push(
text("You made it!")
.width(Length::Fill)
Expand Down Expand Up @@ -646,7 +672,10 @@ impl<'a> Step {
}
}

fn ferris<'a>(width: u16) -> Container<'a, StepMessage> {
fn ferris<'a>(
width: u16,
filter_method: image::FilterMethod,
) -> Container<'a, StepMessage> {
container(
// This should go away once we unify resource loading on native
// platforms
Expand All @@ -655,6 +684,7 @@ fn ferris<'a>(width: u16) -> Container<'a, StepMessage> {
} else {
image(format!("{}/images/ferris.png", env!("CARGO_MANIFEST_DIR")))
}
.filter_method(filter_method)
.width(width),
)
.width(Length::Fill)
Expand Down
2 changes: 2 additions & 0 deletions graphics/src/primitive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ pub enum Primitive<T> {
Image {
/// The handle of the image
handle: image::Handle,
/// The filter method of the image
filter_method: image::FilterMethod,
/// The bounds of the image
bounds: Rectangle,
},
Expand Down
13 changes: 11 additions & 2 deletions graphics/src/renderer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -215,8 +215,17 @@ where
self.backend().dimensions(handle)
}

fn draw(&mut self, handle: image::Handle, bounds: Rectangle) {
self.primitives.push(Primitive::Image { handle, bounds });
fn draw(
&mut self,
handle: image::Handle,
filter_method: image::FilterMethod,
bounds: Rectangle,
) {
self.primitives.push(Primitive::Image {
handle,
filter_method,
bounds,
});
}
}

Expand Down
9 changes: 7 additions & 2 deletions renderer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -214,8 +214,13 @@ impl<T> crate::core::image::Renderer for Renderer<T> {
delegate!(self, renderer, renderer.dimensions(handle))
}

fn draw(&mut self, handle: crate::core::image::Handle, bounds: Rectangle) {
delegate!(self, renderer, renderer.draw(handle, bounds));
fn draw(
&mut self,
handle: crate::core::image::Handle,
filter_method: crate::core::image::FilterMethod,
bounds: Rectangle,
) {
delegate!(self, renderer, renderer.draw(handle, filter_method, bounds));
}
}

Expand Down
16 changes: 13 additions & 3 deletions tiny_skia/src/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -445,7 +445,11 @@ impl Backend {
);
}
#[cfg(feature = "image")]
Primitive::Image { handle, bounds } => {
Primitive::Image {
handle,
filter_method,
bounds,
} => {
let physical_bounds = (*bounds + translation) * scale_factor;

if !clip_bounds.intersects(&physical_bounds) {
Expand All @@ -461,8 +465,14 @@ impl Backend {
)
.post_scale(scale_factor, scale_factor);

self.raster_pipeline
.draw(handle, *bounds, pixels, transform, clip_mask);
self.raster_pipeline.draw(
handle,
*filter_method,
*bounds,
pixels,
transform,
clip_mask,
);
}
#[cfg(not(feature = "image"))]
Primitive::Image { .. } => {
Expand Down
3 changes: 2 additions & 1 deletion tiny_skia/src/raster.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ impl Pipeline {
pub fn draw(
&mut self,
handle: &raster::Handle,
filter_method: raster::FilterMethod,
bounds: Rectangle,
pixels: &mut tiny_skia::PixmapMut<'_>,
transform: tiny_skia::Transform,
Expand All @@ -39,7 +40,7 @@ impl Pipeline {

let transform = transform.pre_scale(width_scale, height_scale);

let quality = match handle.filter().mag {
let quality = match filter_method {
raster::FilterMethod::Linear => {
tiny_skia::FilterQuality::Bilinear
}
Expand Down
Loading

0 comments on commit a5125d6

Please sign in to comment.