dds/
layout.rs

1use std::num::{NonZeroU32, NonZeroU8};
2
3use bitflags::bitflags;
4
5use crate::header::{Caps2, Header, ResourceDimension};
6use crate::DecodingError;
7use crate::{
8    util::{get_mipmap_size, NON_ZERO_U32_ONE},
9    LayoutError, PixelInfo, Size,
10};
11
12pub trait DataRegion {
13    /// The number of bytes this object occupies in the data section of a DDS file.
14    ///
15    /// It is guaranteed that `self.offset() + self.len() <= u64::MAX`.
16    fn data_len(&self) -> u64;
17    /// The byte offset of this object in the data section of a DDS file.
18    fn data_offset(&self) -> u64;
19    /// The byte offset of the byte after this object in the data section of a DDS file.
20    ///
21    /// This is equivalent to `self.offset() + self.len()`.
22    fn data_end(&self) -> u64 {
23        self.data_offset() + self.data_len()
24    }
25}
26
27#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
28pub struct SurfaceDescriptor {
29    width: NonZeroU32,
30    height: NonZeroU32,
31    offset: u64,
32    len: u64,
33}
34impl SurfaceDescriptor {
35    /// Internal constructor.
36    ///
37    /// This **assumes** that the arguments are valid and only performs checks
38    /// in debug.
39    fn new(width: NonZeroU32, height: NonZeroU32, offset: u64, len: u64) -> Self {
40        debug_assert!(len > 0);
41        debug_assert!(offset.checked_add(len).is_some());
42
43        Self {
44            width,
45            height,
46            offset,
47            len,
48        }
49    }
50
51    pub fn width(&self) -> u32 {
52        self.width.get()
53    }
54    pub fn height(&self) -> u32 {
55        self.height.get()
56    }
57    pub fn size(&self) -> Size {
58        Size::new(self.width(), self.height())
59    }
60}
61impl DataRegion for SurfaceDescriptor {
62    fn data_len(&self) -> u64 {
63        self.len
64    }
65    fn data_offset(&self) -> u64 {
66        self.offset
67    }
68}
69
70#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
71pub struct VolumeDescriptor {
72    width: NonZeroU32,
73    height: NonZeroU32,
74    depth: NonZeroU32,
75    offset: u64,
76    slice_len: u64,
77}
78impl VolumeDescriptor {
79    fn new(
80        width: NonZeroU32,
81        height: NonZeroU32,
82        depth: NonZeroU32,
83        offset: u64,
84        slice_len: u64,
85    ) -> Self {
86        debug_assert!(slice_len > 0);
87        // check that `offset + len` does not overflow
88        debug_assert!(slice_len
89            .checked_mul(depth.get() as u64)
90            .and_then(|len| offset.checked_add(len))
91            .is_some());
92
93        Self {
94            width,
95            height,
96            depth,
97            offset,
98            slice_len,
99        }
100    }
101
102    pub fn width(&self) -> u32 {
103        self.width.get()
104    }
105    pub fn height(&self) -> u32 {
106        self.height.get()
107    }
108    pub fn depth(&self) -> u32 {
109        self.depth.get()
110    }
111    pub fn size(&self) -> Size {
112        Size::new(self.width(), self.height())
113    }
114
115    pub fn get_depth_slice(&self, depth: u32) -> Option<SurfaceDescriptor> {
116        if depth < self.depth() {
117            Some(SurfaceDescriptor {
118                width: self.width,
119                height: self.height,
120                offset: self.offset + depth as u64 * self.slice_len,
121                len: self.slice_len,
122            })
123        } else {
124            None
125        }
126    }
127
128    /// Iterates over all depth slices of the volume.
129    ///
130    /// To get the depth value of a slice, use `.enumerate()`. Example:
131    ///
132    /// ```no_run
133    /// # use dds::{VolumeDescriptor, DataRegion};
134    /// # fn get_volume() -> VolumeDescriptor { todo!() }
135    /// let volume: VolumeDescriptor = get_volume();
136    /// for (depth, slice) in volume.iter_depth_slices().enumerate() {
137    ///     println!("Slice {} starts at {}", depth, slice.data_offset());
138    /// }
139    /// ```
140    pub fn iter_depth_slices(&self) -> impl Iterator<Item = SurfaceDescriptor> {
141        let Self {
142            width,
143            height,
144            offset,
145            slice_len,
146            ..
147        } = *self;
148
149        (0..self.depth()).map(move |depth| SurfaceDescriptor {
150            width,
151            height,
152            offset: offset + depth as u64 * slice_len,
153            len: slice_len,
154        })
155    }
156}
157impl DataRegion for VolumeDescriptor {
158    fn data_len(&self) -> u64 {
159        // Cannot overflow. See `VolumeDescriptor::new`.
160        self.slice_len * self.depth() as u64
161    }
162    fn data_offset(&self) -> u64 {
163        self.offset
164    }
165}
166
167fn to_short_len(len: u64) -> Option<NonZeroU32> {
168    len.try_into().ok().and_then(NonZeroU32::new)
169}
170fn get_texture_len(
171    width: NonZeroU32,
172    height: NonZeroU32,
173    mipmaps: NonZeroU8,
174    pixels: PixelInfo,
175) -> Option<u64> {
176    let size = Size::new(width.get(), height.get());
177
178    let mut len: u64 = 0;
179    for level in 0..mipmaps.get() {
180        let mip_len = pixels.surface_bytes(size.get_mipmap(level))?;
181        len = len.checked_add(mip_len)?;
182    }
183    Some(len)
184}
185
186/// A 2D texture with mipmaps (if any).
187///
188/// See [`DataLayout::Texture`] for more information.
189#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
190pub struct Texture {
191    width: NonZeroU32,
192    height: NonZeroU32,
193    mipmaps: NonZeroU8,
194    pixels: PixelInfo,
195    offset_index: u32,
196    // A cache for data length. This is used to avoid recomputing the length
197    // when the length is isn't too large.
198    short_len: Option<NonZeroU32>,
199}
200impl Texture {
201    /// Creates a new texture at offset 0.
202    fn create_at_offset_0(
203        width: NonZeroU32,
204        height: NonZeroU32,
205        mipmaps: NonZeroU8,
206        pixels: PixelInfo,
207    ) -> Result<Self, LayoutError> {
208        // Check that length and all other calculations do not overflow
209        let len =
210            get_texture_len(width, height, mipmaps, pixels).ok_or(LayoutError::DataLayoutTooBig)?;
211
212        Ok(Self {
213            width,
214            height,
215            mipmaps,
216            pixels,
217            offset_index: 0,
218            short_len: to_short_len(len),
219        })
220    }
221
222    pub fn pixel_info(&self) -> PixelInfo {
223        self.pixels
224    }
225
226    /// The level 0 size of this texture.
227    fn size(&self) -> Size {
228        Size::new(self.width.get(), self.height.get())
229    }
230
231    /// The level 0 mipmap of this texture.
232    pub fn main(&self) -> SurfaceDescriptor {
233        // PANIC SAFETY: This cannot overflow, because we already checked in the constructor
234        let len = self.pixels.surface_bytes(self.size()).unwrap();
235        SurfaceDescriptor::new(self.width, self.height, self.data_offset(), len)
236    }
237    pub fn mipmaps(&self) -> u8 {
238        self.mipmaps.get()
239    }
240    pub fn get(&self, level: u8) -> Option<SurfaceDescriptor> {
241        self.iter_mips().nth(level as usize)
242    }
243    pub fn iter_mips(&self) -> impl Iterator<Item = SurfaceDescriptor> {
244        let mut offset = self.data_offset();
245        let size_0 = self.size();
246        let pixels = self.pixels;
247        (0..self.mipmaps.get()).map(move |level| {
248            let width = get_mipmap_size(size_0.width, level);
249            let height = get_mipmap_size(size_0.height, level);
250            let size = Size::new(width.get(), height.get());
251            // Panic Safety: This cannot overflow, because we already checked in the constructor
252            let len = pixels.surface_bytes(size).unwrap();
253            let surface = SurfaceDescriptor::new(width, height, offset, len);
254            offset += len;
255            surface
256        })
257    }
258
259    /// Internal method. This **assumes** that `offset + len` does not overflow.
260    fn set_offset_index(&mut self, index: u32) {
261        self.offset_index = index;
262    }
263}
264impl DataRegion for Texture {
265    fn data_len(&self) -> u64 {
266        if let Some(short_len) = self.short_len {
267            short_len.get() as u64
268        } else {
269            // Panic Safety: This cannot overflow, because we already checked in the constructor
270            get_texture_len(self.width, self.height, self.mipmaps, self.pixels).unwrap()
271        }
272    }
273    fn data_offset(&self) -> u64 {
274        self.offset_index as u64 * self.data_len()
275    }
276    fn data_end(&self) -> u64 {
277        (self.offset_index as u64 + 1) * self.data_len()
278    }
279}
280
281fn get_volume_len(
282    width: NonZeroU32,
283    height: NonZeroU32,
284    depth: NonZeroU32,
285    mipmaps: NonZeroU8,
286    pixels: PixelInfo,
287) -> Option<u64> {
288    let mut len: u64 = 0;
289    for level in 0..mipmaps.get() {
290        let width = get_mipmap_size(width.get(), level);
291        let height = get_mipmap_size(height.get(), level);
292        let depth = get_mipmap_size(depth.get(), level);
293
294        let slice_size = Size::new(width.get(), height.get());
295        let slice_len = pixels.surface_bytes(slice_size)?;
296        let mip_len = slice_len.checked_mul(depth.get() as u64)?;
297
298        len = len.checked_add(mip_len)?;
299    }
300
301    Some(len)
302}
303
304/// A 3D texture with mipmaps (if any).
305///
306/// See [`DataLayout::Volume`] for more information.
307#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
308pub struct Volume {
309    width: NonZeroU32,
310    height: NonZeroU32,
311    depth: NonZeroU32,
312    mipmaps: NonZeroU8,
313    pixels: PixelInfo,
314}
315impl Volume {
316    /// Creates a new volume at offset 0.
317    fn create_at_offset_0(
318        width: NonZeroU32,
319        height: NonZeroU32,
320        depth: NonZeroU32,
321        mipmaps: NonZeroU8,
322        pixels: PixelInfo,
323    ) -> Result<Self, LayoutError> {
324        // compute the length of the entire volume (including mips) to check
325        // for overflows, so we can assume no overflows in the rest of the code
326        _ = get_volume_len(width, height, depth, mipmaps, pixels)
327            .ok_or(LayoutError::DataLayoutTooBig)?;
328
329        Ok(Self {
330            width,
331            height,
332            depth,
333            mipmaps,
334            pixels,
335        })
336    }
337
338    pub fn pixel_info(&self) -> PixelInfo {
339        self.pixels
340    }
341
342    /// The level 0 mipmap of this volume.
343    pub fn main(&self) -> VolumeDescriptor {
344        let slice_size = Size::new(self.width.get(), self.height.get());
345        // Panic Safety: This cannot overflow, because we already checked in the constructor
346        let slice_len = self.pixels.surface_bytes(slice_size).unwrap();
347
348        VolumeDescriptor {
349            width: self.width,
350            height: self.height,
351            depth: self.depth,
352            offset: 0,
353            slice_len,
354        }
355    }
356    pub fn mipmaps(&self) -> u8 {
357        self.mipmaps.get()
358    }
359    pub fn get(&self, level: u8) -> Option<VolumeDescriptor> {
360        self.iter_mips().nth(level as usize)
361    }
362    pub fn iter_mips(&self) -> impl Iterator<Item = VolumeDescriptor> {
363        let mut offset = 0;
364        let width_0 = self.width.get();
365        let height_0 = self.height.get();
366        let depth_0 = self.depth.get();
367        let pixels = self.pixels;
368        (0..self.mipmaps.get()).map(move |level| {
369            let width = get_mipmap_size(width_0, level);
370            let height = get_mipmap_size(height_0, level);
371            let depth = get_mipmap_size(depth_0, level);
372            let slice_size = Size::new(width.get(), height.get());
373            // Panic Safety: This cannot overflow, because we already checked in the constructor
374            let slice_len = pixels.surface_bytes(slice_size).unwrap();
375            let volume = VolumeDescriptor::new(width, height, depth, offset, slice_len);
376            offset += depth.get() as u64 * slice_len;
377            volume
378        })
379    }
380}
381impl DataRegion for Volume {
382    fn data_len(&self) -> u64 {
383        // Panic Safety: This cannot overflow, because we already checked in the constructor
384        get_volume_len(
385            self.width,
386            self.height,
387            self.depth,
388            self.mipmaps,
389            self.pixels,
390        )
391        .unwrap()
392    }
393    fn data_offset(&self) -> u64 {
394        // if there is a volume, it is the only object in the data section
395        0
396    }
397}
398
399#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
400pub enum TextureArrayKind {
401    /// An array of textures.
402    Textures,
403    /// An array of cube maps.
404    ///
405    /// The number of textures in the array is a multiple of 6.
406    CubeMaps,
407    /// An array of at most 5 sides of a cube maps.
408    PartialCubeMap(CubeMapFaces),
409}
410bitflags! {
411    /// A bitset representing which faces of a cube map are present.
412    #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
413    pub struct CubeMapFaces: u8 {
414        const POSITIVE_X = 0b0000_0001;
415        const NEGATIVE_X = 0b0000_0010;
416        const POSITIVE_Y = 0b0000_0100;
417        const NEGATIVE_Y = 0b0000_1000;
418        const POSITIVE_Z = 0b0001_0000;
419        const NEGATIVE_Z = 0b0010_0000;
420        const ALL = Self::POSITIVE_X.bits() | Self::NEGATIVE_X.bits() | Self::POSITIVE_Y.bits() | Self::NEGATIVE_Y.bits() | Self::POSITIVE_Z.bits() | Self::NEGATIVE_Z.bits();
421    }
422}
423impl From<Caps2> for CubeMapFaces {
424    fn from(value: Caps2) -> Self {
425        let faces = (value & Caps2::CUBE_MAP_ALL_FACES).bits();
426        CubeMapFaces::from_bits_truncate((faces >> 10) as u8)
427    }
428}
429impl From<CubeMapFaces> for Caps2 {
430    fn from(value: CubeMapFaces) -> Self {
431        let faces = value.bits() & 0b11_1111;
432        Caps2::from_bits_truncate((faces as u32) << 10)
433    }
434}
435impl CubeMapFaces {
436    /// Returns the number of cube map sides set in this bit mask.
437    pub fn count(&self) -> u32 {
438        self.bits().count_ones()
439    }
440}
441
442/// An array of textures.
443///
444/// See [`DataLayout::TextureArray`] for more information.
445#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
446pub struct TextureArray {
447    kind: TextureArrayKind,
448    array_len: u32,
449
450    width: NonZeroU32,
451    height: NonZeroU32,
452    mipmaps: NonZeroU8,
453    pixels: PixelInfo,
454    texture_short_len: Option<NonZeroU32>,
455}
456impl TextureArray {
457    fn new(kind: TextureArrayKind, array_len: u32, first: Texture) -> Result<Self, LayoutError> {
458        // must start at offset 0
459        debug_assert_eq!(first.data_offset(), 0);
460
461        // Check that the entire array does not overflow
462        _ = first
463            .data_len()
464            .checked_mul(array_len as u64)
465            .ok_or(LayoutError::DataLayoutTooBig)?;
466
467        Ok(Self {
468            kind,
469            array_len,
470
471            width: first.width,
472            height: first.height,
473            mipmaps: first.mipmaps,
474            pixels: first.pixels,
475            texture_short_len: first.short_len,
476        })
477    }
478
479    pub fn pixel_info(&self) -> PixelInfo {
480        self.pixels
481    }
482
483    pub fn kind(&self) -> TextureArrayKind {
484        self.kind
485    }
486
487    pub fn is_empty(&self) -> bool {
488        self.array_len == 0
489    }
490    pub fn len(&self) -> usize {
491        self.array_len as usize
492    }
493
494    pub fn size(&self) -> Size {
495        Size::new(self.width.get(), self.height.get())
496    }
497    pub fn mipmaps(&self) -> u8 {
498        self.mipmaps.get()
499    }
500
501    pub(crate) fn first(&self) -> Texture {
502        Texture {
503            width: self.width,
504            height: self.height,
505            mipmaps: self.mipmaps,
506            pixels: self.pixels,
507            offset_index: 0,
508            short_len: self.texture_short_len,
509        }
510    }
511    pub fn get(&self, index: usize) -> Option<Texture> {
512        if index < self.array_len as usize {
513            let mut texture = self.first();
514            texture.set_offset_index(index as u32);
515            Some(texture)
516        } else {
517            None
518        }
519    }
520    pub fn iter(&self) -> impl Iterator<Item = Texture> {
521        let mut texture = self.first();
522        (0..self.array_len).map(move |index| {
523            texture.set_offset_index(index);
524            texture
525        })
526    }
527}
528impl DataRegion for TextureArray {
529    fn data_len(&self) -> u64 {
530        // Panic Safety: this can't overflow, because we checked in the constructor
531        self.first().data_len() * self.array_len as u64
532    }
533    fn data_offset(&self) -> u64 {
534        0
535    }
536}
537
538/// The type and layout of the surfaces/volumes in the data section of a DDS file.
539///
540/// DDS is a container format and supports a few different data types (e.g.
541/// images, volumes, cube maps). While varied, the data section of a DDS file
542/// is always a contiguous block of data and its layout is 100% determined by
543/// the header. There are no gaps, padding, or markers in the data section,
544/// just the raw data of the surfaces/volumes. This makes it necessary for
545/// readers to know the layout of the data in order to read it correctly.
546///
547/// [`DataLayout`] contains all of this information while providing a simple
548/// interface to iterate over the surfaces/volumes.
549///
550/// Note: [`DataLayout`] provides no methods to read the data itself. Use
551/// [`Decoder`](crate::Decoder) or [`Encoder`](crate::Encoder) to read/write the
552/// data section.
553#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
554pub enum DataLayout {
555    /// A single texture with mipmaps (if any).
556    ///
557    /// Example:
558    ///
559    /// ```
560    /// # use dds::{*, header::*};
561    /// let header = Header::new_image(123, 345, Format::BC1_UNORM).with_mipmaps();
562    /// let layout = DataLayout::from_header(&header).unwrap();
563    /// let texture = layout.texture().unwrap();
564    /// assert_eq!(texture.main().size(), Size::new(123, 345));
565    ///
566    /// // iterate over all mipmaps
567    /// for (level, mipmap) in texture.iter_mips().enumerate() {
568    ///     println!("Mipmap {}: {}x{}", level, mipmap.width(), mipmap.height());
569    /// }
570    /// ```
571    Texture(Texture),
572    /// A 3D texture with mipmaps (if any).
573    ///
574    /// DDS uses depth slices to represent 3D textures.
575    ///
576    /// Example:
577    ///
578    /// ```
579    /// # use dds::{*, header::*};
580    /// let header = Header::new_volume(123, 345, 678, Format::BC1_UNORM).with_mipmaps();
581    /// let layout = DataLayout::from_header(&header).unwrap();
582    /// let volume = layout.volume().unwrap();
583    /// assert_eq!(volume.main().size(), Size::new(123, 345));
584    /// assert_eq!(volume.main().depth(), 678);
585    ///
586    /// // iterate over all mipmaps
587    /// for (level, mipmap) in volume.iter_mips().enumerate() {
588    ///     println!("Mipmap {}: {}x{}x{}", level, mipmap.width(), mipmap.height(), mipmap.depth());
589    /// }
590    /// ```
591    Volume(Volume),
592    /// A simply array of 2D textures.
593    ///
594    /// All textures within the array have the same size, mipmap count, and
595    /// pixel format.
596    ///
597    /// Example:
598    ///
599    /// ```
600    /// # use dds::{*, header::*};
601    /// let mut header = Dx10Header::new_image(123, 345, DxgiFormat::BC1_UNORM);
602    /// header.array_size = 10;
603    /// let layout = DataLayout::from_header(&header.into()).unwrap();
604    /// let array = layout.texture_array().unwrap();
605    /// assert_eq!(array.len(), 10);
606    /// assert_eq!(array.size(), Size::new(123, 345));
607    ///
608    /// // iterate over all textures in the array
609    /// for texture in array.iter() {
610    ///     for (level, mipmap) in texture.iter_mips().enumerate() {
611    ///         println!("Mipmap {}: {}x{}", level, mipmap.width(), mipmap.height());
612    ///     }
613    /// }
614    /// ```
615    ///
616    /// ## Cube maps
617    ///
618    /// Cube maps are a special case of texture arrays. You can differentiate
619    /// between normal texture arrays and cube maps using [`TextureArray::kind()`].
620    ///
621    /// The faces of a cube map are always stored in the order:
622    ///
623    /// 1. Positive X
624    /// 2. Negative X
625    /// 3. Positive Y
626    /// 4. Negative Y
627    /// 5. Positive Z
628    /// 6. Negative Z
629    ///
630    /// Note that cube maps come in 2 flavors: full cube maps and partial cube
631    /// maps. Partial cube maps are cube maps with fewer than 6 faces (see
632    /// [`TextureArrayKind::PartialCubeMap`]) and are **not** supported by DX10+.
633    /// In practice, partial cube maps are rarely used. This library only supports
634    /// them for completeness.
635    ///
636    /// Example:
637    ///
638    /// ```
639    /// # use dds::{*, header::*};
640    /// let mut header = Header::new_cube_map(256, 256, Format::BC1_UNORM).with_mipmaps();
641    /// let layout = DataLayout::from_header(&header).unwrap();
642    /// let array = layout.texture_array().unwrap();
643    /// assert_eq!(array.kind(), TextureArrayKind::CubeMaps);
644    /// assert_eq!(array.len(), 6); // 6 faces
645    /// assert_eq!(array.size(), Size::new(256, 256));
646    ///
647    /// let positive_x = array.get(0).unwrap();
648    /// let negative_x = array.get(1).unwrap();
649    /// let positive_y = array.get(2).unwrap();
650    /// let negative_y = array.get(3).unwrap();
651    /// let positive_z = array.get(4).unwrap();
652    /// let negative_z = array.get(5).unwrap();
653    /// ```
654    ///
655    /// DX10 also supports arrays of cube maps. Example:
656    ///
657    /// ```
658    /// # use dds::{*, header::*};
659    /// let mut header = Dx10Header::new_cube_map(256, 256, DxgiFormat::BC1_UNORM);
660    /// header.array_size = 3;
661    /// let layout = DataLayout::from_header(&header.into()).unwrap();
662    /// let array = layout.texture_array().unwrap();
663    /// assert_eq!(array.kind(), TextureArrayKind::CubeMaps);
664    /// assert_eq!(array.len(), 18); // 6 faces * 3 cube maps
665    /// ```
666    TextureArray(TextureArray),
667}
668impl DataLayout {
669    pub fn from_header(header: &Header) -> Result<Self, DecodingError> {
670        let layout = Self::from_header_with(header, PixelInfo::from_header(header)?)?;
671        Ok(layout)
672    }
673    pub fn from_header_with(header: &Header, pixel_info: PixelInfo) -> Result<Self, LayoutError> {
674        match header {
675            Header::Dx10(dx10) => {
676                if dx10.is_cube_map() {
677                    if dx10.resource_dimension != ResourceDimension::Texture2D {
678                        return Err(LayoutError::InvalidCubeMapFaces);
679                    }
680
681                    let info = SurfaceLayoutInfo::from_header(header, pixel_info)?;
682                    let array_size = dx10.array_size;
683
684                    // "For a 2D texture that is also a cube-map texture, array_size represents the number of cubes."
685                    let cube_map_faces = array_size
686                        .checked_mul(6)
687                        .ok_or(LayoutError::ArraySizeTooBig(array_size))?;
688                    return Ok(Self::TextureArray(
689                        info.create_array(TextureArrayKind::CubeMaps, cube_map_faces)?,
690                    ));
691                }
692
693                match dx10.resource_dimension {
694                    ResourceDimension::Texture1D | ResourceDimension::Texture2D => {
695                        let mut info = SurfaceLayoutInfo::from_header(header, pixel_info)?;
696                        if dx10.resource_dimension == ResourceDimension::Texture1D {
697                            info.height = NON_ZERO_U32_ONE;
698                        }
699                        let array_size = dx10.array_size;
700
701                        if array_size == 1 {
702                            Ok(Self::Texture(info.create()?))
703                        } else {
704                            Ok(Self::TextureArray(
705                                info.create_array(TextureArrayKind::Textures, array_size)?,
706                            ))
707                        }
708                    }
709                    ResourceDimension::Texture3D => {
710                        let info = VolumeLayoutInfo::from_header(header, pixel_info)?;
711                        Ok(Self::Volume(info.create()?))
712                    }
713                }
714            }
715            Header::Dx9(dx9) => {
716                if let Some(faces) = dx9.cube_map_faces() {
717                    if dx9.is_volume() {
718                        return Err(LayoutError::InvalidCubeMapFaces);
719                    }
720
721                    let info = SurfaceLayoutInfo::from_header(header, pixel_info)?;
722                    let face_count = faces.count();
723
724                    let kind = if face_count == 6 {
725                        TextureArrayKind::CubeMaps
726                    } else {
727                        TextureArrayKind::PartialCubeMap(faces)
728                    };
729                    Ok(Self::TextureArray(info.create_array(kind, face_count)?))
730                } else if dx9.is_volume() {
731                    let info = VolumeLayoutInfo::from_header(header, pixel_info)?;
732                    Ok(Self::Volume(info.create()?))
733                } else {
734                    let info = SurfaceLayoutInfo::from_header(header, pixel_info)?;
735                    Ok(Self::Texture(info.create()?))
736                }
737            }
738        }
739    }
740
741    /// The size of the level 0 object.
742    ///
743    /// For single textures and texture arrays, this will return the size of the
744    /// texture (mipmap level 0). For cube maps, this will return the size of
745    /// the individual faces (mipmap level 0). For volume textures, this will
746    /// return the size of the first depth slice (mipmap level 0).
747    pub fn main_size(&self) -> Size {
748        match self {
749            DataLayout::Texture(texture) => texture.size(),
750            DataLayout::Volume(volume) => volume.main().size(),
751            DataLayout::TextureArray(texture_array) => texture_array.size(),
752        }
753    }
754
755    /// If this layout is a [`DataLayout::Texture`], returns the texture.
756    pub fn texture(&self) -> Option<Texture> {
757        match self {
758            DataLayout::Texture(texture) => Some(*texture),
759            _ => None,
760        }
761    }
762    /// If this layout is a [`DataLayout::Volume`], returns the volume.
763    pub fn volume(&self) -> Option<Volume> {
764        match self {
765            DataLayout::Volume(volume) => Some(*volume),
766            _ => None,
767        }
768    }
769    /// If this layout is a [`DataLayout::TextureArray`], returns the texture
770    /// array.
771    pub fn texture_array(&self) -> Option<TextureArray> {
772        match self {
773            DataLayout::TextureArray(array) => Some(*array),
774            _ => None,
775        }
776    }
777
778    /// Whether this layout is a [`DataLayout::Texture`].
779    pub fn is_texture(&self) -> bool {
780        matches!(self, DataLayout::Texture(_))
781    }
782    /// Whether this layout is a [`DataLayout::Volume`].
783    pub fn is_volume(&self) -> bool {
784        matches!(self, DataLayout::Volume(_))
785    }
786    /// Whether this layout is a [`DataLayout::TextureArray`].
787    pub fn is_texture_array(&self) -> bool {
788        matches!(self, DataLayout::TextureArray(_))
789    }
790    /// Whether this layout is a cube map or partial cube map.
791    ///
792    /// If `true` is returned, this layout is guaranteed to be a
793    /// [`DataLayout::TextureArray`]. The [`TextureArray::kind()`] will be
794    /// either [`TextureArrayKind::CubeMaps`] or
795    /// [`TextureArrayKind::PartialCubeMap`].
796    ///
797    /// The texture array is **not** guaranteed to be contain exactly one
798    /// (partial) cube map, meaning that the texture array may contain
799    /// multiple (partial) cube maps.
800    pub fn is_cube_map(&self) -> bool {
801        matches!(
802            self,
803            DataLayout::TextureArray(TextureArray {
804                kind: TextureArrayKind::CubeMaps | TextureArrayKind::PartialCubeMap(_),
805                ..
806            })
807        )
808    }
809
810    pub fn pixel_info(&self) -> PixelInfo {
811        match self {
812            DataLayout::Texture(texture) => texture.pixel_info(),
813            DataLayout::Volume(volume) => volume.pixel_info(),
814            DataLayout::TextureArray(array) => array.pixel_info(),
815        }
816    }
817}
818impl DataRegion for DataLayout {
819    fn data_len(&self) -> u64 {
820        match self {
821            DataLayout::Texture(texture) => texture.data_len(),
822            DataLayout::Volume(volume) => volume.data_len(),
823            DataLayout::TextureArray(textures) => textures.data_len(),
824        }
825    }
826    fn data_offset(&self) -> u64 {
827        // the data layout describes the entire data section, so it has to start at 0
828        0
829    }
830}
831
832fn parse_dimension(dim: u32) -> Result<NonZeroU32, LayoutError> {
833    NonZeroU32::new(dim).ok_or(LayoutError::ZeroDimension)
834}
835fn parse_mipmap_count(mipmaps: NonZeroU32) -> Result<NonZeroU8, LayoutError> {
836    NonZeroU8::try_from(mipmaps).map_err(|_| LayoutError::TooManyMipMaps(mipmaps.get()))
837}
838
839#[derive(Debug, Clone, PartialEq, Eq, Hash)]
840struct SurfaceLayoutInfo {
841    width: NonZeroU32,
842    height: NonZeroU32,
843    mipmaps: NonZeroU8,
844    pixels: PixelInfo,
845}
846impl SurfaceLayoutInfo {
847    fn from_header(header: &Header, pixels: PixelInfo) -> Result<Self, LayoutError> {
848        Ok(Self {
849            width: parse_dimension(header.width())?,
850            height: parse_dimension(header.height())?,
851            mipmaps: parse_mipmap_count(header.mipmap_count())?,
852            pixels,
853        })
854    }
855
856    fn create(&self) -> Result<Texture, LayoutError> {
857        Texture::create_at_offset_0(self.width, self.height, self.mipmaps, self.pixels)
858    }
859
860    fn create_array(
861        &self,
862        kind: TextureArrayKind,
863        array_len: u32,
864    ) -> Result<TextureArray, LayoutError> {
865        TextureArray::new(kind, array_len, self.create()?)
866    }
867}
868#[derive(Debug, Clone, PartialEq, Eq, Hash)]
869struct VolumeLayoutInfo {
870    width: NonZeroU32,
871    height: NonZeroU32,
872    depth: NonZeroU32,
873    mipmaps: NonZeroU8,
874    pixels: PixelInfo,
875}
876impl VolumeLayoutInfo {
877    fn from_header(header: &Header, pixels: PixelInfo) -> Result<Self, LayoutError> {
878        Ok(Self {
879            width: parse_dimension(header.width())?,
880            height: parse_dimension(header.height())?,
881            depth: parse_dimension(header.depth().ok_or(LayoutError::MissingDepth)?)?,
882            mipmaps: parse_mipmap_count(header.mipmap_count())?,
883            pixels,
884        })
885    }
886
887    fn create(&self) -> Result<Volume, LayoutError> {
888        Volume::create_at_offset_0(
889            self.width,
890            self.height,
891            self.depth,
892            self.mipmaps,
893            self.pixels,
894        )
895    }
896}