Skip to main content

xcf_rs/
parser.rs

1use std::io::{Read, Seek, SeekFrom};
2
3use byteorder::{BigEndian, ReadBytesExt};
4
5use crate::data::error::Error;
6use crate::data::pixeldata::PixelData;
7use crate::data::rgba::RgbaPixel;
8use crate::data::version::Version;
9use crate::TileCursor;
10
11pub trait ParseVersion {
12    fn parse<R: Read>(rdr: R) -> Result<Self, Error>
13    where
14        Self: Sized;
15}
16
17impl ParseVersion for Version {
18    fn parse<R: Read>(mut rdr: R) -> Result<Self, Error> {
19        let mut v = [0; 4];
20        rdr.read_exact(&mut v)?;
21        match &v {
22            b"file" => Ok(Self(0)),
23            [b'v', ver @ ..] => Ok(Self(
24                String::from_utf8_lossy(ver)
25                    .parse()
26                    .map_err(|_| Error::UnknownVersion)?,
27            )),
28            _ => Err(Error::UnknownVersion),
29        }
30    }
31}
32
33impl PixelData {
34    /// Parses the (silly?) hierarchy structure in the xcf file into a pixel array
35    /// Makes lots of assumptions! Only supports RGBA for now.
36    pub fn parse_hierarchy<R: Read + Seek>(
37        mut rdr: R,
38        version: Version,
39    ) -> Result<PixelData, Error> {
40        // read the hierarchy
41        let width = rdr.read_u32::<BigEndian>()?;
42        let height = rdr.read_u32::<BigEndian>()?;
43        let bpp = rdr.read_u32::<BigEndian>()?;
44        /*
45        if bpp != 3 && bpp != 4 {
46            return Err(Error::NotSupported);
47        }
48        */
49        let lptr = rdr.read_uint::<BigEndian>(version.bytes_per_offset())?;
50        let _dummpy_ptr_pos = rdr.stream_position()?;
51        rdr.seek(SeekFrom::Start(lptr))?;
52        // read the level
53        let level_width = rdr.read_u32::<BigEndian>()?;
54        let level_height = rdr.read_u32::<BigEndian>()?;
55        if level_width != width || level_height != height {
56            return Err(Error::InvalidFormat);
57        }
58
59        let mut pixels = vec![RgbaPixel([0, 0, 0, 255]); (width * height) as usize];
60        let mut next_tptr_pos;
61
62        let tiles_x = (f64::from(width) / 64.0).ceil() as u32;
63        let tiles_y = (f64::from(height) / 64.0).ceil() as u32;
64        for ty in 0..tiles_y {
65            for tx in 0..tiles_x {
66                let tptr = rdr.read_uint::<BigEndian>(version.bytes_per_offset())?;
67                next_tptr_pos = rdr.stream_position()?;
68                rdr.seek(SeekFrom::Start(tptr))?;
69
70                let mut cursor = TileCursor::new(width, height, tx, ty, bpp);
71                cursor.feed(&mut rdr, &mut pixels)?;
72
73                rdr.seek(SeekFrom::Start(next_tptr_pos))?;
74            }
75        }
76
77        // rdr.seek(SeekFrom::Start(dummpy_ptr_pos))?;
78        // TODO: dummy levels? do we need to consider them?
79        // if we do:
80        /*loop {
81            let dummy_level_ptr = rdr.read_u32::<BigEndian>()?;
82            if dummy_level_ptr == 0 {
83                break;
84            }
85        }*/
86        // we are now at the end of the heirarchy structure.
87
88        Ok(PixelData {
89            pixels,
90            width,
91            height,
92        })
93    }
94
95    pub fn pixel(&self, x: u32, y: u32) -> Option<RgbaPixel> {
96        if x >= self.width || y >= self.height {
97            return None;
98        }
99        Some(self.pixels[(y * self.width + x) as usize])
100    }
101
102    /// Creates a raw sub buffer from self.
103    ///
104    /// # Panics
105    ///
106    /// Panics if a pixel access is out of bounds.
107    pub fn raw_sub_rgba_buffer(&self, x: u32, y: u32, width: u32, height: u32) -> Vec<u8> {
108        let mut sub = Vec::with_capacity((width * height * 4) as usize);
109        for _y in y..(y + height) {
110            for _x in x..(x + width) {
111                if _y > self.height || _x > self.width {
112                    panic!("Pixel access is out of bounds");
113                }
114                sub.extend_from_slice(&self.pixel(_x, _y).unwrap().0);
115            }
116        }
117        sub
118    }
119}