1use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
2use flate2::{read::ZlibDecoder, write::ZlibEncoder, Compression};
3use std::io::{Read, Write};
4
5use crate::error::GlyphError;
6
7const MAGIC: [u8; 4] = *b"GLYF";
8const VERSION: u32 = 1;
9
10const COMPRESSION_LEVEL: Compression = Compression::fast();
12const INITIAL_BUFFER_SIZE: usize = 8192;
13const CHANGE_BUFFER_CAPACITY: usize = 2048;
14const LINE_BUFFER_CAPACITY: usize = 256;
15
16#[derive(Debug, Clone)]
17pub struct Frame {
18 pub content: String,
19 pub duration_ms: Option<u32>,
20}
21
22impl Frame {
23 pub fn new(content: String) -> Self {
24 Self {
25 content,
26 duration_ms: None,
27 }
28 }
29
30 pub fn with_duration(content: String, duration_ms: u32) -> Self {
31 Self {
32 content,
33 duration_ms: Some(duration_ms),
34 }
35 }
36
37 fn validate_dimensions(&self, width: u32, height: u32) -> Result<(), GlyphError> {
39 let bytes = self.content.as_bytes();
40 let mut line_count = 0;
41 let mut pos = 0;
42 let mut line_width = 0;
43
44 while pos < bytes.len() {
45 match bytes[pos] {
46 b'\n' => {
47 if line_width as u32 != width {
48 return Err(GlyphError::InvalidDimensions {
49 width: line_width as u32,
50 height,
51 });
52 }
53 line_count += 1;
54 line_width = 0;
55 }
56 _ => line_width += 1,
57 }
58 pos += 1;
59 }
60
61 if line_width > 0 {
63 if line_width as u32 != width {
64 return Err(GlyphError::InvalidDimensions {
65 width: line_width as u32,
66 height,
67 });
68 }
69 line_count += 1;
70 }
71
72 if line_count as u32 != height {
73 return Err(GlyphError::InvalidDimensions {
74 width,
75 height: line_count as u32,
76 });
77 }
78
79 Ok(())
80 }
81}
82
83#[derive(Debug)]
84pub struct Glyph {
85 pub width: u32,
86 pub height: u32,
87 pub default_duration_ms: u32,
88 pub frames: Vec<Frame>,
89}
90
91impl Glyph {
92 pub fn new(width: u32, height: u32, default_duration_ms: u32) -> Self {
93 Self {
94 width,
95 height,
96 default_duration_ms,
97 frames: Vec::with_capacity(LINE_BUFFER_CAPACITY),
98 }
99 }
100
101 pub fn add_frame(&mut self, frame: Frame) -> Result<(), GlyphError> {
102 frame.validate_dimensions(self.width, self.height)?;
103 self.frames.push(frame);
104 Ok(())
105 }
106
107 pub fn frames(&self) -> &[Frame] {
108 &self.frames
109 }
110
111 pub fn write<W: Write>(&self, writer: &mut W) -> Result<(), GlyphError> {
112 writer.write_all(&MAGIC)?;
114 writer.write_u32::<LittleEndian>(VERSION)?;
115 writer.write_u32::<LittleEndian>(self.frames.len() as u32)?;
116 writer.write_u32::<LittleEndian>(self.width)?;
117 writer.write_u32::<LittleEndian>(self.height)?;
118 writer.write_u32::<LittleEndian>(self.default_duration_ms)?;
119 writer.write_all(&[0u8; 16])?; let mut changes = Vec::with_capacity(CHANGE_BUFFER_CAPACITY);
123 let mut changed_chars = Vec::with_capacity(CHANGE_BUFFER_CAPACITY);
124 let mut compressed_buffer = Vec::with_capacity(INITIAL_BUFFER_SIZE);
125 let mut utf8_buf = [0u8; 4];
126
127 for (i, frame) in self.frames.iter().enumerate() {
129 writer.write_u8(frame.duration_ms.is_some() as u8)?;
131 if let Some(duration) = frame.duration_ms {
132 writer.write_u32::<LittleEndian>(duration)?;
133 }
134
135 if i == 0 {
136 let mut encoder = ZlibEncoder::new(&mut compressed_buffer, COMPRESSION_LEVEL);
138 encoder.write_all(frame.content.as_bytes())?;
139 encoder.finish()?;
140
141 writer.write_u32::<LittleEndian>(compressed_buffer.len() as u32)?;
142 writer.write_all(&compressed_buffer)?;
143 compressed_buffer.clear();
144 } else {
145 let prev_frame = &self.frames[i - 1];
147 changes.clear();
148 changed_chars.clear();
149
150 let prev_bytes = prev_frame.content.as_bytes();
152 let curr_bytes = frame.content.as_bytes();
153 let mut pos = 0;
154 let mut byte_pos = 0;
155
156 while byte_pos < curr_bytes.len() {
157 if byte_pos >= prev_bytes.len() || curr_bytes[byte_pos] != prev_bytes[byte_pos]
158 {
159 let mut char_len = 1;
161 while byte_pos + char_len < curr_bytes.len()
162 && (curr_bytes[byte_pos + char_len] & 0xC0) == 0x80
163 {
164 char_len += 1;
165 }
166 if let Some(c) =
167 std::str::from_utf8(&curr_bytes[byte_pos..byte_pos + char_len])
168 .ok()
169 .and_then(|s| s.chars().next())
170 {
171 changes.push(pos as u32);
172 changed_chars.push(c);
173 }
174 }
175
176 if byte_pos < prev_bytes.len() {
178 let mut len = 1;
179 while byte_pos + len < prev_bytes.len()
180 && (prev_bytes[byte_pos + len] & 0xC0) == 0x80
181 {
182 len += 1;
183 }
184 byte_pos += len;
185 } else {
186 byte_pos += 1;
187 }
188 pos += 1;
189 }
190
191 let mut encoder = ZlibEncoder::new(&mut compressed_buffer, COMPRESSION_LEVEL);
193 for &c in &changed_chars {
194 let len = c.encode_utf8(&mut utf8_buf).len();
195 encoder.write_all(&utf8_buf[..len])?;
196 }
197 encoder.finish()?;
198
199 writer.write_u32::<LittleEndian>(compressed_buffer.len() as u32)?;
201 writer.write_u32::<LittleEndian>(changes.len() as u32)?;
202 writer.write_all(&compressed_buffer)?;
203 compressed_buffer.clear();
204
205 for chunk in changes.chunks(1024) {
207 for &pos in chunk {
208 writer.write_u32::<LittleEndian>(pos)?;
209 }
210 }
211 }
212 }
213
214 Ok(())
215 }
216
217 pub fn read<R: Read>(reader: &mut R) -> Result<Self, GlyphError> {
218 let mut magic = [0u8; 4];
220 let mut reserved = [0u8; 16];
221 let mut u32_buf = [0u8; 4];
222
223 reader.read_exact(&mut magic)?;
225 if magic != MAGIC {
226 return Err(GlyphError::InvalidMagic);
227 }
228
229 reader.read_exact(&mut u32_buf)?;
231 let version = u32::from_le_bytes(u32_buf);
232 if version != VERSION {
233 return Err(GlyphError::InvalidVersion);
234 }
235
236 reader.read_exact(&mut u32_buf)?;
237 let frame_count = u32::from_le_bytes(u32_buf);
238 reader.read_exact(&mut u32_buf)?;
239 let width = u32::from_le_bytes(u32_buf);
240 reader.read_exact(&mut u32_buf)?;
241 let height = u32::from_le_bytes(u32_buf);
242 reader.read_exact(&mut u32_buf)?;
243 let default_duration_ms = u32::from_le_bytes(u32_buf);
244
245 reader.read_exact(&mut reserved)?;
247
248 let frame_size = (width * height) as usize;
250 let mut glyph = Glyph::new(width, height, default_duration_ms);
251 let mut compressed_buffer = Vec::with_capacity(frame_size * 2);
252 let mut content_buffer = String::with_capacity(frame_size);
253 let mut prev_content = String::with_capacity(frame_size);
254 let mut char_buf = Vec::with_capacity(frame_size);
255
256 for i in 0..frame_count {
258 let has_duration = reader.read_u8()? != 0;
260 let duration = if has_duration {
261 reader.read_exact(&mut u32_buf)?;
262 Some(u32::from_le_bytes(u32_buf))
263 } else {
264 None
265 };
266
267 content_buffer.clear();
268 if i == 0 {
269 reader.read_exact(&mut u32_buf)?;
271 let compressed_len = u32::from_le_bytes(u32_buf) as usize;
272 compressed_buffer.resize(compressed_len, 0);
273 reader.read_exact(&mut compressed_buffer)?;
274
275 let mut decoder = ZlibDecoder::new(&compressed_buffer[..]);
276 decoder
277 .read_to_string(&mut content_buffer)
278 .map_err(|e| GlyphError::DecompressionError(e.to_string()))?;
279
280 prev_content.clear();
281 prev_content.push_str(&content_buffer);
282 } else {
283 reader.read_exact(&mut u32_buf)?;
285 let compressed_len = u32::from_le_bytes(u32_buf) as usize;
286 reader.read_exact(&mut u32_buf)?;
287 let change_count = u32::from_le_bytes(u32_buf) as usize;
288
289 compressed_buffer.resize(compressed_len, 0);
291 reader.read_exact(&mut compressed_buffer)?;
292
293 let mut decoder = ZlibDecoder::new(&compressed_buffer[..]);
294 char_buf.clear();
295 decoder
296 .read_to_end(&mut char_buf)
297 .map_err(|e| GlyphError::DecompressionError(e.to_string()))?;
298
299 let changed_chars = std::str::from_utf8(&char_buf)
300 .map_err(|e| GlyphError::DecompressionError(e.to_string()))?;
301
302 if changed_chars.chars().count() != change_count {
303 return Err(GlyphError::FrameDataError(
304 "Mismatch in change count".to_string(),
305 ));
306 }
307
308 content_buffer.push_str(&prev_content);
310 let mut content_chars: Vec<char> = content_buffer.chars().collect();
311
312 let changed_chars_iter = changed_chars.chars();
314 let mut positions = Vec::with_capacity(change_count);
315 for _ in 0..change_count {
316 reader.read_exact(&mut u32_buf)?;
317 positions.push(u32::from_le_bytes(u32_buf) as usize);
318 }
319
320 for (pos, new_char) in positions.into_iter().zip(changed_chars_iter) {
321 if pos >= content_chars.len() {
322 return Err(GlyphError::FrameDataError(
323 "Invalid change position".to_string(),
324 ));
325 }
326 content_chars[pos] = new_char;
327 }
328
329 content_buffer.clear();
330 content_buffer.extend(content_chars);
331 prev_content.clear();
332 prev_content.push_str(&content_buffer);
333 }
334
335 let frame = Frame {
336 content: content_buffer.clone(),
337 duration_ms: duration,
338 };
339
340 glyph.add_frame(frame)?;
341 }
342
343 Ok(glyph)
344 }
345}