1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
use std::io::{Read, Seek};
use crate::{
decode, decode_rect,
header::{Header, ParseOptions},
iter::{SurfaceInfo, SurfaceIterator},
util, ColorFormat, CubeMapFaces, DataLayout, DecodeOptions, DecodingError, Format,
ImageViewMut, Offset, Size, TextureArrayKind,
};
/// A decoder for reading the pixel data of a DDS file.
///
/// See [crate-level documentation](crate) for usage examples.
pub struct Decoder<R> {
reader: R,
header: Header,
format: Format,
layout: DataLayout,
iter: SurfaceIterator,
pub options: DecodeOptions,
}
impl<R> Decoder<R> {
/// Creates a new decoder from the given reader.
///
/// Same as [`Self::new_with_options`] with default options.
pub fn new(reader: R) -> Result<Self, DecodingError>
where
R: Read,
{
Self::new_with_options(reader, &ParseOptions::default())
}
/// Creates a new decoder from the given reader.
///
/// This will read the header from the reader. How the header is read can
/// be configured with the given options.
pub fn new_with_options(mut reader: R, options: &ParseOptions) -> Result<Self, DecodingError>
where
R: Read,
{
let header = Header::read(&mut reader, options)?;
Self::from_header(reader, header)
}
/// Creates a new decoder from the given reader and header.
///
/// Same as [`Self::from_header_with`] with the [`Format`] being detected
/// from the given header.
pub fn from_header(reader: R, header: Header) -> Result<Self, DecodingError> {
let format = Format::from_header(&header)?;
Self::from_header_with(reader, header, format)
}
/// Creates a new decoder from the given reader, header, and format.
///
/// Calling this method will NOT read data from the reader. The header and
/// format are used to determine the layout of the data in the DDS file.
/// The reader will only be used again when reading surfaces or seeking.
pub fn from_header_with(
reader: R,
header: Header,
format: Format,
) -> Result<Self, DecodingError> {
let layout = DataLayout::from_header_with(&header, format.into())?;
Ok(Self {
reader,
header,
format,
layout,
iter: SurfaceIterator::new(layout),
options: DecodeOptions::default(),
})
}
pub fn header(&self) -> &Header {
&self.header
}
pub fn format(&self) -> Format {
self.format
}
pub fn layout(&self) -> DataLayout {
self.layout
}
/// The size of the level 0 object.
///
/// For single textures and texture arrays, this will return the size of the
/// texture (mipmap level 0). For cube maps, this will return the size of
/// the individual faces (mipmap level 0). For volume textures, this will
/// return the size of the first depth slice (mipmap level 0).
pub fn main_size(&self) -> Size {
self.layout.main_size()
}
/// The native color of the DDS file.
///
/// See [`Format::precision`] for more information about the precision of
/// the color format.
pub fn native_color(&self) -> ColorFormat {
self.format.color()
}
/// Reads the next surface into the given buffer.
///
/// The next surface is determined by the data layout of the DDS file. For
/// volume textures, this function will read the next depth slice. See
/// [`Self::surface_info`] for more information about the next surface.
///
/// If [Self::is_done] is true, this function will return an error.
pub fn read_surface(&mut self, image: ImageViewMut) -> Result<(), DecodingError>
where
R: Read,
{
let current = self.iter.current().ok_or(DecodingError::NoMoreSurfaces)?;
if image.size() != current.size() {
return Err(DecodingError::UnexpectedSurfaceSize);
}
decode(&mut self.reader, image, self.format, &self.options)?;
self.iter.advance();
Ok(())
}
/// Reads a rectangle of the next surface into the given buffer. The
/// rectangle is defined by the given offset and the size of `image`.
///
/// Similarly to [`Decoder::read_surface`], this operation will consume the
/// current surface and advance to the next one. Reading multiple rectangles
/// from the same surface is possible with [`Decoder::rewind_to_previous_surface`].
pub fn read_surface_rect(
&mut self,
image: ImageViewMut,
offset: Offset,
) -> Result<(), DecodingError>
where
R: Read + Seek,
{
let current = self.iter.current().ok_or(DecodingError::NoMoreSurfaces)?;
decode_rect(
&mut self.reader,
image,
offset,
current.size(),
self.format,
&self.options,
)?;
self.iter.advance();
Ok(())
}
/// Skips over the next surface.
///
/// Returns an error if there are no more surfaces.
pub fn skip_surface(&mut self) -> Result<(), DecodingError>
where
R: Seek,
{
let current = self.iter.current().ok_or(DecodingError::NoMoreSurfaces)?;
util::io_skip_exact(&mut self.reader, current.data_len())?;
self.iter.advance();
Ok(())
}
/// Skips ahead to the next level 0 object.
///
/// The main use case for this function is to skip mipmaps between cube map
/// faces and elements of a texture array.
///
/// Volume textures are not allowed to call this function within a volume.
/// It's only valid to call this function at the start or end of a volume.
/// Because of this, it can only be used to skip to the end of the file for
/// volumes.
///
/// Notes:
///
/// - If the DDS file does not contain any mipmaps, this is a no-op.
/// - Calling this at the start or end of a DDS file is a no-op.
pub fn skip_mipmaps(&mut self) -> Result<(), DecodingError>
where
R: Seek,
{
if let Ok(skip) = self.iter.skip_mipmaps() {
util::io_skip_exact(&mut self.reader, skip)?;
Ok(())
} else {
Err(DecodingError::CannotSkipMipmapsInVolume)
}
}
/// Reads all faces of a cube map into the given image and skips any
/// mipmaps of those faces.
///
/// The faces of the cube map are arranged in a shape of an unfolded cube
/// like this:
///
/// ```txt
/// +----+----+----+----+ `.
/// | | +Y | | | |
/// +----+----+----+----+ |
/// | -X | +Z | +X | -Z | | 3 * height
/// +----+----+----+----+ |
/// | | -Y | | | |
/// +----+----+----+----+ ´
/// .___________________.
/// 4 * width
/// ```
///
/// As such, the output image buffer is expected to have a size of
/// `width * 4` by `height * 3`, where `width` and `height` are the
/// dimensions of a single cube map face. (See [`Self::main_size`] for the
/// size of a single face.)
///
/// For partial cube maps, only the faces that are present in the DDS file
/// are read. The faces are arranged in the same order as for full cube
/// maps.
///
/// It's recommended to use `self.layout().is_cube_map()` to determine
/// whether the DDS file is a cube map or not.
pub fn read_cube_map(&mut self, mut image: ImageViewMut) -> Result<(), DecodingError>
where
R: Read + Seek,
{
let layout = self.layout();
let texture_array = layout.texture_array().ok_or(DecodingError::NotACubeMap)?;
let faces = match texture_array.kind() {
TextureArrayKind::Textures => return Err(DecodingError::NotACubeMap),
TextureArrayKind::CubeMaps => CubeMapFaces::ALL,
TextureArrayKind::PartialCubeMap(cube_map_faces) => cube_map_faces,
};
let face_size = texture_array.size();
let image_width = face_size.width.checked_mul(4);
let image_height = face_size.height.checked_mul(3);
if image_width != Some(image.width()) || image_height != Some(image.height()) {
return Err(DecodingError::UnexpectedSurfaceSize);
}
let face_offsets = [
(CubeMapFaces::POSITIVE_X, 2, 1),
(CubeMapFaces::NEGATIVE_X, 0, 1),
(CubeMapFaces::POSITIVE_Y, 1, 0),
(CubeMapFaces::NEGATIVE_Y, 1, 2),
(CubeMapFaces::POSITIVE_Z, 1, 1),
(CubeMapFaces::NEGATIVE_Z, 3, 1),
];
let color = image.color();
let row_pitch = image.row_pitch();
for (_, x, y) in face_offsets
.into_iter()
.filter(|(face, _, _)| faces.contains(*face))
{
let current = self.iter.current().ok_or(DecodingError::NoMoreSurfaces)?;
if current.size() != face_size {
return Err(DecodingError::UnexpectedSurfaceSize);
}
let offset = Offset::new(x * face_size.width, y * face_size.height);
let crop = ImageViewMut::new_with(
image.cropped_data(offset, face_size),
row_pitch,
face_size,
color,
)
.expect("Failed to create cropped image view");
self.read_surface(crop)?;
self.skip_mipmaps()?;
}
Ok(())
}
/// Moves to the reader back the previous surface, allowing it to be read
/// again.
///
/// If there is no previous surface, this function will do nothing.
///
/// Note that this operation does **not** bring the decoder into a known
/// (working) state after an error occurred.
pub fn rewind_to_previous_surface(&mut self) -> Result<(), DecodingError>
where
R: Seek,
{
let current_bytes = self.iter.elapsed_bytes();
self.iter.rewind();
let previous_bytes = self.iter.elapsed_bytes();
debug_assert!(previous_bytes <= current_bytes);
let seek = -i64::try_from(current_bytes - previous_bytes)
.expect("Cannot seek back more than i64::MAX bytes");
self.reader.seek(std::io::SeekFrom::Current(seek))?;
Ok(())
}
/// Moves to the reader to the start of the data section of the DDS file,
/// making it possible to read the DDS file again.
///
/// Note that this operation does **not** bring the decoder into a known
/// (working) state after an error occurred.
pub fn rewind_to_start(&mut self) -> Result<(), DecodingError>
where
R: Seek,
{
let seek = -i64::try_from(self.iter.elapsed_bytes())
.expect("Cannot seek back more than i64::MAX bytes");
self.reader.seek(std::io::SeekFrom::Current(seek))?;
self.iter = SurfaceIterator::new(self.layout);
Ok(())
}
/// Returns information about the surface about to be read.
///
/// The returned value is not valid after calling `next_surface`.
///
/// If there are no more surfaces, `None` is returned.
///
/// Use [`Self::is_done`] instead of checking for `None` to determine if the
/// encoder is done reading.
pub fn surface_info(&self) -> Option<SurfaceInfo<'_>> {
self.iter.current()
}
/// Returns whether all surfaces that have been read.
///
/// If `true` is returned, then attempting to read more surfaces will
/// always result in an error.
///
/// See [`Self::read_surface`] for more information about reading surfaces,
/// and [`Self::surface_info`] for more information about the next
/// surface.
pub fn is_done(&self) -> bool {
self.iter.current().is_none()
}
/// Returns the underlying reader.
pub fn into_reader(self) -> R {
self.reader
}
}