inky_frame/frame/
tga.rs

1// Permission is hereby granted, free of charge, to any person obtaining a copy
2// of this software and associated documentation files (the "Software"), to deal
3// in the Software without restriction, including without limitation the rights
4// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
5// copies of the Software, and to permit persons to whom the Software is
6// furnished to do so, subject to the following conditions:
7//
8// The above copyright notice and this permission notice shall be included in
9// all copies or substantial portions of the Software.
10//
11// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
12// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
13// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
14// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
15// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
16// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
17// SOFTWARE.
18//
19
20#![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;
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}
91pub struct Compressed<'a, R: Reader> {
92    cur:   u32,
93    pos:   Point,
94    skip:  u8,
95    count: u8,
96    image: TgaParser<'a, R>,
97}
98
99pub trait Reader: Read<DeviceError> + Seek<DeviceError> {}
100
101struct ColorMap {
102    len:   u16,
103    pos:   u16,
104    buf:   [u8; 4],
105    bits:  u8,
106    last:  Option<u32>,
107    index: u16,
108}
109
110impl Pixel {
111    #[inline(always)]
112    pub fn rgb(&self) -> RGB {
113        RGB::raw(self.color)
114    }
115    #[inline(always)]
116    pub fn is_solid(&self) -> bool {
117        (self.color >> 24) & 0xFF == 0xFF
118    }
119    #[inline(always)]
120    pub fn is_transparent(&self) -> bool {
121        (self.color >> 24) & 0xFF == 0
122    }
123}
124impl Point {
125    #[inline(always)]
126    fn new(x: i32, y: i32) -> Point {
127        Point { x, y }
128    }
129
130    fn next(&mut self, h: &Header) -> Option<Point> {
131        if self.y < 0 || self.y >= h.height as i32 {
132            return None;
133        }
134        let p = *self;
135        self.x = self.x.saturating_add(1);
136        if self.x >= h.width as i32 {
137            self.y = if h.is_flipped() { self.y.saturating_sub(1) } else { self.y.saturating_add(1) };
138            self.x = 0;
139        }
140        Some(p)
141    }
142}
143impl Header {
144    fn new(r: &mut impl Reader) -> Result<Header, ImageError> {
145        let mut b = [0u8; 18];
146        r.read_exact(&mut b)?;
147        match b[16] {
148            8 | 16 | 24 | 32 => (),
149            _ => return Err(ImageError::InvalidImage),
150        }
151        if b[0] > 0 {
152            r.seek(SeekFrom::Current(b[0] as i64))?;
153        }
154        Ok(Header {
155            map:    ColorMap::new(&b)?,
156            bits:   b[16] / 0x8,
157            attrs:  attrs(b[2], b[17])?,
158            alpha:  b[17] & 0xF,
159            width:  le_u16(&b[12..]),
160            height: le_u16(&b[14..]),
161            origin: Point::new(le_u16(&b[8..]) as i32, le_u16(&b[10..]) as i32),
162        })
163    }
164
165    #[inline(always)]
166    pub fn alpha(&self) -> u8 {
167        self.alpha
168    }
169    #[inline(always)]
170    pub fn width(&self) -> i32 {
171        self.width as i32
172    }
173    #[inline(always)]
174    pub fn height(&self) -> i32 {
175        self.height as i32
176    }
177    #[inline(always)]
178    pub fn origin(&self) -> Point {
179        self.origin
180    }
181    #[inline(always)]
182    pub fn pixel_size(&self) -> u8 {
183        self.bits
184    }
185    #[inline]
186    pub fn image_start(&self) -> u64 {
187        self.map
188            .as_ref()
189            .map(|m| m.pos as u64 + (m.len * m.bits as u16) as u64)
190            .unwrap_or(0)
191    }
192    #[inline(always)]
193    pub fn is_flipped(&self) -> bool {
194        self.attrs & ATTRS_BOTTOM_LEFT != 0 || self.attrs & ATTRS_BOTTOM_RIGHT != 0
195    }
196    #[inline(always)]
197    pub fn is_compressed(&self) -> bool {
198        self.attrs & ATTRS_IS_COMPRESSED != 0
199    }
200}
201impl ColorMap {
202    fn new(b: &[u8]) -> Result<Option<ColorMap>, ImageError> {
203        match b[1] {
204            1 => (),
205            0 => return Ok(None),
206            _ => return Err(ImageError::InvalidColorMap),
207        }
208        let n = le_u16(&b[5..]);
209        if n == 0 {
210            return Ok(None);
211        }
212        let p = le_u16(&b[3..]);
213        Ok(Some(ColorMap {
214            len:   n,
215            pos:   if p == 0 { 0x12u16 + b[0] as u16 } else { p },
216            buf:   [0u8; 4],
217            bits:  b[7] / 0x8,
218            last:  None,
219            index: 0u16,
220        }))
221    }
222
223    fn index(&mut self, v: u32, r: &mut impl Reader) -> Result<Option<u32>, ImageError> {
224        if v as u16 == self.index && self.last.is_some() {
225            return Ok(self.last);
226        }
227        if v as u16 >= self.len {
228            return Ok(None);
229        }
230        let i = v as u64 * self.bits as u64;
231        if i > 0xFFFF {
232            return Ok(None);
233        }
234        let l = r.stream_position()?;
235        r.seek(SeekFrom::Start(i + self.pos as u64))?;
236        let n = r.read(&mut self.buf)?;
237        r.seek(SeekFrom::Start(l))?;
238        let c = match self.bits {
239            1 if n >= 1 => self.buf[0] as u32,
240            2 if n >= 2 => le_u16(&self.buf) as u32,
241            3 if n >= 3 => le_u24(&self.buf),
242            4 if n >= 4 => le_u32(&self.buf),
243            _ => return Ok(None),
244        };
245        self.index = v as u16;
246        self.last.replace(c);
247        Ok(Some(c))
248    }
249}
250impl<R: Reader> TgaParser<'_, R> {
251    pub fn new<'a>(reader: &'a mut R) -> Result<TgaParser<'a, R>, ImageError> {
252        let h = Header::new(reader)?;
253        let s = h.image_start();
254        if s > 0 {
255            reader.seek(SeekFrom::Start(s))?;
256        }
257        Ok(TgaParser {
258            reader,
259            buf: [0u8; 0xFF],
260            pos: 0usize,
261            avail: 0usize,
262            header: h,
263        })
264    }
265
266    #[inline(always)]
267    pub fn header(&self) -> &Header {
268        &self.header
269    }
270
271    #[inline]
272    fn fix_alpha(&self, v: u32) -> u32 {
273        if (v >> 24) & 0xFF > 0 {
274            return v;
275        } else if self.header.alpha == 0 {
276            v | 0xFF000000
277        } else {
278            v
279        }
280    }
281    #[inline]
282    fn next(&mut self) -> Result<u32, ImageError> {
283        match self.header.bits {
284            1 => Ok(self.read(1)?[0] as u32),
285            2 => Ok(le_u16(self.read(2)?) as u32),
286            3 => Ok(le_u24(self.read(3)?)),
287            4 => Ok(le_u32(self.read(4)?)),
288            _ => unreachable!(),
289        }
290    }
291    fn map(&mut self, c: u32) -> Result<u32, ImageError> {
292        let n = match self.header.map.as_mut() {
293            Some(m) => m.index(c, self.reader)?.unwrap_or(c),
294            None => c,
295        };
296        // NOTE(sf): Reformat to "AARRGGBB" form.
297        match self.header.bits {
298            _ if self.header.attrs & 0x7 == ATTRS_NONE => Ok(n),
299            // Replicate Grayscale into "FFVVVVVV" format where the V is the 0-255
300            // value of Gray, since all hex colors matching all six are Gray colors.
301            1 if self.header.attrs & ATTRS_GRAYSCALE != 0 => Ok((n & 0xFF) << 16 | (n & 0xFF) << 8 | n & 0xFF | 0xFF000000),
302            // Expand the A-5-5-5 value. The first is the alpha, 1 or 0, so we can just multiply it.
303            // Next we extract 5 bits and reposition them into the "AARRGGBB" format.
304            2 => Ok((0xFF * ((n >> 15) & 0x1)) | (((n >> 10) & 0x1F) << 16) | (((n >> 5) << 0x1F) << 8) | (n & 0x1F)),
305            3 => Ok(n | 0xFF000000), // Add FF alpha channel, since 3bit doesn't have one.
306            _ => Ok(n),              // AAARRGGBB
307        }
308    }
309    #[inline]
310    fn read(&mut self, want: usize) -> Result<&[u8], ImageError> {
311        self.refill(want)?;
312        if self.avail.saturating_sub(self.pos) < want {
313            Err(ImageError::Empty)
314        } else {
315            let n = self.pos;
316            self.pos += want;
317            Ok(&self.buf[n..n + want])
318        }
319    }
320    fn refill(&mut self, want: usize) -> Result<usize, ImageError> {
321        while self.avail.saturating_sub(self.pos) < want {
322            if self.pos > 0 {
323                self.buf.copy_within(self.pos.., 0);
324                self.avail -= self.pos;
325                self.pos = 0;
326            }
327            let n = self.reader.read(&mut self.buf[self.avail..])?;
328            if n == 0 {
329                break;
330            }
331            self.avail += n;
332        }
333        Ok(self.avail)
334    }
335}
336impl<R: Reader> Compressed<'_, R> {
337    fn decompress(&mut self) -> Option<Result<u32, ImageError>> {
338        if self.count > 0 {
339            self.count -= 1;
340            return Some(Ok(self.cur));
341        }
342        if self.skip > 0 {
343            self.skip -= 1;
344            return Some(self.image.next().and_then(|v| self.image.map(v)));
345        }
346        let v = match self.image.read(1) {
347            Err(e) => return Some(Err(e)),
348            Ok(v) => v[0],
349        };
350        if v & 0x80 != 0 {
351            self.count = (v & 0x7F) + 1;
352            self.cur = match self.image.next() {
353                Err(e) => return Some(Err(e)),
354                Ok(v) => v,
355            };
356        } else {
357            self.skip = (v & 0x7F) + 1;
358        }
359        self.decompress()
360    }
361}
362
363impl<'a, R: Reader> IntoIterator for TgaParser<'a, R> {
364    type IntoIter = Pixels<'a, R>;
365    type Item = Result<Pixel, ImageError>;
366
367    fn into_iter(self) -> Pixels<'a, R> {
368        let y = if self.header.is_flipped() { self.header.height.saturating_sub(1) } else { 0 };
369        if self.header.attrs & ATTRS_IS_COMPRESSED != 0 {
370            Pixels::Compressed(Compressed {
371                pos:   Point::new(0, y as i32),
372                cur:   0u32,
373                skip:  0u8,
374                image: self,
375                count: 0u8,
376            })
377        } else {
378            Pixels::Raw(Raw {
379                pos:   Point::new(0, y as i32),
380                image: self,
381            })
382        }
383    }
384}
385
386impl<R: Reader> Iterator for Raw<'_, R> {
387    type Item = Result<Pixel, ImageError>;
388
389    fn next(&mut self) -> Option<Result<Pixel, ImageError>> {
390        let p = self.pos.next(&self.image.header)?;
391        match self.image.next() {
392            Ok(c) => Some(Ok(Pixel {
393                pos:   p,
394                color: match self.image.map(c) {
395                    Err(e) => return Some(Err(e)),
396                    Ok(v) => self.image.fix_alpha(v),
397                },
398            })),
399            Err(ImageError::Empty) => None,
400            Err(e) => Some(Err(e)),
401        }
402    }
403}
404impl<R: Reader> Iterator for Pixels<'_, R> {
405    type Item = Result<Pixel, ImageError>;
406
407    #[inline(always)]
408    fn next(&mut self) -> Option<Result<Pixel, ImageError>> {
409        match self {
410            Pixels::Raw(v) => v.next(),
411            Pixels::Compressed(v) => v.next(),
412        }
413    }
414}
415impl<R: Reader> Iterator for Compressed<'_, R> {
416    type Item = Result<Pixel, ImageError>;
417
418    fn next(&mut self) -> Option<Result<Pixel, ImageError>> {
419        let p = self.pos.next(&self.image.header)?;
420        match self.decompress()? {
421            Ok(c) => Some(Ok(Pixel {
422                pos:   p,
423                color: self.image.fix_alpha(c),
424            })),
425            Err(ImageError::Empty) => None,
426            Err(e) => Some(Err(e)),
427        }
428    }
429}
430
431impl Copy for Point {}
432impl Clone for Point {
433    #[inline(always)]
434    fn clone(&self) -> Point {
435        Point { x: self.x, y: self.y }
436    }
437}
438
439impl Copy for Pixel {}
440impl Clone for Pixel {
441    #[inline(always)]
442    fn clone(&self) -> Pixel {
443        Pixel {
444            pos:   self.pos.clone(),
445            color: self.color,
446        }
447    }
448}
449impl Deref for Pixel {
450    type Target = Point;
451
452    #[inline(always)]
453    fn deref(&self) -> &Point {
454        &self.pos
455    }
456}
457
458impl From<DeviceError> for ImageError {
459    #[inline(always)]
460    fn from(v: DeviceError) -> ImageError {
461        ImageError::Io(Error::Other(v))
462    }
463}
464impl From<Error<DeviceError>> for ImageError {
465    #[inline(always)]
466    fn from(v: Error<DeviceError>) -> ImageError {
467        ImageError::Io(v)
468    }
469}
470
471impl<D: Read<DeviceError> + Seek<DeviceError>> Reader for D {}
472
473#[cfg(feature = "debug")]
474impl Debug for ImageError {
475    #[inline]
476    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
477        match self {
478            ImageError::Io(v) => f.debug_tuple("Io").field(v).finish(),
479            ImageError::InvalidType(v) => f.debug_tuple("InvalidType").field(v).finish(),
480            ImageError::Empty => f.write_str("Empty"),
481            ImageError::NotTGA => f.write_str("NotTGA"),
482            ImageError::InvalidImage => f.write_str("InvalidImage"),
483            ImageError::InvalidColorMap => f.write_str("InvalidColorMap"),
484        }
485    }
486}
487#[cfg(not(feature = "debug"))]
488impl Debug for ImageError {
489    #[inline(always)]
490    fn fmt(&self, _f: &mut Formatter<'_>) -> fmt::Result {
491        Ok(())
492    }
493}
494
495#[inline]
496fn le_u16(b: &[u8]) -> u16 {
497    (b[0] as u16) | (b[1] as u16) << 8
498}
499#[inline]
500fn le_u24(b: &[u8]) -> u32 {
501    (b[0] as u32) | (b[1] as u32) << 8 | (b[2] as u32) << 16
502}
503#[inline]
504fn le_u32(b: &[u8]) -> u32 {
505    (b[0] as u32) | (b[1] as u32) << 8 | (b[2] as u32) << 16 | (b[3] as u32) << 24
506}
507#[inline]
508fn attrs(a: u8, p: u8) -> Result<u8, ImageError> {
509    let r = match a {
510        _ if a & !0xB != 0 => return Err(ImageError::InvalidType(a)),
511        0 => return Err(ImageError::InvalidType(a)),
512        1 if a & 0x8 != 0 => ATTRS_MAPPED_COLOR | ATTRS_IS_COMPRESSED,
513        2 if a & 0x8 != 0 => ATTRS_TRUE_COLOR | ATTRS_IS_COMPRESSED,
514        3 if a & 0x8 != 0 => ATTRS_GRAYSCALE | ATTRS_IS_COMPRESSED,
515        _ if a & 0x8 != 0 => ATTRS_NONE | ATTRS_IS_COMPRESSED,
516        1 => ATTRS_MAPPED_COLOR,
517        2 => ATTRS_TRUE_COLOR,
518        3 => ATTRS_GRAYSCALE,
519        _ => ATTRS_NONE,
520    };
521    match (p & 0x30) >> 4 {
522        0 => Ok(r | ATTRS_BOTTOM_LEFT),
523        1 => Ok(r | ATTRS_BOTTOM_RIGHT),
524        2 => Ok(r | ATTRS_TOP_LEFT),
525        _ => Ok(r | ATTRS_TOP_RIGHT),
526    }
527}