Coverage Report

Created: 2025-10-10 07:21

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/image/src/codecs/tga/header.rs
Line
Count
Source
1
use crate::error::{UnsupportedError, UnsupportedErrorKind};
2
use crate::{ExtendedColorType, ImageError, ImageFormat, ImageResult};
3
use byteorder_lite::{LittleEndian, ReadBytesExt, WriteBytesExt};
4
use std::io::{Read, Write};
5
6
pub(crate) const ALPHA_BIT_MASK: u8 = 0b1111;
7
pub(crate) const SCREEN_ORIGIN_BIT_MASK: u8 = 0b10_0000;
8
9
pub(crate) enum ImageType {
10
    NoImageData = 0,
11
    /// Uncompressed images.
12
    RawColorMap = 1,
13
    RawTrueColor = 2,
14
    RawGrayScale = 3,
15
    /// Run length encoded images.
16
    RunColorMap = 9,
17
    RunTrueColor = 10,
18
    RunGrayScale = 11,
19
    Unknown,
20
}
21
22
impl ImageType {
23
    /// Create a new image type from a u8.
24
874
    pub(crate) fn new(img_type: u8) -> ImageType {
25
874
        match img_type {
26
24
            0 => ImageType::NoImageData,
27
28
95
            1 => ImageType::RawColorMap,
29
8
            2 => ImageType::RawTrueColor,
30
2
            3 => ImageType::RawGrayScale,
31
32
414
            9 => ImageType::RunColorMap,
33
109
            10 => ImageType::RunTrueColor,
34
144
            11 => ImageType::RunGrayScale,
35
36
78
            _ => ImageType::Unknown,
37
        }
38
874
    }
39
40
    /// Check if the image format uses colors as opposed to gray scale.
41
736
    pub(crate) fn is_color(&self) -> bool {
42
736
        matches! { *self,
43
            ImageType::RawColorMap
44
            | ImageType::RawTrueColor
45
            | ImageType::RunTrueColor
46
            | ImageType::RunColorMap
47
        }
48
736
    }
49
50
    /// Does the image use a color map.
51
1.30k
    pub(crate) fn is_color_mapped(&self) -> bool {
52
1.30k
        matches!(*self, Self::RawColorMap | Self::RunColorMap)
53
1.30k
    }
54
55
    /// Is the image run length encoded.
56
650
    pub(crate) fn is_encoded(&self) -> bool {
57
108
        matches!(
58
650
            *self,
59
            Self::RunColorMap | Self::RunTrueColor | Self::RunGrayScale
60
        )
61
650
    }
62
}
63
64
/// Header used by TGA image files.
65
#[derive(Debug, Default)]
66
pub(crate) struct Header {
67
    pub(crate) id_length: u8,      // length of ID string
68
    pub(crate) map_type: u8,       // color map type
69
    pub(crate) image_type: u8,     // image type code
70
    pub(crate) map_origin: u16,    // starting index of map
71
    pub(crate) map_length: u16,    // length of map
72
    pub(crate) map_entry_size: u8, // size of map entries in bits
73
    pub(crate) x_origin: u16,      // x-origin of image
74
    pub(crate) y_origin: u16,      // y-origin of image
75
    pub(crate) image_width: u16,   // width of image
76
    pub(crate) image_height: u16,  // height of image
77
    pub(crate) pixel_depth: u8,    // bits per pixel
78
    pub(crate) image_desc: u8,     // image descriptor
79
}
80
81
impl Header {
82
    /// Load the header with values from pixel information.
83
0
    pub(crate) fn from_pixel_info(
84
0
        color_type: ExtendedColorType,
85
0
        width: u16,
86
0
        height: u16,
87
0
        use_rle: bool,
88
0
    ) -> ImageResult<Self> {
89
0
        let mut header = Self::default();
90
91
0
        if width > 0 && height > 0 {
92
0
            let (num_alpha_bits, other_channel_bits, image_type) = match (color_type, use_rle) {
93
0
                (ExtendedColorType::Rgba8, true) => (8, 24, ImageType::RunTrueColor),
94
0
                (ExtendedColorType::Rgb8, true) => (0, 24, ImageType::RunTrueColor),
95
0
                (ExtendedColorType::La8, true) => (8, 8, ImageType::RunGrayScale),
96
0
                (ExtendedColorType::L8, true) => (0, 8, ImageType::RunGrayScale),
97
0
                (ExtendedColorType::Rgba8, false) => (8, 24, ImageType::RawTrueColor),
98
0
                (ExtendedColorType::Rgb8, false) => (0, 24, ImageType::RawTrueColor),
99
0
                (ExtendedColorType::La8, false) => (8, 8, ImageType::RawGrayScale),
100
0
                (ExtendedColorType::L8, false) => (0, 8, ImageType::RawGrayScale),
101
                _ => {
102
0
                    return Err(ImageError::Unsupported(
103
0
                        UnsupportedError::from_format_and_kind(
104
0
                            ImageFormat::Tga.into(),
105
0
                            UnsupportedErrorKind::Color(color_type),
106
0
                        ),
107
0
                    ))
108
                }
109
            };
110
111
0
            header.image_type = image_type as u8;
112
0
            header.image_width = width;
113
0
            header.image_height = height;
114
0
            header.pixel_depth = num_alpha_bits + other_channel_bits;
115
0
            header.image_desc = num_alpha_bits & ALPHA_BIT_MASK;
116
0
            header.image_desc |= SCREEN_ORIGIN_BIT_MASK; // Upper left origin.
117
0
        }
118
119
0
        Ok(header)
120
0
    }
121
122
    /// Load the header with values from the reader.
123
874
    pub(crate) fn from_reader(r: &mut dyn Read) -> ImageResult<Self> {
124
        Ok(Self {
125
874
            id_length: r.read_u8()?,
126
874
            map_type: r.read_u8()?,
127
874
            image_type: r.read_u8()?,
128
874
            map_origin: r.read_u16::<LittleEndian>()?,
129
874
            map_length: r.read_u16::<LittleEndian>()?,
130
874
            map_entry_size: r.read_u8()?,
131
874
            x_origin: r.read_u16::<LittleEndian>()?,
132
874
            y_origin: r.read_u16::<LittleEndian>()?,
133
874
            image_width: r.read_u16::<LittleEndian>()?,
134
874
            image_height: r.read_u16::<LittleEndian>()?,
135
874
            pixel_depth: r.read_u8()?,
136
874
            image_desc: r.read_u8()?,
137
        })
138
874
    }
139
140
    /// Write out the header values.
141
0
    pub(crate) fn write_to(&self, w: &mut dyn Write) -> ImageResult<()> {
142
0
        w.write_u8(self.id_length)?;
143
0
        w.write_u8(self.map_type)?;
144
0
        w.write_u8(self.image_type)?;
145
0
        w.write_u16::<LittleEndian>(self.map_origin)?;
146
0
        w.write_u16::<LittleEndian>(self.map_length)?;
147
0
        w.write_u8(self.map_entry_size)?;
148
0
        w.write_u16::<LittleEndian>(self.x_origin)?;
149
0
        w.write_u16::<LittleEndian>(self.y_origin)?;
150
0
        w.write_u16::<LittleEndian>(self.image_width)?;
151
0
        w.write_u16::<LittleEndian>(self.image_height)?;
152
0
        w.write_u8(self.pixel_depth)?;
153
0
        w.write_u8(self.image_desc)?;
154
0
        Ok(())
155
0
    }
156
}