Skip to content

Commit e1e23f1

Browse files
committed
Support 16-bit encoded TGA images.
The decoder does the same thing as the BMP decoder, and converts 16-bit images to 32-bit ones on load.
1 parent e176cd4 commit e1e23f1

File tree

6 files changed

+233
-109
lines changed

6 files changed

+233
-109
lines changed

src/codecs/bmp/decoder.rs

Lines changed: 17 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -13,28 +13,16 @@ use crate::error::{
1313
use crate::image::{self, ImageDecoder, ImageFormat};
1414
use crate::ImageDecoderRect;
1515

16+
use crate::utils::packed_color::Bitfield;
17+
use crate::utils::packed_color::{BitfieldError, Bitfields};
18+
1619
const BITMAPCOREHEADER_SIZE: u32 = 12;
1720
const BITMAPINFOHEADER_SIZE: u32 = 40;
1821
const BITMAPV2HEADER_SIZE: u32 = 52;
1922
const BITMAPV3HEADER_SIZE: u32 = 56;
2023
const BITMAPV4HEADER_SIZE: u32 = 108;
2124
const BITMAPV5HEADER_SIZE: u32 = 124;
2225

23-
static LOOKUP_TABLE_3_BIT_TO_8_BIT: [u8; 8] = [0, 36, 73, 109, 146, 182, 219, 255];
24-
static LOOKUP_TABLE_4_BIT_TO_8_BIT: [u8; 16] = [
25-
0, 17, 34, 51, 68, 85, 102, 119, 136, 153, 170, 187, 204, 221, 238, 255,
26-
];
27-
static LOOKUP_TABLE_5_BIT_TO_8_BIT: [u8; 32] = [
28-
0, 8, 16, 25, 33, 41, 49, 58, 66, 74, 82, 90, 99, 107, 115, 123, 132, 140, 148, 156, 165, 173,
29-
181, 189, 197, 206, 214, 222, 230, 239, 247, 255,
30-
];
31-
static LOOKUP_TABLE_6_BIT_TO_8_BIT: [u8; 64] = [
32-
0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 45, 49, 53, 57, 61, 65, 69, 73, 77, 81, 85, 89, 93,
33-
97, 101, 105, 109, 113, 117, 121, 125, 130, 134, 138, 142, 146, 150, 154, 158, 162, 166, 170,
34-
174, 178, 182, 186, 190, 194, 198, 202, 206, 210, 215, 219, 223, 227, 231, 235, 239, 243, 247,
35-
251, 255,
36-
];
37-
3826
static R5_G5_B5_COLOR_MASK: Bitfields = Bitfields {
3927
r: Bitfield { len: 5, shift: 10 },
4028
g: Bitfield { len: 5, shift: 5 },
@@ -120,14 +108,8 @@ enum DecoderError {
120108
// Failed to decompress RLE data.
121109
CorruptRleData,
122110

123-
/// The bitfield mask interleaves set and unset bits
124-
BitfieldMaskNonContiguous,
125-
/// Bitfield mask invalid (e.g. too long for specified type)
126-
BitfieldMaskInvalid,
127-
/// Bitfield (of the specified width – 16- or 32-bit) mask not present
128-
BitfieldMaskMissing(u32),
129111
/// Bitfield (of the specified width – 16- or 32-bit) masks not present
130-
BitfieldMasksMissing(u32),
112+
BitfieldError(BitfieldError),
131113

132114
/// BMP's "BM" signature wrong or missing
133115
BmpSignatureInvalid,
@@ -164,13 +146,8 @@ impl fmt::Display for DecoderError {
164146
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
165147
match self {
166148
DecoderError::CorruptRleData => f.write_str("Corrupt RLE data"),
167-
DecoderError::BitfieldMaskNonContiguous => f.write_str("Non-contiguous bitfield mask"),
168-
DecoderError::BitfieldMaskInvalid => f.write_str("Invalid bitfield mask"),
169-
DecoderError::BitfieldMaskMissing(bb) => {
170-
f.write_fmt(format_args!("Missing {bb}-bit bitfield mask"))
171-
}
172-
DecoderError::BitfieldMasksMissing(bb) => {
173-
f.write_fmt(format_args!("Missing {bb}-bit bitfield masks"))
149+
DecoderError::BitfieldError(bb) => {
150+
f.write_fmt(format_args!("Bitfield error: {bb}"))
174151
}
175152
DecoderError::BmpSignatureInvalid => f.write_str("BMP signature not found"),
176153
DecoderError::MoreThanOnePlane => f.write_str("More than one plane"),
@@ -207,6 +184,15 @@ impl From<DecoderError> for ImageError {
207184
}
208185
}
209186

187+
impl From<BitfieldError> for ImageError {
188+
fn from(e: BitfieldError) -> ImageError {
189+
ImageError::Decoding(DecodingError::new(
190+
ImageFormat::Bmp.into(),
191+
DecoderError::BitfieldError(e),
192+
))
193+
}
194+
}
195+
210196
impl error::Error for DecoderError {}
211197

212198
/// Distinct image types whose saved channel width can be invalid
@@ -397,77 +383,6 @@ fn set_1bit_pixel_run<'a, T: Iterator<Item = &'a u8>>(
397383
}
398384
}
399385

400-
#[derive(PartialEq, Eq)]
401-
struct Bitfield {
402-
shift: u32,
403-
len: u32,
404-
}
405-
406-
impl Bitfield {
407-
fn from_mask(mask: u32, max_len: u32) -> ImageResult<Bitfield> {
408-
if mask == 0 {
409-
return Ok(Bitfield { shift: 0, len: 0 });
410-
}
411-
let mut shift = mask.trailing_zeros();
412-
let mut len = (!(mask >> shift)).trailing_zeros();
413-
if len != mask.count_ones() {
414-
return Err(DecoderError::BitfieldMaskNonContiguous.into());
415-
}
416-
if len + shift > max_len {
417-
return Err(DecoderError::BitfieldMaskInvalid.into());
418-
}
419-
if len > 8 {
420-
shift += len - 8;
421-
len = 8;
422-
}
423-
Ok(Bitfield { shift, len })
424-
}
425-
426-
fn read(&self, data: u32) -> u8 {
427-
let data = data >> self.shift;
428-
match self.len {
429-
1 => ((data & 0b1) * 0xff) as u8,
430-
2 => ((data & 0b11) * 0x55) as u8,
431-
3 => LOOKUP_TABLE_3_BIT_TO_8_BIT[(data & 0b00_0111) as usize],
432-
4 => LOOKUP_TABLE_4_BIT_TO_8_BIT[(data & 0b00_1111) as usize],
433-
5 => LOOKUP_TABLE_5_BIT_TO_8_BIT[(data & 0b01_1111) as usize],
434-
6 => LOOKUP_TABLE_6_BIT_TO_8_BIT[(data & 0b11_1111) as usize],
435-
7 => ((data & 0x7f) << 1 | (data & 0x7f) >> 6) as u8,
436-
8 => (data & 0xff) as u8,
437-
_ => panic!(),
438-
}
439-
}
440-
}
441-
442-
#[derive(PartialEq, Eq)]
443-
struct Bitfields {
444-
r: Bitfield,
445-
g: Bitfield,
446-
b: Bitfield,
447-
a: Bitfield,
448-
}
449-
450-
impl Bitfields {
451-
fn from_mask(
452-
r_mask: u32,
453-
g_mask: u32,
454-
b_mask: u32,
455-
a_mask: u32,
456-
max_len: u32,
457-
) -> ImageResult<Bitfields> {
458-
let bitfields = Bitfields {
459-
r: Bitfield::from_mask(r_mask, max_len)?,
460-
g: Bitfield::from_mask(g_mask, max_len)?,
461-
b: Bitfield::from_mask(b_mask, max_len)?,
462-
a: Bitfield::from_mask(a_mask, max_len)?,
463-
};
464-
if bitfields.r.len == 0 || bitfields.g.len == 0 || bitfields.b.len == 0 {
465-
return Err(DecoderError::BitfieldMaskMissing(max_len).into());
466-
}
467-
Ok(bitfields)
468-
}
469-
}
470-
471386
/// A bmp decoder
472387
pub struct BmpDecoder<R> {
473388
reader: R,
@@ -1308,7 +1223,7 @@ impl<R: BufRead + Seek> BmpDecoder<R> {
13081223
ImageType::RLE4 => self.read_rle_data(buf, ImageType::RLE4),
13091224
ImageType::Bitfields16 => match self.bitfields {
13101225
Some(_) => self.read_16_bit_pixel_data(buf, None),
1311-
None => Err(DecoderError::BitfieldMasksMissing(16).into()),
1226+
None => Err(BitfieldError::MasksMissing(16).into()),
13121227
},
13131228
ImageType::Bitfields32 => match self.bitfields {
13141229
Some(R8_G8_B8_COLOR_MASK) => {
@@ -1318,7 +1233,7 @@ impl<R: BufRead + Seek> BmpDecoder<R> {
13181233
self.read_full_byte_pixel_data(buf, &FormatFullBytes::RGBA32)
13191234
}
13201235
Some(_) => self.read_32_bit_pixel_data(buf),
1321-
None => Err(DecoderError::BitfieldMasksMissing(32).into()),
1236+
None => Err(BitfieldError::MasksMissing(32).into()),
13221237
},
13231238
}
13241239
}

src/codecs/tga/decoder.rs

Lines changed: 90 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,29 @@ use super::header::{Header, ImageType, ALPHA_BIT_MASK, SCREEN_ORIGIN_BIT_MASK};
22
use crate::{
33
color::{ColorType, ExtendedColorType},
44
error::{
5-
ImageError, ImageResult, LimitError, LimitErrorKind, UnsupportedError, UnsupportedErrorKind,
5+
ImageError, ImageFormatHint, ImageResult, LimitError, LimitErrorKind, UnsupportedError,
6+
UnsupportedErrorKind,
67
},
78
image::{ImageDecoder, ImageFormat},
9+
utils::packed_color::{Bitfield, Bitfields},
10+
};
11+
use byteorder_lite::{LittleEndian, ReadBytesExt};
12+
use num_traits::ToPrimitive;
13+
use std::io::{self, Cursor, Read};
14+
15+
static R5_G5_B5_COLOR_MASK: Bitfields = Bitfields {
16+
r: Bitfield { len: 5, shift: 10 },
17+
g: Bitfield { len: 5, shift: 5 },
18+
b: Bitfield { len: 5, shift: 0 },
19+
a: Bitfield { len: 0, shift: 0 },
20+
};
21+
22+
static R5_G5_B5_A1_COLOR_MASK: Bitfields = Bitfields {
23+
r: Bitfield { len: 5, shift: 10 },
24+
g: Bitfield { len: 5, shift: 5 },
25+
b: Bitfield { len: 5, shift: 0 },
26+
a: Bitfield { len: 1, shift: 15 },
827
};
9-
use byteorder_lite::ReadBytesExt;
10-
use std::io::{self, Read};
1128

1229
struct ColorMap {
1330
/// sizes in bytes
@@ -57,6 +74,8 @@ pub struct TgaDecoder<R> {
5774

5875
header: Header,
5976
color_map: Option<ColorMap>,
77+
78+
packing: Option<&'static Bitfields>,
6079
}
6180

6281
impl<R: Read> TgaDecoder<R> {
@@ -76,6 +95,8 @@ impl<R: Read> TgaDecoder<R> {
7695

7796
header: Header::default(),
7897
color_map: None,
98+
99+
packing: None,
79100
};
80101
decoder.read_metadata()?;
81102
Ok(decoder)
@@ -152,6 +173,16 @@ impl<R: Read> TgaDecoder<R> {
152173
self.color_type = ColorType::L8;
153174
self.original_color_type = Some(ExtendedColorType::A8);
154175
}
176+
(0, 15, false) => {
177+
self.color_type = ColorType::Rgb8;
178+
self.original_color_type = Some(ExtendedColorType::Rgb5);
179+
self.packing = Some(&R5_G5_B5_COLOR_MASK);
180+
}
181+
(1, 15, true) => {
182+
self.color_type = ColorType::Rgba8;
183+
self.original_color_type = Some(ExtendedColorType::Rgb5a1);
184+
self.packing = Some(&R5_G5_B5_A1_COLOR_MASK);
185+
}
155186
_ => {
156187
return Err(ImageError::Unsupported(
157188
UnsupportedError::from_format_and_kind(
@@ -350,16 +381,21 @@ impl<R: Read> ImageDecoder for TgaDecoder<R> {
350381
.unwrap_or_else(|| self.color_type().into())
351382
}
352383

384+
#[allow(clippy::identity_op)]
353385
fn read_image(mut self, buf: &mut [u8]) -> ImageResult<()> {
354386
assert_eq!(u64::try_from(buf.len()), Ok(self.total_bytes()));
355387

356388
// In indexed images, we might need more bytes than pixels to read them. That's nonsensical
357389
// to encode but we'll not want to crash.
390+
//
391+
// also used for packed (<8 bit per channel) images
358392
let mut fallback_buf = vec![];
359393
// read the pixels from the data region
360394
let rawbuf = if self.image_type.is_encoded() {
361395
let pixel_data = self.read_all_encoded_data()?;
362-
if self.bytes_per_pixel <= usize::from(self.color_type.bytes_per_pixel()) {
396+
if self.bytes_per_pixel <= usize::from(self.color_type.bytes_per_pixel())
397+
&& self.packing.is_none()
398+
{
363399
buf[..pixel_data.len()].copy_from_slice(&pixel_data);
364400
&buf[..pixel_data.len()]
365401
} else {
@@ -368,7 +404,9 @@ impl<R: Read> ImageDecoder for TgaDecoder<R> {
368404
}
369405
} else {
370406
let num_raw_bytes = self.width * self.height * self.bytes_per_pixel;
371-
if self.bytes_per_pixel <= usize::from(self.color_type.bytes_per_pixel()) {
407+
if self.bytes_per_pixel <= usize::from(self.color_type.bytes_per_pixel())
408+
&& self.packing.is_none()
409+
{
372410
self.r.by_ref().read_exact(&mut buf[..num_raw_bytes])?;
373411
&buf[..num_raw_bytes]
374412
} else {
@@ -389,10 +427,55 @@ impl<R: Read> ImageDecoder for TgaDecoder<R> {
389427
LimitErrorKind::DimensionError,
390428
)));
391429
}
392-
buf.copy_from_slice(&pixel_data);
430+
431+
if self.packing.is_none() {
432+
buf.copy_from_slice(&pixel_data);
433+
} else {
434+
fallback_buf.resize(buf.len(), 0);
435+
fallback_buf.copy_from_slice(&pixel_data);
436+
}
393437
}
394438

395-
self.reverse_encoding_in_output(buf);
439+
if let Some(bitfields) = &self.packing {
440+
let num_pixels = self.width * self.height;
441+
let bytes_per_unpacked_pixel = if bitfields.a.len > 0 { 4 } else { 3 };
442+
let mut stream = Cursor::new(fallback_buf);
443+
let bytes_per_packed_pixel = self.bytes_per_pixel;
444+
445+
// this check shouldn't get hit, unsupported formats should have been rejected in `read_color_information`
446+
// but it seemed better to check this here instead of panicing below if there is an issue
447+
if !(1..=3).contains(&bytes_per_packed_pixel) {
448+
return Err(ImageError::Unsupported(
449+
UnsupportedError::from_format_and_kind(
450+
ImageFormatHint::Exact(ImageFormat::Tga),
451+
UnsupportedErrorKind::GenericFeature(
452+
"Unsupported packed pixel format".to_string(),
453+
),
454+
),
455+
));
456+
}
457+
458+
for i in 0..num_pixels {
459+
let value = match bytes_per_packed_pixel {
460+
1 => stream.read_u8().map(|i| -> u32 { i.to_u32().unwrap() }),
461+
2 => stream
462+
.read_u16::<LittleEndian>()
463+
.map(|i| -> u32 { i.to_u32().unwrap() }),
464+
3 => stream.read_u24::<LittleEndian>(),
465+
_ => unimplemented!(),
466+
}
467+
.map_err(|e| -> ImageError { ImageError::IoError(e) })?;
468+
469+
buf[i * bytes_per_unpacked_pixel + 0] = bitfields.r.read(value);
470+
buf[i * bytes_per_unpacked_pixel + 1] = bitfields.g.read(value);
471+
buf[i * bytes_per_unpacked_pixel + 2] = bitfields.b.read(value);
472+
if bytes_per_unpacked_pixel == 4 {
473+
buf[i * bytes_per_unpacked_pixel + 3] = bitfields.a.read(value);
474+
}
475+
}
476+
} else {
477+
self.reverse_encoding_in_output(buf);
478+
}
396479

397480
self.flip_vertically(buf);
398481

src/color.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,10 @@ pub enum ExtendedColorType {
120120
Rgb4,
121121
/// Pixel is 4-bit RGB with an alpha channel
122122
Rgba4,
123+
/// Pixel is 5-bit RGB with one unused bit
124+
Rgb5,
125+
/// Pixel is 5-bit RGB with one bit alpha
126+
Rgb5a1,
123127
/// Pixel is 8-bit luminance
124128
L8,
125129
/// Pixel is 8-bit luminance with an alpha channel
@@ -179,13 +183,15 @@ impl ExtendedColorType {
179183
ExtendedColorType::Rgb1
180184
| ExtendedColorType::Rgb2
181185
| ExtendedColorType::Rgb4
186+
| ExtendedColorType::Rgb5
182187
| ExtendedColorType::Rgb8
183188
| ExtendedColorType::Rgb16
184189
| ExtendedColorType::Rgb32F
185190
| ExtendedColorType::Bgr8 => 3,
186191
ExtendedColorType::Rgba1
187192
| ExtendedColorType::Rgba2
188193
| ExtendedColorType::Rgba4
194+
| ExtendedColorType::Rgb5a1
189195
| ExtendedColorType::Rgba8
190196
| ExtendedColorType::Rgba16
191197
| ExtendedColorType::Rgba32F
@@ -213,6 +219,8 @@ impl ExtendedColorType {
213219
ExtendedColorType::Rgba4 => 16,
214220
ExtendedColorType::L8 => 8,
215221
ExtendedColorType::La8 => 16,
222+
ExtendedColorType::Rgb5 => 16, // somewhat of a lie, it's actually 15 bits of image data and one unused bit.
223+
ExtendedColorType::Rgb5a1 => 16,
216224
ExtendedColorType::Rgb8 => 24,
217225
ExtendedColorType::Rgba8 => 32,
218226
ExtendedColorType::L16 => 16,

src/utils/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
33
use std::iter::repeat;
44

5+
pub(crate) mod packed_color;
6+
57
#[inline(always)]
68
pub(crate) fn expand_packed<F>(buf: &mut [u8], channels: usize, bit_depth: u8, mut func: F)
79
where

0 commit comments

Comments
 (0)