tinyvg_rs/
header.rs

1use std::io::{Cursor, Read};
2use byteorder::ReadBytesExt;
3use crate::common::{read_size, read_variable_sized_unsigned_number};
4use crate::TinyVgParseError;
5
6#[repr(u8)]
7#[derive(Debug)]
8pub enum ColorEncoding {
9    /// Each color is a 4-tuple (red, green, blue, alpha) of bytes with the color
10    /// channels encoded in sRGB and the alpha as linear alpha.
11    Rgba8888 = 0,
12
13    /// Each color is encoded as a 3-tuple (red, green, blue) with 16 bit per color.
14    /// While red and blue both use 5 bit, the green channel uses 6 bit. red uses
15    /// bit range 0...4, green bits 5...10 and blue bits 11...15. This color also uses
16    /// the sRGB color space.
17
18    Rgb565 = 1,
19    /// Each color is a 4-tuple (red, green ,blue, alpha) of binary32 IEEE 754
20    /// floating point value with the color channels encoded in scRGB and the
21    /// alpha as linear alpha. A color value of 1.0 is full intensity, while a value of
22    /// 0.0 is zero intensity.
23    RgbaF32 = 2,
24
25    /// The custom color encoding is defined undefined. The information how these
26    /// colors are encoded must be implemented via external means.
27    Custom = 3
28}
29
30impl ColorEncoding {
31    fn from_u8(value: u8) -> ColorEncoding {
32        match value {
33            0 => ColorEncoding::Rgba8888,
34            1 => ColorEncoding::Rgb565,
35            2 => ColorEncoding::RgbaF32,
36            3 => ColorEncoding::Custom,
37            _ => unreachable!("ColorEncoding::from_u8 must be a 2-bit value.")
38        }
39    }
40}
41
42#[repr(u8)]
43#[derive(Debug)]
44pub enum CoordinateRange {
45    /// Each Unit takes up 16 bit.
46    Default = 0,
47
48    /// Each Unit takes up 8 bit.
49    Reduced = 1,
50
51    /// Each Unit takes up 32 bit.
52    Enhanced = 2,
53}
54
55impl CoordinateRange {
56    fn from_u8(value: u8) -> CoordinateRange {
57        match value {
58            0 => CoordinateRange::Default,
59            1 => CoordinateRange::Reduced,
60            2 => CoordinateRange::Enhanced,
61            _ => unreachable!("CoordinateRange::from_u8 must be a 2-bit value.")
62        }
63    }
64}
65
66#[derive(Debug)]
67pub struct TinyVgHeader {
68    /// https://en.wikipedia.org/wiki/File_format#Magic_number
69    pub magic: [u8; 2],
70
71    /// The TinyVG version.
72    pub version: u8,
73
74    /// Defines the number of fraction bits in a Unit value.
75    pub scale: u8,
76
77    /// Defines the type of color information that is used in the color table.
78    pub color_encoding: ColorEncoding,
79
80    /// Defines the number of total bits in a Unit value and thus the overall
81    /// precision of the file.
82    pub coordinate_range: CoordinateRange,
83
84    /// Encodes the maximum width of the output file in display units.
85    /// A value of 0 indicates that the image has the maximum possible
86    /// width.
87    pub width: u32,
88
89    /// Encodes the maximum height of the output file in display units.
90    /// A value of 0 indicates that the image has the maximum possible
91    /// height.
92    pub height: u32,
93
94    /// The number of colors in the color table.
95    pub color_count: u64,
96}
97
98impl TinyVgHeader {
99    pub(crate) fn parse(cursor: &mut Cursor<&[u8]>) -> Result<Self, TinyVgParseError> {
100        let mut magic = [0u8; 2];
101        cursor.read_exact(&mut magic).map_err(|_| TinyVgParseError::InvalidHeader)?;
102        let version = cursor.read_u8().map_err(|_| TinyVgParseError::InvalidHeader)?;
103
104        // The encoded scale, color encoding, and coordinate range data.
105        let scc = cursor.read_u8().map_err(|_| TinyVgParseError::InvalidHeader)?;
106
107        // bits 0–3 = scale
108        let scale: u8 = scc & 0x0F;
109
110        // bits 4–5 = color encoding
111        let color_encoding_raw = (scc & 0b00_11_00_00) >> 4;
112        let color_encoding = ColorEncoding::from_u8(color_encoding_raw);
113
114        // bits 6–7 = coordinate range
115        let coordinate_range_raw = (scc & 0b11_00_00_00) >> 6;
116        let coordinate_range = CoordinateRange::from_u8(coordinate_range_raw);
117
118        let width: u32 = read_size(&coordinate_range, cursor)?;
119        let height: u32 = read_size(&coordinate_range, cursor)?;
120
121        let color_count = read_variable_sized_unsigned_number(cursor)?;
122
123        let header = TinyVgHeader {
124            magic,
125            version,
126            scale,
127            color_encoding,
128            coordinate_range,
129            width,
130            height,
131            color_count,
132        };
133
134        Ok(header)
135    }   
136}