1use base64::{
2 Engine as _,
3 engine::general_purpose::STANDARD,
4};
5
6pub fn encode_kitty(id: u32, data: &[u8], _mime_type: &str) -> String {
11 let b64 = STANDARD.encode(data);
12 let mut seq = format!("\x1b_Ga=T,f=100,i={},m=1;", id);
13 const CHUNK_SIZE: usize = 4096;
14 for chunk in b64.as_bytes().chunks(CHUNK_SIZE) {
15 seq.push_str(std::str::from_utf8(chunk).unwrap());
16 seq.push_str("\x1b\\");
17 }
18 seq
19}
20
21pub fn encode_iterm2(data: &[u8], _mime_type: &str) -> String {
23 let b64 = STANDARD.encode(data);
24 format!("\x1b]1337;File=inline=1:{}\x07", b64)
25}
26
27pub fn delete_kitty_image(id: u32) -> String {
29 format!("\x1b_Ga=d,d=I,i={}\x1b\\", id)
30}
31
32pub fn get_png_dimensions(data: &[u8]) -> Option<(u32, u32)> {
36 if data.len() < 24 || &data[0..8] != b"\x89PNG\r\n\x1a\n" {
37 return None;
38 }
39 let width = u32::from_be_bytes([data[16], data[17], data[18], data[19]]);
40 let height = u32::from_be_bytes([data[20], data[21], data[22], data[23]]);
41 Some((width, height))
42}
43
44pub fn get_jpeg_dimensions(data: &[u8]) -> Option<(u32, u32)> {
48 let mut i = 2;
49 while i < data.len().saturating_sub(9) {
50 if data[i] == 0xff && (data[i + 1] == 0xc0 || data[i + 1] == 0xc2) {
51 let h = u16::from_be_bytes([data[i + 5], data[i + 6]]) as u32;
52 let w = u16::from_be_bytes([data[i + 7], data[i + 8]]) as u32;
53 return Some((w, h));
54 }
55 i += 1;
56 }
57 None
58}
59
60pub fn get_gif_dimensions(data: &[u8]) -> Option<(u32, u32)> {
62 if data.len() < 10 {
63 return None;
64 }
65 let w = u16::from_le_bytes([data[6], data[7]]) as u32;
66 let h = u16::from_le_bytes([data[8], data[9]]) as u32;
67 Some((w, h))
68}
69
70pub fn get_webp_dimensions(data: &[u8]) -> Option<(u32, u32)> {
75 if data.len() < 30 || &data[0..4] != b"RIFF" || &data[8..12] != b"WEBP" {
76 return None;
77 }
78 if &data[12..16] == b"VP8X" {
79 let w = u32::from_le_bytes([data[24], data[25], data[26], 0]) + 1;
80 let h = u32::from_le_bytes([data[27], data[28], data[29], 0]) + 1;
81 return Some((w, h));
82 }
83 None
84}
85
86#[cfg(test)]
87mod tests {
88 use super::*;
89
90 #[test]
91 fn jpeg_dimensions_valid() {
92 let mut data = vec![0xff, 0xd8];
93 data.extend_from_slice(&[0xff, 0xc0]);
94 data.extend_from_slice(&[0x00, 0x0b]);
95 data.extend_from_slice(&[0x08]);
96 data.extend_from_slice(&[0x00, 0x10]);
97 data.extend_from_slice(&[0x00, 0x20]);
98 data.extend_from_slice(&[0x01, 0x01, 0x11, 0x00]);
99 assert_eq!(get_jpeg_dimensions(&data), Some((32, 16)));
100 }
101
102 #[test]
103 fn jpeg_dimensions_sof2() {
104 let mut data = vec![0xff, 0xd8];
105 data.extend_from_slice(&[0xff, 0xc2]);
106 data.extend_from_slice(&[0x00, 0x0b]);
107 data.extend_from_slice(&[0x08]);
108 data.extend_from_slice(&[0x00, 0x20]);
109 data.extend_from_slice(&[0x00, 0x10]);
110 data.extend_from_slice(&[0x01, 0x01, 0x11, 0x00]);
111 assert_eq!(get_jpeg_dimensions(&data), Some((16, 32)));
112 }
113}