1#![no_implicit_prelude]
21
22extern crate core;
23extern crate rpsp;
24
25use core::clone::Clone;
26use core::convert::From;
27use core::fmt::{self, Debug, Formatter};
28use core::iter::{IntoIterator, Iterator};
29use core::marker::{Copy, PhantomData};
30use core::ops::Deref;
31use core::option::Option::{self, None, Some};
32use core::result::Result::{self, Err, Ok};
33use core::unreachable;
34
35use rpsp::io::{Error, Read, Seek, SeekFrom};
36
37use crate::frame::RGB;
38use crate::fs::DeviceError;
39
40const ATTRS_NONE: u8 = 0u8;
41const ATTRS_TOP_LEFT: u8 = 0x08u8;
42const ATTRS_GRAYSCALE: u8 = 0x01u8;
43const ATTRS_TOP_RIGHT: u8 = 0x10u8;
44const ATTRS_TRUE_COLOR: u8 = 0x02u8;
45const ATTRS_BOTTOM_LEFT: u8 = 0x20u8;
46const ATTRS_BOTTOM_RIGHT: u8 = 0x40u8;
47const ATTRS_MAPPED_COLOR: u8 = 0x04u8;
48const ATTRS_IS_COMPRESSED: u8 = 0x80u8;
49
50pub enum ImageError {
51 InvalidType(u8),
52 Io(Error<DeviceError>),
53 Empty,
54 NotTGA,
55 InvalidImage,
56 InvalidColorMap,
57}
58pub enum Pixels<'a, R: Reader> {
59 Raw(Raw<'a, R>),
60 Compressed(Compressed<'a, R>),
61}
62
63pub struct Pixel {
64 pub pos: Point,
65 pub color: u32,
66}
67pub struct Point {
68 pub x: i32,
69 pub y: i32,
70}
71pub struct Header {
72 map: Option<ColorMap>,
73 bits: u8,
74 attrs: u8,
75 width: u16,
76 alpha: u8,
77 height: u16,
78 origin: Point,
79}
80pub struct Raw<'a, R: Reader> {
81 pos: Point,
82 image: TgaParser<'a, R>,
83}
84pub struct TgaParser<'a, R: Reader> {
85 buf: [u8; 0xFF],
86 pos: usize,
87 avail: usize,
88 reader: &'a mut R,
89 header: Header,
90 _p: PhantomData<*const ()>,
91}
92pub struct Compressed<'a, R: Reader> {
93 cur: u32,
94 pos: Point,
95 skip: u8,
96 count: u8,
97 image: TgaParser<'a, R>,
98}
99
100pub trait Reader: Read<DeviceError> + Seek<DeviceError> {}
101
102struct ColorMap {
103 len: u16,
104 pos: u16,
105 buf: [u8; 4],
106 bits: u8,
107 last: Option<u32>,
108 index: u16,
109}
110
111impl Pixel {
112 #[inline(always)]
113 pub fn rgb(&self) -> RGB {
114 RGB::raw(self.color)
115 }
116 #[inline(always)]
117 pub fn is_solid(&self) -> bool {
118 (self.color >> 24) & 0xFF == 0xFF
119 }
120 #[inline(always)]
121 pub fn is_transparent(&self) -> bool {
122 (self.color >> 24) & 0xFF == 0
123 }
124}
125impl Point {
126 #[inline(always)]
127 fn new(x: i32, y: i32) -> Point {
128 Point { x, y }
129 }
130
131 fn next(&mut self, h: &Header) -> Option<Point> {
132 if self.y < 0 || self.y >= h.height as i32 {
133 return None;
134 }
135 let p = *self;
136 self.x = self.x.saturating_add(1);
137 if self.x >= h.width as i32 {
138 self.y = if h.is_flipped() { self.y.saturating_sub(1) } else { self.y.saturating_add(1) };
139 self.x = 0;
140 }
141 Some(p)
142 }
143}
144impl Header {
145 fn new(r: &mut impl Reader) -> Result<Header, ImageError> {
146 let mut b = [0u8; 18];
147 r.read_exact(&mut b)?;
148 match b[16] {
149 8 | 16 | 24 | 32 => (),
150 _ => return Err(ImageError::InvalidImage),
151 }
152 if b[0] > 0 {
153 r.seek(SeekFrom::Current(b[0] as i64))?;
154 }
155 Ok(Header {
156 map: ColorMap::new(&b)?,
157 bits: b[16] / 0x8,
158 attrs: attrs(b[2], b[17])?,
159 alpha: b[17] & 0xF,
160 width: le_u16(&b[12..]),
161 height: le_u16(&b[14..]),
162 origin: Point::new(le_u16(&b[8..]) as i32, le_u16(&b[10..]) as i32),
163 })
164 }
165
166 #[inline(always)]
167 pub fn alpha(&self) -> u8 {
168 self.alpha
169 }
170 #[inline(always)]
171 pub fn width(&self) -> i32 {
172 self.width as i32
173 }
174 #[inline(always)]
175 pub fn height(&self) -> i32 {
176 self.height as i32
177 }
178 #[inline(always)]
179 pub fn origin(&self) -> Point {
180 self.origin
181 }
182 #[inline(always)]
183 pub fn pixel_size(&self) -> u8 {
184 self.bits
185 }
186 #[inline]
187 pub fn image_start(&self) -> u64 {
188 self.map
189 .as_ref()
190 .map(|m| m.pos as u64 + (m.len * m.bits as u16) as u64)
191 .unwrap_or(0)
192 }
193 #[inline(always)]
194 pub fn is_flipped(&self) -> bool {
195 self.attrs & ATTRS_BOTTOM_LEFT != 0 || self.attrs & ATTRS_BOTTOM_RIGHT != 0
196 }
197 #[inline(always)]
198 pub fn is_compressed(&self) -> bool {
199 self.attrs & ATTRS_IS_COMPRESSED != 0
200 }
201}
202impl ColorMap {
203 fn new(b: &[u8]) -> Result<Option<ColorMap>, ImageError> {
204 match b[1] {
205 1 => (),
206 0 => return Ok(None),
207 _ => return Err(ImageError::InvalidColorMap),
208 }
209 let n = le_u16(&b[5..]);
210 if n == 0 {
211 return Ok(None);
212 }
213 let p = le_u16(&b[3..]);
214 Ok(Some(ColorMap {
215 len: n,
216 pos: if p == 0 { 0x12u16 + b[0] as u16 } else { p },
217 buf: [0u8; 4],
218 bits: b[7] / 0x8,
219 last: None,
220 index: 0u16,
221 }))
222 }
223
224 fn index(&mut self, v: u32, r: &mut impl Reader) -> Result<Option<u32>, ImageError> {
225 if v as u16 == self.index && self.last.is_some() {
226 return Ok(self.last);
227 }
228 if v as u16 >= self.len {
229 return Ok(None);
230 }
231 let i = v as u64 * self.bits as u64;
232 if i > 0xFFFF {
233 return Ok(None);
234 }
235 let l = r.stream_position()?;
236 r.seek(SeekFrom::Start(i + self.pos as u64))?;
237 let n = r.read(&mut self.buf)?;
238 r.seek(SeekFrom::Start(l))?;
239 let c = match self.bits {
240 1 if n >= 1 => self.buf[0] as u32,
241 2 if n >= 2 => le_u16(&self.buf) as u32,
242 3 if n >= 3 => le_u24(&self.buf),
243 4 if n >= 4 => le_u32(&self.buf),
244 _ => return Ok(None),
245 };
246 self.index = v as u16;
247 self.last.replace(c);
248 Ok(Some(c))
249 }
250}
251impl<R: Reader> TgaParser<'_, R> {
252 #[inline]
253 pub fn new<'a>(reader: &'a mut R) -> Result<TgaParser<'a, R>, ImageError> {
254 let h = Header::new(reader)?;
255 let s = h.image_start();
256 if s > 0 {
257 reader.seek(SeekFrom::Start(s))?;
258 }
259 Ok(TgaParser {
260 reader,
261 buf: [0u8; 0xFF],
262 pos: 0usize,
263 avail: 0usize,
264 header: h,
265 _p: PhantomData,
266 })
267 }
268
269 #[inline(always)]
270 pub fn header(&self) -> &Header {
271 &self.header
272 }
273
274 #[inline]
275 fn fix_alpha(&self, v: u32) -> u32 {
276 if (v >> 24) & 0xFF > 0 {
277 return v;
278 } else if self.header.alpha == 0 {
279 v | 0xFF000000
280 } else {
281 v
282 }
283 }
284 #[inline]
285 fn next(&mut self) -> Result<u32, ImageError> {
286 match self.header.bits {
287 1 => Ok(self.read(1)?[0] as u32),
288 2 => Ok(le_u16(self.read(2)?) as u32),
289 3 => Ok(le_u24(self.read(3)?)),
290 4 => Ok(le_u32(self.read(4)?)),
291 _ => unreachable!(),
292 }
293 }
294 fn map(&mut self, c: u32) -> Result<u32, ImageError> {
295 let n = match self.header.map.as_mut() {
296 Some(m) => m.index(c, self.reader)?.unwrap_or(c),
297 None => c,
298 };
299 match self.header.bits {
301 _ if self.header.attrs & 0x7 == ATTRS_NONE => Ok(n),
302 1 if self.header.attrs & ATTRS_GRAYSCALE != 0 => Ok((n & 0xFF) << 16 | (n & 0xFF) << 8 | n & 0xFF | 0xFF000000),
305 2 => Ok((0xFF * ((n >> 15) & 0x1)) | (((n >> 10) & 0x1F) << 16) | (((n >> 5) << 0x1F) << 8) | (n & 0x1F)),
308 3 => Ok(n | 0xFF000000), _ => Ok(n), }
311 }
312 #[inline]
313 fn read(&mut self, want: usize) -> Result<&[u8], ImageError> {
314 self.refill(want)?;
315 if self.avail.saturating_sub(self.pos) < want {
316 Err(ImageError::Empty)
317 } else {
318 let n = self.pos;
319 self.pos += want;
320 Ok(&self.buf[n..n + want])
321 }
322 }
323 fn refill(&mut self, want: usize) -> Result<usize, ImageError> {
324 while self.avail.saturating_sub(self.pos) < want {
325 if self.pos > 0 {
326 self.buf.copy_within(self.pos.., 0);
327 self.avail -= self.pos;
328 self.pos = 0;
329 }
330 let n = self.reader.read(&mut self.buf[self.avail..])?;
331 if n == 0 {
332 break;
333 }
334 self.avail += n;
335 }
336 Ok(self.avail)
337 }
338}
339impl<R: Reader> Compressed<'_, R> {
340 fn decompress(&mut self) -> Option<Result<u32, ImageError>> {
341 if self.count > 0 {
342 self.count -= 1;
343 return Some(Ok(self.cur));
344 }
345 if self.skip > 0 {
346 self.skip -= 1;
347 return Some(self.image.next().and_then(|v| self.image.map(v)));
348 }
349 let v = match self.image.read(1) {
350 Err(e) => return Some(Err(e)),
351 Ok(v) => v[0],
352 };
353 if v & 0x80 != 0 {
354 self.count = (v & 0x7F) + 1;
355 self.cur = match self.image.next() {
356 Err(e) => return Some(Err(e)),
357 Ok(v) => v,
358 };
359 } else {
360 self.skip = (v & 0x7F) + 1;
361 }
362 self.decompress()
363 }
364}
365
366impl<'a, R: Reader> IntoIterator for TgaParser<'a, R> {
367 type IntoIter = Pixels<'a, R>;
368 type Item = Result<Pixel, ImageError>;
369
370 fn into_iter(self) -> Pixels<'a, R> {
371 let y = if self.header.is_flipped() { self.header.height.saturating_sub(1) } else { 0 };
372 if self.header.attrs & ATTRS_IS_COMPRESSED != 0 {
373 Pixels::Compressed(Compressed {
374 pos: Point::new(0, y as i32),
375 cur: 0u32,
376 skip: 0u8,
377 image: self,
378 count: 0u8,
379 })
380 } else {
381 Pixels::Raw(Raw {
382 pos: Point::new(0, y as i32),
383 image: self,
384 })
385 }
386 }
387}
388
389impl<R: Reader> Iterator for Raw<'_, R> {
390 type Item = Result<Pixel, ImageError>;
391
392 fn next(&mut self) -> Option<Result<Pixel, ImageError>> {
393 let p = self.pos.next(&self.image.header)?;
394 match self.image.next() {
395 Ok(c) => Some(Ok(Pixel {
396 pos: p,
397 color: match self.image.map(c) {
398 Err(e) => return Some(Err(e)),
399 Ok(v) => self.image.fix_alpha(v),
400 },
401 })),
402 Err(ImageError::Empty) => None,
403 Err(e) => Some(Err(e)),
404 }
405 }
406}
407impl<R: Reader> Iterator for Pixels<'_, R> {
408 type Item = Result<Pixel, ImageError>;
409
410 #[inline(always)]
411 fn next(&mut self) -> Option<Result<Pixel, ImageError>> {
412 match self {
413 Pixels::Raw(v) => v.next(),
414 Pixels::Compressed(v) => v.next(),
415 }
416 }
417}
418impl<R: Reader> Iterator for Compressed<'_, R> {
419 type Item = Result<Pixel, ImageError>;
420
421 fn next(&mut self) -> Option<Result<Pixel, ImageError>> {
422 let p = self.pos.next(&self.image.header)?;
423 match self.decompress()? {
424 Ok(c) => Some(Ok(Pixel {
425 pos: p,
426 color: self.image.fix_alpha(c),
427 })),
428 Err(ImageError::Empty) => None,
429 Err(e) => Some(Err(e)),
430 }
431 }
432}
433
434impl Copy for Point {}
435impl Clone for Point {
436 #[inline(always)]
437 fn clone(&self) -> Point {
438 Point { x: self.x, y: self.y }
439 }
440}
441
442impl Copy for Pixel {}
443impl Clone for Pixel {
444 #[inline(always)]
445 fn clone(&self) -> Pixel {
446 Pixel {
447 pos: self.pos.clone(),
448 color: self.color,
449 }
450 }
451}
452impl Deref for Pixel {
453 type Target = Point;
454
455 #[inline(always)]
456 fn deref(&self) -> &Point {
457 &self.pos
458 }
459}
460
461impl From<DeviceError> for ImageError {
462 #[inline(always)]
463 fn from(v: DeviceError) -> ImageError {
464 ImageError::Io(Error::Other(v))
465 }
466}
467impl From<Error<DeviceError>> for ImageError {
468 #[inline(always)]
469 fn from(v: Error<DeviceError>) -> ImageError {
470 ImageError::Io(v)
471 }
472}
473
474impl<D: Read<DeviceError> + Seek<DeviceError>> Reader for D {}
475
476#[cfg(feature = "debug")]
477impl Debug for ImageError {
478 #[inline]
479 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
480 match self {
481 ImageError::Io(v) => f.debug_tuple("Io").field(v).finish(),
482 ImageError::InvalidType(v) => f.debug_tuple("InvalidType").field(v).finish(),
483 ImageError::Empty => f.write_str("Empty"),
484 ImageError::NotTGA => f.write_str("NotTGA"),
485 ImageError::InvalidImage => f.write_str("InvalidImage"),
486 ImageError::InvalidColorMap => f.write_str("InvalidColorMap"),
487 }
488 }
489}
490#[cfg(not(feature = "debug"))]
491impl Debug for ImageError {
492 #[inline(always)]
493 fn fmt(&self, _f: &mut Formatter<'_>) -> fmt::Result {
494 Ok(())
495 }
496}
497
498#[inline]
499fn le_u16(b: &[u8]) -> u16 {
500 (b[0] as u16) | (b[1] as u16) << 8
501}
502#[inline]
503fn le_u24(b: &[u8]) -> u32 {
504 (b[0] as u32) | (b[1] as u32) << 8 | (b[2] as u32) << 16
505}
506#[inline]
507fn le_u32(b: &[u8]) -> u32 {
508 (b[0] as u32) | (b[1] as u32) << 8 | (b[2] as u32) << 16 | (b[3] as u32) << 24
509}
510#[inline]
511fn attrs(a: u8, p: u8) -> Result<u8, ImageError> {
512 let r = match a {
513 _ if a & !0xB != 0 => return Err(ImageError::InvalidType(a)),
514 0 => return Err(ImageError::InvalidType(a)),
515 1 if a & 0x8 != 0 => ATTRS_MAPPED_COLOR | ATTRS_IS_COMPRESSED,
516 2 if a & 0x8 != 0 => ATTRS_TRUE_COLOR | ATTRS_IS_COMPRESSED,
517 3 if a & 0x8 != 0 => ATTRS_GRAYSCALE | ATTRS_IS_COMPRESSED,
518 _ if a & 0x8 != 0 => ATTRS_NONE | ATTRS_IS_COMPRESSED,
519 1 => ATTRS_MAPPED_COLOR,
520 2 => ATTRS_TRUE_COLOR,
521 3 => ATTRS_GRAYSCALE,
522 _ => ATTRS_NONE,
523 };
524 match (p & 0x30) >> 4 {
525 0 => Ok(r | ATTRS_BOTTOM_LEFT),
526 1 => Ok(r | ATTRS_BOTTOM_RIGHT),
527 2 => Ok(r | ATTRS_TOP_LEFT),
528 _ => Ok(r | ATTRS_TOP_RIGHT),
529 }
530}