1use std::{
2 fs::File,
3 io::{
4 Read,
5 Result,
6 Seek,
7 SeekFrom,
8 }
9};
10
11pub struct Header {
12 pub width: u32,
13 pub height: u32,
14 pub channels: u8,
15 pub colorspace: u8,
16}
17impl Header {
18 pub const SIZE: u64 = 14;
19 pub fn from_file(f: &mut File) -> Result<Self> {
20 f.seek(SeekFrom::Start(0))?;
21 let mut buf: [u8; 4] = [0u8; 4];
22 f.read(&mut buf)?;
23 assert_eq!(buf, [113u8, 111u8, 105u8, 102u8]);
24
25 f.read(&mut buf)?;
26 let width = u32::from_be_bytes(buf);
27
28 f.read(&mut buf)?;
29 let height = u32::from_be_bytes(buf);
30
31 f.read(&mut buf)?;
32 let channels = buf[0];
33 let colorspace = buf[1];
34
35 let mut buf: [u8; 8] = [0u8; 8];
36 f.seek(SeekFrom::End(-8))?;
37 f.read(&mut buf)?;
38 assert_eq!(buf, [0, 0, 0, 0, 0, 0, 0, 1]);
39
40 Ok(Self {
41 width,
42 height,
43 channels,
44 colorspace,
45 })
46 }
47}
48
49pub type Pix = (u8, u8, u8, u8);
50pub fn hash((r, g, b, a): (u8, u8, u8, u8)) -> usize {
51 let (r, g, b, a) = (
52 (r as usize) * 3,
53 (g as usize) * 5,
54 (b as usize) * 7,
55 (a as usize) * 11,
56 );
57 (r + g + b + a) % 64
58}
59
60const QOI_OP_RGB: u8 = 0b11111110;
61const QOI_OP_RGBA: u8 = 0b11111111;
62const QOI_OP_INDEX: u8 = 0b00000000;
63const QOI_OP_DIFF: u8 = 0b01000000;
64const QOI_OP_LUMA: u8 = 0b10000000;
65const QOI_OP_RUN: u8 = 0b11000000;
66
67pub enum Chunk {
68 RGB(u8, u8, u8),
69 RGBA(u8, u8, u8, u8),
70 INDEX(u8),
71 DIFF(u8, u8, u8),
72 LUMA(u8, u8, u8),
73 RUN(u8),
74}
75impl Chunk {
76 pub fn parse(&self, curr: Pix, index: &[Pix]) -> (Pix, u8) {
77 match *self {
78 RGB(r, g, b) => ((r, g, b, curr.3), 0),
79 RGBA(r, g, b, a) => ((r, g, b, a), 0),
80 INDEX(i) => (index[i as usize], 0),
81 DIFF(dr, dg, db) => ((
82 curr.0
83 .wrapping_add(dr)
84 .wrapping_sub(2),
85 curr.1
86 .wrapping_add(dg)
87 .wrapping_sub(2),
88 curr.2
89 .wrapping_add(db)
90 .wrapping_sub(2),
91 curr.3,
92 ), 0),
93 LUMA(dg, drdg, dbdg) => ((
94 curr.0
95 .wrapping_add(dg)
96 .wrapping_sub(40)
97 .wrapping_add(drdg),
98 curr.1
99 .wrapping_add(dg)
100 .wrapping_sub(32),
101 curr.2
102 .wrapping_add(dg)
103 .wrapping_sub(40)
104 .wrapping_add(dbdg),
105 curr.3,
106 ), 0),
107 RUN(len) => (curr, len),
108 }
109 }
110}
111
112use Chunk::*;
113
114pub struct ChunkIter<I: Iterator<Item=u8>> (I);
115
116impl<I> Iterator for ChunkIter<I>
117where
118 I: Iterator<Item = u8>
119{
120 type Item = Chunk;
121
122 fn next(&mut self) -> Option<Chunk> {
123 let Self(iter) = self;
124 let b1 = iter.next()?;
125 match b1 {
126 QOI_OP_RGB => Some(RGB(
127 iter.next()?,
128 iter.next()?,
129 iter.next()?,
130 )),
131 QOI_OP_RGBA => Some(RGBA(
132 iter.next()?,
133 iter.next()?,
134 iter.next()?,
135 iter.next()?,
136 )),
137 b1 => match b1 & 0b11000000 {
138 QOI_OP_INDEX => Some(INDEX(b1 & 0b00111111)),
139 QOI_OP_DIFF => Some(DIFF(b1 >> 4 & 3, b1 >> 2 & 3, b1 & 3)),
140 QOI_OP_LUMA => {
141 let b2 = iter.next()?;
142 Some(LUMA(
143 b1 & 0b00111111,
144 (b2 & 0b11110000) >> 4,
145 b2 & 0b00001111,
146 ))
147 },
148 QOI_OP_RUN => Some(RUN(b1 & 0b00111111)),
149 _ => None,
150 }
151 }
152 }
153}
154pub trait Chunks {
155 fn chunks(self) -> ChunkIter<Self>
156 where
157 Self: Sized + Iterator<Item = u8>;
158}
159impl<I> Chunks for I
160where
161 I: Iterator<Item = u8>
162{
163 fn chunks(self) -> ChunkIter<Self> {
164 ChunkIter(self)
165 }
166}