q565/decode/
streaming_no_header.rs1use crate::{
2 decode::ops::{direct_bigger_diff, direct_small_diff, indexed_diff},
3 utils::hash,
4};
5use byteorder::{ByteOrder, NativeEndian};
6use core::hint::unreachable_unchecked;
7
8#[repr(C)]
9#[derive(Debug, Clone, Copy)]
10pub struct Q565StreamingDecodeContext {
11 state: Q565StreamingDecodeState,
12 prev: u16,
13 arr: [u16; 64],
14}
15
16#[repr(u8)]
17#[derive(Debug, Clone, Copy)]
18enum Q565StreamingDecodeState {
19 Default = 0,
20 LumaOrDiffIndexedByte2(u8),
21 RawRgb565Byte1,
22 RawRgb565Byte2(u8),
23}
24
25impl Default for Q565StreamingDecodeContext {
26 fn default() -> Self {
27 Self::new()
28 }
29}
30
31impl Q565StreamingDecodeContext {
32 pub const fn new() -> Self {
33 Self {
34 state: Q565StreamingDecodeState::Default,
35 prev: 0,
36 arr: [0; 64],
37 }
38 }
39
40 pub unsafe fn streaming_decode_to_slice_unchecked<B: ByteOrder>(
53 &mut self,
54 input: &[u8],
55 output: &mut [u16],
56 ) -> usize {
57 let mut output_idx = 0;
58 let mut input_idx = 0;
59
60 macro_rules! next {
61 () => {
62 if let Some(&b) = input.get(input_idx) {
63 input_idx += 1;
64 b
65 } else {
66 return output_idx;
67 }
68 };
69 }
70
71 unsafe fn set_pixel<B: ByteOrder>(
72 state: &mut Q565StreamingDecodeContext,
73 pixel: u16,
74 output: &mut [u16],
75 output_idx: &mut usize,
76 ) {
77 state.prev = pixel;
78
79 let mut buf = [0u8; 2];
80 NativeEndian::write_u16(&mut buf, pixel);
81
82 *output.get_unchecked_mut(*output_idx) = B::read_u16(&buf);
83 *output_idx += 1;
84 }
85
86 loop {
87 let byte = next!();
88 let pixel = match self.state {
89 Q565StreamingDecodeState::Default => {
90 let op = byte >> 6;
91
92 match op {
93 0b00 => {
94 let pixel = *self.arr.get_unchecked(usize::from(byte));
95 set_pixel::<B>(self, pixel, output, &mut output_idx);
96 continue;
97 }
98 0b01 => {
99 let pixel = direct_small_diff(self.prev, byte);
100 set_pixel::<B>(self, pixel, output, &mut output_idx);
101
102 continue;
103 }
104 0b10 => {
105 self.state = Q565StreamingDecodeState::LumaOrDiffIndexedByte2(byte);
106 continue;
107 }
108 0b11 => {
109 if byte == 0xFE {
110 self.state = Q565StreamingDecodeState::RawRgb565Byte1;
111 continue;
112 } else if byte != 0xFF {
113 let count = (byte & 0b0011_1111) + 1;
114 let count = usize::from(count);
115
116 let mut buf = [0u8; 2];
117 NativeEndian::write_u16(&mut buf, self.prev);
118
119 output
120 .get_unchecked_mut(output_idx..)
121 .get_unchecked_mut(..count)
122 .fill(B::read_u16(&buf));
123 output_idx += count;
124
125 continue;
126 } else {
127 return output_idx;
128 }
129 }
130 _ => unsafe { unreachable_unchecked() },
131 }
132 }
133 Q565StreamingDecodeState::LumaOrDiffIndexedByte2(byte1) => {
134 let op = byte1 >> 5;
135 match op {
136 0b100 => direct_bigger_diff(self.prev, byte1, byte),
137 0b101 => indexed_diff(&self.arr, byte1, byte),
138 _ => unsafe { unreachable_unchecked() },
139 }
140 }
141 Q565StreamingDecodeState::RawRgb565Byte1 => {
142 self.state = Q565StreamingDecodeState::RawRgb565Byte2(byte);
143 continue;
144 }
145 Q565StreamingDecodeState::RawRgb565Byte2(byte1) => {
146 u16::from_le_bytes([byte1, byte])
147 }
148 };
149
150 let index = hash(pixel);
151 *self.arr.get_unchecked_mut(usize::from(index)) = pixel;
152 set_pixel::<B>(self, pixel, output, &mut output_idx);
153 self.state = Q565StreamingDecodeState::Default;
154 }
155 }
156}