1use std::io::{Read, Seek};
12
13use dds::header::ParseOptions;
14use dds::{
15 Channels, ColorFormat, DataLayout, Decoder, Format, ImageViewMut, Offset, Size,
16 TextureArrayKind,
17};
18
19use image::error::{
20 DecodingError, ImageError, ImageFormatHint, ImageResult, LimitError, LimitErrorKind,
21 UnsupportedError, UnsupportedErrorKind,
22};
23use image::{ColorType, ExtendedColorType, ImageDecoder, ImageDecoderRect};
24
25pub struct DdsDecoder<R> {
33 inner: Decoder<R>,
34 is_cubemap: bool,
35 size: Size,
36 color: SupportedColor,
37}
38
39impl<R: Read> DdsDecoder<R> {
40 pub fn new(r: R) -> ImageResult<Self> {
42 let options = ParseOptions::new_permissive(None);
43 let decoder = Decoder::new_with_options(r, &options).map_err(to_image_error)?;
44 let layout = decoder.layout();
45
46 match &layout {
50 DataLayout::Volume(_) => {
51 return Err(ImageError::Decoding(DecodingError::new(
52 format_hint(),
53 "DDS volume textures are not supported for decoding",
54 )))
55 }
56 DataLayout::TextureArray(texture_array) => {
57 let supported_length = match texture_array.kind() {
58 TextureArrayKind::Textures => 1,
59 TextureArrayKind::CubeMaps => 6,
60 TextureArrayKind::PartialCubeMap(cube_map_faces) => cube_map_faces.count(),
61 };
62 if texture_array.len() != supported_length as usize {
63 return Err(ImageError::Decoding(DecodingError::new(
64 format_hint(),
65 "DDS texture arrays are not supported for decoding",
66 )));
67 }
68 }
69 DataLayout::Texture(_) => {}
70 }
71
72 let mut size = decoder.main_size();
73 let mut color = decoder.native_color();
74 let is_cubemap = layout.is_cube_map();
75
76 if is_cubemap {
78 if let (Some(width), Some(height)) =
79 (size.width.checked_mul(4), size.height.checked_mul(3))
80 {
81 size.width = width;
82 size.height = height;
83 color.channels = Channels::Rgba;
84 } else {
85 return Err(ImageError::Decoding(DecodingError::new(
86 format_hint(),
87 "DDS cube map faces are too large to decode",
88 )));
89 }
90 }
91
92 Ok(DdsDecoder {
93 inner: decoder,
94 is_cubemap,
95 size,
96 color: SupportedColor::from_dds_widen(color),
97 })
98 }
99
100 pub fn set_color_type(&mut self, color: ColorType) -> ImageResult<()> {
119 let Some(supported_color) = SupportedColor::from_image_exact(color) else {
120 return Err(ImageError::Unsupported(
121 UnsupportedError::from_format_and_kind(
122 format_hint(),
123 UnsupportedErrorKind::Color(color.into()),
124 ),
125 ));
126 };
127 self.color = supported_color;
128 Ok(())
129 }
130}
131
132impl<R: Read + Seek> ImageDecoder for DdsDecoder<R> {
133 fn dimensions(&self) -> (u32, u32) {
134 (self.size.width, self.size.height)
135 }
136
137 fn color_type(&self) -> ColorType {
138 self.color.image
139 }
140
141 fn original_color_type(&self) -> ExtendedColorType {
142 match self.inner.format() {
143 Format::R1_UNORM => ExtendedColorType::L1,
144 Format::B4G4R4A4_UNORM | Format::A4B4G4R4_UNORM => ExtendedColorType::Rgba4,
145 Format::A8_UNORM => ExtendedColorType::A8,
146 _ => SupportedColor::from_dds_widen(self.inner.native_color())
147 .image
148 .into(),
149 }
150 }
151
152 fn set_limits(&mut self, limits: image::Limits) -> ImageResult<()> {
153 limits.check_dimensions(self.size.width, self.size.height)?;
154
155 if let Some(max_alloc) = limits.max_alloc {
156 self.inner.options.memory_limit = max_alloc.try_into().unwrap_or(usize::MAX);
157 }
158
159 Ok(())
160 }
161
162 #[track_caller]
163 fn read_image(mut self, buf: &mut [u8]) -> ImageResult<()> {
164 let color = self.color.dds;
165 let size = self.size;
166
167 assert_eq!(
168 buf.len(),
169 color.buffer_size(size).unwrap(),
170 "Buffer len does not match for {:?} and {:?}",
171 size,
172 color
173 );
174
175 let image = ImageViewMut::new(buf, size, color).expect("Invalid buffer length");
176
177 if self.is_cubemap {
178 self.inner.read_cube_map(image).map_err(to_image_error)?;
179 } else {
180 self.inner.read_surface(image).map_err(to_image_error)?;
181 }
182
183 Ok(())
184 }
185
186 fn read_image_boxed(self: Box<Self>, buf: &mut [u8]) -> ImageResult<()> {
187 (*self).read_image(buf)
188 }
189}
190
191impl<R: Read + Seek> ImageDecoderRect for DdsDecoder<R> {
192 fn read_rect(
193 &mut self,
194 x: u32,
195 y: u32,
196 width: u32,
197 height: u32,
198 buf: &mut [u8],
199 row_pitch: usize,
200 ) -> ImageResult<()> {
201 if self.is_cubemap {
203 return Err(ImageError::Decoding(DecodingError::new(
204 format_hint(),
205 "read_rect is not supported for DDS cubemaps",
206 )));
207 }
208
209 let Some(view) =
210 ImageViewMut::new_with(buf, row_pitch, Size::new(width, height), self.color.dds)
211 else {
212 return Err(ImageError::Decoding(DecodingError::new(
213 format_hint(),
214 "Invalid buffer length for reading rect",
215 )));
216 };
217
218 self.inner
219 .read_surface_rect(view, Offset::new(x, y))
220 .map_err(to_image_error)?;
221 self.inner
222 .rewind_to_previous_surface()
223 .map_err(to_image_error)?;
224
225 Ok(())
226 }
227}
228
229struct SupportedColor {
231 image: ColorType,
232 dds: ColorFormat,
233}
234impl SupportedColor {
235 fn new(image: ColorType, dds: ColorFormat) -> SupportedColor {
236 debug_assert_eq!(image.bytes_per_pixel(), dds.bytes_per_pixel());
237 debug_assert_eq!(image.channel_count(), dds.channels.count());
238
239 SupportedColor { image, dds }
240 }
241
242 fn from_image_exact(color: ColorType) -> Option<Self> {
245 fn to_color_format_exact(color: ColorType) -> Option<ColorFormat> {
246 match color {
247 ColorType::L8 => Some(ColorFormat::GRAYSCALE_U8),
248 ColorType::Rgb8 => Some(ColorFormat::RGB_U8),
249 ColorType::Rgba8 => Some(ColorFormat::RGBA_U8),
250 ColorType::L16 => Some(ColorFormat::GRAYSCALE_U16),
251 ColorType::Rgb16 => Some(ColorFormat::RGB_U16),
252 ColorType::Rgba16 => Some(ColorFormat::RGBA_U16),
253 ColorType::Rgb32F => Some(ColorFormat::RGB_F32),
254 ColorType::Rgba32F => Some(ColorFormat::RGBA_F32),
255 _ => None,
256 }
257 }
258
259 to_color_format_exact(color).map(|dds| Self::new(color, dds))
260 }
261 fn from_dds_widen(color: ColorFormat) -> Self {
264 match color {
265 ColorFormat::RGB_U8 => Self::new(ColorType::Rgb8, color),
267 ColorFormat::RGB_U16 => Self::new(ColorType::Rgb16, color),
268 ColorFormat::RGB_F32 => Self::new(ColorType::Rgb32F, color),
269 ColorFormat::GRAYSCALE_U8 => Self::new(ColorType::L8, color),
270 ColorFormat::GRAYSCALE_U16 => Self::new(ColorType::L16, color),
271 ColorFormat::RGBA_U8 => Self::new(ColorType::Rgba8, color),
272 ColorFormat::RGBA_U16 => Self::new(ColorType::Rgba16, color),
273 ColorFormat::RGBA_F32 => Self::new(ColorType::Rgba32F, color),
274 ColorFormat::ALPHA_U8 => Self::new(ColorType::Rgba8, ColorFormat::RGBA_U8),
276 ColorFormat::ALPHA_U16 => Self::new(ColorType::Rgba16, ColorFormat::RGBA_U16),
277 ColorFormat::ALPHA_F32 => Self::new(ColorType::Rgba32F, ColorFormat::RGBA_F32),
278 ColorFormat::GRAYSCALE_F32 => Self::new(ColorType::Rgb32F, ColorFormat::RGB_F32),
279 }
280 }
281}
282
283fn to_image_error(e: dds::DecodingError) -> ImageError {
284 match e {
285 dds::DecodingError::Io(e) => ImageError::IoError(e),
286 dds::DecodingError::MemoryLimitExceeded => {
287 ImageError::Limits(LimitError::from_kind(LimitErrorKind::InsufficientMemory))
288 }
289 _ => ImageError::Decoding(DecodingError::new(format_hint(), e)),
290 }
291}
292
293fn format_hint() -> ImageFormatHint {
294 ImageFormatHint::Name("DDS".into())
295}