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, 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        // NOTE(sf): Reformat to "AARRGGBB" form.
300        match self.header.bits {
301            _ if self.header.attrs & 0x7 == ATTRS_NONE => Ok(n),
302            // Replicate Grayscale into "FFVVVVVV" format where the V is the 0-255
303            // value of Gray, since all hex colors matching all six are Gray colors.
304            1 if self.header.attrs & ATTRS_GRAYSCALE != 0 => Ok((n & 0xFF) << 16 | (n & 0xFF) << 8 | n & 0xFF | 0xFF000000),
305            // Expand the A-5-5-5 value. The first is the alpha, 1 or 0, so we can just multiply it.
306            // Next we extract 5 bits and reposition them into the "AARRGGBB" format.
307            2 => Ok((0xFF * ((n >> 15) & 0x1)) | (((n >> 10) & 0x1F) << 16) | (((n >> 5) << 0x1F) << 8) | (n & 0x1F)),
308            3 => Ok(n | 0xFF000000), // Add FF alpha channel, since 3bit doesn't have one.
309            _ => Ok(n),              // AAARRGGBB
310        }
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}