1pub fn xrgb8888_encode_qoi(bytes: &[u8], width: u32, height: u32, stride: u32) -> Vec<u8> {
2 const OP_RGB: u8 = 0b1111_1110;
3 const OP_INDEX: u8 = 0b0000_0000;
4 const OP_DIFF: u8 = 0b0100_0000;
5 const OP_LUMA: u8 = 0b1000_0000;
6 const OP_RUN: u8 = 0b1100_0000;
7
8 let mut res = vec![];
9 let width_bytes_be = width.to_be_bytes();
10 let height_bytes_be = height.to_be_bytes();
11 let header = [
12 b'q',
13 b'o',
14 b'i',
15 b'f',
16 width_bytes_be[0],
17 width_bytes_be[1],
18 width_bytes_be[2],
19 width_bytes_be[3],
20 height_bytes_be[0],
21 height_bytes_be[1],
22 height_bytes_be[2],
23 height_bytes_be[3],
24 3,
25 0,
26 ];
27 res.extend_from_slice(&header);
28 let mut prev_pixel = [0, 0, 0, 0xff];
29 let mut array = [[0; 4]; 64];
30 let mut run_length = 0;
31 for line in bytes.chunks_exact(stride as _) {
32 for &pixel in array_chunks::<_, 4>(&line[..(width * 4) as _]) {
33 let pixel = [pixel[2], pixel[1], pixel[0], 0xff];
34 if pixel == prev_pixel {
35 run_length += 1;
36 if run_length == 62 {
37 res.push(OP_RUN | (run_length - 1));
38 run_length = 0;
39 }
40 continue;
41 }
42 if run_length > 0 {
43 res.push(OP_RUN | (run_length - 1));
44 run_length = 0;
45 }
46 let prev = prev_pixel;
47 prev_pixel = pixel;
48 let index = {
49 let sum = 0u8
50 .wrapping_add(pixel[0].wrapping_mul(3))
51 .wrapping_add(pixel[1].wrapping_mul(5))
52 .wrapping_add(pixel[2].wrapping_mul(7))
53 .wrapping_add(255u8.wrapping_mul(11));
54 sum & 63
55 };
56 if array[index as usize] == pixel {
57 res.push(OP_INDEX | index);
58 continue;
59 }
60 array[index as usize] = pixel;
61 let dr = pixel[0].wrapping_sub(prev[0]);
62 let dg = pixel[1].wrapping_sub(prev[1]);
63 let db = pixel[2].wrapping_sub(prev[2]);
64 let dr_2 = dr.wrapping_add(2);
65 let dg_2 = dg.wrapping_add(2);
66 let db_2 = db.wrapping_add(2);
67 if dr_2 | dg_2 | db_2 | 3 == 3 {
68 res.push(OP_DIFF | (dr_2 << 4) | (dg_2 << 2) | db_2);
69 continue;
70 }
71 let dr_dg_8 = dr.wrapping_sub(dg).wrapping_add(8);
72 let db_dg_8 = db.wrapping_sub(dg).wrapping_add(8);
73 let dg_32 = dg.wrapping_add(32);
74 if (dg_32 | 63 == 63) && (dr_dg_8 | db_dg_8 | 15 == 15) {
75 res.extend_from_slice(&[OP_LUMA | dg_32, (dr_dg_8 << 4) | db_dg_8]);
76 continue;
77 }
78 res.extend_from_slice(&[OP_RGB, pixel[0], pixel[1], pixel[2]]);
79 }
80 }
81 if run_length > 0 {
82 res.push(OP_RUN | (run_length - 1));
83 }
84 res.extend_from_slice(&[0, 0, 0, 0, 0, 0, 0, 1]);
85 res
86}
87
88fn array_chunks<T, const N: usize>(slice: &[T]) -> &[[T; N]] {
89 let len = slice.len() / N;
90 unsafe { std::slice::from_raw_parts(slice.as_ptr() as _, len) }
91}