1use std::ops::Range;
2
3use ddsfile::{Caps2, D3DFormat, Dds, DxgiFormat, FourCC};
4use thiserror::Error;
5
6use crate::{
7 CreateImageError, ImageFormat, Mipmaps, Quality, Surface, SurfaceError, SurfaceRgba32Float,
8 SurfaceRgba8,
9};
10
11#[derive(Debug, Error)]
13pub enum CreateDdsError {
14 #[error("error creating DDS: {0}")]
15 Dds(#[from] ddsfile::Error),
16
17 #[error("error compressing surface: {0}")]
18 CompressSurface(#[from] SurfaceError),
19}
20
21#[cfg(feature = "encode")]
22#[cfg(feature = "image")]
23pub fn dds_from_image(
27 image: &image::RgbaImage,
28 format: ImageFormat,
29 quality: Quality,
30 mipmaps: Mipmaps,
31) -> Result<Dds, CreateDdsError> {
32 SurfaceRgba8::from_image(image)
34 .encode(format, quality, mipmaps)?
35 .to_dds()
36}
37
38#[cfg(feature = "encode")]
39#[cfg(feature = "image")]
40pub fn dds_from_imagef32(
44 image: &image::Rgba32FImage,
45 format: ImageFormat,
46 quality: Quality,
47 mipmaps: Mipmaps,
48) -> Result<Dds, CreateDdsError> {
49 SurfaceRgba32Float::from_image(image)
51 .encode(format, quality, mipmaps)?
52 .to_dds()
53}
54
55#[cfg(feature = "image")]
56pub fn image_from_dds(dds: &Dds, mipmap: u32) -> Result<image::RgbaImage, CreateImageError> {
59 let layers = array_layer_count(dds);
60 SurfaceRgba8::decode_layers_mipmaps_dds(dds, 0..layers, mipmap..mipmap + 1)?.into_image()
61}
62
63#[cfg(feature = "image")]
64pub fn imagef32_from_dds(dds: &Dds, mipmap: u32) -> Result<image::Rgba32FImage, CreateImageError> {
67 let layers = array_layer_count(dds);
68 SurfaceRgba32Float::decode_layers_mipmaps_dds(dds, 0..layers, mipmap..mipmap + 1)?.into_image()
69}
70
71impl<T: AsRef<[u8]>> Surface<T> {
72 pub fn to_dds(&self) -> Result<crate::ddsfile::Dds, CreateDdsError> {
76 let mut dds = dxgi_from_image_format(self.image_format)
77 .map(|format| {
78 Dds::new_dxgi(ddsfile::NewDxgiParams {
79 height: self.height,
80 width: self.width,
81 depth: if self.depth > 1 {
82 Some(self.depth)
83 } else {
84 None
85 },
86 format,
87 mipmap_levels: (self.mipmaps > 1).then_some(self.mipmaps),
88 array_layers: (self.layers > 1 && self.layers != 6).then_some(self.layers),
89 caps2: (self.layers == 6).then_some(Caps2::CUBEMAP | Caps2::CUBEMAP_ALLFACES),
90 is_cubemap: self.layers == 6,
91 resource_dimension: if self.depth > 1 {
92 ddsfile::D3D10ResourceDimension::Texture3D
93 } else {
94 ddsfile::D3D10ResourceDimension::Texture2D
95 },
96 alpha_mode: ddsfile::AlphaMode::Straight,
97 })
98 })
99 .or_else(|| {
100 d3d_from_image_format(self.image_format).map(|format| {
102 Dds::new_d3d(ddsfile::NewD3dParams {
103 height: self.height,
104 width: self.width,
105 depth: if self.depth > 1 {
106 Some(self.depth)
107 } else {
108 None
109 },
110 format,
111 mipmap_levels: (self.mipmaps > 1).then_some(self.mipmaps),
112 caps2: (self.layers == 6)
113 .then_some(Caps2::CUBEMAP | Caps2::CUBEMAP_ALLFACES),
114 })
115 })
116 })
117 .unwrap()?;
118
119 dds.data = self.data.as_ref().to_vec();
120
121 Ok(dds)
122 }
123}
124
125impl<'a> Surface<&'a [u8]> {
126 pub fn from_dds(dds: &'a crate::ddsfile::Dds) -> Result<Self, SurfaceError> {
128 let width = dds.get_width();
129 let height = dds.get_height();
130 let depth = dds.get_depth();
131 let layers = array_layer_count(dds);
132 let mipmaps = dds.get_num_mipmap_levels();
133 let image_format = dds_image_format(dds).map_err(SurfaceError::UnsupportedDdsFormat)?;
134
135 Ok(Surface {
136 width,
137 height,
138 depth,
139 layers,
140 mipmaps,
141 image_format,
142 data: &dds.data,
143 })
144 }
145}
146
147#[cfg(feature = "encode")]
148impl<T: AsRef<[u8]>> SurfaceRgba8<T> {
149 pub fn encode_dds(
153 &self,
154 format: ImageFormat,
155 quality: Quality,
156 mipmaps: Mipmaps,
157 ) -> Result<Dds, CreateDdsError> {
158 self.encode(format, quality, mipmaps)?.to_dds()
159 }
160}
161
162impl SurfaceRgba8<Vec<u8>> {
163 pub fn decode_dds(dds: &Dds) -> Result<SurfaceRgba8<Vec<u8>>, SurfaceError> {
165 Surface::from_dds(dds)?.decode_rgba8()
166 }
167
168 pub fn decode_layers_mipmaps_dds(
170 dds: &Dds,
171 layers: Range<u32>,
172 mipmaps: Range<u32>,
173 ) -> Result<SurfaceRgba8<Vec<u8>>, SurfaceError> {
174 Surface::from_dds(dds)?.decode_layers_mipmaps_rgba8(layers, mipmaps)
175 }
176}
177
178impl SurfaceRgba32Float<Vec<f32>> {
179 pub fn decode_dds(dds: &Dds) -> Result<SurfaceRgba32Float<Vec<f32>>, SurfaceError> {
181 Surface::from_dds(dds)?.decode_rgbaf32()
182 }
183
184 pub fn decode_layers_mipmaps_dds(
186 dds: &Dds,
187 layers: Range<u32>,
188 mipmaps: Range<u32>,
189 ) -> Result<SurfaceRgba32Float<Vec<f32>>, SurfaceError> {
190 Surface::from_dds(dds)?.decode_layers_mipmaps_rgbaf32(layers, mipmaps)
191 }
192}
193
194fn array_layer_count(dds: &Dds) -> u32 {
195 if matches!(&dds.header10, Some(header10) if header10.misc_flag == ddsfile::MiscFlag::TEXTURECUBE)
197 {
198 dds.get_num_array_layers().max(1) * 6
199 } else {
200 dds.get_num_array_layers().max(1)
201 }
202}
203
204#[derive(Debug, PartialEq)]
206pub struct DdsFormatInfo {
207 pub dxgi: Option<DxgiFormat>,
208 pub d3d: Option<D3DFormat>,
209 pub fourcc: Option<FourCC>,
210}
211
212pub fn dds_image_format(dds: &Dds) -> Result<ImageFormat, DdsFormatInfo> {
214 let dxgi = dds.get_dxgi_format();
216 let d3d = dds.get_d3d_format();
217 let fourcc = dds.header.spf.fourcc.clone();
218
219 d3d.and_then(image_format_from_d3d)
220 .or_else(|| dxgi.and_then(image_format_from_dxgi))
221 .or_else(|| fourcc.clone().and_then(image_format_from_fourcc))
222 .ok_or(DdsFormatInfo { dxgi, d3d, fourcc })
223}
224
225fn image_format_from_dxgi(format: DxgiFormat) -> Option<ImageFormat> {
226 match format {
227 DxgiFormat::R8_UNorm => Some(ImageFormat::R8Unorm),
228 DxgiFormat::R8_SNorm => Some(ImageFormat::R8Snorm),
229 DxgiFormat::R8G8_UNorm => Some(ImageFormat::Rg8Unorm),
230 DxgiFormat::R8G8_SNorm => Some(ImageFormat::Rg8Snorm),
231 DxgiFormat::R8G8B8A8_UNorm => Some(ImageFormat::Rgba8Unorm),
232 DxgiFormat::R8G8B8A8_UNorm_sRGB => Some(ImageFormat::Rgba8UnormSrgb),
233 DxgiFormat::R16G16B16A16_Float => Some(ImageFormat::Rgba16Float),
234 DxgiFormat::R32G32B32A32_Float => Some(ImageFormat::Rgba32Float),
235 DxgiFormat::B8G8R8A8_UNorm => Some(ImageFormat::Bgra8Unorm),
236 DxgiFormat::B8G8R8A8_UNorm_sRGB => Some(ImageFormat::Bgra8UnormSrgb),
237 DxgiFormat::BC1_UNorm => Some(ImageFormat::BC1RgbaUnorm),
238 DxgiFormat::BC1_UNorm_sRGB => Some(ImageFormat::BC1RgbaUnormSrgb),
239 DxgiFormat::BC2_UNorm => Some(ImageFormat::BC2RgbaUnorm),
240 DxgiFormat::BC2_UNorm_sRGB => Some(ImageFormat::BC2RgbaUnormSrgb),
241 DxgiFormat::BC3_UNorm => Some(ImageFormat::BC3RgbaUnorm),
242 DxgiFormat::BC3_UNorm_sRGB => Some(ImageFormat::BC3RgbaUnormSrgb),
243 DxgiFormat::BC4_UNorm => Some(ImageFormat::BC4RUnorm),
244 DxgiFormat::BC4_SNorm => Some(ImageFormat::BC4RSnorm),
245 DxgiFormat::BC5_UNorm => Some(ImageFormat::BC5RgUnorm),
246 DxgiFormat::BC5_SNorm => Some(ImageFormat::BC5RgSnorm),
247 DxgiFormat::BC6H_SF16 => Some(ImageFormat::BC6hRgbSfloat),
248 DxgiFormat::BC6H_UF16 => Some(ImageFormat::BC6hRgbUfloat),
249 DxgiFormat::BC7_UNorm => Some(ImageFormat::BC7RgbaUnorm),
250 DxgiFormat::BC7_UNorm_sRGB => Some(ImageFormat::BC7RgbaUnormSrgb),
251 DxgiFormat::B4G4R4A4_UNorm => Some(ImageFormat::Bgra4Unorm),
252 DxgiFormat::R16G16B16A16_UNorm => Some(ImageFormat::Rgba16Unorm),
253 DxgiFormat::R16G16B16A16_SNorm => Some(ImageFormat::Rgba16Snorm),
254 DxgiFormat::R16G16_UNorm => Some(ImageFormat::Rg16Unorm),
255 DxgiFormat::R16G16_SNorm => Some(ImageFormat::Rg16Snorm),
256 DxgiFormat::R16_UNorm => Some(ImageFormat::R16Unorm),
257 DxgiFormat::R16_SNorm => Some(ImageFormat::R16Snorm),
258 DxgiFormat::R16_Float => Some(ImageFormat::R16Float),
259 DxgiFormat::R16G16_Float => Some(ImageFormat::Rg16Float),
260 DxgiFormat::R32_Float => Some(ImageFormat::R32Float),
261 DxgiFormat::R32G32_Float => Some(ImageFormat::Rg32Float),
262 DxgiFormat::R8G8B8A8_SNorm => Some(ImageFormat::Rgba8Snorm),
263 DxgiFormat::R32G32B32_Float => Some(ImageFormat::Rgb32Float),
264 DxgiFormat::B5G5R5A1_UNorm => Some(ImageFormat::Bgr5A1Unorm),
265 _ => None,
266 }
267}
268
269fn image_format_from_d3d(format: D3DFormat) -> Option<ImageFormat> {
270 match format {
271 D3DFormat::DXT1 => Some(ImageFormat::BC1RgbaUnorm),
272 D3DFormat::DXT2 => Some(ImageFormat::BC2RgbaUnorm),
273 D3DFormat::DXT3 => Some(ImageFormat::BC2RgbaUnorm),
274 D3DFormat::DXT4 => Some(ImageFormat::BC3RgbaUnorm),
275 D3DFormat::DXT5 => Some(ImageFormat::BC3RgbaUnorm),
276 D3DFormat::A4R4G4B4 => Some(ImageFormat::Bgra4Unorm),
278 D3DFormat::A8R8G8B8 => Some(ImageFormat::Bgra8Unorm),
279 D3DFormat::R8G8B8 => Some(ImageFormat::Bgr8Unorm),
280 D3DFormat::A8B8G8R8 => Some(ImageFormat::Rgba8Unorm),
281 D3DFormat::G16R16F => Some(ImageFormat::Rg16Float),
282 D3DFormat::A16B16G16R16F => Some(ImageFormat::Rgba16Float),
283 D3DFormat::G32R32F => Some(ImageFormat::Rg32Float),
284 D3DFormat::A32B32G32R32F => Some(ImageFormat::Rgba32Float),
285 D3DFormat::G16R16 => Some(ImageFormat::Rg16Unorm),
286 D3DFormat::A16B16G16R16 => Some(ImageFormat::Rgba16Unorm),
287 D3DFormat::A1R5G5B5 => Some(ImageFormat::Bgr5A1Unorm),
288 _ => None,
289 }
290}
291
292const BC5U: u32 = u32::from_le_bytes(*b"BC5U");
293const ATI2: u32 = u32::from_le_bytes(*b"ATI2");
294
295fn image_format_from_fourcc(fourcc: FourCC) -> Option<ImageFormat> {
296 match fourcc.0 {
297 FourCC::DXT1 => Some(ImageFormat::BC1RgbaUnorm),
298 FourCC::DXT2 => Some(ImageFormat::BC2RgbaUnorm),
299 FourCC::DXT3 => Some(ImageFormat::BC2RgbaUnorm),
300 FourCC::DXT4 => Some(ImageFormat::BC3RgbaUnorm),
301 FourCC::DXT5 => Some(ImageFormat::BC3RgbaUnorm),
302 FourCC::BC4_UNORM => Some(ImageFormat::BC4RUnorm),
303 FourCC::BC4_SNORM => Some(ImageFormat::BC4RSnorm),
304 ATI2 | BC5U => Some(ImageFormat::BC5RgUnorm),
305 FourCC::BC5_SNORM => Some(ImageFormat::BC5RgSnorm),
306 _ => None,
307 }
308}
309
310fn d3d_from_image_format(value: ImageFormat) -> Option<D3DFormat> {
311 match value {
313 ImageFormat::BC1RgbaUnorm => Some(D3DFormat::DXT1),
314 ImageFormat::BC1RgbaUnormSrgb => Some(D3DFormat::DXT1),
315 ImageFormat::BC2RgbaUnorm => Some(D3DFormat::DXT2),
316 ImageFormat::BC2RgbaUnormSrgb => Some(D3DFormat::DXT2),
317 ImageFormat::BC3RgbaUnorm => Some(D3DFormat::DXT5),
318 ImageFormat::BC3RgbaUnormSrgb => Some(D3DFormat::DXT5),
319 ImageFormat::BC4RUnorm => None,
320 ImageFormat::BC4RSnorm => None,
321 ImageFormat::BC5RgUnorm => None,
322 ImageFormat::BC5RgSnorm => None,
323 ImageFormat::BC6hRgbUfloat => None,
324 ImageFormat::BC6hRgbSfloat => None,
325 ImageFormat::BC7RgbaUnorm => None,
326 ImageFormat::BC7RgbaUnormSrgb => None,
327 ImageFormat::R8Unorm => None,
328 ImageFormat::R8Snorm => None,
329 ImageFormat::Rg8Unorm => None,
330 ImageFormat::Rg8Snorm => None,
331 ImageFormat::Rgba8Unorm => Some(D3DFormat::A8B8G8R8),
332 ImageFormat::Rgba8UnormSrgb => Some(D3DFormat::A8B8G8R8),
333 ImageFormat::Rgba16Float => Some(D3DFormat::A16B16G16R16F),
334 ImageFormat::Rgba32Float => Some(D3DFormat::A32B32G32R32F),
335 ImageFormat::Bgra8Unorm => Some(D3DFormat::A8R8G8B8),
336 ImageFormat::Bgra8UnormSrgb => Some(D3DFormat::A8R8G8B8),
337 ImageFormat::Bgra4Unorm => Some(D3DFormat::A4R4G4B4),
338 ImageFormat::Bgr8Unorm => Some(D3DFormat::R8G8B8),
339 ImageFormat::R16Unorm => None,
340 ImageFormat::R16Snorm => None,
341 ImageFormat::Rg16Unorm => Some(D3DFormat::G16R16),
342 ImageFormat::Rg16Snorm => None,
343 ImageFormat::Rgba16Unorm => Some(D3DFormat::A16B16G16R16),
344 ImageFormat::Rgba16Snorm => None,
345 ImageFormat::Rg16Float => Some(D3DFormat::G16R16F),
346 ImageFormat::Rg32Float => Some(D3DFormat::G32R32F),
347 ImageFormat::R16Float => Some(D3DFormat::R16F),
348 ImageFormat::R32Float => Some(D3DFormat::R32F),
349 ImageFormat::Rgba8Snorm => None,
350 ImageFormat::Rgb32Float => None,
351 ImageFormat::Bgr5A1Unorm => Some(D3DFormat::A1R5G5B5),
352 }
353}
354
355fn dxgi_from_image_format(value: ImageFormat) -> Option<DxgiFormat> {
356 match value {
357 ImageFormat::BC1RgbaUnorm => Some(DxgiFormat::BC1_UNorm),
358 ImageFormat::BC1RgbaUnormSrgb => Some(DxgiFormat::BC1_UNorm_sRGB),
359 ImageFormat::BC2RgbaUnorm => Some(DxgiFormat::BC2_UNorm),
360 ImageFormat::BC2RgbaUnormSrgb => Some(DxgiFormat::BC2_UNorm_sRGB),
361 ImageFormat::BC3RgbaUnorm => Some(DxgiFormat::BC3_UNorm),
362 ImageFormat::BC3RgbaUnormSrgb => Some(DxgiFormat::BC3_UNorm_sRGB),
363 ImageFormat::BC4RUnorm => Some(DxgiFormat::BC4_UNorm),
364 ImageFormat::BC4RSnorm => Some(DxgiFormat::BC4_SNorm),
365 ImageFormat::BC5RgUnorm => Some(DxgiFormat::BC5_UNorm),
366 ImageFormat::BC5RgSnorm => Some(DxgiFormat::BC5_SNorm),
367 ImageFormat::BC6hRgbUfloat => Some(DxgiFormat::BC6H_UF16),
368 ImageFormat::BC6hRgbSfloat => Some(DxgiFormat::BC6H_SF16),
369 ImageFormat::BC7RgbaUnorm => Some(DxgiFormat::BC7_UNorm),
370 ImageFormat::BC7RgbaUnormSrgb => Some(DxgiFormat::BC7_UNorm_sRGB),
371 ImageFormat::R8Unorm => Some(DxgiFormat::R8_UNorm),
372 ImageFormat::R8Snorm => Some(DxgiFormat::R8_SNorm),
373 ImageFormat::Rg8Unorm => Some(DxgiFormat::R8G8_UNorm),
374 ImageFormat::Rg8Snorm => Some(DxgiFormat::R8G8_SNorm),
375 ImageFormat::Rgba8Unorm => Some(DxgiFormat::R8G8B8A8_UNorm),
376 ImageFormat::Rgba8UnormSrgb => Some(DxgiFormat::R8G8B8A8_UNorm_sRGB),
377 ImageFormat::Rgba16Float => Some(DxgiFormat::R16G16B16A16_Float),
378 ImageFormat::Rgba32Float => Some(DxgiFormat::R32G32B32A32_Float),
379 ImageFormat::Bgra8Unorm => Some(DxgiFormat::B8G8R8A8_UNorm),
380 ImageFormat::Bgra8UnormSrgb => Some(DxgiFormat::B8G8R8A8_UNorm_sRGB),
381 ImageFormat::Bgra4Unorm => Some(DxgiFormat::B4G4R4A4_UNorm),
382 ImageFormat::Bgr8Unorm => None,
383 ImageFormat::R16Unorm => Some(DxgiFormat::R16_UNorm),
384 ImageFormat::R16Snorm => Some(DxgiFormat::R16_SNorm),
385 ImageFormat::Rg16Unorm => Some(DxgiFormat::R16G16_UNorm),
386 ImageFormat::Rg16Snorm => Some(DxgiFormat::R16G16_SNorm),
387 ImageFormat::Rgba16Unorm => Some(DxgiFormat::R16G16B16A16_UNorm),
388 ImageFormat::Rgba16Snorm => Some(DxgiFormat::R16G16B16A16_SNorm),
389 ImageFormat::Rg16Float => Some(DxgiFormat::R16G16_Float),
390 ImageFormat::Rg32Float => Some(DxgiFormat::R32G32_Float),
391 ImageFormat::R16Float => Some(DxgiFormat::R16_Float),
392 ImageFormat::R32Float => Some(DxgiFormat::R32_Float),
393 ImageFormat::Rgba8Snorm => Some(DxgiFormat::R8G8B8A8_SNorm),
394 ImageFormat::Rgb32Float => Some(DxgiFormat::R32G32B32_Float),
395 ImageFormat::Bgr5A1Unorm => Some(DxgiFormat::B5G5R5A1_UNorm),
396 }
397}
398
399#[cfg(test)]
400mod tests {
401 use super::*;
402
403 use strum::IntoEnumIterator;
404
405 #[test]
406 fn dds_to_from_surface() {
407 for image_format in ImageFormat::iter() {
408 let data = vec![0u8; 4 * 4 * 6 * image_format.block_size_in_bytes()];
409 let surface = Surface {
410 width: 4,
411 height: 4,
412 depth: 1,
413 layers: 1,
414 mipmaps: 1,
415 image_format,
416 data: data.as_slice(),
417 };
418 assert_eq!(
419 surface,
420 Surface::from_dds(&surface.to_dds().unwrap()).unwrap()
421 );
422 }
423 }
424
425 #[test]
426 fn dds_to_from_surface_cube() {
427 for image_format in ImageFormat::iter() {
428 let data = vec![0u8; 4 * 4 * 6 * image_format.block_size_in_bytes()];
429 let surface = Surface {
430 width: 4,
431 height: 4,
432 depth: 1,
433 layers: 6,
434 mipmaps: 1,
435 image_format,
436 data: data.as_slice(),
437 };
438 assert_eq!(
439 surface,
440 Surface::from_dds(&surface.to_dds().unwrap()).unwrap()
441 );
442 }
443 }
444}