jay_algorithms/
qoi.rs

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}