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 fn data_len(&self) -> u64;
17 fn data_offset(&self) -> u64;
19 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 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 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 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 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#[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 short_len: Option<NonZeroU32>,
199}
200impl Texture {
201 fn create_at_offset_0(
203 width: NonZeroU32,
204 height: NonZeroU32,
205 mipmaps: NonZeroU8,
206 pixels: PixelInfo,
207 ) -> Result<Self, LayoutError> {
208 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 fn size(&self) -> Size {
228 Size::new(self.width.get(), self.height.get())
229 }
230
231 pub fn main(&self) -> SurfaceDescriptor {
233 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 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 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 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#[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 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 _ = 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 pub fn main(&self) -> VolumeDescriptor {
344 let slice_size = Size::new(self.width.get(), self.height.get());
345 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 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 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 0
396 }
397}
398
399#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
400pub enum TextureArrayKind {
401 Textures,
403 CubeMaps,
407 PartialCubeMap(CubeMapFaces),
409}
410bitflags! {
411 #[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 pub fn count(&self) -> u32 {
438 self.bits().count_ones()
439 }
440}
441
442#[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 debug_assert_eq!(first.data_offset(), 0);
460
461 _ = 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 self.first().data_len() * self.array_len as u64
532 }
533 fn data_offset(&self) -> u64 {
534 0
535 }
536}
537
538#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
554pub enum DataLayout {
555 Texture(Texture),
572 Volume(Volume),
592 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 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 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 pub fn texture(&self) -> Option<Texture> {
757 match self {
758 DataLayout::Texture(texture) => Some(*texture),
759 _ => None,
760 }
761 }
762 pub fn volume(&self) -> Option<Volume> {
764 match self {
765 DataLayout::Volume(volume) => Some(*volume),
766 _ => None,
767 }
768 }
769 pub fn texture_array(&self) -> Option<TextureArray> {
772 match self {
773 DataLayout::TextureArray(array) => Some(*array),
774 _ => None,
775 }
776 }
777
778 pub fn is_texture(&self) -> bool {
780 matches!(self, DataLayout::Texture(_))
781 }
782 pub fn is_volume(&self) -> bool {
784 matches!(self, DataLayout::Volume(_))
785 }
786 pub fn is_texture_array(&self) -> bool {
788 matches!(self, DataLayout::TextureArray(_))
789 }
790 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 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}