tinytga/
raw_tga.rs

1use embedded_graphics::prelude::*;
2use nom::{bytes::complete::take, IResult};
3
4use crate::{
5    color_map::ColorMap,
6    footer::TgaFooter,
7    header::{Bpp, ImageOrigin, TgaHeader},
8    parse_error::ParseError,
9    raw_iter::RawPixels,
10    Compression, DataType,
11};
12
13/// Raw TGA image.
14///
15/// `RawTga` can be used to access lower level information about a TGA file and to access the
16/// raw pixel data. It can be created directly by using the [`from_slice`] constructor or accessed
17/// by calling [`as_raw`] method of a [`Tga`] object.
18///
19/// [`from_slice`]: #method.from_slice
20/// [`Tga`]: struct.Tga.html
21/// [`as_raw`]: struct.Tga.html#method.as_raw
22#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
23pub struct RawTga<'a> {
24    /// Image data
25    data: &'a [u8],
26
27    /// Color map
28    color_map: Option<ColorMap<'a>>,
29
30    /// Image pixel data
31    pixel_data: &'a [u8],
32
33    /// Image size
34    size: Size,
35
36    /// Data type
37    data_type: DataType,
38
39    /// Compression
40    compression: Compression,
41
42    /// Bits per pixel
43    bpp: Bpp,
44
45    /// Image origin
46    image_origin: ImageOrigin,
47}
48
49impl<'a> RawTga<'a> {
50    /// Parse a TGA image from a byte slice.
51    pub fn from_slice(data: &'a [u8]) -> Result<Self, ParseError> {
52        let input = data;
53        let (input, header) = TgaHeader::parse(input).map_err(|_| ParseError::Header)?;
54        let (input, _image_id) = parse_image_id(input, &header).map_err(|_| ParseError::Header)?;
55        let (input, color_map) = ColorMap::parse(input, &header)?;
56
57        let footer_length = TgaFooter::parse(data).map_or(0, |footer| footer.length(data));
58
59        // Use saturating_sub to make sure this can't panic
60        let pixel_data = &input[0..input.len().saturating_sub(footer_length)];
61
62        let size = Size::new(u32::from(header.width), u32::from(header.height));
63
64        Ok(Self {
65            data,
66            color_map,
67            pixel_data,
68            size,
69            bpp: header.pixel_depth,
70            image_origin: header.image_origin,
71            data_type: header.data_type,
72            compression: header.compression,
73        })
74    }
75
76    /// Returns the dimensions of this image.
77    pub fn size(&self) -> Size {
78        self.size
79    }
80
81    /// Returns the color map.
82    ///
83    /// `None` is returned if the image contains no color map.
84    pub fn color_map(&self) -> Option<&ColorMap<'a>> {
85        self.color_map.as_ref()
86    }
87
88    /// Returns the color bit depth (BPP) of this image.
89    ///
90    /// This function always returns the bit depth of the decoded pixels, regardless of how they are
91    /// stored in the TGA file. Use [`image_data_bpp`] to get the number of bits used to store one
92    /// pixel in the image data.
93    ///
94    /// [`image_data_bpp`]: #method.image_data_bpp
95    pub fn color_bpp(&self) -> Bpp {
96        if let Some(color_map) = &self.color_map {
97            color_map.entry_bpp()
98        } else {
99            self.bpp
100        }
101    }
102
103    /// Returns the image origin.
104    pub fn image_origin(&self) -> ImageOrigin {
105        self.image_origin
106    }
107
108    /// Returns the data type.
109    pub fn data_type(&self) -> DataType {
110        self.data_type
111    }
112
113    /// Returns the compression type.
114    pub fn compression(&self) -> Compression {
115        self.compression
116    }
117
118    /// Returns the raw image data contained in this image.
119    pub fn image_data(&self) -> &'a [u8] {
120        self.pixel_data
121    }
122
123    /// Returns the size of a single pixel in bits.
124    ///
125    /// This function returns the number of bits used to store a single pixel in the image data.
126    ///
127    /// For true color and grayscale images, where the colors are stored directly in the image data,
128    /// the returned value will match the value returned by [`color_bpp`].
129    ///
130    /// For color mapped images, where the image data consists of color indices, the returned value
131    /// describes the bit depth of the indices and may differ from the depth returned by
132    /// [`color_bpp`].
133    ///
134    /// [`color_bpp`]: #method.color_bpp
135    pub fn image_data_bpp(&self) -> Bpp {
136        self.bpp
137    }
138
139    /// Returns an iterator over the raw pixels in this image.
140    pub fn pixels(&self) -> RawPixels<'_> {
141        RawPixels::new(self)
142    }
143
144    /// Returns the TGA header.
145    ///
146    /// The returned object is a direct representation of the header contained
147    /// in the TGA file. Most of the information contained in the header is also
148    /// available using other methods, which are the preferred way of accessing
149    /// them.
150    ///
151    /// # Performance
152    ///
153    /// To save memory the header is parsed every time this method is called.
154    pub fn header(&self) -> TgaHeader {
155        // unwrap can't fail because the header was checked when self was created
156        TgaHeader::parse(self.data).unwrap().1
157    }
158
159    /// Returns the developer directory.
160    ///
161    /// # Performance
162    ///
163    /// To save memory the footer is parsed every time this method is called.
164    pub fn developer_directory(&self) -> Option<&'a [u8]> {
165        TgaFooter::parse(self.data).and_then(|footer| footer.developer_directory(self.data))
166    }
167
168    /// Returns the extension area.
169    ///
170    /// # Performance
171    ///
172    /// To save memory the footer is parsed every time this method is called.
173    pub fn extension_area(&self) -> Option<&'a [u8]> {
174        TgaFooter::parse(self.data).and_then(|footer| footer.extension_area(self.data))
175    }
176
177    /// Returns the content of the image ID.
178    ///
179    /// If the TGA file doesn't contain an image ID `None` is returned.
180    ///
181    /// # Performance
182    ///
183    /// To save memory the header is parsed every time this method is called.
184    pub fn image_id(&self) -> Option<&'a [u8]> {
185        let (input, header) = TgaHeader::parse(self.data).ok()?;
186
187        parse_image_id(input, &header)
188            .ok()
189            .map(|(_input, id)| id)
190            .filter(|id| !id.is_empty())
191    }
192}
193
194fn parse_image_id<'a>(input: &'a [u8], header: &TgaHeader) -> IResult<&'a [u8], &'a [u8]> {
195    take(header.id_len)(input)
196}