1use std::io::{self, Read};
11
12use crate::color::ColorType;
13use crate::error::{ImageError, ImageResult, ParameterError, ParameterErrorKind};
14use crate::image::ImageDecoder;
15
16#[derive(Clone, Copy, Debug, PartialEq, Eq)]
20pub(crate) enum DxtVariant {
21 DXT1,
24 DXT3,
27 DXT5,
30}
31
32impl DxtVariant {
33 fn decoded_bytes_per_block(self) -> usize {
36 match self {
37 DxtVariant::DXT1 => 48,
38 DxtVariant::DXT3 | DxtVariant::DXT5 => 64,
39 }
40 }
41
42 fn encoded_bytes_per_block(self) -> usize {
44 match self {
45 DxtVariant::DXT1 => 8,
46 DxtVariant::DXT3 | DxtVariant::DXT5 => 16,
47 }
48 }
49
50 pub(crate) fn color_type(self) -> ColorType {
52 match self {
53 DxtVariant::DXT1 => ColorType::Rgb8,
54 DxtVariant::DXT3 | DxtVariant::DXT5 => ColorType::Rgba8,
55 }
56 }
57}
58
59pub(crate) struct DxtDecoder<R: Read> {
61 inner: R,
62 width_blocks: u32,
63 height_blocks: u32,
64 variant: DxtVariant,
65 row: u32,
66}
67
68impl<R: Read> DxtDecoder<R> {
69 pub(crate) fn new(
77 r: R,
78 width: u32,
79 height: u32,
80 variant: DxtVariant,
81 ) -> Result<DxtDecoder<R>, ImageError> {
82 if width % 4 != 0 || height % 4 != 0 {
83 return Err(ImageError::Parameter(ParameterError::from_kind(
87 ParameterErrorKind::DimensionMismatch,
88 )));
89 }
90 let width_blocks = width / 4;
91 let height_blocks = height / 4;
92 Ok(DxtDecoder {
93 inner: r,
94 width_blocks,
95 height_blocks,
96 variant,
97 row: 0,
98 })
99 }
100
101 fn scanline_bytes(&self) -> u64 {
102 self.variant.decoded_bytes_per_block() as u64 * u64::from(self.width_blocks)
103 }
104
105 fn read_scanline(&mut self, buf: &mut [u8]) -> io::Result<usize> {
106 assert_eq!(
107 u64::try_from(buf.len()),
108 Ok(
109 #[allow(deprecated)]
110 self.scanline_bytes()
111 )
112 );
113
114 let mut src =
115 vec![0u8; self.variant.encoded_bytes_per_block() * self.width_blocks as usize];
116 self.inner.read_exact(&mut src)?;
117 match self.variant {
118 DxtVariant::DXT1 => decode_dxt1_row(&src, buf),
119 DxtVariant::DXT3 => decode_dxt3_row(&src, buf),
120 DxtVariant::DXT5 => decode_dxt5_row(&src, buf),
121 }
122 self.row += 1;
123 Ok(buf.len())
124 }
125}
126
127impl<R: Read> ImageDecoder for DxtDecoder<R> {
130 fn dimensions(&self) -> (u32, u32) {
131 (self.width_blocks * 4, self.height_blocks * 4)
132 }
133
134 fn color_type(&self) -> ColorType {
135 self.variant.color_type()
136 }
137
138 fn read_image(mut self, buf: &mut [u8]) -> ImageResult<()> {
139 assert_eq!(u64::try_from(buf.len()), Ok(self.total_bytes()));
140
141 #[allow(deprecated)]
142 for chunk in buf.chunks_mut(self.scanline_bytes().max(1) as usize) {
143 self.read_scanline(chunk)?;
144 }
145 Ok(())
146 }
147
148 fn read_image_boxed(self: Box<Self>, buf: &mut [u8]) -> ImageResult<()> {
149 (*self).read_image(buf)
150 }
151}
152
153type Rgb = [u8; 3];
157
158fn enc565_decode(value: u16) -> Rgb {
162 let red = (value >> 11) & 0x1F;
163 let green = (value >> 5) & 0x3F;
164 let blue = (value) & 0x1F;
165 [
166 (red * 0xFF / 0x1F) as u8,
167 (green * 0xFF / 0x3F) as u8,
168 (blue * 0xFF / 0x1F) as u8,
169 ]
170}
171
172fn alpha_table_dxt5(alpha0: u8, alpha1: u8) -> [u8; 8] {
180 let mut table = [alpha0, alpha1, 0, 0, 0, 0, 0, 0xFF];
181 if alpha0 > alpha1 {
182 for i in 2..8u16 {
183 table[i as usize] =
184 (((8 - i) * u16::from(alpha0) + (i - 1) * u16::from(alpha1)) / 7) as u8;
185 }
186 } else {
187 for i in 2..6u16 {
188 table[i as usize] =
189 (((6 - i) * u16::from(alpha0) + (i - 1) * u16::from(alpha1)) / 5) as u8;
190 }
191 }
192 table
193}
194
195fn decode_dxt_colors(source: &[u8], dest: &mut [u8], is_dxt1: bool) {
198 assert!(source.len() == 8 && (dest.len() == 48 || dest.len() == 64));
200 let pitch = dest.len() / 16;
202
203 let color0 = u16::from(source[0]) | (u16::from(source[1]) << 8);
205 let color1 = u16::from(source[2]) | (u16::from(source[3]) << 8);
206 let color_table = u32::from(source[4])
207 | (u32::from(source[5]) << 8)
208 | (u32::from(source[6]) << 16)
209 | (u32::from(source[7]) << 24);
210 let mut colors = [[0; 3]; 4];
214 colors[0] = enc565_decode(color0);
215 colors[1] = enc565_decode(color1);
216
217 if color0 > color1 || !is_dxt1 {
219 for i in 0..3 {
221 colors[2][i] = ((u16::from(colors[0][i]) * 2 + u16::from(colors[1][i]) + 1) / 3) as u8;
222 colors[3][i] = ((u16::from(colors[0][i]) + u16::from(colors[1][i]) * 2 + 1) / 3) as u8;
223 }
224 } else {
225 for i in 0..3 {
227 colors[2][i] = ((u16::from(colors[0][i]) + u16::from(colors[1][i]) + 1) / 2) as u8;
228 }
229 }
230
231 for i in 0..16 {
234 dest[i * pitch..i * pitch + 3]
235 .copy_from_slice(&colors[(color_table >> (i * 2)) as usize & 3]);
236 }
237}
238
239fn decode_dxt5_block(source: &[u8], dest: &mut [u8]) {
241 assert!(source.len() == 16 && dest.len() == 64);
242
243 let alpha_table = source[2..8]
245 .iter()
246 .rev()
247 .fold(0, |t, &b| (t << 8) | u64::from(b));
248
249 let alphas = alpha_table_dxt5(source[0], source[1]);
251
252 for i in 0..16 {
254 dest[i * 4 + 3] = alphas[(alpha_table >> (i * 3)) as usize & 7];
255 }
256
257 decode_dxt_colors(&source[8..16], dest, false);
259}
260
261fn decode_dxt3_block(source: &[u8], dest: &mut [u8]) {
263 assert!(source.len() == 16 && dest.len() == 64);
264
265 let alpha_table = source[0..8]
267 .iter()
268 .rev()
269 .fold(0, |t, &b| (t << 8) | u64::from(b));
270
271 for i in 0..16 {
273 dest[i * 4 + 3] = ((alpha_table >> (i * 4)) as u8 & 0xF) * 0x11;
274 }
275
276 decode_dxt_colors(&source[8..16], dest, false);
278}
279
280fn decode_dxt1_block(source: &[u8], dest: &mut [u8]) {
282 assert!(source.len() == 8 && dest.len() == 48);
283 decode_dxt_colors(source, dest, true);
284}
285
286fn decode_dxt1_row(source: &[u8], dest: &mut [u8]) {
289 assert!(source.len() % 8 == 0);
290 let block_count = source.len() / 8;
291 assert!(dest.len() >= block_count * 48);
292
293 let mut decoded_block = [0u8; 48];
295
296 for (x, encoded_block) in source.chunks(8).enumerate() {
297 decode_dxt1_block(encoded_block, &mut decoded_block);
298
299 for line in 0..4 {
301 let offset = (block_count * line + x) * 12;
302 dest[offset..offset + 12].copy_from_slice(&decoded_block[line * 12..(line + 1) * 12]);
303 }
304 }
305}
306
307fn decode_dxt3_row(source: &[u8], dest: &mut [u8]) {
310 assert!(source.len() % 16 == 0);
311 let block_count = source.len() / 16;
312 assert!(dest.len() >= block_count * 64);
313
314 let mut decoded_block = [0u8; 64];
316
317 for (x, encoded_block) in source.chunks(16).enumerate() {
318 decode_dxt3_block(encoded_block, &mut decoded_block);
319
320 for line in 0..4 {
322 let offset = (block_count * line + x) * 16;
323 dest[offset..offset + 16].copy_from_slice(&decoded_block[line * 16..(line + 1) * 16]);
324 }
325 }
326}
327
328fn decode_dxt5_row(source: &[u8], dest: &mut [u8]) {
331 assert!(source.len() % 16 == 0);
332 let block_count = source.len() / 16;
333 assert!(dest.len() >= block_count * 64);
334
335 let mut decoded_block = [0u8; 64];
337
338 for (x, encoded_block) in source.chunks(16).enumerate() {
339 decode_dxt5_block(encoded_block, &mut decoded_block);
340
341 for line in 0..4 {
343 let offset = (block_count * line + x) * 16;
344 dest[offset..offset + 16].copy_from_slice(&decoded_block[line * 16..(line + 1) * 16]);
345 }
346 }
347}