1use stb_image_rust;
2use stb_image_write_rust::ImageWriter::ImageWriter;
3
4use std::fs;
5use std::io::Read;
6
7use ddsfile::Caps2;
8use ddsfile::D3DFormat;
9use ddsfile::DxgiFormat;
10use ddsfile::Dds as DDS;
11
12use crate::gfx;
13use gfx::{TextureInfo, TextureType, Device};
14
15pub struct ImageData {
17 pub info: TextureInfo,
19 pub data: Vec<u8>,
21}
22
23pub fn load_from_file(filename: &str) -> Result<ImageData, super::Error> {
26 let path = std::path::Path::new(filename);
28 println!("hotline_rs::image:: loading: {}", path.display());
29 let mut f = fs::File::open(path).expect("hotline_rs::image:: File not found");
30 if filename.ends_with(".dds") {
32 let dds = DDS::read(f)?;
33 Ok(ImageData {
34 info: TextureInfo {
35 tex_type: to_gfx_texture_type(&dds),
36 format: to_gfx_format(&dds),
37 width: dds.get_width() as u64,
38 height: dds.get_height() as u64,
39 depth: dds.get_depth(),
40 array_layers: dds.get_num_array_layers(),
41 mip_levels: dds.get_num_mipmap_levels(),
42 samples: 1,
43 usage: gfx::TextureUsage::SHADER_RESOURCE,
44 initial_state: gfx::ResourceState::ShaderResource
45 },
46 data: dds.data.to_vec(),
47 })
48 }
49 else {
50 let mut contents = vec![];
52 f.read_to_end(&mut contents)?;
53
54 let mut x = 0;
55 let mut y = 0;
56 let mut comp = 0;
57 let mut data_out: Vec<u8> = Vec::new();
58
59 unsafe {
60 let img = stb_image_rust::stbi_load_from_memory(
62 contents.as_mut_ptr(),
63 contents.len() as i32,
64 &mut x,
65 &mut y,
66 &mut comp,
67 stb_image_rust::STBI_rgb_alpha,
68 );
69
70 if !img.is_null() {
71 let data_size_bytes = x * y * 4;
73 data_out.resize(data_size_bytes as usize, 0);
74 std::ptr::copy_nonoverlapping(img, data_out.as_mut_ptr(), data_size_bytes as usize);
75
76 stb_image_rust::c_runtime::free(img);
78
79 Ok(ImageData {
80 info: TextureInfo {
81 format: gfx::Format::RGBA8n,
82 width: x as u64,
83 height: y as u64,
84 ..Default::default()
85 },
86 data: data_out,
87 })
88 }
89 else {
90 Err(super::Error {
91 msg: format!("hotline_rs::image:: failed to load image via stb_image: {}", filename)
92 })
93 }
94 }
95 }
96}
97
98#[cfg(target_os = "windows")]
100pub fn load_texture_from_file(
101 device: &mut crate::gfx_platform::Device,
102 file: &str,
103 heap: Option<&mut crate::gfx_platform::Heap>) -> Result<crate::gfx_platform::Texture, super::Error> {
104 let image = load_from_file(file)?;
105 device.create_texture_with_heaps(
106 &image.info,
107 gfx::TextureHeapInfo {
108 shader: heap,
109 ..Default::default()
110 },
111 crate::data![image.data.as_slice()])
112}
113
114fn to_gfx_texture_type(dds: &DDS) -> TextureType {
116 if dds.header.caps.contains(ddsfile::Caps::COMPLEX) {
117 let all_faces = Caps2::CUBEMAP_POSITIVEX | Caps2::CUBEMAP_NEGATIVEX | Caps2::CUBEMAP_POSITIVEY |
118 Caps2::CUBEMAP_NEGATIVEY | Caps2::CUBEMAP_POSITIVEZ | Caps2::CUBEMAP_NEGATIVEZ;
119 if dds.header.caps2.contains(all_faces) {
120 if dds.get_num_array_layers() > 6 {
121 TextureType::TextureCubeArray
122 }
123 else {
124 TextureType::TextureCube
125 }
126 }
127 else if dds.get_depth() > 1 {
128 TextureType::Texture3D
129 }
130 else if dds.get_height() == 1 {
131 if dds.get_num_array_layers() > 1 {
132 TextureType::Texture1DArray
133 }
134 else {
135 TextureType::Texture1D
136 }
137 }
138 else if dds.get_num_array_layers() > 1 {
139 TextureType::Texture2DArray
140 }
141 else {
142 TextureType::Texture2D
143 }
144 }
145 else if dds.get_height() == 1 {
146 if dds.get_num_array_layers() > 1 {
147 TextureType::Texture1DArray
148 }
149 else {
150 TextureType::Texture1D
151 }
152 }
153 else if dds.get_num_array_layers() > 1 {
154 TextureType::Texture2DArray
155 }
156 else {
157 TextureType::Texture2D
158 }
159}
160
161pub fn write_to_file(filename: &str, width: u64, height: u64, components: u32, image_data: &[u8]) -> Result<(), super::Error> {
164 let path = std::path::Path::new(&filename);
165 let mut writer = ImageWriter::new(filename);
166 match path.extension() {
167 Some(os_str) => match os_str.to_str() {
168 Some("png") => {
169 writer.write_png(
170 width as i32,
171 height as i32,
172 components as i32,
173 image_data.as_ptr(),
174 );
175 Ok(())
176 }
177 Some("bmp") => {
178 writer.write_bmp(
179 width as i32,
180 height as i32,
181 components as i32,
182 image_data.as_ptr(),
183 );
184 Ok(())
185 }
186 Some("tga") => {
187 writer.write_tga(
188 width as i32,
189 height as i32,
190 components as i32,
191 image_data.as_ptr(),
192 );
193 Ok(())
194 }
195 Some("jpg") => {
196 writer.write_jpg(
197 width as i32,
198 height as i32,
199 components as i32,
200 image_data.as_ptr(),
201 90,
202 );
203 Ok(())
204 }
205 _ => {
206 if os_str.to_str().is_some() {
207 Err(super::Error {
208 msg: format!("hotline_rs::image: Image format '{}' is not supported", os_str.to_str().unwrap())
209 })
210 } else {
211 Err(super::Error {
212 msg: format!("hotline_rs::image: Filename '{}' did not specify image format extension!",filename)
213 })
214 }
215 }
216 },
217 _ => Err(super::Error {
218 msg: format!("hotline_rs::image: Filename '{}' has no extension!", filename)
219 }),
220 }
221}
222
223pub fn write_to_file_from_gpu(filename: &str, data: &gfx::ReadBackData) -> Result<(), super::Error> {
225 let fmt = if data.format == gfx::Format::Unknown {
226 gfx::Format::RGBA8n
227 }
228 else {
229 data.format
230 };
231 let w = data.row_pitch / gfx::block_size_for_format(fmt) as usize;
232 let h = data.slice_pitch / data.row_pitch;
233 let c = gfx::components_for_format(fmt);
234 write_to_file(filename, w as u64, h as u64, c, data.data)
235}
236
237fn to_gfx_format(dds: &DDS) -> gfx::Format {
239 if let Some(fmt) = dds.get_d3d_format() {
240 match fmt {
241 D3DFormat::A8B8G8R8 => gfx::Format::RGBA8n,
242 D3DFormat::G16R16 => panic!(),
243 D3DFormat::A2B10G10R10 => panic!(),
244 D3DFormat::A1R5G5B5 => panic!(),
245 D3DFormat::R5G6B5 => panic!(),
246 D3DFormat::A8 => panic!(),
247 D3DFormat::A8R8G8B8 => panic!(),
248 D3DFormat::X8R8G8B8 => panic!(),
249 D3DFormat::X8B8G8R8 => panic!(),
250 D3DFormat::A2R10G10B10 => panic!(),
251 D3DFormat::R8G8B8 => panic!(),
252 D3DFormat::X1R5G5B5 => panic!(),
253 D3DFormat::A4R4G4B4 => panic!(),
254 D3DFormat::X4R4G4B4 => panic!(),
255 D3DFormat::A8R3G3B2 => panic!(),
256 D3DFormat::A8L8 => panic!(),
257 D3DFormat::L16 => panic!(),
258 D3DFormat::L8 => panic!(),
259 D3DFormat::A4L4 => panic!(),
260 D3DFormat::DXT1 => panic!(),
261 D3DFormat::DXT3 => panic!(),
262 D3DFormat::DXT5 => panic!(),
263 D3DFormat::R8G8_B8G8 => panic!(),
264 D3DFormat::G8R8_G8B8 => panic!(),
265 D3DFormat::A16B16G16R16 => panic!(),
266 D3DFormat::Q16W16V16U16 => panic!(),
267 D3DFormat::R16F => gfx::Format::R16f,
268 D3DFormat::G16R16F => gfx::Format::RG16f,
269 D3DFormat::A16B16G16R16F => gfx::Format::RGBA16f,
270 D3DFormat::R32F => gfx::Format::R32f,
271 D3DFormat::G32R32F => gfx::Format::RG32f,
272 D3DFormat::A32B32G32R32F => gfx::Format::RGBA32f,
273 D3DFormat::DXT2 => panic!(),
274 D3DFormat::DXT4 => panic!(),
275 D3DFormat::UYVY => panic!(),
276 D3DFormat::YUY2 => panic!(),
277 D3DFormat::CXV8U8 => panic!(),
278 }
279 }
280 else if let Some(fmt) = dds.get_dxgi_format() {
281 match fmt {
282 DxgiFormat::Unknown => gfx::Format::Unknown,
283 DxgiFormat::R32G32B32A32_Typeless => panic!(),
284 DxgiFormat::R32G32B32A32_Float => gfx::Format::RGBA32f,
285 DxgiFormat::R32G32B32A32_UInt => gfx::Format::RGBA32u,
286 DxgiFormat::R32G32B32A32_SInt => gfx::Format::RGBA32i,
287 DxgiFormat::R32G32B32_Typeless => panic!(),
288 DxgiFormat::R32G32B32_Float => gfx::Format::RGB32f,
289 DxgiFormat::R32G32B32_UInt => gfx::Format::RGB32u,
290 DxgiFormat::R32G32B32_SInt => gfx::Format::RGB32i,
291 DxgiFormat::R16G16B16A16_Typeless => panic!(),
292 DxgiFormat::R16G16B16A16_Float => gfx::Format::RGBA16f,
293 DxgiFormat::R16G16B16A16_UNorm => panic!(),
294 DxgiFormat::R16G16B16A16_UInt => gfx::Format::RGBA16u,
295 DxgiFormat::R16G16B16A16_SNorm => panic!(),
296 DxgiFormat::R16G16B16A16_SInt => gfx::Format::RGBA16i,
297 DxgiFormat::R32G32_Typeless => panic!(),
298 DxgiFormat::R32G32_Float => gfx::Format::RG32f,
299 DxgiFormat::R32G32_UInt => gfx::Format::RG32u,
300 DxgiFormat::R32G32_SInt => gfx::Format::RG32i,
301 DxgiFormat::R32G8X24_Typeless => panic!(),
302 DxgiFormat::D32_Float_S8X24_UInt => panic!(),
303 DxgiFormat::R32_Float_X8X24_Typeless => panic!(),
304 DxgiFormat::X32_Typeless_G8X24_UInt => panic!(),
305 DxgiFormat::R10G10B10A2_Typeless => panic!(),
306 DxgiFormat::R10G10B10A2_UNorm => panic!(),
307 DxgiFormat::R10G10B10A2_UInt => panic!(),
308 DxgiFormat::R11G11B10_Float => panic!(),
309 DxgiFormat::R8G8B8A8_Typeless => panic!(),
310 DxgiFormat::R8G8B8A8_UNorm => gfx::Format::RGBA8n,
311 DxgiFormat::R8G8B8A8_UNorm_sRGB => gfx::Format::RGBA8nSRGB,
312 DxgiFormat::R8G8B8A8_UInt => gfx::Format::RGBA8u,
313 DxgiFormat::R8G8B8A8_SNorm => panic!(),
314 DxgiFormat::R8G8B8A8_SInt => gfx::Format::RGBA8i,
315 DxgiFormat::R16G16_Typeless => panic!(),
316 DxgiFormat::R16G16_Float => gfx::Format::RG16f,
317 DxgiFormat::R16G16_UNorm => panic!(),
318 DxgiFormat::R16G16_UInt => gfx::Format::RG16u,
319 DxgiFormat::R16G16_SNorm => panic!(),
320 DxgiFormat::R16G16_SInt => gfx::Format::RG16i,
321 DxgiFormat::R32_Typeless => panic!(),
322 DxgiFormat::D32_Float => gfx::Format::D32f,
323 DxgiFormat::R32_Float => gfx::Format::R32f,
324 DxgiFormat::R32_UInt => gfx::Format::R32u,
325 DxgiFormat::R32_SInt => gfx::Format::R32i,
326 DxgiFormat::R24G8_Typeless => panic!(),
327 DxgiFormat::D24_UNorm_S8_UInt => gfx::Format::D24nS8u,
328 DxgiFormat::R24_UNorm_X8_Typeless => panic!(),
329 DxgiFormat::X24_Typeless_G8_UInt => panic!(),
330 DxgiFormat::R8G8_Typeless => panic!(),
331 DxgiFormat::R8G8_UNorm => panic!(),
332 DxgiFormat::R8G8_UInt => panic!(),
333 DxgiFormat::R8G8_SNorm => panic!(),
334 DxgiFormat::R8G8_SInt => panic!(),
335 DxgiFormat::R16_Typeless => panic!(),
336 DxgiFormat::R16_Float => gfx::Format::R16f,
337 DxgiFormat::D16_UNorm => gfx::Format::D16n,
338 DxgiFormat::R16_UNorm => gfx::Format::R16n,
339 DxgiFormat::R16_UInt => gfx::Format::R16u,
340 DxgiFormat::R16_SNorm => panic!(),
341 DxgiFormat::R16_SInt => gfx::Format::R16i,
342 DxgiFormat::R8_Typeless => panic!(),
343 DxgiFormat::R8_UNorm => panic!(),
344 DxgiFormat::R8_UInt => panic!(),
345 DxgiFormat::R8_SNorm => panic!(),
346 DxgiFormat::R8_SInt => panic!(),
347 DxgiFormat::A8_UNorm => panic!(),
348 DxgiFormat::R1_UNorm => panic!(),
349 DxgiFormat::R9G9B9E5_SharedExp => panic!(),
350 DxgiFormat::R8G8_B8G8_UNorm => panic!(),
351 DxgiFormat::G8R8_G8B8_UNorm => panic!(),
352 DxgiFormat::BC1_Typeless => panic!(),
353 DxgiFormat::BC1_UNorm => gfx::Format::BC1n,
354 DxgiFormat::BC1_UNorm_sRGB => gfx::Format::BC1nSRGB,
355 DxgiFormat::BC2_Typeless => panic!(),
356 DxgiFormat::BC2_UNorm => gfx::Format::BC2n,
357 DxgiFormat::BC2_UNorm_sRGB => gfx::Format::BC2nSRGB,
358 DxgiFormat::BC3_Typeless => panic!(),
359 DxgiFormat::BC3_UNorm => gfx::Format::BC3n,
360 DxgiFormat::BC3_UNorm_sRGB => gfx::Format::BC3nSRGB,
361 DxgiFormat::BC4_Typeless => panic!(),
362 DxgiFormat::BC4_UNorm => gfx::Format::BC4n,
363 DxgiFormat::BC4_SNorm => panic!(),
364 DxgiFormat::BC5_Typeless => panic!(),
365 DxgiFormat::BC5_UNorm => gfx::Format::BC5n,
366 DxgiFormat::BC5_SNorm => panic!(),
367 DxgiFormat::B5G6R5_UNorm => panic!(),
368 DxgiFormat::B5G5R5A1_UNorm => panic!(),
369 DxgiFormat::B8G8R8A8_UNorm => gfx::Format::BGRA8n,
370 DxgiFormat::B8G8R8X8_UNorm => gfx::Format::BGRX8n,
371 DxgiFormat::R10G10B10_XR_Bias_A2_UNorm => panic!(),
372 DxgiFormat::B8G8R8A8_Typeless => panic!(),
373 DxgiFormat::B8G8R8A8_UNorm_sRGB => gfx::Format::BGRA8nSRGB,
374 DxgiFormat::B8G8R8X8_Typeless => panic!(),
375 DxgiFormat::B8G8R8X8_UNorm_sRGB => gfx::Format::BGRX8nSRGB,
376 DxgiFormat::BC6H_Typeless => panic!(),
377 DxgiFormat::BC6H_UF16 => panic!(),
378 DxgiFormat::BC6H_SF16 => panic!(),
379 DxgiFormat::BC7_Typeless => panic!(),
380 DxgiFormat::BC7_UNorm => panic!(),
381 DxgiFormat::BC7_UNorm_sRGB => panic!(),
382 DxgiFormat::AYUV => panic!(),
383 DxgiFormat::Y410 => panic!(),
384 DxgiFormat::Y416 => panic!(),
385 DxgiFormat::NV12 => panic!(),
386 DxgiFormat::P010 => panic!(),
387 DxgiFormat::P016 => panic!(),
388 DxgiFormat::Format_420_Opaque => panic!(),
389 DxgiFormat::YUY2 => panic!(),
390 DxgiFormat::Y210 => panic!(),
391 DxgiFormat::Y216 => panic!(),
392 DxgiFormat::NV11 => panic!(),
393 DxgiFormat::AI44 => panic!(),
394 DxgiFormat::IA44 => panic!(),
395 DxgiFormat::P8 => panic!(),
396 DxgiFormat::A8P8 => panic!(),
397 DxgiFormat::B4G4R4A4_UNorm => panic!(),
398 DxgiFormat::P208 => panic!(),
399 DxgiFormat::V208 => panic!(),
400 DxgiFormat::V408 => panic!(),
401 DxgiFormat::Force_UInt => panic!(),
402 }
403 }
404 else {
405 panic!("hotline_rs::image:: unsupported dds format is neither d3d or dxgi!");
406 }
407}