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}