Refactor texture image filtering
- Support only `Linear` or `Nearest` - Simplify `Layer` groups - Move `FilterMethod` to `Image` and `image::Viewer`
This commit is contained in:
parent
75c9afc608
commit
a5125d6fea
12 changed files with 250 additions and 160 deletions
|
|
@ -5,31 +5,11 @@ use std::hash::{Hash, Hasher as _};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::sync::Arc;
|
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.
|
/// A handle of some image data.
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct Handle {
|
pub struct Handle {
|
||||||
id: u64,
|
id: u64,
|
||||||
data: Data,
|
data: Data,
|
||||||
filter: TextureFilter,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Handle {
|
impl Handle {
|
||||||
|
|
@ -76,7 +56,6 @@ impl Handle {
|
||||||
Handle {
|
Handle {
|
||||||
id: hasher.finish(),
|
id: hasher.finish(),
|
||||||
data,
|
data,
|
||||||
filter: TextureFilter::default(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -89,17 +68,6 @@ impl Handle {
|
||||||
pub fn data(&self) -> &Data {
|
pub fn data(&self) -> &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
|
impl<T> From<T> for Handle
|
||||||
|
|
@ -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.
|
/// A [`Renderer`] that can render raster graphics.
|
||||||
///
|
///
|
||||||
/// [renderer]: crate::renderer
|
/// [renderer]: crate::renderer
|
||||||
|
|
@ -210,5 +188,10 @@ pub trait Renderer: crate::Renderer {
|
||||||
|
|
||||||
/// Draws an image with the given [`Handle`] and inside the provided
|
/// Draws an image with the given [`Handle`] and inside the provided
|
||||||
/// `bounds`.
|
/// `bounds`.
|
||||||
fn draw(&mut self, handle: Self::Handle, bounds: Rectangle);
|
fn draw(
|
||||||
|
&mut self,
|
||||||
|
handle: Self::Handle,
|
||||||
|
filter_method: FilterMethod,
|
||||||
|
bounds: Rectangle,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use iced::alignment;
|
use iced::alignment::{self, Alignment};
|
||||||
use iced::theme;
|
use iced::theme;
|
||||||
use iced::widget::{
|
use iced::widget::{
|
||||||
checkbox, column, container, horizontal_space, image, radio, row,
|
checkbox, column, container, horizontal_space, image, radio, row,
|
||||||
|
|
@ -126,7 +126,10 @@ impl Steps {
|
||||||
Step::Toggler {
|
Step::Toggler {
|
||||||
can_continue: false,
|
can_continue: false,
|
||||||
},
|
},
|
||||||
Step::Image { width: 300 },
|
Step::Image {
|
||||||
|
width: 300,
|
||||||
|
filter_method: image::FilterMethod::Linear,
|
||||||
|
},
|
||||||
Step::Scrollable,
|
Step::Scrollable,
|
||||||
Step::TextInput {
|
Step::TextInput {
|
||||||
value: String::new(),
|
value: String::new(),
|
||||||
|
|
@ -195,6 +198,7 @@ enum Step {
|
||||||
},
|
},
|
||||||
Image {
|
Image {
|
||||||
width: u16,
|
width: u16,
|
||||||
|
filter_method: image::FilterMethod,
|
||||||
},
|
},
|
||||||
Scrollable,
|
Scrollable,
|
||||||
TextInput {
|
TextInput {
|
||||||
|
|
@ -215,6 +219,7 @@ pub enum StepMessage {
|
||||||
TextColorChanged(Color),
|
TextColorChanged(Color),
|
||||||
LanguageSelected(Language),
|
LanguageSelected(Language),
|
||||||
ImageWidthChanged(u16),
|
ImageWidthChanged(u16),
|
||||||
|
ImageUseNearestToggled(bool),
|
||||||
InputChanged(String),
|
InputChanged(String),
|
||||||
ToggleSecureInput(bool),
|
ToggleSecureInput(bool),
|
||||||
ToggleTextInputIcon(bool),
|
ToggleTextInputIcon(bool),
|
||||||
|
|
@ -265,6 +270,15 @@ impl<'a> Step {
|
||||||
*width = new_width;
|
*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) => {
|
StepMessage::InputChanged(new_value) => {
|
||||||
if let Step::TextInput { value, .. } = self {
|
if let Step::TextInput { value, .. } = self {
|
||||||
*value = new_value;
|
*value = new_value;
|
||||||
|
|
@ -330,7 +344,10 @@ impl<'a> Step {
|
||||||
Step::Toggler { can_continue } => Self::toggler(*can_continue),
|
Step::Toggler { can_continue } => Self::toggler(*can_continue),
|
||||||
Step::Slider { value } => Self::slider(*value),
|
Step::Slider { value } => Self::slider(*value),
|
||||||
Step::Text { size, color } => Self::text(*size, *color),
|
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 } => {
|
Step::RowsAndColumns { layout, spacing } => {
|
||||||
Self::rows_and_columns(*layout, *spacing)
|
Self::rows_and_columns(*layout, *spacing)
|
||||||
}
|
}
|
||||||
|
|
@ -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")
|
Self::container("Image")
|
||||||
.push("An image that tries to keep its aspect ratio.")
|
.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(slider(100..=500, width, StepMessage::ImageWidthChanged))
|
||||||
.push(
|
.push(
|
||||||
text(format!("Width: {width} px"))
|
text(format!("Width: {width} px"))
|
||||||
.width(Length::Fill)
|
.width(Length::Fill)
|
||||||
.horizontal_alignment(alignment::Horizontal::Center),
|
.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> {
|
fn scrollable() -> Column<'a, StepMessage> {
|
||||||
|
|
@ -555,7 +581,7 @@ impl<'a> Step {
|
||||||
.horizontal_alignment(alignment::Horizontal::Center),
|
.horizontal_alignment(alignment::Horizontal::Center),
|
||||||
)
|
)
|
||||||
.push(vertical_space(4096))
|
.push(vertical_space(4096))
|
||||||
.push(ferris(300))
|
.push(ferris(300, image::FilterMethod::Linear))
|
||||||
.push(
|
.push(
|
||||||
text("You made it!")
|
text("You made it!")
|
||||||
.width(Length::Fill)
|
.width(Length::Fill)
|
||||||
|
|
@ -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(
|
container(
|
||||||
// This should go away once we unify resource loading on native
|
// This should go away once we unify resource loading on native
|
||||||
// platforms
|
// platforms
|
||||||
|
|
@ -655,6 +684,7 @@ fn ferris<'a>(width: u16) -> Container<'a, StepMessage> {
|
||||||
} else {
|
} else {
|
||||||
image(format!("{}/images/ferris.png", env!("CARGO_MANIFEST_DIR")))
|
image(format!("{}/images/ferris.png", env!("CARGO_MANIFEST_DIR")))
|
||||||
}
|
}
|
||||||
|
.filter_method(filter_method)
|
||||||
.width(width),
|
.width(width),
|
||||||
)
|
)
|
||||||
.width(Length::Fill)
|
.width(Length::Fill)
|
||||||
|
|
|
||||||
|
|
@ -68,6 +68,8 @@ pub enum Primitive<T> {
|
||||||
Image {
|
Image {
|
||||||
/// The handle of the image
|
/// The handle of the image
|
||||||
handle: image::Handle,
|
handle: image::Handle,
|
||||||
|
/// The filter method of the image
|
||||||
|
filter_method: image::FilterMethod,
|
||||||
/// The bounds of the image
|
/// The bounds of the image
|
||||||
bounds: Rectangle,
|
bounds: Rectangle,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -215,8 +215,17 @@ where
|
||||||
self.backend().dimensions(handle)
|
self.backend().dimensions(handle)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw(&mut self, handle: image::Handle, bounds: Rectangle) {
|
fn draw(
|
||||||
self.primitives.push(Primitive::Image { handle, bounds });
|
&mut self,
|
||||||
|
handle: image::Handle,
|
||||||
|
filter_method: image::FilterMethod,
|
||||||
|
bounds: Rectangle,
|
||||||
|
) {
|
||||||
|
self.primitives.push(Primitive::Image {
|
||||||
|
handle,
|
||||||
|
filter_method,
|
||||||
|
bounds,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -214,8 +214,13 @@ impl<T> crate::core::image::Renderer for Renderer<T> {
|
||||||
delegate!(self, renderer, renderer.dimensions(handle))
|
delegate!(self, renderer, renderer.dimensions(handle))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw(&mut self, handle: crate::core::image::Handle, bounds: Rectangle) {
|
fn draw(
|
||||||
delegate!(self, renderer, renderer.draw(handle, bounds));
|
&mut self,
|
||||||
|
handle: crate::core::image::Handle,
|
||||||
|
filter_method: crate::core::image::FilterMethod,
|
||||||
|
bounds: Rectangle,
|
||||||
|
) {
|
||||||
|
delegate!(self, renderer, renderer.draw(handle, filter_method, bounds));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -445,7 +445,11 @@ impl Backend {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
#[cfg(feature = "image")]
|
#[cfg(feature = "image")]
|
||||||
Primitive::Image { handle, bounds } => {
|
Primitive::Image {
|
||||||
|
handle,
|
||||||
|
filter_method,
|
||||||
|
bounds,
|
||||||
|
} => {
|
||||||
let physical_bounds = (*bounds + translation) * scale_factor;
|
let physical_bounds = (*bounds + translation) * scale_factor;
|
||||||
|
|
||||||
if !clip_bounds.intersects(&physical_bounds) {
|
if !clip_bounds.intersects(&physical_bounds) {
|
||||||
|
|
@ -461,8 +465,14 @@ impl Backend {
|
||||||
)
|
)
|
||||||
.post_scale(scale_factor, scale_factor);
|
.post_scale(scale_factor, scale_factor);
|
||||||
|
|
||||||
self.raster_pipeline
|
self.raster_pipeline.draw(
|
||||||
.draw(handle, *bounds, pixels, transform, clip_mask);
|
handle,
|
||||||
|
*filter_method,
|
||||||
|
*bounds,
|
||||||
|
pixels,
|
||||||
|
transform,
|
||||||
|
clip_mask,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "image"))]
|
#[cfg(not(feature = "image"))]
|
||||||
Primitive::Image { .. } => {
|
Primitive::Image { .. } => {
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ impl Pipeline {
|
||||||
pub fn draw(
|
pub fn draw(
|
||||||
&mut self,
|
&mut self,
|
||||||
handle: &raster::Handle,
|
handle: &raster::Handle,
|
||||||
|
filter_method: raster::FilterMethod,
|
||||||
bounds: Rectangle,
|
bounds: Rectangle,
|
||||||
pixels: &mut tiny_skia::PixmapMut<'_>,
|
pixels: &mut tiny_skia::PixmapMut<'_>,
|
||||||
transform: tiny_skia::Transform,
|
transform: tiny_skia::Transform,
|
||||||
|
|
@ -39,7 +40,7 @@ impl Pipeline {
|
||||||
|
|
||||||
let transform = transform.pre_scale(width_scale, height_scale);
|
let transform = transform.pre_scale(width_scale, height_scale);
|
||||||
|
|
||||||
let quality = match handle.filter().mag {
|
let quality = match filter_method {
|
||||||
raster::FilterMethod::Linear => {
|
raster::FilterMethod::Linear => {
|
||||||
tiny_skia::FilterQuality::Bilinear
|
tiny_skia::FilterQuality::Bilinear
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,6 @@ mod raster;
|
||||||
mod vector;
|
mod vector;
|
||||||
|
|
||||||
use atlas::Atlas;
|
use atlas::Atlas;
|
||||||
use iced_graphics::core::image::{FilterMethod, TextureFilter};
|
|
||||||
use wgpu::Sampler;
|
|
||||||
|
|
||||||
use crate::core::{Rectangle, Size};
|
use crate::core::{Rectangle, Size};
|
||||||
use crate::graphics::Transformation;
|
use crate::graphics::Transformation;
|
||||||
|
|
@ -29,8 +27,6 @@ use crate::core::svg;
|
||||||
#[cfg(feature = "tracing")]
|
#[cfg(feature = "tracing")]
|
||||||
use tracing::info_span;
|
use tracing::info_span;
|
||||||
|
|
||||||
const SAMPLER_COUNT: usize = 4;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Pipeline {
|
pub struct Pipeline {
|
||||||
#[cfg(feature = "image")]
|
#[cfg(feature = "image")]
|
||||||
|
|
@ -41,30 +37,31 @@ pub struct Pipeline {
|
||||||
pipeline: wgpu::RenderPipeline,
|
pipeline: wgpu::RenderPipeline,
|
||||||
vertices: wgpu::Buffer,
|
vertices: wgpu::Buffer,
|
||||||
indices: wgpu::Buffer,
|
indices: wgpu::Buffer,
|
||||||
sampler: [wgpu::Sampler; SAMPLER_COUNT],
|
nearest_sampler: wgpu::Sampler,
|
||||||
|
linear_sampler: wgpu::Sampler,
|
||||||
texture: wgpu::BindGroup,
|
texture: wgpu::BindGroup,
|
||||||
texture_version: usize,
|
texture_version: usize,
|
||||||
texture_atlas: Atlas,
|
texture_atlas: Atlas,
|
||||||
texture_layout: wgpu::BindGroupLayout,
|
texture_layout: wgpu::BindGroupLayout,
|
||||||
constant_layout: wgpu::BindGroupLayout,
|
constant_layout: wgpu::BindGroupLayout,
|
||||||
|
|
||||||
layers: Vec<[Option<Layer>; SAMPLER_COUNT]>,
|
layers: Vec<Layer>,
|
||||||
prepare_layer: usize,
|
prepare_layer: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct Layer {
|
struct Layer {
|
||||||
uniforms: wgpu::Buffer,
|
uniforms: wgpu::Buffer,
|
||||||
constants: wgpu::BindGroup,
|
nearest: Data,
|
||||||
instances: Buffer<Instance>,
|
linear: Data,
|
||||||
instance_count: usize,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Layer {
|
impl Layer {
|
||||||
fn new(
|
fn new(
|
||||||
device: &wgpu::Device,
|
device: &wgpu::Device,
|
||||||
constant_layout: &wgpu::BindGroupLayout,
|
constant_layout: &wgpu::BindGroupLayout,
|
||||||
sampler: &wgpu::Sampler,
|
nearest_sampler: &wgpu::Sampler,
|
||||||
|
linear_sampler: &wgpu::Sampler,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let uniforms = device.create_buffer(&wgpu::BufferDescriptor {
|
let uniforms = device.create_buffer(&wgpu::BufferDescriptor {
|
||||||
label: Some("iced_wgpu::image uniforms buffer"),
|
label: Some("iced_wgpu::image uniforms buffer"),
|
||||||
|
|
@ -73,6 +70,59 @@ impl Layer {
|
||||||
mapped_at_creation: false,
|
mapped_at_creation: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let nearest =
|
||||||
|
Data::new(device, constant_layout, nearest_sampler, &uniforms);
|
||||||
|
|
||||||
|
let linear =
|
||||||
|
Data::new(device, constant_layout, linear_sampler, &uniforms);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
uniforms,
|
||||||
|
nearest,
|
||||||
|
linear,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn prepare(
|
||||||
|
&mut self,
|
||||||
|
device: &wgpu::Device,
|
||||||
|
queue: &wgpu::Queue,
|
||||||
|
nearest_instances: &[Instance],
|
||||||
|
linear_instances: &[Instance],
|
||||||
|
transformation: Transformation,
|
||||||
|
) {
|
||||||
|
queue.write_buffer(
|
||||||
|
&self.uniforms,
|
||||||
|
0,
|
||||||
|
bytemuck::bytes_of(&Uniforms {
|
||||||
|
transform: transformation.into(),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
self.nearest.upload(device, queue, nearest_instances);
|
||||||
|
self.linear.upload(device, queue, linear_instances);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render<'a>(&'a self, render_pass: &mut wgpu::RenderPass<'a>) {
|
||||||
|
self.nearest.render(render_pass);
|
||||||
|
self.linear.render(render_pass);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Data {
|
||||||
|
constants: wgpu::BindGroup,
|
||||||
|
instances: Buffer<Instance>,
|
||||||
|
instance_count: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Data {
|
||||||
|
pub fn new(
|
||||||
|
device: &wgpu::Device,
|
||||||
|
constant_layout: &wgpu::BindGroupLayout,
|
||||||
|
sampler: &wgpu::Sampler,
|
||||||
|
uniforms: &wgpu::Buffer,
|
||||||
|
) -> Self {
|
||||||
let constants = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
let constants = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||||
label: Some("iced_wgpu::image constants bind group"),
|
label: Some("iced_wgpu::image constants bind group"),
|
||||||
layout: constant_layout,
|
layout: constant_layout,
|
||||||
|
|
@ -102,28 +152,18 @@ impl Layer {
|
||||||
);
|
);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
uniforms,
|
|
||||||
constants,
|
constants,
|
||||||
instances,
|
instances,
|
||||||
instance_count: 0,
|
instance_count: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prepare(
|
fn upload(
|
||||||
&mut self,
|
&mut self,
|
||||||
device: &wgpu::Device,
|
device: &wgpu::Device,
|
||||||
queue: &wgpu::Queue,
|
queue: &wgpu::Queue,
|
||||||
instances: &[Instance],
|
instances: &[Instance],
|
||||||
transformation: Transformation,
|
|
||||||
) {
|
) {
|
||||||
queue.write_buffer(
|
|
||||||
&self.uniforms,
|
|
||||||
0,
|
|
||||||
bytemuck::bytes_of(&Uniforms {
|
|
||||||
transform: transformation.into(),
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
let _ = self.instances.resize(device, instances.len());
|
let _ = self.instances.resize(device, instances.len());
|
||||||
let _ = self.instances.write(queue, 0, instances);
|
let _ = self.instances.write(queue, 0, instances);
|
||||||
|
|
||||||
|
|
@ -146,37 +186,25 @@ impl Pipeline {
|
||||||
pub fn new(device: &wgpu::Device, format: wgpu::TextureFormat) -> Self {
|
pub fn new(device: &wgpu::Device, format: wgpu::TextureFormat) -> Self {
|
||||||
use wgpu::util::DeviceExt;
|
use wgpu::util::DeviceExt;
|
||||||
|
|
||||||
let to_wgpu = |method: FilterMethod| match method {
|
let nearest_sampler = device.create_sampler(&wgpu::SamplerDescriptor {
|
||||||
FilterMethod::Linear => wgpu::FilterMode::Linear,
|
address_mode_u: wgpu::AddressMode::ClampToEdge,
|
||||||
FilterMethod::Nearest => wgpu::FilterMode::Nearest,
|
address_mode_v: wgpu::AddressMode::ClampToEdge,
|
||||||
};
|
address_mode_w: wgpu::AddressMode::ClampToEdge,
|
||||||
|
min_filter: wgpu::FilterMode::Nearest,
|
||||||
|
mag_filter: wgpu::FilterMode::Nearest,
|
||||||
|
mipmap_filter: wgpu::FilterMode::Nearest,
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
|
||||||
let mut sampler: [Option<Sampler>; SAMPLER_COUNT] =
|
let linear_sampler = device.create_sampler(&wgpu::SamplerDescriptor {
|
||||||
[None, None, None, None];
|
address_mode_u: wgpu::AddressMode::ClampToEdge,
|
||||||
|
address_mode_v: wgpu::AddressMode::ClampToEdge,
|
||||||
let filter = [FilterMethod::Linear, FilterMethod::Nearest];
|
address_mode_w: wgpu::AddressMode::ClampToEdge,
|
||||||
for min in 0..filter.len() {
|
min_filter: wgpu::FilterMode::Linear,
|
||||||
for mag in 0..filter.len() {
|
mag_filter: wgpu::FilterMode::Linear,
|
||||||
sampler[to_index(&TextureFilter {
|
mipmap_filter: wgpu::FilterMode::Linear,
|
||||||
min: filter[min],
|
..Default::default()
|
||||||
mag: filter[mag],
|
});
|
||||||
})] = Some(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 sampler = [
|
|
||||||
sampler[0].take().unwrap(),
|
|
||||||
sampler[1].take().unwrap(),
|
|
||||||
sampler[2].take().unwrap(),
|
|
||||||
sampler[3].take().unwrap(),
|
|
||||||
];
|
|
||||||
|
|
||||||
let constant_layout =
|
let constant_layout =
|
||||||
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
||||||
|
|
@ -338,7 +366,8 @@ impl Pipeline {
|
||||||
pipeline,
|
pipeline,
|
||||||
vertices,
|
vertices,
|
||||||
indices,
|
indices,
|
||||||
sampler,
|
nearest_sampler,
|
||||||
|
linear_sampler,
|
||||||
texture,
|
texture,
|
||||||
texture_version: texture_atlas.layer_count(),
|
texture_version: texture_atlas.layer_count(),
|
||||||
texture_atlas,
|
texture_atlas,
|
||||||
|
|
@ -381,8 +410,8 @@ impl Pipeline {
|
||||||
#[cfg(feature = "tracing")]
|
#[cfg(feature = "tracing")]
|
||||||
let _ = info_span!("Wgpu::Image", "DRAW").entered();
|
let _ = info_span!("Wgpu::Image", "DRAW").entered();
|
||||||
|
|
||||||
let mut instances: [Vec<Instance>; SAMPLER_COUNT] =
|
let nearest_instances: &mut Vec<Instance> = &mut Vec::new();
|
||||||
[Vec::new(), Vec::new(), Vec::new(), Vec::new()];
|
let linear_instances: &mut Vec<Instance> = &mut Vec::new();
|
||||||
|
|
||||||
#[cfg(feature = "image")]
|
#[cfg(feature = "image")]
|
||||||
let mut raster_cache = self.raster_cache.borrow_mut();
|
let mut raster_cache = self.raster_cache.borrow_mut();
|
||||||
|
|
@ -393,7 +422,11 @@ impl Pipeline {
|
||||||
for image in images {
|
for image in images {
|
||||||
match &image {
|
match &image {
|
||||||
#[cfg(feature = "image")]
|
#[cfg(feature = "image")]
|
||||||
layer::Image::Raster { handle, bounds } => {
|
layer::Image::Raster {
|
||||||
|
handle,
|
||||||
|
filter_method,
|
||||||
|
bounds,
|
||||||
|
} => {
|
||||||
if let Some(atlas_entry) = raster_cache.upload(
|
if let Some(atlas_entry) = raster_cache.upload(
|
||||||
device,
|
device,
|
||||||
encoder,
|
encoder,
|
||||||
|
|
@ -404,7 +437,12 @@ impl Pipeline {
|
||||||
[bounds.x, bounds.y],
|
[bounds.x, bounds.y],
|
||||||
[bounds.width, bounds.height],
|
[bounds.width, bounds.height],
|
||||||
atlas_entry,
|
atlas_entry,
|
||||||
&mut instances[to_index(handle.filter())],
|
match filter_method {
|
||||||
|
image::FilterMethod::Nearest => {
|
||||||
|
nearest_instances
|
||||||
|
}
|
||||||
|
image::FilterMethod::Linear => linear_instances,
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -432,7 +470,7 @@ impl Pipeline {
|
||||||
[bounds.x, bounds.y],
|
[bounds.x, bounds.y],
|
||||||
size,
|
size,
|
||||||
atlas_entry,
|
atlas_entry,
|
||||||
&mut instances[to_index(&TextureFilter::default())],
|
nearest_instances,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -441,7 +479,7 @@ impl Pipeline {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if instances.is_empty() {
|
if nearest_instances.is_empty() && linear_instances.is_empty() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -466,24 +504,24 @@ impl Pipeline {
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.layers.len() <= self.prepare_layer {
|
if self.layers.len() <= self.prepare_layer {
|
||||||
self.layers.push([None, None, None, None]);
|
self.layers.push(Layer::new(
|
||||||
|
device,
|
||||||
|
&self.constant_layout,
|
||||||
|
&self.nearest_sampler,
|
||||||
|
&self.linear_sampler,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
for (i, instances) in instances.iter_mut().enumerate() {
|
|
||||||
let layer = &mut self.layers[self.prepare_layer][i];
|
|
||||||
if !instances.is_empty() {
|
|
||||||
if layer.is_none() {
|
|
||||||
*layer = Some(Layer::new(
|
|
||||||
device,
|
|
||||||
&self.constant_layout,
|
|
||||||
&self.sampler[i],
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(layer) = layer {
|
let layer = &mut self.layers[self.prepare_layer];
|
||||||
layer.prepare(device, queue, instances, transformation);
|
|
||||||
}
|
layer.prepare(
|
||||||
}
|
device,
|
||||||
|
queue,
|
||||||
|
&nearest_instances,
|
||||||
|
&linear_instances,
|
||||||
|
transformation,
|
||||||
|
);
|
||||||
|
|
||||||
self.prepare_layer += 1;
|
self.prepare_layer += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -493,28 +531,24 @@ impl Pipeline {
|
||||||
bounds: Rectangle<u32>,
|
bounds: Rectangle<u32>,
|
||||||
render_pass: &mut wgpu::RenderPass<'a>,
|
render_pass: &mut wgpu::RenderPass<'a>,
|
||||||
) {
|
) {
|
||||||
if let Some(layer_group) = self.layers.get(layer) {
|
if let Some(layer) = self.layers.get(layer) {
|
||||||
for layer in layer_group.iter() {
|
render_pass.set_pipeline(&self.pipeline);
|
||||||
if let Some(layer) = layer {
|
|
||||||
render_pass.set_pipeline(&self.pipeline);
|
|
||||||
|
|
||||||
render_pass.set_scissor_rect(
|
render_pass.set_scissor_rect(
|
||||||
bounds.x,
|
bounds.x,
|
||||||
bounds.y,
|
bounds.y,
|
||||||
bounds.width,
|
bounds.width,
|
||||||
bounds.height,
|
bounds.height,
|
||||||
);
|
);
|
||||||
|
|
||||||
render_pass.set_bind_group(1, &self.texture, &[]);
|
render_pass.set_bind_group(1, &self.texture, &[]);
|
||||||
render_pass.set_index_buffer(
|
render_pass.set_index_buffer(
|
||||||
self.indices.slice(..),
|
self.indices.slice(..),
|
||||||
wgpu::IndexFormat::Uint16,
|
wgpu::IndexFormat::Uint16,
|
||||||
);
|
);
|
||||||
render_pass.set_vertex_buffer(0, self.vertices.slice(..));
|
render_pass.set_vertex_buffer(0, self.vertices.slice(..));
|
||||||
|
|
||||||
layer.render(render_pass);
|
layer.render(render_pass);
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -529,14 +563,6 @@ impl Pipeline {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_index(filter: &TextureFilter) -> usize {
|
|
||||||
let to_index = |m| match m {
|
|
||||||
FilterMethod::Linear => 0,
|
|
||||||
FilterMethod::Nearest => 1,
|
|
||||||
};
|
|
||||||
return (to_index(filter.mag) << 1) | (to_index(filter.min));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Copy, Zeroable, Pod)]
|
#[derive(Clone, Copy, Zeroable, Pod)]
|
||||||
pub struct Vertex {
|
pub struct Vertex {
|
||||||
|
|
@ -571,7 +597,7 @@ struct Instance {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Instance {
|
impl Instance {
|
||||||
pub const INITIAL: usize = 1_000;
|
pub const INITIAL: usize = 20;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
|
|
||||||
|
|
@ -186,11 +186,16 @@ impl<'a> Layer<'a> {
|
||||||
|
|
||||||
layer.quads.add(quad, background);
|
layer.quads.add(quad, background);
|
||||||
}
|
}
|
||||||
Primitive::Image { handle, bounds } => {
|
Primitive::Image {
|
||||||
|
handle,
|
||||||
|
filter_method,
|
||||||
|
bounds,
|
||||||
|
} => {
|
||||||
let layer = &mut layers[current_layer];
|
let layer = &mut layers[current_layer];
|
||||||
|
|
||||||
layer.images.push(Image::Raster {
|
layer.images.push(Image::Raster {
|
||||||
handle: handle.clone(),
|
handle: handle.clone(),
|
||||||
|
filter_method: *filter_method,
|
||||||
bounds: *bounds + translation,
|
bounds: *bounds + translation,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,9 @@ pub enum Image {
|
||||||
/// The handle of a raster image.
|
/// The handle of a raster image.
|
||||||
handle: image::Handle,
|
handle: image::Handle,
|
||||||
|
|
||||||
|
/// The filter method of a raster image.
|
||||||
|
filter_method: image::FilterMethod,
|
||||||
|
|
||||||
/// The bounds of the image.
|
/// The bounds of the image.
|
||||||
bounds: Rectangle,
|
bounds: Rectangle,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ use crate::core::{
|
||||||
|
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
|
|
||||||
pub use image::{FilterMethod, Handle, TextureFilter};
|
pub use image::{FilterMethod, Handle};
|
||||||
|
|
||||||
/// Creates a new [`Viewer`] with the given image `Handle`.
|
/// Creates a new [`Viewer`] with the given image `Handle`.
|
||||||
pub fn viewer<Handle>(handle: Handle) -> Viewer<Handle> {
|
pub fn viewer<Handle>(handle: Handle) -> Viewer<Handle> {
|
||||||
|
|
@ -37,6 +37,7 @@ pub struct Image<Handle> {
|
||||||
width: Length,
|
width: Length,
|
||||||
height: Length,
|
height: Length,
|
||||||
content_fit: ContentFit,
|
content_fit: ContentFit,
|
||||||
|
filter_method: FilterMethod,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Handle> Image<Handle> {
|
impl<Handle> Image<Handle> {
|
||||||
|
|
@ -47,6 +48,7 @@ impl<Handle> Image<Handle> {
|
||||||
width: Length::Shrink,
|
width: Length::Shrink,
|
||||||
height: Length::Shrink,
|
height: Length::Shrink,
|
||||||
content_fit: ContentFit::Contain,
|
content_fit: ContentFit::Contain,
|
||||||
|
filter_method: FilterMethod::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -65,11 +67,15 @@ impl<Handle> Image<Handle> {
|
||||||
/// Sets the [`ContentFit`] of the [`Image`].
|
/// Sets the [`ContentFit`] of the [`Image`].
|
||||||
///
|
///
|
||||||
/// Defaults to [`ContentFit::Contain`]
|
/// Defaults to [`ContentFit::Contain`]
|
||||||
pub fn content_fit(self, content_fit: ContentFit) -> Self {
|
pub fn content_fit(mut self, content_fit: ContentFit) -> Self {
|
||||||
Self {
|
self.content_fit = content_fit;
|
||||||
content_fit,
|
self
|
||||||
..self
|
}
|
||||||
}
|
|
||||||
|
/// Sets the [`FilterMethod`] of the [`Image`].
|
||||||
|
pub fn filter_method(mut self, filter_method: FilterMethod) -> Self {
|
||||||
|
self.filter_method = filter_method;
|
||||||
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -119,6 +125,7 @@ pub fn draw<Renderer, Handle>(
|
||||||
layout: Layout<'_>,
|
layout: Layout<'_>,
|
||||||
handle: &Handle,
|
handle: &Handle,
|
||||||
content_fit: ContentFit,
|
content_fit: ContentFit,
|
||||||
|
filter_method: FilterMethod,
|
||||||
) where
|
) where
|
||||||
Renderer: image::Renderer<Handle = Handle>,
|
Renderer: image::Renderer<Handle = Handle>,
|
||||||
Handle: Clone + Hash,
|
Handle: Clone + Hash,
|
||||||
|
|
@ -141,7 +148,7 @@ pub fn draw<Renderer, Handle>(
|
||||||
..bounds
|
..bounds
|
||||||
};
|
};
|
||||||
|
|
||||||
renderer.draw(handle.clone(), drawing_bounds + offset);
|
renderer.draw(handle.clone(), filter_method, drawing_bounds + offset);
|
||||||
};
|
};
|
||||||
|
|
||||||
if adjusted_fit.width > bounds.width || adjusted_fit.height > bounds.height
|
if adjusted_fit.width > bounds.width || adjusted_fit.height > bounds.height
|
||||||
|
|
@ -191,7 +198,13 @@ where
|
||||||
_cursor: mouse::Cursor,
|
_cursor: mouse::Cursor,
|
||||||
_viewport: &Rectangle,
|
_viewport: &Rectangle,
|
||||||
) {
|
) {
|
||||||
draw(renderer, layout, &self.handle, self.content_fit);
|
draw(
|
||||||
|
renderer,
|
||||||
|
layout,
|
||||||
|
&self.handle,
|
||||||
|
self.content_fit,
|
||||||
|
self.filter_method,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,19 +22,21 @@ pub struct Viewer<Handle> {
|
||||||
max_scale: f32,
|
max_scale: f32,
|
||||||
scale_step: f32,
|
scale_step: f32,
|
||||||
handle: Handle,
|
handle: Handle,
|
||||||
|
filter_method: image::FilterMethod,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Handle> Viewer<Handle> {
|
impl<Handle> Viewer<Handle> {
|
||||||
/// Creates a new [`Viewer`] with the given [`State`].
|
/// Creates a new [`Viewer`] with the given [`State`].
|
||||||
pub fn new(handle: Handle) -> Self {
|
pub fn new(handle: Handle) -> Self {
|
||||||
Viewer {
|
Viewer {
|
||||||
|
handle,
|
||||||
padding: 0.0,
|
padding: 0.0,
|
||||||
width: Length::Shrink,
|
width: Length::Shrink,
|
||||||
height: Length::Shrink,
|
height: Length::Shrink,
|
||||||
min_scale: 0.25,
|
min_scale: 0.25,
|
||||||
max_scale: 10.0,
|
max_scale: 10.0,
|
||||||
scale_step: 0.10,
|
scale_step: 0.10,
|
||||||
handle,
|
filter_method: image::FilterMethod::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -329,6 +331,7 @@ where
|
||||||
image::Renderer::draw(
|
image::Renderer::draw(
|
||||||
renderer,
|
renderer,
|
||||||
self.handle.clone(),
|
self.handle.clone(),
|
||||||
|
self.filter_method,
|
||||||
Rectangle {
|
Rectangle {
|
||||||
x: bounds.x,
|
x: bounds.x,
|
||||||
y: bounds.y,
|
y: bounds.y,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue