1use crate::error::Error;
27use crate::header::Caps2;
28use crate::header10::MiscFlag;
29use crate::DdsBase;
30use std::ops::Range;
31
32#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
34#[repr(u32)]
35pub enum CubemapFace {
36 PositiveX = 0,
37 NegativeX = 1,
38 PositiveY = 2,
39 NegativeY = 3,
40 PositiveZ = 4,
41 NegativeZ = 5,
42}
43
44impl CubemapFace {
45 pub const ALL: [CubemapFace; 6] = [
46 CubemapFace::PositiveX,
47 CubemapFace::NegativeX,
48 CubemapFace::PositiveY,
49 CubemapFace::NegativeY,
50 CubemapFace::PositiveZ,
51 CubemapFace::NegativeZ,
52 ];
53
54 pub fn from_index(index: u32) -> Result<CubemapFace, Error> {
55 match index {
56 0 => Ok(CubemapFace::PositiveX),
57 1 => Ok(CubemapFace::NegativeX),
58 2 => Ok(CubemapFace::PositiveY),
59 3 => Ok(CubemapFace::NegativeY),
60 4 => Ok(CubemapFace::PositiveZ),
61 5 => Ok(CubemapFace::NegativeZ),
62 _ => Err(Error::OutOfBounds),
63 }
64 }
65
66 pub fn index(self) -> u32 {
67 self as u32
68 }
69}
70
71#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
77pub struct SubresourceId {
78 pub mip: u32,
79 pub layer: u32,
80 pub face: u32,
81}
82
83impl SubresourceId {
84 pub const fn new(mip: u32, layer: u32, face: u32) -> Self {
85 Self { mip, layer, face }
86 }
87
88 pub const fn mip_layer(mip: u32, layer: u32) -> Self {
89 Self {
90 mip,
91 layer,
92 face: 0,
93 }
94 }
95
96 pub const fn cubemap(mip: u32, layer: u32, face: CubemapFace) -> Self {
97 Self {
98 mip,
99 layer,
100 face: face as u32,
101 }
102 }
103}
104
105#[derive(Debug, Clone, Copy)]
107#[non_exhaustive]
108pub struct SurfaceView<'a> {
109 pub id: SubresourceId,
110 pub width: u32,
111 pub height: u32,
112 pub depth: u32,
113 pub data: &'a [u8],
114}
115
116impl<D: AsRef<[u8]>> DdsBase<D> {
117 pub fn is_cubemap(&self) -> bool {
119 if let Some(ref h10) = self.header10 {
120 h10.misc_flag.contains(MiscFlag::TEXTURECUBE)
121 } else {
122 self.header.caps2.contains(Caps2::CUBEMAP)
123 }
124 }
125
126 pub fn subresource_layer_count(&self) -> u32 {
130 if self.is_cubemap() {
131 self.cube_count()
132 } else if let Some(ref h10) = self.header10 {
133 h10.array_size.max(1)
134 } else {
135 1
136 }
137 }
138
139 pub fn subresource_face_count(&self) -> u32 {
141 if self.is_cubemap() {
142 6
143 } else {
144 1
145 }
146 }
147
148 pub fn cube_count(&self) -> u32 {
150 if !self.is_cubemap() {
151 return 0;
152 }
153 if let Some(ref h10) = self.header10 {
154 h10.array_size.max(1)
155 } else {
156 1
157 }
158 }
159
160 pub fn physical_slice_count(&self) -> u32 {
162 if self.is_cubemap() {
163 self.cube_count().saturating_mul(6)
164 } else if let Some(ref h10) = self.header10 {
165 h10.array_size.max(1)
166 } else {
167 1
168 }
169 }
170
171 pub fn mip_dimensions(&self, mip: u32) -> Result<(u32, u32, u32), Error> {
173 if mip >= self.get_num_mipmap_levels() {
174 return Err(Error::OutOfBounds);
175 }
176 let width = (self.header.width >> mip).max(1);
177 let height = (self.header.height >> mip).max(1);
178 let depth = (self.header.depth.unwrap_or(1) >> mip).max(1);
179 Ok((width, height, depth))
180 }
181
182 pub fn subresource_range(&self, id: SubresourceId) -> Result<Range<usize>, Error> {
184 let (offset, size) = self.subresource_offset_and_size(id)?;
185 let start = offset as usize;
186 let end = start
187 .checked_add(size as usize)
188 .ok_or(Error::OutOfBounds)?;
189 if end > self.data.as_ref().len() {
190 return Err(Error::TruncatedData);
191 }
192 Ok(start..end)
193 }
194
195 pub fn surface(&self, id: SubresourceId) -> Result<SurfaceView<'_>, Error> {
197 let range = self.subresource_range(id)?;
198 let (width, height, depth) = self.mip_dimensions(id.mip)?;
199 Ok(SurfaceView {
200 id,
201 width,
202 height,
203 depth,
204 data: &self.data.as_ref()[range],
205 })
206 }
207
208 pub(crate) fn subresource_chain_ranges(
215 &self,
216 layer: u32,
217 face: u32,
218 ) -> Result<Vec<Range<usize>>, Error> {
219 let levels = self.get_num_mipmap_levels();
220 let id0 = SubresourceId::new(0, layer, face);
221 self.validate_subresource_id(id0)?;
222 let physical = self.physical_slice_index(id0)?;
223 let array_stride = self.get_array_stride()?;
224 let base = physical
225 .checked_mul(array_stride)
226 .ok_or(Error::OutOfBounds)?;
227 let mut current = self
228 .get_main_texture_size()
229 .ok_or(Error::UnsupportedFormat)?;
230 let min_size = self.get_min_mipmap_size_in_bytes();
231 let mut offset = base;
232 let data_len = self.data.as_ref().len();
233 let mut out = Vec::with_capacity(levels as usize);
234 for _ in 0..levels {
235 let start = offset as usize;
236 let end = start
237 .checked_add(current as usize)
238 .ok_or(Error::OutOfBounds)?;
239 if end > data_len {
240 return Err(Error::TruncatedData);
241 }
242 out.push(start..end);
243 offset = offset.checked_add(current).ok_or(Error::OutOfBounds)?;
244 current /= 4;
245 if current < min_size {
246 current = min_size;
247 }
248 }
249 Ok(out)
250 }
251
252 fn subresource_offset_and_size(&self, id: SubresourceId) -> Result<(u32, u32), Error> {
253 self.validate_subresource_id(id)?;
254
255 let physical = self.physical_slice_index(id)?;
256 let array_stride = self.get_array_stride()?;
257 let (mip_offset, mip_size) = self.mip_offset_and_size_in_chain(id.mip)?;
258
259 let offset = physical
260 .checked_mul(array_stride)
261 .and_then(|base| base.checked_add(mip_offset))
262 .ok_or(Error::OutOfBounds)?;
263
264 Ok((offset, mip_size))
265 }
266
267 fn validate_subresource_id(&self, id: SubresourceId) -> Result<(), Error> {
268 if id.mip >= self.get_num_mipmap_levels() {
269 return Err(Error::OutOfBounds);
270 }
271 if id.layer >= self.subresource_layer_count() {
272 return Err(Error::OutOfBounds);
273 }
274 if self.is_cubemap() {
275 if id.face >= 6 {
276 return Err(Error::OutOfBounds);
277 }
278 } else if id.face != 0 {
279 return Err(Error::OutOfBounds);
280 }
281 Ok(())
282 }
283
284 fn physical_slice_index(&self, id: SubresourceId) -> Result<u32, Error> {
285 if self.is_cubemap() {
286 id.layer
287 .checked_mul(6)
288 .and_then(|base| base.checked_add(id.face))
289 .ok_or(Error::OutOfBounds)
290 } else {
291 Ok(id.layer)
292 }
293 }
294
295 fn mip_offset_and_size_in_chain(&self, mip: u32) -> Result<(u32, u32), Error> {
297 let levels = self.get_num_mipmap_levels();
298 if mip >= levels {
299 return Err(Error::OutOfBounds);
300 }
301 let mut current = self
302 .get_main_texture_size()
303 .ok_or(Error::UnsupportedFormat)?;
304 let min_size = self.get_min_mipmap_size_in_bytes();
305 let mut offset = 0_u32;
306 for level in 0..levels {
307 if level == mip {
308 return Ok((offset, current));
309 }
310 offset = offset.checked_add(current).ok_or(Error::OutOfBounds)?;
311 current /= 4;
312 if current < min_size {
313 current = min_size;
314 }
315 }
316 Err(Error::OutOfBounds)
317 }
318}
319
320impl<D: AsRef<[u8]> + AsMut<[u8]>> DdsBase<D> {
323 pub fn surface_mut(&mut self, id: SubresourceId) -> Result<SurfaceViewMut<'_>, Error> {
325 let range = self.subresource_range(id)?;
326 let (width, height, depth) = self.mip_dimensions(id.mip)?;
327 Ok(SurfaceViewMut {
328 id,
329 width,
330 height,
331 depth,
332 data: &mut self.data.as_mut()[range],
333 })
334 }
335}
336
337#[derive(Debug)]
339#[non_exhaustive]
340pub struct SurfaceViewMut<'a> {
341 pub id: SubresourceId,
342 pub width: u32,
343 pub height: u32,
344 pub depth: u32,
345 pub data: &'a mut [u8],
346}