1//! Decoding of DDS images
2//!
3//! DDS (DirectDraw Surface) is a container format for storing DXT (S3TC) compressed images.
4//!
5//! # Related Links
6//! * <https://docs.microsoft.com/en-us/windows/win32/direct3ddds/dx-graphics-dds-pguide> - Description of the DDS format.
7
8use std::io::Read;
9use std::{error, fmt};
10
11use byteorder::{LittleEndian, ReadBytesExt};
12
13#[allow(deprecated)]
14use crate::codecs::dxt::{DxtDecoder, DxtReader, DxtVariant};
15use crate::color::ColorType;
16use crate::error::{
17 DecodingError, ImageError, ImageFormatHint, ImageResult, UnsupportedError, UnsupportedErrorKind,
18};
19use crate::image::{ImageDecoder, ImageFormat};
20
21/// Errors that can occur during decoding and parsing a DDS image
22#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
23enum DecoderError {
24 /// Wrong DDS channel width
25 PixelFormatSizeInvalid(u32),
26 /// Wrong DDS header size
27 HeaderSizeInvalid(u32),
28 /// Wrong DDS header flags
29 HeaderFlagsInvalid(u32),
30
31 /// Invalid DXGI format in DX10 header
32 DxgiFormatInvalid(u32),
33 /// Invalid resource dimension
34 ResourceDimensionInvalid(u32),
35 /// Invalid flags in DX10 header
36 Dx10FlagsInvalid(u32),
37 /// Invalid array size in DX10 header
38 Dx10ArraySizeInvalid(u32),
39
40 /// DDS "DDS " signature invalid or missing
41 DdsSignatureInvalid,
42}
43
44impl fmt::Display for DecoderError {
45 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
46 match self {
47 DecoderError::PixelFormatSizeInvalid(s) => {
48 f.write_fmt(format_args!("Invalid DDS PixelFormat size: {}", s))
49 }
50 DecoderError::HeaderSizeInvalid(s) => {
51 f.write_fmt(format_args!("Invalid DDS header size: {}", s))
52 }
53 DecoderError::HeaderFlagsInvalid(fs) => {
54 f.write_fmt(format_args!("Invalid DDS header flags: {:#010X}", fs))
55 }
56 DecoderError::DxgiFormatInvalid(df) => {
57 f.write_fmt(format_args!("Invalid DDS DXGI format: {}", df))
58 }
59 DecoderError::ResourceDimensionInvalid(d) => {
60 f.write_fmt(format_args!("Invalid DDS resource dimension: {}", d))
61 }
62 DecoderError::Dx10FlagsInvalid(fs) => {
63 f.write_fmt(format_args!("Invalid DDS DX10 header flags: {:#010X}", fs))
64 }
65 DecoderError::Dx10ArraySizeInvalid(s) => {
66 f.write_fmt(format_args!("Invalid DDS DX10 array size: {}", s))
67 }
68 DecoderError::DdsSignatureInvalid => f.write_str("DDS signature not found"),
69 }
70 }
71}
72
73impl From<DecoderError> for ImageError {
74 fn from(e: DecoderError) -> ImageError {
75 ImageError::Decoding(DecodingError::new(format:ImageFormat::Dds.into(), err:e))
76 }
77}
78
79impl error::Error for DecoderError {}
80
81/// Header used by DDS image files
82#[derive(Debug)]
83struct Header {
84 _flags: u32,
85 height: u32,
86 width: u32,
87 _pitch_or_linear_size: u32,
88 _depth: u32,
89 _mipmap_count: u32,
90 pixel_format: PixelFormat,
91 _caps: u32,
92 _caps2: u32,
93}
94
95/// Extended DX10 header used by some DDS image files
96#[derive(Debug)]
97struct DX10Header {
98 dxgi_format: u32,
99 resource_dimension: u32,
100 misc_flag: u32,
101 array_size: u32,
102 misc_flags_2: u32,
103}
104
105/// DDS pixel format
106#[derive(Debug)]
107struct PixelFormat {
108 flags: u32,
109 fourcc: [u8; 4],
110 _rgb_bit_count: u32,
111 _r_bit_mask: u32,
112 _g_bit_mask: u32,
113 _b_bit_mask: u32,
114 _a_bit_mask: u32,
115}
116
117impl PixelFormat {
118 fn from_reader(r: &mut dyn Read) -> ImageResult<Self> {
119 let size: u32 = r.read_u32::<LittleEndian>()?;
120 if size != 32 {
121 return Err(DecoderError::PixelFormatSizeInvalid(size).into());
122 }
123
124 Ok(Self {
125 flags: r.read_u32::<LittleEndian>()?,
126 fourcc: {
127 let mut v: [u8; 4] = [0; 4];
128 r.read_exact(&mut v)?;
129 v
130 },
131 _rgb_bit_count: r.read_u32::<LittleEndian>()?,
132 _r_bit_mask: r.read_u32::<LittleEndian>()?,
133 _g_bit_mask: r.read_u32::<LittleEndian>()?,
134 _b_bit_mask: r.read_u32::<LittleEndian>()?,
135 _a_bit_mask: r.read_u32::<LittleEndian>()?,
136 })
137 }
138}
139
140impl Header {
141 fn from_reader(r: &mut dyn Read) -> ImageResult<Self> {
142 let size = r.read_u32::<LittleEndian>()?;
143 if size != 124 {
144 return Err(DecoderError::HeaderSizeInvalid(size).into());
145 }
146
147 const REQUIRED_FLAGS: u32 = 0x1 | 0x2 | 0x4 | 0x1000;
148 const VALID_FLAGS: u32 = 0x1 | 0x2 | 0x4 | 0x8 | 0x1000 | 0x20000 | 0x80000 | 0x800000;
149 let flags = r.read_u32::<LittleEndian>()?;
150 if flags & (REQUIRED_FLAGS | !VALID_FLAGS) != REQUIRED_FLAGS {
151 return Err(DecoderError::HeaderFlagsInvalid(flags).into());
152 }
153
154 let height = r.read_u32::<LittleEndian>()?;
155 let width = r.read_u32::<LittleEndian>()?;
156 let pitch_or_linear_size = r.read_u32::<LittleEndian>()?;
157 let depth = r.read_u32::<LittleEndian>()?;
158 let mipmap_count = r.read_u32::<LittleEndian>()?;
159 // Skip `dwReserved1`
160 {
161 let mut skipped = [0; 4 * 11];
162 r.read_exact(&mut skipped)?;
163 }
164 let pixel_format = PixelFormat::from_reader(r)?;
165 let caps = r.read_u32::<LittleEndian>()?;
166 let caps2 = r.read_u32::<LittleEndian>()?;
167 // Skip `dwCaps3`, `dwCaps4`, `dwReserved2` (unused)
168 {
169 let mut skipped = [0; 4 + 4 + 4];
170 r.read_exact(&mut skipped)?;
171 }
172
173 Ok(Self {
174 _flags: flags,
175 height,
176 width,
177 _pitch_or_linear_size: pitch_or_linear_size,
178 _depth: depth,
179 _mipmap_count: mipmap_count,
180 pixel_format,
181 _caps: caps,
182 _caps2: caps2,
183 })
184 }
185}
186
187impl DX10Header {
188 fn from_reader(r: &mut dyn Read) -> ImageResult<Self> {
189 let dxgi_format = r.read_u32::<LittleEndian>()?;
190 let resource_dimension = r.read_u32::<LittleEndian>()?;
191 let misc_flag = r.read_u32::<LittleEndian>()?;
192 let array_size = r.read_u32::<LittleEndian>()?;
193 let misc_flags_2 = r.read_u32::<LittleEndian>()?;
194
195 let dx10_header = Self {
196 dxgi_format,
197 resource_dimension,
198 misc_flag,
199 array_size,
200 misc_flags_2,
201 };
202 dx10_header.validate()?;
203
204 Ok(dx10_header)
205 }
206
207 fn validate(&self) -> Result<(), ImageError> {
208 // Note: see https://docs.microsoft.com/en-us/windows/win32/direct3ddds/dds-header-dxt10 for info on valid values
209 if self.dxgi_format > 132 {
210 // Invalid format
211 return Err(DecoderError::DxgiFormatInvalid(self.dxgi_format).into());
212 }
213
214 if self.resource_dimension < 2 || self.resource_dimension > 4 {
215 // Invalid dimension
216 // Only 1D (2), 2D (3) and 3D (4) resource dimensions are allowed
217 return Err(DecoderError::ResourceDimensionInvalid(self.resource_dimension).into());
218 }
219
220 if self.misc_flag != 0x0 && self.misc_flag != 0x4 {
221 // Invalid flag
222 // Only no (0x0) and DDS_RESOURCE_MISC_TEXTURECUBE (0x4) flags are allowed
223 return Err(DecoderError::Dx10FlagsInvalid(self.misc_flag).into());
224 }
225
226 if self.resource_dimension == 4 && self.array_size != 1 {
227 // Invalid array size
228 // 3D textures (resource dimension == 4) must have an array size of 1
229 return Err(DecoderError::Dx10ArraySizeInvalid(self.array_size).into());
230 }
231
232 if self.misc_flags_2 > 0x4 {
233 // Invalid alpha flags
234 return Err(DecoderError::Dx10FlagsInvalid(self.misc_flags_2).into());
235 }
236
237 Ok(())
238 }
239}
240
241/// The representation of a DDS decoder
242pub struct DdsDecoder<R: Read> {
243 #[allow(deprecated)]
244 inner: DxtDecoder<R>,
245}
246
247impl<R: Read> DdsDecoder<R> {
248 /// Create a new decoder that decodes from the stream `r`
249 pub fn new(mut r: R) -> ImageResult<Self> {
250 let mut magic = [0; 4];
251 r.read_exact(&mut magic)?;
252 if magic != b"DDS "[..] {
253 return Err(DecoderError::DdsSignatureInvalid.into());
254 }
255
256 let header = Header::from_reader(&mut r)?;
257
258 if header.pixel_format.flags & 0x4 != 0 {
259 #[allow(deprecated)]
260 let variant = match &header.pixel_format.fourcc {
261 b"DXT1" => DxtVariant::DXT1,
262 b"DXT3" => DxtVariant::DXT3,
263 b"DXT5" => DxtVariant::DXT5,
264 b"DX10" => {
265 let dx10_header = DX10Header::from_reader(&mut r)?;
266 // Format equivalents were taken from https://docs.microsoft.com/en-us/windows/win32/direct3d11/texture-block-compression-in-direct3d-11
267 // The enum integer values were taken from https://docs.microsoft.com/en-us/windows/win32/api/dxgiformat/ne-dxgiformat-dxgi_format
268 // DXT1 represents the different BC1 variants, DTX3 represents the different BC2 variants and DTX5 represents the different BC3 variants
269 match dx10_header.dxgi_format {
270 70..=72 => DxtVariant::DXT1, // DXGI_FORMAT_BC1_TYPELESS, DXGI_FORMAT_BC1_UNORM or DXGI_FORMAT_BC1_UNORM_SRGB
271 73..=75 => DxtVariant::DXT3, // DXGI_FORMAT_BC2_TYPELESS, DXGI_FORMAT_BC2_UNORM or DXGI_FORMAT_BC2_UNORM_SRGB
272 76..=78 => DxtVariant::DXT5, // DXGI_FORMAT_BC3_TYPELESS, DXGI_FORMAT_BC3_UNORM or DXGI_FORMAT_BC3_UNORM_SRGB
273 _ => {
274 return Err(ImageError::Unsupported(
275 UnsupportedError::from_format_and_kind(
276 ImageFormat::Dds.into(),
277 UnsupportedErrorKind::GenericFeature(format!(
278 "DDS DXGI Format {}",
279 dx10_header.dxgi_format
280 )),
281 ),
282 ))
283 }
284 }
285 }
286 fourcc => {
287 return Err(ImageError::Unsupported(
288 UnsupportedError::from_format_and_kind(
289 ImageFormat::Dds.into(),
290 UnsupportedErrorKind::GenericFeature(format!(
291 "DDS FourCC {:?}",
292 fourcc
293 )),
294 ),
295 ))
296 }
297 };
298
299 #[allow(deprecated)]
300 let bytes_per_pixel = variant.color_type().bytes_per_pixel();
301
302 if crate::utils::check_dimension_overflow(header.width, header.height, bytes_per_pixel)
303 {
304 return Err(ImageError::Unsupported(
305 UnsupportedError::from_format_and_kind(
306 ImageFormat::Dds.into(),
307 UnsupportedErrorKind::GenericFeature(format!(
308 "Image dimensions ({}x{}) are too large",
309 header.width, header.height
310 )),
311 ),
312 ));
313 }
314
315 #[allow(deprecated)]
316 let inner = DxtDecoder::new(r, header.width, header.height, variant)?;
317 Ok(Self { inner })
318 } else {
319 // For now, supports only DXT variants
320 Err(ImageError::Unsupported(
321 UnsupportedError::from_format_and_kind(
322 ImageFormat::Dds.into(),
323 UnsupportedErrorKind::Format(ImageFormatHint::Name("DDS".to_string())),
324 ),
325 ))
326 }
327 }
328}
329
330impl<'a, R: 'a + Read> ImageDecoder<'a> for DdsDecoder<R> {
331 #[allow(deprecated)]
332 type Reader = DxtReader<R>;
333
334 fn dimensions(&self) -> (u32, u32) {
335 self.inner.dimensions()
336 }
337
338 fn color_type(&self) -> ColorType {
339 self.inner.color_type()
340 }
341
342 fn scanline_bytes(&self) -> u64 {
343 #[allow(deprecated)]
344 self.inner.scanline_bytes()
345 }
346
347 fn into_reader(self) -> ImageResult<Self::Reader> {
348 #[allow(deprecated)]
349 self.inner.into_reader()
350 }
351
352 fn read_image(self, buf: &mut [u8]) -> ImageResult<()> {
353 self.inner.read_image(buf)
354 }
355}
356
357#[cfg(test)]
358mod test {
359 use super::*;
360
361 #[test]
362 fn dimension_overflow() {
363 // A DXT1 header set to 0xFFFF_FFFC width and height (the highest u32%4 == 0)
364 let header = [
365 0x44, 0x44, 0x53, 0x20, 0x7C, 0x0, 0x0, 0x0, 0x7, 0x10, 0x8, 0x0, 0xFC, 0xFF, 0xFF,
366 0xFF, 0xFC, 0xFF, 0xFF, 0xFF, 0x0, 0xC0, 0x12, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0,
367 0x0, 0x49, 0x4D, 0x41, 0x47, 0x45, 0x4D, 0x41, 0x47, 0x49, 0x43, 0x4B, 0x0, 0x0, 0x0,
368 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
369 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0,
370 0x4, 0x0, 0x0, 0x0, 0x44, 0x58, 0x54, 0x31, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
371 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0,
372 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
373 ];
374
375 assert!(DdsDecoder::new(&header[..]).is_err());
376 }
377}
378