1use std::{fmt::Debug, io::Cursor, path::Path, sync::Arc};
2
3use image::{DynamicImage, EncodableLayout, ImageFormat, ImageReader, RgbaImage};
4
5use crate::math::{Vec2, Vec4};
6
7use super::GRAPHICS_STATE;
8
9#[derive(Debug, Clone)]
10pub struct Texture(pub(crate) Arc<TextureHandle>);
12
13impl PartialEq for Texture {
14 fn eq(&self, other: &Self) -> bool {
15 Arc::ptr_eq(&self.0, &other.0)
16 }
17}
18
19impl Texture {
20 pub fn new(filename: impl AsRef<Path>) -> Self {
22 Self::new_from_image(ImageReader::open(filename).unwrap().decode().unwrap())
23 }
24 pub fn new_from_file_format(file_data: &[u8], format_hint: Option<ImageFormat>) -> Self {
26 let mut image = ImageReader::new(Cursor::new(file_data))
27 .with_guessed_format()
28 .unwrap();
29 if let Some(fmt) = format_hint {
30 image.set_format(fmt);
31 }
32 Self::new_from_image(image.decode().unwrap())
33 }
34 pub fn new_fill(width: u32, height: u32, colour: impl Into<Vec4>) -> Self {
36 let c = colour.into() * 255.9;
37 Self::new_from_data(
38 width,
39 height,
40 (0..width * height)
41 .flat_map(|_| [c.x() as u8, c.y() as u8, c.z() as u8, c.w() as u8])
42 .collect::<Vec<_>>()
43 .as_slice(),
44 )
45 }
46 pub fn new_from_image(img: DynamicImage) -> Self {
48 Self::new_from_data(img.width(), img.height(), img.to_rgba8().as_bytes())
49 }
50 pub fn new_from_data(width: u32, height: u32, data: &[u8]) -> Self {
52 let size = wgpu::Extent3d {
53 width,
54 height,
55 depth_or_array_layers: 1,
56 };
57 let texture = GRAPHICS_STATE.device.create_texture(&wgpu::TextureDescriptor {
58 label: None,
59 size,
60 mip_level_count: 1,
61 sample_count: 1,
62 dimension: wgpu::TextureDimension::D2,
63 format: wgpu::TextureFormat::Rgba8Unorm,
64 usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
65 view_formats: &[],
66 });
67 GRAPHICS_STATE.queue.write_texture(
68 wgpu::ImageCopyTexture {
69 texture: &texture,
70 mip_level: 0,
71 origin: wgpu::Origin3d::ZERO,
72 aspect: wgpu::TextureAspect::All,
73 },
74 data,
75 wgpu::ImageDataLayout {
76 offset: 0,
77 bytes_per_row: Some(4 * width),
78 rows_per_image: Some(height),
79 },
80 size,
81 );
82 let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
83 let sampler = GRAPHICS_STATE.device.create_sampler(&wgpu::SamplerDescriptor {
84 address_mode_u: wgpu::AddressMode::ClampToEdge,
85 address_mode_v: wgpu::AddressMode::ClampToEdge,
86 address_mode_w: wgpu::AddressMode::ClampToEdge,
87 mag_filter: wgpu::FilterMode::Nearest,
88 min_filter: wgpu::FilterMode::Nearest,
89 mipmap_filter: wgpu::FilterMode::Nearest,
90 ..Default::default()
91 });
92 Texture(Arc::new(TextureHandle {
93 size: Vec2::new(width, height),
94 texture: Arc::new(texture),
95 view,
96 sampler,
97 }))
98 }
99 pub(crate) fn new_from_wgpu(texture: Arc<wgpu::Texture>) -> Self {
100 let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
101 let sampler = GRAPHICS_STATE.device.create_sampler(&wgpu::SamplerDescriptor {
102 address_mode_u: wgpu::AddressMode::ClampToEdge,
103 address_mode_v: wgpu::AddressMode::ClampToEdge,
104 address_mode_w: wgpu::AddressMode::ClampToEdge,
105 mag_filter: wgpu::FilterMode::Nearest,
106 min_filter: wgpu::FilterMode::Nearest,
107 mipmap_filter: wgpu::FilterMode::Nearest,
108 ..Default::default()
109 });
110 Texture(Arc::new(TextureHandle {
111 size: Vec2::new(texture.width(), texture.height()),
112 texture,
113 view,
114 sampler,
115 }))
116 }
117 pub fn upload_region(&self, data: &[u8], x: u32, y: u32, width: u32, height: u32) {
119 GRAPHICS_STATE.queue.write_texture(
120 wgpu::ImageCopyTexture {
121 texture: &self.0.texture,
122 mip_level: 0,
123 origin: wgpu::Origin3d { x, y, z: 0 },
124 aspect: wgpu::TextureAspect::All,
125 },
126 data,
127 wgpu::ImageDataLayout {
128 offset: 0,
129 bytes_per_row: Some(4 * width),
130 rows_per_image: Some(height),
131 },
132 wgpu::Extent3d {
133 width,
134 height,
135 depth_or_array_layers: 1,
136 },
137 );
138 }
139 pub fn upload_image_region(&self, image: RgbaImage, x: u32, y: u32) {
141 let (width, height) = (image.width(), image.height());
142 self.upload_region(image.as_bytes(), x, y, width, height);
143 }
144 pub fn size(&self) -> Vec2 {
146 self.0.size
147 }
148}
149
150#[derive(Debug)]
151pub(crate) struct TextureHandle {
152 pub(crate) size: Vec2,
153 pub(crate) texture: Arc<wgpu::Texture>,
154 pub(crate) view: wgpu::TextureView,
155 pub(crate) sampler: wgpu::Sampler,
156}
157
158impl TextureHandle {
159 pub(crate) fn bind_group_entries(&self, i: u32) -> [wgpu::BindGroupEntry<'_>; 2] {
160 [
161 wgpu::BindGroupEntry {
162 binding: i * 2,
163 resource: wgpu::BindingResource::TextureView(&self.view),
164 },
165 wgpu::BindGroupEntry {
166 binding: i * 2 + 1,
167 resource: wgpu::BindingResource::Sampler(&self.sampler),
168 },
169 ]
170 }
171}