fj_viewer/graphics/
texture.rs

1use image::{GenericImageView, ImageError};
2
3#[derive(Debug)]
4pub struct Texture {
5    pub texture: wgpu::Texture,
6    pub view: wgpu::TextureView,
7    pub sampler: wgpu::Sampler,
8}
9
10#[derive(Debug, thiserror::Error)]
11pub enum LoadTextureError {
12    #[error("Image processing error")]
13    ImageError(#[from] ImageError),
14}
15
16impl Texture {
17    pub fn from_bytes(
18        device: &wgpu::Device,
19        queue: &wgpu::Queue,
20        bytes: &[u8],
21        label: &str,
22    ) -> Result<Self, LoadTextureError> {
23        let img = image::load_from_memory(bytes)?;
24        Ok(Self::from_image(device, queue, &img, Some(label)))
25    }
26
27    pub fn from_image(
28        device: &wgpu::Device,
29        queue: &wgpu::Queue,
30        img: &image::DynamicImage,
31        label: Option<&str>,
32    ) -> Self {
33        let dimensions = img.dimensions();
34        let rgba = img.to_rgba8();
35
36        let size = wgpu::Extent3d {
37            width: dimensions.0,
38            height: dimensions.1,
39            depth_or_array_layers: 1,
40        };
41        let texture = device.create_texture(&wgpu::TextureDescriptor {
42            label,
43            size,
44            mip_level_count: 1,
45            sample_count: 1,
46            dimension: wgpu::TextureDimension::D2,
47            format: wgpu::TextureFormat::Rgba8UnormSrgb,
48            usage: wgpu::TextureUsages::TEXTURE_BINDING
49                | wgpu::TextureUsages::COPY_DST,
50            view_formats: &[],
51        });
52
53        queue.write_texture(
54            wgpu::ImageCopyTexture {
55                aspect: wgpu::TextureAspect::All,
56                texture: &texture,
57                mip_level: 0,
58                origin: wgpu::Origin3d::ZERO,
59            },
60            &rgba,
61            wgpu::ImageDataLayout {
62                offset: 0,
63                bytes_per_row: Some(4 * dimensions.0),
64                rows_per_image: Some(dimensions.1),
65            },
66            size,
67        );
68
69        let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
70        let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
71            address_mode_u: wgpu::AddressMode::ClampToEdge,
72            address_mode_v: wgpu::AddressMode::ClampToEdge,
73            address_mode_w: wgpu::AddressMode::ClampToEdge,
74            mag_filter: wgpu::FilterMode::Linear,
75            min_filter: wgpu::FilterMode::Nearest,
76            mipmap_filter: wgpu::FilterMode::Nearest,
77            ..Default::default()
78        });
79
80        Self {
81            texture,
82            view,
83            sampler,
84        }
85    }
86}