|
1 | 1 | use ironrdp_pdu::utils::SplitTo as _; |
| 2 | +use wide::i16x8; |
| 3 | + |
| 4 | +/// Max RFX sub-band width. The 8-wide tiling and the `[_; MAX_SUBBAND_WIDTH + 1]` scratch in the |
| 5 | +/// inverse passes rely on `subband_width` being one of {8, 16, 32}. |
| 6 | +const MAX_SUBBAND_WIDTH: usize = 32; |
| 7 | + |
| 8 | +/// Loads 8 contiguous `i16` from `s[off..]` into a vector. The caller guarantees `off + 8 <= s.len()`. |
| 9 | +#[inline] |
| 10 | +fn vld(s: &[i16], off: usize) -> i16x8 { |
| 11 | + i16x8::from_slice_unaligned(&s[off..][..8]) |
| 12 | +} |
| 13 | + |
| 14 | +/// Stores a vector into `s[off..]`. |
| 15 | +#[inline] |
| 16 | +fn vst(s: &mut [i16], off: usize, v: i16x8) { |
| 17 | + s[off..][..8].copy_from_slice(v.as_array_ref()); |
| 18 | +} |
| 19 | + |
| 20 | +/// Ceil average `(a + b + 1) >> 1`, overflow-free (SWAR; arithmetic shift). |
| 21 | +#[inline] |
| 22 | +fn ceil_avg(a: i16x8, b: i16x8) -> i16x8 { |
| 23 | + (a | b) - ((a ^ b) >> 1) |
| 24 | +} |
| 25 | + |
| 26 | +/// Floor average `(a + b) >> 1`, overflow-free (SWAR; arithmetic shift). |
| 27 | +#[inline] |
| 28 | +fn floor_avg(a: i16x8, b: i16x8) -> i16x8 { |
| 29 | + (a & b) + ((a ^ b) >> 1) |
| 30 | +} |
2 | 31 |
|
3 | 32 | pub fn encode(buffer: &mut [i16], temp_buffer: &mut [i16]) { |
4 | 33 | encode_block::<32>(&mut *buffer, temp_buffer); |
@@ -109,104 +138,123 @@ fn dwt_horizontal<const SUBBAND_WIDTH: usize>(mut buffer: &mut [i16], dwt: &[i16 |
109 | 138 | } |
110 | 139 |
|
111 | 140 | pub fn decode(buffer: &mut [i16], temp_buffer: &mut [i16]) { |
112 | | - decode_block(&mut buffer[3840..], temp_buffer, 8); |
113 | | - decode_block(&mut buffer[3072..], temp_buffer, 16); |
114 | | - decode_block(&mut *buffer, temp_buffer, 32); |
| 141 | + decode_block::<8>(&mut buffer[3840..], temp_buffer); |
| 142 | + decode_block::<16>(&mut buffer[3072..], temp_buffer); |
| 143 | + decode_block::<32>(&mut *buffer, temp_buffer); |
115 | 144 | } |
116 | 145 |
|
117 | | -fn decode_block(buffer: &mut [i16], temp_buffer: &mut [i16], subband_width: usize) { |
118 | | - inverse_horizontal(buffer, temp_buffer, subband_width); |
119 | | - inverse_vertical(buffer, temp_buffer, subband_width); |
| 146 | +fn decode_block<const SUBBAND_WIDTH: usize>(buffer: &mut [i16], temp_buffer: &mut [i16]) { |
| 147 | + inverse_horizontal::<SUBBAND_WIDTH>(buffer, temp_buffer); |
| 148 | + inverse_vertical::<SUBBAND_WIDTH>(buffer, temp_buffer); |
120 | 149 | } |
121 | 150 |
|
122 | | -// Inverse DWT in horizontal direction, results in 2 sub-bands in L, H order in output buffer |
123 | | -// The 4 sub-bands are stored in HL(0), LH(1), HH(2), LL(3) order. |
124 | | -// The lower part L uses LL(3) and HL(0). |
125 | | -// The higher part H uses LH(1) and HH(2). |
126 | | -fn inverse_horizontal(mut buffer: &[i16], temp_buffer: &mut [i16], subband_width: usize) { |
127 | | - let total_width = subband_width * 2; |
128 | | - let squared_subband_width = subband_width.pow(2); |
129 | | - |
130 | | - let mut hl = buffer.split_to(squared_subband_width); |
131 | | - let mut lh = buffer.split_to(squared_subband_width); |
132 | | - let mut hh = buffer.split_to(squared_subband_width); |
133 | | - let mut ll = buffer; |
134 | | - |
135 | | - let (mut l_dst, mut h_dst) = temp_buffer.split_at_mut(squared_subband_width * 2); |
136 | | - |
137 | | - for _ in 0..subband_width { |
138 | | - // Even coefficients |
139 | | - l_dst[0] = i32_to_i16_possible_truncation(i32::from(ll[0]) - ((i32::from(hl[0]) + i32::from(hl[0]) + 1) >> 1)); |
140 | | - h_dst[0] = i32_to_i16_possible_truncation(i32::from(lh[0]) - ((i32::from(hh[0]) + i32::from(hh[0]) + 1) >> 1)); |
141 | | - for n in 1..subband_width { |
142 | | - let x = n * 2; |
143 | | - l_dst[x] = |
144 | | - i32_to_i16_possible_truncation(i32::from(ll[n]) - ((i32::from(hl[n - 1]) + i32::from(hl[n]) + 1) >> 1)); |
145 | | - h_dst[x] = |
146 | | - i32_to_i16_possible_truncation(i32::from(lh[n]) - ((i32::from(hh[n - 1]) + i32::from(hh[n]) + 1) >> 1)); |
147 | | - } |
148 | | - |
149 | | - // Odd coefficients |
150 | | - for n in 0..subband_width - 1 { |
151 | | - let x = n * 2; |
152 | | - l_dst[x + 1] = i32_to_i16_possible_truncation( |
153 | | - i32::from(hl[n] << 1) + ((i32::from(l_dst[x]) + i32::from(l_dst[x + 2])) >> 1), |
154 | | - ); |
155 | | - h_dst[x + 1] = i32_to_i16_possible_truncation( |
156 | | - i32::from(hh[n] << 1) + ((i32::from(h_dst[x]) + i32::from(h_dst[x + 2])) >> 1), |
157 | | - ); |
158 | | - } |
159 | | - let n = subband_width - 1; |
160 | | - let x = n * 2; |
161 | | - l_dst[x + 1] = i32_to_i16_possible_truncation(i32::from(hl[n] << 1) + i32::from(l_dst[x])); |
162 | | - h_dst[x + 1] = i32_to_i16_possible_truncation(i32::from(hh[n] << 1) + i32::from(h_dst[x])); |
163 | | - |
164 | | - hl = &hl[subband_width..]; |
165 | | - lh = &lh[subband_width..]; |
166 | | - hh = &hh[subband_width..]; |
167 | | - ll = &ll[subband_width..]; |
168 | | - |
169 | | - l_dst = &mut l_dst[total_width..]; |
170 | | - h_dst = &mut h_dst[total_width..]; |
| 151 | +// Inverse DWT horizontal pass (portable `wide`). The 4 sub-bands are stored HL(0), LH(1), HH(2), |
| 152 | +// LL(3); the L band reconstructs from LL+HL, the H band from LH+HH. Each row is reconstructed by |
| 153 | +// `horizontal_band`. |
| 154 | +fn inverse_horizontal<const SUBBAND_WIDTH: usize>(buffer: &[i16], temp_buffer: &mut [i16]) { |
| 155 | + let sw = SUBBAND_WIDTH; |
| 156 | + let tw = sw * 2; |
| 157 | + let ssw = sw * sw; |
| 158 | + let (hl, rest) = buffer.split_at(ssw); |
| 159 | + let (lh, rest) = rest.split_at(ssw); |
| 160 | + let (hh, ll) = rest.split_at(ssw); |
| 161 | + let (l_dst, h_dst) = temp_buffer.split_at_mut(ssw * 2); |
| 162 | + |
| 163 | + for r in 0..sw { |
| 164 | + let row = r * sw; |
| 165 | + horizontal_band::<SUBBAND_WIDTH>(&ll[row..][..sw], &hl[row..][..sw], &mut l_dst[r * tw..][..tw]); |
| 166 | + horizontal_band::<SUBBAND_WIDTH>(&lh[row..][..sw], &hh[row..][..sw], &mut h_dst[r * tw..][..tw]); |
171 | 167 | } |
172 | 168 | } |
173 | 169 |
|
174 | | -fn inverse_vertical(mut buffer: &mut [i16], mut temp_buffer: &[i16], subband_width: usize) { |
175 | | - let total_width = subband_width * 2; |
| 170 | +// One band of the inverse horizontal pass, vectorized along `n` (`wide`): `low`/`high` are the two |
| 171 | +// source subband rows (len `sw`), `dst` the reconstructed row (len `2*sw`, even/odd interleaved). |
| 172 | +// Bit-exact with the former scalar code (SWAR averages + wrapping `i16` arithmetic). `sw` is a |
| 173 | +// multiple of 8 and ≤ 32. |
| 174 | +fn horizontal_band<const SUBBAND_WIDTH: usize>(low: &[i16], high: &[i16], dst: &mut [i16]) { |
| 175 | + const { |
| 176 | + assert!( |
| 177 | + SUBBAND_WIDTH == 8 || SUBBAND_WIDTH == 16 || SUBBAND_WIDTH == 32, |
| 178 | + "subband width must be one of 8, 16, or 32" |
| 179 | + ) |
| 180 | + }; |
| 181 | + let sw = SUBBAND_WIDTH; |
| 182 | + |
| 183 | + // Left-shifted copy so `high_pad[n] == high[n-1]` (and `high[0]` for n == 0): lets the even |
| 184 | + // pass load the left neighbour contiguously instead of shuffling. |
| 185 | + let mut high_pad = [0i16; MAX_SUBBAND_WIDTH + 1]; |
| 186 | + high_pad[0] = high[0]; |
| 187 | + high_pad[1..sw].copy_from_slice(&high[0..sw - 1]); |
| 188 | + |
| 189 | + // `ev`/`od` padded so the odd pass can read `ev[n+1]` at n = sw-1 in bounds. |
| 190 | + let mut ev = [0i16; MAX_SUBBAND_WIDTH + 1]; |
| 191 | + let mut od = [0i16; MAX_SUBBAND_WIDTH + 1]; |
| 192 | + |
| 193 | + // EVEN: ev[n] = low[n] - ceil_avg(high[n-1], high[n]). |
| 194 | + let mut n = 0; |
| 195 | + while n < sw { |
| 196 | + vst(&mut ev, n, vld(low, n) - ceil_avg(vld(&high_pad, n), vld(high, n))); |
| 197 | + n += 8; |
| 198 | + } |
176 | 199 |
|
177 | | - for _ in 0..total_width { |
178 | | - buffer[0] = i32_to_i16_possible_truncation( |
179 | | - i32::from(temp_buffer[0]) - ((i32::from(temp_buffer[subband_width * total_width]) * 2 + 1) >> 1), |
| 200 | + // ODD: od[n] = (high[n] << 1) + floor_avg(ev[n], ev[n+1]). |
| 201 | + let mut n = 0; |
| 202 | + while n < sw { |
| 203 | + vst( |
| 204 | + &mut od, |
| 205 | + n, |
| 206 | + (vld(high, n) << 1) + floor_avg(vld(&ev, n), vld(&ev, n + 1)), |
180 | 207 | ); |
| 208 | + n += 8; |
| 209 | + } |
| 210 | + // n = sw-1 has no right neighbour. |
| 211 | + od[sw - 1] = i32_to_i16_possible_truncation((i32::from(high[sw - 1]) << 1) + i32::from(ev[sw - 1])); |
181 | 212 |
|
182 | | - let mut l = temp_buffer; |
183 | | - let mut lh = &temp_buffer[(subband_width - 1) * total_width..]; |
184 | | - let mut h = &temp_buffer[subband_width * total_width..]; |
185 | | - let mut dst = &mut *buffer; |
186 | | - |
187 | | - for _ in 1..subband_width { |
188 | | - l = &l[total_width..]; |
189 | | - lh = &lh[total_width..]; |
190 | | - h = &h[total_width..]; |
191 | | - |
192 | | - // Even coefficients |
193 | | - dst[2 * total_width] = |
194 | | - i32_to_i16_possible_truncation(i32::from(l[0]) - ((i32::from(lh[0]) + i32::from(h[0]) + 1) >> 1)); |
195 | | - |
196 | | - // Odd coefficients |
197 | | - dst[total_width] = i32_to_i16_possible_truncation( |
198 | | - i32::from(lh[0] << 1) + ((i32::from(dst[0]) + i32::from(dst[2 * total_width])) >> 1), |
199 | | - ); |
| 213 | + // INTERLEAVE: dst[2n] = ev[n], dst[2n+1] = od[n]. |
| 214 | + for n in 0..sw { |
| 215 | + dst[2 * n] = ev[n]; |
| 216 | + dst[2 * n + 1] = od[n]; |
| 217 | + } |
| 218 | +} |
200 | 219 |
|
201 | | - dst = &mut dst[2 * total_width..]; |
| 220 | +// Inverse DWT vertical pass, vectorized over 8 contiguous columns per step (portable `wide`). |
| 221 | +// Bit-exact with the former scalar code: `(2*x+1)>>1 == x` and `(x+x)>>1 == x` simplify the |
| 222 | +// first/last rows, the averages use the overflow-free SWAR `ceil_avg`/`floor_avg`, and every other |
| 223 | +// op is wrapping `i16` arithmetic (identical to i32-intermediate-then-truncate). |
| 224 | +// Precondition: `subband_width` is a multiple of 8 and <= MAX_SUBBAND_WIDTH (for the 8-wide tiling). |
| 225 | +fn inverse_vertical<const SUBBAND_WIDTH: usize>(buffer: &mut [i16], temp_buffer: &[i16]) { |
| 226 | + const { |
| 227 | + assert!( |
| 228 | + SUBBAND_WIDTH == 8 || SUBBAND_WIDTH == 16 || SUBBAND_WIDTH == 32, |
| 229 | + "subband width must be one of 8, 16, or 32" |
| 230 | + ) |
| 231 | + }; |
| 232 | + let sw = SUBBAND_WIDTH; |
| 233 | + let tw = sw * 2; |
| 234 | + |
| 235 | + let mut cb = 0; |
| 236 | + while cb < tw { |
| 237 | + // Row 0: L0 - ((H0*2 + 1) >> 1) == L0 - H0. |
| 238 | + vst(buffer, cb, vld(temp_buffer, cb) - vld(temp_buffer, cb + sw * tw)); |
| 239 | + |
| 240 | + for k in 1..sw { |
| 241 | + let l = vld(temp_buffer, cb + k * tw); |
| 242 | + let h = vld(temp_buffer, cb + (sw + k) * tw); |
| 243 | + let lh = vld(temp_buffer, cb + (sw - 1 + k) * tw); |
| 244 | + |
| 245 | + let even = l - ceil_avg(lh, h); |
| 246 | + vst(buffer, cb + k * 2 * tw, even); |
| 247 | + |
| 248 | + let d0 = vld(buffer, cb + (k - 1) * 2 * tw); |
| 249 | + vst(buffer, cb + (2 * k - 1) * tw, (lh << 1) + floor_avg(d0, even)); |
202 | 250 | } |
203 | 251 |
|
204 | | - dst[total_width] = i32_to_i16_possible_truncation( |
205 | | - i32::from(lh[total_width] << 1) + ((i32::from(dst[0]) + i32::from(dst[0])) >> 1), |
206 | | - ); |
| 252 | + // Final odd row: (lhN << 1) + ((d0 + d0) >> 1) == (lhN << 1) + d0. |
| 253 | + let lhn = vld(temp_buffer, cb + (2 * sw - 1) * tw); |
| 254 | + let dl = vld(buffer, cb + (2 * sw - 2) * tw); |
| 255 | + vst(buffer, cb + (2 * sw - 1) * tw, (lhn << 1) + dl); |
207 | 256 |
|
208 | | - temp_buffer = &temp_buffer[1..]; |
209 | | - buffer = &mut buffer[1..]; |
| 257 | + cb += 8; |
210 | 258 | } |
211 | 259 | } |
212 | 260 |
|
|
0 commit comments