Coverage Report

Created: 2026-10-28 07:07

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}