/src/grok/mercury/src/encode/band_coder.rs
Line | Count | Source |
1 | | use std::sync::Arc; |
2 | | |
3 | | use crate::capi::MercuryT1EncodeFn; |
4 | | use crate::ffi_dwt::{mercury_hwy_quantize_97, mercury_rate_convex_hull}; |
5 | | |
6 | | use super::stripe_queue::{StripeJob, StripeQueue, StripeResults}; |
7 | | |
8 | | // classic's maxCodePassesPerBlock |
9 | | const MAX_CODING_PASSES: usize = 94; |
10 | | |
11 | | #[derive(Default, Clone, Copy)] |
12 | | pub(crate) struct CodingStats { |
13 | | pub coded_bytes: u64, |
14 | | pub coding_passes: u64, |
15 | | pub blocks: u64, |
16 | | } |
17 | | |
18 | | impl CodingStats { |
19 | 0 | pub fn add(&mut self, other: CodingStats) { |
20 | 0 | self.coded_bytes += other.coded_bytes; |
21 | 0 | self.coding_passes += other.coding_passes; |
22 | 0 | self.blocks += other.blocks; |
23 | 0 | } |
24 | | } |
25 | | |
26 | | #[derive(Clone, Copy)] |
27 | | pub(crate) struct CodedPass { |
28 | | // cumulative bytes through this pass |
29 | | pub rate: u16, |
30 | | // 0 when the pass is off the convex hull |
31 | | pub slope: u16, |
32 | | } |
33 | | |
34 | | pub(crate) struct CodedBlock { |
35 | | pub numbps: u8, |
36 | | pub first_pass: u32, |
37 | | pub passes: u8, |
38 | | pub first_byte: u32, |
39 | | } |
40 | | |
41 | | // a band's code-blocks in raster order, with their passes and bytes packed end to end |
42 | | #[derive(Default)] |
43 | | pub(crate) struct CodedBand { |
44 | | pub blocks: Vec<CodedBlock>, |
45 | | pub passes: Vec<CodedPass>, |
46 | | pub bytes: Vec<u8>, |
47 | | } |
48 | | |
49 | | impl CodedBand { |
50 | 0 | pub fn block_passes(&self, block: &CodedBlock) -> &[CodedPass] { |
51 | 0 | let first = block.first_pass as usize; |
52 | 0 | &self.passes[first..first + block.passes as usize] |
53 | 0 | } |
54 | | |
55 | 0 | pub fn block_bytes(&self, block: &CodedBlock) -> &[u8] { |
56 | 0 | let length = self.block_passes(block).last().map_or(0, |pass| pass.rate); |
57 | 0 | let first = block.first_byte as usize; |
58 | 0 | &self.bytes[first..first + length as usize] |
59 | 0 | } |
60 | | |
61 | 0 | pub fn append(&mut self, mut other: CodedBand) { |
62 | 0 | let pass_offset = self.passes.len() as u32; |
63 | 0 | let byte_offset = self.bytes.len() as u32; |
64 | 0 | for block in &mut other.blocks { |
65 | 0 | block.first_pass += pass_offset; |
66 | 0 | block.first_byte += byte_offset; |
67 | 0 | } |
68 | 0 | self.blocks.append(&mut other.blocks); |
69 | 0 | self.passes.append(&mut other.passes); |
70 | 0 | self.bytes.append(&mut other.bytes); |
71 | 0 | } |
72 | | |
73 | 0 | pub fn stats(&self) -> CodingStats { |
74 | 0 | CodingStats { |
75 | 0 | coded_bytes: self.bytes.len() as u64, |
76 | 0 | coding_passes: self.passes.len() as u64, |
77 | 0 | blocks: self.blocks.len() as u64, |
78 | 0 | } |
79 | 0 | } |
80 | | } |
81 | | |
82 | | pub(crate) struct BandGeometry { |
83 | | pub x0: u32, |
84 | | pub y0: u32, |
85 | | pub width: usize, |
86 | | pub height: usize, |
87 | | } |
88 | | |
89 | | #[derive(Clone, Copy)] |
90 | | pub(crate) struct BandT1Params { |
91 | | pub orientation: i32, |
92 | | pub compno: i32, |
93 | | pub level: i32, |
94 | | pub stepsize: f32, |
95 | | pub mct: bool, |
96 | | } |
97 | | |
98 | | #[derive(Clone, Copy)] |
99 | | pub(crate) struct StripeLayout { |
100 | | pub width: usize, |
101 | | pub x0: usize, |
102 | | pub block_width: usize, |
103 | | pub t1_params: BandT1Params, |
104 | | pub t1: MercuryT1EncodeFn, |
105 | | } |
106 | | |
107 | 0 | pub(crate) fn code_stripe( |
108 | 0 | layout: &StripeLayout, |
109 | 0 | stripe: &[f32], |
110 | 0 | rows: usize, |
111 | 0 | block: &mut Vec<i32>, |
112 | 0 | coded: &mut CodedBand, |
113 | 0 | ) { |
114 | 0 | let width = layout.width; |
115 | 0 | let block_width = layout.block_width; |
116 | 0 | let quant = 1.0 / layout.t1_params.stepsize as f64; |
117 | 0 | block.resize(block_width * rows, 0); |
118 | 0 | let mut column = 0; |
119 | 0 | while column < width { |
120 | 0 | let boundary = ((layout.x0 + column) / block_width + 1) * block_width; |
121 | 0 | let columns = boundary.min(layout.x0 + width) - (layout.x0 + column); |
122 | 0 | let max_magnitude = unsafe { |
123 | 0 | mercury_hwy_quantize_97( |
124 | 0 | stripe[column..].as_ptr(), |
125 | 0 | width as i32, |
126 | 0 | columns as i32, |
127 | 0 | rows as i32, |
128 | 0 | quant, |
129 | 0 | block.as_mut_ptr(), |
130 | 0 | ) |
131 | 0 | }; |
132 | 0 | code_block(layout, block, columns, rows, max_magnitude, coded); |
133 | 0 | column += columns; |
134 | 0 | } |
135 | 0 | } |
136 | | |
137 | 0 | fn code_block( |
138 | 0 | layout: &StripeLayout, |
139 | 0 | block: &[i32], |
140 | 0 | columns: usize, |
141 | 0 | rows: usize, |
142 | 0 | max_magnitude: u32, |
143 | 0 | coded_band: &mut CodedBand, |
144 | 0 | ) { |
145 | 0 | let mut coded: *const u8 = std::ptr::null(); |
146 | 0 | let mut rates = [0u16; MAX_CODING_PASSES]; |
147 | 0 | let mut distortions = [0f64; MAX_CODING_PASSES]; |
148 | 0 | let mut terminated = [0u8; MAX_CODING_PASSES]; |
149 | 0 | let mut numbps = 0i32; |
150 | 0 | let params = &layout.t1_params; |
151 | 0 | let passes = unsafe { |
152 | 0 | (layout.t1)( |
153 | 0 | block.as_ptr(), |
154 | 0 | columns as i32, |
155 | 0 | rows as i32, |
156 | 0 | max_magnitude, |
157 | 0 | params.orientation, |
158 | 0 | params.compno, |
159 | 0 | params.level, |
160 | 0 | params.stepsize as f64, |
161 | 0 | params.mct as i32, |
162 | 0 | &mut coded, |
163 | 0 | rates.as_mut_ptr(), |
164 | 0 | distortions.as_mut_ptr(), |
165 | 0 | terminated.as_mut_ptr(), |
166 | 0 | &mut numbps, |
167 | 0 | ) |
168 | | }; |
169 | 0 | assert!(passes >= 0, "T1 encode failed on a {columns}x{rows} block"); |
170 | 0 | let passes = passes as usize; |
171 | 0 | let mut lengths = [0u16; MAX_CODING_PASSES]; |
172 | 0 | let mut previous_rate = 0; |
173 | 0 | for (length, &rate) in lengths.iter_mut().zip(&rates[..passes]) { |
174 | 0 | *length = rate - previous_rate; |
175 | 0 | previous_rate = rate; |
176 | 0 | } |
177 | 0 | let mut slopes = [0u16; MAX_CODING_PASSES]; |
178 | 0 | unsafe { |
179 | 0 | mercury_rate_convex_hull( |
180 | 0 | lengths.as_ptr(), |
181 | 0 | distortions.as_ptr(), |
182 | 0 | slopes.as_mut_ptr(), |
183 | 0 | passes as u32, |
184 | 0 | ); |
185 | 0 | } |
186 | 0 | coded_band.blocks.push(CodedBlock { |
187 | 0 | numbps: numbps as u8, |
188 | 0 | first_pass: coded_band.passes.len() as u32, |
189 | 0 | passes: passes as u8, |
190 | 0 | first_byte: coded_band.bytes.len() as u32, |
191 | 0 | }); |
192 | 0 | coded_band.passes.extend((0..passes).map(|p| CodedPass { |
193 | 0 | rate: rates[p], |
194 | 0 | slope: slopes[p], |
195 | 0 | })); |
196 | 0 | if passes > 0 { |
197 | 0 | let length = rates[passes - 1] as usize; |
198 | 0 | coded_band |
199 | 0 | .bytes |
200 | 0 | .extend_from_slice(unsafe { std::slice::from_raw_parts(coded, length) }); |
201 | 0 | } |
202 | 0 | } |
203 | | |
204 | | struct PooledStripes { |
205 | | queue: Arc<StripeQueue>, |
206 | | results: Arc<StripeResults>, |
207 | | stripes_sent: usize, |
208 | | } |
209 | | |
210 | | // one irreversible subband: collects a block row of rows, then quantizes and codes its blocks |
211 | | pub(crate) struct BandCoder { |
212 | | layout: StripeLayout, |
213 | | y0: usize, |
214 | | height: usize, |
215 | | block_height: usize, |
216 | | stripe: Vec<f32>, |
217 | | stripe_first_row: usize, |
218 | | rows_in_stripe: usize, |
219 | | block: Vec<i32>, |
220 | | pooled: Option<PooledStripes>, |
221 | | pub coded: CodedBand, |
222 | | } |
223 | | |
224 | | impl BandCoder { |
225 | 0 | pub fn warp( |
226 | 0 | geometry: BandGeometry, |
227 | 0 | t1_params: BandT1Params, |
228 | 0 | block_width: u32, |
229 | 0 | block_height: u32, |
230 | 0 | t1: MercuryT1EncodeFn, |
231 | 0 | stripe_queue: Option<Arc<StripeQueue>>, |
232 | 0 | ) -> Self { |
233 | | Self { |
234 | 0 | layout: StripeLayout { |
235 | 0 | width: geometry.width, |
236 | 0 | x0: geometry.x0 as usize, |
237 | 0 | block_width: block_width as usize, |
238 | 0 | t1_params, |
239 | 0 | t1, |
240 | 0 | }, |
241 | 0 | y0: geometry.y0 as usize, |
242 | 0 | height: geometry.height, |
243 | 0 | block_height: block_height as usize, |
244 | 0 | stripe: vec![0.0; geometry.width * block_height as usize], |
245 | | stripe_first_row: 0, |
246 | | rows_in_stripe: 0, |
247 | 0 | block: Vec::new(), |
248 | 0 | pooled: stripe_queue.map(|queue| PooledStripes { |
249 | 0 | queue, |
250 | 0 | results: Arc::default(), |
251 | | stripes_sent: 0, |
252 | 0 | }), |
253 | 0 | coded: CodedBand::default(), |
254 | | } |
255 | 0 | } |
256 | | |
257 | 0 | pub fn next_row(&mut self) -> &mut [f32] { |
258 | 0 | let width = self.layout.width; |
259 | 0 | let start = self.rows_in_stripe * width; |
260 | 0 | &mut self.stripe[start..start + width] |
261 | 0 | } |
262 | | |
263 | 0 | pub fn row_done(&mut self) { |
264 | 0 | self.rows_in_stripe += 1; |
265 | 0 | if self.rows_in_stripe == self.stripe_height() { |
266 | 0 | self.code_stripe(); |
267 | 0 | self.stripe_first_row += self.rows_in_stripe; |
268 | 0 | self.rows_in_stripe = 0; |
269 | 0 | } |
270 | 0 | } |
271 | | |
272 | | // rows from the stripe's first row to the next block boundary or the band end |
273 | 0 | fn stripe_height(&self) -> usize { |
274 | 0 | let band_end = self.y0 + self.height; |
275 | 0 | let first = self.y0 + self.stripe_first_row; |
276 | 0 | let boundary = (first / self.block_height + 1) * self.block_height; |
277 | 0 | boundary.min(band_end) - first |
278 | 0 | } |
279 | | |
280 | 0 | fn code_stripe(&mut self) { |
281 | 0 | let rows = self.rows_in_stripe; |
282 | 0 | let Some(pooled) = &mut self.pooled else { |
283 | 0 | code_stripe( |
284 | 0 | &self.layout, |
285 | 0 | &self.stripe, |
286 | 0 | rows, |
287 | 0 | &mut self.block, |
288 | 0 | &mut self.coded, |
289 | | ); |
290 | 0 | return; |
291 | | }; |
292 | 0 | let next = pooled.queue.spare_buffer(self.stripe.len()); |
293 | 0 | let job = StripeJob { |
294 | 0 | samples: std::mem::replace(&mut self.stripe, next), |
295 | 0 | rows, |
296 | 0 | layout: self.layout, |
297 | 0 | results: pooled.results.clone(), |
298 | 0 | index: pooled.stripes_sent, |
299 | 0 | }; |
300 | 0 | pooled.stripes_sent += 1; |
301 | | // a full queue means the workers are behind |
302 | 0 | if let Err(job) = pooled.queue.try_push(job) { |
303 | 0 | pooled.queue.recycle(job.code(&mut self.block)); |
304 | 0 | } |
305 | 0 | } |
306 | | |
307 | | // after the workers have finished |
308 | 0 | pub fn collect_stripes(&mut self) { |
309 | 0 | let Some(pooled) = self.pooled.take() else { |
310 | 0 | return; |
311 | | }; |
312 | 0 | for stripe in pooled.results.take() { |
313 | 0 | self.coded.append(stripe); |
314 | 0 | } |
315 | 0 | } |
316 | | } |